From 8f48998279c7cddd539405eb1902ba27d3d97197 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 23 Oct 2021 23:26:04 +0200 Subject: [PATCH 001/263] Fixes --- Cargo.lock | 449 ++++++++++---------- Cargo.toml | 90 ++++ pallets/dmp-queue/src/lib.rs | 2 +- pallets/parachain-system/src/lib.rs | 2 +- pallets/xcmp-queue/src/mock.rs | 1 + parachain-template/runtime/src/lib.rs | 3 + polkadot-parachains/pallets/ping/src/lib.rs | 4 +- polkadot-parachains/rococo/src/lib.rs | 3 + polkadot-parachains/shell/src/lib.rs | 3 + polkadot-parachains/statemine/src/lib.rs | 3 + polkadot-parachains/statemint/src/lib.rs | 3 + polkadot-parachains/westmint/src/lib.rs | 3 + 12 files changed, 333 insertions(+), 233 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 273249468f3..decd42d333d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -462,7 +462,7 @@ dependencies = [ [[package]] name = "beefy-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "beefy-primitives", "fnv", @@ -490,7 +490,7 @@ dependencies = [ [[package]] name = "beefy-gadget-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "beefy-gadget", "beefy-primitives", @@ -510,12 +510,12 @@ dependencies = [ [[package]] name = "beefy-merkle-tree" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" [[package]] name = "beefy-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "parity-scale-codec", "scale-info", @@ -706,7 +706,6 @@ dependencies = [ [[package]] name = "bp-header-chain" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "finality-grandpa", "frame-support", @@ -722,7 +721,6 @@ dependencies = [ [[package]] name = "bp-message-dispatch" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bp-runtime", "frame-support", @@ -734,7 +732,6 @@ dependencies = [ [[package]] name = "bp-messages" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bitvec 0.20.1", "bp-runtime", @@ -750,7 +747,6 @@ dependencies = [ [[package]] name = "bp-polkadot-core" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bp-messages", "bp-runtime", @@ -768,7 +764,6 @@ dependencies = [ [[package]] name = "bp-rialto" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bp-messages", "bp-runtime", @@ -783,7 +778,6 @@ dependencies = [ [[package]] name = "bp-rococo" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bp-messages", "bp-polkadot-core", @@ -800,7 +794,6 @@ dependencies = [ [[package]] name = "bp-runtime" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "frame-support", "hash-db", @@ -818,7 +811,6 @@ dependencies = [ [[package]] name = "bp-test-utils" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bp-header-chain", "ed25519-dalek", @@ -833,7 +825,6 @@ dependencies = [ [[package]] name = "bp-wococo" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bp-messages", "bp-polkadot-core", @@ -848,7 +839,6 @@ dependencies = [ [[package]] name = "bridge-runtime-common" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bp-message-dispatch", "bp-messages", @@ -2444,7 +2434,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "parity-scale-codec", ] @@ -2462,7 +2452,7 @@ dependencies = [ [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -2508,7 +2498,7 @@ dependencies = [ [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -2522,7 +2512,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -2550,7 +2540,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "bitflags", "frame-metadata", @@ -2577,7 +2567,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "Inflector", "frame-support-procedural-tools", @@ -2589,7 +2579,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate 1.1.0", @@ -2601,7 +2591,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "proc-macro2", "quote", @@ -2611,7 +2601,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "log", @@ -2628,7 +2618,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -2643,7 +2633,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "parity-scale-codec", "sp-api", @@ -2652,7 +2642,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "sp-api", @@ -3604,7 +3594,6 @@ dependencies = [ [[package]] name = "kusama-runtime" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "beefy-primitives", "bitvec 0.20.1", @@ -4574,7 +4563,6 @@ dependencies = [ [[package]] name = "metered-channel" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "derive_more", "futures 0.3.17", @@ -5058,7 +5046,7 @@ dependencies = [ [[package]] name = "pallet-assets" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#66296a0af0aede07c27104e6174a3534b15f14aa" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5072,7 +5060,7 @@ dependencies = [ [[package]] name = "pallet-aura" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#66296a0af0aede07c27104e6174a3534b15f14aa" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -5088,7 +5076,7 @@ dependencies = [ [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -5104,7 +5092,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -5119,7 +5107,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5143,7 +5131,7 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5163,7 +5151,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5178,7 +5166,7 @@ dependencies = [ [[package]] name = "pallet-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "beefy-primitives", "frame-support", @@ -5194,7 +5182,7 @@ dependencies = [ [[package]] name = "pallet-beefy-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "beefy-merkle-tree", "beefy-primitives", @@ -5219,7 +5207,7 @@ dependencies = [ [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5237,7 +5225,6 @@ dependencies = [ [[package]] name = "pallet-bridge-dispatch" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bp-message-dispatch", "bp-runtime", @@ -5254,7 +5241,6 @@ dependencies = [ [[package]] name = "pallet-bridge-grandpa" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bp-header-chain", "bp-runtime", @@ -5276,7 +5262,6 @@ dependencies = [ [[package]] name = "pallet-bridge-messages" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bitvec 0.20.1", "bp-message-dispatch", @@ -5324,7 +5309,7 @@ dependencies = [ [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5341,7 +5326,7 @@ dependencies = [ [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5357,7 +5342,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5381,7 +5366,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5399,7 +5384,7 @@ dependencies = [ [[package]] name = "pallet-gilt" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5414,7 +5399,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5437,7 +5422,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "enumflags2", "frame-benchmarking", @@ -5453,7 +5438,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5473,7 +5458,7 @@ dependencies = [ [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5490,7 +5475,7 @@ dependencies = [ [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5507,7 +5492,7 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "ckb-merkle-mountain-range", "frame-benchmarking", @@ -5525,7 +5510,7 @@ dependencies = [ [[package]] name = "pallet-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -5541,7 +5526,7 @@ dependencies = [ [[package]] name = "pallet-mmr-rpc" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "jsonrpc-core", "jsonrpc-core-client", @@ -5558,7 +5543,7 @@ dependencies = [ [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5573,7 +5558,7 @@ dependencies = [ [[package]] name = "pallet-nicks" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -5587,7 +5572,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -5604,7 +5589,7 @@ dependencies = [ [[package]] name = "pallet-offences-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5627,7 +5612,7 @@ dependencies = [ [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5642,7 +5627,7 @@ dependencies = [ [[package]] name = "pallet-randomness-collective-flip" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#66296a0af0aede07c27104e6174a3534b15f14aa" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -5656,7 +5641,7 @@ dependencies = [ [[package]] name = "pallet-recovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -5670,7 +5655,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5686,7 +5671,7 @@ dependencies = [ [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -5707,7 +5692,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5723,7 +5708,7 @@ dependencies = [ [[package]] name = "pallet-society" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -5737,7 +5722,7 @@ dependencies = [ [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5760,7 +5745,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "proc-macro-crate 1.1.0", "proc-macro2", @@ -5771,7 +5756,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "log", "sp-arithmetic", @@ -5780,7 +5765,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -5809,7 +5794,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5827,7 +5812,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5846,7 +5831,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-support", "frame-system", @@ -5863,7 +5848,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "jsonrpc-core", "jsonrpc-core-client", @@ -5880,7 +5865,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -5891,7 +5876,7 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5922,7 +5907,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5938,7 +5923,7 @@ dependencies = [ [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-benchmarking", "frame-support", @@ -5953,7 +5938,6 @@ dependencies = [ [[package]] name = "pallet-xcm" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "frame-support", "frame-system", @@ -5971,7 +5955,6 @@ dependencies = [ [[package]] name = "pallet-xcm-benchmarks" version = "0.9.8" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "frame-benchmarking", "frame-support", @@ -6491,7 +6474,6 @@ checksum = "989d43012e2ca1c4a02507c67282691a0a3207f9dc67cec596b43fe925b3d325" [[package]] name = "polkadot-approval-distribution" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "futures 0.3.17", "polkadot-node-network-protocol", @@ -6505,7 +6487,6 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "futures 0.3.17", "polkadot-node-network-protocol", @@ -6518,7 +6499,6 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "derive_more", "futures 0.3.17", @@ -6540,7 +6520,6 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "futures 0.3.17", "lru 0.7.0", @@ -6560,7 +6539,6 @@ dependencies = [ [[package]] name = "polkadot-cli" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "frame-benchmarking-cli", "futures 0.3.17", @@ -6580,7 +6558,6 @@ dependencies = [ [[package]] name = "polkadot-client" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "beefy-primitives", "frame-benchmarking", @@ -6678,7 +6655,6 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "always-assert", "derive_more", @@ -6699,7 +6675,6 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "parity-scale-codec", "parity-util-mem", @@ -6712,7 +6687,6 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "derive_more", "futures 0.3.17", @@ -6734,7 +6708,6 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", @@ -6748,7 +6721,6 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "futures 0.3.17", "futures-timer 3.0.2", @@ -6768,7 +6740,6 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "async-trait", "futures 0.3.17", @@ -6787,7 +6758,6 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "futures 0.3.17", "parity-scale-codec", @@ -6805,7 +6775,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bitvec 0.20.1", "derive_more", @@ -6833,7 +6802,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bitvec 0.20.1", "futures 0.3.17", @@ -6853,7 +6821,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bitvec 0.20.1", "futures 0.3.17", @@ -6871,7 +6838,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "futures 0.3.17", "polkadot-node-subsystem", @@ -6886,7 +6852,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "async-trait", "futures 0.3.17", @@ -6904,7 +6869,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "futures 0.3.17", "polkadot-node-subsystem", @@ -6919,7 +6883,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "futures 0.3.17", "futures-timer 3.0.2", @@ -6936,7 +6899,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bitvec 0.20.1", "derive_more", @@ -6955,7 +6917,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-participation" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "futures 0.3.17", "polkadot-node-primitives", @@ -6968,7 +6929,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "async-trait", "futures 0.3.17", @@ -6985,7 +6945,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bitvec 0.20.1", "futures 0.3.17", @@ -7000,7 +6959,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "always-assert", "assert_matches", @@ -7031,7 +6989,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "futures 0.3.17", "memory-lru", @@ -7049,7 +7006,6 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "async-std", "lazy_static", @@ -7067,7 +7023,6 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "futures 0.3.17", "futures-timer 3.0.2", @@ -7078,7 +7033,6 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "async-trait", "derive_more", @@ -7096,7 +7050,6 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bounded-vec", "futures 0.3.17", @@ -7118,7 +7071,6 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -7128,7 +7080,6 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-test-helpers" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "async-trait", "futures 0.3.17", @@ -7146,7 +7097,6 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "derive_more", "futures 0.3.17", @@ -7165,7 +7115,6 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "async-trait", "derive_more", @@ -7192,7 +7141,6 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "futures 0.3.17", "futures-timer 3.0.2", @@ -7213,7 +7161,6 @@ dependencies = [ [[package]] name = "polkadot-overseer-gen" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "async-trait", "futures 0.3.17", @@ -7230,7 +7177,6 @@ dependencies = [ [[package]] name = "polkadot-overseer-gen-proc-macro" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "proc-macro-crate 1.1.0", "proc-macro2", @@ -7241,7 +7187,6 @@ dependencies = [ [[package]] name = "polkadot-parachain" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "derive_more", "frame-support", @@ -7258,7 +7203,6 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bitvec 0.20.1", "frame-system", @@ -7288,7 +7232,6 @@ dependencies = [ [[package]] name = "polkadot-rpc" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", @@ -7319,7 +7262,6 @@ dependencies = [ [[package]] name = "polkadot-runtime" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "beefy-primitives", "bitvec 0.20.1", @@ -7396,7 +7338,6 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "beefy-primitives", "bitvec 0.20.1", @@ -7443,7 +7384,6 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "bitflags", "bitvec 0.20.1", @@ -7482,7 +7422,6 @@ dependencies = [ [[package]] name = "polkadot-service" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "async-trait", "beefy-gadget", @@ -7580,7 +7519,6 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "arrayvec 0.5.2", "derive_more", @@ -7601,7 +7539,6 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -7611,7 +7548,6 @@ dependencies = [ [[package]] name = "polkadot-test-client" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "parity-scale-codec", "polkadot-node-subsystem", @@ -7636,7 +7572,6 @@ dependencies = [ [[package]] name = "polkadot-test-runtime" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "beefy-primitives", "bitvec 0.20.1", @@ -7697,7 +7632,6 @@ dependencies = [ [[package]] name = "polkadot-test-service" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "frame-benchmarking", "frame-system", @@ -8420,7 +8354,6 @@ dependencies = [ [[package]] name = "rococo-runtime" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "beefy-primitives", "bp-messages", @@ -8609,7 +8542,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "log", "sp-core", @@ -8620,7 +8553,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "derive_more", @@ -8647,7 +8580,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "futures 0.3.17", "futures-timer 3.0.2", @@ -8670,7 +8603,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -8686,7 +8619,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -8702,7 +8635,7 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "proc-macro-crate 1.1.0", "proc-macro2", @@ -8713,7 +8646,7 @@ dependencies = [ [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "chrono", "fdlimit", @@ -8751,7 +8684,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "fnv", "futures 0.3.17", @@ -8779,7 +8712,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "hash-db", "kvdb", @@ -8804,7 +8737,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "futures 0.3.17", @@ -8857,7 +8790,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "derive_more", @@ -8900,7 +8833,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "derive_more", "futures 0.3.17", @@ -8924,7 +8857,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "fork-tree", "parity-scale-codec", @@ -8937,7 +8870,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "futures 0.3.17", @@ -8963,7 +8896,7 @@ dependencies = [ [[package]] name = "sc-consensus-uncles" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "sc-client-api", "sp-authorship", @@ -8974,7 +8907,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "lazy_static", "libsecp256k1 0.6.0", @@ -9000,7 +8933,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "derive_more", "environmental", @@ -9018,7 +8951,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "log", "parity-scale-codec", @@ -9034,7 +8967,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "cfg-if 1.0.0", "libc", @@ -9052,7 +8985,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "derive_more", @@ -9089,7 +9022,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "derive_more", "finality-grandpa", @@ -9113,7 +9046,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "ansi_term 0.12.1", "futures 0.3.17", @@ -9130,7 +9063,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "derive_more", @@ -9145,7 +9078,7 @@ dependencies = [ [[package]] name = "sc-light" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "hash-db", "parity-scale-codec", @@ -9163,7 +9096,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-std", "async-trait", @@ -9214,7 +9147,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "futures 0.3.17", "futures-timer 3.0.2", @@ -9230,7 +9163,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "bytes 1.0.1", "fnv", @@ -9257,7 +9190,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "futures 0.3.17", "libp2p", @@ -9270,7 +9203,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.9.0" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -9279,7 +9212,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "futures 0.3.17", "hash-db", @@ -9310,7 +9243,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "futures 0.3.17", "jsonrpc-core", @@ -9335,7 +9268,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "futures 0.3.17", "jsonrpc-core", @@ -9352,7 +9285,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "directories", @@ -9417,7 +9350,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "log", "parity-scale-codec", @@ -9431,7 +9364,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "jsonrpc-core", "jsonrpc-core-client", @@ -9453,7 +9386,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "chrono", "futures 0.3.17", @@ -9471,7 +9404,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "ansi_term 0.12.1", "atty", @@ -9502,7 +9435,7 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "proc-macro-crate 1.1.0", "proc-macro2", @@ -9513,7 +9446,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "futures 0.3.17", "intervalier", @@ -9540,7 +9473,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "derive_more", "futures 0.3.17", @@ -9554,7 +9487,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "futures 0.3.17", "futures-timer 3.0.2", @@ -9902,7 +9835,6 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "slot-range-helper" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "enumn", "parity-scale-codec", @@ -10005,7 +9937,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "hash-db", "log", @@ -10022,7 +9954,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "blake2-rfc", "proc-macro-crate 1.1.0", @@ -10034,7 +9966,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "parity-scale-codec", "scale-info", @@ -10047,7 +9979,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "integer-sqrt", "num-traits", @@ -10062,7 +9994,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "parity-scale-codec", "scale-info", @@ -10075,7 +10007,7 @@ dependencies = [ [[package]] name = "sp-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "parity-scale-codec", @@ -10087,7 +10019,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "parity-scale-codec", "sp-api", @@ -10099,7 +10031,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "futures 0.3.17", "log", @@ -10117,7 +10049,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "futures 0.3.17", @@ -10136,7 +10068,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#66296a0af0aede07c27104e6174a3534b15f14aa" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "parity-scale-codec", @@ -10154,7 +10086,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "merlin", @@ -10177,7 +10109,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "parity-scale-codec", "scale-info", @@ -10188,7 +10120,7 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "parity-scale-codec", "schnorrkel", @@ -10200,7 +10132,7 @@ dependencies = [ [[package]] name = "sp-core" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "base58", "blake2-rfc", @@ -10246,7 +10178,7 @@ dependencies = [ [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "kvdb", "parking_lot 0.11.1", @@ -10255,7 +10187,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "proc-macro2", "quote", @@ -10265,7 +10197,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "environmental", "parity-scale-codec", @@ -10276,7 +10208,7 @@ dependencies = [ [[package]] name = "sp-finality-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "finality-grandpa", "log", @@ -10294,7 +10226,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -10308,7 +10240,7 @@ dependencies = [ [[package]] name = "sp-io" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "futures 0.3.17", "hash-db", @@ -10332,7 +10264,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "lazy_static", "sp-core", @@ -10343,7 +10275,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "derive_more", @@ -10360,7 +10292,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "zstd", ] @@ -10368,7 +10300,7 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "parity-scale-codec", "scale-info", @@ -10383,7 +10315,7 @@ dependencies = [ [[package]] name = "sp-npos-elections-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "proc-macro-crate 1.1.0", "proc-macro2", @@ -10394,7 +10326,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "sp-api", "sp-core", @@ -10404,7 +10336,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "backtrace", ] @@ -10412,7 +10344,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "rustc-hash", "serde", @@ -10422,7 +10354,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "either", "hash256-std-hasher", @@ -10444,7 +10376,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -10461,7 +10393,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "Inflector", "proc-macro-crate 1.1.0", @@ -10473,7 +10405,7 @@ dependencies = [ [[package]] name = "sp-serializer" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "serde", "serde_json", @@ -10482,7 +10414,7 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "parity-scale-codec", "scale-info", @@ -10496,7 +10428,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "parity-scale-codec", "scale-info", @@ -10507,7 +10439,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "hash-db", "log", @@ -10530,12 +10462,12 @@ dependencies = [ [[package]] name = "sp-std" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" [[package]] name = "sp-storage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "impl-serde", "parity-scale-codec", @@ -10548,7 +10480,7 @@ dependencies = [ [[package]] name = "sp-tasks" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "log", "sp-core", @@ -10561,7 +10493,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "futures-timer 3.0.2", @@ -10577,7 +10509,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "parity-scale-codec", "sp-std", @@ -10589,7 +10521,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "sp-api", "sp-runtime", @@ -10598,7 +10530,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "log", @@ -10614,7 +10546,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "hash-db", "memory-db", @@ -10629,7 +10561,7 @@ dependencies = [ [[package]] name = "sp-version" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "impl-serde", "parity-scale-codec", @@ -10645,7 +10577,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -10656,7 +10588,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -10980,7 +10912,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "frame-system-rpc-runtime-api", "futures 0.3.17", @@ -11002,7 +10934,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.9.0" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-std", "derive_more", @@ -11016,7 +10948,7 @@ dependencies = [ [[package]] name = "substrate-test-client" version = "2.0.1" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "async-trait", "futures 0.3.17", @@ -11043,7 +10975,7 @@ dependencies = [ [[package]] name = "substrate-test-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#66296a0af0aede07c27104e6174a3534b15f14aa" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "futures 0.3.17", "substrate-test-utils-derive", @@ -11053,7 +10985,7 @@ dependencies = [ [[package]] name = "substrate-test-utils-derive" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#66296a0af0aede07c27104e6174a3534b15f14aa" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "proc-macro-crate 1.1.0", "proc-macro2", @@ -11064,7 +10996,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "ansi_term 0.12.1", "build-helper", @@ -11426,7 +11358,7 @@ dependencies = [ "chrono", "lazy_static", "matchers", - "parking_lot 0.10.2", + "parking_lot 0.11.1", "regex", "serde", "serde_json", @@ -12095,7 +12027,6 @@ dependencies = [ [[package]] name = "westend-runtime" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "beefy-primitives", "bitvec 0.20.1", @@ -12340,7 +12271,6 @@ dependencies = [ [[package]] name = "xcm" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "derivative", "impl-trait-for-tuples", @@ -12353,7 +12283,6 @@ dependencies = [ [[package]] name = "xcm-builder" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "frame-support", "frame-system", @@ -12373,7 +12302,6 @@ dependencies = [ [[package]] name = "xcm-executor" version = "0.9.12" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "frame-benchmarking", "frame-support", @@ -12391,7 +12319,6 @@ dependencies = [ [[package]] name = "xcm-procedural" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#fc32642c5011721916cb0f9e083800c281979980" dependencies = [ "proc-macro2", "quote", @@ -12461,3 +12388,67 @@ dependencies = [ "cc", "libc", ] + +[[patch.unused]] +name = "polkadot" +version = "0.9.12" + +[[patch.unused]] +name = "polkadot-simnet" +version = "0.9.12" + +[[patch.unused]] +name = "polkadot-simnet-node" +version = "0.9.12" + +[[patch.unused]] +name = "polkadot-simnet-test" +version = "0.9.12" + +[[patch.unused]] +name = "polkadot-test-malus" +version = "0.9.12" + +[[patch.unused]] +name = "polkadot-voter-bags" +version = "0.9.0" + +[[patch.unused]] +name = "remote-ext-tests-bags-list" +version = "0.9.12" + +[[patch.unused]] +name = "staking-miner" +version = "0.9.12" + +[[patch.unused]] +name = "test-parachain-adder" +version = "0.9.12" + +[[patch.unused]] +name = "test-parachain-adder-collator" +version = "0.9.12" + +[[patch.unused]] +name = "test-parachain-halt" +version = "0.9.12" + +[[patch.unused]] +name = "test-parachains" +version = "0.9.12" + +[[patch.unused]] +name = "xcm-executor-integration-tests" +version = "0.9.12" + +[[patch.unused]] +name = "xcm-simulator" +version = "0.9.12" + +[[patch.unused]] +name = "xcm-simulator-example" +version = "0.9.12" + +[[patch.unused]] +name = "xcm-simulator-fuzzer" +version = "0.9.9" diff --git a/Cargo.toml b/Cargo.toml index c89e88b214e..2197b1352d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,3 +41,93 @@ members = [ [profile.release] panic = "unwind" +[patch."https://github.com/paritytech/polkadot"] +polkadot-cli ={path = "/Users/gav/Core/polkadot/cli" } +polkadot-node-core-pvf ={path = "/Users/gav/Core/polkadot/node/core/pvf" } +polkadot-core-primitives ={path = "/Users/gav/Core/polkadot/core-primitives" } +polkadot-node-subsystem-util ={path = "/Users/gav/Core/polkadot/node/subsystem-util" } +metered-channel ={path = "/Users/gav/Core/polkadot/node/metered-channel" } +polkadot-node-jaeger ={path = "/Users/gav/Core/polkadot/node/jaeger" } +polkadot-node-primitives ={path = "/Users/gav/Core/polkadot/node/primitives" } +polkadot-parachain ={path = "/Users/gav/Core/polkadot/parachain" } +polkadot-primitives ={path = "/Users/gav/Core/polkadot/primitives" } +polkadot-erasure-coding ={path = "/Users/gav/Core/polkadot/erasure-coding" } +polkadot-node-metrics ={path = "/Users/gav/Core/polkadot/node/metrics" } +polkadot-node-network-protocol ={path = "/Users/gav/Core/polkadot/node/network/protocol" } +polkadot-node-subsystem ={path = "/Users/gav/Core/polkadot/node/subsystem" } +polkadot-node-subsystem-types ={path = "/Users/gav/Core/polkadot/node/subsystem-types" } +polkadot-overseer-gen ={path = "/Users/gav/Core/polkadot/node/overseer/overseer-gen" } +polkadot-overseer-gen-proc-macro ={path = "/Users/gav/Core/polkadot/node/overseer/overseer-gen/proc-macro" } +polkadot-statement-table ={path = "/Users/gav/Core/polkadot/statement-table" } +polkadot-overseer ={path = "/Users/gav/Core/polkadot/node/overseer" } +polkadot-node-subsystem-test-helpers ={path = "/Users/gav/Core/polkadot/node/subsystem-test-helpers" } +test-parachain-adder ={path = "/Users/gav/Core/polkadot/parachain/test-parachains/adder" } +test-parachain-halt ={path = "/Users/gav/Core/polkadot/parachain/test-parachains/halt" } +polkadot-service ={path = "/Users/gav/Core/polkadot/node/service" } +kusama-runtime ={path = "/Users/gav/Core/polkadot/runtime/kusama" } +pallet-xcm ={path = "/Users/gav/Core/polkadot/xcm/pallet-xcm" } +xcm ={path = "/Users/gav/Core/polkadot/xcm" } +xcm-procedural ={path = "/Users/gav/Core/polkadot/xcm/procedural" } +xcm-executor ={path = "/Users/gav/Core/polkadot/xcm/xcm-executor" } +polkadot-runtime-parachains ={path = "/Users/gav/Core/polkadot/runtime/parachains" } +xcm-builder ={path = "/Users/gav/Core/polkadot/xcm/xcm-builder" } +polkadot-runtime-common ={path = "/Users/gav/Core/polkadot/runtime/common" } +slot-range-helper ={path = "/Users/gav/Core/polkadot/runtime/common/slot_range_helper" } +polkadot-approval-distribution ={path = "/Users/gav/Core/polkadot/node/network/approval-distribution" } +polkadot-availability-bitfield-distribution ={path = "/Users/gav/Core/polkadot/node/network/bitfield-distribution" } +polkadot-availability-distribution ={path = "/Users/gav/Core/polkadot/node/network/availability-distribution" } +polkadot-availability-recovery ={path = "/Users/gav/Core/polkadot/node/network/availability-recovery" } +polkadot-client ={path = "/Users/gav/Core/polkadot/node/client" } +polkadot-runtime ={path = "/Users/gav/Core/polkadot/runtime/polkadot" } +rococo-runtime ={path = "/Users/gav/Core/polkadot/runtime/rococo" } +bp-messages ={path = "/Users/gav/Core/polkadot/bridges/primitives/messages" } +bp-runtime ={path = "/Users/gav/Core/polkadot/bridges/primitives/runtime" } +bp-rococo ={path = "/Users/gav/Core/polkadot/bridges/primitives/chain-rococo" } +bp-polkadot-core ={path = "/Users/gav/Core/polkadot/bridges/primitives/polkadot-core" } +bp-wococo ={path = "/Users/gav/Core/polkadot/bridges/primitives/chain-wococo" } +bridge-runtime-common ={path = "/Users/gav/Core/polkadot/bridges/bin/runtime-common" } +bp-message-dispatch ={path = "/Users/gav/Core/polkadot/bridges/primitives/message-dispatch" } +pallet-bridge-dispatch ={path = "/Users/gav/Core/polkadot/bridges/modules/dispatch" } +pallet-bridge-grandpa ={path = "/Users/gav/Core/polkadot/bridges/modules/grandpa" } +bp-header-chain ={path = "/Users/gav/Core/polkadot/bridges/primitives/header-chain" } +bp-test-utils ={path = "/Users/gav/Core/polkadot/bridges/primitives/test-utils" } +pallet-bridge-messages ={path = "/Users/gav/Core/polkadot/bridges/modules/messages" } +bp-rialto ={path = "/Users/gav/Core/polkadot/bridges/primitives/chain-rialto" } +westend-runtime ={path = "/Users/gav/Core/polkadot/runtime/westend" } +pallet-xcm-benchmarks ={path = "/Users/gav/Core/polkadot/xcm/pallet-xcm-benchmarks" } +polkadot-collator-protocol ={path = "/Users/gav/Core/polkadot/node/network/collator-protocol" } +polkadot-dispute-distribution ={path = "/Users/gav/Core/polkadot/node/network/dispute-distribution" } +polkadot-gossip-support ={path = "/Users/gav/Core/polkadot/node/network/gossip-support" } +polkadot-network-bridge ={path = "/Users/gav/Core/polkadot/node/network/bridge" } +polkadot-node-collation-generation ={path = "/Users/gav/Core/polkadot/node/collation-generation" } +polkadot-node-core-approval-voting ={path = "/Users/gav/Core/polkadot/node/core/approval-voting" } +polkadot-node-core-av-store ={path = "/Users/gav/Core/polkadot/node/core/av-store" } +polkadot-node-core-backing ={path = "/Users/gav/Core/polkadot/node/core/backing" } +polkadot-node-core-bitfield-signing ={path = "/Users/gav/Core/polkadot/node/core/bitfield-signing" } +polkadot-node-core-candidate-validation ={path = "/Users/gav/Core/polkadot/node/core/candidate-validation" } +polkadot-node-core-chain-api ={path = "/Users/gav/Core/polkadot/node/core/chain-api" } +polkadot-node-core-chain-selection ={path = "/Users/gav/Core/polkadot/node/core/chain-selection" } +polkadot-node-core-dispute-coordinator ={path = "/Users/gav/Core/polkadot/node/core/dispute-coordinator" } +polkadot-node-core-dispute-participation ={path = "/Users/gav/Core/polkadot/node/core/dispute-participation" } +polkadot-node-core-parachains-inherent ={path = "/Users/gav/Core/polkadot/node/core/parachains-inherent" } +polkadot-node-core-provisioner ={path = "/Users/gav/Core/polkadot/node/core/provisioner" } +polkadot-node-core-runtime-api ={path = "/Users/gav/Core/polkadot/node/core/runtime-api" } +polkadot-rpc ={path = "/Users/gav/Core/polkadot/rpc" } +polkadot-statement-distribution ={path = "/Users/gav/Core/polkadot/node/network/statement-distribution" } +polkadot-test-client ={path = "/Users/gav/Core/polkadot/node/test/client" } +polkadot-test-runtime ={path = "/Users/gav/Core/polkadot/runtime/test-runtime" } +polkadot-test-service ={path = "/Users/gav/Core/polkadot/node/test/service" } +xcm-executor-integration-tests ={path = "/Users/gav/Core/polkadot/xcm/xcm-executor/integration-tests" } +xcm-simulator ={path = "/Users/gav/Core/polkadot/xcm/xcm-simulator" } +xcm-simulator-example ={path = "/Users/gav/Core/polkadot/xcm/xcm-simulator/example" } +xcm-simulator-fuzzer ={path = "/Users/gav/Core/polkadot/xcm/xcm-simulator/fuzzer" } +polkadot-test-malus ={path = "/Users/gav/Core/polkadot/node/malus" } +polkadot-simnet ={path = "/Users/gav/Core/polkadot/node/test/polkadot-simnet/common" } +polkadot-simnet-node ={path = "/Users/gav/Core/polkadot/node/test/polkadot-simnet/node" } +polkadot-simnet-test ={path = "/Users/gav/Core/polkadot/node/test/polkadot-simnet/test" } +test-parachains ={path = "/Users/gav/Core/polkadot/parachain/test-parachains" } +test-parachain-adder-collator ={path = "/Users/gav/Core/polkadot/parachain/test-parachains/adder/collator" } +staking-miner ={path = "/Users/gav/Core/polkadot/utils/staking-miner" } +remote-ext-tests-bags-list ={path = "/Users/gav/Core/polkadot/utils/remote-ext-tests/bags-list" } +polkadot-voter-bags ={path = "/Users/gav/Core/polkadot/utils/voter-bags" } +polkadot ={path = "/Users/gav/Core/polkadot" } diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 251d3acbb38..8766f618d6c 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -473,7 +473,7 @@ mod tests { fn msg(weight: Weight) -> Xcm { Xcm(vec![Transact { - origin_type: OriginKind::Native, + origin_kind: OriginKind::Native, require_weight_at_most: weight, call: vec![].into(), }]) diff --git a/pallets/parachain-system/src/lib.rs b/pallets/parachain-system/src/lib.rs index 9f2e044c720..198a7646f3e 100644 --- a/pallets/parachain-system/src/lib.rs +++ b/pallets/parachain-system/src/lib.rs @@ -660,7 +660,7 @@ impl GetChannelInfo for Pallet { // None then it must be that this is an edge-case where a message is attempted to be // sent at the first block. It should be safe to assume that there are no channels // opened at all so early. At least, relying on this assumption seems to be a better - // tradeoff, compared to introducing an error variant that the clients should be + // trade-off, compared to introducing an error variant that the clients should be // prepared to handle. let index = match channels.binary_search_by_key(&id, |item| item.0) { Err(_) => return ChannelStatus::Closed, diff --git a/pallets/xcmp-queue/src/mock.rs b/pallets/xcmp-queue/src/mock.rs index 3496db5aa32..51f19bf4a50 100644 --- a/pallets/xcmp-queue/src/mock.rs +++ b/pallets/xcmp-queue/src/mock.rs @@ -110,6 +110,7 @@ parameter_types! { pub Ancestry: MultiLocation = X1(Parachain(1u32.into())).into(); pub UnitWeightCost: Weight = 1_000_000; pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; } /// Means for transacting assets on this chain. diff --git a/parachain-template/runtime/src/lib.rs b/parachain-template/runtime/src/lib.rs index 9012dcf1500..bcdefa48f92 100644 --- a/parachain-template/runtime/src/lib.rs +++ b/parachain-template/runtime/src/lib.rs @@ -457,6 +457,7 @@ parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. pub UnitWeightCost: Weight = 1_000_000_000; pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; } match_type! { @@ -490,6 +491,8 @@ impl Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; } parameter_types! { diff --git a/polkadot-parachains/pallets/ping/src/lib.rs b/polkadot-parachains/pallets/ping/src/lib.rs index 9960d66c504..621c3afd923 100644 --- a/polkadot-parachains/pallets/ping/src/lib.rs +++ b/polkadot-parachains/pallets/ping/src/lib.rs @@ -91,7 +91,7 @@ pub mod pallet { match T::XcmSender::send_xcm( (1, Junction::Parachain(para.into())), Xcm(vec![Transact { - origin_type: OriginKind::Native, + origin_kind: OriginKind::Native, require_weight_at_most: 1_000, call: ::Call::from(Call::::ping { seq, @@ -167,7 +167,7 @@ pub mod pallet { match T::XcmSender::send_xcm( (1, Junction::Parachain(para.into())), Xcm(vec![Transact { - origin_type: OriginKind::Native, + origin_kind: OriginKind::Native, require_weight_at_most: 1_000, call: ::Call::from(Call::::pong { seq, diff --git a/polkadot-parachains/rococo/src/lib.rs b/polkadot-parachains/rococo/src/lib.rs index d835cd4aa3e..c45306d4e36 100644 --- a/polkadot-parachains/rococo/src/lib.rs +++ b/polkadot-parachains/rococo/src/lib.rs @@ -380,6 +380,7 @@ pub type Barrier = ( parameter_types! { pub StatemintLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(1000))); + pub MaxAssetsIntoHolding: u32 = 64; } pub type Reserves = (NativeAsset, AssetsFrom); @@ -401,6 +402,8 @@ impl Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/polkadot-parachains/shell/src/lib.rs b/polkadot-parachains/shell/src/lib.rs index 84823d52b58..a202afd63d4 100644 --- a/polkadot-parachains/shell/src/lib.rs +++ b/polkadot-parachains/shell/src/lib.rs @@ -197,6 +197,7 @@ parameter_types! { // One XCM operation is 1_000_000 weight - almost certainly a conservative estimate. pub UnitWeightCost: Weight = 1_000_000; pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; } pub struct XcmConfig; @@ -215,6 +216,8 @@ impl Config for XcmConfig { type AssetTrap = (); // don't trap for now type AssetClaims = (); // don't claim for now type SubscriptionService = (); // don't handle subscriptions for now + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/polkadot-parachains/statemine/src/lib.rs b/polkadot-parachains/statemine/src/lib.rs index 474a9f93fd4..d3d257056f5 100644 --- a/polkadot-parachains/statemine/src/lib.rs +++ b/polkadot-parachains/statemine/src/lib.rs @@ -549,6 +549,7 @@ parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. pub UnitWeightCost: Weight = 1_000_000_000; pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; } match_type! { @@ -582,6 +583,8 @@ impl Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; } parameter_types! { diff --git a/polkadot-parachains/statemint/src/lib.rs b/polkadot-parachains/statemint/src/lib.rs index ade13cc1e55..b4e99ece7f2 100644 --- a/polkadot-parachains/statemint/src/lib.rs +++ b/polkadot-parachains/statemint/src/lib.rs @@ -526,6 +526,7 @@ parameter_types! { // One XCM operation is 1_000_000 weight - almost certainly a conservative estimate. pub UnitWeightCost: Weight = 1_000_000; pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; } match_type! { @@ -558,6 +559,8 @@ impl Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; } parameter_types! { diff --git a/polkadot-parachains/westmint/src/lib.rs b/polkadot-parachains/westmint/src/lib.rs index ace9ce9afac..7c1853c1743 100644 --- a/polkadot-parachains/westmint/src/lib.rs +++ b/polkadot-parachains/westmint/src/lib.rs @@ -512,6 +512,7 @@ pub type XcmOriginToTransactDispatchOrigin = ( parameter_types! { pub UnitWeightCost: Weight = 1_000; pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; } match_type! { @@ -544,6 +545,8 @@ impl Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; } parameter_types! { From 2ac537ee1f2bdbb2fd3063981c8c0e3b12718d08 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 23 Oct 2021 23:32:37 +0200 Subject: [PATCH 002/263] Undiener --- Cargo.toml | 90 ------------------------------------------------------ 1 file changed, 90 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2197b1352d5..c89e88b214e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,93 +41,3 @@ members = [ [profile.release] panic = "unwind" -[patch."https://github.com/paritytech/polkadot"] -polkadot-cli ={path = "/Users/gav/Core/polkadot/cli" } -polkadot-node-core-pvf ={path = "/Users/gav/Core/polkadot/node/core/pvf" } -polkadot-core-primitives ={path = "/Users/gav/Core/polkadot/core-primitives" } -polkadot-node-subsystem-util ={path = "/Users/gav/Core/polkadot/node/subsystem-util" } -metered-channel ={path = "/Users/gav/Core/polkadot/node/metered-channel" } -polkadot-node-jaeger ={path = "/Users/gav/Core/polkadot/node/jaeger" } -polkadot-node-primitives ={path = "/Users/gav/Core/polkadot/node/primitives" } -polkadot-parachain ={path = "/Users/gav/Core/polkadot/parachain" } -polkadot-primitives ={path = "/Users/gav/Core/polkadot/primitives" } -polkadot-erasure-coding ={path = "/Users/gav/Core/polkadot/erasure-coding" } -polkadot-node-metrics ={path = "/Users/gav/Core/polkadot/node/metrics" } -polkadot-node-network-protocol ={path = "/Users/gav/Core/polkadot/node/network/protocol" } -polkadot-node-subsystem ={path = "/Users/gav/Core/polkadot/node/subsystem" } -polkadot-node-subsystem-types ={path = "/Users/gav/Core/polkadot/node/subsystem-types" } -polkadot-overseer-gen ={path = "/Users/gav/Core/polkadot/node/overseer/overseer-gen" } -polkadot-overseer-gen-proc-macro ={path = "/Users/gav/Core/polkadot/node/overseer/overseer-gen/proc-macro" } -polkadot-statement-table ={path = "/Users/gav/Core/polkadot/statement-table" } -polkadot-overseer ={path = "/Users/gav/Core/polkadot/node/overseer" } -polkadot-node-subsystem-test-helpers ={path = "/Users/gav/Core/polkadot/node/subsystem-test-helpers" } -test-parachain-adder ={path = "/Users/gav/Core/polkadot/parachain/test-parachains/adder" } -test-parachain-halt ={path = "/Users/gav/Core/polkadot/parachain/test-parachains/halt" } -polkadot-service ={path = "/Users/gav/Core/polkadot/node/service" } -kusama-runtime ={path = "/Users/gav/Core/polkadot/runtime/kusama" } -pallet-xcm ={path = "/Users/gav/Core/polkadot/xcm/pallet-xcm" } -xcm ={path = "/Users/gav/Core/polkadot/xcm" } -xcm-procedural ={path = "/Users/gav/Core/polkadot/xcm/procedural" } -xcm-executor ={path = "/Users/gav/Core/polkadot/xcm/xcm-executor" } -polkadot-runtime-parachains ={path = "/Users/gav/Core/polkadot/runtime/parachains" } -xcm-builder ={path = "/Users/gav/Core/polkadot/xcm/xcm-builder" } -polkadot-runtime-common ={path = "/Users/gav/Core/polkadot/runtime/common" } -slot-range-helper ={path = "/Users/gav/Core/polkadot/runtime/common/slot_range_helper" } -polkadot-approval-distribution ={path = "/Users/gav/Core/polkadot/node/network/approval-distribution" } -polkadot-availability-bitfield-distribution ={path = "/Users/gav/Core/polkadot/node/network/bitfield-distribution" } -polkadot-availability-distribution ={path = "/Users/gav/Core/polkadot/node/network/availability-distribution" } -polkadot-availability-recovery ={path = "/Users/gav/Core/polkadot/node/network/availability-recovery" } -polkadot-client ={path = "/Users/gav/Core/polkadot/node/client" } -polkadot-runtime ={path = "/Users/gav/Core/polkadot/runtime/polkadot" } -rococo-runtime ={path = "/Users/gav/Core/polkadot/runtime/rococo" } -bp-messages ={path = "/Users/gav/Core/polkadot/bridges/primitives/messages" } -bp-runtime ={path = "/Users/gav/Core/polkadot/bridges/primitives/runtime" } -bp-rococo ={path = "/Users/gav/Core/polkadot/bridges/primitives/chain-rococo" } -bp-polkadot-core ={path = "/Users/gav/Core/polkadot/bridges/primitives/polkadot-core" } -bp-wococo ={path = "/Users/gav/Core/polkadot/bridges/primitives/chain-wococo" } -bridge-runtime-common ={path = "/Users/gav/Core/polkadot/bridges/bin/runtime-common" } -bp-message-dispatch ={path = "/Users/gav/Core/polkadot/bridges/primitives/message-dispatch" } -pallet-bridge-dispatch ={path = "/Users/gav/Core/polkadot/bridges/modules/dispatch" } -pallet-bridge-grandpa ={path = "/Users/gav/Core/polkadot/bridges/modules/grandpa" } -bp-header-chain ={path = "/Users/gav/Core/polkadot/bridges/primitives/header-chain" } -bp-test-utils ={path = "/Users/gav/Core/polkadot/bridges/primitives/test-utils" } -pallet-bridge-messages ={path = "/Users/gav/Core/polkadot/bridges/modules/messages" } -bp-rialto ={path = "/Users/gav/Core/polkadot/bridges/primitives/chain-rialto" } -westend-runtime ={path = "/Users/gav/Core/polkadot/runtime/westend" } -pallet-xcm-benchmarks ={path = "/Users/gav/Core/polkadot/xcm/pallet-xcm-benchmarks" } -polkadot-collator-protocol ={path = "/Users/gav/Core/polkadot/node/network/collator-protocol" } -polkadot-dispute-distribution ={path = "/Users/gav/Core/polkadot/node/network/dispute-distribution" } -polkadot-gossip-support ={path = "/Users/gav/Core/polkadot/node/network/gossip-support" } -polkadot-network-bridge ={path = "/Users/gav/Core/polkadot/node/network/bridge" } -polkadot-node-collation-generation ={path = "/Users/gav/Core/polkadot/node/collation-generation" } -polkadot-node-core-approval-voting ={path = "/Users/gav/Core/polkadot/node/core/approval-voting" } -polkadot-node-core-av-store ={path = "/Users/gav/Core/polkadot/node/core/av-store" } -polkadot-node-core-backing ={path = "/Users/gav/Core/polkadot/node/core/backing" } -polkadot-node-core-bitfield-signing ={path = "/Users/gav/Core/polkadot/node/core/bitfield-signing" } -polkadot-node-core-candidate-validation ={path = "/Users/gav/Core/polkadot/node/core/candidate-validation" } -polkadot-node-core-chain-api ={path = "/Users/gav/Core/polkadot/node/core/chain-api" } -polkadot-node-core-chain-selection ={path = "/Users/gav/Core/polkadot/node/core/chain-selection" } -polkadot-node-core-dispute-coordinator ={path = "/Users/gav/Core/polkadot/node/core/dispute-coordinator" } -polkadot-node-core-dispute-participation ={path = "/Users/gav/Core/polkadot/node/core/dispute-participation" } -polkadot-node-core-parachains-inherent ={path = "/Users/gav/Core/polkadot/node/core/parachains-inherent" } -polkadot-node-core-provisioner ={path = "/Users/gav/Core/polkadot/node/core/provisioner" } -polkadot-node-core-runtime-api ={path = "/Users/gav/Core/polkadot/node/core/runtime-api" } -polkadot-rpc ={path = "/Users/gav/Core/polkadot/rpc" } -polkadot-statement-distribution ={path = "/Users/gav/Core/polkadot/node/network/statement-distribution" } -polkadot-test-client ={path = "/Users/gav/Core/polkadot/node/test/client" } -polkadot-test-runtime ={path = "/Users/gav/Core/polkadot/runtime/test-runtime" } -polkadot-test-service ={path = "/Users/gav/Core/polkadot/node/test/service" } -xcm-executor-integration-tests ={path = "/Users/gav/Core/polkadot/xcm/xcm-executor/integration-tests" } -xcm-simulator ={path = "/Users/gav/Core/polkadot/xcm/xcm-simulator" } -xcm-simulator-example ={path = "/Users/gav/Core/polkadot/xcm/xcm-simulator/example" } -xcm-simulator-fuzzer ={path = "/Users/gav/Core/polkadot/xcm/xcm-simulator/fuzzer" } -polkadot-test-malus ={path = "/Users/gav/Core/polkadot/node/malus" } -polkadot-simnet ={path = "/Users/gav/Core/polkadot/node/test/polkadot-simnet/common" } -polkadot-simnet-node ={path = "/Users/gav/Core/polkadot/node/test/polkadot-simnet/node" } -polkadot-simnet-test ={path = "/Users/gav/Core/polkadot/node/test/polkadot-simnet/test" } -test-parachains ={path = "/Users/gav/Core/polkadot/parachain/test-parachains" } -test-parachain-adder-collator ={path = "/Users/gav/Core/polkadot/parachain/test-parachains/adder/collator" } -staking-miner ={path = "/Users/gav/Core/polkadot/utils/staking-miner" } -remote-ext-tests-bags-list ={path = "/Users/gav/Core/polkadot/utils/remote-ext-tests/bags-list" } -polkadot-voter-bags ={path = "/Users/gav/Core/polkadot/utils/voter-bags" } -polkadot ={path = "/Users/gav/Core/polkadot" } From 138f06d54cf387c26558eece6b521fa8862b2cee Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 24 Oct 2021 11:22:58 +0200 Subject: [PATCH 003/263] Undiener --- Cargo.lock | 8 ++++---- client/network/src/tests.rs | 7 ------- pallets/xcmp-queue/src/mock.rs | 2 ++ 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index decd42d333d..cfce9411bbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2472,7 +2472,7 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "Inflector", "chrono", @@ -8225,7 +8225,7 @@ dependencies = [ [[package]] name = "remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "env_logger 0.9.0", "jsonrpsee-proc-macros", @@ -10904,7 +10904,7 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "platforms", ] @@ -11451,7 +11451,7 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#632b32300eb9376767c2ae7b38e79b3f7f5329b1" +source = "git+https://github.com/paritytech/substrate?branch=master#969a70d1864fc5d5f6c378bcfd03f1b3ea434049" dependencies = [ "jsonrpsee-ws-client", "log", diff --git a/client/network/src/tests.rs b/client/network/src/tests.rs index 7124ebca85d..f3177dbe3a6 100644 --- a/client/network/src/tests.rs +++ b/client/network/src/tests.rs @@ -484,13 +484,6 @@ sp_api::mock_impl_runtime_apis! { BTreeMap::new() } - fn assumed_validation_data( - _: ParaId, - _: Hash, - ) -> Option<(PersistedValidationData, ValidationCodeHash)> { - None - } - fn validation_code_by_hash(_: ValidationCodeHash) -> Option { None } diff --git a/pallets/xcmp-queue/src/mock.rs b/pallets/xcmp-queue/src/mock.rs index 51f19bf4a50..c0dea9bd1fa 100644 --- a/pallets/xcmp-queue/src/mock.rs +++ b/pallets/xcmp-queue/src/mock.rs @@ -146,6 +146,8 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = (); type AssetClaims = (); type SubscriptionService = (); + type PalletInstancesInfo = AllPallets; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; } pub type XcmRouter = ( From 535b2545dc020e0e0bc13c56bdde0f1e3eaa714e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 24 Oct 2021 11:38:46 +0200 Subject: [PATCH 004/263] Undiener --- client/network/src/tests.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/network/src/tests.rs b/client/network/src/tests.rs index f3177dbe3a6..7124ebca85d 100644 --- a/client/network/src/tests.rs +++ b/client/network/src/tests.rs @@ -484,6 +484,13 @@ sp_api::mock_impl_runtime_apis! { BTreeMap::new() } + fn assumed_validation_data( + _: ParaId, + _: Hash, + ) -> Option<(PersistedValidationData, ValidationCodeHash)> { + None + } + fn validation_code_by_hash(_: ValidationCodeHash) -> Option { None } From 9f5c19ffa5cd72068b415ab0b5ba7c9e6bb0f38f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 26 Oct 2021 12:55:21 +0200 Subject: [PATCH 005/263] Lockfile --- Cargo.lock | 137 ++++++++++++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cfce9411bbc..bbf74a6884f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -706,6 +706,7 @@ dependencies = [ [[package]] name = "bp-header-chain" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "finality-grandpa", "frame-support", @@ -721,6 +722,7 @@ dependencies = [ [[package]] name = "bp-message-dispatch" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bp-runtime", "frame-support", @@ -732,6 +734,7 @@ dependencies = [ [[package]] name = "bp-messages" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bitvec 0.20.1", "bp-runtime", @@ -747,6 +750,7 @@ dependencies = [ [[package]] name = "bp-polkadot-core" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bp-messages", "bp-runtime", @@ -764,6 +768,7 @@ dependencies = [ [[package]] name = "bp-rialto" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bp-messages", "bp-runtime", @@ -778,6 +783,7 @@ dependencies = [ [[package]] name = "bp-rococo" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bp-messages", "bp-polkadot-core", @@ -794,6 +800,7 @@ dependencies = [ [[package]] name = "bp-runtime" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "frame-support", "hash-db", @@ -811,6 +818,7 @@ dependencies = [ [[package]] name = "bp-test-utils" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bp-header-chain", "ed25519-dalek", @@ -825,6 +833,7 @@ dependencies = [ [[package]] name = "bp-wococo" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bp-messages", "bp-polkadot-core", @@ -839,6 +848,7 @@ dependencies = [ [[package]] name = "bridge-runtime-common" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bp-message-dispatch", "bp-messages", @@ -3594,6 +3604,7 @@ dependencies = [ [[package]] name = "kusama-runtime" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "beefy-primitives", "bitvec 0.20.1", @@ -4563,6 +4574,7 @@ dependencies = [ [[package]] name = "metered-channel" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "derive_more", "futures 0.3.17", @@ -5225,6 +5237,7 @@ dependencies = [ [[package]] name = "pallet-bridge-dispatch" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bp-message-dispatch", "bp-runtime", @@ -5241,6 +5254,7 @@ dependencies = [ [[package]] name = "pallet-bridge-grandpa" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bp-header-chain", "bp-runtime", @@ -5262,6 +5276,7 @@ dependencies = [ [[package]] name = "pallet-bridge-messages" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bitvec 0.20.1", "bp-message-dispatch", @@ -5938,6 +5953,7 @@ dependencies = [ [[package]] name = "pallet-xcm" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "frame-support", "frame-system", @@ -5955,6 +5971,7 @@ dependencies = [ [[package]] name = "pallet-xcm-benchmarks" version = "0.9.8" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6474,6 +6491,7 @@ checksum = "989d43012e2ca1c4a02507c67282691a0a3207f9dc67cec596b43fe925b3d325" [[package]] name = "polkadot-approval-distribution" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "futures 0.3.17", "polkadot-node-network-protocol", @@ -6487,6 +6505,7 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "futures 0.3.17", "polkadot-node-network-protocol", @@ -6499,6 +6518,7 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "derive_more", "futures 0.3.17", @@ -6520,6 +6540,7 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "futures 0.3.17", "lru 0.7.0", @@ -6539,6 +6560,7 @@ dependencies = [ [[package]] name = "polkadot-cli" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "frame-benchmarking-cli", "futures 0.3.17", @@ -6558,6 +6580,7 @@ dependencies = [ [[package]] name = "polkadot-client" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "beefy-primitives", "frame-benchmarking", @@ -6655,6 +6678,7 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "always-assert", "derive_more", @@ -6675,6 +6699,7 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "parity-scale-codec", "parity-util-mem", @@ -6687,6 +6712,7 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "derive_more", "futures 0.3.17", @@ -6708,6 +6734,7 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", @@ -6721,6 +6748,7 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "futures 0.3.17", "futures-timer 3.0.2", @@ -6740,6 +6768,7 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "async-trait", "futures 0.3.17", @@ -6758,6 +6787,7 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "futures 0.3.17", "parity-scale-codec", @@ -6775,6 +6805,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bitvec 0.20.1", "derive_more", @@ -6802,6 +6833,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bitvec 0.20.1", "futures 0.3.17", @@ -6821,6 +6853,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bitvec 0.20.1", "futures 0.3.17", @@ -6838,6 +6871,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "futures 0.3.17", "polkadot-node-subsystem", @@ -6852,6 +6886,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "async-trait", "futures 0.3.17", @@ -6869,6 +6904,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "futures 0.3.17", "polkadot-node-subsystem", @@ -6883,6 +6919,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "futures 0.3.17", "futures-timer 3.0.2", @@ -6899,6 +6936,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bitvec 0.20.1", "derive_more", @@ -6917,6 +6955,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-participation" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "futures 0.3.17", "polkadot-node-primitives", @@ -6929,6 +6968,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "async-trait", "futures 0.3.17", @@ -6945,6 +6985,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bitvec 0.20.1", "futures 0.3.17", @@ -6959,6 +7000,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "always-assert", "assert_matches", @@ -6989,6 +7031,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "futures 0.3.17", "memory-lru", @@ -7006,6 +7049,7 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "async-std", "lazy_static", @@ -7023,6 +7067,7 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "futures 0.3.17", "futures-timer 3.0.2", @@ -7033,6 +7078,7 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "async-trait", "derive_more", @@ -7050,6 +7096,7 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bounded-vec", "futures 0.3.17", @@ -7071,6 +7118,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -7080,6 +7128,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-test-helpers" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "async-trait", "futures 0.3.17", @@ -7097,6 +7146,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "derive_more", "futures 0.3.17", @@ -7115,6 +7165,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "async-trait", "derive_more", @@ -7141,6 +7192,7 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "futures 0.3.17", "futures-timer 3.0.2", @@ -7161,6 +7213,7 @@ dependencies = [ [[package]] name = "polkadot-overseer-gen" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "async-trait", "futures 0.3.17", @@ -7177,6 +7230,7 @@ dependencies = [ [[package]] name = "polkadot-overseer-gen-proc-macro" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "proc-macro-crate 1.1.0", "proc-macro2", @@ -7187,6 +7241,7 @@ dependencies = [ [[package]] name = "polkadot-parachain" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "derive_more", "frame-support", @@ -7203,6 +7258,7 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bitvec 0.20.1", "frame-system", @@ -7232,6 +7288,7 @@ dependencies = [ [[package]] name = "polkadot-rpc" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", @@ -7262,6 +7319,7 @@ dependencies = [ [[package]] name = "polkadot-runtime" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "beefy-primitives", "bitvec 0.20.1", @@ -7338,6 +7396,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "beefy-primitives", "bitvec 0.20.1", @@ -7384,6 +7443,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "bitflags", "bitvec 0.20.1", @@ -7422,6 +7482,7 @@ dependencies = [ [[package]] name = "polkadot-service" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "async-trait", "beefy-gadget", @@ -7519,6 +7580,7 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "arrayvec 0.5.2", "derive_more", @@ -7539,6 +7601,7 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -7548,6 +7611,7 @@ dependencies = [ [[package]] name = "polkadot-test-client" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "parity-scale-codec", "polkadot-node-subsystem", @@ -7572,6 +7636,7 @@ dependencies = [ [[package]] name = "polkadot-test-runtime" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "beefy-primitives", "bitvec 0.20.1", @@ -7632,6 +7697,7 @@ dependencies = [ [[package]] name = "polkadot-test-service" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "frame-benchmarking", "frame-system", @@ -8354,6 +8420,7 @@ dependencies = [ [[package]] name = "rococo-runtime" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "beefy-primitives", "bp-messages", @@ -9835,6 +9902,7 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "slot-range-helper" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "enumn", "parity-scale-codec", @@ -12027,6 +12095,7 @@ dependencies = [ [[package]] name = "westend-runtime" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "beefy-primitives", "bitvec 0.20.1", @@ -12271,6 +12340,7 @@ dependencies = [ [[package]] name = "xcm" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "derivative", "impl-trait-for-tuples", @@ -12283,6 +12353,7 @@ dependencies = [ [[package]] name = "xcm-builder" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "frame-support", "frame-system", @@ -12302,6 +12373,7 @@ dependencies = [ [[package]] name = "xcm-executor" version = "0.9.12" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "frame-benchmarking", "frame-support", @@ -12319,6 +12391,7 @@ dependencies = [ [[package]] name = "xcm-procedural" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#ee1b80aa117516918bb0591a1ec5a27030e4c64c" dependencies = [ "proc-macro2", "quote", @@ -12388,67 +12461,3 @@ dependencies = [ "cc", "libc", ] - -[[patch.unused]] -name = "polkadot" -version = "0.9.12" - -[[patch.unused]] -name = "polkadot-simnet" -version = "0.9.12" - -[[patch.unused]] -name = "polkadot-simnet-node" -version = "0.9.12" - -[[patch.unused]] -name = "polkadot-simnet-test" -version = "0.9.12" - -[[patch.unused]] -name = "polkadot-test-malus" -version = "0.9.12" - -[[patch.unused]] -name = "polkadot-voter-bags" -version = "0.9.0" - -[[patch.unused]] -name = "remote-ext-tests-bags-list" -version = "0.9.12" - -[[patch.unused]] -name = "staking-miner" -version = "0.9.12" - -[[patch.unused]] -name = "test-parachain-adder" -version = "0.9.12" - -[[patch.unused]] -name = "test-parachain-adder-collator" -version = "0.9.12" - -[[patch.unused]] -name = "test-parachain-halt" -version = "0.9.12" - -[[patch.unused]] -name = "test-parachains" -version = "0.9.12" - -[[patch.unused]] -name = "xcm-executor-integration-tests" -version = "0.9.12" - -[[patch.unused]] -name = "xcm-simulator" -version = "0.9.12" - -[[patch.unused]] -name = "xcm-simulator-example" -version = "0.9.12" - -[[patch.unused]] -name = "xcm-simulator-fuzzer" -version = "0.9.9" From fc381c9fc8d6832a9579a554febd869a7067d8c0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 20 Jan 2022 15:14:10 +0100 Subject: [PATCH 006/263] Changes for send returning hash --- pallets/xcmp-queue/src/lib.rs | 4 ++-- primitives/utility/Cargo.toml | 1 + primitives/utility/src/lib.rs | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index cab3a18e424..7a25640f9d6 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -938,7 +938,7 @@ impl XcmpMessageSource for Pallet { /// Xcm sender for sending to a sibling parachain. impl SendXcm for Pallet { - fn send_xcm(dest: impl Into, msg: Xcm<()>) -> Result<(), SendError> { + fn send_xcm(dest: impl Into, msg: Xcm<()>) -> SendResult { let dest = dest.into(); match &dest { @@ -954,7 +954,7 @@ impl SendXcm for Pallet { ) .map_err(|e| SendError::Transport(<&'static str>::from(e)))?; Self::deposit_event(Event::XcmpMessageSent(Some(hash))); - Ok(()) + Ok(hash) }, // Anything else is unhandled. This includes a message this is meant for us. _ => Err(SendError::CannotReachDestination(dest, msg)), diff --git a/primitives/utility/Cargo.toml b/primitives/utility/Cargo.toml index 41cfa53a321..e628b9126bf 100644 --- a/primitives/utility/Cargo.toml +++ b/primitives/utility/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] # Substrate dependencies sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-trie = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index 69397247a39..a53e9286fc1 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -33,7 +33,7 @@ use xcm::{latest::prelude::*, WrapVersion}; /// for the `SendXcm` implementation. pub struct ParentAsUmp(PhantomData<(T, W)>); impl SendXcm for ParentAsUmp { - fn send_xcm(dest: impl Into, msg: Xcm<()>) -> Result<(), SendError> { + fn send_xcm(dest: impl Into, msg: Xcm<()>) -> SendResult { let dest = dest.into(); if dest.contains_parents_only(1) { @@ -41,10 +41,11 @@ impl SendXcm for ParentAsUmp { let versioned_xcm = W::wrap_version(&dest, msg).map_err(|()| SendError::DestinationUnsupported)?; let data = versioned_xcm.encode(); + let hash = data.using_encoded(sp_io::hashing::blake2_256); T::send_upward_message(data).map_err(|e| SendError::Transport(e.into()))?; - Ok(()) + Ok(hash) } else { // Anything else is unhandled. This includes a message this is meant for us. Err(SendError::CannotReachDestination(dest, msg)) From 23c26db563d1b96779388693a617da0dd51b0413 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 9 Mar 2022 03:11:16 -0800 Subject: [PATCH 007/263] Include message ID as params to execute_xcm --- pallets/xcm/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pallets/xcm/src/lib.rs b/pallets/xcm/src/lib.rs index 10c306f1be9..60c5d4b3568 100644 --- a/pallets/xcm/src/lib.rs +++ b/pallets/xcm/src/lib.rs @@ -114,7 +114,7 @@ impl DmpMessageHandler for UnlimitedDmpExecution { ) -> Weight { let mut used = 0; for (_sent_at, data) in iter { - let id = sp_io::hashing::twox_64(&data[..]); + let id = sp_io::hashing::blake2_256(&data[..]); let msg = VersionedXcm::::decode_all_with_depth_limit( MAX_XCM_DECODE_DEPTH, &mut data.as_slice(), @@ -124,7 +124,7 @@ impl DmpMessageHandler for UnlimitedDmpExecution { Err(_) => Pallet::::deposit_event(Event::InvalidFormat(id)), Ok(Err(())) => Pallet::::deposit_event(Event::UnsupportedVersion(id)), Ok(Ok(x)) => { - let outcome = T::XcmExecutor::execute_xcm(Parent, x, limit); + let outcome = T::XcmExecutor::execute_xcm(Parent, x, id, limit); used += outcome.weight_used(); Pallet::::deposit_event(Event::ExecutedDownward(id, outcome)); }, @@ -147,7 +147,7 @@ impl DmpMessageHandler for LimitAndDropDmpExecution { ) -> Weight { let mut used = 0; for (_sent_at, data) in iter { - let id = sp_io::hashing::twox_64(&data[..]); + let id = sp_io::hashing::blake2_256(&data[..]); let msg = VersionedXcm::::decode_all_with_depth_limit( MAX_XCM_DECODE_DEPTH, &mut data.as_slice(), @@ -158,7 +158,7 @@ impl DmpMessageHandler for LimitAndDropDmpExecution { Ok(Err(())) => Pallet::::deposit_event(Event::UnsupportedVersion(id)), Ok(Ok(x)) => { let weight_limit = limit.saturating_sub(used); - let outcome = T::XcmExecutor::execute_xcm(Parent, x, weight_limit); + let outcome = T::XcmExecutor::execute_xcm(Parent, x, id, weight_limit); used += outcome.weight_used(); Pallet::::deposit_event(Event::ExecutedDownward(id, outcome)); }, From 6cec5c8912aa1e99615d9d844ab8c57014ec372d Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 9 Mar 2022 18:57:24 -0800 Subject: [PATCH 008/263] Fixes --- pallets/xcm/src/lib.rs | 6 +++--- primitives/utility/src/lib.rs | 35 +++++++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/pallets/xcm/src/lib.rs b/pallets/xcm/src/lib.rs index 60c5d4b3568..4b8432a2ab8 100644 --- a/pallets/xcm/src/lib.rs +++ b/pallets/xcm/src/lib.rs @@ -67,13 +67,13 @@ pub mod pallet { pub enum Event { /// Downward message is invalid XCM. /// \[ id \] - InvalidFormat([u8; 8]), + InvalidFormat([u8; 32]), /// Downward message is unsupported version of XCM. /// \[ id \] - UnsupportedVersion([u8; 8]), + UnsupportedVersion([u8; 32]), /// Downward message executed with the given outcome. /// \[ id, outcome \] - ExecutedDownward([u8; 8], Outcome), + ExecutedDownward([u8; 32], Outcome), } /// Origin for the parachains module. diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index a53e9286fc1..0686cd05d89 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -20,7 +20,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::Encode; -use cumulus_primitives_core::UpwardMessageSender; +use cumulus_primitives_core::{MessageSendError, UpwardMessageSender}; use sp_std::marker::PhantomData; use xcm::{latest::prelude::*, WrapVersion}; @@ -33,22 +33,37 @@ use xcm::{latest::prelude::*, WrapVersion}; /// for the `SendXcm` implementation. pub struct ParentAsUmp(PhantomData<(T, W)>); impl SendXcm for ParentAsUmp { - fn send_xcm(dest: impl Into, msg: Xcm<()>) -> SendResult { - let dest = dest.into(); + type Ticket = Vec; - if dest.contains_parents_only(1) { + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult> { + let d = dest.take().ok_or(SendError::MissingArgument)?; + let xcm = msg.take().ok_or(SendError::MissingArgument)?; + + if d.contains_parents_only(1) { // An upward message for the relay chain. let versioned_xcm = - W::wrap_version(&dest, msg).map_err(|()| SendError::DestinationUnsupported)?; + W::wrap_version(&d, msg).map_err(|()| SendError::DestinationUnsupported)?; let data = versioned_xcm.encode(); - let hash = data.using_encoded(sp_io::hashing::blake2_256); - - T::send_upward_message(data).map_err(|e| SendError::Transport(e.into()))?; - Ok(hash) + Ok(data, MultiAssets::new()) } else { + *dest = Some(d.clone()); // Anything else is unhandled. This includes a message this is meant for us. - Err(SendError::CannotReachDestination(dest, msg)) + Err(SendError::NotApplicable(d, xcm)) } } + + fn deliver(blob: Vec) -> Result { + let hash = data.using_encoded(sp_io::hashing::blake2_256); + + T::send_upward_message(data).map_err(|e| match e { + MessageSendError::TooBig => SendError::ExceedsMaxMessageSize, + e => SendError::Transport(e.into()), + })?; + + Ok(hash) + } } From 005d37a1d497840b2b838c1f441f0f485d567982 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 10 Mar 2022 21:21:34 -0800 Subject: [PATCH 009/263] Fixes --- pallets/dmp-queue/src/lib.rs | 2 +- primitives/utility/src/lib.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 8becd30bb9b..23841190d19 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -241,7 +241,7 @@ pub mod pallet { Ok(0) }, Ok(Ok(x)) => { - let outcome = T::XcmExecutor::execute_xcm(Parent, x, limit); + let outcome = T::XcmExecutor::execute_xcm(Parent, x, id, limit); match outcome { Outcome::Error(XcmError::WeightLimitReached(required)) => Err((id, required)), diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index 0686cd05d89..f07e65a88b1 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -45,18 +45,18 @@ impl SendXcm for ParentAsUmp { if d.contains_parents_only(1) { // An upward message for the relay chain. let versioned_xcm = - W::wrap_version(&d, msg).map_err(|()| SendError::DestinationUnsupported)?; + W::wrap_version(&d, xcm).map_err(|()| SendError::DestinationUnsupported)?; let data = versioned_xcm.encode(); - Ok(data, MultiAssets::new()) + Ok((data, MultiAssets::new())) } else { - *dest = Some(d.clone()); + *dest = Some(d); // Anything else is unhandled. This includes a message this is meant for us. - Err(SendError::NotApplicable(d, xcm)) + Err(SendError::NotApplicable) } } - fn deliver(blob: Vec) -> Result { + fn deliver(data: Vec) -> Result { let hash = data.using_encoded(sp_io::hashing::blake2_256); T::send_upward_message(data).map_err(|e| match e { From a10ac2e7d23fccc9ada24bb788a5d688c7b1109d Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 10 Mar 2022 21:51:38 -0800 Subject: [PATCH 010/263] Fixes --- pallets/dmp-queue/src/lib.rs | 11 +++++++++ pallets/xcmp-queue/Cargo.toml | 3 ++- pallets/xcmp-queue/src/lib.rs | 43 +++++++++++++++++++++++------------ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 23841190d19..f743f2f7df6 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -422,9 +422,16 @@ mod tests { pub struct MockExec; impl ExecuteXcm for MockExec { + type Prepared = Weightless; + + fn prepare(message: Xcm<()>) -> Result> { + Err(message) + } + fn execute_xcm_in_credit( _origin: impl Into, message: Xcm, + _hash: XcmHash, weight_limit: Weight, _credit: Weight, ) -> Outcome { @@ -442,6 +449,10 @@ mod tests { TRACE.with(|q| q.borrow_mut().push((message, o.clone()))); o } + + fn charge_fees(_location: impl Into, _fees: MultiAssets) -> XcmResult { + Err(XcmError::Unimplemented) + } } impl Config for Test { diff --git a/pallets/xcmp-queue/Cargo.toml b/pallets/xcmp-queue/Cargo.toml index b9439bb4434..d457e322bd4 100644 --- a/pallets/xcmp-queue/Cargo.toml +++ b/pallets/xcmp-queue/Cargo.toml @@ -13,6 +13,7 @@ scale-info = { version = "2.0.0", default-features = false, features = ["derive" # Substrate frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } @@ -30,7 +31,6 @@ frame-benchmarking = { default-features = false, optional = true, git = "https:/ # Substrate sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } # Polkadot @@ -48,6 +48,7 @@ std = [ "frame-support/std", "frame-system/std", "log/std", + "sp-io/std", "sp-runtime/std", "sp-std/std", "xcm-executor/std", diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index 41e055bfdc0..03abce5291d 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -1079,26 +1079,41 @@ impl XcmpMessageSource for Pallet { /// Xcm sender for sending to a sibling parachain. impl SendXcm for Pallet { - fn send_xcm(dest: impl Into, msg: Xcm<()>) -> SendResult { - let dest = dest.into(); + type Ticket = (ParaId, VersionedXcm<()>); - match &dest { + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult<(ParaId, VersionedXcm<()>)> { + let d = dest.take().ok_or(SendError::MissingArgument)?; + let xcm = msg.take().ok_or(SendError::MissingArgument)?; + + match &d { // An HRMP message for a sibling parachain. MultiLocation { parents: 1, interior: X1(Parachain(id)) } => { - let versioned_xcm = T::VersionWrapper::wrap_version(&dest, msg) + let versioned_xcm = T::VersionWrapper::wrap_version(&d, xcm) .map_err(|()| SendError::DestinationUnsupported)?; - let hash = T::Hashing::hash_of(&versioned_xcm); - Self::send_fragment( - (*id).into(), - XcmpMessageFormat::ConcatenatedVersionedXcm, - versioned_xcm, - ) - .map_err(|e| SendError::Transport(<&'static str>::from(e)))?; - Self::deposit_event(Event::XcmpMessageSent(Some(hash))); - Ok(hash) + Ok(((*id).into(), versioned_xcm)) }, // Anything else is unhandled. This includes a message this is meant for us. - _ => Err(SendError::CannotReachDestination(dest, msg)), + _ => { + *dest = Some(d); + Err(SendError::NotApplicable) + } + } + } + + fn deliver((id, xcm): (ParaId, VersionedXcm<()>)) -> Result { + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + + match Self::send_fragment(id, XcmpMessageFormat::ConcatenatedVersionedXcm, xcm) { + Ok(_) => { + Self::deposit_event(Event::XcmpMessageSent(Some(hash))); + Ok(hash) + } + Err(e) => { + Err(SendError::Transport(<&'static str>::from(e))) + } } } } From 9e70ba8317849c3eff4c9f41235779b7c85f2344 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 10 Mar 2022 22:48:48 -0800 Subject: [PATCH 011/263] Fixes --- pallets/dmp-queue/src/lib.rs | 4 ++-- pallets/xcmp-queue/src/lib.rs | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index f743f2f7df6..101bead3a5f 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -345,7 +345,7 @@ mod tests { }; use sp_version::RuntimeVersion; use std::cell::RefCell; - use xcm::latest::{MultiLocation, OriginKind}; + use xcm::latest::{MultiLocation, OriginKind, Weightless}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -424,7 +424,7 @@ mod tests { impl ExecuteXcm for MockExec { type Prepared = Weightless; - fn prepare(message: Xcm<()>) -> Result> { + fn prepare(message: Xcm) -> Result { Err(message) } diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index 03abce5291d..1a9a48065c0 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -263,17 +263,17 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// Some XCM was executed ok. - Success(Option), + Success(Option), /// Some XCM failed. - Fail(Option, XcmError), + Fail(Option, XcmError), /// Bad XCM version used. - BadVersion(Option), + BadVersion(Option), /// Bad XCM format used. - BadFormat(Option), + BadFormat(Option), /// An upward message was sent to the relay chain. - UpwardMessageSent(Option), + UpwardMessageSent(Option), /// An HRMP message was sent to a sibling parachain. - XcmpMessageSent(Option), + XcmpMessageSent(Option), /// An XCM exceeded the individual message weight budget. OverweightEnqueued(ParaId, RelayBlockNumber, OverweightIndex, Weight), /// An XCM from the overweight queue was executed with the given actual weight used. @@ -594,12 +594,12 @@ impl Pallet { xcm: VersionedXcm, max_weight: Weight, ) -> Result { - let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); log::debug!("Processing XCMP-XCM: {:?}", &hash); let (result, event) = match Xcm::::try_from(xcm) { Ok(xcm) => { let location = (1, Parachain(sender.into())); - match T::XcmExecutor::execute_xcm(location, xcm, max_weight) { + match T::XcmExecutor::execute_xcm(location, xcm, hash, max_weight) { Outcome::Error(e) => (Err(e.clone()), Event::Fail(Some(hash), e)), Outcome::Complete(w) => (Ok(w), Event::Success(Some(hash))), // As far as the caller is concerned, this was dispatched without error, so @@ -781,7 +781,7 @@ impl Pallet { let index = shuffled[shuffle_index]; let sender = status[index].sender; let sender_origin = T::ControllerOriginConverter::convert_origin( - (1, Parachain(sender.into())), + (Parent, Parachain(sender.into())), OriginKind::Superuser, ); let is_controller = sender_origin @@ -1093,7 +1093,7 @@ impl SendXcm for Pallet { MultiLocation { parents: 1, interior: X1(Parachain(id)) } => { let versioned_xcm = T::VersionWrapper::wrap_version(&d, xcm) .map_err(|()| SendError::DestinationUnsupported)?; - Ok(((*id).into(), versioned_xcm)) + Ok((((*id).into(), versioned_xcm), MultiAssets::new())) }, // Anything else is unhandled. This includes a message this is meant for us. _ => { From 484e63daf110271f24db81458303932576f2112b Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 10 Mar 2022 23:25:36 -0800 Subject: [PATCH 012/263] Fixes --- pallets/dmp-queue/src/lib.rs | 8 +++++--- pallets/xcmp-queue/src/lib.rs | 12 +++++------- polkadot-parachains/pallets/ping/src/lib.rs | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 101bead3a5f..7f050f09553 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -345,7 +345,10 @@ mod tests { }; use sp_version::RuntimeVersion; use std::cell::RefCell; - use xcm::latest::{MultiLocation, OriginKind, Weightless}; + use xcm::{ + latest::{MultiLocation, OriginKind}, + v3::traits::Weightless, + }; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -428,12 +431,11 @@ mod tests { Err(message) } - fn execute_xcm_in_credit( + fn execute( _origin: impl Into, message: Xcm, _hash: XcmHash, weight_limit: Weight, - _credit: Weight, ) -> Outcome { let o = match (message.0.len(), &message.0.first()) { (1, Some(Transact { require_weight_at_most, .. })) => { diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index 1a9a48065c0..112aaf378d7 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -52,7 +52,7 @@ use rand_chacha::{ ChaChaRng, }; use scale_info::TypeInfo; -use sp_runtime::{traits::Hash, RuntimeDebug}; +use sp_runtime::RuntimeDebug; use sp_std::{convert::TryFrom, prelude::*}; use xcm::{latest::prelude::*, VersionedXcm, WrapVersion, MAX_XCM_DECODE_DEPTH}; use xcm_executor::traits::ConvertOrigin; @@ -598,7 +598,7 @@ impl Pallet { log::debug!("Processing XCMP-XCM: {:?}", &hash); let (result, event) = match Xcm::::try_from(xcm) { Ok(xcm) => { - let location = (1, Parachain(sender.into())); + let location = (Parent, Parachain(sender.into())); match T::XcmExecutor::execute_xcm(location, xcm, hash, max_weight) { Outcome::Error(e) => (Err(e.clone()), Event::Fail(Some(hash), e)), Outcome::Complete(w) => (Ok(w), Event::Success(Some(hash))), @@ -1099,7 +1099,7 @@ impl SendXcm for Pallet { _ => { *dest = Some(d); Err(SendError::NotApplicable) - } + }, } } @@ -1110,10 +1110,8 @@ impl SendXcm for Pallet { Ok(_) => { Self::deposit_event(Event::XcmpMessageSent(Some(hash))); Ok(hash) - } - Err(e) => { - Err(SendError::Transport(<&'static str>::from(e))) - } + }, + Err(e) => Err(SendError::Transport(<&'static str>::from(e))), } } } diff --git a/polkadot-parachains/pallets/ping/src/lib.rs b/polkadot-parachains/pallets/ping/src/lib.rs index 7ca75669c2d..ffac149a65f 100644 --- a/polkadot-parachains/pallets/ping/src/lib.rs +++ b/polkadot-parachains/pallets/ping/src/lib.rs @@ -89,8 +89,8 @@ pub mod pallet { *seq += 1; *seq }); - match T::XcmSender::send_xcm( - (1, Junction::Parachain(para.into())), + match send_xcm::( + (Parent, Junction::Parachain(para.into())), Xcm(vec![Transact { origin_kind: OriginKind::Native, require_weight_at_most: 1_000, @@ -165,8 +165,8 @@ pub mod pallet { let para = ensure_sibling_para(::Origin::from(origin))?; Self::deposit_event(Event::Pinged(para, seq, payload.clone())); - match T::XcmSender::send_xcm( - (1, Junction::Parachain(para.into())), + match send_xcm::( + (Parent, Junction::Parachain(para.into())), Xcm(vec![Transact { origin_kind: OriginKind::Native, require_weight_at_most: 1_000, From f474297b5b606f7951cf539920b064eb3bbe75d4 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Fri, 11 Mar 2022 00:22:39 -0800 Subject: [PATCH 013/263] Fixes --- pallets/dmp-queue/src/lib.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 7f050f09553..2774be7f0d3 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -345,10 +345,7 @@ mod tests { }; use sp_version::RuntimeVersion; use std::cell::RefCell; - use xcm::{ - latest::{MultiLocation, OriginKind}, - v3::traits::Weightless, - }; + use xcm::latest::{MultiLocation, OriginKind}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -423,6 +420,13 @@ mod tests { }) } + pub enum Weightless {} + impl PreparedMessage for Weightless { + fn weight_of(&self) -> Weight { + unreachable!() + } + } + pub struct MockExec; impl ExecuteXcm for MockExec { type Prepared = Weightless; From ed87d188c6706e73228c44235b67688d394cd245 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 11 Mar 2022 18:13:49 +0100 Subject: [PATCH 014/263] Companion fixes --- Cargo.lock | 152 +++++++++--------- pallets/xcmp-queue/src/mock.rs | 4 +- parachain-template/runtime/src/xcm_config.rs | 22 ++- .../canvas-kusama/src/xcm_config.rs | 16 +- polkadot-parachains/pallets/ping/src/lib.rs | 14 +- .../parachains-common/src/impls.rs | 7 +- .../rococo-parachain/src/lib.rs | 23 ++- polkadot-parachains/shell/src/xcm_config.rs | 13 +- .../statemine/src/xcm_config.rs | 26 ++- .../statemint/src/xcm_config.rs | 25 ++- .../westmint/src/xcm_config.rs | 22 ++- primitives/utility/src/lib.rs | 2 +- 12 files changed, 192 insertions(+), 134 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d79f6ba5227..ee303e6d5af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -732,7 +732,6 @@ dependencies = [ [[package]] name = "bp-header-chain" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "finality-grandpa", "frame-support", @@ -748,7 +747,6 @@ dependencies = [ [[package]] name = "bp-message-dispatch" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bp-runtime", "frame-support", @@ -760,7 +758,6 @@ dependencies = [ [[package]] name = "bp-messages" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bitvec", "bp-runtime", @@ -776,7 +773,6 @@ dependencies = [ [[package]] name = "bp-polkadot-core" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bp-messages", "bp-runtime", @@ -794,7 +790,6 @@ dependencies = [ [[package]] name = "bp-rococo" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bp-messages", "bp-polkadot-core", @@ -811,7 +806,6 @@ dependencies = [ [[package]] name = "bp-runtime" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "frame-support", "hash-db", @@ -829,7 +823,6 @@ dependencies = [ [[package]] name = "bp-test-utils" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bp-header-chain", "ed25519-dalek", @@ -844,7 +837,6 @@ dependencies = [ [[package]] name = "bp-wococo" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bp-messages", "bp-polkadot-core", @@ -859,7 +851,6 @@ dependencies = [ [[package]] name = "bridge-runtime-common" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bp-message-dispatch", "bp-messages", @@ -4182,7 +4173,6 @@ dependencies = [ [[package]] name = "kusama-runtime" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "beefy-primitives", "bitvec", @@ -4270,7 +4260,6 @@ dependencies = [ [[package]] name = "kusama-runtime-constants" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "frame-support", "polkadot-primitives", @@ -5134,7 +5123,6 @@ dependencies = [ [[package]] name = "metered-channel" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "derive_more", "futures 0.3.21", @@ -5791,7 +5779,6 @@ dependencies = [ [[package]] name = "pallet-bridge-dispatch" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bp-message-dispatch", "bp-runtime", @@ -5808,7 +5795,6 @@ dependencies = [ [[package]] name = "pallet-bridge-grandpa" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bp-header-chain", "bp-runtime", @@ -5830,7 +5816,6 @@ dependencies = [ [[package]] name = "pallet-bridge-messages" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bitvec", "bp-message-dispatch", @@ -6606,15 +6591,16 @@ dependencies = [ [[package]] name = "pallet-xcm" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "frame-support", "frame-system", + "impl-trait-for-tuples", "log", "parity-scale-codec", "scale-info", "serde", "sp-core", + "sp-io", "sp-runtime", "sp-std", "xcm", @@ -6624,7 +6610,6 @@ dependencies = [ [[package]] name = "pallet-xcm-benchmarks" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "frame-benchmarking", "frame-support", @@ -6632,6 +6617,7 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", + "sp-io", "sp-runtime", "sp-std", "xcm", @@ -7185,7 +7171,6 @@ dependencies = [ [[package]] name = "polkadot-approval-distribution" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "futures 0.3.21", "polkadot-node-network-protocol", @@ -7199,7 +7184,6 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "futures 0.3.21", "polkadot-node-network-protocol", @@ -7212,7 +7196,6 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "derive_more", "fatality", @@ -7235,7 +7218,6 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "fatality", "futures 0.3.21", @@ -7256,7 +7238,6 @@ dependencies = [ [[package]] name = "polkadot-cli" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "clap 3.1.6", "frame-benchmarking-cli", @@ -7279,7 +7260,6 @@ dependencies = [ [[package]] name = "polkadot-client" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "beefy-primitives", "frame-benchmarking", @@ -7385,7 +7365,6 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "always-assert", "fatality", @@ -7406,7 +7385,6 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "parity-scale-codec", "parity-util-mem", @@ -7419,7 +7397,6 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "derive_more", "fatality", @@ -7442,7 +7419,6 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", @@ -7456,7 +7432,6 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "futures 0.3.21", "futures-timer", @@ -7476,7 +7451,6 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "async-trait", "futures 0.3.21", @@ -7495,7 +7469,6 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "futures 0.3.21", "parity-scale-codec", @@ -7513,7 +7486,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bitvec", "derive_more", @@ -7541,7 +7513,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bitvec", "futures 0.3.21", @@ -7561,7 +7532,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bitvec", "futures 0.3.21", @@ -7579,7 +7549,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "futures 0.3.21", "polkadot-node-subsystem", @@ -7594,7 +7563,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "async-trait", "futures 0.3.21", @@ -7612,7 +7580,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "futures 0.3.21", "polkadot-node-subsystem", @@ -7627,7 +7594,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "futures 0.3.21", "futures-timer", @@ -7644,7 +7610,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "fatality", "futures 0.3.21", @@ -7663,7 +7628,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "async-trait", "futures 0.3.21", @@ -7680,7 +7644,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bitvec", "futures 0.3.21", @@ -7697,7 +7660,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "always-assert", "assert_matches", @@ -7727,7 +7689,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-checker" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "futures 0.3.21", "polkadot-node-primitives", @@ -7743,7 +7704,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "futures 0.3.21", "memory-lru", @@ -7761,7 +7721,6 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "async-std", "lazy_static", @@ -7779,7 +7738,6 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bs58", "futures 0.3.21", @@ -7798,7 +7756,6 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "async-trait", "fatality", @@ -7816,7 +7773,6 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bounded-vec", "futures 0.3.21", @@ -7838,7 +7794,6 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -7848,7 +7803,6 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-test-helpers" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "async-trait", "futures 0.3.21", @@ -7866,7 +7820,6 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "derive_more", "futures 0.3.21", @@ -7885,7 +7838,6 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "async-trait", "derive_more", @@ -7918,7 +7870,6 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "futures 0.3.21", "futures-timer", @@ -7939,7 +7890,6 @@ dependencies = [ [[package]] name = "polkadot-overseer-gen" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "async-trait", "futures 0.3.21", @@ -7956,7 +7906,6 @@ dependencies = [ [[package]] name = "polkadot-overseer-gen-proc-macro" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "expander 0.0.5", "proc-macro-crate 1.1.3", @@ -7968,7 +7917,6 @@ dependencies = [ [[package]] name = "polkadot-parachain" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "derive_more", "frame-support", @@ -7985,7 +7933,6 @@ dependencies = [ [[package]] name = "polkadot-performance-test" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "env_logger", "kusama-runtime", @@ -8000,7 +7947,6 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bitvec", "frame-system", @@ -8030,7 +7976,6 @@ dependencies = [ [[package]] name = "polkadot-rpc" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", @@ -8061,7 +8006,6 @@ dependencies = [ [[package]] name = "polkadot-runtime" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "beefy-primitives", "bitvec", @@ -8145,7 +8089,6 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "beefy-primitives", "bitvec", @@ -8192,7 +8135,6 @@ dependencies = [ [[package]] name = "polkadot-runtime-constants" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "frame-support", "polkadot-primitives", @@ -8204,7 +8146,6 @@ dependencies = [ [[package]] name = "polkadot-runtime-metrics" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bs58", "parity-scale-codec", @@ -8216,7 +8157,6 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "bitflags", "bitvec", @@ -8258,7 +8198,6 @@ dependencies = [ [[package]] name = "polkadot-service" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "async-trait", "beefy-gadget", @@ -8359,7 +8298,6 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "arrayvec 0.5.2", "fatality", @@ -8380,7 +8318,6 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -8390,7 +8327,6 @@ dependencies = [ [[package]] name = "polkadot-test-client" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "parity-scale-codec", "polkadot-node-subsystem", @@ -8415,7 +8351,6 @@ dependencies = [ [[package]] name = "polkadot-test-runtime" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "beefy-primitives", "bitvec", @@ -8477,7 +8412,6 @@ dependencies = [ [[package]] name = "polkadot-test-service" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "frame-benchmarking", "frame-system", @@ -9137,7 +9071,6 @@ dependencies = [ [[package]] name = "rococo-runtime" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "beefy-primitives", "bp-messages", @@ -9212,7 +9145,6 @@ dependencies = [ [[package]] name = "rococo-runtime-constants" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "frame-support", "polkadot-primitives", @@ -10801,7 +10733,6 @@ checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" [[package]] name = "slot-range-helper" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "enumn", "parity-scale-codec", @@ -12054,7 +11985,6 @@ checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" [[package]] name = "test-runtime-constants" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "frame-support", "polkadot-primitives", @@ -13052,7 +12982,6 @@ dependencies = [ [[package]] name = "westend-runtime" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "beefy-primitives", "bitvec", @@ -13138,7 +13067,6 @@ dependencies = [ [[package]] name = "westend-runtime-constants" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "frame-support", "polkadot-primitives", @@ -13358,23 +13286,23 @@ dependencies = [ [[package]] name = "xcm" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "derivative", "impl-trait-for-tuples", "log", "parity-scale-codec", "scale-info", + "sp-io", "xcm-procedural", ] [[package]] name = "xcm-builder" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "frame-support", "frame-system", + "impl-trait-for-tuples", "log", "pallet-transaction-payment", "parity-scale-codec", @@ -13391,7 +13319,6 @@ dependencies = [ [[package]] name = "xcm-executor" version = "0.9.17" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "frame-benchmarking", "frame-support", @@ -13409,7 +13336,6 @@ dependencies = [ [[package]] name = "xcm-procedural" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#dead598b50f8b946ed90cd437a38e73231d768a8" dependencies = [ "Inflector", "proc-macro2", @@ -13480,3 +13406,71 @@ dependencies = [ "cc", "libc", ] + +[[patch.unused]] +name = "polkadot" +version = "0.9.17" + +[[patch.unused]] +name = "polkadot-primitives-test-helpers" +version = "0.9.17" + +[[patch.unused]] +name = "polkadot-test-malus" +version = "0.9.17" + +[[patch.unused]] +name = "polkadot-voter-bags" +version = "0.9.17" + +[[patch.unused]] +name = "remote-ext-tests-bags-list" +version = "0.9.17" + +[[patch.unused]] +name = "staking-miner" +version = "0.9.17" + +[[patch.unused]] +name = "test-parachain-adder" +version = "0.9.17" + +[[patch.unused]] +name = "test-parachain-adder-collator" +version = "0.9.17" + +[[patch.unused]] +name = "test-parachain-halt" +version = "0.9.17" + +[[patch.unused]] +name = "test-parachain-undying" +version = "0.9.17" + +[[patch.unused]] +name = "test-parachain-undying-collator" +version = "0.9.17" + +[[patch.unused]] +name = "test-parachains" +version = "0.9.17" + +[[patch.unused]] +name = "xcm-executor-integration-tests" +version = "0.9.17" + +[[patch.unused]] +name = "xcm-simulator" +version = "0.9.17" + +[[patch.unused]] +name = "xcm-simulator-example" +version = "0.9.17" + +[[patch.unused]] +name = "xcm-simulator-fuzzer" +version = "0.9.17" + +[[patch.unused]] +name = "zombienet-backchannel" +version = "0.9.17" diff --git a/pallets/xcmp-queue/src/mock.rs b/pallets/xcmp-queue/src/mock.rs index 4d8bf0b2a41..18a099e5ab3 100644 --- a/pallets/xcmp-queue/src/mock.rs +++ b/pallets/xcmp-queue/src/mock.rs @@ -113,7 +113,7 @@ impl cumulus_pallet_parachain_system::Config for Test { parameter_types! { pub const RelayChain: MultiLocation = MultiLocation::parent(); - pub Ancestry: MultiLocation = X1(Parachain(1u32.into())).into(); + pub UniversalLocation: InteriorMultiLocation = X1(Parachain(1u32.into())).into(); pub UnitWeightCost: Weight = 1_000_000; pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; @@ -144,7 +144,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = (); type IsReserve = NativeAsset; type IsTeleporter = NativeAsset; - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Barrier = (); type Weigher = FixedWeightBounds; type Trader = (); diff --git a/parachain-template/runtime/src/xcm_config.rs b/parachain-template/runtime/src/xcm_config.rs index 0684020fea2..bf7a717c26c 100644 --- a/parachain-template/runtime/src/xcm_config.rs +++ b/parachain-template/runtime/src/xcm_config.rs @@ -4,7 +4,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{Everything, Nothing}, + traits::{Everything, Nothing, ConstU32}, weights::Weight, }; use pallet_xcm::XcmPassthrough; @@ -22,9 +22,9 @@ use xcm_executor::XcmExecutor; parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Any; + pub const RelayNetwork: Option = None; pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); + pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used @@ -104,7 +104,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = NativeAsset; type IsTeleporter = (); // Teleporting is disabled. - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = @@ -113,8 +113,13 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; - type PalletInstanceInfo = (); + type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. @@ -141,13 +146,18 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Nothing; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; // ^ Override for AdvertisedXcmVersion default type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/polkadot-parachains/canvas-kusama/src/xcm_config.rs b/polkadot-parachains/canvas-kusama/src/xcm_config.rs index c129d4d0162..33d563f165b 100644 --- a/polkadot-parachains/canvas-kusama/src/xcm_config.rs +++ b/polkadot-parachains/canvas-kusama/src/xcm_config.rs @@ -40,7 +40,7 @@ parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); pub const RelayNetwork: NetworkId = NetworkId::Any; pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); + pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); pub const Local: MultiLocation = Here.into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); pub const ExecutiveBody: BodyId = BodyId::Executive; @@ -136,7 +136,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = NativeAsset; type IsTeleporter = NativeAsset; - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = UsingComponents; @@ -144,6 +144,11 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; } /// Converts a local signed origin into an XCM multilocation. @@ -172,11 +177,16 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/polkadot-parachains/pallets/ping/src/lib.rs b/polkadot-parachains/pallets/ping/src/lib.rs index ffac149a65f..621190bc79e 100644 --- a/polkadot-parachains/pallets/ping/src/lib.rs +++ b/polkadot-parachains/pallets/ping/src/lib.rs @@ -69,9 +69,9 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - PingSent(ParaId, u32, Vec), + PingSent(ParaId, u32, Vec, XcmHash, MultiAssets), Pinged(ParaId, u32, Vec), - PongSent(ParaId, u32, Vec), + PongSent(ParaId, u32, Vec, XcmHash, MultiAssets), Ponged(ParaId, u32, Vec, T::BlockNumber), ErrorSendingPing(SendError, ParaId, u32, Vec), ErrorSendingPong(SendError, ParaId, u32, Vec), @@ -90,7 +90,7 @@ pub mod pallet { *seq }); match send_xcm::( - (Parent, Junction::Parachain(para.into())), + (Parent, Junction::Parachain(para.into())).into(), Xcm(vec![Transact { origin_kind: OriginKind::Native, require_weight_at_most: 1_000, @@ -102,9 +102,9 @@ pub mod pallet { .into(), }]), ) { - Ok(()) => { + Ok((hash, cost)) => { Pings::::insert(seq, n); - Self::deposit_event(Event::PingSent(para, seq, payload)); + Self::deposit_event(Event::PingSent(para, seq, payload, hash, cost)); }, Err(e) => { Self::deposit_event(Event::ErrorSendingPing(e, para, seq, payload)); @@ -166,7 +166,7 @@ pub mod pallet { Self::deposit_event(Event::Pinged(para, seq, payload.clone())); match send_xcm::( - (Parent, Junction::Parachain(para.into())), + (Parent, Junction::Parachain(para.into())).into(), Xcm(vec![Transact { origin_kind: OriginKind::Native, require_weight_at_most: 1_000, @@ -178,7 +178,7 @@ pub mod pallet { .into(), }]), ) { - Ok(()) => Self::deposit_event(Event::PongSent(para, seq, payload)), + Ok((hash, cost)) => Self::deposit_event(Event::PongSent(para, seq, payload, hash, cost)), Err(e) => Self::deposit_event(Event::ErrorSendingPong(e, para, seq, payload)), } Ok(()) diff --git a/polkadot-parachains/parachains-common/src/impls.rs b/polkadot-parachains/parachains-common/src/impls.rs index 4a2411c2cf3..2b66cd416d1 100644 --- a/polkadot-parachains/parachains-common/src/impls.rs +++ b/polkadot-parachains/parachains-common/src/impls.rs @@ -18,13 +18,12 @@ use frame_support::traits::{ fungibles::{self, Balanced, CreditOf}, - Contains, Currency, Get, Imbalance, OnUnbalanced, + Contains, ContainsPair, Currency, Get, Imbalance, OnUnbalanced, }; use pallet_asset_tx_payment::HandleCredit; use sp_runtime::traits::Zero; use sp_std::marker::PhantomData; use xcm::latest::{AssetId, Fungibility::Fungible, MultiAsset, MultiLocation}; -use xcm_executor::traits::FilterAssetLocation; /// Type alias to conveniently refer to the `Currency::NegativeImbalance` associated type. pub type NegativeImbalance = as Currency< @@ -100,8 +99,8 @@ where /// Asset filter that allows all assets from a certain location. pub struct AssetsFrom(PhantomData); -impl> FilterAssetLocation for AssetsFrom { - fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { +impl> ContainsPair for AssetsFrom { + fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool { let loc = T::get(); &loc == origin && matches!(asset, MultiAsset { id: AssetId::Concrete(asset_loc), fun: Fungible(_a) } diff --git a/polkadot-parachains/rococo-parachain/src/lib.rs b/polkadot-parachains/rococo-parachain/src/lib.rs index b7c30ebb1c9..8697a63fbac 100644 --- a/polkadot-parachains/rococo-parachain/src/lib.rs +++ b/polkadot-parachains/rococo-parachain/src/lib.rs @@ -22,6 +22,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +use frame_support::traits::{Nothing, ConstU32}; use sp_api::impl_runtime_apis; use sp_core::OpaqueMetadata; use sp_runtime::{ @@ -62,7 +63,7 @@ use parachains_common::{ }; use xcm_builder::{ AllowKnownQueryResponses, AllowSubscriptionsFrom, AsPrefixedGeneralIndex, - ConvertedConcreteAssetId, FungiblesAdapter, + ConvertedConcreteId, FungiblesAdapter, }; use xcm_executor::traits::JustTry; @@ -275,7 +276,7 @@ parameter_types! { pub const RocLocation: MultiLocation = MultiLocation::parent(); pub const RococoNetwork: NetworkId = NetworkId::Polkadot; pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); + pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); } @@ -310,7 +311,7 @@ pub type FungiblesTransactor = FungiblesAdapter< // Use this fungibles implementation: Assets, // Use this currency when it is a fungible asset matching the given location or name: - ConvertedConcreteAssetId< + ConvertedConcreteId< AssetId, u64, AsPrefixedGeneralIndex, @@ -403,7 +404,7 @@ impl Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = Reserves; type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of ROC - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = UsingComponents, RocLocation, AccountId, Balances, ()>; @@ -413,6 +414,11 @@ impl Config for XcmConfig { type SubscriptionService = PolkadotXcm; type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. @@ -435,13 +441,18 @@ impl pallet_xcm::Config for Runtime { type XcmExecuteFilter = Everything; type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; - type XcmReserveTransferFilter = frame_support::traits::Nothing; + type XcmReserveTransferFilter = Nothing; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/polkadot-parachains/shell/src/xcm_config.rs b/polkadot-parachains/shell/src/xcm_config.rs index dad6b053a55..88bc28b7285 100644 --- a/polkadot-parachains/shell/src/xcm_config.rs +++ b/polkadot-parachains/shell/src/xcm_config.rs @@ -14,7 +14,7 @@ // limitations under the License. use super::{AccountId, Call, Event, Origin, ParachainInfo, Runtime}; -use frame_support::{match_types, parameter_types, weights::Weight}; +use frame_support::{match_types, parameter_types, weights::Weight, traits::Nothing}; use xcm::latest::prelude::*; use xcm_builder::{ AllowUnpaidExecutionFrom, FixedWeightBounds, LocationInverter, ParentAsSuperuser, @@ -24,7 +24,7 @@ use xcm_builder::{ parameter_types! { pub const RococoLocation: MultiLocation = MultiLocation::parent(); pub const RococoNetwork: NetworkId = NetworkId::Polkadot; - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); + pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); } /// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, @@ -59,7 +59,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = (); // balances not supported type IsTeleporter = (); // balances not supported - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Barrier = AllowUnpaidExecutionFrom; type Weigher = FixedWeightBounds; // balances not supported type Trader = (); // balances not supported @@ -67,8 +67,13 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = (); // don't trap for now type AssetClaims = (); // don't claim for now type SubscriptionService = (); // don't handle subscriptions for now - type PalletInstanceInfo = (); + type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/polkadot-parachains/statemine/src/xcm_config.rs b/polkadot-parachains/statemine/src/xcm_config.rs index 726603d5789..5adcf22d066 100644 --- a/polkadot-parachains/statemine/src/xcm_config.rs +++ b/polkadot-parachains/statemine/src/xcm_config.rs @@ -19,7 +19,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{Everything, Nothing, PalletInfoAccess}, + traits::{Everything, Nothing, PalletInfoAccess, ConstU32}, weights::Weight, }; use pallet_xcm::XcmPassthrough; @@ -29,7 +29,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteAssetId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, @@ -41,8 +41,8 @@ parameter_types! { pub const KsmLocation: MultiLocation = MultiLocation::parent(); pub const RelayNetwork: NetworkId = NetworkId::Kusama; pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); - pub const Local: MultiLocation = Here.into(); + pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); + pub const Local: MultiLocation = Here.into_location(); pub AssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); @@ -79,7 +79,7 @@ pub type FungiblesTransactor = FungiblesAdapter< // Use this fungibles implementation: Assets, // Use this currency when it is a fungible asset matching the given location or name: - ConvertedConcreteAssetId< + ConvertedConcreteId< AssetId, Balance, AsPrefixedGeneralIndex, @@ -159,7 +159,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = NativeAsset; type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of KSM - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = @@ -168,8 +168,13 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; - type PalletInstanceInfo = (); + type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; } /// Converts a local signed origin into an XCM multilocation. @@ -198,11 +203,16 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/polkadot-parachains/statemint/src/xcm_config.rs b/polkadot-parachains/statemint/src/xcm_config.rs index 380282df56b..e12578567b7 100644 --- a/polkadot-parachains/statemint/src/xcm_config.rs +++ b/polkadot-parachains/statemint/src/xcm_config.rs @@ -19,7 +19,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{Everything, Nothing, PalletInfoAccess}, + traits::{Everything, Nothing, PalletInfoAccess, ConstU32}, weights::Weight, }; use pallet_xcm::XcmPassthrough; @@ -29,7 +29,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteAssetId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, @@ -41,7 +41,7 @@ parameter_types! { pub const DotLocation: MultiLocation = MultiLocation::parent(); pub const RelayNetwork: NetworkId = NetworkId::Polkadot; pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); + pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); pub const Local: MultiLocation = Here.into(); pub AssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); @@ -79,7 +79,7 @@ pub type FungiblesTransactor = FungiblesAdapter< // Use this fungibles implementation: Assets, // Use this currency when it is a fungible asset matching the given location or name: - ConvertedConcreteAssetId< + ConvertedConcreteId< AssetId, Balance, AsPrefixedGeneralIndex, @@ -159,7 +159,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = NativeAsset; type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of DOT - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = @@ -168,8 +168,13 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; - type PalletInstanceInfo = (); + type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; } /// Converts a local signed origin into an XCM multilocation. @@ -198,12 +203,16 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Nothing; type XcmReserveTransferFilter = Nothing; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; -} + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>;} impl cumulus_pallet_xcm::Config for Runtime { type Event = Event; diff --git a/polkadot-parachains/westmint/src/xcm_config.rs b/polkadot-parachains/westmint/src/xcm_config.rs index e59336864a2..54acfeb2a8a 100644 --- a/polkadot-parachains/westmint/src/xcm_config.rs +++ b/polkadot-parachains/westmint/src/xcm_config.rs @@ -29,7 +29,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteAssetId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, @@ -41,7 +41,7 @@ parameter_types! { pub const WestendLocation: MultiLocation = MultiLocation::parent(); pub RelayNetwork: NetworkId = NetworkId::Named(b"Westend".to_vec()); pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); + pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); pub const Local: MultiLocation = Here.into(); pub AssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); @@ -79,7 +79,7 @@ pub type FungiblesTransactor = FungiblesAdapter< // Use this fungibles implementation: Assets, // Use this currency when it is a fungible asset matching the given location or name: - ConvertedConcreteAssetId< + ConvertedConcreteId< AssetId, Balance, AsPrefixedGeneralIndex, @@ -155,7 +155,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = NativeAsset; type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of WND - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = @@ -164,8 +164,13 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; - type PalletInstanceInfo = (); + type PalletInstancesInfo = (); type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. @@ -190,11 +195,16 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type LocationInverter = LocationInverter; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index f07e65a88b1..d48045dbbb4 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -21,7 +21,7 @@ use codec::Encode; use cumulus_primitives_core::{MessageSendError, UpwardMessageSender}; -use sp_std::marker::PhantomData; +use sp_std::{prelude::*, marker::PhantomData}; use xcm::{latest::prelude::*, WrapVersion}; /// Xcm router which recognises the `Parent` destination and handles it by sending the message into From e5cdda0d15aa8ee0edd8832bf94cd2f1991ecea9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 11 Mar 2022 18:28:32 +0100 Subject: [PATCH 015/263] Formatting --- parachain-template/runtime/src/xcm_config.rs | 2 +- polkadot-parachains/pallets/ping/src/lib.rs | 3 ++- polkadot-parachains/rococo-parachain/src/lib.rs | 6 +++--- polkadot-parachains/shell/src/xcm_config.rs | 2 +- polkadot-parachains/statemine/src/xcm_config.rs | 6 +++--- polkadot-parachains/statemint/src/xcm_config.rs | 9 +++++---- polkadot-parachains/westmint/src/xcm_config.rs | 4 ++-- primitives/utility/src/lib.rs | 2 +- 8 files changed, 18 insertions(+), 16 deletions(-) diff --git a/parachain-template/runtime/src/xcm_config.rs b/parachain-template/runtime/src/xcm_config.rs index bf7a717c26c..fc394a05d44 100644 --- a/parachain-template/runtime/src/xcm_config.rs +++ b/parachain-template/runtime/src/xcm_config.rs @@ -4,7 +4,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{Everything, Nothing, ConstU32}, + traits::{ConstU32, Everything, Nothing}, weights::Weight, }; use pallet_xcm::XcmPassthrough; diff --git a/polkadot-parachains/pallets/ping/src/lib.rs b/polkadot-parachains/pallets/ping/src/lib.rs index 621190bc79e..aa83a2ce5b5 100644 --- a/polkadot-parachains/pallets/ping/src/lib.rs +++ b/polkadot-parachains/pallets/ping/src/lib.rs @@ -178,7 +178,8 @@ pub mod pallet { .into(), }]), ) { - Ok((hash, cost)) => Self::deposit_event(Event::PongSent(para, seq, payload, hash, cost)), + Ok((hash, cost)) => + Self::deposit_event(Event::PongSent(para, seq, payload, hash, cost)), Err(e) => Self::deposit_event(Event::ErrorSendingPong(e, para, seq, payload)), } Ok(()) diff --git a/polkadot-parachains/rococo-parachain/src/lib.rs b/polkadot-parachains/rococo-parachain/src/lib.rs index 8697a63fbac..41b22f6d38c 100644 --- a/polkadot-parachains/rococo-parachain/src/lib.rs +++ b/polkadot-parachains/rococo-parachain/src/lib.rs @@ -22,7 +22,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -use frame_support::traits::{Nothing, ConstU32}; +use frame_support::traits::{ConstU32, Nothing}; use sp_api::impl_runtime_apis; use sp_core::OpaqueMetadata; use sp_runtime::{ @@ -62,8 +62,8 @@ use parachains_common::{ AssetId, }; use xcm_builder::{ - AllowKnownQueryResponses, AllowSubscriptionsFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, FungiblesAdapter, + AllowKnownQueryResponses, AllowSubscriptionsFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, + FungiblesAdapter, }; use xcm_executor::traits::JustTry; diff --git a/polkadot-parachains/shell/src/xcm_config.rs b/polkadot-parachains/shell/src/xcm_config.rs index 88bc28b7285..0d491043af1 100644 --- a/polkadot-parachains/shell/src/xcm_config.rs +++ b/polkadot-parachains/shell/src/xcm_config.rs @@ -14,7 +14,7 @@ // limitations under the License. use super::{AccountId, Call, Event, Origin, ParachainInfo, Runtime}; -use frame_support::{match_types, parameter_types, weights::Weight, traits::Nothing}; +use frame_support::{match_types, parameter_types, traits::Nothing, weights::Weight}; use xcm::latest::prelude::*; use xcm_builder::{ AllowUnpaidExecutionFrom, FixedWeightBounds, LocationInverter, ParentAsSuperuser, diff --git a/polkadot-parachains/statemine/src/xcm_config.rs b/polkadot-parachains/statemine/src/xcm_config.rs index 5adcf22d066..a1f19a919cb 100644 --- a/polkadot-parachains/statemine/src/xcm_config.rs +++ b/polkadot-parachains/statemine/src/xcm_config.rs @@ -19,7 +19,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{Everything, Nothing, PalletInfoAccess, ConstU32}, + traits::{ConstU32, Everything, Nothing, PalletInfoAccess}, weights::Weight, }; use pallet_xcm::XcmPassthrough; @@ -29,8 +29,8 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, - FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, + IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, diff --git a/polkadot-parachains/statemint/src/xcm_config.rs b/polkadot-parachains/statemint/src/xcm_config.rs index e12578567b7..52a7e845736 100644 --- a/polkadot-parachains/statemint/src/xcm_config.rs +++ b/polkadot-parachains/statemint/src/xcm_config.rs @@ -19,7 +19,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{Everything, Nothing, PalletInfoAccess, ConstU32}, + traits::{ConstU32, Everything, Nothing, PalletInfoAccess}, weights::Weight, }; use pallet_xcm::XcmPassthrough; @@ -29,8 +29,8 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, - FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, + IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, @@ -212,7 +212,8 @@ impl pallet_xcm::Config for Runtime { type CurrencyMatcher = (); type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; - type MaxLockers = ConstU32<8>;} + type MaxLockers = ConstU32<8>; +} impl cumulus_pallet_xcm::Config for Runtime { type Event = Event; diff --git a/polkadot-parachains/westmint/src/xcm_config.rs b/polkadot-parachains/westmint/src/xcm_config.rs index 54acfeb2a8a..5785254b340 100644 --- a/polkadot-parachains/westmint/src/xcm_config.rs +++ b/polkadot-parachains/westmint/src/xcm_config.rs @@ -29,8 +29,8 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, - FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, + IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index d48045dbbb4..f99ebce9ec6 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -21,7 +21,7 @@ use codec::Encode; use cumulus_primitives_core::{MessageSendError, UpwardMessageSender}; -use sp_std::{prelude::*, marker::PhantomData}; +use sp_std::{marker::PhantomData, prelude::*}; use xcm::{latest::prelude::*, WrapVersion}; /// Xcm router which recognises the `Parent` destination and handles it by sending the message into From 8e73c005e9618efd348f437095204d8626cab535 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 11 Mar 2022 18:45:17 +0100 Subject: [PATCH 016/263] Fixes --- Cargo.lock | 152 +++++++++--------- .../canvas-kusama/src/xcm_config.rs | 8 +- .../statemint/src/xcm_config.rs | 2 +- .../westmint/src/xcm_config.rs | 10 +- 4 files changed, 84 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c05122fe15d..9a49377747d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -732,7 +732,6 @@ dependencies = [ [[package]] name = "bp-header-chain" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "finality-grandpa", "frame-support", @@ -748,7 +747,6 @@ dependencies = [ [[package]] name = "bp-message-dispatch" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bp-runtime", "frame-support", @@ -760,7 +758,6 @@ dependencies = [ [[package]] name = "bp-messages" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bitvec", "bp-runtime", @@ -776,7 +773,6 @@ dependencies = [ [[package]] name = "bp-polkadot-core" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bp-messages", "bp-runtime", @@ -794,7 +790,6 @@ dependencies = [ [[package]] name = "bp-rococo" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bp-messages", "bp-polkadot-core", @@ -811,7 +806,6 @@ dependencies = [ [[package]] name = "bp-runtime" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "frame-support", "hash-db", @@ -829,7 +823,6 @@ dependencies = [ [[package]] name = "bp-test-utils" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bp-header-chain", "ed25519-dalek", @@ -844,7 +837,6 @@ dependencies = [ [[package]] name = "bp-wococo" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bp-messages", "bp-polkadot-core", @@ -859,7 +851,6 @@ dependencies = [ [[package]] name = "bridge-runtime-common" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bp-message-dispatch", "bp-messages", @@ -4182,7 +4173,6 @@ dependencies = [ [[package]] name = "kusama-runtime" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "beefy-primitives", "bitvec", @@ -4270,7 +4260,6 @@ dependencies = [ [[package]] name = "kusama-runtime-constants" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "frame-support", "polkadot-primitives", @@ -5134,7 +5123,6 @@ dependencies = [ [[package]] name = "metered-channel" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "derive_more", "futures 0.3.21", @@ -5791,7 +5779,6 @@ dependencies = [ [[package]] name = "pallet-bridge-dispatch" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bp-message-dispatch", "bp-runtime", @@ -5808,7 +5795,6 @@ dependencies = [ [[package]] name = "pallet-bridge-grandpa" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bp-header-chain", "bp-runtime", @@ -5830,7 +5816,6 @@ dependencies = [ [[package]] name = "pallet-bridge-messages" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bitvec", "bp-message-dispatch", @@ -6606,15 +6591,16 @@ dependencies = [ [[package]] name = "pallet-xcm" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "frame-support", "frame-system", + "impl-trait-for-tuples", "log", "parity-scale-codec", "scale-info", "serde", "sp-core", + "sp-io", "sp-runtime", "sp-std", "xcm", @@ -6624,7 +6610,6 @@ dependencies = [ [[package]] name = "pallet-xcm-benchmarks" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "frame-benchmarking", "frame-support", @@ -6632,6 +6617,7 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", + "sp-io", "sp-runtime", "sp-std", "xcm", @@ -7186,7 +7172,6 @@ dependencies = [ [[package]] name = "polkadot-approval-distribution" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "futures 0.3.21", "polkadot-node-network-protocol", @@ -7200,7 +7185,6 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "futures 0.3.21", "polkadot-node-network-protocol", @@ -7213,7 +7197,6 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "derive_more", "fatality", @@ -7236,7 +7219,6 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "fatality", "futures 0.3.21", @@ -7257,7 +7239,6 @@ dependencies = [ [[package]] name = "polkadot-cli" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "clap 3.1.6", "frame-benchmarking-cli", @@ -7280,7 +7261,6 @@ dependencies = [ [[package]] name = "polkadot-client" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "beefy-primitives", "frame-benchmarking", @@ -7386,7 +7366,6 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "always-assert", "fatality", @@ -7407,7 +7386,6 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "parity-scale-codec", "parity-util-mem", @@ -7420,7 +7398,6 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "derive_more", "fatality", @@ -7443,7 +7420,6 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", @@ -7457,7 +7433,6 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "futures 0.3.21", "futures-timer", @@ -7477,7 +7452,6 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "async-trait", "futures 0.3.21", @@ -7496,7 +7470,6 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "futures 0.3.21", "parity-scale-codec", @@ -7514,7 +7487,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bitvec", "derive_more", @@ -7542,7 +7514,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bitvec", "futures 0.3.21", @@ -7562,7 +7533,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bitvec", "futures 0.3.21", @@ -7580,7 +7550,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "futures 0.3.21", "polkadot-node-subsystem", @@ -7595,7 +7564,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "async-trait", "futures 0.3.21", @@ -7613,7 +7581,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "futures 0.3.21", "polkadot-node-subsystem", @@ -7628,7 +7595,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "futures 0.3.21", "futures-timer", @@ -7645,7 +7611,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "fatality", "futures 0.3.21", @@ -7664,7 +7629,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "async-trait", "futures 0.3.21", @@ -7681,7 +7645,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bitvec", "futures 0.3.21", @@ -7698,7 +7661,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "always-assert", "assert_matches", @@ -7728,7 +7690,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-checker" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "futures 0.3.21", "polkadot-node-primitives", @@ -7744,7 +7705,6 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "futures 0.3.21", "memory-lru", @@ -7762,7 +7722,6 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "async-std", "lazy_static", @@ -7780,7 +7739,6 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bs58", "futures 0.3.21", @@ -7799,7 +7757,6 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "async-trait", "fatality", @@ -7817,7 +7774,6 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bounded-vec", "futures 0.3.21", @@ -7839,7 +7795,6 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -7849,7 +7804,6 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-test-helpers" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "async-trait", "futures 0.3.21", @@ -7867,7 +7821,6 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "derive_more", "futures 0.3.21", @@ -7886,7 +7839,6 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "async-trait", "derive_more", @@ -7919,7 +7871,6 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "futures 0.3.21", "futures-timer", @@ -7940,7 +7891,6 @@ dependencies = [ [[package]] name = "polkadot-overseer-gen" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "async-trait", "futures 0.3.21", @@ -7957,7 +7907,6 @@ dependencies = [ [[package]] name = "polkadot-overseer-gen-proc-macro" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "expander 0.0.6", "proc-macro-crate 1.1.3", @@ -7969,7 +7918,6 @@ dependencies = [ [[package]] name = "polkadot-parachain" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "derive_more", "frame-support", @@ -7986,7 +7934,6 @@ dependencies = [ [[package]] name = "polkadot-performance-test" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "env_logger", "kusama-runtime", @@ -8001,7 +7948,6 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bitvec", "frame-system", @@ -8031,7 +7977,6 @@ dependencies = [ [[package]] name = "polkadot-rpc" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", @@ -8062,7 +8007,6 @@ dependencies = [ [[package]] name = "polkadot-runtime" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "beefy-primitives", "bitvec", @@ -8146,7 +8090,6 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "beefy-primitives", "bitvec", @@ -8193,7 +8136,6 @@ dependencies = [ [[package]] name = "polkadot-runtime-constants" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "frame-support", "polkadot-primitives", @@ -8205,7 +8147,6 @@ dependencies = [ [[package]] name = "polkadot-runtime-metrics" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bs58", "parity-scale-codec", @@ -8217,7 +8158,6 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "bitflags", "bitvec", @@ -8259,7 +8199,6 @@ dependencies = [ [[package]] name = "polkadot-service" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "async-trait", "beefy-gadget", @@ -8360,7 +8299,6 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "arrayvec 0.5.2", "fatality", @@ -8381,7 +8319,6 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -8391,7 +8328,6 @@ dependencies = [ [[package]] name = "polkadot-test-client" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "parity-scale-codec", "polkadot-node-subsystem", @@ -8416,7 +8352,6 @@ dependencies = [ [[package]] name = "polkadot-test-runtime" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "beefy-primitives", "bitvec", @@ -8478,7 +8413,6 @@ dependencies = [ [[package]] name = "polkadot-test-service" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "frame-benchmarking", "frame-system", @@ -9137,7 +9071,6 @@ dependencies = [ [[package]] name = "rococo-runtime" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "beefy-primitives", "bp-messages", @@ -9212,7 +9145,6 @@ dependencies = [ [[package]] name = "rococo-runtime-constants" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "frame-support", "polkadot-primitives", @@ -10801,7 +10733,6 @@ checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" [[package]] name = "slot-range-helper" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "enumn", "parity-scale-codec", @@ -12054,7 +11985,6 @@ checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" [[package]] name = "test-runtime-constants" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "frame-support", "polkadot-primitives", @@ -13052,7 +12982,6 @@ dependencies = [ [[package]] name = "westend-runtime" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "beefy-primitives", "bitvec", @@ -13138,7 +13067,6 @@ dependencies = [ [[package]] name = "westend-runtime-constants" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "frame-support", "polkadot-primitives", @@ -13358,23 +13286,23 @@ dependencies = [ [[package]] name = "xcm" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "derivative", "impl-trait-for-tuples", "log", "parity-scale-codec", "scale-info", + "sp-io", "xcm-procedural", ] [[package]] name = "xcm-builder" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "frame-support", "frame-system", + "impl-trait-for-tuples", "log", "pallet-transaction-payment", "parity-scale-codec", @@ -13391,7 +13319,6 @@ dependencies = [ [[package]] name = "xcm-executor" version = "0.9.18" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "frame-benchmarking", "frame-support", @@ -13409,7 +13336,6 @@ dependencies = [ [[package]] name = "xcm-procedural" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot?branch=master#e6fa871e39bab8ccec27070b3b93a2e5e50ff0a4" dependencies = [ "Inflector", "proc-macro2", @@ -13480,3 +13406,71 @@ dependencies = [ "cc", "libc", ] + +[[patch.unused]] +name = "polkadot" +version = "0.9.18" + +[[patch.unused]] +name = "polkadot-primitives-test-helpers" +version = "0.9.18" + +[[patch.unused]] +name = "polkadot-test-malus" +version = "0.9.18" + +[[patch.unused]] +name = "polkadot-voter-bags" +version = "0.9.18" + +[[patch.unused]] +name = "remote-ext-tests-bags-list" +version = "0.9.18" + +[[patch.unused]] +name = "staking-miner" +version = "0.9.18" + +[[patch.unused]] +name = "test-parachain-adder" +version = "0.9.18" + +[[patch.unused]] +name = "test-parachain-adder-collator" +version = "0.9.18" + +[[patch.unused]] +name = "test-parachain-halt" +version = "0.9.18" + +[[patch.unused]] +name = "test-parachain-undying" +version = "0.9.18" + +[[patch.unused]] +name = "test-parachain-undying-collator" +version = "0.9.18" + +[[patch.unused]] +name = "test-parachains" +version = "0.9.18" + +[[patch.unused]] +name = "xcm-executor-integration-tests" +version = "0.9.18" + +[[patch.unused]] +name = "xcm-simulator" +version = "0.9.18" + +[[patch.unused]] +name = "xcm-simulator-example" +version = "0.9.18" + +[[patch.unused]] +name = "xcm-simulator-fuzzer" +version = "0.9.18" + +[[patch.unused]] +name = "zombienet-backchannel" +version = "0.9.18" diff --git a/polkadot-parachains/canvas-kusama/src/xcm_config.rs b/polkadot-parachains/canvas-kusama/src/xcm_config.rs index 33d563f165b..6249410b1a8 100644 --- a/polkadot-parachains/canvas-kusama/src/xcm_config.rs +++ b/polkadot-parachains/canvas-kusama/src/xcm_config.rs @@ -19,7 +19,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{EnsureOneOf, Everything, Nothing}, + traits::{EnsureOneOf, Everything, Nothing, ConstU32}, weights::Weight, }; use frame_system::EnsureRoot; @@ -38,10 +38,10 @@ use xcm_executor::XcmExecutor; parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Any; + pub const RelayNetwork: Option = None; pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); - pub const Local: MultiLocation = Here.into(); + pub const Local: MultiLocation = Here.into_location(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); pub const ExecutiveBody: BodyId = BodyId::Executive; } @@ -144,6 +144,8 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = super::AllPalletsWithSystem; + type MaxAssetsIntoHolding = ConstU32<8>; type AssetLocker = (); type AssetExchanger = (); type FeeManager = (); diff --git a/polkadot-parachains/statemint/src/xcm_config.rs b/polkadot-parachains/statemint/src/xcm_config.rs index 52a7e845736..b0d6e6328fa 100644 --- a/polkadot-parachains/statemint/src/xcm_config.rs +++ b/polkadot-parachains/statemint/src/xcm_config.rs @@ -42,7 +42,7 @@ parameter_types! { pub const RelayNetwork: NetworkId = NetworkId::Polkadot; pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); - pub const Local: MultiLocation = Here.into(); + pub const Local: MultiLocation = MultiLocation::here(); pub AssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); diff --git a/polkadot-parachains/westmint/src/xcm_config.rs b/polkadot-parachains/westmint/src/xcm_config.rs index 5785254b340..8d01605703d 100644 --- a/polkadot-parachains/westmint/src/xcm_config.rs +++ b/polkadot-parachains/westmint/src/xcm_config.rs @@ -19,7 +19,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{Everything, PalletInfoAccess}, + traits::{Everything, PalletInfoAccess, ConstU32, Nothing}, weights::Weight, }; use pallet_xcm::XcmPassthrough; @@ -29,8 +29,8 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, - IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, + FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, @@ -39,10 +39,10 @@ use xcm_executor::{traits::JustTry, XcmExecutor}; parameter_types! { pub const WestendLocation: MultiLocation = MultiLocation::parent(); - pub RelayNetwork: NetworkId = NetworkId::Named(b"Westend".to_vec()); + pub RelayNetwork: NetworkId = NetworkId::Westend; pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); - pub const Local: MultiLocation = Here.into(); + pub const Local: MultiLocation = Here.into_location(); pub AssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); From 3f9e25a759c0db4fc5facc3d81bb75fe8157c031 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 11 Mar 2022 18:45:22 +0100 Subject: [PATCH 017/263] Formatting --- polkadot-parachains/canvas-kusama/src/xcm_config.rs | 2 +- polkadot-parachains/westmint/src/xcm_config.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/polkadot-parachains/canvas-kusama/src/xcm_config.rs b/polkadot-parachains/canvas-kusama/src/xcm_config.rs index 6249410b1a8..f3432a20766 100644 --- a/polkadot-parachains/canvas-kusama/src/xcm_config.rs +++ b/polkadot-parachains/canvas-kusama/src/xcm_config.rs @@ -19,7 +19,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{EnsureOneOf, Everything, Nothing, ConstU32}, + traits::{ConstU32, EnsureOneOf, Everything, Nothing}, weights::Weight, }; use frame_system::EnsureRoot; diff --git a/polkadot-parachains/westmint/src/xcm_config.rs b/polkadot-parachains/westmint/src/xcm_config.rs index 8d01605703d..dd6bcc4965b 100644 --- a/polkadot-parachains/westmint/src/xcm_config.rs +++ b/polkadot-parachains/westmint/src/xcm_config.rs @@ -19,7 +19,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{Everything, PalletInfoAccess, ConstU32, Nothing}, + traits::{ConstU32, Everything, Nothing, PalletInfoAccess}, weights::Weight, }; use pallet_xcm::XcmPassthrough; @@ -29,8 +29,8 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, - FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, + IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, From 4b72261c15a207340735039f5af12b5005311d56 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 11 Mar 2022 23:26:51 +0100 Subject: [PATCH 018/263] Bump --- Cargo.lock | 340 ++++++++++++++++++++++++++--------------------------- 1 file changed, 170 insertions(+), 170 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9a49377747d..bb2fcf85072 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -458,7 +458,7 @@ dependencies = [ [[package]] name = "beefy-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "beefy-primitives", "fnv", @@ -488,7 +488,7 @@ dependencies = [ [[package]] name = "beefy-gadget-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "beefy-gadget", "beefy-primitives", @@ -511,12 +511,12 @@ dependencies = [ [[package]] name = "beefy-merkle-tree" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" [[package]] name = "beefy-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "parity-scale-codec", "scale-info", @@ -2768,7 +2768,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "parity-scale-codec", ] @@ -2786,7 +2786,7 @@ dependencies = [ [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -2808,7 +2808,7 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "Inflector", "chrono", @@ -2849,7 +2849,7 @@ dependencies = [ [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -2864,7 +2864,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -2892,7 +2892,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "bitflags", "frame-metadata", @@ -2921,7 +2921,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "Inflector", "frame-support-procedural-tools", @@ -2933,7 +2933,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate 1.1.3", @@ -2945,7 +2945,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "proc-macro2", "quote", @@ -2955,7 +2955,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "log", @@ -2972,7 +2972,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -2987,7 +2987,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "parity-scale-codec", "sp-api", @@ -2996,7 +2996,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "sp-api", @@ -5583,7 +5583,7 @@ dependencies = [ [[package]] name = "pallet-asset-tx-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -5600,7 +5600,7 @@ dependencies = [ [[package]] name = "pallet-assets" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -5614,7 +5614,7 @@ dependencies = [ [[package]] name = "pallet-aura" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -5630,7 +5630,7 @@ dependencies = [ [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -5646,7 +5646,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -5661,7 +5661,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -5685,7 +5685,7 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5705,7 +5705,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -5720,7 +5720,7 @@ dependencies = [ [[package]] name = "pallet-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "beefy-primitives", "frame-support", @@ -5736,7 +5736,7 @@ dependencies = [ [[package]] name = "pallet-beefy-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "beefy-merkle-tree", "beefy-primitives", @@ -5761,7 +5761,7 @@ dependencies = [ [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -5862,7 +5862,7 @@ dependencies = [ [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -5879,7 +5879,7 @@ dependencies = [ [[package]] name = "pallet-contracts" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "bitflags", "frame-benchmarking", @@ -5906,7 +5906,7 @@ dependencies = [ [[package]] name = "pallet-contracts-primitives" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "bitflags", "parity-scale-codec", @@ -5921,7 +5921,7 @@ dependencies = [ [[package]] name = "pallet-contracts-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "proc-macro2", "quote", @@ -5931,7 +5931,7 @@ dependencies = [ [[package]] name = "pallet-contracts-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "jsonrpc-core", "jsonrpc-core-client", @@ -5950,7 +5950,7 @@ dependencies = [ [[package]] name = "pallet-contracts-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "pallet-contracts-primitives", "parity-scale-codec", @@ -5963,7 +5963,7 @@ dependencies = [ [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -5979,7 +5979,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6002,7 +6002,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6020,7 +6020,7 @@ dependencies = [ [[package]] name = "pallet-gilt" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6035,7 +6035,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6058,7 +6058,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "enumflags2", "frame-benchmarking", @@ -6074,7 +6074,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6094,7 +6094,7 @@ dependencies = [ [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6111,7 +6111,7 @@ dependencies = [ [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6128,7 +6128,7 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "ckb-merkle-mountain-range", "frame-benchmarking", @@ -6146,7 +6146,7 @@ dependencies = [ [[package]] name = "pallet-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -6162,7 +6162,7 @@ dependencies = [ [[package]] name = "pallet-mmr-rpc" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "jsonrpc-core", "jsonrpc-core-client", @@ -6179,7 +6179,7 @@ dependencies = [ [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6194,7 +6194,7 @@ dependencies = [ [[package]] name = "pallet-nicks" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -6208,7 +6208,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -6225,7 +6225,7 @@ dependencies = [ [[package]] name = "pallet-offences-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6248,7 +6248,7 @@ dependencies = [ [[package]] name = "pallet-preimage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6264,7 +6264,7 @@ dependencies = [ [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6279,7 +6279,7 @@ dependencies = [ [[package]] name = "pallet-randomness-collective-flip" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -6293,7 +6293,7 @@ dependencies = [ [[package]] name = "pallet-recovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -6307,7 +6307,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6323,7 +6323,7 @@ dependencies = [ [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -6344,7 +6344,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6360,7 +6360,7 @@ dependencies = [ [[package]] name = "pallet-society" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -6374,7 +6374,7 @@ dependencies = [ [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6397,7 +6397,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro2", @@ -6408,7 +6408,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "log", "sp-arithmetic", @@ -6417,7 +6417,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -6446,7 +6446,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6464,7 +6464,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6483,7 +6483,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-support", "frame-system", @@ -6500,7 +6500,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "jsonrpc-core", "jsonrpc-core-client", @@ -6517,7 +6517,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -6528,7 +6528,7 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6545,7 +6545,7 @@ dependencies = [ [[package]] name = "pallet-uniques" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6560,7 +6560,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -6576,7 +6576,7 @@ dependencies = [ [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-benchmarking", "frame-support", @@ -8954,7 +8954,7 @@ dependencies = [ [[package]] name = "remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "env_logger", "jsonrpsee 0.8.0", @@ -9333,7 +9333,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "log", "sp-core", @@ -9344,7 +9344,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "futures 0.3.21", @@ -9371,7 +9371,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "futures 0.3.21", "futures-timer", @@ -9394,7 +9394,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -9410,7 +9410,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "impl-trait-for-tuples", "memmap2 0.5.3", @@ -9427,7 +9427,7 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro2", @@ -9438,7 +9438,7 @@ dependencies = [ [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "chrono", "clap 3.1.6", @@ -9476,7 +9476,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "fnv", "futures 0.3.21", @@ -9504,7 +9504,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "hash-db", "kvdb", @@ -9529,7 +9529,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "futures 0.3.21", @@ -9553,7 +9553,7 @@ dependencies = [ [[package]] name = "sc-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "futures 0.3.21", @@ -9582,7 +9582,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "fork-tree", @@ -9625,7 +9625,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "futures 0.3.21", "jsonrpc-core", @@ -9649,7 +9649,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "fork-tree", "parity-scale-codec", @@ -9662,7 +9662,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "futures 0.3.21", @@ -9687,7 +9687,7 @@ dependencies = [ [[package]] name = "sc-consensus-uncles" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "sc-client-api", "sp-authorship", @@ -9698,7 +9698,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "lazy_static", "lru 0.6.6", @@ -9725,7 +9725,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "environmental", "parity-scale-codec", @@ -9742,7 +9742,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "log", "parity-scale-codec", @@ -9758,7 +9758,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "cfg-if 1.0.0", "libc", @@ -9776,7 +9776,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "ahash", "async-trait", @@ -9816,7 +9816,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "finality-grandpa", "futures 0.3.21", @@ -9840,7 +9840,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "ansi_term", "futures 0.3.21", @@ -9857,7 +9857,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "hex", @@ -9872,7 +9872,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "asynchronous-codec 0.5.0", @@ -9921,7 +9921,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "ahash", "futures 0.3.21", @@ -9938,7 +9938,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "bytes 1.1.0", "fnv", @@ -9966,7 +9966,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "futures 0.3.21", "libp2p", @@ -9979,7 +9979,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -9988,7 +9988,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "futures 0.3.21", "hash-db", @@ -10019,7 +10019,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "futures 0.3.21", "jsonrpc-core", @@ -10044,7 +10044,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "futures 0.3.21", "jsonrpc-core", @@ -10061,7 +10061,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "directories", @@ -10125,7 +10125,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "log", "parity-scale-codec", @@ -10139,7 +10139,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "jsonrpc-core", "jsonrpc-core-client", @@ -10160,7 +10160,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "chrono", "futures 0.3.21", @@ -10178,7 +10178,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "ansi_term", "atty", @@ -10209,7 +10209,7 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro2", @@ -10220,7 +10220,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "futures 0.3.21", "futures-timer", @@ -10247,7 +10247,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "futures 0.3.21", "log", @@ -10260,7 +10260,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "futures 0.3.21", "futures-timer", @@ -10820,7 +10820,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "hash-db", "log", @@ -10837,7 +10837,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "blake2 0.10.4", "proc-macro-crate 1.1.3", @@ -10849,7 +10849,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "parity-scale-codec", "scale-info", @@ -10862,7 +10862,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "integer-sqrt", "num-traits", @@ -10877,7 +10877,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "parity-scale-codec", "scale-info", @@ -10890,7 +10890,7 @@ dependencies = [ [[package]] name = "sp-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "parity-scale-codec", @@ -10902,7 +10902,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "parity-scale-codec", "sp-api", @@ -10914,7 +10914,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "futures 0.3.21", "log", @@ -10932,7 +10932,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "futures 0.3.21", @@ -10951,7 +10951,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "parity-scale-codec", @@ -10969,7 +10969,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "merlin", @@ -10992,7 +10992,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "parity-scale-codec", "scale-info", @@ -11006,7 +11006,7 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "parity-scale-codec", "schnorrkel", @@ -11018,7 +11018,7 @@ dependencies = [ [[package]] name = "sp-core" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "base58", "bitflags", @@ -11064,7 +11064,7 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "blake2 0.10.4", "byteorder", @@ -11078,7 +11078,7 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "proc-macro2", "quote", @@ -11089,7 +11089,7 @@ dependencies = [ [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "kvdb", "parking_lot 0.12.0", @@ -11098,7 +11098,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "proc-macro2", "quote", @@ -11108,7 +11108,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "environmental", "parity-scale-codec", @@ -11119,7 +11119,7 @@ dependencies = [ [[package]] name = "sp-finality-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "finality-grandpa", "log", @@ -11137,7 +11137,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -11151,7 +11151,7 @@ dependencies = [ [[package]] name = "sp-io" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "futures 0.3.21", "hash-db", @@ -11176,7 +11176,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "lazy_static", "sp-core", @@ -11187,7 +11187,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "futures 0.3.21", @@ -11204,7 +11204,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "thiserror", "zstd", @@ -11213,7 +11213,7 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "parity-scale-codec", "scale-info", @@ -11228,7 +11228,7 @@ dependencies = [ [[package]] name = "sp-npos-elections-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro2", @@ -11239,7 +11239,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "sp-api", "sp-core", @@ -11249,7 +11249,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "backtrace", "lazy_static", @@ -11259,7 +11259,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "rustc-hash", "serde", @@ -11269,7 +11269,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "either", "hash256-std-hasher", @@ -11291,7 +11291,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -11308,7 +11308,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "Inflector", "proc-macro-crate 1.1.3", @@ -11320,7 +11320,7 @@ dependencies = [ [[package]] name = "sp-sandbox" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "log", "parity-scale-codec", @@ -11334,7 +11334,7 @@ dependencies = [ [[package]] name = "sp-serializer" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "serde", "serde_json", @@ -11343,7 +11343,7 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "parity-scale-codec", "scale-info", @@ -11357,7 +11357,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "parity-scale-codec", "scale-info", @@ -11368,7 +11368,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "hash-db", "log", @@ -11391,12 +11391,12 @@ dependencies = [ [[package]] name = "sp-std" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" [[package]] name = "sp-storage" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "impl-serde", "parity-scale-codec", @@ -11409,7 +11409,7 @@ dependencies = [ [[package]] name = "sp-tasks" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "log", "sp-core", @@ -11422,7 +11422,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "futures-timer", @@ -11438,7 +11438,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "parity-scale-codec", "sp-std", @@ -11450,7 +11450,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "sp-api", "sp-runtime", @@ -11459,7 +11459,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "log", @@ -11475,7 +11475,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "hash-db", "memory-db", @@ -11491,7 +11491,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "impl-serde", "parity-scale-codec", @@ -11508,7 +11508,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -11519,7 +11519,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "impl-trait-for-tuples", "log", @@ -11809,7 +11809,7 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "platforms", ] @@ -11817,7 +11817,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "frame-system-rpc-runtime-api", "futures 0.3.21", @@ -11839,7 +11839,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "futures-util", "hyper", @@ -11852,7 +11852,7 @@ dependencies = [ [[package]] name = "substrate-test-client" version = "2.0.1" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "async-trait", "futures 0.3.21", @@ -11878,7 +11878,7 @@ dependencies = [ [[package]] name = "substrate-test-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "futures 0.3.21", "substrate-test-utils-derive", @@ -11888,7 +11888,7 @@ dependencies = [ [[package]] name = "substrate-test-utils-derive" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro2", @@ -11899,7 +11899,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "ansi_term", "build-helper", @@ -12380,7 +12380,7 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5f286db0da99761792b69bf4a997535dcc8f260" +source = "git+https://github.com/paritytech/substrate?branch=master#5cc2ef8e4c03b64691db367036fedd8f4d7f0326" dependencies = [ "clap 3.1.6", "jsonrpsee 0.4.1", From 865839a180e285fd93cb6ed82ac1312c93a3754e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 11 Mar 2022 23:28:15 +0100 Subject: [PATCH 019/263] Bump --- pallets/dmp-queue/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 2774be7f0d3..c25b56a84dd 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -437,7 +437,8 @@ mod tests { fn execute( _origin: impl Into, - message: Xcm, + _: Weightless, + message: &Xcm, _hash: XcmHash, weight_limit: Weight, ) -> Outcome { From 6462dd9e0da79f3fef593fd01163a3dbaa6e0375 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 11 Mar 2022 23:38:23 +0100 Subject: [PATCH 020/263] Fixes --- pallets/dmp-queue/src/lib.rs | 17 ++--------------- pallets/xcmp-queue/src/mock.rs | 9 +++++++-- .../parachains-common/src/impls.rs | 4 ++-- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index c25b56a84dd..b1b12cf18d1 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -438,23 +438,10 @@ mod tests { fn execute( _origin: impl Into, _: Weightless, - message: &Xcm, _hash: XcmHash, - weight_limit: Weight, + _weight_limit: Weight, ) -> Outcome { - let o = match (message.0.len(), &message.0.first()) { - (1, Some(Transact { require_weight_at_most, .. })) => { - if *require_weight_at_most <= weight_limit { - Outcome::Complete(*require_weight_at_most) - } else { - Outcome::Error(XcmError::WeightLimitReached(*require_weight_at_most)) - } - }, - // use 1000 to decide that it's not supported. - _ => Outcome::Incomplete(1000.min(weight_limit), XcmError::Unimplemented), - }; - TRACE.with(|q| q.borrow_mut().push((message, o.clone()))); - o + unreachable!() } fn charge_fees(_location: impl Into, _fees: MultiAssets) -> XcmResult { diff --git a/pallets/xcmp-queue/src/mock.rs b/pallets/xcmp-queue/src/mock.rs index 18a099e5ab3..4b7fbc895b0 100644 --- a/pallets/xcmp-queue/src/mock.rs +++ b/pallets/xcmp-queue/src/mock.rs @@ -17,7 +17,7 @@ use super::*; use crate as xcmp_queue; use core::marker::PhantomData; use cumulus_primitives_core::{IsSystem, ParaId}; -use frame_support::{parameter_types, traits::OriginTrait}; +use frame_support::{parameter_types, traits::{OriginTrait, Nothing}}; use frame_system::EnsureRoot; use sp_core::H256; use sp_runtime::{ @@ -152,8 +152,13 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = (); type AssetClaims = (); type SubscriptionService = (); - type PalletInstancesInfo = AllPallets; + type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; } pub type XcmRouter = ( diff --git a/polkadot-parachains/parachains-common/src/impls.rs b/polkadot-parachains/parachains-common/src/impls.rs index 2b66cd416d1..d511593c76f 100644 --- a/polkadot-parachains/parachains-common/src/impls.rs +++ b/polkadot-parachains/parachains-common/src/impls.rs @@ -269,9 +269,9 @@ mod tests { .clone() .pushed_with_interior(GeneralIndex(42)) .expect("multilocation will only have 2 junctions; qed"); - let asset = MultiAsset { id: Concrete(asset_location), fun: 1_000_000.into() }; + let asset = MultiAsset { id: Concrete(asset_location), fun: 1_000_000u128.into() }; assert!( - AssetsFrom::::filter_asset_location( + AssetsFrom::::contains( &asset, &SomeSiblingParachain::get() ), From b9cc2f232821a6a68312660bd43238edf9a276ca Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 11 Mar 2022 23:38:39 +0100 Subject: [PATCH 021/263] Formatting --- pallets/xcmp-queue/src/mock.rs | 5 ++++- polkadot-parachains/parachains-common/src/impls.rs | 5 +---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pallets/xcmp-queue/src/mock.rs b/pallets/xcmp-queue/src/mock.rs index 4b7fbc895b0..c285479521e 100644 --- a/pallets/xcmp-queue/src/mock.rs +++ b/pallets/xcmp-queue/src/mock.rs @@ -17,7 +17,10 @@ use super::*; use crate as xcmp_queue; use core::marker::PhantomData; use cumulus_primitives_core::{IsSystem, ParaId}; -use frame_support::{parameter_types, traits::{OriginTrait, Nothing}}; +use frame_support::{ + parameter_types, + traits::{Nothing, OriginTrait}, +}; use frame_system::EnsureRoot; use sp_core::H256; use sp_runtime::{ diff --git a/polkadot-parachains/parachains-common/src/impls.rs b/polkadot-parachains/parachains-common/src/impls.rs index d511593c76f..a9e1c8212ac 100644 --- a/polkadot-parachains/parachains-common/src/impls.rs +++ b/polkadot-parachains/parachains-common/src/impls.rs @@ -271,10 +271,7 @@ mod tests { .expect("multilocation will only have 2 junctions; qed"); let asset = MultiAsset { id: Concrete(asset_location), fun: 1_000_000u128.into() }; assert!( - AssetsFrom::::contains( - &asset, - &SomeSiblingParachain::get() - ), + AssetsFrom::::contains(&asset, &SomeSiblingParachain::get()), "AssetsFrom should allow assets from any of its interior locations" ); } From fb1d3f2a2be1d06c34440cf49a9f369f75483e11 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Fri, 11 Mar 2022 23:56:56 -0800 Subject: [PATCH 022/263] Make the price of UMP/XCMP message sending configurable --- pallets/xcmp-queue/src/lib.rs | 24 +++++++++++++++++++++++- primitives/utility/src/lib.rs | 30 +++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index 112aaf378d7..31885c4fa93 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -99,6 +99,9 @@ pub mod pallet { /// superuser origin. type ControllerOriginConverter: ConvertOrigin; + /// The price for delivering an XCM to a sibling parachain destination. + type PriceForSiblingDelivery: PriceForSiblingDelivery; + /// The weight information of this pallet. type WeightInfo: WeightInfo; } @@ -1077,6 +1080,23 @@ impl XcmpMessageSource for Pallet { } } +pub trait PriceForSiblingDelivery { + fn price_for_sibling_delivery(id: ParaId, message: &Xcm<()>) -> MultiAssets; +} + +impl PriceForSiblingDelivery for () { + fn price_for_sibling_delivery(_: ParaId, _: &Xcm<()>) -> MultiAssets { + MultiAssets::new() + } +} + +pub struct ConstantPrice(sp_std::marker::PhantomData); +impl> PriceForSiblingDelivery for ConstantPrice { + fn price_for_sibling_delivery(_: ParaId, _: &Xcm<()>) -> MultiAssets { + T::get() + } +} + /// Xcm sender for sending to a sibling parachain. impl SendXcm for Pallet { type Ticket = (ParaId, VersionedXcm<()>); @@ -1091,9 +1111,11 @@ impl SendXcm for Pallet { match &d { // An HRMP message for a sibling parachain. MultiLocation { parents: 1, interior: X1(Parachain(id)) } => { + let id = ParaId::from(*id); + let price = T::PriceForSiblingDelivery::price_for_sibling_delivery(id, &xcm); let versioned_xcm = T::VersionWrapper::wrap_version(&d, xcm) .map_err(|()| SendError::DestinationUnsupported)?; - Ok((((*id).into(), versioned_xcm), MultiAssets::new())) + Ok(((id, versioned_xcm), price)) }, // Anything else is unhandled. This includes a message this is meant for us. _ => { diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index f99ebce9ec6..ac5b783eba2 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -21,9 +21,27 @@ use codec::Encode; use cumulus_primitives_core::{MessageSendError, UpwardMessageSender}; +use frame_support::traits::Get; use sp_std::{marker::PhantomData, prelude::*}; use xcm::{latest::prelude::*, WrapVersion}; +pub trait PriceForParentDelivery { + fn price_for_parent_delivery(message: &Xcm<()>) -> MultiAssets; +} + +impl PriceForParentDelivery for () { + fn price_for_parent_delivery(_: &Xcm<()>) -> MultiAssets { + MultiAssets::new() + } +} + +pub struct ConstantPrice(PhantomData); +impl> PriceForParentDelivery for ConstantPrice { + fn price_for_parent_delivery(_: &Xcm<()>) -> MultiAssets { + T::get() + } +} + /// Xcm router which recognises the `Parent` destination and handles it by sending the message into /// the given UMP `UpwardMessageSender` implementation. Thus this essentially adapts an /// `UpwardMessageSender` trait impl into a `SendXcm` trait impl. @@ -31,8 +49,13 @@ use xcm::{latest::prelude::*, WrapVersion}; /// NOTE: This is a pretty dumb "just send it" router; we will probably want to introduce queuing /// to UMP eventually and when we do, the pallet which implements the queuing will be responsible /// for the `SendXcm` implementation. -pub struct ParentAsUmp(PhantomData<(T, W)>); -impl SendXcm for ParentAsUmp { +pub struct ParentAsUmp(PhantomData<(T, W, P)>); +impl SendXcm for ParentAsUmp +where + T: UpwardMessageSender, + W: WrapVersion, + P: PriceForParentDelivery, +{ type Ticket = Vec; fn validate( @@ -44,11 +67,12 @@ impl SendXcm for ParentAsUmp { if d.contains_parents_only(1) { // An upward message for the relay chain. + let price = P::price_for_parent_delivery(&xcm); let versioned_xcm = W::wrap_version(&d, xcm).map_err(|()| SendError::DestinationUnsupported)?; let data = versioned_xcm.encode(); - Ok((data, MultiAssets::new())) + Ok((data, price)) } else { *dest = Some(d); // Anything else is unhandled. This includes a message this is meant for us. From c3ee8a1547937a3ed8274e76d9e3f2794253b679 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sat, 12 Mar 2022 00:02:40 -0800 Subject: [PATCH 023/263] cargo fmt --- pallets/xcmp-queue/src/lib.rs | 4 ++-- primitives/utility/src/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index 31885c4fa93..70cf75d796a 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -1086,14 +1086,14 @@ pub trait PriceForSiblingDelivery { impl PriceForSiblingDelivery for () { fn price_for_sibling_delivery(_: ParaId, _: &Xcm<()>) -> MultiAssets { - MultiAssets::new() + MultiAssets::new() } } pub struct ConstantPrice(sp_std::marker::PhantomData); impl> PriceForSiblingDelivery for ConstantPrice { fn price_for_sibling_delivery(_: ParaId, _: &Xcm<()>) -> MultiAssets { - T::get() + T::get() } } diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index ac5b783eba2..621506c146b 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -31,14 +31,14 @@ pub trait PriceForParentDelivery { impl PriceForParentDelivery for () { fn price_for_parent_delivery(_: &Xcm<()>) -> MultiAssets { - MultiAssets::new() + MultiAssets::new() } } pub struct ConstantPrice(PhantomData); impl> PriceForParentDelivery for ConstantPrice { fn price_for_parent_delivery(_: &Xcm<()>) -> MultiAssets { - T::get() + T::get() } } From ae1436ecd75ba1213f43162175fab3f90c1555da Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 12 Mar 2022 12:29:21 +0100 Subject: [PATCH 024/263] Remove InvertLocation --- .../src/validate_block/implementation.rs | 2 +- pallets/xcmp-queue/src/lib.rs | 2 +- pallets/xcmp-queue/src/mock.rs | 5 +++-- parachain-template/runtime/src/lib.rs | 1 + parachain-template/runtime/src/xcm_config.rs | 8 ++++---- polkadot-parachains/canvas-kusama/src/xcm_config.rs | 9 +++++---- polkadot-parachains/rococo-parachain/src/lib.rs | 9 +++++---- polkadot-parachains/shell/src/xcm_config.rs | 4 ++-- polkadot-parachains/statemine/src/lib.rs | 1 + polkadot-parachains/statemine/src/xcm_config.rs | 8 ++++---- polkadot-parachains/statemint/src/lib.rs | 3 ++- polkadot-parachains/statemint/src/xcm_config.rs | 8 ++++---- polkadot-parachains/westmint/src/lib.rs | 1 + polkadot-parachains/westmint/src/xcm_config.rs | 8 ++++---- 14 files changed, 38 insertions(+), 31 deletions(-) diff --git a/pallets/parachain-system/src/validate_block/implementation.rs b/pallets/parachain-system/src/validate_block/implementation.rs index 3dcbf291584..1ab841c1a7f 100644 --- a/pallets/parachain-system/src/validate_block/implementation.rs +++ b/pallets/parachain-system/src/validate_block/implementation.rs @@ -67,7 +67,7 @@ where assert!(parent_head.hash() == *block.header().parent_hash(), "Invalid parent hash",); // Create the db - let (db, root) = match storage_proof.to_memory_db(Some(parent_head.state_root())) { + let (db, _root) = match storage_proof.to_memory_db(Some(parent_head.state_root())) { Ok((db, root)) => (db, root), Err(_) => panic!("Compact proof decoding failure."), }; diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index 70cf75d796a..a1ba81b6d73 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -44,7 +44,7 @@ use cumulus_primitives_core::{ ParaId, XcmpMessageFormat, XcmpMessageHandler, XcmpMessageSource, }; use frame_support::{ - traits::EnsureOrigin, + traits::{EnsureOrigin, Get}, weights::{constants::WEIGHT_PER_MILLIS, Weight}, }; use rand_chacha::{ diff --git a/pallets/xcmp-queue/src/mock.rs b/pallets/xcmp-queue/src/mock.rs index c285479521e..9641742d620 100644 --- a/pallets/xcmp-queue/src/mock.rs +++ b/pallets/xcmp-queue/src/mock.rs @@ -29,7 +29,7 @@ use sp_runtime::{ }; use xcm::prelude::*; use xcm_builder::{ - CurrencyAdapter, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset, ParentIsPreset, + CurrencyAdapter, FixedWeightBounds, IsConcrete, NativeAsset, ParentIsPreset, }; use xcm_executor::traits::ConvertOrigin; @@ -147,7 +147,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = (); type IsReserve = NativeAsset; type IsTeleporter = NativeAsset; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Barrier = (); type Weigher = FixedWeightBounds; type Trader = (); @@ -200,6 +200,7 @@ impl Config for Test { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = SystemParachainAsSuperuser; type WeightInfo = (); + type PriceForSiblingDelivery = (); } pub fn new_test_ext() -> sp_io::TestExternalities { diff --git a/parachain-template/runtime/src/lib.rs b/parachain-template/runtime/src/lib.rs index b3d3d59715b..fab19fe7bb9 100644 --- a/parachain-template/runtime/src/lib.rs +++ b/parachain-template/runtime/src/lib.rs @@ -387,6 +387,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = (); + type PriceForSiblingDelivery = (); } impl cumulus_pallet_dmp_queue::Config for Runtime { diff --git a/parachain-template/runtime/src/xcm_config.rs b/parachain-template/runtime/src/xcm_config.rs index fc394a05d44..88635089ee3 100644 --- a/parachain-template/runtime/src/xcm_config.rs +++ b/parachain-template/runtime/src/xcm_config.rs @@ -13,7 +13,7 @@ use polkadot_runtime_common::impls::ToAuthor; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, - EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset, ParentIsPreset, + EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, @@ -104,7 +104,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = NativeAsset; type IsTeleporter = (); // Teleporting is disabled. - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = @@ -129,7 +129,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); @@ -146,7 +146,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Nothing; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Origin = Origin; type Call = Call; diff --git a/polkadot-parachains/canvas-kusama/src/xcm_config.rs b/polkadot-parachains/canvas-kusama/src/xcm_config.rs index f3432a20766..7f71577916b 100644 --- a/polkadot-parachains/canvas-kusama/src/xcm_config.rs +++ b/polkadot-parachains/canvas-kusama/src/xcm_config.rs @@ -29,7 +29,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, + FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, @@ -136,7 +136,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = NativeAsset; type IsTeleporter = NativeAsset; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = UsingComponents; @@ -161,7 +161,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); @@ -179,7 +179,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; @@ -208,6 +208,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { >; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = cumulus_pallet_xcmp_queue::weights::SubstrateWeight; + type PriceForSiblingDelivery = (); } impl cumulus_pallet_dmp_queue::Config for Runtime { diff --git a/polkadot-parachains/rococo-parachain/src/lib.rs b/polkadot-parachains/rococo-parachain/src/lib.rs index 41b22f6d38c..47d0588e6eb 100644 --- a/polkadot-parachains/rococo-parachain/src/lib.rs +++ b/polkadot-parachains/rococo-parachain/src/lib.rs @@ -73,7 +73,7 @@ use polkadot_parachain::primitives::Sibling; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, - EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset, + EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, @@ -404,7 +404,7 @@ impl Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = Reserves; type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of ROC - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = UsingComponents, RocLocation, AccountId, Balances, ()>; @@ -428,7 +428,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); @@ -443,7 +443,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Nothing; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; @@ -469,6 +469,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = cumulus_pallet_xcmp_queue::weights::SubstrateWeight; + type PriceForSiblingDelivery = (); } impl cumulus_pallet_dmp_queue::Config for Runtime { diff --git a/polkadot-parachains/shell/src/xcm_config.rs b/polkadot-parachains/shell/src/xcm_config.rs index 0d491043af1..3025651c323 100644 --- a/polkadot-parachains/shell/src/xcm_config.rs +++ b/polkadot-parachains/shell/src/xcm_config.rs @@ -17,7 +17,7 @@ use super::{AccountId, Call, Event, Origin, ParachainInfo, Runtime}; use frame_support::{match_types, parameter_types, traits::Nothing, weights::Weight}; use xcm::latest::prelude::*; use xcm_builder::{ - AllowUnpaidExecutionFrom, FixedWeightBounds, LocationInverter, ParentAsSuperuser, + AllowUnpaidExecutionFrom, FixedWeightBounds, ParentAsSuperuser, ParentIsPreset, SovereignSignedViaLocation, }; @@ -59,7 +59,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = (); // balances not supported type IsTeleporter = (); // balances not supported - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Barrier = AllowUnpaidExecutionFrom; type Weigher = FixedWeightBounds; // balances not supported type Trader = (); // balances not supported diff --git a/polkadot-parachains/statemine/src/lib.rs b/polkadot-parachains/statemine/src/lib.rs index 6b363703fea..e0ac1d51824 100644 --- a/polkadot-parachains/statemine/src/lib.rs +++ b/polkadot-parachains/statemine/src/lib.rs @@ -430,6 +430,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { EnsureOneOf, EnsureXcm>>; type ControllerOriginConverter = xcm_config::XcmOriginToTransactDispatchOrigin; type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; + type PriceForSiblingDelivery = (); } impl cumulus_pallet_dmp_queue::Config for Runtime { diff --git a/polkadot-parachains/statemine/src/xcm_config.rs b/polkadot-parachains/statemine/src/xcm_config.rs index a1f19a919cb..8a61fc87c3e 100644 --- a/polkadot-parachains/statemine/src/xcm_config.rs +++ b/polkadot-parachains/statemine/src/xcm_config.rs @@ -30,7 +30,7 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, - IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, + IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, @@ -159,7 +159,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = NativeAsset; type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of KSM - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = @@ -185,7 +185,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); @@ -203,7 +203,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; diff --git a/polkadot-parachains/statemint/src/lib.rs b/polkadot-parachains/statemint/src/lib.rs index 70d9e8cd4cd..ed7c455e748 100644 --- a/polkadot-parachains/statemint/src/lib.rs +++ b/polkadot-parachains/statemint/src/lib.rs @@ -434,6 +434,7 @@ impl parachain_info::Config for Runtime {} impl cumulus_pallet_aura_ext::Config for Runtime {} impl cumulus_pallet_xcmp_queue::Config for Runtime { + type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; type Event = Event; type XcmExecutor = XcmExecutor; type ChannelInfo = ParachainSystem; @@ -442,7 +443,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureOneOf, EnsureXcm>>; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; - type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; + type PriceForSiblingDelivery = (); } impl cumulus_pallet_dmp_queue::Config for Runtime { diff --git a/polkadot-parachains/statemint/src/xcm_config.rs b/polkadot-parachains/statemint/src/xcm_config.rs index b0d6e6328fa..6e2261b96ca 100644 --- a/polkadot-parachains/statemint/src/xcm_config.rs +++ b/polkadot-parachains/statemint/src/xcm_config.rs @@ -30,7 +30,7 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, - IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, + IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, @@ -159,7 +159,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = NativeAsset; type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of DOT - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = @@ -185,7 +185,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); @@ -203,7 +203,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Nothing; type XcmReserveTransferFilter = Nothing; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; diff --git a/polkadot-parachains/westmint/src/lib.rs b/polkadot-parachains/westmint/src/lib.rs index ca299cb082e..93a5deb6abb 100644 --- a/polkadot-parachains/westmint/src/lib.rs +++ b/polkadot-parachains/westmint/src/lib.rs @@ -424,6 +424,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; + type PriceForSiblingDelivery = (); } impl cumulus_pallet_dmp_queue::Config for Runtime { diff --git a/polkadot-parachains/westmint/src/xcm_config.rs b/polkadot-parachains/westmint/src/xcm_config.rs index dd6bcc4965b..28b4ec72d34 100644 --- a/polkadot-parachains/westmint/src/xcm_config.rs +++ b/polkadot-parachains/westmint/src/xcm_config.rs @@ -30,7 +30,7 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, - IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsPreset, + IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, @@ -155,7 +155,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = NativeAsset; type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of WND - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = @@ -180,7 +180,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); @@ -195,7 +195,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; From d42833b4b9b77b0f3370e2367fa7c69faf52f0d2 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 12 Mar 2022 12:32:37 +0100 Subject: [PATCH 025/263] Formatting --- pallets/xcmp-queue/src/mock.rs | 4 +--- polkadot-parachains/canvas-kusama/src/xcm_config.rs | 4 ++-- polkadot-parachains/rococo-parachain/src/lib.rs | 8 ++++---- polkadot-parachains/shell/src/xcm_config.rs | 4 ++-- polkadot-parachains/statemine/src/xcm_config.rs | 7 +++---- polkadot-parachains/statemint/src/xcm_config.rs | 7 +++---- polkadot-parachains/westmint/src/xcm_config.rs | 7 +++---- 7 files changed, 18 insertions(+), 23 deletions(-) diff --git a/pallets/xcmp-queue/src/mock.rs b/pallets/xcmp-queue/src/mock.rs index 9641742d620..f0a191649a5 100644 --- a/pallets/xcmp-queue/src/mock.rs +++ b/pallets/xcmp-queue/src/mock.rs @@ -28,9 +28,7 @@ use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, }; use xcm::prelude::*; -use xcm_builder::{ - CurrencyAdapter, FixedWeightBounds, IsConcrete, NativeAsset, ParentIsPreset, -}; +use xcm_builder::{CurrencyAdapter, FixedWeightBounds, IsConcrete, NativeAsset, ParentIsPreset}; use xcm_executor::traits::ConvertOrigin; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; diff --git a/polkadot-parachains/canvas-kusama/src/xcm_config.rs b/polkadot-parachains/canvas-kusama/src/xcm_config.rs index 7f71577916b..0f4af7594d4 100644 --- a/polkadot-parachains/canvas-kusama/src/xcm_config.rs +++ b/polkadot-parachains/canvas-kusama/src/xcm_config.rs @@ -29,8 +29,8 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, - ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; diff --git a/polkadot-parachains/rococo-parachain/src/lib.rs b/polkadot-parachains/rococo-parachain/src/lib.rs index 47d0588e6eb..63ededdcdc1 100644 --- a/polkadot-parachains/rococo-parachain/src/lib.rs +++ b/polkadot-parachains/rococo-parachain/src/lib.rs @@ -73,10 +73,10 @@ use polkadot_parachain::primitives::Sibling; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, - EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, - ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + UsingComponents, }; use xcm_executor::{Config, XcmExecutor}; diff --git a/polkadot-parachains/shell/src/xcm_config.rs b/polkadot-parachains/shell/src/xcm_config.rs index 3025651c323..370d6f9d8bb 100644 --- a/polkadot-parachains/shell/src/xcm_config.rs +++ b/polkadot-parachains/shell/src/xcm_config.rs @@ -17,8 +17,8 @@ use super::{AccountId, Call, Event, Origin, ParachainInfo, Runtime}; use frame_support::{match_types, parameter_types, traits::Nothing, weights::Weight}; use xcm::latest::prelude::*; use xcm_builder::{ - AllowUnpaidExecutionFrom, FixedWeightBounds, ParentAsSuperuser, - ParentIsPreset, SovereignSignedViaLocation, + AllowUnpaidExecutionFrom, FixedWeightBounds, ParentAsSuperuser, ParentIsPreset, + SovereignSignedViaLocation, }; parameter_types! { diff --git a/polkadot-parachains/statemine/src/xcm_config.rs b/polkadot-parachains/statemine/src/xcm_config.rs index 8a61fc87c3e..601159de87d 100644 --- a/polkadot-parachains/statemine/src/xcm_config.rs +++ b/polkadot-parachains/statemine/src/xcm_config.rs @@ -30,10 +30,9 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, - IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - UsingComponents, + IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{traits::JustTry, XcmExecutor}; diff --git a/polkadot-parachains/statemint/src/xcm_config.rs b/polkadot-parachains/statemint/src/xcm_config.rs index 6e2261b96ca..fe80356967a 100644 --- a/polkadot-parachains/statemint/src/xcm_config.rs +++ b/polkadot-parachains/statemint/src/xcm_config.rs @@ -30,10 +30,9 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, - IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - UsingComponents, + IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{traits::JustTry, XcmExecutor}; diff --git a/polkadot-parachains/westmint/src/xcm_config.rs b/polkadot-parachains/westmint/src/xcm_config.rs index 28b4ec72d34..24eb59403c0 100644 --- a/polkadot-parachains/westmint/src/xcm_config.rs +++ b/polkadot-parachains/westmint/src/xcm_config.rs @@ -30,10 +30,9 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, - IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - UsingComponents, + IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{traits::JustTry, XcmExecutor}; From 780475dc70bf9783ff2362ff330768f4adb303f5 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Mon, 14 Mar 2022 09:09:02 -0700 Subject: [PATCH 026/263] Use ConstantPrice from polkadot-runtime-common --- Cargo.lock | 155 ++++++++++++++++++---------------- pallets/xcmp-queue/Cargo.toml | 2 + pallets/xcmp-queue/src/lib.rs | 2 +- primitives/utility/Cargo.toml | 2 + primitives/utility/src/lib.rs | 2 +- 5 files changed, 88 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb2fcf85072..20a38c28572 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -732,6 +732,7 @@ dependencies = [ [[package]] name = "bp-header-chain" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "finality-grandpa", "frame-support", @@ -747,6 +748,7 @@ dependencies = [ [[package]] name = "bp-message-dispatch" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bp-runtime", "frame-support", @@ -758,6 +760,7 @@ dependencies = [ [[package]] name = "bp-messages" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bitvec", "bp-runtime", @@ -773,6 +776,7 @@ dependencies = [ [[package]] name = "bp-polkadot-core" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bp-messages", "bp-runtime", @@ -790,6 +794,7 @@ dependencies = [ [[package]] name = "bp-rococo" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bp-messages", "bp-polkadot-core", @@ -806,6 +811,7 @@ dependencies = [ [[package]] name = "bp-runtime" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "frame-support", "hash-db", @@ -823,6 +829,7 @@ dependencies = [ [[package]] name = "bp-test-utils" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bp-header-chain", "ed25519-dalek", @@ -837,6 +844,7 @@ dependencies = [ [[package]] name = "bp-wococo" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bp-messages", "bp-polkadot-core", @@ -851,6 +859,7 @@ dependencies = [ [[package]] name = "bridge-runtime-common" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bp-message-dispatch", "bp-messages", @@ -1923,6 +1932,7 @@ dependencies = [ "log", "pallet-balances", "parity-scale-codec", + "polkadot-runtime-common", "rand_chacha 0.3.1", "scale-info", "sp-core", @@ -2014,6 +2024,7 @@ dependencies = [ "polkadot-core-primitives", "polkadot-parachain", "polkadot-primitives", + "polkadot-runtime-common", "sp-io", "sp-runtime", "sp-std", @@ -4173,6 +4184,7 @@ dependencies = [ [[package]] name = "kusama-runtime" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "beefy-primitives", "bitvec", @@ -4260,6 +4272,7 @@ dependencies = [ [[package]] name = "kusama-runtime-constants" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "frame-support", "polkadot-primitives", @@ -5123,6 +5136,7 @@ dependencies = [ [[package]] name = "metered-channel" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "derive_more", "futures 0.3.21", @@ -5779,6 +5793,7 @@ dependencies = [ [[package]] name = "pallet-bridge-dispatch" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bp-message-dispatch", "bp-runtime", @@ -5795,6 +5810,7 @@ dependencies = [ [[package]] name = "pallet-bridge-grandpa" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bp-header-chain", "bp-runtime", @@ -5816,6 +5832,7 @@ dependencies = [ [[package]] name = "pallet-bridge-messages" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bitvec", "bp-message-dispatch", @@ -6591,16 +6608,15 @@ dependencies = [ [[package]] name = "pallet-xcm" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "frame-support", "frame-system", - "impl-trait-for-tuples", "log", "parity-scale-codec", "scale-info", "serde", "sp-core", - "sp-io", "sp-runtime", "sp-std", "xcm", @@ -6610,6 +6626,7 @@ dependencies = [ [[package]] name = "pallet-xcm-benchmarks" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "frame-benchmarking", "frame-support", @@ -6617,7 +6634,6 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-io", "sp-runtime", "sp-std", "xcm", @@ -7172,6 +7188,7 @@ dependencies = [ [[package]] name = "polkadot-approval-distribution" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "futures 0.3.21", "polkadot-node-network-protocol", @@ -7185,6 +7202,7 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "futures 0.3.21", "polkadot-node-network-protocol", @@ -7197,6 +7215,7 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "derive_more", "fatality", @@ -7219,6 +7238,7 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "fatality", "futures 0.3.21", @@ -7239,6 +7259,7 @@ dependencies = [ [[package]] name = "polkadot-cli" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "clap 3.1.6", "frame-benchmarking-cli", @@ -7261,6 +7282,7 @@ dependencies = [ [[package]] name = "polkadot-client" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "beefy-primitives", "frame-benchmarking", @@ -7366,6 +7388,7 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "always-assert", "fatality", @@ -7386,6 +7409,7 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "parity-scale-codec", "parity-util-mem", @@ -7398,6 +7422,7 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "derive_more", "fatality", @@ -7420,6 +7445,7 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", @@ -7433,6 +7459,7 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "futures 0.3.21", "futures-timer", @@ -7452,6 +7479,7 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "async-trait", "futures 0.3.21", @@ -7470,6 +7498,7 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "futures 0.3.21", "parity-scale-codec", @@ -7487,6 +7516,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bitvec", "derive_more", @@ -7514,6 +7544,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bitvec", "futures 0.3.21", @@ -7533,6 +7564,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bitvec", "futures 0.3.21", @@ -7550,6 +7582,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "futures 0.3.21", "polkadot-node-subsystem", @@ -7564,6 +7597,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "async-trait", "futures 0.3.21", @@ -7581,6 +7615,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "futures 0.3.21", "polkadot-node-subsystem", @@ -7595,6 +7630,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "futures 0.3.21", "futures-timer", @@ -7611,6 +7647,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "fatality", "futures 0.3.21", @@ -7629,6 +7666,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "async-trait", "futures 0.3.21", @@ -7645,6 +7683,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bitvec", "futures 0.3.21", @@ -7661,6 +7700,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "always-assert", "assert_matches", @@ -7690,6 +7730,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-checker" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "futures 0.3.21", "polkadot-node-primitives", @@ -7705,6 +7746,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "futures 0.3.21", "memory-lru", @@ -7722,6 +7764,7 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "async-std", "lazy_static", @@ -7739,6 +7782,7 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bs58", "futures 0.3.21", @@ -7757,6 +7801,7 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "async-trait", "fatality", @@ -7774,6 +7819,7 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bounded-vec", "futures 0.3.21", @@ -7795,6 +7841,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -7804,6 +7851,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-test-helpers" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "async-trait", "futures 0.3.21", @@ -7821,6 +7869,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "derive_more", "futures 0.3.21", @@ -7839,6 +7888,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "async-trait", "derive_more", @@ -7871,6 +7921,7 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "futures 0.3.21", "futures-timer", @@ -7891,6 +7942,7 @@ dependencies = [ [[package]] name = "polkadot-overseer-gen" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "async-trait", "futures 0.3.21", @@ -7907,6 +7959,7 @@ dependencies = [ [[package]] name = "polkadot-overseer-gen-proc-macro" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "expander 0.0.6", "proc-macro-crate 1.1.3", @@ -7918,6 +7971,7 @@ dependencies = [ [[package]] name = "polkadot-parachain" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "derive_more", "frame-support", @@ -7934,6 +7988,7 @@ dependencies = [ [[package]] name = "polkadot-performance-test" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "env_logger", "kusama-runtime", @@ -7948,6 +8003,7 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bitvec", "frame-system", @@ -7977,6 +8033,7 @@ dependencies = [ [[package]] name = "polkadot-rpc" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", @@ -8007,6 +8064,7 @@ dependencies = [ [[package]] name = "polkadot-runtime" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "beefy-primitives", "bitvec", @@ -8090,6 +8148,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "beefy-primitives", "bitvec", @@ -8136,6 +8195,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-constants" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "frame-support", "polkadot-primitives", @@ -8147,6 +8207,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-metrics" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bs58", "parity-scale-codec", @@ -8158,6 +8219,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "bitflags", "bitvec", @@ -8199,6 +8261,7 @@ dependencies = [ [[package]] name = "polkadot-service" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "async-trait", "beefy-gadget", @@ -8299,6 +8362,7 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "arrayvec 0.5.2", "fatality", @@ -8319,6 +8383,7 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -8328,6 +8393,7 @@ dependencies = [ [[package]] name = "polkadot-test-client" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "parity-scale-codec", "polkadot-node-subsystem", @@ -8352,6 +8418,7 @@ dependencies = [ [[package]] name = "polkadot-test-runtime" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "beefy-primitives", "bitvec", @@ -8413,6 +8480,7 @@ dependencies = [ [[package]] name = "polkadot-test-service" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "frame-benchmarking", "frame-system", @@ -8428,6 +8496,7 @@ dependencies = [ "polkadot-parachain", "polkadot-primitives", "polkadot-rpc", + "polkadot-runtime-common", "polkadot-runtime-parachains", "polkadot-service", "polkadot-test-runtime", @@ -9071,6 +9140,7 @@ dependencies = [ [[package]] name = "rococo-runtime" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "beefy-primitives", "bp-messages", @@ -9145,6 +9215,7 @@ dependencies = [ [[package]] name = "rococo-runtime-constants" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "frame-support", "polkadot-primitives", @@ -10733,6 +10804,7 @@ checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" [[package]] name = "slot-range-helper" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "enumn", "parity-scale-codec", @@ -11985,6 +12057,7 @@ checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" [[package]] name = "test-runtime-constants" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "frame-support", "polkadot-primitives", @@ -12982,6 +13055,7 @@ dependencies = [ [[package]] name = "westend-runtime" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "beefy-primitives", "bitvec", @@ -13067,6 +13141,7 @@ dependencies = [ [[package]] name = "westend-runtime-constants" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "frame-support", "polkadot-primitives", @@ -13286,23 +13361,23 @@ dependencies = [ [[package]] name = "xcm" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "derivative", "impl-trait-for-tuples", "log", "parity-scale-codec", "scale-info", - "sp-io", "xcm-procedural", ] [[package]] name = "xcm-builder" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "frame-support", "frame-system", - "impl-trait-for-tuples", "log", "pallet-transaction-payment", "parity-scale-codec", @@ -13319,6 +13394,7 @@ dependencies = [ [[package]] name = "xcm-executor" version = "0.9.18" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "frame-benchmarking", "frame-support", @@ -13336,6 +13412,7 @@ dependencies = [ [[package]] name = "xcm-procedural" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot?branch=master#5c3067e9d58487e866fc8e4ad594fd22e9132c93" dependencies = [ "Inflector", "proc-macro2", @@ -13406,71 +13483,3 @@ dependencies = [ "cc", "libc", ] - -[[patch.unused]] -name = "polkadot" -version = "0.9.18" - -[[patch.unused]] -name = "polkadot-primitives-test-helpers" -version = "0.9.18" - -[[patch.unused]] -name = "polkadot-test-malus" -version = "0.9.18" - -[[patch.unused]] -name = "polkadot-voter-bags" -version = "0.9.18" - -[[patch.unused]] -name = "remote-ext-tests-bags-list" -version = "0.9.18" - -[[patch.unused]] -name = "staking-miner" -version = "0.9.18" - -[[patch.unused]] -name = "test-parachain-adder" -version = "0.9.18" - -[[patch.unused]] -name = "test-parachain-adder-collator" -version = "0.9.18" - -[[patch.unused]] -name = "test-parachain-halt" -version = "0.9.18" - -[[patch.unused]] -name = "test-parachain-undying" -version = "0.9.18" - -[[patch.unused]] -name = "test-parachain-undying-collator" -version = "0.9.18" - -[[patch.unused]] -name = "test-parachains" -version = "0.9.18" - -[[patch.unused]] -name = "xcm-executor-integration-tests" -version = "0.9.18" - -[[patch.unused]] -name = "xcm-simulator" -version = "0.9.18" - -[[patch.unused]] -name = "xcm-simulator-example" -version = "0.9.18" - -[[patch.unused]] -name = "xcm-simulator-fuzzer" -version = "0.9.18" - -[[patch.unused]] -name = "zombienet-backchannel" -version = "0.9.18" diff --git a/pallets/xcmp-queue/Cargo.toml b/pallets/xcmp-queue/Cargo.toml index d457e322bd4..f3d871e1b1d 100644 --- a/pallets/xcmp-queue/Cargo.toml +++ b/pallets/xcmp-queue/Cargo.toml @@ -18,6 +18,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", default-features sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } # Polkadot +polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } @@ -48,6 +49,7 @@ std = [ "frame-support/std", "frame-system/std", "log/std", + "polkadot-runtime-common/std", "sp-io/std", "sp-runtime/std", "sp-std/std", diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index a1ba81b6d73..f7e52a67567 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -47,6 +47,7 @@ use frame_support::{ traits::{EnsureOrigin, Get}, weights::{constants::WEIGHT_PER_MILLIS, Weight}, }; +use polkadot_runtime_common::xcm_sender::ConstantPrice; use rand_chacha::{ rand_core::{RngCore, SeedableRng}, ChaChaRng, @@ -1090,7 +1091,6 @@ impl PriceForSiblingDelivery for () { } } -pub struct ConstantPrice(sp_std::marker::PhantomData); impl> PriceForSiblingDelivery for ConstantPrice { fn price_for_sibling_delivery(_: ParaId, _: &Xcm<()>) -> MultiAssets { T::get() diff --git a/primitives/utility/Cargo.toml b/primitives/utility/Cargo.toml index 2cdb62f45c2..334e7294617 100644 --- a/primitives/utility/Cargo.toml +++ b/primitives/utility/Cargo.toml @@ -16,6 +16,7 @@ sp-trie = { git = "https://github.com/paritytech/substrate", default-features = # Polkadot polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } polkadot-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } @@ -32,6 +33,7 @@ std = [ "sp-std/std", "sp-trie/std", "polkadot-core-primitives/std", + "polkadot-runtime-common/std", "polkadot-parachain/std", "polkadot-primitives/std", "cumulus-primitives-core/std", diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index 621506c146b..525b94c0b09 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -22,6 +22,7 @@ use codec::Encode; use cumulus_primitives_core::{MessageSendError, UpwardMessageSender}; use frame_support::traits::Get; +use polkadot_runtime_common::xcm_sender::ConstantPrice; use sp_std::{marker::PhantomData, prelude::*}; use xcm::{latest::prelude::*, WrapVersion}; @@ -35,7 +36,6 @@ impl PriceForParentDelivery for () { } } -pub struct ConstantPrice(PhantomData); impl> PriceForParentDelivery for ConstantPrice { fn price_for_parent_delivery(_: &Xcm<()>) -> MultiAssets { T::get() From 667273a7be384d3cbcf6d57ea642be27647eef9c Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sun, 14 Aug 2022 09:50:01 +0800 Subject: [PATCH 027/263] Fix naming --- pallets/dmp-queue/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 608bc3bfebf..a50b3df1cfc 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -239,7 +239,7 @@ pub mod pallet { Ok(0) }, Ok(Ok(x)) => { - let outcome = T::XcmExecutor::execute_xcm(Parent, x, id, limit); + let outcome = T::XcmExecutor::execute_xcm(Parent, x, message_id, limit); match outcome { Outcome::Error(XcmError::WeightLimitReached(required)) => Err((message_id, required)), From 0e6fd2dcc773455ec680f7ad84ac503085f0dafb Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sun, 14 Aug 2022 09:52:40 +0800 Subject: [PATCH 028/263] cargo fmt --- parachains/pallets/ping/src/lib.rs | 8 +++++++- primitives/utility/src/lib.rs | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/parachains/pallets/ping/src/lib.rs b/parachains/pallets/ping/src/lib.rs index 518a9e43ecb..f6e85a7bb89 100644 --- a/parachains/pallets/ping/src/lib.rs +++ b/parachains/pallets/ping/src/lib.rs @@ -118,7 +118,13 @@ pub mod pallet { ) { Ok((hash, cost)) => { Pings::::insert(seq, n); - Self::deposit_event(Event::PingSent(para, seq, payload.to_vec(), hash, cost)); + Self::deposit_event(Event::PingSent( + para, + seq, + payload.to_vec(), + hash, + cost, + )); }, Err(e) => { Self::deposit_event(Event::ErrorSendingPing( diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index f4aa8b7546f..6e2362667ea 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -22,7 +22,10 @@ use codec::Encode; use cumulus_primitives_core::{MessageSendError, UpwardMessageSender}; use frame_support::{ - traits::{Get, tokens::{fungibles, fungibles::Inspect}}, + traits::{ + tokens::{fungibles, fungibles::Inspect}, + Get, + }, weights::Weight, }; use polkadot_runtime_common::xcm_sender::ConstantPrice; From 620c42cd0c1e261f6e7c411adcddc32d863ffe3b Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sun, 14 Aug 2022 10:11:01 +0800 Subject: [PATCH 029/263] Fixes --- primitives/utility/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index 6e2362667ea..38d08381723 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -291,7 +291,7 @@ impl< if let Some(receiver) = ReceiverAccount::get() { let ok = FungiblesMutateAdapter::deposit_asset( &revenue, - &(X1(AccountId32 { network: Any, id: receiver.into() }).into()), + &(X1(AccountId32 { network: None, id: receiver.into() }).into()), ) .is_ok(); From 4e53da1c7226b44d209933179c9cd4a36061806f Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 24 Aug 2022 22:30:41 +0800 Subject: [PATCH 030/263] Fixes --- primitives/utility/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index 38d08381723..2c19674bc77 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -292,6 +292,9 @@ impl< let ok = FungiblesMutateAdapter::deposit_asset( &revenue, &(X1(AccountId32 { network: None, id: receiver.into() }).into()), + // We aren't able to track the XCM that initiated the fee deposit, so we create a + // fake message hash here + &XcmContext::with_message_hash([0; 32]), ) .is_ok(); From 9ee4759fcf4bdae2d6dbeef6bb4b8cd29a9f3dbf Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 24 Aug 2022 22:42:49 +0800 Subject: [PATCH 031/263] Fixes --- parachains/common/src/xcm_config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parachains/common/src/xcm_config.rs b/parachains/common/src/xcm_config.rs index d56876d60ca..c0a71718bb4 100644 --- a/parachains/common/src/xcm_config.rs +++ b/parachains/common/src/xcm_config.rs @@ -23,7 +23,7 @@ where { fn should_execute( origin: &MultiLocation, - message: &mut Xcm, + message: &mut [Instruction], max_weight: Weight, weight_credit: &mut Weight, ) -> Result<(), ()> { @@ -37,7 +37,7 @@ pub struct DenyReserveTransferToRelayChain; impl ShouldExecute for DenyReserveTransferToRelayChain { fn should_execute( origin: &MultiLocation, - message: &mut Xcm, + message: &mut [Instruction], _max_weight: Weight, _weight_credit: &mut Weight, ) -> Result<(), ()> { From 187c47a6828ca674b46b913e4e60279853af6a2f Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 24 Aug 2022 22:45:57 +0800 Subject: [PATCH 032/263] Add CallDispatcher --- parachain-template/runtime/src/xcm_config.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/parachain-template/runtime/src/xcm_config.rs b/parachain-template/runtime/src/xcm_config.rs index 48b1e9ce708..007c1d55acf 100644 --- a/parachain-template/runtime/src/xcm_config.rs +++ b/parachain-template/runtime/src/xcm_config.rs @@ -188,6 +188,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; + type CallDispatcher = Call; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. From bd3ce91d85338e694a490131ccf3773fd2362f5b Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 24 Aug 2022 23:03:03 +0800 Subject: [PATCH 033/263] Fixes --- parachain-template/runtime/src/xcm_config.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/parachain-template/runtime/src/xcm_config.rs b/parachain-template/runtime/src/xcm_config.rs index 007c1d55acf..ccbb6eb99d5 100644 --- a/parachain-template/runtime/src/xcm_config.rs +++ b/parachain-template/runtime/src/xcm_config.rs @@ -104,7 +104,7 @@ where { fn should_execute( origin: &MultiLocation, - message: &mut Xcm, + message: &mut [Instruction], max_weight: Weight, weight_credit: &mut Weight, ) -> Result<(), ()> { @@ -118,11 +118,11 @@ pub struct DenyReserveTransferToRelayChain; impl ShouldExecute for DenyReserveTransferToRelayChain { fn should_execute( origin: &MultiLocation, - message: &mut Xcm, + message: &mut [Instruction], _max_weight: Weight, _weight_credit: &mut Weight, ) -> Result<(), ()> { - if message.0.iter().any(|inst| { + if message.iter().any(|inst| { matches!( inst, InitiateReserveWithdraw { @@ -141,7 +141,7 @@ impl ShouldExecute for DenyReserveTransferToRelayChain { // An unexpected reserve transfer has arrived from the Relay Chain. Generally, `IsReserve` // should not allow this, but we just log it here. if matches!(origin, MultiLocation { parents: 1, interior: Here }) && - message.0.iter().any(|inst| matches!(inst, ReserveAssetDeposited { .. })) + message.iter().any(|inst| matches!(inst, ReserveAssetDeposited { .. })) { log::warn!( target: "xcm::barriers", From ad1cbecabd70495aa2891785aba843cdf8907026 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 24 Aug 2022 23:15:56 +0800 Subject: [PATCH 034/263] Fixes --- parachains/common/src/xcm_config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parachains/common/src/xcm_config.rs b/parachains/common/src/xcm_config.rs index c0a71718bb4..bfcaa9d49e8 100644 --- a/parachains/common/src/xcm_config.rs +++ b/parachains/common/src/xcm_config.rs @@ -41,7 +41,7 @@ impl ShouldExecute for DenyReserveTransferToRelayChain { _max_weight: Weight, _weight_credit: &mut Weight, ) -> Result<(), ()> { - if message.0.iter().any(|inst| { + if message.iter().any(|inst| { matches!( inst, InitiateReserveWithdraw { @@ -60,7 +60,7 @@ impl ShouldExecute for DenyReserveTransferToRelayChain { // An unexpected reserve transfer has arrived from the Relay Chain. Generally, `IsReserve` // should not allow this, but we just log it here. if matches!(origin, MultiLocation { parents: 1, interior: Here }) && - message.0.iter().any(|inst| matches!(inst, ReserveAssetDeposited { .. })) + message.iter().any(|inst| matches!(inst, ReserveAssetDeposited { .. })) { log::warn!( target: "xcm::barrier", From 5a34514967e0d9b6f82d75d8634f9d51f6f7aaaf Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 25 Aug 2022 00:17:33 +0800 Subject: [PATCH 035/263] Fixes --- .../assets/statemine/src/xcm_config.rs | 1 + .../assets/statemint/src/xcm_config.rs | 1 + .../assets/westmint/src/xcm_config.rs | 1 + .../collectives-polkadot/src/xcm_config.rs | 26 ++++++++++---- .../contracts-rococo/src/xcm_config.rs | 1 + .../runtimes/starters/shell/src/xcm_config.rs | 1 + .../runtimes/testing/penpal/src/xcm_config.rs | 35 +++++++++++++------ 7 files changed, 49 insertions(+), 17 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 03aca7ded13..2b5598fce16 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -208,6 +208,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; + type CallDispatcher = Call; } /// Converts a local signed origin into an XCM multilocation. diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index 5d34e56b43b..ce6fd6c29b2 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -184,6 +184,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; + type CallDispatcher = Call; } /// Converts a local signed origin into an XCM multilocation. diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index 01d2c33ab79..c94feb50d75 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -204,6 +204,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; + type CallDispatcher = Call; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index 78f5b224f8d..1d927fd3f57 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -19,7 +19,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{Everything, Nothing}, + traits::{ConstU32, Everything, Nothing}, weights::Weight, }; use pallet_xcm::XcmPassthrough; @@ -43,8 +43,8 @@ parameter_types! { pub const DotLocation: MultiLocation = MultiLocation::parent(); pub const RelayNetwork: NetworkId = NetworkId::Polkadot; pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); - pub const Local: MultiLocation = Here.into(); + pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); + pub const Local: MultiLocation = Here.into_location(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); } @@ -102,6 +102,7 @@ parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. pub UnitWeightCost: Weight = 1_000_000_000; pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; } match_types! { @@ -141,7 +142,7 @@ impl xcm_executor::Config for XcmConfig { // where allowed (e.g. with the Relay Chain). type IsReserve = (); type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of DOT - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = @@ -150,6 +151,14 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = Call; } /// Converts a local signed origin into an XCM multilocation. @@ -160,7 +169,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); @@ -178,11 +187,16 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Nothing; // This parachain is not meant as a reserve location. type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index 8252800ac60..8b30214daf8 100644 --- a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -157,6 +157,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; + type CallDispatcher = Call; } /// Converts a local signed origin into an XCM multilocation. diff --git a/parachains/runtimes/starters/shell/src/xcm_config.rs b/parachains/runtimes/starters/shell/src/xcm_config.rs index 370d6f9d8bb..af846f7181f 100644 --- a/parachains/runtimes/starters/shell/src/xcm_config.rs +++ b/parachains/runtimes/starters/shell/src/xcm_config.rs @@ -74,6 +74,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; + type CallDispatcher = Call; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index 3fb2192cd7a..36ce4954838 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -30,7 +30,7 @@ use frame_support::{ log, match_types, parameter_types, traits::{ fungibles::{self, Balanced, CreditOf}, - Contains, Everything, Get, Nothing, + ConstU32, Contains, Everything, Get, Nothing, }, weights::Weight, }; @@ -43,11 +43,10 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteAssetId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, - FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - UsingComponents, + ConvertedAssetId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, + IsConcrete, LocationInverter, NativeAsset, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{ traits::{FilterAssetLocation, JustTry, ShouldExecute}, @@ -58,7 +57,7 @@ parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); pub const RelayNetwork: NetworkId = NetworkId::Any; pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); + pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used @@ -92,7 +91,7 @@ pub type FungiblesTransactor = FungiblesAdapter< // Use this fungibles implementation: Assets, // Use this currency when it is a fungible asset matching the given location or name: - ConvertedConcreteAssetId< + ConvertedAssetId< AssetIdPalletAssets, Balance, AsPrefixedGeneralIndex, @@ -137,6 +136,7 @@ parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. pub UnitWeightCost: Weight = 1_000_000_000; pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; } match_types! { @@ -328,7 +328,7 @@ impl xcm_executor::Config for XcmConfig { type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = MultiNativeAsset; // TODO: maybe needed to be replaced by Reserves type IsTeleporter = NativeAsset; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = @@ -337,6 +337,14 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = Call; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. @@ -346,7 +354,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); @@ -363,13 +371,18 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Origin = Origin; type Call = Call; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; // ^ Override for AdvertisedXcmVersion default type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; } impl cumulus_pallet_xcm::Config for Runtime { From 67c99fdc4bf49df8e9e24f932eb239bdcbb26dc0 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 25 Aug 2022 00:36:04 +0800 Subject: [PATCH 036/263] Fixes --- parachains/runtimes/testing/rococo-parachain/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 4b00ff86c41..94948ddc5cb 100644 --- a/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -79,7 +79,7 @@ use xcm_builder::{ SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; -use xcm_executor::{Config, XcmExecutor}; +use xcm_executor::XcmExecutor; pub type SessionHandlers = (); @@ -399,7 +399,7 @@ parameter_types! { pub type Reserves = (NativeAsset, AssetsFrom); pub struct XcmConfig; -impl Config for XcmConfig { +impl xcm_executor::Config for XcmConfig { type Call = Call; type XcmSender = XcmRouter; // How to withdraw and deposit an asset. @@ -422,6 +422,7 @@ impl Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; + type CallDispatcher = Call; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. From 1fc2cb4bd2dcf7adc0ad8d973742a143d6580805 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 25 Aug 2022 00:49:38 +0800 Subject: [PATCH 037/263] Fixes --- .../collectives/collectives-polkadot/src/xcm_config.rs | 4 ++-- parachains/runtimes/testing/penpal/src/xcm_config.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index 1d927fd3f57..fd6a29476fd 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -32,8 +32,8 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, - ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index 36ce4954838..97b53a9b72f 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -44,9 +44,9 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedAssetId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, - IsConcrete, LocationInverter, NativeAsset, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + IsConcrete, NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{ traits::{FilterAssetLocation, JustTry, ShouldExecute}, From 0f3f8b8623150ebf6b9410d6b05363dc2277acd8 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 25 Aug 2022 01:01:51 +0800 Subject: [PATCH 038/263] Fixes --- .../runtimes/collectives/collectives-polkadot/src/impls.rs | 2 +- parachains/runtimes/collectives/collectives-polkadot/src/lib.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/impls.rs b/parachains/runtimes/collectives/collectives-polkadot/src/impls.rs index 4f821802172..da901ae0c33 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/impls.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/impls.rs @@ -65,7 +65,7 @@ where ::Origin::signed(temp_account.into()), Box::new(Parent.into()), Box::new( - Junction::AccountId32 { network: NetworkId::Any, id: treasury_acc.into() } + Junction::AccountId32 { network: None, id: treasury_acc.into() } .into() .into(), ), diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/lib.rs b/parachains/runtimes/collectives/collectives-polkadot/src/lib.rs index cd947446e26..07a5239872d 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/lib.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/lib.rs @@ -367,6 +367,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = RootOrExecutiveSimpleMajority; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; + type PriceForSiblingDelivery = (); } impl cumulus_pallet_dmp_queue::Config for Runtime { From 57e44204d591c227dccdd8366d08cf12e680165e Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 25 Aug 2022 01:16:01 +0800 Subject: [PATCH 039/263] Fixes --- .../runtimes/collectives/collectives-polkadot/src/impls.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/impls.rs b/parachains/runtimes/collectives/collectives-polkadot/src/impls.rs index da901ae0c33..34656f46e1a 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/impls.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/impls.rs @@ -21,7 +21,7 @@ use frame_support::{ }; use pallet_alliance::{ProposalIndex, ProposalProvider}; use sp_std::{boxed::Box, marker::PhantomData}; -use xcm::latest::{Fungibility, Junction, NetworkId, Parent}; +use xcm::latest::{Fungibility, Junction, Parent}; type AccountIdOf = ::AccountId; @@ -66,7 +66,7 @@ where Box::new(Parent.into()), Box::new( Junction::AccountId32 { network: None, id: treasury_acc.into() } - .into() + .into_location() .into(), ), Box::new((Parent, imbalance).into()), From d1230e3d77f8696a1928792bf7412380e5a982a8 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 25 Aug 2022 01:39:43 +0800 Subject: [PATCH 040/263] Fixes --- parachains/runtimes/assets/westmint/src/xcm_config.rs | 2 +- parachains/runtimes/testing/penpal/src/xcm_config.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index c94feb50d75..3d5f6f9b804 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -179,7 +179,7 @@ impl xcm_executor::Config for XcmConfig { WeightToFee, pallet_assets::BalanceToAssetBalance, >, - ConvertedConcreteAssetId< + ConvertedConcreteId< AssetId, Balance, AsPrefixedGeneralIndex, diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index 97b53a9b72f..314faf38a64 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -43,7 +43,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedAssetId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, IsConcrete, NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, @@ -91,7 +91,7 @@ pub type FungiblesTransactor = FungiblesAdapter< // Use this fungibles implementation: Assets, // Use this currency when it is a fungible asset matching the given location or name: - ConvertedAssetId< + ConvertedConcreteId< AssetIdPalletAssets, Balance, AsPrefixedGeneralIndex, From 3ba852b3bfb2f45c6b3d63f5215582b66f4412d7 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 25 Aug 2022 02:11:44 +0800 Subject: [PATCH 041/263] Fixes --- parachains/runtimes/assets/statemine/src/xcm_config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 2b5598fce16..f79eca94b3a 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -183,7 +183,7 @@ impl xcm_executor::Config for XcmConfig { WeightToFee, pallet_assets::BalanceToAssetBalance, >, - ConvertedConcreteAssetId< + ConvertedConcreteId< AssetId, Balance, AsPrefixedGeneralIndex, From 31accbd975a6f858e633bec03cd441b7042cf8ed Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 25 Aug 2022 16:38:08 +0800 Subject: [PATCH 042/263] Fixes --- .../runtimes/testing/penpal/src/xcm_config.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index 314faf38a64..c2ea08a8d3d 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -30,7 +30,7 @@ use frame_support::{ log, match_types, parameter_types, traits::{ fungibles::{self, Balanced, CreditOf}, - ConstU32, Contains, Everything, Get, Nothing, + ConstU32, Contains, ContainsPair, Everything, Get, Nothing, }, weights::Weight, }; @@ -49,7 +49,7 @@ use xcm_builder::{ SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{ - traits::{FilterAssetLocation, JustTry, ShouldExecute}, + traits::{JustTry, ShouldExecute}, XcmExecutor, }; @@ -164,7 +164,7 @@ where { fn should_execute( origin: &MultiLocation, - message: &mut Xcm, + message: &mut [Instruction], max_weight: Weight, weight_credit: &mut Weight, ) -> Result<(), ()> { @@ -178,11 +178,11 @@ pub struct DenyReserveTransferToRelayChain; impl ShouldExecute for DenyReserveTransferToRelayChain { fn should_execute( origin: &MultiLocation, - message: &mut Xcm, + message: &mut [Instruction], _max_weight: Weight, _weight_credit: &mut Weight, ) -> Result<(), ()> { - if message.0.iter().any(|inst| { + if message.iter().any(|inst| { matches!( inst, InitiateReserveWithdraw { @@ -200,7 +200,7 @@ impl ShouldExecute for DenyReserveTransferToRelayChain { // allow reserve transfers to arrive from relay chain if matches!(origin, MultiLocation { parents: 1, interior: Here }) && - message.0.iter().any(|inst| matches!(inst, ReserveAssetDeposited { .. })) + message.iter().any(|inst| matches!(inst, ReserveAssetDeposited { .. })) { log::warn!( target: "xcm::barriers", @@ -233,8 +233,8 @@ pub type AccountIdOf = ::AccountId; /// Asset filter that allows all assets from a certain location. pub struct AssetsFrom(PhantomData); -impl> FilterAssetLocation for AssetsFrom { - fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { +impl> ContainsPair for AssetsFrom { + fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool { let loc = T::get(); &loc == origin && matches!(asset, MultiAsset { id: AssetId::Concrete(asset_loc), fun: Fungible(_a) } @@ -297,7 +297,7 @@ impl Reserve for MultiAsset { /// A `FilterAssetLocation` implementation. Filters multi native assets whose /// reserve is same with `origin`. pub struct MultiNativeAsset; -impl FilterAssetLocation for MultiNativeAsset { +impl ContainsPair for MultiNativeAsset { fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { if let Some(ref reserve) = asset.reserve() { if reserve == origin { From 004f0449f46664619095e8b5303dfeea95d72235 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 25 Aug 2022 16:57:51 +0800 Subject: [PATCH 043/263] Fixes --- parachains/runtimes/testing/penpal/src/xcm_config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index c2ea08a8d3d..c2ea98591b8 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -298,7 +298,7 @@ impl Reserve for MultiAsset { /// reserve is same with `origin`. pub struct MultiNativeAsset; impl ContainsPair for MultiNativeAsset { - fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { + fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool { if let Some(ref reserve) = asset.reserve() { if reserve == origin { return true From bee34ba3f96139a07d2147946ed241351a80017c Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 25 Aug 2022 18:13:24 +0800 Subject: [PATCH 044/263] Fixes --- parachains/runtimes/testing/penpal/src/xcm_config.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index c2ea98591b8..25d72572012 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -55,7 +55,7 @@ use xcm_executor::{ parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Any; + pub const RelayNetwork: Option = None; pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); } @@ -388,4 +388,5 @@ impl pallet_xcm::Config for Runtime { impl cumulus_pallet_xcm::Config for Runtime { type Event = Event; type XcmExecutor = XcmExecutor; + type PriceForSiblingDelivery = (); } From 5c54d38b428f02e9137924bdf32efd155810dfec Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 25 Aug 2022 18:50:29 +0800 Subject: [PATCH 045/263] Fixes --- parachains/runtimes/testing/penpal/src/lib.rs | 1 + parachains/runtimes/testing/penpal/src/xcm_config.rs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/parachains/runtimes/testing/penpal/src/lib.rs b/parachains/runtimes/testing/penpal/src/lib.rs index f0f2cb2a767..4842cc6a75c 100644 --- a/parachains/runtimes/testing/penpal/src/lib.rs +++ b/parachains/runtimes/testing/penpal/src/lib.rs @@ -435,6 +435,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = (); + type PriceForSiblingDelivery = (); } impl cumulus_pallet_dmp_queue::Config for Runtime { diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index 25d72572012..31b5cc7848f 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -388,5 +388,4 @@ impl pallet_xcm::Config for Runtime { impl cumulus_pallet_xcm::Config for Runtime { type Event = Event; type XcmExecutor = XcmExecutor; - type PriceForSiblingDelivery = (); } From 516669814078a308e8de659979aa0f97e8d67eff Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 25 Aug 2022 21:18:07 +0800 Subject: [PATCH 046/263] Fixes --- pallets/xcmp-queue/src/mock.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/xcmp-queue/src/mock.rs b/pallets/xcmp-queue/src/mock.rs index 62d56bfbfb8..bd30f52fb27 100644 --- a/pallets/xcmp-queue/src/mock.rs +++ b/pallets/xcmp-queue/src/mock.rs @@ -162,6 +162,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; + type CallDispatcher = Call; } pub type XcmRouter = ( From 1dba83a9031c22994795971fac9b1a8638c9efb5 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 22 Sep 2022 22:26:02 +0800 Subject: [PATCH 047/263] Fixes --- parachain-template/runtime/src/xcm_config.rs | 4 ++-- parachains/common/src/xcm_config.rs | 4 ++-- parachains/runtimes/testing/penpal/src/xcm_config.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/parachain-template/runtime/src/xcm_config.rs b/parachain-template/runtime/src/xcm_config.rs index 61349391f85..65ac5c83053 100644 --- a/parachain-template/runtime/src/xcm_config.rs +++ b/parachain-template/runtime/src/xcm_config.rs @@ -103,7 +103,7 @@ where { fn should_execute( origin: &MultiLocation, - message: &mut [Instruction], + message: &mut [Instruction], max_weight: XCMWeight, weight_credit: &mut XCMWeight, ) -> Result<(), ()> { @@ -117,7 +117,7 @@ pub struct DenyReserveTransferToRelayChain; impl ShouldExecute for DenyReserveTransferToRelayChain { fn should_execute( origin: &MultiLocation, - message: &mut [Instruction], + message: &mut [Instruction], _max_weight: XCMWeight, _weight_credit: &mut XCMWeight, ) -> Result<(), ()> { diff --git a/parachains/common/src/xcm_config.rs b/parachains/common/src/xcm_config.rs index 4b78f214000..73e6d9e93b6 100644 --- a/parachains/common/src/xcm_config.rs +++ b/parachains/common/src/xcm_config.rs @@ -24,7 +24,7 @@ where { fn should_execute( origin: &MultiLocation, - message: &mut [Instruction], + message: &mut [Instruction], max_weight: XCMWeight, weight_credit: &mut XCMWeight, ) -> Result<(), ()> { @@ -38,7 +38,7 @@ pub struct DenyReserveTransferToRelayChain; impl ShouldExecute for DenyReserveTransferToRelayChain { fn should_execute( origin: &MultiLocation, - message: &mut [Instruction], + message: &mut [Instruction], _max_weight: XCMWeight, _weight_credit: &mut XCMWeight, ) -> Result<(), ()> { diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index 6d96e59d3b2..5df62e71d7c 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -164,7 +164,7 @@ where { fn should_execute( origin: &MultiLocation, - message: &mut [Instruction], + message: &mut [Instruction], max_weight: XCMWeight, weight_credit: &mut XCMWeight, ) -> Result<(), ()> { @@ -178,7 +178,7 @@ pub struct DenyReserveTransferToRelayChain; impl ShouldExecute for DenyReserveTransferToRelayChain { fn should_execute( origin: &MultiLocation, - message: &mut [Instruction], + message: &mut [Instruction], _max_weight: XCMWeight, _weight_credit: &mut XCMWeight, ) -> Result<(), ()> { From e02878e87a46a865aec00ddd11351403aaee3bae Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 22 Sep 2022 23:16:22 +0800 Subject: [PATCH 048/263] Fixes --- pallets/dmp-queue/src/lib.rs | 3 ++- pallets/parachain-system/src/validate_block/tests.rs | 3 ++- pallets/xcm/src/lib.rs | 3 ++- parachains/common/src/xcm_config.rs | 8 +++++--- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 5c3a0dc5724..3dfcae436fb 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -240,7 +240,8 @@ pub mod pallet { Ok(Weight::zero()) }, Ok(Ok(x)) => { - let outcome = T::XcmExecutor::execute_xcm(Parent, x, message_id, limit.ref_time()); + let outcome = + T::XcmExecutor::execute_xcm(Parent, x, message_id, limit.ref_time()); match outcome { Outcome::Error(XcmError::WeightLimitReached(required)) => Err((message_id, Weight::from_ref_time(required))), diff --git a/pallets/parachain-system/src/validate_block/tests.rs b/pallets/parachain-system/src/validate_block/tests.rs index e06c2cafdcf..6087e6a29a3 100644 --- a/pallets/parachain-system/src/validate_block/tests.rs +++ b/pallets/parachain-system/src/validate_block/tests.rs @@ -284,7 +284,8 @@ fn check_inherents_are_unsigned_and_before_all_other_extrinsics() { .expect("Runs the test"); assert!(output.status.success()); - assert!(String::from_utf8(output.stderr).unwrap() + assert!(String::from_utf8(output.stderr) + .unwrap() .contains("Could not find `set_validation_data` inherent")); } } diff --git a/pallets/xcm/src/lib.rs b/pallets/xcm/src/lib.rs index 1d87187cdef..bd015dce171 100644 --- a/pallets/xcm/src/lib.rs +++ b/pallets/xcm/src/lib.rs @@ -157,7 +157,8 @@ impl DmpMessageHandler for LimitAndDropDmpExecution { Ok(Err(())) => Pallet::::deposit_event(Event::UnsupportedVersion(id)), Ok(Ok(x)) => { let weight_limit = limit.saturating_sub(used); - let outcome = T::XcmExecutor::execute_xcm(Parent, x, id, weight_limit.ref_time()); + let outcome = + T::XcmExecutor::execute_xcm(Parent, x, id, weight_limit.ref_time()); used += Weight::from_ref_time(outcome.weight_used()); Pallet::::deposit_event(Event::ExecutedDownward(id, outcome)); }, diff --git a/parachains/common/src/xcm_config.rs b/parachains/common/src/xcm_config.rs index 73e6d9e93b6..91d1c46a2fa 100644 --- a/parachains/common/src/xcm_config.rs +++ b/parachains/common/src/xcm_config.rs @@ -2,7 +2,7 @@ use crate::impls::AccountIdOf; use core::marker::PhantomData; use frame_support::{ log, - traits::{fungibles::Inspect, tokens::BalanceConversion}, + traits::{fungibles::Inspect, tokens::BalanceConversion, ContainsPair}, weights::{Weight, WeightToFee, WeightToFeePolynomial}, }; use sp_runtime::traits::Get; @@ -111,8 +111,10 @@ where /// Accepts an asset if it is a native asset from a particular `MultiLocation`. pub struct ConcreteNativeAssetFrom(PhantomData); -impl> FilterAssetLocation for ConcreteNativeAssetFrom { - fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { +impl> ContainsPair + for ConcreteNativeAssetFrom +{ + fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool { log::trace!(target: "xcm::filter_asset_location", "ConcreteNativeAsset asset: {:?}, origin: {:?}, location: {:?}", asset, origin, Location::get()); From 25958aa09f29522f847781c870a943972f308dad Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Fri, 23 Sep 2022 20:27:31 +0800 Subject: [PATCH 049/263] Fixes --- parachains/common/src/xcm_config.rs | 2 +- .../runtimes/contracts/contracts-rococo/src/xcm_config.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/parachains/common/src/xcm_config.rs b/parachains/common/src/xcm_config.rs index 91d1c46a2fa..9a9774ae94f 100644 --- a/parachains/common/src/xcm_config.rs +++ b/parachains/common/src/xcm_config.rs @@ -7,7 +7,7 @@ use frame_support::{ }; use sp_runtime::traits::Get; use xcm::latest::{prelude::*, Weight as XCMWeight}; -use xcm_executor::traits::{FilterAssetLocation, ShouldExecute}; +use xcm_executor::traits::ShouldExecute; //TODO: move DenyThenTry to polkadot's xcm module. /// Deny executing the XCM if it matches any of the Deny filter regardless of anything else. diff --git a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index a2bd6284f5c..fc0b834c4a3 100644 --- a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -156,7 +156,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; - type CallDispatcher = Call; + type CallDispatcher = RuntimeCall; } /// Converts a local signed origin into an XCM multilocation. From 9e309844a8207a05223bcf9e8be83a6e4ff3eda4 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 27 Sep 2022 14:52:04 +0800 Subject: [PATCH 050/263] Fixes --- pallets/dmp-queue/src/lib.rs | 2 +- pallets/xcmp-queue/src/mock.rs | 2 +- parachain-template/runtime/src/xcm_config.rs | 2 +- .../runtimes/assets/statemine/src/xcm_config.rs | 11 +++++------ .../runtimes/assets/statemint/src/xcm_config.rs | 2 +- parachains/runtimes/assets/westmint/src/xcm_config.rs | 2 +- .../collectives-polkadot/src/xcm_config.rs | 2 +- parachains/runtimes/starters/shell/src/xcm_config.rs | 2 +- parachains/runtimes/testing/penpal/src/xcm_config.rs | 2 +- .../runtimes/testing/rococo-parachain/src/lib.rs | 2 +- 10 files changed, 14 insertions(+), 15 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 3dfcae436fb..6cddb956d52 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -434,7 +434,7 @@ mod tests { } pub struct MockExec; - impl ExecuteXcm for MockExec { + impl ExecuteXcm for MockExec { type Prepared = Weightless; fn prepare(message: Xcm) -> Result { diff --git a/pallets/xcmp-queue/src/mock.rs b/pallets/xcmp-queue/src/mock.rs index 17ca7129a37..bd71ba363e5 100644 --- a/pallets/xcmp-queue/src/mock.rs +++ b/pallets/xcmp-queue/src/mock.rs @@ -162,7 +162,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; - type CallDispatcher = Call; + type CallDispatcher = RuntimeCall; } pub type XcmRouter = ( diff --git a/parachain-template/runtime/src/xcm_config.rs b/parachain-template/runtime/src/xcm_config.rs index 65ac5c83053..c023a5a6e6d 100644 --- a/parachain-template/runtime/src/xcm_config.rs +++ b/parachain-template/runtime/src/xcm_config.rs @@ -187,7 +187,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; - type CallDispatcher = Call; + type CallDispatcher = RuntimeCall; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index ccf96219c40..ed04677be4c 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -34,11 +34,10 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, - IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, - WeightInfoBounds, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, + NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, }; use xcm_executor::{traits::JustTry, XcmExecutor}; @@ -210,7 +209,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; - type CallDispatcher = Call; + type CallDispatcher = RuntimeCall; } /// Converts a local signed origin into an XCM multilocation. diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index 3daa00b4f3f..16720f6fefc 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -186,7 +186,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; - type CallDispatcher = Call; + type CallDispatcher = RuntimeCall; } /// Converts a local signed origin into an XCM multilocation. diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index a46cbfa2e56..837a6522bf0 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -206,7 +206,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; - type CallDispatcher = Call; + type CallDispatcher = RuntimeCall; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index e3e4e93d5d9..1e7b17de09a 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -158,7 +158,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; - type CallDispatcher = Call; + type CallDispatcher = RuntimeCall; } /// Converts a local signed origin into an XCM multilocation. diff --git a/parachains/runtimes/starters/shell/src/xcm_config.rs b/parachains/runtimes/starters/shell/src/xcm_config.rs index 51f774f6d60..6706c6af6bb 100644 --- a/parachains/runtimes/starters/shell/src/xcm_config.rs +++ b/parachains/runtimes/starters/shell/src/xcm_config.rs @@ -74,7 +74,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; - type CallDispatcher = Call; + type CallDispatcher = RuntimeCall; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index 5df62e71d7c..62ae7fc3a95 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -344,7 +344,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; - type CallDispatcher = Call; + type CallDispatcher = RuntimeCall; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 7e81edaec3a..ec0fd38a3ed 100644 --- a/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -424,7 +424,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; - type CallDispatcher = Call; + type CallDispatcher = RuntimeCall; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. From 013534a684a47c60fe44763e84b9722fd2f6a792 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 27 Sep 2022 16:39:24 +0800 Subject: [PATCH 051/263] Fixes --- pallets/dmp-queue/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 6cddb956d52..c42024e9f8a 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -31,7 +31,10 @@ pub use pallet::*; use scale_info::TypeInfo; use sp_runtime::RuntimeDebug; use sp_std::{convert::TryFrom, prelude::*}; -use xcm::{latest::prelude::*, VersionedXcm, MAX_XCM_DECODE_DEPTH}; +use xcm::{ + latest::{prelude::*, Weight as XCMWeight}, + VersionedXcm, MAX_XCM_DECODE_DEPTH, +}; #[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct ConfigData { @@ -428,7 +431,7 @@ mod tests { pub enum Weightless {} impl PreparedMessage for Weightless { - fn weight_of(&self) -> Weight { + fn weight_of(&self) -> XCMWeight { unreachable!() } } From 4984ef30bf1eac29bd2c885756cf15979534ff61 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 27 Sep 2022 16:53:10 +0800 Subject: [PATCH 052/263] Remove unused import --- pallets/dmp-queue/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index c42024e9f8a..107480e39be 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -31,10 +31,7 @@ pub use pallet::*; use scale_info::TypeInfo; use sp_runtime::RuntimeDebug; use sp_std::{convert::TryFrom, prelude::*}; -use xcm::{ - latest::{prelude::*, Weight as XCMWeight}, - VersionedXcm, MAX_XCM_DECODE_DEPTH, -}; +use xcm::{latest::prelude::*, VersionedXcm, MAX_XCM_DECODE_DEPTH}; #[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct ConfigData { From 5efee4c47e2a7aaebdc2a3cde665093a0175e9f3 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 27 Sep 2022 16:56:01 +0800 Subject: [PATCH 053/263] Remove unused import --- parachains/runtimes/assets/westmint/src/xcm_config.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index 837a6522bf0..a9d80012619 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -34,11 +34,10 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, - IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, - WeightInfoBounds, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, + NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, }; use xcm_executor::{traits::JustTry, XcmExecutor}; From 483b20c699a4bf0803f35c878a65c8dbc47541b8 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 3 Oct 2022 14:51:39 +0200 Subject: [PATCH 054/263] XCMv3 fixes (#1710) * Fixes XCMv3 related Fixes XCMv3 (removed query_holding) Fixes XCMv3 - should use _depositable_count? Fixes XCMv3 - removed TrustedReserve Fixes - missing weights for statemine/statemint/westmint [DO-NOT-CHERRY-PICK] tmp return query_holding to aviod conficts to master Fixes - missing functions for pallet_xcm_benchmarks::generic::Config Fixes for XCMv3 benchmarking Fix xcm - removed query_holding * ".git/.scripts/bench-bot.sh" xcm statemine assets pallet_xcm_benchmarks::generic * ".git/.scripts/bench-bot.sh" xcm statemint assets pallet_xcm_benchmarks::generic * ".git/.scripts/bench-bot.sh" xcm westmint assets pallet_xcm_benchmarks::generic * Fix imports --- parachain-template/runtime/src/xcm_config.rs | 6 +- .../runtimes/assets/statemine/src/lib.rs | 16 +++- .../assets/statemine/src/weights/xcm/mod.rs | 96 +++++++++++++++---- .../xcm/pallet_xcm_benchmarks_generic.rs | 91 ++++++++++++++---- .../assets/statemine/src/xcm_config.rs | 7 +- .../runtimes/assets/statemint/src/lib.rs | 16 +++- .../assets/statemint/src/weights/xcm/mod.rs | 96 +++++++++++++++---- .../xcm/pallet_xcm_benchmarks_generic.rs | 90 +++++++++++++---- .../assets/statemint/src/xcm_config.rs | 16 ++-- .../runtimes/assets/westmint/src/lib.rs | 16 +++- .../assets/westmint/src/weights/xcm/mod.rs | 96 +++++++++++++++---- .../xcm/pallet_xcm_benchmarks_generic.rs | 91 ++++++++++++++---- .../assets/westmint/src/xcm_config.rs | 7 +- .../collectives-polkadot/src/xcm_config.rs | 8 +- .../contracts-rococo/src/xcm_config.rs | 6 +- .../runtimes/starters/shell/src/xcm_config.rs | 7 +- .../runtimes/testing/penpal/src/xcm_config.rs | 8 +- .../testing/rococo-parachain/src/lib.rs | 2 +- 18 files changed, 515 insertions(+), 160 deletions(-) diff --git a/parachain-template/runtime/src/xcm_config.rs b/parachain-template/runtime/src/xcm_config.rs index c023a5a6e6d..03e090009e0 100644 --- a/parachain-template/runtime/src/xcm_config.rs +++ b/parachain-template/runtime/src/xcm_config.rs @@ -1,6 +1,6 @@ use super::{ - AccountId, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, }; use core::marker::PhantomData; use frame_support::{ @@ -180,7 +180,7 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; - type PalletInstancesInfo = (); + type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); diff --git a/parachains/runtimes/assets/statemine/src/lib.rs b/parachains/runtimes/assets/statemine/src/lib.rs index 44f96c1af66..d09394c8232 100644 --- a/parachains/runtimes/assets/statemine/src/lib.rs +++ b/parachains/runtimes/assets/statemine/src/lib.rs @@ -849,7 +849,7 @@ impl_runtime_apis! { fn valid_destination() -> Result { Ok(KsmLocation::get()) } - fn worst_case_holding() -> MultiAssets { + fn worst_case_holding(_depositable_count: u32) -> MultiAssets { // A mix of fungible, non-fungible, and concrete assets. const HOLDING_FUNGIBLES: u32 = 100; const HOLDING_NON_FUNGIBLES: u32 = 100; @@ -882,7 +882,6 @@ impl_runtime_apis! { KsmLocation::get(), MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(KsmLocation::get()) }, )); - pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = None; pub const CheckedAccount: Option = None; } @@ -892,7 +891,6 @@ impl_runtime_apis! { type CheckedAccount = CheckedAccount; type TrustedTeleporter = TrustedTeleporter; - type TrustedReserve = TrustedReserve; fn get_multi_asset() -> MultiAsset { MultiAsset { @@ -909,6 +907,14 @@ impl_runtime_apis! { (0u64, Response::Version(Default::default())) } + fn worst_case_asset_exchange() -> Result<(MultiAssets, MultiAssets), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn universal_alias() -> Result { + Err(BenchmarkError::Skip) + } + fn transact_origin() -> Result { Ok(KsmLocation::get()) } @@ -923,6 +929,10 @@ impl_runtime_apis! { let ticket = MultiLocation { parents: 0, interior: Here }; Ok((origin, ticket, assets)) } + + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { + Err(BenchmarkError::Skip) + } } type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; diff --git a/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs b/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs index 7972667cf06..0ca8a414e44 100644 --- a/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs @@ -62,7 +62,12 @@ impl XcmWeightInfo for StatemineXcmWeight { fn receive_teleported_asset(assets: &MultiAssets) -> XCMWeight { assets.weigh_multi_assets(XcmFungibleWeight::::receive_teleported_asset()) } - fn query_response(_query_id: &u64, _response: &Response, _max_weight: &u64) -> XCMWeight { + fn query_response( + _query_id: &u64, + _response: &Response, + _max_weight: &u64, + _querier: &Option, + ) -> XCMWeight { XcmGeneric::::query_response().ref_time() } fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> XCMWeight { @@ -104,19 +109,11 @@ impl XcmWeightInfo for StatemineXcmWeight { fn descend_origin(_who: &InteriorMultiLocation) -> XCMWeight { XcmGeneric::::descend_origin().ref_time() } - fn report_error( - _query_id: &QueryId, - _dest: &MultiLocation, - _max_response_weight: &u64, - ) -> XCMWeight { + fn report_error(_query_response_info: &QueryResponseInfo) -> XCMWeight { XcmGeneric::::report_error().ref_time() } - fn deposit_asset( - assets: &MultiAssetFilter, - _max_assets: &u32, - _dest: &MultiLocation, - ) -> XCMWeight { + fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> XCMWeight { // Hardcoded till the XCM pallet is fixed let hardcoded_weight = Weight::from_ref_time(1_000_000_000 as u64).ref_time(); let weight = assets.weigh_multi_assets(XcmFungibleWeight::::deposit_asset()); @@ -124,13 +121,16 @@ impl XcmWeightInfo for StatemineXcmWeight { } fn deposit_reserve_asset( assets: &MultiAssetFilter, - _max_assets: &u32, _dest: &MultiLocation, _xcm: &Xcm<()>, ) -> XCMWeight { assets.weigh_multi_assets(XcmFungibleWeight::::deposit_reserve_asset()) } - fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets) -> XCMWeight { + fn exchange_asset( + _give: &MultiAssetFilter, + _receive: &MultiAssets, + _maximal: &bool, + ) -> XCMWeight { Weight::MAX.ref_time() } fn initiate_reserve_withdraw( @@ -150,13 +150,8 @@ impl XcmWeightInfo for StatemineXcmWeight { let weight = assets.weigh_multi_assets(XcmFungibleWeight::::initiate_teleport()); cmp::min(hardcoded_weight, weight) } - fn query_holding( - _query_id: &u64, - _dest: &MultiLocation, - _assets: &MultiAssetFilter, - _max_response_weight: &u64, - ) -> XCMWeight { - XcmGeneric::::query_holding().ref_time() + fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> XCMWeight { + XcmGeneric::::report_holding().ref_time() } fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> XCMWeight { XcmGeneric::::buy_execution().ref_time() @@ -185,4 +180,65 @@ impl XcmWeightInfo for StatemineXcmWeight { fn unsubscribe_version() -> XCMWeight { XcmGeneric::::unsubscribe_version().ref_time() } + fn burn_asset(assets: &MultiAssets) -> XCMWeight { + assets.weigh_multi_assets(XcmGeneric::::burn_asset()) + } + fn expect_asset(assets: &MultiAssets) -> XCMWeight { + assets.weigh_multi_assets(XcmGeneric::::expect_asset()) + } + fn expect_origin(_origin: &Option) -> XCMWeight { + XcmGeneric::::expect_origin().ref_time() + } + fn expect_error(_error: &Option<(u32, XcmError)>) -> XCMWeight { + XcmGeneric::::expect_error().ref_time() + } + fn query_pallet(_module_name: &Vec, _response_info: &QueryResponseInfo) -> XCMWeight { + XcmGeneric::::query_pallet().ref_time() + } + fn expect_pallet( + _index: &u32, + _name: &Vec, + _module_name: &Vec, + _crate_major: &u32, + _min_crate_minor: &u32, + ) -> XCMWeight { + XcmGeneric::::expect_pallet().ref_time() + } + fn report_transact_status(_response_info: &QueryResponseInfo) -> XCMWeight { + XcmGeneric::::report_transact_status().ref_time() + } + fn clear_transact_status() -> XCMWeight { + XcmGeneric::::clear_transact_status().ref_time() + } + fn universal_origin(_: &Junction) -> XCMWeight { + Weight::MAX.ref_time() + } + fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> XCMWeight { + Weight::MAX.ref_time() + } + fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn set_fees_mode(_: &bool) -> XCMWeight { + XcmGeneric::::set_fees_mode().ref_time() + } + fn set_topic(_topic: &[u8; 32]) -> XCMWeight { + XcmGeneric::::set_topic().ref_time() + } + fn clear_topic() -> XCMWeight { + XcmGeneric::::clear_topic().ref_time() + } + fn alias_origin(_: &MultiLocation) -> XCMWeight { + // XCM Executor does not currently support alias origin operations + Weight::MAX.ref_time() + } } diff --git a/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 6a9f36b7684..689c9a8220a 100644 --- a/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -18,7 +18,8 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-08-17, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-09-30, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemine-dev"), DB CACHE: 1024 // Executed Command: @@ -36,7 +37,7 @@ // --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --header=./file_header.txt // --template=./templates/xcm-bench-template.hbs -// --output=./parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +// --output=./parachains/runtimes/assets/statemine/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -54,58 +55,59 @@ impl WeightInfo { // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - pub(crate) fn query_holding() -> Weight { - Weight::from_ref_time(694_466_000 as u64) + pub(crate) fn report_holding() -> Weight { + Weight::from_ref_time(1_303_495_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn buy_execution() -> Weight { - Weight::from_ref_time(7_095_000 as u64) + Weight::from_ref_time(8_667_000 as u64) } // Storage: PolkadotXcm Queries (r:1 w:0) pub(crate) fn query_response() -> Weight { - Weight::from_ref_time(13_270_000 as u64) + Weight::from_ref_time(19_292_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) } pub(crate) fn transact() -> Weight { - Weight::from_ref_time(16_375_000 as u64) + Weight::from_ref_time(37_996_000 as u64) } pub(crate) fn refund_surplus() -> Weight { - Weight::from_ref_time(7_319_000 as u64) + Weight::from_ref_time(9_076_000 as u64) } pub(crate) fn set_error_handler() -> Weight { - Weight::from_ref_time(3_515_000 as u64) + Weight::from_ref_time(6_410_000 as u64) } pub(crate) fn set_appendix() -> Weight { - Weight::from_ref_time(3_501_000 as u64) + Weight::from_ref_time(6_412_000 as u64) } pub(crate) fn clear_error() -> Weight { - Weight::from_ref_time(3_459_000 as u64) + Weight::from_ref_time(6_311_000 as u64) } pub(crate) fn descend_origin() -> Weight { - Weight::from_ref_time(4_319_000 as u64) + Weight::from_ref_time(7_355_000 as u64) } pub(crate) fn clear_origin() -> Weight { - Weight::from_ref_time(3_511_000 as u64) + Weight::from_ref_time(6_389_000 as u64) } + // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_error() -> Weight { - Weight::from_ref_time(13_284_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) + Weight::from_ref_time(23_020_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: PolkadotXcm AssetTraps (r:1 w:1) pub(crate) fn claim_asset() -> Weight { - Weight::from_ref_time(7_985_000 as u64) + Weight::from_ref_time(13_613_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } pub(crate) fn trap() -> Weight { - Weight::from_ref_time(3_515_000 as u64) + Weight::from_ref_time(6_457_000 as u64) } // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -114,13 +116,13 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn subscribe_version() -> Weight { - Weight::from_ref_time(16_657_000 as u64) + Weight::from_ref_time(31_677_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) pub(crate) fn unsubscribe_version() -> Weight { - Weight::from_ref_time(5_622_000 as u64) + Weight::from_ref_time(9_613_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: ParachainInfo ParachainId (r:1 w:0) @@ -130,8 +132,57 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_reserve_withdraw() -> Weight { - Weight::from_ref_time(878_786_000 as u64) + Weight::from_ref_time(1_588_580_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + pub(crate) fn burn_asset() -> Weight { + Weight::from_ref_time(497_452_000 as u64) + } + pub(crate) fn expect_asset() -> Weight { + Weight::from_ref_time(38_502_000 as u64) + } + pub(crate) fn expect_origin() -> Weight { + Weight::from_ref_time(6_427_000 as u64) + } + pub(crate) fn expect_error() -> Weight { + Weight::from_ref_time(6_303_000 as u64) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn query_pallet() -> Weight { + Weight::from_ref_time(25_510_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } + pub(crate) fn expect_pallet() -> Weight { + Weight::from_ref_time(7_909_000 as u64) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn report_transact_status() -> Weight { + Weight::from_ref_time(22_949_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + pub(crate) fn clear_transact_status() -> Weight { + Weight::from_ref_time(6_491_000 as u64) + } + pub(crate) fn set_topic() -> Weight { + Weight::from_ref_time(6_527_000 as u64) + } + pub(crate) fn clear_topic() -> Weight { + Weight::from_ref_time(6_440_000 as u64) + } + pub(crate) fn set_fees_mode() -> Weight { + Weight::from_ref_time(6_426_000 as u64) + } } diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index ed04677be4c..4841ee7c518 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -14,8 +14,9 @@ // limitations under the License. use super::{ - AccountId, AssetId, Assets, Authorship, Balance, Balances, ParachainInfo, ParachainSystem, - PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, AssetId, Assets, Authorship, Balance, Balances, ParachainInfo, + ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, + XcmpQueue, }; use frame_support::{ match_types, parameter_types, @@ -202,7 +203,7 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; - type PalletInstancesInfo = (); + type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); diff --git a/parachains/runtimes/assets/statemint/src/lib.rs b/parachains/runtimes/assets/statemint/src/lib.rs index 151259f839a..df2425d3667 100644 --- a/parachains/runtimes/assets/statemint/src/lib.rs +++ b/parachains/runtimes/assets/statemint/src/lib.rs @@ -878,7 +878,7 @@ impl_runtime_apis! { fn valid_destination() -> Result { Ok(DotLocation::get()) } - fn worst_case_holding() -> MultiAssets { + fn worst_case_holding(_depositable_count: u32) -> MultiAssets { // A mix of fungible, non-fungible, and concrete assets. const HOLDING_FUNGIBLES: u32 = 100; const HOLDING_NON_FUNGIBLES: u32 = 100; @@ -911,7 +911,6 @@ impl_runtime_apis! { DotLocation::get(), MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(DotLocation::get()) }, )); - pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = None; pub const CheckedAccount: Option = None; } @@ -920,7 +919,6 @@ impl_runtime_apis! { type CheckedAccount = CheckedAccount; type TrustedTeleporter = TrustedTeleporter; - type TrustedReserve = TrustedReserve; fn get_multi_asset() -> MultiAsset { MultiAsset { @@ -937,6 +935,14 @@ impl_runtime_apis! { (0u64, Response::Version(Default::default())) } + fn worst_case_asset_exchange() -> Result<(MultiAssets, MultiAssets), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn universal_alias() -> Result { + Err(BenchmarkError::Skip) + } + fn transact_origin() -> Result { Ok(DotLocation::get()) } @@ -951,6 +957,10 @@ impl_runtime_apis! { let ticket = MultiLocation { parents: 0, interior: Here }; Ok((origin, ticket, assets)) } + + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { + Err(BenchmarkError::Skip) + } } type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; diff --git a/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs b/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs index 1a0ffcdb229..512be255779 100644 --- a/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs @@ -62,7 +62,12 @@ impl XcmWeightInfo for StatemintXcmWeight { fn receive_teleported_asset(assets: &MultiAssets) -> XCMWeight { assets.weigh_multi_assets(XcmFungibleWeight::::receive_teleported_asset()) } - fn query_response(_query_id: &u64, _response: &Response, _max_weight: &u64) -> XCMWeight { + fn query_response( + _query_id: &u64, + _response: &Response, + _max_weight: &u64, + _querier: &Option, + ) -> XCMWeight { XcmGeneric::::query_response().ref_time() } fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> XCMWeight { @@ -104,19 +109,11 @@ impl XcmWeightInfo for StatemintXcmWeight { fn descend_origin(_who: &InteriorMultiLocation) -> XCMWeight { XcmGeneric::::descend_origin().ref_time() } - fn report_error( - _query_id: &QueryId, - _dest: &MultiLocation, - _max_response_weight: &u64, - ) -> XCMWeight { + fn report_error(_query_response_info: &QueryResponseInfo) -> XCMWeight { XcmGeneric::::report_error().ref_time() } - fn deposit_asset( - assets: &MultiAssetFilter, - _max_assets: &u32, - _dest: &MultiLocation, - ) -> XCMWeight { + fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> XCMWeight { // Hardcoded till the XCM pallet is fixed let hardcoded_weight = Weight::from_ref_time(1_000_000_000 as u64).ref_time(); let weight = assets.weigh_multi_assets(XcmFungibleWeight::::deposit_asset()); @@ -124,13 +121,16 @@ impl XcmWeightInfo for StatemintXcmWeight { } fn deposit_reserve_asset( assets: &MultiAssetFilter, - _max_assets: &u32, _dest: &MultiLocation, _xcm: &Xcm<()>, ) -> XCMWeight { assets.weigh_multi_assets(XcmFungibleWeight::::deposit_reserve_asset()) } - fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets) -> XCMWeight { + fn exchange_asset( + _give: &MultiAssetFilter, + _receive: &MultiAssets, + _maximal: &bool, + ) -> XCMWeight { Weight::MAX.ref_time() } fn initiate_reserve_withdraw( @@ -150,13 +150,8 @@ impl XcmWeightInfo for StatemintXcmWeight { let weight = assets.weigh_multi_assets(XcmFungibleWeight::::initiate_teleport()); cmp::min(hardcoded_weight, weight) } - fn query_holding( - _query_id: &u64, - _dest: &MultiLocation, - _assets: &MultiAssetFilter, - _max_response_weight: &u64, - ) -> XCMWeight { - XcmGeneric::::query_holding().ref_time() + fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> XCMWeight { + XcmGeneric::::report_holding().ref_time() } fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> XCMWeight { XcmGeneric::::buy_execution().ref_time() @@ -185,4 +180,65 @@ impl XcmWeightInfo for StatemintXcmWeight { fn unsubscribe_version() -> XCMWeight { XcmGeneric::::unsubscribe_version().ref_time() } + fn burn_asset(assets: &MultiAssets) -> XCMWeight { + assets.weigh_multi_assets(XcmGeneric::::burn_asset()) + } + fn expect_asset(assets: &MultiAssets) -> XCMWeight { + assets.weigh_multi_assets(XcmGeneric::::expect_asset()) + } + fn expect_origin(_origin: &Option) -> XCMWeight { + XcmGeneric::::expect_origin().ref_time() + } + fn expect_error(_error: &Option<(u32, XcmError)>) -> XCMWeight { + XcmGeneric::::expect_error().ref_time() + } + fn query_pallet(_module_name: &Vec, _response_info: &QueryResponseInfo) -> XCMWeight { + XcmGeneric::::query_pallet().ref_time() + } + fn expect_pallet( + _index: &u32, + _name: &Vec, + _module_name: &Vec, + _crate_major: &u32, + _min_crate_minor: &u32, + ) -> XCMWeight { + XcmGeneric::::expect_pallet().ref_time() + } + fn report_transact_status(_response_info: &QueryResponseInfo) -> XCMWeight { + XcmGeneric::::report_transact_status().ref_time() + } + fn clear_transact_status() -> XCMWeight { + XcmGeneric::::clear_transact_status().ref_time() + } + fn universal_origin(_: &Junction) -> XCMWeight { + Weight::MAX.ref_time() + } + fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> XCMWeight { + Weight::MAX.ref_time() + } + fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn set_fees_mode(_: &bool) -> XCMWeight { + XcmGeneric::::set_fees_mode().ref_time() + } + fn set_topic(_topic: &[u8; 32]) -> XCMWeight { + XcmGeneric::::set_topic().ref_time() + } + fn clear_topic() -> XCMWeight { + XcmGeneric::::clear_topic().ref_time() + } + fn alias_origin(_: &MultiLocation) -> XCMWeight { + // XCM Executor does not currently support alias origin operations + Weight::MAX.ref_time() + } } diff --git a/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 7af8b8b280b..6ed0d1adff2 100644 --- a/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-08-25, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-09-30, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemint-dev"), DB CACHE: 1024 @@ -37,7 +37,7 @@ // --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --header=./file_header.txt // --template=./templates/xcm-bench-template.hbs -// --output=./parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +// --output=./parachains/runtimes/assets/statemint/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -55,58 +55,59 @@ impl WeightInfo { // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - pub(crate) fn query_holding() -> Weight { - Weight::from_ref_time(682_639_000 as u64) + pub(crate) fn report_holding() -> Weight { + Weight::from_ref_time(1_305_689_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn buy_execution() -> Weight { - Weight::from_ref_time(9_272_000 as u64) + Weight::from_ref_time(8_843_000 as u64) } // Storage: PolkadotXcm Queries (r:1 w:0) pub(crate) fn query_response() -> Weight { - Weight::from_ref_time(17_084_000 as u64) + Weight::from_ref_time(19_216_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) } pub(crate) fn transact() -> Weight { - Weight::from_ref_time(20_265_000 as u64) + Weight::from_ref_time(22_708_000 as u64) } pub(crate) fn refund_surplus() -> Weight { - Weight::from_ref_time(9_422_000 as u64) + Weight::from_ref_time(9_040_000 as u64) } pub(crate) fn set_error_handler() -> Weight { - Weight::from_ref_time(5_545_000 as u64) + Weight::from_ref_time(6_222_000 as u64) } pub(crate) fn set_appendix() -> Weight { - Weight::from_ref_time(5_450_000 as u64) + Weight::from_ref_time(6_411_000 as u64) } pub(crate) fn clear_error() -> Weight { - Weight::from_ref_time(5_519_000 as u64) + Weight::from_ref_time(6_222_000 as u64) } pub(crate) fn descend_origin() -> Weight { - Weight::from_ref_time(6_398_000 as u64) + Weight::from_ref_time(7_112_000 as u64) } pub(crate) fn clear_origin() -> Weight { - Weight::from_ref_time(5_498_000 as u64) + Weight::from_ref_time(6_340_000 as u64) } + // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_error() -> Weight { - Weight::from_ref_time(15_784_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) + Weight::from_ref_time(22_943_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: PolkadotXcm AssetTraps (r:1 w:1) pub(crate) fn claim_asset() -> Weight { - Weight::from_ref_time(11_861_000 as u64) + Weight::from_ref_time(13_178_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } pub(crate) fn trap() -> Weight { - Weight::from_ref_time(5_462_000 as u64) + Weight::from_ref_time(6_333_000 as u64) } // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -115,13 +116,13 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn subscribe_version() -> Weight { - Weight::from_ref_time(18_997_000 as u64) + Weight::from_ref_time(31_798_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) pub(crate) fn unsubscribe_version() -> Weight { - Weight::from_ref_time(8_684_000 as u64) + Weight::from_ref_time(9_728_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: ParachainInfo ParachainId (r:1 w:0) @@ -131,8 +132,57 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_reserve_withdraw() -> Weight { - Weight::from_ref_time(883_121_000 as u64) + Weight::from_ref_time(1_583_652_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + pub(crate) fn burn_asset() -> Weight { + Weight::from_ref_time(497_448_000 as u64) + } + pub(crate) fn expect_asset() -> Weight { + Weight::from_ref_time(38_383_000 as u64) + } + pub(crate) fn expect_origin() -> Weight { + Weight::from_ref_time(6_308_000 as u64) + } + pub(crate) fn expect_error() -> Weight { + Weight::from_ref_time(6_327_000 as u64) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn query_pallet() -> Weight { + Weight::from_ref_time(26_011_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } + pub(crate) fn expect_pallet() -> Weight { + Weight::from_ref_time(8_008_000 as u64) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn report_transact_status() -> Weight { + Weight::from_ref_time(22_963_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + pub(crate) fn clear_transact_status() -> Weight { + Weight::from_ref_time(6_378_000 as u64) + } + pub(crate) fn set_topic() -> Weight { + Weight::from_ref_time(6_313_000 as u64) + } + pub(crate) fn clear_topic() -> Weight { + Weight::from_ref_time(6_324_000 as u64) + } + pub(crate) fn set_fees_mode() -> Weight { + Weight::from_ref_time(6_336_000 as u64) + } } diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index 16720f6fefc..bb9d82680fe 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -14,8 +14,9 @@ // limitations under the License. use super::{ - AccountId, AssetId, Assets, Authorship, Balance, Balances, ParachainInfo, ParachainSystem, - PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, AssetId, Assets, Authorship, Balance, Balances, ParachainInfo, + ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, + XcmpQueue, }; use frame_support::{ match_types, parameter_types, @@ -31,11 +32,10 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, - IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, - WeightInfoBounds, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, + NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, }; use xcm_executor::{traits::JustTry, XcmExecutor}; @@ -179,7 +179,7 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; - type PalletInstancesInfo = (); + type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); diff --git a/parachains/runtimes/assets/westmint/src/lib.rs b/parachains/runtimes/assets/westmint/src/lib.rs index 1f1d86e1e7e..a227572609a 100644 --- a/parachains/runtimes/assets/westmint/src/lib.rs +++ b/parachains/runtimes/assets/westmint/src/lib.rs @@ -838,7 +838,7 @@ impl_runtime_apis! { fn valid_destination() -> Result { Ok(WestendLocation::get()) } - fn worst_case_holding() -> MultiAssets { + fn worst_case_holding(_depositable_count: u32) -> MultiAssets { // A mix of fungible, non-fungible, and concrete assets. const HOLDING_FUNGIBLES: u32 = 100; const HOLDING_NON_FUNGIBLES: u32 = 100; @@ -871,7 +871,6 @@ impl_runtime_apis! { WestendLocation::get(), MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(WestendLocation::get()) }, )); - pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = None; pub const CheckedAccount: Option = None; } @@ -881,7 +880,6 @@ impl_runtime_apis! { type CheckedAccount = CheckedAccount; type TrustedTeleporter = TrustedTeleporter; - type TrustedReserve = TrustedReserve; fn get_multi_asset() -> MultiAsset { MultiAsset { @@ -898,6 +896,14 @@ impl_runtime_apis! { (0u64, Response::Version(Default::default())) } + fn worst_case_asset_exchange() -> Result<(MultiAssets, MultiAssets), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn universal_alias() -> Result { + Err(BenchmarkError::Skip) + } + fn transact_origin() -> Result { Ok(WestendLocation::get()) } @@ -912,6 +918,10 @@ impl_runtime_apis! { let ticket = MultiLocation { parents: 0, interior: Here }; Ok((origin, ticket, assets)) } + + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { + Err(BenchmarkError::Skip) + } } type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; diff --git a/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs b/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs index 5f6bf034fdc..56852eee6ab 100644 --- a/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs @@ -62,7 +62,12 @@ impl XcmWeightInfo for WestmintXcmWeight { fn receive_teleported_asset(assets: &MultiAssets) -> XCMWeight { assets.weigh_multi_assets(XcmFungibleWeight::::receive_teleported_asset()) } - fn query_response(_query_id: &u64, _response: &Response, _max_weight: &u64) -> XCMWeight { + fn query_response( + _query_id: &u64, + _response: &Response, + _max_weight: &u64, + _querier: &Option, + ) -> XCMWeight { XcmGeneric::::query_response().ref_time() } fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> XCMWeight { @@ -104,19 +109,11 @@ impl XcmWeightInfo for WestmintXcmWeight { fn descend_origin(_who: &InteriorMultiLocation) -> XCMWeight { XcmGeneric::::descend_origin().ref_time() } - fn report_error( - _query_id: &QueryId, - _dest: &MultiLocation, - _max_response_weight: &u64, - ) -> XCMWeight { + fn report_error(_query_response_info: &QueryResponseInfo) -> XCMWeight { XcmGeneric::::report_error().ref_time() } - fn deposit_asset( - assets: &MultiAssetFilter, - _max_assets: &u32, - _dest: &MultiLocation, - ) -> XCMWeight { + fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> XCMWeight { // Hardcoded till the XCM pallet is fixed let hardcoded_weight = Weight::from_ref_time(1_000_000_000 as u64).ref_time(); let weight = assets.weigh_multi_assets(XcmFungibleWeight::::deposit_asset()); @@ -124,13 +121,16 @@ impl XcmWeightInfo for WestmintXcmWeight { } fn deposit_reserve_asset( assets: &MultiAssetFilter, - _max_assets: &u32, _dest: &MultiLocation, _xcm: &Xcm<()>, ) -> XCMWeight { assets.weigh_multi_assets(XcmFungibleWeight::::deposit_reserve_asset()) } - fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets) -> XCMWeight { + fn exchange_asset( + _give: &MultiAssetFilter, + _receive: &MultiAssets, + _maximal: &bool, + ) -> XCMWeight { Weight::MAX.ref_time() } fn initiate_reserve_withdraw( @@ -150,13 +150,8 @@ impl XcmWeightInfo for WestmintXcmWeight { let weight = assets.weigh_multi_assets(XcmFungibleWeight::::initiate_teleport()); cmp::min(hardcoded_weight, weight) } - fn query_holding( - _query_id: &u64, - _dest: &MultiLocation, - _assets: &MultiAssetFilter, - _max_response_weight: &u64, - ) -> XCMWeight { - XcmGeneric::::query_holding().ref_time() + fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> XCMWeight { + XcmGeneric::::report_holding().ref_time() } fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> XCMWeight { XcmGeneric::::buy_execution().ref_time() @@ -185,4 +180,65 @@ impl XcmWeightInfo for WestmintXcmWeight { fn unsubscribe_version() -> XCMWeight { XcmGeneric::::unsubscribe_version().ref_time() } + fn burn_asset(assets: &MultiAssets) -> XCMWeight { + assets.weigh_multi_assets(XcmGeneric::::burn_asset()) + } + fn expect_asset(assets: &MultiAssets) -> XCMWeight { + assets.weigh_multi_assets(XcmGeneric::::expect_asset()) + } + fn expect_origin(_origin: &Option) -> XCMWeight { + XcmGeneric::::expect_origin().ref_time() + } + fn expect_error(_error: &Option<(u32, XcmError)>) -> XCMWeight { + XcmGeneric::::expect_error().ref_time() + } + fn query_pallet(_module_name: &Vec, _response_info: &QueryResponseInfo) -> XCMWeight { + XcmGeneric::::query_pallet().ref_time() + } + fn expect_pallet( + _index: &u32, + _name: &Vec, + _module_name: &Vec, + _crate_major: &u32, + _min_crate_minor: &u32, + ) -> XCMWeight { + XcmGeneric::::expect_pallet().ref_time() + } + fn report_transact_status(_response_info: &QueryResponseInfo) -> XCMWeight { + XcmGeneric::::report_transact_status().ref_time() + } + fn clear_transact_status() -> XCMWeight { + XcmGeneric::::clear_transact_status().ref_time() + } + fn universal_origin(_: &Junction) -> XCMWeight { + Weight::MAX.ref_time() + } + fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> XCMWeight { + Weight::MAX.ref_time() + } + fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn set_fees_mode(_: &bool) -> XCMWeight { + XcmGeneric::::set_fees_mode().ref_time() + } + fn set_topic(_topic: &[u8; 32]) -> XCMWeight { + XcmGeneric::::set_topic().ref_time() + } + fn clear_topic() -> XCMWeight { + XcmGeneric::::clear_topic().ref_time() + } + fn alias_origin(_: &MultiLocation) -> XCMWeight { + // XCM Executor does not currently support alias origin operations + Weight::MAX.ref_time() + } } diff --git a/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 80aff062d20..042e0b3a974 100644 --- a/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -18,7 +18,8 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-08-17, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-09-30, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westmint-dev"), DB CACHE: 1024 // Executed Command: @@ -36,7 +37,7 @@ // --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --header=./file_header.txt // --template=./templates/xcm-bench-template.hbs -// --output=./parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +// --output=./parachains/runtimes/assets/westmint/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -54,58 +55,59 @@ impl WeightInfo { // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - pub(crate) fn query_holding() -> Weight { - Weight::from_ref_time(676_316_000 as u64) + pub(crate) fn report_holding() -> Weight { + Weight::from_ref_time(1_324_853_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn buy_execution() -> Weight { - Weight::from_ref_time(7_030_000 as u64) + Weight::from_ref_time(8_533_000 as u64) } // Storage: PolkadotXcm Queries (r:1 w:0) pub(crate) fn query_response() -> Weight { - Weight::from_ref_time(12_574_000 as u64) + Weight::from_ref_time(19_435_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) } pub(crate) fn transact() -> Weight { - Weight::from_ref_time(15_764_000 as u64) + Weight::from_ref_time(22_656_000 as u64) } pub(crate) fn refund_surplus() -> Weight { - Weight::from_ref_time(7_200_000 as u64) + Weight::from_ref_time(8_900_000 as u64) } pub(crate) fn set_error_handler() -> Weight { - Weight::from_ref_time(3_310_000 as u64) + Weight::from_ref_time(6_255_000 as u64) } pub(crate) fn set_appendix() -> Weight { - Weight::from_ref_time(3_260_000 as u64) + Weight::from_ref_time(6_268_000 as u64) } pub(crate) fn clear_error() -> Weight { - Weight::from_ref_time(3_277_000 as u64) + Weight::from_ref_time(6_304_000 as u64) } pub(crate) fn descend_origin() -> Weight { - Weight::from_ref_time(3_913_000 as u64) + Weight::from_ref_time(7_279_000 as u64) } pub(crate) fn clear_origin() -> Weight { - Weight::from_ref_time(3_354_000 as u64) + Weight::from_ref_time(6_297_000 as u64) } + // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_error() -> Weight { - Weight::from_ref_time(13_028_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) + Weight::from_ref_time(23_025_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: PolkadotXcm AssetTraps (r:1 w:1) pub(crate) fn claim_asset() -> Weight { - Weight::from_ref_time(7_739_000 as u64) + Weight::from_ref_time(13_001_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } pub(crate) fn trap() -> Weight { - Weight::from_ref_time(3_351_000 as u64) + Weight::from_ref_time(6_266_000 as u64) } // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -114,13 +116,13 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn subscribe_version() -> Weight { - Weight::from_ref_time(16_051_000 as u64) + Weight::from_ref_time(31_348_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) pub(crate) fn unsubscribe_version() -> Weight { - Weight::from_ref_time(5_477_000 as u64) + Weight::from_ref_time(9_534_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: ParachainInfo ParachainId (r:1 w:0) @@ -130,8 +132,57 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_reserve_withdraw() -> Weight { - Weight::from_ref_time(874_435_000 as u64) + Weight::from_ref_time(1_558_814_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + pub(crate) fn burn_asset() -> Weight { + Weight::from_ref_time(496_802_000 as u64) + } + pub(crate) fn expect_asset() -> Weight { + Weight::from_ref_time(38_299_000 as u64) + } + pub(crate) fn expect_origin() -> Weight { + Weight::from_ref_time(6_354_000 as u64) + } + pub(crate) fn expect_error() -> Weight { + Weight::from_ref_time(6_234_000 as u64) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn query_pallet() -> Weight { + Weight::from_ref_time(25_150_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } + pub(crate) fn expect_pallet() -> Weight { + Weight::from_ref_time(7_969_000 as u64) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn report_transact_status() -> Weight { + Weight::from_ref_time(23_099_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + pub(crate) fn clear_transact_status() -> Weight { + Weight::from_ref_time(6_366_000 as u64) + } + pub(crate) fn set_topic() -> Weight { + Weight::from_ref_time(6_422_000 as u64) + } + pub(crate) fn clear_topic() -> Weight { + Weight::from_ref_time(6_405_000 as u64) + } + pub(crate) fn set_fees_mode() -> Weight { + Weight::from_ref_time(6_392_000 as u64) + } } diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index a9d80012619..4f55696c69e 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -14,8 +14,9 @@ // limitations under the License. use super::{ - AccountId, AssetId, Assets, Authorship, Balance, Balances, ParachainInfo, ParachainSystem, - PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, AssetId, Assets, Authorship, Balance, Balances, ParachainInfo, + ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, + XcmpQueue, }; use frame_support::{ match_types, parameter_types, @@ -198,7 +199,7 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; - type PalletInstancesInfo = (); + type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index 1e7b17de09a..2cb82656c37 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -14,8 +14,8 @@ // limitations under the License. use super::{ - AccountId, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, }; use frame_support::{ match_types, parameter_types, @@ -31,7 +31,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, + FixedWeightBounds, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, @@ -151,7 +151,7 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; - type PalletInstancesInfo = (); + type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); diff --git a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index fc0b834c4a3..4f53eca6dbc 100644 --- a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -14,8 +14,8 @@ // limitations under the License. use super::{ - AccountId, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, }; use frame_support::{ match_types, parameter_types, @@ -149,7 +149,7 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; - type PalletInstancesInfo = super::AllPalletsWithSystem; + type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = ConstU32<8>; type AssetLocker = (); type AssetExchanger = (); diff --git a/parachains/runtimes/starters/shell/src/xcm_config.rs b/parachains/runtimes/starters/shell/src/xcm_config.rs index 6706c6af6bb..acc02df42af 100644 --- a/parachains/runtimes/starters/shell/src/xcm_config.rs +++ b/parachains/runtimes/starters/shell/src/xcm_config.rs @@ -13,7 +13,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{AccountId, ParachainInfo, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin}; +use super::{ + AccountId, AllPalletsWithSystem, ParachainInfo, Runtime, RuntimeCall, RuntimeEvent, + RuntimeOrigin, +}; use frame_support::{match_types, parameter_types, traits::Nothing}; use xcm::latest::prelude::*; use xcm_builder::{ @@ -67,7 +70,7 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = (); // don't trap for now type AssetClaims = (); // don't claim for now type SubscriptionService = (); // don't handle subscriptions for now - type PalletInstancesInfo = (); + type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index 62ae7fc3a95..e8f70c20e63 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -22,9 +22,9 @@ //! with statemine as the reserve. At present no derivative tokens are minted on receipt of a //! ReserveAssetTransferDeposited message but that will but the intension will be to support this soon. use super::{ - AccountId, AssetId as AssetIdPalletAssets, Assets, Balance, Balances, ParachainInfo, - ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, - XcmpQueue, + AccountId, AllPalletsWithSystem, AssetId as AssetIdPalletAssets, Assets, Balance, Balances, + ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + WeightToFee, XcmpQueue, }; use core::marker::PhantomData; use frame_support::{ @@ -337,7 +337,7 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; - type PalletInstancesInfo = (); + type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); diff --git a/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/parachains/runtimes/testing/rococo-parachain/src/lib.rs index ec0fd38a3ed..80d97def583 100644 --- a/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -417,7 +417,7 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; - type PalletInstancesInfo = (); + type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); From 7290787bf307b9162d0698980cccd0f7191f6aae Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 25 Oct 2022 13:50:19 +0200 Subject: [PATCH 055/263] Avoid consuming XCM message for NotApplicable scenario (#1787) * Avoid consuming message for NotApplicable scenario * Avoid consuming message for NotApplicable scenario tests --- pallets/xcmp-queue/src/lib.rs | 5 +- pallets/xcmp-queue/src/tests.rs | 85 +++++++++++++++++++++++++ primitives/utility/src/lib.rs | 106 +++++++++++++++++++++++++++++++- 3 files changed, 192 insertions(+), 4 deletions(-) diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index 72f1c12011b..2fdedd3e306 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -1138,19 +1138,20 @@ impl SendXcm for Pallet { msg: &mut Option>, ) -> SendResult<(ParaId, VersionedXcm<()>)> { let d = dest.take().ok_or(SendError::MissingArgument)?; - let xcm = msg.take().ok_or(SendError::MissingArgument)?; match &d { // An HRMP message for a sibling parachain. MultiLocation { parents: 1, interior: X1(Parachain(id)) } => { + let xcm = msg.take().ok_or(SendError::MissingArgument)?; let id = ParaId::from(*id); let price = T::PriceForSiblingDelivery::price_for_sibling_delivery(id, &xcm); let versioned_xcm = T::VersionWrapper::wrap_version(&d, xcm) .map_err(|()| SendError::DestinationUnsupported)?; Ok(((id, versioned_xcm), price)) }, - // Anything else is unhandled. This includes a message this is meant for us. _ => { + // Anything else is unhandled. This includes a message that is not meant for us. + // We need to make sure that dest/msg is not consumed here. *dest = Some(d); Err(SendError::NotApplicable) }, diff --git a/pallets/xcmp-queue/src/tests.rs b/pallets/xcmp-queue/src/tests.rs index 1b6303ddaf1..595bd8ca33b 100644 --- a/pallets/xcmp-queue/src/tests.rs +++ b/pallets/xcmp-queue/src/tests.rs @@ -247,3 +247,88 @@ fn update_xcmp_max_individual_weight() { assert_eq!(data.xcmp_max_individual_weight, 30u64 * WEIGHT_PER_MILLIS); }); } + +/// Validates [`validate`] for required Some(destination) and Some(message) +struct OkFixedXcmHashWithAssertingRequiredInputsSender; +impl OkFixedXcmHashWithAssertingRequiredInputsSender { + const FIXED_XCM_HASH: [u8; 32] = [9; 32]; + + fn fixed_delivery_asset() -> MultiAssets { + MultiAssets::new() + } + + fn expected_delivery_result() -> Result<(XcmHash, MultiAssets), SendError> { + Ok((Self::FIXED_XCM_HASH, Self::fixed_delivery_asset())) + } +} +impl SendXcm for OkFixedXcmHashWithAssertingRequiredInputsSender { + type Ticket = (); + + fn validate( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { + assert!(destination.is_some()); + assert!(message.is_some()); + Ok(((), OkFixedXcmHashWithAssertingRequiredInputsSender::fixed_delivery_asset())) + } + + fn deliver(_: Self::Ticket) -> Result { + Ok(Self::FIXED_XCM_HASH) + } +} + +#[test] +fn xcmp_queue_does_not_consume_dest_or_msg_on_not_applicable() { + // dummy message + let message = Xcm(vec![Trap(5)]); + + // XcmpQueue - check dest is really not applicable + let dest = (Parent, Parent, Parent); + let mut dest_wrapper = Some(dest.clone().into()); + let mut msg_wrapper = Some(message.clone()); + assert_eq!( + Err(SendError::NotApplicable), + ::validate(&mut dest_wrapper, &mut msg_wrapper) + ); + + // check wrapper were not consumed + assert_eq!(Some(dest.clone().into()), dest_wrapper.take()); + assert_eq!(Some(message.clone()), msg_wrapper.take()); + + // another try with router chain with asserting sender + assert_eq!( + OkFixedXcmHashWithAssertingRequiredInputsSender::expected_delivery_result(), + send_xcm::<(XcmpQueue, OkFixedXcmHashWithAssertingRequiredInputsSender)>( + dest.into(), + message + ) + ); +} + +#[test] +fn xcmp_queue_consumes_dest_and_msg_on_ok_validate() { + // dummy message + let message = Xcm(vec![Trap(5)]); + + // XcmpQueue - check dest/msg is valid + let dest = (Parent, X1(Parachain(5555))); + let mut dest_wrapper = Some(dest.clone().into()); + let mut msg_wrapper = Some(message.clone()); + assert!(::validate(&mut dest_wrapper, &mut msg_wrapper).is_ok()); + + // check wrapper were consumed + assert_eq!(None, dest_wrapper.take()); + assert_eq!(None, msg_wrapper.take()); + + new_test_ext().execute_with(|| { + // another try with router chain with asserting sender + assert_eq!( + Err(SendError::Transport("NoChannel")), + send_xcm::<(XcmpQueue, OkFixedXcmHashWithAssertingRequiredInputsSender)>( + dest.into(), + message + ) + ); + }); +} diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index 1e93aa7d792..5cededd7b46 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -75,10 +75,10 @@ where msg: &mut Option>, ) -> SendResult> { let d = dest.take().ok_or(SendError::MissingArgument)?; - let xcm = msg.take().ok_or(SendError::MissingArgument)?; if d.contains_parents_only(1) { // An upward message for the relay chain. + let xcm = msg.take().ok_or(SendError::MissingArgument)?; let price = P::price_for_parent_delivery(&xcm); let versioned_xcm = W::wrap_version(&d, xcm).map_err(|()| SendError::DestinationUnsupported)?; @@ -86,8 +86,9 @@ where Ok((data, price)) } else { + // Anything else is unhandled. This includes a message that is not meant for us. + // We need to make sure that dest/msg is not consumed here. *dest = Some(d); - // Anything else is unhandled. This includes a message this is meant for us. Err(SendError::NotApplicable) } } @@ -317,3 +318,104 @@ pub trait ChargeWeightInFungibles Result<>::Balance, XcmError>; } + +#[cfg(test)] +mod tests { + use super::*; + use cumulus_primitives_core::UpwardMessage; + + /// Validates [`validate`] for required Some(destination) and Some(message) + struct OkFixedXcmHashWithAssertingRequiredInputsSender; + impl OkFixedXcmHashWithAssertingRequiredInputsSender { + const FIXED_XCM_HASH: [u8; 32] = [9; 32]; + + fn fixed_delivery_asset() -> MultiAssets { + MultiAssets::new() + } + + fn expected_delivery_result() -> Result<(XcmHash, MultiAssets), SendError> { + Ok((Self::FIXED_XCM_HASH, Self::fixed_delivery_asset())) + } + } + impl SendXcm for OkFixedXcmHashWithAssertingRequiredInputsSender { + type Ticket = (); + + fn validate( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { + assert!(destination.is_some()); + assert!(message.is_some()); + Ok(((), OkFixedXcmHashWithAssertingRequiredInputsSender::fixed_delivery_asset())) + } + + fn deliver(_: Self::Ticket) -> Result { + Ok(Self::FIXED_XCM_HASH) + } + } + + /// Impl [`UpwardMessageSender`] that return `Other` error + struct OtherErrorUpwardMessageSender; + impl UpwardMessageSender for OtherErrorUpwardMessageSender { + fn send_upward_message(_: UpwardMessage) -> Result { + Err(MessageSendError::Other) + } + } + + #[test] + fn parent_as_ump_does_not_consume_dest_or_msg_on_not_applicable() { + // dummy message + let message = Xcm(vec![Trap(5)]); + + // ParentAsUmp - check dest is really not applicable + let dest = (Parent, Parent, Parent); + let mut dest_wrapper = Some(dest.clone().into()); + let mut msg_wrapper = Some(message.clone()); + assert_eq!( + Err(SendError::NotApplicable), + as SendXcm>::validate(&mut dest_wrapper, &mut msg_wrapper) + ); + + // check wrapper were not consumed + assert_eq!(Some(dest.clone().into()), dest_wrapper.take()); + assert_eq!(Some(message.clone()), msg_wrapper.take()); + + // another try with router chain with asserting sender + assert_eq!( + OkFixedXcmHashWithAssertingRequiredInputsSender::expected_delivery_result(), + send_xcm::<(ParentAsUmp<(), (), ()>, OkFixedXcmHashWithAssertingRequiredInputsSender)>( + dest.into(), + message + ) + ); + } + + #[test] + fn parent_as_ump_consumes_dest_and_msg_on_ok_validate() { + // dummy message + let message = Xcm(vec![Trap(5)]); + + // ParentAsUmp - check dest/msg is valid + let dest = (Parent, Here); + let mut dest_wrapper = Some(dest.clone().into()); + let mut msg_wrapper = Some(message.clone()); + assert!( as SendXcm>::validate( + &mut dest_wrapper, + &mut msg_wrapper + ) + .is_ok()); + + // check wrapper were consumed + assert_eq!(None, dest_wrapper.take()); + assert_eq!(None, msg_wrapper.take()); + + // another try with router chain with asserting sender + assert_eq!( + Err(SendError::Transport("Other")), + send_xcm::<( + ParentAsUmp, + OkFixedXcmHashWithAssertingRequiredInputsSender + )>(dest.into(), message) + ); + } +} From 23f0bcdc7dc1848e37c0180770755adac6af8da2 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 8 Nov 2022 13:48:15 +0100 Subject: [PATCH 056/263] Fix for FungiblesAdapter - trait changes: Contains -> AssetChecking --- Cargo.lock | 1 + parachains/common/Cargo.toml | 1 + parachains/common/src/impls.rs | 14 ++++++++----- .../runtimes/testing/penpal/src/xcm_config.rs | 21 +++++++++++-------- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b263e588f9d..35b9b3e4225 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6525,6 +6525,7 @@ dependencies = [ "sp-std", "substrate-wasm-builder", "xcm", + "xcm-builder", "xcm-executor", ] diff --git a/parachains/common/Cargo.toml b/parachains/common/Cargo.toml index a01b925b1ee..540c4c21ca7 100644 --- a/parachains/common/Cargo.toml +++ b/parachains/common/Cargo.toml @@ -28,6 +28,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", d # Polkadot polkadot-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } # Cumulus diff --git a/parachains/common/src/impls.rs b/parachains/common/src/impls.rs index c1158263da2..284a431835a 100644 --- a/parachains/common/src/impls.rs +++ b/parachains/common/src/impls.rs @@ -18,12 +18,13 @@ use frame_support::traits::{ fungibles::{self, Balanced, CreditOf}, - Contains, ContainsPair, Currency, Get, Imbalance, OnUnbalanced, + ContainsPair, Currency, Get, Imbalance, OnUnbalanced, }; use pallet_asset_tx_payment::HandleCredit; use sp_runtime::traits::Zero; use sp_std::marker::PhantomData; use xcm::latest::{AssetId, Fungibility::Fungible, MultiAsset, MultiLocation}; +use xcm_builder::{AssetChecking, MintLocation}; /// Type alias to conveniently refer to the `Currency::NegativeImbalance` associated type. pub type NegativeImbalance = as Currency< @@ -87,13 +88,16 @@ where /// Allow checking in assets that have issuance > 0. pub struct NonZeroIssuance(PhantomData<(AccountId, Assets)>); -impl Contains<>::AssetId> - for NonZeroIssuance +impl AssetChecking for NonZeroIssuance where Assets: fungibles::Inspect, { - fn contains(id: &>::AssetId) -> bool { - !Assets::total_issuance(*id).is_zero() + fn asset_checking(id: &Assets::AssetId) -> Option { + if Assets::total_issuance(*id).is_zero() { + None + } else { + Some(MintLocation::Local) + } } } diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index e8f70c20e63..99cbaf0478f 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -31,7 +31,7 @@ use frame_support::{ log, match_types, parameter_types, traits::{ fungibles::{self, Balanced, CreditOf}, - ConstU32, Contains, ContainsPair, Everything, Get, Nothing, + ConstU32, ContainsPair, Everything, Get, Nothing, }, }; use pallet_asset_tx_payment::HandleCredit; @@ -43,10 +43,10 @@ use xcm::latest::{prelude::*, Weight as XCMWeight}; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, - IsConcrete, NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + AssetChecking, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, + FungiblesAdapter, IsConcrete, MintLocation, NativeAsset, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{ traits::{JustTry, ShouldExecute}, @@ -244,13 +244,16 @@ impl> ContainsPair for AssetsFr /// Allow checking in assets that have issuance > 0. pub struct NonZeroIssuance(PhantomData<(AccountId, Assets)>); -impl Contains<>::AssetId> - for NonZeroIssuance +impl AssetChecking for NonZeroIssuance where Assets: fungibles::Inspect, { - fn contains(id: &>::AssetId) -> bool { - !Assets::total_issuance(*id).is_zero() + fn asset_checking(id: &Assets::AssetId) -> Option { + if Assets::total_issuance(*id).is_zero() { + None + } else { + Some(MintLocation::Local) + } } } From a6dd610d7472ba2145c08fdaa9d325ee424a5fbb Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 8 Nov 2022 14:57:49 +0100 Subject: [PATCH 057/263] Fix for missing weight for `fn unpaid_execution()` --- parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs | 3 +++ .../statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs | 1 + parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs | 3 +++ .../statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs | 1 + parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs | 3 +++ .../westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs | 1 + 6 files changed, 12 insertions(+) diff --git a/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs b/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs index 0ca8a414e44..93e12e826fd 100644 --- a/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs @@ -241,4 +241,7 @@ impl XcmWeightInfo for StatemineXcmWeight { // XCM Executor does not currently support alias origin operations Weight::MAX.ref_time() } + fn unpaid_execution(_: &WeightLimit, _: &Option) -> XCMWeight { + XcmGeneric::::unpaid_execution().ref_time() + } } diff --git a/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 20fdd109ef7..76c40db7914 100644 --- a/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -185,4 +185,5 @@ impl WeightInfo { pub(crate) fn set_fees_mode() -> Weight { Weight::from_ref_time(6_426_000 as u64) } + pub(crate) fn unpaid_execution() -> Weight { Weight::from_ref_time(3_111_000 as u64) } } diff --git a/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs b/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs index 512be255779..bd6c9bbc097 100644 --- a/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs @@ -241,4 +241,7 @@ impl XcmWeightInfo for StatemintXcmWeight { // XCM Executor does not currently support alias origin operations Weight::MAX.ref_time() } + fn unpaid_execution(_: &WeightLimit, _: &Option) -> XCMWeight { + XcmGeneric::::unpaid_execution().ref_time() + } } diff --git a/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 5e35b4084d0..79066a2b9e5 100644 --- a/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -185,4 +185,5 @@ impl WeightInfo { pub(crate) fn set_fees_mode() -> Weight { Weight::from_ref_time(6_336_000 as u64) } + pub(crate) fn unpaid_execution() -> Weight { Weight::from_ref_time(3_111_000 as u64) } } diff --git a/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs b/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs index 56852eee6ab..8429b74f2ec 100644 --- a/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs @@ -241,4 +241,7 @@ impl XcmWeightInfo for WestmintXcmWeight { // XCM Executor does not currently support alias origin operations Weight::MAX.ref_time() } + fn unpaid_execution(_: &WeightLimit, _: &Option) -> XCMWeight { + XcmGeneric::::unpaid_execution().ref_time() + } } diff --git a/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 18761aa0bf2..e6f01884049 100644 --- a/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -185,4 +185,5 @@ impl WeightInfo { pub(crate) fn set_fees_mode() -> Weight { Weight::from_ref_time(6_392_000 as u64) } + pub(crate) fn unpaid_execution() -> Weight { Weight::from_ref_time(3_111_000 as u64) } } From d2827e417453aac16aa744862b4843a127c9260d Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 8 Nov 2022 15:18:49 +0100 Subject: [PATCH 058/263] Patched dependencies (substrate + polkadot:gav-xcm-v3) --- Cargo.lock | 565 +++++++++++++++++++++++++++++------------------------ Cargo.toml | 237 ++++++++++++++++++++++ 2 files changed, 552 insertions(+), 250 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 35b9b3e4225..c908727eac7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -469,7 +469,7 @@ dependencies = [ [[package]] name = "beefy-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "array-bytes", "async-trait", @@ -506,7 +506,7 @@ dependencies = [ [[package]] name = "beefy-gadget-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "beefy-gadget", "beefy-primitives", @@ -526,7 +526,7 @@ dependencies = [ [[package]] name = "beefy-merkle-tree" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "beefy-primitives", "sp-api", @@ -536,7 +536,7 @@ dependencies = [ [[package]] name = "beefy-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", "scale-info", @@ -2847,7 +2847,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", ] @@ -2870,7 +2870,7 @@ checksum = "85dcb89d2b10c5f6133de2efd8c11959ce9dbb46a2f7a4cab208c4eeda6ce1ab" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "frame-system", @@ -2893,7 +2893,7 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "Inflector", "array-bytes", @@ -2944,7 +2944,7 @@ dependencies = [ [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2955,7 +2955,7 @@ dependencies = [ [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-election-provider-solution-type", "frame-support", @@ -2971,7 +2971,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "frame-system", @@ -3000,7 +3000,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "bitflags", "frame-metadata", @@ -3032,7 +3032,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "Inflector", "cfg-expr", @@ -3046,7 +3046,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", @@ -3058,7 +3058,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "proc-macro2", "quote", @@ -3068,7 +3068,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "log", @@ -3086,7 +3086,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -3101,7 +3101,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", "sp-api", @@ -3110,7 +3110,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "parity-scale-codec", @@ -3966,7 +3966,7 @@ checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" [[package]] name = "kusama-runtime" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "beefy-primitives", "bitvec", @@ -3992,7 +3992,7 @@ dependencies = [ "pallet-conviction-voting", "pallet-democracy", "pallet-election-provider-multi-phase", - "pallet-election-provider-support-benchmarking", + "pallet-election-provider-support-benchmarking 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=master)", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-gilt", @@ -4027,7 +4027,7 @@ dependencies = [ "pallet-vesting", "pallet-whitelist", "pallet-xcm", - "pallet-xcm-benchmarks", + "pallet-xcm-benchmarks 0.9.31 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", "parity-scale-codec", "polkadot-primitives", "polkadot-runtime-common", @@ -4064,7 +4064,7 @@ dependencies = [ [[package]] name = "kusama-runtime-constants" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "frame-support", "polkadot-primitives", @@ -5324,7 +5324,7 @@ dependencies = [ [[package]] name = "pallet-aura" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#4e1e17cccd499dfe49e8c1bed01957953aa4c839" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "frame-system", @@ -5340,7 +5340,7 @@ dependencies = [ [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "frame-system", @@ -5356,7 +5356,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "frame-system", @@ -5371,7 +5371,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5395,7 +5395,7 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5415,7 +5415,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5430,7 +5430,7 @@ dependencies = [ [[package]] name = "pallet-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "beefy-primitives", "frame-support", @@ -5446,7 +5446,7 @@ dependencies = [ [[package]] name = "pallet-beefy-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "array-bytes", "beefy-merkle-tree", @@ -5469,7 +5469,7 @@ dependencies = [ [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5487,7 +5487,7 @@ dependencies = [ [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5531,7 +5531,7 @@ dependencies = [ [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5548,7 +5548,7 @@ dependencies = [ [[package]] name = "pallet-contracts" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#4e1e17cccd499dfe49e8c1bed01957953aa4c839" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "bitflags", "frame-benchmarking", @@ -5577,7 +5577,7 @@ dependencies = [ [[package]] name = "pallet-contracts-primitives" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#4e1e17cccd499dfe49e8c1bed01957953aa4c839" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "bitflags", "parity-scale-codec", @@ -5589,7 +5589,7 @@ dependencies = [ [[package]] name = "pallet-contracts-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#4e1e17cccd499dfe49e8c1bed01957953aa4c839" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "proc-macro2", "quote", @@ -5599,7 +5599,7 @@ dependencies = [ [[package]] name = "pallet-conviction-voting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "assert_matches", "frame-benchmarking", @@ -5616,7 +5616,7 @@ dependencies = [ [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5634,14 +5634,14 @@ dependencies = [ [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-election-provider-support", "frame-support", "frame-system", "log", - "pallet-election-provider-support-benchmarking", + "pallet-election-provider-support-benchmarking 4.0.0-dev (git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges)", "parity-scale-codec", "rand 0.7.3", "scale-info", @@ -5668,10 +5668,23 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-election-provider-support-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-system", + "parity-scale-codec", + "sp-npos-elections", + "sp-runtime", +] + [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5689,7 +5702,7 @@ dependencies = [ [[package]] name = "pallet-fast-unstake" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5707,7 +5720,7 @@ dependencies = [ [[package]] name = "pallet-gilt" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5722,7 +5735,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5745,7 +5758,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "enumflags2", "frame-benchmarking", @@ -5761,7 +5774,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5781,7 +5794,7 @@ dependencies = [ [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5798,7 +5811,7 @@ dependencies = [ [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5815,7 +5828,7 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "ckb-merkle-mountain-range", "frame-benchmarking", @@ -5833,7 +5846,7 @@ dependencies = [ [[package]] name = "pallet-mmr-rpc" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -5848,7 +5861,7 @@ dependencies = [ [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5864,7 +5877,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "frame-system", @@ -5901,7 +5914,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", "sp-api", @@ -5911,7 +5924,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "frame-system", @@ -5951,7 +5964,7 @@ dependencies = [ [[package]] name = "pallet-preimage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5968,7 +5981,7 @@ dependencies = [ [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5983,7 +5996,7 @@ dependencies = [ [[package]] name = "pallet-randomness-collective-flip" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#4e1e17cccd499dfe49e8c1bed01957953aa4c839" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "frame-system", @@ -5997,7 +6010,7 @@ dependencies = [ [[package]] name = "pallet-ranked-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -6015,7 +6028,7 @@ dependencies = [ [[package]] name = "pallet-recovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -6030,7 +6043,7 @@ dependencies = [ [[package]] name = "pallet-referenda" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "assert_matches", "frame-benchmarking", @@ -6048,7 +6061,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -6064,7 +6077,7 @@ dependencies = [ [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "frame-system", @@ -6101,7 +6114,7 @@ dependencies = [ [[package]] name = "pallet-society" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "frame-system", @@ -6115,7 +6128,7 @@ dependencies = [ [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6138,7 +6151,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -6149,7 +6162,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "log", "sp-arithmetic", @@ -6158,7 +6171,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "frame-system", @@ -6187,7 +6200,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -6205,7 +6218,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -6224,7 +6237,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "frame-system", @@ -6240,7 +6253,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", @@ -6255,7 +6268,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -6266,7 +6279,7 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -6283,7 +6296,7 @@ dependencies = [ [[package]] name = "pallet-uniques" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#4e1e17cccd499dfe49e8c1bed01957953aa4c839" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -6298,7 +6311,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -6314,7 +6327,7 @@ dependencies = [ [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -6329,7 +6342,7 @@ dependencies = [ [[package]] name = "pallet-whitelist" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -6344,7 +6357,7 @@ dependencies = [ [[package]] name = "pallet-xcm" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "frame-support", "frame-system", @@ -6353,12 +6366,32 @@ dependencies = [ "scale-info", "serde", "sp-core", + "sp-io", "sp-runtime", "sp-std", "xcm", "xcm-executor", ] +[[package]] +name = "pallet-xcm-benchmarks" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", + "xcm-builder", + "xcm-executor", +] + [[package]] name = "pallet-xcm-benchmarks" version = "0.9.31" @@ -6909,7 +6942,7 @@ dependencies = [ [[package]] name = "polkadot-approval-distribution" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "futures", "polkadot-node-network-protocol", @@ -6924,7 +6957,7 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "futures", "polkadot-node-network-protocol", @@ -6938,7 +6971,7 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "derive_more", "fatality", @@ -6961,7 +6994,7 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "fatality", "futures", @@ -6982,7 +7015,7 @@ dependencies = [ [[package]] name = "polkadot-cli" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "clap 4.0.18", "frame-benchmarking-cli", @@ -7008,7 +7041,7 @@ dependencies = [ [[package]] name = "polkadot-client" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "beefy-primitives", "frame-benchmarking", @@ -7049,7 +7082,7 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "always-assert", "bitvec", @@ -7071,7 +7104,7 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "parity-scale-codec", "parity-util-mem", @@ -7084,7 +7117,7 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "derive_more", "fatality", @@ -7109,7 +7142,7 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", @@ -7123,7 +7156,7 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "futures", "futures-timer", @@ -7143,7 +7176,7 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "always-assert", "async-trait", @@ -7167,7 +7200,7 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "futures", "parity-scale-codec", @@ -7185,7 +7218,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "bitvec", "derive_more", @@ -7214,7 +7247,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "bitvec", "futures", @@ -7234,7 +7267,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "bitvec", "fatality", @@ -7253,7 +7286,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "futures", "polkadot-node-subsystem", @@ -7268,7 +7301,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "async-trait", "futures", @@ -7286,7 +7319,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "futures", "polkadot-node-subsystem", @@ -7301,7 +7334,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "futures", "futures-timer", @@ -7318,7 +7351,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "fatality", "futures", @@ -7337,7 +7370,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "async-trait", "futures", @@ -7354,7 +7387,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "bitvec", "fatality", @@ -7372,7 +7405,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "always-assert", "assert_matches", @@ -7404,7 +7437,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-checker" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "futures", "polkadot-node-primitives", @@ -7420,7 +7453,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "futures", "memory-lru", @@ -7436,7 +7469,7 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "async-std", "lazy_static", @@ -7454,7 +7487,7 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "bs58", "futures", @@ -7473,7 +7506,7 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "async-trait", "derive_more", @@ -7496,7 +7529,7 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "bounded-vec", "futures", @@ -7518,7 +7551,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -7546,7 +7579,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "async-trait", "derive_more", @@ -7569,7 +7602,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "async-trait", "derive_more", @@ -7602,7 +7635,7 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "async-trait", "futures", @@ -7625,7 +7658,7 @@ dependencies = [ [[package]] name = "polkadot-parachain" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "derive_more", "frame-support", @@ -7723,7 +7756,7 @@ dependencies = [ [[package]] name = "polkadot-performance-test" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "env_logger", "kusama-runtime", @@ -7738,7 +7771,7 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "bitvec", "frame-system", @@ -7768,7 +7801,7 @@ dependencies = [ [[package]] name = "polkadot-rpc" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", @@ -7800,7 +7833,7 @@ dependencies = [ [[package]] name = "polkadot-runtime" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "beefy-primitives", "bitvec", @@ -7824,7 +7857,7 @@ dependencies = [ "pallet-collective", "pallet-democracy", "pallet-election-provider-multi-phase", - "pallet-election-provider-support-benchmarking", + "pallet-election-provider-support-benchmarking 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=master)", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-grandpa", @@ -7889,7 +7922,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "beefy-primitives", "bitvec", @@ -7936,7 +7969,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-constants" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "frame-support", "polkadot-primitives", @@ -7948,7 +7981,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-metrics" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "bs58", "parity-scale-codec", @@ -7960,7 +7993,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "bitflags", "bitvec", @@ -8003,7 +8036,7 @@ dependencies = [ [[package]] name = "polkadot-service" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "async-trait", "beefy-gadget", @@ -8108,7 +8141,7 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "arrayvec 0.5.2", "fatality", @@ -8129,7 +8162,7 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -8164,7 +8197,7 @@ dependencies = [ [[package]] name = "polkadot-test-runtime" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "beefy-primitives", "bitvec", @@ -8216,7 +8249,7 @@ dependencies = [ "sp-transaction-pool", "sp-version", "substrate-wasm-builder", - "test-runtime-constants", + "test-runtime-constants 0.9.31 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", "xcm", "xcm-builder", "xcm-executor", @@ -8271,7 +8304,7 @@ dependencies = [ "sp-state-machine", "substrate-test-client", "tempfile", - "test-runtime-constants", + "test-runtime-constants 0.9.31 (git+https://github.com/paritytech/polkadot?branch=master)", "tokio", "tracing-gum", ] @@ -8799,7 +8832,7 @@ checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "env_logger", "log", @@ -8918,7 +8951,7 @@ dependencies = [ [[package]] name = "rococo-runtime" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "beefy-merkle-tree", "beefy-primitives", @@ -8966,7 +8999,7 @@ dependencies = [ "pallet-utility", "pallet-vesting", "pallet-xcm", - "pallet-xcm-benchmarks", + "pallet-xcm-benchmarks 0.9.31 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", "parity-scale-codec", "polkadot-parachain 0.9.31", "polkadot-primitives", @@ -9002,7 +9035,7 @@ dependencies = [ [[package]] name = "rococo-runtime-constants" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "frame-support", "polkadot-primitives", @@ -9163,7 +9196,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "log", "sp-core", @@ -9174,7 +9207,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "futures", @@ -9201,7 +9234,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "futures", "futures-timer", @@ -9224,7 +9257,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -9240,7 +9273,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "impl-trait-for-tuples", "memmap2", @@ -9257,7 +9290,7 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -9268,7 +9301,7 @@ dependencies = [ [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "array-bytes", "chrono", @@ -9308,7 +9341,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "fnv", "futures", @@ -9336,7 +9369,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "hash-db", "kvdb", @@ -9361,7 +9394,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "futures", @@ -9385,7 +9418,7 @@ dependencies = [ [[package]] name = "sc-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#4e1e17cccd499dfe49e8c1bed01957953aa4c839" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "futures", @@ -9414,7 +9447,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "fork-tree", @@ -9456,7 +9489,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "futures", "jsonrpsee", @@ -9478,7 +9511,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "fork-tree", "parity-scale-codec", @@ -9491,7 +9524,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "futures", @@ -9515,7 +9548,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "lazy_static", "lru 0.7.8", @@ -9542,7 +9575,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "environmental", "parity-scale-codec", @@ -9558,7 +9591,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "log", "parity-scale-codec", @@ -9573,7 +9606,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "cfg-if 1.0.0", "libc", @@ -9593,7 +9626,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "ahash", "array-bytes", @@ -9634,7 +9667,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "finality-grandpa", "futures", @@ -9655,7 +9688,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "ansi_term", "futures", @@ -9672,7 +9705,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "array-bytes", "async-trait", @@ -9687,7 +9720,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "array-bytes", "async-trait", @@ -9734,7 +9767,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "cid", "futures", @@ -9754,7 +9787,7 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "bitflags", @@ -9780,7 +9813,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "ahash", "futures", @@ -9798,7 +9831,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "array-bytes", "futures", @@ -9819,7 +9852,7 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "array-bytes", "fork-tree", @@ -9849,7 +9882,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "array-bytes", "futures", @@ -9868,7 +9901,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "array-bytes", "bytes", @@ -9898,7 +9931,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "futures", "libp2p", @@ -9911,7 +9944,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -9920,7 +9953,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "futures", "hash-db", @@ -9950,7 +9983,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "futures", "jsonrpsee", @@ -9973,7 +10006,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "futures", "jsonrpsee", @@ -9986,7 +10019,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "futures", "hex", @@ -10005,7 +10038,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "directories", @@ -10076,7 +10109,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "log", "parity-scale-codec", @@ -10090,7 +10123,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -10109,7 +10142,7 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "futures", "libc", @@ -10128,7 +10161,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "chrono", "futures", @@ -10146,7 +10179,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "ansi_term", "atty", @@ -10177,7 +10210,7 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -10188,7 +10221,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "futures", @@ -10215,7 +10248,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "futures", @@ -10229,7 +10262,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "futures", "futures-timer", @@ -10655,7 +10688,7 @@ checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" [[package]] name = "slot-range-helper" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "enumn", "parity-scale-codec", @@ -10731,7 +10764,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "hash-db", "log", @@ -10749,7 +10782,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "blake2", "proc-macro-crate", @@ -10761,7 +10794,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", "scale-info", @@ -10774,7 +10807,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "integer-sqrt", "num-traits", @@ -10789,7 +10822,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", "scale-info", @@ -10802,7 +10835,7 @@ dependencies = [ [[package]] name = "sp-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "parity-scale-codec", @@ -10814,7 +10847,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", "sp-api", @@ -10826,7 +10859,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "futures", "log", @@ -10844,7 +10877,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "futures", @@ -10863,7 +10896,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#4e1e17cccd499dfe49e8c1bed01957953aa4c839" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "parity-scale-codec", @@ -10881,7 +10914,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "merlin", @@ -10904,7 +10937,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", "scale-info", @@ -10918,7 +10951,7 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", "scale-info", @@ -10931,7 +10964,7 @@ dependencies = [ [[package]] name = "sp-core" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "array-bytes", "base58", @@ -10977,7 +11010,7 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "blake2", "byteorder", @@ -10991,7 +11024,7 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "proc-macro2", "quote", @@ -11002,7 +11035,7 @@ dependencies = [ [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -11011,7 +11044,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "proc-macro2", "quote", @@ -11021,7 +11054,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "environmental", "parity-scale-codec", @@ -11032,7 +11065,7 @@ dependencies = [ [[package]] name = "sp-finality-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "finality-grandpa", "log", @@ -11050,7 +11083,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -11064,7 +11097,7 @@ dependencies = [ [[package]] name = "sp-io" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "bytes", "futures", @@ -11090,7 +11123,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "lazy_static", "sp-core", @@ -11101,7 +11134,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "futures", @@ -11118,7 +11151,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "thiserror", "zstd", @@ -11127,7 +11160,7 @@ dependencies = [ [[package]] name = "sp-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "log", "parity-scale-codec", @@ -11143,7 +11176,7 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", "scale-info", @@ -11157,7 +11190,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "sp-api", "sp-core", @@ -11167,7 +11200,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "backtrace", "lazy_static", @@ -11177,7 +11210,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "rustc-hash", "serde", @@ -11187,7 +11220,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "either", "hash256-std-hasher", @@ -11210,7 +11243,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -11228,7 +11261,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "Inflector", "proc-macro-crate", @@ -11240,7 +11273,7 @@ dependencies = [ [[package]] name = "sp-sandbox" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "log", "parity-scale-codec", @@ -11263,7 +11296,7 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", "scale-info", @@ -11277,7 +11310,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", "scale-info", @@ -11288,7 +11321,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "hash-db", "log", @@ -11310,12 +11343,12 @@ dependencies = [ [[package]] name = "sp-std" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" [[package]] name = "sp-storage" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "impl-serde", "parity-scale-codec", @@ -11328,7 +11361,7 @@ dependencies = [ [[package]] name = "sp-tasks" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "log", "sp-core", @@ -11341,7 +11374,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "futures-timer", @@ -11357,7 +11390,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", "sp-std", @@ -11369,7 +11402,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "sp-api", "sp-runtime", @@ -11378,7 +11411,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "log", @@ -11394,7 +11427,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "ahash", "hash-db", @@ -11417,7 +11450,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "impl-serde", "parity-scale-codec", @@ -11434,7 +11467,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -11445,7 +11478,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "impl-trait-for-tuples", "log", @@ -11458,7 +11491,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -11547,7 +11580,7 @@ dependencies = [ "pallet-uniques", "pallet-utility", "pallet-xcm", - "pallet-xcm-benchmarks", + "pallet-xcm-benchmarks 0.9.31 (git+https://github.com/paritytech/polkadot?branch=master)", "parachain-info", "parachains-common", "parity-scale-codec", @@ -11611,7 +11644,7 @@ dependencies = [ "pallet-uniques", "pallet-utility", "pallet-xcm", - "pallet-xcm-benchmarks", + "pallet-xcm-benchmarks 0.9.31 (git+https://github.com/paritytech/polkadot?branch=master)", "parachain-info", "parachains-common", "parity-scale-codec", @@ -11754,7 +11787,7 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "platforms", ] @@ -11762,7 +11795,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-system-rpc-runtime-api", "futures", @@ -11783,7 +11816,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "futures-util", "hyper", @@ -11796,7 +11829,7 @@ dependencies = [ [[package]] name = "substrate-rpc-client" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "async-trait", "jsonrpsee", @@ -11809,7 +11842,7 @@ dependencies = [ [[package]] name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "jsonrpsee", "log", @@ -11877,7 +11910,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "ansi_term", "build-helper", @@ -11982,6 +12015,18 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" +[[package]] +name = "test-runtime-constants" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "frame-support", + "polkadot-primitives", + "polkadot-runtime-common", + "smallvec", + "sp-runtime", +] + [[package]] name = "test-runtime-constants" version = "0.9.31" @@ -12252,7 +12297,7 @@ dependencies = [ [[package]] name = "tracing-gum" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "polkadot-node-jaeger", "polkadot-primitives", @@ -12263,7 +12308,7 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "expander 0.0.6", "proc-macro-crate", @@ -12390,7 +12435,7 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "clap 4.0.18", "frame-try-runtime", @@ -13009,7 +13054,7 @@ dependencies = [ [[package]] name = "westend-runtime" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "beefy-primitives", "bitvec", @@ -13031,7 +13076,7 @@ dependencies = [ "pallet-collective", "pallet-democracy", "pallet-election-provider-multi-phase", - "pallet-election-provider-support-benchmarking", + "pallet-election-provider-support-benchmarking 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=master)", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-grandpa", @@ -13062,7 +13107,7 @@ dependencies = [ "pallet-utility", "pallet-vesting", "pallet-xcm", - "pallet-xcm-benchmarks", + "pallet-xcm-benchmarks 0.9.31 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", "parity-scale-codec", "polkadot-parachain 0.9.31", "polkadot-primitives", @@ -13090,12 +13135,24 @@ dependencies = [ "sp-transaction-pool", "sp-version", "substrate-wasm-builder", - "westend-runtime-constants", + "westend-runtime-constants 0.9.31 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", "xcm", "xcm-builder", "xcm-executor", ] +[[package]] +name = "westend-runtime-constants" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "frame-support", + "polkadot-primitives", + "polkadot-runtime-common", + "smallvec", + "sp-runtime", +] + [[package]] name = "westend-runtime-constants" version = "0.9.31" @@ -13146,7 +13203,7 @@ dependencies = [ "pallet-uniques", "pallet-utility", "pallet-xcm", - "pallet-xcm-benchmarks", + "pallet-xcm-benchmarks 0.9.31 (git+https://github.com/paritytech/polkadot?branch=master)", "parachain-info", "parachains-common", "parity-scale-codec", @@ -13167,7 +13224,7 @@ dependencies = [ "sp-transaction-pool", "sp-version", "substrate-wasm-builder", - "westend-runtime-constants", + "westend-runtime-constants 0.9.31 (git+https://github.com/paritytech/polkadot?branch=master)", "xcm", "xcm-builder", "xcm-executor", @@ -13339,13 +13396,15 @@ dependencies = [ [[package]] name = "xcm" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "derivative", "impl-trait-for-tuples", "log", "parity-scale-codec", "scale-info", + "serde", + "sp-io", "sp-runtime", "xcm-procedural", ] @@ -13353,10 +13412,11 @@ dependencies = [ [[package]] name = "xcm-builder" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "frame-support", "frame-system", + "impl-trait-for-tuples", "log", "pallet-transaction-payment", "parity-scale-codec", @@ -13373,7 +13433,7 @@ dependencies = [ [[package]] name = "xcm-executor" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "frame-benchmarking", "frame-support", @@ -13391,7 +13451,7 @@ dependencies = [ [[package]] name = "xcm-procedural" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "Inflector", "proc-macro2", @@ -13462,3 +13522,8 @@ dependencies = [ "cc", "libc", ] + +[[patch.unused]] +name = "node-inspect" +version = "0.9.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" diff --git a/Cargo.toml b/Cargo.toml index cacb7bea7f1..365ec722a6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,3 +53,240 @@ opt-level = 3 inherits = "release" lto = true codegen-units = 1 + +# we need to be able to work with XCMv3, but it is not yet in Polkadot master +# => manual patch is required. Because of https://github.com/rust-lang/cargo/issues/5478 +# we need to use double slash in the repo name. +# +# Once XCMv3 PR is merged, we may remove both Substrate and Polkadot patch section. + +[patch."https://github.com/paritytech/substrate"] +beefy-gadget = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +beefy-gadget-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +beefy-merkle-tree = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +beefy-primitives = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +fork-tree = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-benchmarking-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-election-provider-solution-type = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-election-provider-support = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-executive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-support = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-support-procedural = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-support-procedural-tools = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-support-procedural-tools-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-system = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-system-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-try-runtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +node-inspect = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-bags-list = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-balances = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-beefy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-beefy-mmr = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-bounties = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-child-bounties = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-collective = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-contracts = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-contracts-primitives = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-conviction-voting = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-democracy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-election-provider-multi-phase = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-elections-phragmen = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-fast-unstake = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-gilt = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-identity = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-im-online = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-indices = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-membership = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-mmr = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-mmr-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-multisig = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-nomination-pools = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-nomination-pools-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-offences = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-preimage = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-proxy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-randomness-collective-flip = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-ranked-collective = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-recovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-referenda = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-scheduler = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-session = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-society = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-staking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-staking-reward-curve = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-staking-reward-fn = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-sudo = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-timestamp = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-tips = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-transaction-payment = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-transaction-payment-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-treasury = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-utility = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-uniques = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-vesting = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-whitelist = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +remote-externalities = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-allocator = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-basic-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-block-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-chain-spec = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-chain-spec-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-client-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-client-db = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-consensus = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-consensus-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-consensus-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-consensus-babe-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-consensus-epochs = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-consensus-slots = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-executor = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-executor-common = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-executor-wasmi = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-executor-wasmtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-finality-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-finality-grandpa-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-informant = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-keystore = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-network = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-network-common = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-network-gossip = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-network-light = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-network-sync = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-offchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-peerset = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-proposer-metrics = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-rpc-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-rpc-server = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-service = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-state-db = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-sync-state-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-sysinfo = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-telemetry = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-tracing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-tracing-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-transaction-pool = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-transaction-pool-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-utils = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-api-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-application-crypto = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-arithmetic = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-block-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-blockchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-consensus = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-consensus-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-consensus-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-consensus-slots = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-consensus-vrf = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-core = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-core-hashing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-core-hashing-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-database = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-debug-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-externalities = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-finality-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-inherents = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-io = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-keyring = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-keystore = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-maybe-compressed-blob = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-mmr-primitives = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-npos-elections = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-offchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-panic-handler = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-runtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-runtime-interface = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-runtime-interface-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-session = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-staking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-state-machine = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-std = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-storage = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-tasks = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-timestamp = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-tracing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-transaction-pool = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-transaction-storage-proof = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-trie = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-version = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-version-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-wasm-interface = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +substrate-build-script-utils = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +substrate-frame-rpc-system = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +substrate-prometheus-endpoint = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +substrate-state-trie-migration-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +substrate-wasm-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +try-runtime-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } + +[patch."https://github.com/paritytech/polkadot"] +kusama-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +kusama-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +pallet-xcm = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-approval-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-availability-bitfield-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-availability-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-availability-recovery = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-cli = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-client = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-collator-protocol = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-core-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-dispute-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-erasure-coding = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-gossip-support = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-network-bridge = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-collation-generation = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-approval-voting = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-av-store = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-backing = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-bitfield-signing = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-candidate-validation = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-chain-api = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-chain-selection = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-dispute-coordinator = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-parachains-inherent = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-provisioner = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-pvf = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-pvf-checker = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-runtime-api = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-jaeger = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-metrics = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-network-protocol = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-subsystem = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-subsystem-types = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-subsystem-util = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-overseer = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-parachain = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-performance-test = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-rpc = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-runtime-common = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-runtime-metrics = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-runtime-parachains = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-service = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-statement-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-statement-table = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-test-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +slot-range-helper = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +tracing-gum = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +tracing-gum-proc-macro = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +xcm = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +xcm-builder = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +xcm-executor = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +xcm-procedural = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } From e48282b840a8c997bb354bf8fe1c192de0f5dbf9 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 28 Jul 2022 18:18:59 +0200 Subject: [PATCH 059/263] [BridgeHub] Setup Rococo backbone parachain --- Cargo.lock | 58 ++ Cargo.toml | 1 + README.md | 4 + parachains/runtimes/bridge-hubs/README.md | 65 ++ .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 139 ++++ .../bridge-hubs/bridge-hub-rococo/build.rs | 9 + .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 719 ++++++++++++++++++ .../src/weights/block_weights.rs | 46 ++ .../src/weights/extrinsic_weights.rs | 46 ++ .../bridge-hub-rococo/src/weights/mod.rs | 28 + .../src/weights/paritydb_weights.rs | 63 ++ .../src/weights/rocksdb_weights.rs | 63 ++ .../bridge-hub-rococo/src/xcm_config.rs | 221 ++++++ polkadot-parachain/Cargo.toml | 1 + .../src/chain_spec/bridge_hubs.rs | 182 +++++ polkadot-parachain/src/chain_spec/mod.rs | 1 + polkadot-parachain/src/command.rs | 42 + 17 files changed, 1688 insertions(+) create mode 100644 parachains/runtimes/bridge-hubs/README.md create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/build.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/block_weights.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/extrinsic_weights.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/paritydb_weights.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/rocksdb_weights.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs create mode 100644 polkadot-parachain/src/chain_spec/bridge_hubs.rs diff --git a/Cargo.lock b/Cargo.lock index c908727eac7..74fe22a8dcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -713,6 +713,63 @@ dependencies = [ "thiserror", ] +[[package]] +name = "bridge-hub-rococo-runtime" +version = "0.1.0" +dependencies = [ + "cumulus-pallet-aura-ext", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-session-benchmarking", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal", + "log", + "pallet-aura", + "pallet-authorship", + "pallet-balances", + "pallet-collator-selection", + "pallet-session", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-xcm", + "parachain-info", + "parity-scale-codec", + "polkadot-parachain 0.9.27", + "polkadot-runtime-common", + "scale-info", + "serde", + "smallvec", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + [[package]] name = "bs58" version = "0.4.0" @@ -7678,6 +7735,7 @@ version = "0.9.300" dependencies = [ "assert_cmd", "async-trait", + "bridge-hub-rococo-runtime", "clap 4.0.18", "collectives-polkadot-runtime", "contracts-rococo-runtime", diff --git a/Cargo.toml b/Cargo.toml index 365ec722a6e..b2d34b8e3f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ members = [ "parachains/runtimes/assets/statemint", "parachains/runtimes/assets/statemine", "parachains/runtimes/assets/westmint", + "parachains/runtimes/bridge-hubs/bridge-hub-rococo", "parachains/runtimes/collectives/collectives-polkadot", "parachains/runtimes/contracts/contracts-rococo", "parachains/runtimes/testing/penpal", diff --git a/README.md b/README.md index 3d6a47fbba6..7ac6ca2e518 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,10 @@ Refer to the [setup instructions below](#local-setup) to run a local network for See [the `contracts-rococo` readme](parachains/runtimes/contracts/contracts-rococo/README.md) for details. +## Bridge-hub 📝 + +See [the `bridge-hubs` readme](parachains/runtimes/bridge-hubs/README.md) for details. + ## Rococo 👑 [Rococo](https://polkadot.js.org/apps/?rpc=wss://rococo-rpc.polkadot.io) is becoming a [Community Parachain Testbed](https://polkadot.network/blog/rococo-revamp-becoming-a-community-parachain-testbed/) for parachain teams in the Polkadot ecosystem. It supports multiple parachains with the differentiation of long-term connections and recurring short-term connections, to see which parachains are currently connected and how long they will be connected for [see here](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frococo-rpc.polkadot.io#/parachains). diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md new file mode 100644 index 00000000000..9f89d42e2d2 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/README.md @@ -0,0 +1,65 @@ +# Bride-hubs Parachain + +Implementation of _BridgeHub_, a blockchain to support message passing between Substrate based chains like Polkadot and Kusama networks. + +_BridgeHub_ allows users to: + +- Passing arbitrary messages between different Substrate chains (Polkadot <-> Kusama). +-- Message passing is + +Every _BridgeHub_ is meant to be **_common good parachain_** with main responsibilities: +- sync finality proofs between relay chains +- sync finality proofs between BridgeHub parachains +- pass (XCM) messages between different BridgeHub parachains + +## How to test locally Rococo <-> Wococo + +### Deploy +``` +cd +cargo build --release --locked -p polkadot-parachain@0.9.220 + +mkdir -p ~/local_bridge_testing/bin + +rm ~/local_bridge_testing/bin/polkadot-parachain +cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain +ls -lrt ~/local_bridge_testing/bin/polkadot-parachain +``` + +### Run relay chain (Rococo) +``` + + +~/local_bridge_testing/bin/polkadot build-spec --chain rococo-local --disable-default-bootnode --raw > ~/local_bridge_testing/rococo-local-cfde.json +~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --alice --tmp +~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --bob --tmp --port 30334 +``` + +### Run Rococo BridgeHub parachain +#### Generate spec + genesis + wasm (paraId=1013) +``` +~/local_bridge_testing/bin/polkadot-parachain build-spec --chain bridge-hub-rococo-local --raw --disable-default-bootnode > ~/local_bridge_testing/bridge-hub-rococo-local-raw.json +~/local_bridge_testing/bin/polkadot-parachain export-genesis-state --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json > ~/local_bridge_testing/bridge-hub-rococo-local-genesis +~/local_bridge_testing/bin/polkadot-parachain export-genesis-wasm --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json > ~/local_bridge_testing/bridge-hub-rococo-local-genesis-wasm +``` + +#### Run collators +``` +~/local_bridge_testing/bin/polkadot-parachain --collator --alice --force-authoring --tmp --port 40335 --ws-port 9946 --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 30335 +~/local_bridge_testing/bin/polkadot-parachain --collator --bob --force-authoring --tmp --port 40336 --ws-port 9947 --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 30336 +``` + +#### Run parachain node +``` +~/local_bridge_testing/bin/polkadot-parachain --tmp --port 40337 --ws-port 9948 --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 30337 +``` + +#### Activate parachain (paraId=1013) +``` +https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/explorer +``` + +#### After parachain activation, we should see new blocks in collator's node +``` +https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/parachains +``` diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml new file mode 100644 index 00000000000..2bf9f9867cf --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -0,0 +1,139 @@ +[package] +name = "bridge-hub-rococo-runtime" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +description = "Rococo's BridgeHub parachain runtime" + +[build-dependencies] +substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +hex-literal = { version = "0.3.4", optional = true } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +serde = { version = "1.0.137", optional = true, features = ["derive"] } +smallvec = "1.8.1" + +# Substrate +frame-benchmarking = { git = "https://github.com/paritytech/substrate", default-features = false, optional = true, branch = "master" } +frame-executive = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", default-features = false, optional = true, branch = "master" } +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +frame-try-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, optional = true, branch = "master" } +pallet-aura = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +pallet-authorship = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +pallet-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +pallet-sudo = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +pallet-timestamp = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-block-builder = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-consensus-aura = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-inherents = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-offchain = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-transaction-pool = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-version = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } + +# Polkadot +pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } + +# Cumulus +cumulus-pallet-aura-ext = { path = "../../../../pallets/aura-ext", default-features = false } +cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-features = false } +cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false } +cumulus-pallet-session-benchmarking = {path = "../../../../pallets/session-benchmarking", default-features = false, version = "3.0.0"} +cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = false } +cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } +cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } +cumulus-primitives-timestamp = { path = "../../../../primitives/timestamp", default-features = false } +cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } +pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } +parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false } + +[features] +default = [ + "std", +] +std = [ + "codec/std", + "log/std", + "scale-info/std", + "serde", + "cumulus-pallet-aura-ext/std", + "cumulus-pallet-dmp-queue/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-core/std", + "cumulus-primitives-timestamp/std", + "cumulus-primitives-utility/std", + "frame-executive/std", + "frame-support/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "pallet-aura/std", + "pallet-authorship/std", + "pallet-balances/std", + "pallet-collator-selection/std", + "pallet-session/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "pallet-xcm/std", + "parachain-info/std", + "polkadot-parachain/std", + "polkadot-runtime-common/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-core/std", + "sp-inherents/std", + "sp-io/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-version/std", + "xcm-builder/std", + "xcm-executor/std", + "xcm/std", +] + +runtime-benchmarks = [ + "hex-literal", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-collator-selection/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "cumulus-pallet-session-benchmarking/runtime-benchmarks", + "cumulus-pallet-xcmp-queue/runtime-benchmarks", +] + +try-runtime = [ + "frame-executive/try-runtime", + "frame-try-runtime", +] diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/build.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/build.rs new file mode 100644 index 00000000000..9b53d2457df --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/build.rs @@ -0,0 +1,9 @@ +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs new file mode 100644 index 00000000000..9076081c28c --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -0,0 +1,719 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +mod weights; +pub mod xcm_config; + +use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use smallvec::smallvec; +use sp_api::impl_runtime_apis; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, MultiSignature, +}; + +use sp_std::prelude::*; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +use frame_support::{ + construct_runtime, parameter_types, + traits::Everything, + weights::{ + constants::WEIGHT_PER_SECOND, ConstantMultiplier, DispatchClass, Weight, + WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, + }, + PalletId, +}; +use frame_system::{ + limits::{BlockLength, BlockWeights}, + EnsureRoot, +}; +pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; +pub use sp_runtime::{MultiAddress, Perbill, Permill}; +use xcm_config::{XcmConfig, XcmOriginToTransactDispatchOrigin}; + +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; + +// Polkadot imports +use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; + +use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; + +// XCM Imports +use xcm::latest::prelude::BodyId; +use xcm_executor::XcmExecutor; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = MultiSignature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +/// Balance of an account. +pub type Balance = u128; + +/// Index of a transaction in the chain. +pub type Index = u32; + +/// A hash of some data used by the chain. +pub type Hash = sp_core::H256; + +/// An index to a block. +pub type BlockNumber = u32; + +/// The address format for describing accounts. +pub type Address = MultiAddress; + +/// Block header type as expected by this runtime. +pub type Header = generic::Header; + +/// Block type as expected by this runtime. +pub type Block = generic::Block; + +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; + +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; + +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); + +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; + +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = generic::CheckedExtrinsic; + +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +/// Handles converting a weight scalar to a fee value, based on the scale and granularity of the +/// node's balance type. +/// +/// This should typically create a mapping between the following ranges: +/// - `[0, MAXIMUM_BLOCK_WEIGHT]` +/// - `[Balance::min, Balance::max]` +/// +/// Yet, it can be used for any other sort of change to weight-fee. Some examples being: +/// - Setting it to `0` will essentially disable the weight fee. +/// - Setting it to `1` will cause the literal `#[weight = x]` values to be charged. +pub struct WeightToFee; +impl WeightToFeePolynomial for WeightToFee { + type Balance = Balance; + fn polynomial() -> WeightToFeeCoefficients { + // in Rococo, extrinsic base weight (smallest non-zero weight) is mapped to 1 MILLIUNIT: + // in our template, we map to 1/10 of that, or 1/10 MILLIUNIT + let p = MILLIUNIT / 10; + let q = 100 * Balance::from(ExtrinsicBaseWeight::get()); + smallvec![WeightToFeeCoefficient { + degree: 1, + negative: false, + coeff_frac: Perbill::from_rational(p % q, q), + coeff_integer: p / q, + }] + } +} + +/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know +/// the specifics of the runtime. They can then be made to be agnostic over specific formats +/// of data like extrinsics, allowing for them to continue syncing the network through upgrades +/// to even the core data structures. +pub mod opaque { + use super::*; + use sp_runtime::{generic, traits::BlakeTwo256}; + + pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; + /// Opaque block header type. + pub type Header = generic::Header; + /// Opaque block type. + pub type Block = generic::Block; + /// Opaque block identifier type. + pub type BlockId = generic::BlockId; +} + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + } +} + +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("bridge-hub-rococo"), + impl_name: create_runtime_str!("bridge-hub-rococo"), + authoring_version: 1, + spec_version: 1, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 1, +}; + +/// This determines the average expected block time that we are targeting. +/// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. +/// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked +/// up by `pallet_aura` to implement `fn slot_duration()`. +/// +/// Change this to adjust the block time. +pub const MILLISECS_PER_BLOCK: u64 = 12000; + +// NOTE: Currently it is not possible to change the slot duration after the chain has started. +// Attempting to do so will brick block production. +pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + +// Time is measured by number of blocks. +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; +pub const DAYS: BlockNumber = HOURS * 24; + +// Unit = the base number of indivisible units for balances +pub const UNIT: Balance = 1_000_000_000_000; +pub const MILLIUNIT: Balance = 1_000_000_000; +pub const MICROUNIT: Balance = 1_000_000; + +/// The existential deposit. Set to 1/10 of the Connected Relay Chain. +pub const EXISTENTIAL_DEPOSIT: Balance = MILLIUNIT; + +/// We assume that ~5% of the block weight is consumed by `on_initialize` handlers. This is +/// used to limit the maximal weight of a single extrinsic. +const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); + +/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used by +/// `Operational` extrinsics. +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// We allow for 0.5 of a second of compute with a 12 second average block time. +const MAXIMUM_BLOCK_WEIGHT: Weight = WEIGHT_PER_SECOND / 2; + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } +} + +parameter_types! { + pub const Version: RuntimeVersion = VERSION; + + // This part is copied from Substrate's `bin/node/runtime/src/lib.rs`. + // The `RuntimeBlockLength` and `RuntimeBlockWeights` exist here because the + // `DeletionWeightLimit` and `DeletionQueueDepth` depend on those to parameterize + // the lazy contract deletion. + pub RuntimeBlockLength: BlockLength = + BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + // Operational transactions have some extra reserved space, so that they + // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); + pub const SS58Prefix: u16 = 42; +} + +// Configure FRAME pallets to include in runtime. + +impl frame_system::Config for Runtime { + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type Call = Call; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = AccountIdLookup; + /// The index type for storing how many extrinsics an account has signed. + type Index = Index; + /// The index type for blocks. + type BlockNumber = BlockNumber; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = BlakeTwo256; + /// The header type. + type Header = generic::Header; + /// The ubiquitous event type. + type Event = Event; + /// The ubiquitous origin type. + type Origin = Origin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// Runtime version. + type Version = Version; + /// Converts a module to an index of this module in the runtime. + type PalletInfo = PalletInfo; + /// The data to be stored in an account. + type AccountData = pallet_balances::AccountData; + /// What to do if a new account is created. + type OnNewAccount = (); + /// What to do if an account is fully reaped from the system. + type OnKilledAccount = (); + /// The weight of database operations that the runtime can invoke. + type DbWeight = RocksDbWeight; + /// The basic call filter to use in dispatchable. + type BaseCallFilter = Everything; + /// Weight information for the extrinsics of this pallet. + type SystemWeightInfo = (); + /// Block & extrinsics weights: base values and limits. + type BlockWeights = RuntimeBlockWeights; + /// The maximum length of a block (in bytes). + type BlockLength = RuntimeBlockLength; + /// This is used as an identifier of the chain. 42 is the generic substrate prefix. + type SS58Prefix = SS58Prefix; + /// The action to take on a Runtime Upgrade + type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const MinimumPeriod: u64 = SLOT_DURATION / 2; +} + +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the unix epoch. + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +parameter_types! { + pub const UncleGenerations: u32 = 0; +} + +impl pallet_authorship::Config for Runtime { + type FindAuthor = pallet_session::FindAccountFromAuthorIndex; + type UncleGenerations = UncleGenerations; + type FilterUncle = (); + type EventHandler = (CollatorSelection,); +} + +parameter_types! { + pub const ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type Event = Event; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = pallet_balances::weights::SubstrateWeight; + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + +parameter_types! { + /// Relay Chain `TransactionByteFee` / 10 + pub const TransactionByteFee: Balance = 10 * MICROUNIT; + pub const OperationalFeeMultiplier: u8 = 5; +} + +impl pallet_transaction_payment::Config for Runtime { + type Event = Event; + type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; + type WeightToFee = WeightToFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type OperationalFeeMultiplier = OperationalFeeMultiplier; +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4; + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4; +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type Event = Event; + type OnSystemEvent = (); + type SelfParaId = parachain_info::Pallet; + type OutboundXcmpMessageSource = XcmpQueue; + type DmpMessageHandler = DmpQueue; + type ReservedDmpWeight = ReservedDmpWeight; + type XcmpMessageHandler = XcmpQueue; + type ReservedXcmpWeight = ReservedXcmpWeight; + type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; +} + +impl parachain_info::Config for Runtime {} + +impl cumulus_pallet_aura_ext::Config for Runtime {} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; + type ChannelInfo = ParachainSystem; + type VersionWrapper = (); + type ExecuteOverweightOrigin = EnsureRoot; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type WeightInfo = (); +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; + type ExecuteOverweightOrigin = EnsureRoot; +} + +parameter_types! { + pub const Period: u32 = 6 * HOURS; + pub const Offset: u32 = 0; + pub const MaxAuthorities: u32 = 100_000; +} + +impl pallet_session::Config for Runtime { + type Event = Event; + type ValidatorId = ::AccountId; + // we don't have stash and controller, thus we don't need the convert as well. + type ValidatorIdOf = pallet_collator_selection::IdentityCollator; + type ShouldEndSession = pallet_session::PeriodicSessions; + type NextSessionRotation = pallet_session::PeriodicSessions; + type SessionManager = CollatorSelection; + // Essentially just Aura, but lets be pedantic. + type SessionHandler = ::KeyTypeIdProviders; + type Keys = SessionKeys; + type WeightInfo = (); +} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = MaxAuthorities; +} + +parameter_types! { + pub const PotId: PalletId = PalletId(*b"PotStake"); + pub const MaxCandidates: u32 = 1000; + pub const MinCandidates: u32 = 5; + pub const SessionLength: BlockNumber = 6 * HOURS; + pub const MaxInvulnerables: u32 = 100; + pub const ExecutiveBody: BodyId = BodyId::Executive; +} + +// We allow root only to execute privileged collator selection operations. +pub type CollatorSelectionUpdateOrigin = EnsureRoot; + +impl pallet_collator_selection::Config for Runtime { + type Event = Event; + type Currency = Balances; + type UpdateOrigin = CollatorSelectionUpdateOrigin; + type PotId = PotId; + type MaxCandidates = MaxCandidates; + type MinCandidates = MinCandidates; + type MaxInvulnerables = MaxInvulnerables; + // should be a multiple of session or things will get inconsistent + type KickThreshold = Period; + type ValidatorId = ::AccountId; + type ValidatorIdOf = pallet_collator_selection::IdentityCollator; + type ValidatorRegistration = Session; + type WeightInfo = (); +} + +// Create the runtime by composing the FRAME pallets that were previously configured. +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = opaque::Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + // System support stuff. + System: frame_system::{Pallet, Call, Config, Storage, Event} = 0, + ParachainSystem: cumulus_pallet_parachain_system::{ + Pallet, Call, Config, Storage, Inherent, Event, ValidateUnsigned, + } = 1, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent} = 2, + ParachainInfo: parachain_info::{Pallet, Storage, Config} = 3, + + // Monetary stuff. + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event} = 10, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event} = 11, + + // Collator support. The order of these 4 are important and shall not change. + Authorship: pallet_authorship::{Pallet, Call, Storage} = 20, + CollatorSelection: pallet_collator_selection::{Pallet, Call, Storage, Event, Config} = 21, + Session: pallet_session::{Pallet, Call, Storage, Event, Config} = 22, + Aura: pallet_aura::{Pallet, Storage, Config} = 23, + AuraExt: cumulus_pallet_aura_ext::{Pallet, Storage, Config} = 24, + + // XCM helpers. + XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event} = 30, + PolkadotXcm: pallet_xcm::{Pallet, Call, Event, Origin, Config} = 31, + CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 32, + DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 33, + } +); + +#[cfg(feature = "runtime-benchmarks")] +#[macro_use] +extern crate frame_benchmarking; + +#[cfg(feature = "runtime-benchmarks")] +mod benches { + define_benchmarks!( + [frame_system, SystemBench::] + [pallet_balances, Balances] + [pallet_session, SessionBench::] + [pallet_timestamp, Timestamp] + [pallet_collator_selection, CollatorSelection] + [cumulus_pallet_xcmp_queue, XcmpQueue] + ); +} + +impl_runtime_apis! { + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + } + + fn authorities() -> Vec { + Aura::authorities().into_inner() + } + } + + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { + fn query_info( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment::FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } + + #[cfg(feature = "try-runtime")] + impl frame_try_runtime::TryRuntime for Runtime { + fn on_runtime_upgrade() -> (Weight, Weight) { + log::info!("try-runtime::on_runtime_upgrade parachain-template."); + let weight = Executive::try_runtime_upgrade().unwrap(); + (weight, RuntimeBlockWeights::get().max_block) + } + + fn execute_block_no_check(block: Block) -> Weight { + Executive::execute_block_no_check(block) + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata(extra: bool) -> ( + Vec, + Vec, + ) { + use frame_benchmarking::{Benchmarking, BenchmarkList}; + use frame_support::traits::StorageInfoTrait; + use frame_system_benchmarking::Pallet as SystemBench; + use cumulus_pallet_session_benchmarking::Pallet as SessionBench; + + let mut list = Vec::::new(); + list_benchmarks!(list, extra); + + let storage_info = AllPalletsWithSystem::storage_info(); + return (list, storage_info) + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{Benchmarking, BenchmarkBatch, TrackedStorageKey}; + + use frame_system_benchmarking::Pallet as SystemBench; + impl frame_system_benchmarking::Config for Runtime {} + + use cumulus_pallet_session_benchmarking::Pallet as SessionBench; + impl cumulus_pallet_session_benchmarking::Config for Runtime {} + + let whitelist: Vec = vec![ + // Block Number + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(), + // Total Issuance + hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(), + // Execution Phase + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(), + // Event Count + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(), + // System Events + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(), + ]; + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + add_benchmarks!(params, batches); + + if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } + Ok(batches) + } + } +} + +struct CheckInherents; + +impl cumulus_pallet_parachain_system::CheckInherents for CheckInherents { + fn check_inherents( + block: &Block, + relay_state_proof: &cumulus_pallet_parachain_system::RelayChainStateProof, + ) -> sp_inherents::CheckInherentsResult { + let relay_chain_slot = relay_state_proof + .read_slot() + .expect("Could not read the relay chain slot from the proof"); + + let inherent_data = + cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration( + relay_chain_slot, + sp_std::time::Duration::from_secs(6), + ) + .create_inherent_data() + .expect("Could not create the timestamp inherent data"); + + inherent_data.check_extrinsics(block) + } +} + +cumulus_pallet_parachain_system::register_validate_block! { + Runtime = Runtime, + BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, + CheckInherents = CheckInherents, +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/block_weights.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/block_weights.rs new file mode 100644 index 00000000000..4db90f0c020 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/block_weights.rs @@ -0,0 +1,46 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 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. + +pub mod constants { + use frame_support::{ + parameter_types, + weights::{constants, Weight}, + }; + + parameter_types! { + /// Importing a block with 0 Extrinsics. + pub const BlockExecutionWeight: Weight = 5_000_000 * constants::WEIGHT_PER_NANOS; + } + + #[cfg(test)] + mod test_weights { + use frame_support::weights::constants; + + /// Checks that the weight exists and is sane. + // NOTE: If this test fails but you are sure that the generated values are fine, + // you can delete it. + #[test] + fn sane() { + let w = super::constants::BlockExecutionWeight::get(); + + // At least 100 µs. + assert!(w >= 100 * constants::WEIGHT_PER_MICROS, "Weight should be at least 100 µs."); + // At most 50 ms. + assert!(w <= 50 * constants::WEIGHT_PER_MILLIS, "Weight should be at most 50 ms."); + } + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/extrinsic_weights.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/extrinsic_weights.rs new file mode 100644 index 00000000000..158ba99c6a4 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/extrinsic_weights.rs @@ -0,0 +1,46 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 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. + +pub mod constants { + use frame_support::{ + parameter_types, + weights::{constants, Weight}, + }; + + parameter_types! { + /// Executing a NO-OP `System::remarks` Extrinsic. + pub const ExtrinsicBaseWeight: Weight = 125_000 * constants::WEIGHT_PER_NANOS; + } + + #[cfg(test)] + mod test_weights { + use frame_support::weights::constants; + + /// Checks that the weight exists and is sane. + // NOTE: If this test fails but you are sure that the generated values are fine, + // you can delete it. + #[test] + fn sane() { + let w = super::constants::ExtrinsicBaseWeight::get(); + + // At least 10 µs. + assert!(w >= 10 * constants::WEIGHT_PER_MICROS, "Weight should be at least 10 µs."); + // At most 1 ms. + assert!(w <= constants::WEIGHT_PER_MILLIS, "Weight should be at most 1 ms."); + } + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs new file mode 100644 index 00000000000..ed0b4dbcd47 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs @@ -0,0 +1,28 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 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. + +//! Expose the auto generated weight files. + +pub mod block_weights; +pub mod extrinsic_weights; +pub mod paritydb_weights; +pub mod rocksdb_weights; + +pub use block_weights::constants::BlockExecutionWeight; +pub use extrinsic_weights::constants::ExtrinsicBaseWeight; +pub use paritydb_weights::constants::ParityDbWeight; +pub use rocksdb_weights::constants::RocksDbWeight; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/paritydb_weights.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/paritydb_weights.rs new file mode 100644 index 00000000000..843823c1bf3 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/paritydb_weights.rs @@ -0,0 +1,63 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 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. + +pub mod constants { + use frame_support::{ + parameter_types, + weights::{constants, RuntimeDbWeight}, + }; + + parameter_types! { + /// `ParityDB` can be enabled with a feature flag, but is still experimental. These weights + /// are available for brave runtime engineers who may want to try this out as default. + pub const ParityDbWeight: RuntimeDbWeight = RuntimeDbWeight { + read: 8_000 * constants::WEIGHT_PER_NANOS, + write: 50_000 * constants::WEIGHT_PER_NANOS, + }; + } + + #[cfg(test)] + mod test_db_weights { + use super::constants::ParityDbWeight as W; + use frame_support::weights::constants; + + /// Checks that all weights exist and have sane values. + // NOTE: If this test fails but you are sure that the generated values are fine, + // you can delete it. + #[test] + fn sane() { + // At least 1 µs. + assert!( + W::get().reads(1) >= constants::WEIGHT_PER_MICROS, + "Read weight should be at least 1 µs." + ); + assert!( + W::get().writes(1) >= constants::WEIGHT_PER_MICROS, + "Write weight should be at least 1 µs." + ); + // At most 1 ms. + assert!( + W::get().reads(1) <= constants::WEIGHT_PER_MILLIS, + "Read weight should be at most 1 ms." + ); + assert!( + W::get().writes(1) <= constants::WEIGHT_PER_MILLIS, + "Write weight should be at most 1 ms." + ); + } + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/rocksdb_weights.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/rocksdb_weights.rs new file mode 100644 index 00000000000..05e06b0eabe --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/rocksdb_weights.rs @@ -0,0 +1,63 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 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. + +pub mod constants { + use frame_support::{ + parameter_types, + weights::{constants, RuntimeDbWeight}, + }; + + parameter_types! { + /// By default, Substrate uses `RocksDB`, so this will be the weight used throughout + /// the runtime. + pub const RocksDbWeight: RuntimeDbWeight = RuntimeDbWeight { + read: 25_000 * constants::WEIGHT_PER_NANOS, + write: 100_000 * constants::WEIGHT_PER_NANOS, + }; + } + + #[cfg(test)] + mod test_db_weights { + use super::constants::RocksDbWeight as W; + use frame_support::weights::constants; + + /// Checks that all weights exist and have sane values. + // NOTE: If this test fails but you are sure that the generated values are fine, + // you can delete it. + #[test] + fn sane() { + // At least 1 µs. + assert!( + W::get().reads(1) >= constants::WEIGHT_PER_MICROS, + "Read weight should be at least 1 µs." + ); + assert!( + W::get().writes(1) >= constants::WEIGHT_PER_MICROS, + "Write weight should be at least 1 µs." + ); + // At most 1 ms. + assert!( + W::get().reads(1) <= constants::WEIGHT_PER_MILLIS, + "Read weight should be at most 1 ms." + ); + assert!( + W::get().writes(1) <= constants::WEIGHT_PER_MILLIS, + "Write weight should be at most 1 ms." + ); + } + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs new file mode 100644 index 00000000000..2ec84d18871 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -0,0 +1,221 @@ +use super::{ + AccountId, Balances, Call, Event, Origin, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, + WeightToFee, XcmpQueue, +}; +use core::marker::PhantomData; +use frame_support::{ + log, match_types, parameter_types, + traits::{Everything, Nothing}, + weights::Weight, +}; +use pallet_xcm::XcmPassthrough; +use polkadot_parachain::primitives::Sibling; +use polkadot_runtime_common::impls::ToAuthor; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, + EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + UsingComponents, +}; +use xcm_executor::{traits::ShouldExecute, XcmExecutor}; + +parameter_types! { + pub const RelayLocation: MultiLocation = MultiLocation::parent(); + pub const RelayNetwork: NetworkId = NetworkId::Any; + pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); + pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); +} + +/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the parent `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, +); + +/// Means for transacting assets on this chain. +pub type LocalAssetTransactor = CurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We don't track any teleports. + (), +>; + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognized. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognized. + SiblingParachainAsNative, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `Origin::Signed` origin of the same 32-byte value. + SignedAccountId32AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + XcmPassthrough, +); + +parameter_types! { + // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. + pub UnitWeightCost: Weight = 1_000_000_000; + pub const MaxInstructions: u32 = 100; +} + +match_types! { + pub type ParentOrParentsExecutivePlurality: impl Contains = { + MultiLocation { parents: 1, interior: Here } | + MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Executive, .. }) } + }; +} + +//TODO: move DenyThenTry to polkadot's xcm module. +/// Deny executing the xcm message if it matches any of the Deny filter regardless of anything else. +/// If it passes the Deny, and matches one of the Allow cases then it is let through. +pub struct DenyThenTry(PhantomData, PhantomData) +where + Deny: ShouldExecute, + Allow: ShouldExecute; + +impl ShouldExecute for DenyThenTry +where + Deny: ShouldExecute, + Allow: ShouldExecute, +{ + fn should_execute( + origin: &MultiLocation, + message: &mut Xcm, + max_weight: Weight, + weight_credit: &mut Weight, + ) -> Result<(), ()> { + Deny::should_execute(origin, message, max_weight, weight_credit)?; + Allow::should_execute(origin, message, max_weight, weight_credit) + } +} + +// See issue #5233 +pub struct DenyReserveTransferToRelayChain; +impl ShouldExecute for DenyReserveTransferToRelayChain { + fn should_execute( + origin: &MultiLocation, + message: &mut Xcm, + _max_weight: Weight, + _weight_credit: &mut Weight, + ) -> Result<(), ()> { + if message.0.iter().any(|inst| { + matches!( + inst, + InitiateReserveWithdraw { + reserve: MultiLocation { parents: 1, interior: Here }, + .. + } | DepositReserveAsset { dest: MultiLocation { parents: 1, interior: Here }, .. } | + TransferReserveAsset { + dest: MultiLocation { parents: 1, interior: Here }, + .. + } + ) + }) { + return Err(()) // Deny + } + + // An unexpected reserve transfer has arrived from the Relay Chain. Generally, `IsReserve` + // should not allow this, but we just log it here. + if matches!(origin, MultiLocation { parents: 1, interior: Here }) && + message.0.iter().any(|inst| matches!(inst, ReserveAssetDeposited { .. })) + { + log::warn!( + target: "xcm::barriers", + "Unexpected ReserveAssetDeposited from the Relay Chain", + ); + } + // Permit everything else + Ok(()) + } +} + +pub type Barrier = DenyThenTry< + DenyReserveTransferToRelayChain, + ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + AllowUnpaidExecutionFrom, + // ^^^ Parent and its exec plurality get free execution + ), +>; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type Call = Call; + type XcmSender = XcmRouter; + // How to withdraw and deposit an asset. + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = NativeAsset; + type IsTeleporter = (); // Teleporting is disabled. + type LocationInverter = LocationInverter; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = + UsingComponents>; + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; +} + +/// No local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = SignedToAccountId32; + +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = ( + // Two routers - use UMP to communicate with the relay chain: + cumulus_primitives_utility::ParentAsUmp, + // ..and XCMP to communicate with the sibling chains. + XcmpQueue, +); + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + // ^ Disable dispatchable execute on the XCM pallet. + // Needs to be `Everything` for local testing. + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Nothing; + type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; + type Origin = Origin; + type Call = Call; + + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + // ^ Override for AdvertisedXcmVersion default + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; +} diff --git a/polkadot-parachain/Cargo.toml b/polkadot-parachain/Cargo.toml index 2d1a51f0967..7e9d931b680 100644 --- a/polkadot-parachain/Cargo.toml +++ b/polkadot-parachain/Cargo.toml @@ -24,6 +24,7 @@ statemine-runtime = { path = "../parachains/runtimes/assets/statemine" } westmint-runtime = { path = "../parachains/runtimes/assets/westmint" } collectives-polkadot-runtime = { path = "../parachains/runtimes/collectives/collectives-polkadot" } contracts-rococo-runtime = { path = "../parachains/runtimes/contracts/contracts-rococo" } +bridge-hub-rococo-runtime = { path = "../parachains/runtimes/bridge-hubs/bridge-hub-rococo" } penpal-runtime = { path = "../parachains/runtimes/testing/penpal" } jsonrpsee = { version = "0.15.1", features = ["server"] } parachains-common = { path = "../parachains/common" } diff --git a/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/polkadot-parachain/src/chain_spec/bridge_hubs.rs new file mode 100644 index 00000000000..9091fb82c88 --- /dev/null +++ b/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -0,0 +1,182 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +use polkadot_service::ParaId; +use sc_chain_spec::ChainSpec; +use sc_cli::RuntimeVersion; +use std::{path::PathBuf, str::FromStr}; + +/// Collects all supported BridgeHub configurations +pub enum BridgeHubRuntimeType { + RococoLocal, +} + +impl FromStr for BridgeHubRuntimeType { + type Err = String; + + fn from_str(value: &str) -> Result { + match value { + rococo::BRIDGE_HUB_ROCOCO_LOCAL => Ok(BridgeHubRuntimeType::RococoLocal), + _ => Err(format!("Value '{}' is not configured yet", value)), + } + } +} + +impl BridgeHubRuntimeType { + pub const ID_PREFIX: &'static str = "bridge-hub"; + + pub fn chain_spec_from_json_file(&self, path: PathBuf) -> Result, String> { + Ok(Box::new(match self { + BridgeHubRuntimeType::RococoLocal => rococo::BridgeHubChainSpec::from_json_file(path)?, + })) + } + + pub fn load_config(&self) -> Box { + Box::new(match self { + BridgeHubRuntimeType::RococoLocal => + rococo::local_config("rococo-local", ParaId::new(1013)), + }) + } + + pub fn runtime_version(&self) -> &'static RuntimeVersion { + match self { + BridgeHubRuntimeType::RococoLocal => &bridge_hub_rococo_runtime::VERSION, + } + } +} + +/// Check if 'id' satisfy BridgeHub-like format +fn ensure_id(id: &str) -> Result<&str, String> { + if id.starts_with(BridgeHubRuntimeType::ID_PREFIX) { + Ok(id) + } else { + Err(format!( + "Invalid 'id' attribute ({}), should start with prefix: {}", + id, + BridgeHubRuntimeType::ID_PREFIX + )) + } +} + +pub mod rococo { + use crate::chain_spec::{ + get_account_id_from_seed, get_collator_keys_from_seed, Extensions, SAFE_XCM_VERSION, + }; + use bridge_hub_rococo_runtime::{AccountId, AuraId}; + use cumulus_primitives_core::ParaId; + use sc_chain_spec::ChainType; + use sp_core::sr25519; + + pub const BRIDGE_HUB_ROCOCO_LOCAL: &str = "bridge-hub-rococo-local"; + + /// Specialized `ChainSpec` for the normal parachain runtime. + pub type BridgeHubChainSpec = + sc_service::GenericChainSpec; + + pub fn local_config(relay_chain: &str, para_id: ParaId) -> BridgeHubChainSpec { + let properties = sc_chain_spec::Properties::new(); + // TODO: check + // properties.insert("ss58Format".into(), 2.into()); + // properties.insert("tokenSymbol".into(), "ROC".into()); + // properties.insert("tokenDecimals".into(), 12.into()); + + BridgeHubChainSpec::from_genesis( + // Name + "Rococo BrideHub Local", + // ID + super::ensure_id(BRIDGE_HUB_ROCOCO_LOCAL).expect("invalid id"), + ChainType::Local, + move || { + genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + para_id, + ) + }, + Vec::new(), + None, + None, + None, + Some(properties), + Extensions { relay_chain: relay_chain.to_string(), para_id: para_id.into() }, + ) + } + + fn genesis( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec, + id: ParaId, + ) -> bridge_hub_rococo_runtime::GenesisConfig { + bridge_hub_rococo_runtime::GenesisConfig { + system: bridge_hub_rococo_runtime::SystemConfig { + code: bridge_hub_rococo_runtime::WASM_BINARY + .expect("WASM binary was not build, please build it!") + .to_vec(), + }, + balances: bridge_hub_rococo_runtime::BalancesConfig { + balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), + }, + parachain_info: bridge_hub_rococo_runtime::ParachainInfoConfig { parachain_id: id }, + collator_selection: bridge_hub_rococo_runtime::CollatorSelectionConfig { + invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), + // TODO: check + candidacy_bond: 10, + ..Default::default() + }, + session: bridge_hub_rococo_runtime::SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + bridge_hub_rococo_runtime::SessionKeys { aura }, // session keys + ) + }) + .collect(), + }, + aura: Default::default(), + aura_ext: Default::default(), + parachain_system: Default::default(), + polkadot_xcm: bridge_hub_rococo_runtime::PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + }, + } + } +} diff --git a/polkadot-parachain/src/chain_spec/mod.rs b/polkadot-parachain/src/chain_spec/mod.rs index 30cff43b57c..3637e915406 100644 --- a/polkadot-parachain/src/chain_spec/mod.rs +++ b/polkadot-parachain/src/chain_spec/mod.rs @@ -20,6 +20,7 @@ use serde::{Deserialize, Serialize}; use sp_core::{Pair, Public}; use sp_runtime::traits::{IdentifyAccount, Verify}; +pub mod bridge_hubs; pub mod collectives; pub mod contracts; pub mod penpal; diff --git a/polkadot-parachain/src/command.rs b/polkadot-parachain/src/command.rs index c436a98dc69..55c6400f55a 100644 --- a/polkadot-parachain/src/command.rs +++ b/polkadot-parachain/src/command.rs @@ -53,6 +53,7 @@ enum Runtime { ContractsRococo, CollectivesPolkadot, CollectivesWestend, + BridgeHub(chain_spec::bridge_hubs::BridgeHubRuntimeType), } trait RuntimeResolver { @@ -104,6 +105,11 @@ fn runtime(id: &str) -> Runtime { Runtime::CollectivesPolkadot } else if id.starts_with("collectives-westend") { Runtime::CollectivesWestend + } else if id.starts_with(chain_spec::bridge_hubs::BridgeHubRuntimeType::ID_PREFIX) { + Runtime::BridgeHub( + id.parse::() + .expect("Invalid value"), + ) } else { log::warn!("No specific runtime was recognized for ChainSpec's id: '{}', so Runtime::default() will be used", id); Runtime::default() @@ -188,6 +194,15 @@ fn load_spec(id: &str) -> std::result::Result, String> { &include_bytes!("../../parachains/chain-specs/contracts-rococo.json")[..], )?), + // -- BridgeHub + bridge_like_id + if bridge_like_id + .starts_with(chain_spec::bridge_hubs::BridgeHubRuntimeType::ID_PREFIX) => + bridge_like_id + .parse::() + .expect("invalid value") + .load_config(), + // -- Penpall "penpal-kusama" => Box::new(chain_spec::penpal::get_penpal_chain_spec( para_id.expect("Must specify parachain id"), @@ -223,6 +238,8 @@ fn load_spec(id: &str) -> std::result::Result, String> { Box::new(chain_spec::seedling::SeedlingChainSpec::from_json_file(path)?), Runtime::ContractsRococo => Box::new(chain_spec::contracts::ContractsRococoChainSpec::from_json_file(path)?), + Runtime::BridgeHub(bridge_hub_runtime_type) => + bridge_hub_runtime_type.chain_spec_from_json_file(path.into())?, Runtime::Penpal(_para_id) => Box::new(chain_spec::penpal::PenpalChainSpec::from_json_file(path)?), Runtime::Default => Box::new( @@ -300,6 +317,8 @@ impl SubstrateCli for Cli { Runtime::Shell => &shell_runtime::VERSION, Runtime::Seedling => &seedling_runtime::VERSION, Runtime::ContractsRococo => &contracts_rococo_runtime::VERSION, + Runtime::BridgeHub(bridge_hub_runtime_type) => + bridge_hub_runtime_type.runtime_version(), Runtime::Penpal(_) => &penpal_runtime::VERSION, Runtime::Default => &rococo_parachain_runtime::VERSION, } @@ -457,6 +476,19 @@ macro_rules! construct_async_run { { $( $code )* }.map(|v| (v, task_manager)) }) }, + Runtime::BridgeHub(bridge_hub_runtime_type) => { + runner.async_run(|$config| { + let $components = match bridge_hub_runtime_type { + chain_spec::bridge_hubs::BridgeHubRuntimeType::RococoLocal => new_partial::( + &$config, + crate::service::aura_build_import_queue::<_, AuraId>, + )?, + }; + + let task_manager = $components.task_manager; + { $( $code )* }.map(|v| (v, task_manager)) + }) + }, Runtime::Penpal(_) | Runtime::Default => { runner.async_run(|$config| { let $components = new_partial::< @@ -740,6 +772,16 @@ pub fn run() -> Result<()> { .await .map(|r| r.0) .map_err(Into::into), + Runtime::BridgeHub(bridge_hub_runtime_type) => match bridge_hub_runtime_type { + chain_spec::bridge_hubs::BridgeHubRuntimeType::RococoLocal => + crate::service::start_generic_aura_node::< + bridge_hub_rococo_runtime::RuntimeApi, + AuraId, + >(config, polkadot_config, collator_options, id, hwbench), + } + .await + .map(|r| r.0) + .map_err(Into::into), Runtime::Penpal(_) | Runtime::Default => crate::service::start_rococo_parachain_node( config, From 170c908a9f44529f7e806d5b65a7ce3290d04cce Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 2 Aug 2022 11:54:35 +0200 Subject: [PATCH 060/263] [BridgeHub] Setup wococo bridge grandpa/parachain pallets --- Cargo.lock | 283 ++++++++++++++++++ Cargo.toml | 9 + README.md | 4 + parachains/runtimes/bridge-hubs/README.md | 18 ++ .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 10 + .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 48 ++- .../bridge-hub-rococo/src/xcm_config.rs | 241 +++++++++------ 7 files changed, 525 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 74fe22a8dcd..8edcc339e66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -713,10 +713,288 @@ dependencies = [ "thiserror", ] +[[package]] +name = "bp-header-chain" +version = "0.1.0" +dependencies = [ + "assert_matches", + "bp-runtime 0.1.0", + "bp-test-utils 0.1.0", + "finality-grandpa", + "frame-support", + "hex", + "hex-literal", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-finality-grandpa", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-header-chain" +version = "0.1.0" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" +dependencies = [ + "bp-runtime 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "finality-grandpa", + "frame-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-finality-grandpa", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-message-dispatch" +version = "0.1.0" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" +dependencies = [ + "bp-runtime 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "frame-support", + "parity-scale-codec", + "scale-info", + "sp-std", +] + +[[package]] +name = "bp-messages" +version = "0.1.0" +dependencies = [ + "bitvec", + "bp-runtime 0.1.0", + "frame-support", + "frame-system", + "hex", + "hex-literal", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-std", +] + +[[package]] +name = "bp-messages" +version = "0.1.0" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" +dependencies = [ + "bitvec", + "bp-runtime 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-std", +] + +[[package]] +name = "bp-parachains" +version = "0.1.0" +dependencies = [ + "bp-polkadot-core 0.1.0", + "bp-runtime 0.1.0", + "frame-support", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", +] + +[[package]] +name = "bp-polkadot-core" +version = "0.1.0" +dependencies = [ + "bp-messages 0.1.0", + "bp-runtime 0.1.0", + "frame-support", + "frame-system", + "hex", + "parity-scale-codec", + "parity-util-mem", + "scale-info", + "serde", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std", + "sp-version", +] + +[[package]] +name = "bp-polkadot-core" +version = "0.1.0" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" +dependencies = [ + "bp-messages 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "bp-runtime 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std", + "sp-version", +] + +[[package]] +name = "bp-relayers" +version = "0.1.0" +dependencies = [ + "frame-support", + "hex", + "hex-literal", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-rococo" +version = "0.1.0" +dependencies = [ + "bp-messages 0.1.0", + "bp-polkadot-core 0.1.0", + "bp-runtime 0.1.0", + "frame-support", + "parity-scale-codec", + "smallvec", + "sp-api", + "sp-runtime", + "sp-std", + "sp-version", +] + +[[package]] +name = "bp-rococo" +version = "0.1.0" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" +dependencies = [ + "bp-messages 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "bp-polkadot-core 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "bp-runtime 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "frame-support", + "parity-scale-codec", + "smallvec", + "sp-api", + "sp-runtime", + "sp-std", + "sp-version", +] + +[[package]] +name = "bp-runtime" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "hash-db", + "hex-literal", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", +] + +[[package]] +name = "bp-runtime" +version = "0.1.0" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" +dependencies = [ + "frame-support", + "hash-db", + "num-traits", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", +] + +[[package]] +name = "bp-test-utils" +version = "0.1.0" +dependencies = [ + "bp-header-chain 0.1.0", + "ed25519-dalek", + "finality-grandpa", + "parity-scale-codec", + "sp-application-crypto", + "sp-finality-grandpa", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-test-utils" +version = "0.1.0" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" +dependencies = [ + "bp-header-chain 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "ed25519-dalek", + "finality-grandpa", + "parity-scale-codec", + "sp-application-crypto", + "sp-finality-grandpa", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-wococo" +version = "0.1.0" +dependencies = [ + "bp-messages 0.1.0", + "bp-polkadot-core 0.1.0", + "bp-rococo 0.1.0", + "bp-runtime 0.1.0", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-wococo" +version = "0.1.0" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" +dependencies = [ + "bp-messages 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "bp-polkadot-core 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "bp-rococo 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "bp-runtime 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-std", +] + [[package]] name = "bridge-hub-rococo-runtime" version = "0.1.0" dependencies = [ + "bp-polkadot-core 0.1.0", + "bp-rococo 0.1.0", + "bp-wococo 0.1.0", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -9013,6 +9291,11 @@ source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm- dependencies = [ "beefy-merkle-tree", "beefy-primitives", + "bp-messages 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "bp-rococo 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "bp-runtime 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "bp-wococo 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "bridge-runtime-common", "frame-benchmarking", "frame-executive", "frame-support", diff --git a/Cargo.toml b/Cargo.toml index b2d34b8e3f3..4ba7acb605c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,14 @@ [workspace] members = [ + "bridges/modules/grandpa", + "bridges/modules/messages", + "bridges/modules/parachains", + "bridges/modules/relayers", + "bridges/modules/shift-session-manager", + "bridges/primitives/polkadot-core", + "bridges/primitives/runtime", + "bridges/primitives/chain-rococo", + "bridges/primitives/chain-wococo", "client/cli", "client/consensus/aura", "client/consensus/common", diff --git a/README.md b/README.md index 7ac6ca2e518..852b493ed61 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,10 @@ See [the `contracts-rococo` readme](parachains/runtimes/contracts/contracts-roco See [the `bridge-hubs` readme](parachains/runtimes/bridge-hubs/README.md) for details. +**_Important:_** + +BridgeHub stuff uses external dependencies from repo `https://github.com/paritytech/parity-bridges-common.git`, which are mirrored to `./bridges` directory with `git subtree` feature. + ## Rococo 👑 [Rococo](https://polkadot.js.org/apps/?rpc=wss://rococo-rpc.polkadot.io) is becoming a [Community Parachain Testbed](https://polkadot.network/blog/rococo-revamp-becoming-a-community-parachain-testbed/) for parachain teams in the Polkadot ecosystem. It supports multiple parachains with the differentiation of long-term connections and recurring short-term connections, to see which parachains are currently connected and how long they will be connected for [see here](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frococo-rpc.polkadot.io#/parachains). diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 9f89d42e2d2..30832db75cb 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -38,6 +38,7 @@ ls -lrt ~/local_bridge_testing/bin/polkadot-parachain ### Run Rococo BridgeHub parachain #### Generate spec + genesis + wasm (paraId=1013) ``` +rm ~/local_bridge_testing/bridge-hub-rococo-local-raw.json ~/local_bridge_testing/bin/polkadot-parachain build-spec --chain bridge-hub-rococo-local --raw --disable-default-bootnode > ~/local_bridge_testing/bridge-hub-rococo-local-raw.json ~/local_bridge_testing/bin/polkadot-parachain export-genesis-state --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json > ~/local_bridge_testing/bridge-hub-rococo-local-genesis ~/local_bridge_testing/bin/polkadot-parachain export-genesis-wasm --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json > ~/local_bridge_testing/bridge-hub-rococo-local-genesis-wasm @@ -63,3 +64,20 @@ https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/explorer ``` https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/parachains ``` + + +## Git subtree `./bridges` + +Add Bridges repo as a local remote and synchronize it with latest `master` from bridges repo: +``` +git remote add -f bridges git@github.com:paritytech/parity-bridges-common.git +# (ran just only first time, when subtree was initialized) +# git subtree add --prefix=bridges bridges master --squash +git subtree pull --prefix=bridges bridges master --squash +```` +We use `--squash` to avoid adding individual commits and rather squashing them +all into one. + +Now we use `master` branch, but in future, it could change to some release branch/tag. + +Original `./bridges/Cargo.toml` was renamed to `./bridges/Cargo.toml_removed_for_bridges_subtree_feature` to avoid confusion for `Cargo` having multiple workspaces. diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 2bf9f9867cf..4ebdf070607 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -66,6 +66,16 @@ cumulus-primitives-utility = { path = "../../../../primitives/utility", default- pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false } +# Bridges +bp-polkadot-core = { path = "../../../../bridges/primitives/polkadot-core", default-features = false } +bp-rococo = { path = "../../../../bridges/primitives/chain-rococo", default-features = false } +bp-wococo = { path = "../../../../bridges/primitives/chain-wococo", default-features = false } +pallet-bridge-grandpa = { path = "../../../../bridges/modules/grandpa", default-features = false } +pallet-bridge-messages = { path = "../../../../bridges/modules/messages", default-features = false } +pallet-bridge-parachains = { path = "../../../../bridges/modules/parachains", default-features = false } +pallet-bridge-relayers = { path = "../../../../bridges/modules/relayers", default-features = false } +pallet-shift-session-manager = { path = "../../../../bridges/modules/shift-session-manager", default-features = false } + [features] default = [ "std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 9076081c28c..7fe076ee297 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -50,6 +50,7 @@ use frame_support::{ }, PalletId, }; +use frame_support::traits::IsInVec; use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, @@ -58,6 +59,8 @@ pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; use xcm_config::{XcmConfig, XcmOriginToTransactDispatchOrigin}; +use bp_polkadot_core::parachains::ParaId; + #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -114,6 +117,8 @@ pub type SignedExtra = ( frame_system::CheckNonce, frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, + // TODO: do we need? + // BridgeRejectObsoleteHeadersAndMessages, ); /// Unchecked extrinsic type as expected by this runtime. @@ -367,7 +372,8 @@ parameter_types! { } impl pallet_transaction_payment::Config for Runtime { - type Event = Event; + // TODO: hacked + // type Event = Event; type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; @@ -405,6 +411,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = (); + type PriceForSiblingDelivery = (); } impl cumulus_pallet_dmp_queue::Config for Runtime { @@ -467,6 +474,37 @@ impl pallet_collator_selection::Config for Runtime { type WeightInfo = (); } +/// Add bridge pallets (GPA) +parameter_types! { + pub const MaxRequests: u32 = 50; + pub const HeadersToKeep: u32 = 1024; +} + +/// Add granda bridge pallet to track Wococo relay chain +pub type WococoGrandpaInstance = (); +impl pallet_bridge_grandpa::Config for Runtime { + type BridgedChain = bp_wococo::Wococo; + type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; + type WeightInfo = (); +} + +pub const PARAS_PALLET_NAME: &str = "WococoBridgeHubParachain"; +parameter_types! { + pub const ParachainHeadsToKeep: u32 = 50; + pub const ParasPalletName: &'static str = PARAS_PALLET_NAME; + pub GetTenFirstParachains: Vec = (0..10).map(ParaId).collect(); +} + +/// Add parachain bridge pallet to track Wococo bridge hub parachain +impl pallet_bridge_parachains::Config for Runtime { + type WeightInfo = (); + type BridgesGrandpaPalletInstance = WococoGrandpaInstance; + type ParasPalletName = ParasPalletName; + type TrackedParachains = IsInVec; + type HeadsToKeep = ParachainHeadsToKeep; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime where @@ -484,7 +522,9 @@ construct_runtime!( // Monetary stuff. Balances: pallet_balances::{Pallet, Call, Storage, Config, Event} = 10, - TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event} = 11, + // TODO: hacked + // TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event} = 11, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage} = 11, // Collator support. The order of these 4 are important and shall not change. Authorship: pallet_authorship::{Pallet, Call, Storage} = 20, @@ -498,6 +538,10 @@ construct_runtime!( PolkadotXcm: pallet_xcm::{Pallet, Call, Event, Origin, Config} = 31, CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 32, DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 33, + + // Bridge pallets + BridgeWococoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, + BridgeWococoParachains: pallet_bridge_parachains::{Pallet, Call, Storage}, } ); diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 2ec84d18871..28a8600d2ea 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -1,5 +1,5 @@ use super::{ - AccountId, Balances, Call, Event, Origin, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, + AccountId, Balance, Balances, Call, Event, Origin, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, WeightToFee, XcmpQueue, }; use core::marker::PhantomData; @@ -8,13 +8,14 @@ use frame_support::{ traits::{Everything, Nothing}, weights::Weight, }; +use frame_support::weights::IdentityFee; use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use polkadot_runtime_common::impls::ToAuthor; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, - EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset, ParentIsPreset, + EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, @@ -23,9 +24,11 @@ use xcm_executor::{traits::ShouldExecute, XcmExecutor}; parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Any; + // TODO: hack: hardcoded Polkadot? + pub const RelayNetwork: NetworkId = NetworkId::Rococo; pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); + pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used @@ -79,6 +82,7 @@ parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. pub UnitWeightCost: Weight = 1_000_000_000; pub const MaxInstructions: u32 = 100; + pub MaxAssetsIntoHolding: u32 = 64; } match_types! { @@ -89,97 +93,140 @@ match_types! { } //TODO: move DenyThenTry to polkadot's xcm module. -/// Deny executing the xcm message if it matches any of the Deny filter regardless of anything else. -/// If it passes the Deny, and matches one of the Allow cases then it is let through. -pub struct DenyThenTry(PhantomData, PhantomData) -where - Deny: ShouldExecute, - Allow: ShouldExecute; - -impl ShouldExecute for DenyThenTry -where - Deny: ShouldExecute, - Allow: ShouldExecute, -{ - fn should_execute( - origin: &MultiLocation, - message: &mut Xcm, - max_weight: Weight, - weight_credit: &mut Weight, - ) -> Result<(), ()> { - Deny::should_execute(origin, message, max_weight, weight_credit)?; - Allow::should_execute(origin, message, max_weight, weight_credit) - } -} +// /// Deny executing the xcm message if it matches any of the Deny filter regardless of anything else. +// /// If it passes the Deny, and matches one of the Allow cases then it is let through. +// pub struct DenyThenTry(PhantomData, PhantomData) +// where +// Deny: ShouldExecute, +// Allow: ShouldExecute; +// +// impl ShouldExecute for DenyThenTry +// where +// Deny: ShouldExecute, +// Allow: ShouldExecute, +// { +// fn should_execute( +// origin: &MultiLocation, +// message: &mut Xcm, +// max_weight: Weight, +// weight_credit: &mut Weight, +// ) -> Result<(), ()> { +// Deny::should_execute(origin, message, max_weight, weight_credit)?; +// Allow::should_execute(origin, message, max_weight, weight_credit) +// } +// } +// TODO: hacked // See issue #5233 -pub struct DenyReserveTransferToRelayChain; -impl ShouldExecute for DenyReserveTransferToRelayChain { - fn should_execute( - origin: &MultiLocation, - message: &mut Xcm, - _max_weight: Weight, - _weight_credit: &mut Weight, - ) -> Result<(), ()> { - if message.0.iter().any(|inst| { - matches!( - inst, - InitiateReserveWithdraw { - reserve: MultiLocation { parents: 1, interior: Here }, - .. - } | DepositReserveAsset { dest: MultiLocation { parents: 1, interior: Here }, .. } | - TransferReserveAsset { - dest: MultiLocation { parents: 1, interior: Here }, - .. - } - ) - }) { - return Err(()) // Deny - } - - // An unexpected reserve transfer has arrived from the Relay Chain. Generally, `IsReserve` - // should not allow this, but we just log it here. - if matches!(origin, MultiLocation { parents: 1, interior: Here }) && - message.0.iter().any(|inst| matches!(inst, ReserveAssetDeposited { .. })) - { - log::warn!( - target: "xcm::barriers", - "Unexpected ReserveAssetDeposited from the Relay Chain", - ); - } - // Permit everything else - Ok(()) - } +// pub struct DenyReserveTransferToRelayChain; +// impl ShouldExecute for DenyReserveTransferToRelayChain { +// fn should_execute( +// origin: &MultiLocation, +// message: &mut Xcm, +// _max_weight: Weight, +// _weight_credit: &mut Weight, +// ) -> Result<(), ()> { +// if message.0.iter().any(|inst| { +// matches!( +// inst, +// InitiateReserveWithdraw { +// reserve: MultiLocation { parents: 1, interior: Here }, +// .. +// } | DepositReserveAsset { dest: MultiLocation { parents: 1, interior: Here }, .. } | +// TransferReserveAsset { +// dest: MultiLocation { parents: 1, interior: Here }, +// .. +// } +// ) +// }) { +// return Err(()) // Deny +// } +// +// // An unexpected reserve transfer has arrived from the Relay Chain. Generally, `IsReserve` +// // should not allow this, but we just log it here. +// if matches!(origin, MultiLocation { parents: 1, interior: Here }) && +// message.0.iter().any(|inst| matches!(inst, ReserveAssetDeposited { .. })) +// { +// log::warn!( +// target: "xcm::barriers", +// "Unexpected ReserveAssetDeposited from the Relay Chain", +// ); +// } +// // Permit everything else +// Ok(()) +// } +// } + +match_types! { + pub type ParentOrParentsUnitPlurality: impl Contains = { + MultiLocation { parents: 1, interior: Here } | + MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Unit, .. }) } + }; } -pub type Barrier = DenyThenTry< - DenyReserveTransferToRelayChain, - ( - TakeWeightCredit, - AllowTopLevelPaidExecutionFrom, - AllowUnpaidExecutionFrom, - // ^^^ Parent and its exec plurality get free execution - ), ->; +// TOOD: hacked +// pub type Barrier = DenyThenTry< +// DenyReserveTransferToRelayChain, +// ( +// TakeWeightCredit, +// AllowTopLevelPaidExecutionFrom, +// AllowUnpaidExecutionFrom, +// // ^^^ Parent and its exec plurality get free execution +// ), +// >; +pub type Barrier = ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + AllowUnpaidExecutionFrom, + // ^^^ Parent & its unit plurality gets free execution +); +/// XCM weigher type. +pub type XcmWeigher = FixedWeightBounds; + +// TODO: hacked +// pub struct XcmConfig; +// impl xcm_executor::Config for XcmConfig { +// type Call = Call; +// type XcmSender = XcmRouter; +// // How to withdraw and deposit an asset. +// type AssetTransactor = LocalAssetTransactor; +// type OriginConverter = XcmOriginToTransactDispatchOrigin; +// type IsReserve = NativeAsset; +// type IsTeleporter = (); // Teleporting is disabled. +// type LocationInverter = LocationInverter; +// type Barrier = Barrier; +// type Weigher = FixedWeightBounds; +// type Trader = +// UsingComponents>; +// type ResponseHandler = PolkadotXcm; +// type AssetTrap = PolkadotXcm; +// type AssetClaims = PolkadotXcm; +// type SubscriptionService = PolkadotXcm; +// } pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type Call = Call; type XcmSender = XcmRouter; - // How to withdraw and deposit an asset. type AssetTransactor = LocalAssetTransactor; type OriginConverter = XcmOriginToTransactDispatchOrigin; type IsReserve = NativeAsset; - type IsTeleporter = (); // Teleporting is disabled. - type LocationInverter = LocationInverter; + type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of UNIT + type UniversalLocation = UniversalLocation; type Barrier = Barrier; - type Weigher = FixedWeightBounds; - type Trader = - UsingComponents>; + type Weigher = XcmWeigher; + type Trader = UsingComponents, RelayLocation, AccountId, Balances, ()>; type ResponseHandler = PolkadotXcm; type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. @@ -189,30 +236,52 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); +// TODO: hacked +// impl pallet_xcm::Config for Runtime { +// type Event = Event; +// type SendXcmOrigin = EnsureXcmOrigin; +// type XcmRouter = XcmRouter; +// type ExecuteXcmOrigin = EnsureXcmOrigin; +// type XcmExecuteFilter = Nothing; +// // ^ Disable dispatchable execute on the XCM pallet. +// // Needs to be `Everything` for local testing. +// type XcmExecutor = XcmExecutor; +// type XcmTeleportFilter = Everything; +// type XcmReserveTransferFilter = Nothing; +// type Weigher = FixedWeightBounds; +// type LocationInverter = LocationInverter; +// type Origin = Origin; +// type Call = Call; +// +// const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; +// // ^ Override for AdvertisedXcmVersion default +// type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; +// } impl pallet_xcm::Config for Runtime { type Event = Event; type SendXcmOrigin = EnsureXcmOrigin; type XcmRouter = XcmRouter; type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = Nothing; - // ^ Disable dispatchable execute on the XCM pallet. - // Needs to be `Everything` for local testing. + type XcmExecuteFilter = Everything; type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; - type XcmReserveTransferFilter = Nothing; - type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type XcmReserveTransferFilter = Everything; + type Weigher = XcmWeigher; type Origin = Origin; type Call = Call; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - // ^ Override for AdvertisedXcmVersion default type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = frame_support::traits::ConstU32<8>; + type UniversalLocation = UniversalLocation; } impl cumulus_pallet_xcm::Config for Runtime { From 2def98a1ef3330cebc8f0a759687550a188f7408 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 2 Aug 2022 15:53:09 +0200 Subject: [PATCH 061/263] [BridgeHub] Setup both rococo/wococo bridge grandpa/parachain pallets --- README.md | 2 + parachains/runtimes/bridge-hubs/README.md | 5 ++ .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 58 ++++++++++++++---- .../bridge-hub-rococo/src/xcm_config.rs | 8 +-- .../docs/bridge-hub-parachain-design.jpg | Bin 0 -> 88324 bytes 5 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 parachains/runtimes/bridge-hubs/docs/bridge-hub-parachain-design.jpg diff --git a/README.md b/README.md index 852b493ed61..6eb828f5b5f 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,8 @@ See [the `bridge-hubs` readme](parachains/runtimes/bridge-hubs/README.md) for de BridgeHub stuff uses external dependencies from repo `https://github.com/paritytech/parity-bridges-common.git`, which are mirrored to `./bridges` directory with `git subtree` feature. +See [readme](parachains/runtimes/bridge-hubs/README.md#git-subtree-bridges) for details + ## Rococo 👑 [Rococo](https://polkadot.js.org/apps/?rpc=wss://rococo-rpc.polkadot.io) is becoming a [Community Parachain Testbed](https://polkadot.network/blog/rococo-revamp-becoming-a-community-parachain-testbed/) for parachain teams in the Polkadot ecosystem. It supports multiple parachains with the differentiation of long-term connections and recurring short-term connections, to see which parachains are currently connected and how long they will be connected for [see here](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frococo-rpc.polkadot.io#/parachains). diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 30832db75cb..d6c5b20f500 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -12,6 +12,8 @@ Every _BridgeHub_ is meant to be **_common good parachain_** with main responsib - sync finality proofs between BridgeHub parachains - pass (XCM) messages between different BridgeHub parachains +![](/home/bparity/parity/cumulus/cumulus/parachains/runtimes/bridge-hubs/docs/bridge-hub-parachain-design.jpg "Basic deployment setup") + ## How to test locally Rococo <-> Wococo ### Deploy @@ -73,6 +75,9 @@ Add Bridges repo as a local remote and synchronize it with latest `master` from git remote add -f bridges git@github.com:paritytech/parity-bridges-common.git # (ran just only first time, when subtree was initialized) # git subtree add --prefix=bridges bridges master --squash + +# Synchro bridges repo +git fetch bridges --prune git subtree pull --prefix=bridges bridges master --squash ```` We use `--squash` to avoid adding individual commits and rather squashing them diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 7fe076ee297..7008fb385e2 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -474,37 +474,62 @@ impl pallet_collator_selection::Config for Runtime { type WeightInfo = (); } -/// Add bridge pallets (GPA) +// Add bridge pallets (GPA) parameter_types! { - pub const MaxRequests: u32 = 50; + pub const MaxRequests: u32 = 64; pub const HeadersToKeep: u32 = 1024; } /// Add granda bridge pallet to track Wococo relay chain -pub type WococoGrandpaInstance = (); -impl pallet_bridge_grandpa::Config for Runtime { +pub type BridgeGrandpaWococoInstance = pallet_bridge_grandpa::Instance1; +impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_wococo::Wococo; type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; type WeightInfo = (); } -pub const PARAS_PALLET_NAME: &str = "WococoBridgeHubParachain"; +/// Add granda bridge pallet to track Wococo relay chain +pub type BridgeGrandpaRococoInstance = pallet_bridge_grandpa::Instance2; +impl pallet_bridge_grandpa::Config for Runtime { + type BridgedChain = bp_rococo::Rococo; + type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; + type WeightInfo = (); +} + +pub const ROCOCO_BRIDGE_PARA_PALLET_NAME: &str = "RococoBridgeHubParachainPallet"; +pub const WOCOCO_BRIDGE_PARA_PALLET_NAME: &str = "WococoBridgeHubParachainPallet"; parameter_types! { - pub const ParachainHeadsToKeep: u32 = 50; - pub const ParasPalletName: &'static str = PARAS_PALLET_NAME; + pub const ParachainHeadsToKeep: u32 = 64; + pub const RococoBridgeParachainPalletName: &'static str = ROCOCO_BRIDGE_PARA_PALLET_NAME; + pub const WococoBridgeParachainPalletName: &'static str = WOCOCO_BRIDGE_PARA_PALLET_NAME; pub GetTenFirstParachains: Vec = (0..10).map(ParaId).collect(); } /// Add parachain bridge pallet to track Wococo bridge hub parachain -impl pallet_bridge_parachains::Config for Runtime { +pub type BridgeParachainWococoInstance = pallet_bridge_parachains::Instance1; +impl pallet_bridge_parachains::Config for Runtime { + type WeightInfo = (); + type BridgesGrandpaPalletInstance = BridgeGrandpaWococoInstance; + type ParasPalletName = RococoBridgeParachainPalletName; + type TrackedParachains = IsInVec; + type HeadsToKeep = ParachainHeadsToKeep; +} + +/// Add parachain bridge pallet to track Rococo bridge hub parachain +pub type BridgeParachainRococoInstance = pallet_bridge_parachains::Instance2; +impl pallet_bridge_parachains::Config for Runtime { type WeightInfo = (); - type BridgesGrandpaPalletInstance = WococoGrandpaInstance; - type ParasPalletName = ParasPalletName; + type BridgesGrandpaPalletInstance = BridgeGrandpaRococoInstance; + type ParasPalletName = WococoBridgeParachainPalletName; type TrackedParachains = IsInVec; type HeadsToKeep = ParachainHeadsToKeep; } +/// Add shift session manager +impl pallet_shift_session_manager::Config for Runtime {} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime where @@ -539,9 +564,16 @@ construct_runtime!( CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 32, DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 33, - // Bridge pallets - BridgeWococoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, - BridgeWococoParachains: pallet_bridge_parachains::{Pallet, Call, Storage}, + // Consensus support. + ShiftSessionManager: pallet_shift_session_manager::{Pallet}, + + // Wococo bridge modules + BridgeWococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage}, + BridgeWococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage}, + + // Rococo bridge modules + BridgeRococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage}, + BridgeRococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage}, } ); diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 28a8600d2ea..216997bdf2e 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -1,17 +1,15 @@ use super::{ AccountId, Balance, Balances, Call, Event, Origin, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, - WeightToFee, XcmpQueue, + XcmpQueue, }; -use core::marker::PhantomData; use frame_support::{ - log, match_types, parameter_types, + match_types, parameter_types, traits::{Everything, Nothing}, weights::Weight, }; use frame_support::weights::IdentityFee; use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; -use polkadot_runtime_common::impls::ToAuthor; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, @@ -20,7 +18,7 @@ use xcm_builder::{ SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; -use xcm_executor::{traits::ShouldExecute, XcmExecutor}; +use xcm_executor::XcmExecutor; parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); diff --git a/parachains/runtimes/bridge-hubs/docs/bridge-hub-parachain-design.jpg b/parachains/runtimes/bridge-hubs/docs/bridge-hub-parachain-design.jpg new file mode 100644 index 0000000000000000000000000000000000000000..02feb11e10175b1357835e12b6d3e756032c9aa6 GIT binary patch literal 88324 zcmd431yr5A(kQ%fclSb(qQxohUfkW?y*LF5#ogWAt++#RcPs7`ibH|Bsl7+ObME=> z_usqz?6vkYnM{&-CdtfXlJz{zKYalpONfYz06;)M0J^{*;AsgU2!Mu!go1>ChJu2E zfq{mFM@N8%gM-IGMMFX-z$GFiz{SUZK}tvQf`sNJKK?5CfWW}Oz{9~~BOqWi65|sy{Vx041@##iUI%-{JwLsO4`UmJnD;}@)9_BQdF5p5d$dy3C9=4WC!un9TP=FEUcz^MX zmC74)q+rW)#Z7nSnkDjL%8$Q-A1X-L{XAK)X+FTr^j6EW*gfj=-0WJ72INU?bByV3 zwKI`xf}}SBiHgkFJS)X`y6ms^e>}QVAe~b7OMRX*xL#FOxQPzBq3tH?WySK>VVcR> zgV+V?Ec0i0oQrBXgh+`N7S42zs~L5{TA>a#3o`v>!eb>xbwsQewzDTzG`$4eNqMxe zO(=(i_;I2dxh5`+nOqQ@;|39}oQDo1X%MjNePkuz*Q=u8n*Dn*#Vm3Y?9I_I7$DAdC^RK^=y)0AUJlmb%@n7WoT@&0*q2aC6 z8Lce;LNTdpHL+18#j7B7ZtU^J!Ekry>;Y`+ys(CrYNV16A6na9%+BMko*v;mi~e@p z;>GVqfKQkUidbOV3mZNF5WlU_w`%r;-0{9XzbRZNvY2y!XRyOWw5opy?r+5POO(Gc z%xoR-IHx9~ermsKJwIi#xH17~HCs%8-f)w-;U|DhhGgbn1R%TP<7+8mzYySeXoG*1 z0RV*DUq%LwDuE;QC;bo_l%Gg=_;C8E+i3dVvJ51$7^%xNwNRVnF&Sxc`&mh5nszKO zc7OSpc@6eYGk|AS)y|AkN+0g|WMqNM_7a5qpMZ)g)<|HL3;|a@m}vuX$+-$sEWW?m z0778>RXMFch=7^N71UP;z)U%<&D(-E~_(Z;i~h!~I1y%0-i+?G=rs+Sq+_xVAywT&I%4W6Bo*_#Mht zRUq@BgO&ds+TNe`(e`9Ae`XlAa5<;Ue!!rfFx!79fN~rCc%I;>JLbpZxY4XlwNv|W z%(1u>7n@a-Ai{dZz8Wx$cX~bhm@tguyTuvrVDDUSWZ1d2G{5VHNAEE;q3t&$wp=rO zF_7#DbI1pPx7Y7%D3nb*TxoOhraN_h0_aT!KQ`jlJHGp3pk08(6uVb_{zf`pq^+^h zJNm*&9o4R1G9{qML=p^h@lIuXv_vOqGQ5A9h*dRjz^V8rpOD;E3wbZFFM=umvN`@& zje5WJ-LsZkVDbT~T;mPiu0giwrL<*MJL_#8Pmpjfld}`WJJd@m9?#z|ji6a`5cFfo zX;7Jv-l197T|1Q!a{ITqqeHuvz_>N|>%{jcWkK0TriB+K_|ETM-i4Cq$8CttXR0PP zfeS5VRwL|Rp#S2H%TLnTio}Thhe3J9fw0CG)pjQWx#8?W8cK$2S@pBsHMc=VhlIbo zneABR8^8Uh>HTNI?~ea4DY*Bzx8z>t9yyKxw7uW+2sl>jSpt7vgF$yW*)416hyV-| zXoJWOdUx_yfMbu!68@g!Bg;sbRhq%7B|C5en5Im%$uYZh0P7>Z<-{JhdGXBV&-A>Y zz`)a4s^&n)0aCetspUeLo8mFppDOxUuXrc({jQ@w_y$+4M2I)m!`$Lw={C(&cXiBu zyr6`Jnw?gKw#a=^H_cl_b*8>xAtNW`gY9kc3$v9e#*MmCk#%0nNv`eH3*a($`C$#7 z)yie;->Mbhl;tv23BikUG0SgD_iQ$Ax#I)A-ftcKn}_FT#&20n&tCxmkoBWgUs>@x zeo%lx$|TP6w!XVE%B3LT5SaOU;tQYjk^6ON+a8|ttr|9D~Fa3=C)+@W1 zSXf|3?5cRp)eL6gzd&<63RW&|G=uf^we@_~`oaEBMfVH*>jD07d%^ct_LtYAe=}LA zJboRot?^p0$YI2=qDmb%o?)l&@HV}E)TK}A@_b}r)O=*m@$DNr5+Utiqm*=)kfQK# zUq{!<9p#L*d>SQ+q$@!k108)sI-lp}A=^4aSWkkAA~H|)N_NNp>hULpM7T?M?eiBF z$`3OgPpxJeH?pm_49oLuN!f8vdv+?R&q>?REDcc{N9qY6M~7gQZdrF7mhXgUf-_?f zm+x4v;89oha-coIf}y1L*#v@AR8>?l{!;5dY~o*i{DdIunVemQA>!lL|Ex9u7|NCO z=&Vz!#S42ZPuXytF~xER?#>|#X91eWLZO06Vv|z*0_AXTBC&D}jcOE=OTo~RcMd#m zVN9%+uE5nL^9qb;&F!2M^ym2hbOrvQV=xgNA$AGZy&gBU*gr)qV@<0K0gxW1Y>^a( zE2sLNL`0(f6K5A5{{Td&2k`yq=lT7ZDu0Uj2jWjtzX9AOrm)g4zjGjgI?9F$XN21- z45DEXgxd&wze|A6AJbIwI$8!{jrD--+1veLHf_v^@SXq&m`?ydDL$F$hR`XSTjp4X zWOH1ozOO(1hK9CeSWtHdVxGW(4}Y~tVYtBfr+Hk8HLrRAa60$Zgl+x7{_Zs+1F|QQ z*-lx@Dvc|TG`U{)wjjF|Dn2S%+oS4`c`-);QA_H(`uTYYZnC0NYGM z8~IKz4-egm{cPz-LP~MlsqqNi4z!esjYGzs(VmIyxC96v*p^jA^|||9|&9`u=grY4+o|%3Wst887+ccR53><%$w$7Ug{?h@8E407US3*i4)JEk0%&uL}OvE+&od?%B|f z#nrbX?Sr5&o<%-i^OhxQ&gyMxD!?1sfR1q8CJNao^d?p?s?NvGM8-? zi~nVB(hFn40Rw+_UyspsBKssI^M~&R$jE?zo}8#JcVL--I-UR{5+QM`4p;H^UNIYK zuS}h53nr~$WAjpm`^1pjg2gR~#G`e{eE4==^)5|BU-KkZ@Y)D9?~3E=4in7cm2y<&^RZB`fw^I_g5fJSs-x10&f$DajFG)PE&$f1X0WA24b5&o4`Q;!zs695%!<+#Y{ zqSURLNVH(z_)AU=nuBMNEGu=mbXZlNa+6px0lCqRm*a*&xb9EA30$^6^u_MZ(ik!u znFH)F@-9-FlH7=IZdd-Hu$4C&Q8lkUax6F^WG=u$#440?Uw`#x1&!+f!J zC%y1KkH8vHqo>u_`t?fMkf3wxk44F2uaYM!el6)d`kLDtZmisGWGc-B@!MF|j3g9h zpMr)?aTNa5?C3)@D`Vd6ct^YVvx(m=c}H@xL>i1+s|a(CTbs*r1*nDM9`B3iEl)tK zse!WZY3W}|H(;aIaUM0U@0%Ffs;jLoSQykRqHD36sWYACntAAHab%?lnGt9K^#S@$ zALxN=54T+^5&%A8Q>|Z7gifvB&M7-F^qO4|@?9?%E{< z2#Jrh2biewJNL;F4+lxE7fro>Bit{r3eCag&{Nw#ASy)gf9H1u(hG>}1eQ5dK6sTW7?TGsMKmD!7 zd4|CV`3D@2NmBYNf0o?(u^DJp(c$-HYl+v(3x8JD9|VQ~>~dNI75`O4V$()}{L9>Y zH;qYY0>_${EHjnaaY`p2*bYLdy`lafL?(a%$BEu5Io8YPXCKf1ga%W)elI~SI02&_ zOqK9QV4P63pKq@^GAXu}J51hYyYUdIdYP!rdyzYX?&{(^>fL#mh~W2Ss)9uvK~a<~ zj=kb?|Eo11^zQo2K+`iu?>+&2R2lvNGr4JZIpxn*_qhlv!n8YPQg#{$x?Y58JLGkY zYQk@Vz3I$SjbZaLi*5UUUv$0?!uL!IKs|Uj@yZ9J*oyq_Uk6ntR{+wBr&d2IaOo{% zkk4kM^;qf+KksP&t0X;ES8hoz&*Sr>5TvY{#kt@F2aqP>$_E)=r5dWnzxs-$cCgGc zK$+`3mDyC*6su~0a($L8+I6KHldlubG#{`gl;3K7{Xm)EI8es^$&WRzGE#PxXD8#p zx$@5md{I!vLPPb}E*HEhT}FIb=y?meS??C~fyel&3QMOv4#jkLsTwhE&3@mBt6k27 zlQj)uB4e&2+#^lBOC9|GN)*LW}t zMrT2+xQ_|+wg1cnaQc}nszcJa;X%5jeLAbk6Bu6S1Cn)?@ySd!_w<&~7pUm(u!a;i z*-f#lv#L304C!FT7un)!Z+zyYP1l)r990gBw>zI#DG}^an;LI1Ke7Y>rY9>&jLNc2 z_4D|qmVb3h=!xLmWujGyU{^VZ_xy)*>Nf0p@!R?P!y42;ZC_hoKI+FG7%1YoF|L6F zDC&$H6T%kR2z6 zK-Pp9kH4ctV6r!lD)^_x{&zILGhFUyY}NaCWeBGLfGO(;%il%_q_cnQuDgq3yBmSK zuKy^3L zEe=LpKzhOCP$k;|V{b2=@BZBIIz|TP*Ioq)SNm@_VZY-(U@{JbNk9?{OP_zwgiIEX z5BR5h9sM~@o;aJ6^EF9TW+npYXRjAn(fs(?=vbwD?C}AkeNfNZl@O|)CKD>cxUCMQ zsG*=#>EAXFG|LAskQ+>aLUie&A$YM#c>xX~llA6D+~eiJqQCL-P}++_)xow+M|J;< zxW}H>wA6b3YmKgvx(X4J-GMqon&uS7uDTF+g@m#8Xw{TvcRMT-7lNpo37X;9??Okc zm!*~H|CWdTthaw3G&=t2K;t$GSRtuH-nmTrjb;970l^XiK@(3T?e%ZOzis(vAp#Ne z56{}dQg4p!SCsWEFYVn2r+{s-y#$bcCc0165M1_B8I{(KGq1~ho!AfNyU zG;~yClo#~Od>AAznPiDc85kj1bcM-S1$`ocX9m!~lLQb5&?mrsv-`B;4x&TC+wCcK z$5Ab&|1%(wEAK$U3%1>1S^Xm4L3z}Of*mV$#n_>BV04$sDs`UKZXi`{l+*+@shiNY zH*3`l zjJwInaSBB0da!-*YZ-TQ`P(jyH&HnCBMx@OyNpq>WN?JGt;WdR@N#uQHYENyTv%3m zi~d;_>sbLB&d{|i_FI|v;)m_vM0H27c7Ys8bavF1nR^_Mga~F3=m>&X{=tcL2lFuw5EuL zZyh)i6Ao3^%Foo^M-%N6;}?F@$UL>=hocS8Ng%1mV_GpWzS2RSxw zE3jBxygXuh`I=>#@*Pa(#g4pXB6$;xIPOxoXXiV!=p|8nWO(AJ-8AM*EVAy1u zpZy|C`CZ-;d8&X)fCVC$wOMQxep|YR34Dxt;g~SfMJ81BkuD4? z0XacO7|F?y;I2WC_uJ@feB&$-r{A35;e!VYj0WVqc5&Lagj_^pd1rdPWLP(|s8LV| zUAbMpSY3OD%?7pY_xc%- z8h0MuX0I|TaXX;HPQ6R!@!v!Zt~Nvfo&Y)yw$MpkI&WGRs(VZv?9!zAYzUcRJhuBB zAW~;eMNwLO(e=XZlSVbzGe>pL<5GQhHkr~+q#fDVE7eBHD2U0j^+~FZaqg6_T^fV| ztu|6wJLBi3uC-i?gjPtbfhmF`|2e{t1^Ljlm6O~(Q*QZn=iEbJc!ttg$FNXW4NHV$ ze?HQJUKF+J^mNiUz_{9}j+~+q7{4(WiGF^&Zcw8p(-7z;nWP9ez1Nst+uZughro|p z3$U9DPzfzutL4^x)_pcAY$Yi(BL_Q|4sMODnycl?D{|XR7ZoST>2Y9JENaOM-D$yV z)3^es?UCQ$xNHWkaQ$!vJ6p?i$#>$0t z9aze^D>`;hy~=`e>ZCBWTv~rg4E}i5hOSy&9=|RvtF5dQ@s_A&UZ(2_u=g(LQvI8y z#2imvjd9XRGce z8opyh%@bhkM$)_TwBdZNm#o6ujy8_K?5h=uc?lE7HJnFJTz>tc32V|wzJpon{~7Qh z-)2ds@ka9Q2>|U?!UIEb*ueVv50dInfHKOd8)p?iqyYS;4cYJXE!O#ZFXXX?@B;SJxZ{l59SDGN%0bJ;8Tuf{m#}P=S=bM*?3!i$v3(0ZEovd>T-jEV zrqr>bP?O%%x%G2f^-J6Vf*0O z4M_#nP{aLS{I2X%Tf?|bVheT8oT13o_&%enaaP#T+;uS;k!mT6oGR$P?mCrnN4WRY ztIh3+I`9uMTOo0h$K93cUM)dop_H~NL|_YOhK+<(Q%+)0vU12ziZ$d_@4V-c^tOQ& zLypG}#g@@I5kBM&Y72S@D?=}KN-ntpMfEKkw3p8^rmh{Ok^m8N63-ym2hYSpo$m5C z!S@3C8ZgKb;xrQSE4hFX?u1;_4|zh70ez!WZ(4&IH_Z+Gx*rs-(B|%=+{O&t6#bO1 zqVA!*l^(tiE3!AzynCYt%g5r5B8B+16pAaG+>pABvE_ z6q~#ahaJF~Be#?)xMvQhZXny5nLNJW=`&g|T{yUnjVIN>Q0z4be=|T!?`%3VjQo&_ zRD7nhTa%uGaeG=$hbGk1O&(?5lrkq~Spy513eMo9d3E*d2&RrE3Yt=Gq{*X@`rxOxRi}}$I%{}^bx*B#GGlb_YVl{C4Gfa!j*pMPb2adL>*)jlyw46FypJa@AJm)!5M`q9SjwIj9PITCcBw13kXe^3RQ80Tn;?R=h`!gz9zY1_y(Aw zf_lPpO4-7d1CEY-2<|>XM)FpWCHIBrd{+?p~k0Xc^dANq~%4G2uW~KWM zZnKN(U8EFtfs&@qVN_Ey#b$g2IYQwn?j-WLxN~)ZyMenVt0>8{OIWNz!Unb8WzA^! zJnKm(vU1BbNX4i9qnhp$suKgy#x)b#E&7onwHNP5FjK&$rBhcvOf9l?f98}D)mvfwe660oINxBXe+zm0iMSV`X z0&~$09wf&tN%&nIzZVnXaJz8$P;_?w?bkG`T@BaSRi}o31R(K{@0LdE&&yg4Z8-i8 za0>nsCMX&`aWnKSq8-UvF@TtoBhYrN*orW+n+1E1>gKg|vV1G^QPM`rZG^Rkec3#b z(wEjIN{I^vqKpO7(txzw%f~Juc8bW?YDWVSu@>61gz{_)*9anOvGbNrLCt6Cq_VqC zJ=!SqFD732WLkdWV!I9cvJUn}e-yhWfMz}9KL-5q%k<-pfWpP9^e+q`Uc5g2xt>gQ zvPUVdxVw2`bxa28N+iCb->~d)x`I8JtOd^8P+W3Qcbj8Ym`AlAKChQV@hB61CYx-;a_T4UsYd#uc5rK6luagVj1Ay zd^W^45eN$(8wh(xPxijGSG5C(R2DTH;hnbbbsms9(QW0_B^VhGo%wTZ@p) z3kYg^#Pb7^Jy4y%?DU`&WZ+&!ZqY@03Rfy#kf-Eg<`5up-;D_j<2HYoNN(Yf;=P=< zLNR^k-c&z=Hp_vjJ4-h3$ZOkqIiE{&E_3h#W=SszWuWdeojn}Tqg#lG%@ft3o82!w zoRaB)nyEC93$jW}7le2TzJr&xJfov;vH)~kybyzzd!1CR3|<*jP*o3v7G*W%$_FcX z9b&*K3pHcCn(?%akxG>Ek&CpIu;R3lYs0WulLZZ@6;dSeo#0=ACzY0pwQHfOZ{AVg zng-;AD6Crw$kuHtaLfhxU8zpZm{1e2Twy)f+#ugI2bR4rl%E_@i=l$fY{R-2EEW_t z$Zs10M*B~J!^m|AQ>-mQJngz3mq6$>P730IQ3M3DMCfKEv3!v+VompM?OTCs?s<*^9 z8v;+D%g629pMbtuhuyO>y5JUnl!$2Sq+*Czr0APE@tzu6;Raz-q#&ix{xS2|kURkQND8Kiimprm zyVcPr7=V3ydG;M>--jj);&G!1Bw-9m#uJON&`A78SdL)2P-Bu^>|_Zf>FGb1>& z*)N1Ty274P!_~`&joTVrPx^fS`1ywuMzj&QkC{DSNrB)g^mb9^X78@T{q&)26-BKR zWH6TG#fjZl5Z$v`Pz|W^blus#-eRGO!EAhIf8v_5P2T$HMTs!O>I}+{uWC?^kR;(|PfnQJqwdimXf@s06Y=6@$OoliQ^xoRq2X1&Rm+%pM#Bu&*Amti9aSXm zyf#unDvl;{fXG@zP<)Xv3m_VHC=9`OG3%ZO#I3E{v%(bEm(OEP`rUQmzMIy^Q+m!Q z(YS3o^R#1z>bT1V6fEal&gy1JB{88xD%S9B*yUs==5b`fSKw~8p>23la=ts|O#Ha= z24ceQQ7rR}#R3#+a<$Ib%3D)K9s2u%p}3pN{8iBL9S<3RRujFKg^uaP?#!N0n_?aP zjPn-uTp(Qq7^vHU`Q+r>3ovr>epMB`3@tDZ?Y1jT3b~FE9k10Efw7i}3!<>N=Ac>y z%^}>QvoIyTqCy7P_e4#U=D6_6t*1J39TLmrB^FC;-6`uMr#m+e9_(%SYlt?1{~l2z zImcuCbsekpm}uYI7$}K%JXsIxVY1Ww4cb_j5g0`p#vL`fYLzten3iy?5GkM|@Bcj_ zj(~a-*xDoZmFXQ)KfP2YxI6JR87-K!E~(W4u`l%)SSSk|lh zzUUuU_&=}tUzm>jt_P9U*-XyWR!I5XH%r7d3@)2JMp|af9@%Db@yx z53x9B(dxQK3nfXy$&=>3&Lqh+)x8&oZ?yNT+E@k+kD(N5d#WF>Jfj5P9_+0cEqp?; zO7ZcpGSC7O*)~1&Nw$~cKd3j5UF69#FVp8>S`iME((=g&eXbN0=gm^@uq zOBU&$9L5HM9sfF^fDhC*0Vz*_ic34yc&gFb#y-EeyyZMEtV%aU8@9cI?J0ayOGd#N zPe-;u7AtCLIe%xWrY!%@kpmh9CEnz5kRu_AbqWoGeC>NM%m;Z1h-Eexi*t9wHpe&K zq0|)B>juiN6xkQ*r*LZftQb<>8vh1#@n>*Q132YKOg=H&Zy1KY_WZfwB8&lJP3#Ho z4moDlu@$RmGY(unh+(5$lDDIs55@>{N%$ z-08+;tS%`z+$`F?J1D|p8jEFnJ&+@<2Njobs!jeWrNg~v*WlZTCnj?h^m_yr$Rqb0 z^?kvGI^q>YmJRbo9bTV^0V_&#k2hLd{JoGhtQguehxpvG$Y3`iK;)Ceu(v5uEIZAMN*rT@&fPwONO zC3Y{;fJ$9^!@A9JB|GCFBwzM`mV21W9764&2P@##hN=?MtiTB_tWZ^0cE%DGE4p|w z+DD5~F{sSxVsAvOw)MR#t-N|br`wR{$%v4>7FS*TX~(C9J;oC-ncUznjK@lx z7`?N}>3rh}bg}H}e%vY5#`)P5v~mkf62$y^9Z}KIapWo3*`9-TswB?Tb9v0$>#hR+ z(>N-Pgk`&h0YCE-)SG(X+|JCs6hb| zcK)6bw&b5r?|8^7-i0!azxBV~3XCy8^beC`_pN>rknB6xs0J-gmM6I#FvjCabshhnCC{lZgc0%ID!FwK!#-H1S!Ga?j^!#&C%7i=Pse{FFXxRMt81X z7@`1I=c~yNV`>ZBU0=n8blzSrCE!@^p*h)l3SPm@>YO--D#AO@cjs*?%Im{&!m=>$ z*azUyF~QexB-^o?Vo;8feQ=PEg99;PC)rxL4D{l2rYtORzuBrSa+jdOO^1A3`_SI7 zUH-<OOc?j5tO-9=QQ3CwOo_q`dyF@A7*D9;#N{G-S=#x$MsZWjpJKkB#or;*1Fa% zj!wRg2*yO+^^qDw>drUCRP$FKNv)&Jd&7svT|&^pH0H@0;#Q0$Pha>{pl81-EKee! zJ^-kPng@OuzKXIiPWYx#nciNvy$_j9uIu56p1KTG!HS zJC6n(n^tbR>A;!wFf-iGhZR=yD<+|~SBcmdc5%;=9Jaa&8@NM@`DB#!+34|w<5zdi z0cPUWj;*{FGa*XY+lEIpv^7H3pRxkUyvZz}$gRtr~slR$_uPXJwfTu2i`)2;xv zUKhg<9m3QL;OaC*lHH`=bQON*!=edKxq~!&g-Z$^6e5CJm(mf>>{}wsq!2_kz1dWz zXtYxnWm@ap@W6?$5er&;@3_y!6h!TG>?E!wpri}GU@Q?8O%uBR$^D>fmDU2iyhLr@ z7Im*`M$96O4aq6~MRIXjxswu=$Ycp4U6y*J3R z)DsL!I)U=~B-EhV*)L|WYu0OnXXIq-rn+Ur+(PT8x}Dx!tloH9q+;BxA(iDcH{nff z6baa|`h&sANzASx5H6sX4R$@Oe|yb&{7&QEiwQsVquW%TWG}@TAOAmSG zON!jt6WL6+>*=@0`-Qb*SL=dE2SSLK-ae-@%eY6H+3RM-EfyCh4@ z1^#Kp(@OSNpl-=+FP;GP`-#)}H5{?4sME0O2T*x5En>s&s+R#Jh0u9*#uRtkB@Fpi zOUZE4Zk|VV^uooEin+~A3Hz$_W2Il^lPMPLU+C73kri9Txjq4$Hi%zQ(Q3Sd=7`lL z%U)P(jv|=YVtb(MzZqKAnqszi(=5EL)PUQ?>sCR_TxFCm-CGA2L;{SXJ-Wk~_zJI} zu}=H77gP?X2v2>5qsuBS&-i;XYU5@l+iZBCt7A&`cI-pRh)UiI$J1e&2b>O$e;PVY;3ESHz=I-FDyluPiVU zlAV?Jod&Kv>or}vg7-KdN?o>mvc^>lSqZ3Xa)VZq^(NgrnPNlC}(iJ^6fPg#F9UX7nD;% zv@l(2pGDWi)vC976&1jg?5nGwdgCx*lr7a|5CEgAv$e_`hKc5%D>Q@;w;Hb=MjpKV zZh)aFIajocid{)eny`0y?YI((Ic?D&G>{8X+_gqa%Qy?Fc};CdyZxXk4l=9-WECN- z?Df8qXyce$jnJ_>oI$UHzT}Zu(O1nmd+X9mk?&DmCAXx%JGQ|~$S*s6K((MTdwIpg zfYQi1(pCYZraVdUit@%sH-}781_zu;<-Zjs`bj)Ug{x87y#nJSEef`UdQF~1CDejm z{z*m0;BzZScEZ6oF$vhT9RqM&$o81(a}Ah}tt|_Qp(~}k)Af8MXdCmSO51}D+SdlB zbG3-U%I56}ZZ6xeXEx&vqgk?F-yjwqJTloQwQwzumh8$gwIp3S9PBEcyRa3dbd3y3 zyYz=xVH`Zw`hAs_Jnv0b(N{?^y zhL7%DrAMy$V@=zP*6E+E*=@x-5ve71LcWg}MQ3KVjH`*M(`|}Ii7kbczg+4n)DGsIXawiEAuclW#x>ikWrTPoiz|wMG2gK z^>RL_%PC=xeV(tp`hiZW?5a_jx{7V7?t8vlG}T(G5r^`b!NvNBkt0pim(y*t-lo>m zkNqax@&~xYy-w%q2Mbnd;(8o}gpnPHd=c%NS^Hf6&UF3)eVKh`yez2HFX{O@wUNYk z+f2UO4bs%7AB!;@JON%cV_$UMzZoEv{Ai1O>teD~ymEC;!?Ea<$Cz8z&MGUr&}!>0 zsZ1-oC?MVQ*l&IyaR3^Rt*MeAkC;c`c=N?Jv0k8cEJFH)o~?pZerSvAyNqLPC2f{U zH#;yQm_)TYy-GZ(moJTJy17Gv!K_q@X`2q^Vn($RU-pFLA^(HGuj7K~D%*h;mcvGr za&u#K)E;}S4)zm~>M}d^)@_oV!2|Q}&kx5%VNvYITH%p>;Lv%Kk>Z=+QS_ z>>A3K-^7UHIU$9Yf~)ZuoFN%+NMg%?@0I-{Y{8f5L8dAaP3fQ#K5Pj1=3Zhd>(@L7 z+NI5%+oQp1mZG+-zH{u6l=yMw>)@q}7j10UihH7RDx4x2*jp&$#;GVrsI<Y>|tUpi`% z7k*gCCh~@CkMaqSm>43mfC)SXtVsW#L^EEZXmouOe*&aZok}VN<+a?)*Z=Rr7XJ{~ zpyE*Jk{@R|;lySbS9}7b1J%I=h9!=v`6JQI$bAZUtEkN#Nd;ydc4GUwSg)Bici0m^ z%1tr$gle8~y!M;r*x2msKc6W4!VE^;8q$L4l=s0aB6uG_w|nmOk8K*y4$9{gBxBF> zMt!a_w3v;50+f6@Q1?`-G|Vzvrzqd0jH|QJ%<57*+NA{E(a{9P?0b51>L1&Cl&Lr^ zHE+L*KGBOuPiL+qRxKv7qtw4u0}{G}U4T zjDl^NZ_7yD?HjuO$HeqWRNn&EXDwLq5CkPK)ly>gT3(@Y<#fyZ@Tj05+HqI8(tikS z+J+{)Oy(^2Rion;ukmNl=m};7NBI$seWUIYPI-tyWheo+Z_q|%7qJz*h^>qX%u8*R zrt!@UIH6msOo=psN~vZ0-sE#-FO%Est=MO$cO4v3-bw?nG-+-qyfJt_+~C?!c{7y) z8M>U_5|XOu zo>L!`8q4)g26>t9@43A#c*|>#gzI>k1wz8xk@wBWNT%t^-6nZIzrM3r0iN_24lBPB zz68BxFkc!fE3phwHkRPH;V8j8W&zh!Dvn<}+q!&c`gn{}OCHpX#fnaN-PJ!;C}+1Z zgW|;UGWsj9|D@u#eHp|UwQ{jXDEg866O+qvj=3a8jqz3Un{m~Wn2rf#*t+Khm%2x? zlcUKLp6Qe$wYi$>rVP-?OLFPdJqc9jY0_MLfeewB!hGN?#x+Wl8Sy=Vh0&{qgtlfHQ) z_kq-=b)E~V*aeoQTS7wKb7~S!F11`cu#ELU)BO?z)85Ksk)2)-+u(6@pn|EN%;7q2 zWV6qHA7@m9s>3Q{EQ+nnUQ@ks1kyg=!LGzaPO>*MFDI-@~T|L`VMu8}w ztj>w`AJ+qcA0glcxn2%<$Mb4QqP#1ZA-Wp}HY?6L(StA@QIB={;zzot%pJ0t&!L+h zJ)SpsjTeBUobO_Xye4vlqS*RSZPkR0(9bkC7Pf&OFiKb@BGK@cdmtpskF@7l2}6l3 z_Fa!0?WrCN#|o0X{(i{8=PY!}PZehh=PgPG7Uo6iMqg#A=Rf9BCakm zSzAVC#(9w*A6*;UxoB^iP1Ycib{9MoQdfdyA*UWZZ8v z_gNDpK2<+N5BeEsD%LxdHg0b1#$p<(gHggm>gb%-!ZvqIw1w_uj3weJSe1=X-nta1hhK z9g0d`is~b0XGqH~&MpZq>l8vRa6e6^hlVx`FEsgl^fd|*H-`}UOK6Oe?ZLVzgp7Bb zj>V>;Nf)Ej^kVF5r{*{1EwBd;7O$L0bd!*A1dDLGsSI@3(QGzp~CT*;{ZNM6VJylC2)a&CSp+~9ATXD7D$)DbO`inDEvTekbd~8FY-9S z%S;i-iR41`ZLeBNM$nOOAKtj!2!51HDJ;Q)lA9Kd5WZ{`g^=9p1aVY|gvsR7MsJ>( z2^%;t%ETlCfzOI}Bi1ldhXoE^O;asfyH(muzKgPN(1JG^m?8jmdZ#qxN^L>a#7HkK zF2Gq?pmKD14N6oRwn4PJu-z4#gX5#(5FZZ}6*bZR?S zK|n#lpphV+FWLgYfS=v~1E7GTq7gHYFfuXo%h|;sqrZ5`Cm^q9|2`_ZYGR#U)*-7C zLP6g*=af`Xx4KKnAo~o%Z_@G4{aZ+0;QiZ&6}lq~QOYuL_VYdUPwaR3<%nKIC4L#j z=3jOXlKTX7jy(Y)20!&YUfNDw27D5~n=SkP;NWrS$*T>V zb8Av_?V}A-mtW6+Vn1Y*v2>4pox{!ecB_J)GT%s09xDrJWY9>FU?bYeS6lPi~9Xl9RG)?VTS<1Aj2ZCqOL8~46>=xg)z0%b}w z(GN46-NwY&^)y+~x?R-~JGg?aekC^ywStq(kTQrHQQthh^#p)#dIE6QJ(4gHogr8p zmxN`QeZ;TV?n92FVeXd2YIUwvWkYK_XLhyPEv&Q~i2Qio65lEVf1EqfpQl_`r}*wY zNA#=}ik9+zk#4XSsu09yjc6(To_N<6FTgB}+Xh9LN=r72G&Hiq$tWiLJc1&Ltej^` zkLtm0iXqx|90woMgu(5Nlm(=VdwQyOO$9z9%9Yv&caam6<*lH7X9=4MS&3gcY9N#P z=*x|d%V~il$i{6J@LG(2wgho=$vhZ3_j3gfh0?Jkmep4EP1KITtul%6N{dV+o(5ZS zjsXYodTySTb#ULuoe2cG@dnOUgOeiBkb_zQWp(iCE=iH41TM&YsRaa=p?M$?$i*PP`R zjuUE?ispz}SHy;hG7>e~Em4O55l_Abp)PQuq*$D+2AQU-FOHF^*-|O{t-aXWP=L}r zgwR>fNZL0I<>DIQSDFhBOG*a}$QI2XnwBmL^rMXgV^gfSrWeJwL^Sa8=VqGd6;2xp)8?+4;~vt3tIqpM2eij%{czwND6f`Etpjc7Ybo#(k_MH!~uUG9y`#_As}ex_-~lSKs$K zOIRabZ_BcZ}aD0vHJ#5``uK1YdY zfeuG$7T@s%peyBp{OeK5v>*H)ilUi`B#`EiztM}zF3qw{>${hkBLx2!)TwkT`4A&HAU7J zGf+kdY1Uh75K8<-@v1zEf86wh{c>jZ&C9P=IB>h$3j)F@m<;~9s!J+#gHZG@MHA-i z&9+}y0KmMu^>XH3hOnI?x>eqD@o^1F1#%iJ>}ty-lpNcCE*={0y40oN0!89~Rx&vR z6dkouRpHW3+xr-6`lDQhZAwOw#^0oKj_&if=9RJ$7=W>F7I$amV}Hr5{F>v^!g?aJ zf{(_mgNNI&;ui{_$?0cl$F>faQkwV=3Mx>%iZh*KGc5ma5az-5-tZIYa&cvG3IjxT zMD(YUV!@lE)UA>yZfSUlRhD}j%&4+Ey(Qk z!f5P~ZXfXuGw;=zv7HKpi~ipX%PuF$cf-1Sp*y5PeJg zt?Z#mabn_=h1EUWPiw!);J)!m?x3vH?WOs24AEo!t#T>X?yZF~s*ajYntAu@bMcF^ zBe?H9vyB@BC=V7+Au+cC5t9uvOpc$;@4zWiLuPy$eDg`)C1*^W_8C;djVBu+QHMl} zjM0~E+*7}68(e)|-4Js0|7|9}M+j6Ld&F*NhZ)_v=RQCjzea zf@Nh+1ZIS`5ZUtzitC-zMo%#Dy_#Vbv*D4u*zvRe=Vi(bkPVM{}*p>8P;ak zt&38F;-$sCSaB&N6fec0xVvizv{)Ool;ZAIpv8&=C&8f*oZud;Sa2xrbmx87x7S|h zto7r3>wIhfnIqSeC&_coYi7nPGWA~{KZxLpg3q9wAAMch+Bd>a93t#k2+{FeX)tMNA6*eQa>}G(a(JeY>RFdR z?c6=nZCRn%-5Zc{`tXgutV6hS;pBJyE=f5GMb)w~5b3n@p;Em29vy(DoKt%}RG9Dx z3SN(=P+0*qa2}~GAEzPMo9H}4Ukh+1n2?B>pB7khZR|zbwH@kFaF12SN7UQ3oNC*= z9e?W@?^RV0U6!$y`ET=Wi*x9Qp+!z!oxkr|`Qht2etqnh+yDuNJV1dw8&8@UgP_z7 zG=Aje@qNp3*wesU^^U|*GTgyb*~>F9G`+e~SpP^>;pJe0CzZSHgr6=K^2(5fN(lyY z*FwyJznN3;wnmO-ySIV{9t~t_qI! zs!hwazCU17SC^fezK-k-7KXPd2?=hVqzt_@`w^bcLx9?quY??EZdJYAqObf8_Rq*V zHdnrcJAWrn^{93vJK6=14u&68Wht~ueBT@;NM@zS{MXGpq(~M_M$`6^(5G(x=9bXuxV9`Odh)bvx%sxdWjUPi zXAFiDQbysSoUw#yIgDdLyvI!x3}U3+s9xKRHcBXZk;AursvTOg~T2b%su! zb4!K%!6Mtw&y4p$-L>T)8v26-eYsizHwaq;e7~P*G13hI3LR8KT@*!=byL&h=Op55 zw*xr27{tg8*>oE@-lsx&s9-l|oTj(CPOn&c&+?o^4-R5v}WYeX)~_pS?Qc1@)Q_L35Yf3Yil>-qI6O-|gZ1coI+VGd6GR zI}7IrpnN`KO57Wu`@(LtdwQrI3+QlJD}!6=<6$&efqrnftp;{`c|M{qujS`r;yVR^ zLTKZHJC2>UJ4mZ+B2PqB(bM}(@5_4P#p>GD@3*o!T}j@pb=ToIfMILFOuXigrsn4i zJnuhVEfxG<8$gH$n|%sA2*MIilCzKIol<))E8hov!Tnv?t zKY_fbTaN98&&I5*jR|*l*2G&;i*yYko7(`4|FL+#5iox-gl#3E`_M7m846V~Fxkyx z^g_m?MRdUf7%FnX+anc=&u0Vuuce-8DR5WwBpW=X0Nm=JCa#up*+KlXgMf^yw!D zm0}C$=C~jUC2&S0fNNAY`c2k1yhhnb7u)L)nH$q+%XF0C{oFmVJQd*E)U}rDrz)T| zEwBoc|L`LNlMjdLH1eaW^q0g|kNPr|FM+%JdIQ-CBAOywq~9SqKGb@U-$YeM)%75) zMw(Lmp!vi>B}tQp2EJKV3PCq~)G1eFGyc z`s%i4OueTv;Hmm~hPqO#M3lmET}Ph8HzR=RS)3m8S>}3EkEib1{h-0nu0FAyu`g1| z{)2)cPaSvN@u?s02(9tm&&2sUC5V&vaoOgf{D#llwvFNullvxDC5I$LfR6Frm(;wlG3IdcG4qoj z{jStT3Jg6n5!TJ=LLZLD)Cn>;#;x6BtPQD6gsl5tD?wS_!O^&awDXu6?CNkPVJbLv zggc9oLB=v*JujTqEv?#kSnKW7XtSu8+q6@-)yOV!1)%iIi?*JKZHY&2j)y1$IOuTD z2oONjii#Km_c2NrtdlE=9!g7QZ4Op9@>I`;EtNK7t78wfS7#h)tRhDS+r$dDB*G+c zFkn}(l_|oOCCrDk; zCO1i7w)H$dyN77J4=jzAuU=VpAWnv_v2yW*n_MWuwYgyL^Z;ygHyvtj^#1{3eCU82t1{(+*a3ydA7P z+^ut4rV1kQs%#^*#Z%`D>g`_F$QGW>cxrhwSA}nxhknRgJ1o5%zeQAD#4cu;#l9Gx ze#2p_W5p||+g=Z)1okecPM0Lgf6t0RAc5m6*iAfL3T&(iKuh=XjvD!GM3(9fcS3Hq z9Z1jWHSOu*y26Rk=@P4KThbKtXQca-P6p7$VxrzeGSmcezz8VJ;y-w@$!DZ7hgT3Gm(Zl(`gAW^$9cRt9;rEo)RGhNAk>m2r_TjprDgBl6gZO zo@a1lrp4^hpGxD*Awe?Jc~XIBY~y3Bkm4vj^>vVK+EP=`Syv9Lb1#0HJ7&w1*tu1T z$R-);M$Bq7S92$H5_~F|vovxs7{m9T+cJ1dvgGI{^=4v4mw-t+j=Jwjf3-|{lKc3e zkkgUkURos-8*g_cRT$|tmrz;rCL)Fnzk$3S>YT{bg!_`9d_FD+{EtymVy+id+O z9bFT3_eN!-9%?dTg$y?`BwHMQjd|$iy+id1gV+cOoE%MvKmha^cn0_99-U3Gu0@+{ zrMU#}P~^9Uvv^jbbLH=|$6?9XFA(>)*(cU^F5z`HFw_6(#B&6^XuOW)DKv)m5)4*^)FH;uVJ%08yJspUJSA~>)Mrg+tXnu_0yj; zXz<^r_eH!XQFdnw>5(x|?UXu}sv$u6rJ}p?oVswDo^Kv-;EY+OD)0U8LQ}T5swJa| zix$@QmV`On?_nXvDLYxQVeRFGV_Xy19!N8kB+~Ke6uEK0 ze&vz)C6ZGg0-E^8O$G&n8M`$0QlD8Cxm^A}F>IP(NxCZtnt*v{Sl7yqfAT*+q{@&8 zvt;p!Y?kv;7@z!8L)jE1$<0&qRi{R}Olg#4U%^1e)T9ypv?<%$8@93*GRhdP{`2`1 zSQ-5L+p=fPDNHv(hDB*TWlLQ62cD=X6>_`5Bp3K3x*30sV;X~ceOJhAVEcURWtjOX z(NI$QQn%-e1M^hsL3H2f>|cMl2A;v!Uaa(j1SKMa*h%P&#i5nI5VqA{Ofu9P^Z?jx z4CbUFiHGx7K`T1s-Rg!b$AD+d$v8Pl7wTZ%D~aND~;9%7VpDY*$i z`DAyy)^F}w>XFI9jDarhgW8n`ni5ALG-UyVNu>$62J-{?hq0K?u#yxadyVBrH1wjtpRT_` zjHvyCXs{!irj5)u@OaZ0tGx2t#!}f&mMXqEIFO^45&q{0-PI_%Q(A;8^ z!M4lw)PljS3GMchHZl>LV!-Q#MWYi4R0W^)}voZYj>c4R~h zJSX42PS3gwu0$#>s)4*%|C%MvjmFXLLPL?%#Qe!ao;3fx4{b)3*HSNU{Fnb)=k+gA zlKx9NE9fW)BKzXC&|4EY?nL!lRJ@1XF3ISm3P)6lrRF8%vPcpiH%e(@ zZxp^$SgCM9T>!>rCUBIchqu2wR1+nIx>n_UXsU(GSK`W5L;g4n>sAg1)s zIBk~>O52nN(QEmCuy6$?hjQ*p!-x0O|6u8MZk+x2gH^KPeyxo`uRfqhf9#L!oM!Y# zkSQ=;znPz`aOInSm7RQ{BOsU9YHsXi^dn=`Cb$x5Xtue-(kbdJ6?f3#_d z>6V%)G$wqkfWXP3P!p+pxyYcr6U0fKra?=w(isvOu^^P*8BI zP;esrCM`D}xye(K7(g)VmVj8Ykzawuh$a)*+>zfA8q1dW@~P_C(YTdM1A(FVsU+J@ z+;YEh3I^L-vebff=e;IUzbAW|Q(q;)^}DEGuzg8iV&_T7oA~B3PY!4-;4NcP&jY8p zIbYw&7rb#xla>BD&&O#(8hpTOb=6rytFm_-Ds#D?(7y#QWLD3g|GoP>U>QjF+WS_5 zrCCW_nK~kr9rjz;b^SW>*;pG|e+$t0Nv{w#WW8^Pz{B-3IbI{V#Ai z_C99!r)zGB!{T9f)p469r1iH*ZxdZ*F8wQr`PZ09Q3gSU(=yW11ogf~F{r}P4SPE- ztX|>BcR`5cQIZa5qb(?>gMp!eEi&08M{|0a7yGhXZc7M9IFQME_&s-zqg4zL?2SzQca_Vl!_0kb@Wt}Y zvE?cyx_MJ-iL7uN(~|tiG3AyYb`(?81&E&<>%mo1Y6vofd6{Xoh%-GufgbK8;g~l@3rKsPEYi_+K;sbwz>?rjP*KATU<`-2rdw)F@ZJhf+Dqy2)0f1ig7Vec?ad=X%WNeVcuRg+FMW@;wN4 zoZ{E1((v$d=V1>a>BB0zJ@}oDdt~5apadXtl%Q?7#=w0nAu>Htz~6X!w-X#w$o_`X!YHQ z+t>a;b$DeTRd1!F1#mIXGp>ilZW|L1mPLqqfI|CoTte-~IuCLJ#AvEy9m!16t6m4+)M!ckBQl>xy$X4Bi zFaEOAm{slS9_rJ(m$ZkSk7ZyZg{L|(H+tuD^AF+_cS8-mo>kBB?uu0E@~jLJ(Qa{q zvnbGh-95c|^asm7pdMeKIy!%>_VdUd?z+eMlNx8rYW3y``BaG;jUT2w4zBn^C~ zl^208i>byL>H8z`LoK3qzBW))7K>SasVuR3YSfcEImi5@3O!Y|_E3*Tmdrk@dS?bc zr%W#`{sLr+rN)%(MI7V-_3y#t_!d(_KX4k4cRtS62 zllJA_y%tGZvsptANh>G8Lc@tH)H=RPMEdhg8PXR#NAp!sJ8Cy-#Z=!%HTW3prlB){ z4^gcSYSOOeRy5}W*rov9Z&dtF3raoL z+7G6(jo62{(|5Wrui878QT2;#%276Tek*I}vZA(SzX-9CrtTsTTV`|Au%*)5_VfG_ zuPJ5?LoYmQ@B+U-nf6p*5YdOTB{_3})#JzJbt+@&okdRJQJy0yMB-6vC zZcVgztwp{oyp318(Yitxd$3*-03*tFsu!lGG#w1awI_!kNxc~IJ;6z=Xz*M&G{2p` z^<}fZ!A^#Rx`*AF{UQRJ;o^Lg+;?Phr-3hO_~}MQyV*KE>O%qWLV(X>;`%!1f(HBG zm4{q}-8a}=ekLc@j&9{vA81r>(4QHrWATxT-+&p<)LRd=<0V_77WG=!2C3169gG(d z%R2N{&VI=qKUz#UeJ1}kru^gWwrQUM>5?aGc%TupaNaXr9{h^)esF+WxYjDS$zXip z(qgl%OQY&oIL*hnrWW&4oojhvKZ!oxf&{ed?~q#!RCG%*(%j849qn>HR5bY-jA|1pv>m^9j$_$FA>2i6%U%QB)8H$YMQ? z@XLtCmM8d;r2WX#sxdmIe{{p-+HJ{vhTPf$m}ufeU#Z(gZgoF8e}r+K`muqX*rU_z zW!)?p;7>IaOXfZ4TXo5 zt>#B6ATevJ83oIovD1md`Q?RKj6jBV-{6Ljl1M)IINR(O@eA|$nV8_+6;bjy2F@z} zwp+m*TaJLMqLb{Vn^L-HzcprgE_epJkQdT=I%D#|n}$Vy0&0Up5@xQCm}GQeR%Pkq zaAWqD=ANot3QM{tc48Nk^cj>3v@@F(p2b!jP(!PUVb-%LAXOaq=<^mhoh~(!i7(M} z@pK{~(h0dq)P$Ev!t!ntsjtIyz6Ir`{J1*SpAI|F(Id0hMUue^5n4UL2Gw%K87ez~ zNXHSz&!}Lp0Xd?W*{Ba9W#%=QD1 zOp5|bS6Rbipr%~-lX%{5qf46QlN}|P=h9ntJ7-800pMlza28 z>OnpQ@|wI!{|Pg4Al*nJS$vE1HT2S%!llu0O|e$?la4w6uEIs};qSau|GDHXp`s>I zgO7S4bZ$q-e5y`UJ;nkkpRbF0Z4Fg)__Lh>JPKRdxdBs*=B9VLp`Gk2`x2QP6Yr|e zhZ5mYDrqZJFCD5g7u)Rnb?QLBQwm}U3gr%$ER1}CENQc}b9xs#^?@`dD?;reV9i3R zx8*$^KwEn6E?&kpLk3X*jQX}9vR~GfUZ;M}QltBrpq*P@+q~B1u@*%`v|5Q&;FTUe z8S4ofL2y}hWf>h3Vl)VbMaDi+woeqLaVZ)JZFPqocRrS$)o?0Ud9QoSMM&Y9T!DnqGGShilfrPx?I+EALU4^AG#7(Vfyqugr zl%pR+M{*~YbslzYITF$+$cdVx`-yQ^JHxbeZgD85@>=o6RL z18DuMoSI1BmJY&SdO6-bmR1<+8~o^f&ek;5UDkhT9GSk5o&-zz3-nRde2;P}GswAl zgq>hYb5yPY*5J(qCLg(a>HnV(Th0&Ww64=+8r{IU1AZ8p0#$8{dHzo5en)Io7rYf+l^XvK<^|j<;1SD}Z$raNv;UnA&L?Ek zrJ3p%*2R2@ej`$(ENR3;G}iNbP{67%lOKrlB*80tO<^6pd7VQtQfqCf(YVd!=NF}L zG~u7R^^5zr5)Jdmgp1bx__cRq{j+J&*#tQeIs0@|W(uC#BdS(X1UH<5s7HA^OOB7M zq#Rp~`{*Y1FnMiZqcif_#4H%6%%^X=cJuzS<#rJ z8$9s)+-A4z@e-!FrBiz06?0!$5x@Sg-P0sh1p*v`DyEu|pZ^(tuM`L8;;~%X3-J5Mcpeh^s z5~$-R^}|j{J%Z0bLh|{v;(2_{`dU$wIu*+p1y*6=t2!QWewloZR@Ul3!@Z;>4((ni z#fN3In)iG&BA>l3XGKZs#G%jh*sTO&>GkKMfu?%JleUiUcAU$*uP!~utkUOT8*YIR z9D$-*g1NMTwbnu?e&+^D?B1k0^NPT|+9-fBWQXoq@7p*+EkfBCWx;aZ_l(9-V+pvM zMc;x^B0}E!{eD4nngc_ClQY^Cfg|QE8hvMy0a@nMVPFguLuPgbJs(NIhF$%cjyoXG zvb;b(Lsu?Z5%;-Kr)&kwVJjB@$-O;bhc4>3bkA3_Y)hiaN>&xnbLZ^>>wm zlY^Ilu;bB=nhtD}hTwhBGW75;YD_NXAo_LkyF#Ys%PoL1y3afE)e^rlfZVC=F|3fDzn|`c% zD9g@b=7e@S(^%&;ZUv9^X~E$G_ucZT`r*7jf8`iKm=0d22j9*-!}(_p!;Z^ zuxkz2Vu_A_iF(RlhA7Duh+ID2B5K)!iTf1}HdzsQZa~dIKqi0b8YBlXsGF9s!O8Ft zTQ+{qst?4+)}O;|yNsmby}9;zDkpC3v9}BO?(X>9r|bD)S#>o5yMkyBW-0ag#?Srpkg<9U9CAa)BqI|165Bnnn4sV zw;yQVqmdk7notG^7(t>0$9AuO=PPLUPXqo6}5#&6;64ESjE zhirGO*5F{Otq%8v_`9->(88d5n*9SQZ1r|8J~};$5G2(yHv~INB;oyJitSo3E6hx| zr1<9N`nQ`m?mAGS{S^>=A+# zDeeFq(E#2pHqrPvy+Q^FWe!U?vN;zIDJ= zb*v>+i#!8oveW|HVo_JfD{qU*q5-) zZk9KzPD`eWg#*b@z}`k-TH~BgosS&34zIWd>beL+E|rwX^7PeE8z*+pokO=c?OH#4 zrOiK{1^JmDKO$RiH4)Lsn zN>V$r%sE$q|DVnT)FsuZlmp-2Sf?R3hjgHn^FXUg!0>Yq+q${aC8mZnlD2yx?QiM)|bJbuwd!b`RuM$j2)6hs6XUlp^``hQ?4+D9mK8Qdbqu$%ZfHeii`RdSpSg!`d$v;u3Bt8> z?lpGFdwu&Q0CT|ZCmw3wBKBCRlw!a$%yCB(^}&M~s?8k*oxILtU^MBvZHffj&GQLI zYiV0e3ohjf7AjN$jp}!)>6^WVWql#Jc{UK;6BJ!v!!iw#ALsxYvvvU z0y3Icvjeezok=;uV3@+lXp6Cjri?Vrxt%T$Cq^^fPBu8!eDS*IHn--xH$eAv$k~`> z1|v%4ty>*%Y3zH{^LUE5o+fo$P_oXk6{IcA4bg@B%MCD-PYIHfJ4=xEC_KllV`H}yv2?+?(OZkx#9JFfAHit&3B|G}c~ zm7`i0d!m^r@Jo!9H$>M+W7?e4m})ebs4#Vx-?H$HsbY?l;azL z(9!Jyt>v%mn$66q*DW$3uM5;}at;QyH+6~Rbtu-^nm~zcAbn@W$o%01ga$^@cYq*5 ztuiC*Y$ASHPrNEquy0tEo(VAfZmV(>Lch7olaCLV|z^T^tW05wUX;c|6nC-yuW#oSvi^&#iN{OK@QD> z^h2(rJ^k>^zv5HRtG*17xC4}u?Ma*1czRMnf(SXC`dp0qFT#==A_Mhe9F|5wx0%ZE zD0$SJ58Yw1&h`Rze`Z>Di@oiCgv5q^VU#JdRVG7*kW>uMd$0EVo&J$p1vXf>TGyUG zRln|@+>Fjy!lZ^N$QJEv%vtt>?e+pIG)1_gV#@1#w|D+KCR?vFlCcd(6Dh##g=yXL z|Fgisw-eS7XZtM-uDL*FdN#R5-Q_fa@crSGeL&_{@N|I;WyY4Oj!|b?@JbCY7{6*e z*H+@eEZ-*hHwB5;X?|nO8xWo%?dF*jOz{zR2?o?i{s3z48Ojk2rT+$}TkY*6++(sl z01RzLM{rSy9(>P{D(N^4=~5~h5Qsam1>TfUfKL*+L#HNDzyl*y;iF?8r#FXGPmt}O zC?>tl?|}MaX!2-W#*>Ing|K<929E?3Y-DtWC7a-f06SJ&^|!d_PIqkoh~Qk2knp-U zg^0wZ5DV>8u=1FdF%g|kse!@vaYTn|)`^h<>l!(Vi|)JdAQm3vY=R%iYc&`D{W0&4 zSrr#W!rPYF41rg|i^x8j6b4at zLGB=@S5;&$-B7EOn#oJU$rAQpD@$;im*hSw{Ih+hRX)oDO#-b>>-cJP%vH1sIf+%R zQF?wW%F*r<^n10=i~3($!F& zqG6UcraQ8o0jmp7n$G9>lnUf#auJPHvvM>}ch*1kNZ>M;cnJ?+ggw%~n;Q3_wXAEcsz=ZL_o-gI#NxAV{=2;Vl> zU1K<(m_-mh)){L7N7k(sq-*ffg9m0@2~~uqpCCUEgI`CyN;~+jc!Uif$M9tr=qT6c z4?;64KW+r3%J;kkGtwI&5FsLaj9wN1cCrW))lnPY&8Z2{+c^HwemVuNKn22(rUBOA zvYNurn4I(3;tN0D^mC#90{%mYFH>32)?%&7wv{J0@ULi^vqMV-UT{X^2fF4DK=B2k3m6`{`^( z&MJX&NX3Qr;>60N=Sg3pT+-s{=WsZboA^#*EJiU^7`l7w|MTTRu*pZP3}@+R9ozd8 z_0i0BHVe-gyi&v-(P}K&BRNdH`0IcBWHfD?$(7GUnV(09C=3O0)Qb_%{sk#Cb21Cs$)WBeZnl0)mx_L4;)tw(DaTbkc{9|nMU}d*uvz^W4s6N_IMEY(}xhOnep|=zbyCe0m^zg}S zc(~qTXBU3D?QllF-(%}AHv#EE*-$`;I!$4|O7nMHUf!46hy#N1IpCEVzeB_oTFPBbh8um2dlp>@<8uY0@k7q2F1>RjOy=<=Jv=s z_~3Ordu1()`V{D6eZS^-|KEhxg+Uw-={0AjO_;7hU(CVpBKOKJB*=%lpoG0z45MF)VlcGD2r`l%l7zud7G zBk{`kgZ0EYr&0S*9A?2SxiW1$;_W%rewO*jm15R}8dHPJY!TXo}upg~SzdWU{lS=pE#omMjY!o9Zx++V{Nr%&&0mRzlk&Q*p7jNbfu zA@QN9iy;N!;r+-3yQVhl$HCmI*~;kLrn`Nq#lV`va>AYY?vCnX=|jDMv5#Iu}Mod3~fZt7^`JPtAD8R0S@5=TWFor(A8FJ1ngqCL#o zy3{lUF`}aD+TrW(%Z$@TkuQ52vmmkV|0O|;AJON#HzEnYen$isBlF5Q*kJPCQ|}zn z#k$uk24p&|w}O?)cmqH)#|&-q;5=)>+_JE#oEC;MlaK73vb;O3=Ik0C!rC?UQsFPI zhi!U2vm%<&wF$w*Zrm+l$mfFMfx@0XmNZB~jg(o)V>BR{Ejlv|AW4m2jFq?qaVN_E zNY9w4^Q5Y4Je#uj)ErW6qN{j9h3Ppbh4)nk)ovYExJ$ifqQ&UekeoO3kNYRnv)&vh zswPjYFB$AYi-nx$+e@X{scb01LlHH6-<+gmz3ei-u9*d%wxLq2Fw#WjM?&xK4nt~2 z*w>fnpq`0m`nk=qQRVycBh-gmG0JqJ%O9XAewpB> z(IV6N12Pohy0J{_ve>7B%fC681Zn!X4%#>^2YEs>O-Th@j0Tbcl2bR$E6r7xlMRsA zC987+AU}$8ChLGD&%PqF9j~%pOwOw*^KLJe9lf=i^B1MM-a$5Fa0ukw!YQ61Ph*^& zOLepsC)xk^X)^7k`aIrO4;EHn_>`-oDcq$Jeb(PO>lc0=tk;@aE~m)K1GQHezLk+Q?-@~V3d z17wV7J=%~mN%orPWn7Ea5`?9oJ-bH&tm)+ycH$^qSl*i2#nQd2Yq6}SrAj;%$#TqA znwm5q>{g>NZn`7mWUZ;mAt#nSHZ_>T-n)=6GX| zZ;5k`?f9!YaD_MIxI=hW7{W@FWoU&k^ zo1q<29ZLb+&X3m@Qn7#%T^#Gg@=|w!tICPXGw7$OIyRO8#+Z5}Qp3sM{>AQ09t7{D zfk7;+e>LSQy6ivrY!H)@EmP?!UuzR77VOzlR&T0c9BJ>yz2IAJR=7b~=V&DOg5L zFVS0S^rcf9>^BZw`g=aCZcIq8aFtLj7%>C0_DwoZ=OtDžmdMEpGKsc-V?+C4vk zH0q>bPVqhE@Q|%ZJ-_zj+vq-+%RSd%m$Crig_L;D=;(g-&haN@#Wr9kNuA&FKjn(^ z+9H-XJ>zpJV98$KjlxVlZF=>g@4*GOy>LsaxgBWLH%;{K$A)kM!xxKA%qrP;?Aqli zTT7<9Ve_fwGrq6Wb)vT7h;8M95tZ_uLne_kI$4z+2D*Z{Jl3O5d+}ig#~_2F`&@*% zcr`<_p1a220%FGOXRzvEE_I=gfdr7S*H381!@&jN{eT9?dwTZnwweQ`XVRhSOcLO| z-IZU5=B*Vw8rH+Nvd|yjb}w|Qv(V&x$Z6-OH$9m~J#i!k;3vA-UIP_Yb6|GJ!{1qD zNgPIyyw1ng$TwJ@7IR)!I71s51OQay+}mn2 zsMHGg*s_ioq$!sGV5=I+v;2e+>Q7C*fA-_5elEf39i zi^UR@ZwPu9CdpKo^~zu!G{z+#)D8fI~k0qQ>Nu7=JT?aQ}-!1mA>`5t60V8ywrZ$ z7V#72EH{xEU%k!iO!;qpd~t0R0Q09Z0dq7<6<2AcMYiH0b;2opFS^aM?BwgOnc|t1 z5nwp6=|6-C8vl|g_-BU?7g5#&Ez`TlVg~x$B!95b{<_KJnAVf|;k9nZ$0s8(wxfN0 z$OQ(A_~$i>V1LU2OsY@5{cJGwkSAM8_|@IzUC)1tciJX{>0~(80IRCRcPhuw-uBc+ zjDp=*{RExGxBm8|UFJ$zx4if@*tNJ5QKg>4f{A-^n?=nNQZO3^r#*l2H)MIh*woh> z39kqj%UR$ZHkU96C_RaT$EcavZI#iM=A~xFoqQ0;xz_SercWg>o7)(fCpP5>LA?an z7CrZ>b(7qmpM1RM$UjCzkTj54-RLqs?A`{$h@YF1$F{C5F8DG~Y$UX}~`!;~Ft1=AD1TooC1J z6!m2ISMQ#FnAO^8$xW#qli-%zX<3{wv|^~vK+6vYXVqz;=cPxtdIz+I^|GSkmfa?> zT4ZjmP8HNX5U^}p*IobhZsHHtsRlZDbzbsn(Z%|#r6$6LJGZyE(u=3cXw;t-5Hz#W|;XO z|Gn>RycfH%v3K7_WkjD-eNIQ6iqln@UuJ&UVK;k{H1?2H)bS6J$c$ZT+QQANUR4I) znA!XKhl(GSbKTdNm4zF9I93_*r+xh>x6vqVB4h6^m$+PQNpLNeIWJ;62n4$)eICmZhn;niI(Se-BuU9ZKn= zTdS&@M(B6@ha2{nhI|8Y3qg58hRUH}ToL~-%8Zb9(tl zZ5Sq&&!|U?4anWa;;Vgrwo1`AeH@?e{sPE}|2-GcZt8I@DDM1gnZ={9wA52u=DnYH zdufbXg(A>bxYUVEHvY-&RvO` z=SfzqiZ4@deb$&}%87F~v{sGPcciqmeTYM6#L2XX!-sJ!%Plje9N`k5OcYw9T~Pod z@eF@mi7VuHq|BI8bOOZ?vH0SMw+&`K$MZk3EjmX(t};KqbifQ5I)9D5r7DTMxywLW z{90C|`c>JQBSCUj&u~JIEjcel?QqjD2H!-ebd@{0NO@3B%&1JSpIN!YR}g%yZHSup zCu0w`a=4muP2IvmEQZMT_T;Mlrv!q^bh=3Y@4&vg6q>%YkK$;pK6#di=<{5)7R!t5 zxF3ml2v9vPj_oS;7@$E->RoV~Wxb6wxvbBIF7;Aw=yslaV`sHRw{=P!p^d&(eA_Sb zErn^Sln$fL^zS5HiD0=plJUMSvx-FEj0a(XhlqY#J+~Ok&Y4XSD9@WLVGbDoNhX%9 zE5(8yf0FWe7glgnFY||qpVM|IFMlqZ7h(4H%T7xURSE$fjr9pO?(tSe!Ym(HF=dOh z?wRoO%t20=kx#jYR+cY9>PC#v3OlcW@f{axt`1Ta<0pyCc^YY6En2m5^6le3HD6Z6 z?&Psbjxm)IjnU83l(<}Hf)Qz7f-kx71sf64yk-?7IfpT*IDKXxNG~80St^~ZtvP8a zS$?g3aP+DFD|uWW483*8(xQo~8P<5TcC_ig8+aM6dfi&I8yRa}YMR$(_0vPq=9pu)OKDnF!3Vzg1a6Aua8k3_@tJ*ZGPsW-N)S-g13}*VpzQx2>rGe zkg6|WvWIx<_Vo$v`R0;Kn4+V_Wd9rL4s8F-715a?@ZZqE4ZqX3)#qMk2P60o6zL2N z{aoBI8BX>i-SuyVWM*`Tzd4a%VLzC|At3%HyN193kh6=co4bXi+})#-l{5}6vWcl3 znEe|8GK47kN66fb2$>w6B|<42xU1&ye_Vy7)pvB}#4_dnhqAIWRXq~GI(ZQ+} z{#0mM^+wY-nq=Oz4Qm?T6!OaW>;v%7cC-F#cjqt3qI|B9iR6q(@JA)8$ zB!S_o9vFle=?%0lJ04|@q(!L9M@Hi=Lz9p^A_te3o_a1Q;XL$-rx${Z~F$ zpv5@T#X8dQxB^R?wu(-krP%QX)hT`}K`WVj(=5Brf-Z~FckF?pc!KZ>5w?z2*;&$M zN@!$E7oYNYHMFqZ*k9=?hVujp@@MaQQCLpOASL1l`0Dk7;z8>15ShomVAu@FxK8A} z<_hP2rpbX9P>3NS5Alcpr&az-H5mi3hUQenWC9mk>*!YzdrlR=4EIz@S5qh!tUm)P zG)FlWB?5?*<&wM^Uwi4ZZA^B+tm<<1N7Sq8VeZ0x!=4yKh|9wKFFsfY?-iNmnooY_ zGItYtSPcq7{0RQ$hB^?QQtxLuO0sHVaaS}15&B6oY|;c z(u-F}Jy~w8`oWb{tE)aETIp21a7tKecZM=d5=5(7GW*c^d;^`)Xb-(pX>O?EWDfU za*I@hnIU-^=c4KASenT_%ZP{845eIBW0PCtX$i*pVpAO1;g;HZXk*2gmLL_yS6iAI%ZyrC^*RWJ|&NqE%fy}yNU_&OEFGzIyLJrCOW_}qg4G=~N9|lc|2B+Eaj)Kz39jq&T-%@ZT z9nP_p!c2=KXkDGmtet>i1p8`I)`%Ppi5tgTIkRjQ4@6CehICYXFmu+*s)gw= zL@~7Wimm%lyIO9lt(I&k@j);K>p7a51M2&Lj4)-o4CJ~&PLC2!=6BGPJPE{DTs+&B z0(zuZX}nB_rM+bbU%ACOE%MtEWTrSO+-^lE4z$FO*c_kM&|q*MFsl+aJRcyI;E{h} zl`Kx4kO9Fn@PSI(nl0vQ?Ri%anCw3}+{K z&rBr$I725wjktA(Pc2BkOcb1B_G5%$p3c-^A|ia<49nU=g^C*?uXoCVlO`*p|vzI7KH9L+ib)kg?K_aKEpoe|O zQODm+dsg^tmjNx?Ux(CHxW?3-Yt=C9FoRVFD*O-BKeh}5)lNq7*W87@s74Kc&Rcm= zj2DHhjjod@xD8zi5o!OBKMvo~HW@b^_~CUiF*;j`CB>3f782^QL$eeZnWL0~(Ao{-K8+dhCTb=SAUpU7x*ES&_q^DW#St0!0(9 ze*C=2SmLV4vjqtu8i0xwPg7oXAg?7PkB1d4oTX{ZQ{Zn=-ba_L8ZDfTs*{_Xib9*h zGsw1Js#u9jXT5e9L3T$IJ=Z~aMdv4C?rdxA7pq-n#xGV?2`RJ^Z6N^~C@$yJUSIq{UcHd&7;qYLS#>Q~8q?Io_-tZZYT zwig_JI%mxXFBpG(pX~~8nTR4Sb_88&*m9`$o!0EYj#X0?eEm(mT&qL^gGVE8p{CJM zek66f2CYw(xujMs^iYpmXi;H<|Hz&eRWhYL0l+)6{na_cV5c4?+^xE$2sc5`j!c4W zG%Nz!!pg$Pt<``f94ijN-*2f}-kP?tLJo%<%bLE|F^$J8dP^rJfycr%??|Kdqkx%W zFEIMY0S0%De)J$QgHe)cGI54iW0SX{vA4oSuFoFmr$|uTtS526Kn#SJ9o(2;Cf503 ze<&xx080-esQ)rLx)P7~$M>WZo58+|-^Hko1vEH1PS^b(6(iW=G!73)iT&Dx<_{kO zX-PR|wyI2*?4i-kPcgH;1VvcQBE5Yz?-(`q`Ey>14aZTyvDQoFmvsH(GZz*2_0$zA zF=IpD)&I0yWZ7FH)#7)*XW3ybii(boUcb*vq>(mH_k~#EyKpBQvp$OjBPZ?a{{^6N z+yBXWZTW3?zcvLg(0e5);!CC9UjRJPhKG0rHNS4ZYN=ENkFBFP!HaqW*6wE+XneT4 z(1Cpqx^EcN=akDqrVT7M3xBYj!VHWg=NDFLdys}J$`Gkp&f3%`nJu( zEe4rdW9;3DM)WHev6j3$ei@%oEJ+iq@nPdKLWL%#vw(v?Qw*@a=zQ0vAn-2hj3J^U zaLYT_p7G9}<6)@FKW_{v#>Y>?m=_p!%2C_(<{1hFa&kF0@ZbGbzBq{Y?~mAq8-CoM zX1(=9-W)?2bcb8*F6~`3Ij{HB?PA=)xru#|_Ww^iMhZ$;tNa&pAWf*=eR{J}+Z&$Q zPlw}FWPW_kC8)`E3rajc#$7-ax}2|?qvSf!)tY@ox{l(mv4Nzn3*jjPAwxj?q=(M` z3s7p(UBhk@{7Jgey-vzAd{7^MDE<)`+ktBiU932XBB0G{et8pLdRc&Zb% zScGP*>{B0Wv8n0itrLD*GYkI*MrTmOUjUwE$sQ@f5=l%lps!=kphc76H!c(Qds=|P zwjCGZvBEBm!X@DsCSV~M7%C2A18IVT2S8L~7_ew#Gz-dA+iGvLm6&RESRgwSXoU_bl9% zHAR{hp50nYXH|ji1F}&i<}(KQm>kz4#3$U#i`>7b<1FzMc=%qj&%sw$cYHYP`DY3N zfl~J=^C|hjBkFuo4obZyBhHsE`uVqT&c5uc<)^9ujyB69H|%gO3*+y$73Dl`d4_gh z;c79{Goq%rJ!z9C4xMXa(DnS|xUkoC1RJzVKvpye*sW~A(Yc;`%8SLW9fFjT0_l9F&j7EX(BLFhYB1sTt&c3U?^-Hf8Go$s2`(!C|u4-x!A7=i`opgd8N zPe`Vlo&}`fR(EfQbEYpT2_c$I#Y}L-SvBvU>MOMJ*}HbEkETjJ0D~{a5ie>PDiFrK zh|2CFm*aCf6NBvbS((##Sq}S3O?qmCn@l zYXj)rd`uqhieF;70Xsj<;DeE}%bAW$n;Yx#`8yH`1pLw7D%<&ZfU zLamu-xx1lAaM;T%cj>f9n;^JKvtW;sS_6>IyQy}Y0+OH7DMFIbdDZN>)deHOza5ER zW3M+-Lw&&}rK+WKaV!1qjVbew%aEMiFF>td7_y@+T*_Z3Qh9<@1}2XBQpeBRbMao&#8N)DFh^={0(^U1WV~x1zsmv+V<|4X7sUlCH9U62_j0n4ah(`<=A?wh0 zMibYz)`;5`;+orgDU$7yZyqUqKDoyAUD+Gir(KayYeb=%wcPspeM%ns71F&7Xp`^* z)$=k_yhpNiEQ71KMO9~yO$8elTT&uOiGB*onvaw6&EIT0sQD;zChdoXCB&}1G&}sm-K9am4ljN`@jE-AN_0kxRZk#R zI+uRKXQ=g;y68I>Le)shAKR<`5)4D|$o48!Egbp<8DTNwPS{IWRZwEqO7$(Cx&(aM z)wp*KAX)QZd#~a_ol#iV{gpX9w`Dc-aKj<}CDuN!ndJ>g3XQ6TzOvfdHZ`48!aMfL z&^mEgxH`jNy3xtc400G`i`a@2aw|I(o;8pH(}?Y6V!_vJbxsNsi?SXl~zYcEi}Js5H)%jG+|1obY;-(DI`(nMUiaOtoRh+|S>a4EC+m zr}c$HV+@L#*-$o5Sg&|2+<(k?=PW{k1OGyIO;Nb@=xG+9q&ACRIi0uN=*?FEx4+aR zH6+{ZU6@w(8M@ONb=P~A?U4LvpZ7;rKz@h7pV@3N^Miu*ytY3yN*wvPXrmHx$W^?j zDA1$h{C4I&k~)p?D`uRHgv&KhjZ#~hy-l@QrMd)pxLXPw+QBq9gZv~;A;QSQfj!yj z6HNqYlce=B34~m>i)H`!$Mgy_5XXWD0^(@3<;_LV;3N42WB-gLvd-`;Mq*(6E{RD* z{dWPCq{|HgL)sh5Fx9OlDwEXweyJ~v*Nbw91FkzaLS}oKdA#0XlIE}8y#t~ff9Qyw zM0iAO-!nA|Ni_(1`QI|jP?96|r(G9fsOQ(A#gJ+=lgWL+vRta9D>T-z+4aJp06CvO zYlYnvp0lE`*k2W4*vDS|)XR3nNjCa!_D1fGUekT(#NX8rsj~Avq40)Ju)no%>_z23 zMAbea!O(#h;F`jh?#edYrEM=hBFP$R|Ws67e;Dc>AXEbR|gV^`TytXs-Ej0wpHH z+P+VJTNN77!T5FXsYVaI7$0~A$uCCK7mU54Pi<-d02|tviM!eEN1eBi9DHcUNCODf1F2qz4gAsMSy+?;- z`Q~(AseVojtwFb*pt#ReNbAHj%mMS)%L8i4a6254tc(Z{s~(UW(2xyk0pPKVBGRJD z_7HxRAU7edQwCoUxvt^_>@?@#k&rC!$P07ksSi3!+OH2lFQ54{(c>$laUsfqP%gTR zDV+(2YBR|_AUDc$Y0lw+eT(j;y{GfY|4P-6-%`OV;n{v$suw>Tpydk6V&dQOW!$wVb&5>Wc zs<;X|uR~eNNxgsw2N`1%iaXyR_!rrvBh#v!gCb4fIaD_4Y%7vLRw91RT#RDqT{{m0 zZ?4X{wM<`*oXQMM%BN%=zVn)<^jVv;%`6^g!1tXG7@F+&r|d$EKjU`;!=5O)q*~8~W3YA& zH}+Jg2;x>7ajd^ok?=Z};K=p5Y^-FUZ?t$3F-izP+qvjK=zl!wvru{{kKB(k=LRIW z{i`lrrKP7It|7yuL8^yVNU5@PD`M}#Pl_RAG#zf zq3FRX1fTZde{%oRam5{kE{X}Q+9X`nw7g{4=z(}yAN!w(RJuxSRoHTj>xr@U{eL3* z2JA29s$;;pRgcZzA}FLmX%p_c+kn31{@ynA=%f%WT0JUyOgO3}zxYqJ6&o3i{7b3n zY<%lP45lCpVOt$7OKiU7AGoutu>5I`fpVX(iFhi#;40MwQ>D6?FD6#}Bz=u~u?Q)bA zSH*V6!6;t`EUefe+Jc&wi2?{6FshB+>kaN?=6GaOtvYDM26sPGQES{Q|HL2v$d(jjFt?$#>R60;S zWW-LiWZ=kVbVm<5$g_dX*77i{_SjJ)X}I96Mu=3Zcdh!xoGd-XVSH4R$QAkXp>%uZ(JTD~@cgkg@6 z_@xEqijs*sMAM;23yVFc%qwI*>hXop_m*`ZHOygxt(zr&c9Z+)y1E*XZb(39lwC<) zAA+aUmh-Ei2S)x=to?-*mODLBvMTgSWfK>eJgf;cMHYXnOmW{tLuKf8kZEYE^v#Qx zCTsjvE9(~{3>Mr(Hi4y%Vo+mk`rK)Fn1tsyCA1(;u3e$6YhcCMq%+cDnOF&=Zr)1c z=-{R$;b2T>oAgk^9(O#VczK~0;Qm`qWw;RQeBzqeZ0%!!PYV}v5~~2&(cPc2vSdRs zzQ;6{2`RA@9uDE^VWa?f^i1<%`WlZeVEN7&5E zKNEidSDET&6;Vm|K67(X!a@mLBDUJ7>QYP15oF>mwB}`;W=KeQcom;nP=KwxwRBXY zh-LnA|L=YG-%>4R+qaBk;=(Aa9&w@q{`(~z?*b_^lmb<=*!QUT~Z3@(|Ur581F+IH9@HzDqf zS0L?KzVmFJgD0#V!p(aXqExo%dN=i%_;oQDt?|3jKg{ImmFxS>E}c=?X93)zIW+ox z_d7aJ#E0kA0@>bNITU7u{Igbv@|ddxb=QHe05#PJ$FINbyZIN&{YPUIgMReF;$OlJ zX2|VrA^~lJY==?BzmsiPn5)4fXso);lk+{b0beR5mfl7Zut=-}q}hB7)38EWXG%xF z4Rww9u$vvpAL?BQ^`#p1Pb-z-npFu`r9a#EZovxY1T9_|LpIl&C#_;xh=_%(S*)rI zk!%+D!*=O5LvB=n=Ak2w#<|k`VD@x3Kn<5G9d81*JPfSf++1KNVCN5H)^T! zZ=5KM`7Y;I%lM6#m9LSWiUs!SXsy}J7TZc=ZP z42dfcXYP0|;a9h^Vv6&x3$}a+L1Wi{FBYLxe6mP@N{6bby??&NclwugBp4tk4@Yc| z4zZl12Z_*?o)W^>#k46yRGrw_l0_Oy88>4aKOlP_$l7}E@YNb2oKR5+wy8vBpbW8G zC_&UL9LM%TE*v==lRIg^2Zb(}P73kKMgkIkKb;J$4k4q5NlBqrJ=ag>%a%)ndfH+g z!q5O?>Ik25M*t7D;!Z;Ccq^UgU@A0&1xF)t3?*~WM#I}7FI?;gOQnoBz|Q3fm~Fyt z(Clq>wRB3J{B8&xtFn-N-YrIqOmJuA)%9_-xVq<1hsHe|ee<_zU!B!Ui z7`=E0S^*ZkBra@&c?w531QDjJ$y;G`&4414TIs6Rfo>z~6`hJbrcM7`4X`ZH_3^43 z*=1SGINqWgkx50W9St=+(o=XE-3pr?;Ebnj6}7Dx%5U2-h*xm+dL;lu zFwYd2QVcR9xZq7^l?M@(pT9o)GlBzi;jk%!vhuB}V!>@PO=lElex>u0)0pA~eq*QO&a|0I1A9lsFZ zYL7vQ-Z)sfu4Ogsm+H8WR67{HjrNns3ZL38hvJ61BBwV?+gUG8hHliwb zO`vGl=HR35du0$)l32mC-L*Pwj3WdCrzaVv}J^XT0^%Pd}G{ zu*55cgl=O$&vO~n9s3a_mSiL_HHw@4G|#flo-6fyVs>&gJ-cMKxg&&741Pdisot>6 zuXB;}cQ8g%Cm^daU}nQvlgFV%+|vT6l;FTx^tNPNVPpms}|~ zvF*Oi@%s5ynDBBre*nH($YXg5TlU7AC?f~l7VLzeu^VMG45W&I1MaBJKog4eBou>K z#ZuSwk=f9CA@a61_P$8K*bg=~>zZY%x?#-vk3{ED{9|bv8mcLG9d4*z+B|$LiQ5i7 zg4n!=g64`g63-g?vJiPJ_r<9YvJqNf85Fh{5_5!&6cwa)b1{u^3BDphS@f}3ykFrI zx#Ol?_SrsTv^lvos92O%plstmVgHR^<-X}=f({oazw** zN6Y}eTN8`oa1NJwu5NxhQUwBNpg3&-{IXm_<0>m0cO*Am^BQkffuEUAKdK~L%?B*B z`QSb2$lw&;DsH}d{po2Ras^#MZT^s81UTm4%lis-9Y)>IEGHqseVYpj2h4W&q-R8r zfGuHEnkA!wJ;9)~2MEKKYT2DD5wLQ}6?UGnS9eNqvfR{)%u_09#&YRcSAe^p_fRZK z_xY@V({bwH6nRPrY^Ft23|H5GdJwqFIC+jj4g}@15s89u5r&+cI+nMP^nnnO>9~}b z;T>PWd{{*0YXfUb^avZ_%fHlHk(xAn1u!KI_)LDO=ej5;i%mJxw0faOKFu*xPLFP0 zNns#(6tC89W`q^o8>E%t=kb^5*i(-C`}&vekqUDUi4GwBoeO@xTbDmUzNGIxH@1 zWy0cSSEtreGTM|$6l#YE5ZTUvQ*{{OC+6(T@EA92VM;DQ&=nB@8G8rNW$=*Dl@iFp$t(BsAqxE|PHEeG7e`3z0ty<#;e zW7c**BvDgo`>#CD-{sR~AqwK@qhr~=@tNyokck6bhsVYzX%?PGDRE(koOk;LnlPcO z(wxv54HVd1k$H6_mhy$GW?;jK@mm-ehYJv}@rR>nG$XMKVWA&`OB5ZIS4e(T5EBJ4 zf%law!Q+H!vtfRknR*(jZ8&J7YiR~N$H96+bAUWJCzO$nZ}oY#cK11I!AO+=P%Jba z6PfwpmtBHXM}?ki1@wnBQRE4tMecSBO-x_bFA{YCfWOwix3 z_-aa%@A0}SCnAOh(jf14%XGHU-FuL8;@fn zJdzRJ4irjf*i7xi>PnpVi;oQK$@WZQ$5$elPADjQB5JJcrmA=i<8SDL2LCY!0a*|! zR%!juKRr8yx{I#u+6@Me{VIsuE?};37!)*&!$`R6zxHI$Zg4@-+EbwK$fvvdLr!b( zHTr(m8Z2&`7OR0gOa#f(tO;x!p5gQIX(@ZT8sb}+G~#Y zO&FX}BdlM$rT;iMh5r#`f;QqE=T^Kne#-M(rVrO|qB4Kcc{oku33sn;yCRx=)9Tvj zTc|?9&Z5&Seo&)m7)(|l$_NFzM6>ZI6BF~ZH+-+vc!hR-^hyw+Lx&4AXL6{q;XF)6 zg+qgW(8e%km74}DZEh&2J33oz35-rvL7H6B(NzNJzV=~2+cARaZ<4~qkXbz_GYpKd zmrWRP_akrkv~~xh&vfhq;F)aj++%AF&}CA4B~Jq&s_lhB@aRW+>Ezr}QJ9g#ig$(l zaQmbvCd`g_8e9S>g6UK0twmn{0;E(em+(VH@CXGk+HjSrvnlGDk}7_;pG_IEybui& zc#-^#f@@?kW50ka-eB8TRn5wTilN!kVwTSC8S8Bt4fGPUL3H4n%^uqRK4w240E->` zeIQvb*6rLw>_DYz>DX7TDm4d%MMIqc?}sK-vi2}j!%8Otp@n5AI!r^eU(_xE{O)3^ zd2sD;jzm5(TrVA=6{Hqe=Z}w7lee^j+N{-ddM*;S#i=|mXb@bJ3LILM1#2=f+2Gc- zVZ90@CV?+_@nEQ2Khezc5m4Mx2GS8wJn$|gV#l&{7)C(z4W?+?Du(kMuhdx4LP=7z zcaUi<8W?jWu@Ed{G~W{`V9W%|uy%FGhi5lh8me0$!BQ$JKJXSaf*Y3ymS2X*`7n#F@(4{`Z3Hiv}eq%q1%x(v+A2&56GJfNX%XrxUnOcBCKVPNsGXb0Y45T0%(Vx}ZKC3Dhb`E>Csm(VoT0#-~X3!h>h3vu`JB zwz9m8h@`bHKQx-VWoxMX61BmI-|Kukqu%IOdh3VwM=T7|8{FTOI&B3*A zX#+VnfJ22MQ@X`?pX-euOPLxH?!Ws8-(X!s8^;Vsgz@mX;J>^pZP=%$LUeg3U|Fw;oXu0f{c& z)5ZnRW8_id8B1=hP+5klJ_rEnC%`aaG!V0DCOOe_k)seGD@QbZA)VyzS`)ZCnO23v zAz8w=wN?wiE-xKuZ=nMO2j7c{Vab4?ZU;@qissdUL>?D7>!x1bim*}qpi{jLJ`HIa z@#}YMK}?3{;US%SS15jyGIO3&{szXs%|;%w@EqHVV`rZWgcC8b4+JSDlWeu#JP=$4 zeMQo9x!E@VFURuawew1m@!+{-np+@e-4mw>%Q zodyxY-=ZIo4O)q9`b=di@EN4g1Ez7wQB{CmBEQT!j3(fZzs?6`;t%Q@@FQaK|DtVC zk8b7%g0^|oGVgQ5s`aoz4|x zA24T$(ya;=g~n-yz&4LF4K$Kk4GfzBqbeKnDU?Bf8*#wfD6ShwWw4M+?DN_9Bw!dp z6F_kMYxDcweg%b9AlwcF02boaZ!C9l9z+xODc444D3jLJwFBK^>8$oFf2dTbO+(3o zt#*rFlY2wGZ54Z6ZV`hA*Aqg`vYSStrHXamt@aie4r%sE!8K`qQ9%)LvZ;RHLh8kd zE1U9AZV6WigG!YH-Pui1=2T1HkWxzF$aD@_JpNfJ!cNU};7EUWR!Y0b!j){aN^hed zR#&?LehlTEHp2g)?v$q?)Xw6=tPPDOa}=7HQb&-;;NOmVvKkA@{y8d_nC(^=)aJZm zj$?LLMdi#b9 z{Vl>yYui!dHJUa7+LhgYzie_i-&66r05gX{biaXv#nMxD)K>)#HD-RaLP}#Y5eMlj984V*px!aH z+Z~xd2r|$2J>JttOC#$jn*)AY&%3N^QRc}p!}9rkH+;C`l)643S2H@y1qkdBj^^0F zxmreQ%43_SL|ZbdcQGxYfc&*>YvYoc8q;M3K4W1u-p|h=W7ejF!Qt&vMagV1yhL-6 zs|^toh3&v8000W`CtT8!FtIGnBUKQ<`NE!i=Xy5ev)3k{0x`yH-z2}NsP?eE7^JR{pszGux_3UQT7c(m;ou-V9 zb|D7#nK$1G|uKY^;8#@9!DTadoe|>WyiPY7!{tGUvVhsRrIyHHAUr6a%=54AWJj!0z5#@c;ebX;=P;SD zHutVK$FHQ>?5OG$Z9iUwg1NAZsx~(pE4qx8*!EGS>^Fz-E4L|z1UeGA$_a;;eCO4JU` zR31BiVeX30LBMGKIo8p{Nv{|otU_=Xu(hQA-H`_j;e5Z1DSz^>ZZH4-dcBAjxD2+O zV;V{AqW)yL6!ULW-lv%MH=Xt8RC0|>F5Hg7bfKro=ui14-yBJXKnV$1FMH*+KW%hw zwC=g|L}n|m;KSpcz4*$7CE>89*xSIy`Eg0t`*((v-qo~(YE)GBTe?kwMoDKyENCj{ zZ?&=*^Ez;K&^*CpLYDBh86=tn{M?xZT0%u#2X^LvuFVv`N35?=SX+8CzdDydKs(c3 zj){r6zhoDEm)W0seEY-^$r>U>Qxjq{IKm7WK9`eo*8YL#ciWd9f)|oeu1vhtRY73m|23O9KjV3&Q;a-^itkKwe1cd6FOz zz5W-#O`KCRX!7D#{YH|q#z<7>@f5Fd-sv&m4^VNCcjgy16%Nm;2+0bhO9C{x&+Ni7 zLAWDd;zd==z-OXd_Zg#f{H*m%H?Qg+dXC%KFEwBTUbC}X9D5zhn9Dy12|%ww5yj(5=7iP10a4SXK=a z&JDpQhJEA#Wa?3VcBn}zyO0}<|50eVl?@CcYCnuju_AW$=T-a2Edt@gAcSfy=?k5% zKeN^sO|u%sTQ~A+{3NDF;VT}pWM~~gNuNjwfy(SqXG*siSmut8evQci5&9t0qF3FK zK;CHbmfd-}MOf|)Q>5nU@#&%hjI+W}gkZ6vb(VrBEwXcZiIMIBgVnu@7=%0C3CxGb zA+WeprX+s{rwH+Qco(c?uyINf)>tINQ(?(%2|HLqH=Ig0GPi}fz!bNYo^F0-9i0=C zp3|yM&5Hi1Op9Vx(>baa`4@nkWL#jN>LXSdAa=f?A0nWc5Qs1Go4&&-PN=^rltK=C z<(ouo3MBe!5`WuqfrA)D(FGiI!$c#5YPdb6YvWF!v&x&YfS#v;FDkBDuV(j5& zQysN7LX{jJx=6C4{7p$}DwvM;ZGGs$xVD$!6TI@H{ z$8m5rZu&ie!ft&77FM=2i5F0E7&Amup?EeIqjbWVna*r1W<{N@Wt@K;GWY55j?U8H zBl4F5GYLD%>SaR?8aafhnb+asmn{`lM!gHkRMbMmy(wMuX|rSvTb&8$C-}mpjb}*nq<~D>wycC}b)WtQp0omR%X5s#7H-g63!$pJWy_&V14=>@53)@Wi(TS4 z>KzX=J=`uT^0c91L()0~xXG0vHl%aXYfm8pPlUreEl6=*t+eHwJ?sQ{Ww{LD#`~P- zx>s&=v@!Tl7#&b&+llfR74k=2*%T87${Rb|JAT3XA?_rDl@eANV}jG zo?uZ?Fo(Bs`oT6Zgx*DCT3iy3AIY|9O>kFZ3uLdmJo()EUvEfM`!%8L_7D+G_4*qE4lXeH@+OKc_E}rq99V`2NFug2p6p`|5@9M@80XrxL7ri3>Uj-TqKCt`0 z0M$IJ&}mj@x2hfjl>x8gcWgRqhf?2kcGLY7A+k=y$vf3y`EqG^p-979(@bT4dekeW zIO3!1J!dZjGpa5Jni@Yr40Fzcx$BJrR(=e`5UNp|)w(x8^7}lG&pPgt`T4+erHiR& zLPt*&s#MhH<6|n%J(jfAlDpH^Z8Zf000W{|Hz2-JZ=;tZwOn(~-m}}2bq#HKv++c} zp+=tQ;@BVz2TbRDdpkhl@bn?y(rCV85BWBvP&QUkR)*7-Cv zKy_-*yk*Av58O9h?~L`g-2RV&x$jA_9$!`I!dB|-8LtEF-9Fv9=zYnYA>an~o4GUV z)1B|>1*|4so9H}o^Wm@GBCEcMMARqChs4#0;83U5Sinqe97w>-LDDpEP>de>NxP(} z-APkf#?S3rN^!TuIwewfELS=v$23Hxl{mZ>Sm*~hth4{xGuSaw$!A>P5cR(>hSYZq z`b;7F4i%$USL-M8*q1hffQ;5`_ZNV2q4j}F&hRk&Ei{7~M4w;fOoJP`nbmF7(b%kh zmjjFSM-3YH+bP3efatBV<8y;Q>Y3Qp4O%l*POvh9iTs1?k?4JP>i zi@NuYYO34%Mw8G21PHxD=q>bKl+b(c2uPLQK@?P4Kzi>W9i&P}KtQVWUZg032qL`; zh&(sy<8wUcyx;xaG45YCV~?G+=K9UK%3gD2X6?P_{NSI#t9WdVyd(lj#V)*&LOI{W zA1a}wPrxpBq1AeQi62&zC{*{Bq$H^?4aBROA=mPu^C|NN1)v*LJ3rW}60HL)qhQAsq6cqwNPSOp_|vF ziPL(V*n6EUIJ8vF;qHyhhAPz^y8QLaO?cmmvbg`mh*JcSU6FQUBo~DUMt%6}%;+s! z2Rn^>My{qC?C7mIX&veWR*{~U`$$$S=1Lj zyBHmvZ5}GN+cU1u%s_rjtJdESl(^?oGqW!IY=?ej@cdJrnjTqI+dhwZ@!oe3=x}t>~I4q>lzBy4Ix&Y z3GdeYaE!UiM}E!&TAwe>sSn%`kVhO4Q`Vo4sz_O8A9EjGhZWF#aYo z>4M7~zmGW*JVbID;?WW31L;Cg2EH8PgRZc1m-+V}<{3 zTtz>5k7Q~q@%eZl2Nqq8oH$YJj%)X6h*6#hSt{(n)9CjQm# zU$EXW1@Avt>VBtx4cRZu@yqLfmnrS{;a@UOCcwSFxJJ1TezT+Sg}*Kps{L<%f;%4l zyAUbwU*LY4Kj`ewr>^DH{sPIVRIF~=WF4M_3t(C`oN)_=SH#r4P4`f(MH2JlWs+W#5WABj1slYfN# zp54deA}13f%UV(=Q6w`HG~OuK!JjRcmYk3hL=k_@Q&i&TQU6jMuYrJF0H7U9^OGX^ z7vOgpDu2{y8Td!`7tf#P0%8RP{&jXYDqnVUs)e$xfc`W8h@aSZa9(A{* zL`1rqRR3)bW# zbitj8*W>V$;p-oh694N7fz-j3)Cq>v*Kvp-j_BUGV9N->0@HK6Jj$1gg=qLwf&DJ@P|$f{8-&id4G&UZ7<}y7s5A^cY;!ik@Fv1 zqyKdF2jIss@>_-7+SlWyejtC*$Ji$_u1|(VQQ5>_l%MB=T)tk2K(tsyigVgDuhKUh%NUW$JcIR5Y1|Hl8KjDUZ0!B7*u`~L*{Pda2~g0107 ziYd0`B=mpLw!hioHWMqyBk;rqC~DvGWR!X47yXab=@EpoR>44& zFFR={D%j9F(QB!rivtSta+y$bJbHcFB58*WdV}|p7&x~2Q7N&25JP7= zE|24#@Wtb)%WA)@mV+r?dMyfMGz|>7l7|MC@Lp0Kyu@pgU~;ypbcn6?d^^o|zif|$ zGM@KjDKd}1iQ70}2s32(s~&o{%J$e)Tk&J2CzSmZI4#>T6~!@Orb52WKEpQ;m8Ns> z>Eu3D4m3R|cfTZy!*UH=>x0axosDOu0Qs6Jo425H3$@MSFq)TA$)m?(6h_=VjLf)a zTCVDNdh$5Q&qHUU_(-k!*sunlB|bB-dIO}{;cZrw<&+|}7+G>2s@d2RzV0Y}o!-bx zUl~ueAEOiNt0?RPg_{deRbYSoQqfvOSwucy^sb2L+75E-d)gvN-x%tC?xu-3{^ZqW zS5)*#5EvB6+3A>|Z-_(F*s5DtPTu&i){MX)471o5Oj#Q!$pk7^u5e#W=RdkB=-Dp! z=qcUmqj8?DMQ+|NfAC`KjW4+td1a%9Wo!F>sm++F%0(L4cV*!GD=q@2)gpU~JQEi`Y@GmKt!co>AerwJN>EwG1A@wMpAe8Htn*M{8I8Sppti`D{340uer~=00Qw zYydO!D9HuRmqH5B@3D<7v+6#(kt+gDUGK(>6D0AGK6p=gd^(g%5v$jIb#6Z-19=p1 z7=vYw1LAg0T$CJ0vzme^e>?kZYk4Pv$TEV+@i7yW9*xvd^w2@zL7US=Pzcn<_dbHp zjexwYv??(&u-)2ebM>8NKt*?IW{EjF9=|55B(Ud)Uzo<%d>fOy+`bqpuQemCTCYBWH?M$j@xCC^8y}a%OP^YZQZ!G{HViY6M;6F1(bUwa{w`|O9E>1{} z0t>R&zaO2kVx8h*R=f@d1CW;Kv*|DMrjc}UH+kE`!9#TZv9b`vg32D|5Xktey&;oe zz_U9FwtR1=1@YJ<7=j~@p+{7#Q0nDky}qyHnpPuSOpI^QCANa1vo%-zG-0wHKU1mT{;KRaTbhFbNGGT2+=*#Q(4vWoSa+4bh+Ao1CLj;@YG2^SMdAiUA^wZRm=~ve0QP+b*t8f z0NNTaWnSMqbLWi=dV-064x_0>w0QQs@&v9zc?nNYEx=^zbgJxmX495g<)=kUjEKt= zy+0=@Nsi!i3Te!GZuoWl5x0LU80t3lRlSl`@!fq})ZD#6j+I{0tLHFGaSW#(C-Wl!*pB5rLhT<+q>vK-j1j zf#6=T^42$PooagKJ?F*YR989_cT*IHZ4)1!9J;|)k(%@o@Bu;#D1uXcdo>r$SNaw- zj0`C+aI|@AR&zdm!euJ@<7J1RVVF%#q6u3z?cE+syr5%)!d64+$?mab=`>m$Jm9@= zN)^rEwVCz+Y`-W*2;0`Bt#Zbz-hlz1(Vo4yt}~0KQ{*_c1lBz!zAYlT*=LA7Z7ltJ zq_8KC0h$Q4b+omHA`Dj(>EK{(kK;JKA%&tiy^k2d(&IgU0mgj9gEVC)5~DxH+$9@o zR(y9?$VMV}L)@>q9Y(r*p$6@71P=QDi>b?6hOp;yv9v@=l1NuK5-3gPp`ZnhTx;R_i2LRG6obAEY5Rpy)+W8=?1rf?yhK`!7OQLJQ5i8!>0kr>gyq5W!rlwqhx9ldFCNiSJkVW3Tl@G# zP+p;uo@44o-rCyJ?isGTmt&S}*d%Lwb;voSkpUU_2_BMjtCq8}-f4{DtN(s!ofcF> zJA`^Ot3gpXVBZku?O%Y^^B^58!y>@;jG@G<7wXkc9U#=s1b(>KW}PB}`@QkI6W7Eb zX7!f=lMsr)ZZfZP0>}I>%kN(fDT|i{R|FrrEC*i5(bK5krIn6=%e0=#;?2C-NflYc z77DuP5uN{(F@wQi4eN*kNffPok&pNOSvKYD`sR=AV zP`o$R16|+Xb^L+|q1PtNA86Lk#)1m-b|OL<9RPsa<=jFjBjtm#7BlN+KC+z0(Mgz1wTX;lVqNw>(*eCu3y8@Cu%MmBRoJcyR1PI6SV-+n|8M^XKK+=+J= ziUeb?9_rm2Kx`G^w#P<+!%=a}pg#U%>S&K?1HG&%@c#nz$&KBrxORR*)}aVS#Biyc z=)&fPkX1S?NB%=#)%>CSImdU;BU#TqG+z(0O!6ULbm44 z>^>_B-iH#p+!1xv*XeTFd+x*NPq#+IW`mYx%`l+|GXrsdJ6j$mB zp@~klxGhrVLX@{0yWX@FQQVbyO`?{*+MSi3GhTSN4T|>_2`EqA2za|jY_ZGH_MKng z$xina#Q2&PIy|t-#A0(}JRY^P{Dr7{lJDC~b!)4ThcWN3p~8rR!yi2FvnR2JrYum<|fG?)F#2^rKHuD=cD|1gb=E-0XzrOaz^?^7m5 z0I;Trw*q3*u=k6t^!KJHd(U;iZIXD$SJw9DiNw*3V&Fn!aSgI2^Y~H4nzg4C*(A(= z0h%1dEZ9}}cy!22cb%@XJPslzcZ2vS@+!A-5;pI(=T-oWZr6Mw!T1_?B;QB;Btf1G zD2F+iVE!<)g6{q~@(oB_^ug)H5`ENg@6xNgv(bQeVrU@Gg@+2c6|=iE>xI^PuJ2~2 z4!S?S*`AsWVtWChf8!Y4^wQfbA=dXD1KnQ$LeZ_#x+^j@tN#3j*Y4oHfoHZcax}g- z&w2gJUMSZ+1<^Sy-BGI5v^di-OL)qQaDj$vTzLdnNx>+D<+dFBZom4jhII}cnHxl{ zxqzfdqCnldi;A=+0qzw%JxIW%i?4MA%oTH-w?a@*e#8Fk-{uPuuLyNvWYDg!ywm#z zHhze?)t(UdBjGWq_e4v1uxLu7#`3d7ID_Sh2@H_dJq=dKJz4BGjaUUz$VI+S35zS( zI1&IOds^tHVIi(^y6@+zI4!heT*5mE1ugcbt{JURaUvL^W`zSu9b#%E0M1ztZmGv6 z21%WR--!atPBSC822tT4NNkeb*%jKH5bwba2^Curud}s2B7MhiKJM({%;pCJ z<7|=v`cJBIZaGyM);A?Yf@;V67=Z)#zoQhG%B$3dqZ} zZpu&>KJnPcAF3pFlhfZvdG;nenmG)GGzH@5F2!?{7k?RdPt7Cbb7No4k6N2-@{^b6 zn=g#S?=MWevis6>hh(sN@JZBB-L2GYd|Nakg*@Tmc{oR+k6*>=g**_RtC|m5wLp;?xv*?Rt7 z%MK?bi|!#Yw#n`Hixd=dW$DZx4kkuXU9P>U0NiVnyNjj|u&lju^VT4Hp!VUZ5-RMW z6wNG?QNRVE1EL+}eOv>oUKw)r;=WRk>i}c6=U^2GD*+~R@d}RVjGfgM539?1sA&lK z&Q>~1cczV4cP=1klpzF@Um*e3LAF?fLH8o7Y-=gum}o;c9r%2PB$b6afUG%&7 zBEd#SgfDVj-A56nj0!SB2UiOMbNvnxp&9!%UsC7h?&MjMgiTV0=f^O-l?Faluspth zXh`7}@9`*fAKA6Jw|boyE*+M}#^{(A*mOH-fZ4oZ4^PKlI%sj!&*-hfaP@9va|L!G zGGN|82`O(aoc?5xGFJYjy#|T!@rrE4n9ZT>7D8AV?=>rtHM3rJ8Z4t0gM6zR^CP~h z`BfN)K6O1K-eb3CpP5CRhQQ>80tr@zUzrQ($oME#B*#^6Srt9NCDb48;`s~Ez1nyB zp(m)mZZeW_!?M!k?u^l4m47P~^=bt`u7y3LrB|K-Cp|wXmy3qd^tut<4cR3?FssoZ z-|Bq>mISe?Ont?fwsmmuqbl|4$yaY<&nBO`4n#sLo@-b~L0v>|Uar}rODzX-0_#4! zMjjArt!~xmwVWd>)?YCQ4t}NP0Ehzfsf5G}H`1;Y3ZsWa=E$P;H3tz`Ee_++b*SeB zZWZ>;=}%@nk{pmX4;=Tr~>eCSDRdUsTfdoz2b) z@}Er}+w81{ALK4IOxT*d8=hRAh?@}tVqT3FL zYmFlwle0Vp2iE~l#M!nd;rk8n#Qpjnr>W06Ipg&Q*{bi%liKRIK>$lI!RVSM;aLe= zKxb#Dg^VSe!;aEsta-|bVbZ(jE>dS9aaH!ls(}aY`GHt*rP-uV_Oztzp7JiW-L}Xc z;ZeL?`T~g*(vFqY9frbr+3{H%-a*biWHJ?vgmxrtV!~4<7M-$w zW^!ezBok05R``QiAcNhVj7rW&v@pgXPeH@Sf*^hhyLMMKm%U`7Xif=LKTZYz1l0RL z35i10kGeRNJ=0|~`<8=!c)FF%R~rc#rUEQZF=2*-)H^N__QQ-LjDl1<|p;2KQUguJfy{c-jDu1b1280xc;FwO2rX;4f zreWTb8M_tgMnaQGNW}c6@oV>uGNv_qgkO2k=>32(fU=ojDaRK2fR}vGlt4OV^fVKZ zhYBE)q~IQ#DG|IKl19|=X_?fexE|Jb`uwR#$t5XP5DqFll(|ZK!(=Fh7pT{^D$BKwJBglDJ)xeP2)r-91ej4dYqEIIiijEW&JL}EPlyGbB^fS@^qN#tEi;k!Mw6G2s5pS6 zYLw&{R&sAT5rNCAs<)02f>9n$$WBqWP#5f$AV@6Rs0)q&pxH(4snXrUm<#Hu67WVu zhBU~N5qdRLDH!W1z*sQC85-0o<%hTJd}9|L6ygx|WQzhI2a0or5wiOfSXP>-h!qlG zjB`_&C@*EU`I@uOB+^a1OfIcdwojlT ze74Sjpc+`!7yML^N4A)7fmvt9oQ&59%y`bF#su0{kn26y-hc)>DtYTnp_bBeD4~Rf zPhnru83p+ZXx3u60Xq*_z85PHm!wZDDyS(5&}wWYjIH>E+_G?QkAJC%?KNZ99xX%c zo={RXQ3Jd>7KC=v%%wdX8L|NkM5@cm8;6Csnh3};H#1-asqmGc zB@7@^)gCG|v2qa7Fn?fWWW;PNoMe5-0DY%k66eTA21|4n0(wEMgcx+*n(|qRh`?8J z_WO#b89>ue&W4l2H@Z?bs!DtI=ZD$HANmQkEFnhzC%nwyv z9#eCP!KTK>G&g!nM^2L6#rKR-B1(vaBfq=$aYH6EHpy*6hfk@HG$m6Y?M%&kb#_Kh z0p-Xn(IhMYA27bFMSWyRtM!Vf{UJTlN2}96T~Qa4s4l~KWDdGjc{@3?EdtB?j6s*# z65lXg?Wo)ztuwT#yd!Sn7f38`W!@zL0MLQYKd4oJ_ROMIdCSW;_30*tzE zRj%gF9cnpUur)m*Uh2f&yNiDQ|FNLS9p@Rii|7QGWmww)W0CphV#A6jSh&#a?8Q~h z7jC?P^y|(ph}|{c{r)lNP2^m@Bbu#)RwTBqR5RJmrOoEcw*xMf4MZ`{fN{*|I1=A! z`igp$bH;Ci9BxD=tt_39c|8i#DrUVM>bK0Zl@a@o^~T=;LE0t^^HMr95fWE$vT;o* zOsbd1EOJ!_;|?&RG54Cd#r41&nSj@7_Cxe$2I7Rn3zrCq$!H)?$v)A+`D6qz^|_8q z@%VIk-7Kdo3qfjt;pc@0x(*4SFVpzg3Il@#%aiX6U#Olu6A$DL2p`8#H6?PrMUwE$ zsR{!qHOu)39uk9WF_{l1m`(tR;;P|oW(93VB)V;dKGL5YIF&o22DREr>E(xu^2ZuvN_bi}+b#ja5Bn(}&?%PoK%vC^swd=#4dXZ9Tuua_ z6`hfvU05Eefft&e?J*CG+$^Tm0NARBACGE(=xYf36s$3Tm(^)MI-yO$2)Spkyxyf$ zCXq=ti<35qQnm_B}rsUk+$!okNpYV`L(^D_b?XZyqc))o>`Ya>{%!f`N7pCkJ zAuEJsG5bP`JIVLDxOtWK)o1#SNB4)1Z+o*A3TB@cRj4@Vf>ah+*Y(DaczfcAz2GZI zzPJn`s_y%4)$rpC4R(_*I7Q=(H3VEQ0F0z@)RjMss7KTWV|+|tX4ht*vS|cuK99d4 z908GA>uVrHOmCE#&3XFlObXty`vf5siHWpLvFi9t_9;5ORZkAkWB5$c2uYwEIFKNZ zAXn1sv@bKc^a2-X7Jx z#dOZNlmZ0OF>Vq!>qcY9EM3tRL4HL}L6)yPB&EClgoGVmI43!3k-PKt7^8jD!y(HS z^{i5NjT}+vLo|CJ_xACAza;+0le@y5os#zPN>a7ox$KAX^dV^4L6BKNwxit_KS z7uJEI@@T1ujT*h`ASv&gwC3x~GkLR*t?1ACzeW96DnqwnL$>gTfrW-O@7{W1S?#E} z$@!P~ofu9ICxVrl@a%_Q5iO5JUvh4>#P=vAPw1|-vb~Sx@P>IM2aa*-m+O|OrvCR= z_8WqaLg7(1%%WGf#k#Ky-+wK;)mhoO+nMl5qPOWZq#sHmn&9)Q-;VJh&paUGV|54& zCix^(bNzGuOHUq3`{zYPmcn#eHx*kW>fQXhA@S*P;U#`|v;Eoji)|0IFUtiqn3_h)2CF)erLQ~18Kiy#&Ox);ho4xmre>d=j zMcQ}HI1iF{eYJ2(OXI0Z(|3fkA9o3(8o+* zs5rlm3eW8s#tu)N9y3Ba;$05ER=8s5PFbtHX$P{ytn|BhvH;aA07slqPvT-af&np) zwce-rG=F{(%Zu+W1S)`w6}CND4vL;Ce7o<~-W%|kDiL$B>w!gFzXDg%IPHhE^x5P) z6DeqjfiL|f2h2L?%?u+Yf&1*^?}-VWCJVbj=v9b}5L$l|QAs3cb5a@t(U4?Wt{c@xu;W>_#t1?5chBSLo*a0f(buNSq?x0 z%X#Re4o#6r#-_mGyw?xx%fvTX@oV{ee{8YIowAgt;B$ z31PT6EYu(xSTJ7jB#w@mXAwJtmCttq)3uqa(A7PMI88Z(Ky}D7l@D+6&|KT4-jOJ> zQe=$k43m7yXmI72L}LhoXJ0&|aZZ;jBHKBQQJ&n%3R%dF#!5L5JZck;gg7kvCykt>{@D}n8UceeyuXE$^8aN zcf(yaIren0x8T0V7W%^zMPtf@SZL>*ZkS1tqI!VD6L^i?gFV|BH!^NA@e7 zf(Lggzt+Tjd;8|`&id{DbwSySr)mBKd$Q_@LtN3+lZ?y$d=x`>WQHHDt z^ELwrV8?hhl|ByiZII#NX)}#nV-Zvc!idleQwAw=CpNc{zLsNzH{uQAo1;R?^PCnWg4yVf|{hS!~8VF=*+&PjcLLNK^xZ>Kf}?8$_3$a7#1nE11(@oUu6~as`=bw& zt*8inMgXTaj3s8GSPC9lp8d!A+(1lrrY@s!^~lqFW8*A@E68~%1V~67dG=zyD=9*S zP*Uhfow@pxfqy4H1j7kwD=)94Lq!<`WNhB?t6Z%fvO;5y(wYr!U->KvN;SB6Chuq9~cmj1_&N=`e9xGr}5yI|k(H zvl!9`OL^(U`FDnx$QTNx>pgG;goMWGG|{())deG`@-E_=oQ*@VMYEKu2YC>ptoBcZ zb`=oI(fPv4WenA!$r2)Srn*ROl$*{h;iKi-BbcL0=`{n$K(r2#B8VGEFPRmCQaroeC8zG;s3N4c? zZ?dOb;M!nW<-Ya501o(YY*Z*4%I60OVp8QL)Vcs)7R7HE7ATH0_ZvmXX&zZ_^uB}to8s~(wxqEHq%g!jPLmL zVq_Ljms>W7T9Pr8Ck2%`VCo27_>1@Ttj+moX?4G+xr;VIelo($dLUdsxOnG{)3><2 zrMn8E_lfDGcjX@8=dyxc!jhPhQfMz7_R!-P=GlFBkKX|^Sv7{E37nO+mC>D5kwL7K zP`FqY*=QFA6<~O*%wByi2c7`$Zc}zq;HPphFcdPo<3?TV*1@8PT!=IYZ~xo8opD! zFN=D`A3sWL`B_j}h^IkC#uPL0F&$3hSZShPpQ{wIps1m632(x9WZ&T~KK%YqWMzWSatiLgm$ z0V%NubyE)LHCTX22)zGb!dy6F(_jtvE^ZYHxB?MEw?-?YMuSxG81~bpZ^j!*02t8z^s$@^d?x#Owhr)4S;<;R-0%j7K z5o2veP2)sr{QMF(tl=JoZW5x(%LBp?4~s6h>Y%n6#yAPuuG{PJtvqI}UK=CnJZA0N za=Ie9wOfz>&bN9^DVj(^&^{YollVQw*~v-!rU%3J@1}TY@Ht{i7NEZ1BC3a8!vRi* z-3Oy{B$GxeQj%h_qWiLcmD}gY4(i6h-~-hshe8r<>$TZsLy%9M9||#EaftgQVtBoJ zQR(-^ns{E}sV@#1Wtf|9z{{g}7+7$@@yA2^+PDgaaDtx-(c=aICbp?inh$xw2!$Tr3a+nh1{A?H$-){<-&% zXN9N$`Z@rKhMU`FUCh&>RRpvG3OCM?E1|bXEc!ai%0e)^l(uf8xzF%Oy-8xZpZT?t z?{U9_dq&UR)L9~+kPx1lHXOjlzX>cREAtkWIsg3o-xS_`nbbas;wOv0|6nCAkO%oo zqyrQw&Fdy<6;bQ-#e7FgU)B5J4{;&DRt9NK9-f5)-l>&MXx)5)rB0#F_iq4$$*$TV zCnf*;Dr@_7=LLOP03{Iu%^;bPe zNAC@`KB__O-=p@gl)_4&SVGw7%!=|?x|6+;;_c}L+R6Mx9&Cs3!k#WLn&r2EgNhKN zM*M@xYs?RYK04wcV+%+-o@U*v-iZ71rFUL?oJ&+N%W4)C+WM$60I^{@mUbNkyt@uD z_;_LCr`d&Fwb&kND*hsyNClE;Vd)8MRrzRKVwD%~%4MqbGZhn6)BN!)y%4=)=OOm{89SYiE=(Rnj<&_nF))DVvZ}Y+2DT z^#R;=8{%Q*R#&tlqn2S1u`QvYaN-ca+_8#tlHF);q={iuB^lPx^+I{2du^&-5%U?a z@>U4fU0WH9?wj(w&TnLxvahzrp7b%;V_ltZ*4&|#qQ;asOgnh@uwA|-t+g)7?Za6m z%R4srZyFC>@EO5RGgYz9!OZB4baouO=Euk3o^Y+ODjzu~IaNktug} zJ{r^@pd1kwU^L)Tt=C-z%miJs~D9feyk(-6U z+%TRzw$Q937!L-F%3WdJL_abD@1Y1z+V=}mHy?`7sMAj!W!396MWx@XnV6z%00IdPOC)k_WLdi zsSL6LpnrmWAuI3meEIjfj-hDOL-0UGM<8OhkP>7)4`3%ZI;?S_Yhyc{*^+)qC!6xB z%OZwIKj~Ys^~r5a&;xyBh9Y+db{I8Cov-W_5@oF9Zl?JaVQ=Bb>j?$tW$GJ*$hq2= zfaDOXX9`wVjAzd={qlphp)A;$eTq$f1*DJ3$lQ%Ob(1YP`CL{C8bh(_U#h6n*n5`n ztyi+ghB&0=6EU$7ZaqhQVziY*H&|D_yN!ws1Zji=no~=f1oUJr_c-!5t<{6Aa%P?( z3N3NCFj;CW%mUF^r1Z~Y86BxE*O7zCMY6_q7PhoBIYI~!+U4ztIc|UiE)BrW7!})& z>SbLH zJ82V|vN%ki65D(`Z;;$-hIa#NCkr7iDCT1t8vVTTmiZHQ*EsDEwoYf?MEuybrP(U$ z8f~a#^ipYn_24~genPrN=<_5jZ2g1}rj#sk0)`aeOse2F)9-uGX-^dGo7DK&7bC4b z<>|{KDZyk}3@fZ~KWj02RVxY)e)j2y+$zv_G)d#~{PHx8xx)d@K)ol~FWvY|HKK&T zXz0B^Ha{X)OM2$5yHB(!U*5X8g=BWX2tp%yUubFDNj2UeKM1`|-|SjZnZ3*B9S2txBkYRmF-aUed?-U~ zYiAZrWca4rOe$8NyndK${nkTjV$w=PI$tNMSPvaL0+*kxcmWQF_LuYG3spLHu#Tl^ zx>~s5Fz_??eRLk=kcy178OvwvG*+G@*TUFSGY4*{Hp=cQ_fzn5tWq<|j=NLq7-BpR z;(#?bE@2UHj$0LW0~9JvSUiFiQr!+#z2s(u(t2&b=z$%*y}X(oyXC^Z5M3F3#m$LS zWiYt+og)i^J{VNasMt6Uom5YNZWtJI5jUldshJmbV0V+dn7w;TNV}Ewk;m#za$3-v z68CB9di2`lS5e#~8TV)fBuLZL_2F5c6N{|)y3Clz_M7KpQI72Q!=B6?ElUx3C^Il| zw9y5rhCTpDfGs7s@eNGRIz{nq(t2k`uVowqhDc<%BfP#Sbxz#v0`LV#H0rRV4(hU( zV4F1C&7~%Lx5!qaBE|viGK>5UVIpYsFBocgLW~AWy_W2ZB%TpoVc>fr2)GJJx{gx< z5aHv5!z(b$$#lMDaDEI|>s&$Ckj-cb1Am9q0S^5rgKq4p&Bj;;- zvbkBuU-%FZd+jEp@up&Ch08in2u9C_xaku0jYZlt8TSlCo&~U@ z^@LCpG5iIq|t*;cj`m=+2sLL6BH ztI5EM+885KO3k)-_0Tp47y%{;lL+HP_?%eO+v0YDisCq6EOvDE`0VW-mbcn*)+af1X=OIurn6v?s+eVATBzW~%;jz%FESUoe$;#mZ|$0PHH5)o`1p9=&nk zJezN|-c6iNl{07VDav(F6}r43z*rqK1%QT8)k%Tcl6xGgC~70Fb8C|v-T(`q5ie|M5a$uAdyI3ZScnHg zqvP%z$p@L=N$-u4HMM?I%u4Rcg&L0Ap;s3>Y~_(gA&>R*`cUKXBB(TNs9lYw&s){_ zSgwz@kHNTuGX#nqn$Ib7N2F9`bS}A#&<46Y?QE@q&YliQq|s^2Gz;~V78n<>(y_!X zvgBzZcsECEOLpqXlKLuprGvoav|7sE-Bj@Pr&Thj9ULt+w8P%+JK}bp%=ahJfnT$a zcvBaz*3m{kL8TCFX0zNB2}B?wvJBcCoOVe5N(4eglviHtNg+Eo2@Nr{U-=^`fn`XK z?sRK7=bA&IZUyR7_3FkfmstyoYTe!X1*0KoSxipC)3#AQ2-`?DBE^X&s&~*Y59M=- zq=&_XWeG@*#{1N~H{_Y>;L7f ztekV!v-fZB=kmAvLWy2-Qpi1E;ghU(`3pC9xZLQXC{{>Lv}ws5F- z`n_Db<8*7$W1o4wbT^XAk<>dib^5DDgoJOB2TFwieNHx{0lG}n6*`~wx5N3N^s-?j zHL>*-EE+8{+G^2omY!k*3&5c3Z3~pwota ztk$HY>W9A0t{@zj=}SA4RK3B|4BDHI=vb^-2H5vMu07RDQ>!DGqWh#%GAnWBVa`00 zFVwuEIw{viJL~};Pr+eK(nHNLS&~M#N20O-_nKZXNc1{$d4=0jXMg3{6G5%J&J|IZ z347cm)@cZ!I4N^gSR1YEHr;BIz?s>j&e+r=;X)MrndEk4ti+0Gl_VN7(eQxltUm1P{wr&{ z*G+UuWro~mk8{VS+0)r+60*xcpRtM1+<-d38_B^^KK=MWO(m+X|Fz{Rs|)H2{M*q_Y&)7Y$xqw#9ML%l- zSj=yYW<#uj2?iv-E+_`<+TA$@iA~^iLk4zPDMckYfvM8UF_xR1HAFlXG-;N~RBKkc z-lu~;nc|Jg@Q%`-P8lhVza9~u?5nf2mwrOH_|en zKki%*3i}8NwgXLN>+bX!b@K8S3j~^Y&giFh8r#n5%evt5A+&4~C1&h?8MfaaZJ;!9KR?qU;bqzC_8FXdEXM1XDpB zcrDViKT#^^QUeomqlyv8%>5QvHnIE{$+zdDY9SmG7nO~BJ3Jh=7u3Ur5*)TaK*CuT&w!gL-KCIaiu9^g53hu_khC@N|&4fc! zG9v3WCg&Qb&uu;z2Cs^axSDl4H(&KO%}uGf62N=KDcAF4^s+_MdPP4M;ygElj7g}N zE8EQ++Qnv1I8dKWQ{SwO@^T4}qT9}~3}UYs9#lXF0Sp?oI(WRz+q=Lb7`4KoyJkL? zSU%LfR_u6!%9qPObTxhEtxYkoo?c6FACna9M6dF#&thv z$K17+ZgB1@(yy|v-SAT4tBcaq!+LeO(ed3q9jXIcbJ^6S1bV(j$p5+|!-T8XKh4;U zf10rlCoY0RTFY+kR8=&Kj_os=fneNQsP>F)wX_B)C%mpJ-znG{`PYf_?dLRaVWncY1>5qrC)LE82 zKUy2ikN18T!YYp&VOJ;==QK7VgNW?bU0ROz9npsKFr!3ogH=WUTt%Q)w5*;_Vh7Z8 zw0GY?_3hO1;d>*KjrFm21OvEzLHv0})4&@zUNczlU%)k=Q+s7>6@T1JczG@(;QC*{ zc+p)1ZRj4!OO^CfFbCqM7^ zH+2!(LR&KyZkTqk057pwGpCyH&%DTrok0nTU8#D82ds?X8_EPw3U2H=A}uFo0NqBh zDwVoHPVT0<#K|sDqMFA8FY#D1v}Ob57HC(L7T8EX%-~qB8Ss3;V2*5(hzSK2i0kvm zTyh|{f}?BFHrZHbGkNVy{{r+G)wL2!^lk8sdGBphDaeP(=8tx-a^Ec(PQGC_aD(~} z(c~S_2g(CLuvie<598`G>)Z;&MrV!Vj$s4dj z2Q-4<8}vVfACN}KWvp-%P0FFZt%)>50sh3NAWc^?Mnqi3@s&fJsC)`jMP{+0sZJzW zASU-(hpDi0IgPU+)}wN~29~(|sLD(yk%tZ9&u35|#TTf)2{vY}#`N{&k!j^;IW-IP za|9^+w4FQfw*haUElj#YbFyNu#?~5@`rIWDn5D%kQk?_g0FNp%WkXHQMX93UApeM7 zVcHsr=7KS0W)Jpg3&7rVXtS+|Z3BXIEvS0;S7vDpN2Jg-WRlh1(7c#H+-yU!Lnw&e z*&690&w5i}xWa3K$`+d2FV`hUV)ST92{TFo(Qa52OvLWxSxX5d@ zD#17+%Y8#dGJXM0Q*X5VzG#1I$DB|Fjv^>&j_{Z(W_L#62;z0}HZ^+x3 zQGyli&&e?ZG`!a1`q$WZRV#TUbh0IrlD4`o?{FE@B4vGkls<|sUoj7~UP*7ub3jHF*pABa0>;ftC5q_Puhd~zAs*8NTZK~Si-V+?8 zaD}(F5*6w? z@f}3R4Jv4G3Q*gVgmNIEA<1?W!C3ZV;WX0dy1oUPTUj6K1s8EfKmg5?wP3BpB4>cK zl=J5d7;R(>OVxcyAV($NE>byZ5(=?Vt8+7&RTK@}7|@yGltU80fxTjPl`SQB!bkgH z%P+ZdMgl|@?UZ$6ra8h}-+Nc_$r9jP9w807(x}0t@SkUcD#2Wx%a1T3msfkmC? zPze87uGGaMd(MJ4u4jXbFS;Pz%~tUBGr5pKZsT$CZU|XHf-FzB1S?i^cnS=w1xUHy z@V)=?wuAKfi-0vR=R?8?YsO_h7V{EMT7!LEaVdMK3)}F;{tMk4Gem{$=lSa7(IJJ) z=nq%P*ZS6_{MfvsyAxZ9vKkd}CguNdT`Fc+Mf8I8b~<+TM#GVP|&r1H35w^P^{Te?0UKhuiIJmu?RyKS6jXQ{U>4(J4EU>#|gSByItK7q@xUg zCX(yCrkNZFs8+T*zw<2iNJz@gN8n&_`zcOl!Bhg4)PcBWEZxCpd$T+6BEd`buR#?wjfi z-8wBH>C%`o_SZE#8*EUw7KSeGS2Qmt7o}Er-Xd4{YC%ym7Mm&_w}^Pq>A3nu zH|~cuycIOF&m2O??eN9KzIu})89XlHkC8FJ_*E9*UctJ0Yxj()K4X@9At{+HrAL7o z{8NA6Qm{-2?55Q+XR}r=Uz%1fbbr(Jp0bA{|>7S@D&TLdp_N#AK$qLva{XB8aAjtY`1hpLVXpX zxs|sJG_EHApz<9NUZ(t(&UiIimD&mO8zTF;x%*pj&83guSw6AbGv-4+d0sISUk>bS zH0B<|mEZ;oF}@}*nzuVuGff?GMn!LDn#_tA(u^R0bF#-ltAqg!i zUeC~-H$=?!rzUJfLyQh(kCi&?Ch|!wKl^XcHRS+ybF;*qkfw^9il|*v00B~J02xQ#O=G@ucm{fQrwk?DU^0Ibb(OwXTb72j%I!*?*t zDmV@N`p46sEyP(d9&9$(&f^7w+{PjG%my)*I#AWs%$k<;RP;UewO*@|r@GAX@t5gO$hb;X!sr}Zur4{DaV<0TS`?vRgg=L8p zm6)ohJ&f@n@%{zmq&7?DwI{Z#92NV(1Ck&Y#apn_Xck<{dwC~i0U2XKM4LsID>w08^ej>W*wbaUgQq^%y+y!sU}>c5 z{iwH*8<{DoJPcq877`Ra?FeE%IYeNV$tj!MS#S7hovlMT@1}79?9r2Q*L)OWwox1h zS8!+1Z0GW#6;Lb|^Lex)7Od!?=AfSty1429I%G$a-0h@GIsNLdBCq6;MkDq) zIg|X5u=3`3x8LVU0s!u3V%B%G%mc|Rx%%^f$(IaZeihkBQ~VOFWt~|ReW1P0NNuf= zt7WB+1=ig0(Qgw(+Tz`kOC_sg^Du7B7Z?cW10V;IIMM&9$kafro_!Q_B+CA5sS(5n z0ii5rS5uLnF9fyr6^syb`Y<^Z6kccdri@0OwWi@msjK&9?iu;^J*~a#654u5AJD>$ z0Kmb7*xs9Y(GIxST^uX#P%9_0X6SuJmd#jdDjWCgkD{EnIA@xLrj%J~mVq5Z!<~|; znyjUm9-k~Jqi|vVjR?pXg5un1D-z$YM>B2t&fAwKKFUd4cuo)sdKUP? zkEU*7h&@!n9Oviu;o#=nTkR{ko%xo}OPlwESkAZr^rLB?^oxuqAsn-fFHnqmE#047 zt!dF{2ypW>c>@0|uQ_2+*!I`~6oNQu4q1%chy&QS6DTNN^A(j%RX(61?e z*vV^&YmY84iWOp$f!2&*92yO)5-&V3s^*oTDtfm@J4`A34y}G0XS_ftEo=l~BtLIs zOb-|oQhF`|EpWL4sxzQRY7cn&1mA4`0|7d-RGZWaX9raNrVB@GGvk6xvOZAg|_rzU0t*XS2f=FMcQIXY> z*NgP6Rcrx78V$Rl#!%c=gvQ0hbzKG|SDUM?Te35C$0o{h(vp(cns39=ZFtdS9DVM? zwv>=&Z3N~$e@sqC0s0`phhf9&*SUu#MF$;qH{z|i3j|}sm7;Tx>MBpzAQd>?V6(%S zaS=tOxN4L_BD%m!;@J}{?8}I`Q9%_GBF(e0>n2_h;MelA%cH}$1dR=DgjyZSye&l~itv?K z#}T(HoxP7*hl0+DJ%0$Zinnm%AbHfU3!}GjPn3ArQ)cQ?NjB#uD&JkFOuE|nv|x7m z^jf3B3O8nrM!GRHlX3dwdKoNk=_uqA$pE*iB@6v5T{Bwk_DH8LGn+fmbxLcgX;XZ5BDH+S*Hv& zSq<+CO*t>rH&#!4`mP$qD}HtTlNDXuz~BzgyA+>*&KJglS62R`bwSj!h;R^#V&^T% zc>Pc3pI2p<(NW9nN!p)^e<lm8#+8WSA+B{<$3HGH2kT1RZ%GJPBMS__lS}ua4-`6P~8h-=; zs+>s%Ikm2;ww)5pUa96H1%T8U9fZ@sMF_B!L8q?D^<=vaO;gO7g#HYM`-arHj!0X{ zC~`4wR*kGKarrpGoh%%LoZLCL;1**M z0+t@azvgoY4Lf5F={_ixbnfaaDYSMyo2ke;SA8gXQNJ_e^s>}&Up~Z4zbv9{jo~iD$R~8Q1RNVgkT%2N`S?-SGu;~w*=|L2 zFCBdZD>PV*2;nI|TbX|KcD;jL&MLC$H>mKVQu=brZp!V-A!z_LVQ?IS1p>vsa+mTA zeNA<4cbtf;gvkIZA7-;?v=D@Eik@FiqQ93vz!p4Q^%6>?$;{?Acb;<=`HH7A$R*Xc z$H1a)$OgPzQb z#1dQ?^WfSFmNnaoq#67~j0`GTf)cNSQ-3{dHOpBLdS39$jfwm;^|#xd%B` zxAHSpUm7wqmTuqyL1#9^m*3Q9)X zw6;=9s_En>qE3XdP*ik4P_!VvIF*d&6HiufP^G6?k#VqaYK2IN=D*oqP~pe+Aiswg zu4MH5E|#85G$5~qFm|QpU*Wl!f%wA7>lgL9q+aUJqSE)=Yt!O(!3+*4GF~Za!qxcN zx12&BjCJX)8*O{{QEv}?kG-Pu5Dus(UT0f|Hxb@IEKwqFF@~c^a=|$zI0!OpEfpKg z#%Mbfr_Jel(vdJaPx!&v@J6P3UL5~3zUJFBs

1Qung&bjy!(!Y!y9tns+bcQ3u8 z#epcIHpTMp2?9(^-yqb_dlXbmi1+<9fzc z@*KGOp`i-Yi zal3=AI4u7e@6-b+QyKrNk{f2U4$#KpkarAvsxt}$hJSCeY%rFfsvAE=1jumHzWL-> zc=2|8_=OR}EkUx%Rk>6}PnR9t1PjQ0W*`|RTqR*)8m&e%x8wqjIz`SWF}+sF=#18O zY#Y08h5Oplwj;FMdW*iLC**v@%S#EL?7VhDUT7{jfBOq)!9;z!L$Keq!r}m|Y~TG1U%8)&zWn-!@}Vyu$nHdrDsd-W zkCFoNHM+!Na$BXcLy=y+^!u!{`fT<1gZNt^e`qNF>HGJTfA9CYX?uk^XsR|RTDA$< z)lN)B>m$)MrHxk6P>uBoI%4Qx#WFZ`26+z;d6fhWSnPbUT;mX z)?Srt)M*%_wQd#Lph!@Vz-7h>rNzWi7df}v3hsLjW^OH zHu>{$OJ=s*56o0idd9I!hHhuN9|HCQu4{+;-X`MZTv>CBVlx=x=KXw-WvZu(H=~7` zw|{ObmcGk*b{*Jc8F@^q^M`i@Qnptddn>xfO&61^3g3eZqvW$D2xO@kjAT?_g zjytF(&Tr^=pw6UN3gWX`38Rl#1-KV?c5ho`D&M6k7i{iU5HRDQ8%|Aj~1qsC07=b4$PGB~dYe*YFLfc)o*z{f8_A$nFT z!Xe@BsYLXcdt9;E=18k?rbKJHT|cifOY1TVuB@v-cvzs6(rT?1S)j#Y`%GJA{^0Ou zCmXw-s4j0F-=oL)+y+?z7*838$vk1<4)=UliY{j8u$@0xw3 zCwqkM#?N$aM{xz5CsRO`F>Bc3(!1lC#;5{%*p_m53D%@Rf?Q@RfT>258Uzs7z;i{gf)zbO3Taj z+6~-ZH;R<%!(X6_bu7|0eXp7JLkQ#lP_aJlN^f@j@`79fW^XW4d@cNg@I&^4!ksgL z#&f#yb+4jcEPW|EW&mJtN%uFZ-)2i7IJ_DXVwY-jRwjt_#a5qh)5 zm2&bf?1#9%{l-R3kXMEtU)Rx$9Mx62IXS<&l57&y?toCw>xVqg*8SYzG|Al9Vg>R$S?#%-*dlzV(G&GPb+AGO_i0OJShTEJ#vpF>PrIe-XK` zQp4)X!O*4+tO-Vw+0O|Ew>07>;!WDFv&lw}nlOlNj$o4%E`0y>AGvt$!|{hvdjpu0 zXd&1bCn8;Uf8M*oh!Z!;i_E+7dA;WG7bh! zo6A>^opT%m>}FRt3&QKu)N5y3nl16)J1(?MFLPH)vWBa(42s}AbXK;qc5ThQwY!=3 zZ8d$$vN*M$1@Bewkeg-IfQxC;fu=%@*IQKR#L&}Jh86j~8Z)f8a1V5YD%aB1S4s`^ZJQ1x7@DffL zj?`XxT>KX>$#tOcYjuiR8qQ0JW|Lv#A(jPW;i>5y2dXp^O6_p+(Vj5)qHD6QWveE(DrU7MI}!bcIu)@1%)Z@E`7ufs z8`yD4e3$pp;Jb!d1#9P?Z#MSre}KKA`ce5)F^1X*Wfp9Nz@{?m;+?W#!cfsoFTZ>hquB#yq)=!qp-3z)2c1*atqCY zG{>h9k*ovbRE#pEa3uvIDgk=8Q~gSj_9eZ8GAmsS16whh=ZH^<&ecUnx@A*6z<1rV7o-#8X56BPm*Bqf7+88NGpwOxmPsahAXW0W*LfG~^2kaqmRmsHQ4RV$^~J=# zReuU{eCGbqwIRLjg9IAsOhgI?IK-ApS6^CAyL;j0z2X)wh{+Y^JW{A%uXNch_4*Lt(b@acw52~1s6G5zg* z@Ln7|2-SHX8m|837v6`TMe6Oj=hc)eBQS%xl4M5O6B9Bp{)f?=P-lT^+pTv%8a;kP zjre&8viQ(zR?w(2^Bo@pt5=?iI3T@t_e0ZIkmF!t;TSd-pEEE_zaJ4f#l*Xo`VRN4 zz`lJZT7dYz`Dubol&r$nkN~&hNnNM@2^-$dogEz2v+HPceI(ASJ2i7jeA9)hVn?)usWR@RzMp@_2(jV zQb%JXJ2-R1mp`(!Ls)Rgt3;!3Rn3xFuVHK75Y(;Vd1XouTKL``2Qbnr<^iXM-gLD8 zsSq}K#H9|*4QXUw&YL;7-*GigK7Q2dMc|;|<#GFa5a2{Q;mK^rg@a(-7gUXWpkM1A z$B{_k5K+W};!wdBp!ogzYjw$`|2MMj--)=&pYxvK(A@RA*EOZ}ABWIHVVo~j)&lO~ zjt0^ngzyL3gA!eGN7f{seBTbt+)m4T`x!C73{rktQTP|I^at?&nQhDZoJoh{><4re zJg-ZM)*kJZHhUtLAe|dTO?~~k4UvMY>Zw$r+%h!Zc)w!6?bg#5T8x;`ozhyfQ@N=e zJS$x*!hvJHMp0&4GszFGmiHI19Wa0{1@qoynq?c1lAuUVr>D4`Kl7d^XI5-*Z{v1l z8>aI1-+%0lA1VR>K!9hza?D`rf@z*32Xv~kb4+n4-^MmrOu&-MrKOaY7txiX)=b#? z9@OGR;n&pY+kYnB_(c{HCPZ#9n**Yg*xGN^Dt;Hp8RRhpSk^;Itz&8jzwIxLj73|x z^B7uK{i+42v(Ahq-e_Hrh{_fUd~jY}K|9-Q_k%uHj?- zte5g>Pn*<^E;7E<+`D5U;hH4PDglf|9x8ITk!fp*kC3utUFUhg**Ax`OdAoo^9apF3u!o)_3hf zGAb-}4z7zlgvP?ruKfz4A)J_ej@7l67dE9ml+qr}*i#dE^J24-kpR<=fru^vHSFZ* zyJTksB|%``wagn~nO8|`-`QapCy9A|jwoi(S`MSgodXrF5*w;)4c^R2h|be@$Viw?}ga|1n9AU#(Bcdy@Gh8K#B_6PKL2 z|08>RBL*59gh~n~j3mwLtvkQoSFD0@$cLxu1~gVnknD)Y%TcENZGca{i?Z|(cJ^j| zZ&|T^{vf{sF*iFpPG9b4D{k~=Y<~fgBgwknd|_P?U8rWgq`1)~6-)qUU*GEkO`S_V z=fsM$Iw8w-!m_Lwa(o+(FPm9`4l8nJ4ZLi|UCF@%GE!)v7TW;`-`-t=NJQjcfY_av7D9J^ zzr3O}!Cd8deHP8OTy=w*KYpvEpSkh8w$H%CVwUnDB&stZW;nBJPwus*qpRlcSw{DI z4&A9FPs*5yuj^1BYZ|OO@Qr^41ChHZ&%)YwZe%_UnhVMJ#~jFcFo8v;*;8GczQ#{B zZ7`m1;ve_%rLJEko`hMdwIGJ`oG`M%S^i|!W?Oq&Xvl2!dFiurgMR@$(BK{-pD^f?Pl>x+w^&;nE}^EY%zRH;YHbo2##Q6_ zgWAw)JEX)g2l~nhF&C07XJVXxOt1M+tGvjso5B$oyG;mW84=*SnA&ajXI071K?CyPmpN+w&6eFB5WBde^|^np^hU zBdnM&KY(zTEOlK4demz0gg3}sA3-sA(>!gH zMB1Wm5k7-{5XH>9C!rKB>oQ!T>s8gE1iW^m+{@N+;;!RnqG2ETWh~MJ=L11!oI3Er z8WJcBYJ8H}+rT+Vc7wycF0j@x-c}I}R1=u`dQb8fae-4qsbzu;=QidwFzrm7P?eYO z2SA?Lu*#=Y9&J2XkB!9$3x;ui!l|o5f-tN#SbW5`HOLI|R!kSflxbYk4%aaG*^{xy74t+D~@A4 zRqFviSo0zoc4?I8(QE_!D!v~cxF2&mYjl#%OI8Fov?yBDYniXk_Km~ks4vZr2W&_t z1r4ksmTdytHfuen?2H<+q$&30&(+u9txO$(LuJh}G13J^?hB6y1%tbOaQbOvNAbVz z1{(xdlOVBEQ4jAOIzM8NeQO^oV|G^=#)GBMCUR+f9EQbj&b(Fd;fr!GlVnU`@0pbm zbOCAT^jAl6Y3_!d2XOZH_8kJ5`9{wHdE?FHS7Xq!OiAZxXqdmCfrV=uc`k{Fhx%2K zLqXTxxh++JpNSc#X<5nCQawXfMh?OP;dEc`(};2z$*Dn9!2i}#=@t89!9;Pwxg4~N z!rZ2Lry6_ZIM}8R1fNnsw$AIQBC$>RQceBO+HeX$XJod9_gE$iJxo^@nRmidg{+op zUdW+`Q@E&&{a)?pbDWGWjP|4xNf&NQJdw{Gb@<5=)8prgqg^q;uyHYcLL_oK&#qZU zW4Wt(CK)~UjB+?i3O!8a$nA2bmI&{LyRoLm;4Vaf5$tkAdu;k>=4HM?MNC3kOaGhvK0c@Dx-XDCk?0d#j=M$CInbUFIC(G@@93S4 zVrQnPKoNO$hpS%zgY)P_8BPUYta>4Y;&CWdW|65gifhwkTx)pb2dFZJ8zx))&=|!4 z4QwqQ*~-maJ7=O8Jt3i5Ey0RGN`nmW^RRYEMRxQcoK{Zx5j6f7FGn;QYS}e-$H^xF z1d#Tx`G1cvbnLu5*NGhu9TlX1o$ukW7@Gg%8|%NAcMy~$@=@lO!uuLS|7pbs;jI8P z-J5_~0@gmi-PSn$Mb#1=BkCJZ^BJdRG=X;0=GT}#)U|Fvuv>*N%>9OY1^Do-xxQ}2 z$RG?@`NuJrj_!OY*E^xBtZD-LvQIq{_|FOaeC}=?$SxlIIb$pPt70TqyjI2q-8ntG z@6$Ec5U0#_wl#jQ`s<}0zxNVBrt7xXJdtZI*IcL;3Y=_q;p;iPxs!JKD7X2Xt5HS7 zN<%1hExd79Y9xm)q$suAxpBwy)c!j%!@KBsE%M9$WOG?rRD?L`X6`-Jov(GhK6?uL zmtWVpteGvvQFj=+eJcEhEf%${o Date: Fri, 5 Aug 2022 10:57:12 +0200 Subject: [PATCH 062/263] [BridgeHub] Setup Wococo parachain backbone (reused from Rococo) [Bridge-Backport] Rebase-fix BridgeHub] Added zombienet startup tomls for Rococo/Wococo Fix typo --- Cargo.lock | 11 +++ parachains/runtimes/bridge-hubs/README.md | 69 ++++++++++++++----- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 1 + .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 21 ++---- .../src/chain_spec/bridge_hubs.rs | 51 +++++++++++--- polkadot-parachain/src/command.rs | 15 +++- .../0004-run_bridge_hubs_rococo.toml | 37 ++++++++++ .../0004-run_bridge_hubs_wococo.toml | 37 ++++++++++ 8 files changed, 202 insertions(+), 40 deletions(-) create mode 100644 zombienet_tests/0004-run_bridge_hubs_rococo.toml create mode 100644 zombienet_tests/0004-run_bridge_hubs_wococo.toml diff --git a/Cargo.lock b/Cargo.lock index 8edcc339e66..c0e22540ece 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1024,6 +1024,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "pallet-xcm", "parachain-info", + "parachains-common", "parity-scale-codec", "polkadot-parachain 0.9.27", "polkadot-runtime-common", @@ -9923,6 +9924,7 @@ dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", "sp-sandbox", + "sp-serializer 4.0.0-dev (git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges)", "sp-wasm-interface", "thiserror", "wasm-instrument", @@ -11634,6 +11636,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "sp-serializer" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#aa7520bd0a2094204a6c0b33865aa264e6d686a5" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "sp-session" version = "4.0.0-dev" diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index d6c5b20f500..2bb2d2b9c68 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -19,7 +19,7 @@ Every _BridgeHub_ is meant to be **_common good parachain_** with main responsib ### Deploy ``` cd -cargo build --release --locked -p polkadot-parachain@0.9.220 +cargo build --release --locked -p polkadot-parachain@0.9.230 mkdir -p ~/local_bridge_testing/bin @@ -28,41 +28,78 @@ cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-paracha ls -lrt ~/local_bridge_testing/bin/polkadot-parachain ``` -### Run relay chain (Rococo) +### Run with Zombienet + +``` +# Rococo +POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ + POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ + ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet_tests/0004-run_bridge_hubs_rococo.toml + +# Wococo +POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ + POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ + ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet_tests/0004-run_bridge_hubs_wococo.toml +``` + +### Run from cmd + +#### Run relay chains (Rococo, Wococo) ``` +# Rococo ~/local_bridge_testing/bin/polkadot build-spec --chain rococo-local --disable-default-bootnode --raw > ~/local_bridge_testing/rococo-local-cfde.json -~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --alice --tmp -~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --bob --tmp --port 30334 +~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --alice --tmp --port 30332 --rpc-port 9932 --ws-port 9942 +~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --bob --tmp --port 30333 --rpc-port 9933 --ws-port 9943 +~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --charlie --tmp --port 30334 --rpc-port 9934 --ws-port 9944 + +# Wococo +~/local_bridge_testing/bin/polkadot build-spec --chain wococo-local --disable-default-bootnode --raw > ~/local_bridge_testing/wococo-local-cfde.json +~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --alice --tmp --port 30335 --rpc-port 9935 --ws-port 9945 +~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --bob --tmp --port 30336 --rpc-port 9936 --ws-port 9946 +~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --charlie --tmp --port 30337 --rpc-port 9937 --ws-port 9947 +~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --dave --tmp --port 30338 --rpc-port 9938 --ws-port 9948 ``` +We need at least 5 nodes together (validator + collator) to finalize blocks. -### Run Rococo BridgeHub parachain -#### Generate spec + genesis + wasm (paraId=1013) +#### Run BridgeHub parachains (Rococo, Wococo) +##### Generate spec + genesis + wasm (paraId=1013) ``` +# Rococo rm ~/local_bridge_testing/bridge-hub-rococo-local-raw.json ~/local_bridge_testing/bin/polkadot-parachain build-spec --chain bridge-hub-rococo-local --raw --disable-default-bootnode > ~/local_bridge_testing/bridge-hub-rococo-local-raw.json ~/local_bridge_testing/bin/polkadot-parachain export-genesis-state --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json > ~/local_bridge_testing/bridge-hub-rococo-local-genesis ~/local_bridge_testing/bin/polkadot-parachain export-genesis-wasm --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json > ~/local_bridge_testing/bridge-hub-rococo-local-genesis-wasm -``` -#### Run collators -``` -~/local_bridge_testing/bin/polkadot-parachain --collator --alice --force-authoring --tmp --port 40335 --ws-port 9946 --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 30335 -~/local_bridge_testing/bin/polkadot-parachain --collator --bob --force-authoring --tmp --port 40336 --ws-port 9947 --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 30336 +# Wococo +rm ~/local_bridge_testing/bridge-hub-wococo-local-raw.json +~/local_bridge_testing/bin/polkadot-parachain build-spec --chain bridge-hub-wococo-local --raw --disable-default-bootnode > ~/local_bridge_testing/bridge-hub-wococo-local-raw.json +~/local_bridge_testing/bin/polkadot-parachain export-genesis-state --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json > ~/local_bridge_testing/bridge-hub-wococo-local-genesis +~/local_bridge_testing/bin/polkadot-parachain export-genesis-wasm --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json > ~/local_bridge_testing/bridge-hub-wococo-local-genesis-wasm ``` -#### Run parachain node +##### Run collators (Rococo, Wococo) ``` -~/local_bridge_testing/bin/polkadot-parachain --tmp --port 40337 --ws-port 9948 --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 30337 +# Rococo +~/local_bridge_testing/bin/polkadot-parachain --collator --alice --force-authoring --tmp --port 40333 --rpc-port 8933 --ws-port 8943 --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 41333 --rpc-port 48933 --ws-port 48943 +~/local_bridge_testing/bin/polkadot-parachain --collator --bob --force-authoring --tmp --port 40334 --rpc-port 8934 --ws-port 8944 --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 41334 -rpc-port 48934 --ws-port 48944 + +# Wococo +~/local_bridge_testing/bin/polkadot-parachain --collator --alice --force-authoring --tmp --port 40335 --rpc-port 8935 --ws-port 8945 --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/wococo-local-cfde.json --port 41335 --rpc-port 48935 --ws-port 48945 +~/local_bridge_testing/bin/polkadot-parachain --collator --bob --force-authoring --tmp --port 40336 --rpc-port 8936 --ws-port 8946 --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/wococo-local-cfde.json --port 41336 --rpc-port 48936 --ws-port 48946 ``` -#### Activate parachain (paraId=1013) +##### Activate parachains (Rococo, Wococo) (paraId=1013) ``` -https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/explorer +# Rococo +https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9942#/explorer + +# Wococo +https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9945#/explorer ``` -#### After parachain activation, we should see new blocks in collator's node +##### After parachain activation, we should see new blocks in collator's node ``` https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/parachains ``` diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 4ebdf070607..e716dc69ae3 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -65,6 +65,7 @@ cumulus-primitives-timestamp = { path = "../../../../primitives/timestamp", defa cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false } +parachains-common = { path = "../../../../parachains/common", default-features = false } # Bridges bp-polkadot-core = { path = "../../../../bridges/primitives/polkadot-core", default-features = false } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 7008fb385e2..09835bb6562 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -31,9 +31,9 @@ use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify}, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, MultiSignature, + ApplyExtrinsicResult, }; use sp_std::prelude::*; @@ -43,14 +43,13 @@ use sp_version::RuntimeVersion; use frame_support::{ construct_runtime, parameter_types, - traits::Everything, + traits::{Everything, IsInVec}, weights::{ constants::WEIGHT_PER_SECOND, ConstantMultiplier, DispatchClass, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }, PalletId, }; -use frame_support::traits::IsInVec; use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, @@ -70,16 +69,10 @@ use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; // XCM Imports +use parachains_common::{AccountId, Signature}; use xcm::latest::prelude::BodyId; use xcm_executor::XcmExecutor; -/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. -pub type Signature = MultiSignature; - -/// Some way of identifying an account on the chain. We intentionally make it equivalent -/// to the public key of our transaction signing scheme. -pub type AccountId = <::Signer as IdentifyAccount>::AccountId; - /// Balance of an account. pub type Balance = u128; @@ -489,7 +482,7 @@ impl pallet_bridge_grandpa::Config for Runtime { type WeightInfo = (); } -/// Add granda bridge pallet to track Wococo relay chain +/// Add granda bridge pallet to track Rococo relay chain pub type BridgeGrandpaRococoInstance = pallet_bridge_grandpa::Instance2; impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_rococo::Rococo; @@ -512,7 +505,7 @@ pub type BridgeParachainWococoInstance = pallet_bridge_parachains::Instance1; impl pallet_bridge_parachains::Config for Runtime { type WeightInfo = (); type BridgesGrandpaPalletInstance = BridgeGrandpaWococoInstance; - type ParasPalletName = RococoBridgeParachainPalletName; + type ParasPalletName = WococoBridgeParachainPalletName; type TrackedParachains = IsInVec; type HeadsToKeep = ParachainHeadsToKeep; } @@ -522,7 +515,7 @@ pub type BridgeParachainRococoInstance = pallet_bridge_parachains::Instance2; impl pallet_bridge_parachains::Config for Runtime { type WeightInfo = (); type BridgesGrandpaPalletInstance = BridgeGrandpaRococoInstance; - type ParasPalletName = WococoBridgeParachainPalletName; + type ParasPalletName = RococoBridgeParachainPalletName; type TrackedParachains = IsInVec; type HeadsToKeep = ParachainHeadsToKeep; } diff --git a/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/polkadot-parachain/src/chain_spec/bridge_hubs.rs index 9091fb82c88..aa89eede88e 100644 --- a/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -14,14 +14,16 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use polkadot_service::ParaId; +use cumulus_primitives_core::ParaId; use sc_chain_spec::ChainSpec; use sc_cli::RuntimeVersion; use std::{path::PathBuf, str::FromStr}; /// Collects all supported BridgeHub configurations +#[derive(Debug, PartialEq)] pub enum BridgeHubRuntimeType { RococoLocal, + WococoLocal, } impl FromStr for BridgeHubRuntimeType { @@ -30,6 +32,7 @@ impl FromStr for BridgeHubRuntimeType { fn from_str(value: &str) -> Result { match value { rococo::BRIDGE_HUB_ROCOCO_LOCAL => Ok(BridgeHubRuntimeType::RococoLocal), + wococo::BRIDGE_HUB_WOCOCO_LOCAL => Ok(BridgeHubRuntimeType::WococoLocal), _ => Err(format!("Value '{}' is not configured yet", value)), } } @@ -41,19 +44,25 @@ impl BridgeHubRuntimeType { pub fn chain_spec_from_json_file(&self, path: PathBuf) -> Result, String> { Ok(Box::new(match self { BridgeHubRuntimeType::RococoLocal => rococo::BridgeHubChainSpec::from_json_file(path)?, + BridgeHubRuntimeType::WococoLocal => wococo::BridgeHubChainSpec::from_json_file(path)?, })) } pub fn load_config(&self) -> Box { Box::new(match self { BridgeHubRuntimeType::RococoLocal => - rococo::local_config("rococo-local", ParaId::new(1013)), + rococo::local_config("Rococo BrideHub Local", "rococo-local", ParaId::new(1013)), + BridgeHubRuntimeType::WococoLocal => + wococo::local_config("Wococo BrideHub Local", "wococo-local", ParaId::new(1013)), }) } pub fn runtime_version(&self) -> &'static RuntimeVersion { match self { - BridgeHubRuntimeType::RococoLocal => &bridge_hub_rococo_runtime::VERSION, + BridgeHubRuntimeType::RococoLocal | BridgeHubRuntimeType::WococoLocal => { + // this is intentional, for Rococo/Wococo we just want to have one runtime, which is configured for both sides + &bridge_hub_rococo_runtime::VERSION + }, } } } @@ -71,22 +80,29 @@ fn ensure_id(id: &str) -> Result<&str, String> { } } +/// Sub-module for Rococo setup pub mod rococo { + use super::ParaId; use crate::chain_spec::{ get_account_id_from_seed, get_collator_keys_from_seed, Extensions, SAFE_XCM_VERSION, }; - use bridge_hub_rococo_runtime::{AccountId, AuraId}; - use cumulus_primitives_core::ParaId; + use parachains_common::{AccountId, AuraId}; use sc_chain_spec::ChainType; use sp_core::sr25519; - pub const BRIDGE_HUB_ROCOCO_LOCAL: &str = "bridge-hub-rococo-local"; + pub(crate) const BRIDGE_HUB_ROCOCO_LOCAL: &str = "bridge-hub-rococo-local"; /// Specialized `ChainSpec` for the normal parachain runtime. pub type BridgeHubChainSpec = sc_service::GenericChainSpec; - pub fn local_config(relay_chain: &str, para_id: ParaId) -> BridgeHubChainSpec { + pub type RuntimeApi = bridge_hub_rococo_runtime::RuntimeApi; + + pub fn local_config( + chain_name: &str, + relay_chain: &str, + para_id: ParaId, + ) -> BridgeHubChainSpec { let properties = sc_chain_spec::Properties::new(); // TODO: check // properties.insert("ss58Format".into(), 2.into()); @@ -95,7 +111,7 @@ pub mod rococo { BridgeHubChainSpec::from_genesis( // Name - "Rococo BrideHub Local", + chain_name, // ID super::ensure_id(BRIDGE_HUB_ROCOCO_LOCAL).expect("invalid id"), ChainType::Local, @@ -180,3 +196,22 @@ pub mod rococo { } } } + +/// Sub-module for Wococo setup (reuses stuff from Rococo) +pub mod wococo { + use super::ParaId; + use crate::chain_spec::bridge_hubs::rococo; + + pub(crate) const BRIDGE_HUB_WOCOCO_LOCAL: &str = "bridge-hub-wococo-local"; + + pub type BridgeHubChainSpec = rococo::BridgeHubChainSpec; + pub type RuntimeApi = rococo::RuntimeApi; + + pub fn local_config( + chain_name: &str, + relay_chain: &str, + para_id: ParaId, + ) -> BridgeHubChainSpec { + rococo::local_config(chain_name, relay_chain, para_id) + } +} diff --git a/polkadot-parachain/src/command.rs b/polkadot-parachain/src/command.rs index 55c6400f55a..e5734e4320d 100644 --- a/polkadot-parachain/src/command.rs +++ b/polkadot-parachain/src/command.rs @@ -37,6 +37,8 @@ use sp_core::hexdisplay::HexDisplay; use sp_runtime::traits::{AccountIdConversion, Block as BlockT}; use std::{net::SocketAddr, path::PathBuf}; +use crate::chain_spec::bridge_hubs::BridgeHubRuntimeType; + /// Helper enum that is used for better distinction of different parachain/runtime configuration /// (it is based/calculated on ChainSpec's ID attribute) #[derive(Debug, PartialEq, Default)] @@ -479,7 +481,11 @@ macro_rules! construct_async_run { Runtime::BridgeHub(bridge_hub_runtime_type) => { runner.async_run(|$config| { let $components = match bridge_hub_runtime_type { - chain_spec::bridge_hubs::BridgeHubRuntimeType::RococoLocal => new_partial::( + chain_spec::bridge_hubs::BridgeHubRuntimeType::RococoLocal => new_partial::( + &$config, + crate::service::aura_build_import_queue::<_, AuraId>, + )?, + chain_spec::bridge_hubs::BridgeHubRuntimeType::WococoLocal => new_partial::( &$config, crate::service::aura_build_import_queue::<_, AuraId>, )?, @@ -775,7 +781,12 @@ pub fn run() -> Result<()> { Runtime::BridgeHub(bridge_hub_runtime_type) => match bridge_hub_runtime_type { chain_spec::bridge_hubs::BridgeHubRuntimeType::RococoLocal => crate::service::start_generic_aura_node::< - bridge_hub_rococo_runtime::RuntimeApi, + chain_spec::bridge_hubs::rococo::RuntimeApi, + AuraId, + >(config, polkadot_config, collator_options, id, hwbench), + chain_spec::bridge_hubs::BridgeHubRuntimeType::WococoLocal => + crate::service::start_generic_aura_node::< + chain_spec::bridge_hubs::wococo::RuntimeApi, AuraId, >(config, polkadot_config, collator_options, id, hwbench), } diff --git a/zombienet_tests/0004-run_bridge_hubs_rococo.toml b/zombienet_tests/0004-run_bridge_hubs_rococo.toml new file mode 100644 index 00000000000..9c9c9c49d15 --- /dev/null +++ b/zombienet_tests/0004-run_bridge_hubs_rococo.toml @@ -0,0 +1,37 @@ +[relaychain] +default_command = "{{POLKADOT_BINARY_PATH}}" +default_args = [ "-lparachain=debug" ] + +chain = "rococo-local" + + [[relaychain.nodes]] + name = "alice" + validator = true + + [[relaychain.nodes]] + name = "bob" + validator = true + + [[relaychain.nodes]] + name = "charlie" + validator = true + +[[parachains]] +id = 1013 +cumulus_based = true +chain = "bridge-hub-rococo-local" + + [[parachains.collators]] + name = "alice" + command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + args = ["-lparachain=debug"] + + [[parachains.collators]] + name = "bob" + command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + args = ["-lparachain=debug"] + + [[parachains.collators]] + name = "charlie" + command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + args = ["-lparachain=debug"] diff --git a/zombienet_tests/0004-run_bridge_hubs_wococo.toml b/zombienet_tests/0004-run_bridge_hubs_wococo.toml new file mode 100644 index 00000000000..b938b1672dd --- /dev/null +++ b/zombienet_tests/0004-run_bridge_hubs_wococo.toml @@ -0,0 +1,37 @@ +[relaychain] +default_command = "{{POLKADOT_BINARY_PATH}}" +default_args = [ "-lparachain=debug" ] + +chain = "wococo-local" + + [[relaychain.nodes]] + name = "alice" + validator = true + + [[relaychain.nodes]] + name = "bob" + validator = true + + [[relaychain.nodes]] + name = "charlie" + validator = true + +[[parachains]] +id = 1013 +cumulus_based = true +chain = "bridge-hub-wococo-local" + + [[parachains.collators]] + name = "alice" + command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + args = ["-lparachain=debug"] + + [[parachains.collators]] + name = "bob" + command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + args = ["-lparachain=debug"] + + [[parachains.collators]] + name = "charlie" + command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + args = ["-lparachain=debug"] From 69f2159c012606c7c1fa5d3a573b767881c5a7b0 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 24 Aug 2022 22:20:46 +0200 Subject: [PATCH 063/263] [BridgeHub] Correct Accounts setup for bridge pallets --- parachains/runtimes/bridge-hubs/README.md | 80 ++++++++++++++++--- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 8 ++ .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 18 +++-- .../src/chain_spec/bridge_hubs.rs | 14 ++++ 4 files changed, 102 insertions(+), 18 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 2bb2d2b9c68..3da73178a5e 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -12,23 +12,34 @@ Every _BridgeHub_ is meant to be **_common good parachain_** with main responsib - sync finality proofs between BridgeHub parachains - pass (XCM) messages between different BridgeHub parachains -![](/home/bparity/parity/cumulus/cumulus/parachains/runtimes/bridge-hubs/docs/bridge-hub-parachain-design.jpg "Basic deployment setup") +![](./docs/bridge-hub-parachain-design.jpg "Basic deployment setup") ## How to test locally Rococo <-> Wococo -### Deploy +### Build/Deploy ``` -cd -cargo build --release --locked -p polkadot-parachain@0.9.230 - +# Prepare empty directory for testing mkdir -p ~/local_bridge_testing/bin +mkdir -p ~/local_bridge_testing/logs -rm ~/local_bridge_testing/bin/polkadot-parachain + +# 1. Build polkadot binary +TODO: description + +# 2. Build cumulus polkadot-parachain binary +cd +cargo build --release --locked -p polkadot-parachain@0.9.230 cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain -ls -lrt ~/local_bridge_testing/bin/polkadot-parachain + +# 3. Build substrate-relay binary +git clone https://github.com/paritytech/parity-bridges-common.git +cd parity-bridges-common +cargo build -p substrate-relay +rm ~/local_bridge_testing/bin/substrate-relay +cp target/release/substrate-relay ~/local_bridge_testing/bin/substrate-relay ``` -### Run with Zombienet +### Run chains (Rococo + BridgeHub, Wococo + BridgeHub) with Zombienet ``` # Rococo @@ -42,7 +53,7 @@ POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet_tests/0004-run_bridge_hubs_wococo.toml ``` -### Run from cmd +### Run chains (Rococo + BridgeHub, Wococo + BridgeHub) from `cmd` #### Run relay chains (Rococo, Wococo) ``` @@ -64,7 +75,8 @@ POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ We need at least 5 nodes together (validator + collator) to finalize blocks. #### Run BridgeHub parachains (Rococo, Wococo) -##### Generate spec + genesis + wasm (paraId=1013) + +**1. Generate spec + genesis + wasm (paraId=1013)** ``` # Rococo rm ~/local_bridge_testing/bridge-hub-rococo-local-raw.json @@ -79,7 +91,7 @@ rm ~/local_bridge_testing/bridge-hub-wococo-local-raw.json ~/local_bridge_testing/bin/polkadot-parachain export-genesis-wasm --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json > ~/local_bridge_testing/bridge-hub-wococo-local-genesis-wasm ``` -##### Run collators (Rococo, Wococo) +**2. Run collators (Rococo, Wococo)** ``` # Rococo ~/local_bridge_testing/bin/polkadot-parachain --collator --alice --force-authoring --tmp --port 40333 --rpc-port 8933 --ws-port 8943 --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 41333 --rpc-port 48933 --ws-port 48943 @@ -90,7 +102,7 @@ rm ~/local_bridge_testing/bridge-hub-wococo-local-raw.json ~/local_bridge_testing/bin/polkadot-parachain --collator --bob --force-authoring --tmp --port 40336 --rpc-port 8936 --ws-port 8946 --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/wococo-local-cfde.json --port 41336 --rpc-port 48936 --ws-port 48946 ``` -##### Activate parachains (Rococo, Wococo) (paraId=1013) +**3. Activate parachains (Rococo, Wococo) (paraId=1013)** ``` # Rococo https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9942#/explorer @@ -99,11 +111,53 @@ https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9942#/explorer https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9945#/explorer ``` -##### After parachain activation, we should see new blocks in collator's node +**4. After parachain activation, we should see new blocks in collator's node** ``` https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/parachains ``` +### Run relayers (Rococo, Wococo) + +**Accounts of BridgeHub parachains:** +- `Bob` is pallet owner of all bridge pallets +- `Alice` is `Sudo` + +**1. Init bridges** +``` +# Rococo -> Wococo +RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay init-bridge bridge-hub-rococo-to-bridge-hub-wococo \ + --source-host localhost \ + --source-port 48943 \ + --target-host localhost \ + --target-port 8945 \ + --target-signer //Bob + +# Wococo -> Rococo +RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay init-bridge bridge-hub-wococo-to-bridge-hub-rococo \ + --source-host localhost \ + --source-port 48945 \ + --target-host localhost \ + --target-port 8943 \ + --target-signer //Bob +``` + +**2. Relay headers** + +TODO: + +**2. Relay (Grandpa relay-chain) headers** + +TODO: + +**3. Relay (BridgeHub parachain) headers** + +TODO: + +**4. Relay (XCM) messages** + +TODO: ## Git subtree `./bridges` diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index e716dc69ae3..bf040394f5b 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -82,6 +82,9 @@ default = [ "std", ] std = [ + "bp-polkadot-core/std", + "bp-rococo/std", + "bp-wococo/std", "codec/std", "log/std", "scale-info/std", @@ -101,6 +104,11 @@ std = [ "pallet-aura/std", "pallet-authorship/std", "pallet-balances/std", + "pallet-bridge-grandpa/std", + "pallet-bridge-messages/std", + "pallet-bridge-parachains/std", + "pallet-bridge-relayers/std", + "pallet-shift-session-manager/std", "pallet-collator-selection/std", "pallet-session/std", "pallet-sudo/std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 09835bb6562..30cbbda4a23 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -467,6 +467,11 @@ impl pallet_collator_selection::Config for Runtime { type WeightInfo = (); } +impl pallet_sudo::Config for Runtime { + type Call = Call; + type Event = Event; +} + // Add bridge pallets (GPA) parameter_types! { pub const MaxRequests: u32 = 64; @@ -558,15 +563,18 @@ construct_runtime!( DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 33, // Consensus support. - ShiftSessionManager: pallet_shift_session_manager::{Pallet}, + ShiftSessionManager: pallet_shift_session_manager::{Pallet} = 40, // Wococo bridge modules - BridgeWococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage}, - BridgeWococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage}, + BridgeWococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Config} = 41, + BridgeWococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage} = 42, // Rococo bridge modules - BridgeRococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage}, - BridgeRococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage}, + BridgeRococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Config} = 43, + BridgeRococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage} = 44, + + // Sudo + Sudo: pallet_sudo::{Pallet, Call, Config, Event, Storage} = 100, } ); diff --git a/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/polkadot-parachain/src/chain_spec/bridge_hubs.rs index aa89eede88e..4bef37f8932 100644 --- a/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -143,6 +143,8 @@ pub mod rococo { get_account_id_from_seed::("Ferdie//stash"), ], para_id, + Some(get_account_id_from_seed::("Alice")), + Some(get_account_id_from_seed::("Bob")), ) }, Vec::new(), @@ -158,6 +160,8 @@ pub mod rococo { invulnerables: Vec<(AccountId, AuraId)>, endowed_accounts: Vec, id: ParaId, + root_key: Option, + bridges_pallet_owner: Option, ) -> bridge_hub_rococo_runtime::GenesisConfig { bridge_hub_rococo_runtime::GenesisConfig { system: bridge_hub_rococo_runtime::SystemConfig { @@ -193,6 +197,16 @@ pub mod rococo { polkadot_xcm: bridge_hub_rococo_runtime::PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION), }, + // TODO: when go live, check it: https://github.com/paritytech/parity-bridges-common/issues/1551 + sudo: bridge_hub_rococo_runtime::SudoConfig { key: root_key }, + bridge_wococo_grandpa: bridge_hub_rococo_runtime::BridgeWococoGrandpaConfig { + owner: bridges_pallet_owner.clone(), + ..Default::default() + }, + bridge_rococo_grandpa: bridge_hub_rococo_runtime::BridgeRococoGrandpaConfig { + owner: bridges_pallet_owner, + ..Default::default() + }, } } } From 0373bfbe53a87c27361bac21373d61d66e6c3bdf Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Sun, 28 Aug 2022 22:32:58 +0200 Subject: [PATCH 064/263] [BridgeHub] Added best_finalized runtime apis --- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 2 ++ .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index bf040394f5b..d934d21d7a0 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -69,6 +69,7 @@ parachains-common = { path = "../../../../parachains/common", default-features = # Bridges bp-polkadot-core = { path = "../../../../bridges/primitives/polkadot-core", default-features = false } +bp-runtime = { path = "../../../../bridges/primitives/runtime", default-features = false } bp-rococo = { path = "../../../../bridges/primitives/chain-rococo", default-features = false } bp-wococo = { path = "../../../../bridges/primitives/chain-wococo", default-features = false } pallet-bridge-grandpa = { path = "../../../../bridges/modules/grandpa", default-features = false } @@ -83,6 +84,7 @@ default = [ ] std = [ "bp-polkadot-core/std", + "bp-runtime/std", "bp-rococo/std", "bp-wococo/std", "codec/std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 30cbbda4a23..ba378e78a4f 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -59,6 +59,7 @@ pub use sp_runtime::{MultiAddress, Perbill, Permill}; use xcm_config::{XcmConfig, XcmOriginToTransactDispatchOrigin}; use bp_polkadot_core::parachains::ParaId; +use bp_runtime::{HeaderId, HeaderIdProvider}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -233,6 +234,14 @@ pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } } +// TODO: check-parameter - move to bridges/primitives, once rebased and would compile with bp_bridge_hub_xyz dependencies +mod runtime_api { + use super::BlockNumber; + use super::Hash; + bp_runtime::decl_bridge_finality_runtime_apis!(rococo); + bp_runtime::decl_bridge_finality_runtime_apis!(wococo); +} + parameter_types! { pub const Version: RuntimeVersion = VERSION; @@ -701,6 +710,18 @@ impl_runtime_apis! { } } + impl runtime_api::RococoFinalityApi for Runtime { + fn best_finalized() -> Option> { + BridgeRococoGrandpa::best_finalized().map(|header| header.id()) + } + } + + impl runtime_api::WococoFinalityApi for Runtime { + fn best_finalized() -> Option> { + BridgeWococoGrandpa::best_finalized().map(|header| header.id()) + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade() -> (Weight, Weight) { From 2264ea702c8a8d1a99c73ca6bc22a56eebc9f0d9 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 30 Aug 2022 13:42:14 +0200 Subject: [PATCH 065/263] [BridgeHub] Updated readme.md and script for local run --- parachains/runtimes/bridge-hubs/README.md | 148 ++++++++++------------ scripts/bridges_rococo_wococo.sh | 144 +++++++++++++++++++++ 2 files changed, 209 insertions(+), 83 deletions(-) create mode 100755 scripts/bridges_rococo_wococo.sh diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 3da73178a5e..f10fcf5764c 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -16,7 +16,7 @@ Every _BridgeHub_ is meant to be **_common good parachain_** with main responsib ## How to test locally Rococo <-> Wococo -### Build/Deploy +### Prepare/Build/Deploy ``` # Prepare empty directory for testing mkdir -p ~/local_bridge_testing/bin @@ -24,7 +24,10 @@ mkdir -p ~/local_bridge_testing/logs # 1. Build polkadot binary -TODO: description +git clone https://github.com/paritytech/polkadot.git +cd polkadot +cargo build --release +cp target/release/polkadot ~/local_bridge_testing/bin/polkadot # 2. Build cumulus polkadot-parachain binary cd @@ -32,88 +35,18 @@ cargo build --release --locked -p polkadot-parachain@0.9.230 cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain # 3. Build substrate-relay binary -git clone https://github.com/paritytech/parity-bridges-common.git +git clone https://github.com/paritytech/parity-bridges-common.git cd parity-bridges-common cargo build -p substrate-relay -rm ~/local_bridge_testing/bin/substrate-relay cp target/release/substrate-relay ~/local_bridge_testing/bin/substrate-relay ``` -### Run chains (Rococo + BridgeHub, Wococo + BridgeHub) with Zombienet +### Run chains (Rococo + BridgeHub, Wococo + BridgeHub) from `./scripts/bridges_rococo_wococo.sh` ``` -# Rococo -POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ - POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ - ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet_tests/0004-run_bridge_hubs_rococo.toml - -# Wococo -POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ - POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ - ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet_tests/0004-run_bridge_hubs_wococo.toml -``` - -### Run chains (Rococo + BridgeHub, Wococo + BridgeHub) from `cmd` - -#### Run relay chains (Rococo, Wococo) -``` - - -# Rococo -~/local_bridge_testing/bin/polkadot build-spec --chain rococo-local --disable-default-bootnode --raw > ~/local_bridge_testing/rococo-local-cfde.json -~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --alice --tmp --port 30332 --rpc-port 9932 --ws-port 9942 -~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --bob --tmp --port 30333 --rpc-port 9933 --ws-port 9943 -~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --charlie --tmp --port 30334 --rpc-port 9934 --ws-port 9944 - -# Wococo -~/local_bridge_testing/bin/polkadot build-spec --chain wococo-local --disable-default-bootnode --raw > ~/local_bridge_testing/wococo-local-cfde.json -~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --alice --tmp --port 30335 --rpc-port 9935 --ws-port 9945 -~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --bob --tmp --port 30336 --rpc-port 9936 --ws-port 9946 -~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --charlie --tmp --port 30337 --rpc-port 9937 --ws-port 9947 -~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --dave --tmp --port 30338 --rpc-port 9938 --ws-port 9948 -``` -We need at least 5 nodes together (validator + collator) to finalize blocks. - -#### Run BridgeHub parachains (Rococo, Wococo) - -**1. Generate spec + genesis + wasm (paraId=1013)** -``` -# Rococo -rm ~/local_bridge_testing/bridge-hub-rococo-local-raw.json -~/local_bridge_testing/bin/polkadot-parachain build-spec --chain bridge-hub-rococo-local --raw --disable-default-bootnode > ~/local_bridge_testing/bridge-hub-rococo-local-raw.json -~/local_bridge_testing/bin/polkadot-parachain export-genesis-state --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json > ~/local_bridge_testing/bridge-hub-rococo-local-genesis -~/local_bridge_testing/bin/polkadot-parachain export-genesis-wasm --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json > ~/local_bridge_testing/bridge-hub-rococo-local-genesis-wasm - -# Wococo -rm ~/local_bridge_testing/bridge-hub-wococo-local-raw.json -~/local_bridge_testing/bin/polkadot-parachain build-spec --chain bridge-hub-wococo-local --raw --disable-default-bootnode > ~/local_bridge_testing/bridge-hub-wococo-local-raw.json -~/local_bridge_testing/bin/polkadot-parachain export-genesis-state --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json > ~/local_bridge_testing/bridge-hub-wococo-local-genesis -~/local_bridge_testing/bin/polkadot-parachain export-genesis-wasm --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json > ~/local_bridge_testing/bridge-hub-wococo-local-genesis-wasm -``` - -**2. Run collators (Rococo, Wococo)** -``` -# Rococo -~/local_bridge_testing/bin/polkadot-parachain --collator --alice --force-authoring --tmp --port 40333 --rpc-port 8933 --ws-port 8943 --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 41333 --rpc-port 48933 --ws-port 48943 -~/local_bridge_testing/bin/polkadot-parachain --collator --bob --force-authoring --tmp --port 40334 --rpc-port 8934 --ws-port 8944 --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 41334 -rpc-port 48934 --ws-port 48944 - -# Wococo -~/local_bridge_testing/bin/polkadot-parachain --collator --alice --force-authoring --tmp --port 40335 --rpc-port 8935 --ws-port 8945 --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/wococo-local-cfde.json --port 41335 --rpc-port 48935 --ws-port 48945 -~/local_bridge_testing/bin/polkadot-parachain --collator --bob --force-authoring --tmp --port 40336 --rpc-port 8936 --ws-port 8946 --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json -- --execution wasm --chain ~/local_bridge_testing/wococo-local-cfde.json --port 41336 --rpc-port 48936 --ws-port 48946 -``` - -**3. Activate parachains (Rococo, Wococo) (paraId=1013)** -``` -# Rococo -https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9942#/explorer - -# Wococo -https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9945#/explorer -``` - -**4. After parachain activation, we should see new blocks in collator's node** -``` -https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/parachains +./scripts/bridges_rococo_wococo.sh stop +./scripts/bridges_rococo_wococo.sh start-rococo +./scripts/bridges_rococo_wococo.sh start-wococo ``` ### Run relayers (Rococo, Wococo) @@ -126,7 +59,7 @@ https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/parachains ``` # Rococo -> Wococo RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ - ~/local_bridge_testing/bin/substrate-relay init-bridge bridge-hub-rococo-to-bridge-hub-wococo \ + ~/local_bridge_testing/bin/substrate-relay init-bridge rococo-to-bridge-hub-wococo \ --source-host localhost \ --source-port 48943 \ --target-host localhost \ @@ -135,7 +68,7 @@ RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ # Wococo -> Rococo RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ - ~/local_bridge_testing/bin/substrate-relay init-bridge bridge-hub-wococo-to-bridge-hub-rococo \ + ~/local_bridge_testing/bin/substrate-relay init-bridge wococo-to-bridge-hub-rococo \ --source-host localhost \ --source-port 48945 \ --target-host localhost \ @@ -143,13 +76,42 @@ RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ --target-signer //Bob ``` -**2. Relay headers** +**2. Relay (Grandpa relay-chain) headers** -TODO: +**source-host/source-port** - WS-port of collator's inner RelayChain validator +**target-host/target-port** - WS-port of BridgeHub collator -**2. Relay (Grandpa relay-chain) headers** +``` +# Rococo -> Wococo +RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay relay-headers rococo-to-bridge-hub-wococo \ + --source-host localhost \ + --source-port 48943 \ + --target-host localhost \ + --target-port 8945 \ + --target-signer //Bob \ + --target-transactions-mortality=4 -TODO: +# Wococo -> Rococo +RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay relay-headers wococo-to-bridge-hub-rococo \ + --source-host localhost \ + --source-port 48945 \ + --target-host localhost \ + --target-port 8943 \ + --target-signer //Bob \ + --target-transactions-mortality=4 +``` + +**Check parachain collators:** +- Rococo parachain: + - https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8943#/chainstate + - Pallet: **bridgeWococoGrandpa** + - Keys: **bestFinalized()** +- Wococo parachain: + - https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8945#/chainstate + - Pallet: **bridgeRococoGrandpa** + - Keys: **bestFinalized()** **3. Relay (BridgeHub parachain) headers** @@ -159,6 +121,8 @@ TODO: TODO: +--- + ## Git subtree `./bridges` Add Bridges repo as a local remote and synchronize it with latest `master` from bridges repo: @@ -177,3 +141,21 @@ all into one. Now we use `master` branch, but in future, it could change to some release branch/tag. Original `./bridges/Cargo.toml` was renamed to `./bridges/Cargo.toml_removed_for_bridges_subtree_feature` to avoid confusion for `Cargo` having multiple workspaces. + +---- + +###### TODO: fix zombienet ports as bridges_rococo_wococo.sh, because networks colide and interfere by default, because of autodiscovery on localhost + +###### Run chains (Rococo + BridgeHub, Wococo + BridgeHub) with Zombienet + +``` +# Rococo +POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ + POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ + ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet_tests/0004-run_bridge_hubs_rococo.toml + +# Wococo +POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ + POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ + ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet_tests/0004-run_bridge_hubs_wococo.toml +``` diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh new file mode 100755 index 00000000000..8087769b57f --- /dev/null +++ b/scripts/bridges_rococo_wococo.sh @@ -0,0 +1,144 @@ +#!/bin/bash + +function ensure_binaries() { + if [[ ! -f ~/local_bridge_testing/bin/polkadot ]]; then + echo " Required polkadot binary '~/local_bridge_testing/bin/polkadot' does not exist!" + echo " You need to build it and copy to this location!" + echo " Please, check ./parachains/runtimes/bridge-hubs/README.md (Prepare/Build/Deploy)" + exit 1 + fi + if [[ ! -f ~/local_bridge_testing/bin/polkadot-parachain ]]; then + echo " Required polkadot-parachain binary '~/local_bridge_testing/bin/polkadot-parachain' does not exist!" + echo " You need to build it and copy to this location!" + echo " Please, check ./parachains/runtimes/bridge-hubs/README.md (Prepare/Build/Deploy)" + exit 1 + fi + if [[ ! -f ~/local_bridge_testing/bin/substrate-relay ]]; then + echo " Required substrate-relay binary '~/local_bridge_testing/bin/substrate-relay' does not exist!" + echo " You need to build it and copy to this location!" + echo " Please, check ./parachains/runtimes/bridge-hubs/README.md (Prepare/Build/Deploy)" + exit 1 + fi +} + +function register_parachain() { + local PORT=$1 + local PARA_ID=$2 + local GENESIS_FILE=$3 + local GENESIS_WASM_FILE=$4 + + echo "" + echo "" + echo " Please, register parachain: https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A${PORT}#/sudo" + echo " parasSudoWrapper.sudoScheduleParaInitialize:" + echo " ParaId: $PARA_ID" + echo " Genesis (file): $GENESIS_FILE" + echo " Genesis wasm (file): $GENESIS_WASM_FILE" + echo " Parachain: Yes" + echo "" + echo "" + + # TODO: find the way to do it automatically +} + +function check_parachain_collator() { + local PORT=$1 + local PALLET=$2 + + echo "" + echo "" + echo " Once relayers work, check parachain: https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A${PORT}#/chainstate" + echo " Pallet: ${PALLET}" + echo " Keys:" + echo " bestFinalized()" + echo "" + echo "" +} + +ensure_binaries + +case "$1" in + start-rococo) + # TODO: change to generate: ./bin/polkadot key generate-node-key + VALIDATOR_KEY_ALICE=(12D3KooWRD6kEQuKDn7BCsMfW71U8AEFDYTc7N2dFQthRsjSR8J5 aa5dc9a97f82f9af38d1f16fbc9c72e915577c3ca654f7d33601645ca5b9a8a5) + VALIDATOR_KEY_BOB=(12D3KooWELR4gpc8unmH8WiauK133v397KoMfkkjESWJrzg7Y3Pq 556a36beaeeb95225eb84ae2633bd8135e5ace22f03291853654f07bc5da20ae) + VALIDATOR_KEY_CHARLIE=(12D3KooWRatYNHCRpyzhjPqfSPHYsUTi1zX4452kxSWg8ek99Vun 56e6593340685021d27509c0f48836c1dcb84c5aeba730ada68f54a9ae242e67) + COLLATOR_KEY_ALICE=(12D3KooWAU51GKCfPBfhaKK8gr6XmHNVXd4e96BYL9v2QkWtDrAm 15642c2e078ddfaacd1e68afcc8bab6dd8085b72b3038b799ddfb46bec18696c) + COLLATOR_KEY_BOB=(12D3KooWPg8SEatSi9HvdPJVYBNxeNAvyPTo3Rd4WPdzLRcM8gaS ac651cd6b9a2c7768bca92369ecd4807b54059c33775257e6ba5f72ae91a136d) + + # Start Rococo relay + ~/local_bridge_testing/bin/polkadot build-spec --chain rococo-local --disable-default-bootnode --raw > ~/local_bridge_testing/rococo-local-cfde.json + ~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --alice --tmp --port 30332 --rpc-port 9932 --ws-port 9942 --no-mdns --node-key ${VALIDATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30333/p2p/${VALIDATOR_KEY_BOB[0]} &> ~/local_bridge_testing/logs/rococo_relay_alice.log & + ~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --bob --tmp --port 30333 --rpc-port 9933 --ws-port 9943 --no-mdns --node-key ${VALIDATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/30332/p2p/${VALIDATOR_KEY_ALICE[0]} &> ~/local_bridge_testing/logs/rococo_relay_bob.log & + ~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --charlie --tmp --port 30334 --rpc-port 9934 --ws-port 9944 --no-mdns ${VALIDATOR_KEY_CHARLIE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30332/p2p/${VALIDATOR_KEY_ALICE[0]} &> ~/local_bridge_testing/logs/rococo_relay_charlie.log & + sleep 2 + + # Prepare Rococo parachain + rm ~/local_bridge_testing/bridge-hub-rococo-local-raw.json + ~/local_bridge_testing/bin/polkadot-parachain build-spec --chain bridge-hub-rococo-local --raw --disable-default-bootnode > ~/local_bridge_testing/bridge-hub-rococo-local-raw.json + ~/local_bridge_testing/bin/polkadot-parachain export-genesis-state --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json > ~/local_bridge_testing/bridge-hub-rococo-local-genesis + ~/local_bridge_testing/bin/polkadot-parachain export-genesis-wasm --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json > ~/local_bridge_testing/bridge-hub-rococo-local-genesis-wasm + + # Rococo + ~/local_bridge_testing/bin/polkadot-parachain --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json --collator --alice --force-authoring --tmp --port 40333 --rpc-port 8933 --ws-port 8943 --no-mdns --node-key ${COLLATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/40334/p2p/${COLLATOR_KEY_BOB[0]} -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 41333 --rpc-port 48933 --ws-port 48943 --no-mdns --node-key ${COLLATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30332/p2p/${VALIDATOR_KEY_ALICE[0]} &> ~/local_bridge_testing/logs/rococo_para_alice.log & + ~/local_bridge_testing/bin/polkadot-parachain --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json --collator --bob --force-authoring --tmp --port 40334 --rpc-port 8934 --ws-port 8944 --no-mdns --node-key ${COLLATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/40333/p2p/${COLLATOR_KEY_ALICE[0]} -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 41334 --rpc-port 48934 --ws-port 48944 --no-mdns --node-key ${COLLATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/30333/p2p/${VALIDATOR_KEY_BOB[0]} &> ~/local_bridge_testing/logs/rococo_para_bob.log & + + register_parachain 9942 1013 ~/local_bridge_testing/bridge-hub-rococo-local-genesis ~/local_bridge_testing/bridge-hub-rococo-local-genesis-wasm + check_parachain_collator 8943 bridgeWococoGrandpa + ;; + start-wococo) + # TODO: change to generate: ./bin/polkadot key generate-node-key + VALIDATOR_KEY_ALICE=(12D3KooWKB4SqNJAttmDHXEKtETLPr8Nixso8gUNzNKDS9b6HtYj 8c87694d3a3b3a0e201caceaae095aac66a76ba37545c439f7e0d986670da8e6) + VALIDATOR_KEY_BOB=(12D3KooWJq6xkfyV3LFxoNgsCskAwLv6VfLhL3cjHfW4UxDNwroF 9ebcd7371b427d63c087762fe3dc5a1d4b01875cb8611c263feda21364d4a329) + VALIDATOR_KEY_CHARLIE=(12D3KooW9yQQXz6xUJY2YgizKTFLS5fPaPi4KznQdMHEW4Hzt3NL 8bb1c50421a73082b8275ead6e3f150a774a0f8d6ad4a786df5d5b7688115cb1) + VALIDATOR_KEY_DAVE=(12D3KooWJP1R7XxqEuSryXSKYRVz9wdMRvd5LSqjRpdK4AvjnB12 e160d8b0ef4d66e02abf6663417b024f27f6cb2f826b746f3d267c58a32e9c05) + COLLATOR_KEY_ALICE=(12D3KooWNsQbEdW1eyC6TXAJCXxZ8qzkCJPWFqhWjco1SYYdaKPU 93ac8257255961b3d3ec0038747f0f9c7ddaa5dd352297daa352e098285965b7) + COLLATOR_KEY_BOB=(12D3KooWQ9yxdcf7kavoaKNWmt92tkCGQ8C4sq2JNBgCjvGtvvFg 7e396f01a8a74ecf2010ab2d57ca9fc6c5d673914d97ad6a066fe724e60c3412) + + # Start Wococo relay + ~/local_bridge_testing/bin/polkadot build-spec --chain wococo-local --disable-default-bootnode --raw > ~/local_bridge_testing/wococo-local-cfde.json + ~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --alice --tmp --port 30335 --rpc-port 9935 --ws-port 9945 --no-mdns --node-key ${VALIDATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30336/p2p/${VALIDATOR_KEY_BOB[0]} &> ~/local_bridge_testing/logs/wococo_relay_alice.log & + ~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --bob --tmp --port 30336 --rpc-port 9936 --ws-port 9946 --no-mdns --node-key ${VALIDATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/30335/p2p/${VALIDATOR_KEY_ALICE[0]} &> ~/local_bridge_testing/logs/wococo_relay_bob.log & + ~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --charlie --tmp --port 30337 --rpc-port 9937 --ws-port 9947 --no-mdns --node-key ${VALIDATOR_KEY_CHARLIE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30335/p2p/${VALIDATOR_KEY_ALICE[0]} &> ~/local_bridge_testing/logs/wococo_relay_charlie.log & + ~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --dave --tmp --port 30338 --rpc-port 9938 --ws-port 9948 --no-mdns --node-key ${VALIDATOR_KEY_DAVE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30336/p2p/${VALIDATOR_KEY_BOB[0]} &> ~/local_bridge_testing/logs/wococo_relay_dave.log & + sleep 2 + + # Prepare Wococo parachain + rm ~/local_bridge_testing/bridge-hub-wococo-local-raw.json + ~/local_bridge_testing/bin/polkadot-parachain build-spec --chain bridge-hub-wococo-local --raw --disable-default-bootnode > ~/local_bridge_testing/bridge-hub-wococo-local-raw.json + ~/local_bridge_testing/bin/polkadot-parachain export-genesis-state --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json > ~/local_bridge_testing/bridge-hub-wococo-local-genesis + ~/local_bridge_testing/bin/polkadot-parachain export-genesis-wasm --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json > ~/local_bridge_testing/bridge-hub-wococo-local-genesis-wasm + + # Wococo + ~/local_bridge_testing/bin/polkadot-parachain --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json --collator --alice --force-authoring --tmp --port 40335 --rpc-port 8935 --ws-port 8945 --no-mdns --node-key ${COLLATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/40336/p2p/${COLLATOR_KEY_BOB[0]} -- --execution wasm --chain ~/local_bridge_testing/wococo-local-cfde.json --port 41335 --rpc-port 48935 --ws-port 48945 --no-mdns --node-key ${COLLATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30335/p2p/${VALIDATOR_KEY_ALICE[0]} &> ~/local_bridge_testing/logs/wococo_para_alice.log & + ~/local_bridge_testing/bin/polkadot-parachain --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json --collator --bob --force-authoring --tmp --port 40336 --rpc-port 8936 --ws-port 8946 --no-mdns --node-key ${COLLATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/40335/p2p/${COLLATOR_KEY_ALICE[0]} -- --execution wasm --chain ~/local_bridge_testing/wococo-local-cfde.json --port 41336 --rpc-port 48936 --ws-port 48946 --no-mdns --node-key ${COLLATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/30336/p2p/${VALIDATOR_KEY_BOB[0]} &> ~/local_bridge_testing/logs/wococo_para_bob.log & + + register_parachain 9945 1013 ~/local_bridge_testing/bridge-hub-wococo-local-genesis ~/local_bridge_testing/bridge-hub-wococo-local-genesis-wasm + check_parachain_collator 8945 bridgeRococoGrandpa + ;; + init-ro-wo) + # Init bridge Rococo->Wococo + RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay init-bridge rococo-to-bridge-hub-wococo \ + --source-host localhost \ + --source-port 48943 \ + --target-host localhost \ + --target-port 8945 \ + --target-signer //Bob + ;; + init-wo-ro) + # Init bridge Wococo->Rococo + RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay init-bridge wococo-to-bridge-hub-rococo \ + --source-host localhost \ + --source-port 48945 \ + --target-host localhost \ + --target-port 8943 \ + --target-signer //Bob + ;; + stop) + pkill -f polkadot + pkill -f parachain + ;; + *) echo "A command is require. Supported commands: start-rococo, start-wococo, init-ro-wo, init-wo-ro, stop"; exit 1;; +esac From b8887433f1d922303bd82fb1f511fb93d989257a Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 2 Sep 2022 00:27:59 +0200 Subject: [PATCH 066/263] [BridgeHub] Fixed TrackedParachains --- parachains/runtimes/bridge-hubs/README.md | 46 ++++++++++++++++++- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 19 ++++++-- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index f10fcf5764c..8aa4bbde04f 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -45,8 +45,12 @@ cp target/release/substrate-relay ~/local_bridge_testing/bin/substrate-relay ``` ./scripts/bridges_rococo_wococo.sh stop + ./scripts/bridges_rococo_wococo.sh start-rococo +# TODO: check log and activate parachain manually + ./scripts/bridges_rococo_wococo.sh start-wococo +# TODO: check log and activate parachain manually ``` ### Run relayers (Rococo, Wococo) @@ -56,6 +60,16 @@ cp target/release/substrate-relay ~/local_bridge_testing/bin/substrate-relay - `Alice` is `Sudo` **1. Init bridges** + +Need to wait for parachain activation, then run: + +``` +./scripts/bridges_rococo_wococo.sh init-ro-wo +./scripts/bridges_rococo_wococo.sh init-wo-ro +``` + +or + ``` # Rococo -> Wococo RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ @@ -115,7 +129,37 @@ RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ **3. Relay (BridgeHub parachain) headers** -TODO: +``` +# Rococo -> Wococo +RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay relay-parachains bridge-hub-rococo-to-bridge-hub-wococo \ + --source-host localhost \ + --source-port 48943 \ + --target-host localhost \ + --target-port 8945 \ + --target-signer //Bob \ + --target-transactions-mortality=4 + +# Wococo -> Rococo +RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay relay-parachains bridge-hub-wococo-to-bridge-hub-rococo \ + --source-host localhost \ + --source-port 48945 \ + --target-host localhost \ + --target-port 8943 \ + --target-signer //Bob \ + --target-transactions-mortality=4 +``` + +**Check parachain collators:** +- Rococo parachain: + - https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8943#/chainstate + - Pallet: **bridgeWococoParachain** + - Keys: **bestParaHeads()** +- Wococo parachain: + - https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8945#/chainstate + - Pallet: **bridgeRococoParachain** + - Keys: **bestParaHeads()** **4. Relay (XCM) messages** diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index ba378e78a4f..b60b132b9ad 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -43,7 +43,7 @@ use sp_version::RuntimeVersion; use frame_support::{ construct_runtime, parameter_types, - traits::{Everything, IsInVec}, + traits::Everything, weights::{ constants::WEIGHT_PER_SECOND, ConstantMultiplier, DispatchClass, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, @@ -192,6 +192,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { state_version: 1, }; +// TODO:check-parameter - remove all unneeded and reuse frm parachain_common + /// This determines the average expected block time that we are targeting. /// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. /// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked @@ -234,7 +236,7 @@ pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } } -// TODO: check-parameter - move to bridges/primitives, once rebased and would compile with bp_bridge_hub_xyz dependencies +// TODO:check-parameter - move to bridges/primitives, once rebased and would compile with bp_bridge_hub_xyz dependencies mod runtime_api { use super::BlockNumber; use super::Hash; @@ -347,6 +349,7 @@ impl pallet_authorship::Config for Runtime { type EventHandler = (CollatorSelection,); } +// TODO:check-parameter parameter_types! { pub const ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT; pub const MaxLocks: u32 = 50; @@ -367,6 +370,7 @@ impl pallet_balances::Config for Runtime { type ReserveIdentifier = [u8; 8]; } +// TODO:check-parameter parameter_types! { /// Relay Chain `TransactionByteFee` / 10 pub const TransactionByteFee: Balance = 10 * MICROUNIT; @@ -483,7 +487,9 @@ impl pallet_sudo::Config for Runtime { // Add bridge pallets (GPA) parameter_types! { + // TODO:check-parameter pub const MaxRequests: u32 = 64; + // TODO:check-parameter pub const HeadersToKeep: u32 = 1024; } @@ -493,6 +499,7 @@ impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_wococo::Wococo; type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; + // TODO:check-parameter type WeightInfo = (); } @@ -502,6 +509,7 @@ impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_rococo::Rococo; type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; + // TODO:check-parameter type WeightInfo = (); } @@ -511,26 +519,27 @@ parameter_types! { pub const ParachainHeadsToKeep: u32 = 64; pub const RococoBridgeParachainPalletName: &'static str = ROCOCO_BRIDGE_PARA_PALLET_NAME; pub const WococoBridgeParachainPalletName: &'static str = WOCOCO_BRIDGE_PARA_PALLET_NAME; - pub GetTenFirstParachains: Vec = (0..10).map(ParaId).collect(); } /// Add parachain bridge pallet to track Wococo bridge hub parachain pub type BridgeParachainWococoInstance = pallet_bridge_parachains::Instance1; impl pallet_bridge_parachains::Config for Runtime { + // TODO:check-parameter type WeightInfo = (); type BridgesGrandpaPalletInstance = BridgeGrandpaWococoInstance; type ParasPalletName = WococoBridgeParachainPalletName; - type TrackedParachains = IsInVec; + type TrackedParachains = Everything; type HeadsToKeep = ParachainHeadsToKeep; } /// Add parachain bridge pallet to track Rococo bridge hub parachain pub type BridgeParachainRococoInstance = pallet_bridge_parachains::Instance2; impl pallet_bridge_parachains::Config for Runtime { + // TODO:check-parameter type WeightInfo = (); type BridgesGrandpaPalletInstance = BridgeGrandpaRococoInstance; type ParasPalletName = RococoBridgeParachainPalletName; - type TrackedParachains = IsInVec; + type TrackedParachains = Everything; type HeadsToKeep = ParachainHeadsToKeep; } From f721364bdd431aaad4fba03f1db63bb2b80ce658 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 19 Sep 2022 13:34:28 +0200 Subject: [PATCH 067/263] [BridgeHub] Fixed Paras pallet name for parachain heads relaying --- parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index b60b132b9ad..41b50791566 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -513,8 +513,8 @@ impl pallet_bridge_grandpa::Config for Runtime { type WeightInfo = (); } -pub const ROCOCO_BRIDGE_PARA_PALLET_NAME: &str = "RococoBridgeHubParachainPallet"; -pub const WOCOCO_BRIDGE_PARA_PALLET_NAME: &str = "WococoBridgeHubParachainPallet"; +pub const ROCOCO_BRIDGE_PARA_PALLET_NAME: &str = "Paras"; +pub const WOCOCO_BRIDGE_PARA_PALLET_NAME: &str = "Paras"; parameter_types! { pub const ParachainHeadsToKeep: u32 = 64; pub const RococoBridgeParachainPalletName: &'static str = ROCOCO_BRIDGE_PARA_PALLET_NAME; From e4fc870b0675ff089ab45ee47c2d43067dd2489b Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 20 Sep 2022 10:54:47 +0200 Subject: [PATCH 068/263] [BridgeHub] Add bridge-hub-rococo to CI pipelines --- .github/workflows/release-30_create-draft.yml | 6 ++++++ .github/workflows/srtool.yml | 2 ++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/release-30_create-draft.yml b/.github/workflows/release-30_create-draft.yml index ffd33d6f1e7..38c105d8b8e 100644 --- a/.github/workflows/release-30_create-draft.yml +++ b/.github/workflows/release-30_create-draft.yml @@ -50,6 +50,8 @@ jobs: runtime: statemint - category: assets runtime: westmint + - category: bridge-hubs + runtime: bridge-hub-rococo - category: collectives runtime: collectives-polkadot - category: contracts @@ -150,6 +152,7 @@ jobs: WESTMINT_DIGEST: ${{ github.workspace}}/westmint-srtool-json/westmint-srtool-digest.json STATEMINE_DIGEST: ${{ github.workspace}}/statemine-srtool-json/statemine-srtool-digest.json STATEMINT_DIGEST: ${{ github.workspace}}/statemint-srtool-json/statemint-srtool-digest.json + BRIDE_HUB_ROCOCO_DIGEST: ${{ github.workspace}}/bridge-hub-rococo-srtool-json/bridge-hub-rococo-srtool-digest.json COLLECTIVES_POLKADOT_DIGEST: ${{ github.workspace}}/collectives-polkadot-srtool-json/collectives-polkadot-srtool-digest.json ROCOCO_PARA_DIGEST: ${{ github.workspace}}/rococo-parachain-srtool-json/rococo-parachain-srtool-digest.json CANVAS_KUSAMA_DIGEST: ${{ github.workspace}}/contracts-rococo-srtool-json/contracts-rococo-srtool-digest.json @@ -165,6 +168,7 @@ jobs: ls -al $WESTMINT_DIGEST || true ls -al $STATEMINE_DIGEST || true ls -al $STATEMINT_DIGEST || true + ls -al $BRIDE_HUB_ROCOCO_DIGEST || true ls -al $COLLECTIVES_POLKADOT_DIGEST || true ls -al $ROCOCO_PARA_DIGEST || true ls -al $CANVAS_KUSAMA_DIGEST || true @@ -216,6 +220,8 @@ jobs: runtime: statemint - category: assets runtime: westmint + - category: bridge-hubs + runtime: bridge-hub-rococo - category: collectives runtime: collectives-polkadot - category: contracts diff --git a/.github/workflows/srtool.yml b/.github/workflows/srtool.yml index 82b8585b358..ec8ab3bf902 100644 --- a/.github/workflows/srtool.yml +++ b/.github/workflows/srtool.yml @@ -37,6 +37,8 @@ jobs: runtime: statemint - category: assets runtime: westmint + - category: bridge-hubs + runtime: bridge-hub-rococo - category: collectives runtime: collectives-polkadot - category: contracts From a2a630fe7ac4bf9cd4cacb5293a4f94175407331 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 21 Sep 2022 17:29:36 +0200 Subject: [PATCH 069/263] [BridgeHub] pallet_transaction_payment + Event --- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 14 +++++------ .../bridge-hub-rococo/src/xcm_config.rs | 23 ++----------------- 2 files changed, 8 insertions(+), 29 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 41b50791566..bb5b4f55aed 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -378,8 +378,7 @@ parameter_types! { } impl pallet_transaction_payment::Config for Runtime { - // TODO: hacked - // type Event = Event; + type Event = Event; type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; @@ -417,7 +416,6 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = (); - type PriceForSiblingDelivery = (); } impl cumulus_pallet_dmp_queue::Config for Runtime { @@ -526,6 +524,7 @@ pub type BridgeParachainWococoInstance = pallet_bridge_parachains::Instance1; impl pallet_bridge_parachains::Config for Runtime { // TODO:check-parameter type WeightInfo = (); + type Event = Event; type BridgesGrandpaPalletInstance = BridgeGrandpaWococoInstance; type ParasPalletName = WococoBridgeParachainPalletName; type TrackedParachains = Everything; @@ -535,6 +534,7 @@ impl pallet_bridge_parachains::Config for Runtime /// Add parachain bridge pallet to track Rococo bridge hub parachain pub type BridgeParachainRococoInstance = pallet_bridge_parachains::Instance2; impl pallet_bridge_parachains::Config for Runtime { + type Event = Event; // TODO:check-parameter type WeightInfo = (); type BridgesGrandpaPalletInstance = BridgeGrandpaRococoInstance; @@ -563,9 +563,7 @@ construct_runtime!( // Monetary stuff. Balances: pallet_balances::{Pallet, Call, Storage, Config, Event} = 10, - // TODO: hacked - // TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event} = 11, - TransactionPayment: pallet_transaction_payment::{Pallet, Storage} = 11, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event} = 11, // Collator support. The order of these 4 are important and shall not change. Authorship: pallet_authorship::{Pallet, Call, Storage} = 20, @@ -585,11 +583,11 @@ construct_runtime!( // Wococo bridge modules BridgeWococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Config} = 41, - BridgeWococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage} = 42, + BridgeWococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage, Event} = 42, // Rococo bridge modules BridgeRococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Config} = 43, - BridgeRococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage} = 44, + BridgeRococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage, Event} = 44, // Sudo Sudo: pallet_sudo::{Pallet, Call, Config, Event, Storage} = 100, diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 216997bdf2e..543962807d3 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -182,26 +182,6 @@ pub type Barrier = ( /// XCM weigher type. pub type XcmWeigher = FixedWeightBounds; -// TODO: hacked -// pub struct XcmConfig; -// impl xcm_executor::Config for XcmConfig { -// type Call = Call; -// type XcmSender = XcmRouter; -// // How to withdraw and deposit an asset. -// type AssetTransactor = LocalAssetTransactor; -// type OriginConverter = XcmOriginToTransactDispatchOrigin; -// type IsReserve = NativeAsset; -// type IsTeleporter = (); // Teleporting is disabled. -// type LocationInverter = LocationInverter; -// type Barrier = Barrier; -// type Weigher = FixedWeightBounds; -// type Trader = -// UsingComponents>; -// type ResponseHandler = PolkadotXcm; -// type AssetTrap = PolkadotXcm; -// type AssetClaims = PolkadotXcm; -// type SubscriptionService = PolkadotXcm; -// } pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type Call = Call; @@ -225,6 +205,7 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; + type CallDispatcher = Call; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. @@ -234,7 +215,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); From 812917765a8d9b33bcac6d5815ae08bc4288652b Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 26 Sep 2022 11:58:26 +0200 Subject: [PATCH 070/263] TMP: fix missing dep to std --- parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index d934d21d7a0..db023747414 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -119,6 +119,7 @@ std = [ "pallet-transaction-payment/std", "pallet-xcm/std", "parachain-info/std", + "parachains-common/std", "polkadot-parachain/std", "polkadot-runtime-common/std", "sp-api/std", From e50628f44b5df490df245f040059fadc0dbdc36a Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 3 Oct 2022 15:03:13 +0200 Subject: [PATCH 071/263] [BridgeHub] Added chain_spec for live Rococo/Wococo --- .../src/chain_spec/bridge_hubs.rs | 91 ++++++++++++++++++- polkadot-parachain/src/command.rs | 4 + 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/polkadot-parachain/src/chain_spec/bridge_hubs.rs index 4bef37f8932..ee0e62cd17b 100644 --- a/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -22,7 +22,9 @@ use std::{path::PathBuf, str::FromStr}; /// Collects all supported BridgeHub configurations #[derive(Debug, PartialEq)] pub enum BridgeHubRuntimeType { + Rococo, RococoLocal, + Wococo, WococoLocal, } @@ -31,7 +33,9 @@ impl FromStr for BridgeHubRuntimeType { fn from_str(value: &str) -> Result { match value { + rococo::BRIDGE_HUB_ROCOCO => Ok(BridgeHubRuntimeType::Rococo), rococo::BRIDGE_HUB_ROCOCO_LOCAL => Ok(BridgeHubRuntimeType::RococoLocal), + wococo::BRIDGE_HUB_WOCOCO => Ok(BridgeHubRuntimeType::Wococo), wococo::BRIDGE_HUB_WOCOCO_LOCAL => Ok(BridgeHubRuntimeType::WococoLocal), _ => Err(format!("Value '{}' is not configured yet", value)), } @@ -43,22 +47,29 @@ impl BridgeHubRuntimeType { pub fn chain_spec_from_json_file(&self, path: PathBuf) -> Result, String> { Ok(Box::new(match self { + BridgeHubRuntimeType::Rococo => rococo::BridgeHubChainSpec::from_json_file(path)?, BridgeHubRuntimeType::RococoLocal => rococo::BridgeHubChainSpec::from_json_file(path)?, + BridgeHubRuntimeType::Wococo => wococo::BridgeHubChainSpec::from_json_file(path)?, BridgeHubRuntimeType::WococoLocal => wococo::BridgeHubChainSpec::from_json_file(path)?, })) } pub fn load_config(&self) -> Box { Box::new(match self { + BridgeHubRuntimeType::Rococo => + rococo::live_config(rococo::BRIDGE_HUB_ROCOCO, "Rococo BrideHub", "rococo", ParaId::new(1013)), BridgeHubRuntimeType::RococoLocal => - rococo::local_config("Rococo BrideHub Local", "rococo-local", ParaId::new(1013)), + rococo::local_config(rococo::BRIDGE_HUB_ROCOCO_LOCAL, "Rococo BrideHub Local", "rococo-local", ParaId::new(1013)), + BridgeHubRuntimeType::Wococo => + wococo::live_config(wococo::BRIDGE_HUB_WOCOCO, "Wococo BrideHub", "wococo", ParaId::new(1013)), BridgeHubRuntimeType::WococoLocal => - wococo::local_config("Wococo BrideHub Local", "wococo-local", ParaId::new(1013)), + wococo::local_config(wococo::BRIDGE_HUB_WOCOCO_LOCAL, "Wococo BrideHub Local", "wococo-local", ParaId::new(1013)), }) } pub fn runtime_version(&self) -> &'static RuntimeVersion { match self { + BridgeHubRuntimeType::Rococo | BridgeHubRuntimeType::Wococo | BridgeHubRuntimeType::RococoLocal | BridgeHubRuntimeType::WococoLocal => { // this is intentional, for Rococo/Wococo we just want to have one runtime, which is configured for both sides &bridge_hub_rococo_runtime::VERSION @@ -90,6 +101,7 @@ pub mod rococo { use sc_chain_spec::ChainType; use sp_core::sr25519; + pub(crate) const BRIDGE_HUB_ROCOCO: &str = "bridge-hub-rococo"; pub(crate) const BRIDGE_HUB_ROCOCO_LOCAL: &str = "bridge-hub-rococo-local"; /// Specialized `ChainSpec` for the normal parachain runtime. @@ -98,7 +110,67 @@ pub mod rococo { pub type RuntimeApi = bridge_hub_rococo_runtime::RuntimeApi; + pub fn live_config( + id: &str, + chain_name: &str, + relay_chain: &str, + para_id: ParaId, + ) -> BridgeHubChainSpec { + let properties = sc_chain_spec::Properties::new(); + // TODO: check + // properties.insert("ss58Format".into(), 2.into()); + // properties.insert("tokenSymbol".into(), "ROC".into()); + // properties.insert("tokenDecimals".into(), 12.into()); + + BridgeHubChainSpec::from_genesis( + // Name + chain_name, + // ID + super::ensure_id(id).expect("invalid id"), + ChainType::Live, + move || { + genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + para_id, + Some(get_account_id_from_seed::("Alice")), + Some(get_account_id_from_seed::("Bob")), + ) + }, + Vec::new(), + None, + None, + None, + Some(properties), + Extensions { relay_chain: relay_chain.to_string(), para_id: para_id.into() }, + ) + } + pub fn local_config( + id: &str, chain_name: &str, relay_chain: &str, para_id: ParaId, @@ -113,7 +185,7 @@ pub mod rococo { // Name chain_name, // ID - super::ensure_id(BRIDGE_HUB_ROCOCO_LOCAL).expect("invalid id"), + super::ensure_id(id).expect("invalid id"), ChainType::Local, move || { genesis( @@ -216,16 +288,27 @@ pub mod wococo { use super::ParaId; use crate::chain_spec::bridge_hubs::rococo; + pub(crate) const BRIDGE_HUB_WOCOCO: &str = "bridge-hub-wococo"; pub(crate) const BRIDGE_HUB_WOCOCO_LOCAL: &str = "bridge-hub-wococo-local"; pub type BridgeHubChainSpec = rococo::BridgeHubChainSpec; pub type RuntimeApi = rococo::RuntimeApi; pub fn local_config( + id: &str, + chain_name: &str, + relay_chain: &str, + para_id: ParaId, + ) -> BridgeHubChainSpec { + rococo::local_config(id, chain_name, relay_chain, para_id) + } + + pub fn live_config( + id: &str, chain_name: &str, relay_chain: &str, para_id: ParaId, ) -> BridgeHubChainSpec { - rococo::local_config(chain_name, relay_chain, para_id) + rococo::live_config(id, chain_name, relay_chain, para_id) } } diff --git a/polkadot-parachain/src/command.rs b/polkadot-parachain/src/command.rs index e5734e4320d..5e0950a5673 100644 --- a/polkadot-parachain/src/command.rs +++ b/polkadot-parachain/src/command.rs @@ -481,10 +481,12 @@ macro_rules! construct_async_run { Runtime::BridgeHub(bridge_hub_runtime_type) => { runner.async_run(|$config| { let $components = match bridge_hub_runtime_type { + chain_spec::bridge_hubs::BridgeHubRuntimeType::Rococo | chain_spec::bridge_hubs::BridgeHubRuntimeType::RococoLocal => new_partial::( &$config, crate::service::aura_build_import_queue::<_, AuraId>, )?, + chain_spec::bridge_hubs::BridgeHubRuntimeType::Wococo | chain_spec::bridge_hubs::BridgeHubRuntimeType::WococoLocal => new_partial::( &$config, crate::service::aura_build_import_queue::<_, AuraId>, @@ -779,11 +781,13 @@ pub fn run() -> Result<()> { .map(|r| r.0) .map_err(Into::into), Runtime::BridgeHub(bridge_hub_runtime_type) => match bridge_hub_runtime_type { + chain_spec::bridge_hubs::BridgeHubRuntimeType::Rococo | chain_spec::bridge_hubs::BridgeHubRuntimeType::RococoLocal => crate::service::start_generic_aura_node::< chain_spec::bridge_hubs::rococo::RuntimeApi, AuraId, >(config, polkadot_config, collator_options, id, hwbench), + chain_spec::bridge_hubs::BridgeHubRuntimeType::Wococo | chain_spec::bridge_hubs::BridgeHubRuntimeType::WococoLocal => crate::service::start_generic_aura_node::< chain_spec::bridge_hubs::wococo::RuntimeApi, From e406a37bb39b7223ffc066a78cfa9acb6eaacf37 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 4 Oct 2022 13:25:03 +0200 Subject: [PATCH 072/263] [BridgeHub] Added chain_spec for live Rococo/Wococo - root/owner to None --- polkadot-parachain/src/chain_spec/bridge_hubs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/polkadot-parachain/src/chain_spec/bridge_hubs.rs index ee0e62cd17b..ed2f678ee54 100644 --- a/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -156,8 +156,8 @@ pub mod rococo { get_account_id_from_seed::("Ferdie//stash"), ], para_id, - Some(get_account_id_from_seed::("Alice")), - Some(get_account_id_from_seed::("Bob")), + None, + None, ) }, Vec::new(), From c60aa82d60a70411a5141adef266c5029e5e28cb Mon Sep 17 00:00:00 2001 From: Anthony Lazam Date: Wed, 5 Oct 2022 17:24:03 +0800 Subject: [PATCH 073/263] Bridge-Hub Rococo Chainspec (#1729) * Bridge-Hub Rococo Chainspec --- parachains/chain-specs/bridge-hub-rococo.json | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 parachains/chain-specs/bridge-hub-rococo.json diff --git a/parachains/chain-specs/bridge-hub-rococo.json b/parachains/chain-specs/bridge-hub-rococo.json new file mode 100644 index 00000000000..97bacbbf22c --- /dev/null +++ b/parachains/chain-specs/bridge-hub-rococo.json @@ -0,0 +1,85 @@ +{ + "name": "Rococo BrideHub", + "id": "bridge-hub-rococo", + "chainType": "Live", + "bootNodes": [ + "/dns/rococo-bridge-hub-collator-0.parity-testnet.parity.io/tcp/30334/p2p/12D3KooWNARtf7sSXKHsdzHctSZYaPMskTw3fVFbBoRK4Joc1KqY", + "/dns/rococo-bridge-hub-collator-1.parity-testnet.parity.io/tcp/30334/p2p/12D3KooWB4E9XQFHerwn3HH2biX6cBkMu6pkkVieMoSGfmMHZsFv", + "/dns/rococo-bridge-hub-collator-2.parity-testnet.parity.io/tcp/30334/p2p/12D3KooWHC2RcMS8sHjicdS9FMTy1XDWvyZDQRut6TwxyWpBMUo3", + "/dns/rococo-bridge-hub-collator-3.parity-testnet.parity.io/tcp/30334/p2p/12D3KooWP3E4oQUh8XejaubDZKoD1bNapu7UEvPc3cCfsNdQngSq", + "/dns/rococo-bridge-hub-collator-0.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWNARtf7sSXKHsdzHctSZYaPMskTw3fVFbBoRK4Joc1KqY", + "/dns/rococo-bridge-hub-collator-1.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWB4E9XQFHerwn3HH2biX6cBkMu6pkkVieMoSGfmMHZsFv", + "/dns/rococo-bridge-hub-collator-2.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWHC2RcMS8sHjicdS9FMTy1XDWvyZDQRut6TwxyWpBMUo3", + "/dns/rococo-bridge-hub-collator-3.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWP3E4oQUh8XejaubDZKoD1bNapu7UEvPc3cCfsNdQngSq" + ], + "telemetryEndpoints": null, + "protocolId": null, + "properties": {}, + "relay_chain": "rococo", + "para_id": 1013, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f": "0xf5030000", + "0x0d715f2646c8f85767b5d2764bb278264e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x15464cac3378d46f113cd5b7a4d71c84476f594316a7dfe49c1f352d95abdaf1": "0x00000000", + "0x15464cac3378d46f113cd5b7a4d71c844e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x15464cac3378d46f113cd5b7a4d71c845579297f4dfb9609e7e4c2ebab9ce40a": "0x1094e8f841122bad62ecd5016f80587ef7d91043c828e3112f668523db811cbe320676ce35043f553c1a3775a10ba54bd5757e48ebe38bbf2b4c4986896dcda702cc4c407bd279279ebbfdb5ae2cd29b04ac748a90bcc23a910e303104e47b8c5e741100320fca26c26c665a09fda76a2b2b11ab6d36acb8942132be5b436e7602", + "0x15464cac3378d46f113cd5b7a4d71c84579f5a43435b04a98d64da0cefe18505": "0x0a000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9050f9ffb4503e7865bae8a399c89a5da52bc71c1eca5353749542dfdf0af97bf764f9c2f44e860cd485f1cd86400f649": "0x0000000000000000010000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95c7acbba5f59ca99cd7b8256f6342aa094e8f841122bad62ecd5016f80587ef7d91043c828e3112f668523db811cbe32": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9867d04f5fd090d96ed68a6355487eb8a741100320fca26c26c665a09fda76a2b2b11ab6d36acb8942132be5b436e7602": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9aba72baede0a06ab63b4b66340fe145acc4c407bd279279ebbfdb5ae2cd29b04ac748a90bcc23a910e303104e47b8c5e": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9c3e60052e92d2d3cfad167f41164dd110676ce35043f553c1a3775a10ba54bd5757e48ebe38bbf2b4c4986896dcda702": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x04446272696467652d6875622d726f636f636f", + "0x365c9cdbf82b9bda69e4bbdf1b38a7834e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x3a63": "0x", + "0x3a636f6465": "0x52bc537646db8e0528b52ffd0058147c04de43c5471053205adaa403e5359022427e9a8f1ce9335b4e6262a8de34023299aa8ef49b82d21f60f545eb8f587da904aaf9ea6b0c0229d4198f28423ff92884fff66c3446ebe5b5dc02ccb76d844842f6967bcb2da59429c9fc11ed0edc0fde2876969bfbd61ea0fae894337f29deb1fcd428f7963b2ddaebdf76f6c9729d86fefce6fc953eb96f672729516f4e4ea36ec5a151d7f4965bfea351d12dcfa151577a74cbdd230d39034bf45bdf727763d91eb7627e59d081010c60c0cddaef0defdc01eddc1c76aeebf2ce9d03e2de3964777ec2a15373c73975232baa5ba1808927bd5b51c1e29df65ca71550d92b4da3a2b39b1a259d7d47a36ece5f696b9fe246737591444001b44c725e7a959b49faad4368fac42e2d87ee5ecab2f5e1b04b7bf9a3dff9dc86195dc25481891758018c1de9b351d84baf28b0e2a5d3348abe44410e5efa8e4eb9151010900a76a4df74caed487b99e47cb4ae4fd08354575f6a425bfbc4de1b4de3b097aaa8687cf66e858228fc0dffb00afeb08ae1430305486852d627d7a7bbe3d947ef5654b4bca94fec6e07fc4e391010d00976aebfa66fd50eceb0a28c2d4e2ce1053476e6b7d797da21a38931b4c4e00a3560b0d34e9d8f3adfdf69edafd339a0963ba731c420c4db9ae47ccff3ac4fecf465ae5177a6dcc3998dba964f6fe9d02f939c9f7e399ddbec53fbb5e5502d77df1e5da7af6d7efb9c9b7bd67800829f2ddb24e7a5ed811eb367779a0ed0ba3e3debfa34b9ad5077e31c907ed7852858b0842e84c1450facb0e3bc36ca7ae73a9d823bcecb3b2f6d51d92f0eefdcbdd4dc619f2fc540404040557638e79d9f3a05779cbb1bb63d35ca7bef3df6f7b8bb1d42014874c824e767dc7cde13a9a16209553c3bdc98e4c0678790f909fc9ed3b7b1d3d8753a467ffe68676fb3da59636fed35f316a8049179d27ee7bfe7eff97bcfcbbff71e74370f044344e18d6a6f3f35eac2eff69b46f1f71057526fa76fe8bf3bbeddb53fc9197151ddea044cbc5b09e1c6dfd3b71be0dd4a88d53b4d48d5e95aebb777f4774ee3e6fe6dfcd4f9a8fcf2657193ce9a9027dd690f50bda4ac11892eed8d1e9db646a47e74a7c13e41a7307b1b3f7d99dca2b3e6fa74699e9d52a7497b552ffd6d77467f6fbbf0a5eb347ddb6dc03fa74eb3ec0de22d7fdb75ab7f1eb7ebfa14c4931f9060e09f5fd7a76be49fdfb8c33fd77971736f6d505e993265c87cf4b7dd1c1e3ae5ec6dd0e3e6de693c582f3728d6477bf99f85c265ca9429f357f5cfe1e6fac416f6893de75d0d2ac97008ea6660507a0146dd488bea564208fd9def56426c79e759a778c7b9a9516e0544e69def78e7eec6e95087436577ee2e4a5da31c3fbb733a728d72b051edce295398b946b9bff31d93869c61459fdc3fe8cfdb9f26a4ad73a4216768d127f7ec6f73d0b22664f6e9b9d376709a90769de62250f7ddceddd6a9981f40d9791b6da92b20307fe1b74ff639dd4bb9c6b9f5d96f7dc8ecc3e3d7fc5ca7f9bdf71efb7bdcedf3dbda146987deec6d6ffb6be621ea6eae7f3b7506a8d4f9a8ce9f3b4d8873d67408020808488877d6f5e9ce6fa79db946b137caf5a95da7df06fbd45e800addd428f7b551ed348d7ade6e6a140b797003828697a29acf3e9fad4d1176e8ecd0faf03feb34216c2f3ffb4ddb1e7f5783ea311f81ba7737b77e3b9d8dba31e82853d96ff64d80cace037b8febd385cfdbfbcc0189c2601e884c4439e30d7e36e6a1580f4449cb44230804f4ec1c10f6fb7c36eaf2d3b74d1ef5b973a70d817d72feecec93739da69df1d69fad1e08a939205118e8a12ce0d9db59e3a1023d7bd628f7ed4e23c240cfaed36ebb69db53dfd5a06a6e33a86bcaee1a759db3757d72ee5cc55c43dd0a48cb3b4b87191cb4450a4c471074704147157480a1e3083a8ca003093a98d00103131a3a92304161fa420e31a628987460ca81a90d5317a62d4c3cc891851c5730f9c07486890939c098a4c881051317a6274c4c984e60ca92638c09c8348429099312262f98b8985c90438d1c6b98d830ad91838c494c0e2e98b0e4d8428e34729091c30939a290430b39dac8e1460e367298615a82090639d0303991a30a39a890e30c1d4b98ca301d614ac314035390494b8e3098ba6042c2d40213174c533099617a820905a62698be9864a0038b8e2c3a5c606ac2442607108e37705001c71938ac80a30a38c6c041060e28e0e002070c7064c1d1829a326acea8a9428d981a2ad47ca959420d1a3559a869420d19354fa881428d136a9250335483841a26d418a1668c1a356ad628a1512346cd156acca819535a8344859924665c308365a6053348cc1c311334c3829915cc403113c58c0a66aecca460c6ca8c11335566b498b9c18c0d66b298119ac162a6063357cc583153c50c15333498d93253c40c11332898a13233c4cc09669c9869622606334ccc4c31338319296670500243c90ba53094de289129a9514aa3748512154a6694c294a6503aa354841218a52da52b4a5494685052a294a554850406921ba42690be90c220f980d403120f485dd444819405090b520d4854907240e282b405e9095214a418909a2041419201294b6da3a251b950cfa86cd434ea98aa85ba856a468d422d83d4850aa664a532a102a17ea95dea1335a8fea04e516750aba852d42d3509b509d589ba443d4235423d6254859115465e4642187d310ac2a8cba88b110d46528cca184d3162c26806231d8c6c30121a61318a62248391969113a33346488c9a18c560c4c40806a325464a8c9218b9608465748449059717ae30949450b3006500a1c811c6940292136f09a630b109738c1c639888985098c0c027e815504b0e2bcc28e820c254055315dd048c024a319f30c9203191e38d8ec1fca243091314727ce17ae3022627b098eb0bd3096c851c5f74bcc084831e229892113422b4176830d096d098d0b2684d6831985e3c29b4222e2e9490601b8cda18b1310af33a960e24a0814846768412174a6c60403e91a900c70e70e800470cb226c030644b68321388c5682e605dae22a815b214e01043d242ba822444da41b442a442a741ad983f184d817ec1d6886a907420c1606cc433221a918d0cc434a01351cc43c233c23b82c5059c018d82b5a050684a68496030c05e80b5812181b5003b02c3820561593025b031d815485c2075b1cab40f2e2eae1d4c2bcc2a4424c4a1eb065307970d2697b91a5151eac2b5858e2de400c385c575459c42acc2b5859211b508389e30a18029e1624174422c238e3189d037c88c508a0295a2a68d9216a514583bd041657291a38c8983b6c11bd338c8a6c88ec894c892c86690c120ab4176456645b625a322ab22a341b64416830c4bf6043d23d3922191c92093227b4106459685aa41d1a0695031990b3226b216644d6451502d5406d48909842984ca82ba82aa827aa5a6a05aa946d42ab5884a444541a55287a827a84254a051995118466018bd31fac2c80ba32ed4b0510aa242748a3944e988984416d439b082404a43caa0060b39ca908050bac1d5851a2ed490916048a00886188658a68221a9c828bc21ba856641679069210343c6850c4c3685cc8d2c0cd916b23232333236b22c646df41bed056b8b55854583e78297e5257109411ac2758299c45c622ac14de02fef090c0b2c0798d065c4d5c4a5e58a01a58256416d60e1a04ec1dae2816171298d7191994d804d30165c83c6d22dc878c057d430318c6883782562118d8835882a8847ec5c6078597858802d786ebc2e442664499066d4a4419fa0341000cea88a3705090d1218a5301a08950af14b5ca375705531b77000dfc0d22286894be02c680d4c42e818c22485d945264492a22454dd2091e9d5f441673179d044bc351a05cdc5eaf2d4b8b2b884e217a616ad4546a5855e1bd91073073307f18de885de224a2142a18b6823a219f1092c4481fe417b51aa01c5628a317bc05f70170ee3b16129612171958951b07210c5288d99802442a98dd10e2250c2820722a51694b24840c906a52d2c6044851e37e40c6094456483f23ba6605cfd78a424e9c0912749787e387284f2f3a109cf9123d80c43f48a70017f44703ef848e1e9c091254f7e043e3a3851e244899309f81b8292234e3cd0840813264e18e0a384870913270c18c0f3e2a4305992c487c787233c3c1e08c0138273e281264b8e8c200a4f08a01c59f2e47d9af0806004498cf8fbc2f12869e2a4c9912447963c791e254d9c44c0a749089c2851f232bc20f8e044098f0f34bc2e4eca08787a8e489132029e224d9c2c9122858702db038213c192271f85a7278a930bf0b0bd1f381e9f243e2c61920488233c21e00981047af081e7031f48d2c3009e174e4a8f129e1f94ac9c149f263c51787e68b2c49f0f9a2cf180cdeb8153b2444a9325517c82e0393202264f9e24391285c707264b42b0d3a3c409939d1d8f070e044792f824f121890f0f0448af0b122c61926489cf92264b6a1e17ae478913261d38c2e31302253c41f094de0e5c14270cc89e0e9c149e253e4ba238f9c1270449661e17d7c4c9129f245142e0c3111f274ca41c59f2e4934409c1d738e661d281243e4d42e081233e4a78a22cf1a1c99223477498a2189610d2107aa7a26e0504f4ac7a3a646c23b6c14fd882fd80eeee2ef2802ed2458a38073cf8628c90488c2f5e177c44aeebbae2bbf8ba628c175fef45be2ebef8f18bf18a7cc1c7fca2bc5e64f8e083ce5deddc74ceb1bc607c13f275750dd735e77c31c2775df0718dfc1e33cf092184ef35bfc8f1bab81f062346dfbbb019dff562e42b0200c638291ccd292bc4e47b96b4a66318e38b1c9977780fce182fe678ddf07b93df53408c175fef001132bff7ae2bc6d8579c9323641823e478315f115e1cf9c5ed31e4eb62d8f0e218239c932f7eaea17bf0c10b5ed7f5dabd0b4e1a79609127e466eec8178c91e3638e70c22a27a4a38961d87b33c6eb41fade833087ab01734ee77a880e421ad00006740dd775f5752d60e75fd7007b5e577d2fc6f7ae4bebebe22b620f4248e183f0f105636ca8a3a3a3331ac537e79ccc373773c618218c317b19c705708c7c29c0547a2f26c026e3f7de7b345e91193ac733f29c1256d1837046f83ac6f9e0285ef1697c5d598ccc30d640b318e373d8151983cee510c48a310241c0cd8d0d31c686ddcccd1ce37bf1c118df631b62b4815f06bbce39e984901fc3031ce047ea00117580c7b061c337616417bb06f8e27c2fe27019a0e6c5181fd4205fda9cccac65314677c5f71e5f1733337c36f08bf14118e373570dbd62bc1ec308b58c7d3c2ec093cc1c737a3c86910004e0eb7afc2e1b183233bf78457e31be17af2bbed8313ed80f42082965e638e79cf34d0863adb0560823a531be37279c70cef7e09cf1ba9c73b4d2ca19648b99f98a6c03338691de63e6680347e6c8968b3630c7c7f4d5c073ce095f8c93461e3346e72e1a775d2e464c745dd7753d1be28b2fbe1b3872cda4939b420ae183d78bcc7c0de0451640c4e119b22cf28cf1bdf74e91497cc5c801883102200200002fc678c5eb8afc6cb8ae8bafeb8aef45e6ebbaa27331c699f81e8c30c6c8cc51cbe6e39b37e38cefc5c910c21923adc1728ceebd075f8c103e1863840f42f8b68b2f8e9099afc8fc2264aef1bd08f9ba38f28b30be1863c422ecebc58b9dd361868e30cf7de089c2e3431427475007f0c1b3248a8f000250d2a1c5d0a324490798307122001a629861861974c880f9284922a54992283e477c968000044e4e3a2cc9a6d484e7c9932433b8028060894f931044b1240ff88440069e1f8ef8345912e5c9112552968060070e2819827022029f195007f021832bc00f1f0538e283a72709930e1cf94093253f4849f2831b80c9929e9c1d01b0713e7c9c3839c5e00a9084278a0e9e9e233e41f034b941c7070260720590e2236549103c02c8e17c04b1c4033c519ee494783e90248a131f29477c60b2e40725477c9884208907787cc0c17ca224f19112001f35e0f0491285a709cf07a248b1a1c6f9f081478a149e284f40e044c991283c403851c213e54812293e28e13952801ed4017c3c51c2232565fda0c42749078efc20654993251e38d18cc0034ba2f0007104010538c0017cf8c073c489cf931144515283c58407081e2647824802021a663ee084c9910f3459f2810f3861a2c3816006e6e189e28123519cf82451e2840993243b4a3e3c521c88263c8fe40a70a489932552a2f00071244a121e264a8e04c1e3439325479a2c49f283120f78c0c9119f20782c104d785ec9f508a23801010f0824104d789e00b41f3454b18644c52ab654a31f88c4613faaf87e543f51a58a2af9e3543f3f2a5554a97e5492fea8e0cfcfcfcf0fffa8542a0c8953fd3412e790b89f9f9f9f87e4877f18898a7f1889fbf9f979aa9f9f1f88e447c53f3f8de4877f7e7e1e1215ffa81ac98f8a55aa87c4fdfc3412f7c3489cca42e2541389bb90fcb08a91fcf08f4a2271ae91385544a2628744c5ad82489c8a552ad543e2548c44c52a46020128869d730c0229495c42eae620b665fe523b1a87fd796dd49bf2d2e336adceb3e2c6650bee113dbad479f2aa5eba9c1f56d158ba8115cf47bc5b1d910552cbdd5feebc6a9aa65975ebb9d2a7ff484dabf5726b5ea6d92e19338de434b5d2d44ae33a8f4673d2d623a219d5ea3434241a2769a45a5da7472417899ca6926834796be04c69546355928b68485ee10c2c4152b52388b9e5222ddb32ad087dcc5ee997dbb44f2bb9878cec96109010f30278b742a28ba7a396a2ade75a0efd275a2492c7a845d748d6bcf02304584aee8139f6444ed23492a6915ce79130176d3d94a4694e2289482ec29c2644b350de6baed3d9767fbe47e4d449aed39afbf48b18bb3ed139662e899c92ace6ecaeabbdf37b646ffd1659cc33fb9c6e927b60d736ed7d125b433cfadeb322cbe21759ee5ea71f94f796759a0eaa9f4ea1b5ddfaaa29ec224bc42eb25ce759db6d377aa2bb674b64afea456e6d4c2da7ad86c84279cf1010b973276a3141abf7f16e1574e5882aa220324143221175aa39f52b72a80de9b1ece50cf36b12892e0cbbae4b24125d4edaea36da32ec325538e1e4823674db739f7e76ca1b75da1076dbad9dcdc78e5f6e3d50a6fc74f74e23f2f8a77477e1fb65ebec6d31ff9c0557de005f58f0060bc478fa34cb59d0e503f06ec502269e7626352196b396f5c9b9b5d1f4c959b7bec8afe92d7b33bfac3f9cb623eb1e0eb5eee158fe5222ef915b5e2d7b9d549d52eb5eaa44f2e9a5cd399c696ffde99a9336e770eae61cce689b2eda9cc3d1ec9d6fb93e5d9b8e3235b33ef1adcba136e4aa7a30ebfc6c0fbbe5b208756825f7f889aaac65104a5917d915a8e53250103426c6c2591a85f9b5e3af4d73cdad4d3e0fe279c9e923517a7577f74c47bbafabaf2cb3c2419a4691c74c945d792bc668af77fb8dd1c6d8b6e7f2e931c62c66318b5996f69f8f1d639605c5d8d995eccaaebcf469396c94c82db7fc5a96c51c94b9b482b02266c5f052413c9ce73183efbd2a342f468f31ab62756757b22b8fb9a6b58f4ad87b334ef3620d8da8e49af52ccbd6bc2c8fc65ef83cb352b299154c0896753167c74cd379a26de4d9569d6e986b9bb6b9871ec44bbd2a4ce64d8dea77ce4c342a0ee91f417f555eea657938b0ca4367276d3cdb6f57f9b69a3635bfd9d3ceb268fd159db14b8bae396b44d8a36bdb856f4bd0437f591ebc2f8b65bde622bf5ce531cdb3a959ea9623f377bee6f76d79e875737dd27cb4c13e69f6fa586fb9c83a2dcb2ca736b38209c1b22b7f5996656556b887845a720c31e22eaed01dd772d7524a19dda77b3a9332caacad0dfe45a3dfc957a46bd997e5666da6a799526e961ba9a6743b5769ce3221ed86704e0efa2b7b7a569bc209490e2bc9de1ed93b67f7f4cc6166d9ca8439d531081d4297ce5564375f89975bd6a83d1b5d985728bdca9163d29fac98652bd77571174f6736e7ccdab21569c92d3add2ed7792c9dc28cadec60925dd012f9f3971259d7dc350ada76087ba04fb7a0335b8197c7975967ccadcc1f93ec395bc12c5bb9ae8bafbc642b6ef5aa16661a0b87793a72377487370a73a9b5b58d9cb791bb97aa24cfde755dd7db7a26bdaef72e6a85835c6e959e4f6a6ffff4e761de72d7a99c9eed9789ede7d3fbba69547587a9a658d2e2317fa5635bcff52cab31768cc5b4d5d35cc22c7b39cc4b7f6ed9cb24bb96f52cbfafe15b59fb856dbc592fe7a55ce6d49d4b4dcbd97bbb3ecfaa2fe5222711616f76cc2de72a1ca4ddeaeeebd397c33ce6a34da40d99f1b63ded976b33dbe567cb56b807465dd496d7584e9359334e2d87568dbd90a67d06d38460589ebecd7df77b5bc37f5bf7ec2a6f8d5c6e5dda308f9be67eb459a38e0e335b5f2a5a672b224b8295a75ff897dffa97bda6ed691ff965e5a651edae719e3fcb24fbab1d832e15df5fb6a7dd72cceacb5ebee252d12fb6e2d7767ddeb75fbe321dcb612b4cb267bf4c32f898c7f01e5f79cce5c85e8e392387237b99647fb9c8e15c9fc7575e3a5be1204c2d5bc1308cc3bcd5ddddcd567a8b9bbb81f38755d60fab38ce44e1bd2e0fcba5b93beecd0d3959fbdbdae9755d17dd7a46f3ba28b5b9e6f4c1412e1dd44773441f162439b82e33daebd78decb687fac8a35f3834aa26272727e769b968cecb42af4b87e5d2e91d3d2c2fbd218490edeba7ec77647dfadf83fc600ecf1787e7cbbada2279299761d35da36218591f71bb36f6b2173e5b9b918b6672aeebda3c2c6f79f7e5837b5834a51a9f0cd7c6eef00b9ff7b08dd30733884119ecc561c7b3b13e5e97a77434dadc534a4797f591d3316c9aebe076530e1cd67d7090abc6ce589215c16b527b71f8cb6d4ff491533b7de080430eae8b03839d2dbda8bdf0251d591f74bb3e0d21843ee8c8457e7178cb63a02338a22387233882237b45f6e2005ddb5c9f321f3e1e161f0c1403e7b80193a1d14b6b79e8d4b5c94f3932f8fdb1c3af5bd9b874e7336b94e59037ea6fa3ee9c86195ce432f80eb7b9ba9bfdda7a34acfbbab875fad9ed46cca3bdfd9a63f6f65faed371bbd14b8fc9ec76f935a77edba38df672cda3df1aac53981969540ceea2adaf9a125f8c62fc956e6d3dd67f3ebe6666f8d12d871ce4b97523187f655b7fa84db6cbeb452f2fdda73fb2c8899cc8d95eebc1071f9ccf2fa79c991e5025d363ecb55c3a841046b7d6dfc6dbb57885653e72e7dcd1e066700e02da8bdb6d6baf469aa9c171ca9163faa183c8f3e8f179b462c7cd7d47bfd18bf41bbdbce574136943d8a3ed896e9ff326b987e5aea9346bf051666fbd712cdac0f675d698539d5a721b68bc811e74f76eec9d23fbeced5883e58891797a45f9dae6fe8a51b3cfa96fd2b7cd724f37f6b9d1a8e3f6ce8734d85b1fce60afc965b0f7f47087bd3936f6fe78e83a2fc540d4af1874d8fb43b3f787c9de9c9c1cf69efef20987bddef67a8dbd269a195b82b6c20bf33bbdfd6ade9748241261d445f64e17f9d5ecf59f96762639887423cfa3bd2d668ef9d8643e3a7c401fed954efd8a9c2feb13bf27bae5ecf7b29cc6ce44229128bac8592342ade41e9747176d77fe656da65b2e9d815e8a570f07fac8ef8f9b73d2fcfa3589fc56a087ceab976aa087037d64ef8fccde9c1374cd5e17f1eab2abcba55f918f2c6a7de253bf22eb135fe499dff9964b6d08bd1cf3a8f1903d75e723db4050b33ef12fcf64033da4976356720fa809b1a2976b8b3faf4dfedcdcc718af2dc29f5684b6a7b558f629d8f965c1e0b798e7f4669abbc9cc718431479f3362b6c78dbc3ddbe0cbcdbd6b94487210e971b6988feed30f1f3f7e3ca753cee6ac95a18ffcf28f7ce29dd996fd34f9e91dd77ebc61de1be68e5447d066aa29d0e774087db61b9f6d3d96ff3c74a239e176e1c3cc210769cfacce83db6d3532e729b23d995b33deaed392834c51431ab79cc6de6e31cf247be7acf6d63ab2d7f4ef39b456e616745a11cb4aeec1de56a7212379299dea2592d30bda4b04ba831c04ea3c9d863acab498e9d16f8b79e998a60d6187b607fac83396dc43ced86b225548a2d5b192bd93da5b2dc95e96a1d74b13225f96a7308370839f6d105a1b7f6b44e88f207cf6a90d69c73c6ed0798b2e6ac94122cc61e9ec937686f9bcf5df730d42ea96bda6cb317b2be6f341482d3bdd693c6456720fcda1cfd7ac8de5d2e37313b5d774d95b1fb3d3239639f53b5f62de935d58e62f7bd9cba8bdf3e373c941b0cba53b295bcc33b4accd3d8410424b5ac93d227c25a6c9c10a803efb92b7971578f903bc5bad20e8a91bdda0ba950ac0fce9dd4a055c9ebad118d5ad5430e6e933ccf20cf4d1a97bcefda5aa8fdc794ea330778ec38f4689dc790e8da2ee9e734ab14de4da36bd3791f366d5d0949e1023d5296d7be176eb63ed184bc7da31769dc73e0db79ecc7fbed9dd85d9ab7acc79bbcc42ec506297d569a7f17039bb4ef3161c0473d933deee341e2e7bfb339fb1b71fba4ef776619697de6db1edf26757caee6eccf640cf1c734a5fe6b421efafb417f676ebb7a59af2f3d2d9365f13fcb7ebbcde2e8ce2afe53edd83f9cfcbedbe6f6f8dc87bc8f6aa9ebd5d6e17bee5b37d5ad727e93a4f6e1732f1d782f2feb24eb7c6c3edc75cb3faa15f9ffe76667b6196b7dca1b3b55ee417f94586ec979d8b48c72e3fe6bdc1396bed871321904987de1b744c5a9bcca75b335e4353b2d7048176692f9176e9d259e3a15d3a0b356a3a3b6bc141a433e63aefb17e797dfad92dbf30cb4f176d551b5272697ba4633e2a6d97bf67c969ec35d1788dadf1192bddf2d97edbefcc9c226d487b0f6b1a0fd9b72361afb13496ed4cc9b6f49979790aa3fc6c730fa553cee4061f6ed29dc643cc30c73d2e6bf1ec521bd22e726b93aef3b42df3dedcf366b968c3fcda46feb6ea943391b42ce40f27baf43bdb6f9d7e4d6ec7b94ea34813703bd5fda57444cea669afa9edad7f7db2c697f39c736ab647bae8b2976967bc0507b9dc28e824772ff59c64297c2addc2dc8a210621aa4f7c6a7ba44fc79c3a656666771a04a663f6326f94763667add7f4b37d4a9fce1a1111bbf4b95de6f26c6d2ecfdc729d9742f270a29b4cb53e9c3baf5b4db73cdb207c915b4e7de2b35f9ff822d7fc3297a75e37f770321f6db34f99bd3ef0a75b3eb521ec3d227bf9a7676e693c64cf8e44e473d617f9adee995e64af4ffce99abdcce52dbfa6bf0ecc47e72d3808db2b45f5d333a7ce42d4a52684c22c8f6dae4feee716296773937f6deee57647f0d2a9e36ed12dc1cbb9118973837f6dfc4e23427f5ade827b5876de44dbc340f18755f4e9c8628b0863b48e1d1a91823bd1dd01e1e82e6e333ab37ba93b3fc628f431460bc0d2bb959526de63821516f0ad5f4d5feca0cb173af852c20184377fa16572733d7b687ab8b262c443874dc40e749a46c187f632b9b100c4ca3c8e33de122a7003fb89ece072853037aa1b10714613426cb9c1099ca8495c113dc10722161431c3a8f24695202351c62863fc68c4958f313e60c7bb95113978f96e654416af7b60c4131050e9c0064f088d41260b59b06224050a2a40a10321b8c0024804a5489121d48bcd1486105599c1156e4c81822e37d08aa042c465be5b55a97206116150d085216e1534c6102d78c21a3b784256c41257114f5469c04f186c78a10432c23481099408376a8a3022a801dabb55115ff8fa6e55441a71e46eae5bbde5d46942a0eb943f1ccb9d3be837c6f6e8d7df8a7ed3272be644772e95e3702c8ff64af73e59d2af17197d8f3c354ada5b5fbae5cea59cc3b9f3ef4c55876339db2bfdd427cb79f33e591eb79b3e592ea4d6777ffd2da7ac15197df49bd8766db38aeadec608957676d2f3db449f9239e6d3a79d42fd72d5ced38418a14205f3ccae9ecae5d4ea3c1ccbaf0ddb726ca25357edfcb089d64602985fde03e69747bfec942ba2ede1726aa7b88773d9fa702c9f623966ddc351eda8762867d8e676aecded384d8891f9702cb7f39aa8d3f1887e391ed11f4e1ce27e66988eb001145b38c28b326290a023084b6071653a530cef565038e14d4474992e40598050a68a9ca7a3fbe351d0e315a425650e2e4d5041852b4284a1821d697ad905480c22221bd39350c2dfd3c306507105115e90420b84286327471249789863c3bb55125624d1c4ab82d2e4000710413002195b44f1052fd4a8d922d3421857f82214060aa2586387e6db2b176d8b2d9a17665c19217c41074f7cc1811156250a0c418526a0f0051486cc303d1df1cb1d20613344ad54e8a0c277ab2d33f8620b136e0b125672b81945476c29620a32f3a74843053456a89914445f6a1686985a81a0415173bc5b4d6185363485135f98e28a288a9abd5b4d0144a5522d9ca06a6266008514601fa3356953a8a5226854d4192e5a97aac3099116ea7cb79a81125ea834ef563318620755db82288baa9de1838a230c0d8c5af36e25450f9e5d0b2345160fffd27c0cc478084dafd33a6dbd5b49e1c4d3772b2966c0ef02833abfed93db32003ebfd0297410429215aaf30c3274370d043a6adf84d030830c3b6c62d061ca81a38666a644aa23919651ec9a968cf0b5e3e1dc69ec784cf15ca79f750ee78755dd0d05db0577dece73d6ead83d15b309de7bce9db3bdcea343bfcf79f4e7bc27bef7de7b8fdd60081d240d4183893eb9870e420821842e1fa0aa62628c3146813a7b2bcaf965777d82f0b9c8fb1e9ed1af742a37d21a955d7a7b8fb4b0af356afbcdfe79d628b6cfe3f5e130628c1c638c9165943c885018a28a1899983eca18e3c90d687a5d9031b2e4286315b20a7690baec6942e6b4dc8c72ab945a597bdc3a46ff087da08ccdd64968696050d9db69840064ce818c31c6d8128d880de179818e4838b21a9a9912c5449a55d1c8a14e63ef297b92390ed248a459cbfebcf11604687892b3d08f1f7f598886856684dcb930247b554cd4983907bd140b056936b377036f0909090949cba2e10b0a90462c76216ee08a579c0223bc8147c418a5452dcbb2229417658cb1e59718e10fa8344566961ca5acd6754d97d1a55b9b8e32d5ca78801eb767c9689d8c02d484b4c74a63e2e2bd1a7dbe146592c3464637ec9a394f6a4e0d154ba022a755b31f1a51c32e2b9352ca2cc78218986f794e8c393127e6408748a28c70e86a18063fe7676196216a60597e39205726860a11393bd319203a95e944f80e86619b740688f3a7ffb2d1f188f223e4180f10a740192d695931ca18e1eb0295103ecbb24e6e534a8a5d11c94b8f598c6e410b2d4b7abbb44044cb7ff1cbc2150e838cf249eba03f971b6b3c3c8f1bfca8ede01edaa815e167a1bc76c84c067d51c6274f10e573e20443f018b94128b9bb9bdbd91d3b37c70dcaeb7663ac29ba315ac8aa1806ead3632a3da8ec9781fecdacb61bf31089288600dd98313289d84988cf0bd2c5b893cc5e4ec1913388c924cb6a8fb19c34598e8cc92491b84fa4a14c99320c44b402b61aefdc706f7c741fc8108394030ef7861d1f69b2d413a682464e4d0e4d4ec95e3fbd7492bda79c1f3f9ed4919d19c598ca4717e6a3ce4f77a7e8ad1171ab9f7e33a5642a169837580099ca0c5b4f3808ed0e9363e64bf352d2c7ce39e8cd346ef465f14191733cda755a3ee950b75ece2e55cf987334fa5be3477b723c9e5f639c1c9cdc4d13bf20a57c334a29e5137266d5a5c14fc627af886058b18ae2e963ce47bf3f7ce45b2ec3e0864e257b73fed61f3f30eaa2cb5eff5b359b994cd45ef39a975fd35f3eadfb482983a0ca7ab10ce8c06000c2fa736372fa5abdc213177ef4e9b9297a5f3dad1ca0a8ecf7c73f787a07c606b1d278a4eccfb04617239c9dba3cc678851ae315a3cfe8d1278cee1ec218618c568c31c2087de0476fadc802ca9429f3113e7cd931896c8cbc529f4ba12f482c767cb4724545aee44a0a6919da2285b2316d46850e88f468afcf73a348bfb4d0f190120a913eef65993265ca00f11edada57a0816d3a3d3343e9dd34df6deb06e5c18f424cd701896ed19c214dace5f4e9b9096ae63767f6e97db4d0f18025b02c861f9fc6004b80510548f33072b4a22628042b6cf5d0a5950a1d5b5d129088adc080f980001f2d36e4783c6f2c2afbc586f82f3684ad5c194020025bd504b580ba9b8b8df9e73e3b831b7cd1f21bb73c8c3270718978c4438f46b8548d557e08b531e7bc34bc74abc62a7fe30f1dac8f16f3e2783c7f67c41536040767e41e78d18338067231e598216def13faf6f8aefc0b7a8b887c28d6b3758ec77d55a00e353ff4d0de66f2841ebabc34623cf41eb26ecc135a838c8f7e697ae8ef5be980e3a595417d7a3eada8ecf7adfeb5187fdf6a66f50935165a20319f901c03c3fcf0d22113d34b7fd3cd7969b287d56409514f58a8ecf71405fe3d39384d025802154b80261b58f5e3a90cb6b9c3895eaf294f1f83980fddc5930e30aa7083f94e8ba71cabdf9709817eaa3cbb4441bd9c85cc43089de301dd6912e81df770ae13f3d036132633ed935badd704fd8797b21c5ad6c264b69055ae22e66f57d1f2cf9f2681b763d91e1346577163fe76957f13321918f4832a4ce6619f1c046a7803899160cca5b2883ac33bc7e342a1de98c4a0bea47f7e61160f1d0a3920d1a1bd4c62d04377d30b153a9c5278e99724f4376a1943c3477b0d7d51d96fd4f22f8bbf518b833306b110ac30c48c419810e4f785143267ad7dd2fcfe78588087380fb7bf340f75b894c9e480583eeb1bf5399d97c2c0c3413977ba347c74cd5e1afe9a328a5d5f5ad3a1793cc7862abbdc9c3b41876e8d7f3eb3da9cc34367ad08a94a97277278d827a60cd9883971078d3bd8a91abb5063c52c0bc94377b2bc3522fed0e15bf02d19ad182d939b973bb85c610a2f2d79f3d0c29cf7058e6a9f68faf4e0c6d0f430de2cdadea0bc5a4da6e9b04f2631153e6bead383d02d67e8d2aa52a1bb8f6e6942a24b18a16b44a0cd2cd42e2854ae34b1b51ddc4bdb5a11b7042a9640c5c308ad0d9186b649f62f36a482c542ffdc817cf4f151009a5fcf62b5d4ef8692d3ef0372797476411ec03d98b82e6fb9b3a41ad5f26724e7a59e3c8083f015eef1bc563724b2a63aa6ff3a3036cce506dd29fa93979a0e27fa73bec2419ceb31dd4d63c600718ec7edbf7ce59f1b79290e7a38cfe7ac752848bac914f4ee07aa1ef8634ea3e31651bb391ed12d662f071e3a6f1f8f3c8d88bf64778af1a3db5b82d78844762dbaac733c62a3a010f81c4eda9a10b9837bcb3261adc8a8082094f1d186487bf4f6689bb82e0f2d64a17735a8443c9500533f15080848889d2bbaa79dfd39dca2851e430c556abb7b08bddb89b4b375aac7bc0326512848962059826409822548509024728065f50378b76a41144f47eefa3464923976d828e8ec3388970eb7c9a54fd0faf98efdace31ed2e116b57828246ec13da07d5665717b21a5dcc1fdf421eea78dc14af57fae7a6bbbfe96e50d3daa3e6e5215999740a9cb7a737d6ae78dbbcc4fa74ed3817f5a9b22d0a70f81aa9fdb7dde439e4b9db636d53f9ffe687d708f5645e62f43ff9cce591db707d162fa64b5983e5957e6a34db4b9e790bab32e77f6fa437b7d220fd23aeed10e3766c93d60977fdece8b4e9f9eb54cedcfafbfeaef9487ce9b8d927b40f8f3f2aa6e6df79b53a3a0dba17735fc3c15d63209d4dd5c0efa37efcd3b772e657a38cf0161e99476c6dab434b57620730cdb32e8f465ad4dde7ca8f7c7bf1f2ff5c3c3793e6715a33925b5477ac47e68d4e5cf993cf109620a03350ae32a5c85af40cc3aee318bc097563e9e76031b06362418d800924659dca8cc9f7760ba8cf6aa329efbe38b92ca3f1a05fd790e3a0d6814f5e8cf1fd028eacf8d346a12c9fef25fc93d66abf1ec57dcc040a3ee8f465df7e5871aff3c871d1af57c038d22fdf31f1ae5565786fe39934ef1cea3a1f9ebc448ee212d3f85f2ca9431bdd48f91e935d347be3f1cfb61d969e18f7f1ca431a50284500b844c409805422c10064168f94a4b871e430c42c09e7e5d8e790ef427724e71a8bdf0a96b9b730d664fed6533d72c6639fccbf664ce2f27bdb52297652b6d7b2ccb5e90e97bef59308ba68fd0da2084d9430b5a4db2ef8136daac4f8f5551ba057d6e390fe749cb43ce4b595bb4ac2b8db23cc62e2e97dbc5562ceb8ab322740f9daff0664108535996f352134218ad5b44a8cfdfbba6291f1d6e56e7c1ed0a74e851e74189f28755f1899a10c6ca4c182ba5e825cb43a954c54a98192f6d3356c254b112c60a7c69c52926b718c94037de9856438af958455a0ed3a758f2abe432a6342761eea8cf68526b120ec95ef8247bb327b994e6241f6dcee19024c949d9a6a34cc56ce67cde639743eb3398edb9307725773e53f2197bf90ac9f6941cc3485e2249cedc6942e874e971933b4e1f2f139a9f2e679f6476695ec64dbecbe7d684e67ba45f4e5f866d975b9b7bd2668dfc39a556db464eda2c92e63a0db5095417cdd196358e749d47aadb68ab0f67c8e5d3350b736b9b7dc2681ecb1ebb1cc3aa3b38b31d7de2c6e13056c258812aecd51f50eab2991996616666cecc903c8783d438c96b3cba0f0e526379a8b157f5354eda48a439493efd6942a60c5eb3c940721f1cc43dc95913429a21e54c1f0f4ba32669ceccf8a8b33473b838448884a654ddc79c999e038729c62d308aa7a599e08f2f27da8705cbe1ec118747bf98477b1f961d6e13830e7b49357eab6baef3523bac4d0c76a5c39adce5cea51c497493bd0e0c93b334878f764eaf99351edd5fc966f39a24eb137fa6642f0e5acd31ad364976e672bfecc50187d5fc62568b99ed21f98c33d7644eb23d735a1f59f4c13d62b446aed3ecac0db1f8dd85b93ec94df36c88e63285646666ce9b45efcd4b47f252b071a46793e6e58e97dc9a0eda90fe6cb48936b8cdac9a1a873de7f9f4fcdcfae01fa8c2f1665c40a9cbfa0a96f7772b2b5e9ed2cea6482649c92c5913c273c667f45269c6f22086cb8b794a5d06352133f6aa7ec6e726a794d3a53f4d88d4794f1342f2998d349db7985c9eba4631741f9db9344a0a89627e6e323e6a6517de9c9f1707de4248a4d47d6d94b4b66bfa7b4e47ee8652956c0196d7f16e850499a72f8b60402faf4baff8a7ede2a5161cc4fdb4cca54ff26a6ef9cc7cfa6d9f1e351d58e47ca9cf38c7bcfa74679ff6fef8e9ae53d5d9de9c9f249fa5cdf5e9b217fe2df96c9ccb47d6f509fa9d331b93ec67a0bd396ff9c8317b35c69cb5213decf0676c0fb533d447f6e6bcccac4fdfc6ec85f1e58c5bde5a9119ab647dfa499753dbc31eff693cc4e76bdab945cd9d2624c36674f6b9f1e43967dca21567e46821674ed3413ae6f465a2adbadc464ea9cb8dba68ab6e6dd71422d769ead3e53669a4bf948fb73e4161286e8a2abe23729d7eda90e9237f9a90cbc23e499f5bdd66e348a7dbad2fafadf6e979dd5c1b82b9747d9a2eda5ce3b0eb349d1bf56b837da22eb75b9f3a75d674c09c3a75dee6762b147ef9e4163ffbc46e5d51b1772b2c437fdd4f299d26b63c1cf7d16f5cb082877f3d6708ea6eee5b7d63ef72d0e953fb7342bd3fc8340ada1fed3a2d64ba4e77d6d716dde9a94f4d390adec2434c065bf183bbe0157b6131be757620f32307d7c58141865550fdf9b53da709998fd2ab6e97891be3341d2eb79ab831df73b9456b755a4775544775347a23ea5c6ac61daa9170a919eb1c4ee6a8679d3bddfa44c9f60bbefd0763c8781fef7ec0841b3ced0c0b42859f05a1c20c0b42e5cf9cc60c0b42b53e734ab3ecda32f2f7de7bef3daf244ada2867a4edc21f39d5322c08f5bd1b3db7198da037997ec3cbb73fa0b7458b4e6d40ed8f4aa39c174810ea97e55b08097f9f96ef6ce5c5101a7f9f15dfafcab78fe06b2e43ab46dd0e6aa146392f8656dfde5afced2efc6f77f9f60e6a945b2de1c6b7379646b10ab8bb47feb64bf323879a10283cb21c45b4a24f7dafd7f1cfb77fce2ed55f1e4e3fec9fcdf98205a1cacf9cb2f6dc6923672d74b4b97fdba54f133212e21c8f3772a7091955c7cea8b787bebd871a7555ad15a9d6a6ba632d5dd4cb5abe9db465759bfd65528749a823cd590b47815df32fe86f837dba5ce749176dd7a46d77664de6db697ff9766c13f2fcdaa4cf0dbe9c1ce780b8b7f7f4b61c356af47bfabea14ffdb6e870b3795e04fb6873fad4a71d6f330955ff3070e99b3eb5bff624d4eb6f555b861c50b783a59e5e6a3e1cf639eb893beba14f2cad84524a282574d041079d73fee6334969ad94485e8af9b5e521c77950ef098aa9d162a04fd6c80fea75432f85e370d80bf0ecb722809d177f723ca01ad5ff3a30ab32513c237140c2e0e59708f36eb584180e08ef564b34a1bad51240ff1c3aed2c43d227e61ffac48ee4efe959d5c57382845b8f847e1d986737d228e9bcc3fe837ce7c509befcfdf18cbdf302cb187f4f3c64af8b8f0e5d178d07e7cef1905b3b745dfe3d5b6d50efd4d8a06ec7c1f618f45297b7c72cb63a87f3a04c990f258887af2d304022b4181de69d3f302dc617313a73304a6bc2395ff374d29a97ccdcbc309ac12c7bcdd9cc9cc368a675114191e8358b5658e65ce634378a399948a4c53b1f0945f1ce6bcddc767bcb3b27751425582abde6526be99373326e34950672838c1b63bc741163a88b972e2b8e420b63e1a076e39dbf312f8c98771ec320c1a1afeb82c30f1c3e87bbdf83304a28e56b96dcef4118a3b4a065bd660b46292d6bce0b8318f69a316b5e1786519a6950d35eb386d12cd33491685461adafb9b228e3d128e35a332691322ecdc09999d73c53ca9cdbee74353599c3812373394cd0647acd26122dcdcc64bcd1549a8c6bb421fcedb426e30d8736841fba1c30478ed79c638a6c885ccfcedfd659b2668a6c885c29df437d724e803522986cfe35995eaa9350afe91ff4e278c42e518c3aebcc6a9db336ca452d514b9d599fdecc669d598d5fa297b88a43d734f471e89fc7a146ddf9a55137822146a36e8d6434ea463022188d72fea296a82582895f7881e4821a2f84e189366a94c0324208257cd07330b001666666f6550eccc959b143e182bff56bde79e10225808961609f3ebddacef6c916841bc3eed3a7c7ce39e7d78d714134ca55575d75d505f1c4e74910ef9a4f1ac58e2dc9ecd85522b0c532053437397d7a39edd3887a737270e814cd8ed3e9defce4c54b313398c6dc5189a42167883105b5f3e154306a037a78165b06dab88ae56d794f3b53b14f729546b17decec70a65f5536a6d2a777bd6b7e3fcb6260071d237d6a1f86a8f40f48c6ec780662a03155984aa3ae23f30f0c1824bec36a59842ffaaffb42070e2204981b1c30ccfd38794e9f8c74f1d2a58b97f621e7a587465df69977ab27c8b8de79e10222fe79c441a3ba37e74e708b1b5ad4c86275a18108377794847ae79019be697857e5a5a64fc8c501698f1eab382096478f48401dc4328cf8e84d46a7aca057a5bf344a5a5c1e15a137b6b8712d2d6a0475188784bf222f90f888c55fed883234b97179634b9619bead6865054bfa0c6f05bd5416a671a2ab91c50d2dd91b5b3e6a54843e6a57b87cc4ac950e352f6d0606a846bf5810c9f1e0d2b3c726af09912daa8e07fb75a2438d4e61bb0d84cc331ffd47033a55c7d068f9d8425bbe3862cbc77ba25f1cc1c5df1c3e9a3e7e1144e6efcd4787dc4e1332248d51d06b6b083b1c107e3783777714f8066666e6666e679e73f2646666ce396143a8edd7ad74e6bff71e143bf837b446c4a101edd6b539e9edce7b7ab65fd635ec93eb76ec6158c5acf51ca3175684866fc71abb90a5dda14fcfd9fee8d3cbe9e10998666f7f62de608b016ba44fa4a1aa440513c4904fa3ae13f3ef3e23af02453143ed9746e7e1b4cbf0ce774ce88f99fd861d2c6fcb62b7ac1d1a65d99eb34e87d36ed91bb71dfad42e379d06f4a9dd32a2fa3b2428e98de287eecf36e7a444bd27d4fdb143a32e0e0d68d475ab0734eaf65f1cbefdfef8761c1ac54ef36e05c50ce83b2f5cc082bff387a440dd0dad09a35e0672ce347028afc19da2e770067aa9d3c3e12d24916c1f8143cd3e45a73718f1e107268dc23cfa9346518fee13840362553fd590c3de1bfe3290e3b0d76bec358d9cc6de5ab2364a7b4ddff6fa0f09a18e6118f6309d76de9c53a690ce39a7bb0dca833addcd134cbd4e8ccb402707a4834077deefdc080781968126e080b0e513007190164287f9cef1905140bab1968d99e853f4ab3ab6e2a3330d80788be3117d4cb5596077fe709885fa8755fcd32acbba5840e988b521fc53486b423a1be204596566ef7669a5b6632bd82728afd48670c49ac4a2b2bdd0564e426d372d99c12863942693d5433b5c420821bb9e3fac1c2093ccc1ecc75bdd9063694378e49e4eb76deee6e66ec80ea105e1a591cc2fb6bb795d1896f5766f18d27f723cb24cd3b2deee1cd2d445d1553d7b868d2e31cee90e7d7260d0987ac2599d1b3a41bd39bc73eaa6d39a1f743c68d6ddddf035ec0d6e50de489359d4b637138d2aa996b8344557f5b34fa32cd38674661a75957a6f78e75cbbd7fc4457f536f096e0d94ec7c345694dedd2300a9fd62c1241e1326f4364d4febeed1dc13b57594c802ad4dddc19f4d029b34fa7852d65a35eb7126a302f8539f416f35219d551807b385a66d57140b087ded0e7acd564726378e8a578cbc3815ee7d004a024a3de161a9245a8d7addee93820eca6316a1286b0f15b635e6aeb07f33cf4b0090e07ba14a6152ae4320fa14fcbde79f5a0520ed2ee2ef6ce9f7090b62d14c501b96cdb80721020e65f7eaf77cea70372b9f3a7e930a550af13e3d99f3664be11c7831ddb7ae7b21408f5b9733c28ccb8092ea5c35278c85678c86d3c642e3ce432d69887031d665b8bd95aa864efc9351f896ad54ca6cc7d8c047a07b30de6e140a76bd4e7cd7ead315a841ece59abc974f9f5cbde13746b0c5bd618698df968fde1b0cfa09f184744507773e3eae1740ea119f5c74b5929e936bc736b93db0caa740b0140929043049c1f683f1c0f48f3e3071728a6fef89183ce0f3e9d2a0d35208b498c2f609c1a5e4c8dbad51b756976dc549ab87a5743c31fb8d9a8d9d97c292ad70e11be73055fc17c5ddd578c31bae6cb61a21e1c0fe815703ce024e3a1d3a7a38d1ac7c4311c4aa74f5f1c9076e811cc8d64500c7027e83792f150fa8d621e0e1979383a8ddaa119e05045dc093a8c961ad9a18d28c20e195aec92dbda2ad49bf3d0735e0a033f34238d5551f98724e07840874e72a909300e74e8ce3899f1f03a273cf4abc388e301ddfaa2667d826eb14b66f638043d0241a61c19ba4d8cfdd07b808e8146d53c4cd140df800c560fab1a541ec6e66d88982e5030a3813b1eceac51edd065abfa5d47a0eee6cad5e5ae729687139d52985db962595452d90366448d4e831ad5df4e732d97a3e02d1fdda5184be344bfd9ac34ef9c36c48667db458dfeb4a0ac4f7de2a2c68fce75b391eebe18a30aefbe1843e87bb391160a9779cb7b3ceaeb13dec871e5e963f72c3741f1b4b3a6019997cedb8a8a2c2fad4f3f14eba5c0b7b612174f61d65b6684a731ebad34c65399f166f3dc7d41461a36cf697e3ab5b2de3032ca3c9d196fcf7b9b4eb54c471b350afce852e8a59e3c9c251c8a81fa94c501618f2eb5482e3c843b45bf92898f72cb478fe1c94b711057e9d3100ec529e8e89483b45469235210afbcf418b7bcf4cb59b0a0671bab381ed2de502d8b4210a38cd7f1b762d7b464848f362682327a1b63e953f4b971963e45ef4d7bdedc175030e259d3017b69312a6ace1b91801b724337e438207c84e3113d7a015c8a91609ce8d15e1d7f39c8c4f6f2131ffd92fee67c641c692ff6f7fa9bc341d617f5d4a7e8d81683ae9825891a81821c10fee871e580e0f828a3d37498e1a190e321e576d3a7e853dacc693907350a4ba3dcca892c8daa59350a8ce8564f005db642e8e373420b7f99cb474d8b1624fe72978f3b97b57cf46949238dba3727248d8ade43a376a06cfeba3010803a2d37f6e8a746b547bfa151cfa3cb55abe0ab408c050a66d0769a8a249a3ed95c796823ad0e5f38f1c6476fdfc184f5ed3bf4d0291c426ea5c4151cbe77ee0f2a7028092ba888c000010101ed5ce1564a40f1d781d13bf734248d2ddb10315cb62162bae85367e1f21dc3f70f49c38a3e6d3f4515df797647e3345b539fda9fd48e427560ca90e24a14644431e6bf784114efbec822c68379f7c5123ff85e39205cc5f1902e5df6db18c9543bce77f4cdc90161930d74a73e45f3e913703ba687237dda29f5e148d7ac7b38aedaf19c87e32f7d7af509b89df970a45779ebc86f4edc713b2297bcd94037f270649409b89d1dec149dccaa769c0ebdf41880aadb999e5920cd339ff68a263b366d6d34309d7a0f57b553ae68dbc3746aa7f0cebc6ef5d2a126245a1b22305a9b22fcd1f9a33fcd88666d32a7d6c602bc6339ef584ee146d9397e22120e6aaf8080bea9308405d0671b073d1ce9ad49a07758cbc3914eb2a29279a90643174140d2dfd04b2179635ede27e6e503f3f2be2f2fbd572f551a52228c971eea2f2da6c7bc14e60618b8004693017a295725e8a59c962d2f25fa42094408127a29de91feb8bcd44b82184ae802b423fdad5e2a88116cb0da91febcbc94db29d9eb247b4d2d77a0740aab50af49835ce5e14817719697c25c3a6b79a9cca5b315070488fa6da7a82e9d53e052548838ed685ec5b4535d56514015530b72a8f26347f3cb4abc748a6dbc936dede5f5aa4ff2f2a979f5f6db2b2354a8642e722ad4475e371be8d5553bdaa6690e5dab76cac8369987239dfa14d1037ae999ab7628ddb24d8c0d74cd553b636ca0b59140e6d47bc89c3a84b607ea9afdd25e1e8ef429605e7ad6432fb35e3d1c895d8e07f4cb5ad63d1cb87ae9ee46da1e08f483a9f851336814285ce69dfbcc3b1adada7903623e14ab4c1880be75508dbcd43c39203a3e7ace4b61a9e9f19a60dc97d347bfcecb47285ce6dbf61a3573cd25b536cfdabc77d9ebc43c17d929481e4efbd64fe966e35c3464e35ce4aa9d2f1f1df51e30a7ee9cda295738db0375919d92f370a24ff112e9e9e144f621eea354c7a3fd723c18f3a7651bc5369b1ea8634e2d66e7c36977da0478e7fe78e75775382e3a37ddc3716e6daedf6913e81d69731e8e1bfd3c15c378c572dabde3dc719006aa7d39bbf31f2f85b9733a37de09a437a8940a17aa73410d42a6d0ccc84800042443150030401c120a06244251d5c40f1400128cb0525c4c17e85192e32885103186104244040000040066363000843ea9b3b4c0f9742ec3511fdb715dcc66180c446a5daf8e8ed2d831a959a78e7623109b1c3496acb55a95d6dffe3422db64c335ca28445414a3bc66fc9c9271bde153f8f589d9eeaac46cde79503efcef2146fddfd306b88f63b6b1126e3fc79091674f8c2d09ca93f06372e48e907d528199e1f5a3d1e9752c11ed95a43f79c3a2edd95601f21dfd9a5a46f5848851e4dc6052b7ec738e30ef7d0b32395e8073d76d3826eef7d9a98d4331df07a0c9cef6904340035e680d9a687347c336bc6e0048e3e42aac9a42dd77042fc774370e0372ad6c625c3228f7eb5ebed0f472d482ccfcedc738c463dd7eb66d18bdb2cfeaa672d084bd7596d924e823aa2e1cbe7bf56cd5d97b00d0e9a1c1c7f30e2f9f868945ac991be9d0bdd31ca53fb5b0bf456231f4c9e4cad08aba09e4be07192e45fb7796cf1a89ea07268a59e354413e3231665b636443b173d1a2b3731d170dbc22ddcc8ca3a168fd26b02d7e778cd31592e01057aac1e901e34244181c79823caf1cef5a6786da1abf226f7fa3e51419eda81d584508824219a50ea80eef1114d590e9009c0794feace502baf3659c8f5ce28f972b73327f1b34ca47291aa07055085bbdd05843a09ff853e9336457bd385b4ad530914bc963d3d93da9bcb8b6320683c3906d20ee0ced6de4675d86766609289a9e2c1a02bde051bedeb90363a8970cc903d1681fc7ea52d653192e8f6438fa4f818375d449640e90bb84fc9927aa25e57238365f3a94e12b293482934313a50fda0a87ef5a15a690390113ded3e4c38de3cd4965d0dc3a07f693222135fa7fa263d21bc87aebb4de63770dc6a27114dc50f81da62583206ec2c5c23c09b17ed6c8ce844e639757b47c14bda9d85909a684361068945c162a335d3ad24c59257f799f4c6c44b2c6a6bcdfc0ada6dcd4477b52451533f8747fbf32a3d2c956990195b9f79d134725a6c10daa3fee05fb368f6c47d62a1fec60ee7e6ee503b652834bb58e73695028992402a4fab66bfc5b38ba2e5e339f74943db20a728261ba9c035f19737c1beeffec8ea2a0d0a9503a1d61012c40b659361c4cbd9621c55af4b5e42a6a596c9a9765542c88119e39e17a3c64fd3b5cdde8fe227ba273a46e74501489acb57277c45367d974d1f46953749cf5bf3717d3c8583e9b784408c78e102cf717c6e77d7e9308c4d3b14746841e3a77171b4e2c754d8073124f76876c8fbb90d58e72dbc57237219874e7c2d7341c8e1c9a1e4c4435abefb32c4aa97809d6cb2cf956b61a93846308327a023a9dd5b158ef263f16ea5a1c4fb8947bcd05e1e67104e9af95f9a101da722dfb4d0bfa5187cf6b060e686ef6dd6952887b2e64258c8afbb47cc319611288b749bb313843944c8ff7246a0ec4c3b21fd24abd8d480eb3de08801b2bc32459f079a2c316645b49e7a80b8dbf48d27a0993642c47f0a4c1d2670b4a2f4e0cfc3de58a92aeb225f471166c39010d5d8a795004e1827118513f25ec9c635c38c4833a118c4734f57164aa70e080c007db5dfaf9ca7282ce966f05ea49d6b35bfdf81b870646ecd8f73bad856fbd1613c74995f54d4910a784ff9041947eea28c4cee9e40afce921de638d691f09dd1a58126562ed4a13a8d7114fab016a25030ba29ff3881ccee114928de1a3c563695092c14902af1b8908b21a4023d93c0cf34bc3449c7741996a89a8d287aadf59958d2357274855e0dd938b1cd86976547097f761d6df342b1ba574af0da9664391e424f083b89c8e8240e8afb9a1fa38338165bc3133d54b9427eeccbfc6d19237f67d684e314a62b797cb1e346f12675529d1c4deee1e9af5b926db2b47bcdc7c9bded00edaeb8ab4d02ac204e8fbbf441bd1e8d0cdb7e5e5b0b063ba98a20aea40703228ba2f0397d80b33b35b9623637080c773d3e17dc442dec1a81d8a50a7224780db8d57517df6231b12872acf57eee27f839ca3581c520a4d8509feb301306a52981ddc3fe19c19a26a06536f444fd7fa63135cd1778b1406af7b2b138039a6c7e7513776e763c7edb1f8f16945ec8471849baf59a78913a90d87751a4a858dd456d08f34620a2b4b9a1912e815927cdb49532d2f3dc5347c3b16924259538a448a3d048ebac9f037d4a5adbd08e0bb97f3ef14ab5dbf10e06b97e76fa86b5b73e9c0b7defcfb6d4dcd33ba21df9b72f8ea06a160e18fd1b0ea59c152e308209cd62a15b0b90e98324e109acd76d7c3ad5f7ed755bcd7ccf18bfbe64351c14aad9188bd57d595ca7e9d89e2141b157b0c438eb56489495b0de8672a769754c499a2d68c0bf1d8170b2f8c226e318ef8b9f4911d510190c0c209216e00a07724da27a9d9f061c32f966866ae980f4039885bf3e1e75e966a0992e2d29b9077939240f581fa8fcb2572fedcec9e11b0575bd2cab7e2f9a58e8f54609f95dae2b7228810f9077e06cba1943ed1749b6e39d2fa3f509243ebd36f52f9762fa68545d8b3cddbfcb0dc1982db87dfc8be03a66353b812687ef552676c75a79d6ecbf54ab10ac4059615ce673c8f00e8b4e388a2e233e2526dc132614543126a10107655d1cb2a3f0bb7bda43333ed46b14f5468e089d0d4caa52832ee3680536831283c6d9724e5289f6d5bdb6c1ab92956d2c8664070ae281a644a6e1575d6668b2f1c17149eb64b9345f09b8ced121d32408d38087603d7897f79c03bc80b7d1dfd283fb740e38ed73cd8050d3204a12124c1a9a45f0c04f6261ee150860ffa6f466096bc0902ea0f2ee4bf1158ee9b67825d3fec1f3b0e9cd2ba3a56f624bfcbf6e323ccda3e9940335042d9dc2f3a0f06764352c69a0dd4b7f0eddb16ce69a900f55cd9648bdca513289e557c9a2b9e31698e929b1798e87d0d161d6b817665dee02dad475310dfb0e86e64393987c11260fe99d5f376857f2d54c6bc949baf27f14d0ff282da2e8df5c9394f62ca2f1baf494eff71431637775c58bd32118b6f2478ac744b5e00a2aae669236d456369e4c0d699a4ef90259b6264a2e84f5265cac051351886ae9be6f3e418fa9032935c5ceb5ad3a6505c97bece411cda04341c51afcbe6da36bc7121566204dda0439c95c9450f49d0b468da80abfe5b31a49aec97e4e2a54b6beb98ad432772bc14b782f87a0d979cf340610daab40b91ad2435635f4e724cd021639aa273edcbf69a8dfb397c538fe5c93869f085e1aeb0419175d11be8ac820f7be162d78bce9615a2a1752c098e1fb858a138a1ee06a353b192d429ab676098c60de596cba4d1e0a05c2ad310f2660a605b6692486acde45b309631fcaf4dacf9949b3349afb64d3c7c06f4ab4c86ebbb376870a384e55e355d3fe6a3c8cb04b358c278784d515b6ec2c42fc4da1a63f1e05ce49607dea86ef5968b9431cddb8d117454782f0ac31e05f91c025da880d8f598b01a0001d885b1093ba83df35abb380a2cfeb1c3406d7df8479e2cecb8d669415f381250d74c12abe5c3152fe79487292daeae92cb2f14cf15c00b9f1574d7ee78063efafbfe9887f2725eb3b4d63b62ab3e3d6fbf96c1bbf60813d02d4806dc2f646ca86b59c22c482b84851be29af9fad8e37a6c6759968c76a667e829a0ee13d3f12db6690a51cba9f790bb442ed285bf8ee8d2e9e4e22442e50f1953c008bdc01e556946125a821cf8010c94e9bec17610d8fb0231f0a7d160db82dfb9ff3b3a9f5e9b59c04b79a60afd5036ade448c166eec523e81c83502f78076ed41effc97be045207b143109bc03febaeb0107e42c31f54e1341d65f67b53b07981bd8722099d3f6f1368be4ec6f1c3016ea6ac2fcce0ecdcf466af316abf290c7dfa05acef6b8c58456aebb5236acba7d6338554bbcda07d42063ea0451212f3ddb75090ed2a956167eefc44043102d43084d148855abfbf7528290e3236297b9e99eb38b02bc280e0a33d8a24a88a4b4065e6c4696e0e91687180f0ed0703842211455b72af9b2b3a14074681f67ce585970f90ff049d9a4105db6d6eeb243ab936e9ec582a2e6e246f453bc9deb3f542fafd743093065fb7a5e9d4bc9df531679cfd15370c86381ee8b28358105f54553218ef519a43950eec8a3b9c0c59048019a3aff8b87c9fb65085633630535990d92658aac6e30a57e7382494217da16f11965dd44d4aef3174c7d3c9cc2197714541abd4651323783d2f1d6082bb4fc25235c9fd55613691b27e17d56671a2212fdb2488056d108b95ecfc2f568e7c1ad163205da12e969fa7c9e25bb98f9f9ec4ac19606a5805010c0682564e2103bbd8b442392a9c46f0c0c00b73ff488d91d1edd69797674d56bbfac29e099f3649177fe90ef9bbc87784ee8c0cbe454af6909c2853640dc0b7771078809b35dd3169f905b827a9d4bc52c6f527d42d529211b232f1c5e2038629ed3eca1b30d28b1af80d3cc0fd4d2619c68cb704e1771d27afcd311eda8114db528766895ad2e89db3b7993df4e6d8ca7d8ada7e91918b310b9a192db90e8ac969ab3146dd255164ecbe75e522afd2dea377106cfda698e95c5e2c8ff17f3ae8b9eacfa021ff1befcf6f05418a465a8715c5e5344e7ecfac2820297b79e761acd74862b26a07ca63cc8311825e09df5b8c30edb531a9eeacce05e9178032365c4104abafadac85f17f0ccb17f8a4b07577ecd191401051bbd11b888fdc9912020c0eb0fc9dda7bdb120a8d4e1dd7eb5d707f10bcd0bf781be280ec089a6d33c193dd9df5e787f1ae87cd778a52692a502d0bb645a90b9f2fad39e7ca281a9a0b29aac4396a84ba9ae452938de1597c8339271a6c69135d77be8b1f01859e69eca8cfd1bc62007991d0e40b9867bf7cbcdd5c2a2e85f0bfda16e1fe31aed38329faa7bead8fb939f2225de4135269178e3c8d61693bc9ef23c33daa6c58ef70cd2ed7c32a343a4b96046bd9a5a4a1f6ba9bfe3ee0aff2a1a6eb524fc1476c4c54ff580197e65a49e849b927828ce7b07fed46d93e1481cd1bbe6a44bd8933914b3a3bfea43452e4004d6c3ef7eeacf2b5ffb68ee0b809bc79b7cbd81b176060fc0154686638842e4121d35b02e3f1c352a2b72ef908a0615df5ba0d0e1e981920a2003ca7ace5931c3288fcf31b629c05d60f51d44bffb7f41679b1d07e218f0fca03c964192712a01a86fa7ad16b06e375bcbc12a876dcc560f23cffb969c7bc7535d67e4616cf745003be1d086a01be2153e55fc5169543c97f99259abd1a2c4b0b87183e5bc4dcad61b6860db581fbd5c66ec5bf2d455681817d8e46e195c0758606ccb628109e49b702937c4de7945ca31cc2e7392027cc955c87454b3f6d6a8ceda85999908e47a36959135382b1f878eecd82802f982aa4d683c2ecb31686197ee4540452524eaece00d6a441fcc0ebc781ed292191e05c15c4489c62b56432ffb60a1da74e29967ba29ad255911075da9864e5df4aa4ec21d9f0cff51dc3c843195c286364c23497da4a8e3119880dddd9e2b334ac4c61628710951f972d4163335afd6bd63177b1d13139597301299f6ac8669f331a6193c884c9d6de2c423406b859e6de43925c1357054cd9303721be504581158168ede5ab76a2408cf06d5852fe5fedee6c3d468b30a7362515df5486a5cb9cd22882eb8e702ea3dcd36e6fd85430e466fb70f94c4d651c43f1fb332bf1ceb49d3311338204d26efe605d164e0f5c2c0967d0ce73a7b5d48affa0ba1bd22ca25f15a0a2022ec7e0156622c409b9f3202f5ae1e0392e048401901a595dd13a16ce15d63ca6ba919c26a087524540426c9a872531120da0cb16351a8f5bdeb3278400306ed9d06126c9f00d00def57c497dd24f20f8e8c67bb3ff900bbb02885c173ef315e1dff018ca929438c674e9b8ac8194e24934e2b0ce4d84fbf031c2bdc6f8467aa763e3dd79e78427d69f03f42ef2d0d675a678870fbf722ebad4a67d370ea60659b7ff6faff16a00e4860a4b6d03f9156a19161e51363c16ed7e583074b1f738fa3a28e03c1958309e6cb812ec215a01f7c44c11f16dc3f61337bb39bf9c446a9ea7e6c855e525ed13e85666f32af7fae3c53e452e8f6d4af4195845da2af5e9d4805f172d199625510ed6f20e16c219d8e88d7cff84d9b9fd1e02c43ed56a36c46c18a0a518cb8e64c331a81952f83468780f2ce44166240fa4e36eed48d483536aebb5869973b5bf05329aa82a38a05f05b06bee89057e9d48ee74b20c8b70a5075abd1069eacf7e661a8c55690a0c7111acae5fc023456ed28f52ee21a17af675e614321ed6f1ca132d59570583dc387aa8f602c588dc064124a3b5d4048a7eeff3ecce876e9f9289845130a8c899b3dcc90416d789715527629c3902e2363578396999d4bd9997bcd72ea195c81a80d58b99ac2f431ee549039e4d62c64148a06f18e69884dfea190e084017f6f83d2611b3e88c6d049f09c6a3a3704a009e1187fb848b52acfccbdb4e9a6890a65837e4b89ed671420338c66180e6669ce0a7ba19128a84282c0564f7610341e6dc824118d9c70936ce500d8bd4142a2f26463cb87829733598747dfca60680ee6b796788c11098fea8db0c507c4846dee060279a6baf8b8d91327e046db6ab8ad28e9dc422f265ce0505319c86cda289840a7bc233236e7a9d015350ebc80dacca853c1eeab3b372596153afbbe872d22d6ef838b419dfae1ff6b84d51a181ac53fd16c7cdda05f991c98ce91b789f3b1298f7c6b5229a301e858f9a87ab2f5516288a559687a63186dbbdf23fa37ce95aab5547914584067a7fce882e20fca2531d44eaca81785b1600e3a2a4012ce374e1cc6b55a1dd35105654c0ba755ccbd04657a8fe733aafdbdee17a9d88118fe26c080fe23d1a16374cb749e4df55a62e4acd80833b659d694d2d424ad6478fecef84b8f213f1bffdb6e87f7c923bdf669f1d425d7e5865067bee2503e23da2522e3e1a455565a06197e39c5fd5326862e26144d114522718cfaaf8d7836f953a214d894af82c89748732238d6f23d89f08dcc7ea8df4e3ad189a6844f065231206dd2d14650f713b6a9590e675dd817285027e38d1a1ed51710a3807c10311782619e46dc7b3ad1cde08bd84e3a71ed651931137f67ba0f64cce1019a42a384265112a6c90f8f0182c4cae05b905b96437409f8e396f4f1c31b4488ec4c192f306d6cf868bb255639d08e69a651d1efa2940fe72a8a92971b07f885054881ac3f012f162e4f7a09bf04cdcf999a3ba322bcdde74b1c68a4fcad442a3ade9b94ad088289507a2023c4461e67f8da363c9a9bd35114f62f6c95beab24a47474f432e05914c512ca15a630931a9828ea0a8cc76101908c37591b9438082a9b0d3b9bf52bbdd571e9cc82654011fc059244e0b34feac770c0c4cd64f12e57be500e2c4173587d9f156bd4d20015502e0ca1a90c225ee5e6464fdbcdda503c6d2bfd38a5220fae1d6a4867457e79b69cadef36ba59ec382c5b8f15d20f3ee95ad670f88a2061c5c333fc65cd163cc3ff89d08dd4f6782f6b248a50d7937192517ab6444e1de2e0a6a83a88ee62d0aaa0784f1e436350821debfed622c61435572b16fca12bd9c817b67cd9886c4f7763b97077a840e8c6583f4da3a3d06d339d30e8ca662426124c14a094769954b2a0887d3bc3c60dd0d973b54d81b657c6d03872226f8052589c57646199478b43cb0d859447458458d4db0615f415e570e145d975cda06ad4b6689a83ce7a9497b3ea94457c22275586dad09a7b3f54d7c556e90ea374f70a21fdbacbb2399db4ec048a9b186cc8e7368977fbb8ada4d0db4d891e74a339216b21a2b15e47592e656aabfb2f102b7bb4253cb3cfab64d2cfa0a349db843fd98dd908c2719bd9351e1478702a96b6e8da99f13f0855b24f31128f6008ed6100ae60ad43e2a10e6fc915a4f3244427fef996823b94181319581f202e83aafce3ce44e27c918c0f7b16e58770e1cb4c6a8893155ed48857924ce667b93d7c7eba0b11d63db166ae73cb62aad0ec0e439cf91c984d9ff7cb91114b935d9b8b0c5f366322a8b50092f6aa09ab558c5468a086d676db61f3625f10214336dc22cae43a07d7ce399d84817ce0a292a0e95a95783067a34eb4ce51f3ccfb0ddf8d880197f340b8383da0a2148bb72f8cff5f7d49a58148626d7494caa227a4f7f7f608c08f822d8184fc5f804238fcc587d11bf4a14646bcf6528bb7d1168a78fc8fc14f9a57093c14cf71cb983bec8a1c7703a252ebac13f864c3898dad88f454c75803155fc18a4a50f4970321059871b1fcbc37c842a67cdf88a1b7590f6227f3816ae3da0a43b65b5712f0a4eb7b20b8c69368d52257ebc33b4edfe0b0a80147ff3cc6a151e13cf082db30d0521a0ab20bd241bd03acfb0bb7a0987490a349ae9fbde5676e4b77117ab613793b2bfd6e85cf74f77c97aefee93fe155f526a548da145a362a970d30aa10319d1b0281738b305b88f546277c6b30c260d81f8cadf1495233a9140e6a1c8044618b25055fb124975b69b9dc452f8e25b8e80b024b0b4de2303b9c97389bf96e4838b5f564fe36599900a885e2ac39d61b19d4750b15806508aec1dc5af3160e5acf7412bbfa93aa22a3e070de87f4048b9fb1d4cbaf3e6adf8f6e0c50e6af8dc136ff6aefd14ef463f32ca35f9a48f257b99317ebed25d7ee7648e5257f89afb788a043fd66c5e8adddc3d61b28cd149b4478450effeb204de8f3dbf0a6d911388411ac5b65b621ea6d4a904d9da11782ba12749b3a2a81281dc7b0832f2ddeb5c11b797af091e24fb325e4c41f83b8e54cc748c19f41ff5a93e8c461911d69639bd0aeff6739dd627be2c7d3c15de41a9221aed853e84e717c853eae03f92a66b99788c24eb86aa035b9923d90f9f6a6eca8ad043240108e116682af562a245c7405e583eabf6273dee3c038ce4411ef0c080c7ecbd005c5ec181e9b14862810877d35d0d36e7a9d2bf8dafc485e2d3d261012f5c83778acb8b03f80731d4e549e2d066b77f820cf392ed14c4dca2a9a0ab9fdab89c2a1065cbddd35cb7269fc4704702e6a593adf6b21f5592400e224621aec4cf607ed5508f5a6da5e5ec47faeb17840d628ad97b78973d42e4c656be4d36c5096942a733dc8a21eb26fb29fca2eb8f8a9d5396ebc3cfd4ab92397bf37ed28ddef722e91e8107dda018afa4169f92674a110d40a998ff7de4597186067baee0d30aa18e8635ec7c369a540637295263c34f25634ec52b3483e5091432963baf4bc22d42200a9c4eda58694bd8b6e24d22238a1bcaa8e89eb524a53496bdc5baa6036be60d55253e932603903711e4b1a974a1a2d01ed82f3dbb2c967985c4340208c191402cbc096daecbb627cbd6b0d0c7125966c527ab82ce6633a1f45047f248a018d2035855028a3ed3a780dff8b4521adf19be8b18c99197fb04d0c0903134fd1525aa7b7a3ac78430ee792b066e903219016053bc918e16021e6cd623df3ca02b99f4fe3de7046a78a792daeb363e4cd59696ef866383cb380af2db2efa0bc3943265ff369d09b1b7f9fdfcb01bdc0d1295b7dbdff34ceff9436f000ffc88047cf38e0c6f04d52eb3de60d682f362490fe15cbb0a9d4eaaf8390fd3c8e917d4f37a7f3515c31717280244cd582039b8e663d08fde037f507bfd19303135f456cd443b1e999ac22e7341d8c701d14fa8c5fa28f3330af3ccf3af17d0e527e03da213dd38e494d16cf3bdc31c3091e00ef52dd9157fe57bd553d880d98200690ffb9b24c2fe2ef5c213de6f5b0fd9f5757643ace8d1b84e1fd3e05c62cd41536fafd375c21ad861dbe56fbb77b32b22d3c985f2268d9e5fdd112f73f3aed3456d894369f51e47e1d794bd6b39df162e534079e3beee1e9066815b85baa09c5285634a23fdcf794781ccab03e99cf8161206d8ef51464ec6e867f8841828e6cadc64166fe3d7205c361e79e58778e4f0224d702b596b33e5b4827cee9d798b82354970e41836856dcfaa146932bf0a5ca6c6b351bf9c79a79da7a5c7299f4c711095f245be4c8c2f5ac7035dc6690ab2dc9bbbb187e6db7e684909663e92c3796ef97bb321343d790f3fa13c34fe524043d2ffc33235cd42e30535a0c2304ccbb32a75ebd2e21d8bcce4a56bb324f0e4ba1faaa082ef552515c400d878f8fb0e110b0e85c139a2f7af7c2b7aa8b49b065e4e23d0cdc136699af6a63363d4473d97d1a145640fb9b17d112b76088bab1e758a62f3ce6055ef34a2f0a807860fc8930516ef805dc13a8c62dac18a1d6f48b72449fe0cdffe181e132b979ccf0ae2250ba608aed691d332809247f69841fe0a1639202b4d7f415ce7eddf9094944927f57c9b143cc35f4dc5821d30de54205d844ff9664a69ee4843d82048bc4c0a92b3b261e873fa037713926b5e5d829f1038379f1c73ac01d48dc7004017a6b22b2814ea906e91e0d0dcd3671e0522ad82bade187c758c249a3976acafcee4403841966880ae9402e753e93504b1e744969badc2d23927d4d8933001de4e8fe23103620559a53839854682e5652dfa6b30b1a5c8e775e8d4f48401d22cfece588bb610fd1e5f4401c702fc803f751d0dc9e8e568384d0edb9154c22a15a5cb647440bd90931405871054ca2abd512c78d3a9abb480423e5b046a409588843118a1aa5ee45dbfd4cd8c80bb34c2991856ca1f33f83d265e0b9499880fff299af2fbb8c968fa4956ab9d3e02e7733c9b71608cf723ee6d50b2d545d4c1bcfdb657fb039d4c4b5bbba4d8f66801b742a3146436489360b0152e40414f0044b8fb80dff2876a9d12bfa8cb8dfc1e4a0f9eb9b3d54eea29935473324807e8e7b8d604c8500cbee8d04d8624d7cfc140ffac6b64c24d93aed18ff54038e463e281a1a2b1530054d5992a4a128ec26392a9ce00ecee235283c913181b1e73643e2d457cfed09f3cb95635408f34e4f2df4a87174712493af61d830eafb7cf2a3f9655b22f2a36e3cff908bab1d63d5fd306090e0111c510b94d8fb044e103ca0a1ba3f4c5142c0fd3fc51a5449f541e60f60c994b192ed80a42f52b6026f1a1130257b318be17fca00f93de8e057c3265c0bfd0bb858304705cb4d4881a54a93f5d58918501d000152717e1445cf9846a8f74a5325fda4a6a90ad04d98887488035d1994f3798daead6a6381d3c69ea3c6446169005da6559fa6daaf99cd229047711988944f7e56b4b41e50003b199bfd04457dcc1726c0f8033e8307ee00706d31027c6c048bf45f43abfa9b70667f28c5d9de225641bded41a8d98ef813544f3f7db91adde1a1285a21e40d4f7c4984b00648ddb2b31130db09569b36ef9d173122e128943b1f3a489a60cc17b1ed652a78e9e80b7b98b512568e66d94204916c268f7e5e5f83b7bca713955df836400ba5599714a0aa8a64932d7be6e6a95566629686ab1d8185f72077c8c4e5f84b3aa44ccabc181abd5cdd1afb085952caa817c1552b3df6af11bbe2cb3e6eb275fc639b8d9e7a339bd811e2f23b2442da68204c03fd297a22c9ec910e6583ffe256f0f3a036c1fb874ecb678c167176d44d223fc8f2f997daeeed3a63373978f3e93b740a8a23bfbbc41d4ac6c40b14012635ae4fd2ce3502469a5d055baaaf2ac4659b4f036ddccefae1e96fc44bae55bb175007facd9ee7ae13d5e3881642577f5057dee6b25c5f2708efad8e5954cedfb5c27b5a581864c788fd102ce6cd5346c37ee54e25fd696b07a2ee428c7c084dfa22ffab92102c135ec1a0949e7b3bb319cdea7f445eeba0a3b60921da011d143ae3c144ccb8c27d20560377f5b8ac2cf6cc2d745354548f1c1df88af27aba7924a7df88df3690977bd36b1e53ef0f20bff181af92dfc5f11aaa8bc37121c6944447749951587171dbe4c4769794e76363241089635981cdc7874b8d970f144864f99986b571fd4e49ca3331fcbadcc0be602d21a92db2a4165b2a49275d71900006f63e043a5a5537170d9900b4e9c690f785ed6ad359a69dced5c7a4bbd7869ab2146ab5456cd2f238d9b5b5277b6f2f807140bc1ff0f81ad732b40f4aed55708a75417556204e0ab3a297281135c4872a3dfbddbe776d5402536d564ee71cbe684f71c4d486c008f16d315c6a64d846e6f0cdcd4aed83dee419bad143e6cf8f42c6654cdb485c43e9ffe670ca1a77caf2d83f98b510e61acdba00cf823bd62d7aa05be8b633ba752e4012ccf8017e0347fe1030c2f8882231a51afd3f0bafce79afc802904b1854c2ebd3d7053bd08340597efbd32151e971a29ba6020a6fe5468b18b0ed92f34a2c5908ac2a5082292d3fc780751e8380b8bf04e06990a6b2ef974c72c7d72b878d7087d37c23b88caf1c02c04d6a01441a4582cb7343502d7980873783b726b2ef76ca239f35e20cbe4be1230ae3ffbeb6165121d58c15965955ba5d00f055e8c92e57719a5680ae9f23c8173c340dd59c9a1be3ea76767516168bb60d8a635c00bb95b4dc57640edaae56c8bf803ec6b0138d19aa852fca1bc170624cd3444d2edb867965b2604dfe163ca174cb164195d3506e4624a9e3314b8b21b639badeb56b34f173bd18e5f23a266bead096daa91792c2c2bc8b3c8b7ab78fd21ea2cb51171b2d3821a5f987ab1f769c13a1badf277b605c5d3633f366450f5e2a6d82a77cab7aed948af5bd76424d985517515a836614e919e94a40d68496d900077dcdecb913138be10923795415c8893004db4990ef8248f613cf0fdc34aae60b903bfe3d8ed0d653d008464635d2b0ae8c41a1c0dc4c5d85507e72d39d9aa72fccee4d9f24967c1fdc893b0644a742e6f9940992f2182770713d207ae9ac71b9f5938c2a8f6e381cce64986ac0d7d4f4b7f8541018eb53fca9b6438161e9ebad82703355bf5a01dbd758a5713f85d311c5f0a904a79ee20fab17b12aa54398bf65813ae3e80b839f8adb2595b1d1d3c8319df12f26d5f366f6bf5b3494109b1471040723ec2ad9e662e745163243929e3f7db6480da4bdc83b241c9db00b5bac9c542f15f93880a1ea9406cf8ace8ad9f18fee4f755a160dfdbf6e9b869082c8942249d6ea4fd88cbf93b5f69ae3e2599a1a551702a7a36d28654e952d6818a424e0a8394d6f7385f6422e55736e5cb8f0e9962c80566ca18fd520178bd73dc1c9408bb651d8e0227ea397f8a810fb6cec4ca98b183f4acfea913f362e6a82697d3c180fc7d360c638facf29f828c1a3931bf760c9bfbdaa49cd1e5768645e164b8d58213163c2392e2b725d4e4b6642c6ef616a5fa7c175dbbaf1e132ea1e92aba292d75b1dc9401f22aa6f651dc52fda7e03ac95705eefbd17b41ed733201323aca4638a76a18a62377300f255e96d839a499c399dbe7cfd967b69a2b9d8e59f4bb45936836ed8a0e8be1050609abf40a9ccc5ede1795ce460c150556076db2c0a622a6d38714f91df04dbdc8176ba9a39548d83d5d76127981e2ed85e2532a5938a21d96b81448e01a9d0810370831dcd1138404d5e693985910143a40a03597ed2e2fb0be3cd2336fffd39a8143a69d0e8e40d5a7bda326207c8fcf72317b061d26fc272acba75bc89071dfc3b0ba47217adf521bf7cb16ebd9ee787290e842368a702630593c01506bdb26f55add3031c10dc6fe7e0ced9074010844f09c3ae4ce4b4214f37552941083223e74aca24ba31b892b8b48998668343bcb3ec1424a9e34922515c79b68e21cfde887973e33111372a494348e900e5faf65b9ad38f213840810c3ac18f2954c103c3849d02b638a958858e8f3500c0e027e04b931b922253c2e3c4682949fff97d5b984179341b1690e8c69df08f5510a2f230e97dcb890274108c4878ffbd9789010a2035f5c0d3cad93b3ec5b6fd2b86ceb2b11e248fc0a1f5efd4ed3c8f462b19f02f6d501405bee512866197d87fc1c941e0f60e09a0b91118a2deb60d3d26046d91e0430f2fde75d1f6b99a6ff06786e44d25b6833452112df3f9ab6e70d7c64a5a91d39662471e1a9b9853193ff5db30f3dfe4c76c3d6474a2209aacec77c945930520a8d3142c0fd061e91f1b96e708fbe58d3ad3fcf7da669939e80bb2e8adb5b4b678acff67826bd625a3e24db5ad0e479dafc8110c68eb7a54f834968b58aad5e42893f83a7a196bc829147364ef9c9762572e0b50756257f4dc0fc3ce6771e0e95f52cc7db7abfc4a4f176febe250a3cca89ddaf931f825af13b12c7f255d9ad4c6335d5eb08917d2e5c706099c5b08c407cd82c0969016cbae79f111d15ab5885eca0bc2db9bc8de52b549c30ff1f0d3715b6c188f50ed2432bb28ae5e65e793cf03484662f8471ad3fb011b7114efe31066a89830831deb3a352fd39c64bdbdf663f4c166a854ea9f867ac9ae22fc36732baf8fc0a399e3d6aae2eb2cfabbe97bfb2676baca561146ceaa00cfb5319d182bc924c5f87ab27bbd33e1559b5ec4caf640273dae2a16b464782e3953b6c8b4de1a9e42f82f001851681548c4d9e8d7a44cf02a68b56353057e2f12d348c615194a1214582ca72bbfc28536ca84c4a808b1588380ab2f3f2bb064625f8ed89d03b2681db8d9fe0fbe496f39c82e1f4e4518e983a76079ce5c1f75d2aaa83cc0eedaac518e542775453b80726db5a88376202c622e02cf8591f3258a943a85dee0948aa8c4af8058162555aa88c3b84ed112f7b2aa1a4284923dc2a611334d7ccc6f0609042bff50fa9112a43a2be8cd163c7a21987db5ce78cbd4a59d2c7396335bd228fb24df91b18a2e65082f005c1d072d0a07689a8b739246893c13a2ef2d6d7625aece84942c811998556f7e88319b765484543af4777f84ccca93d61195f66fab0f0ee61092c91e7f8b9098fdae7bccbb47f78610fe5d630fcd982a523829bb3458eac2fddc72d439859225e34aa458d5a08cf4108b80020eaf71dd3808d574110de082a88b9d871bfc34d0ba8ac217a039531b12872b704802dbfef4f8594e90f86c0e4cc0c6183b6ab4e3ffeed0aab87681f85b98d14a51072f890619780b57f65a3635832527592bf9193aeea76697c58c1c014d90010f0bff32672894fcbe0092b59245f22c2aef6b995805713e0d6d2497c85a83ac9247e91ebdc830175d8e47541e0fbd3efcebc107be151dba137db4b48f6e508ef272056a14ddb1eaa62a711179aa7b9a349ad250fa9e7d3738944e8bc4bafff6d81737d8f62dc577aab7c1e5f75a592a9a6b97512e22194078bf863119cda277351fcc3d681f95a8c304005a8a6bdc88f950ac10a2eae738a661e46d66f9d5d9c466cfcc91ce3db1112cb4fc63f237d306b7e4e7aae2c44ad13649a00de70eebb16b013184790a2b84bdc1fd17062c1a8ab6cd166c0156a900d0ecf2b1d7b17a3ce8535d6e86bdb25b2cb842c86743cb1baaa66e61467a93adb64a5394228371829b057bfb89b95ef88f0e7b344472087ff7f00d87e2337ea508ce5f4a47a3fdeff6750f2ad694de40672e54c82048dd66b2bdaafcd60dbe473f3fe6e6dee4bebdb9d6e3935326e7f5e7f0916ceb99f3342a5b02704255dbccfedf8d4b0c0cbbceaaa9d2d1f739670d861335d98346d52f14c9ce884f81a661f3d81c728f2b61077434e06e6ff89d5c7df84e8deac6dd0e60799e7ab7ee58b2a98ab9c051477da623576848a5a487a17268d7dcc2121d3f2542ff4155124523a1c039263a0817851566c13d28397aecb5e89c8a3115121b0b265c39aba7c4b35ae1f7a477b5b31f0b6858870faa76b9a303204e179739baf32e61b2f5ec87cd320a2d54295a34fd6d686b238e1d4e4d0e1a12aa744c26aa9169be4cfa8e4bfe125e5f0067a81c535b87cc05ead182d158bb25f24c4431e6c216f74e3ec475dd826b745367231944a27345cd819863a20fd4d1b1d7c66c654112fc740190fec8901f6da6023e48d0f1178ba3604629dd4e7610e241428a42217cc539953bda3202948c0c686ee7731c36a56f1517b4c7eb1836c1003a1654969b04844f9598b9b4f5eb6730d434b2032fa78ab8df5c9cefa8f82d41bc41eeeb5e49721a208b4c7911478d828998f4641f4f1354534815cdea8148857977a82f1b8d0c3a9cac2c18566e41bb4ed44dddfad41c7547c9270558274db9eb34ee1ebc3088cd4fe9d7eb92d8294c7c313a0d9782cc4e38f9264cb69cb733e5131afa477fa809648d8a6e95099719200bfb81a6ff9add671ae113100faa14692fc8f0eb533d87b585a4bae90fb2a733c6b00413fe050d3dfa9f22c424431e8bd9cc24c9849a0948d4398bc1f332b4915213c9e63e899c0110790eb082010a9c2cf20aac3c1d7922476e12c595a6592c58ce972de6af0a869cfff56b3ed053a10f238e157e1e0b6e52ab10e7215889809c507eb924d9a4f6f20ae329a78901950057b1e0cc801c7f5aa6a3437717e075dce3aeb8feb54d4862f6721255c4132279bd34e986e00a555272d0a4411ef37e7c44da42c234e14f7b6b13741e1fc482eb55c730e944a4c9dedd2bbb4424aebbfe74a05ac70227f06af33e18d35f0805b78ae65579945d4ca6b941adca8fe80765ea594f6fd72f4ec51eb8d8bb3640d8527c069df25f2b0217cde775f3eb3f572b0331f23f78af4510314f0153da6e055a375011d8973291f7b3345b8c147685f839b82d4cddb17ee834490c2fd33fb6eb995b20bd93bf0198308353875c1b01d016f8a35f6b49417689794d608019d1dc0ae76b092155d32ccdb2cc0b61fae1368f4b8e4520fca4e6c371c4d19c4a1c299670d2f23f82bf820bd4fe4a59f31ac3c81b742f99f47bdc8806e77a52f3158de569f7b661db0fef0c90c15b2cae36c6041e0b8f5785bbdcbdc3c43310c0b840015ee22f97c1916d9c53a822bd0e382fa43aa1bb415608006e372110b8ec2f9a2e25c23f4e7798f24311464b5f2400533d7fb5074c5d48b78e8471c7fd87cdaff7dc5d88f6ed48eef2c441fabee0a8b20e04a4ed150121b785a97c801903af99491ded17e86f8a104c4076094bb0d97ef0af6f145bd3f98ccbcb286812898abfcfa351022c0790e4d99758c49722d4a514f1128aa1fd3822a0ff03e5f17d947700491bd750aabc8e5f82b0aba5da9a80f61b3202d29aca715be5411225bcca00d2f51bec0d92cd803d1915b90186d1b80f58840c1fc8343df45faa4bc6bbd4858ef732477154b31eb01d57f23f3e0d83a9f580fa2d2904c46cf2a81a73b8700b81488222d141ae920cffd5997d2d6c1083733882d73de6146dc9d78010f3e75546405d0c10d77a52ac8e78b0410b0a997671a896a590fdcfb0f9bb2005f53cec630e83163497f9782159638d0bc9d270cb32ea16b2b400950b706efb804d6bf28ca1f58ce71be637419e1cb1019d2e0bac75566bbc67de2ac9b20851d77189b6f178190fd1285cc18ca6e9a48a129e41f1a0bec37e522aa6ded9faa7ba70297536d0b3f9f10f1e579a58635def3990dc5ba0571a12797c167154455282f02ba1b5b19d3f585a45ba7547ecbea2dd85a5a8588e7fe32fe7b8ffa12348320cd241bbf13971fd6e252efb9d0d6277eb0c103a7b3c305e02cd489ab5f7b79745d91ce9dc94f5173b314552275086ba9a501c1cc298bc0a92352b70412e951264ce162dc4d55dd1c38c374852b4b54dbd10fe48fd3ff8be9039d6a155e5abffab3869b8c4a1700bf700cc859186b6f7d16a5dc21efe6113ad42cdfd7d73999f27994f40f17c7d818bd26b190eb94a1ddd0c6c291425c266886108ee48f31f1180fb28544dfd2141650da16453d6860335cb1bc96395cceedc9f02e0ee68b8dbccb0f5a8ca73193651615039e585006544858d34f5efa600a910055997d2cc4cf9fe05a9705dda8a9e3d940c10fd1484ba152e5f115cdadbfdb7435f411d0bacf82b187a2ee042e2600a5988af4075ab98893b80d5808adf4a05e26070607dbcfbfb8cbbd9cccb03ee55c41d60f879cdb161ed640b1a8a126649eb80484a5054657cebf96884d2f8af4de64452de38f5ef7d4a288ece2ad224de008d8b7f4939c4114239aaa23a720e5b0e22fd68bb6d25da6f57090c4a9c62e3ed457682036fbea67381b2a152ac60ca5ad1c20dab371443986c75950897185d932d4de893d2c1cc35b11516c051b22663ca875d0d81d48460e1d8cfdabbdde8cc8f9f286c88087a31c41b1241df85c617a0e2216664379ce1f77aed1ee8c2ed4a97e26cccc199dc9f3e0a356821ed2a3cccc102c4f93efb6c4fcaff2e5b9fb552af0b2c64c70901e2cbff2131e9cd2a98185e6529e6e3880e5304dc6a6de93835f7d21143dd13e7cc0f0c3c395f58e166fa5f309fa3a32466140362ed3f07c936e90ca3a0abaf08c75f6658b244546d5cf4877b4695c4d897f1ac5f64a69903b12e58541fcfd94b70c6713cb83bfa22df549f5a6c141423ee473ba2a80a524afa2e86aa09664ce95702e00a0a0b574bcb640962a87e4b1f4e254720f83f6600d640eab3ec5d1462ed198210a760bf608979124ba99259561da74baabb530bb6979c33960d5e0937b174970f905d0fc7223fa768cad0838a26039755911d1f43d4b2dc79e43f41025954278a837b0d948cf92b2e6e0a2132eba20284ba0ac1bdd3e913d1010ae704182d293acd28761c8051bcd2f0aedad8bb6953141bc07eaaff72e67e3feda79a1e3352cb471e33e967be29f10d86ab31cc01f0d6807cbfa2b1f0041fa0a8a6244be124c8e77839796127b044ce63382573ea92237d263437f262c300d905f66cca8199d85dd980da3f2c3293246e1f518e93dc7a4bcfade82fd9bf2ca2bb78b3f85adcb8b707a61956d343f1fedf1e47b44204e43f1284c098630cbf650cf869e5f7f5fdce6e0e30bc1868992cf02ba71f90121d1c33238354898027680e2ed57cd4c696508be88d2edc03e6aae7ddeb75a76812c9c0164380653500bb07efafce604e7f903fd7599b07a21b881d462f7d642208c8987fb2fcc2bfa22df2e50132f23acc62ae6f5a1e09cbf27b19d9f9fcbcdf5253155a72a395e58c9ad186b5bba60bfb336a0d2ff909b09e7f7e29cbff2b879fca23dd8b07a8f6e849d2ef6299c156ad71fba3ab5a6b4cb7090c0a4bc949da8ce6c22852530fd668b3afdb001c34875e569118107408b7b3f7339b4f150b8434ad46dadc9b6b41d92ea1ef8af30e4e725ad467026ddbd332c424f224d425e86762fc9421f90d79664b245e7137c37b11797d92a9aeb45b30e3fd6b03d6d07add5103074c468469e204398f9b9c9448fb7c52c1201d88d737d444bc1b761e7bd72a054f8e9ab0d437f5cbff856863459d00fbeecc38fc246d92980bae4e04c123e09c15c8a9d98348988a969f05e78865ab68f3ee0acc25384aa96c9a6942dea0a658b596c9a285c0237fe6c4b4a40564b08293fec315af5e26205e9a7ceddefbffda6c6c6627c0dc546f710e782e0002a8d1eb0835676508845ff16e2ae16fc7e66e44a4b880feacaba929f3f88d6a6ee43c3b64414402f1da820ca8a8258d2ccbe2a8b9470163cc0ace89909bce0cf4e3a7745e7869269e72a8b4216e3dfb3857e2eb0c8070ddc183a0a16ed18d064ad7be311f97262cb9e2b2403135eb4f4a3ce663270f33ec524d858e57b1435834157e768dd7130f1eac85b038a40040936eb43fde00428144ed177f2153729cf738043fd16adfe4f7cdccf89927cd97f9b56b8c055414aa1f9c66558d42538512e1c629f8deb900d4fd60c5c6745524fb4af301bd2caac08e1f9696f5754088ebc6f9bfb48a1072f7333c65e13f6bcf7f15f14754641c4ce80827356b1a5ba56d5ca86376e7b4c9268b757ec81010ad7346eef48f258e320a26de31cc015b5d3c7eaee06ad7fa92035bbebd83f3e11999d9933b64becc51483cc45bc7cc56869c0e9dbcc047027a53bab6159a519e2e0c99d44625824e20954d2477b4483761283f6c1e2c843f055eb07327e474a2bcc1b69a5475e1301025c6f0c1ad0fc722909a62182c3dd8f8699ffec82b8bcf08a47dc66306e4b268b064669999c9f68f1d9a99ff78b218f2ffa46095227fb5fbc270a88f8cf5dabe7f25487de01b6506afbb7320eada206d4855acd1b2ae4c44d60911329906866f6dbd6b5e79a30826ef90feaa35860406fdeb013820d1278f43e5048199c83ffaf4e61fe8a0a1ff609d7da26302dda66121c782575f89b49d292767ace1c5d6a6536f49089e6053cd0caed9808e642e4873d21f5c8f6b585651c2a2005c0cf5e1b0dbb034bd3bb8fef4d9ef713622f3e87f1fdb06a3459183ad7b017249655dc8281675cb09c0399496fb64f3db44d17651b79349377f6e660617503756be00483d52dd4ce0417cc14a44a7b1ca8d7179cb012494e84e62b518129a25a6ff1c8dc2a7f291205322108a34deaba758aab1aac8bc0ab4f22151a900a62ae3a65d9a946f0318957190a810a3a856e226989650491613c77924becfb51d26ff9d36601f50c57a19c299c1306b836dbf44b49eb3748a540e01ebd62d032838b688189326ce0ab5087f669f185b3de079785a1b9bcc5d31568347d07579cc7b73636dd4a530acab98b5137188b4eb484f2338fe0f50af40f2a788384e77adff37ce480fd0abea25d952d068d11f07887dfa30fe1adbd1dd21c98f47b77240515924fc120704ef4a1acf9ac09623109b15041130b3c9589148b61c46a0071e7c66d8d1a7e858e74f6ccddf1c3420b3ec5b77557828b3ba5bfb274f40040804454d3fd6f43d3ed2b56ff4f57d2299475052e3dc0b998fea8fa04d1d415fb788c5963fba42af5b7bb3eadacc0cfbb0c37685f11d162e9dda7e647b5cb8ff24d67a71e2389d380c46cb5c7d2cbe1e5ade9a89902390ffec95610c1c227c1e9f8e754d00f6a2cb7d138442ae10dc58ad833691634285ce56c762da1f7b33aed3a2178d64a86ea81b0ed33c7f2a119e97cb38cb41d880c327de6b6e1c66ec48b936339ba5091c23e7581e299f7d31ff7b2a5718f613a14231147827616d78b5d50175e6a432c78d28e9e8e5f6cc62410a11267ad7a97c85751cd8b537d0a82b42346296a86be965cce4ca08128462271ba140853ac91ad3d28e98dd95194a55bd6dd89ddc244e3c66ff0fc20a9849ec30fde58c9be37ae8cb5db1950cc48e5776f6e07172f02c5b8d001813595b9c36a4fc351b16cac187b57b5a7cc55241d767316b87686d487722c0ebb797874e0e21f69da655a219cdbc3d2b52e212491d5f2248d1047c9d82bfb0c5a0be81b6d4a364f2b2258fa6261bd7c911c96b322421c4c843a1e94db6c3be68c81449b15724373baca1b703b9f3e647fa2903b2c8f973e111231bf5d0179d9e4c8902087edee899f0d86fd81eb7a79b8ebe61da0098c996d5c716ca56f3b52aa30316f0d88fe9557b653bd77401a016122565cacecbca41e6892c345bff3fa41ff8bf1b95568e2666cefe88596602f655b8e1d07800a7031627094c6ec84afbfafb810843969273f42700ae0ea28ed0f900783f0417d8450f73db002196ffa6318e130f6ec9ba31a7fa90fe4605054cca744c63ab8676d537f836fc0895721930828ea6da82f1552e2311a5f6128f58112b4841bfdd33e61bc759c3a504053bdb0c05d349a0fb4a935b86a923679b6e4ffaef779e703a91095b92333a4e0736ca19fd4fff07372eec3b186ec6dbbc19b154dc3ea9f2a8c301ce0d42194e06d63828fab9f2aba25896382f30a6a90c9897a3daf727ffd1a04f1190d168d211fe24718c29a6261c2ef53b203d8514fdb93ce4836c2cf77257e5639535845d06391e1add9af8cbc2374447b97fbde30f0b87cb9ecbd5411edcd1976adbe7484f02935b41e9584dfeba82527f59e6bd87962a0fe39e415b876c9b848e1aa21c22509042f50a213092dc5d737e6525a960a1ec8ce4d16a14886fd72cb256028f118b003d5802c8e6d0303fe641c0e61734a01b5e2c78f8d802008d5da5b90e3dbb76e79ba405362040e25ec315b223ce09005055935aae6aca47423cb03950082163631c878dab211f36b73aa878bfe46b470b0e92899182a59872bb6f4022fddcc2231e13ba3aa30e18bcff5828928d0e709b94e91fdd411895c995508caa377f4501c72f0ab93a491736ebbae5b55a7e83ba990650308c2768dc70843c37f8cd0fec9776390fe0298b834c088919964d36ce42cc54f80671074660be02f2bd9456a79a2e57e23e02b66bd2624cb183b841703e0d8c6ef2ede6266f882f750672fb93f4b9c69a1cfdf2ea665a1595de011c8275daf0a30f134cbf37285815755c840f35417940d3564778ce080472720858efeadbcd29118b41c7ba96b7cfa88984f8e335e95507870f00271f4047299544008eb3b82f45914c8720eeec2e22a49a7a939dbc2b1f516133e779a55dce473c0767ef280b36813a3648b932c3b5e4dd23a198271aaf1401559882f889cbd7077ae10a3c65f24c8ea53786f65757af19bc926fddfe7f9b29b72d8d8547e237d68efc27733289fb258afff9258dfa60c2c15562bbe8a84de53a6e311b63863ccce9d36d8b1ee83b10338efac283d0dac4991c4df028908ca6a8818cf958d74425a23f3bec858357699f15d6b6d5685ef983c5ca3e23ab5e1a1b1b1f5bb0ab9caf6385b365ff0ef51f7f2eebed8cdd48f9e9b9a45e16177f3bc2f82886b50815477e3d712d8b876f93586d5f15f4a79d59ebb3c59526aa0a6cc625ce4940f7d540e79fba9a9fa07dea01a8630c795c8bcec0ffde6e75f35435bc03b150e2fe2833ab8e4d338ae2e33e3fe1f2e206869cbe8931a083b3d60852ce78e577ff7565772448076c0a6c7499335efb470f3ece08cd1de703039d04e2b9ece715e4fc83b85ee6808b42abbf9ba792828105f82f657c47ba8ce4fc73c332b5b94ee8781d9e4ef65f70a8590a2e2a6a143f3e72423d6b4a37814737a9d54d387f0ceb464acb0ec53810092aeb0f9185af6ac114c095cfd8e4de107190bcd00c4f1b53be23d8868fbd16973752160db0c4021c500cc6485b58c5718c3ff58e3f7ad61a7cd3d1054aa3e1616f8329c672640b6c21aeec5c04208445f7be58e11ad8cd4b33c77a4fe8fd51122bde0801c8bb40806e5481d0cd73c41c11d71ab4aa7b690b8210525f1b1509a5a01efb297334a6c3995380d29715d17540675f288117064a644a7336e37f302246c1e8c536e00e1346a06c532a1bed3d29351aa2bcc7d8e62f1ef6a2bc7423e1c99ca48eb88acb555c38d273e20f7944a8e4e41a3f8a1abcb392015f751514397a50cce2b3a9097c60d971eec269a033c97ea2606ab2fc3ab299b5fafb2aba72c533fa87a89e5148c6572996ea39229da3b9312fb83517da3b06384c0525b2a1ad46a122d9cd9fa0568e1e2eec75d2c35263e2857e43fc3d712681879584c6350dd0cf503bd0f347078c8fac05856646b6059f058e741316f7edbb171be65ace011e30c297848a262c30f62e5a2da016c99ec101e878760fdda497dd9b16907222dd97e4fddd2c7d3a2a5575da949b5063365d0085cadd25209a07e574ca8b790ad2ab1445ae8578aead028924b19e40b98df893a615ca6a0721ad3df00335e44bad441e2ec87b2c2119a4ee2d555c4d7c4e6500e9990d514c9daa1cee1594dccf8a78e72b954899eaf46ea2ebcc0992832ee01ddc9ccae964313b57787c74b484740f5722b27a4bd0df560be3bd9b745d08e5f25745744fbd9ee2bb658168688e283c4e365301bb529f0513f064676d321debe991b069cfd82667a56746fa1bbf90f3f3b184fcd119672be6f5472c3a7622bdbdb59b3412e223278b1fa5d44219ee9812106807c6fa5d92a4a5ebc828530b87525c2772aca84370a140386484f8ce112ebd650a9dab52c78f231d758e6e836c3ed5b4271710ecc5d5fd55ce2291bcc0e6151dbd003aa8d308810dce7b46668692dc845a7e03c491bf3fb5c94a6de939981e86baa15b3bc23e5ca6e2080539c3b9fd7450a6bd33ce23995313fc7418453c8f3a95c277387dd2a1b1358c90392a9d056e10f18cd59f74dfce3a8873370796079ea1aeee201633e3bea90e6ddaf92623b9c1602fdf6568d8dc15624778e2efb0ab939609d6e7065489d2a64b9cb51a1e260ae9c0e82704250a26377477697428f276466474448a6fa8e98b061b758d31f02136c18ceea5cc26b5822d406ac1a10dfee111455a25823541a40e2f9ad91dee9612a10d08665d6a61e72b6e6a1dc2ba542e7f2cef6bd11a7a817b9608ef0c82100615291cc87149102f875645e26e5664bb9875a3add62373da9a741f0f874b337979e3046aeb97a89fc84caadabb2648919e6664983ee28c19029e70560c971aa133502549272316f4135295acd0ea43a5783a4b50a22328aa99212c330f48958a651c8cd0597b521dbfcbc1d157aa490f35ae1333f14ad62a0f9d67b52e312225092f39bb84643b6bd3fb8dc6534a94260a07879cb585dea8edbb7a8dd9adf76d9dc05cb532ab8118abd01e1fc4a015963e61b5cd99c05fdb60973e947fb50c9708f3f86e69c36a1973fe650214f1c32e815a72de0e939b47065d4585466b772be5cc9708cb55d31ef2aba2a7aff123fa059ce7f6e06a0ebc38fbd657f82a9505615d2c6b2f8286cffb29b7446b7cd840be21418005544b863756eb5e76ad412b4e5c2b579b2bf9beb293346bdff79325ab47b2ce0315cb8f788208ee22852c137c715ba92271cd0341505e716a41bbb3f8cc94e60a6192984339acf97eec9bd6f37ee54f1c4c01fbc8cd97cab12603ef679aadecfa99864f71933e867c006f4fd791a04473a157c5ec8fec0e59cb88cb96a4fd2d5698d3a349b635ec9a1c8cc394fde30a6aa5638b857a2fb6c8b47b123c1d4579644862c7832235d87bf68d0233f1cfc89d957783a22c4fe3f1d429db9cc6acec798d7b957fc6b1526b64c7a64ade1b07a89e608eb5ddd8fd504574bc81bc4e6d84e390b6b7ed8599ca155ad3c689ceb880aa16aa09a87e26e3f6e846f800178d1f001845f532970d36fad421cc7994e0b305800c2d6d161dd3f4a9d40f146e823ef21d12f7186c03cdf24da36ddd15d12923f7d42fd8e4591131cacdfbdb3628e748d9abc6c1a771c054fe2d225b41ad2af1b85b62761e238a919da3a7122ab21388fbbde85d626c72d13848bec526acda98c4d336b2672419688387a25a89eaecfa7296abf9595edec91588ecf975d2607ac83090ddc7a0b1677002e1c53c05e93537d0851dd5077980ca104c3e6a18e247b2338d280d8bcc3140b32e08dc6b75231cf32d29a481c8a5f53cac027215d003ea19a824befd0441215eb577cd374a6c3b0bf381d416fa3a06c0d7d0f978a3acc5b358cc32f917098820079383ad4065c74fcd400ed64a6f810e2e83a5ea1270f8c60754ce2f1989900a94cc418be5ed2398ddf079387580d71a54854dc57113ae033a6340db212e2976bf2dcd5e4c0d086f9f1560991b2a39a600569cb692b34c0f950c1be5d0e8d403987098cfd9ef13325492af6600f845bcf6568a8a111e0e75a937c0c0ff3ffb174b3d0d7629cb242a39a0d14018058682d1c284f611f82fb8235e5d20b478774fe0f04db0861520ec2b4f0c12ba3d1933b1f6b2173fede38afa8cdd5ce2e3570c9365f75f10f665d0bfb8adb7ab7924e217d262e949345dd88aa539751fdf4afef12f610eff0f3533726c37f8cd1b0011596388569f4b11d59e22d766d09e79e004ddc2813097f89817003094d2fa069c1958d06bdb0614c80fa78e297df0986e83cfccc02354109eef2243bdb6211044fda787a8cc8b5e07a00e81ba3cfe26930b9a8f792ea4dfb861f1987a35370eb4fa91fb0653264c01dd10efbd37cc47cded57fb003105eaee7fd7289f2f8269b2ede0422f2344e8dcfe31ad83101972037d04a0c730b1da0138287b9f590dbb51a3718ad0d7e36f38f7a087305461f14d7908e91faf9dd82186da6326ca19d4293e3dca2766b4cc87f4d8511609f545a6504742ed9581c7e7e937f11822a548f93a51b9294d7a816baf542fa4774737f6dae9599c4fafd60d7dbcb3d74e4fc18aa9ce7764f17cdf36d6d1ef980ae540303b79e04df084e9caf1a5b1f41f6df25b868041babcc4bbddcdfca1e0c4421cb91c238ea4d550a53b66062e53cfb7329c9c3bd1be94cd933bc2482dd710bb198019cac0807f9edef0011c4776ae1cd559fd6656ca35bc8b3f3f2b12b6af27a9dae08e3dd2fe3062ee89e21196cc057de5b23155e681f8869e45e5d4bd459bb2636f3e1f78867ff5d49ce11944f1e271e7c4a8365600be125490f499974dbf70c0cc22014dff9194a3b79a460a38f6bfc7f5e9bdc1a6fee9598b6d9082bde2f9fe1637e57b33a76dee2f4e274bafbac5a8a758746819bd902eefd8dfd2695a9ac25700c4b4e4b59ae75326e6ee5c33658ce338c53932d243eb2cd3900335f1c21f71b17d639e033cefaafc23c6c0497675da3722d0157df4f005019eb0f0b4dee336b518b5d86544dd6c847daafe1eb07cd11e7ea08bdfb9b493cb75a756ead99eb14412e4b2078fed5dc3db0ad40f0b8907abdb7b9384ff0518e7fb5eb1c35d1d49af345be4a04ccc6e38049abf81fd3e6b7def674361869539318ebd122294fc074007463adfe131c4fedab51fa724dd8b58d39f098f03862c7ab32c2a1138edd48f548de2cbdbfc2d508c1cd8985ee32c80aabf16ec4b395a740cf4a9f68ff4422e582fb8118466d5b3b17b4f2d4ffca758c3beaa148c302f4e38f5ad6fce62090f43c0dc35bc49bc47b148d2fd4af41a97f3cf1aa103b6d2373dfca033c9003e2a719264835643ea59e065f679a92c1b0de900ec111cd47ac82b9f38d0d4ac1e0e37bd94adf171f7affc85e4cc5d6c616cdd5b41943ce7fcd5c241336af736cb8ee3c08d8f110c48fe3bd7fa754ae8e653b4e9fc84b48cd009bcb3cc10c921b5230f6521a1e1d5d0097d90378aa65955a1a8940960e48527701c14f632c312237de687cecf81db43be1146bd325a296e68199e3beab4542c77fb2d8268fa5c16cf5c112fa72d12bd9d69c1b49e46a2923964a04ff065311ad6d663224c578e5bf84e9b0a57309d9ae2318b9820afe66b2155c40935945947b8fc95d03dbc6c7976a3e13e7a8f6d73914527cf25aa558fc187d2fbe215a7494228737dc052517a92276d8a15297dd01b6e1ab70fea69f4b179afd287e60d92518aebbd9cc467322d0a0dd5f456c8ac3bf8d16d49581115660018b37e4f42bc949b1aa7bc59174b175ee42021495b14f8a915bf6c5e997ecb812717772ffb01edb586d407497981cd0332ea2ff3af7880b000c6531fe792d3e26f1b8ff087aab3bfd5a015f696dcdb177369d9d8dde6f77c11f97f733addb567067a1de3ae95ea7b3f9d1b50f80a33c65e206ad204e3518424254f57224d61fd356fb35e977543afd34fc0dfd1b714d6dffd91caea6a58b164aef4429d4d0ca23d1d6726ba636367170402bb00bd166b23dade742be010913f7ab1dc2d4d9d6f0db97ccb57619f2f51122110da974d037bf62e72f9a566fd1b5383a0fe29200de30c82f16b24b62f9d49a70d8a09cc3ad0672f3a6e8ef1c5648fd944e70804991c988d70ceace4f5eb0f5370f87b8c90eb0fee0b04584394d75224a41df6ef369b00d14d17d56b74ff5d979a9e68ca5904e22a661d18f0a4c11f6dded4587af58f9049a624d67e8b75891322446cd3dd8b4cb104476a1b44ec15521a9c5496fff9d6bcea8e4d04dfa16f966bcdcedc1c99b711b81df04943d9a4534d9c9c3ec76442147bca90b291c389191da5baf1270dc88772d63038bd841b52bb4b813566901533f74b539b49eaf2e683c8b6b10c1041f90b0e6f856adeb083a5f0166b3fc9302ca0839c79b04a62d4ba84c4136d4e8b82f3b83cf1e0fe329d1416f5db339a386d0f2173b4a03811953191266c63a61ef9650686bcfe30ac2614a77d4d2176a018cb095420c57caad5ab7b42c348212cd9dc8021f9e5802763751cc5eacd039cbdeb46702c8c12530e1a6f1228c811bcec2a5a5519884d17844d3951659c3ee8c0768d3c2d75238048ae45bd4d531006e094365201b09b5205f57b78d5b5255d67c66dbbb9c7705029cc9749d412a3d5ed0aea8bb24dfb90d68e6b632361c71d234f1084d2715c853142cb0b35fcb56a27375d5a8b6652f56c23caba78e812fae48059f5cd836c920b6ca5a202121edfcfca1137dbd973d41953849a071dabe01053128e5ee6cf0708b0a967f3ec69b5a64f9577e701e268a0b77cfd034cf7025ad75a8e58309c1137074d3937cd5e412def3d6e9aa391c1ae12e4e2bef951ad5247c984f214f813ba0afd819d7406173808669801100030901c88917b72b84dad3533774078d2bee39192295bf1dbda68c9cade22bbbb764b99a49401dc088508a908a4f939dc0bc4550076e8314944683bd8f8341300874a6a019334bf7487d06ad8909bea47c9435869f6fcd0db514e69aa31d2cae11fbfc2404a53f45266874414991ba4d0d541b7eb3460f95a0d2bafc02876c89e335eaa27f6fc99443455981384e6890d9baac2f884b161559334dfc665b9b298a4f052f170c396c09e6c12d174a9427b820db3c31aa3c3b33bab499a5f91b854aa1d73a9341eac1c03dfb02437591fdf9eac8c7de6f2d28d3db9a9c67869fe45754c88b7c9d54163571932c4ecf935bf97e697dc19ece6460fb307014801f8602b14329a6a4c2ccdaf3188267bf252586db04faad39eaff96063cc31daf1b5918c1157a3b1e00637743c31d40291175e985cb884ba7b8c315a62d44f470c96f8eeee31c68879213d55c3c0f10171c58d7829becafc302faad5f13a964573695967354bab4c8dd7520e6f17bbbb539a8dc6dd8a492205965faf3b672d77eb5e93d03a9bc5e86158920bc49274e8653d316747cc0b1951e2537710085456cdf6976ec0f2ddaa61891f43b47ee8ee5e659e7aad4960f94e3f4694d051987a0da5218b7921cd8079513fc618c30b934c4aa6c8e4b2c48feeee2377175d96f8d1472293110e2e38020912a868f277fdfe1205f892538ea05ad5aa37744526a1c5135a3fae64515c992b581738ce19578e084213b5eca86344919aee78e54988e3bb9ceeb5beaceeeeeed1dda92b21a9a44db8cf233724a5218954e3e4b223eb838b486714e2531a5d6a41c6af3b4cb6bfcaac79a6503da30e81a8767c9ac32b07587e7cd0524aa9534a69a49ba865531d5a60bbbe26b5824a9f9366492b13538768e323f2e46fdcf8619aec25830b891a649c31fd435e9a4ff2d2fc909b7e20800e93edfa46166694738fb6db58c2040e43fe35e6934010af9922a2517d4a7ac305175c70c10517ee5c540702fd41deb841001c7ee2ff3548243c0a856a4552adea3596ac93c6182396e40a638cd1a5eb4b4629638cb1b34dba921289a4922271e95058d2c149c258d68faf7529ec1f774cc53cb1e7be71c3daf37f0899e6caac7a34433e425c5c9dabe31e536e831db69002092dc4d82c7183924cc3174ec801155e5061095291309cf01d53377862d349011254c1450fdca002021774d8f40a5d8851240c2920426a0839d8a005dda68f83524a3dc620e808f69cb389ed083936584314aa26080100364002dc1a1785a08f8352fad84d09684209495480842a0c09c30afdd04c39d9f49f3e69a67c36fd1b3385fe0f33e5db94524aad26702806918d60fb9e130a2d723fc0b18a2ab4305362155518d9fe35c934c99912ab6802915dc1d8fef526e6a03411c5dfbf23b31d403f7b91f5f535edeb674f3f48114bf7906f351020417c1031810b884ae526aa7a686fbdd7fed23d2c7e8bb3b52cb2b22cb2b4c801d96b0f01f9561b615fbe16594688acece38af7372e0652a4c7f69ef7b8bf692017c7df9e61f9971639407bfb10d0de6a238af4c06fdf08efb587803502bffdb812de90c0fded2f2f7762ee5ecbdc7f1f6465ca1af0e4d140a40caa46e624515fae375dae38faa6e6d8d3fc50a2b4c8fa202b1f192db2341142405ff24156422f6bbe970fa4480ffbde0329d2437bfbda5fde03ffa781c81ede630d447a1ac8cdf6945469294d6525fe50e2cc2c8baccf2207d08700fd2982407d292fcbcd1644c05083cce9b4c2fdf7457a749f7d963df73db22b93e991fda781cc934f999a7d384fd944c9f8e906c74f3713a724877205e54f8e5eea6837f6e38afdd04f9e9f0fa4488fecbb0752a407f8d903f18739c0caf79c0652a407f71e0dc4f3dc771a48911edd731a8847cf2ed34026a895902b9f4ed51befe38af7e1acf1b4e8fa202bf526853fae602dba341142a2976a90958a33afc071450401b992bd5cc9defbee8bf4e01e8b1c80dffb1ef83d0da488f54050db1fd7f4c0df69208e72afe1b4bfcd7ee3257f2ddfc415ed434769da88227125741923ac16f5f8edfef6570329222242fb07c4154d43c0ca71e20b19c8e0c8e9b4e24060e6cafd23ec6f5ad423fb0fbd66fb97bc01c7159597dc8d7002204c610a494ea795b872b51147587dba3845ae408422a04ea795ecb30f5db5fd3d26700184a47684c20e3d539a2e70262282aabce47f635f9ba6bce44f0487f58666fb77622c1c37d19397fcbd83dade81d9de396db772b67fbd59559c9ae3a608838a917153a489a9232ab76cfcad1b7f6be5a6b8e24f4f6eaa375e5ac17448652e7aeae890a2b67fa427c7c95e0a705cc956e40e1da723b3230e47e020f4881944529016708d266bd2e93292aa087c477bb949c652fd8879007cab5327adf6f23a63bd7579d0d2f400a69d1d5fab568c28925624ba54144e34938e1331f4888b288d1ff21ff9ff4cf16cff217a4c1387f23f21f2038eed4ea368b44370bb4cf59f5193ae1b38e2abcc5a6bb5ae4c2fddc961b4ac6ad5aa3540f70f6bcba77e043a2fdbb900874e4835664ae82fb13ff66b92a6bfbf8bd4d8be539c0264fb5f165f16bb76125894fe9cc0ae1ff3f5968e92caa7343a5a03741e21fe15638e121972508c393ef9dad7f697dbc59f622fc5bf640e4f05adafe35d1d86334edbecbd76d3b80c63dde5753e0bac1efa71d09431cb5d62dd4b42269b6b49ac1053fd0aa0b15da60eafaa434c75188aee34a6cc0fdd6795130acd46c1d4e1ae9962b263c9d6dc7a0e6d9055ade7cff6ec4eaeb1743770dd968ef50c6eedb29ebfad61d6b3b7b5cc7aeeb6a659cf786bd67ae6b6b66d5bdbace7bb35ce7ab65bc3d6b3b6b5ce7aceb6f6d94e8e2b9eb1ad7dd6f3b535d07aee6ccd633d5b756b20eb996e2d643dfbd64aace7b9b511e8e3013fafc3dc76ad9661b14464b2b9082abd2c4e5d8740f6d447c820753a1777fc18e4053860c31b9c1843129070041cde70c44acac31342f67c27c0f6526344119dfebc2c74d39952b2e7177bbe1482889843c6945f6109445082913288210a2eac68ff1f668ab55d872fd1f3f505f61f6d2993ecf954655a24f878610c8646d8655d17384948c05859385d2668a2aa3a321842b5b0a418e2f1a2970d4e05020ae6beae0b0c652186c625a0aeebba2e1b1abfae1854615f2b58b91efc98239ee0b8432c8416472c1c82a2ee989a418d65875cb27ea40287fe4f4835e6ff09a9c64c095f4e02ba640c5c35d62ede6fda05c49e053108ba5f1f7f3cdffd3efbf835fc598c37fbf8b378c3f1bae2456bcd59859929ded7af4366caf75573b83ea7c3cde36836f85bee1e733aaca88d61864c12fe7879781a67311bebf0a5a26a57abad15e770db1ee7ed316ae35aedf6f8a5a26cb5366f8f7318029ccdbe5494d55b85d9e14b4571c086033721c07126a6005351cec4144a503d122fb16911a41cde9c7d15e7b697d13d1d76dc83cf3dd521b839bdd91cbe80db653f1eec29f9ebf35463d05fd8c371dcdf4f47b9df74f8026a9ab6bdf65d0e6dc769df732fda7359d4bdf53c51a7b5b76f5ffb8d7b2d872f9b5a8dfb8fd32fe0d634ed82ef73af326d7e0137f7026ecc5dfaf9fa1c057db4e7aca659eec2d85ed8f2c8decbe1cbf6aefd7d9ca69097b810dcb59eecaa3b93257097bddff205fffbedf368fc7d3a0441cb795c9669dacbdebea6e02fc749da3610fceff31ef4f0779edbdf7f3a7c01b7dcd91cbeecba79b99bdbe610eb4bd3386eabcf71dc5b2e7c01ad66b204be76f802ee0ddcf723cb953bcb9e864244e065d395e8051fbc600a2fe0ee80f4820f5e2085cebd2834fb5f0e7fecafc3efefdd6ffe63bb0eb7f75ec5b3e7b8ef3e3449dc6ff9b32f2ff798cb3edbaede34f79ddd9cb65f8edbcbe18bb5754efaeeaff97b30ffe9797f1d964ced7d7edef187de75087ebc3c5c7bb8d768aedbf61f7f2d7bbf3cb6a736b49be3ac167defd2bdfd4fdbefac73f6b31c76f8bf77abb9af5cf61f0ce0deb5e87b7ffa5b167d6f7f0bafdd655af4e921e216dd77c9fefb9bfdf799bee224599bc31fdebd65b71c52aa1dbfd52ef83fedf2f9ed5566975fecdede7348f58bdd58739f8f8e25fa876fede96f3a8c21a87ff8f63cd53c1ee75ee332e75ce7f6b7e73cfe147bf74e87d4b9e94f759cfed367f6987d2e63bff965c15efb9ece14cf7fdf6b5f0eade8fbbe0b71588716ece877f4fda9be1fcadb7bdf7f0908b245debbb87ff7b8a376536db50bf61996bd8a6f5cc67dbc5c963d17dabd3dcde18f4d414ff5b76d0fda3c4f7308be65b50bf759f6d9101fec69ee1e7bdedbbaa7df3d7d4cdbff6497eed3e18be53e3ea7ef6fd97b4fc6796e2f87a0eee4ecb569b91c666fb5781b10b7e8fbfbdfdbec3dcddb7799cbe18fcdfd97b7f7b4fd9b5d364e47f7b8cb91cbe1f5388747ec4cb4bd0b7dee376db20416b5d0e75edb179b7d941bd3328d6159bee7b8af1c167181f0c7fc8bc5690eac90832bb87eb1bb0352e6c00a39b082c7bf1d68f7b51cfad81a47e98659ea631dd6ef786cff9384bf66cde5bea4183f47f1638c2709bb96e3eeae5499f5e96b1147169b3e888a4d1f7c8d25939bbe0bf8f4ab0e4d3e5e06809edaa7df9768d0657bfcdbd7d7b66c69211fff60e3b7af452e0838a6a68842d3aa8db7c57bcaf1c0bff1d0fefed68938ddb2fdd5f1e9d7fb5e0eb5057cdfbde31c6e7f75c91878fbfa352bc07b2c2f03ea532dea9efe7d9c45dd6be1b5392dea744b9ca4fb9abe3af4b1e96fa1f69d21e4fe5e65e21ce2f734ddf7e95b7fb5cbf79d76b18f690e37fd83ee4f57fdf8e56db13a9ae81fbe415f1febb044d390fee11bfc0ad2a1dc2098c32336dd5e568b2bb035df9101e1fdedb1a69314fef00dead0884dadbd2cd9531ebee9a6e924e1bf5ad361fd1fbeb10e7f38a594524ab7d03efb26764bb6efe39c75af85767b4fa7e9df7bcdcb21deb4857baaf1a6b75771ee7f92e86fb9fb9aed5fcff43ebc3efa9e4c67897ebc3cb878b54bf6da675af883460660fb7e96f1c78b392d22b7058ea929846ced31de3ef6f6719a3eef7d9c268fa7c3eb93e32cd5ed6ba976406eefb71c66dfb9da05bff6da6be06ff97b30b77cf137efbb1cfac0fa7ecddd7b997b1557995c9ebbcb61a67fd0adddac69bf8120bb5e96eeb77c1fe7d0c7c6dfe596abb7fa1cce21f65c0e8f68d9f4d5e1115bfbf8836efb51ee4cd3d9abcc2ccbc77248c48e7fe570023e765c61b13e5e2a4fa7d369cb8f19f0b17ff88ed2b3e5cf209ee32be4e440ca5a02fbd3fc92a5b0479439834e683453fce9872f56cd969d98235e9ee98d3268716a48c0251a07053cb7fcb869bec2bec03b5a110530386b472ba420644b182de405961fad8ccf111740b2fae103098eb5b2b1c98234d954ca260a118506549cf69ca914864aa5accea4993253661a99355dcc9a92193351136562221a85e6101818199f29649e2669fe3cc998e18943b01443e6c81c0c9512c25723712c2d576b5871a8295229ab93521d9147666e6cbab099b1c1828ac3e1706ce818333629cc05180a6341344d9597b018a45472da4cd21533362a1b1586d2c291a94a51914292e25033a9948d118c20316293ba68666c529d8f362565623cb286a60b1a1b0cd5c529854921a7931c022361240ac80790e4d44e0526105f2e8064f5c307121ccbc6068a0810a100dfae6544a9f388d065a118ca060b2a0ca5052ca269a6b648a552b20d1c6aa626151c181c0e8763a9b0a905d8d410f1888dcb3263713856a498104d1c0e8703059b8b868ec1e170604c93a567165385a19a7021545256a7a63a99f45d7cca4dd5a6a66c523765c0386e7471e3460f73140af5200009621c53a2096b0243d9146acfb7294bc534552d541b19549b0b9fe83c11124618383bac313135688116a3c37830a04579058eaf7171d228a34fdf42973d55f3a469510b5dca5aa59452ea90fefcedf247fbaa5266a1c59390abd9136692865498e9f452cd13b63a970acb347b370e77de077a3e36be060ae19597e66b2533f2e666cf1c1c9c3d5f2bb126f69236526562221a854420191f19333c920d336c9c48e0f052b9114f09e1ab9147bc3465ca658e4cd57c2e22a112ab3d8bc66324cd9d91325e9a2f6550a1dd59dde063c47367648dac3111e1d08d64d85861622a1238d879c22717162e55fc0b8b689aa76b8b3ded699e68587905feac43137b9e685016e69aa72762623655d6364a9f12850a5d37c8c1ca2b74b01e634f1ff0e1990fde1979923a904ab0c31aa39da038c53a02922d614544c0a60213b879990130dca7b3f1414dd7759dd705cf8acfca871f800062c4c48889111316c7e6c807c202715966b207e2eea0dbc68e9fc3c5b4b782020e3f399f1c37dd7953573553bc9b79b188295e1722a95bb9095b1971d385712c4453d74554b9a9b3f152cd4cb13893e4839862d11049f341a01d762bad5bedabe33c9e116e7b46bcd567e5a64bf37d567bbee6e591954539381165becd3faead811fd8eb0218decd4c1199015bb94974452ccdd7624f6cb5853db1d55e6163885411653ece222311653e974534114526a2ccbf306e120d2196baa8e96cdc24922296d0208ac2e2cc14110d2269fe141d71c19e2225ec09b3e7698720994f1b22cafc9a3f61441ee66f1974f3478c499a9f65d0c94b13040342ed09c4d541b7d49f1c29e01949926cf9f367dccc146c48132e6488182e040c31c400c6961fce24e10c31ec49c50e473b1ca1f6fc116afe2866a6d42c8f9a98a5f92997e5ea11ca34325d96511393344f23d4895e7a4f3147dc13b4b179836f4a7e788f78c604603d5f9a0fd86581b0d6da5a96212fc9c7ae3c03f32bd677f28ca5b57f1a97c64d7896e4cf4ba39d5c9b93cb72b580c36519a1aecd083542792b920e3bec10006fe5896192e6a35c966b1351e687ce80c34fce6acf07c1cc94f0fe27e72306e804123253eadb502dcad032ecb3c62188d29471946d1a3218d53a30050dde70c5a033ca5aeb0d431876a7e666891a297cd98c813529b8200583b71d53353dd8605033736b50330327d4a8e08b9a11bcb58332b000072760700b29604b8513d6a8c0620848d841911d3e2ec30e6aa005c6764cede0053bfcdf41095620fa02c66b4083143a0003d64117b5d6ba81a1832cb620606ec7940e9a60691d1841017842f242074484a0833074c0c4450206e5b801df2fb6256c76f84028120525606dc7541125dc14f94111299ef427a4222e2822e48b2d05f8ea821218a4c211f05dc20e1fff80081076889f0944a0d8e19381c8123bc4d70753b86193a620c39f4cf18529ceb0c3bfc199c07eb6852f61fd8a5f36dd26aed966dbe6f605f4742e0b16f268a04ff75a97add770aef7b7af79f3cb23538c5fa3f4b7fa5d0ec14db78efecd61f7f1a5a48f9fd2d7a2a896ab0ac87e7b2e6f4f73f8c280aefb10ef2edfdf6e97b77875d8adfbced33b44e6547bfa5d7e01434f53ad85823e1e4c71f7dcb75d168ceb36cc6af2b67419c3977bda7dcd74eae0b69abdc7597b8c1fac1f8ffb3a766e0b96c5af5df7f176af32b52ebbd8cfbeab1ec6184facc3eda30eb98fafe22fe00bb8bbbfb90372dbf72ecb5c97b71d5c6ce9defb78efe3cc7dcd9f0e5f62dec1cddb8259bd7dd6cd5db3bfba8f93747157352176acf4298883eb5e0fca6296619f957c16faccf319989960261e0fa85d2eff12509661210f8685de3fe4f18072d84449c8ba2c5768c62c799f2e6314fabed3be5c5f9b9a46ad96b7ff767ce0dedcb588d33abed77ec37f7f07ce326cf23bb0f758ebd644b760ef79cca339f58f7701e0739d6feffea0120560df7dbd0cd83cdf65917ff69de7895cebf0bca7c397ed596bf1c5d645347e87e84dbecf3dffd1d02da2377991e74db20dd0ee1dc8dee1d1e1cbd6321dbeecceeb1d42624fbb1cd2c774c918d85fe301be6b17f039ed027a7f2f872fe0cea0cf2879fa2e63142a799519ca9ce69a6b3a742fcbf5ee9b7665d8bc3cb29dfd2cf9f0db9eef621056f230dfe3793a5342eff96ba698bce7ed4c01bde771cc01bee7ed7bf2bdef3a7cd9f7b5a9695aa4bda8a57bee7774cf05d19efb1dda73d1cbdd157de87d13bd6b5ad4920139c606df358e285487f4b5774df50b1864db3cb7cab43208fad3f3fe26394ed25762f7f4d0dc451ebefff607eeefbfd742994ed2f7a06c338e28df534a5ffb1d54c788a2b7b6fdfd1d9bbe3ac8f6ace5d0bffb90f7f1722a33f43de7e916d0fb83be77fd512d6a01df7f03ae6344094ff6f7a0c6f8fd7760d7e0247df3ba2d2aae5d565e1e970e1f7b6c675a96b51dd9e3f7ebb99d47b7648f3fc3f772d7a5edf62a8e617f6994ebe2245dfac92ade79cd33ed723d865d8fe9f088ee8b5abeef7ec7d7e91851f007b9dffd8efb5d77f1376ff637cb3176bc9ed765bae53efdab75578b5ae8dfdfc0d538a2743a6a570b42e565b9b47d2e6befc9ddd3bc69e0c67e871c63b3705ac4fd0e4fc788e2fdd531a27c1f64735ad4c2fdfd1ddcdf0fb2690e2f2e7fd9cbd62f7eed8ab06eb9af717a039cc611457baa7144b91f64c7cbe91ddbb53a3c62631f351dbe8041e4be28a6af2bcb970850c7d8113208e8d90a456ced350a8218d393bd5d172e62fb6e8849aabfd14a2badb47ef864573a4b272f272f271ee8d4ef63ed66cbb395fb7a7974affd7d9ac3934d37bad18d6e74ebb446b7ff7278c1d7de37fc94e6fa15c1e12ce2be7b7c5d18dfed763a2c222c62d7bb811fa86917efe69793bd7d3dd9a0e7e9922d04f2783cf8b7d7360fde366df3e04ddb8a18d97df23ded6afc40804d879824fadde7fd9f7aaa0e4f3e9f3e99a497135cc4c9efed771ded3ada1b18fa726857cfe7e3f91edc3e9cf7797ecbe07b720ba8bf223e4fbf9c14f172725f4e823411c4481138ccd085232738ccd0052b3ef7b8e0c11cfad8a0e7711cf79bf62ef83d2e83214c297eef823fe8b59f8fc882ba37b93ef8b50ab2dad35b759801bc3b20da1b089243bcb1e75d2d02dfc5fbfba0beef61af7bf062ece5d0c7f65e5e06e0a75a04be08fcfbdc7ba01e226a0f6a0fbea6c1fbf7e6d0070dbdaefe57bbbfdaa57b50bb803cae7618c48144f6eb73269e8ffd7cb80fe7711fcee33cefaf690a75efe1efebb4c912f8b9dfb60fe5ea794fc15ab91ce2d0c72ef9ed2bf82539ce12a8c36b73dc8754dcfb7ccda107947dcdde779fff38efeb7b5fc1bf0fca2ea0deb6e7f2f79f2f871eefaf07fef6e0dffc7dcddc7bdcd74fc7dfb20ba7bdefbc4ec4bd4b7dfc5c7dfc55e3ab7fd0f7f827a3c5134dc34fbba72d610870365cb34675586b5771add66ef7fb4017f4fee3f9f0d7c7e0a55d7dcc637bedf1db4eb76c5aa38f9fe28c9ff37e7bc7da5aeeab7daa45dbd3c75fb368eb449b6ed15e0b01cea6df69d87b95c9051f3f248fecf85d0c4256217998396cdc245772256df6942b5945288fe4b0392e0bfd1c7707ddfe391cc714fa118b417464240ff3a3b6ab901d4a3054383232080986c8c3fcf9850e3b35702d577e130401a0b03131330704b051805a821dcad5a90c275fa90a6000ac07113187e738800d151f4285767eb8410b204401ae4a6adb58912bb922d5b833bf83cab62c76c80ee5aa2323863d6d54aa283a32ddc5039b85b599241502107059a810f48a36a674de8ae101cd520c9354e3b2b8962b271020840eeb6df6b4377bca150e1adb8a8182ca4df2ba3096ab1a5787b5e50a8ad50e770663161d3a76a876d818429641aee417d16455518586ad7262086b83071bdec9a238606e6e56339b21eb4bf9f51473f8cbbca7a42faf18c2d81598ea0b587e586532191a1a1c4e0090830e31d403168c8006224802091224489020298185fdc98c1faf8c5e96eb02b1ab339aa419fa21df38e122934e02ede427a5b54a974a4fe69ce2073f901209cbb24a20234aa5524e190324961a54f49a5292dc84030709470fb06faea7d3288560d0ad8a6f8c485e9a38bc349fe4262166697e146e0060bb56b994c001806d59a3d01370487213014280635f57b43a96caba41cdc21347aa2002121b648142811380e1850c1641a02255638a2431a2d4d8618c3146771dbabbef20778c31003b7477a7a51a6374235e908c5c568cd18d78a18391ab63c48b1c8cb8538ce6d2001053da1bb07c3fc1618731c66869ad1a4b926404fe8e275ef30edddd65622c4a5121a5d4eb6f16bb820637638731c6e81d96e175a1bbfb07965c5ea834f401c5187a82257efc943c1172f71230461359533156f7e2f324d5b2648cba1932b08c28947b1aee4e6536991dbdf0618c317a16b3e37b925b63dcc38af202ccb6daba17703801400e3a58b4c4815446139fe45ea825eb5ea8b4521a008b4901cb77f9d58ad1ba81a3c60e13a5d6903650dcdd064a8d1d025022e99003004e70d0f969cc906163003fa8dd0e3008a1743e81fde54f77a7fac5e512a6c728e394d137eaee33029306a41d5fd24827293a7605963faf1a82ea5ed41a96f82eabadd4ba56906d014b18e07a992c1bbdb4038b28f2710e37652bb34849e8e30199709d4f6479ddad19364d428a6c4f1f4f3c9a2960a733f3fd9667b8b73eceeb6ac69f1cfbddced7e95c33260f9a701706fa7840136bada73f62ccd2d43ad85ace7218e8b395b4af6379b762a00fde3a11673b5fe7fb3a35cb33dc6f79c68433e1f2270767908c563b9f9565d93d978733f9449806fa74ac9c79ddad977b368bb1925ab5ebbd889685366dd3ec07037d2c6722ca3c255fc7c2bceed68fbdf0a681a45a37d07e409c89c86e60367a2d6b40bef6342bf12ba111683f20ce4464658c64bc280b11fdd3f819986f3654f2f1809ec60cce44e475d7b1cd6a19a36a7d1d6badb6619d4eb5ac9a595054cbaa56ad49585f2d28aa6555cf66b33c13fa2dcf943c16fa4e16d2a139091fa7d3e9b45272cd9031fa441dcff6b18e812cafbb170e6925a0219354378f750cf4e136ed566b6da007cc33a1121b0a6ddaadd6da387b61a08f07fc3a9666dff2ba5bad8537ee566b715bb5b56ab75acb6698b519a66579a6f358271ea9c127e326908d9726e866cf0f3f1a9c4f8e8c285eaa9655ab65d56a59b55a56ad9655b1207406e91c21a7f60122d3091a323775a9b82969a7135803c3ed6564c7cf6a44d9133b7ef6829812ca64dcf4f9f8198d9b4c3e7e161373801ff315db058bc28e8f3921a65c0053c51cdfc7c78ec41c9e7864c7af46dc14fa08aad991e2245187ecf8d50431a59ec0104d15c60c3b8c1dffb4e357548d7193f7f1ab8c9b4a3ec617c2cd8e548b1d9f5e114dd58a589a1f522544534da5d4901a61c7a7438829f5e92ae6e83e3e55c51cf8e3d354cc6124e6a01f710829d279257ea5c81592b6c76cdc5ffb68af297b693b9d8f07bb97255e9a4b64c6fbea7d4de20a2db6b0fae44cd249bb2c331ea745341185885fcf2af6a08559f23c65ccac962508a4c2a26241e5d5783ee868b3ab9fbd3c36ba3c637b76ccc6da567f724061782b9fd25be56c99f4b1db96e32ec1bee4b2cc80be7636dd169fd5e70bcd338a9940ccbf247b69decb229c95976e22cafc6e8b243345b485540c4a0a9917c4d2fc26541049f36d4057ece9e101ea93e325fde1928c41d9e36ccc724b9167cdb26a8c75813a790604fa0a230490b73a79d963bc243519a18e3f46e3351e5cc045c6cb9f1cd0e969d0f0809906ea350d1d8268478722225b73c2a743108d6357bd367875d9c46c9a9022f695f895cce25834dcd46542edbae86c3031ccd27c8d86a57159bc2cac9d4e604d35495e16933404879e6acf9c2f8bfd8c69cc535d4cd081ad46286c4503157b98ef834b5f28408d6917328100c184563544051c7a2b4f68cb4b25351a3468d8c0fbed7f6762f2366b40befdd04cf15493e4611149f3bbd5055c62cae70bcfc83ddd242269fe85b934d78a9b52617161ae0d6633424dd25cc2e2581c0f0b95278b64a2f8e47846b0d7b29d2ec09e3d91e84bb29092192fe347d8cab5abe6d1499493453836a290b462872229f6fc30eff99fc7e5021167cf9f913f63c41ee6cbc81f37441fe68ff2278c08c47c5006c56410ca4ba04f4e8c396c3ca0ae94e84f4eec610a2314b6726c758962668488e06ad178497a0c4a9bc8d072b0d309fc17d3d7465b4df32cda1a00ac67139bb177f28ca52fcbaa54cb32ecdecbe289a17a38933457d69a812500ab811f2fcb2775063a1b7bcd4627cf7c3e6f6521d62b71ad0851e25784288157462f7a93afa799f2594d521231e5f34524cd1fa1bc5469dcd4adbc54ad88262f09b1a62ecdac58c4943b4524cdefd411aac690db61a5a934a37cbb883dcc17e58b24fa30df245f2d2210f3bd7c6ff25dd9501c375d1cb7d9d786e2ecf9f23aa0b362e96b137b981f5282095b796982dfcfffb4c1f3f33f61c4203a1bc943cc813de8893d3f4e50137b8286ec9c8832fff38518ab99220a2392e6dba46a22cafc11ca4d222d6249a4842444235bc4833853445244d29d82c89ebf85110a0c3823948a73c28a46267f72bc34df66d06992404260bc04427d72686caa4534a125e0f0da5c5f960f46146cf5c496a1ab60cb1f6427225bbe6b8f62cbbfb4b3a0cbc29e1fca6ec44bf24139b5e56b3ef8e0f520001f7cf8c1e2fc70592c1843706871f6fc6bdb98b13f7de599ec434456a36c57b187f9f73453bc9a494a22a6783e88a4f9ddca33e256449387855802dd9b58a8844a1a4b8316754cd18c400200006316000020100c8945e3e1445002dd07140011739c565846964ae38120884114c6400c638c3186106208328a285353430400d02b866debfa0f231168d8db4d8b8531c165fd36c6785b90349962c9f67f2e3aef7fddfff3c9aab03f2db33978ba689278db7c0ce518bc9c02b2acc313861f6b50b408932419fe023ee62c8ffbd46931fd8241a018f6b546f398302fa77701d9048549e68cfc51281a752705699a2b7839ba5e8fe7cbf6ba01c52a63ebf9c22fffe27f04986d20699d2568ac23b170fd9107704026e9814192b16d4ef2696067462d1a8181aebc4476d33a0e8dea9a59bcc5745275dc1a13c724e37066c2ee793c11e2bc4d792474bed7db019b5a82bb33b2b4ebc5f85c82c1640609c7780eb48e9df8d53f66e8756cda879f030bac679dfb00cf707ea2d41e8803c00727c03673b94dd2e413fccc82f892547a98ea9951df9c8e4901aed55e47b9687408385dfa8a0d2d0ef4a9e7ff09e8d9f580a7cce2029abc9823736a16f91cf2858d990f80f41457929f8ddddfd6e94daada357615bf4b66541e5bd03c7de0a421954b7667165ab3850ed90a42cd54ab0e0108d6fbb4b911a7b748104c721f4b87a5553abd3f997f32e970818b3dd84cdaa611f0377f3c879d02de04d38521f6a6ea1d8010786a800c97c6d8c9d92140ac7e4b7f4821b051d4c038028c8b1c6ef5a80e5a1c17d4dee852d178b55bd79bcfdff4b6b4b2f6f42fd2573aa645d026897d8ff1cfd00c709529b1a4bb7d3dd2c13006c52e1b93265923f7b0574c241c48a4df99b23134a3770f78bf6a88d7e0980926e5de01319e61a267c4500e38cbf4fc6b073b26bd91c2b249aab0d8d92feca4c046809cf37ba81bec1661fa1ab12bd3c0cdb9d2606e0601b8719c3e9b12cd7b054edc148884a99fb34922c6c46e4a59e161fb044c6f77413eecec15d7c61b231dfe38b3161745f6268740baa0501cbeac29b69f09a8850696a52d021d3c99e37ce245df64755ba2a8d8063062d4643664863a62b81d00654339753184676bae3b463de480f1592b3d743ed56eb14f82e53d07726eaa751079bc7a8d117519bae6b44067d60242c41eb574c6b9c7911677cfd2d2fb05f2ebc1e0801e0207d2e4bc2bdf6eb43d9376f2416399dc71b6338d6fe6375158f10a19fed6cd49277120b6c8eeaa2315d626642d21a014ba9f0ea3d0b23a1a616174c812843ed5a749682565d0c97e7a5955498550dcacb4f2b5c458ef16a741ede1de0d046d79daf6e1de799cbea3964ae1d7a70a54c6ca98b4e57095f479b6a6e333d033a6dbdd4e45b216d461d60ca5e59f74e3a7daec5b4048271f8008ffd9b4549e9ca68d3dd679ee1b6422ae53591300c94897947550e4eec1f6ba7928b1c90fb97b1adff01b812ff1fd244d94b43b974faad5db75ed7b292caaa106550fde9ebb73ecd9d95043d9cd2c1aa0f732cdc327e99c7a0c64808c216f754e397e9af04e5f979d82bafb433c42b9d0e80218e8e7e39334b6f7219486e24b99a728a797f149a0ef263562bcee152ab78b6c7260bf9ad982e50913c6d1a870b25f85fc2eb9303998acf4ca8491ef08ee4caab2c1b7174e7f643f08b173150d6a22f1dca6127dfda7213e4e79e783e07f3b19c41e30a18501eebdc39a639718e84933b48d0c54754dd0c286195a0fce6862379da34dc2f64a820b9407e7d9c2ceecd4611417bace016805c3d31ee807b7624af57a3828408ac4768987c8815f48b3e0a16a3c2ece64880a43c8d0968fc56307fb5914003ab05a233647c506c84fb85dfea8803c83775e0a94b7483c180e8cd8a54352839b60c66c2aecc0315fd3041575048e0aaecd28576e916d92e0d02d570b10184b8b4ad00f32d581b48310310ab83d33c21d0c77902c5c97c70b1d917d9c70f2c8c8e7e0d9fda24710125daa7461a0ea432b398e2af9c755c0a62f74c591af50b06542d1a7910ea84729b011a537ca1855380fb66337592381f2fe3f0390fef08dc20dd15c635690820b2225f3fa2de346bb3e9a60af20344df92db77dfbc72f524de2bb3f2c1f91a7962724eceed83bb9c1a20993d5b42b5fa166002c546be1b74c1e2ebb88605c7d1a3f4bbcddd9c7b58f174b206315b68a778bc88cc049c28014cb46140ba1b728117034b9277bee29eb12c4e6387d450508c0a9856515c286658163729d9d90acc93426e20ed8d3f195c604bdc9681560c6d3b061a3b5bfce8f90d3941800a1681cb2d6137fc0532dd9f8fe096a268fe91cff5c6e7d11c2906382e92ff822737eeb419a3f2c1a93163b36d800e923638101c8b42eaee35c95e4707500d69ec57befd82625527252a551087bafc0d4f58741873c262d376b35d87b5831f8ad1c6259fd1c48145bf153cce12e2fc2d01e5ae19c20ff1d79ff933ac4b23c1eaf6ee60128d11eb269ba0c68586391dba6a28cf86a809d857b1a82175866240b80982b150196e8cff86a0f6b3d675c4519d6cdd8a023f7ff8c149c0872605519247bdf0b40a0a8dfcf4d50178678ac300e066754452be32ab007c5ac4ed5691060f224e375dc2cd3ec05f67fa16d77875593cc69c7ac770794629c9dca60c53bd4cb5ba968aa42e5788ab4f1502e886fd1fb536845a966adedcbf5d9192838b91c8605713bcbfb39fa21f2aa4273429010b07bc48a83325247514c27fd1dbf12683e57fafa0f97f099a6aab49c6c85d9469e4971a9a34ea83dc55444adc9bca073e6081cf5b2e36e901ad49310957d37eb494c71bee5c0f060e1af6172032855de0f9bb1d3919420da69269ccf2a6025738232cca540598a88fc1abf6c0f295550a2b8b1308bcbe11e2b1561eb349a974f11bb18e608ec859b9ea447993f4f9409b5ac5139cf942bb9a8ecc60b12bfa69d32da8884d53a18d9c7f17b2425c29f66f922e15fec5d638e62fa9b293b59464e6c32d252ce1bd9189840c38b76e3de5d2eef8b4d7fb5870f27496e9a2ce859cf7683b21c9298c85b44a936e7a1b12ba5f625150b52d3e54961ee8e6aee7852611e8d0cff047fc1f0934372bb4219d976a44e7466e14722e5103ef758be00acf6f951d7e31ab87d508506ae51821d7cf508c1721f057bed55dbc12d622b55e847ed6fde7562c3cebdddc83bd15d01b2250479a3601003239cde28c51998f07841ef1cc86475cb18fc678796804118750c40a20113e389d888789a4551e4e1a59da6f506e755cc67631d3019ce4a956d202581b00bc62a383d8bea96aba697c120607ab620c3861635ac2c24516a45034351099f52fbcae550414b21d1127dc45a2eba98d5984ed069b906c6780e4585298142974577e94a9f924cd074545ad44d14093038e9a239630c086085f9b4647ce28950226a031ed5002a3cd71c922c196452a853b328b116c5f70c72e207b8ca63f9dd20fdd9b96344f42674c695511605a2842b7640e332c8b4a805e1e18a498067c3112850bb544a9450c0050b755c01c6e03e28a1f4bf052f6e0cd25a1bc5bc36de95014474506733c9925ec1f52c2844d2959d496b69a087d958bfc6810aa2702ebd58bdd4412bb93c8b042e7e49ae620c4d81299e4f0901c144b70281e1cafd271b07ad9baa23791daf9507373ea68beda016920be44960a53fbe9883f127d3e28245ef26280a13200cde3600cc3d251288821960132fb0223802f84fb5f2d7b8aee10ae936235697e216d0cf208c272859f0a07540c822f41a158359314f6701c1fdda42c79392f8b0500031ef408c6bacf111ff53a7a71720e47c74e2e6f3843ca0f47a36833eba2bf5901291b9a12780c20dae8351f7ad13a118c84dd3a8d4397cbb52f4bb384c7ad192165a2a5e1ac1403950094480c10780f3111de8c48a97e3a129ea0545d339b4b0e9a5d087428bfb97af475cf0ca7dba1f071068868d906873551fa197464ff5e799f0b1117e8f7699e016e36767a2a54d1f6eb106604f067bc764094e703392614eac1a807142ac328003a7964ec77901055ec5634972844e1eb5ababcb78be42cd2a688ef8d795ad1bb19d80615bdabae0558515668bcf769ab243d9d704bb19bbdfb12201e76d66dab9c715ab1486e1aae81536ca5df48da6d0fd7f59c1821b6b3f11b140bb435d4c8b10162ad2690ac511f22532bcea148cb968f9a01ab84bfc158ae46c0fbe43d31f991c95e0c8b493e5688418c74d64814709496d4deed75f1c6adc5797296ce1bf40b82d40120a69775359acfe06026bc3a0e1cb46ac34d93a133d71b01c0efcae921a7eed709905d37689cc3972078d6c6780eb7800518025fb7bef0a11f4ffc5107486687ce04a83fc8d476e909251d26dd2a514bd8f0660cd003868ff18e1725c002476876741818d8dcf1b1130912b36d29889ba9f35f3bb4f113c59b8ddbd4f94a8c577cfa048d3f75415c20c094860ac20b9d4589bbd8405fac27e9d5c1ef09fddedcf849f7ed109fe8491651487253e331c925539292e49794738d2f4a453f49b96467ed687fd7d8a8ef1f613baed1bd78f798bab90414967517c104c783e82edb2c67b44760c8fa50ae69463a01256595d3c39120869f57668c7344cfa43288be82cb72fcf75cfb3c21de0ab5fe9d8834e6b7907eb2bba9c8c66ef1ddd40976196bf678e932b0425f74044264cc35b8a95fcaa8b94548f4134dc03d2d5f0616f828bdd002d29d74e6907cf32e528b60f7be16455083ff859420aae5d55346ac9b25b1541cc433549f26b7ef1d94697288d3466912b8ebc02268db2237738ec2ec90667ea5c57880f2572859ea21cc714f344dddd6252fd62993f7ef295f2599796955dac92a7b66c5592fad8bbc8e2ca438a1231c4486d0957ff3e9435c342a059d840dad29c632ad1f838a9fe8f66e50798cc329fdb75fa932517bb88b50bbd48c19939dc2f8ec0563e513f95e93f544e2b691b433865dae146447a50bc90aa46d0c7dc6af6c9a1d964cb889744025fa9cb932f518c25e04f2cb15b10db9df672c33f6235ae02b2eb4be06f7880ee4c13912822a6f32118c6b99fad217223028a5bd5ee994baead0375863a520cf20c41ee02857a083c0f06fe86a058d153f0ab504b4da0e43885eb443ab69575bb97998a59d4d3cc87503d5ddfdc53c3817cacd91656a63aa598ffdd459393cdb2564d70760220554fbf17c5794391e758149b93a7523b91d6a6996495a6b414f289e9812a8955b624b44c92fd473a81edef67228a698e1aa3a91218ca84e800c876d02694d7040d190ac4e43f400c5e856aabef4c8972a0682b78af1c71821225450d3637028083424fafd95c1f3534ff380b2327320f6319c263b116bdf94980e4177d1506675a8607242075f70516560b969308cdf413d4235956cd3d65029dd5178f7071257f4a7bbd748c9bbbff3944bad0a14b627ab7a235e39e15eeb6e56e82ae5b30a29711d37f41cf792be471baed7ba2bb9b8b5424a0eca25d12b6e0df781d2d472491f0d80162519f62743a2faca646c77f899641d114e255b7193f0e544b1e108505f196aa1f7a734572f426187c1a4c8d0af58c877d36a357b61364a50cec11057556a4a93063a76fe21ceb983eda4a14add6cff562c52df57ed60ef3b49949ca2d9351ae483e893aadd2e4099a44f144ac3b25a7d231b356ff11ff9c08a527e6b42a22f9f57db7028ff35621758845f7c4275f3bd816ff468d0f6f5cd8161fa853ba0148989f17991922ea782367f0325361ce1298a908a80581aef162110b3e3702ac690c86a61f2cc267175ca9a7a341b3cc243b98de1a121463392d2a5d275b0e59d06e58b7f09b85d90f43919b9aec020d3878f31bbe640cf6b902563769cd7803a99e52ca879efdaa5cf8077cf569e38a9ba9f09429099a010eddaaaea663e23c05e02583034ddc0e10d349b05da3767980d15beb33a22f74e8532de71456e2d582bcf259db1c1b6fead0248ab2ac7139aab17c547b121ce2a591b6ec01d0a6e30e164e739b2d9ee6adcbbf7c3bf64878bd93204e95a2d7fab074455334bb907a9f2502986fca04dba434ee742e5f1812727daa90e689168ed2eb2d647e6f0dfc551f7ec0e6d3e096cb8164bd25ff11c7ea92a07de5a50efde429ed8a0bf340d2e78105183f833d67ca4e6437d146ce5c84e0b28a1f07a0180b7c0a61e6088e5af3f70c41bdb23e34fbb4c746a4d05e0a211b96b8ab1984fa0722262db030a7f92d66779ff1ca499932a820793736a0d81c64e27fcfdef2be7fd8f07923ae57008592c82b7005b84477030c1666649564ea1c5263d92eb449bd15d222d16457bed9f96db2ef5327a214a2380b17e98f02271465a630630441dae554a1aea6bec959f680f4656ca79aee431ed584f08637fd5b687394b054a34b3c489da0564ef4a23b70425b400c8277371a2d1406e202a5dd2c48b6508a733655bbce974de5cb2ca7e5ee9c23fdb2bb6c8933d097d1a532d1aea8fe987d778421ba1424e6131a08633f1dcda65ca17cb39ab5d3d69a8f3a1009945ea168c3aaeff3c91305415fd14b7378e32bddeb13c79f44025ae9b6eb75c62aca40e86ff13fe0bf5214c9a14345f117f252cc9695617adcf3a030e478a50c5ff5ecb47a36f1c2e91a52c4d8efc1a6fee05ca718f41b6e20713f9578c5bee0c6d3870f16139a95958100e2e562f7c47c4dcf4955fac8b106b42029df69a69c567981e7a789a671b7524494542316a497b9dad6480420b7411c7052e02fb56059025ebf9182952d9c1671ef9dd6bfcbb64380dfd2028a89d9648fe6894d63bffbfcbcfe690f5ba4e7cf3a47371d7215734f42d41a4f81108b2534b0182be3d2b48cc1ba9bc47e2f6e0098a258457bf1020aac878844a1419316e6803ec6fa9f61d3710b3bd5451ae1c85994d5414949334e97271a74638730df3060ec84edd1da26452c7da44490297ad0d6bf0a65378083e2d180cf22c2ee78a2304d95aaff1f76a5540ec65f7aa5f2ad17a1390931875911d4ee3ac65661183b15092473f59621d6c7e16b7b49c064cd67ff68152509c3c3957bf1a3897559b81364c6c3e78f83a6cedcb77f339bf3d9d1c05a4f3ea2f6a800adbd308a9e876f51578dafe65a27cc0655fbdc97b54f937e84403d1f3e33e8636aec71bd2c21f7315c8c74729ac58d9bea3629cd699af74a592b144c0ddd1aeb152f9d0da3d34487824e782b27f904cc51fbfd494475f1a1e5bbbb0b281fbf9b7780ea7916199e5d13f9b0ff7a2f4432e3e40f28d427134d5a1d4bc7e09d3d05f86f01c9c1bb8c0791a92cec58171506f649195f5912fcef75ee4f92e2532cdcca95971a63c393503bf5f7a39f0cf547427823259d719bf81d46636481a6744fcb3e6387ae5859e545879de8b45c8007966380d92316ffe102f417bab38f5670caab668ab536e681b47f5f2cdde62df673f72a90ffbde3bd9b3e90e9f486b1f936e4acc906181197f00f70618735bef00de47b43c4c1963e372e2216fccde485e3d308dff786a0275449744d7e8368f6d4308612a2ba6b3d2b514d03d7e368c3dcbfd0e378217dd61b8c2ae6b17831ae2506caf25f9589d9906cedb2745653cdc43e52e775a248fcb9896f0b37b55750699db4d57cd8e74a143ac28ac783866761ca44f3df9eab0421e635f22fff00018acbedacf5f6f69c42fa5ec55d9986eb01b301d3fa763e12f0df3db596e57259b5c09f4a0235207a939e68b121032264984418b7fc516c62b517732efc16c9b9316fe3dc6068051285eea71345f09d97fe15d3616f73855adb7a9236179ad41b9bf6e0c2ab770022407c594058ed9abb5924eaec5c9b25c18d1a720ba3bb731e3b525827b616e20fcbebb43f28e3ec8072c2e4c070d184dcce6219f9c41384dcb4e5819612b1950cc13bb692d2c6f517ee5a0bd884ef168a6c72ac0221bc08c6eabdf1bd042b0f8b60ebe832111246b5ea86f0152d30fdd7ca4166fb7cf5de82f1f8389cf5e7dbd0fc966b8b597c9b8086cd5ad1e5cb01f884f88d4c5d06c0e89a039d4f765fde7dd77491bee58a585bbbb216eb0c71202727b6279ca48abb861b1290eb38ed352aa25bf2a355f25024043259be6b4cb1d67ae9498bb7c277633ba2993a4ebc5de60370551c916b368ba79d3cd046a72ffb4d3d5836683cdba751eb682d0a3a486ca8cd8c3e92dd5a2552bf440370655ce95bf217fc2d45d324114e141d3409922c25e5261e626aed2f7181a42f07c1364fe60c3795a421ba02bb7c9188f34207a1e309b9c9984b1c7f0d78dbc03bd5524be6c9ff08b43a8a619cce556c631818f3f28faca9d9bdfdbaa219ce1011941c87712f535a3ef2d65e73268812a54b3b6fe1b3c574dc3fe4419ac8ad0e04633ac31cb51c01f67f04b5f761875b0d43c88189baf1ff954bffe77b1b6500d452797d151e656964cd6f07d94671407663daa79dcdbe12d85ea7692b00914c702f81de16f8fed07bac793fd1f958236fe943f348e8061f20c712393e9f9e1ca94bb3d8802c9f4cae7102b41924b6c979af6d3404b73f552cec8589a346b1c4f5aae4a7b04999109f404fbc6b2320e006ad389e49bf2702dd6a1f35d03721cf2fffb7d9f9e551335142945bfd07029c995cd580e694d2cc5b588b1b22355938b2dd3449369554041bb25b5b57e103f819488ee3d8d0d30c208f2b63f3117dca78655560cd735f4f17a3d3c32b97d8096253a7f1a188a96a465d7a7c607b511032748c57e0a3b653070d916ee3fec3832104360c558f4a63c69aa64983ed4b7ad6a32761bb341014477eeadf81779192296bc1d8fad4e9336b8f90ab9372ed02dbe167395ec89e7179c4198bd1520cae884e22733a509acf584edb54ccd04071dead1a54275100a532f51b97e455640503bf73a979829fd408128b97c656354b9af8d23dedc78a58c61553e5dabb4b1147d4923ee7af42f5d7f06d99ede0c2a561a2ebbc06d5e132dc9c55cae4926457b78ee7eaf70f1cc1b01e60103c66f274445386981cb146ad991d7d10cc4f639f8caa429e62bd55c907a51a50804ed225f7ba6fa9b109028619754b595d959a13278a16bf2d5f0734b9049c3b809fa28d32bc1b1503c88f21b3beb6206c51b74cd2ba0479d0e4f0a45790ed0cab5ebae09a684a3448bf0a523f03e21f054b728c63b6d5d7f0f206161325a4546c0ee7d6f4cd28f749ced9e20dfac9f889eeb5b6b85b025af1aa9f3f1862a54c1580681349d7a67457bf7c354bbe4e253c0612f650b9a9275280acb58011362e87880f8605bc2c8c6d8ed62634c3395e236f4f2070b1453a8d7fb6bcdf0c43b817f0f46733b9aee7161e2c0861323b47b9cb1849d0f559731679c78092e6b0da01b4124a99d88346341d50bc415dc004df2d9e32bb4f2f60163623a0a4e02235288e2f5c256088c6849cede9394d2747890f343db1a12d56ce1d0d26f58c3cfe999e4d8aa8282f20837cdfa18ac4099b64f2bb66594f8d200fb52e81601638e6e77ec0f7bd35a1d156462009bf0d598280ffcb9987dd66f5832fcdfb3a961d3398caefacd728b43811b3ef94a5df7a06f8efff6c5f5cd4ce3fc07200ec2afb895b2b2b67e31156324e0d4873df30949c15933da6e52e09f3ec07c8647ccb8cff8e12795c5f8fd1e361654336825ed24b4e5164175d82ae68f63ee416c9e0e4c49426fc476a7a6ac81242ba88fdf912d74a94d9b95c583f959bb894216a1391a96f94f44dd18602723a9c65e7badbebe60ad3cda9339c42cf2144fb594dbf4613c74bd39dbc0dbb3b0cea603a80c7ad1d79fb9244912b2dd24ee7b1dbe013f2f4f7ff3d6ff9db09059e30fac193a984eeb98b356ec5b94558713273a8614d21d1fd26a957dcb09e0f9dae1daa88188ef144acc4207c2d3132dd2087fb9acebfb5d13e470cc956ee26172d48cc31c2fa9cc7027aa38072dd1e99e543ef6b8d86821d511e15383c87224a7b6be07c2ebf46f2d1e7dc951adf913886e5c02c288e79927331d199838b9991ef54b348f3c597d476cbb5dd79b2c915500d525629d0b928ea0900699019fa0c2d06230b88836e0f65cb8838784be2f70245592fc26e87eb44c0a61837c918179b50ee767f13e4946a49b82414b0c50de7dbd12d1a39996401f404668cdf60b67125b976a0787fd11db1acbaa9b2acc06013c9b69875dbe980dbaebbc7030ed5e4087a5924abab8fab2f4ccd8e6d44b844289b8871dc777d5ea50a1e758dff8e0bee1c52cf5bf366f02b3d12e6a2ca9ca13d309a149d139fc69107eae75629263663c583154cfcd96dd70175d75233dc1909babe510836cc79d73c930ba67a42759e758e7475d7f049417a6aefd868420b962cdb1e365a4b0252d4fd331d6f6063c78be230feb88ffa1f7fdddd9bfc8527046434240e3430ffd39559d156375be11b884c6a7cd498d883b6790bacc1b9cdae34b2fa35f2b9d204e1866c042ae1349b859d8d1b461b04b1ab87dc3189b988a4e497453e97d37eb1020e6f41487424fcbfd2f9e048535eebd6423d99c4b8b2bac39aae7822cbc345a3d252708bf28a183140ac73a1f63293c672281c8859a921122e4431ca21a27ce479adcbf55b7514feb95aa4273b416d2836e4513455af34b5f1ad792aa5d61261075d1c1f54924e0bb39be8174b41f6bf1f3a360e1ea6a94920b29211f013a2a381ef7b7ab4723b40d185ef888225aab360a4d8612b822527197e006b8be5b4fde7be7dde5e0ccb6f7dd6c508097e99d4a28a5eb04f01007314128a3610f1d12d0768abc50fd31d2c9383e699107679e4af3f2056c6395f2ce35c8a8756a288f78255637f6a575d4c24209880cf6e15634559490d7616b8e222d225dc49ac803be8f5dfc1f238bf056df82e03a952a9d5d05a12bc0bb26c4f59e5a23d32515847ed70e6cca4a3b707d09c3f3b3f61bf75e799d8b7e067aa7531b5671dc1756bed93717f205b79f85f469038892b7843aa351c596b61e06dc3010383a609ae19e1f7a01b32b31a68b61d8a5b354312b0c60b4f45c6021b3efff3150a6b99d44eead0ac2bc01e5168cf1334e3dcdce78c306ae2cea81900fa72868b9540fa7f5a367b6ee84813396d08ce50b24b050ab06a5436b96ee68872688020fd0534af7ea112ddefa24745922d89933c15c2854c1e1bba0180ad0ad9d9a74a2e52a967712c06f3af7d30199d203ba545d2bf5e5bcb869182bfe6742120bfb15805e106c987d2877ff972216cd0c49417732edf79f2081b0213b91ae330f56a41409fdaaac98626823ee798dc1ce6c4137f8a7f0b81153da1191436d80c0ba7c7c30c8e9fa536490adb33ecdc5f107a2629969b9672264235c2e4fb84c1c872d99da7f00980348ae450b1ab2992d1c6ce0262dbfa25d70abd4728efa106b1beaa4e6706b7ac0a510580078539d6b39ec8420da3eb2d2e91272d89fd03655ca88856c5c8d0a51c64909b760d127789aa43937895ad02b6a490856d915fd79903cbd6644702248cf5d6cb7cb513bd703c3dfbb76f33fb7ded519cdd9a8a886fcfe8664b53b24469b375645153b38420cd1676fc237ad9df8e9289cbb2755cc9452a69ca4a590ff9e2a9333349e15612f9d324dfea2112006d09e954c6842b5e4988ae5e93e3be3fddfd566ebc9a6b1684accd289cd4471499207b454172d56867054057bc51a75742a21455e5faeb1773fb29640fc9444ef9ba01bd5c609c7435f68c9728c2d11c36966866d08981579aa6328cbae372602852dadf2af4f1b64195db85b3489951921fff6213427294649e243133ed0d96791e6d92afc86032f6447eb286ec899a675cf74bfde423011b219a796fae6aa82e539170be887d72b81663bdd128f214124655ce638c61d3c4db2b3694c61603e050d204a68965c20352a2ae14d068e6f2c05495ee9b21a4d2640c1e5c9a7489286f6826d7d44db0e4ea5d94e481b522e82653e0e092e4d2b53392bd14f6bfd9c2312716d190507eb4386b66efda5c9ed498c14e6945f728b38b5995a7445d06e73270b81f0ea4d2515a733a75e8d6d702a85b98b81821bbe377db96bc5656e9eaf4bb5c8caed453fa6b0312837225f2dea4748904ad6ae57d2b8995ba9ff0b6c54eb0799801107d1129545e312bbba83f11e014d324461fd9fecb5dfdc3413619665220c2a4b7a9c6a5d383394f20355e39431e64e7bcd3f791680323f24a2d201baea94c5cfabba30f0abbd73750306e0381385f258461129096c95a50174cd23fc826e6405b8756e5da815c4509df0169b635c92b0a8a2631eb87ecd47122f9409acdb14bc12609e49a364dc294ba9f1195d23a662ca62e0cb4dd25626fed14eabaee9f24311972db6bf6fab8820942406dd69824eee5fac160e8341eeb7353c2898863ea287b1191cf80a176f751799dd78de3a1fd890acb427a0639d1521d097e7ecb4fd6bc152562f016f05c49d98f2571f4826ff9d08bd9302db0c3726b6b805cce5b34a99e38d786e0955f510d249eef1d1b1b4ecb903a15cf1457fa2764c404c962f346d17a24cae39d487a21d6550fc08d2805a444e93533e829dbbd3c223658a33cedaded4db4ef702d7226cd2aacd4f8b7808236e52c48b42cfe7d40e945aa21fe57f3f18bdd9e780c2474111c8a13a33b1bbf42c726dbd3c90cb6753f4b7bd28b549f8961ff0391ef8b800312c8de6455ee481b74ad090353213eedc40f522f32961deda57eba5d9914952f8069d1f39734c4e7bda0ce40a6d2a09770f4e35e44df0543b10edb8be0ed3b64acd4fd117da00049e1a895925c795c2cfe1d19460006f7bad750f33fb0f56762fb67ba06a68a57ef84d4f6672bce98b7709b2d4f20e2eaf98e8c1c01274175db56e421e76310cc508b4bef72f85c84f1210d29d12526b90123689ed2d02a73a6e77387a14e5fd2000fb7a31b0f91019c0f6b1c1b3a501af2d95250031cf00fe5bb40023c5701d47e109ed228e059a43466007839ff9b1859bc76b286817052c7993e3a7af0abf09799835186b8c18dbd5fe5d308e8cc4d9fd2c9d96b780b6da5f6b74e815c41eed0beae01aa8d8e441506a5f1ebbd0d28a7c00ea26fb0f0e95e11b2db730b186f6dcc05cd569897850b639ecd48a1d7502a4d96b2bf6b6fca9eca8ba20385d3cb2adc56a08b494ede393c5d6a944044618530c342ce51eb55454462def1dbc12884d202bfa020842d4baef71c3def27a064758c2c1ebf2b86285c672fcd7dda37f481baff566d5773d800d5854aae014294bbc84116e1f426f0440b4be368a7308a890a78a0c828c8412f1ea542853e16635187f0084fcc6fefe278adf671402c4017f3283d330edaa690f8359a448f8d8a4b29ae5a620fc7c6f20494185cda48b159ec7e6682dce1ba2606abc404ef7c5a643a72af4fe3802385e7c49bbf42e9c60a839b4757972987aa7961879c55e8be3c2a0721e8fe3007282612c194bfa7606a628b9497d22c213319335932b98eb025cb5485083de7c5496bd852cf37ba8783533bb51bc8427b555f799a28404852d149486cf44e12aa8c11eef71fd91a9c053c9bbe40b718790d48250696307827519c12457be0184c8afd51ffcca85320307c454834b55f288679f6926987032179a37c9421c13170d2d59ba0a4aef36d0348ad5e9cfdc34ea8754b9cda8305bb9dccb105f4a351d8ca179f95bee1bb53dc810bc1bf3852a1c894bbe9273a00d2b80c4a807a1d241c3acb11100c8d792dc2b4bf4650df14158e12c1e9daccb131958092ba2d77ffb2aaaff5a7eeb3e6f8f5a915889a3da403874418ac066dd422a80fb40a6523d8ac3875e23be60c624279eea2246eefb03c19b0399df0536b82253ab1c5946043c98fa1a8bbaee95849d003b0705035366543d6ae451ac3980f9e9d4455d9a34edf850f128d42032f1a605a92583a43987961f319843aa8ba799f83480e3c0c393b266e075df884be75811c87753e05974118d3d5653fa296c3c724e0498a7361905b05405a139793d359609c5d69a8c10a6f698a0725807906bcac1d6e7917f2612ff5a7316ffdc131eaf9fa4e06bceb13fa41aa84dd65d1750e84a4a85d30541a83b263f38d12d1e0856446b6be08c83d3445d075ce4107b1f25c9ecf4f422e75108f8d29cb4bf8d6c253653a5a9d9d2d073671756d2fda200d512a09f04fb71a872e01557cffd105eb86fc3c32fca07527235dd17f8dd874ad55eb1618cd2c49baf99534bba8a2248d5e252c499203101888479ae04685200563b2ca9bd72d5b06652de62588eaf57344b77a2187a7d680e98b6829944d1a4f263268aab7f2e7a877a08c0c4ffd12226fc53e43a1650891c051869f88aa8baeec1c076f3238f4cc80f4a41ad81451ed0f88166add166ecde03d92b21def265c8535b818c4af122602b02ad7053822d2a8705dd574e56161a21c97d50e48d04c3d8c036cac8a5e2448829730ab42f3fa016029929733abb2745bd800c11dc0369585374962ab78b0df032ba61d1c89848ee5915995a873f26a811e3dfd35b2925591228c076f596e745665c04947666dd986326e64632310d1364773a01ae89a9d3158155d749370edcfaa28feb3c46e3cd38111a5b0638677fa52f491e4ebbcf488c1645508dac239e24acb83c1eac058ba0a03c540bac708cb40a92934461e697aa687c956c54c10fc1c4ed0a25133a541641792e6ff6c524e2186de421482c4c2c284c422e401cbead0406b17269308288caaa8b556a5ae73e8353d9940f558e0ab0ea16d4a8a989cebf8b69b766e20e4d3bd53d10fb3e04a3282731172646da2bc275f61fa76e7eef189d4e2bb68fbf85e8ee52e5903955d58deeb94b7c1ae9006866d336e97382baff4db97adaf037a1a5cfe26116241b2855802f3306e8d5a8f72db3b346b310e16a8085e6418e2e9b470d23f2a39220c81216ffd5b011506509573b11de91a6bc418340bcfc67aaf4f31e26acf9b0933817e1c97381841dc4343e15f4041025ae38c9d877a57a3128a5125953f122a33574209efd16e46d10e795c263401d0e68831bb2018d30440a7c338f8371482815538f855219b7903fb725567a5562584348da2f258f4234ea8b019b20b973c9f47d59999385e3814cb5d42c07afc8f72a318c4867999b3d078f8dbf0b32901b2089c348e7428de73174e22d2a9ff95c5ff1d699ba6be0ad44eed4bd10c05173f63a65e32e80cd61c91405077b2ac02bef8234537971a98bd1b663f94188f7887fe59e34bca22693449a02cc26020d421c862a0d542108eeb3fd1c5060af3593d5b8c39080dd6759a08c08349be29669b56bba98ac1a27648c6fb5e98611c65bd3edaa577c99ff071caaadd384258b23131587d41600afcc3d1d4569d635a8f30c73a3983871a8f097ce9f2577bef0f4e7d1b8e4c039e13a0c211d0748ccfcf344d25d829f1e90fcaadd37a1d16f5199d4c63c0c464c7f2fba9cfe850950b490e7738a44f4d4604863281b66037047247c4046c965e06c535bbfd1eba56c02fdc8095f9f440153acb76946d2b8866e78f82ff835e2cc57504c97830f4788d30e8fa41d03c5e00badc8ee3749c16fee5469ca5f2e47a70501b1da115de3bf33baa572dc01583466e0de62c6df6c2e54e1124cdd6268164a73b1e322605c169cf71a2eb39a2d32d6de76a3abb7beb8e44339b1b224905f574e8456e4ea9737b85b7b488c60445add8b5971984fc160bf86aef94aada5168f56cc06b249005058fe002e318c365fc81820275df77336f8487e553722fa1aa2d962826730fb0c5fbb61638fd19f49dac6ceeaa33a91f559338e2f6a5484ba71b8a1af68dde94ce3f58b4ef00d513018e1f190081681a1be228a028906c54621317e08815fd621dfbd4b41166c0d8e63f18fce645f5b76c505f84b3f0b46ba11343e53df37452536bb1f8aa993de1a4ef5c610cde701d6dc65f7105fa01b3698617e55fe20dd5d2b8620bb66c488b4c1fe23e987b69019974a66762215b2542db98b3c6c3492c459a7bcff64875a9e44f0fd4fee05f53b243ff7787d6324e88b6dc858a55ba2e9f8a5388f3b22949a8f3fed3ae3dd25622117cc8cf70684f829aaf2f2eab3b56a3825dfffe013dbe263f794b3f0aec674ba816e028845a8e2283c9274b0329f1d7ad9fe3387b7157ca02d5ddcc80d881e0aa4c0d16fdd76f60357f2de6418d5ef53c491302cb93150608bb3b23c48311c84c3bbb4636b7260c9c8b0952628b3572fcb00707dbbef39a8840f46a1c908ac58dcd8158e3bc2c9353a72355211aa08a083902c64597ac4328b44009211070b530f788f7c9d53701371dd38f2c0a943f426f494e75c8e8c7cb16d44529b6d11a5a62912003516b1e236537d50bcb2cead68884be03b4bbc0218c7655d9ca28e500f41fc7d6390f749cac75619abffeb7bc0af8a6a4a7789f0b9c8d23edff268170d52736da8c300c957805e1ef5037348566c4172fb67841b239bb562915b88bd5ba6b7a7de18c6f2256ee2380500f7b8acb675f83b63275ad68154baf85fc918ea505835e765607612ad8091202ef368a4811850a9f82f9c197f1824f9de9fcbf84d14c804722e1d2a0adb43e853eeb1e260d80ddc2e1324460fdf2f05f735cb3cae420c496c0f2878ecc93c7d3d6e8348e8da81238e55da79848ffa81c058ec78c227f4262428ef81f5462dce5a796ef1a7a32d81c8bee91bad33b12e8f434c0cfe68371f8341abdcd6126b56dc8abe81801d4ad91fa793532c8294fe5524abdd6ab72ee5d7cc12ce0dafa683a10af2a7822f30ae03d9bea1252b46756baa51f5dbe47a84de1add9beb9a09acd603e48ae0304ce9dd0a75c6e4cca35733ea87ee110613b02528140c1d6520212722a88996d81c3044fd00cad381f2f5ca22550c204f4f3261ce7fb70e407bb54cb4396cbbac66208007e801236e9a653c81e4ba1d7ecb936697d7d9912d59321d0f97258737339252b1e71e179f1a2d591750b5f94da8f7d992cf149b46d66330bf992bebd99e59df7176646cecdf7fca96d597a93d0e072f16f5879752ca83039bc8c8f0878fa8a37763519bc98716b617738271d243e387a269090783713464fb4a34a65b712a9febb5c99bc66a741ca9c81a7c33ef8627c49e53c80040af0e87249335121e557bb13dcd6f82730a04c8a66b044322ca0f59b08ca342a9cd636cc6dcb43fd72e1c05308bafafac6ccf524c2af85bdd239aa351f804ec73887bfc564ad2c2ac0ecec13e23aaec956b100564468411017871af1d534b9f11178bb063311e4db9e837574bd4caffbfeaed66aad6cf14f482c15f68735ad1f1adaffe87385a6cf9133044e69f261fe3161c11037af8e564b91b910bd09cd84987320b4193e27f96872ef00a1c9a3d3eeae11eb24301dfc3f175e0c0c8aac8588e992771a50dc739ea73ae5280871e4491c0cf36c059c2ec91157eafbb68183443cda40cde54ced727aaf05f56ecac9a9224af00de1b889489249cee42688b2c83b993666c128a5f200f4265b852c9f2370b315dd4aa679a5f6870180331a905a47837a774c4e0e0b771802e8b8bd5c04c5e013253b097032d441e6a2ed768b0fb25691386fa09cd62a22b7371ce7422370c80bb29b7ad6542c20f414d1dbbc85549b1ff509bbcb993ec85e5aea381e0659eecebd1180bb9cadbc08a0324db42327517f2121d41cad25c8979ca389c1f0ece5abce353b53ebd7a8dabd06087e788dc0c4bfb2dc023d0b191e568104ae29394d337f51ef37f4ac91b76db60943f48454c948c292d60293a1de1ee98d0155afbc57aa483f1b169ad3dd25e8b88aa32b5852f622611f161f8e76c50671ed0b47486a76a014f098457ebcca50894b22071f4a641f5b4cfdf21ec15931cedb00fac23ff8c38f661b95d196a332814ee9702fbf9bae9d17ebd88850b04d7d6c8d2e099855a86a301bbdcf275b555f3c8166f660dc036d45a84424b2a0f81095b7de1909b3f054ea618da1c2826ce5523a989724ccd5f69683fd1c5d1a866fd181b98e620e880ce9154313e62d155742edee3c3d52e3b46129b78f1c11e1436f2fc5c43e666eafb32e389aee5d9efe75ae53abe10fd401aee0ef37def93dbe57ec8a54b22f7572cb202133ccdada5db44cefd75a68f6a23930a1f5b9aaea42909efaf5c6120d1106fe0ee63f6b3753e69d58d16b854deb9c4c39ed2c04b6aed80aa2094890bde2ecc5191ff8ada0392ca0ddcd8a7bb680dbff0df5689e21c253021c2595473045f0beb9becd238fef0568ad4ebb1658183a8ea03fabd3ceef37fcb9c41e9857223a82d6686956b6ce01f35483825cc09e0012aaacae3d5eb2da729481dc1263c0a26c44d95b960c483189642a29b15be392a9af31cb81539cc51802a4a0a313f5c709957747c7c62fc54e661b93c8a02bd0573db09c55ad8c82732437e42644c042f4879c1221f00847058c51d9e42b98bc7de4ff56d4a5cc64e93089cce94887c1f9653e0ea1ae561f2a0de0121892e92367e757c33076aee7289a1e8eba25554052b8473c8db397b590f09763130d9669849b51ccf09ebbd600fe9152f6afbcf44154b3de795c8318d9f60141a194f4d2b8fd20d486715478c90647cf8d7b81ba29b19ebc6fe9bbbc28360cf47b52d76b653098234f80f85794b52c17f798f1240f37fb50d9886739d78280ec4e9144003b678961472d129f2c707ba01613a8d435c9479c17e38ec2c1de484608171c503e75ed0c6a5480f94b574887d92c0a89253f980b32f726226ef27632629d676ee06e75e06412a0edfe14142e5684ef64c4b9804ca0533170124325323942d82f0b73c495cdc379bb801b3180fc6dd55558afd3b9f88fee51077e54291b63fab97601350d6817f525890c4baec25f97373c9a8ff6a00de4b57494c56247bac712d2ea552d6470aa40b6405c37017c77183329b1b5cf07f22b003331a4550fc65518a90ad9332f1aea067a0391bb5411014e0c9edf636795f5235616a08b264a83f457480fea61d238b69589d9ec07516a0b61a52141f4a75da8fda82b3a58cbfca8cd09841c5ca3fcd8c254c3584d6e938a05fbfd24919bf1a9bd9930839e1eabb93f21759a5f10ecab6ede78300688c2bfabe07cc0863be2363eb429bf68db4055661e8fe0676483b579ac24fabbc9ba9d2ab0e7103cfae23dcfdb43839d3f94bd37b5f0e223b4e0d235e84d2a65b3976476ad3dcdb403522e526a056861bb270ce47e7cf7625dd499692d42e669bc5fe885d3110e08c3f0e529bab8e7fa9dee3f232a560a8327ea6725b29ead01954e222101826ce9848a870616f9db171a920bd064476983b44ce51bbc05b8e522ae9defc6355166d2eb740cee24860f55ba7d9c415d1a441d5d8ef6acbee9c73d48472182110f67f7ade4489548afea25e712375680f87763c0eccf6d603356b4403bec97ef6cd12d14e756d313d17df8bc7a22e22f15bf52b29be6a5ca9f982eeb3ee4ea2042b3cbebdcd1ee8bcfe854a980e7bba5f48db4c5fbf1e759cb3277e518ebf372e693c3dd13dceddc34aa150618a49339e31a7a22e3570e694ce8d454f62f68d10c4743c421b0d7e0caa0c9c60102623f4761324dc2426418b8cc15b8237f3b434d72c871ed4572aa7887696a2f4405c1b81c996843490e12b1ed890be79444f994e41207ca5c7f0a34b6bbc688c15a2539dbc1577e531e8690a62497f40b87fa6e5e3b3f172e20070c37833fb4dc3c0103fcd8e0c5c65d9267f4d07d9855fbf51974a6fcaf2d4911845c100551afcf1464cb7722ef374b40d6ff44af19cbdecf6a3131e316ca18ef78a4d65b5e18fc9364fa2edb4093ed6132c2e3b9cd43617978757914ad4da7e6433dd33ff2b1cb84b29021f5e5909daaa5c28e3826760ba7905ed36ded55f4f14d3152ab859379a025019c35c1d8943e101e61fe30634c89ccef3733a9e189abe072a6bebc196a893c17a800148cd8ef53f382190abe7d5217b789903b5b529b8e32641375fe247578496f6cd780aad924248f71f838843e4e18c1a3d9819205eac73592044ae5ace285393d33b36e7fefbf2da532d159c2d47e7226a584853bc8744045edeb7100efcf70a113470ae48b1068f9056b6d90c36f56eb60fae0e7b6b3499f83639bfa0fd8206d00f34c1dbf46945734e55e374dca4ed56a4713ab1b16b06b6e7ee64f1b8ffcb494329a37b2f562940409753896282b63846126ab3a69938d19e62eebdad257feef948300ef765933aca0313cce200f300ce2ca42e2a339b0cdaa8aa3c43b8c94edc17b8e5844628752dd934bb26d1bcd7d64a2fa2a74e677a77ba331d8528700dc1a2d01436ca7d6aa422a7f9c9d106e9c7337a78847a9d58c9dc7d048c16b097861cb518a996dcbf6eec5bb9a5b931b229287ced614b1372cfadc0260aa4bbb90cf807ef4b69531bee45018c3ae976e48a89692dc09d31f6e685cd28cb4f7c1d24414a5f197a4f4f993ab0fd47d0dcbd9db21a032f5fb126e36c39cb43d4659b0ae271c14c18cdcf8929f6c30c645a91d49a49d7c7a2409b72c867375741284ef8bc3640db5fd21c58fcbef6e0ca3ee19d8259dfe24dec97993f54ae291c42f095f521e493cc9f124f392c493845f129fa43c497892f348e69584272118af21c55d04742076614c6881a1ce9af60ed34e515c9c3d0eff7b57c5362b5cd7be6b21a62e9c2f9b8c77ca0c373183a7b7628ab0ec747685e2829b524bb450c3bb8aab4e4063b610d2e4872e98df075f876d0e51fdb80e5dee508959cf8d99c55f8e62cd4af1e2e3872fb959f53fa8b21ec909f43ea7cfa192119637b16fd0918e58a078305e80ddf8dc62ff2043e7c4bce9131f9fcdcef2d19c7e47f69ca56a044d20bcbb47d5b3f1b7c2f97dc817a7a5cc7167803aeccfb25bac19ad3a00203a516ba966ac57330cc5886316b5e042318964c8536f67d9b286fc69e2494712c5b5ad4bda15cc63e6ed0ec393ca94164a1315977a1513e80113969238018fd767540bf540db74d45fab842baef30cf1e122d0853581ab80650607be695f0b54e6c7ecf7e349a29efbac1b4db3ad975722157a91c94fd75cc20f2008ba639d894ebb1b60bec7c3d9181e9982a11c9f03b99e9d1ccfd620ddd3259ced33a034e27d0f54b57a4c408f9501bbca420c7bfaf45104b7c94ef49a254b0ae196f369ea637af3738453539a7fe151a832748511e57f024fd79b01c0c810ae4f762ee3dde1bdc22d8b9b0c06c6a3a5108b10336ee9a40f281909bfee74b456e0cc116843a3285a4fd54bc91c4f8616ccdfa0fdfef9148f8ebe9d520be7be1b5ac4d0ff38af6c4e2b991631c95341b40824724c5cf980c0ccdbbb255dd82324f28df007a1959c0a6cfc629c8548ec1b58f472184bf0f38fe8b08c52f30dd70439ad43946e84baf56f8d2e93ab487f85ae425fd12967965ab03d186247433c8113b1e9dace4017d6cdeb23e2b07d6a1ec52b7ce5ba046c0c039ac6de8f5920165a4d073d4b8d73f98fd08ad5bdc4a8f2f67aff3c004389db73fe4f6dadda1fb1c5b5c56db83f6b8de6eaeb823e8280a0001e789614aa9faf158526e5efe9f3a05b5cd4f1f6a5e21a8d940b3c4cba91dd6dcbbda59429c95109a208e8083fbadaadeef2eee93f39a1e6d41256aa4020ccc9a91af627d25b0a00db0221da9e48a2e67b31765d60ff663745cdef9b5a725703e96ab7816cb7a35d9690283b7d632229750e7590876e2e4740588a293504ff309c60ffce3b5a8cbd504ac35d8ac961d308981a4216a930b0774dd87f4e77b50b741775d70401ceaa996a16632f61c097ffff6a552b7c0903c607aa66f2ae6ce0fc12068409c24e03e79730686097bdd022a52fb4171a6e5401a64c3e4411e9213abd90283b5d203220933209e6f4533b8166d044e22cb413785e1ab63043bed51424618be16c062dc04c002d41654603256c83e16c0640d8e6214e7650c32d60e864074d780a24b4253587e50ec2fdd99d8ac37a8a1f97f5e778250ace5e05f7cf685084690e83b3192ce1fe48c5e3122d5ec17d4aa2e6989d466b9f0652c2ed34b8b90001638abb20ce3efc43ae49cd7589c3e025dabedfa35171576bc008fef6b186849a230d07a11db0028c1e60085db9fd210c538c750ae0abbf89a62770143e16e71e02b222480b21dcdd05ee219863829a7b6808f2404bb4fd6c24de410e934ec55fb0835ac861505ea7c928ea6c8b0e9a6e59d44c71c7597cf5e7068ab31893b348835a7076da18187e8e3449a34a78b0956787d1ee038fb5289d20a796b9fd6027beb77137a8ab98fcfc155ff1db427751cc1da166388bb1eff83e628cc679617cb98ccb41cd70a6e331269fc22e958de0a576af0b6f3e6c4d7baa52bd66757c570e87c1590c9af00c0c3f8708788c1ef9f8f8082183cfc361702603244f721013c835c9600b9c61958e0e1f7e8538183e7c9c76a131fcafd52ef8432bb09560d47f0208f7cd3b18661786ee440444c0a638848e8f8fcf10337076e9e868a1a4c30998f8fce00b2a3e579220129a206403a4114031c20c64d00640f4800b0f18c9a0e8064180ad8e420a2a8c70c413b526239064b00614f0a62e92d0c12b5882b79f50868070f771524a2921ed5efef4bcee8b373ec771fdd1a3a89ecdeb2c111e503478a820f3370840594113540d16023778bb39b8cbfb12696c81c6f1f68fbbe27b9c2bb96bc6126af6a52b0ecbf44fefef4c0ef3f725c8e3bdbff700f26c4f44b5fde9a9f56c638aa26153715121942121dcfd6621de9e4803d93ee22e6b81c6f47690aa65f0bd197db694787b89b7eb5486a8190673393000f2441b362094f2392b24f6307b9838c35ac4f14aba45edf7a6d31143280179a210e867c9945b5347083b9409b7cedcb88fc6616068664e97e3d4b9e0968190a75f2857ca068e9c499ba4b8dfab1f7cbc13741a9237af74de25e3e5fed76ddb5e06aa47a650339c28fd6d4b6d8d7f9b73a637548f54cd09651286320a7994440385a2150d6ba6cec4185da15063464d960f63e16c068f8a82dce52724906018e4479c9080488e703f37b4dda0393453309540ef143495cc14e09e47b3889e824e48a6127ae790176d5c5af5a669d7e17c53e84bb9c0ace8772e30bf8271f9d509e6746bd74f6da0e657fad17163d9e15e79c32853dde3afbb14e6606a3dd7171481e0098774987497f4cf5d30d7ede1afa67db9b8593d760a82a7a0159d41f1e51b13ee334867d61914633488b330be686ce17ecf718339bb5998ad89da83c499fabc5e0adf465433cc8461217a4bc8c29692d753c15768e931ba15d52c31ccf1de5238c2db9830c21340525a106078e154cd2a74120568ce0fe055ec281ef4995c3dc1836f55532a84d2a4099426533084d2a409ce144a1326184269a2046709a50909b00b86509a00e1f67b81f628e7c6d1eee47da8aaaa5378609c9650eeee22e389225a93f122e3e58924ef8a179817980d0b6a06130313338f90428a991133635b82473483c60c1a4f38e1092ab443018d158d952c424a02fb7f484790365057dfe9743aad6490a6d07e31c2cc8742a150332c981db06858342a17b456f09e10cc0b7ee8c4708684e5cb8206051a145e00e409c92778e52e17171717146a4cf1a278797979a95153857e4631313131354f83060d1a6fdb8922aecdb579a17509361f8bc562d9b47ea84442eba67563248570f3b517385f4d4d4d0d8e0d6bad7541264ca4aa068d4a7add78dda0f229253d51e509bd81e306cac3b1115579c10c288e1c38706c44397272e4e0b4d41c1d393a9e78c21531c4161d3b74e89043b5b64367c70e2657743e1c3870e0d0d141aa4253f872727272524801a988a298b4d7c40a3c5658a12bda544002194978b0c0834787e10c06452cc05860e173f2d52728521618a8406608b0180ce5c5beb62e614e0c7e60f0041830c1a07685531fc4766231a426387f70869424319c31c9f23059ecb4b0b3f38410ce1f9cc1e00358360c674c8824152d7c4d445bc7b006528ae80b5b6801d533610ecc7941152e063ba7253a31482206486850e18cc90fc570f68231204f7cff0979e08ae78bc562311e9e194ab4d0420b2db8e0820ba81e175c70a1478f17604139c1833288e1f90b68300108c450c0c02b546358032947f0ca5d282212439803733c3e8696486fc10e73fc67545354383bd2223bc80331e459b9cb95b0c4125b9229bf907309a429bf9053ce0d69caed8b268230ad4c2b56baad4c2bd30a5785b6c3c494534e39e544e2e8462db7d907c06e243090c0400203098cee258220c4184dc8b9cdcd8a958dc96422adcc299b989209c77151ac489284a3d40bce0b8ef3624e8934651222228e526e09607cd1849c4b204df9859c4b204df9c5b6c9b92599f20b3997409af20b39e5dc90a6dcbe682208d3cab462a5dbcab432ad58e194e0bc50c2944b204d99644e893465128e5a41bd18e2bc489224c99c327645899219d6216b2dcdd46074eca3d19aa90a2d8ad7eedfddddd1ed1017ee4ff648d972f6171e6c65193e4b6afe68ed514a39e7dcb68de3388f7627ef43a554d545c6cb0c08728066be1c3a70a05c64309cf62110e4b18e1bb7bb6bd4bc6adef5d686bd50e01c01cd240c63aecd4d53191a726e08724391c927861ba2b4d6d5ea1fbb300cea87879bc987c946470b728173334148fb6830c773820d8e12574808e7e3a23cd1ceeb25ee6a544fd74545cda48f3aaf99dc8574a2d1a4936cd870bdd00c1d10c6a60bee6a8cb004ecdf5bb0df8f8683bf9abbba29af1b20a8010454d349b98103051c396872b0728274e800021a1212d2a16387cf0f5a76ecd059a1a093820a33545881e64cd0569850bc1cf1a0c311875d6a426001a6840ca1a08fb6246475ecd869c14527851678aa0a2aa07aba157854b861f4e1d386750fff6b98f71fed851baac1719f4edfef9d1a0be554c083051774f4685a5bd15360ac6b4d851b0a36c7f7b02ef0d8165ca6c2ef581d1fb3391e6659e061570022ca54b83f4629dcebe3aed6b9f7c75dfd3bac8e4b84e3736c8e4b04f4321cf702c557ff0dfbffb235efb235de8645e171ecff8dadf996adf1361685bff6dfda9a57d91aef1685aff6af5daab9f64a8d6b9950b8168bbbfa696cf72c4b7fc6762f63e97bb6bbb6895eab25befa61ec09afecd6851051b2601a969302a403243cc3461208a154041c63b70e3869fa0186a820708661aeadc517abe3ae7e1916e21d174bc4bfb6b4401cabbe9bb2f95b3a8df8fbef73c5a9ef330c4e7d1cfefefb88fc80e367239f8518658970383e0f87c5efd77158eafb5596486fc1a9f752f6433d447d288f0fbff830beadf5dba518a3df6f9962acfb7edb1463a7efb75a62ccfbfe7b8170fb60883d3b73b2334b33d4ced0946a9ea1e11a2d35b59a255bc3145ffdd20a2935bf8c41cd3535dc4ff002e71a26dc35b51d9c9156063a950169049501193f0304792e46ed879c07655042f9c9126778fae079d18b9107772d3da4b49ea488400ecb435678fbf8f3e38f2fb9fb92fb120f9bf5fa73540fb4b2e9871fdcf5392dfee0b04d29be86ac707c1ea2fd41881a7d70d7fc787fa8527fba7c5e6df576152aee9aef42049aba99a6fe74f36a7695eff9ad14634edb5850732bcdb8418df3b9cfbed44c30677e17c5582bdd5c4fb32ae7c79b610dc7eff9be7485c961dfcff726cf82faf9d1c743d96e3b59ba7557ba8bca3723ed9aea4f8fd363f491b2f93f386c42a9b9e21967a71f5c99d9804a0d6a18caf0602b6f4138be078dd87a0967b8043f77f2a5d43c99a61098204f7bb109c18518386611068edddd904b650c602235b8f3a187ed4e292494c49012158fd15852520325262649b329454c4d58b26c4133044660f8d0a3745284f3ca7301c21d7e7b682ceafcfe3091de82b70a73e24f0bd356461014a52985075bb92553abd154a3c96113cf9efce0171cbf66a86689c7564b35c6a8228b4c128e9f6b8e04e1f82409ce3542b32750e05ca3a4c607c797ed32490ecb32359999c332956922f34406ef8a1a4d1ef7c9d4fcfb11711827a16e9a41324b45a8fe364390a73ffecc523fdc6a30c5d7aa07ab2598131fe668fb2c53ab1104736a9b95a9c557acd154230c18eb3e7e8da61a2370ac1102c71a62e0f8324931d6dd1a74b5e2aa4cd27665925427211cdf260538c61aaa211cfbda0c41a9b946138e0de304cc893f656a383e6b4e999a4c1599a519a019241e9b3e3257a08c51a349268b8c96199f9923324c38fe945da3e9488bc3328c1335b2d418a3adc31ce1f8304e401efff8b4e513fd5bd284458d0fa76c06c19cf810358339d1ddaa604efca2b92409c79f470e9b8161cc645284e3c3274a382e61818fa8c082f3ac3293da851c96eb90c3b22a0987c12b709c82f35482a354411e6f8a23f07a38bd072dc4f20baefff49e9f7e7e3afa3ff18388cf6d97059ffb18939705e90be92e0bfef65448bcac1bb6a7cf8257c8f69425643ef7f33923b2938cb350e6c54f02a1a20ad032eea175ec350bbe0f8d59df0b89df3dea1b53cb821f8491f741741f3f0823d415229f7e10f1bbe72c0b5ed6776fe83ebe90eee365c187f17512425f7e10f2b2e05db9ebf41f5fa78732faa7262acb7beea18ce55dd60ddd738f43f7dc0dc248487cfa41c8ef1e07fadc733788f8f4a10c72efc34f4f638cfee961eb7477d081e2b590161ee26b89b6135fae6e8bdadf1968f7a495ceaa4eeeda96bb9074db1da283398ee32007e4fd6fd6f5ad8f313aef67ca715f4f8039cc3d410112981342c67dcb87e8fccf8e0a0d38c1e2a48a9c5b781d853934e66ec433d0de533ca7542986f8a235af3094c950e342ddd5839bbf7d0c3334c1fe3f380cc6229c358109f687572a76269f2938a8095830b7cd159482fd63804af1f2684b24ef67899707b53fca265a47c4142d1993331f1f1f25b2d5ca5b1842188e9284f796b4183cd8ca3dc30ddbbfbf9b386c761f41d69c108840bf0e0e53e1243cd64866fd43a6a882db077723c1dd700cdc3f9b40888dcc30f5923e8a4f64b2a8faadd7afe899c79a4781c63528c68f800e9d94bbc9caf634f6a2a83a409e19d89f07c813b14331b0b710d8bf8d80616067f51bc1b307a8b4ea26eeb2102b61a8838c7e458535c8bfd802b66860438791534e39a57c193b507f7097d7da27527afb879ae06b39c2fe9e2f5525ecef4b3ea76b45335dd86fdeb6f93107761adc71db7ce352d55d9ea230c74f4a38504f45a7215ae1e7d349e8742499b2cc21a812140fa812252e0bad055dff3a3a3a5186d011117f1084da290b0f047086bf5de2f72c3e9c6118239c617973c5dd10ce8e2a0c6752809c88608ad804112abdd0b7a0b48da03a71c7e5c809121fe04c6bc0832850e05cb113245974904c8f33334015d230c4b0887ae6b8b96ddf403eeef1e39cef538725ec0dd4479c7490127329e698f07d78d7755bd7c9e7b66ed2c96d734a1b7210e23006ecc458bfbf92c72578b095eb128e14b60ba776e184e17b5154c83303f2481cbfadc0b1b5c0b1bbc031b7131ca5caefa455041c463f1b19c201dab851f41dd5d303c77172081f2a9c84f3939de4df490e6b720547158edf4c0ecbe2b12662a89f380cce9c20c95dc3b16748389a620bce8d05c756c2f1616e2a387acfe8ed29ee4904ac0e7688bbe2fc955d155579eb1575c9e396c3a9531a650a9c57998621bb63cb8edd1da3c718390c67529ce05c91209131b80113a648185b9c11efc3dd9de3e836a9a49c84f05f2787e7c09e632685c899107fe035ed0395fbad0789336ce2c1870c5a5515f5ad4eb4af0e5f136aeea3a10c9b329c454a7be8e5fd797018cc771e53bd8c77f969612c0fee7ab93ec031b07fed1fec8fbab97eb78fac705e7517fa0bdedc48f04b2354cea354f899cad834e73ad985e7efb94501f1188786640f9039513ddb1614b4a1807038bea78a2da859caa39ea5250c63500cf299cf9a9a5a50fb21c5447a0b9d1b4753d988430c513fe4bdac2019cb2398137b2b02cc5c419c4337cf236073053f18fe1402c2e610cc02be3cea19b8bbbbdb513d31fee88931ce1903714277c518856494d1354004638470b5c2102a718ee3525b445102e74fc9a0cac07016c503a91344f1d1f22d2b80707e1d0ee99dc6177cca4122961097f9bbcc2f1041c8607c45153ca4b5c2ac68e28fcc0a9f09a12042e948159ef7b09577f858287b77350f77f57f370fc15f2af59fdd61080cb6f4037507d5dbaf436081b22dcfbff79bf3902158f5a9ffbe513d2af8dd9cfa1a5376080cfede3e44fdb0ff7db59efd141038f59f55f9f00af167af0a15f51f0edbb37080b2530df5ee8c81ebdb2ab8bebd020b8618622feeecb8cb7ef6c1717d1d77ed34d5fa5907088f1ff5ee60f8ddd3f75a852312b5b1c39c2a83ca02e13d7dbf799b9b9b8de09bcf14e33cce6d54cfcdf57757fb701c190b71bd35b51b2c8ee0fa37efc56f7ed536ee2662806a54b666c95daaafaaefdfbe46d5bb29a1962584fecccf5c206282de88aca6067968300ed6f7d734c5d88cdbd42cb9abafcc50f1a933de45657d9ea1f9db7cbdb9f9fec7310644cb5897a57a28633dcbd629f59dfc9800f390d9526f5ef53bdee3503d3b5c503318a101b6c3369e2819eadf5c960cf56ffee6ee00bbf521f6fcbb3935dddcbc4f8cfdc420189c4d116a19dc5fa325c65a168339986131e5e6d474cae230880506328425311603344343e570fa996f140ef2676e4c50c76e2853d901380608490d82fee95da5c5c64bd40fc72a2d4c35d63efd2abb0d456a71e882f0befb3aa4c46110f7a6c45db5a7de9a1a924ae3ae95959ab7211f7143f5d4a716ca8844acfa4d89c3eaeddf861c86f3fddecd43544f4d151ccbaadfb8be8c65d53b4393cd50f157bfaca2d6dcdc9a9abbfaa1549beb9f6b6a12e6d46b3305d7b716ca1a657373eb55f7ed1548ae3862e1001f88edf43e341fc4e9e1c387281b683a7b4239ea06f85fad8b08051aabde4565218eede43ca5b5e6bccae6a1c36c6ab5b1b9f9c3f5a9cdab6c6e36826f8ba9c5e4309bef6f61f117805ed63da402f7bbbe15f4fa9a5a8cd1effb3ed40d5356538b2fda6a358b6255d9d8a85436360f512ad5b5799b21545bb66cd922937988c2c1653217880cc8a88d25eaaef8c1f5ed7bed53b2e0faa8f7e407a938d1666833588e666833545e70ff4cd38c967e960feba79f7564a64affccd291c3724d2dc961792a3169e2303885861820dcd916537c35a4a2498067d12c9a453784a1d2129b9a6c7c3edd9a5a11aae33c43bbf1353e9047f5313f43833c15a6208c9d8ea615386f43b8ff741463275a7cf5d3239b7b849bba3e9f9a5a4130e7f559a5a5a656b30473be6f9cfbfdcdb5b99b946af399465d05c557ffea6768335a409efafd3341d1e6661a3837d79b209b4c8316633347797583f399de8899398a3837d39b9babcdcc116ed5adf181393d438352677c3e35e1a6f159a505f79f68f729adf57e5ee17ca2d91895954982398d638a982ef55936aa199a857117cbe6fe706c53bfa6d65f53658696f7e97fa03fd2ff35fd354cd146556768403587e598223095fe992a2d93047968e07e95a581638020cf87fbabfd70cc1187f9f7cf00419ec6fd3145401e25c8e377080cb6ff35d46b518fb210dbb0995edb43d3b0d9ce8801c2fd2bdb83c7d86c6fd82c9ff86cee5e36bf10b8df6573eb08dc9fcab13df48ecd3666b37c8287cd15d358205a363fa21400ebddb150e6a55e8695c143fcd4bffcf7281f1ccbf89777d48ff832ded660be3f651dc3fcffb09e50fb737a223306cd7763382cdb260ecb59b4d0f8b092baa75a1c96893cebe3b04c74fa54d2cc7fd736d999c33291cca7aed552532bc0f7d754b9e1fb6b966cf8fe9a2b357c7f0dd38defafc1f2fafe9a26d7f7d764b1f1fd0fe4b09cc2fff8fe3fe2b0acf33ddfff410ecb30fc05721712876501fc3de2ae16224012340ccda04486a2ba0475f431497dff2b392ce3977dff4f71580ec0fbf8fea7bdf0fd4f0500dfffb5017cff5709f2fdbf14c3f7ff1520dfff4cf7fb1f8bfdfe6f527dff677120876517bec71187e5169e27680749ecfbad90c372ccf7db241c96677cbf1d7258a6f1fd5689c3f2eafb6d91c3f23fcef7db250ecb357ff3fdf6c861b9c6b7bedf32715846e16dbedf2a392cb3f0b0efb7531c9657781edf6f690ecb3a7ec7f75b2a0ecb393e07082953e1fbb1075130371bc130efb2371af09fad0b7895bd09f896c5b1453804fccb3eebf50260fdb845a8ab00df0d377facfbbaf98677b17eb004d0ba0c50b1de46035e34f7c728e37ccf2d42f37d1860b0e166570d3660b845eaff18b96eeee16eb8b98687e116a13f6e8681b27ef4bc8be572dd7ce36db05c36fec7a8869b8958ff8306869b0540038300fec7a8e766d7db70e3661b5fc3bf184073294d4b55ff06cdf7af5be4a3b9f122a2f91603540cf8d1f33f46f5e61b5f80efe6d7dff0288ba200c8ad97dd2294deff312200a5e166d47fb47e3894007f290068b85f1580bab9fe473fb50054777f8cb2ec7ddc2234e3fc0b2f7c8f922195f2f12fdc22b5fb1fa3ef66d9a76e11ee4bcdf02fdc22940037bf40a90cb2ffe8f7d12ff53f4633dc4c445f86ee05fa00b845ea0b00f87e86d4cbf02805a87cdc2c7b1a3a1cdaf968ddaedeac7a545751ff6324bb99a87b1f0ac0e9bea500998fff31ba37d72780ea66d4d3f0d6dad3fd31ca06c8b1ef718bd013ceff180d20c8cdf6aa7a9c62370b799c532c08cedf1ceccdf755a7f70458effe18e5013ccf2d42330fed11c3cdaab740dc7bdcdce37778fec748753310ce00bf738b502137efd0530c405e7552a9fca4f2ff3132c0cd44a78fc1db397dccdb89fd8f11909b551f833f90b7390ce083783da817c4bb37b7de7ad7fe8fd1006e26f23e480e3dbce7c9a107cfff18e1dc7c7f0037db0ff2373864726e114a0d00bb69c9e0b40e00bb45aa0c0e06c0b945387c33ceb764dec616e13011026e66ee8f51169279fc8e5b84c276dcac6c6c603c6e86fd8e5ba4cef068dd4c833bc0af64721e875b84caac687c4ba6d5b29169d9fc8fd1016e2692f91d333932bf9ad991f33f46346e6e3d0d9b5ffdcd2dc261221c3384504c3403a3330688f919333837cff89b5be4c377033f463837ff6324e466a29937000ed8cd3c6e918a37f06304e3f13f463137e37ccc8c9b6f7ec6fb8805b8c1861a0840c30c320c20480c407ed814bec7ea3c0c56c70bc0e67899c53e6c005aa6c2bf60753c000229a425237c319c4981648822a9c2fd5504788563cc69fd2b9a0f0700c0b99573641e3d70c438c3293638432ab84a0ccef00ac49202ce300b8e708cf87d04c7d849a4706e255d0ce1f8323eed4209387e5de940081cff754005c777ede0081c5f67075770fc1d1f4c70fc18aa5481e3db80031c3f0708380cceaa28c14a07707c1e1c066756b2c0f17ff001fac1f19b07351c675e3881239c2d155184ea69312a142ca3c69108b418c1111b50a18407448831ae562b0f9a5281244f5871628a069430c2ca96262e1e93320ab13f85311f4e61a4f20da1814fcaba8a4539a858823c5d0473b8e79ecb3b3116f3f05f3e3f66cd372293f1f3daf053dfe58dc8541fc3c5bc7f4c6c21185be38bfb17ebd68be28beba6f8e23e29beb89fd6dfc5cab0de54adfcc2c8470665469c0df1c5a9fc55fe9c8ff8e274e28b53f905e29ff387b2caa920105fdccb1fd4acb23c58ee3721d8c412a27a7fd5eda3f8e27e165fdcbb2ce1b897b129c6da87934098fb6e963e988b5a30f7bec4bd337993030915cd68b1c67d5ce23e32c5189471be145fdc737fbab98730b78a947b2887a8a3a4f0604b892abb5b87f86affed8dc8b60bc4e9e703d1e50aff643b4b2dcb597e5937d09f2f84fefc2e3e5dc5577c1454eebac8a06cbb14cabc5e411e1db61d5479730be17e186544f23ce9ee34ab6dca888567595fd061e1eebea2107687456d08698a1504fbcd0bc01d0a3ce8b0ecd8a3cdb93be536773965f794127b52ca0d42987c77e9eeee6e1bbb7b6329a5a41ca518c258fceceeeefeb09d6659e275453a4abb221cdda64a4a9751a5aa31d6c1977ce83201ecdf12250387a32c1284e2f8dda98db87cc28fa078a2ca5802095585e1ac368227d505c3596d03a929b5c370c6c518292db8388214556238e3a20854b808820401addfb4abd48bd4575390c64512ec8fa29d931f13a83e548fb45409394802fb7b1546151060c0049328b25851852c56c1f1c4e3ea744930cc3034d897e004fbd32d92724004f6ef7e85859415f8f4608a23407022dbc1961f789122b1796048d6afb3136330cacc8b201f59bf8fa801597f0c5490b0d700fb53548faf7a157f15a3d052735e978e6c2a1c7061ffad7f80831de0f8b1d6f8bee4e3e34384bbbbcf40fbc209134830010646c85c07d85d0676777797a81ecf1f4ca67982738b8a27e0f8323ea56d81e3d72670fc95165be0ec065be038c3010970ec2bd55593707c59abcbb5a40545243802100f8a64b18b21b070a1d1d6af66972b474724f08d0cd26438c1614105044a58f820045b68582481881e1f1f2c9a70640b5316451cd972145f106f5b5d75462e8c571245846a319c41514331018518a9a34a430448242d518120a90a29ea0ac35952edeb46f3f1f1d9c096d484b78dfb6ddb3e7f6bbbae229ef80b4ea1546a9515e68ab8f29c0b3fe4a2f8c1dc4b11c8b88769cc711cc731f90b36f90be21e24b4f80be25c535844e1ea42c86289e1ac6605ceafc3d1c083ad3cc307cff7a2a81c0920ec070c7049e0f99b1830d643f1353f7347e00939233821200f0dd714372d9087fe7c0ce44d0a3c7f4b82aef973abc5d7fc55dfbcba3dd4dbb5814a97c30d8ed26eb5b9f2ae0df1359fb337985ad50deeec4fcb523085f95faf309f3a0bf3b7da129e31b86b2ea950263f6f459d85b22d29c67ed88a624c072679ead38956a1d9212a469014b75a8d4b38733e272a46608413ed99c332645272586ea1260e8357d470fe3017386f53f09c34c84303cf0fcf6f25db90fcf93fc4980bbee653ee66ba5d2daed57c1d86fca4b4d6183df89adfd917f89a4f2d0be6ccdfacc59cb581a3eda1f1a9367fabe1ed5b89bfe6f790bb5a89bbe6b66d43d4f89b1d722a72d7eca12498337fabe1f91bd376a5fbad8abfd0d5b624c803ada4fe8aef6db5adf6db92c364f07c0ec861483c06a51c6d57b06c59f07c98391f3c7fd382e7c3cc1dd998f07cca6d5b92c3f2968495380ce68086789252e3673ac3474a95141e6d723a53874218af5dd0b6dcf5dec7984e7cf94b1dbbf33c1c063d3e7cd7c787afb3b3e32ecf3a3bd212d16ff953cbc35ffe2e5945ddee8abb5507be0fce14cff972630a02e7f91b9c367f79fef0e13efcc3f15219d4adc8f377316aa5cf59ca711e4a85e26c86c128cea25e6589c447ad50967e47d40a83214d13953ef73338ecdd15bfa1d415ecf4f161f0382698fbfadfab5e421e0f65b354d44377a1ee576f7679eaae5b6b90cfbd1791a81053ff5ac63b2ac87ceecae096503ddd4ba6ba7218ed1ac614f73e9cbb2cee2166fd68ccbd9fba9c136737954aa5ba99aa6c55a9208fd7c371af7a94dd817bd57bf5250a48455d960ca8a70f04755d32f71668aa823885fa2caa7baf83dd31c53d772da598c70f2baa949a29ae4da59022846d307412a5087b2ad8f2e1f4487d9cff094cbccc527798dfbffdbc7987f8fd7d77e032cd437cd83e75877cd8f3bc9bbbadc7d9ee1db07764d9c3dc3c88bbee3bb0fdb410f5b5d6ef3f067f978fb741883f033cd644e408c2fe3a3dfceb970ae214d2fce1effbec1646fdbe0ff5db43dc9685ba3f30c4196ea7d4b6f9882877d77d77411fee4cd57b291b2543eaa2fe6459a96f9c7aefe4acd4ad32594575dd7797b752edf73e3f0ddb0966e5aed8315593a85eae9d95bd63fe66b7de316744fda03f6df7b10335437ab99e1b9653764719d42654442541feb427e5c805e6486056e8819626922ce184c9165b4821eabcca90c0164d28c1114a58820b9c504942f439e18a129e0439a184832b70200548b58940ba5085184e504212b4f869e206528698ad04bbcc21e4f11a0576500c2c2c68011012ac18f2428b219c56ee729812c0b40cda8bc203d46aa2c05f9490a81e96ffdf149dbbd33aaf4c7ed3f830a9832881842474c164041216c8244d461428a8bb0f47d214cb30b24489147ca1812704a140053299451427e8082aed76c2a30e83f05db5c298bfbc2dbfa55b77ef8007614cc6f78a5d857770b8d37103ddcd85471d5621840e1b4a8c1dccf6f29eb4a8f1b3ecbaaebbd45d316e360fe981bb9731765d8cb1fb2ca5fcd87537d31be526e14b0969e04158e3fc38637cc1e6807c08e14738699c4f638c787e0c9a47a844ebd7ca9a20778a211208001000007315003028180e09066391204ce338f10314800f80a050624e1588a35114a3208a3186004388210a184088314499211ada005b780be41022418fb4b7ba488332ad5329b004c070512eb92c3ff55b1e07e1ecbc8a92505d822be8405c80e6964f85e0e9bbedf78c3978113584dcfb1b651a73905d916d3bf3e176349a02f5dbbfc1e9001f0cd8e57a11e65b8e0d6da8e397af58b4930155448fbbc44d4a074306a89b5f0e0da8e513b3461a2edb71f54d617723c345bf9ccc48c61c726109f7b19749bf5ce817115112addf500b8f00022628c5c0a160a230235c21ec853e3671ea068953c43255fbbe5f7a0d378156c6487ee9e46241967cb95b85f9596675482fa9891d862ae5a6ccccc462bc8e713e3f5c270ffabcc89c2f7d3198d7015d0ef4f361ddae7bdda3e46b8b8a9881174d8bd7923ede7bd5d85eb3689e03e124e12719f816c24e0bb309352dc5b0be711bae95b0914289db1e17c9359eead6692b57c5d05d017793f63be75d41498c0e0452c1ca8490fb0498046f9a0f8dcc2426ba92b89817c5bc51b72a50ff06ee1c6d6d315e3891f9cf7e6ecae1621743b7c5df78fd98ace2c26ba345685db52a8645b885be48848c5f5a3a90858218c3d13d53c453318de9223e6e3a3355645370d128eb0859c88305267358727f5ec0992f8241f135a40c513cbbf2033c6e72443890e9194e98e08e7ba98055d529c3829799d05c2050d9060695911bba4c8647a88930f9c2c164881e4b8c75e105fc0fe3260d3908c32e5eb133bc53f7f16539ad191e8374ada51973728d28059485cabcabf06e51d45ec92f488e0688379afdc0871cf3101c9ed89328655b51c11bcc3c16bc550cf72dc092c7e073c11b52e3eb06cce00d0770cd9a5b576cc36e34c4dc57ee16a2e751abf0c8075c9de3d5f869363744cbc58599fb663efe1f2cc82ad093474876419d73824ff288638ec57567f3a0ac27b0c0ed743a24f07df288715adf25ba955d3138e645dd4ab66d8042da58a267c66e29b76a1e6862de526319910fcb8421b5ca0a08f35e6344e0c9e13232aa4559d53c8cd225e0f347a77059c3ebf780970e0af080c7b2594a707738eba4e09ee1b62539d295a207a9463fa07fde00d6c325814877a8d8ef39aeb76b1c0b6a27d830ca24697bffa5cd70a2115feffb541518682a121fa2b249a2bbd90bdba1fbf0c16fcb29aa7b3b24f2f011393a777d1ac7f6d80eb1fe3aa2919ee8bc8ac1ede6072f9c1b200abf50523f0e93871e957a8e66f7eb8eb5b01628c68e0c1f3f9f2442e8507edf8f75156eb3f4e98af8e33e3383980b84c489fec055475f041e65124d205a93099a89e8606df195074733b6315bde2f8805f4b73b4db3d975ed612ba524358c3fb6849f90f1be54e0b66652d30857f09940fc0346ee3cbce22962d9063496ae2e088042a547a40d47279509a02e1e53009c727b12eb4061bc304247817cfef9968819d8f7f780818b112121bda7893ee3740cf138a6c2695acd1011f142c857e5e48d6e583f580363e72bab2208c00c0fc0abb936d146e10f6941c7e669d8a304d3f40630ae3288f75ec3f423f7011b858fc91904e6e702ab8824d33067c88c55e84198922d164abb5ace4a03f4d35ed5190d485e7eadd6652b94f614d9aaeb97f2b64d3a2a07d4fc60f2fa8d1dad0c54ee8a9e05f2b9acb2490420597833d10dba8fb7de2d47cbc1f2e5ec1f2400857e2254e8573521ef05b7c40ff67880283045db55fad8236e06471be4da9956d77870d804fd85dfafc63afd1e19c4083e6dd017fe4759ea59806fd9c067aaa89801c26c3fd8a3c1c1f583325a4fe1e6af35f4b5bcf4bcfb061c07e4ff1b7e5e207b7d2dbb2656bc8a89cb603696dd034e08039f2808e5eccf1b62d1c9b36056b010e75b3e176e3d9a3d43fa5693552ecba2d5b56837810010a298a0c1c7d4c8f7b9b1f88dfecdca837de5b1c4f362523be0debfd416fb8329eca371e7dd550719afa824b6729181e7407ff21528c6108783e39ddec6f27e07bebcb741c2c15f906b7be026bb7bfc1be56f70f8a4ace07bb399b8d51b1c5843685bb1fa00b54622a41111c0373805ecb83b2691bb1c289f3909a75eb61edd639796715cd9185168c08845b4a84611b29818b280989f7568ed22ab3fc553a1b4439cfd1a84b5da38094a337ed9107bcd496db82cd3387f08b82fb50b08850489da8fe061e1d51073c980c8a0ac1ed627a1102efc9e5c2ac0a10e5a487da7149aa3e4e1b1b13900e9c0fab0ba577f4bfd1eea22dca69f97ef925899dfc988a278e47c832c7e5b0688dd006ff21568b159d3db0788c0960a3b4369e01b22e3eea413b7a94e969d73d4c2b88273e6992e8967714e42f308cf852ea83a73f32e7947cd84d5d5ed3db40fc9ce0290c2e1ffe865daf2d1a4b7826c19f743d5960d83c55ac321003cf420dc312cd6df99ad7c9b72a055a94937c89971581ff4716094c5b2b064b587a305fe551e9573d9c07fe754b5a07e0a8fb2ca441c2578c718ebc344eff589d6b2046f13261966cb77e1976576c3ec8af8394c34fbc97e6e84a1bc261ee92482fa059dddadfef284c25bd637e800e39ec351afdcbbdfc062d7520119b43466267df3779319f7976ecb730aed01aa04cb4406f492fd138c5095a0276ef9a06212516f3d7a6e827755017159a125c757d8fae1b7195ce8999c89d41bac18df822fdca6fb1130d963edafe8ace3e802e838ab4aaeaf366fd46a602826ca0a8543b2eaeafa10ba291d70b7f9c2fecafdbdff70ed6733fe8a9f9e3105b293593ee77871248871f397d8bcb2d1cde16cb18135c66f95dcae3122b654d43ee0aa36e3b5db41182fbfe908423305dff2164b4569bd942afeb6071c29993bf0d7cea626180deb155c6b9f70a6a1560cfc72e3a21fd4372fc2ab14946e011fee8dfca39bc6035adfadd604096427f16d1374ffe15c7a39cb9d4e8be7f5ddd97739b77d87846b5e832c22e2361d1b82a7ec878a47afaa3de964481fb76d57e18696b72c52405523dbfab50600d9a67677022daad3875e17f68120f4951ff4d1607b8a0386e512ed0c7c44239a12499cc9a997122f3ebbea3ec6d67ac5550f8a8399b57e3c5314cf696b8bf25daf9444694218a51d707075ac56d4f8306ad0d70150ef93668a956ce2002a75014fa5d30a318322efc803f01c26005b9e4dde339f30ccbab09f29e9ecc0a521096d5dffc645a3200ad85404ea3d919f0b65af8efb45ac225ae9e8933e566d8bd10cd2f1fef4f642984dcfa63524276125708334569b4e7352d74beb07e65c503c0107240487e59eba768ee73ceaa0dd12fb1a4a24bc98a460152ab8527780378162ca2d9f7fb882b10a01e3483ff85116653ed79726dfd524bf0478b45cf1c1dc926fe97a34676de2d3c366ff700d28758a251ed68cea30febd1cca44c4473788c4f80fc31bf2fe54ddbcd4a964a32ba5a1e59a271a0a65b956baf82996a48c50a90ece5accb5cd6611baa7f579ceb9663c3b8b6ad762128775372889cbe95801c748bdb3c1fc5367aea0546b612110edc0c3a2d595c8efeb1b4b480c125e554db7f8bffa1a72611810fe28c84f06dab703a0c51c9335ca92c567010047130fe260207c30884009b236e9f520180fd202ba0db9973059feae66659026619af8e2c0f7014876b37c3f92f642fbf7bb7ddb7872926fdbdfeb8f624cc133e82a86010a0c397c6818f8c0ce269cd33936ad4b77855691e88556bcf4a4b7f00abd5c48352faf0e92af0142840c7c444a1669d05759401c1a5852f276a58d77060929c7eef955e56fdee3e84fda3d51115bcf8bb604087d719b66a02c2bf1101dbbc7a7602d2aaaf8b6413b9e5d5c59f76c40f371140024f2570038541caf67349a3c3835b9a657b2f97639a67aeddc7e22cfa262f62f8a028e700deedc99009aebaf2568ce302aa47b5547d45e10d27eebd0dcb550c707f989063a88fc2a3a42b21c708329561f698d0967887a22471cc9caaad15117e5b38754cd71fad72c16a16d8b50481c375cb8ece8a570ec5732a3585e9e64e952fb7d65bbb2c25c2e3000b835d4815dc76eb4e00d5a490d32fbd45c7250cd7010eaf033fbd705057cd6ea2de701a38eb75e90fa20ec48a788507ab319672a8194e5b096a758981d1766382f3a0ac49cec8e26891daea550162b69d4d76d55f61be861c83691dba7ce8b50eafa13fc395b37aae5bb6b8597b417b1c3df776d0d8452f4262cbe56c6568d1c8e1e832a49a3dfb1f471f548d05545f3eeaefed8de0384e7e69dfd6f0d0fbc573d41d3e5cce54cad8e14192ce6c036ba7742e598dcbcbb30a0b61a4a9176fa32f1c810ab6e46666909547b688200445b7be8751020bda8050eb00bf6b2638d23ecf537c1878fc635b9a0985c91d842ce2b0bf715c1794a17a9ec30bb9a4baed9cf3568e9bc4a1d993a4515c1d7b3ac95bc8ca9a3846bcc1e5b6c0124e1a877e6cf9503caa3c5798ec70456145290384e7eda6d3005d0219093f3f31672543f8f388917e064e776f7a448f250d6113dd0c28fe0f4aec92a5cbeb1bd05cb4c544001f12c4d5d633d6ef3a63c6bb2963384372ae6a1380dd3cd911e5af90fb42fb32f2d66b42e47dcc761eb236258e23483b2c7e90e9cbbd813c23926123c5965d1871d88dc42019844b321e3051e6ac17896cfd791c9c91081e6881117db27db82426e28552df53624af9beda800d527935612c7b25fa9f54626c950c3c3fff54ee7ac1a06c4c9dd90a69c9ef2c9902d32e80aff4494303b29f01e127b06084480412c1d2e800a5777d343aa0114f48140e902b7da13ca2cd0f11699aaa0a4338b3a9e6a2aa16b8ec6e400132a6c3c149dd505e5cef0250d354e6cdb21e66ae286d17a40d47fb962061c78422671b0703fb01212894802d499cee857aa1c2f9143039f2d7de913cc47a415df2ea8e57f2640a6cb503c38def9cbeab4ed9b7c43a900bc3f32c4ef8e88cc18d72a08df21bc4370aca42c342ad3e30321ac2ffb6985d083cc101867598093ad0471918d03f979362bac3dfc22422030704e5158dc8088cd47f826fd70f8adea846bb386b80346d62086ec1a61221f44094ae2436d7d5ebc6813f0d805122f8bb233599a6a0d8b5a750d90f39ba3c4dcf097e1e2500298025f6007c1572222e3906464f410be4b68e55b958b0f76f3c40a99ed62cb4a849f85b1b3cfc661cebd2b03bb6cc914727145143525e83b6f8661ae17ad469db7a2c05ca2a665986f5d88501f2cd0edba2332d3d756cc8861aea62d72e255581af74b121f571772e7906087dcfe070fee069d417f4c9f8ba14b657f448453721b5306841d8e93eceac9c9d0c578f11729f2e70ad01387a6719c95723b1ff89f3a1dc048da4c785a906ca491d1ca970532937f5d0ee0f171fd14e51793db0ce096cb2549f58aa4bffcfccf7c99a5beaa29ebb9c8e933dff470a514b720f51dc72a4a533884cb5165575e9a853906b7fb7f81bc8f5d25bdae73590f44f453e746469bb6d20c9d2bb8f7b79c74e6493f102a4930e73625a77198f53e9929cb8c93f587aeccfcd313220e2e6ef62b3e344b29f56b47e7fb89a5b46dbe8213c20f16e1a5a6c4b80e4d1fc622631169fd0ad1cbb4eac441e81d62ed27baa431c4b61d93bc08e87054bf46f5615b6a72713d2a8545d6828f6b5caa5b45d9d826f6c8361e9f6e93be759b2bf23846a96ef9fc792c2f632b7963c2c1cc8f6a7aa359a66ebf186e4962ea169f625415336581e766e1b56d18a5c440b86b66369c6756ae5198ad97b63293d1af486d0b9c87124a80d646cae3472c99bbb6e24a5f84f87a49f979d6b43a97df7115c191a0660a137ee70e4d79ea0a6c58432947f42592b2cdc3d59071b4d3f32a38c2d03f765e2a6d635a749d4317fae3374ccc91c1b5ddbb51862ccde262b75ef27463ee36ac081cc37ee664d185da70ac597a47ac45290e187388fedc0decec8c17595135d769b9d091ba457fd8e5edefa0714733deb209cafa7711e8efad7ea480b72f3411b45b1184d76515da52d20a2bdbfa52b97c0dad8fad03fe8e36dd93bfc6ca62ce354275637482550c85b916d78c1d5e97d690d2dbe671d85e790e1de30bcf608eba62615920874a179207a1bf0713ade2154a8eef9dc75e3f735b7b1c7b9f39512068ecb5451306230db4939935385282307f1b29b5a01467d733881487f39f978d0d84d208440f158ce3a47e52c805b34f12cece122717e5ca3f7925fd62cd6d9b454435a6c67da91caf88a0846118e182038ee879801668b21a74cd9116a1bdb73e7f2310b88d5ca1571f7c6fcbbdbc91724f8d70f0c2c2ead649c3c7f5faedda358cf8edb6c05a0be98419d19ac65f3bace7af57b4974b7edf4f7b21ac15394572571733f73e4c8564c98c18aae3443b9389bc9db399585ed8e08eb60d0bbffb45efb548efb0268db2a8057c063a4c498d71a2b7b8ec3bd5a433b84e4fdfb4b25e38e1de1a9bd705192cd94dc966f50964d2b74d1c0eacff550a43581c0dd3a261ab9ee73f4c9b51863ad53fe0d8a88434b2cbc0d91735d6c24cff2b12599a5f19fb8531ffeb5cb1ad38ff783ecfebe388098841c5f6afee7c1c07fe13fe3d3479c58eea0bca668469452fbb86623380c137ef62ffdd696b06dc44a844d806c1d09da090260be1aeff1fa3f5c555e242948b274551b0fa3619cd588615b0238ac77a6f6c05328b067043ac30dd5d0a9ce3cd150f535b9c5599c7b0a43b83fbbdf2610b4fc9bfd6ba0d3a2d310e6e9ce056603db95561670ec639361e94cb0a26992ef803bd07ecbb5d77e6e664aadbfe18c2498e53eb8589f3015decb51dcdf51ebce704245dfe6f21bc2b06bc608d5640d707282246d975b307b64b6ae80eac2262a0a4a2c60c57a62b56b30794a8f19fafd385ab5c00953b61bae2bd0473f94919dbde731d03adc95476a8a766a1935136c6084e4559ebe3a2d288dc99e8629c4c7a510fb4f630d0d56266808891718d1fa43d718fda7ed8da0010c3c1e500b1d4a9343ebc0b1656d7873b8a40aa7cd562a90b57dda623dbf31a9cee71f0fbf1a31a81b2fdbc86eb1c0699802c37b73f08f18435f75c9231324bad39b52e9672064828c626644dca71f0cf56a8067675731291debae6adc623f928a2c5cd978f29dc9aeacaf3cc7d49f760a3aca00ee20a25e993b8bc7bd166a4a09bafca84ea8562e717120e4e54c6ecb9960761ed5a008d56865b82284d4fc2aebb9ffe169d57f4905cad938b647df1111707924c42658655e8d14577e28de4df21a2400f025a273623848da4ffb287b8411dc596f04eddb5051936e88c7128bcfee9e7913b0a0a55bbadb8b480ca6638471c1190d44a545b07730b61f6936597d10a76d4a8a50573c2862103e7b2179a7f95d5ea560b6d3cc276bbbfca02d26bf5d1558e444910a98f47b7802f2d1606af1d623a600b8e139c9b09e664b50a5eadcdf4d1974e04ce5777b51423d8773d3aa93099fc46d9142f56b30a262f0db99dacd770c6469c892efc177ae094885db975ad7255b77128765428c294a0d74b1e4269076ce68fdbf0d9c5217f21ccdf98f7eeab798549ac3e5d88c10c61f1efb1f5fbdb7597d4053ac20a931d88137494229573342123e1d09c3e7aaf9f4de60e96748aa84ff679f9ed58eefd2bc608f681a662fb7f687bdb6d27abadaa94b3fced00f6b1845feaa631f31b6088aa18445b2fdbdf007078ca4f2ce8e9d68f88e34c7d20d2056b1507a40639ef34c02b09cbaeda189aad71fe319ca33c16f3c7a4990df80c35c3693816c698720bd923a5a0f94c80f208a2dfbd9782c6a743fff00b047122ef1419d69e35cd30120196ab9d687ddf1954856f588dd091ddf9eb65f5df4fc432c0738d84ef37c56d509de3d369d6016549c10c993e89eb05f156e7f702484ed273c86d81f86ed175f7c73c680fb613e440ff69f782b0df235a623cc87d43fde1b1185da7de4c70783d53655a8a5e6497c1ce334d42974a0522cbb20ed9ae67c9258bec3a05cecca37af9d6eb548d619a5f5cf79230691eaa705dc87063cca1c9b27f759d62a4a5e5671adb38c26b5e80e44024f8e26d01107ab38ea01642500041b10c0e4858244f496e016b5ca6b2f8aa0d1f7a9c612866a6824c4a0d43d8e2ff27043e459e255a8c894759d3f4c1df005c5e2c9f90a23db9d041082453c8172e0a5eed7a9335a1400faf0291b193ed00f33676a8bf2427bd58c32ac006350594c80ac6a569d7268c1581655ab41b902da3375ca1e810da06519774c57400236d19eab72448408ddc6f013c40cd2ff70470e913e42d7a604c9f88173815b18e35e1cd0ce2688eda394c66c2c7f285ddb5fba13bda56ae06432c07b49d3b684cee104a417119f0cd8d65517b642b5d57edb4d55e6a23924d2ba35964338b74c037a1e9d6d6587db774b58b4858e1e8ceab71b40584f45704c5a25496217c2587c55257068bc224d443c030a1ad69518b228381f727212f1c46dc17adcd024386779e4c22c4c66713c1abc80ddb5b4b434fe97b6c656890493ece0a63cd567dd885327062ec5202999f7b148d8b5d1f27a0df485495d234a886c5406e6adac905ebc6750c31bcd512651715baef964dcab4739046cf837cf2192abf3df9f21dc3ea8de08693cc025ce6d7db8a8f2c8e5bd8a8f1ccbf6642faae2e53d270d9d9cb485ce4fb32146c987b0595c67b30831389184ea7c238750aee78fe2b350330eb83225a79baa4b24b2e2cb5ba064709f74249861228ed4a98a25687b4f2ac7ef205415c2e25c23acae032d43659a2a6c130ae9cf5039248a57999a630cdf0b43df6f8476f9b0c8f1b171a9439eccc6eef2c2e9a77f5f4bdf00a7a2fd4d0787c266550f431e9e59879a5201795995f28f362333d66a9cb3bcfd7a94bd7212aeefba15ce66f86b58b0874e7e382782882c886ec25c6fb1009adc8cc0cae8dcfd852e6a1f3e74e1c30732738b912a384bbb047f8bfe55c8b6016394cc6b6f2ff1f0a333e29de9e36510fb9f8ccec94b40b33eb4c8355709eb759aa0dcea8d2d0a48f7c235be26a590babedbb1182f3a0869cc3da7e6a752311a1abd002db0ce4fb6e769d8047f8f359454ad896083c8fb232debcddb63084f95dff3eac5fd8d5b544e9ffdda06d317fb341dc2c80701cf18ddd34f9b7c5c79e8818785d0ace65061b45136e33cfb6a08868a5743fbae15b6f0c384c9a8d6e9031c2e77614f90dabc44b2c8d0a8bba4bfd26b4cf45784ca8011d8ab534166d8e4e537486cb70eee74e6ca31ae02f7d1b00c1242b6d9fa4011c98d6964c3ac9aeacc072c6acf24b5cdb0addeb1d37e4c59902a2fb3806ac4ecb555531ca008fe6643baf99dbbf1abfdb1426040df0a9dbc1d3455f8a6aacb425233588480bf0614be3aae8c6588e80519323c2bef74bbf4d6b06ec202644e3e890ca4a3401df9f0f9ad36cedac81cf0c07d6a074847099343cd99413b9cb2fc5e1b68db3e65201dd9a66c0742f2e8882b2fd34e13f59d19010a4ad45e26005145305438af0a8baccfb2c8ec3a7d1372eb5d1f08db7bb569edecc14f069d1690db969e04123f0948a778e8860a0a8a470d34e3bf0b86a1571e1915009dac6312fd1047c7f27e839595a7c85b21f9c818f5812698a1ca6eca316c3878e5c856b115cd05dd82c97fca4e244e7324ae82f6862f01f545412d2694f2581788d963fc4d08b0331686f1ee27bbd2af208744d85b1bd07e8ff846a7257f21b78b67dfca29f4bf81698f841f5a558963fad1464e4cfa630a467256bd9d97d3885165a826707bc834c932093ac9ba6a12aee7dc82fdbb1a49d6094f6440e4afd19ca476fbe2279a73bde77fedcf5966686dfa765c9d3c5a96cc3f64bc488a98e5c518ced45900cfd535a11942308115dae529eb01b2162097c08d6277bdb34fc89c690262ca9e72b195a7af490004f6ef6895d7b790604c015e1da584388fe720d07e6f97f1a69bf0964eafc0198d909a45d3c47bf6b845f54ff3c8a94e1fe18c8e84278a0f79d43b6dd37c578856ad72974933862119fe8601c4a6fb45b96c605b962cf3abfc6aebf68c6186fef335388fa5b032a5c90d0edd6f8b75566234bb426a553e1e4f68adeccd7070af4bd2dd6863c858a24cd691e7ae416ae932038bb70f33f60ff10bf927e11db112c391624654a44a3ba6f3c9c9d4ea0459c44bcdd335cb8a1ce04b6728408876d0eeeb131cc8956b4ccf4d0507dc44cb40afd8a41d4f53e87fc1243386df36cee4e8398581e743b048b9ebda0d27b4e13993dac37d5f95ff364dc4d4da8db034236d73e79b3130ca19ddeb3ff962d719e189b224693c44120d0129998c3e5a18df1d716e81dc0e11e1238999c7ddd90d5c6678dc7b7adbf698d27b4b1e77362e676ae6b9f351db43119ac9f578f1538d12bad620da1b24edf94a948efb976b4c6b1d03b94ce948e4294edd8aa9a8bed6617a86fe44ff9af354e5e85ae8068aafd5e8b625bb331f0f5e18724eb571ce9ac8dcd10ef97ba88964b188830bd005bc25faaa5b725bf6f8c68fadf333f3db643493a692c22e60c1fe11db7a12549dc4f496002582a697c6097500261f192cbf4b30aff5a3fd8ab641005839108934a145c06b56c399e0ef5f7033b38ddb2a06bc25c585307faca39aeb9523653302a7dfc9e5144504c9b11df4c12231d4494606c68d01fa7166a18fb8d7c0d3ff5a540098ae75beb8c23e7174abf27fbb395bc6fd9b7a761a039860de3b81f2a76c387eb0156f76a12f66742154d0a8f02b23730e9a0c1c3a83f7c796a38e8d4b4e02b0e2ccd6ac10f858f69385270139c29c15da00a8a272a35c796fb3b7b1a099900ef5dc1d85711e452675beaf2a52f83008da97481e1bd8bde9d8482a3332f79819e18357015d8c3cf1e097ed57a1e552ee5952a21c3670e2a6919599ed554d4eede8b8f5159b3f1de8a5b31e92483e9a6dcc82a7dc491ee94ead0782ca6c1d7b78f55978429ecb85487015203024563fb87246f83f92c9d905ad0e1cc3aa21521d3da92d57602017f47a869426449b46e6b3469e7c2d0b8f0608a8051a922778faf053b18d696a0539360eec039c8bca3f46e0845420fe515ab29294ff288e04096f682059715a895b4bb95cceffe56d673dd617c6214bbca0778fb6a41a8610de019c22364394df1f4095fc10bed89abb41cfe8be40720306c6b2fcd9f9ba6f9411bf70e9e3db1a559a1a5faeaa915a27734eb0f325b9d4b77829c944df0d4e5de535544d9bf67d5bd6c07e6dd022463265213c12c5d099ac94093a86e779916f97b6011e1a2c25e823cd798a5df5f52d6dd5e7985c55450816a35b52c739a7f6a0fe171c91aed866ca93c9a3061bdac9b9f935704c4ec3ec749c584b59af4fa74dbb999aabfe63784fbd69da844c9b2d23d052d8f0e9ca4e5b3b049a0cbc58efa0f7915512ae7c6d4796158ea284a928d80bc86a5ddc51bdbcb6df320e9672a8ed67fb96002ecc8b5e35d766a4bf1c595f14e324cc8dfcc56d995527a82fb45c2a6e9c1f1b433733ae57f77696bba5e82042b819152a207ee3b4d2938b0e27dddf6cce6424c4209dd46d2e81eb8ac526be64921804c8faa4b830776bd16733e8b503d550589fe927bee2823a914d61b5cd3f7ed57f2c4f497b3963da6a7efe4ea35dc91ab127622a918692bf102d134ea2f92420ebe2aae0e74dc46d092aec60996d0c14d70651a34686528d41cdb5918bc93ec9803a4c68089fa157445223f49ca974fa0f6e184bc97f2ce4f0f096284ec379919df51bd65cc41f7c9616e14e7dba4531afac7363304acba8c7833baa0cd1dc0f195ca277ce62edf44b0232391970a44dd9151a1e778b68e389406812a4ccb6d06d8e090cc8c591b5262603e21c268bd4dbe451c1a19f567e910ee9b5e900b305a88cb4a4734ea2418454aed810d4b2cac181f90575ea60133aac13502fa3c46f93efc2eead81fbfd3cc07f1c117f71666aebceffcf2485901fd251f60ad3747e1c6bf9e8ff1d85bdb4abd56c37379e535482f6bd1f27397c4f404a86355cc642caeafa518dde1e34d50d9d5682bc264c29ca8e4b18eda129a064f983374437cff0fe323d2071cb6796e22fce388c64b8f20a089456e07b6b7298a25effb43d8058f30e4adfe0062baac483511b551f0c2d0f2c2583547b958e4e72ff2f571c5db95136f027d518adb63de600bf84fb9133f0ca73bbf71045143f1dac12672fd590a4d7c24e7772d2b6dca2c489bf899d8d9166eae47d6d94b8cd63554bd9b152dddcd58ae2730210dea686092adc4910d583e73394c1ac40300e30147f288b55de394e6ce8b496be8041c890a4d90bcae46ef704a5c07d2e1a76c9a67f32f322bee8df1ef69f4708336e172c2d1ea3e7e236db4b7b80dd5c0dadfd4d2d88324962edb35792675c384efed08900feb7aadcb4a31b80d746bcfa43c446c8ef2247a11d64d7a86a2033066f65cba4c5745871b891144dcb3479d6a37b4980936b5e8d33d25e5106e121dac0dce265f631b06d0bb8e6dac142c102da138f909303346d7dbec20d0a59d9d4201ff7bd4c909dce32131e2a9a033c928749902310f1dbfa08d6047568134bfce0c346779733d170c380d9a845724fe59e3c56b0b32d36ee74632d2303fb593e376bec658e952c09907d8a5803b5a78b4455656d6a775cd28b628eb5a90a4a6988929686b08fd9ca881025cacb0c8d9e68872403548e8c2ca1ece97db456cc75d03ad9eae7a285127631b7e8a03a42da75bbee057dbe2d523bc5974a85b7f7e7e8e300d94d54f22581ff28588b14dfaf64eed36666401dac14cd30fe2817b03eac45ed05b11b5299d02dfcdfefc7765fa7d8a011609cf0e19a5f8f45aa2147dafd6dbf90a7d1090b5c40a5031fbb6e8e7f2b0749585189c0dba2faee2de6ab9c4b657501ce6a40d6a4b7ad09c0ad5e48ea23e6fea3b0b19512c9e7f504d47698af7e592fa3089aae3865862e42522f8a03d5592f0b3d4ce128acbfd264959b8c09603daeeee74a5877fa590cd78a5072d4e605dee43033af4be1f3845584abd4bd962fceaea103dba75972db9dafcd879995d966f00dee388fb2d89fac64939cbc772326dc5a4ed6e7060a0a9585a5378a99afdd0196b4e23dbf6cce16be74eebde880df270a3496020c503154137e7a792493653a2e9b114f4a740f1a0a4f43ade515e6bcf13bc69affad26de9ab37d1b31611cb6fb5ab60a007265bf5dcd07e0f03f82bcfda00e195efd76ed71649e156b993ebcfcd3b06bd17b445856255e8dcfd9292e36341b477dd675b3a56761d39fe8acec33d682dfac3289fb03b0464ebf07a1b4d4b7a7e998c7ce8edadaa04e21c574b86e26758e8fc9761acdec80ac30f3a66ec501c4a8e9c10d2c1fc13ae9db6dcc97695b18b35728953a14dac8067750a8b8c4e1b68a98f9fd4b9bbcc1634d6833c5b0b9b7cc110cf48fff5584e5fbd6b17fe4bb78144a744d332102ec40459f896bf8bd0f2588a909aa07dad2c3812ef126c5a5d6c4874fbf941adbf9b507d008fd9d0beaee23ce2df19394643b5fbe0c8846fabf3f9468f578110390f63d1490e278efb76912ba073f382fef1b5fc966f32d4c8d64ed7256324dbdd7edec47f9bb9d7d083560ee158f5701df432e6a21b726e97b0661ea998873fb61c9d05111712162133f721d32d0384f42811008af5fc9d37d5b2a86f3a0a09828febccce239ef191fabc2a29c6d62f94b8146df5e64ccbbcd1c7e3850eedfccd078d5848b62741e4b67c39b40f935359264571686905849f181e75bb259692c9e9f0ecefe256e92e2414a91fea8f15d49b30393d5dd5bdc2e9f8842fecf92621c320f44b93935e50262cec943e8a969a535ff8096e564ca3c4b4c310b077c9bf033151de81abf486e16fabd07eb2f7d1de5c508c52ef23e6feb1527f8a03644c852c1463d2461fb310794e49bdd448c6acbf75cea59971543ad3a4b76c423a256bfa3b0f2c809bf0aaaf396216481616c90006efc66ce663984d3e269c8548ed21e98a6bc2abde9ae38f7b161e1a2c9cd76c29fafab33128686ca65bd2256e807a87303eca2bdcbbe9c3fee03d4f50d77939a5e6e5d6d8cb3c0466188e15ed1128259eeac72ae041e4844ee03002d30e3fb1a20e8bc60a3cacf6a8625bff54d26dc4c8e0174fbe228a03272c92fd48c00d82c0ae38d2bd40557288fb1e743822dad16e96d7f80625e590ca0494d82ed79bc42976671c664056a4d3913337788aac5f4dd3e1b11359ebc67836e2ff3f22ab6130630324cf6bba701f20e903711106f1bd8fc84c63a8fce709533f568041d2e1bc20ca5f5b4874fdc1ffad43c0bae10a1f9ee2ee1d248001189982438bec302444d9d6a86ff858ced0dfc082ce9fc1eafd6dd5589ac10adc7186510f1fb4950c661aa3672dc36c9d9b44fafe73c6eaeba471cc9c03a0fb112612d07c1b48b3f63bb3fc7586a68f24b424d1b84efe5c9ba175a74c60d286d38522627f604f8ce15473efdc184e2e1d90393387e84b3c7f322cd813046af8fe91527e450a16193c7a6f6f372f9d9e1421a37758091adcf96d63bc31989a6164fb1c56e21251fcdf22c36989e376d238958d839e42acc45d1b09cdf77e225c55057fdb9a1f76eee40933252e44737c1a563e96bd01b66253710d2bef1dfa781ad6151b7fdadeec2116ff8146b42a7744a562dfdee4c586d5bec43a9f4b5ec0699fbd410bc864e2e4d60083324d7008e3909aaf698c06685235298c0196c29416047292d82a65be2e1d444320f41fa59e9094f7ea28cd13014b9e0f0d6b62d13cf4958be2aaf35a4875fe7cd2051d1c554fbf7c6a89b2a35b639beef2e4999569c09322dd8b93832c5196aaada1d617bd82413b7a7288ae0be5115102ac2bfe48319b9962e4508b6e143d4040b567062c8cafca1753a7ab4fa974c40b7dd494249cb52ef6f0b418cd1c63714eaa3118aeed91454cf6457c58b445ac916edc2b79360eaaad9dea4541e08a65085101af95c64ff862f2c87d81537e0fefd492267cf0cb64a1d4506231371d0025a3ec26f49fae564e90bb0deb05cff0cbf938ad5960a579d4c722ceee12b94e398a810dcdd42dab642851df4e768bfa60257fdb42fc8397055d213b5c1d4fb8ae7b4c41d041735180ebf9b8e03b4a7d4f0bb8dd2cf855f8360fb0f6785c59abc7e5382f48d2ab7e5bb2de5658f21c4cd6f2e46f4ad81bf39d8ac3035c6a5aea5cd8b087553511098734ac6b140fd29900e642eed42eb008ba199880e73363896a51a9a9b21dd769e636bc59628232f512d545a77aee4f338b30b8558dca44d7f6d21f6cfdbe1413a475c27f7be47ef44f28f38812166a0df805953bb0d53325038f2a5f93c1ffa9a1e6f966d13fc0e7f42f1117d71f2b15fecdec3a4eba3e63e4ef9208d6e189ab860463c61201262a1a3b20ac60386e874feb2c00f28f966835b369ba616b6fc361c89087935cfd2bb8e4cd7bef1788f8e48367b0043d589e5f1f6b37737e0e68b24aac0017d292329073b57573c7761310acef2673883a4257ed2190c9fbab357c1cbe8f513aee7ec689503f5025f61406121a0629070b9e511165db23c12ba529d79d91fe11e3a6e3fa695584146310d744560dd4256b231c389688c75aac8ffac40aecda382afdb7e2a80dc0c12c5e4f825a1d6bf2d9976bca68a85cd3c4fc4156129c530b2b22be887b7dd98f1e874e0eb975ee091b2f96a08dc7b64b098e4f7c2a602c93653c7fb351929c351e394b438503389f50341a80f45ae81a65579a3839ba28175e99de063e43511bf632e472a74ff5abe7c1d6c571e826d582f9efd3e0131a9b3cb8636f1b88bded6d8b2280200ad515bd993cff008fa1bee5aff9dbf9672498f35718a583069fdb221b23e483ab9ca6b95a7aef103218667bdd887b4567f28bff2c80b71290c2c5cad47eb1b3b4d81c8c0c4ede8fd1898e25d085a7891de7b0673a194e36cde3efc8a97e6f35d5b5b89789ed9264e992228182d7e1ea2bed529006a4f2a4813b124c81927ce2e2194b1e44510ed5e967fabfd0932d3456c258fcdead7d44701afdc293a20c1d6f2f31ff5e73f9e346155835ff5998ef86cbe6af1c4447068603730c5cd1e7b8cb2135e8580fd30c2622c43b0f10ecd4bdd54ecc38c7ba2ec74d5f2d6cda9bc5bd0a85b757014dcdce761a5ef9f6f206a55752f24b57e57bff1b0d328841d52a0f1c92cc6841b92ef1064e19ccff5d48c27074e04f7a186ea930e46ed367012baf39b5a19aaf2131311620ebf0a0da2464c5b839f9900a63abfb88110113fafe8182d5b13ef1c8b4f7ca6252188d6c3556471499ce375b0c9cb43a85654918d31621c1b56a1cb8b782d527ab81409e3d8dbe230be0392a283766738d42d059f2c9125c4cc07c359957f4ef7e7e12468e1bb880a009b65d2c92056c0370cb0c692ebb9d03cff7a4e8f37811debae3164871b6249619a087e2fece9d2bf9240239a806611be27a2036554accbf1e200fc5f6dbfc3a175b042f0829d8fe3095f905148779113044f1e613456dc64e143fa333a468fce4ac867f7000027f5a402a847561195fc6488395bbe444977b48b0d7f55dcff66424269be59b70e8fb8f0c07795166091932cbe5145833a5510b5da769baa9563fef88c484277b885141f022b100fa57989998d6f53968cf6c516aaebd821709922249a7b96268d962251bad8518e1552b4c6e27a5c3be85995ee6597410b891379dcf32d91f1dfe788e15e43dd75d3bfaa8cb82e77d76fe6fa5d22cff9796dfa7b81b3d2b06c5b56bcde3ffae6195cce7e7f2acb02172ccf2d707f8b6c1c1c9f51b1a419cfe56e88f9104acf05d9d20700dc70c5f49ae31e3eea3ef1feeecbf6154cc29f34a2cf865a947887523087fda533c50cadc7ad363f2955705b8ebad7095b878128cbfc50ee7b77c573ad8c06509705d8ff3f5ba952173aeb7611e81846959c1835912355eb9bd88c2953a1fd62c038b691184009059da454881d67fb3a8a2761419ffa95078a000001132ba1985f022b5d088f9ab3206ca8fc5f3b50495f857582e479b74ea1e82b8400b8f6703ba46e95e7cd570be9672ce4e9f72fc92642f4c8418f67d02cd73f0ceeaf3c5ea622e596f14d918ebd57a611d7f6df0787d6da909270c3a41c327cc9c160154c46b441cc6b48432722cd304d8780f90fb016061f4812d781748236775dbc0439d977fd0a652f310f0ed045652d3070006fd9a61eb22a85544fe604648d8aa570b5d32bf043cc05e583d8469e52e1ee4798343b2c6d2051a59e62af499a2ef648a81ba8f93d84fe761de66f700ebda4e5cef57d672f85fcb131d4c684f0f7aeea50e655693d31a7dd5d99b61a870b45b8b363190537531565bacaab88de8c8aea80632c92e268bcfdebfdaedb924492efee02edb37ba33c45e67acc39a680992a059fe70c9e9f12949a69f9f1b20cb388c59fd6623a5ed22a6aff2c53b7585a98507d7bec657caa535ae1d9f868669d515a9169d9873f90455777e5c0403c457de5c01ccd5771084e0f0e7892a59a2b62a097aaa1ae12125e2f54625f8938578f4e2f88c67b5578dc66f1c628d02dce4d8271f84d493e766764a97cb1b97de76ce8d2f1aeb821aa25d169779f60059ada4795ced5403fa1bfa8ec39a8997fd8a9a38643f9916e8f9a79eae3193f2816606c7ab53161412b817dbe29e31577f554812a0e5dda955380c75b60a30175e3d3760645cecfd134dc9157703dac0c26e7411d360697b2116f530aa3aa182fe64f3b1e4dca08762555e3bd24b951f8df3fe17ad227bad487873f0b945abf505ac964574afa7165cadcc65bac46acb209e72952adb369e1fcf3e17b737552f623a827fd7a7b6777036e5944df0cf2aa26cbad25d7048bc4eff5f7daa3026614b19466d4dbc8147df40db15cf67d88e8ccf68e22504161fdd8ab30bfa78f4836775ad798932aa3e8ece402559c7461b6b03c9cf4ce2480dff4c0d3638ecc863081f459e25f8f22c6863baa96eebb7f43722d8458129cbbc9cff903cd9874fb7e6c80de0ea3767729cc1d162fc03581863204d0de470b68a50d09071ddf85408ad88c3ca486e007b34308875355db8cb79af14cee4dcb69f88a750f5eadd2d16b9aba3979a65519820a6c9473b3d9b0035930795158de001235319142879d526dfba3abea5637c8ec58e546be3675dfaaf9f513664272ba93c69b5f5720d9c9db224e3fcc381d83d8658336fa782f5bf2a686695dbd73a751043234b7e4e240feb5a6baae21370adde07cb6e31ccd6ea63a4ea14464684268a5afd713266f56c06a06558856ed25c5033449cf6692156287ed9193c951c6cc61e6e6a1cafec6b56a802424b318a38d6d1f93274988ee9a4347dcac6410437336ad05a44cc9b91c38a3908750d83991e869be8627838b5df9a086a37acb5a4d2e9deccb2c631fbbc0f699db361567c4602bf11ff2cd7a235258538420dd6a42ca8fb9b54ddee0e28c37b7d0880be406e1f60c8cf3de964570bfb1fecee6b44602803757da4b245b5b059b58578ff010bd1730ca8dbdbe36cb7543d4081e2dc4f5442d15a895b4a9da02dc0cfb1e1a94f2a982320cb80c0fbfa675d123d71d972987c2c9215be8b9f93fe6723cd2630518a16511a0ae06e50f6853fea81e29c5ee1e7636aebb72bbc51e17445d547ba97cf1e4ac76e470600816e70aba38d049120ecc35665bfd0bcd0bbfe0e2ae2e0e3723cf6f20f7c301adcc2bbddac4f1adceb70d033f9ed58aa26c21829c986acc6067900e507426157ad881ea9974cfd67bea55b7b1a6f63d7c2ce1388f15269e919eac2a7b535c93697551ed92a24d4f39e8ae402bd265f3f6fe43ec7ee65d5386a63fae8d836fcddc59fd72be63c95f0e082e3b11ae81050f3f925dc68cdbbe2cc70dd4379e89d04a8916fe306657e7f78009af173c5678aa06a3ed143213224c40e19be0b51cb8346af9be18570b4205146565362f377e87afb4a4f9bee0fe6cddcb42ca7554f9dc7a5b0693af366e5652ea8cea09ba6c77d9a343a22d7badd94ee4c1f2b59d239da6a4e11897a7e2328ab7252378058045c4c80e5d2a8a2325e150d9682f678f67205e4cfb7ffd910dbfc9ef2d1bbb7001d6e7d268da013071a02cfe89b7d25611bf3a28f1d1849371240b5f85a3a8e9100d9ccf8bbee80226d90bf9a42bcc7303725bac635d448234d34d7a4210d9ad73c34268070fb3eac19b16ced2fe1a3ee88e8ab8136f5a1065d7c91cfa93c6786e77efa7e49c461ccb07e6d54d71a364a4d40110f2fe7ff77671ca1b54a83953317a26b74df2def890e07ebd687925e8e63b8eef58b4439c93122754ee36f0ba7ace488209c5f737b85232f7944724a735ef3f6a2f02e8928dec8b7ed99b39dff2761f8070a61d4bcfd02811208566d81ac4fd92b640cf0a45c5fcfd400c8bc38593cbdcf1ff0fc2f2891b260fb8c525ec70b4a50760bfac22827dcf81b93cf4581890e092a6dfc3a4fed3eaf885c1ae0c7a0ec9b4860804082e9c66709166026c147bf0ad1ef05d0a70c5332a27cd29dcfbfa769923d5b4392091b66ee8832006440a8d9f40ae77fb1aead67eff00496347ac1f373411f52908bcf2906cce3574cdf59808fbf4a804a975583c40b377dc6fbb6e019346b119087381303e8921f98e5e16310c43221b2fdab0b5658105052a9e75071c2df5f4641402422dcc3239cef0bbb90208d46f8f7ecbee0ebed8a8869d1a806e912b85e4c615b47956818b15a4f10352890933a8fff27384595fd47da97ff3eee8e8320c271859e3e302a8b3aa7b19728afc87d8d44a6a68813994d10253795007fc5a6cdc43cbeb66042f9f3ec699432543f8e0407a1e1baf485ee5fd1dd0ea3cb4f06f13a4ac44c143d1bac2ff64af3b656d581fddf1ef5ee7eaf289f0f7d8a363c99873795d8288fd8ba1e373406f97f1b00e2f6527fb73721f7e781155d1203c77f2899d65876690334209fe4ff2f98e7d2b63123e5275c938cfbe79eee9699d96d051a56c7446989b2a6cfcf6cd0ea79db9e69b15bdc9ab2c736f788f723be3253e3195a0d2f5e313cc6f9162fd94f3d18889880220dc56c7499b067d71f80525b5ac5c52703a7843f964ab3f704bba5717b721d860f39d7718bdc47d35f75f73dbed4907603589f48bf9de0a9c62d7f83dc5c93b3e38693c3260ec9879a3aa9295e2a3390f10661e1727092c9b9d1a131e3fead8e91dd1b52d77cb39c15c81e6d57c9023daf160fe2aef5ebb25e28272b6542253c14e03543abc7ac0e13a1208cf59382e20e6231c5ce02bc21cca140f0652298e22a9416d5c3adc2fc05ba3ee13ba1081e63f04cdb4fa2655cad9cb679a800a025bd39f30d298aac8eccb04d025257dd2ad73e8135d5e6583075c56696aff3efe2b16cd530466079ab718076ef7ace616074afc0c29bc84e6c988491bf6124169c95aaaa571ab2900cf1a9b044c00aa1622b816f256159a846e2290cbe62c5387cf2c09ae94e0557b1298fcdc4333b64cdaf6c21934040261cfbae0b76f46569853cdb35b073bc68d17bdb7b2a302f9f9caef63da4d6a7741f1d0a2f9a901fb7527d384339aedf306e8eb3c2effad6e4b732d0336266cfa5ae93d3ea428725d295a8bdc3fbd74372a000a03b6005bf2d6b3585fffe1b4a274d2b015906b816014f83a394b8a79a760044e96bbfdfbb81a441b567d59baa4bb817f378be323298479da6bcbabccec8653e95abed7731ed9be82839ed1eee7008c4f9afd0a94cd0edea03572c33c06df5716e1e37ca299996e981b1546e36f5061e103fa7feb58c639b1f61eb1f0444d64792d56c4f66012147e54f13e036ac18bb4103887fbc58b31405046b10bd8ea8a4793bd9d68aa11b2724c76115fa64006e5e17e3a36c44b74c564376230c8fd281ee322e0d9d8dcc29a03f981cea26fbe5b2b9b7624167c8cd1e4a57f7ceea828e7d642bd1fadef7ec86ee008120f7c08a7f29d5764afff133b80e0d6a8167938feac0bc83b55ad3242a4d94bcbb228145f47004f2adadc8087cf023117bbae808b826761f561ac50d4fee5cea116a7b844935802f1d38f42e433d392d707b750849f58fe906cfcc34fdc2d0eb2cfc095b9fe898b1969cba6f72a7fa1194289eb1a70d10df44093398b38dc46451542e06038f340b47d3a04c0de0819e131f8c0810881409d07b94fffc211665901ecafc27f2106a23bb6e1a6e0c6a6779b6921a31f6f6baf5f72ebf1fbd681505374fe0f795ffe3344b62bd85569a0c7c3bfa961d5dd1a890fe269f56651610c23920cee7a7bd64e23c905d7301c94e2d5f00a8445f4744df193e98d8aa1de2e820579a522835ea55a45aac10b63c02a8b388b9240d4b0501d937961c473d8b1fe6946d4dfbd3b754b35fdaf30789a6127263b58776062827d21b10e78820fb959f8eb796b219134c3f331474b66764add3310b099d9629a7631ab1cf077b595f9450dc7e67a36ed5291102c679c31fd34c69f404c0a420e0b49ad5dba73720f8426f6d378ce9825f7365717b290251235f39e333ce6c4ebfa33a681ad0ce7d62cb48105c84de060723252ca3ef943f9f7c08d9c481c4126dd66b464ed770173a39413257d7ac3b2edb1da8f37ebb1ed086df293f76bca98bbb1dbfb632d0dfa052c0b23f65c9c0b9f5e4e0893c8b2ce8d873196f41ee244b060cbdbea0a80dd76f1a41d356a63c2c148e921ca207579d9fa5845608787118ea18984281a43660e6b62ecb7f922c6a3e3f944d3a1873774294ce3eeab933d3e7111f402983bbe2e80074a7a3f3829a02bc3b0e04cb77a9f7314d9e19d4e47bdccdfe54f6b6d5192ad5d60360730ab6438dc10cff3c9efd0bba654e5268f06cae6f3724c17a67bf85f801c7365a40e4578e493e3ab0fd29261aab0f17ad1637d6a0545570ff388ec57fdfad6fa1c52670345c561a0eb6b5971f5b69704473216a22e5e9e028ed8323aac64b16b20f1234030af75f3a8f3ce6eb4462871ee8bc87794076ac42f8b6091656bfac7ce926680e3ad05bbfc6b31bc7811cbb9de0edd93c5830c6d9f96fcfb19de7c1ab1c34311830dc4b20ca06ebb9b6b26db3f2f904c508aea04b3d68c351c59325ee897b35a2f5bb6636a38cfadce10c9ca68e1d8ee0864f03a133730b03218d7254be0cd3cc43b338b6c9a8d037acf8a4c638e70a7c02044a57b7a457309c15a7034d5aa769b02687f9604938105f510ea829a23b1d045a7eadde2b79cecf24fa4d14fcc9426471723ecdb319e09612aaacc1307e2d8faf70b4336e6785bdd6c65e4a7820ee23121112d2f6eeb6e5de52a69452780a220a130afc21eb2060a67ea0ab41ba2a7fec1439b3cd2b0aac9f2bf57adac95c130c6388028b1ada40c136f9420d77670c466ab8502a039a30050bc8b0c3640d5090893d63a0c16656600417e8c00c4fe438b182999b1e482107ac2468a1470b99f83c3cb548f082e5c41623c0620d44608175576280220627c858031a7557629841a558e4e0a46e59f8b91b8633d40880baab3044f171c26b589d988ee925af4b8698505977a54517a1295af86c5a6c81468da41adad0c20bb5869a1654a8f14d5d8c4ed502095a4cc1842fc414a13db49ffa6c34750b34ec7d78795337e3a4db52764928e1506848b32434dc960f910d083c3b476451c404b288acbad33cf0d0d0703a0d283fe1d9f12533a091070b5ba3e38e4f1c9e5a19195fe230cd5fe83e3bcb137596ec4e0f22582d88a91107c704c316b3439390d698539bdf3fcadf4aa8f79d135d06340627e6a6411b8789436808823462ce1323788a6052fb27d0b32b7e947b3ca440c3e5298287a7eef8ca8fa33d05a255f468b08f4f4d2f97e5aff76369f3d05df3d0cf37c8c840430ad250da3cf4d71f105983c4eab4c1ee1aecdfa2eb08c408820149eda7b1c50de9346154bc0d30d480347c10d13cf132f363c0e6a1ff2a4281e4b85e3a15552a95ea46a6bfeb3a3a4187f111f6cb3bc4d0a4c1ad49e26b0e811999b81ca5c795bdad924b62a2d130fdd716ceb0abbddca1176a8ad20b1b60e16323b678d8f0c14994f2e5e4f9d26ba7799a877e0de4048f8086bd5319ec1d06dd8086b1d53993c6968dcef15494426d2cd4fefd72ba90f353c3ded1d91abe04b4061ac6c4ec206ddca712cfc969141653a05a18c91426da991aa7ea344e2f611cd4d6ee25fbf53392063ba779c04103bde16f9c063be75b094bb1357270703ae7ba82869ee3399e629b3e62435b891168386be3d4feb95d55d83852fb7de021c442b95aeefccbccccdfbe7ce7d1c30aca9f0f0df66b387c28010d57e788caa351ddb5631a14a247a38a00174aa59165818620183f1a5d04aaa2a696e4826ab150d5fe2568e4bbbbbbbb63d835e76b139bd8c4268639e61ef66d97120df22c42c3b935a0e1b48969d068e4fb4ff3d01fbda86a1efab316d0b055a019d08fac16232dadf29a58d3a8b055ada2f11fff22126922b55ff329d0d08d348d6a6b74843185f2b6aaaab6ff2cc065e217594e051ad29fda4f4d9b6c2d81d6a5a6d3d679241f1aae0e8f464571594143ea84204a303333336fdcdd0d4d75f70b129700c980261144892564124198e665dd9df9e28b2f7e9943b544129e12413023d09059ac4685b367e3d6dc81c6327b021da1611ba9c1a9fd4c64b60c178e1c81f6b7aa866cc473e1dfef29e0b061e30b36b5df876d127f4fe089371b389a877e1014687f680349c3ac92390565d23579270db3a9ba82270db33b57682b5242978a9e5525405d2aac50d139bfa0aafd5bf742c32485ff132f233f49e4a1dfb5405dfc5766eb2804e6a1fd5e839e9eb0fba2f6bfec15506a7fec1a9a6a73a186271a56f95bc192718e5f49a37c94d4205d3d102b36da1186260d6abda451bb842744ead61d3e4c1ab54ba042ab6e0ded18034ea35cfcb9fa57fb0812bbd3bb0308d749ed9797979797162f9cc999c981f12112bb33933393439f3ef6dacb103b80961e36d07f990e236df884d801b43cfd381450f92ebf9e7a79797979317543483f44e5e5779e5279928b177e6df942247607861722b13b333971d2b8adfc8dd424475e2000f5cffa00352f831d00fbf568a4c8fc9993cf8df2c12f5bb6d450d0f394da5c0e8d391ea02f9c3fb20fe7970463428af852b40b1035d6ebba2e9990ab100d8433b503580de235fe4c75336852fb11ced430091352cc97427b5e0e8b957560ef31c618a3bf0804fa1fdc27044665f9b01ff4200ec47d7c104bbfe987a8fc1092ff720fe2b1fe30bcb0476525542a551375368a43cfe2853d2acbb717b6b4b4b47cd8b57cf8b55bbe7e8da7d4ee35d017c60a72cef31abbc6ae3d3f8efb402ffaaed15f087a96ef01f27ad4e574f0940ae29a8b35e217f66b7fa3b5ff817da8440d7d426054eebbfa037b0ff8007de87d80be7f9746d8df0fecc3f8347e3fb0f6c2d91f41df2e4c087a0df4fde0be2450b8bc0b75f9a0e86486949e7e504c99212b3f849aba21bb30309ef44360bc4a8b172211536178a1df3afae576b0aaeed6e8be30f4f56bcfe269bfe2a978266f48e95b0244831a00e1052c38421537324c2c0f8e94214f7aaab006296496863daaf6dfd2d2049d3f5f9bbf9e5a8266a71c810b65e8420d52d6304586be132c5383c90c9991a1df3a8c17828098dfafb102e6c7770e08cc6be417a5b6f9216fa30dcadfbcce61e4833e5383720681c6770902f4fda0fe59638470cd3e172066c51e09ac0af13ab16faf50e2059d2a7febae8b99e049a1a0f147cf89f832fec6678ef1d7f3d8edfdf14a73e9ade19f258176eabaaeeb923246795dd7755dd7755d4d2eecafebfaebbaaeebbaaeebbaae0bbb2e24687b8a713618632aeff6e3e8e57dde4c7ba3e7a4b4ebfe41302626b4d1fc654a886fda1bf2e3afcaa61cb030f18786891f7df081071e6ad49831a3461b6c681d5de547048e06e36754508e72a753345238fb5cbfadbdf65561d76c5e38e2e183a713f6fddeda1b5c2f107bdf6253bbb148a376ba17b489304251afa78179e0cc89bf398c87b5bd4e0d234dbd3ef4ce622eaa5a1e76feb9601f1fccbe2c4ac35c7f7d51afdfaae8f31610aef1093a3ffb6c535d73f54dbd3ef4eaf5856de4fa2effc954d5230c6fb967ee6d0d5ed7778da7bce530d7771da535157c3a372cceede27238733966e4aecfde63e25f97b7dc0c7bc3ff7a3fc3a2b6bfdebbd814f797a886ded23e64b9a1ce0f6fa89c872dccfccdf305e7679ee6cd0779f3dd9b1e392166aa67de9c07630acd7e4a4e88999a7d733abe661cb1effa7cc89dba32a2e45aa079814837b706e767cf0346e7034c8dcc9fcb9c1b6d18a3e5e4d66017a38c0e852cbe60c19e76a8ee230913313252bcf49c903f5fdb2f4422a6fa10e2354908e46fe5256664e4f7909ffcf5fa632f1481aa72ffe368276654ab1b9b1a9a1134a192193223d35ea451fd553075076080bae15251f759c52c1f0374610058a80bb36c230436c2542c8cab549e32c28df0813f7ff21382f3cdd6f05fd18e1fbf9552a05b7715849d789dc0c9aa0b50eaae583b359c35b28e10b39882fe70e2f8213eedee8aa911ac31be78214e32f79b171b9cb36a32cbb2ec4f358c3315c35cae8fd6d054f9143f1fbbe349d0fa35910e6e1d3be272fbf66d0deef37b3a624c4c64d62433e830fca0c3b8c7cf7768d51760c361f86d38ccbe262f9376711ab631b700e676b0d46afc1c1ed8a155f9edd06afca60ede1afcdc8366081d326196314e07d78cd3d135be065aee503853630471f135510e06acc3703b963b143f7e73590385e4ebb8befd405ce38ce4b9844d81ab8e5490c80aba487801910afaaf201008040245ad7b1a6c8e0ffab0d37adaa7593da0dfad46c5977ba3c6e75c6ec75710e8a7068119990ce45e92ec419ea66dbe2d4adbdd3efee472441008b48540221008040281e67561cde330eda4615a51b41f506b6f6c5b681b08a48148aec29c2381d3605b9b516983eb5fd823a632332311b34f58820e6a7cce3997f66fea53608c0d1cabc82de6fd108436374f0bca078b88de2f28574e0088f8efb2096d366a977654a6a2bbff6eb73716da8a02fbf1b7b8ed6f441a1573ec118efc7c01ca2bc3cf4bfc198795342a6737e41aaefd31eea756c699c8760d6ec7c08b29aaff574d14f9b7b28c807af4c6e974687f18db933975399739d5025dd4d42e54b5a5aeca8053576613a06d770fab9f34cf93961ce585397733333333333bf3b5f1cc401a3348dbb866de381671333773b337b3733733333333b333b3f347f989d234ad8af213a5d544a7c9939d263a4d565c44b20d377333377bb311dccccddceccd31dda6289ddb3d46292f795d18366736b30c04d2b4adb5adb7e6b8ad4322ae45a2ad698f465b93d8a4b2b2b2318b7373333773b337b333f3a7039129ec46a93b4e94e0c49a48d3c35a31f1236ee427ca939d1c254da46b7c7a9aacf8868f7cf113ee93ba430c3fef179b4466d10e315b43b411a1b13b662342f9738daff5c664c805888bbf90891a5b0d8e7cb4dd2f8c37f1c8173f3c4fa6c8711b2792858f4e13247dd3aa2fa0f02c99828bb04d0b8975c2c3d140b7c193a381465ef610ff3ae6ee4e5b731b01eda6e9658701632a4179e33287791ce692601096e0d4ece38d4ecd62ab51b38e2a67d5c53f5659f906fb7802ca3ab0ea483c670acf6954f69bc9461aa9cde32989442aa9fdd9276f1a7c82f2e74b1ef37c8a06db6f3a77cf69b05ffef5a1e7e0ec0dae3fb855776b84520735fb88a1e2ded820b28fef655edc1ac4bed9a087a93029687e065e9d218b0c0402edc0d0a9d987a3cc86c3643207dbf08e5fb361636f74cd7e024734ca3f7b03644fa9c87e77a590695986a3dba3d160f619d86016ce10acd9dbc8b29fd9f68593bfb0ab3eb567cec1ce093153417cca3e1fcb84291d122c74501e467453b7358e40970955e8d47033c1a01d13aa60c268d0f61466add0f3ef6b2ade4addbda1527a7ebee1e7167ff3bb8db3e446a1f65f33a8fd1893a1f6cf6652fbb3286a3fc8b3507550fbfbd92413360a2d98c1f505260336453371220aade4999ee4955ed3461e69e4919ef346af4915cff4228ff3e8cb0b9b1948dbb02de4c5e78d42f5e719547f2643f56f26d5dfa3a8fe9e85ea1f75501d475f1fc69feadf5bc8b301fb5032a1fabfcc0faf16547fce1b7dc873c93ebcbe50fdaf2de4b5803ec4a6a8fed916f260681f4e27aaffb6953cd387bc6efb704ea1fa735bc863a9dc87590daaab78bb5b83bfe4f5d81afc246fe4716b6bf053af576eb335f8439eb3b6066b5eb852a85c83ca9f79216f519b8a2e6425d6aa803d33335fcfda87224dd3be2a2b00aa718d54e57307aa3f835a48f567502f289f4350fd197405f6b1f685cd04865d815d5d47a95be02f30c4cb6712b4bd81cd0fa591ca4375022cd8d7fb1be0fd03ad405580403a0d66de85828569904ea3ae1a50158bbaa2585082962cf114080ca0325c35a01df70f455f3827a5decc4c930082d64c252207c4ac6946c1a6e6e582da3f2f2fc82b14bfc8338917287f8706b86a34e9c5a8578d771da597056a04e2454dd46ed04e4ded6f912a04edf0d6b8fe7a8f32243fd1ced5d90ea4489122458a142952a4489122458a142952a4489122458a142952a4489122458a142952a448912245050a55edd7b83dca0b9b1948dbb890888e482593ca0a4b07a3c5e5c5864dc5ba51c51c9716181dcb8a8a0d316eb0e146eeb056544c2592dca9fda31bfee5c66d465414eae7de73b1b95abde236edf2be161b6cd50265d8270306ff7ecb6753fb7bdac8387575b26a3859b51fe374acd44cfb22cb61a930a6d09db1c59cbdb1a252a1d1e485cd6cee0edfb47ae536ac98232f6c62f3a6d52bb761c90bbbbac3dde19b56afdc465ef2ba69f54a72f7eef04d2bfa4d6b226d0d947bf24cc2ccec7299bfdddd28a158983931df8fd1b1e0953162b12b3f59a375dd9ceeec585076a9da1aeeee3a3225d363cc7a28c7ba314af9bd94921d8b18a5dcadb1d1a526a5ec1823c71863dc114143aeebba24cb2fe6741319a3157c45b4c2fd0af9f1d690cc0cc28981155090aadabb9d13df7de01f8c2934c61ca9ba42b475dcd5256f8d239dd371430d7581c66d57de48249d022901e1840084e35b3b77cc0903eb485d2cd4a5820b49a89aa85d88e8f7f76f091dae8004954cff8ccc01eafed7036c7c051a4e1017e87231644201d47d22a60f63d4bd023d2d0ab43d495e665e66e9ebd2a54b77f7df5d291ffb9d31468f490451a2976055a740bf202f48c3f0970490cc0b9244095a978c8e1581355b6611e3738bfcfefd7cf493de5dfe612eb43d611feeee2e7f478fbb7bdaa1fb1913babf55ffc57eb1d8ef31b62fc188effe7e6f90bbbbde4f6451a0fcb17977dddd67d3eefaf3ea76f32e8adb8bbd06da9ec2c9aafe9a741fee417e7209ebe0efef62d81bfcc9216c0d9faa9d15b51153b706e41a876923b8e5ceea3aea8e061adf7d3aea84ee533d469dea8fe144effa71187face5294cf59d63eedf0224d6106be1f814a85cc23d605bac0e43d81bd819b686bfb7b646196888b5aafff5e3a9754abbf7b97eaabf91eacf66a86ad8d4b6302dee8197cfb0dcc5a2e47babbac9099f02fdc57eb78644420d4f75b2b22168d8ef296c9b2194bef6578d753d22f6091ace1a87506d65652e87567a9216482affd45d8da10a5593da100ae3b527288e0968aa866d98d755727b8d76884109d860fc3f5597cf3c1c7ee3a1e1b77c7c7ec23b9b027fe5775339acac4c2f076fbf3056976f8e88ec71c87eb27cdcb78161e673b37621ad1d4bf72caf895e3ea4b5fbe50ec4e1ffd75339fc0d311ef472f070f07661bed734de6478dba0f632fee48533f5f41897e3f41a6fbf6dbf63ebf66d1fb2112ffcb979a108567567fc723e53a71744d619dfc53ac8f8f5c2d6a9321e879f3c1c5c0e19df0f9f37df7b21f6e3f768d4fcf8edd3a017052288f041438e428daf52e128ec8dcfdbca01eafd7e423418fff3b67aef793de42f8ccb6fd5c5545dfe86efa5ba7c8c4f64c317d2976f1b74791c8d81bc88892bbf205010fbf335cfc76a5bd5b4dfb155fb40efdb17ee0be1aabd69f49a37227921d354970779e1f654978fa4a394792bd3e26de7855d59bcb0573c150f483479a46d8798c2b1c414f70b5b406aa0e1eef00ee88786bc848d74f3a9129e2ac261e24f4a3f9ef81813ba8fc3415e88c4b6eafcfd8199746a65e2f4bcc1bd78c753cf1332abc69db6d198e0197a512b5cd8156d6045bc4800ca6722aa3fc7ebbaae223019a712d5bf1877b0fdb6356aaf1853a762380a1a9788221a8c7198f6982e0606f4178c8e7d8ca7baee318f727af4c84401457840821e9c0006355c08c30e76e80e5851a4129004305471e353051b23c8603e3b18c3b5832daec0031dd9822738b15488428d31eaaec6e0836aaabb1a830cacc0fc53a50fcec840f7e3461720a24a256baccb01c12a55f3d628fe667741fb4336d2c59c349ee21f99fea94ee3d985659763f25d82881b6f805d99cf89a1c1af6f0bd2af2bfcfcd018b718e90ffda15d74d10500b038c2532e23fb88fd24bcd7d7460b7ae022ac436fceede8eaaf499f027509c29f9f3f1f71415ca840b80261d0c73a5d989336ab08f7c04d5887fe6ed232dadc8e30e2a95edde8e02ca9cd4f583beca4419d26dc0337a18a2574ec5012ec977b00cb609de3305cd0ef99549ce52cef71fee62fc4fe7d609fa94119df3678faef0bc2d59f7c2b59d0288d92f132870f7725a30efb7c7ffac2293f07cf99348c7c1c3cd799e1b91352e4745cbf92050d69953eba06e507e16afa3024522bc9974da4c7d3c77afa421154ef7d498312a7aa7cb8ab26d2355ed334287fbe2f9c35e37460d5fbbc4983128bc112bd97ff162fe4da827d7285dd74377c31be70d6f804e57fd1b4df5eae644fa37c1af5f2f265168d62182185f1bb3070b40d9eb3e73e0dcabf385e79eaa95cbd7cce6a503ef7e187ce6a02ff8adf79259b481dc905913d7b42e6bbb8785b5bbcad303c20dc7940228b179a563c2051be0aff54f9262fdc4a5ed8c9277940b0f754b730fca32f9c222a431c356dc4565fef7882912228b0398521d62ac626bde384e7090b4af5cca7b67fab4cc7537352dad1e8ae9bd35ffaa7a3e57f3d030d922feab50131a3469e069d0b1a663f2d1a0cba2efb81b2aa9cfd1c59817a6a94ec68518c30d95834384eb856a8c8de68e7e24393d21459c99aaae330fd3a3a3a3aa1253e27a55d6849680994182566117b767c959127f250975fa4feb9c8bf7eabfc7474dd06afadab1a769333277b238a7f994e8be6021a66d9cf4fa3c2d0929ee8d3a830d3a98d0a37284e2a4ba7f3c1081a1c27dc2ab2a4a1a890e8781636718abdd13526461e87e99f93d2aecbbe68b077baec470d0500a906b03d645eac0e5ff663021a230f149a466dab73181e4f653f0ed3ff38dd0d35d2cd4969d7fd673f61e4a9fdda4ba77661ba1dba8f9e767c754f4d1af9f9a6692b67a02e4262c5fe87a862d98f02e2639f10aefef9f097dffc82708ddfcf1720248ddad86a547b0cb15672b8166aee789c3b7367b23cb532dd9d7fe183476a2c72b1a4f1d517953f46e9942f892d79a4fa124f714d1aa63f7e51c3b9e34b685c87480de78eefd4229d45051259b3fac7ade6ce0968c8ace9fc84d5d32966b964cd1d96a7e693278dc26a3f81527bfa3777be65de7c2bb630fecbfecbe5e0f83572ad79850ca5943e64c82cafbf3e1f52621293f2bb65acf192dddd933d8cdb0b672ad79e4b109f8febfbb743c698c3ebb1bf385e97fc5c80f0295336623e586d63d2973c5289542a9548a352694422954aa32f954aa5d1974a4f1a75a5d288542a954a255289442a914a17b743be7fa9542a953ed652a9542a954aa3128934c46844229146251269442a914aa49f4f323da9341a955ee5fd8728a9a88c462aa51fbda6321aa98c4ca55189542a955edb4aa552a9f4cce528955ee4855d45a4af9f8abceea3477fb9d2e36813cb6885a4d269246fab4af7cca974a4d748a352a9342a9548a592cac7aa627a92c79565a53422b189f4269591ca275b0dfa8f489f8f12e947aff156227de186c3eb6772962a3c0835f1da6baf2b5ebed245ecef671fc2a3e7d8153d215c57ce4099a7a50cede3000ba1974baa7c4ea77ebdf64673bf41845e72957b0df384ccfac3d7a0e2b22f2ca266df1e90feeb312f08574cbb3cece7c7f0af9f0af0eaf5a251950ffa75f9da9cf3eb9cbff37ae7f5d056ae22cfeb2a126015bdc6ed6dd5b40f6d1f86425050ed43bf3e425a880c1ffab06b0494e3f66a6619bd9b00510a5a919253dd95195635e707a78809e868282b5a50ff90f213a1381ae5d2df95bffacbc7e6b7d7559b9777cdc71e838272ed764e47e8e747ee71747605ba1f3ef7c035821af485b10a619f3245c3766e5ebfe67d5d2e478b7c803e8817861efbcbf311faf95a261f9373be94f37174e6ed0faea08f201ffa5db0aaff29468b2faabf0907cb84bbc2b1f54bcfc7f541b8ca3f352acc7ebe8fece53b8d1a7be3c7fccce386c05ecac7b05f0e08ec658db3c1d928ce21b5f59ebcf263959fc98916a56ee5999d0a9eb787b6611dfafd97a144f6691bee81e9d6681b268032abe5246c2a364c7f153bbaca6f95d090572b6ec23a5cafcfb48382e156afd8d1b97dd8550b97e8817b58077fed0b4150e5874af0caa93fb66d4fe580c03e3eafdc9f7b40efd3bba75d007e95cd9b6a9b85f11bb34ad92bc87ec6a0baf6b1cfd65042dbe6bada4663b90d621f20b186ecc37fc96d10093008518d1ff334c81bbf1679dd432113feb1fa178a805fc419b6d209168e9046a3202821bdc80c37756e53fe6caaab268a3c19a541cf116b28a9a7419d0e061dedcc16aca3974e3805883ef944eaac70417d04e1ea511a5ce182fa187d10aea25f89e23f1ea5418f423dd377a3f711a6a2a358021a7eabd93d8a5e04553269d027a703abeca441d11722c1ad2a7adaa0ffe47288beee0554fa7c92d5a03ff7603e7b6334f2b86261f01f6244228d4618ab51a4f70f914623d24808d7d127041454f4a164d556dd1ba39ff205b4f4a30f656bbe8cd228d1fbcb9fbd417a7f8de471955e889ebeff1023ea7d55b6380e08d1935ea31e4883f47d837e914a9e84d2a0bfe813a241dffa491eb9856c6d8d1aba32834ddde67288e266f2c2aef4259446f94ed19b3c954ff248268d2abdbf5c499d6c55deff7a011d7d88b5f64657bfe186aea36d643116d6d3a811e9b591b7d5e4459667cec4f22101d2aa91b6291291442c1a895aae3973bbcb900b10b19a3e967ee4715df1dcc7615c53f1da88caa6c2e5d0462ff2464eaa686724622855247a86d220338b3f92f76347fe30aa6854455915899e37b532a2c7b81ca296a7da8891d9716b245b3bb05afabca7417f1627e8e84befad4699400fe27eeb691456fddb88a730d3275b18cbdf3bf4248c55fde31374f4f910fde84b5f287ad2631c10a21fbd26f27470ddad41128d9ecb41f2443ff2443aa2e87dabc8cb11f280444eb642530ddd0312411e909879a17402842b265b40a410b4c35a25675903838dba64fb10358853832f53761dd85dfcd48e31562e155ad5f0a5625be5def1f5fa8ed7e772fdfcb6063fb0c129e487a8f26b9ab76d8df18107114620a1048dcd119c29564e781ad5d3a8f668a4d030fe6ad5e610a7064d1f0885865f5d3e901d5be7efe01abb6edeae0e21887f8cd41ea28310014a7b075ddfa9417fd0170ae11a72bd7e6ab4b8c5214458631e3880ade11ce67b83eb7e7b3bcb5014418df18bb102fd53a3fa5deb2cc9a5aa7c84a7785bfc9fc4a95ea441b7d91ade3f0ee35fab1af458d36a554df56f55d3744da358c5053326af787de1aea28c91db71758c4d04f3fea5bfaec1e56f3bd1d0868d2f9c1b9bb8043b5888bb2343ee096365225572099c4bc03d45eaf4bec18e270bd800c1c8f199c8a668c3ec47df6928e8c8dfd37e9665d7fe4ea7c3ab39bff0e2204c11846824f37ec011d3833fd0619c47832e0123bc87e45595cf4be4cb9f5b65fc811b691fee6e1fcc33d19e00ede129cacb937d6e33173f1886c595b25bd3a7a3f3bbbe2bc64bea609e291641011ab20d8b6dd826094f51ef37226c9f1abd7315fde9d6feb60f5d98cb39e77ae24dbc486b7e0cf790fd0cbd48aca16ce203f6853b4802700f99fb4700ece5fbc73813c08becf46cecd060bffb50011aeeceaac7cececefedc617f09c03d708d45aa7bb4954384a6c6d45d45f1a4b698d400d45d9521a73aabb2c45e4a6c1f4ae58f7f6dd817fec50501aaf17a2c363a04ab7b3207adbde689983fedb75e9a139aa6c52ae5471ed05529a562faeefb7908d12897efefb1375a9e65db14fded7f61b86f8e63a9dcf317b2d4f0358f629b73b2f4cde548a2327a1af3f9bf51a51f7de1a97202b64e1afc637e387a215e93d0df4afa70a91702d1cb17cefaf2a38f5c8e1e4fb0cccaaf88de892598982223fa213332a26f08cbaf7cdf8ff9fc301e87bf78bbefe225117d8b97a4e6657a54181f7b49ba67f1987062a7c8d4c090e93e28e87f2bfb4386d0951f42bffb212c1f75987dd10fe9bece61f6591e07cbb70e435af9e83c358d06279743fe72a5ef9b34fac21e55f4dbd1d7449e463d27b45f6246467342f44bccc8885e5bae87e8133d7ba02f9c1748fb131059b77ecd1be279e661bf3ba2dfb23bda1ae42f363863831349a35cf8e747569d53a5aa3bc680a4c1ad271a5ecc0e9189381399e819678267328c2b41bd2e55dd7f5357b3cf3eccb26c6adfccb22c7b9017cee49892cb9129806bf6da6b99877dfcba3dd6ddf669980cd65c0eac2e07c47c66ec72e9d1a3461c7a84110d5efd04da1f2e4bbe60341ba4d16ec307ff2382f8fff08456e7a9eefed82563505b45f5976199f598ba44346a5bb0ff17a63f6661fa712c8f9f0b76dc3089ba5529abfc7e2c51f93749a40ee30dfa779c820684575e10fbd877f5aaf1fe2c1fe9d44422bf084f75524a9e6e799607e329344c22e9ce534628997476f43bf063c07079626cd4b0ab52ca4c30c6862cc261e412ce6950caef27f04a5a2103308002490ac400e5075690b9cad0240a12d4a841065d1a7e2aa8eeca8b2635a4df53b42e8a2e80589d0960d05c5ec0a6d09840f7171b52130a8542a150284402111d91e4d6e0f6248f36b86d5c88db482cc09ef941fdb27fb920b0ef993ded04c40bc2d5fdb59f1fbb63e69c1f76a2fee4fed0e1757e736bc8974f50ecb98631a830e6880dec8d1b66f6318da22f5fbe8d46c9d7a837f268835c6c900bfdf4adafea1710aea29955eedb366febb54363147f63eda39ffbb147f5271ff3e2465e0f5d8fa399977f474041ec2d377713ecd171ec0dafd9c4e2cc26e663ca6d269bd99c9f3de6f998df1f67f6c39cd87287c2997a7dbb2d124d8c31c618638c0ed2b0719a1b28022b66a06de7041afea9533bc44c192c9723c66b596bb670ba6fcd97d60cb96da7647c38ab0c4fc66b1b112ac39362c6ef378367cca859b6458d5b09e843d9eacb48e3ccd938ad647ef41a67fecc9eb397335ebedc6253495c4d7c3ec36b250d768efc4fca2f8caa48a3fa6bd2bdb135c50bf3bddcef43f9c5f7a1a9bf0f2f22f2ea5e6e717ad937a70fa517559ee4e9c30b8a8aa4cacbc875e4e272749f7933bcef35f65cb24fc77cc9f1d8fa7da7063fc6bedf66783fb225a314a1a0bfa46c7ddf36f7b287be6b7ff93d766854455554451528140acd0cd4fa6c78e672c4f81e0c68c0a23e1938ccef9a38c8f84093d2aed5389e922cd65f39e683bc10f4df6b5aa4d9624d5445d55573d95c46e4171791abc8a5ba6878342afc9e0777c30c9a2faaa26ac61755cde3f3b82a11d1d149c6894e1e3f7c71a92e9a566bc68cff3c1f27ec79fc405bc96ba44de2e0f816f4a541f9377ccbbbbcfff7291c5e7e34126f3ae2106914f7f148cb87114995d1a6ca6fa491a22aaaf646acb961ce9873bef7a16ccd393f1e3f9cbe953d48301c4fc930cd8f3f8d624fb591eb71f0fc4774c3ae1a87c70fada4713cd54696fbe7a5c11b667cac33be5004f57b1c7d92e179160dd362b2a4a76850360ecef7799406a5fca86ad494acecca6c78cfc3f1f2323fd992acbf24cb861b5eb6ae22979146d9f0f2af233d6ef8ba185ea4893593b66cf8a2aae543d972f930aabce0a2fc8442ad4671517e42db835ebcad2e5e4babd5caa2aa715a2d39a2df948f237aebc96c5e734ade891161509053afd7b6511b472ffaf8ccd2b66bd350fa317df69a73ff74f8d3a79f3f7bd8631813eec2a6a8c35cdf8581b7602feaf58b450c7b91b7232f5ed775619a171b4cc2532a7f6da6d754fc4d98c74c3cae9a5cd2a871ba278d7630e2bbba591d9c9c7a3d6814d320f6a02ddc8f68f0d2d8e6f2a7af611848c61e1d27f14984d223d269b20a8598521b9b23f82f6d4e4ddb481e3369f0fa18bffebb8e4615502cfbf961d7b4125a7dae2ffdf55da48f340a49a3b0fffa4cfaeb7acfaeff6283d7973c8c590e73bdf619017ddc35ba5e5b9084b13cb58b3d8d0f9116763de58498a94fc22826f2913be98d207d213ffd683488bd0ffe8e68f0fa30d0e0f51cf6a12dd66bfe7585b077d6dea06914c6fd2ef7bb9c7bfe4d40d4dcbc4291974061d20a77a749917afd9504cb51a2bf1e879b80f0f5d727e1dbb5dae5464ca051fed7675eecc17f7d9dc3750c26a79055abf146e95c50f63cf64657ed915002884d750ea3bda6fdaea6699a36b52855c3d1cb5c10a99b45dd55949baa3137e587b149e5d5fc6262f3b5d7353fd6f9854c54ec8b2d6d9b235383d7ef2a0a911adaa8da4b1b8d0aa90f8dd2b4e7b17d3875b46dd2f96944b46fe6d1a036f9f713a251d7179f07f69af67bd2ae2260cbe5b0e135970b8b9efaee418e570befa25c3e6cefc3ab2ea86ea8ee4a8be6302f597edd653c8cc70b33e250b2aa945c8e15172fc6cb532779244bf668adb1344143fec130fee12f1a943f2efce9e0999595e71ffea2512ccf71441bfc72c8c1cbe13516cfe5b5f562ec4ebb0d1f76b54d56679dec8eefa065d91631110481e4c63e38157b97d7b64d4a9dd8834d2771273e893c114a64c59ea9d364c57111c35cb077f98430b6b10ffb34d8a837525ddefb0b73ccc5dbf5629c3a1931076f97875016895e12f073f06a5e067c6d234271306311e5f2f2a51216e57d8c0f65134e1fca27741fca2a54b9c2825a6179f92e9eaaf2c738c9f06c8a601927c44c5df99567f17eeaca17a5f2c756a8913855971f5c63bc90585dfe87a8ba78ff0dca7779ef13c235c617c6c73e1a0d820dcaefd4a0dcb69516e534508ddb38c61cf1a372aff8b1c69317723d456cb7fb5d965de93e7ab4c1f8d106e5dc3952f95d75b14452fec16ed8a747a34e38fa646af0fbce03c2d5c77fe4b37cf2577e647c2f9d556f5c3c5e107bcf8b0bf2c7f0e682fc276f5b90bff3e882fc323cd382fc3978dac57b7968eba7f1f688361825490e2ca6b4ebfe41f0f39859bb22d6791f9f764aae1c467e8cf1f1b797672e870dffdc83f7f2bfbdc1315ece4aa23748bf4bfa189eb7ab437ccc03bd1cbc18c3833165bd0806ba2e17a6e32bff8f7f8cf82e5ee8f2def3e9bf7e5167fc572d2457fcc33f72159f7f62b4a951c577f72bf8c7530e0667f994b6f12ebac6cbd02af74253b12967398c7c9f26b4a450e563a146a952fac82cb0ef1a157adf35487df4782ccfe3aa8454f21dcff33ccff37ce7e453f04ea7f764c82835975e3ad969547ce279cee4e4f9eada4e7f7acff321e38170fdbe6bd49b565c1c29993c700483e20f65dd7796a7603eeca6ebc0cf59397c373d60475807f961575f10076c6170c0e1e5121c70c0013bb2e3eb0c57c382f2b11b4ff194c58e700ffcc33ac8972e40411ab686fc109b41955de5479a68e35d5855186ab6a8d2a6ca58a4e230e3c3480487a41ca864acb6854dcf32442323000008d314002030140c070443b158309195c10f14000d8dac466c4e9b49b3240821858c31c610420810002200042303690300a424b5129e27f026d35b80ccb2359ab35245c67cbdfbef362d2a4e0b363573b93eb04fc5fdad2ae8ef57205706adeb73a1927fe04b2dc1a21d2c8a0346bb2c79755bca8ed952177b92908c7e6bab2f0424dc3a66778803f8440cc6ab4a8c3ab028b88d7f8a863323435af88d56dbcb87b717e18596d1e1d25a50591898508d7571aa22e816da829561425beb4d5899b4e24f8aaf00218740a583d20cff0250fecec50d4ac9bc109071ba492f8c074775661941dbeaec62fed12ebceb2ada734349c7827f08f91d50fce3979c95a3b443a3667622322203ab6d5a4ff88fa45788b2829fd5aa48ce3aff0fa8d45e8857a98b78d4f9245d86dc2fd6aab0175a4496b18b520ee8cd64c999493f44d7a9b380c10fbe10ce207733cf1863dd91c9ff8dc86fa097fc7a5318b24063c62e2f9b44148b71d0968e9238a5bb901f31939f91d8b254fd4c01060d7eb12ef7be5b2a146aa271cc45ff278cdf7fb83862b338c850d62fd7e02dbc1d240dfb98f51ce2b460b30886000cc4505c1bed79533a936d4b2a75176b94a3a38fbe53fe8c9fa23920db29c34e5dce19121e98b3ac6f43240ed17cdd45b8d72cf897660f6dac55511e1fed43c87d4ed0f032f0a65de432d561992c5da02294e842675146a344db845e6ba2ddba310d85424c939be6fa45911570e9645249387b8bdc2f317d03a536d2db57c435a43ea2b191829b916e4dece00311abd262fd55827a23f2cec656d85433b0d11ac9d4829213a22315c4d131e23184979672acb63ec9a109f2310c2006af9ec14fee5a270f038754ce0a3574a4117df802c7ae27e382eb903a17c02264d45f962273563e81ac4df69ef9ad9b4c2b0e7e8c692b2514981f06b958409ce47bd919edb9dc6a6bdece468569c986e73b536fb71559fefd5d34a1ca0e75fdc7488aa95b71af12fffcc9443f386df67d4aab3dbefaaddc52d286c03dc0c85bcd64cd53d205c399ac7c8c516c3a9335b597255a144d964bd3b3c6b8078f7c45dc799b424b08acc9724d6ab22eaf92442a61283f2ecabaeba5eb8eabf8e10259f1d6e8a3bbbb42e5fdbe93787d34eb07e7997fcf952628e90a58c16a044e8432306423a42c3702307e111c786b345f0371a04c4b565c6118a76e9b0022f92945226502550436700b2aae424f7f9db13d374e9951f0a5c8a96c14a44481be5465c464e5dcddd2900d50dd8c782cfd90ccde8451010e9a853dcf9144d5b309e69ca4c8126bd389c20421908ab61f3b3a15a9a5d9ceb28a8eb3dd39e3b7ed41805d37d0ca513101b4ed8bf611490a85f7014450a99492562ee07a4331202c2b51dc29f53113df5c53a10dcb554c950a84961019766c913224365273a5b5ac97ed3933566da56446caa134c36d59aacab915f4231e3a72a9ba79a174abeffe3f9b529d8c4d027ac4d5ec050cca1c807a1d395d1874191d3252dd9584bc213b44f98ef332c8f4804866faa4ba75dc822b5963d51ad7ab9b481f13da8c98df8454ef232c3f60e9a784c809cdf2db214c56bbdb91044aa9745215c6b4fa59f335565fbfb2ad14ee122bf18f9a4d885f42a411235608dc3f915b9f7b4144fb1ce22d2a2875a9cfed081e9d4068e40993c069c554918a845fbd510ae0d40ee1c82e2a660a7b7080f83943e62238e4dc2f8b8b35a999384a23100551710921f7459215c9f4bdf403ef38faef6310460282006d71dda8b7f91da59833624a9b538a25cdc4e6bb6840f623a438d5ef7745258c507791ee9c989b7d03d58373d1e244747857027f4014632af079fc41b9083537e48d3ce6ce41168f375993422f921f87ceb28c38bfe6489ca64576e9641369e31264d74e639cca2e024032fdf04ac6411cd1f1b4fafb819696e4d19ccc44df20d7532b3c6054b2e2869cc5ced781f142a7fbf7acf1275631c6cbbce3f09d45be86d5e857d435e42de27389c8012be76f7d792167ca1e3259f18b5f716746d051059ff90a4b94f42529a96f2c80af123050f41c9b2f60de826e83aa6338102a0f9facef43d388f7e54acfe90463dd6bcd5a2063108c389402d26f751f6bd6b0b5a538eaae4004aed13eea57d2c3d407ee46ab39a6e930f69d968072d1ba53d846c41d06746dcc43d38e6330eaf3ba16521ecd0c1a78d54c782b3e58adcd40cda869619337c90db82e132760f0de0cb4eb40a687da551df3a05d4fd588dab136d263f736b66606eb572914ee9bb53ff7df62ff635505f2c624289a64a317a78d5b6a71e8e02be6ca715e17c7228b484fbcb4d5c5cb77a7fb9697a7f4c2e11b32539e75b10f9770cceabb989640392b54cd69f1eea74caeb28bc5aaaf4465b4b9dda9b6b61e50b539cd51978e00c16f8c72ec4753ce640468f31821b23da7e47f4063798397ee5c512856dfd901defea43a6fa03865aa96881a6646605917e7600245a42fd163a2180f1b0d0e4fb5b7ef418610158db728285941bc9e4bd7ce7048702daca6cdff55ec05536dfdc25206a66b18e6f6c1d9c987fc09173baec42dd81d549b8af0c15af89067a00a3cab54168f4f0628c5a26e8e43268e815ce2c61131a196d7b5c2cf43f5e9b5c5d5ad9f2c0f39320a89783b042b3d785fe56e3b40e351ab89d1f6749ac75b2dbcfb5a6ab896692422a92584ce6f94b1df21153dd681106cb9b6e3d25085c2968888fb2e51f05b710fde4f786c43a376fbda4c76e312c4ff1ea151a8b30113cfa39582bbd9f2ac34dfea0d6022b62955d8c3f0006fc4e1c93edbf4b28d89757c3a68540efef7045bd48a22667e7cf9424682a9f5f581c900868c6954fb516bcd7b776f402f92e7666bf44b52ce7bc68a08616b7686bd41ad482a8d9eea248adc1266ae6282cf621c5aa35dabe346431b3dae45aa919cc8e9bf0bedb281969bb51c8b66be8657757c619420da4c520ae373d9f27d21f8e71ed9edea409d60ec85bbc5b3cabec10a7fae32460e4c61923a828a2dfdb30ca5c08758b67948bf16c4368224bf2577fef61b049e88c2734de36c5ba33c339066e75015cf4f577751a77c0acdf3b1d25d41078373bf01d7cfc81c4adbbf59b6f9ede4f7c16a583d4049a44f7671d135f1e49f3c39baa1d0dd2ca4cec688cbd074592fa5272e91207fc7384e473613d540a321c4d837be631234e452d9bf43939fcd21a07c64f048b75503742df27d37f82cf0d09c17363a8fd900d868df6a106793963942a0069a8e43ab7682dc38e45c4013194500182541f10a3466b8bd82c67331d25136626382e07b794656362100256a68c520c4bc2cb6c18c11089e32cf8ec6b230ee80b98ad8fe0f540e04e2d8fcf3c95e608551e0e149e1150854afaf0904dd30f11dfeeff1e2e827698eff3c47ddd27c4e0be0b4e45e1fe848cbf34a6e7554c55abdfc2c9ad5364dce563e336388eab57d8c9e44c9c6ee627d7fa23ffd3c4627d682a226012a2674198e36deaa2bc7ab2d0551f1700103cc4bdf38530513c2ed9776386804aa6510112405aedd515671ab7ec81f5f064b1fddc7479117b2107db75455c497017dae22983f186d4064fbf183a1d3aff43345554e3734955cf86069114c562df432d02d858702ac464c252511e2fee4bce751431fa1307bf15332dffee013b6d7e98d3f8ac22b68d3520305fb20daf73d80bb90c4a1d605fcbf90b0ff3f28cad1e6a99d3e46780febff866a854623d00bca328e910482b611deaf1663fd3d8e607129df0372cc5f4a7981863b4ed633c20e8a2ae9855341598d047bda6984a91de3c402aeb158bcf067161dec965283de967f2e784d0eb6fc0c6c157b7aaad0a0954b8030c4f2060b758d86cfd3ec0286048704eecdf7b038221b8cf4566663a7b468784bfc298115e0ab78047a7d9f45c1f64e33dcf7d78c7ee90f2c0dcf76694210a6216bbd4a28ea8840b08594a209b9910523271ac6a206ceda9472d35658f246cf2a7192d722d2455a94a69ed7050127b44d8edfe3ac4b2b693d85ad2e15a9809a9865369874212ddd846f5f7002756e8e32c0beb7d387444f812e592921e1504e8bcc9cbe5cf3ad2208573b03cdd35b063426332960cde9d86ffaa19090d92c5b9348727a2db30011b85297a7b4b0eeeee990af335117cbae0797506698d4027678659799e27d7172baea02ccd25d5998220ce6114c0321910be3202640c8f0d82456c2f4f1e0b22e48b72a8a59e6d404e7204af2008a62f94d7ff34f2e5e70491e39e9f0184bdd8347f5188d368ad382335981289c42ec7f9ef13516a5ef7cbd20a02d142f326d714ef36336ee6f0f0ee4ec52c66cc42e882d5c3277f1efaf9b97240073199c178c0c25fdb6195b66b83a20efdbcbc9e38aae76fd5fdc48cd4583b386888ae22807eead60f44bcf4352afa69d6ce6ae33d8d9d149deab2a22edd011afa81bdd2e11c262118d074daf1fc0c17d09b59dcecbe6b6ed4afc669ea20317d35b1c871d5ec8f532b25d0f773336a5043f2dfa84bf026130d5f2609deb6a4050a4ad4662b202328384694e26f7b04c57d0836673b745b4fa69c4c9738069a9454b56e4ac20c3fc67d46fd0c0bbb882e3529feff5580a13c043ca2064f3af84fde6df634331aede6cba960d665a0764bb9fb4dc2d5b186320db1a3dbb33dade3aecb1e92c3c59a4884be4af0e75b02ab4c90dc5004d6d982be33aa4653f05e4c4f69045ae316e9cd6a607f3eea4bdfc909b538bfd338cb8029939731fd377ca10868737743403c59cbcc7de6e80d9616d6ca45a78f707cf5ec0afcc88da93055957adf9402aca4460c016ac9ff9c7c20a097fabf0c9fc99b3ea203a4bdcc1d58a59107858f759456068d438e860bc5ec07b2180755fd71223a34c1fb0e295915594385a10ab246b97a331c70fe283a64c7a74053eae1aa453edc7aa9d679f810ca88ecfc4380d6698b88ba5817d52ea34ebf8c02124986572169a2f85e3e0ee8bc33c1ed187b2662583eb84ae9a5b7503675f7751ec7ecdb7c4b143b8602a8e9ddefb174e9475d91120eb87a33f466c33bab8449ae197187e69de91bb4184a51445974a11c0818d4b318f4fe3b230d4fd453cfc81f4b76f1493c70d11f07362fe8598803111a4420aa1999b175a568383d449d55f6bcc814a5c5072668b2f14072b970f31c1515ba409f8f4962214404ee66b3b94e40340d7cee37f91492d47e169f383fec8699736b7c1b575133278a713aaa17934a65c03c7b4e7f7f88206b488f5280828307696ea92edec7021064f72e328dc2fc7fe95f7b5dd4f0e0d48a542db8f4998c00e5df6d63af79cb9c66d96e610347d95fbb8dfcd1b85b285431d59435523e316e41c6319512287ed96c709a2e46f2ef689fb4cda4b51974c8ffc15c857008879536b58b4296143ca22d2c27bb1ddb0c41d4735a268dbb84daa412804c24f9785f7e8c80c2a707d41aec62bff39ea0441dba5e584dd0c3181781b862439e0badbac3574d25f2e00da6223e2eab7ee3e02284de2bbcb731b0335e88f8fccabeb2351a8da7b5e7e212cdaf47609f28d3882d974274efb69172da71924532c46d638b3f0cf1e4a1a9cf73bcb5322c7d2793c7885c90647fd41973d7680a0510f6a346dabed2bcc6ec91783136e31aba0a19c09ef5c59abc128a6a54ff59855a3daaafeae0299bd54dfab4cbba6b28baac8978041f98431a15ea28a420d30d69c45a6721537d4c32cc21119405dfa1f2d21fb775f15f3954721533c126d1d70a446fb52429097229047a57064f6c55e4f4d7ff69fc0600f145e6b876e19323ced2f69ead2088a68278622b874e586800907568b76424e6b2489f656fdd2e414842550ef1d5f1f7f9d61ce4fb452b4e7c98d2306d9ec2f6e2b30b5184bb1ca1f3674a44e7b860f0081a802cd785449cf3d44d0f07d3239ab81e66ace03433fb3c1f73997faf4d7507f5e7364a3cbc58a14106ab2464a631c8999fb9a957e6b8fd6f0b84f303a56bc651057aa500661fa3fa11fc72236de696d0503be02e656aa75a3c571423dadc3f295a58b760b7ba584108e895c4adddee30d7f8ade29391b5616a2d436aab01cce43b15229550090305b79d44d123dbf54a523919451f1555ca4a9284f70b54f13695c4b2dc678566605372ca9d212bcdb930e9e6215ed20070ecceece0c1480a9faa2cf4d2e45dfb18f90d48630b28e0cd05388088a6dee79a8670e2bfef9a79b8e9b2070e6b47d9faa7bab79438646ae689838fcf4ee7b50ca8c348a04d0ecfdc8352ae32e9b624404b685aa348450144a042797c74e21cb8dcec82a12f3c170e70a232b5ca29d00b4b20d14a12a93054ef7e1a09515f214e9aa6c9cd6f24fcd134551d3bb279d8f3317e9b63a24e3a803844eaf6eed8981326d8a32c23f3534742a24ae10b959130f25597677eb475c2d1c6aa667d849d64914a6fa2a860715b339c0e4480294af27ffd9d1c4ea5c8328ad813ad0cf3aa9074e52a0b8a4384a94fa6cbc9e4ec630a572bf20c8efaa146e59b62c461f159d7337b1c82722d6d732e82f4b672fa3fe92beeca3bbb0e4243b8faa76127d791097bfbc31753fef709a79c952940ea277fb04c193934e0029b2331adef95e2203c5a6ec58098631084d6e4c9388b4a54b2cf0905064ce84398087889d6e2ad9b9d298e7148617f84642393d735c7bf206f08cbd1a8c36f124c38b05546b050357294627b294b4a746fc0b0eeaaf2be6709ad319180fc3cae3fdcddd2ab054fbe31b2bce514ea46e4167d80421fb030d2f2077d0c1b2b9859f6c417dc55adb0aac9403b321d7ea12cc6fcf06984fec75c30de323daad62aa6e3c4fbe63da8e2b664347a5801d2d822c2cd71a22aba24d311071d05fc76dfdd0a15917fa145b6b4901e9812cb2bf03174a20f71f8f4f1d64b86f07e5f4fe0bd4b2049af547fb97074ba5497aea7557b5433a6fdbc0bf10e301394b0ab712cc7a3f4b3bea74cad40eafa5577cf2c2aeb50f3fd2a0a6eb2e31c853b53765205c551362e935fd9c4f2dc832329cd9fda99c445270a0c037f65e4ec2ba18c2bb6c832ab15474e7d717b512b81e06ba258ff6255505316ad6100a5ac36d6a693171d403e63727b5c4ddd3c5b0a848e13aaf144ab8ec750d717adb8b6d090f4d30c04db3279b8e1277478e3941915f73ca6e363b4ab34398c4148ec2b74041895debf885d8c075337f45fe99c9b32b6937e6f7882d00d3dac5ef81eb5cce3d66e3ad9008c57a86c48a45918425b08ef040c148cf3a42bcc357319e25af507d76c598144f20ba224d0db51994e22b09105d4470660c196f0b8487d7d3ab19d81839e6574cfabec50271b80ed6eae0e2214717e0f31b81730dc2dcfb56ed2252bfd79c9ccbe8529ca6eb690a9f0f0ccd43370c8c7052b02848ca5557e33f7c1619561344957bd37b60f9779ccdeb066d4d268ba855819bd04fd1d7af7673042f31269dc0e8d352983ed8d95c02839e1c17c8b69be125ca46fb5eaa1575bdb0164d8365c575e5f303485da6766cee91ca24823f044ae10d6dc2ff1a1ef417893286eb70246c03c9d584a17c25dcab7d88f9722776203fc062d697040ad77e78109b402090969ec179320b98dcaa0df64bf19d250f0a625c59817f3914815593c844f3e4bf7439702761365ad7c0cd3625b8245602ccaf3bc7b0ea1c40976865dadd750c40a9dd54297600cfd3d5d7eafcc4fdde386f5bd6885e4da31bbcd675ff64bc3d47ffcf6c015a13d26562f1e103483fc4959015dc949a3592829e2e00cd2684c2ae2a07ccb617dae3d6db7ff1641670d32d862dcb49f028254d29e44138f9ac89ea95ae858652b96f8ed0d55118f7feeae646fec99b65a950070793832144957214cfd793473b9c440268613eff1f4433b5500a819810833d76be6c2ce7b1c48cad136c476f19782329080ad979807a4c5e13c64364c0933b8e8622549363db8674fa8b13df4e6f35f168260c32a32c83773c1b098f824a5c6a9db0d7426ea8fa4446dd588da5715acb8d3ca3993a98747d06735e719d58ee9126a2b9096bc8f4d37b02f95e0049f8f57a71a26bb749c9d69862e6211e2fd5b7b01960c6235a4cdceb068a69e1955344f2de7f01227cbbbdebba217069abbc039c636515466cd1ba2e44a0c5e63af7e4899ba6fbbf7f1f6df8bd74ecd71a9a4ff7f432696038ad86eac4781a06243f8d34e170add7d5e8ef05b6c0bfd36b845b18eeb64691acb5f3953f55df15199e5d7ccd49af9a0962bce97c84a74fe231807ffd58d67eaec1c3da33f741f4fcc8ca3449182dfe81fab41f0c55f2c9e3ea29b35bd3ce6c7ba3b9643f0e8557c9173d2de6f70a820804b1ac20b1a968edc84f64fe94f31a057adffabc4c0a2edbcb3e93d51f5ff32511a441ef7c4de0f3491341234d23ca7cf524efee0212efbcad8696fc5ac24986182cd93efa2a1a1e3480362e1b7da2f20e3290b8bb38a40813762390c2b2c8b73cf8f050d7755a27d58994e041233e45e027a985b38fcd4c484426dbecdf428c5dea16367333b1fc23fa5ab3c27c3eae465c43cdb6ac292e55e1454421fd7818aa36bbd4084844b8f4665f816679ddf424d1187cc2235f62f52a5009903d731146459c457114aa76f33e1ed04821551ee82dc6a9a230bbfaa841ed14f152e15f7a9827aed975a22de41773f278ce8f31ace8bb9d05843e52ef1496f806824a97db5aee901107da08af0c428299f6988db19fdf7580e49883a137b9040e76824b447cbbb735d008787aa1e864ad7327540e7517e27bd536f746508df50f8f40ca70a0eaffd093a8cc6216e82e4146f29ccb7397c1fc0e0263575cad57202693a045036baee446b691be036d8f42cc936a308815171f094a3fa03febc29d317e0a573daff44c152820ac064a58753b0df4cf093a19a3c8725703469631fc6b7cd1b96bb1cc668a6c51504da9b6a0d6336616a2cd02983264408d686e62de6d2cea5652a7bbb0c5a4c71bfed3aaafb9d2c09112e9f23467b48b145861ccf4a32395fe50abea2e9b8d6b375c571ff581aa9d8fc96f761d632e104282e1c226096456094f57df9c504da1274b34d3cac7455991c510f887581ea51832963f30b9be39fe138d7706999fba1ecc830235e720e326567ff9cf62973d5071421d243ef42ffb92d4ae723d871faa8750bdde16cb6838f6c893148dd3e5640b606c34253db671ff0124236b8a43d030a09e5bde2abfd7c13e091cdaf6b27d4799c5bc25184ac4b6447e135a619d12907acd51a72c3f77fd3d82f9afce0f5e8cfa4847ac4228b358b9be0146c88f8e06a3517c3cede472e925641602417a2b2127a9aa63fa06fa61e425a855766938a7847ec4ea5f49785d566c205fc7c7848352a2f238e6e97ddb365476632afabb360a591f1bc2ac85d899d604d91f556620d70f53cb114d3872ed56a65fe15838c404d7f90532e58780a1e87580e6057b92cdfd5e8239bf29a6d95c2cc10208f46e2292d5cfc94e6703b4416c93f05b68bef6b7d3618528fe8011683ef8925acf1ec029919d939febbcd6c6735172061e011d73d18ec9f5c4b559270a0e88c78f2e1a7cfc576743b541f21fd30053951ecf2fd9fbb90b9bbc710121b8441cbdb3d69d4bd2d856b44cb7aa3d0b43193f1a07dd2e070faf2476aea8f55f26e74138c49a828fd14a7c39b4e126268eb55378043443f3d4c98391ad401ab62c8ba8555d962e6c530de5003c8939c1080b1bc2623aca48c9e1079da33683e4a8b4d2b16aa3d2533ae88c687b794f04d8b008d042e863916389541b092f0b4a9486c2983e0c267208a3f06c286f36b2ed445dc20870aa12e2632179b61cd1ab78efc07ac8b172c801549293ec23ccc9a89279cfeae66dc835ecb78a70b5b5d2fdae68ace37f457544e122c7c2a4c560efec20595f2d367eece94b84064c747e3c442340d3f52c816ef3efb5206f3024d736bdb31237b6bd96d679affb315f8adb30552c87cce26f31fa5df552d20282d7966725e3af0e9d04a3ecb2e260e2f65f10b9718c6725c06f4bf5446573c32034cf1eb2ddc65f7c8d3e1bc1b3d2a10505a45806bb48d905e687b633075389bd232ad833d78f340e009467696850cbbd034f9a6b20d152cf38bcc80dcd70a7ed4cd3990a8c6435c6450f902fc6a9445086d875922b83779486d151b3ccf0107dd98616db824b8034957cd972c6b9545495fe6c2c040ec343b472d44f2d79da9a6725b1041b71e59f071f009046bc8eab891e93387d14437c37d63438d55b48134c96a19b518f761f8dbd55007e2ba20e86a9390bdbe15ea1e30d104a0f4ea76e2e76979bd107fa0d84a158c479432b4b7b5c5ecf7588140a66756615fc10859d183f425c34492647e48310be26ea262a62aedaefa1f126900ab2c6f2005148bc2993684265ccab22aaa4920d5cf62aa7853469d628526f2d0d9d88e3b9a230c0c99aa8a6273a5169a56476889ff2cacf2075549d8a1b1daea59108759d5446a951b911ea9987a5ea32ef4231b3eb4f5053c8933791447a1bcdacd3e498a72c9738feee32f1f61ab3f976d9d1d99cc7dc4393758fd90d1b727604c0b81c38b5604f9be4347e3554df3a42f96475dd5a4b31e156c1376453bc3c46545ae80c153439788fe1a4ae8e14ac7070b91d57aa89039aabd09a7c6a4c7e3c9402aad81f5494085e268526a39c4b843b45d5b6eb54aa611dddaee37466406da0a1731cd20e50586cf8399169ccce58aca7ab56757b2dc499e9657d75b9c9fcdf5af654e3a501f233e13173911a00a304ff7ccd5a55e0ee8dd22c5cedb9a613f7ccd3efeebd000a30f0885f996995080ae989879fb7acb79eaf527385ead8ec6e9052163ab2d5f7ba11009a7315d312f962656f83ec769012d5fb59c7b7308f37c062141ccb938ec73941dfcaa24614307a95712c4426ac26a84049d0f933c524c2a4ac89fec3d09e25e5f58fbf046c2325f754a36174d4fd549d945b8d8b5478b84325c0bc44796b23e1514197536265ca24f451d919d17cde8cd6d4d861fcac630ad03aba4662e13945e50530ac1985435399c668a7af67f4e3a9851f9deb67e6867344a43eaf88f7c66ab9a2b81ce9c40da880ca9c58d6a3adcc3bec1b2dae40bdd3c3b471d21e8c8fe99e039942d1183acaa388b1bb0916e97076cf03cfb5ee1769019862fe1dcbaa23ffe4e91711663911e271aa76fcdbaa16e5b0b6bb8a21216cb4fd8a12741c660cf64fac3fc1c09e4d6ea6aec1b2996e4a0981b0d4fd830795acf1cdc18d60749eb9ea3906dc48558a0dcecb94818c52faee674c607631b39ebce6612ea5297a9fd083943168735f7680c6439723abf8d7715fd549b16d73c35ac684779bcc3dbcb20ae6a61d4fe54d24597ed5e161c3764a22cb4b8f1c551d837a2eebe5354961b4e46d3b876dff07a5ae89bab3272c1eb91cc435013810c408a83588ff103cd2a64fc4b47a1d2db3111e0214deca084c4a7190c42e22721692a8842f4d52829626c64293935fa68345b698d4e8792c6b88cc30940002686d2fdc4cf7514e039d116cfc12b03eb1029da3aad4b5f74bf40108ddcdc455b9d0f692647446a0f3e8f1ea98252526429c2e918c516fc0381d964bb0162bf3c71c5bd90e7462280f1f88994083c9ea3ca94264bb1213e4842cd9c01eb960c80626c04dbc805791011509da8cee34e72d11ebd4f56303b88212f24b0ff369e67b9d221edf80012287d3337acb5d99d35397403937438f3a6333e12e796cfa899202060cba9e5a2c2b464491a722732c535750dee07cee5eb641e15d13602e9f116e54fa11f9aff736dc368200a59e77ff1411abbce6e05ab7d73ebe680bf9c548177d2f7b691406897317363ba09729b608416dc0db4e4b41eebd90ebb98c915d1c1fb12886b2585591d76c3a78e054521408aea5638678edcd7817915732dc829824115e284a3b7d0a82b0d83925d95262196a9550d03af25361c6af26ca1c63b462858e98e621b7a14a67e441016e85b177fc6f172bc9eecac07eacf9018f0efe2c86760da9593e8971150c7f883108658346934002471f154d63df32ec733ea900b09146c590145297d17bca0b73215961876d2ea9716beede08727e10be5595c99436524df874138aae49de7adaebb4f4a3fa486e1e782d758be3c3e3b3460a101b5b4ce6073bc5e281d63a8df89acd941ca14a38c52dcb0caf181f97f72e8857e0d336d1c51c632d800db9376458d71b26fac4ca1ae36b27455b8c117eec50b5271147deac8cec90ed235ce18fae505856d9c2b3fd085882f8f7ccd584c86581a8e8759c14fec06c552611333153c0ff07ed09c7ee8f56986aaa3418633266952cb8c05863be8add548ef9ce8718c5f1650506fbbc91913d83cb90388b72099016a0848b4300672cfd9bf4a900f2c05020415bbcde880f875087d2126f679e709f2166499437285d0cacab5ee1e7b680968169dba9e25873e5c5ecb941ad8a16ce88b3b6380eb3258ac7d5e11409282249697c7d459f65766aa3a230adbd968a82934822983bb45fa4325c35ea936cde5764d335ad6017c0511689be6d1eb8cbe974ac32e49fd7365839e954b3c151fb21579b46b0c98e45cf86ab9f9302a88c4c0938e0911ecac9ff7d888037fe46cba506fc4fde1bad3f101f583ec0e2b552831efa77fc8b03680b2f838e878065920c833accdbc76d1bd8a52892666281c895f9025d85ff49099c6d64237291cc7ed659a616d2e159519f7fe1f41a821847031c1a72f9ef712a7dfb39e2a491a934b8548024260b06e0d3536e49bf2ec8751a589492a2bfc55bbd53b56afdf9396589d3c282297dce350788a6c09e33839591642c44fb942d18cdce0e2b1218a0e49636861375178862c5bc8cdf50bdfa3193e772080430ef15c0f87997fd8e9330d2fa2d3b33eafa0bfdba21ba22a61401dc70a1ad93f56179a8c4143a9e568b388ac3fa84553915e2710db1b55d3f2c1dfbae010bd73319c0941168840496b96e4914012837a8f027001531a61adc923ec7a28a3c78e2fe1c3dac0f09e6f4d94dab0b6ebe983b8706954f91c5a30601e88a920d3cce1145b8f91daae115e1254936acdbe3b703943f9060ef76f40f4e52418935173da29922f555949783dcf93d5073ac66d113b68510a59d0edb2945633c27fa60d4a34d42ff28c2082d44fd07ce8c678ab2645a678092567869d241aff70ecff8c42174c668ac9bc091e41f5e532d035602caae6942617cf2404d9d868b7225ccc985d8e1a673647095e3f89ab3a8399961f50e4314db832cd80673efd35a6a284fa628b3652401b87690ec0c540b561427632cfaf649242f327ee50682cf75f24729dfd6989d2d6b9ce406691a2a5461a302f9b017ffce29e1e9bf3f58a4bb388d47d65ec35df58a63f39f102fd6f5a963990343d100906cd6a62cbc623d0ce7fefa0179b00318ab723a7787abf4bfff4c475ff9dabb62e7cace4c1eb6c3a681cbfe5cd55809aa793129c1abee9a525078e9d97ea6c9c3c53fd45381eca9a84b997a8e2f29ac24fcd613561cc23eb97df03d5d15b32e901d1685ee27fff29498838645c8d9adc90975901dddaaab25b3c308e20d927ccc6db3b9fc8860723e9cc07b4135cf21f270c3256ccbf908e23d79cfedac289b1aeee9541b58cf5af601c3536a255a006d8613e93029ee081bf577d6f71016d5b57b75389332f4bcc69872dd33475a3e7edd07718dbe5d923bbe22da4ccea1e1b020c9b90dd211410fa6602d0abb30b0ce9d0262b548473bdddb8a2f88ff9e0a4dd69575ff055fedd0a234716f93c6e9fd1820710e0898253681378879c18221c1174d9a4c7649a6fb1dfbcb88f4dfb19b00a833b6e70f6e1287b9a494d0ecc8e3ea60d7bf7d2c9eb8ef6256e5a56ad9b363e413e33d76cdc6e036028701c103c7de7d2700bf5c5cc309ce8b6d63923281f21223cb90f245db6526a87aaf8d8a3986f937d253db32cbac113abf1ab857a2aa5cdc4aeb72e046a599b79aba6c588af62fc8e8c28689add02bc17d5eeb79404706307d98d0f17a6afe22e0e19a27d8724782947881f3f231ec87e917cd7b9243f0b39aad9d837ec60b3cb2f683cea40660c19802ba0105e6f7b9847c95e99b5898679c884b52a96a4c78b96af04e62c87768d0aa392e389588562a14914670c53f14d4daa2ed968299d41aa2e532a1de852d12bfc6fd29e203e550420f7a652c71ed9def45a8fa29db08e4ddcd06d633950db9a0a3801f32d6d0696e52c2acca794b37fd1fa36b43e4d24554422454ecb618a074e4096e3049b690c97ff665bad420b91dd1a11e00a4a86bf72188af80ca3d429c289ef992c4e3444d725e9f4382bc721a9ff373802e35c16e22f82f425035fab08ac9f7410604288a567f5f55c3263035756da5ad722e2210366d7bd7d3e263a9fb61decc591a6d1f696599bf4888455d10b7fa635b3f81a813253d601df9837e46ef34cce6364ad68e4118352001f47ee20e407ca90979de3e52da61dfd3c3d06690068cd183b08140435ceb137b57094b3d8d66ab6f2eb89c0e66c1b41447053bdba25300c9cefecc91bb25fd7495f62c92ada365179335016e1b0e0b50c983bdd91a61accd0bae9687ec0659a795839ddd4191ffe3bc676cec322f86008cfb1788168ee31d12f4317b3db4154ab239d6674a40172ee6ca86ead5b150fed9d68b6cc3efdd0ece2d885c63dfdde7f68eabde4dbd68f320292d99e1905dadc9750ab308cc3b546b69afa14492a004d56e5d796a0816bb6b07476ad90742c0263ea492f38ac273d283f2b660ed8a666c5272c76d2c56c0403a49ef79036d3160063d040c83160813ca007d06dff6186421b0e9b27ee6ed353a377fa1bd5efab51e8624e08130936b6557f714b85883b72184cf7402cfb99dfcda65a9a15342cae2f591e43c361bd8e5c8103cdba6b98028c162359437dcc9d049a8c8cb55da055c8cbc84ddc29f406e632f50be86a7e742f3506342f267c1fb2be749bb13a4a32c13ea85492859ae24b5660fecf74b7834dc951881a14be23acd3dd9a4e21819ff81dbe13c62b76e4162f627e1339ca70425386e23223260549d004b0d029659f7039e8d7c0beb8d77fe3a9b4f12414c48b7536ea8bdef7f286a96f6cd285003ee4045db374de5c9bb8f39d094b1a65d7deb812e8e886be4b90d63a66e0ace1b6fc04951aabbc4d8563ca9e354e8d84c14ec2344d18d2aa700845592e3501395ffd2388b6a236d728bc480e40911877a0833e0d30549af968e233195c10a3eb00a0054e8a51293178a84e443c5672f66a447f67127cb88be7ac890869dbef04b93a2958150e80522d4b36d587335b4b3acd85a4ad51445bba920c45be9056ac6b422f5296d5b2875d6d01233581318da02ce46f268c123945b6bd53d86a8e14996ac3694103374ed7fd84e1ca221cb782bbed0a41b29514d6f5d3f59b2592fcb239d3c65d3309f2077531205ab2697d02ba79e0e84008718661b09af42f0cb444404b1aea82f2387ec725efcd25a700e75dfb8eccf9dc4514bbcfb8ae2af08cd7eede6363fb2da5f5c10251af27619a865941a79c40dcee8e83f9d143951a4c6ebca6596c4b55f2caafea644d4f589b5ccdfe5452704e41d45895e903ef3aca3ef8917b72d0149912b79a015112f350a10f8f50fb663db625067a2bc715872cbdb6250d1bdeb2f1d4f53cbda202315644fc9861911c79e6317c2b2b15fafb82e5681e631edb5679ab2381ef7a922526fe6869efde3f4266ae001ef0bea3608f8a4a7bf4bc7e03f19a4f14870c31e3e5a24874976d48fa90d08dd45be82d623e99acd4c80aae67f20780ce52b2265af27dcc61bb7aeb97e4c54ee184d9d78b99aea820b6dcd3b126355fa81da38d430cd8e3e8c6debc75417edcd717bf0f737f4d35821bdd4bad28461ca1b5254091de0a69bf8f3c280f649ba70f60a4a9d0964f0ef4797aa4d5ccf7f66964c33e8d0ed0ae9d340159de9af5038ff35860c9c053f4dc5b8e19b06c052e8c754d9d70b605abe0c0c20c563ffafd049b9baa2aa055cc73fa8ef70036ff36c6598f924fee562d6a3254e6e1b53cd33aed76864261d3a7c851e6de36ba80c1c651a05abc23edaf307324848cc54881af296c2f286e08f49c3c929d05e3d930aff36dab643da5484c0a861700fa52126982a502786a6f1c3008ffd91b330370540f266b9f6e1e061496c5468a9a931df07f56302dc64cc7a395ccc93c02ba184f2f7dbe2725627a244aa910b468864e864abeca5148dad1b271afe05a6eac704b447991ac6cef31c570242e91ce378694c440976c3b4114b43b6fecc495988103ae61dd01f9bdcb14042559b83d20825d3dde90af7d20b1d43abf1b8556bb6195859582ea14873c4e880a0e300ef3f112b741d30f43f30fd4762001d0700fd07087a0fb8fe9ad843e70042cf01a0bf1073e8eb03078819e8abeabe18c5bf552305a68702d4cb3cc0f115403a431a325785e9a0f10c60997d1467bcc3857b48bea74fcb04713790c3781fcbea7251fcecf4640430cd023be2dfead06943046d857f30028db41f364d04495c89c32b3ee674995588b3aeea21dad95ea6b6455de104c613c6f11e13c2ed89c3cd89e1ded0f37b4c10374c043726f6fb0fbab83771dc3c91c30dfe432bd67b9918bcff1efaf5f2f4aaa06ead0204ceeb262bbf5c0038d9f191791eb6fddc57f78eeb6e8e510ce5bb3c714e735b227f63c329ce13e65cb0cfc65a2818a3b9887607304f266803e7c65949d05009ff6241347288a0ced956bc617ec39697a0c3446aeddf6d6ec2e683ed30a9aa021ef4b4eafb721b8c51ef78eab8594cfafc6f81eecc669b9a291dc754baa63a0962d9c62d9e76a1885a133896cdfc3b6b45168f9c3346a38149b3accc6156facaf767ba4348e601a5e862464df92b3c97cf4a5464a4419a8e127c4de1eefb0071585414d51d96ed3f3430c26265ded77c672ba38b1d63bd7af60498d39c39ebbc6f39e707ee905c79765df8134415447550b95936204a14adf32275a86b090c0034437ae5e63bc91b4b0e1e3d880cf6073208116e3e8fa4fdfc3f3f4d1d68de025c62a2238180977e00adf4f6908440909b21e8c647b57dfd9bf60daaf887466f4d60d13b40143e7be4467c55fb2110433951e892c92f919bc776b92c975f39df28da5e7314fce871603fe522655af3491a9c43c84d6126415821f639abf28af350de0a56c2c44af7f0fbf3de24870bc50bfd2f11d902f61edbc8baad63c40bc95490a1cdf17f28cf40b2aba350c437dfd11be6c68fc2fad17619d721c6d10aa713042303b74200d5fe204ce02bd4f463cac922c2182c75ffcb8afe0994cd5d2053b72f5208baf8355c6b1cde57c7347aaabdeb6b4b11d59ccfe9e00f112fec3bd440e818284fbe4de32edc2875eb161d3ddf283cad25b57105c844a2ac6418e5a439fcadabbad699237fab6d90dd91b6cbfa3b07227ec2914c2fb82b339c03b4f127234a1649f37769ed71bcfdbb46fc0b4d4f64df0cef6f3001c27f4ac97bd83c4a18f4496b2ff7c0259c04f0b6d244ff9db11518ff55d7c66de4992f7b6b182b1aff92d669d48138607d6bda9ff40e836683645ac573d73e194e41c5ec5c87d7139015f2270484436c3102c910a5c9430534e32ba6fdcbb50476bfc848dda0aec032ebee68e7fb435df2fb943e46a2396053dd3734d26e6dad060e2739f08e0846d7150501a145a3b1f3e2f189e3546771e1e4184dd7f3396f00521d21c2ee267bea30e526e94cea65d99cffbb0572630968279c5fb5274fe139482c180b4b8fd1525c72df68d11be142224a288f4661f7a451ecd7a9dcb47a4af2e0f11e513303a4faa307dbc66fa97a46643862fb18df7e04fcd1a8d3ca900aa7e7a1315cfe9e1e2c8b0535de5a3d930b4c0d0329fbdfefc5fd11e2b2cfd1d12fff54a22c4592450bb56ebc5148437fa3f2d2bc4c2dfe7fdeafe8ad7483f3c8b6c75b23c89c7482b68ab4870af9c12e875020dcb90c47ead66b9609b4afbdc349634df2966a8160bbd1d64f2c0b424846425eb66efcb7ae4d8dc0a80889a22337881ef6e914f98df903e350da4e9a11976e8d2265a0c61accb4e7da5c2a25ac8743429038bb19f0be7d8c21914598a4748443620d96694c80cef729519677f8e4c590390eec4a62f9a8c6ff5b11dd395061253f42e942f982d692b51f81a93f63cebe4676d17722404928c9053f2cb9df963bef601d5422f010b322014d50f62489f2458a7784038162387910479f3f966f108d864ff73aa5c174923ac71ee1ae0fa5f310ea6b7e694b52c1e4cffd33095c85a0db25b858eb1e448d4e01a252dbb0d45c906aa454c626d67092caa1c675329ebfac57e2924cbbb86bc859b8853bc591f548f0cd0488795a6e35141a0c328a2fd3d481de9e9610ff30a9f6e158c24d2f2a8273e3ecd938f65a215bf2c684ae692b9641e71badf4f7c4e7c26c01edac42f65b1f1ecfd4c54ade832edbb8d36237ae8080c825d664e707374e80d14f432521d520d857f4e84e94d891755461cfadb991bc37f84dd8a415bbaa4d319299f35ce30d833fbe2ccdafc9a41496cdb1f1d5bdc07f2ce9ef2b59edf6a28c577135879087b9e526f4920ce8e775d4701fd8c2c8f5d0e23fde21810ef4728fba6e2c264ddc279570f2b358730dbf90f8924741d7864070db879f840e295d858409ca7ed29c0a79b766fa2c36dcad13a145a5868192f4e1ffb4ec06b03cfc6715aec99cb5732dd35ae9e9f94cb71a57338d55b8fde879a6b32ad732dd4ac63d655aab704da68cee33cd6ab91937cff5cc8658b5326c85a3b40f07a0b2e2d34bfe31510ce8bddc91e75a060ca8bdcab4388b1fad0218c37cd09740fedc12c418cc827f1dc14462ce8e8a85d4c861050cd37e9d2865ea9b04d176b19f717ba65bcdb98fbfac555424f5c7acd417a443672ef17df662d00d67d0d7d7ecb00219ff1fcf5da62deb9cb9b9e0e7d362f998e9ba0094d7dca69b72445771355e31b11271072b33d1579290e3fb02966f4ef5e28c4e33da181002b9ebf988b825a932ef41175fdc2b53de10bd89b4ef30ada9acea7c33b1f529faf21f2d7a06f7b5af174d18dec5b0fcf5adcecab5e16928593d9f486b7915107f2598d8521006daa3743cb29f9dab8365c0b82c1d8f4304d8b72f5ccc9f0fd74c96286261b8faf91951ca69ba8d5670498aef27956cd6ba1c5cfacc66556bbd05faedecd68aec3b93cdf8765bcc002e647ac00eac5accc389bc23c3e4e0b7440d94720be5b3e673213a92d87a8c19a7c145511cbd039293f3f6070b10a3d920716170666616d5fd49f8278080d0dd0423b3e23ae10232cb8fa02cf9485fd229f2164dce45a0465aa6e848c2d36f55ff8d0667d72e87825a653034f81e49050a41199b90718345ce5954b2cc3f236daebba6a2ae3ec39992f6e9d4efe7c6404f89770243d0809b2409bb144c137387b94e8dc5c8166b43ef1891ec9e9383c03567956407c889d5d7adaf58049d98bd9b4bd1c8ae85ba1e77c65c6bdc3e389045da1259de1c34e186c8e21724f68da6c01ea5bca737cfca373f99dfe013c99ac5a8ec16c19526e20a91ba815f88c905dda53858f4c0ab9e4a883e979f8a91674c28841beee88ed0676708724193f5c2245a46c4ad27b30e58f84c87839843f581f87e625e007da91ba8f55e0e43a07a01811ecb2cebac0a7fd1b26b1ff24812628cd2afcce7f64e1ca8fd696e8840ed8c40ca088213006cb3fdad26301d43abee6bf16433ef4d4cea83c61de2a988f5b48f908970a262fe1cfe15dcf668524d0533629d6a3980c0a2684159454144c226686ce958b80748f1e00b31dda9f43692c2db69c18fddb099877e29aef0363a73489a50c883f4889753a38509bd023e282be4f2f62f85af28b511f43aa617496f6c59799a939b8e124d006aa347f42bc1042583552b4247509a2a89ded797b142075d096263a73b8da828c4910d50b46163a7f78b256a2eeed80602f403bf86344fb2a92b3adc3352fd2ee94bf9bbb64385d020c5a175986a6671d1559a37aa927f7866c484d71a3d00f5ec1efd122e1d123528a91b2122781c79600f9dde6dcfaba84774d01238a54641c70d114885715b3ce13e3a472e7006fc4471d3bd37949e52d402a80d3d060ffc8256e1a0ec2a8dfb8e7f7c8e8f0e796811c4bd409cf808ddac825fe28b3009296cbaca0b0b3d28a030723e2a7a2e9e9acb44f4a83078ee0d2af906a92f552e4b0eaa7cd0438781a2b03202c3ea1d72799680e12959cfd1bbd8f419528cb96225ca15c646b2ad69906f5371d30ef721cbd588d45913b62e22fc314168f82fba0018cdd1d384a2a0c6efea87dc123091b14e9e379784b7decdfdfaa51dec3586500f1d180330657395ab7a3581fe244c043d4b3e319df79cd98a2a0be7885101f74fb59ea3f8bcbb19f92b96de201fc2dd1ca984ddc15eac5823fed8c08f09ad97e62c754b5cd291ce39882bc9f425c69538fdc02aa7d5bd51aecbd436852e602b0af8a411f99dd8347501d44ea31e5beffcd0d61c2ab5b1ab39ea9f419e64e6fa739bd130f1e7e3de7e816a45f0fe439fc8bf82b4f2dbe8c76d2ea8318d56d51849b943b161cf6861608d6a389ef9f5ffe357eccfbb52f9ac926f12148dec9e470cf5aad2deafcadf237e7d0800211ce01d66fc3c098024d3ee4bdd221e5e0f9d3d3e189323c0409e344b19de226d6ae996da8b6035249b7549764d8fe4ec198b26fca428d53461ca80510179390a2450c49e4a382bfe27b88c6d2552b1f752d9bf92825650aaaaea70bbe89530a9954ec0cdbc52a87c89bb174472b5974c9096d9c678d52c6ab09fb73ce3afa5a20b80b8fe80458f2c0658067335c0cdc28e3dc4c4513f7ee3b4266be448cbc33b8132c9d01e1ef46c79503f11abb4889fd59005ea97028673af67792124a341ae1055943c9341fa7edddf4ebb925cb249fc4bf8082d10b8582ba5332de5b996bbfeb04b2cca25877330d84de5fbc40776cf91666bf640a73a5e2fa0ffa473211da5c45d755364d3e0df84d1b4911d3f179c59e54d9fd5bac2028ece6323418708d766f0641cb0bee073fa05cf529ad1e1e15e473d71024061ae4527734349b6df1ec9219c1da43040ae06fe5112d48a6cb47d0221b0839297df8ad7c4ff9054d19e888cdac5df01535de82e9731c677de11314c2f835e403f5a20a9e3027520bc5c12815048c6a01223fb724682a4225af0562d7ac46482047c9ce8cfb94f98694cad76de3866e9140c917387234eb827723219030ece416d3d41b53ce196265510ca91e3cb1ef755c77e13795b433714c28b5f2b9165c90bd3d760d2d54526c9598821a6fe5a6be38d67ed42d9d12d80e2bb2f801bdf4bb3137ef5d72708b93a9e231c348dec68f1ffcd442c4ec9016ecb8408f46874890441d311bac0915e339c1ede80569ea9243734d555476231dc0a78c6078369940001f8a51ad57ef088a62767a8f7b59a556fa95d81259c100b494161368cfaf3696050f475190ba49ca9e7ef01192178664a09ac02030147c0dc0ff0683ecd4a2a5006668aecdf1be80a7fc4470f9023d129f6e4b4f40b196882ce38838b118f0c00a5d74da54f60db970a82d57ef0bb9124188e3f8388a47377beb4691e66bd33023318023856538faec5f0a35409739b37780572a805e5184c323ad7ebc9615d5e1bcc56aec9ea3a794df227515e27f0101bd7c696e3bd8ed7a5eb1ed456e5b1faddebc6f226bd195d049cf94c4c8cb64dfb48e7a279d3b0a550781240463f9853afa2b2351223d05e1d00cd4693f1c4f832247ca6b1479ec76959a5eb06211096b2cd86602cc42e77d03d2a5c85d394a65c08051dd5e2220396e055af99fb6d5819ca7e93079061185cb26258618619e425458793637e5c84a9ca43d4274d3c4c575a9c772fe7b896fba78dc900a329dcccd3587232e51d33440a5615b968ad9a3b27b32256f9cf7f2ea44beb68a0ecfc97e21d800178ebad991a840436fd0862cf972b873a05ee08a1a3ff73d6ff96bc9629732926617508d4b0d7d989d6846e8c51a4d14d7ab32a4a4a93eac202f923c9539f261be0cfa1a03d56c74b12069935853b5eac5c474f7af668d47942444c396932761ad98ee0cc2f66e7e7f9c5bf8d53ce229f4e2e6a258f5c9adc633c8651c0153487e1096b31c478a0cd77ba8d235a27135e422fad05dd21f02425e58d85208ed6781116d39cdfd4347abb71ee00f7d704398583e73d88b7de800bf54cb350b233d587b5e11e5f67fbec3cccad569e1438724b92c732f31b755c9599ee5e9ebf61cbd3ea464c16d3d0520e938c0635dd66b08aadecf63ee2b585ae881e710602ea97350cfbf3eb132096ae3d382ffcb3f91d8b39180cf17639330d76effaa784269c54f3de6d86a02e51f4858c5ff7f46c060e13d9a48a76b00e098ea24e6b806d4bb3de9d604c842557bc6f74b999ced6a0140eb83d9d858177fa4f490fb21e3f9ceb90db95e91580a74596d1143fa6cb65af109074d3ecd5372386fd2412c6562245627ce3df48615daa903b362c3d1c926de9c95ab676fe7068944f352909154689cc143b66e92a47c80dc8ce997f417ebf9f021bdd9187e173f00f7eb8839339f172b5a5ffbed1e00a5a9e1931ee23274ddcb3276c3f24594e02b5170b674a9cbd928608d8a3e005073bdf2902f22118220d6b687a60b6ce16f1387f9186c220abf9a6f7612f1b22a12ffa6d3a8c11f49f7b5ebfaa02cc5db8f179787a6ceb1be5ffdbb1f1e9639a2908d1cfa79617820d4af3efa0fbc9cda0b2affe7d31b5775214c9c75810dfd0402ef14db44c41e8b01331e51c09ca8fd8082468aa2c759c481601928d7a37745c7e60064212250b997665beaf491a062809c98bac6484be44b21dd7f9ab4d4d24a0c6f8cdb18c8fd12f2600f0718dd14541d782317393b05251155772965d4e3957ce74af3d80ecf752fbd5b13e87a56295009eb698fcf551671ffad552e5edb8d81730fab8c7bc08c9171d02f7ea5348295e003d2c7bd9e1f0fa6bf47c0f10acf5d67c0de22cb0d019d6ff60235d6d30482e5f74caac8d0c3ae7323873168cd3fd28f7209f57eaa56fa20a6c254b69312ea7d8a1d56ea92c92a584e030843d052a040335bb5e9928b4b5e8c852d66ec38219c2034be3f103a1aa66c4c1fa932c1908b62831d26628485f2b379f80992cd574cb443b861ab3760efbb994a9031e21502373036b42408839799913979037ddfac7f940177cce7161415fa6c6c2318a5d4534888aaa2e8475b11527f7ae60e98d19d80d19f9aafd336200ced2748ab637e8a880266d36ff7eb1a94ca9c99e43209fbeb9a11382d090992646886390d248f7a25db2ea586e8826c13c187760ffdefe16ad7a5c17acae3720c826f63e57304605b912c9de81179bb23a7be412b1c87293911e664998ee368a6c44676d49e426bd42c8e8410ab9092020a9b93be556a7b493dfd63f3dea8cb6d1c4656029ce258d1feacbd8111289dd02ca47e239599937f6f6d926b63d30444d7aa41a9db584ab04cac840c3211f4ce1075e19d48c62862c4f31b1038de297f4127b2639ac952cc76d07680d8c5b5e95a0c23a443831512ea6d3b1abf51baa1bbdd056e0fb8eb8869d368d570ea292dbf0fd3b08008d862bde0723a52f122d69f16b74f3671d900901804ee2b28d0d604a75d0f07c6d334be387c16b746acd6269af2cf282675e06ca2e0be5d38769512c73daacd31f456c8538fa294dde331a61fbbd33c40c1123e70b1592091aeb018075b311f5cd5e010e1fb156f080d98ee6dd03a00cd3bb93c7898814be4447915ad844dd1211c26b6882361a8038fb2ac5af7a20382680ad3e63d1c4141fb62025909b822f13ec26bcc9bcfe8a26bfdc16301152f66c2e4a72617a6e92908e94aa6cb315f9626c35912b69ff27b228893cde4e9c246906e1276f45878042d568abdf0612b96b1245f6ca9103a9458a1639b9ea26d8e647d443227b1a291e2d35a1482201732337d91f334b031af8662fea68782227acee1c70340099ea03fba8c38318fd244d2425bd8f818a01f57595953924b1e010334daf666682e86cbc19b5998297db68652b9c051ef3ea7c9a4a488492e773375c1fb2d41f9ad03f47053da69170db9108d57adee3900a0d89ae184819f7b6a15a66a48600de58cbef68bee1cb99a155de799281d17ee1d16c6ded950f6a220730346377fef58053e8a7de60aaa2b0c44a79b31b82649cffa24c6b6f69c893cb0ddf1cc891eaeffc8b0563547aa8f4ea9feaa470c10e6f8de2e1cf99026bf44423c4eadd2eee199b02d229d0224b971351631de0eb6079ef64035066ef094cf083a8c608e80cf89f5beca7617a57ab4387c36b524ed63c22472a38dba3a91e301a2b662b894d0d6291b32ca094d3f0e3ee3b2d2b72420d965bb4de7b2ab5a5f92aae17691d8cdf5240d96cc5312ae84b08c3710561432ef7c0d9d989a52e7228f2f9b018a70df5226d8042e8fc082e612ef1250446aba032684fad65430546ef3ae4b52c07e29019819b3aaba5ce0de9222b6ffb0db235fcda1bf8190c35f6e697400e088800e95f97ef32359c42188b81598cb0d0564725d6a18889fc0073750deb44c25da2524ef7056a96d691164d7f8511f46a48732e4206044015c29340fb231aa5076d4057a16e12ccd476026e8aa9365094f1871d4bc1dbf9e1dec5bd7c1bd6934a269b497d77c0995043c4c16ffd0106d2e3c62f576bbcbe89592bf00d27d512b1a833684d069825f89afe8262a7f25d9014d6fc902af6c1951eb38f2738e5b076a220305d1822399924441f3aea97f1d23a834d876ff9a47a2f34a033b020403db2d828dd5138798b4334a3a918918518c538c08d4de152b8822462f7d088a3e9bf34bf5ec3e257e4cc6f608c6a69c98ae664e4d95109831eea31edccf91b9eb67b0ac060940dd330b410ed79000bcee9e43d1a2a01d66ebd99397aeed9166458ec779f8796688d6d2f9e7905b3127e1488cb555324684fb54e0c9fc36b705d6f7f0b840093652f69fabb9be16554f4c3f185eb407f01e8a7badf200a46d1cfa30e99c57228277a378712346e21a260460b0820e99408ef8026eac678368e691ecef199723218a4338eda31b4869b710a60daf40b73c76c0767b108f8887a4175e1637a370d86cee1a49de8ae18ebc546e9e1018b97a7dfb18e2e2581161312aa2728ab1ec1da38fe4feac9bd00ab788e4beaccb0c6de3b7df510b3ba271ce20d826ea37edeb54408d5d6bdfe8e0804664d58c987c23072199bc5142c57ce032cea44080aea6a2402ce0df9a9d965b0e20bafb3b0488c62a06afc4c88ad9c4dc2b36c82f2b9dfb4231bcfaab7cbdb691a4f4d36e5b9a4407b9b3a7c953bbe74f4586f336ad33674f9abc0b1a5cce448c783d6c4b90537b0e41bcb39eeb51ee85935bc1d041e1d9bf862922c3fc38b4c75abfa6db527b160a6332efc559f1b23235d839b3dbaa562630831951cef9f3da0b05549dfa13f03b26989ea477c9aa47e1b145029c47744df1c909ed38b31aec423d55f18fb69686e8fbdde5e474f4c319d381c6611591537e7164fa7e777482442609873a3a5eb5c54cec97667b6b8ba02b998ed12176d8fa5907a1ff11a950d50e786f6d2794c53e8e9395cea1230e3432d0037ddad2dc9d511dda871e1154b05ca20c9b9c776d4075079020dfa36363f46f2db625c3c08b4a3a723debbb93d7c4c0bd5bc587470dcf2b7bbd1457dbb5518534c472b173345a232dcc262a91b27e57c29abf3f8565565e41a599a7061bbe7503681c54814319298b93a8b356de5877df3f5899e8218ad80858a6fb2797ae7b1cb9ac27a054db6997ab33ab23071310f00b2502ceab5d12d8686286ad874c584d786bb51f3e98c6631f6638be0c53894b333486e5471d01307a9059fe8c1427a052d040f085fe78f32643217e2de06ef9572f07d1eced3b472abfd1fc1d099e9a83dcc4b1fb5a96983ca420c340e7bca328fca823e19e04db8327399f7a595cbe0331242a0bf967944c1a605b1ff97d93576e6357438e20a72ba80e170080bb9d4ac1c704efa9cbaeec871122a285656df18aa2a0c02bb48b05f8365faf610035099717462fbf80ddf0723f985152a80d055201c9365f993f17a4428d73180cc5d17070d8553e4404d06d1abc25cf8176afe3b250b6ad83b75d20351c6a59a2c4fac5dd8e70ed05803e507ef0835494c01f8c01e3ef39133eaac4b600e717f6aae357cff00d66104235f88642396d41d52cd582e777007fcb7383958f279cb5f14c7f1907bc3f139cb68d41a5da0f9f1b88dbda1dfbd3f15f3800f560ec93f37d4baf4c122eea412169c4bf4b9671ac08ea17091cf0873fa289c4ddfdb7520144847375970179771938dbc7dea7fab2667c558b26d60d18e8303cf845235a6d411a3bdf81acee37c993f1b786051bf2605ae67f497a41f80a6d7232c9fe68ce46bb1be42384a8d36dff893093466e275368057adfdbc4f102556ccd070a55c657b03e6dbc40cad63f77ba1bb12b42827de64b45faec4f1d86bfdf0db8b5569a945a07d8d28009f173c4ec8dd88c5ef760a8c2ed1f4848881e4834eeab89e80cfd84f5897c662b8b93b7038e2420037c2c65d0c4b7ad9942310871296a68cd9b400dbad48311091fc4d3bd5507db26b02ddca73cddc4df51b7ed139b6349e76d9119690267bc39bb0624baf3b38d7dea087fee9a6b3223d88b4c81e65e22531d6ed43cd75d208ea66be516deae3490260e1076adc0ed688ffe1d5b3123aed5645ae6c4fcaee4fb458787d1207bf29235ffdc76db755232c551880f9cc5c40e398940b94cee5a8c22ad80611d665d9c7559517b42fd3d2f35da9066533cb869c006b28930d7aa2ea430c010491a7a0ca260f1418c970ca958ccee72948b62e61bcebc2124f010090320845e49ff9e55c583ce72f20b12d3fe0b2f8a77ab841f3616025f862725fce22fdffa0017cc9047ffa69909cafc5803af333247c15d8c8a3da6f5654bbd7b7276d5b4375f269b9d22dd6a7d1e1e10bd35f5ae18e5540c8eba9a9ead0a67e78fc2a05eed579c39bad001269f8fa8d6d99d7cc2b5c66c7f8e6702e04572151e2578ee2978f9e46041fa574124ab30500795b568171c32e152e92fe8995bde148270113d6e5200024a67afd47c09d3e40a236cc01a5f404f18fc6110d6a9bc187484b33b04909c03afeac753e8b4558a2128c4369d1ccafba7b35c07eedde6f6917296b11a1ef7cd3d1b3dc5613c615a81b5a5da58d4f9d786db184f67aee35105abb618649c500006f4b3431612a405922682a72c5b2d75b3db2fe638ee4c3aae6712d9ef04430fdf74580c9c551764ff43895a45fbda967d07464453052d48d41ea3e8f1254704b15f9deb792beb70689fd6e75fa81d9e97446ec9eb63f6a9fa2506a532038c60a3eb159397f8635d9240bf92eb95c4c65b389009bee28b7a9b41d9f7d5b61a490260c8a2c101da5946c344fad8654acb19260e720314248040065762218916ccf715b1cc391caa61e89cfd974cf559e7644922521b766edf4d22f6a0602e95d8e5f3432b0df8fc96e95efe178680095e6730a6a42d0ab5c79d6f4c9fa34b54625671192232c7813598ce112d3eddb1c91771869e0924a1bb7d02ca361c1cb7fabbb1c932639ef266c2c03a44e562dedc5e1f24118f8a4ff2e096a905a9f5a23d00832928e61da53fa93ce240aa0ee8cccefd94080b57afbbc836a9d452758660c7b96af7f78ab4b22c0c6d59b0f987a969ae41c116893c03a6826fb4a3fdd5e9d4cec308ea8487aff7f8c3d9928b715d2146ef3d4f1fb8b9e2c111386f487b095dfd7bfd20c66a313384e077b161b06f44a8980b31224dfd209d34cd06f11667329a71751fe10a49cd29da2fd01365e60d786c0470bfabff4257c8bcf6e5eb5ef1a12c1d7f68f1fa4b40480f4de1c58934637bf722f574c5d63b40855a1f456851cc41f547eb0f8bf133ad12a0b6a8c7c7f87e8845fc458099282688e3b4563576f2c147e2992fb39591541bc51696c873d5ce28a2fbc38be7abbf2dfd939f02db162db213d7e3f32b795ef549cd6d6f1403faa6067778da67dc79f8868406f1f194aacab5363198b6b42f781e3cd216b582021ecdbbb1afe1e0681c592a879ec4ea7982c5792c2150cab016618270054a34118092950dc5fc762f08d9d8656233f2fc9e155af4c7e01f1f925c658e22bf4f708352925f23a4229abab2343fdb858d0471494097f9201baf156fc1fa115bcd7499de66d6f394711d7dc6373403c891b6e4b61438cadafa8cbf24389d1fec9965ebd380abd72c719fc11c21dc51327846da506542b520cc6a868399516f1a3fc4e688d8dbf66d2a33f75af0322024b13831783caf952db8eab2cab3330c88a274bff297f835d89bf9414af1723fd96148f4e0f94ce69eb2f8e628f208cca05c096210507d00bb0ade72b0439225aec3cbc5ad0f42612b63863f5a58e0b276227b39886b24903138d6bef27652eeb459dd80a8959aa732b1d118e987669227f7d59c5e169c1c37e204c6c65ef97613198277c694e00060533e4a1ec5bd276f36952f1bee2a2a743cd68a9dd9419b1de68f095ceab6b4dc706aa64f060f76f90c8985c3704883d3cc7b270873a40d04829166cde98eea5f9a1d5396d02614c88d31b2b61d863bb967f70c0d9a857def98f2bf60f22b1f39e2e651b8b353991bbd4b58898a942ce7c7ea1771dc3775f303e9a3ad9b3efca922541e0928519cf7aa03d708ead630718044469a67517f52b8406e18f1ad0832af35ab05d912774ea78e816802081292b0703439bd50614903934aa12b7de45dad26f3493dd910ad1a9ea42dd1d8abda99534aca171ba67a7a7f92082e964d39e01e49a82cb3b39c84ff7438aa21281d31b7d0e5554249700ca0d4b8db02876538745558d7259aada8b9999b72b558baf8464be257142b9aed2af3690affc267039ce89df77c5b427805992f4834cfa5bc89cb8088c4569069d7379289fe7cbc869278258409b2cdb85a87474dbfe6d1adf424a6674e7a5512c461f29635f36775b1b65ed613c3df3905263e93098ec9357023e095043f3ae6c6842b1703ae815faa07e5e7fc34e5194efb98f77198982c066dda21002179206b702d70ba9bbbce920ef27947033e18e915ea2a5761a97b67e5522ef22ade98165aacbdebc171f9d4db008ce160e518f3b510ec7c778e600f846f0010ea0accf4a9d67458818f2f39e09f00c6d87467e358d23ae63efa22174104499e79b6d2c9cef570ea04b7a90a93c23d2de8e106cce594d4d67f1abb2cddfb237fda18a077427b5c83d11d4050ea5e92cddf745516cdfd6837b090ac4148d5651be992bf37c7ee1fa902930e9404fda44282a007a8e408e39b1f09c84524bc0c20614f77c8a215eba5ce3664a59f5bea02cbba01007a4f66fd94f1a9a9ea8bd54443545e12ed42633598acd35efc5bd6c4a9d71353c9c2016e15a77a4eab86a2e0f39389152101317c020c95e1637a9da23ea1f80175fcfaf50a6d0f3696cec745296a4fe257326cd63b64a24cf20aad80800f18a800e57f10374e96ca830ed7589a8a858d30d6f1c8a2d40ac5d8ebc28a2d976e6a36f329b473f5550019d7db0d986ca8003b5744fcc8fca88f88da5ee33c410738357d8043ecb571fcd1668c228b11859214c85cc9665fc51fc928423da2a23c80d03b8c23d6919c27ebd4f09acfa66406403fc91eeec8c1da3463a2d02cbcb91323fe7749855d91f740405abcbafaaff55afc4bb38fa51d129dc53db4b2bb68a38571540647483026804281ee245bd3258f7d0859a8100747d0194e5605cb1b6d9a3cb596fad243ad6a3e0f98a8d1d6cac5aba392457f3e5081f9e415789c09ca4f2695c396777d716987d8865586e45bc24aa5709e76160d3a6485b5a1704311b4c27dd8a0bef970644691958cb1208d97dd842d1111103f5c0452ee5faf0dd801780cba12c1d467e2865dbb59af6b994251728c1721ca90bde927abfa9029836738b42c5b83b3e186d5615dfac49f0eb8c012690ba1d4e60391c4c1998878e5aee5f24adca24b54b9f2005f0bd680a15bdea2dfd8e6da1c5c7cabaf864a070688275a09d6012d80993f0583d49780df3637149a457105851682da097cfc04a63490b16e55a718aa75d294fd05acbfb5d198b1d3f3af320067e72f6c0b33b1a47782faa9bcbf5366b5c8dbb8be0b74b89f56b01a7aae23d524c85ec91b0c96b068daddfac4120dd7404e6e2e92b868e401c804ed9922ebace3b7c064a308e17f2acc5a00f33c6d42991bd93bea82f706ea5e8035419ec0663cb5dbe722940fdec54004112f8d8aaa8a8bbc63fd41f87ede3b7d5bef940f60b3094537b84e8990d3800581b94c90f86641e6f3c02c7bc8cf481893feb6a0ffe989173c920cacc3344d45e40199fe2de8119252006c74eba4786ead2a0fbef2d6bd44001c3bab16dd22b818cc7c40800d4fa703a8d5b361bc70839640a19375b026f577f1186285c189241c101e6fc86f2dd716dc059c412ae401de2750d1d9eaaf4e2c9811dc7e0fc5122932c52ac590e037ed1c7a913841973984cba9e2e05e95183e7f4220d25cca4ae4bcd3c252647e9b4a9df858e85d082913308b4ef328884c40d56ff0abc768c18c60af0bef835e09d331b7f03d13d0c6c38e32d108919d89cb0e3a127c05d4b4306e7eacd9877aab0ca4cf302a5b695de67178f36d6ece4a9afc7a8136b6680e285ac8ed6a9bbb339199cab10ee04dc93d5b2d7df1bf82330f6aa4d13685cd034ec28a552425356c4c1638e744d138e3fa1219ced84061c62f267f8389e2716c5c4d0b73cd501886ba2f569d12c020723b85be2ec39636d4cb82ceb26101747746b4cbe66f9c602eb13852dd58f4b497916a18944953994c6982a6364a90e70cb97b733c6c6b9bc8c09631ee084b8363280a1e28149a55fd46afbebe3a0127d54b9830712ac8279990b1b6f8271dc014b6409055c31ecf50e4c7bb089df186316fc28d33a8564cde037ecb27943a6869a680d25a6bb182f9f37fad730cbe208be24039dd6fdafba4220395e13552759978106c981a369230a7731eb5b3e06798463cd8cb61037bca50dc6017edca659e066c96be0d7e553a6a0b45b26c380215bd3ccc18c317fb1d131c5af73c6ef41835a938c75ef7c1ebcf42546843acd87697e3c349f2f9e526900518bc6d73fee0ca10248b408007a7a24df9ab664f0b40f99717bba90a93541fc13cd3c9297475e68eeaf1ca067158ca99f384cd6012b4840fe0c7f7741c7629b99816e286806a9cdd40cf6db68f145012d43c49bfab595e7d40798b1cee2f7320884de6dc33c6aec9e023a03b2655a30d819a703d9ce3cceeedb0d726afde298d614231a296b16b2fd14330eae4e6a19b9662028c30c8995c9191573b91e4320fce38a83e051475d5c8405071f914f0cebc3d47de1f96deee69501678d78d43612b2bbc92da54c29a5140629065306b5db1193a1455737e6830ba25123ee201a25c4cace1904c46dcc7e0daf34d2f1d79a2468504201f3937d577f5b7242f7798a4fd987b20883e8be9c42b781fadbe76e7708a34728a326a7088a44de2c8a9a9c9a686e9a686e226e1b89b86dc49156a29460a9e4cd25d2caaad47193d213badff9d07de1a2fb309ad0a5d24cd05d51691595a15894294051625160dc845b8c847718091fa1593495a1cd228b95ed4b47141d514769449a1ea5a6cd29126d1ce4386fe6a668db386e34227123126965a554ea5ec0172fbcf9c54aa9eb5ebc800123060b6461f16696173062c460619121a38545464bcb8c1926130d76a1e102f35203c6468d1b0fffbdf94d2e2e3c98be8cf854473c60467caa41e2c1d406b461c39b6dcc1adb1f89c76c19b5ccf864145dfd62fa5b3e99483ac89eaeb2f0890649073e9960fae5855fe24a476266e65ca626a38b0b7471f176619795b142d281349ddd17aed42194a451f66aa5fd82ff692dbd4a413cb088c245b75f4b4174917002ada518ddd28bee4b75030be4102dc351c6a8454dfe0780b8a34729659527a4912863d42247af4e4df3aafc288144d632814a2f4e000bc8050eae2875fff7f77d5f7fa33ac539cc5aecfe172349b052e0ae57bf2ffefca9bdcb448f774522afc69f5e8ce6c197a7daf5b723e956e8a7fce3d334ed2384f063a08c70bb09bb1ff33d5f6046e36fb02e9e8044960c1e7493a7dc9ec914f7ddfbfd98fe3e2191aeeffe8eae3374ff4be20a0cfbe63e4f06e4b8bbbbbbbbbb7b9c394d84dd97c021319d94495c2182599895f19f2796d1cab8efcf6edfb4d48719b40aa19009a20d781274a41122a08c082047788400cda809d1a8fa4310e14ee8ea02296069b0e8eaca8a80096954dd21100144019aa669483ca1b5734929e49a436c2fdb460410e611c23a4530ff266d46d35344c88830d2fd2c9f17071cd2df46683f44edf17ef27ff40081585fd97e476f9afc219a1eef173140e1736bbd38e0100adbe377cfa9c9f67e38a051d0736f07a9477f5c4d363aa86c415ddadcc19c42b80c4f13d4a2a67d2c84cf095465098ed00a9c44166ea069e1cfeeeeeeeeeeeeeeeeeecf46dcf93f67f67e677eef7776f698c0f79f69547b30b3b2c038c511d59034d21126354d7e4e9b72cefbc14a84e1fa4a478f897b43512d995c80008a1248689e73cec830944710d62114c2f6ffae7b985174262c122d805c54c1ab247cf0f5d772470331e98c8efe2a30abc0314bcbf0afd76e3bf8a4e3571755d0dad56f8e02b614412777a454c265134a74b44cdba800093bfda928a5c6900ee109a1f97e9a05e63be57cc49cc348e3fbe288cf03744dfba87946682f64525f9af830c691a649af9726be6700aee42136edd128f9dcbc1a5ce9d8237a4c1c7a2f94223d2cafe082f0f3680adf9b6a32f2d2f843ee85a26ac2080aa28cdb9ff946cccccccccccc39d6dba5f198eff85f157de7e34b8c5a7777777777f7474de6b0b1b191a41f3eee01aa541f7f7ffced5ae4cfdf73d4c71f8edf859d2860aa1cad07275fa48cbbbbbbbbbbbb2f230c623767653a87fe96bd9addfddd25b560790708bbdfc3b5a8695293f1e39ac423c8100aa1970393fe1d353abe1925aa50271c011c05d98820650abeefd3722842c81145c47656c41141312633353dd337138f2822b641d1cde98244175f7bd1699ebeefa33e68f4aa8f1aca3b5c3bf94b1f1448cd9b4caa2805ca5f3ae19cdad4e0a9658cfe04b7315c6a92e4e7d01fc769ff32a71043acdc013f788832f3cfa8f9685410fccba4b1686f889661214ad049a640cb6fd20ff07971c087fe1c4f14681ac4ab50a1524583b811f2854cca4bd31a88241fa4172b06a8938080543ee7cdee2bda07712a230f8d821e937efe1e8d724f878da7dafbbeb94f9977a4135c675a6982aed6c4baf000ad3b63b2f9f0d4e6f5464d46e8e4d3d85403b66e503599f666a3fd636545ebca8aba102d84ae6ed00fb8aeeea15723bd285dba740652d345a1f341bb82a9f47e2c4d936e1d467aaa2779aa3ea8d43c79025243a3d745d1d5495b3563dc12ba67ba508c70b1008edd997778da28d8fb82ffa9ffe674c70ed9a7e036a6b7ebeef3aa0fbabf7486c2ff20f2eb11d4a3000d08f09c73cee905d15c061dadbf241884a9b7eb05915fd93ffa3a7b41348f897fdbeb044e5c319082fbe6beb9efe74af2b918742c5bb6ec8f65c78f5274f5a372765e35d1edc4bd356bdc146d92062a9f39668e398e024c396fa68707bb65a8931109ee9b938edf7fceaedbd42e0dda6285ced5d5efe7ec34f7f922a6bf29d2be666fb6d7b8af3450ed23e907ffedb9d883fff6358a7bedb49d6af421866ede6e333dfaf3d8a95b863aa9c527dc2e8cecf10ab828b5299aa2c97af54ec76614338a198556b341cc7acd7a0746a9494db2f835835146195fb1bb6b8298e51bd43551ccc0ddddddddbda7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7073e95d6e9677efd48a1fd3016613ffdf5cb3df64bb8a3c123d0487f0d5952064d212d68bf6826a3fd1c8947076d9b6be4e487f6935e2b241eec43fb4b4efaebd28b7e18acfeda0848632c8b8e0c28438637cb58cf552e0f62bd74ba0a672d331ccad1ca0d97dee956bbdaa75f0d9b0da956ac3ef282c58082dca5ea984aa552a91a28483673b95c2e974ba552a9542aae83c1c2321b6ad5aa59fc62580c28e8f57abd5e2f168bc562b13cc8d53b2e97cbd572b5cf4b46e89ebb5cee7297bbdcf582a5c514e3c5308e315090ecf57abd5e2c168bc56279d06ca855dd1f5fafd7ebf562b1582c16cb833cc8833ca865c688c6cb09066b1ed969b1ebc7947533d3ebf57abd5eac178bc562b118c631a0209fcd66b3d9ecf57abd5e2f168bc562b15c5e4635467f7af183db2156c1199cb15eafd7ebc53bdc6297f7ebf57abd6a7435d3a67b9960b31fdff76f32c13965ddac59c5afc19984b308e1cc67d4e9be44776abed8d3e605f3548c69a41d4834c6a088b19539f72351284822a9fae10adcc654ef58a364505595ce3bc644a8a8b2c44a1674362229e0c1c1cf098020c5932c76d6451531a6f791efdc3f871ebda858c1f594737a55737f4290fd2a82bf7e52c27f08911c7f78ba804faf4e0e8e7a73f72f4224c7bd22f137cf0bb2dfd30713ffe8de12fe4520747fc85fc48bf817f18aecca78260d3011a80127c21016e146ddcd94b93db845e7ee5e757494ef2a55d0174b78b1121c77f3467fad105d5217393002039f22e79b60b6d1361a71236ee39553b72c6bda0f3e7782d90ffe76fa1ded073f088174a37e2141045a4d7387835bf03261d5db5d80f314cb4c0e5aa363c8d08a99f0f961233c2c6617e5a9495ee7e79f9a279f936f62e2a1cce2e958a523a4881f413320365488215ae842512981dac4a2152e67cfc9935bd5cd392195f3db882965d4a694519b734a29238cd10a50d3b4a9cda951fe388fd0a2bc80164515d0e684110310c626b8bbbbbbbbbbbb7b77c7ecd73134c890ba23e85ba8627e4697eeeeeeeeeeee326a3a5ac68b3172945fb43846334623e20b9954b434fc5fbfcfe889f6e3ff7cc61845222f88d3e8cda561510568c3fdfc0839b33004dcc6744d8e1f4074702b8a4d0c4888fa6b32b6b8c98c56de619e98468d5cfe247efb6e8440d862dffc668470408e0d46fd6b7c6a12b645711350239846efd39ee8b6d7f6e3708bbd387f886335a86952934c17babb13bbfdcff164753dbfdad0ee5f19f9f3c4721aad8cfcc8a41e3a5a850ca1517bd73c26f0e57311c6f818c2e1928f1bbd89b3c238f8eb466794ad304ef74368dd8f5df7e36ec1bdf8e09c9e6b8252e406b49a20eb60e3cb4f40bf7b19c840ff82609e3687fb29e7d4a626b97b557942a118a182d6d9c51557ccccf876010565afc2d01d7193977d7f2239f1fb8388ddefde9096ef3f247a2ffb9b13bf9f48ce694286fd017fa2fbaa83b68e4675071187c08f1e919cce2112ba7e78727e82cbceff049fab422804f6eb886a3c34ad8e643c011951086bd031213a75733b2d6a9ad464fcb657b4528f982c05ada66ec5df9c78da1c79da1c9126124dd1d472744eb7390e65070f9e9a4f24676a4588e44022392f06d07e7e10da4f6d06013f7a43a0f7e2deec5ce608b583620407b47e97604e1c3aeea1146151eef7279c736e10ef95ac11d581423edce6ef6956584714c2efa751a59bf2c43d3c897e3be93cfc254dc84d2f7210d8edb908e72f69a7154f58f1021e96ae0764e4c35af184154f502943d0bd1254a806999b231fe04f0f02fbedc3fcc827ef531542818c280b15794be7fcf6d67bf1614260fba37b5aeabdccdf1cd832fc321f5219749a37aa11b9a807dcc6709f023e04297bd3824fa7200cf682c16031278df2a4581c2884faea3170ed4d8d2fbd1be91494c91c898ca7092dbdb31a05f3a5f7261627eadcf0d541404bef4ceccd8d59a7e26a35b472d5ca575934ca06168b1379727ced27c8f68646944ec5564b4a6b4a4b08d628d3148b137f6cf8da54fceccd4bab53110673c17c4a2e5ffa56b23851a886afcd025afaded91b195ffad6e954943513b236d24868e95bd528ad8bc5913a347ce52a54f6664650a7e46a255bcd5643531ad54284c5913c355fb908b4f40cb437f24bb2d5e29f5694927fe959068b237f1847c4f19567404bcf4ef686e54bcf473a256130de81b5602e5a7a36d2a8183fc3571e012d3d876071a490904e492a4c509aaf2b055afa082c8ea6c3acbde12f951ec8de4c5917547a1f9dd2562e1367be6e0f68e97b581c8d8771c4df9f968e9a83967ea6535aab47a3b62f3d0f7b03bff45fa3baffbec2404bdf9962be3a2dbd0b8ba3fdc4ec4d7fa9f4a5e7c5d1841847fcd297fe05920ea517852b4fb10b2b18563c8cc335b1e169798a5bb55aad1fc6e15aa91f98a736990c2683c9841887d34141211a1fca3cb5c160500683321894e930ce267483ceca535babb56aad5a2b1ec6d97e72f0d0b86ab55a3f8cb3f1d8f003f394482683c9603218e36c3a3544214f8960b0288345192cca741847244483ceca53a2566bd55ab5563c8c23faa9e169794ab45ab55aad1fc611f1e0f881796aca6430194c06a3f1a510e3887466904274c260520693329894e930ce14a2d159796ab65aabd6aab5e2619cf933c343e76ad55ab5b41fc6993cfcfdd0f81acc539a4c068369301aff8513e7448871a60e3b8cf184e85793cb5c2683573bbec75092619f269f0783f7820b5e8c57f2b82634be0ba7955fb59e6bd1f8312d903c19a77392bd4cc6e9d0f82dd418c1c0846ac0606030211adfbbc1d9d85a3f35b616ccd6da5a3f34fe89c666da561b0f8d6d65da56a56db5f1d0f8ff2272916d3a3464265949b6e9d0f837644c0d26240356826930211adfc60cad45d4fa91216a9544add60f8d5f434a178978442bd14ab4e2a1f16158628c2913e97031996cca443a34fe0b920ed2ea401326a4c16030211a9fe35e26939cd0e8a4bfd68fd6b57e687c6e738191746a3a573c73355773a5793c94721dc7a2d1d1ab24249a4c87713618e388ef5593a993d1a9434948609c38273ebec11887936271c48f5cccc4b962b0347183d1f826d20e4d5f7831051df41489e532f165b4811a157d8a9f3a56a3b6272121ed9078b86fe4abfab9fb6ab543538e739d38271cb7f21417a5e37e68fc158d9c6b69e2378d29e8469b6c936d596cb346cddf5dee49ac0e80cb8cbcc5b938178b73b168f456abd5a22f4ebeda1ac1b471f4732e4f712bd39c0d688c31493c9ece53e9446225d1cdef8f4f6235aae4c56fc1425799887efbedd93302928c346a494768946114258905db492c19a037cb105756bc971e205d794857bc9a811e6a2a2dc3793eecc34f9a7f18d65f8ca41d4fed92589e621f9fd91ecb3d9fa104e7a453de7219ce45e37b4bd3699446e3b38fa74846484868941e89c4a2716536a301d6786584fd9951284bd1b17163159dcb8e1ce4ec3c4e93f06b342939fa6babc209460fbfd670dfbf3b9c3beda0022d5a66a99fea02eda0022afdb50475753a644f08531de00bf4209dfd2d31fa22dc88d37eb484e88b4cd19c3f5a62fb22a24d24fad112a42fb29146bf71230e8af8dbefff42692256b3509a8842ab0eba3f3a71a7e932f0b793e8344fda299ee4af17618c3142f8f1618410bac7d6a4e0fab9920e2e1dd898982f0857e1fe3c12e7dceb94483307018a0f54d40453f24a04babbc36d09dd7eed66330cb855515e7553ee5e47827653a1dd5a74162d3485f6b794f66faf57b53595cbf0cfaed354aaaaa928bfd62aca2e6e458a24282341f96b040249751ab5b238b2e2952ba40daaa93c359ab212625166662a2dca2acaecde28a6eaf6eba662b85ed274b8ed28c7beec2916afeb6f5342b72f026e281b6842bb79e0b625c0710bfb8b89f107705c894733747b85b9071768c9493c8022f74a8ea51931d1f1d0b1c34e91dc2b4cdac1297b364bb39f03c29565a7e203b333f12e890044883873073cd0f573ddde103add657729a2073a7e8e3726b1448b6e0ee8425909512e7aa073d7e469997e484547c75dec79073a77cd9dfe589dab51fcfddb8ea7e48fbce53251a8179b523ab5b966ac5170c6dc355d9b8bdd893f819ebbb6ba2b4b962c59b264c992254b962c59c2b3d37946056e37b622e8160a0790d085c2012394eb91694a27a5e28c586430cfc281ee0d1755f68b4aac8c8ce801ca4eeaa1c5d0acbf289485c5abb045a10e1d8201c3ab1e4461570401c59ed0aef3aa1fa15a906252900501adac78b563b445c5cbc7d5848e465eed1505bac24c162444b78d91502da8cad04c36a7cf1062b0975c29b486d66caa2e463483d9a8b38c70a6c819f6607fb01f4321ec0cb202f522b48430862f2736658380af70c901be42280cf015cac047c3f87edf62533f80f80a9128c057b8c40f5f61087a7c0c2b36c52b1fbeba1108f0d599d0c35787020fdf49b12976f1f8ea34d8e1ab43d1e1abef20877ff1fddec4a61868005f9d04395fdd09fad597ece8152c36d52a017ced2904e06b6f0180af8e848e2f4db1a96ee17ced2a6ebeb615387c6d23d87c6da19fbd197d7f2bd954c7525fdb05a8af2dc50d5f9b06399ad4c5a67ac886af5da486afdd040d5f9b0435bd7d3f136153bee332fe38be721266f8ca58d0cc34f77d6529325fb98a18bef214327c651bd07e96c1a61ce6321ed3ec02daac04867e0ec1a67c36bf452d3417d92e68af15687f0436058fb88cc37c5d598dee1e36055f4078d81bed5fbeee0f006ea061a141fb8588f6bbb029182323e9d0363e11e0d57900cf003636a217105e9d05f06af783d76383569e121df1c1ab93005eed7a58898e507f1ebc7ac3e78c8757e70e5eed7470cda87f0e5ebd41276c005e9d395eed2803f9eff0ea0d9f3b02f0ea0c80573b0078d53477a8bf0e55cb53da108e57e78d573b1c5a43d4dfc6ab37fc3be6292d96f2ea4479b5bba16331ea9fc3ab377ac8535acb06afce1abcdad1d043353d44fd7dc7539a0a8757e70c5eed687666bc7ac3619e9240d2e532fe327875c6e0d54ec6ab260944fd3f874d18bc3a3b177c16e3d51bf088a7e4ca655e2d78f586c9f3aaa93b79b5f3ff23f386576767c3ab5d0d0fc6ab373cfab848e6fe2f5ebd6172f1aaa9a3e1d5ce04e70cafceaec5ab9d0c8fc5ab37280703863775100944b234fc4202500c363414e3d41486e8c80c5c2dd18e14294bc3331108442b916ac8e55a1aee4e4db915d81541403167c299589ab983031f574b265b1a6eba726aca8d86a609266baea6eae767696252900501f54eef2c0d37e5b616152f1f17152a4bc39a4a4ba21dd158da0a08e8d47403bac24c16e4c48948bae40f244cbea40fb398b5344da55c492672471e91ac2c806841558666321e96a6a9f419420cf6aa31346669e249245b1ac76265fc7d5b45b121d1da82fa14220b76699ac253530e8eb65322998ef9b3a217f5af29ba3f628189a9c111bd18877fbd81eee7d81bcacd0abb59c81f360908e8358f56d0f5f349e42f7a6d6c53c186a8ef6b46dd06ca5f4539a869e9a28ba31c7bd30f809da20abaf586962e3afe0a0dc0432821890a942851a2448912254a9428519224499224497676767676767676767676769224499224c9cf069cd08ef676febd4b93826efbf3e1eeeebd036fd6eef6e84d3150d8f0631cf0f739f6d5f68b7fc36778e963284d34a1fc3d99f9bdf207ba971d6205b240707b412a113a699da1f025fce5557f2898f99c5db76e82dfdd4eea48d2a1a53b771bc9612344a770f8fd1f8d82f12cd8ec3a93e9bf94b37434aaf37618a536451b3722ad94ba172fcac56091d132c344c3e505a6868d1b7f9aff946b61d402a9071728c948a36246de9ab3eb4c26a7d2f193e65e18e1706218270ee227cc2f1f1795ae72ab55a398e8140e1f7df6c69d73712e29a5fc5eca1929c34b29a58ce1a5c491de4b29a53cbd943732e6a59452b6f052da481b2fa594b2c64b9992ff524a296fbc9428e9f2524a2969bc9437489897524af9f252e6902d2fa59452c64b698334bd9452ca192f650d92e5a59452722f250df2d98994526a2f6ba494524a1c925912c64b29a59c414a29658f46915ebefc8f464a295f90b27b291f4af92c241da40b30c8e0dcd8a45037e4b0a1061a6a70cc40f3bdf0bd3023438618643e185e70218677fa8711d382cc939d642f6c8c6a6c36b61adbd63d7763db361b5b8d56c965a3b1e291ab5f817971a1416a7142866c9131323931e3a7a5c5b1486ec52357ab8d9de8a24cdbe08bc6174c88f3628860c080012569b6065bc2985a1735d825d9adfcacc02409899123389c7ce532d1b537cea329e9214fa75c266b2273e2b227b4f4b0f4d089c5813a327c854968e92192bd89e14b0f559d82ab155cad206b058f506994cc168b03796cbe3a17437b739ad229d86a09b5b2686901d4a8b76271e04fea6b9a76a8c4acd94982dc114900000293160000281010870322812ccaf350b70714000f6988406c5a349607a391284b71208410430400000001001000630064ec08009c97f867bd67fcb8afcf2ff66afd322deebe4f25f6bdde44dc7eaf67d465339db6b2040345e26573d436295ce1416a1b131287a426f3ed3f9b752b1eea6752d6933374976730fada0fa724ee278a7ecaffe7d6bc6911cfd53afd623a141d9432eff927cf212678b91e58399f5ec73a15de704b4744ad98cdfde47ea55954568db5fcf85a8d048ea6aa983d9a8d3893a1fccf29baf956312a5f1471e1bf2bcc608b0085e980730aa150737261ba9e95539167dd746b758a821c96fdb3e8f4ae58a9493e8e743ae5bbdd4a4d41c35b24be148d8fbff5b72e051c296a6076a3a540b1f5cfff244bdd4c76ec0e5075a51898a5e8a4a1bbf8a1bfab5240a2da81be3b526a88fd63434340e232fcb0b206943ca9a17b2c297746177eebef9a143052edc06c4b0ad4701496abc6ebfffb5f920a70ca002de88ecc3946aa7f80ffdfd019d036bbd727ecf8200aed87667cd421b1c9ad7eb29f1b665c577689cb6b72945672854662eb62a59133ea96a06a2950cc4ca7cb846551fae94541c384feab08a7d3e4d5565d95bcf590adaf089876f741bffb90a4f365362b9fca2b1ba79ac91d37e0cc8d5eb204eb344d6261e4c778bfd0867a32d24d9099f42752adb54804208401e71a5ed2044c1ab40553ce7a24194e0a24778be5a60a197130a6eb6d4b113b106d8541915f31a6d3316d561e7e3c7c149305b64ceba5bf40ea690de4c79f16de39cf6db304ffe725a64fa101de82eea21407f0f3f9b7a0085d085a991bb215c16c02a9792be5c53d884d6479719d13277daa649af298ea2f98391897e7fd273f00e2798f30e8bf450cfdfccf95f301e384e2d4dc606284736380de836ed7786cd5c7a5ea05c8cd30b3a7cbb052dc32e6f21b2c26635c6f80cf14637da411150346cf30017d0a8c09d4e6a473c6b8bab9e8f7d48c63f97763eb98e0d202bbd1af9fb9a8040df0630beb78f74a0be167727ed79cdeef7f3ac43bccd99de9ad17d004baf375a32161ad00ada99c094b97158aefe52a033a2077ffdbd873fd470d57e3ebc996845732eb35009f36e69299b25ded7dc26cb9e8654d0fa9ae4bd4b682c901da084a1ace9d8be8b8b1e6a8e2704dc0fab0dcf12d40a65720f6e6c8f21b88baefc05be4dc81a3c5831bceae53d7c06fb91bf76f803fc08e143c6ea3bb71f6907a873fd3901cb55728c405ced6f29428a40562c951286228b3a81faa54e6263c11c4792bad16ddfd4dc941add16a222719bc1a761a64837599d863edb2d83a46eec7da60de8d57a6a199c38877df5b6480f7e60ccfd5906a0782c6763b597ab376946b8506e09ef803d82d2201c8de4ee280bc63d080357840f00ba72438fdc8ef75d0e1239ebbc4d0c4569f0fcac5ed4ee490b898a2c9bf77d8f746480923a6058e19b83487efc067fd6dac1dd856d308aefd473444dfc6c2c7e0e5b6c8a927bb6c810dabfb48c1a4981b753277a8e805e7f5f69684ec2901f4e11241681cbd42759ebe3e6bf72cc2ff691f7528dac7bf91ac7f644371af8c0dcf06499aa51b4447b0dc1f4501f281f30aa379bf980f64f2b8038612451cef80c99a7243499ff3673e0dfa8bd0fc460f490b6a581acc28780b451bb19800abe6f15fcce1d8c298b8d5e7047332684a12d4ab8d51d66ed1c8c4378707b45ace6a4814136791d910d50f767adb029be359dc6292f3f7f8f1d1cef1644d9cda2c801ec675deb9c8bb285127f8b40a21bd1005fdbd8163192e200aa549ecc862420892ec0cd0df4fd9a4dc8126df90904f21eb7105b7361d99119a5d9225605781b099132fff5006890e39bd3ee3199bbf8343d25c7612fcec1853d1dda967ee927dedd5d1360434ae515798d0fc4276b4b9ac06399be057c43b551a9095b5ed3a02e92e31533d6832759e479f9d075c0f4dd683fba193d95bbe8c45f7520b46d4985e06119b89dda5a9a40efa2f78b7d4ca1281eec480ee6e14e5be5032b909543090e911ae4ad40fed928d4c4bacd365a49769114887f4b862b83692d4db9b8f0e2d8d3d3571c480fd02dd7b08d1e3deeaf334a46158d12f855c8584530b3d2d6e9beed98eb8235308c2e99c2838e446e755ac7d5fde8e85546679e638aa11c9302fc2d8bb3d88593582402632ccc643e711e118c9b4c13f43e61671533a44fdc099b6115d219ec124a9ae4840713570499ea21192a5beb2319f9dc25847dec0d57c000cfc65b9606c4469416ca45241ccc872416ce4f28958036ef5f65c546fa05b46bd3a7ef11d25ffd705ddff7b41e7fb5ed27f3f2ee8bfcf255a39748b6ff0f45aaeb70f38bf2d8f0a41c4fc38d17dc2e21379a1968f2ac6e3430089aa5a32c1a0a03f9b3a4a1778158f69240e58935893828059139f3738a61b1afbe360c3e6eb52160f3c75d82583fd1027d0376b46733116d31ab3bf2610f61701e6bc0803e100274b2633bbf238b3e607b8e93748a771f3267e13754478ecfc711c5b926ab8150716041ccd854242e1f8d971f3841e3fbb838f75942d6af7013acaa54d272d95632800f792d2105d09639d1f2b6483e9b01acce9cd040d3b3640a9313d023d43a2b0fa679bf4599ed8685efd7e2d91ca668e96794f390350c8dd02419b66d56b719fc6425eded7ecd1fdf6ec5910fd2c706817da51fd547bc3be2274f97b2662e36101714fed1213b88c606551a0a8acd90a053ccce480819a5884f5fb8a9d3888da4c88e0f73e8c7a0301e2c90196e5b3a843e7a572bb47c12ff269aa50e9a9e5ad9c8a4b63a5f29ec909038336104898a084a56914179b090e2d6d32e7a7587c62a9b778234cd40c27b5ea43024208e526e89864cd656f20297ac5a02da12bd6163ae4ece23aa9e3b91f8965d7dd1ca534168641d0ca721136d13ad1f9891390a1dae45d5f097054b3479a5bfc4a427a08ab6bfd7540129d6d9ada7daaef0b2d4d451d2772442abb81ff216864f080db0911199750c3016b82feec575aa18bbf8cd873c99293f21e43c2641a56cda561696a3a1666a561ad26b54b1a45eda1ea4a056dac89f4ac9a4bc392699aac40138774f9b3c8fa2e8db8cc6d107faeaff4c6e557278c71175dc0568df33405450fa9950f879b0377d2d86017a21b38e4fa8da50a88732e9c951cad7c5c986efd1df649a0a7d4c66df56dbd3e69337ef4d3918e32844bbfcc7b48a13cb014cfb60c7401732e9631c33aa79abd05b2874608830f309b4b3bdca92efa0e2d749d57da48ef8f7688f718d80eb4c883e96d4dd06d61d93ce7989959b7689ce3574bbc2f84b30d5be486f764a5125d6119fce71c9b4cdbe5c0f5a316ce0732a98d2dcc81796943b3cca16cfc3355cc6cd376c0f9570bfa5304dc8d59e7c0fd6ca38f1d95a922a9e1997ce775989b8802c673a6a0e050b9c7f8e70ea715f71af28cce67461acd009edce42a5059dbba58c2c9b88985b095742204b278b38d1221a37254627811cc20b0c30a07fa58e2768b95c9e2b6bf567196d5cab471b13eb6b8656dcfbca0bfa9a8ec34348304ac53f6ebd150c6e4bc6b4728ebe5404a5af3eec99db2637474dae173e7c729dfc521ba0e3a2f37c44a6897071a10a138cfa625fef30843f9a52fa68127f2255f22462924b655715be64f261c31e0e6014c2fc02f37677671a35d71fd401c65432ac7c03d33a062a3de6190476f43e44b5848b2c17a050d650d8119bfaeccf40e6d0a3ee5481c58718fd51cf4c30b28a11c42f66fc79c6c0d7ac5e8cecd0a2e83a15ef69d8fcd77344054275e93509687aa3c030f2eedc05d2648595eb7f0f90b18a1b36196c5bc0b30e1260f0b3900826eb56b323deb5e704ea63baebb0115ee1ea59fcc80b73844d094f0cf7047ab4f0332f56e8e208065afe7fe632eae2dc8d90b52c685f8cd82cc51378f1af483c4772ab3e9bdc600ff5cd05391d96827923e316b223abf67ad856feb5a2e68d7f0270d0da7947c21954c53cf36b15708ef587deb3e1961aec099b840d7d760dcd1feb6d06d2566d6ae8e5dba50e675964597055a95d05d22ddc3318fa6dd7111a7e646f5e8e52483b360ffece77fc9c0b39ff3beef538f6f00839ae3cf6d9a6cb2be6d0b934bb8a26bfec45cd7bbad6374915658cd9f98eb7ab775dc2828d2005b819fc613213b55dc37c0e457fd52859514af1d3586026e81dfa513c56ae4e1efd0c12df0bb74a2582d280e59b02b996e530fb874eac52339bc32844f82e3c652dfb297e782ddaf80b30dd243ea353a1a70a51f181f96d59664949ae819f28a01ee9d03c57d2ada5cd91fdb80eb404e8297f38196ddebfce5cd969b66b810ef864188b83d8841c108070e5e26bed94a0be52bcfa55f47ed9ec7be9ec9947b14712f7f78570786c2e8e935e7906dfe6715fa08b0d022dcbcddeb0239cfb4a766cf5279884a8c8f3c2e68a63672c57f14faa4401b01bf5f3a11aadc5f9208826e86d5f716ca828330e4e9c778b8eda852f2a9f09441727355ee084246933f7bf7efb8da572fdfc2d5201d6992212a616ec498649f08267690bfb6d4e79eee80ae5ff9a50d3a6a06b20fba31a1d5025ce28e6313ba86591830df661cf0d4a407d4ecd314cf070c206361f304f66dff2ac25b40e380f0f57d4e86934f66743c2187d60dc34a93b07ec93e1dea1070032681aaa13a28bedba481b4448911a69af418ba5baa089983b5e80c1bf822b5b6d92050a2e8f46f8d1acf96e3c46c36ba638d866e42f3517b72a027c45a4a30d21aa1ef79fbc228c20ebab413f3b89ef1793f97a5ca85ce9945832bccb8eaebf28ef0ce8f28075e5e7f93156e8d9e95f6f2e584f67feefb02a0aaa7c39d6be586581fb7114cb04f37d31e78c521504cd08bd02fcf7e7349cbbade47e34e4e92039e1d037a24c1f96c663d64cbd0d98d6526c2035895cff0386ee19cc65f740f02b4d9ae0e86458f8a6b06ea41aebddcd8643485bc8a7da64f406ebfd61816ae6b9c065f4a7d2d53d918cad7037a3645c3179565a833bebdba4c7f303b8b144c5888ce313c85c07d965b0bfc56375138074187a2bbc0291f40fe714265b3006d21f79aee0f251d790b7d54bec15233e544a6233943baca15656e15716ae0f1f38d89a4957d35b37dba06dad94684ef0f2af3ac1f4ec6123d83db48256fac6010ea8836b69d5b022357e8056c8c213ac4a759e8cf9cc7680731de0824dfb1b2846b5be1dd573bbca0b31a8403d475f7fcb4939ef1c48bf9a33f78895d92bba15bd82b954bd5b153ccac7da81d0d91c7fcd8766f98da96e50d3605b3f64eb3f75b8efedc2f5fcdd7d905e00c51132066a2a8f4fd8b5b3a73b49349504586cc8089ac17baa5b4761d923f58a51b20a3f10af2064b6a354d45a8e88a13c3851b6c0b7f3ca3879aa56a9c4e2fdffcb7188b6910b0666a3c01c85ac960bebffc66916e5a34df83e171a46063541a9db0e5521ab076c0e48b60ad16a13b90934fa9c718990d37c889b83bdd8c1a3fe39d03688bb62f5a875d1356fd6ec96f7e83181b4c0dcee9d75b8f2c0db0df167bffdec765aa382d38b87465a9b66717593aa2325c77a7957a10818348284162be0cac4a5573a7a320b129e290e82b8c84b83901920d704979651999fde1d851704f83590813cad334ac9f6e6cf1d1b33a9dc6bdfbc46013031a31cd65611602c4577ffe254d278606fde836e30904b6ea76f4de1f4b4286c5c44ef39eb0cc4c2cb0f76bc05a6ba1f1c2d4d434951b186252d3b43a687c87c413b4a5082f24ff9d4899fef667fcb90162dcff68aa3bbb713963e2fb62fd3b9c642ccddf374cb5279a5d27cb5c84658f3a8ca662d60de8a68d01c961f18829bdf89acd14c16c273e09e0cf9b43cc64933dba0c6289886e815cae26c3eda7da8b17dd8dff73040c108e2928ecfaa49471f5d36f7d92a7c2af6c12c371dce54ecb39111f1c377f0f2cc3c2d1d90dc0ccf291ec091a8ecb2232abc29888f798864542befa41a7ba7038ba28360274cd360dd2228a8f063b8a1a9e6ec625344901715e2c45edc22f89708e443e04b6be2370149a1705bc3eec20a592ca25d39ddefe1e88fb926444ece00024952240d789e8c5a448e3ec30120aac783dee1dedf5834bea898f82e3b8e58cf48cc9ed354394ec90082efbb1fc3f174f57591b2a9a1bbe8a12bf97ee13c5b7e9a63c3e0a9db2d7991b5698c5bc93293e1c9ed68488341f6daef684b38b549483891cf391feb36a5bdca467adf90a115ef1d8dd7738f573db0d93c1556930afebf197b6ed6974b5100c165de4dd9f966b4c5e9e31cbced41b6bba188757e01e0c354f42a9b23e31aa5222722fcea7ba5ecd6a63b05fbe026cbd8c201ab3bf009d18d7c9ab4e758d050b87109c8c4c2ff6f2885e243470717b7ed3e52121ddaec10c1f7a5a611cad184185838fe3e3a896a8011d7c6438946a899a83530b9babcb9c29638582aeb53f1b743d709fde074666b3b391a75f7eb6a2aa47517f3c59f0b3428faae673ff693bbe2ae9cedb98d1ffb6d954dd1a7240d510b03a0df7e4853dfb9833c77fba4c238b9855276da04977582e679ae2d8f07ccb5f7453e2cb1919bca2823d05a6967fb22e67505e5031eaffe8f81b97e2c8e19ed657aa233cc20cc6d9310b5dc47dcea5b7102d1595a516c02a52518f04f2548d82f451d6f1ca4dcf472859dea19b93473a1a8b851bd7771f9e808075ed5046218c2dd72b549ef5a0243a31f77f44eb34b4544e6e14a1ee29f5658be13be37062e3462b97e38cbcf72781de228ce52f8509d9ed60e26ec9e3d4ac1085c367ab8eb4c049c26e2a07011087c73310c3bf27479e1ee2d9fc9a4447ad8b9c4a45895eab8ae7f8f19040e2cde2751d9fc92830ce57c34a92f74540017491659ca8930f92f145b748cb6f6d94f5748d4e97ff3b44620b4b8f4aee410922dc326799f4e63d9df983671b65e3a0ce431480d35e400456814262bf61cf99007cc1b9868ca00354a0da2e9e81469f7d2ba2d55d843a670a7ba7b49e73472d875373d9b972e94cbb030e2f54aa35ad649f8f283df9fd6f23e294bc0174290baa09c02925fe46d01380d7288134890f23ae9525346bfa7fa6f99866444809c38301733841df5896959678de1ae3f44d47004d320994727ef045070f3c044424e35d4a2fd7b849f66794c1dee9ce04655a82f8c24ef4ec4ab62c08cc6925c80badf2eeac2ceb45f06ef983a18d8034ba1dc724672c7af4587e75545337023881ca735585861dec978d219d695b75c271bcc402691b9b4824936eb5d35f7a0b87b3c67bf92a8cd1dbe63e23930b30359f1b6c7bf468105cb9d83bda0f066bdf4f8df39bd8789e03a0c9d720ec28892c7effb828d11ca08516c2dac8fc85f821bce32c6d5418fec0d54a835bebbcc02e6bf589d39bf801ace6d461e46ff203a574f3a85ef475b6e15232fab39f9ba3d20d3d4b78af4abba7c180b8d3b39c4ff3283363e060ecdc16a4f130795d3c39295157ef315ed0a04a1d0b4882be861cd9d00eb8f6df74cd8078d5e41ce5994ea7604a6c0105392b9d780cc91a197a605317041caa1e0a2fd062402c9d0b478107aed0e737f3b817b64842662e2c1fe5f3d39b8d425a84d4009c9caf3f4ecb3d179e2d06d121f1cf7e71bf5e3c40e7d70fb9d8a634b35a6cad820d3355ed779f4b19d7b7376cbf68ad628b2163770de20617c7f0b1691894b4be79e8c7c379fbddb6b9a910e8f742d74d8b7d1b21117506821176f0389111a9fe595537fd826228607002b362c0c2575b1129fdca3b20d416765812839a1e1aed5844e2c5ad6240b09feb9e7eb3221f8b7b5708d39229070f7f5f0d452f5b6a12e753a3145ab4f508c1f41039c4173c18b39cd831348499a2d30567347810f05653522514bc8f757a35038ff67164f1dc962ebf141589515e4831f663d628f22536edd56e4f122ec880e3a2613728a7ed7f01de21283626bae357f8f8b4a415ba8f6345a788c3bd7842da5dc53639d3acea5501a989d89dfcc09eac952aa4c7b995d5f3fe2fb1762cd207884f4b1a14ca6cfc8d2cc40ea3fbd9d5d143d29a082886dee2c30c39c785b2e08500f31be99232009226e02b88112c28f417d7b0c2e17d0a81f3e2ebf13d1ab451b2fdf2c322bac41b6388bc822f4c83ac4572e67dd770dce56bbf7e2c3ac5c1570699a039eef63ceb31bfe0deffe5679af4741b39e4433c261545d6c940e8c21412baa0f86a7f4d86fb7593e81ebea99d4516c49b9bc6e47d7d91cd126abd5ee4276bd813c402d20c07e5e5d68674afc9ae8e79865787a97bb93668614fc4e4211b8b734fd6a99cdff659e612f29f370208dd01607e7de760c5dceadeb8d00d0d1b86420111d78884c03fcee9ce8f09797da8674ce504e1f9bdea79b65647e4fa3a6ffe4600351db14b426b40ad1a17d483e1ab35be02d23ab58eec7f1aa6f47bdc90de2bb9d0f9f316fdf676800d2a077f6c8cbe6129f02710797ec9e90eb73bac7ba2792d8142d40174f362fe6a8b7e72884e2f60ad01ae627f9340809695d250eae4fc6fb0e2518a5165899983f0f234483317fe8d5abfc361d18ba83b064d89261e5a9f85d88f16bda043a2d8ac7402330a4cc0652f95a6bd2580e12aa1d225aa421005cbadd6938f935a0f282077e2db8cc133a2ea00fe31e530d4cc0731d3497b18dc0a05b26221e53c66e984ce1687c8d820505ad309a698d82a0bbae923067a3958d2970710fa39c00dc9ab94cc1f20f4f32f089de90b4b2f6ce74f5c10b4191708facc7541dfb7e7554aa70f50bae99785dea5cccfb820e833a70afaef6f2a74a72f2abda6fa2f0bc8ea355795d541753387e761f5cbb0fa076ff08bdd08c183ae6ebdd749733b46cb600383b4f51defa5a1b7f0cd65d57d9d526c0c2fbe9ad2c6d2f8bdba3b521f7e2097f678d44515ceb0518447e71363047477b12ba1a7fd1edb9cf05878273a88829e6eec1aa2b085f8464015721179ec4ec348d0831673a33d995a9e375b772b857379e6a5ecf71d6a21527c0e6eb0752ccc1e81e08ad0da2ae1ab8d4c608746ee9b6615dd204617be852391359eddcf627625d68974abd896f7afa1bb7224025684dd0d1482917207bdddc855ce4e5eac9601145684f12824924157f309583d0ea060fdfe0edbeea111684d9ddfc6d97b54f4587b9a57d52389a9a04a579242ba6a426560a92851de30cefc6926ae2047963ed1de0e01f0673b9f9dc140364307daa2774c7f5508d2b94005d5475b0cde542c00a6ad7a4fab12c6bd1c02c149f0a739f23c66725f2ff6b2e9f486526c4439249c414174dabd807ec8cecb16c83e4307c1c72e6e1db82c15853151e1ce7defd62563a8da59f2ec333f4fa7970a0bbad05c98a6b3b401f0aaa6db83c58bf933a880ccffb554817e5aa8467889b7ae598fea232e81a9169954c1ad79ccb2e820886c6a6cf4fcf305c1658ddac6379f80b68a87e5c3432a993b2072afd54651ca44ead745af6383e6479a68b7b24f83784ae013f13d874e6b77c5549b3841d026cb1d78af20fdefade6d8f43c9edcb53bf2ca70b6f6358fea531769aed84f1b95696d708e9a59b509d25ad9792ea8b073fea762aaac6dc1c1ff72d4506f0de41aa16a185ceda5f48d19424b94896eaf6fae11adfc39e7159c688141edc6864f6cc537c76daf8e602eba54152f9b048f041ff486a1587392ab97c0b37030d8abf0d4ceb7fe6fc0dc6bdcd8b8dff3ce7564a873dc7671584311f1f775c6423524658f264acb64bb73ba12ec1c09441d3f309cb9d832663c1de50b7022a4a3483f94d52db2804a423596fdfb604dcb64853bc917c237fa62e70d0a5818c07a4ff4449c5edaf8cf8a26e1f885422109990c20be23a1fa895c38258758805f7928725d3c4d69f7bc5b3f65a2669de1da996b01ad28716f8ff7eacaa4d673e320fe3626ddbe9bb5a086476043a730301521591dbc19c96e8f91f88a3c92467058be4479bc3050d89f30e89ea4aa7911dd0bd4e720f2d38543be2e1c823c14e6d317d832db1496ea43b58efac1194901efb914a662a873614b61db8c28fc397a13a4e948fc0b2ed64a45662cc689197a3b33474eaaab6efffbf5838cc8b879c914536f6c50aa42c01805a20bcf0fb9e9ac96f7d21b8a38b64098b874fb7db93a3a05501601ab4cb6b072fb62c8d5c317cb435eca0c00cfba1c6bd4b92ad31e051d71acb8d664146362a10063fd3f3e68b79357a3cfc67e4da78e995c38d91205aecc33a8796982394311be1fee38a97380b0a21f4d583d600962ce244fa83693bca154a89c6ba61031dab65104c1c01c891c7db96e7b77488edf703d1ae241aa8c1817777326c83e6be99629bc1df8235d881a3f1088855640add9df7550005aa94e57470c36ed749e92873fed8b75d9cff309dfe2e82a3f196cb69c9d128ec95f4dc225e8c467150ac12c5fac8b5b0a13fec98f28146cddd7a3a8a3412da8a81dbe600571d2870b90059b90cdec9ee13d1ec93d382d22c839cc2ce30755a4abbdeb90da50e8b9624f6fde99e33143e4795c59de84bc41b7eeb1bb26b1ad06761cab308e06976c00da1ad157ea9c58efc3e1737f98a0ba1ebcba7b266c61bf7428b44294349d2e73544f14b4ff478a681a50263db81c476ad9f045aa016ba62b569ba8dec23e56e03e39d6e555a8a6aa1ad218aed8ececebc98ac47ec1176d6299491028d1b1618c141d41f6d049e4e667d8a9a943b409652dc237e2d6bd01bc67648ec1a5c89d3aad225b34578bc00694b05ba839f9f82ac69dc64987bfc34ee16545e9283e69cf1ecdd52a7b505bfbd8ad85bc6b5956839065c8f90094020112cfc017cc577e7506748b8a389f1f1ef77082028f1d4082b00fd8808c398c553a6f7868e3e069c4a923c8d3e3c62aa894b5bc3d2dc93d2057e0417f4f83d7329806caf74e3178afff69105d2da188b7ae7753fdd6f538e84831616501b843f8160ad909438ba2fcc1f3250719222635788065939ab4db64ede759c9c082a1f2d63b11355df7f0f515466a45d2083428959f05a8fc83327f43fe60b8c2f2d3d9c8e59cbf9c4b30ed923f6e73fc72d72d4965cf57f745f9ea613a1e894eb788660367c00a6e83ae313165d37704ddffe83c64d5329a09384ba24fb96861106af0c780a18f9c619188d2c00b6f5c61ff7f71554f6e97624dc72301e5e2830a22d08298ad280cf278d3902ddb668c71b1dd4b659e418a59aab1a1209f62193730fa7c287531a9ef6a2c91b3c75630144efc14f69d579e397128a8a6e8c934c10015feeab555e6096466f4143f1a2f0a1db1e798e054ac1797a7369772d65516f622fe8b10a1bcfc0ab92a766ffc094337fd50d08406513e7cbeb9f7a6fe3713500636a4206fd10cf1121cd26d0354007752cb6994a0dbb9b78ca5afd6ab6aeae62a8ce245c398f44badd4bc00212f1f5afa3a91d441f287a5077d2b8defdb3a72fc7d182911827e49271bef95fcccf20d0deaec1ea691fa2a4b8bdebab10c2488b9190628552f34a0966c3066493923fdb18d53742502d0dbcf4cf768dc173065651bf96a777c1de86f9a0a46d86a61c2e5ceb10375d5315474a0283e60aac633cc6948b98a6781caa0cdd94256a2a95e1a0a2af1bcedc878ce15637fd37795acc81b476016b635aa4574475b70c5068799f077b9f4092e7776cd319753462ec248656c24bca34fe75056b54d9b78655086e4b9bf0ce8ffd04aa27972ab7145398a6dd399a2ff3451a94bdaed499d3999055ad5e814355887a1aa884abbdb9f6256e3758ced1caf1df3e777c1e773cad34c0870fbd4c8a7b8cc0df65d14268cbf960c462a38a81339bff1c63934eac839762aef34022c8772f67268d457e631e6f380039b1255edde957635506cb90f305c999a00f81cbba61eafcdbbd355ea313e187e436768436ddf8000d6b583ee22be3fd5c8a9070b488939242a9b1dd8c829151904103d3a45701810558f8d7696c1beebf2f62105eda85a47ea1ea41ee38d14112e0456fccf961985d8ba7f9a5c4123ac30bdba889a034287a804a7dd0dd078671bc1cb40662a4a646798dd467380799cdfbf4b03a78aefee23d434a1dfa62315f12cba5d1dfe9e3bd5cd84f3789a3a2267345b300c3d9f0c0568a7e5f74f28f90c15ce8e23b7eaddf4a0ae22d306605dea7f01b5a05dd82d08d69d24af43d63c89480737138e83756ddfa949e795aead86fc02ebac52439d86c366d4a261bed684c0f94ff4c7edf6f7c715348e768e51e81987bdb3e6398a694f61569063c0e5d4b403385aaea1f12be45a65ea41ca12597c6e4976db33d04c49e4e5f089ca1c94258b1c4cc0ea26614099ac0b186eeefa2e8c220977e55f1a5f5813828fc245377d1eb91372720bf08e5e73569fc3254a3e60de0ca493a82cd2187b7fd6c180eba493e5bf8a42e236248679f8fcfe95162e2b303e0ed0b33e9d5fb3650741bf7817f167c6e20cb74274b820bf251a77b74254f5d95f6ea120e72915a2a72b83d5d8a442fc4d214b2d46bd429ec0993626887d41440b1e3f54abd67d5efb47ecc0f41ef14494d5fa73017e3fb568bd534c4bfebed50a3c4a8b1b2fbd8da97602150296b60b1b497acae81a5b74ccf8047c72ab236d8e1c1348e255a1cc02dda55eaea10ae7048b46d8c558170d59a995e5f87a38b71efdced21f510b6ed87d90fdf0f840632e029352d2d94f5bc4aa90e14c813ad480d9eefc4486008511c816036eecab2fd6a769f988dbd97735a07dc439df40decc1648e228c7fdb9b3daafe4bb739bacddef19934f1e568ad258665a021017446d4e3399890d60a988b60ce46ed94e62c450730598aac47f15d52be3ce77b3749c34ce021342ef81273a1955c41acd5a5443131a18492fd1ff0762739a2ea3dce9d632445a79257de2b23904c8d448098d22c2b50369822d443a938c57cbb170419f63699950a739a6c4e345d148c12da9d59ffd08dde072b9d11ddf2385ffa522ad7aac68900503b55681010b9b19c23e2b98313b38e91b382f8c421439919cec059653dc85aa4259ee9cdcc50d0b43854e8a27b2ab343fd1578f91da6b7c7bcfc87712026d18f4e14470ecf6daabc5e49364504dc9768d7762fb72100784736ccb9181cf66d29fc9605fae0ddee7277bf972ccfac1741ac4e97b5a554b9de3ca3028ff1e753aa3602046ebea01c3163dcef92a27ff8753c6f3f862964e342c349a4976c4731ec595be7d03ed20addbba14b4f5ceff96371af400dfac4d8549a50001c9466b9edce8cd11a668f83019c1b6613363f031c1c15641f35055be4067840873d555c92db878374492c0b041f0ef5cbf1024407a394c19cc341c2be32b38ea605933addde716aaa4e4ff4540b76258085bba20db151ce11aa5a9414c2e5db70c4861ec8aaaf28924564a1c195bb48d5fb4440d591463a9a6a085a951287e5279f7282dc9d5d578d9a87ec0b4efb6bda45e55f42c4cf4e4a2be6c81cceea7a272db3615a0a2b6eade96f6a763472f74138ee226ae66b526f994680f1c122bf3314b8290e19c95be58e92b370d951f944503c69e31c0cfa713c51d6092b983fc705f2761d99759e16d95f6ae92445d8915390184e703b4aa36156e2b16f40572670f7a79a8f8948408fff1f86d5d212fb72534c315cb9e1612c0afd9df9d0146f612a9ac01f9d725e46121b4df22342bdf7a2a60795c0851d7e0636a951568805d87e02847cec77a1bb4f17f7137b5415b6edd91d84bab4db260a6596403afb6cf44c24f7e7ff1f7f6f8793aa8b7ccbde7fd4b9c7dece32b9877116243d8a3fec6e7023f29efc278f8c200687c09fbb6d8ba8f92954435847e1d83d722b8c711e3d57d66b460730ac5e4ccfec33a44ee185840e03d4cc338032ad9694d5b443249049980987640ecab768e47de92cace4830d186edc19d951701977eedee56fb610b85efc6f6a578d822e863a3309a84874b2e5f6ba74a8b14ac6f642cab6ca985367b3f7ff44633ccf6a1fd9f934e5c8e933b3dbdfe069d57b614de0548ce8573f571454d08202c58b532c2016fc8ed2992d58f58138947b87004a9de4af61284bfce5ed25a69c02802ca7f42280ebc7df8861eca3567a0366e1f08dd5c4add8cddf0cb298d631023ec6fe28234b035411054038747277ede7a8fc6d3d183e20d20c42d8a0e27eab284b81a0f7c7ec49e80166434d39b42ba9f35f61ffb02cda2bca3b96c38197b722acdbc8ff9507269ccaec507f1441150fe27e7000047c51e1db7985b29806bf256fdd5c2c5d05e8f6019516e3e8370c4715b1cb9e998206c5a5efe29193d4bb3e14a5385315ad880664b47a18d5428388a1df7ac8c69a58ffe1f5bba9feec831784eab9d0af80c42d944f7af308bdc08890e9f2566c76b7d77dda4f603007002f403993a1bb1bb9f1047338bed713459881f3fef5e46542617a27b8b4d0bd0356288286386570c3ab2aacf120ff754f8dd5700b52125a70bcd2ea454b63a8601af863b4e4b16f9494a13b3db36b19f14afb4f48c3a654b94d6b8cb794c99ea6d7aaa7ecc362135352e85704dd06d6605c12f746e0b0ed4d33c72024f93f56360058dd31c691ef3970923bcbfd0d4a7febcaf5553057e342fe8009430b4cea4980b3ccb09da5e28244360cbc50db8924475ffa5907b84ec80d8f59cb6f5d886d6106d758ae3f1e1075f2f8a01a3d3c7475019d91260583b678d0b7e82c994dd1c3b10dce7e2e8c5a57aabdb1c447404233efc8823635f2230603bc2507a6215894b0fedc7ef596d4eb9bf91d8f75601ced35a05118a2e074c33e47cac11255c27f2bd1894a8bd48ff77ccf7b0f3794bd62d4dee8ac5ffe419d03a26085f2842b77d743bada68885811cbe52387f4ee5cf8bc9abd084c3d0d47aaec68b196641f4ae212b8cfe4740c6f89411fdd39f28cd9383a16e1159f75447bd54e39a9db6244e380ed4956b281861fe6f1e276ea9022efdc84ed14f82392dd1def735a33cdcf399042e2c64e6e60b3b457e12ccfc0ad103b1ec1faff715331b64984c756abf5c60fa794b323a5a7a2a60ac9df679db751a6b695ecabf4344f79a3a54d1db16e6c29eb340113cf066bf82965dd1635b47784f96b7ad4d701c92755a523b3c449f1fb7e818f2653a6d30b5a5d10a7b5cab347bcb1c7765c8069f85bc8ffac285069a47f00b0228c3878da3819df1b0f80f912eb6c83fac4eb35a5ee7dbb10eb706b4c233d39a12f9a4e5eb90fab295df58149e0cff777708f1a73a355d1d77008f25f5cf2e7aa078fd2e0bb98db028b140b588af6db58cb82facd01653becf4dc1a2345508894b63af99b20817b1624757c2d5cac86638bc62ba4f2e5ac7dc83796b2e01859f815dadca7609798d3903028f40c7dd0679db2c033e235a61aed3bc6516a08aaa795a4535c44592bbe613d06c108160257715e5cc6902ccc148a32c6c3091f71109e7ca12023ff20a3ba31027c235681960294d02e76591d3262067a38056d74a91a6dd2dd421750ce91a8a7d284e0b87bf8f4b347a998888e28613ff8c4e2b68c9728812924ee805c3ae095b4502b0ed6f4d823cb0377f1f56a8814f1070fa2fb36455560eca1a24429e58fb6e6b8920cb90dd14668cdca2cf6744a156d37d7cb397d6cc0eabd98fa51028afe4d4885a80d5da24aec3c86d4e47479ce6d3543be6981ca152db87ca01693ce6dcddfab9e7cc3276ebc933f0df6dd6cc11cb501548e8a803cd444eba28cc98824472314336bf4164cfe5b924acf3787e848d5ce4b20b6fef831dab2fed39cafd7bd06a8742ea682ea469320793cfd82831991902d49054437d536ee383cd7d596039d68d3be29b977f0b42f040ce878dfa621b10d5c4a794c2f9b8907ce4b298abc78174311d2855c974c05ebcd4bb72b15c4c229cb5e1169394b8d95e92d2508f46d8dac8dc792b9430866bd9406fd53b8615c694ed038ec503462fe6b3f35bc99cc67ed19d6e6f847116cf8ac8255378dc84d10660ca7ac08a8ce29239a478075fc95406fb567a6d50c3a05eb3299e68db9b79c99e5d0896b7c2fbd39e714f69669d3d11b5706830df1ea84467e7a7c56ea606633e7336cde7a8a0df5f51470cecb464b468d379922b9b3bf20804010f0eb16c057c34274daf0bb11e61d90354ed8b53ec52ffeef134338b4d620da4f8fc8a4c9aeff23eb4e94145884c89a040ffc8ad941b32fc9afc04d185a0211b82d1698199ebfca75c91f8efacf55a2a31e8109444204db9dc95955646949675223b958347f77187d2110f5936d6801c3fd8b0fb383658f5fdd3b086af3ca9aa3eab83e3c128ba071c6be808987d9ee320c62c4d6a1e30e7e5c0fee0461115922916dc27c1ee785b201e2f768c6ae7a416f27ab9abda7f6ce09a2de588d192e33f524e1e6c34d31f6b989bb0ca25924615699e2930be9a36f46813cf6f631e9419e83f314e426324eca98e827a9cc43cb516e67971325aed79214223c39bb66de1ecb42c45967305931db255ead6b1e8c9611d652b978b71039c1a97831cc96d0e06d6d58da26afaf85564809d02b60cb3860facfb0dd046a1ed382c182fb4803e6074b544d684caa389006093d54c1838836f74b6194b05c23cc3a3309ec3d2ab2051b2d28ecf7df2c5f44ae8d989b458c662545ee4859c52447cda30bb0e335da72174510fd18114615cfe7d91eb009c164a5797519583284270a130b2bd71920ad14d55c391fe137297cf0e9ab58e8441fc1686f676aa891c0d3f155d59b7be77e5e99575fec8630c22d9e01da811522226b69fa5b95950899cc3aeb7623b69c98b98f895dd45577f1adfb4189afb9638dab7111b336436ad93382a4324146f5965a7329497a8c7a63d0468d86edf178222b1d187583903da2f0ca0177dde995ac82d921565496b886bd862aa413cf065ee5b0c20e9330237f57589b07f8d1ec2d7199a6b9eeea188dd5c00af3fb3882b900bebabf2d74ce7dfb82dfa69b3aceb024266b3d9e455154fb93851a7f2441a9cec5b7f248ddaa7f5c833a40edd9135aa7be5b4094a77122b344a4c8ff00fbe940724f4ea856d53ec1c840f00fd260f651e51f6324e01aa48cc449a6a086dbdde41d23bd22615cae0d76331cdb0171f130aefa380cab27d68934379648d1f3c99dee7f5db1ca947429ba2856b92b90d46aa4c52f3c2fb8e62ea762f5ef1204ceb1e7783dd1d112100a7faa9416c0df9b6dbe88f9ba2fd66529cdbfaf79aafdfa0f18a0a0e10d6ba657c078e93696e0f07562ea4e834f6098e99cef93845ac54ebdc5f6a6d07f1221e64c133ccc81d5ce740e6b480e9600eabac753c09d0093a6ba9e4cdf1f087b383e14784dacb27add7319b13a81239af43ea53f7e42e4d4944e40a0141d03759bfd3b1b9baf102cfa040d88728268b888cd146f753a874ca94816c9c9ad20c2a11dac66a7c0132e124b2af3e855089ea6420d2de00da5391f4b623654e1ea4568026f1ef1e2350d53f3e166135effcc3bcf11a59d097f326365e503925c8ce1058fd5f4202a2e8274f105434d15d92c2a13e743daa466240a5826e0cab3d2a26fd085571c87fd7224748ef04886d18733f4df0a49c0cb2a622d997553a60dff58d9614ec4518ae083cb891603c1b788e26bf9537ef583ff94c85690d9e13e95a762e908e603f6b5c877062291c758bda01d886231204923f493408da7cc48de8e24764fb7aa60aaf616254db821050feb4a6f1fef787fff250eb0cc2b47ab559f18bd8b687b208c8dd253c2fad06d5db38220ea825c08f9c601ee4bbdbdc7920157629e9adfb744d3a83342ce804a65a5be8aac45811b08870a4af964b690fb14aa72a36d819ec843c2f6201a78b25810b36458ce8f920b22653764a440f5c1af70bfe0f056e7e2e5966ede202cc7d675b6be4e181694ebaa7d7ca46513c48a99e77766fc14f103e9a05c6a5f515fbb39ac28846b810bdc9a1235cee71402c4007f65098aed376700f1f2f6fcc9291b8a9ac47e3e2a856912432002de121fb14cc8347a27cf580f83ea30ed2e212aea2090af6db7c92220fbcb59f4450ef39e1a816df2d2081bd90f38868e28c11fe57ded45d6f653a2313bce8682c574ff72da1683875c2940b6bae314f542e18167e350bb0227d2841ee9a60c8e364da4e4d375240caf7d9fbec4dc56d993fd0636285a64d326f378c3e9a78b902ee887be62f1c6840d987b492621ae633022aa2b446a62d5a47fac5cbf4956a4ab2b68d18f5057f227a8e5ea8b06eb36f5325677e1256169da05dbdd0120ee42f52571b15eaedfb0ce6dcaf1a0b4a85ad7ee920e35b0ba63816454bec83738d33aac9d15ee31cbbc68c56767e2f02879a2dbdfc2247d027833384743497b472c875a559087b48310d1cf13afbb7c435b61ed95c8298e47d10213d028ee49d5bdc0ff8466ce650cbba7c4b58014f5dab61bd782b24021954b5ca4da358c5c14f37fc9b9b5be7f5b2b46553bd0855e3748514de235d61bbd49b65a3786271cc6cb7b80002c15c0551f3527e2ab35cec7f6cb4d6a26a99ae6cecff94580a62efdf80de205ce007dadcde721b4bb2022687c00ba72edfcb6cb47d5ecb3a11a77634a2cece0f5ca2b82763f30d122170341b18c576743ca0dad2e43ed81c1d1b49f1895a5968fc931e50af98443e759069b5ade4ba11a1a15910c9b426225769d7ea93e613ffa5bab753ddd5abb1ab670b345088ae0bb525b972f146e800c19f5e221466f61e8309bfee0e35280289153fb5bc3a1bc8e34d3da58495347f41e302aaa9427c2cd0affcc5c95891b4a5e4b613c985983d882ae619507622da5891eb00c60ba6a36b51c481a69262a20004742cd3562f71d91b8f4eefee9d03b8f00200fd2aa9520c08b5d3121bdf66b60b4c5524c325612ba33899c3f537d5c4c638474c3961d4ddc8f5ace87707be898b19cadf709e79b33a80524be1aa4bd46a762060470856c39ab04ce10454f10b9f38fdb64f97a4c8941245c9f79f3f8ba716a084f3bca51c0f15df12813f1450ad9ab7a2d24c5cb6737c6b78dd0f5ace88639a17033d6a39f4d7840e107041b9eaa1c59622995a66b545f2281e28afcac6e4f82ab671bcf79786c4df00b88d2721effb1ee03b785ec839771e11f763b40daa3cd034f3c3f60b0a8f5d3eba6347be4cd750f012a9562bc34ca7032320d91bd439e0f0b89373ed2fcd8402e0687380952d038f879d90d38c31e97028309d3eaa5bba256550912c70a342eb6bbb050d6f23f0904139f8329cd82b490ec44f5fc863d1f72bce93c75d00b9ff8df53e3b79d4e19a80b54d52d2325b18feedc98cfd422ec312afe2a20e7c5aeaffdbe265f68b4cb3ac08119eaff434d9c42c5b165f3af8ccbff0edc592a4416a9aceb81a7386a20f4b9125e4ba6012167e2ca2b71ba3a7b8431d7b84ea1bd4928eb5a8ee177a58e21ed24f6c8a9c0804d7e78d66b8aaa72e53d5576924fea007e83e15821e83817a27f61b261ce853fde807c658afab9b1fc2bbbc885726ad6bf25aad8b9638d0328849c617906ebff428cd994fb1d28a44040a2847c6fb5aafae8aab84456f15210fbc96ac5483612b2d8a367504b7ffd93cb787623ba8a2b78596187cc79bc30b34606242079c7819e8a733afac8ec028b7780a49fb10430bd1484844fcd5320ec86938ee8cd53b89042b0b9cbb05388dea19b7fc0ffbf9a1e0c0c603d90bb365c0ede03122767b064c6d450b4f797f4b2b82da4c26eb826b6fe88ff7c86fe43738ee413dadc9a2c98aa419051718d64a92dd97c18340a6808ae603629ab95e66c47fea265f40515754176bc98b82f0d44c27ad55673ac9a46e861fa15ab2207733be81fc7b494185e09028bbb13cefdeb0ae750d86822f50218b5e5e87e18236790ae8aca2081220d5604d8d48df2df2cd0a6c2658c40083bb6fb6fcc756c1c39eead90d817015e5959f63cbca48b3cb7cdbec34e8383e754ffea5bb1c3d791c7b59c526e01d035ac567902626313207a90e11384bb29364de9ff9392eed8d5cb46ad13ac57d600e0821cd2a4d352960f44b144dace12877890f9d378c931104c5506363ebae3a95a34793feab55d61bb99ae6254476ed12b5c45c44251cf7a9dccee5aac7b8de00569f5693247a13a90cf9ff566a9fcf24f074a727bb5ff5e256566fe740bf8ca0cb08ff66a70152d5ab9b5cd1ec314ca959a58d3f9347aa7bb390aacfa34ecedfa98a62ecafb74622846af1c5d2a108c14b6011e5afc9c92856ff80b72aec824d664315a2f2dd64f4a83f90e8b0eb2ce16e4003034cc0de2fb08c94eb03f742ef5cec00050dd07b4af3e84cc65fa5c5fa6db6bf250b0e1aa28de36b0b5d5e4e5f5a860a6daca8d1c6950d79a3697fe728f1fda33861dca898af6faa6bff703b392ef789c9fc902816ecaa5f5baf11c925bdff19875d2ea98045b20b7fcc95d7cf04ed638899aa3e66bfb00d038624abc86e1041f18b966f6f0168f51b1aa54963cab6bdc28543d0cd599d3e8798be4cc2d5c82e4cf3729d51886e97ea55c5dfc30e054253b69abd8b42c9912fcae4aa619db7fbd80445e9fad62545d5690aa6d9d092d939a0e477c90878e6f64982d17a6b940d2e734766a8d0dba5b3755e04f53adb0dd2b29f53f6cd0dbd9bedcb68adfe111fe862d22fce3a81a875ba5d994c3b4052d1688d6ba6bce8be531f0a01cc4e66f5dd4132d2a46579aa8cd501e0405e0e068fef473b87cbf3e2a53f996821e5e7121207c05f476143f7ef9b605b6fbe3b71f6753bb120498003bc8fa8c8aceba8af22e92317fee09677d12b044d2ef44aec8c234ffd8856c1f0c870c4e2d2018cc9c6103529a41617e93b7ba696f956cd253a9c01523ac580fccd673deed538b0c7eac591f12b5e78379e11d8eac8fbfefc3c63b2b6f7b1ca7be71286219aa5e1b83e27894dda6164a9523c2672bf9deb5e030da866a1486e596850acd10907dadb08a177cd722d7afec48b9837dfa27e73b01aef8a83cd9305f1b94871ccae7701ec15c67b9dc6c5ab670164bb0e6935e8cb8e7afbe07c4dde68d1157152020cdd021a45d66e5c505819251d25defdb3bcde7ec1edf6ff33b4be03d4a1ea994f996e31b60dc41a89d43f865acd288b34b56e83507965911e0ae9c34d14e1de8f9e24ebbebccc67547fa6c7c6661e9bc99227862dff116a8d344208696413b2b7947207c406b506fb06d94677a60291fe406bb0673500c2586700acc18a5c9b6ca0587238c5ed70df6e694c6831bae3996e54779ff4e96f3cd3a8ff10638347bac78d4c9cea2fd63ea1cd90dd9dbafe61ef6e09936abd22c5c0ba1d4b44937afd73cc945dfd0fed8a9adb26bb85f5b361a59435b8b435ec59a5fdf11f62a0cce7f1a6cc94a2f2957f2313a6d1f736c016b0b6a8fb116d14bf58374121bfac50c82a1432e48c427ee1569ed066482965e7020592c7fec546627ddb8ead6831b46ec75650f78146da0cf9addbb1757e7fb0677da35bc3290ffc061bc48f53d657a4c0b1353bd9c5cfd6fc9853fb8e58b1e70055ecbd75fffc43ab28709f1c965cc92889f5b90b36ad8d47ba37f339646b5affe47c3edb55b4199f988f0b2ee5f88458184b92611da0cc89c8ce3eb139c24c7adc24e99ef843a4e1d0184dd1667cbaedd1edd8aa9d6cd6a6ff7dde333c425a8c4ff7816232afad7b28943c82fbd6a6d2110457450f1da33956c841ff760655a4cdc0ce365c02011112d26460e77f38ba398e4b591e0add1e27ca1d8030d6439437006bb02e29d3b0673da36cd32fd637ca4250281426bba5e1ba4d8bc11d764ca37bda1e32051c0d0863f10cf6a12ca45ae70d401908e4639a6ac55bf22ed844c3b2604fb5762666250adcb734f110cac8460261ac376d940eb8ea9c6202342db4fe132742a1d66070fd3077d877c4cacf01aadc1dce6ffd667771c756acfbf8abc5f8fcd3e1d0ba3d6d0f5e9a1e5c3a611ffe7c36098d36c5f00b3f675acc2cd8e30f0850558ba19dbb1d5b3f6f0d86c6ff50100d62abc654b3d9280b8c7e06a8ec42b3f9760640626cf686056773d6ca3e34fefa666d249526e338267b5687e3ea6ab0673d88ad58f7037bd63f7087c60f5cfadcd29914a39f3d42b536c9dab48905bff05dcc231c9251924325abd3a6c816c7e24243e57f9e75ac9445daa8f88c67d800d6270d62ab0cba83e6d83aefb58997f08b75ab878e05dbfa07739c54eb5f4cb5b8c90a17b86fffe974445e62c1fad1626c67a18841abb4fe35ece86249192b40041afc98c10c6866104f35ea60c88ffd698ad4be1c018739814f78d40804bfc4430dc63c940c04831f58038edf45d8b4354a464938248dc3a5ebf17d049471411b015bf08fad214e22dc93b2308001ebfcfde04099d6341ef69b16635fc1bec9b4185b5159bbea6045037bdd45de014253dfc097f8c6a9f1df151b559986c3c1679b8e8540fe0be62eae80eb691b2e016153c6a52fe6d4eab1e21cc629c2a5cf23f6785602ca68dfaeedb7755ac7c36a1ffaed3f5a8c3d77f37093c8b5539b8ab186fbb8a7a6b0be8145647fc17dd3a6ac9b93e346a3ab80fb3a95622f3e88adac84bdf88f77220f2e7dbc5363a48132303ea709cac9931abf4205eee31d66b2c32556c225982ac29071da04999971eed248fa481729c2de8ac8c736461e359e230ff662a7204c7c0f0e473f88659c36b50de3d4f89ee1120e7e0f3fe1128f1e02ca7c8efdf379453facfbd6e233136ec25e3cf314c17d7c53e36d6a3c33e126cc5384bd78a69db3831ab96b22bda4ef3262c99a08c76e257719b1a47006249e6d78d66f4f7c43844b718fd0f5b1a78b360ef74d138130b108f38f6dd146c01a5ac9938c9070476a8c474098f88f5d50e3b1152d705fa72a97980065184733e157ef4ae381bf7b66c93eaaef8ad8ea88158b1c1746b9ed49099762a7bc7f34e2b818974019c8bccccc90eb6a3b134f39c83ad99337308fca241ef87da4367df179c2593872e070514118fee369f677469f1c0de4e42537fba3bbe0d231131ba874d881ea2051c80e8614a2cad83a70099e8014bed46a0033ded26007638c30c6c71863ac95112270ea0751d575e052f5eaa39eb3fad60407b819a8f38553099827174ae1127c734308e12e4e09745c625ced6aaa65108b1da92e1031b527105371ec61f761d78ee9a0f20feefc731a50a63aca5eb4a2ee437b0e045552e8dc0c857e33d4ed22fabd8eea36ebd6e9d8fac11dad1bb10011536177f624c93b7150860828b3272e7d4ba4cacb8a7da45a1deb868f152e1119797870c907f6e4d2d08c46198aa3e1125c789e15293e95baff485b435d1c5883fc074343abbb1994e198ba1fd15f81e5728bc03ea48bd3ef6e717a6968e2fb7b5d1ed521afce634fd2b4695e320dd3d0bc54af6248247e52ab77059154990683ab0c01571ddbe58cabee7d7755c12ef2b057dd8b32ca18ad0861e247974f314253a47e1e4763e5ca45c18733ecc1735f0b0981e603524a296555c9154429a58c32c6c7186394315e7a808b31c618a394b166a742231fd0a912f26b8c34432a344203a4b2b61066e5bf4f5d515d18ea42990bbec84b0951f250b2708921f94003ca40c8db5bf5150009122448902041820409122448902041820409122448909d435c1a60b92501a7c86834aa3e3b3a2812e96a12b673b36fda766ea5b7832ae6f9ebdb751d34dfda9c73f628eb6b27894422914838d7755dd7b569d9dcb46c66737635b42cc6acaf9d2412292369a48d849372cd4f488432532e14d116d23ed9fc372b966d9b765dd7755dd7755dd7a5699ab6d5c86c6c59f7cddf9812a35b49bbdb28d262bb1ac658f5198daabe76927648241209e7baaeebca7e5ddaafa965d7766dd7766dd9b66ddbb66ddbb76de3b6af6c5c46b99ded90b7bfe02dfbb66ddab76ddbb66ddbb66d65dbb66dc5c4b65263c5c4765dd775a3c6529e4dea96b263cb96a9d88b43cbd9d5b8b6bb6123f60eae4cbbae0be7caaeebd256b6cfb9d27db3c6fc36e7d2265d57f60bbbb25fd8d2ae2412894422611776cd1d5cd775cd896130ae8ecbe695aae8d2ec4cd7b07103020f98a4519b80d8d1419148a9516a74c26a7cf08c91586f8b1f263633ac326558f50f853770d798539c9a76c5401958bb45b70e96c5bcf874a8458dd229adf2eebe4a0a8a28f4e92bebabbbb9ebfd0ff4d6ba9b204efb759174624e1c7b56975d6fed57f701e1b2bdd23a2c6bacbbbbbbbbbbbb5bcb1ad3ba3babaaaaaaaaaaaaaaaaaaaaaaaae66976eb63aba9914aa552296c56a70a8b219148241269669a36a736413552296f7db057a34deedebca6757bdab2cf6319dd9af591aa0161ac43586ba4787a7686e63aed60ce39af2cebb2c9615c867538b2ae5b1fbca71397a68f9a39678d542a959adb36e7a437b6cec6a665d9b5cd6b9bd985b10c9a8c797e955dbf7ed9b8c1d8d460e5b29975376c742b4db81a366e5cf39a17f60fb5419c24921394ceced7afbad91d97b1674dd932881c44e8ad4b5d17f616b669c6110fb878f8ef2806f6e003948932d24f889de9c9a599e27076ff9c9fffccdf60d3f6f4b4b07cf70bc3898f4c7c45e52947f98abe5ce2409bf6bd0e5ccabedf814bd76534613dcf734e17ec2d4f9b62d8a5409b62e8f6d475b05f68c7cd4ec559549ca588087ed451854644c0a362316ba17fd3475a0476d14aa3d937aa71dc4534944251ae4253be42556e82ae7c444dfc041d1d067ae228280c678ae22c949f024655ff173c394ad709bf588f1a0cabfe7bd3d8ad7e4bf78dac14164bf74d16fa6f953ce0b6c5fac748d414fff874f251ad632d74ab0a96a778d466a838cb5bc5598e71d6a2e22c7fc12d2c74c7d696af13eb168f6a31e9c1f2177c161d31fe06ec494155d0163ad96ba1b2a6a038ccb3d075621d857518289fb03ea26c82720fca2a947d5046a1fc833dcb863d8eb2901bca4470b8087bd62bca39ecf1114621e117eb4d39c9f21871edcc5bf1338b951faf7f5b2385f3737538e235e5599ee5b907f7cd6a3da6f480fb788707e31413aa75562267c833ef28e1129421e5c217bed5b3735434b042c4a832834197633a4013d7b0046fe3073665873a2c5034ec40407501653e877f011300ad5f7d00d822568c8754eb0ce48a55b553b16bd8b783bafbda3914fe276a07cd5731c6eecf3125557e9397d790d4cffbf3ed1f1de008c1ce2ee891c1e3d44efd38f5716aa61b4914f235caa2c18525853dadfb3c0beb3ece6505a7aecf3ffea7a3b1b96033bc7cdf3436f1ee899f67ffbcef2aff462b1f13ddeca64a07f945eb523ebf34ed7c8cc3300c849dc34030858d30ec31ec91ee61d864138661d80a6d06b6611886e2b67fa06f9af6adeb5cb41958a775dfe7146a33f85ed65b1ba5d3046b1aa9d57175d8db78866988a8549b11df33cc8369aec9d03e1cd883d87a7dce39e7fce7fd50a3e5d6282873dde28ad1af79aaf55ea29b700986866ca37dc4e2da7adfe0740b1a48dbf40f08637dd2ae618fc90baad5c4470f27a9ae251d28d375f69ca8ae737e618b466d0dd6e767b72356d07380b08dc27a75537351e16743d5b46b9ac66947b928b7a31db2f6176c82ec824bd7b51655bb0d507b0c94d9ae699aa669d7b46b1a87f24e9ba1719aa6a1708a4b16f70fe51c47556831502a772dd5a8e598eb6ab8e8000494e14a04ca30333333730a0d0706511aec5920c0a900a8d008087a540c6652e8e96095630c6b33f8c3cccb4b4859e4d28774511cfab339f2346055a1c58835607313ac3313aead6746dc00b2c84bd8b3386544a3220fb6ae8f7e703eac7717b05bef275066bb6975be50ad378a4b6c2d4f93d112ebeb24dcad4fed9f7fa07fac6f92c99ef5162d06a8dbb195eb3efee7daa106034701326cd749b5ced0097b16845d012e0a57675e735effccea4aa1c5a8e693fa391f2b654dbbce39ba28ac57a1c2b22ceeeee05bb419f19c429311bbc8bd3a3a3a3a3a7372dca87966755607c24cae31c6643b29291626d5fae75297a75a256bea749c3ce7849f39ef5967a1c5585137097bd63179e230160142b834bb28844bf206ca68326617279fa40d4f6b4eabba2ceb9a56d5332b2b3048a2481245e4ca8ee3528ef82f768c59d360041c1f5b6884d48f301a02b70434628316a3eba7812331a3b1882aff91a675f86b49ada142233d7ccc09e4077b3206a21ca61ca6386c399ce150c56189c238e3c230d8d329ce449b3833e31262203c846f9808e37011cee1233dd89348705b3f8e935c02614850feadceb749527cab6406157b72b95cc1f26fb2d0d057d02f12c1353c3cb33bcfde740c383a23d4bf5532fab74980e1288e62fe6097900c2695cbea079028240e8125d16b329b14212c3791459d8ac7a8221e455431335d1e9f71297419be82e5f12c77e1d2f5edf13470897b3c89458c8b1709e012ca531ebf0397523cbe071fb834c3e36970e98fff814d2a8fb7c1a5181e4597da8c288a317ea418545c85c6997e9981aa780b9de12968cb53688aa3d0946714e518cd0ea2d83f9f73dc2fbadda2170badbe82b25c06bae2212ac3a42e2a34f2e01779ae9117c91a67b8f44b7ad92df35ff00a5a84ca59544ea71456d1ee61ef17b52e0365f946af73dc3f543b887e328a7d52d151689682a6dc85ceb7d0149f81b65c85364fbf9056ec29868728cb29ed7e4aef42435741ff19a88acb405b85b6137e913758866e45e7d27da378118d81daa8c13f501aecc5aba02db407f6e253d01dd88b4fa13ab0178f4213c02f5cb0170fa2312c483478ec72964859387e89a1cee923a1288a51342b91a88aa2c903050c276446975f136c0af604b014da1b6c11fb822a43df5014c5289a95485445d1b4505014a257171d068a42f4f8d98d44d439f37d04ca882edf4ac0cee1d28c2f585293845311a010aafc76d17691e8536627680e27aee11fdc022ec246b00d82cccc43999188071d3286e8dbfe586dc69c5bb727a85da2effa56c32a80226c5e949b1789389bf3a2330f7b1f6fa14a76b282056e47ac3996a75e5fd10277aa7e6933b603c20d726aa84220dc6048153df670526115a2f648127a157a0c8190ea52a19110e0546c6627f60489bcc0063541e2695ba02307a85ecf4181faf9273a778c9b90009ce81007d1a1a88bd9129ce82f189bda27a7b9d40cc5895efd04dd2aa2b08e6810b15eef23a2471922910842988b79e6b1eb7b52e563d733b163275afca49345dbc824954d3ea8f2cc230d004d7b824a8842d1e29a0af7d4dd76a2ee9ba17e7bc2b44c22c1c1fa310f8f54e20894097dc989246c11aa8cf2dbd38d9010ccf579aed97a6dd641599ce938735d74bba876d1eca2d8453f17bd2e3a2f6a5db4da91d705c4351fb762a2ed50c98b30124275800a2dcb3200e490060343c950503098aa591eec7d67c4399dc38267c6a5c8d0459828f6a8321251659c59f102f7354f3be1d2c755a80294e12edf3c539e87b089932e44aa3c03f95cd2b035482e812ecf3f32015429794e51f771a3d14c0f1ed5abd0480894540c32841b371e707166a68643e9e4c419c94ea852f408a10caceb845fe4f7c9e4c1f5c8e1f1983c35faf6f4ede9c704c20d1911b95c596152833d791b9d8384674a275e932f19294952654e8ee7fda3d18947d650b7271f70a17fa4ca3cec39e901b7326e978f44409915bc805900b9665b74119c236e6e50b98850c8e3a17aa80a59ff160bd117170a0e175677a1089d8a8e612126f23aa4366345158560e82f3802d064e2f2356c4099d0a54b8a957f2a2affb41aa2eb03c2c88be89e4c748b02f8f2a9d46f4f5a8afaedc9a5bb8f8f38843228ffb4172cc1ad0eee28c73a24531ea2df9ea44c71a928340705aae8198a0bdd3a4c686b88c2da367480736932428f211e428f317e38400f622b0a192e55bbf6cfec22dbc2a6851489dce755b41cb6937a4e8e1b8daa6f868a3dd2d0e757831197d45007844b8d95a3a0baf107849907558e6e3d5be5a89644ab1ccd6eb2ca51ac7b6095a31f269fcad1ebc855393ad966568e5a4faccad16aa7aa1c954464e568dc13471b080833ff8275e8ab7381301c5d8ec20abb180361f89352bf586da87c8e06adf2e5c8e326a8f23fb911c2f0b1135bf5f669a0721a6821b87640145c0084b9f9927bc6f2366176c24f6a8c8480a6422343f0a4aafc6472233291a1792d11b056510a8142d200c15e7c4529903133c780fb4815ca90f525022ecd0e29418d110655bfe1d2747723865896498d5f9ab883263b788546ecf1d8996b07832ed711e2a64e08ade7d87f15e0ba1cf00bb588ebdfbc758f4b38b87bec411a0a8542a150777804b71542ee50e3688e9ddcec72ec3fcedacf737fd193eb72605fae565d48f46c5b00bc41422d07d1b763285484828222429174a31cfd405de8ace1101f7be80e468efd421542e59e63bf800392ca69177d3bd47240b9760c856e26a29fac2294eeec7122fa9deb70e4d87f2158e77be8eef3b8e76e71143b88c2bad1cf005a472aa2bf9fab13e2a65a1dd82fdc6003f91bfdc2af3a2535fa0526a1f23512701f4cbd5f7808ae032e3a620a7b550fee63512b16ec7110a67ae492f78aa351d5a8d6c7d6e0a0409b5ef44bf5ea404800b6f8616b8032f3d5b91136e9be48fdb02d8e50f73d5b21a1ee3fa8845a592b1f6bf5b5d9c0e792ff581fa2b2aaea9ff921ac695d1fec434cecf3797daefee7d73f9f6bd1661abbe6eeee438d9b992d21224b2468d082216430c410fdac9fbd7a36bb25a83f65dbe0aaab6e4c9bd173f6ecfe0799f0c0dd991bfcc2deae0d27c1aceeacbf51d8f95083e599200e8eb4e589a998ea7ce8ba6e9f753eb0975d76bcd3b10e7b31d526182e78d2751661771759c0e1481097e61773b4235026a4049411a15c24ba463f1451178584a210088a3743e82ba8b7b055665d4fd6c38dee09e170b8846a13d6ed2e8e78c3de0e612f0ae99e9def79c2e3a4ee57a8c045217148bc290243c226aceb25ad649320c97a707daca98eb8fd64cf3d10667154fb88c3a52231874b201cd99b4b30368953803221d143d94387f41375df36ce564ce321bbf6656f9c18d98ea5af93ea58109d4f68e71479953ab87eddb30528b375df5c58534c2d130e441098603441dd99549be20ee0cbde486a89ba3c75878069f2035c1fd6bde4c1a5af71eac6253f24cd667fc159e370a948d549faeb21d47dc226c8ddc3a5f9bb8c58b8946cdf4850ede42ecb9c25611d0d87cef6639b26b245e8f6c8f152dfdd45d93010245c824ee6ab2cebaaaa2ee7ab8ca290b1357b3feb36eb82d83abf3ceb04ca402e413a5bf6ec6d1af67667d88b9d9dc45e579d553dff6dade80b099a1d0e17b36940983eff2e3460fb7da1297c463b44037ba17fcbc3b34eb6675bb0c828371fa23db007afd11dd89b215331319afb4cb42ba3ee9b26ba8fe3aed27da30ab785ca3fd757e80af8925d050aa1664fa14ad4ecdc016af6cd00d9d9a64ddcb1a374365bb6d9b317ac2dda01eb580f3bba166101ebb0360684b8c1b22cad73813059873da35587d1a6df05666aff4567b45200bcc17ad3e4a0ee2d5ad16fd6d88fd4a5c2cb4a0b4014220d28b3dffaa85605bf3318f0e1b3033c426251e1f621401b687adc16fa105c8803c17ed90edaf8ad6b2b402854782670a41086e2976a31c618638c11872cc3c1ea52b8ca90bb99b1b584247e9044123f482289247e00042014c9a9f2186f906f401899418e9037f8170cbb8e19a8909323094ba2c3ff004d282c3e981529541ade85064d2438332813ea438c336cd20d51d668d718e3b66d6fd01bf406817a492b5914a7564786feed10ea479b017a442d122ee940192803f4780c0716cf4f6027c0165cf360138a06e3a6cdc0442294500f9481f1c4a5ed4c61d528d7a63b66dd8ec472dc96006576709d04d8a22f17c5298132db47ddc38d38d0b108ba3c715b060552fd38b5052b6c1946732c0e7686f0f1cdc7377c83a9348a74131a617471b03384258ceca0071ba9f00ddf74ed6d036933b6b78c6ddbba66746f4018692356bda1308d7ba83b21eaac0ed58d752114d77dfb842acf5b574150773738270a318dc69a452e815662376c54a3dac436f184f39b9110ce46c3017bd3b841d825600d46800e3a163314ba4c50100653020926357450ac1b040823bf4bc01a90f09291124870912acf82491270dfa2a0d5b33d3d1cdfac5481db9913e5a15010461e862ceb9ceef8fe41a455006b90df68d74098e6254c5850a16084dec01678d360c8aa759c82725131f6384199aebd1379fcc9c80977139647309150a8a04ea4cd00bd535c82e7255006a88b07cd9d3977bafbb6c6c74d93110f8c6ecde85610b5c1dea45b4312802ff27b3202113abb01d416a015b606c2c8ae98c593b74f0315eb78098449e570fbf823c050ef4099eda17f3f51bfb6f9acf7f6802eea7a40c7ba1e4ec106026ddde1351a6bd46684bad89f314087816382ba7ec1a0063508d48b6ad3da4c21dccd08e78b02dd78d7285710e51b221046a2d808140ac54ab80842182951e591c0d1a956d2f54e2be19b10c618ef00d43191452d6a518bc23adec938529b3a09f085b91bce09730a5c16aa7c177818a1775a890643fb4a134ee6a075dad67dd203ea7a873dd82fd89bea00bd33ad9910ea5a0905d120b80a3adfdcf8604195ef55c14e9b6cf08b4c4dad779eecf4a852857ba18adcb8ee1128f3e9605c5d10db3167e31837b176b73cb63cf6105a75d1e2a8a2a553b0a080e1c4c8c48a4a0a8a28c481362dc3bea7b1950afa4fcaff50fa8b16f13986b150144f4159de34c55b68674bd372d2d0b00f7e916f3eb5e9a36571f6d6693deb63ddb1bbb3167ac98393b4ce372d340705306d46cbe33f2d8f55cbe30e1f6c9212489543ced10797260c16852d735beecd7a255be80e699a822e91c5d9229bb3474674517491d0d5d924bb4357097b4b986c93e55927dbf3846728f3e0971eec19e1faf3db13977ad8c96db07783277bcc194f6b4eabba2ceb9a56b5343d56c88dc4410d9a52a854cc37a5492d191a9101000000d314000030140805c482c1782c103475f70114800f89a2446e4c9989d32488711443c4100208010000000200020023c4010ca5ce948237621cf234610124c9e0434aa5c92fb34939a1634dd914c621da727feb1c58672b225d6846824bae6784e0620ab08f9b49d2ffba41a8357ff25a200e9643816014c5cff63bb3164c90aa27c8cf8739e1e45073e8f81d7058ccc6e065ab6d3f0fa9ee0dd0e541558928331628cea1b5155c6021bd936fcd597b22a6c9d90eb30fbc6a76f036bb060ef7fb24c1cc85ddf0ca8b1ed5e9ec1b725a526622f315adf4ac98942dc8e860b4aa08ca1da9d04b3500b8f05c462995d1a6ed79955b1171a9269870ada718fe4771ab7159e044333685cef24b86af1b73ee6f863031bc0ecdbebf19823b1f43efaaae197986e7ff40f18f336d3fe3dffe668c0580d6d013c834a478ebf143a1c7e8bb725bb8707f123e26a6e66db667576064009275e7c06408017f3b32000c00be669ca382d63f2680259aca67889e4ad776aebf19bb249d295dd96a10883ce20ed0195f11c6934f0f31bd97dfe0b7e2878cf7fa171f5ef11e979cee77992fee1edeedbb0a9612f759127e546adf1e817776872776213d297a9f45096939239831ed2bcc03351001cf78656d95649e2cf1c1ce8617255129544f6d2610a6c3e6dcbe6b10070844eb8c9a45954c813e3f97c3315efb43a40e7e17b7cc2675d2632ee355794f33e00b553c6511f8a83aa5375bd0f792b0b48b65009d71004f1fa6b6e9ae9061eb25e50140f8d0ac7e559fc5f3ac4e987042eacb99e22d8700e2f0b8e121e24971573002f7febe3a274a5a94c1628c0d35ed2ab4e62b151b3b2f985a0915e18c04dc38b1d71367d149412b115adb3196fe3742c42befcdc35992b6be65d84c943da8159a9525fab920dd0e01d5066998663f6c9db73f34909ee2052c4b3f9995a74ddcb46a2ea6f8adce277596a619c5efe0a4850a5196263df402858d6c63f92f04c746fd2bacc3cf5e3ab23c9ec4af81a8d939a242a6cae699242792ca83e9e713bb953e5d1c70db028813a5aecee4819e1aee864d2908da91a711af03998cf156c1b6c67ac12a04e1605538711ffe954c54bb2f7409a76129e09a9085b300c01549da4c32236ba8a68a2c31a05163761e4a6acd76f07613b0e13e1595baa6acca38fc62b01b04dc3cc4647e3c85a6372106f238aec7e586086f4c56bc7438c0e43216847e1fe95011907918a14ba45bfe62aef4c44810c90e176d6e3935fa6326b4f090b49b0fbb073e9953b7e7a09d7aa23c5176b8e0d9288a567d3a3cbce6fa9b3778fbe0418577ee59491ab1705a665e165613b036188913d98cf7e6fda3eaac8e69ac2c37ef7bca41ac3e9ff5809f36089d7b9b952d1947e9f42757eb99d8dcc05962e28d2f58ab73749a7aaecca965b541193a4425c72ee118648ac36830d356308813a43036c0bc0bf3ef53f9d009b209494f9eae1a4b7f78169e8556c9d448d0cb97aef4c29db1eab9b9f1eac67905a98f40eb2fdea24c4328d36cff987a7bc871ac4b306342bf1d02053846aa2690e214dd37a72ff105e81a35b4b05401aa7eb6d8d5c10d2a21c8cf136baa929e57d52734ff686dd468ede029cf4a935f3cd896855221808c7164690be105560596f653392d3f3470fcf64aff107642a49b50f0e6226356f15fe169ec2a246871a8f591711f50a6e875f0ae55680b4706e317cea93c0a7581e51a76df04cc58849a006ab8bcf05283b3d6a8dd92569bd17b6d893f8f96b2b596cb6a84e9d7c530860232ebe9290f4a540b24cb65a65e46d2c7bc54108a9e907e89ae393a9664ee4c63fe5d8e8542866bf4b40e46f3d0d066af230cfb91e81941bb28f01c1ae2cd065351a88e4d422021167818be946a7700989b9a2cb2598627a3a1ce8d00d24e91ddf4404351024352a665ae80a294ded7ac62ea9b79fbb7be184298e02e29a6d07b8ec6c9aa5bb698eb919c06f34e7c26a99003b7a902a61f62456357441f4e2ae4a8d0028ea862a488f37d8392a961656eb9394a5614bc595185c23724c0574539f04f0d8b79a70e672e150d5173454788b552530dddb01a9472b9e5f6cc34f6086608bf5d526b940d80a7917f9159333e12b5e20fce768616672d181a71529a9b0dc7e6bfd7820db2124537282cfe2defb94ec9793dec645e972833957aa75bdfdb695a3e6d5a24ad62f0ed9cb568a2d91a4aa2279b3e3058f6f4fc0cd3d23df28a7e2592a7a66b92a119c8e738d953742898ddadba1f6b177c08e092784691e7d3e45d37042c6862830fd02f8847d88b0322f67f5f4ac916b6b55c77f82122013dc136966c49eb5cc413baea30832023f1a926948a881f959fcf9e8202246363496203dc29aebd2eeadc0351370123c266add3544a24cf34c7b837048c723a248ddf758500b6d7a9e99559ff7fa41c1a5fffc68f2d8873af0c657de9c599dc38e739e1f20bfd26edb432967a326531df43932418e16814d89206c99eecdc4b758f6e4473c892bc6748955f9627397ca2b4013f3bee5affcc85624355840ad27b692fc4b3f22f740866a0d08ea177d6425f4643d7d4432add2efa7c39624646a9689bcc0837a8ea2fc81865333d556b3bc6cd27d4208a83dc528f7ca64b155e6f3943ed9a82ddc5645fba873e52ab1d42a53e15852fb68844d28dbd316753e341df38c074413a4bd603bc7af93c7910a4ab735ec485ea618dd477598180c01f909532d26540c78e3e70629dd0f310d0135e9d3b2a1220b77281413523e74a9b3e95a869686dfe637c12104ff60e7422c024355279d9936b2e35e05dd0d2a91c04bc009cd3a6acde61165e4f8d616513cd2d6c34d7d8cd8084c15e55758abf2fdce8a49452ba6a1370224a24cc089faa1b8bc5530209084a268ce423f5364fb962ebf77fc5a4a0a6647ea9b1c53a0f465ea8f1255df42117e55d8f98299696bfb4efd6854b7c0ba56137646364df7fab54500c9114a3f626c682fbbdfb26293d4e99c37cc75c4358b846b1180ec69f4316a916cb83596e556c787ceb4549fcd51cbf74bd55163f5cc21716f13724fe7b7b1767bd6c552f4eec5742d19f6ca93a82af0eb3c75c0fa57a9b3abca744e1dbc42f732097a87cb61a314b884ecd6c12127e85614da55bdd3cddd811f2f517f62b03744f507bd78d3b17121afe480d2326e091377d6c1410f9ee27b261986d0dfff8598c7d84a0dfe89bcad1a29f7d618af601d3695fffe38f07fc361a2299f8d040036948438a9c0d18247640de8d5672ed3902d2be8b3e3917dd1e377167d3d048d15ebf65375c5d6057c92d6dd2831655a1fde4e9a6140350c8b23509e6a89def3fdaff2a0467bc3d3e64b1515b5659f2ff5ce436ffa48fbcfbc4c1405fe26d4d7f22eff610b827947392332113c8356af24fea71d3ca1dc567a2d9cf14720e03cc667a382573fef1f11a2d94a81cb64ed95863873c9dae2d419ef5b82a2167c9849d7813381d047bbb9c05bda979e0b62e7a7b49d8267aff27f0f36f241f4027da3f274eaff253657d8e760f8ab88f9415042e0062126ad83f51103c0496bf312acf017021546a56b1364685ff4b581977827925e1bc75dc02e7e9542e4e86c2a0509ae45a844208802b5fa2a06ec75a14d10b1c8105bcceb6edfb0866f0c45d73fd92d295c845988b104370b2cbfd8a1f046ae78520f7b4c75ed1e1a0c17adb923cd7446a0aad12379275edf4bd00dee7cd49add85487d09a828a8e54072df82ab3998f35f633c19c74f7d5d483a222d8717e41e00c8a7f4f704308c8c3ef12b5866f6457d407ee79aab0d4fdae099beb8f037de25651382fd81b05d1dcd0d7e768892b294350fcc2f185a252fd44ddad5bbabcad9aac5c697f5948860382bce14e35beab53b316d155124d4bd848df05238051c65d514543a4a8113bb35b6d5b13e02b42b58f2c5eb8de84fc56db501f451952346a56fd1a34d8173eb5d3c0fdea6047ad19c8789d447401d6de3498c74ebbaac492f658ad325a447bc74ec6688c2a6bc7e14581c0349353f91f0f6fb84c6507fbbdd96dab63ea8c9647aea530675aeaf34a3d4b7c814b4ea2e4bc41ce81cb98a9e266148b00639c937ed7b39f606bbefcc34b4520ba193458bbc2767843a27c1cdc2931a039663ae8f6fe7f8485ec6833511fa496f89c4890b065055e48185732db2396e3524986e60bdd72d749c0f29e2e2a9a1529b139b5924cae52a5c9b89001b89cbd627754b71386c4cf091d75ae772d9f23e540b1e4018dd07d0c19ce46b0b9db4bed068b1572f134525cfba5cea40d7ac7cb149008a31d23375801166ef9c65a041f8a996b9f2570c0c1fd4e9a662aa86410b06819a181e20dbe91f6d0d592aa8c2a32504e5e73ffbcb3f5e4225ece9181580341a88478b3266d13d04819ad623562ff32895fde2bab65615d4f090c0b8651b73816ee673b6b442997d02fd92aa228cdaddcfc8d2749d945270365d94e177e0400484f56bb853b86ab9e67f6f6e433d2d3184b0c37e9386e7b91ed1be8e0a173d7693d86fd65e027c2365483853415cb710863c0f427a4c9a53513041ae53d53f251b76c208c5dc4fbce93556a9854ca82174fb7f891056d25393593f852be25a0beb809aca9db49958ebc556f70a436acdf83f382435dd309ec34525d02e519fadc691acaeec941c03c446d40b40dc9211712d35ff6b3ad57b291eba39d8161c8f4df4adb1cf68710ae3d1a6f1f188533b617d5a57b0ed5bf70af0b15714e552ce3e684dab355b321ebb8ccbda95d5043a6d286f6ea7996700c6798aa4971873215841ab363c55a9748a0e771d1ed56d9d676dcbb076bd837c3bfc11ad39c813f1724c0cae134fd02b882fd98f89780f79210d1c63d729f42e537206db0650b9bbbd035060fd61c62bdfba5fd1b4dd1425df1aff3400139bcb7d5708aa204c67c09e34859ebfc0c48d4e056c6aee61da55c5522f4d13b1048bccdafa50de4d0d4dd5d98b42d0f691d540443d7df6bc6ad350e499ded00641b7f01814a57ce768971c43a3a42554f4f480a8668ade05d8e932a27031d21d6e9d8675003ef29d0e1c7ab2b10b4ec9eeed6a7d68024508167a112e43798ac4133217c20ef61b76d30741270c2f1a1c65600b7e0b0474e4b0eb03d97742c473257261e826c4cc6fac2ddb5e9a27d0b9343e26cd2409510bf8a17650d13eeb4d33799238f94f3d8e2a45b501d5e4ad4b7972c77886b6b1225dd1a84a7defe80d99634081a96425f774917e817f381582896346733fc6bf4300c542d0d1e9a32b6881a74b6f80b3756bec00892f891642f90f1cecc78194965c486c8e83b2605111b07f52f5f407b0590d8378ad13764901536c4a8b5071d1e8bffbebefb6de9689e19d9e358528f7e48c16267a71bf6708875c04a8f008632e8b661a76d891040294e4453f0699b31c902b75fb0102e06ddf7cdf9b0e90788d0724097d34485eeac2fa7c5a95da37450e1aabed206008b659a4406f843ef00d66aba13b37a17884d518838d44d35494676a467c11722e317819d7da065d4ef4349c4813193647233d9e2c353c683d22c3f36924282c03c173409cbe5d94885887190afe58555417c159e637a16c06af224fa7f6c2c015b059080edfe25dbdd9572498551421d70ee55f141cafb075fffcbd967636f7643a0ec8d70c595f186d73ed359efbbddfc68fc5cbd954c5684f061880c033c24e273b19c86bea831f1f4198c516bafeee949f9561cbac8eaa3809a046c456b829ea4816a290208e87f9732312724b68ab76d1a6f5d8998055e10d858f6ff0a6910fafee1818be434f91f5a632875c75e9fecaa0530634c1a5ed20c3dbb84affaeeca0f4b0b470b735c92aab622e98925b9ea0d835134cc31adc4400c76487f8280786a7da7f65adf86c4d2d34c23c2d6601adec06311b46920c2660488583fd4cb96abafd1d1736ac516303dfc315d01fb305eb0b75decf24fda10b6bb49a4e2cb6029a3163ec2b80986400421703634d952823c3ca4c40bd70304a5fd8a00f2b29788228d9d561352ffc205689adc10e3d441d3b502dfbb58c98b4f6acc6311cd94add32e39a6a68ef589e8fd0bc02009892c5f2f3a93f7a2e78fb673608fe3180f43199ea90c7677f274b59f1816f1370d63d360d92c498dc013715fc08adbf6784ce73b3deea579dc12db273cc7b0ea073334042d6e7b8ab3a394a1a8df1ddc49ede9af3f3307b9f0ba2a9c8a5c74893aa93957c7b58e442f3a8b68ba9aa1814c219f601d0acc8b4b97a7a5cc96d858471cf6e06adb7bcc3974e257197c64538490070e0d0111d0a9ec914e1c942e7261745f4f02dcb3752b90238940842678ea6b2e6281b4c830a42a64681023eaf7fb67001822842f40ee92f226cfa343c4a147f187c682ae5febd833173b132ae1e93a7cc8954e0673e053fe778a3811155fed6cebc420cbf869e66ecef20f33da3356d1cab696b0f212d896cf5aa7cff44c13dbe34d6bd5e164551de89b6f2f6e17a2857f569d59ec7326b82fc222f3b0dc57e03a9bcfbd81f12b19f2a625829a9c11f99bfb4b8743c04fb0df4319df3b30bc87059b305fb338568cd8e182de8294af828c09cff143082b265c10440847fb081d0b32a1207679bca6ad95cc819a01548f5374b384d93e64be4483741a37651e4a424ab2e7fdf662e85aaf47a28d892f02ad5939fb3de4ea6ce026178d8519a24ed5f2f1ea50fd113e50d303f8c0d2e8424db471e41874515f1a9202a8e91bc603a1a1c7c46156774a3a1f7c6094e7056835a4804b2285d126095a1f91d8e8dbd0ffae31224b4a76051e32a8ac24c9a822938629a5e65652111eae9d41f9df3a798a5d8a56ca962d0d438f2504ec0d1232ace601bc76db96ac00b67462dc274a9e6fd92fca54d207e3ab0effb893354a771ed823271249f6510cb518cbe229c65eeb2934e88887811e706cfa8191da6639e81d058a911aa57c60951ca077145725f07aa3dab130b7a1403755d08bbcf09fd28c18fc7dccabc58222a588eac1002fc73bb398605a339554f66029e3db0aa37281b03fee8b02fada47739083107cc8ce6454d6d9d56a8483013092a7da1600f0ebcff996caaf145021df0646629700dc9a91408e22dec75d88ca7c04f77db2cc9aaaac82e864b0b6749a6237db021c5baf90dbadb182adc2025b8f95f63e1d5dca3daf3ec6fc0b336a8e6e9195030b80c9ae4be53fa68bb39e25f16dfca09f83dba9bd1f9eb11219cb2ae8ea77e9f73f207f247f94cdb2121dfd7d61e4e6cc265e7a61f2885230008c943114be49f8b3156e8cf967c761e6a06077af11294ac0b9db04e1619f64ab247d4330bf806eb0702659cf01cbd4331e47c1b0fe5eb64dbf5dbdec1da74e67aae046ec32ff4c93558503e8fa87652761d092ade2947ea6a9cc0b5efc574cc0adf37e437d9031d03711e8167f7a22cf7319906f6bdbe53acb324619d012e3ecc28b9fb2ea1df6fb3eb3595665c76a753deab1cefa56788137488f48c3950e4563be64ae77d7d07eeaa76398a8fdcb03a0296ca68d44e2db9edd41603f935b26b09a15a49ab8fcf040351a5128e5c07cdee9ea2c20982c9f1e3100532ae896d54662bef9b202ca4d5fa9689d95d864f68bb4a9480e3c5780e2455c595b891eb1768778e2af53f8be69fb9342e37814d48ea5d1c981065387633683a633010f890909af11a4caecfce2d5d6352db2a408cef7aff505b241e5a2dc92e8d2fd45ae34b6804bd20d6bfe3bd0c1cfa9f78d46838b9ec88a60af65b3a20835e50b09dd26686512f9f9f303424156bf6f872c20b7f7ef7fd629d210493857772798a8b5775bd9b88055e0eebe0ba8830049e2e5b9843bdc80e83b7da4609b476bc557fbbc4f89bc765bf8f42012477408d0cc435bb0a27048ffd8dc8c23f03b98a8ff3e51a135adf2401f99466b359eff05c5de21c16c47466428098a0f38520fa11df1890929a3c84d7ff3e0fdc6d2e699b88f8f50271dc51d7b0b72fdb1c78494c39699c27c143aa627f32cf3c3a33bc9db536531acf8bc90ad506d7add57fb1e7e23514cd3bbff14ebf7779a639dba32b7014a8012f0a02b0cbd9722b141108a104b4663fa1030121f58447c8259fe7afce114bc80126009b02a296ba03450a130bf48e17ad948c23201d62a354b8736d2f683cbb2020ed1d3e6d3119331596c82cd0d15b18bc4aff84f2c5892d8427ac3815bf3773436d910609c4508b43a266f57d18705b2e440ed57723621dcdef76fb737b2d227ec8a9ce954306012b76f0fd6e306b1e5aab628212f07dfceb5207bcd4b9cd3f2d9d9b11673ccce35b7082eef3e37d0969c1d5f4e63a32584c27f9a583753bbddc4e3e32a5b3668e4e73108d704bfa35fa8988a5942c569556145650d7d2f33d14487e6ce4535ae80a20cc5faf9b483878ad56345c10931d0e0e6cbfae55e797a7a76d43ccd621d62f2e3d9dcd124bc542676cad10e4506fd53a04ce4a537f812589a6c0c29af280b63f3af1426e392325ba9af1f96076ccb55165ac7752005a9fded260f3246553276c916821684bfa0f6f36d7ccdaeec8fdd55db396a9b1de3006a19581ba0cf8df7a14c2280e042991b1a26edbc108207d1d32945b69750ecf7dd4f67cfd5fca22e0a0941b62e1a047a0788433699480e09b156bdc128f1f1ae776f28f67e507d8a838b68b40b1c9a6f3926b306a3a12586cc2e6a3e9119b110bf149fc0b669d546cf9d5c4816c29377543226a8dd4771def0d1327aa747a435f69701c771bd49eea45d47a30777a02dd102885cf77168c3cc65c2869ed8549c551e4d30ccb0bf2e23b3ed59a3cbdcb6eab2d2fcce1156e1c872ada196dccf4b93a59324b0f25984c6ad3a0f22f0f865269eca5d5e17e11a11883f49fa9233f8451d2a9a95fb5af6250cde99b0ec980e2b93e7c4a0f91814e17105c3243e30cd4d1d2c4aa05ce90414b79598a2449ca657e07535f12ba7fdaeac3516b9844dd0e9da42afb91a372b8ed6d7b48871457630529d2745fb747f279831bc5084caf1676f8780dcbbaa951a84da68eaf4c422cbc8a855a6b3b0ffbf8d5214f2de6e96a110d87f3a14e4e1e3bd2836082972596a904c619262afb1e15aa0401c8d4bfd83bcd9edc3294dc41a24b71250d2079c2357de341fba8bb12ead05fd00f04568dcd5b487f40b1f800d5a1f941f113f3812057f18423c00216a6d8178dd238228d00306a178741082a168781ba2ed023dc11002f57208a87ba81620e397444871220569a481f5c5cf80e3a670371dc10e0d4b84c0f29d6a43f3fa501e15a026658c56eed6145eac6ff48358035040403298fbc900d877ff4036209eb21b907a73a3e85841ae3b1504d86fb5a864ec174e0c4f4b5dd71f0bb2db01702b5feaa215c570a6f813347dd76cac6c8dbfaa2ce9524cffb25c1f5ddb5a2802a8d95cc465b21f0daa175235e62f471e0829bd19d507526f771b16e0a805fb0eb0c3c623ee2cba1378e7830659fc26efb6141fbd93b1dc2944f6ef7ed5dda89ff6f9d918d8da5bf1aadd8ab4fd5b31f9e865d0de0f51a3b68ff00f37a829b1d83407ef86dc95b816cf2ea7f68964da82c40211fd4acf1ae2ab393d4e255827d64595a3d9319e6533205dba67daf1e63423623a0eb5a0bbe8d376cc9bf2dd37483bf14ab756411e3f4e2a0713bf9f83c8552447d204789478e1d8c3909c754c19457f6b683d64627d69c330182976e8be9a6beed7c821f309ae07f6ec3fa37ccb05002da385cd38242373179f06b6eb00864462d93a53f2e8adc318a805af22bccc8bc51ad7573be328881c50436ef5d11ea83490ea46a30061b28dad2a0de7be8653612bd43a7cd56edcec72dc96f4f687361b032c9513cd3d131720e2d98eda6130e3d4502c0212987482c5cfbf8c10ca2cf4a1d8e74339deb897f696b1db28f92028fe56bb8320c195de14c78c644cef4c6c502d1cb30dd8549fa1101be19686144d2a81e2335a1b9ccb42430314ceb341a25daaca1afccd89d09dfad70894fd2eb26582e2ddb3b24d91c5597db9f407fd08e8c9e678b209dca9663fbcd66664d4f27c79bceb6eb3d8c920c6f075e9dde11000d052bf636a1a46ce3a771704929134b16329e5c429e50305776a51ba467b644ca059f7a5a2f0457b31019d252d4ad0ca05186b8b5a9869bfce687899018446f6679acbe5f675b94aabd54b844567dfd9c8c6b79411e09967c4d75b40f4c00e2dad76e12515f7d31188bbbaf7694596301dc98edd711cef36a32ea72bed71581540695f1cd7b809bb14b16b1c108387c1af8c05d9d6d16d276aaafabc383fc5a98e0f5ad37316145052e5f5029b636ff566ed8053d9532a1293478c22f0b47097456ca1aa0f926e82b3b7ed22a924cefc5c331409aefbb405700224b53aff3913f1cee847ed19f5b372887174c2eae6abec5b2c9abe5568d294f3a180bc02708482d6458c99576540d5beebc936e0ee9c12a0b8cd6e422da4d93698238a6e60e49ea0e6d4dc4e8713289b3ac85607ee16e65c214ee03e43cf5b5682184d3c8c4ec8c9bf075c3e685f2fbfca9c520898ae6d445e310135fd0f2a909006ed8d0e2903e405dacd3ee3397b270145a49a615bf41f9ee872667400cdcc50fd83477514ad10ba9a5dbb8a365330bfef0f583cc071c21ebfcce2c1df8a25f824ac0b41fb0096b3d021f3804476c9a33d9a5c433e126299e3eaa2fe67d7e94f6c7ab0361646bd01b89fddfd39916294114910ad3808102130046e1a6173a29145b5c364f0659021464708c709281353f4de404d79eb9c7df5c2383429e608bb8245f10e2a6e62621024ed7a4a3a0a2b788e2bb99aaa1a052fbccfc58241adc855e4fbf7a6877a6175937d3d8ddab317f566b9dfc62046a934d63ff6793efc9414cac733175311a6b59cb8ca98101d015b61ab873c6d197beb225df3fe640924ab61ab75d3784c0b99898b022713be41bdf39204c485f3321f677d13125ace169b02a0e626e69b7593c618178f5b88ac49df23338f0e4181b86c40c65d293a76fc6b49ea63ccf1d344cf9d0ef7aedb5b7dc16225d8bc6922b820afeda772834849dd5bbb8cbef33337a0f9a65d1deb908139a2af68337bfe1457811d77bbd7788ccdae93e752b5ea42753cc906540c2a298420100481419ee1f302801c4181482a32ec4924d88cf72374e99de4b266905c09c249b7aa0962485d9704c55773c89f66e38b06e28465f1ff0135515340c02155a9163cc4b3e864d987f184bb7feac54be2946edea5bde603b0872a93acbc5eb2124165062f39580ca6f9bca8c81375895b33d86cfb51fc5043ad124612fb52ee48016a5d2a2abfe3faca798977151669a1d3d171ff159e803ea28ebcec62f26fb85d5bebc2f081a7a908efa9657ca5b3335518ef94679a9e9d644f0c5d5a1810d90f0e9090fce36797dc35994d27082162826ac4f4616493e45fbfb137913c46176b48415026f9d7155c7fc969da5904611944ec9ac9f79073f6bc9ed68ba0588724b279088e6a8ee1dc4d343a32525bf9e225dd0449a84519c6c680c9e1192343550fb28a18c46d92703177a8a2bf315ba8f5aa6f0493d2b853eab3e221bab8e0091d1ce2d6c371a0344a5ca09efaa0c66dcd2e5172648821ca3deb733bb0887a6a57b7c988d4d2ab31f7b3062613d3200cfbc45e2429e2103e9549864875ce3f1134d998b4cd8dcb6e97f6917f5de30f304f2f24b226f9e76a306412e4ddacf6463f16625da2c0f44543d45ff9b6336de0e34749be125a49e6bb8acf0c481d367398be162b4fd6d090b54e44765821fecebef1e39cc5ee4ac8130e7cfc6924e9425b67c86a541bd23d484888a317058b93bf65b2fc50560d7746ca3485c5bbf63e74a3ec10857210869e37f41aa1376f38613ed8c57e0401680082f7a24c70a3c1e0484b9199993d5d349706f90471adfd1b382b03069d336b453c66f98e7f63954a888ced1b70af1c2b68f0f8c4026498d9844851b3d1c0a2fb3734b6b6eed4fa6ff0bac530ec96c44083066dfa4486ebdf686d0d4bbbc659217f130a63519b6c685c00fa13524225945c7907e2af76088c8f7d17a194e7b7f33f7efeef7f987efa9402c75f15003ee971f067ff170d34bf8169692e074d53017d185881034a82fd12fa285ddf2973c4b4dca53d1fa393f22ee3bfd157cdba0545f82b512051e1fbb5405c81e8dc46bc007e7f0c901043a6bcce59bba02a149c323de6d756943bfc463693be1b2f2654bccb97503415078eb2398013fc2391fe65c71ca3ad7a1a38b8137de73fa591924c5a68e2fd240e609e0d04d5262a039c5225e3a10b87dcda4bb91467037738b8c7f87dc179acadd4c59bb76c1d4c1c4a325582f59a35327965c042a9fd5df21e19971fdd88a09955a2776e5fe14ec78fe966bdc6f819485b0010d100032ac8b25c5bcd137965336afbb74380f14e588b0983896e49b5efcb0aa72cacd4c25d96f200df37774a3086c65f974514e2360c709d1003b26eac5edcba4e4a837d6ef206cf401a5c4d393390817983a74ab421802f7f7d3dd42b0bc540a4e21b42fdb7f396899dd0bc1d08686e32ffcd24fc8c873d6a3010761320335a86951b320c15c6a375d105c2e6d90563ab0eb5f94bd769eadf651af2cb7164ead7025232eb4648977c81a401fdc6f60b57a39be383085c200d50bf615c251669e0a758182f909ab9f5709bcf934e0862479df07d85e5414ae0e8a142ca153fc280d3a93274eb9f92954c890acc2558438c5ea1d68fa41ca27a027e74709d872cf7c2b1ade02abad0125fa0e0c0de3674f398bffbd6ddc610dcc8c34e9189a120718284a34c4a51ebc665e1e8a7485596a8ef2f48b6c04f5d92629bf7596a2094c3b29e8f4b665967fef934450a2b09f551ed975a9b346603138a8e9964a02220a7c230f2ff9356243c80345dc80beb269122dd10938da0779e8301d6cb9a60541fe388995af991bd0ffa089e6480de2122606ce9e63b987129a59970cc26771a21583f5361acebe40a4bd2f9252e261c5e50c068876fe0819a92f9b3134679421f0991dec44f8272b905de62aac9df852dca4df0af13b3b2c0b1ec05410a78b60bc80eb8b36a1a99e39bcb2408e9f13259ab00f9b8679f3339202228616c76162c433e5c851cd9eeeaba1dd9d8e76b92e5a6a111ae9548caad8975974d9eee1ddee5880ea880e03a9b70acd75809f1d867d9306e2c872094330252f2cee0a74ff608ccdc5d622fb83ee196212b35e062c79b07b8972d350c6558536a1b5a20606fa12189df5e8a3687cf1b6d20b071335de370904669e446d46889fe870f8e81bb04734ed1dd6940f6af844a5102f55cc22c3ca62100352ef847127800b7f347b572b36ca2f725163ab1a4f759e1c11f996f14fe9237a1dd235ed6131476891c7946e9893c79c1e9ff693fdfcb2b985b982b1cde4fd24b9359140c48524a091c51c9fd5e9bc257d4fe99899d94e84c668b932cb34e97b902411c18cec8a4bffbd5fd46f8c302ee06c38675f360294d037662c4f1ffc6b8ab2b3677ee80b03914dab72149e84db8effb0235373778a0e8f5c21ac08aa4ea84ad2765ea66d081d44ade9d23522a84ce64f4453aa1e7683e3800bfb718bc126de8a1ebd7116234fe5d0ea66a1862ff0252691e741ed0330b2a845844e7bc73accd757cec216bb8fbcb09784fb79b664180f78804b22e069038b45038b41d8104833dc6cbc80aa8894dc0c83afe91e2f42060f3a4b6d74a7c83fd23319638cae0882c069b48ccafd37f8339839f39335f075d0e33ffa34d9f444d5324538e86a56166ead79f9a4e7825b1b677248bc1434549e2bc53980a75d1d27f20064d319fefefd403384f922fbeefabaa78905c18d6f8980ae0f15671c956bb37c58a57dfc7c7f2810e0b7456e446d03ae51e35463780e0dd454b3f35dc1f4066d1dd05f241fb4856282640ce50d86629c08ae900231b3332625ed04c5d466aa6a7165f9cc5aea236e2619fde94812180cb6dd26f7f6513fd90dea50e0cc48cebb3c185925a932ccc9bbd9c25eea83ffd9d26dbd10f676eb9580b2ed047beb21b45483bb89f13e15a3a3d02651f389d2579fb55e4560dc6857aba1a5a54bc90f6a11386c4934f2a0083ae17d6cb7dd287a1a831afa6c00d041f49302c1f0c250ccb6c150c14cfabf3a4ca39cf67e66770589043254d4b7088f2c37aa98c9f681608e250b5c4e74aff00ce38d255263cd61480cbf7ca0cd87d8ac946816a7e5c34caf5bdb12df86eda77352519dad50904bf180ae2fd6d261784c74cb93336fd259270bcb61e0fec695837b3c6d91771518676fa039234ba35bc31d74bfad7dd8625debda5b7c1122df9e7acb17e4923a9fbf3be07883f891fbdb841dc3d9c7695459097a6864e3526f3303c7abbb853e220598a94c18755499ed5733a8b3e823b7d567bf560f0d5735fae890a3eafdd14639e7610593f47d491a815cd43a330c670504348201839cbc0d30336e8af35e00f2c3654507aaf9a50ad2f7a5e09ed49375996216b50e3718eb9dd5d2d7b20ceb0dff8962d8e496f775dc13faa089e97e1138c2939b9a18c041cfa8aff595cf892d37f38ba60f140c27a8043d02942ece898711b3da43d4b471dba9244bb7cc299c6308d4e870fdf2d91966fd53d01e32a18af5fdb9470c745ac90174010ffa6c782a066dea412d4464013c5359c46660a437078b88748bbb2326ae86761c4f7b37e7751ee594debecce4e8096e9cf93acb07b66e5f201adef44072a9ae5396daeda243d8eb7b6d361c36c397bb45d04a781b3e80655cb46fe90e6263d36e4f95e467ff41da8c8709b721e071f4ea8b52a58cb4bcfb4e46b4c092652e658f22868c5c18d71d8517a04e6e9e0a361396873d3101dd308247346842268709a59bebe20cf28a11f9289e4f7590893d76a75fff1ba5c0b021bf7c3fa9f7eaa0fc2c4ca4a3e3bfb7f8b97e3104eec261222e00c5b74584b9556a80c161e1e402afcd5d77ec8c1c31891dbc4dd0f2cbc141dfe91701157e62fa3835dced2b920423b150c24bc53ccc3c45d82ccb12b601e6be1e521ab5f1d6de7a721af629eb304518cfcc1e29ee88af28a1bde725cd28d6a446e51e1a93fa7c7a8637476b4bf20d0a250bb84a237c66db078901b84872d8a8c0aca8ced66270417510ca22f85a4688044f1eb81009406bffea5e12ba078145bbc2685a13a52e3436c73e1edc727d3c3bdda3520e157f3dbc119238c1b777fbb9122b40837f88628126f1a79c7f24e4b65e7a308d2e21c2602b3968226ccce8bc590568d0d8f888dacc0ca77f93975b36a7eaeb41540f8c98b1def664ae75824e5d678396e3699c472c9ead5c236913673e2af555c62780488ff4afae6c234b42acb71be5cfbfc7ba499d7e5568336f30ebe3016e9a50d306c591e4bb76710a6b99256098250779503fd8f4996c209edb51647c5005c092eb393785f66ff771b5d2b6bc62ba9453f092ba3c3d5a73f70b1907f61b1ed99fc776909ce03bce5e5bcd5bc8580657a574b0c344372d1f0f8686bd147e7b32f970f5de10dd413e6f7197c4dc32f3daa2c50f29919d5710a8821267e3a9310ddccb2d2ca0d530405e84534d92be5e302867d28a251609c34f0050519812c0e1705609ec94716589a3216e231b5e1118e056e7187f848550f8445bbc3f5e8ac2fd5784b6cabc4403f3a0e758b7605c0ebb25afd4be47714e17f6d3c01b0525549ac8c389179354c54d58c14e36af36a96feff32a74e34ab039906aca77d148bea1dd5c63ae963ab6ca4195b9c2030b437d28869fe0d43c5b85d7112d9b2eadc1b17b131bf7d334fd17d220748e4f0f60556ac1c018195f5d4fd0170a686bb48376cb8f91eadcff4f837903cba62daa49bfb1a212e5af584bbb903df4a726489002127e5642b7c5fa5baad017e639f3885a6ef35b8102d4a1ae7427654439282ec56fd25331f15f23e501cd7cfa0bb38d6e6bca2a0765d643978145c26f2cb36dcd86de1ced379e5a28380f808de7e328353f9e5aaa73c48f3b1a29182ce8dc840a258711a90a6bc7101e27f890b387870964616e6143ea027a59fd9880cef92e31cac2955a7c77342c0ebf629d29ebd12746705d6ec2fb3acdf66b9967172052b33599a23b34c03ad91ef32c913890c5dcfa14bdce08621631928541708a44822174712b4efc03562582c160312077f6a3ca72bb21737e6f5654507706d3ae5e1ca32ca7805da4c574fb0ce0979c100726ed833c149ca2596730a508fe2a8195930c48010684a9f828b7a537c46aaacf5380ec26ecde403e8acc4161c20a8394b801af0e3e83de5d34bebf73d68bf36841650c08ed887c6166964a399adab08064ab618dc2f3936786e5659807af5c940a4c2a2eb25853b01e0749021597400d44910ae424e810940245a766995cf1cdcdd930e5238b83ca19343185ca5dbbe12fbd41cb4b7072f1d63bdce38920714c2f99cffe8445c98b9b0e2893d83deac798dbe755fe2eb064958d7b8a00e7810f184f7ccdce7d54da7000da75e0efe9a2020a75c3e86930294bd100c6991d8c5b9068e5456475150f3a4543e7891a6bc254fc2c95a769b2798b61791eddf6dfadf3671d68047aa61a7a77901a57d65a2bde4cfde033675f9cf3919953e07ec3dc5590da785f5300bcbbb67f5d3e5b93837e3ac2325dfb31bdc62128154a84fa3146b9fe7f7e5de7e5118dfc030cdc9b99d0cb3c595153f57f8774288ffe5e13a1bc611c753d4e356dacb9b8feb72193fc1b68183da7686a501711ba50c3383828a96be3ec853c7ada60a27e24e87996de0662421b8d537d5eca78dbe37b1991251b562baef329dba439daead98fe70a03dda0a56f7f560423540c86a56042e6250f6bad920053d79c11d9682a0734526fa737628e9b67b1f071133235867144c6ec1eefaab320c24391f401ac1fc44d777f55de57508daf4f97ac224018ca7f0c8a5b924271a5325525bc0973ca538cb42b5cc01a64a2c125618f02be18a0b496ae455cb1f7d5cf546356e27bbc0646c267fa5c0b3de3888276ebe92d9ef749f2b3a683aa07be355206b9aef30af9b811541d2d81492d181915f5a07b98aecf5e90950b3532cb0324cc7d47337f3db9b1ee032e1d6d359d61565c8902d0d5dd0ac5669884e1afab9210f988fe5c9ca6c1ee180ce930e73d99a53ffa67077c656e8803b89d84b7dcb53c9b3271985972c868727c17d2f0c7dab97ccceb505cc5324bc274b5a6d0aee3367e397714ccdaa2fa61c8cea112788a50509644e80e9bd05c39a913981404592bc97e85d5117d5f86e91687c56a0d8c6c9e242f2cbbd6d585cb59b5b224648d1834e65e3a710e4830b0413608566d1159fc02c15fb85cd4d79121f9ad6af15a7a7c1b9b390d687fb331bf7ea54e9ab98479f116242bda788355120c4f2a787e24cc5c2868975dae588f4ff6107f5c8dad74bdfade9268283a2cfeb985f0842cfeba4f50179ec0fd1d52b68f55c398281ae1aa5fcc08a256038801002b2abea0a499891a6e5fbfb05fdb4d38995b2a08cd98f26f7889323b7c05bd4c17c738b88c488c29a210dc30769707572e9606358599c2fc0b3acbdfbcd97f50dc63d05bb240118a2db796ce84030d67bbe948b5a35442b7fd28bbfb155d0a11c459928aef4e4ef51716350364b7aeccc499c15fbcc82ac141f59cf8c56a9d4694ccf690bbdf6a67198fd905a205419ee0982f83e3f0d53134fc3df9387b19c26299b49c7eb12fb60eaaf1256c9ab9bc355711b9a8b870e571a1529654f8533787774fa3be51edb9e978a18eb14244df9ba9a065806cafbdf848a0e021d4fbbff1e0f9597e9b665ce47e54310dda74186b7a64b4e7cdd1bcbe73628715d633c1581e60625870a4087aa343e54b560fe10feb802641b70b14a88d04563014e151876d25b5db543e4e56ac5a38b05e42b63db694dfc96887a18607e2debe3ed46a0e6850acde04a225a04e84a48db245ad55138e1f64ae21f5203db2b3ebf1ccb81c28fb7cf563d71ab18987d3d474a2fb7ea89af80cf3adbdc35b2b4e02992584b05d33c8994bbe31523a517928f4d1ced7f44e5459f002a3661edb09c0b02bc98c0ace8d311a0b22b114042e11a0374cd4126e08d1934ef3948d9497f67dd652690fd559fc133168d8310796f7ad9859b7a4b292a450feb7da74f718c6eb77b0026d458390c97eb42fc9cc8be20a6ddde8f468ac36b41e4d059aa5870e4c9e377b4fc37ccf4400f3ac3b640fc8818733ed09538cc4039041274dc0d874ec97ec4960e6b858552a134a220be39147a84e38167b910f14149761542cd551f85050471f23711d566cd56eb5d22cd62b6da06c0cb1025e12585c412a76021ad3b252e4e4a15801714a65740986f8c4bccdc8d151012aede85849ab593dce2cdb162ba9621752a9096a24179a1cd8f9c6cd91400e6836a324665cf28c6577ea4a616fce201360082e06f75eb39e04bd0d5d8dcf12e1d93fa00a39f1a2be734f4239653d8376df0628d9fbdd4ac06ce1ecc9654a237f6199bc8c9d8a677ec55c84c37d8b9ecfbcc176beef2b0cd5c52b7e2d950bc6f364a9b07b12466dd1d9a457cfbd8d2663dcc6d027ad56703c2fcd491b8a9083187d212b0926e08caf54fad74a9bc857ca508b34a19ae9291f368ef57070d837d8db0c2b37e7e0497058debac08897e061c4813dbde35f021148b75c14ec8308b28a3220100f2aae0d23c4a1880e988f646a66c05531ecc0a14d59a3dda62599187b939e929b0729a4ff9812accdcbff7515e3043307dda9365d50afdb8b2e8711863b0aacc67a20c6ab48fe186e2935508c4ce5f86a2172b34935ef36538270d2b9f2f421447c9f4e206df2267acc16226fd158ee86c75cbea79f90799f84950bef8b53382410f7e6c7b00c0330a6e767f75b8cfadb6cd91a1e94b0b2e738bf9d516f3da011798f7a4a9e4f7fa8b8afe69750dd422d3c9edeffe624452a7d77f9e4846c3495941b4d36bb0bec509c2ea4b96afad4e12810ac6b772534a98808d52cfaa405ab7eabc659bc26dd1e642a3303549861f6953d246c6c6313926eff76d5cf01ec6e6736695a435686d13e5d9ef624af77a67d734a91a411bb3734030941ff15d60ebb07de4a048104bfdc8f601f5ae1f2250cae4fe4d01528e81e42eb4fc59a8de322752a6bfde882abbf3bd30e603e5f4f9ebd4e0de1dc7c6817e0ccef42c24780d9e309175be530821e4ff0d19a149705c4b009369b641451922a514a5b5eb8d4e568da34de7e424b004f1e18f2f0c1d652827da119ed702b6a227319595dca515f2045c161644dc9ef7ca26f86cf9ae67eb3dbff38588eb522565cba1f63097fa1e6193a7744a8b8b1b24d086d55ed9d7a915f01dfa08dc3465d789a6956c51e9c9197c8507014a7ed28398799559fb89a66bb8677415f624803e8799a6bf26b1a85753080b9cc9a2de940c1dea22090f225620a577476e09512c4ae470c78f8f53db375c2c4260a2a28e460f60cde98a3a50819a8370a07f194841d31598b5c1cbe97b5123f1d9ca7ba5624c787676ff3210ab918a1a24a3979ec583a037d97a73ddc89bc42f591724ed40fb4c1bc04434a86261db7710024ba3cf81c0fcf03b44872d53b145d627c0f4950114b7eb49f680c4c73c6a6cbe44e9aea3897b6b0f8bffebfee7fa43ffa9b2e5f12f1bef32236ff48265a3897d9c5ae173b6ae10f3ccb665105764e112dbe651c9ad4ea59c19a1640dea980948154f91d884e802257696197666a84024dbbf0db1ee396a0e335529967d98517b3a219af61566b501bff3389741dfd406caa50a038129467c81b6cf8981f1e1c6c13952d774b9862354559d4aa652488586e21b443c12f7c98b59da45c8b5158e284c61d15325ca81b53e28c83fb5c4773214a2f9fee4164c6f0ac14add4a69b8ad360c29e3be0323b7f81638ec9d323c2e483b26d8aada4c9f153e864bad0e9da21f95ac101a5ea103e21f1c917bcd8b215ffe30b887a712230226281773704e9e0c74b5110d7a43d3b3b05cecb4fd09ffe32545605ba9bfdf2290e17c8a57bfdf4237a2d312075015e37ffefd169cfc55e85bb060e5b2adde3cfb519f4477b3ed015dbcaa1757cb964f455ed7db91ea833eb7c9314e7694ef132c05686fbe7d7a40fe39fd1f977daa6345a0e6a7d0187f8db7c20445bce24ac3f36286ecbf0a1dcb22b395837026427953003601f4a45e15debe58ebe0084cfd3ad538b8c62ff65ea22dbe794c19834124a23942cb73cfd41968efc3f65baf453b0b66e8579296dcf1ed734803b44475500b7a65d0e8120a4141d0555502cee1aa49542c50515c1231a1d289becfddb5d482858e45010fc4db4131e99ab54b2d9dc036b060af0e348b4e8736ecf57273b72dad3464f2140bb1906688cd2285a1351df96d12c9c1924355b6b0c30fbb8baf308273439fa928f10d4147e655c5da79bca4b24c195b18eb12e8c0844718161d5afa58d83bfda926483267637d70a8d84ad4b7f0b50413cabc8bcef295ecd376d7e012a57b1e1ee7c50d78451421e437ba8ceeb5604f2ac527c855b9f975c16c9df82b252238d265244cd908124db9c991dc5ac27269d2afee838ed05fe6a2d0838828b0d156aee79b17e17c653c8d93f3b0271c72efb95185624b9ab65110122853ca8b0ea92a9e2856890cab741f77a49d61907b4fb737ecb5f200f2066ba6b8c9728212432ade9aafe46e81fbd914c959d5acaf74636dc3930c375986f6c02b93bb3faa486911744259558350a53d0ee8c285d65bce272bbfa252e5ab5401091e12aafb3da8269b897ada03631c0cd647aff46336074b3aa12855c0685b59b65a715cb95020801b5415f5d5cec83a06a1494027f9f72ef02627b6f4f2c27b2957765ee155ecf61166e95bf0c06ea6c0e28f4440661b0c762f50e4148a36a2348a521bbe5b08fcf60fdddcda98fd00346c63ab30697b8fbddd84c665e4762ace167a30d141690130a9ae50e8ed290c0b47e18eee8def1136f0968dc0268873f87b00b4c296c21f78dc1692a355f3d92f27cd276c9e4401b72a363ff24598da1adbb540c02f16f4e1bef041814a91fc7063ed24309563cc515652dac9f276f7cde48a4da8252e43527a5bd3bf4eac57e383d80b8077069e0cf5e541a49c6610d48c742505414ee600bccfd47ca2fa5010317f3553a1f74a3542021914a26a193b8852d6e6248baafca4f81ca6938a514af70120410c5d2f5a2880c4719867b1c955437d988372f3ef0a99465a6c7c4532199b8faaef4948eb4bf57bfd6792fb8f9a10ad98ca6251ea9bc4779ab8e659e080723397bf22e8d0ae12dc7164a0c8c983f06a9fdadb50c944cf0f65637be07c9b0725ce8d9321c519c19ba9e0f5875b783d16b817a38b798ce912be77d953f438c3b7e76168153ddd6553f0dd150d1d8f8253bbb7650041045de2856b280d2a599406faf274300e15bcd2805d14ca90bd34e845a54344d3cd8d48134bb3915ca66fdd73bd230cb148ff5449193f3a26bdf392f547da71ae5863a11282c7d8025d81b67e091e0eba89e05a7e7b148b84c4ec0805314bfb84d26c4a7fd3f086150fd82b06410ff10dec7df4e2ecc442fd381b33d7796e5aa1358363b4d394706dc43885319cdb93c94bbeb9cf7e7b484f5ab55155e9129f9344b8694d223dc85f444038233ed7123c3169e8c0065c0adf09baf5cd9454a3f24975b1725e83902c55b9ccecc69eb8ecb898a3ebb2a4010820715bd2a33e822de281f6155b3e6d591e1e88e0d454f8f10c2d714241648930819ced2a3ee4dcba7dc568c30d407895c9d3fb721872f582c56deccba25caa523a3d01cd4d1c59f78da054c631e5273505e11f685ce2146c42070c54421d74a8a2c9657bb9f3a8663f9a8499e247592f0ef243e96129c6a8008ab6d1c7a16d08b7628aaac320027ee33408924786f7abff2ad859509851b546e33638290b167b6da07eaec8614922ec2b60314e916bd093d0368cc03323c3d025715998149e683eb071213871cd1231d3ec40e81283e4f2a330fd262cc590944a20e454fef46330809d9e82ee3f16c3f916f4f787da0c270660fd68f5a6cc0ffac63bfccd0ded2d40788be649a07c55160ddfcaa3110e86678060acd526c84105f79f6e26bd9f12a2c9c11902f917aec45df262503aa6a5feed6f2e451ed09801929ab34bcc455bfbd4c5a310d45c205204ddff28de87ab1bfc4083a83bc9af0ff9f42dad1aaaebb350420cd02f0af7e36a3e7a053e6db6c9ebfa688d5953263166e7e58969d6a38cd7070d68c65a643d24dbf1d73ad6a39fd127c6583ce81598ace6f7de03208256e23cbd3c5d322dc08ef08efe616bb4fc8c24fe0318952b3da9b9330e16f54309c278968cd4700fe8ea72e34cb946cbd7ce3ecfaca9fd452bf450f0c5a1b67eff4200c20ebf43683958bc6f0062676168c4fcca02cf96c20fd89fd160c1ff8836f4ff50b784b62103769d5a20e8e753201ccc8498fca55682daa67f6464208a944e2d00362aa52a00412d1a99a75fd462fdd42e86f1067a5071c218c7c0d2156bf8f2ef55241881f6bd50b3b1ca2fa70952cbd0bdcd0c0fbb9bbb795a663d50093d43191010b7b0dbcd2fa44960dd97ded60cdb704194830e0cab7fd02bad8b92cea7124578b8d47ca2044d483d2cab2ea74cd5e9bc399314f4902f5ed361cb331678c9ef2f539a1df2ac3142254360cbdad389292df8770a1a631d5ef8bcd520d3d29216ecfb0b29492442998f76ca427571807ceaadfa6674c15fc02593409e3c555b31865bfc33f7e1f22e44959ef9ad8ba5a277ec2a08a9f03c413bce39cae4316eb6f3b1d6d568caeddb1ff35858f6b8595b391abd1f757a92b94086afa18f602822f8ce8de68f70d9e710aa732fa63f384cec846140678af8b3fa6ad59a19e82198224807875a51378ace0079c403819ddeba467bb36da59b3d406f5df71ee30e003d860ee01bdc8a006fb115806fe05700dcc0ae007003b312e0267e65d6f6eeeaee5b79778f567ee108547903aaa6d2895c3052b29a0e66f1684e3d4493f016222efe082056f36a834fe9959a8a7b01f479944b6203ea68d115bad16cbf50a18a87888af033f7d17dffbcf3ed0362e1f9d64f9517ed129555714dd45ef5ed6a9aeb61db8decbdb7dc52ca94920c6208f5074409ab1e24aef36a0e5df43ad6829166ae98f14c4848f1660824533c4d8f913e0bdb4c5ea8f8212121695cc416bfbef80152cc113f1487da2a4aa313253d2063b658c1f23bb1f4635aff27926825e6f9aebeb9fbca3a0e0e6ae977df75347c6c299d38565ec86d15c63c0c639e66695ad7e1d80e87edbaae9f8b449b7e221c382b269c848f3cc33c22df6523a35ab9a391d2e6441424b2284864247043b663eebb2ebc3adc90918f2d27598b6c7574bfeceda329671bb242b8880f0474667bd40e05dfe5628e09d2ef6678e9cc040358ae7f5f844cf0d21917c0725833d4377cb766c806e0659b338a37cd19d59a21db34f4421266330ca07f9c1b0ceb55b776020c17c813f9d8caee8d05df5ae258072fff4000e755ad859469edd6094e29903dc15bb5c8a7571eb2f063fbbbbb937c357a55351e5eb6d6ee46af7a73ce3b770d9c6110a7e7633b9b0b719ac542d462fa607ba303cdaaae7dfd316a3adcc694a3bd6cfdc067bbe2d425bebb4a7277770e9bc8ff78485baa4ee7acb2ca5abde5fd7474ecfc0a7c4c7bc8f7971f896a18b2342cd97e6fe6c8f64d798e63013807b001a356024b61b054de1a6b19433adc90edc1b53e7f36127d98fe9b91cfaac8ea9e0b81b9103acb395acb948a18c8c805187668f606b1ec8266697877eecace4a2bbd55a3dc4f30122de00076feb5b9ab0fe15ffef45d083476de173bff6eadd4cfd4cf84d786d53e84f0da307d3135156a1f6784b77e618c36a8f0149ac2191dce812e04b1f5ab46426b448c3a08731a17eba2eaf6d08e4babaf7d0af5b597af693f79ecb8b4075223431f29ccefea0cfd2e0402247e3504f22485fa5a08e4c96da27d0698073078e2e53a4190f0a5bd160639018e775f43b7d971d178d5d738a915d01a0d19bee8722c3af879b9e8877ce436ed217b116ffa5d1d05775c13dc71f14b1005e7179016dca67f05b7e99f210c6ed3efe302d1e5c205a2abfe05a28bcea894e34203714c0614dbc0fa13d43a0972e108d885e621930f20f56ba88517882e1e16d25afbe8aa1d5d1bc8ae09b22b9a72a2d5d13a3aa9599ed44317dbcf5e30035a888f90acab8348aac578583971227d1dcd6db5d6d7428ad3435c163e8697be46b5edb5908655d399b19183d0694f6ba745edb5e8d25b091373ce39e7e4f047f30c98f27c0e753c2bc32e12638c31c628a58c31c6253aa9b9bbd42878e94777d7a40c753c3b7534f8449d9f9d2fa59c2fe7f790e19471d28f3fa48dde61745d8c56f608360e590eeb141f9f292811ee6e779943a93643dd368eeb3acf33cdc0e0e974427d2f26f7efeefed13a2bdb355233a9544c0cc7cc130c4c4a2625232303334346e6e593919141c99c4c3232325c1733f874777777770fa13d42beafc8773f8fc23e4a84854e67c6f237c8337488cd185dec4ec62adbe5a49a0c3ee404493b81e24dfc8deba457fb8b3726695127d497fa66bed40c90167ea46aa0606052294ecdd3d7ab1930a89429272615131303332326e6e58b898941c59c4c31313101f09a745dd7df7ddc1dc38f8def1e29912f861af9aecb88cc496995409f468d7cb57a413394c8478d705da53d1fbf67ca89f186e31606ef477d3e0e9f12f93a540dd4a34c26d4f8be979719a69c1a5e4da32f7524c3cc8079f9605030303030273ab331bc3a74f6f2451799ddddd999d946f904cc65b6c889a4184ad145133a6cc0bc73ce1941a97943d1754768c124785d1040c800bab0f1c000a9a7cd6c2811a16e4364645512137762da94a894133f56b6eeeecec180cb41fc8e71ebc1dd6397e58b2894f5f096c07259f0774eb8db89b7b7b7370883b72c106dfcafbc40bbc423deb81629bd8a2e1d857ec59ecfbb2d9ffffcc1e71f65abba7ca8065d54bd9c956b205d4af7afcfae7de7ec6edae1a10ce59c524e29432f9461c496c7cc7432738c36f2dbc9fc267f65adbec6fc314763d69843e972ced07dc236d2d5a54fea7043d6c8a76934ec31bd47537e4193dcc716cfd70a88ad80566d1bede357d5ea86e9743279617df91ba83dcd91724a3965ad6df3f58a6a614dfd683dcca9218e5ec9b086445626544dabe075a105bbbd565df86c0d6f0b760b7b685f6958233f5e1254b24e51c20391a51d9a28c9a205b01790a8b8810b0da49012cb42e4056fc84e114fa60c49e1218a2bb09e0b38b1822a473c5952050c1e1ff18b29829003307e70d2451832bcc0021b9e2859e2411086cc3806244a5608c1c48c06214d0879e1013770c193256168c1831bf8e5238a17645c00834842490a66c8414f165d7c2851658b2a96387212e58b3204e042c4224a0d65ec60074d90901184174270610345aed0a0430f93299470994d86aca00533307c3005103c1c2c15c1049132863c41868fce85e843063dcc8498cc9210a2618c9d253f44312083e848169ea538c50b1d57940f194a2dff8cc5567d9e324bc5acada31692e5f0fe90b6c891234992cca0201b7fc2424bad8713166f38bc3a73666baf24e7f35d9565379960c3d250cee2cd0cafce84591956c9cdf61f6ef0b8bbbbbbfbfc68f299335d82399e05259ba5b7958e9fe5189fd7ea0ceb905aa082b75af03f406cc97777d3bbf74eb480113477ffcd7d07f52876099a40bba7d36ff3e76b9aa6853df427fff6dca989f61ed8843bae497d5ac322f27be694f2a7ec915fbf876adf53a9e6d4a74bfeb9c34e6be8a2aa8b2df878388b0f105bda772f7f4542b3507f7ac9fd5dc00852011cde10acfcae9b04f75e9e8497ef28a87a01ef53802da5da476b7a0e94817b203b2eee35d0041c78bfef5fdeeb578e03392eecd9bed2e726dc9f400a5e1ed6eb7eebc222daf7d4aa695fb5d87dcfc67d4f17cefc7b8b0bb75a6b18a38d8c1f9f861169eeb86b34e8a2aa19ac260944ebe5d400091c9c38b9f28cdb48668e36f2551852a2627d482ba5152b7fa6d02b293b7207e28d5c61e8bb3852461d113e101950830d1073d097331ee2c41c6edb67945f83a594314a031121049b96b29c119ed55a6bedc641bba9f49a1dfed2f690e18d10c772b8f2a93cf36051091538f03a2dfc206147d65083c9153860241ead563838b71a795af87c8c83f965e04ea6adfec9f39e4d5e17b24c6539f0a72736c370863f6b0881a185aa86101859f8b030c5773916fb596a9f8ec97e7a621587988dde6fe055d9edabbb7b78b7af324877d756ccb9c6adac576bcc7edb77ec42aab295039656146c58eda3263b9e3d0c711fdd9e7e6cbddbd4c95a6b0d25a76da0aa6dda54c32535d297da530d477329447add4be9ae79ee9ea6554d5a51e9d7ad7e57ab57b9964e48707ecbb881a9b631a10b2b5b7c2dac8de56359a5d534d7bcff6d203bd61f69f8381ec6d1382c242d596e9b1ca5cf1b9da1cfd80ac6211dd2eb10ce0d4d07af7ad5347cb7ea90e193d2325386f968d64386f17cb7d6dab18d352218fbb08c97d4da3f332c772d09184f4c820817dbdf5e4424b8cc9acb6ac85763c20a2754354993fc2fdc4c60cb626007f58afb8e7507c312c3502736c1a409364dd2f3193b3b56baed034c4b721b4d1b400d80b081e57737a130801a005183957b563e9ecfc3cbb0da499428f6f3fced41b3151cc9ca77bdfe6959af62316a39ec25bd6a253a06011c2b2829297d93b974ecb292872deb1fd8d77f3bf665a146969963e40284c709337c35f377ec8ef972090cbde29a08b895cf2af40a48af2270e391b7ec6fefef0e6b50903bb677f4ddb1f13bf68a7bc53e5f875765adca8722d504cdd2a078c3527cfc97812c175123fa847b7fa5150ebd3058cf437d3fc1e8ad1554b05e78bb5fd5f8ef58157ab5b224acf02fe187840a5f34213cbfbcc5456dd34f13de1ac28c97d7c7cc1e72c0147220c8fd65623cf44f3d9b787821178fecc551abc3589910a657cdda1e06e63b18eea3896b9c0ff216f7b70615cdf84ede735109ddaaff85f74385f70bef4c58e4e1e0c21b95a0efcda14ad5abd78fd9f587b41eea28811bf2757777bf04993bed1e135ad4d185a619ae27a97b1d3d63a9469f7e3abc7bab813ea4059f9f2ddd66ac6bda8e7fbd5e967e4f146cd4a0c042af66f8d1f8fbb3700f60fdda58bfa0f5779f71f722bff07c91614c8768643b14f7ce2de9b5868f2d77177e181f75f02d573ded376e55235b4155db747f1833d3bb0cf91daaa8f2689694b3fa0913e855bfec9c5f67c682ccec352c83df910adefa3ffaa33f3a61023cea7bcda2bf6ddbf32665d524eba0a09c949a7298b9c637373391da7d05672e100efc78ee97ea95f7ee8571cbd9b84da3ffd90eece12f2f7f29c97d74530e575fe35efb0da4e9f2755dbfabda34e56c7493d5abc034d245d5f6f304182cd7bf40b86fc288163672b1b1892249b6a3de900e1d367cdd8d2df48a7b189ec15e41a0577583a157b5fbc1a359bed5ed7566e456e946ab8c3c7ad53f6bd80ebc714bf743a059ccdc7bda735c34cd6df2d7ece00b8219db63be2967cebf513dd66c96dbbef2756678dab97df1ddaf89bf8efebce561eceefe3e1b8477d839161859e7b104c807198c7d988785188a0a4e67cda386af86fe8ea55466119566517ef5aa86797c7ae55fc33206ead52a855ef9734aa857fe3722592afcecdfeca6fcddfccd610d0a72d6b53fffe5598daa06871d81b43db884211cbd62662abf69370d6f0b56863d9ac1a6aaee4a9bca4a4117220e0b3a9c23e292855ef91748d6a9587efe7cd075d1ab60f7978b7c35f5776cb5818ba5b1912816b3353bea9bde145ed3d7dfb19da905f9f341207f46dbdd8e9d99eb9bbe73538efb9bc068b98fb68226446b7a1956a7afa3af09d1f6d0911f8240fe7c37e574fd8e8217f4f143da186fe84fb007ff7c25cb96ab20d9c861cd0ed95eb33c7aacda771cd8d603b98b8dcdba418458ee5767b7efc0ca81d172dceb687e1b46c0ad6fe0e6c51b7e0d34c59bd48c871e6b3d4044784b24c1c40d256cad499fb462c15b499f54352d31cb13732a9b37cc54db4b54afe68019474382ebc6f056e771347833e555538d3759e8ab599752591da388efc6a4246777945b19de12580a745a01a53d5e3f67c34789a04714cbc645dea2eff33d6e2fa95ead787605670c9c3fbd42da81b482065ce4f374389d603895ba18638c318620869163e4c8d1a54b97eeee6194dc4474e1f518a58c31c618b28ecf983fc618638c5d643645804dc0618a0033333333b33b33734d04a6fd1ed3a7ac9b38afa05b1aa63f7db799724e753b6d26eecd07cae3bee3d8a2bcce721d68faeac37b1782bcec697b0ef4f1e3ebaa29670b4da6186f0cb0855527e5d31390840a173aa9135321d86bad35e42427b9aa3dfd7e39b95ffe901bf715f37dc7c1ab82f50abedbc809f6fb943588dc91dd45b03b3337f7f80ade39b37506592098b88aacaf3ba621e49b305b5730914580b83a85b77a33ab7e0204b98d17deaeeacc191f413465cef808a223706c0400e752497fafeeca6a7e6b5857a5ac4b4d55a3eb7c7bf7299d2d074699a33dad60ffa4a1b20594ee9dce3d89dbe186443064b9214bd3e5e386da886fbe7c36e5b03f833adc90db1d314b1244b8a19825892175a80cde5e82b78497bf07b03ce3af8d65d0f2c9f45fcaf2a7c2ef5b829ddf7d20dbffbe2fbcf26bb4735e294d39db6bfef2d2e89440f905a6440f2e512d6d5862fd9dfed0d561e9e3c081b3d50daca026410afea052eef85eaf97f59fe00f6abd4b972ed6a3c9236d0f82635eb69301b7a75cc224c999c48c2c943412db2d830c08ce3ad8feeb6aa5e17c9749a97831e2c228de784954f39cdf374982ca1c5b33d2a09ac8ab0b23fd6b41bcea76f2241bc716bfa24d7f931528a389b10ccd2de8818b922d488c843e2b46eab2160c6ec33e006deebef96f95e3bee3366ee3366edb36df42f6a14a894bb5a9f82e03794b7b060282c15b5ae851afa6f839046206620602ea959cf2ddb89402db615cea983b8da2db2265777787f17578a9855e75eceed857474a29638c1ddee895571c1c375675e60b6328ab9854b424f1c590c1e5e3228b1426263e98293e3f40383849f1bd64610ae2e3ae9c78f8ea1413966ffbc12485a90ab903a5d4082ee8c6751db751d38b0baea30d67e70824a5d409a53407a69479666c64d516ade0b46e29d8d166fec6d9a9c5d5941b6c86fae4c8ccc3978242e3cdec2092074a2916520687ae255dbf729e724e09c5276d24c2c106225f0c1946be182990f868f8381b896ec80187cff4c487aae1d3b60cf1bd6461c2e29bd144cf77c37702e2a33612fdfcc86923110e3910e9ba291f3f09a6180620bd1e800d84d784605f2282fce9b257e17b28b324c67be83822b86d051c638cb14da70eefb5558bb74d6b3612e180e4c341a2902c8d59dec248144fecc015ffa5e76318d89d1e644044ca1231c8e84188eb4e2523150e9ec08a00a3090f1517438939240856b6873f3f7d35cb3f45bf53a167a8b30ae7928753696ee9157fd7836f1a4d2b31966717a82bb30b141614159415dba823259490126a085564fb512f6f6933d82286962351c490c53584bd281806b428d173830e8e2c8185eba25e602f0aacc28822234090c24a13d74509612f2ac8f65fd40c0525ca1851b8d83e2d0d7931b4c5e8746484649464fb4f40de7200106632a8e2055f1cc132b3f73404d00db00002133fd052e4baa720ec3de9c8c1480ea018c387480a5cf7e4027b4f45b6ff9e8c4e5666915392596489ed93cc76ece463fbf433814c4b13e8f49a402798ed370d790b034b32213cf0f0021b7a9ed86b2a321d518124890637d8c118ae6b7262af29c9f65f939269cb1553942b262903b0d724846472826482a26492299980944c4148dea26162840e96d05086901bb4c05e2f49470d3c80e227072d6334e1ba9e9205c25e6fe947281646d26c0a1610b9aee9f583bd2698146dfaf9c1bbf203161b7bbda319959915db1faf2764bd21e915dd40a282091851802146135790bd1ecc7bc1073bd020081e1f6465b8aed7c45e2fc8f65f6f66db83228f8c218f70f1628bedbfdd92947558b408122a72626f6704e3ba9d1530ba2460744b3a9feec776275be29696ba5707f356ecc11640b0e28a1d845062818b05f6724539969cf8810ec8486a22735dce6805f6724709d8e1a70788285a4c29c37539241fece59238256ecb152eca154e0ae78483f2f6724248dc1224ce878d0ba322208e7eac0c89c275b71eecdd543288a28913a4259eb8e2badc8b077b39580ef67231ce75b91fa2ed0ad18665a3b259b1bdf1df4d8aed4d09dbbfbdbcb553c3102ed062c30d7e800312137b37181099c111413082079e185c778b59207b37590ca6c8020528e8628726565c7703dac1de2d689bb53ad7dda030198309172fb688606f5d0aaa4741484140deda71b204831b40e145164764a8c0de0a43810f5690455010163dae5b75b0b70a2d01450d4f7ac0c209315cb70e2de9bfd5a85a712135890ba94baa8fed1fdb55e6b025dbb0fa7218ace8a8ff6a4ada962c5a942c9a14cd496b42491a5052d29206b35accb6f64344af10512c940ab5629b1e7511151ab2dd30b3295c8270a08288904b89bd74053374e9c2031d62a0822eae4b73b097ca70c4a0062330f0c1c91049b82e4d622fbd54b3973693d94ce6f4c276dfb914348f82909244a0a5c850161d6850c30c2e99bd33e8861b6421a30761041d99c275e72c05f6ce2a488c906038f4c0881bb8ee1cc2c1de59348d5eec9d565a485cb510cbd3c736cc6db861f3058b5978d0a2858a229cd0c007d70df6caa2230468e1c2ca961ad40053e2ba12e9a7af54e2ec955ba29051a2b07ca5d091747224a12449599204e24e5a6212f3a2082117844184eb3a127b7d49c29ab057c666d82b7f98f8152696fbc8761095202b422ed44231084664898152161624e1c485027b1d4601289e687142e8074478e0ba1e3b62afcba8e0011238d032c48b2958e0ba0e6463f67ad0148762bb488fc145da8b2db66dcc6d9282848a8cacbca0ed6d2c1ae824bd42fbd89f7eb5e036ac05c415ffe5287601526cef3059c10c4882e842890f535c36d8cb4547493ff096cb4ea05c16b21c143510596def6619652fc76ab097613b4fbcc0c10f428431c4165db82ebf68b0372e49208b94241b7818a38c305c372ab1cfce1143422cb842e449112870f504baf515bd08443811c61625b2173861e4051e64e15203911bac1cb966d819e224083e3e57b63801c5d5456c3f4eff8d2e06df9d4a7368e9bbd328079fc7e5f3c6f8bc2dbe2b6796dfd382df53fa52f05da7e2bb0eb3fcb58b6f93e1bb5d64f9abec9b5309fbd9f9aa55b770666acc05d8eb59bfa92543d8042cd9c2ba0e3b588f9115ad94f3ca771b66f99dc9e7401ff7828f13e29b924bb073fe3791b0f36fe09853e94e25cb5a585f6a96d266e4bbdb0cdfb6c367c5d755f1350ac9521286bc78ce39e79c33acf1a6a50c810471f5d079414c2021e820cbcb052448cd0cae27352874cd0e7eed6539ac69027b977f130f83f8cbe5fdd106612ca62c61e4f2f009fde1e970066f8d1d908b0392f0353bfab7dfc2bb63757873b15d01d5650b02cf76787f581d8f5a3941b61bc8802a2f187275ec351037c2451425179020d18a2e416688813453317794f1a6873069c1cbd520db9d2ac5c7b684307480b96a5270200de0197279b8e3ea2277c7ad46ca663b10727515d80c40b18a0dac0cb96c8002042457d79e83ad2a9c0cb980e0508248c81544220172c556409ca7bbd33a428725d796e5e39fc14ce8e58a5f9901ed2d221c802c8fe0c26039fce156c610838dbf09f1cd2eca15ee870d08f22707140e364832a488e1e28629303f3ff48621db4f171b5f461876f7ee6ee98ad1dd9dc9603066ac59fcb1bb590be699af1f11122eb685f40d1a4df3ebfccf08bb841053ec14c2355fd5ade89a32ac51121de1e208964f027551d5fd35b8193eff7a35011ede56c5f7fe35b135c3953b8d6f8caf8496df47a8d84874448a45808d444788d81376c41cfdfe5d83d156dba6c61192b9a009253090628bcb9f840d3499400aa0e40a520025578c29f0ebf57abd5c33040ba0e4ea09f4ca7bf541f1bd8d4b9021b32da168b5f6685bfd6dab5a8f1594baffa04fa8508c7c371ecd78c858c6b213e8f3d3b066c77c3a43e6e1170cbdeaa209b08c67b359429be67d77df0f2404e29966f5427ed50c441755f746ccd6ef50b2afdf45b0dbbb7ff4ef5e474f19e3739e17398e790de0653bf7c4f83853ce26c657b9a05ea99cf4aa7ed04c25532de955fd18638c9b189fb451b39b181f8ddf45f9ae2a49d5456cd1248d00b3750421b6bea8c0b0f569bef881ad345d6ca53488d8fa9b938f3efd4b937424146dea572fbcdf91b754416e53ff8f6cb591c51cdb7fb256f205adea53f0ab41b6aa829ae57d7d9513cff3549c1231b82e5764bde78cacf7623d95974aa552a919335057e62cb65245fef348e49f423e87388e431d4d596cbdfc8c35e680798e79d98e82eceaa4cc39411dd5507d37359969e2bb2a215b554252aa11118b65ee8a659b23cb1c123630cb5700572c0be0c8b23774c5328d598b26c96d82aa883f968f681ca1216499c690e5b731147398ae8d2296e9af0d2a96ffaa8e2cffa511b34c4366990690e59822969f1b8a3938cb1ff38a39602cc7c02c7f61ebc7962a29dad4e78f2a1dc4960d992a0c5b9f067d57251bc0cbd224794b95a43a6a960d99aa8ac8b2a164d2a892bc952ab2216b56eaa86d2889ac54157155297d1d3d53a492d9fa2fa0084b6e535345deb201739ba3668900464c55515345b626c51b3b2314416989cf862ce6f06c7d1b4b9a35e3eb03a028e608c1d6b7518a3960bece207b554936640380191157f555495625640386ad364bb64ed9ec6cc884ba88ca88f9f5a34f1b40b6bec0ccb006e6a39dcf30210af34dd2071f0af4e10277b14dbeff516d93171b5b2bbf50686b0a9bc0def5c1aa8be709ea37530ed378d136f539530e871d4d922987c68b5ea5b2ccf44095c5d6242f9a85fafa2aa566f1d7576d31a5b230cac409350b654464a13824ba23b67ee7798f42b93b777ee266c7dcf9cd6bb621c51cded7e784882c6f889bc51ca6af1f23cb4b22aeeadfc963e7cc864c489dc51cdcfb60db7160f7de507f7d2f89fe6a4366eace2689cc56017461eb5f1b320124c59baa4a655125c59bfaed8536334e3eee7d304d52ccc15fbd50c6b66d636bdd421164f1a6fea549b2f5aa926cfda8aa618f1bb1ea3686a091a156a9556a955aa556a9556a955aa556a9556a951166d669771cd7cc4829259552fac46e52ad8339e2630b8608a67f45b0f16930ebeb95d3c41cd25f226ba657fe1cccac577e8af5ca59272471e53050d800a95aab56359899b74e416ee3bfc57a8524b236247175b255820bc0116ffc3970e5368ec4ccfa77923bae65779ce974e2f68e32af7883ba340c907f6958aefbab3a6d627c5b4a4606999794936a496228d5ead6bd36e336bc799b189f67afcc6be3ba6db60135ebc6cc4e41272727a11828cd7ad97e4e411d83effeab6ada94c34e7cce2de99c9286703a799e46356d6a937a267a42790d6e96594a29a58c10991840191e199fb669666f13e3ab8980669279c51c9c1dc1b6a6e4ab1152f38a39ba86c7c2cce8d3bc628ef952d62bff9392b872984fdd2d3685c415bfff49c9e47121b1f9f23631be8e86103442685ef146e625f3a2318b37de5dec94effc0dfa2bde70b784a473d26ec67b49f2dd1182ac3f8d530b4e41feda7b0f906549a9467332f56b54d3a63629a51d839ceda27c57e6250333cde0a37f4f41f23f2d3e96c1b6dd12df95794d4d9bb27bf128189be8420b069b7839d8c69b7580b8421259317a2c222222d181edeaa27c324262eb14146d5e3240d6659258973962658a58ff5310b1957ab98d7f2dc27e53ac87a726bcb02685eebdf742202b0e64178e065330b7f1ef529eab599a603cf9303274a99494deeda7d4cbb539f9b8e7fe9e82bc1533abfce91059dbeb246bd669890c71e52fbb3484b0fe34663147bfcc260387efade8f2c7c1619965c0cc51f9e4cfb832633a27e56a6262644298c9614362fd6f8d2d1657fe97c6c6ef244bd9b2393237f76242175a84e069359a26f39281356b04eb2f339391d2ad8a24e32333ba3241acc8041220192024244bd82b03452666afcc12ebafcd104c3bdace78345e4723086ac29c9399d9fb447d4e50dac69f034f4efa04041bc0bfc1d30ff1c65f9a22c0590eb5241ff7edd25e72ddb6d8fc61de6cb326fbb829a5b4a3f1645e5d0d6f13e3e3a058622fcc4ce63504c50fbb22511298f53ffd34ebc5fac3d0c007eb0f038568079fd34906336bd6fd846232b1665d9899f5bfa7201828cdaa695624528116d65f86a75bedf2bf323f0ab017468a75ef680e2759ccd1bd341cbe4894c4c8de2d967acd388a39b43f05c51cf5fdb7986928e6e88f990961ef16f3c1bec9acfff6da78628e939378d31359a72771e52feb7f0a3a059d82a4c8bce6cbbc2615a9978cc2fa574fa3e9e94c393c6082e2017c46ccccba0fb6f74f41f1c6bfc31947ddc4774f41d6bf2656f3d3ac19b3fd917b31418bcccbfacbbc9a5586d5c217251f8c8d4ca02068c67afe44ee34c24c6b1f4332cb776312ac799c00a2c8c5cf42ccd1ac85fc9aeda39d9c8434c50793b246f1b1e0ad6620be6861ecd29e67c803be7e74c1d85513c39a0c38161d20e9e2d23e03f46b78822729d0e8ca800604480f3f5ebc5c5a5813431ef5b5fbe5e8f48a1906724c067d2d842cf4aaab7c38de626658b46119b27fd7a060c3c697618ff86fa3add9f1d6ffad873980c1910b965012c51546ae1b43153ae4216d0a348b2357ecc0c61823c7385d1323d6ceab95e8b2a3a366f07d7ce93424dfaf8cb417510a66405b74434429588a9d0d3674de834e6ef1c58f9abbbb3b4b2c52f4a355e05ce1eeeeeedc48eeeeceefce8de41c45f595bbbb3bcf5ce1eeeefceedc5578d3c3c2d99bdddd9ddf9ddbaf80626646f27044e98aea587e509e3fe8156ea87243564a902560a13320a31fe8ec8a58135cf0c4aed0514d1d15961f9377fcbcb2922b2c4b32864411cbafb4a1c0872794ae707eb915b2b1e098e1831338b0a49af543c62169c58f9e6dc59ccd194e0c0e169f7510509058fea8048a48123850221ed52e8a18c8e2d115cca321718931cf15a999c222b768db35f0de8a2e36cbdbe4dfb9f71d701f5899e21c9ef1a8a4ad3595490dd5108d040001c314000020140c8985429150349c077ae40714800c85964a78509a89c328075218328618430021040000000c0181a91251100050a0572069dc4f7d9725b0ef3f1d601dd6d4a87e8156b0a12c7a39082d264fa946e46caabf0cb6cb59dd061d0da17daf0f3f77715a2cd25a090804b7fcc8c7d8753bb8fab661b1f76baf35612f2b53456bd3ccb5d883eeaba397656a05fa0913781d6e0ab3dcf088a43305619ec38d2c0623044f7cc3aca43affd0358fa3531654954d916f53561d6cded927abf70e9c97fe9da186deb79273835073150f354662cd5c007ce962751ba4d3e3ffd92be86a6c8c17d2f3a963c2fbd30497cdc65bb19e8286468306d07c5dd23d74b6c00f85265a05dd94091f7474adcb4c0215a7f74f9b3841b660b3e39d22975f56ea698a2d5a4b5d85bb4a3e6dca60a50e65ceef02b94299d4c8252fe2eeac47729c622cbd313ecb9ebcf0ff84b1a2d171eea4113001a7d713e72600d41177c6af30ea9479cf835214f4ad3110d3d691bb4a457c569887a48368465e5aa281439a4c79c4c8840de9b7e7c0d86ef9cdf513eda9bb5098602ba661d5b82a4d7702071a7bc564e3fa5bafd966f6df45f0f797bd4256b86b66450c776591a07578617e5fe50390d6d90a09207e66817c9b231767a2d4836635f434d7a80f00b5d1261494fa4949ca1c02da9eb305a015c26e0ef9a0f06cc2daa4b95bcb85f842c92e5ba844dca5ce77d0be8f6804fa44313ffe5ae5d403a9ca5e8a374319cb2dec4dfb4c7ac3e0a2c7d2d39d178e17b6e95379dce00a7091a5be2d17ae3d298318e7825d4b3ec9f7c7b6d6c3dafa70a4220108e50591837a537041c77671dd6d5298974a969c378e9864099fa9c2ddbb3138e979e88a0c51922f3eeb13460362514427a4ab64e7aa53b21c39e99d3fa820c842c1ca1e26afacedf42a245b9a42d9ba13816dd4e819dd27db587835c56980068f8e87a24cc4cf0a2ce15f5818b5d9e41e3978af576e4ded11778c176ddb7272868c40f8485c06988f68a78775bc9201f8a8df442837c59af1f8bd8651c810225d612a9f36bbd31dbf41e475ccce050511197742d1ab5dc33e3a46f375922617e81370b691b3491ae689e3ea553ac734f95494b1b3c1c9d4f3c38b59031bbd5f1ef20ee727c335ec6a508479cbf111973e2bc4210e8113bc0c9f36528395cc77ad4d4b6ee0097534b097b573b1393fa15d79cc85899e9d94d7f893cf1f76c4051d19ffbf6f57e2fb1f5352cd2bc92919ba321ab5d60704557d361da636af1f7a32a8283491e79b171cf952e82a8a7c40957040eb8d6fb9aa329cb823645007ac6ecf86c8f4cf3b2f640e0d6761dd96782294ab7d680a79a1955f4cfed2b131cc43dbe8111be17be77dcadc61e82339838f16133d4ed7a92441e91ec06015072073c997bedea65e2ed232f0191969f0d70515c6456c3b70b7411f388b88b740608dc780101342c2a4e732a910e55ce7abeb279c02d728e917562175b35a29330eeb268b9c55d2ca1c0db7e6f20a709893d5129baba82c9975c5934f6b26044d9ab51baaeb1bc118e68e343e71c7e3cae3d5391a3d64f5a64ab5cf54e7e08bf3f51974800c9c8c49e27bfd26519f10be2122ddd11cd5393f650f19e3e0c2879b55160397c94f924a4956a423f6720b0d23fa30a8e59d281df4447413d6900858007e8cce43ddb986fd81872496767ee14e191d6bd1c5e125fbcbe3f553af498da25e2e9465f13f2b425b0ca4b02f28e5a8a3f6cb14ffc225afc11deb3757e808c596ebe075e7141b6685dd79cd59249de65275cb56db19c67bbf899fb3f67ba39c636e9fa795cca550723e4d1ecc7dbff6c81fa36d031f0e6a8074a458b71f6b2f8302a719a05aaa6765c442ca914ae8741b378b2b51f6b305862853d61f9df32d1f8b141ca1cf26568ebc828966908728e4e3abf390e2474d00124f711410b1674d3fc5d11ed3c18edf3b7d6a3e561eb1f42cd589c0f9a01494eded89a25edeee59d29380f9264a4785a8bb6763f1740766d7d12e34730621a61f25fbb3c1e4d14c53a75d7fe24efb9f95ca7bd8d957e3229fba005ef13440bb5b9fbdacea4201eda6ce1ee3998e11257c0439c505738ed5f9debca6317eb71032db51c93d3c122547bdf2421e699248a98e0c6b37942c45b914ea5f51a51001c8aab3ba0ac25f075894d82aa754b82b4354a776bc703c79f43771946934471a7b99d0408d973bceadaa9366e666c6050fed70b2fdb5b13837df524a0384419ee2c96a4f70874de42c1a7b0d6b7bc2b70ccbd52df2283d2adfafc8628185515d1a46b4ddb9e56ea8f1cc68533a020533bd4f02383a6140c0b6510bef4d032fd3b84cf32f3b9983471549fa4a07488a60b086b688008e1d75f1bc00cedada21434ed1d098c5f30af42f2d84dc0511303c40a55d99080dcf5921b3142af59545ebcffc3e53e1076e0649b64fdd08439b8e714cc29efbc079cb5fd7203f7b3c74983a5ff305cb0916114b06075ebf3c5ce2830f99e5b1694887d2d0d20798e51d677a36764c1ec80050676426726fc352dca6da73dc4677ffb9fa1afab089e6c7795299dca53b43c23baa91b979979530646538ef14589824137a90f6cea9fdb5a1c9a7022a01cb06d816cca66735fd2413b850c5565570ad191ba1a570ff4fd45bf47884d046d4bec68c63107b723da959cf0bfdfbd642641f3465801e9cd55157c4a693c483917c7ca4e9923c47e1b142745bcafb691f04cc19a90635894103f38cde9dc1c76108c608b607dfeb37ac8b18e1439bd8c41268598489e7a641159d4d958b2829b0b3baaaecfdd12f3218cbf8109eb2c39519d9a1c021c472494821e5e507d6e3352c1e63ca6bd5edffa1e6316657443740fb233a15af120cd94ceb9b6f11fa7f0ba18f77ace3325bed57c6844af2d7fc923ff2131938723e6039ec1787f70e87b5a04de4eeeb2a9e8982546f744d3bcb2adb8a839dc737dfb4d17bfc1a217cad9e9be9187504a92cc0051dce02e3c9e7d3dfedd6e96f5543f41ec6e6c46ca434734c580bf7bd967bd5d37c29a3da6162db02378acd8d76b9919a8defda78656e840bf43616ed5c6d3ee788ce6075a7ebb0ac05da46dd651cb8b4ccbb89e3c5dbde2acc9b083d9c178c50376928184478302c0cf61b11f3ce0723b88b4df0b6e7727d04cd1725a06caf2b12abb4e0119956f5dc15e4a1cc4528aa022cc88c4d572adc9a211f4ced5f824c12d0d28e2afeda42aedda96308c6df04a6c221c767508c665edefb75b6b4bf25610150f5c95132537e3d9bb840f509ec64877e6217f0f3c509ea9a490209efa73cf9ac2525fe8778702deff66e0cceac3e84a07c06c9ac03734956ef64ae9157b50157aafa05d05e3314c8137f651736649dc9ba5e7aaa5088e84f101686b5f1e29371070d8557dd0b71ce9d5e6bbe19f609e279692f87423298280e2aee3eed6ac0dcde3b1644da1bb329e230083a71e0ddcc0226002f5f3b87896b0ae674e218067453f7e1d4410e2389c265d0f6a2b1909cf73ee8f87ed752133a8701dd866cbbedab6d1292abcb0d0895272c3b365f2c8191e6fd782cd9e8c1dcad5c10e6820874c86bfd7cbdd937151d0596a78c4bb5fae0d85cfc173567e3d96d36c7256d94a1441a7266d040217b1526326f07b2b9223b70b9f924507486d0e57dc12893b39f08452f0ed1cce4cee5bccda48484de03ac109e3020c490e1d5afa2803231237f39486f452549e6abc4fc8cc5239e7cd0afb0fe40132e1898f753ae37232b78cc64b23a339d3677d8eafd00fdd6657f16e7906645c0e3c553b8dbf350a38ea5b71798d6cb92126a6f35039dd56f298b52598ea7d112393208bda962093a35c7d38486d72d244de91ea0a08e036a3a97df1639237fff0ac4586c0cf3007c6136ba255848d65a6ec17ffa2c46e390d8b198a660ca822522c8c44e53e7d17fc9862795473ee8e62ee728f491d4c6b9e13d8f057d69ba55a383d7566b0dd322cfee67dd180d0492189f361b462f0ad76fb3708ea3a5080b5633e84aded623aaa99a60c802a4e14542a65ce95e4e2f2f19835a6cbae25d9137e3e98b3829b3a976c15e3fc5cdf81a56113537449b28f211095e434e7c64055bea8f89a3d4dbf08f20ef92274720b3202e179adee540d439b16fea17ec817bd0ee7513da1746e996d99f9b1187a4191b71cb1130a7ac4246081c893dc64d391062747de20a5fb9e5fcfb9d08f1ab602b99c3ad301bac69fccd3f88f2a0bf081719974a91f58e17fa1ded400dabcad8ab65df227e0922511b3a2436d0bff3c9fe965bfa5584caf359a595dfdc07a2ff9c19c435b4be288d95b975ae089b29fca30e01cec0c256b958fe23f7c5390234fd7e168d80c793306e9081e604173800d6683f58111fcaf5e495e5980395eecf32f45ab2297225d39f3d404322a1138a4a8bd91402c8c015937f582d431a43ab535d683ff15d0363de94ecf8f48a234e03a06bc936110fbfef6b0830071a2c19dafeb355509bcd12640de70e1ffe0b6595ac12125c722dd12d4d9ed07df0f7d6c8999cde8a6dd7c382778ef9445fa3d33a0c9a1b8944463d2c2c16d850f027b6408de8516c90f7f251ade105964fe4aac2fd32e70085249f40b70196d2647e789a44fe002420b6f5b4efde6678e697a5725d2a09b40821f6aba657bef24c82570cea21f9a615dd6196831058441a6d39940f2f8cb4570a33e86ac24016bb20b858bf25ff904159259a216297154e5ecc727671638d96d5c798c9278d1854d67dcb81c4b84343958d8caa74bb869a925280c73941bbe042e8ee778156e50694d8d973fd448a5d64d3bac98fb5d67adcf890172e8212cc7ba9e0b51ace11ef9734f5137483f8fd29f2576938b86e24827ac33ed6fa61c75fe5037689651106151b34496117c11aa6f78701d8292ee056ba555b4b57089e6993db4a0abd8abf6bc54f6424a5df789a3d563ae071f307f617248d6970f10281499c7ff1afc81a9a5692f1c2084fc39954dd70c326f333cff4892eea490f343b5022a9f5a6b5a06d6103c0bff359a1e10738957a58a0383e2499ab9e8c9be8be36fe2981d5b44f7a365896ba6085c5ca78f28b5748b594a623c93d69c226ec5c29a18ba759add1bfba327c2a8517ae7059fd0fbd29d5b6d232a2ea0d0b1753cf4f41882a9c9883785cb2e64c2b10ff027524403d7294659a69b49b680cb33e408016b8d15bbc0a50076056a5b252385c31aa0b7e9aa6a78615574d939558c341e46fe39b53263eb8b84fc850918f0ddceb60a1a93595eb61be3084508b77283e2b004a2abfa0c930749b9c21dc49ad62b75d3826cd93ebf7fb45a5895850addcd9a9176292ea2901c25153ef81c7b719b44a7f5fad71ca12f819daa0c8f05e3cd1a5e8400c82603fa75917ddd0e3b6f711ed130c66e718c37ea0a207372c2aadd4ccb5c3aa937293b26ba4bebdb8b15eadeecf90fec5025914224ef7f95e5bc59cb5c15a168cd482cf8a6591e4e91152e99675eb9236cb0e52ddfcae09aabf31a92472a5371c137f4dd867fdacbcfaf2e75d38ac3da5bc72e119529f828b11737c4ae4c6ee13af8d8aa673494cb2f9a7fc411450d247e64081bdc9e495253ac281398264e416e3ddfda0795ee8fc2089e216083a4e876ae162f952414f5e2cc912e64272c33aa31413f8187d322a0b1fb991927ed5f4fc96decf10f813eb68d49a70d86d0ab11914caad2f99fcc3880f6c813e658dce0a5176f9758402022b019d64e590b5458fbb870289f094382a6b349e6ec174bf59ee70ddef4c54a0e4e55775872fe2d65affad68c05d1f55c7a4b1e58101e686eaafab181f2f289648d09e4803e222cc6a58c9d8dc4db40a68e0c0c87001a91985884ba23791a326916e1a1517e49ad714f6581c9fbd22fa0e94914172fc4b264092be11f77b127e21cdeee7f7ad88c9c9b128ceee58f08c8551315e2f3d12953f4ae64d29922fd48284ad4e204c9bb5896024ea662d3e66e9c4cccc092004fa3135fff36737064981ee96f96c8390844152889f873ee74240d7e1702a669114c801ac8946aa301689156d3d1e42d269fd9eeac6fc80fc66957ce3bc91473b3048b6ad069def7dc77b937f6ef4856f662349d4c7132e0fdf8490eddd56498e8809bcc2b6046536dd2cc110fe3d2ae526fda0fd2523703558fb181d9eea848196fb80dff40eefb2de93d74bc58c80583b2aaa38ecbc6e08b7c75f9d383b94b1a6a738c5a87572ebf490c3d534a1def59e2803cfd548cdc92aa8353857b41bc31650233d7ac22d1c44348ec789c301035f13694a976d09827198b4a34d21a471e30696718f71cb23dc9f07b68273f3467856ea6aa1d5a669080b613a6720f63bd468a08fcca093c1735f76ddd4840f211ff636e1bf6b64a925098e89dda8a4ef8320f338d48c80d88fe607e3755d3537ba92173079c70cba371c0beba7eb1cb0aed17f08ca31f8f2b2114d8228cc1fdcf606c623fc25a1a76675317a220530e3bbe2ae4c7784a58de502a75f834592772096fceac580a1941c836f2a342d13d5100ca9b8cade53e504c7d0879ce36afd845bc5296586abb857865db98404ce31a6c0fe1c08775d004e0528422d269002382d003408d5b49390e8a308c28ab7c0942cdeb651a0fb1e60e0a1684565db104a8b860676d34cda0394fdb19ef42f904c1168b05c43ee3e170eaf7fe3f56d1aa5af9957eba2a5b0aaa777a45ac82674bf08f08ee5a991e480a297180a9450fc197d64feee7d86d45d751eb920b80dd2016b38d5083dfcb361ba921ea24f44ebe908cb6dfcd0adcb0f090930cd5db5ee319778a04c7ad5942839a0205e00e5782e967a103b9015e4010ac7cdd9b43f0e2738eeb2eaeb069588108fe260e5b8c5f6ec65cd4219cd012a962d53bbfb84926986b83ffcfff49efc5c602f92319334f73d586934dcd660190be5e6e45f08707f03e25c48007f6af275f2a29955cb5dbb29cea9d2cb3ec74afbd8f862ae887e4076560ed0018cdb5d210103f25a591dc034bc591d9deebae674d663989bbd14d3dcd147c967f70a4a6dc17b107edaf6ae04bd64f221651bd5122f6a98864fcffbd89d694441032b58eae7cdc43577e620c95469545a7db127892b6747f21449cbf8fa00a38bc3fb93583940f427dc2c6d06e22a409a178c1ddcc8cbba5212b07d14edfaaac9a7f71ec4b0f12836d822b2c1b49b724a88c456d7b54dad36873922a597243a4e7c6f2f417e0421c513cc4ca83044a86b76b8273440e69c10aef7a14be0b4bfb4082b431f9bd84413b27730c2c70cd3fd35e92509a8e03c8bc088a3ec4e2f8c5bb4f4a72bee97dd031bad2fb9142d526649522b5c5d97946dab3c01309a61eb7a5ffb9c5f780f78ae5d6c2e2f88e06c626ca616501e5b55884829224003f11022ccf2d8d359fdf7cb2aa7d10b823d56a0f0e5bbb4366dc3325139bd6dd935716489232e4ebf7ce8eb57d55d0a2fa5911ef9d1126fee3cd4e4afcaf5082dc0eeaa98e7d42223d3e5eb1f77fee55066ec51b573bac3b846c1be58dcede8afc88a0970ad507befed3dd10e3954d394e565730dc6768759b4fb9f05850bc1e19d3ec68267dbfe10f210d6870ab04e91ccf74b5a91be62ab98be8eaeb12c5a8aa0dd10b3bd4f570e8d04a7ad5cfcc5bb71513736b2baf1225b66eab355bb0040473f4a6cd3be3d877f0d1f83f654262be687c21ebceb8887535f0c1b9ba064d36357ea26d2b2d10b203e0ad5b9fd0cd86dc915d5b55c5734e1009549fac94267ca4647f983b95577bca85f919f843c8cd146b0cc1c01d6688e7f3bbe42a6efc5bcb511f730fb2b4d1c0b4d37aeea09284f99d9ea6686dbbce5ecbbfa3234d0b6e16d331ae8f3bdf69d54c7731e8ca36c0d6815a22e7d30d26d43e9d5f3a9ceddc64b04b550615b5c33c85981af1c25d37b96939a093b6a6ac4972a911afd9be59ea41a5a032efb8bf92ba6344ae2de470bdc25423fe8809f46a406cf52842d65aaed34463d422bf310ad4b82e348ae0a09a1eb16d46f6eb591262fd9887923bccd9bd383bd65ddd8221213425f206ee40f09f59500124675d586418fbc464dce2b133d20614b906522a195656288061fa9bc5729e56ce2bfc128930ad6f2625f7963790b3dc434bf0dea7682edd725544f541b472a9663253fc42195b33882c2edb2eb9b4126ee9f7a89d22db067a11c632df0636de12497e8262751ae72bb1bc406917966d964a889d1f82f665825281a077458ec4f94c66adb67440a9ec6557fad10cdd18bdda12973cd7474b5564260a301ab1fce48c8f01ffc99b9b9e80addef01c4e57f4f980e0a024dfb681a8e1dacd1103171e08ac0ed340977cc5a564482e7d5a66354c0be2130fda9429bdeff566f0fd53865f48e6e8f9a5642c2d72a035f0d9f2d321c0a3e19b2e54eacaaedc719e2fea78ab3873ef560145d0706fcd40264153937f8a99e67507d741a047b1728b043261289e379a8969650cea768ec0e614728824c312e2c116ecebc9906e0ef18cc8e5ab7613a71837a6c1ef830f78c51d2ba3aa0a4cefbbbba93c2fe755d1b449d93c6c6cc8ba8eed44b4f6341841fdb431d32c819dd26220f3105432c29cd48eb7135d355d52cfbc34d172ffc1c6e5833a4531f76f54038868ea4ccc3b5ff01b42120e3007bc1ad1b6ff3500ed88471a9499c174716a2c71ab22158104ccf025460f5797d854524acfc125e455611fa54f9cbae95b7e41bd374831a4bcd78aa0ccf83c1205970d3b4e73345fddbc1a725cc2c73066644e786f7e180b6737f413a3a4183251145496779caa6b243757612b96fe2e84bc6994f2ef458ea59ff91f73a6b5f4c642a0aff8599a303b77a3cb1e502d6576274174b1a0ae66c658b9f508b5246e4134787d3ee7cc24c2be887825cb9db9e9f932b0f5f6f60777a2567d445e788f8fa106d91cd036a125c04e24941e4fdff1ea5b02c9cdeb4e3fedf6b6781f45f8caffadbe4c52e1e09fb565970e5991d1f7e040d4c2ee430a4a780bd5217a8cf2b5dc8671a64f7a6a657e452b381714f951eca32530abd0d8337a3d1cd740e88e3e09730522625b6d175448246deba0106542d59e0f0bdef7b9596a7057906f72ac5e3366a81f725af7cbda4746837ae2cd87af06cba177ea3a6094012cf37f15730c4649f4170a8cbe074edef876e24c50d531a659c35e0dd80c66adc0f73de7803ffffdccb7b1ee8f3208c36c975b89b35ba4dd736942a183a05646cc18443b5dadd16d833163870b65214a9149ab02305f7c7f0400028942598da05a4110cbd8a2d2a1790f3b21ebe626e2562786972704fd45f4f5987c53e170c8df9932e1778eb21c1cce53ef3ccf98ead104ea5460cb803ce45d7e3e3accfaa038bdfd6d31b67b58453457cb55a5c76f7a43dc1a112d38448997f6864481bb466718252cbc3bc7071c44a95d6e5930b22daccd40d2216cfa0cac28a53ad74bafc751b1c3b794b119bafb6b13e2c020632e16581a5911d7002432f539c4c949e2cd530004c020a6e449e05fd5938e3632ff2912b9d5d916f728bf1e1c40812609b6be36932e955d5a4349c22583ec6fa8769efde7afbac31e4bda1ab2f31fc0f160736901c0b5cc0bcf9303653410d140b0f5e094861cc41bdd31051bbfddc58d91be3f64e77a00d5b2acb3900a2ee3b6f16c5f7c2aee0741366893fe4a067b9ef68ab5a22feb9b58f9746aae74331d5efcf8c8927ce6e83c8fb4192aeea1ac220241f598b619ff72f4656a5193f9524949f31562ff22ed51944a8b0fdd42b8e70be9c386e9099d740bd62ed07fbe7844a33011e3b9e8b2159238122cb2661cc247e7c5a945aaa17f6e545f24ffd79746dadf7602615e72cd2e629ea6908ae26cbf043668f0aed9dbc496bcbdf3450c4fb5478211b7c0121aaf69c30bc61628582d82aa64fc45900fcf1224ae1e96b18131246371cc4ba19ffc707eda73575dcc937afda36aab89239eb13d386780eac45c6f5cd0122176b8d4dce84c8f61cf3762b5c323acf1e5634640abd099b1537b19d748be2067b088e680762d49096db1dcf9bad3584712193d7fe0f03f29abd4c8f7f3760725526e6b11eaab8b66b5f1febcdfad34f5a5901ab18acecc96392074cd56a56d002f04b8a376a42c9f125de92020beb0a687cc8cb508cfe1c18b39bcfbd9ef2d08e1ab60162e743661f34c9dc5cbae64eaf153ea6731992c59ee5a0cdd084276fb533f3bcd4491f5f8822e2f1b06f0ea6d3c0001e4ec13292900831701eba23fabd09fcd0869af4b380c1bf0f4bb2aaa0efcb1319c98c9a7e62b22c083721340d1a51136a4eaa1ab2b3effdc30cc74532815f540e4f5ebf1da1d5a2761474c9c0cb8646b98eca575b1611ea29340d5eca9208df3d2df869fee2089a7233da3291d91fb9d88b81154e1535c2a5973dee9c380d5766cc025a32582b655abb4a500132cd4de01b3b9fbff821ba07b0f5a8d8baeff3a15216223f247e22a489ec44f0882fe2caa4e3a432d78310c9e4859b92de2f2641edaafdbc28731bb3df5dfe3e34f869f417833236b29a734730d8184fd9760147b87f9cedc4ef05fa420df376460249fda113823ba11f974c7c152a03b9cb82fdd3e6d6e49d39ccb8e06ce471391c8b25ba24a68f0a7de5723ac293a197f4b50217db39c63730b48495a6f3a725d498ca0c4b6062e9eb3a84d04f06e69fa87301e933414e5a242c68fa88c055fc6daae6310d2e08ba26f646a8e120262606ac707f6204de0a888a8309c26be1844725c6e60996e28e4e8262a06960fe6221277b0574a5d687726f41cb00a30ad84e70a072c797aa6beebbc2dd09aa6cda4eccb102052ba37bc8df76babae21928d2b8e72bd10cb6c36a5da297d522b57c50ccd20722ff91b2f274c22538e4836bbf38764809a0cb0da05617b59d0095f5012d773e65b283bcd5a44e451b6e132fa014134d509b0e65d3ec13a2deea1d622e9f97ed494f29f7819642871417044285d84df877bb0a4877065593910e145a4474f69ca936d727ca38bee15b5e1cc73850671b12f1d846080bd231850dfdaf17a2650ff3e0627098e3f2e134e35203bc38e8d807a74dab008aad758c07c70b162a930815899ea51443a61b7b6c8a90c589a88a82799a014c30ae5b8be837d057466844076895be57dd71b021fc01aaf4d132bf2c4ac12f3415d73e68907cd1fc1fde0c5d09cdf775702d90ea871de94436d1fffe8121d056522776a2ed6d0822f06beec28f16e3dda60c6b2ba5dc76f2b7d56f834146b8b81044defec6c1b7daa684ecbab72583df0e4428edef708975ddd0b2d6b79d80a48c3498348aa63f4d57360d13f6e4b18a540a86f9952cae5f9a356a0647f658517a531095f698809b8028fb2b9229d3da16b380549d2f1e4b955c051192d8ea91bbd7a1f9f468e71a68e8d37e8fc56b3e093c75df0d6233559695a23188a3e1aa591ed5bda1869ff34f36ff9f480c2a182b9332da5b8ffd11a09cf015613556a95d8fb7fac7587dd04aae5b76a957c1d2240ab95999922c850921066e39fda024e8eea17767d57d3e0547b327b31ec8e4fc9f61d17821209fa304687e6d7a503e539c338af38647482023c4abab6a5a77e7f3878a1af8303f131faec56e59c168a8035b2d1e2c3ec6c4b1500b656ce493d8396c0410cd40e3b3498913b79d85b0677e25cd6f420d8010d33fd5cf081de12d65b5b135f7f34e5fe30f1cd02f8f0926a49f3998d534b00c6ad40271d7acc66b89d1dbd8db06bd3aa2dcd594d91f34af869bac855be635d110fa600e51b8b127c92db40a9ed128816ea0af36e2015a67ace0ffdaf9481d20099045fa5b0b219c6567b006c414fe132160a53e808c03525454a632a7dfca9808acf0c189d9208d7bc90692a4a47c83752a3400f0a16123f5ddcd80138ab7803de1c6c0769f8325ecd107ca3d00661f8a1d80adbb02540a8ac73302c4419d4988151e174359b6068f12df5dc1bffb11f9176e38eeda0bdd1789c574c06856d0217cbfb83f8fb46c149785f43e92801a7e9695ca2001a6841dd4918c745977025ccf545e26c38c6465292a3d0470784801264d67d6ce0af9880ac4aaf5a5ba2a37d1173f7a22448aa0309b8380a10519c2111f5cfa3140896e398d914d31d6d902894005422872741418bb4618793b4126fdc86915b4e959e6276636f25bfc754ea84c8bcfc16ab91081dd4aee3696a187aeca3e40811bb1aa39e1d9a6886b383c4cacb0b365acbb20521707ca1b7405a48658812e899237c24043adae4f8b8a9c70b7089c279df7d0c629a5cc36d32b123c8815724e3698f3e535e194b7b79c603cf9a5702dd8b80badef76f4b18844deebf95854b9e5d9f2d698e04afe0300bd02c065d1667312f29d4d36c1cbcc4e7150f78291354980add5a02b2fa98f028f275967333d320dc3427d804626170975765ff4ab9069251fcdef7f7317a8dde8a044b38b8a710f04d51f37560120ad2511cb573d1493510fde11816b10b2a34f85900f480b2ea563cdd909511c9853747b74f69d7a46550a5465f776249f832dbb4ae1b2c509d8fa4596aa675bcc46bef2e4d119de9994a69d05265a1fb315d49e4915fdcfd17958064dc4a7cc1d0cbe27043e34127dee9931ec56462c6937ee7178836378f147f1aefd41710f3cbd9c270758e916ca4c78d32eeebed854db0ed81f7c11ad06237513f451a53e41da9186ea6f34f99ea4a013efdf9caa7aad18aa3769a404982f0f5b82692e792f741584f20d2d8545b7b2cd40f3136f6fb3d08634301753103495ade273b88f6c04f9ecfa19e4baa4090cee229ec57b1c6d66147dd39f49e31db3514a95133a4d1fbd758f952061d1fdd6e396403cd62ac45aa40c5dc991321fc004e1819285e8ef466d64f63fa1057a7b97ec64bd19c0a56449aff7256096db42e91f26a9b49478202803a7ea93dd38c3257206e4a39113dd80bc81f0bb2759c1ef0c4030c62c83dcfee6db13a1b710f02efab73fe56e6fe488c0f1d48c90deae253871b0b30b5ba5440250ee83a81184f4dbc36d4da44d68556ed3d923563185e7bb6d22d7b2fb02bdeb6ee79255660d4bcf604ef97295318863db2a012427ba35c07539e7e7880be6410a236c7515530228caed8b8a8eb42799dcd492918ff6c6b21d14b8729c9cb3d4e4c22c038090b7f5a06746f403c336bdb5d564b0a8a095fe5de0adc99db3237ec540debc26d60bd69cfd12cbd217009ddfb8d5e46f1307294cb0a777f6c7145f73e3b6491181148e3f92949474321ab366b6fceb0f869bf9fba6da71181b0b6f0a77a5259da8868c605abf917697029893adc0933c331f59b5d02c10e3b2307766c03cd001afb6ea8d679ae8854ae871c7f0c117ec2fb5c13ba57d7ee4515cd1789da4fa2edef24cdff559574e6e2a05515d37eed793b0b9cc2a30ffba9d68f8d907e536ba560726e97d60de499b00e0677a6fcce38e524d596911faaf05f3b822813acc0f1c31fcd0eb1c7f49d951c4c2cb0e7813d63e54023e05c14ac6cbbd3248d33ef002c17459439e830a0346f831e1df73481e0720a3e4b721155df62ac87cf40b9bf4cc32caa0aa13cd1d2fd931053d6829d834b91a80eb922af49fadab96c3bed74c572bdfe6f4a318266746f0949dbc6e70b581d165558bf007e4b4cb6100d552be105fa6697cb867574623601394f7a6373347b0df8e74afd9896cdb9de25bedad1ba40fc4707d62de0137e89a76895c1b1855d10c8a2c46a83a768f8b2cb7f10f1a0226f56edf810bad04f7df1615b36f1fcdc9ae689a494f1ff4351064da241081520557b4aeb358a3cf8ace418b50d5bfa0bea99d57b4b53420b8261059e985027645eef32b1b2ab8da866dca26adeeb58056cdbcd73bc8c182c5c0c8310c35041ef8aa1d9e209a0f37e2d1ea5fb8e12381b2503914e8c53311a13ca8c310addda00d9abb3dbfca937e04a9cf508c53f6f6154c858359f724470330bf6768658244aa4ff6f154823a2a5a411594cfb7832c3d6d175c6ba5cedd98d785e5b361e635f89d74edda80ebc9164274631ffdc4a5cd0d6717c382677f16ebeb2132426b4ba4b2d5cb3afd791d171f59dcee488f553d7ad9e02a03b391b0e069ffc9394b9290a32d5708684f18b854ca286c543df329fe24a081b1be76d99bcad809f8c320342f86369ca098db82e5ffa58a9e43114f7c68028922e36e309343f833457f8a8e181d56284410bce656291fa17bdefa4158b2493cd3be20bd145cd8848041251aaa14d05f24bbab82ea9b9163aa75eb4b878c86935f6b29769f6e26cfea2cc4526e143075ba6cf5bf08e75b440910dfce978e6b63029f662e1253b8a19ca4c8dbaf3ece09fba849113a6700effb954ab5b26e655219f534f87d0822d7f26a5208e003fb4be11b132b26b3a288f5d8db3ec7c4c3f906d8e616666462136052899193734263d3033831b1af45317bbfa8d1c1bf18901e242b2eda4119927ec4c34f6ff58440faefa4da500b1aad1371c3cea0d7f57325b6568a60cb0dc787e8c3842670925a532115061dd90809cef9290f0a02729bf81fd0f4290c54afd59b8933aeb9448c8c84e2d02859dac61ec5f0c8240bb03ca639048a6780ccb08e8374404bf420672351b5fdc09e8ac3929038732516b14438c55857ab848df2e8909ad114e4879b654cdb99c8631025dfe68f5d20e7302bdee7d1add58c37631552821913cf71c4b7724e8bb127721ce0ba05e2dd8070b8d0bef4150f929a0735cb02a379a550f8246920ac9560d11c4c1106a980ef4f3a1f56bc067d78b2c784dbc919df48551094a91352297d01e8498940091f156f652326887c8e6029f07eb8de55f3a48208bbd1b06659586a910ba70d524ba730ff0dd3ccf19abe98d1979fe81ba03354a1155645d215519ef62644f24bef9603ea61cde5d2b34a7f3e79938485bfcdb0736995d104002de67b274cecbae46e3ad06d1d74b0f18fc608ff1aa08ff4ecb9fd013aa10e3ee898ed305feb56b7fa7cada755cee098f158c6d524c3e958ed511f761f894c8b8276f87afc7aff73ded42f740be346048dcd34c655cdf81109c3de542bcb8045d1b241a8596eb031ea1bf19cfc48db719c48d0adbac1c44718353fc22e5dab003e6076e281eae87b82a77ea4bb2ffbad8e6a7a71108e3e2b3483d189f830ded4bbbccb2d30aa8c47486f2c3284ff4008750cd869aa711aaa0f23f839c404331c7c232edb84a02b7c3d4cb5448a930203ab09b123eb6f0a4a63aa78aeb0ed9218d602014a336409ea629a06b5176fdbeda0540cfaac6d70589a82b39ff7700928044df254c6f301a3c15c97f2554b0b582af08114182d030ec911073cd4eed81bfcc5b11f25e45d9aad9cfe21bfa70182373a0ff8b62589aa5d64759b031becf3e247b66bc89f452a49b2bb2c4d153bd0a355fdba443a4fdabb54a84e71020084036f571352ba0e5d8c04ac33a9094490a6366ebf6659177661486921bf53e310459889549f9b417c296393e9a0d8f2c7cc1d97ca13712aa9137f85cc3d9114c990c121775d20adb3e0a3b0668a21711143133d8a7cfa8dd571e68b21f48c273bfe79a91df843a67148cba24875991e6af4097048d6ffbeb9269f916fc3b59aea984993a18e26c582b42530ce5474fcbda039e2f8c113b9c2ee0aece07436bebb27954df78b9a56c0dce872dc7ffeba927790ff18627da03dc6f9038e34a1321ecbd183fc6cdf67c26b1d9dc9024397e4912e84cdf7b5d6d3ac44ee20242539a56943d918c5c544c92ccca2d555422b45eda29d60bfb12a11cbdcbf2d3f7b6df41e28bb490d2a39f04528c4d3be5f1d69a63fa7050ee9b89a3a8709b40ab90efeafa0a11e8c769333f7dc8848ab78577c3685d4ce4d00de753f8a95e5971ba2a228a30d6a6e0555e7b2dabdf43ec59004f0bb20f8cf41f7f42efb1b31a4b65769da2177a727a61d18185f550a165534bdd83e68e4840ae8defbe239fb83b4fa64c07bc7d023820bedf8cc2556ec07a8923de99465a7f1961516cb205f85899a10aaf225ee2fe0446f73fc06b67e50d8d23a3f5f1e55c93b3cf7ad30722b95bf1e8c94c90304e5dfa0b095c8cfe6b5626c71d22ab1c27022ed48a6e4d5576cba988ecd557f9cb1e531bcfb40874b10523053418704f714aef0a66a9458889f797d1462780fd246e0f45cee0bcba109acc401961afe910514ba4199e8aa8ac33fa8ca1ec16f98766197d568a9bcc9abd0829ed640998b8a94af77c6ab4dc5a4c1b3c605621be29c812ae16e6abb99a75341bd7aa71893fd640e91cbb0cc159eaaa9f86192bbb5773c4c797988c6fd57028e9e842be3757d1ecc16ec1a116b603283456bb9b9fb6f2642076116b525cc0a7cddffa60a62232f2aee9fa899ca9f9087cab677aaf513038c9896f95649229a4ae736ca9642823a9a0b821e6f791858db44e2e4b1683ccd9e426d5833d3606a01f6e6a9376adc30d3d11b1b972fe9740a0d0455a6f0333ea98e6948235ce4046d00a8a863c5c36dc1e9eb845ed0cb164704067f99784a928daa3317b23c60e06f637cebdfb6ed9786416ac88a09ff636d2060da53540897c850d73eb9353b45e33e2055cce5d261634a076e914da9e59f1d78ab42bf5066beea7190d78297cbae8880a554c1c552ccde155e1222fb88d24dab2f8a180400ce58d3b37c1388e4b6c661126a91d1df966e07cb36b147925b17ae3882da923bfe745270f5f00886c282aeddcf097aba28adb8fa6ad586cac982e0d9484f6dd59beefc1969850edddb5a477a8f2841bcf7054c300787cc73b49dc635c6831d13f16236a152f68e6dcc15c31f3d4acbb9360c555793b307c703fb2656ad064ca85711ab90d78af5b70ba121f5cac6781b88ab800b996308d2a865348a139fffa7ccaeadf0022db3e3c7454010428476c22729a8c3e97925375c639fba9024f883a371c27b88dac91809fa3e3dd44c1c2a201ea7c4a655da4de43edc6114acfab9d503ebf2c2ad1880daa8d0f0e54be8b496100839ead693be880b01a6a12ace7afd06d2672430b0134b323d5dcf02c1c3458ba15650ede4cad77c0cd14a7ae364ab369a9b13410f5dce320af5ddaf0893ac57686259456ebf684d4cb78957d30ec007840ae8160fc3ec2a82faf769957d294687497fbe9eec46b6a99605c850b8ad207b82c3c56bd3150f12b8f77822d2b9ed0e1f6b402e7fb2d144e18175f18483839d14f8f8861706e5f05c4dbaa352e712503081e992a3016ea391709615603868a559ade6dd508abd79517559f2c43dca21b4da6a354091823fac33e09cd4fc0b22cb0287dfa728d509e2e05baa25a3fed32a08b070f9371ffd7240777abc0f694f541b080ccad628bfa59f05039b8d523d8c1f3edc6a75ced16e25a819bedb841a14806d62ec00b754d137d8fbd57dff0e7bbda60680024cd0a081b280fa43f44caefadec44f1bd8cabb60a86b9837e2231e43de5a46d80e14d41894fd43669d988432a4a959ea0c33dd8fe00762d31ac8ea5f9449f638dfab341c6a8cc439dc5f003d3a9820035a81b4b63bbe84b8474a9d3e17c4321e9b43491cc383234f0f2dbd6a5ed9febc24b59f0a6e01eca29514c61d13f26e867bf8a370c8b65ba98a70f49a335a3142d99c253b278c8fb3759ebf6a38e2a8dc2cef2a952be94f68630f80910cbf1ba04d2a3c13d04fd34d1ee43ce5e46c34115b58cf4099ab7182e28d89b900a82efacb1c3bcf7a7c36840dc69dd74e0f1aa2f7b010f3ba1a20b0e3fb7102247c08a900627eb10107c3e48fd7a355f8467a05f4808d12384cd8144e155f32a5b3326d6d996dfaa1198463e928eaa0e40ba8237b883bc16c6915fccda993d2d0b9795682b5f3cf7e4557b55fc50f7c09d00b39a54d08b277b8c4a4fa2c45d08200b4a73608d1addc361f49c00ab9c229fbd914e9a0d98bc3af0ed7905d6666bd78dfb345037c0099770624a06ded38bc19542b647c3f79748051a601ce416da49423c38bec9e380320ae16155d8064d456b209044eb7ab43c80faa34297de5da4e693c3cf23371b80b717ac5805551082b97aa056dced93c2f594ffad03df54644437e06b4234a08432b4dfd0be71d6f6310b847b025e80ed35b83b62b0fd758cdfdf81bb841d534ee16a44b2e99968a895d5b9550bfe23c24a180bede87b8a4741a687477b323ecf1c507a28de7223fa14e6eb01009d82a5dc685abe1d65857c7f1ad8cd500e80577b8c340fb8c208e273c07a4662788b6a6ab3dcbbbc5f87723f23630bec0b47e9e5ccf4917fa810a7f5f99c9c20656cacc519d27ffcee57864377db457ead9261c653679c09ce09984e96cdf2ec85385f9c5491c712e848dc529f12fc8228d7dc96957f05c739407d7e7772b8452b97d0f3e5a2befb764b3e32bab10b1ef2ea0b4d5d4222ffc6e69615638cedf1d735981f99e71420ad7df22fcd9de8270f401e61038675ef7247afe3558c0a991892560752e83e149b2f2ebbb0dbed1bc1e0e35febe5b175dffc0cd1d45fb2b430585b53b485104eee0a939e4a92b8d817774d1dd9a4d01e792ef6d68733c6626c0d16b7cbccaa235a65844256bf28ef140399abb18831c526881536352092738da265b36bbaccf21158a2abbcc0c90c99e00283d0063231730053fe6783330e27b10e9492ef4f046a6c186eab92c858ec61707b37b5f6986a5285fd3b9c7daf70b058c125c0afefc6c84e3d334a3292d6aee62e9569c1dbae6de590453303d28894d08eb3f34a890e39e25352fd0f6bedcd8c3c9346b307390fc40c8d5503093782e2aaac3f7add60be6c69e8f0c9d40f459a4b6e73d8c893b091e84d3b1a693e94d63d3a9ace9e3c4a14f4011a223eb4311f738bbb239f1ede163d44cae21492cdc7c2882e3d34c22aa5a122c712d46143a4e7c474fce4e8c3d931295d91c3a9899aa6a3e96a50427eca8e7f342da41f34abc4c04c280f90f6b2c3a4ea182bbc55322a23228a3a184100728324782e1f80d19e23070b01cbe0cc4c61297cad7d46f88670edd9c0062984b1bcc11e1bee3f9b49e718566d8c7a2fa920d5b25c2b9b9eeca9338a30e326081594ba5b81331452966100b551209fc52917068270a4227fc66df5e6ff4f75a0846165b5f2c31a620e24283ef1d4d3b44c002300c26dac51562f4e9800b8a8e863326c5ba4ef9a6c7bf140c6ce1cf6980902206518d0018dc343949d03c9794da0dc98096dbcc040a49117010542bf1048927871de3ea23a2df9b54f7df0a51e39148b856af9e1a74340b9c941429edddd4bcca475492f70f63b824bd3378611e82fb3c88959bb1810724899a07f66654cf4660eeb4edbf180194304540c8aad1a4bf3e82b7aa2b4219d6604f1d27326ab1504ea2af00bf6c05a510f2459e09fbf714590fc8b28bfd652c094088c0aaf65332358de8a8acbe08058fb9bb791dfd70684b0ee5fab6641bec6e5ac376718bc887e089d72be006cd97701e6d6057759279068346f79ec12eb31d913ebbd441cb3d2679741235144790ee6e5cd6cd04784bdb2e7f01f1f0b73354d8d8b07b5734b91851b49f88fc5651e9e4d3672e9c18d07a0f18be2c91bb3d3cf3aa2e06f90bb2dcbd0cf9eefcbc509aaea80206797d072cf3ff76cf65d80c5c666063407ceeed21273db9f69320f483683805f2fd90d7f6289ef6e53ad3cd080f927d7dffaa33aa9273ab540fb4b0147cfbd3a9a60c8ef79dd597348e6c896e70239b8618052281521dee749ecf45337117b3d3ac9941186f8464ae96952262da9fc5685697ea46fe20eb5697c64a733dfb2c233e81c0952ec80aa7502ac6005017d4dc0425a2c502b632e7e7f15f495e417c2ebd81dd10eec3dd03f826a98babe2f6a3405a063e2b0cb61d12b195a60aec98362492811ba0180bdb8686f3bc4335ecf60d409eeae9c03901d54771ecc5f225051aa9e0ab1fdf9f26425ec11c2fb44befc0622747f29b8857430f0f9586aa2b8ef0002f86aaaa96fe3e0182910701c90d6b1081700a2df07fee8ea010472575afb508807fe22aa1feafd43e1673300ff4830457926b6ec552e8416165ea3f4f2925bbd3e43556be05f602d3fe2f155c16513cac37f99fab5ddacc2b5b722506abfb11b8f4f2c0423b65245ce97f5d9ecb2154a4a6dbe8089d7e58086ccf1b81ed4cc29c0eee34d541924fc8fb80f3beb30f34a389de3d3e1ce75755d59f01b189bab497519f2e005e3277a2fd8a4fbfd21c1fc4476d46760b9be285d0b1d20e0a7fd55ada5a19603f4810b56ce3af6a69982567d4766e3893ec2cbce19be921af9afb9edbe724d8c096f620cce3745aea6f554569a3a8f4b8ae6551b25e763a9020774fe8d2e1c8440c51aeaece52e6f6855e6dae4f8ab199d6565ef878c8f17aac09f443b1d7bd4582c4fd4f9e74301418b1ca34414d79fd712fef6f8e4a2ab2279b4a8e6c7c1138b10d12938fef0f1435c86cc2b4f79acf861b676fbbe0057b631fe9d6d5efaa0aa5bc30321b5bd1682f619f506eff7d55e00c6e187a0dfcc53f52d787d51646fb07a45f19e9cd49f3eb6b987e9cf4a9be86607e142f3ea67c9db4597eaaea54872594c340728bbddd853510aa95af19b3d95c8e30c366de4718c373ec930d2c212c892b1650a0e48c0c50bc07298b063a5d4c97a998cf13ca60fb733e374555130680e23e6561b1d127f5cf0ac8979c3562bcd5b2d28693c3c01d61288c89a7bf40369f5032b749734ed35fd83f41585bd8c87d5a925e8a49b66962e460238b234acfb52c9f16d4df3410a432dfe240619a782b44d00ba56459c7053e48528508258f4005133f12b2b64e745f954752dd3dcdfd4c2c84d01892c841393300912952e339c5e8e4a1065e8f4b3e4d69acb2ebd957fd915875df7c463557b7b03a0cffb16531813390dc289d2dbba97542cedff929e08bbb7a6e2855769244c1698f6091c536c7d6c178097baf9671dde41c18c1632c5f654c3bfb6daca1032c84f22e8d4f9f69f000cbe7e0aa164740017748790f0f21e977e389986361379d43b7a7404e0d067ee75e7d1a27c52b1cd94f37df486b8fd8964b3ebf518efd8a3ba23c27f5a312113904de8c85caaa301c271c44e853abdd172076173393a89ca275176dccab40ffbe31e133af2a37d156ad9c695c200af7889d487a8dba091cfe474ab33941f1921d9060462f1aadf4763018b25b83cc75365a3b8399ec35202ec6b1e5d8a8532e19448e6dba316163f7b44d0ed7b87019c9bdd114bcba96e05c0eb02ab8946e8915cd9165ba1f8b95ddbd1c9cced53366a81164dd79a6da7d35c3a8c131c72ff4e3cf292aa4b36a81594fe900859ed1221203e4203ee37f62ba1ce9cc724bf3e38d9dae3d5caaaa1981a909fa3503a57fa58bb7e340f61e1815d4a0999ddf61c7113cd7244a18831df4942aad2949ec3081a4589155e271aa225dcc8fed86bce6da5827faf2921c8ceb65621511bd042515954fb0971f5802ba121bd7eff50af1b159771bcdd01e8caa6987cfa77f61533e015b178bbcaaaa76863868de92c00eb06b8e44a5c9a5eaaf0a078f17cf518f2a5e4eb5d6dc71cbf27d7d6f8f80c77ce62189d3f59801be976f09f8117ddad5a48228613d0c812f8d08def88a03fe3e03f306be0e10a513dda0cefc5368a7f8940d6ae808844d408aacdc2cba4175b3ec2a19dd9d20de35e70d8d0cbab44280c570ceaf9c0c58f988e22b6a4de2e4d56a80532fc36a564ef1277d486edbc7b4f3c0103b686b8e19e827ede7a5f6fa59d5a85059f222861259c410d19340c80291c5277683275c4becebe2eead3aac86aae3588d202399299e1be192f2a97777088b6b9161d77a8a894a8b22bcf390f7273c031f86fec3f65b2feafe4c1ed1a36beb32e53c3d20435fbdb354396c99a8a169903263ce5e034ddbded0ffdddc214d4acd7224609a6c56fa7a8910aeac920906006a8d52a3fefb661f5afb14a553d48f64769e7622793ffa248037d9442020ec14692e6d1b2980f48588b059a80965cccd3b98d13e96c4f9ab8042c73ff2c9dde5803a2decba8a4415676402617a4a899c5f928733cd945e3be7ffcf032a508184b0e83f528fd0228b921c1377f298367acf26c48193c3d08a6528c177e5d5aa435fa91fa7c029496ec0bbb0840387af0422f75a340610791b528af5fe0abba6ad87e52643827279310c51894bd771d94dcbaa66534ff044e5ffac4dfd0f859245e383def16d59ed6fe2eacab562cdf2bca3959482087c15219c632b27e678f6c36611cdec02a52a4118fbb3ec26a0eba813705eba8ae8c88765591e7090a0863857a589846b7fecfac4bd98b53cc47b556fe52308ef63322892435f6f9509248f90ac9a072da95dd2f53100d179266237bfcaad2324e6b02b03c6e61d808650b3d8acb8f73bfafb3d3b20b0036ab2481068483bca0f0578908507e9ebc910e97a50c22cbff3377d378956defe7bc0e564f00388d9704079d7169dab1c5c336b19641cf031d97be7fd90892b846a00c846000fa0dfe8a65a4860519681d0e81fcfe7c3e89f0fe3a49506d076b12df610257580bbd78bb0b9baa5a142822a0b135ae9da204c54e558d1e4de0c102baf77d0a61792120bf51bff1f5e595275c05fbcb34dacd017f34674c8959d14cff04183ba05a479ae02d56cb316307a630f61d890e801e7861a140746d8fa6c483a98db70f0714f19a89721c3024ae9c6f8f10c59c53d74b955c206e157d0d7d3942abadf08bc8dc8ab445398fd3bae5550cab979fbf68f71828d5828417c344ce7be199cefd25197f03154831e305a106e54c6549b36534f6f2cd26c287efb720a86d588e028cdbd4a973883a04db1bffdd34f9fe64013d04be9dca9ee30394ecfe396e782de78f54d809d76466357e8f0ac580f026d838dea83a416a690d99f901879098ac5d207aa1ce6c351129a6efaeb34a3cbd318a4b1b2417c70f8e101494a2c8ff625ff3efa4d032d81eac30a0bed9c45abc6eb068c4e469a899e1d58e79ff5b05808b67d46bfcebe208fa0e2614849e874aa3fe1d191f7b6f2cdbfcf21031e1cea1d5fc9061fcdaf6a5c7ea8fa640ea2b2fc998939157962618c035de1f733a4589853fcc8ab6ec950d9fb3eaa7506ca8f38d3d37b02a371390cfb687aa745d6ec40fd597fdf868bf0dd810d22ffa389fd6ac1a379ec60557139e75e1266440b27c3b3a80e5859134980d38753b3f0279efdd19d26048239cef6d6728392796660f1852859c2ee87f1c91574eb07113cf1786cb7b0f3abc9c5ef3f220539e9acc789afb4f9198e08490c477e92199c890333cd43a0d88383b6d8f39b85d4d5937b0afcee40232f42daae264332143dd762fbfa747c608acbb46328a2651c63ea0ca691a7db13a290441c55f9beee7b034971818feeaa3e493ff6f938579487c67d5aa75fc58785fb8dfaef3f565eaf4b43c4bde4d9196de194299328b1a89fad5d0fd2d76e7bc569a97712f8700358226808c3760010891e6ea7ad89d41596acfbebebc4f94b1b6fc389dd585d809f4ba57a4ef46ad3db434c81c24843a0d377345648a3be486ada1cfbe283dd0f74b7507fb1907e86f46d306912614bdb1890280ff881eb6531e5d65449c383286a2baff768eb0c3d7dbbf23ae66453b86b09219f0b9f185255ddd7d971fd8575152cb1866dabe842058562dd3a9206203acaff7cf87cd680454c05e91d496355cc19183272f1e0ae4e61f93ce09c316c9aa933e958a9832564fa9125e39f58be29bff1f4f6ce2147c348cf03717c20e813c43a20d70f4b3c88d057492ba0dcead8fa1427803046f4dfb24fb50205af1d3e7298ac378223f96b2534943bf7c7def096ff46df5c172cdcc9823a150cbe5f04b468c2dfeb13ca87ec49bc29094a61702e3dc4f467c0f536713f031495798b118110d13015116368d2f1ba57c4bd2824abbebfb54ed08970edea2782a70124075d2199974677399c8686f148fbd9512c992a8110f848acf6fc2242e52cfd4fbc4106227ead6a465ea735b71e5997efa0c1f38f20d5871de70264f3d629ed8f953039253ea2359a9167b8ef33c71e23ec39089b02e984bcdd7b5ba192aa09a91adb611599fd83a64dcabe5195c721a865ffc8d846a1899a26cae4e147553cf88fe0605f930598ac7d7a9dbbdb66e531712e01b3d83c581ebcad76e95cb40b475dfc6e6cfa63085c2af59401056f79cc0bc3a6a739e27e4aeb8dacdc4a999e7aaf989cedef03a8c195ebadbe25069d08594ae265570b1de449b86c2a7119655595af8be1e977d5c4fc3f2e80e5eee9d99f879ea0c1519bf594d922d51133a2f61160407d9c5f718f5c59842ff201b2e74d1f9bbe701ca09ef3c55ab6b7f9ac49644a65232cd36156246aca6dc672650637d90e7a1b7ab7e152387d7aa2954087b8c214ee07c8c89a8bf45f8543cad7d1bf893e9111ce93830ecff05858e906cec06864d0793d2a01595792488e08d9aceba3aa0c4a75f6d3b630788a80a5dc43946a00138aa9469d148b2c59d8a945cc0dd9cb37d27217f852bac3a3f48619c56b3476a86b6b371664f3ee0cfca2d1d353242ca5cbe4c16b6d846042d59b06b461dd21cc3f06162c43e545e29154a864fa20d8d587706183378e7e53be9d736ce904c6a387fff8e6669d92745c35ca470fe850d07d308f438331696091bce44049ceaf38b66a0062f3ed1a0ef72575da1057c9bf74b093f430035dfe75d0d334f03f93f43aeee341039b474c8b78b13a935a447f1d4552779524f6bc3d6aae4a23cd1d0c42b54f1abac61abc98415b2942fee70fdfd42b81406260a9a3b6a1ae2416d3805e4a690f6273c556e173ef5633565dc947be62d26b8f8635adaa8642cabeb87dac9331cb637bd1d2a12bf5f447de1ca27d08536270b0af49122274e75aa34015c7b18318537a22e4a36446ee47332df2878390357cf0f05e041c33b99d5aaf218f362857fac3e1e7cafedfdf5c2de26eff28c570efdccc808ed1ca94ca14edafca67b00942de760687e41c52ab13e98035fec13eabdef12394bac30fbf025b7e0d53f50dc5a235aeca5baf97713ba340d5f6ca2fc1f78ff228cdb369e38f6603af50bbfca93c8dd5c705bd2b8e61b6e3dfbc61441d7a1822321e88526ad0887315e4af7cae1aad49e6dd6268299415429c42d69ed99a5ed1814401a061eaa3f210188336eead95aeeed364fb2e4996ef66c146ee9a1463932598e544809352715cd0f38e63e8f6b871110ab6a3c5b7c7fe9c6ee2a8f6688cd708ea8bd53504263d8b8f02cf5218384c115b8c053a090fc08859fc7b6674f4169ee256af88d9f011b2c8d6f61b4fa825db575050a1f78d654c9a0b7d9963a28859a76af13873e27bbe95ff39a9d99da8dde88c3716e7270cedf5eb0b2d14d33594849b652e668a65825d04d5b0205eac26be60e44b739c7bd1ec3826934318912e50443f1ace79351dab8c7842f204a738aafd682b90acf592438ba62d6b57a1b963a8f8cf6d2c633f3fc23869a46c739d22e9bf669347615da4ffe2c85614b2dc0d854b3fa3873fe2b28faeef44578009ac5e1b871e641327293435936d84940549bcb1216e8822f236f0e95fc7881398e788fe75af8a99065383eb206e4cf8f5771c432bbc51e9e2cfa16ddedb28bb203270075638c281d5b0bed55e8f36007e31d6ac5289da03aca8da0df4c573a4876609524e1473f7a83028548b23f46b6259598a27c8e3124595abe8320a0d43a8e8d1a5ecef51547bb0332948c1a7653514c6f5838ac4186eab660e9974fb035aecc026d68db9052aa9816ef55ad873712c9d6015b3d0630c785d48423747636c7255a227044e381540f196bf782dcc56e22c6e04ac77bcead34d4a381d5ef66b7ef48d68b9e46a4647df0c7b9260ff41f46e30248173cbd21375916b3c56a501d467679464aa4885dee20510860a72e56d1c597280303512857410400e393f8285fa5293f9030ce2fdb742da6ad996e66477b88d3431408e4b0e41110030ee1d52a395d3169aed651f04e22fdce79e7b5a2106be578fe4867157697dc3c87df6b0526e04827d0d8a4529d7b6629ca9f88ec772a06fd1b663475c5baffa55d2deb82fadda56bb64c87cc81094e88460948fabf404587071b7a89d65e4bf81181556a13a384e3c72510ffa21965bf4db1c21821bd6dc5a84a01a4cefcfe1f3e25a414842ea661f3d644a080f4bddfae69c17843c22b673af955ef691b072a773d8feeb02a6f909bdf90c55ecedff36e63f8ffd9f2fa8a33557694d10a4a9afca37856075da0cef5dae559d2641ac824f4fec7c749434c518758ffabeb874d558ac1ef5ad1910f60a12a5fe5d8f3a7ec3768074d12ec5838514be58be3b68fc3d4dcc887e7616c586c06b258d8e62218f47a1a190a0e2a26f44f25408693a73cfcf28f4ff988ef681777e228fea8c5485224852fbaa6f80e2770c9d6bc53516c91bd0287561ee5aea989cd5e4eaf99e73094fbbf4a506acae519a458f230a7381049c29d34e3f17562a119ab4b4e5ff4a16510d0ceed9a5296a05d7e26f4dbc991f6bae01a0f5df0eb60c5d5c21a1f9d6423eb6bf164ae20610e37a6d7c3c26df7d708f766b9de717a05cb1d04c35dda5149f70bd1a8d4a0c75847522ec7f8208536be9aa2aea80ad9733d448f34b36cb769821fad0c603582c404ccd2901340ee5a43d2165edc69574466b2aa2fffbba5492c339d0a36b52d4b1a1871fdfd241aba32c364e7dfa9c1bfec858b6141f6260d9d1562b658f5010ad869335fc2f84341ff0d69cf9342a5c620ab830889081063c2d6a2f7fc1b27cb7e41bc7ce280c5ba195b2652786bfecf8bb8a37ff98a02e232b1e616e7a431fd7f17a241c61e15943f7d557b876b3ec822f83c565c1e2374c29238ec14353d25d58874acd1df864c8b3bd301b3e98da03d241906c11d5e35095139f06fea149dd2a6a450aa48aa4d953c4ce79b454d61b52a8cc7d51ff1794564e8be2d5ad47f2a3aa6494c864f9a4bc683ba43647b7e7322801516a778024b69efd97182af68c2b1e9ed17766b50f9720d734c77db80bd8449753b37974b3701b90cd9b39118c482e9c140635cd6e0ed968c26b4dede60e38ef3c1ede1ed6187e7acf9df84c08d44ccc385885e45f061e0cdeb855ba67e4efdfae914d6f4548f2261d6e9147bffb8c2d6cf7777c22923a95c9df4f03c68d40d15eecdef2d187a207f48d2ef249e2df8dfbdfea8941149e9b400a4726aef7e08b543d0d5f48cf471e1221e6a333333d29ab51492735857252a151f107e596bde55f5219432a4fc7baa4ea875f5291b933f7b2237101f3d730f680b8a4a56b2ce096b316850d560aa6e0bfd2b677c943d0255406df3ef5b75bea6fa9e16e937d3035c804704d17b7313c4bc3913354366122e77235b591457ecf3c6ad9096ab479754e5cba9a73a30c307c26589a1f324ac4a9b08cd29db5b631bdcd6fe245ae1b8586726c22922fc62cbf8ecb4376507efb8355083e188cb4500a460be1ee7c3f35e0e6b42d9d8d091b43a1b54c8df00af805cec2a9a7a241b851fa2c25e440ac340de93ae3533ae31ff382e29d828a7f2353b5f49142d6d4ea950bea90c929f73a913b8bc13f65301f610047a7e016df013a477d4fd5e021e9720b3a0bd23958fcc478201aeeb5a9971dd8c43903aa47de735a305616a70379046b04f348e22250465a2b2adbe309134971085291caf7cf4330932b30f7f161a1fb84302dd023abbf36350665e63e8d61b8acd760135dc5b2f017e00de435a6fdbe997d3a3759c447b2dbde7b6fb9a59432a51453073407f906916a2a3d3bb32a7525c6151b542381bc6cb0c81ec2d4589a8b43c07c5690527e3c7d88b2a5b2d242b4d102cb76396157f0e4ec99b302db72bed458f09c933dbb05e9a45008f59737a40fb1347572fa947304087811c4713e22eace9ccfc1b2326c7452f4542e4e8610c235cad9c685320fd3ecf5b18461374e1947a18d2be0a6fa71146c3f9d5565a5ae6c362e962b9b7eb33a29caf931c6cf381ff135ce874c6259903ce77c27c5f7c8f988fed919da8a8d156ec4cb0927b10b3e7daac0f6c46eb8d05ceb30717ce220c176117b23d332c6b1394772724253b0dee59e1c9b8e08cb82143834cc386243977b90f82441424515486af08565b9dc8384051c14dbc21a2128d81590d099c072627446588e25c60ca2b8c10c68f033831610610644381c587bb9e7481a438e48094561b9cb3d478a10852341c0e24893908ec52ef71ce139b263648584a5b314cff7dea6e2c83d4b39692ab99ee96446668d8ffc8b541c7b399626cab3b3c64b2aaccfdea8b0547cdc5dee2693e0243ac6ee5261a8c9f50fad78667e07a4c4f5dfa4784905874bf14ccb4952655584a7668723a29a725d25674b4385957538d7e34aca4e4e53a982134bfec447fe61e4445149d9b9ce737d8a8afc9c159d7960d326c64ed9f992a3c12515e220102f0f3dd4eb0c431fa34060220e026bf750cf89ed30cc7ccc8a79fd04baf3a54a8575e7ec704e3cc3ea5677ce0e278d1d29aad9e5dcf9a5a2308285ba5669ece434b6737538b17445c991a24a63e79a72dd895c9f59eafae573b251b2f48e2c382b6554fbd6b2783858a8b4d69c12d8e059c1899a152e522c511ca12206cb06152ab03eac9525baa8e9bc085259828c23422d5b979052c38191d32d1184820d4ac88824342d6cb8162091b1785da0bab0d96c1347605b683a26a84f70b2284c709d301c2b92a05ff0549c272598b2861174712c592829d900282dda8383460a6203c0f89943a0bc014f58120924ba6e71b385112b040b9058f9881b1006cb477cc37aa9f58cf048cb22832b6e689fba3021fda2d745892003555763755cee91010a4257d8bfdcd3041a37d6e5724f135be034c183501956e5724f1334b8e1db274d34d1d9d8162ef734814490ed2ef73031c50b1bf4e06502a88789232d30830926a6dc81470a65aeccfc04fc98f3bdf938e6ac756bf18ccbedf6ef4e40f7f8641c9cfc32b963f750807fbe1e2ee3a0f4907f2d53ab9bcaca7cde85ed21bfca0844c04159e7f4e7efe11e691c940e90328bc283bcd10ccb9f8c83f2631c9452be94ee2cdef559afc65607fbca7780bf54b9f5ebadd78f1e0ec8b12ea1b8bbbb4ff7c8b1d080e36acd1de7f4798538aa342d6a3df8603d2fda32ae9457573e6365ff1d77b39a36a30b92f3413fbff293481cbcd24b4c73ac8c3e9b69c82221034e36552168c48660646fd99d2d71ce47c67369c6e4d2250ed26cc741faa6cbb179c9c05e4dd8599ffe0e9b5892df82447e7dfb1ab5a217561cd17f5183673629f08cf62f2c3f007df1b9f4e5f7b28a23fa12b4ba34d4a16269d12058552cb5893e7335c8afdf4b501cd10f595a5e86ed9f960b8b61e8bb123674c171c171c9e199ede9bbd08067b4a7efa272c1b9345bf1ac1cfb6ce725283e26d27aec0e542ce950c551d08e930e158edd91d2a1f22e266c98eddc485d17255c6e224b2bf42191775018ecc2e88ce0a060a20e13f93b7f9c0fc3d0940d339f0ec530f4352f626872e9c7b08221a5c22697db05e0e119975b5db6aac5185994038861d5476b3384bdc92377e9bf4cc1c2fac7ab7911fa5f829a870fd9710a1a804d6d9714c3502fe887399a5cfa39563894b8f471dc486951d801a062a9ff049787be2f413c6375ec60f3c285e53ffce14b1077b1c0eed06b5cec7df8b38cc1a49715cb0fcf604fb160907ec812e5bab05070e9b3d8f04cfc70658c4b7fe58767424f5fc344ddcbca9f78c12253828d519fe844aa9b63e592e299eba97ce252150d2e17933e97fe25cab934d37ca3c1a51fbab02e55e3d20f595a02086298f025283583cb8ddf288761e87b3a36f4a10ed5a5f1b1aabaf443f4376e061d377e3ff4b7efd2d700706998f95cfad94edcb99eb9ecdb6186bed7f70c437f8678b10f49fd182d7d99229360779c5e821ca4ceb2fde14bd0a52f030e0e17686caac96a7aeb11b93583a61427499e424fbcbab9f1a9115c9a5cf0195c02e3a8c533da8d330a4f2e280ae8901ba71a17f4e192174e2e6e6471304660b69cd4521d8c9bcace8fa60bb0ed6cb51ccca26006d0fc26504bf2cfd60462b9d15b591ee48f9e41369cadd99a52aeebe3cf29f444514e6ab99e698d93a64d1127dd3869e238a97e7c6a44e72b0f938565059b35fae7abcbebbb9ae95ffc0ec6e79fe66996cffdd11d396535e03f3f5275c1e02564163f0c6c7777b7e636b6cb95c4b9e7a00d26b5b4bf308cfc162631ef60506e97df2e6263ec7e9634461ebc9f258dfd2ca947772751393fc9496c7cff907f6ef78b93988a2446aefc1eb1c42629597192d1f15439299caadb4bfae7aa7ff6cf54ffbce9b983aab96c72f952248f60e712c6546838430acf3777a6b0fdb1bfc972315ed4a8c171a3b7b74eeadf9e65dbe40d5f6edc5f4c4b0df0c8bf7e3cf4edfe6c70e94668e1848e22e9e0d67fc3ab1eea129bfc860170b9fedc3ce287f2bb50b418e4dbdf85420ac3c0b7391afa4aba04cda34b32aac042e7f6e94ba58e0a8b037f9f6ec8efef97abc062c9ed3b811e22e472a3aa493e1501f193493e8bc2206ed8f280c6b974c62f0c028835c9907ecb03b24bbf1dba3b3f24f2d98b1b734321b187f7a94f577664d8febedc0e7f23bb7c7ed19abb3d89111b5712229227c90e42a72460b4b3acd67bc0940cc3df1c0d2c221c0e3abb4b17543790e2924370d56709847e7174f9702f1b17db810123e0b95708786e0e198f3dddebd67a3a15717bc0be2f7310f02ef62f0e562055abbf8a9cab217bfa01412fed12c920c25a30cc0627cd3bff862797bbf31b55a2270e38897b68a0e44e99907d88386b8c1bf689c8864303285cac5e324e0a71f4983f411912e0a4962b6ec84ceeb40287bc44f2a9283e8e8ff2e34fc7899c0f347e0955706e7c6dfe088243eb60fe701c82a874c081492050d23044272c1a1030c902ad9a4e3331ecd53a784c0cdd45538fe9682c904413c1a3e8ffc20248187dd20ba0ff0f7f81491090e125068100937060568c4f51a77008a2d98e8950438f22edd37c9e4207757d8af19e02936200d23044223ea8bca4acb25ed775b17a0bd4162c4e318c8b6133da37f49c18b9d967ef54c3bc4d53d44a20e9beb7e8be1fba045d3871320581bdc047de295a37adf4679cc2314ea1d5e8a027304e02f3fa6b2eba8185b233c66a52aab5cdbd5ad44a20b18f431054276a539fb8daef877ee274fa3105b11d0164ea3eb43e844b1ded87ae040a6eb4221d80f801e26b5dc7514a3b8ebe15b1893e909b1bc1f833166098f861bf8007051886f8612b71e3f58f26890d3b286c954e07f909b41f75e8be28d85a051b76ab3a0b2ef5139ea782356beb5326868d3d657e73d737833fed2007e3fbbc7e4ac3861d248196f99c0f2b55ee75907f9d45fc336e7ce79ac530f1352b6cd8419352ad058f9916a039312839efee7694fdeea3b6b2e2bec2f9f49ecdf9703a3b91e6b50e7c63d0213e8dc7c0240bf49386217e7f2b5cbfe64edae14a9c88e378ca751ce545e89c73bac8351696f99d884d4e9ad34da71c5c559f9a15bf6f7c27f241a42072fd72cf0fa6382a3ef3ec8f5e574fdbdddd9d88adc84d166052911b3fb60b7a143fa2a0739a08ddc48d51d80ee219176e7c3f5d28af995fe87fbd8ac88a44f231ce47cddeafce5efad7c3bcd9530e023ad658638d9b659f8c10acc7f9e82086892dc5da40d35fd8b059cde299fa31f4dc3333f398af7df67c89fc678eeb56d489bc86d60b715c972b9b15f630af7c1a53e21911fc0198877f22a832c8acc0020b9c8f59c695ef713ee6778a0c1b76d0455d354eba3aa8836a9d45e49c2a389b2f60881564d4dc1b97ad1803e876d9e93a4074dd0d70bd6ff0c8bfbbdb63180fa5d81f3d1cd4de1382ad1f921620a127024208f9f37fa03fbf961a2610f4fb9c68a99bc9391af9d5386a1f34ec21789dc5b3d7a31ca2e3324f95ec5b36eca713d13783dfe9a364aae68e935860cd1f27459952bcc44e60cd253577c3b2a0eb46a68e93ba8adba3e373b7cfea5f9c8ff95c55bcfa3864bf7d98fdf6db4fff162c9b0fc61211ff71e2c6899beb6de24b64e3b1011a8cc68f13615c2c9baaa9c4491207c3f46fdbfced43a27d967926a78a2241bd49fbf9db257de435fbac5e779fe220b3dc3ef5550529e3f3c5eccf5cafee9f165fddf463f9717f3f5f17333365422911349e676d8c89898989449052ca8f730a8d5fa494d36bd91f7e1a2ed1c491bfb78a8991f918ae292b69e27b5eecf131c6af1cd9f24cd2610e21cbad2c0e7a7fdeeef90a4fb02cb77d7e5a34ee314ae9bd8ac1a2d02fac964ae1eb1062ac00075aa431c409aba6e2044378a3cb3daa9a1cd5698a31aa20458d2c7800051448b62a94c8b92e2354104f5218b122c51a9865e9b1014b8a267842a21802095e3ce18a1aac8ee8d1f9b9748e50c4755dd75584eb2c2189cb7afff2c50d3f667e512f7a5d9f65e1d15942e74811d37a96c5da4b47c77592a0c0f23cb1ba7248c20e8a30650d132cd1c424a57862072a1348038a2c8624210935a6b02c4fe43c01834642cc47610cd4095e2085227e4cd70f86144187988088ebbaae6b880abacf8c0ca8862f269cbea0a71f22b6d314ed0454d33eb85cc60d41a9f5e41481dea008aa1616501002105a26d972b9278749ce0f2a8fc63cf30280cf5df43a5c72e06871e186d7020bcc2bb8b3d85851b15d88dbb40cab179d327a3333ef08d91842bb72e039707777776707cbe015bc59a2dbd8b032cab91204db2f27b31485194e8c2ad376f2ca418839a2b818b35806f6c998da32edbdfaa1c5a62894095f2e57f9c5589d24b56cc3641062ac95af8eda389253c519a6a5bb20c618638da368ebb489239631c6187d05f61c162ab3d49593c6687379852d461642cc30330ac049198d955159c638eb46b7b0fd714a5294329a613bce2f7eb224a5c4cb2b3347b1fd5b9604db5fcdb01ca36f2c9594524ac9655eed9c18543b2cd4c92965e704daf38c6f4dd8f81a336749d8f0afa3625b8b8adde2199ef9ae7fa3c1a5ebfd1ba8d56a5c5fabbb61a3443ab46269071ef9db1047d9a53ab028f6b1e10c1ef9a3a146a77c488b0a2bb85d587f9946758b526fa9d810c18525e3db743b1d93ae48a7ea9674a9cec8f597b12f1bdd14456d04b5a143f8071bcf8272d06b38ae483f574543d4bfea73fd396bfd16c91aa3ac1c3922d79fb99aebdcc9c11b5428c422c26a60b7220eca8d1551180decc613630684d02ab41281bdf2e5d7745a77e324fbfe9d7f67c449a1f7ef72b49bf08b4421542eaa0e155a791f23134bf5596e1842d123f60ba1e2289413470f823131322befafad88422c076568a51272b225e1a2bafe2e2a279db6b81d72c2f2ec097645c437b47252b8a142a86aadca177ade7f870aa13a540815ba827580cb3d57a46e87e2196a04f340228af8ae8a5cf17343ab58ea8a7867e3a0bf5d598d7bb815b1b0c7607f4884839f2d6fcc92c1063b8afa6acd4dc0de30077b637ce618bfcfa6ab71d03b9483ae46d00db79f2d6ca809c35c81fa2ef75811e5865616083cdb8fcddd7e62a943a17eac7862851324f5fdbb224eda8ad4ef9f97a750811557aba290131fd5fe422b2aac8ffec22e753d0a14cfb470fd3b1b246ed8a5aa00badc53854ac7e59e2a58b743c5526815571d4aba879a308c6d625de8503af60adb53c5a987896a8ad5ce7de1f2144c9e5ccdb790132fb1c9ab5842d00d3b54154bc829b5a90a293937338d296502cdd68c32c3983f390e4e9d1863ac22365d223645ac65436dcaf5d7d2905146d93bb006e46097d3a1ba54877212d7d83829a545e96e3a1c4da529b9ae9d42db8d8ffc8d541dab040a76fbb9fe4eea2f22811b977b84c0b9bdb950a960c3ed8ad6cddd58b1a44df18d084b0bbafe5a1429576b5dff0d154bda4ee5b12b8fa5f9a87e76aeff9c4287ea504c6cd8a13ad4f54f75a80e15e30472616541b890290590848a9927cff6bc9eb3bf67c87ccb81656f4acf6714664b8cdc5f13b07594a159767665df702ed13f97e019ae4211a49452baec8f7d1ec1dd5c6394b2f2a7f2021b4b2dfd6c5200111ebffc9ca9704b7f6e6a13bf8c5ec44189c6a976ad5c5b413c2d13f16df75a6bad38586e30e8cf2fdd59d9d783611c04fe41fa0ba712b73fc024c95f388d4ce1fa6bceb163885e1ef937c942b3c286dcaa17d617eb14c3dce02c71b0eb1938b245ca6a651cc0b2f9cbf59757ede6e681b5dfecca2f26f7807e974fb061ad3775e3245a6bad1527e84463c3a9e606200c44847f284d33339077bcae2b7eb3e5e035e7a4cd021405ca27e57e9c449bc86d75fde594524a29e54f27d737263c1d224008204c294c319bd023ff70e6caf04cbf7359f4ac9a2040de50e61fc8fbc8c44195e541de077b47e7c6eba4507b7a7252c8fddc284e6a205cefd9b9a144c4b02137816414dbfe3e819460ebcf9683cead9260438e5b5d7fee27962aa7e27e3815f783029a52449c0a0836e454974763f9d09e38e85c14aef5713f9a0ff793868ffcb7ca328126502c693a3ef2f7adb2684e6cae6b395268394cb7b0fe2c954cbca14c8c310d8aa3e3251baa293bf809698d0d4d5123d74fd729a99b5c4ae78c33ce38e7fc24a5ddddd7c5d177a7f3a70b9bdd50c6390848542c4d201fb9fc163701e6f1c9d2fd59bbf14ca09346c4497302c54feac0fedc909e70cc2deccf8c428a2c1aef5448c47379d7e5bd93765c202f389c24e304bcb83d16dde012c7ee8ea51b0ece80cdc0865335e5dffe3c867962af0f49fce95df7eba9e779b5ced40d4e1cf15c36e880564cda71fd7d20022402dce2293e1c857bf5a3bb329b9a8afc6adeed5cb79b5a8bc286bcba2cdce919617b08ab71dd3dbc71556ec8abe791fffb5b617dc258ad7e74970c347e09a9eafaec1df5c773706f22d77f366bce1a5befc16b574050e5df7a5ac68565a04bc51054085185abc58d86679ce7e4caf1b310af796097bb570141fb963f2db6c418240726b785eb38707a7012f7e860c9f507e386e0ad3c3a088ab37b75ddb7229c78c6639858031b6e345ea2a7182f55d0529cd0f7531d1bca3b43343c9a1fc3a358b23c9acf7da1b77d277a9af102ba5354efbb16d6bfb96d07768a0b1ec47ba61c6cce4356685fbb2dba70849d8fd1d09ff451b45e6a53639b7fac224fb238383fe9a39f8cc3878b82e829dd0639d03896a88da52e88961ada34bf4df3436da6eb51d244643e11d5d46c00f91488345d1f114374a6f96e92bed1f9311b407eece8bd3363ea33470fe3c83c8408f313917962b81c0da0f261b42a92af2cdbac28949989b57bf6ee27e94c18af13f9c441198541f9a38a25d9924e9a871c836190aa2804fa7ca38c14fbbf98fcf9a40f4b3e91acde3e96645c963f5a8ded316258f52f6c6f2de15e22665df9d33c2e1467300c3c73a14802c3f00d2519d73fda2bb1af17abdf152392c1c1fe198568511b72b5ae662ddc360076e3d7c3951fc7707883670c406cd0a14f4da46bbcc44e60ddd0a6feee922b364e0aadcf0a6aa566a5486b2a6960a0be7f2d409c471070c0d3c3b5d6c25e27ef081b6f98bd68c533f23dd4fd701c6cad73a450b79f061d577ea215c3344539d838c819f44d95d8327328e98f73c6cfc1739303c7492cdcfe1c3bfd399cf4e778e22546238791fe1c3932f6c7cd395a93c388834f7f8a3e24a19752cb719323154ba18fe60607fb43a1279d1cec1c373eea38e583fd39701ccc71e360cfa01c2922531cec5fa1565b85e54c9b289c53c21ca92b6a72fbb52a9b63a88a4e99c98638ca91bafd2e2c979c58f2502eab76d9b9dd9f23154b2bac164439e1e4a862897956fd3958b1d43e3f395a39824ea8e862134b318b275a303999fa5d52b1d4ef82133d542ce5487939b73d9cdba9db36a6f8b149eed87e02f004e158cc0c3436b1e491223b72e9705104a594d22695270b79a48821503534e612973a4646caa794beac5c9a56ce4f06e1ee67fcc7af3c72931c0dd98d5f8fecd6356cdb9bdb2f63906b48143de089315ef1f23c8f7952403ca91ff80d3d1ff2358ed715af8f318bd70ef1bae663a23087d9c58ecadbbc56307e61439b5561e30d3d09643732b9a8c7f9a03f8976055d970fa71c25447ee894cef5cb070c73f717801747cc98174b1480f23f02c03e1a1fcc30d7d3cf3ee4a011f8d030e82148c0429814e7cc115551cc8e5866988b7e1843011ef98fc173460c4d10adf105d0f72b28c8875b470c63c4e6320064e501e683613668005584033770500d0e2f6d1ffa1bcc03fb161611d4ab7d37444f63b0ba0a83516c8043cfbde7b5cd0ae7c72602620b7d47310cc330ecb1ee435bf7a12df4ddf6dcd7b0d510faeefb21c900a27b4ee5edfb6bd7d34b9a01841340ecab3c10dc770f847d151f2dfd40bae71e88cadb6faea53f0a74cf7d0dddf735a87c1af84240413624d9deff87b689d824130a89b6e75a6842ff437743df0fdcb761746258cd38eb18ba9e7db0eae363839332ac5e37380e2ee1a14232e79850c2cc9327015e34ba43bc927e3b4429295d6349dbcb72c14518469082841e70a105175c68a145ed5ab97277c76dd24a6bf67e2bcd686dd556062ec0376880d170295fbd58ebd56ab5300c67498849adb506c95a5b23461ace08c341be4fa910ed0b88ef6f04a06b755d2b5690137902e4a435c5c7ffe2d5eaffffff63add6fe4b1064fd775d7f5daff1f51793e25fd7334f26514a6790c85ddba49c8ff9d5ba5a35e19a8585e28d719d015d5fb031f0681c2aa414fb8d73b1f42a088d06648c31344f4d7ef5d7e785dee87be5e3a42b0c27d1d5ea0607a88b814629a5dc3e31461feb37649694e981bd10d3fce8d7c2de3f08cfe8b8d5e4ef430e3a0471508738a20912473c5687200e7a0e12c85a302c8dcc0f6ed119e4ba3220fc30b37c129300f91f63dd7860678a47fd3855c56a19ffe7cf7ca2835a670c015edae39e1fdfd45ae911bef8e28b2f7cb8887d031533d7ea79b5664e58cf054f89161d31f585eb2297f5f27096a8281613e70a2b08f6d0b79f8836394183af6c085e5f0282208f14545ac7e218e618190de490001998c551789cc4c474cb09850f142c9673f3639fabebff77154bf5fbdbaefa4301804cd75725404a50d75f06efe19f17472e9dd8ef1922072fd83f0f15a2b821f3104042b121c8c486313c4e9a4c9c2463626228c59c00d25ed31ea4b981002f3c4f6234eeff6eedf935774dd334ed3528738c96f64d9ffe9def85f7010058004c92ab8f51b26c6d52a161b3074c928249973677341b379c3b7fb5ecffb76ddaf82ce2337583b384617898fc00614ee85431c6a8c2d547ae42bffeba9b6bbf9c8f54a79a4d87307b4e9edcb51b0c1bf28cbcfe977b5450dcfee6284d9d0105b8e37240865518b219646e90c811379401251845046018181ef927a0f6c80032f1037180588e8d0f8273325012c761e6ca35d65aadadb1241347fe20f8ef79d6665996653d64bab34cbb6ec8ac8bf2d76f51edbeae2bbbbefe5cce870d615dbbbb767315c3388cc3388ce3b82af3038aedcf6e07c5bb81a1f2e160e35be5fd2d8ee7c9bed61e3621b7e6b05d16517f2d0fe8df7eeb2abad8f5dd2cb5b3cc86a87e5d79242b2a9f13f1855c217706edbb8bb4cd37c54f415cd8ba7dadd817b91d7a06a9031b3f4d9fd86ee9607570c3e1385d64418c1337b3ece666d9ddccd99552ca1a4b1e68b5701d3bd9f02ff3c838275569fcc25c2f5b2b15b1bc3e944fa58c38e857a58d600031f3e4393fbba0d8e9c48993aab5dec7c82636ce39e79c72529d2e88b0c58e55d1164abbbbbbbbbb9b1b85eab6d6b31ee36d0a712fc444633814cfe0b804b8de35618baa9c93eb9f37e4d58ecb452cc7517c1e2eec4dc03ca3913ac0a0fff65a07b49f3cf28fe901868d37e48bd2eba94808cb83bc3d38e85de3a8edfad7a89645850cf483975a871e7c01a837655f23c13046300cfe97a8e712ac43b13f62620f540ad57d5da91beb925b020a13159b6ecd6e3590a7dc48a7e1368ef29b0e9acdcca846759e58a8444dd7453deb10d1000000000315000028100c08c462915838cf7349f20114000d768a4472523619c88324c761100319440c22c6000300318480216668c606018c54366a21e387d42368f0f5e04dbf425ab1238424b09b66b1a091249f8c17b7753dae116a81f10861636e36d1ee2709f6c8a009bb7e4619c9250f380dff6a1b8400ae9bc26ec4f24dfba108c9cb8a3b16af8b93ea89b7ed8fd077768b9e784ef0260cf6010edb5a777d4a05ddd02c8a2c0079ea9260d4e4567f31fe15ab1d2e0e5c07c126d2349bf5e2806c989d88a1df35e25e08dcc70469e20ae32df279946e08053a5a6344f901dea0445a7fc8cf1e2c410ff5c53042089423fe9230d0d61f8f77b801a624c839c3a4c066943d99944810777fc0ce1cfd9a3c2541b248eeb47061a164f1f42c2e71e73fa40ea047afeb87784d2bbe4174f3662f975b9bf72832b6d1d3e7291032134a13ec540259b1584c33b0fcae687787d2d6470092357f0479a3510f459761770f8e13b57e422572cc88d27242ddd7d33d5fecbc2834f40b7b8703957d25073ab3b5266fb29445d7aabaae1d3fc35313123e18c096242a614691066a8b4060993bdcc45d428f902fac4f5c52058cefd05cde4fa5cbdd5367d54cdd5de9ca4b394b32d6fc1a5d9ffdd0acbb5f1505241700795ee4c9de9e37884d8325b47ed470288a0da6d8b680b26227351c9a3bb678e909689d40592b43d2f942b434c56beede68c5842cf161825c90fb65034e2ac9a4750629233fcd3cb10e97d019cc46221b0b5ec2677b300beab6ae1bde62d632130a72e6ee678cc77ccb123421f398c42ac8cf33c5cb996ce5644c8107e5e3eefbd080297363cb7f02c7e9126324d3e96d3485e2e75433b02e28686022cf22be0747e55fc220af6988f65207df33a64826584d2661e1e8b88382756571452b1eb2afb869e7a57c893c1339157fcf42ae5b828d339c8b3c5d1e9a9b4836f1e35e9c382f372f9522b84546a353d9967d1274d19c6fef91cbeb9c3ecaaed33df078c705bcb1a25ca2facd19d7707fa90bcf66355c1c42d29d0991b11dcb069039e1b3fbd9b5ab6818fffc3753e41b9c7ca987ae2abcee62649cabf96f3dc63b8a37049e67948e8be66af73b95146864bd58e73e71644f4253378a52948eb5ba89bf5e3bdb6e88e3de7668df4a2547d8f10460449980a59fe46f5a82680addb7bd60f5a5081e4e395d27a9dc5948176b551c5c880f48c245c8d2d39e98dc9b53b8732d4ef8108cd5159165520f188313b30bdee538ae5da2af4fd359187219f8f9b72815b0de5a4d828488b1818ad357efaa2511519bc63731eb82e70592adabbc81e55da0da9f35c744040ccdbd218d65e800fe13a11a56b4949680415c48c8174e20949ce318b26bcf1fb7e70e688fa16f43896b5dd8db77a51d7395c05ea1391b468b306376fb3245a6e041b4e1f87d9621c17d1304fded4e05652fc1db518ab9df72ab1fc6319beefffb21bba84fb77cbd682911d0e840bb4651c1148ec2076a9727a516382d997f687fd4ce0dcfb4008ea24f72a2dbc30f6a98ebda0f959a34e8a1f0bbd980a24e0e33cc7d9c9a5c7e3c4c1b63a543e307a7feaf9b3806a6a2995dc30d310438bb36ef3f4255f6dff507fbf56e0cd9b7de4a9012640eb1bafdc7f1289cc7d72a89d1f20e48e0243d6cc5a9d70b088844107f11f2191bdf1201d2cfde3afa183999c3993831c314afb30585e9a16cc5d842c8700d158547919de060248d1ad62872dd6d22dad932e992fdd54e9e06e8045c250aa3759809617e89e21a4c9a017be77e7e8c124af35a8daf3e0c6dc1576abf5b31cf945e7c159cd6fc64728752acdfa9872bd8699559d10baa2dade5462dfd81785481cd729cbea861da52978b06d04c03709e0078b6078bbfb71d393e780b115a19b160435ec0449914ec0f3ec73c26e6dbe267cb32aa5bd12a28961437b05e0687e2b70b3c87d119079806c892abd1baac55d64411c1a4e11bd7764cd98ba01cd21b65ff9fe05063223c828de0a868488825c7ef84602336d439f148bd45ad6d0501059a7c20388e2a7c5a3724517fcc84a2b721542db0208d52a0a810ad60ff75506754a740133471a24d85e551f042993a6b70782ba1cf06ae3571254b5865e87c0db50b6c14c089e738c14f3dfbba92b14a9c6ae4f8720e2a8b89769940c5e00141848dd8e3b90d84c9a543d5ee58522901c4e594bda265a24bed2e8b5fc416e3f1b2573989cbd01498be8534b8f1be71b1d751df9c101e19b34498d183d1b8e8cc22ba6081b83968c987517e72d29328eefc27bf3897252056b222118e34c7121218fa4d19231bb9762378055cc831098046cc6da78feab68e5c006c53f8d44a70ffad2e6eeab0b01a700c09a8c9cff345f0a1a449bcec7131da76fda69b838aef20c2f94d97234b0c25e6dc6b13280361032c355b51ecea444080688048370affc064400fd52a1c0bc516879ef616ee062aef1db393aa5ea01047f65cfe18fc06489dfe3b08e3d8dccbd1f707e8be24f494d0c1771d9c5809e84ed28b590995bdd8e82474bb95bf5a85b8ae4def453719861dcc8e0bcda6184b9412b2980d3894b619e344bcb4c7fef2be17a7b4bf655fa235d6ba54c733714c5957e747e462dd326788eb4fb78cb28b7d45d8d826f4b03320e05e7f6fdce562dfc9393bd72f2bf62dd122ee86c6187b39cbaa3ccb991c0131b9b188337776a4a52d2bc71561ec5c9b224197771122bc6250c8ebccd8cc21cc58f3719c23c4bb5caa628de2aecff1778416ae610d2b0200b22771a3968abf995a920fa9145c4d681d4b0b754c8feb1fa623ace44e023fce6c8452327195b78d1548a765a4078b5e47f120d11f94d6821ffd01dfd318c57703fbbf5550f732c2026948ba790fa70bac2f41ee0338d2d0d485a6c9f110de7a7fbffadb20b2ca4b806b253751235b7a5d0833a4e07a08132a7afe4bc61d2ef4ce508ae81f4e5b18146d4161a9c4e1225d851c173c329fc00b2922d35cc3125da8338e99c1cd4ecade61f7af6a58d7365f3df3230608bdb61187b2788874157016ebad6083c636845aca21520bef60432af4042ee2d988df1e59760fb93efaf2fd096019921bf7baea206825d65a00beaabbb209505a7dfbb20160896995e22070c25aaf51eb94b2e2b03a37455f75178f4d448a581fdf8accba5bc9e54e18ed4865209156dec2edb2b6c412b83158d9791fdac03828bab1acfbd04a3f605de737ed8100e61ef9825617b298557604644b354145d8474a471e31120b2afa86c699ba913816ea507d5e18aab326298fa03ceb01dc175230b0726dfa5f35fa0d00e609f5c1af8a06de1d45021a7c5236d6be8cc29527f1180464d1129c8c42523d6ec456c071019256dc2d9e0cd34ac39f4f133648a3ca0024c83488c7415f86b137b5d35ff5cbfba30bc45b1908e0a3ca258b63664973c9e299927e95e33d88dbee3082ccdcc34822885173c10de385052ce8d5ae16316205e6f7626726e8f1c181d82c1b665ad3efc36b157c890c185d13041df59ca971298751f08a2c1aa2640688bd152ec63ed758ea0c863d1310c08a4d3dbf0a5c657f9770c9c48ba783c0ffcf123e7ef9b7fed30109c7dea352dc7311af9563f9b81afe8fd250a38f3c24fd79e7a323611b4a330d542d6a994bfbe97c83947de8b5b3f89082a6703a656056168d8a32cb6da8d8b95303e3499b3fa4c7859cb0eea8aad4305aa81e55f27ccdb12ff79194bc781e2342e65ab27d01a080c073e7c68fd6ccc9baa8504396f66acc0aa18bd23d7eccd544f9f986a39840b5d6842147176d47a5d967cd46c8af5195aaa0df9e92f1d7dd2dbe21cc24b4a4cb146a0be5ca743e5f4b3ce1a3a9bb40d8b669c290194956ce0020f9bd9f984eb40af3ef4d5c39f164d490d4181c1bfaced8973174c2032684ccde4287ac668261816c6c64b8214aeb445b14e12bd436edbec8b8bc64727cba6b18185443bd04613bf55c985adfbad00fabd34b8a4b5f3e1bf4d73a7ae58bd95b70ff11788dd8a721c0a5c5083f25b1a69c8a41110e9a0599830e4424240d6a1da2179e772d6bcd0f4377a253d5f7bb403f13022bc4a624fba85f39746c5d3f5097a19f477d333b86e1418be9f769860b502d6b4076de85e57d9f922e6e8bf65c3062216858d08c7604ab1500a208a5febc63f8a1e7a619aada69df74d4bf81de2cf5cec129f7cb3d7d66a2f5cdcf405d6f7bc8de234d6bf2935e50227a33007c3e7448d4bcb86c50665a8a3e7c5f4e682ff52b2d9e1288f7b29da0d5f22cf897f9e82fd48ae606a7976232ea571f44627ff2ba646c68cf0337619faf17ad1d7dfb55e77011b258471a6808ee87e29661225e08b610412bec9000e91aeabed6e7c5fafee86423885a710860fa0f14c5919ea35843d68196d8fa1ce2b5a949baaaadaae57aaa0b7865020ee02af82ba46b1bc7bc2d51d8e4edd5142cf961776dea67e5cc330eb1c56d8fc00882981d204cea1db7d1a9c3be416b76c6a2813c2c56244aad9014f9a4427e34f7badfd53d558c22f0969451d89c441481e9028b9c08723f10900e2e7060ff0279cb609728a68ad61d15fb7c0ed520aa444a2e37e0ddc4667141b02b8f41fe52691dd5f56eafa4c0f508806c96e46fab4521ce6d5879dee2e5769a955c2617dcd0af92d661bf0bbaf35fbbe537d28fa92409a81bc176b5e96d3caae37a881d42939f604edc811f1d2bcc8e28195650421c9be33a9e0ee2d4aff8976eac28101adc72b75f3b0b34ce2c61dc378f762c42adfc0076125c56f20aafb7034dbb4f24858f98061d5b3b5e19af6d60db17867fbb8c4717e1daab912d403bb93fe521afb37a002428491faf587284785425d82f9ae5aad44e9a5e1de78e30409ddc2ba1205adde9e684387eeb7d4373912aefa435cde97ee4a355699e2e3427fc2da06b4e153050da6c95b776405a50158dfb58076d752f11996c9ac0d297b113d364182ae92fef8ad5f1ad8849d17f6e7468cf73e887294d04f8636de7b59ec8beb724ce0a4ffa385ad4cd65918205accefaa9a7831ba3081312b65db9f3e89b9d37bfce590e10aa1fa0c92cd752f53c53e1c7671650915515c0f0e261d04358b11eae7573dff44685e77f3b6c6fe2d1e311948991bf00cab740088b543fa15ab1d2dfc0a94b040610944edc0dd6364fe2e25954b82fb973c333a27bf3ba4c8fb1d9128040e53a87fa65d9525f222bbe37ddb9d36e063b5663b3e58f6be97d4f76ed83bb89d059f6082cfdc5bc80bcf2944524084211accb60f58bcb49c10746823a941fabe9eaf8bd2cf637e343395a95941d834bfbeb0b1c1af75562b5089818266a9d731236980d5a6aaeace790d714fad068ef2aeb7cc4ee869465de9e7190db58afe3f24c6438859499b83dfa98908005409da4c2b50e37ab0b1e2b5861e61523983c2337a22d2f48d33ea471965f569eaac9fe6c88b174fa511ff148bf2b978b607e25468a634763496db0a3641b5d960dc2c19edd5a2f2365a04e460e286e4603d22a38745e82d4b383de89fa53350166bc35adf261cec3fb6eccf65d11ccfe423665307ae3a265c631f9ac01b1334b4d0b6f06cd6ed69edf07f11fa7d8e34c4253a2521a20fb71f9aedb623848d073d5c22122597f77b0ba9d3883d81ee2179dc1d9ee112951f5aa6d507a145b81a12e22033105f092635d2320e135def40a726e75f598158a83c9cb3c9b126b788d6b68a769c28beee8290cfebd9c270a66953622bda9c3b0feb1109e61350fa880414b8fd85d073760c75f48911195afd6a90ee23b75c72aab370d803bdcf046f607d72e3e89de788873b83d1b6632148aa2aa50ab9b7236768db38bc7643ca11e31ce60b8285dde727d5383205e84284308d0df5e6593e4522372b392f44c2e49ef287b3532e63f77b8f4d982574eb28b27d98b8b508ac025b0921da9404888fc3a5999ce6ef0ce09d98bbdb3e86b5b40ced6164ac5bd7632b957261c6dd558b933c1cb23620c6ac461e186206f9eb1ede941b027f0b1924b389a03534f5d88039ade865d7779872be2656b9e6156a3c5dd3a805c806f6222299282bf66a8cca53e8340fe2d30732530691886a2987679d40e31a25ae5cfb6165c8b52e30e8468dd25e44d2255c82ef19a3b91486b1040acba04e6fd8d4408b4ee7d994b628fbea533aa6d45160de345db241a3911908f18919412f90b94f26c143dd8baa938b3dee731d53333f01d5bcae0e07ad22610a5f6e50155ee9bf3e6bf387b2576909054a583465ed98e3c908691896878840b4b24fe659b81c2a1701c75b9143326e7f264c3efd3d99d3992ec16a21fa25ebdb196aefe5a367bafb2515e8f0d4ecf8ace60888f4673aa7e836d976f401cd52ea37095856596efb2a31f567f6ce79c7faef1a87894066589085ab47f70eac35e285fa621a41ab94c39d1241a063d69d65ac0233bee9ed35c6ba4ccf459fec4453d25d5cab636320db036bf46eb8700e4c975a9b4b143ed545137334f8fc214331f07968de2483554c08918dc1b53f606abe22c26942f42abba14e45b18e1a6324efa2f6afdc273ac68bb803810ce3102a538036b7b242d98669d2b92744024137a26c0de28a305c1a0159f085945a665fb8b78694ad4419bd4fa491444bc88e5554c7ae4337b197078f4be1ca4df7c6f008a1cc53f8823165f384c41dbe24d9956666467f572dee7c2b53852ea0d66b44349c6426a684220e0e91da619586ca80d685e586dbb6dcf77f1a3d0c3d232ea99415a656b53dd9fbc63d8f6ddc7e876eb1282732e0e174eb1fae365a16392c5d4107ea27d9a62ca8498538ae68146861752e0da51b6a4cb9de584e05ca555319cc8fe7fdeaf676e5b3d849109d7b3bb1161641067d17da02f7e7a49d27ea558a66bd3ec20275e48d0f49caf7457c2b866f056497fef1c85ed7423a806c8cb1aa437e59525a80b5b4980840a6a44cb7ef61253f9d78b516c607d0dfe44eefc7daf1daef8da8a2e37005d3d32677c3a3e1d75a825403010fe923f75535eb962e9851b8c02d16f69d81369dbc191209fb51a7be37180f80f12b8e7bd48911297a1f1bb9c49b71df59487dddad74aaa26858027f935b97e7f94250ffda47449403947f1f828b345cece9c5c9fd9e2504cd58f07946713071fb2a49ddcd4bf61c1ac043623f0d8e1a37a4618a30c3d4d8c21cc225694a01441e6a2606520f0489cf6192384be1e8ef89601404f71d207c49d6278901956836aab7c03b1d0fafeb68a2fc15891508629a786d7391e73e46a41a780e2b3655679ee6acf0781dd08a5d43bf84488d2241398f6f19ce59163dedae9a410db8dce4e3796d1c28eae0cc812aca28a2691bfd6541bbd681c31594a325421a2511e1348742f55bae789593109bdaa6f683ff7e028f97227e3ec77cffb8e37a07a63998b350e170c82932fe258dce45cbd80ff31770a195bed24e63c795873895794e509a0535fee5413450330cfc518344fef709492538040ceef32099a1f9bea14aabc8d0b6d95379390b5cd8f6c236f3c17b5fb0abbe02711ca03dee6276dd5a0f8afa08a55c28f2b4702206d0fc07f6ec15b3883536e8a6dc3338734124017694016624011d547ca20ae30ecca27193ff6559af077478a9a2e345c05b46a60fd91bcfc3fcee3580b33df90c860ed01daf01dcc07e524c0ca3df6dc55fe7e36819c031fd2c0a2e87447ed289d8ea268f6fd4ac0a00b6806f994ee3989654c7cbdd972ce5cde3643bed923b141b3287c0e0e9698afbb2fc84d1e69d92bed5b07c70d1be9c12155075936cb581e320847399c612c6eca434f1af32b31ed86b07a2b00a48a07e940aa44916404831c1a3f03650e905ad9c12b2f2f1a6f992aad570f52f698f018aa4c5e1af918c20ce338ee5d3e2e86f00d38c4558b879c50ec90716b8c676e0e4576832a2cec4210a5b4c47a8812991c808c95d90191948a51391c54ec7d1c639ac60661320ea1c6edf13b0d84c2c10c1642bc484eea9e70f4cf5658a3a0461910ee038e0f0e36f14871c7380060b4627b6f6b6b9f964aabbff06f4422298fc11a347bb54b421d2ee0e7a412454c9f05c043321e99a2a676c54764427979826c4c07aaf6fe87950e888c912ed0c6d3777542921eb411c1222b25cdb3af79fc99f99a872acfebfa5a18ab4e148c8b74e2607db484184e52019b5598690f043b8525ec0973a5dedf2eb9aaf844d79b12c22059897bc01c57216f427bd5b9619a516a58c01a8dd5a56f26444e96192acccb9e43c32bd3068fc33b39650d33a5479d480689fcdae48cb1e6d0b8bd13888b1c509ff6cf81f2a7626c7a0a167583e84560cb683700623a4f1094eb37c0912b0224e88d10387a4abd182c83d025f0b33126c940fda05f6ecfe14a6371c3a7ee877eac3218224e50087ec4854f7568ccb13d7d015bcb40128c09d6e556d019f50d0fbd2c985c7cfac6580f0e0276ad932d36e8f2607cd96b271d1f5dc6a09dccb8ccb2d87a7b244435fe48cf3043aef6fd2fadb08dd04f403148f9e8e9b7524474966bdb47af90db9604fb4f20874e475b12d7b97190a5231f52ad1dd6f00e078370f942748f698ef5c184920fc9faaae1a30720760b2c731c1edff0a22938d8b1ba8285387fc6d7cc827d147bf67c6b50f770ea1a4c96b2ed61bed0f1fd9039f875e798836b99085b049f2a2edf3b78791530ced18a6eb2a67260e8d4b80f5c9af0c1f5fafc42e0500c1bed72272bf3a195acae573c431fa7fa059748ff7d8c0ca13f636fb4cc1fe1f48e755efef38d0b24b3f4c5553ca86f68e6d7263f1223ec89170e43bfe5af6765970774386c8ba2b21070f411aa472b96eb150fce65bba89b9621860fcb7089f7dfb76bac2ccbabffd8821b25fc1b1b5ef8869e222b08ae129982477a5081c10063b98d19a1b9d1fda8b21ff87cc319d54b5255847095daef22b9fbd047485d888351fa55ea8224e324894288cf78d391199fc995d45d7e6893f81601be95722fc0576d3dbcf8edad4b416633563798913159e63a9dd9c3af36379aa5845abee73df8e7833327f95155e3385cb81815e0e42d440829b722e60995489dc65f97c4bcd81df8d5b33a9ba4114e59eddb4138e6e91a788388185e87e38d384b5c8e01501946ef5923365104022940fe02483284aaeaa57030bdc30440ab63b6196471553737e2fd5f88fb6ada1e5b63a6ed3fed66a1c7874c5a2fa721f9a25d04f1980726f4d46617f799d0474b454d49b331716dcfd8ae594a8eed1c5cf0ecf7823908ef8f62376ac51458def579da5a4a65ef64bcf43e751dfd6032fd33fa55644119c1a3881b1693631bc49660dc132d2f9323df0a613934920838b9ce2b9b59e5bffc265f6cb0c365b437550f1888a0b4c12b36b15e449191700d2305142eb799fa4d425eead2131209a9507a91be5e914f209b688f8a7c47bb9b394add49da78dec88a68d96c23ed6f3a170975d5ce26a5a3aaf7988b732a2d4c54e27297529ce2368fa30e19117a825571161d0f0a51d6a9fcae01a9ee255deecfa0de0f31a00f75a1aca0c25050649933907e8eeca62d107d6d25a5be77303628663980bb6d21875a7b00b7d98b280d14d003b2fb602714a123362c3521480387d1546cc0dfe86d4597620c8d0a68d94cf78f3224ed4d25f532c28014c00a39b36a2e5adffcd0039469cd23794f1102417877719fac5e4dea22d24e23546ae2786fad8ced4cb63fa2a4bf26239a8516c203ee70b9157e4ed9700af344f5042ef1738275b682ad31d063850ad35b83d5f00dc9301ea8bd2019e7818bafd77c32ccfe5e467011286e684336626e081ba3b68001ec86466872bdd6884a172515bd7b2abc388bb329ec40daebe65cf64de274f672e96af8823619989d3229901b093710225cc4a6f1c33eabd8b4273ea1bd8a6467182c00ab22be6e1986abf9b2034a2ba5aeca2a4e83d4818b78d8dc2846304230db225918610974f1a31afa9744505365897f7169d058edeb46835b64fbb83616edeb20bc46ae07beef96ab1490f556b5be58f4720e35e886924044e427248122baf22062a4c2955d59217a13fd63026e39ecf68bbb7a68c7825d748b1675ab18521727db119eb96020e1cdc87ee6fda5b6f93d49f25d6d5c041d0a0f914715a5d58298807fb9db081ef62b5b1bae7d779c9545b5bfc2ded27daf7cc84ac5a448a70a5831b20f591dcbf6a330de372371cc9a9e173237f6f3a802a231a795805c01b9c627dcc4bae234a4da36c8303327bd9493ad09c423f47273bc2fd868f2a3dd0e3f50755266c368ae168676cd1129f44e65779d1563141864b0dfd936ff2d0f56cdf7169699fbdf9166ba2054acb2da2c767fce08ae6b0b04810573c1e6e72ae1dec4206c9140bafd43477a0c3d83b97743f6d5611869139937f3ef81d04ec2920d14adf5df0992e19f8046c30366983d6a7f16a60059b8dcb45023297f07e1d48da81906fc080c21a6c5414b9205838a8b1cad42cf662799672666b0165adb909aaca45243cb75c2cf1da22e704f876c1c30b8b7eb3baa4fffc2f6a77d6b42852f84097deed43b9da02ce315a9612fcc2ca6c99d23db7e79ceb28157aad8fb856328e32dac20402d20eab633ea273317dbc7cc686150920294c001b53f7dd06420f9acdff66931e7a99311724d9c7de8517d2a488e7a78bd748cee6f9138f8183aafbc42f1ca35a61778757423232dac9093229672f9c2b851e4d7ee93ad1114977a2ced8a482b11685f0599426ad6a26b5a8ec20571ee617965e75d3202d9edd44e330edf4583b8c6c2334a41c626f97dd1b9fafe6ed18fffc9d0eabe4539b79c4d75ea2d952891f818f62338054d15b21939786d91d39267fff612d4a123bf945a58c705c40e33db1906c2679fbbdc823b031af09a2cf1a0e87c58d182063efd5a661357fb82d44061789b341f6cb42fd5892ab9369aba30ae692a3cf8e9a72b9c57eac1f4ec5f22d117e92bec90b5875946fd160b4ed0a6d5ee982f093d7a5e2832a023fb0fca305bac92bdf2f40299dc43b0f5ff89bc732e7d2837c4af54dffc02da84ebb7e0cf9998a2f9941d2d4b2a629607555060c9833c6633a31ef262ff531c115bd56515c821b03e7b948b16f3182b8e0458f885583ef5394388cec27a3e52f71a85dd01110c9150fff81887fc5196bb12181c566fb9189b06baeda18884e77055d413fc22375d82807d1e0f0cdf66127e8615f9e569229f8250187d5536b4845d9639813448eb92d1aedf7cca00c5a602de42132a292024a979415f77f1d2130a5096fb56cd1f541538eaae05024e5b95dc4e0a78edd93d1e0b8310a2863d14b09dd12f763c5f0faf5d11720ddcf9df890ca075c2b44b6ef41f0f79989113cc4cb8bd2d9417edeecc49436cd598ec02e9099b58847b9a8b0134267f4893102b6f0ed68a7d2d8913c922c1b685ebfdac222d404acd69c531e684c426585e5d168f7dd5a24e81b7c7cc3e6652c6fa0f04380e4bcb2e5402dc6bdbabb4893a139d424e75f0ded10d9bc78ddefd5df6f4d70420641bfe53a71d26af3226ff456706a0993caf7e3fd84a40ae42b32e00f025cd7250219d57aa809212132ad712d9169c777b8e139771aa3cb1afa01357c79a5fdfded3ca41196b7da0aa35cab171530dced840043d5c3ca906ecba2cb0ef5c361c2ca2be10825b25a7d3391800d91df2a4a6bc8aed63091bfa247a26b1efac74316030b1a6dc17e677d2519765b5d9ce8e19eded28397b17805b47de16daa1d0f6798fc4af51ed19d309d531c7c54b83d9ac05019c995c1323bdb1a39e1278bea1a072e896b12ff321b54236b2015c1a4530c8ac0d4a92484f867cef13426157be4751c52fa955339b76806571d4049f11afa1195e7598410fa28fe79e6cc8a57ba33231958e1e9c5728290ea046ca4760d69be01f1b7498c0142bd41fb4de8186f4a03bdbf0baad3aba7012847de9f1def7292c43998f189df46d1b54f6307a53791ab69179fc8c7506eacd3c43209dbf752b1a2f9d37c983f33533e28c0c215922a275a606e0374a9bd0908e35d2e95cf422628b7f1189031d4380f6597723390940e21907dd138de0a60e525e4448ea3200ac6d48d465f0c6d2203a0272e822ff2f1fc6c5f6facb77fdce86e69f9155119d5d4c030834758866389ec93f27e3fb353e7bcd18d4e630fcad789e5da3e6e898dd3d3c579016ee2ed7baf1b662b7a13274522adca9ab848c1254c9d13b0e7f12b7c96b001ffa5362693e33ed9309b26f5553f31dbd8a4be9a2d4ddd1a3310bcd18371a22ee9d1745b7eed14483d503f78494788fc84b6b8a379af9f6bbdd50015bfbc91c2af8bcacbc920fd442fed16b007e30d2b2feb18ae3d8d495c5323798ffb2bb00142eb124f2bf739d11dc0af65921eab11bb460f4dd3fa18ab72d3cd86b1d0ffd17d2f2a5c0f66965689d374047d87cd2915368eb2b9369dc2b4629a002a8e658fb01b3442813ce43864c67fca1ff15133c73e4e050368cd89f673805af140839c30c1356e78b6bacf76d86cd148acfdfcedadb4e1fab960ef32120f33b32bc2b8f5e1a9bfe032944c8852fba2e345c06149ef8d574f4971f068e5745912be54e6e43041f1a69d4676d501feec6a2da1b46b16c70b86316d6e05a7fd865cd754b3a145ff3fd0214bb46fd9af22616e650567491e847f6ce457d2e84445216beb8400901e07fdcbca6aa03068fbdafecaaa0cbcb8a937f07d0e5e7e4d80ef38bdca3ffcd6fa18859cae84f5b88f511509f5327f371f3a25293f247cfceb1fc9be3585a9661c1aa7922c0bce3cac9f4fb9221b0d94a7770fb472d125aabed031b0b212f9b597dfcbb482c2cf79db5a8fb3be1d9b2fa1ef8a24d8758a4fefe226306a01ada0ecd1168ef1d7221c47658fc8b741a9c926bbf9bac48ed8253e0467b1c736d5b2aec8d32301778f1fbd246c7b495dd7a7860d359d555225d8015eb8a23f27253385866f690b8b2eac9fd167562faa5579092ecaf4709a216ea412304d21ad9e6136a229efd097981fde403a60c4aee94fe7405d79511c4e73d57b34c278c6c46292164f9458e0317c2fea1e90e639e4de4004237a84b5e412b89f191e6ca2370816aabe3c05aaf2f6738e5a5ad9593f84d58be882a135e65f0c0494271e0bced7d654406ffd39f317b2ae656e1c1e15bf42e8745636bd75bafca231c5d9f9e9ea9a2e50a11c8e0ca483bc85cd3395ac8fa0a5dbeadba478668cf0ae60465c2d2a2e0b301993b3e8e7bf9aa12c19a1b447c2a322c7a760b7be7a5ccbbc9f32bb7e72220ad7856ea3f0293d15800fcae3117360fb8b4fffa5dc88625caf29e5705eca9d1efd0aa1cac24266aea4cedc84f19b1e71e81f3d48c8bc5ff7044cd6700d09124de375cc4d452d06c2af1a92985d560ddb56f21841ee9740b1e316801e5a2d763045693138a3a5ac197131d796c2ae337c7a957d79d14a4f301b63eb03b3f5e09885badf067ce718c8c3d7d95decfaa9ff0ba7ae4d80eb4aaf62d93121eb796b060aeb540b2d7e59955715fcd39b6cc108f88a9d0c45c76a01959fd23a8c7573b7a35513c624101bbbd3277e643e71751f0f16e63cac799fcac3b068b8801b49a27fbec48ee8d1de2319b99c88488d22046278cc60d1112c957b968b6d4d2f0ee99785058396eceebbad709800fd8bfaf4991cc87e5b2396fa28ad31c24436cbc8ccc8b42ec10fd809709eaf2da91035d5a745d08f1884a59a0ac231b3687e2d1a03392bdac458c1a34bf3eef0e79790b46f4e0cf82c9d575804ac78cd88a667ee61e99afc6b142329dc7399a9b0afe8ac8624a5fae60bc33eb8ed601d25a750de323d5e590d72a68f6932860b66478bfa1dd452300416bb1671145f16323226b5235248cf0d9074b8d1e9a1ea7093c8bf01d3f51238a00cac0ac732749b61f8b2c6a601926ada48428760a16b83aa478f22a5f36913178e3952b6318aa8c59904866982dad624bd6a95da5f322797346bbfc2e3e8c3d33ee86b9833202b12812e4965bdcefe3819feb71cc9330bfd3bd4488c4dac10c0b4bb6dc9085b621d2d4c5f556247d09f60c652b7194bc3e6fe1281ed7d8942bb4903bca16c1076215aff135c5f02d10dd9409bb93c3a1305bcc56fd107f7217b335f293e39326e5bf6137fab1472165c93646971093ed8425b99c015624d4d091e01d61e537a3b5d975d38a119ea97aefa3ead70c3cbd32250718668fa29b874d44299ca585600d37ce96f4d9d6f26c51584a612bf2432d5a8eba28a496b5e67a12ec3ac400c491e6d26c067615356a74ab8b13c46e9ecfcd72def60723eddfb4cac2929b6573ae3efd377c0808ea1cec89ce6628dcd40a637988a0bdb8f1b06d9a89b84af27bdf9068149fcabbb33f4b77e368cc0aacd771a3eeb6f303c324999ece3fa11007d3511ee673552d9fa3816fffd5767e5aa6170b47adfe01e3d8295b7c30edbbe26f561ebbdcc1ee3c3ee36bfa3c3a05b2792d641725a9632251fccad18ae2661e99e966a89535e47c4a919fe850c9a407e26abf9493cad0bb817c05a371a592d4b69015aaf1889ea57cb01cde10fb1d26edc0e26aff546ebb8e366a73700f51e72df2a0c3ce6ea02e361ed7571dd71bbde4f02384008c0b276929ed0584d63c97dd1a40ff67688928b84d067cfd6439e271cc8e1db12c3c871ad0e3987cced8c8de6ddab64648330eb6887c213dfa09b307eca70ab3592b47071e936276e2cd167695349db7c6f66eababd9a1a73732d702f29ca35d25d667354e2f7f111f0ad76b511e29dadf60e7c1bc4cc5160dad872dae4a2ad4f082a789f4dc213ccafd96ce269f2af1f970a019ef740b6c6e92f0a35da1837708092092581a8f7e27f86152ea39a53bf57750007aed4cd490a5cfb5e5cbe9c880d994a2949baaaaaa1053ad7bb88563aa0c2e0092e2b58b89ad012c0afacd13e708e1c13799ad142519ef2afe21b18d04c9e920bc2967bdbbcec7c22dee0c3ef6140a1c77050fdb8978930c9a05014ea4ed542710202ce84f166221e1b360738325b495853b23276875b7064267fc140347a7e865ba7a5247c63d2681814596e02cfa0d08ebf793196f0ed34e66b849a1202c4bc5c07df793c7fd4012726a74522739ff5600451d2f196372f3c604fa90043d2acf63d90b6ae54e6739789b88ee62ac564b962a5cb6aaf37e52cbb784f536c909f4a559f78e4c534363d5da267928e795ad2bbb4312d6b7d2ece7e48b7df9f945cd5179879ecfb35e080c7b9bcf4ab4187e3cc864e7a2abae244364dff50f7556ee5ca1e361ada770d5587cd0943a17e321160a1d6a7801c34985f55f6cf1e0dcc3a932e6d4b70ea598fb6af3ddc5968677efb560e63013250d540903c80468c41236720cde8e34fa342df5ea65ff0590f771df533747ecae255f17e2c0c10f7e7094769132aef8dda4378713ff2bba1e26c330e73dc0083ec9e0b118901c2f0d88233251de99724da97d26fcd332fb2798ab6d18bb8adb1ee99753b1c33dee5c3f4f0b141296714525b55c7de42549276fba5aed04780bf302daf4510b65262cd9562fbec3d294f0101417c020858880985d0288133568990947cad488d9817f05f8cf00e234f62082c89210044238d824dcf0b79a56edbab259cad7f0fcf412fca6ce993a5476702da107cd407d3b487ac7dff3f7a60bb21c49698dba70b68b62228f74a4517196868e7a7fbad82031cb02f6a9a21afc99d0031e014c572721c986a0fdb5fb3c2f92e8d19bcfd6aa3782687203915078af8fd4b513e30d585668799c3d7bf0b3afacb53c5839f92e056e51d34c6feb204e47982c22cb14e1fd9325a41839ce38ceeceda0b98d3d0fbbd4159648bad94f1fa171d236505df91c908826bdd127fe221413e35db3277d768e7efb56dc1f638c5e6f8e472639ab4a0dd0ffd702eaf9b20c9ed0303537b3cffc600c1d8db00a39ed3e37e3989ee0ac39ce922ac7e72e5534104e5460b987982bb186e67dbd33d49888f4958f671550f725f39b9861450762f5e73fde1c2a91de4cc38b3be3ab598cd4078b1e636b2230fbfdefc50c3088f7001faff14a908f6bac2d64587afe0697f5c2ab201b2f425012ec1e87910305782f51b2fd78a59dd97eb1aa229ee0d7324bb277047d8f9e24587398a39dbb70cbaf4f8e28b73748c7d9d333c72bf5e72d8fa036f82606d9709e04a82e02fe5fc36726996ae4c2d41f16d923550b6fe6f033243a8b3634b38c5b9719c38867d779c9787e4016a0b07c059a43921ee63331fb32ee1d70b9eb979d894f6228845bf2b06b12855c32a6a42e4014d7360119ef98802295f4508c85bd927569a3810352ccae0241046a98cecc813c51d6667893c7bfc9b6c890d49099acb2ef84221704045f2bacd2e3fa0dba1f2cbaa917922538989a3600e0f54140d1a13dc73da94fb8c655f215514c7308361cd06e1a55a2c4daca589296ec84ec07c690c3e16ce323cca0b3ee3ef3c0887f49ec5740df9dc1a91135b81d935811a9265845e0721b0394c419fe45b9228d88f22061c401f85b02a6a00b31f5453945d22f611186e76af3e8cb41c4f96bc5ebc228ec45a5b6476c01c24ad984695c277579fbae56b80bf4d84d441b583bacc70847a902c26ac8404dc1ae71c4930477b088f708d9fe4115d49dd6050e1e678419a7509fb487e9f3ff2c698aca274b0cd18c2f981b4a2c32caff438e48949430201f81bfebed03e0f4dbf3b995cd500330cc3e04a6596e2e5cc5d7769c44684071a6357711fff77d0a6fe95d4ae85ab17d0a66278b67c25e3f05029ca857fc29e5cdcc684f483ccfea54b54ee0765ca382434d802b00683ce089746f25e20655bc96a9ace791184cb62d023dfde9b7c7c47aa6063c1561156563ad475e6951c11767ae34c6b2104ec5da430682b1214debfe9f57b2105b92fd73de35806088780ff8420b639e41cb6715b4921476dee8a3afd4790aac02129981d8124cad1eadbc083433171f193da0e5dddc484581d1e46da282dc105e5b81494d13a92b331c13bf20de8f8d1a75985245b63d38cd91f0460711036058d60a3391a4574d695db2d76b702290778e4cf78962f0acef7b51be742660df27e5785e1a9d9d819f42fa69c3e22db230cdc2c9e20bdb2c468d6587c1b8328b14bc192a6311ce4489e5e4aa045da7170c77284ea423dff0df986482ad8d8a158d4150ab527bcb6d81248b3e3fe1ce8d8e6332972e51cbffad85b017f1b4f181b1cebce36477aae89a7e3598f8aeb6fb3bc3e76192468074ce4b2ebe2d232bd9d38da14cf662490467778ea4de7fd97c63e90117084b9373d075831e2c7892b90afbdd8c98ec863d0bf2e466266e77086a5179f0157862175c4e0558c266797d6a76e8c7e7871993e4520e4375770fcc3e1732b01029a25df824a582cd3d2a68420613de7e8660ea0c04b895407928eb1da8ee33fe5864146b708e71eb3912020b82522f9b949a8358e7276c986e91582606bd7b7cff7401ea15028e18e2de92305f8356bc39935aecc00b65d61e9a0bd8e7844696de14a254d39ed8068ff09fa15bdfe167b0b71624b255ad5d9cdcc96a6e1fd2316e87cae20599bf1b027ad4f68286d1a400df2794da43702091e47571fd4adf8815130896e4859e1fe9e1d40817ad03b6853a79041244070ae3ff174ee860849e054c2e132a883abbd2110dbbccaf8e037598e84648b9d1d3ac7cc23efb2bf3cd3f0712477efb66dd0cc0339c1d60bfc0a3afdf866e55d709465c990f17d6b75f737a49a98884d442899ea328143a45f9a14f8e16b781f84245a1184dab8cb5a1d70aabb5e987ed275c3fcf0e37d64936b3500b11e035d2ed7f3b883a68e0d0155d2657e2f995b43d8ccae714888895b6523b58914b5027d6078f0159a54fa500b987bcfc12b294e7c7b633ec29ef05667b243219fb536c3da3c12583c9987e8ccacd642eb76578baf0e3d17c300fd583fb797a6efb8384fdf6e1f65aa48fc34fd5f7874650392e5e86a20a418b28b1421f0be3c34faaf4252d5ea2e637d527a8ebe525eede490d9aff085910c67884a81cf682d8004bd5b60b7b3c8aab65f7c087ed757e29f5b984db7776ccda1cdb71fcedaf9f92d1eab6c362948593b4f198c8fa6a03ed7512e61a4c3a702833833a5bdad9f1495157c15ba5493115082f3f4cf4e37223360945676eac219affd028bc9ab2d013350901dc70e0b8e3878030e4b411c4cafd30d4e2cc27b88b32257e29c1d5de237ecd52dbff9417811b5c8290a81e4ca807627c7312aa40d966d343aa011612ec4309b509273a156d3dff6f50f762048112bc040496f63e7c6736cc982e6e79e631a2996950fdcc7a58d6cba70c5aa81cfca02f041ed05af69c48d5e2e2f1cd0db2d7b90c8fb10cfae380dc75d78502f2bd740abbe8bd9245490d5492b9be1b95cb4150588606be97de5e090d0495d5d1b18f4c54c9e5e5f3c027bb713df2332f4d2c61e67abc3903dc1439b14fab9ddec9cab735d6de84f24fe553dc133a07cfc4962980ac8f01399fa8caee1cfa46f968b76bfe9e00ce6c345b9657c669ea6b4124d629f829c47d995ec4fdc6d858d8973ebda50e29aaa011c82f4205aa82a49a0cf20a2b7d09e5907a1bee8f6071ac2ba5cfdc9d3c68fe7e7e455659d69a7660b5382b7cbced79bdb84f7d8fcb93a9aba8519f142110033c5ce6005d0c4f4ba33e9f419b9b8eb592b2d679bcabd14da795e7c049c8c2c691d8403bb58fb52569f679907dff77015466adb130ecfb42524a20fe798eb2d00378554e08ccb7fe7a73c714fdf4d9b17d025c61f6273997402b1888d174b206347133a07a390dd4ddb7cf5dfe0ff45de82783157936834db63e91780ad52ee8820349ee74f0d3bcf4be3eca135253f37c7516c1b18dcb88fb52db97a0c1f4e6960fcf06cf7bc0a976ed9c99e0763b0704ef962159ccf0fc4646598aac37ac723859a8896a8659f1d0a764474d87bba4195a04dd7a7aa1b2417144c4ba56840422c8021b187f5cf7580405e2919b9d2ffc92f97aa661d509bc9bf700ea43c0dba0f317094dad0c7df7a2b86439cfd1ea558229d16b4b4b82a059e9149fb73d2668e2a4f716057e95d5072ab3db79060603cbca668efdf65d07bf7e5813101fa10fc86e9031f4e38052999799b84a6a7f54f210f8fd19121a39c8301fda4824415f634e2b565b507ea193388f96a3f2822b61714252a8826946278b46fac403eb0ad539882cf51d5ee96f7c07affc1bdfd12bfd86eff0514a231bceb524afd4de4b5e191fe013bfc32bfb8deff0957fe3777ca5bff11dbdf26ff88eafe437bec307253d8d535d8e11fda074c997677931b3b2533a1b47a53ebb3be9bcbf0585cb503bfab7829a466b064054ac306e463d5627a07890fc75efae75540d7148962f386bca17c8bd8d9c30222b472acc42caf11c961f30622aed4741d842d9b2ca970c076225539a0e4c59a14d66b0f681b7a8ee8828abd19e8abce4952eaddd57db00c2e5af09d5dc70a4ad1b5684ffbab86df0b78db0b57b6d65fff9e9dae50925e9a216acbf401f9d146ff975357932ce6084e647a5aaee43b9a28ed1b9ee725348224ecbd9a855ab7f7708da5171d42d02b2ad1e757387cd246cab3fd0b2d26e0cb4c026110fa075f882ed07a1f6cbf08deb34cef06bbe3847167fc422eed8de2af69a4734e0fa0fb04a3428fdfd933af283f1caac1653099f48735d108b6d74ab63788c368440edd48da6cabd9b9c9fc7cec91c017ac13163e18f19ab6c9f5aed416e9cf89eae41ae3404bae8806be8fd32bda5426081fdec403d29f72dce39de152aa637a9a03d8b26dc385f7370a9adf8dd4b2ac4221c4fab03eaa0125485cf9b4cd45bf8fedf92a0078411c32a47cef0996ec963f481df61fce10db17dda1b785e50ce218b707596203ad9ce5542e18835cd9aa2d146606d73d1cc0fb242f940b9e5cde02b5bdf0f1810504c465666fe2c3de41b5477612e8b8dd80c17840cb7a8ba68416713cba16826e9c2a0c5d0492e5e7294614bcfb4a0aedb8f22fd3b9fbac65d79098a467be1b8d03aa29284f1f831958f33b14603ff2d57ae99a4e1339f7e01424c3336d36a19bdcd0126fdbcac769cb1d57a6d8cc042f1d9392f1935985585bd1db7e403b025bee7399b9c76441f22bc397b20bed5308d778f75d41969d8f98f14a66ab277fea6db72644af1f19dc2f3fd41710d7ac560254fafa0482bf0167c989e029b169984c6d91a2660f989544c3cf98275240a858eea10cf82fc159a7a41a65cbb91ec370fd2ca33b1948f991863ba3a7ddf6906cb825d364acb7c4ed9f41ab5bfb7c3a099c0eb71f7f79a293bd6999ac59ac2d267f0c7110b55505fcc6fecd82ab1b0a55c239b300c06045895a56abca914bcfa901387a3c879080a1c0671da48ddfc7e8366de8e0b079f20e7c37f190456a162cb785a94c9dfe7c3f9c81456895d0cab307f3ad7a1e2692f3822882e4600212cc2787d91a997ff6d811f604c5e7e6f6ea00ab0c12652fcefdc5e91d7e5f233d02fafa0588bbef7e4ea46bfc8e9970ea361f3a0eab0ca34ffd1d36d607c12e55855c6cd1c67cd5f887902fe09154f5fbf389f7651695b3ccae8f31cbd67b608f2f65b8938d2b065fb0039fecb5ceb805582a26d97f7efe449f29b7f21dd10be609e355f49a1e5e45bf56ae58d1e98113cb7334f6eb5cee6d4dddf0c8822957abcea5c08d690b9eac7c28e0fd1d11a493309b588e35c01b1c170ab02a676d563ecda286a5686b9263749040503a44008c4e4150cd5c9fcc80a78a68c52a85d1025b709786fdaa75b4b12a181f73e0a8e40f1fe56ff3bcec04599ca85797d664ce101de06eec1c2cc81287a748a3dea7a28a8e371bab6215038536fdc6ea61b29c68c0ac5995291c25022423cb5c8dc56f143e286405d050e74879fadbe43b8eefcd99e52b93861e1f348a527eed063152609169a38d22694e5c61ac097187dbf9322c931379e3ffe1d27bd9caa3381680a7c5a7ee7133bf4871d39b80b645e838f95a3678224035961f74cc616203e695a9587809070beafd020484a695c90288d6b9dfa3c162302226833e7b95d188e8a267d2ac29824edc408c9f6a029cf838ed41f45cbded7fb3935afdcfa3e4962dfa31595d34bdaa12bb67fdea638d0c236b3876bff6571f1b3a466309212c715830a2a4fad95390baa6b862f4c348f57157fc617400e1ca271ff325c4e90e6926c15c8f44e8a75579449d1185273060e759817f183f7def8f312d2e0e58cfde81fd6e946de5bba0e9dc3293454484550d0350c1d3ae2249d9dab18a36b0c5d1984d948a932cf3bd2bffd1f9dfa168fe82deb866e426404803005fd9587f9e07f9873c21bcc8199bedd19cb68994d1c81f81b40ebc05a5d1e3343352fa35afdca910d53f9d1567f1c4b8be3d045ea9d6edac8f99c12e80f6c0518e52c5b371500a0a39c0717018a6ca71a466f0dfef222865c8d2047322c84b7ed50a71cbc83deba432cbc6dc7a6a5239f49f09c5ac2a682f600ef98ead5b4dd00d0729ba5c21c471af8ec1214b6d2e5a8b40a0b359342f358343c8229696543f330763d24fc22e531e4a93bf5450b45e99e7ec3c7de866c9fd086d0aa8aaf17d08b2815a53f09f4e9045e138f513b9a31b91a9145ec9fa5615eb4f5610639ef3700ba8134a0132c78ea3e55db63c452df4af4a80f5efef6edbca5c1fe3fa1bfcbf14c4fdbcb719a1fa255aedf5d6e7e392a73f0bfb67b5c5449aded6e289f518277583d9a493a45c7fdfd41d4c257ada98d1acd30aad63fc97a9c23b0fbfc3ea1e179393311001adc3e79d4012834aeb8ebe96d1b2c4eaa27a44145e21ebb00fbba11316eac520ef5062e783a930bcc5e562e655e04e98706ac87ca8d2b6f267fd5db0151ae00d3e1ef4e85fae4c29c0c4b1495fccf93fb305399da6534a3baa4fadc4872155c7a9a847ac2b6708fa6afe1889f5a088c4bc062e788fefc22640990bceb94001d10fe50d8d5877b21a90b732140e20573737f37a524eec4ae883a8588d4575941bfcbd04ddc63425ba93b040180d672c10292e1d094fe9696f92311e539b6d84cd7856d075b5eed629ce47db5055f0e5c8bdc30595ebdaa649c6a4288e58c833fb8fb07bc9ab3a09053b278cbfc54ff01054dfa23e1a1e2525fdae9b90a92e07d9ff52057783777ba472938a9c44ca0750ef27c962968e0da96f1ab2977c44881d64ea50651132f2eddf6005dd23828e64880e666f1dbab84c6d592eddbc7b0d6409bcc4ee2437ac359c7593d5aa0f106d333e6c4c68c5356686a560165a9fe49efdaea9e634a2a3fbb439641c2d6eea45bbb835ba9979a69a5a7c99d901cea06af48b62a36c918fa8856cd7a43c18e5095500a498723032d6eb8caea5b3eaddcc33d83cf8308f4133657d3198c8543bcecaec5f6618e6b5fb07f6e94373214398d6dffecf75cd37ba20ca28981382792256ddb5c6bfd8d2eca105f3e9cb263205b7ce7fdcc49ba0db3fee0800fb5b372482b4e84a5d06a530d6dc01accd34d2ec56e0f8ecd1512451d909fae89ece8a84e505c793939fb009742e524022c0203568597e32280cedc7c655aff12a95251f6eea6b51ba6813021be79024db8f4fe0c4cb45d9cfd03f69477730cfb1d10c586aa0bb8db3e088306288ad364cd764b9b342c5af58f5bf72e04ae59a9ad08a596b6b252f7985672f9c5fb28a9d614e10b5a4082c9b280fb5c6a656c8d1bd7b9db9a12ff67bff71d5135b2296f8df4217004b9afb120cad29d6f701467a6dea1aa4028d10643e1796046c86b0ee5dadc49c82466199a3de64a3df87d2dc5bba585d5d5e8736d0c4819bc988d68dd82ffe76554cdaebca15a646eca36f5ea3c374337ee40cd559c7ad5e4db99eb0b61ee9717e473b3ef5ca6a3d58fb833f8f68c440e8f1502c519482b015636934fe73c1762c63f49bf88aad5dce4484aa2af0cf22991e2edad289dca97e37ccd3b6fa0569d9ab2adbd3a20df292d8f1a0cf5913b70cbdba135ccb4c82434eef32a266d588e8be0d0aa3a3f2e3674e69e18c369497942be4d545a64ca930253a4b8c3eaf8d505c3423cafbf58fdf73d741079a5ec62497f836cfcf2972d0df03f44435ab31ff75d45b05dfeeb77ac5733d253d294c8400f7751c6c32138bb950fec1c52bfc76303b71b8e64c21f1796cbcbcff9e630fef57258048789f40ea74abdc9a58b19ef305dce66781c2d1ef36cfdd91990bfb26f3096ed866b0bf3187c8d802f4e5c039db4cb480a8b5ddc4ee533135d4dca3bfd9d9c7cdf89104467c02757de71190274f9c828da3fdb2a83c82889d0a52b894dce48b5488a338b6258f5c7c2a758355a6c4740908e5565ad78e1d5ae6cf1dc74b2cb43efd9b695744c9a043fe4f6f12f5012bb37cc2c843ed81c2866e720939d9b7847088dd0b2e53c3d5f04ea9cda66a157c64ccbcc35204dff8e10582211dc8b30bfc27fc9d78d05c27e30e0a404e77caab41764ed3f0710b96398e878a3b4cd291e60c34bf7ed48585af192862f5fbed028a5fc008661ab6635f4e2ca60a3de235676dcdc12804dad9fcacf46be92491d1c0ab2a77c5c55f412abd66c4313f75869feac926edab0adf4d9c72287857b747430146889f9702f6636338f2e4d28d4064efc66204750148d449ce29c10660e72ee2ada1c3a82963055a3dd5cd1946a260fbb940378e2caf739976c92306415988e6f9ca4f3fbc1ab10f8c97a648ed7c515cf44efebc52d3ae6353e3df043d3ee71f62a281acb43af5306c9a91c2b3e7fa1face3cc617700719280289ce70176a6b11630ae14698a89283e497669cd2130a207363988336fb6290ac69131dc5e5995f0d0a7a2535e6103720adef656e3330aa5c0f00af4ffdaecd472e9e531d46b4842ac37143fff915c8907232bcc54475c6bbff3b0c82164511ba304e69e94f5822228cb5301563fa9be4863fb6d8d50637f91664238740987441969cdbd83b8dfb9e6acbe11229bcf443e26760c39141e3be16c91425da977bf2affdeaf67054718e93235abb1197ff701d1f5d530f375bc1ae000ea741a54d664bd104ccc41a9645f10cd0dda83f4be35923c36e985994086419377e08140c8f1d7deca312f3f459cdc0ab2210a53af3b523a641d574042ae8c25271fa80d026aa0d41b82797e76cbad46af60251354f055ad4b1cf085f307af14b070b9a0aa2467b860481fb8a8867be805023cdea52aa96d00c728223b74da3d11deab4eafe53d65434d94dc7cd64041dc327a2e232fff815abfd951476c343e050c64ef40fcf4ca825a255b6253ac74383fbfc2fb0128653b003d6e92f27e85ea9c579967c40cd38b72758fa7f17e499779ae7974e66149ae2b931b6f2541b432fcc998f020e92f823baa0ce3e3ae15cf00cf0406ee6da290e9b45c6c0272875d1497f9e56aed3e738fb41c2012239f81660051a83264712c94e82602cf52da82acfa04ebb5e7316755bab082f0296c25decf3a7683f8e90575a0deae4171404fb88a63d553215b51797134426204282c5d9e93cb61147ff0d6d47c5fa95c4c9b724859f5c5b63363963cc9feb8a1a6a7d8bcba953a3c73a9773a189e1995af72d990cca43a91f5d8faa95ea8c4843263db8b454d852263d2c462f0c61ac95c792141624e119813b289f1623a055f055d2466916668c930024e283c198e7e1c122b955317955d981f5706b575c4a5094d47172ab106d36be1623fde095ab4e5f06408692de700aa11584774e012a8dae47564a569a4a10338bc4c81891f5cca47fb6d9fb10baed281bdd79a06567908ea8f37c402b3e01483a099f19989b8eccce477878e230433224514b31996c14cf38e6c5b9e5849559be05ddc6d89a6c1cc23e42c1812f10f2dec532c492cd98a08787a174673fbddfc11cd5808d7beb0a4e2bc3401e377d0d71699fec4d0532c237e1cdfd47c03c8e2e03729bcb30781e3a11c2e8007556b544a5533e4a4ad190d5b2971e593b3c64e95df80d0f4668bf108f01064da6eb5da7bf8cb6fee1257e0768a205e1764184b595125046063e26345d600196cdf5d42fa06eab714b648377c2222d05a3e90c86f89d0144e6d59d18d0864351e59b5b723a24ddc6954408fdaff7be6ea8b8154fd6cac59e2511ce4e219bc2c5b6bab2903aad12ba021f61d7aa71eabe4e3de1a0b154a04abb27f46297e47d31f0927f941c74bbe41faad206d89a92d06f4abeac748d1a86f12e8af6f0152de914b6da9e9e51492f3ccc41c78b9ecca1f518cc2df868359fe55b703c4f9ec868f449b75ac3eb06f6874337e5a2bc1bccc87a108d17947a6c238adef603bdbe9600927f6aa92c2faf57f62cb1e93e323521b5f4d8dae5fadbaa4b7e43e25f2b850a74815397f195cb6402173faa51352cb4e8837c941e450bfdf7e43e3e4e527f152e7a693116fceec3c040c010fa0b031ceac4ff66285c1ae75863fb0b8846d52a4c9c4a5286df9d390b6302ba5f59640affef0cc4bc3c0505efd699d2bac6a1f0bdd69af833ce27e0b4b4bed0f71acb17e9ff8ee45c1a7eb6b4472194278ef480c9079661bd4a203709bd89570627fe18dec8a2e80d4e42980a5b8285944540b204ea2784cdf6a4ecdc39353449597da44ca54c6f8f4708908e46c3bb3aa51ff8db6f585c384ed92b903f11376983697a558af55c54ac88a73121be1574d194053eb73a87d474763130eafa5871719a30678282af96aac8f8c79c23bf39a00ffd17a311d29a16fc11f2ddeedf444cf090241bdbc78a287263032fcb16a135ad91d156b4057a86146b0abf92013bffa59f77f1fb4deb5d9480898336db476f54120225a7b9ee0f4fce82e783b83822ddc8999aa2acf39361f0024efc7c6a50d0dc0e00b2031ed451ae3aae291902649786459c006491a910be31a6b0921082ee15b527a0c0d6d22b1e5d7ce6221722d4cf342628c93d682d3b77fb86cd7f7a85703d2a6bef0fad3f08992ae03a0ede0553c7df8698d7bdddd37acd218a3b45cb0cb1ee499b8e805c0889e8fe1d10bff0105737df85157f3c3383fcdf6f566207b212f07a7821ae0e626aafc7f78987c5ef17d3e0adef6135b09b4b47920249ff1d0b8ba3f37ecd249a7020c365907fff65941e66839dda375210c43782b96c8473e59832f9ce3b97b890367ca6e66a4de642831d05c5120d62fd24fceaec804f45b72a813b9c3c369a01b6d006698f50f45ffcc3ff3fd14704e22a16a12ed20e6fb9fa3b279fe413da805adb2da0eb95d85527e425a9cce671677a30bb7726639d6b80fbe7fae235360875c88d373f8b52e80019fd4479c0cb195c254d45a537b9e47e27d2ef9b1c2b5992adecaa066e0efcc4e376cc9e8d99e09f7af1c76016fbdbe91710483f6e363660a62dcb2a2be1c79b2d9079210969a45b3ca36fc6501caf10a21cfa360aead1aa22be0e72a32b84f44f22e517c8e70056da007be6b9a07b9200addf6b72ed67006d2e932fb2f4d0b8bc93044d2809611f51421060fc877b3cb3edb1fa1cb6456ebfe30f9a704f47f0562c0bdbcf6ae93f482973b437cf6f90be90b9c012d7138e8df2d84b518503a3a9379cfe2e625f2d8ac4b4c2decc0ca54112cb952ca3ee835e8ad4e597912accf18d15bb391974af963596720d94c25e61030d6ecfc936518530bd462188be7a91e20ca69576357481b94934ff4403f38ebcec41016292bca874944e221806365f9f9e63aba1acbf653f36704ab23bfbf4a6f626967bea0d797681d7ae66cdba2ebebde6c3964b669ef51a300b144c048a0e039d5c3016564d0779e4928aab07936a76024295dddfec6f0249cc51aa8aab99149887b7ef0dba0ff494c87e994443015a15d5877a9f1e77e73e5f83baa0d509f282ea2b5e0da3cfa69fb83d7b118f98d070cab690b6775b937b4b29934c01a109c409cb09d569dada5089ca4e3369fa9134a548ba1621ae3c9d82c0281efa41ad2c79ba1490671e00c175a2e2d6671e0001877bb5ea4e54dc7eeee9819297ce26d4b41e8397637c7e54f550b1f33685ba47b8bc91e8e6a42ad098a17291d19262595149898182ba27d3e7752592e5b64a7ba4945341274fbe4a01fd4be184d1f70525105edeccc9626e4d3003f469c8939d3986038045cbbe42ca0e4c25d44aa3562aab2ab15675d11e003df750a1f30178eef149f237c631d3e7923b96667f639c97663c236d5879f034e31820a716c7945230b8405266b3d8dfd8486970b2c207397f6c851d5290ca38264663b2269758666ece592da5cc3c67cacf18bf63ce2e28f374574132b9f2524a5242fae9f2673802d156099556ba844a89e6dc39829495526abd9bc223a53aa1d24a852a7542a594d24aa95727ccb905651b74fc32f2ecfbbeafd2187145b5fbacb5218d3432599604ba12a952c94b9d57d97a3785474a672d17b7061c3878de7783ba77796eb4d68083e68b2b3f93473f12938ea896b35c0d4aa552a934a94f52a9b4f9a43e49dbe693fa246d9b4fa793c47147a85b9342ee2d770409833ab9213a99e9e299d784bd0c371f9ddfaee0156f3c33088bdbac57bd3581616078266ba34d98598bdb50afae60715bb23581a1f8b9c33307cce25e1c2c8aef176db2661543a489c546230d91864873873477ec108934f4e5b8db901d6ab17955bdb92f07fdad09ccda3627fe2991226d9c8986a4d003dc3659eeb87aa573b958731f983367e2cc3939f43967654aef3dd119e4993583308cb5dc0cc22ba6b32b6fdc385f359cae6ed2598598734ecaa59d0a4e17c7b077739c734e2a85b8d229e55aa7cf5a7bca90dab0fc64d7a2b6a49452ca1ab6d0ca4462baa494524a49a74e5d792ab74dca0da68254f54a697345aeac4cd9eaa020254ac66b34def1f6932bc316aa41504ae90aa5f31503522ce5e219a0be4129256eb462324396afa1922dc7068ecc76b37ae24c8c5e39bdea1cfd349472ba7edc60af95b74330b872e565b33c3b07d6d03034f3355f33a7599b900db6c1662da64252ac1aa1e3172ee4cb0fdce6daedc1312eb83309c442330a6e69bfb5642148f9d0c067c88fd77b6cc1438b209de7b9fb16ae15e6c1abc0942527d9ced96bc09122a37af1c9da615aebfc3a7ed0c1a3c70f4f483eca16a9fb6af5bcebbaceeb9cc11ed81bba4d5a6232813d78fcb8ded00ed6a5e3071e7a557778c21050c11d79a84910e0194ee9d5bdd665264bc74ab68e6b650eef1af2d09173bfd7274b25287df1c30e3ab8cdd9527b4b1e0e94069a1a70c8ac5256be9a347449a0842b49f09dacfd2dc073daa2178ef23f1228815eed10bbf74a8095ec90822b01d76ae5bddae18a2bb92366665aab5c512aa5f45a6bad5cd10c9d6ef9ded30636533a27a5ccec52b2a4a62aebc4ca3c5ecb3de665496511f7db6af79353088e10836b02faa848620485ec04048a98cc153725830494e4f9c147ade5edce60383284233adf5eaded80d450226bf1edf652918326bee7eb4a1f5f7a7280f3e0d58109414580a0081a1366f2074494ba97ee09568b18a040c209ac88820f0a4010849af9e3e493f2896fa72daef06dad670486821d233230c2080f4cb063849653336284e33272f432c2e709d7103760b3274a1082278e78596b2d0a6a1386ed134b1471c388a109c3f61e21e374e3b68d6382f61c4e0dc7711c97928aedc478623eb1598ce631a1d576278bb91e2694701c67690dc34276a861f8c77b2305b59449878930be266240c4122eeab9270649dcd4161d8b9225d729a7b4bc00f6519f254b666e2694254bcaedcccccccccccc2c5972e8524ad9d3594e72b5cccc3cb9390c194cf50a89ceb628a58c0d5f715c3856904f2c25b3da69c82c954ccbeea4d0522f4545259dce4d347dc97d6ba55bb753550d6db7cf9706ea2c731cbb0d593a17d2c9a38f79398697f04821414a0acfc0dd729331ccd05f7d47a532062671cc8ce41891d269044ae9e8e3a51581f792524a79dc21f8b63989542775dec12a4d2e8954e7141acac1d25a679d938e97299d3e9da31bad94d2f9842221a517d43633b34c8295cc3c4941b89bcb3a799ebd94041b5a599d74d2721386652d275b6ad1dc5699dbea6c1ace4d360ba6e86c8b35bc1a2fbfc287cf81d5fe067eb77913f4ef386f822a7c673b9237c119df95bc09aabeebbc09ba7ce7791394f1dde74db025f5ddc99b20cb771725064a8c149515969616192e2a1a34bc09daef54f026c87de7de04b7fa5de8adf0e2b5f0c2420dd30c7feac61c6e61b5331c2995b586b56e9b9c4d2927e9132920f0b6bd9241852cc33795620447dff22c85088e3a50f2391b500a5bb306881f35f3fa3851e4e5772387e9db0eee7d140ae5a890a26edcd092bc9b60e79d0c0ae4f297fc05cf5556bc395f0157a04f989490fb3a31e455cf3d4eb8be6b4f9642204a4ed3d50361eff9078e3ffe43397f13052574c1c18fe236fc28a18c21aea3f84b70f4fbf751eed1f48fbf608f973eea9bbc5eb5e72492d5a1944329f4094372ea16745a633dd01ba78435a70c19cc00bde14469d8c3e979e429fc38416e02f40f76e07aeed1c1d1cf9e7b740083efbcf3e93226438d7081f0806441500d866a9ad8dc03120a2120d1aa696207c9376fd781a66d741d68c32379e7a3be049c9ea8643685a3f492571ba47f9d4b1ff53bf7dcf3ce9bce9f9c9dec3a972fc3c30e4c2ead1e29b4212383903b324d478b72f290711d5acca1451ea21b8803a4a96106577a72ca4a2778805a0d725eb39e18cc6cade70857a553a645172aa8eca66438f2d00bcd5f6851929ae84cce2fe30b3c55dc7106eca60914cbc99d03b5fa0ac19d27967007e7fc9d0964274874d1eaa6c78514ac91823bce8085309ed95c12c12be92ace2caab8dc417718cf4cef7c829babb81ac8132153b5f5f3f35235047d78e6ad9f7e5201c7f929e158bd6f066ce440ed7970e08ee9f2597a0477bcb419b170066c86102cee0e3af44ae66082eb39e8b043e928e5ea5c2958e9a8f4a42b936b6216670147f92c2ec37c1a68e49c132cc57c70c719b019b01933746610e98cdc5007e533603c73fa9571062c059b016349149c942b853303a6e25271a9e0987cce39e5e98867aa4b2178c5b53c3fe95cda77c4332757e1594988db6a99e2e30d626b283ede202f63f8097418c734e8e39a01537181b6c631b254695568c20c19f54aba8ccc0b3cc373597cbc2c20ff0a38a648f0d255c071024901474a830f1c2b901828d303f228d007a9e252c1b1996ef21f351d07bb23cf7468378540744d6a3c35ed1df54e952272c7d2512ac81d7916c6c07082a10cf4e1189e3baab85e25470596d32ba902933c77fea8d2a519309ec98163a4cbeb4d50ceae298c4d985214dde71f38cafffca84eea2e271e2d8e282ea7d086fed30fad2922fc5c7d107eae7070cce4daec29a969fc31250d83738ea994abb587eadcdc408e3b45e52fa015baecec4b645a466e75dbea27843b9f7bb2d8a24816410dcba208b287da13904022e765c48911d20548a8a0c24812161c79d5c808810e31d9e087ac8a22a048e2e708503842411656300008532801089a841105a0139c5a1f0f6cd0e0e70450184901d0133534877612a2c36233bdb2b0c2b3f8416bdbc291c7cb8741d9cbe2e75344b290e24797a1a55688e8a15288420e270526242b900882a5688284265434d9d101cf4cb7206d280b17d36adf91bc176478c63a7d592a13725918006651a193cb3f854c43afa41075153be3ce1a1c439da6a96cb2fab5727fc94edc6d600a3c514b6bad73ced0853affe43cd377bf7bbf6bb24faddb8ef34cdffdeefdaea94594517e3798729c3ea57fec40bf4b8ef9d5e7cbef269d3fc1fa230f992f7935acdb97b55f5ceb15c5a97bcd8ae1d469ed4bb7e49576f45205cf3d58ec7c476533a526971c93037f72eb14e5a7cf4f4db4fee4f70644acd32c3eb7426ffd5404eba72110f9fbd7bb18a81415d4c9647a79e104da5ff9e2f273de7d26ea3d9c3e3f81a3947ffa164029753e9d420b7ed6bd2fc7c9ce5e7192d3e2781dc5db1b86e299146fef2278e604f6703a790a28ff9b22063b9d9cbf1c2747f1df1b5e37390adc71fde4dd77c3e7272f7d396e780abb9417c6777e025b48afda63808d22bbf2fad82ff9946801293f812da1508b394e604a1586340c31793bd56971c7c9ad37e9bb4112a9fdb780cfad5390df7e394e6ea96c5ab75ee3addb94b7f2c5bdcef90a93cba57ee4e1fdf5d1479f40fbf5bbe1f4df8385cef392e79e211cf9cec55b697247f92a6f85c9e57c86b7d2e4eed8419da3514ddc9bc60e3c2abcd6bbdeb89a42a325d3a0a6ed4de0f4c071c68ffd7a3dedc7149ac38f54e6a9abbcfc8a9a5e051223a4908288eb598a1808417996220643dca89e7b785cdfa3f3dc63835a8ce79e9e9cefe9e9e9418223bbf9f6ae48c7d32501fad1b6a270fdb0e493d8fb331228e43c0cce2fe3d7fa761d7ad5d45e1d38887fbc9b7a49bd8ea9878379d63080d6d504684781216b21b5eb3adad18e7694b690b9b31dd974887c6f45369d5e754c488b2bac40a44334a4c5306c81e09297fb900a2a14d568d058129b31a34848a582b9b82c71c9905174d4d24234944abd58587456568a5a2a2ab3941422a118318a6028283414aa762fd1d1e91413f23645aeb729ba799a260ddd9163f2cd0d71464fede586b8f65fdc986db5f852c2cf92b33916c6711c9192ac54a474a4242b1519653ca387511da394921bd4b6037a1ddd8ff42ec977a74324d6e2587a0d291529d5ba234f7ad5ad5eb14b90dd4b552ff54c7a224b1dc8c3231da1e08e8fe76e882b25389276be7924c9be3dc8f5bc13e2d6700fe876dedddc9550af5ac9b733d801c1000a0183c70083f3b3d3740c30c000030ccc30c0d00575b30e68058e9687f4e48ea517cf975e2d8e1d2ca7c5913bf2ed54e74b2f2e08d6e228f3425ae41e2441dfad717375321ae40cfcedde8b67d8f582c574accceecc928b6798c951271bf2a427cda2357d4312d285dc495e2349c707fa487e3ee6b9078990f780fec5c74ee6ddb82c4e8b2e78bb7db5f8420de74ec1f8782da73361da6b3837abf4b2332037c673e8153b0b2e99b6c5e0b640a46bdd18b7d12b8ea1e10517c0d26b363b00c23105138e77b55ac99517bad8f0f6593aa07168011c99870570b43fdf5e031c6d916f7f015b2d29e42bac571d13d2a24cc2f3af00da100989c58886b428bb78fe10b4211a42a443a453f4a445a9e4f941d0a6e849abb504a7458984e777d06609ce92d792979216e5119e5f05d0a648c9d05091518bd208cf4f03b429322aaa15d5960869510a3dff0cd066899025b125b1a2a0162517cfaf026d8a828a848a84725a945b3cbf0b68b32407065b72d3a2d4e2f96580364b6e96b896b88a98b428839ebf05b429625274547444a4a4455984e74f8136444a88868886705a9444787e16d0a608e7f52a1ad2a2cce2f957409ba2213a3a4f5a94b4e757016d889e14b58a5a3f2d4a2c9e3f05b421fa99cd88825a944378fe18a00d511091109150514e8b5208cf8f02da14e514c18a60402d4aa0e74781364440349a518bf28ae7bfa00d9151ad46c4a4451984e73f8136444c888e888e84b428ad787e136853242416b329ba6951ce9eff036d8a6c8a5c3c23bfc8c531fc348d0406b01fb9a1adb61d7dd776a64c3da61e4871a8abea0c69e2d6706c1f57c3266b1ed5d874c126ab93753258d7753b2d4a6fd9ed74a17575456cabdbe9640b0dd568b276320a16e191b37d665b2d8ed3b5e3dd2e1cbdb0dbe9d5aae784753b2d32754d9647a4991e4d96173b9aae6ff78834cb3e6998d86459797324c74e265f0d9bb67533b720e9982c2b0ee58016c7ded857af2c6ce75bf6edd4ead896b5ad27dcb193c95a2cf2b55e681cc0f8849aa94df2f2e74bb3038c2520929c73cacf66737a5377035d483870389c5b39c771a39d71cc3870a849b04853633c43924e6733891b55e54425ac4bf758a43f6af8e33a2f81a46ddb7c6c9fae4b3fdb6c6d1b89c4d6b33f6a5cb6eec785561f191d26123bfb6694721441b4beddd2482d124eb3a88ca300726454fb9611f1a33df25a5104b1821f4937b4d280825a3cc0f707d4a2cd12517a2780953c4d8a115e4a99a4e4f3d25384d84a4f79bd1c51845eb68fd04b9273e0e63409e823485d7b1f102b8ee8661cd376d63e5f8b637a7260f591d2ce3aeb930a722517d234d7ad6edbb4244b224d93e77194524a3fefbbc17d5fadb5525a2ba5b4565a41af57305536b160fd6998f693cff5f96ed49f5ef9704cbbf5f99e01595a900f075a9afae26e4d29e8d18942a1502d1955327d4215861ea8a085041b781965a8974ad970942ee50bdfedf6bb772ca53b68f78ede7ccee0430f720adfde523af521dde5bb41761c5cb62e7df3176468b1070f7cfb267fdb7c051363a3c5ee41be7512c9871fadb750fc0f66bf79aac52de53f42bff98b8c88facdb9f59bd36d335dd3b79bbe5d857729397f07ba94c21b0ef0d67da07952e892e300df7e806fca73339370a577872d1b9072c2dce91de0a8a1579db2915aa97c655f9092b9d209ee981a398e67f8bb0aae054d4c26a152d48552f0a74b5801787c14993c84909ccae661022dca2a5ee680c30d397886924fbfdfd998ac1af69835c8194a21053886e9a470477f0fcc621d3de6ec5007c7d0e04adf668f1c2e37d01d2e9df3979cd25ee510e2e8d51577641d5bb5d3daf072a1afa87c4dce041db3a59b8b63e8b1c6cb71864a5b4b22c9d076c702e44a17b8f966c3c6dc36ef0f07f9241b5a1bcd711cc7711ce794e3a95372edba39b802c6e5bc8795f7f14ab86ccdcc410839859ae9342d438b3b4c8e3776798d2c056166b136dfdcc53adfb86c4d7512579d0482c0460de538cec5863950e7d7e1d6042d2caef17c5aec805a5c29a55cca124033b9cba55228e7c0d16e8cea482892d712365d29d1d24aa9940a5740150ea8ba71a5b0a8801c6ccec9bc412177c66d0a21311bfad2a78666813250f6f728e503ca503354a51555b34c8e504f440ec585828392c3c92356cab8f07cb6bad5adb7962deee8f978346fc8f3f12aadf4bd066591962677e46035c6842b6b076b9f24497e60bef24dd57a79cfeb6213e86537a3b49bf54a76b49793a56ab9ba4fa657d3069251a6ab5d2be5602ba8b439588b9372de583ab25356c2c1a9b55537aa9b9aa48d5232a286bee552ddf4ca066505b33818c3f00c7dee693f9d4b01b3a8e7829fde9556dcafacbc13eea86aa95c3f5b5c7906e3ae30066c683c0d6d687c87ae1a5f033c7780e73c616c68a884e7ed4f5efafc6c94f0bcabb3d6cab5d62a6598460d3c6d6a636bbd9235306053e36d6a3c0d757ae511e9d50465a00c2c42dba98db9f1fc94992cef734aa5cc694099c854ad0e5d66b4d2dad98e340b05d45b7b9b08eea86a793b9ecfce6471b01dcfbd50ca444a294c75a36aa960f47494a06628da54b53c1d938d37f3f1e28ffcf4ae97b86327d4f9c04ea8ad522e06eb95fdb92307ab615cf923070be3f28f1cac619c10efa745978af23a239e41f1983b303a29bcf440f092092f7d093c13c3eb96c033fc29bec2b2c2c292b2a14bcdb9425bc2a5a0ce4fe7118cfdc4f9110c02be1e84fd4c809475185acf8763be0ae3b8c9da6ad83ab92dc393d1963b1795c7c1e04aefee065da88a86c72971a57b5e672f3df04e98a6ce1c53bdcea041e3bb7125087e374a6eafafd078a632f3c61c13d2cc0e1c55f8d429a463a7c3c538d80baef44b6334542dd54dd7f80e55ad265c935b5388026a98e91f4ac94f6f4e48b3be1045eb218f9a793f57c6f0e91c0354b5268ccb3d6e31c6b3b77c7199834d28dc1135b320cf77dd99302a78b6c955e1bb41c331d363bd9a43bec91db71d0ffc6ed41086df8d6ea4fff2f2dde8bc060b2c7c372ef538180c4d770000c077a3736ed105cf85ef46774291f1fc2da0fb5a6eabf35a6eab24b7dc56514e4dea00e0752eb8f0dd48f12ec6f398ef46ca57d40a0ecec655aed61a6ae8ba922d953acbd5ad73706525a19c2ead2494d3557112cae9ae8063a733f250f17c5452a64802cff3145d08f9013c4f910411bc14afcf139a3001f002f0ddb8b2a593759d4c0874f10c8acf51057b699dedb69d6baf7e026541ba9173126a465a99f14c6b3a49d6abb6462da6f4aa556aa8231634b969d6cdc7132b8137e361c1ccb68ba8faf9d5695a94b5d8deb282bb91c0913bf9e6dd77e374a25eb7db09c0b34cee006470b12bfdf35165030d56b56e385e55cb86cc84b9735eef55af5e812ad754e55c6fa39baaa58aa95aaa17ff55bd54392a98ea4685a372a95eaa1c154c7eaa9693dcf4dda07efafa92406a51ba189e94c9542d5944ca68523f17d5ce42c1b85dd78e8353c72adbe8c609e9d5f452104e086a86aa4dd40c25849a01d150422825a8a10ba2805041bd9a281a4aa8574a5043a8d913577a95d1f40a07bd292ed469268b36013cfbc5754d96cb1702e1f2800eb82811e373aef1c2ce05a26280a85913607642ddd04bafcec14017ea392490ef908bf56a965a77e4601c8c8bb5c89e8fe7e3befa15af14853b82af9f5d0b4a4617d4a29441bd92deac0930dd044ea79f03bac6f3b1c11d51b351d5da761ee0f956843b6e3baa96cad562d7160753e1348b837130556bf3eeaf01b5e60b51b326dc2a65edd3371e19a397da92344bd5ea2d1cbd47d18cb088b2623bc2220a8851b32177649e9676c2b03c6ed1c5061abf425f764755cb858636b03c66d6e7ec230e98f5855c6cc24c0f06c54fef6c78b248013c2fc077a34469839deba2bc0bc11da58c3d1f1a79e44a67da8ad0647d3bbc814ef06a82dc889ab93619a1679db8a3e7536b7744cd3a2f79e724ef7c7a0dd3695a6cc7e2a7977824b35033d415ccda7626cc4e11beed923b7230d46cb2a40c3543cd2805ab8b63da3ff0f50db3ed38311d94b5ebec5ce07a472db2fde11b650658af7e700f8e3fbe6b1e13940d5ddaaff3e37c3836ccf78c7a357d5a9c1d5a2feee8f9a0c0d133fae92770f46a3f83fc36a2664d9b864a98a812df567bebda1d2ae139fff423b248af4ca1aaa56a85dea5202936639d7047cf67ca2377e6b243b8f467d622ad451a24e40db167e4d53c26de9316edd7fa6e3e1cd54b95d3aba9c251dda85caad667c33de8fae2ca1f51b3d1763378dc620d0ee6c51d519e8a83956a13c6a7c5f67c7e769eaaf553c633346c68f0a8cc000740800b2fc4c0000098550b31b02013801a0218800c04105f566085360a3033034883010e8080ad52d26cc9a5cefb4c2b2a309850b39feeb26da8190ae8f5d351b416533f1da58582d32c02a0825ae41e24b87eaa5a46b59f9287891f51475d8f116ef023ca0daa52554b75d3620bf909f290aacc8902a7d6f664070942a9d7224b6ea975770de71325a1ef1a325abcb8638df12cc1628c245894c12b9d52ac249b2cea3e3188c8d8f9b6826756be6b3ee4773cbd1a8fbc74d9cb1cb866e3429708509ffe80eaa4b0034de8404b432f3d2c09bdf4126db23a4043171da89768355687742c17a14ae48656aa472a4ff5a940b416c7d22ca8c54ec1379d4dd66bb25c74a040ccd0e501b30394fa8f1ae9fced40c3ee157625a1f943d28a838e5f4619ad9faa67aebaaa8c678a90846a1066d5228e48e29947196c58e2e198520febb490c411ed0395e1775a82b89d124b39aa3ba75a89a8458767a4773c2d4b10fd51d772aaa51cfdf453edc42425c6332c3219ad11b420f4d3dbc2d2cb323bc948e9e5fae9d2a403233f9e725e3fdda4839f4e41961a25f23ed01f639c7262f0f46a7a4b10b77d8cc1a3fab005c84df5a005570c8b31a4a57a10d25a5a7071cc74256efb3863f6d3c790f6d367cc78c6fbe93380c216cfb0fc8cc1f393460b421406af09333728ee68adf4aeca57b2061cf8b9821e68990b2477acafae5d6d532e6cd9a2760bd01d2bce4f9f3507d6626c48d56951c6b548c56101e3569e5a577d7271817e505b3ca1352e84d859164690272164caa140a58b004ce480052808028b191648c0a06b5bc3ab20a2c512b4f841ca0043461c01b9820b9f9d3e82089deaa6b4a8810b95822ab105d184f4cd7e71e7edb8c794cfa65dfa9547e8b8c50c6cbf839d59e898e5cc334a8e087794912faa54a79edc51a6fafc34cb6b980ad4f345c94f3b6b6dc63afe4cce9fc9db5f9099df6a5cbdc93f93f5dc0496fc03bb0f2c914a5e57f2ba9257bd2b311d3de6a13ae8aab4de8daf296d79844b9dfd935ff76723bf6de055e71f48720f2ccdcf86d9f9b4547ab27a092435ad3e9bc052aa30b9e34d69d14bc94eb59832aabe67a883dab66db2d65ab7e97293b29581ebb5ac9538b2d52b97bc39e2bcc5e5de07fad35b4c955cca3ec89ff4bb41793669738b6bdf07bae2991c30cc52808499ec13cf3d39483ca77031e7a4205de205115a35b28f20016bd500410653497dab5ea9e42455d244e7632fdd47b3640dbda2b4bbe7ecd960113bbceb36a794524a9b148e3d7e47fb66c71ebf853e68e8833ee7f38719f6f051c475c9e13269afc0e79e1b267ed2a03b72cd87cc0f40b4387a634a8f5a38de9b4ab9af565c9391f9c10977e49a8f9acc913d9adc916bdfb51614dfa194a522f0900e9eb93b1c736ae79711557bc9f335a06bee6cc2484711b9af09231d46254bd7d1e387967c49988c491d29933bd247ce244d0ac92159bbafc9d23161a45fd9d599e178631c8e17f6d2532a498b500166dd1dd641019650300975dc1c30ebbef815c54bb727b874087787c9fa5ce6903e3d1c3aae29533a3b68e853b58268b2a0262fa0264f276f2288bdfc9edf023611c49e0154c61036b0972838fa7712c4d12bafef9186916ec31c7a255dc2bb63848f361d5c468244ecd988cb74c933d43d965c949cbdbb7379ee912177b4b1b3832376c7bbf3f2bedca58ffaa5fbcad941023878c6869ff66050070f1d5c1d93b55ad920eed8cffe77a7c519a6f0a0dd1e38b7e9975a5c9fe1c8475da95b2ddad1ace633c423c43359c6da49337b984ff212d803c947fd4d7ee849ee98fa1bdc8d6e5b7de93c602f2557e1e3f5f2eedc5c2125e85821f45ba577a7c531e5488b9dc4cb94165d2c955d7fc97d0f93ebb90655f280fc21e90a0f6a3ce40d5b9c9bf3e23c739f14cbd81f7ae3297597ba8725472547ab6f52dc51ee1ce1e97ee4ac455af5959cdbb9fc3c26479e171e914924ace40d92564e7077e4915ef1d42970683f05ceecabb790d669b1d68e863249af2aa5e1c83c3b3493256113a63a8ff5a9d6de9b4ab9cbc8346c16c66c5a7b6f2a4561341c574c663fc373cf1729f8a15e55a3af452e8dba149a34da28776cd89091b1f6f200f2c031db0bcea5ee2f376aa61e74958936299d07927fa0109f9b3e67d3e75df77d2e41fb719c75fe1cc0359fd3f44772a15a995ccadeadaef22123599347f2498b5dab45ebe2240c8c7921f78a0624d9b0863bfa558b38a9e78b117c58ac7e54fdac757185173b4e20811392f093990543ada65db608e45d8244295d81270a464853c6ec7c7b5b4b29e9a91393524a299db4c5975a0ecd1912021078523220791ea59452cafcdde0babbbbbb093c4d90354147caef069d220a17b46a6813573afdc1b5740797a6699a3a09ec46cb71359c0d946ee0c8e391dcd15698acb5d6d06f9095bba8b3ce1e5bcc309873af53fa059a09d3ce8536e5aa2e0f4239e88acacb8921b8d2473792c14f243f831885a085b735b438fd76d3b084cb054e046d51e4484dc7bea7c0c437ca7777b709aec72dcfb29f7316b1e0e71226fc5c3121051a182184114a5c422ce091c20e381b3f7a444b54f1ed96e8e8dbb7bb821037df8c8401c2b75ba2d6500dbe9dde96131e6d91c650ceb7f794410847d820044bb8a63041af0008255a239ba8f02d3d223708028302044cf8f040043c413062895a8f12889878d57a96e0d39a1bb76db4573d560061886ddb3697b6650a57b69e93b840669a59415b02cf4de9c9026654fbf1ba11ade44ac721071cbd6a3e02e28715cf506f8761568f01f86e2ee8338f0d704c3b8ec9e280642e8608a2a33fd0b00711e46877d0f1cbd8b3e79952e9885b9f5d023e389a3efdd3497ae5d33fbd9af268c2e8dc2de46107a6b1acb3bf48960de5938611250e4b16377d7ce1a7978eb8f37da0bf03c70cb5d4b9d5db47d96ac9aacf9e3da7ab57b25611d98eabd52b996c47eec031cc923bf441be8c63e6109d58af827047d99237d3a5abe58e94316df6f421c7cd81739a9647602769293b629ec952908e9d63244b1b44b8eca3ccebd0f117e432929c2121222e23c9d1f99c98eb321213187d3fb7c8484ca0a48b1feee2486ac8c8e665f45abf2ddc3e1b2f256388bbf967f3c3f6ea4738fae8a79ea40c48a5b8239c043df78061f4a305838ab7932565473f3f4a19f71c25e149320209eb554f700aa928f9d3e23875bebd6f382746081052d2ac9bae06c613168a8c7bc2983a2db64f222db60a94b0ce427abe26acc50de7471ff3a9cb9d2243b2e9d3a86bfd64b6a6ab45971976505fc93d4e8c1070e29749e7e4c95eaf36d29ca46d9b609dd469e6046b0539ac1949590fb594750b519ea6c924df4a38a607d7c7a649591b7517d47be8fb86a5f6c1b873e38d379e737a2dfed88cf8cb3177987b6e3e993233b3fc2943edc82b65f3e474ac577348c34c9791a1c5eaf4ab4b75aeb15ed885a5d085862debd51c533c521881ed550f901f9e3bbedc14d9164f8b804d6d704719a3348aeb330a5fc4a4b8a38cb9b66ddb9ad2cdebe629295395368ffc51690909489260f28c24c991244ca47c1746a779a9d7e20faf573248ce7a05d42b285cf6818e53c814329b38cd923c3e393f69f8d14a5a0bfdece94af2234dd15a6bb77a1bc8f4bb819bc214a62085a74ee59436e8198911b4fb8cc4089f24fc3c2381b552cf488c18faae29c83f28c7853efabb21fe0099011c0d29951dc1868d970cf69af2d52d251774471b3feb8bd2e642a7c5d1c64f972d35d06063daa8e28ef2e5c95ef2255f1cd01de58b49b3e4cdd094384f7eb6361d264a3cd99c9bcfec65a0d17eb4c1b54a24e85a4eaf378f725eddb82a4bdde77db24a2947efc6e9b3a16f39c63453f743f9b6cd6badb5d6bb52a54c30051c532adf8d5e59912bd5747b25533c94182b5ecac792c2c2c21203858505755958584e2ca68f85c5eb4a24cbb16c9505930cdb4b3cedd202259edfd16129084e9c3871e2c48913274e9c3871e2c48913274e9c3871e2c48975629d5827d6c90c37267436927a9ec7794204419a33e5ce392b4d6f3877d67abd3967cf79e724d9e0ce496bdd6aad5b8a9b79ddea56b75abb296783a38176db39e99c73ce3967b595d239e79c738226ae5dea3c1c3f73ce49eded9fd2b3a99006d9b1f7ddf85a4ed3f8a3eb3cefce39676d3ae70c47cf643a9dee977a2e7247cf2315e9393b069dce36b9dcad643a7ec1c1b91c8e2f4fd965eca977ccccecdc7372af46ef53b8b0be2835f9987c4c3f6ca33acf102222927ab660b5465ef4f3821a21f13c7d350c754a7dfcbc781a6366f2094325e3181a63966af58a7a4b10774a167838867a0b903b23880b3e3c233dc8b73043e8a90b3ed3c7f0f5d4c716604f3d7cf18cf7d4c31c1678e80662a192a55a4fe35f88d92ac0d30c22eec81d3942823b72300ed6e2458b55488cd3e188c85adce18eb4787992b4f8d3e20d7dc8bf31e380b8a0165dea46ad947d626b6b95ccb1bc52a29cb6d3367d43414141f182de5a69a5fc99ac1bd270bc2173551ae14ae7983755afa63d5271f64e71e688b82ade8f0aa2218f12cfab780a0aa8533e7744cd36293d9a07e4cd7af5e3c385aa459baa629e479b1ead5741bdea57c5542dd510950e119477249477240fa785a8a4ad95463dea14d100000020089314000028140a080462b1603098c8ca621f14000b81a24a7c549d0ab320c8711452c618448821000000222083d1840178ad17f679d544cf4eb7ec0d88bdd0103f4399c3f3e15e0666235c874b0d11da86a7ea0d393d0657096b69ab36f96e088b4160dc9268a73addb7e11d24cb50bebf8acbead84ebee13c8888b0be34ec60c66b8aa3e34ff05a6f50519f45ab05add721d7ad092dea2458d31f735adf24194da022ed0eb96e4cad5a702546c261fb47ce8e9633903e36dc1fe5b357381075fad13551fa169431f6161ac9a3d7ebaa2334e1284313cb26912bc0f129f1d509c444164d27579e41d5010404a8257d352ffd588209495fcec0782b6e83c4968b00f0c8a7ba40fafcb566e65f9d205d88a0d4eaa725317715e37f8bb2625c36dbe2a9e04deb22d131b83ad88a48ab3edb553f5e37e1494b21d9c92956b7d3ccd1059933a1d6667d765d086745aa5637dfc1223d1b0c77172021b43a7832da887c1a6f18580434fb6a20efe18559dbae6bb614c254ae107cc7c4e8eff59adfd270895c48b0efdcfdd9a87500f52456f697fabdf970f625b0a4a361cd3dbc4380f597df3974c0465a018f00c9d55c62512cc3abdbe1438ad59d71e0d55a103298a0622fa8d5d7548bcdaade66e63be5edb3bc59599d520cfc882ff8709bfb30848c34d600ecdb082eefbba42c22c3956c71c20db3acc13af979ae84a48a40ebee31a3aabb67bd48efac2f7fcb6c9333ee0da954806e68cbe50c6dea74dcf2778b89b960f8d80be66c6efd7bd310757d2a2974a640a498779162e51210a66152d38b6e951f62342604ce216fd92fbb653afedebe3f7746efff870282460b7cc960a28b49443a4124d6adbfe1d2d4a4b60e4f9214819117a152b7454d227d1cff198b046b6ffd7615bcdfe136d9088c6ef99b60327f10b87e9ad6f4cd1758faeb81d9d7a7e986e8de38a726a788f1a4c5d28f40f74fd376ec58b624b005e0687d20a18dd8c34441d697aa933a4da168c9bf2a2b31dee2780d43b551b5415ee38bc79f921669e46bae860edb04d9f7b0bd32cab72f524cfcb50279cb638aec5354c2011e2ccfb8af30e8b03df7dde177c53acd8d2881d07e4cfc79c446990b99b11a82d8b7a45002b8be984b01389f8af73c6b1be88af7ee70c909381147cdb5700b13ba8fc4db1a053a0121727bebdcc68a82c28d0e7f07905a8f83155c323f8d3e777cbb5495f528e146c4a46e302de798deb6a8d8d36474e787cb80d342d5245657e01c3b414f61a7908e2561f0ce012e999621d39eab63641f24dfefbb539ab1190c34f22998e65326c98f9e23c259b48b61cd634fed6a1796cfb181167267f87e6d0b2396a83bc8774dc3dba04e74aef1f09870b737f1a266c97d0246df14580110281e1941349abcede2b078d66e50a30d657e7380b04414637d6dc0a8fcc2adaaa1f00753b8a03a88db794b0b581fd2de190b69b2781ab29ed0a9f36356be47f2c2f8f15ba595509a9b06813cbbaa5c39029f637de6ef31abf0bf5d64328c4b337e06059dbc8282dbbbfd534fd564bb9a8746e268c10870b9c3c951db9520fca2b3829ebba265cb977d39c11803edcb0a9e39267539135f5bb8a39be959fbfd8b4c1e6c22e41f3b156b5cc3fb9c8aa337206278332cbbe779e88aa432a2cd947844339b598832bff797904395eda6590922a39dd78c07e879888d97aff2b661145752b9fd795c097ac24ec01c55242b81c1389f31d05d44c9310e1ebc90cf69c7a9c396a0ecd42c787fc0203216aaa7d86e5db188c777b8689a17d3d62b8f990c9565e94b7855f7059a6c514af9bd5eac8d60d6a5eafefd252e9052ec9e5a7db7a01be3051e5881db5e4fae3b6608f59997325bd48f4566846d022b085e75f2e4a65ec5421a02b9d9de54604288e3138b99d349280ebc1441d9c9d6b09ae257f5cc9294d30a834d4987656b6df4e99884df682b2fd219e198077011df25d0ba5d2d182e549de9103059763f6a665b096a1d4d379bc35c01ba0b0c7716ab6693fb75f6dd2352aa9a8bc9c6f69fcd3ddd0af5edc4e6ed62e988b3cd99ebc638261b49af1b661c82efad058f78bd69283a073cbac4d3abff0e265419121cbe08e0ca84fa00795b6fe86418a3cd72760faeddeb1d4dbb2c899e1b7da526d82f8a2090bc436f7a662b3423f002189e889da836002f0b810f2d23880d4efdc12965cf698785d351d5abc8fec8d383eee14c5c86499b0f32f7c7bfdb35fe955973a51d9ee10179fa29fa91d2c1113cce95d08a5590e748a96b90066d82a4c5213bdc9bd1a2f55417008ed1acd282adae584683f06102bd1a72870a0ef800e2d684de205040fc9227087437eb740773d1c311b7d0ac0e4039e64ad14152709040cb07ecb27a07c4bc97cc8a119b8e6d6d8916a04dba6eace45f06d427147acf678f5357a02cd54ae69500fcd51c5ced3a3594f0d2ec92a2b227f650bd303adcad15be5f4dc733c44ccf00d572b74806c7efaabd1e2f1dca2950c1a6b40e9b2e85c115f681fbe98b931049ed95767e20f28aa267332c5097576f8525f7e16088c11b74008f92c3397b48cb87a98041d3c13eb15775670c3b42aee5ce6c119c38eea753ee07db0beed80b8e6e3bbbb1e090550c244645367aad8802068f6e456a21e0f78334115a5ec8e1de51aabb62c9c012a3117c86b6cf95689a8388b47a939785147b79b05ea66531b3ff7655a36c079ee2cfcb12785770b68265417720f15ac20cf10ba76e0db996b866679de8da3ecac89c60f3120afef5d4b196a1810981a08d52dc2608d31845130706fc6ceed21d0cf391d1c94313bd2b6b5ab57527ada8eda3db7168e303210a1aaa8364c20242e2d17d4fc678b9cf902ec40f1ad131e93cafc401a56e88b4bf81ca7555ccbe615e6d554ebac127b7aa8eea31105d4922d189c9f4129295354a512d6a1e771f83b8f2e8328ca1dda46da2b785ca617f2aa0fe51d09be3ddba90c5a7d1ca67e7d8482837efd4bee1cb820512df2c8c470496e4c51c555995b67702a037bba154467b7936f92e2722a2066b79b8f474bfe1231408291eb4a5fd9f5d0a2e4ec54f0d5aa94f9527f76e4a0aa281f456364987a0865e81cedbab62bdbf225090c0c7788ea69f45c39067df717608feb4edde0166bfa9cd974d86f8cd9496955fac29f739d381958f5f32ed9aa3771ac7760c7b27d5b90c662e236558c66fd92ffbe6340c14ecdea93058589f7c43027af6b3def9c66556de6fe07126cbe44fae4d016bfd43eea10fff1a4ba91f6bd63628a1a07f0a185cba399f8948bd1bf90830fd4fbcb734e7801c759b018fa10850c9d7a944a6e4c080c35f8e4e52e09a6ddc013fbc0d69cfc990e4536d4fa2595ffb70d24f8f9a8986ae84f9ca2cdaf77e7d89e5486cd5fb4c8a90ac68310374fa57ce81180391577777a6274acce3cc199982bb1a14e11d776dbdefdd1d1f3822dade2849c5ef12d59100375738fbc623666a76917032d4b2c8bf28a58e85fd82354853b8dc02077c9a82b2bef5c5660b8117560777967de23951f2745b9be9a1f52576d6a6e0fac205026a13970a869fad198dca37372670e7cf1288ff671cc151a224ccbddc768d60ecbf39bca43a3e767950e842626a48ee200073787c449b9ec0fcaa7de6f288e91589b7ae51ae73a43b7b7a33068ac38f810ea3df692da29a7d5f7605530d3fe4256692c8bfac412d19a7ceac8d6a320eb308fd23f177cf4e95929423f1508f73914d2ce6ed9c56e4d06e729b8d4f1b5aa81a8e35bd4540d5ea6a45d6407dc968e3bbfc7853b8dabe2426b82f896fbd429e54c55ec40613f2c15d4545d2e06fbb11012648a596e96b457d250cebeb54e000bbeb338e64e74c30c889aa054fb49bac2401eabf1c95b0838e0f00d13d5d1325a92ee4b445ad538af376f2d0447799b9d81aaca29bbfddadec208f00b4a0f386b1d077b626905a159dd540fddb5b8472fafd4d3e90c94b980772810d1bea5df0d24141f9d8477c59519a21a35156bc77a2c4b3a6cf2ff2e510e3268d8feb93c4116bca40aa8c287e05a8da01a1d710dd8e4699ab686cb1b38518af7f8bcd8c263d6827066563620804114e0679e5a5f339fed84168440382583493613ed03dd132a0ef41aed641d03f512d00132d7773e85a1e592bf69fae9fe508c54454e885f8ed047649317850e13888dcc2cd4424a7660d2fca581a3f8606f1341433c9966595235abff52ddc2c3c7344757c25c1b39f4acec57d7c4ea00b40ffe9fec52f6e635752b1eb8237f2f4625ef443f65f6c73b33a3d3937f9021a4122fe272b39ec7eda4d1698014242595d48a72fe805d3206231d80db0e0b4ffded36f3e69b6c50b04df1b383ddcc69cb1b40ead616dfb873c1ca28768fc899648da9c42ab20d508e4b66dee11d3f8a3f52f3a904ca61ce0124efd7883b386872b3bfbe8fe6721ae7afe6ea64139deb54520825f1a1dcd6f576c21be27f427145e84c658449c31161064f5c88f4f00d2e2181b09f66e8dc2c92a5299201cc9ac2a9b191bbd1202d5d1d4172c66c6111f6c186b04dd64f9a4355dc564575b4c61e026841e8adad16b4c9c6ccb40a57b09a1653420b24a93ccd3d0c8ac38eb0034aea1432a8472ae2a55e304c94cbe0e984e3c037451c406e82f7782fafb0c8b6a95b7d94af6fb8c5124a39738988899132d0154cd2aa4b3892f648378117ef8a5370a98747a0911b4d5abf6d0a38336892978464062422f817f30a8deb9454a08985bd8f29e6a72e67a5d4c21f6d03c7f5fa281db0789d45d8adce08dd2171dde6d1a12767e041b6d455c9d3cd0f1516d2b2fd2c9df8aa850d8271fdc77bcb014cfd95b169facf326634d1c746f40ff3b45a3746ba481caaa6c84c9bf3059bfefd68e6d5c2ba2bea1dbda6ccd086c81025a2881d7307f26444b642af102fc8986077bc1a70474d0bdb507b3a2623b5e8901d05d83de460b70e04ccc06206c9185094f9de55080f664a48d64e54ef2a76ce71c14df9fb430226599d8fbcb5aeb53189070d22fd6094e2611b898f632fa8881b387316856c652a6ba350799388587b57e00b9e0f04808ca9640ccb9e7b428f03394f0c3f31b0ba6f09143bc8056e9f0a7f5cc7177a31155b673ec395c016d8c19662b060801a88afd262fef43f8d7c284b3270539a5eb216ba5315a58c54874be4ad37b642d5c1c2e7bdbd1be7ad0897c46b74770a4347c6b994a111b9db116cc4a47644a8e7ae2679eafa53186f1d94624c96d5205862216546d8f841cb76a47cb52b98e2c41a5cb3b1a146de83868c0f094b56bf93da1b7a2c20af9cb5f046c90f556c5bedc34b028331db298053da54c81646e9d427d8e9d23d88436cc49b50b97f33abb0e1e3ddc3a3a97d3aa74816c14494b3ef0a9cad41d59374c83dff6e3de8c60b8bf5c2a44a8b913feb891da34aa01a2c4d3f65d141e4781a22164140238530640ba2156225e93d7f288a3fc8fbcee2943fa61f966ccf6f3bb8ef4aabe97daad714511999638a599470b839013af4d92eca17f51b9ad537ddaa0c784a07842c7b19afc25048476eb5e164ee24d876023303b2e1c298037c042b2b3ada889217756b65460e37b1f8ca26fe90e13463a111cd86fff599ef06bc02bc56e6d4290b7818e29e43144f6dc8384de04be802969c1ec5dd05fbe670de464e468edad1d2cccb0ebc2fd1e1ded936dd7d31658842b680e57ee44029ac1b20663d752909ff026f61566be126eabad968a39a338904389c492c28a3e3a78581592b608a49db0c86bc1b712337511f27f6c1243aa7737e692d9fe991254bb9c713003fa1da42bffba000bb3af547fb21847a918944bac227848b41d190f3d056f0bafd41ae66257606817f24e5e0808c83b7ef224283ea77df811c4d9352819dddd7b6d34378f2236cf69cba63f11bf32fcf05ece7fed85fa11dc97a9d3e4c1feabed160926ab4a56189ed732b481b4aad3e6c0d49ed5f822e839535dadb2438832d62d71ab8f731a2154522909e92c63992f438d354750d8509e2a8c4526b991b76a2fa14eb025695f7a924bddaa17ad92fb4b53abf5a21544db3537caac9983beac915de665108310ef09dbe8027e11a6658d35dc5764ea2d073fc12eb9c3412b18cf660f60016c2da2a50228165acc250e8e48f1c82cc206be7d5a3793550847cf1bb7995fc5ec0c7eaef3249606a8e3dde8054f40d7f5b4a73aeb94451338c0691410e4e75fa3b0c15dcead90e4397500571b0dd8132128038a800795dd4194206ce4221b9a8ec7cc0615b0e69436ce5ec456b438bda959aa3cf49f82e9aa6a90fc1e1cea70ea32cf639184eaee871be1dec984caec6a50865d11da947521d42f6d2097e8aa71e217d5950f0f5535a335083b0d7f8141c32c17dbd08a1dada192083adba4f086c7fe7217f23a5a2faa8159af0a867eacdd44b99751a1b7f09f858e55ba8492b4aa8653b1c3c94ee3704e382634c6cab00e06c2850fb0c92eee28bf47ddba79a30b9cdb1b94fd64f0391453de27fa04c901566c743086911a9b0637b1f09d12b428b4801471be7bdac348bcc2f0a45570339a842516851d76f24b3e75a458c2472c873ca820911cd314e64fe8b2752b5e114a1b4339b2697479e3f4c1a719b3feb758dc1bb9d49e475f0f8a495e229994fafa27fe407f4bf421c3b731830c0b809583b594cdfa058ad599f5846ca239832c7765c582697aacd406d8659a20f6b89298784662e1848aea7d46617da4c0902e348e429e4d4b3302fca6011e637f1cb2b7e4756e2f5cea240f8e9887720ec295f39a0f81334f566717f2d7911f76c22e48964e3c643aa7dc046a9b133785d9046d1469e4dd9ba1f25249512c4afafc4ac8cce1d163b38271534575a66a3147150e73b714816d8c6f4e177e7d5e3247d0cccdf11ce842d0240531ce2db7dc7336d7ebb7bb0a34ad7e435e260d101dccc180375640c4dc69ca5bc19b681f7f3aa55ccb20a09cd101c0f1353ecb4a407cb7e68ba41e2ef15a3f63ec460d3d0324dd37b1dd885aeb9a55becca4794f2649672b16b6f813b7ed7c8cd1c92ab9b60304d18de12a5a48f5b97808294f0861aa7544df7514c4ab1cfa9ae982e24234d7ddac234f450ab862ae460e91638a654f214effaafb51c8241303599a693b25ea363d68c2800a74c86ed0c312257eb5c506d0fc0c4038da3b34c9aff29df5d24f8efd25e9a2918b6dbeeac70e8593491c73247e55f4f428c1b34911276ee8f891c511771367c12fd1f7e1b4941feb2acf584319b2cd0cf2263fe4e641166f19660d54784a31984aed605d4a3a9338d67b8df6853cccdcdbf74a38b2a2430b4b42a10a41553aaeef6d380dfdebbec683c7a95dc61f8201435623b724be9f4d8d5593e9503073827f51b248e80c0a470e2ad00dea9e41645a0828d462628d7d3032997d8c7ba050202fc8646411eb9e8d5d30ec14bd8c39741d9d2665475aef4bf5e80e69f85186338aeeb1b2fd4d99ad5ed96ad4ec3c74d21021d70c2b4caed70c4029040e413a1e6362c2f78342ea036a62a7907acb96759fdee18533cb82e640045b202b3d82ecafab1fc5a833f8f9d18ead445a5bb6f03f72ee7b5d5fd6668ea93ea8f40fd9890713e7c88165d265c0f8898680203d019ad9e77449ecd8bf9a39b855acb25f8253230294844beb9aedd7c80d9988bf33d9a8f0ef036164b4a07446c92b2733b5e2b8e5b00ab2c2f715b0b2131c97fd5576071c80e91d8690d3ca689a9980ac9a296853dc03f7f11098d8cbc6f2dc00711b53b3a0ded9d65394103c80f072ffb7c56bccd997c2e72acbfec749e1d7c05b448c2783a948c0987533c639674274ec354f0703f6a5e77f47a40f2c30b3bde7f705a235aa5d48def2e57601f33dc02f743f9b00e343f22aa1f930b0fc27809fe651460c76fabc6f8efa1ad822d8e3d219b693c07412c0430b7e73407505571431eaaea6a08a11431dd5751dc17205a2fdb4aa2033b8c737c57d19a0a772b4fd58ab2034bcbbc750442bff7ed7ada85cfcbbd5766bc35698a73afa14101205c67396a11849abfebf899fe4606f95408d4e4a8c75a59f1e7241d301e6ec2ba2e91500ff74ca96cc17a9d29f6af421372ae9c58c329c966e0ae05d4834f0462825ed1502a85ee5f803f1a887a5f644f942a4c2987bf9217b41b6e0eee3901266a48e760e55e0a1cc3f5fd6fbe839a4d0e4b3316429b737ff1754d97ca0d13bdbbc74a0e4c3d725e340c25b0db3acd67c1e9ded8912b32f86175c110c15062aac96ff7fbea60f36914f73dc651cf698f5a0f5e695f3db746e767d090121766460628cfa88f64ca82131a50c8f3afbbcf58235de418a4c75431b88e8e599c2f687a210ed81af1e684cd57f1128d62b0650bec680816f2a2b60f1d842e4fedd0706e3ff0b4f7b8633e37751fb170240a9decec116a379bef8ff8335343095362252e4bf76e6a0f1d349db48b6461556f3121fa7ca83a08b812b91a1c5de9a004c7e68830043540f3fb31ee197ee9707f1d466e929f6d9d6950a34764fe3baa151e5ee1a27fe3fa97cbd9b21e777632bb59f6c085a85a0333a3afc7fa3e387abe06e9c4956c8eef2ed67f81d5151be954c4250e4483aa27ed01bb8b83c6463ddebce3f2a0351c8bbb486c1d6621209d1309ee7399c7740331263e90aef9d9720a6f8a70320288f1101a7f2699b60e1185b662b51584df4924aedca49fd05e6ff1880a12378c948c4c153b08a319bc52bee806eecc032bf7fb4e1fe27c7676ebd0049a2841429fdb316deff8420f45f7b2c828885a0beecc5acd84945c7a25dd3ffa3954cb776db871ec6c62ca2f4ff9b9d10400ccb7e49ffdf8c4009b917f6dc13203aea5a9ed1b017827fa3289a9eb85e0a669647d1ffa7bf3f53f23e33c2f5d86f7c922b8f2b10b93889ada35f6992a1c2ab6abff8a9e3761d95e883b5608c9dd36e549275d6a9111f5459f5661579fbb1a462e7f3a8980cf9e875b2b75ae15d813477416d5caa320170c831ec5781ce206a08727365b73e10bbe719807a4b5df490e08cd802eaaefae1e0145544e85874905a96228c3661bcfc2049ee7768ad148413adb17a391c7df2b40f78697a5142131c8a7ca26befa1b743591f0a229777c994b56793fdb96a6170bce7b41ad3e43a4343760fe2027557a37c34aac18e0fbb7866859d108734eafae14001c0c3e03a98a82b993c4fec89c4138cd05a2ba65edb91c82f5332694b1fc2147bc4b5dfa83910aaa4a7b2d33aab3539ea79416b3a5f9f2bfe8447eaa1d2aa22d558867414b548cac2bc4428c7b0526563b386af5fc4c9b11ab22a1d499635d1bb6c281301ae7f4c48dd2be0d1926dcd17333e26def279dd87dc04036dfd0a3bd7e51da8276b1f13b5b5adc430570942a6c1c7944d89702a171b5362a44f90e9df989261362222952242ee7aab198369091ff5e7c51b2b354e03046f781ba52fd36d28bd68f0ac374c611c14bf3e1779dd77e33d75a7351438d8cf4e88ed2b1be13456ca6067784188e5ba6c0289e9398ca5c3fd44c70b81957153fd7f376999de130ef8e1c9b822dfb0019053c020b668464dcdafc1454ed9fe40487be267fa9510c36aed9ac8199866830b8480f62c3ccb352ba833f53ced6160c7a207568b707b90c0e2e6ff32a06a4b951e17291d6c1242dbb45b542a79592d21419122982ec4be58777f102774b9a8cb11db278644d0f7f594eff56a222ec79fe2a60de0d9e23f1549cae081942381e1d5215322aac40fed50a49f52ba4602e183cc4bdd2682f46adf02074706da25e5db811e3916443a39329069463b4107e35d56a3e8527cca025bfc12b0407f84057e54feb7f5e39f389deb59d7c1b0c9ee718202aeda7d083c69a8a75f151c834f17a1b27329a73eebdc52041b07c45d4b8f9fe91dd02312bbfb394ebb7fda3a7ec2d1017f1f1dd6060a41d41ded807edf340d56aef0484a1acf8a098d922c11dedde86de189c722253e440bed338a34718f271e2a4c14aeb27f567e73303dee3705fef3d3df02f1ce11facbb4951c6c905362a5bd2584ed8285cfa62e9f825c19e16f1fba17ada9ef4e9e0f4f20b6ebd2ad924bf80b9d28877df73502d1f32c03550c9c96e2157b02051d4d5350e96afac2b804b5f3f16c1ea4b53ca97d1f6052f3a8bb9f59bb4fe07b5e92ec35008cd7004c55009318d59ea45e02de7e3bc552875e677a00037c7cb4d6092a75b005b00137f0e85a9cb32a57d1404419dd138624b41f8b0e7022ea936090390fa672b6508babaa70ee6736c80fcbb28a8f3dba24af0bb6bc09a644bc4f0f9d90c91957b49a11f2afe22780d32747b81ebd0821f778d8d856d81cd8c8c88ed36489adf308c23419e270d0582f2d14996a4a76ab012c6f7a54b80c2aa4842ef7cebda9752e053221cd70d46af7f50e77b36dd98e9c9bd33486677bb407a1fe63da59e1f80d11a3e8930716cc40185bdf6d05e8ef7227ec65f488593774815a5d4c62b7754fe69c973196945e0f3e4d1869261d0fd8fbe5c65bc679b84c365df880531b7b65b44e638a77f8fd5de99c80302809df1dcaf011ffa431ecbedac43e6b85068252c57722e2cb50567be5c340a7586cde1dd37e3bdee1958aff20e4dd93a8fb41989d452e4e385dce2533a6a10dcdb2d01ed97b152ce673c405a642ac0d5b4f7fed29ce6783c86ae378ba450d48687f44abd23f96fb6cc3caa3a02629e8d9b4c1909424dba3b160a5e717c57440e24cc884fcc5a512c3874a66e897ac66458ef2d575a2269ade14e01e50c2d5b7b6b458eb783de29ecd1ff163f1bd626e6c3df18d18111ad88166a2b1b5bcb1b27053903e7608874fc031f0762f5cb4590fe796de762068bca72cb83d191328d16b468484221b83c2c79cb47dafa91c2ae1b280109150bbbc9fd7641dc5b568d8e4371f08b10eec8557de69304f4bd12336f48908b3fa83cc04d31d0a10878c9f5e04bccdf156bf8bfb653fa2dfcfc17d1fb38a4d69ae075319617843f25fb9d36303aa6c4a1136a490284c214aceffa34129951d100c3ae0f3b4fd342a1a48a953baf04279a4cf5f8aa54f3411ab3072163a7c4f9cd4e9e66f7e2b0907439b668f8279e540918b1cee30c86777e3bb5b7892378f26510f2234508b621ddaea246166935ffa5ee55fbe4b10c8635198f96f1acd47dbbc277923b5a39672a2c744e0346a641bc1d24f6d152747a8e65307648494f5c77e14a3404f586fa5b47951ad2974ab8c3a155a44ec3021747197a3942ee13a316c4db48e4f3eea5ed9548a3e425d4ced68a42911719a6e73a2f21eb2a4ed61968652c7b0990c9b80be78aaa3a12c110682496cc2b499f8792385f3a6f27749c96974066d04010605cfa44feab5e290cc00a72aa7ecdd5d7bb58125362b6a73a031f26643a1653a46f2ba45c54138330c0f00e1ca67bbb5e1b55b93e9479d0a1b80b5032e13817a153906cfc7349559d3417ec3762d4c27e34784ac930145018dd143164ac2983f1fa842be71bbef5b426dc01b51fbe263614829b6225410987d7d49fbf2815ad4b3e240a1da00ef040453795b9f8d94d9d991f6b44776652a077c21e04e4aa1867ffd38e381f2ea022b4fc8a6e3520bcae92437ae02236b0c3940b138cb3f4ebaa24a97802d3b0e648545e3a6d6f5daac50cedc1df3bdfbc5151faed7fb20c3cab4818a5a2c51c97dfec6e7ddb152984eaf611db17b0bcc57d932811f400f413fe82374403b3a4d1018d22f9e3923f427b26a36c7a0377ff88e07fc37c49b4dd6a0f4d3ef6f5a6b3906a802471e16dc6219b47cb8bf571d846024ab6903f983d0321e4f418b57ef9f6d74e9e1316119d836abe0cc6bba5469d4a6b54476dae52f174d5c1127b814d4e53ba92eb703b0ff7eb6eac80763fb033fd9e68dd4a1fc143b67052057510c95b42a2db16355a9925c70b9730291cd86b6ab7965894bc8a41eedf9ae70182641a1e80ec40b6df3a4124da140ae84c6c7c43c224dfc411b6601e64f8fdca97767ff3b4e39e103d8a3a87f17ee53ae041742bab3d669fededd85ebf470f74518f7e243bba985773b8a1e469a06bfffb294aea36e49e95a0546be139fe0c9aa2b841d10a49bab851e0bfe4ad11d62570c8fd6ab529e64c9382ba0e611e15f17380022210842347b8dd6798c1c8102b6434277bb87f36db85d64a2396312a430dc3b05cd4495d19c88b44e698d162786d09447d6773598a967d91af604a3cb0d5871f7d9727fc235c79a00f719179570ea130150d282a0103f0bc49a841d941f11587039b45e37e54e102242f583870518ed75e86b1e5367bcaac93945829697e2a2298b41c50500ee2769614faf671b762ce25e1b86ea4565623689c204dea05d8e24b8a0179e5b5e98038af4d0875afe8f86b30639c74a155f94b1c2dd89a703304ae078be867103ff287fa3b8cd1646e076c3430b03a99b58424c170749d06cd0b8e3aebec2d5186e9462ca5a3e89484fee959173f04e7e1d39e5a2660d73453ac8184970e3b98eb9d03b07fd4027782b071fc72e4c7856a790eaab8fb023011fd4c6566c03a202c0749a2c1e79cd6ae289e612ac3ecf7ac37a5779b2cbaa3bd64781db02426e73d9d4cd04822f23c2d3ef303ab704806f8c4875af9bc3c8b182722c8d6a68cc891a85f44f3cab7984ff5b6ad2a0b4fd91806f3c8bd9504e275ed8bbd0899bda43a6e56b4fbcfa835a2f1142d19c16df1b169a06743db8833c1c75c41e932e9de6bed8a222b2918b82f3f70022742de2d389642f50f760c7fc15dc5e3c1930e9eae8095720a1f5ef45dccac05353603231aac33cab1ed237a6325bffb4709da8dd9597e532e885f78e1791db2f349f9a95f12721ecd0475a7178e6dc9821a6e396ee622ed6cc5cfe3ac554b881b91f74ca7d273e9f9c4203204344e18c128447fdbc0fd370287c3876c012b641dcf1329ea21f8a8bca25612b12509400c49790f44bbf265103ce19b07bcc10ef45297d97addcc80dc69b437f2a912edf4460f7580631ea808f176cb7f100ebd799f0948694dad723fba881e345328808a27829195e2a03baa15bb182f5f27b4b552573108f6c12f2492728b5a6c8f278528fa816ca89dcce1001199bb34d207d340015010a5949a5deb7363c6994a467572cb17fd60524f13e928134bdb1e6067ac8658760926a3aebe0e942398040a2f9b0382c750da74fc69cf3656f034721b8ddc724aaeffafc267b20fd8ff6455e64e1992db85e0ccab6771a2bd568ef9707fdaf4c774a74da1f2e646cce8edb218ea6b615caac48e3955fa20f77507810a68b25079d16879ff220d65a95d82049fe60852253cebf2eb7d30ac0eb8c9876a589b4bbcb8bfc5557f7e8a02a21495ff2496c4afe30d4632900d2aa6c4307601ba618dd1901c4b8a4631b52fbba9c3559be3e285ade73e48fc4d9f32cd9191874d4632a521a935d0960e44252d176925ea04a77b63c8e7629e93f39a7997c970f0f3127a5fa94290bb390a611896b293d47cd5f475d8518e69ea3bc0e39c7a5b32b7e81c7652582a4a2d381b94b1017a075df836e0863c9b6a2e2f9a8e01ac351286a42b643a7438f23cf999106035f97d242db693600cb885a58db1c3f36335d479bc6f67c768ecb3d7b918196377037acccfce8a0667754bc614b3896f32e2a07cfc4c6fe526016bf4ff1db0579713fd782097422243f3f22f5115b802b09e0d0fd41195d8a4948a384e89e5c6246e82d4ad9202622053979a07001389f031c8d3bdd2286b8b417bd876154b8a7c9002cf9223567c3513efe4de2bc3f9458ec60b19f15514ab2e0197fd8848db096ed3ab1fcd75958ebad67bd4808a78030f5ae1a1e3275df6884b8cf41221a1b649920ea10f4413ee6ac5606c8f067ef1b33d447e116c7be02272fe4800a8a363c2119d4f24c66a4a34614d2562811d018da15f98a4e01af1c90e5a7294c2fc96ae0494f9de5fd19ef4536871e063ecfa3dd5d685eec8e035d026d0f94aa5d4b74d9160ae4750553f72f1170c3459c47473011d51f44e5ab26649ffbf8e26ed84207bab1b4ed38b10eba2b86897a8a5ba23c71c2db71e27b09cc20a370f6064532c8cae7363032ef36d03c3c923f7224bb51ba83b09e429ef9434047ca3ec8dee4e52442a79dc1c01cfaa4d962b8f03a379e9fe3c6bfe57dd006379476141bcad00f559a76b260f49cd0ce3f75da7a10813e0f7779544e5459eb088b44d82d00a2a5998763811a3a5c9419f425d38077df2de48de09e50ed17f0820e6f1b925ad2a6580c700f0995935e263eb25aee3a3b62c94b762ce167931b1dd0ac06c8bbb9a406eee11994c287939b06b3523f2a9305f7a80bf14319c267c64a8596640f8fe47838b51d210193c305afef9c481f6ea4031e5cb0919bbfeaa213f32b03901c4f806afb7ba66f392ece44a94ba460082dc9a88279dfc0cc80861419b7e083cffd3b193a4257444bc4015d59c4ba54f9152c0bccaaae488a361995c98305b577ee1bf988b4ce126b91d5378504b5885c09dc6d385f7c6b5d818e8adab047084d15ebf4d2efc12fce27353ccff8f6f07b8c67f50bfb253628a71c321d7c67586e04a9a49b491d8c756935dd62c24f0352d827ce5ef8fe52c7996cadcbaa70e5f0a9dab0c4bf60f587e7da251100f9d9b10c2e36ed68df806f88aacd630b97a013c8b5ed93a0de880987e80f012eedb1191bdbb869aa919d8eb197b6627b08548fafd83b8bf4b205ade8740a45bc48661a66949bb5f209d0519e7d119e9b5ad3a30ca599af31bef345492b2359c8611266226e49deec3f20ab5effc56cb489f39b419bc52a5975b029634ab1246dc54abf1372d66bd891e70e08097f2be4b4a254afd1776064424c2ce48fba278b4816f880e7f79587d2b92058083898c3f000b69375b92cb7a189581a8ea9261b58eca9d31474fb6e0e290e06d918dd4c862c578e83b6682c482ccdbf24d0fc285795953ab5f5470fce1a10c100932828b019c28cff3e848bfbc2b48c59c9687a85107d4958c102873aaa1131ad62f67d358f32556fb414a7a8ef6a8e615af1bfcd7ce60563be90cbd1df0eea2f04d86c9687992b7d7acdc5198825c246e099b4e4fff21fd25d7cfd4abe5217d8d3cac7b929379840202ef37c5229f2630c30a094980284912a62bf61a3382bb3ec3d665d1efc7cc02a4b1a7d78e199e00ab3d80bbdde9801462f74360ae24e430fe92ef4d2180ee421766458cc78a25b10e8d8a519a8284a7b9e72637bfd13bf082eeb2291bee2d50190b4722684734ba2e8a0cc66f56436ca8ebf4ab7a6e38ca9161c68f02515c738ab4bc6e3215f9beb4fcd85f7b7a1326974656b3992bd046b8b2c648524c7115515a7b4c136d1a24f45847628e7561613bbf04d7916b10aa744836ee790e2234511f7224e6398f50fbc06c603c116a46e1d5c9eb2c0108c0467a76554fbf73bcaa08b3e23111a68a773a00f87d6ec0a3c3bf0186268383c85aefe9edf8ddec8c2d46f269b1f543ed08b1bfc2642d3a0cf7fc5b171b899443a391ce2212296f3f28c9a5c5f3a6eb21e807c90f7cba441c553bb7cea68a5957d7ee14480a74df7ff7eae6e521d2d8ac6adf973a624972c8059080eeb7a855068bbc7a2153125a0b8162762825c0a1ae00fef73301a7c74d7bdbc43f53f0ca6200c4d18d0711af07d3fc679896e80c065e996ca03d9d2eebfdf51393a6d3fff7848fb6b740027757e917bf7e79e8a76b04fe5c561d160f7b9ec3a7e537e5e7743d497b5444c48571e97fac704d309223db6292d26864d4506f0905141d93473fdf03fa9456519c18e4b54ec5a5f48336ee56273f914939a6680bf928fbe716a3aeb5427e960f0c285a97752c6f4eb7d5eb815cb0fa78f65fc7dbcf0fd83f0def54d35782d21013f799330fc414ade8e47a35cd2d8cb210d5a2152978837769014309b7dd188038bce39bc6a5e1481a2528529beaa22a4e065609a6f3a19d1d62e7226b233fcc29193722ce60a499c94430f8bf5404f214ae89f784cec16d83bbe24b2ebeb27d22101c837caff997a257e26039c7486bafb44e09a24328d62af4729b7ba0f45f5b19e8fb4120c7428c41d03ca96c3c36fee60aa57d36406df8131b738e469af18a19a3bb521ea9f6c5719ef484ac81fe0125e3f37103ddd710143eb90fe8bd34957de031a83b1b1c67d4f98d1c320831dd25aa40143c050ac8dd6590e4844f08afad7625bf263967f58e9a5d0313a9ed9ab7f7f41474c5033129d004c8fa7a68271a83af9a06dc5470b27223587e9502f52fcf965f80a0609a41bfe875bf20bf1fc29d000a7c21211447dbb3dd7e7835b5f6a770253c4c7a6944f7473db7fee1621323b236862fc060c825ef6ced085f897ec74a229000339573226aa4ee86d27efc86487d00fd2fe45770af8ffc80c296409c921849bc4eb250203ad62b5eb8182049ca93a47186f7c235073b4a56fc6fad12ba0f121cd505d5216042d0a8c58234d2223f6a5d6a57ab735327f25675f4d57fe6f81c97d19bcf2bd9d37ac366aa02306df345a41b730071ce85d13b4da4c284bf0c5b7197fef9c667ffbb2d4035d47cb079f7d19a3c09301983122341fcb598bbc074b37c7e4b552922df06f62bfc79932c9d748e8540a4ebd06ade346ab024ce88026a8164d0437d5e3a76827f58b693bafa1b0da8f964050140fe98bea5458b4ad86c4be162beff3c53eecc3a1c55b9e5685ae1959e2d37b2139780acb06029fd2e12ea799946961614a072d3b7b967cc64c1be7a7f4dc1ebb537b1cba64b21c603855ff083b02e0c2d2e3c9933834b48afe061cdfa09f5b6239dd12c23c9391a22bf0a5c3559f68923627f2b4b4736531f938d60bcb7f79ff6d56ecb99f408970a50b8cb711584182366c97a04e2f04bffea3940391c38756576eca03d59c75c6e39ee2ea265ddfa5ef9f6c603ed6a8da54e0651de9aab5d6460ca269850e894dc4af9e1f41f84768b3d407f5343d0795c11dc9884261f1e33ead6b4238ce2d7c672cdbf0de94474f11dd6c048ea25a9b72e07fc29c0d10458c97d02aa2cebd149898709047fc3c438bb20f88c8189b9686f26d518bea1a8234ca35049d80a1cd8111042417e12f996f5af8686f8b677ea470a7b0fd6729ad89508b6450fd77524e9bd04d544ceb8c8a44d0e2b0e428ec1c4a7c2a3a1f3d0dc12c6fef8ecc4c955a250d03a88079d05f093bf8800adf6a81391e81639a671ce88c4ea64b04105e3c68d828cee628fa8dac7ef2bd166ab69e6f53dc1d73be60de24223cf1d2840dcd07c0427600406f0e8f448796a53f09898a78db7d9cae473b7d7fd44c4229b295ef20f5a44a71030f0a558575333b53f0ee9309f789f70f51c358c1be659ed87c52a346dcf68fad2bae4e56c752aeb74d304091bd66b7545a179bb0e6e5e2eb28014bc44232ea77ab57e8a649d04a3b84c0c90a289f2b9589db0647320edd60eab0052909e84383e826efd150018ecc04c58041b8914e70468592e523784d98e84901cf950fc8cf2e62ac05b6fd05076d44d94d7733ce31af5913626422c94556f9dfe1d4784babd3c621229e9c0ce24e129a3047d724e897951fce6d185ba2e08a5d95739b69ae2fd38fc8fc49da07b7e3776bbf2fb1be5f569e7537586210ba0ec08205a213549d4cb6f7e6763a971d08e37f212e08b9bc6f8d000f44989dd5bc17a1bfbf37837e4d27b9df379ae494459d48cfc700cfdc6be2361c20e56b4f61d3bc134a2634143bbd085a72ee010d123eed6ed3bc0671bc2756e17be4c27f90e92454827c23421adfc4652042361c00edc4661cb69fdd7fa775e48aa00605b2f07a0b2d5508c9353316467d1b933b0da4d7a953bf2e7e266107f3a8f2087433b235b632f4730579e1e4267484c94d6f11b405317f9f2326c0218715a262a85d71913f7a90da5593cc8fa59e10d64e983b47d70c1b161f760473d72112f4827640415ca8a7e4a2126bc3d4c5b8960308f96a5be5fef34358b6a510530dea6ade82ba94115b18ae685f842abd0e79e3824496bc33ee68e1563ef510172de0d1bec7229bef4c05b46b895dd5e13ae4a7d9d52e244c7058f93b4d53602fba19e4cce311e104d8d39fef559923fb7b66196c57fe88ab241812270f7c867cc0b2c450e11bf5413e55a10b1f1eb244db68013ae3b4486bc238cb2572d4da6b0f6522550d3e9c43519c54a49edbb32bab7acb0abf90c4c4dbde36584be96b2555f16c4fba3c77c06b61ba74d9eda2d25f4c43c777f5fd6883951726c7ce5733078325709c2a98f430008cba3e1bd63eeb9a7f4297c9ea50e97a9220113563a511e347ebf666c2ec172cc2bb33b2abbf6033eefa566e1c992c7b22516e9fa1b710926cd6ba39d06285aac70b31e5360703b2fdcdf54487719f8064fb35a1ee711a6a76f7b17b7a3d1f6ed2c37dc29063770f4d3298277445045faadaa9ac7befa78c67303d3e109a30e139d5063ed3640a449a087b0f4c7b46cfab85d6b7e20ba1f2f9e4327f514a134423710bf5b56d7ec2174159b3a979ad710cbc3690172d4e5963fcb182907c33f350e17c960bbfd943a285f392a11943c3afd30c6b98d5a681d93055d83e5826ab17d3f0bff08feb979072e4e53020bdc0947fb0598a9fc53da5817953392491c2fff2dc802eb38b8adb09352864371efce6ca330281a4ee3dda9c3a6a7ebd33c85302669542cd5da7db6d76ef6770540b1484865c0bc421483bb5b234216cdb9d19f8fa608798000b0eb50b696370bb133e119196ee71be897508b0e30b00a60864f3b9e2aa7276c46007e02bc97cf9d1776e0c92cb75857103a18f1fc7434a77be2700c22ecc5245909c2328d4d113194b3963838dcd4a045ada6a00db80e4388d135699d313a8bbb1d5dc5e11987386a09233259c42681c65ce05f73f0fa37b09b7897a4ba17f558fcc4ac0a9ccabffa897551ee94455ce8b63d0fa4840f478c832cd7664756dbd6082076f62e7463d31ef598b052df5223990b5ecc14c742a741a54a166328c8e24cbc2c09bb38bc85a615a993be80a98c768565455afe512fc983a63163a081f760b3481d8a99b30718fb7fef4d93cef105990740389383098a64bd6599ac81e728077d319062d613c6a18615ede88ef048a9a99de381cbcf93bd7d3c42ed2f9102240241975ce0da19a3a7f62ac79bb692aeca70ab99c2038e394e3c120fcb96a0d2f630bb51fcb37690cc518fd976791f49dc1b1f3120d22296fbc2fd0e1b17c0395a48c6a063f5cd9757ad1915e28ef24c40f41767b6a9eb574fe698a5d4115877fe8d2993d12bbefbf84727a125d87aca6cf0fe0290daf0963943c1f8308fd849190b0ba84aceb53336e29d9a1ee74a078e6c5c8b782b71a8b905891748e650d43df410900b1921645186fb4780255db16ca463fef686d88f3d3bf9b700bf20ee1189ebf0ca8342a8702138003c283c021bec728d1e1ff114b8c2e3908693d7828101bbf277452cb7770a70b0ba8dc482d27528722307a6297dcae60f971eb974e4b06c99d04e349ac31c2898fa8b0ecdaa0b4fc437507489ab58b8bfb40ca50e01117539b5000ecd12b65feab3d0ff9ffcb40381075052aa4df6cd2c766413b886c965dc3d19d9de459a3c2c2c43a49f719af5d51f3029f7f8ee05f52cc05af9b6e7a668921fbc564fac8bb36f7cedf21060edf8d4f3863e30bf86e2c79d2005ca55b526c04639932b0bd0623bc711aec4f5132c0aeb7ea7da4323781c1ed62cbfa13d7361e5afb452001545cdcbb4a2c30005d8927d62dfd393b982f6cb822be3a3ef15a260223d9d3d6b81fe40e1f3e6ffd27ab9b65fd157644141e92180846aa90481dcd65a9e4c41d460c1fe7ca1506561f1fb0581e82a388d3f57d6c50d117e622e8c7841b437fd5b2993027609a2a62b05f720f159dafdc8dc5e08589d71d310011f2c4dbb8efef0ad9e7585dffe708c59798308ca8028ed245fd680594fc5bd3762b528179260889e67e028c0387f65fd61cbf707b7fd0dbc733657c25146bff6e1c065ee7698eb03ba9a7082c51841dedddbba9a413a7744acb6bd39a2736ef13ef6a8522c0c7a28aed312a96d7f78c771d9eb0c7c2379145974489ae98dad90dad48f8ca71e4f974457b72b43e1bfb1269fc8ec00c65dbd81ca342e1481b57da64781f8d84fb958dc5bec4f145eea417996c09f6d2a557d55f5647ec056fa9485a0948fef715f060da268bb45936a0fecdb01c7e12bcbd67fa6516471977fa107b7baf04aa6a75d072647bc94e22f7d2856acd9e5d3140d49d4ef61b939b43d9e943b64a41976ac8e478cb52e683cab12df64176bcf5b585e5ec27b7624b3f6ab351bb65a3140383a63eeb3bbe820f9835d83dcdbc2cc5f9774b3267d611a234dd7e3171d539ef859b58a138313c5b8024451ef7fed40a98f1b4f3a8be72836312f9d81307f03cd2611a2025a0845029d838f7be2f405ea11c8636c63f9466c13e92ee810df5f1ee73f5133be9b7ec1f34fecd3d21a77d0bfcd3d8fd3fbea247fce9fc0bcc7fab4f4869df72ff3476ffc7afe8117faaff02f3dff813923fc57ff02c7dfdff01cd6703a4770cd80d48014d0f52a0a0d2d50d3444725a048d9dd97d0a22a84e5e1d625b5e8e0c03cde3ac008a2f9807cc866f0d44ee78ad451dacfdd245cc2f0f58ca47a5a3a2f3e512dfa90ea8b4ae1991434bec24c056a7597e08700cad653e1b5f7f7a2b5b51e0205c8147dbef3cd87f278685ebaf722c6f13c850732ece313342f164027bba318ceb3e335f70208bdefeccc5cd220482f39162e073ed1390a325a6b06a1e4fa83a7127dbd0d6862ef46f403ad3389a76890fcec15d02a25c7caf65e30b89eb20a9d7ce17e6bc507a9469cbfe83e230ec52370f3030cb7ece026deacb2a6079e0fee93e9c8aec0f08402b911af29212505aeaf1818115c81e14da5f62a5ce8fde3876a3653aaad07183fa135071646a018a593a6830a6f03c471d800da9530c6d391993a60a210ef5f73fa78c709aa1a9098b6fed0c6182d2fbda8d5960d39c015be944bcda09a28076cf2c3fb005a5b87918b94df20656763e914a0bf24204df792384d2f087a4158933287d544ae02d104354854ee51abd0a97a04a75033e9cc74279f056498ff88b6c6ebe0eda1d9d73c38a587ed03b62d2b87db772fe194a2601f353d422803da15508c7a3f0399678edece872d15a76004038587a41b4b0a770446e7de513d9cf271843475189a649ed6038a376cae103466631346028534a15c35fd8ca15cb06555debe9913e02879cea10bad14818b6979ff22d12b44b474db11312d8e36e205834cc3c6ed9ff0b7d1d721a3917f8468859fcdead20624913c1d6eb0ef1e8d4316a30c0124136124601f30e624f4d212d64f6e104c5e90e60bd5ba3021febb3ad657d5ebfe3b0300d18d2cbd2be0153953d4d4eec3f574ecbbb414a034b3289cd9de90231799bad29da409008b1d0265d3b06ee53023e4e03b83d343b5a72aea1a55d78014114a27d24de2af70454dbe2398991b5404c44c830f077d17c2415bc3f30f4abcc0165f220d04471d84e4369c10e9f0766f8be7a26ca0ac5d289e4237a6885f331f7b221ae561cfbd59730fd7105398c79e97ccebf0ca6732f57f07c05b1bff1921c8e7aa93c465e76f8ee8a8fbfafe0d6375ea2f26485f033fcb2989b7979d1db0a9efeb4c25c8cbf94ce6bad5047fc708ba14f1f75559ba15a684e08e5e9d6802f7bcf0ab5b33bd94b493437743eebf96772340c4bac8711dd5c5facb317a6666cf38eaf41f3df75aebf356a43d37eca76d2d7d71a8aaa75fe4ce8b9a44527c9c9660c1075280b7edf4e4129b13ee13baecc3a14edef91e9a41b8b154d02a1c10b706a51e7fdb24936c0b88f84bacf2e612deb5d7b37517682687df0015441b9ca4996d78933f650552722782a113b3ded85a50e10f1eba1541fef016a1096cd46af87e33d2d19cdd5316c382244bd520f336dca15cde44fae1e4e12e118c3dc8ad1e8539dc0fee53f329d73d85d062a86ab5b536363904bc8343a9b77efc23a12b1f3c65b29051c6c0869cbb5df3596bc24db3563b24e80b743de97aa09981849650d42837584358685768c779011f328df82225e497e23c88339b8cb78d62c2b6b0a793a225051b2246dc19727486a963578cd84c4d8c8871f8853c1049e7831ac637ea732fea0c55d1973de0f774ee154f0d38f5da47fadbc8bea08b090178e3541480de4919fd6a04a96dd1ec0ef57ea833372268628589dc060900ec0879fb1ab71eddd14c13d782f8433159dfffd47ae038b7b2a324bbd5a2f316271d2e61fab0ed3a0ddfe8d6b6c9494d5b03bf2df885ff761592209e555ac65735a3d6c3f98691087a458b08a1fd49edad606b9d29a4b1fc8f4786da975e4b651c05cb6becebdfb0ab28878c84a857418d417373b225e455d307cf5d2e2019f1cef94fe2a36c592a5425ae12ce9b058036c7fa200360428aeb9876bad2bf84f1e6b8d541d3b54638594aa1bd76edd061ca8662fdcb6f7d53da7afa30eccb2e8d0062114b939a982e5507b1cbe016fd5331ded6db8f055c70942611c64dc2cb1769d706687cad8f85d4aef2ccdce01a789ca7b591991473a8fa7ee6e118ca6e1fabd845a8d4680c80dabae347963a425e6e00359f6c4a4c6ec32304248929a2e7686f80439292da8c045c6478f7c283a27f6927cdbeeee192f7e574a22b339b1545472e9a559eb36212424fc012d89f4e1ea2138ff53133f56c892c11ad996e473d266e7e520bc20cdd06566a423572da17ba082434a8069d70d509ce4d8be7a44a0d83f99122b39f0c83540ab1369cb0228434a20c12696d4d7f3b75893f1b28573dc284f8e23a1f75feb10eedfa35542334849f99e01aeaa2df97c2ba99a0f72b661a70a1569decc9ed6daee61683754fb44d641586a63f5dabdba5efd64b04aa4ffd8c2352fa46ceb3014969130ea7112a4651f788dd0abe6c77cc555e3c8da148fcb0fa43eb321c774a2b9da03d56d3155ffdc1c09d880e1776cb01f25bd8f02225b9abd0b861fcb7ca2df6eb57573b9cfef8d6e8aa334bd457268492de2ac695178df0a63496d3fc4a3e65edd95d26459745c1102198e6149f4de88d0bb47fd66a2675bf8feeed115ae5051e712c5dac4a9d6a7b78703f77bd3ca92f1ba0d46bcbc88d66a154e3199450d8cefa8be5f9112034695901cf12b941fe27889826f9bc2d44d61e61cd5480b147d7fbd448840cbbae97ea53188208235369e8ebf094d58146423b8cf7b90436f0baa42a0b3502949d5bfa2bdc12641172d6ca2d6db8f3588ab9fb408b4e65a37141c1d86c27fbf5e5e6fdc1e683b15ff7e7b793d5274d186473324e16615683c41deb4e1590202b69a0a3c3297427cf8a836728d3592d09e0e14a3a587dd8626c6c93ce383cdc86aae99f31af49e02f4c44094120ab86de6c690b830151445373e701112c50842b1e16c9e784583bbff8b2c3e32537686a91b88b5dfd0420dcbf904f5b3005613dfe6a71e3b1b2bed13f47d156de47755e56cb39d3a28fd720772eb6887a75e37b930c42ab00801536f66920dd4246599d9db9121e05557f13e8b4662ebfc9eddf7b93fc0d1412bb164e1e1a82b00023743e890d8108248f1268af82f0b31c3a3359cf8ecdeb141a1ce5e8044865cf27719ddea796389537e737332a84ea42699fd1dbb0b327fca8ebea810008a27b3a249cad51e52abca3b93982f33a4b38f2af3fb9045440a9e1937f037ac596bd7e8cad8409d69e89a03907d64200ac5b66cbefd08a8f4aa48bec160db815c0d01ed76cd02955dd95b44b962f91225904149f32365f0bcf56d11f2323a66726b75ce08142e52735d10acdb383009e0d149119bcfe197d0e370974389968745ea644bad04a0b89baca342d570361ff714964ab493e0fe9abf90e20290644eaa498aa34cfeb5972a684f701dbce40b40948f850f1c5cb2b010193c9a786c92c1711ed33d255839231e818cb24050a7619e0f7f44abe59c78e0493deaa8e65ca9117d9c54041fbc6740d337b5a77af4e5a3250e05e58b870a73897448dd0fd2e409053ec306236c3a751cd1f1c86604e59808bc0dbf8748d7dfa326d137829dd0d5eea3f89be4d7a3e6f0e391f514fe7edaa0bbdfe3d3d91f75ee5cf46ec474fe56822067e5b9e404380a9b1ce193e512c7d4de1873a5f230530bb36522e79a18db229c1cb3e25a2a4574ffb258a3b8dd73cb992f24e3e5341824bfb608f9a4bf572b62e1130419b9c0c47865380ef60b8e9fc63f2868500cfe24208ce66b4225d2d5483ba378fac90001f415aa43d02c08d7d9b9fdb2d77986f3e1bd18ec8d8ae3c77059e66daae188206e91366490e07d262dba4d37c14b20cd4bd3c0a3280bdf8842378dcd7b844787cb4d43a2f70a7bbab5fc8cbccc546cc3eaf96e5964551625539b4677470100768f5bda46e0e712b11ae5a79affec080ebed579059c75627b43dfcb7c97bd78b91c4c0d7006af4988ba4faf1cd11363b39378b1b9c5cb931939533832ac51677860f18c531afaca64dccd1a9c7fe0f87a390c7b79e42af55737cbfc787730f8a101c8c43802749c7fb64830274d170b450331f050ec75e8a64067e82d8bcc388d5c5c955c52ec94bb8661349791e21d3ecb4c9628cd95ede8e7dc2ff9b6604d0850f747c67a23287bedd090e724b6d493038d4d1f924945a1009c947315a9981f2795457b7a3bd57a1db79ef5fb553a405be69c8eb868af1f8a1dd04b75a6ca690b5ee4638cb2d3abde1da96957c2c6b632ac879b684e135e0ca7fc6b782b859ac77fa68802cd2da65033c9041f03d4ce54df54a5632dac65a67017e3cd411adbc1fa5b3e5e494060485e427bfca1157338a10b358f18d13cabc75843ff19bab40ef37a964f268f38f7f37e0bb4c3b40d35e70629e0d6691a5782ba941e4501ba41f90d873305354ccfae8073a20998459a05c2599993a563f2761440c26abdbac74aea11b1d8be87de9008d847b9ac351fa890f89aecf0e87fcaa9f09e0a35c38c1c4f5982e620ddae5a7d49109f934691f6f33b4758b2d4e28ede6ad3a56603c5b5be0197ab5566a31d5c3646362405410ba9bc00f580a353ca1de5cd8e1922c043acd79494290ac873a18090265c9564bd891a2a26162208fb5afb8fc26b9f54087e1f493caf6cfd74b64193cdd5a37e7cf5bf39551685525ba137babe171bd34cd6eea591921093f8ccef0b9ae3439ad1365fb9c92b2cd7c660334d97af9885431d8ef88a41be2ec78c092601213b4e19f8dabe19f17f3dfd1fc979afe79ccfb96571aea4ef35d6119532d6daf273287789978658a0aba7d0550419737febf6bc2e76d416bc238bb348e36ad80d11602749bdb74caf22f3fc5a26d3c0a2b7f4eeb07e08abfe6bb3421487fecbb8e845fecacbf26361d5a348501c68a279e9283d281fde596e6af3f553cd3ac02087bfbddb4294fab3a0880e38f7127382e9eb0ffe7d24bd15d862f57264436b8c8430dfd5b3bcf86e65dd0bf91f09a19e9dc0a511d99ee2ec3952a307206ee00f212a300e257a60872242600f4c24b00e2162201f2256200e497ca00e238606e23829cebee3992200da3ee1189942b8c107d9a934bb2bfdb783843dea639f354612bc46fb2b55893362a4942a92323e513e7f7469f2c655237b38422739932cd11dc36c04319da21031e61958b45d4ac135414c90d598c698071dc83dfe00b962e32d876fd6616911caa3328bf4bf89edb4e29495ff6a3e51cfff32f37f5d9989834d2248a8425ab0bef21152d1bf9e51bcffe00deaef44d6a64a9b8369493b77942e00f2e7389e461eb4cc2bc0dd360539781060509ce49d8999dcbe958ca4ed7b60fd338d1daf3ce8f71128501c61c52960368c346b1a5779d1a3875a5189787cd40adc38c9c85c81a18a52ff53c62fe20d4943fe67b4dc725aa1788c97f3ebc0680af79614f5eaf43f488cf3f0315b5f2541a4a4106ee94ce70d9e6bb2700923552e6ae99aa3f460da2393cf9b0ec351d6d8c30e4c073469b3f5be2ebad2f87100758d58c4453eb729e3d1ca4a2a08dc15258db78754e299e32151374457cda9c409b0934e275db0c7ef3a32a9d2a4068f3c24a77b13e2b541bcc103a0e8d3d0905695f9315759c9889780b6a07a3c5268a5c23c9a2833ed27e23b5fbd1c0937e64277cc0cad346f0dc15a0a56da1e223130a212e54f73d9bef755997868f8f7275d9a8cda5c080a0b3a69e82794b2a8ca515a774823e967328c25a543a2b269dfc78c75deebc41a699fdc6b69b7547ac0e622d4310e9313a3957b4000167d95d460b5d29517853cd1cc733dfbb2e41110f4010f63d501f90739b7da562a26e4b34fe6a864e59f78aee18b40603dd5237a332c82f899133a5286e809389cec5f75c7d16b872a30d21f0ec923e9cd633838d1484be5ccf1a72db62e763e7c02402dc01ddb416a7100dae791ee5da1ae47b6ca5ef807b04a2284ad4f1cd4388e2d7bb4a64db2af312608ed2a52fe197beab029e6e44e1a14fe8a2e50e479660945259794623110d15403c8fd154fda72912da1901c266478b31fee04bdfc67d13ce401a70b01cda4c4adc0480ea1acd9149415a0797baa645e54d85da95e1e031c4c45ed2c8e1ad157219b90c5c07284245702482a16e4f4de784e4402b51d4425705c4d3eec34c44425d90c835f3114a81a06c66401f10d486aa2444c162059d990fa8382f25205d4bf29253d19677c21b142f01479a16ab4f131e93ea4df6e9b9aec083f0479336300b2d43068c75a4044db4b45e3a7426754351f1dd99e962b369dc837b3b9b315042b5a3446b089e0cad326c178500bce7c12ac39864f3155579c1643dbb4e2b8306176d1b5a54769cd291440d7524ce18b12c21dde597e3dfc8eb5118481661e4bf3df821ebe5e3b31952172cf305610448fd847c76ddd333c5d628954587c67d31390b0c5919c6db555951fae2b06224beacb6dc6904fc0f5ad6aeebecd136da0ae0683bd93c3b3aaa40af540a98137e1e01a7af2406ffadbfe76a5810ea43b66abde0f6bb3d0b5cfdbb61cca3245caae2b5f0159d992aaf73b3df71885a8bc48a9ce2c3dd0c0a51edcd30e1ec0dfe9b12cbd15e4e4a3985d60be85a5c8df14e33d37b969ca6ec355c70314a5c2e131396131965ba01f912c45cda112d1c1b13027293d76e9341e0d4c3c834a25d1c6ac9a267f17faf483e6f5c0fe5c3afecfbdbf7e6611cda1687257f3009c901aec1dab43fd40364af7a0b6090c3317cbcfc05b848b4677189a9035c1d6890d698e82911c80e3324833e980358632c24b9c57cc065c8e01d210431915429805402ccfbac550e60a83ca9d2b5e80d03be9207dadbe68dca582ea1f889bff455b5f54c7b5f438c42690fd06adfbd3fa0594f40bce21e951947ad022cb2cd078cd85324bfe327f40961746633b208121bc9cca2ab7ae8706f114bc514eccbbb900cae3e17f26b772368c214823d1bec4463be08db40a30f694e5d496bd142051b891e48e083e4250d809c7c6d00aaee1a05302a409a0b0f32c06ed8f1034eb42d5cec448099b1dd3bcfa7925b904541577e1f2329b658d68f4366e32ce9a381fa53e2bbf9c3f41563376b30cc2a174c1a71d5846447c0b6be1e4bb5cdc1b22c5912ce9b589957e01f031356f623478c7eea9279bbb4d073c51d816dc26de57290e27a0c125e65ba952ac514164664025caa6602416263c23a4112c4bc659e6efc6f3a3af10baf3eb19737228433ccd50f3309b418995999e264fa003e57899e59df81f1516b04e48cdbbf2c880de7b8453688f02d13b86002b3641251c6517b86912fe5afe4964e87d2661bbb4442e69750ace6b78847f0b4b5a577e060963c3609fecd088406d2087f6036b309de2c1c1a6e92c560f089ebea56c552edbfaceebcf148e427cecba6b3d8273cd6dc3b20f2551e47e9656af15b548cc63e27be85df2412647bd2995b62636613a1364e371758d80ba4a12e17f5045748ad4e94db8384f9d7fcd5e23f31084e62d1c006572f423455aca924841c3229146f544f3c0dbb63a9569a808e9c319acb0706e38e1ae20cf23ed2228f0acd5809ae6a6340a08b0d58f84bb34276e2c218a233edb791240f1f5b387250a7d1b6c9288eb588f178624aa08f80ffe6aac2e178a5cc88bedb5ba1cac1296b791b6e0bb68e04bc27a21a26124060a183a507c9c76d36816f1e1c10901b80b08c14051a714f35bac0506e8b31fb7f29c00313f01e4c561cd9ff7ca3972cf11a9c9e87ca344e9b776920734ff9087f888f7dabcc48d812dda3832c31c73cf0dda006842896444508cec8ffa302840191fddabfcd28a8ef37984d43b09f64ece1e9f142a220de401dad554752f07901b9046bf04c888e46ebec987d13a288389a1c066af348acf03aa33d730f1d85df1e2cc157535cca46393c70fa59214488a66a222cf21afd700dc6e10d56568bc4f15be63b481d3c444cdb41c24d99702ae4701241a68da5170b41070a6307c024dba87b73e5f442dd366d3c199e2e38d0de356976afcb88123e8558ccdd76cab15c8b07b75ecca1685f3d4b3fed27d9f4203914c02d04aabb6bd666b2b663a978d7b43c3b853696a5ebcf92720521372baff33d0624131f04eae10aac3a5c6b28767e79388d947f3379e582de7f9d6659baa1aae578a3f047cd0b9d54f0b184d3cb44b622e697d2d85aa9791d698fb16b34dbf236ddea44fc9ea5b21e133af42f8b6a782a4193b62063c7408816cb2d2f9c5b739003125d9ae719127f69285270e2616d129196ae38203b7c1f8be4ad2e04c1ce515a74771da03643bc2309cc714eaa7ff47c761e337812cbb253450780f2092b92d90fa9d35267cc31d6da9f0ded08135d052229d422d0d02837075b85417136799181bcaaf1e1644c023318d78d4212a227756ae64d8a24d4f244aa48e6084a4ab9796aa0884ad78d206cd850c4953ccec543d0be6d1318fd8a773a6c12ce627c27f4896ac6ea3445162181e45f0c8638928d275b4aed664f7aeaa2941f0de4d64140ddac132ca7d56da30b2f5858e6d3d38d17bd482ca5ee1b5a75e1a66a9108d5024d155af19b5f06f9108812ef6f58bc8aae75c74b3e49f372543231426e1095ae35cb3bf6708813a80390b6b9acca67b7daf1c5a84e6638ace927a1ad57c443849059950900004f670cc45ccd41376e92d40f34d56715afa72250f2afba15b2347fd47823bcd732f7203dedd17363cd33fd8991e726f1bbc781f1a140330ce405084661870025033a6dea791e7d03550c6d4293380a04d3acd41e2e86bdd88438f010e23e8aad67913e1fd9768552359ba4d75e636415da48bf3283156e0118b3e183907040f243d49fc455d7cac5f509288746b4980481a40ec3524892fcff3ad4d806818931fa3da16f8a0d9f00b070edd2f99f174deb6f453b181ebb52587bd1150a5bf7f5fd65c5bc2203f6cde06b0721bfdeff3d66ba08a2249700e70b2a81e27000d88d7f16487808d6d270dfde310807a1ed90d933038038e86a67c1720960a4c86f6aa4efc40f0f19928c69ba63555e343d71e905868a82cea00853252f1c5ef0b160f6962e62c92b20554c7131a3a43719484b7bfaed07c97bcaef9b86ae6b3f71e609d6a1fbfd6c526a14437812d6e772efe20847bf5722dc5d9a40799505659202f05240aced66930cfdf6e566f4c50cb050a7f2241decd22941a091a2010ec8b3db37c8d32bf8430f884cc7f169e225a3515a1ffced7306a933a30f1bb22a63cd758ebbb523e9e2bfc690df0781d0cc395f0d9325e8c02003acd7b4e14680d48d77f6fe00f4b65dfa1e9c2ae2e49609bd681ee3fa20980a46d7c009935653869dd9e6ba6635f38bdc31731ed34675cb2c41db12007f1fab33ee26991a169900133fa1027c872ed2a984ce25a691d6340cd277c569a2ceed53e28a8220949435f4d5b81522319e0234c885f82972433aebf089aa12f32b073319816c38022e8444dd7ee3619f6ebff134b10fc63b8ae6e2d8a9304ad9efa2ebc5f467db2419a731aa150f30f6442f15a14fc95ad4b2ff84c19cf358a0b5d0c9d244a641cffb485a00551280e27d35567d339691b94c451cbbb68a40a2ce6242a2f780c84876f46d2fc11a00b17422eae0e6a6316f558dc0e5cc01746b17bc96952701dc5009246626cefde182a1a54a5187dbddd4d79999fb0f291c10ef934bc50dcd3b93760b21e6d3ae0fbb51602aa567e76bc847cbf82b6d79aeb09ee38b1abefe3b7cc0cecce8e1dd8d7e9ab6fbc9bb65ed688c30bddf80b4c804c1d74937c62a0decfc87f08fddef3874a7768130185c5a3b1e360deb30eb272848da03f35a34fc30ed08f5fb4007400003d326ad30f643bea1b08a616076aab8e9f208803c4abbc713ec50b0a5e97f67553b9d0e759e046b6f12075e34e93ca4dc70feb6a2e029178a66d22863e49dea53af33a505e46c9753ef9c496d322c980323ef20a0136a10f51b9b02abce2d3af5280be7dd0dfe2337435fec5f3d2a1ef583211d286f5ff02a58fed9a36468fc69413112ca61d03bc2d3bd4fe2da1e2f4b8cacd277d6c678a848a87081abf86c1813beafef5d284f2daed8ff22406cc76eba73b4b0a25fc741dad9892f3c05344e8538bd7abf7f1dcfc8e17f7d1f55c64702d40982e132b256e0b2f22807251f1bd506bf4f462c78702cb349c5ed9e8b6b023cac9241eae851571a3f0bcb0357a7a815ef421ef5f43de874fae40605b54a6530c259509212d4cacce248cf8a0d175b589cf5da87b6cedc72b3efe19d39b9190b8b017c6008de1b4f1b07c4754ec5e24db4939e36ea4d7ed793c948dc931c5dbfbc0f36dd1201ac039d59ced8bb140e881609f94ac2fe9f25f24eea7eaf482322a1290ff54e2b799a5b213c9f3fece9425fd04d468d03f74635b448c18bcea6d90bb0ce3e2eecc42696b3bb691e09e29d9fedf1861bcdf1b31913d21de27a66a9c060c8c3c5a19abac084b53001edd6213fe12e0d0e38c38ae5f8332bb0ad065e12af43ca9d0a581fbc951a106ec43035568244306b47f989ad6dde94fbd30ea55524ad13d40b42197ef8a4909d20bb0803075af5d1069f5c62e6149112293f9561c593179301975ad21329a322282434dfce60f993c411c96961a207cfed60b6a133fd8058899f845953f7128142fa7edb0a51a079237ec09a2fb182f9b03eb272d0ecb6a0568c707383bee5fc44740fcc487fd29527f1f42307f74bdf1139ba6489ee2a76c0162e6e703d7866c239a32acd9ac2e24e06e6ad97430a33c1b295b4668f9798c3b2c187c65503c56fb1884e5b6eb7c9e886b05f12c8e83ba9b34bc538f398085817e93dbbecd89642883bc438f0ee0640a207bdf2b227fb625fc4e479b9b05114b1966d20cd0f50a26400d72a07a85b7f3128b01abde44b235edd47d5cca1fdc80bd769332cb1e23c6e75537ab9695a369c8b1181e6c3c4a90cc182b57b47399b80aa70c6b8e2954a69f36454aea43f8da8c52a6055943830bfd0f8fff31fafe3ca7059bb292bbea470affbf9e92b1bc631842f91dd7a9ea555d457c6a025dcc312e3a403174480b016190848680d6fac44ceab201a71d184587541349c8d67118ab1c6444d8a9cc8db61f02610eacc34763e81eac09e85a2e27c3a855250f1188b1203fd19a2f8327a7c0b2b366fcc84580b3f81316d4ce74dcb4f674373c8ac5a68d2b91332836d1522f5f349b17494601252831033c51e458ea6c097505cba3531de1d3a4ccce2913ce2e2a1c1193208202ed2e8c923970ffa480a96ea6073eaca753a866434cf183bf65fb87e7abae3b29fa08c5a46bc3eaade7a3659ef26acb6fcd3b86a1b79e4c9f468d6edc5d4a8aa6316631de934637c736c8fa84af58282e6b47aced42eb12793ac67a3af672af50a9ec7ea12b8c07b9c16840c559602f03e0e36b78070044d91be0a301238e01939a3379bd16241ef60dd5fb1aac2d18cb1d59206e919f6547209199ab0b6928f87c004552202a236242f0348e5810ff00e7dafe868fcab4fd0bbe4c2aea00aa0d32f2fcece779a1c95cdca59f3e68e463e5c7d7f8d2ad74f75f8299fa8bfb6c3ff24f1c7a6dd1cc9b936e45534261e3302a679897a88d84b50459aa357db39406d8ef3e1479155b05ee7f809ea986a59cae02b9c002ecb48b1892dcd4e052674f4fe0aac0ee8e47e77998046e0161713a1bf21fb60127f809a7bf66b9968e8915945bc2e2cf8ba444450685b970841ba08f3aa043f55517d78cc0b3c95569b67b1870c47e3489b306dcdad288d2890152cd45422d1e706273fb670516d35d568a5887225ceaf56ef365412f03673083ffcc4f02459eab3791ea2576dcd7e53b725d0c60ca8562aae8d721e3154d8a7233fe498f9af226e0d1475af31416836aa9360598d4471e39c69c437f5072318c68ea12c1659d78a5689cd9f9260b9950c83ef1db694a997d7e4e9ed7b5986929c53a400e3ea6c59be2cf98e23b9785d88b19ab09bf0f6223a1d131497a550779e690be41a09f9bbc2abfeda021bb0dec10d9a555704aa3625a08651ec0f29986670a56628f2c0df2e95b36dd29b459f9c65511df277e5298d3cb9520f0e4ad5fd9930421b2b1318a98758708de08e49cb0c030e0054a1d4e3aec0260c70f9cd758b37e8ee986dde1ec307f25cddb3897ecd65b7717b0e1c0601a53f4afe52be459984865fc9272d36a1ef928b8cec20e0c531952d6843d6fe159986fd7e028b5b1f78a0e597bc834655269afc75d225838ba27a324dd2bbf0ef38bd9ed56afd4a918cd775c9079ab2f8a5955013e805c15188619e3fbc5424b70ad4cf3fce65d7f61ce6fb7f2e69797b25d7f8ac264fd8e63acdbd429c74dd79871e8e013e6622eb7adbbfab01b61340a952555905841432fb1d21ef7fa3be178d8f6665bfc995f59e63083a2c2d0c4a4fe5db0c0bf5d4dc5e79cf04f4a87b99dc560d735ff32d55c27818608807865e753f6f0f9d643a1f15eb2e78f1ceebb19adf82b9a467b8c10526bd1ee3721274f03b3b94c83d51a6446874dc538995995fe3381190b95ca2f391dd294c7a66efb323edf8473f8e14d96dacac4ca67af61ecf2c18645a06d60150a43be0a83fc561e70e5cbf895fea23e3ce29c596bb5466d7b28c8d88e3ed83a349accb116230dca1ffd30e3877fcefc68ce349bc3c21f57ea9eee1abbe8252ffcb64445b3a869286c26838532018fe4f3fa79debdef722cb993d4d58ed4140af16d17e58354e0b311a447e83602249b479f52ccaa9f0b52b8fa2d91aed5cda8bb036d53546280f163f8e2da4458f91383bf1a9a62875038fbf34c0ccbb7e64a34d0954429095a829fa75609de3a5417628d086f3d4d96897845672a8d424f11b377f40aa3b53b155324feb54cbb8b589f69e254462bd3e6d126b1e523ce4efdaa280d5527157dd9ac23a6f9fc6823728392d516498688a4166bec09f762f5bb9b334000de7cf464bc5584ee51fd7431514587dfe03c6f04a952f68988d2c86b8fb77627f5e8ecb940b71de7dabe8fe752d5708fddffc09c5ff13c7289eb7bff93c05f9c6be8af20c2ea989eb7a30bbce8baa36d307415967688bf0ff5ed8636452b3bd83ef53c568a9f7d84cec0ae65b29a458241fa96d69764b6b4a5a87bba11cf4efce2cb0254b00d911673e117141ca119ed2210b5375cbcf63749c064f780fec56939bf04056121930941c86aff7ed30698fbb29129948715955524db298d36e6a676fcb141d6f217b73cb27b0ab6b0fedbefd714550555555bf4f13cd503ea7fca8eec3deecd810b0aaaf0d39da55c229051bc7cdfe72b9ddea7045cf1fbea7614cafbd8f16b121f1e935262610437586a4b946348ce8053e3c3471c788c513a259d1ee5dd110da4163ba21505df3e7952b7e60ef93f5500a246ebe3f922c1484a38c0b1aab12195f3a6ee46bc46d98170cf420e6297a3ba2a49cb6005d981f5350ec7088afefea860309d2624476e3ca6007a38621c67af3fcc6c42d243e7882767ac62f4aef00cc26c15c7626ce97b0270fb1cc89e7f682a6cc01181c7973ee227104b843e7b2253a9fe20cd18368ca8c72776e4a54a1caec958b8c5353b55206b5d33c9a518878cc741f28fa4cb7d84967eb56a04c6c8000504ee00b53727afc865a968515612ed7ba9f70a347ec64b9150a751eb62fcda8c9902cba54ce42c56248786272d153534455a44e546e8bada4094521134d59ce7a62cb35cf56357a260df03d496bf772fb978cc501729f43e875e789bd6413adb9bb2bfa8191c69eea8ae72fb560d5f3e6df4a9a8c790991fb4df07896ba4bb6dbb53e426f97788a8f2a90ee507bb7eb0c1ec2a6401c82cb64a9950f00db8dc1d317252367311f49411a994d08b6e1400432c81b92fdb295f5b29dfd66ec244deae3a20d85468d8e7cb82c8238f0420a64730f267a355ecae3a955fdef23e4650356fc3725f6b7f048fd28266c17d47d652c105e4bf4b87cbe4114d994bda4150df6e83b1b0b5dd24bf0f540218b442f302ae2da19d04d88149f557b6943fca0560e4cd1058471f86ad2976dbc79d36385d588352987217dc1ee8ac0a6f4ecfa85288ad8d664090454cd738e66b719cea765fbdec581ae8f85afab32e68c92bc38460a2ef30e61ae4a0dd6666c848ffe45889715ee87eaebe4675f37f1db3c248cd72199fb30aec396f358e5685b53317ce0e0d36abf55bbc1635650153778f47a08ab3432b16f691516b58b5c0bac89828ca2e53aacfead48f8c99c14ba43d26e59a251bba006f5ab800ef136d7b0f968c47c2fc20a17dce0399b9d20dd8653d212bb95b896a0bdf984a6a88b2a543d4f779402959cff68c829492ad2cce98940d00d463838e1f08351aa657d052ccfc8ee0980a66695538e707165e0858b4511723f67c2a605eb3456e2b482f2b94d89dc05847ddc3828d943f7a78e9cc19035185a4a7da2214f535dfc9a7bf57cc9c9b484ada63b151cbef387344a6f644613f228835e95bf5f2de35af63dcf501561d8d6fc9c668df4033e3bcd67ef22bfe9fa07c120d062fbfd3a4663b83fc3b107f433d4c14d8e13baac3aad9888daaf14e3f6b6f8dc95c32d45317dc2ffd8ec97662c0b2aabb379e6c1271aa80aa581212482113405fa6fc2bb60ae1c483d14a7cb6095d3ba83b8745fd5a5e4d93e636d9fbe540c186b8f9b637dee78738895d51c426f9f45513b1d45c1f2cd2f49ab5223d438ae01a638e87c13665e1a63c07b37a94f028d53441d713a0c6c9aa0eb07b6706dbb690b74ab58cffacb3961f1c36592d234a463de9d4979c47c41851d46fbb81a0f1da2792fb24da19a0f5cab64fcbd9e7662bdc1ba436bfa64aa70f58b1eb7a71874eff23fddfbfdc19c364402a66b2dc311aa1f23cf19047f496b2cdf02b06114379aff24840b79ca4037a9c42835830da19a50366a99251db25fa1dbab9ea24d515d77a96ce5ad355e8913b190ba1599ffc1bae86dea94290d91111dbc7980218fa0e0098693b088c0314abf0b7545e450ddf577be18b117863f74d5adb7b932db79432a594028e08c407fc075c141810651abb426d843df29e8606649588bfe82269059898e50480d9005b20fa433e2b93f2f8414cc6271eb000d39b6e07d147c7f406d273f9b8854f465a8c8e6e14ddc8c2a72f3e7a932658f874c55ff988030a9f224a15f9e30d213e5927a41b41272e1f4f491f6f58f13f62908e8c5600c615ed410c5237b70388736edce8f9e83f2cc7de663561f427e69391d0b402869387dfe1f8c10164c4394ca5ab31522b46481f1d0714ee667eca77a72d46dd8da04782e5bb1b421f6f28f170c3a751296c09a7a3935111a6d1e98809f477323a1db50ee48ebe9359bd92d1cb28cc4e667532ca56dfc1e841860000eb1a3480b67c74ed6c7c27b37aa94ee667c715a4a4eda36b07b770b1f11d3462199f47909b473133a951588c143ec5182831aa6b8b018aa912c42799cdf26b8b99c2391a0325464acc1422ce915965ab4645cf5c5927068937991e3e459f478f9b4733606dd5b08942958e416217c8b80f6689c9a8647a62905a670710d28c1a9a1d42336a6a66405901fc6879b2a9e83534337608714eb70368c715518ad99d8c54d01f27a36a54741e1b9e436ec9e9a85151c6a8f2c6bc742148c9ac3887e7e30ea0d6d9f1d3a8e83b7e3e7aef000ac0eff8419a3b7e4a42df9d8c6290f8f4e227a37dcb2deb9262b2fb686b0325cb9b63bf66b64e8607845e0617808f96cc94d8c48a162619904cd015374449d0f2d1657e5ae7a5dac2f1535aa90e7d8ee28873e61624cec1b89c924e61c496d91cb021793fb76e1e7d4cfae82f27995537b37cf418550c96ed91d43a0ad431505847a6a2cfa3c6a2cfa0982913e9a3cfa223a3efa09194aff1691d8ff9e19c182b3847e5cf4729354032ab98263e1f3b0cc8cac1bc820ce96e2b4858d5a831b2120527b0a9688b8d8a8ea2471e7c74b8e523e492f566a424c4bd0a790fe40953f81424ea408fee2d65973853a3a263cd6e6d926fa0d106b5c0233ec5980d4d769915e7a8960dc70f9fa2cceaa3cc2aba8c93e8323e9c837d741921cec1125de6493c194df92803f45126e8a3cc151f657e3ebae49311e774387e4e5aa285e387733a68641941a31d44af07f0031afe1c8b717686d0b1e8fa129d5fdc06c6db67b886e572c22cf160fad310699d20578a4ee0f3a8d7a124b63c6672e53a195e95e88c2b8daaea20d252ce89cdd0d4ccd0d4a84fdfa9d675a9c5cd6c9d34c960da3a2332fcbad2d8fa886923f9f466a39ec778f458e369b8b89da6d778dc6a7c5261769aa4e4bae09cc2b4a1f1193ee3309b0d8dc3f83ec6db6b31b44886cc3279716c33254fc68b91194fcf65e0530e9cf39c080570b0805b72c6e15604ba65b9948eada639e64034c79ce77178e859b6998c7c077d34cfc3cdc897b624f04d1c02f605cb39880320cd86e4078c0b99e133dc6b195b275fb6ce04b3e9c73cbf3a7d07f0c362763bff9c8f98dd5ef9e790c389dc29e83ddf270b94c417af595e8792e879d0196a9a10cc330782695c075d3d736cfb8c7366de20b8753c4bf8f454da2f4ada07b7f66303b3e44a1ee618068f581f31cbe7bab41f57928da5e4f912d536d915e758dc5ba2193c53843def4dcaee56e9104e99a4385283b4dc4c49d750a35d48e9fe524ace2425cc6e306da23f2712c4014392bc67a6f157aebc6fafd1940e663725520bacef9e11a6fceee15c3b27a93c5097ea10e064e2f18d806fef2ed5d14e4494034c6f3a252ab90d9bcb06eab489fe3eba4d744d592e6038ade34ea663a507c4378c628f384213661cce83a26484297f9f98ed90dde14699fe907b42b2fe9fc1ec8d5f7e67ea31b0d7cbcb7c6a1464200feb668ee12e3fd5177902b85d127629857eefbdf71ebff7de93fddedb32d72f2873dd04dd4e29cbdcf7de6b8657d857841863d4223001655f26db44435ebb990d3983df152fbe9bb1ba5790f8a482091f6cde170329719e94120c24a8685c869048a28819f33a840493262c21a59437a728825abbc2a4f13a7404d1d011413eee538c524700f9cd901142df394e3cc224a7e9999ec9e48bb323579a4e3829806166666666e66e668a6f138cf1ed0030654c888851ce002610e2db37291486110d17420960f0ed392e95183594078e35c010864a30826f57124cf9f61b9349149b0dedc793e1bb4c223541c1b7db4cfea18a19456cf18de5db6b4c28c27839a2889f02d090ccccfcc413244e8034822c3891622923380233b3f683b180aaf1018b467322c207df3e43fbd1466066e6991edfcd20608808274380f1ed32b2f8c206460c8d51052a4444b1623a61967c3075881517325bc8666c80a1806797b3a7896737f5243d7b8ceb90440f86661085131fbe78c2872e62a66a2184cfb3c360c1420d90c0a58b145d64e1041834dddd2f33366e0ad08510413cfb34f90927082acfaeeda81759b2e822850a90483a01947618babb33edc75b2f805080ef5c3800932c4340bc20881f98b4e0d9311c24954a25441737d0228314409145092952dd021298babb4b45365f78f1a6ef60904878f659c5b39b867ec8f23c3402a02126450cf9c0e4d9af1d66e61f7e683033b3542c6cf0ed1633034085831966667e3b2a20be1db292264cd0858b164e188105163cf3cb3333f3ebb26e068631a88c912446976f6f93ce2d5421468989a30b6c0bbc052c3cfb1423896731a2f04c53c48c55a4d7277c67e3e63b179eb3e0c4f3119e5d6a81c9b316b83cbb87718467034099a52e3ed60f287eae2e508c883f9c10925da6804756172b2a783fa448895d8c90827ec20876490a82674c51ae304aa3c8d9450c273fa25065e108247e3cb16a94a68c08d2b2c082090c132f9e2cc10b2a4df04207568ef00407d8ebf7ac1cea850d50a658b9c29430acb6dca5f680c727cea36a42da2d7f5b91dee47320466478c975ed3bf0fcced8f01b3e3d2a3c19407f8da383f7de5bf8e295bd26c1dbd65555f81eecec690ee0f90e7294553dc25cd9fc058f31f52d8ce6009eefae674eee10a60898a5114cf56e1f965453df7b3070192e67d3dc0c18f3bc5d29bcc8c9d7e56e1eb89c04cc3ec9fb78e8beb210c058d20315f6e71b40e092e5c9ab152eb8bc8e572b5c5cf138288f65ef7608211756bcd72e376bdb82caafcfffac139c4afa1227e315a66ef19d42e1028a62c7360ba80ec68189b9ef61cef372eba492d2866d358898f2a5ab15223cf11837cfefb6446cb74d3a250f3995424c78aa364cbc73bb3937c20302ce723fa86a43339994776642116cd69984d0ea1b86ca277629f535abea5e2f0ac6d07970d036ed487c3c741e86de8dc3713c3b0e06bc9767cdf118a6992adc319eb7ae071f58f5da2feacf9b379bde6c9a55e8951be3b1c7ccccaa3a640d2ec71eb4e4c539eb43f2bd3cad5590e32577240736c3fc238be47f648d5e7a8f850ff3c18a19bf6e7f4f6a33f4779a10e8aa3d4046ae93583be65213025d723eda068f6d452ed72f6d452ece267e6f5d33e0257784012fdd0763da3eeef7d201fe1d62474e08bc8480a9ea44292dd9281fcc3cc09473bc637e6a454e518059329839fa98b703f0de7b6f5555b5b9a90da1aaaafef09eb7bec7ccccaf396666ee02eb25ac2a0e2d6007b1832348054c00cf99570a3137000210801598439771e4c86184d17853cdd3689030be2089305af22a6599f6021313233333338326ebad9f1f336536356abc340755c7f3178267df043c113cfbd6b069e61e96a7df5b02d3577e1eb81b45c999c3f8e1e2b345cb32110c7cf881ca92b2c5a7e771575c1cffc5b315860a72567b7b33abe20830765665cb52ce988dc42a4c76cbf25d08db08ce43ab2deeee7f7c8aaaa31527086387aaed6fd37e5c6e793b06afe5de8ce06837e42fbea4bf22386f0447c80cb91de079ab63e727b7f7d8a19019fc98e76968ae03da03a2ef7e91852f9c5fbed756c4521d3998cfa15c3dc71842ee9132d365c038a7d34ca7a9719a9914a868da613b84d027f4e9188ecce4342693d7a8a971d375f97a0a56af3f781d52e2c96303706d06ec949d6e6ab86d9bf11a34343e85b01c9a909d138746038d4f57ed86e604bda600d92a8fe8bff19a9c86c96b6c68b8c966dbcf5153e334aa3e7873c771e3468d09a197208470a3c1edb00fe3266d06188f718c0019f7b4c9a43d1f637453f418893e463779f7565d288f238b44b410688744310be714d56842dae1cc0cd7ed77357cc6e315f10af4e8518873666842563e40eacf6834cc1a53fc699d2d7239a3efc76833f0d77436be33923d0dafa1e11d741b2f02ddf48cc06868dcc6218ddbd8f663b407d0b8c9e4348e31dc6868dce43ed806dc76d8e13d0d3f02f334bc934ff3344e8386b36306c85679bc902d4d1526cc979c060bb37b2b1a28cceea99ec6311c9a2d434a207d3b0d678d86761adc0e5b049aa00dd7d53071456ab8c9a3c398fc45a3a18669d3376d34ce1a0d345ee3369bbec969b61df64dceaa1a0e43e3500352c4e449f66bbc0647b36546134783eb4c8e79ace18a98bcc6a3bfd4b8a6d160aa4140b6ca638622309e64379a2db3dba419760c1a2acc6e939ecf0af35864fdf2e89a2644fae59946c372af62f045f954c36d93867b09c08d97335e16692fbdcb1ebe581936b3e9c3c82cc976b0cb47571f1c75c3883f307252e6069354c7f3f8d33ab08ba29e1bf16f887fd187a73d96f29c30ea61dc7bf2567c7a5c149d7b1e7fc139924f45902b9229191e623b4b435e3adc3a9c8757bcfc3282f3d1a16534b99f25b47c740cdbacc578e82ca1f4a70989de62a8beebc18787633cd6c3675cb75ec29c5b13c2d3638cf13e9a49f0a54de556c4e26c8444f7fddef6212781c06c69d904aa67559f33569efb866ff8074278629d17e70ee7b9dbf1176e1f3779f6c82159fc248cff612f6d0c85517c713c854ffc23c43fdc3d21cea9c2aa7f4492610a2e8f8d829a111df804bb9d9d8e4151f04506d7f9971c26c69febb39c323c06cd698732d25a808cef0fa326a318359f10980e4938a73533c2270893f009aa4ab9aee42f8ec192cc0e9295b1e5a043b650bb4c29e713b39f98cfb117b3213c5c2205428cc3e99d497e8c77ebc5438d715e14466514e34e1b0e9fa0c7a4e90287c327222803f322054a66a2464167a28cc3b8955205d67cbb6a34d4fc4ee1135c2848dedc80ecef0f14f3f9cbaea0d9dddddd329835af433e5c60bc93e15f02fee9b8f12ffbfd99798c59311ce015a597eaa63da1df9fd65925280a3acc22030cd72d160958211d1aec96082b71ccad16d501bdb7cbcaec761e3640877c8c1e3a91868f752e5f80f4eee6e14c3793e592d261e4e0121531a1af435e957a783cdb33965062565f5555e5d68d6cd2b2b2ea5abcbbabcfb974b9aebfd2b6449b854f457c3282430e1658e69ca345824d607a8361980e49942cc15c4b57a682dcf3b41fcc319355ed8f8a73569579fbf6ac132cee5e971bd9a4874f323333335fd7d3a74f1f3fbf984969e3e115fbac4a429424e19c247cea4c2ad26e7b6dabda9e45e2c2a75e261209b353241d9228e19cf51fac08adddadda00d3b7e964f2530e7c364ca62c6610ce698090c9a796df2be09cf7de7bae8fe3793cbd3b05c6dc42efd7457ef861a51566f74cba8276999dfce8f65ef58c7e0fc62c6a3fbabbbbdbc75a6f1f086394cfb264cfcc4c93d194329362c386c98f4cd714f39a624ac73863ed875c2f4d31b32cd37e5c53ec6e777777f7f65e53ec6e777777f7f6ee767777776fefee767777776fef6e777777f7f6ee767777776fefea72ea3e9a6f7c81294b2b4418a133430821fca1de03f33c66276064f92e7b0603cb2b166058f1d72b166000bd6201c6cf770f0b309478ed150b309afc7e00c8053f8f0701aaeef1d661950aed645b16c4542e66377f378c8979bae932fd3be655afbb0044bf061133e66ddaad1ca17ad580c4bc0386d810844f6f073308b3b11da6f680cece97f3cc33cfb8329933f16991483fd283ea4b1e370b4992f9d023c786fd338d5901d39bee21fdee8c8d66c8cc90b99f9472996c13284fa03082a24885412d7cb4650740b8e271bae9b38a228a2bf467ca5213134992f95d832af485f555ad4a799060ea901428bfde7152142b723466bc49d1f25d07e990942fbeaf3416d55dc97302513dc1acb51a44cc07a49ffb60551dedef0ab339c875cccb44fbc1bde21c4ee21cc9614498ddaaac18204ec88098d2b18784a9e2ec876973c3c543c92fd588f4d860dc922e719cc493bab8166aa2557510af5aa7f7677d553d3e52808284ae10191d2125b18a7b5a4753eb0dd452be7f987b47981d77319fd5edd20a10c20d6cd21789584367ecbbc4c4ecd9a3d6b98e9e3b757139348a7bb20e493617f87cb7aa2117ac268c5bb73df43cb9eddbb636e83c0f557567825a677d50d4fa10bf2b74a56b2295a907b3e35955c1547f485c33657ddb577dabfdd024e556df16ca1ea3de4b9a3cf955f144d25118342a27a03cbeb4e4606486082d24b0821f6ffbd80c9f5add27f7880079303b9c7f38fe9ebec7024c6f600f66b7f60230a8c47cf006a654df734d9a9dff73c532bb09bdfdc14704858b16415a10a90c3d41746a04568a4a07deafb3aa67042929a0f46cb37c8d3a240429cfbe5a77ece871eb1a3257849ddfdbad21e6f3ee2fd2cfedee721630bde9e26a91261b7cc33a378c6ac702178e4ed846139e61128636513fa3209d13a3b68a0a4dd98132e4c12b449f20435ae7798cd0e23cc29d2cbaaa3ae4dad4a828cc4e8b6092aa67c551a8759eb7c72b7b6ad446a2e519841e4e7aac7af8f438410f9f1e3edfe34a8f1537e115af9e857ab46821428b966727ab454b0c4d62e88961f53108c5d013c3eac995e512c255f4a59eab688336e8f748c9066d15a0e7f37cfe5d59209fe7f3446ba2adb412682b6df59a90b6d2562f5306eacd72de1eb7ee0b165f977559ddee831baa1a76bff760decb9a7c66c5fbe0c69821607537378fdc98028c57b7f458677c3df6320bc6c7ac2cc2c764665d992c655756c2b20cd366607f25ece55f118f93ea500821e41ade419745e4eb8c6410722c75a4d5a82ad48a5af97dddbdcbac422fe59c2693fbe9c480853e58fbb592f9f9c6dcf14155d8a96a6b7a526e666666b6a46a2755ad2f56485180045b3de233cd12db1e0f080ae0442b46abc811b03c21094b50e20b26a4a215af5828b1ba83a115f8e0cc19f93ab482d577d35fa07a1dfa71f25bbc0e3581c5b73f9f7fae43591cbd074cdf2951a9549fc494437b75ddda577bd67bcbb9ae6bd4e899cd1569e7d7b73c0e31b922edfd459e7372e84ad277607ac7de52907460f4a265bdac497777cc92580ffd7906b9de9ec36ee8580379924fadffb8d71a6c93ed59229a1cd13dde6eabbb7bf81779c5cb0e39dd17bae326bf25d836a80b79d51baf5e73aa47743a456f04f6c4b08aa1490c3d31942086550cab298ca896abe8ca7209e12aba8a6600bb2456d624ebc988c89a644da8108289d97befc1071f64d81e39dd179ef3d6551e52b12fb0c76e9e6ee96f0a4c759799999b9b85c0a413d365e8193e4561762675c146b9f77d8508db8d57f9093901fa02bc0a2101657db3616f27df57fcbe005ffba110321fb17a787f104000cfb6139e76c2a755b8faa3e3be2d1865c0c80d23a09dac7460a4aaaadaddaa3b0c43498889a3daa6ce462311b430839b636076369e3de7f90c15fab657c4d42b66e7ee021c26aed838b9d0e4e83b131345df49938d930b36ac4a5dce506e4c7a64eaba38e96f83eea3a50e1f2d2bee78d58428135eb92836d197a8500a04cf41005fce6be6a28d7243a0472e029a821c0ea4c1900a8a544044c4d42126825e875470f4ecfa434c000d2d516403ab52965b1dc5e2dee64576e85797f1aa0919c02b1705042fe735f71863b022d9571facddc080d6f78c30e51c758c5fab6a0e23e1d7ee24fccaed1d73127e7de5da0915e9ba994c619c51516b33ee8ae180dc9048c74adb2a0ac2f6a54196364d651995e832badc6c6ad03c73221a97399192d718621ed1a4cbf5e879523c297ea58d49c92793cc7babf1c33ca2499fa41fbaa6540f10bd9bfb3455da34856d9de91f763d444dc88e2f591f33a83d97a58dca0e1c1b76f7d35e2e6e078ef0756f0fcb231f5c02367445e893f4e78002d3d6a162f905ddda9040a7e17debe9bd8b5b453db95179ef5181da7b6f23d243e5c8ce1749d22ffdf43ca9cbb1233b46f46b63727910548eec7c923ec06a4078acd4dbf459888cd520d0df6eb5d66e5d1b951dd8ea6e095f24b70373163f285760d65a0b2d8ead17955b6b0c666f078e707180c9755effe59058fec266bcd1f0542ad5536b1058f2200d3c0881de4b64690d08cfb7f70e32b3b65fa46b0f781e6bbb082b40d0727294b6c95443c9792b0d2d01857b1d5aa2ca9f94077b3b4a63d63701501e2fde5ee3fd303b2522b2bc6d3610698ec8103e350c38446c380539ed9c5cc0f1dd4cee303030aedae3ba6dc2f3cc0e93693c5a0663e253c36c3616b3b2ebda27acfd0880ea68b7b409c4f8e5abbd70372c592700a5a65f1eb3d9483ef56c545b99f6000df36c83d17e64bb9c0d10fd759e7f5b275f4997fd8361988de7f9056bd229794c3ab641bff64709bbfbe81a3875396f56bc06e4fa3d92e337e8d9bb1e7a7e6793deb3c137db63e3c38840042eaf3b78b542042984dee606cd33d7389b282f6e00a4d4120320a534cfe425e5be689d1a299facd3ab95aca343a3d647eb6c128a8ace449990002c167d54a92c75a23aa2c7a99a269cd6d1d40d66f4bb457f51b52e35d95215bbcc377c825bd27e302c7189d38edc4d0fe11ce8d029c0366079e83a402d0f931efae01ce8d021844d78e816c70d511dd08356a5052397721a59a0f7805a47038a7afe829e9683ea782b3569462c90119094739a4c0eb47e2491669205b0986b147f90c5a83ed4a5a827a7cc76f60ee7dfce731c191806ced1211a60f9e743de2b1a2a55015384a94334b0a28ab9bbbbaded23eeebc7c120c57c1ec401399022e8db7168e87ce262b277defd7ac0304458d18229ab6841d3a2a20d4d9925b0508511df4d7f42154330314baf43416350010b3e4152e832add7a1a0a320a31920050163666e665ec9ccdcccdcaf3ae2839073703373dc9911767773ddddf39983344a9973a8beedf57177cb1caa13a12f17e19b9f83193355c1a42a37616077f3a95989deebde69e75ac25cefb29f42ca138cd1094b5a325ad2a515e1ab62494bbe9556154b5a32c297046945f8aac0172d095f4cc283f0c52ad282af8a252d99842448ab8a252d19e14b82b4227c555ae60a98de746bf42c99fb455545697e6daa288d53cd57fb79efc1dd9cb73f2c8410c268bd1f950e391ca922b43279f5101898afe80ae0020e4a7bec937d81dd72950f11a22130fe3cf9f9594be513b96eb3f8589c65dba99f5f1a10e611c5ddc7b5b3b3233520ac999e705e55ad579fda6355fb02fb8c225788fd0a5ed5f6d82af405768bc3b2529e6ba03af8bb5505c92028be35b25a0610f2ed4a5dd7e5fa94799c1d1bbcd42b587ef5888a9eef1449b95861fc4ea183b7722075becb1e2ae0e114413ccce2350701ffbc13f05dc5c9b7eaf4174f70428429f4abdb80f7fcf97b12b042910f64a004134bfc907a7c837fef74e2d376be3332e0819c5b9e3d06f16400e55f97e066b3f82e8b01957ffe3c065e489de64ebeddc96eba428c2da4408134050b53a4d8065b58b1e41153eeae16f52bddb8b0417c07830c7a85aa967f5bfe291717de7b50feed142dde5a3105e9df73394d7ec2b1c23fdff11164c80b7ef8e7447088c13fcf41072bfeb9122a59a85e80458f16fef94a21c63f5f9f9f17689102140484c43f5f181441e89f4aa549ce11c0142d9e8d3426128771ae98e067084534000b23305070e4822e4304fd704403b490e204892b3d5192684016464353ac284103ae98668033440552110ee34861c2bc0e3191f4a26566423c31652051345f5e879a78e2658889bd0e355145eb411338f8141547920a1f2a5080c3021afc7b1da20289ef1c07080cd3ec65c9b784bdf75efcbdf79e964dcd94714b78525eda4e3b4122249a1428f12c77149a79601ea8d8c05ee2786887df3d11460b96a005a1741f0c2d08a165b90fb624e4526f319930b13c3a936b48c9069f1e11b9e9d0a877d4a827653a67e213f360b2421e6e9803d56112c44ecaf21e99920be891294b013da7d44ecaf20ee617c08fe7be5214e958c3f7debb585ad6a61db9253ca953a39e8f46bde7b007f3e6c6341b261133cc92578c313e8f97c718637c31c6cc638c11f36e579c6d1e4b31fa6e33db4ea3a2cb2d8895458c51864c0e38c418a3cb90511ecf979f13ab8f8e4dce81aed3512e2336f20a94f828241b152d972e1d877db0322d66c9a7635500a13e680ef4383dc6cb7d70d4d1c1243bf973dba22d3a730b142396a3cdf2d16190a4d4b88c3b8a43ae2a760acac347359a7cd82e3f2f2f96b8253ca955352afa6c4b16c332bb3dda2dd16397830be7741ac61e2d51dfe7337a0ee5013da687c7b3ef52f1024aae5d966931db2dcce2954e3d71a90957e9daacd8d0079167fb01e182a02d565b00959e607a539232994b3635446fbf21daec6653433bd68ed9dcc09e397be698f396d9ccb036479e5fdf26d59bcd669b4d0a6b69f96894e5174ea32cdf6d886c9465b9b42c4562e617a8fe75a6c5841edd03aa33a3a8f5e74734e7f2175dfadbc2eab4be185645051487a73a857fabc794955fa248f7c1925bc2933235eae134eaf97bde5b90463d87db0f2611cec181023b84493b7bfbf3e7aaed1c448cb3a9013af48ce3818a0d18679382ce83cd723643328f80a632c73ce3785842e586fee2094e54298ce3017ae61967b36e394f8a87e836bb8e714ba82c6703e616b7e472cc97943c739e94a634155d53d86a3f901c11b2ff1cc90f181f62b90f4ec5cd26a5491b88d8d49039f41b32871c0f5478b0c9fc06cc5f381e20675383f4e836cbdd203ddadc6039c45cb3c0361bd53e1ad5254dc11c3a2979183e2bef8270ce94b098252a94a850a2820e30e06985b607e4d6685f78fe7c879447730b86ea780e65425f1ccc9e5e4cc8756a2484d33a979fd6a851cf253febb9b2aa51afe79fb74f96c98e3dcbb2a2252d6949cbb2a0c5f5aa7df8c485d1bf977ade3e36caa7fdf7fa073ef9e02a0baaaaaaca0c2154ee56755ed51d234ce5623e29562be541e40ba257cf8098ac4544a45f7e719a62578dc8c9c68625ce99248743a3b67568d4faaae80a4ea3d62defa00692b2f2f9911224b445bbf2c1691d5ee1e0ec987e77fe0c2f112c424a6732ed15587efdcd08c1d407425874a807473ae40327cf494ccf3cbb487a55dba3b9bdb22ffca03c965b2d548710cfd742255a8886aa460d61769c0455adf390927a16c60016a13a0b0445c152e9040000d8d8c890f1188c81f2e8e502687b2cb744fb027b04aeac0d667bf79080be337df4e2213d87c1276e15c7495f4c9b395f87880083675fa8ea9dd679dea7d3ab64e43a09a16ae11e75313b189474256966996b4581e98e7985c1ac521e41b660cdddcdfdae52a9d48f9b170684e9ffd6833c244c2c73cc79529af3a41607bde25d03fdf64f33fbb014060ad22b446cc4478cc4491da1a8a8a4ad5675410ad5cc0c80008000b314002020100a0885429138281ed446517714000c81984272589b4aa324c761140419638031c418628821c61091a9a1a10100eab99309c14ad6ae52005c6ee27b952c280b71e69151afdc6faf5ea5ae8e27413a8187ea551a7c6a9daf5362dbab1475035f1a3f3462bd0d6467e0c13a32fbd4cae855d25ffae7c32223dfa655a537f7a60b172aba00bab28ed29c616305aa8eb690aa38bed4b442d53d2ce1a443602556bec56cd35208be148b2370cafbdcb01116ecb48227c956ee598e21bdf5ce3147488133385046bb00e002781be439e4d1d22d3804673185140ef41631f09e5b6803db83ff9b6d48894dd048dde4d24e95d6d335c8c40cf6db330769b735babb06b7d1f4bb378155e0b9aee77040a1620da585182b62caee8ff474819804c370e0e082d6a4a2d4e888ecb16d1ba545106864596d0e5b9a53536b3ad5a5ae95f13529b6f6fa340443180ec42a6d60ea5a16d7a28505d7fc370b673aba537f3fa05af95c946b51b5939dfdfda4f4698d609bbbb5e1b7cc761b92c5ea18a0d5dca3dddde4f173a2bee0414ec0cfdc3cacabf4c14185260a5d38113b7842f83204382e234e748fb6f31411a38cc511c835423af9d24b7a5c261552b49028d9864777d9dd2acce96a65e3e79a426a6d8a2b82e255b1843ce70248748535b615f39e0ff899757cccdfac0f11d84f2de0a05b13d6c88ba2623787fccd7c3cce6a9ad52cc6880f2bb268e8f65d7299a7f8f6fc0a4796d53563d81a6af2b8927fbdc27804593f3c01617d3e0bdf116b0f7b07d7732918a84ed0cee73a19f5275c1624d6d3ab5550da0585b7853e3b3c0b2b787d15b885df2b35b799bffd99c2751589f726797873f4aeeba6f0833b832381f184da7af346eab4baffb18e7bbfdcc6e0da26d1203608c52c04e269c79cc23fb7e5406588d39a1f21a575403e3c20a901125e7b405a6d3cf625ab13657482a3bffee8bbdeeb0ccd36cf353a2a381258a7ab6dc1604bb4a694472b770dbb341b33c6729bd704efe11439443a496a2fc8771ff576d4f1ad4edc5039ea29d834ae7468a0f7dbb79f189bfb64b8df20cd2aaf5893292fee3fe13036d83bcdfdf42d56126bd4f745a17650ce1ed023412639a2151e42aab05bb61d912a0f92424534a5213556d107b258c0ac7a02a256e5b11bf9486dc95198c3dc0a6536bf71b59089c5e493820db508c5e4dd43847a66433f7e75621adddc6643fa8a10a12fa8fe656b4419eed36680246cb466db7e2f32c5537a034edc745fadf272b9c5a925ad1b6a537eb2efb3bebbefe9212f06eb7a36dfc9097d7cc4c21f41374d1f26d3e61f6aabcbc8c5aa78f9589b1e5c942909ce805299354780909ecb04ef8325072a3430bc0ed455742838e82dd173e0bf0efb0e88c9d5bce94715e2d5921a8e1cc211aaee706e969823cf4f69a956babea95c4f3518d3aa1d13cdb48d43451c8e3a9c41f43cab200dcf633c658dcde1388d2ab3e3ba72419ac826a84739081c980bdd5ec8d526801ff2d6aa718114191ea452abcc1ef032b5e570a908d7a2f2c28a265293507cf93f16ed60a97ae7c931e2d8cfba3a65b434a7a3c79377d95b49ef0e6894b9db2a9194cd6a482cc55b3dc9feb5bdd6065db3388ffbc5b7ba8dabaf8e8b655148bea122f9b0a12aa0640518444faec39ed1585eedf22a4d08935a31f9b9a61a7ef524c08581d06430ca1a4aae529ae55cd762470580eebfff9f09c49a7c405ea6bdd33b8b1786efaf35ea3f4bd82eba429daa27344bbacdd687354d6845ed1238754b66a46aabddc175ce94154ceeabaa8e5332cb433b832518decb380fc9ac0f3e69266427396b28526ad35b648071f622cc2b533a407e21731d69ce43133e040cf57861571eb5730d0b6601a6e723d4c57127de003717796bb8ea40fb1834b7d041788c0f253823f659a0cc50886284600dd0e9836e8498e1507c807f584113405327199745a0c2e0445fc6dc32ac65a19b16e32ac48a03e0178fa10b138ea54bebad9f3079d58355040672a9760028a6e88ed61328ebe3050aae628f1ca0451eeafbcce4db754f4d5f82804f45c018ce8f2c486c04e35c5a8cf707b49888d164a60eadd6148548dc99454def4d8931b2da7917a8e5df63af133968e70f2e8ef06853ea00138c6c6718685c0a0d47ef2d6ecc1c667cfddcc367f710fbff06c0545057d2babe065002b11800917bc4dc88567d19f6e70cfc796b1089e339dbf8434c3c41e434e03f6b564b3764951abc586c6198fee18436722e9a5eee746ec54ebec2d1f5ff50ff0d14fbeaec8f2142fd53c06292c5ba1051ccb419a12b6573dca71bc5bb3fccdfeb441bb4e8ab03b8dc76ce87e793737028de6e38678119dfc6607beb337b66ea1cc28332205c4e9de21971b05ee481038e183bb57cd49f7a8104c6f94867eee92a8557d18d9d6dcb7caf67164211d86522a5442ab710f8a0c03e4ed67b788b5f798c83fdbef5991cdf62567a1d8aaef18ae619a78381a672a63e31410a4fc455a856bbe2cf981044ed0e19518aae75dca7414c8d1c52dce041218e775a2c249cd68233730b70aab9d27bd0de0d651ec8437b99fbd800d931e0e09e5e06004ec6b9867304789bd0cea71e1de59f6d24b963b1b37a886355b0d4c7b53b7a15d886b0937c714e29f6cad574d1191241600f6b99e2284a97f6642ce77ee8a3db794b0e4884121dc88c7ac9c4ae6413adcdc7f7e20c39cbee7c4924c3950911299623580493917165b17825adf9da81a85407c8787888a118df35e8e6767717dfce70ec5ea981787648e550b9256be6c75669955ec45efc02f03dc1acd43de23e75763b3cc8725f02797cb77e8d129f97b3a686987d5bd8ba04e8db1ac81d9a041e1febb6ea3a37964ac8e8839a46c3e1bda9e72da84758e8595b9adeaead7b557640676b8ff6dd2325a6d6d32595e50a67a64b71562c702615216d410b6b65917e894cb9ed6274c6904351be2040d331f8fb9ead4964291555d2aca32413ddf6beb55b626873274c8cc95864b0ae41690bc865f3cf866d015d6b90e8921da8238233f7c874804bf2c8e8f41010bf46dc4806bb71cbf9753527ab97fb7aa73707deb8bfef43ad826b987e41d8f47942f8f3f866d42971fbc9954684b2073b517f69dd65c972d819e111f659f413c7ed6518340f22967265663e78a99e2a8a8707273017162840fb16bbeaa05b72039de0b7fb20c5aae043292e62cc8668244ee045caa02fd6e0cbfa6cc1891391a080ff124014ef6cf977bb2fc5de26a75750e780e7a11301af3011377526934f5809bfdad6ef34c1f76524e69765de21e4e01571646a10a403c1aa01e3346a4b4415e129c7ba29230fbe72ac2724e017427569d0000c4c79433eff1d36101b8b972b3ea356885b0a1ba4f358002c6d2d0b4211d6efac848af14ffeebc32ecd80dae667364de7481ef8aa8bef8f391e98699b7d51b782140fea2ccd537ad882f65bd13027acfca64a56f5d184838effdf7b11bd687edd07e758aef8950e3456faa14b3b714b47a7cee65ea21ca91df2f86d30116b8740f423930a17a6129a6cee8693e4e79c0df24f4667a6fa6e730e77c4c4a7dfed10229c66e4432f55a90f91654e4f66647afa6c830957c404d5cf6d0df39d8c8db62a945d45934fa9229e8a26260b0e45d6eaab81529b3aab02174e4296acbf1aa7ba706ae950bf02a3e7c7a21011359562a9e8e3911349834bc6f493660acb3e70634c6ab6295c9ae75ea8bf2d0cb62f88b60280c35a1f8ebf4d1fcc9bc822ddf7a38a8b3bb53b12a9083b8b33c8efbf1a7a8dabcda831b28bcfbb8eb1d4c07141b8751237107235385a754544e047cc2d7debb6653c978112b70fbcea1d362480db768d53210dfb4d80d7f5a422d95847cdd1f920e17805ce8066017813499c036d93fba60c9abca3ca43c5c66e8c37f420db7a5267a5df20c4e61c400699d9a247d67c5865f5ea8c3dd662cd6d62f0499f0067eef0fdcb1f38d8a0688f286e280463b28befec00c5cae8ac1f078123b90451dece4e75ad9a8c6a12e836269451792276567d5b213cd1971698075ec6d0ca68a1d162cafff9edb612622bad3497386623e96b2c04c0cc510601bed10e96f59c672b68aafe1ab7e15f420642ea9e32e3e19c813f73d7e36ede82086c64e54be214939ecc230eef64ae2ae4a5134f6051155077bdafeec6044bbaa2a04552cba84a9b5f9fe226e67817e89426f51d169a3a2ad1a9155dad25590f421148674eac1d45808171d15c0c816ab5aafe4d9ebd308e618d8cd997774da5195529c00a9ecfc9a9673484dc3d0b8417ec01c89ed1f05332cb1f4d9d1669ee30b76575b176e830637458d1568723f6224b55447cbaac3d1f1dab79afd3e1b4517d5c3923f95a96ffb2b0cdfb862a80096ddf89cd52ad7d38638dbe0a45b91531d2e435c71403b52c2b9388782964d90251786b1ca59aa4e6d0672204c75710e829adbaa7bd401fb7ccb0f8db7effbef258b0154bd4128de0beb0fa3aa2a1c66d54c54079e3a1a228bcffe35a6fc08eba885f9cc012639ac65fa15dfc004292d1793535aca623be7f7cf7fb8ba83493fc010096b851f944a6334f274ad2a3bda33bdbe236788b84a743b381a881b5a7871fbdb9753265aecaa00c60acfcdc469ed09a31a8f124d09bcadeadb8230fd782f3b792b59dfd973c39ba36e65f115dc7ea00dc5d8f71a99230d1583deeb4c3c64c1c3f55dbc6e558b715e297bb34a2bbfb582b3638764cf4020263502ca769cf8bcff73e789cb1e0b0442f34086825d346006b8686afe0c4f3bea0f92993b72a128b8ace4bf5a2553f8bf0565e8ea24f55a20054e8cad593761d9c3b6e9398acb51b10f87b7b6c7abb1f939367c73047ac7490594fde087ca2460256d9fbb113cdbb4e8a897b4b1331e110d7281856200898837b2c92f76b1d39276305318d7f602a9096283723148561ce94a4093399dff68aaffa57604dc186ea8e41057a2a3b6340d120958d028b7bbfae06d4b499fb252d430b78b3ef9cbce1a2a7873ba991291b7aa30f9f79e434c90c580e115d9e70ee8b73bbe3b6744b1e1d741facdbc7ea0dada5e3e1c473da0ee46956f4d784175c32726fa2f4f5290120021309cbe45f71af0abfc464687cd38410aeabef77d5933a9a11108c8d04cb1eed14fa11115b53d09b3be087953d66c19f80b4e6ac60e6a5222a7f657c75177311f21c95eb1177910c5565493f0a4122b6ab65747a3a33a943f0c7e43c83314d06fef8d70652b546dedc6ec40bd8c3b8264885b2838eeea5d86f2e5d6c30c038d74d1aa6a5c70332a90aa41388247a918f3ef30ea01d7ce978341de15b03af21d90194e5d8462bbc20503d671ddd6d74ed98d41cb69719c8a53033f2dc2b3dc0e945e2bf240696171773e995212b188f50a1a77837fef753001f4a04fde6f1ffc9b74a2fa44d856e668f95561acb45b2961f49193138830f8fac2d1909570aabed9aee7f92914b7093915e6b7176e6942a8c0d2ed3e9f53b8229c9de8b47eb950679c5b60aa32c3b3c6fd036e5ba1bba3caa8295ec10dfb71ff689fecd3492e2fd876712fbd82c6fcc4dce62b527efd1054e9538aceed5ff64a258278f78abff708a872f100f509a585e37cefac7bf085f8e2c516ff4fc16bb9595e412857417060a1e0912db047facf0a4eaec3bd6527a8de9006658704b3f4bb8c622633e0e08fd417597638b17509283a502f12c5e59692238d0fe84342f66495760ff2c83d249b80ac11efc1b9c28fd7197429341fc2696e8ba0bf501d48339df18ddb0d4a597734a0effc018bfb100164b4e44f368c2bacf81693c5202e9fe19fbe6761415e53c0eae8769a1b8196a57311cc54c20b45eafb3e8b31fa414d4e9173e0a46731db4025fb32e6a5abbdf4441f63b2eb84a62e3c14457a193b4ca23926c272413860a0d98b1e46e263a56fcbe93566647131fb54112faaa2528b695e918cf99dc8022fabeb8c8dc12de8c43ceb2fe448e30574d918e1580522d6aee35c65c4ad9c62098067cb32b15780b6b2bb895ac98138040ccec651c73f6448668974c0266917acd39ee7981737218dc3026a31aad248251e8957a542e4aa4cd4b08422f2e25eb2db9adb5318ede8bfbaac502b3091a1785e7428d9ba43d7407b849e09426d564b5ccbcc19f03f24829112b127ced9b34561ea363bfbfeaab3d91d09baf23794a992641b16e952bc5d50fa802eb438561917b88672177b4ac5c9081da2271889f6499182e142591e78d264d04e7b0622d912218574a1351503c25a56dc691955c537af02a4acdbbc7a543369c5c244f248522acab0a2d9328bc4a4eef2197d1b8450be51867519601923dc3882f7529b5ec9ee9aad867a609d58f07d072481063ba2dfb0dae6a4193ec5604309594dba0946b0bc6fe50fd5e1411b81197af84158ec4c79e01ce388c155cb50fe186fe21ee4744b5242204f8c1a041d3c5bde3ac29117d89f0bbfbc396d2cc3a7cc7ee2bd1928114162e78c6c0a748541baeb29d61c35454db4e8d29125d6a5f9d6e79f57f25235d26259825c1e67f6493d1762de696a94e43fa944242dcff4e6daa04138d5b8e12a96a0b6a6228bd42683ba275ab8eaa8ed3b1013aba38a94e15e517932f623a96f019ae8652c26af0beb0fe7689b419fcdac0fe17fed18cc958c51f1f33ea25356e95e4f2308dbc81637cab0bb3a55abe22593298b85adeb78bd1ec3d7ba4f46a02185fb8a453bcec424355e9a476dac0b1db3b7c180767f00112a44225e29a0032cfdbe13f2ead9d4c316ebb27ec0739539bfa9cdfb29bab9266bddf28ae4b2cecb58da4e0502145954e4994b49126d1ca3e7435babde5e131c2bfea18814aed62e7fa4689d2c940b10cd5ba132cadb07b2716fd124c4861e09b8c65c2b3e532077470a78c579a33c676b1294957646f225954f4d7527edf39e64fdcdefd20269635356e28ebaf81984a4ff10fab3c4373b206c79ba67ad04f5bc720f8d783d66b91a8322e00fda917810291776a5ea21c977217ef29eb124079348a126d955c6e921833711201c2b503341c21346c157923bdda3623856d6e61377b0dfa94f72e980db961f0c6c310cd5ad0f4584d90d529f33c4c0ac802cdfbc83c83ce734035b43ee62754dc30eb4ba2c6f7e3e782e631f6a5b41eb39a26e495479470480a96f66f456b2ca3c55b61f488031ab46dc569bb738eb8470d477dbfa20358b44190150cfe822e4205746be2e740e9c550aef5fdb963ed8e65db4a55b73f584b92b6284d2ddc046dee19b600615485e090ee0dfd156ae4704f600cd295d2e2d1b3963d17dd83166669a52ea7f2b27948dcf033c04fff8c37fc397f29b596eed82b1c189536a0bd67f145c9a2419469da5badec642277cca7c5855b411df15210cbdb0c79ece3cb473854bd2a042131b8898580b8ff8f3b4b893a7eb26c2be05550de2d3106d3a6ec8e2cd52f97fa41697ab15fdce770492351e6533a7129fcc81008520f26a8c6c9bc6eb828ce525d673c0e7895720f605c2f6f1d355ab7b6d0eedf65e30d4e5d237367e11ae4b35f7d5a0b333913f35eea81e706cbc79069a7601314b2ba5055ba1bfb0c755440360d95126e9630850df768355b41c5fc57f56f758d3869a8e4c0477ad54af742cd2542f3e08397c99a2bc8a8416f01580a7859268646c7ce54ac1fbac15cef7c6a6ee7c1d2e251a7b6f98b0863241e7e776d9edfadd5d83a84bc5c3a5b5ec2e4e43735e74640168465b97aedd19dc1913b292834a485653e20c7e7872517b13df1207acee064284f3729dc3c6e29b992e5d28e8ee679e7a5b8ae1a5badb829cc0c3a9890564a7036367c624d55cc1360e26286ff4874c6705a7d83897e5130ac7c4b2f0e34b3b338999de33ffa35930fe57ca7c027b4c3583549cc643dce1a6ac8e8ebd3fe9092cb0e62eb1d920708d45b0a4f02de28fdf402186dc3c40ff5525e2841cbe806a34916db678ccd03ea43bfc5303c33942e142551dae1b961da7468c7b323078a9b3b76b4209abe30ec7265f009babc3a5646a31d631d5ff3b0a6a14a5cc22422d3c9ae77231a83c7b4a18144051ee2ae8e5a0e3ca337c293e15fa7569c45c2b45f12c5150d732afa7fbbf9c323c68979742631b610358985124ac13bbd535cf445b380e2f0149cf99477af7b1f8c3d2854751c10281ed0a8654389e468106bea720cd8c00eb3a65fefde718979400cf788408fe740c02314533e3820e4ac011ee0f19bc8c5f18e2bd901f15e0c7807ffa1c296f93aaa93e48e81641a3024df63ed1b13d8896e7ce9c362424c6a8ad47ad0489c26a4b9ecd44db0cda80667349db3cdc6c4175ca48758392588d64db569bc4c524651bf4a083d6dee596ff1b1a383e28f0b11d7a623ae90640b5f98b47243babe0d59cd7d6a45b78521cc55bf1bafea2ccfa55cf829ca73204613c297e4b9ef2ae71d4ee49be48bc7ecb99b4691532328d1c7d8c6b770a4c78e49dbd91e46bee5d096cd97c272bda60a2b3f535a23509da09d22f92c252584aeb9a03acd1a12800b189a47543c558bf6a20bb672ad4312e34601532b401981b25732013b79b2359512152a065005de1931e52e4c7a1cd5c94294f98eee8d80dee920f4ca68505540f8e7b0456d76c9721fdef0af8c4e0cf0c52ae9ce188f07eb109bbc64e9d35f8e56592c09d1c1cce6e1b4ea7db47dd652a1a2dfde1ce79cbaec4ce7c22453c4ecc176d9e04ad6acb3559e874526bdb0de54e245aeef3e51e1d0e07d9b41db40a55e9b249505282bb05933e49769d4bac55347cc836d13f1bab7ae8df24dc37e8cbe9bbaee6349220b644a1e54c4a587fb7e6d0b00904b795d7f60fcc13c11fa919a5cdd534551e5417eeb98436332d10d8258eaa40826e18af6313eda646059a39d85edc146ce14dd1124569c62fa1267184b4f70b053f549df3f4316190840bb1f18ec647f395abebb8caa23e0b8b74fcbbaa7bcc07cc2760e184a4d4b1af1bbe77ffb0a4aa613c7cb44b175ec83d610aea1661a4e5ddf6cd99002714d71615ca05031a6d44baa6fdce48b3479c7bdf2ea51c27ac0d03559997b098c91cbfa8beb141b6f1a39e531456fef85eacdffd20107d859885de2e5925fba02a194a06b1253a935293687df92cf85028c546893191d284581d188d36860c3a362c97e9bed0e6c7cf84f29fc293eaed863045de0c57aba265b5914e47addc8d335d991c012561b407f1658cad733cd27b1c8dbc92c3b69c434fac51fb7e00f40eb3a5e9189d243ab4c329cdd65b822fa547ba4372708d23c6225516b15b82fd24fd9a9fbeb05ae04fca31c669b502970ef47064243418229f1639a8870e7c7990daf94fddaa0ca291ba2927ed4bf24784a500c477a224c934c4d69914e00ce13b80039e2d635995c6aca04f77fbeb13fd833677470473710895552dfc8513e1d5b4b6adbcd4ae0e60e583b42522de4275402db5f394f6e81c5c669d41b7642e10916453c4bd7bfc8a3526317b921320e3db4d80b97f2b692e59745ac6b602fcbc072889a06ca16b18b12d16d264a2127520d179546c51c61c0fb8ad8f8fc78e3881e5ba9f7d2b0140873072593c2bf0663e82c8615f4ac14a1442c776ef03c20f0e0435551acc92e9f2fa539431a38b0db2201b786c9f46a6e38409e197cdae830709b272cf7e4c6c810d36bf517b9577f52e12387484098c81b475a32ce689f561ec7bb7015428b4a84da33e4731bc5e06244d51f0a609c0c55d82ceeb85520308d2ed9ba8f6a1add3f92094c8c980f314e6996c4830d2d507b538aa40bd7a30dadfc26a7dec561ed817c882b58cb3d93dfeaf92f4f0469057633cde4774c3eec7c9c1ec844c44eff4b544648357bc393ceb8f78f1f4edf1fd36e3c0345aa7abd0a443f3a13e5703cfa795d700e13bf144e525618c2bed5304ddd7d262e5a32e3ef41565c673719c83958d4f86ee2f581a77c7584a3db8f6e8df273cbfd2687d5f7c7f413cb4031172bae869b79107b43ed6f7d8db342eb12544d321eac33d14761d9873e94d7b5cfa4717afe381d8c3a10a4b55646078d70d27ed78bb5e39e428cafeb0a5f65cfe8e30665a53bbbe57e138750f753666290cb00dc8495eaeeca4ff81393154f161368b61179521cf1073b10eb0a6bb78ff8d6e3e78890b64acb7d934beaf84bb5379906b2aa21cf1d80ac90ddde9573981c8217cc23f3479afcae50bd991e42c31ef8e1b9aefd2687d2f77b4e1c4fcb3efa8dda5dbd5b6f72cb9423f0e8e1f599a79c540f7df4643dc4bbd3044d30b6d87875ddb53df6d1cb2ef14cc415808f3de131c3edb802b06b81f464f11297517c809b89a70c080452ab51d34b178fe06f112214f2e8cda7d6b05309cf1f86aab586ec562580dc62ceab4c55fe13af1393499509e9a63820bcff85e9ef7d35e1a5b6f82edc3b2bf055a765229ad609cde0bf9764824068a270765241311c2ddac2b9380d310ff34ab85b4897d56dff1e1b1e5ee88cccfa3a92f2d520d36a2d0fd23d306943e9b5fec603fa8b963a30d2c30b412273ea30a4d49427f418f80ea55d2f413557d452dcfb49814f391115ba9c0d89af186e1a68cb16ae4239d9233020facd3581c357fc296d0f0c347794210769a2639926a6a305b20134cd361392c0fc1b64b6305e9265dd5f3b22b0680572604eb22f48cca022837df5ce34d692cf471f0332e7e73cb98875d3c1d53e8393f24da7e2dd9a1f7a2d6789d2e29ea08d6604aae2aad7c8a0cce22defd0d7eaf7abd57d8035442fa953ceab4f0b11ed7efb6333f5abefba766b43739b52aca80830a716009c644e9e674606ad2a9e12e1d80cd9604969be5f411ca9304176583242abb384b1d6f2189ef1a4e7547fef7527d539675db04224c4be9cc3656af9ac09bf3ca335458912122c6c1d5e5fa7f8e11efd7d25e352a19014d308614f19e02c153612437a1e7558b02339665158326bc94944df8537b38ad81ead2199af40340e4fd376b44d856bbabd30ccc5def1a2bb1e3e358f7719453c9752f3006301f0b528ab8956ec0609463a2e1ccb803dc5a09976949a8fbe9704996f339e15888d236e488b25705e15f0861ba4faa3860e7cd77840a2221cb80ae1154ff1c96c07128808f32fe6048bb25a68f6e0d60183a523def4d0c8e32bac37e704942c1549d50033c600cf6d101823c109e09a605ace977bf04c6a0d6914de6395b984c816a55ee80be60ab26ff601ee6646175ace5d418365dbe3329bc01b502c81292df0319070f6da1cf953378e67fd313d2c842d9588ec10ab7b8b153324cf71e0c137c6068389254b2d839edbe98840f9a781f9c620622430150cae63bdb1343ba2e9fed6583fc32203be93294c4fa60b5f260f59c381d9713387f6b70787030034b0e3dd9c47cc4a7ace071e5ce91f0225322b5a17d611edbd1ce270dd73ca596988a5a0b348cd3e6202c984078f62460e0bc4025699e45e171ce580cda1d9cefcaf0a23b0c33e86e8eea36c054e7d9c1b70ffc32ff5fe0c86893ce8290d4af44e14c8b33e156a889260e88b5a7474baca54d7e04a6509c00860791bbd5ab257b4dfe7c57927e6a0a34200a6c86f9a53eb496ef7e4126fa3d7213b14043beb9806322baabf652514cea09a3f74c2fc96c71230d597d5f77f4ae1e9be27ebc7549a4f56019eb57ba5415dfa880e10236575859c7282cc6b164a8e27af5a4e9b8445dd2db904b335e970b03e2101fded152bf946201c5836fd3fd14524bafb78587d32a8c1dee08a3a4aa462cce88b3e65ca3590c794f756dc24ecd73a5399d9891c7c0f955b44c05c9fb7cbd956928a4e7b91575b64899a2bb69871e9c7186256134db2158cd915bc2960da7121e486ff21afbefe1d3de06ffb8583b9f20b7af184aebab180b40964436fd5e3900ac235da8ccae0a1a0701e0cd7ed4bd6a098aa8fe2088156f0f1f15280eab971146a56b455c3419ac3ca3ae1e6ca2598949a19c7536e159917f8bd6d6d14266eb6b14132cebf98e093e1e7a56868f0d4b424ba3eca870aaa56b3205e36f9b6b671cd01178f249bf0ea7a5cebaa13a64a20930db89c024bfd8bd59141cc69a23d5b8151bbaab191d55c6720f4c6f65faba56bc4743c863edcbd48642660e44cd8b267de3b923608dce8292bc76bd0bbe33912dc283c10a3c3f5854767022267823d7ba6bd8f3441e0669e1292e3d2213de05751f6874da08768c6e6281d42a1420e6591d500e82f6f0ba0ea2d0c6dc606c7d06bb405c3e2c627e39acd933c7396e005929b4f39867ce295841f5604f8a868d91a2260c9214d4f998adc07764dc175c79b884586b72774043de6ba3b3f2f0e9199a94b2626cc171e99b8019726a63205d773e12a0eff5e67ff10c8751ada269d463f24dc80fad0b166804a214e8fcf5552b8fa28af11a0d4d3e8c92834ecb982961e6237fcd382ff65799c227aee5f125e0b4cf73fd83fdee34763936b8e5b1c9be5b9397579764794df3becfb55215d466a489021c10d8a674e0b701bfd391256f9da77f2de660ee23d31ce8d37dbfabcae38b1c373306108a3013f0129bf1f58a37d325bcbb5ac38fe0e23d04c6756965c5974a97230eb745f464e9a0aba4ed6fcf4736c6879f6f4f5c012f0ebcab21e277ab1e7b5402c2d5113917ec6fcda1ea9a094efcac95613608e3525f0a74c602113faaea5e872d781a561a4a8dd58f3d563648a72f30f6ec620bcf6862c3a42961b94d5230c06fcea5c5ac6f5e53dbb0a0e5c272d6cc9157b9fc707c9f4b044317498c93020b8501ea995528f138fa09f0b81e25dc254c1dac7d77a20b27156fe7f9aad929c0798839fd2af7dbc7be5405564301d74dba91d7551759c3887e313b2c37d2af0bb7072c7b6cdf2a904ff3fe590a9d81de1b5a0987d03255c0bfb5fe135ecead0ecdf54e098ba8cf8423219b2bdd42f56ce9b955f0547fa3b7400ea39c84e9bdd34d33ddea814780a3c1c800c980cfc6a6bc4bd72646998399beb76f1f34d947ccfdb4c55fab526762cb9aa04a119260a02b22ae6dc5eedaf38a34ac237aefda3c5c4276cbf2cf554c41c2c041ddb8b707023cde32ae0061e985a62dfc372d5d7a42a79dc96a2204194ecb418da905e5db662f4df27d2635bf1ad173a10670fafc731e35b459cc90c63406ad23102be96a33eb2382b01e0a417c372875f1dc098c3a1385fa81da37a8cc506482b8923e240b9bf2a2a60c569a779cef18a5fea058361e0755d534021971a6ca799421d5ee9e44a911f40cb680244461d248ab0a7a2f4800e4c8e30f2f41c167b4aad4c3e7c8e11273d1978250d2cd0ff6ca95dfe204e50b5b1d9609bf5e0163993a26d3ca033292c4c16a60c7976672cf1f0877b5188c9a8ef782dc034a39c043186cffd7de30ca1f89cd8ff20489aa59c93b22928de36f340937d5630091c02c1069062da0874c98a979f6cf38a2eca4d0906d4ba39690646ae6832797c87364812934ffa8c1123dd65eead0cb8138a6d31382c50d9c4c1a6594d599c62d91578f8df31f72103853e45a171b2750205e6fe57689c8eecd7706f1006292ab4af20868406e886757f13af5d399ff7270bf73a18a24586df54446a6794620e7f0b652e45be9a039bb36fc0589f539fc9612849115e5ca3ba9b21f6c44a8eaf64f91960b5fc62a7900817e8a28b3465ce50e8dcad9ed12932b8a5bd148be2ff5024b1ae5ef995aea4c060c520660ef75b972b61b58ffc0f5cb57073e021d136e5ffde884b4f1dcba02cc9a1642ec4a8f621b99c6751af0babd17baee9a429e9bf4d7aaae49301475c76d11d4a2f6e1b35a0f31fca2669984ed00e8e4c57b466e2d8908096fa1424a1b92718fdf9e81e1c84ee4a3b8e873a22fb83b4b93228b542c79d59f458a837613a9a85448f4541e11124b4861018cb7e53348f30547be35d4e1d35f182da664880d252985c56a1b944fb054c50ebfb5830d3feb290f9d963b9b02e1f4d098ac48b2c4ffd6e20dbf8b748a854ec8a9a1613709f905c23049f21774ddc7e8904b70bef2f853ab4b8f98f29657718170b5129c2f6a6fbe79b730871ceead378c7c129b7f0b9120b3ab0b69c2485f6c5a5e1b965a3f023bcdf942232e9e76c11dc2dec72a12134f81c3c1b4cb15c18e159b23a9bd88a9b96d49d484f5e6e5abcb80e4d29c25b149abfaf32cccb6d7fa0ed527486850e20cc0602efd5959482c43b9f3f52079edb75127b06a2929c95f71c4a492dff8c4c52cd294405a5088144a4192472f38fa4f8e348f0d2760e2949e4f50240c8953ff901821eb64c36639e21c20ab3cc8c00675861689e910338420b6a165a17e1c0e4c801de82555e93673542498ccf05cbfd06898a3257e605fa776e0e006cb7ea0b765c39a021ddb02faa7b76034151340016a13cf39a5530b7122ad37000cc128e7b67cef4d8fad63a72714f6082bbbfb4cb5ab28896920f38c78edf9890d1e6dc759ea5e629f91a3600190823446fa99be751dc2e0b5482824371fab2b5dfa17a936a5dbbcd81d275a64871fea0477ee56aaa69e385a0f30ab0e63f5ba3173f9a1e6de077788106e46cb607c1e3494461d92ad913781eb7e68ee6fca1927b7866a3dad09952f058fbf9868e38e91f3e09fb96d1ecb7b1668f33719c39c22884f86ad638a205b13a14ad2391bcc7ed2a284e955d762123c76eb5b67d6d3ef13d0ad5075c23e2f12ba08fe2d79805fe809e313388406ee7ceb3211dfe9f359f7bd2851521e1190c00f01abd0bf872e279568460ead9f91ff8e110146811a2b70ec5582af1582391d24a246417a4d87ec9fb44ea89b4872da62bb11329328a64394462348f305a71ece9d57f1c7cf17472f2bd2f492ad8d1a48a740ec9a56f41dee6397a6e198be262c580fc41d219fdb8833a6bf2952a4b7f9e04951c017b7ac17477b927e4ac6cf4ba519e4d10a255de7d907d9db39c04c16e0789ed02762b15d758c9dab52621c8a1d8dcb7154a158da0d8cc6cb68752a1922a87f1ddbda5b35e836d48bb3f065828452234e9135dfe70cb6f2d9fe549997af8789c32a9b1ec01fe2cfbdcd6eefda69c2d35dbedfc2d578c030838e31a0654197b66d5201d6ebec720fb9d58e9cd5a09343360a081f3922b945d79c44a53e2845e24a2c4c50df4ae39d9ef084f8ffa2218ac283522054d61924f76f0034ce89ebe5a593fc00b1cbcfa90a41e79f0e34e0f727170a5f18a05b336be3cc5f4b81ccc09aaea61d9413fa159f67df76337370aeca287e4ccc1d619be89887104a6ba454c503b7a9c7f9efb4d279dd95c5b2ccba78b621e40e682abd6e96d9bd273e240df1263921507c8e19837648923b605cda8f46904e30156543f4a8947329723757727e4325d267051986680124a6f90e496bd3b95c93e1c36a45412dad91504a17e96518c3047c88621682058755e99e50f21ef99c4ada380cb11a0a5de144a10daa065a25a3f0521515efc4fccb0863fa6c33f1720030a2b188ed6aea3defb38df2d72bcfa8bf397b0172664ced74563206cb720df65eb5620c651ea70480ba7f6a27d44469f4d3c8b90dd53304b4038cc0b368db4a699a3173f6976066782d913535464a7656b618a6fdc6e17a69031f99cdf98eccd5511ddc5e277e94984a8dd6fd5d17137751a0b20dc5c8c06ca052d9d7af7d862502873a20e6d9c0496a8261a86f35b51f008036e0de9911664b92fc9ceca21847c6a6117b2af4605dbd92f1e654d652c759bd0d41f1034fbcd09b782485332eb8617d56b5318b6126126de8a1f3f0306e102a60cec7f147d3c81e5856bc5769418ad8ca7bc90e86457abc75772b0a56ebae62e0ff22e8aaae9073b37e8dc5b10e56360c04e694377193438360428e9f0b642054aa62189edfa908402ba833dbbf58dd338070235b7bf546ab1b44fac003b1662374092d352110c93a2071dcca8cfe898c589bbe716951afc2c213a4f3819d3363f16796bad2980299718dd0cade97321c186be210de7284a7ff4401f0bfad8279de767e39cf6384090c89a1676c56610139015c0364ed35ed77a0740cce8c7259171f423dd1934511750ee3905680889219f23b91a63cd588fd2cade83fb1a3d2319b184df5cd1951c10239625c39213d049fb741859afd01ade918723270b749aecfeca5d028eb63436fd356ef3600d0ccf45493ff0409b0ae9314c6a74524da532ef09cd562d74ee8aa7e9fd8a39ac14c7e5f636cf4fcc61a529ee8dfd6f01fbe7e4adf520d8fcc169ea33c38e65899afe5a009961f261f03227089da8b6e43ab2362f2c773f42d689ab176e563e05b75cad668ba81c5845887800bdc281691c81fe993bb07fa8cdd51db84d2b897ebfde36ef2273371237c93206addc59c44f0f23c85bd207eaea9ed2c0d162a03541cf75a35dcd9996ea07193c4ed9d0b6aa1f0e99f5d671d8e8cde61f9c9198deb1e3533ed4bf2cecdf827d2a241afafa7825ba126ade9e8c1b80f473a6c432785283876da69e67add9693e061af17440a85458160fa1fbc9678f1c4dbb71f1683a32dc969091353c31108369c76d30dba6c05f4ef04c725239628d21d4505021f95bdb243c6ad5a3dc3db01752652dd98d113f0cc239002539117243bc65bc41027250d194c1580c62c7dda9de8507f4e6b55d3f64ca23034d0a5c5d4f091b672859bb44e8cc93fdf127ab00cb303d31f0f1febe7e8b95058d09e12f512585c58e92034ac711bc7a474748b93f65fe2e7460af3868983e4cbb20d97797a5fc938825a02fd9e73345db0823e49be25a3d65b8d5e1c361c5521c1dbe409c9891d89dc8dc139d01166c4f9685caca3279ced22176dd13cfc219c9a1ad037a85049c1c0c9ea80f4b86994b518ddf16e67c45beb2df5e67fc0d078c6473d4857487fbd218e0d48d298ad85de5aa79cae5619a8cd1e2fcb874f59373439b2b74d96516659dafc6aed43fa90f3ccb95a9e4476f8812610e37921d254feba6d583f23ee4b1e403cc51c92cd2d52e5dd395d9148abba6a76c8f521fdff8fa26b70be34dc7ea84d472b4f3dfdaa2d0ddd1695674b73cbbd4317390fa7aa7ded66978c5c15f84fdc699f1178272cebef50e1efa0735361241cd086b06092bd7507c40564ff05f452bcee89ef5148159c67fd91651ff7c63d784270861e70ad2d6e810ec8077802799970dbf4f87c28e4a4ffb05e5d435ce55be2455ff92b79a58c490985f1ca05645c044baad5aabc555e536ecbb92f5e8402cb32d88c0da1518f488dcb59412edf57ea529905a1b5723e23c373670f042804c7d35d097a931c44483a2bc8dbae9f8f1e2b4687abaa5b131063d0ef10f264f696ce6290ca41d2fa46a6590e485f0992cf092cdcec7ced119013eba13cb00f77abe1d9540be7ce12cbee4367b0b41c8ee8a02a721bb7dfa671ab9c3a6160bc4ff7e1af1cadebf8cc1ff4917b200d9c1608d42fcc3dc718b08f48c031a2d7921f48e037a53ad83207d30d917a256203c5a77a5b37793cdc2b683d5418a0f873bf4fdb83dce2ff11cfce040c9e5cdb3ce7680590d490a44866bf02e49f1f1a41b1f234faeec462243a05dbe45d98c6ed66346f5210d2ee7a7f763c7ec086b09e444298ca86526d544c4ccab0bc82738f6157ff02a8c29a4884e82439374301b6b4aedc03630fda3d828145ffbbafb5d930b7ce61f37c199c3d0aada0698b4e053199cc284c89dc89574931f1fb2bd8f9bbf840e1b34c5127236eac06440b3eeeec6c19f1032d1a89b249c827cbd880acfb8f2df93977dc00a512841f59b44556c56fb59b46e846d68ba380b066880ad4eb21de288a43a859f77608ba6a99b53879740fe248652e8763351aa552739187a3a4d8fc840ab6e8e0c2b48e7ff7aec2ca42d9ba5a9c23dead52809eb6ff812cc3e132705af0f515dcd79581c494744a1813524e33062a044586f18d0939a14617db474e59dd7711657a6a5cb9608627aae37f0be012fc5ab06ecb3a80abfca87c810d3bf51d309eef09e231d380962753ebf32a48b6695265e76a2d7d102a30047aed9aa83df569d85daf104168bd968a997db6100de2deb4906e166c106bc0de5c723cdcc65b55b884c04f1bd5c1c84c90893d0b25ca16b80ad0048e84076505a5847477191b9b485d749e6b13dd052e5925dd2e15e074034a11412418f0780f2469808cac91f4a36498895b4dbc29ca865db9476f7c2479ac7bce1e6354ba5df2a816e3f79fa05232092b3da7d4040bf7e6721539f9f318f21dae0f894a7305113474ea8d7c5769860b93e2ac659ce86f8518b41400611554021486ffe2d7141a57ef7157b9c955beb441392d4b62414cdd1716fac0ce24ee888fbb3dcbab18a286e3ffa5457eecfbbad78b93e6bea27ccc00da06720c7b6e3907eb47d16bf2bdadf3a455dd650688f1b12f61661dae47afb47f176a426ce330d9e49b4c5e62c7c08159b8ec965f959bc4eb0114471258ee7abe0f4356ff27e2d5d76717eadde03cafdd4b3e80a123093bcc25ea6a0c059339b7d0015056fc3cdf4b531101b28eaa74a45f8be19c0add7f5e56a6151698cf4d39795c38d83c614bce6ac0d5a59363f8a7e3e53eaf2cccb3d9c115c63cb4cc922ea801f30056a41b071b462efaf13375e78bb4020ab6c46c913da9ac0d1279ef146771ec21be2ab8027dca115d922500f56f3dd86b4d244ef9551c8b69040893f737d4b21d2e719eeb4d88d92e6f1f0dfdb415f2f0c97bbd1ce0aba4804d12461239000a535bc1c249853b0c02569e58123dddee84b5e03d4d46948dd443acb010ed536d6589859912374ef9beb815cd012b5e95866f718c0803100330656b7f200737912221244ce5bc3a372b2c5c05ea0efd5b3bdd983e3664bae67e6ce401d81c7c87485672b9ff449a8f403b4243024f2938a506e05cd03b8e2d2b7cefe7f4b3abe2f971a7c3bf48125ab0721c465634b8a5f249deaa90173f09a6b1d8df7e4a425fc78334a520a8837d42c07ee4ecc7a110efb80d76e464b686e8b26fda3e441c10c9441bcd57aaef7b24a5ed228d19a184fff2f6a7ae3f665e42e5d76ec32b2f73c7f45f36d55eef505cbd64faea021f5f7c2aeac64622ea05f555e25503c0b0e6b090d6ea62169aed9956fca5c0db00249accee70a16242bbc7c804dbeff615db23c9fb0d76acd30da7354214e7a02a5d56eb48788da370750a40127c9f2f77f01bc3bfd7b3771cfe05b49b834b6008678c60540756fc2b0f61395cd8b95ebe7d2cf06a05a00e0491b827825894e530681f7d347b54141c4308d5236cfac02e95639c24f508e733f531360c7dcde9c5dfb8fa13ac15f4fee0561df2e6ec0ef1f8840e4e572c82c16e184a3ca0f1b4e1d4d3f8e46ec988998b0010867f780f6f72b6aa984b5f516e4b0426b1a3447b8d6949457ca05f78ad2e9e5a21f63f309be36912cd32d00e66d864e70ffa20bc9689d21c287a7b3847a06aafa26432bedd5ed92be50828fd530de4d00a6a2e08bea1e00ecb93be9222d3532f69de5695eac2d5d985f3e50a1400d5e39ad995f87dbd096ef109fbf52607acc76af431d4a4fed7dc72c79681b390adf5b101e9cca1c7b8abd0ed1472e9c8d587a1452878036c8d4fc8f8cb19f4cc43dc5e388b24e61b74032ee5e9c057f35df5e42e178e1839f5775a7d65fcb6c9a38c34b4228dce6dc372f7399dbfdb67414b7c2a5290e9c8300fa1647cd4195da6d6bb273130901aed370f0e4abe5fae617fc1564c691b4d5d8b79a6757c6bf0d02b83348b7d9b7549261ff9a51ed73a223b56b3b61670786ff21a542a0b258da879bdfe7d0001e34e0af6c7736ffe82a5aa9d1ab5bf1fa001e12a2fc75fbacb72e7f72d67502b151fef4cd407bc355dd21c236b3d8202dd2e88e5f9c74c56474690ba5729b7eb9512566f1c75a33c7b45f35436604448704ec1d8679e621f9e54346dd6ef6044f2eaa6eceddf68cdc8da610156fff439404b180ebdbbe36d343090cfaf71152afcd59465c1ae7c28e3e4bea180349ca874fca4d990ec0440175878eb7b81dbc26c3649541ab60c935b767cd8a77fb86715a00cfa38f8f9b3cfc40641f46ae928159414d86b79c5f5592773184831489bcfc54bbed4266d9ce5ebc56d3fa05f742c57021b4470e798a754cf7e3f59b2b10a358645dbbf0c27dfe9bb5372dddf8c1f8efa8bc9f91b357100e4a9f3eefa93e674d25a02223aaf3b660e3f6b99132ce287cdde5f6285999b510a06a90a47aea07592d01e9af65b8ef801e2469c5e2e3f123edabd60e193c0808b90e1b0c1d7181735774c4985b473902cbea31ce80c119ef14c1d195a1bd5ad00f209f43c6520537d4a91b59fb120f906f45b6ca16f9862c09d7690cff94a130304ab3a2cb9c6cbc3e8e88c610011c6aa809bc6cdc8923fa9bbc3eff4c72a5e5f48b646eb48c70370745a4d0b3b4f8ea88b665d9b256791085d9a61bb288314521b8cd8a38b1c2f6afa9de35a44dccc2c342112a753e2863b5bec20db1b132039f312e4590769765e2eca23001866aa86fc72aab09a593c5718a7c555662b92bef3237ec3500b47c7e1de2dc16bc980b48931374cbd1aa9aa83e8566a89d16bcb212409f13f8d3738717a09717359e1027f11c680d9061219573cd9432e08db769c0617c4587d05320e5ee39646139366c7dd763f50399c5319c1914c839258ba034441191df26ffcd2038a509d57ec114eceeee9bfaec1bb6e5dcb3131064fe3c8cdc40bb5e72515aaca3cd2b597a18250840a43e99561d9447bc88d97ba91fd9b310fae581540c6de1928963c4fe294d4462d845048bb70d8e7e2abcac1c14077f22875bb24b68c6d683fd38e88f2f33d96d6851fb821aa097924765f86e5db77982c518fa86ac9ca55028d1b6af02c6aaa731aa076ce0679fe58981e109178087e82a99b67513bed750ebe8c105b6f5ec7b31c0e66f0053beeefa672ff4c4c282c4fdde31bcc458e744ac347115e3bf104e91ff507932c9ba406c52c006f204bccea7dd95ae9b9fe5f4ba3310a52b697d31fcb8a2ad4f1d711856e1fe874d6d3642045a6719d904d3d8d0be4c64e05b5101e3a5e52c3a08919718a90ecdbd1cbe1aa84e51d8bb22c992d2a6ad94990a7e4ad83e04f671919ea3780d402fbce630073b2da6ad19c1e2ffab5dba3ef5b4cfceff03d3230bb9c3d52ae56f0a9f5b0623490795e77242d495362626e9e61397a2bea18a09356b2d96d92f79c4a5678243e48f028a732563821eb636b9f186c4d954bf6750efdf33c8a6869f9c10f96cfa9a2bf3cccdc052b1bd9db7c2b060781bd736f6c6c1cdea9471f860454a56809edd10335e8457a9f38b27f0ab096012f755121eff6687a7a3c009ad034719f1db4f7a9d7ef6ec91eac5bc51363f080de07bffefc0f4722ba6e85145226960b437af88af09cfe9120790bb35000585ac5325f457c3b4050e603e1fa52c2b5956edb8a7acca366d9fa8bd2a6b1738888938af50d03ed84fffaf2535b3785dd24d40cbaf618d8be811796de5fdc1bc5c8d2b94c704ee28807c06334f2b6e14dee65412ef4dcd6098a77f0ad0260a9a8e0a8ad07f68648026ccf560dde3347d49118da838a2f1f20845ec5ebc6554a2e2a550baa7a916c232507d43701be5b3e12ccd2e12712161e3d2d12f26c3d2a3a6a65db93d32f2b1db17667cd9242e50155f97dccf25dbb4af8185ae25b4213e8b689f5b67a0a207f99c682b59cd3ce2f67a334e650ca793c60fb96ddd2f753e08cd82ab6efa3078db38f9a9158721118faf86f43441250b5d890c54f1206df28f69d73df67347a347187ab481e3e099e203034b872791b960b644107cde75405b3a805416a927a8adc295def739cc2ff691d71963752936c97e7fde517bf0abff0636cd2f32b83b0f31ec6b33e915614d1b6c5978e79d0b089b2cddf887ec0cbcb77847e6c6a6acf2b57d1e681e060e3c2bba32a58289cf4743296247e2389fa4266b1bb28c30d0accc6268d1010522acdddc156d7efd548afe6c0b1ee751ba32c7bdf059e1edb3c82bccf544a6a6e8914622ac742e31e7386749cc0e3828ce4f8031f5a88fad36894c062b1fa5d9ae2fdec7a9a831aeeb01221b6450cba47aaf8d589566a05473e042aeb62a2f81bc9f9bd6f9e551c885c4a2e770d0264bb20345e31e47c3439d38c3a6b64fadab0c9f94e68159fedec81001011e21775759a312b5a6ef0b10691dd4ff44a478c98b61d14ef3f25e2913a7580797253a238fa4549172975acd911e3f8224bac00ca4e965ef0f407cf2e5db5033723f2136b04d896de60075d7b3db046cbc8fc0280d8aa55af4959fb5dcb5eb3b884dcb342214dd5e321f7c00fbb6c695a857ad964250ef440b986f6aac15e5e55f0c8c505f048b4d471b62e8cfa9f40c6a5412f7b349864947d0660f0d3d925385a2a99e7e295e3dc2769e286a323ad55d6eb11d5dffa72b5cc5263cfb1a29373da4791b76a03868c7f746c97dc84ccd8d1fa80a55de5308094c29620252228209578afa0d6ba7ad68f2ef883747356bef240438bad51617ceba88f8ec690365cd28133e3ef7694f01f5fed8f30044e3296cbbe27db655fe3d5c2ae9c583701f0f3c09ea88a4c34063d7e1ea56e86797a96b40b26470d37f1944bdf140abe82cf71273707d688724f84c4d9c12a94d766fd7185ae40850ac5b92f99cd7cd3a035921dba9ba2a01c067932dd76c58b5ec0521d7e2cd067d85dc426120281bdeff8a318151223846b7837531d7a6fb0ae6839064adcc4b069f4c2f11dd4d02738a18819420204162124cb19caf91c19c2273106509c269fad03d4b123998296606a39460984e0fbda72423833945e620ca1284d3f4a1679da186275912000c8fb4113cee556cd70a0afac9fe2c43beb86164761c71495043fe8a411b651af3f003bbd236eecb2d8a28e91909b49c8430f96927444e597f4b35be510ca749da8dcb37392bdb6224814527cf28f98a11c04b73c7f11ebe32493ef4af5eaff59ff1cbb1008711010dff712b8071381c2d9c1345b227c058047ced497cf94ff4f520322c4f55b9ce830f19ab04b001e37a5244b572134ff45df431f6b177cdcd41db760e6745abfcff88921f768b8c1152691730deb66c8fb234a1e5d870b681b86d422738871ddf34f82ee0482bedc6b7b13d0b355f4b1b3166bfc4b93919bbe81ab2a9fce6ba8bc1bb7fedd8c3d0525490875a865272d23f6b8f65e47dc67c9856ddcfedb0dbcbbac32c75320d25f64f20595b165b406f85be55c61934f0bd0aa6684e86b5ae64177c3c3ffc61bc2cce5f015193b212d42599fc8597cd4cbc75ac64b1f58557b58645eaf72360ff6e6254404c4c2bbca16ccae8e707d949bdc0d98b4f387c6bb9d72f2b7f428bfd9afb9bfa59635f40a8df7545b1bcf8a5c9de0751b27a4179d1485e535e3d039a11d0818c76817025dc51f3093cb706f24640f34f6ce7e8c6e3c1af67b431c71c3b4d68d09965b98d9a3bbd8387a331377e8cd40544a9e4ead1140fb6d2856f30205acc073e10d9f2ac07a5d1d850f4d5e69d8f611ef357cde25fcce526803cf839b2e0245632898bb3fd71af6bfc4e40af828431f886d36e7460d57774674f76a7058ff9f228596b9439cb5da7f02ad160fb6a8f7ee971d0e06fe2a40313798f78b0197d3170c97035f446cf9d6aca99719949698d01226d906ef383226fc5907e2e985c568deba67f28b9ed2775c02e7531bc95ccc85256b2f2fda4d604243462679b5f10d9bec9bbf561c6cf04023444577063dbfc09ffa0f1e122e2eabf7e393f11b20845737e4096ac9096052d221cd5e7c2bda893604315959d25cad89b1575a5649bb2ce5dbee386f65f558156f1a43c1b5bb14e843ca777e7bb9c35e8b6b7a6d80454344735eb4bf5a8045a720e2754b00594ddb6ca295aab1fb37fa35fbce3edd6e6b32e3a7ec6c3cd5041af3afb9494a18aedb671c68b3a5d201a6745629f7e3401238b698aea7d0afbb81d8fa8bcb09299d02b98ff9dc86b78d1220fb8d773ee9a34d7b19c9461b7c0870d754933e137403c4ae5b7ad68e5645f00b1c8ed000c8c1499778b06033011636cc9be0c684a97aea7d82b20b42700509584ce3f0b0675dbebd15ed05907798402869a5d5d8f6150e5ea4619e72b3ed33c42bc40b2b272442958124730216e60cbe2b969d4e332811e7a54e8ae13dae3e2fe9a981a5150ae0087e71c0c59f8595ebd6c07c602211ac0bc4a657f00839c87ebc390f6da14cd57a46835fe8b6cabff078ebd56dc1291261ae8353957d0433036428df4763165e6437120ec51520c1783b5abb8ec9ed8b887c92c66ad1ed7c1b4ef9d0caaf2b6f7ae21101f1c06d5906513896d146aad4eac3db7d8f5dc7a3d970c8e0bb0562e8884a285a175de0abcaca5a901e4cc7a135c35682e841a23f300c894c90aac4b5df0a21f3b2446869a5298f3b2a63875ff8e87184ed9426d098c8134d26109ed280f0d095bd52b6cd34f4093926115f545e3fcf65ee250ab12c3aa69b88cdeea202600350685872db45cefab5a8d3be78d7d0139cb373cca17817611ec13f2406a675d9ba8863c8943614eca133aa2c4ab450b7170b5110720b58835a5de592c5acda7c352fd676468ad9a6906af139cee3b0f817115bb9e3aa006ad7e541c906149e852078fb6595097857e94d8eea63873031a65602dc930b25723924023378621f9e7247dfac5c6037b795092dd17c9dab80975322d2ad5b0c8e24e77f16e336645d2d3ce3db9416545e849ff8ae90cc4803dbd40a5ab4456a6ad9392c926f1e95d229e9053c9f0d8201b7f180c02c5d84bc3323271981d266a592f78462ed9980f90c548c1a8e1ee1f033c8b38d14ac5c703fa85c43e5ebc6cb4638cd721a9ec0c3b0f4119c4a409dc9d12df50001e5e26c85d7113777bf2341137f6cee2b2558ae9a24af54b032fb3372a93014f5b52422691089289c04a9c5b00e61c93a7cd66ef90dc6675f087b44216320990f0e52156e6f02d4a0bc987f0b4bb0a850c5eff431424b0720e204e2c268c9405315415da43c6578c20fe82f89c5977d6186bfbdd4160fe42033d8e5a40904f44c94bf4a209638e51971fd3e267727ca5bd23dd6924640df7fba6562892117b4e6cb422070b665cff41a987b690382130ad1142993077f6d68e67041e80734c6c747c01f8b3cb81daa61033f3f8bc35fddb339c5dc4c244ad77f9b15b02158ce0aa5c8923e82942e438b232e16cd86213d349949d8a21359d148b6f588eaaa120c118820cb888385645699c5d822f61b977c938d3e7309c1bdb139be297659e3e9b2b554a92e934ea8719ecc35f14ee02e9fef4461b04761611caa23d7d5d37736ec271dcea28b7624c6f3d892e44352e00d793648d6be1034f253cc485fc5e373e06599e2cb381a64f67957415226ae095ef0a0d8ef853dae90efb841845cdc62d9937a9fd7a213479c92070e171f2294476d26233e251d53b110b82424ddaf858f26a266d806c4c15d72bc770d98cbc29ee13c8570116b55129b3fbea39a0060c45058ac904466fde1830759d059f168c69760023c4983260a9a993bc3195b6173f56abd93fd7fa6949c3282468abe35ede0ba1f8587d7b8fe7a5edcb8781011e1c96d6b9ad56ae0a4f83d12a1c85ed3c85e9ec8900c6997fd16d42b0510565048946a49341685c3d663a3f100a430ebc5240dcec11a5acace3e8ddceec5362f1b5e8ef4c9d2ec231dc3c00d122c67fabbb870ae09500033e3465290c362b9ca550b5ea605bde75733747a9860f87a3c7cd4b0bd925255cc4952ede2b2c03d35794a49e6458f3ccd551fe162ee339ba595ce12445f3f91655586441d27e90a88d061b6ba72f8bf853c5a7a8840289255bafaac5f2b337e3abd9baaeb15bd29ff4bb8aed9b80baaf4886c4f68cfb90e5974fff43a4357af6778a042824480f18705f1684230c9e8335007618ee7b6bebffff3f017600670267cd2ac702ec50d0a33c3541a10bd5e2d6f2710c824f28c0cf077728dc2e03e99a0e27d5a88c3c64cfe085d3948ae7a47e0d7dc03946fe24f41a085843e2cfefcb06e0e33f9431b22bafc254baba0ff4bd93026a5b66ef76a8ade80083953124e7d32ddacd1f2f36de041e1d6bb78a38c2930c84ae2790fa513fed263675a3c1ddad992dc34668a0222a907e17e610f8cc0e47cf93a7813f2c6f0ede330d332f7d4351596ff6672bce72d044e390ce2e847a5f2f5104b96bae9994c29dadf9a18b938d4c9e923fc5875e19e96a64111786ca96f4f7147a61d437ac18953c74e6b829cd14296f3d0505d9cfb025132fe96e1eacf20bc9d13437fd55254bbcc88e8010f37db2dc4e3365bb1dc922c5179b65ea780fd68123caad95d24813d08404a832b82fdd3d8cdb5fc4bf2c6a80e1d60167674cd7e7ea262d47401c1552853930a1395ab4589784725b03c70c1305246f7f9a1dc69629fa3ca3e18191d9e3dd0ed6ae71bbc153fdc75c2f33b40a638fd895fe75a594ebd49bfb31791a96c4ca89f9ea9db0e7f16d30e3001a68eea65911383f69840f3d8b29b2a25588f8818386d4aa9d826e54a41173716be9e1e72a1084686bc8e81f6f982979e8a8ccdcc789e0946d7a5158274e98d400c88bf2e194d6f62c6c26a3b7d22faa625f3e0ae05d3b8ccf44dc384101bd97b08d1b2c911607610833a085449e8522f9a49769c5c028a161f4a670360f706f8691a80f7953c92c58b5fdfee8e14af2be479dafeac898ff70223ebaf883fcca409699b7e60ee4cd505d2b81b232310707850f3a32acc3ec44addf7221762cf9c638d43d151c77584749d75eb2e639bd3886c46511d30edb0a0796fb46cab4950c62547c6606fa510219e18e6c038d59564eb5bc044055f07563b9f75eb6b969167bc03a10de756c5307ee27d6ebd2673fe29623784de13817933c254213f10902a7113d248150b9439f8a474ec32c51f04b88a6a329c74fdaad342c3a2171d1342708da18fa4eca191a91fcfcdab3f76edb250222c193a64cac6802b7e071a4ee92510d576ca0eeaa4b4f2a02219b76732958a0b83dd4b53ac1108be6c11a51f8fc4a08bd156130e253bb8ac981415522e84a4944cec1bd7c6be018c14216ca6b6c9a1d5511d2a4ce78db4327e4f62e740e9dec39574647e09d651ed88208f3e13bb9121021e68fc5265e4e47c17820999e880f2c36b77578d4f7e1c3a2d793cf2af1de5b666cedce8007dd4d9bf0706592ebf42a6c7f845a86e50ae4ddb6a5abf22fc497711af70b82153a316f62b1d1e2623015c61c04a9bc7cad53f938e570ef090d1a728791c6f78e83745de0c9e08a63d72ef22ba23f85201c13381d5d36fbdc7a7b1e9c8eafa604409b77d7f489f8ebd9f99789c4ffb6e2d3fed7e924ea3e0c98bc4ad69b49c96a9c2e1afc72fe66ab18bdf783b471b04b3f67cbc942fd309c42bcb018b4c61ff0ff13a8282e11145b70d3aefdba15f772235699dbd5ff635ad1065999b90ab97e6c41dd0086a95186b5bbca2e3ae87cc0d81f371834ef145a78650980f0ac653f729b97d519a93dd47b6f68d65e13cb1d1bd52b40d71b9f094644b31d53c8ea37ea584ab0d59cea18a53246e77ecd6a5c41e2ba3fa6475d3815f66c2d7d866f0cc852911e4e4af8fc67fad4082561053743a78fb0479c58bca47c6bc0418f16faa0cf7ceeb6bf0bf34475207adad893f83e3848b4f377d3302939177ed8377027d4a3cf51c1558f75c7fdb88c0aec7f6ed71a8ecb153b6459192ec8d8507abad896cffb1b575893a1271a7f061917ba24427fd3c2409c4da84bd50fe8fdf75b42361fd7d8c5fb1e50f8d2d1115820fa9ec8342acc898ed687da22c778533f808bc0051c15067ec08d47148d413526b6de52cc3aa0b5f85bb55e983fafb5497229a0ccb2e451c42b3a9e1c97f592c0c7b33372891158bd92b8d2255ad28422c3b692e9cd1852d3c5208df6d5da97bfb864c1cc51f06c77abaed0111b47c86a1c0186b5cda2b63c3106c4bd7d149e5053905209607dbc311af53e4c1b3692b6de0569a11e6976f3d8193620c28043f27345f423a9d887f2436ccea9a1af24311f526b8ad4c90987afca3a054a36a276602ba7611444fadb588778e2a25f8e14c2eacf3dadb28f4777fd045c01fac6cad1453709fc10f643475be5152fc254221bde775680fa2fb50997623138a97c289415cac9c2be143c40c871e322fa4d631f401afb1f159f40f4c905413f18c8f9f7626459f46362b42bfcd1fb14ac5d0964fb25cb13a27638dbc68d026009edbc7295a9775051b316a27e363b3c90473570ab513eaea028b052e2f0b746c51c015cd4091afafd564ce1517e5df196387627f9a12384c3b97ffe27bb3c677a60922926c06620857dcb4fee7d644e443150244be8cda44032e3b143c2f04b2ab55f92ed67ca6bd1b8c067201fd5a03ea5f52f638c8d84f45275658905cb708525f48fb00a2c88567ed5bcce99bd51885c0c34ee08a0b05ac9bedb4af29e9f0739a04430e012211555d1f2947ed50d3a85a102b58b9d4d3f92fc7c4db121bfb86dcbbc2d4f4a404b4da2c2cee07866ebfcd0ddfe0cf5abfaaf57848e0dc3e0907dacc8ff8df58557be0c8f1044751f0d3dd8d99cc281223e060a0ca89bd867e65490f973397628729fc4af5df5f3a0413b3335ffd78474cadca0ebe3b2ea54932d56492a18ed5c27634fd1c335159319e4237a8cea2961f6c856e45b4ccae6c3a8cff9f2285c0b2df081c0abb2f5c5638d4383d21f883391f8665e11a4a90a05972d14f1a31c722928a22d8a96470ef2edaf2ba671cc8490ea5664a72f0c35915829f086cf92c26cb6bf900d2075efd04c1e84698bd02c06126a6e114b99b32b432dc2f25172380218610b3d48799f25ba38e82b918b603bf7bd261fcbb87ba735ecbc32ea7185063533a3d6e058b95fa4680d3a86fc525ffdbcaf94180d57bab3a4898d6daa77339e7d0c132e3aaf51af883c22bfb0f0a1946e116148e581bb26f2a89c83c197524a917d71a06aaf3ba5a802ec1f1d9ef1e7d2e6a2ce9ea65258b3f942847cba88f959c68ed859686407ac161c92d92e8d62e40231c90d8e405d7f43e0ab506777f7561fd28d506e1d4c3cb861e5d6dbbe5a4ffb92cfd2c04599ce955b2c7f0b8c201e6d490dbccea20bb0f8de42b7845bc022c03f255a618374fa427a0b8320e3ec2ad6f1a105d7c88a6766038c76a34538589dddd6b5ca6f932dbf537ac3480fe68e3968b8372970366701650741c70ff30c4fd24119909c1839b4524589af369a706f2bff6deca133aff407dfb1caa220f5e24567f91d2110eaa03fcb23fd3f9ea2019c4a77b25d2a9731120e2bf09eb3d1a3ea10300428d5e5724b4e9b602118710b06b95b12448e1302efcdc5e8aa7eebedbd0ebdec3cbbc3510a3c5a9a54e66aa660f43c54267f9bb5255235a90eb56c3f8f0b3bc5c325965e2707a37b25c9d01596259d19063383e9c7956955d2ecd3ccee9174c498b661da086005b30ba31682da6c9888232b97c0105f01edd3dde235097fd386b08d106ca5c3372d5a8a42e09a32818d48e9c5a2cf6c354c8c16564c8341055bb2b7b599e26af58c6747c00bbd2b02ccf1f06d9a49445a73c966f3243e17cdb110a2436ea82f6289b0b2c154a61fa9a82b337907dfc4716f9c6056f981bba63719472c21a38f22634312365313f06683d6c6fc35630f92f89505afe58acd33ecd35b09c309fa08eb9f4ccf623b9f53a72716cb9faee50db0d1ca4d2e5fcaa23b24b72b6b0b2c924c496508c1511eac1fcba0c4b14ae0290aa1739d23b9f132317619d7260f8b6a0d869ef84c40dd2f23be00dcd1fe12467ff79f5b7c46aa81827317a68af105b152d8afb2e536260701c3d349ba2b2bfe33a94d91918337de747a966586177236c46e7505a76e85d4b7ee69b84358b1388dab3ee41dcca2ba036e9e80d7b886ace8b9c0d7ee7c22751a29bdd6155cc22b4558ec942a69109efb64a514de11a2af2570248741442f35127d1ba31760173a1addc27aeae1c48c2a03bfd7834c453d547211124413ce798a730ed7225b018e1d15706dba4bc60105aa283f030f672999455432e4a897beb122c63b50184284912b849b2135151ee6eadc467c89047861321cdd0ca76992d51d42658aca67f950a2484749c2149bea7ea312e14d94f3b198f3bc72fb7e9b468897f30477f796ea2570229eb94b2a67864ce338f2d9c57d2001746203aa56fa775a3090b29a0e2c6a347f5696a778e89aebddf2389fad8d822bb83cabb6897897a8caf55a7f4a07706c6305ed124b80550fe1814a18c85260373549dc0da6a0bf77cfcdc409e77bb4611fdec066d16462d27ad392b6b79432a594021707de074c07a9ea79bf7db75a6d1ecfaa6395f7d18ead5eebfd6a95e2ea7da9df71a15095f278f80877d0cf7d38ce7c843de0671a4d52d993496afc9ec5938b2df51e65483795b7fa90478fdafabde17e198c1fd375726e28e985e0b5033d0874e538af842225308221063abce0491660cc766d1cc03dfd988f6b6db984a82a0b7c752c0552ca6dac538543bc2f7ac75deabb9fca3d3f8a03e3e79ad1f55a425b2ccde8b626e4628c11d544353e711b9bd3446e133fc658abf14154cdbf899c730fe552f5a7d2314faa1e6b7b53e4363ec2570946a358ab3edb88aed0301a71348a2c88478dc39c51c4a2dbdd5d17a230a216a02ed20be2a81dab5f86e190065500814c5085f81090601cc2b5259341fd993b0b20fe1d5106eadfaf92a92fa2ca24dbb68130d5abd0fd7c15b8e3088501e97e7e17bf48e52f54d52115f52914ea13a2aa9ccb472d75fcbc23d23ef6ad43f3aed727a50ce92c52e33744cab8dcdcbc4c8bfaddd4cb5fee564a81612fd56e26ff42dd1b679ba98fa98fa663b10623b8bd3a965453a9d483b03505a6be4845bdfcb8221c38b903f93bbec55d19690d22900e0377dfdd75ff5d775fdfdd3d5aee432925277f67aca190dddde52dbbcdc847364a894283eb9741ec7ac8f97c9f8e929cdca49453baab3a36e79ceeebcfdd080b64bf4115f6e7cf47c915b22a0f14a2aafb51f5bc2a30f2abbeeb381518724da122a8c2feaafefd53a8ef50a91490aeccadabc05015f6a746d857fd7aceb3e2bfaf40ea81aa9ffffc0da92b4455e93bd7361c0a546184fe0555f8426ea5fae2a78aef7daa8f8f12a2fa54e8fea7764f6b54a962acaaf77ef5711ff9bb6f6bddf1d4a7020b3f353e070a1952fb990517e2b57f6b7b5d85451048ff7effab86fbfd5e774f745d7ff3bdbddda7bb7bfbd73e9f93548eb2b5e7f1f8b70b3de706269992bbf945da749fce600423c79329250407ddb64c1a485144a92f9a266f780826c3319b7904597c5fe038989564614e92c59b76821a9865a02a5f21aa257ca1ba91cec9902c65f0e5c139c0d25e94dc913ab9eceeeeee4988408bbb6bc18259e66a1d37c8b26ce3f415ab30a0609e724e99a5a80aedba485488a8ae97d745a2124455d545a2b243ec22ebdeca5cf446a292a44a30a4dcee8c59d658deb0a2c88a24b662688a105592aa38c95439aa1ee02a0bac5a94a75a141f6ac7eaf7a00a12350fb8ee479e4a74777e34d5fd9e157e3a27a5b4485298ea32d590a4b051c36d4242d2a186eb255ca5e84c23171a7250ddda0a82ea54a24269d4ba5411aa220400781cd59a2c7921c06c8f721e4141654a0f42d49dd2c3931a4107a0ea563f105887d87fd99c555a6a4d942601385183135c6aff3f11852b294094258f550455afc2a8ba8d47f7e81e4115a2bb47f7f8a8c8d5aeb7daf9ac5d94b593b176d36bb7a1a218514a29254d8dbb2233f315ec7143d5cc9cf9928f02eadbf8b37d2f09ba3ab60295f911322fd8504d51ba42866ddb361c7458d9028639e79c9d1771c458a1a286feecc24b142b3f2819f122a5a1f822055fb420084c83fb284a7844c9217e4d1335a839ea2235b1a5862fdb86e8505650fed055a98a6b66ee586c266e8faef5d11be734cba34747e11fe9c6e4ca21d5bfa5bb37693826c77d8c4b388e9963f7c871fccd1923e74b98bfc8f6f28bcfb33246191de47cf4d81b8e51e6eeeebe354fd331f68d2027435555cdaf93d942f96598a837c3a8712aca6dc741130e9175fbf9fe28540a854ac9450077207f03973b40fd5c047007f3b74f4ad492ddead6f9c98dfbf25751afa911544281b8498afb7d1ab55317eceef7342f175571be1cb70bb3030dff6b988ceee67e8edded8d46175d069121f9691d7bcbb66262b8d28ec5999531c0134b2815050d394963e64d688ba9542a3048e6d0328c906b0f5a1f1d186e476fa4c07033f28202c3ada8cb461472bd0db5941c6ae3108627b437476f140dad6a31767777b7516d2fc9517fab9a2594d404162a94a8e8e2325a123b16e3cb15638c4cbaf81ba58c320888002ad33ae68294bb437eba6ee6093953a3213331132b3196c669cee3d9e6d631958ef96fad63ee04ed388e93cd3351501f726d8b1d637fd1a478f413345ca7ca463647e6ab67791d6a0810c767236b33a46d9c05866c44f52d3c30e421aa2b998099547f0e0cb906065a0109ea5a01483b06eb5fa4122cc11ae422f71edbee8d6e6bbadddddded524874919860fa20bcd534cef6dbeb6cdb6fb416f2a85b8fbaa1b0ebbb6dbf6ddbf7efcdf6a923fa75f3319430f9f5c1c3bedbee38427fe9a9c1a725ceddddddeb269f5e2f4a8378056b239f9790520ab924072a978aaa0cf9897b922fb73056d43828707b0e9c60b77d2db09cbf7dfc24a3a0aa1fe857b97250308f8e49a9002985fcf828a420e4024c935eaa0ce2a314f331639e6381b5911fa31205262123789d08ca3b3fdcbd0fe88903b9bbbbd39ea0ecf9ef3cd7e9bfd3e31e083f3a367bbebf9e1d1a6891ba3b3f368f67f34408c30f2a27e06683ca329bb3f3fc31fceb3bedd447351ed5206e13dfc98b9cb6c5e8149f9f68414344457ba4b4c4d44e9ee34115246ab3f8fde424ab3156638dff758d46ab1def320cc87c9f208c2b52a311fd58fbe254a51b6de954d188fed7a51748b22a958a3b0995cfb2ae99776f76b7bd79bd8daeabe9d87ad782e672b88db2a401e5ee6e17a26331d7cccee316c24ca23d26112577726e1e8fc720fc9d38375434bc54479893358dc3fd8a9c01782c53658f873ab6a463dc84077bc0386c074a7808719b0a6aeb10121252b239324fe2e5840651bb0455b6372ca0fca12c0ae5dcdd39599d4c158c2a24b437610845dfe2e0d862e2fb690183bad3cd01911674b9a0216ba956396852f9bf933ac6556841c3176c1986ba60ad71600ef0aefcac24cfe1198b4a655691141a7295d554798903a2167ab200aa9255a5dca0e6f34695cfe494c51235dc37aa94bfc5cf52a54c017df9a3713ee6dcc34c0089ec7b173f19c1a01fcc847d7f0789ccd83ba1049e356f8d2caa82d1e8662fee9e42ed6721afda5ec6d6621759c57354334932358eced66d62f04ceae9494aea494adaacd07093faf5448d8f5f0d9f41435992925c924cf2656ee33f5fe639e06ccb82f2330c88d7f8710475f7a65165742eea8a89ec32c68ddfd76566d0dd37ba4f22e4b30ad59fa36cf72c26e8a24b884b4af98291c9224b39b7e3142f4a5ffce290bef042a3dde0c465b94189972049026102d78b87a0b659a11f3c0cc517143d70e47eecf0f463884691b27071c376ac87283f7aa0d22f3598bedb818b123f70f124898b222e88644e560a14fc00451625a0b0b1c5174034861f4cbc64b1b2c5052598c10b23340611cc28c2862349868890d245d00bc6e48a23267aa8c10937b870b265e5848d1c362755a6932b46d41083ca16329af45046154b4600ad8cc8a1862d9c2c4d275a6648835be3c9d720ade1c31a499464db94f2fbcd092911890b90e4d0050d11d42139422131411092232492248c7c3bd1450f3c62fc97ec6bbab0a10b192297837be164a50bead54572a252c3a74e3831c181484e2a40d26d80b1264d2b29945517c989e88a93d0c79c82bec6e586510d5f2667582abd6ddb11572cf1700597185ce1658502dad1b4a03990b08c2801b1024329054f74d645523aeaa2f484cb0d198390b52c880732284aa886a586d51a54d645c21234059619ea0e57dc50b5aeac51e3bb5eb22b5c5ca952c3974527e3c52f3896685b6cd1460e54a62ed2165bbc3428d3164b5f13eb226d31430d5f06850f3c280c9c6f73db3624af8efda0a1b6548464c5862512d490be7434649367023258b1841116b8518515b3a9c59c92c362469441f67591b048515350488f61007591b250523b20742a63e1b2b183ba485c60a9adba48589e505ea2f5076255a1df93967b42a129c812ed22095e0db8c4800ca718cc008d590465de57aa2c8b1abeb0d43076a5863a58d47093acd4709794fa8aba14e5072941c0c164082eb3b6629594686222a2c112364c31eb7fcfc1401bb5a32672606389a45987610414146cf183510f4766fd3b9e83001f9210c1c60f6500b1c3acc80e5b808e58e20444280165a6e354c3185996c2a082c40c8c230533681943056d04316b9a54d2fd4d879a1fc1adf1948c6c10b272021d6e886e94c11935e18414c7162c648894a6c0e0430de90d2328dae8e246b536b6d4624811d5dc20c288982528a129b424b4146c4c5368533011828b4a444accb0d1942062d2c38603955f53a1d2c9c9caa6e4f465db948cc4a841bca90d79d950464e92c68059f20453ab2d7112b2c1a9c6a4082e524e00a03545a2251eadd6049428eabe0a08215e4a484034692724209850422ac11257f7db89090b2e28a62f48d47d55152c9a142c54685c1cc124e50a50dd8f1f6706f5ba4ff354b7eef7e4bc6a484a4e75b7adb851c3ad754658d850c3ada2e9eef08d0da231a563c5de802a09654288b94bc5a0a771b6c674be63fbfaa0f6d374acbd1c4d688fe621cbb13eb6c696a2062ea2b82656b957bfd6ddbd53715f0c4156a38e2f14e5f16c9b2742918afaae93a897ed85208bbfa17e878e2ff253284f842231c7dec4efbe99bde10e22ea1754750cc5a00ac51149971b6ae874e898744ce6779d0e207c35756e9476d835fe7850c31d84a0ce09d3fa8e09dd8eb54036bea8b4b97dd57a6e81bb37303fb80367c5a8740d6b1ce02274cc3f867aff1f2f4a534b8537f62a7ca1b2e035823d37d08e45550b5c85d4c042a0757b5ab789430c317451a6e83a6e8eb5cb1c79934d75df87e454d1b0b95c4cb4888666ffe543755745036dd5ab5e45030d69dda902ca208561c51627a5373f4de522925d47bce005cb51a12157819eb4c072bf7ffbfd38307cf71ce06008b4fad3fa02ad0ee184d4540f5188dd4a68a0e108dde72319e8962fbbdbdbdbbb63c7f6eed8b1bd3b766cef8e1dbbdd39a023715f4a29a594524a29a594524a29a594524a29a594524a292567443a89fbdb717f7683a190d79cdb9cdb942fe3149af193b28a36d24922a86324f2244a9284061ad8089954e5934c9249dd547d28ce6eea26a0de50c8abeec72e28c4a51eef21ebd83a601bec5f20f9a51788361872d006f150b73532c89a3628f49dfd999f83e1f7c2dfbc11baca2043714253303b1e531e07050a07c5f3589ffac25853ccf455df6a512893820c457a205086c21e6c65e9f170c72a1a9b3f9dd0f82f247932ca1143610194a32618281f1d1dc9284f15708d840a2715a5547195fa988209b9a6ba9af23c9e546af53b1ec3b8d3405420fd18c41ec0fc0ae651cf791ed8c18021735e080192eb6ab4f9818d7fd75bdd7416f47e68914e9253a6337f9bdfab639d04f33bdd4092921ee03a302bef97e585f546528f8e21a197cc263dc524edf8e4ebf856a997f950effa541ff3753bbe9dc5080df5b09e584e492c27204f4ad819cc37d8f30101712f1f68fbd5c322385bdc6afbd58c84adfabbd0e3cb05d37a4cd6ff509005fac80f02a4fa2ef528eeb7f9b247101456a02d353d05f5e8c892645d117e2155a4a3b76271ad1edca471f8e5364bb1758aa1c97f7ddce7f8b69ff9e6bb6462bc9d96f976e68a79790e0709fdeb7798fdf5add6aa56ab0589a9b5daaceed462cc8ac5b57ebf5b312010d07c6eb5a22d10d6a08ffcd53659df938591207ff526c85f758fd6b79382dbb8938eacb61d8b32a0986f7d7b301256bfbdcf0ae67f627ef530a00feb27f8d37ad6af7e7eac97dbb726071269d1f49c90820bb49de7a26a8c6e6b6068e8f3f7c0de5ff694838358c8a3f9e4c3beed777cabd7f1b13e47cbe372b4643ab21328c8ee3299ceb6fddcb62732b95d0ad27a4abbf5336663fa302d100868f5f28158bfc539e97c1ff973fb613de13ae62ffaaef26041e697b01ee7339f5bfdfc067d5aac6f7d91996c3dfd64658f8510b8c23c7b41e4772708c1451dd3a1a04c5665647be39f2aa2cd032361be7c135adbb70827b80d2d48833d331f6e12d0ba0dcc8b3d800581f5bafc067db81fea36aee7bec88c7eb1729e23dfe5fa1999ede5ff3ca1ff13f31bb84e411ff91cf8c3b9c0ed89c4c8ed5b20110e5c10163faf5b8f137464558cbc2bd293d297e73c593d7f0735edfb9002e04356e2e0698e36f8909b76c042fee4ff379f83eff500f858bfc127f3345f8e877daedfb1d3dfb7b3ff76c6c1b733007c3bdbe0db59d3829222c8421c14d40b901fbe28ea539fc5e6bcdbac8f57b5b238add4772e05fdeedd04f9dc4fee2791eda5378d5aa8f3d18ef84bec8d3313bc44f41d9b089df330bfdfd338addf3fc1735253a7a38ef700d758c2ce76804040f25f0f349ff531dba68393bfbd8ffced05ca97e00f0b0c02eeb88de7f866be15645cab6fabcc6340d6acf5d18d351f798e6b6dfaa8ce822cc1526aeb838fac48e12494fab8c86dbc0a0df928dc5a6d1c5fd5984ae3c4ef7a76b67fff1552aff2e7d98ee7ec2c49e989b5fa22b7b3f8a3e6364a4c4e9cc44bfe2a7ee2a39e1d3ee252ad27f8e5394f5c32cffff1693ea49bb4e3c355caf161ecf5e132853a341ff7b00ff53bbe98d7f1c17c8e0f265f1f4cae3e989cf960f2bd19ecdbd98e6f673956dfce3a16f21c176835be50940163e0d787ed531ef21cd8823012b867bd09a8a72091f9a86781449ef8cc47a19e087d0e84ed8c462d5482b220332f7380bd89b21a11c036f1c354dd03ec4d9401657b133de749093b9b791ad379cf99cf7a58ff56992f6167ab127636030201c53cf740308ffa8e7f3d5883416891d956197082b0a6efc33dfd8979ee1bf4413d7d0afec03cea89fc0ef52d03939171bdac4752dd6a7c148474ce8c510f51aa30baa2d694cc9071c5e7258db3833ef73ef439f0e749ff06feb49e8230d9a0cf7c14f8c3fa19f3117942a48522c2026124d0473dac411fca7aee613eace79e05936090150804e46dcf4b3a164f4801f5adcf646e1387b8ab10f50bab221d3da58ddc6e71507f117a7eecf8d2ab8a1fcd2ba02928ffea439aa4fad3248dc3b3d77b2d64221d1fd2d7cc87cf72599dace36bfdeba33ff3c9bcf771cffa6011e683c54fc5ef7e67db078baffa9dad07eb9dd6f1edecf5ed6ce6db99f7ed0ce6db19abdb71ff1d06e90aa4344a4b1551595b885eb409d1fe6e13a2e1af26b037deda91aa2d0ab08d7f980a653a3a13d81bff555f41f977f41894398ed2d72b1693c974745a707dccb3c7c2ea63c0f5587081b09ef2e3370e0b25ec0c0848e65b0fc43dfd15086b30c87c7f1a58ffcc17993d913f5bbfbd4febb7cf11c66aeb43aae3ae77aeba7c77b9bbdce56eb3fa2eaa7ef06cce04b64af5a6135245d0d6d3dfbd89799867bd8b7bf93ff365b8ed4bd8d9f6331f7f9ee4007de8cf803f1c088bbffdcee8ffc880b0d87ad7c7a95a6fc2fc1648e4890febe5138101d7e7ce4c904f41222c70dd66052e088b60100f0402523d50ea81b89f0fd43d10ea81b6973b1d230a2bb8cdc7426ee3dc938a874ee03ae61e8bf00ba822cdada8ec76376f64f62d38a0492922882f3c78510326244a336c48b49048bc98456080a3062f6b08a1435119b30d8c2fc25071d92c4da84832c41834fc60c42c6ae9c2888700a3736666f6e6715575de4890b32073823f2fccb84f8773ee7e45d70208db02085d5b30a167fdec5512e48ce79cf3f75b8f9f146af0e7851983fe82774771a3f5c14f7bc36a4145abf2bb3a0cf42b77ef1e3f31b82570739c092919585fbe7cf97282c37c54cf7a16f8b302b9067a20278126c4201dcc47f5a922b3f9edf9a85e05aedb70adbad27a24f0ac035f6e1363309ffd592f32ebe21746237f8a5fe8b59ae77450123fb590dbb8c72667258f4bd53d1af991d73cc9979cc99bdcc99f222d0ac5a148148ba2513c52f21c37024376e2265eaa1eb9fb1b84b91b45a7d99e926e6b4237aad1697de87830eb7e4c45f3095b651ddbb654b7ec4f0586dc4a95877a0ff52a549702bf635b4ddd3877ff886a7fd26dcd4c8df27b1efba3b7f2bc95e7795e8c1ed45c8c91a317d99927a27cd97d3274efc5b8d3de8742752147845655a94ef56ddf815c4d656d144a96ea7a4058d5d9d1e042435ad725d0ed6c63b7effeda1b2e27f26f5050a2d4dc50734b71286ec80a4cb677a7b727a8cf23ddab55afe565e665def70f77a707a453a9ed795da89b0b1deb0d1c02021a9d36ae30a463fda896563afe8ebbedc07fbd69156c21fe10aea2f6c69f0748a4d1501b634f7e3c5bfbfb406fed41c065b3affa17e4f6c6ffe381c91a79b68301c10ca02a9fbf8b31825268dd66524197d81c39248dac1449292a4b29a57a9751439bca8f627ed10d0c617677fd9342a8b5d9ff8c7414d5501249a6988673ec5b54c518e35c304c41cae0b52e6394ee93dbda25bb7331babba33aba4a87466989518bdc76a03c623139a70bb4e67f6e520b17b5b80b1c2ad52b68710777631c7fe28ad135ea031d9d96d47b3ad6dd4316e5d602e5119373fe406bbe9b9ba4e1a28b2e2aeed4b2a43a0ea132c81b9372048bdaa4ecee82edeedeee1eb7fbbbbb7d3b450b5f6d06e5ef2d8e5813f116a74daa213fa9d1440d9bd640df497a49ed1ea22d31130b8e80a25c496249921733d0a8212761a935642ccf45ddddc58bba6aae7b76f3e8028e1500f1a5cb16235d6ac8436838a92113b1107b461c4438e0a0469391a1599f51439d336a50dbb11051e1624350193228cdda8c335c50fb5d1bd2d45447c38beaee6918a99ec695ea6afc50773d2040ed9ef378bada343492a8a94ffde6f1a48a844802d4342b283b0bda666b94b6d9da46954b453d0f0a27f46f5ac738a881821ac783c20ea2719719ea6c3171d4737a92ac540254097344c36f5a738c46a3c550a37deb39f40c1c6a8cdd85869476ece5ae7e71af1ad2a6b9cd264d19b989aad7a6c3b4a0dbe2a0fbdddd227477774ff7ee8f8e71dfc0515ea5a813590b066d88d5c240356aff0e0eca4b87c7e359cc98a1368d3106000719ce71854b12b57f068518b729073698509a428c256698452e6fb4d1a2a8a1262db5b3a8fd324c44a3d18ea82a9023b498811a477a8083064d4d3a97c7d3311e8f0b20d258a9614d8d3c7a6c31428a1a9fdb22831a9fbe60d0438dff3048a3c68f4151e3cb16898ca71a91caa0a246a4261f6afc96c7139562802105e3f1b801c688a336c756daa03559c3c8952eb32ec38cd91d4357c5e951439a1a2a1850c31e3586612516461b62d4b07411c3cb1850d4f82b1dca4fd09ea60c169ec71363184c3ea81ddf606a83a6c6a399f2789c4989234d357e8ccf05510603690035765f9ce85c25366abf44e1557342892867d8e083123256d0b90c548b52edda1f3f8831c6a1951b400b10b53b4614628c31f220451833d8a043adcb2cc2a0c61863ecbea021ad4d3e9c3820c1175bea222925f1a88bf4c59124aa5cb4fd2b463f49875ba24cbe1464737acb92832f61403063497567027da963cc811feaf6405a62d40da67f9b889340689a48dfe4d09b22120db908b63d176bc31f46e50f9bc6a5f2af51f977466b215ffa8d6bd0977c697d3491116dc2e66ca2c68995bf85e0efa2c66155fe56e2209890bc58a2e96661d72af750cd26871f3449716a9c70912a4fa15109b70c21a0a122881a27642a458d13f2f3937fe15bbaf892bf01d4a386beb4a4865188ca63d4301ae1475e7376e2a4229ce641dd43d5888b7034b03e54545058904e52e29e8fdc8697a4506ce297419256f9a9f22fe84bcee44dee146931280ac5a148148be251acc5a4a8149722932f7d0b71fcdc9782db3435607d746fb0f95bb72d757e94ced687ecc08e513be03c54a95d83edd85e757d74e059f02f6f5421bb650a1255d49d62c4c45477ca0abca8bed4383b0589262abf7f31c54993aaa3ee142746953d086c3f7fbd119a561decc0e42f35821d98208ceb04774b9d1dd82d75fbed1d84f96ea9f2db2d357e35dfb1ffa07160311edcb21e4b4b1090443459baade9ae507f0e86a1ea0dd22b5b53689c05615cc77a8514d6c70c1739136d155e0c89b0236cce6b6d160c4e724258ec048de9d55debc2862838b06e316a87d10bee476077bda8fdde02dd1bf928d8366538c42b093d7350486e4ebae5e6e211d73cfad7ab0963bda914a796c531a3b86d4a146a65565d73f7c655f85319b4c2b448e54a451366b05fb07b03f5ba3295ba81f6739ddfb0473dd34caf2d95d4171aeed20915681cdae4a505cc71ddddbbfba31d6b2b688c31a680504ae8ff827b25b6442223593b602f1d31395c30b06f3ef7af6ffb1c5fea633ed4c37caa77ed748c2bb28a39b698e794ac972c22ab9f93e564d7ef74049294f400203daeb96a51c962cdc87c25eccc050201713f674010d6abf799bf9a727b291304e6bb19f32dd6b35cae18f087f572671dfd78266141562c1870f5811b69fd022dd271a544e4798cbb6ff0eeeea12c490793ce0b009ffc0dbef934df8c8e1d395e3bedeeee31eeeeb2218edb4022dbcfe7362eba7b74cf11e38e80ee3cc7ef73abdd6240d9deb88c3d16d68b5e8e99079aef8a1cb771ef2365fe6746c605c698d6f7243ecc473f58ac51a7e7841454ee8e0a9f33a8344e164545ca148d08000000f314000020100a07440291582098a7db661f14800b778c4476563818c6b220c76114c5188310228400030c21c618234435ab2000024e96d34835ab083a08923c5b152f05e88029fda1c00fb839c154ca57892de080791ddc7bbfd4e955b5707a88dded0d0c715efec74ba217eb6d7c934de876f42a065d28ec49d35eba0a574d2b7cfbbe83a3fb9877ba74684c49ed6326c2829ccd3368ccdb7b1ee85f6603dd47ce5cba8159c60fe4b43800b7280de94050aa8cdde407ac3100aa9ec8ec0c06905f9e68f376352097cd6eae66f5e213673cc9d2148d52549fc51b0a275083e2025ee3ad973f393241e4241389fc0d34d42dc90e01f48a38d650ba275922fe146c1bccd96a6dec4c8b0e0019e87b0407526ba3d44da888c59ebc87d6e41570867fd5ff858eaf708a92f873806a3284e4adc0c49d07b6987f27fae8c99004407a9844b4c1f61f86fd899cbf4ad5cbfe67d6b4ca195849983d4a309082bf7cbaa8dc6c7b91a5b6713fbffdd9f9274dbaae684daaab0c5c2b00f5d10fbd378f9489ad0a9644b89b91ff30a2d6455a6ffa55bd159ba273751f5927f8e5f3230d81d571f3bf040030ef4cb8d81cf17b47dd602527f9d0c8e1b8efaaabdaae3e9ef25849e91e0601fd480424dfc4fa099464a1eb90cd128c356191cabc777468c3062b0aa85c09b058712b1def0b0c073bf09e449f64d95836efa71bb00ec56ddd4bdd215d9ab25780f0d268d21766b92a56e742cf7ab069970fa85e231a00c771537a6b172b3a83b6ed5306e5beb7cb7c110a7d785676d7d0338a1ba9a1df139931223029196d024e9e2106f22a7ca6b4ece9cf2d0fd550dda0d312800c5e0aa1a3e42dbff08bda121fcaa7a7ba465920357f48ecccbaac3a579e63996a702207533b2402c21482ee7add7575db2ff15204a8c596a1981c4312d9dc785e935ee7a37b67aded854c9d97b965824f82b7dfb77a386b99d04061e8677a516209657f9798bcde75427b1dfda15524a5a649e81cf0d5c964920e0bf83c41127e07853413bc49b3c6f82d9478bbe9b5d555689fe94f266c94f0faac8749a020eed29883a448e4b52eb4a82862f3d337c2119020c8b220f34b1a1b0516afbcd9255568309472205db411949b5b3474db31fa2b086d9561084df5a6d4d8cca38636939cbe7cb1dc9aac8ddab454fa95566c9b31d6647b8058b07ad24a609830196cdf590fc93985b6e0bab9063b64326854261fe58e09c5bfde3be5f4fcf38199488f9ee03ab3172574d44396234dda0f29797a82a94b000f9ab886cd810af78fd628545e44c424e1e01e1982d7be914aa26661336fa08e5f781ae1023497acdb0d8f95d96e4032b66a5014775111d2e800af7bbc6dd4edd25279cefe56489bdef032229364f40833821b2dd96bf5f919e2184a958324684cc91b282c50b8505ff208abbd4d1d3e7be82c80320e82d1ed899aaf48ece60a93c04620106ae548559f4088cea6032791a7c9288e2a262b63804094557fb3bdbe01e46a2d45a1c3da0030e7a4f866179d9b415fb5a2cbaa147086a86602ca81f246e080dccfe3d688255b0793dad748906690537ea803859b8c1c2967fd90ed134353f6b475fe1705f3eb55463eea0cca48ce000f4cf48199846be311ef04dd0dbf9c4e9e0fbe6d2b706bb03802b43e95d98eb55e45c7108eba319547418c1899d43939cd1308bb9dc912117f2e4f9a25244633bc663d5963d6ed17cd891d21cac4c4da8ef601a8b0e70d683519f12c84b4caf833dbcc45a34cef0edc8aaef88d600a0da756ec820a5730f3d748d23de0db701c0ddcd97da4e553584534ed5238f72a45d832f0422da403e8a1f75825c57e531f48ca9ceff1de2253a2094839c3ae0967c2664b0242b827ee92c7175c17b6be18ff2342c4fcd3debd1d43272ad33b0233e4e3f9c4a8b5788e861a5ac8cdc5288994c47815763c150f02f7e0b2566ef7e38056026d7559b91c84fb0e14f07f8e350532d2ec9151355b8e960cdee6b105741eb7a6e19345e9970204f8c432041f31de1688e6393117390f6f8f3c229ddf210531b20ff907e798b35ea719854eee09944661d8eae3b447e9494776c9850ef6e6c77be62715ef432f16e4ed5fd3087da60d7d6c08cd89fdc1d3e77660a9c2dc1ee2988ec33ceed47fbc2e664a3d27897d0c8ab89b148fb427f7ad28f390009fb5d32aa3dff1fb91114924c34967ba753b2239ef9be16a41b5a85829890eaa96e7df2ca66ddf411060228a15a4ff0066500e440065582c0f480a453ee23a6c01d5c2b7c4079509742e4504e6b8ca65dfbe4e69cbd745811167770d2c8314d755eddc6bfa5e75f340431c24c6b92461b461ffed8c444158f206a8b895a71846de40a2683f689262e5edf73b64ebdfe0e18533b981925c19c27559deb8b57ee43151ef3202414bc48bd80ec91e86d9a2b06b353d9bddab7bd9388ac081e638803f201576c49ef1064c5ad336f658dabe84521bf00136faf28f639f31d2cb32e3122aca3652917578607be50b1794036f06cb8089bbf66ece029e0e89eefcb4363a0846496061c05aac440f62d292b343252b365786632cde428aa260c5eb488b583560004fb020b2bc270bf27c34f2347ef818199ecffef15c429e8fc559c00579deb36c4c2a05f23cfc016f90c844a5cbe6e2212c8f7d2dc2deffd36692219d964ac052a8fd39a2c61a5c6e0c1e24aa89820459d300da7865f34a903282fea9e4283448bfa92b563d6336d823180e281548763e05498000d58dac71c3f334c3827126217d126cda8d6f52d8e5654ff01066c5a81fe39db3802c1e6c8d1f444231d780be5297455a7d1c93209ab2038c2ae0a4aa875ade8cd1159b704c98a0a57cc832d57d5b748f163f40de9f729ad1e60397b96086fd4328702e97d0a339a145faa20c4052ef8466cda0dafbde33b913f7fd7d053501d822aa57e74cbaca8b1172806545e13e72812886a5b679bdd8d40b331760dbd73d63ef8520761004c6ab36a4445da9dec0ed6c150a8635eda6ac06d4f67c2357bbf9532f797e6634e9a1e6e82ad7e02e3e68e2cee30421595513c9445f34632c4e61d3e18519a68e15ca9408f4963ff6918b80032eb2707f5346f1a9e5d089d854d68e11e374dc7adf47da14fcff23f73310d8beed66f17b13f9e1291fb5d8d1abe9fdc801b02ecc2027844bdfaa0c3bf3cb40b0f9f5714d7b5be1ee3852df731691da354a95b83ba94171afeb4bf39785c4721f9e7b85b1151c6a6783be8d1c58a1bdffee6045754ad0d946c211631c109e0f3abcc948c08f1cdcb3be26ea467f0d1875a5b93f2dedb41023802264098c8b65ec43158ca6a99ded17ce2c812d3ad79faa7f4aed7a9c001331b1d1d8f6370b4c6140c5a061a2313d38cd9ef80929c44c64c75bcbdae59df36a6cb28fe19dc607b8b5611cd63074ea72109d25a027a2320c8dc6dc8f5cba989b2b541c4230808029d3dd3f949526698f51b04fe0a92d8a02865e47d2615cbcea0073e870e203402b991b9baf54c36557e4a45a0f271e04d848b80e1aeadc70edc29b912b0183a9cbce6d28e3b5d58930a84a400351e63454045a738eeb7d118b0101a97c1332d7ff171b41dbab2db96ac1c0bf56294b8ba90059020dbb9a28406d503b5af29765d233a9d2752aca417017c555ac3ba512d8c6e606fc6192a10a19eddf02e5d78b45bf8538f613ddef657478265d7f5fef5776e7ebb3bb118970d799fb2f6bba5f4663d3dbb49b4da24f6e24effed6127904ed2527a5a0bbb1c4753359063f616410b72b07d6cd0b9b13affd83aaccd2c120a407aa31b00f9efd04952877f8f6552f3064daed77f437acf524dacefec94ffda22b69778a2863fdc74530b61a107f690da111c8597b2e0e019d4376065a1fb7e8576bfcd33eacd3cd4db38ea1f6ebbfc5265fa9b227d4c7396c07771ae5bcb02fb434560d2c4b100f0858a1d22e94d3ebec5a820c62d9e36ee38f820799182f18db2d11f32e69f56c2eb3086dd3b5f526228caf8c6bae38d4a9de623b579aa47683436687c0df91dc9d1e2c75bb0c59ada332792f9e2be89dbbf5b72e3d479c8c0c719a327e08645f80062897a2e82885a9cdea4b7b9cc00675f320dbf312ffc4c9bcf3de1a89865418509f7fb9fb384def6e8b749bf4a2d83ecdb4a751f9a4813b1c280be2c39e7eb34b938b5e6b5d9a9d27d4c520d01fe29ebafc421ecd1652a477d18c9ede12900025c255a35885272bc0031e2ac5469842c491c34e218cd82d3a206fa8e67cde377fcd2f10fea917bc7d6c22674a1f1699af39226f9a3b2b6e32e0dfd33e1c03e7138fa2ed695e71186ddc66e011b3d680045237881a4eef907d4fff233fe31a44716a7f8525b657588bd14dc278b6437fcfcabc44451b8ec8656634f810bacb308060e75346d66d0f2b5aef52c3914c2bbe48514f06be6072871292b0332c14f9cf5a1404221b45aa637cbaa7462249ee0add36aa573751ff882c916c4704aedd1f0a3d4103f586762e7c0a183640a0d1fdacc3920b294f317274e64d44d06d302d6f80caf018565a7a0366abfa96ed4393273f7044fb16d746d17541da9969f70898298cae54626660ba7e81fbccfef92f8fc9014446b3ea009d429c13a19b1d53a7e39961523b1cbd4fb9364a3c6006e9c4b6a71aeeae9b3ee607352631c9c0f588585a0664309923aa75be521b50f3d2201eb76c0e38b36adeec3f51a001a7184073156f5931e73b9eb42b468e629b8f26cce07ea7a08bb445046f1dd0485b4aea68218f1e14f5b454052950444cbbca928b0c16822a867ddf3048c9efd7c65a9087100fccded140e45b1ce0ce7fb0598e8a11ca24ad6fbf6a9cbdf0edaa46244b96ef243db47439066a5aaea5e694b7493ea952cae9d91217509e1b5d2c65fe0584ec71905b69760230a8ac2e1ba9ada58df7abe45a6fe29f33e486cc72d6636eac2d743a061cf4b1fde1c2718081902932d30b647b5a662bbca57335bed668984e98f45a5d80261c6f4d8ab78faa8984b422618bdf595e705f344e14bea366b89f9b02aea78a836a7463bfffa5f7ce029d6fa7e13d0540f6c474146aa621254115f0988cbf6288f1a44ed02f44c0340d8547280b9860e10c62103a3e899b018052b98e6737af7296883658441c8c105ff04ed1e440e6bafcdf0fdee2c4eaae06db67e35ad728607a1d3451a2426e193631fabe6504738a752a7c6b73188f58cbd21b7ea39a511b61495bc8d7f357d9e3bb362157343812e7c53fc8feee020f6ab484c9b12b684ff1cdf9cfad89d50eb82539c55d7f53d7b8a88086db4812a36ab5fd723d27ea70fd71a1ff0bad58a01e8f8b85a304463adb512b4f3971950e74d192774ced8c7503041786016afd141b3e6535c2bd8d978c9151e59b48ab7b4d607c1918c19004937f617454d8dad7ed266bcaef72b52a00c6c6d38eaa74047931f93942d65b0a19c0b4e47fb482122933ed3dc81ec0902541010736184dc76644ae5d343e4707564ad975db78e7708d739f19ea7e4ac1bed733d9d1d4d6a42153859abb5d43e0ff1ec5836ba68af604153821e6b59f837fd95b30d0388bcd06f0c0ad11b1aa9302324ce0a2a8ffff36a037b4b13e19ab0158b6e42527fb9362cb195190c643d4e1f82a7d448539e481a679838ef9ec8660890c4fe28dfcd2805ed69354a891a8fea882ce2be5647501248fb5565255df570a88ca5f836bfdc0e1b3570b67a0c26130c96ea1733cf072d81e604ff21c033ae95fec5a0795920e2496a03c437c3223e27acc920843f409fd688516c1b9c0a8495051a9ae1a439097db2845083020dc336e0fd32ae7b1387d284f64a8eb5cc1f45eb825ffd2c6ac6896647f247eaccc92358393a14408264eb16efb10f23388699aed9b3fed8fa3e64d1b26fe20fe530c7c9c39156ef22de65991e7dd3bb6c52a337cdd922089810608392e63eae78b276ec14676787f632004762f4392941644ec2cf6587c9e92d2a27a3fd5f6e451b70b4c5cccb67f86d8bd9415a98904799ef8e5e774d3ccc49373e87c0fd51b57f1b84b60fa4b60e3b27856bb3c65bf56feae03c09d4b1d046944bd669a659aebfd54e2d5bf2c305522781cb4e8ba2a7623222f003eed302723bf37e204a82ffa6148dc23555087f9176e6e9956b0947a222fe5a555d60557bbe553e6ff108f226c90312cc0620f18f9e98de25c88dc40991556f3c1ed16807f7d368a3635611fff559326f8b8c5ccfc435c675cdfaa00a16b64850d81643ff98d1cfb69f180c8a05d6ca6d4fa2a3dfed4500a0b811135283d6210ecc3fe983712978d1e0f41777174aa162be7379b9b14cd0e12d00a522f272ae264f989d6ad487ce29fc85c79dd0d8e90bc5d3bfa3656cb88154726d4153370b04a61174d50c1a493d0b24ddb8b2e5675c6e0119e705dd48ae24a042bfe5636a23d0d6abc108a1f336ef063163f76b0fde44928db10f440820d1cc517e93328b652214b4bbb2b7de2de0cb0858ecd111294dba9d5a7b601ed631ddb8440a7f7de05b2c2089ff6c3d66c3710321e88e0df65a297e5c4ead8eaa3e54c50d9dc4343627d8729ed40ca75551827b25088264b081c5286580ab1e9268c6da03562883a6522a45fde5b10194d477ffaef0ae6fa17d2f49328be6b47d2a1f32232e99bea53fa75203db7de6b16281ce2876371073c9db6fa96efaa20436e402a8ab07e5471aa2c72a73f98924471ccb3af181ba37b269590b936032e5d1bd2eee88777f766ffca360e7596ede02760ad83bf68d3baff498f31282adc9f450e3bee06ac4e189543c68819d5f37665678ef2f560298a0ade895f8cf2225beee247fd206c297ce1215f43eb08c0b71cb66d08c7dbe7a2688d8dca3b6fc94cd6ffb1a6ec947fc15406f1a33b441cb5d65a1d6f8c94e895f63e2af0cc61bfc401dca9f7c2bf1fb7e1bee8df9be1513502ebcec60c2657c3dac96ed826d1cdad020450f6c383debc7250276ac1a32bfb39289c12885c640fafa71954275616813596990c4cc201b1a2585f05cc2d002ada4da4f042b1a085ae63471bbd47a970c221065d4c64d12f1237c25ad964ce1b87001126003b4fba6006ea090ed0a3904d824ab0ce3b0975fe9385247066f5425010cd369786b8805944b43e84566a10fc1eaf1f5db60dfd6447c7a039b984415ee21c42a1431c4e219ef350a87183a4c5bc80f40b5bedd6a70f9b038ba94a5a3cb6ce6c7865b556ae03bbb45adf667dff41156640ed3ec3af5e8311d2a370607be52ec88165a622b6c2d548ad40f7f89710196a663ffbfb0246c72e91e804239815ea76665c7e04e61133a27362986046a8c2795b2a071e77b4804a9b38a5b029487cb69424dc25e0a1fc568e10d9573ce60435f4961240e1d34d4183f3d202d9fb31caa4122184452a6669aa86616053a7adb2aee13da7b1649e74bfb638e9082fabab48cda0b33602846ba0fd32a3aac8dafdde20fc3c22d54d658940e20c3ccb3ae3752220c5dfb522d930f0fc5bc301e6c3e9bb046089d9a6c65594c4a55e095842d0f2a8c0f173dcb2d60ded5ca64fdd538acd785d415a69b276a9370fd95b90e6193a0e4d80b2ed80dab2c3521acdf129ee37f48ac5d5ef9d62692bf681d8e1b8b58dfaef0bac01ccb9e0ac940c816250d60bc31b2f709b78ebecdc252f8d21cb289e7894ef6a20eb30412c85e7555ea2b985e74568c052322d6bb9fa512deffd00a9ad9a87eb9ca74474eca7f81952c435ce2fb25e4dce1c7228856d663a82ab21443c7298e4d4e8fd7315ef449c68186468223dd63c57cf207a07135ff08d7a409c8ab6173b68a0dfc3f761bcea01ab147f93ad3dc48b6414345d3d952cdaff6b739de7d28c6137ba315397ebddaac77a4f38a7d59c5f42bb0d086eade67872ffb51c14040bff60f6b0fa28296078d839dd1c77c73326c78d05a014deceaf002b879613f4b0e77a2740e209af5cfbc4b29a608e95db1306de9ca07a4eba5bfcc436ea6f9bdcf90ed4862c4018e9c297a20bf5e82eedc0b5dcc2fac185659c7d850140d324606fa613a0e90481601bec93f7485da1277aa089892b2612ea80131647c89dab06dde0c4bc022628402bf834a88db4c60f584f28d110860185053c0acb4375ea8c0a95a64305174087a5b7b395a5c3552fde33935856dda4438485be90ec63e1e9279aa626f64ad6f1ec10df007b94fe3e7d52b0877f1205e67816e9ca5952380f8ec49cfd60a4cdc90a092556a25ee1f20d312b833e6611f7693c087c1e0c35d209377538c5f6bf0a065958776a5544f71e34862a5614705eb4027f72218a6bf2de4315efb0e8d87fa7e6508423ca616663bf71406ada8ea339b3ec774728f99748bea1d3773aaebeb5da901ccf0160b465643e7005f582bbe7a5a11fb6f6be8533670d74da448ebbff7a4ea7893adb69f848c3308c07275d2321f7a658a673b06a406f9d8ee315f8c14e4b58c19745ce209ca0ad7e1301bacd182bd14c4920c74b4a8e1aaa8f0d9b53ac57649b5e32b825057e2171f371237d1bb07d64d1c079a23f64e9b2523ae45020b322cb9312f7c21feb25ced51a09fc1ec1557942d21550d0249b496690d19e722554336ce3e2787be2ff04fc03cc6a7faa38f07a440c316650b6b478a6e283a9de8507897f3a724aaaaae19ea2e7722c669fa2103a3a45a86f6df0f5419445638fb8871f0dd007190a928016bab0a65ec50c555062e2693c3aebd14dac340d7f808ece24631312425de022afec3a074821f30ff19f3a5435501778c5b707d07efc830593e50f4ace158e2edd655afbd06c27e250b0df72899c2d80455c07c586139d106b26b2a9bfa996c3d65ca1ca5fcafe04243f1bfe02f080071654b6e8774f40096a892d95b6b43aa2bcc004c1dcc3c741fa49775b27b918314f0ed8edf557e267996a72e0be1de6b972e5a88e39cd0754ff958fd48d292313ae4df5d0b8934fa41fc60f7d16cd94c4e701c50e06e43ce88fd1054a75f8f7e04d076e5c6de51c4add013012c20a093bf8d25bb1d080058535964b659fc1074f7c432d7c36c98056fa98c1bc43ff8996b8cc7a014e79f825df1686af22902e5a020d60b8efcfb4f13e92787ce5ff1174b4c1ee87ace8b407b7344b3f8bef1e7ec5d45fa0efd9379b806cc69b0e38edf03d31d64805495dafa283aa415077eda63fe98d039089ab61031746d4ef6628fe98e25f9c3f9143391de0918c91b1886fddb6e64cd27a181005d52d62bd2db142599a66e0aa2ccd471a068071fdc13955e9cc95758a94b44a95f4b192862cd21337661f933f0586c0ef0dbf73776cdf2ef86f1c2a2860d51dab805385cb2359b2e08c811a302dbd342a84403b93f02640ede77368da2d249436ae37c73f3501b31db31f3030dadc78f44975d897a74362b3930e6e56d162c06a5860d554e000d807a208466d17879910cbe21c008917890dc9de80c04a33973ed8c4d0c76bf2743b5bdffa807d455a02c12497874823696523d2f1f33baa562d9f0e9f29a3da3b571d3b8042eebbbecfc39abc0733356bd5b0823a9f12a4ee8e0a7adf5f6daa204e77ab2394dc7662a610c9658d23e93193b5aa8add3a048440b339edd96cd9241032ffa6fa928c997985f05394896f46b137daa6640940ddf3157a70350f5b9fffd5debb1096857013c2c1e5659d628a9f24448196ecb3f31f1dee847f9fc563ae706d160b71a679e0d25fac334fda1b3db56bd0353c6a0a6ee35f43411184efe508cff4c09ff87773382080a0ca4d5cff15463350dbdbcf4e05a0af92203c315267868e8c44777607550e447fa02f64e5a633c00f83357f00ce160414ba8bac32874604142b50eedc906b35d6ee05923fc788f6c9221c2dbccfa35dc01ed847fbad53a62e45930276e5f6423b08a0c6cf024f76d5ea9425076f582a41a35a21656ac016ad474426f188d3ea73f3d022441a03c81c864f606210545a27f13497ea18133f8545540d22401b9d85b58c3c5480f938b86159b763eb023f34f944d958680c874ac5335d8cfc5df050d438dc6f454ea6d0998c6f34f21d69fbb07143cd3edf74067d8c0fc928adcedc69c3ef8975d983b9952051bad3ec8188491be151b25293b93d06f513e9ff3243cc557c7c48933ab192229651b00747cafb9cca0ee57341579949790f1879eef92274cfa56934a51898bbf0757628b01584443a73856e5216b605d532f8838ab77c9545e445bade2638b5d0178587d0683b926f53712ca840dcc0265ec7cdc14ff4ac3c1f5c3d2dc21369bb9a396b3d8d22a222c217c815402f9ad3c5057af02e89874ea10d861a179f2d6991227bed874ef76d0ef2bd1650aa3e6137783b937f986482afec61b0f3d08bf906ea0063f6514d6189eee0abf6f5b0308056d7ca17d28331889eb80b4a35da2680ff251f709c9ef84619d9fa6782e283659b5a9062804abe5c5919206c384850bf1714a627ced92bc7acf366284d94916bcfe8551878fea48ddeade1e07938daf3853a577e9a9c58fe7508e54467fdc2a74ac198bc62079b7bcc24c111f0e94773e5fb06387c94a0bd45191b74f364aed81c99d4d1298021e7eedf1151b64a03eb931b5a4ac6329c1f457dce06efcd0ccf2ca084a6bf7ae306cb474a9701958bb30b969f52b1e4a2c7bab874d5cbabc060b8dc3779a9c78d47d543c20862998d017e363edbc72e64ced2af291ebdcbb94735b0b832307ea5af2e0d6134236057ca0b1127346a9f547855c9219b32977538dd11930d074c385ae5c413f399dd34a0d99dbbe2024c677620b6dc1f7d5b0cf33578f1be8fc8cfeae641136db45527847dee4736a80d4a4b747f0c75185eb16b28a6f20ad0c0cb2e8131685618e252ca5f022dfd202ffdfc2ceabdf8c4967de5b5bd337ef169d2c404fd7b765f2cfd02cd512a31ab5f23962cbd7cff4c069cf30b855dbc2a4eaf1fede6efcb3cb8398957051c0ab7ba69d9e23c890e7a0941cf50e70c4c1a9efd21560e93dbdaaba5451c8e276c160ddde1a8d1bb9d0256a8b00ce4e1d0a9069aadd81cafd5b6f195fe8431dfb294353a532e960fdd200ffcb7ed7c8ebd60ca6dc75f23b750ca006324d78c3cdd5f8c1b993b8d44b1bdfa33f374314223f284d5e3f592dba428a995c5f04718a6913296cc10edda730f948fc8f8e3fcb7211ade30d9f0f9b6038e1d038c6359ce759e3b42391b5699f49244f3565b188544d7b9c63741bf3a7394d4e0fd148d43c83b05b2c3e285fa5763bbc9ce180e810b957261d1074fa642d3ac1d633519a6bbf758fd2e3a0b79a0131a36b9c7041a94115a5f632351de986ecc479ca094e43eed0fb9d6f29cea9fe5578f5ebd00daffd85dd196b0afe8a046889345e34b9de7c01c15f5ca8c62bb862229e051ca80c6ca11cdf3930c032b869e4b57381564dcfd36b037e59f2d5089fe32f5574ec11a3c558cd5511b14e6970d7e062a27913798d41407c3bc83b775f30e4730283dc94970bf8dae6272b9c296e2e7f06b2705a18b2cc8a1f8069a78f2dd1d5ba6012d9fccd426920a08a63a9febf1f535e7f371d5f9ee3b9f81e9ca626ca5f6dc2598b172668b274e979e94360cc88c4962f36501cca2f29ffbe7f7795779b47a886d3b7b7afe8619c531bc265484984c0d96286a8e42878a3e2d348b1cbfcc517938c11fc530f3b54d6d88b2e4c901221b8d58270862d96b157926cadbd04b932c5e0d661a6837319fd34282a8053c1b39674f46f9673899da66749051bf61b3d3bc0179612aac65dc091a7a6e7303bcdbea445393b3dab159bd00c68cca743bdf564580667cefc3ea197c93d4e2ed288755b6ca6f83fadd40262827f75eccaddfd97464a38d98e13a6b5251d05c4ad040f341cb193d5fdae97f8adfbfa57555d07c626387e67e37f7abd4c43e77360bbb12b4ce6689e5c798ea6b76b84e0c2a8277f14b74eac6d97fd60266b6cef33f8c699c1b8692ff82216980fc8aabd995863fc50c8fe73e0b8023285a4b4e5138a2476a1ae09128c3445511a61599151bded225810e2656808fc33370d32a3bb0dadc59b0ccf4b823d53abd5ca8e4515581342cfe997c8fd3f541104fdaeefdde9af33142625e1e95ce1cc9140c65b3102b9143d926a561653e6c4b0cc30d15055a1adb25128f1062a5d142f59645710c1556927534b6be14816707c039267fc44aeb89563ab7b2d948892b076786452f70313867c5927b980aaa050ea4857d7a924bbe4a278b9d268ecd18d1ccf36e1ddfb2ddd9bac6870e62d68fb295d6cbbffffb67e4a24fdab98349382b5e444176c21bc06b5c846545b28d646a634259f0823441f8838140e073845f03c80426e5141f98ab79c1707f82c70a978a0e2b151b599f4e44d2552905f1d1e88ddfe2e3db3254a2fb1bf216954e320b8a80e8591a2332ef3c554c003dd6602444d6e53ec2d847cb4ebbf2547f2b3b92dab29c3d50dfdd9a315f9945161a6fb1cdce8ef4d5974552ad750a9e95eb9fb9d925380cd9343a97f0d445bbf874e461f17b2d1590d1ad474851431678580464a1f22d9d94a9360f6f607de818bc78da4401a62ea364ff6b7034704780e9923534177b76b5c086634cd27f0ba46b8b8ebbcee6769dc7f4d3dbc5770da3acc47107948cb7f02502031ed82e9d69c735acfc76f016f8bc7a494d5a19206263fc866729357d4b42d4bafdfa568503780809f172825e92494fc77137ab32662b93a5e448139838fbbfd5f9bcc36e15aba0126fb764b92447db523aac391cbb915b5de041a9e1473672e56476c5c667e61f66398a5b80e546fde9b6b41819ba49b528e7b05e3dbffc85f78920d43ea8847bcdac3fc154115dacce2b21fb9e311ee8bfde35cfd692d80258b6cf40c008082ce8b1bdc3f405ebbb78b48e3236ee88fbc86b82c7c2fdac40f1eafc176e7a0ae3e6708e355e4e2458283766dd122520a843b3677c1e3b1510d6c0cbe3bb4792d00a7fc6ac4912bd9242e4126d59aac319ae7a574b71014d5380e035fb41e5e92c2eeb85c957f7271954961008f735882fb57c35891395dfc117d021f3d9341d5e2126356181e0d9417aba167ec9f0a0cbe5daae56cf2491b9f636f691ae18005fc621165d571a98a2b9c855b78effc1a5e8d0792fcd5faebb49258e68b9699e6d57cdb9bcec99118374e2b00d891da863210cea1deb0674540d8d0711ecb5832a28a13e9d543e83799e0e22aa568baba5416d78ea8817bcfb83045b97d4414454a46ca20516d066f6ae4cabeb7f4c2cad0c1f5293267e2a502c7946d43a0ca782404639c9461699a17e376099f3d6a05123e782ab9957478722699e8c5e4e301550281bcd823a0d8a752faddf7cf739202996a4f9f8c3322677ca6f514ff484a32854325a59e99f5a15366a6582b3f1ff7de0a34459ab555c482ec96c849dad34ce8b4e27aeacd9cd49ecfc4c91694f33d7d4d03dd45224bff23d245b8644aa5ee895236fafa63d55bc63d95485bc4f00da49713673623ba8a110e2228137448e99959a4e939b2a5cc34d5d6b2048636c47907f1257ced3979cf74037c12dd04824f7fbb653a0ee498b00917f4c4336382cbc5d1176a66a6368949d6db123d59b9921b5982fefc344884e8efee01d1ddaeee416e5a7f9b0849c2ddc57fe02480635603c38de6ba1ec88932415ae0da9a79c7c2b58ebd551524b13d51e0fec09513fb4d17c3fb608dbd581026478bd027b9e85fa378d8f27b549cd920aa6190c2acff50f2062d692b00816a8eb3dd4da3f6395115c9345a1ee194957aa686f17ef84187daf34c747803999349f52a37147eb4edf3276d3d48a3c9871d019fa2f3a4da74e0d3b1b0d50b411379c723d9f336870bc1d381e69c9f205c6b24290cac56758c6b47ad4413ab897928c249700e1986bb662a6e9281cd015f291f5ff08f39561c1aa7440c6395ac6a8a2a18ad55949931ef3392b7c3cc38f40cf9613a49d9441d9fb89195f230d03f14c9b25cacbe09190657f8def68a1d1131c12bef9636c4499bd7b526bf783dc112168474812b2023699c5e40a5ca90f549c224f5226150ecaaa438049a32dff4ffa508448ae6dc201e1b69d802aea263f2cbff08af3f070a37a0473a7b5b14e2f4b1304caa271c1f472a392efe636a412a8ac9f9ec1bf28f8c0d4674806b88b2cd7c1026112d3b37995887b5cecd0760336189e646e31c2255aa7da83fe8e27ba2690c5b464e64d2b1836f1cb47db4cf400d87e1400ad392685aff6422826399612133125b3201d028881e082d23325a1c2f797325f0343e1d849f1aba7e9c4ea2e2405344f6358dafe564d1aebd4392b0ddc29a663c10f7ed12accb1a06303e726591d2d46433bcbf1d0fff45951e465dccddcb6048a70445e20215dda28a31ba899ace7a48b64248dbce246c384c125a76cbb05509cbefdd2333c4df6a18bff0d75625930c2107b7937a75a8ac088c40cfe8c31081123cad03e814065fe0265b6df9bb84daf3f904ae2455a2b1c3fac61433efcd59964231e1f91b21313f8745e553d0068ea552ffc041076201d0c2582ef10bc2e4f0a10a588ca32feee6f29ad95c256fe748e32d48cef4f970c6612b31f70b4e2e4a925b301707ff226d52b2b09d24966f1ba2ba8ba94cc4c1e695c6c7e7bd9d2a96b1f45c7c15646db29b5d4116ee96ed4134dff9f0338d990adb44a30b68c9893b6b58292947db61a5e7fac701f9965e1a0f26684d6195624f230b55690a4e2a662dc4be8554f68b3c454bcff304063b780e4b2877de615c55482c126e19595157e34468c44627ab701a8ca0d18301ccc2fcc6639206864458885c092bc646c4950a896976f19fd550163a626558025bb2d24d97a25f6305dc312958948ee3e77eccb68e83d2376f80810655a86da9db843b9fad261dd3ec65482066dad0b861d99e458e22363a9b2c1804910405bcee94607faf9cc5a0a3db8254a873215b8aa1bd7091826c19718b227c3fdf6be235dacba4a34ad09239480b70644f61f14244ef64bb44365c3a3eb099e84a9d86b90740db7e8fa375194e399388c2288879e6682926332c6e04414e1333b2c10a5fafe323358f2b08dd187f2adf7e342976eb8446b4bb11c43e21fe3f6fbc0acb6ad4564a47c7732a22a6931eef1df280c825bad28c037f4f5bbcee6101beea325a14e91ab14600c5127e4a9f009f072b3ae6ffee1525c6c6e393a37a2ae3358477e09b33b982e96b9878851d94a1e6f0d7790bd0cf2530d141f6b2f83822a06932ae95c06a8a93558d00cfced4966d7f40f7d47959b182030dfd7e1ca9b01e0f418eaf5aed01ca99a7cc4d5d49bb59c8959230031d5cf51b541617653680f0a4a4369d2b5811c7cc0dc96cf2d5e1cc207c48381cc9023d4c620518bc8c1f75656448246acd0dd8fe6ad9218bf96183834dc9728849ce249633070b0d7e28bc95a6b0948fb2ca33728b5cb5f3e09fa3112e4d0a5053604ac7c7470e09d4ddde54db07253ca50630e2d92efffbddd58792febff184dbb3742ca640d72cfc6636da11118269a7bb8da90a3015cd5b52ed3c887eb5305827b77e6134761c99ee6e2143c1368fa5fe03ec45f5e9442c92a50449a41c161101ecf96fae2bdd68d6acfd88fdb19be9821bb93f2cb2356cc8c1cee62baedb86d198a2f834e83ccf743906db231a41ed9d624c5971e272135ea481f53f106e2e69ec381debc2e73324f1382b51d70c81013257361e29b164e93c8b247b952eee2453b241375795d770ec5f78c7c530e6a62d11b843fc59e4157a27c02f09cbff5568e594101a0afc7aeb4d19ca3c1408601cf5151bae7d56bc8b27c6db2df84d8205917e9c211a516b530761779fb823687c30bef17c513807569742868a4121e7e759922404aa3784ebaa40d2213e55605a253eea27018a970523bbb5d9060137dc216f300206c36176c519e1233a811bd24208fd9332aadc8ab25fd239bb56419c32817e9bac06b55bb51e51fafdfbec6afc92c71dd32d2b183a9c11405b14b2ef85b2a60cbb33bf6f3833508f1e0ca3665981966f953b1fcccae0410038afd588c2cd6e328dd95c0a480bf05040adb753dde7efedfecfdacbbf77ebf47462f2e8f0ebd7a5da99eb58a3639fda68f8c17497fe08614ca352f4088293f4637d995549f8eee9f5e9cc64da849e225a5cfd414262e6dcc4be194539968548a1138a266fd524b8fd8cf3a31dc4c7acef4b781a2dc8fbfd40db5d115afa7d4bbef2345968a64e26e2f00454fae0ec646b6eabed234f7cda503bf9355d282939f0a0b26cd15ea0ad460a7a5bdeabb9a2d0efc344abdaf105d7e34d3b4ac606a89ffbb65b3a6e34a95c374a293c6be73d472257377f81e7396023e757b2f5a67aa7766886f75bbb1a68eeda78b0b68d5d2b9d62cac863b861014bcad2622e87335e3ba2c5c987bca1c530d7b3ade2a0440677874572370f6a3310ae0734edbd7fba78c3b46d534bf733c526ff5929cc43be449e817971eb83f733dbcfde2b92f888497e4462d6601059f5abbdf9d8ecce04ab51feabfdf59c54773c08625caf6beb568f9743379187bcc92ad5127dcd2773f09ccce2e810f9fdf330f1332b24a328a8caf13c265e1dfdcbf4e173f74c31c30a8b31aceda9e6f021bc35fc1d7ee04f6f1a83a4b0c03ed957ca9474ca0c66192e4cb214b0856dfe544a0c133d10eba5480328e036d4dde97fed26fe54286b3f4e2860d724c51e711dcbb9f9bdcec7a9b638ad95c7c8be9622fa31259ce92740a19612ae4e511a3a9d89101ff3e1199c7c77ca9bf686a6b98f2d94119b038385814086a027eb1bef14a534bcdd18161457f6b24cc85d57810982401afb373d42114513cac3744766677e0bd8adef97ed43527a85ae270ec8b73478e1d1046ae187d42b2033308f612b940b24a9f1a024117668d5714d4fb15234d402c1893a70f7bb8923f914240cc1679ee93988f828114bcd2c74fcbbf608731c497b483e3df7b0b19914ecd352e12250967151ca0016ff1f71a6d603e7d40b35c970728788dd8e325a41f70cec8023a0c2fa48800dbe37c039910c2b64e26c0b9195b851477689ed3125f49d3846f81a4a50160a006ab9ccb85924c3c9bd2be20acb7931c1fad9b782b45720a8208e1f9ff0ebfb031f8f0ff1625d631d9eb370d4e9bec1c7e52f4c2427a1516d34b10ed57ccb2d8463207f0ad7ac811907a7f5158fbe5f78a10cbbf795f51b3f84def2e0a4bbffc5e9162f917ef2a6216bfeadd4511acc1df4d812213e457d5754cc482d2a163220a4a878e8948d03a38266241edc0311105a503c7580fd1a075b0f6df77546dc4a06c05311cca66d54d3530bd293cdcd2be30b5197baff87042440a3ac274ad4953714a04e2866efb137b10d8a025d0e4dafb551e64874089e89a7a094d7249884f6800833c8f876511e59c2550b8a403b610c75288b486f82dd61183986430b15a422b9bbf9e6700d97ba0f55877491885f753906ae18672987acc4310319c7319e581321231c974274edf430a2fe958292c34716bc12cb15f4c3fdb116fea9832c948bdf70753bc0b0bba2f02352d65330bb1821d4696c769c50ca6931dd4e914d3a096234779eeb3b459535f067675e8d34210de31a9818b7b183781de4082e77787e5bddd5e0f8d0ee19abae165a49e875ae59b7fe9fd753e195860d68ada678768b9811a4aa9162bf39ec27892e347961f5a3e0f85be4e6614f949f4bc9b4c1a235ed370be62dd99b96737d5e2bcba52589ccbe9743952620be7722cdde0038ebd43713b9907506273bc414ce5e30c7d0cd4db42e65e37c5ae3f22204b75703ec38b2b1b5f12d24c96880dee4fa3e033ab3bfafb311d5ccac047119b8c9abd0643e13b2b7a5af55f1bae372770eef21dddb88c411a3a360b522806d4897bb8cf65aea1273d3b58fdaa45e140112155a14349f82b90d3f832549933f17fe5300005acefcad1733c23227a826475a60359bd8ec65082445f076925c0ac239b2327c3eb62296e4414f6955fcc941f3ee2e9f81ddb8795ecd1747021669fab27025b208fb976cd62b288c1b4bec1b78acf8c7e81c14d04b4b8baf183cc3814c1a4b2b9aad974e55cf190164b734e00e20cfa449f0d0e4e5c557211da2dbb9e1a661dd0220beefdfeea5d75754b817af7c07b52c2e8ba48301c3f5aca08524f2ca8d3f528c12a35432ccb216a881988c71d26435f7b6c16dd76d076ca20a90f89abd79a1ccd29b153562999343c7aea37985e1a4d4bb1759c4f61f0ad3ceb8eaf0ce5f725c4e60677a92b86e919334a3492d4e064a355d7630cee58323f1f5ec912a5b7d6a6f438afe0168cba1196f7a0154e4557f3e6a4ec0be92c1d5cde65127ca13ce975a297d3abf1ab4d1a30a853b0b691c72b23b8ad83296308445fdd4ef3038d910f98b30f1c6dd59d488b0e4b64a4a3f4eae3c0ebf2330f60a0f4345e6097536feb8770d3db5aef84c6ca1d9bcaa8afb3c41b606801f85c71b2358ee80ddbe3e0100ca3cd7b0397097526784d05f8c22828ab3f0cb0904bc5771540e063a4629fae2635be35c6de7c51a4d7aa2ec3f7ef5bba7cd44f6c15fa25465344972fe2e8df17936de871bfa328baf245739870483de9d7e3bb41b5bf44dbadc79618f9d7d10f13aad22fd406c99a289e82cdb57110fdb658bb7a29628f8f79ed9358cc363698b6e18fd09f7324379bb0d99a03f2962d661c36c5920902b0287facef203ebb874e0e39e0408e83db3f65121123a8959fb0dd0a077a8a0da61c0b5541416582edef3137bb0edb1fd48174f304e42688d5b1a74c889a32c156ec3341c34be3343c24af01565c54478dcba52e686ef9f84782bb1229ac30963eebe4ba7470aa86769c34f03f1fc2c91b4fb1a2260905db9160b9c7bd722241b1e26974b57f839616914601cc2307022af9c25cc878df9e6c103b2f395b06d1528a0d83e29b50c082ba4871ab2da1c11469418ca996019c20ae3b91a0b48d1bde19110daa50ac4d2642e0fc3cfb6ccbba507ba86376f0a329d644b09b479180fa6ec863ea6721f9d6a2615f69dc04521d8fd89c8ce2a294e89003e1a96708143b00e63c8f447f0fe4478a2581b5ae7853d1105044ca83f0cfea8b63d11f5568065c3c71c0acfa044fb6424d4883ae0022d63ec2c8ccec3e98cc4923b95ebc0b29431ca446cd4b295bc39214bbe988b95ff414179098c767c43106db8ac6a5a8f809affec41483f5525fd8a717310e289df135a8b832f3534ccfb0b9d5b79a046126909504f5b72c26721a472c0bc49ac9dc260fb56bc3ef82317f20c4d7f0e6eb0bf578bcf45700566a8bcac134d765ac943e8d850b342d09d12daf629b65b13e1574913bc3dd13b36f06481fe3557688d4e62ba736eb2498afce79b121f62131cd6c15ff5c6b9f96b82e07f16c68c78f261f0bb3e25a68e2b0bc45d1f0d4cf4ef78b183d3f5c4ea2511423a2a7a048005d0c4a1ac4fa2d4115f2ac6997c94a003f90b9bdb0d29db92426c99c9ede87d0f72d0c969d1b5dd0fa0cd188f187d15b10add8bc850d0904cf01d14f0467aaae92e7b57d827e289515dad52ab233829862ed9126cd4b94b7c10bb5187cbaf2b9d647be23bf01a76c6bc73a91ec9d8c679f96cad338e963f15767d31ff4948217e2e5d814f0b2d17fdff285ef517ba36c99ce72b3d63a74892625b52ff969f680edb3d65c1593562e036b929badeb9998f5e293003cc281a89c30d050aafd6f214bde9cc0857dfe57b4e896941e1b04e45de3d6fad52e853ee2b48826d2b5cf81ac823f4fcdaab8d9222f51bc62df8fe1f4c375d676e0fd3d776c0b6c401f574455f9f0a55c8bc42ae14d41658c33afcc5d1ae56c8699770ddcb7fe948c26c0eee04d98f6075140324cea958904f850163ca70f5352b1b4534244d863cfdff9099bd1508978a3ca6bedc67492a09908a6556c0b3b1af3284e7a93159f8b199b8270708463f474b12acb8b86195da32d356b51e24934a320eca1c60caaf3b032548ba00f4652c49f51ce206af028dc7e35686b2af3850c82b69268a7f57f8a88045dcee095dd7644ece1dfeeea10ed4c3d176bf8c3b519b4f439b4c51a2183449dd33e31ae801877fbb252309cee927f120c42bba116e370130e84d9e0ac79db3998ca27d6a8d6dc449aa4acffef73a2d1b00df3fc06150303e8e05b7b31b972b33a2123ea1952879fce1d22887cb9fc0045c6138c92267e02d318439ecec9346d44f7a6a9733c1dc4c62534226e782237b927200fc3cb30370ec8ec005fa46315b9219fbd9f7587148bbdd81808dad694a21c14dc9d38343c430d2fd95add476273aa027773da0246e4c4ebbfb3183234944934dba84f10b6454796c3ab6c60b36fa378c3fe0026a0be15e4f4b901fa695aa0418c6bd461e38413302db071162451525cd914fd9edf86683d3aaf6789470e140d7183062db1c7237c27d5425c4835369797fa4339a935aefe1ff5797907c04a39a271bdcea7d57a735c4c907e9fc18871893725f8191f6b03fcf9232033c93469462c11450ab86475170d2b9ac3e455a3731847b273159a2c80d6e757178db790f4b4e83ff7089a2740698ff710ebb169ef82511766258a9a5228b5583169b5239f99e932e56b867a54862ef2e8d159f9248077a6c2aa27dbe5a6c510139308e82bd0b8a893c16f8e1fdec94660208d57bd05013bb2699b4cd7131c17f3503a8f95e1056ed745c11b422396b00219e57c89cf6dd779514fef182f2b995a2e2b64016c03b3d42485c71e87bf1204b6ae8bcf07bc7841e322bf1a88ff55496621905dd2f6fb9276f4e3f65c1a55985237287b563b374017cd24fe660fd03c9613874b6979955519e2ee5037e4d7983f16ec9b1f13ab2a451d13307767fb4bdedce44d38bedb7ad6bda4aa515817d01286cd81607ec6f8b7f43578492d2739a2917a1b5f6d7c021a6805546cad0b210428ae4046cd4e97cd2dc26e428f1b9bebb8de481e3c99e126e0cf632ee5c3c090cf3a492369de9108142138a59cccc0c7111272f13b015393d4470e418ba1934c1076819ce0a1b4fec92042def666580dd33b535fe44cf4135919bdd91e216aed6c57a511aa053cc88cdb34f802f8b4dad1e5e9c072cfa5bb3e59f1b8e1f96168eb85f65f6d4b26c22baefe7a5f06bd620820743e56fe84542c9959a96d135c157710a14da1c18f2cf72739f64c07d3b14225157673e9a40b96f410dc0b9b7225a380a9ea6444954caa4c8b3be7c873d16788c21fb0b65c69c98b17f6aa844e2387a2bdb2a0ba67cef2ab0543826e1779e30d8cf2fb9b7e9343e60c22dccdca1c5b281fbe0a67918bcbce23d0e3d5d3342f770a2b72290e4a321ec6018497c0ed58e2940612f2047eb4618da46b32b39cc5dbc59024d9bed1566e53232c6e2756eb00911ecd71b77d4fb65d6c52b0a4e77b9846519631dbce1b4bd1f3e25a2d5cb63d0630f1eaf06d688023769a60d7aaa0b01d269a8fbdba718509c53ce6cbd1660eba8ddd0b38ba7a14f19b9a1eba05d03d8b91744343a6056b02589f1af89335d76b6c2a0bec348083c4dbd6788b86a54ba502508ac110909d90143a5ac73ebd52bbc578cdecae31e08062153ada9df692de56f7f2df6c528581b96b7dceca072c1ec3d44beb5e74302998b0994a8c1a8d2a6e0314be2e5462318e80cbf5cd107f05b74de2173702db638f472357249e6a742e390a9fd071d3eaf445cb499d43b8d2ce75ee40f4884d1b1412ab870a54a37ec24c8044c334df6ec8c3c7ff3b7a6f6ed3141b4ed23775a0c878cc86e6375f567d91be968a2f145a29e9ea9a0f7ea7c567c2968f7aa8708e281109a8f0815b7da1e617a8330956e1f5ba9d7aa71da3a25af7c3b9efccf78f91e2d310ba7100998241e8270c00b5a75629a13324a29890df303ab6b9807ba9a13fae5cdb8848370e43277e64a8dee11d115db25d77497913abc2fd31d83a4f230906b1c10c71b549f539a267f0ca422f0e8ce34605652f5078184242eb53b7aca2b0a3fa9febaa9355d84f4d92f738dc621b80f49e91ad8080204aeb324594e7b6c9f188625cd9737de2a67bb99dcdc7b23ef988c43c02d463ed794ea5eb46b8796bfbc94a3182cf2e267a77f5a3582f3a218669ef5500105fb8ba64e51fda8667deace63ab081740c43f5bbeb445bd3e6a10e842318bd518da52a2c0ad2cea0d73db3286be6fe148f3262a93e6aba9a7a2b8575b0bff20a2ca6a70d67ec0cc1ed71b0a76ac74c71b1bfba4d2c1ca1b0e5f892e65c1bee4966e2557bf1946f5fcb53d30485685a8e0e57b79ce40f32bdaa27219ae3291e9098cd7638d38293650d70ff9d9118172f44d8dcf435bf3854ff09dba5817cc834398feb57fef51a1338014408ff9d890c08a3cd84960fa6f3fd814020d5d7cd52b6158d9bed225d2c891904519c9da1a2568eb19175c0184006bc995acb4821274acf7ade2870a1803e720a841ee4fab1aca61086726c23ef55a9e443fc0ea3b3a0e7cd4e570cf390302fc4a07621149dcc948914283acbeb97cbb92a8a0f3725843695ee6bc62e693f877e2ad8f00101e210a0d322cf66485d3f26c800020685cfde380f879395734d4ae13d801b25f86079c1474b2faf8717d7071333815314a09cf05d9e9dfab4034c8c576e8d2cc759c509fe31541a59433bff1b701b7aaf6c3b53f79fabc095bd4e1a71b3b47f457c80cb60b3b6e2b67f61312dc31a497eb5e8c2b44a1c412f15749f33ae0cc3a7a6decf16f57835178315791888e162cdd519f6c025e71f0ca511499c41c2f9f61fe56195cc1ab0a76b86a56b4202b7c99def22d1bd3ebd00c445310e79742017c5fdc7afeca213c18164efd7e52d3a636501951799f9a9459c943716c013c802683459c0c9c58fbce007c29559806ace0216961da9203e0b901597e6b1cc30842f7baf5dfc36c00424c6245bb638c4bb81b25ed36ef6f3a43aa28e8f602d1d4afe9d668e29b5538d39e56be25f2bc50051a80bf09500ec7936c977fc1638f60c5ff5dd1762f5acea6019ee7d66abc05ddaaf3d6e7d360b2163c3d9bc7edbe6f829464572efbe9385d9e3a981fe6bd649f84fa8376a384ec437e14a5b97ccfa4180bc28a6dbb2150faf12a2440c76d26a0a004e2a00b8e2ae4722cd8412d02d4ac0c53dbde0af3ca2fcbb1b68cd8078463a6d2e1fc42126a8bb42dec06b33c12776c09958e2b161c3d4477e833ff7b30f85221ab2102b0d434b04160dd170e2745d5ace26537bcfcf51dcbcb0b4dec2536b30765945bc32ea68d26719ea9c0567d7358b2977415e18f4f23212116d5e78a2f6a4971108e3b44351f986a9bc0f69fb891ac852e9a0eb7c996fb2c361780afd4a1889b8f319b84d260c43ac09ff1740b11d664187f5d9c8e84cff6066a588dea5736be54de41f325b948c7ef4b0e55250ae884b6f43d8324563b62f02ea3209db283b898ae067e700916764041db52093de326abc8d0e20256b4a5ee154f949610e5e0b4f444f108174168d1bb64e7aff334e9653a577bf9816017e58e2b90963bc13fcffb34e10f64453ca69fd2d202aa7b06f9ac4fa5af06b0705587282446288a6ae6ce90da024371d00ef03f14bafc8d0314899392f340ce520be2eff0737eb29417062532f5c0f2a7170a5c81ef5c09a4d02ab15450996d2abf3c7d381ae63efb768025e72f9f33528a3d969dc4962f22c85bce9dc05b21ca29ebc7a160588dff56e2b9bd558ee11de8a69ad4515e2ef3d80831f787203314b855666898b2ca4e5e0976356f3872efb75a9bf0535e397f781c93bde82c3b550920c1f730321a2bd4e33bfca09767ca5f7333493d92a419b6abce6300bd55001cbea51d9383bc3c7e7b826db458a1f56e6815c90e163f49271c94096cc1e298766926c4ea499305b497b62f20b1c95dbdfea047ae8556f7bfc957a7bafb1a97a2402ee2a2d96b9017daaeeb81cea24e87b26d9655d2bec4dc4829a75bbc883c5c05109f2dcea4e382d7f70b16953d674a66be495b0834f9a00d87bc91e0ea512cc118341867c501690b8589362dbb807399ca62292146ee220ec095ce13dc0fe50c37d642abd28ab2b33221ab8ca028537148fcd0370752b5f820075905778927fd42edc107437174ed1b8f632351119238c425bb6db81f67e50b8b28a39cf00612f9c56eec876e7333eb562fa9e7c82526ebe03e36b9bcbf182778f394668c0c52df25132252f1e354f77bedd7658490ed66cae5efbf42664ed75046ed15e5b5dcbe8fdd8f3cd70f8fbee248263e589f9183c67ec328678594bd20cdd92cf20648c6258853e254acc2a6715be9ce57514a61b3bf9e737c5ea75132d8aa8e36473e016eef632c5590f52e3ee6a011745533d46874b730339b73899001eabb13281c74d923f0c0d2ab93ec8d3d344a55fefb8beb35e0a893f9e423124048f4da1c93672c8f2f2bc09cbe1812614ea6679624593a9e46d1c1b2efbf158e7762d1ee873bb9112f52a1c5865945cebd7268ceaf49dad2d1a31436860a63cae79a9aaa18fc067d5473886337e53e9bc628e47ae204e08562ff829aa77be0de96e8776009f7c053d6c78683d32d964f2d4f1012b3602d4f5015f994219f53e958568386b4540c9f1290e1393e7cd9c3846a9d1bf10101e4b9491279f1e29cd9f6e201c9420a854c56207e4491a0b2f48595edb6fc765c03c6db08d4986f6141dc95d971f9edd2e8b0237b4468ff06e2a6034aae8d23c3567964227082cedf709f829a06a1632313c1dc3b7d683f2301744c8335b07204fc2dc395f42f0cb3df8db5c7c14601e48a2ad204e6ac1cf7f0a12c2ebed4560d1e348328b7eab8f2acbe5db6ccea12e809384dd35097f8e1f0d6349331429d712c76afce197514050f10b6da2f8bb419fde3920aa6766beb29a2c97a71530d76b4f81ad858add6635a374a7ec92ec2769b3fa3d3f98cc9e685d406cbed10d23b30aae70d113d4f8a4e56554e54fe3a2f70524fb0b0099293d4710a7baee2a6e23324e1e010ef1dea48e60a483e4bf8f1e1c04ec9774bd02025543b7d9ffc448254e094902e3692baebfecf918baaa0a2928ca6092d846a3dcf8b01dcb04d949c037d45693f5a51521627e5f3014f3f1441b0e61f4203c63a72e50673db0d29a3889fb6f4b46e298fd3b5cbc58f55168c2b996872d56ce6fc808533658c5093ae068d19400a66d0ce4ce4b6087aeb007bed9064cd35673d8b05c371e0081cd609b65cc118ce450c0c98078f26bebf2637cf4c83714b6e84159b1090a6aed9e70126997c5f7ba51353af2b40c82bc82d5a4fad3ce816af7eb2dc860d933377dab949e7638b89180ca94402543ceccac9d2fe5565fe146bb057f918e99e300bdffa1c75160a702e06024dc21d0ce45c9074b52f209229c407eb93a4390212c042e083ef149dc86ac3b3f47aa6220ab306ec36d9e8d04559d6266e0da30cad8bdd5e82e909dd84aa96f21dfe17f2c933e91d6b3ec58c5f2a8ec3dbb3cc95d24a5c6081eaf4300cbf676cd2ae3d52e8e1310296d4f1e790a9190792c7e20cf71d6668af8673bbca71eb90d265e14e9b1a9c2f951609d054df42052e504371d6e5b8bc63283e704f22095df010d670298c1c01838781f4b0bb6944d223af27c403ce2ff7118c8660c9c2a3850f236937b608c1981b1c28878e0209e239efb179ab7be135f6b3ff1ae55439474bb4bde91092474f6ad0b728867000a7af1c2d8d5461748384b496dea6c440a98b3a5e12137c9fa4d657fbfd5223df41b443a183efbfcca6ab2fe3d72820a609135690fef9addd8110c50d202c0e2a34197b2d8bf56a386ed35b2e5c485d9fbd2030d1c126dc1f44a733608ef1bd05cb516484ecfaa84c5360ec3a875ce4867930a2e1f9698ee8489a0025e794826b67785c4a8c0e7a74fb91ad25107ba2fabdfac02fa2d8eef335948835c0be7d657174024b6d49ee3d493f1a8d9eefa565d9400bc48db345f3ed7d45658dfb3af2861ef57de65b70c1796ea2d4fee8c174af9cc07fecbf5b21b77b85354946147f065f7996b0e6649bbd6936c44c5a384d7f59c120e96d578e8e90e9bd0c7f98143fc5ebd70eb343eb46be395bfccca3626300a293bd982b54db6b5142a4ab29577f861313400d585439624816023f27a1d5709278840f33c9cb9c252fee45782833b650396cab70ab6cd14f929c2aa2560a6352d658509cb51352896cb6ed844222c4e81a282131ae57d0409d9c80e6af7b9ab98314ef48001eef0c54b1458f3cdffb1862834b2b74cf857f0ba6012bda370601946dfe4543f2ed43b83517647e6809fb00d9df33bf2a03af76b1964f88eee8af6fa375cd7ab318c7142e7148cb8caa5d65d63db102f94bf0e36aa6fd75f4bc5473d43fdf55e90615001643aaa923a4da6a4427330a0e4f7916debaf4593c8a5804efc5981dd261e31a532583dd0435db27ba00b15237edc85f185aa503e64882e39f46b92511728859007b8b4089c52526292fcdc18a7c7d9d0f5c3436ff20b73b821d57786cc6dd8d9b1ff0c536efaae59903069c1d8f5530fc7f36647a7b5820c4c331721e650ec4986d7542b21794b40d849e916e0cce8ff0fc23f322de0969fd884c82cece76c328bf3da101ffdf79730db19c0547c8bfbdf7497e55b5099db26219d6d6f671b47e979169881102d0e030d2e2e2062ab33ccf1c593cc92a490626cb1fedf22b36f342828a34587078ced49c74ad0cec41ab662a3a8890a320d28003e0dab0eae58636556518360705234c5c5fbffd9613ed9cd429d45067d972df9896781c322e51ffb856867a0f7e3dc9b1f10b6de11a8448f649484d8d4a443d5d545b2a1e9d5b6c1404ff63405be2c3365c58e251192bd405d47e7b2c74acc50d0839be442908bea0729bcb2a914339117b9f6ccb4e7d9a4b88ffabd507cc3691129e0b84f282d5624d5f17140216d6e057253c1bd0b331e06f0ee5bdc9063f486f90c2ae638282540b056f57268c0ecfb29907a1bc18ae7d529ba7b16a58a31adf004fae271646ed66534059dbde14581e7d519d9c5f5514eb9e4711a79dd23de2f5e9e99c051a641b036e76531cdaab56c82e1adae1f0f41bc21dc14b107bc2bb1c6b94429a81757cbe2dfc81807a011983037d17ceef7c8437881b3f69206570ab052a7c67937d52fd722c1fda3357998f10ad993ac8ee22426734156a241ae713f307e9592061d9ccdeac31c4a66bedeeeb159784242c91552b9fd530a9adc3e00e07e1c1222f4a2511fc1c14449b13f579087aac60d9d4201cab7ca66005f00546d97472670bd78c6048f8b8e85390a3cb7ed4337875101e173a011e73f387bb360b4e4cda17409df40b926885ea1dbcbf8878b51f4549b5c09d7031cf4601231cb4230a2ef8b45aec1a6b514c471a2637960b6cc2ec17302b003c13d5c8605ed33164a2dfdbc546f439dd6e8051c652b5b0fa5be46993218fb797b7a014be685b5ea5869908472f8d0091c358bf23b43590a84ede081c75c5568c57d4be917173a9f46821bd9f5d161cad5a7712da7122b526b13eb809c4a8758ae6b77bcc2d1cabd852d9c0f9f185f64590aae2374ec1a4a313d1dee9958737ef60d0bc685ef090e771bb7bdcee560a57dd3228a86a3fd6960a6b5506e1f72ed1eaf91105df59030552b03cd56ef35dd56ba151511760b9a80962fdcf09e4d4952b3145acf5082ed44f4241cc741f4959e84338022354ffe079c9e840f73d341a1aa6811eaa618f80ec21f721511c91a0b0da730139972048b538f53ba47a6f85b57d4cd4db9994e5c4c2f2fc3c43a05bb2612fc149ec5a8beb1077cd47f844a2de5eb53ca311eca39c711aee60c78f89b34289412857e6435c323408e42578200418a4040799f8bb6e334f1f96eb8d0aed30353fd2076123c8637ef28412e90285a0c7bce494fcb693b1eb2bf179c65631489ce7f39d05a89ac98c66928f1485109ca5e25c6b506e4c652be54431e091f9c44d3eb278ff5695947f1d2e56a351dfc89b3ee3ef8ee9f116097465330b702d3576c3657447be696bce7a71dbed735c907c530ce2f5ec5e0a3b3e34d731cd0aaac46dfc2194418f6b4dd908da5907e07a9810b4faf6e1b43c5b12bbdafa22e5e28651521e114af088c1c899c20937ccb8201e9bb9943e848a2198b6468c8f58ecd06cbc34ae946e15dc50ecbb60e6796ed39fac6d4bf87d7b3eb7cce58771f0d92813c0e6aab765910859b2f9de342e30d5d0658514a88822581c86f2a221b96a871613e9b3b663020f5e80a7b9e3f71dc28052c6b037a5256745a30561aa5031ae1de55a22e936e9d989509cdb30497cd06e9dcc166189616a0be4128b36d01bcffdf19988881201f5186c7cd7db4c7d7a18e7d82806aa1b2b967c17b2254e37adbdc678d1138b3259f4842f6de7b4b29a54c49a60507fb0629072f3918a091d8ab68a748a9b56f158a8d5f238c30c2285fce3861e3c32f11429fd165f418e375c4f6c718638cbf8575c4ce7d88cb1297a58c01257559ca183adcacd4eeee5356d831c00babaf1ac036f1a1b052c4f71bd6111f6bc2ba17bdcbd15e8c3350ac87432d3d3f7f66e2abc47da5bff8e2bcf6ea9bb81cedd554975dd7755dd7fb35755f91cb01195a551f489056f5d71fd2aaf9396eada95bffaba995c1b895b9dcfa38d7d7ab5ef5aa9fcae1e1f89a8b9b61c26aedf9b7e967a25853866198467acb99c03ecc75d96718fb2492b57f71a5ce5e6f323d8e5f5cc99a38edadf6258df4ed618fd33517eb1b39586f873d940519b71836b64723861708617745b967c6e4b7f3ab9db3b3da67c2f56b0cfba11cc7e7f07a15610ad16e8d3b0f5f769b514021e3cea094b2a01cb9483963e7ce1566d7a58bcb976f069b9b846355211ad937e026e0da5c58069debde6d98b6ff253be7201cb8c3a313c4ee50765422e33282f042872563118730a802c4ea37c2a241fcf3276be1a8e5697c4c9afaf5d16872b9fb523558fe2cde20f5fdc563f92377ed60f99d83572c7f73570a82e08c5818ed84e1e5c227a171200fd954ab785573c3dda7e219290cc1ca17222e633ef087cf5d0a0ac1da78f1dc451b5878f5f0d00f598d20d40082941c0d0b9235134d0b700616674bdcc94522ed43e6f6fcbe3d646ebfccedc7f1ee05fafdded1f468ef44d0f785f92c458c9d3b9d153999eb1c4bd9e2e4363744e6f2d3008d741bc1fc24bb2a9da552a984b2512fcfc602332bf91080c35a92524a8eed59c7762108317875751aa7943352774c0095c686dcdf5aad45a1ba9b8a71c1d8ee31566a66d879a1bcd00c1b2f34c3fa856f866dc8e4f24b33a6bb3333fb0b007e2199618cb131a79b2865949717163a33333373bc89d1ddbf58086f0ad98df0621d25a1890d61216ab55aadaf9ccd959c056e8c23b8f05d3e7725b701391d8b1e2fddc2b7f04bb7f02de816be856f11b12d9801d1218410cb328c0ba8955a1e77c50889a972468a65724619322239a393e29455867af413f9a8d5fa69fdb47e5ab0d5f1c2b8669bf9dab591289d72be67f772dfe2069286ed51ca44a36e9da239e77c7f03dcda9808babb95d3c0f518639de1caca3c721c0012a4c86fe458f89101174ec0518187e44cb869af1ff32ea200c56a426408d7cc0099556e4a9a1d8b5b15253dd5990b46cbfe67a056bab81b5e6dcf41dca294d64a6badb4565ab51da45bb337d9b818e5360d044a29a5d7557b547ad5cda1d7547b32f88af4fdec436b57928dc60505753e413e413e4130080605157563f20d709d6be1669cb38d7c6debf1d73f0bc1fdee3aa781bb6d3dfed2cf48901bbb1f0df7ea9a75c8f719b61cfa59965d6e55ed4833256d06bbda86183c6bf75dcfd7754de8a5272520d4784b4c2e8425a0f6e00dd904a2c6c7d8a1da73f778b9aac0e3049c2a1fe4d1710f82d5705b906993b491362ee6165a0b8315c3300cc3306cd29f35bbae2e87065427e31a0d6ef1db83d4f00eecb3873c5010e072079fa1b7693559077da4b0f0af97f2c49d0c05ecd67ef8cd4d2ae579ff35ca9d1c8475cc3aeac31cedb5e7e4ea79c6f5c587baeeee343d4ad7bf74bda3f1018ddc2524884f24de253ab1b36ec3d7dd7ba7c4351ac38f7998b79ffd052113df40fec44687ea6a7433325d4cf7d54afaaced3e8beab0ee09f427de4426c5bacd6c205842774ed448f00ed8391744b004189d3bdfb9e01d5d3c3a3a3a3a3aaa3fdfe7fb0ce6cfce8966a992ec86a2b58b3cac63fe115d2d6ace49d302fdedb76e862da3dc0c36d0277d1152472a429fd46d3f68f783dd0636a199444e1424c3aef9f546cbb0596f9ca7da7a7957a607d6898adce828ea384f0931bc0309fb3596c6e23c468cb0dcc77fe63b901b91a728decd4a73ce396badb50a15a0a00ebd60c2aff02d10664cc12e7c690514404e22fc0ab51f58a142baf09b6bb5f539865f218441455fdc8aaa42bc4bbbd9511ed979f7a16ec3af59d50a495364381e87f5e8b0b36e44c635193750030135500335506379ece3bb0f581571608fc75758e744b45e3972dccf9d6cb56ad63a11cf75a22d964ee78125d0ce89200272e01db3732e58471779a47c4e7461add6a250ffb1b1dc78d46ab55aad560bfaf8401fe8037d82c448e5cd55e99437d5a23ecaf480a8c8e828ea380fef70f2c4574658f13da8e52b0e8aef43efb3352f1e2cc1bb1e3e0c70fb3f9e82fd67c2652da68b930785c2d67e59af13f17cd5836dfa3d5f312b75bb593a5a066eda3105590c4fa45ee6e1ab46e1e2b07f203588af683afb16e66759f633d06f8eabdbc89f5cdcc24ad4953fb0cf9a51b012ebb227363ebbcd8ddb30053a0291714d06bfb1dbdd4004cc9f8ff10ed8eda8af657fc3256e88dbcc6eab55b33643a1b0ffeef36a1704d6c066720922e04fa19009b1dbc4eb5e5d5c62653fea077dde7ed49fad3b6fee8448d8efe6ce9b56f584512299b5eb03f1024040be822c1b03bc9f04f234acdc4f125d47b10e96acf96e51d5c6b84158ad45792cf18e7e7f3a46367d00dff45d96b8a627c5904349cfd1ad5c98dfb0e8e2c3d57e93d265ca3dfd73d02d7d46e24c5fab80d194b0f2e1fdf84fcf309db6f9806ffa6ceb717a53f791fef4bce59c7e522aecc741319fbd3f4e46e2e03d617e4ce56f25d94b2291be6e39a47aeabe6a69a9e34bfbd2ba14b4df52b097d451eea35f22d2f7f4d3d47d42aee9b43ddd2600b33d8e939edfc4bd40ea84c45c1a371ff0e99f389290181adaa1a0fdf67cb13ae79cdcd79f7d0cf742fff6bef9d07efb6cd366b0d8f92ff49ffeeb8779373de9e7c3705b8ffed3df52a9547adf724adc5f53467a7e6e8fa4d556919e495cbd6249ef75e3fa637cf46f343db637fdf6a6cf4c9cfd8c3798ee337261480fb79cd39bb8efd4a1c0aca3f4cc3a486f82e1ec675033d98ea647e9b52f915e237554eb30aec28f816fa4fd645cf3f94fca6d68a034a454c0c310ab89cde6118650440302bc001db1b819f0691712f10e8f757405aa46b908603ce1c38a1cbc459a5c5fbf080dfef4af4e8706ff9453a0592626d67db81b12f5c32268c4d3d390c5404e7cc541ad216f2cfd4dd4df4544aceeaeb119003f638d2f9203768258fee3d8518d31c6c830c02f177ac7e442246628c12e4bb112e5e26a0954e80ccd2cac6d2f6ba6a287e59d11d6ccc2c68e52b21a4e11850f47603c898227cb3dc0e5286e08ba475e7a2381763d3d51f0f073398a244c6e2e47b1c3960bb79c991a58be313563030b677aec7c1c98737661690ffb5aeb67240655213bc9a6ae18887550497b3f3336b07ca7252228cb5196cb528ea8700e2e4b39dab9657029a38bcb52cab0d23b5c9642c6949bf1963333c5f667a5191dd8ae31467b5d97fd138cbde216adb47fe24c70fa18f7d373cce9ad3d75a7770ea63b710cf327f9270ee64d8fd39576170edb7dfcb5e6ce6cbbae2be612b231c374bebdc3e77274aa496820ed415804d620b1f046efba3602d39814183dc6189bc866a600815062d725e5c54d5aebac9c3f9d2e6184de70c65e229bc9c18c90656c67c74b7bbd9384edffa097db3b124291711fa1c9b0318bbddae99ac0cf362d2ec02e01f31bd7547ac80ad7ad6e1e5f35d0c683f1384f5369a0d2639d771f0a4683a194cbe896436360300d3e8651ad72f0ce6eb10ef9df9b674e9d561ab78ea6625fb19fd1362ebec6c13c6f996b1784626db7629e72a7cf201679c59c5016626920d3cbcb54e10f9456d5eee1517bf2617b1242690fa7b9b48a2f974d834157befc121703bc5d94e8cb2d07deb0f105de1c0a61d4b69cdac9c7f16c4b01de21d98d6b95056be1d39bd2469b56ae2f047529b6e5d05a2bed62ac652133911e05cf223d7fb139d5eb1252add775d58edb667b120348684f9241047d78a4c955ff833e5c39a4e6684f5e5c6cf69f97ddb447bb1417092bbcf9e9cac510ef7743ff1e690dfec8b754a4a4ad56418f4c20fc696f46075b1ffaf0dd384844e5e08f13b65bddf2d58ddbc8ffaa454922a8fc236e957fdd5cdac11fd6217f1a5d6e43493613050c193ec4f884f58fd19f875a5b2d25d1a71ba5d4be0aada2cfa355f6e97b351f83a2bf4161fbed556bc5b427712620bdc95ad2b389f49a4622bde536fb24fba5d2e3b8e5365289c39eb4615acd2a76550a94718d667a98d293b89af6ae87e1bebdebabaf6ce7b9cdf557874161ebd38f8f32e1527d5e8c0ad7f55ecdb78aefd57f5d8fa355dc8c141b614e303027181818989aab7b818798cc9797d38f94fbbc1ba3e4d1e1b4e7272861fb2fed6e52ed45618bb4eab3914853cea3ff94f3680a35f12d0ca53bbbffa80d1fefa4a8f6fc8456d52bab17d6d15c95be917b8d53da3e3e1b236324026f7cb9bd5f625815a527f0681595f4fd81b48a86564c0556c78c2331c62864a3bb7bc79a7846c635992c921a733307bbaf76c3ac8b113e7139242594549b79c2f2bd7c7c7c7ca44fcbc7a75b41509ea4b2e81bec6050507429af0e06b517ffc98d953a8441ad6238bb5414ebffc116467fa855f4e5f5f1b1da753aedca4cb79c6ea160ec9ff6a212e8a30d616b7b7dcdae8b0f5bedc59fd1c1ca06c23aa251e90a193b527aec18a37429a594524629a594524af9d81336be8c4c8424e31e036634f66a677b143c4b7be634fad09b2ed1b86cdb9693fdb6e574ad30cbe096835d5b0ef61786f99693fdc51191a1c836c409deb0efedf2eb371e7cb18ec6dfbb1b39d81a0466cba91d07da8bd7895b9081e078c6f1f0d57fd4b8ad6b02ffbab2ebbaaeebeab48ae335c8fde80b41ddab29a53ee4bdb48e6fe067423feca2b5861bec505cecf743a21191567112fb710bf250e1070bade2f63abe688c4e58ff2e138236b302fbd9ab659aa6759a1536c29f8962b3f747c5bfde5e0f35ad8b32c6581fe34c80fda669d8f3867dad18f61a777518975dd8356ba471cae8b1092bcfa5d059d6e18f3561fb3905f910a23a45b03f7878d18ddf58ec0799e8b4398ea25aa91685759b8bcb6274196364960e35d8edee32bafcd16a920c6a57e4c8118506f6878c6b8468972347e47a754e48173da52ccb60fb33f739f412ccc020b2f203f290093739600d735d7df6ccdfa117414eb0597f4ea11b822e7bb92c74821e6e9d4b60788bc17ee9719ad212c7b796669cd8d2d7fa2514e413e95b2a1111822a7d2d55dbaafa5aaf22ab769f10d4b56ffa4cbb97762fec868d2f5b8e256d3e32d35f68dcfad7576e89bd84a06ee94d3de6b59f9134b8ad40b16ec9e949a78e0614ab49fde6cd47fdd267a692bca6528e083a60e496ea9bb8afca5bbf245bd52b98af5f6d35bd10d4bdb61c539749adc45d6f2d57b3a999b8128742fd1d9847c1b360ba26f56fd8277db66d39248dc4a4ee0b825a62e7b2100d8edcd4652125b2dccc35dbf598a6af1c89a3a90f6f7d188ea6761a0ba73121fb554d2b71f05a4b2271f56d0501bc58174be47ef68568975432d96863be98cba40e0d5b5b85fd7ccd6275c6dc89ba31adc2d1aad26b4734adadb59f025fdbd198fe05fbf56d57dff4d81bb91747e23e229764e2ece6244e8876b70ca7515917db631b5f6c7dfad715abd6252e7a28d281c9099010610b162c6a43152c18172ea094522a191635cccb422740620446e5d2cc1c601be6a05aad45a1264f9eb3e67277f7ec3927ecbc6377dc41d3e0e5f20edcb9ecee947a7bc9aa3cb1eda0066951965a5a854a86450dd6c65c97854eb0733ffbb1283ecbafbebac0f58d752c5c45faafae56e78ccbd136f1adaf30ef7acce53f722483d25d051c54c0706020adeabe40381064480c2a07ca06971e49346f7c69e4abfe288f6e7c1eee3691d2500a243e07e20759dd10f2c35ea00f74cad82cb8f153fc917065cf1ed9d48a923be78125f29770bd5dfbb576015ea07558dce9c4e716e01796ec746247d3022b76459a54805933d0c05db0e2c38f9d0e77c1da5a80cf5d5c7884f885edcfe0f3f3439a1ef3e3cf4f42c965a1248edc035c169201d1adad4ac2e8fe65a124866e1633dfa42627179f4a806fa412e03b2bacffed60d983003ee6f7578e6b4e5e587333082f4d8fbefd22e88091cb17a727305f981f2777b3a8fd177f72fc78fdacb992e39f1cf691a3d431ee9390fbe2779cf523379f72cd85ac6952d817fc06567295bbb8d1cb914512388bf70356306aa2e88c2c2624a3555faa8bfd78a8a80e71e1c717f64ba54eb88131c49b4a55ebf5d017ec608c1e184d9c1b1770866e62628c71c61967c402f610a04fa0ebdc85d7a545b58cbdad8e75cde2025f72104208218410c61863ec48bbd968c8518b52613fa83395b4eabb891fb9f619349b14848c6b5859c6db05aeaf5d11ec0b411016c4427dac2b92d5de588aedff206c36913e1234571fc7e10af1196ea9ab59a9d63c44577ee6cc4393087ef6f2c868165d39b1649f316476d212c8a5dc467610bec79a3ebe7221faf0d0a9ffcd27f30994a12b7f164d2ebde27ee6942756aeb413cb9544d7480561dc6f8271e563579d3e3f2d20257232b972ea38e11d3357be0cbe822fdf055f55e6c95856f4a37fd69a8d94708747c7e789ce94567d42a693fb0ea51f409f044d1fdec13bf20676b67c550570e5cf205f390b8b749ff088e4b101ea449d9d5e4d286d23ffd3ee077562379fb4278bec37e713b9823a5f952172ed4a26bf7ec6ac2c6b6d6642412c71afd73e9b4b2ae933a644e2a573077a6342ab5ed0fefa7e2d85ecb5a79cf67d339d949baf5dd86b9ce48ac8b75b059855832cb7d74fd302cbe767be14eb88c47bfdac34f4e3a53f2b4bdb6279e0547f9616a99c41165f3e5766fecac1c0af4161bfd6ae4d29a544e4676f7dc9c1c029afbda649ec9020ed696137544c7b192629334ba6dd37e4a79ccf0bb0d5575af6a7d3e9f4339f9db8e7505bece93fd44da1abe9f9349f16812ced65f095f67db29425a4bd1e52e59105e282d6f54ddf3798ac7e197c05f35de3dbb76ddb1ed52a1b28af675ee64f9ccc49e6749ae13e22f1cefc0d99e72d476646e66f7030f0ccf39633d3bd4c87b2717aec847a0c75c58be2687c5c7ffa53f782fdab05ff1add8f22f1536ed39e0ccc7fde4cd16d55ccb77c69b758195357db86c670a797dc672b0c677a533743c4714bfa98713aa2707cbf6f13a05fbe7cd972eb44712288b7762fd04e6a5196b091066d24b61178d92c2065c2a46da09452aa5345c95584900ece0863072a5ca4e0e2355431e2aa9244121dd7b9df4d0db146a113d8904568e78b0e294f14b1451843280103567c1ed15e16da09e3962e0bed106d8912254a971b325d64bad43869a971d272d2420425acadd2c9c2b5a53feca473bc71fd5326f48a01415a1a508270bc47028c41adfa647fb98e5dde7cb457b7fed2c7db07c5eeee32a8c6e78afaea5d4aa743c798686966de6280d78775c08f60d80fba9075400ff5b0bb99dda1945d111a58b18b5bacb0a2c649cb494b1a3c3764bac8749146fc7dfd637e6e8deb8fb35a114941a3e793403f4c18a101c5fd78ca75094bd7df3b8debd865d29e03b5e7ef443a010ab20452720edc7cc0ef238a80a8b8709b80774d2cec640e2cbf07f1c31c65f3b017780486d151174456b010516941096a5d5f82880b819a6b863d31461382ba93f3e9e9d1b91fec81476ef75489eeeeec5ee3ccb263b7d39e2d1b5e3fa1617784dd9c8d11638c1e63843eff6366f6f8c5bf8a504dc8c0f58f1ccca2e62a82775c792c7cf73803e85dab9c54461a83163282408b186324c09518b8ee49f9304a9f3fd0bd1b3017660edfa774395512aeea73fb47ca36fd0d848d1c0934b50268adcdc08db1a1070d6d4975aa8d90b841160b80cb4248281103091b482cb03197858e308210ec8dcb424758d952d35e772fc9d259f70782bcc4a0cb0ac40032623992228870bbbbaff0a00833ae34a28925de9f165303032e574c21444495228ab83006163ebc10ca1d8e0a2aaaa882bb3cb7991f8e8e73b666c56acda01872c14fab66188f1f73c70a10eb1306085cf859e420273f46dd31ce239183d00808a9586f8e23f658ddba332e487b3c4fea7850d7ab5536c461cec9908775cc20edcdf93cb0f4dbe341e204f2f08ee71d0e8b8042dc097fb8f3b35510d0932865180279e011f864d5001de843ab58e8082278422877cef9d483783a37ee7c195a457f671eb91f73c131e14e0fd9db8c34033c847e94b8f3b5560de0cef73ccfbbd33ab9e0490856b48782a92e324ce5ca18589e5821c60d5e58adc30bc280b1a15bc0c510fc42a80540ed49e003806601f7ab19828969082786f8c1ed21c8f01a5e21c4157d95512b934e09117c64cb6db8faf45024840d498e5a42f0d083194210a1a40b8f10412af8a3a3994d791c48016589bdaad85c60672e0bd5c093aa0107524054534a5b5896e880095bba2ca4848934942cb1c1c25c1652f2431221253c4a7458c08396acb6ca56f718a37bb7a3628c54898f1f63aceeeeeeee3e49342c4c567fdf25dd159981c5590ab2daaadaddcdb015abeb7064e3c68f47b4dc58631955c4806268092b8a86c8618c2b6a3eb058dcbefd5d6b8c110d237e8c10c517317290032be260c446809f245a3aac569d8298229da8351281c3130fa2c90d764b9145d918fb530a8182a82a62108312d241e20a2baed891c2fb8c73ba900120ae8021170ae2c8f5677719bfffe1ab761e1ebfe1c278c5f6fb7bbeb238aed879f91d765508f6fb1b3f035ebbb0df5fcfe28a2bf88a6df98d5db1f0d2b8df98a0aa71e5076915af562b00e34aef02e49028b81f09b700bc23fe8d1d96e993935f79070eeb902fffe2d5651bf9f2c7ec6edca6eed8cfbb52074629326f2dfbc2c2e7a85561e1bb73ec28032fa49472034cdd3b960c1d42e8f013c2ccccec1cdda38c57c02df6b4e703ecb9a0b4ceed0a81e095573b541296b90538aca30b0b816efcc0f8ac60cf8ed9e31cb9bb52030f9b63ece92ea594524a29e5e46070da52d21e169ebbd7037da04e709430ead423dca49ee111d90e04820938058b9438aeacb9523e2f71a5943e185df9c31357fe27b9f23da9822030abaa4ecc752fc0fd725cf7afd608d71fe5423dc8e0ba0f365c4f4193ebcc2a226841c5d36de8d5f6011a44a754ca6b0f7aa9c8831337461eb4dcf81b0a260d2b196b56b36245cdedcf5123090f49bcb8fddfc3c4edf77848b24397db9f6d391dcfc022a71dc90e4528c0a2e1230355350660871b6eb47fe32bd46b415390d1dd7dfde03f7570e3c7d8699c51060e4058f1c312203f0110b1bb6d1c001218dcd82587189d05a1d144ab56b3aa2c03f88101aa0de3f6a37670fb5f480723dc163ad2ba3d83db36061595ca6aad4656bb2c84c3119f1e6a108a30fb314801b5281b836a5dd5891d26173e75074148222a445688b250941748621078260e62e0e03ef22d0e6724e9f7bec6db22dacfa107a805910be520840f660001c8e789bc2c04c494594a23d52922be3871c5972ac8f882e5c9172e22b0821e20a61002420a00dc7859c827e87e9fd2224a952c51a240d8d31efce9037d7a4e44d18afdfec2b6e197af6961218c3db047cef9df759fa5dd9c5eae5cf9b1abac8367bc028b962f82a0e50b2d53da6bd88241eda5523445698a84ae71bb770cb5108f97dbddd1e10f9cd22a38c50afe637f60a05feeeceb67e4cacc1777d31e17f64b41e69fcc83fdd4843f5460b4be4a40a75847472dd6bb20347c52970d57aefc3f951d5c396163f07851ab1a7aabd8bd449d8c65f6eeeeee3ded451f6eec9df64c9c094c0f03a1e9d9f4b59a3ad343ce3b138426533c7263ebb8cc6a77d55eb6acb2ca2aa594dfd815f6ba3cb31bc0ebeec48b7807c73bfa4614cadacd09cfcbf800865e50042404831a5c167ac1d18def4c20701d70a1b81b755e3485f59d96af7e003da1c3323db362cb6d224d0ba6877998ae88919f212f3a2c53075b9994a931332453a3868c17e0e705539c15bfc68ccc10162227565ae53b597c8508af7652ffb5a1e0b73b29ece745b7c80b0f173f6a95ccc7af9cef749e85732cedd1d4e8450e46bc71e3479ed81395c48f44f48a002ce4022e37362b7e75273b4f5aa5f29d1bdfa1b4ca4db04204e21d5e656add9d5b696f088cf6945837eabca8bda11b9f870dbae18b1eb2743b8faf623ec29c7c27a63be140c1c4d81a496fb92f66e3be88e4c69a0b9f7adb43a2de69157cf873996c60ff3d6cf9468b7e7ce54388620315e3113a847371a3f69cdc188152375ebdfb79512c2a7230629ff4a8e48d9754350b118d0000001000a314002028100a0704c270482c22d4a55dee14000d7e98427c54188cd324c9619842c8204200008010202020224034da06013ab6791649bdde8bc726ff07a33daf2d5f27f555b180c75706989bc85753fed3970b4b101563ee722ba8c5584b0b1af3be4ef429d3a785b73d046d8e419990e8997b1896290e4abcaa40b6f3425f370d9106d843f6291e1f33f95c353f1f1eac4b2622826c0492726f0d69539a728d80c86e629f440251aac0161d2fde3f20ee99104ef9bf1e219f077f22d1e48c8b3d816872e7543c5a7686d9e8f6008d3d402b1a9f0496934bdc9deef362d74d51985fae1e6ffc07936c703c8d58fe7b39e69298c7b32fb1ed13c3861c1060014cc722938d6c582a67799186b9be9e75ca031ada3a7da012492df292d1245ce35d0d45d7494ac2c294af6c3fdaaf452f011ff09f7b37053e1c8a075dc4cb802f7930c1a3bdc957e2a4ad2e228bb0b9d2e6eccdd6e76d1c14bf73bc7ad3092c4ccec19da2d5ca9f549b575b7b7174e4551d7dc30952da720a9d726a29472ab5fda0f48ce0be043c449d6e03158f278e74b63dd0ae1638972b10962be00450b17fa7d64eabe5a3d582f210594baa63951363f6a6b63d820d228196b7be0d0723a80e869ba6ff11022c41a56f50266bfe0bcbb195962d89b13ab6f9b7209a0ade4b51630b4af52f6a5569b11f0ea7e02f0f9ad24c0a4ab2c54dba51214e26f013807fd45748860e2eee5ec21542c3f1f55e5d2d67b0b343cfeeaf334515bf149455512a93c7b172300f3c3bd7725962778beae059b3e24db837e8123802aedb1371c45602abcefea6813de0fe0dd4dd74467822e519b9f79c515246edad8aa49ca1a4875daac6b53c8d15d3e282cd4233d74ab386b2987fa0ff4fa466fbc944681f0583938333b4900b1bb389b251b02867335b050f7f827d0592a73855b5bbec882fce3ff57ab822a344ce55a02ad72fb3f9ba67c3dfc944e25dfd67198f7eb49ae12d728107b425fe1e05dd93f55b29eb36a3c777ab23eaeda0e94c107d525d320868cc591ba7501779feacbba9bc1f810f17dde8387151ed9e3e221b536c9da11f99bd05d2577e03314fe9ed806fb9de8b8fe3244377d1943dc35526579c898a389b287eb65cf998a3862018c3c03871e2639547f514e7f0beb0919aa95e3a9ebadeded9c755809db32b7a4d12566adf8bf6b07fa3db07804d8133d8948409e8a86952b36ae61389f3620f41eec30fa105329b061840b6a5977ae8d785ba3bbd2c7ec7d4187c561af54aac02f1d56362ed113a03db7e3dd499058656f461f895735a705e8e09e76dccc78741255653fbe8adcedce281f70f50914b3f7b5f3b9a2053ad6be7266d7f8cfb10d2a4895983797bae1148ffbc199b5f9f680462b4a8136d8a5d406e7a7794092f75498b288ea223a271fe770b069a64c2a7bb9e8e85c69dced6cde3671ad07b0fdc5ff467425ba60691a9afdb4136984d3c985877c2da491a1e489109aaf84da9835b6037ac217947cf2228ebba954a33bd1102caecd07fac8ed8207e6f7dfa1af54263656ad10377880951b09e68291ad85892f86fafef063e3ccace2d6481ab1b745650b75a81a745e927ac2895006a8cf47bc602bfb0b3e3cc30c17a757db15e9f42e4fe94283c0900ca2e9acd5500e12b49a431fdcda3802908bb43269066b9ecbfeca6a70673d3a79e2138803799c2c33c6d71d79fcaa2cf2d6533c0f5ba1605279d14863506138eb8a2c5265e3d0cdeafc9a5ebf8bc82895e98f58316977f2646cb01d2a60ab9ac2637332951e0d75b9c42cfba9d73ecac829db5243481c31556540dacff1d090c138dfff58100b1ce0d04c6d2d4bcb61872db652151d52e7e6d77625828c1ddd1ace6ad61c6dabc77e06af56ad0c1c5f162d73126da77caf9c509af6cad45d0f305bd7bb432b876a0447b8c6787a0f02a27b8aa8398258c4c2e7822e043fa3bc81eb40a23f789039023ecda5984ca5a096468dda0c8f212c2638c63f1816c9ba8cda483cfe948a40869b902ec09b582e057167ee8df93bc0ce6fa7c3832806257c0716410655761a37b42d00dec68c4316bf507450aa32d1e00887571179ca381b20e6f077a6bc965b89c79467cc5c59d9ec75df1b39712080b41a3fb1c4ea1670ae3980df77656f8448a3c10c5c925fa5a5c7bacc48630a8f5b030f9a032022014279b00e679171015ce3ebb9d9793530ac39806cb9a53d0709f372056b9710117ae74d16a97f1256239c1f8986769562aaec48ebe4123c7fa03a5fd83009bd28c327384d507b6c0cda71dd8e308a3b7d007a8e083c4fcfe92383e5ad681118f6c69789fc29ab0b62efba636c7be9e30e38b57e85674775b25c46bc4da81a66ddc58f31675cba9f45c13e0b845e27cc1f370ac019b492a9fb49adc522cfaadf0a347f50f0bcfa6ee6ddd18277a1642475af6fb211bcca33158cf92023cd21e341a5137f4ab0c3273208c067d1953847568ebd74fae815c71e73bc7f491438d5577383b052e3ef78fb6df860b54038b65c88fda2b0e3df8875702045227d09bac1bfb28cecd6ce0bee1cb4d091ccffe36325c72c7ee2bef4882752358e940a47476387df813ded8749a0d02ad5ffd56b5faeb79fe1f94225d29746029a69742c152562e1712a8947c71dee4285fbdc1f1ad972c38b95c3aa3509e743ea5fd7fc0a0599f80ee77a06270be37c575b5faf8bd33a548e616952a70711ea633c518029d6d2dcff4d97b5dc2066b2769c90b29fe37feca9b16fd0c2f71f7a9232f880869bfa2022202897f5b117e477c6b4f6e6940a975ba6c205a55f2eb9b576042d66444adcef63478633481e9d5aa86585f4d2455c1ba06f813c7895d115d1df218011d9f2f8b859efebd0e77510f773584a45d7e4985778e035da8acadb5dd90380b551bfd81ec0a61f2d07d253cd9d0419067948aa0949727df434d657ae13a056da6fe398c011697b84a1aa922d3fa9878cb2c92321d04dccba9108311ee4a2755c5b4a45fc55ca627c287216428dd269df6188c0f2d7b8ce23c0200b37b22b997179ac3c757b96812f936ec84e05bc442c33808da84673debf33ed1b8a9c0e81260717763bc66d404806e9883b7d91df3b2f2fd07aae8525d566a1bd1c682a62e44e67248ca83336c446118013378ced6abfda6bf10b5d4f10794a75ab8b2d25243dff7c26a90e4b286944e61b1610d551e8d9dddf91eee9f2830baddf49e56b9ca37c67a93c402840fc52251c8c5c2f2386a075b6e644bbfafc1db393a59c9495ef8a66d963e6e84ec8294904c109762e1b374cbd354cd169280b16ced44565f05d5ddf42628506163da6c3002ff4f26bb45ad27227c825c997ad7f781956ba27c67c7359b92fb8e7cecb0db5663c2349eaf66b3a95114b0616509ecd9a66452e7c4bdb5defaa9ffd801230b3e205a57ef7b8cf888b6609e89963b38bd08ef4665b498f2efc91ec995f874597840184cda3e6db7caacfe88c52109a3c521332c6d021a0a2a3cc453e1114002d9541b1086b7905cdede7c7ad710af81e171dcc79d630095b3df055da546b95a06bd2fb5014424ccebd537717f2af723c38db06df884951117d01ee09e9285a7376cda24a1830ea92c26254da908b70146f712ec31342e08539a1b08fbbf8791df074a7efea292968652f4c7f7cec0c6a35a1fdb6a7bf50bf051498df3ed3d70b581751f07c0d9441077c0cf3850ef046d4b6941618d6054d3deedbad2fc4da5e020cd0017b6a10eaeb12145a5045f5aa4fd9fe0be3fcaf3d64a814b2bce9101f3a9c671ed1fded4729c6dea56a95c01057ab74550c8cc6c1adc9bc8f096f6c35320b098141f9f09edea33dd1992419e85c02f0a39d0bcba2b130bfe71b8d0d4e0c5c4936622bf7629ca1694ee483f94235bef543a20357833c7420ebb4a2b3c00678fddce9cd8012e2788c8018a1e6706272329173554a8a3982397b8111c98f3a8f03dfbb6cb9de9687831768ab3030370aad13a45253efa38f846215173e7ab39fd4f7a803b272511ea4806acbcb209320f117aa06ae36fc255d6ce7219981765186473e2a19b97643a10c84e0b0077e5d838d82089d6caa59f88dab7cb26b82e4ac7c6bb022c99059f9ce5dff41b9df8a5904ede4cd2ac89605cf350e9ac51fb53935aba9225c045a14c319d2c2210605a3e503618802ceddff33417e0769a09848dccf92007da94a85125be1fb1d09b4130f129ad6ccbb681e0baa3e287eeae6d292337b3bd42d29844c2967daa4d49a3a9ac544e84927cc9d2611c0a4da00d7ac7397b10bc2488ff30ce35324b5b8980f42206a44bd10fff5d21dad828f78048afb59d5b93ca8e4ae2613608b8d651d662c6a5a60b5564bf8e0650df4a3b1e2d77c9f6d9a6163afc729bb19de981c0f76e1e69199cd78aee47e55413c0c049d05d73aa50a85b748fad7607523eed95b25e67abc00e1efae080d13298a17cffe7952691996c9f4af7d3e0b4c68c4bb40e9aa6a904e6b5873b4d8f7108a620832f4fef14c80e87c9184b472a3ad4b680bb3575570f8d03b3360d2a9c2bc7ebf27f008e22f8ccfbce786c30bbaa2b5eeeafe153366d37188103bb513097287a97c8c75897010cb48d95e490d436c87af890f3d93e46f13f6b8a00d0a08f026d8bc2088deb27cf0cabe4c042284c32aeb58800dd51a8e386c5d202305c4c59c9b80c05a26f927139f677afd1e5f7f20cc88c3372124a5417e372d95e7329dac7f8549ad05abc7dd033da42674b97c67e0c9fcc1550cbe420843d4feb2dca1e74068d1b9d00c487549f8fa5133e2eb32217035dff815bd904b07e70c9fc8b15e38ed452a927a94ce05450dca799112bf819f566727605d167c794139e4bcaf6f35a6a564fdd481d067cb4c481b5d1a28732062097bd751e0003fed8b6162bcc5d12231d0aa88cb096d5e8af52784ea692ad8114ca97bd30ba9675ccdec2afcd136ed684233ada28d63d233fe9cd658dc502d97d4ae5255529ec253a2e9e13eaaffabb21cf174bf57eac1158a7765ba94bd762e93bd8b513afe9dafd16a40aac94abdcab9a7bd80c9b91ac4c6097d4dcbabbd2b67a93b85bd19b69625f74d8ffe44ad1a7c33afc58eadbcd9bb13ed27c0e69559a0a4520f2ca09bdfc4449dc2b447162c9c06bbd3fb5ce00cfb8adae365d77c5d1f0457ebf7f4409ba1595311ef18415570f00ff6af7c190db85f68da0bace1951c2ed3c831529e6cedbea969da2a61d1fd6ed391d7f89afb2bef1a5a266aa0de32739e07cd6f83d0b575016c857c9d18686245929373ec44a570097cd47c858fa2ce1da8ab876578f032d1ffc715f8d64e32a4bed5bd87553d23d832e705cfc5e3065eece938a15252d39831ddde80e19a47b899d6979fba14bec1ae39870773ed2da65b5750467bea248fddc405b64275811d9be39d112c862025d8da5453db4944c806bd426e23cbc22786b92ac8fbf8233f9e869067a5df02d1a2c33e0787d5659d132911dd1ff0064d72aa68d5f01aeed8272de85052a12535b854bbba8ac076a67e633c72b82636ae09389d4d28447fc0ee8968781e91bea30ea531d0e68fd6ddd65672a58e61d2afcc35077c6fb3472faf9224c890119ed31cf54bc0e1dd62244b66c79c4bbba0b37037533d2b48a1fd1002de1b6421d02ad7ad4bd34a897e9e12475bb8a68fc929e95bf42335e32e51df8286a610101d4f69278fe22b50506aefb11ebb675c933c14d83d8e6e040fa96398b56113e3d6175f329f1191f97dce2e8edc08bb5a174675eb06af4e5b2c04693259c4fc2f9ee9bf22200287728a71e41ffeafbba2f64bcd8de028bf510e6fe0a300904c3b4e89ac4c783c5a052f64586e6c34c6fc8dc608b1c797ef067ad46736a3faba12aafc0b82b7a932e51d3b49fb290e4d8ef3e074675f4372db0a4840c3ea09cf6b3ddb52c7c60206eca25bcccc8c344022eb157b6f612a4e9c30256b220a7902323641441b2c0a0c8ddefe8adee0c3013fd908ba0b80bbb694c05c212a23100e4408151979af39a878c6dbcb56ca66a4ab18e84f062acf2dba65eaafef117a7e90fe2c700ad90e75cbd21a8859929e06721cc14dfb532909bf12927b926cf5921c696e9797a1f12af24bce9e94d1d4b3d241095384d6d29043c7dd5b01c642ff588d541ad139081f2504a6ed474e23fe31b05e0a4de9865a92de16d7d34d23b623a2bf21f55b52c8b6c4531057ea4daccdcd04dfd3c4dcc26da6e8f03408b8c457494465b9f13400323b3a2b3637919ce9cefa5bb5ad683fed9ca565bc1b3f098c3446354ccba054b4967c922ed28a282c70e3cf0e51fa14808796022c0b691fc821efc350763ceeb3a018b3dae5cc51e93eaaf1664e3e0695f171f7ad4fd383fa73e6803eb8e65008b4c7aea11d00bab10f96758d9e4a42050c0483d54c19fd51241fa7b8e7722ee58708d8385f2497a9a9381b6d82d4928e8ecbf5cda08bbb846c5bc2c091a82a772fd91b44368d0f38ada0df7091a0a251bb5816b0ff0531537e9a83835f1a92575f3646a1e801114de589ff31468de968f42f17d4edb3e7cca422589c386892fc0064252a0ede23e240f8d1f6fc1233dc2ddcf075289fe61badee4f6d29b6f8fb4596c8646e3c0d89c4161d888516c7a66aae2e5852cc8a25836e2e13d7f0df2383c95ab8e51d9adcd2c951a8fa0dae1e592da472924c7d454f46f6776701b8dddbcd4b9ba4319190276e8ecfd27820e10dcd50ff508cebe89ea80793bc625a4a9d3fac1c686d6f0d4f9573bd8fc56843408432ebe5ceba3ba0f4b7404040a7aa288d55e5a3a057235129c04ddbcd0e5b6e0a7fffd98aafdd290c9973e74996522736e043e0607709be0b4c806036070dc2ac9c87ee56f631d09f76fabe979edfa66fa9c69f41fd40ed06cb2771115af9ed4424ce7c423c2f2f593dddaba6f8dc24abccaa6bd9afb3ba18d6ed9b44eb0c833a39d653c023492fbdb8001adb25405c75cb4b17eab0242722c297a3bf33abfb9dfb025b2bc0d2162be1d30859759500a3589c1b4847da1e50e91e8360c457d893549e120d6e42182db3e96b9107fcdcafbebf8073b5d6a276616836ea3d0e69f06eddbd511a155ccf1fda2f64cfc680d05bb231cad91e054690806ae417c00cde35fa0a99811b3f1ebdfdf5f87a98caa21e84416d0947f250a72482719276106c967d7e51958dc9e4e22bc100dfea214ce8ed13b1b7e225b6ffabf7798fdafe3e79af53ccfa1ab38024b332926b47e5e26b178d1263ab26829a0f1116c25538be48996bf35d290483386e40ff56965e73ac50583f077fee3b56e366d3e65ef8e696e015e651ec9682edd97f6d05dc361b6cd5114ef230e00f52952ff55abf60e6286023fdcbaacb7358dd93078fd0ba42af6da5c57dbe48b4f585fed7f0fcda34a496e02572581843cafaed265b578dccb48531ee8d99feb0f228f6e68fd61d3e3d3041e7122d73eec1a7a4f87f9e1c6001fdcf56f9f6bd79b19a20b54029ef2d6b73204f08910aa9e6b4e0768a989481180738df24e368b30f8ac15cf42c183b1a3a453a0e466333400fbc5135cad5d68d02c40b59ea1579d6b072c330524fdce5095a4364f1f64ad0fb8add2b29ea93b80381de27503fa8f701a42f0bf06915175a8680df4d33e35c234e78c0dd37e74f5ae72524490f697ff5df4d69ef3594613065d37cb50f11baaeaf36709a7d023983b5881daf4638f6efe2cef283e40ea9cdef35c50a94212c680e8d9deb36b3d87135dac5678439a92dd222288b33771486467e8dbc7364221dafd939978fe9ab720daac1c152563a43d71e76970e718dbb26e4e24170e9547824d7a6c491da30816d1cf3a63f106d083ae2db1e65db8326b4b78101c0ca616695c7cf37e6ac7c25719ceb605f6a57ac6c733d7171bf1ed5e1fc417e320241439c3341e43faf58342201b5885c6ae49425b4ac4c7afcee4aa0e8b56a54b2da91d9e25c9359ef61010c754ff0b9368298c46525c887f24ffa8ca7e2c8665fb600e6840caf3d019adfab82f66612c6b94ffcdb509a4deb4753254f244e4ffcba10b0a804fa14de31fe4f788ee1c9bd293a786cd2adcef9ac88aa59636a6c2abecdded71909d388aa5d54febdeb835a1436016f16ce2dc9d818cb9610266b65984dce35ce45c113bdb2072b9e2be75344ee52304927102c4bb860168c014c11d4e68e63b2a7edd7947a28e2ecc843ccf40e9214a42501690114b0a494c77190b5f20620bbdd08faf48a0773f4f051352eb645c5ebb09480b7dd3b2040970b7e4f62ea6ea8da1efb6fafe437486802a4023c35ad1c01527e528b9519878bc0f03f2088d7a7e98ec85d1ba10a2c38a92e0e4bc02d34ae5fd8469cbdb299c1f5ef8c48ae14ede6792a45079010eb5164cb9e5c22cd742339b2cdca8d50f8209cd7b2b57f6d6aea31844084da50cda92d0fbbfd5248d78d2bf366d85ec238e4307b8e901d210934612e90135cb59f0b109996823519051e0b87d99f46aa421c8ba2900ef8bf65141d99eec9c70db869e244a022b8890d0c404506de82ba778b9a811bbf4954ee6d41a96800b15182be3080e3914fa85e62b56d7e67faf0ed56fa44a6aefcaf41a5cf11e3716e74491857f8e2b06b8601b5713570c618566e7f8b4e390597962cca4e224c92ff412b71c129630573005b99d1139c5f4c652905b5b21c19ba1b7cbfca4077152195abdc116f66f20dfeb50ee6022932ff361166d6ed4a0efb46cac887b3283baf85a08294747119dabe98e5fcd4047da1be01b8283af2da57952aec2082c1cac7380fcebd3f1625417498b6bb3258e7f6c1d95bb8c7a9c130d69506aee2f5d4577ff2476e38c4b27700052ddfda4d761a767ec3fec366a6e6b5ee2ba3be78b682741d3cde6a7125ffb435221da98a7985e00733858445acc90e7b2a97be3713af75f6e1f5efedee8fcbb248813a37f86253e49245bfb6aa3c4fe495d58fe9b69b9f743339572d25a1007ee820442f20d4a7aa01c608bbaca321b9b202120a910af7e87406bc3705b4a0190b6028fb52c5c8cf8bb76b5a5ac00411ed1603fda16321f6bab11440cd291d3d542a86eea4e9af49301b736f0e654d465d7a3b425742dfcd0b3fd2fc67689ef0569a66f2e7756b8b32e3ae82d590008833e007aae178b93b3a68efbc96f95238d276bed03521625b628180ac8862beca86d99b336e4ceca48697fb2a08eb421365a80958f676dcc013ded75aa113d4e1e9a7be7204bc591c5a705db36cd9954d12ce6dbaa90203d5da1cbab0f4c57eb1efcb8a755d088646378720eb6e6f506bfa65169556d18bc4afba6a706f2abef8f3997ab5a6ea96b44cfd0400ffc6b3703c4d067f08a7e240f9d011244da30848a03ba7a21dd917805b95645af0d48011ce73e3e8497b685a6da1f9a9dd796dae3e4224fa30053adb81111e17ee13003470e0fcf38061f9457179fc537bf4d0936e48c1154fcc05d7395923dd94308950ca2a850095f8941b66283ba9aafdf0a2992d1dbc5ffadbfb5e4c1251173927b850c8984dbc802ce5e107fa4a01ffa4e7a8dea900380b6267ef1ce0f874d9ceb6b2036dbb52d6dea651ada54ca8a4be570e513e5bcd6f5abf149e9c8a02d81bcd7f26cf0e815eab230fe96b1524ff94db8617d639e0deecbdf1c5e7d1668e5d0ced3172d1f61e0ecda901699ee04b154ee2a37347fe071bcf075f7c8f73a9594376c960aab28988be2da4f6687021472ad29bad8f9a36eb5effdc55117706ff1af67b1cedf8bfb52094634a1b237c81e8ffaa9c1a8411e634c564f91c7e2c754467538ae87aae17a7bb40d6ef9e30ae6103a1f31783f8b8c6f275d4ba3ffccf7a5ddb090f593032b61aec37ce05bf009a752ecc4dd233c27929fead2c38698c21819ab248e9f9acf33e38863532bfc0008d5d19e343a6aff808eda83de88e3029a91d2e1b8ba0c73fc126da1dc3b9d4ca32713a9abd976b650fc3bb84ba35b0003eb193c5bc691e8817f250bec9eb6aebbd5d8efae06035f7f5e3940010023b34abf71430fe147e788bdc2ba3d128bca473a813e4485eb4a3ace3681ce88a9991e89e57ca1e6009a977024f3f9ee9ba0e24735a5d6fd7ddcf5a09e62fd7e57d34bce33f0efb41b04682d2cb81c91bc1ba41d4d9d0da0c41cc966f5b3ab1fa9ea086f328488ba3700d4b2419727a6a389f3fb3f809f31a76758d0d6c3a7bef47234414820db43b63a1e0fedee2c8a38fef348574527d6f7a7b061f3f205ddf5753324518a11284f8ecd50ca99bd811070538ab5a5ff7f5764d81d209f78b3246a0df39aa74b39ed727fea0f6f4c753e950a73863b65793a6c6c35596752bf6def3484c51728293ed947741852470c95280c456ea27001ae74e0c568d047e4d4c72e1226b68940b36836b6cfeb6a7e58bb3594bbb59dc45bc31e8ce7ad11999cebebd57d90febf6d04bdad52dc1e03406b3b347512e94c30033c2fe7b4c26a13b5490bf0fcb057b2da6a80fabebc0ddc04ab1bdd9a9277ac0942d6a68c6371ba22f2a2e5acdca1e074f42a440268cea8c562ef0248672e7109c3661db5c52fa97d3176b4d9639ee2a8a37d245b8b4bb5481b7c4419f17e05c0e5476bf8ff30389ff5eb144c3666621023ef24120a1733243bedaf6dc77cf66123eade2b7cbc02183a59708aa83e5523f6cb4a02508164f9eb507286d2159a97c1b28545eedfa7e2e7d45ad3a2da7cd4c3a6585ac96c5f99c26ed9319173c855e9c59ea680a13d578efc2a30b04978f7f50008c60d92d810790e4aed8fb4e0c4ccfebc2e7fe03a860b9795c694a2450837169ace5dc8845ea6253a8ef0d02846348102e2fa187044d768de9a3df74adf7e2d38030af794b892a9ffee00596ad09edf3f45fb7e699eb91d35a46d5e1cdd0151ffa60e988122f75c326a963e041f4b9a19f7c07126b743975974c1b96c92106325de129b14848d71efea425f3776d70099e06bee8b49ea50dbf263dcd5215f67698bb9fa69da70963b7d42a352126f04254db9490299a8572008896608dbbd5833fb233879dfac8e856cd60ca158da5b90ff60cb1886e73626561e73d60cf77c7ce6e8b9929f0bb3eb89965026c4001de32ada8ecd1c31d952bb9482037a9b7643f28de1d1cbf574f7c92f550c167bce818c25a64658e5b598c1b11657fa22fcca90f50f732fe537e55655aecb9d95785b4f4097b52e3d8c8544e643c75611559e500327fa7d86518c093054847a6f23b2784ec13de143f4f97b274e708bd306432281702a3fee283fbc455f7781a97e958a89b67f209538b00a5faca7468112e2505ebd5f3074878d3eeed71b861d7dd67e8c121ca8564aa047293bfa648c6e49935677786ccb3bd95a56f920448ff79bd0cb7cc3b3f55e69b6b7abb3ef134db49738257120444a5f9520e4529a94514f0cb37299f949046f24db8f76cc7521226ef309769fe5381b2b45847124814c98132b47889513a851be48da4226148c839b3d2f96fe48e90843458df75de794cf2ee0601ba417e32cfab23b124004a942d1e439c207777eea81587bd279bf45c89b2e08a5a252aa6142a96544d338eccb0b443f37b5b9be0f38d80459c29335c66423af70efdd6dd5a0a39a64a51f474d879173a97b312e26e4900802bafec8ace3d01eab73aa065801b211e21f0d37de258f8493f1517ae9113c08acb3c504e46af1368377b50d5012ddd4c292b7e522743acab80f3ff1a4eb9bb8a396bae4a9dc3ca1751895fe3ea9ab610c16d0d36475f16bba1aac9cd09edab6359f541ba0710cc6a2ea4d6b51e46a9daed95819fd20567e0360dcf4d9f6e287dd755c4cb0f4eae7906eaad3272b88cfe965a17a92ddf08b09f8bf6ab8bb70ecbfe5a3e2a451888039237367911ac4616f1bc591d803957b6907fb56c59e7674eef22bbcadd018466b49a0969a648622bbf9a3622b7d0cfec9d4ccb9093f61de42770359f788deb94f7d905cf9f5e59167eb6e0b36e25c278dc85a79cb0fcd9d75e0cb90d0fd0e2047b67bbdcae0191f014c3ef95c5e91624a64031554d3db8158ca90a9b9a0ac17b5b544fe55e05be92b891e7db1644646c8de6c95c68d971f51a9f282d00fe192efdbd752a93b06f5c7569c62ce33291ffcedfc7c36c62d92e1f53dcbe48d99113ad7a6408743c0ad0234457714ee9c313d53a8f3c6f069039f158083f82ab42ae467c30d98927ff26e633ee515b62bef16c32e723113118ef0a8eeda5410680a1dc72f8255c9a0e2723bb42fba82b50622888a3345a7dae2ad2e69008b7cd059eb1c3465ec4cd11f8899c87a460ac3af82872a35ec9e5ff72812f65947decbdfe5420a9dd712de46411463fcbcc1fda5d8cc389a44df1802ba73f07bb54c48083ff5a6f056d2494e703ddd64f8e854b527c42066f62ba05deae21ee9602665e9c3bfe6eb4ac526339ee7e6a797decd3d3ac0877a9a88ced614758f3e46f20c34286a313a9bb06500f261bcc66d87cca11000b41068b8cc68f4827a25abb9abfb5f1f50dacdd4d2f0bacfc06571db7ade81488aa3e961827587525f279eec90aeb88dab42b348a6d5c222a16f7d480a16e15a61c6f4b9c24df138af618b28e0c01f197024b2e0b2b8fc04d0d61006f3872640b6e8364c987cd230014a719868a6c842ded8800754b4c35f85749453b47625ee5cb465b2cb61b320ba486e2f475a7b96b71fe4da18b46412ba292a2c705228d10e84b2b83980f1314d97320b22145b44f6e4459714153a2c98b7a4c6f392ef0b95b2a796d41ddbf038cfcbe354ae01061475dcaf128a651fa1d992909e820293a18a58b7696011055b4c6a696437511dc11c46d8e9a5650409e5c8c820bb34c6ff50341940eb8ba2c9e1753316aa2bed6c04c6e90017a3ea4799e85446cd48e120857c47b9945c0e3f560fcc3ed984080a4b01fb6ba1b0e58e898a0d8a3c053b2a5aaf9fa2dcca7da8a5b1bb8b6e4680aa2a97e26248ffeda3f6efd25560ea70a15b58b75db07ea596ad5b7589f3000431d4a17c141a98aaedadd00da1570eab2a74473206bc291f30544cb4957832ac69fc114b2ba14a55789ed0a0f52f0cc9f8609b8f5183878344e0182528cbd58e968bc8b925a59a61b8e06ab4cd5dc2ce6e50dd31c4db0dcd29966ef545a2b4d5462b25008e6c88eac0a2d1040408bab78a4146dbadfc28df9dd71cf2819fb24878347bfca9a34c8f9b4d1ab1e205a698a50d1c987482dcc67813c8bd728e435c968313363e490648def1b5727517ce30ff6d7308d39e37ae93b7e61127538678e82919884082e2825048725b0aa98b03bf75a42fffba16bdf80da6c94c0b0a408cca3400e599fb4b6ca6bb46ccc3d7b3cd0175fee20a1b98a808618d0df504583ec655ee7420a9c0893d20bacbd8d30ae829ebe8b321099469dd872686bd3b965ff8ed9ce411c41d0780171bbaf4f5e87a880688cf0f1e221c967b01707e45cf3b3850e27fe8b5aadc210d400dfd63435709488166112f0057a45a40156a94485a3c55332cb4dd9c74860e59e6aea0f03a412f0fb84c40cae1cb0f99860061d22b4708bbbe85c705cc7fe68208043625e157e95f79f76eb9181b2b3a983f4cf5739429284220205470f8f341373c298fd9b1f6fc24089b57cfaa6ef589f24c8baeac475c8ea51e09fe7c5e84093e0ac5ce7adc9cb37ddf92c4682f77bf5f41f8013ba63f901b2d5a3a8156c0dfaffb366add40569b590e85d29bc2f449bb7d94ac2c786c2b35f2a759bcc586ec8973e77389fd8a358fdc4adc8e9fa67cd172fc01e5509eb94b40e218b569deaa0f78871230bd20856a391c1044b79ca2b227bdfaa2dbf5f18bf93ef7304f49ba552b9588d962c7d0855dd3f3dfc36c2e2b550a60dc94c156e848f4c06c5f1e590072b73cdb27b8c6e838acf153a5425133c4de47c6398a5c5702695a251b994a7eb2d81df018e941970909b5a5f4854d153b2587fdbffb25209a6c1aa9ea225a39acc23d679f83bd03ea5dfbc6edcc7bfe924dc90cba30c038ab6de0ef2d325a0823148e3c15492730835e1830ee518d3b26d2947dbd405a03fd21423c2c1204244808a00615a1b6860af80f7a573b1259a313240c4fe460177422f1b61eb6e4b25175af62ca444f94888b3ee21f0f6b417e4e46e64a44dffda1c7cc090158c21ff1ace1428a3082397f6b200d6f63518fee5fbfc52527b0d9ee3324acb976d3756c68fc246c0e4aedc61b6133a7ef105fe1b96539b0074c809867acdc6524d26d6b556b4e6d027d1f373772b5031785fd24e82fa5cf0cf278ef964bc1445bb0f327fd8d017d39ad5e59793845955c28b6aa81da58a86008bed86eb3b4e4135aec04b404e7d29a2178ea162217ea703a039770c78b947ebe2d55951e4c3b7be46d18bd3ad349f33a43fc6d0da63992151599d3dd141a6bd8f79db528cbc0db0551a84a782b6c301cdd7b54730e06339611b837a2d6a7ab8b8f7fd139ef9ace625ee2fb6bfd712e58389b6e280a296307d5d89e2a9a4dbc314e052798e5617d52bff509dc1deab4944665434cb097a887d817109bc3b32e2bce2777421eb3b53d19356734387968eb19e9f76b032805cc47e3bc7290f7679d5714de63bd83e1468ff63fb500ad1b466e0d807678d502b42e8cecd200d41fbfd457f3d4cc80e88a92c20fdffc45b3c2fc2a4472a5d39acb9215f24653b05f0bfd06e1edfdf764828326ca3663376a4670e9a133185c6d8c85518c9b1fc6345ac0f391991a5d032cc1471dcc143e71ad106353bba13364ac86813927d1913e042e29946f267ad93efcf973352bb5b7e0eaa2f0b0d21e8d2891e231016296d5bb5ae00a1847bf96ed416d3221245a0034d6ffc162bc1439cce2a876fdda03a46bfa5aeac799e252248c599080f60f5c56cb1a5d0a7d7437b9713805a095ec7e9bc3fbd78a5beb20510d515449946d73d6c55a0bea506b387f5cbce62cb3a79188e34b3cca2a37b5368f9b5306fd21c6466897e29cc41ba42a41173edc33aa95e55f8fe8fe1b4f2a9a17655c2ea1421f60348e418c67a10f4f2e27b88481c5dc4c6f14b99d743bd0c5b93839b77e5ff327d3f919a7548c8bae054e2c81f9f13d7a455613e68dc70489fb7c6145f652729ff89e181c94fa3d30bb30e7192b1ce006e504b97b1e99648f0df068d0e0275c97a4e7e55dd5a9a255a0bfa9a3ee64a9b9f54ce7915fc5ff2b0c0a6a76a168ee5c5d2de8c1cbf56400175b7991b402edb4b8a88374564e00effe04940cfbb710796e14cf29f6c7e20318263099464342cc536b77bb80c7e3f95d538d5d599e03859ff453de2ec62a3009ae64f3a048c7079918158aa2e2281a8829d04955972ce4216dd58fc19b7c738ac6e3d14627809c5e14b3eb382840e20c0b039d786ba8f0b17ecd13f3090dde3762d74f16a4f29c9cb9accb1afdc189b54e2902399aa88968b994a82c16f4010ceae8dc49372a0094bf0ad1362244a2744f013cf7889917a660a4a5a0747376cb4911f55c01d453050cfbe69b4f6900b1dfc3598e175fb4834ab9b82542695bf2e01582d38cc160834dc4ed233e8e040dbfbb6d3bd4cf41fcc02e5b0bc33ba0fda101ecb1e4d5af6f1371f17e2736815fa6b79a75288b98280483d1a222b8ca5d150abf7d4f7d4229fde1052980a17ead2544d46a8d76ac47624c99ffe521b97e2a1caeee2668fcf3167d0088ea6d469352d896ee60e84966ac00f3d390ad07a9f7eee19c0a9ac549f53e610603256a13cdeeced84341314ea230171bb8ab12593bdc18f7efc6964718e83c16a87a9803ff679f67f6ff5ae66fb58adff01d9d3ab1164c965ea6456faecdf9eb1196a6b9ad19d89f6715df3dc2e81e26dcba565e1fa138c08cf078e1114638489d29731e23be1e6107975bff15684ac67f92a5892627ec413571d2dccc1cd2c95646b9508d042a8869bfb7e4b61dcd5cf49aad55e389ed13477b4c787374d5d2952aab9612fdeae2ece7749fb2e3d1776dddf1b84efb167249ccd5afea9b29dbc13034df5a80e7499900be0490cffe63afb8430823152eea27622b34fc9f4b658dd1ef02ff04235317988868fc07138b134f3f6cc1b2739f1dbdb17180c18077f80d98f346170cba49580247e8948432f9c37c52f18f61a46492c18d6c3d508ccabd5a7c7a67d4d9d069b5259d96587031f39708b9da1bb0becb38e50497cdf6b73e29dc90d191dd8883c1dd7f90e03777890e858e2dd12f5679eabcae3b2134305483331cf2fb24b64fca002eddb887d775ba315a58381b8276e704427b040c86fb7d73615432845e679e67d2f96912d6dccca280e484a7e3008c76c0109572d0d22623ea2bad2affb41711a193870a07e2a47387dda05ceaddf30792e627cb8bd8d4f9f9e2c4a23cf3ba909f8cb7c444ff7eba8e83cc98a2b69e16bb0c681e2b807052e3fb2f0fb82ba04b2b35578eb729115cfe050f720190ecb3b17eb2c778422050fe1774515eda701b9b19ba496fdaebcb81be816834c037ec14443a599a366fe6fa4e1b5b0ff99b2d0433d8189bb5ceeb07f017e7ff99110d77e1e7e0af88f7b706f5c16da2b2b0952e360d4e686b86b209e4db2983518a4336755eb56b400786b7e97ac8849daadadbdda9d9926e586c19c2bad409e87633cb87f30a0554d5c8d00c051446fc1216c67c4a9879a78e0b2ff21260b3016126db64039c7102948543463c3b26e6f8397f4f5a058d611675f012bdc51827be7d54801235fd5da478b16567f8306c7dbb1ba3062be161d7f5e762a010747498f1208880f3a1669bbc18d77f7c16c2ac74d4d16106208db986ee317598b6599dfd89f9d8ae5e88613f6f2eb74c268f0037fc6792033491a6313b0b50c9c31c95be1a24cb9412f013b5c62717a9409daf64358ca8c455e7bf0053204756e0cf4c1c054b1cc3a595863d711406a70702b0e71e6ba5db06c36733f9dffe9586abf70158c6ac2224ff4d6e78b7ae15976eaebc2533f3595f42a7ed075d5078520db8d1e28fac1bfc50c5fb08995e3faa7d5227c4148e12117ec43022ec5f2f986809674d4f185555c8865f23593d1c3082291aaa0f3885527673271fc8547f17a7c1f49f451e0a3c6cff3143e73a813915777bae1f6d5db449872a01122ab5a402689606c7b496e35c4e16d6501153d93a450e27b768e8c769a693d6550eae129c796c8a65d3da562e0a59945f036cded1bd8e8a00df2408af6dc384d29b93b788123668d12fd825fb3e688d81be9f3f6cc4ab5426ae782a534cd3270fa966b35119d2966792a7f71e96c5aef30fcdc14619d1b1664ba702c7a6cd8034bb9b167e99461fc268189b25b8cc2703910a78327cea604680a1715955482e2e7ab5761fcdff435e904882c85774d9f65dbb518720114c3aea9274f98c685155e3c5af6d1f551d9729c7337a38b8d1940f4a62fc54060b8045fa6095a7656120df3e536f70fda29511ce727b1d5265c8e30728784c2a36337a2f385845bd9575fd6574a5b6fedde179e74074d23ac090cf93a1f4b5b48d436c4e5855276645e21433d151d76c727e6d735328eb95b61386619fa2bfde03d1846112727558018f5831259c7f5481751cb117b7057f90823e83f09bbca1ce3797f626a4838e5d15003263545ffed59ad0831a65fd9a30732d65a809c3866a6f11a2a82dadb7abb2986282e0668a9b395fe26a99a3f71516bce49fc352a1aa31c070aa0d583e0da60327653c03451772fa04fe6d107bcf62f94947351a9853e346fed9b5586235da5714e2e2961e3160a39922d779ae40bb5dee1f1d539584ad48fa5d0ba8b618fb6ccbc8fcaeaad6baddbfda1a3970591eea44c99a6976d7a39a998e666cfe63d0b2236387b919ac2a5512a02b75f33ddc9e52ff42c5a40ded24ed4bb47c247cea7c2d103a09f3670d7db7fbc0829486ec5c73aeb5f8592c3fb0df7e9c843ecd2d215e424793745f76cfcff95d3a5937145a968be6f5bde8b713db1c88c782376195d0047e04cf9b54d39111361496ea6f8bdf05c00af829e7f890e9f5857207c6671c584fad2a540daf27c90dab9390fb5b5dd70aa5e90de410706d89f972668fdb08da5bd93e47b9cdfc24adfcd3e67f368d8518262a3d4d835f28af8c829479663efec89cef9c67962836a02820e8d2ab2d7ace5816a1b4881c11d7d307c3c35fbdc0efe7309f13e2e5eed0a4e409f9078270515e0172f0c7ebd3e94895503728eba5e480ca3b3d2eee24dd1b16eac0eb60a861e424c1acf2f07ae7a102d99fbf005e9cd1fd19d08ee789e0bebe195162e9085c4d45604e4b0ad1faecbf123b731ef23e059dbc5c9229b0f4ce54517ea35bd223f4311cdd8abc90d0b5a5eb110d26c3be3cf9b4fc614ed8afc9d18b20d4e862bb03fe0aae22ea58386520c3ff88efbafa7b477eac12a6e8b49faa9cf1d7412c64b900ee83fe8793aedc8d5e5c3b0714667767d32aff850f7147750113c34eb5d5165fbd90bdce76100d97ebb6ec115380b58013fa68dca2aacda6e04bfd87f2c1bfdf2de4c14eacac90c252abfdb00481b2430274c76faacd80b96bb3e32a06c71636b7c1201a69b0489365b4c6a1e8e0d2d8a154253c90676452cdb78d95a17f5464a0cc494e3fb5a3a9c740919fbd3eb9e3da7ba5cf81f576f81fe84e1526b2b30cbc65fd43181f90caf34bd97b6066ed194471b8e8eae46836b211bb2431b2c377d5181c9c281d37802f7df9a87488241dda882e88f2006076d0c8c0d30cf3a296586d70d9cc4b67e6b3cff00b77946788daaf344e12eb9bba7249c6074ab0a2dd652d2127e85519d9b03d539fafff3bf513b8273492dcd6936815ccc05787e92cbde9109945b93c9e14aa21b62282e2f859ffd59eb686ebd26d629f27311c6fa08c24b8d1cb9f056f5d57a253f053c0e70ce81254ba394f3a1924262ee7c13d29d9a4d2e69163ca0eb16720df0dfb7d407afb5df7f14ed444663463a8ca41dbfb5877f2784d2bc64cadc5be5fbab3955ce8122f48fbd5ba65368deefff2bc79215fc894d570b929b5c9050013c1cd9a424cd7656739a3a504e23ac40698336ad1d8265840a8f45b51057b0c737f0e5c25bb5cc7bb2804be5ec891d237f52196ce51ec045eff2b401748e7cf4c4031634d97781cea3f7ed69bd0b7f1709331deca3cfa66699641f5452e7a25bed42deafd02864dd0898e93420fdd696045f1c2b729d3a4b7c8549c8000618a8cd41f6635ee99413d6847be3caaa2923a316462ea00b07d8040f66ef1ad71d83a2eb58268d2a85e539288e52d49e60612955241ae61a3af8b70b6e18f2a8aa49b628f0ac4b078c48988b5a3e54ed817a10d90d055c835fa0c39355d7680547f224d162414564ef764a60f864acbf4b5fb2684a491ca99f97829f6629235a5f9372bc36f14cdfa1d5e30c7169105962dd4accae08608402963f923a4ad60cd7d19d42c128a56e42f855b2d436404338a18c4b1fbbc4b6bc73611f2abf169e0eeeed31c721709608e2bbced3991eefb8ee0ff2f1bef81621635c9f87cd0a4da25b8e8b1879c5055526cc9046c60c5e44b68601e312337b8612cd82ec78752a2166ac690111509d4d7be9fea1b5b51ed3e85948353b05c9a62ef72d987995b984af0b5bf7fe3e536dd029482d5061efb117151f38750d04286702df1dac7c5aa2b29f8f9f3075d6c93d77e88c8f54848b010ec6fbc08282636fd8d1878b2a5540c0199004cc14a99ccd417e580cb898cb183b2ee643ae70abe608b1a8317b3305a667ea68972176b16d0b4a6c23ad7ec74feb8ea76eaa409a24cdacfe61f75bb6d6119f74d21899ca3b610d5dcf7094d133e74c53eabbda05d1a291d2f01157d6ba3ba011a50d9d05f6bc430f3950ce99e4b4f50125c3a9ca06e157d965a932eb23083661bba9c97360770992c2024934a8d2bd04b0d3633d81f2d49516b8c8410f1e1ebc487691629cdfad9588c484548a9dd65249e53f11775cc139b8d9c22d66914369ac3cfe18a04f183d76e62ce15c1ed6cecf7ba7d7ca0c2361228912e9c23abdd3694144d0d91725ca1c3e1f9cfcf53a8f6ffae6aff69aaf1549447dae4bbb0122c4521b8660bf164d45d962f39eed587448945fe7afd4425da513ca97ee5977146a56d0c5c5d94d2c657a0bfcf7a36807d3f90fe0c3df97d0e960f39491a2aac47920b3f15f2868fb9a5a65b871822a7b7ef6a48fe8472f9d8ad04861c5bb1ac78adfb182ef14afe30aa235dd44b7b2e00d1db06f1806b62ce53ff9b68fb98ff1510a12a53c155c26ed9c9e5fe517b8ec172a66ea8ac11d29b48f6802e622dc273012b0b306eada4b50f4d5aca6a0cce56cc8b325c1c318ce3b64c212644e62ccdd97c26a93b92480fb3b70cda3bc67160f65b6b39e2aeba6d2a2f40f62edcc5e3939589b71d82b1f21a4380c41a14cbf070da558dd9d6c38983e03c0b0aa45dfafa06087066f638b56b1eb9dd755f146c723490021dedc6185d84aadb4c6685521be0509f2ba17c548f0b0079b3dfbec999b59c5b465c0685a4a162573dc2c6b6e6e450b535afbff1a7614980fa3dfc9276cea299cc7e34a3f5f2b3e018e0a94ce93435e5d0f8832f229a80afd8586eaff97f77ad2e55dea3eab2a84e14a2354e42ca323458c46d495040dbf6f7fe17c67bccfa107378bcb25a7c3506a03ad7b48fe66c8dcd650076b166422b9df75b4ba875012fa365fc34de07376c9e885a6f0e8f7250c6c2a3d6d3ed14bc8da721861d3386753810fa681c4e74c177f229355f4c5c725f401205d3a977328839103e277da9b2e35c10d1276889a50c968bad4d736c8b930f8b0a26acc152944a89ae885f5e91a62d9d99d203841d50482290f4620a9423abf3bce2cc1ebaac280bc12fcf7e61aa70c811b52e3478202f8cdaa2706a78c848b409f811881d21071450eb569bcf2afb3c897bd41c2e476d21c8065f3c160982fd0e28a4ec9d5939a26005ecfc26ea848bf6d109be2b0c6db2cf4663e571b5de386d32501febaf652ab7d0d6e4b5d7953cd9640b7eb0b0a233b24fdd42992bf875a87607226597864104499c326c4ec3c4b901649554afdbeca8bd298e14395c3593518bfe9f8bc4f170543d0781819241ce2fdba67dc3ad023159dc08ec1d4b0cf04632fee2a4338d7c24b2bbb1b53746f8a675456ef81ac981357102d622d9ba400db309cea757ab2907f1706b2df9d4ba8e05b3381ba4164259b27e39bc99f66b87cc0a2e4c7a4921d5f46e0d892203fbdc104179c6e1108ae91117cb3a7ffa68af6b25b5694c4f0555b68954741db59b420d6aaf5da1494d4a7e8d53e8c7f88183d7f89ea9e393aad28792df8e272a765e012b60189b93339141fcaa8fb7aa9eddedda5999b4f0ba130e725db4f82a49a91c62d282657b9c91f81e46ea8b18a7ed161adfb98198a80c22e176c286c3379f4683d1e86046afa35488145759df70b840fb876146f0e01fa37631e800b08c876af3d254395359e6247ad9a2e4ed59ef6be0b1a98e16e8b62859417b9b38eb2022ad3612b8288746f90e54f7af520bbc077380caa6914c45c2d30ca40e4a3505bebcdc5299894e4fb25119a550540dd2296e6ee929b7eb0c6f31001087539f5fa3fe5ac79879fc22234dd5dfdc6cac91d1e3e7b33d0c37bbd237a44e3c94ace7f9ce18f0f7d8de0470ee3047921e1d9cb0761210f54ea757c3d39de480b6f83d54004308a3c8fc57904ea5311ef1fe84aed1ea8a179572b05194ab5471b19e2b04b3a07fae6d04edbf6decf3207d1d67b8fd2563b7e3d1c6d5999c2f828f30816574a13a06a0125247c6d4f80e17721eb10b5012997d8f1406e2321ea4e44087a57bbace6ec329f98f48759b0738000873810ee98303073b68f7890f671ed86abee0ad139dcb9217396fa676d844dee06392393ec39e08f28193f9207239a80d61d91800e04047738fa4afc983c047f6653880d39acb03311e4f47e029a363189c902557a8fa5cc00030f9739fbeef3197264c04003c24c48b7b90fe8f4b0b928c321c1926a15ed1ff8ffd022740b8324a21541c28188647e99a3678bed792a23d9ff00772fb931c92fc4a7482cb28fa61542b2f4929552353a5568c222266cc429658fe5ad6b6a4440a00b808ae6c1cc95b477a801dad23739dcd9b7805cfe5a6fe72be5795e9c96969c918692fb633b23d0d087f909cb84553e7f25ef9d1a3ebb04822ed8258d6f8308e38d051586259b101d070f69f82493eb16fd021bc7ac737f6a4de5618c6428e2718480a42eb512b009368ec61dad9198371bd130135f8cfbed182cbed89648022ea545e50227700b5f8a56788ae8a1832453cf7716743f184bcfd84a002faefdcaee2bd547fabc64093de2789c7b31c2a749a27cfb58f6ffc8e07364a259e5bdc0ea2b7f29141282705d20b78c216ec179086730cc89e551c9da02269e586600177a2dfe098a3ebfef70744391cf1ac5e1c7126581d4c93a61ced9334dd37b2f63a000352ec66a4f4c020d2f01da0cca789e0b75d8888cc7d22b31dd187c0ccb329d1f8f4b3234a8b0f59fc7cc44fe9266f1da8de1573b26151626f0a0856dc626828e4959a0c60192fa78acf57b24b6cfd73eb9f4461dcb412a09224a5bcb7d52754e1bdb56b472db6a7e6cd7225acd35da1c2b38f49d416a7d989a663f81c64d5d56405f416bb9c94cf27ad2543300111626365776efe402334ac476ac89b108365131ee44d26a07a68616d12287893518664402c47321fdb774a16cca8386b90892ce61d38202941dc1311000718564817bb5c0e787cfc951e22a459af66f3a707e6ac88d63bdf1dcfaa2d89cd168629979d47e55ed3d7403a01372f4d486c501dd7ab83bddb47c287027fc20b55a52afd40b6bc5c3f180e46c95e3ca4d0897d7398ad5deb40a6be340c14fd46ccfb6f01626d89af1751adf74bb89a40224f7701de168a500e883672823c31fb66f57792c6a8ec3fa0626b96f7880519893dc318086e3e830208fc935d681381b5c6fb817098684ce7186364aeee0c91f5c641b4037d43741ddc0d304dd3c634a6175301b301e155573ea3ff964378ba3ab1e3466108d871d12989424d5cf1f2c71e0c94d19d38937291696c3b93a3556e95281d60d011bf4baa84beb2e7aa5ecd957e448112d0cc49e31b9970f4c1aa2e395f3f2be53fb3e5bdaba799db65532f3c538fdd32642d96bb22b62a74ff6773b4e86629bfcb805cce57e25df63b88bf05d55ac859914b2d57844f4e3974dcbf21a6fb2f41f128f461d278319ae8099ba8107c0520357c0a509459bf32fc9836953274ee21e77d163e7a07facb80a5760c0fccd6601083dc3370335e82a1741b4f0d95ee81f8b0bd2d99bf3d3020aed0cc094715257ae54876c86857aa76fecd031265b733741a7dab05d231ad378c1edf620bcf4c40b71ce77dd1d64e76333f5bb75009f2eee39da9e650f7796abdcb6846e845e85e74131f342bf816ca6537d9c3026e3ffbf7d79dc591d4cf40d410642d08db0a406341d05c205a0b40eb02c8188a5c515270c202f7d3dade1e933f7be49475ecb46732bd81d1da199acb0326b629828afd3366669a41581e30db11697173647ff2e9a0f8c400ed4185a5e63456d831793121d561d3fdfa897e772d94e5f2f3a15c1a015572d581dccc13c61909e13cb0ca51f720a8695e76c02ef47d1f7de6b89ec5e968f21e14ee8156f3ca75d6d3e7d931c45aa0f8045b83ae350d17e0f05e22d84eeb3deda34ee2fecf993f1f07df41661c3314fd4e8b04a6e5d15178aea1cb60944a2f9f36f2fa820434eaf62b184360e2f22d149e78bef5329fa5a19da94829ef7203893a6e0fe2adc3d4544cb9c37bdb4821f52444668032efa602f7218c7e06ab853443eef8da52e6d22161857b362b8a0a451515c5d3a0bb6ecf80db93e6b9e6e46a8cfbd67c7e16b96c2a5f36793dd72b99f89a9911b1a0aa27e4de7d21ceac8ce0e4fe62ac4d65368f1d106970667d3b402c6b0b1fa4cab38c2ebebc6eef4d006f10d1a80edf182faca38007551a4c3552c2cce967a8b7b245a0718dbf8f2f965e607665fc69af9478ff875e19be031484ceae7ab6ae4defeedcdb25bcbf0d81e892f7dc5133786653a25611afa47a7b3cb5975c059152c1e9640c05777950c5133f76fc734a9228842a8640ab0994676de8efa6a883f491d7112351b8d81431fd46396f08d8493cefbcb43ea1a028ff49d052aa5387b3692a00e6c1c5db02cfcf904b0b8ab4df39322e1fe8123d60525c659c5e8502d400edb51b1fa76440d3effaa9740f1bcc5370e0da6fb6708d3b2858b531f742cf4a54a244bb3967cf1af5e469c38f14c97efafd35a7f97a1a1d371cd52b0d4b74d2d4cdd29ac553892dc2e1622ba3fcb408ca334965501850be38a902e4f14d422e65c7d10faf40fd98ff751ff63df6430aa29770f31e2d51f10d337af823315dfeffd8c77dc07d90c15ed932cd84765b9f479b570502ddc9c24393b5965b04d04f0ecd6e40da22b9594f5ace0863960f2559083ec9b4e6627d6ac0713a3624efa6ff931f90946cf8cc23a621769888b0eefad43f904dcd6ed3e6114184b9f854bcb434216998b8f200e2121dc575410a9786e08a0eb4b6c6f78e5a057f8f3e3fb4f6b8ba71cc0517aa7a9ab01387ac4e1644fa2397ad2c1329fae422a87e28aa8f95a37b76bab32340422cdeb88aebd4845e05fa6eda3b7ccefefdcdaeb3824020b3efa1a6437fa2052ab732d0c8634e37fa4d5e32c3eac743baf895caebd663fc5f3744240b9f2d5dec7ee4673c521efa532d92600d40abe769c566647e0f37aac86daf67d27cd393637e39a23a694c7377e8ba87dce197d653c0ef155cf7a575186cde1ef2dc93ad472212a2af05fdc82dcc64e4b07ed4da0871f971e19057f3c78f7372d0bc043f998acf0856389c84334a5fc672d823c6cc41db2ed47c8bd3cf20e8c519044eb07dfaef0dec2659e910dcc94a927a0005ab9ec89c5075329cd967702cb1d7b52e80e95042994e02af69bf408d623d4aabfa9a236e6dbebc0abd7d97d068129be05043204151918f6e5fc422b2a1a4ff780a1c39818a461a10e14626d8eb1ce54ba6bc35e5905b4abe8699d35180ed491aa75bffff75103d74f61d2a0883057947769d66553e1c899940670cdd4c4a63d08554cc4735cd5708899b66853dba5c57eb1a19be715d868259ec511bbfe4c8277391fe44545b97fbd2e4569a08f357fdfa6d0903526486ecaa0e25b665fab2c753a26e8b205f2646397be12039fcb7aafdccd6dc95d414579a5e3aec91c4c4be57777c35ddaaaed33fe2fc02ba05acabbf071a81dee6a235fcfdd911c379c57c44db6f1189ba7f3b3bc0ba7431eeda6d235965a165666ab20fb7acf114b069388ba48931497e8928d6369fe6a59d3fb3328f469dd063e034e5a2250b807fa5759326a713a98a2101eac4a0a5186b17b686d67b430fab3a1ba98e9479989d2a4c7d74a9bc85cc214a8d3d4d4d154d15bfd529321b29b81ac44f8763ef5cc91a0128080a8f776778926e4d5943f2849f6edeaa295753ca6b84ed77b814ec531edf676770ecfd70f67785a60380bca750c9406dac605dea8a63d8ee4b28e5b1addbf7674611a6c26bdee6271e47c5499d39de4e5e83f8c255f1aa833f07cef3fa04e5eb9933f6dc146f17c48ea380e8f7e6a632e161865c78f4f75d03fd85a660c3b347a8941853ad4ff4c9ae6123cddbfcf56c3f114010d74ae8d9aae7457b60654aa26386a8895f7c9d9c6bde865764ddd4044a18a181efdc328108341545abe93c5d3b9af7f61465d9306d7d12813ef75c40f05224f0e739909d5ef2dd5eadaa5b9c2a0798035269a353bac003d5b5c26a75b8a4754c2bd5b3c9212aa1d27275c56a93d97698f626e4540e935227448a07cafd9aee62e1c3a53bfb13781608605cbcfaee925ca03f1180ae16a3cd9bc923dff53b018eeb0826caf58bebd889653b54c86058268eb23ac96a0436882b09ba3c42a356699fe97e7c6e573827df18b91d26a4762323c8e0569ef622c8a36d35cdf9855143d0596871a3bfb8e707a56632d02ad25f84cb7bf7c470facb335a81da2fd14786005a180ce0f91bf490f4eb3e1425b5733ed232ca3f796e4a27895c52ee8e3974a4830e76662020f65a580ebbb035b654581183d131cb0c2b9b7df606e8ebe2ed35007f4ecda95a0772af1e4ad671e881bcc80f2f353f6af149e9c768280d9f2638a5c7a27e8f12cbe257a93062204c34a9f45049efb5088d12423d1e260b11f44d8d0399ae2e76b80d8e19b1a95ca75d188f666a91e5f244172040b10c2dbd2ec55e6b404c124a50129a415ec34af8f1554ad97c9384c638bdf69be9e609771894d30985e30eec57129c27557cd655938c1ad2852df9bd64143d78487b70040d1775957287933294a71673c3dbbcc4e21a67aedd8da6989e678624e6fab649aa6bd0e675cf179f48b555c7266a7aad821c4f031c5164a33bac8df677e3c7a0d825b77eec8c825b2280398936d183de8b63fb8820f5da0fc84ba0a49f277761bd3f79673cbeaa65a36551eff73780c884ca0c343743aef91f2aabd7015f0e96600b855735757212fbf41448048262a004e854efb886ba0c9cbeeb6f8903834d80d15aa3d7fe02d21de0a5fd6c1f9d78f81ee1a9a047b16d85c70ad1b5a3bbaeffb001ee92546ec1716779b157cc206dacc6e53c30b053d211059f3202a60e35bfda7e50c7e4f2ecb983a2b7708f7200d0770900d5d9d43c4b097368484a36624a3ee8b6a9d13e50a6df02c0c16e60637abc743d0f61603bb118a34c5d775fea4aa56eb273a585e2991321d911d0d666de2170d72fa37543a60e1f21bf07bfce5050145c41f38d324ce994f7f4fa29a27239ba8741a2285e809fe51efe3b13252a227e20df70f11887537eb8535a78673f1f5fbf9a9d845db7a79c5f36d4d2b756e380ad00d1ceef4465a84c4528a31ffc4677864bcd0d7def4b1545be845636dff8423146c0a9326d4e3e410cd3d478f3415069b879f17f8fe88c2a3859d4fc9ed231596bde568d47d51feb2c709d424d16a37de0a1e4cc6fd80bb71da1a1aaf989a647df2dca58cbc4ba77865403ba804159a62622adec67fd3c439e397f9248eb67d1ed30b46b841d2d7e64df76b615ed690526b829f5edc8f4a8c690e10481c1f4752cd7cdca7cf00f712997f3c37d0676c0464bcd652ff4dd7201129a0cfd67b1a6d22e47f60470f511315fd7533c6ee8c1a8c6bf2ec434ada2f0024001d868b1f070b866faef2f98a70104ca449282995fceb01ff72831fadc70666597c5fc4f4b1c903796dce1b23e6cb75d6a78446c5a30a17e6d0b952bee182a46787a782f7888bef1c9c00795930201af407978e37910314901bf911a314984901c71dd2f00416fef639d730af82c48f0113811d412278cf3e223ef01d556fd0bb8e0888b6e8a0742ab6335b3420d930f4a40d8e0807fef74e03bb8c0c31e767f5614a8ca77944ab7f1a806c8bcad9e3d95ea5093fcab8a197fb27c1eb426ad7572b3dde9b313075abdb10918a973d03dd92a9003182e2abb4ed071d0b6ce461a01303e0934ac469347fb8020726784e47747a5bbe1e92cd29faabe0a042edcf077329b7021802b17a2a905e1c08e06a08cb138ebc826ce46340748b8d128d947ca10fa91f7a5338aad8edf878532ee5491ebe69c28955b2c7aee1839832c648aa9d0b419fb1be4d546d7bd9995ab9e3a5e3d214a525749993d5fd340b59b9ae8469c71cc3478dc6875f4142adda372f847ba9d2c1c075c169f947f86185f79792a89be2826edf0633254e16bf87fbfde4437623974252823b5ac466a84d91860e4c39faf97f6d3cd09c89f7e8995760b48616b0230bbed383333d1362c92f91eb9774b766cc453c194fa36bb128134e547d271a8830c00868616bb9aecb374f390a01e657d2a40d16ce4809f259b00f53547b2d8248f454c83b87aa9e3e6d02e5114401ac370c63315fa3a34f6b4a0428495a23c61d50816324f60de5803438d0b068ff57f73f904c033e887f2418ee491dfe564bbe754176ec8809903f0e9333c18f9e672012315c0228dc20db5a65432796320f10fc79e2c8ae584a2748fbaea4b88f1514937e82d1595a6e6904cb376008a8ba6dcf164365aa4acf5701f43440b70895b3c7381a1ae0ea1a76eafa96f2e454a414f1104bc5b4917d882e1aab705ee0ef37ffedb19c3136885877c06dd4ca5e0a52bfd9846a9ac201248c1ad67ed6cea7cf43809a50ae4280c5d2354ea40d777f20e33a5dbddfb36cac35f490825f85913a20bdf403f8e0d51d00b028b9c584052140e464e194c5113b0c321104a218ead76bcec144cb1f58f1275b2cb4edd096e5d17668a1ae0d9cd0d285ad7aeab226e77128b0555f71abb4130ea8b350dafbe89b1b42146b0f18cb7b651492730fa13d2860ef12d4c459e255584dc11e2dde9b2d5a64a7532ba28b9c35d1740950db2dc35f54f307ca97f7558f63f8739dbc1135065ba77c89f9fdab83e1cf6d577f47a6471cb9ee3641acbca5d5ab3a94708460f065a27b027319d87975392ffdcdd90250046ed894fd28c3ece9cc8b139f7aca966dbead74ff7f6dfa80841686a8fad4c9695631d928618d262a6c854d586b6bb247f614ca1551fa583865d0ce2e1ed1aefb201142d33916430ce27b4b0f9957a37eb42c4399c1d259d47a1258a258761d48064d792143c5458aa9e4f0a0738fcebc90b051c45e0c921c2ce2e01d376e5302a949fa0aad91118955131ed4c682a9a91ade47f1edf317a1d9500f0bde6b790fd766c0cec49ebc4e49009741e826c78179add1fed36cb07b19e93d3b8e4117e7a680d9f47a50fbeb9b685a55ff876dd215d03581c78d220730ff1a1cd63e83e97c950da39b146d39bb4b4d3d408d929b26aef744fc33f31954930ea1c59722495570a8fbec402b95ac4a109229ad86fe9a5d135b76474f524fe9f764e1174c7e200bf72414ed49a507c30020aa7395a5a304ea99b0ecaf40a3c8f7104c0106c0d1c2ec4cac0a3207dfef6c0197805186723dbf01a507fd2c1c35d050632005c773a10d6932aa802c22040b7a49a48582e95f96eb678d011b4ef9b39b1bebcaf977a08d95b6e99e49652ca8d05f0056c053d2365c89c962e768c6d1b638cf1d40f2e47faa671b4360326d128f38bceac1ac836c6326843483768fdb5496559366571ceacac2ead0de2272eae91be691cade71716409e17a535b82054b6aa27638c11e35f15d6c698e862273a75e454af9a7edb9b80f46e0880ce18d26407d9634d8bdbd534ea2e20910d1d9dfa29f12aa34a2df08f2234a02e1ae8739ff33500d51b474d5ac7ab785d1255be682a25669efc68f4808850adc04069c8a885b2f6b6ccc09e2fa17531f2422686c80c93313dd6b400a1c614af4e1dd96c3885494a52ce88c1c3fe20fadce7fc4b79017dfaa671f4d4378e2ae501e2dd30c370da04a8ebc6f8e165513de9882ad2e3851d31d080c420d59b048e3e7dd3385ab751c306c7950d502aac983ad24fad140e72c0a00244828a56f6ac273c2377d0ce38d2d36fd158335764388636b051e454c3da12327e60c7182b5f6460944e5249f606895acad14292a1b01b8410e18103489493d57115629dddc0eeeeee5c64dab868e252c585a11d9ce3071322ea43a01f3ddd335d67bece809d193b53e6397cefbd370f651287adb085adc0186322bce55f48e190bbbb238d4065f807749634c3cbb9867c3ce65111a5717e4131407dd3385a8f715da47a06872ea97055430883c9ea66b841160c309e55561155a13482aa6ef6786828f92f4acc028961bfee60563741b3f2c80d345ec0f0c28e5b1cdbbb6cd9f8b9bb43153585eefe3840376e497cfaa671b4c6805819af183b987a356e3c1d6930c584f9aa0a7a7103900e6b03885051c6185f8c313ec3647573df1b8e6d8c872678e64b8fc180dddddd5310234c941896ac1043851a67ceb2c88a8aae2fe7eeee476355378fcac252f36e3cbda4cff1be2c560b594347b98c3187198221d908366641bd116d943cf780212c63ba84812aa56cd495a57befbdda8bf8a6bb4b6d77c718638c7d8e650bb30dd3ca615b39acabea727677332ec8e8e831609648355d1d6181fab1c5fe209a6384924081c2066a852d9606102dec73c1951f971d463d2aa819585660f0df0fa4a4d701bf4c1017a0e6c0aa14c41762b529e2d320a85dbba8c3cb2f0b97ad1a943cd9707f40aa8cb4d66fa1e09b679aa0aa9bbd50493fdd7054dd4c435575135f5c4ecd9d2015e34a500c45d610c92813d2a0cc8945ce39e73046931941babed0b8609dd21011d3288901822008fab442695eb849e3c50608823af57a8d33c74138e9d501e08e00264869fcac5ad00082d5c9c6a323e64927cd3327a79b21c0c7b971a06393c051a74e711c47912441bff54ab6621530cc17b468eaa5d142f8dfddf8b49146a38d22f64b6fd0c9c9e96688fba2a86387eae7af6f458cc3c1246707febbfc22297ead95c85f7f7c91ee4c9261fd9963d18c5301fa899397bde3838f73033bd8893f921c9c69f1c312e25b9e1147b748e619e61cb0d21014a4254548084a14f147f1a728fe8e8338d2eea53735929ad59cb8d3d9e248bb17577b352752fab46b86a02f3a597b544bd31eac60052b582d06bfdac794acbcf941fc2b453b2f5f3a3ec69da037b813a0f8167782d6c40fc72935277e4512ff9274abe6c41f49aab502d5b2a1bedd2cf1773a5bfccaab20c40a42fcdac3b1c5af4a55fcfa5441f88bf845310c4551b4392269a25ee12c41415a52761cec2c8e6f495a4f63ba6d8f71351b792302dd378f71b49f37249d527335211d662fb0d61bbbf1873724f51a29146dfbbf758a006e4cd2ae28fa6647bb6c607e8a3b41c377dab5a353327daab9f02b52f836fc1cbeaba0695768f5ed6f56f821fd7062f920db61183e15cb0949133509ab18e7d4cd1d0a45b71dfbe00bf92f20013f1b301bb0f32141ef4aee54000a856f5fe88642f6859c2cc16157909c3301f36b6d043a774012e8aeef03bea520e9c3ea707382170c68adb5f373ced65a1b6207a7b54e4f543d333331312f2f1b921d568f299bd6138cb3da5a959c5cede8dab6b905520a82015867468e0c5df96d8c5bec985aeeee3dca9404880d4d15686becab2c4b16334b8a6b63bcc19414a20c3606a7b2ea0df30b1552244c06d8565996bef9734105566df8ec18d2964ec68e29989998a011627afac6e30791bbbbbb3bc61863ec2b7a2280fd41f4b9cff91732b554bdf0967424b97283c8660dc155498ae6a885c516ce37e79c3fc4c939e7ac556af1b492b498b4a2b4a6aa6eba8e8c9b6fca9f8e6c6592ca3095892a33f54458672a60803c2bb6dcdd913c54fd983851d384a33988484d65ec18638c795637431e635614a950866912a5c90cac195b3d3ca3cbcd98afa6a9444c154acc27005fb24f4cee3d8aa95860491264c4fab67c5dbe2f1f986f8c727965ec18638cb77ca9ba294636628bd97d9e145108f7de7bc7808d191b53165686f1c292c298700828a8518388113434aab02c13b1273531ae1d62513631204c2d6090a46f1a47eb161af080e14a1ac41812b44307638c6b78dd1a5fb4989d356a10b1bbbbbbedd9344737e8908a41680cc715cf9178553791924224268d14757b1f32d3b9d9c3af32e7523a179e7349722e4cee38e79c7376c718638c7d3e1e638c45ac1aa836c6f94a11819924b08029f52002c2c28927c31483654cc4a28685bbbb0d44aa6e5a8db021cbc92879793bd55b41ec1905197bba3eb0a9f5a246162c4d5a886161976cdd177cf52a0a0cecd1cfd76f0d71fede7b57b0b2b2b2b2c23a2eae91520599608c31eec2abba99148ee8c2a46f47faa671b4f69dd5848c2210c618632948ac6e86b627ba40f20232418a2e13a6aa9b514ca63493aa1b13249f9b59565f805c31caaa42831217387871617dd448cb488ca864a74c652a28a1bbbb3b550ba07577d7a25475938496a7502fd1f285880bfb7577f72a4a55379f421c7a4415aadbbb635929dbee32da60bf1e667777b3a76c8663a2794199aeaa1708e12083d346cad01603d8c018e3a5295537b9422f4d62e9cbed718c20207dd3385a6f10e3c863b400aaf118638cb7beee1698f69e7e30485795c53f881a12e6447d427a403ea8617536768bb48ab68aba8abe8ac046a0c7186351c84a44cdd7875cfb91354c1b4015798b5966ccddbd68da4046036fa98d95b0aebf2164c3094775b050039903f02062a5e9a6884f2a09bbbbab505675b30c793ae9c6f451b9a9aa9b24925485d083969f94a19155aa774d4eea5e230f14c8afc88c2ad7e747628b15a074689ff3b9cff9f751104c076632000aa588a48c4ebcd0f891850b0b5b4a585802d34c49c395c62bcd97ef5112512803a822dbee41d58c560082b66c497991e48465adc85c602d614445b60124e11a41553df0e9480a0cb2740345e43e30d19382749c8889161d4a5668e1826507afb0b1951739a0367e1c102fda60b9a2424d95a6243fa08c5c46334c1256c0b6c66846faa671b4c63d34b34c878e9565898431c6582cdb13eb47b5312edb226a6602f46395064a688d911a244b2ab815199a848cd8dddd5dc691f75bc7dc3137ec86ddb07bc52c453483322d6de7b1a49554966511d5da332bcc96d606ba28b548d3bb02e382244c4c0e33f303e4e9f1e523e902398cabba03461908c38ca753795637c31e120661aec26885d90ad3858f9072009e8eb2784947786940de1aa6706909060c1830b63b908d9c1f223c7efce86a01c310216ba4ca05de07f735d5a2b11fa42d339aa86013830b4d11a3add1480b8bad7c256abb971b4b94c572a3e80a88182b9489c95163ea8ac82f8790cfc66760c20d3698311b6a12f365086d4bea880a349a3a9a91b3dfc897872acac792d0c79316e3907e4b7600da6309979a5102467468b11102c272460810164be30b215978e878d99732b132b2116566b7c72b3ee78e31c61813d1b01968b2c285191c33a4f90ae243065f53453a32030d474361e08c290c6654f5fefdd6d68055dd1cbb23d694dd1166e56359b162a46f1a474fedab208b6ae4cb08989131236547ca23bc1ca39cf3e7cfa0520e57d282939cfd54207372c6a20a0dde325bd2378da3f54f53d9ef890a265778541608b855caa0e2eeeee5aaeaa616092f5b41d2fc909208c9924189fd11f6956574860626173fab348e0f217dd0060ba96c48c891b2301f555f4a34bf6bc1f6ddfc4951b251d02db5627ed9c193f4999f8209346ac0a0c2ac0c2acb9df93b9fede4aefe08d2aefd5aeb0002b03039db993f6b1de0c0658b8e364067673bf345545bebf5acd18220c5e1bdfa5eaf7e7dfb5673d7631841bbbbe9f8dbcffea1dbb73700dc7a796a51e6e04ffd3b74008ffc2f765bc98cf3670e3843cc107433a0e6ee9f6d1dbb0b6d56ba3e0d731e541060cd857f3f8419e27fc4e9aa0b5fc48555170220b40eb8555df8f76f5f009821ec9ebf2866db71bfe72fca94ed41cd85e177c06f77dd1d30397f52805484eff9ce0f73eeab21e3ac20ac05bfbe0d8b9ebf2a5cdb5e60047b50085aa1a2dc3d3b16ac6173b6fd4ff8d94e9e70ffe24a088356a80077286c4b4248ee50b83c20d9e3b32bd6887f7cdadf0f71b59f338408e2ded13ebc3111fedcf317a56cdf891bc79c5fc44dfaf9336ec4d1682388fbc7175145d891d2c6df381634edc717eb2e93503ff7d0b33c3748a3b6097c1dbd25ca56ddb910a8a18a714e3a272dc13833075acd8135377178e063ecbf0b0183d885183f04f07ba08298bf28541b3f08bab0bd8fafe8938debbe41d918073f6de6606aabc66943a2e78f0ad8de853f19bc76ad6109e02dc76b5b6a5140b7ecfa736ad75a629c1938691ace9e407d3bed6b4b8179436e30520ada9cba1bed6b8dd99dbaed161a8d81ccc6902e1b8abc6811b6336b1c6c533f284c0ea87457af4d49da9e3f28bc4d6d394e5b115d435a01c669f3a1ebd36e648c7bfeb492c8aa70e9521ad172e66867d646fa965632acb5993958f16f15041e71ce98a671465a1bc16a2177b417693814b64e6a6fe61671bae68a7e6756dd0ecc79607f0228eea634afcdcca186ff411476cfa7e4b536675f1a6f93d2d436a577e1d5e23d417271b79aa336294dbf9e30ce1fdbc1fc3d99e2fa39a9029f52902c41df56dae4e7246adf3d7f3fad3d31a0a2e40ab4a5940789253102aa273b6725c39f2626d993c29ebfab4f290bbf1fd59ebf5f6f83b0e72feb6bdbd1af7fbd16f0efe0a493e19e3960d36873d226e8efec578aeb4005a573040b5eaf6fdf84fd2aa28aa8361b7ab4cd0aa2da9fd59e361b93b65fbf2785d07eb576e2c699938584b683400e021e6cd6d4b68f63a7ec1d03e68395f6c3a2daf369ad3e48822d8cb356ac2cecd741a1cc239479867206dac069888fa5710aac0a0c89be4969a254c4071594145596a58b30694fac2a4492ac0f5eb652257a11f342e6c5ecd605e429c3a2e451b605c66ac7a36c2f8ff09adc7b198489859185991df9ea994d1afbaa395c108d49b4ace8c754d6d44207064a8e646810a4288b08099392fe6acd40a03eaeaa94e17861f212e565ea627777f7230e5249d8afd3a99cc1f82d7b343538d450a921a2c64a4d163eca39e79cdd1d638c312ee2e351d47499906a9b28126da6b274c0e30d8dc05156c368c9b775058a568f4d150f638c87ae13b0aa9b63978493b29b55025fd0231ea7f2cb0ab598bdb22f44d644958d5122a20543ecc22843f42098d5cdd07b68b0143234635ab4b082cb90265762e60fc7540e14344d54b8aa8059da71fb643bf8c331b6c4018ac897f465289422b23c9d7befbd4b94aa6e3ee1a02232628915d6b19265e464e316a2b8b802b388f006102348df348ed65869b2e1b9bb0f6d4065fc56c821c6c37771c618638cf10c65604a303c304960983c7773ce3967c718638ca588b05f7777f75de5ac0544793a496559b6c813cb8a6963ccdb9f73f4b9cff9bf69fadce7fc039101444626e3d2c21813e99bc6d1da07c6ae996a5395411b2c1ec6180f8dcd6003d25041e43a3768bcaa6e7e4123a6d190dd9e36e1b9bbbb4b11417970a483c1e700cd48fadce7fcfbb05f7777cf2bc8b6bb2b61bfeeee9eb35e446df7af59be90c822a689f44de368ed1863a2b1aeaab22c8fecc49ac2d43504e5824ce59e954e5cfd91dab8747d7a2ded82b47bebdbfae3ad8930bd1ce76eeaa693d43567806d5b18a74d06db97ae40f6fc6189b12bd49e3faeb29ddbf3c735550205d0d89ebb15b47062b37d69db64cf9f962e9b66edf9cb42637bd8f30783135a1b41a4f2b57b44b0c768ec9e146ef60e45972d37a8bd4331053576d93b14475deede9d0043910c8090646bef4e40b2b56758967b9eedf97b7e5d1a7be8ae324cd9b88dfb81d9da17cba6e4eef755b6e9eea70309178b895d0a0d68ad650047c24f0a0dcaf6effa5836fd4d02cec4bdd6fe08f42d8962dc3f19c8368aba53d06dff07b72d0eb72b8a715b1e8e4d6de4b64febd3b74bc08aa3d8f6829ebf2a4a3b143fc4edc437913ffcfbf8ed8f37b80ebbfa1d4a88bb923db69d8266c375b879900696e51e1fdc768f75849bb7e1f463dccd579ca5d970e044b36d38bbd76c1baeee11ac745e0cfa0ded316eb2d9b67969056f6a6f778de6b569b8fa1a677b11d686bbd384057e96843f435b8296c01f76286137ededa691bb1fabb2dcb8f1475ab371e9f16f70e3abd8e38f96863d826626d93e01ef14759be004dde383351edb0bf4f8235813617a5d4b29580770337e56b962dfe074f63892b59ff38338d69e567776e7a7e547e15bc48db813b45aedf920bf983de4ff4933874b6e7b433158ad46a3d56ab55aad56fb995363338ea2388ee3388e36e41c8639e71cce10f935ad619cb5d68a6bad389c1ff0539c6d896d89063fcf103ce10c01364edb11db0873e7b8b56eadb5d6f13b8e363fe00f6d4af4fc5129dbf7f1d366087ffc35af3ac671e286f66d126db2466bf383ed45d489a3cd0fb6af389b127df3b5a7fdf836255afcfce1e3b729d16e6db6a7cd10f56d5fab362ea3fdd1e26c25f0e3c776560e5fc3b7efd6864eb70dad257f87a489f00221cd36b4d6862fc2f6f8d00f6bae610deb535ccdcdc775b0958ad8737ad8e1a7e1b7b8b9f1091434156c1154e0c703c6edc23761df92277ae658596e14186cf1ad4541db620654b0f3899e09ec3a3fd847e1357c144e420876fe0f7a2610e2c8393dd8fbd6e2b094f1fe7833075d7fe2a73b1f3ce79c8f5f84c53d3e9bf6d83d6bf37b3c5071b6534cb0e6668de7a6d0bb04b054b177f57bb0998ab39d22dc6513e0db90b6ab95ec3991c237fd14789ff0b329c933fdab53b30df6d82b5c0ac7b93fb9ebf1d9984e9f5a6bedf1994f4ff8d9f32b9ede63c16ddf923d3edbcd48d066b459e97a337f5488504a47a0ad683304ddb419e204bdaad95297a0e54e33d21f6d73832174769ee2d39f1f74c012212e0cc55114213fa57b9c359e99f38fbb9f4d330ebca5a07b9782ee9c414af6d8dd81fcf9613edd212e9326c21f1facf584db7fac1c680fdaa268fb2449ff678b54f4016da5d303fd5d057e96ec393dd0cf2fc2f6f86c9beddefdec1ebbadb541d1f6abfd0fdb71f729d9436756596e10b6f7b0f80db07798a4bd9a9bbb578219e284ce969aff33c13cfd39c94b060d7dd8b9cfcb60873e88b32b61d3de0c1a08ba36267a477b336cf07de66bab43f37404ea48d55759eedc12ecc1e2c60a1b2cec040d7dd8a1cfcb60677ed050ddf1e7496087fec5f124b0e3e38303f47d34e0e34384d581efa48f0f0edca7a4cf5b1d386d20822245260b0f2e2e7450eab9f3b33485051a3498740939b33377e6ef7438f065b9430d3043ec74ec8bdb41d284f9c5cb0649df85197c134272177e0936c8d87632fc1d68716015a41d92523785bda36ffd4bf879daa1091108badafe3b90dca1b0ad09b34ad2f6dfb56083a1935b534ac81dfd1f151cdb833d7f5486b694cffc146c6767674f3bb8dc604b10203125f8b40c802f8752d409142a985fb65489002c4dca141108ba42a19c5592766695d4a854ec5a6a52a8d0d48c00000001a3170000180c088603922c0ca3246a363e1400095b864a5e443025066391401448210c032100c2000cc2000882400c00811c4f43f604e2c9a22acd41d6e453e6111736f9dbc763de3087e9cc304ca15d4982b23872b0ee37c0b40adff7d1d9f7ba59703244d52a94d8b272d939964333ee35a9a9fde57ef955ca7f6b0424514e288a1babe80e0dc022124cd76934895faec09f4547c8276b99ed3a626f105ea62d766bed37b68e21fefa567d3c6de24da90fe9aff077f5d70dd37f960fb88e8f65d7a435e978a1d06ef5ccd2eace8f380710587a7461cdd1ec9458df291798dd4b268e39befa11c7b73d220a6eeea70a1e4cabb7f67de162fa019e2eaa50b144ae2abbe2c464f6630d5641df37f3e1f9da9bb733b450d72aa7d2f37e092e2407734b7a36d6a11762b562df6385edeebb9e3f40cc493179b3e407c6b6fcba58150a35c6acaca6eaa8a896ca7d2f43079d3be6cbb564c65072b19b9b907be78b6226c8cf61c90ef6d42b81735c9c4a00e2c5fc81ab1f4fc2a14ed0a6e61d5ad97b4762108c9ce532d379b4b55fb9c3d6fe38afa74eb17aef86316d0e54e2123ef3048ccf742794fee50a4a0ce2c6f6d9017dbbaf5fa25671fd8a6d6b053e369e2463bc2f05253e22d1a8a9a16656346998b8a28ec3e746066ff0a3c77db4b8eee80346472f7ae42eaf0da61a2b18a1ff82f4297ca10766acd47d69f9b998d1113e379e2048241a39cde64d079affb442a706430051536063e0bcf9c347f88939c9ff576edabf0fb8285072cf8b6a9f2965649053ae64a1637a916a842433075e03f78c214768a79acb5069a149fc166554b2ddcbb4b072ee320120dd9491b60258ba70c2227999f1d3f93e921c17f525b584605f99a8310443f0dfd5b4697226ba33e419853fb06bf6568515f023efa580052ab6bc2076064beac0b19082828b90e3b237063f212dc00846fa7c9588b34e847c92a0054db76d0743d8016aa7fde4e790202d7d780094e7b136b7ba824a847ce7509346c08f155647ccb99e984d9206b558f4dfb5357d65c6ff424b38cfa67ebb90ba8d74874c78dba91c63451ee240edc6d2390aae4b6de8ee555bf8c3cdf36e7d8fbe1b7026b81275ec9f45298225ff58429ae2cd2262b898f1efa7d3ed2c477285d85c9aa0cdadeed2314084b43fe6399feadb07a3d61cba6407165a7171e6bbed2ded4b22afe238c699fbd79ec044d3b97778458c9aa1edf8dde8f10270b0c3ab5f3409c94fb28d80f4529087a935d64f96d924207a59b38b97ae84bbd9c32412afce933145e891d521d205a6bb6ef4d3daaf94e3e7cba7c49ea8ab75ee9cfb0dbea953e160bc5f8b9a1c4d14afb72bdf48ddd44b45e76efb6f7d3f687663108a84582413156650bba1a6d0cc77c70865501cb3d0615726c429adb4a21344e7bd89fd3342f173786376d822bb1a3d30f756391b39f42dc04d4731b3e514c40a5b22f94b88cf1e0fc7952caf01fad6eba1e7a0091c43ba37ed873e617e8323f0f8fced5e9d126a66693d9e0906102845ee71f22071068cbce4a59b81bf117b86744625e6ec1300c4779995b7214fac6517db19fdb4344a08054e0815b492476527a6b0383102ddc1a0108cb16ff4c4ae2ea2dc9d8e5200f962a5a74be25667076c448723185bb993bd1bf52020dd6984a82992f1492a3396bb2cf9d69c9d7c8e3a0f1c32dafee66fcf9dc382a4a28b78d7a3d8bb0d6ae697c73698c642362140d0fb9e04fcfb7c372d2d1799b280fb6fe3617e85ffa8e105eb1cbadd68e0b2ed578ccafb574506e9bc0d43b47514f2bdffcf593ad938c4439dee5c3d7b18b1d2b4d6a12710545cb170fc63ce4f4fc404666c12471449ed72889df0c4fdb86b2982913e20efe53a6fad5acbd02db773316688862c75a9e77841454145f9b9d2f923b9cdf52c229985958e58f96f99ff41cc5ac5841a40447d118c86a3b5ce4abebc1c941faad568889a95c6225e98c8d6bc63cf8fd5c5a3d519d376101f1f6734ecfa3f135bfa71c6ea582419b66861dfc5581f31492395c94e6901a95cb79232c300c2bd780da9ef9b48573c1a5097f26a1c651b2a5291967fde73515c395bd9eaef8f17c174fcfa45c9726d99f0ac297555b7a68b09a043bec6fdb478780d975ada98d0f2fb8102419c21de644f85da9a5720af92c6887031dfede413eee95b693aec6171664fdbbb57e2a38cf75246deb3b33d7869eb605f9b3026301357b5b1a88f698ef9d251c6cf9ae95592b25fc72ff8fd3b1d6691b363956f15569fb00742f3a21c3f2c6553d7fcdc27ab944fdb83d8c230c485b4890b94fb38da32a898d227f2df20f20f3a79df65233e28bee05befddadadde77f3475565274656d601abc78885d34a40c0d45e530a25113e0bc1045a0f07229a67083528d365e947115d784fddcaba1b70f81ebfcd53a7cd8974d0415e649cb58b2ad675c8627447772394a4758e078a5d9790bf7c1be209af9f4c9f9e5b5385cd6a6da88f85c58d169ad2d09db3250a00a754f600a928224d58bf6eabaec8626b8a50acbbf9d807e768fdbf1cd607a922e78a7992887dee32daf9993a10b3aec133d46e1db1b8f364df56581b118cfab844e5fff74a02622c420c14b358b29edd8c35bd10e7f73c18d2502406720a3d9dfa374251701fa8104d8d8fd502d68dfaaa198612e1e00beded7394bf768a88428f97f1e888a6cbee61aa83e38b96cbd9574af744175b06958aaa26f2127f1e606e6ffd84a55af86b940b59e1351756c4af75fecb4b4ecc0c78c4298094b2592792c011327ca92658785eb614859c2706cee3f574dd0279de7d55f1bbf39b0157e01164b0b488c491632c022c0ca5e3220e9b0f306fdc0bed67e11f35fda5e4102a1cbca5bfa81965da758f9a721c84e98af8ddbaebedba3d8a3e5dd40153f25eda964670c79dc70d19f64a0281aff0e65168ee4ae5e60c60b5541e5c7c0817b7463dd0608148614595f367a53b04bb7b4aa1758ce3d87861de5703ef4aa7b389a92fea463b362e838bc2f47bab1e63915cc8bc3ecd93427a8bd14a93a8a4d2a1421e86258df83782a8b5fea9bc280f7389864f4e8d2c9d0b62cd1a748080716fe73c4a8d928ca8610ceb58715a033948947a3cf161ddaee44e3b8b5635e6a28fe2f0e619f69dadf68b2e54b2af13b3621863aaf68c50915b200b4a0f73167fee58ee1098c05890fd00758274aa3dbb9673dd324aa10549d64e4e376dbe8a8d686588fced65b75979f78529ea66103421c5ddebb76587de245e7a7df6d07ea684df6aeec2df2f9297ec56beb91b140eace33b2efa5de3168ddbb1f229bf57155a341927d4b580077dad2046773e1c09a1e8f0361122ec8395b575f6f0626912d92946568a9dfa8162cc0d671dde934fc99074c25d6c9ef818c395e5a89870095b1aed98899dac2fa39e2b0420723c564d2d862090fbd28ed5d658395de6a8366d472d558a4939161b89b8a6e51ffc59b56b2785d70185fbf529246034a423bbe36767799dbd6328ea4aaa2f5769f39badb12825d068ae780fe62ea4956003d1b2579413f4b323734be2c7e58b145c2001fb41e9c8eb7b8d4eaeba3e4789cd8743ffbed29b142ac66cda4a4e055dbcb351a8c1aefadbc385c278afbbbea5e41f4d8008fa972b8f2587137ced29ae8facb6e71f8c91c0bbaec459246ef14548d91d99badc50c3e39638fad89f912665e00c83b396c845396fa16e2929af792b874998b75c995de639a14cb5f56389be0ce52c95350a1df241a3cce0f179660082b72ae52bf5cd55829c140248857d6c49d33419610b3b3ba05d32842c41287451fdf6892b18d1ee0c8a2fa41f11056be04bf6917670afbf6924cf4c4dcb99842806632d7c2a01838924c254a934825537d35538137939f3ec6b8cea1ad1b8a1d719c970aae46a2767c00057e47bc322c6c9558045a3742413fdf8bc0b076f96525fbd06af5a37e0a0e0513a5b137b795f283e1dc51c54553f08e1212a314eeda7d35e995e5cb8364e2bba6d50679d12919b7ca9158f01a9e20ef4b8bcc34256ae0a30efc60f54aed6526666eb2477e572c9896261cc2bd1bd7de5725a5d652b86303a36a14535b1e6e3085f6c042162d9a2869b4835091a281a660b20fa68832778a1dacf0ed1fc2eedcbb866e462ca67d2d8322120fcac5137fc91611e2c77889b83b2f132680bf6213e41909a33d57e318f6e8ade0a45b8d52536e954280dca7197c9b8caa9bbbd03d5f6563e2700ace77f57389061baa46ece2f59939f9e02def0b7401aa98e117f61d340a2f9996f25f9bfa9a521fab2345ba0d95993e3a004b81635edf1d172d182d5ff0aed92f88127cf5ded867eeece4ad299f17cb16e192c0316ec07398207c634534ab974586ec136dd05ddeac6c0d5e594732f903e14d8cdf92dc3c32c0ee0c8c23fe6469a979ba321ad4b963dbc5ae3d61fb980e5a8329b701dba74fd3bd29f97e84c423161d2eaef620b0cdb3dc515a518305d4dfb9731fb114cf24951ac115251ffd4515dd1903445d7b9317ed033aaac88e8fa59a464f4026539b96c4bfba788e8b7b468c2f80c9abfade9d784e0d56acd694dd5a413812b0a17389b0a58786f9525ee196a8a444df3de928a47e97cf015d3e5552468e7f32de840f2c075d6c10adef04e0d2550a41e937ff84db567779fe49ff4dda836a25de819b6622a811366d6a716a2a621ec1b6bbf49385a90c4d6301bcea1a59fd2228ac854c62b6bc8955dba8ec29ba46f44a9dea523343d4405c8a9c6662697b96106e128d0cc9a5f77ca423b2c3cfa24406f31a1175b2dea81a5a11d29c36f13ff3a09e97d2a3c82fda68067f3f752644571df64fe8d3903f83a75737bfdb3826e18d86a3d8c502fd0799b3e6150ad5b6ba0e9f2bc32a6842c27dfee9d6b92842c5933f245a986f556029fde4ed2aa0e11138ddd075cb12c65a53f4d1600e214075513a5e9661789a2db156385c3dfd3e3c3840b249ba3fb40b9fc01499965c0cb31cd8456c03bb25cbc60940152051706e237a682a7329c37075933cb73b418d0c9717e2efd4b749dc5dab5260945660f33f99ed350a21157eafca1906d210816a72224b20047c3e9404b929022f15c44fd90349aeecc5ac0a5bc8d40fa20b80969ab29653d713abb33210aba282a309f285abd37cee63653464dfed9cae5938a05084814deb448082f72fd324849adc514dba0850c22d296673d105e059f4a900ae3dbb38a1d192e4011c68712af4a8521b895518f51a0e7a9cfe7e9bd5153b1d32be3180b99ec190edd6f5457f512989d0113359b05aacf22c47e19554ea11c4ce52ec6f098d0091e893ea364ac25e39ecfe2d91e1c49540eb81f48d3ef99bb9c13354fc67a6ab7a9a093b953fe4429063715ec7ac873d62093f496e6f108b5ba88329c238e559f7c6f286b0fa98f9486eb9aaa42440799659f12c489e633ef3f05db29f39c0341c1d02ba06a4bd84c1da41017782ec864156771172ac7c11329430c9822f271871c6eebcd50035a9d8902427a74f60660e8adf3bc80e7b7e0a770cf2504825e87413fdb494e5fa709a9b2c5139e0311615113b35da33d7a145272fef349129c79c43278f5de1a0b2eba21db274d32fcba6c367f3804781ce1eb97b9ce10e74d32d69cb43824a376aac7922e1a49525f8a2763024ec5826b6f63589b6d3a3e32a96a357682edb246bf6b320b3d9805e4af4dcc90ad2b0dab2e7c406c00ccd50062a2e4cd104854c0c2dc58d4bc332e2a269244af21e22f918cbd7889286e88feb3ab70e09155649c7752c29322976befd2c50ab95764e1be4640246404bc7f257f68fd03022e493b65d0b3201df08c4eae720592405b558950689ba259c261d1bbe8aace17e43eb1fd5ba5078c9796f135c1214de151dd85b91a2b6f584e8ea3635c0aa0210d6c130afd6a9e9576739a189948899099e27fbb05d82392cbe5181753510054d44d231be9cfcf6288b25d5e8d854549acf32cca2533ed0f9d7f30b926b2d2e80a276230adf4b38c13a4e3b522784d3c5ec8e9560a984ae6952d95a9d9018558e890e04247191c0b156570e41a4dd91f9a820f456e685880cce82e3837a4611894ff2ae8c3677181e1ca03eda9708ce2bd6ca74934e5afed4c2975ac792f4ad1fdd14e6ffb9454b8ac7e12eb70e5363c7fcf6fb095e761dfe70edb5bd386fb877fd259c5d49950b9f1cb359263c2761decada3c02153aa5d34a00063d7d0f0a38180f8a486fca5a93eca69d3fe77111a63bd5ee26291c5e6cc7370ccbbc8d61bf4e121acd2664769a6dbc8417f2d14bebc1bd511f4b0af919633510117620472a5ccc32996176131dbb1fe68d3d03e1d25602ecd205c62c1592a9b5de5fdb6fc1d46c16015cdce2c1004ab254c712e18446f8d7cadf67b23c7814d5a64948b1837d5d095c999f92e2929f9711646f8dcaa11d08cc7c8b7af882c472162cfe7ac865251ee260edc70191d2065d1a219f4c6624997afabf3881d81ce393300c4c1845d75f9c540117873ca82f05c5482e700d3228ed44a13b1f52b5df3c650d2edb48aec885cb56d26ae2b1626d80fb31105accc1605cbb9fca84278d4667a7a4ae20c62c424bad625f6e2df7ad9a42297ad8de6decc14546a891be0fd7d6b2b0546d7e6be760cb88de0fa6abb6f7946251796bc2a6407cecdb701090273c053257e5db182240b2fe603064afc3b7c0352a7b23066c74f991e4e4ded032c550bd4604813a7cadd974fc2097e222b94c2fb02c3a1aa4f9890fbcaef9816620da845e43a334d622df7b4f7c4185c1211b5c4ce4d00cbb60bc29ad817628b4c248a6e73a5a0f3e7ee1addd485abb2b01c3239ea141a3a8e5a061a9398061cf1f595cd8b0c2ec76b33c69c44a914d54c0faee18811019797e98a6735932f5574b78801ac1446aa165289bf41f5471f82b1fc3b35a8d88c5a8adf846cde454def34fb6b16e76edd7a65b536f4c7e389665043396e596adc90331db7b6a18f1f96757ef2e26db0945e543e15050d50702218dcc14e5250e56379d50966bdbcc5217660d2ffaea9687dea23624209e30fa0da85185ad0a612b3460b03e9cc413dd755076f9ca1fd78acbb440ca86ca825f8e433898e090c41f08e015d5d56ce3b8c40661d952d9c4d860aa6b611317686cb41e7f469e1365e2911ffdb04b58eb346f1fb4d6ff11039a09db63edaca309f490a5e4699fb99d3e8d15328d6fe91ab477139ea261f257e17bd8d0cc28ff056c395b205059fc8c116155484aa303ba4e3f8817be737d093aadd9da57f1a7f00e61b56127bb5fb798cf2da44307d7cc5654d6f33989449c30c1dab9d281d6a5138684914d225a0430df7a839d2d480c730a88ef9d85a24b712582f23bda8ddf940c0913c86afdd0fb935f887ed247385d7f02c069ebf2950cc4dab773db649e6e3bda5887430c984ecc23af480b03da869377b8e7e9c86bf4574cf9b24efb5a952d9a34061387450981b9db6227bf77b55828b8c727d40837abecc7f09a034058e10dd2cc709540f8eee321052c040481050e82a8efdec76a14a1135a08dc6f6521735d61799d8866719c9390b39946209eb9ba505667a5c715384dda5779ea0affdbdfedd662f62b7bea246df71893bd62d361a9963552df0440a963c630c8b5f895f0f1c97b591a0b7297bf25c80a90bf579f178489d2d2063ce94d7ee460547f144a0b0507bf445ea18feafffb90fb23c435e121a32ba28d294bf1bb0df7e8b376d3c0ea6276ac1cfd4371e4ab1ba27ec89c5a4d8aaf7ab1beaf48da0845f88fbc66bb7c40fa290203be6d2351fa24be44922617c17f46b5c945e3063c685ecfeac2dcc0a43926900c6bd225612c76d6635ac6c2782fafca645c1b906c22d52d55953689e1fd30ceb02da4b449b2151e31e99e3750df22095a993e59331f7c0f44c861e4de3441e7548b209b540e4cab86e085a214e35c4a2d8cc18c6dfc666a68d4cb2f7f475759b6b61b44a4aa4e8ec17b28be73e3b8867ed2b111fc75e6f12fbff9f2e51674d14190e427d835675457c6175b1598d85f2dbce1af37e993f908647551a1a3c228b34acadd5d76388e6e3559385f68ce21f0238b48a8e0a275ab11361bb12fded03a24c16c876a00682cefec4bf650bdbaf89ecac023f3d6daba1181d66915d295093cca80798603a816223d6852d5b7094e27e17e4888d828930689d9d6f1faacef51fb833b84b7dd450266a5ac5f63b9777c50b5745aa008b2e66da0a6bc9426fd5fcc64e13cb92dddbce352fb87c17e747a7ed036a5a98e015614d9fd744e08c8d949397462dc36aaa2009e96979d78b5574a0fb8d1676948f89637b699b56e1716a1970197463bc3727042edb80ddf12787142a7b92fb8f34185d010388e449cc7311c39dec25e6806c560e88b019eea58f7a02fea932edcf931a6ffe1ac7c0ccb59c93d50fc9c95dc444112e20a5db5c628d4e732af6f7a90e1db9e627a7380eb0d72d8ad69517fe9b15bf00b77e8a3a8609d8b60d664895d6df9c5b4b68eb06cc4dd4514c73e5e6778a105506a6f6e2294274b24e0e82c19cb3f0e44c716c6d8a4b6cf51e6b59b3543652946750a60ae479d026d5b3c8e39ef6282ed7ba2473d97a83c30f384a83f1860da720272fa55d6c577961c70e8514d09cb525c42a09d9d14dc99c27611ade68d89639b76cb25f216fa74dd5360c3c53464351dfb181c2150ff1f11d2ec26d35284ac391c0c6f573d3a35b26f8443416702693bf9aa9e767a2183e611a6743e0e45cb8afcfa42267b4439a75d4e7fc869c1a4444b30d4201454c110046fb8c99ed3b13aed64019038014cec8fba0c8c677fc617989854a63ab118f734ba50a5520ec5b1c9fc27c79985a65fd68bce015911e1fe9f4a635b289ef5c67cdef5294b7a21eb9a8643370b89ba0ce0db715af40566a295c3136c8984f0f1f51e7f1e25317708f1a2d7261f3fd397880433c8678b0832b6d022a72540662bf8322931ce3c2bb78713e06551abe6521d8261ef2ab8023a0b820897186e65a51f45d04feabeabf57085f0fcde994b4cc89d151196ceef688af8e5660045d94ea6f9f23463f778b765ff0a3380f94c1d6c024483a59858e0e4da9c0183ac0c46611e4080d2f7342210af5be49ff0f1093588884785de67df557de9a36fa9d26244d0aa8a04d8042865a715418072fb251533fa301c5b7bf18f4ee387708f5982f562e407bc6d930980f460b094f78a5afb086ea077a7427cb1b5d8e02860690fe7ac0c2631a10311452f3f904468e3a45dc35e478ba83a0da6e678e9727196dfc555431d3b4ae93067442a60c928aedfd1bdb57e6ab7db8266434a22d3294123aa616bcb7b877849a2618ae5ad68c1890746aed15221882d4cb7a26ca6db1c7b3aa6bcffc5aa99172ac0a57ad8ad1c350a6fc5799b2e135b1550a22d041420e92bed10f89dc77c9dda8ac04587d599094fa4238fca5f4e0f99665db88af8d20ab610ef4806ad8537f76c97b19ad8d9e283b03fe2f3e5635bf1cddd25a5d7a05fb4fb20289abb3f4a3d50814bd52c23a3099aed85c166064722a0d8a170510421fea99402f5a68560146d0253ef44ac807d2f1641628847b2727596a668fb6e46af0e8f6aa359b0ae9aa3bc9a1fcdb0ed72489bda7fd327ba88c0638dde2eb0a8ed4b6f12f762d7a4fd42646060a51c6a6361583179c9c2e39a073bd4b6f58041da8416c2cfc3419a3aa001a7be5bf4c3a69116ff8b18d12db12ef19dd8d58285f174c0c2386cfaba3e8debc87e620ee538b7df7f6f00074e9d55c3fc1b7af46ae5ca9147ab8dd48263445d00c885c9636884818185c789a0e2a2ba133a650a06c46bdeba4ff9d56eb44308e4d73e99e00452cfcac6fbd1162705a96633699e358ed80a94c69482209c7816b5f27455f2ab73c9a1d3fd18d3deb2ba6d681dc46627142b39780d535cf1f5afd7c61d7d6c9bf42641bb917cb60d8153a4f65dc4d6975ce7bd9d292d574d564f7d4e7f4e68d0c15af190412d5eda8075b43d3d030da01caee753dd6ad6ba3ee08c7599a3c696c5e6a9b414a7d590420210e1c7ad41c217eb8706cea47383d5dd9e0bb07ca57429c43b640ae68b45fe06d594797bc77a6f4d95e723771114d832075b00eb7047d8dbcf501610893bbba9d5049f1c9625c127809c5087ae689f1cdc64c70487ec62a92fd250f842edec54754a89ccb9f4b96820dc2335715cd766223f2f4b5500fb1cdec1c434442257acbaac1ac8a8708b513f338fdfb96cb73d453212c5fb1446b9a17b0a13fe4f13904efcde23de18ef237f10384e10e14d96b566ecd52b4aabcd03fbc7e25316a9b6d8809b8c272380d067afdd662eca537ed37a3a34c7ef2e4da8358c9536b93ab05dec6d218e96d33526d5cd98d0edd72f058ecb27bc02847a8d200619ebb03434b4abec4ab6d7f594e26ecac15998dd6a366dbd4bba230e761247a7f7fa09e0e7c0c2c4bb0a224725db5ff205c01768f03d3a67f0d9c25b059c522fedced944ff432459b148b1107d7f4d4be0a4c791901cd33b942f8982a5de3519b95d743aaa9a65042ff62c0b6b1556ceb4f08e2e60da449876634c9786e0bffe4bc2a2b3d8b356bcd1b5487c17edb057684fc52e9bc1fab237ee7b429457348a383ff665979e5f2cdb63f2cc1f0c79fc3b5ff5d810632c94c3057af05ae1b21522c8d7e117ef6269990f8f99bd6a8355a6a9965a636307cf1c9cf8ec4ebd881c3e80f251c81090698ac4b1b1c48d57aa5f10401b3df701c014ed3e5e333d16e88a8491d5b8c07ff92baff774c3ef59dcc948780aa2053e608e35173680a721aa42f5b84e2ca0d985b7ed470482c65e5beede00caf525732e73dae281c6bcf3220432eb895e067f2945b706893c74710d82f9f0304142418ce53b0eafe417cb2daa631bd4707f2d74c1eaf412a7a2b88e44411c77fa0f35fe5d1da9ff13a5add92a96b630cb1625700347a07c50d2b676d317c9399d55a0adc9b3e013ef09128e0e75d7a0dd3a152cb8a78a4dd1c6b04221e9a52f8ab6e4741fc7255474614c6701214b9dd74d3f8e395bf48530d8475f551955ba15e0f2d53a35199660e30f21bf671f79d640e973344b7d9e83f93e158fa286680a78940d07415bf7c0725d94634d308223ac345dc259ad367cc4d765ce6afdec0b5684aea1e790e94658327c2b419cc234aea14fc2c12d4488032b44cc396d6c9d2a87ac8077e3552ea60724b51f350bcced4b22a10046f61401d31a2ab443ec25d27b9ca3c57e3d845eb443cae2f6676964b960c1b9d92513adc26c970ce28ae400d94c5a01d0df68198d69a2fc63d26dc72786468d6d0c44e019fdbb8af3ecc839287f8da1e20a4a6904b5c60ca47c2b84f8325475f7e337d1f2462d532b4717a256c1b08d527e74d92e21f57cb59a440916cb82a33448671ed6b9069992f6dcb4cdd5332c7d044e77cb678403ac2a55e3944a47448e8dda33aa681e5b54330881794b52d66b57b673cdcff32d855f7746b165c0906554df50c012b85be5ca91a365005050e17940658bc68207534b603bca17e9db41a793bb4e0d688e140a688a52131e6e212f57ca6977cfcabf81be904d4b451d55d99767e66b94168dc3ee045696f099d1e15d76ac8cf8e58e6021af662f7f15338821988641138002d6ec08cd8a8ae08b7e6afd1e995d28aef6d21c862603564719f3605e53aea2d5119520e5d0f3e067011013e5b1e69def991ba61aa1c79b3bc661355069f4f241df568a62f371a4763d6f4e43403b8bfe32074e4eda4a08703f130c929deb74d36e398a30ead9760eb0cb429873b28c62c532dd44523b09a2d6c1bbc8d3dc7a8d7c834addc8ded571ca8b7bb203d1c2aa6521766b445e55c9e09dc12d1ee21509eb2dcd5bfaf3081a050fa39aebfc7c183a1c8931c0545edfa33086d84a1dde12f511fd22556227a2b2fe691fbbf0dd1b4c6dd50388d29dd3cfbf174c21b126603f59017c14dc2442c99b8a7305cffcd79103c26c46ae552c40f1bc272c81a2de41c68a64e9ea365a7ad427fab746a0f789e809cc10e88b6d50efaa996511fe0cdaacfdc94344fbcf0f55456803f93aef1ad0d9e59da9ca90db447a438cda4f7c258f867629f3dddcb5b81aabcad06982332d2f132e92651b428c52a0d6321a6898552244c2165cc732f2e6241cbd0dadc627a04ab60a3226fe6c233bfd6b03f9e14a5f0473ede0ec65abf00a1959e516d6b93d51d765558dd0e489a95bfce4308b053c5e737082b5feebeaeae3128d77be8ff9fe188c5ae5fe295bec1905080676ae08cc6c4edba5ba8c2192cf85cae8896f0e7e0d73843a215ed5dc449d30875d751f8424d79c6e96873f406e1031d01e0602295e063d5b0ff92db337089e765c42d833367d0dcd1d2c3666555a7cc6d3bfe739e1cfccf5cec4239ae4b3bc47ec71d8899ee6f8b976317f8e383da317df0cda59519ca95534c7e6d9ce0e11e95e36dfdf30e9b6444cc1b3b893d82892da9508ae17032e921d7e308d8523ff08a68c950bc2880c07074b5af29b5aff0452accbb7f0f4bcaf6572a5817f56edb5579511d164300e3001507036992b8189146db28b361167934cfc9d6d51e656782c602d0392ae3922ba98a886991b44635eda2a09b6077e9eb3f323b5892e3e96889ef5e3dde36917d38d9615549836680b93f16dd6e8cbd6f5656fcf6303c4ad70b220f23349ec8c8adc9374e2ed40bb6d3ffcda0d0e8f8fbae4cecc85b8c4c267b6ae3a703b434b43024dfbab4ff7db3bd261b0c01947498171cff505ac360351e7258b0cecfac810ab662b80cca1ff43d0a19ee59ea1e7d3914beaa1772bab908ace9e8a9e14017ae08cd33830283d302fe82d0a7c9065c64c66e6c50a1d9a124e82d7e2bcb577ba5abc41657edcf79d54caac68efd28fd190a1fccc530057a734d271a85d9a6a5ed4a2c399f5043e5b95559a148c9fd8423294b79a22eb178df180daeb101cdf66a36af245d73faa86df4b1935e6e71179989187e212993b22c2f687c4edd7c526eaae5195a6112d2c43a5429ac7d71bde0522a97176c3c905b5b7ec4afac0197965f64096fd3a0beb759187d9d34af79aa7e017fadc9f1d2bc34c8f7d0dcb3cdbec02a79a0ee33e9200d44b024c1523e90a5cbf62b76cf04604546d8671d1b9d17ba3449f85ed9c055ab6abeb4810c20c4a6fecac316ceedb171fcb44512538305271c926eac0e9481c7386ed71b717c8734a2041810e1e4db6ed2ce796a47019f4d88203d03e981b7bb39a06288b697a13aa5e3d636de3d015bdf72c86560bacbdf24b696bf247a58fc1712747a52c7fee02dde84beb25c84de381ac5eb55084d8b24da1890e825e0dffbfd4a569b5e8b92361e74c61d3143b43a10c600a02232450e75d145b1955a866815e8fe68ddba65743d91ba32f59d8218b4e3cfda428b83de0f239b0431dbf96509ab3de718f3aee5e44705f7006a1bcbf83c6bcc4916bc8dd8baa8f672db6187b978e5c9e5cba30693f5d99f4983e96e72165936e2464c2bfeb5ca12724c16be991ba9d2f2861557be143d3bc8f5b4a1a9c0823def944e7e364fee60212282bd6bfc53bc7d4df078a71cd1749394d09bde18ab226267b50962dccda8b6792693e1a031a97d2acf26f389bb33539ebab653c0b9c9258b3615901b90b744f65d2fd1563428bec53063821bd46c6c114706f04270a71cd914e3daf2dd8b1e4d17f638bd483bbdea237e26f1fcc34d9587968d7a4f365266769e3d22ac5a8f3ec48ea6009adb13c27752aee198357d8d4035076865fa28c820fa92853c25910a8137d1ef2421d33b89a3eb68c1c0633e860fd9ba38d114fb2c6dacdee8f58349fa0b83b7406bd9e21311274ab873c81be28dc4b2429b7c87ce7035000f735e74c8c02efb5eec3a3500593d3e9fcce34856ac903921d9143217b5de9ebbbc10583f18bf23eb21b3fd983209f883b631f08995623fa97fc4c4857634a98c68fbfb7479e8c277183a35e6ec7134d87a01f1f9690d2fd67808261588a57684e44614dad7216ce5e0dff5f492aa1638e8608178c578b7381dc469f2942f7172564027d37285df945c51a3f026d0472d314661a42cac068e1467f511252b417e648eba3867be710a2a02b22becb12881d597cfc1d740e905c9ea11ede5b5b4d7782de6e817fcd001931b230f1e22b89dfae595d103ac460e8a40c87dd01227ab25a68c18e8ee83469d9e11c1017636962621187db3e5a26ed3f2b7413cb614bbd218b1ffd63c4157ca1bff577c22612dab686c672155268881db6c814bbc9198973f842641311dd70a6631f929eea18a18a666ff9f838783ec292820584d2e04c69819cc21c5fb4c6475562763721ebaa46b66ac147794887ac0d2278be43759f31750c425b843e9a7b1f5aae183f78902a46689c183e33e964ad4e48f5578323f8031ceb38527991590c0b606f55bf0b6a75e3727d10172d7329106f0403db98d1e2e7d1910bf03d11866d6cd9fe01ab21424391365a439cdea2f8610dca35da984805499562ddc754360e00b0898305ced379935d0d2b4738c2a021e6a85dceee7c724426c2918c9646b68ca6426e7c763b33b8c2b889a1c3bbce5e15c20ef1168f93e17f1ec02d1a7911949c1430227298644a6ffd3fc8093c69b57c6550a44557f0d38f806a42d7c7f93d4035f45b49b1ccabb0bae4232f0ba214c499ae6c64a69558f7d948748d3373b903199a1183a9e6e4929751f8bced389f9555600d2350ce5c74a8be6c071c6f723808472bdf34bf43ea44d12da0b10ca4468be0c28ed05fa9124116759b3e234aa97f43789caeb14c19fc9ab3299da8f6924556118898b2ebddc16940fd59cc0e5811e88d9445c60f034a70c90118e9d7b6a7568fa8250daa37e8f7084e546843e2d5d3213c574043f873d4e6a050c4e08881201145b23caf16fa5d3d4c8e2ff5aeb23d450a7b50c466ce4f4ea155873b21cbb491a52204661e88c9acafc2123100494ce8b963aaf037b915d360ae65fefb43b01994293fad638ffa21e51996b02848b61272a8cb61a19d04d1140687ca61df999568ca6690123490b2751618b8e1c020a32084425b293d2f9f6a03fc4e18ca1c6d0ad1f80b396aa92d9c6f638a771095e850f8e4704dcd1fd285e18f32420b56be104e74fc05e105017c5287355711f3399965ea46024a99f7607d51aa224cb9b35ddb624bffb1d0ea2b29f02845a3ad3ac3faebb6c51bee21664a388d9e046b97ed163bc2944fdf3d43b6594622cb38c26eb204647c88ba750ec9db5b48cb34c20c88b2cbd5128d80d9503d2d9f0ade84eb5944676c1a226b5e202db4d383e686127e75db6e3c24af27892f09da937c01e2df6a36ab18fcb602754fd20b41ff7c6d435f082cd9ff655f0b3ccf601657014b21ac00b9453c11eeb87085211e2587f4c3d7f89100fabda9b87da0abfbf9f51daaf694effd136def48fcad14c83a3995daa7f70e3908e24a64b863542b122547e7a3e10bc94b869f191c9874f8b265e355c0d047c1e4025e1100da02e5cb406b9e77b02a7128481ad5541e1b055d55c985b2d2a3e74859a8bbc2d5b28ff8191c005baa283ac50b7cf828f9e890b5282a8133c5988cfe85ecc57c6027106f120cb84ccd9aa6f706dd05cc1aa64696e009752f62262edf622f36caf5217e510db6ea75c02594c76674ae34e386fcb94ccf9e2d9af1b4b8865763669553e10b6ffc19fc3ebb8c2f99b37cbc65e2aeaf44bc1c99abaeeace0b09cffa5986e95a1c266b62213a5ac590496120bc204bd3794851646b17a1fe7a1cf69f32bfa0240d0822611dffe4fea293643a5d238eb0e6753bf14b0674621ef04fe7ba414477680a550f242f96ea18bdc892b6fafd6a215bb12d34a8cbf77ff7b98cb7412a414d608650065616b04d200d20de550a6536ac34467d9a71863f823a1e12496f73dc27be7e326cf85615923ec9226ee874c8fbf095432d7abd533a62b00480cbe9810e606b5a699785595b341d5e557c20aa5072e67d68c8476f6a597526fd0110398d54549cafc9222fed693ffee4e1f6eaf82d6bd78d8761e248c93e7398eb2504f5599226568a97ab84104b756762f7bbf17eaea0494472d52cd0f62318fa0588d97e47313f490fff4258cd6ae070e969b369a6faeb9d1209967dc576ee6b6a3f8547338781ff8111e69d22bc0355f52b99e1ddf6172481c2227671d8148e74cab8d427a067e839de1d3ea1e4c6df5d73cf724f7a5169df12607f03a6f6457cd4ef29e1a9303b8b40c965267d83a99fadfc9c5801a3e9275f7cd34a682eaf475b9946c64efdadf5e847e792c2c8a0777c04c96a024ea770e499b79c25d56c8851960e0790b74217c690017a0ed0dc3f04ce9c54074640d37a28993d552be9f2b65b436a7e5788e2e095c63fe8de6652c0ae4a9e0e4e730d8a7f440ce0d5169582053dfa0a67d0c4f481280db045d67eb1b07a7ee412de1dacecb3e24a21dbf53d92911a6d8bedafb4bbd5ce1e6215c948c3ee29bb907ac56fee148557249e2ac265d02e1a1caf65d1c729e42a444f700574d0156c50870963877edc7d9fda68bc3e929f0d45a281744e45d94b66755d92df8dac9d9b218f51fb633d0421ab2680f9da9282c9e5fc0d0536c2b236b5fd23a95aa72b7431a3c282e43c9a4cfa89730e4ca4db92836757c31e7a2db8a5c237ac4799521bf14937ec31b907ce754b719a02543d0428ea312f32f1960d3b0779d72e516719b73981a8890a08ee2fd7498db40e31edc67e590246de536081dd228fc586e0c83c0bfbd356bbcbeeae0c1e10b6646299800c77ce5c649ccf6fb88b0fc2d7ee948b576d9d464639e2fa88206d0787b59b61c242416176855e47ac0e2770026bd10ff08ad6e6e6674d7e5000e6dd15f05eba9968649db466b9587e5d4fd45a94a352b0b18e0e1ab9db02da79db08bef9cb00b6bdfc42d68b3d41769356c3355c7bf4c18585ebba659b9659c14130876df767588d802eadfa20eec1b92eb7cd782730a2b9c6a6b1c40588a163c3f2a2c78609a466523ddf1a164a87c205f15fe55e369fd1d9ff6b96f94848ea8179184d9f5c450aaf86319f073d15f5872912b0fe0f763b5cf8d056cb6021ade49625b304eca4a07ba1b9f173133c0a6eddb6f673c2f2e74b0be8adca1c56d5bc303966a70d39adbefebdf9ff674deb02184c1a64e28802ad35045c46889ef1e8e081b6f9f7ba7c951d0180afb5c1c8bc7fc684b42f6de9bec2d659249a6c305b9059b064b393c2fda02c68fdfe59fa0e9e2fd9d6db4781785fc3f19fde29f2d9fdc8ffead7fb58fd0fc97cbe572b9441eaebfd116067b71be9fbf170fe962eb5b32461741e0cd6fbd87e1e83fffc674cff5f25b7fa383260c71dce60bd13e15c71777e4a79c7719fdf2ff298e3cd71f20aff4f70fccbbd409f1fec71090f75d08026f7cf1db6fef79de0f1902f2c421aadf4491c7bbc4a5ee5f7cb8e47d0bef693806dd96a8e2c4aeebbaaeebbaaeeb9efb212ad1ce1e7eef8770a28b84f9e389b0d9d3afdab6cdda9c50decbe2a0eb99f3aa8f839c7710d62f16072e4e600848f3d13dfd38a00d7bd0cb124940b35ab1144af7038a4991e453da876352185177e7ae0e519a03a4e3ee74fa83eeb80336ee805d4eec56bd4ad5fc77525f629162589489b95122068595d81926aba4ebbae3bed284fbdb1aecc8bafed6e79c2cb6315fab5ad56c09ecf857d3ac0c9efab4c2da3871a1cf27b0b6e572771723add8d15eb72d97534a7d90b5566b831d5db7ba769ea756d6a4d4647dda345fe2b4afaf89429aa6d16ddbb66ddbb66d53a91a9603a5b6c98eff3c2f09e38dad958a42b026ce20dd019ce921ea89a6cbcf586eb09eb30e217439d80b742e5a1ee8bb6ccb3ba1edec699bc58eacdb6c43e21080ae03be528a3e58f9ac813751306efc591358f720f084d2e5b91c339354b50b60bcb1a21015978c1e88da1d6cbfa4a1631e2eeda3bd1062bbc74bc66c221afdc3a1c089e3dfeeb7ce013b2eee74f899a9f9f5c225d673e192d103b1de431e28105d6d3e6db4feece02ff7eed7fd9b1375b8a7a2d71f276edb7f5ff8bd87f3296a96996a888e90a6345dd9f245cacff85451fb745854c573ff74d47771146fb70ae56d3347d77bd5a7636ae9574b5c5926f67e1cf4e8802ff7abb0efd7bdd471c296f6f17ebe278eada38a53cbb885b2056e6ebfe600c985d30bcf98af1279c8b9a55f2c295df97fe9d709d7af8bf38967b060c789054bfb68699f245e6f2670d6ac94a5c652fafcaa2dfbbae8330b1a34513e5a0c9a0d7c83afdba7b863830b1a6989723fc904735919afdac106816407ecd65aeb0d5b95f4bf2b9354460623efe2f37c976365dadcf161d4681b37b81c233375c1cb3132462e0b0a4aa9d33246ab322d1071d61333b4ad5819262e77395606ea8ef64a17827506471b619981cdc9320383912192524a29a5f478463b81c60933978502dbef97634e3871f93be684d47549361d6bc9958233b77fc7b501fe6ee2f6db66e28e5e0bf4726c89332bd6c0dd3f833bd6a18a8203b8895bd9436d44e1d68dc79c37a001a2221117916466703bc91f0532b8b3c174ac25195f90aef7f43b2c41de568942f255ddc3c7d67d7f38bce7b8db891e705d1fae2b45e67a0c398cbff7c5e87aaffaaea32ae619fde9f057bdf737ba03edbb9761f7aad0fbf9e9d85eca16e577e1d8dce643caf727c0f5af1f9b3dddfa5a90085edc4c20984efde7798db081d911055a81d41d5bb4fe8b296f3f7304d1d1f541cc08a74b9f46ff3810855dca5f5a5709ba7dbffc2040c2d5bef97dd4dbcfd33e3ce49b1efd3eaa0e0c5ced37ed9b359159068b3854df07433e54ae3eff8dd6bec38d85c6ea8126eae8e8d7be9fe57738bf93fdfc3dbf451dae9dcecb975b43da9240eeb87d2c054eb66b7d1d6c3f9ef0467e15c70672b7efb8896aedb9a9e956df33f3eff0f63c74b9f0fb2d1ceaba4e677bd57f4f6aaeff9744d1f55f42bb6e48aa54aaee3bd93d8f793bbeabee857474b8c6d274682c6dfb4ea5e2441cdbab5ea5c5ca7521fa2aefb7505ed5fc70701cc78942f447ff12866e0943d77ba1fafe72acef892a5ac3d1671361d6400215ad9e08b306f525d08dc9e3c7f570fe2a6420e73175a844a1effd67edbe8ae3f79da8d3dde84fd4e9441ddbd7dfbed6907b4d68db341d9bb6a9c4b17ee7bd8eaa863a2a1107fdee3bbe5ea65cffeebbf684b81feb7b9ed71f8ece83aefbf92a715475e2c87dfdeeabbcf2ca4b471a1bac6043d26be1e9ca3c13e584441724a8fe50875259431dfaf26aaf02fd33b8f32b7dd9b56b57edbb6e4401bd73448bf944b4593a35e1345d6981f68105893042fbb42fe19fa1245120a961062d2588a209a01f0f8a13582185133653d200f5cb27fe197ac284992c474b3d64410450bf84e21f0d40910226054968f0e283d28581821c9a73474dc73ceef5e5d6dd218be336e7147978a452166a46e01f66e629c27863e74b246aed6477c18e2c3502110fdd48e36270b905ffb00ded7d3615d7928c00e7fc3b6c41b945c7d7a573bbdabf3ed17b3d7541b4c59d7414ba2eb678c6b4fce2d77c55b8b1f36be2685d6f91c78f2510d0684b9db38daede3dfef2e3a0687b16bba31ad0d742660dc69c4b9fd9001f1f803ecb0aebfd62db825580d10355b18b9dffb27b4dc7dd1151672c376198031df7a454cadd9d524a67b4a4ee9342a19973d2af15ec97f31bea4e69a54c9999d97d0a31e5d6d6ca76cb9d610f79b5d7348e69f9b285df26c5d6faeed65ad75490529620ef645633e72dc2a7e6cedb56bd65b9d05a69ddbcfa0b53b556ad52d653d5344d6a539b1b534d73e972d61e761c8cabf69c5c41f76a75f59db5bb7bce5a679dd2322f7515746f3f43d5f706fac575fdbed55a35773a5d6efdf2a2e2ba4ee5ec9b37bbaed9e86b9ab669daacde9473d6ee6e4dab92ce3a9b74054e772ab9bba5942e178e24fab54e50847e693f7f735d605bb56aa1bbd74a9dbacb77714e3696c8325366596528e37a529ab25d82dcafee769f3dbbdf59b2649692d99d52eaf37bca6ea9654e6dc8356d03a97fbb2b31f7a7699abb6b73ce29a5bba6514debbaf9ac41a8737577d002fde2a8f69e47a94b4a29bb900bed94b5b26a73fe3c60f3358ba929a232ae27254a37073be0db4f7f52965f651f7d6b9d3125adee6c925c8eb1c17247179b26364497636c3cc6e649d2ea5b75093a15dd9170266772a6f9d34dee347f4468828272262867eaa6168bc5face86b1633755697553abd56a75934fcbbba9a1dcdd553e37f08f8d71a5f8add0a9f4cb9bb65cfa31dc9d890496fb35f5a35ff44d70a4d9c336f847724f8ca7acbad2317e6c758c1f9b4b3755efbf6f80f1b4b71d6c964b61fcd841dc3174eca84bff5b3160c48811e39dba296c2b542cf7f563d07fb18fda674e6a02b52e57bfaee4291827572f572e63bc94224804d4a74f01b6e14fbf02ecc3853fda9be3ba39dc233fe7b970f24b7e0db718577ef842c60861bcd9bc292cdfed55b5db3cf975b0bd5451e19a9898baa91baaaf3497fe42b5dca0a00e871095cac4b4a6e85bb134d6aa655fc1a2a59b3ef4ad5fdcd75048fb1ef2c6f8a00fd4a8595169e271b13cc93a9223d92996c7e54c4cf57f422c5b9edc4afb7c5a2efdd1b95cea4e975ea92ceb4cce44a589c7e59c3f0fd67511c68fddb4bd8b1f903882fa45ff837e69620c3f1a33cbcc1fd6d316efedb3cd9f568a41f4752eced891b73852d0079e8444a515975f4e1c4f9868fa45a75a4b63e9a6d9433f86d24e38cabbf3f5c3a1dd2a4e37fda2cf9ab2f23178af5f2fdef5ddc436240651a00b97eb6f3400c21f92a89be68f0850dd04d54d50267413144ff50e4eddd456dae9ee847c75c20b4218e112eb5d21f7ab7311f69077ecd16b8539610fed8e0c457140d1ce1f96b8c55a6badb52e05a2208738cfe08625312b8a2c09ec0cb1354b626ac6c4d24cc5d28cb161b4ef64eda10a35f7ab387359cd652ad45c6608f08c53ea6e2acd279d4d3eb13437cdd7d5de82b153fa356537f9490b35ab945c81848292621b3a5453aac6d0d9a5589a2f445461c5112333474ea4c8a206204e736d09bed47d87bfb1bc55d775abaeebbaaeeb3aeb596fbef8d5ea85b52fb68de3fa7a7f2f3f8edbb81bd3b3bf6d9cf801189bb52fb8ed5fbcd066bbb79dddbaeded26f2e0baaeebbaaeeb5ebc7df142ecb7d65a6b6dd759ebb158615115a1fa5fff579f0b2f30b2decb3777e47ef5df77619124fa7ee510e0fe13474f1cb7a5eef95b327aa04e44010654a5bfbbfb876e73df374685536c8a2f4c5b8e13a5189aa6cb31299c625230d92adc4cf6890d6393926c920d63c3d8273629360592fd52ed129618356adcd83e6efeb43ef0881d5fb7a8caf633c727b238feed72f0f5c41d352ef7f3d3f1230825539aee0e86ba9c6fac457ebf243f693bd067a81cb387d6983d1408eb8fa3c58e7c05a97dfa5bf4212f0749eda32969e252934e8b7cb5392d1a9c98a59f430410891d21031b2022a17fb4cfb8e375a509edd3f5abe8c3a5159e4167384a249c981d25d29de00a766434f449f0d13e4cf347a54ddcc15a7484200623ca48dd1d2c75e5ada15f6ff50f036d40d45e19973e6341627bb6f78ff2e906cfe00d6a5030c80e1e4ae3c523dddd773a50d23a85534aa933bf6b2ae139e7d418f6dab16d289553b6240dbae683a03b5f4a29c7d7fd6007ec06435db9a58694e2961a2cc5525768c8e5dc4146bf2cc0e3e3f2cee5b9cc3576240d77b096b0bb9fd26edb76f0b058c3555d75012f6fefa647f8566badcc3f7982496c1b308995bf44dd28fd29d9a7efd0e719236061e553eaf60a2b9dc90895fb976545a64892a97046b64219d951d45417d95191ec888bec6889ccc81899912b32236a8ec8e0a587a8231cdaa8c09150158aa091425684cc1ded37a51064a63004890b2fae2a8f4079d4124d939a8646b3c288945d5744765d2d59911a2ece655991a43bdaa7ce03a55b8ad856112359ced060a1c911511079620d99158c8e728c463003992d47a294a00265049b253f929ae2054a699d92020f600a433021e3465258c24a414a133202b82c3372c2c84b95e91b6fbd599714c7b9895b774f3aa703790f3e85dc4261e7749f4ea87047d7edde1125092cd42cc55085911d8688a658c144c50d4e78604111d99029d990237b2608304630443101991c6444b258324664435448232b8a2ae3e54916263f14e9a1c808a3127af6d0724592182574385a339582134e0c7101919a430b224294ac902622492ca5544890959028c0206f84b088d89418c309a9b5d65ab7a8a05b81103148ec33a288524a299526656650928510591009e2b442101b542e2ecb8a74d06430970cc8161991982562518a9019028923233543d6c8820c21811541d8c0c60629425c31841701194381b4e0c8522a2b10a82a03a204111bd5e2b2ac28ca91efb2ac88c91195f45d8e1ba55456a4c2a53609b7b6251332c5f52ecb84b0e08ef627544f989021275da2da2079c2224812503b5931410051049525c28c9165f1201609984582677e153076207774492946052b2841050954111b20f9522675dcccaed81b2ed608fb8235c242b15f96d0a3a231ee6eeeb83d4d2c73ccf6347d7b7adaa4e6936f4f2d9fc0d8514ac92fac292bbbd20f8d66c36c369bcd66b3d96c369bcd66b3d96c369bcd66b3d96c369bcd66b3d96c666776669fec93558a41a185896d629bc8f6ba71deca4ac9ea62bd2b925c21e4cab752ca9e2e9aa8e7ee4eeb0b99a6699bca85188ee33a8f4bb3f2beeffb56ac2bb0003d6bad05717084b4bc9c9c9c9c568b1593179e0b172e5cbc70b9940b3c183060c0b840075cc2c0dbf938903bbfb3f3752031f83a007893635728a92ec7dc38750c4f865e16af73aee8640652fed8d9e343802bb31883c5949552042bca1a8d763c24395541060816446e922c95286b785ef1860d0c19911215d6786fa2bc376544b0e60097636d8ab82e2ec7da38d52068077c658b003824114d3101145650c166cc647922a509104b9298451df378e75daa83a3efa5218ebe936a33d4c121afa4fc8160c59614adfd80c68d1c22d0689ff913cc1047cc88aa3441c4cfd0922725b628c9a1043c1801d4fffc33f4021436597c211264062840fd2ffe91800a5da81c2991040f6d80faa7961b0d60813812834e7a1f3202b6e70f00dbd0ea8c036c0013000b6c7d8e1c3818dcc8d1801c382da7e63706e2b3fee3fbf9e86b9f4585e56b1fe7b9f971b09a9c7f1c80b3c58aeb5a3870c4d12fcee77c8d1afdbaa0e7ce201800f8915e0c7eac77fee85d17531c776c70e7c3fa55238d156b482b00c02004812c40ff4ef84327c8ff02718cd12ed70bd185288f78467f8bd0049ed16f652df84b0b9c1416a04394271bdadc9025870410a59951a9e282d31140cd7d3717de689c156e7f0e8dc901f738fe7207b5c2331a84cd1db41c07849aa330553cf174c4142f65e4c81197155430c1b4229da48dd95162914d0e244218d42f5812d895224e5801c3022217ccb09b1bae184144c39127279ea2052a3920611a13c513de42c50b911b1aba100d01819b5ec8ec9cd803875f1e3f2ef71ef8e5684f89a55f220461f9e173246ff70d225458e104cd0b6eb2ac196241c91040c8103d2935818064cd1928519690d24601488e48c3e4248934593a96587cc617f67253532103dd515ae9d7a75dbe74898633749e315f62991f49805116ec027601bb805dc02e6014d8050c037601bb804a8062c024c024c02806802bb0fca05487dec433704892443685682cc2b294e00a6cff0c29515f019bb04c6c2d66069760afa012ee1414e3bd6d3cc13156beb4ee609495aa231d6884e5e77e4919251575a64c9454d474a7b456cd3d5aaba6aa9eb66d2a15c7752a8eeb3acffb66e779dfb75ab1bcd5eacaed673951b9dd6edcb4919b267233f5c4250a8acb13972d920a934c924a12a9dd4cf199331b183768e6326e504376cfe94e65cfe94e69755aaba66d9b8a738e9b2d396d53a938aeeb3caef3bcef5bad58df8ac5b216047170725aad162d5cb87831439c9c1c6f86e3ceed6f7d1dc81eeeedf34bb84e3c4305767471fba9114d52802c124084142c820c21454d9121645290906284103cfeaf2d96bb2c93b2e5e2908265f6f01605055176b8e3c3184c41121c5190a2287db228558e409998184f0c068312223fc0704464bb2c83a1080620427e50c2a2e807a536427e90293d71b5cb32a5315994be5c067359360237bd69f2677bfa32e4b663bb877fe4b994daeee99eeea94f2da3b8ec1d3788830c0a142d58725519191426eef8302b509e08a17a3273c2f4e4090dde131d586082c2e58ef667144edd290a8e58973b141caebb3b5802bbba2c8332c5bb2c838274e965191416da745bfb34b394dd01a952d2ad6a5d800803430a6bc2080102c893c480565e985201a9ff3cff465d4d0a5293806895fe4b284aaeeb899b6b2fcb9e9c01d38d4b2e12a15085bf8ad08e1dfc2d76403e8e199884bc002ea940a607cc642ecc906288c189932c544f5280618d6a07224f702e4c5104e6b24d6c472a122a4aa904b08c604a14588e44e18494e4c397c4e50853120daba41d989e870912b860e585322e60618552ea3268e062c60912388859018836b93489854b935a600232c95c28e27697652e0031443f48831da59374329ae2099268f6f45751270499a40b9a3f39186afed8d9d33f459d1066fdc940edff03051751a822f7014679e5ca9ff5ebf4afa24e0424904e08f5e957716bb1d14c2a5cac0b60bcb14cbf3f84d9f45bd409f27781405f27080bcc452adad9c3508c86a7e6f7e011823ffda0107442a02704d1af1b8fa04d7c9d209e3d3f5000e28f47083f5c943f9da8e3066580c9c0f331f394215991216949d3714f1616cb32254877f46e53ea8c654e59006980305c049581885b976d592b845b6b5bac18ee687f9ee926495a88016c81c615363770913139a3c4973441c1d64456546bad15092aca3c315183088aa2b0d0840a6d72c30ab4895213194025de651992932dd6baa8539aa4485b57cb52471a62a48a6f887c54b87f69320bd2048c101797654da2ae5f9635816ae189110b2d3829025908c06519932aee7759c6a40513eaf5245bb245b6444ac684c895ccf6e9aab10dadb28d4ad90675b6e1936dcc661bcd33d88607daa6d5ea9b507f0db55a29759fb3d986a4958756e7973074db5fe898c737df6c8d06b43cf949b11093d2674bc9ddab963a74dc45b22465926448a6802e68afee544c2cf70c01eee56b9c2654555225fff3407b298e425a28543547032e995e4011f080227066f6b949332fbbc289d9ffb655f89a3dfd5b7bda779ab6f54b6b8d16d63374cc33c2344dd3b4bfd15a0718ea0a69bf41692f19301a6ca37b9efbfc9ec571d1e2f99f0b9770be850bdbfafe9775f5e7b4085be1f7ab5f1a5ff07ffee03cf8bd758ff6606861fdd29e15d2e897f6abb001fdd2fe0b73f44b7b2ffc200ce219dab3f6da4b6e9396522a1d48544316412256f52344bdbe3c974d8bad3ff60f8d6db3b6abd40b36ca25b75b4bf36ff4b861993574cc53e9278e302871fc76c06e076057c87bce1347d8f55ecb693de33cb7c20d44ee9705c39f3d2c1ed7f776fe806fbf1f0c79f6c8d17b6dfcfbfd2a1cedfdc4d1f3780cba2c6ae21c624d7f2265f79cee9443f9b26f2c8b5d1696c571ab29587ebadabef666e71c65666667cacc5232338f26dcc9dc1a5867db68b23a0ceaef524aef1dcd023c6efceb66b1a0cfbcad48008b58f93e6a9aa6cdd989e0bd56a1cb0102b1f2997d905164e5bfae8042ac6c812a58f9b64d8d1faec7f50db6f1e3eb7acf2fa37f5e73878786f2b6ea8fec682d8cebbce6b9dcf7a7030460480026c9dd01fbf241377b2417cabb0a99b9a804cef734ad7466680400208000f3150000180c0a0804028148284ab34cdb7d14000c7488406c4c3497c6226120874114c3500c8531003106216290310629e510190587e00c5af7820665e283ba0c9bdaae8d1bf78003d2623a1580fb17ae79427f403d97177fa51e1a73ebc048262bec8ca7f78a168ba3edda8e3c8320408b2a0cd6f91876a0b7442f8bf82e08acd7aac7a2b330b474eff7ddf595de6c8c5fda55c1da0108082dfdde9f8aebed828df79caf14c7cfdbe2c208648ae3d532e37d14872c30db4d2bd2c26e1cb33aebae80d63e59d1d4f0156d0df40b7b626494435b3306da363904ef7b4a2943492e48f57054a5e498a58551cca0972a706214b0286c24e16915fadd36a5081bdc49b6c21630adf4ee22f0d0482cd392a1a12d92e8e92ac3f8c6679080e88ca9b559b10b9c38e9e087351f9a9880b9044fc2433d7764a55cb40559367ed872a135448052b668abae102d14090eeef78ec7f8abe1bdd81a11c52a99e79bf01d6c5a25b3c0a4101b8c5819a4c8a4c09166444dc6c1af4c8354b956b45570ff63778ccac00e3afbc92c64a5f56b3fac831ef012e6b604a906288c2ec6af60dc26d53aa7aeb0662ac9de7f4b07eeac92484e7acc1eac22b2f1ce60a38220565bf47951b36c9af65e5c77bc4971803c6bd2635f487fe596ca92a513f9c33a539410a0344a8d43747f521ed418b0d5daf96c16d1b9c0665d9e75a90315ce6ff41eadffa27b17f06252b520f37681a19eafcdb0aa80d7f0f7888bc9a8a99d4788b01b47b40ba340193eec5fbba7a96743e077bd3c2406111e34994fead472391731a8cd98c732b2e702e0faab0dbade0f530a48488f30ab432b209257ddc0072f41222e6f0490a743d3d1f70240cc90713404bdc42f7c320fb888df1febeb27f22b4c6cc3323d529f5e791d677f67fe93220199ff05b464f9e0c19447720e70ce3416e0573a86ee92320a109bef8202bcc4ca0e7a9c22af322c2c5f4a2c454d0817bac0433417e158c0dbc57bc668448c55e98803a1015a99af929be3424262097b1282b0e1941def76f0eb83bcd2d523c67a9dc31af7891b575f9926f2bd0f26db7836c67a172f6d3cb559f58700af6f11ad6cffb840f32d6a4ac0a6e81a6fe4c84645ce259f635c4be445d8769f3b78b88b0471ed2ab2e9bddc714063b8a55396820014c411dc762577340e904cd53f10debb8be419ed4d77987491ee47dd093952d4b6dde22ca29625713963fe634d8ee85c877acf10978dbd1bd4e77113c353c3f09d787f3b5aa3cc590bb78a5ae9cabc302535c0b3bf59fd11fbd723b17b7e248a429af2fb4164e8f2906c8ff04b88380fa0658fcddad664d5344605d688ffa76b5340642f0a653ac3b428c0c0675d8af48c55e53424461cca93c10146410ace2e5fb7a7bf816c9bc36209da4713ddc637771310384b8c3ea482d6cfeee0d8167c894429a9b10ae0bd1ec8b3c981b4f1a51a47659928ff66af49f0bd57e24c8da8282ebede576759250df3f85e7bf6a6c0a4f6ce223fd497bada73a975158e071a9a1fe2c0f5870a2ed80ef9f770e34f07586a729194ed26fd2d6cbe4a595ea140c34a219bdcfb2e6ac157a7fc510f72d08a4bd534bf5f793d1eec16d2120d85226f49471de6d19e873c2754adc448b9501d2c48892c73982f2b28636e8484cce74a098f5d6eb0c47435cffdfd8df3b541882606398de96a75ac319c49ba8c9fd3175a82a13405f87459bf3527e613f73af6684960105f16aaf313fbd504966ab2c30a1838960048e815dfdb08a09167fec55d9ff061ef7396d300a8d82088880333dea4bdc90884a6b67ee3f722d266af7abbf66f719b7a1e327c81390cb239f72f40f8c603d5a713c4bfc470e179b07e802f30dc09e3cfcc17083638dfdc84294d15873bb976da9cbb0d3486857ecb4bb9dc7e5be85f635bd8c4fd921d02c507b73e7830f1a6a9d548c1078b1524960d6331e3d470d09950919803157c10c0811b1a23e96a33c585a279cf81b1692b32fd4942e33d84d98702dc1c66fbfbb8b7dfd8c3510c4686d9fb41c3ce60c66fb7a51c4fd41a8b986a22937567f8aa0f68b7f989b7f7abe385b908e9c2c847f7686906c1c18d21dae43e80de0230ca9f99f82fe37699548330ccdea89686ef4a6429a1541b6208673807ebd34442cc59c9fd20fe8c8a0a24173ab2aea6413656c08f60566ca91609c5f27f6f4cdfc958500a077af6d5e9a138bb75f9d05f4693ce91cce636b105522af107650e4ec670f0acd6909724e52b515e0f28a7d3c8940692f6f912cf130b82a3da1e516f845017c3e78898a9296d1bc6bbffca6365b2763b8d7348e0de8347611eca610ba52c8aed2d6709c6ceb4064c9705a9fd4fdf67a945b118cf30199dc8cd3aa404ed244ed0360c3af5146ba2c4fc2d7da0e798cd4f7fa16c00e76b3221c412d1874a92e9ee94260e04b3eb9d3e3de775139f884a53526c8bcce1c406c98ca978d6b4d066032b337d902e3e5a2f48dd1d212ae540de489a9f495f304064c33a1dddc8c6b8bd2088492af091ab834177759cea7a4e0d371b1221b6cdca03d701d57e432d9e905ae36133423438af3ef53a7a9ff190b354aa4996be0e341f382c289d753e120ca36408a7dc60450d7718c76dc0a0954ab35190cd9072dab451246397dcfbb468182624bad9f63a02e0ed718d1de93643a1c2e433c008e1941df9fc8718ea2954853579e185c4e865d7534be9ac64e89472cff85e0c0fef7f6da688cefa84505316bc0a862953d68b4de3480c537702d7ca94442a0775179e03d98494be48486233e0cb3f3d0900fc85531d6df7995d363299e8e5595011f7c6c5767f582ac9c6e1995d383e9b254f0ba1b5e66a7b23c49a3cee42984d68f946253180e1e7e8cb26b93607f768dc3453d5524ff2e5090d322fe87a9e2bca65a7bd0c27961d4e5e93f0747b2f563443ba12637e63a23e9fc09c3cc2f0fce97c946eec8c229c89a97dec4d65208b2d1cae64b1f28e585303899c145226d2489f35f4f9b6fe465742eaf20bc4959178368426d9be00a27035f8f1471c817ee82563464b5dc096f3e049ae4055dd9a565dd693e75fe213b1e222697093e130e990cd750a0c143fdadb7c6a3a6b36895d371398de6a66e4e2d03a8e842cc5e71246cb13c7ab81aab0d2f1b23226a5275386da374ef90982dd543f882cab70be10fa472e77fc21d4204f8542360d9c494d715963d030b44f6765e7e541db31c4c2ab02336cc736a138e646cbc86077d894f2ae93a0adb96befde4f0f68ec56fa85207f5b0cb516fab8c52efc399f86956c95e78c7d62cac554447aca1de701c161fc8acb9a974d3e91d3707c71d952d824ddf22b76ccc9a0b2e0fbde470bcd014912406655df5e6af55b3feca01bf60bab2a4b1a47bfc3312694d7c82003fcf538b2eb6e9c5e88098ed10c351c71329395a11de509108e1632e526f84d9f072369844b94a0b8a62f953bd54fd9e67b4026c5c1577340c72d00869e5e318aa78036c8c790ae1943e6e561958b745abeaab5510e14d816790de13c14bbf309dddf80f3a0673aa69b6aa2a86ee30853518c12b5c81749557efbaca6a20bac0e147471ec39bc9cd11dd3d6fd3156d674933c1747e8c6355e3705381af4cc2f5fbf705a3d7b3a650af3040f195609115e41b051450a5a2735526a753d42d0533f30c108e0eb827dc054ff42710c261ffb8c6d19113e7633cfab2ff6ea917eab633a56c87bd5f85bccaab3be055b8e99083edc80cc2da7d87eb6479225c72c1842fb7a095642e0f0758f12164c279be0867b756c0d8321e36461b7caf3be7bafa742a074b4fa51f4e334e8a0bdb9290a7aeb7ff2b92c7d51207bee90119471d7ae31c16bf97467d9579acd5e59291de053bfe679cc6e2c979f1674675fc88deb21e19cb72e34c9080cfc68642f7db5e3cddedc8894d6e55c4b628be355bb3ebc95a4947598ad6a18b8b76918c87819b9c455e55e962dc677b6bc113b5b265c64b25a13e01087f892d8e5aefafb4100319ae4bd7a78adbb6f2c703e076c446a30e6e289856d1fd9ad21d651f79a9a6432f3076f3522158b54e95af489a2ff385b3943abfa05f2c7bb79b3f989701cf97cd845e64c30f4514a609156076bdae24f717a1de8009b529942df056948550cf4392c0db32f838fec6f8b118f5cbc1c92f3037eb2eff72467b643ccf88d4ac4176032c55ad09b64a0ebf4d6444facfeee43dc15fe0c80a1a9214a974b9811b1130e33c23eb205cb61bd4284e60451b5b9b3d4ecc07c195c62d8205bc38a61eb595013009707652fc7349ea8c0a436e186a210fe1d641cbaec188a2449c7636cf63e6746a9a30af41c5260d14aaa872ca4cadf09c4a2fa9bd0bcf0563341983f7d21899f037d61e2a07bd97c1ce21b1151b5aefc4bd5fee261c5ead08a5ab3951439a47c3a1aaceef06acd6abe62d1270918a2896d237e6ab582f1302eb31363d42f2b6cf538940162aa01076293a6372d735793f4550391211d02201e3ac241155c31a1a5a6630e6bcb53cdd017725a3d50fb708afdde3ce274354446f22831177e9ab10b1480f3586bb6c889e20ebfda7b94a845dbfacd77a29b6aa5220b3d9793022faab65a6bc50cd9a7f6abd61385f02084d659892d4a7dd676b66eb5d394b1ef81b7fad7db8fa0f20dce6b565c3c5a87f95006a372f5a5060a6f44d1428778daecd357e67b7b00c2cec0e86f5b21e56050bf21eb1d4baa66081e5c826c376527f238462810303424c323161fbeb95b496686c47ee368314943824291014137d814ce21b0f9cea0ecbc6b720a6a1140848c185637582cec9a3e6753320690098b4d83b29ef8af3959d501a7eb6e032b96c42a66bbaaac5b3f2ad0e84658205418b9762a7999588351781327929ff5eccfd34e69a76feceba3794cf78dc3fd22ec049f14f41ae740673000e238928669cd6f10f9478298665b83e1b0cf8657905e00cde5e20eed413148e7f87bddf2119da9f16e3afc5dcc3cb802870470e18dec875b580a9cf752b92d18d9feaab5bf0e4c66cbe66309e2b62bcaa510373bc2dcd811e2164738f79e933e9e94803e8ac128365d1b9122c197e214cd4f521e30870c217ab3c222471e2b9bd8cedaf285eef97661226e7a4fe102037efd7e3969f77c17aa854600003162003083436f1ac6f36ca52ccf00a23efef97c78e4f0ae3b74b3a00610b5a834b46c5cc96e425cf8a6a80b6624523f460833aa0f8aab0a32db41fbfec08fbfcd0ef5fc2e485eeff92beb1f520552c4d676fc360b2acb97d7164e13489f0b552057f74699fd19ebe633871e267a94b5b81ebf2396b38ed91c8edaa3955091e5483a6629a2d630bf57890564e4bab958501fd3dd476c9cf8bd910469b2c94d8fb286943c02f72b5a2267bc8a667030fbf5dec3c28488c205124ec059da43a55e0764fdb2d37ac995777c6c2baaf7c26cea5e01679537c3848c216280935a6640e9d6146b5456014f62e201cc28f25cc0cae1d0aad5d0346fbe02d97f1b214065aba974afbca2e6f4647168c442cce79408a84ad2ccde330d109cdd8f261f30b8c89cd6e1747de5171669a0cbc1f6f93575e5a46a7b0d952d93b331566e16c3ebac06f7c0d37ed9753184966470741d236690dc6c5bd1e87c8f5ca24b64ca48d675b56d1bbf414a1a2324469066b985a01e9658f2fcdf0f9dcec89a22a40c9ff29303fa797343a2b6a5fb01a954f50f8b0a8a422adc7ac87901e909d9dc59e6b4edef49deaabb1c295956fb062e64c7b349f749368acee0b6bdab598d91830e0578fde09dfdb479ad1eb2de43d9b212d56ccdd22822c9b99cf134c538536a802a97cd24471dcd022aba2996d4ad2e46cb3a0e6d251a96cab4f971582109e970f271cef9585678469b66961d0ada80f8e382c9989528e4bb4342fa5907d5380d3812461cdc9c04d421a738d345dbd21551671591fa80dcb3aa490f4da60c5361951b3c4bc6ea04460c27c03b647c42b30d9804957e24b54ee73bb307298279f66e3b07509e8f058c3e4c713fc36265266f566576c9ad43b54965a4a810f62ab9e1a358b6936345346a561abab9c40d1340def0ba2199272af3a585f3eca0bfa426ee4286c36e8d08602d796934348aa1f9e27e4ed0adaf09354ac2ad5d58702808a61a2df9e61bc5618c7da6898f784756c1b266e33118485f011fa7d078b2630a77fc4a6b7f165ab64100f70074a9f6b8ce05e5151f0ce9bfa991a3cc4c308d5b510cee9042d3040d7c55bce4d2111c8a523ba69bd23822bfba7ef3a0ba4000ea56498fdc298f630fce5f257cba29422e140fb6317214dd2b4765c0d5ba898940f306a8ceb403fcd5e8aba488ecd0ca76abc59c06528e66258366248fb9831f31a97b85970338e15a30a16525c7a4962c99bb44c89223709b4690b8e63bcb14adbffc0e71e733ecf00fb958c11b733b7661e5efe55a807ea89416630bc9d253e25a4838500dbfc5c4a825cbc489ae75517ea0829f9d5b540d576009e9ea97a4368e4c1ec5a9622360d90a2aa447be84585b9a224cd9411945f2d517c55942aed070b060378360e855ab23db3140d6be24d87f7bc6444f73a7cbb7b8c8cdeb311f72bf234aea080e7e9cefd81cfacceb3b8974ec22c7060a36932cfecdbeaf8c8d9f6c0c8f59c53000d5ddc51d08e75c04a7417819839663c0e0a221df8c19459bc2103b592e79b94e90756c6483cd7a21766c211a34d6b45f1a43c474c97d29230687892dfae712cd18b22f8c440a1aaa18cd3202c263172768f079a9297e1d8d58c911a861d887c9c5d2b96cae2839c9c60a89743d6bbd08610a02af95536aa6aa6b25192ea8c3292451d90d50269100ed7ec6930ad23f6e0c83efd0a89c0a638f0b199409838e3cc068a8e929ec6d8b5c19eb6dd1a7e90615c428e6dd1213c43d1772a8d5351932ea9f299b38ea005f93f445d46d7760d4eaf4a57e74ad11dc6c38e13a082329d91e19cbe8b4b7c4886807b6a6d2b0d4c86c67c1eca534082a738e854cf6b9824801de54970bee2cefe8e1f43d9ed4aa69e969b40a7dd8c6a5dca41d653a6ca3f39124429034efe6f6e3c345c87b3aa510565cb8805eeffb58046b3e22507a004ebbc7c5ee040e83567fe425822ce096567441c2b47a52deede566d753e4e3c92c288512747cc09ecddf9ce3c1948f6e497d4796b226a992147149258cfda593dd4840d8688c6ed590602585d290614a61bc46200458b4431e6df1c2af34cde372c5b47cc87f7becd9b2e86d3a54e98f210a6eb2c0837fa1946385609a9606ed440f72e2d642ad3a59086721884acd02245d559ab40ef1fe28e8d67bf647060864a211e8aedb6f959b4b181623a7f10b988ca7f5af208803440c34658f6cbfc5172628c9a3917e70671ec0b5e49e199811995e20caed545eedd065007d1166ba7c16f042fe308e0527067273ef3c7d752bf23731e6d88a9e4d9843b90a120b8bc2d2dc1fb4ada04be250fe2848bbb266434f5b4be51af92c3e4a7c0c6d266fe57c108ccf2430b6be0824944843f52fb1cb3461b3dc100382211501a2d93d5375f96e653e361598c4387da5e536dd2668f119bd7419a5b4b7ee37efe02fa1c248b996ae91d5783353f439abe0f2a2349729a2c3c9216265e4e4b7a1b045f15aca80030fb264db54caee1c027a2fd9a66bec633572eb2b60f140134a887a3930a29d2ab7687d52efe106808221f2dd7ec44dd6a6b3db5bf04ad9b61c5381c0909b660029d4840ec92001151d01269d5e1bcb5e03ad4038d6ad410a03d66c95526cd0e69133811d3438f81179661aeda485738c075e215f8a0a3989de24c63254a4a4217b18fa49382254d7e236917ab889af55f5ac2e74a7660e9d852ec86e2d5f694ef06282f67802155bca2d7aab2e74c8f43565d93318bdee5c45d84a95219fed69b962f01391f51fc140d6256e2d88cf5a3fad0c168bcff57f5400772eaa80c984f2805053d264b09b31d2fb8fcb7ec40151ad239279ad7566809747051682e2bb1250543ea3d1d0e4fb91fc4092ee3a75d2cf9a9a4eefbf83195fbc9f147e717ac81c403cd7bbc97dda66844d3962708a6565399ea3d256699951161cd13e726997c9ddc5494919b68f1684430934ab87eb9abb5d3aba47588d2c177186f662106b3faa11da92be528da6e6bcad941ea42f5293da34420002c25b513c603538fb0e90e223617e6620f2f1569647d6d272c0dca0f867584c3b3a6a5d0d411baa23a9bf63b001e88c8d7c36c2b956590a5c32922dde4f0aecff3864c89d08b39c155060937876d7f4af4255e901fe57aad8165063b24d69e35f58f1bc193868972a25dbd944ff277a17aed7a27656d385f95659b9399fcb0f550de0ee474df2e3f75a6ff06eed12226f51b48f959efb8544bdd507381480689058ba67924fac7a23cf4f648489fcdf48a9af2c1d46d0992db099806b18fb89dd16d4d04a9731bdece4d323332a7135b1d96303dada2c09dc474a7c6fd7c3b16ed951cb3c8115eb53bc08c18ebd498df483916054bf986421d2ec8c5e913af923a5504d85bab98855c395716f6b113da6b299e7eb9b4a8b824424cb9e6a18007d8378d4e16ac94875e17aa210b59648d34acf41c0dae24c004a6461761e40f5a8983b7986ace869647475d11e87b23d551e140d4160c125d1a48d528486d137866e06985ceb7c5d775fdab09402f531976c02c91dd10036f445b5bd7f8267ea119a2241c323969b571360a3b4beba8f2bc0cd32713625798e3928aaed9f2bb6b2eed90159c62d2c75e669461ef93d14f1302964acfb839d198ccb1dd2c7ccc016b6146782ceda38c6fa38c43fce217f82fd9e278effe76ace74d472b13845e2b80bab5b8469f079de330e793cf0618a1faa8b398e47585a927b2923268acffb222d27eede1b592a4816e135da8343cacfb72c04022c1b73d9cf1f27c06592dc474b026231103c1a6d3b039353e243e4b0f2eff1b053132793d7b589094a1b84408845fb3b27d08320f11c3173ec469a396211dbd07007ed707ccdea1608736aee175e4626682235b7b40f6d1068ca3537ba0f3dd5c9ad148f978bcfa96de9d01671488108ccc2a1ee14ef57c138af6af953b466bf21432720fa8b8ad82437ed415f2f0c7a43903ea28493c4d69d26c63d1cb400e6eb09a34ba885be20485c4a483e890c7a999ef62a82c81bb35228d69c044a424963280b503534f99629f4f9887a8042290cfbafa7f5501497abbb80ba2db4064ee08d1a2e1614b83d493dae58be92e01e55c3d7d594d2dde27937d066e9855264fff98d0eb3b508102f440909ec902d91e835ee61026ff524a4380a0a2c78512bb54dfab6dfa77f7b401c75b472c56442954c179024d136ff25cc6e63c63e09af1fc411e6cddb268d6433d7b479ede5698cad96a6d8b67bf47858302bb85cd3c5a996938d506ff4d612694757a2e11f10fcbed09bbe7b9f5df50a78d23aae21b88e77d367eb87353a48b44e9b0048e0d70021d8a132fb547eac8656eb787065e631f4185d500be16f1d8aeb9e5df3935ec1575d9eded1df3af49c044804e1526ff9b518da42be7962589c8c85fa2dade7da21e461901d63cbdca1e54fd3f518251af285e2731f1da7e334d37562357b6e78b29cb44ec4838fad250b5560e0948cae1e02b411252d2fad9e7787f28d97058caaa19d79dcc1a158a065b7ce9579f55d7cc522afce0f5f10c10bfdd34cf29e9e472cc8a304d238825370358ee294b8824b70356ec5d1388a53e00a2ec155bcfad36edaa5bdf49feed37ffa4f3b6997f6d27f3a4fb7e92b0dc4f5d859628c7ddcca8a604782b3319f536afb09318359eed2ed9ac02ca6734b6d6f42fcacc7444ea94ef84efa455686349d38d045d1ab1c3c753ca8ad28b6e12b121d99eb90cd9d320a67aeab9fe914abed15277dc5a59a5216e5529af2539af2500aa0cc013124409312a50b580f09f919d17d15b771b47dc9eba714888216f4b34f77664dfcfedf0d68ee5c0a426f872ddc090cc6f0ba3e353c97bf00f311ebbde213069aedb25593640e6bff584de0ae9d2cb5a4bcc7ba3b575394b700fe941ebeb2b380c1088526d877a977549ed5fa6aaadc65e51fab66141b201fb1ceab7c8af5d474d9a7b573409515422b11d6ff3d821d16cd600cc8d9102f1dea1ef72ba5e905de72660f318b8dd75a4f0572b81c10ef37a8415bfe949ba6a06f7db9be0f6b64bd9b825b37954fe82fa7e78fc5c2bc9884b9ddf2a5ccbe47515f282f795a9e07daeabf7c11d80e49c8bde395ff9b6e0c2f954b9e2c7acbc66b52c05d953ca83173fd5597b45330dc0d2f16bc9ffc8357144ae67bac72b68865c6b0b8fa1fca86de5cb7221e45c0848a3a333c2956638689ab5bd3d0094c6c17d73288e11bf6a9f17237e0dd7ab7d59213f3b90046916f1658b9e27399b88ead63919be378aafe33d41365f845b02ead3214764910ea43263e519f13af4e39dc9ad1cd7f8dd35134703283d8de16583cda62a260c3b85b8c18192a6583a1a316aa260320bcee784bd0c5db9ad310116065164db2cc3b277dda91c7c4e8be0c18daae1ea04b5e12f67efc9a0855e9432f1b2ee931d8cb12fa2358c64cd828ffe70fa7dcba5e11081717b11b57f20c2ff361a3a4a11fb7e46d8e0e6a077088e25a0452c216ad9fd00a342d75cb843a2e43156112e6c6e9c4a2cfb88b9936832ebc3d5e5b3949607232d947eed368c31e936ed8c40b1056d1dff37d33de25cfc53c75bedf6e932e16805e3bca256dc82cf36185b8d89feb5b0f9097f0623d7c3cb8c2d8a618a4688187a9dcb5a48f6213b0dd72e1c2e9c5c1aaa3ec1b3ab3eb4a0390c66737358913939dff2c2d840ced7a7f5ff557926ddf4a43d55b1d180cfd86d15bf004055709aa0c8418d8dad8bf1a62f3ab84854bf38862b114760f42371a233e0330c50bdf20dca385f121dc34fe09f32cb3b7baf15d888bc81ad2347bb137feceb54a75d2c83c5048ad8ba987e95c36e3181a90b2818787a3409215be73fa5d510aa41a521b8967fb9f1250a91bd4e3790adffa52be45fcb8db23d138087cd7a285d32ddb3acf1d3ad7e12e84322544f6661ede4d148b9756cb649a863a4d3e769fefa113e6d9ceaaf412be6052ce56e11b0d029903393d1d6a67120cd5df1aea75b8cfd1de413fb03a32225c6cfa0005d09bbbbb50128f08c38df78cdb79aaa545b72cd30d3df568067986900580d4da8c67901a1aa41e6374cd0d10793ef62c66d9d55a54a6adfef47dc49e49127ae90efbaa2a0e1836bd26b4a11e81c66aa30656b075ca2df347d199c12027c3c88ee88f0118a7015b149213a3c0871132fe7023283d9409db8fe6dfb47063a0db43d5ddcb273e787e41a08a4e7019fcebc7620bbf5e393d0e687b0715defa745ee65061c80aac090d4b036c53cfa8811d3c90e03df5ab6f3a3309c2a282373e1eb81a8147d0622dd437515ed660a16054315baac188370a6ffabdad8d183a3180901d23dc6b9a685a7c90185728f9bd911015af6de36925a7f9a64f9e75e32dc5d8d26f5a7cd364e94c6d53f3789a3e7b3b41be9f68e568d377d4ed290cf346b91207a8a55753b902420ab5f270736584b4922be8894445aaef0b9ae85cc19cad7db60641184957034793c78ba169f576325d867ba40168a048c2b8499834bc891ef868fb347e8ad4477707fc00cd8c57a59085b258c236445c08bd73b530c24c3da9a5c3115a77a18f9077da13a164cff1eae88c38923b425bc34bfe13ee3d5df612c5bbebe4f4e7d823ec0487161b702214defedd86faa0ace200f15710f6ef3809bb3783e49a1ad8112d4aa8e74d94e54ce08c5924e03c0728df669624f4bfb39fe996134bce6aa52b4ec4355854b1e6e1ed19583bf6dc6e2ddd3f4258485c2b5521476c17b80892234c406703ffa6548561d96d2a352bf68f243e7e307012d0460fdee631dd0ea396ec8467e85ea72dd84b7fd6e272dd4578b6bf7d7bf9c696338772690e3a25bf473ea61386a66fa721c8e7738f98f765c8d4ac419bd44b85427dc558332b0782c4797c14e89a3567861a8d36501b1090ccc89bd03fe235768c79db48c4682633fa3f6c4c775d24024025a59a181c70aef1da52be40f3d3196f62011da0971831d391070bd35a6048158c4a1bd8da3d6bd7a2ebe4cf66dbe38d087fa22077fc87f688b403d063790008a03d7fdd22bd269de54422a76475e87996534f8f193e8feea8fc571592bd2db36fc53a10b3e5db11357c762cf4548761ce4e58a4c3b606483d0d7f236f07c322f825536a9c48ebb757ca8f1423e8db6e408c35d48d431a8f65fd5fa7abc8bfb88bbc9c70380d35312ee17edbfbacee4b3c968100c26082ff01047d803621e99acbeb6622b43d1c8772740b1b77e47bd774b418d68f1aadc571d73efd52863a0d298f3fa28ec6826f656481a890173b80f9477bab446e79ba5bb5b1b0c9fcdaa19c1f1d4dbc5265966d49177de3f4fd92bde742374a009881b60adaa89fc264a39c921a2b880db7cdb96f1179fe5cb0cffa50ba1d200f11afc58c3f70688b0b95ad7deaedbeb07a23890023125887251fcbd2e0bc8a8a1916ed52d0c3e0e04aa72acd3846fbcad8e8c3a3a85e7934b1a968226ca64849b975228678fcc01e03a9f4da99a43c20e671325f8cfaa3812addd4684c7f3301ac709c565093571f43d33327153bf58efc79ffeb14bb895eef3e197032a72fc2775bd29ae63c1453b5dde165e62a1bda297888eee2d508af0362a9c0b8e35620ab18fc3b50da1b6a13a48e5b60a2b2a3bc0448cdc50bc03a65faa04a0d997d9769c360f225203b6b632540ea55c201c282c6f2c3f55f75b38295ec71479b39ef96d0e2a56cd95451062bc936909379b7a1b4db64646568150bb058464ffa40a4c55419169beccd088750fae55fb5a0ab5463bb63e33ab9ad78014cf4881b2784d55bf5b99a76c84a74576d41572aef944863694dee7c3d425d4aa68151f3340b2c30cf722d9ee08e38016ed5257d322a6201907476f045d0aa926de8e63639cf10603cca28dfa4e446093766247165d2f18ccf02f1b806e55d099e3002e7c9258697b5dde8f1d741fd8906c377ca11d6692bef9a028470acb52928e9b06fdfe4c1cf2bae09395e248e18673ffd613c8fd6b71235583719869d26435169174bc121d6a7ac387014a6de065a1e002a433c85eca9748e5c890e8353dea319a38ec64d114df369d0aca79d986300e05657eefab2533b9c39a3b8d4a5615c7ea69f27c1b7891482f7286ff8ea1b6105d4c2dc446a9bcfe18d714f0a2de9ed0f8a5bf833fc4d4aa2fd644648399f2bcd3d69fd7d090b4499262d39d3dd0519ad398bd4936cbc4145a4d3899e80b2c856bd3f618e4de0c09b1763faf317edea10d7e9892577fd0d6bb04068d1a460bbb89f010914a1b26f23003bf1585ef05e8ab0b8d53efb8942434a723fe9b847c15422e3934e120623ebf02a773f0068557451c6d79e99654a5376f671216cdf71ab78e5a13f2f0bdbd7b61dd66b692c6987917aa38e2ee9afae3ea3fbe519c1851705b82a73a6076990c55876bb03bc17fb18dd7d8691fda10b91bd4d6af0d92ce7c9c2028c7ebed828ed446c3e4fa25ab0bc2599f1b1338e79e57d573c0923bec71a18330e0b5df6346a053b9c8e8d4f6170dbe2ab52e466102a4631c8cad94be978cda178c85bac304776ed8d6a84a85c86703a46b78519668c0b36454deb56ed694dd5445cf314f8c2f3298a859c14c5617680f2568c6ec8f8d75906a4499a9415cc9fce6123bb327e68495ad9be9ee1469dc4729ca6c7f9c995fd35834181431bbc956e431e61d859e5f48aff16a3a1e6006424b901fa18a0ed487ad79852102e55e9b01d628ec0d2a37e0e43e8b2af16b76ae3a299f9d5c9ac80a21ceeec6e01a395c89135da1c6d535f99fe339c74207e4fb77ea86268f0f9a5376a41d4ac5e0f86f84d6b8ab33a8a145d3f84f2e0944d212b4136505250d84682448f4e7342bfbcbfd1b98729e68ca5d45d384e7d87df99dbc152facbe96e59342e2fc2887b9df1fead5329c0f721df094b92ce88218403ddf58eeafe48cd463d8890b8abd6edf4767a63b9cc9ea650e50f6cbd38d200ded555c4eaa705d50eb96cbf9596fe780a6afb6497c524aa2686dea33ec1b9ee8006ff2a955f78ec92943c0a3447f43896060d83049131d72683a29e9626b02870b3a28e75e0701eafe1b9a1ef11cb8009518256fefc705c72c930e9384a3a0c2d2b97f7b831f35b7f9815f2d13c17f271165ea65bd191e3a03ac56f780b0784d86e2148e07e3505d26be5e944486ff0eda5a4c3087c5206441b18ac43c28e82f1943589c3da14137455e5a057c629e5982030eddcda8d5fc2115ab003c6fa6f37469429c2d4628acf078c4e8111724f1987dde6e317efeb09239b3b0ed57ba7fd2d15169ea6c60c2314ff593c64b2d739c8b82ec2b502525bf970ae527c066f32563796c57909f0a1a1a692837c7106e35e2879ba07a3a49203c259eb039180af5881539e1fc478ce914be47110ca2212813c36299bc1896043eacf41a26398bb0c773dc8cdddfdce643e1f43a864b0b863c2a6e468a713fe5bfea3af4f301613d0beca370ae0916c055a880afefa3557b8fb2d8de55af3810b3917756ce16fefff33c7ca2cdf9f5b81397d54dfc0cd949bb61ef43404b653bc4669625af277d0513f9462ab03bc1b56eee189201d28b69c6eb8e25e8cd17b4464231d7ad4336348ae224fefec92312d0605033e74e92eaa83a6aa7875fc2a9cc9430f20c931c21cd12e177a4cd9e68ba3fda57b527d49d75256bef0ad336c39d7b7e83114e0a7ce24576deb0dce2540bd39747c10368fae40cb0a48672f4baf4df3ad7dafa387e5974b65575374b5edd9168e75192dfb542cc1e0dc635d261f044196d8718a23170c186ffdf76a8634f78d24b1a5bc012d774d632cc90d6e0e3297ed36446841acbcd3c26ad22578a39985024dbdd517b14f2f8b4a9a0e954301c37969cf31973690038a9db6cd51c14d24f83687f0f8d6f56878c3b4d1e6e5657c8464e58a18a56c85207bd2c67b4b4f677f3049075c0e79f788a65e6ae27eee5d0c14e59dc54400d5ac5d104941754da73d6ec47f5adb2821042a828efd41c096a948bd8f0f340f767b2b44705c6048c2aeed11004185c5b3179e6f2441294c356b437424836b70b5fa45c2da787aa1d8b81d32b5f99f5aea266803e7045591db012d453d1aa3cf1ad513d0ce32dccd80e7ef4ab41e368fa391f59a278a8efe94f40000b91d435e5a11f01cd47e963ae5359557c1920f481d5d909d8c09e4c8dafd382a5f108bd904220d2106fa6ae3237c131e83bda5f6ea4e8a905de45d45f00403cb36128f6df1e81892a264e66fe80c9effeac33e3325b02c027141e38d417e827780759d25d3c982c4721a6acc62b9a908d1edfb4969a0b77995c978e01635cc59a93ec6ba32d2a7054dbbc2951aa3e2f421f39f72dc75962b9e17673a17f8cf00b379f9b4af0bdf500bc7ee8b1178c9f4eb2c217834afc1aa6859d358233b3cfd764f7007b1ff15a12970221e4f7634b4d8e0c3d2f7a9d8088838aba2d9c1a7b71f95525350beb4e05fff6240f96074c0b44afc50a33b4ca941c0e50dd34000e97596e9af1c46b929738152b271111b63676f65a023b2d7591abf47141e6c4d84f224ea91a46e949fa5c0a1d4cadb914c24a2d89a8a79bdce424b8ec0b09d6e4377639fe3ae42b2a5b5935adfc328ffcd8403d6a238e2a7357d4aef73caa3e5a79337e062bad44ef213bd1348e4d39d9de6ebd7e3073f69d9c9cb1529c61064da5d6080cb91f8e93cf6ed7316f0dd6bc2881a471afb86070d7aeeea834c2ad7d0497215ee967a04288520f71dc097f98df3c11eddc6cca35eabd6b5aad759f08bc8bc1c8e53985d6fc95987b830aed7c8c11a865bf1059e378193e80b89acc703617cdcabfd0d7397d21b219fa704424b5123a4822ef2787a2239a22ea9c6c2c1d76513598a130129b9cb086f02c05f89a05b26705f3324161ea01ba2d9c88ee863ed370cd4c5cd7e4f015038fe17cf064cc9ce844d16c15195889f3f163840e410b5f9cd5ac2c68396633207910c1b558d56deb128e25b593826651263fc42255abaf680250ad69de319f27d5c028d8de03667cfe8942aa833e173fa83d39e52e9b67a1e9c851dc44ffa9a939925aeb7fcaa77322f3e329b4c573c1e447488d26ab771b306edd76530cef39da56f40c5c032be60e17be832533c1203b4ce77962b8a2e3ef33d7e549750a8190703e93b96bcc1509c4b243d9899dbec5765d5587969cfd81f19cf73beb3e89487ae7b83c3581a05eed4741a914f4014497f6ec869b727199ce7cf7796c84f369ec4e9ce182ab17f98421a92d566703ca5511426ac6ed7b5ccfb4f219ba656fad8977797bb3ef6a3c0b4003fe47281077480f01ccc6d97ed56a1bf5f75197161abd2a353f324b442136478235b199f0d4bad129bcf88809bde770e27b4d6326b0e86ce73f739f933b5f59633deca34cd9d6353f1186194b25c763832844ddba2f6f6b83f154709e793bb1cc8cc156c370826c8f5683707f90b147c6abb7e30d4ba633747ae1935edc282787d4719db72f4048a01fa4457554adc14fc3189f3d97fc4b82c0674cdc4b183f72ce4b99014876b96327e21cac0b2d7bde43d4bd7fda3156a2276612554a31d7a186da675e98040c2038afe497481b46bedc0dd4f7be8550fdd4ca2c8a8291d89771409d89bd6152cf08fd19bb1093ed9e66c36ede94c9b32f5a99f34de99c9f4fd5aed16eb20b7528a2b90c58869f6eec50d50a4b7fddd94f6e61028a414b4eb7a603e4b842c5d6b0e879c43cd63f90215358f68fbc485b868021b76ce40d159e44c686ae687f3a86d2ef8b98cb73598cf745f903224ef30707a3d0d2e5d323651d668c2f4e3d38e32313d95c30ad76d34b996e28ee7111fc38ddd0f5972330a9c0e340138253733345fdb98351b31ced9bf760cb34c6561a9451189a8a685dd0f48955a0847cf9e00498f0655827dc7058cf368d1f1a038d29f0af71ffe803c30a94cf666c1594504a555aa7dda8a5b789a0f086d64477b641c770ca8574b1a1ad4813e1962cdb6c2438fe97e8930677788b08513384bcbe4f09818c3099c5bd176f7505eec1c4b76edafc7722fba1799f4f95db3d1ad13388b329adab17c038928ab9effe63d6b20af31ccdda149c665fb5a64bb6daf7d5822ae80d671e58b853fb51e0f8e7039f15122b0f816b02f1e45ad07bb0bb7d1b4593331d12c1227102bc0ce9ca92962b6af4714cd03372518db8c1786c80a5fbb75ff771ec7d11ca21970a90c33cde804c714a9b5db351dd6595f1ccd212db1add0260d3cc7a1ead11c8aec26228dc531f1ca86221bfd0ca272fd58f7e8bf52a18af6facc3bc101996beac59be5cb88c4ffc79d62da40656045e5ad7fedb9a5af44d2034a70c3a339c40b90fc18fdb251e9b314d8bba54b766b2eae1e1da97948b875cb8e0e8a45253951b5e9e2921d5358ae9b31e81b7d5eebf3463faffcfbf9702d494301c1f06591a330a4176e399adeb173f94735d76a969e2da6651576295d3834f7e39aca2f3dfe939e2dc95b38746be21c3dd895bcef59cb74f5657665b144de92faf42db3a4f758a55170fb31bb2cad4bc4ef58c42008dc71f10122b843b10745fcae450c04d11d171d204277297e508d10bcbba01121b8e8c001058e99dfcf6092bb543b273c03935cd2b69bd059cc722ed5fe04676102e4f2a4b19fc0d9ea1e52de4d44c466a86099f9fe4a03b1280cb1a02919a657f6aeec563103177998dc82bccbd561e3022d6d23eff3071c00a2f9679e1718eaf820714252d1b0633df761acb1dbe21654fc5537aaafef073be1407f035fb728481c6c6bb0fc5c13e1e766d276fd2a5da07b08deecabc09312bcbfc3772b268076e8930edd9e4aabc2e721105f44421d22c1dbcb25fa1b84f06738dc273df20c50e1f257c49d51bea96d3b1d2c8856e1c5cbe6dde724bd901b3210ac2c916947ee2c91c5374edde1ba76a215305f9bd302289de492d0e5eed5d68cb68025aae4fc5295e1bc76b5ca334cdd090e96049561675b6040d022f3d4a26559576261049cf3e6bd8540dbf02ad1b60b07d08de447528bc7707c2abe838cc40052cb9c08df564d00b10e5c3f592201f2694eafbf6b3cc6ffda4262b03cb51a0297e1820b3dc95e061f9a08c3f23f210781e9354024a6f818c2b1f1630bf104443c6eeaa7f0d84e1917ae25c11a3d89583482cb4d02a9c26178e266cff1e489b1e38e85db595ec684f5cc4a919a53505b6eed3bffdfafde3b5cf6e89448d50dfb29f05c65c005f3a573536f3ec3e370a91fd1f91e2321f3d5fe7da92d7b7b1ac1c466e096be81b00dd4494995b78aa4c7ed9d21aab2b1405ea115a1996869ac1be8a35302ad08e41aeffda9fa7a61622cf77b3996e22b7f37f953bc55cc8ab8a838972c362b02fbc2e05811e515f7bb2919347f61cfef1c31273a799ed9a676dace67b807730d5b7d672e431c1c3c6a8a71bd786cc861e38b08530249eb791dc4721be0459f88a14417987af81a54cb3a6e0ffdd5a5a713312eb665b339ef05eceeb650df17420a771d4ec93de406065148afb87ea74d101c82731a392cc81d3713592fd0eb99342eb3d5495e723fb5b519de0d446ebb73e2529615eeac320c237869b36f37dfa5a8868a4bcb322508339d2ce7058cca5ac1e94b1e95ca4323233cb172d4c2b63fa758b535526d19c509ffdbd92ec2e69410318494d2593d33467207eb6e8e39b1fe716627da92c91c7b09195dc0770b689bca0a98587fc33cb40bd8661dbe3837763d7315b2d6f335f4d884470ff7d6129c18e74dee862a98283454f9e4d89007c2828c2d3884c3b52bf2e804baa17503d70223953b7861d0a0d72c1ebb9501c201420f340a78b1b68aeb66e539b58898621394738fd1018473c963bfe7825f1e913a44b55cf5465612a9d258d5cd29591484ba62422eea9f970f361cde3e649ab5090a1316917057dad594e0a57543821057ee1002f8d550c26e0e97f78dec1db91941d5d2013e381e682daa0335330616604cb6da4a15a2d77e744ad4414d7c5f037155b93a6a3306fd2c504a5c9e8d8f65bbe6aefd458a704a874cfc2cc1ab41d8a773e70b7fcd456c24c884d26dc55736f057a53bf5455dc51208d1fb15954b2e65a0d51d162aa34942d0766b3312b66041af55172c50af2171246069199604b9cf39e593d99eb988b246a855e587e0ca650c8bd2e30e6c7f783bb0cd0872a891edb8827a66206f15d19c451c242160d47cba635d9bd1d3d68a3440bd2b5adce050ff32493581b525d214520d4eb536db7eec01a270a335afb820114d7fa9f41b42dd941ba46ca71350d89fed39993268b20f85b8864c6c2c6fb0e05d2d5ce1075e5cae066f7ba8dcdb66a30b354e053d4519f6050ffc9e854c433870670201db90c0cd5b198f64868cefddb89902b86bafff3ed321d2d71b9857b92493c41d49a1ae52a4300832be89e7ac50dab724d85a81455a86f766f02c1e446296f9f38bed2e38245690a8238c43e03257439b0e28bd2fa83178e76846c650d8fffd4468b77494702168142379140de98e7bee33c761b31f534358e119344163ab680289da164d55e0e973e7e57e106b91b107795679c67d584c7d995f1c84ab1f7270dbea6e198dd67fcdeabbd543db4f60040697d8fdd964e72ee1504b64f12b73e6ea642d90eae2aa642a4bea8dd3fc8930c7c8cfc75eedeaf3aa7a5f39392f21feeade2eec35270d20fcabc09e942b12165587c0299052f0c58bec59e67e191246ff17a5d946a055725a823b2f74dc8513fc65f62da065d19287fbdacd73362a615d3231f8a75d3ae507b753400eccbdb295b92541c0ad0f51251bda24ff391c8ca0cc33b2c74be35d6335a7e5332d932668a1e5b9303787d9817035f5b5534745ecd05a98f0917366193eef3383c4d07d38f2414f685ea21675a67251589269839b1b41ba96b7d05ba2999e7c5ebae4fc1ee5d28b69b147d03ad615d21b306ceccfff6eb3dd453e63b34e47a18d5ebed058f499601a62986711a2ef179ccce6d88b1d8ec77af56a8124d9964911d866558c8b832fc9d1ca2665e4ea216b633b8e1b060d87d6ba1227a0d0414fd93ba375052d3b2f1bf83a41edcc9277134d12948704b80ddce24e3451c6f60fd02ab0b255b1c3d4105a213f567322236c0b86c1f18d8cf5a02a32d1a8ab4885831a2cc82c5e2252d3f33d9840e615463b28686ce94b35695641c0a68140df218d329db0f2ae5c99dc5cee30fa9f9c69fbf9149c40c5d04cb874c04428fa62798ad7395f11f6de676da3ef33a5510cd8882ff5e261aebe87bb09beeb55342de44f68669426c5836b99790a60cdb25e927469cd6071a9d7aa04d1b4f772734cd5ca952114feea23131a6cb998cb144ea96575653e294885e064be4a95ad38bf39a2fcc19f157cc0e075fa01f351a0bd3d2d3be22c08491b02f868481376e09c3d5446dd281634f06cd3fafd0a22b6486d6effafd3f2b8874fc852398897e7c5ed8c0810dfe6f77d87b5fba3bb73bd3afb49e9519b1e3f5375673b9b86e35e7be3b8ae96045e4ba13c0b5d04a3d4a93676f32f73e3f77ac17363a3ce21316aefe47aad10cbecea9ef326d0c2599fe91ad385a2bd2455dff29c91f0002658ac65f133dec72dac24f9eae4a924b13b0d18020641c22a906fc5a02d6cc3d4fded9da4981dc0b011ba8e6b2b70a0494372089dc0672c3aff983e55e3a887fc29c5efc1eba2118a1cfd1b47fedafc18da00b508ea8bbb67180c26eecf12817786327632856243bc7ee9f4696ae2af7288c1ec824f85db061a30dfc675bacd12ca2d8dd5ebac59c47c5fb2d2687d41320d399258936669733fd459c821e306ad5c6767fd9b760144597381a8c3b8b57bbc4b08c747b5da1a1f94ff31f342d0c3714827d108988122beadf9e732a0993bbc807ddc88ad3c70692567d756ec31ade8d0ac38ef713849525c099757ee88f64e9d74a8b79887f0b3cbd7a3841648afede9e10541db1836508bd94d3a98ca1a319db7369c96912d6fbc9615bc0ea377c748f5e34cc4385a852236ac81d0042716b798479120d0b55c9d85b63870662434f2bfb1cbc85b87a0476816e8875accf282e1b18d64eb83f8295fe78cf628c4859ac97b11b0944f3d16fc106da03ca0d2a60e4e67f2f098895b5afc58b8e246770d5586cff879578a11deb7317691b155bc068c448e55c53258a9734482142aaa44a7032e459a239109c3dea9c06cf617de7d422ecf726b028f7c153126ef09c287c4c407fa3d2f7138d00a59df7ea3d2a0612ba32354f532b10e248e54efee41078ecc345d227bbc459c43a9a478d8182954338bcd288b3749895901a51175535b4655f696b3322119080d4ad078b3b57c5c6ebd1d88fb190a4fcd49a97d0d413284bd9c449b132e925bb6defea7e346ec86ce2639ae6b5d328d7cd7880b7f483982ac5435a0d618413a04f38e7daee1400c0139ea8c597894a00aab0b742efa48bdbb5b1db73fac462caa1fb1dc20475eda08b51d3444b194a00d596a9baed4b23da65498ea693e19f2d4a3803759a2a2569640e7a50d1d3afd8ce264b2179946ed3df0a9dd820d4758a9c55649e59166df6fad48a56344e07ea9bbd91ac65bb22f21962100a85f358e5b0527b6360c06ec5ba3a1aa59243aeb2dc87220da11126d13e32102620e68cc13b0543ef7a5f7b9a2eeb3c0f1ebf4aa9de67129409b222c063bb0aa75f680570e2c6403396f3625e884d7729fa571c5c04e95161d6a4031a79362cede72fd6677b430b22421a4efda0e054bfd8ce67f6b70993c6c4a4d1e4a35bf6c686e57c997900aac56cbfd7090b02115c4f7ceea36f66b25e353dfb6abf84f2f58b3d8e84dfac5b6bafc15b3197652235bf77210a936d6b8e08700c170d99d378e6b4205420c6793201abda82cfe58cb18f14b986612c24c5224780fc7f444bbd737b3a4f3f7eda2a798ea48095f0ab4333279143838324cbb77ad4a0d795cc685104e5a771e0318145a738f720932913465f34b2e18a1963eb3b9a75d4b34e45780a723ac2befd5be07d050660af76c6b546399b6eb7a927059618d8b14a87b265c45473a6c3ae2362ad6e75aed99f7738234fd341c19ce26475e4829a583c0b93c659f4eb24ad0aec013eebb4333da52debddb36bdd51f0c8107ace1218231e59d8ae3d0fc4640c5966ef5a997390230f66b170781173cda9a26d651af7dc5703fbf7fd8a710cbc261d9b4a4e487a1aad7cbbb9a813d4ce857fd4dc50c90c0c329b37e06b3910452946e177451f1194360a6a8ebbba2e4bd47f22caa67a5e82e09d9ce904792378e023c1a022a6ead495c40cc424a07c393d125f631e9844dd5c1516d7348c7615b4485e33e146a9daf801b83446690df35367ee623b3025de4ac3bc07e9d7ba2ee8852bc583a84c9657eec4a4df698266653f26e60bfadb64f089184ecbdf7965b4a29654a0165045c0457042830466e59c3de9b1f527e9d43422264ff1ffe767b11b283a1cc951baa2f3d6539395ddc6fa7130ea3cf149e6fc3a0c8fdf6b4fbf1e3470d43f0907e5074c917214f309459e68cbef5b79ec435a7d7238c1d695cee43ed6bc5a117650e571c66b8d6b0caea1d6cf32b0e7d629eeded5320fff9f3ddebe1461cc7fc8a67a87a6a4faed9c34e154ed93a535dc5a173f32d47390e9ccf8133cf4dfa05d953ef4ff42f587f70efbed9ecaddb0dbbe6736fb39b8ac2619b9923dcd0df777646b8413273d835ad530f88f33048f2647f7a6a9f3af7db09eb34dd2099fdc32099b9a7afbd477fc3619bd9e1efe27a74d9df8f380ec7a1f63f2a0879d23c6706e272c57ec42fa043f456e11a03550bdd40ddf27b8c1bbd2a892a49eb61dc55d7681e1f9a4c114205430e3e201951b1d0c3142427b839c1ce155090e794285fab1571e8730a0a397e8f0c27393ee75feee14b0f5b3e12a4943247a55fe553797de40bf18faf42e24629e57c2153c6223d20e5f04a29e2bff4e0274286327d00f8d247fa10f9cc5721713da6232682a8523464488ece71e4a22f84620d5490fd9d0de7918b600ff9711274cd3b9f7af2a363577c8a792406726f8329250fd00f6509f6882ef9aef9d18b2f6fe2093ad59049e79c73424993e79c134a99c84413292c582933a0905529276c523aa0f1c0e6f230c58907321d142d76698725285aa88892be136aadb5f210030f0f3e9cc00516d3630546131c0acba1944619a222b283165e6c072622aa21505cc83ab96350a0e4f05bdd910a05282b4041a21a6176505480622487dfa22a12c24f7ecfb7665c7b67772778696f9bfd9fb8691bd82f6448475319a994760a199aafbd7f6fd8daededf6767b6bb7b74e3fdbecffc80d9c3348965df36916d21732344122921c71f1f324c78e3d9992a318550937eca50bfc7f274d15915b3588ac146ef81fe57f4b8f3d9115b32a851bff9b52541d686a6a6afa420dd771dbbac64f5a43cb9ea5c9f95c7b05d9e270660d6795469f149bebd9941737b671d254040b27685cc0c289d20b584c275728a593162112ad24bded1c987228610b4a295da2c3902229260a72c79cd0e0c4851cdea733224bd29416b74189c32efb9ceda913dcfed9dddded6bb2487de150c4efe8fa39e39a35ad3569ac9c5df477cfcf70312744624db8b8c2455ae5d7fa4399a35e07dee417c49fb1731c5cd659daa5a8b833879de748fb86fd08b2c2fa997cfad3e3014248f374d5a76067eb01cd6c5f4e97274a7110992dae1e507d797be0ba69501addb171c4eed81dbb6882fe8c354193e77c6eba07d4ef3b3a4dd3e3417fbef480e4cf28f1c51da5c716a4941ea5a42ea594524e59c27469c6cc34e3b39154dadd2dbbcd7477d39ededd3ed17093d6cc6ad68d9862922c5a9a5126ad99cd64d1d2a435abb268d24acbb411d394d1ddcb7477db2cdca435b39a05c35ef2a224a668a43469cd6c96c4148d26ad590dc35ef2a224a6492b4d9a747ad194ad5e8a1f630a097fabd45a8294c32f934989b91d5b4226b6644b0c87229490492d713bb6a4490c0732b1254631254b1f9e0d51eaf876034239c3a1fc0f6cd811945943f2ac71e394600c55951bbe1219c71d38b35c0eeeb35a1bf8c1e17e90a4818eee6de40eec5ac983cfdeef7b7aaf86c3effb6765daca1b883678a89470817992b620be8acbbd8959eef77ddf98d89536797ead9f3d50dff0ecf533d720fe9cfa86f70d0fb39734e68fb741106f9a4ebf3e77035a6f1523d2a868327d1e1ef6ebe33c5d7d0543fb61fd53f6f673dec8105ce4acabde06abb7e0fd18a8af2fed6b1fbd1eb407f71687435d8fe7e918caaa8fc17db53f41ef27c8b3fa9befbcfd4fc4f91c30ac2f7f037978708ff3dce3609ed5f3f0b879ef6fde7b2f8791ee0384f6e5a7740c6515b62e14fe13e8c21dae5ddf10e2997e67ade720de24352dfba09ab50c683d2c605fcb6c3639f8714f47fdf9f3e78c6e0bc253de0008252d09a83f7dbcc95ff8d36eb3fe09d469baa7efe609f5ed9d50f444e9897e5be0dfa9906e68c1de32b53d5956af609d529e5c56617f6a5c67b570cee99476d26c00e7d188d895fba8ccdba4941287994ed345bdf6d2db80f3ce43dd89c2419247a3da3a8d43289750586badb599fdcc5a0cf480dc6b9f815a4fce3ad3a8a669b5d68a6d90ce38b06ef3ed5310033dd995d59839bcdb72bb5c8f7c5f0dd67e3d3d4ae6ec9940a76eb395840c68c6c1c5ac7091b3dc312b59c4ac0c61a4632ce2ba67c6ac28d9a943cc4a92abb0dcf873ce568c5929ca1d1342494640ee58153699a33662b91d591e6633b2326c69b8f329b62a2b376c0f7cfc701c9ee9a3a0b83177ac0a539e1f252ac175ec8e1ee555c29ffbe19a0c8a45f6c70f0dbc31222bb26e3c70dbb69909fcf02312bc499226f8e10b1f927e48f343955c65290831997ef77069a49472e2569e3874ec99b7237bf9dd79ce3593c98210e040465b103db832dc0b588b66469ffeb0b5c422f34a36a63472c206901e0a85b74b29a5f418a350cdf059f9846a862019249f08a26ed43d13d7d33752dfff8341ee0c5e6ea70d8c1fff07cfce2739aed334ad035d5b5b4deb3a9d77cd6257677d931687314a504a29a587fdb5507e0864869438a375484852c7f38b1cd6075d392f4296602833fde801c9acd1a61fce3639ac41640fed6fb2af6150fca0283f27e730e5ea63966e1348f8f7960f5ad9431b9ebd67ee9ff39e246bfce56ccb1a9ec460a88335bcd928d2799e9def7b1838074ffa128740f4658f99c671c877ac4919ee7cfd7032e5cc253fe7515e8f2e539afd07f375300a7b18060eaffc7091fc76308afe49c3de7b4e2f7d0eeb602aff8381511e35ca5c47f3563b291f9452e6c899660795ff2371c0d4df80d90a3cbd83f582218ce945b3a76fd86c73106fcaa9b761a399421bde64aee9689187ad29b286b18b7ef7da8773ae711cf4267d5b3ee2ea386c6117fdecfdbb0e72e71fdacfbefe07de04c664ba776a5f81ccc8dd874066e4fba80f5b39eb3ef43e9c9fb7a37bd7c561a7d33efe33f4313e17c88cac3d0a87ad5ce71ac7d1e150089e6bea64ca5b86e71abfa0e2493f7f1dc4e42ed74f429281ca1499ca142a2850292273d99f0b5592cc9429a36529b60519d762050d0e2c24e102d3e4410b131226625b92e49a3bb64585314b2cd16a29b59c9a218a25652d772c96060b115fb22ca9c410b144e408234c30c20667f9cbd7dafb080a1df9d8342dbef62beca37b89859c8bf8de1d22f32d96cf7111c6e7e03c8cc779a1231f37ee3fdf87ff9cefbfc23ebe0f7b80858e7ce4bc63a196f4c93ee761e09cec1db7f4c169dd60db3d070a1df970fbdafbb0af61a1a3d50b6d6f7f857d78df61212f923ed97befef78c3421f489fece36b1a8e58c8355c14dfb150c44243848e7c6cef58c80242dce95149a716ea3b472fa59a29675e64858e7c687fb190e7d0de7b1418c216c2918f0dbb910c61fb9d211ed6b047913e3b44361c82d051043a07fdfa9dc3e6083d4adee27b11628a60ca2f01cf417378d236e43383a20aa30a93252987339605c9e68e658912cba23453e03a72283499f61788f1e7e9b94ebe7fdb6ac19b4a3510c8976bcc66ad7493f18be5de6a6fbffb3046fedc2de3bbdd6d4f13859252ca1398d5acd1ece97060fd9d4ce38262068a5ae4e14d4d4d4d313a5082df81317359d7811ec4bddd8d91a57c206ed8656935296316ab969c90c3614fe6be7ed9dacfb14fe9af70f899763f1aac5ef5ab70f5f46d743938b4da4d4e0e0ea21984a6a626a50ca495bbee069c7905769feafe825ff727b0b3a0ed2eee6417b7bd376fa7b2bd834a6296be87f3e65e6e1cde400cb472e841766defcade6e9bffad91625df0035546dc78a54ffcc8c5aeffb47d167ef9660e6f6e5b5e9c5038f9dc8e6ac9e540d6b832ab216bcace9b78cfa1be3f954aa552a97b6f77efbda9efdc5329f0f4a8d49fb8dbdd37f5273f9dbe53a7f7d4fbd30af67c0abc388c71f109d5f57738bcdfa5663823dfe762e781e190f7a9ee51ff13f38df984e71ace3f37125f35c4edc71c60c75b3eabbadc28147de247a2173e68d9c872db1624dde64ef8d145d97f544a2b9d5e3427753a6713d264cad44875e54e26a7af18105b62484f664ca69926c7b92696c52886a585c934992816a29cfdfcec2bd71cfce4c6426490ecb52015989b0716e8b01a7825efe4211a328d82fa0b19cab6a5a7c3df664f31f781f4618012bc1193b3e7e1213ffb9dcf36d0790af6e3b405af3dca26b5c9d9d3a42932f752e72598f3143c5599bd94f8d471de8eec6fde82379f025d5cc83dea55208fce7786f17d75fe933b3bcffd0ee6d1c13c3cbebffffdfd4bbffecadba17a18a0ea73c0d07bfb38a02bf59dddeb913dc521f75605f6802efb17a340edebc0ec2de609e24d9c2635a62cade64b59e220de245fb3f2014357ec675e07f3331df3b3976058b124c9fefddd3268386528c4a3a33fffc4d8cdea293f25372adccd4d18a432bada875dd676544cbac88048b9e85d87792410dffb08c4f7f0100786ae3dd5bec964596f22161ce7fdacb3d6ece6fb687d769c119c4134c7f719a401317ac7906594e2ffbcf6d4a281a538c3149c764a066c2f254737a9a2e2ae799c5a3655b11b44b3f69926c39fbefc6f81377e375cffae8f2d567de0760e45aa50f2441bdfc1e80bac09d659bc8078390f2d660b187c70218623d816580fce022a025e699a8061f01a418b2260ad170db07fa980c3eb0358c06a801901f35eab576ba9a0bb38033be173325c600d78b5f082a2c1b4790186044c0a58005e41c0dc03f5d54a2c0163c28a241805f7a58363e92d4ea44fe02c78970e810926d00a10040d599098c10a2052c8420929301896384111484dac9481ad81edc0b04105ec0212600778a981692f2925c051029813b021ba97cff09a8d2609987b51f4ea2e555e36446902760616451230a6540f309d97131815300164f06a2f8678b3214209356848a0a4e80c931ad86759966545322c7296c490d79cdff87fe830303530229883498225e009931bc694c0480a5dd67c4102031730196052c04eaf0294812941c3b918c0abd93c811d09f3056c890d6cc68b8ae7d04b5fc0fc0a7e351364605dc07a0b226028fbba0106be963408d4bc8ac8e0c551180196d6a29fe0e244a4d0567816aea5a3e82a5c8c1be9a65ee35e90c0fa882960ac570930ff01e668b2a11f9ab821071d40c084929a34598ec0629fc12245d1995811138eb82289a6a218b2c06ebc9c78f51550b852c7606370b8e0745955595561d226872d196dc68a8e3129caf1675891bb8a3f8b5ae1ca77f7f914a7d05cff785d521d6126aadb574382b82ee73711638c1d1d04dded0f4276d0da3c2b8d351823a5547e1febda6f65746954c311e0f6e40670bfe81eb100ae2b80ebb93debacab1a6e002e8500ee660371c12e381d9ebf82db9302007097e8c833e05050a49432066b8574bf18dfc3e2b926e07674624ca90dea4e9abd69c4897922ee4cb39b10698c1b59546e7873a5b456e969b8eea941d41103e9d269fd90524a57425c29a3ff4a882ba5940ea4cb5102f1f5c088b1bf2fc707877367e0dcb130557253c0750b11591d664af6bc8261f5c20ddf86cf2f79467614629491c60d6d9e33da4843e2589827b9f33c0d499f801359f6f487bebebb6719a59f45faf2ebd3768f79a87a8481d229e9b7a493cef8f2ad836df1cde5c9f0c7923e94decce8845b5da2a3491f731d1930515175a508ee0ee0acfd550d3775b3273aa2b77b7a228b7e8cc85a21ddf09b34e27b3b9801cf7332a9ff13a59c111549e074783ef11820774ca9a76636e68aadaae9b666d8c880a31533a1157b9727e28f257db25fd970ef4d27c51db88ebb3db7eed62ed111c5257c8e2f62207f82373fdc9845b66cd97ec114b9634a620c9077c42c23b2e4b3224bfe6a841bb26454ce2b183113af31252e72043ba6a424b74727b63736e17478ce6e679c0ecff54a9c1b534a923b7644923c23772ccc133908bdfdc3cd4b7424898e3c7e2c0c0a3928e6205916228393f1f677e5fc121dc52fe1731cc9ffb8be4447fd25fc6928c7a18c3d66c79347d43194e7cbf81e0f0a60907efcea6d705fa82b8122a531bab4a46c64753f572edcf82e4ee545771f604c0ccc171187fc459683a83017cc961c763982a192fd2f8e31ca190343260606cd778c014919253b8440071d56405a01f665842f6dbe1cd9c248962c88880163440e4854c01021d1e21341ae67dab06133c4c20c4aa8f0c408871c907c105304e5a50cca4be6925c08810f5f88c0982c71c414274e4eb246258b91942f5e8a723d82a58af602bba20612b82059a2a487335e46c8d245082e485421a5cb17234bb12e5974b9c2a76baaa8702f52cc240e7c63e1401451c4b834896da9a27a820b12112754b9635caee4993bc62592f7a8842dd765b5748a660400401000e314000018100c8784028140281cd05559f91400098594406a4498c923811c06410cc2300cc4300800200080000883000628a48cf71c212744258462107f880b3d2486901f88150b8f357765493dbfde86f151f1da094a12c3c3ec7cfab10dbe3a993b1a9e2bb4d91e1683f0435c865004f1839845888b054f7d75a5def0acb59907c41fdeadf9703a44615e139ac54b4fbefb5d0829e43c49d22c46781cae2284e7209285d6e35fcda2c9932aa7e76c4f9c9b4549cf97db0c410f813844f9202e163c6976c72af1d817d773d363eacd22eb49d316c589a7371bc6c3ee0f470421c51f4fb683fa70f5e1a04094b5eb8964b348ef1970afe363962c57411194879d13f1811fc42f215620f08497633cb3272ad77386e7a9c9f463815787268b8607d7dd85ace764a3290f2afa3dd96e4541fc7ce119e73820de02a22c22cfaf36cc0f16769e3347ca077f3e287b58cc07fe0f162db587971e4485e8404c7b28338c9a0761c26e18b28d5eae01711f4211c40fc405441687b8f0f3748b8322da736b453cf87f5adba42054b4f6846b1d88538650a1e8992fcd82e9b1aa2e4fac674f9b05898717c780a07bd0c28f14ede56290c1632be77396c7e4487bf89f524171926b8585b0605b5090c02bc1db8e4144dcf7696980b838dbe3a45584f0fcc72724f71f7c24cf278d2284e13f5244760aa888e8f170972108077151e3492f67d6664f95430c883c153dbd5cc3c3e9215428f6cc77b360f45895631ccbb36707c5694faf86e9a1741f8bcc4eed24521ede340608fa87c5bab93f54253de5c2d824b83c92db4db41779c286f8ed27ba03f8567715ba8e25107edcd83a1c2ca8963977e0bc40d6dd00091553956625db877ecc18843164819d51951c3e4fdd9eab562c1a8b746e613dcfdd24ca7ef84af4dea201c2e8ce6367f7164893a4ddc135dfd4e6b609af5bb1e1d915c778ac07cc5dcf538fbdb6141b9e6b4db2106521f678394d0f753e48b107157f8f769ba621ce6af580ef38105411424f82e737370c089ff73d666e16939e5e6ef30f3b1fa638c4c5bf279b2d520f2aae3de268da01b1e2d26366575629cf77ad9820f0bcf3346b28f560f1df036c4768544fd89ac5b827a8dbfcc3ee3eb1a629821c2e69aa7e962ab999cf2070d6d46d40060f7f0e94f9b0fa7753d845f8f015c21ea2280fff3d548220fee12f04650f88fef0efe1720f8a7bf8efe1b287c47bf0f770b907288658f1d7d3b2510a84cf164fc26f163b1e16572875c15208d618a18eea90c836988762c3bc75b0367b6846b54f5179e8ee68219fada7f9c57bb60f66e29b8a6769182fd435b5720bd2319f81117f7651ecf244ab5944f7dcdc1b7fcc12b4e3269be9b156c34020ee837f0fb2f017c6657c576356132f51b084b4ff95185f11c86f053d4c0d85adc42a0ae75f4ebaa1e2ee79de977a5703dcf070103f8a4124fb2efd3f5dddfedf4eda61d18ad7c6865ae005eb55b0636ea42be568a0bd793972044c6a42a050fec6214e238f20f8bf61601b17368807295f7311e1eae2ae67a354ba87b4436c9651d59b7a53dbdaafea74fd4eea1d2e69b5f06a65496280060589538e988d46a1b44887d44c6884515c32f48e2c6839796f08404b3a1aeaf5d951bf80eaa0e63b0ffb7e1f2442f81d5890024004faab2da27a31667aadee075354c938f2ab7a13d58e2c4ecee9542cf12444eb74d9c58ee4f72994d8c74979ad2dd74c310d176eba92d81164dd840819bc654f6fae47b82de618b86470165c2f08cbc066d199bb668f552d1d1f677c01b2be74fb2351a8b771a59d1c2f5d8434d30be140f39c8bbe74175d7e655ec0d40555a7e729dcf830612c4e7c6c0a93ce20d84fb187f5e5bceb871613b9882afd784cf819b84fa5134e6f53a44d1d437ce7c0238e6650adedb005a78fae806d490f5f2ebbb09263972c268351b15a0ca8134d13cedc938041d9320c488bf86955121b8f94efa6a9fce5ceb6dba5d082421a60a9f14ff668d0203ee3102f899a04a833f63b90d70e7a19b8076c3709bc576eebe5328906602b8494fb1c85f32aeba24b82a1ef1b6d4b19040b0e9b9042d1fa4499cd71041d5db91e31714d9864f696ed8276aa6c2a3a21ee8f46c50e08932ce156728a569c7a513df37e18beb26024e33f26e2681f78b0f2a740759ccf3d1c49151ae1857f306b3065ad0be7084c32efb9d05c380616b5b283855d84301a6b2849801340eccb71d66a53b7da0fc449d7bec2682cbd9e3d0db4a8b4b8b9de1ada57558c98af6a0acd2b839d6b339d9d572608477b12f642b8e4408fe972a83da9db1dbfd0c74b7fdcdae52db62735d80a392a21bd8231342f59f48dbd603e02f0876a3c218f23f9f20a20d886f82d1bd8c898b05145d1f289ce78568af2cd5e6b98a0144b4cb32a21fd499a8446259dd3c59bea94501b699fb97530c5c7f01921c473c69f4d8cb66768ac3824c3fcf6de699346864535996366c0a70258976272e81c2e8726db75b75f1126c34ec42c6c07b91cbbb8884c5de65517bc0f0d7122f273a410a9102bb0474080671ec3936260c3c800d9edd1c5f97c0a45e1249afc0a744a340125f1755129dad5dfcbbcfaf6d40b64406b9d4cf17a037b0cbf3c638b53345a2a0f68e256e050229e0247b1722813cfdc4cbd24cfa4804e367ee812ff2f297c0d4f71925bbd29834c5b862fd30d9f66cc73bb55bff099eec26107d6e538f9502aa3abecb5fb09b2b627068f8a366f65576434087fb209805f1dea862704edc7658a102d233555b747531249bf3f9bd4b80761a183fc217f5088712bddbd4f3136693900d0413edf98d628883161781c6e4f7fe7dfdc726888548d8a140eddce8004551e0688da28caf14ea9ffbf11efa3d0b29f05d9f95b14dfe23bb82d50a8932789ff359a8814b140276e5fd51240e87045f21912cca4dff67c55cfaa40b36b4ab1c88fa03008c39f06cc4cc83253ee14d4533b12e124876c736294ea57aaff4a782ea1cec160d682791fd4d97acf72b38ffd01a98140c1f925d3b87d20059815fdedb86a0a8d450ba15b0510f4a2d38fe814e010711c4082f7573a8b9c0493566303ff1cd8cd477a0cfa4610e2dcdbb419256fb1d5fefbdced9df5243037f72aba9d8a6270b00ac280e503f6f1c71074c62c6363fb61b423b2a97ffbf39c72df2ae9d4229ca3707c8d4c57509314fa050c9805a6a4365d51aac09d15f7944f5083bf955005b20790731ec7f8cd1d0331760afd757b1860411d1f4da115e36be6f91bce11737ca28680afa0f3cb9e14538883557019ed862d41577cf2769411c0e8c692c8aadde3bcbf72d2160f909c46ae0cb1c1b78cc9a1630fd88c3cd0191400cb62c0806b2fffca459dccedc109a44e47c9700b3706b0f1e24e1d05307a5677ccff93fe31dbfb91cd92f9648b75dd1395877422f19709cb7323a73e50e0aecec906837ed2522bf8161d7f3949fbfe1f9cd797255eabc885b8b4e5b79bc3b77bc5538831efc4e46dde615db53da515a7bd138fc684c7d11f4b80d101a5c883b639d7dd7fc95f0e37c9a618727648b5c999fe5dd3d7f3d29aa016ca3178a7bbc0816668b4d53c722f5d0517b442f981bea8a8550c97e2af464d297f0611247455d8549035e9ca5e143e3ec9e02bebb0a8d53490f08f92f2d5242cd6b3a930a85266b7e6e32a57fe9b58aab7ad082b831b74b4800ecb491bb9516cbb1c31beeb79eb32cf82730bc425be29af72aa5ddf8350ff53eae88bba283e5571ea23d297a46cf66b9ea65c05d4934d5337ede8e2b7c14f1525fb13fe388fff291a518274a3f4d38a02a126a9337fe12e7a60ae086344b943b2aed4f863a5f9414a35a13f710381c9edbb03f4ad4bc2607f512e45bb8161ef9e7037a2bee030684ef2091d28efb656abbfce618b755c254cc4431cc65d9c8b1d2bf9ff926e98800939ced13cae43e6c8e67ee5a3394fb5001678c3968c3c4bf202c76c283a1a8bb802fd883fa7b49a08c438f10de1c1a6f1e3fe944a742eaaced14241df45b2ba47f9981c1448c0fa3b53f6e4ed6afe5f090bc395063f4809d662a6bb22ec932cb6bde04cd57634fe3eb36b88fd4e74498d2683a2d2e051fdfa0b26929c62d570722ec70fec07ddc0539ef2aaae03b9100f5865da8c8856e7d6e975cc20280f394f8f9165bed4009f99d32398a34d888b18f5b3582a0a4b2f3e0b84ab43143a0a4d122a2dc8e5ccca6874e397a393d6356f144a9c837b3c361da9901e10a7863788e750ac1e86817432babe3f415024ff58c8c7eea60f8eae358cb42f1f68e7fe36d017e3cf4fa063a2e0bb9d54b2ef6b0b4b871202c85ca7484cb65d60856228656a2542f7e233e17c403aec6f68b00946dff33b3c979002114a8f9c8fecc88d5df232d766c335a8242b8c60c6fd7570a52d3021b8f1042d838239ee8a89e10eeaf88669b096a985b830ece88c5fec795c8a08dc4ffd7d7ab72feb68c8193e3e45a376c8e966ce9902c507cb19d80054ef99cc177fbf8f358a42d13f46028b9169b24f2934d1c1a82fa705513a3141e0b1a3bec26f2b8b9fdd1169c1adb1cf4f4f348dab94b4911c517a05eee64505e1a84c290b9a2ee03f48a9f50f94223c33cb18d7fa49642e6e09a764231a6678393bf27279cab9ae9d407766570b30466f80be8a26e8d3445dc6fac0c269e6ac056489dc53afdc74350217089564cb7560d1734554759f684296e1a96f53097762d7072a16f006ce6a663e15ddd3031752beccdd229a135992ce4d8feaf7c200e8fa45e21c6e8790a7c22a5374d465561ed53dd17454d3a836ce036e147ec676c011a1fb6d8edfa4e7e898e72a7d65d33bb8e66c455505a88106c61ef57a3f5b066141741a31ce1e9eacb81d364a159790f70d2b5663a6595519955e00c6048320c7cc3ad054b6c70b2c67eb7d42367694e06b7fc86cdad8db829f08e7c45a5d5269b220cab1cb379f44339c59ccad961a61d08c9036910b3afe34b5154e3affa6ae2084a08a7633479a929501f8278f70c19fe214bc0195798d2b985c73d0d8a018416bdc39f6e6d4fab45787f29c0015a619584c95c62fc2980a9c72382c6b0144081e9e6006c2edeeb16c45e7a4fc3fa191a0035171fafd2f493cc552b8d38a4d62e28f211ed2e941f5a13c83d0d73f6169245aa6d2c07b26ec2a54df07d17d0f644500042a808d1f92b6c6de21f6c7096f89a775f518127409e36b2b46bfa9dd1215417b6b65d4185a357037871d7a6645bf3e22507888c606325c04ad19fa21edfd24d6e5b01e2c4106bcdf9602e21aad27c8c91b225baa04772e24542594d14071c4226682917363c69817f861bc5fb4a0d41c3aef1acb5fd2d75c28405a72f2251654169ae9fd61a90cd989eaec6cfbd06af5ee3d2cee6d952b66c10c644b251bd8ea46bb596d8139d21cf2235ed7c0c09b543b21fa7743463e40bf0ed0c65bf10a66f725741dcc8cdd10492f6fbb958fc643825367c01538314199c1b88cd8b98a2adedd70990023800925a010dc59396582e877e85917898f454d344457b0eec548be6bfcd04820d070e5cc056498b30537553d9719a0a238615cac01bdc18f11959e33c806472670a8a8fb14bee2f0f3d65b78d63096555889feb9b9ab252db05249fc4147642b1e3ed896156cb218142a4c927c4b38a54f652f406db3426b76350ddc7a9bccd1f01abf5a67e91c3caddacc07d621c05a01839396beb951eee6f6a048342bc7f2db40535dcb1ccda87a87fabbe0c14ca238e85aa0b3425fd5bc9e915367167c858e4a4bda3dc83440e42f6307a5af776a0c6aed27db4401657ac27b4cb49355438474f0435e10dfac936c57c536bf4524d3f8e22e84c9883b33081c21a7e06b818db0214c26e69ff41b7ac58cd69de82f965fd391acfe1ab1b9fae741246ad9fc43576463f98b2846c50dc5059515e6494148a0299b4db34a47da0588e5a23036efb6c4d88616d8ee82451a0478d5e6b3e82a48743094271605bfeea0b6a1cd30b5b40f833680365f8c0cb125f34160285dfe042d8463ba92cf248add1c86203bf1556c45c9db5600a015dd605b48b76a532aa618a0acb0dfd8b25bc675f22fc592bf0683fce891765b9225cb148e5fbcd7b2f19750a4fc2f88b8d708c211559f205ddec161b8f8365c43616d1c5f1b79dd7cda873e5e0d4e214b7b6f72235ef93dda3318827dda633b7b4f47a2ab9985a1168cea7c608c9039d2236e01ffba221b784c4697291c4646ce7701c4e827b7e35fdc234f4d101d43dfa7bf54ce5d9db496d3db8815cfeee254bca13e2bb26f6d811fe062f05dafedd702a85d4dc194a22125a20996a7906ed7097c214671a513e6d88a258f0fadf4a6698399ec1898d4c20466eb3288675d36851e59bda2690dead20baa61bb74b15a0491fac669009b2e089e81a2da8469f4096cf698c1a397a8439b6bd5d0947fc4f5cc81630a51191a2f3877535f56494cfb6f0ebe24921f57a4f2c4a459f76a810b5b4ca5aef20c69714ab39e1d0d4443b217d5a701c117c75ce1ff07559a23ee7765534c84c2cd6992d28b04502fab15b00e354200369fd0af0f7c88c53c972d17349d9bff4c2bb975194d11ee107f58a968d7aa5eddce416d238f35b9b338a12334109941200ac8388b4a0c07d8b8de91ba9498b4bf0b9af5c754eb45b675df116ff98ca6d911a127c1478618cf9361395f8532d77d4f0b83b3f101a17aaa241c31941fdfcaf070ca57805901bf2cdecc970598d724f955fc4d062a5c0f4e0f680603acf22cbdd52af4894a2fb62df00b62931475fc2511c36bcc3a1db203a407420310c7960a201b6009a2f1a18a940d3321e862aded8f23ecff0452df00f6d4d66366696882fb354baef7da03aa2b3c579a98700adb66925792f16e6eb8656c18d220f2682b3da1c6dfeee0932137cdff9300cd03300b229994777361542c24dac5ab90c8def7de51e13336832468ac5df848a37f616050db561622a8e4c4cc1a2eab47a5a42d997656149c87eea587b540e79276844a8cd80d2c8386c5c8ef6214f5d46c49293e7b744f74f4a76aa562e35289604518214a219f353176ef2bb190709fb4a7900cab9a9ff55e6c2bc3eb40e5a98dc5c8393084a8ac01f1be9422c2fd24f6d02f144aad219d390f9a8913335d671f361e6de264fb30833b3f6b743f4d5c79a42cca13cd6712f3f03c5c47cae9def5c1989e298cd8d703b8ccaac03fa5251ae8edaf3dde847327de5d2a86e5bbc1976b8bcf949ce3fe25001cb15fd0591dbfc01af6e69bb1950d55331edb3da851d1115dc11a06eb8b6d388164f878cd2e194584137179a8620c8a1989a07d8faaee65fce999bc662c05615d44a14b20d37f9d09e098b5609457f24cfe0bf91c19a25d37564a9b5e2fa668eb388c1a35472106a5a235a6d0c1962cdaec041d34a5001b0586d4e2021c546cc7b199d18225f8d810d4a75db55259b9832db1abffbd8dcd17a656f2a7d402250f47b2eac647fcd3d4599c69d3c27324b28e690cc6239b3fe4aa051b08979c8d566c2107b5aff9a65239ccf9a49cd2d1f97560e38244054f8291a9593df875a7b218e51576ec5df2791c08ea06410cf5e882c6dc739d8d8d7b7442ca417452d12e8e0fa74a738855cc8c0be528bb95dc3a30641ac02754808d0148d5c3bf7abada96c4292cd24b41b9ab4c3383d2445628808b37b0a8bf1a99840233a733138d93d2a15d34e838853c1fe863b20c60eed89ded1f4d2c73eff8e2889ba705b9302ce21418ad0a981f52174e92a4dc2e621bbc6909649e2633729d83a02732bf73ca7021d458f877317189aaee95b40075df9cd6d155b495d2d5b1483fabccb82433c342fe745638afe71b890d36475a1111a29a80821f9032e3a42a2011a06801b9714452165f2413798962154f63c57fd811c6ffc7f23114561029972e0fb123a23bb5ca5a5a971b94c738379f52c831a30dc7c55502c01fc3bb75b1e99aadcaec7d46dd3ae28c684d00336599e9e999405202071377dc7acfedc41d52a940e8932761827cc47c2657448c1d36680e4c09fce546b89d33c3acdf68fbf14f4f4167e90a85dd79be6bc0f467217f04844093847012b3fb5639c031570f8a5d6904e0c9ca6a6d5a478032655e7996b34459a013b0bb0a153f8e84eb158d4c823b6cc686597f8c02db635ab5c34d09fddb6ce289054944f4d877c1f111f005752f448e575a00396d012ae908321dc75bb8b64770a71b5ef9da5334d44559315eb976287afeb8590bd6e755fcfa9db24562ec23a99e45a305bd0c34d102ea836d580c8a5b916dacf3d887a212375a8221a57e2922c96be982be3cd0af2495e0de0b7b1f2d5c81c1e96528cd1254902915b2185abc2d61e1c5e5047a93ba39f8975a31170f11ad4cadcd0c4643c5cc67cf75aaf6295fb344a12de4f2ee32c225242b70cdb75fffbe339ffe7b8a189b57edd2478cc8b1eb46a7b6555a72dabbf2d3feec19f08395e49e95aa8282dd38b7fbf097edadce1aabe371e2a054918831c5bc10ec2ed0e3588328e1924d8ff1480fef369567a52c11a7d02acfc19d2adfac3af39e07d517b04e5090fdcf0ce1a2c73a5d3c012ac6e02b8602b162c23eb4459580d9aa7574a9aed4157df359911c2918df430988e042ce86bf9476cfe98d193f971264a580531a1c21ce3f4c276dc02fe0750a669c67ce09e9882cf0814bca45834ead02d7df627fbbc02d0d56f55b80869cdd3202449ec93af0e27ea50f7e3469f0253b4b0c2c4126156b22af84fdf527462eacae850eb1d1adbd0ab61bb8e348c991ebf1797d566440578d0ba36c5da86a6ccef7d4e1e57ef2b09bdbe4d53b475e5c224d00df9dd99381e168a266eba28edb45b8824353010c845f8e370618c8b22027072c7e7f2fb0292163b2f1a814751d947cec9954465214564747c68e8af4cf290343ab814186722eedf9897b360de5cb70b675982be4b8fcd6d83cae5994f34f82857c843da381fb6793b9f80dc2b4077b29d8e4d4b98bbae98a504d85c1c365033b4e4ac97492788f7f2667fbe2a5b8bed2c5ac7d8019460c9f49f345d8a77a04fb7f8cdc2eb3b684ce800a7571bb1fc17d42571447f488ebfc63ad29112c86290b9f0372883ffed61078286d17719f7c7fb8f728299fff3b8ba3b63ad33f14f9478b2c38c18781bec62fb2218d589b1022ed69d70a183560ecc1e85a5e9ad38c82e58b49af1feb01e1bcc9d828af7402d3f88e7e213aded919c9a56618a226546556eb72e2bf5b01369192ec428a1ace4cd814a0d7ae2bd790ea831b5143f1909249124f72129e86a65e1229bdc211f5208e28debdce2a63c6c572bfdd2d717649e4ef574d30b0cd326df3d03502cbfe7f873384b1c70a0ef847582c257c0eec063383540be75b56de3af333e6c95ab576221209a2e8e2462f1766f86677474409be604a4121b8e749f5e8b9b91094727230d87b01ebecc5be86623399c11c3dd79fee4f52ede77911e41c19bac5ea75d8302d373450e1810ff752f97ea5d8497eb1fd24bf0b6e4980d60f41adb385b463894efcab6e3574d1a851f05d61f75c55add0e797fbae56f8c91b96ee9792925db623cd6afc30f6b0c27676019f3a19a4afbaa673e040f85f06df321b309f377c0b12cd12dc466002a836eaec499037be957e051259db2670888fde752920f3ea3c69d43bbe890e9f1f82f1238c677925a09edaadeabd60ac7c3d87a9e748b8176284eff7e5d270ffe421dba3dc6102bb989701497eb412e45830ebac40b7f5f16d5d55d8ec0be8b170343b215389fd80416966a4ad48f370cb024519b9eedc0874ee264960b77bc0d317d488cef517e46dcc6d87e43eaec57195c772fc4b282145a00240fe0e1cca127e43069eb15e4fd96d524c50af83cda354a3751b899d4f05809aa875608135401b4cf9927617ce9e1c21d2cb69a7c1751008dc307d1b2828842ceb53093b86029801c8dd4343d33612cc2483ff72f633f6576b58e36f1824448af342a37363929ef464eed6da5aa9e1e085ba0cd1042353b4ad558e328b54750a918b1398aacffcb45ef9435c63551218e155b1d6c5362bd87b79ef3b45a883133a4c8f592f71e91d25683850bf1f96f1d1dceab1b21b96c71e37f88fc43194b54e4721d6f18d80a26394cc038ddf6547a92511288eddb7629ce98fe4948e472c4f9a190c914a084289ab6c07aef43c43ae1c64b721b9677408bdb7f765ff6c3448be8c8ecedc7bd9b62d13fb1af9d5182d6c14ad72528b70b25b1240a8fedbec0d9b58a36d6f9f9ce552b1ddeb60bb41399dcdd2747a5a994d271c7b1ff923feec9a64a23a9dc4fd04b046873a86266a07cacd85b89485e9962570066e3ba5c6ca0191f52f38c07fb9c7fe79fe0b18ab95eeda403f0c0bc5d27d271396c29d409d66192e454e4239348b23bcd2e80868ad8c3989f3b79278edc93595b8fa22a1d9b49443f00b23e0f6e1751160072eac8b2f1052f68a4d0fd2eed9f938a085e49b225b98bc73685ca142b5e266b84ebaa9334e05a1a35943c00aa8210e5cd1e7c0ac53bc833f8c520f809e5cbc35c83321233b3cc540205cef01ca0726cd2015ba64a16858050944bf80420716d2a2bf182d1b07fd06fd40c346358ae3bfeec024a9db556f3e9c153d9e0ac7854e6dfb9ec96b067a305291c4765a8f7901e1046283870b3803cbd6b94f8c3eb4fbd869a56e8e8d1448ac1968aef2919c6e41f11c24cc4215c25584ee5b94d28662c4213e62837544cdb05bf50bdb1ad8e206a8fe712dc457df2769906650bcda251cb6f7b99a9b2a383e674b614aa2f3c12ccaaea1569a24132d9a8688ab3a7e271a6306fb5c1e48c5df372300d903919e22e2d8fbcb102b7d34dd79e25ac949cf8636e05af9a4702df0bb59aca53016e1602060e5a8aad22ecf9aac0abcba019b62ef154908bc38d2415f5cf12c1dddecac758e4a475738c700d0a2d89ddc078ecd2a6a9c7258da2d6e3899329b2e9611232472247ffe9d44b8cf1e0bc252ff495654181a02dfdb3bf2a38d669d3a30807bbd44d0d2062475f5a6353d7e8aa6db076324c7f0e1f1f64e289b431cbdb2e962d1881da9a3f60fbf82f051fb7def6a97c1e2d647bd4e68f8331990d40760845a09cc5256c5b767969059365248190ec80ecd33f384b19a54e456772c28c1e5173165e2e0f8c1c8ce695d7e8f554ee9f160b8fcba510113e945c834d1a5b35f9af27084130e59621fd75796c96266da1ee8630091f786547f4b397ccf3a4048686765a72d6aea5a54845b6daefb2f34494141bf12d304557dbbbdf9e41931739b961adf48a798a737b15d9f7bbae9ba9289586ad4b286cf9e51a995d7c6114eb8ef049eb8023102cb79a737db1568b105d0eb567d1df95f4e7e19991060a0f338296c7c327db872a531e2b129e3c6cdd31569fce299c1878a4bdb630c071e6f9c16937a9afb276cdb3a9767e01d3d14618afa6d2015736152101951c6b683e552b8cd5ad60e78b38dbf546971921640b41866fb4edb39348ef146805774db92dd5e89fe66d85fd34b312d3de9761875edce8354ffb9bf8be9135fd1f9da84502ef79cadf8150fde25837bdbb91d484018dae660a4f692a16a27ab48eb43f2a98ad9b58e034e1abf8cdda543567b70e438a8bb04bc2152379fe347349fc9b6989cc48c678d2be603782b8fd910b04ffe4472fd554fc4d326cc9a8c81fd7efe6b2d263aede53b9e4a141997e8567e763960e988edd83ec83beacf0a6f0abc94d213bc960575233420b8e74fa13dc8c3aa6fc62f197d689337b86289250a3efda944ae2c149607334517f7be599e7c83f956be75923a03b33d0de493b1580889d143474ef1d49b213e25fc63ee228a15ae26a8f894182d0536b47c1203fe5a33a6360c7accbbd141dcded70daae5d04c86af4d51307bdeca18c85b0189a21e793cd45e6016a0efd5a1b5320bb3dde4746a4bfae063b719605ce85fe408ddf67d51840044a6f719ee4ddc37a3810a8f40d53659ab911a500112139cc097ab18ec66d98964a9e016d41e149d486f2fc8496496d8d9cc33d8b85648c9a14960799378be7c9170b7b26389a5cb986bcad1be453ef889ddee0d6b22e21c54c4bad48a84ae20a38a9e5f38c25052964bed50464c9ecd30a45021677ab4014b90904227fecd9e745a928d328d6b5c7e8555f6e71bae86939fb79b64d7be161a26e7eb1355bb8d37cc593f51ee18ba70012a153804a548604ec406a8f827bb12cfbdb6aa65e9a76634c6763902d6be41a425626c2ea800a0382dd777dd3014c29402d56123b43071ca4560b76818bd2d934d5eddb4b5309489e9553b2c0fcf276137694f0b26343fb6d872005b4fd9d23438c4833892165219d40ba66c84bb354db6f419fb4995693fbdaab6bad0a1650e9f0fc57da41784a9b829c2f5635c5c8cef33a0bb40de35a2ba260f9c2f56760b4803283bb206ed235ef21f20d44b0befb2a9463741ada74214b2c363aed9ebd09bfc22fa38615c2f70423c17b9c08f2d70b0660a19ae5a2ae907706b9cc8fc03733876998b3a07510552e19d8c28aca7ce653d7077684f243491136f4327a089332b9eed888a19a7ed41c9282e7f9e37bdfa132325a467f9b221b88e1cbc2d1a55dc5d03bf53518c90eb98c7dc8a40189dd336cc1b6c475e9f82944e186c6b5666430f4a89c382991d28cfa03af2b4f51102e9898ce9ace1da25d907bca253c0ab9a0cb6f7464f78bb6b44bc51173a11311b296773d7b58120c04641cc24ef681c6de7e91c1c56d23d5bd0205c604dba1cb657b184aabfa252d038af82302894b4645bb1073fdb3ae10a37b42bf16c008dbcb5fc5d8ad39487e8b2d3ca50b435726d7a051201277e7b6eae3f35f131536f8e020db4444530ec7a5b3c0dd8759146f580dd18fb834fb72e28d90848b6181b3683ebf6ddcd7f0323262179eaa22a48c4b0a21209f49ea955c91b36b798b499069effa4eb3c9262c086108e01b4bd2348401aaf2bc9a7114ce6630702955a65e37267e7ad7c9476e2391afdb5ea633c78f6062274fb66bafd021f04f8015279d3da6e9fceab0cca078d60c53bc1da333e1952b62a54073075336952dfb9d717bc50255cd9af6557987dc760de0d86cd0b52cb916a9e4c5aa55c0c9f2288d7c6a1c440b6bfc70117634569ceea24334134ced79d4e4142b93de74260ea5d0fbd01e1df3dbd5e9d3e77c20bf08dc7171dcb39400a4ce53f7d10cc41299dbb7dc16c229f0475147b8481cb7e6c43e5de8a3ff9c63fd32b5e30b9114074dec13201fb5aa717106b5a7a7ac9b3451d2e52316a604aad9ab30cc69b21d878b6e6ddffe3371f7d481277bac9db899fe7dd629f298b727fe9e6d4f42884a167e92b0e870521e83d8032cd9d045f21b1882e362bee7a3df202826215861a712754cdf74841f282a0b5619600d64268a469dd84d3428ff118b94ab2976d625f9753df3e7c244c96e728ba2785545c863c5f3104837e9eaca8874cca13c3d9831a299b85eae4d851c4799228ce06077624ead3ecc625f0ba2c98078650903d2f88ee8984528577071a94c6dc0df24e4607326e8d7cba6e4103e6591b2777d04d234992d8a8de6ee143b29fa887f9afe3910e5acb85a6810c9d3c4d5e7ea7a02353c521888b18b8db54a3aa277df887208b327c08145263e15b8b2c142826960c2e33503232d703d2546ccd4d50647bc57d1f5cba014db5ff8bbeb6c591b0dff569362d8a2fa3acc29b4c8976f27fa0e215cb5b403a020a626f213eedd7df447e1ef78cf8449deddd1f11462a66025f6ad44bf449f8f7cc8f93840724420022e37cd44d83d017c96935f48896ca3ef0a75f84fc5ca86b2abb6180f56a22922eaeabf0df57c8ce53c237d46fec3c2591619724091331d99b9a095d4fce5b6181f31d3bc81bd6d35f25c99ec686adf9c83f9519b2a6bbd6395bf5d51bd04caf87cf3130cca099d29efbf45370eabb369ec826a2edea1660759a4ce2499fe05beaaff2f85c6a20a5dfabaff646415fe4695ae414f90ee4a3936b92c9979e31ae39b4c1cf050bdbf756132bb1a6d92bb27435d27475a950626f45962fbb86369c863c8da77d8a68cf788b1b00a8e557494f7c10002d0b529ebbcd80e7aa730242fd5d3cc590dffc9cfb8f81d22144e0c21557ea5be9c166062443072613e0da35b28fb04aa9f9c613f4e5694e5e94d1215c5b2b0bb8dd0be3c1f580afedba3ac065cbd06698879e0c82474fab5448862259fabd3fdb63ccda0c37c2bc5375cc1407e345b2becac34ef8c4affefa5f91da398e519d8184d069dbd119acb5b19b13f2d5251ed3da9a2e84a42c7b89310038c4617c05560ec8740148442fdf6c254020b9ecc03a305177694484b6feac01c3e7ebade4ec7500b0762c4828f3c205193d99d78ffded336eb69f7a8f60b859c18b2600e7879735b8b8bbfb36cd5eb0663e4a1100d47f741949da3451307841caab0bc6f09eec392b8e1107930ce4d5635bff25a697a2b8188b5ac7686c4eee89123b427d4a914bc1d0e297d168354cfc880f142122ee3e91597661ae1b2bfc903f085f8ae3e94dd61a896e3e3dae17acb7317cf1e2ea203b48ec3c2986100261eefbe792d2b959e087332115e449e55736cccd006b67dac9de3bee6914f2f9a99dba14609c66505e3e9e6214c19acf8ec65924f8ca523325efa26fd0af85a0cd79dc0861ea186d902978060a7bf14995c1d08e0d0d714277df565bf6640c12626197603b94b19e53e1bf5cf15f55fc5b6f78ef4a64d485a10b5badedb1da081035349fae41ec66f4e23a59d356276ae4f948b9348ff300616d314eb7c04f633a827e93fc22d1e90e1bf65765c505dd15c2cf4c275b20ecb9c0f59be5848b4b7d4602b4161c7a7398d958e2031ab4a2131a21514c96882177ae7642a79e9bae3a979324004898c48345f0b0c21f086f0ab42301384eb9467c0c2706b4bd1e32360f4d8819e3d9ddd68c8bc134c2497f189a00e85a8356946590b47c9c8130bd609b291b0d6c347061b55c276555e37130f1d808148ce08869e586ff6fdd02c35341833db6a044db551c9e2e0540133341c3e7d6e1305035c214f6402a645b570fc2d415123c90f6399a4fdb68fd5ab6e54871441828a22c8613735b8c4478d6e82a65916dd9cd536694374a95ba4ea6ac8c6bcbd9bd866cabd9756146fae6c79000a3d726098fd73454846e4d32090721db0a25a9933719161a6787018f0316b5b26c9dd64a81926af32b3c7f2e5bc12eb3bc5327cbdcca32059b8c2c789f7010b76255de17b4dc83f2070bd663e7ca3d17870ca7bcacbb6a02a86935a828b8091ae95cddc070206c386abeb60a3c53430e4de1d540da4e65ce14655c8c5bbce7fcdcb4a7cb6d53142aa96c874f379607b5d5268a7afeba277399508cb3e37a89ed5c07e109519822988d97f1bb7d7cd1186c3a6c298a42b431b0c6d89aa4b9f4a0f142e4fd7ad293147931ea24fad05a24fd01eafe2a9733024bc4ecd2b257db356c7549b6ff31551bbe06f7839a4c0a0aece989ad60aac5929ae98a93931272c274b3cdb0a1fda4e6b35c6f445e4fe81215baf946437ed1fad178deb928b50afd256c0d5711c8c853813c05025bac675cff9e214092df4f815cebd5206743f80e613b37b61357cd23b093bb3f10cecaeac2ded11c6d56acfa83949bbf1a48c97dc6b8768ba0d153d38dc9440255ff468fd97e09b9f4efe36f4506ab4b5340dd446feff25aca0fb4cc8a24b07714747f3ebc0afddde29eec9f83a60beaf4109b8598c5936dda641361a62ffb094fd75633509a731e0d296df5cafa99122bf10b2ccd505af8f2114560317e4aa28e1e772cda6992ebc11784ee1c2a0747b6dee3cc61bb46a19b070c32c8ab30d4a3ba22cf3975ff9b6faad8be261dad44574b9a29660a3d900fd88b783398e229ed50e98eb422dca7bc779b0af3075ef29fa83767a12f760245a71f598a342dc137e530270ba32d8faead789eb09736e452c702c6ed08bf1f2a59e8f7baca3cd90c8e8879254cb6096375b62bf797f642b7217221247e337543656b56b2ff26d759823b4f73d800cefc3d0551830f0393bb9e25856674af99f40f68e043f4af5b3fd623f28cd0ea8dfee6b33427732fdd7202ffda6b833d9e2bd608d05f46223e7a0648aa98e41ae408528cfa0f46858344bf79f54972e216a53f445d66a55263046b6f33d45da6c0300af12ed9377788aa1b1742f2b060511cf2228a7514cb0038ab8055dadca5b44677b7ebc7b93a5e0a8d7c71cd82f0896e0a30aa812284ec868ff52863d9c977c9651147a1a12a1219112d5345a99e8082b70a0a863866c8b74b626999a0c146c281c86c7ac40ee48c94ea9970ddf1cc83fd06de9a33f2d826cc0f30dc85c8bdfcbd12ace32e1d37983a21cba0e5d73fcc9fd61133b098d0309b112e4f26133694e9ce4623bd47f46a85f87ba0a762a68a661de0e2bb311f69718d48e84b9749eed899edb2fd21d2c806e92ef3b841808966f632d23308760b452e9f5addd40f5aa80bc7289566a86b2f4d54f0107127b6a4564d98bb8275c14e659b923d1337be47849046b59aceac372d74c81112a2af71e68a42e92bcc68515908b668f7c8e85dc7beb66edf88aed5df8fe63efabee3c812edddf3ac612159110b09293f9e2577a99239231dd84e2c4415d7267385fe0175182a3c9c03dc7b6fe5d469652ba23379f6f5f9c6c623fb6523ec5ff3da375729500a1d342ab6841c289b6fdfe8ae244f45228eb5c2b250446c3ef29d4870010bff16717e41266de1fb8055b454343e4266e9fc6dfdfb331930a9bcc8cdc8915adcf90597a1131ed310ad2a8492d5fd95f50ce01e0e7fac7ac6e340d252ad1f27a320c29d8389e71ec0e52e497df9d47a9e706f08a4c470f7a130b33fffbce0f5aecc8d33cdcd6c429c33a80b1b7a6ab2e669fb9da1ed910def009685e8b8581e25dbb4c134cde1a22f07a891ab792684a10bc3b211e0465dde6b8c9a3c36f98125f12169082d7272765e289964676a4edd1404e2907d12c780b8bdf3f5891f2b1db061afc4a9f37b6a9f1991f549008946b8d208484c87eff70536bd8bcae9e9f9a9b41cc45c3db57e98ff44e37e8f56d9d76d5c6f303f954649dddb98c635e9392371f746863bd0c94f65180b42de48699ebbb77a6cfc54b4aafe55d2878388f3c85782e68e0ed867f8a96e455c4a9a9b5ce5d7351fe74968de983d3bbfee7f25f6a66e02211babe3455d758425df9c0376bf025b1d64871bbc3c8b0c68e1b9627fd73f89a4bbe94e1568cfd6cddac5558d52db37943bad41725edfbb2cac90608a8accda402ec7e36ffa155ac65ba18c8be797ff666e94367037e7366dd70d651e59b121b7c45ce0f990fd995822bd6bdedc78fbfcd297cfefca822bd74433e301fa85bfe541b2e977cd8c4d2dca10fc3d000cd1002a9415ac492af40b89e13370df8ebc464704ddc131c99b2f9e3cff53f44b73fd03028346128e7a8f962994ee41f4dc867ec3b90c1d65ba146cbf3085168e0f11b5a1806f78f15afce7048a05c27c5e804427e0425bf1dff3ddf1c8f8245cab1b9bc17f17283c64037a43a6755c508a424a1b6cf8fab2d2d2e22e4b0868038fa282fd25fb97b4e23d2cd7a3772078df78fe50fb8390378ddf200e03cfee3717432904c762084148064c881b56b7bf529817db23b958671d6162f25b9badb1f677b7944422bbbb7bd30e2f08ca06d406d53b4866cb4f6df0bcecfa28ee29577bb3d4cfb89b05c3b0ebb46fadb5de2c11c70bcc4bd9686729a4fa42fadfe7d8993f4b0087b4cd9cf3efbd36bb37dfcce26cdacf81395436da3ee7e4babfdd9cdc9cf3723d937b3a679e1949cffd10f684d17dce9a89cc98735136daf4210f0aa8fa8544b653ffe0c22d4d951971582ecfed72f14805876eb8087f72dd0df8f327fc995d5ec17ccff4bb6c946ddcfc4ce5224f405d719d5ebdc675364ee8cfd250379f758a1595569cc6755247988b6aad79696718866197cb45d9b51d3ae6461f4419f1e8a5813c492988e451e9e8e84217744557ced9662efbac1b7bf81042081dcbba75b95c244117ab2045a50b5dbbb65af556b98fa2321779059e8bb2512e922a0fc014dcf9e08d3fb3b8ee601c1f752d6551574164e37df3308d96344ad09c41d394c6c7ce36f6d0686729fc196f72bb7b8fadee132352adb599f42794985beb6e1dcbd9f1edc40f37fcefb30cfb201042d8f2a14bc20e625b4ffd10b684369f65f0419757001f8344306bc3f5605fb5874f03c222afc0be6b5c073b088d54586d55e918a834e8726b21c637f686fe8ea692d05ff559b601bfc76f1ecb39f0831ca7e23a096f385804977686550cc3ea8d6c5b6cb351f769756ea77e542b7efe1765a4382c656bf7c8c7beda9c399cfaf4e7ef44197707fbb47c5a12031cb1dcb0c82bc0b8ce0341b0c81330798cb6c3a218420ccae20d97333f9c0181fb1429271c3192c4cad118250be68f3d5f1fed59049233519ab29723dddddddddddd0ba7b9705c698eb6c26b2df7b20db8630423663fa6334a7686e51a0fea74f8661b71c7cfb0e7304a547cd48c74eb1d864e8ff367e18c326e5d7c77fa3a03c488557539f3a384333c92ae58a3c76c9cfd98cc603103c40c1f77762890104e4081035a7a67023336ecb81f93991ab6110cbbdbd144604605cd0733445465cc00d15e30c34325e60a25325b7c59d9b065c583112b129a5644a65cf101951554385139519164840a1634325414d1a4505182f6840a1f5aa869810aad082856313ca97e38628582105a141f564fa48ca0cc130d4919197628d38212658e5468ca9870a4cc0fd59216ab13ceac74083343131a0f48a860c589978a07106857c8dca04d21d3040b9924426452d0602013642585cc90aa87352b2356a8ae904223c14b0a4aa8ac1451e5072d4b3d72501d3186869508c6bcb0aa610c92108c19a252620c910b63768c20cdd0181599279a2ca18100cbea0341682d884073d26155c2144a4b532c519d9962051510539c30c2142e16a6e8a1a18085e6a4cc0a08186d052eab2159564946e0838a9a1d8490c20695192962d04e9002a9072950583591a248e584142298c96255c48cea4b142c3031c649c8109a154c6578d06410034504314c8e10e38208628c0c8931d2961033c2eac7164d54a1fd2066d54397150e49ac9c104145154d490faa2170d0ce849941954418254c8439f2258c1015166148600a23a4ba82667534852a0827b41bb4105dd1820062454308b08862421435a88a88020620a25041852888a48902886614050f28aed06820a3fdf802822daa272354619ab42d53545b3ea0ca020a272b13a0485a2d81828519a028a222028a12342050f8582151a30a01154840b17a41df276d89be47a88cd057088d88be3e682f7da5a85efa3ed1c08061a2f2022689e603cc1115156086a8b28001a23d81e9a15a42cd2ac98c6a4a186d47972c9450497165052508d51453342c3bac889e18f1858996c297249a962f47564c5f86a884f802c4f5a5071735ab2066544d8471a18be64509ade98a1626081508a64cb18396e6892a8a2798a87c7822898acb134734229e18a2757902c8ca87277a08a9d18498d16a08a3a2d2050625341daeac6e0862cd144dcd0eaa2a9eac6460b245122847343443b41c9c00b282133d42a066c5c4cc52188d4797294a345d592105a1d262ca901d563c9e684930518d49a27ae2481543a2f00244b3e2a587c6849a559199550e6176e852448927ae681f0822c994d58e1db4204fc430594549a21d1d1962880a8a2e407a74e921851a266690b40da321a16d17cd062d2a30461c1142cbf2a2b2a412810e2b970dbbdbf91898683834a164773b6f07c6189b4012e3ce4e942121d7d903acc952e4450c094f550c01a1437ecc27232ef99e5e70c567e5f327274f3c0c3df93082d61ab61fd353d3eef45bd1a8686d04ddfdaea2a7d09111954940e693203267723a4a9a4a2d2440cdc509973f973222fc7fce124576f73b7688ccff496add3d002f6361dae203d316324c5bbc4829271d12e2729425659b271886655913a5c2450b172752a9544a6b2e69b8947998992d48236c59e2fa416e79b23bfdd189083244f85875a145d870c5842454d354114c3655e2b102c26acdc9981dddfad7992a26fd8356248295125df7635a6287fd3a9fb384938dda8f698924bbfb1d29d9834dfa316d311a4ab33b4b663fa6259e989640620921b6df6d5159bc303d89c986e810232f60274f3c3cf9d044b6fab2058969cb921743024cab20340f97134f8ae001c992056f8b2f53b29517edab27dd0d60bfc5f176640da1fd638437640f768f04d2111312489664a922f4e302b012425619381ccccf9f3910e4cf0fb2008703d4bf9cddcafebdb6f1b6ce8b01ddc61739f8bac8c9c9f9ebc2c5f6db823c1df6f61dfbfabf6aa25c8fe55e8d46402d412a49773a4808ed2e67c31613169777b6f566367ced16f490f97e3242bf82454a0d7df0eab5932373562f6d01469abe6856844060be6012122388137968ac20a2092942b408b9098213c487c878473821048d1021479e186244132b659048828a1c80a4e083101e63842c110223c8028238145a849011d2802050442942091568102282300608e2501010e469f1c404691a820b70fb762db2d78fa41f473f86767d4c8434f56d36a7d6b676988d4ab91e8b7ded5a96fedcad003c9efbf13db6600bee973378ad37c3625cab6fca753daf11944640da14fbce62bfed9bf178aedc16f27800b76ffb66bc877d0ef99e7b0517ee9833786bd6d8c0de723837fcc24df38db7e49fe5784b1be7e796dbf30d1712da6f69db1bf62d27a1a0cf613c640f42d6135d73e8cf8790d2cff4d65a5decfad6daeb184fccbd8c8582ee7676a28c7f16120dffb9874f6e2fc6e86bb2077b28edf7e22165eff7e2a1444a08610f74eb03336b050dfff568b55aad56932673b264d09dc55872d6e113917e85f7be762cedf7daa1f4da2102e54164ff7eaf1d677616f1cb53ee982797817d6aa116f6dfa516d71a4fb725181702b66006f8345f0071cc0de5dbeb736711423ab76ca2868d981543f45c43f260f8eff614a0b17f1887d5c7d8dc767bd876f77dadf915dbeaeff0b26df67bedb0b2b388594db40e052db85d3cbc4f8293e71a4ef305ac186846717d1f653c0b3aeb81c4b8ba51bab3a7004d9ffea342b473f82d112f21a4355df675ba586e962919c61c6ff6ed7b6d09dacac8e063d2b175fb53e3221dbfbefc1c9dbb69cc2ae1a53c2346830968f0f67c3bdf527f10352384a8ba45d2de0269cbc72d0988b47d6a63c2acde886ba45db218e3c7549077409bd6ae67723e78c78f5264a907ab900550a6acb56fe36dcbe1dcefe930d0f618b42dde927bd1084c749decfd7bdfc6dbf7de977f3919b4e7d6e13deda6ff720dda75f3c19b7237cb0ca2ee69b2ec61081da3b29ca1361b6fa31effcb1490f2519c7c14d765280e0783b7516fe36d1487837d0feae963a8a78fe23e686758d0be9b4f6b5b28b7a0cc619aa523244847b366a3ba3425fc59e30b39a1e48e84b22cbb7211943d1f724a5067fa116b04cf452348aa43e6124aae6217d4a9121c4919eb667167f1d0964f65945c540275e2fb11eb87865d507c10384cf443c38f4856aaf07484ed6a8b252a559aac607162f1d04b7a492fe91d4b4ec563169da253d4e2436bade5e27374565bdce9436bade5e273747668df5a6bb9f81c9d5c94892c692b5afb252a7a2917e9a5a3ddc125bd0497a20cb894654b2daca573d1930ed9cb7117e4914f643b773fd3b45eadc19ff957e730ab8747892fe2dbfc5842bae7fe8c0279b0af4e05f2d026b70279b2faf16395582536d54ceeeb7272de04ea24419de8494a90c7bad26acd9d970b6289a0edbfddb592b4efaeb56160226cb4e9a319f224c8d3b5d0e868d9f1bdc963b2c24b944de409e8f05095b415c51525caf02418c569ad18c675403529cab0f1e986a3157c82d10d1e3abab9c146f3e6e6e6f1d00d3ebab9d1373737d8e543fac687347e6d63c3ddd8fcb3c198081361224c24d32c6d0fb21f939a261207becf9c4ae7d73abfaeaaad27849d9de6ea5d26a29d4e9cb54aa5552afd34a2cebff5a0b48d4af55aa7f4a7b24af534a00deab52a95caf43b98ea5255f55ce73636a92ce78dfe07f54d55a1f4dfdce8973652e74f49eceba3b65cc4d1e948daad0a46a9c7294c944aa51e0fa552293c944aa5b0cb87523ea4f1fc8c42a5f48e323026c2449808131955285964b10fb01f5316567696ba34c618e363f9216c9f736a9d3d17e5224f00f658d43b0884ac757e1a3163bff56419a5f5e78cf363ec276edd85b07b3c47f6f969c0fffc18e3308a732c85cab273b1f3a7dee6fc289839cee22d17b57251454229bbb22b1369b56e94a04bdf6054c618e7c7290821bcb9b981372d983174e5a29696370431c6782186f1e110bc10caeb7fefe321ff8b8f7ae6bfed10835862a3dd45c721ef5f8a0596ad80fd98b0405283e4be1a17165eeeb5f7af0436afbfcb2fb38d1e88bd043842f939e35456dd3cea559b0de793da320ea86e8cbbf031514be768a940c22e3c845dd885c4d3d98b64647d088564646435deb046a12ec63e843126c244980813216948b178f1a94b886dcf0f08dbfedbb3931b08761e1f692f603fa634623616465780d959cc96e90aa76d80fd98ae50c25aaae647f9d15f0ebb50d782503fac375f35d5dfd7b47dadfd958de5f0df6c0fbeb0e13ab92fc77540755b8c03aabb667fb1ccf9b476fd4c3e86c56bb9ca6532721ad6976343d7297e0002e4c1fe6ffe2c4184aa9bef5a3ade7be3d6631fbe37c66b519fa5628faea134ae83b3ce39679d75ce88bdd7ead78bc377bf58bc027fec6aa9103ef6b4d6bff03a6d5afde829be80d5886295eb99b5ba0fb602b3ecc96e70b35c5f5a719d55c11bcedd2f967a9dea75dad8cdcde209c094a20c6805ca804f9355bf8364f453f5211f6c6543bf59300cc3867616638c374bcd34226a4b3ddeb0cf1b861b4419be547ffa1354822d7fbdb97d311b5a5c31be59bcecf8f3cebf59a0929590ccd3de403e7cb2f579795e722c2b42e84eb77410607b20681f21c55d567cc85d4b183b910fb9eb5d40095b38078b2420ac0f5d4f56d15ab0081641172c824675cbf99cd8bd25a97385eede923f0de9ade7d1e196537da2905d2cf17955a21960dbdf53ff8bc56f96eb0479fce306c207fe72a08e9016495846a42d9e6505a13f0a41f81e7c1d007a33fce34be0431c6c1b401dff1c5610ddb915c7f2d9f3c761a803a78842bbfe7ccf5a30b2958b7251364a6a82f40443290bffcea37815af1225fe6bd0e522270a59790da9f212b2031227b2f12b51e343cfb55f8e9c6ec12724b06e560a5abe8440d9ef899eff76f73fbf73b12194a7f3786c08c9e1e9bcddc5bd778808f1b1df2bc8991d82fd5e4194e09f71a5346f86a7d900f2500ede0cdff163e6e0c5a811a38c32c648e4cdf0ed44f67b37cae02351f0720dfcb4bfd68a323e0f6314254bc167dda07d77ae64bf67cda05d08a69964ce401daf7ee4a405ead86731d1464a7407c1f8110403c540328e83763fd2d9b93afe94e582d5f29ffc291f0b07edfb85275d8a4771258beb6ba8532ba7c99f94d64dbedde6a6d8450b49773a14c0d871000dfa36d27eef5fdf4b1fbc3fbef854ce9d666eac1c34fdaab5ec6b0dcb94cca494187fcef9de7b51f8278e19ceb06bedb5134d1659512ca510baf80c29a59f8a111573ad374b7cecda482b95ac1e76cc642f870545d3afbfa3e38370870584f63bb176b1df05e4f98c42a16c6c6cdec6db361ccecdf7d8fc7d1b0e686337dbd35bd0c6f2db2d9b18670e3f0c8bd7e219a7cc58208430684f3a2537a584514a1925842eadf77b0951b2238c43314d1662585034b6cfea41bf287fe771ff12ea789039e79c106208b7077540d08f350efeb7218618e2770c31c610cfe9d28ffd7cdba334a252df531f85fafaa9b7401b632988f1e68377d6e96cea47957e0667152d4d5a7c34edf7ea6165cb1e4b9b4e10ea3f4e431d10dc0206e0c47f2f468b6774691a1eaf640f6a265e455668ea3ded285960d9a067562f061a6c506badf6b34a1d5e0a20cfbbf7ce3927d613401e4db5568e0e7d7228a30fc48bd6b40650077bfa99dfef20037e6dbc8d7d76dd395c3f9371036cbbd65ecbd9cbd1160bbad3d100f2744f010c1cf8fe67b2d9646702973e2d0da00ec4004a94265007da2041226274e36308840f6c0732d041c6810534c080860496e86e27a6e294d7a57f94a9944ca5b24ca53ee79c53b0b35bf7d21879128d16962648e99992324ab92db992eb649c574a4d236e6077f37d5a9b467cf822e2562a67f933e628a37c41776f293e0d0f6ac4af319f06ec409816c3a6bbbb03ed29b4b3dede9ca898b70e6f4beecbed63ae21e37212802fa0a5d1c2d2bd9992cd7f39454bdb8dd8efe5f201593264314e1f1f2c50c79da00efca62a28294b5094b292242447d5c87db8107ed8c5a73e1bf06945ae830c24e9ee559913cbe6e4668d99711d8e8f450eba768c534ae93edde5d1864af0b1cd8fad138dbd7fec71ce97d88f5bc47cc418310c8b118b1dc4cc69f8c2310ee318717874160cdabf036bab7850c3bf06f65a0bea604075031db0889e6a440c208ffdcecedff1406ed84112fd1127725cd42497716c90c98d03ec3088c61b21960ddd6776f3806fd60b3abf0fde16857a9b3fdb6ce1b3aae8f9d9b3ff2436a79edfc252a9d4db783bc5e1e8ef497dfd14575ffffda00d847abdbd0d07edbcf9e08db1b71946dfda5a776499ec53eeefdddf7b3f9f5bebf6b973ef5f0d774eda9fcffa3677b6656f737f0ba1b58f7b10dacb59086db510da67fddfc682417776779562fea4863aded22ac7fa41cf5963020c653c1ba7dc2c22ddedec70327bace259b38cc328b83b3fedfbae334bc2452465f79394a5a42995af7befd3daf46d9472985544fb7e906e73d78dbee71c74d349dfa96f673c3bddded6713ef8d3884503109c7009160d2dea857582ee9cc88ecf62a229779d8e60d7c652207408659d3ef5dfe60fbe17e39c71ce38679c33c269dfabd5d65abb1036ec287d3fa5acd855e7dce88b2f2f1371eb7c5ad0a8eea8c22989e98c16262b8e98aa00434215560c4b47be2dc19e62dbd05b80fdd1cc322e1b4d024481d204be887f95401d247e4482ee08408037e35922cd2dc522297bdb8f09cdd1ce5219da9c39ec823af2c5886f911c6d6ee41a1ad25d6ac7db4acd393191e5de86248748c06ad29d9d01ebd4c38791390ed2ad9d1fa5371b6feb871983185f7351a3e8a3e86b4a0366f931ea69c4ddda19078b3c01f77da311b1cb49a863b1d66212ac1168095841e84e67be7cade5e23184f8e7e3d4f6e32d98e16e36dece1c0eea7bf2dbcff65170eb30d0ce5019fb97b3a09d6d37cbad12be30ba2f3cada5a11134e29249e902f24030995194306654c29752d248a90b7ad3c6fd54eea14b3a84bff7de7befbdf778a48cb00208b77be0dbdf0c0814d5a9dad0848d7a54e9ae68614c0890a0b2a3490b4a908c40223d289212920f3fa1c790932b7e3c39228811fb3938d17485928f16a660410908a924c450d00c351469a1e5881aa0a4594189ca0fea151021892f4b00b1660b215470124451acb5564a29a5949ed06384284d242845719a48aad45a6bad95524a29ad5cbd45e81210596bad544a4a876aad43bf33b93c2b421031c31153988005c811272634e1440dd10f289ce1f294c234b2c40846cc9480e4081dac50a109245f94ba1c39112a210577fae1882abce8c08406052744a21327267040049a1c76c861090a5a8a28ed804e4a1861ce135e4a3d86e050ad4eca0ce51065ab06d7ec3189bc6c2021be6a98a2478b069949201aa46cff47431324aefdbed2a065fb3f1a9ab67fe7fb7557c74fdd2e5e8ca7c3696b7c3fe4819f53a73ffdbae9adab4f318efacf9f6f11486ef9f36ef933a319072765dc7cd64de6ecb973cee11f5f833cef23150cfb8b617f857ca9e93eecbd041db677ebaeadf366106f5c4e625cc6b98f2f809bc20d7306376cb54ad8af4e65d7fadec405608dae0fa7ac6f6f544ee2d41a15ee682d12f1ab1246bbbead6fe34f15bdf46aaaabbae9a4f626c813f763e50b7c695bcb65f7c6c6f6d89f6fa3f30dcb49ce62dc057077f533b869c4ba3ddccad97100061c74e0f3810f23e850200f0066c6f8da16863df6b137419e9b337024cc6aeb372827a5bf38514ba92250a7610266bf1713d796e169cafdb2ccce6c88c1dbf006a50d5f062b1bbecbb0b4e1672fc3f70cdefbffd4fd62c007faa9fb411d19b471d4adbbbb7e741cf421e41e7def8e0594fd760c54324b3106b39f1bd6393b6ed8cfedc697f2d6e8fbbd1891ebe84f3ab738391b39f9e20832185672aa6814d0cebddba11f29d81c0075a07def7636b59b07b373402bcd07514607f1058c3f637b31e0777f45a4604307d0addb993a30deee763ac8c0066207de8c09de8cb7a79cd28887ecc1170c48fbbd5e88f27ac1f55aa2e4b5c4c70482586a6495a4a42d1f261d6df93329cac4ee2f7df300e8a76efb62f87f7c1d3f75e31763661cf0e5cc38e64b0eea70e9d1e61cf20b00bf002f86ff94fab43e952c333a7ecf7c345ada97efb2877ecde886fdbd5ce7cfbf5a6aa5a75112b3e3472d4d52f6db4167f600f67bc180650365d6a9bbd70dbfb5f57e7e20b931a559ae41297d31f2d70da339bb664f376bb92e5fe01fb1ab436e6ebadbefe8d79719077d4abbf8b4f6f8d3af5c976f38a5f12b7dcf17c070eefda4b6bf18fe3605d7cebe0bda378bfd76a6044912315af6db6f376decaf92263b8301793ad46799be97d36e866519e76cd4d6658ffdbdb78b3ca9ad436efc4072cf1676d19a3385b98bfabc599b71609f714196520f7e3a905f877c9a7dae817d8672a78ff28ce33ef659deb6ec73c63d1d6ee3668faa9165500dccde8b81519bcdd9d8f61e9533f7b0971907b6599c71ef4eeec1803a0f0392c4b5fda10b59ecb76996ca60c0ecd1d703ff6d9d6ffaf246017abace03411bbe6e723dfef02107234c266673fbbd5ca8b27dfb496df9737d3a7cfaf69f6ef6efcbd930200f9541998c2e44d933f67bb590050c546050da1ef8bf17837e57bfd2f76600c9eddfd1aa46fb7e54e297b361bc18ef8900e3f5a56fbe66c31fbff1dedfc6e3707e7c532e066d9973cc7f5ce47444c7b1dc8d1eda1321e441e90a60b6fc97c90ba1cc38280c43c19833a08f6dc0e0b2c733d90540beebb3c468ac05314e5bca9b01262fc0c9af43dac7d121f7cf4f6a3feec771e41cf21fa7c3237781dfed598b79a5d089eca5708802dda3ef07ed8333e693a12d3fca873326fdb9fda436d7f94b185f42c8b9d790c5349b7ec501cc9eef4e3bbe3ca2ca867fa3ecf89285a4947daefb9a6d5cec717e7cbfd721b7ff4f6a3b973dcd39e8e5bae89c0edf8f4b75f33db8a1b707e0dfaff7a1df5d3f6edd7ca0fb91eb913d3784fdb6eefe93f2caadf39d7f52db03efc5782f468ec8c93ba50ed7e11cce8dfaf12bd7232990eff9e3fbc737e6924926bf0e2121dfb270b4ed53f936cad8d9d9e13c90390ab09d9b400200f8d03b30044084eee68e713660c7973076dc51ce97c3f19490d3e17bbef40e4b2f807db875f4f38dfce33aa00e7fa242640ff2fcf80a3a7c47ce62c04b902634c0234c11290ca1a9e24b90157aec56dcefb582d08c422f24af2367585e5248e42778ccff9ca74caa07a8d9610831488cc0628405d46807d9c3083524c126d8a880d463fe54e1470964ee0bc91525b09af8cf79a960c546edf75201ccee7e273362a1091bbef61c2e3084096498bc9818d34311ab0b8b899db8858913865e43b63022e6880b2bcb0b05242f14948c5e4584aa08ac23dc4e20ac2dab2b3c786cc1c2840f204aa082bd8e8408f22a72c3c6fbbd8ac410c4eb08ca0b851f585ba68b85455b2981bc8a24bd8a20ed4eff8eece5c009610fc491664942bf97948b96ff267e33e6ff84b0a5959f650cfff13d803deb4760db7f77c3b15c0610d8b3be354c585786605dd9f919e173708cb5e2bd78bb4bd918f2e5bd789b7292080d80fd3607a006411b00db236412a12da8c4cdd646b16434030000019315000028100a078442a148142649b8093f14000c859c42764a150ea420865114c418838c31c400608801000460684aaa0b0051ff3f7645d252a6080ea2c8318a03635a145f9e1a1a4d82c964bc43b2feb51edad3ca5789b951bbf277f0799c680a5f3834e1dcd8c36e42aa1bd9c87ddf429440cdf3ff0e6462f2bdfa6d949628e743c7302c57e0e916b6366a49c023245a0a319399993433c2d57582df923eb3b84780391a35b9f2c3c10b2aa4485d5c882f1693ff13a063921c3c3350120b2e8e672bcd81c2cb9591a266179b33ea274ef3b558a968cbde2f36c9776400a0b4b962424055a7950d2957e8d6d0bd493e486469f6c27f7f867c95e074e44e8a2f946e0bb2c684130f3894fa365ae2b75556803c104dd41ac44a1c37215ba598e1f0c249f420629a02801e6a3a40a6a92d4f21631940a816ad26752f6b926de99307229109759e9a99cf22cb05812a65caf2ced5fc762a22f9f4c2d825ba9c082936c94b0e65d6231cc46990f9d291c8ef33d86fc8d2b4905e7a9396c295c6abdac3b1e016b4d5e396b19594947fc40b05f0ac726ff1ccdd568805310ca93b7a80a4b61a039ad6d705285f1ad3006244f029adaa21a6e5b271f91431c703152656c088420c68c2641c10bb0e51ed9ede798d36144cb161e31555376f23b35ad917e5ff5d80c3ffc37bb9b5f2b439260a9f958f66c992282cc2f800ff3cd6798cb7942214b83d87fad64afc30526ac42d0a838cf7f8a53374121e993b527a6a006a94a9797457ceeb35628cc0fa7198afe1f48f974b4be6be512db12541f0889ed12eb50bb63895309b7ddfa405363cfa70ea9843069d43c22b045819e88d93565d4c55655780742eea209aaa858942793f31222f91635cda9f3730a9aa44e34037b16c8a26acf96c4a802f2912e53f49733c647fd1311f900621ab722b82777fb23206942c6bdffd810eb03aa6a07da640711841334f65b03b94fff531711e151c01750b629b1111177060cabb2f50f6b9ee8011cb0c89caa65c8e02cf6077cc5be480b1e9fe64d31cd1c97704c68046982232f811724fc49f565b3d1f751f644e7d21b12f9e4d11938ce4e063bd53e84f2afb66a429e62b3e8e8ca778863f8e9cadca3bb8cd7e021412e9c8880ea4cf7e3245c4ca74021237f4ce5749083c837a32d0ed008dd1f8e803db7ebe58991988e0111fc738aa4f5ae60cc4b18483f6db60826418c6ac3aab1a76b5fef3aa6fe6f84960cadd74ebedf89aefd7127fd32b4273ca0818de9a4bbd67109e76684aa8c63760aeebb77ac5a5e902bd58579725a74a94b970e30415b06ce023c35db6bed7139b429c37f02c26f26e308c425111d76aedb9493459711cb555683722c4f12bbf306051e9b94a23bf317e7c65af25fc88fbe961b180cadbb02b855401edf828371b0201542e295112c08398458e118d7b91e623a011a1e83a2831e9eda48492101b38ecbe8b17152c99d4663bf1dfbaa35a2c496eb794a800b49406383158af4479f406d667eaa6c38f29669b3244a730b5c1ed225f1ca3b8331c20bf03c7e52334ded23cb2c830cc10e9b3b04c8da0da83561a29f1b10ac9ba1b9172a13a395882c9608b089cfdc51cf0d81c148c84f17aede0fc5afa6adb7a219d72cd2a887e76985a866a7b0ae3d9d11150d7940f975fe4a7f250bd7822400903772a6de3b05cb5eb689a1699a96d5bfca05381af463ce5cdab1f1caafc6b969443ad7676211362bbd7b93339fd36e51f873a9a551d291d5bc5d8666014ad7f47361a1508afa823c0e1b7b177b5583e9419949c6cc043c42ea264043165c0e0de0fa5758a5e736cc932b72b5feca2a952e520145ecce42e57f936ce0bf615e5d497e927e28b8051df6c6d21a8702ff921b6c66d74a6441c296d66f672cbd8ce06d478006c2399051475f36557eb728e6c3b811e46021cb9235adb46ffed593f9a1a517c8c8fa648874c0313d4f0fa11878841dbd8cdc8a337a4747943aa794488a923d81aa420856b3b34ae211318591d81534f7a60f51f532a29170a6732d08b712a8ad0e85205c3c42011824d115b7a7a8113541acc1fe51a44ad9d4e2d8bbe05f8e60ec71fea56797c1850b0ceac9e75b78212f678d09c4f6f13708aa496055ffd8688bfe4507914109441fbd40465b90a55f040ef2b748a0b81763f0013c268d95eadcd1d8ac7cae529bdbee1858793e37ea4d2d26d1d232339852a9ea074c26082bce0ecf78a4685dbc9a85b3f31d4e36a3a2ec9a1e865cf2438c566d4a3aae18c0aafb234c5ebb566a2d1c7988b92864c9f76028e78f590c08609419e29d60a16c5db56198c48a3b25cf2f5194885349b13796e1ab890920e8dc5760ff40ed228f97b24933a54e8fa596a731aa33c029849e5dc51a5444250caa89a7b8a412a61db5f73ecf41c8dfaf340ce3a597d3a4015d60669f1cf7370361cad1486cebf823321f7ed42fd8e6cd7d0aa3f17a3280a3cb3bbf0ff407fa81a2e2b184ab823416f03f9ec2ed1111fb543dd16a5243a2595e97d392dcbffde00d1dc8d9e46217bfcd01b5c64e2f444ad610bd0cbd328e680cde4a35c62322e3579c8b1a08518a6df0bc8bd83191e7dc3cc9f3b65b5599dff5ec2c40b6e8e066e1e60d24adfad98983d6fc9583f8ad5821bd212b413104713337227d74b428ae3728e0129750879797693910bdd875e9b9b99db2cce55682207baa392c382400d3341ef00e1bd5e446f7e5308daf7322cc749c6bc9a903d470c9b96ef3d5da83559fb8dd2303e4507720acadd5806e1baa52aa939e37cb685f3e2b37138274680edb0e23a3ed5d636c1ba9ee6ae6b570471c1cc020f197291d58673bb901dddfea32f21748f8a67ae7719dc412d3261b548791e3a5dfddf1adfd3f94418bd669929cb28c1b3a378e5ed068e6a340a4f477d8045794a3ef70e5b5875a3e7a55a2768c3f92fc971dc4fd43635797357665ecced6bc9f6781830ac8b4ce13369df8b19b76ddce54187200b6ab42b47d8a80cbd7aaf20061dca9db79b359bd0152fc8edb664d68e6ecb8ea107cea95b6133e909c43793b18e177f939ad3400ff6f81a1678fad67913d4b4709aa7f6e4a80d310b8dc235c69bc26d3c246c72b5eed599f3d3070ed40eaf6e3b0f842c14e74b4790e12301a38690eebb58e0752f1649d020c008a25bc4c890fd26c3db6623f6e814eaa3007268620eb73438334966ae3777aa9494313624110e4ab3e51767b8faa38dbc9cf59122dc9e1b32df275c5be472841aa5e94710b621e0788ed2a4e9ad96180289cefa634cf181cea0a70c8b15ae1400d6e9a81326c14a7f783004e174018077aba4d3b99016db122ee84242723227eab4133b9e5f10b8c654b2e4eb5b9283089307509400bb315b741b6bfaa67b9b68c82e22f6ddfcc199c37d8bae040cd96fb45f4ad687e94656ba0ab703b8609c0ac4a5631b5bf9d04fb6d3af4801a0ce0675bfddd6cfe1310e818285f6db15e3cd550fc71830c678e8ac6a54a49b939cead2c5de7e15e9f11a77d587bc50dbe3e0cd2bb0546d4076ea4a1417d31f6116846098b828f983bcfe3e7b4e19b4a38c57ea249cb181c8ff6e8a884e0b386c978238c3718ebc26dd00e305f4cd1ddd3c27f5757a534d5dfaf616f6cb36e956a1463fe841b35fc693b1cc08fae3a309d733f5492d5d0075ec867a290ebd61474588d2b1a8f51f1c8293d1b0895bc50e17728b393f4e9b60c278cd64bf47f8f6ade605201e259defe905fbb3559fe23fbfb7f2b72c49896c200ba1cb7c0a584d46117620445c93ab583208d2f3b6e279ecfa022dc2b3d0f0ad6ed2e838f2cdd6ec5de24ed9de166cee72289afa82da5ec14d73e40102a2879805be5f80313c902e4f3756e7590acaaa6687dee7dd0c4177098f9f44c0602ad0ce7d7f452f4e4ee6c919c9d3cc5aef08577b714d3793467970b47e5ddf482aef04f1499c86cde9ab61800573caae9d0fe071b2cc27bc25f3e593ff9e2f067358420f924b71ce2283977b51d4800496916508db7c0e830466c2d463a16701dd5bc740018eeb241ccc8ebc326db6e6468b70826320264437e88b73eaeaac521730c833b7c5133af9abfdd08cb7b7235cb4769fce7c80d5eb3d182526b5cb238f45aa24ac80514557d33cbf3b12a38489d50902cfcfa5a8da72ddd4195dbbb9232a2e24656a7b1c4c50024095cc8bcf86531e376646682d0f5eb9fa290294c47fde8439f06bdc08a3235d81951ca4adf2c2d90fa368e4144b0e7d8374c776386cbdc047449e3e9ac9569d0bc4d40fd21c9eea2bb296bb66db2486ef5d823acc7dcd9322c2e9bdb9cdd2e4671b1774565904fb7aa44c8ee3cbb98901b32fbbb853dc23d41812e0cd5224097b52e85b42054d028f52624f1f21004c14fb5c76de2186ff745fee5e4ce7705b3f56310bb24f70b2b2320ae476be1ffc0d482bda4edccb0bce0e5b3e1eda108fe04b8109004cc6c60f37906e8067ff1fcf0560996006add46e051045f18b77a25f9a4729a8931ac373acbee3d2d3f7279a15b12c2a55c88e857f63d40a08277c521d5ecc6b81324b58567f0c0f47dc2b110245dabba01ed231d47d7c59d4b4fbfe0f073599024bdde2d59b437ba84e24dc730b8c2f8c70b7188a6238363d233b097a8b725364234730bbc00cd36a34faad355dd71d700266c73209e062e2b66cd4914b6f0c3fce61136459bcadf887c37e80b1b9ab0224f58ab1e5d2d6313cbe594ce9be845b2f80e5fbeca50e45f30c70bfdad92b38fafd452c5f3ce854f2f501f1647cdf5bfb3849b6969cee681cd35444001b4113dba604566ce0eff2406ce3232bc2c950b3c8e4840af56200ee94a02822a421e2e31acd177813832722e259baaa1957882c93a3a14fdf013fe0d251cb740515d8b23f8d154d735129ea6fc3cb16e888fef4480cd1e6e05fb880d78e5b0273233435b99d10fb4df340ff5d4ba804912b289081f1ca8169c860ec58841c56e796b475bf79a768c7179114e72a50708d89879ad5d54e06ff94861e58f0c88b7d645330b4f383d70fdf2bc9983ca46ff73cae39b26674968a2373acd453082a5b609f0391c7b444abc7889871d16305667741ec59715ac3ed563b5ef520c1865327bd17ea66f44b9635b14474630202ef6a81a538dde972a1aea6cf53a6ab200c55b02a69a5b50d6429af5a3354066ec138a2e60c5606ed9b15b62e9c76c3206080917904022edab2675a356c49cd880bd84a29a8313f01e27f08ccc5ac1f5cab0f166aa759c3a2da583ead5d0f938756f5d07a84c1003d421501390b6b57dd44fd627a37d8c3e512d92e6624953b0e25c56db7b27090e6de4388061aa73510e639cddabc880050c753252908b7fea9b9b34cda8d229c9a08415d3cb4cbcafc7eedf155bbf1e23e9ca30eca6cf5bebab8daf14201ec1fd0c1ffb709c718ce51d62a97a062dbccdf0388064a176442714a6e219e8db8af6ccc902c64143c8b3af98be9af369a87602e457e5730f573560870c6edfd22c1ac38a4b2c21a5d6d1cad6778c1f8433cfb3c981fd9c74998952731016de1dc528095aea92ba346117128e0bf0e21f9525e1967a0d65c3a76bba9045acad3ea439ca1877dd99395e3ce8e818a6b17c0673311d215822872e61f8477963d2c33bfac5adda608084326b609c95f8c16ec145478f08c9d444f2a481e2873230d5c461e09afa05a55915c715101d567bb991d64209bd54b249885642d1c1339311cc43e0771329ca763c5ab832a85d9ed1454c77a82d34ca55488f44dcac4bc4b26b47700c6728b046222db0a6e31d4cb7abec1f7a849ef6f98bc7020d2761578fc6f9dde8432d1d0328bd6add34f1dfe7ff63bb1015abbfd6e2bcd8201a6b80d4d5b78ae465095f8e6f5fa32cc32dbe6b5e6a31243ecd3e603150c07653bb8f4ebe1e3bf1fc120e990ab44899e85914c18ed41935d149de3bd6a154d269c2c42163c23e3ef4b201ae49a0fc82c4ef29539d065ea755ea29a4b7a251537aa402f99ffc7fba3865cb1352bd57838327203232b5de0cab250b5dc12e0a26345429aa018b741719b73b63ce06e3112b90aaaab5d996486eceb87af30fcb2af8413b635e14e9bc006d61a4c565e5a0f6e0eff4a7044866754682c6bc42b32e9d4ef82681428b2abb5c99d00881038657fb0e399a9d533191972a4e19e116caaba872e2c652de30b11c41ce155e96423be7b3e43d9f0602c71cd9292eb0dc67410f4d05e776bf5da45aad4209a547ac6864a939a16ff6fdb83a3db188698d272a6193a10265cbce3418739053a5fb410cc12694fc47e3fc5dc0e681be710842ecdf7af6a548100a7879947c169a516ab7567121a6a61de5e123aafc94d1719da8b7b92492c8cb56428f710281a0be783d5432dd9497c963a10b6d8fae364b5009e7253d3cac79a6d4a9f8ab9dab781e486b34adc604238a40c7ba4fa52bcb19e4bdcba5427b1c5d2331d45108352e08afe626de6e351a663e8b8a234b3f68cd66879ad7a23460db41177b28b0d743d4367b41794c4b4646da327dc28867783aeb1046b4c3e6485f7579a3e79f1e0e04832ae8f94fd20ca54233d50b9cacf07e2e121a4df1f59addec26d4c41acf4967b269a0bdd9a51e4b8e1d2013422c188d35f9c9ebd1d8a3ec004967ed6c7963707ddd146d2cfa3a4d47fe154d05dd20adb5c4621a1b2d4152bfe9ab6bd41aba2ec6af41ef20431ca98ed4b040abf52d17b13c1c187193025a0901d55eecbda51e10ed736c97d856ff8feab8cd25654dfee47a64a69bec4c281e0d168f1e66c31121a9065eb127d7af4107aaea516109c4c23f5635233ae8fc9075f8964c45521454bda80c06180af85f834e0de9ed0238f43c4504cabc2db831f5d3a7a315dce8f0bb0e8aa3454584c525cf5a4c51074a0c8973d3eeadc14390e4a61b0ce8a38bfd24915889fb4aae4192498d74588f6a3ddc4308bd83f3e48b881ca4ff49ff51318f9da0b1843cdfcc221c69841fb906b4189af6a53ae66ec9ebdb883faf2174a66fad42ae30e21f15bff64047fde60fde0f4a2acbdc26625c0769e3ab9f565c0989483dcc9b6f929f2d576764e1f432b1cf9063e8cb3cc21a9ec9ed39ebe4fb179a4a5896130893dba35818d84961a8c704bd50be25d0afb25b7c3e531423f6303abdee669e425fc2f1138d1ec694b20684a76a6c8cf50df00733249aa51f23e1c8ed86dca0a520194cd5caef181a0cdc9c5d1b38308f977870fd80cc5e1c16fba1a7aaf171f765787d62f777cb4bf7bd632fefab042e1bc4abf405fc8d3e8b8bb04afa23596bbe6a040066837ac7ae6f429706be6518a16ac719fe62fbfedac59da0883b298ee68ac67d98f05b5c2ca7631af08b41558e9b2525f0de2b3b52adbe6c52248a25937680f4ee2cea50135302c66dd6cd360c95ea191057724f0a6dbbf7de2e4ecccb5799074df6fdc4d3dfe53d554b3f402baff6424ddd888c7fa1a74969fb4551e7cc3614ab428794bbe8acddd1879e45ee659eae1e91106bed729922f1770f780eb412039582a1ec36d3c33d4f61b884cfc5a30d4644fc8ead73c674796565b92c3c7f952f7c52ce948037993ae5fd51bbc9ba64f439ec5d9fd50b9f5b5aaec1f0cb4e3e6f1cecbf7366b09e398bed4dae763e63134d32aeb85717d0b519da8a9be9cbd33b2c8b37690cdcb61eebcd22e7b7e71dc5fa8cfede8892ba43e18301470a1eb2048bdac25f52916518f86fc6d25ed31052bfe13c286e91583abdd3f1c078606e6f6312926045c180f89115454c4565200a72684240c06e2a4954f5ad1f78a97d8289ff0c328007e9c7303503c45cac22470e9140bef40f1b15f00d95d122fe05fa9356d254e35a2c5cfebba34c1f4e51729709ff545355f752c0eed568bc6753c206eb82b8368c8fadc2b4efcc497180c3963b0cb6aa9d3c3b0535f6361a01bba5704e6ab29f3416fc5b0142d39f73a2dbe3d26dcc047bf9dd83f220d461171e9e62d316df327930c01d53bc7c9947fbd9528ac3600d656fd93e8ec87d6d56242fadf7c8d11c537eb47aac55272846e500e4ccb627c0f9c014f9eef8f3e65b72c22fd9899390657c5a4dbbd8714bcf8e0b77a4ccc38c76b5197f21de7f0f41008cd42ffab8e85cfde367ec6cedf19334659923be99561cff7d30d46b8395333637cb95ed8a3aa6d1469cf391b39dbe31f2128fc85538b33700cec1fcd8e0ce743b452dba202d4ba59a6055a6dfd856aae490a00ae262d20ed9edfdd259e2dd91f4ad44b1519f6f972eeca4febac511fa1bef32388a52082b661889b22fdd89d6ef704c257d49087e129fe34ab014c4d4a0caf538962f337a23b7d567865457f26eacffdcd11b14cdbda96417aa5138bd74cb2770ed35ce0f47129dd438c77e168f887146f1a50fbc05b5ab75c8c7128a983c2822a82c4be849f133b89e8f09611463bb0683492d0f085f299b40c97015ece53642307d46bc6011784b6d8b945fe0d3703f1af92f3be50f574ed262274b4281a2021dcf49630a6923d203853b60aac9e1cb8bb98577b9fa808317faa45c5f153639619434366f78b8268d32b4c5104eeb3f84f5e92ab7b545a11e762df825d0f348bb30330302a71f36251801eee14256b74c6faf099058a1d2d34ad598a2c32f13a414454a07fa171d156f7e0586738cec7b710a6723eb4c92d66c4921f1f7838f79fa61669cb0cf34099750eb14f1a5b3e06e1e796a7e12b7d3afd62dc7945d0818e57589df9bcfaa54bb08e21e60140068bec4c867b116f13968f0a8e3a5b61102ff733c34d605e2f29c90161a03406e207b1247b7a107ad88c5de197b358f20fa3dde397b00fa181ad85fb729f1179ab1ad30ba18158e451bf916ea551a1d1ef75c2e8cd8b6b213185190f94e27d1ba1c44ff8fedac8b7451535d1a4a1475ecb0c0661769a1ae7337b156fc413f6c7e369dc13875341484dbc057b9aace0168680aa5e4adb8216d5a4fa2487d1ac1cdea1152608693f04acb5d583252439430c89d7b31f09caf3134258f91f7e4760811016c12a6829c82cf095de06c6469a249f239803c9013b999f7a48a9d5c5cce12c99906c3f98c15cbc927ea2b5c738c0d4a40c6d312739801bcc57840edc4a96e05b7f0e554e8d4031c1c5a8549859880a2369ae437fc27e6ba753c75b123e5ab1c1fd7f4d62a5a1b03df327bb91f4e661fdb9fb726dbcb417104130a8d97035ae66f3b4f6745d0612551ed8f4fff91388bf1f4643a1783f011b32bc755428c47f47910e6052b061fd58486f4d51d5509730878d92c599793cf9d124f7c15700d3b4ecf9b876623ec4500637a2950ed82f9c9072923d6f52d6c4a134a4eca6927886163305abf70164df499f591fe875df1d8332e9c057f91d7ab09876a93ab0ccf8d15c30aeb603880ba3d44bb05f5cd4b511566676da11e4bab3db2faced4101ddc1a58b987602a059606570d64bfd4a47fdc1471b1ab0029d6e7626f63b32bb88169f6801365b355265b02fd64c489858edc84a94890b60af42c9c0630b26a8e117f8211f2020a8080c4ecb754b51ee9c15cf281eff20065ebac98c6fdcf88be1d28b3e2f092f49bb93969cf20281c853dc650907c97de20b08f9421c2c8232ecdd98f8a218ec9367bb6b25aefb3f23f45392c59b0b0a52f972e5ab8a93047bbf65de4182e4f134814acdea8b24300635b8ea8cb5052be2866c7f83fc587e7c002e01a24d42e3dd4b061d18b27875419ca3cb2eeffffdd2ae35cb29019715ef7a13fa2cc9c22de22adb5aa512086d0941247833b0401645c8a506f8a564170690af199aa3a95b77508f2ebad9926bfa5bdacc1a061eb9b2fedcd6cf9353f01e1907ab813d3dfde33780e95e871b81f4df817e64d0528114508761d414c8d3aa019e17bc6d00bc6f4dcbaf0b16aeb7cbed780e413c857c261079e0c87724300f81352e60c954a229e9c406ec6bf982b83e029a5158756480083edab51dd6aa68f933b57e5460f502a0351886852bc8308588af2586b9067445146b0143d33232b88f2a5111263b402dbdfac804340fe57a36181ed46f26927e036324beb53d5db159ef2c489187b933b8af41833ac91f964c74062f23348c33622a6cc58b5cac24f8e8958231ce00f98400d410ad3333a70c3db09117741bb4065e045a3fb80115c9f0cd12156819864c94ef4205fff7af11d469c6c4b999529e136074387f7f0bb10c253fd9c6eff80e26e53733294dc51ea8594e5341a9b067b0f9ae4c7e57db8a4bcc3c15c5290be7a91ca15ba82dfb094aa2973b77b110e10f4a30f4a6e2d820b864f34370b5b8807dca6151871a5e0d686683a0ceeb137879b2f60a505a239724132b1b0085ac88fed2b40c59a32e138c8b2e3220dc596bf07efd38688ecb222bb2de1540aaaab685aa4deec80c8e6b2a1f7d5cf71b996b514bac34582633277e8a04f251d98600b542ff3a6c744a2106520bc141da4ca679f45b889a7f5f96308cb57049a5d3bb0385ac437dd51e490d163a23f372611d1f457b07cbc608c27c5a0aa37cb0202f2d2238373f417528cdd5d6cf7034b8b4cd82c1a4228d6e1ef7075e2e029a43f1dbe91918b4a656c15a1c34d8b93a67fcd83a8ded92cf33da3951ea776923988c2ba98dbd75208ef85f9da2e7dc789de91b0f7298d24efc2f9ccde530707fe8ef3c0db67c4215445a49649b30548b140384e0d14ce3df43b20b5e5a2c6e47e72dac7eb2f930ef81a716e46279a4109a6d6ceb4ca39bab57a066ca8028464f96e7fdfa071d89b736b6d8866ecf07c67ef6ba602d5a24a5654f218b0efa285a7ea9ea9472d352a47e30a7333925696a0f067b5a1e78166d6fe7215438194694eb83ea30a63ddb9639362fb8388f390048b96be6806e9d5a728a3140678b521edfa293c1994cc1a0f50c2f552c3e37850048b85b739e93a1a3f0bb7a1601993b0602624cf3e53ce94fb68505f6cdb1af9e26fb057c9bc83d8e1eeb5950c503722f2d412ce73abc1c7e6d1d59e5608c69103b2edd0282b750a82790310cf4135133b34c9520abe9f8204ad9ca948cbdf155c2c5bdbd56419996c2e92376ed7f13c0d58f1aa9d4040b9ef835b736a99756955a1b7a759bd71a11b6fb4195289b24086025ef1a6039131a63288a080aef7aaf65ab9079fe7c40a8c8f8cc9c37d3660d40320d089143c1ff57479516a59f4e1c6b33b68d5a8e87043f479871dc5b8a43db14ca4b0395e4444331fdbca843317251b0a0889fc404f64a03fd4b6471bdbbded85f1b82f4ddf8039c1df372a53051d1bb20b0f933318e2e39a2446d161ecdff92c2e80b68888c0146602ad300a203923c559f3e11a0b6e9449623df62ef936e007c9db135663f745864a8491e4080e2cb5774a39c27c6398d9066894ee5cd66105f0f3b2f8312d3e8f6ea79cafda22780ba47fc7f76b2c80c3555a71736a633a6f7fde4353a45cda01ad62b9818fdde467bfebf8966531873a013d05ba0bda682639e1966fabe6bf46e6814bf7df6cb5414119600bef8d8892db823c948696ca258306e1c1d2b02407981efa5cdd5441ea1570efd86815d898d723ef94d88425134c2fa92bb87543765225d909e226f5786b5f105ba9de4081d8d72dcc955b703727822b0669cc2666793845e760f78798922d9a956f9e56aece7fcc3751e7ae681a5742831614e8651845e4346c1f563640c289e6e7d4180ad5f73acba0f79387055026b55c92807c089964edc16df95a527d3b1486d0f0d74af0b4380ec358c0879333a3e4508b84ca8982f533b82cbef3bddabd0e04585ee35f84ab0f57e33aa8ecc363095b60c03bae290313e35770e9ada90e041acc39b4c4a08882f8936af089a43b186860b01313d642df84f3710f7f8c5b183182df2075ada900e88782df9b2d8dd519ed9914c16d84c231a6b1130d308fa3c92842bbd99da2574e2bbfcf8e0aba0f06cb1bc0e0914518252559bfa2cc50b2b846825e9c90ae5c9d9f2f80821d80456e28e5e83302eacb58c626d5f93f469796eb9c8216b398ea7e151378f64aee24864485c9010256a8394c08cda9f1b4c26107a4bc4902a3ca8963808598104d2f6734a54b2a71cf0dfd893109d701cf0d61cb197b6dce6414e11d52a12299d006c9a899ca71a615cc865aaaf0584ba655e006e8879a6a2c6116986de9647863c0025918a309d27ec83430445496503a5cb4c520e134c65c9a6b8234f1cb2ff611dfda497f5e919f7733321af288009b1cf7ff565759623f2aa1b9c5ec091cb050f5c20b09204cef1c51c5a7ddac0a0959f65e072c1127bce32c8c40b5357408f7c233e261c9b258b8b156788f4f2b3905bead2b254948ebc66ea81f92df4d053555ef4c1e13b866e458a14cf4d7ff5cf827ce84d67e73b058517cd9d9b2b0d0cf0c03ffc1e260a26283a0de4065a63c5a063e948620601fb77de7a48a933d90443c7bdccb47471ba85aa4ae033d4eb68e50d185942cb487972e23af368efacc5a521924644954813e659871f33111ebd9bbf302d1afc8e7a91251c48b5a565c22e54a30e3af0b21476039c462c736196515386bb3685d9d26fb43fa15c6fd985ef725af13800ebec9c59ff97dd2563c86349b94199830f26abcb99ba3755569e4c8e91ee0b901dbf0422fb8233e5610442ac822bb714e42a833ef86266160dc0d5fa25faddd9ae1e3921dbc073ed35b77c099890503a223fdb2b2b9d3a0a9d334b3ab93c123c28c084fa2431c0ba988e57aa84456a73c36d97735268ae3b735d457de21b89866a54507a07a8d4cd4565674b315251a9cac0b67ba5ccc35a97c285ae5a8bcc89d7e4344f1d8ea25f4e0a2c93091fe5af83ddf5115f10fef054e52e3a0ddd0aab8dfac1efc923035f09cdac3757ee2da383dab819ea0b0ae22a4b11f30b26b51290f6af3fc2d27066e613c3da1c35b2ccc6a501b0df2f1fbe5f2468bd20bb33f23626ed864a614e8b77cc708b541e6b7feec67c02aac55754e1f09d53cba678578cbdc14a21bd14a0fbeff06c165773ce793063b0c28a6468a71629f50bb9570819110bba5ffd6698c4aa309022c0845cb92e77234b6351cc45a175e7b498c919983246b1bf4c16818f0a40878b1999287a0115189699f38b8b7ca3e5c976e245676ac57a12cfe724554fe39972b7b009e8894f69de4592f74f29412773c559970cfb085dc83c0f0338e00cb459399ede7081dd0afe50a5e72700bad7879262ba5665a31139cc77490ff8e55456630a38d6b26e7ca54866a4c6b07ee4203605531f4c76f784c72b8991b41b224b30aee175bfa2129305a309ead21271382942fbfd8b1d634e5d3c407248fde1f10fb21c7cf5962deced4a6be6ea90f53195ae9c8bd6b8cd70288671ca0377ea9cb92e3252d1f6036c14a73c13f1e9f09dd72095c2b7f18da46406e10181964224ae348064e3ef11e5c2efbd66a708990c0b08354f834c0c0db04b498d298de98ebf74957802f385720d980107013ecbac11fd8726db17c903ab6c79128602ffb471f3e34fde759c4bf26940ec451cbd40206ff462e4d62e8d72270deddcae4f3a534f36faca5b8df0801eb689beabf1e8c7176ed95237f9184a30bcd5513e6fdfe38a7cd9d6cc71270d63370cfccca04cfacd05f5f726ac41db24f0b4a3099bb4ef02ebdab9aede08bd1fa71c7fa53d09df1f26595648d7c1f2bc79b61bee67b9fabd4b03fac23febe146447b7b1004af3d41758869d1e013d6e47c2b176ac4ba8105ab60e7dbd2fd080372352ab202b51220484297019c3af7ee732ee1ef8aa215dcde6156a2935d29d4834c953e694beaa0a67b88606e3eedaa971ee2886b43612b127b1a181dbdbf79c0e2000f501ec8f3d6c9ae4de7a308cc4a2b826c90102a7dad7be6375f581511b6d24698d1d4acc367fe309fd01f2211ed6bd1630b691a1214c420861c84d0d0150d198e0f098099539e80551dd1d654c9623f9f455eb21e82a5d1603f452d1e1279c132d851e78238b37164faee2f162ed9ba359986468815eec665b69ca62056dc1315c20f544772e9c62beb82d24691a107346c5b3c56add2edefbfc86af8e9e891f3eb089af1cf5912c5723cdce4f01156acd2ae4c0eab14385bf40a6650d99e23b11d51b8c1000f273558f2e5692fc7b2ca0d3700a99f2e9f119ba4375113cc2c6ed0e00141376acf315190e6b3b4bdf4a8ff1852d901e287ae2e1a9c1115d295429a72b1f89be47f19839e9335bf4fef81df0743641f8881fc83f2f42c0f8cc3fc549a089d18e434649d25d181808ae6a2141ccf1f0d176394bb8fd14cb0fdc71cc3439b300e87b119302395accc6568e9e771a03e6acc8054f2c259fb5616e288ca34cfa394942b05a749d5b50f56a5db720a507bb95fecff271b99c59497b443d4eef7652724ac2c731d92c783183b22feabbfba51bf53b74fb5a39d48d50ca866d9da16157296972c87c994b8b5f9837065664bc6715a19b160de2c69dd10a7f809eb1872682e64b790ec31ae7a04670d2e3550c00d3457d8641c624b1068ff063ac2b86b8c57c63d5870454165ea6d77cc51c6ec244b86fc3db8eff6dd67d33467b3c53f5c60d116b3c0b5c5271fc8e08700478d1a4f63d26539f18dab2e461e1452a48fea340695957777d811b597f40775e0b543daf73acad085e9ecee3515728144b7926f8a45f14a1ac5810fb8d98e00b024e16a60d17be6465f6beb7331c96952d5b21d817e465f81ad9290a9571e503659061fb33aa6cab045df269baf34334c20ffcb1b6c870c9626a41d449bf7e8363d218329320b4033f0079f0795d40f3205835d456493c8354b6e73e0f7ee49739424df45db73363654f1ac1319cce184ea035939e5400b27ec3e79eaf34e73260ac6b67b811f4a0504993443369995fa73f03cb14b6824f5cc89d965ef85206311f25455149bceba308b837dcbd7c3af1b8ed4fea0f78ebe51d02fbcf408fec8478cb6772b3b05b67705e5eeebf83635a1bb2b75b56ed43621839afd7fdae5d3bb6b92a1b622805769464270336ed6ac4a1c97b2746502f5f0fece1653f9631aafbf6629fcaacf116720258c720e4ff9fd41f11cde52147da87478eea3fb137fdcb6efbe92005185eac505e2c1a34946d2e91b74eaa29444581fbdca93d86013157e8277839cd82cfb78f6a63c6abdc0834afddb216efdcfcde3d8653ed75ef8e245f113fe433cc08fb5a529e6533a5efad797d758536ec40500289c2ceb4b09b392e24a191261d6ff18f3baec651ee7810704929f84e99db8dc5d9629c8ad581e22623fcd2562989f9e2d4550cf5d53e89a29907917b18ff49a25de31eda6a60134040b46423069a963010ed33285fb15fd6d178a704fde614a45cf2f0d63e631bbc8cca39a28a01f9f8140081e3e96d17ab7e303aab8d3a48ffa48af490a2855ad3c9f54c10a2318a4fd1e3d82331c777531004e08979565914aeb0b0533968c756266a2b5dc2e4705021ec9fb02c38d178a5eb04268df31530345f3ce055a67c5473f49f0916619d429d8859ea2bb71179f15c63be8df80c723a09789c055a6258571f5578cb630854f83859f3821e5497ca519d224a4e8df97cb399136e9d970fab4cfeeef584a5d22c5ad06284232d19553a6af089af2a3caaa6753a05a0d3a50117c0666b3c42e59b9a1687353879d6c56c11b6e18605dae6191e63a3371694b26f7355142b8fa485733a7012e22c3e67a263cd0fa0e93a8e55674e7d2aee4e64848351274408cbc8b7e68560bbc6be5b0491327efdf47859a7ce6dc57caf35718e017b78e5b573db47e9a7b69483a83f430c4b86cbf1c9feeed17f412e86525d2f8a809931df10b5cd511c54d40f73fd4d4347849e390d7d3807f8fd4a8fb16811c0afe4f68b88950f7309fd9cce104e6940d967683b92491c13f5a5a6a773c5c8444d2a52c994968ef7eccb30dc4856a401a362c03f6b019b91deebe423e508a22b64d1332c0bcec831405edd500d33711c34e8fe1bbadbf7451e5db4de9468c42745daf4c137b8c6329b595d37d55a414654710b407475717a0d8d2a51eedb058cc39a26f55e8ffa200e3b2c87548c6e028cd82eb746de0de2d22b08c005b6df6b21822963eb7f3c335ab86db962350cac37f66d7e76d09e5adcb213a05d8352e49056c18e8b369ae9283891b61ae7d9bc0eac2e949ea833cfb7671a2ec329cca92cf2c338183ccef5a49645cce5ca21c9f743c8937b074640d2cf81076a6cb7e88d919f890d3be1ab251381bd43f6886567b1fdf32fd2028551347f3499a030ec24d0208774a74827b20bd657670ee4bc156e104b9aa56523b8415e811409ddf5a426ef9a39e70bc5a4919b7d941c9265e652a87f9244facdea5a16430f833f25d107f85c5bb585550681ca93ee91118d190e1015690f62ea6c75e534b584fbc583804e757551a24610c95ea5f90e5d4a3957eccf9dd10e6c412690ce21767038c73a9164b104e6c441d9fd12e7ccfe98fbb70d2805e264f4f0f61ead48d20d0f55f1b0ff5ca8cea4a87376cce319cc6b875cd773b3c9a0525623ae81de8a91805cb3a7dd3893130e40476e0ad53bd886f16570442e0d795dbad471f897060ba5f022c8f72d359198b8cbbf2be3a157b1c1016fbb3a6704cd64c5457423938457f6346348b792f6963f6b598661e252554a586e00abc624a0174d1e337e64092bfe32965be53619b080b4fc89b3489794f673afcbefc33858053c7921a052bb6b512210dec070d7af76c962f3a264b24f67010fbb2b135b02f75f8831b5e491079f8c76b8bd1923814d4a3d6ac6bec0f4a8d5d1f347daf9ee3480fdf709ec19b12607fb7219fa20f3150c82bba6486b1f83e2896d3673b4bec32b37c28c27dd9d413e7092d58cf80723a0013fdbd81a48550b29baf18333fe34941f9d116da3779cc93d506c4c24019384c5fdf3574c19c37cfcbb69fd4d7df28f629bed245b255c0036591a3a7400573d7c958ee07e3f4d418e8dbbed3daf007085390afb8efad3a008c4b5772f58fe3eb9e6fdef3c67ec13325bd783ebf88a94deecebffc15185e663af52065b47a4bf2fc56b0ae4c45002c5a9a2e129307006243d88138dec16990b7abfc0d138d2b0b29f5796a1aa4f176c183cb4c185c34a1803de1779129be91cc2a0ab639569efe27aa1c002101e000bb6716bf0785d99e25702ec1b1810f608943ab5466b3c9419ef21d40d86d4b22d7dbac9924af2ed075615b48326ddc52e50b8688feea26fb4cbaf6cf30597dad3679e75eff1aec280bcfab233541c861711dd5fc2ec1799d04a4a4eecaac2b55dba90f80e1b1e0f056b3dcef28877b693094799d724936737ad030907c7845c5da9c6772cb9cd3f16793a68fdc6e9364e059b0f27aad080a68d73939652e07dd49870293a8cffeaca40b5c56b4a3611e3525835d86d61f1e386a185b4a12a5013a8b649858de16279f9d86983e9115a314d488cc8cc6a735a702a689306d95447230be4a2b7ee1673bbcb0a3b8ff2713aa33fabd60b0b8505d2b39e7f5439ad3716f4d5cf6d6f944e74f2c26c87a8499d0453609ce3e4058b89ee240eca7a82a130eb5a36c8a2400958e5c622870b565730186fae7aa3da1830c04833795bdac7a389819403318e39de161c2244986cb9e2f049fd9c88c0acdc6dd87abc9ef69bec60299fad16ad1b4d388fe526c2e4716b2cf5d400f43c7a9c2cbff7068afbd36fe67b42aba82d850ee9f27c373ccda3f938d83ac171bad05e2a9c559f143d6f2b68d75ba5f847b880bf715730361ec2746810c834beb10004654c4076679757609949ce3ac2365412eab08048842385acc6a4fe099c6da1001245e2a5734d49d878a4cf2b2c0f5bbedcdb23e05c2cb10e12cd08514f7c16f0924da2256f9c14b8e038170fef7e9f3d2eebb96c03731600ec9e28312a48232d4bdc0063f40128d5945a2b0bf4c96dd479982647c92f4d982339f4d181c45e38f5c3dcadf2baf0fc3013c7707110fdf81d5d2cae02abe33636bc28d53aefa168524363d0aab514150eeb3c9571512834b2a90437132e1986ed4c693c35142fbb4539969900240d3e2e9b1dae5adf15ae0ca0d97c9fb102f8b2e387bd030b40d6c1117c7e58c74994b9e6e403770215adbae0c80532f48bd9aa2921d50a96270181a56d024a44c8c10f39b8a4a817a7266f123bf847cd593b41d6bb087b170983338d8024fcb762b2293e31bf39746434fc4ed803ea33004c2323c5df5dafbb8b47131137411057c7336b06eced3a8039d4bc77d930721cbe056f1b0763005dce71365c8a2a42ded545d668cb880abd50b4bcaa1945d7cec373207f0567c890806d93c7dbd964d38a734273f07df43ae5de34dfcaec1a70961b558679f43728594a92404bbe974bb55e0febafdf7ffdb2d08be2c509c49b59780a1637c912de98e368da19e4770619c9cda868beed4af3a5fe58be6d009bebff3731c0b44836a1852f1a23a82835231f674ccf2235b466b86b14a8518fa048aa6b081e587c18559ae6566cf34205e88ea766201cd2269033d626302d7ba4fa2ddb47a3c803cdbeb3eae3c87dd9b1a40c1b68ee6d9177e12788d23b9043c166f16f8e80a8a14b2ed83e9a35f301931b120dbbb6fc9f9b93b1ca09fab8c5098a6835cb900bd45fd2a9820748ed9e9da0150c9cfb3ed24d389b49285fa960c7eb027ef285f1ce8dafc8e69b8b3ef78f3bb3be1946e926eddf62525f93433cf32d02e4aa6e7e2b824842b5d14c21dc4c865598b2a33293386b1970b76d01fa117bc959bd1d784835eec16afe8dd2c6b71227620aa7802529c354b0e9b00389afa8477a51f93dc68b7bbb09ab4078dfbf3ba6f1ab0faba47e4cfb9b1221eab52f5a1b933e563dc3bbd4b7ea43e8ecc3dc8d04b9fe9f87744c7cfe5ef63959a365ec23379aa5d0cab9bb41ec3c030308680324d1fa778c5f15bd3b6182c71f5011ca8684a2a1319a31664175c56724d38c8ebe1e38e205284f70f60949dc0cc2cf839d509013799c50e33f182cc3a58526128f50220295ebf9cf5d1be8a07d95d318857a1ffa572ceac4c8db29c0eb565e84242b5b329415222cdc86d7df79afdc473e7d7ff74c6591dfdfd3a70d1df493fd0ab28535d8720b55e5c5c0274565e899d245062d392ce501a64345fe12696145f433c997e705b6566f881715181de66c4b82909df2330e8edd15ab37ca56aa8c09de4b2922b2a420b88d5163e1bffa1aedb0751b7af321dd59c42dff42548eff2b81d1e2291ed44316a3988f3332393e7ec0ea90ad7c629a0ec9f660f6ac177357abfc686ede2fda649ad19a3563c666344834cce935e409b0d6e8f2b1b0f890d4ca7e0f7a964c3dd0a1a5e2b618ff5f62cf8678c6b5645d86dc8a9c9857dc49a27d3770f2a10c37eedef9190822a20a5206142220551bafa3a8a885204b74e81625cdfb52308ec2b7026291cb71e37e3d930c98a9d4c01c928c3211e0f1b765c13ba10fb74e066263f72747f2b8f47d5daebe56581ee60ac42da8c99831e7df649ac3de9137e1352a935e5c53bdcb1d7793bfaf4d400cc7c068fe29395f815b967bfb36c93d1b2f0911576fe5e9909fa3c07cf5a09511536a4a90250f4e75f6d231b2b4b1ba12a5b5b126c2331cce245d862545c422352050edfd9c35e629867836b181df3334f64e53288965b487084f208209ba9d56005890cd9cf0a8bff9610d222f6ddad8c63be6183f77d791941ff89eab7519716da5fb43b473d1495a3b6cb0ae311b4e9ef7ab04065e2d3e3228ccef6380e784d7ab71204b9bc3da46735213e9e30264385c9ecd817870201511f62d4de2c79ab5e5a6a446118f1f4691e26ed5550d785330f49cf5fabd45c51b9df11dcc5afc94453008dafd1b10130b7651b8d24c77ff803629a21a9a4a7e9311cfd8d25c6d2eab8b3b1100b64080fc240e2f30b203b4c047a5c81d5f3b78dfae0a050e6efb38a4488141a9f9d0784fd05c4f1f11b3fc68b39990719f844b8f077cb0895a0db86c4b1f9aa69d6c1dc0282a1cb1692c1d9ad9a30e0d1b90e86bd4d8bd3046cec0e7a1dbe6f58a2fb206f8d4cbc30b9ac4bef49fe02989585cb57fcf30b31aadfc1b058227863ab49c64f1a02601695890de5e1f5abb79e91c397500de8682627bc4146410464045680c400d8eb9d31bcbcc20b3d2f72e075107eea31578a9bf09716605c000afd3ab8e35eb4f1295795c5175f3ff79d8c142d06b5740d5fe54760e0dc08fbbb40a46dbe8d97256c724fdba2b113bc1a1ad39879bda320e68a9f10b43f362adac3474d9df8ed84748a89698953ffe7ad43565e1cbae87f656e26b792d95e0951467629fbfe96374e566a6c4e48e2738054e36b22f11441b8c150075b0a9ee7a7d14941fdb33f05313723c5c2e097ccf02debb3957373c408efe670fc26e944529c7946ccb85d258fdddd99609d6738be5d14c67474afc6bcc310c97108287ce45b4fc219fcd1c0f0841e9f3465f61ad66bba9c5308b8c92f8e69d30650739796f3532d4b82ce871f565685e0d927eea4a844d7324c06515e1a4bf4731e018bc21edf039fb42e4cf04368c54832ee46321a1277cfc61fe2a7f40a32476baa664d7252cb65285eeb9db032c3a802b9cb6f6544076e08e8515fdcc8002aa4e134ee74a156b780984e8829a95450334b484660c3b1554c104a00c15b362dcc70d9f88e0cef59d68c26bf53e1827d5f0cea38f94b41021ac40d4eca1c17c65a751a60436d81407bc48b14918cfec70c8401a0a4924c452b61081f203933103d000d5ecfab2a3036e060ca438a6017184fbc333398fd94ef6b984620c5138f7c9364caf18ce6682c6d79e669cb8a44d7a8123b90f8537eecf7731a18100e260bb03e6fafaed940c0346334a13c26c2607b37175a6ec3a7ef1560648cbe1febce7e14c561a3869a072cb8404ac77065ad1c20fe394e2cbb7df6aeace1eab0a6dfc24116a4b1867600f3a0ea99ff2547f6eb3a3fca638ca7c937e0e54000c45fd737a60d18bcd6df3d77e843d53c6714f93f08fd9fa4dff1aad3c03752e2187b68d409f30fb4ea90c6b7a9bc60111f3f90b4d33fc0bec7e3cdcbc4cf23bc8a7f936a83e62a4489b651302c55b7180ec0591036319dcd56be9f9053fd196736c87d701a2ece699acf0d659a7f265a3eecef2697fdb4a269b158fc57f1be45675c0bf9fbb6d2a85c8260b0efd0aa605056783b7d4e19f343c8b0d090a973f7eefd148abdd513d86a117b3e6ac43996c02492384cc837abafb83bf46147f52387cef8d7e3b220321a8448a4fd2733288566c9aa7f552296210269137074ccf992bdea6f9fdbf60471b3b568baa5d6479710d443d681c553a8c7b724d4165f2bcdf48f6fb181b3afe6377f37d031fea8d0ffe5035c40acff231843253d00a5ffcfe24bf202bf62066c00799f81c72b34855f71ad582411c01b4438aa6f9146988b83959ad7313118e5c1c123180a10301a4226e62f738edb332cbcc09504c65d511e0041ba95d06caa40ce00dd21a600dc00a61a8d602c772a63cacca5428568b7d0fe40a061f556b2b8d8c5c38e7ce2296159da74a024f5f0c60f2c366a265933884ab197babada82324d5290a1fe7306c92d67f765a71aa4ae525d431a91a053bfee1162d9ed861d4748edde1385a0a5c9ab89d77320f5cbbed448ebcff91c6c4d5e7ad805fe56236769ebad6cd8abf5dcc405981971c0d9b6f9e6cb260f15468291ffaf441e4921d9dba47c9d795b1173ba29e6e40b0943460d14c8fedceebb696ec9058fead31ff96e7040d0839d7edff368dab16fadbb4208957b0c81a9b5181297836c53f4f48d1e339f87bc405c88dda9b0a2ac17c013518c3f4e924e440290885227e2eae114024abe58407cbae8fe7131d7d59ab88b89188715a39f28458ff866ba0ace140b3f150d8df350fe1eb150217cd51d56b21b2f248d7652cb2a3e0f0215846c438d098d7ab0b2f44a842ecb22d4cb26e7e7a00daf97b3949dbd38d76bf4044b01fc1be0865098a720a608dc12bdbb91f05f25d741ed50bfca1f8e6da2b6fe4e8056aafb86663550f9f7d3f0b6e8d2e4a6bed3e8906b693ff6f859eb72e6bc0d13de132f8eaeed6b6a3300b50249c8bd15e1f561007bcb1a097091207ec778603c1ec3f7a22b876c6ff088a254d71601334e3568765b69f0fd58df6b2925210670b97844a825e520387892cf19a098af0909c358d21745a6a431ae416a51bda403b9810272f787c0fb3e656ca6c1f4d4d66c5f23824bd117a5ec18fd37f7ae38046d0ab10d88ec754308473f3022de546ea2237567712125099fbf7cb6c0880c1c977c684ce54fd95b743bd488a7cf98696225411b14bdabad32bf9740e5190fe34756a8b57f025826be1033a9ce79076733b92a6d9d4c509cd63a8c7508e0be187edf7706655361167691033a178e50fa0ce314a5baea8d2018d94843cdd5b0f367e68048f0857417ac623308b5a3a5b9126741aacfe47e5037936139c253405bfe105a9e6e2a2b327913b361107e64723be70977faff21927d754c921483ffd3043d874c53dd4cd96164cf7aba9bf674f87198d6bdcd88f503c18643651a932e8a9a30045a906e23dfebdedea731c2bf22560fdd1f7ee6e8497560a5ef3c73b8c8d939ace1b3bdf0a2b32b9e2bd5c11072c230690d78a7c9d676d424fca12685704fa20f279410db55e0801658dc6717dcf13da36f021dcb73e1cc6093bc46c78d0404f90e94260b3386b966d77237136fed39447055a4c499dac9f1cc2f5229b72fdb66855203292d3da20331e2216adec7eed87f0372f93f00317a957bf6929f59a7f4b79c46d64424eced93450b89fb160328d467625c2b83bea3e908a4dd30cbe14bbf425a6a0b795ce807a1bbd5a40c08091bfb26221b5c54b244987215babd56206ff679cd97312a58939d58ec6d014c7f6e41c8624f788a5488c940c272a289c357b61ed9e2a8422ce74970257da8cf7d766d63157dd6b7203e8949de9947a21403d2e3a5ae3e18c4c34cdcec019befa4dd4a172c19d6f6672c4541ef8d019155c84ca7658f94f36118fa71907c7ab2dbcdb3c09a0938f95a8d9b661745e0e1f51e0f798a9c1585b29c1086205eaf3206785c7b8800cbf0228747c032ce0a7ad8365f9f4198db7ba45f92eee952c12ab53e820e9906b8231072012612df4a3ece7eed39b16547707177d48ea0e595fac7d3715c153f10071512dc75680ac07a47f0f5fd9c8510d00c4274cb7e38b60c1ebadc10b202ce270026874be2aaff3280964359331839eeb8d12a3838242ac5abce724ce1bc76087ef2d0de4333f09ce8466a45dc67c9fa56cbd6630db5030ae5b3efddd08c9f1a414ef8a4dd389d5bd8bf225c089476a383ef5f0ccd4045960dcd98d1a972f85e0c7952f52d09fba83c332251eadf2df5012f6ef7dd35c1c014b1a0dbe138900a5ffe65b41e6389aa6896d8c373f50f4527859adb76660d147b04d8bbdd48fc07053c3ad0106808f0571e216c58b02de7890c224b4776dc9443004fc6b8db7fd4ac40044860f4591217c0d1e32d18ad529e80e60ba4e2ab6fe6e21b94a70b55eacb970c6fab453e61ccd5641b3dc172e7be284c697eab2df18628491a930894900fcb9b10e6ea395a5b953f9cc03d873d9f3e8fc422892a7f0e186e37c073771979eaf59c94a1fc02df6002bd1366dccdef47c7703fbf0709263f57a3071125d63e7546e872a64aca4b0ffaa20449d8ec3337bdabd9150b1be0c1da9f59ba6aac66a6e0c26491b8e21587d10cacdc82a4e0b3d67f70b7bea2d659e2d84b8c1eb5e1e3bd53b1f453c110246ad428bc2347a5f3f2b040d44eea776384c701604ccf171029b1e0d00ff1ffaa7af5b5480ff284e9c834e0f9c18db21eb4f3232d2b8abb52ab049d28562248f652d7d3317c74cca34c1d3f76bada224828b7e967a0246cfcaa4cd6dcb070788b5d436a31606337e2bfb767aa6693505b5b431f6cf8a8debcd02f6c2b517fa6f7fbd09bd7dc2d8f0f1e2a648d5d2874c7f1562d913d4248fff7ac452822ce1678a2a348ca4ac2f463462cc6a1294f2b4f49f71e3c78721fcdc12d1ba4ea036d4b71668d7039c7ba00fa297434aa0731e6d9cb98f86d85fe355edc833730b5f5e6f27fd4f75c29bac4840ec173d8ff434e3d0d6820f67d00861ae129a707991fb0e6034e813e9d67806685a0dca0fc11a94838287a7830fea05a772aba9e71c036bf1a6d662c7858db834c616fa6219a54bc4b1629d93d100798259ed197ed6569e7fdd6f5c7e734576b8c7c2a2062e01632e7e0ca8668511d1aed973a8bb0603d8f983d39b50d93981c835b9033802bc53945f784e7e511d3e6860371e99e319eec25339bb5b9de9f29be556d4541dacd3e0c873361fd4415804acb405e8c40631494d05351b67822b3fd46a2ba08278763900c2dd8ebbb236e153299f7482e12176144b5427cb9f452c1c107b2b95081c25a12a80520aac2563815e92e1fb8e0dc3eebd9c59b3f9924b922a46b24fc5f1afe422826287d80174f4dd90098d4462e2443fed50c2e0f60bee41fba1293087937a681e423ffd002a2c27608fe422898f83839e32f444c30a12b3c2cf614c24c192b37e35660c22ef0f9b5fb8cf4d0d78765c04b0b04df16cc4d7284eed03aa402083f60df1fad198da56aa794224045f3dc773fac02c80a0c625e01029166340fa0f79c3514ed3dc8e3e582e344590704620a455fdf9ee276ada5938c215e8a81a00f7e6de9da612d7b8a1359f78cdc30be3ccac132d4cc9c6001b2ab008b2a6e8928dda5fd59f076ed88518a5998fc782eb0cef64c96d9d902524d93ef47bde7cd02a34d21e8c4848a57997cb1e01a5c4473896b0a61eed1945cfa8192799d9bd822543cf6903025b8422b32a5c8df30be8ddf5e69a8145394844cd88c7e6008b0f8db5053f4e0c3ecce503157114da2d70fd71df36865f32698589e75fd5717b32927673a20d81d1d80864c07cdbe81536d05521516030c2201eeedc33d068068f678882e06e5f7c3e03c1b64b3a426791894ab3dc18295be50cfd9aa3de2940a8f39d9e90c2dc716f8ff7e0001c2992de0fba2467853f1384103523b01d0c468a29e13006151828a067fec32987b9cff1830f6c0b57ded330fbb7b9438432682e5d862d8414820f4a462e72159fb1caf3041b0c04b64c23db9bf3f6eee007ca1bf54050645432f79ee1826c5ca80f360721fce30db909c0b3ed6f681e84b7fe844298f484cce4ae91c39c5fe857af0bf8bd7815f81223c540e41299c2620fff56621a495dc6f2ffc2e3709b1de843542672c40c2da3bf53e2fbdc1e35b794b228880eab61aa1ba0627c907250859d0dd52b8d1b6523680e303a603f77299459ccc40dbbb7d628e0748c659c41910b33293300a603e42165d6c3ddbff7810e377e6154a8b4498f6d4df434a7f0a31b243036759d655b84ebc5cbbff62afdad1d353283fd21ddc6c1b7e6e7a8dc9ea75fa3d0c03b1d45942f6aac8a511e41615f2f3f41719c7375e0662d4e86a54cb371fca58c2cb851bec16fcf27e862b13b2821ba87b5272b3be947b8369a1cd312b617698c75fd452af653f55f05d789da600f69ec5ffb2f1ccb4d1e5482ef2f7b569c9537b2e7782b1b13b267b099b457cd856bd2b47335eb3a379214f5fc881d213e53d6e93b0f169c15514553319859bec3b5b2cfb0805689a0de3e58824ebed64ad54aa5d546dedbd343a487cf8b891b8338f2cac0c9f4551c16721ebdd5aeb950829537a237640dc1899229b9b564044aa8713e054d8edd9775deaa72b2a96c68f08b7822b48b79a0b2068440e8f92a14373052fa3d3614a20dc283918404a9204f88a20641002240156c300449040e370299497b80c834601838479549c35fe1c078e4503c1c673b91adc5514123a9d3e4bd186e6e8336030a9400af22682325022ec00363ad52030147a6299d16228724a2a178c2f91118014cc30440e7f848d8e8ac6c42978be1c5e491ae22f8190204bb0323015700e839086c24be0b0a95990316d08260a1de3f1782562963c901781cfe0a0a248e16e0c26962c1d4fe9f10884207092acc682246390f3641912307e1cab240082834003a021184296a0ac20676013c1c5c02258149e89a3065773a779f29c10790724004749991474e03a500504ca89c0c9e962a0394e808c0702820cc182a070be493c3205956e1927097bc04b04e1802283341ea6fd002d62b41dae99400980794878cc418808a6453620700018000400a4a4240a02424207434f5985f43a5c741821046e3e942dc0c393f4c068f202558530a3c9a074ea085404661927097b1099c19f8ad7bb763713bb3dd8db8fe151f420b6983995dca46b6dd6df6eee2236edafcf2f70d33e0d8d31cbd4cf68a55d2fb79d4beefdf8fedc3dc6d3f3c04df3e5f5fabf3d0ed3e7ec0ae8247a87d60bff6eb77a3fbe7f8699afd2c863d83fceb592dfb4aecde8d773cc1c3e5680ab205e08d5b4b83df48578a6064b8b179735a48e82ff5b2f61ffb6bc553a06f46da65c70b10f12677388badd2d7fc487ee0f5bf3110582734d71346c1105200326009eb86b650ff0b274b32d42b95280f802ae02299b2fcedbd642513c7f60e9d3889c43fe9f5f8419a0d53444e000ce9096fc6403c90463144393a17398c203f7dc597ac30262076b918992e61b205361e9b67f1b29c8ffb627e6b7a1a18b26f6afe2babd5001c2d547d9b48994a130132c159c2657c46a0412a69c981e8ccf2947ceb557b650bdef86d06f4209282ef3517bc91666f0e971ceceda4b36d26d7d4ebf08f1b188d83cdf68b84d78832a233d8a8610d3bf8b1bda092696fde424d801cb6f75995340d59a941b9c608f2e3bdc4c82856a7695369218878b9c06242d6aa44e4cacae71566e031a901f4420744e492d5f354d4ce2bb58b2dacf2bde5b3f4d4fe9ea55e4d0509690820655406dbedd2fedb26a616ae1542abc5f2ca08e769db510ac49be3c19676f17f8263afc5728b2eae06bab7651b2ad3ca638aca7c3c007595e9d945b7e040551bddb45bca1776dd5f4d7e6b27ee5ac694ed9ddc5f8ec20aabb6ee5aebb635f227f53d373464f079bca5125fdead9470ac367dad614befd588c136b9b204dd521a8999e37e9bbdaba1abe423b9696925cced39a3d6497ff18b786c166bd0f6f14d8ebf0f2c04e91577a84fd26fd0a4ebe698b0c7a589428a3969834c5856d5ff6f2341959c97e0a411581a3f58f6dcf14a7003391a6804efa4c1788c52071487f8c1be4863b04e638534b7658b41e465973ef7d216a475f82ff6317c630c75a10acda9696bc66765169789215556579af079873915ad2a8e06d2546a5a869a844f57828a02afdd9cc3f1cfe56b6f867a6854326b909d01a84ea34999351a7ed7156f47a44942e5bbbf6610c926ab01a8bdb9fdb500b31f038c7b92cf42e4ed78656a0c6192483fe910c5c3b2dabeacfde1195304521f72927123209dcf407706a2984fd725489824297ddf30083087269e2835df97f39311cfc96d11304f1e06bbb99805a0fd726d40b41af6d9049cf609541afe7ad59f6979bdc2270f2f4fa35cb8ae448e9bdd8322cde41f064c33edbebb4ab554acab7ce1e8800257446ab210b823f814cf2bbf3de84a681c5bc18aa5e9adb7ced6b3476220158b18d9f6840f7bb155fed4288c80071f8fbd685884c866c4a9d2b70b505cb6810140af89011bcc64e625450985387720310aa2bef0019e08e1a23bd78fcacf73e4ec7fd88995255f4b77f6f74a5c24ad1a15f4585a5200e99aedc9ca6cea6f32d4cbd6e09ffa1c9f32de611ae3198f4759142631fd4ed005e7f0e27ab5fb8c4ab9fbb31964d8878a6d69b22e11256965ebae9f088d86cad73f2dc9fc035c348ed7ccee2154a6dcd4fa4bcea7dbde0400a72f4478e04b8e84d9f0bdaff7620fd4bcf059f7f2c96612ce8debfdd2d570c839697cf048bde2b52c81e6595fb2eed4ad02ecd1d351b42fb34f01c6d136cd8f67b281e5a9fb2052b934fa44051669439109a107f52a68a6f65dd966569a62ff193f0ffebd067d9ec7cf489f49102f3a3d76be7fbb15fead2ebf5df65188e7872bf8c08108490a2edb67d8a623ed62aa7667f0ea7d04eceef7652c4c015c4cc9f7b4c949af32bb5a7231ebaf7a106966ada4fe31d1e65ed4c4f66d6533f0b2915f467bc1ad4702d727b602544db84c97e84c7a655c1eb28d1f0a85d42398d35dc764fb494501d17ff03d5a432418ff344ab3cd337ab9fe8b42b1fae5e103de979a2dbc0eac7a2e95d0e22adf2a0bfb592077a43a817825e10520fc4057a3d87cef5bcdaf3e99bc52b3c8cbe457195077a9b151ed6eb433d105d8fc260c4641e7647957a70eb6069886fa3f7acf43a9826f729419a72f7b3b867baceae8428ab5e8fa6773dadf4a4f4cdc24a8fa66f2aacf4446f032b202ed2eb91746e5a567b365db3689507bde32b39cd47957e36c48aeb50f7054acecc412745259e6e1d251f6e0e97f2a0c21e4f773459fac1273d3cdc6896e2a1f482d82aefe1d1dd290de29eb5b246795496029ab8d15a6050ce5d4d1bc92b0c1007b77b5306626cbf2e13a3c96f1fd5fa90c1e6c186b217d81f0b336fd0bc9ee601fd4bf5c7dc9ded9638eb27255a8e7e0530bc1a488f7db9ecf6ba7efc018ae8606fa16c8e2144edf821f5fbf8649ba42412d9dd9b5b06d605cc0570055a7af0124e9391030e220e0a07c4edc0f1e08cfcd0419e5395c8e066704d3890f7e08ad420837314b9cb57111c3ac873e86bc816d6a551c3bed04383b5d65a2b330307723f702f22473c3feaedf245a4cacd01941ada941a99151c5948062f60a12666dc18b68746bde10c996b7d0d8981f53234702e8e3bb974f91a4283212de820c7750c9da9a63a28a4907be52a24a51b97a75e3d1e32be3d1e589d3c80e6daee195d0b1f9bffb653fbaeb5d6292b2ed9af6659b6b9c82e7d7b3c645c7bd9b35cdfbc9ab7e6c9162e34d72fe5fc5ad23d7ba7c7b1e9d5c576afdc6ae9e35a6c4f77ea957ade92eaafa5ab7b963ddee7eed5f340eef6abd884b90a15a6d31797de222d5fca5c2449111fb222588a5ce945aa74301bed5c440ba594d64a5f4332eebd17e3211e38b22ccb348d0e2519ea511a4aeae01e1a878c4c2e232fbc8cf0ac983132c573fa2a42c4506ca88a11197af7a91c93c9c47146707051a2659078f28ac143a17a4a3559b12f32b8424de018437ac21868cc69c91071886b0850e74e31af2136e4d701539aa6e9fd1a6234c48a9665ac8d689923e68b1193489697944a2236e85dbe888cb9872ef92992a377456ac02ed0322b98f335648bd534ee0bbd7b11a1a1d66a2dedf12202245f44885e4492bc881cbde541cba82047a6a807191d3f7490e34e7c7490734a7e7490e34400e920e77208e920c74d30440607eebb7c0915790919e1e11c45d6e54b484907798e0caf1f555e4060e9b0c4aef45cee0229a2df0e08211d42332eece85548242c495abae40f7b1da0dce3859210bd5197fc5b49afa284a9a5cbe789bd8df7791e5d6644af6286a15178145a0991a638451e1368f610ea3249971c03cf33162e49cb0c4b789dcbadf0a3ce68001c85cd7f8be23c97fc61763e0f0dab1d122212878430ce4244fd79266a43566eaef7de7b6f34a93f2fd1d144aff4e71cd2a75ed33e402db0fef8709c8b0f507ffed282f9a93e19588b8864a870601d614e311586aa4595c8cefcb9da6a2dfd69754c8c468196a73f975975fbc19aa5267dea5132329b7aa5feb0aff2b19fdd0bf5e7332e9a4db79d1ad49fd700491b6b63437fdae4528d08999c31f539e79c33b359c6b7d25a53b3bf358e23d7c7f1ea714c1ac7711cedcd274ee49c73ce62e6a876b33c8e49e3388ea3281289a2288a39d3db4bcfb22ccb322b19a97f88b531691c476d1c335124124551b47b33b9c078d483e9a3a8699aa6692564aedb79d33169dc231dc7512412b7484551d43c1d8b21c562b158cce5e584ea683ef860e758e90f3b759bd4df6ac9f2388ee3281289a228de300cc3580c29168bc5622e9790cbe572b964b60c15aea3b1b9e1dfc340d83939393939b655c22863611886a1188aa2288a351c7fce71ce71ce717e28d439feb75229cebb8ee32a8c7bca31ad2ecdc5b638db32d956c9b636abc5b2a4711c479bd150ac44a62a96aab855915651cb2fd6f0757c6a58c31ad6b086fd39c573bc485b8cc6b45816cb77bb66cbf53a5a48e3e0ce5c2ed7d6f56761205d519e57618f1ee7b9f6817b9f6c6c50a01840072d0ca4ab9dadf370e05d75d88dbdf8262b6d1796011bfaa7c4e1870e1a03c6580613338608c9010ed68401c6c760f9d0337594c0889048faa16514c48579635c2004820524204be410c502c91124e40a8e1aea0a51786890c47aede0c425a4e5c20cf9de7b2fc61863cc73341fbf83fed04163c871438324397cb45ce0821a4008514bc052800c19312021d6a0890f3e6654930d2ccc703febd121c80e262d5801e2c90c6c0f213e6eec3861460d361425f1c30537ec0f526ecc00430c164c0e6ac041941facd80e7cf891a5a7079f1d1f54a3861a170b8d19661096196262fcdc7befbdf7628c31c679462541081a3330d2821e7ec4c003126b862936f0b8e10711375cf9b143901c3e3b300c2c1e18631c43e27befbd77870b345ad06af1ec6802648b8c1d33084fbf3172d0c14585c6c7c48c1f0be442101d6efcc000e363e4a036088192a386cfa1860d63f4c8a10926433560101e81c38f257090247248e1b9ac1d3787cc8c9f1c6a5c8b79ce5522068f0f2f21af242f2aaf1714ad8a7e8a74280a52e4a3831c9f49811d4203312580984115a21a4133d0afd6d23abb2552493475f5796fad1feb8ffe913ae534d62be5f5a8d70f12723d10c618638c31c618535dedbd386735cbe8cfecdebca96a9669daae99f6a3aa7b2b6da26adad5e3b85d5d4c9a73d9542f94c3c0ec7a7ad9d5d327a09350a73cc6d5eb0375cab5cca6ea767da0999f99a974ce64497a9e5f25258d45444730222271fe4ca0d6f4993e611d2b12d25db17369f5eca69ad2c399a6696d6f4d6fa592a9b4396d9b5c5e5e605ef649838941a164ba196d6686fe9c99d9323334343534dbe68476e204fd79e2c4aed9764edcdc7870b3b9a7791efde965142080104208bb7a0048d5548afe4cd56ff36da77a1f6c2a5e3d14fa830dc2a69a7d86a0430881fe0ca13e08b954d3817ef9c3249f265057017c68f93d7c9056a80f2329295112273be8a1c3f2e3ca8ccc897ea8a717d18c5e82410739ee6304b507a02a32525e442cece00271d2c4cb490e3ac873aafcfc3939a09f73e0fb647f31678b29429c88781598cb57d533373890e32805abb5f4de6e94b47edd010627431de47470a2c4c9918eeaf2e584099528c0170b83e221284be763c0903abffa14e5419db228ce4ab2807d37ddeab0a18dc53ea65f6e75846f633616633d953a368b2175700d99a2af8371ce1c879f859f45c37a1d6b6905d227e5e993c290a99933a6986e3fb12ddcb11a9b4f67bdce9643e290aad81a0e351c6a38d470186a3d38171793c9643299c2eaca364655b8203d0ee556321287a54beab19429143b75897d8a72191470a029ec94d30c5112250bcce9e420656d39905e85fc2d942c68e5e0e6d3a9169ed461f907350d7fd77b185dc9147f045218bda3c1ddfc9cf425e45ad0b0d3b75a302fa2e8c2c54c1d8b9b1c568fa6b59beef9f3b9edff1d0e0ecc52d3396dd627971945cffb74aa99cbf5ee4c55642a25f73a35b3397d1ced38c7f54da9b641e0ab3aef8a55c844dcf4c19dc0c90d31703de02e5f4e8030f15162d4951061831223945c519274b48485244b6e0cb1a4c792224b8a96883c3e34478172d965ccbf6b93f4b42cbbdc19edd5fedeeedbe3f1a1b99ed6c76b9f5c002bb7c8b8d65d534a299d92e2d20973cbc5fbbdac6f2ffbdecbfd7a53a6b8dee454bb52ceefe2ae792b3ef538b4eb4c67326b2570857efbf656501cda4be00aef55aeb74ad31c0fff092bacfafdaacca56c76fbe94e5fd87a7768f9495e4a7abc94f4f81d4b8ae8fe9fd2253c6064ecfdb3654025cd87e916e14a4a5cb494971315983d61ba7c3139316922a60725838b8b8bcbcbcb926eff30532a42c33d999dc882137b7aa212359b868686a6a64664c203940fdfd3eb5e987c414cf966be6f734183e089e9f215440c45103700c1c44208590e00d0ec48b908e1cc3c0940678400f6f45e4d703000104acf01d11d8c2545e840b1040b8d0f7c586203193abc1f1b1ca8c7031f2ddc14997142c90d1b29356aa8f8d03089818e991793263a2bf6dc90116508adb5760857bb7c893cfc60e35404e649f842c40d1716d8e044255a30894cf4947c62d87c80613379414fc1a1051183210be2062e646b9bb0d0adb5d65a6b6d6d92a3a7c98e1dd65a6b9b08e5a84d9ecc409b18f1f34d70e0c2741283f5721276dbe5cb498d8ac2cad04b090f253e5dbe94f0ec2c576eac6b548252c8a1c98c1c84e42048842071a12621725138dde28143c20d7148b4f02459a920230991ace69cff487a20691d6121498231ce19c91112279aa6698d040b12a40d8996ec08fd5c76b9cd2f7f8ab3d6ee40075b7a0a94cf3eb707c45410fadc543458a678ded580c3fdf9e4265057a778733c5abeee9ae026dfdd969955a7054602ee1aa9022640247a14fbb9031a20b904f0d34f076d6c50b03a680019badbc109be6464cef97fe4071bd51ab44a1e1971c0d97a6f09268871430538ac6000a48565d2ad901c38643d8204571f86239c11229cedf275e475c488c68f7ae40437e45c5bb90d1a3184b01023079a86e536880dfaa5712f10478274adcbd7912023b3da3a6582e09e705c91dc22c0d3789602d72fc75cd72b6f917927ace0c5985b5a58aeefbdd87a95724b67e81a07c84eb94a76fabda8212173cc2790bf14c5a1385225a7aa6a5d63f9a7d25da5fbcf295afe9c93561b380650141e4984a21c4531d2344deb284851b4644ae86e89102ead43142c322629953d3a58bb2d5a1a71fda524f5dde54b4a953e3937a594a2c5c886a45c6e31724a41921224e5470a0c4631782f231a7af7a916747744081dc42de5cc9c516c2431e7fc17224988a7465c7bafcdc00d49d02043060e347a58227ca80c352e77efbd97c64b8817a4d430c2763e28a594d2f9fcff7f1a517f059d8f6dd57fc5e2f109572c1e39246489ac685d76c80a59d76b1dfef800b5c21f9fb088e88a779cd6da6baf27e9a612054874c9696cee90a34b4ec3798320bae42f3ed15c42b2b854c708bae4b3459398d2251f41b2d0fcebc576b4f64a47fa948dd96d27fcf1b940b715fef884b2ea3bb68280845cad20a016de7befbdf7deb07dc54c945dd82dba449f7a7153611ebcbae3cd7293eea87fb78280845cad20a0d6d65a5badf9ec1ac364cfb9a501695710d0a7b2bdf7de7befade5e4b34f0ce372abb5eaaa350cdb18a93fdf70ac848f3e8567105016caae5610506b6b9e95ac7aeb3c6e3cfd796965ca5972521e55a89e712db3ecb38c6659cd8a32a24f7db67747939878cbd08b4f19a83f8f697dea51a82850fbd17ce4a79db490e23c9f5a4c8b69312da6c5b49816d3422da4de8ccdc48ce3ba8e732dd442aed3c219cb34ead13c5177c7711ce76733311333311333311361631ef338bac6a1110683c160d39bd4ab99331de716cf631ea90acc715d97c7549fd766ab5b9c66bc33aacd166ea56cc6a942f58c31c618d38a732bb7b4dccab955732bb7b48bb9dce1a95d7b478a43b9fc811cfe66e66026e63967ae180683c16023f54ef424b0373d295518d339a776614c2b866118868d1aae1cd46eaef56e6ca956ebe560d6365f5bb586afbd614cc76ced922888eda085f30699f844a38edca249fcccd69aecda9337deec7953a13aada58dee79b50ddbb0d2866d1bb637eca31bd69f835aa6b44437baf3474d5a5831c6bcb46dfc510e66627f4eefe59f766d9abf2d8fd8de4deda75d43687a3fcdb3befa03e9c6b0fed9b76558c0817aaf24a5f9c1ee7eda95c22d4a718be216c52d8a5b1ff830dc1aa98a6f058dae716814c7a27166c92b2e14c5fefc8654c51467d10d27ec86b3ca0d6337bc62639307c55cb1a1d8e457f2c8c92c9d6bb2562bb2c81575ab2a7da250a8be0ac1aa045c4845a84a0d29b8610508478383714548afa2258aae9c98365f4369ea0cf85cbf2761ad39be188234562302aa5717585d3fbf047ee0e6da4a4b20083cd05a966b090c01b7255005af9341c59c606e0914c1c73db839615343834be00a1f7c1ac5e4b316d915a7924a2a471a1b67f5643a7d2ea595577e3ba84a91e893d3984cf1de896ed3dfabe978a67b5da7b194ebff3ec3c183b12677bbf16939df9eff1681be2d8c7ed32d87e19229b4150cd54dabef8238f004d36092c5c629d79b6777509b07d3330ed28c342d9922a6d577e0409ab1d53d68d18c9d72974c314f2b1a54df9eecdaa4d1d08ce004d356600582f2600c4fd06913a35f43290dad33d4767d660b57a94c971f962ec902e58269f95a72fd75ceb9018981a39c150c70c00814303ab2525d276ee73ae7fca752735630c0012310191dc54edcceb5d60d546b31a0812229aec8313101390636508206aae47dea409b0118801b751a9eb89db329dd29052fda74270dd659196d3a3d848d117d725e828df44c25e05e454431ad71f4de26977b73513f14a922a28e0287a78a9228e1756411d2f8a24377409feba9025c05e6131e3a669efca071ea5a4f883c29d2f144054f8e782283274c40e9812203941ca0f88042048a111424a0d8e0c5c24b8617502970264929a59452cea74f6db5376f9c33ade9bdb79d52692b99765c5e5c5e5ee8cf17d4a68291e93655cc9e41ed480f6653cdcec16c3ba71d53b544c5a050f4272a77945b3b6909e440a7dfc3bcc96bb5545b0fa49f562bb5946e3b32fd61d845d6328c31d630ae18638cb38ceb5bd004ea8a40a5a10e15d4d85fa69e4b6e5946bae4758845e63663c2c2d08afea663af34165115a52c14e7b989551a4b2385190113fd732f8d14e7615ae3d89fa3881e25bece45617914d2a3ac3ceaa8ff519f1c4419c168195fc726bd4ecdf23af4dacb3dca9229a666f438ff69459f2afad41b7dea2d4ca6f8d2685abd0e6879697c1d9b94a5a445a6df9a51a3a597094ab4443a2853d0435087757a375d0ba98a9228719ee7cc5d9e395812efe56049bc2f4b04d1bf24be8c306249eccf513ea8f075288c0823c28830228c0823f6e73030443045af732965a1c41e5d3905c1e472525a2aba98721eb849e9ef94a298605da6d6e3d0966b53957b02b3b4e24dcff1cc65e02fbbd84df5d29de04ee1c07a82713122373f93592b3daf51bde8b588d6a6ff40ebea74dbb97da883b473c25dfeb2d3b4c0f16dd2ff8d52bad55aeb66adb576bbf7de7bb74e0aa7719c85b12cd2707c11b746d86c49582785bbfcf5c633aeda36cf7dd788d0331136cfb86abba44c91d56cbef19a8cbfe8dbd781fdeddc681f98c5faf3ecdbb9e9f9fb40cb65afcf55e43db3269a1e71d3d46a551775535917370cbba3eea83b0241938b1dbb9229527059bf9d9bfe1ffe31e7dbcff6481d0e5a1b7d72dbfae69c1304b0beb98be2bcf8a9b74e64ca164999ba77034db0ae5e6b0784ab9a1713eab9adf83a9c5b273d32957a3e81981e89f3dc5484c4b9dce57297cdc5083c32322e2326a438763ead528633e065b8d4bc1aca3bfea245e6d5d0af460219d7380919d738a51f091aef3e1566b8c655e878c65fb4b045fdf98c17d3797373d145e1eced94cb7c27cc2d31acc963263fc178939bb8a98a6451f973538caad078894ba9f3814c3d7fe152ea700e4cc1fad3cfc6fcf4e731270fe603637a6877f9405311fd39e781a62bfdb909d460a631a605025837c1baac8bda96c4c1983fff00731bd431d71f983107533694389867f607e3ee051ce5f8939fa2944b19e3da56f817b8ea579d53565a6b9e432f21e6935bd6d65f93a9e793bbf4b7415d72db92d686b4a1ed910898fc432105ced6e8929b42aaa2f4d958b8b9e656d3b490e656ce5cb6d01acfb25e3fcacac29f3e0900d29e5ea34f6e73b8c4269f934fc9a7fca8fe7dd3adc4799eb316d65a739444538814d3929484050b1212680a4d3ea61ffc3d6cd29770857f716b66a2250ad0d4624916b8b390693f3604b35e250bca2ba78995a2d49f52914c5179c703f7a0f6d36bd8aba97584fe9ca773d25a6b2af59b6a4a15ddd19d7ef65bfd9c1c5aaa5c65bfef4cc0817bf6175d6e2ebe6f2acbed2682055ff4ffea57650aa93afdab5eec96b3dd542e22cc6f7e53a6b89b8aab51c9fe5fce5ec3176f2a53de48c8be151467f2fcadc8b62abf6c760b645fce94f34bbfa92a8f40ed804acf2dfdf997658a2953944caa2fdb54bb3fbe5a965ac9f2b7dfb7a56b1368593327b94a76a96d097cc137114e9813902d78646c7ccfad46021bdf9c846df2fdd3252741875d72d5ed95abf476c2dca292f329a5b9d65abbf5acb5b667aeefb6debdf75e8c31c618e39eababba62b171fc3aa26c7b404cc75c6bb904beb817f3fc5d6e4d19dd44c83ebb92a60eecdc74cc776ebafd6eeea4d7dbb9bfa5d7cfb26e5fa1bec8afb5fc07a4ff7ff56efa0854855d499ce7394bbb82f149c3e2384041ca414bbfec41e692295e5bf57fba5ad198150ecd3dd5029b54a8a4ee1606295433030000053317400020140c0a44b2280a922c0c6bfa14000b5a74446056369b09a4b16028c851104651186388310401600830c628a5941a1412c55f2a2938bd2c0095e157cd9d99f5f8cd26d01042117c474ec01be3cf525c0e59a0e698331a918f0fd059d597cf9544210906f2283db0afba2d1b94b2eb21b9649e9167b0edbc79692948479eafc81dde91970fc74b2b3ce0942d4349450f1b85345e6dc733ac8b5b6599f5c41d2462cd57c3599165e603f28aa74dcb55aee180e5036dc1c96114fbbcc9b84f546e5394986ec19ed51ca0cbd86b0e358b886dbb27eb30e6acce3ad4d4ae51a0f9fd0724fcca7801ae26c72f9cefa8920f2e4cd2ee73963dabcd75cf56cea1ab5f8198e66dc68971502ddad42bb46076a19fc942bb1a1f12c4a7122bc266f9d3430b635a69d1715f73f76632baa08e4e0d4d93dbb87d9f0993209694d6be560336f49297d73d93e6d5b49cd73056e96426b92dacc913e02dc841ec465894f7c22ca58ecf5797bf67503793e140a9edb126c79eff7e6f92ee0e7173b4e044ae308c3aad8e5934e3ac695734dadd1ce58b449619b9f8ae130027a49d98e03434142a28439797425bb5c4d6c4e36abd8ce32c40958b4cc67bcb2452b967de46242c173ae5ef9eb43c398662c18c0c9ea1525fec9674c18e0e83ef934584cd405ca3c4fca53821a112ceeebf05a03c0c94d6909fd7a12bd31b68e23203010ff66092a00a512aeb50e786607d024fc2c2b47dafc75782b41d7ada9caff8c95e7321c5ce5bebb496ded157049645bf084e507e38da46854f46330e99a48a01d2694ff4d88803f79f2ed3b6b10d4e3321dbf0aa34d94677335ab61df358675da1db0580378f98579bceb9ec88af5136147e3e1b955360af6c16ebe50ff0004bb09b12ac3a1eeb3e0a944973d46fa83c4740eb1bd00d8a96182ee2a641c1b81096db6b64b8092790dfd27b8ed33254160b516ac5796c102dcc574d84fcc969d8777ca658fa80178e2a7a2b750c51ee32c8cd6887f9a853aea317dbb913b703973c7d2986a89255c863caee202252131cda3a051b6d56e3e87c44ecf9101f6aebb3df22cd0ba6a5703ece480b1a96994bf7a569cda316bafb4ebcb686ebc0de3a70460240eceeb2923559cd16c9149ad67ae71947da030ba569f3a753dad02bac6cfc71953c1e217a0ba484c1ac27324150944979717bce024283fa6869f2ae9aca3810080e6d5ebb92577e0e30f280a1504ee2cf088e1fb155dbcafe9f11dc886a65076def03e9652d96485abee2192e3422b613ee9a08b406044609e4b03b28f430a9c7911f4c5f3e562b32bba177c5f57ceefd29f81d7803b61ea45392ad449007e601434a8b9492c312a0ef1e86403826d2bf314e84722638441ee13c02419191aa680f59ddcd59283d114e7be3e13412f4d46d83f0ab7808e5f4233c435f29f079f6a18e789badbd78491d9daa467a6d02548072e84652b47a0c371c405fbe0968391f3cf6cdf8eb95bc9a1d88ec854a85310a9a3d7b3f6828aa1466b615232e17f0ed046a172e7a23c1343241b1cc8714757db32f40d7639743cf90645571e02ddc511f8e99e0de5d783f68c50d310be65f1b80a10390f497609b0001813f0745903cc495d986add70e2d14ec93de344e2c94e25716435cf2587635b10d79d6bb7a58ebda630950136b08f9832b8feb5dc4231408e0c45a15ba24cb10d7e3d0be23eb0bdc89352ef25b526da6f4aeb61b64e23b41765e7ffe9238e9a63f2548027fb637367c9b451ccfa6277548fb6a5ce4ea6d4ced3db9aa533a003b08ac94cf2824d537289abb0a61154d9e2c6cf59f95fc8326b7bce034185529571cbfd4066a0c680f064c190563eb40f4260596f950cd56eb4ea1c1080ee90c16aff003cb38ae2877284024e038cfa40d3212500315d64aeadf0e33691f33148125686e7a2ec2998a14f197b5b46b0f84ba11040ce5346882eced42fb855aa8ebc520d108ea494e81a5623773413aa62e5ebe8c1309e1572d74cb4a690100af8f7d10f8649860ed28b32abf5fc50d2f2c3fe34de88fa3326e7ad6a6df9a021053f2ac93d3a65278f15b33c4818596a44581c005705ab713252a6c37de8959f958e2a56709dab8b6c3a99fcb97202ae397206673b794099ed028beeda64991f564a20b722ae95f4c84a59b168c8fbe03a4e019541855db14745e52e80a8ff199ad1f41d1a0fc36b0aea5868a7c32fa092411cee327b4869b1c97cd7615d8a194ba2ec533ebc81c3febf7a6a35b0d37fd6697a8a561d5534f8750fbde487f048f0b9bc0410981976df50f190d3a46ab3e7b56edca0b6b7c7b0cf349ac1758e23baa9eff2b0622db63db91b9fabb974853d417ae2b09d1c3e10f06c6e7840649287c708bb23048c3d6f80a6621b1f4e62d1ab4bb0644e3916210ebbd87ccdeca74800dcb798fa584687f178387e0ffc14041cd15b7e7dd6dbe7558aedd104ae0d7d0451cc83a76cd71ad50bbab2395ef3fde28247c3498208a6e0254936acb3a9491ee02ffe9438c8f89f8c990ca0dcc97ea61c4e5988de02ca8bf1c9a0e59d7859566470ba4d13bbc28a246f20a4a654aa63e745a99dba1713fcdeb624ba63c7ea81129f5ffa0cba2658d7017ae0c02197ba5f8d8d06815be568eb084aa004bcbe65aa02bc494376212bf7486558cfbda914a5121ebae06f1e37ab736708f1e96146a4b58e9a0a487b5d30cecd8ce013302abe7a0827a3d4bc4de17849df301993c530ce3e4667ea18d45e99f3a31fe084c7e0368dcf427faca08013329729c92114d2c3da66904f5316a44e28c24c9045dab54dae6c51d12ad721dd4c2303533539d6d28059edb63d62784dde428ca816ef8aa5a8662e4d05477caff7b5a0ef88db919d41bb6096b9413a4a20c26a4a251ea8cd10b812b0d8e1a3c40c150eb7d13ad69d2a3683842268a40609039e110502df2c38cedd3ed41a61d2a6e6a42a4273c005289ea25086bb56a68c19374670d8e0606c4aff22575bd7b0c3a71406c1cc904c83abef2d611f02fc95f9ec88827310281fec1d72012a73504a5331e826079477e58e0cecedfdb7cbf7a0902dcde2e094e3c4f7ff3698d5fa90ce4f081b48775d7c7b2afc4bc7fbf85014ed5f5d2fff6cbfa20b2b264a88acc1c9a78f84051641d3702032057fdaa663e3a8ad926e197093b7c610ad990e01bc7acfedf432b93dd6ab15cb954d63b256782c473b8ff515af5996cc0ae1967f7ac053089f45e775324391b898ae7fabfecf47725d84ba5bc386721ed66a8ad022b04c85696c10d1aaa1742535db4bf817ccb00d9f1189af53a383621559c5c30ce828660d7052e2fc57ac7c0d07d7e0991c621ab8568a43e57f0c9433e04a662aa5db4b8105ededc8b201e1dff31dfa389a33489875ef5140f9882f22a18de94de596454fda7542f05ff2d3ab6bbcc81b101563476b173b83d40dabce10a7f70ff729d436483e09c25e7a8671a2db7d4172ce4edb8121810e18985bc57a2ee19580be004fd84e923a29beb002ac6775c9b2f67e584f83b783090abf4b43996cd1ac590eaabda3d8c0bdd542faef1c6c4de5548199c0af8335c757dcac9eb757c7d00e2c6391926d8a232c73e2090a1108ae89e7e38d30666a0ff16947d72d8dd909979ffe257d69c3eaba551f4f3a85890fbcc296e3113174f091f152693f2791309b4c1ca3d44fb783a8e795791b5b9d3d1f26fbc223ae879d9fa0cb60134588776374248f2d555dc1102271e044bef1aafc3eeaae0d6427ce0214bc1671e741f825f6a58d50d15ac240c451e406760b16add1d406d3fa0f26a5493e2111e568517997b0ad10117af1357fc39baec8b956afe35de750db75811f70058b00d2c427adbdab6149e0e33d85ab1b5ea6e7db01c1faaf081688d2c04381d5a02c0643c09a63f6195f5c2adc57114df12b06802bfa4f9649ac9d04d76a47a0835ce5f03ad8a946a270d14641465a3710eed88f0b87903a4a43fc6899e324927c6ebae1ecbb3a2511e81010cf8c77dcfbedf81bfb1c9ae44a2fd618722694dd62d4db4612c37d06f8dd88cde9a6834de94e57345a643097639d1dd0bad1b5e8ae3eef79f1c9ae3fe3921e8cfcbf7446d10bac50d381e3fdf7351d93ff1e938828eada66d69758c44100d9036089f1e9c15293453d0bf96d7d8246699e942d1de069ce4e10caf9a9dfdad111206a39409b8b05c7a4c6017d6a58a65e76607daa7315656e59d15e7248c4ec1c9a214c2755c697907ea91448d88bab001388bfeaeabae3ebe559f63a44b166cbdb83d6c05deeed13ab46f290a0be4c644a5d19a5d990a980e5d598a085fc30e674c8770536d4276c1d7dc0d850b14637ef73e09306d43a125a56dd0cd212acf2c6f0ea7aefdedd1b2f3653e3b2d59a36e31b2f82e05b7a06ea766407717f6b877fd88aa5de103d14d36684f72327d3049e1b11a92aa65bb8b77c07ccb4ebdefd3928c5401ea5d252d88fcc566aa5545744981f9863da6671a815172bccab8281c5336b3a77a5d8fe958d4cb5f77ebb8da104e0c013bb49b13ecc876ff2cdae4beaba80090850668a449d334db3ae484a3a700c84b38b4e3bc2929d95e1e2f792e20d946cccb7eec278f6cf69a7f340f2635b80a6545a6ead70467b0651d915e517b5f3463617204550d8637d9b109f01331b50e30185efe75c7b7942b8c605e11583ab59a1b4d85b761433c09f2bf99e254144556f2250cba6628df586941e2a7ba225e45c42c8bf8e5570d49ed9f605331a3fc9658be7cc62e3f8072c29318ad916bd5c49f0e25b3fb25fe6acf7d8ca264ce4402ae05df9bf5060a876f429f5ecd408e3ece7e6bc5aa536b876a49d4a8e3bd08c89f108ed8a27530299f16ad38713d5993d238fd4a319b696e93cbcdc5ccd4670da8d12ae8d6e035bfe6e094c58486b997adfb0b3d82c24a2cc3b6d783f8f65f1e0fa8ba1950bb9f8deb27263701191d157ba7ceee78fca3ac1e4905c0a893c56c7991c055689f1c28d0df6ab9d6aa77b114f93161ef52624f4099035b8cb08a2f939af5f4c6fd90ad1222a330ea4f312fc44595ec9ead1347f9e6327bcd3283f01588a06635279680b67279edf6e597ec43b3265499699b71fad015016d562495fcb8b46b1407ca5c8c5b126552e6e45b40b16db7478ddd2907d4e5be9a9920ba941deb0fc501d552826f43a6e545efec29895d260d6ffc423cb4b08bb4eba758c4308f8c7e36ececb2d7b14ae64ce412722903d4bff0c10fe1f783b7c787edf2b25a8957fec0f2df071ef2d29395918c1fd362bc510d18438c2c8cd9377b8f1710a2e542c386992aecd93aa4ded47db49b70ff9686a164f56f64b5f61b41f6f27f80f3546d0cd47475f79af13c0ed086f86800a94554ef78df34a991a0d6d44a2d86973435ba5d7f8e8d070038afeeee9fb9af2e58ac252ae1cee7a8134d429001a31e5c386d12d0777a50ec440d6a63d7092755529b1c258a15d82ec7c60634af93def0288051febe4732dbcf9f79f10601f405d83afcf714bbdcc9ae907bbf1840997ed9bf9d3153d30257937d713848d10e0f5bfdbd9c3e1edbcb0aca3dd03e6b214c1bb52b372c6da66c872048d4aba03dc97b4322d5b41f47607197f58a8ac3ce1017624356ffe207ae2b72539e4ee6d57db883b2a597165aaa889fb7106da431043d0f7af807a6a81b8e8f2e6b69a9e2d29196f63e4b8af2b116681ad23bc8bf1199a6d108199a6d0726d96a6b21c00df7e4e988e6886ffd8f3429320f41dc52349a15a12eeac8e88ca55597c2e4607b0df35352c21413778767ff79fd9b70b244922d8de425bf1e6637208a8a6a3cfc03c50b7f3bc1e9ce511ab672c8dab98fcb90c3ca58b506e80248d323755eeec399d92187bbfc6840e08b2883ba3ff6c1622cc06ba811f80deb91712c29dcfcce64320b474e80feb9592254180d0a61a3fdd0b809086f4c921649063e62c83254030e0c6d32706bbab866ac953720e186b467b493b6bfeb5446cc27885d90f1a20349453ec9b23561c005f4f7714f340fa941485866ba53c209c6d24ce066abbe5b3c2300c5418bb69ee9b0c3e947712a42e3dead520bb94b414ea6429ad772468837278fe066846cd0a873cedab831c44a905a58cde7170a59a496429bd12ee5493d8f3be3b653c4342df06a58e0e0ec9f2d8240de6e85ca9b8ae799baaf93da9db3a23c0728ba15f51698a6a8e3d4f61ab3ab56f0248fe6eeebf6f3aeafc4e1a466e7f38f7a9b5aaf7cd1fa0e56b81c8004df9c10d709b7092784fc1f24be471cf618f156cfd99466f34d43b163b8c86099576f29c149f039e1983ba906eb4137980f6da615e570f8aad3b53109ba088c0aa86d80a93726c3b97cd2c5dd3bf8c89e70bc8e68f3a5fb7cbac986e3d8ab8500c89f5acba0b31efb135f51a53c4f78d8b8fb6b520d73bdacb07105eac9b0116a8413e495237fba71b22522fc85d78d8dd10fdc4eb34def5dd8fbce97ee57e739ea98b09b852cc832511eaace00719c25dc2d2f6e384e3e186189917141df6057994dfdddf7844055084a7a920033b7b6be00e239027d6e1df3808c38ef587609a718967131e5d9715493053989a1ab5c3642604a62668e2f0431a78c26c0694faddd9a51132479cd2ccc7778c238151f9eba06ec43890e26f8ce798b0db40582fbb84a1734265789b0b10d946e85270dac769ead36e0d454fbdc6b2e427f4d627320cf8a6696d1f943e8faab0b35ce996316b85ad86b0ef7c57a3019a079978525455f051a5d0c2800d8095de6e746b31ab7f6d6ca4b95b43da61b8eb05f936a3241d2a87244c4531ca062196bd03b4da10521fbb3908e0b4201ce07bc664597540de1e700c89698c9791c41ce99cea053c1139ef8154ce4a392810c32dfbd53d7659bc031e644e445d28a5baee3be0cf3446ceabae5a41896087a9aab81721b73c0363b42ba1c72bc56d74f304f93b95543e6f2974e25785737f9a640a945c3d3a5534bf9feb11b3cdd98fa83a76a3987e8a9d0b9bfffa4bf056cef686db3af7bd4af7c187b1e44f1645b09be899021ab6000f98595521ab0798232c020ddfa9e6e32e4d4070766e1c0a08554f7a0ac7c42d93f62cbf18325f1387773bafd1c08d513c02a24d5017470fc08e178da60392ba68ce8266019ed74fbc464ec9c6e69331c4cf1cdd99be079804bd3f39af7fd2841874b655c1be20df1cfd4d533dac26f16dd0bbcf5a36cbc6d7ca05924add2a280f6489ee92dc9158d49e895f4026dc6665ec688b78358109c67e40621ca234159200daf76e79a5d3643c69830cddeb3c6fb36e8612be4c3af4643d6bddc4c5533c52afcfda514c7ad562e44312030e06b80655058b1fd5cd240470f7dd0e42617f16fa4b8b694887d89eb9c449220b4bf6e3ef38229f498d77fda51e08233d55f1a6c9f82ea881c93826f8f69e07887c379fc0d1be7e9a173c3add6625169e37c2646b1aa49af1a39f9b27c8eaea69b7ca95d8f346c79be595a71746b1fdf6c4931bd7e901bde243c9bab1e9c3a4852daabe77988b8d28db83e9de5377c3e470b06daafa15c2b001caebcc2bad6ed552cafd84868ff6f910ddb43886a88d05a4578f8764cee21a21e084042cf9c29acb22f90a9394e79be7acae787b0877bafd228ad2cd8eaa54a684c2a4c1bd7da0b07207506e2a604fb1307d4eaded6f815eec1cba8ea84e2cf808ad1a12f9d7fa309715e09f1083d45c7f25897bf75ca4262f4ce47158ef2848cf1d3f541fabdb663ebe513e5d09a9158ae64119b8d334fae14608a3c918546766b7ae833b2a5827cbf4ac4367585cb94f491fbe9521044be011c0130f383b75ca285bcdf271f61ba03e5302d70a421d45871d47b8e530c4d534e8259a10e5742977928403c6b22da0c2874969f0a003cd1ec3cd0138e3b069ca21e194061b018f80583c0f011f1ff42e1df2382b8f3cbebdf748be80f0d752e92a9cd2eeb661da8f66ea3dc0a5b85266e8cf663846955f8ec59335ff6f1bdb92d640050d5088e4e4df3e18dd832ecaaaf7607f6fa77e6137e724df2d2f7df4b1916faa17584ec086c5692fff46ed3da88158edef84cfc8e3351a206479a4830cc07526749522bf307aebadfe0fbebc7981d0cce57b172fde596032af72a1864e02dbe2a8ca4a009f56d9f61de129bdbd3178daaccaee86cfcd2262589667be4c95e552abd5af8505a26b0cea2d6494781269353b592e13184f1f0de59222e4b3bbb17066e14da84002584aa33d57ea3ee86545a06c4727ef1e66b6521187caa5fcdafd524a5ae74f120c9a9dd8c89bf307fa46c2304a2a4c574b807705649cfa6894adb120ba1345a2faa77ea90fec54256d007636890dde4bb5a3c1eb18fa41f01931f4b30da5d52b011938b0832e96c9b7c8212c784cc9450c257f524e452120953758fd076e1d60e58b526e64dbb62b461a73eb6dd722f26747fa7f081b0436c0224d14861660a7633ea0a36a92e3fc1e79b37c5996fb180b6ddedb14a2c6f9b7b0b9935c407a44d5f22eb4b172c7fe5334c5ed67f297191f3f2781cac2f29d7252927b255e822d0aa48d3fb7ff7d13d4ccb822847dda1722a6c930c71a7d82a25c34761cca6fc15c038296db1c19e5293442e5a412617639cc365b547c3a9c63e4365e8238963b4797523a4ae0d158492eaa581750e0019365eca104a9814b9a188d1d77ff302f68520847daaa2d89c3c9fb980ee133e3329c059a5c7491bb563691ea5160637ca43227b2679e926f476a20e31d0924659d23c42456811bff796c48f9c4cfcde098a1fb1a2e9f634f6b5097e461a1c2405d4a0699bdd1192e00d1d24c5e7727dbaed724504124f8749f2867da4ea3c382c5863d8187394717b75aeba92149d48dcc94b46fd2c5579c969810b16b73e75524062b05ab8a9ae09821ba926023a6408a154491f68cc2a4ac39902cad9bf6a9f7dabe0b7cb2101b7d06ed2e94430b67706575469fd851a6ce57726388f53361f2785beffb70c554e0fa1ff42a948c519fda712a595d05320a384594d641264ec24b18fcf4672d38e55450a3f291e4d9575235cdf3720dc36a92c40555294cac2b588f2f39e0cf1d5830816fb394ebc295949ee283722a9e6d9dd3f5d933870d09f4231d3f539fcfd170514fe179f2308805d0ce3714b624f77dc5ec9688d612b035d87b1dfebee01dbd21bebd54c27b61d37a0e8235beffcc7bdad6e007603e4dcda1c2906ee30f80377240e98cabea6244ba20a887c3f374267bb7bcb58d668bedc918186755daf044d71b407fb4a008e76a5889d30b37c9858eb5a50cddfebc3b9704eae05613bc8f3289f352759ea018453d7bbdf2aaad276c9a446dcb66916b31952923ccdcacde350120ffa9dd7236a4439b4ccd4b11a52cb0423f431a3806c3b73d0d218b1554573d962c78292f84f6a29359a2eef909245c686b98ac5d5b4099a8bbab91cc0ef2fda6e3710547c9740f7ec9a42272896be889b66a3a8079e5fe9412bff48c4961636568dd7cf3d5639f4d8aa398a13d184ba56f6038ce59802a7dc808cac81a17d1732e9604cb245d75930ab1e6698548a65ccc66bf4a6be2f7867c0886754a898030de1a084c84951a5a9031f10d75f805e4f1260bc32319e9a56d0d404493968697c30f885e60f1714cd541f8c1ae0e4543c8062f90244b63c0d9f8cb1f858386e8ef65ffc16e28db6d6a8ef193633c50c79a42301c0346291ac6a0ffc752de26b71d013746c26e980931905d0341a4474aa11ba756326cb21a511da596efed9009a2cabe40628a95d16637a880cd34e39838190e396d3499c90fafae77d3cea0f0d69ccb072ee16ce172b2f8794ac09a04f2916ed4fa9312bcc0cd12627267a833b3610322b87ae32772bedb5948aad5045b5c7cd5ca24ab0520fcd2e4c12bd930bbde93795472d90ea1db9abcb37687efa878389b423d80783aa6d698747b3bd2031315db2b9e90552daab96e980f1a6d38a12dd6800268d4e18cb09509bc2ba63e154c080498255a01b13eff9a73d7a10dc27da21f9120f0b2603b0df1ed32d863af946f0e8028886e5410e50801321fca675fe0adbd3a260bf6baa588280291a75a1b100cda5d4f693f7b55843563c2293ece207200aca42b3fb5689b60334cc5c666fb8ab28e0cbd21dd41da4b9a3b2cf3ec9456b1fe653542590f20eee85bf72a3c5f2670b5075ee9fc628abff8ea1c969dba45964a06459b888a80d7bdc339d64dce1f7b3b32f98719f9155a9207e9664c4d8d0bf883bf6979cb374fe3d724bd6897803cd6d715749e900e2b4db15bf7bc3110fb1b16cc0b69795d248cdf3f325af315193cecaf5e7751d6a35c7e1f6e5aeff5e4f77be695497e9dea9ce1971d5b0a82c406b2231037f89f943c716da33ac2a57c09c3b520ff685a29de105c8cadab1a1dbef597483c86896fe236278207392ceecf723c0095e992910a2e83b9795411dc70c31bbb7f7c62e989fee431faa98fc3c78e1898b7550ca9a47812351fb3ca10af49193367094f82ffe01703428ba49296c2d2e83489b347631af4b7fabbbe5920fc5f0234976c27876f29615b8354892fe1114e995b3719699423ed1bcf7002e9abcd6ce59643c3b81c297bd511ddd4ce967785f9878e80c83d96a0b46c223275bf26c79e6ae62b62d855081078806c535c588ee5f9f8634ab2f5e748b64bfe4d720ec27732221ab7f17128947f1c0336df5bc777618b3b3ae63d467e2d9ac9d3d031f4a1bb556cddb8690dfa7898828f96a26bdb5f3d9ceffc3a132670348c00f571234b8b0739c536a0b7096f3afac51020d761bc12f1bc466825d6576342d52402ef875171a9f4e131a5f9eff28bf61d0a47d98db25dd8600f190ff79feee048905867b96c088eacd2c04c7d5a3798ffe0fa5147fd8b5a7a204738b4e37724dcebd2d872859d495e8c60742aa2dd14562f4ec3a0f952755e6ffaaaab49fc5faff97b15ae32b5b5bf87dd6b2fceb57a53eebaacf3f836addafccda6a402c66e11da0a19835976bc2b3ffdc176bec6975949436739425b6ac25ef8be147228af580904df4d8d518164b124671e48658d5a6263eb525d9adf75c6110efb34c46f2057010be22b823f622ecff11fefd867830320a3d27e31cc50f49d0f0a79edfe2ca3811993afa7d51b8477caa130abe0a22e91a385728260eb7b44d57905af7e2016fe10162cbf38f3765325e8f5f9dbcf91172c5009cc224d819f0db51c56ae829bbff6061ee1fc4c15dba5e6e2f891bbb01c5a266805ee804884d2daed44a39e99dddbab4ee2c2312ba7f4edeb6bb6f8e77dcee62f41994809ada27ba92d6fdec5e5f1fb51cdafaad5b6bbf161410c0a1acaf788c30580ff11260c76c5e98c8893170f04412429095b3c94c37e253da0d3601610519040fbb60ad700bbd34d6fbbcad06da37cc62a245f587cf1af193113d602549a6e232c8104218d71047ec9b6924dc5f7d23b03c46342cc5f3ac7e33b18cabe2a0d886cf180b63779c9eae63efb408e8d8071b877436a41e44f5095fe92175b9703ff9e45039a22c606f819f2eea38888b97da007eea9cbdc158595588f61e92d7e61b8722b5dfa6676240b27c8ed221d57c101c913d280925914583e9e0ad4d7881f62ff371c324a4c21a5f5828d4d1311b933a550ece0872bc3ad6e92f15f6b2a363e63628c32e637af1d7e81d5d8f0b86c9a71130c14f999c7c5fe34fcc7eafa3c08dc25ec478eb4872d106dbcd9690191561ad6c4fa6ccf8d518bdbcb0483a189a465562528657cb474fdf4ab952720c7924b49d1ecd7a7d0a123dac9885ce041a4f668bd23e3bc9b8a8e8d0085e48bea9b203826cfb0a81ba94788e21885768cc99ead59f71d5bc01e4821822a51ff75d367161af9abbc12f048353fafb86b6fafc089fc2d15c9c40941841ff4eb583e5768d018ee6fd9b1caa3dd9d06b84140a534bb78276b0dc819c60b16215987fcf6b03c7773033028741ad8f0de81491341b8e4896153ba01b4b711504ab3f5c8c15d2777e40f11f9409ad2ce25995517ec2f2b3c634953670f06a84374f9d5116dfb2b042dfcfb23dc00afa5d7789687bcad36cc16b7cec0d2bff493430ab5fb0393124ef91f863e00764c92ee4d6ee61df091468e72e4d8cbb211711481d5f96e1ff5d781cf235edd799a857824789ae5183a137fc9ab1bfe63d2e611b9f7d4d14e2f813c5fe7fde0a160d5613ff8782cbd864fc19b9045fde258390f652e620f93428b188e1d18aa8220457e965d18ba4689e79c3c45f3cc59bf5c47ef40c0f2b75f84ee1b181a96258697ac68d3d7dd40434f40eb705c49a56b5e9d356dbe3cffaf5c4163b15fd1f58964a5c4fc85a3d8850fa1c7dd430f96d0cc9c7217a72a23188b8fb7b233ed10d76aebdd79cf8805355a487ea19ee95b85079905a8e957f0185b8d5e438ebd07f014fd7626fb98d0789ae03249c167307df05a74f60f75f59db7b1ce45d8b84983a27f90f736ebf703461532acf63a26905123fde26d35cfca327bf0c325c15e6ca1658a8bf817629f89d0b98950011a7a5c2c5fd650ac131be447657d13ef9e23918b1aad3ed150d6bc7b0fe820531f27a14a8d229ebbe0b78c5aa4f936ce6dec4fadbe3df39ccef112fb94d516a67cfc06d72f48292fd68f6266a4a27cffc6558f0eb12bc39b9a54e69b120140c1f7b9e284e3ba7f2ea699c98d1af83cfcd61781d5a3d375190ecefd6552c5f9e7473ef83c06897448e6181d0d44c99c18113f54badba3966672e81b673e2a4bd282041b64ba0eafbc8464aeb981edf150366ab1792e7873632d54151d599d07ed0380e7af2865a7d644a1c219709535c68985b656e4358583067074a4002e5f3832fd4915be036a7ac56684c5ba5b0537a2005899faa63bc35376e90a5491d08fb9ca91ec7df981169b4a5ccf668a91d18dce8df3b48ad201d22f6ecc90cba5354fb45a10582589dadfe258d036acf98a5821c3735c85d9b8fea8c02d60366c8b7fe72442a603f6d2d92875bb1519eab859cac6e1670eb6da01e256e7a2e39ffe7043d117a3fcd7b1f60357e720923f467f0cfc1af59dba22d96e5f6703d33e57b299748709f3bf3f049068e4eeca820c4247a14f292feb922c8c99a240c9678b2a93800a01657cf4397453fe3a1a40e08194bd0e63160c3a66649c186f8071a7108da0e22506b93ce0f352141ecec0cfa47b439218e33c487cfb453289d8360f89f90b3007f7594147038737bf857a7df5fbefd763f80a6a45f59281af79377709f71522b29a9d0a70c857d6b0d373f47b4aea82afc153a290d9eabd04dd9adb43eff873ba9e41af8b8b950906f99f27c582ef4821b40c29db79856ac770ba9dca07c4e51ce95e4463964f8dc241475ccd1e2252e385b4a401cf59be8369c84d270214060c93583a707c17f9121dc3cb9b7e7e0191f3a125752d947fd84f6db22b9fd32c5c219dd5f84d19689b83e6c56f6fabfc98aaf12623b2368c9240fffa3931ad7ecfd8ac04e2d66d9c847dc86846df23bef33bb7654ef1f35ec72be83b81938d4043e30fb0b188b8a3845d52cb78bd5c0586fa34091d08826ea79c32e784be48eb8b9432a8cadc3662a50dfb6cd2664323c66d16e56c8485228d93e4e8506d34d00a4031619c0333b08cad4126b235401a7bc8b8b8b2294fe14c5e65966b1840a2b12b2ed8f022580d83fc81440a09039ed381f88ab5d235ca013abd3d8c08982e1117eae08363ce116e5be04a79e470799b20c0064d22640433807948ec3499ed34a5bd5b23f3d632b6d07215a5bf2f9a9867f1952ebc2748fca7b15b2793f10cf3e0fccc418486809daf2bbea6840813d2e0d32401eb7a6fe6b3f9604d4b348f93f9d9714e208d1d065e9d878a1308092bb16b939076161990dfd3dbdfe78ca260df94874daa085900b206fb3364cd000068641fdb4ed39906bad80cf4ae783a4ffd36d6dd7904bd64a6ff0e456dae9f8c2cf4a69e292857997b61305ccaa39b5357526c994532b010bcc3336c0e31dcd1b712da1f7f83e60060a767e5f6e281dfa5116961cba54bec65a00dbd1636aea0b1fdbf03a0d218ddfabc85354972263413c881ac10902c04b2f3c371930d2d90e82b83d052beafc52fef2d6f9e8bd88bc756964d20bfce9950887686a3bbb7e94f7fc3f05edb5be9568d31ef7a4f31a2186b6b0146f1edfdade402ca32ebe822d9e6f4f3eb5980267fa57068973f02b0d4e452487164987f3d9e578d135b5523431aad5d1c36fcd279d6e1f4fbbcaa95272e313382d011e23d83a4a0c2106da17d1af0d28f16394a843820dda2fcf115455c74a69fbf19e6c41d3099f97fe6326aea6106160ba16633554b9d870934e6e162717ea4d2e1f27d7a4d9c9a5473a42cb9d3f054b6662372414ce1738d69ae7c02b2679cc42a4f6a2acb0e82b4655d0686b3b051194f3b9a8728b5558f779e92b2703308179359f4951145a533997f4148b74144af2e504c45a6a6bb2dc8916230e5a7fa1843a8833cb1a21a6aa444468132c86426db06d84bcb38912f797583e38b23699293b0c722e33fd02cde55e724868bf0d1e781601be5ef64c6e48718e447cb7e2f13fe015643466a3c5c46079910128b08631620e6ddb988032ea1bac34e20033a7df68a20d80423338fe625f807cb4b434f901e2a26bd051a2afcab188641a9e5b0d6617a8c2d1c49554d01506983c3e033ac1b8385bae84d5b80faa71926b1180e57e94ba7df8ade2c2f13d5b8d2b8fa8f2cb4144c59d82b7f14ebfea0a4d1a926eff03c27e204b951193b2375e132a2294948aef79232f86625b5b41c7d74f9f0baf04022158f33d12587697cdaa261832de2e85f84130d33d0b040d909c25c5b7e6abc5a112a4e14b13642a37b129aee3541941c295b0ac91fc04f116e95edea9fb6c0f00300089956fc5a3a08f745d3357b520f40bfc0d9d1b03e2142fa49c60cc28c9affb200d808269ee085aee4608217c812833c06b52c06fb87729f4806596a0480a9fbebe8ca3370d80029246d292f629691ae8173ee3675807d63b76d0b11cb7c4a43e81fcfff6648dc664650e55d8289a72672aa4dccc3983c2eec68815ac8012076f4031bede0336041c307247d0ecc658116900e2dc56dd67f41ddd61fd9d7a6b5c6568a43d52c1b78ada5ead6a8979197fe649f6cdce16c8b3655d8ed376ed236b04085dca9f39740496074e22798941921308fe8962e18f190fec7e655ab869d2cffc63cc79c90d2a42131bd6e617a55e3bca9403e9c6744439e8fedc4e59242afdf99ef018b1ab004de178e008e1c02e9e6b030eafb2e0d4484e6b93df9253d44ad2f424b6f881778db99f03a59cdfb614d6c3b2adf4c1de0a37ae788829ea92ea19e3d9120ca2fabd528502676bc1fc2e2eb3ea34595d32eb1fd41d514e0b3f915e4fad1e41e00442daab145ddc5edc64be77755c8fb9adf248ecc92f54e5f25f999c231571b9294e586dc82bafae68ce7d70ea83199d8d64c4085d1bfe2e6dee3f9d498da0f0b15076ea39eaf21d9db82f47ce0ac4bdad90b591b34223138731c3597c280d53157a0ed580cd9791703ea75141471128bba941c3e2f148e11c31576d5a77aa60f6855d88454b3d0653dd89f47ea47ec27d41f5f48b21d06f4d70ef9bccd207ac45e82a11a90527b6b9ea1eb3b2f599b4a1e3b3e63cf12014fd7759fbc7b64f5d0b84e06c52d49452733768fb8cefe15fe0a0329d067fb0089a0063a3477c2c98923b64db960dab6ff6e2952b8d46b9517c9059e859e4ce4d8d9ae91a0a056044251410a65d4bc0ad0db0e7b351ac910806c0c56c00c72b8d0c4a4a0890dd8167c17e1c7f97195a949f836a9f312e031e6f32857c8134ed660b90730a0f41707e1d9e8580c7f61b47b40bb1aed00b259470118c076553b4a7e9382322f0054ed1bc873e29cde352cca6a24894b437566ef2d5016052e2326e1af71feb860398048d4dc4d8851443adc62445a894dc3d32ecc27911f7ea405707f2d801f470e29ed7043a0aa561310cebcd661597a940372d97d99f1631abbc023903d9204ecbdc931094d2d3bbfa8c5228b724ee82af4c3e4c1060103edb85d2410a9603edda8562ca9e49e5b6ab8ae7814fc58ea91e58b83ea36d04b62cfec44364ea1e82b61198ba74dd74e6687234d3b3caabe3b311999113af1269c44cea98e89282d4307ab08d54fdad99ce03794245d77ba301c7f64c438c7d0ea0588da20e272cf73c418064e1595bf6d0f7914b691c9aceae8646fe53c2e0b9fd2203215997582f4fee130ca67838096cec2f0f7f836cb26270620844e7d2c0bb248688cfd8f1bcdf10da8a0865f3b4610fbb06e0f4f38ccdf7096e36c6c05d0af1dc0c3ecd0e4259fd5822fe0d54b2364c915258257a2952313b61c14895cf09b746cabe52e47711e64f8ada0f19a2f0ac01ef3f6aa5d680cfc65a52d6803fe9d7427e31272f43c324fcfa97a5a76254b1a3d1772d3eb5385e6da432d24e7cbf85e77a859e6853957e140d14e9550af038e1ddbe9e285a40bc00f104b684560132efba8c62730501cf6e285e89cd0f9c54536bf608bc34d4f706ef9e86788c85549b1ed2d4896e2da595ab383f589bb58b58bca7998ac1dab2b10ef29981770689064ba4940d3186aa7cf40eb0c99b8689311a0336e13f7830044bea6931a21d5593a948a60e0dbded1ea980896e1024a08e632884e5f1295467ad21878e4c1bc8ea413c1d2fbee4c89c3020da02af2808af66c7f071e82e2f16ea3c42c9e11c83d194e53ec95a22ad0f8788916b5abb68e6721b55a5bca29de433445e1e9ff431dca8609efa1b4c63dc15328b3aa4949c9be7152bc1b646f25761847496ac1fe541e0c5327baac1d0f54f11d2494f37ac4160728b9895ab519aa5d5b0ddf965ac9546547408c37989b7f802dfd472a75a294c1b21d84c3930e309061626bbcf854d8cbdf8621fe7f221b1a64408a8d1a21b0480834b4d05c98181be5e4d236159a63e14fb60b4d116a452091bd378685a86320505363e3ff0e0c00835a8c57ee705e11e61803f1e6ae82908d0bad9b5d5602d49c2eca4a4ef7ba99acf88f349f86fe2f17ed1853db519cd1ae41b1c864e558c172ee8d80645aa47cef64c67bc08b30410950b40123f89a2dcac02fa4736b84fe2a36ee8a312a25040b60ae6ac2f0c70e336b072e3d7345652db55c0665f14c4121f00cda76130d8c61de3187a2a887635f2e533d0a2260808f5876cf1c2dbfdcdd0db076a5ff5190ed8cab31455485436754f1e46e018363a24aaca62effdf386468387a593cea54be56c4ada08f0b53aedba7f5f2162e9a93bc6c406625bed766ad336911e3665ad2f3f37f6973ce2185d17f3cbaa9f6d5197fd3f4f1c9da8594ad6722beca7092b794318b4dfdda4529f9c3c72f62a200b9dc52edac738f907c19c311ba7f6ee54756030af70741ca28255bca98789f1dc9329820908a646c5f4c9aeb8ca66cb0f4355171950af6441b33acf4b737fa0bbb0881d9b5f3938a13a26dd7d0166136d0054a12c62726570d7559395dd3ad00b13574008b97f2ced6821654ef2c04f7aea9f86460e0a4649b41ea5f2fd378e68e29cbcbd57779a0c46f90dbc78e333675bab1176d286336e26fccd34f9a418781b03a67d16b3931b0834acde04338b77e890cb7398d85803b25231591efe7cc9da98429c56ee45b5213fee00645b37facd7ed64e4ddad4b54d209c9ddb6f8dfe42197427df04d4a4905d54117a57ad2184f9ebf0e50bdf1fe9f4769c04d88e9582a77863cf1ea296cc0563798d8ca3204a5769e5decb5c9580fd1b11964e5fa78cccb80ca2947f520a68408464c31783a2e6249e0b4e09d85d713f6556d8932885f3f5ef96eb236a9a47d2e5e5d5b38d290db72b3fa06765d0c26ba0a08c83644b8cb340f430413888d94a87bdd2530ca0c4d10a07ba3ffb77c73f51caa67e1f0cab1ce07cfc985070a609189c0965a5e91af5800a16d723f53120a23c6bdc20dfb30ca608608178b3adfaa50817acfbe857a78d3c1a06541006d1e9745e6c2885bd24df3d2a8f47211c47727373fd3d609a46b22613dfc3e1007565443d56bed12b2b97d43e8d04b655bb18e14954a8025d44de5f01a73ae8b6a8d348901a18034a5e216253d0341280f5c7c71cab8e2d3bda19fc7c3196bf729751defab46a417c4c52ce1f63021f5f66d11f27b23bea2a9658f44e901f57e2ec14bd5d2aa16cfb75f2751d0b8493a81a17835b2170ae7f6395169084c441a6881682139f1924ea7a49315c80c159d2c38c2b3f7899d575a8cc07ef7e40e2a40dc84c43f63bca895c204b9821d96921f0dd5daa7a8edf197f3530e625d22be5af8251e779106d60694d3a70ec9393305bc9038d780f84ff58475d5b47f418303327647f8147157e806ab30f3cb869059b581340ca1bf4b07e9699d7dc50551b726926f016d8021540602b518c71f0254f640d741e3d47c2f7814e529d4f8024d57357b47b829dde7996040b18141ada484a3ff1de730b58323f7583ddff29956469ecb8002a239abf8edf617f524acf5551c4da6bb1e99baae4fbad1778a3f42f9d27e1ac3d96818e92eb54afe5589c50ca5cd0027bb52550b2a6b3622a9a252c30f00890292fede0f415f86ce8fc5cb265ebec99fbf0892007a21ae767233a427537bde892e33bb39479d531fa6c9ebf97ba98759f31cf5277eed09461eb083dff2c815fb4c141b82a7a9fc65119462c33df79e5b38892a6211fe0e3e46cfb9f79e446c0726634c810d08c666c210f6f28fac442cd3f2f7d60634a56319ba5565fdcaf5910c8da814ed046996ccbd414a0fbc86afa20f16b2700a2489c375202c8b367799ece97e0067c40852972aa130612ef35620e95e052c7ce9fcd69914e0ca1fef396e80592e5261e6928e73d71d320c90c98a2e951df3faf85484ce1400affdb1b821de51d7f0045487fccdfbe184faee93f01ab92219b612ad37a7f4f329362c6bc200dcb1d7ef522aa60d97c6b08cbefa405803f3c5d95da6892e9088801217393ca7d164eb1975bd610c122862995c25d7799f95f9d9e15e68dca8560023937b53322046aaebca33a68a11d8a781dfa26575ff00fa292e993c2f02b10eda362936cf892a6c1040402caeb0d39e55d2cf8da177b4cfa361cdd34e6b69036ff0c854777a533fe6699a26cd24ec075bdf8399c78b413a0eca2285ec639d8b962af426d823564fef19d647096ee66f237b334e7b80bc6d586b221dd3bcbb9ea322402a57b9cc6e51970202804846d1e6dab9ec291c9ad0ec93776d7aaf57ff2cb67758c836314b4eb6f6359294118acde546d112a5a21137843058771eda4d5b15d80f245622a87a18834037687eb1c565f94484fdea950eb5aacff3d92ebba427dec743c7b8ba8fdd695806eb605bda64b1d6b6d3e1922c9c254b427e74706a0fc323c1006c924de245e6992e2fd583bb4fed0d7b749a64c9eae6a31a09f172d08774905e0a278f109aeee0660a9be1be72ff5fd008f78ad8570473e848a656e1835b73bf87c8ef6e80950a8df3fc8a19d0bf51aeac655f3ff2f3a8087820c31fdfa4fd39ee0ff77c82d1b76cb85cfd01fcb6720724884199782ea3422b170de24318c5c41964613a03b4ece230492b86915b35ef31326b8f422814fe628f053688192770c08b3b10170302ed5f8aeb8b7903655420b42635e114e0f146a5d1dfa8719ec41ccc01e824cd88393851ddcbb48481b28c53b03c82d88e365dc839d2af422c4265837c4fcdb3ee48b30df29455af82bef64fc0dd0135c3c6803a93bbdbd251faec6dfe45afbff9c67470483c307139803230081072fa6202392883dad50768cb05b77adfecc499ef6c51f83bfa691c9dd184b20b4eefab5dc45be5dec35af4e1414d2101db8e5e089cd711a917400393fd57f4bc299d6a01d538608050907c2803b1d5785b5290b1079af0546d0c53758aa66ffd4e17cfa65d00e17b3097c970ec64e9f47a5d423c569f82b30fe3b033903551e60b0ddaa10013dc3b7a8a0cf5a6d3729125209d35685c396c6ff57c5f5e04d10edc90c598526226872d850f371fa75b9109f0675f468a72a3643820dd60ed255445c6ebe1a91fe3a6e4d75b25de9f54b549ff41d95c538bc44c5795270e0ea8e8927e0323b21d25e36981296789ac8164d5e852a1a50c7194904dde49580a54f895d64decbccf8c8c8407db7586c3225ab3343ade553b261bf5184121b5521007f279ff5879978ba519bb70a493eb5b66409311108a230a3176533c37d4a8aca86db855b88f7f9dacd0a8bde5e9ec79bc347a52e284975d3773a3634d55242b27dec7972440eb73c9aa0c885f20938124c789a18845d7e21fcd4b985f493ed4488234245f56a9069c79f7f21d5825fa55045479d8d3be5c996a8166671e6e2284ac4e63272211e9998ba44db91dce5060b1d18e8fa5a6c3e3ab6e8b96fd975d318936a683fbd8ac6409c4ce693752ae453c8eb883afa91406ab8c76f74cc95cee2c447033c4e563ea24c93a11f3803b7ee06acfaf46596c68e826772a80a765360ca153c5a1a71603969ad8949fa51a0ef8040beb923a553bb7332bdfe8c644efd9193e1f56724636a9f150af3a60f96c787c656ac9bfc98928d157cacadc68aaa7fdb2b6b96fae745de62a16766aabd8f2eb4e06c088bcedd5dd16878c89a663f0c4c18a80ad32784df596f3e7f4d1f5006d3479c5c40bfa878fed1d5a792c4ead334762873300a27d39b8f48e6349f24c0b8e8935f37f861e08e6854d6ad1a467b3d36cc58cab5a57d56d9d16779569f64051c3aa435cd84f8f2adb5a36a2a492c6e51f126989000b683120919ef807656706227afd020ab9d08b38eb231e018b8128f489ca98443fabb57d7c5620a41f1e6a7d96868828e755b1f5e7572784d15eba586a4320817094ac7c43bfc64a524452faaa526525606ee1cb3b8f3a88a3bed1d6b2e3f0bd57c32f1eae5d793a46e626b0e468f3f2318147f34988717582de73f3044ad58392c420bb2a4175facec730106a0afc6e926d63ca7dccc90b75f7ac2da57b470426b2fc1c1ac3d1e81e3f12e8758f53da11252e5f5db23f1c57ed4dbfdac0523e5d5922bb9508ecd6530698c290272d1cfa8b7ffa922e6f5820c219239e1474e62fcd1f7e759e5cb12c5dfb504da0dd60a42f2db1163d3a398bbb838bc730ac0ee656b3b5e50731b1727cb7d949716b3ee4a8ebb70d7a7eccb87cda1439638e31f7d26199b0c1b37b2b0c7e0983ed674c96bbf13059ec80c77331e7b566387a7e478345d3f742e0f59f553d94212d5d84581b50eb7e36798320baec8f0a5770e52837b2b23cd9959d95833fdda4d0f51dfecc7d3a5bb3096f9aa565ebc671d55b1c656de9b0c2bb9b2f6fb1692e4c68beaf01132700d4d2ff8c7cf4de8ff2891505dd82f36bd85005297fa6c28561f66dd35311cd786feb40a516b3b9678a70752a9859e10fdf96e47209a6edcabb4d60f9438cdadbc9f329d9234b06fffc721d2dbadb6fdfa54083f5edfb9b385fe330f1f8cab0fb1fde6aa5e0a74b2215fe62e3c4af7495a7edf4b83e53c58ecec66bd1acfa5588bb80a42703db5ac1d9781732cfd9c6d4065b5f6bea1fb7437b10d56d26936f9c0b8c0f89557db99eb5a16b78eee3e80e660ca08a5d51902e414036a2c366e5e5094554d1bb132d851ddf2a323270797f19382ca2fbe11dcaf3e85cdc8855e79c82e2d512bb90f27784b12aaa57f8d808e64285c4208210000208e186f27ccdbbfaed7d554f54a05f461a9f866404e13bf8743ed2684480aadeceeeede01e906ab06b80631f37f90f47befbd3785cf1d7f5078498e0fc87134825ad7d1485a5ee583793097d727b5901fff8c8caf7daf7c99b978fdbca9dc7023d07da4723393e583349bc1787d08b58e5be5a6befc78e77df6d2e85bbba994524a29a5945292a47cefab94525a6bada5a91fdfd618a59c73524a69adb5566badb5f6e727aaa2aad562b138193089c2edc3404199b1565a8c060e714baa248947e4ac28bc6e1af9ff62bea42dc895b00426286982139e300485284861891353a04fd8c0d0010fe088e1032038e59061061d217830fd05dcf5e6c1c47f01ab081e4c7c4a5b4f487c894fe41e515922491c406c45257168d322346df1d8020200a470c310477c5882831e4b5088b3c33582832333bc15fddaa8f8d09dde14f44084128e849f17686cf358e48f339bd9035ce4cbcb329acd609f61d9b78813db339967d90314205fe6e76a6da5f6c2a83df65ad5f67d3014b3d90cbc57be6416e75174798f4d3cb1e8321fdb5836339f3e9787e16c66ce0bcbd995619f310c5fd9032e1835ecef83998fed8b1fc4f1624dfbf760e66b1b726c6f951c340d56e1aa3f645c003eb1a90dfeb976b4b8e42122873f9e214e3c43a438fed986b0db4f8e86014515af56ada2ed284551c127388c8f6f3f0f46c237fa792ce66bd5a5555cfe90bb5318788c36bcd727ad60fffedd34bc9356a0b19bdb18e3a3b4ea145a54a46021ad702a28525046272622bd691996f1652bb5381f383111e94dcbb08caf6b2b9d374d10b10411483811447824220744e8501b7ee50b54c87a0fc20d2184104208218410420821841042082184104208218410c297306a9d86c75d1321018218526180ca9adddc52c5599ef43275f227dfe50c5c4443c2c0557c8f4bfe7b5492e27b4cb244f7d22605a2f83e397c71ef7e79d79262abd85cf7cf3da9c3a1838a4fd12259ac964ec5b3749573dc135ac59ffcbf8abf2a5ec566e9a86449191597392cdde42ddd8e57802e53292457d17129bac723ffee3d98130ef296ce05a777414aa162bb80b4add5b8141df72c2c9de5a4150e4537fa940e859fbc8aca68d441feb2d149d7e2b11e8b75bd4a275972d4c99eaea294e893c7df8da2aa3bf994ce842582a8944ebfa0946ee32a4ab4fe944ee32a4a34fc2eafd15f3edadd053efa942ee328f064bf3091a2aebf24a8f082502caccba8fa912cc9a2215775422969c8d50a91072023b895c51f0054a40887fc567414363cd257a9415340ae384d5671824661f1fa1715635278d4b64b5ff8cad7752940fb2c061a72ed27c57abb36e472f5d486fc9dd220a3e166dd7b5b2db7257b8ba64a712ae13ec919a2fbc98f8ac73ff9b928b962f17892030a9b8b092cbe5da9f5f65a0f1af20dfb1691765b07793c72c9e80bfea5f5d154cda0e01009bf5f5b0fb910e88e6b0efd8c3e463fd3c7f42fac822dfcc2bb6f395c42b61fdb030a49425744408043971ce8a0e1c2e5f38047c812d8a3020488e4e1c30e4e88a0c7138a38114495c09fb35cd97d661abccdf9726fbc7bcde30ec6fc14a01fcf0f0f2edfe4470797af7d5e0172f93516e03524a7d15ecedd51fe99bcc47ef472f793fb3438fb8c3b6a047cfc4b38fe141e2489a15fee4e83aee80c7c229319fe0660bfbdc43bfbac932b88dec60f0bd9cb4dda5e3efed991b6cf3f49db86813f7f7edc751bbaebf92b03a068cbc01e6f1aecb74d93d2c9aecfbd53fe7ddc0f65d790dce4e7e3dd477b9eec1a9277d8f517b8cc9fbb195d309600dfbe6631260b3dfbfcbd869c5fb31892e79d6dbbcfaf2171e4f2aa21b9942b2d2512245c06be7eca95ac350cbde5eb6df76dbf6c468b7eebe6671db692bdceae7f79d35c8f635c1bb370f12c1f3ee6c19cbacb7efed66940fbcaf3a6912b6431e4e3b7f1c3452fdadd96cdfcb3cb5e77dbd6b56d317e17ba2ebb6ea353c181969f5d34e82aebfa0b79f71738fe873d86fcea15686540c56dfcb888119de8c014e88a6b0072e844079ce01085c367a1c0cd08119d2447e68f478826cc2004921e089184bb40081d7ad01b1011e88a031de02852040942d8084183103508a1128205456450e406458a14f14191248a2c5144a8080a3e206b111b4ef1044572d8c103457ef0ae5f48ab7c1f0011210f4823e420a2045504c707789845761491811ec952e8320211254570c4905f1c815e81d6fcf110012242c407911e22acf76080c083ae4187401341810788a82006321082aa88205170021235b8a03ee0f4c70d22e8fa6f100513e81b84199af0c1c1821976c03a6dfcf1ecc048119dd23d3038e91868cb4387e6d13b681d2ca1ff2b7610039c5b9c91f7ce4d9a89537e44bd39f306618c3331e0befbee1e31ce19488a31a77c21cfcfdb9c72ceb949332a9df167648427a876528925a4f0ab950ffe853046296954493b2fbd2ace33671961a657c5365fecbdf7de7bf9f6c820bc32ac49d8c3c258de309d8934134c04df04beaec71c0e610ca2bf5266f4a2dd4df4f71af34da48928b3f3a44f2ba753e9a67453ba29ddcc2d5a267cf01c91d233cbb2ccce9a65735e9f6d14cb3e13fd36e7f626d4cc5ed4d168ff36936dcb5eb4bd689a6c53b489b2956ea7b6fdf67a9b6fc231e198704c3874e5449452ca8d66f4b3d2cd9665382bf1a0c9ff300dc3b0df4a59966dbfed5eea9b267a6dfbed45a7d369e5742add946e4a37a51b8ad2a265010f12423c480491524a8d52fa1133a54c29130e24007e8c611862186218626f42c51612c3af753419db300cbf86bd8663ce8f450ddb1a9611fbacc3e29b704c38261c130e5dd520b42a4ba7928e928e5e639e4efa947ddcbd86d4648278d6b4ac6d1af4b46d5ac6f0ae316de94645e379774859a59552da9a1bf0ad0965da614d3d2e6b4d3b4c3cacad3c48741c71b2488690fc64d8a85b8141d32b636bc251d1415140f15c425d794b285c42897e77fb57dea7fcd9932f77632fdadb63f4c4f5deb4ecaa31b9c5aebc69d013b778cf29034461e9a67453ba41f107805893674d05cd684a418dddb865a4319a529729c5e5ebe007e08fe707407c090f108e441e2008e101028f18e96a6505061a7485adfdfaf2da261c138edc4ea0643f9fae289018a34383d0f9be56534f36a578c6b756ca126b35eda8f9617cdaf5c859bed21c151b680db5d6c8c2c27c966f1161975fdca7ab949189a6abb8fbadbb6b9549573537a20965da114d3d724783210b84a6fca61dd0043f9b7844964d93594c382b93c442da3df26c4af1aaa7c91ccd94f93407e9e796b8c31570dda48c83304d8533a14c3b6a8ca6942995591e5f142827ff44f3e9df1aaf6ca63e4ddc1dcac0787d8de254346c5a530f4b8b38eb93baec57bafcf06d779feb54ba0eeb50265474c1412836ca1e6d1515d16ee11228678c9e28cb84432b5da16e1fb3999a3d0e9232baabe0a07c02ea79c569e6d315b7990cb86d92fdfcf45152c645bec8d7d8ee5ac585b7709d4ff4afcf3a7a7a30f969aa0c8ce77791393c5cdac0e252eb70f9261c68ca7fbd09254d297e6badd5840309405156200a26fcc086637cf4ac1ff4f004f0c7f30314e7312288c7081d1e235a25141fa956341563f0d7a73c628c8af6a03931c6e4ebd39394117dad5f71776dd7dfced21c32abb9f1c206f8fdd2a95483df52d6d16077aba99fddcff68bdb49e9069ae2579aec317882655f42f1fa10ee5e63c6f81d560869b2c7bed66a6b6edc7b4b3c78b7f15fe035bb77f25edac1ed2da170bf77f7128adbaf11efdfbff7bbbdf2de7be39f3cb64b1776fdeb26618fdf3ecdc1427d583ac518fcf74b3aa0a9c65a2aa1743633faba6946b8de1fe18b47777798ddef5b0df7d6fa1863f19c750fab599d3cebead778af13eca115fd2b9d300d34a7a5061a381582f0fa2e2323c423ccaeec7be9be7d932a13c4ebb6bdc6c40f37cd63086dac0fe1265db56f3570bab5846a4c9efdf65d0688639b7b42d37851a7bb275f6a4c19206e332cd3b253e9069ab0cf5f3ae5afd98b5c42715caf5a6b85fc95ee4895258a139d8c524aa952cd3925a594ca5a6bad125a6b2d9702192451121559ad1654492e055af6cee7dc30c2ee3d494a15961c1e7994913d487104288047950f3c3d28c2059cb4083c12e105d1a06204e7811a1cc9411047747064881c47783802e40810468e6064e8884a020dc99064861c3e49787033043d2a01effae70d64d0f908a9a123363ff820c708124400c2880c466e8c94c0080f233718c931a2c3ff0a233f240146187766e64508796429e703f0b5af7c89a391d6544a3b33f79c14fff578d327c1a79198d327c14cad32f2fa59ced44da59c1fbfa6d6f933726f1a2b08f25b1169fd6b6dced65a1b6f1781624c0a3783ee973f2e87266d4fa37dfe6dafc8a0b59db5cf5ab74119e326a5dc7ece39b7a79452ba7dae98d6d55a6baddb678b691d87d2d85f54d56ab1262aa84bab6d0ea5efdb6cfbfc339bf6976bbbbbc0635c9efff2bcbb0b1c7f0d8fa17dfe196daf6c7eedb727e5afe1db7641db305078566d60a0707cd9ef72d56ffc701a3297e0677c89af72024d83aee27e6f45cfac39edcf98e21307a983be97aa285351f225c78ba9aae8828a32b5f2f05ee68b2ac6d49c8ac38ba930782e3353150c5ecc7b35e7e72fa0e6cccf8931f2c443cda9da0cd7ee929fee9e2f45f064eacfef9be576d3ee4894a148e2cb7c24489658224912254af89c73ca4d81b893ee559e322bd648595590613cd9821c8d5858b47e31278cb9c16d621847c3cd26cc60c47761cc241905e1ff7020d0fdd2972f26a594c5a22854adb5b258d65a7befbd17931da7e3f5fb82d3f1fae5ad168b055110d555e0efbb0a98e4746431ee8b1519f4ccd6d1254a9b53e6020c140e9f6e182840317725f3c7fc38ff95ffe6b7597eaa1363b00c637ede34d3557c4a83d42534e8fa782c2c8f4fe991f848281784960f371705fdf1838ec05141767676767676767676767676767676767676767676767676767676767676767676767676f263188cbed26d08f1b93b7d6d463fa5c99a52fa73effe1ed75259e2f927f3b798ed53feb270f2337b949fbb8ffe6e9d9f7eee6ace8f62fe89e8a7ce33dad4f267af5fe6ad7d9d264f5fd4751b422c683f3749fffcfcb423e9c79e92f486911f7becf38bdee6ec4db68cecf3a6c95e6f1a14bf378a1828f64bd935243f79fa7977943d47bb86e4dbd33fd95d7f814f0ccb33eba93df6799b4f27965f9b79caf958093dbfdf991f633fbb0d21fc7945247a98c1d07ebe48ebd75babacb8c9eb8ebed665269de8356d467bd1ee34c5b58df167bc7b9e39ffa45da7294ebb0aedb494062017bdc5f2671b42dce44d76a741358a3dedb417755a77916b5ddf36d6e5fc2ed4741b42dc0cf49cdaec727f01c60c3496533ccb7287bf85c416d0c9c08f0d21fefe6a49a50c4320024d4919863004baf28154628912499028814409225b7601e6cdfdb0713f704a045122c8b6cddd6bf4ce769675dbcf395f703fe83937257c703fbce07ee07ed09ce038ae061c0d381b70a4c77e467efc31c0196f8450434869fd99b9e9a96e7a8230ca49299dd5d28ed3d1f1e5c5e9e0999bd13d73e926ddbf9e44bf865fdb85bb616c182ab885911088c8a70442f2392710934f4a291094cf5a6badd75a6b2d1033964f0e081d5f0605b55a40e83df75cadf4111c90201617007f3c49c8fd2001eacf8eeee0d3840222cf8df341ce4929a5b5d66aadb596f32128a8d5ba32ce0739a37cbaeb573aa79c516edbcab2aceb5897b54ef4f463d0a416444f7f8668cfd0d89c915e59b5c57e7eb6f7b7b7afc5d85e0f1adb3d7f7c99cde4acc39e065dc997b3eb340797ff42acb986b04ac11927fe12ea2aa12efbf3ef9cd7e9743a9574944ea59b1e392a8c922ff42d4e71bac36987d349076a156be6a44dc82b38a935a5ac69870965c2c14b60211c145d28a7035dd115bd35a478c0fc90a24e37689f412d6b81ee54a592325807eba8b08e0aeba87a64af838360161612632a26827f624c060484e46658e954ba299d7ce4e0558e0fbcca51e5a87ae4f85801d1c12bee868be6a872543d727cac80e8f450a97ae4f85801d129dd9476609d1c15eec951f558f9c03a38880aeb946e4a37d8ee7077ed957d3a9d54a51b1c642536c3ea29100ea18ad636e4cbb14077da934ff245febd3d1c0e9ae67a9a730c2aef88319547ce393927fbb828f82d64851b2fc1435db6c14331663eb58931165ed986b3e1a29c0b680f0e1ade8ff9a14762cc7c494d38261e3c404a78cf43a918d331ed49c2e553221428c6c8c74bf229c63c1e201db4279f4c38f47b56bb9b70f22ad3207336681afb9457fbdb949ad828c6d0b98a2e1508af0fe105218570beb5f6e596516371525476d7ee35efbe0965daf14ca969e98570b6f85cc12d5ee1a4fbe29e0753bf45e470a06bdddc139ab2387cb95bc8f953aaae14acdd6ab5b65a4b1f6a2d03dbb0ab757bacdb2c84d0ee5e3a554abf8482755a9bd5d2cd0f4be96f2cf922b1eff40797f5c4e5db70193198c590ffb05eaa8a2e92f6801f334e88ee374e9d3e7fc0abf358b4704de8b1b82f1f2b02877fe9c0e8f0afaceaf72d67d39ce8f2d447dc7a2ed6e112ded8b4d65a2b9d239b88fdd0aa29b569918989486fd08432e198522da4edb4dd6b4c3826d433e19852269409e746ab915aa49694c93707915abdc584634205e0c70652cb84c362431a623999522425a4252c38482d134e121a7b2e09dd738acf7f2214bcd18a744c3e0749991456d752a5b7f09e8352844e263b00390f663e4b8a766fd129ac2d8595c2faa107335f84ca3ff3dbdd5be2ee2abbeb0745283e492d1d3e9fd381eea4169f39c84381cfc7a4560072082284c811244778bf4141a694094787d5f30314801f5d44ce0f03f2410f8f11d6c4fba55b429d9cd82de305bc812da4cc5e98bced4cee7da5d397743c5ebf84baddab313297d9624fca8874bddbdfbd020366937328c1152d3e61123b38c71f0f12433c438cbb41d3dca7bcafb06450bef21363567a56a290152231e6480d6745593b0e098d3d87c4fcb7e91fa213ec39079e82d9ac08ea5e06a385c3eb3b4a102b4e0f66e2ef29283e5fd4b5f4c89799e5effa878ad683992ba2139fcff1e84e57587cbe109f9f75bde587671c7c7e66b500f19983cfcf38e2d59177641e3df708329fb26ab79f2da14a37fb02d2be94a42a3e21d5e1f357b4f8030026b129505d7363dbbeb49574064b27fd255ddab1416aa2b5fe5289874947a36da26dcbde647b9392d6dae44d762fc998fc9332271fa44e62c1e88186d90b0cccbc071363ed525635022942527af8944035f99cf3f578055ed763d26392a471a6a474c951fc53791453c5a720098931fd059174484148ac18d332fa95a0c7223ecaa7ac628cf6f35380a4e8a4048931d84f20257817e9d4c75fe32256157fed147fed1a177577192039fa8e0284f2fd0df1f95a97b24a01d2a5e8a404892e33622d123a00878a4ff199030204ae677908e02771408719299ef41c50b149f559fe7a160ea4d824b95b60f9eb4975cf60d977c6e327ed9cbbfa2fd3a49471c36ab02d4720bae4bc2510775e3d98f9b8cbf207bad79cd563a193b7b4ff543859b32cfba96d9dcd5e6cd889eb41d7ef3566adbdec6a78d4323d5ba765a0eb45addbe0317b8a92a2e7ba2eb9caee3487ad3934474a97d2753deafa8a127cfe4927aaa7e8322b8e6a63135d6610ef653e0d34d450830d36c4fd96301f561b29332776c2b824747f4314a6ac483a8f457df9f9df93d9fe3d19fdefc964a09a9381e4ebddefd53250b6b1dd55f8961b28083edf767593743670c3e614e3ebb06ed471487415207d3dfe3e81e8329fa4235f2689c5e74a106744f70cc4e7f714a018139104c518a12431a69587eef7b8ef7ef14dadcef7209c5aad5b0224fd42aca1f483e4a352aa975722f61d4575f9c5ec856846fb3a9f94d98c96492de0c77e06de33f2a628f962a245a2afd90bd168341a755c0f3a3e7dd8751b439a9b9cc41a0e3b1af4c44ff49b745da687ebed7577d1d7cdbda23a74f543c7af377fd7fc6658d7c50ece6b5f9835565e4ed0a718a343a3f40eecb755b6f9947540d5755d33b26cd7b8b8ae0b1fdb7dfbb1ad1e4cccbb6754c6115de2e32e9fb28ee8128dd03f190361a1d68e5fba26c61842f8f6efd7705bb30dd7ab4b7990b9242030010660d1a5bf1edd5f0f845c119c0f34ac58844ff00b097abecc7fbfc605be36a9057bf70d1dae3be386dd352ee811343784eeaf47856508084c80010d608003628c44f23ec6c4f82e0850009574285e2daad2a0f0e063619393a383f7abf572befc19f3e5bea1c3fb1bf2e7ee32413330decfc5dbd04a41cb9fda9094792ee26f38548f05fc07845b97926be85a0205a8da5d9b50d0540f4675b5ae5674b9aea1ab5f2d3e4351dddc54fc01e00508e76c41a60f61dca49473ce4969134d3451adb5d65adb44134d34d144134dac80227fa6ca1a0ee9cb9fa192da2138214e8813e28438214e8813e28438214e88133282119828421223aca838fcbcc21981f1b8b1b00ed6c13acf0d49197a922ff051a854aa8f5a4230682638d563215fb36ce0f0fbc60487a3168b434ef560e0a8c5e184f3922b2a20461c50df56a20ba4f3b9ecaf053d18f91c0c3a8713a1c7a27eb5b1bb9f30c1a5d4a93627420f46e2d5a623e7cf83a1b31bb1a2502a073d302115c40571414798502694291565ed4a27593395a5538cb55a4a29a525d4ab32f8b90fc20e66324c38549a703286b515ad66196509892eb4e7081ddf662f4c3810c223b4a9629032711521040757813f9e228ef02596582cc6dc1d8a44f752284324eae24f19a0d722966eaeeb35764d6a446ef2787b9148f4f955447f4df6156d9acd88de7e165115ed4173ea532a0334e3e3fb367b51ba8104887f751abb25db5d8c43a24b28987df5c96f85a6c34c0b85bb84da3aaa7596af80d85abaf76659bef6a3ac2bc736535b3cc26d4ac5bf31268707dd5bb096c7e2713f34acbc058790f3128d837838082f818570121c81781ce2e9e0d9c0250855ad6c8795e096ea3dec2a57125fe2c7d60fdde90ab7a40c0ee2bd00b50ca1a2036c83214e83a8a60dc5a1ebf75cac7052d0d306e3883127ace3ca792ce2ff1c8a314ba80dc531658230defb7adce58fd9ee570e57ce83a12a8eed4e4f38a20b8db5cf219bcdc02778ce39c61ebfe2182bac7308a3ae2897d4b7bfc2179850261c2274f0bdc01cf4fceec207a0428431ca185f655ed7857a5706c523f25b91c33bfe788cc0c133c474e98477a96e39dccad28cd1dc83c63a3de19bbb1a8eebc63f5109fd81c1fc792c5e27082b2544061a0a3f3247a6719f4eb4847a25e415ee89b81242cb94aac10f6a099b202ca10283868fed6025d2f09eb59f03453d16d7ae66b45d6a469e8173469e23bd33525e57a72b9a63e6baee75efbe007c62f532ae4757acf36022ddf434572b242b2274a73ab3a652267c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c2cb43fdf7e8e6f5de40765d47af4d5f36022cd2e909c11dda9100ebf734634942fab19b8003937040e89700894120e8172c22150523804ca0a87402987403904ca21500e8143b821ad163724de11bc0370c480c54051f2256730de7371c92dbcf792bda05aafc936b649302a7f29a57c491fcbb4d31efb8c611886e57f1af6b4ab9ceac4984e5795e70a54b18eea4417989fae709d6ea8f4edf46635ecd4d0000040004315000030140e078442813896c69aa0c91e14800b9194425c4c170aa36112e430083292306388214000400040666466040275e349c7a61204379926993c7d9fc9dbc544776fa0d341cfeb2dbcd9a8562c2e9ab686c00de70f51aa4a3c63622f188668a4044ec6bef81bcebbc5fb1da9bede04c045d6678de7dcfc17773db766f8aebe57336e4b08020c00526b014cfd4d84653ef2c8dc97c87777e14e733290efb1f9d11d11472001efa0612e48042704b99197582ad4d3409e715127ecfbd478e5c2c1dc2747880a3aecd61317a1a4fa494fead146712a2b8286b8e46e6de410cf57d1de184749627e8a64330706f3e32b2106387cbed573ab9b3e60ba3913aebdd43c05fdca96e703c5098137c80d77cb69778bedfe32d1269ffa6442445b33f0b949a8a9ecaf3505d06f7c2234672d06ab8eb15d7f14ae65471e33249cc4e6380d92ff97d39efd8ac4ab29f1bffdbf46246c488eb46aed0801496309c75768f57452fc442bcf72e2e1e6af227c0ef9880c6c11db27b7f9cf84f0a6cd0c69915058c2e6c0e24f1b77b5ff08ffa19b306a1a599dc1e6a30134d948f3bb5f27f88688aaea1f067de4a8485b833ef91f7d72c5e709745281b0c8eff654b6f624e94480fac4314c0df7dc13b75f098f6a7aa70ef8363eb9e5cfa4b6e473e2e0ccddaab8752ad08cb73961e22ed0d406392979e53f41ce8f2bc5934026c71bd215df00056b25fbef4f1321c85ef72e4abe4687dac2c1f84546909da21f66d152b57f009d699187daf335c0589c928e044ca0e37d75f9cdda1acb4e8bf647f9bbe09bbbba2909a1c2832757e580b01c1c6c813ad68221e5052a9a7e499f637134069d1225fcedcf00c06368bf4291d93ec531337d51abfc39b9a660bf9d0f4c4d63d15e2e39bacac38e2c12ca738f9b73c22559da16809ade299f1f3d5fb17a400d81aaacd1ccb1db560e9d3572f04edb818351549eed9221a71500705b9d40814275077c1fb2cb200e06e116c89a6023b7ce0947d9098cd79121776a8e46f4a98042193724f721fbc80a97e5daca689327a3a3c53884015dc4e51474030455ca1976cf2022f532aeb19406f49a0995083af11660ca74ceb7bb8de4ad8654a2bd9000189713dfb60d4a5a910946476371c81e34d72a54a58516595eea06e89135257869243736d8311497d6715c486f6a007468f0f86dae4e8a448df37f0d340b4080c161dcf574d27b869baf8bb092f98377e7359348974feb4fc57510862ca050d8b2ab7b3561bc9a2b37c832b1cb4bc12a6c9561f89dd51d822e68b9086c05646c3be116e99ff49d8a15110cc442d519b2b1659db17690ed37da466077570a7945d05566f20cc4bad23991dd0728712c0b49b85749c58e8241c5dd8bde2642f2dc8eb2589a477436839c917aeb24ebb20578472ab85a9ecebbeff5e218ec4c20d88d59b6d54d4c10ebc5af2318b68703c614e7746f2033c40ddafdfe0752ab0e4f568f72d89c48314a50fa3064ea6a78887369b08b23b189b0d52c537db28a04e384143e422c04e3b2520c204783402b6a63c4fbdd5c27432d2325737e24b89449c03e48fbf21488a6ac4453cbfc3228523882d298bf3dad0797d58ea6c6ce71ba01fbc70c4c11fee11cd35b33f63c78aa4601f155d22d59f838e6a87b92339be2826e91db8d2ef7a29d32bd8441847c131900ff27e25b3dd64a98a7112b9c6f0de2e05d14af2c11559ed11d2818a200a00fd91848848f6bbb2ce0d86ef2119075de433a3a6282a7d4dad795e4000bfd1929748deecfa7adec7d1f6f240ca369329233666560f6a3518a552f46154177897118bb34bfae6a4587dc94cd3b44a7a52ec5f189157c9f7275f697e37f396984b7865e2295b7ad299b1adddb9cbef288c3d8376047e96aef7fa4265f476140624158d07953b70bdd92f00a8282ef8b9a31985c78fbf45ca3d3ca3e528a78a0cffecc40b9833a95759c541084a0beab995d6997752e45abfa28c949ef95a85dc2bd546ef57720053e74760baceeadfa0e6b03ca7cfa363e40e14beba0e72d513c1c9a11f60a017231cb2736480955df81766cd4650fcd72872551ac3ba2c931aa07c580935a994d405e03329e7d1002c03c09a450cd84a5eed019337fc2578c3b12f03da7e9a7d4a32178db791f459f6f0db6d894c77b5d9821d50f004c3af09b1fdb02bf8d05aaf626f8772d19dcb3d17f0b2587dfcb5a728c5c0f346e7210d3a1b2a74fb96c1f43ade9c0c6ad9b8d138aaefaa00cf48906224ebd6888d17771c59d2b74773a2618d6dfd84e5a016aa997751bdecd287ab12fde2ca370108269e66fdd415531ae6f4f41ffa48b10f3f2fdb119dcd2081cf80d94b7c75b970e260b2ed8fb8435a5a1baf0c84eb43682de1e841bcfdcc354dcf977810d51af29ab04e0a1eb71e70e0025e668bd3c5703f91026c1c2ff1a9c3fc64c4c2804308a6d15fc54dff2be26803991f37b215b7a631064ce68bfe144d6f66cd4005979d714453e90d73ebb3ea577fa70d9aac030cc41c45fec1ae5d4501e33457e4800ecd8b1a0eb112c92e499e2717d6a4cd83d01db0fee4a2bbdb3927fe0a8791c799899db603c561895dcc378121a066f41ba4846213bb92aa446be3c7a6f1639aedc35f08061baca2bdd38eefce8890e948f1dc428bfed59d847a5c2288bf029860744516ecd2a7cc3ec8da20d6b318a1c3c1e806aeee613a1b51f5400e9860a1484c61ad485455ff0041a1d457924103f3a4c43db89b38e1c8a7bb382eab8aa38029510c177953d879c9c74dc29a78880e8078ab08448fb82fdb1989407878712a62d217fa9aab54ff9835f06071a784b4ef3090d79f7fb9458142687d05d9501a605e4edb037e1a8cbfa073d468b95eb0a873e9a3c0c85c4d1e3b18696caf129a32ff49bee5f031e9a597cc072d439c1269f235528eddfc1be1e8116ddfef54dafd95a2af86df4a8d8d5b71028b172621e01b1dff0815e5afc8a4068837dceea8cb8dccad20699c8c74a8923c31e6f9550af0e64fc15011bf3f69772008ef54ca66e0e02cc72df58ebfd5ab9bd3579e820e80befa8018db914edbd08e303a2f05a7c063a86a3b3464c6f243894e6756589db56aebdc7e04625f1723319d60963aa5c7e4e207408e26afcad55d18929280a23ec791c51c2829ee2b9d3b8f1f49c62823e7d0c3a8f5db74633807f525e9e2e8da63598c90ec74841ce8c7ac2ac21bfbab9be8db2fef22671c9636c37df41db822acbf0fa58420593b51b70acda7f3d2fb493ce1399799c9c6242fe4d1ddca8a3553f9bbf0ca2667caf15d684af35ecd1a93dd50f6bc12158ef5d31d9fbe1368fb65e8721ff947862f14f24ab4f285fbe94ed0f53a6194ec6c4b80b5d83180337ee2dad861a28155f6c1db006b722ade20d2a78590ed0d86fcf9ab84514f4496ccb694811cbc5467b9bda8be9edac7b9dafe4b0e7debb71b21ad97d127eda3e4dea0d1e8ae75023cc4707ffb9a071c04f48e67271870c249ad4fb35dbdbd801492fd42b5198cc821599c4602551379df5e840e3485e4ad61838ca3e6f41055777b6ad08265963237e031bd21a9f5035dd50ea16c3cfc681e491337354a251e3d33d4a1df0c2046d8b18607d5ffa40f1595a8490ba2ba40a96aa5dce19c098dca74bc1381b0183f416a3298837f842d024c331007a4442b6448184e9b8f59029a45f583257610e4c9723b6b5692b86af27cc069ffb03d2aa198ee163d9896d25fe5ed9de16628f4e20607d2b59d7ed3f9ac75c930381fedd804909b48212082fc501e7651742f95277adf524941c447c7a7e4fd877dd6eb5b37c24928c0ff2be21f346efc122b1d5bb84cc33645ec87d02fd414386dc642f8432ffe017e496f13f2bd906602e25a650bf3e60f632ed5a201c3de2648d2885d1ece25aba54ecc00c500d97e1c5d6ffa8a6b46f9225c2b29e20dd147d74fad377147f4f3bdd7f3e221e7f00d6f6773788b7368bddac3d591eb5425a687398a214faad09c73ef9ba080bd3d34f235d6766f141a72e06b9f2d1cebe737d32b04fbbae7fb6a5c88ce472e3987165eefc7c610515d9980d992698187732771cb0325eb98d2bd7c1318357c78e59fe9ecbb2f175f9b990af081454d37c4a0539882770d51265fabd8010b89c7d0219de173a80f22b6ecfbe6977e6996d888a06da270034c92073f1c0185920a4799e8ba5a798fb73e769979fe550d1240da34f463e749c5124f6afc1864eed79fea9c569fbbc1a2d194c79dec6e106073e3066b1fed3a98944d8928fae622683f18b28134627085da1d36c005f9c651f1314f2ae3910726d8cc7269f984a2106c831f2acbb407eb0ebc510a0254b1cd4e21865cda390ba99878b28ab53fdeae6d548678251ddf8846a32eccb587c67c1fb762d85bafe3dbb8a80c5b89b46b42ed03849480b58ec736741aebf5cda7ca8ba7b388277e1a9a6b49913cd3d849be1040430c350aa50406b5942576d27384d7c245eb47319d7c00c649af2aa249de727097408801c8301f58c4e58caa6d5dcb2f5c17dbd1303fd0e3251389e1bcf802de3dd345be803ff051efa36fb9776eb47483b79617031acc85bf37dcfc96ebe2e9ee4c51677788cbc210595f03fac5c85164f12274912ed708d52837ba9998f5499e7bd9e6d14eaf5597d8040084ef2d66c1e1088619b29bed642feb53747a95bcf8f8d84f0c995e712d7c64c1b44e5e72ebae40c25e5b615c81ab858c7cfdc1199a301050fe05d3c5894e13fd4a46d123aa4f24f8e30f5712b0d183e9f7865a2380a081362b6f27a4a3383cedd17ae81411bf68c07482d757df7899a1fc733dbc930a514992dfc8e496114c2784cc4eb63e877523e814be3b212f18b2ca436be9bdef198245082bc83a8ec90c3bcada4675912d30b489b741f7d64fc678c2db13fc65364bc546139a87f627253438066f3d3556e1b7f345eab928ff5e265c4ea1881c161f4a68a1a16d76065a8092492e07bc20a1f97271d5f4b57b12dd62a2403e91bcae3ae09b0f8f0fb18ce2a68545f8d62953e120c2219b8923be60a390a245641ea35a7e942fe28a27e42fd4ea390d06c9ec078c2e5096a8d340e460af580d3f0647e799866c1cf77a5f366082d5f5e4bd9d430a455db8e68acb6920a8b05336146e2cca3ca45f8405b41b9e1e2409b45c7a5644423e0c1ef423cb0bd1d60e8a0e3142bee102fc2b0e4f92635479b301f629b98155f4979d51763e6bfca5ac8d95500afefb80063de39147c3aa17baeb5134b3c9e4bad074c3818aba64fe9e901bcfa9873f138fc03084e729c3d513e596735af3880f1dff704b85250148266117668554eedc10b5433114600f9803d8cd5618bb8bef8ee561eec0229024bfb220a51c30198701cab7000e1c76d98382e49aa12477ded9b39597196fac08ec32254b7a4d1b0b70c56e1d34122d341627bdf698e57801e0093502cbf677782f23f6155c4505c9376ff2ace3e07c508a8b921d3a77f82cb3bc23ea82fe418c79ac6d9cd835a19dedfa663b9eb29b05613da6f470fd040d70978f403c6c0e5ae22191ba37b409fde095f4dae12e2d237663f7a18bcd5382cff71e3a20bb34b074653640fc7b91ccec6f6ebba04bb8db859a42bab0798a3aad00baa7d30910b5bced35f3dedaa4752a596afb332fdedcfc4605446851b5b794113d3ffbad7680c340a0985b6989c8a027351165d86037252f656b6d13769e251009a7b95080fc01e059e8377425d5f5fee7de28bcdcdd3bfba91cad07821f2b4513087154b6bd5bd2b272eae51e2a31c35a96ede1b052648620361fb4fbac108700489c1c828982efead0eb84154090d180281107fe8532b4e5c7f4146ff81035de19d305b9e96bfa0e5d9118efb3a3ffc4b6a7bcc32048e521f33b768f30bc237f55e346dcb05e1f7cfa30c1757d5975252a2beb51be6c18a56e4df97462030f2a97263ebb4cb4f5dbf00848ef4616eca54af80a9864395887573efcf7cc1c86a74cf70d09b97576681db47351f166e42b17dd95967f61dfb6c35680604c7efbfccc3a8e6765216a8eb49347f830e08d3fcacedd2eea4157c2615d140197434c6c02ec7df75dc0f8f9ca4bf799492270cdd60b134c6ffe79b96b6251641c234a9194a59fa336210932508264d74bf24524a93ee217ab03ce758a4118cdf4f17a60cc9163401f023acd9b7ba9f088056e67229d72f96b97cc9c5b297aebe72b5e4a5eb2f5cbd58eae22b97afbcbabac4e56b2faebe7059ae5d0539dc62c4f19be1a705c55fa44dc8fd11f3e907c09756c4588bfaaa8ba1778d459bde2dceabb21f8a88af8bb96cb9bc725006f8c14aa6c7429b616cc39602f73986808760ecda759bac4a7e297f580f831843aab3fa686bd78c5e5bdeeef9ca9cdb4cd52e523ecc7dc300bd8b049bc23d85a13c915593679f2a70313a9655cb8c8d04547844f43905edab11b17e6240f5bc969e4411e8061401249ade2de82989b78648bc0bc909c385b5670feef2d19757b71f024c28fe1570aaeeb1083ac7206b5cf50d5cd1c1b90357b538b3d3f840094e1b63c54c50a6a9a8fd7ae702ba576ca9f039ab32b4d0aa1ef2daa459d53070ed336ee692f1e7839841be7a5f703b854eac9ac181946f1a00680a37d3716632cd36f6f494c395de0f595055cd3e9f9d4057726471cd15b9f4d9dffa3e825f200f8f4f8fa5b52801a09d4ae0c3ed152eed7947c830d1ef92811ab3b3df95409f63f8c2b663670e02c64134f957ff333369047e393f8be9757efd4b87d0eea3975d8e3593663c6ca7028afa07e95ee362370896cd9c45246991e4b68bf33822d928b46ca9cfd242dd5cb173c9eaa7503d10f6fd2b5d5d8983186cc0a0ec48498f794020f01c1aaf81606fb89922e72bcc3483b9071557bdf6db0d991bb84771e110c5b25fa0b091e90981777d8a57cec89f5799046f5ee4406f4cf3c2c57f2c445ab0543b598528c2faa67f5fcaccb080970a1ac911d1172845105c24d1a957090283304bbe9e7b5d9b01da7207c42d722fa06ec267bc2dabe7510103149da60455db2e812cc286022d8a3e4b5424e5601b17efe41778c9427de0c12525afd81c3bf2bef443a83a37f6460eca01f10834b26a488ccaa234f8a96e3138384ac1c0b614bc7e5ff343f57c96b6e2110c4e48826c17e1b0dd54b840969fb1c2533dd0e4dfbd9587a1f3e57bba2be12d52de73ab3ae8ed9cf02fc924b51fa1cbbea2fcc28b922fb9bcb0d4f52bae2f2c71f98acb4b2f94bfe4ead52b972f297ff1cae54b2e5eddb5d82fc5efd9621e7f105ffe3d935885c1e1ca9bc0b815c4dad2e65238fcf656aad2f0acec90e9bfad99dd877fc7948e8a797d2f7519693927301a2b93d9e0b6879f8ce3cfa552567826772ef9bbd2e711463b8eb5a1f8080f3f78e917e73de3eac3cf80bc8a896800664d5d039bad398441f4f0e34a215263e26761c10f3f95347daae9bc9a2afe001f7e5e3c4f059fafc68102a853686bc6fefb37a414fa76ed0753e13b19d9dfc30f56823d17a9eea0516a18e9f50f3f1e3d6ffee36420a5d8caa1674a616daf0b14acd76a485cacdc117e16f690c36fcff31338f94c3bbb1fd0f5c378499d061d1f134395007cf801409fc89c3960d6ef712181dd19861f9426242c5d145890f525deb9de10071732ce9b436de48b121fc1456ee6a61e0c72431f7e74533c94e3879fe52704407a3fac956488fc37710961b30fda54378f2d6dda50dcd86b0065e4cf8f5df5eb71b4c72a974b869f3dc9b12e63a3d09c0fc8ab580f2d4822e1742290f411565b2c9cd018ca03861f090e85ed8103cd3594737f57072597ebe28c46ffc8bd0110a6a943bb0b13380c3fa81e20189b632ad794683be51c65f8b153b558f9e46c5ed0cb1cc0a7e383659cc0cf78809ca6d2d97ffb8fd9452eee46ecbced4e0d66961d0c3fffba16d35cafdb1d39dacb3307ce71626eb0b16d984086ac1254287e6975f58954e8e206e2a2b20f97a4c81b980590e72a3a113d806107c1973a2bd2e8427ef348789f6519903acca66f9a8444a00066b5fbdfdb068038a7f38b965a2b33fcb0ee8e0ac361b341d2844da4a6e132f9d4d48069db0eca36bda6fa73a505ad6560e688f4c72c62c0417a0c3fb0859b9bb4ed187dfedc8cf3514ba39b95bfeb41c32ff18cbc8b6685cb3cc6e4616cd38d44a71b3d4d7dc0b0b25837ac346bbcf71ed91a75650003bba197b6fc8876434752d9658713e56e35fcd393fbb6c352373bdf6eb093f0fd7723a62bafacdd8dc572fc6e8c8148b0f0663f356cbcb957594a11d3ca5be4f4030f888590f7dd0d8de0e8d3d23aec8293e0494dcaf47b427ab48c9452eb2954d228bb21d716c3b982cabb7d5de9b3ca67ae3106067152583b4a230b69ce0aced66bd2bc0fda912f85df07dd1ef3dfbe04019c09faebb51dcb7d2281f7eb8396c55cf725b4b791abb28e20c9d9423f2778bbd6eaf24994d247ab4523cfd7474a46eec28cab1d2127f0a0febb5e1b949090ea038ccca6d72df153fc64b04c245ed7b999a7733ccf9de757caa918207081b2267ed246fddbfda00ae91912939d502e9d9db60178943fe9dc55c09b61fec6390af146436faac0ac1cc2ff7ae9dce5e1f6a07174e804f5f4cebf9e7a4d032a9060fe40e9b64077b5e4bfd6c51fad23e1da6ff5a73c248223ab4ff4f40e7dbd866b50367a05f9fa82083ac618e002657ed75eb58c88924b75f7c00e77b57bff6c8a12cda34f027adf35f4bb2ea684f32be2a4beaf5a8a37ba08c2ace49c033132df5793d42b6c18d234bde4d1c0e77640871b0b0450ccbe93daac6f7e908e9f24eb0b52b218a34c6e89f443f6fa294002242a8e73228a90b2bb8844320c0941c03ea5f8c92f2f826538afe64795ca96bc1645fc3dc2e8fdb6442a871d6dcceef43a58e412f7f84ccd8adfffabc27aff088e6e780fea1e4b60467c0efe3010630c059dfe5c8c5073d1fb2ba61162ec635b137805f11ddcf12d6719e3766d471455dc0d21062ee94916e31bca8a246110d4fb2826a10bca90cfcd26a17b605abae6d293d02126599154476324c61abf9c482ad4104352c2444321be8814356208ed9450be8e12fd55ef41e970cab51178c3abd0b581fa7d176d3c708b5b091fdd4f75cfd8f8d22c5f7b78bed0a95b5b3e65f3e96af8ffdb80d74a981e85af75b048dc5b3edb0dced5bbbd4520775538de3dd04687a3e93a91f859d40817a028299690265c9caa0382fc051093750981abf5219414f61f9e2a9c60c4eaa1a8ff21339c7d5a74c6ccf886f4bcfc02f5cd698a6a52b1c93af8901d63e5464214602ee88f24d22f623db9c64602886a27665102dac2b708d80c9a89afb48c9a2ff727a71f4c33e72daf4f5ef4defde13d50036131761faf3c7621b280472586c1584be84bcffda4a1bb51f3990278b06b5b45e084b8fc1361156d4185c2116f5945df9febcb6b49000eca16132de1edae56f7ad418bffbafda9a93772fab8cfe1d106e36e73638b2d44809675fba3b10e20ebe3c4bce98c3434118606645384db3ec35bad56efc32825fb3bc3c0ed14a0b2526113b2d389c0299e401d2ccb14d1060d6cf14033b821912afa6c8f270fbdf1b236ac4b939344a962bcc33428f734a79e671b9441db4f1ba510c5053224fe29cea8981c0c2f1c888f7d881071887133bf504985e531324a9e51a1879803f48e0cc2520264fe5d51279c8a991170cbf685030d0f736209b8814b5031866dbc9d0e6cd20cb619dabe236365834147cb85a3d0ee9ddb881cc2ad70dd94d5342eca1310800e316d87f1fd51c012720a81dcc45394c0a7b02f0ce1ecdaa73b8fc4ef9232d9dff5b78fd3152c2456d00ca982a640aa83102e9116e1f81acce8285e563168a08742ea1e2394d4e1774eed8e3246a434291bc6e23171ec42fde19dd10409166c6641ddc30a947280e622450e8ccbcf623058fb0e84c1c8e63018c329328f6108c230d1413578b84a72bcc159cbc32b0c91b3ef361384a8480be0255a0bf9d78b02cf415fd375b828ef718935414c13734bc212d3f601565e3bae63b88d1a88e2d898f258796e08658fad25f1d8c76626fcf88c6a44b0d02582d18c61b2ca574851bccf02207c0621c3e17a797815a86785fb268b51dfaaaa725344945522160ea45fbd67ec789a0bb1c4c60e87b69a16444e34b6315334e0baa8b2a0915c4132e9fd4e4ef3f4d200e509840e7e087ee5c45f16ee190cd412d4f3c288b99a30dd92267e6de525a61c026302877bf2b9d076a0fb04ece82200bea82adce7b4052e0cc2dbb4faa5b552e5814dab3c243c64a31b8cd1336012dc41c6eecb2c479540898dc7a8d4225efe29e3c3670f2f56cb798068c4378249fb06a28800fcfa5ebcc9337a1b4ec25dfd4131b426559523d5da98dceda60766da4108535418f143028e7d3ca3dbb4c3506168626bef1b3616c923a81aa7ec298c97176fb8906834f98aa4cf2034ea29802bf903fe0816685903ac2c3e76605569528b2b32ff0e3444c7076e1e47770ed0cf8ae01b34520e2b0cae90d71615033249d90316bbd95e633d222362ad55d7ea077b81e5dab50e97be09cef2bb4a585a3750eb9ffdedb3087990847c3477a1e306cbfb30319994c1dc96b4b2c4b395418440cdbbce530999f7c8a129730f924c35e63c8e3c754650ea8129bdca7675705b3ca990e634364cf299277ec7d83e90982451236b3fafe7a5f9623e43826ab0fc39b125e5f6e09e2b19aa388898b41c931d32f9db075872214ba9a26d2b138b99433fcc3d67d8626dce7a045ec102e5ea17df35800425843aa7f7cc2da6e862f41f5603b77850caff99215f7664858d57632acd0c01a840f966ffde03f616654bb8f725c3bfc6c70fb489dc6fade43e7378a547e0755bb847aff4b81275c6f416a5a945b1ae2757b269b4f1c240cc029cb76230fae6eacfa335925c50f581508649455759771e6dd8a6765fa59eb662acee948e4e5ad4564c75113a76f276b1e0109f86470bfeb9f3b4fef03aaa2d94c77044127b74958cdd9983f8d9c3be8f7fc1f2c103822ed802abd683b507842aa6ad14016da52a6891e213a42c5016ef264681f9ca1958188f1e75cfef38aeb681ad0585b5b859a4cd3e3aa17e5f1fcab3b2cbb1f6535fe5cf231bd08b01ae0c5e400127b1a76e73beea4fb6176769350da36687721a2fff51b036c09fb907b4553e983d040cf3a0fe1698973fcde0e24e69ae4704dea97fdc83e9d5b97e5233e69c3bf00838a8846a67722a02927138d922916d0955706a33ccbccbc7757c6fad80045a9ab0574afc3e9c912d4f137c4bf3a43ebe45043cb3f176b37d87479c5b01c29285f1734fd6cd36fbf47081a4be9d50b5e10258b442cbce4b6f2a6a69155fbf4caad31caac4b7fb944ec99eedbaee06301772d8655b8f1e557e3c9ca3de3f87fb7f6f507f4820373793089cb922d7be0f1312c8a82b1c58862f38ec9c8a1b3d1bc5f300fe474cb6ba55b057e76c2dbc23d13595ed5288697cb9a09505c202e5c840fc89707fb3b8d2402a9a048d3350dcc70d60f0fea93f7909c24d7aedd95b144d978726f4e9be074fad51c24116507e49e1d33cd511f43b75c57653007df102f7e7dd79c59782be2765b2cb756051dc174f662377a60f37330a7d9fdc23a8e819e2e64c8b685f63b9044cdad35cadec784c4be37b65394ddd6abfa383b8bf10d746d8e03da9006d651c187ea86d07a0748705de20f21213aa1844f736d8ae579765c393de1b524b40f4a76b65c559e7e65643f4185802e8773b536e2a553eabc2800047df99ce9d6b5e9ab2a1d2097ce8de856e4f6551007eb4d08414dc3be5586a5fee57b4a9bfbf75319070b78f60a7a926b9cf9268cf2b8f092d66d975eacf5ac38b89a45dc1b8e4444127b26904f391d52ef8ab29e5faff5d74499aa305557cfc2b39603213609a250360407424c6329cf8279a6ae084397cea073c4f300080876dbf417de50b78e411247a94660665f1727ae042f7402b073dfbb9a65ccc847612435802b32dd104449ee03d2e86b66d2d2681253956ec12fb50bd5bce14877f56622ffbc8f20d6e8132d756b55f5983c968fe9e6fff8ce5b8f69cea062671c9d04f096cf88d9919b3839686a00df017837fbb02c20a763aae832ab14d4392e1f9eb3540c33e64b56c4132db3bba6750649c34615bea25dd8a09ea480fd72691c7ebc24ca0e82385761e543a7077edf9f8149645baabc14fd4f5fd47783bf10fae6666a0bb37a55094ad3f6995f096b441c130d00c6fc71bf5e44850b5197182069e217984a01d4d14f7f606d9e56475cbe658f339d035e0821a7c882e89a6046507303a927f13c09041b5fa1b6151803c688811530e192025714354edd695e3d49902703cc59cd3b306adfb1541c65298a1adc5821b310f6765164507a29952a7947e014fad969339f9fb71d4c5f7e495edf4b12a4542ff5b824ed8b56e2083512115b0921462eda40204993cbfa68cc0c2b6df52fcd389316b425d6f8587d8110f69b73735756e6279ad091c1601f2ee9f60fb35067762d1a9873f4aad833f2834efdc9039364273710acb6a3d0dc91a79bef05079b4f6c82099fc9649ef98b5dd3cba04e4f2ef72c33d49c2683cbb71c48b376620c7adf7fb888f753f23dfa174a2d741bc6d98d18511a5112fbc2805318ed4bafcc133294a11dbadd07d7b5cf65bcbbaa2f7fdcee4efa689524c7d0bb36cd2aa18d13df755c8ae807ce198a4a35fcf6ecec45606e0e8b1a01cee8b123f63b4d8f1c8eb106d36a6e94732680ced3e3fa114354d2d4c374e202848e35b603924a985a7efc892c66794f34524e500203a7ee168fdcfe3253a901a9d9d02cd31acc1ad79387dae418de32cfb5310de662352a61382cb607c905127227a71143beac4fe5332d5dae6284b0a51ce1f887c5134e8fb0a04b89d689f4e3006a2c941d1904e834378a79b95ab68506ed316e80d7a3a91fcaba68c9789f22400edf883c8970a1c80615eb53ff50aed91700460cd4204c0960935c60ea28523260605e20911d3d12f4925404df0329dc8f2c9711dc0fd54dbf5ffe4be2c2935b13bcb8cd38c09c3995d308023899d27e618d0679032f44473e7132dc3d4dfb75ec98c574f1f50229ff9507c9ca96d66a816665181c7fa7280e165da089e19db41301eaa31351711fd813111063f38fb5401d753c0d4e8f50c0a4d81264131eb41fec851e4a837d021dc896762f55f12e25119c269baea7eef5248c3f8b1ff90a6603fa2e2d49ad64159e12fc64a055b62f4dbdc72dbb09c8bc81a54e75c862acaaa83433db7c0a80dfb26906ab0952914ffb26be8bf76cd5203bf170240e54a80dc0e5c42a14d42cc74a92768cdf329e195c8171fb18f336a643a3dd12fe54ac36db7d865a5210d53b3536b80cae512b8edc786c6438f4842c8884d99c3613093bfcfa58b736f9663cb4af1dc8b81776d2ef9130469704f978acd47320978bdf4228800bda16d13e39c4710085c4a91ef63adf8d4e86dff50f779f615f16fb7805a2c0226f04009093da5143e9d700926102cccfcc5907fc2559b3003a595eb8ddabd189547c3f3e58edfdb0d741d19903b07148d838b3c6b37776ab674442c0cb6586e6e7c79cc2e83cb938ae2a0fd0492c185c80c7eeea49c6e46b77d0c98c72079aeff0b587db0df20f823788b0da80d2060633722dfa41ab4ab9e35bcf9e9c9d0c319c6c8b8566869253718910f455b12e001e35efbb5558e79b53d7e6b7b5ecb0ad21e3716e682d1856981c6d04f239facca1407dd764dac063fda6fcac89468d8130a4043585b740b09282bd6bf8de66267f93b6a42a189f18f758b051eb7c3ac73461803d850cf08457c6e0d4b281c1c9d500f01278f4a2882c60b52bc861a456d71b13a3c70b6ac426f930a029525672ba5236b6ebce27d948717596d8b1d0dccd96d4f5b573cb8ec2250967ac47ce0567c9b160aa96a66c5baea69142ba63259012b6a25f52546b9159cc928b67b8c250e89b387b4ba28c2f5a082186d07063630214c7377a0ded840d711025492880a7455df73ffb6636dd859d7073e87df7d08a433a5af304791e2f397e4b82ddda33a236025d02561330ba86e95afecea7a76c5e330540d7f4ac4680d3e14e7cd049487aa72bff612b3073193448885a18457420c20d47fcfe791c159fc3451fd04217743b5068a1dd82505c1d4697dcd1647ff3c22bb9bc860bbc7b5aab354094b88018cab0cfc280081eb77b6af23cad9cb0f94c06dd4c60fb51e9bcbf5058daebbefa9c6f1871e954780f602441f85f33f5a462909403213500fa8c42629f451f4731ea636273e1945c2096e73c6aa657a8e6a594965d79b90168a06980d9ff79f87480d63b096985385c22535b192294689a9e3203c7b473ca16154e6fcc40b09ef892ec6733b14b959d44ea7669c159c3810ee056571a410084b9100911ee9599329b533a0cfdc71e398a9507bf4d5e87f5f2fa72d00510894e23ffa94fcdc36bcf167ffc15f3fc8455f384e914ee0e05fb59709a9b38c64011053ea7ca74aa2d62ebeec17a65495c3e72a9ceab843cb7eaa410d9c72737d37945cb18acb9c4da9d1b23da8e21f1250d07219400ef3704af7f040c290115aef02a1770b8127066f5254f77e200f704986e753c9610d196fbaa686d5157b0c3ed01593f1e8b39a9a7855259c1bbb2d6d8084b360d93cc8ccf10b189beeb268bf6cf2f4eef8287cb145eb69c5346c1c8e57b923554f3077725835e26e8b507b9ba811c0789a0b1abba2e0169727ed7a256584a30e9a01da42a61d90afaaf80284c93686eef6b193b6f805b603e27316561c053b2011bb4b183cae0532d20526904fb565784cc831a8410fadde9df156952b72d78b28c75036ac98810daf33ca249c1a043938cda2c3a6efd4a9c17996a48544fcfea763fde8f957cc3ec800965dec85a47ba146087d64cdb14743713aa1441c1763057b7840a90beeb8da9e881a0bd4e389240086e80424ec7f077eaa5fb26b44d4aa6b214526a06c62f8126a4295929df20655b34fc2f9c30dad1f3393e7ed06b2dbeabf3f4e38aca8c53901d9dc1aaa8c8af323b065670d75051220830ccf81a08a6f127d121c54cf0dd08d85e8c8dfa0e67a12f90133a743efd6007fa3dee57f67a886a112ea2445dcc7915b604782b562eb6f16a0b333e2446db90cc760df76933d7fadbbfc8e385af7be08465b89ecee0f1bffd0dc62876fb0388774d8de30222abc4a91b8d083d4e7938038158d3b2138c4e116082cbc78d9c9e709495f0d1e7f557eadf63d1dcc1755b562e6f52aff8ea51171824bce63f340517514958bf491ccabd30bb6d602fa368ecb077e66c86c86959223a0898fd402c98ccd76b28a62bb4083b49f732f877a16f2e0a2d51ce846223cc891747ff8734b6d09b2008e2651798078cc4ca353021b7d7881aef9a0c56d2c1d40f89cc3ef0d7840295f3d62ccc9fd0751003d2191bcd318ed64e346363c84c264f9bd8ee0fcaa3ff40be0458db5068fbdda9457b369accce3c524d65d25c13ced5eca9e82ff9d01503e9509a7e04f1fe125ad52b933dcf1ee3d392a0c2bf4d43fd07802f27b7402dc76badf904b66478cfaaa094d8b83c4c661d40ab3423133093f5cb1c1b599c478957ec881860a63cd8078a4020cb003771481ec52e67f1f7f1fdf6ff26787ac55f19f128eed445205d21c07c69973bd0ed7fe5761af01e099dc963e3461906fe3f623c2a9f8a50f6bbe236aca2a62beb903ca88333c1a024db5344d2ade301b9531fc10abf15fb7723f8d93d06e226f3a1f11ec3d127e3fd3e9a535eab01ca1205ec3e9fc8d0314405c850d81281f419c5868e968e95c2aedf40d14c70bb1638c71553c15b1f0b72ade5d01c06ff671b7932280ca020114e472e47b0df75f4c96c3971a4b87c94e69702095005e1e0092a4b5c30299d973510463d97004f33834b9807238eddafa9901e94d8213287e084ac9dcbff7e59427c1355f035010131248aa8708502ef2c65f5a2057ef2379d45aca0eaec3d8b9bf3c69710c4b226f5735444d8fde87074cb8913a37d22ab6a49a20b97abf375df843671b2f3e54cd7c4604035a41c7b321b41af4dc6776c4f63ee61581af69cb245761febd75d095a1bc8d407c1dae329491924522227ef489d80249230e472b69e854133b26f82f6b8bf351c794160ec25cb7ae263dab1e10b07ce638681ece44a8552008e833eb9148c55f3d1a432024c56d8764cc4737a327c92d1ff3726b2d134262d90706e985ba4123c339873300211980ad8f52922a5ad4fcf2de576d4075a0eec4e705373c6396c8c668a33aecb53f5094abe317df679c4e915f103fd5fb385bc2ab7841b2b0d8fa1ba3296995c49644c86aa6d08de4526ef6cbaf72337b3a96506c65e2bc6c29ce08e2f01e62390b50c24c6e3fc457e836d73f22fc70a274a4f52723884f2fc6dfe3c0aa99967be46546c28b9ad4407713256d27ec495f4e76b1a4ca9c3586470308508101600d96c5cf917e0b8b4e7b7608d8abae584e08ede32c48e12407821cd05cbbf7862d8ea13daef452a63b0850a57826a8a4c47ceaa3951a87868a84613aa5edfa7cb558b0d10f58c1c298a5776ea1038b1d69096efb93494c970a8c902bcc46f50a0ca31b0aa582f23f4b43b8334804885470b9377554009fb70437611d8aa8b04648ef740b15b6c540b1f4ebaca3bac2a780e68bc2cfcd5348d63a3c29ad5337755457da14d07cb9f8dc6a0ac9aaacf92734ab4eebaa13ad905967b21a64f8b9c2ece5c252a8eb4b1806ef7863485d451e3ac93f82268d0aebe6ba0b624b9db433138c5058969cd0c77f2bdce9c368edac8e18e4d8021938dd342e96a08b16882c35963410aca4b0cd87baa248a1be4663b8a2178230644c30a3ec4c80959619407495b4d006027568d3c7b3e492b8cd4723a0242150e33cba4b891459f694951b468029dba9b16e4fa3ece5846322af691f3e7db2499196f121f6bb5f0861d911fbb0fb5f65bb0a095020859d4a45573412f3704a0cb5067b2bd6a877df203b915840958d10a69605d523544225d1ea1f8a955e50f3e63404a8615079083bf414e1c1e4f4107546d600dd5da8eef35545b05865a4e5eac67ef4379474a00a23bb97eeaf45a05f7db4405f93d8deb0d7b17115f4a0ec935497edc99e8743138b85da2372f9dc84b910250cd2748927dc0a67562be965f422ca887749af626e164b3cacdfaaa28964579106c808e142c52d26d0b610219ba2d43dc62c9c7e4869674d0384bf8756354e8d42e53be01c699b4d6d622520d8c3535838810f263f451a17c0a4d7c91d50cf8591207e7a1e1c5a4d3104312f9254a42bdedcb40ba326ac37cd305c748aea3c97bb36c7b3235986089f13a8a12e082a904e77e7900682f7379aed97967bbe30c42e67cfc6c0051ab1c4076d6651fcaa27da159970280cc3a5c22ea97c34b4903d8a5082a033d9b3ade7d31a3d8011f66ae6de8e261dccfefbda52e99f0337fac2bbce140d79f8b71f52bb036955d65c991c43540ae5624c847c68db5977094748141e3eb4bf1f6f96083a2be3ddcfb12eed6d1894e3f8054822505d75bd394434024dfc0402fb9c05626fd02dcbd586ac52fc3b8d5bdf8e6e114e3a6373a5dc0e80dcf12db132706c4dfcb74b463f54a427495ac0901e992aefa212d7163819300ab4d6bb5314e3765338dc6aff410ae050ed368aed8a9eed3bbcf8d2527b9b499fb9a016e6da4c51d7b3326c09634cd7ed6d1982ecfd914e441001d9e5530bbc392b5992f4ec3fde1acc28f2cda41e46a422c0cf51ee7fd1a4a035fead2f62b9d19155bae222560ce09a458c1b1caee8b26b282304085c93f26fa3632b3a74e90a169fc256702d5311a9a8a0a0d42d5d47cbc1759e434ec06b9716f22fde6fd1fa943762672d59b5b07dd0dcf28ce334f35bf5f48c0729e2689f846bb921a795b44668a423343d371c3c43a4aff6b52c9575dd6f412c14bb7f872269ea8392a26eae4de9c7998a60bfac0950f78c3fb3dc0eb6575913980b14e35a3b42dd16241c5fc771504a97373f1dea81133f3c21fe84bca4f347d3f9df691ed7d600e406ee0c1b1d7d35a05e179580cba320ceff67c519131150409c7b6ac2f1c4628c9c5c72c99421721a004acb25eb32618e6e598768354035efdd5342553123a0fe10faec6830050d1cff6056ebff7333e946f7cf32fe448593052014e1ca664d2f75b97c8183418a57a6bb2acabfeaf468643948aa371fa9feaff9a36bc88690defde47e5a3364b537d726d8066ded258ee9abd37d0fe481154726d3e4476d1b33da047299c88199ee5f61f19fe229bce6c5a3e392d93c70bc05416868d4866afd5be2fce11149749c713aa1574fcef59853587268f8c8f267c21c0f7334eb4739518db13b22422aca9b4f5d9a245bd5ab1a153a46bf3a3c0e3b1ac27654b0e97b91f838913a58f5bc5fc9d9043a3aa04771dcb9108d54745c9b7803d230ab965da20d38c183e3842a98eefd91047fc9ad307875f3eb8723ec5f773e5538b97289806ebeec47f4dbe5147709305605a41b5c5377292c9a8bcdb777b06e8bc80b8e7d4bd36527e0b97f8178cf47e4fdcacb48f57a141e49f28b2ae31adb096347835c015a04635b6faf9f09ada760da0572560fab42f9404a747ce33ee0a255c091d05c11b720e25cb9119eb10b6ff010c9ad753de4b161de4505c68797d242a7f7b44ffa1b86e522b1d1b9817d3d288a08050767eacafda7469ccc881e03c029a78a356ec3fdbd679137c0011a0471dbdf3133f44d9b9099c11f4e9dda229e0383dfd14f67e46de2d3b82cf16867337fe5a1d5353162073fc841cb737db709c9a19049449dc71530c90c5201bf15864ba7360d057195251ddd60d823f06fb48038eee428c027cc3e6a83f1d884f0480f3c1f524be91f9b6617e3a4f6928cc68397f6dae527c4274cc0d4a9df892545900d863f0460428b52b89ed70192e0585918113f77903f04511bbe430621fdd8be8bd55f516ea1373fd2ca4b3294572f4e958ec8dde15daf10d8c8cdfc476e0b91dc8209b37a827655f4ad4dc40141bb554cbf8baf25f70570ba66c0c658d2583340d5bee968ecc61ef7c7bd12cafc0690ea3f7fe18eaa11a5e9b1944902785b28fcadee9fee1838c5b8328017783bbdd84e673532fa0d57a9ab5ff10f2c4bcb3c26d1aac3c5d2d25ddfaa731f7becc6250aa37104eb3a8db95c9031817f86512d620572255ff8ca1055eb5d997eb090df5874e0563e2370cf6277193bbe3d3dab068ff9739e752a904b13fa13eac195c26dc1a64dc85c33e6a4cbdd8daaf65422984c85c06dc9f51a04f4845396886d93e46259dc8e72963124dfe4d3d20aa21790cab9acb897fc1882a17b248910e83abc38fce5975cfe2b3ff2a22005a06ca4712d11de8238a0ebe97573ab7b79c9d737855cd8939ab215bc03f3ad0fee49c883cad684cec9cb24abb072ca3da55815fa9148fb385c4fdef84fc90de33e3f09eaf31c126d475798eaad90f5721504349403e435e41b8d67d9f265e7ef12b59e0e4d8f538ece3a8042578d454e94a2c40263d43f340464548d9c565651bd947849551db36716e62297ffaa635f46ee644d4789de91f3fe9938355afb5598dccb00c94d467201b280677bcb5c70dc1e80a7665062adda8ce92a675192976a1aa168e0c2129ae2e08dac5afcae66498bfdb4f02c26516ad6e1cce2a73cadef1016fccf62442ebce5cab00c198a49dd3a53e61ed09623ae0b9810e49a69aefbede835b4123d7033abc19c9530d552547191a67b7f58613bc6c260a74a42af6f3c64e3811b3fd8e2c166265e41026fdb3ebee5115b0fdcfec026faa0aa6fd87a2af83bfcfdf66b0d7f23f7459336650e973667b3a23f7255530e39af1d8da0d2faa4f30e17f7af3197ab674c0c7dc612382494fd19fb2456db8e477bfa0c586971f1ae7b76ed0ff4b1d96f284adceaa6ca1d85750670b57ea0d8c19947013ec455d037834124e6544f86edc841ea8b545cf36804e822537a3bfc7853c21a7f050bd09f9906868812805bc00e3c2849159aeb81a107fe1abd85f4272b75e9107aab015f1057d5a052e2a6bce7ba6f136cec0356be6afa9ab8f56aa2a80751a4bb989e1994aef9556ee979324033580043c8b75dd9e33e030dbef7fbde98c057b3a8ec9c573021dd3e0fcd6b33d27b3ac2893ab722a82c8367d406f91b03f350051d202d9d558126c744516cff926f0e1f608689171cd6a1c81e49e3bd4526cdbd4e944df41550a9dc4a3dad4254b7146883326b81a2eae5da3288c56f2de2b7ae251e58cb52895b8a5cd09518324c7ca275eb6e5e4861746ad68be3291452745d825c8220bd233f1c74bb09e01e7fd8d13585beb8b4d9faca89b9462828e65baf3a8d3211106f2154b5de3a80cbc04b669312112ea0dd215de1fb2e168643946a6f4cada62efc184199d21b55a2d0e8eb7142e38c618babc65cc462379082be08e1a8c3244b04e2c9b7c5098c8e860d9a96b4a2de985e66329f9011df63eb4b93f21d2d14530fad0809a6b0aab1419bc2a629c8dde255db64fdfaae9616623658e697512ea76a592920ba406079971c032f13279127dba13a5bdcae549053e082c284b4e1088b04bf3e33e59f59803c0cd181f591ec604f635a7c71ab0fe27e40b82a10f95d2e954ce7101eabd1740816353e886ac85435e15574cbac2c3fb8c798e4c20a8df3fb2c8e11ee518c736a78f99438755fcc424244c511613ccaaff183acff9b07a13af0bd2b0b238e9a4158a825aaaa8807b424c04e093d4867382fcc6eeb8f18af0162deb5cb83ac785762157f0992bce844701703d4ce708b1a9dc189f6b43242a9ae192d47880aed5ef10e08601b31a6711067a73648fb45cabfa6aa2e9d76d2d141c5fb1ce71b0f9811ff53c7123746027c6d45ba6a75f84112e4501bf3be55bee599635e05a1e42456138eeaaeff8666b9f5eb82b1d2b651e57a2dbd3e98f17f768fe84063ea09c885e11ab5edb4aa58761fd41052fc1b24384e127373c919c8a6a7a88fa910ddec13b95961467a1698a491e5266960c96cba599990068b66a76189897497791eeda1b33b8e0a1370b10cc690df33ba19f84b3bfff3e5667f782ffdef59dcd25de0879925febec105f82c31213d4bcca5cb629374b06036fdaccc49c97c5dd07876c17297c102dcab82fd358533af38a59dca6d378b42151a67c180cb5ca544610ed31a76b22be7ce33ba6f1099c4f0d2ae70006daf2f29b072d81510c7ef73a0d5c0f0d8e1c8e4d180d78a3c094ed7e37eb2cbdaccd0d4769e46e80899853671477ca0578d4d68ff9189dc106bc586f6a31573d3a8608e4d4646cb2201be02e2bbb25538301eab4da651c1d971ac359b4605c67c61adc9b42a30f11babcd4c57a165378c79b630304eca4365e81b7dbf7da0d2922f73925f86c3f9663a1c227c9c56dc6788152d6a57deac1c895f948c8a5d427d4a6dd8cf7cb5747a1de4c6b45c5ca64249b72adbd2510d153cc5904ed90c62c1d5cb137d1c7ee6f986ac9cfc1b6f0ef2a8e38f7d7de61e6654b4170815866f900b57962a4f2f47ca42822c43f5d9be58f22a4040ddba5c53a76457e73738f0ab49a5ddad6704a3661c814789a533aa87066c38feb59d00587447c857029c4a69c248bee4d6ba6adc4c2d727a545514d7b41693c6c9cabda1b63c5b1c7c6763346b3c08464734b6e1ccd3d2e7bf600dd582e3fb0bf1d8b99e99cb6d9a87506df2ef4243d650d1470cdffcd9a06e1a8b59c71999ecd7a1f52733076e596f2d8e21812829be9ed9fc96ee2b0caa080c3cb82dd40de51d932ce5dce12844913035517517aac1d82a69a0134e6b35200da29f2b0f0eaa9e52f62b524d04ed5cecde385e530bd9cfa2c7f306f0edc1bffd329cc014d3f78ac21197407b791bc82829607f6f599b7f0dac788bccd943b375755c7d54ac04e8090ae44c141eff5daddb43e8787bc0327d60bc4432e9421ed0a4c3d56d2e0c566edfe2ef3a2432dce1fef302323e0404ccc7b73dee6b4e0f7d5854e02941906ae5d9731b1b6fff7623217c26de3258b1a57682f94c516602246f72db766670ce8bbcae992257ebbfcb0000a62cf0419eebb98de1f1df7131d3aa7ec33bc2ffcd36b40f7f41653af3ddd6b6ab53b26ea875349301956bb0ed9de214e0576567e0feac23fce2492131d79eeec0fdf476d090848184400210cbe7bbed3f8918368858f5f8eedaab92fad42b4067bb15dab587fcd01113e3a6a7229fc10b42afeac8c5b5f93ac5c3ea33ce34fbd091fc0ff07bdfaf3a9938f07ec2a8911ed6be80d7453a74b3cea2d4be81f20e527c6ea30d8940dabd9f304df3ff0095aa7a709138092a4c7700b3a4d0ea7b6713ac8e09b00a0865edb4b5487db4d09eb85ab9285004c09201df73fdfdf51faa03f603b22929b0b37c56fe26fb51af238fb0061093fcda1da3707b1defb20a60fd3be41311f4ef8e7fdb80de5e9c0e02066d879f5780df73967e9958d364b003e03d63d9687e420ddc190b52141e1710a4dd2de22e218fdddadd47a00311e30cf17f052c1ef5d0e41cb5ef968bef5341d74c38f45a231aabeb41f93e3bec43693c4c7f207295a25b55a3bf2d9769c886ce1e9b13d560441573f6f1bf6208bd84ad19ee0651f67787bf23d39db2e8db19fa9363dc5c0a5d991dc53662e6ccaaf351ba519a8df8511628046b7e7bf394cc3e952adcdbced1b8f357414d99f779a7d922a4d8a3e257b053f339e03a61ee4c4c1027496e27643512d0bd7ca967f4da3f3a13226286867e5781c73a979c8cd51102180a9d5e999f8dae026882fbf224f1896ceb660e8a82ec35420b89d2192af6182d3730005f02e6d59f833457214b17d2c1408f5ee3f4bbcaf294549c98e714e3d63e6f90cad5da68e9cf8245d42ff3adfa308f644fa6b77e8b0112d89d0370c8c40bd791a0aeb6f24e525e8559d8cf2d741ace90ee6877981e988c2e599c0fdae703da7259d06095fb498f4cff9107d69c476c2bf6ca779b544be307f09012ef963c88651940f7e6febd5b940e4e571bdd8d93f1e2f0e5f4433e185979a72251c972a43a48a2525febfe3f1c2a1dbe1a1e5b60d19d0202e977b64da59cedd691623db94a76a3bdb5f53aa826d05bc9a41cd0196b43a8324ccdc5c3057b4cd481f2b9e9ca1e5c31bd9eea3ae9b7089ba9f8097397d713916ef5462fa728330b9964db953851e720913ad2d1a094d2208e40c30aacc880fe1bf4c68a5f44ca92854f3a378f41aaa3c96685b5b42b5c2dce65eae094289bb2ba31e564928e49725e2d0e1ea5abf8bcc652956357064487ac1ed5a0f922b3acbd096633b10ad6ca2504638c9fa4cdf2342e0efcb8acfbf5d3ff29adb20543b0156e1adc3aee5edbcd48de8d3f15a39f591f04bf943fb63d7ec7c5d4be8e9fd62d82b66649b9102a354f0cf174541bc3c534126887134294d1ba00516bdf69809a7328afdcbd98d5aa1f4094f77ced21e799ce8d8e2aa9d904b54b18bc90c9cde616f5591d1c5398affc7917b1c9c1c3884ccd92c33674b668bf5a4ca76c910f91ca30f5806529edb4b2e6124b440a22ef8d2516bad65416b9603808ec5c8ffc41842d0e1f1a6f663cc5f710c8c444e6a8e53611a934d4a7080da09e67232d794f5e768f8a813d2f74997230d5425258dbe18ab8fb669d3a4ba62341465d59056bfdad7a8cb608d6c066456743faea2557c1a3192c7569cbd6c7b42618581aa0df3e5e4806698419d76b75af2ed574bb8e41beb45e944b7afae72454cc5e5a83b68fa2a026ddc2a292f89bfc32fe47565d0f8cdad92c4874f72b0febab4848433241a3cf42d6c8c6fab3e4b1f77169d3168d7df91c4c0188ba965ee52fd55db3062c38446efbc91a28f795a52acbc62132d0695345d24447399bb9e744bd1431e7d491a7eb2c2ed17db57f5a874b19661d0cc38efbdd4f3e7ba6edc837a3210e789391e330c2c47143dd51998e2d15cd09c9f2708468fa912b9f36b48a67a583b430379b250eaf9ddcac04ac283071a9e9e3608d6fc00e17637d81c2b20cc47a7fed9473ec9c78eadc78315a5bd2171349756cac4e79c23e80d2d5fb758deac4501d39234fbd4afd0808b6e0ba05c6aacb999dc6276225221cf8213f93e2682efca96a31b6fcf2a5bbc84a675cf3b0006d2b4533e0c4c9a18cd549a499e9d4b965c39f0b669202f0e9967a9ee18e4459d5a55541e8b38a8fdd581aa03ef48d0ba9591c87260d6ac836b216c3785f23a897b415b8593b7febc3b529bed275658a6f874dbbbd00921066ef72feef12be215d87886c4aa93525dfc52e7b77939afe5f7aaae8c517ffc69128889cdbe89c45d0eb3dae6ec3414ecdef62b90b3e337e40c29903a9556c30d0a5e007bf32c2d9827c9d89b4b5041cf228d1b996994cad9ed2bbdd3547c9491ca2b9a950063082a7449a0829d2b5e6220b0cf7051992eb109d764ed4ad0664a566bf449f9fb8df6db5b3cf67563902b7330c076df238a43b6500e54afb6295eac6c75dd40d7e807b9ad50986ce7e9fe29ba0c1a5e2a09058f87250f43f20d86b21f0ef726e4850d9a9a32e8ef41032389f2f1baf99873dc336832eb2836b5ba528186b2f045f053bd088a6090b2411c680ae96481251617c802b7b6c1ff9989efab8f85c3a7df66f8ef63344509ea7437a642aa97be80dd88897b871d21b8ca99ca9bbeecdee7a0c4277ee2a4d68d64a0c45e1699ffc1e8cb03f2733c8b4d3df6bac2a8e9a7d13dd0ab3c1b2d0d944a144968f142ada2fb0f55471f862d74c1e5b400fae19ebd6e2ba778cc52b2f7135947c63d27e99055d88dfd3c28798a015a97138bf84aaa9547db563af77b8175a58a57252e6686ae4b13ccfd74a75dabe89d459ee62dfe8b25ac42d1e5055989707c8c7e1a437c1140a061d63a07c634241bb2b538d678655b411177208e81851af4101d9d70f2135c257886caa12f56f3af030daa964103184007e7f2f4b10373abf8b424c0fb4884e59b5cdc41b77e38f6641e79e28784b2d5d8eca22ed8ce382442558693552eedb08c19be541a98f475dbd2d6400284565000a9854925ba8de95e1f70d72c301e2e45660abf7a5922690a6c92a1e719f9102197ef8a482460a0401346d6020ab29a1302ace04e122ade2483e4e42f8f37ce25c68812106bb37bb2f5bbbe76feb479f0d1529ede23b564d8196b38ec531447d3877310291750cec84d9d40a2338fd63ca1bbc0c90c4458dc763fe2b93b53c98c72f4fd3ea1a35bc98dba84cc4387eff7559fd9f856dfc29d8aaa6d1d874dd577036f41df97fe5bc7ccb9376dc70b8d819a987121949e0cb09d3949831e856adf199d05a611cd8d78a493e006988d55ea1d59497af59788fe80485e15877a4128f6c3e010ce2a6d0119b3e7438226bf32256613f9a1d62f6c9162b54a8f00df7a67fa890cff77da84c4b8e72df91528f706898ed44d22bd2723f934aa91f41a902885b5d2c155740c76d9581bbef60d60240b233c1f838f072d334d2da63de5197caf0f0e300b1e8e58d93de11b1062fa4ff52bf1600a18d72077496051e65c5f31155106857361d00bec4096c60a00bc7620d21e42d1810660436208c56c0baf883a1fa690d82b694ac5b64875cba91b4bb454f486cdb8e51dc1a69d99291cd358260c8cf26235b0f9de4e4481226eb681a4130a891bd538d115f10c111f49235b61ab5f974b13efefa78865ea98b8ea9fbf86a3d4fafb4f633c7d5afa57df9a50f0ee2db67ea3b393b3b3cf7f3fc5b735b1d676fcdf1df4a8ca77f11076532973d29c9a6ffcc332e6ffe177f9e21f5fab6ecad71964b1e7f48c54adde2f29183d6339e63c697d26a95de6cf8b317bda65a1bbf4ffecc7363b59f67ed6fad36dd411c3a75b17e9375ba8546c42914f56215a750a6372ae43e945291af470cddde22dd6607d2de043a35d3e91492941290544eca758313d290919a25a421235eab14e30fed5d6aaf4886ee94946e1b7ed060ad5292b457f4dc98eb8f143be895ba7f10d65f3c241d2531c18a60ec83f8194f2196568f307e0aad106cb484df86a791d2cc93a8689cb90f92a33ec986a7d1fd8a53ca7d102cde89ab384b243eae43f2bc5d0b8407025a5a5a5cc4cf99dede8ff8db9c5a5a5a5c727da8221eacaf062d422b645222b546ff3e946dfa6a8862a956e938a34dfd84ce058c9ec2aabb9ffa935142fda770ce2ab585f356f379666cab4c7f3215e86e578e78d4842f6863f1eb756dcc07ddaf1ce20b561bc66266f8625262a54f3309c32a9db62127fc7a613cc721a2fb4b0e81cf81102994bde4db702ece174358ecf619e7f0474314ddf76f25ae36f45aa56129fef54a5dfc3a24a4352ba4091aee4783117f7901fa00ed806f862eba3bea6be62de70c34e8ee98988cbf46451fe7639f4c25ac529a675b878e3e1986746ee6e263598dd2aa1d92e172863ff3eb35e4975f2e899132b0be18a6f0c5f0811836d02d9339947f6d5e9b31867299cc6baa928334ef38645b8dde70705b486db819c7f8ef1cc317dd95a8020a4f38e008f7a16cec430a75ff63b7304aac4fe21293f68ab712ff15476bc37f6d3937c3176339a437e3bfb1719695c6a2e62d46dec2a8d8e7299225b6b1c19feded6f0c9763792bc31d4bdddd4d749fde2f970c098de30c33b6b7589eb14bcb25b04a9f62b1a879fb20288edd624f491afebd2fd6c7d7d2dc9d34a4b4af3f1be78d46cd5bae13f750b47677d021d31b1d63f89fd6dd41f99f429a312e16356fddfd43a4b1dcfd72497703f978595aa5b17b6da5b14a6fd72a99b7d8e779c3bfcd1abb75f7cfdf66ed6e9feeeee1d9d1a934e39ca76b71f9ebfd32dfaf387bff29b46377c7905db14c957437ce8e1b1bb0bbf1a4232ca4a40d29699df237bd6891f03f90fc4d3fcee6679b34d6214fa11592af1d27be0f45eb105ce6999b9672f8645451bda5d038cea19c560da984544039ba553e3374ab6c5431dddd437f367c9a450554b3a850a25954bc4009cda2c246b3a8006a161544ba5b4777f774778c8d86a1bbbdbb866e19ddeddd4debafc692c617c41fd211c76a955e9b2f97bc180b1f0ac8457aa399c175c8cfe80df1ac6c226f748e435154ac4f92b0fc4df747b14ea36fc2e548deb249ac25ed99aaa5b90c3faa28570ad52a15476bf479d24ceb51ad4e5b1855c424d60ff11329ddf9e350ad526cc72118fe72c933d52795c6af75da86709f44deaed17d1aae96c4cf94bb5ffaf0e33b3d14fdf099c4277aa8fbf899f2630c2b52de8ceb910bea161a69a2bbedfd203c37f317291415dd66cfe4ed739edda3db5b4077dbfa6351a1868c89df84efd3fcb53b9d72349a6d52b514df2782c5f9b954c27f84bf36317e24f2161aa9475f7f2c207f65fca2e7d2b272747777abf4f75de183c2f749b1d95bfebad94bf396bfeed3709d37c73e29ba1b37af68cb14f741f998b4bfac0de7adfc18cfd88bf88235b730cae61646d9e4e04cd78d28dacc7ac2d99ccd7470c2998333c59f9afbb15b18753f93539cb15cf87addc2a8ee66e96f061633a274b7bff2b4e1bce5af4a6bf45aaf3416ab33ecee5b7f33765a5a5a5c404c6dd969b6a5a5c585c6a606e7fa506f83432bd6543a6f369682200ead6863f3c264ceb69924f888df6cc6e5504b4b8b4b771ff527438bee06438a2bbd392a37f39f799d619e24a662ae93de09e55fade34a67ae76681ce71028c3d92af3bf96f63397e1780e8e65fe779235e37c31d7f937c7d4e6e4e05338678e9d30cd8f7dfadfc7ddb7e5b71571f096a1d3ac3bf65cffce253a4ea87a94bf09e7ea44b2d60953fcf579b0d5a891b54ee28be518d6a39c38ff8eb01fc33a441c95c4ff302cb1e3dcc48dd3c791e2fc1df7d5a7d72ab5e192748b31cdba58a7b730f56aebd7fc3ee9e267f2c370d2ac674c477f5beeee1839889fefccd81fe775dab2fb5f7b735ba638274b17eb7bee5167e88ff35aa5b5bca4dfcfb4d9dd45dd9d14dd3da3bb7de08201f197b80f7f5bf64afd6fb3fa743c6dee0f4573383d771faae8451bbed8dd20e86ea8fe5e2ad0dd36f4f7d2eaee95fe5e206063135e6c831f8a667aa3977cc175378ffe5e9aba1b143f874fe2bf98ba3f6943ec95fab5397c28f7e934ebb65cab3f3639850f158b3dcdc7bd5eb3d70b874f447cb19656e8697e915cff969b4b13d7799dbe76a76d085f7046471b1b1e1f9d9cefd9b1c9993c3a37379336fbcf71e9e8d06a3ecf9bf1acc4363897f6297c5b689369edf522a90dc7f28fe324f3b52459e29abfcd4b7bbd5c8068bf96766bf4e6b6fa00207004fc6358cb587eecb97842ba3bd65fcb0d3acf1a859a5f8e13ca655f6d92cca757eb329a19725e5c2d5faf6a692695642efe38ceb09bb1e0beae860c5191b45a3f827e384d94486732ed85bc5ed956a34c5729d35e88bb7b452284478934f635f1634fe1bcd96d619eb729b8a0052b1038f91144fb51003f803cd7d2869e83d71f63d956a337268e36354513ad22a2b2838c45fff1c36966ea8f607831d28d07d1d0adcac10bfda918e9176df55c266fdfdd38f4a7c2a3db5f2fdad07f9c4d5b38ffd6d2c5bf9febfb6b9cf57eaed40004c0f11adaf8d87ffcf06cabd1b7815f133f88ee3ed2dfcad6f8822fda6af37ff3cc06574b73f8e368431b974b878633d3c971f1f8dce4d07278fe47a7e7469ce18e4ee8baa1ddcc8943bba1f57c6813665c5e9b1f9c8e86604bb02107f22891fa4bfc914e1a9edd3dcedbb5492f5aa45aa579de2795428aa7d0e7711afd8de65ade1839bbf6dea21f4bd1227d4da45748fc8a53a2597b97b038dfe8f5fa6bc358b6e27c1a2d192367621050f844827c4aa0f089b8724a554c5b5a3d9a772ee17927192367589c6fe4ca29aba599624bc63e5f4bde682c477db5e16924cecff8856219ffcc86a7d1bc73c986a72d26ceb7253d857614cacdf199eecf6e658d3d85769cc57273fc1bcb29974aa72d8cdd17e7db62e42c7f937894bf49a4b1fc38632a2407c34f33bdd696cbd98d82eefea13f6f87eec61714475ace6e4017e8aab1b44a6b7e9c21b6c971f984ff33137f7e5c3c61f83835371b82b3d296692d7733a6b5bf36b6766708dadc661073b4f99bb5d9885f6b14e3e9d956b7e5b914243e49d29bf1eb86270e68650636faeba674a623895d96a7ec467f5dabbb69f4d785a07b9c50f6f6b7bb6374afc26ee8eb84ba1b477f1deeee1afd75b1ee1f512f567a1f578a2f28da9b4df850348ee98cd71f1dc87fe6e2585a1b769aee6ee9f6c4eeced15fa7c217cc379a4a7777edf5b5f7bed7e9e2d75be6bf5e2dcd33d7bffd48dea8673a6ff65aa539cfdbb5349f9e4a77c7d05f477537abbf66a2bb7176a4dd87b29ecb349ce40cf105c3878ae1e6b5092fb609ed384e9bdc9d7746c7ee5ea554ddede9e084072210010f2d385630050d0962c06469082970a2dd4067093063d14c4c127ca4bc806301b3e0841749231251b4f8c8d04c0047ca0b3a0a00a6484cc44307254f065001161dac740181975e419326b04ead3481062f2f3010c07be23d71f2442573c411318a523dbcb4b724e5799e7baaee974aa55279d775dd53a784c53b7f7961a2a4f31e885071151328cf5739e52bdf21b54aad98f440048c65c553aa98a795cc1226b0169995130153e279b712f3c4a252a55229554aa552a952a9f64ed575de7e81974e79312f2fdd2b999ba13c57f1952a66490ed8932793612e324b98187530961619252aef5864963081adc8ac545454324b52b0a7f68679ced27956b074774477849327542cd1a1f3235432542cd10156d42b28f202c30058523d4c9804c0f39407a0739533a1828993272eb4726af1950780c55d64981ce1422b19260150711799169900b0c83c71a100a8c8ac6452de1e0095aebec0e0e44507173e95fbca3b2096548f4b8e2fd50313e3f94bd70979ae22d3a4855e5e9ce565a545e52d9e2ac649cc102cde792c304e044bcc113a08a5bc25c6c9934e28e547e820a452398c3ba55e626060bcc5c59f74422d4e5e589cc40cd1e25d8b773e848b7b1fcc0e20317ce529860c06729eafc8602097721d5460625ac825c6898b3f71a11657897102e347b8508b7730324d649c08604618618411c6100148798b4c939493272e04e3a91840a41c26668800a4dc45a689ab62f810c329e59eefe0b5c43ce9845a9c25e64927c4e22e314e8ed041a8c59f74422e318a76f8a072180722e52f45881072f1ce8768f198215cbc6ba1969823a858a2036cc53b21188cf8967418b01599270ce43cc7404ee52a4fedf041e59e3b2d79aeb2a4f28eb1945aad54ac980105d6f9cb8b0f283e98681658a752f980e2a3091f4eacae5881076595c5ca8a273e25ab299dafa4a47a5651ba2a60ed4a565ab0acbc98951521788192aa421707504213584b8bb7976a15d84affb474de8a8aaa8aae59e998c0e7f9aa8a5e55d1dede2c3d5aa47a3a6fafdb57de15ad9838800b6eaca0083ba8ac56562cab9655d46a35c347c7117c80a2be950643a5c17822072a53a84489a212250b1e19402b15169528291aef894e254a2a8a2a0acc9bf24411583c99af0b2ebe1b4ef42bc5e2e2dd2f5a1875322c322a322b1925344051d1a2f36e389103161498e7513005a83105f632a3c7a77d52322b2b3e48f83a588a072095566142a5b35061a2bd304ab1b07c5f8e1c1e0b5014996e8aaac614180c58807944909931658b4fc99745162a4e64015332c308308fc5091616161616161b5e7c91f379dd1351409082adc828a12bce92ea21619e121bcc731894043105e6c92849b9d1113e1516cff35858603aef1727bc8be2e29d7720e852dd132b2f4c8c68013362042646c98b141729b0979718c05ea6646124c4e351e960462ad0333cdd13de6a05c5091696186f45c55351716f46a55fb2905284cf731edecc8b142d609d1b49f5bc4c798941771578a101acf3eea50b58d7ada08a94cc4b165dd3793c7410e1ebdae50b1da8ac40e68b159706c020095fc02009454882141b291c375ebc26de8c124b4b8dee05967bbe038def53d921f57d346ac428f95e60a1e1a918452b1b365636563656366260be26df13311e6c15d3b0af89c7e321b1e14a3e26602927602923de4cea6bd204caf7446a081f1707e86084f079de3529b22103d3244abfe09deaa1d1a203041fcb53fef232e5a58b55674366b5b221b36425a6b778e9624a131b9ef216254bde4cca916881e3860c0f6fe685982f46c9474386f532c586bb4cf9628b2e3c1e2a783c60783c9ecc4a87944fe6f1789e7252b4e5a8ae894a05c946d72a192b7fb9e13860e86ee0f01bee84c36fdc701c381c870c131c5f13d8937e79a91e1c9e92e14a58603362ccf0183770f80d1b4c783c369ed838800d281ecf0e4fa92c5143a58baf89c7c3f2947f4e3c1e1b9ef2efc97700249680a55ea674014bad1cc74a66870f371c873bad9c090ebfe194c36ff80d1926373c87b71263c3bb183cd0e003246a44e99a942b7999f23589f99c7833295fc57c4f0ee0cd78303b525dcc0ecf71780ebf11b3c3071c9ec37778324e383cc70e271d7ec375f88d291f8e181d61acb0582bef182c1b366cd88851e2f98ad35059a9d0304ac908f16652bec211b3fa224593f21b313de57bf235f1c0a7a4a728f99ae8f08f05872bf97278a774c4348e984ed1dc50d25368d8701afe42cc4a8ca21754649ee8f8c0b772d6ca8adbb06123a6c62a8686dba841c35768f80ea91768b058322c4a564f9078028604cc17d3a486074616992c565432562a21292da2a888782135430cdaf36e002b9fe103506149f50c41858b0fb1f219cea2e23276f8b0f219eeb4f219323b78b883b9b8caebe91a40f85652a458a2b8a45c5c6494a854aa18312e2d2a6f51398bca93b2928597e6a164c79422e491f4665437a2dc90a2ba1145d55ecf47d352a3a7f48b0d2d6cac6072b0aa5851d260b0aa6055c1aa825505ab8a172d72d8d0c28616368460638a8d2e6c7861e30b1b60a47404780a39021421e82e05c591d7eb7b824977e340b9cda538e24abb4f964fb4fa27b083be131040778b197f9e2311bb64b3385a4b71d81f9733bd7756eab6708650f79bfc0709d448327241bd5e4188409e9d109820dd5d0f4f7477471d0d08b8a03ec61f1b9bc4cf93de3cc967aa43c4266c85645c12898fb3622d7a40151e503e40c751d444aa5113e946337e173ddb2407eb93d6c9ff27b7f906bc015d50e2df669283ff97566bcda66add47f255e4a494e4606eac16cb64b894d9ecb93b458a6bd58e532673dcc2349a7d71debfd9b0147250262bed0f43b8070501cd3c89bc7597bc1504f47ad1c66f02ba515b133991fc873f99cbeeee42b85f4b73ff9192371bda2a93c98abc0607afcd78246f33ea1d675a4bc7b94c455a2ea1f5e8a1e80823ff83d48814cd27d2e24380164e00b00024dd3ddd892c60c6866766e28b6ef03e941b81891080229d158189fc0326748870a0802bba1f8a7e0a78d20d8ad8eba1001dddbdb404d9dddd1975773c7445dd1d517f0980f225c04937d4dd09c13c9804e4e85696a0422c718efad8bce55c42d538280e2012849c48ffd749a873283caed8a183a0f8568eb35cf28fa1e83863e4ffd3381640000048e20b6cef07f1e3fcd16d769c290d298d566217bfc897c44e124edce400c095abd3f1dfeef4257cc11007748139353579d2fcab75ba010144dd9d8feeeed5ddfd74773e1d9880146c41e7f56173b53ebb9537f4e9f86db8246336571a13a15e3171a4b6f0d5ddf574773cddcdd2ed74773a5d8e0ba7bbe9409a197cc11c2803cc4cc07ffcf05b862919cdcc938d610ad260674213f3169eb98189b76672ff14cbcd62311063fcb75669518d02f8587b3f24b6d0dd4c29182466600bf3bc2d8977869812d19124614d409e60f1a408ac9656fa7ac98ef87f9383b2b19ca1c31cfc6a5b44ee6fafa5555a7b7f9c5827cdfaf46b6979da70a28f23cdd8ffcefb385f42623272904c7210878e5d7ca8895dac73da519cd9c7b988270ecf3bfebd596cc3e4acd867938e33e3796b91ad3b49cf8fa9cd738f8adf8a9ee938efdfde463e18fadbf2bdf4e654fc9c44263b22fb9b6d92d11616afc5d52307475c435aaf3007df1f574bef1771f0e9cdd7668c7b644ae6fcd893c830c5f7976447acdb6a0be73bcd7a4d6eac36cc33a4f9b193d85f0ebe0d1d53d186498a8894865eafd8383eee4812d98b367cc98ed0dbaab1b5d2f143d15ab5163b8639288e4f692dc73e8f1c2be584c4cf74466df62ed5a890b54eb06c93c41f4bf16d6153b6493649ac439810c10449cb644e19c02407f1f1eb355f2f0208c184041d180417dd313ac8706cfc9abf26f35aa5e33843f128a6822064318fff92fe336f4677e7facbf500c7c4cfcdf13e9495656b6f4c365dfc9f81f8b17bb5ad794b185803dfc1bfd15c1ffba44fd7e21c97b87e7d6c6da1dfd2fee020be9324a78bf6928e030e79e25ec8414cfa4d074af4379a41f0e1a4f44c8263a2fbf1cc766cc201000704facb63aa155f08d7f2172e050506540da07283b97fcac165e2d19339a7dc0791f9db72ff94fba7298b552bbbf3c7d071fe9ae723076de19d8f3d7767a5334c1d873d93ff143e94e8349bfb27dce32bbdddcf8f71ee9f447ced53dbcfbc56e9d34c3da4375b6df85eab34373a39e820d4e73e883892a54d4afa3093eecef6ced4d01d73bdc0779ca1ed1ebb9340e2ca70409c9ab1da4ba1ea57a094c0dfaffdc5a4c4c454e431313139fa5b3283253258126589146d8bb56cfc25feb5c1b171669bbd15fb9638f11f3f7e74774696dc74777777448e80d400092553f163b7308ab42412d207b245139b717fe5a86ff9cb6d5ea92711b3511608abc3d2e80665b16a659882f396f396b306a9341dd6f2666fd9fb41584ca1ee8b95cedbdf2e2829cb98154b1c133f081acefa0a188da0a452dc93938343710edf5bf4a454c4f437dbf4d52639750c88c20451bac55fe2639ccd5cde6ed4f1c479a69446457b1b27bdd969d44b4ba1fe924eb34edaec79baf899463dae4be2f338a3185bd27363c5b73bfda1ec6cfadbb2c5fe77fad18b364988e8e97572b1ced76b762bef5057c4a7040736cffb99e2796f3995b4bab9682d50279d4d77776077e7c61b72667ca319e7fe29f74fb1afdd92c4170c31bdd98498dea2faab6581bfc498dad8c2cfb94cef246d3ccfeb8fe78fa4fb10ff7ead466f7e288aebd35ef4599be2ab4dd1ed2f9c5aebab3df5e339fbeb5194e635fe35fe144e9bbf9488949c72998e34975f6d86bfc447ea36c2d16c1f4de7e9cdb7f937637f7af36bd1bdd03e3496ba5b092f30f83e48c7fa6674326866ba27b16c7cc1d12607ca26bcb82949e9c8692869480f312330247ac58f1511750cb8754c646c6d4a544b9904e92fec86591ebc5529cd6b4973c77d759bd4e1c4f9b6e9b6e6657753429ed9c8cca7bbbfb9859e5d8cee0ea6bb7be95aba3b96ee6ea5bb53e9ee565daabbf3babb2e8ceeee427773a1bbb7d0dd5ae8ee2c743716aed0dd56a08214ba3b0addfd457743c109dded457737a1bb99d0dd4be86e552b01ca65334f310615cb8f65dd46b22d1afc5bd62ab54343b22ada86279038df47c6a3f1056d669ea2a5d523287b7b26518425e11ef749dd1d902152be185077a5b3361d3fdd79b385f5675ecbeba5cdb90f270ff2a8798be568ff6e05ddcd437f429e747717e3c499d6ee1c7d2cf15febe4bcc568e0533867d773e3e749c5bfdda84ce6b84eff9b0ddde6db04e2600eff5e1b2691454da4d7eb8857fca3ffec45ea0e663c8538e84ee3ad5ca6b5bcdd6c38237e14a5790be31e88b54e1567eb531d426ff797b24dc2e2519e34b418c36c92f84cd9267d4862488cba3b7fce7377a3e03b12f51d39728408898eef4817dd31a2e7c6b1a41db1a2bbdd7337bbb53cc4be23ddfec2f7f3dfe69da4d72ac5d971b434d1ebff6ca9bbb15042370e8068d28dc74973fd72c963afffb8fc330f122dbdbf742b6fb4522420cf78fee8f98695de00882fdbdcf4f7831661b5e2acdaea18e9071e9e83dbfb41545bf105f3e3d8fd6ca17870c09b9a5a4d2eaffafb4186112eba06cfd3dd715ff1ed319e4fa11532f324baf5c5a3596ce649f48ac592948e9a988a88c48a788031293935c562af1efd19d1a17b697ca817425aa7af4d929c6f7bdd8acce0662f10914eb20ec1d97184e10b3e912d1ce3498a54ec6e9afe887c20f74f9efba7588efa4a040922b6f697e7e934addc3fcda41ad0dd627f43a2d0ddb1211fc09fc5587e2c563b8be5c2fc612cd7cf95c61e6afe882f88efd37be7cce989ee96d19f53029c8cf89c68e82d7f61eaad4cc5bf43ff349d6c781ae5321d67be1649a47309ff03b97548d3482710929cd709ffd3747a7c5fcc894f73e2ccf59bc4873121f9408398f8658de5b19cb417c36b691867b13e5eba80d338cea1217fcb914e2062fda69102f97136eb901f67f428db24a25c266f45d7dac23a9f29db5ae71016a7904d12715648b649f991b24dc2ff40b24dc243ea51b78a0b41c0502a4229b7581f2b11515a6addc035d70a71eaf657185513840341a004510a62344368875417562c9b98988a20651ab53511dd422343b7d0c85254d1bc256cde12f64dd70ec1f087758e53c8d2ea11799b19ff8d3a3559d1dd2730417797a0bbabc0f3cea5efa84677471d1439785105130c50c0457777385abe5c022159c041777b33fac34601090a60427777525020034b2f9c208b9eeeee7448c01125284844c08aeeee0cd0041a68ee0a6803dded0929d2810b03f430c015ba3b654309294f60801356e4a0bb551cb00030ba1dbcbab04377775b546109265b6000127aba5bf500d94a036c33505284eeee763073618104014b86d0ddb159698be1dc07694af130a2e96e128ca0bb69d65ed2df96b31db1fbfcbb92417727f5c7c314f733b5b93644000f4bdd45f0d01f102d309030babbbd3a532800f1e71f5f3c52b9408f1a952260adda8bcb7fb34d0ee25989dd16d6798554a050b20c43ebb498f8b8543932438a46afc4d5035ae50415a6403313c314ccfd53ac56694c2a8c21ad2aeac175abba97ac08c9a5cdd42df6d68bb68a9f71d9437797dd9d7177df253dc537d445b70debe33e69024353543a56ea3f2362826fc809bc9686a4830d752fe9dc7d281b624a741fe9161a79da6caa427717f52714a4e673584b5b2b2c08fab5b944deaed18f24bd4e389b447b2657ce10eb248a43e2389bf7eb5842d5235cab54acbf24fea5f889f050c6d6e8e9cedbfd3c8b90b76b84a7906cc7a6d00851add23aa17e7ca6cf3337bd68871e4fdbd2382d650aeb2c313e1a67b64ef5858c230c3f15ab15ca8539ea93f0b5e28fb3892911908c5f489c6f142e415a7223ca780a997912353511e1dc0711c5f946363c8d443b542d2582fb21f168884889722ded90cd53a4629d432fce5b238730653c85bc482f7923126d781a8df8823636363c6db57cbd7e48a0bb3d4f2fefb5434e353536f87e6dd679cb346ac34036f8b0daa41a9091c617cce5ad53752f3c086249045abe3f1f241f346830749bd4ddb55ba5244a4d77d70326b8a0bf97159dab9346a3a24f7fa86913ff6b2b77eded6f6e739d767c1b626acba5ffacce1000ad6e4f004f394ff131b5e169f3176d9db22fc749e4f57ab1168eddfd04d3dd4a50b0c30f13f88264f964edd84d001da80ea903e22fe9a2b5e5bf750cf0527d4377e783ee46d29fcf07ba451bc65eb495dedc5df9f504a1f1057337d7b721e7ad878951b514dbf034ba7d3ecadd5c8b7e0c6b23d1dd44fae3a941e30bd6e0253e8cc0905e2f1d9ed2df96ab8f638fc7392e6dd907fe1bcd9efbdbc49f2d947b9da1586de8a4b5e3dbb28f6c8984f533a651ffb9ce3bcb9a635ae3b63aa6ee4284c068d66fa592d72ba4349a71d9c324c53a2f0ff3e6a8a2bf659eb5469bf05f518495af5748c5af43c227712c2a3bd55e2f5c2dc5f573d4bf5e9748f8645394d353f8b6a1ee4e7577add2a9d12de65061b4612c478beec6239df9c5d83fcda5bf9c2bba2dade64cd1dd1f565b63134a0ed9dd38f74ff871ec8df4fda76b71311acde6007d9e393c1ae7b034cef5efdb2afd5c5f747f2e2d3a961b4972bab298798a33ffcd1525083aba9ce8ee773de96abbfba53f576eefbb1b00fdb954dd36b4a1bd35b3d25633b3c11f8e17dd2dc6b21df187e3031c2b669ea2e792acd4c529d4dd33dd0d04ce52e7802e1cf0a6a6e2ec9dd9f0b4e1b4b2dbf0348a92c513a02084ee2174b791fe6e32bee04c7c7ab38e0bbca99989354f6f0ebb7bd63d652d3ecede6b67229d4bff40aaa520c6240d2779a3343a92b7f938cfe5cc5b329975f1f19c39792b673e3d7fedadd8b24d0eca489a6d768bc7e9f8e94892d3efd3b7e1d21fe71f568a3fdb2497c964ee832607f7719f22fd03711c3c07ffa7c5aaa578c67f3849732cffe88386a479261c6718fbe19e7fbc1688dbd8f85f5b6bb495717965329983feb64cd22c966358a9e766dea22169765b673c088a6b72e39d23bdd5f2d6e46a2f6d9cb73b96e138c33996b5bff5ab2569f6298a739cb69967d66d76d93f2d36ce4a69b291a4792c6f8ea9cb647e29b676e636bb0c49b38fb3cc9742c9f8e792bcd9d06d762c1b67b5b2fb7424331dcb9bffd31ce62d24b1a916e5fa4db97e9378278e9c425368a41308c61f8665138891868c54eab54a5d7cc7d4f3ffa58d74424dcf515fa7e31f67a515dde211e6a04c265e1bfef5fb50ef5826f349a15c7ccf8de32cebbce5de967da4b9964ece5b6e66ecf86b6239738f9a37175ffc0fa31e87f4366b4ec4a3bdb9dad0cb10579883f5a168cda7579aeb63ff995b3a71d331f5bfd4bfbcd931f5d1da19f65ca6d706ad83160d4e211dc429a4c312af57f850af9798718cc4b1980e3382aae86e30084a502b48f5234a83b5fab61f4bd67f7c0dd61c280a0ea8493708b4046226a019e7017b6b2688c65be21150f8447c087961e1658157f8d2c9cd7e2693f9db68f386393b922f7aeb435a2393c968d69264e90465bda956a9f8b88c8947f4e7899fa56e104fd6cfc4d52af559818fadbb1bd31e2931e871d2f3430f13abe78507073c50f00cd16055f2970c97fe92c9bce685c17f9ab95afab2282b140b0a3af29613d07da0d7eb3e5962dcd79dbf95799c463bb21d169d0fe8cc747254eab42350ac3683e2b561387d12c9a1400e92eb0a2e20b05c4f5c372c1c2dc04996365727a633cfdbfddc18ce4bdeec13c4d9fa443d3fa6b33b3d57a7380b6b0ebbcd5308870038b73b37cb4d245026f3d70bc7c19b2fdae9064a93df88372a9b28dd4d238a778a75665c536dc5ff361b1274b2b9692a638162a53827f0896e10fce1e2681d8bf36d31284bc6a2e62d890fab268a0691868cd4b2268348434658353ed478d7b08098d592d2b97ed87aa2bb59ada8a62d23ddcd6add7483ac16cbeb15c37dcd2106363c8d8280eadf2cd2ebe584b3b5093804010701e0d083834723051a0ad0dc6872b068522028cb8f634ef34f33bf5e32a7d919c7b9d1fc4c96bfa92ac9665c96ebdfb7d523994c969bf96cdaea24dfc5a020f1c85b4dde32b2e1991494e42d2320b2493cb2f9662fedffd258333af4c0d88e9ecb96e77286c3d686eb5145370863f518afa550d3f6334c6f3d6ab8410a99cb2076d60d509a7503c9bae106a4b981c55a1b5600d2d83ce34d36900dd2e07f1a9e381b6e680dca612c1e5df0906276770ecde2413a8f1daccd3c3e9b1abae8b66e6dae8104dd357f492111f775b476f67a39fd5f5a0cf7955583901abc41272525a621f0c3d910191a315160c598208c81f11fb05c0907721c581e030d5268d8000d4d34c898e1083348608624ac1966bac19b1091dbea16573a2a794d9d38fc742dce6332b196e3ccb8246292c5705f63d5be5e55c9b1d7b883a2acce3026f31af71713587a541d670df84d4e33bd612e67b7b276670d78ad4cd6ea71c52a93210b960c0e60c96000197cc01ac65bb681a0bfc05c27ce4b9bb3e1e7077c9a498ef8db6298410c0760c5c0c30e2dec08c10e280dca1c531ad98ce3ec78ade38ec0dc48da4b92d36964329ac1ff0b44e6f7f38e1d9c0003126080d260a5e3146793d780b925c3d373630e6754114b4716744ca123081d3c72e0208710399a72d490e3051c2dc0f184e305d60d14dc60d25d833b98f1a5b8c77d7c9de6cb9b67dc6d6e6592e6f138693378da683e8e3326abf3476b6b40100cf37bab85ef43d919f9976cd1279f797472f0672d1ad99737cbbc3ed4fd8ffd389bfe97e6fafeb68cabc533c4f4f67f691f84bdb4d83f8dd231160b32028ab242a5bd41e251abc9a5e5add72b08287c2231fcd942d9c8d138a90618a093d4867b34ab46160d8a49359ec8fd538da56e9b710f283ae63ec80b1e7801891792582fccd0d88245230a8b0665d1b8e96e39cde2fc1894e1dc3f35b19260b168ac1c581eebd300eb1b00ebc3990184190360cd18c29a5143cb5ee20b7fa6b7c88cc86b642d99cc5b329a9278449d64fec2e27ca61991d780e247b92da43358a4b1dc3f0579bd9c600ee2277395ba8c0667c86b409a8c9de6c7d1de89cb19273a72fab3f069b4bf78c6699c363c6d96568f5832a090f32fd932965832bcbbbbc26280d13d7130560c29dddd558915e34983219e369186b46c62c510028a306038c13cd1b55bb2608658302cdd0dcaaebd76a8e8592f5bb05e9800731f2428f74fe34867f7975eb461d007010a9f880e4f89bf2643b35ebcbb592e5e7477a6b767b904e1d262b5dc320c71ee9f6625b6962957c471c001071cc03108ab250956cb0fabe5068b458a5b116c160b6ce5092b5bacec80b50202d6ca05582b43582b2d2c952f1afc9b8b3898bb0f454151463334a324c3f11a90e6a128ae8ea9ecf5b2344662d98ccb689a669464feb219c76b64b13a4399d3a2644d32996c4826a369f21c7c08f72a597c134ba5493748ceb739eee139d8ec8b34a9dcd0602c9566ad9220a3c1995192790df84f13e9f1b42d3d9e36b208cf3c451ce2798b5e2fa720f1c829268e96a5028346bdc43e72e2274172df8a253e82c4dfe6c45265d1dd479038f8642d8ca58280b5e1a2acd038ce30c80988a624443caaacd411ac9465a562ac144c83373737ad1b1ccf619ca10c8ade178b780d4833e33d617948ba3b48b33c16abbba2c1fb8e6bc09c2d675c65d95a26564759dd0fdd9da3591d8cd5bd58dd0dabb9c06a2cb09a0241405e032844e45f95bc06f41a90066746c96b8280bc063016f39f81b97fb2624923c37d95cdb89873ae6fcb252c97a40d4fa32027a0f08944bd5893dcb892b872dc874f79240c2b9a67c765740b8d28bd5e3e65d0511740d0855817589220f1161764c0050770414848f3bcd95cceca85d5166ab00527792c69be5e3b9bf14c6936d70adb024c0b5e34388e33b4657ab540012d0801f1b469e1862c6ca19320c9e5c4d1ec09b200812c3490feb0b004168c6081058c289d048987d35b792c33a660e0c0e8810a9d0489131db58e20016f69dde69bc513376db02b58e00a4a5bb0c21456f8c10a2af067b599bd647da8fbb9ce7a1fcad61cc941f173b8b4f76b758ab9b43693e2fc3b03e6c62a40008bf5ab70a40a3a508109546022091217a9b74aeb62eedaf2b5de127dd62a9d930a45a8b0432bd7875141c614a62441d23a820424e7cd4d53809a82638a338581b95a9bf878daa4a0032970400a46a4a06a45bd586534de128fbc25c350d3568a3e64e33887643ec5281c210a516614a2a2e0499078c6363c8204c4b8744cdae9c5174f741224ad49fa1124208dec161a11eb37c93ed3ec4c9df81fc8173d7c6103052040c149771224ad2348c06ac969f3c5b96b674b8aa0a004059b2768d1dd4990b444ea479080b6c6fe696699db329da23fa1c9dfae15e9249f60e4ce27e8e0842eba3b37716e6b0b4f9cf8d709517a38e109e80423272fbee8ee9abf9f1752745399ccbd30824b2ca43f2f724dc802acf3e6d6e62690ddf771297e137280d53aa638cfd18ce39cf18ab34cf8e263c213200dd29011f192514f67eadb9c9860c4614c98691033a1bbc16b69df12a6e8bfa4d312c825e4e806c3493e8573f489c969bbbfe460a6a9559a4b7bade8164fdcd78510fa671f024505058de31c7abd8270aec9f17427a0a0209a19a05b682408c8955366fc62a6372aa44850105094154a420a92408238379f308c8b237041002e2e17316cc1822d48b085f86d71d3dd200e7fb318071fd5d2fc636ec413f738645b71c0742c6930f0c579b3f569e6b7654cab7dc799ca6449606e1ce7b53f8a3f93d5f04d462b0c6c796b9c75fa238107ddadd41f128e400214127834f838cf8dd966c74843463c57e79db5bf8e4bcfddaf553bbaad4c4798c1082830021423f818e185292b98e3385be2e37f1a883464645e4b73b1ce293c7c536028c215dd0dfe389b78969b3799d78034a5633a73fdd0de66484346666813371561a9083014a12382143e223c016297790df822a632af01b3b54c484346ae2d8fa3cd5145dded437f43c0620829e8eea6fe86f034842042982204187c427821cc08c214419805018820a8803045773bf5070423403002841ad316de92441a3292abd36b40fc4f73cf4df2f6b906cc4d2407ff69af1ad09553a7fbcfa21ce62f99cc6bc0191c2332190d0d08ce786bc6654436ce9092b9ece3488b38e82f59c62fcabc06acd369969c2192bf6432af019f30bd9e67c55426cb4db2565ab2f496ecf699c6fe693297e117e7cd31fd0702fadfac8c1f406025a705994952862f8863f7499aff4631cd13df70dedc872c6ade783890d344cd1b0f9b253323fdd06d7c898c8820dcd7eea6bb9b460fe9af07adeeae01f716c9bc26e184133d98d2831d249144125d13dddd023a05f4d703285f0f9c7409e8ee9680f901716ac2871ac779c33e1d149f1f4da4532289ae0b1e20d932bdd3069376bed18f16cf9472588fc4f94614e999c4af4376f0c40e9ad8410076207320b77907383b681d2821053f82fc679ea75f4bc3e27c5b1e6b9562505623cb14cb6e5ee99228a3998991cd3b4999bf18ded2fe131d1d3f592b040748a444e94e724c1d07413870e140d5ed18cafd153e89b3942d5ed0dd529ab494212df6982f937d8dbf2cad5207f1ed6dd95aa68ccb698b2aaa411360d4d764b0821e6840051a7c410329edafbf13f7491ee336bbd3e0043470c269403483191c3991ff41ac13bdb9bed76923af759b739d19579bcb19dac2f2e678e646f246718de3cfb452da8bb776c9fb3f4e30ac4ad33159b6c671d22772d065b8da71da649e1b6b73a4f9ad14ffbe5eb970ded0de5e74f0413a83dd268f9ec3bf54f44b33f5bfd571daa683e374d81251128c8907eaa0789568b472e63007f1cfc87933cef69225aeef3657fba2638aa9adde926199e34c6d2e93613c2a89b630cf1ba648d95a269bbf46bb14248909c892ef8a1add1d23dafc820ba48098b495ce4be481138ca05b88e8cf0533ba5b5caab558acb3da9048ce2b6ce8a829c9e8ef520b6eba053558f045fbcb5bb15c397bbdf090e6800ac49d4f250537b7306a0541e8f617c65854c11562b14c6fb312dfafb8582c3ffe544044b32a90f114249182dca0db9bd76a93121f0a42802f78c313174edafd5c690d696ffda7996768f5033a1e6a1401d28d53a43f2bace8c1016f6ab2b577d60dc104051e0a4ef001217273b4fd2d92010f035d100d3eb663e851197f59f3222d2ee0df793d4f202d2e20cef476b3a1ed5a5aa5f938e984ea007043d7483f2f30c1075f096810f5b91ee9a4655b8b80d0450954dd36aa83cadd920e7777b7bb5bd2dd59b2bbb3dd46474220d103d338d220b84f125fb44324e88204582cf9ec34354dcf4ff3e636bf9d4dc7d9fa84e73339183e8973f93e692fe9323e3b18cb847324ffbacf4e53938b8f5f047b3c0f3e2302ffc942558a67342b7ec62f8e76e6e648429e423b8218bff00deea08df4b7c9fe665b9d469d66c61f8f13888332574e2993d527c969f3bfcd2acbd88efeb3ead886b4d2a88ca6f57a39cdc86cf8efe7a7d1ec0dd33a6de1dfbc56297ea81fe918d6645b8d8e8e71761c674833a6e308037a9a9d34363c8d5e2fa7bf331e52d2669f480e664c4797053901c96ee2fc5beddf2c5642b335b9b46e6b9e6d8db2420ed654ea93885da255ea8f29ae5ab1963627ec608bb4e169e4a04cd6e4e0cc69669ea2cdb80726e76dc61dc413a955b3d973498e93067fcecdbcd219776c9d1c243a0ae91cc792769b49e1934d47d6098bff49a175bad921a26a93c671ae64199061c3d3f6e52d6b94e62fda241d5081022a3840c51454384185013e2a9c7c540841c5122ac8ee26ed8cdb388d0ddeb2c15fa4f5960d33ee2f5baed51f3dc9918f8a231f15b06e7fd1cccc571193a5d5fbf78388555b67480145777fb5441866c5b229f741442a964d53ec4c01d3dd562c9b66cc8aa5154bbc8102745bb1c4325166c5b289095f500363fb2b776d9e629db7fbf781a836a9c88dcb95839b4ce251d48b755ab1c431298c6051c791d26224c6b9b42fdafc388ad2fe92b171a44fb1a73b6f7f61ac0d0ca1591bd842b3a6f8c1d44d43f70d8de387a5811acdd2400ecdd2c010cdd24015cdd2800f9ab581ae591be8d1ac0d0069d60692346b034e9ab50114c0d460e2447f4ca2e88f8908fa63c282fe9810a13f2660f4c7a40bfd0d41e3c60b302edd1f1227fa43c281fe9060d11f122dfa43e2457f48c0e82f094b7f4968f49744477f49685a585668c0f470eaaf47d85f8fb2bf1e42f4d78300dd0df3c20d1b28b18111fdd96089fe6c10457f36584177c3d080a1e58a03f4778513fd5db181feae40417f57cca0bf2bb8e8ef0a29f4778516fac3c2a53f2c64a801f3c528a2bf1848f4172302fdc5c8407f3144d05f8c16f4172307dd3d03070d1b0bc8a2bf05f8a0bf051ca1bf0560a1bf26bcfe9aa8d15f133dfa6b024805071e417f5805fd612cfac339e80f2fa1bb615e808103b93f0e34e98f034af4c7010af4c70115f4c7811f74f70a0646864b8cd58b14fd2951d19f5209fa5372417f4a3de84fe908fd2979d19f1218fd2db5f4b784a3bf2599fe965afd2dfdf4b704a4bf25a7fe967ee86f69497f4b02e86fc900fd2d35f1020dd63783c502b35b40608527b59712dc4d77370b18432b90428d293360a102336872de58905001ef6e97e3e0e40c63e268c919f6024c70644401227ae6107111113f07f8455b670004d60c823476cadfc492811332a08901ad1dd0442744add2274e96fc8562c1c08a2e8802909ff113ceefa2d3934a36a97bf0ce6b69ad96969616973a8572f9d22f1dc67a010f09b0b01059578c50b8a009dd3608e1029deee6b1b3440b0ed0b1264c8460c194ee9e194f24c487010b843ce9c6ac15dc74770d20000f1538e98e52814fb78dee24cd4ac10152b0848e122bbeac138cac13f8b04cc0a49b67c765714eff34472172c838bfe92ecdfbf316edecececececece8e8e8e8e8e8e8e8e8e8e4e4e4e4e4e4e4e4e4e4b85c2e97cbe572b95c3838383838383838383837ae1bd78debc675e3ba71ddb86e5c37ae1bd7cfcfcfcfcfcfcfcfcf8f8f8f8f8f8f8f8f8f8f4f4f4f4f4f4f4f4f4f4f0f0f0f0f0f0f0f0f0f0fcfcecececececececece8e8e8e8e8e8e8e8e8e8e4e4e4e4e4e4e4e4e4e4e8ecbe572b95c2e97cb85838383838383837373737373737373737383f3f3f3f3f3f3f3f3f3f3e3e3e3e3e3e3e3e3e3e3d3d3d3d3d3d3d3d3d3d3c3c3c3c3c3c3c3c3c3c3b3b3b3b3b3b3b3b3b3b3a3a3a3a3a3a3a3a3a3a393939393939393939393e372b95c2e97cbe572e1e0e0e0e0e0e0e0e0dce0fcf8f4f0ece8e4b870906e47fd4314273defdb847488bd0f444ec8263d2512c7d21641c2d9f185863e087b810ce9f686b86fcb4ade00986c1276d2dd9e00ba3d23987253884deaf68ae8f69a747b2511dd138b615191df217c6d5285dd320c6d8551284a349f8ab3899dea077181885f7193c43ece6e6f88af36c963e20591ebfeecb5a499cd7af4b7cc25ed9679e6b67ce9cca757b04e1f4b3c7f364718883fdb8a7dd428d6004cb717d5ede1ba3da86665600bafccdde32c97242902e2a829465444a434945414635262622a8a15091942a4841433024382d57fb24e3e65d0edf351cd8ee2d1575aab4543522ca09b66a6bbb1186f270506a2687cc1dc2c86f124655e2cd759e98dde1053d16f657d7cc313e7d6a85669be760673f031d0f27e0075e36ecf87f7fae96e22f1afbdb7689c7902b9c00e929caf57cc6632774357cec462f8e2ff4da2b3c3a313c511ec88b31d9b7478ca288474c74872be5e3112ffe0ececb82eaec0113e2e9c212c11a0c014dd15c8f17ac548dc6d9b0505129ace7c5c38317c9f1cdfe6ca297d5c3825144b9486babd1b9beef1dea2d08e23ec0916d4709f50a962604dc089989513ab034c004737757b602bc792c01311f8a29b26019d49dae4b257e3b53093d71ebb2dc35eaf0810018125b02050c40153da01483c3b2e2307b4fa024d8ed02cde802c1a904577d4cd11ac0608c117cc955a32d6009a201b9e4640359c9be38b1567c5d1da30f6d716bb95358a014eba7bc905f57a351145133a4de4d8b18029f00529396358f470e5940bc0d134b3a39058740b8d7c9e61b543afd70f5180c584aabb3d1b14f084029050800dc8fd259379c6938412272b0153580990352b01deedd9d01e8f1ada45a4c27088e03e89b584534349021427ba1bccf86bb5123f99cb78da926e62373117919bd84d0c87087ea67184859822fd0351f151428aeefe5be6f93497e15ac6a09e9ede9adc3ff5e0803ae04d4dcd8e32dd5e4cb7e7cd52a28947432bb1a4bb2dad1ee5469284d52abdb929879374e59441b1fb64d4bc0145cddbe3390b72e5944051f3364e270424717f091f40eca63e64b16b63321ba695d25c085f107b3bbabb83a1bbdbbb6183568f90727309ff0f36499c7f716e2efdd3623837498ce7181a80e7ff02c1f59f6c2e400ebee0385f48f86810047be41ff1f850aff1a17e7c757b2cefc317b4e169b3e1697b12d59dafb561b736bf1edf87ea9e2735f005451f2c0244d18d2f5863f7338ddd9ec436e14311e0c90708c0eaeefa65a5370f72b1be6dde3c2b31eb0802b006c02212246614140434ce5c6d50ecf57a7a738cc45fc3b799a7e8ca29938c8c8840133d29b972ca39f2e094a3b5b3181e83688078ab49fd201a1146e3ca29c325b7d8929bc822a224a285150024c4f94ce2d8f441d0a67a44deaed1501179bb46dd5d185d17b8d01d7bbd624d4eb0c00b42e0810e145802173190e20740476ca810c122021796086eb0849f2008af4e6104310c000955d8306300421a100325866b01110841086aa490a3891c36e0410688029003a702fc20824a16b2c062e22c21a2785610527458e8eec0e8aad05da1bbb34215583926f882232b67c48a12022baa8a2ea2766828b070594c294421c7c2dd4069d1dd31b8fba2bb83c21358504d5ab002f8144e7bfd6736e90656b9430d981f8fb328138068e6491413eb7f6d2eb130043a27d41a1de70741712e6d6e0ad1a1fb8b26b25eb9adb4aa553fba5e79a21357ac6ea56774ab1b9d8c8ec5a5eae91413aa19563c1696952ad5758dc303dbebce07950e5daff858f50c95aebbae5bad74bc17afbbae4b79465db7d2795d975209c2ebbc6ec5bbc1d4752add4a7b5d0c30e5a9a8ccb878a7f23c15944ed5759e4b5705af6be955a7f252df754abcaef35886783bbcce5351795d13bcae53f13a1f3a1c5d97f2585cb04aadc8e86e745dd7793f9e0d96541781ceeb54bcaef352b1ce53b9b4e878305d104bb7e291fad1ad789e0a27e5e2791dcd0c9e4e0815ce0b90d492d7aaf62cd0a95233aa95d7a57ab580ee085d7bfd7d5f77dda93aaf7b42c7e2e1d075decaf3525d97d3d9742c1d8ce7799eca50d7c3f3545e7793025f74b0dcd0c5a00183c7806ee5e9e8545daa6b6fc973974ed51e556997750848758c0bbadee1055eaa53e9542b5d8c2ee584e7b1bc529d4faaf33c957b302d33ddaaeb566e543834dc749e8b0a8bd7755db7d275afcef3829052e95a3c550aaa93f1caae4bd5742d2f5ec7b2d2793b783378337832a43ad5dd8c4ec5f3ba18aa2fa5f274789ed7a9742a5ad7c2a37b5979ddaa93d1d3755dd7edf08e565e5abc6ec602b3e2753e742c5d97f23a5567d4c9e0fd7831782fde8ab7ea529dd779ddab93c17bf156545d6a95eaba9e2ed5cd742d1d4cd7753eba193c97d44ac7a2eabcae7b759dca0daf46a7f2ba95aafbf15eba943743974a799eca53753f9ed7c1e0b9742d1d8bcaebbaeed5c9e0b9aca43a158baaebba1eef72e0c00294ca61d5a5ba964e95a36b6979bd1cd931435783aaf33c156fe5bbcfc6cd8b51d7799dd7755d4daf34d94010d17df1c313da7f80a20b293fc46ce1459179040e0c31c294768a0142374181836f9a37682628ac8800538c55f2010a255f2c4d9d1d844031c3148c660c373f50e4b0f9993236d06384183c7a625c3a1e23745ce0918521011eef4283322dcc502393c26b4db9806df90d0e393cd182020d3287a1c171d0e8275e48d2b2d512ed2a0174e745507717b151e00baa3871b161064d565a565a54325878565a5232c038159124c332a3e2c2e2a38299d1a96054622a5c2a468aa5d242000e68a222c35b6951b562c8e0c381262e446044554b46119ece8ed51055a702d482263f8410b104e0a95c544268aed0240946e020a5886ef562d1b043119e0a86540dd9c504ad72e0411315151a969cd898213480140f4510a0898e96223035606a0401c406c9825dacd00428b7f0204bc0b7d2928ab17a52217180225854325464e866e414a1040ab4e0d003272c60304086f7b2da81b5f2a30af247a218f1c26ac60588cb0d2a485438acfc18510228783042034adcf86e9cc4088514e08a262f25ed010288e19ad1118921e56345074f112e2b3f2a2e2c32523d473ca46470016285262e2b47319a588c761401716931826912814ad00487aac5e3a2f27a21868acb4b910c002ed0c485c5688699019aac52dfaa53f141c565e543058720a197b2a5078b918acb8a8e18442d3c54492d386ac470030c377060c92108d30d45b8b0802b3931727a8e965660542b30ac5e299e1618550fdf61a565c54565b30aa2f2d9a15bed48c5ac6edcb4b084a99f221f39585c6eb4f4143194f4a2c332a3e2a1e2a1a2812638c85011ad7258cdd0c24315c30c9c940c4634016dba142d55a34aad849001800252606b0738206505513aa081280a40c41051f889f6a10c00488030426448d392901e8c5e3e393337d85083ab5e80451850f042093a04d92003838e95276a40030c5cc087221d5e32d8807951b9c20a1350210506a2a800144f4460c84c0f1bf0010e706a0a630b549801018527a8206659095082d2827cb8ac48b104949e961f689101295e3f3c386abcb8b0acc4b083042340f2a252520f47408a2490041204a0316507051de08006882842a48727470d150ea44c8148d00f9e9615150f1c61820f50a00905184088094840024a282505f9e801d3692195050f0cd517fe04af0b95123c23aca67440f07ce0f5a08507303bf070e0ddc0b3c2f340aa03de934a49c7a3a3814586550c317478383c1b5d0dd60b2b1a1eabfb5432bc97958bd7c2c2e2adacac3c5597ea3a958e3123b5239582262a0434510951c1c1bb952a14f15da0c96a05b37203108a504181a3c20113c506540ad04425a4e5864b0c3904d0a4e5071510a9195eb20a0c0b10161f1597948f1c2eab193461a9a2490d2445b4f4a42220059822345985d1640503430f9ac06499143469c921c35bf1b1b25931f1c4c68b0e0618a13a40931931336252352c1b961cac308c58e5c0e2b302949a91026289a2490a09c746c545658a26a917151854ad179c54a78251b55c80ac7258f9410506151715253035606ab4e468b9b1f2438a0b46a8c0908a62c42a8b26362851dd3c66830caa17cc0c1596aa4bd5a486544b303854bc95222b3b5692563664742a2fa9950a26954a79a995144baac52506556a65c80a8d194b2eac1595952eb5a2d251d9a83a161da91d45b4d0b0da4013195314c15251ed602132425504236462de617441093fd0020752629045596b5a024a91900e3f7a6c85c2136c7a7c3358568e00a19414860d7690031a94b87182013b38a0083eb8010c5440820e60c006bca189322b816f15a2f0851078b0032c4440440000800408234a493c00a161061c37ac3801111de040008688c91083ad34c828021196dc9a84d1852b5801c9111c37583e80c10b58a002128cc0034a2481440c003f14514a3aba810d5e5003143861140420dc000658a820051788c20003104014bef0010f5490022b3ee0a4c98b4745ac185f70b145116280c5094c10820c2c81801763004072a4c851918f170d2bc60d6c1083083c6009272f02a0872320453e5e3262a89a8b2d6e600319bce00426088107329080249c24e9e1e8878f578f0e0e0d0d96aac3e0620b22c820062fc0e20426f0400612b04412087072845812243ad8805bd082902033640c21062807308091223714408001725154b48491ea82c7859516bc2cb06c40a5012f031e063a978793bae9c0979a540e29bf41831743b7630586948e2e870a4777a3e5051a34529f6a068c8c2f060c303a5e627061b5acb0a8ac782a2aab55aa6b185ce894aef492362fda90d672858640a559f4081a006a44e633ee2377e7b4853ee90d5486d6e896d111d3f80f209ff901e4b62d6c3fe8ceb9591210048f1c19a9c0d02b1f32ab1e36a7fb394fd2e6661babb6059a1a116a59b4e799a7e39a141ef5e2d7a06aa25f4bbbb69a0ebee0b585b7bfd9748b11dd2244f79737777745d8c2864683ee9ad02d41b5eaeeb8e81f1c0ff27bcb10879f1f9f9f9e1f9e9f9d1f9d9f9c1fd70fcecfcdcf8f8f8f4f8f0f8fcf8e8f8e4f8e8fcb07c7e7c6e7a7c7a7a7a787a767a747a727a7c7d583d373d3f3c3e3c3d3c3c3c3b3c3a3c393c3e3e2c1e1b9e1f9d9f1d9e9d9e1d9d9d9d1d9c9d971ede0ecdcecfce8f8e8f4e8f0e8ece8e8e8e4e8b87470746e747e727c727a7278727672747272725c39383937393f2e1f578f8bc7b5e3d271e5b85c2e1cd78deb07c707a7078707670747072707c78583837383f373e373d373c373b373a3739373e3bac1b9b9b989bae9fe8752569b7ca32539e2d29a719919c2877021dc5b2e2d1721dcddbd53427797842d5e88bf7e46f69e68852dc419c864fe7269c964fecab41fdd5d6434ee99f6e38cecc78f1f31f86d619eb7ee8e8b2d58e20298f0568f1e4fe2d8fd128f449a764cbf44126289aa6e1064854c08911056d1340e04e43833ae8110092bbc79aaf4da30ed3bab41777740e8ee7ed0dd6991aa424a099d0f7ac083ee6e07dd9d0ebabb1c74522030ad9855cc3e7fe118be204e8dff00f2eeee06dd9d0dbabb1a74b715cb2626a65b68a405742d1097d7ebf58ae11763f54591da62949c3112e3205710ce910b8ebd5eaf575048c759b9449230a7d78b87e3e9f39630a079cbb92488a6e5ca29677a108f922cad1ecd87a2b5fc4f21cdf89f7684a3e6ed1646899506793297634140e113714101dd4223af570e94f84b6e22111b8cc8cc489243ba00873c316487212cddddb359e32445e38c4927d2e9a61bcc8df4a8594d5a383595a0c9d61487a9cfec38239b72749797298beeae553a521bce614c0f68d02689369c791231adf8a0021f0cc0f281faa083b58405f06f362c5b222c8324cd4b582c5950666d3899e766ad7f9ab764ff34192c6adec46a674a539422a064d4a00c578a24c3592cab7522a518ba671eb2921ec04a024202a31bcc4ddcad89ef79c2c0bf65cd56a3b6f2624a3b0269709e719a459a00921124966e10cb646e73b54243b27146339b79a869a39933a010290de26b673c26563b13a9901023dd58c80e2fe46ba2230ff245102941a4e82051dd418c34ee696ead53901dba3b488ef63af110560f1760f5d0430f99867424a3dec59bad81b979cbd579c44437d8a365b40423278c603c74818718f020458332af196fa11172de642f99cc6b90868c601e7a000246cb4807c2049000b080e4e896d9ece26833ab4803b459453022596ec4719a85e149bb8fb3a3c3405b38494c6400a225d69017b2dbcdce5b9e33d3b014ff96430d18ea612886eea15c5a1b1069c808661222819003849ab0847674cbc0992791ece535e0dbf22d69a4bd22cde232cf58ae8f836c781ad9f0b45d7befc3aa8041b10e8b0261302fbdc276e802c45e613b48d1dd3b2c813bb0e4251dbee80ea9b5a85450eaf6b19e05994268464406000000531100204024188cc60362d980d04bc70d1480026dc270a24c9a49a32c4929648c31841000000000000002323310009ccc1df8388f8ce95cc9ced1e00c421a59ef3b3ac87fd1c1664a8b0b551e97e6b154de76308558c78b81b21cc3591f2193b7d19d21ab51581a20bb78b76b8ac79e080e52e373e312bb9e2614b438eb69e171412471612ab95f6b6fff4ded37549eda44bc55c825d0060f6a1e45f6fa5a8fcedcfce5c3a16b39bb68876ff16def1de1dab527a0cf59f27a3cce88c01240fd77b11f67ea8b6f005247faac5957f1ecb41a1c1209e5e08142b758d9e60a4cce756bdefd87e26b5e771f3031eb9a9053a5fd04647a522505148e6caccb44d3bf78e13dd068cd4ca91237562d165f5134f9b2a76465e13f9c607bf96384a0c641bd251f6d02c9a2eecce869728ccc8ce79f7a1f7fbcfc84eb4ac043637c2650987223a038c5b9f798fe2b8172298f25d09876c4ee51901c5290413a5eeb665b9cd0f48acd260f35fcf27f33ed1e07eb493fe8b1fa3e66bdf4ceca4c9649518dc91929a7e0dd9fcc5e7318c9dcbc16e2d0f8436350fb67cfde8c71bbd762b8e7a763881d188a4c8a55a850fb57273f9a6ee18c17b4dbbd2821ddeedc0866462e35a2dfc73f1c5d616d3549a7e26f1e510d81d541cc0dbcd1b98dea49ba988fa27e43495f2f6da303b533ff0ff0e1f5de1aaf2d39b7706b3027a01e666367bf639f72e6f9d413c3dae6d7d72b341146abed5a3279bdc436635b6681190d5c239e7e2ca5a4c96799371ab27ac44ba6250992584349df3628d62dcd76d1010f7fce09f4293bcce49c4cd226ecab72b0555a77be29bb5bedf4a8d6a95ae8bcbda7a9236cb30fc1b2dcb55a9b71f37296e316d0932419c228e8a2b6eee3cf4fa65ede34f8de854d03353a4265f7aff17bfb6e3cc750163f84d31504858c4d2952d60a73c495b5018255f02cbac7d16195a2ad3c01b0f7d7c8d512163b85d0d8d584c2a2361c06d5e1d8a80ac1565d00d6ea61b052098b9d4268ec6a42e18458fd5278baf161e07aa72d7302a0bdd31165652c3e10b4f3b551a503edbe05b5ebd3802512163d85d4d8e58402a3371d0cd5e1d8a80ac1565d00d6ea61b052098b9d4268ec6a4261511b0e83ea706c548560ab2e006bf53058a984c54e21347635a1b0a80d8741753836aa4262f7c265682dafe1566d1b216d2364c305dbc16b255ba3566d506a28d40963d94b80ba3b3b4381f16bba19a981774395106dd115c0b57a305e751916bb87a2795f0394176b87cb80ba386d5409c1b63a025aa71bd3ab1560bf6b60f4763446d9b236380cad09d7461590dab21390b57b63b64239b63b0e436377d330e926d5142900d5ee51caa942a31a53d1a2c550af4ce4eac340ad7f4dbf78b48f356f3d56ef96ddd48bb5bb414f3ebfdd407547d82d0a2ef0f8be30a9650a31a00028c9431f5eda0575c3b1a8319a297ed3ea6de19f4f1964445098eaef298189c4a3125c2dfaeb9e237eca7b6cf2d07199770dfd1a4665120c54ad235c6d6c8274e47ffe1b1acf47f8154598a7bc8bdaca3545400c37eb5658286effd531cf8f91c5b6b3d44cbe5a004b121cfe614bb2202e8d387740d162422091545db56b6597d1eaff65f55dc61be80a9506f93c5d0e8fa82258fa59bbeb9ca8e2c458d27c0a0d371980384d8dc0b97942e7d5d571bf427f68dbdf7b0b4f67a84c7acd449035a5d40600fac91e37e7b7171a9288f332990c107797f47b788d32dc215caa805b9ea57e25893cc92aa380892a8bf4f450a00be744b80b75f1f2c02112942ed70f32f18e030bbfc1acb3aa5f3a9f883395c6fdbe05cc03ddfc7b65580caf8d8d1bd8603727f554de3bfd93f588b7124262df8d4b1744105e9b8cc75da3d80fbfd15100125ae9ecd436b480281e60a581da58a06c188d0e9c6ddea881401af46cce1a5160682d014304cf18d1ebe1595d29a762d9074e608a7b40d5bf80c47753100e68a72189d55430a0c9447b8d2aa99368f7c90f245cedca76f38a987781ca093f92341ea91e396dc87854f5dfe721183275bd06e8e695a2637fdf120f344edc387eae4120f9c947afd0d0b92dc335d2ee675c4d7dbe2d28d9f3b8d926f6c4d787b883716a950654307c30ff38a087d7ed22a99fbccc4660a6fd648072d88814f42dcdc900b12d51545c4a2d500e1eecb0957ff4d7e993bb678361209cfc7e38176dc397652526423606ca6543d9d6fb0e479be6d384962dd70a7301f849c5a535e6ce90f8484da0d0e9c90591ddebd64d4a41c8d3e8ca1b2c9dcb7171f4c7a3a68290eb2ffba12f52b0c1c75b1763c59c775b697a4ed78e02796617f44bdbdc85f0795a0f8f6bdd048414f5798e6d567c1185c5f726cb00738a9993d29873d174fa41a0155006a3f98c9788f763bbd4b7e134a9abdbbbb8be57cb098192ff15964853104b281f90150e7cb6de9b29b118cf9d3c5c200c7b4a71fd8412464f1300359ca85b3b4795be25007f5bce6ab8712c13fb81bea1b934d09c84377a8477a57053efd4bd407f77e93e2268d8e3ae262253854c8be0b501979acbc9409734c8766b4df8df6fa17b1b31a8bca8cb0414b5099ab2275479ff59e1cbd484f7c6d15ebe6cc6e1252c8ee7bd93c7136873fde58683bd5df9e188afdd55fc05e51dca5358a7df7c031e022b2beae6f757837e5bdad93b71f847bd54db8ae18ebbf84807fb7e5395723d7d587a8f8bdba68654be6263a29abff4d4b6e9ab30425515a99bd8b7c59890e953aec2385994e4289aed3c3249f8586e436d402d5360662782804e5a395d6a61912e887750eca426b15e16422231fbdf44fdc2f1b2549c8fe4da4eed3c641a7a01ce9b2a9bd0e93305f39ff531b5e38ea23b155567731bfd3226fd06760d0e6eefe620eda361c8a590ff50a8ca6b083ebab3150c2a9ca46b1258556ae6c720e2ebc6108dc2704c83282acdb5e467f43cc44e28b95b92784058fb9e2197c22749a6b1d529b40388e9141f520cacecad3fab9cf1870985fa4aa2f72a485fe8f43990fcc0f168ce019b5ab91a71c8b1fbc0d3e0187c73a056f64df34b9da364967fe569fc8693169fe7f8e4fc5aa9a4d3ef2cac8f01d152c0571dc3d9872b558fcdd790a1f22b61351ee63b8cabe00bd5202415bc15bc2a1badefda4c87fcd7a0435901591d993a5d08fad765a68daf1eb21e5a54cbf0f6268c1cadf2beed56aef2b4f5e7fc7574f8c7c1d8f3b89933e4714bc15e09c6b0f52373ae4cdefc0d5aab24051e6bb2e5949acf42d78d313f50e8ddfb406b840b81d16f977dfccf45088d2e0ee4b2cc62fb1c92fc05b241ac7c2e307e29e18fbaacd0943555c32365b6b64153ded6ca4e88ac6437159869398556ed6a5ae11dd1a4cd5d7b132503c4cfb2edb0e1cbfa8f6e9a3e8866da914959fb4561441654d9917903c4daf826662e1d5db500baec674e10bf9ac549260e7c60326a1408efe0fe0b1e2b00ebaea3ab63e3073bb193f4feb897a33a34513e968e1e4cce249bc2a09a8b32203e4526c51aaaf0c5293b52a64e5d4e2b0c570a9b013509cd136ecd4fe09603f50635b5f493b59eb45e27527c72ef40e6a5435a59e7bc623a9cd92613d2ef58f1a023a03b093cbe67e5e85512203886d4da04c3c552dad3d3c696bc16e87eeeb93f49ef34dab2d7ebb5725bbd9b0793ec61b4c9a52b39bb53f82f89c048a3fc5d3459a32bfc2e863a5ac10a703567a7b5fd4a66f5d61f5f7c6231908ac657889d047a4ec097b54af175a1faaa5fbea3cd54041f9784d5caa26659a552e80df2fcd6ccb6ab7444c5ceaba90427aef83eb24972b59acba4cb737ee27213ac8e387195a457d2c8013e5591a9c974bfa3becd4d1cdd2553eb3d443990f2f0e265cc17aec664b2a6fbc0bdc49a7f1aae9c64b5d3e2d857c5a8bf150a0819d0462eddf0d7fb5f1f8b9f85fb8e406374a1e92da0419afd13d38a6ef44264f9ddb5ce84d55281c83eca3b27a63d2983df4a82e4871f200210ad09c3dc7a89907e8b0a70cb61beb7d93d79b01bf1e8fc680b0a60c6503d87f462b5644666b4b6fc7e111d8b309f15be87f02d064cd422c152ea03c402a273cd36af4eccc403901a5ea4a639c6391348bb2982833f35615a36cc2a6ca5d9fcabfd7ead37ac7fead5cd3d2c57ded7ce52346d9e60d487a1d5936a5638218112c0518b7d3ed4ea7a17784c238f9eb6bb8d284a0dea935f023c044568c824390700a2032b4a872e601dec331c1d1fc7b8f2a746a712ad3dbb6095e2ee2160ad7963f90ab489f25ebdbda0a51cf101845695eb0f5526127ff576ad4f59d750218b197d1ac9d89b3d7275de3e18e0b7802c7d5f9759be988a729a2047178024e04cad0a3595419433c514ffa2c46fff6f7c433bcb375408b7c67073f512f5b803fa2a56bb00e474c7765adad22d1762c50ca6c5a0013ebec1415dc49a4ca8f291c32f7111e1384d37fe584fb6cee3c720576a0cf8287172396b3e84a6fba3d008b67a5ee68b2350f985758feb9117d28ef34fe2258c8d761793a43df192110bcef6a361671a6f0b79821be965974e993ce440d1fbbaca5999edecbb28cd81dec8d16f347ad6569e770b5c790db555d2f27b66a8098aa7f9381abbac770b1046404683b80cae6c1b16851dd6c4170bba7747ad91498f5a01d0ab66a607fb2f7ac75e2ef47b9c00a2797a6210d6e11b477686a5853e9b1320138353b94910b5cb2f4d77646bab7894c09e632d7e568ce0c9d6e3fe3347d200a37aff69f181ab1be37efb4095461b66c77e5282696dffe4fd90981dd67ff2c46ac0c03fcfdbb92fd9d4c8316d4cd29a1bf98f476d4f19c5cb0fcdefd1692dc7f96167f404ab9ba3968303f606ae8e68fdd8e73ef6602ab6f98885610656e345a6a1d3307c578ebad8e0869399abde7f559f81f37479a3979da1c96d5846eb4b24f64d8eabbce3fb7844d6301735aec70fd8ed744e683a70c896b13c9c077f7f1ace9082816393f8bbb42701e0d61626e618e09e29864dce1f1510986c4f071faf5952ec924f1bff899b4f6a27a2a2bb070e8a118cce35cf34df0cda7b9ce2fdc85df7f078226870405c338a8915656a7c34d8826969fd12e67714f3ca72db2dcb09f22d32a9c59805ba5a18dbb23e7f82a5a71c4b8dcf7a9dbd6a8c4d72b9c6d56fa93c7cf91f5618fe5a9ffc8448af75c3e4be83c1fd3e4bfa32ec0edf7ec1ea912b29dad53cb5b3fd8dffb859c4697221e0fa8520c9ea73c2e9f3c5fa13e373e87988c2b951227ecf38e5f225fa0da6c0847597666cb4f8f1fec682f7cdd124ae498b75ef025cbf001663aa0c48fa6977876893e92796c95a15cf2b0e2ce566abdcb549bcb65c8a0d639df56f3142901760bc9e421361912a6be80bbfa0f3db4a78604ff44cc4e3f768dba303d966a4710ab80d78ec9b390f675b0e08ff6a6cb8fa2671b77cad452c78ca8af95b78441665cc9b2de96f65f01d66696da7ea6829edc958197cf310d3aa7c437ae3972e082979751efe21c9f16fcbbcafd525a03dd6838758c69ff2d6d7ce0bbdfa66906f0f903fc3597f20d986caf8b9fc5766eadcacf017caaf86b4f685e3a6f4ddf7e592779e3902f7f35b4040bed9ff9af7d38f721da3009eeaed0a9a47de6367bc41a04925d897b9ad11c512263e217aba2557bc0f7f066f9e68f919ba73dbd7230d36356ef61ac6955ff08797ee91d7371293a53ee909bbdaa6a20e25031c79f582c6645113c74c51cc33eab45ddb3265c56c4eb980e52fc1e1175ad4e715eb1763051ce881a33cbac09bfe00e1391829b2fe9caf1a31d420757549c154cf76c0059494f1be805af279d4bba61db5636061f28833ea1b4fe30769bf054b1bb2d769f049b2a9b8e9b35cc04ae790bea9eb4e26a1eeefabe2c12d9159edc5178270f3ea595903f57f72d0e16edfc92523a353d81701bfa752301e889957238f47c4f4e91379b878e5494d98a4c89fbeca04cb1032bbc512025cc0120ea21316e529f158f5e866f5155f4fba9480445c761b2dee2a9841e582678c19636e060b12a5829998681f8e992800936dc65f4758ac115e8bff45617ee899e156c4bf9ee0c7c68581ac29001aa468b9fa0d527587a8ed9941b4d338fae460842b1a0a4332b5b819a00b3a2937600d1e951c9ac214d16ec2fb414ce424c0b934152a702cc71ce10d92c02dcc2f5fc5fd873c1c37ccd97ea9346bfcc078c08f0aee0f77db32cd5eee319cfc8180932343e6f8fd816a4678002fa51861144f3c89c6539fa6717edc2b3efca94fe50f0914f451b0187b780a541435569a1edb1510ae73be6dc83d37300291b0f5e8d73c18b0a339e6984984b01ef3b87ceb34ca58e7a8e2cf81d4535e773c6da8c3639e280129aa92e36bb618b20e411303acac8d6bb5d9745190dd4ad8d6edb38423f30389932948aa530a6bfaac72c3d4f4e8f26a4f906f0286a6415a55c1e05d2dc7ee408af39e41f80ef858f0290cb08869c12a67748979661c10c112226d95e7e78e3b871f55646c82ba33cdaf8f424604f419982dd28f89cdfd34ffba5d866262006f00faf361b95cdb341d5320b695997ce962d04c9d796345da619856fef07a27dba830d888f5173ac75f3a81afa610c3f66b39fab7c655c32df4bfc107ca3d0e9a352583e1919d047756309897671845e7776494cc54500a0eb145f68be268fb5749e89798ca3023cb1de1532b3a66a6d3b90cba0124d0763174862067fab0d8b39c1df6a5288f94ba64d474aaab1b809363c96cb79787b7426a0d88c65e39a9e630512d329021bcd1b7feacb52cc9dc7fe542e0a9d3b9cca21678584688ec13521f9fd23f3166ea55f9f2e0e3bab23406fffaaf7810d556483db1ab8485b8e0dd0a6999ab3031593195177104bb4c608442e02ac6b30a8f494b57ff01f69323fb3fa4dcbcaa7ea8c386dfb35809787284a5ffc66cdfcc7d027e688ae02210c243c23ae94017d48849ac1a2085e8da22f06ca56c25e89d5cab4176089da3dd4ac477d5e6bd01381b3ae0815ffc0ffcb58ef01795b8b0ebcf6ee058bea54d2712e4567109564633c35e5e8230cfd087e86b745f25fe9eec89ea4d145dd82805c9378bc3ac8f362ff6a20b2a1bee0d94e1a5f164ee895af90b0bf8fba500a4b8e8b62612d5ad996ba8d54b7062acddb3e7ddab9114eb513fdb203233e0e5ba11de02cceae9f2a7adcfe391b7c06a06e22b9f54d5ed3fa0231efe200444a82d68d8630646df0a7cb802fa2a14c873c5e14ed3c04a192ca9ea40e81f2415493bd0f1cf027f270c16508e63bc2469022263089b64cdb8b9f8958fc0078bc1cb8011631e974aee99ef81ade18afc0b74e09bac3600f6abdcf11f177a143eb75a2432ea11a2ac87f8170cde15eec0258ea823603875cc66442879332f70beaccc07daeebc9f36b22d19514d3db0da03d99abaf34a39de2262766168ac602c9f5af657369807ca76c17652d17d37f8b583076eb4dd33d306fe2a3a4b1b2b41879d8da929140fe1f9101f75d47c71cd859f8ff1f6b1119a8f30724805eb11f6c59212648cb277a9ff53fc53a2aee05820a55f1d524409c99764401a6244d880e3b889098cde3995b20e971f7d58a08d6fcaeb424edd181df2d53b9f165d625768671c55f1396846415143e12dc77803fc2d7fa4e94ee81c2a7f9a7ddb7227a09e5676624cff11552d68e92aa2c36d8f2a598630b40e82b3283a9df5ec19a17f1257f42d54d879a5da1e41355081d9abe6b5ca240415deac12733fbbce26070190874d960008087d2fa10de14e3caae1dc431d7d1f6497d144ed25a49c9f6b5765cd1e6074e855b5496285d134fac99b1f272ae07e921672fb5fc7b640785e9d7e8dc79f3b7bfc3b08c00cf6fcfd98cc00c33f2a6a0edb3c6f98f31f4f0a80ef6bc64614d54064678345002089fef5e31dceb73b65d32b54f4b65ead03a1f91c85ec946e444532a743438c94b68904de3f6caa8e9f0fae041c38f9fbbd1a8d43afb5d13bb22212ae1920487b09573bbae0102acf8c78320b36a5e51a1ea80ed7d16bfa2ee15eccb95fea218aead26b1bcb18e1e2caf0756ee53e0ec06897c7df243947fb16b44ab598bc31212d3b523cdc4625db1144a496f2e9a853907eb7a8eab6c8d3531dba796f0a8425419def0df9861a1fe42781b84061fdae7e3324d22e2f4adb01592295c7ab4bd37e65df2df37185b9e7385c8b0079719a0cc74454361404484579507660600136fec1937384edf670797ff89b76fffeb8d07be9e1e3a2dcc45c4146e62d8c34902bdd2b2e548e3dab4420e76cc55d0c5a501eafc7f02d0c984cae1dd6707c39cf44b51256a3476b63cf91425c422892f655ed2f4cbc34b279aecc9cbc4636d4d147d284eb3d6f6f2915eeb85c44aea0dd9675e05b20a495694bc83abe0d56ae853a4882a05888d8a4ae6ce85d91781b2872c2ebbf47a45285ea8460aa7a1e06590bc4ab55799e19b316ad31ef29379405b7efb5fb9de3b9757e3d28b0441e2af33e618d59caeef62e8d9c7718e3621f6df3ab4d3842e094aeef657160475fd12cb35a3c32bb28e52f7b850f0c4f764c9de3b799e1452aaa3748dad12cdf3e2f4131961d4b7a031528b561edd905ca2ecaed09ae65809b6978892383623d1850d3b906fd96b54d0a32888b4fb7e853d75adcbe8b5e740f8388ba0f958521b5947a30d162f42740abae6df66b68e0594ac385479126be912cfef96c9f2104d1089111d23d42275bd5534db9d178c24f627443b2a439e25f8ef70bd1c8b600474b98812ce5eb772542d92111b1ea10f9e4635d703f3a32965611ec21b473032ae5efb0cc71007ef1b83a339e7ee67dbb44c8a821770edcd6541d13a29961af3504b8d04d40d98a4cf899e719ca77354176ebc3f2c269e3c87044f4b0458cfc8c023076ddf15fb59e5ad2ffce3fd0758f8624272e39b257f0020534c884e76c1f50c716721c013d1aa0ac4ed651898b9ec1d393aecca0bf8a3ea49752ded59bf31374dae97ee5c7df9f33ef9500ef0e7814b1dccae276889f076e636cdb38ad68ca0179ec803811074aaba1b2c4f357e9338847efc1043a80b27a894980836dd3078ef67e962d922cc3bc44c58c0e184feab1e6ecbbc0a467bae8405183d617d8b4b197bfa4e3a66cd9fcbaa3f3996c31d69c8bba77dd8d8626d24f61f6a2a1fc0e4ff6fe5a781fe4f4e2c5c8302fde532796e11aaa97a7273c9ba13b19a1f71da4d97619cfd4a9eefca5495e4e995b7e492ccb76b9a0d186827bad85c741f108813f113c1ea4d0f35405ff0a8071c13f1754b8b0d1811d353d554e64eccbf2f96ba49d40c24c3de35c56d085999c436569e51ed3b6b6f389663f7aafdf582111723680e48993b2e744b130c3d2e13e76101926f2404bb51ffa9d44e112ce7ca8ccdf4d247f1b377a6d9f223dfa8dff6104bb1f05083077a5ba004384b66f9202715e91434fc3271e8c7370bdcce48959c82d9da5b0d30b3c617cd4dbc865fadf064a546addcfd5b3fe161dfc3423f7bd478b427dedf17e7f7afcae0d986837b5ce3b57280292cf6b338e3c192cd7735d1933ab9741e11258b0c0f01e28d116333d1d18c1ec3f0102b25bcdb5d64df661d02f63e9e16183947b6e803fc426744f4d4b58e5581aa9a0172a3b2b94e9363eacd594b9fede5794fbc7c0de115c9923a4412a975f5cf7379fb534be90bf26c543918225b203b17fa90f4b4e5d6cbd9116ce9fa63af109707d8ea67786a6572a5e69270910e867c81d373db9d568ea86f3bc6571b086cc6935473df2bdf91ae2e26553acaf234e8e86073d8983bde473d377cc4c5dc9c8b409de72d43d60daa11afc0f95ca3f2aed49e4979c7a31a80b63db6d6dbe53b3c5b3869ef56e7df4a22a7d9cc487794d6be6b46bdda3ea59bab18804c5d9cc6161ccc7d048917f8b098a40841217603865f2f356292f5681ef0b243789454d01488afe484d518e405b6a0a8f2909d2631faa08a594c49e43258b431c3644399b3a95ab3235eef12f93c368fc4537900e152daf3b3eecaddffc67cc140492163a2b234ae0c034b254d1dbcabb996681d5f069d2a5cec0bc5da6d34a3a748c0386d0d9273e0e25cffb02017e497603aa518b56b18ae00782150ac46c407b504cc5742db08bb57f0784bfc9cc2f926361d1264b10e858ace5af252863888e6f8e6e04058b130c14e2f158e04f977025724dbd753c9680a315421deb8dc3e94106fe3096f7c43d100f83cfe613e7947eff42764f68bc9f8c76d1fcb2c94fea7f97f01a851ea089fae81c0fdb1c078f3b26a0c636381acf7f740f546fc6c0a1365fe6c6e5550f71474672c3337144867c326298885721cc721cf13819054c0e7fcb634c7132f14c9a932052a38853bb71d6ed838bf8bdd02240d42886412f77bd7ada5a3b4e1ca41f6321a0eb53c5502ac465626784fc598fc2355434a872d0d79762f962f4cbfcbe4c3c8c829a2a75adfccf47e3b62557ae4fdd1e6a4fd94e918287183a34d23225a5fb748348eb1cebde1847a5cb40070008bcae6a238c4f7d558fc3742c99a469e249a6f567fa7cea65323694e4c0280cddaf2df8daa1492ec1e6037a3ecd305d908d504959a0e0079d1686f511179f6dc9d926a52cdeeb60635c4ffbd9ebb874089fdb65613a35fba2c1f80c5e6c9c83017412bab94c66d33df3089ad062b3864bf870885cd39b0fcfd355945988f22807640d177b2904ea089f3dc172e0e5802f5e4b41eed4e945392444d6e180735643211a64c3bfb00142c0c24453be70085f9528ee4458df72dc76225d4c9b12136100cbfb9a1f5cb6b492d6e6aed008f0ddd5fdafeb1fd9a0e77ce08c370483156996c4a04572c917beb8bd004ea974388886dcd850da8ef4cf58b5b21b9801e01cbaba4f97814e642d31f79333f1430aa13c0cff97f3d9f22125f5407f918d3034769e7e21e59b2c9e6b3276b86da59be020701cdaeb4a7ee5dc82213c782923512f0e054922bc727df923ef07ff0b1d760a64a88e49e5082a0c9e2111c5d61b3f0e95a3aa19f2045e2087468ddde3b8ec0de3ed823696748f31ca5e15b6459eff9cd09730670a1fb340f6df9b70b0275b83d085f695001150fea4e36532c23c2faf817458bf10da7f3520fd5640199e8640bd5a10bfc8a079e78ed2662052de2e13a09dc44ae320436e56577d44d82edbe6a62e90c0021703642282fe10531613bb80ded8a189d88b14bf82034d6a35d79f8d3eecc0917e501dddfbe9a9bf329cdb06a219707c9631ea631667a5cee141a80d5de7dd24d3c0aa28af08024ce4c692d04686d4d03e78944256ce08cad7c58f49cbed73a1a3284a96dcde2b0e29818320054878f0fc5ce266fbe4228c265d7b241f72b93ce6977169e3afffe5b11ae7eae0aee3c5797be003fa6855c3f07d107952f85c307dfdb89e39600cb63b4de8c1736ca982d1c6c84d6ffe5f99418106a02bb5f7f878d95249d36716f6c80aba17251755bb30aeee7d61976636933d1e35dca746172fff28d9fa61bbe55939967ac5d719a17892f0963284a1a41030000cd78cd7fcce8a1ab36dd6b6dfb4baaf0cdf5d9b9d247535c1d50d62e6a20e74c5e50337044ef94836b8f6b214895300ae03666db80eb01a12260fda808b32d4d259d547be71923e6a331a0fd34baf34d037e7549347d4efcd37a84e78d4844ca574a52900fca06f394e2270db2f8958bc1dedc99da836510c15dc6e1ecb6ede06c78aad1b4b27269c45726d992f59988698c2f96b002366f9fa34ec53cfa3620a7b06153b01245e3ebbb7963ccddc2b423988f997b470da14c8dc5decf4916fa5960e660d75b6fbc341217f188baf6bb6fdebada72770e6c16fb25b7285b5924a783ad361f1d401376f17d2ca88c552a522ea8f9f004187ac430eb6d5163565cf065f909ba5b2223fdf6a1913e5ce74794dda7aa98b5ada8b94c34b94f31a98f55250463b46af937d1549ff5741e81ce31d61dcb760cc9e45b06066443caae518d329d8a27c9ff6020b76fd2ef79ccdbf8159b81c0316273de6c0c5c5b7b029ae5fd8fb9b226313e50ea9a37e0a5262ca9cb60cc84f3c12f2a282db000297b1301eb6656cdcdbbece2eca7dd989be8ab6057f4c6e325bfc8210c60b497a2d114ac6d492453c4fd9e0c246fe854e6ae7128c0ffe8e3c941bf82ffce7054065cd0a37d9a0deda01e1ebf45097c1c9916636d62fee87f022cfac2a7bf66434264cc1ec08e02c06f360593a390b691aa04beaa4c9a335c611d22a366984bf703e48d553550e7598440868b14facbe285c62d426c7ea9073da7265c2355d4ae021db46d12acdbac932c0429898663adb52fc25360a3a4d292589765a781f6d9c124600d0fe4a9995eeebc86deb371bf38ba27b733a71d4ccdb27c3e083748e89c296d35af0f679c2575e957f9da005cab534c1ae672f287a18bda37103f871fce91dde6eb15a8ee2708c0875b102c63342bd7ed0198484a648689d5222a4150337716c2c39f1d6af87673abd5614e79ee389e3c3a367f9c97c2c6a96b740114263760f0b77b1a43aff905a8816c7a1eb05fac6e585e8abb1e3f47432f883c48f514652fe6f8d18d0b5e1ae07f90ce7b421e6e6a1f57aee02714d6a8ef187f4bc267a0e8383962430e01aa2f6c4b48e86e5789e304cd4b00868c39453b062a4fd711e43059d588f1475facc9708d182e91bb0c94b9c34fbf35931f4561154c7c5131c0d38c69bf270a6094e43c81202bc66129e3b778c7d6b62cbdd39d5e1628ad758f298c2d7f8bffd755ff0b44f5b5e2862e6a8169fa61bf94fcd5ce4983baa78b2b9a8b69f98f8c2e84fb6cc090f66cfb1f1a7ad78306ebfdb329fc0e89a36d921367f2ad139d1361c0ae31bce44c60a8ed31be8753589bd099fd7c33e59bf4b9f6fd8dfa9bdf86d7310be66236140e1f837b387ea7882155b347caf16db01b218c3eff556f5c5ee9dd7cbed2b1d5f72b87d787667a93340bb3f579e9e638ae3af7bd222ab8d268f296e461a70665d0676aeafd760c00ee86c1a1701aeed393b842f0022523239002ab8219b04730ce76814de473cf0ee3e98927d36e031ae141fb74bec187125eb287d953d55767ed20a61b96c1822253ecad5c77c9bf0ec7386b5422107c8d2539e9fbfd084e99b1c202b26c285ce92328db0a8daa49c0961d9de31edc1dae026de93dd94e1bc4d63cdd1a036bac958b4146f992cbf9a18196a74439c8fca3d98ee68e73290d60f9a0ec155c59f311a7a4955287f080879e5d0b0171e1d0e625d8c110f89f07272957351ca80120048b9790a89977d8cfd1f3d0440e414fbc4d722a587aa991f24e4d7402bde93e0124a3723868d99cd1334608b223c8f71f27fe71c4fd6ce9bbf922fa631d2a08a7cc8a846679dfc023ae3902d53a21fa36b47a6721fb070ce1516b690971875d25837e2eb88ae39e464cbf5268b4247d06a2f19be313b50573f8efca8be4872a3302133e80b01c894865905c7eb919dcc38f531f0bb0fce63bc689d993ec5fc0020ea0377f9ab5ba116c60ca3454491863c9da34125f7d987c99fa1b58e0e5a823e3ccacc8350efd99dc24fc28f92acf5327b91068c0d29edf3d59803ea5d8a4c84b9e45fda2b3498792a49503dc0c5616124a6b42067d6234ffc34a1c2c3c5e00ead1e615c5be5197b399390f16353afcb6c73cbea870c482b4ef883d9bdfdc30c169b25b15a447a9b8006930de658767b2fe7323e7374e4fc1d4ed71257114a12dfa24abcadd21b54801239b7d4874acf0ecd9604485f59b41e94faf8bcc9fe4829fc48835b962032d37ef5ad7c411a25d19cd2fbe9a4e833500e21179d6635fb541f59bbb658507511d5d7f19bcda8c30ff465938975bcbe5ac63fbae2fbe9f90e63f99ac93d3c67e939e965170e0990aff6a5078de19c3143830eadf83f3405ad6d1be053e174cf7216d7a1865a218036ec7486310434314450020603635317c3396f2f42a60b6acc25f9f0a27620bd75acc772f4966f98aa016c5721903be0ab44a417455e5385c13004a311d41c7baeb35fe389b45bb2398180e46e0b649935383ae59d04654be016e83f892319c089bff2fc7ccf7ae07db45a3d0ecb649acb4e30dd93e7e1a6eec9c0b132287d287b44664fcffe0fccfd91f88c78e22ecf6a3b8898394a2d8278f4ad5c3130bd8ccaef904a619982237f0a6ae5dfdb3b3eade8e57a81c80d32ebdfae54b2347b4e65627bea658ce135a9e5567e76e81d38ee30528a39a418c2f431a555f68c1f80d8081319fab522955c8862eef978ceeab24ea09c3031c74d116aa669e58c13ddbba1812f8e1895cc298c2e798afc1f39bae23d807c6e5418e2c48f6c53eecd61f777a7f7315771e3712bcd14c5a06656cb3df6630b8732c0d9d1c775837000892627271c482d6930e0fc291743b43d85ccdc5f496e5ea258ea5f3aee05a10b6f81effd6ec66c158d1574e21a3498eb327b42ff8170a9a39f0c7f54a8dda3c6a09eaa245c45242ddada0bdb7b780989db847bc22b3c591a753acc5bc9fafd4dc1bb5a94db69eb53ed7d2c3a59afc76fceae4963c496f63f1093bdec3d43f5f2990eab7046ca2dda6e2efb32943df8b0db2ef02cad12d262df0d2cc8066c00361933c6c4bc0159b417aaca1cb893060580a99a072231175be3b3103adf3d1c7fa6ff61d4600e57c436dbfbf26a6c90bd754389484fe61e3ffcdc8f2f79eeb1d39b37b367f7d7f02b6c960a14029c86f307b214f6364d195058fffc09715453f5493973df7ccde6ff67995601dfe42d669c3421391a824e0d3617c4ad00fa4373feebc7a683cf13d191e7ffd3fa002beef5e457713f965704342f914ec5d0aa6d7e1457968aad4e06ca58ce6b5dd3624e596ecbe00fa231a246a04c44e44696582e63308ee2512ef6fd53a48b8ffbd23332c8516329e0b50b798c6f27c83d431ba7271300fa353fa9cf27d8812c642a1637941d1613df2efec20a7565a798dd5c438c9d226fdcf00c497d2450f326ecb56803f8dc446d246b10f0a99a25d53db96032f8d2391969e90363f4226a937a4e3cdbe89d8d67964ffac82c424810a2234e1e37ecb62797e2697655eaaf63ecdf8bf8b403dfffb4e91deab876c507e01bc21f3a5d4e30a570550ad94c4f91c16a763e988b6f3ad4fc2466ad53e96b3f51a297037dd0037f730a2601d1c538334aac80a31f19af2de9aec7d95caf615e958782f8bfb1d60ff06ed6298e8c0af84977e6d86c08ab60564d813866e54d32d87ae7306b01d3bad7535b6bbbeaeb3827c29302608b45aedf5ad5da2db8abc54fc96b4ce58b846da0bd417060461201b616beb91ad582ace4d456ed30173ec8ecc7a2b00c6503c9a3809fca2e52be36ce404b2a35419863739b25c7700cbe57065b7a622d61fa1f1587ed18a384862a989b12f91d8b000f06cc9fce8e41635377b8178262c5118c9e3b83cd5dc294b718b687aec3bd1ff4f584d5eae450dfecbaa3eaf70793734e49fef5b0ffc1d96ee7f5c3710fee4928ec58785e43f9699977167889da456dd676bf3e20e67ab8bb9a0f51959dc9eee8716d9c798faac183290387fb6462654046074327a976d772dd8f1dcd87025e5f4cd4c12908a099d9570108b63c3c237ee7825e1b5b7b7fd7cf7f928c978899cef815bef4111aa39b310fb71efb19fe9973dd380e7bc0ae013c7c1d9313c128a34f240232080e7db341d5cb74296decb5f278c22309e532b0bfee88720d69164dbf059acbfeb69af7d05a7476d161e1dcfb70f1e1c82598fcb4bc004e13bdf07053b7df4315c261a6ae279e2a5eab5a9b10dc1bde74605d42a085be97dccbbcef19bff052fbdfd9ba2067bcf81d284b4f6df71d5c31f5827fa334f36366c2f7fe0f9e82e72bce1b18a7ad8a68c8a22a676fd49426ae9b5a55b80ff547757bf27730ac5ddc63b36e4b37c09bd135bbca9a287c4e51bd32935d63c98263d712103f99e60fafc9f73d8981ea29f962d978da87e1ff345d8c508414f96cd5d024ca5f3613a0853df64a5abf761fa0380a260c0c851e2e3d4bbc7464ce6819b2061549e383c584714409cfe4cba4bf0544effff1a3fdbdbf2cdaa50e8ed6343eee7e8bafa8a5dd2c1a202ba1a49d2644691798d70c22a6c3a1b7790aeb606d7aa388e3f74fc9b3e3b581269b728325e3534dabd3a8f3e9f69aaa03118f1b3547beef2651f4eea1f126162a7e9ac7fde15b1b8fd86faa91b3625e2c9f5b84e99e0f97c4c7c9f9a0918dac8a3f39f04b8babac7c2b23370cb1138c2f18c44e9a6c7965fc509239c70ef989f1b96a2f063adedcbe7a48a08b0d09aa2a07d8700af07f62d02b7deeca6130ca72be5d2f74fe61b85bb29998c3eb3f1eade936f77d4eb790b5ba4fa0e9088510ff384f19b0e40e9d0601e60fb33fbb656878a207732ed1a586775650c9271f3dfe20d49e973616e27c84063f3d10abd4cd43927f1bdb5fb19a7475ccbfa16b88b38e90d06f52fd92928d4ac1b59e942d128c1c0d5453415f80f620d0427863963b7cf2d7b7fcd869eb7c19a9ffd7025922dfd7edfe87cc45cf56b7c12f244832b1f9e22478064741be81cd8336f2e425cd1a75464d293e2ad5f3538c20d9de3f98a50b0d46c3e04e789bd99a659bdc5507c5783b5e29e4033ab3df2ffbc4cd3b802f224a2c67163b0df9e321a5276af08c8e6e351d0bff5aa92599417804de73853d1a89483fe62c077fb936302a3386050fa55667cd6840554b2c3e80840bceb32dc4f3452bc43ddb0b947bbe60432d23206d03af083b9d58a29c9e1f8a65f7a9d11c9551038bec17ad939478fa75f0e62ec058642d34ef1d8b78b1602b3dee5f2a01197c8cbb4c7d110fe47361fa1d7d9712db773f520d13215f9dc4625f6865e7a1bd3d4713fd32dae261fccc6234293c8f2a778222ad0968d309039900b404a8394654d6fb32d77a16f70ab17608ac0ed1ee91d16736e1d7cdfe15bf8bd58f69264c8d0d15f38473bb3969e2900fcaa5456eb6527972a573805753f5c1fa820066fdb0ffafc5dd1b3bed1992ead5df50fca33e9306a90e3e92ceba17d01d016fef4e3f24de60e949b4a2b22b4bb05a99114adb2b86133aa07e09357815f8403add06d552792be914328e3926060944b78295d7b552f35a5cf8cca4411f95d09c0480c051bf04e41e624a569173320fe65c474b06e661b17a010e6bc39800df00d91c77133478219dcd9ada3f7656579749501fa56f9729d9f2c98fb5c01b620aec4d5ffbd812960ccff3479a6b80487b368472fdc8e5ebee2b5d3299170858e7d6afc1abf48245f76d93fd07032ee1c731974ffb11d6409f8a53885c0f70d9b1700ec2120038376fd903a3f6ffa3652eb090ce906e89426563c0ee095f36eb9a3fe8520dd18af1791410088d3c801b5b49c1fc3e9bd57f06999d44b8da96f55e553311bc5d2f30c44174032576b7a351e13c0e5b4355f40052bc386800d4db329f13384df2a0ef3792df5136d684707846168bb3ee0a212cad05af66ca26b7f39b65066338d0de59d8c3014e4e0cac86538d7328f40fd02ff28fb129e3d72758413ab78d57f75f074030a7c04e1cdb921e3e8618afef1fec37540617a7a222d16c3e0ab575ed30c0df2b54faef5afc7c270ac54b9f1d559a58ec2804be0bf12fd939232d9f84145e17f30efe2fd2d0a279387f7a26b96a53446e3d856e71014702c2253c53b969cac217584cdc36455f6da3ec77ab17566a564a58851bb61609805564fada662d2ca12f5508f1aaa348044ebd6897c999527c37fc77d5e85f660655a547b0d32fed6ef69f3f3804d304b129dcdb253726a6286c5954761db17f9b0778c3d4248a8a1834cfe8f59b42e4467cba83a8b6ef36083047ed01589ecc08cc48b0c0f2ba49229561b42b47968d23cd4f40d6097c8fb1dc0ac606b67033d544ac22c1eb0df7b020b2f01225151f66b95a7e16560cf3a6aaaf9494decc2d034016fa71831667aa3a4dc1528f8da03892234b63f5385d511aa9ed235b9880f1ef7fb148c8ac4d504ee59410421c827d28b7d47b0aef2114016b5154cbb44c42ac5d765b82234f88c34fd69b77a790c3a76e0a653219756db637af69d31fb7071c00a2fe1167b7b15ebb656d014c74c853010ddedc2f5cf88eb06630ad32e7e454b76dc95c84c62b9a4df2a3a1bc412026f9438f052a3f82bb48f41254146f36729bb134adbb59673ea26ca8a2bb6f6154fd2c04217c5f87f9b5562db2115f185a2c55f0431dbbdd47ef993e1d2bf1114dda064769c81461982195c07739f75d91f08b399fe7712f092d2e0fa9859ec42f1b23cabca214cabb7d167a24893f343c3b7cfe647dc13f80db8d07e90ae0a56f6104d127ff73cede364b7036feb58cf0bccdc629c122d674fd549e4dbf69103620be857c9d0a9eaa6f15ad0f20cc8b9c736971f77f0688a46f57f893eb27fc4761d7771f14ec227bcdbcf8a77c050f1c41741653cb3b2e5f0415a1f45b0029dd3441dc50c00a2f11a7296cfb870a00e266ec7f05eea091d1509015d87044243e78f254cfc7e8e4c459f0821cab30bb30bfeebb586537f7633a3d7f2eee8c10b5cb2855f4b3dceabe2b72307e995aa60807a1a1347a2c267fba3958e1c2facff4c02e3ba6618224fc14462e04556067d2c0ab7a6a6c80718325d8cdb0286317d86f0aab950bba649ab0c981e8a5917a3629e6abecab6a54fb29e2ca7a7fa9821c2f500eb18b370877e3e86030cb8ba1b9c4364b6df0c5e05884ecbe72ed0033fefdc9fd4c8e8462c71be43d7bd3782c58bd4fde7146fc9b57ff781a7478727032b9ac4bfe7d4f03fb48fd9723f06a15ff075f434687ef921ec22c3ce14fdd2c334c429297d30a060b5a52e8b628346a10089022d000a5eb4821cd63efb0a5c7c8ae575b5f6ebb21ab096728ecfe476c722e2cc8048788cbef459288f62b122293563def5a48159d8c2c7407a430fd500459858a268d606aae9948a670b2fc69fa5384644b7130d7d29680d85c3abd844d3c6eda7a61b0e7e36ff77981a71ca448d8042dca07269f75c03bd07b553ddc125766658ab5787b812557e9e92ab29fe739a92ce895008550e2eb4d542408286a38953d9e9640d100a4b9fbc7183f8ac69a13b82747671c8cfaa247be7d5eb9a6a8846b00876a30799938a3bbcd001312679a57aade60878209c7facd4d1b7c451aeec681be23ecd6ecbe6d718aae6e7b5b1359cbfefb5fe6611e32b743ee02d4fc83c0b972683b31e4a0a008e840efcf971deb3f6f4d3ee416d043b234e810bc790f06aac0f666091810fa5bad3b5cc26a12f69504cd2b0495a396655e2a0524c0c2a31a0ddca087b475156d334d1571ab9d148096772f9a825a10d12b39c91c345ec4e0e7a1246da6a5b3d7812876629f67d82232d985279b6786a49f440cf9f248d68ed7a3aa450cdaaf5081fe1c85ff6b34182727cad524d0b308ab42b3c82947ca5cb7dd5102c3f3e7d3d564978107acd69464efa2eec06432f1d1696acc018ebe3a998bfcc4c309a3b60519fe021477ab832d1a1ebf60f7115cefc0d5186031d8af3cf842679a6ecda8513ba796940d02e0aff7f0cb467394077b27afbe5cce62f89ee00d17d830707a73b5994f886e8201e6c4833475202825257834cddce7eb8a039bfcb5e7f4de675bafbbc8fe706ad96e799ba926169ab954ee56ffcf0cae727395eb8fa503d3494e06528ac4455bba0677103f9ad9b276f0c5e673060b5b2f57181daafe70363425d05198b27dacc94c05482bb36389a3d655f09fa898838f28a75b21d181175207fce18ccd230ded73c4c22eeb53873530a1bb9b823f52842b176defc63f947e8a9b1509a1bf97f7b43be59b3794de54df328cb8e18ddabab0dac7ae4e356624a5a76462854f852dca533f05c75ee6f1db67278e5fad81d3cf440e09d44e3008a2b1bb8eb17735487a283a092a7f1cd49b2759fd9301fbc22b14e1b5cbec39d42f7c979837ad92d4929ab30ba00972080a971220b43c29b451925939fcb0f800398f09b0685367a26eb35d16f430fe6ff56c1c08a1341ba8bd2a262fb538b2c0f835ca56573b4749a1bfe9c27cfb03e81845ad53b0cdd6e0b03b194496d56d85fd38b235733452646816bd85dd6f0348313712f02e63aa26b551a9156d4699d5f504acdea864159d92fbebcb5f4d394d6510ff7736fe3a0ff8ce839297abf773ae2e97ba5c309a1c590d19ae226eb04840c794a2f526c902e0fb384e68d2b58d3f9807b1ae707d642e8610e44ad5fecdc0c23fc67656032bdf4322e2c7fa60c68b37c741cabfdd1c615fa32da665169b89e6dd1fde7eba3cc1a43ec0dd9b1409aa372607ade22d10622b805290022e1ae103b5620888963058cf5f468506ae082daa5fa2619a5f88e2d463a4a0dd8f72c6bca8418a8e25b294e3177d379ee7bce327fc611bd92223db96a0d16926812f167593eb76ecff9e147cb4c72be9f62bc0d11aa5d6db5328a47debfc394424d68dfca9466807017586aed50d249245b82117204a9101599046aae04f61c41bec15fbb559f5b0531150069c6e0e9087624679164d752e991cc49c7ccf375747e9b5d125aa6092d263f59bb5a172397b6f2bc75c88b031401b584c9b3e9b9588380d8ad863963fe83369e81eac3c51cc8bdaf8bb549afbeb530ff6e30f43937f6f0f88b50484bba0aa45f76333ec9a67c5da54f4b5685ddf70b5587bbc060cfb6dffb8209280ff492bfdd0e4364c7cc17d35d5b0b30918f57ff3eb6063ec67c30b8415f2fc0a4e0a12b59db2e744623fd70133e2036520ad740c46317af99422eb751a345c0a15841c0bd81130b6e9e4f0502c51af180dd92f3def9e4efd809f37149b9dd965386e738187b26b507b4c75d8f0c89f6bbe6b63e597be1c6b545d9f699ac8239b9fa66f4316d23996a0f90bdf40acf282ebb50a5cd64a3fb09dcfe33d67ae1abd55ff0acd44061c9faf154c6dfb9a08a12525d60bd5e99d9729e29275af398a1c380e4b37153cb035260a1588fd287183dd99e5dd694b62ce3b73d6cb5145c9c38af4a2062015df40d41c931d18984b5e179d5e05b0ab7b792c8ccc56fcf8975d47fdb016865ddadeb73f0a78368525c3b5436dca73e34a16a7cfb08f7e3f9a58682204e2fe12d609edf654790a6d0508d52c8261708a6b20981fbad5688352e1fb941ced7b7752ab989abb8cac3c01ff13d4d85ff08d69c2ab7a265fffe4f68a28584b52c121aba4e438dc9182e957b22b2a01b0d4a515cd7b18524e4d362a19041c080a3f724a89ed099c7fcb4233b1252419eb543f905578caa7ba7109f72fc79fc97746e6b131ffae2fb7edec4e6f2f032a9f13f0ff03ce0fa4c554628e44d324dee0b8133f115ed67b53c91bd9441ac089681d2f82bbd933e1ebf79b7db3467554f501997f55545fad759b6a2e8675b826add5a7f3ebe08c582a5a6c179ea63fc0f1c4e04239e1c9a63a1a8eb8e2d174508e1a646dee15e88bd6c0dd47f8505118ee609d1622453d71ad28a355e90ea0cf77af5522e9d2270ebd2a57b2a9b5ee5129dfb0ed3d35568cb4016ec04a72749e2180cbf8088691c507409521f4b807fb5f668dd05baba3d7d0d55382b6a06529baad0217a20842eb0d3a21b9dd5d553163be4897b537fe1eb5fcc06652152bb868c5882ea69679585126b84cb80a5083a67485e9cd00fef54a1243d37889cd170a2c98d0153134d0ea1ca28991236273d98138e342b11630f5d3e69ab9d273a46477033b56e43fc12d0df810a6aa4388d9ec5180c2efecccc6a7f30baecc879abe77a6aa11662a3601b8be050ebed0c51cb997abeff28ed71b79f326f12d30b3af9e3c567fb20e670c57070da6e23871e6dd433e421f7e48f7011721fa499e2b26746afcd86c2b96955d04cfb7ef1e5c7a808ef7e4441d5cfdea9b8fe4f4d1057cced7bd7677ce7afe55484f596bfb769b1b1b1e8954f4485984aa8f216d4a32180e09edd9b5747c6ee6a0a2b3645488d0628bae1e85992b8b212aeb10ebf57a816881abba12326e20c73442c60f4ad11a87a30ad877ab6c9d49398b33b3f1a1f701f9a072ab80a8e644ac8093db60955c14c8588bb7c9f5531069f45852b80e488ba26865d8b9e81ff273eabbe96550ee19307e029e9916e1eae18e83efdc372ec02e1b068aef2d2db958703b34574f315671fa6fa131cf398e0d86658f63fe473e90264d015bc39c02930020188c4b43f38d50b3153520adc9c3ccc3748e70d85f6266f498737f64c5f260aa4fc96bdb2698a3963806987e7ad9f85e5531aa6784b33bbb9f5690d1229d2b8bfcd2cb6fc05f7038685e7cbfb2f65a425864c5e4c9b185ff2953e121581bfc75337b29018ffcf674f2b70cdac56b6dc59a997987dc97bb5cba804dcabceb50c9f39e56bfece38b96bc93ad1f7cfbb821bbf5d94c107039e3af266745c6378b527d40248391c9d057c9357b9550f5e27ead3329f752256d8eb66c64b5f55b2932ffdb3916fe64976e0680560f9979e8d9ba1cffe5018fd9e1eac0e92973ae2c0f9e65792ebdaf22eb60b6db01902f750d3f3e6db9f2a5b75ec7a7a6c6f19a39f72c66c6f8a7aace25a95b987ff32af68a7b9109fc61b4149c493e22df1b9a23f70bbd4327033eb71d4335cda23daca9ba53249021728a99b0a24e14846d27a52dff5a22c4995d407a15d4c197fc48f1b30e66688c33a2d503893388ccf9f4dee3eaf2f876833adc6517b671065cbdfffe34ae6e6d415796e0141b771128937f9747e57270d8f5f96d334bc0770223e8b4f01723f7b736323a19b958821055c0e45262df0c4e16fcda1205b239a593c31ae30f90b4bb89194241d670b1113b33fd256f9e80f87d47cb1e33613a9a4306894c1da69ccc967a2f31d24322228a9fce73e72610665e4601f743e8cb4f81ef9f12206354d165e072fbd0cefcccb3d02d865568e31c6db298379b83fb714712fb55944faf718356d03f2d07efe18b3cbf67567be0457c8c087898b6f7c48fa0bf4605517833cd33382720daa74ab94b6c916166f5c099f16595d8337fead929439df2eb9ff8ee420f7e8168746cd61f8ab912eaf7ca9f93af1f0db255c790812dd1515cb82c6abd90d9aaca145452279eb32b9697e85a431defcacd75905362b465839f31d01302b346087265a7822105457ac37cd13c6157885a8d06f372642ab9cf3574b8c452b6ffb1d791bfd8c300cbd4182803dcc621790c053b60e7ff39fc4cdeca3bc9fbc0204ac6eb66eb4076bdf52f3998b2d3ee20f198523853cf0c3d11677538003ab1cea678f611c4059b4110b7220a7c12ce416c3b847d3c21a934069beedb0cff584b6c8637b0bbf365e79c578ffc8bfb23b5d279046693d1bd5023c03d561b7ac47e240c42181088463080eefe60ceba0f43fe7a25d95ef98908941a26b71c9ca47c8ba735edc2b81766e083c8cc1a6f3755b325d8cd4b0e887103a45e39f8036ee5029d360a2e8769153a58012f367b3389834009d0d85e19c05604efc3ea927a7e0959055230266de66580f3e4036561993174ab666e476192280667b61b073ef5a4d548ea66ece1f45d7efc9c447e8a664ce7ae8ca4c0c14ba14385d5ff47e8b7535be8086cd20e79650bf1e05d89081f58df3dd9174ce2fb60210a88958318fdd155e458f00c98864b0b4d1b50a6cba8ac50d93e8fc286a5ff34d148c3517adc551a1a54d1c0178f1e1ce569886410e2795b5cf2d918b40779271d899132e8a82ec741fbc4c1a3b60cbe790d7a139681f7b61305f02b52115fa6686e519a5b0f9d09670df21ebdbd30f2beda06e97974fd6375727ef70b2f69823cfd3064d8fb032368007d5994174fb1aa78e1b2ecdf5a5811ade6c8002ad28e2ef66f1be49fcbbc2b4e8e7a3cf64341a36dc6a99e0337f64260a371b1fa4f461931aba48825f5ef6a4369e45b5d058874ebb56710a948b3abcc30b304657d335c19bc8b013dbef494b13e50bdef147cdaf086c66f5f194ef98663e1071a9f2105f3d33a3fa9b5bd172e212edfcd68c27b7f6a49bf79b3ee5d63bfa7a686964d29da62d94a2c072cbe5b9b9d14f3d795f47f013f191d0cf6f64710ce6f4f76f564362c4105247b250d14175e36c0103d702d827902591dda583c5a09bd47b1e82f4767b037a81e4b6dac215f8752143369c22e827a13bc3506f04d5e329a4c4c029227bb0c969bee0d9d4bb27893ff4c276e37209f3005bffcfdfd0117b341149bb3fd852293ae9c029fc2f34ba8b23f65a4053f721da178c9fa4d9301db33c820b1581c9f21427218898c288775f56a9ae30921c62d06852afe14192dcb4d16725ac2f5c93f05277b9dd11f024b1bfc4f9b3f7b72b49981a679173020de8fc462e2c0a1ddcfbcaaf8337ba94238990beed9bc3af1fd2cfbe85e39da20f51f2f93aaf9b1061315781c2792c06abbcc1824c7384fe9dc114a2a7fa27d602bd9330642536cf6abed90eab8097022cfcf777009f759894ae30de848c883dd0c767330a51933423437d4d0d9bad333a11119470dc2b5f75f4092847182387bf677365091d51781c1686887bc5f10a56ebe31b0e8f6710a7304428b7b4b24bc1d74289a12e46524be62b455d021478f41608974dd3ffa98dc70a78afaf766902df03b8672a9a744b030cc5baf48dc3ee8f13c11ddba49142876327f0d1a73cd612b84c9a920a6563b6880e2718f63776afba20e7ec00ef66ed0673025ad705dc8a447ff3bd4314024541e04bc732eca488790124131ccd941550259aca34b86d203027b91d66464c3a11d0bb6fead76c23c17a779680370b96898d830f7ae3316408e969821b6c834c08ef6583f7748bd01a570903d6357bbd46019b02c2c2dad5f84a2005020e0eca237bd5308bad6c5663b35a18cfe173edf1e6e6cd0d2c342be68c7b8f238e0eede3ac21df514681fa98c558b4ddd7d5c7346832d9a18bc2854d9546fc675f2cf08fa3989805353f781748155af3e9c03d4731207294d92507fe6ad10428381293a7ae29767272a24e8f091ad66d21b7a24f3332ad418a43ccf567b8012f5179307fe2fc529072f407eb565f216e535adb18d44b1edf09907aa11463069617639ce300e90be9985c5c529c4b10c56b788a78f416b08028f8464b08c6deb79495a7c2ebf598a9785be9f0e0635eced2cdc5b5f00a67060124393c530bb3c9b6160c395c6751d089f50f2b104db6f366f05f7754fe5c18cc62614dc17fb7edb63c272abbc0f27ea022f1bfc0a156273af92a5be388bfc1b0ed93191b4980aaa4f103837c5723ef42d15d7a64de71621dc0b2880455a22bf2c7c2d4472f6ab200295e64ae15879bcbdb0375f10221c428e1b328dfedb224c7ee6c6ad02865615b8d36ae949064c730dbc82303f3610419c1c4f7e9e6c6bd8a1ce5b384a39e594b4d79e3f31dbb0c20c513a9a3458d0f99aa76456e93da3d00a47783188f8a744da41362dce573783a7a58010baa92594a32779db3ed1d5a3e83b8ef50461d8a3c425ee6d55e32789d0e52ad7f5c2225307d7772ca9067667ef13f0b74d3d62e26404c71dcd37b748a97c4a341b9c3c7ab751536dab0cfaf99f2dbb2ecebce4235e3504f525d075e1ad253e789a502781db6301c200bdb0db819193bbe4e43652abbaa31df29b622c87a4091761a6b00bee7308895bd8788953e38f02f3703db93259ae179b348f32dfd426a1a4274de955c23c451aa8bfad4ddef21e37a028b2dfbe4d562f14ee05b3dc70d2b1f15bab5cbd97bfeee7c3380140e806011bd74c53ab3791eea4488ec382dbaf35da744d70c1ee8d1938d0b38561e258c19b11ad2932de8fd36631a6a8ac1553bc9461eaa914fe5b0d9b2521423d9c41a15004f59148ca90dbb9e31ed3a7463d0b2f5c44b07ab3abbe8f53e8ed028b521703c4f42654b7b9d09b867ddfef374574ffbf9b68a61e189417b0722e937d609a17eb0d59e0c1267124cc86e622d1aeb8a6c1476adc3d5cc060083513c73c042df5ed72de8122cd045733015b7a5afa5538f3f9f149b059cca1f31dbd6bf34c91584521fb47f03d8ee4569989b9046917445d4435b638c1d9a34353cdbff7ec032b983d27cf2a67faeae531ad696a3b81d9b3aa1dc7da339c3e3646e6b257e0596c51c38d7ef40f12f5c7f8df09455df0f697ae56079575e332dbf71a63156a65460aa7ed156e8a13a45c381063b4daa50f699dcf768362534926bf1bb28b7d2714eacc1078946a3a072b41a5464089f9427f251a09448ad4612e36a4e60ca9186330ee5e96abc5f3b5a8418458e74a92e5b466008d37270749661103a4c479071d136f152c78262d5d1857501586c9f662e6d94e889723361cb399d8090871204279487706c813d9dfd1c1321bcc7e250e08006e11dcd9390e67490e1f5bc38031b49bc81a7ebcb0f202912b390666c526e27bed9bee87c62ffe995127587738630187dd39a272f744947e8ebecb2956033d3eca86d02599db87c97ceee04aba3851c6f55dd972e138fcf24e4683b707845786898998f5897c25762f639762ebad138037c4c8dd339ab93d998770ff89e4019795871792178c14e62a55350edee6ac45b89919ee34125eb57c0c350a05b85b00ec651c260e836ef7e9f3a00ba5794ceffc01eb00442956f0811679433cf5e937a6b87668155c5de388182ced0578ec22b1f4d0c57e32c82f898ac9451e5d52bd69271685f6574ebcae20dd0043f895f5a43e3a77a81ed0387430670f368dfd57ba4c38896498e8da20413cae9b50aabfc3a2d0f8a2892b9a752c200249ec6cad44d6226dc21e955a16824b928a7440a682fb7003bb4c28cb0baf3bc6df2df392a9e331f18086d7c87c96bde962a0d668ea1e24259e5d2815708149872f8b901442a06de0613860942a5af508a78877fd0174bb3e0e0603dfeb9bac382970f0ff33dd40265559cd141a70bc1fa382279ec7a16fe1ea56f2e32f0b8266247c4464566e015f41a523fa732341b0f6071425053894aeb136fbd5df217b00b69f4c785c15083733b86e88820dddee5efde37c44f44db39fa0788766d34e5c272c08dc918fad14f6394601e57d30171ceaccdd724781caf6250a94788c5d6d432ae3a01dfa5a67b2b7bc8437fcabc4c83cc496cf23bfddb109356e780fb73719bb27c1c5c3b4b8f10a27890f8c573dd1d764c47454296527a905c1e3856105aed509d54350cdcd71927118ae4687c11de3b2fde872ed950f5db815878713b1f4a0db674f50bfabb9a1325dba7f45e2a9a62c8b2e546d14f29d2cb1ddbfb8a364a143ebdc83509f07e7fc6c46c750b481c8ba21c5ea0310f12cb745532c52c1ccb733f0d4b0ff58c3014a476fc21b10b1fea15501a45611ccd5b05d308b37ef56ad07ab16305ae328d8df11ff8d184f5b4b310e9916716f32cac001e9f4d5413801c286f048b6528acb06e5de9fb8ab2e0d04779c5e9afd1ece08e095bb83ddf185423f575358d2460cc106ad4fb4e9bc47ddd97a9ec912ce02d4e1840d0d2a7b9f4ff3932001aaf09fc6de749fffa5779155bc60677a8d27a4ee2cf826141db2b358a631cff50e4bd84adc57ec57bcc5c027c0277e5e790faabfa5c38f18bc061ad17018a647884b61274c6c3bd7a0ef710f20fa25eadfc667070945880666197c7a948da12806ad8eea530c78807c11985c59b6b40a27b347d95c5569dfe7eee1e0f6399bef954f8b19cd8b5222df9805f828abdfb58e14bbfbae754affafbfaa19347635c9b1090f8e1dfbd3b05e43c5ef7f8de1a0f090a6e4a4ad864dab445cb7fdffd60366b8e6020ec654018459315e598d9b44e88af40e0977721ded26e6362ae875e77a97679973396544823db685766189c9f4052b98811bb55c2b17f8aa9b689b83449f8852a312bef57ba2783ec913f07529d590467b8e2450087c3c04730d2a1716258823ddcd024f01e0fcd2823178029184f91403b4ce4fb1d95b4245559c19cd4260c823c02e2cac6ec880a29c126ba5d07912e70b2d5686b636edc77b033d3e1dbb0d18b6b39cf8ef840d45fe02d6ba07fe8e86b54dedf9be37ecdb90a284b2883b5c35a23f5fc148043065dcbf09f8717b1c2317e6f0b5b2453113e52adfbb922bc02442578c53e8af24355b58dd7950a2f90cbd576e05090d5e4d3186f613d7a9dd03c83534852cae045dcbea5dc8cfe202c06f06f895fba51dfc3498172d0534d058526c64b92d66de87a5238e686359863ad56e6125dacbded0455338c4fb0c52921036a45342fda74d325fe2c0f182323e2f52e979004ae52a6a16595c2712f1ee9021649929acda206de4b25247f5ee95f6434a37074a4cb9ee2f9b7322c05a9445503ffe6f4fffa67e39dc9ff5473e97daf9af331b7b3fdb3aba686fe830679d547c10d178da1931520f34acb1b8ba6c2385a92966c7b389897a774e430e06935b52790500cb426d82bba29c69ac759638d6b104959507046d26d1d0f984a83f752e9f623808d8010088f5c1bf46ce78178c921d29c3e3c573f4690ccd814ba4b49c16a57d0418e8767524ce298a09457cf339915d10903871b080ad4e965330d6383b19b2ffb0e892d553fd99d7a5f87913b3b49641323ca89a8a30a2e80ea75261ad86af7b938de44e91f3e2783b6d2c3ae46f57797825b8be1c7fc36fc61585200adcc937900862ed0b9335faecc1f89258c02d6ea0e0c0aac7216a066e19de199320b8e2cc522a00e0f2b956740353fb391d5c64b8514d1c76caa32a14c0ddf1d9bf263991b0534023f0740eb71ee123a36ff3fe2f93000f225217dd0763d715a86bd3cfc17fc87c83f441b7e112da9d5f1a53f541a3a3d6033c101fb709901625be708b2cfd2e3a68cce9522adac7fa22f927cc856e3097791ce84642b2a6b916291af4664839336c41337e5e67196fed8ce09ea8e4bb8a566ae3247521119886bf02de0295d937802c79675c3120c7cbe2f426ede71645a75471c4772e71e591c5965e4296062fabb18a607a4af4df2a577e0d9972e7e20c2456af18319a31be937b8ed6fe85aa0ef2fea8b6fe83a5f416c4ea56c0776120f3547c3ae923599605d439e83ddedcbca63712e871c87633897bc55fd42ad189177f2d2ea6c4ef10a52e5e5755b3009c0f7059482a0a926c76ee33e79436d93b4cecab9960c77fe8f395373ab45b69854afcd6b68a6a64d17f485e6da23fe99511ebcc0903f4f7d4e9f079cff3bf253a40dc0a89bdd6d13983203584f60370e09edb32aa353b4e396bec5a042fea3529048302c67742e1a398a097e5f68dd620cccc3db9d8c9a42e1dbff6f37a2e9834901b52e4ef8efd4737104d1417af0e640f0a295aa08026a872f33ab77cf0888df48f7fc905ebb301d626a31477ec38c98e655b5fb405d3fc387b20c4ada91868761af8e48213aec4ff83ec989b020f12d56bbbc5202f3a73f6b8d99485bf31e8c781da198caf6761b87f5b27ddedebd9af1a38f230be83170cb149cced79da73f043309fb5dd790f1a25bfc4ac8dd38c0f02dafbcd2a11362a162861d1d4bc0f2171c62e48af7365d840cfcef7b8e1610717fd2ed599db31b3838055b169adf668c1ed3056c5e55e16048cbf9ded4f7b84831b477140345e0715cb02ed9b521b5e4f13e702118f0914a50069dc8c7813f22366df9cc23c28c6ccfb70b166a46d11209f0752c949596e37944363bfaabcc27bf243c09571d3259839471823057aa87c214a05490c9678543ccab3eeda87c7f9c5d486e4340af90d1c38e5972c46aa542dac831b795d47047eccbae44902a23cf686c828ca72b08e2186ec28af41f97da6c0a756c5a5de2991805d557803d00d5aa98a32de1451abad76aac1bd295e6c669f90616967cbe193460df12248434cafc3f67b4d5821b1f2ed444d7d8b666e7dd98c8b9b16a411c31fc4fd2008b815a3168d2a1d1aae4a20bc09dd459222813864e9aee93063e17651551136cf2a49d7b922df88ba72c297f69125e4d68b25a87bff6580515b127db3fd303ff63b9e9488b6776c1967a0182099bfefc30e6e1a9bd1e7620f67c4bf4c914992ccc352d29fc8ad356e33681252a51253ba555fa79c379f82a4a279f915b9541a39ce26323064bb55ca755ea783de8488f3f0125d489e3176174d41ba7687c2cee5ee37733faa7b31186ed0f017d0cf030360184c6f0f22bc30e5f988533e76f2567b76f2210749aa7380c93ca7a705cf0d663e6c22c7f8505612062d271b6cf11a57cab03f0d0617f688be057b1e0175ac7bb31816b4201ed12e8a41508df193561addaac8e321b1382725a3b81bb5cef7edcec423dde2f4fa49df9de6bd3a20c05c698fccb5d593ef959e87d12dea81b6d35ddc6f50b5c66a1edab0ca024255b79264ec6e6be774fc2c3d998d4b2b6d7f4abeeac4896cde5c1eb4c6b78dc13f304307ed90cd17ba676d4f07870221dc723d1aeb3b64944153fea78364283fa01b144221980558f5ac93f81b855a3ae8736de0891a1faffcc141a4935b51cafba8af062155ccb275b22136e0224f4d196e59ec661e6a908aa14b393c06f5edf3a058614ccc148a903e27b636e6b9cd7598df62d478b06abc0c57feb17b88fb7bb257a5580c8e00296eb27c110954240dae9a961cfe2d5591b25e81e1b059330e21183e04d195a529a8dd95d73555dac82275da3f6546e116ffdf4d49f946f46dd225d5a84bc70435697416248afde844617c00c926a10aa08f72e43e2021a9e779b946a910db23e0912f35d61e3b6a5e3e996331da20763fd23ea4e1cfd3b0c975061e0af8f69dc4e58c022e4e23cd400b840feed6110b1ffdb46d2a36a6057ee1a6f1b32bb0d659307a6cbb9fddb27c105f6619d40fbbf788af08bcd16d37671e5787c1f5b33fdf0582f382066ee02bc6e4114d4bc35fd102543e03987773591ea0a36904b213786a12210381b543fa633473fbea445174dc75b88736dd823e371e70d20682907e7d493fc12c80468c63f3dd78faeaa6d0536770cb8dfd0bd392b80e77a9110604c6ed9d364786d730fe6b1c9eeac681f83c0663009e7360e57fa33231fd800723ebc46e1658c8d1c5a4bdb56b536fac702e00dd096773e8305683e0e2b1507cf40d183c6f4bec4afeb6e4857583030c3bd39db068d2d00f7649fc332e22428ba9e5e3af48b794b0d6b53ad824c4a6f511549c683b9062de18eb4998058d8732bc48bf6fd0ffb902daa290c190f89117f27f22f8bac3342fa6b5ae8b0f842c2fb69b8982466d570891e98fa3b297fbfbb168a8f24889b7f7a2f97bb4fe8046afa783b830a4bc9a783b6c9fb93c3bf09591cff5c8c0788d79f50a51f398007f5de9a5ad4e38ae06ee4bcf95e8c138e824d74b4949a05a3180738dd034f4245656828a2d5b17757ae1ed47439eacecb877f7275effe84b47a3eb393b934629e787d3dd03e86aceebf63055a69d58a0823bb2a9f061a127eb467169ea2353764f2026125cdcfafcdd35f47231bb4f434dc3dc9ed47867a5566342dc145c5d79064924c77ead3391336d373c673019c6fdc87dad185cb5b584c0163136e57e6060f9687a13f1e346102e4a2ca8db1c2e61c43db8f57a126f4366ba7e35037d3da17f19d21461b86dc46531c6086db798d86521b88346776408a2e63ed9e0d5ce89dbd050d5423677e3e025a2d39f441d305bcf4898ff56928dc625a1aeb7e1ee156f1d437859f9b78d59672718848343416e9f5c978a79b5a539bc9d73764eec96b7660989ecf8c7f07678d8bdee1031f4fb7a90174f8311a3e92d56ffb738d8f0ed5ba95e5110c114f1bb4c7e0e20fb9f2cbe0e52e4ccbaf864f9d818aa94f146ec018661f93ed1b0fd297abcc8990abc360eea2550d86e4642c972931f1c0a0b71698369c32f74a0803d409a9ed5c2fa262ab52a10854f26865391963a4ac4364177fca6bedbf0897f64b0406705114131a0c3ded606c829ec6b43b94b7a25e027150ea70ef10259a3377a4747e5942688550550cf202a83a3511e54cd8ab4c5061a623db5a229a26b1a2fc1cec2ce8e5be1a163df44afd78ddd672f185902fd4f2a145f93765df1d087ebf86129aed838219ca9d46dc5618bc0f829e40af67526f4be86278be9ca419fdf1de283de1eb141dade7f5c21bf8d1d830014bb5896dcf2833dcb258ef3768ac063329d860176865a28425c0aa2b8a8edc8bf0ce67a0ad80798747fd5f7c3607f767d14bfcebcbd50284fd5ed91e095cf5c67cdc96c3c8ba7fba875c1cc886769f7f71cdf6d7fd621f4c3c39b27d1133b9267a309f76b9fb89a160ba2ab0f1cd9831cacb16952b19501fe80351ded33b147693a26d5c6e27d3f25e651deae52120fec5dfe4841e6870853b8ae3496c8e41eb5f85a3fd2bc275ea6a8894fc14dd48e94f7cfc033f9ca4298ed37546ea0233a704e4402c739bbaa120a8b23cc30b6c00e50fa81e96f624ec6f19f5c76ffe530dec4c675ad346b69c10f907cc8f7db9055df87f6ff7c0868f8b61fa2d9709195fae77b109ced389054ce4dbe85aeebc4f6761a9d8a0a517cebd31bdeb1bdeb6a8bac761f99935ca3b425969750d45508a9ed1102778f151305575fe6499f9216102b41ff295d48febbb4b9304045685cc1bc0b97b895fd7b0461e0d839ff895c0865534e8eeecc178529d9dae3cb2ef087b8d4ae4e5ddfc03df0c428a5c9289448e416f2b5327711f39fd5b1b0551f9374f0b5cdf87fd639cf5c520397bede94fe8e30dba6b9f517e28850fdadad9c52794a60e9800ba265ea728a9c8f1b1ced6f3f25d162c896edd79ebd5f692e551dfaafb8c6f4124d9083791c5baa20b436489dc16000b9fd4b861f53ce4c923f9308dc672f0da6ed0ff419456800c290febd3cc1e2c7c529b9b7134372161bf859514bcc8afe28688a44c359076c1352077fe8377bfb46027ea67059148f09cf2b0388d5393284954cc96bd2f32bbb9bf58d6d81c221d49e722dd7e50fcf1681edf3824ab2ce41e8319a1f589fabe0a71bec4659a5257b2c2dd646af63fc55d3439847eeecc219ae0fb5313b433c7d30d4ea1fd99636cea1d1c23b0ee9651e1e988c4837655aaa6e37064bd229f36d2e2507c247b070acb51a2b6f5fd9586f7f3e103ac0247b9a77798b8bcebf9534a9869bcb3211a51c55440cc4e28538ad1835be73a18d0af898f567a049308f00ef103db4752d7bb8800fc491d6b1655e555151dfdc188dd4c3a344e8588eeaf2983a8a088811a1943a8e1db11d80bb634c4abba6523c7df35c75f83afe01cf8f928f544b4958329b092415b0b1e3b7fad137163cf0a48caf32e82a0862bd0167b2f323da671464d61ebd6f6d0ca639819c46d107331317f1ff0cebbc3e67f34f2927ffd8598fe297ab168011f367d62187e6fca4ada2758a6a7876f66bc4be81bcd8f11e92898c21fea1c7275ece679c1b72b2c8440d9556f79da2756db5f9378f77839dd1119539cf35c8950f3ceec7d6ee043013a82bb1ca91c7a441ff6de1d7e0acbcdf11375bb5ff64c096893337b5e59fb5995595f794e51b7007d1066ecb613de8c7d9244c644c7d48f9e221516f9f22f1e70b4b8656021d9ccf587a89ce83a8f2f973bc46260d10f793546b9439915dd70b854ca2116d8e618fba44c7e34776ab8cdb7e030b1dcefe30325cf2da7f4314b44877f19d902c8e6cadb70ee67e6c349f8d343468c4ab9968a96eee4eef352a4891ff27765efee215d49c5d29e48a0cb237e694b2d1f54f3ad5bfd973e2b0fb3e1f54de67e50348cfa278cfa0fb54fc82190af9d101ae226bd3abb3eaf4475e648c9ffcc612568c3722fb3fb85f0665f822b78ca1baf693cdfe4245ceaa56405785c0f8599233069a1acbf26c2b64c8c285a40de88b151117c05876112b1725335daf06b50b17d1bd312c46fb7c59c5d640760b2d1fa63d66084d42d280c568fb90e42bbd1c74239b9c82f8ee64dd1e00e01592beb63e948f91a10c02e6d20e71caaac91810233e2bdee611781475e70c57669d2763a7f56083f657e4ece0070e2110efbffdf8393d0ae03dc0137d6abe9641f6bfcfe9faaa7e364253fc545646c560a263e172b12a19b709a57e9c01edc09d1a62647bd8cec221a5ddefaa4782cb4a3f9a8940f3ee3e64e6d9c4786634d38af2b5e55d52001a9ee7d4ed63af7afcf6a9ca84fec5dc321e015e1ad62723c5c5fb9f55d4d039f46c36f1d0d37725be147f74b172c3b516999300b0d739c741463f4ede15a5864cf9dadde0cf5f1668fd5e415daf0ad32f4f1513286f1ebda8c461b0cffb1d55ec6c7dc772ccbb2f9e86a1e14bcfd16cbfdc5df2a58f17b33971a19dc1bcc8a3f921953c30a6e651ddcd02e997ad664f264c2a7b37f6d1a1b9a0b9cdc6c7bf07d0416f27a10f9ebc6499710181ff7df72229097155f7378a9ebe76b23b8dfea917f00702b14b2c02f491f6ae5660a4bf46b15051904675cf4bd6b156a40320d9ad3b07c82ab2269c510bb8542a9ce4e3ad2a973f02822fa081699e967b68cab88ca2c8f077e8d5f0efa29ab2184cc921a6113441808dfab4b1725b1f6956c80504dd9a291f945565f3c1393f518f2c260c57fc7eb2c87c6b63d126a60a177c5830a22d3911c18b955f253ac34e8b2ba4e995fd54a0209c261ee48b6f899d17c97fd0535c7abbb596ebccb99e5193690d09cd7e642264fc34190bf529201a12dd2e196dbacb1cbc1fc9e66bd56de55d905bae69a10fa46b075372508d9aa3d808f94497b8c2c94d40e04267a1444e8bd25cbadf502ed21186e676791c3cdc4c4599901851932820b4ec4af101ab51d0da12bfc3e7ab9dbc1ccae679447bc173fac657a78a5a397a2586af34ccb8fc116b744f964cf4530c1f02577d54a5dd9a71a8024cd058c256dff15589cec79e461cba30bd11a4d90ce1c11c5024918d01466d95c378ed0a2d2d16150fd0a8113d49d0776e3b1661cf19b51aa1539bf5e1b0dc943a75638b0a4f06840acb979b5704f4dce69ed59fe06d30d5f8f008901749b3752d4772959f0c4d068ae86ac71f1dc8bd800e500f499a353d57e190df10e091ecf8e45c2546def61284ffc2c8c58c329df43c0f31ea9cab50eb39168edba447289887ca7f8cc47e4a9a85281f2db877c0c7be1127728c366115ccdc5ca61ee69b355944f2714730c3c6bf40a8ad9de6fae5a2bb11a17092d16e9fbc6fb81ba7c6afd8a046aa3e8e0be58897e1b2abccc244d684255b1b5e4e48a745a38bc2937f4133f51cfbe0becd5cf30f4de8617393ab8c36f65900957f30f198396110332a56f9ad8f1ef55fd8c4fa4f69b0a21ffa39dd1e96d9acccaa6bc7147ba8d03ea029316df066d1d794e8b7c33a2e9d733d18b31506bab2cb4c66c7a43fd679305eb100ebae81e8072ef639103b5c6fd287c3c2894d406d0f1fb3351380d5f486ef64e634b7bc45704183e136e2788834eb26560cbd5c63f2d80475410e2662700f2c222ebb87bb6e2fff4923d6982461c57020fbfa365170566d8e6d396eb64eb04ba6cfeae124216811d55f108271ac4a559735b64f4ff30e78b5144798111104b7f1283950d51e4d85852f378632059ba7f5774d4fa9405917bd55ab5a48474dd8bbbc7999f53671b7292547a530694520c40dc4350fe3a40a3034c96e39b5934cd8b780ac836e6dc4160b4f38637c120a3c1275b143ded1859ac5e663256ed102f06c683c2511f5101b95a695a9ebba9ec59e3d8f215171fc0cebeceecac41953c7fbd4e25792a6b0781f80f990846019a74a130d3ec0b9a4790b74a8250295841ec45ec9beb8aa9a7b81fc6104bd2a5fd9cb63f42afd600851e85e08b48d1d1bf9d4aec9084ce371210592245645214d39bb4f9451620494db2ef0230935cc530c0ab06244cda46da1e4df30f4c155b306aa06ace5785d5f4d81169423b348788cf4bec5778cd669cc2f399e1877c93886df34716a8145621c883ab8b751b5d4916f1658e7b39cac128e148476f4af78497dd57be76ecf65e0015463e1e73c47c0045373225248fa571afa8cfb12c3819602f8aa5d63cb5e7ff8641419d1ff60f84de1b1a64cd87ef39863e8de5718a0a80281f227dd28cb1842841c165f1dd7ef25dfac307dfa178ae04c1f369c9354a652efee3d5c112f4e1a20b1c302f88a5bc75ea0a9052c3e29e041ed16d7f9801b22f85ce81ccb235e62bbcf55757ec9a9d32d62a7a39beff38fce1a247814146d8b77e0ac29195d84d14b33b3252e2b5dd514b9f0a096db12b4b16ab4011d68b41b2daeff6cd8fc56d38d858d38aba0f2fbb1b2eb0ea3a8854cd498a8c46b1a35e516dbb01b99443ab2e5d79839491e2c961228f701ebd5d7fb9e3ac8df52163a74f63586b06c3cba7ae58395f37e4a091543e7b3b1f42eee9fe21ca599fdc1c57c3a41c99554b7a52f74fdf2081d409aa368b5838a2bc24ce884af447abfc02665e2c48f28f60e2ce41e2098b341dfb78763012fcf0bbd6671d83a8d8577b48917d0b5dc98d5d09a22358a103ad0cd671f2e897e007abd98ea69dc9ba24b059ee23ad67f07d7abeab3ee2a5471dd486d60fd2110db8a46b49ca0662e70a054fde64749cf87cdca56b105395bdb2849662e801304b912e9c2a1172f4b614c20189616c59111e32cd377609f343f50b363959967ea89e28cf67c84fa73cccb817a0b4908ee02e516003b98c83f88e22e973fc95820d9ec8c5628ae7bb8ae3deb795f1f9075347c9f22394d104c6e2c7e9341bf7d0e977d3ca75c044817479c1c75e1f26a1f99e22e0225888af1e3ad7622b55a5180395d3b09da942e4df800907d72714a051d923400b6d7b449cf1e8361139e7484ce32e54e2258bc2715187157d4651cf4a63ff0fc2238d82ef3746dfc2b7d569c8a7de959b9e2762d5418de52b111746890800876a173ca2836af4bebe2dbd07a5ed888af485906d5f6a7490b3a7e5544efc90dcc20ca09433cee5f42f7742b2ec6eb4c6a572fc0eeddb7a669cbaaf2a7410758dcad6c65332cadc95d28a4325eb07b931f130533ba7033cbadb43f1e7d748f4afb7c8f1024daa39501cec519b1af712c3e20560757eaeec9f34956901a55679601a01d8aa25b1dbff1a2f335aa6e546432cb20af00c03bf0b72ca1045f24b88fa13c6ad18a38ef936d7e8df2cbf30c2a2adc26896be4a3548104b4685513a362c3e81b9824e076e80e047a1f5b80bbccd2f37a1462dbe9ea00612a30fd507029c35b1bc1205b1a22018e328219c0ca7577665dcc6ae80b92b45bcceef42aaf4c20b5e360c031c9c21f581d98cf768ae7252d35f8f4a5796982285e2876ef2e5a313eb5057934893529d4fee9a098852eddaa5b278a734163ad83d579eb579c9948884797f6417462df8511fa1ba74d1471ddbbcbab9dd0cdceaf488317036764543926084f5f5e9770e629fca70b1a899a52da8359abeaeeeb2d208f04b9c733934ebcc1138033eb685db8ff1d96ed06afb5291331544bbf760f92a7d913ae291311f361f53a819cea3ae2edec8f8323282cc271ff7c8a65fb88d9bec8e600436733d6667388866a6ec7717ec07501be69e576cc9df7f30b39f169d1167b03a139a80bc6c1ab480b31361b935b618ff0c7eee8856cf675c88b0ecfe6339e07b3b25f4284179933c67c6999a1a96400bb7f6c8c59a5b30e2e72eadccf6fc4217b7ae4535abfc35068d3222dca5f5e003f6d347801ae7e94eef307719994143b2925ab0e6e1c95311b31f3daccf483eb1628aae6844cb6177308b6a1abe3c547aeaf0ca5911e495cd82e0c7df79277040b2237e98e3e38b689722117a30f27c718b6a171a6a01d99a6fe436980c1aae104b0a6af4653340c7833a223054be833ac2e541fce5a390acb2b4eeab830bcddc57547b58bb227166a095f5001228c0768e6b53f343de6e4cb0f7966426d0fc7fe9be82ce3c4f96dfd2093ab68d8508fe22641a0bc001cc001b24dafc7ba30496742a2c137cc33499430628291fc4b539f58e4b3fd6a10538061198aca920aa1eaf9e2d2848c9da9282f05853e6b4251bd6db9e69260abeb892bc88db41c075458cc574084fafc381a2e258cc5141879c1e09af545f4c26e8de628d3951f2deeadb34c8eae42533275cbac76c31547da0972c98582e15a2dcc9b8ac95715b125e9adb761a798be5bd9e6970a1dcd0a4faab89bc48adf34c306e2be28b9b22ae6612bdeec52f1e9ba02a91d2ac3292397d818df08ebfb8d58c666fd3d37eaecad7ccaba8b84870c484a5b8de7a62028b76caaffe1dbd303c3b6b04d0479569565d1078e79f452f419b2f7cb359730b43fb8f1d928094196b79945d96b3d84768d874dc7db0025d5cac2ecd848ed36ea98be83b2425a52fbe64cb42daca694ce91c909640c2d4281a8217a4805a47ac0b9ad594079497661a584eba90fd2ca2082414c9708ac3a6c688b01d3e1f16953c5e720779634b00052c08f45d23a739912b154358ae15c4ed8ad950b543830eef422da7d134aee8e5d120ff04afbf29c1f4f2c2a1ed9ba9383e973936cb61dd8e3b18dd8c151967739043ba0f3b06409254d1bf0a90f732e0428802c40e93ad9a87e07ba0b9d0705d27742024d184d7bc3de74cab69c8fd831fb4c5134525e32bd7036d07403acc29a85f35767e6585808fc3b03ae6b85d00e3010c89b4207ffffffffffff0fab1841a3a91b791394524a321a97c52858a2efe6769249a6241341eecc08adad7f9346026710a20ed10d6869d151d34b3122af303aedd462b34ce7776130f8e4d15d69f9d5ff61609cad456bad2175de24fec2d17974361976e329f385c2a546d1f4d6d6928943472f9ecf99c3633595509d213a78b1c71cfb3689066d5ded42f17937b54a4b5d36d24562e7558f143a967c542edcf056264747e5a51045072ed0f77d2fe7ea3d4af516263db3cd32238476922d8c1e852cdd51e928d6ae053245eab55ba54a7c6e4565bdc075d0229b6f21377656faebbf8e59ec617a93ce6ab6d359599ce44de9cbcd2b1d45ac2316bc768e9767a65ad70f8b3b99f2fc79a59222ce1ce878c54146cf23b654ec6b76c5a3da2756e966967cad58c383d6319cea86e83491216d04d1a10a1dac5046c55f472553a995a12ccb8a1021409625c84a1b40e0002203206f045931e245569024e9419fe19e46fbb23042c72adaa442a652b65adb56216e048923a5053a98a04315eac9a4b764f6b47e93a42c42472a8cfae942591623434e14a10315492db62e93f24fa1d4ba4c3c29ed2e539ae2a85788cd1f648eafa65268a7e975cc5ac30a0f93222964ac1323737fceef083a46e19cfb98b89451cf951e3031006260d00122478a0c3971224792ac9c10418728d659264fcaf54c47f428cbb2868e502c3d5de924479c4a655249515151c111748042995feeef262d34f64f84c842c3083a3ec16c909bb66b4a46ea5196a58dac651122870e4fbc52867fd4a899ca64946581838e4e9c65566e69114244447a8810214ea4d730e282654122474a0aca1b4196850c3a387169873791bead62f44d34a75e87af10b2e4b99a48e679872ca1796384c4404726ce79b4defb50bebd2f4ca8a4ce27368f36ef4c5ec2b45a334c67c8798c96e0cbc64fcaf44187fc54424d4f8db9fc74a9112516e1c2f588d079cd9a8442e8e624fda4688c8622061d92386ae797c173f2332d83b22c2ec75a01838e481cb3caed13d912fb1b4898841cdf28f6f7326a8f38a97e5321f24dadce39c2e0191fa356ef4eba6f84f13676dc95a3293b464939599c8db8c0481acbb22c488ea4a80e46dc1a4abad0a2eab3948c0c592abd2c42900461b501c4880b80b491216c00016276e67036801c152041d0a06311674975ad42cab796af08b5d5f46b54425ea57a724447220ccf9f7d3e4735a26a94655916212b100207106303c86123c891d5c61e5945545256968e2ee840c47a55dd771269eea35941c72194a22683ba4c5b391f946511d2021d8638da86eaef4669ee1c448438915d4b6525082a004284c842034810349645c714741442a5a773b2b7fdacc746599647e82004ef652333abcf2dce44591621405680a8b40124c81973f0b204d101091d83388a7a5dea33c9dc394259966581438720b0172173bdaa0ddda9402c64c7c9d133b2aa3b28cb222408117400c290fe1ded6487ec6da12c4b1174fce17cdf2c777f347d372b485294a0c30f99f9c657b325aa1b4559165739b22c42548064991144471274f44139711eeaf4649b28757041071fccf5a7422b39da315419007923880e35e8d8839f753ff68b33cf4ed2839b5fe23eae884fe6c943ff4aa510d1d0c95b090fea509fd545bbde34c23ba442ae09d92e45477e3aec60c728f1f9df9a9336b58e3af42f4a79f699acf61e655952564e74e086192478030741d8d041074f93d4a23cf7df681dcab2a0a1630e4b659bc3bd6edae45972403547b573f1f03d651c2ed36154e698b46b14c1e1ba91194a55a45eed11a20233902c336a1084053ade808ecdb1e6366e96591615a0a1c30dd7471b29f54fd7b6b60d6d978ca9e64cf4a98c0d4bf9a073f7cb8a2cc5c8c91ab8d916ffc2479becd5708e615ffd5ba44e370d66774ca765fb99456858fba627f9cca5a36740ae8dfc50266e47f366b06cf5db5b067db39701e95a4773714f152a198c953f5ae87852bdf131d82a2e93072d5d3e6e31fc5b1a5add9c0cef86213b391f7d4cf5930886e3ece93fbd51b76ef80b86f8a8395ee8eb9c5e507d6cb614e5eaecd4054d4729f5780a0f912d17f4ced8b1d598defeac2da0a1b4fd3c8e87c95ab0be94f04f23bcefcdc22fd4bdee840caa552c986ccee4c43d3e2be915d6789d531957a7b556f8e42bfd3243dc87aa0adbca0deda29ae93145057537987a96cdec7b0aa7911d34cdedbd50a5e048a5f9c546f728cb28e8fe2e330bcd11a5935030c455ac0999ebf63fe1d4d37b725d654eb513d032eb2c536abbee6a021e62262f66d4f52a31c112e749a8a7a6f27809fde908e129a3ee9712fad23943535c89d126a1d73a8b8f1a29b57ca10e2438badfb4cc7acdb2e21174d73fcd26b5e3bbce089c79d0e8a9c657fb2d022a85d49c85ea7d9c08abcc4a071de3271fe1102e1ddac1f5f9bace4e08b7c99d8f26a4d4eee6b883cb0f557acb440b823f62349c76add9741010327d5b793a4d76a97f90329d3a9a4de44b2ac99faedea49672469667e520ca33a97efa5c2d12748852b98d715a982ae9d252f99690dfa1e4036352ae739fb2f9160dcab218712227ec3f3859165e4e4ece3872741d5dbf9072a5f638055bed16bb1bb19121cab22c8b902245d630c18311b316795af9cb106ad445524a735699ed83d03909e5c48fccf18620882cbb4ce555e7c79489b22c3f90031072b06a737337aaee2f13cab2fc408e935f4ee444881038d83091430f50615f1ecc6dfbf5432e9d9e9d268390d351d2da5d3e69a98c7c711ca6d8575b95f9742e144264195171810e527c525f74870b33539a85b22c1e477b3beeae4f5cf56bd01f455996388c0c9183e350f1223ce84dce7c860c36deb9a22c0b0e38d3a144b44be14afc44591622cb57909cc84124c92e8b103880b011e406bb88cf213cbfd6f6742f537607ad95d6234fcd03e4be1cd73267c40bf584ef8d5be9a222d59f76a06e569fb41c29e4cb5007af9bcea9bc5b88689603e63ebc8fd67c327c83b22c71181982442509bbca89c7d12a10f2cb5b00040e206c005949d2c6b2a8b81c2aabc8bba1c6b278a7185996653161020e10d3a27c3c4bc70721376ed07cc8d8184fae8e78e4b0811ef38c2eb1ca33b32a480b6a70876e7f11d265468fa5d020adf5ba65ffb59d141cb90c2d59feadb96234834c8e88c838527a9ed514deb8cc663b973cf35499e56a3441062b174a745eccd6064fc71262c08731d5545fafabfa0e1203187c723d87ce701fcfdb17ec3f762e364821e35e2e403e988796c8925fddb04d0ab1b2c25e46686d3c72c52959426470955b608c11e7324adde79eb2f19bd0a75fbbdafd158a8305af0b21262b6583d2a6654192b286996450525708f5c247946599c3e53859454e8da44a5fbdd9ebf639bf82de4ce677e6fb6c4a2b0da51052fa968837211a15ac8412fbb91a64997f29589c1ab35561fa5de468742f3e0a5fd5e432b3272928386730fd39a70bfd231b671cc2e3ae90cd2bb3125184c491643d05cb1244062740cee8d0f2944ca934ca032800724607e030c11a647c9b91e7b59f425916214284a4acac0c5959a3488a4a1aec4764b02c4174304188397a094d9fa9699b8372648e9565112224e58d9841e4489121419605c909978091731753424a1bf50ac2ab4cdd992a8410579d80047dce5d7a7bde45bf44b01cc694d29fe35d1fb5ff943314fdb72b36d7734c2d550955a1b931099dbc3328cb72b22c4384644a07fb20d5293de2d428cb72928aa4a49cc4a17272f203f7f8a993d6e1bfeccd1595121c5fdaf39acbd2d4ce5096e5e4d0752f5273da7de5ee41591664979fab961944a8bf0dcab2002145250e111c846d90cd778dbb5a83b22c2a2b480cfb19275637a60e13a12c23e084c99c15ba3a469983b22c276d649df80f547c59dc10414af6d9a9543af5730b65595090243972a292f2cb22e40c206f04618703082b06cbb22c270801e7b1d3fe777f8ef6a32c8bca42e1936511b22c2b459e0666ac2429b2812024006218f5ab5b2671215a465916212ac90682382f22474e8c0c591695150382b5fe96e193bd9629745096a5ddc807b48d79cfb93d471d2dcab220697724272b1ec7b2083103c992410c96a5484a1b592a282b08120fa49c24b40f744065ad7080030fd80004342007af254224e5031980000628708107584002660c295224a50315b800052230010948e00211f89524138040101ce544020fb8340107042105082b14684042338301cace89b91c8bc89ec46164080a928562c6029051000712602b292908e8e52a1d38001003c4c181021c241f208019800704c0810018060070a28132ac480a9fb8cac9c99123a11d6147923f507865ed49077c9c9c60a0471056910af0382729444ee23032248e3852883c808c2214d8a192b2b2246062868e73a2b29c089f9cac04c12f5002812920601653d0e14596113210300b2e94e8484101d24032e4c812808e32082000258f98051bce28198212c77bf3ae1de8200738b8810d6a40033866f0860c62008317b8c08d36d860c11a6aac200d15a4000d149c617202131308492507b802154a0e70056294ec8042004a1e010b329c51b28b39d882162525253aa6a00b3ac4616465213921b290a88800492df0505262420b5d9891865c5cc1641626b280c5492c605152820b1e64e000b7a0450d68918159a09cc8a20ab210428919cb196834925ec34ea5b3294450807e60b28a92121342a8e200b08884892a6001e400af404249091f50d18e640e2367b417596ed80f9645c5912451f123496450846be08853101942032244ce683f94949898a2dd0544889c515262520a13521849f2252526a3e8218ff01557e1334a4a4c44e14406f1870b458902453a732c953e7ca2a4c4d01c4b45e50c242525269cd8444949899968821099c0444989491f2c01884a9494983ca2123240627014e120205ff1152742e48dcc800891246938915d296e349114355c084382b0da40a2821494949850e2488a8a91191419b288a01184d50612f7345652dc50414aca1b4796ca1b5e64b961648d20a880c8f214a4b82107a7b4c0c80c3cc50d3e4cc20f922829318944494989491f2011045f31e183c923dec81a44882471a3a4c4c4112525268d28293161c4224a4a4c14d1fe46ca27c2c40f88c804251e410a429002100720c51e4a4a2e19620e97a3a4c4a410c6e670364c085152623288a5e26c10594113198282c61c448cb8a0a4c444102b2aeb48498949207a884a12222f282931010491b5294456e03c24050e6fc3484a1a2b4896b760e58c9423292a2f30b250de709594367a8d217f50713986184969a3a4c4c40f253fe0214650d8dff01f0c89c35b40640de1219d84053d9803052525267d2829292931e1c31ee4504949630e14ac220b491236da88af20711aa4700aa7d080871449a20257494141b2d0303224855786a00089abb81aa70656035483ab01d620d5206ba06ab02c480cfd6059ec07de293bc76a831d0e21296f648d20ec70a8a478916544888a6f911423de45528c204182a4890ca1819020ec700c89c34892348c24f94e47ada8ac17ccd12928585159477a8d393a05054284180a6268062b488ca4a0610610952488b293442599c3e5b894a487184a22e41345d8000a8c008501941cc11378d0510b1db6d0210a1d84d051084ffc2821231348e804194a8cccf1892d54c0044fe004277ea4e0009cd061128c2bc626f860084d40420b2504388319c840062a94943ca1e4095160c2194d10b232809292216d98c0c8103348606668c00c234ec40c6346092c608611278207c004144a4ace28d171868e03f0a204beb8431d2e60061484010b252f2881c30412b804194e6477889137809082c611242a093b1c48861c3903c990232b0e23448e141962091e25252b1052092225252584a0043128218592921223016a0441410ada083207197238b7ea79d8682eb5188540461c927a438be835cfd93145061c10dbe183d7fcb7dc8f11c878c34a8dec6f2b5d9eaa65840c64b861b94ae5a9ab690a4db7e1ec583ae6f8f277ad6203af414b8f31c91622af0129856c955a67274e359c7e534af5d1edeb496950a4ec2e5dbbff184734983e1ef75d2bc4d7e80c29ad4bebd7a8a5cbb03a1966483cf99af23432e75719da8d55a7545366869e0c8f72d14a8638cde3f518d2a6e3e6ec3f4d994528cbd22745c81043af4eae7e33d3be5983424618967db67a3a29d741072d18f20fb9af7d94cc7b5a5f685d2af9a9f255950a215932bca0cc2e956b14195d3076ceaeba366878f6c8e002bfe2dec5c488bfcd72838c2d28b52e29b585147a55dceb0c32b4c0ac10bbf51393754259b855caf4ce9c3273080bc609df559ed9b4faeb0a6815ba4984cca020195658ddcad0e91bf4f6836e04082927292bcb520232aad0ad8cda4d27132be76432a870d4d2c153848fc78da7609bb613aaa2d5e5caa4acb4800c299c42efc710ffaa9e2b9435c88882a16143ca92571135a22ccbb20841430e5e2b325081011950d0dcd52afb904208b5a22ccb0a2f329ea0d25b99a5f23bcfb55096c5c82272448d20ec9d828693e184a5e6b8d57962a4881c8508917582b272c49bc968c2f274b82bd3a763903b136ca9758bf29c5e02a62b362bc735e65742a24eb9b415217487340928b97beb1a756420e12cb959bf7c52dfa61d639dbdc4b674f73d37ad1859bce0786387313831a35e3de6a4b4a851961395143880b011645962b0a318b894b15fa8d4a13fcbc4d83fe824d3b58c367eda310c445f6bd44107394a845096658e4564598618c91dc2686347306cd99f9bf4a34ccd71b00318bf386fa6174ac6d8c9cfc10e5fa0850621a310ea42244407e99841022142129a1acb12040746d8d18b854b3d4ac4082553b678a1d08e6a4ae6b5d6f4b84a1224294478084358961347e1c547929c088101901604b1c18e5d1c74d8169d46c6a43683b22c4528429121284844f083210c418d1dba58a8fd8c350db649b323179f2813a7bd455cf5b870b78498af760e4aedb730a9ff2874b02da974942dd6624e5368fdf9934eaa054a7e27a94dc34b3c8616ead51e2ee342ad3c11cab29ca0204972645982ec988541ba6a258496ad31e7b2e093d2e93b53af562d130b3747cf22af3be9fb1c1629599fa56c2d56f9bf5eb1b6cf51d91ae5eebe488eb0c315aed0a06633e7bcd19c1276b482dfd66944b472a93750d8c18a463d69df5062f2f3b88ae4c38f474f1ba55cb12afcceba793e4d6a2e2d22ec484526d37dd4f1f4c6a465743461072a8e37aa4586d82095e918493985dea79bb38ee88e78501064adac146861872914ad4da933e798552dc52fcf6490fde175f29214c84e5a9592194f956ba358cea7eef8794d564563d8218ac59adef89da5cfa60ccab2c8c18e12c489ec3ac38e50e09f4748ed59ab2ba14459c10e50a46f85fa183ffe3f293fd1af92d246bfe73799f3861d9e68440915195d78ce9f74946147278c32d733a576914f9f13a6fa1291e94264f4bb094dfc9b3189e6f6f79a0d19766402adbf931e42d58bac4196450b3b30d14abd59c2a3e899c9f0b0e31246b5cab5c728741e5f0d3b2c71ea959f7b3de6cf32b522871d95d04b656cec987da5a78104b1c20e4ad0c08c1d93381972e48c3956ccd82189941fc29ef246d640030d10ec8804077640e264c81133763c62c891339c8da44c6087232ab0a31148e670392cb08311438e9c4103175860c7223c8e38d61b20d8a10824894062c60e444460c7214e24b0c31007d8518805ec200402760c42003b0491801d8138197264013b00b1b28f2d2946bbb631ffd06d6f6a3de5316af154d6913382a8b811236ba0b1c30f7d14af45acbdce9863ae72821d7d50c8e8b1c6e66f4fe64476bd61821d7cf83bea745a839cff970d5179c3063bf6809acc27daf7fbc24e946591c31dc9891147593979c10e3dfc7177b58ed1ca5ff3407023cb92c68e3cd8ad5135db5ee81815cab29ca8a49c00619d60071e9eedecf8301966738db22ccb22a40541961d77b053938fdefddcf13c0decb0c3dabc430a2d351b5a0be52465e5052ad85107a5bc8c193da7ce984b14212d08e24354de6863071ddcfc16ef59eb17abec1d73d074c5282594d0a45ab8430e7e690cbb39682126631c3be270b2030e2b97a1ec743b691b2d0b1122ce2bd8f1865bb4984e2beb1b5608e5c0d182203a90b0c30d6854e66183d67b2ed33bda90cca92b951a9dde5f5e62071b18a1756ef5bb2a850bcdb0630db8e7acf7c7c343ff97851d6a5066652e1ba4beeb2c2359c1c18e34bc62368a9faf139ed318ec40c38e33b031677c64d4bf668ab22c40582a2b3bccf02aad2b2a73ea3442435996651192b2a30c7b1c9d3b4d7d29bb92610719963df5735a688d61c71894fd3ab370ff1393ab18d63676765aca4fe72318768461bd19b6a3e9e87cd7828193f2ee2993fc6bd861b0e30ba693a37328f396bf9f2becf082f6a283697da9b9e4b9a30b2ad318c36ad5fe42be5096650717de91fec233c6ee2d31cab29cb411233bb690674fa14e9ce6dca884b22c76d8a105848abffbff8db22d85b22c2742e0b0230b7e089d29b36c4cf2732c9c2743dce998d57fbc57586af7509934bb4f675921ed7fbef19e7e3ad22ae8b16cc4261da7734c5261d3526aa30a5fa11e4e41d542b57754740c2624854784f818ad64507eb22820cfcc5d854ca36c282c478f485ba95f64fe09bcd0674ae8873b25e3881d4e3896e78eba49e79e461c7634c195c165d0ac7d395ac8844d7ef86b964c5b0f05d9b1045db878d99b75d6a62b61dd29e719349667cf9390ae6a4eea44b4eca97620019d5d7c7ceed5cb17819817e85082c931dcb4f11142763cba4c8c818f1ca94c6cb7ca6c520cd3ab0f3753b7b91b2786f23bbf889ed2aa3bfd81154c8681e6ddd89607e9d9998e24980823572e5c06791dcd6f46591621494c82618cdf7696e6249f64830930d458b3c274adcb5eb10c26bf3864eaa4b39b0ef62584e22b46561c2f30f1c5aa35fb760b173228ad93364c7ae18650abd6f39498495e0d135eac296550b3b72bae3d946559319224650526bb58c7838e79a3eca9d151960505cbb22c7e86892e52bfddf6f3eeda6d8e83492eeed87767af93ab4f73103c98e0a2d31df5b8bb6e7cf97be0901d2046054ee44892373a1044871a4c6e81d0db14b97174aef8820009b22c6e30b1c5e9b36ecb8c5a9dcb38cab2b8bf1117ccc00540de60528b5cd4358ad4de1bd366c184168b89ac0e795bd3a26382c92c4eb1265f76b73aebb4144c64d1b9a7f8d6cab0b162128b741a214be585897c102ccc246ff3ee9378f4d82bde0dabe149f7cae43aae38aac6bca6dfe7e55428adf8333c9bc7906a52b5acf04447f537cdd82945ab40deeea9baa6ff54be2ad21ca4fed00eb522c5a9d0e37fd20fd9b13a4a0d13547c7f427e7947e52e2a23989cc2d649bcdfd36b33c94da1b7d898846c1859ba548a5d7bbd6a11194b4a9114caf8ca3b73469945c646a18c715fc8cfebb7392f0a46953a213f9ef951188a3e7a8a8d1f4547994ca0d8e6652e9597f2efb53ec1af149b1984ac123b3d91b86b55a6d6f7d3c974c25badd5966a39cac5c809d546ad9f35871f1dda4d1c748e7a8eea2b22a326ce584ab6d6d53e6b8f63082699c0a38d9037733ff1594c1843938638f35c625294658982c925ee204dbb4c53f30f9125cc3a5cfd8f7cb8dd5809e53d85ceba730c4aed94b0937b9042dd54b4924d02d951329950befb1ee7c04412dbe72c35c6d8ac533d994365650e670388124c22a1101da16594e747fda32c8b0f7c5984f8400544d69095234518c9b2b8610209476b3e612a85c6382294655916152c8b0b4c1ef1cb98ef57283bb94287224448105f03c81b419660e288b5fd4e54ead89cfb41599639f6a4ddc80cb460d208a530cf3432eaf8245b946509a2e309268ce834958b19a9a350528d06901a982ce28e1df389f8283506bd8922d653bbd2354a3317ae4922d4519a767b33e2c4ca0411ad9891b27369e88f8e0edec0c11b268748c7f6afcfd2cfe525cab22c4b0b82cce16c2ccbb26c9ff8109595203d303104371fb37c6c6de3d92dc4b16bc3ebfb0fda95eec4841096fa684ed26c352a9d16980c22e922624a3e0a45881b492182c6b2b89114229c0213412c37aa53298452424bfd042681c0538febd2afd9e33940bcb64a8f78dba8caec8d33f9831f307ddd59d4e4691de63e989e3a23cde5ca49293e583f4a79fa8f61636bd430d9c3bb9a73148d423ddc26b4d4b26c5429efe421a1fe3d69d677f2dce0219975d6bd3ae8cef4fd1d4cb9326bcf2dfb45e47650c5ebbc42eb8e2af6ac436ad33ff39b189d2d462674f853f77ba77a99852ccde1d3b74d31e7cae154aa458ec98889d359161407fc4fe7f02342a4d4c1e174ae3f6485e7a035fe063c8bd32433a93f196a3778ab6742aa5f5de3526d50842afdf37a44e6c33761033ae56869dde69aefae612dca63120f5274dce23051c327735a5522f5c4a43ee57b6092865ca4d2b154fc85971c0d7b4715d9f6ac47cb243739c34a89de347ac7c58b68864da9e91c55cba032b71c9894c1f01cffe685d83a59f2810919102d7a455e4f6e1ea13198b1b473faa84db9be08c5440cb6cbf556dded9239c7240cbc4cb55a2999b93f9d60f88217d2bef94ad62aa93e6d284b9022292a69044931e9429eda5bcab0f5162ae5825acbafbf862fb1f9426db2056eb3a7ea98f67097167e9d84dc355362277e16ba951a5a7293269d4c5838a4f8d52f4dc84fba2b983c699e7cce25bf9d15ceec5ae45c363d7ea80afc68f43c67a3fc938c0a691db5c8dfa8a3993e294840603205d5cdc6dc9d7e17cfa22c4bca8a91212773a8ac2c8b1021330892b262648d3954de085202132924d4a7acd041b73a19435916216d984401fb986bbf7c453e1e8414142c4b7b1052f6c4040aaadbff6bd7a1a5aada031d08624c9ed09da99692b29a4ebc9cc0e9bf4fdb493beaf1bcb18120c6a409bf769261751a1df57226acab934c36ab5e421eea36a3de53baf93251c2aae4778c199bbda51c6559849824a12d55f227bfafef5128cb22c40409ac7493239fa3083d298a101dc750b896215ae49936df18d1610c6c4fb5ed5cc91cf62ec6b3aeab378b19b55efa40073190cdaf3b94df6c0a7b1801ca6e083d54a10e25e7a674b66729958a74d0ca76a369c6fc94083d509192bdd33e2d5e4945a1c729d6d0d5e0ae45bbfed7083d4c91cbf9f8d2d4573ceb283130418f521c6bd3c8693a33312d9465d91ea4b05588df1c3eee31ac1ec78aaf40a5c728f024cb4668514a99fda2d05be49bcae9a1c8cfd5b89cfaa4759c4191a8170d5a78f613cf663bcdd89a22a5cb13e65822a418fd5dd9ad4e2c345b2b3f5139b18e328dabb6764ebada84f3bd3a794919b919d4842edef67b72837a9d89b51662a399bd6bae8789d4c99e129fd3513b45969165117209b56bef955026858b14083d2cd1e6ea091d328892d2a3127da990f9e2e6e9a354c58910596844a107257e4fdd41b75e5922a62040583180448f49203fda7aab1cbf934d093d24a14e32fbb7127ae6834488100f2c403a000488100f7400c81c2a428204016228491039d851cae8458f489c8566fadfb8a3d37d4830a6c5b48ed51c35fa47e469374729fae7b5d48e70ed473cc617bbb9d38df0ef5bbdbdc67cba6546a46637c6b42a4f3dc68b50e67715616ff8ba97143ac9962722fd52b8d49b3486dc11a1e9b42afb44089dbf75887c43aa598d263768ca10ca8d10d721a63a9b853046ff7fd6774d7d3a42a42dc3f5dbcc7b98d120da302e4a85c912114d108c929abc47a699394f208cd1ac7db3fb8bdacd1adc0310e61242efeb870d115a637bfcc1b0e2e7745c116a6ad7f1841e7e384ba13ac6f49cc5adace244882c1df4e883235ec613979937789c0f9c89e657b5f67135dd83595a72bc5345337684bcd1430f8bcb94e6311ae4789c0737dbbf4c8a075cbd0619ebcf4b99d21d92f19c958c6e97fba11d34351a3c84d6b317b23a985234fee57489c774408891ad5e483137a69c8322fdb6cd3d9fed9ee490943968d4a345ea1f075dda8c6739ab35c8201c7c19af539da3528e6fe0e6a48c793b87af776ed84c0a2585fecc7f4ad4064e0b53b24d6dcca199347ab041a9ae36ec7fc7ad969ad0630ddebbd629a5f0a43d4d35f8eee2ea6dbae95428080a7aa4611d29758d27e52b234603da356a50275aa6cced19b8911fb3fea7339cca0c6c4e5ab9cecadac6b00c6786cb1b4fffbc5224648d27f4200332ca8e213ace766a1565c555c6f00a33ed782d1e410f31b49a73678676ad543e9465e91106d55eca6d2dae5fe32765e5c4d34879a3053dc080da34f3b1c4daddc6bf80460dcdf6beabdd84bce0072537a8d0da38ea93468f2eac27536fe84791a2792e70a35befc3c379bd7a0b9dad8f7e29d7357a0b8d1e5a386aa1a56a8c7855f96721cf32e74bed792ce032373f6e4b3dfbfb15521a466f5c0669aad33dacb0720f4a8dc778f32aeb5105d45b698e89d1b627438518f498425acee8ff72a5753bf6400754d0430ad766253708995d86d10c7a4421d5f8ebabfe33429b3da060c8f13462b326a5538ee2468f27f470420b7a34a15b9161bd5daaaaab081e7a30612d7bf1a3dad5d8ab28cb22647b2ce1f8a8b40c9b51f567ab460f253032749cdd6ce6151ecab20829c23550e991044eacfaf8861acf0cabd103099a7bfcf3ef5c993f1ec378f118d39df598d8a09cac2469c318674d7eaea4aabb52138f62289bde5187d09f4aa5c4c04367d6aa74ece76a18693b97df5dcf9626e4210c377b87359d5d46cb778e35018f60bcdefafa5dc9187d2e30320fabe495162d5e1b11220b0d1d50e0f18ba58bd856da564b66191ebe58a65fa9dbdd496dc947e0d18be33ee6ce576c54951777e617a9e247eb56e25d187e424913abccbe853c74f1cd78cb9331e4918b3c57546b36e7d84906a57b093c70a198d0726f948a9959d3003203206b0439b2da585944d2581623beb2b288bc0a8f5ba037bfa4fae04abaf7b640fbe7381bd234099dafc5253b5a4f4ca9c6ac69618bfa144fdf7965ceb358cc775032fdbf8dc8b248dee5e62465a811321b8bb63ba34ea34a675e2d582cd5658697acaf50e651a9e3c7c78ebe7285edae45881ba9725baa15e8a8b76f665d7f5cc98ae4f6c74eb6ef5ad769156b617342859a561a4caa685f4cca95a64f952ea5e2d127b44e1a75d42746549c74d23ea6fda447213c859eb3faf0c15345fc33c5a6638c4c3278ea9cae1467f9ef54a655a9f7881467bb8d2fdf633eb9d128786d7a5d859028f2f3f86995f810e35a28eee4e799d4eb8f9582a23f21b2d4eba447289d4ff0e55a346bccbcf9399ef84df737bb94f24e7b279659b5c820e2e489c97870e27361625e87ee58331c3c36719728f1503dbea57a14003923c8b22c0b12223c3471b453aa1b45c39ff45096057964825d93c27596cb18d7f51e034c9c5dae16eb2a4d47d7a32ccb1b3c2e71ceb83fdfb63ac36c097bec55ea0cee396e0865598ab80c7854827f19b3f8a8f2d4a6e7021e94588979f5fe733a5ac2491c2ee565d72753623b49ac4f56e7e8ac4f9aec2261a8fa8e969d4de6777c88ca1b90408896fa556a56d335a20849c10c783c02a149eb660f7af4aadd11ee8c4c23f4b484961945c8911495142ccb9114957e834723d06ff1155b19cc66445916462ccd55bcbeb3f75fb888731ecda8ca7536af064511abd3ca4ce84ea1237f367824c2979fd1d663524b9541c4ef2a94a6b78839ad0f715c35f18fe79b460b5196658e149515242df04e71c310abbb6a49d1de31a9390843be10269313ae4d95eaac6942744a785c3dea576f569465e13108d6958de7b67acd2c515c06ca43108adc97c95648913373200c25b596d39d37335b40a051c6f3dfb589a49cf0f84342dd8b2ed9dc4f7f51966565f9e12c0fa33f28d70f3551964525c5c8901315f0e803bb5a846ac78f2621e683e79ba921abadc9845096e50c1e7b689576d7d8e7c8d2526df0d08322d4a7d4f28dcf521e98f9d91c3acc73ce4127e081874e9d92496851fa5466efc0a7b666d7faf828842de06187b5b7db764c9b7d4a58875d774c294ca6350f1219f0a04366ae4226fde6ee4a3a87f74be79cda36b9af7258ebff6567162574fb386c32ff3467aacfa25438204eb7269da6f40637fc5ccaeb28434d25e0e106eec73608a1bd934c9d36e0fadc94b0fba0358ad9e04b9d4a3b9f30053cd680aba711fa6dd33b54946531010f35f8b232c34857676ab434982b6f1af245f7880d1aaef53237379bf9f5cff09b8e4ac74fee515d6a86433df6c8a84d75f553065c5737e964a69a5a5b830719f6d53a9854a23265ef2a292742788c6121b73ccb8cee0b530b788801bd9ebb9336a961a4fc8d18f008c399ca3b7690b2c15549d4083287b3818264a9a1c30f3cc07012ad56bef49b3c3d797c61f939d98f7a4ded9dce00f2460a787821ddf79ea6514961c2d585cbf4be69d763b2b4ca053d96dc4d9e6a1fbbc3630b3cb4c0230b27adf5d1f5adbecfb1c0030b8b87d8f84a0ad92e3342e07185834ef31a3e29994e71630e1e5648fd8e52aa3989789757411d6b4b67ab6e53d2e441057474b3cfabe62eb4018f29bca23366cd331efe83407c88ca1b5ee02185a5d0ef22a4a7068da628a883946d37f23d9e5c41c1d220525b09f559f59e70e8f8f9b23e7548f9dce0e18456669df4eb1c638a545792b4a1068f26fc753a7e90272e525c28cb22c47ec08309689e2b93ad1bad95e6093c9660eae9f30c1f44360b29c18c9fdc5de97e94f9e5910454be6de818fc93d626cab2e860020f241cbb74bc368546c631d8bcee6533ca75db468631d241444afd516e7c2a15c3a45cb9671f513a9d470cb4521fb2a5de86717a95f2b49fa60f6408e3dcaa4e8452a7fb534541b2d4506404c398d71c7342095121a32c8b1a417440810c6058a384f648297b95d87fd12ba13f3ebf5c53a67db1149b65cbac7ff2a9edc52bb43633faeacf152f6c7d6ae2446a176c6a948f51dac65546179e8eeafb7dfdb4695cc9c8052e56ed3beb371d3c5ca42bcd74d68d9b95e76f91c9e6bfacff742f755bbcb659783e93cd60732d3c95223f49a54de669b43856a7ff8696d538cd2cccb8f39aafa587d5ed040d3264a1ddca673ffdf8ea3c42462c54a231f75599e9fc7258e0b15c0be97183c894bf6225ebffa49a53a3e676052e1f52781c255b07652b5425fafcdfb592b156561c1ea49a6f2b57918f2791eb59eb185115e6f25add5b2d4bbb948a5dda681fe152b4f81015e97a71f2daa753182bcd53c3dbaaebdc14bb6b5db5a659a256a5e074bbf2333b57e921523c5a0811fef3d2dbf5a3503b4d8a13db5a49bd28d855da9afda3cd978a52848c5024a308a167dcb5fc674181c92c2db4e88df6aa1c647ce2ce8cef9d4bbbbc663d818e9e743f74dc634c3bf1bd8c523c7dc5895cb55e46a9b642283781aa5b9561f7b31a4c4d60e25c766a5dcd6eba4c9ce295dcecd661ea61622576948e59376488d025d2ffb9b5abfd7789cc120bdb8f2ef408338d9a24472a9129d552aa167953fd28a18c39b696881bf1aa9ec44193979e790dfb164b623966a76536f8a78e228186fb57ba64447387049ecca529df525ac71f619a6911b372443a09cdd8646ea2538dd063f252291ec2d7b418910c73afe58512f9e022be8d2552b392a9ed55459cf643274d0ffea226021d76576cd45d299b44c4eeffaf9f49b712ed0ff10a516d9daede3d848650f6538c7c8d29f5090b81dc12139949ebf797109fe9e6d5f18ef1f71bc49ee1ea3d6a2d3f464d10a692d392f1731bd30e84e223465be694678800a19a557a432829d563fbc3dff955d476b6d751e887e7831042a86d9029577dd8a4f9bec8985fd9cc876314ebe12d6c76847e0fb7ccad33eb8f49668fe901197a508969e7d4310afb90f3907c77ded8a63b956af0a0d29c51faae492b257e23c80fc8b8032f4ee753ee43d3e7ecb07299d133e7cad3fb7c40461d8c2fe7ab9b65d6f01b17a8112406cb220432e8700ce6b9636f3a0f1a4691011973489f794ca694da7cd3a32c4b0a12232968a4c106903428e30cee495b51264d6a90d932ccb0baeaf8d2c184fcec659461df8c59de8e122d4df9155f411dca2083a55ebbf4ea75b931668632c680fccef89e97e6bdbe18bed34a37f887864675187c939b3c869b3acf170ce64e71ea1e9356957f4135a633b9faee85b4863221df95b47b55195d68e65f8953e3a5738dcae002ae9f9f6dc2de02767a5dd829dfb44da12c8b1fcad0c2275adb830c694ae7a432b280fd97b0574f257352a12c8b1ecac0421957c895e89cd446ed6abbb7a10c2b983eb3c9b437215e7450f8a48c2a9c338f58f92d576f1f1424cb02471954d0b3796d2c979db5b6654c81b35d4d42bda5461952704ced93b2152242195130ac27f9ea32429a4ad55006149acdeb4147d6932acf0b653ce17e95da59b57ee11d6b42194eb04ee9f8517cba0996365b59a9fd75eb6530c1cd317ce69c3eaa69501a652c211ddd85ad9ecff3513447194a30ffa7789954eb9ce207a18c2424a3c2b784fa8ae9189465b9411948d0d54a99e49c07113151e448e91afc38463a4bb13a5cdd28cb728264c8b208e93582a84ce28731dee04983902d2e464264eb2f1d2ee22e458c84a9f928fd562baa0d63af55f273f546104e69c1b22c8b901fc2e883abb8b89812731e30fc08c6b5499d52512746cba9063f80f18b1fbef8d10b213f78d19c6e19f65986b88efcd845deda43ea5279c0b903408420012244d94902a4d738b2dad881881fba389b79ada7a9043f72716759dfa84f9da66bfcc0c5a2b3fad64ae4660fb6174982649320b9852aa436adf4d328cb92821fb6489bf618c4f347b17147599693395450f0a3168bba90eddfd272d4465916151f92b2420b557a90bdb2aee4e6ac4a11aec1097ecc22f78fa7b34611525facc20f59e0254ae7eba374bd1f347ec40299a5505ac67511233f6d64c82f8b9021692ccb0f58acd263308df2f1b4360d413971210c4122841faf486b566eafda6831dfb224f9e18afd3b66f3ec711db67fc28f5698b537a3988f6285f1672742650e655952b02cabe8948a08592157f34e3f5491d4a385c84ee12aa509440c3f52719690de39d4e7ef4d28498428e1072a56db6c3278dea05c57466600870a7e9c62f1aee5a14b480540de08b24224490f7e98e2de9849e64e7e29fc2da1a13e06b526c5a450e817f9ad49bf57a34691f6d2396a6a253cc84e14c8fab6ccd1fc76de09c5b239eee122a44cb641f9010aa55269da6ec59e6ddb6b04d141851f9ff8e18963542ae6f4c65df9d28f4e7082003f36f148e11abc34a98ce61bf9a10934881cb17272e369d37e6462959ed55b6d68d252633f30e1ce6bf169fe5fafd25078f0e31289add6ff399d6c8414cab22c8b9095941415191459aeb282e424073f2cf1a3128b97af716d54a7fe7c1a3f2871ec2acfe699a93da77e4c22f1fca78378b90edc3083046fe0608ea5a2b224a15426df7cdb3e0897a12c0b1224ce2a2bf723123f20818ee9ef42ea9ff620343f1ec10b13dba73b5f2ea38e60f4366fd2b9371dcf46e0c9cd4d7ad6d8b17442d230632549910d9c116425491b283f18f16311d76869a733aef643117ce7e72435f4e8c93c11898b96674c1e55bc8d883fbbdb289995ccfef910aa8e784d26ef3d9bd610b6877db80a51cf51ab1066ffe0feda836799518470664dddc67b6d3acd411c4e4c485933ba3b4441b03a2b54530be1d95e208cd3b06f274a40584acd2a19e7537cf60fc7f0ede9fa731c0d911ff4d1e25ff369fce6a70fbdad8c880d1bf21ff3830f48bde9d9466528cb727eecc1dc5fa6ef75ffbf96eaa1d9f991f7e6a74a7b08e541cff4d0e135ae47cd54f0030fc8d61dc46ca88b0df90ec750a9859061322b53dba18f2d6398cbec172d9ae3471df64dbe6e5acd33874f073d66bc4fab639427d98f39789b4988758d73ef23b31f7238ab9b191d657614ba99fd8843bb2a333e8756dd91d90f3874aa72d58f8cf2b56ef6e30df6fc87ef2095cc4fb5d90f37dc396eee7768d1e95e6dc0e3a5094d55eab15128cbd269fc60039adf59b22fa492316a083fd66066d549bdb554f714ab4111bdea943cf9ab639886b5d61d4b967b380d0d0dea96a9f5767e9c01a945ee447dd4c2836c8634e9d16a1fdcec3d68adac1420f951065cd98debb8dca7f7c8f0830caeccac59fd3e88d2ac1f633066e16abb75d6420881e18718349d39889eba4ddf7218fedffbfa0e73d1fc9fc0e00718fc9843cbea1c9aa3f857f8f105d46a909e94e7162eb7c20f2f1c757f95ddbf0cb571173299a5d70a2917d026fef4eabd3f1d5a5bd0b3f8fddd2074f4137f68c1add3625f935e6d329d85f563a8cef2ad5b8e180be88bd12a1aa245370fc38f2be45a35af6ed0b16ea21572ede1598d9fde9f470c3faaa0e70ea369e7a1517454e0d7cf8486b7d1274453589b90792e64d8342143e38714ba777975aeb47c6c5b093fa2700979a35bd45f5d2a8ff0030ae8158d51f37aada5fc04ee73affe4d93083f9cc06c07f72cecb3522f05c4b80088710388690388690110c346167e34c1fc41ae8ee9c363fc4d906539e30713dee4a1d34fbf6d6f6de8c712f8522e954e2a6d33caa0a0e18712548df9bba95fe76a4cc38f24fc31497942bdbc5d5d177e20c1d6e2a2193aa47fc98eb166ce957ac58a39ad35064a493f8fb99e4e5dc5604d84949a9a4aef470c4eddcba8566e2ae530d29cb378cdd90b23ede0ea958bfbf4b10f06f2656d14737260bcfffb52de9caed5f92f789df977da41460db92fd2d2f7ac498bb796772f92ee9d537bb5ccb779d1ad3829e5b8cca2e45dac23f5a372ad8fafc4ba38e8a0a13dbc5536e45c28b63c3dca965bdb312e8e79cfbdde5ecaa36ea18ffc743acb2cbeccb50522bf2393a9d5bea7ad8559ab9631f673d26e5a5aac4e6b54fe9984d24a6b166edae7fadb1c31de92856955566db7349dbdc6e2cc194c33287bd1718545e3a549a6e918e3597dc52b442bd75cd13a94942bd2ab8356336db90d552b9249cafc7611e6694f56381ff58dd8dda4643a57c19a674fcfad438c26559168bb1f65764a85b245efeb50afc7eb840a7c8548958f6ea3733ac5eab5d0a9f5b88f7a93293c1954cda48ed63ea5526cea1adff3b485d04152b4abbee74a8677cb6f1408f9a14565cefefc48149becd5a526468ff4d447287c173ae6d97da94b0b14e8a43f4915d33ae7863e8167a89731c794cf0ef28452e3c724f4bb4c25efc4625768add5ba4f549c38c3464db3873791aa0d526d73cc1f93a60974b974a9ba6582d71a653251b26b34860984bbbccf61a538d32fc17cda12295f3a1a3e96e875542f9559f687b912cf9c4ea2d657e48a28e1c7c86e5f9d691be3732c2244560b7c4c42f531fcd7be923ac6283880b021832349da53d07e24497b47c187247279e7aa44b9269da42281c8349e4b8b1d8f4a3df880446bb7e63283ea7c0dc1c72314fa46cef5862aa55994657957393947f0e10857e87d1975db394afda524498cacc047238e266aee497f7c9d71467cb71ec75bf6d5e61145881349928222836579828f45ecc265ccb6a3a4eb16d2820f45b4ae5d72c7fe4fd5690a3e12e108f1f3763a6ee83c197101903782e00311b86a708df15c63f07188b4efe6ecb75a86d8b47efbd369de46e7c0e0a310ca125d3122448470347bc53bff696c07071f83503b07d3984106b3570be258fadf6ef6695bcd81c064eb2cb167ae5486296f648d37f800c4ea6a5fc7e6943ae940f0f187d3a7e9df7f9dbb47f5e18774897ab833d124f202126489828f3ef4264b4dbbe65ba38458c1071f3aed71dc3bcb966d96c4c71e9657eabd5a67e5a25940f0a107577ff7caf3a454c5ef230f9847e17542c935dd7a117ce04111a7443c43478c7aefa0e2c30e7548f443c7fca5654ea312820f3a5c9ebfa4eabbe7b0e8fde98dc2cd56cc4892acac15194cc1871c72a59979cd4bb54bf9c0471c6efba0db856e90bee1233ee0705aad5736868d4a442aeb093ede903821aaa35e0a997fca0a121527f870c3223b6e681935e9cf2f4a91218b0833c1471bf4d56426728428cb22040d1f6cb8d6edc7a56999287900c8a524405412202a490758e0630d3ed470b77614193555d4d868f84803269fd947eb8e2263148a11172ccb899114341846c6ec28ab6e5c3314244894e0e30c78a6ce3a9bb8153fad357c98c14719ce7b9965aa06cf2af46a0451711cf82083e2c5e3976ccb9e7ea12ccb1c3f6425c5c8898f31e4b769b22967dd6587b22c487c88c15c9f731e22edc664509645080d7c8421063ec0808c597352262353f6cac7177c78a1dd686e3a5f936fd897e0a30bae9c2a19bad6c4eb14cab2f84a0a100f0cf1c105f53f9efa8fbf961ef5b105b765cec14c46394a33f3a185b7e37bb52b55b57e06c14716f6fce9c4aca63ba993d6f01f0c595971b81b45862c4b1c3eb0f06f887a0553e8146a5d46bcb3ca0abf898c10a767150e1b536bf9f9a38252d6fe67f528f44f340584aad7c7fca1453b24054d7b2699c4b7b6ed270a26a9f3d53b0d8565b46ae59e57dbbb9e6069f9af336af7ead87182d1572b3f539a65934d60bdcfb566d6395a4c483b8cd2a073169e21e3868f25b0425de6687a49b5bb0f25e81e764ce774be6a6d1f49d0c6e47bf7a54c2b621f48e057264d13afa2b4698f61cb77254be7181a45cb18a9d4acad57783625b48aa1301f539eafd48a4a8981ecde6437f2567d940e43dd286bf2a3224b4985a1d6632f475b4b1d4d05c3acdfb12d4546757b02e3d06c42e6287b1ad4f90bf4c379eb91f72fa5d2176f8c169e616f7b5af6e2f09db559ae68d39ce485e67273c7f9dcd118dcc5fa65f8749b75819442e74ea9ad8b9b0b844cf137fe59fee1e1c28eeaa5643035fd29740b6e4766c61875f23f992d0c62fe644ab59ff7d7621527bafab4f872d122b11a7b5beb562dec59e841cb9647172d559485d971426dd0d14bbf130b64cc955d4aa6072c1c216394baee197d1b5f5924e8f18a844c53a5746e447594087ab8e2ed7832c3da78d269a5010d80d08a446ec776cc3a3bdb8eb22ccba2b22c438eac131500e8a1072bd41dcec566ffd88a27c14184bd8dd16315b98eb1f38b95421a904ca86410a533611c0c8601613018000003229b460013130020302428128763d170286bb30f14800243483e664e28282e1e0bc6e1703014088542c15038140a83018170308cc14094c971aa9a0043a892e1bc45e81fb394811bf6c3188769cfe759085631e5c1f7b17b36a84abcbf245831da34d42c562b2986625b4d504001e6d2f1acbd9a401747d765fef17cc8b8209f04ee8b20f20a669b1fbe78044b3f02e7e737f7d8f2c94c08431f18af7b21b2381cac9de0fb21679881063c1529f43c857c43e270f1f5bf5c266f42ec874cf8cddcf30262701e44cdb3a09057f5d7852df4d73e162ca151599edc38ce338312f58c2a7255e4a0d14eb69a823ec4492a82c77f01c1fc95b240909c3050488c31584ee8239b04148eaf39220d89e8305815bc2e98e8e24089b3cf5a310c3ca63bd5c054d94412e0eaa1cb27d4dc0b6b423adf9b8358d82e0d26b6aae84e1a1f866bac77e9ac44b457c20a0699b00ce9f388125025d5879996c8c81a3bbd69349ea8e8f24d1d68f91d62516df47dc7e8022615b4d36f6f3aa92e038d1ba4dd0410ed038d59b51a49246fcb17aeda232e7c40a05c41dacb176f7069bba8a14f8bd38615e7eb0585b8b78ecd801dcf5fea819b4f18adcd7fc81fef539be2e8c02387f262514f8be472198bf5ed81484c8a673b1171fdae4b6adedb156e437115124e785381b87e5d821cb09f500464c799d7fe84f03997b9f7723e1e72ff85880a64036cafab6cd3f14d52f559ed30c5126eab6b625b58af2d31e6377acd18f786bfe096ad881d0c00480c16fa3889dfd9571229f6a792bc1968531bbc458720ecd4e8da642a6988ab8be3337466dbfd67308fd58bb4d9ea13c5c01b6b6a4cb3201103ded90363cedc9ac2104fef5d94313d31fdca9d92356a7e5220fffc9d9a4a14363490b44730da215e3d1f2e2060ae91d7a9c6b2f921c8510684cce406a2662f09f706437eb4f44e500647a64aec932f3d4c0416d02f13f745f0ef8aa417ccf723364e612aed9a73504b57e46152c74ddc7ac8759127cd418f5c48decd390c96da8f3cc10ea1c7f0ffbbd7cde283c6f83873bc56dc78d51b75e2a32a560653b7d8729a669b75223a0fe9c22781320f2a171ad2cda54613f684e3226391b9c85de80664307ff66203c7def409efd840cb6c64ca253b1ef0f0889e52210324b624a890a7c33c1b2542a40002983e3f1bfdf025d26bae000eee216e3f562ea911209f6d0099fd7116fa9d7e17e1ad673dd4e91019809bdd37fd4a7bf2c4c2c032246c5608aa8197b1bf69fa09e7b9f1dc7ac479aeee8e36b15df5d6a982ea3179dc3e47f31414e5fbb5fe6d148c3a2e1956b099ada257465d50d923b5191c54cc412886d55370c5a04b3f010e0c9e8b0668f29584a8911795a7036b41d76189c271698d07d90434a6862f7004f5da005f9b449669f9b258b9419bc354dc6bb8f01142ef8f7c485ca70c1e743c8158a913c63f521e4f58b26fba3dee637408630c1245c51f11ad8b7cd9e97676fe6c0ce2f5d3029ce42ab92745b2f3f88ee0b9f30a82c631ea36e0106913e8d4404c0cbedc89517bf04ef526bfacb85964b8226593cb87cd8992fee173bea47fa83c839fcad678124aa172ce26548be7e51cc01a6098ce74db54be86a51356c5734cf0e247befb9bfeb39fc71c3bd0b33f9f3e5492eeb07a579d0a3c6acf33a67271df5486bc2fd10fea8c966b5fde156db6c0d1dc8bf1a2d315204bb04b4b5279e2b5642c8e053278ad4f040245ed5080680fe3d20109b3796c4026a133b0e1de43663ddc9cbbe0776b311bced42fb30fd9f3d708d424e51c6ba05986964ba3ebd8a94c4d78e88a98b5b8a9caa87bd9e498bda0d918432ae8f84fb9bb6c7a5c48fa02eca9714fc1ff7e44da0fa33b814c2371b236d2df3d8cc9976d6704f2e7a465f323905b1cc5ec455ef3f2f165f5915e06f179fa7cc6aaf812a3ebdf00d21c705c0fb3d4cff81ee758cbb0a5acd236a9bf8e6712415d199c814b4edcb6a2d56c3115942c45a5098432854dcb52a84e518a55efa4d7b3b6cf01c475cfbb9cefb900e9384096e76231ba5a338f7b6111f6a2e3ccbf6a2103db18eb6810da05d8b72a34c2f77ee9ba172a3dd0fb366c40f96225446b41269ee32381ab15316c17e8273e2059ebaea0b404a816392600d9846493763f2c26997d88a31a0c2c39fa17c816434c6e711bd80debda1e35fbb24b45c105e39617b2919c8a4095a9f4b1576e5919eac2078dfb668950085e69491cd5be002811fb26bd8de0ec5f2d3b2b2a3d5d04509dee1d055f95f73dfb4e750e75f97921bf418b7c5ee85640babb7ca16c671457b98a1d5ee124924890ead9901eed4e6579a9bec6f1190095f878c8dfe11ff526395e3d7974b6dc378ca0d4cb9ded903ae1dceb0ea253f1feec683df7ee81c3db29db0e6e3a8c8b016e58272bcf51b253e9f4f0683adfbbed0d04b2a7b70cdb296d868dac960c29eddbf87c4a82d71d501446a8a77516714ed97c8cd68cd2cc54e7731ec1aba4ada8448b55bfd1f83d464e775302a0066a988e9778542313c703487d40a496e5c6d23e254721ca6d2c3d315d124772c8b8b8e87ca0ef0e89b314506cba84eabbd07dde5f40416b1f3d38e228b6b76e8628e233b2a8339ffa20c944332d08754659ed1189b6fab14b56409a80693ff7d1da0b9b2dc41adfa1c33d888513f3eb84fbc5bd4503822baafd7fb5bf4ff47aab1cde245c1b85bd1b3a8b54bfa9e0109826c2a14e45eadeec9d6365f79148c30d0b2362bea4d7ff490d90ddab8bf8f885004e88223f877812dee2ef881833995994951afdca628537ad3b868cfebe67757d94a1ced2ae4629c6d2367f51fb40258659277c0b11f483d4665611b3bf958b05155518da3556a2b5cfa1996c4f60b2e0b60281360bafa116d19a9b872cb2b46c5443098c65130846f341e0af9b58ea7462625557225bb90ac9d22333a8f4d22d128ca9341056639b886e1fbab1867ce32a9f68f5499c8a39b53f4ced9584abd996dfd064a8300140fd0aa7c55e41e8bc1a258683917022b5383a899fd69a4a069195c2da584f9dbb005f3d45f9474a04663be80b10128dc224dfd0b36563ab3c2e3390e0415f9e2f485677ff6fe6363ff72b8a1ed5a0364e1d97cb8928d831388bdd9483c3893c8e95aedf53dd4775a750cd1e5d07ec3d09d8c0d1609ab7886b40e39d2aa00f3fedb232ec358691e9a8474204edfa8ac531d4f4bbdf52360bd757c7e2e298e82e559ddcd81d442c8656cff4504aaadd9073f4087cfe75d62ec30130a80c9ca50e8ae54a7d33ff68eea68c458d38e17b4d621cd41f708f4f075e3312fff9b56505b0dd6d2f26ce2061b1e1c318d67717ccf668953dbaf15ee42a067a2cba14e33f3566fecb5ec5491ce9a1438d80463d58616d865e6a0830454bec450e4792d54c69aa02d95831258d661609efd50b3347f0d1744df69027189425d234d643ada139c7a940722e865d9a2de2a16a741927cac131ba94fc1f1a49182b1e9e2b5b4efa3566ab3aacf14274391a45704c70b778b3c0bb584587b8e3b5cc883e58aca42dc30652b2123b005f7596260de9e5915fe2b93a2596c3ce25dade2a55e506e7c2e08592abc59db5d3873311485bb0c9d1870e7a7c9b7edb3deca5b0ab6963b1f5c858a30e7f58a49670d6ea14d349c358e1912bdfc1d7ea8b8ff0a9514ff6d21a970f474a7ca003be098fe8f77382dd493fc9f17703fd130fd9f5fb132c0eb59df3bc20302334fe8e7f81ec0c86f98211781387e2c628893e90a526399590063690065b9852f90ea9334aa9a6d9b04a0c644bea6fad9396ec7322652f952d344327faf058d9ea46ec7812acc03432dae71e2054514946bc7aa55e160462be20b4bb6f6003b93688ec0fc09f1f60ab15ce5a8414ee07e96c069bd471eb9821304f3d462e138dcfc0bdc26d3176ab6bc15ead748dc407ad200aac88b0b955a4d229086f2b205d9412b7e10cb1c78f629fb009caa92c35c0a1602586042de4463e8cbbcc8eca224e785c92587ac5b78dea4a6a2053e67790bc451ca2a25a49f1e84b1c0e54b950cfb19fad057c7e447f207b568da81a32ef6d75c579de23d67441069d761f3667fb7b9bd70cdb35859cece11f4714fc09c259bfdd05b5f64cf44dc3bef5170e68b14e177eedea6bff200a8aabe2ea0cb2c86db941fd456775efa399860e1edbc5b3d97b8068917eabf8e1a2e74a139c12b39121373b61bfdba81bcb3cb32a0fbe8c07913486055b9c5667dec8ca4bb1faa094d72fdfd3a7240855e24db316a1b5c17262b1df5ca4df8926dfb9878a706a456c60032b45fb59afd5e2e69c33579fd217e6762577f6141c36a253b1fa83c11073b281ec26846838b347667cee7364b26bbfbd1a47aad865a6519838ac435b8ada6e856cda39991450ed62c32767a9979518409afd923044ad006dd5995280627fad561f3fac86329c5e26422565c7e1312a1e1ec64a0652b753a497ea1060e43c41935afdaa5e0ac2080af2d7127ef97dce66519d65d0300242649c6442c72794e58e9e426be95a2e5b68287a2fff4d56fbc64028fbabe820859f0f153a448cf3ad42a788576eaba4d3858bc5833e16519a870ce6830515b2a6e0fdab849c3ae7d51ffd2ed4f25184c2720cc9db2927d047371fa9c7a3302750c60bec19b03610ab3abf272247d36b7aa11c7b5de8e4293fcae4fed7e9f9f98e4a725a91d7089663be2ebee94a7f6bbf07becc124f7bc752a3c47970f6262d65fb5b3bdff920faddebda4e0b3f1f21082bbd8a7ee3fcaeebd7315f4b8423172ed2866422c26a2377ebf3bd5f8436e1fedcbe9a04289ea4856a95361d92bf61541af9259c190bbec3266f67f780e908a72665182d929c161c11eb43a57b71098de6f3f8a8d421cc938501137b116d9907c80b39ccbb527f2568946bfb730e4f675649659c3a1d3010bd8303c031f46b91b05d77742807eb870a827b6a695d6941195b5fc718c2363ce2036961992a88b4d8568c3755278f8a86af6c877fb3b06b22bfe30282f273d073ae3f3e865e24565d5b569cac8a890072716420a02c2dbc15a1414d19a7f82792dd2b0a1bbf699495555539b29824262676f844a5b647fe382d99be863bfee04292908ff4cfb470be38b0611c0fc4bc8fdb490ce83b90127cc5d98959789c20208ac46cb495ed9f44441bc24faf777138d10c5ae1092155d5343b2555a7c9d41b8343a96c80b03e792978a8316d14316937b1b1a49ad1ca25d0bfa61ea34b4fb9b9af33c681b9a129719e40451713077a42d6188286d95cda8c81fd12183d21d624c3555a049cbf42f8e254a952f650d42aa64262ca93d82318ca53a595d25b49abbc58782bea96d70a8fa25e79add0334a0dea8118e2945dc4c24e3a8382c25e417c4b88f228170850c7075c7823d6f2cc8d4263036c854e3f2d149c42dd903c1003391bb3cfc9f8ed6aa845146bbfd38617a4d12359ac28c0e3080c0e91f74cfeae3c18f2e678d99bf6565d83fb8a2dbc5aec5f00f368ee09aaf9c60825390131b238b2e58aa380bf288c9d75115d95a868b466f6c02a234b29d4c3665a0040ddb02b5e39eaed22a8253c87638024dda32afd29d8dd2325d4b4f147f550103a1120e475052b1a7cb01f51d3c549d3cef03c48242a74802899d96d5240b50d542222fce70817ac15a0843ace9ab16012c42b73675cf8295601102fc56baddfa1798f2b89d058996eeff80d5b447f7736a9f4be7016f67abe4e573f60703461088d2da8ce850d8584017b18d8db3a0b75449da4c2f844dceb9149b67e22d56d18d2e9b4ff4927de3558084af4bab42284e3d3f533b24330fd0c8852a8b2b518e0487f6a8006794739dbeb7ab9d264983b3fc6ad70c18671005d72c75a8c9e97f4b40bebc3a44644be8109f11826fae783250b9e83489cfaf11ddffb9a6969eb490870072a6cca8c9ff56e2f8fc43c837612d4e3f2e46263ab537bebebe1bdf22b32fd69e8d9df3b907b5faebc82a5611f640eba447ba75419225c78759dae3840b02f6ec59c05150b7997fcab5ad0225f19f1b05e0c664d785737a61f6541d41a7ff2b4d7bab5dfaac24ac68274becd5d64cdab88f77cafadf43fdf02b784a1cdf3ab30133d229f0918d8760ae16fb1d36c885f89521816b6b3408783ebe00af389bcfc9e595f7c07069359711a3544ae2bb4dcaa8bfad23f0ead6bb7b95fba08d8e602593c66956196f2543756ec9ef4e2ead5b0e8ebfdff5c39659b2c67a34334498d3c9a967b0dffa06bb5a14bb432577a52dcb53f4d8efd71f0882fdbe0477ece29da85fd10eae654a2d57b212929f2a2e4c1f4e0657ea4ea8eedef4b27d63fa2010516a0c8c6b9faf5167ecd93f86f232916cb7cf398aca90c03d085c4e6434fa5f73656eb27f266e512f4d64a3c7f8fab8aa8255e6b7d14dd7c1c383cc29cae18f71b28be3cbf3d7291b29ee34ed1e476ccb989f208bbf17da1f790ffb06b71820a24a81da40bd72c9453d49233fc8f838c2417bec52c23185e9a03371521f51dff2f9f68df57e7a7f5e0641403d10a335c946b262d8bcc6b0494dbc96f64c2b5b08215e4a92a7d834662be6f6ae55955c027b91233e14c178db41832fe7a9edd33c5ba9de4e5f4842953cf5af6c9850630d0d8be1182d790715af667cf3d806889131abcfca9a6335f933c5535e755f8fd1988493d3ed9b2a99a8139ad6e9f82cd5e8a70d12e8e2f684fcbad0a138e24613b327d4d29fec85361f3cd76043441a6765348fe94254e355be804ba6314d884bb99668bdb9ec0b3c08f6bba522f988ce190612618eb715fb0f25c7dd5c670879e01ce863b24fb4de939efba4bebdca13f5d8a5e39201c937281f6a497b4250e0e36eb5c6f70ee685f7226f129f3b0c0b8564ecd4c91982addc2de69c857ff10bfedc277185c0dc7b332bdbe03972f81ed9f955e8ff72b8bed1b77a32fa1a826bc001044d1a01c0a55e95e74aaa5849312cbbf01d8694a12548e8f01782f0c4618a093ab34063bb2761e00c986c1ab108efe9890381e46f4dae6164863b83e10d98ff8a87420d181638bb6890b9a9ca269f998924bd99ad63b3a386dfa8ecf53b6756f046cb479c9a9c13cbf78128ecb3a87f4ec32dd85c187d30a03c48c1af673986d2ca31aa80e520fa2e4e493d8a3d92d443cb96deaa8e558052f65750a785f082c1f50933e527e8508c0e42009ea88bad161d09738d733f8ff1dca5fa7d2ca1cc42bbca265ef2ab10448e0e43abd8321d98abf27c3a5cf77936c18e2ec55074f6b29815bc4d77098f7c2645d34c3f0384ad2e438aec8c365db77c2a4ab0cea25cbfa386d7b30276fdcd0718e3973b1f986887cb440479c48502cfee8fa897dcb22a1a0108ef436256117bf2d3000de76f4d642ea1621e3bbd61f63a2f2dc19a7171daee535190e420ee759b0368d92e490a321e996e86792824793ab333c7d78269cc234d94a3ea396718b0a853c80d05a2fb4a93e1e2f6e79507cf00d3f3893d6fec173f2a1f9eda93575ad693f27645a500c6649468910e7ac35a951c0b4a46c46e199aad25aaa006def624c246009512857a2d43fd2a99cd40148fd84c9d4135299a3c64683f4674136004dc12e36f9bfea7bfc3043dd1f8d4dce3869638af0547934de919d56bab3a1ec5aedf3d7ad8b4226a3ebc7de82ec592cff4a5213b245e7ce129d9f5fa24888886da85e4aec0a22ff00c8478a5e0e9e0b08e4be65e705fdb308629ed863bb2c4cf3cf29468a13012e6a13e14e9631e49350f7e3f5fcf2714640f58fb760fd2f794911eb82386a204bded77a2c72b6c78e6a8cea68eb8c64a574008574aad0cced7209589196e1b83c6e4125cb9e0b9bb83b5ae741e96e73bc312c0ccc27ba4ceac1c30c96b158698a47e70e5f38db1f2b5374877b9ec2008838a4e728520e14d7680457d1465d33fec564bb3cf57d21c0902627f5034482aa58e384977e71991ea5f4186b30c74fb593606ad28fd342f302160d959d5c17f370eb24b0d4e802234a2378b043f317413d5a2b19f2197709de157f834489876967f628ee2c8cd6e1630f83487eb944e0d491bbbae9ff68488c8e936374005f4cb35a31bf6f6bb69c0e3d5305c20527a4913cd3ed14f24174829ca586ab0a779dd162e8bbd92499f9e99c138ae2b56b1f015a736b95039566c64398108e3484d900e223bbd87fd33ad96bf8a4d87c9070f31ad2898a31d51935728c60589e723ae82e2e880e6610711f7094a0e9b9f133f1422089c183cd9cc7504e475fb3ee4106c37b0bed5c2bb1104d85fb28bcfd88bf70fd92e3847c28b41430dbb616bce35c4e56c9c1a9efaec00bc9f11cddaedb864c1559eba0c20e32d60364b7866139e8c0f9f8c8df42bf16a4c135d05e45ce0d5dd4fb113941643397daade79a1021a159a663c5fc9d3cd1c3d1edeaa3c8275cc2dc237ba921023b8c9d20619473f5f559507cc6747c30d6f5b4a5e535cca06fe83a9c3aad257d4501e3c8d25cd262f612d1fe3412d34a066d25fab48e83d227f62efb7edf50d7135ace7de9e03b92838143298941d627a3dc76386a066de53b0752e0ce120008445b4ce95d5b472eab42d71d00a1dce1d92e2c64cbfd8936314c2d01ddceeba7ea837b22e34b8a2a6b24377c480cb03d4d605c94c870e66c4a8455890a26cc8e518090d3464971d548d15cd2d84d74b14392df8a2773c11d4bf9a59e81e48a56b528d779bfc6ddfe54f7bf2f51fed428d46c67c0f9d7f05642f984eba2a75cce5421d5340dd4b0c1248e9126620c05618892a3e9e2c294c482927b242d7a1f15e318908225de20068c123ff080deb8aa27e403e103f30216816b447b0f46a634198a0cf342d271ef5105cb062569e47f48a87be6a2af1ba4560b6d32b5885a679c1246cd82015108649598f7ea8b3b3aaae74b1795b4291f16e04b4cbb6d49d604531b5bb844c4a7be53decc014df5f08f7959175b2fbb1651bb08a57d527f9d62e84e8f824764be13f6bf0b36a41223a34471181f23250184c02d22c0b57458a12fbd7dcb0b7e56ef6e99f40df879380a51d5571cf29173b0ac761c880027f697dfa9af2883db2f180fc80cf8776d2e31000e7b3a8403a2cf835e2d79106ce543cd1d5fd3c4ac9fc68ae3265ea62ae8c20b1fbfbf5e236dc4d08f6b93abcd5b36dacbf3d5f6edb6f46e5b2f6dde68b3fe6df4d4e61d6cbbfe70ecc9ada977af80b742f79ad5dd7daa1de0970260e5bfd0b34da4facd84fd19b8f330d64e595d716fc9209609137ddb0162de6974e5e1b0bd7941ffd5c6e930ff42ac443b0baa625b824bf21b20fc194cef9e1bee3ec8f187e0e7ab41daec709847e575e56fea6ecb46acd36a00acc1d1f7fece12a8a86fec37e9497815bc8cb3ade4d24432074e0e2941b12c9cd4ba88883673a305eced2a13af242fcf88208c23b6c08ea63f31cc8aae3603440baf402446361b3d655d6706bf130b37d08e1c8e2ad9ed9a1e6ee01f161484872f783c7a0d6ab0601bc89510d85d3ece1a8993a2349fc6fc2b430a9a3552a368fc05d3f16d58ed52d7a5286b447871fc4f194ccaa35c46cfc522c88e5a5a001a7f13c39d81244ffcde65097f05f18dc0692b7ef0e23d3685195d30efe1662cef2e975abc9b72e4155f741b147dbbfb114d0fd7237dd5546398b435c21604e067a84d7bb2bf738bb9f8ba9ae9f7c21fc3c95affa18456092faec6cf40167f0710c1e450f15b50504423a732e32c60df57c0e25bd7213c0d1672cd5d63530cfbf8999f792425c65c5961684b6450437dc77741ac85864ed8d816098ea0072405ba9ff599842d6d04fad19e1d173cfa11e6b6f4a524b685875710576e375d259ba2afbafb732db8b674c54172b5b9e0b27443526ded6aeb1a9ed54ffeae085416bc615fc53dedba4cbb79779444141583f65df3ef169136d377bbe209dd23aedce151a2e723b2fe85e865e0d7b971fbca3a609197ddb2a8b96869866c3532b846abbb9e15ba2d6c3a91eee27a261ffd40251e131bd1a2cf93446e70229c05f594d28c0da57b27afc92a8f8ee19a4c8899afdba2ff791aa8e971c6c09c6703df940cfede37d2b09218bbeb6094414e9522d688d16c99b710804494a53630cf6a3784e1828282c683036eb09a206ace655abaf17a15e9163e1c99ef2db63d5f5e9be5a2e6da2add30a4566ff8b8322011425b8c9cf72e48d7cf152e2d8e207a1d310e5fc1385d0134fe2437b374e909d1052800b1270ac01475f3961ded65126e961448e26831d0819b73936d9848662b1a3706048bc2a62b7ec850e411dfa01b35f6944964dadebc9cc271dd15aee0e9e7ab8ab52c6e176752d228a02c7fbbe8185d3b9220b39a6c066db5a226980db7c3b46927ee9a8845d3dd1c7fa2817c04047a486deecf4dff72587b0e6155494e1d9c0e0775f8ad9596302e76cd5af49b653682588b1b214930600324c19d6cff1abe8037d9cab1ce735aa747e475730d66d1781a5be23abaa29ac2b725dc849437124649ab172d8a22e87929da0450d2d3ef7ffb98309c41a3b527b34be1b73503284ce7bcfe810a1425366009cc1d3bdd65aed26ad4bac8e4705664e385c71c4ca309c64ee7cfea98a3aa7ecf18ef351b101b0f81d24a109749e30f3bfd5c9f0c57fd1ebd4724b7085640069791ab1430ee6d23c80ca7493c8b538f7566dfcba23825378a85fbbc884237dc86911a81900ab0bd36186ee3ddad696f27e52236edcf82b35ec7ce8165a397bb6527fe6d41ed4697fdf5908c755c648b5e6e6e91c0445d703396f85d44db6c700bc7750af18c44501da19ab4c4bcd833bbc3bee22068a89e9b173b7921bb1e410aa0f9b51610422ad2fbeab4af41c5ceb35aa4abc4c52aa31a74b3f28d0ec31888b8c2c87dbe4f70af3800345a800c26731724bf4c21d9e9020a6955e0d749053838ec8f94ca900d4b428906d0ea0c4baac850b2f36be91a7468cb85c4c415d1ae1578b7d71b74975a2e4e31185dfc72d480c15a8a584ad39ace0d8149d541d0e71be83094f799163583e986389809b1112b885f1d3e00b3c0a40aa170f2932880366189c04d3b5cbfba32d1c42e1ad947b0df36f9222eb5966ace7d82793b0f87c4d1e7cd8ecc976890a946f2581c0db057c9cef2d857fe246af141a80d2599b0cde506b168a8925929c644baaeb0107e33f4fe86c14140967d1ae100a1ff90bde88182d83d77da2e73058ab4ded3fd108b263a815bc5ac4a6a414f1741a485dea8137c43900ebc8430cdfb17acf70690330d5d7a9bc9c4089d6c3fb296a2b5208ab79c1abde7c7c07174fb16ce4580219140a836373eb2453b1c67c742437c96e57e0853a5ec5bf15e26663162577192b6b6df41fae8ac32afc20e610c1129de19eaf8628223ea8e4b09652bda90f2d7fa8ee76dde019c983b16a10eccbd3f10ff07feff64cbf9b68f7e1b750de301644cf93568a01f7ee2f579903f98aad578ee3a4f5ba963ca92336d06dd68c1b40115d5316ce20a72a5f90dbfa3a9869faf60cebffdc7ff5cb260ef6631d5feb8c1fbc80414e047f113ab35b9ee4252b8cbf2e8b25c9b9b19a6ca11ce40a9d4d37efe1c2f4214e054f8c2e0dd1cc3517ab95231bc9fb2a1f45a2405171ca17f4485bf6c12d699c46d4c1170e5f7002837fb378f735cc26a822ea2d2b3c8ed50dc106ee0ccd42881ee0ba6f92231c3b8a872a1af4b946ecefffd5e8278c0c18c7997787962c9b610b080734bc21d7b751932a9a0588511459e0b27f16ea05a8247eeac755a942c01cc240f12143229eeca52b7d907f0fb1b82472254878048bb3ab3e95c33bfeb51e058a02c8b3401767a80b20cc7c9e389c6093ade031e2a0c94934fa74d0e0820638e2a15b0193c3e115c5c58581c225a5e39c30311f1b417b225219958d51ce514c721380d88a1d31b2b2ae89142c7f92e53c36c7f2c74d09f46ce577b276302254f57d43973462c1b67748c26e8295e1f6047e8c4788b7428a5538c136b95305e2ec12942c768150701d2c4acb92030262ecd8941176827070869631e5c58181e97e164a15bb49e0303c9628e5c60180797744e2776cda1ac74fc215fa5d75e9876f15584c629c097021adb06bf0a505a35f8129038c0d0f672ff347559d58a5519a576dc0c719e6ef761c32f3a8ed8fa03b83600c19676de99fce5c61f9e7d684a0175d0c9f711e0e00ed77eecb65306675c628d4a91339503e9d8eec8fa290d9438668559f671116cd3e2f48d6843a7eecc98d237137abb14c3ad9f306ec06c37dfb6c645d83975505b0c79e3b4b20bd051bc4d05bbaab22865adca54b6c4c26660501d26502f78027ca168c2d7e35b0568977563376ac81ff19789453d365f8f74ee9dabdff97b3ea7dbb57305d4bea8d8db957168f0a8d0b7cd2402c96c9f56c1233f29b9267fd04d3e7694e50b1fe3ad782a3ff76bed9ac0fbd0f77fb6702a8254155add4beb52b286c59cdffbec943ee0faf8ef4dc490fb30e5067c1259f4721237044bb6b0e4400256f0b02f6eb192808fdb709da9f242bfb661099be605a1318040d1384d1abf409bbf1447f0bcbdb2f95989c13e9e13a0b2284df510278a0f01f40f3092150451a53d07681df097392a2286885af9e7c14597829e2d91dc9a728e25b1502c880beb42553db0253d14c2a51cd1ee69bbbd9842fbcdd22c05bafa30064e7396bba4d468237eda6f6cb5469e87a45b70af1a73435b278a585e6098678774c3de6940a12155238fc5d42fb1355d74e2d587b9c550a93c305908b8c38b66546c8a5ac65fa7e6a9ff564bc7127e0a83bdd473750b5494985be61a6c30ec43c56288416ceeca872ea1c0e16800e059975c86b3f471160a857d7b378e82d81dc362fe7e1b8f3aebb0aa23f8a00a4f414635e3665b27174b90503e41196cf5d2cf12bb6f4922dfdbfd16cafd80ac7c56a6397bf8acbecfddff5a63a7586109cd5c7d09760b1edaad8c7f253f3a51d01af30257b9533304214a4a8f44232b1086792e32575d767cd2a8e107c267a716ad19ebc6ec0646eb9e640429f98ba9839e784b5537105103ee2421cd3f548e7f3578520d9771f043129ebf9d75db68a8fcf06a0190d2e766a44c82492bfc438a1c34fc003c9c812bfe5a8281126c71bcb0a701430e8ce14d0ce2776305fc990635a4fbafd790e184f076c8864fa527870d7b0ca7c539711a33cb227cc3548302b9a753204535eb00cf85a63d79adb502486a5fab379bcab4714829ea6158c685035934cf1cfe503316c05cd7d825d5799d36877376a7198f3d11c1c6f2397daddf20765b8680ced5291547566f1aa7d3864acc76d4490768d3394234604d45ede64acc691ab8514ddcb82a1f2890ec588597dbe2986c8dc20630bf08be712521ffa8c7453d3a19c3499885983c0c5be314e0d37909b62243c9673beed4e9ee3343b4b41c213d83891065cc3a9f175b0c31aaf6106f12e0518956f81cc0ac6cdd717f763493f17b2e8742a24f9953e559d204bd0fae76bc14aa741f61a8f633c9157a3f9cd87d19a4d27cca84aacf9227c89db856ea1cbad626968fb57155a5a120fd1cb84bd1f4a223252d92eab1ff0767d894917d819338eaa67ab8e8a9d5946d2fd44ac1324ea5e8a65b7f107f43d88ea54e2ac39c57b9fc40bb8cbd81e5c272a9ad7f268f39facabe6da07db8f3edd638f4f2808f29067e76ec7bc082b203f15aca2dfe2341b4fe7e875eaf4da36494dc3fa689dda3634c3ee4a533cd7a651bfc80e70d80b5eb4e32c76f4249dce9c006cca3afe6fe7f8172c29fb9e37096b799e61e7b21b964744318e28032518b6d05374273ac6dc74ecd7dded9f1dff8ca1407ccee3520d3b6caefb00320bdcffbd7c8da83e05201c8279e0b81699a9c65acb5f5c12667b76ef387b55306ba4d37819ff985d21bafe703ffacd0ec90f22f10d164a424175a4d077f977e140ca45d0ee63068bad3c2530a05dd853e2e736d6a72dc719b20d1f2f896220c7009c6051d734833203b565f660f7cd9863fd8c73ca3dec09bb94f21461a578478695c70d39560f8e0e54b9a16a205df4dd04f9ac1db9234ac326c758f825a1ce694a8982650769cc27338562f620c4e5950d9103e27baf8de709b62715872721defbbb99de79d6775e0ac9c87e7bd358893597baa96854c0f5dfddc785c2a3794eb3565fa31d35a75f4a0073da64917153b93c941fc34f38c80c0e0f1bb7cd48c74e90dfd178cbf0c4a9f6dfe01dd3dd30909e5527866297d69c9cdb9d67caf4bc9ea2e5ccfd91947320391c85ce28e5b8d542fcfc1c2e2bf8fea8cb4fe9754627fbb531b6a2f3e2c99464ea2c74501b40d9c45f3bfd45fbc55421b15c0d26e7c15ad921a8b21c65054e31d5ceeddd67c818fedf2caa21b39eb7130a48517f9a33973a78eb8a00044a9c4fe393a004ab56bbcfe6aed530ae646c33e9f2c52c34f20fa9e03d330c4a529cdc418125f2fcec2899fcd9fdb907b75007e386f1a25740799698468b414a27b26ac1558693b1bb1955f13b248fdb8b196fcf9c57e13cb5210a5811ba3a919d43d42506665dab1bee27cd512b1b8cd2a75a8d993104bc2201fc71086b08dd00de828685152b87981ef36fa42d1220f218b993121a3c1e91eb3de3ff65a1ae85d93f6848b6c713dbdd599f45541cf4ff7d89a41239dcc3d901986eb985cbb7805a7d4c4e45056cd99c16a24aa07672e472edbacdc80f5c056eacc9f07ce4d5754c7d83bfe5849d47beec9eb67eb85f211bac43646862bba3f7187788fbdf8356bc08b2c93b560dee77c4dde79933a0aca62406a92f4fb6cff5b14ed8434848dc0d7db1d2d3626889b36f5349b92a4e913d3e5bbdbeb7b444cfedfdd79be17ee04c80751ec86be66995d4a7ed9bb7b11a4c208759c8791252a6d8b1315b9d45a3edea63378461f7b2035a67b0d89788240d8720523440f736379749a262a26cdcaf01fdeb8de60a179289b49b557375cb2ab58973c8f663ba40e4a3b29e655138414b12e7cc3789f968fedfefb8c72d055b909f99a49ba369d9aa10a2e473ef5fb33c8044d2dcdfffeade5d4f44ac96ec927ffee6cffaffc93477215561c6242ca3e4308551714373568e249a071d525b93b6e9ba34bace0b87aa6c62b81987d5a4fdd32006191505368c6c77a7ea2ac155792fb97053116b8ca043af15d847c1ed755c0c7225cea427bae0ff498220a6d467510a705c4fc2cb0e9da47a648e40994096023932eb9699564b1d221685be57068cf072cde180d97905cdfa3c7105ae000697fb01f4330cb5261c1f4c24f684e5bc5afc4a8185a57ced407ffb9d9e2f983026c2f97e0a5c0d33748476661d0a0ddf93bcf470659bfe9cd0f018062fd349492e40f72558653f3c5619923ec3a20973bb2cb91dbad8643fbce787338407fd2873dc76ffef777f8f3b5ff3e1ad71bd131cdc48528acfcd6fec4633a65fcfd751a30f68ccb4bf43d6500f3384ee3ff57a8ec1efc3ef7ccfab877dbbe4cb659a89a772ad264f8768f9c7c506e71b2c247c3210925c038e59431e1d927bf3a19b2cf2d08d152901830a66efe120094b707c9718061010304fa5915441c0c85a231f358c44775dc4c3f879739dd477430a0e11a058ada8f815612afbb5a4d2c6392fbcfdd01c70ec3f152aa29e0a0f79fde3ebb2053e1783823c7a1b61d4c7860b2001a494be64e42fe2b5b9405cd3b0f0b7cb16d53c5f5d40934eecc6ce49183c5a92c3920165843e19bcd4f172cbc59dea154595442674b090ef6af5ce8c5082ba0cc4f9aa1f888d182938fc139c3b0c702d5f9c36b529a85cd050c3fd7ff4c0f2f79533677c6d0272409f2443bca09af6b87ff7b4606b6e5fd8f8283a36aef3d180b1a52777ebea9ff7afa021bc8853fdf1c4b86ce0f2638fc3666834865f79ac37cd73ca21d208b5e755567a61947612668bcaa027449036e3186ef0f3430aea690033cc3333cc3333cc333460f9a7f105975db33e58af80c17542d2949ee160ada12ee1f001e00c0a3d9ee66df0f7e060139108a0f29108e4828f709eb392606c711489f52d390315449922449529224d1c43301240187114e614bd39958427fe94558dfb3a4bd1f779db6141fe020825e529c99705230c1e341b8318c24932ae976e36b57f686307699edac9af960609f849d0d16b24ff6346e0003a991515b977f71ca6177b257459327f30525ea65cb302925a9fc1bbd4063e4e68e96b969738317e7ad0e6a49f60d2bea8d5d245ac4c91fe4c293c4baa0428ca7fa93a74fdc0ce1462ed624c47ce846c94b626ee02229e64e2d39ea7857b760e37f3a74cc9b78e21e6ed86213aadc634f658d6e32d2e3462d343977aab56a4fe71b2dee8a5b2299713eab4e881bb328cb42f758ca01c40d599876ee72d55ad00ea703f0cc943fdc88c57f213e5cc8da1eb8018b840e27963eee8e466d448403375e614d49e2d99628ba258e811baec0a2e753adea249f888854e06fb4a28e5f9ff3ee74cd75462c708315ff758d74eafc9fa91babf892183be7d30ffda926e1862ad81ce23bba7c9e553c15dd8646857f36e9a20a0023dc4045d7416b762c56ca6d086e9ce2b08a29a67fa5926375c314aea65589e994193a2fc28d52681e2d2651b7430aad62364bcf5c745f083746e1a4996b0ae1294f6c841ba2404a4f0eed1c73cecc93e04628ec2411e5525ebdeedd00457f5be13de529c137bc811b9f58a2e45825b91ead9323222228b8e189523033eb9027cc79f646276e70e2b89a163edbe3c626f29cd5f309e29f53ca8edcd00439eb1d73d8bba076263c9728b1ee928c8888b0e006267c59136773f4bc65e98d4bb895e6d929f611111115dcb044662545c99a95ae5a57e29aae89ca37dbfe299185b1f029bd64eae44ee28abb2dcf6919f2a3248cd226454dfd76f58944e964b345abce7c95410249b5b324279c1c6d7984d972f9dea5709224e56f38829bd81c5de2df625305c6386349d0de0316371a819f9ff4efd5d6da19c48c1e7783115b88cc714dfa6bfd750c0e90716311e5efe6bb73ef8c653212811b8a78d384499bd14454e75159b39b5aa8ce08901e63dc4084413c4aacdc540c6e1c829d70cfea2bb6e086215aa9d12cf91e7dd3322222d26348056e14a2d94f82c646661dab08519266fddca434973324c28d4124dcf67fd4cd175bfd1b82d0e478963b2a1b881b8038e39ba49536d6d24740b8f1073489e1c4f45efe34bfe1073b6ead494942c86b0910df81d7e0461fdcacf7b0b1b4e4509622dce00396febbf69a4fc8de24e1c61e8c16fe64b72aa8d48c841b7a603a9d99acd9dfc3876ab891877b47c2d2f26f97fcc1c399994dfeeb686a4278861b77f04a2e7832493f32537638c6577fc79a201f791db0122c6a7e4939e15f3a7cdebb16df3d73f053ffc69966eeacbb1c4e3fdb142526676ffe033db8118782c719b98d8d993c6498f1406ec0c1704276d42c9bde9f82c68d37bc52e1c4b83287dc7043b1e4ca6f77eb9c4e921e7803e9c28d3664b1f43cad49e1693f416eb021d73ad176356899b76ba83b3ec921e5bc33c41b6ae0b4424cc3c6d7cc0c166ea4c19cc25ece9ca43cd53e861b68b032e88c556a6db42d840f37cea078bf05b728f7aff307c6f0c0181d188303636c600c0d8c91813130d000be61865bbfc4436b88fbf194a151db8a1dd9f4e94a8f24dc204396e9cddeede5bcf3d1230d3663880a44448408711f41ce10f2013286f4800337c6c025d578cfb31d3a751cd0831b6270a5d7df7d2cc9a6370c7d32cfd4247e4c13c615fce00618b2b8944fbc91bbcba88c1b5f5853aecd1a1bf782353a95826df63aadbbe0955c399ef4779f9fc38546a62e624d8cbd57b700831b5ab891857f2b5364c3c735ed9146902137b090f8d1a4b1f41fa69decc18d2ba0a61fe4f4da52b270056e5881bae4e1844d97bed35a85ac849dce5161cbcc1f66fcc80d2a14d5f369bf42891dff142e4f36193feb73982d053cf694e58e594c52370acf87374d95b3cd6b42e1ae93dce377b854be3da1246339ceb7a245ea04443daffdab6ebae44dd8b253cac9e49a8d1b138e653397e48f9aa4e82ca1f05a27cd8eea9a8c4ac8736b5b84a7664b2b09acce46c92b62f9638d84e3cbb846c577dff411d4945a4cea3313344ce786114af28b6acb46fabd5b84d74efac2c7b366a63d6e10a196ede958c99031031bc320cb72dacf665be14a1bc250e3e48fd92f2f41d69302119113c446308c16fb19c38bad9e070d1df8068eb7404484033680515d12bf2e79fe8ba3df66e5563fb9bc7d516578c996d5631a9f5e98eabfdea325ef8b6e086cf0a29c35f9bd61a565f52edafcf1714fce3bd2161bba4043b2c4fa72750d968d5ce49fc492442d4bb9a564a447902167888878d9c0c55a1f93b019d3c67b68e316366c916df6126aba808d5a9ca4fe136e3426031b683f7b860ad270113cc1062df67c5d5294e5740c968f9cd580fb08b2820064c2c62c9a5b3f713c27d53049597827aab9a749e98249d988455972f4248d286d7961c1fefee44e25498a9a0407365e817a4a9ba9b99e4cde157cce88daf4dbd15c6e0561255a65b5ce58a8ac486d572d5ac96312d30d6cac02d910f7b9fb01b1a10aa36bbd9e5cd239b0910a2ac7fc639237a76ff8870d54506fa2f657099771ed53f869193bc524db07197dd8308596fb12dfe3319e7c96a2dfca54db5775f72734b0410aaa2c66f79ca82c66b283513427b6e75a852bb184e36043146e4539f9c247cf0a7a28f6d978cf9acd49cc64a48719437a78086c8022b17f2c5b5aab3c7b95c1c627f0ba3432997f4f245c6d85da59f868ab9032d8e8c423b55a315da5395a7a0c3638514e9ac232a577a26f13d8d8046162922c96586942ad34e9845b131b99e024494ba5b953f86b60b08189728cd9a24992fc5da176c1c625d0984cfe1cd74d78cb29c1181f20630a362c81058f5739c9b8cb955cc146258adf33e1c4ce8c62192ad8a044d12f46f734b9d0da4da2932449b22bb7b6e01b326c48e2cd9494dc706ff1a423e19e389b4bce23de9d03897356fdad8e0f0ff13d22c1feb2c5b29d7ad838e23e79322669fbc36ec846234e628ac97e6fbfada60d46d858449643c6187d34ebf26543117a686775eef28ddfda4844922833717652ea8d1a1b88283366df89323193148ed0c0c621d4f4569df7e484360dcfc10e6c18c23c61b242b3b35ee6f818e1410f3386f8b05188cbe3a89a747ff37f13224b67398465ffef71109ed877d96689fef1326243109609b2aae9ee9a9ea5e1390072868d4070e92edd9757494b1d1111393600b1854e49dbd72c6ab67fa8938999781db9d633dbf0838d3ed0b96c73c5b9d8c0061f3ca9523753ab3d98d69ef96eb16b373ac1861eca41db2df5e48fe1636ce421ff737f8fb9c5437b257d7c98ecda71d9b84355399966e3feb6535260c30eab979c24cbc76cd42199935b0df3c9532cb14187bfdf37ffe85b56866ccce113d3f426372dbf0f4744446cc8c1aa78f72f21bb639f8c888868c2461cd48f8cdfd227b767fe83d77ff062c2061cd01ab7becccd9a376f287665e6c535c13583b0e18692c5e7e82948d86883e795935052eb760c2608c6b0c106da4a90ad575fb3544744441c61630d86b04fd2264db6fbff881ab6b8dc27ed9dac21ac1111911fece3a0d11e04042222bf081b69b08106ff64b15abdbf24cd35222262051b67287c99c6954cf2a43c304391f1fd67be632187828d3228f7e55aa93d8d063bb14186e2afac6ca42ae39fd818836a258c6a12dfa42737222212051b62387996309bbffa2d52b0118643afe4cd8937ef686064880f1708f9f10303638c3186efc0c7136c80c1986372e5dc894b398f828d2fe4d539e798f23ed5520d8cf18131544006079860c30bac5559b6ac327143b7d10532789455d73c3fa15d820d2ee8e667b7b669b3c676a48790158201001cc1c616beb811b26145fa63d30263b93e7592bb4dd262230b7585d9c93572263b041b58a8ade3c9336e491cb98460e30a665ab05c17aafde4e088888815fa147563273da983c58260a30aa637938edebc266bec0363a8808c1f1bc4cbf091333840860f6c50e1acfb5049fd5e828d29f45ab33b966d728736a4a0099a27e5c9bdfe9675828d28984d379c54e9e1b96128141f1ae4444fd3627b425993ac3d6bb1caa4d609787a50d5cb5a13b09c1a93b8a16402399263ff63929666969094d56ee44dc5ecbf12de93263b49768a4ca924a8f13cfb6bdf75976604488f1b00f19146db404227dbb664ae9613a64c63880f37d838c2295afcbed27dfcd816f4b061844bd690922b9933c436c44611c8137f3f942c0ab14184cbf35a90088bea2967185eb68f9bc9dc16d909c39827770c93dbdc2925189db09fba97efff96a407358081fabd47ad9749cbf02f92a4df144bc54c48597d6198eb71ab98a46b4617d4e885f69ee4fc31695dd69316d4e04559ab2ba565dbadee152c4005357651d6937e97bc310f09a1862ece7c6671c3f3d5d572841ab9a0c2cf6fd0e93d4155bf01904d8119357091a0b395272a69fa7d6ff1af879cd85e36cd9790345c04b668bb4c2b496214a9f8d622b313b51dc9a0a1252ddaa0d53d7d31b7296516e54d325249de81d4908549bf9355e5be825824ac9694a0f1b623d6203560911ca524df30f29ec2fb0aadb4b35c9de6cda173c553d1c4d598b7d2d3d80a3c8de5f6e77e95a7d460459a9d2627e950275b3ae280168c2182069011c48c215b841aab48c39ee0f94acaf77d06a9a18a3533f75d2e8b75932415fd9db831a58def29d0410d54bc77b2af65926e13c453241a67bf7ed23fde262bf951c31449a2cb68f6923e6fdcd1a8510ab47b93187347cb5237298a3d1f3f7f4e4aea8eb951e821a22c4f6a99b05014c725a95c8284e6b7170a6c456534774ffba71cd40045169d9345edbe5a5972707cc48c1a9f48ee36ddd447744a45442a30a4071c38a386279c0a5df92f47b9b3be13c426f94a39c5d0b4d9327c08087cd4e0046f72cbc7bfe6c9576b6ca2ee5496b6c36a0a9ee26d4609b88626d49273dcaca8ddedf94c9c83fe5c981113997dc8141e192d257e8984eb90fdf2b378b52d51a4a76bf495f218b512c89594503964b66e901274d09793d7f4f5fc26c1986872148b9be9fb22094349b1628ef1d62ed50c6a4462d7178d93ff21915c266b4597ff083d6a6a8dcd991d4f3b22c9bc20fd390999e44650c13f68b2ca32c23462517d37b708d3d398050b8fcaf904063514a14965c8dc92f2dd1aa89188a692fe46ffbc632993a006222ebd1daf58db64a29e037721350ea107bff9e417a5eaafa96188f2e56a3a57f8cb9c1a8560df6d93850ba3a9f28888485f0d42a43b15bb3445cb9f1b841d611e36532a88929b5b927e2b66f6ae462036b90ce97a4ddf39019149bbb3259eb38423438054a0c61fcabbbfff99fde43a0de6400d3f90a19e2bc96ae6e87ad7e8430d3ed8b6732549c2f55b34a880031a20833d90a0861e4a50230f35f0405eec0d72a9a72eee2363b4a1c61db2fa12243f856698de0edcc534329d7fb12b5b8732ec9cc7e5c7844a35e890e598e28495c5b4d821c30335e650430ea6d9c9d8f9fa9b0992c1811a71405349e99ee127755f2322222ff01a7030f786342b6c2c4c64b43e23222235dce09a6419b55b2cc5eb8c8888f4a0428d36f029968fa6ca9bb21e4ea8c1864cfc2f294954fd74126aacc1880f6b52b699c4687a20a1861a9eb8b431675f6cebbb461af0dcbc143551fd8479053220e30335d060670baba9796deaa635d43803266bc5ecb27133142707dd6856828f07d150a30c98f0177396ce92ab3619148fcb0ddbe62788c906c95d29eaad98f44bf846e3e5325f0926dbdacd9b4546ed2498bd26b3d82749101b096faa57922461badf3dc2276fd8ea4ff77fb246c0dd3de6dce696f298a308fcfaa5579e8eda5e0e2218f9dd5e1e1bc699a2e3bfc685b1f46fa668b12d638c07a3d7a94ab557e195e2c0e04f63c5ee5c9775f25fdc25fac59532413ee4be48fe24c9f748de8b2ea778d5713fd79de605fba3b9ef76472fa777c1765c49f177be7a6b5db8275ced0559d918ea5cf03137b3751f2a51e3e211f9e0a9d75205ffdca2fd922d5ef4d8c2989dd36749fc74712d18379363d3c4a6244c0b2ffd42e77455d5ef2c2ccbb9e642d437aab2e0ccb25fc23f53aab160ed55c792b4adf184c5dfd1de4e8e1383f70a7a2e4e7e9fa0ad39573016c7dc3a6dd2eb5ad1a55b8a12b3cad058c145f17869939ce493b28a835e9c08dfecbfaa0215993a7f398f662a7ecf2d6ad2b57e2954d8295bd3bee5cc29758a3aee691e8f2945c714c88565dfd8fdb79252f039dcc7b9246d55428a448f16f36f2ae9349d51a479aa3d3746510b1d5120b7d1217350cffd87a27bf70a4d295e927350246969aa56ccddea4f54c2da9adc9ea22bec09c3b3c93946dd132adc892ec5a6877ed4bc97139b702163ac28317cbb09bd2fc67bb51c75a39ab0c4132c5ea88cf699f854633a39598eb363c29caa71b2c56c51cb4bb41544c24269894a969243ef64c961ac84b99e757e624a64fd7525e5d424cc3eea229f628e4a62af4bf3e99836dc84239149c17f74d234830f892393787134d474ff883de6ebd4704dd7b423ee6c1dbd6a6d84a9911abf938ce052cdcac78de40617916872d68a676bf93f452059e2a6662511dcdaf69524574c2e2250f3e89672c91ee2bd2ac13fd334c4f9426bc69ce525475b884bf21ca2928f999c27845b3badb9ddfe161f84ad6d714eaa4e39d782a8e36298f63ebff604a290dfe125c7aaf21810a574ad9a3475638eff704c9b9ca419b3f7eb87b5648f498a39c4bedb07eaf256b0900f6c6c0f573aeff1eee18a694623a57d93aa072b55ae52a8574ae6a18a368bd69496c1c44371d6c498a249d6ce1d92b7de242fef500fed909849be92437d79c9d5e1ccbc8feca8ad351d566993c3ced23968d9ab2cd689ca814c9389941f0b71671c74cf71aaab4463d909077a5d2d59ac94d3a6f30d56a8a02fd3d249e2744396aa4ebab0e17358b30d27f14471cb247b9292c9063d4cd4f789199b31d760ce141ae5996983986a307732a9fe2c37a6976950e24bd4749b348a976848fc12b4bf63444a976738cb9e94945f123d936628ef6a7e2837293626cb90d96554cd5a9b579264a0c2ba2a8b74a793720c499982c4dd6f8ad9a318e8e9e85025c9ad1dc7309c5ed324e17430186f54e4c593706a7fe1dca0d7bdf9e3f4ec053645468deffb0f7217883d31c5ca612d9972c188faec9723363bb905b4c6827b7f968d530b7812b2eff64dd20f66a1309fa4a496c44e2662a1726dbf351b0d19c42bf0f2d1e216c2ce3eb44217f3a6c2a6da7d0b562121e2d553553229584885e4b624315dfe6ffca750454b76f164e9cf7129d0594f36233ae7b0a390681f2b5fa566933a50e833d786ff589b2e3fe170d533d9d6ebd93ac1d9a8249b5d3c2dd9262487dbe7a5f18ae9640221aa1f359e9c38e7120a8b535e52a984e4cf95297f44f48f4948ba768fb20d26640e09c96a6e55314cd5538e70b84bd1e5265700232475e5cae7f1d9355d018a80a57eb87b92cbb2740520c229c5cbd9b9cb848d0f430b7da286ed73f55b186cccce92ad35754c07c398729b49ddafd732304c72b2f230da2f2aa13d85f76495a1335f74bd621bf3376d89592fcc1d473ef73ac6b1182f2a4d316f5f925d38de17f34ea5e40e992e4ad927976c2ec8f8f54fd6c9d723c3059f25173c836f647f8b4d1246237cb785293a3d93eab5a863dd06cfef1abd695185b018dbb36e793d0b5dfc663dbf7eda5716cc58e4ac9c7c9ad158609260b14db27c929eb0488e7935a65ad6bae42b32492e99ac27d574d015edc6457d27f92ec5562c277d274bf395b26758e1a476989b4b7f5131abf0b42d49f2ab42bd2c67327d3f6a9954bc9ea2eec947dac651f1891daa4eae8c95e49ca2fa3c9d25b3299cdeee514bb19ae690265a454d2b2912c36f36d14cd2d170147fd58b99789a9736515c99a1f555aaac6342c15d495a73679d390514cdee650d2f66b29f305c9e0bde6da2d49ea04a1039f1b07662aa139f6461e2d349e144f2b4c67d4d523661cd966afd49f95349d1c441d324a1bc5caf844a264cb39c35d1c2357f4c34bf5b2f7f5d96ff25b66462528f4e1defb74431bb363c6ab81dbd12dffb5f94a4a769e394403cfcc768e890b24e6229ab2ec9c48f9f5549e01773d094cf6aaa8cc4916a929999d59824244e59492df74dd9948f30ea98a6be4e294ed01146eb18aae782957a8d70bd73ee38f929d7c608a38d4f95a82d82f51377a53693909a22fcaa8dfececc1a2d11ea66d9dd4eb144328508aed27def9b8d76a50ea1bb45a67d1ddd6c528628e7282d53a90ab17c486fb3b698a43c21cc27d5265e7e4ab30f0237cb3d65d9b9ab0bc2f49a84cbb80391f6781a4f3f19bd0544b2f53969c92597dcfe01f528172b19ea34f4031db52bf5e3632cec03d1a9abf25973458f0f0965de55d924af90ed0199eb4e9115153a7a287a66bcf0f2e09d9498fd3dbc62785853d6bd6839354be60e84d6891f7395d801afe9e8ca27491dd031d9af3f8fc70b2574f833cffe8450ef8b9239185232457eac6fd0e54057ccd11cbdfff089839e44c67308cd493b70b8d3479ee68cb631e70d8931ca0497edd7d4710397d9a4ca5d1b32c1f4a3a5389b67c369329a98b2f484c56b487384ebcca549a706b3e96f46cd8c4948d390a49a5cd365aecc8806d32b36472d4de7cc19dc8aa3a525764ac13283e14b533b83a879ca6089529351f6152fd18888c80e4444840c01b23e826c0b900064587f73ee8bce374d71c4c78f1ffb407c308000637835d3cfe64e6f51420288a151ed0a96d6790101c270c64ca21e5a05c3b73f1171a9570101be6069b49c2d4b31b3f65e58d389495fdde450d5a680005d38cf598af49df5549a216890800baf97a8e1a41cb302026cc177bf8d8c9e4b72d819d8800908a005b235dfc99149f3a6360b9e60498a16328a85c37512f35aeaef92730565e6e344f9982b7c9410c00a558a9b3c88647aae9c2a9cef21af927f05ddcb488f1df2c3fb0104a002adb136d8d9f88d7b231b20c0144a424707b724490029b8399bb871e77b62bf512889bd39b387160a577d8a6f3b8fe5957c42490aa3275e5cae246f4e306cebe7e41f6297524d6893d0adb12439cde9c484d2977ce3937bea1d0e11b28084004b208012d4fc5131d47f67354c92e0f7c63efb2c1102019040c97e994c850970844b58910bdd647a9a03060218c1e4ed55f51b5dbbc42f10a00807999c71932f4979735c200011d674639e842953f10d17700c23693e6e87bad3140d0b43c90e7631c4fc7fadc138693c736fcfdb1a1f0e605451edc48b39ff8b93f0c933b352b6a4f51117e0f0059b2b55dfc5a4f9a4bb17444929680a7626258be1e0852972a231435d2e2967174d9bf6eef77ac888a6804317c52f8b3967d9bcb96a2e6ef9285fd9252d26491cb848b8ff1493d5ae7229b7c04b2a58a7f1d2a4390e5b5831c59834bc098e5a5cfeb12749dbf9a24a5ab452f9e46b4d62236a1470cc222989e35b31ce50c0218b3b3dbca8563c167de7f49e3a7b0320437aa41164c80870c0c2bfd8977c5f7b459b6392bc4924bc56c2e10aa5e3e5d6f464af1eca028e569cd7b3e7f01763a695acd833caf685a8e5d867051cab404af6389b83d788ea3854611cb58ecaad49451fea69a44232010e549419b57b9e92a720bf2e85369fd6fe9a80c31489e5690e9d73a4e7588a63a5181b227b41a30a0026e0204549325464c89c3d7f8ec2dd9cf9d67af1c31ac0210a4b574ad6bc235be587c24b957cb5e52f26491db1000e501835ab3fa5a83997a99fe842c718acc63de4898db890207b868848057078c2f34a4d5268184727b20b934934b7e0e0442571b27678a7ee938263139a6d4ab28a8a9a38f7be46a7a4ce80231367fe9c96efee1722830007261233cd379749bb9cc48c212d20238d2043807000c725684db93a0593c5ddfd36a38c4403005ac06189463bdbc71cdd93c7201a382a91f8c9096bd29d7b724fc04109b2339f7aa89cf84e018011704c22ddb3d891cc52a26a4270480247241af150a739c9887f6aa4471a1b648c317a0c32d880031266f02ef994a32e4c362222d26308103f41fc0422221bc0f188b24fe79b3cb59c4a1c610110138888f8b8010e47285a6652f6bc25d47f86a31164c66476c25d81322a800132c868328eb7e0c78f2d4305222264f00007236acf39864f65bbaeee8888480534408690068206193fc0b108c64c67e387e68d6951441aedbd528a932be04804d6b93e447426de9434a959e35854c07188522fe9444f0917c51cf1c103128ca181315640860a7018228d988bd4ac9ebbe2380a91c7ebac5f413322a2021c84b02fcd93d6fcc5586de4043806f155b2b89292183d7fec0d380187204a699218a3f44c6c76c750c1182510028e402459189fc8ccb97f7d4068a2c92197ae7c56ed3f24e47dc4d77eccfbab1f3ccf1a67b3957c3d011c7d30eba4df64923464c87cb857bb2db7fee6cf0be24078d0011c7b702e43db47355be5bc1ecabef963738b7a3e3b0f6f8a3997f7ccd5e885073d84a58a7c7ad99d8c88886c00c71d9eeb14377192b7cdc9480370d8c1a89d525d2a51b684f40c1c75e8a4ad9ca35bf420f706a183ddd12123cf63b435cdc03187379b6ef6f06fa4070e39d4be963579edf5999ce1830734c01107c62ef4c95c98a4e18103f11ee08083a14379cc2906f72c1a38dec0a698d4f11782c30d7884a590d1ed4bdeb481bbe8b497356db52419111161831ed463123baf4170aca1945e8fb5931a52f9e015bc3c23222269780e7a9881230d496a9dd74dfedace19178206c5e39dbcf1cbbca62e03c719924da3a7fa246c77a87ee030c3db264b7a46d65f864db6fd922ecbe7fa840cd56c67934e54b3f31e11113103480f83630c96f8d92e9f621963d40638c4709ce899299d7fdf85418953efb524679a3f7380030ce5bef52aa974e529fa85f37ff6bcf681c30b678c1d2a5669e795942e3c1f4e38e96c673a261172031c5c48ce0c1d972effbb21215bb03fbf2b839f9ce3de8888c810213e7ef8101f233ce831c46fa0053a7889d27144937806471628fbccf339e4fad288055309219b1b4aae5069b8d451327d9638acb0d9c9260972327f1a1d1c55f8d583eeaa6c6b26958c15e0a082f9651e272babc4471c533065d6a6e3ea598e130e29983edd76c5dcdbd74c1938a2606a84c49dea49eb09e9b102324e100c88881c20692438a080e3093d70380147138ca9d4941e3cc7a41ef103c40438c0c104e7f2adad764b4a31e258025ba631fcb667060d560187128ecff155226767f3260a389250ae782bfa31353601aad8918a53f9e5cfb9bb538eb98f1da8a0a4647b26ff7f455f46769cc23c156c9345cfef30459bc72bd6867e4729d00a9ffd184ef4144a9eb083145612f91fcf6bc7280afa9a426f8912e6b228ce4fa2496a284e5d7d9a334b4686bc0314477494b47eae6e0dedf8049ee55acbbfd5d2e6a5788292a431293a48e8aab5a313c9ab8713afa56e64e4849dae36f64fda44fe5e255b86edb730191111d9a1893acf65b2d09f4c70626a3ef12dc6264a30717650d51227652b9c2397307c59d4870f11e7f30e4b1cf7d9175ff5febe2f20eca80427c99e7179e311119121427cc88e20c80e4a183b95182d16b7c08e49bc394b9669451245b4796b8efdad5424707df3fbd4e1b352dca3083b2051cc2599a83fcd8eda23ba0f336f6229471ccdf6627df00cf9f70e7634223123e39c6498116a85e9d33c1a744c5e04274495f861afaebd55841ad543ed2ea967dc135146498693f4436c127221eb3f7620224b9e524a854ae2fe07073b0ea1486b9cf073bae6b1218893e63d26f1356f922e44b2bf7d5f8eaaf93335d2c3c7eec04790337af470c00e4224c5e5a8ba714db070f9f1b7631007b91265ac52f2527dc4ec10c451552addf5c86709fde0bd8119234880904d40ee084461337c73bc93911bdcc08c119c61460944446e60c68f09ec004495848b1abf5fc36a6a64c71ffc1017e23fa7b689b7c30fe6b3ead40f51c88e9c8077f4c137cf25cf6ece3c9bb3830fe63fbbef68d3712bbe87a3786659ab97f89edda107328ded949e6416cc1b1111f98146193fd0208188888888101fbc230f6ced868a721f173b8e07d3b8f7c587bcb994bcc3eaa7653a15a37d7d3bec600e33e7299e78fb19dd5187fa747e234e1c13434f63071d1276d63dd98b77923d40d83107eca2af06c96c053be490e5e452d36b9763523dd811074eb04ee12479c383ff1980c3d173df9628e38888889037ec9a59da2cac3c7655868f1f2ad8e186342ed76b4d8a21d26a83796256eca56dbf9fb0a1e0e91f4b084d155e5a43ebe63994665e8c49123598b1266474b298a42447831d6928b7c3c512acc025682c60071a6ecb4ea25645b75dce19ac9a0fcde31fa24dd20c5525317c6a0dba9ac61d6520b5435f55f8d693090044d841865c34f796c7c534730661c718d6ce97dfc3b5f8598e90fda10121ec10c32a57d525ad468fd4d3d813ec080327e7baac26d77a98ef08f151c60e30d47999a4bcb34b6dd9f185452fd3899ac79e8a777881df2c7a174f8e793a1464d1f0c08ffdb1a30b26dfbd249ddd4444dfc105a724ab986c136232dbb18553c24fb652ca1ed3dea1852b496e8ee5370b8d64a7f9c80b6e42c90e2c582e63314487765c818e5a1ed766bc65c21d56703a5ff95712bbc5bf1d5530e8a6778ecba1b4a467b0830ade7b95773c8f16e514540d255f925bc35fc5a5f0c97325b29f49ba7247149c9efb8a1dccdeda6407144c25dd754a7b69d37a4e06901d4fb03f37c7d64fbea8317e461a2b40638713f8bff5143aa927f033d258c18e26983c7fb353bafd159d0a3b98b05ebc5678e6e61c63762ca1b039fd6a73b6a7cf0862c609d208b24309f78efc8fc5a49b2d33490b2cb023095e92ab3589b213bae90e246ca26a93494f3cbebce308c91b3b255b97ec30421276f92d5a9d101a32c90ea6b0a3085efb64fae99b68f60b7610011f4b8266c89aa99c0f83578b9339797408a3badeb8df74ff126fff701f1d50828e607c9727fc4287accddd82348600c0093a80f14d6fb81fcdfca2dbecd6c12d6cc6a9c3179918767e37c59839e6ac40472f12b263ca5af92a4af93a7871924e969593261dbbf8a4532a0bd1396bc2051dbae0b6a45cab984c1ae96041472eba3c9fef3f59b15127830e5c24f961cdaab279d6948e5b346d266f0e51d19f3d5be0f99f242ba3f8c9a18e5a9851d4ba4c6c57970a2deaf8be92624575cc22414c0c55f335ebfcea9045e2e9dba7644933a555472c484bc12c5ea96f8a1916b76fca26a76d928dfc0a6fac2da746c615c7f8a87ae9ad3d96542bdeb8bc7ab63a2bd60d9aa3fb6f4cc9f62ad6e95896dc3c445f5a15e52561c2f5924ad0910aaf92a45a62ecf98e411da8309e5b7776e8a1a0e314ecb9271353123ee3673a4c717c1fc9bb93925c9623e828857134d8da787c056f49b1e88bf6656747a1b9c7641b962576a78842ff12567efa436c7d3a4291dc79a7c2540e8a244fd7a92bc89a57a6e3137c7adbab24dfde5baec313c750f9c35a4ed56fb23a3a516f36413bffa6480f278a7937c97cae1ccffb4d3493a2e9428575edec8888480f1191116020032618810752a043134a9514c49318f37fa6474c4726ae123b66b7d9b4c73a30a1e312a624a8a574b3c15f333a2c816f49d2e7b5c89b70a2a31294f06fbb53b72b633c131d9330c9c9bef3e5a54b13d32109a74bf2528dad8e48782725d9f237e76db971a003126be670d164b99b8c29113a1e81e5556f86fd205bee107438c213344c920d3d6791af8f6c0f336ed003083a1a614ebae895c7f41f041d8cb84a432539e96b4962f6e858843973260ba2194a9a5a11cb74a49b48bb021d8928af99584936afa89532b28013e84004fa3f166161a36b8cea38842796498c27252925d1dd800e43903737996f357cdd2f44f256e2a5ac91bb9527c41e4d4c4ad3ddebbe07f1a9e6cad01b41fc627db28e895b9d74206a8d12b44f364ffc181088db27b92dd426f6fd8395c4774f3935ffa6d10fc7cc2c553d6124e5eb431ad115aa64fdfca9e103a5964d9227eadf3c7be87334fdfecbc192ab07ac3c6eb214aff4340f5e7fee079337ce8778b0b35b422efc2e6de50ed68692aaf2adfa63ca0ed6c698f12a9a7013d6a1f0df697ab3e7f44ae840abc7d84afa75f1994339192b9aa19f7f241d7230e63c512565c5eceb75c4819cc91c4af389a9579e40071cf27bd9122a5e9a556f444444c71be88ee94a7be6110ce87003e39599fc2e8ec7bd75b4414d82acd49767a402630042071b2eeb923cfa3b7649a98e351883764e5973f0e85c51839ac433f396cea8b774a421dfaca29e4c9474a0414dde96abbffec27b1d677864e64edaa0ae1fcca3c30c97aedfc5f792ec245b47194eb77a72ee6e62c2a7830cdac929af630c6619b10b49af942dd1218636c9f5f3b69a3f7cad230c697091ab8cd19a8beb0043429f601b4b4c270693747cc10d376533e5eaf04262ae98637fcf021d5dc035dd86cdc77ba9ac830bc4a7d8e8152fa7ceead84226fce76d4f256dffae05cb9218772126875d5247160c173475c7977460a16cc25485fdee0a73e9b84239dd1d2a8966f244a4c30a6592da53b6ce967fb33aaaf064091d6a995e6c261d5438a653cbde72720acbcbd03185ae37bb5cba24558e94031d5228d2efc4dcf1672ffe3cd01185c4ba0c61af172894a4dff14f926d9a489fb0955882772ad9528ada0945995729f1ef928e3c2222f2e3878f1cfc40471316eb8da2a75a67313f4407134849fbe05942eb5552c81270d1d5e029cd3f99b8124a4f4b7eb2e55f37b12494a65d6d6fde49a32544071292a4d87789f88f1ea3e30888862eb12f53bef82a880e23ecf7a1ce82ee49f5c98888880f1d4530860f6e2932bd695b3a88b04bdfc9a55299de9b32720c83aecfef9b4ad21d9784d126ff107e2974889f2318596de557787eca2ede2007308cb8147b71454a437e835fb8d12f49c1d71de91cbec883af45e93157f97b44444488192322229ea317677bd5e5947d54531d1111d11cbc48bc94734f2c29e94a9d63175ac7f0b88c6d6d63e9e209b14e27a78fe9d7930b652aed55b0984c4e212ede8d66a767a99f53ddc275b59192fd2fe5b485dad91a35739fa3169ec5247f96202dcead4839c1636a27db083966714a9f5b952f4a3cf932841cb2482cc624f335fbe4cc118b4fb3d62dba49924f9d0316b4f6bcb96be802395e91893e279ac9ad2f33e77045a13177ad40c7c463ad7b3e39fc20c8c10a4a5adf10f15352a5ae8212c5c43d9ffc2a19a38a4d12d27be2c7d37bb602395251cd45934bf20915e77b96247b2c4f51facdf856398d78ce8c888898338525bf7b6e1a493b61721ff810c2023382780c80985102119112e42885292bbb9bf4492a0729febca1e2e3cbf295f828ccf429e9c43c9b47ad203e7282acab20c8101588888882137c33e69366fcf259460f20fee30c334e289c0f9d82d5dd897be7057280c2b38dccd12da6d8b15220c72758718f1a3a59a624d79e482daee6f075a1237b03710be4e8845b413fa7cc539b044d8f0de4e0c449d75366db9996fc01e23f7a6020c726d01c34a73359d4494a46e5d0847a97297c8ea9aacb44f6f596bf6251e55223986846ef55ac2fcf461bd920371011d921db3d2ef15d0a259ea2c7ae243c440819490e4b74a93bbf5bb1fa6db34a184ff8ffb9ec12eb6b4309d635ac9dc9272ef62993285dd4b3b390cc5e26e53fce40634806724882dbafbe8d0abfa39690489463b23c39b7632b854644444a8f1e374083bd2191989c4e34f1a4d4f9a48c88888c91e311a4ef989882054f6af21ac8e108cdaf4c30b184aa10ef888888101191202547234a3929ba329a26a16184a697c418c9b039d98f888804d99133811c8bb0dc3a8652d97c5a760e4590daf91d69192ec53b92885e5b3aa754ccdd2b4b200722eaf8d2b16b32a5f48f0d7203df334444ca086246072cc72114f5ca154fb212347a0c29e31f90c310658fe525cbfe981feb610e8d4260627759efaca38578040484287977e8e5ca2e9e1d042b1f2c2be5e819fdba8f17115181480e41f4269fa9fd9f94298e3d828c9c320281e9a53ff1cceb3d8188889020ae821c80f834fe058b72396383f000081a7ff04365d199cfdb1d06c8d8400e3f24c8979fe05973dc2873f4a19ecee4a369a4fbda11ccc18763ac4d8efdd375a5eee1b0ecf999e47ea9f4107239f4b08d5ca893cf548b4a8e3c24586bc8d2d047f0405d58868e1e7521a711111107e4b803bfb1d16d2bbe5ed2ecb0069ded9c27b425c2327c88889481069012e4a8836b31bf92e7e78c499c0e8b84497fae95a4b129c71cc80d76c936a597ab8f0f207b02325290430e97a8d3275e6e994c1a08e48883ba26516a724acdce11c801874c3bd879daf6933bfe811c6f50b2436d96e95f6634871b8e7917333f3f2222d26900c18088481a4036c9d186345cf6b9ace570fa8f88882820071b72ac214146a3bc05fbdeb4070d0211112121c8a186e583542c2971472f1f0912c487f8d0808848bb8f33cc58801939d2505a0e35911335367472a061bb7aab9bed98c7cc111191213e7200c44790112114c871865a343bc247400e3350fb12b6bec173f6b10c658d397ca790c1fde80e62ea3976e58ce1897fcf12cf505bb5c921063ee57787eafc2729382222d26684812c79935829d5f78649b71960284a6cf56aca173e6596e30b8ec409ab613188a6c9487b901d2939bc50ba0b675259e7da98638e2ed0122706ed14fa376a3c30e3c78f1da91c5c6052fe3b78febca7d58c32cc0872c6e5d8822272a96fa23432460e2d24ca46c6ca49ce480f33864420471630e953ba4ceead90cc488fe363c80e1102811c58b8c46c95cad241e20c8888508e2b7ca2279857484bf2ab233f725861db8aca31bcaade25f7810f2120c85185d3964ba56e0b213a15f01837b4c48d12fd9e427bf294bce3f9c275a4903029d7f1d36492e5287c265e5ff265c8761f0a7ee797e4192e5694ff84aa3fa58cb1b4d682ef84523a295b749224dd926f02b5696fd67298a6926782eb06c86860c125098fb194573cf2276add892b0ae5763d5ec2ad48f81cbd2b6e9a5262567072c79c6bfa55acaf169bc7ab53bc558195e578613c6dbc3b157a4876af6ce8bb0f2a78d1cf797982a8784ec1068d37dd31056e9af93a255f5a4b615f1e4de164354b2d294c397c495311be9a1c855e11af3195bc665014f4a592a762688c790b85e7563946fd646d624081670dbebabb19af4fd866e14e9394f5c4baba1f6126db097ec5da92981e7ba1e584dabad9eaee23f64d24a5789ad42a4dacb2bdc124590b9dcf447e39f527fa98f8ba82965cd1cd4dbf44df3164deea24af7f2c410555cf2958fb578e5602197d0d4daf9745564a98135e52d0a895724e2791e017a3a5ae44fba692e82fa693928a46e24a929d2c7fe7fa8a42c2cf32f14c8c99842ad147f83ef5e1c47f97f338a20a3d999abfeae46c23ccd0e4164b8a995c658419e357916e75952e4235c1a4cae15404abed152f59d7e66022be2a3361b3467dd91071189b93deb5431c6ed96973e69fc50c71e7c88cfaef941c538824d9f267ca689fd79284482fe4b64d7e2e4fe520be4b7d1b63f9fb5a2988433e953cd1faa9aa0c8466fbe17372539c4902e21c3e68ee31c93f98d366bf37bf9459927e60bae634fe4d0c97b20f69f2558fb29e753b1fbedf94d9a45b62caf79065b7cfc1654e8a1d3d5cd26bce54e26bd59a07eeb2fab5e495fc493c6ca29b333e79872b4757fff62de4b403675739437950f7b00e4ac8269df5746e1d1d9cbd32134b32a9a46e0e7fca98a56de440a5a7abfc1716f638a0c19345644f6b07077f4a3de4058d49a8bc01cf5abb29c665dd4237341267e14dbe0d499b262d19678362c1841e13ad98d36bd0e4601bdfde6258590daa79869267a366d5342c6752673cf5d06054dab967a9674def0c5ea5cf9d2daac64e9e19b69824937c93338d796548d6aeae3585cb793c326c9298d377fdfda4f1c6b0a579eb4edb57064f0c5f94d49c2e5de87b1706b336beb5c40e0cb6858c998d26cd96fb022687b58995367ca69c173217dda84ead4b52d785634c552594d8d8858e0bffc7efb1f1d49ff62decbd91f96149f6752d58f91f4f34f49bf4b12c98339428f51f7368d8b0708573937d4f0c69d7ae4079bea0d5ffad256a56286f496b162bceee6655e8e35b7a2e95b56d4685533a3605d3648dbbd6fb9d149342e2faff4b4568899345a1fb6cd9f9d146ec8782bfc9f793a72beffc13dcb86b71729b727627681dc7cad64344f62658a2b55afc4657ec4c68d64d37df9878b15e823d155d39a9848493d279a479923b4dc2fba7f1d226399b1409a592e266ff23f0d5926e97327e491aa19857bdc3440314e1dc759a4e4d4a239a018870697cab482975b70c63d1dca02946364f4518262b0dd1f379192d138c273a098c37a673746adc9594f94579c3eae54f69e37b5f1465935495f65e24e6fafa6e31e94b9c179c5592c22f899999de051d5bc563dc253139baa0730ab59c63d39b9c8b3a5fa60bb5d1c45e71b16fc86c9dd65bbcd9e7e1e4bc3763da02b18f136b5d62895d2dfe3cd924c9b7428b5c67f6cd720c993665164cc99a233a7dca1e5364a1646b68682b13a55262815fcca13a961458786e72bcf5d77d969457bce157b34c867f0e1557509536e4a66492b07e2b4c8d9b934d86d0a4b382729199f2d82cab575189a1b934846613635524c60cbda1cd42c635157d12ce4db2767d5751617f96bc0d6a41da3cc535ad9da25d6da99942cf97d349e1196e2ea5e8375c8464fbee5648b189afc142c9e249aa519867ce3565a2604f7ecdf9336da524a1a0c7734d3ad13fa9041459a5da7d699674527dc2dc7a995258d633a1f244a255e994f79d30b45eeef07b3b31cf89cfec6d66f24df471ebf3a352f7a3897b362521349d09df3f07134a7ebedb4a294ae34b9463a6d37db7b7762d61d656c9345952092b6336a144f661e7c44e615127c924ca594476a3af9b9c249104a7977f669f1397248984a627a78fba25e5db21d16c8cb996ade6657f44efdbed39590ae5ba239cf3d47fe96ec45effc92c57491d1d466ce9f9c6826a4a1e2f628dc1565ddeaf265644aaa91a19c6e325d9449472936c559356ba2222d1625ad0704b6ba287d83aad3596a4214e9e312761d348e50b71dbbe8915c3f63f24c4eeaea93f4cf4dd83c8c37570ef685e5d09e2a8919f4c12cf4322122310a7493994d89d78d3a518803874e62426c14e2e0d1f0762fc414fdbd79f2c539a588ae10736ddcfbca849b12fd3074f334f4d92f7b7fc440c3efc1d1232ef62d11917630f4b5c8aabd271535c0c3dd8137e351b7b52fc744444240f7efd7fb547e578c8e0c1b34d492aad24794d0bb9432668c78fd9a44c4912b343a659deb165e6dba63abc9fad8227d9d2801874489239d9d399cdb0760e6e77fcb4efb14a799403268bc9d2d964132fc5e240bee6db7c3925fb8ee0408af4cfd4efe6fdca1b56fd9ebbcbe11f4edf80186e20b4c3f5c58eb4ff6c1b1cb17872da5131d850f694542d6e8719d780aba9ac988692fefc2168a801354d5aa6b9648fe2488f34e4a21e639f98194183186728cf44d69a9c36c39f73e99414334f30b11f6794018234322044c89e60054284ec196294814e713916e3971d928fff1680a0031de8c10331c8e0a6ec911bdfb6c4ee801863a84b9849dbac16c5f06f4a5ad9d35fdde7464444ce2a0ca65613ef120c659861c600c4f84203f1c1032068ec10c30ba7df144ca2f5e27a427e788f2066a0185d28daa52635474f675dc88a8888c1053aac27b953c8764962437ea021c453f0e3870f11888898e10307626c213929059f333b0dc96881f697ce3d99ac64cd1192053c346d77fa5db38fb170bed097c14d8ac8cc5e41b99d10afcab9db99056258e18f26e5ce5e0ae1d1a40a7f495bb84977425eae0231a8507cbf75ccf73ad91306624cc13449b24cee570ac490c232a5fdd1627fca981e1111398118515846df5755ba4ece1d28ec579e32e33a22225202319ed09875bfd749d125762322226238a1d4d4fc133d670f51204613ccb1927cf18f400c265c925d749e0d09f303622cc1bfb9d20c15cc80184a502c8ca52c212ec30362240139c9738c59f9e78018485853ca9353ca21dbc20704621ce113a3da450d161111213d80041181184648b44df9337cdb4ab2c8408c221ca6b77943e66c2e8d1844f053f894aa256584c730c8f8f797525f6118dc42ead80926491a0b065179b5b1dd041886477eb0bbd4f27a79fca20aed41336ecac317c65c8cf165a6299689011ebd78c7e62c577e4d9ed320133081444306c683175aa524adb5d7276a6717a524cf78d078c209939e71c6fe80872ef23c256ff618fa83bab9387d25b1ad3c279345c2457e3176aacc07518b6ed1ae896165b2859e62b9e5e1e7ab36ed3dfc0c338e900cf0a8c531bdbe4c0e9ae671fee0418b72dd2a6b667c44360b74939c4a8afcf550b70a1921e0218bbbdd733dd56d364c38e0118b53a61fad12b1589f61919ca9d1515392b63c7a45969d96824f4da9ee4078b882cc1d0be719d6d16239e0d18a523c337f933fc737c58315e6f28e2926b35771ce14dcf4acc243159e249d98354995e8faa722b9939f70a2a7c8bd242a4c5e21fadf9298a4db9cc224f88e9e9476989498e27082859d6f548ae7a7663c093d29ccd0dd78bd73795719859dda62c7b43029899b2166fc5041193c4471f4602b1f1e85c2f0d7efa1841293249880620d8b0c9bbff2893fdf8c8a274b72a7db1367718f9dafe37ebcc7a3135aa7ee18bd3f9ca0ed2fbd329ae7b4ca26fcd9cdac165b2d42d304b3dae993709d39c52e13fdc7ca26f978a8f41b266afbb4fed2cc147d018011785ce23051bb616942547421f0b004f69792e00e7cb4087854c29d532d694d793e2694307c92bf9417d929eb4924fa650cbdf2e9f38425918897a6984c427d4c8098e123043c2271cc270fae6d6249e220e001094355d589a97f84c9bc4bf0184f48dde88c2d63033c1c4199dc2b8f4698536145638c8ce03287c7cb175d4461b59afb3be8b4953c1491f4a94e924c4c51fae48c3486a4e0023c127127a14326a94b4a92c488603ebb94983a87e8f2105b4c1ac6e2a5d0e93f1e8630c793c3eace39222222e4878f2168248f4238eb415bee72ceee4d10337a0009e2438068800721be925a364f3a87f800e243c8b200081a3d4444caf032068165ce25761eef4ddabf6fc0cf486305cb431026ab6b13735da75c07c2123f2dd7939b185a407c7d2bd74976444464480f1cc809c818e2e3c7192a0832a4063cfe906478eb2531ed87c2a8b5658949bc8cb30ff97dba4de92b1e7c404b8e277517736e9cc71ebee89c24f55c0f9f5e30fd2a0f9f4294074b2fa538a0017840dc3cdd62adb6427b07f3b77bb4942ffac3c60e4b05ab4fa2e475602d06f38c954cb9ac15f0a0c37a1225d895fc6f26c4630e85bebc6163b4da53cf0f33cc6016f090c3d25e576979d6fc7f3248c0230eb9d8c6cee9bb63c5118d1f1ee48ce30187739c13337644e6b6e2f106fbbadd5de62e95a92343f60422222343f60c119134868c80871b3ef51c2a257dcfa3191e6de0b624d9bbcef16083e1962f9e244db24997352c5e51e4dcae15f050c3b777e267c5acd9a44f439329fe47955877b7a2e1cf6bd5e2fa194a52b7588a999afda219c8bc143d4965e1b796321cbffea253d222bbe14186def534d35553c85f3cc690a68f26b45954fddd27c0430c57679d504b7f62cc3f0c67faf794db3d621e18126de4a44a35fbccf0f8c210216804e9d1e3003cbc6049524a3132b95ede982e546247694d577181cb7632975797721e796ca1cc95e6299a46afc95a402f89a671e2a3fd9e0534c97fad498c4b1b331e5868a7cdd2cf25b13fe6063caea0688c9accf246e9ed38c0c30a5dd59688aa011e5528860a1aa357125d43d303ff4010e7811923283ca8603a41f6f35ca749a54ee114d6524733f915bbf4101181010f2924e6f6ff349782808c6e1df088c2b5edfd267da46e4cf380021963ae635e134f2c9b041e4f305eac9fb7f8f84d1525f07082a37f52fc143dcab4c7a3099e4baf6f7e0a0f269c3ec7bafc7f4977353c96b0c9f95387b09f8712e893756ecb3c4709f24882b3711ee6f340c21fe3e429fb1c1e4730c747264ef43dddc6c308949c2b6e7b4eafa97d1e4548d6d2382e61e17a331e4438fbcfcdddc79d4f9d61a4a55e6ed2091bdf83c2d8243a659750f22dee0846965534c6a514189497e469b2bd4699f42f4c2d7d337d65fa6a7e61872f988acaf79e241363da56d8d10beaa3c3583ced4b993ac20e5e24c555e4cdc6f88a294dd8b18b36d24ed3a8d57acaea829c91b314323c49c2c98e5cf895c7d385ceb9c1eb1db878b5e459eff01a35c95b3c7792eec62cefb0c53177fc7ca627b530c854b43ff9a4425d5a54727277188ff92e6b165e8598894d91451f4e775fac8cc52968c813472c37091958d09d62b00db193e48e9760c72b521f2f31597f446a78821dae48b213a3849dbcf60aef6885b92d97e5e4b0dc7ab28315a534e1937b27cf3ce22a8e9daf4ad2ffe6ef0c1a670001010e76048ec0a8e4aea2e1a1a1402410884461201018b23b2d0163130820384c248d0543b170a64ceb031400004c2c1e4c3c22242a16121c140e0783c17028140c8602814020140a8783a1706b1c6689de1a7b6fe0a978b81dddcec048ecede675b908cb654a248e814824d07e23252a4f03ba357858320eea88b4b85ba1522699f4820015c8d362ed265f305e3d800ec47d61514e34c3ce14878e6c63b12db962a47223049309bafc64baf382f05ace6311e8d8edb5c58b1cbf322e4f7ffa7dada483e759771f63e46a0bd328208bf9d1fade204967fb32c4c44bb8bba538a49ab465c8e22f49e3f79da15e33a644180e8de8e1280782fcaf73c299ba4174ab2a1fa7ddb09cb232696843d53b4a8c633aeae5a782c151bb5dc7141afda5cc2474fda3c738a6d120af87e4d2c4ccca6de73ae9a3a4e806e5b529194b9bab82a80155fa7ab38fd0974d403aee3267a98259a638cd4cc48931156e10b4c7a6bda7d4c1bf904d6741410272852aca823326277de7bfde3a9d5f73ae941358bd228e15799915482eaeec6aa6413b696d92dc36efca90d876b1b9255d29ee8d1bd6f24e5fef722742383bb407f35d7ce480823f1d93beb68045cb35c60577c5b63a075cf7e4f9d98bf214953499d0e75e7ea6c90ed73d060a90363e8a1067321ad19aecaf77727620b0ac8f25b9350668d6c9a191eb0470b9474a92ec9f52ee928c80fc4f24ff8ea7c4ef510e19c74f1c2171eebdb9b0d660d289acab0f0be4d2662998a47bf9abde14f538c72eb35a8f01caf7748cc202cef36159e366092600f1de60578dde181a38d25c8ec8daa19210b02c2a228418783e8f1561759278bca7813f67c78d8c18ffdef60b2c804e72c88aeab4907eb730286afa428d54f70ed075f1d11faf168538e5c58cf9f7233a233a30643cfe52ab53752b172e5b294e4e61d5b8a335053042c24db70dfe44915ef32e39fd42a984f1f74a3611ca00ff8535c16e0ed85c4d42805ca77fe944871558fcdcc8061e8675e385e4b3988669825a5b79142d727f3fd3a805003c95b4653d65502253d0a1cf50e953bba9213772b91d21eb62e3918f8f8d1db419302f7d1b895e0fa920ed9d3e1c85097a4fb1af330b74edba7637d9fb07f2ae060bf7de1185ccfd74c86199559af259944ca7942be49b588d992d5b2c3cea6e814f5523a6ab341ffd4756213262cf16fb4473acaebe3b76a1ae79e2224bc2a5f15f9f686eccb0b752db89200d05cf778906e9b814ff59dbde0d7724d6389a4d27bf53e582390b370e132ab556f5bcce5024f5f832513513b5da046b1f08503b9707769e8283ae38bfce18b549f53a6d445b33c9fd549d57b3486e0c6f9104a353502a9d2b438268dc9fe8945ba83980ce9ef7213ee6af616a571cfcc9955cce5ca7688c319dd4f1495992ef85a3f8bb627a403185c75c9571e6c03f269288727244547980785fad0f71bfd4a9b93aad62467ca10b5d21f790e2166991120fda368b5770f7bbbda3e6b6964441b55fd13919a3397f75888046b4fb0012c2c0c6bf0bb7c1c1f66957c9821a39ce8fb65f9f73d66289015e504262ae7ab8b292a91cb235330e74bf0b5f2450f605452e98193cf8fc5da1e8b78bca8f0ebdba6855d3992a7a730a4fb53ed79de43e54d873121f9d3ce006af5fa342483939eff99e091a0b4121056bd802f181f0006783a3ee5c281f719588cae6eb74509d7e92755cbcc7c9f663c5133a973dbbd1c4ba07b63a2cae4268db872f4f31e29978a20a070b279602982967292f5b6c0f1534af484ff20fff25c0570b7f1809487a3928db2a8f923ac343b6be0dc15bd74019a01e345e23678d02f789b919c85dd4c0c30e192292d1db6b103abc5415bb3c17129d84b49dfdaf453381a1b371b1747bcdb29cfe392cf44ec77daca14349f05fad18840b00087d4aa146b3855fcb5feae4a7b1122e51c786730303f6355c49f6cc6710400b1b8e4ef7640060e85ba212e799580458848b1dd3e0243cfd78957c7def284be15c8aa4ef13a2a1e1b27ef6ac9fef5bc0f34d278572fa9e7981361fcaa23ef7c9f81623a4b1a9ae7d5d18f8ada59010cb80ce4d0dc316f8634bfcfbd6e58270e3be553f7b4432725eb4d1cae7398b1278a09f5bc62609a042c1f9e6d8e7a8bb11a491113c776f812415d936fc065798a6c1ee0e02f6c8f1802824b0be527eb4a8be29b02e31e2782fb8b7844715ae77e234346bfc54ff8c5eb8cf6e01392d38889c80f965b0f010e6601e1eec0be0cae0efccb3cf035be1dacf7ce84abc7508fd204055575ed6a423cd791dbe0fba1a44b8fb89bdf8c2a7340ed69d883a76efb965aae8af3c2f99ece431f1164f793748eaf5d785130f35ce1a07f0f6df539214c6070c3386df2a635b4dad23f800157316fed9754ff12050fdabf165109bbdc459c18e1260a170682691b92924debb745af349ee22f5d68581144ce1958e3b4b79aeeecbbb94a23dae535d63191a2fd6954c57fe8e903a9a5d3deced0b6b2b7cb94a644cf974fff68eed5b620c6f2d80de6b5eea9dae2328522b6cba850d4d106cd2c0f2abbe6077a69fef98fee447a7e43cde5afcd3ce9f9587063d0a8af9515e7b43a50ddf1c09a8a1a3d6a5d8f466ee663f55b2aad1389dcefd2699c9689eab70d2e270c994fb768df1e377c1fbf2e9da45fccef7382e08a4bc2b604f9fe22f586a910a9693eb789377e4ef5071f3d8e1d4e22763af6b16ddcfa5d43935c3ff3374ffa05d9ac610390344c1e00f5b534aa08c62836c7a74c493ccf08b867c3731c7dba0c4c4e4a2c712df443b2f00e13cd0bf491b79e97e12dddd33abaa7aac536703d33635f04752ab35c3286ec8aeb9277936b159dd35d584335d073f83a92656595af7d7ee1a1a2e551857d637b7ea3a5af2458a8eb97adf577c95d43aa57db659d06a4996fad187e779c5f1d388ad922946fcfac86c96150305dd9458014e00116efc10de669eb44232b3b71bc6c9e8d0db160ec2495a2331acbd71f14bbc7cb556a7e4e1667e04398375ce325d011d76780b8300dfd8f310188651da85c0deb8dcf53371ad72d114610181a6f969d7a5dad8681369d59cb0a7564e5ddef1b2c9746a23fff6f84a498db2ca19fbcb3799fe46465ca54245f46db86bf87ea038c249cc39cf30681199d9b2ef53e74b581a098b712c59b46e35e80b4b58823e777c2b47a5a74c2d9a94c865d2655c9a548dcf4113a3dc567b21b802ffe2c27d43cae34b171653d579fb1581dbc6730ea6f302819e259f3a93bcd93a6f0ab8db763dc468d3d64b9a5fcb7486f6cadc25a6269d6a6c247c0bdb90e70b744edc6200249553418197c331ee2e784a5a0d57be4cae5c30f38bec3b7b0a4a697cd90d347dfee1a2ce5216aae72e1761be0d0d29d48f5abf9757a0ca9d047dc7002dbf1f220e01cc07c8e1531a80877d778d7c5ef1a6134a4133071190a8bb0e95340afe0407b15446515649dd2cc59b6fdbfc2e30d0d865c48d82c05af8bdedea5df818541750dc26221a76ad28ce1e16797e2fd5f92bea49095fa98121dff2bd68fc8676ec82a9a6fb5fbffd4da7da1fa29213631bfb429779722bfbdc0b2a5932347242e2eb9de18f9bb1196856364a8ca9d33a658819d9273acc31d22ebd5bcf84a6863d70065c27dc1b9414dd83f0e881ab3fa6a40c34828c058fbaf6f49417eb14ae932efe5f323b9971fd570145e158cba2f67df981e71eef0f19d42929d7242829acd8af0b402d157d9479acfa7f552a0a3d7e05f4ba8eb8feb9e56c5b6d6a7e1f584dab117a88f9215af290e1c4b28a3cc9f2f966020db8ace5935bd74158cf21c01816f5d86b758fc1e874c511348f26949afa03e836edfeece028ff1bf9d9c24bbc7a2950a4a3a730e8e7b6ad70c58a74628f169edd79fc8e14880ac7760a96b8775cf63ae6691b9935a2173e8c94dd5395413e9126b36af4a50bed5226ece42562b8017701d061d8f7391c92d146f45bcbd1ee2140fe5f1e8d50afeb64894f61ec41ecbb3b7234e78ac37c8f842be8c62fa2619cc1e9f60f28d847234a14ba3c5ff5bc7eb6dabb75eeed04032eaad91273f9666e68ebd8e097995c80a242bdb39199e794a170bda3de15949c62f3b8fb8e19a775b9f2ef00dc4a379caff9411f20502ee6f627ab906712fa68bc291596d2aa2f7a21b16b0eb345a2787861c39d01943b7e36e6f797e9407e27234eab73d97795c8e009f826015ae87c2342b5f46f00e50f3175b97f9986bcc5a8b33a078f8726cb4e4b813680d60c5b4c32a8494f0a666ea9ed6fba141c07b128f6360ae92b428432af2a45acdd1b8a238ce8747a25462d5867fbaceea2b279148d703e579c92bd147aacd401fd0fea85701ea07cc5592cee26f38e5f55fad8d9893aec529370bfa3d4ee7b4647231d083ac304c9afe89300c03b0046374966ba7b5a9f5fc5ed36c1092cefb1c3c5a5c359205bf5ee81f12382f3529d4125fcea3d747ab7313e7f27b8c90a7087cca56075aed158692516d2df800a2d017b69e45673a3de05f17b66047058fa7c48de5ca06d43f01492b55b4170c763d15c564d20652bc639d0c244eb7b180d1038b3f578e7ac6582ccff49924f8c55fd6612428afedc6f7496b0bbf51bbd9b757e60a26af64b7905b9f40ec5fc2ded64096b6ec7d8c9a38f27569522e005adff90591a2793d41bae2da328c759ae0fd7df413c8c922ac984725542c6ca5d4f6e33a747c284c51f1e4a22dca14450eb310e44683342c1942e777d921e248168ba4bea789764bebe880362d96498bdf90e86c4e50be01be993b02de761627594aea9a2002afcb443b2e18bb7f9bb5e4caa2740da9c1cc0ec34ccda900bea918a15d42302a4916e80d99095005856a5545b94a68305e89d4c051e507bf54aaa7d00b5feca659f07e276991a57119f261a8ea632cc3191831bacb3e3082c3ddcde2fe02034cc5055747aca9918545f479668401329e03ff7a643c0c3284cfc927e698a3176e1ca487964e2019a45126aa6bca7690378f5d027ca2933225718861d6718a870a010a0d098aba72e5008a0a117743cefd89859b0e004604111a4eb393eed0f89b26108040f874b2932e7b37fbca0da04f6d3c2d99729c89afbd08944fae544a43f40d4375563185a615b145250ab92f58c78371d8204bafd279958c41b1dc5d48d68a698375a245555ae165c4b2744e9d303da539ed1f171ec18e79252caf65e5187922169510bacf12dfe8f66cce45c5db70992d5c55aff32765b0eea215c3f0e9e582511d62aa12ee2f155e5977561bc442d833b2b82fd7c3ec5ec1e18fcd3483840710a541f8e48314d82363c74cbedc2061f73095d0b540c0bd39e96a449b6e196ad2985962ef339010a28aa683f88b312632cd5963de66a4b1dafecb682dcc015851d6f4fb996c5a5bc9b1f6d45caf3f2758ebaf12d2ef48ed9b1237ec239eec2606ef173e6e7720b017b66aa98268483392b0a9cd477ca766794fed1e24d7e3f341c3746f0b138975694145a4e63944d472394f98a716b55955118c3b8592a195cf0e46fb5297a556b4b4c74a2036032d882954790abc611f3a7626257e9f1272a7e935b8130b6ea1b80b632ff209181bd47442786273ad340a76826e2594f78a6f3408e888032697d700dedce92f9d4472fc1769b4acfd966f53044dcbc67253b3820c862cae8e1155e4f99bba7d075b49836b1f0ba55039a06c6df5c6e58b8800326c34cb6ccc0df1fd89b8513a461b0217d016ab9bd49c98383ec03fbdae31112f0ed1e79253a1876cde5f4c2c7efcaaa1aeb8e7606411504b3db0a917ea960998fe2bf517552ad4a1544d596ae68b66c570b471fa6c0ed91c2de394bfe185d991303ec96e9b593a4b0da8443164131f60c0cc2e813d81188e1c417362b6906a775a5d5caf6237ae37d9b9af1eb60a43395a41152ea413bca48f563e12f213110c527c780d3f27890aced8a041425fc262e1c1f87cdf508ab7105d685e3afbcb8637a9b6bf2fc1306ccde571f801f8014bcab18567fe5e997289f42d3bc69fa9541a1c8e8ace9c7838dfa52437b4d868d0421af42bb3406d8704231eb8642e62e9c2c60763de67481298320be94ea886300b4bdc4be40120978e975fe4ba993ffb12931efe4166f0f14ada90610068f944b8e784810bbe56b624821244f644587168791f678b91039ce9383ca44fe4b5386511afa314096d6cbd8ff8ca6331f83df37995234f87288d404111cb5f02bf1b2a18014062bfe8fe9afe957862e5a91d274e3300f8c6695519ff758fb6fc1268c7894489366ba5237cd3731ed8dd1a905f4d1e872ed5e156cb7951307f1b9adf802e168de331eb4a9573e850f675cf8824394e85ebfe36fe0869d5147cc70cdab0fcd4a0d571b2c2257f501430eddbacc01859524369870a6c6988e768b2ee1753cdc820fa713f2db36589a2e6694ebb3c87bdda1becaabb9f6556e1039ebd0b6d3e273faac7f897a983699f42cb47ea985d0ea115522526aea15f5b29bbbea6eff7fb06346ab6a8ac5a963072f50e143743b14e86fb7ffb5415240675a41ef60c0e47e6123715cefea64f50323048a01ac740e3ed11d73e2dc3f429d1456104912a10013926925f34efce6beb59f91969cc65597bcae998a93d733dfbcf9dd9e1ef2da2530038adebbdf08e70b26b5e6fa707c73b4e627a9c9362aba8328db6ae600ac88d7aedf57dd2b1873c1d47e753f1d5ed803d728e799d93d36620a1fe2a1c83b147b31738e14c47e1a7b86a321cf09371e46b32d3ef1b78ebc962d8540781f2f5ddd46f6866aa4e4f6bb1890544decbeff607715142da32ea577d7441aae853902286bedea42071d2edec2ff72744261e2fb247663175fce0547e7ae89b404321aa95ad0e17121556715f8309aab344a0a5769a6c4eb12dbd2b10ca2d6141ff97c955a66e917b82a208b008d19f33cc7c53e3a404127d81ec30e1f5ed5ab58d43ee05aabbe0af6a6c7696369a70df37679cd010e820e2f14429c442412a0259451dad1d9864cf67c2449eda5c6321e59a3485dc52008f8fa8d9d21a3ea678c74fc962763c7536121f647dd07526ba6a1a542fcc62482a864a52d8fccf1e8378a9303869f12e4ee624e123a873367a2924505431ed5226aaa59b6aaf721a95a84456d85f7856f0e7996de210baa368d71a6e8fe9294489ca95d9da2e710b20cd053825999cc27d6dc93ae8c812b8e66ec40ab50beb09393d1abae4c8105da6658e47ed0c55d5ac41247115daed6baa2e143ba64b2baa1394cbca2286308b17a61e874f5747937565493e5dad227120dc02d2db260ca95a6a4208aff614175f1338e4d43e9d295696369d2933d3782700c38f9dd7437458913a07dadc9c42cda571e3a28fc715fa32729038bd6a57dbeae6ca931b61d4a68895a22edfdfd8faa8544d0747467d77b134ceb884631e32c348ec50b4d94cdc41742a67883441e9378474150d4c50248cb0c4b3923d376f4be1e7a3077179044152a8708e3a9b6e911e3ad2ec811e2ae3e6891e2ab7e106162fff4f78f9d2079d6564262fbe259cab3f9c3544a64f8c55bb26439c55baa44f1eef50dd4e9c148574961dc3d5e985f8eb4066801ddaca97883f7d7d63f2421d9d53502c3b2840b9c9dc4d02babb4f9e0c792da37c79edc6007622427019804c96d19cf04ca65f992068d8fd6644397bee24c439434b19724f415026f03594d921fa2d2b11cb9850eadd2042e50cb8e5a6f8e688ad194c7d17da47183b0f15174b9b695496439b696593479d66ebfb07bf3e4e1b42ee78e56cd662de20f3d0d393567a7751a02886a689c56a5dcd66466f5bc0f104335f154915922a20fb5d7f019a62c4da393a42a4bb1a1932edfd63e8bc46da0c7ff0e737b8024f39300f865b6b2608aec343a2ab9fb07d1ea30b2ccef4bdb9263a617712fc432cd73bf0655b0cc1b2c05bb51bf01fd26a625f36691147bcfa25d87611971150f4850c8fa7a6d910ba64c91d4d5f98665fb1e181ceeb287ae3671f598fc97c70bd6535aeb1be367e9b02bec73218daafdc92a601704a4f092dd8675739453f08d48ff2baf92047fe6a0e49fb87bf239d951f6e60cf8a99868f17852c76399cd2ce0a26e61aceac6349d2179d8ef99be5ed6b3ee16144fc57aca3b987deb8a195aa0af1d6a83aabbfeb61ae1290ebfa9279aad7ae31d98645468922be690661c4cb89288c8856e177c9f0eb0bdc8dd9e8272f1b236d0afc1ffca9fc1cc4933fd1cb65c419d41ddb9dac0e149280173a96703b4fb6a3a85f45beec642e9c5474a46df475c093332aaf3afe7e7803bc6f5b0ee638945132d2a34b2287bdb1ce5c0dae4c6bd82231d30c95438c534fe165eb2175d1317eb2bbcfc1691ae3998c650751029d8d3a139ae85aff5622dd023224a15844f0228acab7d7aacd27cd7260a1ae1a050bb1b1aa76f38e33b48a176e1cc3212c7892b9f524424ee5baf229359badcd429ba8bccdf52b55e148bea18454679ff7a32e8fd3ba293a640efddf12933435619f54b93eb053d18c5688fac60e832c38e6f37d9234cc4732bebc8aa810143beb331140a32e2aab27283f2b8bf683c593daee8fc1e01969d2ff6e45c7a1d8c73595ab6ab646c7a04c755237e350af98dffe987ca1661e14e383ced756e4f86ac4f6bd5a97130bbb1881ff7395f94fe90871870c6cf41a11cec2a91eaa51724ad4c304e7fa2b1f8cdb67385a761060f13015502002fa01f368504ba4ea5a189c4f498003b465a41d668fbb0f14f83146ab11f7d6e65000e14c96197450c455fdadb346c80aa16a0401b25e0c292ec427d97b48cc432101da8360d1be86d90c3919bbb4e7259fa24e024832cee5c8d4911a4ca0320ef08ce0f561128d15c166ed7610ed67b5b65177bf410360f01b73db8d84ac6ccc850c976dd65eacb21cccfb999c15f21751e626a7981a9c45e3982984622ba93c1dcffc51eebbdcaa46e2574e242798e18a1d1efb93e4b7e71d6a2107eb13bd42a30ded95329e777307f369687d8cc70a66ae872806f93696a7a000b3c71af5dd5368facf13696b9efaa6f352306d075d930f4aef0518a74d7ad3c00883c8660baf6b21df3caf6ea975ba66859a0d21008d7fc2a0770410b0632d682233078123d659cb3737a72e1f31dd410fde9518a0d96840e6cdf9e936d0852ad202b2b2beeef48a24b922e2ebc86b4acd5c58040718094b052510e349ece8df682768917c08244cd8e7619ec52af6d2f8383450197f6e359e72fe99269254e6c069fcbdd73749a5d291500cd68d4322d09d24c6b37c97526faf7f6f600d7477adf9a714fa40595a0ff65098737d40871df7dfbd838033a35f27a3ec4b203899d6ff1587e378c096450d2624f3e8fcfb3a4d8f16584b4619fb6141a3704df4692b3c9fbfaea9637103f7b63de88eb543587f5f20bf7da96d41be869b0e0bb3a1e9b01fe3224c539d535b8901e64f6603ec604533860be5d4877ef1c0283596fe0d1d0724bd6cec7dc758bf1dabf63fa01091bd63de82480b7f25d5deb6faa18e6a82088eb576dd3d601da434e60bf228e0915010fa648fc365e0e0aad948f8bef597b93b22d4e79074195f0444643dbd575b71c5066758edf6b0df5cd1e75fe7c0a81fa342e14e7f6189f52f991845e386c5b625e5c193f4a746a0e93c51278887f7dae1a97f7b10584bff8b8edc9b89ee31061b0663113198e438e08e50b2717d64cac2af5003ad8f10c84327dc564d3a1c59fa7be8b2d6cbe4c7dc9e9e70a4af69e729dce1134b1211d8ab9278618876ebdba543fef8bbef7e94abdbec8722a067efb6240b17d5a2c223743c1a3c26aaf4196a7898b9d3e478a4c494ace8b0d2e61293fa46584a43fcb7cbdd263f6e0e32eb3b1e8d2bdd76ab8f78de5cc47a410a979089ef39490fe20245e1a3f61444e1e836423156beba2450275f0390802bcba5747a75d90e61b58b4f19927158e431dbdb2b37deebf750ab11aa465d1fdd88060382243323a831bc2c2651db387c77644f5ae678f99167a4c305c6d96b3b4bbf66c85a53165865e6015a5f75c8b66edcd29adb61fad927f7e6fd1e1b70f453cf308ccb63ecf6d2d62c55842c067dfb76c65fb266319c4583617cb8ac53254b199368e59590879253ae56bc79c5e7bf56d577c5af091d236b41d446fb7bead8018161adf7302889c48e083dc97d4a2fab87d454ba9dade5e838a2ee0cd0395740e56f3441d13f7b127946e5770bb42d9443f977c9185ac629f058be4dd5c001bcca2de08387801403b9e218c92a1b18a73d9caea0a9c8f44831b96481ef707bc67b9017ef28493e38bee5c730f8726656e8bf1dedc7149495d5944bc3138b4d6958d26a1d2ce78ce71f52ede3842a2197ebd07b8305d76c08d30624431bcf4d4a227df7ecff8d3c0425e184fe3cc872d39ee7fe2d08f4784e217281066ba38b86ef80f17601026d554f45defb651cbd3a73a292d62a709cf1bf17162fdb6ae34e39a760b1d128f3c87044b475f650333a63e896eb0a4ae359f71d35126e02f9de024b15b1a0ea6442bbbb44e83cd0875636607a80e5030d63abd090c8524db52d94b57e087c20a1d37b00fe4e2d25d6ac2418df0197af19469a96f87449859c30595dc4490dd727a49dad63a2f43a7c51891b5bfa30929e6c7b0b516feca15faadd7456c19f344ecfca27bbac116c09ed51df0828a145ec5973176b078c808eb2616319817587ceac456b74216114b2690d9018477d786b2d690c6ad3947dda0eb1ff5fec5d404bf0ff78fd001d543da2080d76bdc0f373dcf6ffd09b035b8f7dae5766e7a70e979b7ef73e759bf5c20ec037a45cdec37036784e35b90536c325a9ad429b22abc2e69bd693c1ca9e21075294ae83ea4b62f9803c2f9b849e28f0a056695484b2315a97af1daa1a84f92583278736c4b1198532248e428fe2d4cf8151654739f1c73683d461702e7001cac1d696150d799e922a8d03dccbb499d35839630c6353a32ceb127a44bffb4a8335b1487098461a2f9f20fed4d54733ba8aa45743938544703bfe1fb4d1999076882db18ee73a81c59da8e4825c055d4d06356f4689400c13704e80a87a00fc72bc3f56d0be17d903dc9f00b7165d9eaa95c99676531a40c83d4daef2da6022e1a1ff883b30d1870634e8d305e8673789582df6681ee5906ff9187fe18d5f83a377270175f16211ac01f5e704eaa038ff25b4e54acad044278a908b58a681f9f64e2442b9c064075977d0259746fe4e9aa68089bcd8c6d0f89840def21a1725c5d7735f71032a1bd7cb1cd13205aef96a346a187690510c78093814d73aab9221da11f5e672e3c7e34405b58dd4095109d3ca35d7fe189db8336f92c8532d0389fb215deb041e278fbbf0d0da1f1235d465ca319163d7708747d69784c139410c17a7020bb7c5cc010aedc8808e5913cfd809f44f660c3ca65711f24fc3702077d86e91acaa732904883963930711e1dd868127a1b8d7e83a9f20f089f88f33223109595ecb3627cbee301d192137fedbe5795ef495418587bf4859beb30d4eb25aec2a3355c3385284fb218484c055dcbd64b1b78d2163bcd0e335c6c8870ee180f973548a88b5941d6ac8aa7a3aa179bb14fc69dc633808da368562c3c9588f1d8d2f787e19e539043106d7b1560572c4c457e986e81882f888f6526c56b4570826cfd7ce37ca3c81068861946d4db6467eada7a717fe54e7e5bfafe3eddc8caa2116845835e4e539dab72fdf98eb659bcac81b2b218016e693423d5d1d02a472bc71f37e85f2024e436d5075dce2da78aa62571054a4b7e8cba49604619a2d4b3a0397d2894d9020722311c3d7d257b1f9a85cdd254e12d41cf49bb11053afd00df0e746bd83086aab9cb978a255309e19e63d43d49f61e66d563a2f0fd5f3026aa36f3a6b42967af9985196a216d15a76be2d6b274a346d60d17f46038040b460c06c6477da8738ed45419a9ce48077a3b629870fb3ddb972f56cbdc438c19406794b56a3bfcef35b7491d6964dea1242cc28cd40811c225fbc29af96dc67187bcd55d17bcab33cead1ca3681cc2c43f471dc7e83795538c8735a243b0d8eb38c60978d4923a07a7a37c8cf64c6d7fd8d71221b7ed72528c7a22f68efe31ea0a35e86464d48ecf224491ecb26d77cc262d4e78ab1802953607632879d53622b79614af5b85bba6c7372233187a4d194cfcc4cb9000065e62e886157fcb545bfeead4242f0a1c70da9171e0be0dc437f8e12721bcb109ba3ef7afff96cf6371b56263c57ee8c0835b845e877d30ee7a36839f3d78f2395608c3b6d4d46cd29cd66880a09dfbabdd5359ca3523590f14004ce7515ae332d074c254d18c592c228bacdf7a821ba6aacd2adf77408df1f596538eb2a6b230fcfc83dbebd4086a5ee091817f1cc9c9ac37d9147c20e8cb3e9a0599fd9192d064cbf4ebfce6c60c84958cab1184b1c491fa2724df192d5e5f151caecbc381af5cc65c8818271b65486f56203160b7f028ea57815cb96a8daca089e9d3da8cca013a503c90fb10239a5986505b89384246b344f6b7f287b560aad35e700c5b7aa469ccbc465727750da8bb7cfbf4ec5e1b145a0be1bf9f406750bc1d13b22481a2fb423608782eb641dd539458ab56f249cb5134ac36cb4332433c9118d8385a9acb44aa2cb9d929e543ba4a9c178f6744de2e73240c64c08082c62029bef3d8fdd2e9f126075915bf331eaa8fa5d3bf57340a0d3c7ed55c6efbf67acb116219eee62fd5d4a0f521166933dbaec81b23530573766e4b1ea47b0677a52bc33e9b2e178695925fd0bc85b019907244a501950454cb14915414a8c7a78d2ff86440489c9f783e86ad874762622ccd8ec8dafb0c68fe6cd738c040e8d3d4ab1137c06990e27f0f42fce93498dd983b72afecff7717682e060d4eb0012d691bcd13011b621d2154fc0be1023b6a1fe91f942e6253ea059349571f92fd44055c8f5d5b3c106ae66362e01d58645b62e1198691aa02bd5bfa80e417371334fd12769e57d5c0bc02442d87b7407698cf88c4b6d1ece9c215aee5142dd49ff635045743bc7d97d7e478a57fb7428521bb7ab42f8ac226b5e08ee62b8489741cdcf831d41e5e908c53bf5a3b73d2bbb3686afe5614769687050fb6e6189ffa5ee8244564e66313929f0e58a015053910e4faacd63d5e73d539a75622a786b3a5608f9198d570fa7ca2593594ed846e9bb1732146d9adf1cdf533438e556960fe8c830eeb79ef9fceeceb090582d35203e1b34f8f37341dbf8da0e1d15d4ae67a9e788ae30c9b65f39a28b6b506adba5ee0930d9e861589c290f29d2cbf1da7808bb647c64476ec20f602da4b4947a558ee9291a9f25c1ec0675b57859924590db0f979aef30581857ac996caf8b8a416e902b42f7d868f5cbbe4d3436cf1a5bcef6b842f6d53cad60ab39c93c287587b0a01d899d2ed3f77752a601d631f7c53c0778734e91272a9ee9b25d42dd031804609874466f0e714e3f1af5d6a21d33c77821da180744d23a238c13821f4ee8df6fdc68681007b88ee9f2e0c9cbfde37f731d83f7c45f701f03c385e3927e5a7efc202329d7d0047a3b91aa216ec4c1d6e4e34580270d278c83e179702e0d3f6129b4821d225314d51433dfc6b6ed60ab06f07dbce168b8e3c11d8e72f37a894de9e99653d3d9a2588ba21fbf9a5fa8a54a9b06754f6bb970ed60c7d6933e8ef4c2b1838fb9f692eb4c8493af59480604be85143c57f529d2da5ec9619a929858d22e2081cec09bc3c5a711c960f2d746fba6765e44cc11e5bf7f028e393e5f064855e11a04a47268dab1209f263987e44f9f0e4e3d32df8c40a1787d4d7a4a1ac75afa235a65d07e0b80c132977cd1da6d71b745282e3adcd3fee69364ac75b3e470d669efcc90942db75d8bb7592b92a80d2ceac64bed36b4457293336885774ea95463f6941c9c9c89b9f2d06aa6009e527da8fef9fe4154285ef1e64120fd0a66dd77e4d4b3ceab2267040d20740c1ba95182806432f127c15acd1cbc53f957c1fff49540aa992b5fe18bf90c2d14da54ddba183e63b852576a619f1adfd89192d689e73e001456c52993f47ae69d653881b81e6c90d173cf1f482997fc1598d55f677f7ebf8b1921436caf68265adf4bb72926451b0bc66a892f5dc49e5dd57b57075665903b225d4b8d15df5a03f22405db2c6ef9df08f68970db8d04868c426689916b2f515515b00992c3b44073c0963e3516ecb9d30db3827ab9f52aa7ffb15dff6c22b8a825e331fb7d060e4c018ea27523c924cad1d3780a7fff74ac030bbac594e4180438d9775df3adedd17d78bf396f315c5c7ef742aca009de157953b7207305ea4fabba16011373457f31311b7ae752c7e98735e493d7ebabc6ccc1db2bfb19f9252f553af188e696f7b9743c528cebc2551147028efda8f1acdb18725aee84fe136affc6966e96a1ca4d7f87eeb0506838bd4d886865af2c31ab6cf026ab4451fbd624e3a18cdf819296942175f1ae4f75f490134faefb8189fd051f9bbc3e07d3e974f7523b6b535d88e94465c733ca98f7719baea52b1a44e927057fdb1a27c3efac45b03aee0553421dc0e51eeb19c0a2236ff6a5e95d5625b476676db333788729dabf549c8b5e35c3c0d6de83320f544b4bb8069a3c7c4d5a3a1dca26bfd362394c54da4810e9f026a6f2a76cfba7adf77397947e9f4168d4dbe9ef720b5dddb2ae492c6864443311b75b53e4f24fa5261b86ef24394e096c8b165edc483079e25a6e35c234d5b3d4b0321955272f949b1323e1373d91756b86da16164c61eb7900b2fa6a6be09d453ef06b62b559528ee26eaa709af33c9c7c124d648f8b0ec7fe7278d3e41e7456a2fe072d352f5aef4d8d3680c91b2ba9da04c1a69a40a566fe69a2112956db6f656feff304beafc34eafcf5d54674016d67011aabe26d3440ae24c901033ccff33ccff33ccf9f738efe10a2ff1d341091524a8ff27767139172570ad1afb39f70beb1d96f0fd87708bf2304a206d906ab069fa416b83897fab72bdda699852af3ec3a6ec5030b49397bcf9d242e33530808881820f0b8c299cf36b855c8c30a974d678f8da638b1c4a30a78e6d83827bbbdfc7d635b50070f2a9ccb738a1b3c1b52a3141e5358c3455fe5cf5a929c14d6b7aa9253fcaebc570ee70119376a340e1b9f031facc00a1e51c02e43dcc7a4902146282cb3a59f21522da2e409c5387f9bde4301f0838713d49cccf5398e8566b409bb5fa61caf289fa9f1c18309771a3ff58cb761535d42517b494eb53493a2898712e8d0acfa797597164f42fb7b265a654d82854e7b20e10c2a52c174ececff23d82567ce699a1f242a0f23942e2cb2f3c929821aa4bda673c72a2d65e04184bc7b344e8ef75cd7c370dd92681573b68c159a218c4a9acc3ebf280d7930fe7057bd1f63ca6b1a189ecc78cc61622119fe2259a3868d5f65862f5c55cb4daa2a1ece64462ff6c94e9b7caf19bcf84aee8adddfe19f97001461c62ec878c2d566c95c3137b60c1d704017d99aa8c9ff312120203372f185c7933f99580a6eccc085d69556e11a2c26e11a336e71588a4db29865737000046473380808dbc8e138fc062c98610b5bc3493e25582cb9de9019b5b0ec6a5386a8dbc9f10c5a64e2f96975de6aaacfcf9885b9ea9f827b8e9cbf7860862c12bc43b6c947747963713023166676b09cc25c7fe6de3360819b6c56d984fc06335e51d2193f69e3d2809ae18ad296cc7869ba13dd56d061194c3b09f679f3acd8465ba72aa66c60c62a4e93c4dbdf3b318bcfaa28ea6faedb4ba5e20af6dba63ea36255edec9222e2e729334e91f5c4a7cdae31c99e630a4e9e976694c24eb3ad97d14edcd899418aa48b75d6a17219c4c18c5178dda9bfbd57418e1d3a76862888cd722907cf983bcc9050a41ad751e14e9eec720628d8ebb8175e5c7dd38702333e51f824799dccaad4a35ec08e32c668c00c4f60a91ef59d8289fc55333ac18971c24952ca19b33c270a267d95a44e6da2cf92fcf28ed0ce279af83689d9336a32414b9a918952d00ee7e1970b6660c2bfcf9e4f516e63d071d8386390c120202a987189adfcb78359aeb0949e6109b7a37536848c82199538777c5fc46b13cca044f5296bf7973f77d937664c42b594c34d864fa92d3a431279fc907f96d27612cf8c4854f9a4285ea24282ec4cfb268ca724af5782198f78bc93e427d45db2ce3be20cd53a53e9b34baf11fee7981a6b690f27348311698ac48c7a547aee3316d18949cce6b279b7be2228cdbe953d2e9c2711562edfac8ab95c7144f42767c8e0973f4f7e082e2661d5ed3fb7d686303ff46c3d877ece8538bcbd7ddab8b21c2604b1fe615a42eacb832857dad38e379954411c5eb27f5906338b0642d7d471e2a580e04fbe124db2793acd1fcc1a7227327b3f98cebdf5c424e538d9f7a15862e9f9e30325a5fc695fd258b687fa644bd51a4cfaa9f4f0c91a1d3606ab8ce7610bf918c73ec9fae2e1ae9b3ff593a41893fc1d5015b79cb24d454bdb2161b33e55689228eb507c5450a91cc5a41c3a1c2e74f8b50f5ae139d49763a13df4b464e550f78e4ff6ea33e2608e7191498ec2a18b16b2dcbf27547d439ba5b9e5433714336eb496f2d818b661db0a2a1daf41f76303bd5267316d70b1185f43b19e32c915afc498e26ac84e43e68eb6cda2791a3ed1e4da509294a73347c37f826a12cb44932df333d05d49b0752ba1f372331caf3d9fda675c4be26578436e0a3f39193e49908eb3f0ef2bf918d098f39eb6c2e4f4c580f9a7eb4e73d26e3a0ce4494287ece0625783a1984c4aeb89ab8cf90b8da9f46bc5547db117cc0c1ad387f98df07421c9a48f31f3c91abfc3053aa78aa1a9ddc29573161fbde412522d9cff365ed64c839c05e3aa692c39c1e40f62a1fbbe93b7bf2bb81ba663ce392b3c963da595d42895528562feb78f5e9bea332a246679889d9eaccea660dfbced99e687bea4c045c9d9e5531d853e9895dfda68be928642514fcee4fe315428e927e01ba24753d84eb046329bd8e61363df84f5e2a41c56a7c56526a4d1b334cf2d77949750fcfd28a1d32dffea56fd7993409de6fbac8d75ae48c032a5527dcbae2779044f0a933c866e9c198d502629c3c8ae487537a308eaca4d46bc07df6d0611a874fb244f8f955fc350c2929444d964612416066da5e6e15a4f3be260ec1bce72bf8dbeffc048b813ae2a52c4fd5f5c9de424dfdcbee8b4d2cbe458f7b67b61e58a4c789817a74e99f3afd556f8ec22394ba786940a7339ba30532cafb42d6bb6e6e292a643461717e59c5c9b3acb76306fc1c97f0ea1157b4dd216a8bad5a464c91ac25aa4654990cda105af256767dff021bc5920529ec27a8c2c8ac92aab7a6271975c26984962ae5adcc755a58f1d0c1dcff96c574f7eeba05c7cc5b78f0e449a6c1a63eee6b0bfeb988ce6b94e727260a75bb2cbea344a2e0e7a89af1a7f38782799e617afbc72f3062c57b889bbbaedc7b8e1db8aab9cab2fe5316d68d389d5175e36e816c37ec8ea64f2b7065d3a9787ec4a7fa5066a3eb4f2cbe79827260d091b7a2567cfebf4a3610b9bb64c9f2e1e3f43725eed133cbffec7663027e16c529394326ce3259f566e64b83525fbb4d9c6f0b4b94972f8784f8f89c1905116e329a5bad2c25092e4a09b73a45c4c313018e7d2cea2c949d8ccbee007f5de9d9a6a97cb0ba591e892b773b6105717ce9e6ca22cc805b52c9e078dd7305f6d213f3984c692fc1c27a585e24c84ec9f27933e0b252db58831d7b81d0b45c5b25497af50921b4ffd5ccfb849ac70b6704d52948946156c79bd601e6cbb84890615f43649d2f2c6fcf9108d29b8415b4ef4570a6ee84f7b94f77a370a8f5caced0f42a1998f1ea6ab6247f109f76bfcfe946c96a213764b676d9a26b83ee9baaaf93e5b9860bde7971bcd61652d81b48c11fd7935aa44092629c9b69e14672a118d24dcb6f5aee12431a88676bc8d101d35682001cbe9b37c6238f6c609121a47308889397aa4cf973885280d23ac992694c79cc9f4348a90dc9db341db6230d1d020c29eca3493c69c31b665079e0210901d788ef7188629cec6fe8e8ad9ae968730cc5862658a16b739a70f10830667117040091a90058f60d8d1c2e5c8cc24567580e1099b92d7b5fd8b4b32b9a66c7b7c7cf245abfb9eee244e8a5ad2810ae0e8c01864e4e80008088ed7a1c3a31796b4a14ccc190da9d138b6c68dcde1c18b336597ea92b366cc700808c88db5b123c7e6a0c62e92e3baec6ff84c524cbdc04317565ae8cb601f53c6bc8ebd31038f5c5ced7abb321e259b250404240764b0c003177ad8df7fb8ecea490c04440cf616e06881c72d92a45fd9b421516a5388182a28410e13e8c071c3c31658da2cb1cb9bbf445505251083bd05018882472dd0641f2df377ba90a9101db43899904948bdbbd9f7434062e0310b7c5546aa42939cd30c0101b181872c8a1b3d3322c3d3a72432f088c5aa7fe1f17566366dc818630c32983d60d1596d5af69e13b1860e1ceef10a36ce5358d1350404440c15940004c46f9c63a386872bdacc9012ed2f1e90a10110101e909123c77bb4a2981f4b46f7670562c992ece8c64a957bac22dfd0713975a8051eaad8369594969ba184eb535112c572a69cd2cf89395498c73afe9dca895ed1298a6d5f55ee1ebebf3785fae65b612f2a851f6ac43d69c6d992430a6f56fb2dc53c2bd1f518c59b7721674fb23a7d0f5134f17b17cc2ce3558c3d42f125bd53133a74a38a1ea068638636bf58ef953c21787c22c9534e77964f3c3cc179d8e5dca5321ae220787422b7e8ed6ff9f51b32f0e044e2719704bb50a3e1dec49dd7d3c24939a6ada02636d794af9f534cf899094fb2bde029984850ab947653e54dbc5ce2aef7d23096af4f7841208687255293f5ba3e7aca2152258c31ca732d94586e552fa67c963bc6933845ab33492b27f1639384ba9b5d463d2512e958f2cca821487cb1cc4e9c3cf178c469dcfdaac657ddb516783882f914322ba7ef36a71bf16e7cfb952e79faa3147830a2d136a93351c73c948b60bdebe2c46edfe07a480edf81c315b1a513bb0e6fcb1fe744b465c9538e194a2ea1ae810722887d73950b966a637988743ecc8e6e46f70ccfc0c310e7d124c513f33726831e85f024b5c81ca34d6e3e32f020c45242a7cd6d2c176d10e7c7e4498e4db1c043106ef212b76dab452a2ef0080463d32261b62999d8a2c00310e658d7fc56f2a60e7afc819cdad7d4b7dadb280e0f3fd0493cff2df865c58f471faa24bd893187d78c69e4431d53ea6977b97c3d8787f0e00b1e7b5032794e2a966dba233dec1e4afe354950b193759071836fe0918736a56fa704170f4731c17c7cc3674d9a1c2c09eef04abfe94967cb332f0c3cec603ea1a47bfdd1cde1f1a883a7a37a9eb7a1832d7a5297e6b59c0a79cca10aff245d421e72c8a6a7f3a71db3fcbb471ccaa136656c79b3b98e071c922c86bab89921dbf67803fb169dfb93abb55ad8c30df88aa6b9941391caa30d7dcecd2cb12ba5bb65c39a7a2c557dad4fb4d6809d9c5dec2449bfd6d5e006d1f0df4a1a6c93fc5fccc484c0030d9ca88f5e6eccf7ef0c58b64cf2a5facd407ede2bb7fdca4ca22138767894c18aaf1a2db879c9dd64e0666acee43e3bca20410edf01021d788ca11433aa9398184bc871f88d1a3bd8430cb4f7a52c1597eb666fa00223f00883f29ede2f7bd454ad85d8a8e1031c9b838c1a2cf000c35947f44bd4f3a6b91e5f48dde4cc8a9a9324e709088887179ccb27871aff5c39945cc0a30b496e36e3a9939906d7830bc6952cde9bbb347d8cc716bab38f8baff1260123f8c0041f1a2e5a6fd85ca89750ec533b498b6a9e6889c4943a2c6b85548573051f9528277163ddbd06d7d0bae08312c9d93b25eea7b8d5960794951101073ce16312a60e1e2e97a856f5c10e24e143127b8e9893912af1ed2dc2472412a9182cfcdd09597a0808880e4814dd9254639bff4eb85bf0f1083dfe691ab9f8bb510d59b67143070a3e1c71666b1ecf924f7cf446109a2c8bfae56b30f90020e18311a76c39f8585c4df396456452eca53ef5983c498a682af4bce67d627c244213f573a7c62c3184f58108f3d6e87a86fcc79439446279e8fca4bdae29d2f16108c6c27b76e59f5d9b05848f4220d3b94a9476436adc581b3b3a87093e08816477f7cff946f992908f4118b7567a625c3a4f4c10895b54e8ea85d8fd0341e6946e4539a16c7740142ee3a68c1efe039e49accf3cc9a27c42c1871f529f908c9b621fbcf1389fc5d27a49221ff29c929a06f5345fda1ed433df92523cadfaf560785093437fce2589251f79d82cf8573ee1badac783c1cf3f6d645c92b27e87e582cb7e364f093eec4025d1849a8e49ec1cfa3a98a63f0997592f7c2ce9604c6eeacfd9b54ee79018956543097ec841314d62e89a133724fa88031bf61bf32b6559723fe0e0874a9d14618e8f37e461ec73d20c97825b16f870c36692a1423b4617a9948f365cba9e557e7b7b888e3272a0810f36b0a904cf60f9120202520304e4021f6b6076c3a6b974d4e8f3def85043e31526a5aa34e0230d854f9a84bb5025c9ff22f840c39ac29b7d94ca193ecc70c7a92d21cd2f439ef25e5ac8c9608a25df65b5bf68cf8cc18d9dd830df57eb9518de2afbceec50c1a2e4230cc6a6af3bf153ac2fd9aa0f30e4e92ac6eb1c27d6371f5f3827b9939c662b57532f1f5e4812190be7bb69f1a72ef431d67c5a8c553999f5c105c3363d574a83e512df422191d943a738792e470b5dcceba9933cf4624916d8aebc201ec604fb101630b90aa71b55223cfa7185f35a2a2f3121c4636905d737fa7dd668fd54a942aa7e9fe34742534a12f24105aa624ce9daceb7a47e4c610b1e56bf4afa9e33ff430a44497ac9f345353ea270092d1f5d725bc9dfd18181137c4061d76419d633b645ec8f279836066993764a9c4c8790b103070852f0e1047b4eccbb6ad27644ac828f2658ee269594ebed7ffe10127c306149f9fa92d524b518fe584231f642766654dfd23f9490f479f4a44f2e191f4930c9276a5a1243561f484870dffc9c73c2649b847c1c6159cb1ba5528655658d60e64fd4c55c7a8ae9471188dd60729fa249fa291f44a0cb3fa547dec8f8456318f9bdd8557c5dfdfd690823cf3cdf397430402318aed7bb58cca2a9abd234809118bb27aed2469dae6440e317cd2529486776132fd20bd0f0451f2d67f5ea6940a3177809efb9fa6e563c0fa1c18b53ec186229b94649e2bbf872adf947da97dfad8b4ceef0d59fa539c5391745678f35e21d5ca41a6ba7dd5a92b87b0bff3f06ad7cd516c9274929fbcf598be365ccb14c5c1b190d2dac99d7bdf298593c9537aa2a9cdcf8268bf34942f7a79c73831f0b2a2f76b6ac4ab32a581c935c5571a2e60a77f20a935dd8fadd6d031aae583454634cb44ba315c9d525d744daff9534a0c18ac44365bb941c7cdb5b45e289719f27a864d05045ff4998f3d39462c24b2315c55dfb7d749e51f17240031594189b9327cc65f4b07440e3146912aa935caaf1c67b1aa650cf6aec2dab690c5729aa95934daaba36539f145fb0bb2446b5e8fe5308688cc26499248b316751d4a3e36e51b33b72ec09404023149adcaa298deafe6d078a4ea888b5cc523b767a80c6274e26afb16330574b171a9e30764e163e854f93b1ff018d4e9c254586d7494d297e8640831367ff7aca750e73dbe8d051011fd0d844527aecb89c4bde5f33848626bacd1cf673d0fac83f137aef6664beac4bef314175aa893dbd5f22b9d2d5680a3af6b95b82b3b896ba335e89ac6e65fbbd4d36e594389cf02e9f3d3bccc427617596b3b1568b7d5b125cc9795ff24c8e6d762430a93e25e70209aff7a5c4b0222626e947147fa9b5b3dcc6fc71c4d6b6256448eee98d4847643f6dd47852cb88426f44e8b54d8c092fa294e4b43bd37f8d1d455425ac579c79ee7e1371a5688275c59c9ae5410427b23d1def2aff7308264fafbbe73de78a212c0fff290593e492af2d84e63f9a4fceef25fc8428e78f215366c75b1f045231b22b8a9ca7b4208e21df3999570ca706e2d8d6f3de71437b0404e9f65226c9db276dfea0781cdbe861da46f3c39529ad6d7788e84bfbc0fce54f9151574c960f468c5b07f70ee1c1dc83eae1c4fe3dcb4abd1e50b9cc92dbcf53cc7930ba2d2fc7327a7c110f644a22de69c433ea3b9c0e0fc892809204aaa816e08005f8e0c68e901bd8c021019ac0dd041206f00e1d65e42040007c7024900000ecd051460a061000bfc19671031c7be30002d09103012c00400ec66027988d1d394274dca85103010a68c20ed731868f51000530818ca92b2e06db13f31fb284843e61ffbe2c4783254409a4bcaf5c4ea7149004bc4bcc5132a5a79f2800096663c70e6c7419356a20400147301b3b6e84e8d81a3510a00023f80ec818630c1fc3000a2882d5a871000510c10739e0317c8c0244866136763419639451a3060222c2c863b3c45e65ac591692959401023286e7a8b1031b5da3fd07366eac8d1d3676dcb051c6a9916304916094462fa744aba5a4251160f41bafb16a3f962451e417885d2751d2fb73ceb72f92eaa4501964eb059f1efdc2b5c50bcb2d575c4acddf61adb10b9398c3e534a11e3a264310d1c576a325942485c728915c3025846d92ecbd3e65e8000189e0823531b5891bb736431e020212b985264b668d1b4a4f268ad8c20d1a3b7b0cd994cd482d1226ee324b9e1788d0a2b8b77b27ff264986c988cce293e4febbf214594462018b57b8a215ac58051d4eff2ca6a91c5b09711cec0a461051851b3ab7a224136bf74f85626e65da29658f214550718726c12fcf7f0a7b4a96f9d053c414495e9f2ecfb2c7bb934829cef79792844f4548c1e5cdf1c828fe5bf5f5ddddddba39228ad7f25dca686d93a386a234b72e715f1fba6704146426516b2f6aac62f61326f94dcd63c7524b16f144aaa9a32907fded98473a719b1c6c2cbd63fc6cc289dae34b8cd5bd6cb79b6893a8f84dbf49315542229a386945972c313ae564278864c28e765db57b463081bb446a656a77928c5ca2a01125a58d7b9ec73b2296d04e54d974112176a60e229548ccfcb0955e5726d208257af1bdfc9b25478d253209f42c63cc9fc636e74822f128d53c8b47c294d2e4a819a682ae09894738a258cd8c6d31c74d8d1bc108a5dfc2651ff14ceb0041056e90c349105984220a73bafd176b2bdb9588bbee523eb5320404c471808040208208534d8acfcbbb4ad19b1039c4156345dd4a5672f668c4102e88144237d398c4cc6dd5492a4288414404110840fce1d79cb2514c0a39d2fac118e273a84e25d207e3c31e92da24e50b3d13dae3881ef26078f0929d7091698bdce1d826ea6d274d2ed27e206207a274ba53e82825b617a943e286652dcb311dce99cadf492273d892655ecebeb20f45e45094b9f66837399c552271a8e2c2597a65058f397030a755d0d0dee62f165044dea06d8a923de52096ffb9c1a0d5f26742e53b6cd408c02b226d68d63d0916538f988411369c66d36aca4f7995ad236bd893144293664cfa8d6a68544b5a4fcc6c10498326474b1b93d6d792682894e895aaa5e259e832889c61f7f513d79310657d9ae1f14ee983b696d4e63210834819cad3cafaddb262924f065ea32b660ed9ce29263286634c5da29d3e51524a0808480e1b7b0210904444c41086b6e3e42499a4f6a879040ca5578c9a4c92605e9244be50fece9823239371192044bc504a9e837a9894fb94a9f241a40b66768e78f85831a6e00f225c306b76c50d36a1dfed16ea0c5553a9ed20a20534facaa75b15334fab7244b2609667b01433bbb545d5830816a8f5f13de9addd4abfc29e35bef119b640c40ac89898d4f3e4912a906d16e622211b274945a8d05692f9f812a22032054e2763fcf8f962f89455440ab9b75989e5f4515315c3225140d2637ab9e5c898c454040af5e8b4bebb49480d1c64e0a8a1c3466920f204c4d288c5f14c994b459c70f54da79a47d97867459a605277a97b9b74b3985584098a6612be34494796b04b6a4e951151429f532ccab8c602a08448120a9d1e3dd6685df47f0811247492143a48aa66ace62d7284ef2293d8b539edee190202e283b791818811e8ace4e9f4652db7c473e40004a40391222477aeab9694c44c0a47885068564db9d2d2af398c4d761deb1a5318d7d4a58291306b13ea9e34a58d3e80f10bcf62dbc3a6d866b3f1c529db358fcea7fcddf6e29852a8fcaa246a2e0d2f340b9e84d490f7d7ea6317675ce860971531ce872ed88b504b279e0b5c2c9e83d5464d1112e9c72d36792b9d5025131e4b5bfc5d42a55499eab4c28f5ad02231a6df9097a3b3f0c7f3bc7310b32c9a2c4adf15ff4aa2a2ba148b42374a6747df126cff800525c9df26565f4ba9fe8a722a93e412bac4cbd6ba42c9982a586fb6b764adc047f7b3c65b322da10f56b4957f5725fe3b7bfa5885be3a3296d51a7ca802b973d7defa9a8d2e031fa9a0e3bdc7ec927a9ba3e239c174f7e2925827cbe0e314d6275fcb15bcf3720e061fa6f0e75b342ead3ff83b838f52649eded75345830f52609bcde724f3dfec160c7c8c42149ce790d1e83dc9c30c848f5028395562fb5674ca988122dd78629e46f5e3134a496ba6707a37ea7b825e9bbbe8619e64d24e3c955b4e5cf950779d139b3017dc7fb71f7c6c423fb9f4e383c5b6aa77f0a109c6d2d8774a9572ca4b0f3e32118631a8933639200e87511005510c8230a437251da3135070c83c1a0ec6a2711ca9c20f138000cbc2c060381088c24151200c053114c4300cc34008c33024c34064c628a38c1e5f9c7a4763b44aefa8c58a5d091e1706b92db3db941f39306c66f7753634d143f52531aa001971eff8651410532eba83c458bbb10e65d5865af2913db980c2d7083412b5482c27a9205981561849aa150e79d7b11b4d42b56fa4c95890cc71009c7e53afbd388118c041a22ae2abce92872c60c6fee26200e0bf5f7eee30e5b898bea40b56292d51935be1190770f20ee2dffb74de20fa89e721bbe812e1fd619620e3a350f9b25283d6bb63606901d90d39810ebf22fff833992f6bf88a59180faec3c27a3d9a1e08439a9e253158fd95d3a625f40d8192bf6c908420cfea5105a0b87ec9ac4811e8c0650301b0e64facf2034dfc516c1fc4e24e00cd67b2db5c3bafbf26eade68c40d9abb7bf4ffb99eeb621aaa8b49fd41baba5229c2e7db1888f82f7db147ad9069b7f51a5cfa01dde1da574f8b46addf28513ebdbecdff39ff04e206d2fd3a099e8f055b9228940f241768f664fd6b234b2a7a01a80745725a40e47478dd5edfaa0e239cfdd4228658a74975ae7f88f215159e28e7f7c2dd1efa6d4613f24b0e045facf20e325ee1ad9e75d0216bf6664a9e8f11cd8b8b62ea12ccb17164a5e1b9a91ceaf5613f449cd187a4775260f3fbac0033730c4636dd4c18fc3a1d00359523fe8f5f383dad9d2a9a08b688aba810e6e37a88b6d5fe778eb82f8477bf4c49d20b3f8316209465ebc6f1a82e2b8d4a8d44720724bacc60118cb18dac76a3fb950d84dae2a53efb6feec4990fef8864784f2ee1ef1bb205b17421cc4f08bdddfd77a109e5487d3e42e8a89eef445f93fb896b0dd348bd445c5db4928f10e50725a6a93d0053ee3a8dec8a8c554974ca1c9f3e782cfbb8a374fae2eaad9c48140821f8472519dc60ca5103929b396866b4410aefa48032d4ee23f96b7844941ecc374511bdc59bd6e47841517f436f2443d3c3d5137c73b8c6b19fca26a62bc1869dbea0cb735ee0582ab6acac241941eb1a8399b6dc1c50cf8ce30ecb96c117882c95089335977749c11d413f487b75b522f89f47e081cc7793707d2758b79cba86fe6599d2e24acb66769ecf0924d7d01f591e42f1e8b22893dca599f36ccc02d63bb7875f537f12bc5a43da2c00c4ecd657e56fa1397c393ff15c3d8453abf3c2912c00ce24448a447c20c6d4dce9214ecd1f4075353e76c69b6fe5ce20fafeb0ad6c5f03dec04266f4cea2f235858fef7cd72a3b14105c008bb7a3e2fcca66868b74506f4c150dc533c66a17d4841dacb60b41541b258640a0f59fa7d8face0eafc996742062d11cd68fefb6ff9e6d8ff37f6a4b36c50bcd0de4daea347aa096818387896122904cc82529d48ed5bc38453552899f3552a2958ccaf8c572e663fd1f7488e22c9340ccc23928a51795d624639c5211e7be46f5b2f3f074f8e3e611f7802b7e968c35bbde28963f3cb7dd105890aa36641675bb294017a88ca00f74b4b60a4f3f5ef8f1df9530272bb748e7257852d249189d07460a7b99d0c82a4b05e5d04031c1cb51f929b8a43e6a6217b9f2affd7550f4b0637973998db0cb75ed1e60f6baa7b90cd8099c4c515ccad9e42a6df646b895ff4b44971473118bd01579ab0471c76e318d831ae48d47351f62e28f069781f37bc33772f1c2d27b2fbaece93dc8eb842c91bd315334b753ab3116074d8facb48713489b741c6efb397220f2cfe06370a9b0888857d07564ee66903e517492c990135c12b9b682cb58cbcac5bfbef48c0641b079f5e74e50d030f1f9d797842617f81b8fc72c369b59d7ad5f2c40d1e93656bd3dbcb11ef981529eedfe7eb460f22254e99015614fd30f0d7c85853ea662bdda8c830a766bfc9960f0b7a83eb25c0e35dd6b4c204d337acd6b91eb7e33ccd3ed6c1bca18b733d23ab48e681d84f89b436f23cf1d89e9066d53aa6d4980ec5fcd587a70084e6365598f2fac058f5e935a05f51bce14a0295491735601c8f58318fd044aeed6b2a627f2c84449090ae2ba14a1d3d0495869b7e88677eafd29d171b5a587f9115603fabf7c7a11a576ab4275d84bac9e4fded3c2d5af03c3bec05a8ba07e3bbd6a4ccaf5274c149c3593823ae369d1e23c8748e2c40270447479bdef73855a95ba359c93b1e76677ab1968cc4ff01940235fd9f9aff2d022f2f23c0561bc3e510a8176c0f3ebbad853cfe3cfd980855e247a872a01d7a3c101dfec16902babbdfef8531ea23233c2108fb3686f91e34245b5863c1756992d2e65f7e8eedb985df47045198588aad6418b350e60a32c7b75ef0b4b3ff6591bb921ffcfa2e0e1d088eae8b469b42a1a6a59672d6b8abf3d763722bb753f53320a56a86360bb041a65b294c73001f6f09a7fdfe6f2968e23c3d897e2bfa0122245d2195eb3828b201c5ce384225090c6939b0acd0996a955b433967151d0547533caa4986b6d0f3d5f4c653620105247655e257b4e440c6d44ac52806ff40fcdfc57483b1b14821134400721045b3adbc6d3747f317c4f44853be001274ec6d90648b3db664e0df71118cd689e04c59c39446532b80fc5a78454737330ec11c4c0024cf8f6905f1f54ed2e1611b9ad30de3d0e5a32ad5b65f53831873f3f246211b1c65a8aacf21d8e7a280563b8b34d2d3d7d541c591c21fe50c047e276d3837cf09191fd36b3bdd5d6c365fbefc4caf81ee30ed6689bdd3eb85db6fcc0e1b9b03002b1c5eb041fa752d43ba950127066339316f12c056a3c1315fec238580ec8223bef3e802a4a07afa3650d51c661e739f806a9639c5122d273f35a09a2f47ef1c136878451de97a51c6101bb352e0892fdbf8ffe89d3e4210f8ec7b34cc65b167e6af9b49350f3917767764c45cdecd86f985bf7c6046d24cee758ded220f67ed913d92378d057deb8985ccd6e6a255f35e98f4a5ec05c9658a98ca66e684f8441627eab4244bc0141b6f42eb417fce5b291414f23e76abd20ccfe7fa5bde47399b6fca00c88d95ae4cc46db2a2c4f756569f9c847b195a465448e994efb83482f733c3cb6e683391ce08b9d260e59ea533ee1ba4130beb04c5b0d860402929bf3915a9aad93f56db5756d6a85d8e1a4464d4aa0b324e213c4c18469061e21153e5b7d8e4bf88168349b018d9506cf9c4ff01c4039dfcd6fc30a50d9100ba36902af9ee013a121d0edfb7e7cca7dead586b0950ebcda7d9dead49ecb14837ccf0ad8aaa53a5eb6ec113301862ec604a3a054ab036ed9311ee267da95d12b7094cc6ae92f71c12adc19489837f135bd805997607026ea3955f0df311c3d303081ed49bccab80424791101a724f6888faa0f78de1f1a1712d2b96dcb03c09573fb09a44de51611ea55e1281ea34e0874ab12fda3cde5cedbed43d7694e49aad3b74230f74c8d0ffd1af398cbb8d38e598e03de751cf57ec22e64e5b61f871756c90025e8d199ef40384a1658ea61cb5e4f28c59e0cead024c4b88b4cd1bf8184c0aba16f6c55a4775921a2ef4d9cd3d816e53152cba3e66957b5879adad7d845e5d575c6ad06d6114f276caf096253c8967990d9160cc56ac96154cbe3ce736f2f1fa7ddebd7f6e761172f73a3057cad238e461301a7a6f854d298f9abd35833a15ec97e23a42c3518dbe89e2f3207c70c8db8e299c093150bf6c72678f2854542b9bdeb281353f1f0686d19e5def01c616cb19c5baa034c4019573a0b4b50de975e3ec532ea4808f4bb30976037b3e26ccb125608bf06ff0fe2be720de818bc23aa4764aa0b32f5dc2c2a3c859a7746035505954aca3a663b58b735825476e15ceb90aa5896d6373b283d44211c936ce267815501091355da8a8725da46c2c082cbecc5895f6cc2f7ca82a7db3d4241e43a77c1731bc7018cf1ddad7f4ac7bceacdbc9b74abe5742c82f6a6e89450f3ffa55fdc0370139ccbdd365f45208a554727e7adf8466d7df1f752130c97d6aaf5727280896774ea59e47aafe61b6b3b1a16c11b373fd43a89cf044c84891d420274b63184328050cfd6e7d6d1ea5b7dcc3f88cbbba25860c4cf00d345fe00b856e8aa5f6337a479c6e0f44b8755908b0d3960c3062a478c6c6e2b98cbf175225ea3a79cf12f2c67e8fedcb3e418a9374285dbbaeed712beb02bdb2399f856f9e0f11d52af5ee498baa4bbc92b10237cef8c7045102290aca055d113e603303d0a3170fc117be22ae194c591b7c8728316750a958f4deae13bf6ab8e40295ef8dfd4ab77a7edb3f15dc9f47f8a13d9230fe62730c837c11d7a6f09dd29df1ddb0367030ed3d89208404f19c2c2d37e5044b4198792fdb2c8239c8f20ce9e8997ca20df628863697eba96ea41b12a7aa7381ff304109868b0fba5a49f65923c6d10a1b8d4df30a707b91138c5a64159853adea822530ecced363544a4d5e17a5fb4a0571e8203161ef47aad7112f920cc00bf958e4680fba3f6a349965f1a8a269f253011e78e33fcfc9a6d606f796ee5909e5b3773306d397704e72b6a4acd946bb1dc77fa90baed3ea9c20c95b50f919b0fb2bc2824caf864329887d058b3ebc5daa8f4e0541eafc55822c18fd9d389920c570f13b68432498b23ba5f6a1c7b1cd8f9260ff6d979dd8891f949e45c8068a104e700662c507e5d013d6728f44b182db1ddd4f66cc460976af2cbc9e1952b9157f29ca3e4ab598886cac9e07376d289dd467f58d0dae7c2d402061eec7ab8b6d548fc1456a8a6383e82bd031512f339cae32d12e71093bce8967b7043ad4300763708ac3269862c5bc59ca3e241c5dd5941975a8fb00b8b627b50ba280ab74e74f938e8f916aa2b3a6fb0e29a14d79881a42333e9d904e92663ce0c47ad07506bdce2ce764311ddac7b4a3c19bac9129507b227b9994bdc4e825061f5e09e207b19fde4790c32ecb170274c7f3dc7a4d862e43a7c9fe0320001c940ab4b0ce95533b4128ae6e9b25ea0c7ac4be6b4eceefacb21fc6cf5933325a4ff4f2a605a83f4549d0e1dec83b568efecce140a728ac213071bf962ae8ebdf85e08ffa17427d540435e938c1908ec36a91e2cdff72194f1d0870232b7c86e2487da52081754a69a9ce349bf261d7a33e228a176ef7e2f4f15102cf63162eee54724daac20c5387ec17a52b3602165e11736c3c94fa269ecbb98505e2700a65d0a27faee321b9fefcb464c44103639531c9f22555417a882b80072cec78c8ef15e3ad0b3283fdd3778f85ab6229d6ac4505764c48267d7a36aa1787c470944df84363d6c3c1dff9ac3630d3d0b8ccf181466820c4652f15670fdebe94903f405572fe31f32af6ecf8d05faa3b4f7a3679ce03877f98c7d2b943fce08ff1ca35d0f5b82b7d96cba1f3e369e2d239ae8812d2045af21a0557ed54ecad28b75cc43103fdd5ef336f935e677143603ce4ebe11635ebc65a6e78228ad47ed7ad755f3c9070ccae75bb10f00357db40a8a87ab5c2b0f13811b67221d83a08da21624a0e949f83b7def703773a6844e12a160fbc739f89a52f6007cf531d4484b5d085f1a5ab13736c15654b32b97ed9507c16ff2d1d9e451a3e4aaff08fc83392047b0912c0fa44cb86f6607b3fd50bf6bb32c3721f094e1d2814abd559cdbb27116be3c1a06630496e0ceea84c4cc4f6ddc29e97f5fff6275a415570c231e416a3cfd773c25382ec79a1639f13a1c7f265e734c0049d12cb8b9f721865080e43b297d4e0a3f922bb56adbd0404443df98b92ef3bc7c3649c0d353e7a4b9a44a0583a74bf49caee8dfa2f4fd884fcc9fa9f6cea0ece931e03b0e863f0fcd1004379517ec4bb88d10b79de920d124bc2f29f430f2dfe615a73fdbe2aa835e8cae488b5d55fe4b830fbd1f9c7526209b158b822bafb43e0896addcc753cc6bf1a52c221a640ae68244aa2487fc832c9595d0ca517b3ea7b31bc72053969a5655678a3e130d57851c72a68dbe8249efe9071f6be64dbeb5117b5c6b985120eb6615805fe0851e3ed9c6b2e163910b1610bc751193f6a8844e34e99f69b5f520dbf848249468df3cf5ced20597a67eb153897bd2808b436c7e32a0466447942eb7720ccdeb5715e469e43e9e1462c13daac5bb32214b8a085b1f94805a96d86d692c3497c9b93a96b0ad2f618ba9cdba9d067bbf5add457aded77e5e95401b911d497adccb294d6d2ba9e4c6a5a6db8836e9b5d3404d4c17c06a33f6b54ba238181eb0ed300cdf60a6d7c90ea08cf796462384e4dbc6cb3f6a981002f009", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0x3c311d57d4daf52904616cf69648081e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x3c311d57d4daf52904616cf69648081e5e0621c4869aa60c02be9adcc98a0d1d": "0x1094e8f841122bad62ecd5016f80587ef7d91043c828e3112f668523db811cbe320676ce35043f553c1a3775a10ba54bd5757e48ebe38bbf2b4c4986896dcda702cc4c407bd279279ebbfdb5ae2cd29b04ac748a90bcc23a910e303104e47b8c5e741100320fca26c26c665a09fda76a2b2b11ab6d36acb8942132be5b436e7602", + "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x45323df7cc47150b3930e2666b0aa3134e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x57f8dc2f5ab09467896f47300f0424384e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x57f8dc2f5ab09467896f47300f0424385e0621c4869aa60c02be9adcc98a0d1d": "0x1094e8f841122bad62ecd5016f80587ef7d91043c828e3112f668523db811cbe320676ce35043f553c1a3775a10ba54bd5757e48ebe38bbf2b4c4986896dcda702cc4c407bd279279ebbfdb5ae2cd29b04ac748a90bcc23a910e303104e47b8c5e741100320fca26c26c665a09fda76a2b2b11ab6d36acb8942132be5b436e7602", + "0x5c0d1176a568c1f92944340dbfed9e9c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5c0d1176a568c1f92944340dbfed9e9c530ebca703c85910e7164cb7d1c9e47b": "0x52bc71c1eca5353749542dfdf0af97bf764f9c2f44e860cd485f1cd86400f649", + "0x79e2fe5d327165001f8232643023ed8b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x7b3237373ffdfeb1cab4222e3b520d6b4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x88fbb13c02428a6ba0e3c362f503d78c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x9ba1b78972885c5d3fc221d6771e8ba20f4cf0917788d791142ff6c1f216e7b3": "0x01", + "0x9ba1b78972885c5d3fc221d6771e8ba24e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f308ce9615de0775a82f8a94dc3d285a1": "0x01", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00000000000000100000000000000000", + "0xcd5c1f6df63bc97f4a8ce37f14a50ca74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb34db9bf7072c23e5fcc4c407bd279279ebbfdb5ae2cd29b04ac748a90bcc23a910e303104e47b8c5e": "0xcc4c407bd279279ebbfdb5ae2cd29b04ac748a90bcc23a910e303104e47b8c5e", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb364a2023e1987811b741100320fca26c26c665a09fda76a2b2b11ab6d36acb8942132be5b436e7602": "0x741100320fca26c26c665a09fda76a2b2b11ab6d36acb8942132be5b436e7602", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb37428b13f2e5363940676ce35043f553c1a3775a10ba54bd5757e48ebe38bbf2b4c4986896dcda702": "0x0676ce35043f553c1a3775a10ba54bd5757e48ebe38bbf2b4c4986896dcda702", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3add4f66f85260a9b94e8f841122bad62ecd5016f80587ef7d91043c828e3112f668523db811cbe32": "0x94e8f841122bad62ecd5016f80587ef7d91043c828e3112f668523db811cbe32", + "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195092cf984b8a6521a76175726180741100320fca26c26c665a09fda76a2b2b11ab6d36acb8942132be5b436e7602": "0x741100320fca26c26c665a09fda76a2b2b11ab6d36acb8942132be5b436e7602", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950b861e1707ac2446d61757261800676ce35043f553c1a3775a10ba54bd5757e48ebe38bbf2b4c4986896dcda702": "0x0676ce35043f553c1a3775a10ba54bd5757e48ebe38bbf2b4c4986896dcda702", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950bb7409db8b905d2f6175726180cc4c407bd279279ebbfdb5ae2cd29b04ac748a90bcc23a910e303104e47b8c5e": "0xcc4c407bd279279ebbfdb5ae2cd29b04ac748a90bcc23a910e303104e47b8c5e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d9ae2954d96d8a5d617572618094e8f841122bad62ecd5016f80587ef7d91043c828e3112f668523db811cbe32": "0x94e8f841122bad62ecd5016f80587ef7d91043c828e3112f668523db811cbe32", + "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x1094e8f841122bad62ecd5016f80587ef7d91043c828e3112f668523db811cbe320676ce35043f553c1a3775a10ba54bd5757e48ebe38bbf2b4c4986896dcda702cc4c407bd279279ebbfdb5ae2cd29b04ac748a90bcc23a910e303104e47b8c5e741100320fca26c26c665a09fda76a2b2b11ab6d36acb8942132be5b436e7602", + "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x1094e8f841122bad62ecd5016f80587ef7d91043c828e3112f668523db811cbe3294e8f841122bad62ecd5016f80587ef7d91043c828e3112f668523db811cbe320676ce35043f553c1a3775a10ba54bd5757e48ebe38bbf2b4c4986896dcda7020676ce35043f553c1a3775a10ba54bd5757e48ebe38bbf2b4c4986896dcda702cc4c407bd279279ebbfdb5ae2cd29b04ac748a90bcc23a910e303104e47b8c5ecc4c407bd279279ebbfdb5ae2cd29b04ac748a90bcc23a910e303104e47b8c5e741100320fca26c26c665a09fda76a2b2b11ab6d36acb8942132be5b436e7602741100320fca26c26c665a09fda76a2b2b11ab6d36acb8942132be5b436e7602", + "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xe38f185207498abb5c213d0fb059b3d84e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xe38f185207498abb5c213d0fb059b3d86323ae84c43568be0d1394d5d0d522c4": "0x03000000", + "0xe81713b6b40972bbcd298d67597a495f0f4cf0917788d791142ff6c1f216e7b3": "0x01", + "0xe81713b6b40972bbcd298d67597a495f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xf7327be699d4ca1e710c5cb7cfa19d3c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000" + }, + "childrenDefault": {} + } + } +} \ No newline at end of file From 58fef4fd972dffc53f676e1c7a48886d63857ad7 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 5 Oct 2022 16:25:34 +0200 Subject: [PATCH 074/263] [BridgeHub] ChainSpec Bridge-Hub Rococo live read from file --- parachains/chain-specs/bridge-hub-wococo.json | 1 + .../src/chain_spec/bridge_hubs.rs | 104 +++++------------- polkadot-parachain/src/command.rs | 2 +- 3 files changed, 27 insertions(+), 80 deletions(-) create mode 100644 parachains/chain-specs/bridge-hub-wococo.json diff --git a/parachains/chain-specs/bridge-hub-wococo.json b/parachains/chain-specs/bridge-hub-wococo.json new file mode 100644 index 00000000000..b96b5d4afb1 --- /dev/null +++ b/parachains/chain-specs/bridge-hub-wococo.json @@ -0,0 +1 @@ +TODO: setup diff --git a/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/polkadot-parachain/src/chain_spec/bridge_hubs.rs index ed2f678ee54..7855f378667 100644 --- a/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -54,23 +54,37 @@ impl BridgeHubRuntimeType { })) } - pub fn load_config(&self) -> Box { - Box::new(match self { + pub fn load_config(&self) -> Result, String> { + match self { BridgeHubRuntimeType::Rococo => - rococo::live_config(rococo::BRIDGE_HUB_ROCOCO, "Rococo BrideHub", "rococo", ParaId::new(1013)), - BridgeHubRuntimeType::RococoLocal => - rococo::local_config(rococo::BRIDGE_HUB_ROCOCO_LOCAL, "Rococo BrideHub Local", "rococo-local", ParaId::new(1013)), + Ok(Box::new(rococo::BridgeHubChainSpec::from_json_bytes( + &include_bytes!("../../../parachains/chain-specs/bridge-hub-rococo.json")[..], + )?)), + BridgeHubRuntimeType::RococoLocal => Ok(Box::new(rococo::local_config( + rococo::BRIDGE_HUB_ROCOCO_LOCAL, + "Rococo BrideHub Local", + "rococo-local", + ParaId::new(1013), + ))), BridgeHubRuntimeType::Wococo => - wococo::live_config(wococo::BRIDGE_HUB_WOCOCO, "Wococo BrideHub", "wococo", ParaId::new(1013)), - BridgeHubRuntimeType::WococoLocal => - wococo::local_config(wococo::BRIDGE_HUB_WOCOCO_LOCAL, "Wococo BrideHub Local", "wococo-local", ParaId::new(1013)), - }) + Ok(Box::new(rococo::BridgeHubChainSpec::from_json_bytes( + &include_bytes!("../../../parachains/chain-specs/bridge-hub-wococo.json")[..], + )?)), + BridgeHubRuntimeType::WococoLocal => Ok(Box::new(wococo::local_config( + wococo::BRIDGE_HUB_WOCOCO_LOCAL, + "Wococo BrideHub Local", + "wococo-local", + ParaId::new(1013), + ))), + } } pub fn runtime_version(&self) -> &'static RuntimeVersion { match self { - BridgeHubRuntimeType::Rococo | BridgeHubRuntimeType::Wococo | - BridgeHubRuntimeType::RococoLocal | BridgeHubRuntimeType::WococoLocal => { + BridgeHubRuntimeType::Rococo | + BridgeHubRuntimeType::Wococo | + BridgeHubRuntimeType::RococoLocal | + BridgeHubRuntimeType::WococoLocal => { // this is intentional, for Rococo/Wococo we just want to have one runtime, which is configured for both sides &bridge_hub_rococo_runtime::VERSION }, @@ -110,65 +124,6 @@ pub mod rococo { pub type RuntimeApi = bridge_hub_rococo_runtime::RuntimeApi; - pub fn live_config( - id: &str, - chain_name: &str, - relay_chain: &str, - para_id: ParaId, - ) -> BridgeHubChainSpec { - let properties = sc_chain_spec::Properties::new(); - // TODO: check - // properties.insert("ss58Format".into(), 2.into()); - // properties.insert("tokenSymbol".into(), "ROC".into()); - // properties.insert("tokenDecimals".into(), 12.into()); - - BridgeHubChainSpec::from_genesis( - // Name - chain_name, - // ID - super::ensure_id(id).expect("invalid id"), - ChainType::Live, - move || { - genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - para_id, - None, - None, - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), - Extensions { relay_chain: relay_chain.to_string(), para_id: para_id.into() }, - ) - } - pub fn local_config( id: &str, chain_name: &str, @@ -302,13 +257,4 @@ pub mod wococo { ) -> BridgeHubChainSpec { rococo::local_config(id, chain_name, relay_chain, para_id) } - - pub fn live_config( - id: &str, - chain_name: &str, - relay_chain: &str, - para_id: ParaId, - ) -> BridgeHubChainSpec { - rococo::live_config(id, chain_name, relay_chain, para_id) - } } diff --git a/polkadot-parachain/src/command.rs b/polkadot-parachain/src/command.rs index 5e0950a5673..fe8dc655869 100644 --- a/polkadot-parachain/src/command.rs +++ b/polkadot-parachain/src/command.rs @@ -203,7 +203,7 @@ fn load_spec(id: &str) -> std::result::Result, String> { bridge_like_id .parse::() .expect("invalid value") - .load_config(), + .load_config()?, // -- Penpall "penpal-kusama" => Box::new(chain_spec::penpal::get_penpal_chain_spec( From d9441aa144a9efab3df75bca2901a20e9edfed7a Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 13 Oct 2022 23:42:04 +0200 Subject: [PATCH 075/263] [BridgeHub] Added default chain_spec for live Rococo/Wococo --- .../src/chain_spec/bridge_hubs.rs | 116 +++++++++++++----- polkadot-parachain/src/command.rs | 8 +- 2 files changed, 90 insertions(+), 34 deletions(-) diff --git a/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/polkadot-parachain/src/chain_spec/bridge_hubs.rs index 7855f378667..ce1705822a6 100644 --- a/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -14,17 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . +use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed}; use cumulus_primitives_core::ParaId; -use sc_chain_spec::ChainSpec; +use sc_chain_spec::{ChainSpec, ChainType}; use sc_cli::RuntimeVersion; +use sp_core::sr25519; use std::{path::PathBuf, str::FromStr}; /// Collects all supported BridgeHub configurations #[derive(Debug, PartialEq)] pub enum BridgeHubRuntimeType { - Rococo, + Rococo { default_config: bool }, RococoLocal, - Wococo, + Wococo { default_config: bool }, WococoLocal, } @@ -33,9 +35,13 @@ impl FromStr for BridgeHubRuntimeType { fn from_str(value: &str) -> Result { match value { - rococo::BRIDGE_HUB_ROCOCO => Ok(BridgeHubRuntimeType::Rococo), + rococo::BRIDGE_HUB_ROCOCO => Ok(BridgeHubRuntimeType::Rococo { default_config: false }), + rococo::BRIDGE_HUB_ROCOCO_DEFAULT => + Ok(BridgeHubRuntimeType::Rococo { default_config: true }), rococo::BRIDGE_HUB_ROCOCO_LOCAL => Ok(BridgeHubRuntimeType::RococoLocal), - wococo::BRIDGE_HUB_WOCOCO => Ok(BridgeHubRuntimeType::Wococo), + wococo::BRIDGE_HUB_WOCOCO => Ok(BridgeHubRuntimeType::Wococo { default_config: false }), + wococo::BRIDGE_HUB_WOCOCO_DEFAULT => + Ok(BridgeHubRuntimeType::Wococo { default_config: true }), wococo::BRIDGE_HUB_WOCOCO_LOCAL => Ok(BridgeHubRuntimeType::WococoLocal), _ => Err(format!("Value '{}' is not configured yet", value)), } @@ -47,42 +53,74 @@ impl BridgeHubRuntimeType { pub fn chain_spec_from_json_file(&self, path: PathBuf) -> Result, String> { Ok(Box::new(match self { - BridgeHubRuntimeType::Rococo => rococo::BridgeHubChainSpec::from_json_file(path)?, + BridgeHubRuntimeType::Rococo { .. } => + rococo::BridgeHubChainSpec::from_json_file(path)?, BridgeHubRuntimeType::RococoLocal => rococo::BridgeHubChainSpec::from_json_file(path)?, - BridgeHubRuntimeType::Wococo => wococo::BridgeHubChainSpec::from_json_file(path)?, + BridgeHubRuntimeType::Wococo { .. } => + wococo::BridgeHubChainSpec::from_json_file(path)?, BridgeHubRuntimeType::WococoLocal => wococo::BridgeHubChainSpec::from_json_file(path)?, })) } pub fn load_config(&self) -> Result, String> { match self { - BridgeHubRuntimeType::Rococo => - Ok(Box::new(rococo::BridgeHubChainSpec::from_json_bytes( - &include_bytes!("../../../parachains/chain-specs/bridge-hub-rococo.json")[..], - )?)), - BridgeHubRuntimeType::RococoLocal => Ok(Box::new(rococo::local_config( + BridgeHubRuntimeType::Rococo { default_config } => + if *default_config { + Ok(Box::new(rococo::default_config( + rococo::BRIDGE_HUB_ROCOCO, + "Rococo BrideHub", + ChainType::Live, + "rococo", + ParaId::new(1013), + None, + None, + ))) + } else { + Ok(Box::new(rococo::BridgeHubChainSpec::from_json_bytes( + &include_bytes!("../../../parachains/chain-specs/bridge-hub-rococo.json")[..], + )?)) + }, + BridgeHubRuntimeType::RococoLocal => Ok(Box::new(rococo::default_config( rococo::BRIDGE_HUB_ROCOCO_LOCAL, "Rococo BrideHub Local", + ChainType::Local, "rococo-local", ParaId::new(1013), + Some("Alice".to_string()), + Some("Bob".to_string()), ))), - BridgeHubRuntimeType::Wococo => - Ok(Box::new(rococo::BridgeHubChainSpec::from_json_bytes( - &include_bytes!("../../../parachains/chain-specs/bridge-hub-wococo.json")[..], - )?)), - BridgeHubRuntimeType::WococoLocal => Ok(Box::new(wococo::local_config( + BridgeHubRuntimeType::Wococo { default_config } => + if *default_config { + Ok(Box::new(wococo::default_config( + wococo::BRIDGE_HUB_WOCOCO, + "Wococo BrideHub", + ChainType::Live, + "wococo", + ParaId::new(1013), + None, + None, + ))) + } else { + Ok(Box::new(rococo::BridgeHubChainSpec::from_json_bytes( + &include_bytes!("../../../parachains/chain-specs/bridge-hub-wococo.json")[..], + )?)) + }, + BridgeHubRuntimeType::WococoLocal => Ok(Box::new(wococo::default_config( wococo::BRIDGE_HUB_WOCOCO_LOCAL, "Wococo BrideHub Local", + ChainType::Local, "wococo-local", ParaId::new(1013), + Some("Alice".to_string()), + Some("Bob".to_string()), ))), } } pub fn runtime_version(&self) -> &'static RuntimeVersion { match self { - BridgeHubRuntimeType::Rococo | - BridgeHubRuntimeType::Wococo | + BridgeHubRuntimeType::Rococo { .. } | + BridgeHubRuntimeType::Wococo { .. } | BridgeHubRuntimeType::RococoLocal | BridgeHubRuntimeType::WococoLocal => { // this is intentional, for Rococo/Wococo we just want to have one runtime, which is configured for both sides @@ -107,15 +145,13 @@ fn ensure_id(id: &str) -> Result<&str, String> { /// Sub-module for Rococo setup pub mod rococo { - use super::ParaId; - use crate::chain_spec::{ - get_account_id_from_seed, get_collator_keys_from_seed, Extensions, SAFE_XCM_VERSION, - }; + use super::{get_account_id_from_seed, get_collator_keys_from_seed, sr25519, ParaId}; + use crate::chain_spec::{Extensions, SAFE_XCM_VERSION}; use parachains_common::{AccountId, AuraId}; use sc_chain_spec::ChainType; - use sp_core::sr25519; pub(crate) const BRIDGE_HUB_ROCOCO: &str = "bridge-hub-rococo"; + pub(crate) const BRIDGE_HUB_ROCOCO_DEFAULT: &str = "bridge-hub-rococo-default"; pub(crate) const BRIDGE_HUB_ROCOCO_LOCAL: &str = "bridge-hub-rococo-local"; /// Specialized `ChainSpec` for the normal parachain runtime. @@ -124,11 +160,14 @@ pub mod rococo { pub type RuntimeApi = bridge_hub_rococo_runtime::RuntimeApi; - pub fn local_config( + pub fn default_config( id: &str, chain_name: &str, + chain_type: ChainType, relay_chain: &str, para_id: ParaId, + root_key_seed: Option, + bridges_pallet_owner_seed: Option, ) -> BridgeHubChainSpec { let properties = sc_chain_spec::Properties::new(); // TODO: check @@ -141,7 +180,7 @@ pub mod rococo { chain_name, // ID super::ensure_id(id).expect("invalid id"), - ChainType::Local, + chain_type, move || { genesis( // initial collators. @@ -170,8 +209,12 @@ pub mod rococo { get_account_id_from_seed::("Ferdie//stash"), ], para_id, - Some(get_account_id_from_seed::("Alice")), - Some(get_account_id_from_seed::("Bob")), + root_key_seed + .as_ref() + .map(|seed| get_account_id_from_seed::(&seed)), + bridges_pallet_owner_seed + .as_ref() + .map(|seed| get_account_id_from_seed::(&seed)), ) }, Vec::new(), @@ -242,19 +285,32 @@ pub mod rococo { pub mod wococo { use super::ParaId; use crate::chain_spec::bridge_hubs::rococo; + use sc_chain_spec::ChainType; pub(crate) const BRIDGE_HUB_WOCOCO: &str = "bridge-hub-wococo"; + pub(crate) const BRIDGE_HUB_WOCOCO_DEFAULT: &str = "bridge-hub-wococo-default"; pub(crate) const BRIDGE_HUB_WOCOCO_LOCAL: &str = "bridge-hub-wococo-local"; pub type BridgeHubChainSpec = rococo::BridgeHubChainSpec; pub type RuntimeApi = rococo::RuntimeApi; - pub fn local_config( + pub fn default_config( id: &str, chain_name: &str, + chain_type: ChainType, relay_chain: &str, para_id: ParaId, + root_key_seed: Option, + bridges_pallet_owner_seed: Option, ) -> BridgeHubChainSpec { - rococo::local_config(id, chain_name, relay_chain, para_id) + rococo::default_config( + id, + chain_name, + chain_type, + relay_chain, + para_id, + root_key_seed, + bridges_pallet_owner_seed, + ) } } diff --git a/polkadot-parachain/src/command.rs b/polkadot-parachain/src/command.rs index fe8dc655869..2845d2b2ec7 100644 --- a/polkadot-parachain/src/command.rs +++ b/polkadot-parachain/src/command.rs @@ -481,12 +481,12 @@ macro_rules! construct_async_run { Runtime::BridgeHub(bridge_hub_runtime_type) => { runner.async_run(|$config| { let $components = match bridge_hub_runtime_type { - chain_spec::bridge_hubs::BridgeHubRuntimeType::Rococo | + chain_spec::bridge_hubs::BridgeHubRuntimeType::Rococo { .. } | chain_spec::bridge_hubs::BridgeHubRuntimeType::RococoLocal => new_partial::( &$config, crate::service::aura_build_import_queue::<_, AuraId>, )?, - chain_spec::bridge_hubs::BridgeHubRuntimeType::Wococo | + chain_spec::bridge_hubs::BridgeHubRuntimeType::Wococo { .. } | chain_spec::bridge_hubs::BridgeHubRuntimeType::WococoLocal => new_partial::( &$config, crate::service::aura_build_import_queue::<_, AuraId>, @@ -781,13 +781,13 @@ pub fn run() -> Result<()> { .map(|r| r.0) .map_err(Into::into), Runtime::BridgeHub(bridge_hub_runtime_type) => match bridge_hub_runtime_type { - chain_spec::bridge_hubs::BridgeHubRuntimeType::Rococo | + chain_spec::bridge_hubs::BridgeHubRuntimeType::Rococo { .. } | chain_spec::bridge_hubs::BridgeHubRuntimeType::RococoLocal => crate::service::start_generic_aura_node::< chain_spec::bridge_hubs::rococo::RuntimeApi, AuraId, >(config, polkadot_config, collator_options, id, hwbench), - chain_spec::bridge_hubs::BridgeHubRuntimeType::Wococo | + chain_spec::bridge_hubs::BridgeHubRuntimeType::Wococo { .. } | chain_spec::bridge_hubs::BridgeHubRuntimeType::WococoLocal => crate::service::start_generic_aura_node::< chain_spec::bridge_hubs::wococo::RuntimeApi, From 1f27ee76901e056d3e8c934f84ae2e828d293332 Mon Sep 17 00:00:00 2001 From: Anthony Lazam Date: Fri, 14 Oct 2022 15:32:37 +0800 Subject: [PATCH 076/263] [BridgeHub] Add Live bridge-hub-wococo chainspec --- parachains/chain-specs/bridge-hub-wococo.json | 86 ++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/parachains/chain-specs/bridge-hub-wococo.json b/parachains/chain-specs/bridge-hub-wococo.json index b96b5d4afb1..9d354ad11c7 100644 --- a/parachains/chain-specs/bridge-hub-wococo.json +++ b/parachains/chain-specs/bridge-hub-wococo.json @@ -1 +1,85 @@ -TODO: setup +{ + "name": "Wococo BrideHub", + "id": "bridge-hub-wococo", + "chainType": "Live", + "bootNodes": [ + "/dns/wococo-bridge-hub-collator-0.parity-testnet.parity.io/tcp/30334/p2p/12D3KooWCNomXYZWuhwHsWhZpmrFmswEG8W89UY9NjEGExM38yCr", + "/dns/wococo-bridge-hub-collator-1.parity-testnet.parity.io/tcp/30334/p2p/12D3KooWKSq37RLqP3Ws3FtJDYB1xsjoBeJmehVYDZcCDRNLBXas", + "/dns/wococo-bridge-hub-collator-2.parity-testnet.parity.io/tcp/30334/p2p/12D3KooWDkSQzQYC7VwpJKF8VJtJZMG8bcvWXm1UEJSKk8UE2iv5", + "/dns/wococo-bridge-hub-collator-3.parity-testnet.parity.io/tcp/30334/p2p/12D3KooWQoUFxyPbpotTdUpfnsxQfQ4uyxz1beW5Z39LGM8JPhLi", + "/dns/wococo-bridge-hub-collator-0.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWCNomXYZWuhwHsWhZpmrFmswEG8W89UY9NjEGExM38yCr", + "/dns/wococo-bridge-hub-collator-1.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWKSq37RLqP3Ws3FtJDYB1xsjoBeJmehVYDZcCDRNLBXas", + "/dns/wococo-bridge-hub-collator-2.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWDkSQzQYC7VwpJKF8VJtJZMG8bcvWXm1UEJSKk8UE2iv5", + "/dns/wococo-bridge-hub-collator-3.parity-testnet.parity.io/tcp/443/wss/p2p/12D3KooWQoUFxyPbpotTdUpfnsxQfQ4uyxz1beW5Z39LGM8JPhLi" + ], + "telemetryEndpoints": null, + "protocolId": null, + "properties": {}, + "relay_chain": "wococo", + "para_id": 1013, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f": "0xf5030000", + "0x0d715f2646c8f85767b5d2764bb278264e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x15464cac3378d46f113cd5b7a4d71c84476f594316a7dfe49c1f352d95abdaf1": "0x00000000", + "0x15464cac3378d46f113cd5b7a4d71c844e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x15464cac3378d46f113cd5b7a4d71c845579297f4dfb9609e7e4c2ebab9ce40a": "0x10b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", + "0x15464cac3378d46f113cd5b7a4d71c84579f5a43435b04a98d64da0cefe18505": "0x0a000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9050f9ffb4503e7865bae8a399c89a5da52bc71c1eca5353749542dfdf0af97bf764f9c2f44e860cd485f1cd86400f649": "0x0000000000000000010000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b4dbfc3b7761206de75b3a8d70fc3d44a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b908aa810c364ce8c3bd964ff3d424cc926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9f52c4b3c3fd1c798e3843e21a38f1421b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9ff9bdc7d7afef8c14d5b253d4e25b33db0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x04446272696467652d6875622d726f636f636f", + "0x365c9cdbf82b9bda69e4bbdf1b38a7834e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x3a63": "0x", + "0x3a636f6465": "0x52bc537646db8e0528b52ffd00587c78048e21c5d10f4f107868930efab1b838f4ab956e37666c7f315033ee36fc62e8f5cc1f2729a56a67ad16541632755b67b4c33c707182dcceb49492c72443bd03548e239738f428dd94584236217b6fb2a59449ca94649a119b0ee10e5f4d2b20202028702a8fb7706a8fb7dc3d1b1f3e809203256859831668e0583e3935df72adcb5b4fd4f016ad3d353fa9e683e6be6a2fe984db6bebdab39938eba767b3404040404138eb6fd318b5ce9b8969d66160a0f1e0b1772b2668f0aec7ca9f552d845560e20926618c728caa3c7e7df46eb504973ff1389d14703ab976e7d78e19259e8d7b342e0b22092020a0a0af4900010141f92a2c775915418e63addff0e4546d97ced1a1d79e9a973e19c53eb70468edeea3dff0dce6bb673371985d22b1bf9422a9f9487920f6bbde1b929ae7798e316add653a903a463dea18b57ec39a64eec1055385265fa582227c118420044901065ec071ee4ca7d37b43dad9df7b8fd9613b3fc8db868d437addbd89530b9d73a0df706f335504398e749ff741c838e288838d7fb40a497f6a70ae88744608f8cf61fbf3a63efb90c2dfcdfdcfaa9ab70dd09d5ff0b9f3eb6dbcb1679be5ac5f2fb6477fbe59ce8b7dc188c12ce77596b3ce59bfcd8070bf0eb7fdb7bb65092d5fe7333b33b316cfccec0eba1eac84dd3985e25485274e3defc1a9fdb744d03fc7d8e6e1ec571ef82b8f7ffe1c72aac7a33c3523a8d1a0d0dc4a0552bc5b29a1c657d4b31be0dd4a89d5bb2c47bb614dc338c5a9e8ce21a7a4bff34b6eeee3b67fb90368d2e5c336cb890f5049bf360b22baa4357af48bb320b48fee32c828e82ecb69bfe1e82ebb5c366955fd74b9d529fde6c9adc297eeb29ce997dc6a035efae5328bd624de72b955b77ae971ab8e51492839a248065e7a758caa44bcf41a817871736f6d4b5e1c71c4f1e5a3cbadfaf0d0afc5e4067d08f9726e4be4475af7255db271c411471c5f55dfd1e77d53c7a86afaf59a773aa8e642262e675a41e8725a139a5b2961e5eb7cb752c28a778eb1cde2383f71caad80beb8af3cdeb9ebe16e2ec703a79cbbcd3d6ffbbbedf3e61e728addf9b5d7ba6c97a413063718e5febd2c87a973241d313c6194fbf5b75d0fe36d9fb37597417ac32ecb998c7ad762f0626cb308b867df2b5cee997d99e90dab761f70adef366a83b602e2f2153efb5c9fd33d1bc73455fbf5aa3ddc35e23d7eefbdf71eb3cf673a23043b74665ad91ff7a8feec974b69973b80e67cdd6539ce37fb210920202025de51c7a83a9ffd62cc71ea39a7d6318afd8677838c62ef41abda33fb8953eb264eadb36b9c728c72ee5d06372472f826aaf9cfe73f3a23c473e8cf21f5d95fea8fd6fdf51e4c79fc9d0eaab79b85cbbdeb51b567bf26a7ea8e1928da7ac59e0ba0ad3f77eeb221d6791e9d8caaf09ddff0b5dbfb8ccb238189aec576835e7e897c244abfce9e39214c16e8972ce091c07e9d5de4843059a05fb2804722fe3afbc80961b240bf6401bfceeeb2202010d0afdff0c5987342d6eb73c728e7935375ffb9f3eb6d9351ce6bb4e7ce5da60319e5fcd1c928e7377c31b61b3fe68430d9d523117f9d3d7342982cd02f59c0afb36f368406f4eb18a7dcb3bb2c8805faf51bbee11bbe61b7394639773d98f268ef7450f1b2172ec797a3d1d69d3beb723eb475e7cebad61da7aaf3a58e51ce9dab76735c6e0554e59dbc9c8db6eedc593bbcb03168628dd00e27ec586307187678b1a38b1d58764461870c764471e2b2c3891d2a908942260d1930648620636507143257c8f840e60732639009834c1176ac40460aa73064a89cbe2013049918c8b0e0d486cc0a64c07072e304874c904c133267c8084106caa90b272fec68c10e169cced81124d3458609192fc890e1b4e56485531664c420f305992ec8a071da824c144e65d801744a43460b3259908142e6c9a98c1319322dd8f16487122736645eb0038acc1464b2c87091f942468acc123b9890992263864c19323090c1828c0b64542003a4e38b0e357490a1a30b3accd051868e2ae8004387157458d1d1021d5062b2104346cc1662ca881923264b4c1562cc88e9124385182bc480117385182d315dc44021e64acc14629e10f3454c1762bc00c325460b31528809430c1860ba605ac2a40293122620983860e0807903c60d98364c544c3330b5c0c4021314a628a6274c4e987660a2c2a403530e4c38305531ddc03485c90626294c3530d1c00403d30b4c2e304931f5c06485a90ad3149836c0b001f305660d130f4c4c989ec0a801460d9834c09c01c60b4c193064c08001660b4c1660b000a305183360b480c9022606302c8079018c0a60942879297da12446c98b52124a482819a15483120d4a3328518909a314a5e444690525264a2f28c9a00403d217121ba438486f90d44082838406d28a7485d4041214485e90ba207141a202290be908242290b22899413242290d120c483f204dd1dad0c4407a01a90a4d0d5a1ab42f9a1ada1a4854907a40b2421b834606cd0b9a14342c34236835d068a0cd40a3a2c94063c1a88d111b34218cd8d09ed054a029315283d683d117edc9680c2334465db4238cc61889611486111846678cbc30e232326354c6888c87863584ac0a182690bcb40a4852387d094de199e134060764164e67b07cf0ce7002c3b252c40a82d4c22bc3c90ceb07325b6494909962cdb07a6009e191c102c2c9cb96f1d2d8d104a90d323d901143260da735442684a22013c649cb8e2564aa20a3857fc03ec8b630dbc8c68039c2cb12b190899171c1da41a90dad091a1332282218528bf4e28d4172414e418221b110c292fd00a60c3a7ea0c3073a5c3000016443785e62164f8ac9847641e68337c5f5020b0c8c0818191a1118800302b9ca40113ab890da2805959e2845b15cc431b42160565c49905f946220577021e162036a8155805fc82e4030ae2fd913305e30726d712de16242898cd214fc45282804c5cac2324208286eb1e2885798638cdc082961b9b1830d27345855ac362c36e415425e94c840b28175c555053ac14fc87a00f345d602182962ce904143a90c302d904f985cd809310bcc84c601b606d6054c0c581830366064c0e0c0dec0dcc0be606dc0dac0d8c0d0c0ca809d81a501c302f38279014303b6066c0c981ab02ed811302360596045c0c080a5817d013303a6c66586eb0c5719a6174a6c685dd0b86866686568646863685cd0b6a089a16dd1b4a06541c38216867605cd0a1a189a16ed0bad0a1a15b42cda142617320c19052985521b200e32317017328d1831ac12768b98342c1ebc2e5c858404790566b1aa8054c89a28d9c0b24266592690b4882c601c9cda10d910cd8864c4322217183490a63861217a414279522e3730155c6dc89e904d218302f6e4822373429625bb72b58129119f8850c4284f0d0f0dd9161917d912de12deea31616a51eac14442c7a067d0516445c8b0785108bd71311182c3ba8235062b0d0b8deb0d6c892b0e182c9da57bd0573410fa07ed83d6415b89f9421625931243063986dc82dcd2001d5a1a8a24230b426c41e98be946480a3035804cc0277009d803a8021854aac2e88da7a514049826c044c157202581adc060482cec85b5c62c639bb04bd880e46257d20a18d03a41963144c602d9c55cc2e98d0c8a7c2286cbb5067b81b9bc28d918486980a1522a833405d612adc05c442e30d08363e36028c42d40200b0eeb8d69460c8357d9166e42e720d34214236a619231b9c0572414a4183c8561d0636834f849a84b480ccc44c88c10975019a12e84c80881217406cb80a970143c038e01d78069c04d48332417192f325778714427da8ba482ac02cc19d209305f886ad08016c50215809141044a6ec0cc00260d181a2c20472b02901f062080b644a3e1da777b59c1e2b7d3c40808882831b273041122d73e2390ec1021120a054104a3800187589c113e4d764040e488923781cf0f498a485244920938f4a2082249428024889e9e240cf02962a7a72709030600bb704d7a8e18f1d93182c8ce4e080200a1e0928400c911222660b2438225448e28791f243b23308111221c5e713b45204982848811224794fc4e11489244c0070909921451c4df009f60449222768cc8013ac13531c10e0f91264d4cb023049224479a34d9a1c006b9702538a2e499ecf0304982812136b8723b3e468c38d2630409223b24d82181042a60c48e084460a40203804d704d788ad839a20826b8263e487698ec1c81e488c3252039128219b8852be24813244798f824b143c4043d4a941821c264c7889e2324c0e12922490f0e0fa804370222467c8c1861c4670702249884131ce93172c4e70892233920121c4f11497a404064c7870445ec24b153825a3826491880c123b8263b477c8e304972840f098cc0c02c1c9224477c8c30218111447c92f434217244c91b614282cfe176a70704467c90902004447c8ad86172c4082447881091397518d28a176858f14e75b91510d0afeac9c44e43a76195ac84106008303f000210780033333b21de830f76538b5a9bb2badfa36db545adb62c8b52eb596dd186164c5916b52c4aa9d56df1c285bd16a5d4b2bad96aabdf7b56bfd76b5956aff5baad5d6bb7772df8765fec85fda0739673d239b7d182fd245cf6615952b6dcb52cd8ddab492861b7b56c59afb7ad7dddfc42b043d87b5648f65bab03f042f1bd197b614f29313892326ad02decd7bb6fbb771db00fcab600b0bb413cade577af655996cdeb7ebd0bf9b525e542b96dc1deddb576dff6badb82fdac65b8bd16ef365b0ba55c6ba1e5203f08a1f5ace79ee51e5bdd2b65eb10daee1ec95d51ef5a701b4aa84509b1910c8542efc96eeb416cf7ed42cb52809523df7b96730c84ce0f393e721ac0d1b22cb62cca009cc73ee4636959dab3ac8c2d6bb7dbea0e3d0831f8205cb8fd2486edcaf4955dd66f775ba6b7a5f76e745c988f05f4c3ba77a1732b7ba58c50133d0865c3c7ddf2c151f7cb2e0000404a0877b15dd83e307ab990d51b820a48c07ccf877e6f77d7b9f77a080000b04377335ce6dde56eebf56ef6de5bcbb29ed53bec055993526212c27dfba08d8d1004d874109bb7701976f7a67adfce744bd7ddec03be968d59f4bded03583dccbceeee1d7a33b8bb52ee6e76b56561eff50efb5caf65657d6def5a0680b017f67b0feef6dbdeeef72c8bd2b79c5deff57b565bcfb220907e0da4002feeaeb5d6ab793f9e65edf0f6317c10428861bbdb524a299f84b0350d6a1a848d61ef4909a17c0f4ad9566318f62ccb7addfdaeb69a00dd6bc1dd0e6d6843d9927677d8de87f9d819282526257cdd52b60ed27296dbe12ccbb5eb0e59d6f6c8b2ac1db6f77aeff57bfd963188c1d7af777978dd036801f485c3d52bb769b6bbe57ba4b55001e80e4000baad05c07b4fb6ecf7ba17c2b743f7ae65596b5996d5ddfd7ab7772526b79be19390610f1d74d04187d188beb796b5dbcef56e5b2d174228bbb79f7bafbbdf83af1bc207bbfbc1f73608a1b5d6f65addbb5af77bafe1ae6575ef7b0dfb75871ab2f5dada6ee77a8717769cf170700518c1111f242460424921f021c10d3b4710f141728489122245343932021e6ec40e931d23982421923a00909d234c7c041080924cb683a7082320e8e94922801c76e080030e3237f008214972c4a708234d901861e243c4e7c8084690049583926690ec28516284470fa91b924852021f1c520700e23704015200224076788cf480808808901c39a28991233ce4c3c4884f930000d181878f11263b487644c0a4c90e330ec84e93263b4c948c2049114498ec2091a4881d26448c3431a2881d2205e0491d00889222769ad8cc238af03102022247343982e448087684e008931d248820a0000738001023768824f15162022645e8305d017690d8e92192849111e420e30a2082243d444480e488084490a447c6003d3d47786a4e01d0e180f82449126384911d262813ecf010f1496207090f39441000932b40139f264792d811008c0392c49110ec30515253da11811126497c9a1031a2e7c8114510f1e921819110ec1841e34680c3eeec3009011126497c8c1491a4a7c7088f92cf4e134702c9ce23b902104192e44813263b4810616264a7a7082249ec1881e4081124478c1c514408429084884f123b1409243bafe4d8044c928c6067041109243b4f009913f8e7e7272ba25ad5aa56aa463ff0a97ee4fb51c1f8f3a352a97e7e7e7e7e54aa56a97e7e54b0886a7f7e7e7e7ef6e7c7a97eb888fb51a95e919f755711a752858aa8d6a97e5e919f2da2fa51a97e9eeae7e70716f951edcf0f17f9d99f9f9f57c4fdfc7011f7b3459c4a1671aa59c4a9ac223fabda224e158bfcec8f8a8b38c74554eb545d44b5ac82459c6a552ad52be2545b44b5aa2d1201588675ceed089a187101ce41d823cd6e9593101614c2a8c41e8568557dc8656341524223cac38d3911591bb9167b442b77f7955dec1dbb636f557b9593d8eb5850c4a87c7d1787a46cc7821a93f2f585a2ef7b756251fef9e5fc176beef4d518e5b531b65fbb5d8c41b659208cf64667325f7f1cf46ca09492631cf4b03dd21a447bf4d81eb1202c4a8c118bf2ab653a9847ca13fdf211b6d5fd1679894a89fe60bcbde4994e3b8fa4589bb629a3c33c8755a4a425116d6cdded625bfcc591fbe3d6edb1afe758cc74a45bfeb6f69b27a19413a7f89daf149c6a1dfed1a322e5a14329cf065691f2d031d236a384b6405f998a94d3e54af97a30c6b0ae3e52ca902f6f68433dd806fd4acc89fdb13d58c823d0bffaa83c7458e5d9cc4703ab48f9998bbc6ed08732c7bcc20f5d2edd97aff333af4c65b4414665b4fac8972ea275833efacc30cc891db217c59c08853c6639212cca5b524a893901adbcd341455a1d628c768b287f8d78708c31b6739ccc1e63f45dc63cfa0d5f7146cab38e398c3e1bd3aae9dd6c6b37cb9d0c824170fa064d2acf83cd29e54a918e697ced92345af93127d1cab4cec998740c6290e9ccac0941e8106e106f94b8512c6b8439368a21d7a076f1c843158eac2d7eca5db93bb1b86e5d5bf49bb7eb040fece7faa4153eedd988a8bbe314e6ec9007fa74e9157b098798749d88decfb1e98df9e513a32197983f8cd6ab9faf13d343749d88316e94b7e69c739d783fabba9ea68618a984ba3cbf6a3895b9f32beeeeb6fb30cfe88ab13b8e16c2071f7cf0a23ceca37e508323bfe1fd91c3adceef7972d48373fae5789bee48da08d2d07541bf2ee8d705fd62359e47facf4327ba2eb855f830e4708730f49b07b7ca69c44b4479422e619c2f8f6c1a42ba8956e62e8f56ed1f3d3948af904bafd085904cebc258e4d9dc94300b4287d1e10e811ea963d44238b72cd3890e290ff49163d17a240d92ae4c273acfa579a8c82bd13a314ab26884ae5959ce3e2a126ef12564873288eb47ecd37b83be5bbb88b1b843ea42086bbca35f1774b8b9df0c3eed3dcfa0a4f5147a91d6f910f240bf5cc22132aff3333a237dbd9f3feb8568f4aef37542ce7359212f72f9bbbcce7779d5aed3f3b84342d12ddf2c67b9cb4fd8f1e5e61e42e8371c37f832c2575a131344ce58e6a8fd72cf79ad795783bc737f3623778ef28153d347dbbb2ee87363dfed72b941c7b61c3eda9ec390442f8788176985d167f4b958449f908a50c41cbef4d07ae8453931ca13c27cfd86378b1d125dca50496216adfc21bf61cb8a96f4d8b16347ca13f2cc63bc1e26e97adcaaa672229d23fb62219d6385547c85eec33c96ffbc64e7b9b4aa7e9d37e8d1178b1dc21ea9145fe1ce1be66c88ea155281eb30e4925648e5a1fbf0470f71bf7efd3a447962acab8594524ae83221a25b5acc555d2c71267376e80b4349225a4f414497434497be5638c558b094d5872ba4f2ecd7a6653a22979447bae5a36c08eca38bb6ba1f99e430d4444b543a7466afb014fde5900e5d94e9b0f32c15795664dd04d34b4bd0492c8ad1b12c2762f92bb4b9cfb2cd7d2846b9b5cb9859d0178bfd213d663aeb97c32d46ba56da633d391ce7379cd226e07046ed7b0ad17ae2d82b5acb37140a854494277a16ca6287489f9c82aeb97b36cf350a21e5893e7d5d8b97abc5ae16cbef6af995d34331140a85a2877cb320b2f5a8c52f9db17c5db69f4e9ad61e72e9bbcd40d17ce0af571f38b74aafabc54f1f6dd8361925a2d507ce4c675dee6af199af7ab1f46a09b96cdf2c428ef9f4b512b39c09a9fc1537f7a1addf0a6df1adcd7d8c31b4c5e8371cdae05b59ec0f193bf464b62c5035551eb5c7bbca430d66616ec977e57a53dee6259fd921f328bbdcaaf6fc7a7ec9cb6b9e5faf0a6f3c99fffcc3e1a255f59764e99cc38503901dc2de5e71706f1c68e5773645aa0fa1e7ce3ddace7cf9740fdfce94e7f2cc9ff7c0291eb2a6a6a6e655911b4f7649f9bce63d2a5fafec06af6fcaf4ca6fca47bf1e7cf0c1de497df82dcaf33cf366eac3bf2ecb833ef4f04d6b14e279d6178d99efc5589167e3664873c7291c20905120fba35dd2adc2cf81369d617f3637305e32c5bcf7a4d7c837a5e6f0a6fcba65c9cc2fb901d91fab853c078c03d96a0ed4bdc21ce40c6d9a03c981027957feba36f7cc9bfbebbdebdadee38c02a9796ee1b0617ec3ebe6b1553eb9cfd03a7d07addabb0cf56773a2247a3a8da8c95f44dda381a125209768442af26820d5469b7b3421d1e61ecd94d3e59397cb4933203df4e083bbe2b2549fae4b6b0f1f3da33cedd2eb5e98dc6a0fff2e0ac487bf8b5cee5ee637cfa608ada7d345abc6e5a103d921d263f7f0d5a77bf8f5cb2bcce0e53c190d5d5ea487cfdc872e573ef3eabc3c74593e735f80ec908bd61005f2a6bc25a5944016e8875521796282ab9c1c55e35e83e4e0d5ad70702cf67bfd056b2cbc85dcb90e4e7d73ccdd73701cde56359593e70ebead7618d7db414a7fafdd72b8439e5b2d77a037fcfc86e30e91dedecf01b08303a069e5bf7c075af92dbfe1ca5dd88b88ddb90774f9e5750767afd2994e8d0160f9e54ceb0ed4a7e737119c8271d7f4396f557b9593f6a5b07d3b0c9e1dfce77be576b3c31db2de7ef37aab9de56b943bf8f6881ea7d76e5a1bcbb3c31ce6306f1afbc17eb01fec1dbc69b5a84f57d9a707a47da9eca5a7564fcdec3becb0c230cf3017613e72e7a60e5e35eaf5f4bc79f5670b02178dfba3e9ccf572e89099be033553c323c80dec4d6b10eceded9b0dc1deeee00ee9c6f275bbbd3616d9587e9f37e569df81fd6d759f69dc1febeaa96acee626003ea27191003cf3b843da63651fc0a33e4d51a14ce7398fa400702b1b02fbe737a6181fc0c919e80980d6494369951445ebfce801a0af1d0034ee6e7ff9cbdadc5fbdc5bf36f716736fcdde3bc0fdb1dcbe5bf41edbba9f5bf50bebbe7934d04dae03ad33c629addac9375a4fed18adceeeb4a29ee7406b0d0eb40679e837cf666fa035c8d70dc283d69a476bcd0cad28a615b583566f5a5d86d6d389ba950e1a43dd2a0735b9d5d3bc4e9257ade4f5f430ee1c0c7b6944eb89b4b94723d236f768b2eb4929e565b99475af0c8b4438d7b47297e9a5f2976f571fd0b7e5753a7bc5ba7cdcddb5a84f3f4ffba496578cfaf4e4f5dfaedce5df9cecd8ebe9dba56f168445e3fe78de2eb73a7d3dfaae9e4beb39e60bf46c76f568a08fbc061179adc9bca22eaf8e793d595e35e9400f7d57cf86811e0df420225a6b325a5117ad7ec2683d59b46a5f77f568ea9474570f3d625eb9cb2f467dfa31af537ac49c47625d5eba97c95f1ed04bafa787ce40daac9a7bfe92569f3e7d65a0871e77081677b7b1747777c7e62aef7450c9fc3c15f636252ee8573df17857fd9d6ba168836d21d7e2b187719ec202a03fc0bb151458fe721a139a5b4141e551ef5651bcfce5341f686e0585157ffdc2df33feba1c16ca72760bb97b36dac8b3a522b7d6dffab3defab3fce659a2b54a8f56d5bf87bf22cb45d4bd1539bf12e5113d2f7135c1b889290cadfca29a57b17fb4f6605bdd9f6ff7511e914f5fbaec9708ebc129923b9df79569855bd55ee5e467654dc1b34b9fdfba5ebe46b7369ee93fcf5b7d41bc7f7ea978abf057fad2c75bdd2e5f7d38c7a24bded7fd3744e59f9e2ac5eac32ffd3d5af78c8feec3bffee6bff7a4bf67519ef5e96f25f40adfaad80ab13ebdeec558cdb371995f10984c679ec8b567e39a8ffcba5c068120d6d9d9371b62a55bbe413b84dda28e51d26fde0df3e69e9fab0fbf75c64b0f6d753fe458a603e34c79d8a75f305bdd5f79b9c6392c37852c18b72c8716ad392e133bcce4297f8936f76f73f1dfe65eb4a2ad9fb7fe6b31d1b6ce1b7c970dd1bf6f6ed0fe900e339df590c78d5d6ed14b9bf4de30bf79bbb9bf2e8c37c7a8e8374cda2a7b3bf48c6acfa6a92f758cd2e808fa73af73d2cab407136d8e51d714ed7c75af0c5b277648dd971ebd07a7d831adf0993dd61eccb27cc78c129acf7b11e561976ed16ad1ba513efab59873361ddd651090beb4eeb333666d15dab4579ff7ec966f16c45b67b7b6ba517ee9cc74e9f1adfbb3a97934d0a1bba5fed0f2d883fd7aedc15ee49957f8d2e7fdbac8a3d78df2d23bd359e711d1ba6ff9e5301b02fbf51a914311ad3dd85b9ed10a3f3a746c738ea66e94af3eef2d8fbe4eec90a5b589ea2dbfe4191fdab8afc5ac2dfeac2658665145c1b3b5b5b5c19f415c2fe93ab13f22e56d3a47c3ee7a40eb67dbb48519a69c93ba75c9850dc499ee4ec84e77739bd3a7dfb07b3693d67dc7a83967979f737a0096dead5690850867b0022fb60b2564e9220967c04c21658fafb2a74775ece5e9e56a055cbc74c84de04837710abea4b5a787072091d0bbd50a8ef03138881fe84007f88d1efc468f28685c09030c2e72500528b2685510c1800a9491172b780203415840c9728169cd69cd39e7cfa02b3f2580011eef564167f8f86e1584c67b3208dac2e93481d3a9a6860235354d7400061d2041092508016854027e15128890032b627c51831a808053e73b2d09226a6a88389d889859e282f204ca0d4410e8420a6cf0008b12499041160635e6950734518509230861053d808a142bacc0022b586006d604169a28432787949f2b98810b2b8c718616b2703561a50928e83c207bb7827284d7deada0547100cd0144fe798f5c0f9701b10f1ddbf8a391ee9ef7f3f5dafdbc8778ced3de8351b26bda9db3a97134d29bd6e8ce2819bdba10a3e789de7ec3284e455ab58f2edd391be768eafc3a1fba73369aa3910e698d0efd8a1b8a51d2e1e68c92de5b0f46490742d3de7df51762f4ed3d9c53d05f96039bb307f8b3591ce9284e35adfe8f4edecd310afaf35a8305a3dcb3af93d6d06a4d0da7aad603a7ea2908a7daa53b4e551f5eba7b241d316cd15eb597ee7a48cae3560f8ed3c901a7534d4d1035353ffcf0c30f1a7c67e34040402ac0a913c6069a9bb5e69da39e4d0e154478820a3080a20a96d0b36b7c006dbebfcbd87f298f86846bdd3921ce06e2382ceb6a0fef60c39ed3478736ec9587778ee2d41bc1a1b9d5932d4f7ab75ae20cefb2e7377cc329cba7af9f3865f93a0f4ed59aaf1695be3e3713a3221338634c2146151b5c91460d2c26809868e22d5a25eda979cb2f6b3b595eb537316add0ab974f76c42547b34eb16adfbd3ebfc2b7d808610fb8e8d046b087b10638c715fbf28c419a3e851b10837ecf5ecd8cf8debcb1790f064891e30bb3ad41d7d4ac30e586db513183b4ad9b16110ae6bb79bc747d3c7181d724f8f29523a948c4d7b7af4786b5c70017ec5b5281557c86a2c674a1861f74346ed5b0b81ba3bca4b4a295fc7eee618a5a118d8c57e51099dc69bef05529855260caa30c8497ce77cd42488708fa6be2d4ea478e8a4ca1ab4eaa44a7542e5a1d7d7a507063df4a610023d7c5f1e3e2f0f5f97f7b83cf4f7200ede96af507a9182b65e2195c7e52ba402e16c322ed7a33a2d18467f507ef50667c32b862c89c518a316b224ccc07ce935210b3a2cd2144a8f0d29a4d00cf3084b906fbcd1dd3076775b69b8a5bbb7bbbb9763c73034a4224229d367ca1fa05f9f53b4ba542a0339e7a3dd6512601cf76828f3368f7836d29bae15ffbcfaccef9642eb5e292c65ad783892f29729522a4b817308381f4a71c135197b1dfbc535ba09cec431c6c8bc576f0febc7ccd272369a2907a3d157ed87fea628e7e3b905c52e9b4cb1caac09b90bd11fd4b35ea3aa704763cd884a62a268cd5b830e2deee2e253a55e5c1eba8443833d1797cd5d5c7a709941806f7a79e12d2e2f97978bcbc545879622c418e3741d63bcd2100b58951983dc46a087fde5617b79d85d1e7a737936a7def26ca4435a7b62d00fdeeb37fd7dcbd748652501c691f4f2d286b65e23957fd7977f70d9884133063df61ea4c239f7e68430385d1263ef8ddbbf37cb613a4d61d0b21cf6ab2100633784668c76fd7a536c8c39c411471c3fb593437dd7d420df3eb10699a35035255a6b48b406095d3ec25c64d1ea5fb58cd2d345ad392dafa7f7e9b1659492fac40855f2b5170ddb62dc6d7b376aab69f17218cc72a4654d8fedd1e5a4d22f890d01bd37c93276364343d03687bd35d3c97d01b2b73ff66dead87908de76cc34a1b1bb87ce0cd9b320d8b709d79cf389fc89e88f0f2ff31ae461011ed23cf4ede1b4aa8cf47af2e18a934d061ecd4b3987aa39e4f0f5f4d8e93a85acb9d90fece3f93585b61e217465f8e713d37cf0e1e296428a8b89a13ffb240d766a7c3b37bedd08b6d1dc1861907ce8a1f2c03cd88429d9273ca8c96182a971d44727d18ad268adf95af341822899232aa2d8157af20ddd19df373fdda1da390bc2ad7ebaebf13d638cfb649f3ca4500a0c4b27ecb74f2fa0fb45e7221531c6e8457ca94f6f3f255c2ea54de79c17e7bc7cf4eb82b4a21e3a8f6a7abe78ab739d733da67697dbeafcddaee776178a9cf3b15e4dbfbcbd2d791c2964ae27e8bcc400d35c97057a02066d7df90de7053a2feb2592d1daee6e53e9844c1a682d047fa4d0f988113a878ad1097c195b529ff731ca88c47ba8bdd341d5fb5af0def437d75fcff5be7dc9f6ceef31bd84a793e684408f3da61cc8d3b559cd121ae6d964d4dbbf9e5b16410a2b0ade39548cd0747ac3044d0fa90be248bacbe88fd432b3d09e57d20a915aa840d1e4171fbd927ee0f14d2517e7e3f9baa1ad33d9af92cb3f6fd831d21ea9654a2d0f47569e0aaed63408b15d6a6adfadb79737e66da6da12e78478df146639a79305bb61a075f7947347c7de2c88960b3707ab936ff89dc311d26c88e8912201bfe1f71733084a4d45bd6773d55c7273327047d37e6dced168ae59ae9540ff2ef2d0fd25eda999353fac9a0ca15c36ecaefdb5c319285ac78e1bfc6b8768872eb7992622030101013d0be1c3cfec4bde17a2e332af8bfea070aec8c047875b4541ede7d683faf768dea669367ed220eaa14fcd071aa685b6208c7a7e7a36d67c594e531f84a0add720ffa61093513b310da2500f19e5b84c6e17b823be5deb397d3b34610fa3f668dee9a1bba9030d3a9c5afba492b39c48518c7a3ebbd0d62bea1f13f815f5cf1d9ca753642805f4a841934e3b3c3d843d58c5beb7296f4bde6adae904d9663a64d4290a0dbee7909e18f57e342020202570ae80ee92c77b9b81a2c1f66bc74c131a74f7ed32cb69efec012a98050123cd425634cd74ea18690ff6b0219d118222c1fe3514d2cabf4722d11f77c5ed9342be817c0b20f3ea2737a35367a36d4e08e6edd3d9d44097b28484bc7ddd9007ec8f1e7705f5a6d0a43f226a9e8d9207ec908db23f9e9f342da7abe667a495bfba2c5bf4d0b66f6dd0a1da953c9be968a2ec10e77e4cf793172fff9c8fca5f37ca3f27e2d9ac9447f37c4e4d5b29d8b6bf52fc7492f2d339e19b09ee964518dd9c8ff68b56fa819cb87d13795910fea175a8ee6f482b0a601644af6391733e600e7470b2a43deeca379c11822271571e52b856443cbd40e770b5582d168bcd620b187cf901bc5bbd608cbf468e53b527424e415f9f70c309621464ba44befc79764210b7bfb7e13cd91f1007a8719ad81f903e955c3e827c4f3e21a04fd781d355ffb60a9fb3f7f3a8fae7c3df14c8fe6055ef46e19a98938bf16eeee566394b368eff617fd21920dc4fd7713fe9cc0a34ff3a552fb7ea2fe756a533f4567d6f3efd90c6fdc1aadeede2723d42b3975d12dc8551f22577619494ee17f3e72e48ebfc2b44ebd4d69b0e1161dc1ff0cabf1b463d7f9056270f7d37da0b21ac3f1f7d3476f70d9d6ab1387a21e59ff63d50efdc39759d1e8d13b2965f939a34ad07086e9887421b06b3497743a2d520ff823c9b231ecdf3d9e582d8cbdc08341ecbfbabeb123aa247894f124e16885321e8cf376883360a0c51b83fe61402fef49bb74d3bb0658051cfe55624039cbabcfdf9558453d29f5fbcb18b700af3e73dd363cf8725d1a4bbaf41b68c40db0fc229e8cf7de0d4a6dc3f6f00a72e7ffe004e5dedb4605ff76bdc1f93721a8c5a8f02840c70aa06498253d56d0942c53ff701084e3def00a748fffc084eb95594a07fdec3368be382bce9abeb12f747a4707f2c7971c4717a364146b44ed1293bfdebc568dde957100f05b182bca49306f9b752dee9a0ca16039a9d0562795778b53fe96ec1a849570bc65cfa9cb4772ead9a97dc710ac6abf3d0140519f9d25af393e4d372180bc6e7a3a51eec4b90d69a972eaad8ca75f825caa395d6455a0c691e6acd4b251812e5d9a5169d8d5d57cb6de5ee2e85d26f98477ac86fb88b165db4c82276c9e76a010393451673ce2cb4e0d4246d16736ab1138b2cb4d82e3fbbb380df353fb7fd921aa7e2b5d8cbb691c74de497e6033f7af42ab275d329461ff8d335c8284dd3a6e6fc8637d3992ea29051d1e768734c13a9746d34da32cd1fcd434d4dbab5c13a5fcaea9af4e7daf4a945165a64b13fab0a35cc0faecb613030f0061898090343f29a1d12e3248ff176203b24e622c5d0aafa18276d93449a93e4d33bcb99372f66bbc15d967303adaa87f11b2639901de29ee49be5906048d36b26903785539334616080ccd2ac3ed41e7ed2baed454e252030d34f3a64ae912ab615908a5785a5d86ffaa830aae99bc2a8183af97aebf0f61a721ede333b72d04a7218779c8af1aaf1a03373075de5a032ee9cb3dc399bf6222d43abcbf2edece845abe6c3379dd36336c7a81ae3ed3ebd44b1597213ada7afee18351d669b81a291a84f3f4c895a9e6d3350348dd6d3a4190ce521c15824b774d0cc6b8866fd18e521398caf732604ccc6501f2601c15a8e72e4a03f3d70ae10f90d5fbc998ef4756cdb7756486e8e5171cb1cd3c9dc44eb84a175d289f96ef2aa3d20d3c418797ce4ec874c871f1b8936c8284c933527d4af872890fd792af8b3aa52c31021c648f74b8c74d58891ae9718e9a61123dd2e31d23dc31a45af168db4ee19a590bbcb49ae457f36998de6a512add897e82b95a8639a52a9e4585672e86e9b81a285680ff6a19163d449db0c14eda23eef433e724849be990ecf74f821ca33a275bfe42177ee9c44a275a3f0581e72e99c0911924c4b6e519e394b32c6c5b28cfe9870aeb8a647ef2df270d477cfadf6983efa9c8c9a1d674462faa63cd325ed33ce3883a492324a59f2dd92b7972867435cbb45bf5c56a255f525df2d3ab1677496136f5e692391e4ee9e21618c67702ae67494b2a14de6b50713456fd974adcdfd68b3fc7a7ed16ab23cdbb447f37c54453601cd45ce598eb639a699ae613ffdf9cd1b6d96bbec8790639ae8d9e698c6f26b6ed5f4d3e3361f36b790bb4c27e4336ed597ecc71ccd6d13d3dc9f938fde1bbd79b2b7333c6e5515abea63eccd31ca716b625caaf62c2ff36ef5022b7f3dacab40291e159eb20fe94a6154bb0ba21b85510d7dc78c12d02bd3ce7ed85f778e56e7a1c997cfcd31cd452bfccbb3cdc2feba1c86b619289aa33dd83bc7420edf511ecc7928d4df9487dd09e1e2e59c298ff4fe970dd12f1ddca73d59e2ab9893fe603857b484dda7ef7e0e88de2af64b963c24d837e5817432eaedb33f91e5fdddca09a0bf1a73d2300be2bda5daad6196b3bb51e656f3685ea475391be2925b7bcb6ee9ed9de5f4cd8b9b555552fa6639f2729c6af70ddd43df289cea1cb85bd30bbe4b628e6da637855ef36ce6e6c0a66e65d19e3f775d4ebe1d6ef4e645b839b13fa043ef9bd7aa6fb8b9553f511a5c70c6af0bdeada4688157756f35492412892469e3a9d1a7ff44918844b27c45a6cd8a2d33918b30376d557b95139289e49064f29b671239b6f18cdcfd9a34930973b8434c8e893092dfb0b6d59fe7c98618b9c96f78c78c125a9dd88a72d459ca2149988f4c94e4eb0ec2d2e9a1c79128dbb06d6e5826c4f5215aa3fb0cfb94b15d46e105555e00ef562e50e3af51b6f154e9d07f5a9248de2d1241d2acf02110d7a21592b6aaa99c582e22892c1791fce6919674b97b8b8489444e22b9652d695d9623a24bde677ec3d8567f9ce4372c721f7e4b444bee9cc8395189443317b913398b5ca3753e8b7c44abf62ca2cbfefcdae2fed8d0e626ad2f9f90b29262454a500bbe3c9077ab165c79efbd6ceeee67cb5d6ef8adcb7e50fd845f1dcd9cef73becff93ee7fbdc8779daddbb4bca4c7af616023073e72e8b5996cdccb34ceaf0485a37948564c859fa484a4d935996d1aabdccdc722df351e6a22da315a3551592270d4e38572dc0a20551dcf800bc5bb1208c2b6f80772b1648799612ba6db7aaca74f6ebd20ab7aa3136e774b86e3f6e3c4b9cfc74f72e0be2ed92f7cd5d2a7ce775296c6cb31ce92ec318e54c8c722311e68ffaa36167cea83f1bcd79d27aca7cba68738e66d2ea3f2d1f9d7e4a76349256cd726d738e664246391f6da22ddbae4b47529e90e575529ee52c88f54c88eb9bd67578fa69d5c571853586412ba9a087d3b989539a8f7ca7bc5b2ad615b7e7bd591eda76d4a36dbab6993cb4adcbcdf2b8bd5c20bf3258b162c58a152bbb56fa81f120eca1a6c607ce82b05f30be1eee3ca05012839ac68351ec2f0b0de1d47331460d615a0dddea65216e4f4e7d75ceb9f7424798fcb60af7f9ee7b6f724a8a1876796f08fd5ebfa02140481f75af8acbf588509a18a569d04b5268ebeedd2a0aa0e7afa7e720b526880f3d9c6c186213737ec919a5c8391fefbd1ca85c2006126cd0461950a84215399ca2706b98e1e505523c49630c577080c210a230dfbbd514569071f16c868d02c80f3ee868da2ac1ec8eee1c0af6c2c6b4f95e8cf4075545f4980d01bf695873810665c7e60269350515d7a3f68e6683e6ed0f63943bf71ecf89e800db8ca4f0c029b7b2c1977fda0b033aeded98692f3c866b724a736f1f3be77c71dc7a51a4190acfe1c4486b68d59feba9078a87e72d966acfa1578d67a986d5f9a0a645bf7ad39c937f3e37f73c9fcde2f0a3354d5c4e7bedaf9fd7c9a8f7cfe1d3364a84103e6747cd1acdb9d349d3ea5c97f482eb707d5627a18c913281dfd427fe3af4e279d520adccfe2c440ecfc39136ad1aa4f5041d8df3f7f8bd5ea681f3c9a8754264f6f474382724669bef9c4df7a63d1ae739da14616469922c2d34ba44589661a26b14d22cd22c499868ea1c30e6e9e07db7edcb6032a71d229882eff175c63539b5836de4c4c6989cdaf16f93f9eb616f3bbd8ed8d8db623ec7cbad1d0dcc1efa36995910127b9be9af89bd0de62f0b7b5be9af10f636d25f17f636ed2f0c7bdbe82f91e82f11f6b6ecaf11f636ec2f0d7bdbf517097b5be82f184c6e0ee76dd65f30d8dbe65f26ec6df2af1cd8db62ffa5037b1bfceb84bdedfd2583bd8dffe2a123268709a644d246a20cbb4296cccc0e11743ea0bb87286898e53c274cdf176e20517142b43cb7058dc5d90663696d30feb516197c45c03faf0878f7dcf6ae004c2bcdd74972da1ab4da5d1a0d67e35c777142f61495d0d8683438274515052fa1845b6b09f2a45fd6c1e5092f371370e21a5af4c5712e15c63faf1d06180ef5bc3618ef60fe798579e7243784e47e3cd7f2cee7b369779a165fb5875534bbe67c3c8f9ba9b5381fcf7b9bbcc9ee8e910ab62949692d62c718bbb770aacee6d263f4191fadc01a50c7710dedb9fbda5c2043b854ae9cb850c1a20a0c04a2a2852a54d680cb5538557b8b159caa920a20b497f7ded26e689547171e5d9e8dc3792e0469f57ec9d6f90c860e2a3aa84c609faf168d9df2e8c2e59fd74bcbff40ad381f8f47978c5485b422ad4857485e486eb04d8e2aa42c51e8a0421a838485c485d485a406498b0e2a9caa3cbae8a8c2a94aaaa2430a1d544c5295ded25b7874e18186b3b1fc398f2e4ec84e97d1d949559e8d4579a0e1689ed31b94d0e21fad3cd8b012290f34fc2355f91d95e4c53fafbde57974d942ab3aa8fcf3251bc7f585f3f17874f9079d47174ef53fe791861bfffc062a3750c1360df49c871a3448e31fe98627ff62bef2f0f2ef86a07f5c64f082af3748f927450656bede20c53f1e5ffe41d24a0b164ed5eb0bd2154eddf08fc75752967fbb69f9423a21eccf296357b896ca067d51f268b868792fc8a3418578a0adefcf326b714d4ee938672d64d4baf3f1689decf5f9f5de53f1cff25ee17a7ebded87cc9986468b16146a4712587f963539d361d30533995faf32effc65ecd7c8fd6612708fa6ba153fe71fae1fae67cf69a273fda3cef9d0b99ee9cb74de0feb7cacaff3e11c663a9088271a13b3339d8e994ee427b499e9a06abff3b716a374d82d8baaea2b1ce558bf5ead6c02ebeed1bccdbd15c2f65ac7a299e958969c2111ef5f23f72efbc1391fce07afcce2723d2a898b4b5d0ec55e3bcb3bef28b8947b38168e8310fa0d47a065f243f4769d7d19631084f065f0871885f660545c6689cdf7dec42c0821b4b050777787b02bc61863bc302c5bbfa4a0d5ab4b033510b4025af1b43c2d5fbe7015aeb2589c17e74566399b45aba7af31c8ea784d9196e9fcb0e5f90d57f168c4649603bd19b524c7a85f9fed37ec54efe8d585093f13fe97e9e8c4156ccec7ba275a9d18e47cb02f75e7630dd71727c4d528e5daf2ecd735c6b3b3180ec5ce2bf67a7d89521e0d073d9aa8865663d073975fafd797277179a4335ced570c16adba2fab249e8d96436327a2034784e1ef245a4f4dab565d1a96ef867cb39c905b9665410b62385738dfcdf9a5b9ad9e7ef7bd4dba95cf6f7837e9eef90d275104deac4081f3d1bb99c0fd30811bc24afcb00fa9f8f650956fb7a4582a533a2809e7a3b12d34e84ecb17ac09ebad9a3f4f15dffc725d238701b1bf80d5d91f01b19051988443bea1c1bdb8ac4f9579048d7b2e2e90512db5cc2cb48d9be56c36c4737e4cad26b4d5b46543abf389708f66ca885910c2ee8edddd31084a44c51310524a1eceed1367eed8b163afe320524a1fde3133333b8805792b1e18e5de63ae9199ce5ea3f9d8e76467824c7999979779e5bce6ac26b90b29c7759e96150af5f0ceaf0bc3b20ce34dd4ce47080b8d424a704030ca31bb2cbc3c7d209c9a735ab0a0551fdef9e5a6cba0f3716540ec638c728e6118c6cccc0c99c2d1f32cfb39e15cc19e65394c2d28dad28a8946da88a495b6344718863114add6bc7315fbc7fb4455f5aea39c9995852ef8325e91e86755fc6cd4b85c8f3abd3cf46b179b9a763ab9d704598a2541ab6ec55e7975f36cdc53807b70b1dc30ce5af168dacbb30939745e3d1b25fce5d9600ee7cab93ea0adf268a0ef9667b3563c1ae85bde56e937b42d551e7a5c8356ddea9d13b27e124303e36114b6fda9a56a1bff0ea765cb9ae168a08b619e618d38a0a0dca1368a82763dc1f980d6c65cb81fd0af7542907842dc10cb9dbfec078b085a7561e8ec13c1384b368eb7281603edb9733eeabc20b666389b9b15c3c33dc3c35de3e1c6f190a13cd4f268a087f8cb43c7b6a0877e33e1e51aad3e725176c202f32d2e5a7975a5f266e195f3011d4382f69cd72baf1eba923935cd8296579f5a24adda8b53cbc3a63ebdc01fa882d995cbf5a851ca94710ea117edc6817836d17778e7729b9b7b346ad0a24b7727e4376783e3c40862850238293cf44a73e37c40bf614b091a0fb772ab18747372ab9b062871c236252d6e25c5196e0c87c571715d9c1a4e8b73aa9e78f4e0e1c4234a79a703c32a38873d9b27164e138e73c57a9d57b0576d3a6fd66e37734fec89f3019d09e70302457140a42dcf2654495f1e7aa92175a9242e0fd9a5ab7096e98c04a24baf4074499d5cc1ee447adc75a615881ef2107512f24855384cab02b040961e72583020ecc994876e4979c682b0280fad78e8eb355a11dd39af793618841edab60b035a87eb4b44a4376cadcf8dce32684034ad93085ab5c78038f445db67ed35270453c2f9800e9de46c30152c0d7488cd00f582871573e2a1579987be1906e47c40b794a0618c825eb1f5b8cb4ec2029dd73120b8eeb25eaf18d0439fe966c780a0634fa06356b02dd8c6041d8b12e52116291e06557968c5432c1e62521e7af302c1a93a89e054c51ec0a999872e8c87137a09887f58c5f0d2c2e57ad4d26a2db7ec8515c87899bf1acb020c65414acc1b0b164356d8a08d12b28256b115a68553cedb312e9c9aee9ef9afc5b015a736f7d8154e89bc1d0b03dbf2ed6cb02b4cd35eb119a513e9ecf04bb115a3bcd0da9fc8615fb115b6aad8aa0bade7053177edd8b1452b51793697d79216252b4a4a567cd752150fb90a272e9d9180e521af80e521eae48a7527a148ab02b47cf4a515b0fcf28b3ab9dca22a9ca55501583e4aace7b714038e775d8881e5799b8974c9c6f1cf7f7ceab5293aa8f80bae834fb4e49d94f0d7c318e8a3ef8a07553e521ffe25f29b40de4a57f8ab31de3030fc1531de4a63a0e12f89f1665ae26326fd9a186f2119fc35656cf8789d3543a225a38f2e37ec4af6bbb92ec8c0c1efd8d18616f2cb2bb6da206c855dc1b08cf1150be3dbdfa625cbb7efc082c60446afb1cb041c8ee54a9e0db6c2a9f3dbf48df5d2aafdb585b60b5b79f9e8fd05cb7704d2f24458b4b9d01e8cea8f34b486b66f79c86bcd1321012d4e8b57ab9d79a87142302e9c8ff6f602381bcc094bd3deb4ca7cc556a7a515b3c2b757d22f4da435f4d5fa5a83ad9c8f764b091a8a51eda1adb93c9ae8d6e61e4ded2e1fd9a0bdf4529013b2df3e6a6bf9e8d8ead9389cee2d8f26bacbe2d68351ed73f5ed33734ac7569cea6fc7ae70caad6a00846fc7bc606eb4635938e55636c8f25db131be312c5cbe5f0db8f88a75f9ce806a50c6574c8d6f9c8a69f9f629e30671aaf690c2a98aa2c2a99929a91d5f570ae8ed284eb1b7f3505a3d157ca5298286c07ed934c9c4a899e8ae8b2a563c9c89f4872e7030e5dbd97970caad62f0e4d981a800dbc45861efa1460ff2ab2f4e0df2046a9912c513d10a08080808e70ab78ac1135f5d96679c8ad241a3caa613062b9b4e18b0487114523ccb3cbf0e1a54306afb2a821ce7511e4cc37ec3a727b4755c1e83a1c6ddd434d3890784703601058a2c5f5cd1832514210e1c787a08af3ca1e221ad73427ee8cc30e30554f8c2c60fa0a44187a3a66cf1d079e0d4d6ecf06e35258a294190892b4b74b9a282386a9c76f21e281ea419a643ea3d1865a561d19e9a8710c2e90e5e5c1ce53d5c73a804846b78780507647868519781e6de4f26cdbd078a0707ad294a58ef110511a2c8c157af8953505181030d5c9ac08427c6a041ccb36b39927099010a52a020021ba4800626704ccf6b2842501c4c7c9104a03560948065214217be0c010657384309d382129270fa6bd4b1a5b7c309e194c20b66808083248864a0c1772b1c6491a901074070160e72e0840e37e1bc0207321859a18ddead701064d12a6d4061cad6d0b0338ca468d817ced0b42b88b8683ab6c8daa822041b549102091af66e55458a11b4ebddaa0a136668d917086f5086874fe040e8ee8484bec74ed120a427910fb41217445c683058bad0648430ca8236ab804333bd5bdd00064137684214042d634354052de60b2325b41cef565354e1d747514cc1c59fe4e6ceb07f9a82080f213cfd0ddfb07cb79ae2077fbd5b4d9145088acb7965666606b0718dd87372c0e1061e333b644e3a627298604a246d24cab02b644d191b3e5ee7c3c5f1553cbf61f7a873343fab626632dc7bcf9756e7f03de7edcfbdf79ef3f7de838ec2e1ced9d739e79c5bf75270396733a77c34ce7f52d0f9ea5cce0961afee72fbced79d0fe7aadd062c95ed90bbe1e6ba3da62075ec91c2c7b11d13f890c6651fb0c309dd41778c2a81a1b143e89c63770d61437e4940c1af37840e9274ba4c61144307218410428f5a19ca48c51a638cf30a97a3d0a75fbddece4d27a338b4d2d8314e3d77ee4a6068f0d7d979d621a31cdc2de3d2a183feec93112906cb6182295d215126351ed494683d392a07ad35989218d2489449fae3c64b08e4f0245f2bcf268715d35a592beedc19245a5552688df94a79366b6544eb2994518cd60e58b1b256d64a94d2c4561bdd9c8a514c878a8e7d744213614c62622f0a1cd76cec04cde5a15f720d5a9d13703837dc5c9e1d724f7399417b99a2add7f6d25e9a4b73797845416a6e770a89de6e39211616c593266a70a63340fb93e94d384e28f413daa23340cfef1f7ee89765d1fe86578431a49ca894ea562a843dcc39a6d08c6004000009531500304018128a4524424d17750f1480118aa64c62529887b32887510a21638c01001042000004064468060d005ff9116c4f27b09d0ad933b01a8b40c70a8144530f025bc3986ac94b0c63b5e66d540a02eaee8d39f174c33d135858070a81b591accc5f05fae8dd37dc0e6a0a5b6839d1bf786c4678dac37cc4e002853804a541e38152c6103fcb376cce6ba0f4f59c4c6f03c062dcceb0207af9a3c81cadbbd2a1087743fa36441e9f4b5fa0e0f2960fc8b48b6ebd1109edfce22a2e077b16f221684f287e1006106b2327a54db4f062beab16012e9f1b5f201cd35bb49101c0da521ac65add23b6be00bf106a1e4f9cb377474a614d7ad147f469b54d38a06ca71be6314e51011b9b334a9cccc2b2851e555e0f81283b1e89a20f4b8541de1449323e668d0396829ce2dbd10056927aae828993f4a7125e44d453fddd0f91954b52501413648e358f7176f24dbcb66df0da146c6aadf484eb21afe02c2c20b587aa9081faf1bb2fcb0cd743374e9b0ed14039e838407f5e8503b28a4c87efe8892a5db54393644afa077d3e5c39218af2a76e2ed486683f0115a2cbdb12cce0dc8b85a415df4b1f84f4e620a8bee947d9682baf8818407a1d5a7d2ac3cfd0395d6648666731789861549d4e51c86c3929888c0363798341e586e491f45050773eddaae96176106675845bdadecf317d82cac92716dea34cc9cfe5f6ba31747d5877de65bd5ccc899b2da73b6862d72a5544410464c52e4d4f5d7506b85d5af7b4bdd88ee7e18f455f11b7c0e722e7334bcfc85bc5905efe2ae2a0bd18ae49f19f1b613a3b445c77b2776d2dd2e0a5799d816d5a92111029b5493ba14e3719fdcd737fe8ffa672ae8fabb024917a330234be65bfb3ac755f5f489a77a7abf405f0fdc6394b96c033482d6b08731335b33480d0976cda8c8ae5ab8149cc97a07d5199656fa2efe03462e6e44a02053713937dc2d21602051693dbbc23e3d0e04bfeafdd498b192fe5e0d35a0508ff2618a7070e7a21feda028df13c29d865f9d83fe9ca90221f8a01605e2a78b6a2a6ea9d92c8e852137575883f0bb9558127ded1cda67e239da08c1e4fb83d56b40c165bb8618991108bfce6d202c20102582c43cc8b232011c70441d06003c9873bc3f41bc95bc955cb798643960003d65544c8593c3cf29c717b61297cdc8d1fda4b508f84d5f4f1822fd797e816c188c43c0015d6210a88bcbdfda547282d3a06c2040936d859929a4510ec128e11039b590f4807ce832c6c231cc3b924f6f612c43e8d894e1e911a7e90b839c55a39f9564c4701da5e1be5f890d16e4a35d85be8516bd6f083c1f5e503f91137c424c3ba7d6124ef4b2fd2efc473506239557fee747033b893c66f442b7d920a2c6ab774b107443ee034c839506b3622fffa2983ad904bba6bfcce9613b941ff1437fa6699adb0ff0b70fc52c6e56584896ddf7030c3288ad026266e0084d4b71838a8b21278384c4fdd87dd32d30d42956556635f94207c2eaaeacf7f186c987e223e29f41dc7a562b6b1e0d392854d5f7967d8f750e72b0c0ed89a697a8562edbde903a96bac37b4dd10e17e76c599cb9a43bae333065df0f43b5221a418b1473e17aa3810147890b993a2b078f3d34156aa7587baae867e29ac254cd52a2429bdb2688b47014eccf0c6fbb86328b08933136d76e5c69ca3a23455e554304b8549aa4d508d846aa55429a58a49aa4d526d864a645429a75a39d54c5261924a13542013120ea688f6d093b21e0c02454611febdb8dcf7682cc76730fcd3af8776d387ae78bf3ab15985b14c894c7a9d7e682d89b2d01367a68f5ee5b25152ab8e15bb7d6e634862bca552f60ce655adf3fa634a8d1679c7a38920e33da2bbeef2b702013f74c87754e620e895c67a0b7165bb464b675290ae3bc73077c20f77373b0e34e43e5202b776bef3546233d4bd6cc1ea42e65a5a23b481dab7dd21e4204d29d4c6ae07751207d529d96aa6f834cc58b0d3d7ba5fbe090b3b67857d3ec20af293ef257d20da4a4e2040c9cbf17deabe4fd4723889c34348f47641a3169e680df13a10534910babf0b5bb2645bb476c029522798def4337083fddf1ecf990d0692c7251e0ca3c8afa0a24f441ffe9b84d2a6c8cf3f2b663fd6a77b1d7601847bcca75f4a2bbfe5fe3a04607ea46deb93cd93f6c67415333f8ca5bd43c243f2f5a7306f09afc2228c8eae48b35b27c5dba4ed94b9427b4aafacbd4d359bfe43b5a2906278c435ad18700fa9aece4262df92efd8028134a61bdc95d236ceb876bdbe05b6c2c75e4b5c4858020f80d58b69e78ce33d81da3ac4b74f1e55dc3ac228f30c18a2089f5d8f9cfe62f81ffc357dd50e2e07e14fdca8f1904639555280ae1295caa826c4b0a2153f3a558b003b21b7634f72f453065f88706cd2a0b8316b7341a81893084b057e6802e42c009b86a05e8a07b420bdd6269c7abe11c73f60494645bfd7545b3c613ac3b99342741826bcd432589942e07f6bdc69eb222a571940c2a112cc981f51fe178337e2217c72c1f0ee88086e5304810009507c940d753e6efad7cfceec74b5d44a48710afdb37a42244e935b9f953c9a6330e8edc8a55d3d7cbe21bb5acee8cd380e7a5a40232de79ed7b8a20ed2ce8aa5ba1423ecb0d3c803a09d2be454867060617918af775e530cc3e23aaab485167cd467d37260af770250b9253aa4a595ef25fef6e2d9824b7fdd761a4a6f6e156fa9911d68864b54a0cff06ade8933eec39768e4863a1d3efec68b62edb9b31af0e490af838131e87f3a89cac596809834d55e9dc07a9a15fba5d383c9fd572fc7f632b5766018f9453e735986e6ab71fec86101469cce55af3cded7e37b712c41b087e4d448d6215b80270a6188984465c418534de20f661a0b5d867ebf31b0a41bad68ad31369132158c0cfb3968ab2a2694adb8a85012902307e93b8c2ad89564b863b527b802eeb0e75e95905bdb1404c8e3737e72e111a2707d347e10a43a99a8e1fb175598ab75358b6dd16f3b01c2e6924cb9f0ee3e6d4c13c63680c7f0a67b885016fc23aac541648ff9a85293370db3a0dce1067617fb7afbd03c216ed4661264daafbfe2cfed2a7f44457b4b7d296add1ceeaddef46441280836f1a024e6853a56df5dc1ae1b80316ab9d701aff0dd7569a24e5ba4d2e01f892f4a7925949239df3a53a32097fd98132283db01f2e07412d74306a536997a177f5c2416914690faf2f782337a328f02a558897b0005fd1612ff3b65001eaabebb6e4a61f99e4acf57b38867cf59159cc3ceb13955730a2a761f8a14946753dafb3ac8e7a179272e887550cd6bec7d5c11f36007b63ca6dec405b10ee680036ffedfdee212f10776e001ee0005db9c99e74fa75330ac6395be58625fed69f21820723f726176adaa62236513f7b64ee19c2134a7bc2f995205a4d29333178148695416da4d64de0ace4db85c5c73ddc2e25895cb4d92163d8fd263add83ba797a913833090d0857b1f13609d2dd227b1cbe5e3d67a5e3b834d62744bfad6a19c5155ef832e9955a770e316b3ec39e3f55b4c0c58cdc84b380e2e3785b14ae7cf866e16679ad03275ae8057badc2ee1c5e7ae7803e74790f5af624098653b6687066bef2493156a7becf732421895a47411085bfd39e6cd5a801e4dc141dc28adfca8f01557926e7633893b989056df0228ee82dd5290506f9ca066ccb4d9e25f3768b2ea59c7520c9d2d27dfc0fb8f4a76c39648ca09a226e85cb24552e525aa86ec77e5c81615d0f023bb85d76657262925100c40ceb593ee6edb907877a2d3e1417b7db0bb82e4551472a0158ca7bbbef13bba7210b2e933bdc8aa5059c13792eef0c14614fcbf13b4dd4889ac09bae75da32e2fb2252b05dfab9c8692c60d53803c15a2012495b42d9c90d65ecc17c54963025367714d858b4034b48de3961eee4fe8bc4503e074046393315f3aa2e26c3a2334d3cc8ce7702ae27c752a340f39a1b00b121ebffdcb0f59646d50cb353c3e172176a0cbae44886ccab9bc532ad51f77ce6c11f5c0ce312bfccfcd3c25a6f8ee397b0866dae8799b67d8d967abcd37ebe6032192d3263b12cd9275b5246c754e761d97928a0c8e144bdd0b697f600dfb210850168ca63cd7f641d43eb9c123527d6801f7f68f502fe89efae08c5cae05b9a6da1db2b68b098efa4dfae779e25f313f5d09b628874252665506b46a459ebc482982ebd67b06f82d14e027ddedd67ecdf8beea3823b29bf9d5fb81c22d1655bfabecef7963b5fc9c33363b90672aaebb8b3b3cdf129d5b8423b5c31b3980aadb23c3db3545902f8b9d25f41c2492a02517a4ed6d60f6a552d280b5fa9762a557fa9ea4e6a00ea80e5cf6e7d667013b6e42ec27b3396ff6d6e4e7ddc9fd59d7435cd9e7670a3ac4d5602e49312d92563ab03b231123958b61f54deb206ca5ee689aa9bdacf676f4958ec5641f1ef9eefd38e7fa8d2f864d54e4df8c3c1972d8dfadd1a2b2241026afee4db62523c02445a61bb72e50d4f702eaf0d9753a7b4d999bb03e79c61f38f182fbcd4d21d6e51368130438549a1242acc1f950445a3a5b618daadf2cfef20c101054d3b137979632483a2878158bce5aee07d1dca4a0c131398bad83b6f2dc1e307ea3f11869bb86ab3dd7b9270f570978cfa6956c5ebf06567762fcea9d14ee6d5f84d0050ffb555d1c0f46be44d7c20807f6f0ff872f904ed7a8f272ba42702bf6ffe0699814d219d8e6785716c26187b15f483f051dea488534f2deb809a6c8bd399cc1c0b21520c0a66f0e5e1ac9e664fa21aa4541576546f46fd87d260d8e5af1b7ff06c3a545b48045601701d24bd5581ed06d703c6ca9a6ec3e7070100d8c9fe5585a26656598420c2342e0d9dfde2500a548bce0b92bcdc2642afd8f47ca190f8519ba749c44d0d8c946b99dabc0e10311b7a03a2229ae8a1faab9af3c5c682ca45af7fe2704b7ff124f8be06aecf61a88574f2a08a624c2fb9562c932e52df62fe6ba05366b53e934c833e438fdcafb9ad3ba96caf53c43085265279ae71ab2262a233de2905866905ed10eac38b4f2a24a727579d889a12753ef8b004db33a5e0b57aac0d0c4f1990b4d98ddb3aba021c183eadeddebe33ed4acd95d2754cb00522e9c665e546d226ea2d71467bd41ae0ca4b63dae81cc592eac1a522c27ee1346c6a07026244b19c1d47732ee0ee70bb016dedba11f99b0ab9fd95df4fbe8548a92579a183b9a5523950e239d4a1fb6a356e1db741f314344f6c1a059eee865ecc4c3ebd142a911cad24373be3db40a2426dabd203e36089164a060b3a3ca205d74976a93acac6ef13c8c27b45fb0addc2069690a0dc491544aafc57636c01f3e304db3e03e65d39a59067eb32505bab1930173ff2ec6322b26e14167c066b6f6737d71403a68361f7c5bbf66a408522427d780e554ff83b0d9dc397ca5fe6a0321780baf953f11b301c2e99990843a43f9a1c9a306013ada5c85d0bce338a8992ae096cd1372110c5d8fc5ae6e28cf99cdfd438e3072b7f0abd11e231cc02dafb2c9e4c7c444ae1a6b5c261b40762bc0308f9ef915ca15f296633d659d7f38e36f88b7a132ca8e040c71028831350f7fa28ed7938b541d87e2d31d6fa2439b8481ee1d0286806a5e7be4c45d19cf3b406b68d8ebfc93159024deb509c839127c0e20b0c4011ef86682de15dceeb2f982738ea37a0e858da23e3a0de9b44ea77ed4e94fbb4f9b0cf5632c7134cbe09201b20e9da02750b67a90eb360fe007ecdca17187e6ed7e595e043a7d758851dc2b7505e925360e3ab105179be05213177003bad946c44ad915da6eb9b6ce3a6439c6b776203c878a0aebe9e36bbfddf38de304a24677a7010c848b76f8010d7b1047d6fc80ce4b3333babde7a4fbac6ddb708d46e29c1fc7495386ddf61325d4a4e4e7376595704c40af12d2f22368173f8187934b18ae971b4102b412cbfa63947dc1ed8d984489784aa44bb29b9c9268fffce105b7a2c9d3ce7a05f5179cc227ee1fc50916e5fe32c27b8a0393620134b737ace699cfb29a234476dc9c19f006160ceda0cfb5c8e276a24d35271cf077f2ac1be4fb58acee813d50bc23dcd096b5db8741f6614ac2b56d60b31c966428bdd40424f694774a8a803cc0f8142dd18634207e9abb8dc18a80f407cb0b743028200fa47766ebcaa59c463ae2a5adec0421c4b08cb589ab9b8910a27f0fe12a0c1e2e2008fde3c0bf325b6e554dd8d6a212dfba83fb2389654550049075660e82b9897f533d9acb4443f1b461a797748816b7d96effe3d54af8eb59cea658d76b2cce92232c5e910a6a108aa35fb5e205a462f95a7ea0a863b94bcfba80d7748d715c714d4a9d791a120f8f14c540f571127d04a95b85c3c0fd4b32d1dca0ede44a7b942a8d4c1d6f6aebd90db638fda21d3b053e064f6a89a4b106347471bb2548338ee8951c2ecd5b9a35e826540e444f3caae7e36f20bc95345c0fe7025120481c3da21f2b5cbacb0aa2fa2f6ada1b772f4cf6c5dca59f069da8b1900d986a363ea613dfae065ca6821c8e7cf15321c322aeae4c5358e058ffe78bc4be88fb639b08d8d3249d924c4811a7e1a25897e6d0b58e7ec1caec7f942ece9f304525ee87a46eab34c4a620e88c34e8ac13f32807efec6a63de8c7216d9cb2d7b3ac9a07b288e5cc4b31028b2f0a07c630af095476abcd911885f2334069a0e3f27290a0b3fec0aa715fe65543fad0560aa219781aa76babd57d73df4c044012cbde74a9d60d98676169753bb33d31d82df5d73d4b034aa15bafa9f878998217fa5955d67ead3f07bf83e710ad7c4517e33f985fd46a9ba19ffd5bfb7473839333d961e313436806c9668f16ba12420e2261cf0b4137320a2a08f6a5ed9b7d73c38f7bc047668cdb8338b2d0f6918de0b3db6cb6625678ecb1ca7dc8c91618b55472490f182c82ccbb5357bfe9ef04e0c0db34ed63dff4d8ef80ab9f984dedbec3a0a2273d6b740cbd0c6589ffa4f8d3ecc122ced1bf94fb4ce9723397cdab0f1f85f2d406d0e8f75aec2ba09adca3040d782d75ddc3c04952f44f0d3ea07c9b5a6659bbb440ab1144bcfd9116a65260321e0ed008ef964bce2619e7069ceb697f8fb97681b3ed9e4b112d7761806bd3db03dc77fdd92d7623e143a802bfc89b5d98a8ee50a9cf4a1a3af32ea0466552bf3094dfd8a4eba2ceadc78189e25158fc84250f7439b8e0fa51e9cbd0c95d9f1cdf857cde473f636215b20a56dd1c612b7d36209861e1a71394dfea46c42370222ed35e4e851838546707512edb863bfc0e1fe252d0aa103e62e15330b9ee815f1e09c9e3360994a45f01437dcd3d6353c8639b53b150bd0f4e19e10d08f97b9f19d2b301a2607713c85f90e636e6ed73db1f8eb79bb9e245dd5ed666daf437a184da6ea13fccb49145f49c662686d158a4a5917505f27e79fe9adf58106efe0efb7ddbb9da616e2d98bdc2ca344273815cece6a2384a50a4ee85bc1dcf7715f77ec4593347d54900cea9272935edd02a553fd422442eaf1498e90d0fdaf0423d1b53df573154b44ce174c0d23ac086075441ebbd91eb27fc7ee0276aa9f4b76e1316a8c2474c8035dc249c3887ce3bfe5e2d15c5610bbce33991c3226b1660d13208951dffa1341d983bbaf8877c8aa8be38e5e2c3dc77241b775fda4b88437108440ca12cd98bb3bfc40f74edd67d65ffcc1cf762b7dc9eb5986fba01bc057e4057b69663066f5202a978ee924bef410029a328c826e1b1bef8cc92c801f1bd89f8e5b2090e556428f286b764048caf811204f8eb1de81d92b801977c420d149f4313bc4e8109c26ba4a8d1809bd62749181a4f9d2e1a97699164883a653e7629e7f172a576e571da1af5ed8fee532bc68f992ec236d00cee6ac9696c84c11b1e6bf09388c363e285d0d0d4a976a32962a3de3215231d416574ad2b0499ee67c8010d4f7243dc6f830e1c92d552452bdb91fc0968b49870f90f6483109db35afeba8fd398a8a4711c1792bab1d6ea88580f634a93be92e859b1224ce4e1c3231ea4531e29c005e7de15ac5272848c34fa191148f19be7e1c47e207ad10ae4548b077127a20b2df52a4eb2efc2b4ef9587f8b58e481880856fd1764f012e1e65fd19143213802a518077ad3ec9e8e51f83c6906fc7e8c1ec95fd845933a645209867221e936a96776250e7d508896d03d6b142a243828304196b2f39bcb3a6cd97ca12f0b2b8018cdd35b7b4b27f18b9849bb5663b51dd0a1ad23f805b23e832abb360630cb61f6d885ac173051fe4cb262fd8f0150b4791c6ca3061279dada114ed159eb57d7963b1959a1604b7ef710516592616568a1e5d619c54687b70f37bd18b4d8e05c017d4b162fd838e27ac7b96586b19dbed0ddd8cc3dee620cbcd95356578dde83c623262e08d4ad6bef004422f15215b11e5c50a4db1d1b6c2d4419ffabf96f35534483904a730e8d06ef92e77a349963c4bde9a4c65ce7fee5d3fe0f6023ea4bdd7ecbc07885577b9c0016003d0bf9af2ac3fffdeed79346afb73a68dc9052436a9e82776e8b1a7407337e16de8d7a0a545bad4f2df33af3a1dce9638c13de89c619e506645525d865c66d3db340f2ed7addfafff0a67014e5b20cba81bcd232637b4c65bff61ecf311b1c9f0657ab9a293e67acb50a36fc291a79c2bc55fef8e5967bcd833b8d432cb650ff5198ec2b53962b30385e02396835848d8df8c4263f937a80aebddf80482bdf5d2f71d2acfc02b8ba7dface7f75b3730a2a908e6124b1dae026073348ff84ad107be2bde15a198bb5128eab7181d45aad0c41dfd4b9b9b75a5553005b44d2416b6f5143ce0c1cd721ac370267c63900c5f52fe07511d11637d05ea85be6d9626f1c7216e3239e57cd106032452de4e140c6cea9de46a5a61ea2b7a82d240e346dd11afe271f709e87adf25899a86fa7158e270d68a2af81c01d8760d092c3a7d96c59b4a191658117247eadc14796ca3d921f94ff833a303156fb0079521a4823439a1f732b0a86774e30e654cb0b31330708207d3d75f374124c823e498041ae8345065230d97cb4cbbf8672b25d7254089af454555a476974f97cb390ee620a46a09039ad16d4a6fed38e92b748fc9f4dd9572bcc92408b5637f8860689053edc08c337ae14be910afa3e927b0f5fc41f2c04b67edd4624b2650da212b8b3f10e23ea8bfeb81517704e8e49734ce1c1032e7b481a11d0222d80632f3f5839903d8a94f53102b957a73224dfff7bada878dcb196227ce7d531fe29a38b9b52eda55530afe72f51d86a0601a5c70a4c54c1e09200dafc2203aa879fdcb508e9a1f28343b6ab2c2baec9ae6b871d09bccc958ba4d08784dca01533f0d02a2b5e1b017a758667abab52407a3fffbdc68f5fb4c0fe2c3a391607b307519828c0e91f6dbbd087609e4433cd5b4b875d1eb3b7a5f67c035e4eaf00e476c67340b40a1e063c20230daaf278946d539de8238d4d56979c62908530163ee4e5adca97be748517ee2bd6e0390a1f9628c14fa382ebabaa24977d1abe5c285a2a6145d49421d63f987e8afdc1ac91747e82e491dd12380a4d7e58da6619e753cfd125be41ac867184764a469070c61614c7b435fbbf1447be1967d04235a02c99f650201290919736b895e356e46d1b4ffb2bb82507e88c1d53495a456a85a39b02aa27866844712e1cc4dff000270a54288a29be5cda3866f775b554f4fa4692ad33fbb1931316898283a84bf99efa777146d0b7f02ccc0dbff89aae5050e0177114f669004187845c06bb57001b445075843b9f61194f2aebaf81a24781b9420ff4ceca49d2e7ab17e821bb2c918115d97832296f883aa8cb2df5ae1d1de62c8f5dbbc62f47fd9ebfe2236d38cf2b1891a135bd648ae883d4215b73b4af050486c9de331a9e1adac6460304e1ccfe64fe76b83d0a28810f7c88f3033c6259c06db782c8b21df98f0b2458b012ca7a54a75a48b0a6c5a248404f5ea01446e229258de3168cda004b2704930021e7a603b713885a665f9367454ba1c61cd7f1c965c376d7ce7ed3c24804429cc007c4740fe4a9cfa2717afa4db2f7160740b4b5ddf522f3f7ae0c62c09dfe2dde057068df2547c975481b7e7709b6c7a0fa54857382976e937eea04821a135153fd9b2e20134d43552d0fb731598c74f867622b35e16acf3968facc8309f8005731c805fcb73eb311e4de2a9be32c1edeccadd78679d87b327d237b6d5da55ea5bd6a621a7f9bd95a80f940d8b3d7cbc9caf312494b6a0d0af8137832f3ff04abd4a8601b2fed2db97d8521d3a7dcafc2424cb44973bc14f001d839baaec45fa94deae671677687a62307272b291c502de5fbd21e8d6f26c077d01d4ef89140c513e8858a2ac86440e7f3658c5047413f40a49fde606a21f090b7324eaa5b5c5fed28716259c60c8c161e0be34da64b7804234890aba854683c3e0706c5c265454a0d8a08187805d848239341f76f04d9e7750722a193f8d382c49f0a258dc95736635ff1790fdc26ee85dc6a400c7f84d55572d6b9e4ac5e8e40ec1eb8cd1bed8cd622d7bf022642a50a094876b7cef8bba029b12549f607f2555a4d0139e53fc999d695dad98bf9e1894550ac1f2ea0aadc829d74c9cdfc3ee43dccea8922c09a0667ddc75438e994a94590e8dcfca287ca70c6238c13ad9cbab5653f44c4ae239f92c3fea2c12fa7d9745e31d73a37048423c2f8911ec5d4c20718786675ff9ac3cf2135d91253773627e99cd1cb54243a0df0c5139e716542b886facc2b2a91651dac387d482b8b746950a6b74fc432d7880a96ebc958ec88f5444158c87f00b6164ce0991f4dd4628c33d1e573303648a18b413af622ac2fb3fee37a1aedd889b9553ef086108a2fac54a3514631f5e1578e923eea35e3d4b17d92dabcae8862ce6ab9f394a8a249922a3b0be899922b3d0d07eb3f541695929a2049514331a442768dbb528f354b6ce24b8cfdc2a27de1da5f15ea964661a87242d478250819c1fada81bea370ea405e770fa26a4405cf461df97cec688bb585c4af32fefd87961c0876b7ac7dab643b26dd0294c1ed94a384eb027595f045d4538fb9553ff1fe188b4c204bf56467936d04076e99061018c17cfd158df3218d007099af632d57ab0f0c7b09c01d37c8253e31b9069551791e2da8cb5cf9ee067081d5fd4a690570f1d4aa34b436328f6afa16244efb0d6ef2b4c91d8b90fa517de988934cca2f58be44df9a6f36ebc868cf7d0c3f11fec019647cb419784b1d54c4c3fed09009173ac9c566c634113b97547e271941a73423746a365887df2b1ad4f39573eb24b3831086dc47ad290d9c2a2decc4cb6ad62e36a3b4ea38629b46f92a273334bcc2cfee164749c71adc68420d14a8e5bc7753991f4c8889dd520589c8c49bce757adf937b42ca5d4f1a8c1236a69b543981a11c42431262f1dd0c89204abad2234ada69deac5a96be91044d309bcc89d6386aeb084e93b8a0ca0a3c92e9b563aa450d29eb7f65343abb727958681f9e381ecdd99ee1b68d6677998f67937a4ccd60b3a2d73b9073a5a30c02364f4109a7fbb817a430c9072eb67245ea5128e19786ed7ac66d2860220906369afe79e1c5404b8f54cbd7669e9e5a8f0c19496629e1dfa8578d73bf23885ba7faba2ffd0a830723eed1398b75f9e7fbf271e5dff7cd5bd13e0b990d9d3cbbd0371c86df430e91216dc6337cb033c07b23a6e1eee7aee2aeed51e8cd72495456a1773767538e4fecd2e7165992d8bc55658da708689957ba8d5c7a94ca5a76fe97fc59be376280648d1d56de192331d2aed42eb10c2a13e0bf918f3ecb587635fe0ab492e84255a78810c40b4800e602d8267a765175373ed6b11367e3fcb35ec055fad344d629195feae384ccafee9e29d1b233a7f770c5ba910bd7f51891d9d96120b56ee798fddacdbc7b3de383932a99776920d4fe2878c09f23f336753f0b3895309ea33b3a5012fd3f45dce74707b5d020e0a01f7a5c36ba15578dd2bc9e121dc97315cfe678eab78c777f7669c85a82ecce0489a559658a334abf2dc46362263b48228116710980e526949925e71eac62897b8d9989df50e128798e924a75e15791526834daae99c4a0e1c50a07ade5ded5bd68c1757bb7dd61151c325848b1bc8a371ca12d9bb56a5740577d38c5610307785ee8021487cbf0b68057e1c28d053002987bb96e02a7f872c329495bd5f9b18b7b702787406483fbe371bbf62b0c4cec496e8013fc6ba94123e9f5ee83c31e4b03a5037f848c4d6861b609ea7fe83e06974f51e8d02e71fd25a41998cc53971038b39a5d267c653e4eb23d3f3525a26dc662565668ece748acfe0c2ec8dfbf3efd0e9c3b88a6c7d34de6993a1cb248c5ee4e3080f2a11a9cf54aa29790adc83f6bf8821b99c1ad54a462d78c672e4eba2e0cbec7ec3da4e04ae1fb8632fa730a878161c69401ac044e80c53dfb2f7449644d2c7d1471e51e2732bc51541c3b81074235677c787444b0a9e38839cf2c666db89d8290d55cd101029e893714e340b365f46dda678f72ceedd0771d344ca2ee43c6c8b54295a31b204309dd3fca697130f8b203818fad1c82749fb9bbfe2f6e9d927d41714d880e1139c1e7aebfb90085bbe54f0968455bbd43d72a800073724d7718853b2615267be8a27a05cd5fcb11f2b25cd4e28a37df55646f68284d9ede33acd7074b3e264b933aadb68d47a0862bc47652504eff9ffd3e8504935955c6df2be8fa08a029535aba11f7926bedd13dfb63406e65cf6204702c3499b1d6239676656077b431a99904175935cb8db2064217537d39cb42037f6d18898e1572c6cbc9b592b8de67c0fed8d5aa140f7380a4a4e8817c29c6acb14b8631fbcc48d04d51c64dded9cb5179047c9142ea71da276a66164bb4d6ba1fb1a16b600192c586ceb15171305bd89a023b58618424dd07dd424e570e7fc6b29c4accb7a3a007f4841b80d68e972cad750bd35f4ad69b346c3405aeb00abddafa69e8a87ad6740a36b651fdac45210a45db53434849331dbbab2bb273295e9ec0656eff5e0f10498f529b088a8e32aa2bafdd0db24100bad4621bdf6cb0dc958d1cf63ae115df24af9e2e484593256747b8eb109f7163adf14e271d7679c739e7440fbe58bcd1fe15a9979595dc3d2bdd394b02bef93bf584b55cb334fa2a5a075a5810291c2b69ae663eaae9be30ae847dd61bd7648a3323975d5df5ba029c847352f9bdec89a69015a56d0c5da2ef19c5c8f6faa25d309ca6f9d5238db6910abe7647aeba82677e4aefde65b0e67f4055ae4d5f445d13abdb94897c15ca737a88b6c108f49986c90d2cab0f57dcfa20eac250aa255529fe5efb5f1420ea2d57118adf26f4de93b1e800ef9eba24b99ca437eac3645726bf9e13ed96035b60aae041463d1d8fc781c077fd3c9f28b0e5a4e66a7ef753adba96af6a36c568345905748e13e6a46a69b8441a7098204b07247c3546b917c02a5131fbbd359f8221a6aca19dcf495f9669ea3cd99723390f90bfc4dc37007cbb9e4c371f460be09289a4962021440dc79f1a31a2df59c4e18b2355782f284a34e258adb8039388687bc702044330433c4256561c06681ff80e478e1230d39dc4c35deea7cb81706c2a827a7d2aad5109f289aa631bcd94cfcffa3cfe9b595e28aff8781dbb830befcc602ee801c5605834fe5d742fd59120c19fc932709085b87d7117823df81e4bc37d8cbd0bf9977df71117f352f54c5246a44f72e4062377dc998c431e2b27d842e82342dded405695e241c370e38e665e5f78c263e05224bfa4211751fd8d38cf95d045b700520092c0652a30bd5f8ea1f415cb27b4c048281f5edd4b229af49c753e0c1a13f2082aac46b42679df8d342e3367d57c0ee836f9b017e32a023a0731251c79d273d3cd143dd27ab329ddca62db25200c378bc92e9ba4a77ef5cf3905cf56af857962c983cee98cf7baa707a2e68ce0499aaf7f2672dcc4b8e45e55cb54ba6adcc11a3d43ea45aa2be1c15568c7974313b655e7f7536be2482b6e44a663d501edc2936e1825713b39f8279b639cdc73e5dd6a0a321e474ee96caffc73285db5c5bedcb1d3798045ec81fe3cc162328aec60847e4127d9528683b88da31da9b44eb1b6e8249abd4a6a30008dfded9e6ea3b1d447b252bc534ee0b7fd1867765a6c7c61b57c3cb5c889428302a19f75c95088569629b23c1807b5ade54f24d28c19865fb68b7cc2a725c96b2858d7d9cfab6a3828738f363d10c4306eda16b0ac0319d8da34bf5e84e42f573b9a17ca5a0f4571c3ac7bc00164e01d380bd3ee7fda2a5758e6175ffcb056baec085777143445b7bb84f8132451422a400ec9d52b512e83cfe55d81af90bb7bc428a7bca1b230b2b814ae5d9e0f0fedb20d5f424bd11d713714a610644090b4640fdf5029632ab159739d9552a7d71528f67ee624edae33a2a2b9d7b2f3cfefe82162300a087082568f63f55c16b461fc823284a7a0f189062fc5afaf54da3fd9c86042d126a753568e1b1c6b102c90471e5417ec962e33fa596a21540da3a471f9d3c77df62f561cfff288d0715c50f98001442c541b79650bdc7178af29107519e975a8b371eaabf61238767919700345fec8c0e15af2435596c43713077c064271e1e08838ab440a425cc19090d07033ddb68f883a909ca3a0130def844f375750000b743a979edb4e8015e0d4f00e7f3607b19c54790e8e310babe6ca2b52ab984c68b95352838553f80b2c2c503694b213f4a1878f6a9da0a0e6eec76774f64d448280c7cf6d8b56a58eea27d627b34b63c391d6a13773c6a3ede157538d5f60c3e2640412d2d186ca77937252b2771196c0036103b6ae15e49f2f9626dac76d8e34b5eb6e29f141d63fe8e770a294fd58a973327acc3974eb724d6a3742bec7049652044395b8d9d4b7171457a9e38925bd7011bac4763fa77ec22c08c6a8b8a1b7942cfe7efe4bad8f49a376db558eda67558644ab331f18d64ca9b8918e0b70824174bd84db01a4930b4dec43f46afc35fde0bed6146941701740f532479210d6617dc621ec4f65516a88db800cb9dfcc8a11661610ad7d809489e673a3012ad3ec20f1d3ba39a43b2bbbed2bca87050b6e69e4afde1136632dd09a59144cd5e853d86daa04688a680c1d563838005e56534d32df9a5a51082ac8bed236d190b255d41c9ec8e0ee0161fff16101039ffd8724287195db38f2f00e6d9556e15ccd833b9993776b6d39902b88d459884a54e329db5764035c8f9010ae2daa0f112d95486d4bb9f1a068b90fb797e5393deb3a403a53b16866c38f231ce17012cb887220900fafdca27820610c8f3d4120b02385c5ff4d3d074ee76530d8cd10c36efc328a9b195536527df6ee468bbf520d78e5be0578dd26b73ad97836f2be2261b19b984d73c40f7f3bd08329132c5c86f4187c386c17b3ee56e471cb767d984578bb8a8ecc39404ff37f5c8d30a45a3d82c760a603724875fca5efc8ae8c3150ab10935398c16b0061a41d0f24c0b75f2f262ac992977f793f247edd25ff62226bb1b704b045c92f419e30dfa165ac22911c7c195f900dacde0f088d518a4ea6af5bf4008ccd2051e0b3ccc483446dd62cfb863e5bd84088753e09c592756f6992e16d26b68697a36c111cda07165a79611324051bd1c787842dabbd38537ab6fae0f5eab62133edce6fbf6021a6186431685f50c40879ab419ca7b645eb25d1462c4e706b0b37b841980cc806003faf7e5362fc12ea3a641b8948f979c0d37ab50d782cd9383d5dc62e6dc6bd6ba0f96b83b98fa783a3c8efdbd64672fc5f1d3cd89a36037f7ac027a80ef9cd25a6da32f5af95f16f5091704ce6f61cae9a18f4ce962bd5dbfe2c7a978130b5f43efff8335b8c9e35701841fa8387ae006150f98a72668b989e59a4f9cf2315a4f14798dce73f8459c7172cf5e26c6675010efdcab22a1894309ffe4f8baaa41dfc5e5b168c9d954b0059c2687551a8d652ed08151544e75d959cea7cbe40b067891d5cef74380b5218b564b4dbb14730b4a41bb340c4d3c8d51ac2aca63e6bb7486254b58b2c464383b1cf0c9e60f854cbdf9939c01c29363882d22c40a983ab3908320d8aaa965afeb81e7ddf6f31ae8e333d6a79bfd123e9a2731394fb949fd8dc6d4604ccd07917cec729180f2f150399cf8c164e4d903011c24a084467160e3566b650b6d63409c9f7586f54e1efda3348c80934eca6409e03e130a00afefbcbaa73cbd5aa102424d1ed06f4510d42b7f58e65ef5aa227b0ad5e3c6b81b3b0c8c40e941f4b2bca55e3fa4952c4f3d30120375bf8c8bc4653bdcccb5f7f1b9e1422e536a4670b587fcd871fdf5ba041ef64d402f5a535f17b12aee96cfaa2d5ad9816baf21542a6a860e23d300a5e5d89acb249d12c13b7fb0fa7a29b9938603e1b40a01f7a705894607f49d8fca43a8f3a93c83d5e65993072658d4836054a5d44c38a701ce1e26108a55a2aa53c8a49ae7a34b0c13c967fa8bc2aed683aa504d8db29beca2102e07672451876e0c9c1dc1dd77b82b6183b215ade1d416ae3561513cc8c619ff2c8fca514ebbd5ed27bb4ac94c22990ddce20d325a2d01ba20b4b6b3223dcfed05dcef785dc20c2e32ade294a420c07631ae8d73fd1b7bc6ce8ac2f7734b80368c22451e6681cd17bb62cefa35e763065ab4748d97e47b3b248e3562d369d3122d49ea720874e2f2729868cb72e8dd85b9ff3a2a5471034c54198943ad46cffd00d1ac4ee9b2d23d79273bd94651db42ea51a6f7a27f39dba7db2f76bbac0518620436c43f13d5e1a49974396d44561a1ad957fe99b1074b3188fa22843225e59ed9513f9e1ad6891a131c436b91124de42e6e16c54813206080e4d4b2cc9dcb513e5cb6098202048800804f0c31c3390308739c354ab6a20733af2f85d11fd7220ef2946e2f4072a901ac0e415c45bdc6b2bbafa624b61a8e38f570be7b4556d18b57881002f9c34d56e050a2e7f614415c3433170f25ec8b1852536d76c23ac4fb797b185bef0f53acda303913d24d008f8fc02b77d540f60c8283e81da384ba2e9528b22df5cf8d316084f8169f9762e9ed49a0c811bf355da6ffebaf7ff1ab74dfc64f34088b513f84ca3aaa9bfc9758b4cc9975f1fe0000fa5a1411ff29f6e5cf650e362afcbb71e1e8ab8fb40b204b2f24bed509be4f18a0226f348afdde53bccf742a43bfea02f03a527dfbf3c4014615774ce813e3e5afdcdc3ac252881fe983202bd7890d76fb8f2158623b80d3ccb944ad39edf17617e930c378ccc98ccac750191d1adc6633b7e172065e9cebf663579301bee0254cbcef1a46bc0080f172c60c5a8f93521575a8b48dcacaa828538f9358534b897b1a15a396ecec657ea7aac05792e1828f3e2e295847d10820e6a26ca0f24a426c418bb244172ce6ecc58f35c75829df734393d004f381fcc8a36ad0f4af68da11b86d847bf736df68e0bb92768b06e6ab9c677b17edf564ca093786470e175e0248d14e8ba57dd10c3c53a3bbdb9f56a1d613441e939f4fdc54a618b54558eb335e4293fff188d94892d32403cf67edd31d4186adf29d1306f12d8cc1bf616b285e4d63bd40474656983520395b22b42333490fa406b366f9a6b9bc5e83582584f570498df3b833cf558f19b5b85995c0f29a832c27ad7c29f1907f2b065408e3e82337b68e83642d285e10843d28666381810fc14e6341138966e202fe688818f896fd3d0b932e6573a3b29502d9b45ad17e7080058325421c3da8720c9443052fdfe0c1d0bea4016bd7e30121840dcfa3adf16606834ebc05cfd23fe653c739b649d9473436d407c0903c428952f48346bff55422489b71709653c647b3601709b89200936155a54d58edd3dc95d1dcf21625c5560328a22127d3042bd16ea9fcd6faf30293df8d4675143337394fd5d9c1c22170f5590c48999d752bbc404353ae4d93881672f0a1659e253bea82e02bb100910c753ae106b4017e8e96952cd92470ad8a7e3564f4436be07e698125b27e64cb4ce11a63fd79140a026d50e817e13d2f25791b8c748b685511dfb86e57fa46b93a38d51817612724f14e6c7fbbbda411d13e7a70d93a854b82406b09c174008712eca17b8e1c19e7077115099f3576b380c77b6a9c4c9d9bb2a6c1a4577f42a8cb10c29bfc6a181949f4fcaffd95f91d50ec74c18ad60856dab4e80d3a349119a1d34d0476d34f76feffd34a485dac9ea5f29e53a092b9e1fdbfb46028e0b7f3b1bb95d9b24b1f4c65ffdea0d7b0612d0c93f22ea66a0301c1838638d4237d66a50624a814f110347fc81c092c9a5eadde0834154660d4d53da50e732b444626839dfce3bf7036f18c9bc0b025b9dbd756b0f692630b6c1aa519ab1ddd30e660ca8fea27ccd191851c05158365d738aed2543e8ee8e0e8dd4a88e7aad864092c6374ad817aaf442a2489072cc25c9d4964c8643097a2a6d44daea4a30441e061a891b320c1c21fdad4a3073f07a514bd02d56f3105a99a254479655c92da4f240d664ca0b9b6eb39988fcc18d530ae567301014699817074a4bff6dc929ad4a6b47e1744a14924217819744f58b3270e9933c79080eb02cac44b7a8f71320283dac8363629daec116dd2d5b889a411fd6d06c18bf97afb0bb04215495521edf96090cbd7ea15e6b0c8574849dff3e2daee544b07b4fe4815241a0d15722db7600128feea15b26894422c4448fb713245693e53d8955a8159602941016d43c854af5a69aba65777c51f5904276813cdacb7ec05bb74e9b345ca4746eba5202bc128d448020cae1f8be95662f371a1ad09f570464b111fed151d13c50420b1e3b3bea2885ce3ef316703a95df458328b24807b6086fe9fda4527b29e0ecb6e8a3e46375f128cd5e77f2a783dacc4aa72574c2029ce7bb91e7ca9cadeb1e445849ac205f6406bd56edcf830304ff8a04578949b4eb5c8459a101bb12ffc3bcb9e4e384310550d44114b9f79af5c857626a45242492658e8a01660269844c068d95e16b457c0067b160db410f0a7b9d5a606d0ce8897fb77270169d00ecd64258bab990467fb4adc816437e5c9b835f3d49e415e9255ac8da8a3230039f2ceb1f4c21fb1eafb2a77a5953b86a55af525b3f6a9922afb4692588392df83e56db2f0a0659910300c946614085b8bcdad58e7c744da021a611ac9ffc72d4f37e9e829f4927da3451d7864a7add22961a2ed196f352f5223696dde87037c7426dda337ca3266249f9a90e8374524e752710ac62d9bc7a67c02136195e51a60b45d2abb4789ae8814528c1fe43b61a905e56e4a936ae6b2068bc8ca688dcd78058b004892a16ce2caf26707d2b0316660f18ff1ed501d2bc23b1c662aee8b230831dfcf0c88bf30a0837bc613c4746ec8d437b4eb7a73444a2be449158ea71d5479915adc6becd9c48f65c36cc19633aa42b38bdd9cadfd7b36c4688cb6b9acf58ef8ea87ea87ddc39be2a7abef99b22a89efa8cf43cf81accdc013aec511a393d9da71bc67cb9a1a26eecc34db22c346d5bba9a058473438f7f5445ebecfc880d7c8832316863d1c44bc23ac68ccb613d0344d133c7b0d7dbef73392025443a62a447934ebe1b05fab45fe0bd23bbaaddfd839076eee7209ff3dfb574654dd978d7e979c60e2cf625def8548293f6c7fc8d5e9b521f178716c2c602dc479695dc2b879eb75759431fced752564e62064c38fb78fe5e1865f1790353765b1e6e9923535efeb73ff7e0d67ea0aaa99095463050e729c85790f547db80688fa07dd45d138f86b8988469a3ccc1bdbbf871af870c8bd2e6ef7489b2559ca1c09659faefb09c2741f1632720b53f3fcd8e928e21d89bba6749c3f5b96639b7426597b46cf4bbdff976b5ccedab9166a990abb8e9e3cff978211d000ec3ca8c01381ca1a6e4b465c8d9d9e09845cb487fc6791dc2df60dcc19c979620c7624c2b8a87502760b6e0ca6666b3546da5b69aec95c666b0898899f7e78712ba377694c759e1c148b1c66ea9d91ad24523ea244ac86060ff9984b88cf51630ac5b4697159404ac7c98e70fd48ca5afee6fcd68adae7362c5c60a3770974b43c3f3dd6131b614190a7163606f0d115d3df9efad29ae4b96de49a4c47fe5032d784f882ce00df3034715f5bd76076782f1fc7e8ab78e5e984a9bb2c0e013d27be0646a533ae9cf009675f5bbe113a3eb19d62e42c2ffdb6f5463a301045a7b1b9f8f359c75f3d7b1928391b3cc7a0c02e7a61548969eed647bcd6b450d11dad377438241ebf0480cf29012d072c0d29079a5d49dde14383e83ddc98af7cb79c3ff815f3202708596a442e31171ab748e08b8c0056bcc5d6c9609f53a1756b72d49f78945c39d28c57b975e406bd9a46536c8b139c01e7646abb2a2b5c09bf139638e58e5d800c23d221c1ded8b0ccd91988e485cc62c2df855be30c5671627c79526b079d5fd7a7f019c58fa68463f7411ea3100a10f1d2391aac2fccdae9a3d11ed29e96900f20c2ee90d185cf46cd4835f5ac793cd0da50f1eb84802be652b8f14649e76dcdabb0ea1b021071682ad7f3d0d8605b1e7610e8750e04dc5456d0e7c3a0f4e640a500ebd05582e3418dacfd9c932218162831d78903471b9154ecdf778ce6761fd0238818417ec1aa8c0fbb08663ed1d5abe819a7c71d7a7a0fcc3f29aa8cd1a341774848b36fad296e47747b75fc4f2390105b293d63660dcac5b762c304c05ac57a8434255e2e20fa1265b064b28de92a240cee92c1db7470bcca800501a0cf02e9d568b0354c6fc331a121de99f3bf33a218c8cb0953124041e101d4de1614e0c47c943622a5d35dfa3d192a9cb8543da7af17649f52d338fc46fe183b4762e8f947dc421fa07bdba3c52cdcfcc85fb18cd499a28f4890e0789e03a146e0a001bbad612bdc23e78c6587dccf88c6fa1763915c286b40a6f06d0df61df804f195e7ab77ceaecc1ce8b851612e3adc016e86b190a60e65b461ad87ab931d191e6c76331be9a7464a7442e86b619a4afa76e3b5da5d086ca4195794e53d8897c6b54b8650482d145fde4fec9ab3a111eff157d7704a3eb9146d7aea5b92458c5de62f3800e6a52ca53aca9a5b3730134f506686480fffdcc68720f951ce73a7d4d146b30cc4d8210492aad47e22fc23b666e2401c8eb604a518e27677348e44873e6b0d3d64c97a5b6af71f6b63946bacba0a47fdac1aeb1cd40d8ecc64a26f72491e08811bc10acf9ba4b99e0dbf9b71fad236c3029a8acdf1192d4c9700d934b06f9bf9635036fcfc3f2bad521aae60f05e15fc71e64c550eb45804b3c37f91a449a78fe756c5b499fb1c264a5d43870df25830834425201442708a623e833a534b065ff69fa12cbe854e642ca30896e2871779cc504f01933b412b3f0cbce75773c17405e337a4860775ca0aadaca50c4e844b1e94fa6e80ba3b4f5050d562812b3e9d6847c854329e36828c63527a93d509668cfd407967ec3d01e568c9a2c91812ff1436de9aec322f2038940d7e0d2f74084804c6cd97d2993dbd0f380bd81add940830a5b5c60c50e00ad2aa6b6a49c3de98cb5cab67a460b47820a9d0c5df2adade3171d35aa2154548c90476701ef1949ae613a86ae161e11938674022606d6ce4d428baba8b770790da7da01c131d2104c2cde1053369715ad05f1e872331577b6d41e293ec557917ea1d6271a3c2df8cba83a003d7eb177bcee08203ce0df205ad037db808d809ac9557ff6ff30128545b1134226a9c542aba2dc32fb0a1e7aa894b9d99d41443a51821009b31d1cceabeed4ddc9298e14046b7b22e6154e514b5a1f6d3b385761cef855f762802fdc420aa14d79723b15ba4ad1bd12592b5f5310c440c52072c570deeedf588c46b448a6cdb203e2ff2462a2fa4578fbfac692e4024bb3e3d822500f886ad14540c3bd9b42ab9cf53a376306890e6bdaeeb04184007861535f42d0e33ff21efb934dd31a70aa915e7364074ea591c3249b3cf3737129d64cb40e5384ffb39aa5d554e7e1a533d2db914f937a823d22ac175b53ca4cc0a448fd45f6841487fc192eaa4d34929a72c1f51af1f463a2da50c9c14f9fe93c4b3253a165c538438d0484feebccf3b23563113a13b4dcd0e182a7efb050b2009d0ae44fdc650a709f02d248923a4804294f305e40623fab0fdb684465db30f91accd113389c020f60f491f5408c31719ec2c0c785bfd2adaae37eb7a3cfd68a26de1022ffb530e0a20017fc3caa929fe99642f2d92d53a95145a1d66723c7b00fe1b06f5cffa7bb79533e8ea7d538377bc2340c091ed5dd3275c68e0264b15d6c2c789de8bb190a43c37cf14356ee790c5f66937ae86be6a5e0efe01115709f1db4c07ef925059153ea8cc72b8b8138cbd0a746346e2b8f12a91991c3002e2ed009856e0c573842fc5be614d9dff867576d0faeab659cffbeedc20e167be3eece66a6218dcc9df349d080011d741449cdc7e0c6da923126e9735f0dc847c6bb9960820a0462b822109e3a352e1846e07bccc3634cba20b282a56910ef2236b8009ff1a1d6382bd941ca4baebf6699888b5641d9ee7ff87b330faadb7fbd292bc8258491d116c4c6e3f0b9c842ac2483937ff37c9e1d808e1f01e6e71583fa9230031ce4f5b3a8d5b1f3934242612d8445d52adb49afadf545497dee69c21fba32d4d4a6bc9274ab6ce55be2b594606b200f081993e41fc13c680954506070b7dd0a50833e4569e4fb5e5f5d8dbc22c41b6bb688e3ef46be5f14064a0bee1e34aa1d3e98f67700ba9bd66263da3fe5d246829e64b048b35f490b602c68afd48c2dcd828bd8cef9a9c2c7d20e20ff6c270f6f21020c5845567b0866563657713cdfe8ef6158d10405e8f303f86b194c9a127fbd574eb16cb8cf5eec679c6053cf366f22676b42513a4ac510690336d1fb7130e24e1d01c6b16c467f576032a87656343535824ea912672984854661c3fa5e7a130e9dc39f2fe5791b1e91e2742d21c22d1df0d28f76faa7d21e857891c3df9b8339bef021a0197e313922127ae92cdd451e5a037cb265da2643a003ebd3d61f7fd12281277dbfd4d8029efc37009dce32a6566e1ace22475e33f973d7388ebe75389034a8109732be61079a689884f34cc224f6c2b4e4581e6d7b16135e347b5090fd54ce15643ecd45d9f92e3590ca3c1c8c0ca3319968494490920d9e9f459900108264ea45c349852acaaf345bd4a73a844af538e35fe65a07fabd4a34c5e40af08c8b4fa4d4198b809fc09de3a79c8627a5b0feffe21dc4a855c693094d4487475052aa51b2ba32154082ef90ce77f2026e06f059dfaf8eecc561bb99a30667da381a3c99337b7ccf4b94c58ebed1ab66237d5e7765eceb4301512cb4b1ffda6e2811b449d926d8de87ab282ebd0bc20aa411407548e19db4b589dcd8bcb964cd554180f2f149fd0efcd99458b7c25591d4518c3e5acbe546696f5deb92a6be89f14519db8ab7742ba1a7ad9913955a691f8682dc6a0148c63e2218db73817eb1358fdc1829deb9c6c68928a06d9b4e3507a3e84cc527209676e1ddc80287ebb757ac13b71bdde2ef18a8f37f53054f5ae06db7daed5d789bd3247bdffb768d6c66fba8f90fb446807040f5c65f11110b8063d4c469f5161cc55303609ae003cb82440b9fe7bd1df69dc3f67b3ff87bbe0fac408b02ba26b43a0a8e5272dd087b2ccee4d88aa0c84ccc3ce79239ec77154868748d55e010580ac58b37b5df5b83bdfde77bf594ae4c3c16d0fa3feebb14d5467a2b6449ab23244d0690d58373439068e82168a863ddd01284ba8c0fd6aa0897660ee4e0e39ec81b81a0e711d85b45004aca8f82b09fe09fc277792048d590800ec872a686818369aa2b07ba89122342a17554a918a7c0f280da37ae79e44fa69dc136f418a6943f4b0f689c39756254d2061cb49b2d3dce528f571adb5b481844df533b641eb9f60840518898e45b8c719b03fa114cab3a94bf0ce7bfc421d67ba34b71ac494b65ddc663886e902fdaec42d0e53df239d68089d9372eaa22656f3ef6547332e1fea7077371f73dd4116c81997a96377694ea0deda1af02b07ebdbbff8b56cf19a0424396965cab75004fbdd5b369e6ec8358a4dc1f9a2873d69b9091e3d22f280972db80ff72ebb5ff4d08fc71f6adf58ad3aae47d5292ce2bb108f467ae21c2e0bafc51c050e5aaabfff2dc78f0e2f689ca7c00e8509a62b721aa6f717a251f084141ffef4015d07a1a93375c16f1cd5ce42aefc63e1adac52138b45f9ac74860f22694442142404acc89beada5b74c32dcc707c685eeb5668f37a5deef9aa9a2c61a27d0a724e7eefb7787f289acb96276035d387b7fcecdf85343227a3b0618809b7a2998f9937528f631a860c97fab64a136c0fde95aa0ad210a3c7e9f018c49d89112e75b8e0bf91cc909e1360bb9822a6dab8b121414961ca5d59b81903381d8ab47911db1e888391c88eec7e54f426bf6a59506894d6f9fae3b9aba13e17b636e1cab0483b92791d53f32db255cd40166056c70edcb22f11a0491e0a40e04891d12f2f9b27e16a8e482232f8a463399c8e8b17a17b4e1067d0e69fe225014deccf242179fdfe786e4a124a84eaea3830eb78918471bdf8cd32af3980982bcea53d40b7fd8a11fd6a32d36b96bd012bd17cfcfe576cefc863104d258198fc659839d45e834e233c15d6ac54044331d61a501c33d9f620c8887c19780c250e12cb57f146d24a636fd5125345336f78dbe4994ff63d9c8756d324e6b0e98f19df21810d5791adfcc8c84167419cbe0037bd3056d6f6819f21499c7532b7be110edb737b271943509665ad4e819ea92a84484acb33b693ed46da2ff21b0280daaf8aac0002d31d87a2aec217c83d19459b8a8d82c331fef03096c62e4f6346af932e99a6385cf8d468a6a14aa8d365fef348b04834eeede23ebb7457924fdfbef8ade90e66c41a29425dfd1ca8e2351bf1b6ed5ffc32f1a0415f98a4b1acda1f76acfaa727759027b3a1965b849658ab4e783cb336c32aa45a0bb20b957ca3fc1481edf02459c1f2a6317cdce40d3fc519b944be655be771c677cd8ca8ebed521f8465cb7ec386b10b28fe738175cf22318810d494af17426f022a94da3cd9249495a8c27319f3b1847b21c2e8bc750bf24e1b0684d742e54a4921a3f9b7c86b23fb031ca3fde254b63422c06bf1e819e8a20a2835fd63035d4e9dd0ddc7f1b8ef79eead091cf7ffcabb7fb80b8203eef45efd950a87dd6e027003332d729e30706ac0ad766f76b109473e929427b3ffac9fd176947c54ae24b269fdada9901ab676c45c84650b7d28174ea6ac3a4bb04b489ab7fb08a820300c28a119dfefe6145f498f74cc7471224e52414707a032fd225d859b0cc842e4b6c3269ac9c6b131e81549c691492385412a4f37136a3ef44bebd047dd162e13252094dad8429efe5afc3fcb5e2fdc151e1c84ce064be88d4ac577a75c6be275a7084150b046cb7bce9f096f168f0b9a61415b19daf0c4738ae21a56a649bcd96211dace7304b8525840b76784f176b574c46cdd494d949175af79d2154982e75b5030d62ffad7d5f40aa8439409346949624f89f376103123cc99d50777ca259ca0865f3e386fb3f08d46d6df4a130788afd3e2c0be7cc81db0f98116d378e4ae1c7a1921fd354fb9868b13b728f7f127be417dd7ef55262a56e8d6fd627f5664a724797aeadfe522389d29c8d0e1aa6fb97d3464048b7ce119285179eef24a10f3eff942bec656316ae5584142d217035758b4dff188306c54a70f2fef397e7dc62d00cddff5c335a6db6bf79464e830c1c249faf2af73382fdcaf4cc074080c0ae9cf24f82c783ba66cd85428f413ad0a363c1ab7798fda654eba8fa11c0ea850ebd4e65873699a9f8841e23e833749fe0a641869ff3678f630e112707865b0a7e7647b2a8d4cf19bc46b48fc2b191942173ce4bd439923ea2586c11620f21d3beaabbde1a170bb84f3bb337cc0fd68c9e6a800be861dc80ff59127dd488466ab47cefc6869ec7b8e691e27d24fdc90c0c088f8d1369032772687a11bd7e2ca927d19b7fa8634ebf0a3d859f5daf51ab1569e6a53cc9f4636b1f2eac5e95c34f8b600afecd0ac965bc9c418ff525a77de4efc1eab6a230409b84e838425fd4728b29d054ee83095272382b45541b38459956d2e3d5ee518b508a526e21a5d8959ca44192e7c9e28d6aee9fa38b138cc5428504a88c2f68c21d771bf50026aa82f01744c4431ec086d82129a8dce0f3b2d98b58c5469cf8001e7a072bb26e18bfac22fad5082ea0c7b8017eb34ef4a92132223a153b7ef4acb2a41e2db109f2504f9dd8160686726a1f0d8b48bce9115413948b45312064037203b1adff5df2f392b1ab18d033dc075e6963983d69d28bee2e8fe50d991ec4e6a2b625367eb860121735e55645037ca01738495f13bab0f8e647e59d21347d8e3e6964f1fed9f700e7605127ce6b5a31268b0cb30d24e2169c3a5bfe7c8adcf968b984ef4913f45740c7ce0b94756e1916cd108d79af3e4372d93520c531c4cd5d001bc32b8c1be12fab885e368a4b61c3511521ea958ce82f30adebf3b650f1c6005521b4b065185e9c32044ee8b11750194045f6a4f2ba488962a3626929e7027448ebdceb4623e8ea5342c49626ee418823df0df1d1a257936845639b477c92edd6eff51fb8790f63a0ddc551fe8f5ba7bb469f22b84eef9e12800db4a85ea2086d098224bf6a09dd5da0524b264793c36420686c90fc15311588ef00c66c20fead0ac4004a08ed161f715c3401cada02740ea88ade4481cd9d5e6858953008c121b2ede53a54e3ac760a3c3b3ede7950fb7520bac9f6473878036490231a65bf16806a276125832fc62f6c41684fffabe672c8ac7a5698515a6da952ca4d622a15ccdba24a5732df3beb0fbcde9dd245218d0a1e0462860c071a744e6e2e76669aee36ac6b6d6d2cee2400ca6396a09e6b9b90996e00eae9d5a2f7f362cf947eb30a1c5211100cf4a89a381ed6d9f8a65ff16cf4f3b7b93ae9f64242fc7bc488e835d3119d15d17d2c12ba9936b803d00607ef204e693e74a4aa1f774658feb0121ea2687c912c99009cab44976cc34ce85230eb9a70d7251fb729d9b622c133df14e64f8ff830499a0ecdb55efdadb4fb21b788b70fc0b27ba3b79f95b993e70d0a119ce1f9fc6944969d682cf801da2ec43024e7dbfa749cb1dd5594555344873fcbdf874247a011f631fe1dadf83adbcc46f07cd39f2d0288559a000a70a3658696ff792c6064faefa4a7fbbdabf50698a6db8579ba004b2f9c8de0d0ae3e7963bc209eb1371044f755b45718e8cf776773e9dacdfb23c8848d1d6db4c3829ef12e4e8b09b974d347e3ea76241c1a67aea5237e05a0928b531215419969ee38104ec3f1eb560f629ec42c628503b6c626579573121f5440170db2d6475fbddde46131153f059545270448da7af1e4fcafd26c239d4ad492189f1ef67f58ddc17626338bb987984d98a6ab4fd9706fc330fa465c1fad2ebcccba11bea65f212770e32a41a25b289f817eea11760422e19b630660b106e906943e5d6a39915f2d123e20e367da8ae888822883f50c31150c9b682cd17894e5823114a8455e2869c18e65c84b805291027e0995ccf52864099c462adcd0f09eae9603d90d490ef087119527a739ff7ef13959356916a0e6f18186d9fefb68f3f25d2d0fb0eb53effc7a458327ca6fb6159d12a1f4b804a70bfa184aa187752e824393e53bdcf2ed0521eeae2abc58bbd8f641bab667af748cf523eac6b214abf233f76869f478e6b271a725e62c670c93075e3ced1fd6516526cd6806519b72e30a8b9983121477fa245011bf8ed01b2ba820992f8fdd04c90fc07901e123a0854f2f8bcf5e34fc43de088ee1e8e8e2b239e4865a6c655babdc694284415fbf1b2dd249a4189bb17dc1f4c7c9cf7c13f4a78b1558e8f6fbac0f04c6e2b11599787c79a59d94f14f18b11bac9a581e4a5a042a8d143ff003b74800d300b87aa6e37bf3e567f149df065ec2de024cfcd64eba736f9815dd3f68d7bc21a30e10b49fe480547672be8f3a06663343c104eef09841ff9b070e8f2f5fa1c62ad38051dcb44becc0c59f7e3eaeb71fb75745d4d1a35d9266b1d8d49cf6057208552d4e630ddf2bb6d5a5d1542b678461fdd8b7dda0dcb36a55491eece02301ae772ac4f330a4da5a915882a81cce6c3cf47e1add5775d9a173df39d21084ef316482018ae09bb9e5ef70c6cc3d6a5acba99e41414c39648a47667388bc22e0430ae2d85c551b8fb094c03d1eed5694b8679cb79756fd5e6de8ba76a9d6c007be80bee83774bcfb84372e81d2d40398c086f873fa20e9a1f9dec8602acbfbbc0bec907b8290cd7b17e899d86b8dee523b96edb0fd2c3b0466c44fc91bc1a05c1df5697773067aa8a6bb932ab7d5bf45560e3c218d63ebdc8913984a108ca580161cc0b44fbc15478dd757e11b0ee5a8f061e542e5d8a4916416ae2bd2217040f592201448455fc0491b858d88ccb3bd60595f1ff55802457fb066e89ce8c80bb477190f780ae8194291586940ee5694852e497900650b11f38bc86ef764a2ed1805b469c26829ac3c3404f76e18dfdcf9821a5a074729edf01e7a6a46fe328eccde5045c4786d8a29e808dffecc1d028ad0295c5b40d855a5030544a1ea892df81726aab931ca4aeb4dae98d955a7418c8886b293be89120f6f93044a95a826ba59d56bbebc7d223a77377f0c95b52ff1ca802077817b469860bdd0d84be707692442245065237b4bb9a59452ca9464a4056005dd05d90c7037bf1f07083c1baf47c6e4b93d57794e43ec05fd3838f7de1499137087e5278373fd191618be3fdc6a38d7ef3224c1a6976bbf43adbae4fef780805c087419c1484e667e501c99a2756e3f10f84111051299e607451444ba9af383228a27b28e18bba34ff489d127c6ee23445398428c4b84107d8c1c893edd4c8e18e96672c448379111264758468eb08c449f23d12706922559ac6e966449162b3611830b2a500107ce4b1489c2164896480921922532168958c8224b962ce918a3498cddd127fac4e81363f71122264c625c2284e863e448f4e96672c44837932346ba898c3039c23272846524fa1c893e31902cc96275b3244bb2587107d1470b26478862ec3e4214a58f7442f42122228a314bdc2b9c8343f9c1560ea804aadeb3c0bdafbdc01d7ece75b791fba010f8c30377f2fb2827adb156e85d27adf5b2178b1806bdb10b7bf662d9288e46d07bf41e963d0c7bda1b119d9c604febe0ea699db7f27b9494989202bd5394e4f73f412ec483f88ac8778886827e94fc34ab83f4aaa8fd2981110ebda0ad7b40d9867687304639e39cd07bb64318a39493464aa1378d724e4a6bbd6cb4167a5b5aafcbda7bb12c6619f4ceecc5b02c1b8d4e70c4187ae31e617dd218638d92a2125554a0b78a0af6484febf05b61314593097a9b5a4bc15a537912096bad94d5d031b9ff96b0d63a5cc363892c2cd09ba58eea9379b554473342ee8dffb21da2ce77b1bbdbbbdbbbfd2109ee8be952a4d07b4f2691420ebb096e8227bd882e7bb16c7482515254482ea515ecfd6531614350d5d3ad56f622225ba4894e2d364eada4855aa8a5a5a505e56242b1bcacbca44abd450a7b5fe0ee228a3bef630a5e07419537504ba181a28edfa00805c174c2b6141a945f0b812affce7a3a0854f977902127567e4a594fb39ad5828a81e2f2823d29d4421203eb2638e6eae2227a7f317936bebabec83eb4f2150cd65ee0d75cd0c257aba1983bd9c2d557407878b2226dc3debff6a1777a67088767d972a205acbd7001e30006884aad0355fea4aca757fed7058e089d6fd013828238ecfd555d10c041c90424d0dad1425681a1ec7f5fc06434c0b91c32deb7ea003712a0ca6c9881419b16c06b1a9d24cafe5d4a7efdfeb58d6cb5cabff3fc6c34a1dbe64195cd9d6c791ef578324cd070a60c47cc708b02eebf33b0f602cb604972275bcf57ef22ba22fb3b8f6723857a30b9f716599114822e3443e49cff4c7683fc19097f26c381dbb58573dd454483260b232bfae2d95c44dd884776a1dce1055c3713c9457411e5d65c7269605d8469b0476311863d15aa8a6445d046b6644bb63a9ce1d699724b862366d9c2a275ca7a4c456860701862e8180080c927d8c8d653f94b2b9e4d56045559511659d15c82fbbbac28fb5f0060efd97079714959c118d3832a54c194c545e5eebdf7eccf4f4beede7bcfdf7b0f4258bde6bdf7a65ca15c4c08e17befd99fe6a945c02ff7c79da8f223274da19306d56b51b08b45099471078bf2cb70debf38ca308430068de2ca34c2e84f7ef307b85f669008c2884d08b13c21c417c5be9772a96056894ab24ad058b1d2ea15e5f7f6a71f0b07585428c3ef2a50a1fc7e25c379efefa3fc0e4208e5d04708219c50e07ef850497e0f63dc11756234613d84fbe3db1fd8d50ed3b49c502ded827241b5b88cde9379ef1b110b8e1001eb05473c1b0c6de0f63f04b7f708ec4fbff76eb827116594d1e76687fb62e4cff71ba8f2971aeaddf88d2d829ffb0df4e681ba21e6f731bfed3d99ee1805705f4c8761bb940d1d92404228a1ce112e743d468e9b11533117acf650597ee43e4cd22dd92fc45ef6b6ee0345f93d843f8180fbe18ea8f31e6a0d2184d1a313b1a1c7ae5a253aca1400c18e50620eaafc4dd069c0d192e146d352b464885513fc19dc10af8b566c8f9073e94819bbca496e630354f977b8c3b43a0e54ce11242846195ce534cef99fbec0dd0bcafe1d4df6b721016e43721c031c9f8c127ac751075b0ef5e363ef1fefbb5633de78bca7eaee6377cb61b4f5f080df070cfefa170606d4e37f598ec70f53a14afef61e0c2e8fb7ec0a2c3ff50fdac0e0f701b3f160baf1a1f278e3a1def858f99727bd7c897ecafb784f557af92668537af918daa4a45e5cb4ee21c9f4515af7a2c874d361fa9751be45d3404e363db6caefc99cb6ceb4f9637fc35934ec574a1a49f3a1b281c0fe00440e60d0821e1c0972b3c35322c40a3e925c71842b10b949d950b42e8795b1bfd877d763d763d006043d5e183da86249175f58e10a61dce0e7d1373e726e74e80ddeded6c5c7aedf70bd2be0faf830bb81668fdacbd87d779becfa134d9331c334a7018e8f42c07dffeb5ff313caf375e86c37d40d35d7df41b30e305fd5065764698355ee1bf9d86ba47cf3e7f6750799d3b5a600b9fd50e3015fc608ff65f81d1fc28dc7d65577a861dd54bc7f2af65ebf600cf78f64eaf0dc7a6cbc4fc01d0de4dadddd9f3f777f404db8a01dc2f06395118ae04a4ce600c7772abfb596ee3e76ff7aec6218766da413fba07189ff1fc3fdc3c2d2c8c0fffadbd7cf0ce7c279cec1878fbdc63e7c927c18e17bbbd9f7ec86ebfdaf7dcf706c77bea2f48ffe2f06c21e0d050c82e0c3db8f5f679be8aaf856b8c339de871fdff46ccc8fa755ee8d7e67ff6ea81260fbf5ef7b76837d78df339c9b809cab75b65bd462bd327c6f72ddced16baeeb1fc4aecc7068cc1c907383ddc65e06379273173b773f09f8ef0d2228df9befd3784dd7c09721dfae46be34e4fb9ec8f7bd86defb17529cebde1773371d4f08a6c08756ab094ee001caf6afe9b2dfa13cfbdd29b7fdf7f7dd70f7ee6ef951ce388573f6faa9dd6b9b5b83e19ca5703ea6f1c03ebb6abdb07f9906829c1bece763d8c6037ba9d5adab31624f1f935a926c2ddc72386737e09c95524af9b1bed422db8f1b51b66f25e09cedc1a3756e88c87745b6af95edcb2af0f5ee8e3de7e63f9cb36fffb61535d65863adaee39c7d7905be7ebe8ce2cc0c44fb57eb1a8c6c1f4a24ced99f6c5f6e2de49c7ddf3ac839fb970f1d64bbcf920adcd164fbaee3353b5e63df7f04c9f6dd07afa96fdf816c3ad93e86b5626cadfde7353176d3e140273ea8a20a2baef09d5cff5ef9a1e4777af7dd7017e2895cc3f570c3a8c0b18d70ae66381a7bba35128ae5b072277f470e2b5ff1338d47f6a37a030b796e3972b7e30965ffb206829c9b4c9b9f651b8feca136b70fac72672f3f9355c69ae15cdb1fc0b9ea19429821007ebf1049728cdc9902a6026afdda170f2c5bb8beeddbb1a3c7510bf19ab8f514ced5af7fbb09f8df649aadc4b9fad209987efcfad07e6b1988f531ad6b24b9fe05df6a0dc4b9fab009b8739d5c1f6e1dc4391e15e0faad047e8512fe05bfe3b2a40277ada495784d7ffd9ec26b1e500d8872fd6e798d7fd562abff62eaa6c377a48f967073c021dfff0447fcfe1bdbe18ba3acbd9cd3fd00163c085f8c73efafebba36281f4a58238d33c6186b226d080473957f9e588cdfddf0a3b6e3b630ac45d4e4226957a8d138cf7d41c0a7fb9c46862e47eee0efc891fbfda5c6437ec540a654870e30f77800043937393eeecb16b0c98f1addbada8560c5236705146447a7c70844b0baf19173f39c8b99913f46572208af7ef59741f869adb59aea57ee3977ebd55dd31040eb3c1afe9d0c110b432f251cb2fcdebfc7b66e478dfcfe3e8dfe80df5fd75f5ee0ae75b2bfd6e91fcef944bd0dc7fbd9bd7f5ad4e91f38409b1a50e55f2bc64f835d1f66385d5beb7c3957b50cc8bad5ae816b6838d0bf5a28a7a724d31906f07ef0a2784fa693c1b9b082f4214f00e11699e2f0834c3f8ad61186e21288503966bde1785b179fc8f4af84fda4cb39d258a480bb98e94ade00fdce37fcb264817b74ea0d7c19bcc6bf421b1cb87f93097bf924e31f68731a3a110165af94d229e79c92d229e79c92d229e79c92d229e79c92d229e79cd2e6f40e33475e36ec781042d9ff2a1142bd264c57072adf5d2097ef6e507edfdd9ffc3e08138ff31f2f82e93ba9647e275ba7efe410e93b4954fa4e16ad7c37572caf9b3b442e8b75a6948ec3f7994c39c618574a58cc52eb3949266a0a0a3eb994523a4f78a512f460f09f9ad5c2a1d66fc33d2ea89d582dae67983cc12aca8976dd400d621aeee11c9e596ed894c5ca54e1c41a46c2482412e93d089ce13c181d981f8ff44f7b92447a2b64a5c0b41210e74a3c303a403facec6fa540c20a794dea8820283b3c29251e3bc43362e11eaef2ce7d62b9735613962fade71444c809af82ea45611ec4094607a6872ead682cda48cbfebe60fff222ff25c391ff925dcf80eb86b49d5650f54390a9f5645a4a8482e469e7c26b3bd9a19190d7c8af90a80583daa86a3d88f493e06feacbcbcb4b0a06060606c6a22caa67e84e3f4e2b1fa80c798d7c53c571255fb012cf4b7cc970de5602924abd4f65386feba99fcaa4cac4a2c4d3303dc0e8047162a8c413698907060606e6354dd3b48bf37db9b41e179722e750afe6e48b0eaf6ccb39265e8db5c20ec1fc70d5ff3fad24e49ccfb7d92449e06ddb329cfe2d937483d1d1c1dd69755a411b2b04557e5a9d7cf1fe27455e93726275621965a5edb4d27aea9f7ec01d8c4e108c8ea9f560f0279d4c41d91f6e5d0a28fb5b2becd6a59eb02daf4939c1eae15c45c1272727b0158a98158245980b81bb128f4dc997d6d3f227ad0711f824f89b135eb9a45ad95f4549f647a584b23f2a49aa083cd99eb4530f9fe1a493fd4ba71d164dcb36b89d56ce3961b7eef493fdf423453bf1647f94ad3b05c94eb7ee2424fb9f6cdd8995af9b96edb4fa01777608f74f1a941bb6727f57e2c9fe264d83b9b41e953f69a99696124a053d197f169855f65f4911657f526a28bbca4b265248e48fd7a48220777277fa01cafe2ada6975c2b453d54e29da8907aafc51b413d64e27da8935b79492d312b8c3ab55f63adac9fd59e479f98c6e78e59c07813bbcca3319ddec100bd8bd5043e5cb61506bc1e69c325e6badbdb22c9332bed471ce57582da242910683da05430cf4ca00fbc9f45bed2fad676ea9542af502a303a36363ec504c4c8cd522468653e2b143259e120f5e711ce7820c193264bc80573373a68773fe32198e1d7a32fe2d2ac0dd69b5cafea71dafe9ec50f6558f93cee987d7d821ac9e5a502e2f2998d76c5495cd9bd00ea378e118e164054b4e84e878452120fc55441326987152a40824a61892151501e484073cca0fa808149c0051040f298868b222220ab9c3af041154e4eeff2b11475810230698a4840b30560411be418e20a2052920828724e07b051ef8ca0f680817b9fb1f62c50a50450c9162081196c0363fa0213fc0439830010b1843585e0c01e287f87002121386e88c4480b32f464ac0388b132398250911cea0c802ab20913511228447c80c8210b2f3427c3cf7311c9316141917b8f6e084072615d1040dc0e851c1c91078253fa021bec8dd3f3104167954049ce507344412720c376308217ccc1046861842ee9ee6240a8f1e3d7af4b8753b68381717171717d497505a972347cbc796d8125b62cb779f5bbee3728e7ca27534342eefe2e2e2e28242d5bf01515a876972d06028cc9a6d88441dfdb26a44ec4bcdbeddba9c7c61df61e4b875778688590d7e67f23db3cf3bbb1fb70edbba9a83b5f561f54b43d51c739d8fcd374df99d75a04097932f40f306608e9f935f56438c5fe5bb89da03b241517ed98b999b8eef1c875188bcd7b51c74d07ff97e17b71efc78db81c8f544b097d5ef626e1cc7511f763b9ed0c9dffb398c361d4e99e53bfffb7774471fffb28cba1745765fc14fe54f7a1fa5f74f71f82fbf3cf8272fbf9c54afd1f1a6676131fd15ca2d1f3b8c7cfa7baf632f83230de6e8397af6ebda46a32dc6adbb7ff22ee35b779fe56fc0abe5b0f289d63d211c1d461ef9e83ed4de93b95be78f7d74ec73a8df71209b361d4e79f4a79c437d0de8b86f7a1df75d832aecbd199d6f39d4efe2e3c73ecb11033a3744e6b433caebbaa63522caab8b2891108ab28b48a994510a45d9851233902cc96275b3244bb258b409eaa3858c4808456984a45446a1288da07506d58708f531c20823e40e9e236300aae0e30394676ebabbbb8afb7af078dc83f0bbbb5bdbc1e56efff66d87d92fc389cf216c28474ddc17d391564be02eebeeeb30c618a59452ce396706ff524a6906ffd6cb5e2c1b9d609414155269a545a665868c9728a64c9bda4be0f3cb4cea05b875770a4605f3dc6b2eb42054da62b0d048b2224a544df84d5c0de83e4d4489281125ea1625e248198e9bbbf564fcb323c4887955e4ae5b314a9e129bb3a227931921460b5bc46035b9686d8473578890468265efc583f5676922eb9f1f17b887b14571e0d6ad1953eb7adf4f78cfbfbb901b8e16f268e51c10d50b48480909590bc40b325e64cccce8ee9e81929953eb9d99191d9e223c33333424071e0d1a3468d0985e329cd83430c410c30c00babbbd0800e6ce9c29c85d4f0d0315987ea302c1c88a66a8c98a5e2b0b0372d1d2fc6786468d00a0d0c010001b988b1862b01f4386633f0058fff55dacc1bec3ee0cd1a18f7a7b4fa6d6fbfdd8c70ce7dabac93083006668e8a076a29f7836dd48b217b9401a5b08da740bf62694bd5b5af4900d1b36e2dbc870e2dbc8e0e3294832774d23dcba05b910000d987c42eefad1962dea3f34cc0c401221774d5b5249830110a001399cf387000e4a2660239340ab003337126043d08d3880030c20bb0194676eba5bce2dc1cdc87a5ec68d8ce1604972d72dda26ec3e9212112103838e56d9b3220318c000d300190efc1a5843bfc3ba055544e82b5a3871eb16916b8003d05413feae616f80a8af563c23c33173b78c9091f5608c83c371d4d035ccd8d0dd91880d333466482b186dc460525a6416913480de59dc17d35d56f6bf2d8526e4de4f0d57f9241737393403785930c7bdb148228918312cb4e08269c98d000f0d82386287c562b17a60f5c062cdeba3d73c0c934c2e1f139d8b4e8a19beaacb33c9e03431dc11b6e110f7c5da49f9f25b878cd02af15e440f3df46b17b0668473fea09f8cf41d6277370a5873e7fa94b89b833635b81a3c60870e230d21c0a43925c771d0e600ae721a1632dc6e542335589073ba9c7880b9196e43430d216a1c31279d42b307d008ad268c8004932e9450638a175031040f90149d200b209b21801cbe17fb41081d07bf0197df3f0821b43db8df9f7c33ca37eb0f9b9f9a00f7cb6a332bd68feb93e98736f6c66dbee18f83f764209c715514fc603fd57bb8751042282377efbda092dc74a152254d5a983b10c62b899a25c1fd9285dc4108e103caa4c0fd302646eede7b0f8b526ed8a4efff48ce759f5f0c945aee20847068d68a6384f24798ed8139495d14fc9282f3fe751042a8422acd95f79e8965e5d4633ab54c1ed4dc81aa262a72ce143994df8b0b4aea05f79389270f83450861041a1561f9c37b0f422f324904612b3ff903ee8f8f6d98fc21460c0b2db8a082ef49bce7e40f5285c91f649c31be805921ec8f0fc1fd72bef7264d8d19325c6646cf7460c2263219cefb07219c919921e30515e7420b2cc4c4d8b48749bdccd4d8221ae1dccb2f46efb901218470076c21904aa9168a5a61319da4bd7a10c13e7e47bad3c5bdc648e3d47a50bf9d7ab80ad52a97528a6aa19456128974ef55d17ee449efa57485d24be98ad57a56fed24a405c35698b44addcab85b6b4d0ab67e5add6835a59d1b08c6aa112d37a48a43fe99c20762f4ae7fc97ccaea450b4455e24885d3b6789524a27a594ce9bba52d774b22ca415146d9157cade3927a5294a3f95cd6b7f05455ba4dde62d514ae9a494d27953f5baa6534dd93badd653fa4bfba89528a574524ae9bca9eb9a4e29cbb2915650b4455e8da5ec9d97d57a5c1ed37a5e1ea5a15e4aa4540ab5c272c2ee957a39fdf0c162d745d216abf5901ed37a4a8f6a912713cbca15351e182891b4d34a3be938e78373f4de3badd643299d72ce29ad954da69c734a49e99cf40ab87fca2653ce39e5b5d7bdf6b25a0ffd4b7b4157c0dd69751294e7a8944a5d2a7a18aa1a190000001317002020100a08056369922689be0714800e7284444e4e369487847118474214444108c400088210438c42ca38c494222b00008c68f1479a83754fb87b6a119301b12527d4825e18df1d146112ef6e7e27a856bfa37d87f6120a94806ee05fe4e08db18cf88b324792f332e8d28f141673d641889a110489209eee8ea2a8b55c71b30afdcee0a56e0a5150353a0b25a37b3c34bd3b5cbc68686134c204b072a73824d38be3bbb98e846045b3d6de1157e689a8b75d05d0073ba093333edec29022267c5697e39ffc66fbedd44edc417d396f0de81dbb155a02c04df1d3fdc00e803ff27131125efa17467e7853d042da240ea4ce53038c6a30747ac4c3c567127469753ad7f1e6bca14986454bb03879c049c58df491c7565680643a1283e6287389b8056aa71d005475113982d76fec93f1c6fe1b5910ed948949686ea126c68e9042c9558360820d445966d2b488185ed5d9e5ff3870c6277bdc146684444b140753fab2e6ac80e2e321f22139cf320dfa738cf48380f3f75ad5c73bafaf38501c88148377454c4af62dd87d3e6b8082823a8fc03d3100c3a05d1f8f722ca52171cabbd321fd572a1d37df41ce3555227f220aa94a11ac8a419682c526a8a4c86b949133a4d37de1b691eb06319c464db9250a3a7b030078d89bb5f668a7297c346859e43ebcc84af22473230a13642ff98e75056589c92c308e449e9ca09bd9f8d2495cc6f698f56eb2c7506b1c8027101f2501e7bb26f0bda71daf025e88490b324381d80593cffaa029b3ef0e838b3cdfe3587b7357a6af3220057df560f002cd1b6fe89df9e20a0e0fc3e7a68e277bf729f4ca15401c19da45f4ecdb07e2431214f449188ddf81dddb7ffaac16c68b715a5e429fdafcc75673d10703e2d2ee7013f7cb0492c01371e7f59071acbaa6fc505a3e4d04ba4585d3b324b757c12f2ea0e09c94ea19cd9171a1a877332231344e69402e323101b9e4341404ea875f124d66333a0e9e0fbdc774baf63bf18e11ceec44b7824a04a71a2e8e4fc90a5791a5a8a7bb02f9b9227e4884cfa19b7b812c11667b7a559faf41f0f94e7e5b3064f539609416faba0b2e69c9cc3437f4bec66674184bd8aeea647618157cf99eca13131fc52e3ab5236da20c09348db277aa4508f8304491e69f415794a17a47d4b7f44ba46f8c5452cb308e964395f3f22dc3b15fb6114d7319dafb202a639429745809606fd5a323d13c140b85e512b8a1ac942209bf2a62707e66940d8fbab6245c56f239fbb738b810d6e4c6a4e6c6d922a078fb8621eec7c25ccfbeacfaa4be7e5c253234e5577364e30bdb410d955971be4698a81feb64f25944c2e068ad6cf06994a63497af1764c35217b554574d4449ca4f1bb0e4c24c05cae01f17ee12091732628065a612a5443996267f898ca475639222d0585a1c6e5e0e1b7394606267c7b1760ced621cfaacdee5a3f0a31ccb1e5f3f2b565fee47db09c3f23404729032a1e02242216dc22dd3de73f9e9973aac86c0f7c5a1133e1546be43c1bf302c8ec2f617746b85180f4fa059f9a00c246b6708a2408d5ab148fee230422fdabec101405fe8d1d98c98151cc505588c6163e2d5ff47210b45383869bf70d305a87cab3ed9f8dc98ffc3309d0759d840184cb49d0f3e3f0d837d43788575423ee06a6008eaa53be24a6f159c64b654e1a1ad0b13e78537ca3e7b302b83443bef0ec9bf666478be37a60a4240cd5fe5383409d4319c01f5215911d434828966a070bd92641fb21e356b113b6428465508e22240c381f2724995e6b735bc9b123ebd9f5445a0520c6f5ed11d5dfd0acbdb38dae9322ba47516ab6ab35011b637885e67081664433dc712c34b78082b868d7feba25b5be1c75c8097d59482086fa903bc8584a1bef009d8877fe25a5818fe65f2787e21df63184ef70ac7c0bb17106de4ec4e49a1851c3e98fa54553067bcb35afa131840f35ae1ffd84f9214793c52abbd4996aec80efbdfc050ab7ba37fe6b6e748127c2e0417bd1403e255a8c9b71710aa027e418522412f546678d6ad8ee5fd4367d709a61b6821208fa0c65a71d0ad9723ffe4b16df1e1081259e9bf88811efa671c3bda94b7a248ab062282b4e93896b051e26d98d11ac4dbe28c33712cdc31d927a45ece7695f92cf4501ea41be203d0f7a33ce2ad9702cfac44dc1f4e5690f574a610052f69ba3212923f270b5d3f093d6f4662dea0fd98c54d00a0ebd15859264c2636b6d50bae81ce7accde3556f5558123935288b749b1347918b90117c0654f10b4771886d386ddcb0277e5e1ba55e0de98bc13446051499329260b1b2553a7c67a936e9b5c855ed5e66c3c0b8c768848a792a372f8a1dc3a7874d035652dc6e68c4b2947337740daad9844403cb84cce932544a1d1b0c12f49fc946b1abe74710653c7888a850ac164c1a420441422768366925c4bd5e6522fb4e6219760ec0717f406f12e08c39102a586c7c3b9737ee8bc2270b90c9326a4ff7a63d6d40f8342ff0a59af122e0dd7995d4b0f1a08b9a4ff8333c934c240acf50950b2cd164dd5ae2239b47d325897329fbc24899b4639cc231fc71cb0708176a8c2b26b425a326f3194d0e6e3a157a80cd93d4a4eeb1034bf502221b9888198109df54084ad6f9b23dafaad1c4f064f1695da43ba86b6a5a73ae74dadf6bd4fee0928fde71bd64e6fc0f346871f110d68b874a3c8e699313889f54677a911c6da8b3f36f4ede1d98925e593702f3d7cfb22817e9150577e3cb0f2d847f2e288ff22d3fd99e7544aa3a198337b563a0bfb8e7d6187ea5fb433e34ac66c15d515556bef5fe4eed581bd512c31b324aac6335866f7a8b90182b5502be38da16b1142c39a3bbca8f5acb874c1f7b6c6715bb7a7de45247df5acb21b42d179ad141ff257cf72cae2a3829e7d238388bdeb12e4ebea4346d4efbd68745ab7708acfcd6c992217420623aa7c53e2b6bf43074f585d4989acf1f8b9393158104d1140e193a5dcdf2cc07679477af4ef81897bfe7f3034a9dd8ba4346e35daef10eee248727fb004d0d179f9117783114cc3972ad80647b069f9c081cb86a30077cf2d33cf15b07a66fe849b3a2d9804070b3476cab960f33802b10daa4804856bd20855083bb57cb3c9ac7408a991e710406bdddb8694a7bb85ef8673bab4f5a5b189c66ff5534a2b554ec65bfb921d57baff9f9ecf64196f125f63f38719260f84683d5aa7119a7c2d4c9ea134c8ff116dbcaf4eeb2e19291f1fa1b356c7608b4110ef385f8c82b44d643a5fc532196241692cc58601b84c494200629387b280de3e18d82ff1fc5f9912546af272932bb492521ceb1c43d405aa1f286c26fa8bb26173b179e9609f0836be21f1bb94ffe3e467b366d21cf535ed3bd7333035cbbe3e81e603f8a107e365231b6ab79fb51e1bd90c8038dcc68a31609219a6205470d4cae1a1e368885c13d3dc084d1ebf216b405c0bd930875b06c90922187f031588e783ceb331c2d908da95184e943eb720508fee951887cb00ad3d1040850d6078dfb3e9138f7b149712af6c1b23f5f1f9d635a50767b3af984e55ba5e32405823967e4a39bbbc04084b35672ee4c50be5b3693cccc49a3082feecef365df0dc73230e8465f002dd090dc270ed166f81730188eabb6436683a2e586b0595506bffafacb33d4b51bccbb8601fca1128d710e9dc32ee78b386d383cbd82797d84b8718f147a5d15307a79a5d825434e080202e884d3957d59da7fcebf0eea6e7abc50cb2bac656b3d544310cd819f2cd62ddb42c94981ca141fd16a856c966266b0dddbc27df8bafb1c860daf03453386b38d669c2e3cda0f84adb57f4149ced36f60b216f9db5dec3036f07d740ef7e9b19736a47834ab68b533e3effe30958ad72fb5063e3c49960c9ec9c4631c8927255301cc34716f422a61643d8511ccf2380454582a1ad9a49d75c14e0bba34a3d4bdc37137de631179a85ab7421c65b3169d5702997536f75be3813e148bdfb3c2c278f279d768ee4f1937ed56f693f939932aa41e310503afb6c2bece38efbbb8c3583ae54b36fa2e7b6be10df2f833f8b0e53e60a0541cca27734338ccfabe5342821f7b34a202821761c2c1e424e8a2110bf3bfa801c7dfe764ac82b1530b34b56bf5baf37dc9fe2faa355e33542d18d49bc11587c1d49d5541ee454723f851736637888e5b904842318f0882d9e6e94f2b85f35d877ce42541df98909579647ad3d604c72e2fc38f0e0c13d333793d40646c6c182f34755249f90443893a4fa1fa420aa249100cd81d71d5eceb313fcb1bfa35252d651964807b8205c92b9fdcb121658b3e7b5b30434348cea8fe019211ef46c0dd45184153d79c37cf4df28af1d9ee470f4da86351006b6f01bf923457b556090b8bb838c68aeef67c75af2d9f64667ee0dc78b11de9ee414ec6400dad8bc21e7c1dc31fb7a0b3737cc457c042208d8ebcedede945188c78699803d47c9deaf8dad39028710ff57a29c77112ded1c98fa17eeed044dc006411a80093e6db1b0009e3f0c2ec8ca9c5c68359d8b7b4fc8a9946dc201ba802511c157a12dbff494f9a085beb7fb5a9ec5855dda7000c312bd3f56e539239700ff97519c541925cc5c8cfa959eadb40282c9fbeb2425e1b858d244058224077823f00541577c5f89510cc6f406dd961def12b93a8bedb155dc0a477341dc4d0b9e949c10a77cec0302bddb8b6cafe7aedcf1724225a80534fcbaf90c50a27df054aee717ff4dfadc4894153fb1e4443d98c53fb0bd3e775fd012b482be838e88dae85a162299466f8436e345b752dea9ae02bb7373e8612844a0ccc6929b132461493ecbeaf4a69684de0ac6eb6d8188d699bfb2a18e2627d55028d2bf5b93f0ac14301648caba272ca3223b2591daee6091b6770bf4d89695dc46dfa53fa21a7ccd05c56bb703e2da5e2cab8c39b0c38d48728b80f3346a20a6d0d646e63d0688a64125395b4c6aee281d339eb9733e2fb906647e0af5465c0b78232b7bcd87d9426812516080d7ddd2617061932bdbb058f0b18caae742254277de07507080f7dd22a35f6c72655b1658a9b47dafa0c60f6dc3663ba34d6034b630597c9ce78dfd8e108a3ce14b2c0e0f35be2d5e1ba4cdec0f91d0b31a0e0414b33cbda0445f847ab00f7ed0c6be126352b13a65c8ce5f99bdcbeb59ce3e03206c12d1374fddeb73b4437bb87a2945f08688094487de44892ee67c5e12f41eebf17c3ce7e0d465cf137b469f7be1e3e6d953cc631d86e69851ecd8b6a5c8d17dfa82acf1e5fe435352c04e3238100c2de8011599b7841eda462e2461d8b8f04c2171a2a463644bc65b79d7def79edb5ab4ea66b96db2bcd3bc241a50189141684159ee1bf54909f736eb9fa9931c5cd841fb882b72b487a7af6d5b1f5d75e7eece511df457639e7daab627ab8144aa6357a0deb2664081c23ec62ed4006daa9e637377cfb995ca84ce9294f9e90a32f50cbf367929f5562dc136594990dbb916e3a6c5bb3c51c0e55ccbb5ab0c5c419e8efa993fb6402ed983778d377ca9696cf8508fca19e961445b7e6a149fd53ae350a09b847e7ce034fcb6a0bbe18315be079a7452a9fba2ae3f2b43c04b91331e4689d3fd12b3b4b5d6ceff19bb196b451fa9e44d307776af08a9f8f8913917f37f8ef12e4c6158e0794b1acaf7830c1fdaf5421c224f094b7b2601e6491308c80156e07bf8b84475d7eb8d2a0931f704ab38cdbde2c01170646c447d726883377be919d0020b320110b5066e40552a69406e2790754b00f9409eaa2eb48690d58431629743b897e63a4a86e6574b47511425a979f6e7ef7e6e96f5d00056f59a51f0ecec87e8e9183c462b7012b607710f573863fe55dcb80d33f44bafe6263fc5e1f7bd444e4febbf6b369875583761502997424ae76c5f39aa6a4ef6a633510c7a6c375e43593680d706e3d5ba89357064ce50651b5ee8e7c1fa4fe35ee4be80c61a049cd7b1b2f419a0c1725606d994eea6f8178dd41efa9afdc1f320bdb3820220482c1fa692da6507690669a126804c5ea478e26f721d863ee26e1d22bf9da9e40ea9252b5c527400fdb7e229a216169b0bb7b5fe7bb4a8ffcaa5830f6a8b4808437358689cbbebc28a2d0f868be9613e810b893d70d5b30456223c450e1d4e93eee18949f7bd6dfe8dd99689e9523997b4179c939ce0e7655fa009e272354bba3ae950824e3f482e9767baaea2eda0232303d94a38b95db1ef5b3fa66bc6521369215037e55e304f621bf75b1109eecad58f0681fa458569bdcad963d8be11fae95deae248f342795daa2b9b8f83e291264f01623fd33c5e7e9d77604eb72d1709f871b67b7b2264cd39f7ff2d3dadad4713732add49c5b6f4bb1d867d819265d678d84572792b243652ebb122ebad56e38721fd1e7f594cedcdc1e434f793129fb1061e6d24ea06746dc4d9e3c388d5a1024014d62c9b657374b8f804ed7a476bfcf333fb5460650474cddcb23908469920302f7d655afa0ca3aba6828655e4e0c21085f396913c5d350e67c6ec809a35a5efcac6d4ca42d6abe136b89336d4cad10f7ac5a18881ca070db01a3ab52466b638d97c4fb8dafc40a222e2cf683a121f7628acb6f2bbac324e999572398c6ae90170bed940146b0ebef3bbd5d9a8e93f67b7b06fe89aea823d1c5911df0353e5094bf7d9a2a9d70effb373fe49aa7f9edb88677e70acbd4983e7be79c6e90691ab2797749076029c998ae03c412ee55263f40be92d40db3e8e4caf2637bbe67748f2b7e97f761b6c288c7228c9a8a09318b7384d8f25e00384a01efa45f65380ea004bc53e48f83d7b971fe4ed57f38ec525b22b1b7f4358537a9804f8e236d17e5b281e6f400cca2541288b015d066904ec5c52fe8f4c6733f562df16abfcd45bb7145851f91f579841b05dc7f77ce6c288711e6d9cddf9124f622eb6fba8681df0ab6c9734097b9e96bd358af6907ea3f153c2ab22ed1ae53ae8746f861a22fea0aba0c99de40f65fe5c98ba41f791dfb6e09c9565372683cf6857a1874841acc93100d1e71a534e623dbf66bb6defc92d93986cdce0f1a221f603a333821283ddfa3de4887a3c297670b7c1a8c23e0f86dddf715cfccc39e3bfd7e51d15139fa98ae7dd4b4180611c5530f2774a2ab8fee4485f7fd0c5e058ae0a506caa4b4dcdafb58dd58f2e76361620015375b5e22ac6612b18e45f0598ca09a3f3196414c6d07141bd41d0ce30e2e97f0069e95cd27f3c1e714a77632062504583415eda3ff452e9163d53f495b9d8d428b6e0f1e6c0967f165c8835ae1d8b173a2698858de084266e600c5251cf594b03e30570d64573cd54e51093483b6badf3302b892afa7abf87a9133941b45166f69244694804695524d18cab7e7c42a0aa386604f664be8b012bc5c2869498c2a1fd45e214d1a768ee0a893ee561603890a24375164213fa97967c5219b6f995c3bb80c4e703e58f781500f6110d02388661af058b8bd7721ef88be0e87a9e3a04311d31eff8ada4ce7b4f7a2ac711c76a3c4e2ebcf7264bb1ad5996f71a1c31fc0be902626ac9d4e8738fa944e370ad4741b00c0563d2ffa307b74292eb4df258f7a690fc837fdcfafc1c8431b76c17991b782eb38eaecd4fd787ba2dee765697b8df6f4fad3584a42b0dea5dd734f5b7e1141ff2d9a9f06d8938bb45df04910b97c654b0223301d9240b699b2b27ed3f78511aff43754efdf77569362d20dc61b3e67013c40ddf60e36c5acde68260629500013a0bc2df95cabcaa2036e26cfe2cce65eb86e891eec792f716e764971460c6c4c9ad326f6b8f31e44901797f20599dad551b237ed4c1c0d151a4dbeb254cef1da266df9be4653721a7d449090ace19ff521e19fad6becf93f81da0c998fce844a135b31580b60cb545ba3c023266bc1a07bc96f31527cf3ed69e93d241ca2fc593243faa672fd7bdbd9a25e6f06e08b2b3b2a683410ede3fa71fe70e019106ae8e20148835256f4763c61b687c441bcfb8b681c5a3f0008a6d6c0fe53ba53c0dd7ce73289b1314c7c3347c2766a52db8dd1aad643f05767d5aafbb116c5a3683dae2932e057cc23cac0045071b5f3db3777c0f0896c48186e8ab91add397e9a3c63f38d3c3efcaffc12826cefcf2743b817f938968a4058182efbabb8026f1965405499507bae8c1bac8c9fd033ec565d213f30a43f0fd18bdca1c5e6eeffdc817d51e9bbdff133bed9d619e5ffaf1b84ad11eb466e640f6b59b3a566287ef8c554e60fbf140d5d2e45a6ede902da73728496cd5f33c5ad1136144c5ec3f2a7475b93af53227f8a984b380012e30cf90949b203d48b534d7a9bcae7427d0c6fe49a3f3df576eecceada3dafc95c16ece3d65d085285bf2f39621e7c0753facb1171c1fc083caf7c12ed5104d8c296c20eb8c1266519e3cf17937a077ca421def207bab36316f833359a61f190cac9b0af439437f80763996363a6fdaf01abbc80eb09937e51dc608d7144c0a5d8538079a5374d447ea2f296cf9a911068e4a94cbe21f6f0a489a6905067b85926ea99029c385264ae4aa7ed210344426548bffec59666c616042822820ef53d81aa900f1d3c5c1930876f7d7bd6fb500f3c7271d19cf4b30c0250a08dd3c4b9519733d606c40b7d525c006148bb04682a1fe25c8a2e06b965a61979bd5a010c3aa3c4dc0242b26ddb39d354dfd57cc6aceb8296b0f6b7641200200a168a3e409f85f2435e058d3a50e82ed07e0b41cff7529237bd3d72ac60fdb9e485bd405e0fa9086e210e9b1eb083210c8e76f0a8dae8ea8e879815847a2a6e0e78b06e45af62485b5dc782552f45e6a73a839848efc6ebf39a46c06c3abb11fa354d45076082da215486869fc5f04cf203345ab22dd0749513828d5c3f0384a48294694687aa67141ed9e5a2faee8bdfb95bcf7fd431c46e8d47cf08b2703e64a078103984ab4e0873d066dc6ea5698f595c166b0e5d8c8e1e21854e6883485601dc2faa615aa346dc14dd56c15cc7bed552110bb25ef8ff508fcb466588a187949c93a2441b6d315076bf579518166e52d1975d9536b9279409325d9baf3e1e0ec8a3f4293e2ecac044539cb2874d97e13bae971a1827f03f9bb300810f14a3ef86cedb9bb8582e3aac7070bc8b3d1199f83295096ad98970e5af7f1c684fee53d76c2edb6e28aeb998dae30f83e556806364153df0b6e62e28926f72dfb57bbadae12e6bbf331664321a5ddcb8a1de41c60c5303ebd38730f28833b5272b95aa9c826e2eeab42a942f9c01c4cb516343307af50987f608d6460ccdd88ceff70ccd6839e64e6d64d0079958f06653f34710ffc339a9cdf0fb3423d3ffa9e27d0dad609c819e9ff9ce16f667bc81f3dc34fa58569bbaa76b83d848788aecaf862fabc4a797f5f05b2daa47b45ffa9445d21c91e43776acadc66757477aec48d07115763e594ab0d27317e6045890a31914982a252e2a48e21c0606e3e8a4504624f3ee58d0e66ff17c895f9e3b6884fbdab7845c066a7479cd2a187621b356434d0420fd0366484904d34402b796f413eba44de9d0ad1a32adebde1dde51f2e686bdc7c81b80204a07294ea29aa04ea8d191410820f987df576c47296a15bc6c8ac7a2f9b4555b79fdcd52713bc9586d0314991d1a563dae49b52c6aa551ecf23d5d301d1e2c62d183e60c2572f1547324307551902d79310ec1947535458cbb97a506d66fc20db5050bc80ad1daf4fbc0ad9800dd91565958f496e731aff66f7df68ff21fdb6942898fd438e279f21cb8ac93756a59326dfd095a82549bac2bc448078a66f285e96646310af50da2b9d3251461c60742ce15a0678108615dcec784ee1129ce176ae1f78c518a3e33c138d0761e1ab769ca3ddedb82f1776361ffee8925947dad6aa3a1e87cbc1a3b0e3a5f821d25f151424e65659f931482e58afe32c0feb3713de0c4e83f1358db303ac284a622848c6512a651d4e108f0998ac4c680c10f3cf3a305ef7b3504b21c0f0cfa1e599ad7840d5b4bd0d6e586785e29221ee70f058a74ba3b8cf1a86a5b67a631cb6188d38c3e4a428e822f579a68322702de803ffbafffecf68a91c684c0124de90e2964f8c93daf7eba677835068c9908b70380e0c0b02d6a35811db4ade8077913ab38a98aeec44029a2df8431a944895502a0cb4a821158c45a57830b7160a5e14e8cfa15493c6622a493c0f8ecbc2bf404be3cdea8d2720199cb7b6c0837cb59dc735af89792c7b87a5371ef547683fb09e32a51013db7d7db286943441abcc5e8a25b194649da69e314e3d06551847a35960448f8b78bfc529c7b9f55b16df5040ae2e18808a42985f3e935ee004a7cebdd0503cedfd8e74831daa9eeb5036b4661e0a222fbdc35211723fd6cea2e5782a430ebd57844ce2c2e6259f4f46f19b53e462718d10b07544170d08fed863aea589624c8480b708bb97885104d66cfdaa7af703cf42bc6eb8fc854744b8025326cca9685e05f82d4f3bb69a2a414368954bab1ab0b30c16b2547cac00147e0881d22a9f2452698db2c83beb594ecc86c13b5f4ae0aacd994e8c7aa6f3f42265274e4e64ed537efb19c79112685cb5fe79f13d2f11a630b14f221634d6e6e82957816ed71f0a2bf72470dec24e72d9d1c040c701728db3c0c4321ea0133310889a06ef2f1c42153fffbe59642ed5ae3fbd838b5cae93973dfdaf96a5a4cb3d1dd7a1602dd2444fdaf02674c0a082bb8b53f35da046c44298aa33233f50bc2af9e60561cc701f88745f07a22698c905981774a1d6eaa5c3c8a23801090aae1818262d0fccf4a51b5f074a4c2b44b5a1fe6562c3721790b2e4ed41930f728678f0a112c05a5b82cbba89bd9a2277ee30b9b57e90babe200828a808cde3fc55d7aa5e2622433717a327a36f84615bbd5e6d8a5850bf8cbb5cae89807e971025598c163fed33c30fd77989c1502a36cb4f9aa6770ecc36c48d726e316fe9dfe8521b2be7975f08613b69d00c3a0b049c593b7a2721898bf0c527c4e35324710a82e8db781ae12517a63c4b847c20bd932d499768c2e334edaf55adb5697a457bfc8e87069efa8687bf8db4d3b4187826fcaa48af8b65a3c6d45ac142ad526ee76453a5aa6e7626f15a2da9476a1a76b2388010ddb0703d1751d6450dd581f9d97b1cc443210d1f9788000208763b9017114d9f32a6749ceda32d3c6c37bc85159c06baa91507083c51c8bc8f43e512b1766f1819e29819f4a78ee0833f011fdea4446550a11406372c2349164b1e64d5b2a854e1a55ea8d31801829575e6d13df5c862464e5e685fb28d4cbf8614c931ca0cb5017867fb543dd4dba02360b3a8192adcf49189c35eaea131039e737798e0f2a9267ff87f7c07dbffa264751c69ea53eaff7cb79a04cd869ee460b70914459de5eb9c376b9eeac4ea5cbd8be836d684543a8e96c480a716e1bae83d13136191a195a6bd71bc25e0d27863e115e425026ce350843aebc085862984e68de3d54a2f51a33aaa96d575a84e0441f6cee7fa94619b37ab3601c074b9de0087a2447537754a848c82fac290051529655c003515bea409b382d54265073a24553ae8ea51057a2ba3bec610bc1873848b5d03a15b5405539e5ab9b30be1dd074136e87888ae016c8c9e56b80394ee3cd39cfca1debceff91d3c074e1ed224328e739b5d856c054d0ae9ed4e842c9f2e60a7914fa0d9a7d9e9d539172191d903aace5df89d4cc9b91448ad64d17e1012c4bc6e0766c45f188dce66747cf74a632ecbcb89110d91b828e4cd27737403ca20e83c508a973ef8b2ad709a6f16fe833d54dddc5c31f06ff67eb955457186042310fb12b87be6aadd17b600967e6d29a6a081084fb8353a73d3e46d86073cada0a701c70354e965ad939988c3bd2cb540b1a9e8107515380598e370fc0ee341e1205c66fcd277153f858dfa4ff1ff458828b59c09423ccc8d240a17675024403da1968f57011171830339ec382848e7fc0634486c1933b91d217c00a11841cfe27f08fb580a0292ae29f9003f591796c72e06a4ec093d7968a2b4291d5dd54459a73920b0426957561cb9c54d2767026a88d627b1cae9584b9198a847b323ea4d0e22cbf171bbd788b9567b6de566604966f7a409d17846bc0b30254a79632c23ae1faaae190c089fb543f26f54ff882b6302c4c75e972fa815296b53edd8c64010a8e9f99b636b46d3546f67597f7b5ad2a6b2ae93e0572b7128340b3e725879a78068752819c06d2239324746d355f723b79fc7318ae1ec950c0ff0afeb02e5316340dadc400f1e5d94dad2d11d1a28f334070654a8e8cac693b7b9e924d9808c787d50ab1c31de72148bba343ba0b891c364397e6e7a0511d7babfb5dc1c50c2ec5faa101b87117f01f3a20c6f18cbb050a48b70fedcec480e9e8f82812f9146a47406f8ca4185dd4271f5a06480fb24cc895ae9a9b42b7d4897c2a49c8c6e89c2821fcaedcedf20c2050929a95ad7d6186b6ab8375592a6562bd4555a41bc5dec4ee3d056bf5f4d44a261909f1f73cd15fa7fbe1d0dc2eb4f52ba4198a8411468c84a4897a2da0446eb77f00def597e3f295a177c4d9fc03ccb3d15757b43abaa517a9adeb0ffd554e5e497300ed21e5b89945aaace86f7f267f8a4a789f1555fbfa6716f32cfb7bb6710e2ae9db54bca6ae82797bc3e80a9f96f4687b98f067be81e457296d0f3dcc0c20f7a8f04d45d2a6e97bce1ebf7ec213b0ae5cd7d796ee72141496b408598aa4b784b67d32366a8263df2cb22b205d045d792a4fc832961fded7d77d94da2c24e790e09211b7e127e50f046b931f168ca2218c44d640c56bcb6cab2982d2eb03927ef141d4008572a264c2aefa7f8b89a8b631f2d18baae34eeb1463aabe5369ba1ae8a77a5f0678128c48856fafd847492752006be3ec3fdcfbb0f2c758244fdf7daaea0cf7c08e4355cdb269eab631e7908b45e55ca2e087f271f963aff5e451aac0c7067d693f6f636abd51f534d4e472702214a559eb080c33616b73dc1d50f8918570a9d08000d6d8bb9213fce5e5444de063f72091b7609459e8307bd8b8f4188238ff52b1e65d00c5e2765ffaec44d5c4bfe8c31d4fdac5b46a8a954d4e6c25fb18aeefebad8458a77cdcdab38a4e3e182ed2ada750909f8c92ff07738ca075600e3e936977d61cf64bade8823b46ada1c398e5edd508c236f7bd15bead1f9f6fac4a3afc599286bc389cf2039fb4dc608976789a226326238545ea6c44b27a6e80c4d113b856619215a2ccbb6930fe836ec1a8e62700dbb4a3b2859d72ef377004eb13dc3eea5b0117fd0adf01110ddb6b5fdf2e8c902a74a13f7caa71678ba222718cd00839da69a1f762799688b55c4a9eab1e6f119e247330859e40676db809e84714699b6c8eae06c2ce7b4c8fc5a61256556de396361717400406ba2a84515cce2b943e06f13ecfbca602dfd45404517272c6dacb1958fafac87f61b1660dd600bc07af433d4b934726885bf2e5d6b2fe40bdbaa59efb58eab8cf5f982efcd2e61a655a2822b01dfad07704f1f52f04995a5494d6a31fac897eed0f230486fc1fb1b0e680e6824c39b1991044303cdc22f266f278cea62ec11242e7d2a9b5c936dcd92c5751d8a1646a17a5804546926ac56d08ec98638fad3a419246c0a7471f4641e2b59623365f47e8e0ed3d634db6bba36b6de2e8d6e19d80a4b0b502523d2886cec1ca6830407c1d0afd4712d5169d4ec691810767615cc0a8633e867a30562a40f76480e8d1c16c49f95e4505af75e6f8b8520dba385260089b5ebf1baca34e2b43401c3ba97f085ee53a6376eb3bf7b86f160b18b2c495e8178712e57d221cf18ead3f623addca04bc71e0c359941c35b99b2da025e85e47aa53860280358472b03eb40a00d7dff085e91fd9ad5e75cf5db2ef738f8d654577c33244191a66743f105568501ea97c81e5cbc8e4f4199465096e1ef4b7603c73689a48ef4634dcf6e26f7ef833999fab8405a189aadf4e160118d145536b49e732b719fdee3cbae2d97573fdba02d1951a37028861f8fb4253522af28a75552379ec06b9ff90704fe00d32458bf9d1432172346892ce0a2d0f005963bce49e89399c07667020f1a1b355cb3f004077ab1482f1bbda83cfefdca3397c44fc86b240bd020bc1cdc8c66d76b1033196291496c861f48efb66754a5793187332509ab7f0eb96776ebf0f03fc40cb37716b8d26733f9af3940202e1f43a0fc1a28a03f95966277505464bf4b660270c9c43fb614d64c8b7ec03a9081909ae98dfcec3337ed2614c6551d14c7594f48696ca101c56b5f07e478c0ee1486425de3bd8c00872c61911bef0f10e64b9862b85562dc342813d15238e86d49bb05c912010dfe264ea8cf388588d66739d3c5b57ee71ec8873ec2d21b55e062ee461263b001bfa0c768bd5458a98bc6dd067091c6ed074e51143a413640e6500d5ffb4766c46cde5676fd184b3ff0e6c28159d9bc0ac7194f40687d320ab16360b0a9e2ada91f27aeddd936d54a59a92fae9d5bda8d840faf02298323a3ca971b21b39e84d009045721db6343577b656a424dc9e3c366c30a41e54e846570b311342f8236b54bbd0a60c780eb0006375def128d67ee0c793f4e14d67db33700bdef9a689ed32107964621b3216dd2398adc87d5de2f3340eb4547fc936f9661abae666d8f9e5ed72073a77bcfcd07b55d8dd5ed4305481ff2101616101a8c2b88f1451f654f3015b0e0139b48806761001b36223381cc12bb36381bd09ed1198c24c9f10ae9f2a1a4aceb7e41ed067fd761898066cd0d344e457e7d748611876da64deccc72f8ea009c1ef118de6f85510dc2e8971c5459bc84ce5c1e16d2e978d6302bec2af4180d9782c7d24e6d0672ca168cf3800573ac18674c9203479fa1c36da85484865b954df84a0fb85dc37a34a79a506cab2cbe71b4f07d82f14733f035bf81d090881377fd2f6e4c67eb5c8205a931258f36ba0b468b87f3a8b701acb945bb6190625836ccd84e0875bcd84470043e34dbbcc3342c5a8ebb4f893fcd2d88e23271b0cb64d6949ab1b80c0d6d1647c39e7b060c647af3fd2bdf22b6434e88aec9502bb07985e252d6ce96bec92b070dd34027c3400f98708846332c9a369e4ea3392e5dbb459cc1873fa59e5d4d5bdc7407203e16b842153440b14ab632241db3201266c4344a52a62f1048354ed27d14e35f183cfe5405a2f94fd23bce51a4b25430cad7eefa9e70ec905cf841ebb5e5292a73d12c6be9af412679543a7249946a6c27624b4af977e2164bd4bd285cb4c7896b17d132e262e0cc40a7ea1e38d681537f46f8db290ee78d23ac91903e999338a4e11359d0617e4d81c7e541d899f6e613eab6147ef284a81048142b7af4e8ceebd1b6b40ae660211d8ee3f70057a41f0746409e83a7cf1a7eafb9677e8244e8b70a54953c7528ecf5a1d674e717067a0ea583d0b63c8a518c50c66cca4f93f2b98ae17744ec71cf5029434b96815d916380927eaa05718d2f5471f50c343472e49b6f1a3d95dfefb936074fbf20fe26fae57a2945d1245a65b0b7cb111f5d0c38045a5a7aab8487d0963f58103b91ef31db2cd6321ed222ee0b31230a919a741de745a1800132e41a9091c2e2e16124e443f2c4b1a9086f0712027f570cc84e283ccd08cf723e1878639391142764c26258b4f10ce824243d8a98bcf21ea31a0fab249fd48fd1e24f3802d070b6fcf2186166d0e2bfc33b9f85e96a13e0eeb11905611c53c276614b7b17b356629551230d3064dd00d4a5983cd375530acad579d17d89cea3e0f31cbe1b1b4297b2c200e068f7eec5ede4d964d2133c461991472ceee34fbf668e5a322fcae4ca639266c6490084d4c80ce4c88d5ebdd9e0b29db7e17064e036b061ecf980d71af2864d199707afa2cca23c07564c7833bb84ed4f4cb7d72c05bba8db0993f7439664a908c629d23876f28744cfce50754ee5eac84798141f6414c429fb01f4bf6330215c562a63b32e4ea9ac7076096d4b71e68b5bd2a100d89cbbe0e04fb2388ae81543cd1f215698a2ffca967d7bdc3b03fad992935a20d03a4e078e231c8c290f5641c94eb2d5e39ce12f4f6e17ce69c51cfc816092de623291d2b0584e7a95f5db5400a4820300058068bea747ef7107b8ab0f4e1882c956521dcd0cf172217723ad09dd42dade6d4dee2da54c49064b09e6084c091e5bef3d2d6b1e0f65f3b8f42aeb1ea902939c2713e0616105241bdcb296754ff32419350e2bd830dec65ffe9b6f5bb46a5118160953f72f6cb698c650d6a7f429185c60fadc11e69ea6c1f944061898be9f9030fda63d340de76e86e967ae03922dfadb0eb6034ac59bae81fb2905b03f3dd978d39f2b80dde694e0308b643384a9cf6a6898e2acc22e4cbb9998106481731413021f9c8760fab3dd2dd2918d0bdcddbd8c182d731569fab9bb7bbe67f7f9fed1bfadb5eed6bdbbbd1bc7451b0a5214a3fe7ed772fb3ef1b00612367b35a32ab529feca110d964f6a88ac6d19f5221c9293a4ba73efdcb7e0af23725eecf6fdf15b685992b1ca2fdddc5f72c203bbbf4ec9e365e2f2507ee8a1fc09e6f060fd5366d89c24524a29651561e06ce534d33f8972b4c0f16de29c3d3b1ce2a13f93bc7d8e874cfa3726fedaaed7bac596ff769978e8efc53e8e3847da0ab83f2709931c26d7157ae891967198f82b1749928b4c918b60ff223538d2aa2002730233a60d868eb0ea694345d8632dc079085ea18bbe85cded1384fb71c0208e342578c04359700f1c692948c2606a900503004730658cb01268709635fc8a34256a645c1a1d11845b9ac1fd318261b3cc5eabb52c4ba26cb7d492a3301e46fc38fbec08f709f2694d92f1b06551cd67b89febc266599345b2e641fea2770ed9ec420daef93e92b00622b6fa89c04774d2123210e7fee981c2e70a2cb85ff083fbfb27def4d71dd8dc3f3fd148c45be64bf0dba7af386c5e27e3adf68e75f4ebb56d661f67e9353442d88b3ade9216bb8726619396bd86fbd1e0f859d6a6941c0e4541598a70019a221c70a469118473c5530b1e26579a5b6c8eb4cf49c2e437d37f38739ac013383dfcba1a69a9f45e2a3d0be7897730c7adb66b71aec1b5aee50a362d4d848b40ca6608e811f23ddc86e03c558d457f352b0f41e22df93d9798304ebc914074b54357a97aff87612caaae773f8e6cc9cf710ed8ed08690bc35f3926c9e6cbaf124ba70d897a5e74f4232b47a4d217b6eb554b1cbbf4e0f82a4cc06135d29288381ee770faf81067ad8b13384ee9f1e3dbf803c78fffa38f348ef1597dc43f92c1201dc33ecee10d1cc1749902c72e3887383a9813e000c78834830b6759432003315a80bd769491e5a1bf2bb6fc6b6797bb5633d895131e583ebd2dc87f1c71c459de5593c7fd8ffbe689fb863f717e96c378629b17356091032cb117e10c4cdc8267d77c14a20ec6700108742822e3cb0862f3be6cddc0392eff864b07167ae822a34117646466b2f0c2c434ba0202209ed842348b58603f634516475b92e8210328e65734010220b48c2183a0313ec025568b12e61fbff6914692831e7a622b28fef2e5b532001e5accef4eac8bed1e4b27222e78421683c1bb015d5785cd2c67525b7a315e0fa28bac2c9d0a5b04cf207000020dee9fd1f6d838c6873070e9394ece49bfa391d2d722b8eb4a1e0b5276dbcdf2733cfa577f9ba54fa79b7809941205fff6f533818526f3fbf65d614c1059706f200b1e7a18d1a8ef0a1ed62ba5316d4c6fe3e0e1779c1b39190f738c61b32361f733fecadc9bdedf93fce5ef48d148f7fe4e463452dfbfaba01414d8818deb6742fdd277d182c0c6313ea0c1a5afdfb9576bd7571acb4f061a73b7b37848bb7d3cf4cf81868d1ed61a2c7f057f6d94523a5f822ac868144b42d87e2933119174860cfbd6d4b3f19a5a73c375774723ad2ef480fc3842da7cb6baf9f8a0b50f712ead85362561ff6067e2b8b96d5b57e3d3999b8d07d678d8db76da5ac27ee3b84f7d3a13c57117046b77e0b0c9a16a158e897bc70d1078f8c85663c1fd79c35225ed867a8a3ad601f9d683db632c7e3a4f8e90dea346f4888641062d839ee1b8ef4ca0c41b78668264e04ee2904cdc9bb89f1c47c3e0b251eada1edd993a9627632de93fd5073a6c4cefddf781cddb7cf0aa88bd1ad004fa28f2ba547fae51632aa235e998f3146eef28e729c69b0ea80bea84e28d114dc49f03bb99879e8916d580742d7f653bbff5e970e157c20eee6eacaa69ec87e04d221dd010174e8d95ababba482b5d1cc71d50070476403e64e60373f7274871d775c1fe350d6b81897307845d9eb63b6536d74c50e26d6e6007d40579cb6b22dd4ef34a978bd00eeb8064cbbb59a9033677403729198e3d9a03c016d4286ecfad68b09d914361b7e72abd5be6f794df75ac1ac9bec2cc189df494e7a18d87424870c61b9b838d60a6f8c9912e7446730611c64c742e3e33235b79d0f90a5438b0fd5e171fb43fd838c60b0e63bc64c1718c171c70ae63bcf0e03886893338cf314c808153388e61a20837ad450d74174166075dbb9c74e36ac9d479df298a34b0ca7e31a2ac083a3156e5a53e774fd508e2a961a3868da02b1d11366a6cd46c445f969a1b35376815a70fdcc07103c796058e1c387204bd6088ce6ae98c1caa1caac9139484fdbda0a3202eec2786ca33994c26d50740152583b621850eeffbbe4fc7aa468995cdcac6081b58b5345f2364546b94a8089b1d363b84cc089d99a9542ab50304a06e04366cd8b001028fa17ae3c68d1b3c3e8d1c3972e478b0a403b08617b7c7ed6144b559f4f056abd5aa07cba84e2b583e583eb61a260b7c78ede587c783070f1e3f80802008028958260a88feaeb482b4827099795a369fa0a1f9538308092264c8081dea878690214284ccda1683bdc1909b2143383231d89b106e4208fae1e2c50cb60881480821cc5a9225e2224204890badba3c2142840871b984866a0f227837373737228820c4533f9f96210045021080128fc745073572504400458ae0489bf1601b4602780940005e0f9ec59136f34015aaa5f1c19817ecf57530afc1784496190c268e30b32e3322bc3a01c381c184b0481c69425766ed6b8ac0190107a78833387b91c6c506441b8e342e314c1f46f07a07c7910555f02065e238c2089f0e8d37fe0dce7833443b420438261c6947f4b866493324abc386c0469a114634238ab0cf6844be3f8d46a2ca880783c160468ce0406384114618e1c8911b3538f2e9e0c8822a7eb0cac3239f94898f7c3af1c6ffc8b704c9872388c48ac0469a1144b3148dd468e43a49a34b97cd143469c98c49bbd4263563721ca593d6263543891ee88cce66dd333aa3b319a702235e28c74d8ea374721ca593e3289d1c47698dabdb068212c0dd7d6b68d4d0a8a15143a3bb4b0f5a5ca1c4a425d3464ddb6cb66da61b6869bb61ce289d4ad07903c7713cf41014c4d59a049704c72541398ecedaa441471cc1d5ca8d8186194a4cdaa536a9199376a94d6a06000010aebbe5e6898021dcdcd310107c21e07f1090c7b740103e04c10702feff0079bc0f10846781e0f700ff2fc8e32d08c2a740f09e661ef6a3c0bfa72e3cee6908847b6ac2c3fe1de0f736a0f72bf07b1da0f727f0bb279a774f44b2d57f0334e10fc0ed051c88e20c56819c143d304002e700a50e1c20c201c6016e378041a201a6b9e00a8e347ce3fbc63d01794800ec345c63038c58e52f4e8afd140af552a29638b69f722ce54b0ecb4f8152aa602eef600e054ae130f7fdd6828e7214ea4e71cc711c90bf4e3309f3be08f76908f79f68b84f44a719accb70c4d903a21f18ca56bf07ce34363ff666b2d56ff268352b1d3381310ada0c7c64b112286315dc3cf0e3016d44d8c7f93da01e6ebb2c6ff5cffb3ea44105f68d07fb687080fd666f08b707b46386630f18b81d56efa060983b9d9ca718139decc1f6470e080bfdae468d0b836a4a5661293b2965150f8d3ed6dad520e947f939c43cfe7237723772378ae221fd0dace221fd746457c543cf6bb2ca263f8a043921ec140fe9cb5ba5cbd8a7a9cf2ad3677bda6eb6b7cb7818d2af71453d993e45bb8cbfc834918479edbd16bf4d0b9b9b2847182b3f7bcd08739fdda88fe20dfd0692b026922dfa37db0fd8f9d98d307d79732c72233347fef29ebe23754fbfa72f65fe323dfdee034bef81a5df4c5ba9c97839c4b9893095d5b4029b5bd632ea92baa42e298dd1afe22fca85a47dde9540053d4d24794983638d4e8665d1d75444f108a75aab9232d992cf0216c7d2d3a38a4731092755191eb2b99352d0466570964958caaf8dc5d423611548b6e4cb8894d4b2eee16844a59f229983cd1c0d0b116fe43758022212913f9e179b4b3f587e0592dddd37c723202c3f7367482fb692fa48472268c4579af8a22f935ae2b8713adc1338b352803dd6c94a267019ee50b4bb7a386936ced9b91f5c35993219c513c7af49cb648ee3d724d2a4e7afbe755bc25a7f1315d6ffb38107cbe9311664ab25cc6531976109c45632d192896ef3574ea4e46ce04c255a2b97024a671b1969c54b081af68eae0fb8c98b628c22614c7a8963f95d0e9605099331fa2b27b305ec45feeade0cb6c0c45e84c2dd6d1aad7ddaa7b68f4faedd4413f1f7263007561fc242fb21c2eeb5592c83734f518489c00ee321ad893ed26022117f5f41bcf1cffd85050993b1f89d74e9a72da5ef2f858e95ae151862f53a906cf97f4a781850e1292a2c3d2625a6d9360a4e9adca1722bd359c06538d6c0882752956f72fcfdd850c25e28bd7cd37fec052128a597ff02f777655ac5bb4261fbfa29fc668a496f8a6173c5f24340c7b855fc298d57de43e1bef4df37aee02afe0b42ddbf507aee5f10fa2e94f9f55f58c5bbf22e94d27377156d77a1d49fffc2bcab78df43d3c7ea9b766057dd731f63ab8efb144acfdd1784a0c8afffc2fcd2a7509f7beebe20bf7e8c99b89730ee4d1f6b4544a31b33365b8a2d7a4094c6872466b159c64b0df0c4f35512f6f910f4c31564d890829fd8dc02cf0fbd8881e70f714998044ae0c310134368e10197d87c1c7f4518e0f93964e0f949fc55c2201046e8a0e50668d45083d8fc27fe52e10c3cff01fea2781a8538d2bc7cf11214032b582f61932693c9b4c454aa5a5938c657ffe98797c11721bf3686f22b58634b4ab1a2a4622955f0ac957ee7948b18c7398a51d2479c9081f6a50c237aa23140ff0ed63f36fdfe159a1e558f2b4ca0bf7dfc95c5617d85d6efc21233dc32dc1d917047344bc0f89975c93b1d34c31d0647dc1fdb7cac5f7b6b2ca369928bee028d5348d10cf69f000b1c08ea6c5e0d568a471e3407b07fd780bde25513fafdbd832d109f7e448a4438e2ac62e128a690c4e8bee8ef0f058f394f0c1efac6a2b4293db58c2fa34f29a594d77d49bb9f6abcf12e8c875d6c26ba1d0d8d8dfe435d8dde208d31bac8e289a126c618638c71ce49279d74d6e07927d78505ab8051d2d8dc32598cb228bb2b1aec8f18d48a8ac387234d0562d49e15c37f97cbe5125333da82bac1068e608ab0c224064c1138cca735721cc7d55ab94ae76f9556bad1597ba202147f457f2d3812d6efff850b5946c7196cac55f97ddde5f596758fbbf3e86cfedde32cace0a17ffbd02162e0cab88a6a8d2be3ba41caae2ea90232604e2005ce951669b853f2dbf5341c552a6ba5a4e13afb7bde48fb72d49208e9793558301126bf87a0c0f20a2c5f80e50fe11e9a80bfeae71d1cfb88cb4ab6717ff5fdd3b1c0b66d31c8bc68d99680a185f65013fe8ab42586b0fc3ef2d71987f50c3fcd84192c651761d9b43172892c641361f93137192cbdbbcd8b7da493de7edabe74f35790035d4040c7a8aa6bd0b6a47008c7d52f4538ab7295617401c51929a5ec963d5b764b292547b4e108860834f3888e26a3097b3b7413d8bf9b5841c23a963b087b77511da1364cc3e01c8f82bad019a678051f3bcb685a0d1ee3cd1b372937b96dd2f8ef1a3264c8109a0ace707f69d4f547d65683e590a2e4d4a8f68bf15920b248f26bed1f09abdeea9f290eb3f1fe51fc55f3fe5d8d4f3dad01a37868e34ef11a377a2be56f6faea8d3cdcd83fdbf9badb7ba7ba3b7e2cd7d05fb6f3f96db906cfc5c673b579a5bd43881114c3ccb558452ca954a714a7a71ded5fdfd341775b9523a9831cb78a9542a95be4d2653a964bab9f4a6ef4a2693a9d42da594b2069bcc261ce20a433c2482afb281cd61566db2301ad7e022aef7df92b0d9e52ae2b09a98cc7107dfc35006b63fd65adbccec87f68325e9a8e7c88836abcd807a92b02421d56846b421a31a528dc8a886542332aa21d5888c6a483522a31a528dc8a88654234a3aea3932a2cd6a33a09e242c4948359a116d084b0f504fcb8c6a4835a276975235e9b6715cada6cef3beef7452e1a85438aa5085a3b241781567954ac2aa7fe173e567d36267b5354a2973dd6c5324245bab576db51bd08665f3d97eb20acbed074bf9db8fbf7205f2d72bdb202cb72c5b167f458a44a1307a96ab48c409dfe5e19330449d2465864b052228fe8a65b410b5132728a20b2323e2c729a0ab4895183f4a04abc4cf1129fe140f65ecfe2855ba4ab7fcbeb97e9528e09488147f8abfe2cb8fd2318a84752cc618bf63475ba6b7de7e90a58f3c34b5e48b294cb796c0196cbfdf271e4ad9b2651e768f87b2c75f7e6565cc30840d3cc8e0882d8e9cd0e1c8ec809be0632949074424d1832b484fe4f085498c97baa891a25434df4ba5927337d7ed664b6f6c56ea52ccce0f53c000892d4040c31960c41ae0acaaa935aaa589115aec600c1b76e8811934e8be7841c2463ced0f463b3378e04a0b84d881192f9c30e3b1205b898477fa610362476602256800860c8d25d2f0827ed9c195f036b085180e74d9018d2b347481ba610bc246fd120315a26b3ce18205416690f982c5082e8ac4338cb407615537be3ec246dcd4075b3381227408010eb0f0a1c6c5a8089634c0eed253d0a5624682a7cb7fb9bb10ddcde0082da8a8c1182d65d46640ad20c39dcb9368a4f11431573cb3adc15985270c786084c50b145e948087587e3c658073113c3f89c3a2e348589c3f5dfe2ac24176ce5a858d55ab7a59ebc07419eed8828fc02bf6978da6abfe8af16b6fdfd3d2da51b0d2ef9d0983bb93a08b31c2acbb048768f75b238ebed3de2de8aabffa6b84712fbfe3b87770be9cbfdda98a373d6fbd30f40e85a16577316d6a32921faca922c1a69fa669fa3cbf4a93c9f4f53b13583d34999ede68bad264bab974ef87d33571e6cf49bd50991f9f7efc28a594dc6bfb1c7f8b925e29af4cf979ce397bfe9c93075a9cdb28b7d128b7f62d5d8cc5d9ed9861cfdbb275d7789efd1c47a1dedda5d7165ca8c1a977a106735bb6af5ff0c4804a93264d9ad8765b483df72814ea66175cc0a9ef2528d4f7a783ca32053646bd045b909f0223eee2cd5d93021bfb2751b2d34414ea66ffe92d70df18f540ddf4e29742fd550af105faf163ec03bd9fdf09f2c6116ca48109eafb96ebc14a25f2aada3f04c4f7be630fe9c5364e7dfc746cbcb1dff5e7a4b3d223efa01ed5b5d703c8296209c47ad6d3c03481e5b3be938d5b098bf291a007e4cd3cdc7cb62b4b36af069b3da00d47231b4e851266f3abefef66de150f83b01e96ebf9782865d6e6b3e793faf0e5b358dfeff94898958ed9dc15ea63ccc606ac33d8c767d4f090150d96f5a8f7f1ddf6e9f8f8d457c20e667dfc4cf8e1036c3c3f27f259ac67dd167ed0945cb150de0fee5fb1ae12c72cf95e4dc278e42bd29c40d3f80322afc82b038b5a441a1834b8df1b62a1a4f7e3afecf968f1578c01ee6fcfa7e5f3a9a0e357df5f0aa65fdded47b62edeb0aca07cbffad5b5b261f99d986cc2bef0bd8ecf1e50eaf3e6d3df40398e0281e800572978fffd775fa0ef7d8cc94f89634fe66177b1b096352d098c0f3ec2dcdfa71561f95d121a14d8257d3af276683c94a54f475e0fe8062b5bbee96b3265320b4cb118f51d894c2ba6af89c43fbe6b29320d1a2cbffb9ab80c4b911660fd948a3bde380cdda7f3e33bcff3cfc9ed18a29e7e4d26fef1de77c2c4acefa427c10c38667d17a54779f2f139eee34a91987563bc417d97e4afdc1d1a7ffdb87907a340205ffa74e47f608c75b42345a1f197bcfd5d92bf803c2b0808b892df58fe095cc9bbf9c4b62bdeeadf82ac37bb1e9087d2061bded4670f68c69b20374f6cbf3b791ef8759ef78131d69ffdce035729c4b752df7bef5af95e56fa17e8f7a4e4ef81f1bff8f1937281c6f26bf440a17af4b8d9c3a8af1ea23e7e3d3efaabc7453dea76481eb61289379f78d3efd1605bc8670fa8b1b0ee017941feb2f7e7f75f7f8f5130c684802d30573c04ccf66b40fcfaf2d39157e561ac93efa012eee545bd47e1debfe6e5a3c0ef4f09f736fe0474e3fbfdc6670f888ccd158f0cbda2638a1ddf3785bf320efeca61887678359b9ecfc7fb88fc95b79c6afeca5be8a9c75f79cbeae53dd13cf4f157dea2e3fd9e8880a07cbf179480eff76608f87eaf0b0adfef0d0df97eaf0921dfefd1827cbf17a6f5fd5e91bf72009ec9f77b65fc9501f04bbeb3923f1599f157d6f95319efe8c9f77b619cf0fd1ed201bedf3b63c2f77b491e1a944cf238167fe5027c922cfecaf8633f395a0800544290939901ba34196235d1e3fb3fdafdfe2f8cfdfeafc85f79004fc2f77f65fc958f3c92efff8c8c7cff676684efff8efc95717cff1786bf728eefff90fc9555dfff9df157fee0fbbf247f65f0c3efffd0f82bff03f9fe93cc5f99c7fff8fe138fbf3208efe3fb4f58fc95618ff3fda72cfeca02f8d7f79f7efc955d2fc2f79fb4f82b87f044beff5493302b3356e4fb4f4037c088bbf8ddb87907dff820e01009bc045310f80bb21cf03fc010a4c231e085806f23e4756c985c2a3508142f01374b1b96909b13f0416c98fc8f4bc5dae8fc6045e0da7c4b0242765c1ea11cfe924ba566d62b5182809b83a0d05272a9d8e7110a72f3122e0137a3f04a2e95cae46625d586c9920f621324c8cd43be657365e0110ad27a1e21146ede62f34c2e15abe4669d1d4a749e4768c9cd411e01436e6e3d0a2f24023b5875c78f9bfa213b845c2ade8e2142b6ecf81f1160edf81b01264b9e472875f39087226f16f209781488f208907f7c924ba57aace7117ae29d7033eaa59792a1f7e4591e014e607dea01a89b532fbd77085c197884503fc35f1ea19ce463974acde1e7e4c84799e01efb9c4bc57ecf23246f4ef27ea970d20ff039974a7d72734ef54c48f2d293f2e6fbee497f1ea103dcbcc57b13be1cef0970a9d81c02c83f80bf098f7ac08ddd9ce44ff8c2fac57eb0bed4cdf7515f0af53c42496edef27dec01e1f73f1e9024f63c42ac9b53ffe4de8cfa13be07d8835e1ea1ac803cc29370a9541a3e8f50094e6eeec1ba24d0116e4ee1433a8293f0590de87133eb2f7deb801ea7cb23944bc8482a0906b8f97e8f26d6927033096f04c9f308dd9b9b700a7823974a4de16623951aa0c95f7aafa5d73e8f90026ede42df002723f447381919e179849adc7cdf00f69b7c8f0694f04e4e24d4939313ebe61fdfe3c4eaf13c4225dcbce5f44e1a40c2e99134800424cf2314deccfa126eeef14e1ec88d0e22974aad0ac001f24347f8630138978ad5a18202c24b85c33787ff43c7fb00a970780b0380ac2e8f504e21bf5e844ba5e28800e4031f3e705e37e3bc08978a5dbd7edcace216f01fe820f22a5c2a55c707aaffa1e3c70f1f3a7ef8781ea105dcbc45c78bb022a2e33f588940e479845437ff78958fffe0815c2a1cde729323858ab7ac70ea4a01383ec72abc39c703b9543c7c65e0110a813c8f500a376f59bd026e706eded2f3b1d5bf2e158b65e011c2793d8f108e9bc3c791e3662050128000149e9c7000134a706280264cc000fc121000af0474bd0e18c227010b100371c78a7c0ee87a0280213c09e0009080473c56e48d80ae1f010ce17140d80b148015192b72798402704f350f1b00f7240218d28793dfac8b2e73e8501ddd6527d1a9ac87ae272edcdded2eb8700fc49c200b929850ad364e3dee3f8c6ffbce2fe6d060fdabac1852153e8634d20760d964aa54d6c65e41a6a06b1b619b45715bff9cf3fd731c9cfec577468b1af0c59186021e3906e884fb2d19b8bfc65f2a2c615eeb57d95481460b2bb0e8b07c6b450cb2a8cd065013238b2b68587e117fe120c9f14161f94c7ec016cb7fc205f7c0f25578c00a58e01b583e0b587c81e5bb10a5082c02965f258b3258be157f4d2cbf65f29bc75fb1270b231cfc15c9f890e972c55fd105586ec032fef82bd25ae00596df5afc75c2403846233d51d0d872a9e12f6977964063045886bc4441c3171d9c2c0fbec43a55fe6adb5f7aaa204a03cc17517c7126e513cb48b09c024d0e0e9bb11ab17f8db01caf517e2186071a8e6274386384a3981d8cb02345237d44bce19e7b0e47c2a484f9f8f8ac1e9fff7762bfa2f7090ff8f777623cde0718ddc7fbfb906176e1ec48407236e448591655b063591e612e4b2447628156b6b8ef01760ee69e757439731ba88f17742450ca401e60942dd5ec4248168b31a1b6a1021b63f79fc816f76f45088a3ff85684a0d4f7b72204e5bf72cfe35a1182c2e3efb592235bdcf378f05a1182023e8f6bc5255bdc83d7af957aadbc6c71ff37ca16d27f74d58f311edcaf7a6c8cf9c7188fbb02e1aee021f71f9290164c78f191c9622cc816c73d15c33e8726079c2d0673022e383b5215d9e2be82dc6f47316905c5bfbe7fbddcf7d7b378c37d0d1a2cf71348c2bc074df8703b6e9e3f98b359dd3c7d30a7e3e6d983b91ed9e2fe839ba70c73ef481256a3560233b2181763dc4b99ea22e5e0cef4c08c2964311c37cb9f1b374ba09a9be5ccc6cd72a8c6cd9296ba391ae1ec4898138345143eb298bd5916610e75b334c2dc1564b008238b9d6e964747a8408216fb6e9648decd320973df3d12e648b2c57d77f39b6e56956eb6b272b1890386dee989dad2c91cecfc50a8dbefc4b66bc5f4f4ad94befecec67d7c1358028b70054bbf13b342b4f2bb42a13e7d28955e2b427ea1d02f5dd7cb967c2bf2eb95bf13db8989b1dc5d12712adec88fb118dbae4b553462e38da468d8798d5a32d1554eaa6a95744a221a070adbefb43b8735c650b6e29b88b0fd2ea594d565fda2ab9cfb46e7cbdddd638c305bdbbfca56fc76ffae5faa2895a8d4ca95a8d48dcea753362dc556aa0214ecd8aa930af6ef2f7d4d50a652efdc090537e99c5e2a957660f4025ba38ad3072cca890ed894115498e0a4026bc29146050f3ad889238d8a12f850e143450fc76d743e6b0a2c38bf2b8b6a1f5757d5618833d22081184e7c51410e70b648d0e060fe29d6a21883fdbf5a514822113ea3e67d3a1306510c0162ff3805f6efacaa8406162084d8010c62900128568629aaba76958ce69b3e9d49b4050cf6af4447554091028c03fb971ec7152e3a70b10514445590d1a1e9b162f3c04facdf25611eebc791b0ae351ed3057616e878c5e224349f6b26c26a801e72208a010469684b20d53821031d730311ec3fa90d3694b068d42b3002cb9ff2ab0c58be27df867901965f436301172cdf46fecb8065182c6962a0c04f60e9fa910107199860891e5e101b43da08e59732fa8fb08bc176ce8006592031039f349a88e58aa54a4679b30bd6b365d81c86ae1560ffe92f1f12566335636a7deed3913f4b73f28060c630b4e5200229a34f986006ef00d28c45f4151e2c32d72794c0219a6c8cc148b6a2554de15367ac51d9d81a294a80b3fd5ac241885538c630b35402271916c48f7d38828a2f8e00a306246438db3701122500c3aa70a42111f4b65493c9643294c2e0d29558697ec9718955baa10782bc15bbd4266c186f450f4cf1217e09b3ef92c964314c248af01c416cf68fb7225085e96099792be26c754aa8256c0d1c6948889153054db69e150e91224b8378e248f32206573bd0455656cd307daf06cb61d1af9679c87de6b4c0f439241ad65c1167842977c445c13d81e9c7e698c0f439fadc11d148d78fdf327f713d1dd2e780fa48e67a642ad516affcbc8fed9d40ef3b0f6ca3ee363e81a00ab2452bf8811daba0630f04631c90acf58987f44b4936c6e66f49b518d723612db3aa2d49c2a2e42da9f6235bf44d6d32958280c09f2772f0b19823c21469c8f4440e6370e6ca5853a97dfc952312167fe5fee170f0d70e31b81e4cc3c099cbd20448cea29188e96f48f3a92bb6e8d77aa94bf5b24583b64f47d2ff286ce680382049370e08530e8803f2d7c4f4b9227f9971589482eb42ab01658e9b1165ae0c3784e9576ee37afcd513e4af78032c4b60e5ab669b7f5bbaad88876e2dc76486e32158c4c352ed5c92e62a521fac493cf4fab95e26372776cc5d0a8b8262b72fe2ae0d4ce22d174e0db2a51bdef7307eae98e37e761454591258f9d9d2377d079a6ef6e414ccbde082b7657b6afd3561db2de22d7f9fd83584a3a291fec945e72f7bb06ec3c685a1268987b2d69f739a4c3aa6d357d0851a3c4d395f44d3cd159bb1f1b612d6f45365832dbdf71d96aebd5abb131877b0a7b71f4fd7c375e27a73eaa387d79eb0fdfc4e7ab1119b2caef11577ee758df7af09fd796b70637c3addcf326cb6b834ef6a7ec42b25b8b1d77913e46afd69a391886bad41d85ae7cf68e4d39978b630c1dcd8a6c1523a0d3ed9d35d3531fde997986adc9cba2ed4607bf3fc0c343e3dea77f0e9271831eae4f9d77d67fa2e983befe0fa26304e0f6b5dcd8f1e524cc1d5bc4fac6c398e9f92fef8b024de929fe3a1b4d27a890760a8f8c13d700433c50c77a8c81343f46ee03fe9cff01d18db42fffcbe2d94bede164abfc9d8efddecc2145cda66edfefa136ca17fdbba9b486c2279031688f126e2067309d7effe25a5377d9f3e85fa6c53a9ef2d8ebfca9593a46798f9db9cc1fe3fc43ff5a19accf9b58f443cbf7e9f3dfc7d3ffdd5fdf77d39d8efbbd5c9f7a82df625b83a35b83a5d259d27ee9f1f653741bd1499268d9427a02eeed3e9fd1473dc03d6fb6cedf7e70475b7f43cb8427d63d477351eae505b90555d2b6fb0fdde676b8214d9fd0746d9f2148fac578a374d64be04b72652bbf439b204d6ef298db094abf9b3ef9489b9f76f09b772d2ab79636cfac4db10f6310f7860b10986de91f48402678113354073658610081dd9a0c6094c1dc0eeaec55f269375c0db228b0972b822a48411183b45049c27a8040c98c9829e7588211200000010007315002028180e89c52291481ae781de0f14800e82964a60521808a4418ea3308a628c22c40042103000206288323425b40100e1638228db63aa5828a5fe3b8083c17488ac9950ff66c505743773eddfd4c54076044ed45a5057ba48ebb6bae44a4abd1c2cbd98868bad9e06366fcb2ecd42000b3b665508106917a58a38018a3274ff234de6f4aa6834cfb95c8877dd18e4caf9722195b46e8d6d77ec0c72319851a760edc3b5569acb460304a1ec80d39332d70e9f6586b36a1d76c6467cde6b866070b06aa8c67cab3aa4ed1870a50117a01114c99cf59827a9c88e7b29cf9e0964ac0f0cab1a6dd4c594e8378fdce657fe1bfb15086387d2987febb7e834bb1e49001817e1412b7b38f5ef13da62a61eb1872dbff148c7f8afaa2c86ed446a6d3e83f82eacbfa0a6ebdb93f0492301a51471fe26ce80b1f0da184fe877570c752452a63e64f7caef4889ea5d7efa36c3339814bf9375c16500b21db6216d483cbc0f175306827a209d752c7f5dd7bbaa71550e787ac615e7e007b0de0f5206c2d68c21a4bbcb6b6384c42abc170c0099eb2b96b7433a132d329fc62981cc9fe776c8ea9c65234e4b4188e99963ffbdb05100e23f3e91a31ff2205fa292457af44d57f3c687f4274b8293ccab65f8513f7d8e0f68764269535b0450442d15f930df154996cef663e1e2c67e2d6a8884dc1f9bddb7cd22051890456442e85b20a22aea09a154f3f0fd2381ebc6f110f9591445c54b6e38a2a860cb157405f0d5d8dd928c90d1a1ee79cd8bc3ec09d92639071f15bfd3d30bf08f9642bc9127e6ca205a353759a3c0c638c10b52f6ccce1e565e3d29eb98becca16c7ab168161eba02da9d49dabc4a914deb2270d265074528ada6deb6063d54126d9126b57c7a145d7088ca875e2a919c567a2867c59c19f8806d625079b3564a7cd6d17a25e4863c350546167bf04edd2bbb5a3913d3872abc3105013e59b79697bc45496f836f6074e406bf4f29fb6b6d81642ea776d230af85d1227a76e3df7eb04c5ca69dec298b73e0e637d166a29716a91e08672843c0bc8f363919ce2d75b3c613fd1f4ae2586c7479bbb862cbfc8245a6cf1ccc32f879c35465b72a067be839c72c413e274da67a50e59724660c7a2ecd31150866d85cbdc16ab727687b06a779cd03167b7b9a95363a9bd1ecbd024bda5453391a46e83dc28410221298e40fe5ce13d2a2b9fac6a9d9798627713801104d4745e46f2b6c70b607b5ebb5e90a1c3dcc014cb3d3af1e35f70122a0ba83f4d1f49fa89491568f0483a5dc9794af09ee4e1d1694b652d03a550d7bd3704a1b46029da034842859fb2ef261cc6e7668ff4af77bfd7c75b9565cc2e55c3a39031987ba97cae077a4318795826e8f8b2f40ab21ccc1aa1f611fa5840765140933c0ce6ab49ab6d172009a5459a086520394b154b597aa0791d3064607e85020ab7c2521a0b6e1b3c46dcf52fca9d5cc4a188ea10442e646ec30171791c27a2db4fc686fbf4f22565680c068ade2926a0d22a0fe3f4e7ca46b4e7a8c45f7075b7e031199de3b6e30bccb55abfd32151d8059f618e9bfd65e4fee90f1688b3333e2efcba81b9ebb2ecc23203a36672165703a9f02e3d1803a382d1a650c22b92271baec7997eda50f5a1d68bba1827a5e12f468e53263f6bb50100b10ec1cd2efbe4d386884254a6001aca8b7fd2c6ec8c5121d1cf1e96fcd029e1c676bb01b016d4aea06f46c9de74235e5e369b1417c6487bc47041764f03b78328ca5cc4516e22370f22f2c1e1cbc9b8a1066009ef0a1ee05f5f898789ef94d02a28b304ef034885d377f249f3501c851c7900dd9c423f2b2070f076041ce926574829fcf3c803b1d1230327c19ef6d2a77e1526606768dd06d5d20dac19c6f88209288439365a07330cb8981dad962dd12cf680da65d9d8827ce7f6a80aa170e04c5114d7f19944c8070a9b88593c57e1cfa91e911b50a84d1f05f042f8245aad487be62189fbb19dee1c201749961ad54f51ebc635fec06fe1c98e8f0094c2da8c4256a348adc78844e3f5aa9e184e975f38825fcc1a382dd3a22aeb1088348715f33c90f95d6c4c1b1a7cfc703ca870067723c4e250bec4766dac3af560af9ff2bf617340ea706fc6e46a3bc19683c90025e032401301153625b1180d3ea64791817e8259490d933096fe7979af729a3654a9a4d7cf152eda7a10b11aaba6fdef46f24ef5867db48d827426eb3db00309888088dc2bcac27c521c674ead2ed26d974c2d6d5638aff435b3a28fc204c43f20a7b532c00d1139136145ed94197970cfd8569f67f1add390a252830f557a97f673d94cbdd4c141ae1dee36c9e6b6e9e91277f758e46372d912328a7110515634b7998b25469ee0621eec522bbc509ea86963dca7913a937e80a51643c5300d206352995b4bf96e7fc6286745cb0eff02a9112aefb47bf2db285c192f5c72445427fa074085e3184e464193328527ae89a9652d9847212b92a26a559094578b960bb51362d4a8e0a56fb3ced0d9325c2fe37262f9e509f36ef09ac0e5598fa3d9a4e7ee79db583c6dcb8eebe87c81ec72dbee01f339fc458f76a54ddc2bee4b44ed88c1eaebadb0edb28d06e3f4b2f315df2aa7fb3f7023c7cb5092f07c6de58a29cc752072515657bacc64eeab355eec7e50a1a59daf03f5e07b228f1e22100cf3a6237158671017307c0631a1985d76666ece99851712134b18cc40044594b970ba54e910bfeb63709cbd7cd92611b5ffeb533a68c017778e7a254ed62fa5dcde569501177c0cb1d61f01bf5c4f7b2d260ae63f5f1a6158b8cac78433a55cbb593abcc3bfb18838b38e6557588ca6a6d5dd8149ed2dd8e61d0aac5127db2b92989eecaa38a3a5d400556c48bb8ea8b39c2a0f592d0cb6bf22273c31f0820481442796ac101ed46f0833ba59df0c61271e9c3cb8193453602ceb8bb9a7286f180575565b2da3e58bb05f03f7419c12a11451ce16adaa7b2b9cae31f94c5376d760d604f03c8911a16e9d769652be485eb8a292459881206e5d7e0f57eea3531dea2b900d9f4937d5c5093e20943050a23adf78dc0be78ba47ec0b6d37a8e35b201505ed4c1e84a84568b6289b05a9b4b0cf364991f45b7409f080862ad38a138e01b61108f265a05bca5fb69355fc511b2a91719a781e5d2007902c361820cdfa50853533d15158653018781ef7bf6008c704f4c26f077590c5d7e92d262b84c846a0e12f49ac0f06287fffabb7f869663f0b46b818d38569f853c93e9d6a288addd3814d58019c9b2c069e9789dac069c3ae0324698cc2d0553ed85ca6161602f15d09acec00a9ae5e82b49b8029b10457ee394b142264ff5b668f31b48bf557187329ffdff89691865d86ba089aabd1434871c2b258f6dd8a5f45fe3e13abb898a2dd485da0ebcc14caf7eaf34b2632e0273ec0d5a2303578be84b1a05730fd48e73dc6c4a54cb2ebf2d0a7e1e254bbc413c5b364ed4b9625b0d82f9b965e9f8196d04b759e60d9d4645aa6adb9652ffb6a7a42e3a217a42f2c319bf86c0a9a8f9d543bdd31de01b7b2041bf908a5cf154b3cbb242c8a304b028f66096eb1783869f6ec045ee312c9fc99d5ac5c2ade67a37a5525b80764c5423d36509a5481084afe2ace02885146b69d07ca05320fda1ab78081b37632eb224502daeb52c402d6a471a03abdb2e1c95f620133e6358caace7d80b1ee7f9c87d8a6d8bed9aa56fd1356deaaddb9563c5aca71da80421d4d3ad6a852273b7dc4a4b35b2d22cfa9df794456542179dad1bfc12aa48fce1621b2e3a1761b2904ea0cfa5bcc22d36e4b0b3a52019add6c2f289b9ddca8091e1b9397ec14be8f876c9341bfda701d46ceefa333cf72a11867a72b44f41d79defc4a0f5014c700e2792b578efeb59ca5bcea3e359ac0faa0bfba121d3fadc18b9cbeb25fe04fac530960e3026ab8dc6d6e10df89eabfab6bd7ed5b0fed05c573bfaf0d228c62d4ecc080ce5cca8ccd36c7b1464cd8e32ce6944fe64e544a4f688d490ccb04aab170cb0ed19dde3d3d4aabd0170fa075c17ff48339971a1a66ac9ed6b3d25fa16c8f086d44d9e7afbb3f2863a64221261be4b9337ee39645546fceaa147a95d96475363ee62243670358363c40fa4dea74c95e403c1cdbf8c126ff324abe26928dd58a2f279dd2d0bdb2095b48daa17bed9f673d96d81a78291afc52dcf090cfdf76f62f9adf31b76e4d535f0ced8a8034053a50f271f28baa02e68d6d2bbb41e56d62a36f44e408382a0845a894b171bf5759c07c6491cb4e919083410e2b48045e403b1be8a82dd839793733cdefd3a1e60b8efcfb06c3b56c1ffb0da377c173a3b5a9c68c5f10bdd64cb1f8c8e59e4d315007825aee4405f0c0d710b804d20e2e8c489393a6292ac5b790881f35bf7707693a50c38281d4c17b7d8181c700dca38d1063e4f01ba682da01d1240bb3c9033cdc9f2227e34f491ee76f91b84de5e67094c5b9de11c3d941d80e81c9cc254650d3ac7df5f524dcea483795217881e7c520c19bb3e43eb9c0cf03266619b665959fa22df5c467df983949dbd427081f023801c682a1aaf7a0efda589b31ac2a9ba01a57e5537807379435a956f39037164f6310fefaa09e1b739468674f05001694919a3f53e3b1f230368d90af84d0917b90c2ce9ca0fbbfbc9e80d20a82237e22c23a07c90341de97a8fcac3e2654c8992b548fc620efc9e6e6989806d753fd0acc12595904a48b3dabd46cd988414b40da37b4deb6090bc744ef6bf525104b666464498fcc0ce454fed738d3e7cf1edd740da0cc1bcc333a8006ddd63d2cf8573a69ae2fdf4872b144a8c9d34aab2e9a6b2ee19bbf6983252ea3f7b5a19da05f41e9610646e109c05b73cc3903559769d986108816ebf5f754c72857dc9f892d8dd8ecca7f32a57d8c79f0322c9b4253f47f926bd9a577d64ed25e3cda635bfb2a9f57df26e1ca2539bec4097bbdf09d363408c089ec9f148a3db27200f0298108d189df8fdb246e714819a66a0835b561595c62c0754db94aa6cb86ddba76943c7a08bcac1fb88ae97a88853f8803857306a763df01c771389cec8c0f263923aef8e986041c5044b08034ac5568e61600f2d72611a6f72f98bd580d5a520024c3755cb546f45ea87a084a094074908130fd3ed227cb01435db8afc71c3c907d61998388e09e1a9e01b4cd325d878ff144c363d1d2e49165724a4c3c9269f7467e7aba1fdd8d8ad770b84c2afe37cc2afe1301c2edd4f87abb36a5e20f69b1f673c804cd9325604bdbe7aa53e278dee3d484bc46f44234c8a40a9b75bb44161e06bb66558c7d18614df6c934c23590ce3883fbfbd8240a0db49e639745bf67c76465b9e14c2ab270f8b5cd17582c02d19a706567a6560c6b04249f0242438d1d6c6ac7239afa08794d31824f72c4fc0308c6290d5a0bfa498bb87d130965b02784cab163654c4092c2c1910d7e8ec772b4d0482933d200d4429d6068a2c95394867b8c0c7203c21fc9e4dfd80e52f87f70697dcc610d74cde99758b6414583aaba469d07b45b689b5bc480f0fa5349a69133cde86f679ee438cdb12d91f61e78446a882d742288a04cba6271cc6bbf834cf4e28c472baae9593e8387ecf81cb9e0ecd143adb95c004e93548b9d9eca8b3b0b8c1ce00e2ef70f29b35bf492b6ea54f358a2d741a12e3c75ead8b27b81006384a68034a1f76e94c187ca0fbd2976c317cefa4ca84c25c61a5dc71a6f304add0071641db8b14107260660d9cdfb5ef61f3cefb46835d0fb34b1e23bac26748498a28148c8a6802cbf5d06144c548e8d6fd417ec464c7d027b74590662d9a003d23f7b18e5fbaa7b788b052a5d6581e4501bb410835a928e856009c43aa431d019ab7740a37f20b6c9ec67823736c1827c43368b780c3cc1138628941ec2b96681b052a51b07c915303b940bf59b7cd5fb649aecfd3189fa8a221de286932a80ae7da2ee7d23a5f11ff39648146cbbb9986c3d402447dc91c106283953bd680e83a4ba027c30b17e83e666c2fc77ef59b18e1f58711c015d7d61fafa5c0d9e51e874c4a7600d9e4bbd7739860605a649f7e84b21de5d3d9d743f9dcc47058207c8dc88f026adc4acccedba7025459600434fbc2ecd864dd00e5e07d37d1eafe20c61caf33934f762175e34432db98aaeac062ab0e68a08885c31d981447f184c4b868592782c9a09692cdfe04be9a34681fb5f5e728d0a764db2d3d51abb28e544b5c98bbf94df04647cb2743a05da533727eeef49bb90728c19e41e672f14d35f35b48a7601afc7780442842cb8381d06ce1063907184af8e5a78f180873f6381ab991d91ab94815b03836a491e7b357ce2fc6110cf28dfe9f20b491797bc78fc75596a031651fd32bb6223ddb431c024c08386e291e29efe015e94303ffbc1c87f8d423d59c00f8c5a12870610b1f3339334073fc7e63ba1a7ed825fcb99611ee454b1502208264ab39d84ca349b325ecbc25b3bd8f2c3f94366d3cf0c74b06a8acf1dc1c3e175cd892750a86d0ae3b8a0bde2ea3d18daff3b7d8d290b03bba6c719c1474c948f9bd0412f70ed0e6d3df2758b0017665d5946c8d5ad7e1c0abb79897d45b02392b8f47205bdb9809c695f4c3ff217007443fac325b9e903da8386758429ce904373c145788cfe0797e25a100111055fc12c68ed89f3b198cdb138f7fcd58dbf131d2c3ca50a5a5e9134a2a9f422a209e17087e7363d57da01cead59ff759fdc65a3cb9d999bf5cf81b35811b4f3e8d378e4fe676ef0d0e66ae1173423b63ce9c72d1a98af04636626191a3b67fd060e00ea6550f3f11271740147627187c430ad4daad6d1144aeea93facc13a13b87b0b7d44a974d4dfc7cd4ee159d7709d5bbfc9d3598e19a199ff140329bfc6cd226d1d4508d38dfbe05690a33d2383b8de6030caeaadd7565dab8dc81da483eaf09496c80a514f476c4dbe1a1defd5f5725186a9ab50d7d2be73e1eb6d091d874cbf956b4410019b4f5100dfaed5758c5fcd4573d26e1a5899f75f821f69fd6810b2cb5b83fc3f27fdcc574a4c60a0c5d40a4a7a610a060c434b3c59361dda287b1ad445d583c2b134eb77dc068d72666d562feae245089c067c815fff52a15734b2a0597e2162d4ff2446c2d688c28b64abf8bfb0148a4a4ab18413ddeac0162046a0dfbce9a2df8841e5f495d244096494ab6147e548d097396472b812236e80c328094bbd347e6d1db0da6de575306be64e644fed38ff48f1b5b718d9cb1de710008d6665ab245bc12687f0480f353511f441c1ca017b1c589e2b2921f6ac4dc15d94da226ba107bdf5468bb55691f866265728997167d6a40fbf86402ea17e768f81c8a3dda07a69d47c75fc4416ceb926931b2ff94f381026a1a697a49d3646c70ac5aeec220913429844890fb9661da555f4b3cd95b1b39e2fde8f5a62b0dbcd22cf09f12a4719fc2d96297dda37a34d9fd9d2c2bc7f2098c96e8707804d1178bdf9169ad73a127341dfb4dda09072e872bf85defaf3bcaa698c7851e8d05ee6831964472f15f578f7898a59ed78749281171855b0306459a464cf32fffaaf1ed3f8d73680f585e5580b28904589ba8d2357d51030a3ea184435d5166bff6a8e222a2d510a72a83981207f754c86a4046a3b3600ed81c5f1abddfe131e595e8842ca542332812395241e42e44da37a43d0604bfa537d5925394fd89dd0ccb91011da7c7b8a1c3da7714a235b8603093764ea1797bc2a122ea6f9119fbe9adced533648b84792fadf1ddb7f8d78e9f852653aa82d497fd25484dff99838fd51e16f5fa0ac0a79e6dcf3e8e5808e5baf7580e335107a95e013b42432838a31e3ba5933894a5c45892b6e9ec358bd704a7801a5319b92784fe9b19bdb4f66aca7f9b03d451839369b2f55354a61687fcc347819255128c332d4f9f43021c20fdf292a9ef5091a8295f791e87a394f0714049b8be5027b467e0891bb5785e97b7089cb5a3b056cdfa9f4ff4f59dbb7b91bb8f59a173d343068b4706a2db20a7972702c32949b498382150166d3e438a65508de1cdd4acc46a862593a8b204784bd00a3fcdbd72a6d29b9d88208498accae59cc46b3e36dc06289da541f6d19683e4049919abbb2a5147e707bab2a75b150869bdbc3fd3e48420b4fde4ff4bb671d31995ac4598e48d65cb00a9ad7c90d2c04d32a47701015c6ad2e88869885b1f2f7cb0aeaf0d3c3c72284d36194d9dfc9604d2dc3ab898b59a0ccd39e8140d3b285b7506bcc7c870cb859fd5a1463fa22d7d2ed17b0b6124dd7ad109b627497ff0167de732e7fdc1d96d9887bc71da640acdd9ee2f21c3b11c05778d0865b49169b4fe387088ba9e2e79e13d337380100e468f9ae0738727ae63562359f6765ec2f6d0b980dc70ec70067998dee61486a2203b602199bfee5f9a9c7a806626e940f6069cdbb5898c007587fb65b804168af9b127efebc40c7c3e0f74983e7f949c4cf27707ade4d228c7f16ba9843e17ad7e2b5e817214e5bbdb5615c0c81cee453cdcf5afbab9fae41b5c05be643b04b37b3c4ce968dca15d409ce4b79a9aa030095b4737ac25428f900e80e6029debc7fafc01a298742adff652bb1a9ec85ca3292b2bded4fda16d9c358cc0c7cac58181fe621fda726f4dad390c898f52f753c0178b1fc2550fa0018a539f808c1fdee250f1465a439f01fa1c91db79e40be1c4dbf92c48ec6027384cab9d0e238ae003478043f344e695738e0743db8b6e6e65e50fb40289b6321e6dfab901febd126e6c8fa1ea55a3ed57ce5dd024706756c5ec4789c08f0da5c40d413402ff37c62d7abd26bc1844683222422d169ae6a2360f520de175cba89c042f9bc840cef159a4f811b269fb89313a24ee3abf5499a526781c50aac94a5f7dbe651461af59b686c15978e58b8fecb3045ea121c2ccf75225fa09b46651c4efeb3ae018b3e6ea5f5ff7e40400892838f539d85c9547666a2e38c93d9cfac28eea961f6e99b33cdacee5cbe630ec8148c7a96013040f7a2aad180826531c1a7e696d7b4f4c6f717dfef958a62342a9642a9f221d5a34e5f94b22bfd4a3e16f51b6dba450dd438a505e7500e920d879540eb430a277a7ae6ccb9bf64975e319d5c777691e93239d936d28d721f70d1b29516112a7285f43630523954087da111a134a11ea7e28db32dd3c6237c061af28143dda6b53742c470a183d0c89df1f2406b50e8a3dc58a10176a07413e9ea150fcf5e6cc784da3d799ba63f93b01b2f68cd519a34c8fcb6abedcc4ae9b7fefd99790bc1cc57b904abcc12e3d377a9b75d34998da4b5002738883d23ebfbdae68cfd338ec14d032822f3e3f74efa6e0b2522ef3063a5e227b1c23e03d6ea589aa56ec8d488dd6d7a731b1331323ad0313536892a53da5ed4c2c3249cc9ad602dc74c3356b480c113cf796e2b9daebfdc6d9782053cbfbec8c739ac52fb821cc856a7777e93b0fa2c316ffd7620771cf7473e92885ce0886a3a3a57bd0e1cc3cc76446579cf4c6457b6b7ad39933740e2a94ff70b1ef67e1b8264d3bc7fd65092b60d0e3f04e7c36e0cb347a28fa4d86ed6d4680ba0b9c3e147f84336cb503ae19d3b71525c0eb9f59270df580e688097181516397032b70de8c9c42f4d0edbd8649fd21aab5d91b83303419f28e916fff8cb426b8ba0d77bc50a465174bc337cddf75dcb45cf9079b8ac79a3fffe1b34786d8b4a78309c82f82232ed996c1b44a5f132dd7587a3337989a6100677a93667abf6a2171e0a47f49a5a79717390db273b36a74ad6e74d93823b5e4cd593c909b98a916e692f73552c8f026efc124e45508b1b390564ccabbd87988069e8a2f41d1d392a3c7cd65876431c751f88c1e91e437ccf7f372020fa7a25dfa95d03c9e39374d1205fc11501ba4f02cefa6869ce0ddbbf98c66ca053a3f11a35b067f524584c0a5cd6195d135087ab978eead8cb328ba7d758c86117937bd01caca818fa283d9d2a83b8332f13ff6eee4318ffff0bf007f19a89cd06b9eb06782e738e29310f3443aaf190c3b9ade2ac293deb84f1e14cf4685ef00bfd36013528300ebabe5c3280f6dca945e1a2f6201666c11b498546db1df65a87fb88fa7153abbe7bc4998fc0b506d2fb972f8246bba9cf69b7fa3171cfb25411706071b1fccba36fc154e100e2ec9baea012d42de5eb2666dbecda8d9bcaf49dc7c5a983faf42d651aa95476895a2b480d714486c19d083e897a9c405993100607bceef9a69cc72d07502584cc51f46501f825bf2dd6fdb97239d30a69ff04bcb62087caf9b906616c01b14a1174a825cc7a518c1d4ef051151e6aa51de41ffe3ccc6f64628591d0164ef403a34503a020822bafa3be32852f9d1ced4603f276548d1e0f92c043e217fefb4e6443e10b082e3cf8bf0e2af49086a8a5c1559a3ec50a70b479b6a8917699710e5f39d2ee2122ecd99800ed1d12848d8d21a296d0698b8d8fa209e1d74201c8074af80386938000cc63012bfa338184392b126217210b1caf01c32c976fc722babb25e9e549179cd3673630f892ca01f992706ddd25406033681e6be2e9c1fef815c5f4c3a2fee0256a5ba8f7df9045460e124a7b0d4f6ad1d5d3efa4d24e1e046b8cc7493ab4dd42dd228d298760175aa7a462b1deed2bf43aaed9823993e74cb7114b064f2717f455d7add6db24ec7a017a51c0e2c62bdc404616ae7b0b121d5b6cffde4446f1d560d1825158a3c009eb82e0ac922a6f49034c7757f0ccefd1ecd10db02c364d0c5c4afa41f4139f59f40a45b3e9ef38f2d71139bd1a7bd1d9aff8cf8ea32ff551110b2c2858831f360a56747fb8cf1b53236ae922ec22980d89550bdb0cb83b5ec512719c0e30ce7a9294806642a411d7c17af39d7dd0b17eb0f3886c8efc873f2ea1c4bd0b9fd7d73dc838fe0fbc82e69745f248c003abc567207041cef4890d54553e6c49f440da33f4d50a0dc888235a67b687128cab029ac92704c8bc804cf0f29b25cdf304f470002fcf24a13c270a74031394c49c775f92538d798c28546481d5c1fa437d12b4ea7d07a0a90862071d51bccb67e596102ba38592d126c72f332fd7824d1d347a377e12d282dceae3ddab1054bca815d613ef615d68b48a8214209d8c394de385332d4cd3a4f9091ef5f5bb69a1f70d235ccfec7c6f56933a45affaf333cbdb0b70aa3e999d65e874d915913d3395ddcf8fbc964cdf0f31dac4927d64bf751b8e5055e8963dcecf78328ed2c21e0b44995a3e25a5baa93d13785e0438db3a192439dc46439c94aff141269a586b0e4ab974a0c3a0088429b192d5a9f5dd9ec3223fe264658991359e7c81cf505314e1c48a1eee06c70e433eba95256c061a9cb5bf860f7b996c9b1c44de70ef749720c18f6a3171871a64ce2c946a994a7ce97f0ed441e4633f93225c7af88fe0c4649d355ce9b87881a6c680c730f75139f04d2ac4c6bd2508bf3b4bee32da3363765ca8e78142f7ae87baf0c55c542241ed137dd7ddd60cd82d031723dea0c8ec43b909b5c02262307f3e92fde02a06732d5d0f47c2b89f6b08bf2b0905502aa495d13640588abe59c2fd5982901351b132dc416bea2a0e063afb1e0c346060e89510de2e1508bda7f58d5f2fd24d0967252b8af299807ae861720e7b5f726316fbdfc077ffc42ad44a518a70340222ccbeeb974143cd518a8aa67bf4582565434bd15f2e5c3a62ac5c93dca20dbcea0b4e191c51ef4c90c4005b35ca3c031779747b1c16a00dc46a0a2b6a595cac535c2cc15da53237cca7ad5a67ac1e72cc7f576421bdcc28435f71e5e98f69fb0f0cb5648e65630701bde8877b1dab4a613a8bd3b1c276aa7b6e802025109fff8d9a61ade05003a231d2ad169c500bff187ccaf90a9db3f14fce710fbaf04a50349fe6f2ada6d22a8c26ca959c23ca56b977abe856608069af6868f9296a7479608fd14baa30353d01769c4b816f9e291f32a0634ba7270d3fab976583d86422ba2a55eb0eb4375903b0ce894b6610333d80c8c8a0768161e860e89563c9ab6985cefd9a3c019786bcbfcfcb82fe9126f7458d30d2f4a631a9acb84bf9e440e9128716e2ab7cd4a7e1ac0c983285602ddbee392fd74b07042f70ef47947b8bbdab959704f325232745c2f3a1ef85a5daceff943a80840e16051271a3a01d0a6332b5877adfdec9be442b426afb3dfa20edd57ea4e8ef51e171cf0dcd2114a2704405d8fed9b22dc938291deff9404c9911a1dfd79de48c3453c3a3ded00dc2f21e67c0bb25bb7c24735fbee7d8403510a3e571d1ecfbdf7cc319abd115c1a835dfa98e9ca12614fc4c33fde3beed7c6e71a09d8b2734d062e366d6f9097e316ea0a863d9a8afa718f9c7f892a4ef1be5ea9cf1082032f384f02b826f045a1aba71289732e7c45948eb0f5dfb35577c888a32bb454566a300a4e77c473510e95f9eced25d8c1b4b9b823327ab17fb074cbe328a0ddbca4e3745140a04dd4f974c5e853113d6de40937ca918887d8232d86aab0f7cd472211c028eb64f519dd0f68c52bdbdcf6b2c16f553bb7a589916d66ca36937eaa98922adde23c7a5e13f760fbaa86f7cea5940e63784d8dcbaec597c031c8607fbb6ca904ef6f5555ee9ca8e6a498ae25c87c7da0e83c40d44e8316de0df0f1c45f60a3f1a6953e1c7b304414ccdb4d7f124b83b8c136c7089dce6c89805473f28ade0396bcf625c70277aaf8725a99cb9e370ccccf885a92f09f50fbf94d0a9f6f8c3cbd440efe7025fedbbdbcf2fb7ff0f3bbeb86c6e431f3c64ed872867c54aca682240bef7fe3df7c0bdb9fe5023b2e433aaaa760d6830da1cd8e2b8e7c3845d5fc44ef5db1c730c5954381994781ffda95b1d862d04daaf1c0734e608443207b06d34f648dd7f9c6327882b479ce2e8c1d71fb5d5e7852d46d4442e6aad072792eca3b83838318e8381b6ed2446cb2905eeca170fb3e43d37e180f674988b9b9b0e83d667880d7b754574d9fc385d22b64cb7a84852a4ef5b81a4f88de3a28e9875551634fa55a782be130c740758e0b47507eb5130fcbdebf82d64c724407a2b860bbe89b7f29fbd7e2495044b77974fc6b5b56991172ba3ed57c57d23e83863b345e4c4742d86e1c0ab5f33a397d407bd1059555e0b595bd9274dcd09be702470984bd537593d7d1d52fc8844acf7701cdad16f0f63adafdbc50c9d5470662026f71d4a92c41bdf50d670bdac4da2551b0518bd4ae06499d890901a112ffd31ffee6563b0585563be9c093736babcd85b2ab0c692bfa84381520c3d15c0e08cb06397ae7eded311c7b1c2a6e9629619edb8c1b50ee618230376fbfd61521df5b54bb03ec0daa06db5d8a307d78ef71c5e9af6f63ea3440daf36d986a1195955f3a65c79046002ea3b898957f17a25071b94325102134cf206ca5394ee068af00690bccd8f677973d9c239fde484be1de2d68902b5a70959d75116cccfcebba0cf232b767df82bdaf0109866ddd2c33b268e76f511d2cb1bd532ef091fbc6bde986228a37c2070f63ede118104e791cbcfd0b91e85b3ddb92575a6214e74ec2680a02bdd92f45c037a8f265ca3b5271facd73e134f21cb2fee1b7c0143188de346037fad014ce87ca850fd18b4948616c89c7e1d635a7f73f354bd1019ce57fe8ecf98372f056e720779897f08aa0469207c5689ea0b8e4ce10fa8f5671b6f3f593124b85a86c0de8337dc85b6f140da3947ece95d85cde87e33da8e235c3dcd53f4048fddaf4cb948189f0a630fb1e755524ee5f32b69b626a6ac15f6624e5d8bf9a88c775f2efcae00916919768dfe440f2515e7689877b14cb6306d1da47035ec1c570be13a2703500e6c117b3cb9f1539ffc0c6e020ec1d5d22bd05f6e9848b4a47f2f1a87903a9e7dee161fb0e42507e22e2adc6a482e9e0475df82b584cfd1558d935a913c41e8ec01dede4ba7d7f284f4e8729874c7f51710664c2a26c3274553a28fab4b1ee0c78495aa1538405cd61fc7163b476c50fb8de854c73605b695db56e5e88221b2d928c776b574d5c4d540f954315655986de3ac856fd0db65a567ee68d82208c41e8351d4d6ce3db53ba7a60acd06170cc0a13206caa9c2f406e8668b329f1806f5e07d90d92476bc5e14e7965ca0c9987af339fabae8b926062c3d009091f6806fa074cfd086790217a808ff7d27a0bce4b389ca0afede990295f76ed77a42a152e98c48e79ef75d098926c90e5b59b523342f83b71f2666ff0a862c320a13be9c57c0590250d418a03dced71fd932bb079d22a2c65eea1f399bd42f9208b432e061d63a58b704ac3914e56c33bd6a470bc75b502100b7c10ae854586a9e402d525118828d2a0a607c0ac34648a7deb16979eeb8ffc03123a7598b92464f588e8d0efa3d3fa5766bddd7bce850a2f99ac7f717a298f0b5409f1ce47b61c70e0e2b3c1c5ae31251ca3d53426caf40e4aa55e19d6b192458bc84aef9489846069183c34dd8d61fc220d6348fc4746b08c506a69f2c81728d7f028560cb7d8542fc3134dd030e6dcc79751c61392eb0a2f55e0c53ed046d72693b0db788411c10bd696ae49699bc1f9900fa52cd81280b2132fcd20fffc8ff6b60b63488dde3b24cf5e22955b1ec94c3bc1e60f496722f013c4d5432a72c45946390ebf4984a5c8351a1373c31317c8e447fcf9c1b6ef97a1956f90e0c31bbb213e935990d040724c4817b73218a6de44babe3288db27ac4d1e1df10a8bf68eadab6d8f7ab302a5687177f5108f268865939681f918e903a6be71f22531fa75fcec9049dcedcb25acca4358c22caece2997fa3c2eb6f1cd74ffc7fa64173c30e19048ec5cf0d1bb3bcc32ba4cd1436db01cb700a8cd58de66d35d329cfab733931caf2a9689f93546547246491916800f4070bad8c120d0868346c078d9ef09029b8c657bf95234fb6c13d0963e5ad6968b0c9226a068b26dd37038416c48c86fbd340f9b21b79773420923aaab5a53d15046c57d51f80b4bffa9c57b45acd041c6e8a1deee6d204f2b42de3934c10ac203f9b585517b8714528f35c03718c7f692432e109e36b05a1167ed7cdb639a6a1a369c71548c670e3a00ab86655fde9c64219bc66f350d6f67374e102f622474f8718f5d2e547a96cdabad727dddb3ca8faf4cacb6da4b0e41c79e3c73eb28810d60b2866a71d46fb56031257fd976acf0fe683ff4e3cd76760ed201e947280407697ef78f4495a2e13559afa439f31e0de8a91f3b80c68641d02c8ce4780477873bb2a138cf089c93b864653959ed555ccc07e0cf731d73bc29870a4fa4f90dcf228b97bdac6a067fa6633ddcf6eabeb1827f6f94de2beae8ad80479eac3c97e3090f97428e6ccdda8f70b4a952d21aca8247b01a4ff2fbc6306833a053737d44179d35627391ba4cbd0dc6b58e97ca460315bc0c9841f612dc18bba4a7786f65091c7b31e47162171e2c18ad44940f93432a1d7a231cb34d3d060d33031e55ae23820b636885e538753e424ea6d6c491dd425b60c92511c8fb77f2e6e33ffa375aa4ef373835f60610f1891b871e2bc1de48c64ef42371bfb8c6c0c2544f63af45f709f3b21eea49ba8bbac93899f340d94216333810737e596a871f2ce3196c9fef4439b0288e8d2f4059c97c0d62ca516bfe3a36e969dc574b0e613c56f1d0c9e59cb75200fbb46557846001e9185da6d52b2bd6501aa3a21c866bf5975553e41559c8c3220f71d6b88bd0485bc2cc0fdcfcaa469a9c7f5e99a2307998e676b02e584e9fdb64ec09c35393ed60bca4126e7dfaae072d2a45b34f143420b1c47787c803e477582df7edb747e592b600e9848e6266848f8e2a388bcdbe763a827f2e3b74dc39fa9dc8e6ee8d08b131eb1e53d541b285574777182df7edb147c19f26d55430c5934e1115d93e65a4a53d77779e2fdfebb26d0d32c69436d28e151415c34165ae6efb5e9fba37738c5fcfe37a6b3c77a0173c8e4f85a23500e9b3e2f2b02cfa9c9eb6bb50039317d7fd61290d3e6bba2a0e40b1f098f282b2ae86a448f718c5146028c9896d25ca31ec8186596953ed7b972a40017c9d02f47a2962e3ce208164ad1ccff9c41d883017198d9f642a06fc7a2967c78c411164cd19b7e9c51d8a416e28059ec926eccde6c138f7270b6bdac7b676556c523ea940872107789879ea5d9178fe4aff124f3522730de8c47354aca92c29b5cd01feb1e1ec5f734099845f00a9f2cbff64e8dfb96dba02110826e6fa1bbc574d490965fb952395732c004ddfc94c242dcd4f9022394c227d0c5260d15d8b6777c900257a8042aee43a9c105c7953304b85f0dc175e63087435a114c5763f1b98d2bb59450ac9230278f84399147e336429e482a8ea9d942b03c15ed82e53908fe30bb26da5af102707bfaaa93ba6c8fe2b20fe6e29b84a7e41ee111d82f1b720e012e9bba32b820fed759204ce4ea8a6027dce55acc2e384b9c2b9a7c12fda5bc125d9f8840bef0595fe3c35fa4f8fe40b6aed8519b9246c02c4a410c3d78d35389de34146a5e56a2987fd2f633082a8cb02ef92e7e469c2f06d1339daaf462fbf17f405aa1131467905f353ddd1b63f06a80d0d3e3aecdcc587fccab1f392fd76538e781e8970a6b29bf78b37238c0208dc2c665315b946a00262606d32b24c45b02a5f33a058de800d0736e1f796d0e568cf07ba74f007938570235b73f6145b95c93a99154c78ce9f9ccfc564562d7a8cd0744202a56e30699726925f4c82f7016e6efff40639068c69e17478c9ad668181ee6b0ea13af01db4a2e8c7b118faf0ea58146b00423f1924541298ef624ae1d1386635f34c5b0c4a1a95c49e830bb2a55661e863942179553b6e183cd944a0aef45a931a636b46b10829edd182b93dc5139d8f82896fc21520f639090df8d867ce9acef2d193c1988f3174d128c322fb45a1e5263e5c942644649b0e2f617a61df1726c47a9781d5c3e1fbce54b90d5a1842b2e9a893ecf4019b624136ba21db6f6b32f335d08615f7d8e9c84c821944a642461101e27077e6a6ef08afea3c6c929dafe6faceebdca50c0eb36f77fcffa73e3ba90c26b32e838ae0dfeed9a06d9283ceb73abd5e92c0e32c997703dc61a36d4f21ce12b0913059ca37c537f83e685128b861d0b44f7a7a28accf4d65eac7981448cfb39b647d68b62683d02990f356e6d3b9ac413f7ed44ca54fdb79ee2e3e344beda7d09d8d8c0c604a806467112695b97007d5e5241b79db194b3ebb0df0d2f726e360ae73c564f2d392e49933c969267409f5857bdf0536cae9377752227ca550f0324c886bad9a859c2cda9bdb48307508e8b795889b92a9548b3cf92366e75f97fe81dd995d2c6b1fd8d13dbfbcde16c67c5c08c3bdd53e554cfc4d4207cb13cc3f87eedfd9a0ebcc100fd88d0c17e7a95506c4a20edbaba14d7b5c049224713eda850efb9bab8083c44027a800e648563fe8e389672fb001d16501ec3078f939991e898c8f4257ae7d866e773b170f26761eb2cd396e230b6e9d9ac032c5cbb6b05e86aac3e3d67f368c670d8c617b6f517a318e209c75d4115592349b3d12ae0615ce975d7a6be8898884c75daf6614f2dfcfd878dd0bf8082c2b69615b9e027a16dab368974c667d79992e97fe9868b5ace4ea850ce0298a45e952c37a1b6d46ea929101acb9ecb6167c4e6c68f6649a5fb25dd66fcbf912b6181dbc2038e8e896a1756066964a39e63ac6750fc46720ec5bee0f4d58c35f07eb7ffd787230aba722e9a9911e1c5be4aa703ac4a09448c25838efc079133bf88cf4a89d8bc79b14e8fd663c2458c8507a90a88c5145b011e908db912a3932f25b084be6544237b65deff427d702a46953a3fdfa7498f53b26546f8ab1962c94938bde296cb9cafa9e3eca7a3d3bd23b8a19ed05add9a5ac2f7186eafba6c86c2da31b6246dbf06079571b4985a8aa8abf4d614d5c9133ecbd2a1c7aeffa4e7718cc50f4e5def9eef591a43b424453c3b321885264740623ffa680b83887505201b73d8e393fdae0656e1c8678ec499e9aa122b841189ece3e7eee2a95671abd48da11b0106f18aaa7cf7dccf667f10121ace4ce3912632cb9eeadf9989e44f4352aea8352485e2619994703ffd87985694e138dc6dd21a3900e62f3c7460dc66d2c57ec5fe88475bf8fe62c7d0a4beb9feec63428e119a16c0a16fd4cc4248877f3280d83e903596cb188e38614c45f90e6fa47627675d26438667be74a8130b316e6c524341c59695e14a29daf8ab9cd3af58a6725369f4a8cdd1ea7441d387e8b02b72bad4f86727f8dd88816660fe28d3da8d9b6bf8406f820bc9e6158c113a472e16edb7d799e5f2700518661593dac5b5e26d1198d335f3c263996b7aae3867aa9b20f236e25d91587a1e345ab3d238a5a78440829d8df37015cce330a0f2a5eb04882263642a3ca0a042e81fc09b1887c1b6301224cbe5e652c04aa4f88be5768c6e6c570e6be2ac6c9950692b641d8ca89711f15ea4eff2562a6fc16c126493300405ef3de1829698a595978be75ab60808bd78d00142d74fa56528df0a6ee4bab3f3e56e7ca9f9b0aceb0b5afc4911acaf8d7a967babf259f42a6f09038873efe89d72a69e4000b3d2745c109e3dc18b8a27548d4bb5db627ecb62ffad35dac33179847fbe44f147354f9c6f5efc85cd3df1a740acef782451ba136be927ce2e05b692c79308f4c896439c30310fdcea411256eabc7088c42ecf845a0070f3bc1329f68190ecbe66fb928fbf1237835682638749861bb84d786c44905840e6add2af61745e535dfd5b70f069c1785985633efc62a16e46f2d9796fa066abc40afc512ad1acf624559eaf3344d59035e375bf25112988a4cbd379f399caa157dce22dfc8addd7a9f101dd91bc5c324b9ef21d8e098ddca52f49ba588d96801c323a799e9dd79ab8774f31dd901a33769e6f3546785752b526b18940ae49fbfa597608b97d99bb67128f5b7f782fc6313d8f826f4bc344b40649a95d3d994f0949d03e6f302401008020cc88084ede545b036c0d54285ea03c6c664e1b2f80414e749c365a9e7c864817fc8ea2f96aaaebae4f79402622c99339392489c2681fbea9d944320344c29511f64453661a2789db5168d949bca67ee2a4d6b34089e692599e5148c15a205ef20285386e8a5b20d1753aa235875aa77abae9bac99684b4049a8eb15aca6a3e98b365355a78c5d1c1ac5b965dab0ab86b60a2a67ec1aa2a2a11edfa6e6c9691cde74ebdbfc62a90538376e6f3f93dff51821092434c214b0aab3f5fa2440671844c6104f0af92ab7dc42a4e97f3510fd246d4f211936607468ebe1d968e0ec1c13b0ba01ab780abbc14139db467f141931a5dc5eb440fbe312c865c38b945fba8c2856b5ecf4824ad815247ce62f3d262fc5e702984ee011ce0769569d949a740e26d8a3053204fb9d904a5088d6a013fa59093944ad47d308e55f142e341c578d3fc809eafacbea139e3c6bf6ed656feb8a1fc04be49f3a869ced72bd9a434e540633ed2810cff176e37b046caf6bf283e24c5ec9782a6c9e78677a0c7b32b24cc789b09b265fe9bf7f441ec7d9245cdb9220b917f0a46c8ec777b8ac8334a3f9e47c27c6579a992a8370eae0512f550a477b43074d7376eaea2441c204f161bd86e9318e0ee12537900ac227510212aa0f61a514732a5200287ae307096fa1d48c92fe7479677a7faf83e7c08bb86d2b74ede46316ea64098c5efac904594524d496a7d6d7857fa6f4bc62926914da4d8a567de0c6dcb12c2a72185df81f009323d4a8de1dd66537ad1be39ad4d90bfe759a5247da251e194d55914fa8a5dbfea78a4d6bf395d7a0fac822d87dd1434d483b71d9c1d552512225869b35f6c98af0364d3e8578fe03aab0fdcf0127e6da132913a506f8a4ca840dd7d02d9984d31de7756cce81930f01e5151a0e2fde78fcc298276d60044620d382e398dbd3e713e5427bbe8dae56326be7911e02640b712186a4d8ceef2fd4777b02abf076a5e40fc5bf76d6f23db9d5ad5037d7b3c852750d73ec6b80f5b81c21f158049e2cf1b7817178112cb6cfc53006ce5b7473efe8ef1780466f7d8e2337819152f8bceadcaddade54d03c21f88dde2a0752d603f0396a1b46b3cf1de5ccd64ec458f29e7a637066b841be1cfae0efe818d2fb01354d75e42bf9b82c3d261a53ce7335480dc036561fed3d3347a86883fbb15283acda11240dfb1cba8db9ae3bc49ff3bc5f4500e917b41a780694373599574b604a2ec55ddb300144e9ce5a472a1156a5984e19ea97e89ce4562da35c504e635c5db734da14bac79583d98f6239167330203ac5d7915bbb286bec9b493a9b613990a3b3f622e8b23f885ebfedb042de4f09758abf47861305243bc1996901c050f56bbeb48db01766a8e87e9b93017368383266b5520f8212a2199e4ae1d10f1d948e0f10b20f97e7848b8708bc04d4ef14e4bac33791581c37e2e2d1afb0570fb595b21905563a5cc1859755748e45ac08a10798ad6ac05b461978dc8c7d58dc8242ef8197bf82389516e8f8afc4acadc92dc6e3000c46a0152200e52aaa86583c01fe5dc6f66d3831f497b6245e545e7ad3bc06ae1c82ba48d7f0bd1ccbd2172b524e3274e0344c9274575b1448350d0917645c83ddf8a364cc4cba40b6d889ac32d177e138bf7cd45071b5606f841150f551c6f6894805f4340f52666c7e7b907d2df5522f0e288a52759b401c0eabfd0b4d0ea11917768313b039023322e9ea4f54afae987ac5606c4c4890e5e386a7915462663f79a2d67ca957e323ff60e38b126c2147df926900cff1be6f4f6ba0719e91119341d01dd311feeacf873be5b12d57de1b1a614bf168af20663a5fca68b2e909828ca1a28031df0c5a0ed12d7f5e9c040a6bfa041045cc83ecd4764d2b7b05b414af1764cb19d170a45396ad12c5385196984ed06bc894319b1a3ec804c9b90277f3c75f76443b75045e3d84a59009ec359c0f01d3e669ccd8a07d13d4e874d8500040e69c46b0e1e2b8da3abc59aa9335c7692e0d32327be74017c60b055b8b1849b3e5cf4cbcfbab99cc7c7cd33e66208673946a5a1af0ac503cd60ff2e47b56594fe5d03a38ea7cafc918b5dc7a2c5ec2c68b1ce87456bb3a7983e5d6c8d0e864970dc2653f85c9fa6b0a86d9ffe75bdc8fd795ee15d584c2f718bc1cad55467bfbbc75475bb9700db1e47a4358d44f22affb1b6e199bdd35e2e5d5b70c53f9131c3d2f334447caa4bfbceb3532aac3531e96d35470ac5b8604bcbbd39f51c570e6734c0647a5b28ee2368ef090873c139ebae4d96586ca3f0fc225a3c21fb71be6fcf275847dc87549053d17b19804b06ced245c731e8e670b244a6185017fc502836aeaa52a9a19beaa98153189e2a221be23a4fedebe55fd4272b5104248eaf8e952c4a2c8946d4afc51285eefaa528d586f7258954134bb40867415707bce40dcec21826571a0d1caf37d303be069debeffbf71d13ee8d1b179be2622c4013b9a74a94bb6a3204aff751ee900306a0444956d1b70526ca5db38bb181f7059f20fbf8c7c5cb38e76472fa34b389379151dc5bfc8f251e156498541b9faa57c9d8b04a8762505031d9f8757f735fd681ab87324b94d2f49993f5ba8eff197d9383d7d1cb00db1abc3def2a4dccc8404a26ac18265b38bd0f22cbb847767386f0f5f903bdb3482cc412c570316873f11e99b1c729bac45936cc135c9bbe7882b5c20611cb8b25de99432e1775e30b14cdf861e444973224c3c4053a77463d05e64d24fab88177e1761965299528dd27020f1fb171722715dd018e0fa5912a4feb6bf62ccf1728822a15f245fa8f09e06744863a75f48a644b0306fbc58041f096c7480d51d95ba672710912a784d6b964eea0d0cde5643b7b1319e3b72cf9d785a99759c626b3ea4189ab33dc23bf9a6111019c70e11ed863a99aee7bd88e41fe1d080f8da3710037c4ddfb51e31e194096e0b98b6429076e3a51a256076ba885487ad4b9f47e22f1422568f2f899bd492ce049f1364ce797c229cab135008ecb47e5d0983b4e19efcf51df27ab5415511a40e2c40a5113708addce811b658d07fab9f2f0f3e98f36e81df9e6e72d82869be261fd8e783c200db00cd2606f322580d6f57149e7203d7acb487a6ef914de20cfcc9859c45d28df8015d55e4224653343e920bafc58389961c27ace7871601241ce32d36890105b6b7aee88c534456611fc722714aed58586bbd658b71aee4203dd6ba0bbd448f45557a01afc547894cbf7ff0ca92880c09b48b698c6d5b6987a75f48bb7c3397581bd0f7043e2c89360a672f3c1469f3a002c3541c93ccc2f53899b7edf39a578976796da7d88b574fd683631251c943d1f02343cf1eded32af3c980b0ac850fbbbde2b0ff9820428b83ddde7956fb6e09148fab4fbb400e8ba27e7d92545e50db5ed29cb7babc73aaca4825eea7aaf5cce4e0e7ac598cba76ce3266bcd7faeebce3246728aef1d7cc5d058de5aeff48b5203fb12d52f94d35a83f704bc62d428cf1fa20310aa57651dc669fc3e6c97c626f831dc88fa42aac0bdca487e13cae84cf673a149d88a8e742ee103d8355ecc9fef2aa009d3233828de7f5222cc56549ee7fb499eb1240450c5d0c03c1ab6aec7d3056a73f01ee8b5cd1e9120f14ce29b7fb4069e6f049edf161ffe342444d1240cae377090270f344b88d55d1b71ae49f4fa414ccd43f29cdd932fa641224e72d498517fcdbd53dae03ae8ad5d026f8f8d7c74eb080b6fd7c52338e6d49419aeffb6410e6b5a37d33a6c73c28906fe2af7e2b320951199ab82c406561818671ebefceb378b11d9e5a8d11a5e13a3b24003798b0a34a12611f575c451e7c1f228a0bec869cb867416c0f3bbe62df9693a374037cd41ef1b890b3f8a2287797511decd37f826f9440a2b8ad8dec2cb9064dc6c730678b878ef5dcfafa86ab037f0e0e2e9bf518cd5fc2e96249f8881ce121e933370c29b904478af1f65d6cf664a5750343303973b940944d2d037f80fd957fe6fc7e8fb82f602d8ed7d47d49f25ff1da40653d194e375d3821d8120c2b8bac039259f910bf8a4cd934d4d04fef1b47a26fcb96bd06b39e8c4bf98f2f6a20f195f86bd9023a7f89f8b4805ea43ceb6b1a997239fd78fbbbe4f080afc2e80dfbe3944cb67c89e7a4de9892eeff3c2307cc9f0c78acfea99b5cdd7a09dbae69b8205fe17e1da26e8995540b68a5f211943b3b3bbdd564e4e0b32135eaed471995482446387169c279a37e34d124e861a7d026f6799f9a43c40edea5e3f110a7a130e2a1490b235c4e1df1b238ddf3f16f9a4418e0f6171b1a4c3a1f82b139e0d2a817ab02ccbd849e78611b93ca9282f77a3ce2644e79693b3a682ec22331075d2855ea13ac7c7c9994aa0e21ce7ce8020d2a1b50920509c92ba301d09409581f122ada8440261209f5f07bec8d93dfeba6bb9c043f80a9077ae9770ffe99af9182db597f61121635062ca83288619b974cf19f794aa1cc0dee4c28a6661c34687cb0903f297e12604541c8672f7825c9b7cceccc87bd35f84e1d80c95e1177f7d77632c192645611c6bfe31ddd40a50d301fb2d3402da9a7f96292f6b623239045017ce64a17180b234a14677369e3fa03b70254cd87fc3ef1501696d94434cdf7120e47cc4d67d4b330d51d63eab3f90d3184b948d8e0179909c903c31b67eacc3a5e0e1118006f50673cecd3577a25496c9c0bab54fabf859dbf4faa32e9470be3cee1d367593d0dc25bfa0bb22eae89611e0a67af08acb00b9dd3afc81c48d66bf4e8b5bd08a8aebecf6b25b3b30819f1c560bdabf943ac70006e816ae45497e967f5b2623e22012f889640ab472703b5e39aa6f6d291e6a71049a8c67773ed6faf5ce6fdc41e735b18fdc619f846d980fae0758910c9609af3541321d0658578b77a2d148751e00583adcfdc179efd72a7e328276211c71046adcbfbb54206015251d595ddd86dc3ade8f2e4246c3c6d82f2c8fcab464d319dec72fabac9c003a001038082714722c6003dbc0a1b7af610702d40f8ceacdf3ebb3bab6eec5f0207ed16fad35b2092184104236d97bcbbd03310b720be90bdab3086b285543b9a28f6cada27b696fc46a73e8d4371186818ea1a0581da31ef55c6633067080ee76cd661759ee8d339b43b7c2403663da332d7bcd18c0a06756c776e139168cdd3a67b31418bbdbcd5260ec2f86056b7fb907c620be6ed9dc0348ee818301ca87c7f272041414234427a494a1d78de5ddc92ccb6af61aba9cdc6c9691d38c524a19311923ca28572ea38c32bb7ca2e22856b24986e4e8caee9cc055d22c048ab1a2c9e884e6a2228f250b96524a96ab58aa5b4617c98be4b52b232b8c6f7feda27218f6758b0a7542ca4827247765773212258676aec7c27d64b30c1e3df42ae555157af59beb4157f62bcbba6a475c267a8a95dde5771de6d61bba15672b1b838decc3209b63a6ecaaaa64758c541dab9e62554f7975ed66188dbd6ad22bd0555edd8cf297eda82e5565afa2080263980b6577657498d0b38f6ef6505669afce65bf6c0e65a08f3250c67d56d7ac48f4baa3ba2ba357b72ba367afa195d14de2e195ed30d747dfae8f7e8d6e96d92c53853e814437069ba0671706f4d18521856c0c86ebab180c83aa7337c7609844ba0f0676adbaca95285726e2946b97d98eead8bd52aa7aee17672b577121aeaab894cb5555bd3274b356715a57d57d555daa835abd4f5d30d361e1ec9cb51eb2590687fe321cf5b24ba4d7d725b657b61e6447361f09d1cd3db0e8f55622d0e8a0d1b58feee8db47afb06c94729295dd8dfeb206f4c02ba2eaa22ae5660a8f5e6d5c6671219b63308acbb8bf8cbbae58ad66b30cd6e2b53b1ad5fa58513e3d94c27149902c91cd32b81e6433c429a16b0f5dbb75b3932c4c68f4ae6d4fb1a0572b02d9c6229b512ecdb8d0b3eb650f787865f4eda3675674cdd687383bb2f5a2ec9b85a9f775173a28641fb6720c064ae2e1957a18eddceb5d69415dd1a19dbb76b34dbb0f620bbb16064290657a4a39e5b522c628208629a126313b66730f8c3d64b313382492dc2f6961b437be2eed65abcd3d90d06e3d76491b7ab56fe360b2737fcd71dc4537f7c02cc4217b1989e133f8294b35013a589e5404f86a0422b81e1404cb93ce8083e547bf2c88e5b31358266298d16576f3ca5fe600d2e5a5e8302e6f56e9118cf6d0e576796d07f6ebdaca767560f7badae5af876cc6fede00d139cd3e40d39e7d92d76ce8a1d7bbc25dfe7af66a57b863af99c22bdcd5f1bac38eddeb6ad8a9a83e7457aaa8f5ca1da25f174674eec2805e2f6dd6ae8cc4a2f7295302be7e663a40f7ad5c998849cf5e6f8671a5ca958978f4ec32db214fbad7c86629b0d45eb1fc35b1da018c45fcb21d124b07e4ebdaeb95dd6599884737fb45615b86c3ba8c583a40bbf26237677f357b984cd48111ae5b67a0c13b2c8cbd5a8b235db6cb4117fd02d9dad98ed15f77a15790ad0fd9fcfa8c7bdd31fabb13dd689085f8d105e5315358f4d72ea2a358d92d22d18ed17ddd89eebb8cc4f5d7adeb372267b38cb430f597032a8cd9fa97d55a04f8ebd7ab46a82fc70c4df0f5cadddc23d7ec044e7141b916ba994ab1af5bb69b1dc5be6ec97e236a36834ecfd9281a6317a6fe3acc9480afbf46d7ace823ab4354ffae9db3d909f890dd9e59ae316733e8ca3c5f2f410f9c1de3ae59ecd566277460577bf6ed3762b5b9fa66b314dac56e96025fdd05b165c15736f3c84de0776ab313d39491584a79a38058c708c387227786b7f29460f969877abc873ce0fc6a053820c106c0cf0a6080037e594f7ba8999ac1b5f2fe30c49e05a4de659aa689d2699aa6699aa6699a6c50e7e5a7e9d3344dd3344dd3344dd3344dd3f4c732fde07a2cd77b97d49d948f071569e0f727bbd47f7089ee28aa5612e987148614947dad1bd4f8258e51f2ef481147d0a8d1c938cbd3d1116129695021ac993c78cf858a2d4b2826f84127f8f9c0ef3facd0304909c12f870c4520d6ef9a6703f60ca6ce019efa074460c1b96b722763562415aec2f4f0dd95ea50409c29caf24adae9bb27e3d17825ea14ab8e046711b5de35dd4d6f4c089e24102abd8faf9ecb0d9b3305a96c7013c1d3b34a3cc5688d6e997e23ba744d6c994e22d51a21a58fc2321c2f8b974677d39fe4e829f98992b4061a418367a306af847d7ae3e0b964b79e379cbbe67a4e090253cf41e0dc35787a66e96ba18ed9f83aead15e2998bab561ead4521fd63d023d0c19294b3dc6794c65fc9998172e33d4c6a20b65e9529e1cd61df53320be5aa385e62ff45906a664f46851e160c3471cf065f362dd4deffa5ef4c2f6dee338082104d57a8ee340a06de3b8ca711c070271dc768edbce71dceb86c26d95e3388eab5cad5ce5366cca72cc4beef11cc7711cc7711cb7556edbb66ddbb46fb556ae7295b3d6eb5639ee95e3aebd564e748ebba88a44b57295db386e74eed7c5711cd7190e4e62d372cfb21ab1eee6d52e9f59944fabfd65dc6ff468aba23aba99c22836439cfdea0c8768547fd56a1f16558ee3388ee3386e7bddea378e0b9de242a007845a2f0cf7fa8d7bbd4502e20b62dc763386e7c3f33eb9c511938b66499cdef4a6294e31650eb5e3a7f7bbe3a77d628bd401f2735eca186f8a171526e9eec1d73d3b08d7934888f008868f483064c12f670b1d8c43b3a4094f26a1c2c820d1d35d8a1b6aa62466615586a35fa34b5409868fefba444f2fec914b20c53826c19d89916e9f5a98791988e9657429d67836aa470b0138fe9aa653b72cfd345551c0eed751c237b595a66a7083db097e38098d1336c1fd83e1803fd1e5451a3f3f3f118201ea5c1266b00bcf05138981e2a93a5d42222f22c11c15c71959b0945027b63413ba044f3a743a9fd2ca0adc97b26d5e4b5fca203c17c8043a3102ee1fc95373dbd8ccb6c13d5fff60e8f3980ec7c39eee208e1726c9c1147c316a590adc37d417d57b2f038263495e519f69f599f66c0b2298623407d3c151eee0f81c63f4cc42612fc32171322ba1508bfc20a9e04ce270f1499c9b654db623bb3288fc9112870a230371e3749712870ab35d06e2ec9bed2bba8bcf6ce3741797d4dc61cd8afef8cc42bc9db4d9dbd4ed991423f254d9304639b5be993224268e7997b29b7be0d9842a6fae24d25d84db84f36cd46a219ea0506bad9af6aa69b10c357b96389208134cd53c1bd69a4da8d2a6bb7889734597b2c7cb9c67437bfca559882516cfc613f0277be680ecda2fed9b9d68643ab26b570b3d3bcc76640f5db30f5f9b0dd91addc5d066a76f998d02e2ed4aa05e29e465cfac84422671de8cf848862a7ace1641f0c3d92f0c8331c3914d4c64734b9b975d64473743ac5d0ae9d28f2e3dd13385af9bd8127ffd7dd3a5fa78ea0e354b1c891344102452ad35af259e7ebae952e8f193912e6d17dd4ce1d08de9f0f66bb3155f8d6df6e19095f8e230a985ecb90567f6b9489cd7121f2fd81d219c5afd6621e6c1d8ab6d23ddc5a7eca16eaf54a6a61b1c93e0ea970813892093972364176538b21ba3bb28c50fbc3db31932c159c671b56699cd5009de6c0c6482b3fb2c53e170f65ce12c7bc94e331c9994c2cb9ee51570e49e5daa26ba80ba25533840a8db85c9bebdde9cbd3e4b9cecdbafcc2201f17b33ea567f65f77597ddce6f366a38309ba9cb665983e32daa06c75712070a194f250e1413e7ae93f16cbc87e54bf5d33db48832841dfc8e80b1420a1a2f47b8a6ee67fbd99e10ce2903475903471c6fe70538c39dbe898d8363ebe01882e802b390d7470d38c62e70a4813bd60c6a3fc7609892f1d38bfa70efb4e17a2c170dae41ac94fc8d6e9a2ec1f793a9d7cd1ad41c71863491b6793362a481b76bbaeb199cdb0602f145de49135b220daab4c1114807e912bc0d1f2e7a6a7a37bdd38d500c35b5487d5608a283e96f599665599694d763d29d9493493be99f68869a23ace999e823d23c1bafc6b26aac4cca721c537a185886635e19057abc549315d5a9fd7561182c5d10569de1a0bfa85b96855116655996f58bb2288b5e96926e699ed87259d8855916765943ba05d4019106df680db8e10776830ffc1e838b25f85d66d103705e1040fc106305860a290565240a81b8ba691976591545a72963d7d09aebf5f52613fcaec5ce4d11f943d2f82839926323c449131e2d8cdcd4f48f253e3a4786d8c019142461a2881a50fa1cc1ef436ce48c93243c384680d4fc2c41a24364888f9ee94972054e10204c9cd8594246476559c4162fcf7b3330896169a8f0f2cec929b9debb59da48214e9af06861e426fe58e2a3736448dbf48c93243c3846201058f313f39024d1b1bc19efb28afaba83f0c1a8c15324503db86984fcc0950f8a60895534ce9984690355386c9fcbb4792dcf85cec41984e70295741b01f7a4600f54d27009ee2420a69743ea53f650abcbe7d893630e6ef83c6d70ff6574060a298150ab2b9fe94c9c89919aa133d333eca17936e8336d01a633d44797689e8da7447579ea158575679fa47aa81597f451ab175f7c11be1ff01be197e127433e1b564c548a1eaaa85b60fcfca77592b38624b8fa31f52b52918a14c401a1f5aaca7054f1f87a9356a7f16c34a62e810e14e0f9608a7a4972022a1698a2287aa3dbb274471dd2a0a8675131cb715c55cf94cc70c0aaaa604bcb425d98f733a3c0051b2d70010886c78ab854bc004fa3852ef8e8824d178260788a0458a4261667938b27c6e70dde8be7e269224219ab13183ec2489920368c4e8817462754f89ef034bd7f7581d2699a1ee5e3a47a305dce699a77f698ba3a830dcfe4e1892ed5b5d9135d7c4c019fbc1c813a15d4cbf54753c4cb752aa817eb79fa3c1bf2ef34ec204bf367ba70620b7cd1ec54c56be95f7636b13ea8f88b0f2aeacb7b05c8131507c8930f045c38d1e5c2c1135f3ad1e59ac39507dc97cedbb65ab38be76600474ce2d924c6438cd9886793e8d2afc58a02470a638d67932ef5ebe67b3a018e87d82f895116e2f9e4b95cef2a70ff9a9a3069d49382b2a2ba6569a817eb97f54145112fd67d91867aa92e15adb09a50e19bba4de64f972e1d3bbcd2d5056aa9d7327f7521ce26d1a5be96f9d26c0dbca72f1e196df013224e941e2a4e85f42066ad285837194fd127a59425f8ba78191b0a52cae9f3a494525a5078ef5d31c618e77c5516949473ce39e79c334a0ccbf1307c33e4afbff7de9bd59cf031ce39677d33debc10ca9e685e8cf1595398a689521eb419a69c92146193c6f266c6d8645ef8664c09218431b8095283fb17840f3ef8dec4e70e45e13c75289d1ee8e07eb457cd1729d5b331618c125a519dd042cc117933fa110f153e7352def766c8f89c34b97676baa445bb7045382243382254ec085bbe9419388d70e104bf2df0c3828b20b0f47bbc2800bf1f2b009ba266ea32427dd913f1c9130c047e7fc903c02f0381e91435d79c96c1102e92b095810b1e8c818b1a2ed230878bdaa263773745d198033965c734503a4d2922c540830f60e09793240668d041f4416a721d81be807487ab084d830a1d301cd4f747439ccd31984bb12eebb22c4c7d63cb6607d9dc61abdeed5866ddb22ccbb22cf7428724abfec594cb12618d0b45cbb2ac697d641de5b22c8b1addb252ae728bb26eaddfae6aeb2dab89ac8f9400818e4d585542bb7557eaad732bf5a16f189663f04abddccd31d87ae86eb78e816edd1c23b27e51ccb25ad5340cc3eaadd75b5f22e559ca379bb76d82d90320c67e51ac5a500ac84a397661529e5d18d1eb53ac6b229135b2ac97edb03e42b16addac0db3acfa0dc5c22cab6ed6a65dcaba552deb66cab2d57aa63066653138543109f40bc56e29903b8add9e627387b5ab695a0d7db38d492f8e3611e8dacd220cc2aa85b98e61d9af5a351b023d04dab65048035d170e3d74738f6a73cc75b76b8dabcddca5b0cb666cdbaab6c58832ac0595c239469463440f331183df8b286484ebb160bf6c8ec11748db9e650ffdca324ba9b69d9e5ed84399b41514d26ae835bb39fbcb6c8c08bbd38c599665716ecf8e9255190e2bcb5e1d80f22c233dbb3925dbaecd6ad78175b6633b776ab308534a7f5511ce2aa771b1da7accd6ad9e7e1aa1fc9a18b5d9397bfdda5eedcb76641c877db3da338b6d4884cee5181da1ec46ac3673a72ef35663b603f47a6140d72ecc284694b71b23c2a04b477f6f98e9185d95ecb5a6642837432c12d16da39cc67d136535c3616d9c767396659716c6be59eb229b63b0088745b54bcd9ed583eacd106f598c8c6547a8b31cdcb73fd1ab85b8d6ed127447a05f30db113a04bddeee4016566d22ba6d22cc5daba09a5d825ab218ccd910f6b7fdba9cb76ad92bf7300cf66b0777d061167a15dd1c13b239b66a615703d98afbcb72d4674f47cd2e0362e4457bd05eeb31d0a9e8f4a23677766af3ece12e774c4607d36712f8600a0a6987217a8e135d6a7be06af385a3d1544129a514e5b88fe8455474aa4247a7a1934e6f46a197e2b65f170569e7b86fa0ab23bbc72efdc6bd3620c471dba7d373b6d66bc7b06c853be8dab113d8e413d8607a2c63a738ad7e3a776142cf2ecce85c0c95971ba9702414d125da441bc765d8b773a2ed621947e9751068f2744974ed33c9e829bf46560b8decf61aba19744dc405c129230b718a5da9a26a334977dad5eec55dec372248d3344de36cde72e5ec768ed38e61dab14d0355ed669c053506d97cdd98d9a381bed913d8e8c8b63a7bea76b3c98373ccec91c1c12fe70436d417b367f483ebd7539ec8228d5040d805596e9aa649d3346dd66a7dab2a5741419944a28903598ebb0edf84fd35c9a6fc75e8d7c85a28b6be1998c66916071abd024db7eae8d5ab5145998e72f39b8edd9c7bc488f0f51b31c5e6c63d65631886b5c40e03fb686638aaa9b1fcc35f77308e5d4ac63876f34a632d3a7761dbebaf974384eb7491804f707645c75e43a15028c775141008847ddb4476fbcb96b03e6d77a58a9abd7e4e87b15d65fb2531914541c1eeca76ecd7caf6d0b92cbb2bdb4522f40b6bd3344d2b39524e7a8f09bbc8041887028ce5c8ee86e3cc928078bbc8a2fc6547b9d8e763b6c3fa7661ac4f1746e5dbb5edef98cdd98d1161ec55e56fbbcafde1ca440ce3d8b79b635cd9dd06e3668847a3297bb64d1b37d567df46d3312cc3517d9ab25bb99bb1699b260c9ba669b25e59577c309e43183bf78cc02001fa1876d9259563a7bab472ec5897528ebd3e1ba363280ffdbab08b62b56315d75fbd6d1b3655bed99427e0133c3a56b163157b08c56a8d351d11c5428c412330eeb9e2d031cb3d0284b0afd8d75d28741818ee877ea5d89185598eebc2b0faed31d35145dab1ab7c3b4aca497f184525f42ce5a4936e4a6874ec25e03ebaf5cd08bd07c642dc7561aa5bd5cd3d6e44cc56158eea9785a92e73801eba1489b2972013611b866d58c42e6bbdb24ffb8dc66cfd8d6859afb29b7b60368b3076b7ed18b6617974eaa1c74c476398ea9655dd42220663bfaeab3198e1b8369b03f4ed30d301dab6bba203f4ed394028df2c7c825f26bb033db3a2eb2847c9b6bba26303dd1c23cab62d7bd5d58ed9ec21cb7db375746344186238b06f365394cd213af698e910e1eca263cf21ea8163a603f4d0e5b6a204e821d0c5ee8a0eecdc4bc0ddfa6664c76e7d33b6f758d1518f3d47ddb49ba5c0aea807bed1387055553686092caa30ac12d0332bb21903583442097197c69dbeda8c61cf28a5f9f4dedc0307d76a45c7b827d1dd06fab66ddb4643f428f48e380c604dfbd3ec8d9859884536b3c4b0607890a6699af694107d8a76948b3ea25c3bbd1946cab5d1553494eda06fd744a7dc41df7664c7be5154ae3893707ecd2e76b7d36bf5229bb7bf3760f4807aedd3e93710a5c7ae1d941d54ebf64cc2f5b862b76ea7461904ba9f5e2fcce8d88509bdc6b0e07a6a33e8c6b0e051e83e1859e54a12ca35fa14945f281404a21bdd0e42a1d7ae530ada34aa51aa5d524a29f6eb6507d9ebdb8681eb57ca4918d62ee3352d74d0af10ca2864eb47363f1dac6d2c58bb26036b35b423b0764d437912dd814216e21a5de4e3492410a805a4724127819e7251625840d7b10bfb8dc8d91cc3b2d977a3417f1908c30eaa366300b31cd7632d06d662e0fad15f9748224bc201ef42217fa40aea75d2551e9fbb6f007097213edf3a5ddae992cae79ba7fb8a2e6548f0ccad45e71cc1f35bdff4cdb36164fe8718d4a528eaf699ee04718aa22e8f571288ac793a958f2eb17cbea2892e42bc263b5da22e5f53491f92268bf174e60f8f4f26f327bab48d0d3cddd18207cf4b18a3fa8d66b1b14977f342d8e8a45b46485a87b27da48db4cdbd14e579feacdcda24ba9bbf95813888bbc4ceca33ddd9d961d2a44b2b4e9848494949993ffc653862fc460361db48ff605762dce36b27ba305123cfcecaed1bd2559efb660b2d767876b4e0c16e59a4f914ba0385b450c869e6bec1423b505026a6242a552110b3a506d794a0738ffdf9ab1f6edc3f20ce853857343db410a7fb22d1a7a7dd87767a4f21ea4dbf24d6d3390ba2a24b175be6bbaef11482e882726df4ac8f1da53feaee4749290785b81a38431fda2a4021d1943b1de54ec7029e7e27907d989b5e8540efcdb0ac2e68ba89eb8234b0a2d61469af1cb68540222c542d9363c0d3b1e99b44039ece85ecc3a0e95ae888a6412aa9c4a6439a26683ff48ba2b0c9c22bba0c71485ae0d9618864ca200e910aafc55fb79e1b08ece2cdb74d13c1a91862cf42f00b10f479362acb1482670fe922785ed94692b012a8993e6f6e1a98043cb79b49d3b939fdb60af0c4a2e95b77f7b435e85148dc7f62c31d3a13ddcdd08d0262d0cdf0743a0cbc4d74372f06b2ecf16898cf922e59f0074fcda77f7469dafedef664df15d85702ddcd27eb5f8e1bae988f385a5ce193d3a5fc748e7429431aacd3a510401f12f7c989ba93df2c1410d374e9915e4b5ff3c16e266126a20bf5c983e7255025115da8db399814b047943fb8c1faf5de0531fa6e3c1b8dad7740065b96f59e8b65596f0b6cbdab257a60961c3638c16f097e396ca8c1900964f266509b5956e8b33cd2f2c89b01f3e3a9f166c05f7236944f4e9a6525e544dd4d7f396b7082330d6c7ddea26a74c9b27e2387fee5581587a14a1a6ac31bdd5917c107297aad4b615da20e67a63bbc4b0678b6cc67156ee6b5ccc329523e337d011138e5d3a65b48cf54cb6a8271089b00f183e7330a7e94867ba6fbdcefa27aa20b1127e25633d7c28761ff10248e6a7086e1bb1aeba17aa89e4c6d7ad0eb42c4ad6c28215d05d23f86a47c5173c06adacc21ddcd579776da40789180af5edd954f9b39a44b16870737785644a04daceecdb594adfc464bae769702e12b9bca8664df051f06edb4892e2d07e241fc4594b758f85ae03bfb5e0b11b6b2213dd7226c6abd20e9511890489004e1c302c30bf1b6f0b2883c2f090230fcf57828aa8b9e8aa7aa7a64115cfdfeb2280b0b750b320bb98554835c82050c5fa5cf110c935756bf370a58553d5dcad5ab9eee84e0fb955f97ac2a7c5d18699e7415115d2214220a218410f6a6a1e6ca07cf98032b9fee26842c782eaf743f493ca036a03058b13e5d5af97cb5a4a5ad90e4c82f30bc0a308c392dc030e6cc00c35b2b04c35f7b070c57be124432b9b2acb31c8d6358b08570e5d54e7713663bee576e1410db9be5abbf0c0918792b9eeeeeadb4e86e5e293e55bea05e1806a5bc1512216c865808593d52b552fde1da9dbc1592ee661551ee7f394a8a4c31a74bb94272a44b79dae8e420e952ae7a72a5048820324949978478f856d45dca4955cfb4999fc1f340dc4c057169dc6a87aaaa99482255f0a63c1ebee66a774cfa44ccae9544ea2ed3c0458af0433bbcb421bb2ead5c9ea54bf6f232e615e38793a011f7f3309e0df8f99557b29fd546f54c0add5efb1e01e42bdb5922acb476a58a158bcda03e0cef5421714c6130c3216ff48acd2bbf8743fc4b30cb8f2f8cce4c9b6933336de88c8d8dfcb4913612e7463e4f22f29046c88669135de01b60cf139c3cdce03989c035d0a646a80adebc4f0b7e66009be400e3644a8416c1535e12bba77cbad3209ca0920b7b600f168281b8fc55391b9b7b6deebdf7da0c6103843bc410bf4208c1b59472071097370a08459504726d2c723361d0da4b2b6df6e992921f188857b2676cf7ac42440bec892e2d37e690ba4ac47db1c5882a0712603e378eaf1b82be962186f8e419628821aa9c1cc72c7106e175f3d38a57e54023a60d24c0fc9c55e079029e3309784e20bc19f3b9b2029eeda34b40a01a9ecbb4892db3830c31df3f88747143073c27ce17433c4f1b000000040af54025b0b15d790262f82824aefec486abfb3fd319205e1da6ba30afbb7b1988ab21374549a58c744988cf5338d185888f5e7dd25436f6f7666a9e081b8b74cb3c006c34d2ddfc1036e2b0d87845951912d35394d44cf5c0503e3210c7233127ba54331cf6a88f792f848d4420118cf25c0476222d6c622193eee6af854a66ecdd43859729a419e3b75410560662202ea50486cd10c3f894d199eee625102a7c8c8bced09967030e897195bfa7a272697fb05089853eddcd4fd9ccfc24cdd4384931e6afffa22d80a7818fd48712daa18dc2faca8a7d18867d58c5420149160a9962a19028367f7e643326b299341fb25050905da976489982b0274eaa07cf6bcfaa5bed7489de5afdc41eb7238085121f210c41fb81d80f7eb20ba906b985cc02bfcb2d3ca973052d27ba68353535115eaae5d464c953135de416afa5af86205dd8d081c81737b8679e20b79074905d4835e07e4b27b8cf43c549cdd3a9062678fe414882235611c1fd69ca740c816350526573b5a1e6cac6471e211dd1840f2248e00f109c4e323344272ea99146284c5251ab693373306780df6cc154017e975f3c97ec35795a209148241249e2f7ac27ba6839b1a54f55524604ce5a0e1138673db82f79569e8b56135bfa941002201de04660ade60ab87f6939d145e5b5f4ad4ecb4e01787027a007f7cbd1de206d8e635aa92b7bde15dc5de31c9c2b1b4841ada64b40b42be625c1b87aaa56a3a44bb9b2f1e95226499e2ee59aa44b534fdb1ca97c7edc68613511927329a129b2833109f26c34ee67573c1b2db54072e40852102ab5aaa8d1cdf810a41f2a0f6e9ed7d267782e954d6ce99ef39072ba39c5f740b243f50277ac241bb04a015ecb15d91cc7b32909a7e8a1c24b53bca82b51484cffc486e99dfa53b6239ede28209e8789954d97863c1b48baf45e94adfdc080830d965a0dee0b79d04608a638c04d6b384af35a3ad2173c97e9b406b85da63bdbd21fdd35cd909ab11b4a13b11b0afa60373434f1664aa3f212258ccf2c388ae86b89af8b7f46bc4780f8f7952a2094326a355314ae4ef6a466a2cc1cd09f58674ab07417839a2452d76e70825bb611ae5b39d4aa3217ed342ed8398ea7b79ceeca74ea62dd5156126a144f6c18feba2c16691c9184049a8831818f1a21448ce420e1691c7f69d6c7112e0aa7f7cd175ba760670e60e94e74bb9a840a458e87a9e780ef11205b876f1082db01ad440ab0f5ae4bd7e3a54414106788a737fcc07dfa6c3c9c3960c387fd04c45357803723d22baf89c96703e28e1de1d4e4275b23a7004b2c2f8d372316a9f9ffa9b2c0020c448051b2a3a41f15e3843142a824063c86c97e078ae8fa9a8a50fe782e359e2148fbfc48f9dc3e524a09654bd9ed438be80e3e0435bf1d1e10c207a918e3d43f5305f3375d7a9fe8a789fe7d87a40373e4a34edd2c23e9e40423f0c609324897a40eae2c0dd9f076b12582a0f6e1779a88d4fbe11167b703df3cc479f306e22967a616e015f0078434d088e9d19b5fce842e900fd2c8862e80f438a8862411d45c4411b12fd62def50f4a00b62843f705b1217f0be38d34d68507a27fae94928a7a9d325486ded0eebee614c74d7f740cdb0a60742089d883f3f4df5e4f6c112c6996ed23ed4f4e04dcf26ff58ae4aa68148fc284ad17704756b18001a51ddfca8070d10813ccabeeea87d75c65bb10fd4fc7672785014bcf14ebd1a4f1a20dafa5385175c8169e097f3821f1853c30c16007e395c30c1dd83e7319a1ff21335654a5858f6679663a2917dec4797e43b42201f5233d421f25ee0214e8444601178f39a7aaf214dbbbc97f73229bbc709d7f441b453e7cde8db50024ee0b9d989f231f2ecf04c1e25d4f71b58042331c6d983eeab460d7587e749cc02bfc79da6993c780b18e69eb2d802942f57cd45a383fa9e2f1a0cdfb2ffa28c7707084838fcced47936faef7007c73b7578501f7cf783ddf08a26c0f8de7b3109f59d065bfc63912fba881af7ad4ca84f76092e210e4570fcf1b5a9e4388e548a1e6abc94110997ca15b569f0cb02b865eceeee31c109645ad67b778af1d429fae285efbdf7ea3eda8ee5985e96ee6037df750765c0cf372511ae773385bb77ef762c1493fa2019ae1f92280d9ce5c7f0cfe6febb3e4b775d77b0dfa576812ff0e5e4018b22acc08a15f8f7d2941ea2f0687040045f5ede5fd6b70af917485e8e12a6438c44aa249a2a68dceefeed50330df89259d78a8a4774d9b1ba9be8d20abc4683b3464359262c8feefaf5d0882dfd6bdaa17b7a68d45961d62d6b7ecab00a0775d237fcf05c36f0623c1bd6a921707781fbd7fdc10bb06e136fc617f54522d489a19058661ab9621ed83a8c3134f0743ab176a9dea72a5ba35b9ac294c2fd3ad9a01ee7d7a3872138bf1f1a35ba6b29600f2aad2d50d90fea217e5814810db8027406c7bf5030801d839a5f939c48ed204e4daa28a91dba34fd488a4e5352749a724e39e58dd862292a6a7e4da69ee822a30f8e7d3aa349e19a314218e3af098bb983c70def84917ec89e8c1d7928aa5612e91d0ffcfe304bfc13d3b3096e142a983ebfcf7714455114759fa02c3ea838ff654fbc7bdf7d627a86f2f3cfbe27e17d825e2be4a77b1a5811a2ee17270faecb724286c63bc3314596494acacb32d49c90cbe87d14744974d2fb492cd12518eff3783654de2958ab9a76372a056f8737a7e07c9cd9fa6963261f7a8f4e1dba8c5efda22cc782ab3fe061ea5615cc1351446c45b55eed09ac27a8e795cb830af8827214cbb2534c8102299e54818b39e4610f7c78f2629d052947b9565857b1a09b62a598e23d79a15179211dda27ac20dd16d4d78bf258501fca59504f3a0b526e3d0b4897145bde534eddc71650c716eef5758bc8caa042f7af419507aeac3f3ceb2fcb66af768aec28e8f1924d611d053d5eaccb82eb6510b853588716bb99c270064fecf96581fbd96561e08a4cc43be0738533bc565819be8cfa7b5c3d8cf7aebcb6facbd18510f8eae6e842c8bbc2143418423823737421a4bb874fa33bcc226346f6905495c92167fefce8093299c01a9b2145a693191f9c1e254b7ae6cfcf749233ede0b6be824d42ba94e78f8561d627223e66bad425149be9c6c87445bb3c3bec4c3f6aa24bbff4b1ca6aa2f333994c275d822bc0fd1f2518bb45319ae1988f72a4ee0e631d7bb68e1d9b939befaf54d6637449d4dd4a7591685cf9e81e4904e5f9c06ce6f0c4e5d1a127870e3eb85f2b6b4e335df2d125192372d4a753c7ae15d72d6b4e285dae3bcd4c4bba7bd7afd3639795ddd233c3b1d2826addba5d9974f7b008c39b313c5591a2e261f88c9866f0ebf1926bf53a88fb55577dbda2288abaf9aa0eabaaaa705033c351eda0a8ca4afc5e093e57bf7e55b63aad28765a55d5cdd8a5baa3bfe82d7ad92ef2853e66382a89219d56784c7f78aaa921e86e3abd4d7437bdeb50fbf9f554e182e25d86c239568977fac6113fcc0a0f0a69176a089e8741ba94cf44a4a85a49a4fff1901e0f85462e3a873429ba345184aa14a99ed49d4647c2b988f942913a1a45e039e76422b6ccae03cc41c344a789d24c5315b23012853c78b1a38497193ce1290665c013a93b48d50109b6b430230b64e09763872be0fa0422f0cba9431d306604521de81000fc72ec900339f000c656810e86789a931452277e394fb80043ddf0cb79b2733dc1e2c29e201161f91b5fe06079067469d2c11c7e394f6e9e0001e7d390d10baf082c4f63e48e56419f6041884e12f880e9cdf3524ba294a24498b680522ac2f4471430a5c1747ab20cd50e264dc842055ec4e00b2bbcbc27586241d383e565cc210d38c30f0b1a2e4c3c5ced82238ee8523e354980c6d3dbe3e5f55b79afd165e55d1ea497fe9484fa2358ded85a5cf0b20b353f1f9af6b18522e8bcc087408994d0a7a749853d21a0a920cc71b202fc425049380ff5611af842ef838a05d0c097957957261093785183272f34891740665ee8a7bb323ffdbd4c80a200054100b264e685de95797950a7b9c6740769624b4d6c996aaa133704ddbdc75033a48134d1e5b5a1093c48c0c20eb248c3ce0b7c8db2f60f9c2e9041d708a0a186daa731a875c993a21b5cf44774d731e045244a5235c68b3deb243961821ad45c2b3bd4fc7e96d4646fe0913d21f0e287090c2cf9e1e91142cd540d2a4cd2c31373b250a5cf9bd1cff1b0ece2cd6819a99798135b1ade1c7f7a7878787872de0e0f09c81f1fc89cbe699c213553110b153ef7cd0d0d8973c30b1a358ec4971824b688a1e60e7732644e4d133c4c789183fb1850f2721ada24f650f3e36182e73ebae0caa9a2cbbcf76874b34243cd9d1575a8449e8cd73d119633a7d079f103f765d480394f06844aa834deed280c6aae50b67cf75e272347563107382c993514a505ee63f254fc65dee0be0c193528852c36be3775f2a711d0bf2c30d42fd4dc3e3f5d82986a9f25d64fcdb3a626baf4674d0deed6892eb28bd7d2d7a93ad027ee5c486a7e393718306f60a50368936bc5c17d11b481438cc88b9a482589baa4d34420112245aec09d6303875c5fa819fea839dee07ebc8937427a08111a9c9b4817b17ed44c3511dca7b06973a38822e06083fb4724b1c44bf28ec8fc091ae32920104208effb7cb432d4d4fd684585771ed15dff7a37c760f80831d4fc74e27112308925ba14a38c33d4dc755746775da8b9e22afae99a19521313c0c107eea380c262401ff38fa5638c31c61823a513a52845297aa3e9c5a6354177338d803e5d43cdaf8909662c39d44c4d23978ff99946004d23a05f715173cf482fea650d0cd23472a66796e07efb90333e2490fe813b086e1fdd354d773df366b4f4892ef27d5883e195333de3d32da99927a7192b54fcb4c07d59452c988cb6921a01fd90192a753925948a9aeb8e4e9be0c1086184113e9b63e49b6fbef9de7b57a6bbbeb4e050f3fbc1f37d1938bd9fceb5767104f7454ec8401f3170a4f4c207217c10bec719a738c52946118e2f7e62ddf574339c71c20432b40d35c31ea804833f1515177c792c5617b56190dc418a50580c0844c80b6abf6770864156e0dfdb260c3c92e80208ee2ff1b4e853f11739610d1922d24b82472320be4a438dbf2b5560418419dc3df10a3628a914d5850fdcc748316488a63f16b9806bc524bd11d09f34a8f1ef05eb8cf40a6a3f5ee1e383db474849e50d4ef08b0877139c597cc09988869e970c7e489796dc6099c625901497c30d49babb727461132f88f5cd10fc700e3718e9d2cb821634f8612d4717365dba39bab0c18fa55bc826e7e79c3b60a69c179c3073d249270b33df3a7a4ed37c7f769c70a2d36c0b33f5a0139d60a6c6263a4df3493a052b84603dc65f13052f4b891434be7e41aab33210c35faf5f59566559958ce9329e8a11fb8bb471acaa595573aa5edd4c61965aca3e7ccbb2e02dcbcae8dbca49adebba60436aaa2aaaaa286a55546555d52d98caaaac8ab230555559535551f44edb0f2245a0875760780b093eee4f78b3f54c097ae121b417bc0e6f6681629281385ed2c63f49a34bbdc3e2fe8d485191ba24916addde375314f547dd47f8a246fd46bf2722a66420113175a937637e0ea1d2439c69603470cc0818f06c74d5a5faf91a457469feaa16ebaebb8cbbd1d56aa7ec8b82c24fc43ecce488b8e38d8e1656168a89379b2b9c5d6cbaef3a3c0cfa3e84395d9a4e1b5221d407a110aa05ad755f770f36bcf16c440c5b0548c0eabd1994b65d51e2e1eb9258052b295488214565ead5a985a1ded89b01ad23d4f71b0ce812fdec9b255ed1119f3c99de0fb7c51b8a4829a59452ca25dd3b3728c1f1328ae02207c7b35031a66088125eed8e0a351fbad4e8963ebc346240a307efe26287891d31d0330c717a78e74f7419e2be6e41f9bc280d2994a30861a51042087b0494cb4b77500e518ef297e140a13b5daa1edd61f9c4a4c90f45fd30a17ee0fc994cba84f2799ac5739135740b3ca91af0a45de059ddc9a48774b2663ac1f34024911fd266f281e7e56561d864d2dd0ff59b4fef21afc90c9ce80ec5b2b8cf465715c7dd67156c137d5026b4099e993ac1f308cd70b09c6638e82b8b62bbeeee2f6857aa8b04f5294be261485d96ee2ef5207521864279e80ed5a208d5faf4dc37d7a24cccdc3678f611968fec0acb7ddd517f98ea2175d6ee9bbee91bab24a9caba3b2b409c096844f5790b5ce095ae10a021401408f45b514308712d8aaa95b4b3b34365ebd5afaa8d587dd3377467c787fed026344997b2d3259409eda14a300f1e3fc498da48d5377dc3a30ae2f257a66d2c42b06841844e93f474492ad9b997565a1e419cfaa531f95503a7b6a8d44bd7aa233dac18d108020000b314002028100a8704c3e18040cd54c10f14000f93a24e6a4c18a94910a41443c61884081000018011101001860900f66e6a32d46b91df771e695ca8fd92caa2f7b9edf4f7362ad8bb9bfc37b69a8251164f06e1370eb5d91a807a4cecf55bb649271e90a13cd6147ae874f650569e84cc82166f0d68aa5969bf038981d08fcccec7040c97a401b10876a50a48a7769c77866ae91cc7c940a20a17389df9511c41477c1b8e608416868b2e8d945f53e5fc84ee28e0d67e8b01ecf9046290065ee280e803e8433e98915c2b312b9d651172e21d16c2ca3130b45eaa04f2214aa0c4df80299a8e41369703ad9efb52437c6cb51ce312964526ed9d47f0e80353a0dc0c05051223a6c24fe3c3d1f4d9200ebc8105df00e13481430dd7cd20ee176ba984f6d9018eae3d5478d24c5d8abaeb10af775771ea3f6d4ce1c5586b1d6465cf45fa5691182e820dcb6aa3ef122c633d3d53ed440694343a9cd13b1d38c1479c6623bf5782b9bd46c3605c068f95a10be025ac21623c6f76950860e8b4e776b9b65756bb1a5e8658f47c47dbd4945b5e2dbbed840a6e242e735dbff10a400285d43844418acce8fb91aae745346ef71a6f4e6eac6f2c44b4f4c06bfb1851ac4f258a578cde6b7615d3df90e3eaa21f2fdee3ee3cde93c8b76cd9e7dbef139c710b7f76f35e8f971d5dbf59b257854948124cce97e930f8500b22146e46ee73a7dd901b57b968d7e3c8c5fe22b418a7b299b01f103938150f0fbc1fb3e714ac7f2cb8f638ab5279d9a3a0252920415f2b7c35a27c85e09a39d39300b9d66d2041bae16de4d1e31f9ba88f2ebb0562c28e3240dff092cc52d56acec81ee23445698d0361925756f968b7012581bcf89a88b8cca38a27960dd8d248d5bb94075317f4f6383b47a46e5e2c01be305f4f2a06ea96a9fca938c52e2a9763599d7879643675d49415ac0016b7e703179a29b9ae055d50b00ae04d6e4242aff08fff044b04b3ce871a01dc80cc0e5741783bcbea2e7d213e594863849cf944b19e4d9309392a2e27b32e1c6d3291a7525519b1ce063dc21bf15866fa09b430b7f77895c19aec028c2bea9b09c9ca04d3a6e83ee1db93941d059f8ba63927189d1bb1baa390b091282620b4de9f4d0564251762e7601c5b8342bce4cd97304955062c5fbac93d2f19da45fa2b84f02a915184d129bbbbf82f3187b9f3d84d3e3bbc26205979842cdbea469751d100d57696f59bfd9d7aee2f339039f4b7367a7bbeca9e0fd59a17db6e65f16b7a89d50d0e2aac097199a6a544bf684d6ff1bec78d60d3f3c6c38fee1440871d92b095455f46114574a2882a6a2dee8d82285c1b74107bcf94ccd306651cb584c41ce20384b9f66101969a33edd7b8494bf4a8d1b90b75821677c9e2bc0bce2b792c58f9850849495ab902606cc32b808d2e2241c61a665012ee90309007b25cc35d0ef18b88f23a43e2f7ed14ad116892091d230c3add627c3cfa7e0b5514e590bb8ad54847bfcc9ece10c41bb1d7c55f3be737ffcbd3dc04328dba2c1481278fb6ee7a5b2f1e5425c75b7fa416effddedfc703a1dec1a8baab1b388f2b1ce5225f54081c66e6c30b8c80aab15d898366c5964cb7a02034fb68c0de3c85e09a89d3b3ff13b160fce8b83e4a18691389b1b2e71367e41385687ccc1fbd6e4ed4c1b96c6144099d9fe466cc417d2f1c4d0d09fd587b04df8578beee89d641351d2bd0c4bb5d7a8854ad3208de078981c894d5ea3f5084b72337bda0c6ca356518d2e97d54aa77f7cf33a457259d396bdc15b21f767883c05b1f65006d44f0063ecc8610bf64a7eeec17f192a664a14fc4b9aa499ae878a276632fd3556e218a4cad95e30e83b6cd26f4224e32bd6edb0a1e63e2146cea7fbf73936e2a84577d35d59847ced1008632b29131ccedc36b2b0d70db5d05ddf92128d8c59c97408f082c7e20214b6b0273d5a6e7ead29ab9248b2706bace51425bd313230bc1bcb5c06677ccc601a7dffb52d63c31fccbe1b1321087b340f55baa40b479d926cf9580431188e3fb3f5e8ee59a3bee4cac4f1eaf61858255e9b1edfdf62c7ff29629518f42e7dd82dda217c5cef35d425a7063d61566e1eb14d5d3f0a740916da19e9698169bd02a519747d9183c5169a0f347bfe879b89141bd717cbad10f6ca797f771ddb7f8c896d28fef91da110974e586417fee9d854d9a8ef5883891201357b7ddff001940483ab0f142bce4db06830b116533982a36398525ee299b33df7fe844c86124390873e0c9be44c94b1ce5b15a06b602ca4ebfcc1ff55399af94a9b856a4e1ddb49f252844a333c8e6abb0f5fc88c352f03d145fbefe4093cf8413f7a579ab0c68624a9d085b6941b49a2fbfeb68746c901c9333c4c5aade623505bc3cea220600739112e6a7064cb089af15c58b5185e87a98a7506546465489e02128cb490a01dcc1e3744fc6077607824715866646ce3b713c0c46da0280a555f159b800857013020a2a2e64c5a7b640d2e92246ae154660cbc14e283824e3e44a343025e34f320b803abb59fa609cd7710b4827e245044ac9e0f7bbcf891702a10d359be46d5a4397f784c3c5b9643fb975d22db01d8bf32fba933b53498716f05631c7f43bd6da73642e76bb94a5753ad30c9e17d6ce2f86c40b2282883d865e6c7c75bc38b2fd4d807b657a6dc1161c7ee0709d193303244ee29148f0d5559b3d023c8746b596099c54515cabc507b12195049137a2d119beaa2326925c6bebbb61813f1960b2b0b7ab6d768e1f638274d9742c5fc41b47d2fd6fc6105d7134a45da08d5720d2b8922fded4eb49f2fea35ae4e9366324265865931660b0fc1120c98323a64bd93d1e723658855e506201de9a9295b42ab05c285e32356865844c7244f4fc02226dfff217eb1df29d2f3c90e36a7721c6e8be1c311a8268717ada1649348af48b79f9628f451112b3204e88bde9817fb8efd15f98377b0bf5ad9aa8ca8b95fad4e6443730339a1790bc74e54bdb5e15387dd011af2ad4d86e65c4793960a61e369a369a2d9149e140f91a66a9d75abe4eace9265b65029016d17783b6ddafaa3ad89a4b7dbb08ce0c3125c890d045450d6bde7e378c0b43a18ab5162e3c236c6c014c4de95f87396d0a94409e208de0c2a675da527c9be9c18c023d54c90381124c3ac74e2af25c67cc1ab02c3a7f2ba661046394fe260320c2ce9b72d207bf95176ff25cb0bc62ae6443b8c27aaa0d5141f3df5f8ae6611345938572b23cff29ac53baf9817d234e977e040073592d9bc1661f9da88709170efe71a4d22728630e1b0ffe7256a5ffb9d3d571e770b946ca4f7b5a1e086912282528053f0ea392dc3824111c59b13f1711452e39e7b5aadd0c862e6bfe7904f0994084f0401d4833bdfad94dfe02f9ec9015c8d7eddcae1039b672eaf5745d769f991f5050485aa9fa41e91efa5a69d29bbe9b920c9adf394a52951808e91171137b8543c9cd2be169e8dea0616abb77a86db7166c1d43234b658564dc6c0003627d5415e2f684365834b9795ce168720bdee5de74346d56ddc97541045f32555ca381392324173b64e9a5c6e3d2f010f3c36872b8c238190056a18521d329c2db9bbf232eece7e82ef7c6ef58a3fc03c6cdfa705b673b7daec36d0643eb01995f512f12a45add3183de5bb5be0b67d3a79f32375093b42ea37e8ee6cf13542cf7e4fec751cc1884c3b0f1dc31f8e81c36f1ccbc95bba64b4da7036ba93f39e3dac09a5e2df3f0ebb3085b525a5aa074023b4957c19fbe5b327534707cec4fe241a831ca9f3a5a69f7e03e6ac174e322cf8eea1dbb135870412e3c5c02c9347ab392d372ad23ce724f461f0264f7caf93d64e62de4b939d90c6cb0e2d79f24929a06e7a2cf95f41fc7531db054493f075978712079ac9df3763b2bf908558a11142d579f6d432e598acfc209d64f5b90cda7d938623e194072ee76e3c3062a0ebd4a7ef04eed284937ab16aa97c789c9d5c051f2a59c4685cd8183a3a49e4361509873078dc57011dc9b2450f2bf5f3b938ac04859c5a592484d147c117fab3565909aa545e8fcb504a8c539723d46105961aadd5ecc05570b80cce37036430a14b4cf1892536ac2697d0ca53402db0463bf104f39edb9cd86b3acaf6dfcbf8ecc6403ada4451612b6e5680453c9e96078c5ec8218e33e19b59e52ce0d90afc8b06468163b310be408685638e52a41c815083c1cb9ed45c824e0b59cc3bef3c994073694e3d46dc6b81ef11e6b674b64225edf4678d888304f404f464dd5da7a1181dfa42280ffcd24b65ff1b5d4e4cf51461995662cb17c3b3a0bb974965bf35a7b8c5d8848594f58bb58d5574c95244ea485c13e6f81343a2a1cf51de9896c6a8c8c83f5e5dfd0a082215d0bb4a1983fd307dfe1d2e97cf11c2bc889086e915aa864b650bbf16a6184af2a452211d960e1fc15095b8a3b424406a6d0a1bd74c3dd342e2561492a0392f3451ddd74845973ad12c15a39b3f65ddcca529590344253bbc17fbdb17f202a336230da51c89907b209d254b6618128a7dca8597818d4d6de42d367fe0240e12512c1c48dd95ec8a6cead982c2369b7d0ac7ead567b343bfaf299437f259d5cbf4be0a34963792d77dd1bbf9d44e0d0f48b7a2368093e4aa5e2ea8e31a66a6aca19ca2b95bd49b3fcc04b385131457f9040585c740accaa3f0e228fdda5156cf9ada7d77fc97570126a218b9e567cdb69355b82a611ce9be0324eb9639a22fd41340ec7d0fe15c6e495b4a27377e07f0453feca938bd555d8078269c1b2fc6ad145a1da882d50d7dd51683b9295acf8b63f88ec6dd97c1d79d6edb0adf007f4dd0556d2ebbb749796ecbf29dda7b2922267c92380a6e4e7d48fa82b089d62ffc2ad38b26f70017c22f0741e228e72fb118c6cd731ed67ed52575488fa0561dc5fc3609620833c4e4a68a67adffdc39928455c852ca62826df68bb8511e83fd62ec300b88f39d2a0e0023e726142834021841280bd9a8ee8040220d1507115543f3b3e2078ad6399327bb3893d87b8372540ca02c0bfd51c96e4a38f62cad7b89f44de9fc04cdcea00ea502d5c7d4e1a20ae611fc60b0f116208673a6856528608a42515cc0904710fc057518fbe0ab829815555fc85d3d597189943c98d5c94e8884aeb2858b1835bcb6ec7f290ec2587e279d7aebcb7bc8873be07a241f08c6351340ca62801d096dc7556cbd90afe63af1c4843a1cfbe13118a2dbf5fe4dd2c7a6d2082afdb6ff649a85abec01c4f251ca374b4f8ecfd948300d075e3e6c67294d245ddcd7dcc6d9d85722e899f2694f893e746667591dff4db59ff501fd50108494a73ce45ce18513c6b8d73f1d30dfe03a143a5740afd3853aec364f45a0d40edb8acfad59869ab3896555412d7c9e6acdf6ee6e4bb6e4249facc35f4a7189bee782dc4d9a3ef904563656075b5e254ce257df1db7a352c1fd3f133a4fde19f83430d76a996c28e92aca4a7d6397a81af18235a80a83182aca32cfd67826d5127a343748fe9050e99205cd4ecb5432292e34fc4d7384a00ac6c8ae86d1788617205617c2ca027918acf16a907a45eb57c3aa1fdc259981a56d8ecd65e6e16a0675fa5446f1b807fb0fcd2439170e8313778bce2676929c395548795a9faf92fa0b532ecb13b15ae71f58037b27d3b223b176425f2a86f48cf4792c3842d9f28273232612d2592031a17bb5b0a8c0d69b58aa36e9474d9abb948a6b81937464f789828be247534ffef26a8163e5bc012037a781b9f0085897232cbc7eb498bc9f43936d7c70d789c9a99332ba74ce7c2a21018b43e2fd5d9408ab3a2782170fbf56fc58794e1940ccc350a450458d589eba9d8dfc8a6b30238951cc20cfcca0b334e968e9922d52466f9c5166b4e170e27349981bab6b6c4b514f515378e3de81c895496b1368d82be5a8198a1370f3954ad9eabd326b452e53c4dc4c8f9c9bc975b95bd102e3ac2140a3d069ce2b7cd87e88bc682c84c9e2ae70f4fb8214148d9f139f0a4719977fc6787ae2a420f5c0be8deb8549a8f5fd2ceda23f234cd9f7ff94da8ec1823c7a5a472952f30e3b87e168eae7ec17a34ada7b48c9a0a3305b7125048f31e9d6ac2eba59250abf51fd12b4932c92b4de3f8606889957b8f071659a91c3cfb603a57f4dbcfba51717b0bebf6f42b1599f2fea5dc4cbec054ec607849d2596673c7e226b2a05c00b28dc73f3c9d1a872c9fbe6671d3abd9d40d04076fcd41194458326352df07147c59c77ee7eb2dfc51b93e1fc6bc4b997d99d61f67b91b3b50690294f5ee5e0a2e0753c8aceb6679fce059762eb44d206884516b2cf39c20a260d3226977186ea22895a6c691e434fb39220bae91172874e5c22e7887fb88ece9259a509df7bca82621a30bca8ecb20c1bc90999840f399bb779d97b0560de369c41cbc53d48649840489a96b9bc774c93d7f588ff9dd4001b8140a2bd5f9a28b5a9dd3bb267ad22d918ba0d04e90ff6014f2a9e008821f0c44a00f3b008c146679cb27c274fc4564815038d53bbc9eea30660c9bf00dfe43e7ea5d2c0b5864ba1c96c28370a805059b0b52be5bea140c781ea8c847c1fd9c41c144e7af88d3b45ba92355486dec20d6a62d31567b6de73d3cc6d3e3a81930cd802de0abe26dc054bcdb2e990102e97c02d37ca64dfbd06375ab80dd07dc3465a8b9219c94fc02a49aaec29d20614c875443373572c41ce6bd03ddbe7655a324a5c40181fb371394d03dc98946f8fd8a00258baaef7d2ce2d1e1293c704de7e9ca9b2a224020f9bb4505006e7dbdc178649172c7bb2a2fa05529078bd0ac0c521eeb34218955d7b7726f3ef67fb8c8f2cfbf1dd31798d24ad29a7537a6793bc5ecfad1150b967a0547352363e88896b8805ae0d1ee046f3c4614684c001ac2317e34f21964d6d0fe56c9178fc2a80cf0ebb8cacf00ed6a8e9b296955baea4e8ed736146c805ffd3b11f0c3ac73adbe8619a85eb343e50f2f8c71490337d1a6c7b022e670f996b53b55df6fa9d751cb36f5729a224833a9494fa99d036a7cb15e077275f18cd745b01e5ba2ab68f6fc5e512b9b59896013a60382a9032480a855736f8be3329ca9f80c79c23fb197c4ab8d704f3b08905f379c78bd6d2c2dc02a20e8c8f1182af2a27566c9e318f7c067e51531b5da76105c545093596021b84d224461416e16404e3144f2cdbedef80092b4e79611b759ba3d9d3bcaa49fc514ac689230b97b5247ed75d6ecf4dd728b30689fd5b68ece365e9438b214f4d7b068b703c5df04b2cdb9c51824b5e35365e8e720a6ffd68979808582a031496c7a38b7f98a767e24235020354894939754aeb5e4b25e0f96ca4dd9c71bece173ca0abee2f6fcb0a6d1c5916acd766ef9ee067318c0b4ca166c069fefa748951983f1c65cdb4163339bacf370a7c8de7d6b242d7b9e8db9f514845f26b26250d4ac15d3c83b4ec30515b2faf5ccc6c57901f8a8a09a8d490a552cad227bfe610b3b94d334d0311725051f028e468c3e949e22a75aa0c6ad67267794e6ab36dd86c881e5be5733acde7572c8b9ed3a6a0291aa67c832e32adb9b1709454ad12e3cd5cbb4ac9c6fa4f5d495ad4b1b3416dc7cc91ea5f25dc07c9bb644accd06b633d678cabb5d6d300e8dea2b178553b1842059aff1794dce36a3f2553956ebc1e69e830e89307509ba3cab81021868f4467be4156182080bca66a79579fa9b25fffdb4ebdc2a17de6a3b25c0f630ff24e6e3de29801960d526605557e211ba4d6396e077bd0083ed11602d95780724b8ceb79f342fe8b3bc2296a653e3bdb1447fdd742435f69324980d91c05b7a57b4cd4fdba89650fe928117a27565da6880b17507f18636a29f1ab9cafcc6da4faa31139df554b561b5b1107303dde8497d7fc3ab399297a7d6438a6df0df39ee5019da39100e19b14a3eb75143d42e2d4f175c23d4e0050b593cf4d4cf8a9d57e57994c36558bd43d24526006fe3c016918e8baa8aba3c0698d7cfb4e0469580eda73d06bc534a9c7c41cc8f596f18da082fd17fffa5448000920bfb10e6f9e5e17bd358664fe895fd017082207dc819e4b8f5f48fd244901b26d12e5560f78db9610163c3586513f50ffba48e07a4a998ae2b1032b2efc25f86e63abd34525556691a4a45e22c96894278d45fb3988cf8fe5aef26c0f100b7d38213f0dc24fdc2135f70ddf5414e7f0fd40fb37cbc01c7cd596818e97c3d535346397db8b90a52a6a6884bd7d26b49e6f0bddcea9412cb43a5d6a3ea3631e217eee162ea29586c84abe88bb86ce478e8cbfa6c842784bec0bdf2763b87c5268b339c17d25914adc478aacefd6c1d36109cfba9cfb809cf0bed09b067449a714f430d3373abe11c6063a0254e65de66979c70e2a26191ed3bd2702235ba97491a2d43d1bf4151023fe184e733400d09dd5816a790ca877e8c14e8a020c04e7df30ce8522f690e982347dc54ae4063aa8ff46916fc4724ab3a79996985ec9eba376777f58eb85ae4c35dbe52c125ebbb741744cb340fa7550f751142d63e867b039a068b25c23df8c6bf3ed26b7439f5030f27f3fb034fd3dbdd4d8100d4dc8bb8d7609018d993a61c8c368f82cb419cf77ed37e64636e6eadc866b4c8dba96f3a68dd45fc2de76608a19651c470f5d1c77a9581c5ff68acdf96ec41a83e8f53ed58542aa11ae165b95927aa59085b432a8ed310306a2aaba1e003b303c2ecb2b8ca3d8462fa07e690b124e3118e63f3f188c6cbb12b95a23ee03f680a1b1034d704d36e0c72e98d083d4f5c46f22226a78034664c57fe1632d53362e62e2ded1b00e1f18176e08a0934d7a1fee38fcb6fc8e2500cdfd60a5e845e68bd91b73ec2fbd6e9372733e99779a6657e3468db4fd1000fc14c09b3bbe85828c5e34519231f5d9570d9c853825cfb9595806fbf8e8fea108033b9cbbcbb78fd1e8246723c34987d870e7f2ca6633bc42e07d43dfd07a2bd0e5d30abe5044a4d25fa05b473932c2edc0d5f96ffb2c925060fa95773caf600721407b21e445342bf07e5a8f2aa4f34df6eb0ccea83d8cd3c925b9cc4ce143d2a06ec266f77e9af83fe323fa21f8c90128ad57db0ce05f1482b176c506f52e0d3c67229609b40a350e1484c3025989343f8e1278d72056bad106bfc25682a9e26a0f4d6f48590bce6d9ae2f60c80f9b8a74d4c30e293de64f514ef3e92291fd35d3e60cef173d9807958b7af3264419ab6ec09e80d1ea8be13a11f6f69cd527c793e6d2ce8a2c25dbcd2b7d83c0210b08dfd7d6d0fc787c356c2a15c4822a9b52e63e2343551dd1d639400650a6e0a3e180a61113ed73513ebb307f972c5fbcc5cbd54853763d216d150f9142339223321830b05999c54e48e9c814c37a273331bf144d6398a4aaf56ae68bae89753374cd4c41e2efa41c111382f1bcaa86dfa0186d89ba38f620a467244890a9821b2133337adf038e08342400014d071762feac6dea073bd1346f9619b29d797d8088430b24e6be9b71bf3edf8c7cee888cd8826eb7859252ae338a6b84fae4dbeace5c1a36b19a92a638a85e64f57a02c2fa746a426427ad4e6f0e1e011a092184af3b6c30c24a106b4cd0c8b7738b81ec0a799c72b7202ee950c563b880fc8bac35229745683f9ed4014f184c70155e12c0bad6d2b1cb18830fa7f5e6eeaf461932a6c01a00a2a6dd52d9fa4ceb86689c7037cb566b060c1f8adc13f7f69a4197f0c0f384398b1dacc05afbf8192b99e2c2c11e2fa8613cd1380a81a727ef5a1f67af209d01f4c766c8d5e64e605bca85b5ed0164b009578d1c85d3afd46cd574e423cd84f4688da8642d8a3d89a69a69c61a365f30ae4a501eb7f07c70a27017f945baea07bac1c02d74eea5c4955d5c1e55b6bb1c63b2204938729ad61a12f278937de63846a15c58068bd09e616d689fa8acdc4a75cfdbf120324e0461784009b6ba78fb268e020042ce31fe179f31c71984065a7091f713eb5d69ab4eb8cfa0ddbaef1a1b7f0a10f3c837897d0c71d983f1b782c578ca16205aab3996abacef9b0412aee4d6359371045c3d66359ad310f8de7c40d9531aa633e18f78e2f1d8107fe3a0da6232f35f0c3a86e1f53c7d96a55207f1f6c24e39109ff818b0854ff5b15040c4bcbfdf417c78fd47987bac82e648c0cb935bc4e3eb47570bb1e1b7dfaff1471cde0496dfe0e9d09e537bdac3ebe115c1c9006ebaaa7d59cdb1110d822978aa59826eee128f50b54d8bf1fc6dc613fcfaaf7a1b0dd8e3e3f0b2ce6e7c33ed9a20eab25d029081d7f4f6b1df2d0a7c1fcf01965eb8bda852c10f4aa660f048393fe29dad8b879842fd5edf101dfce5a296938a5fcfdaef7a0a378f8d5d32bf11777e4572ec7afacd47cbafd3551c5b15bf78ef31fa8a4869835fbe0d7b74bb68fbb44b1fe37d1449ed93f0bddc4922c435a3917e149a145b2c40f73066da5d5cc20ad7c23aeac8cb2c3e0398f036794c1fd2f717b642fe52156d57529fcd59db5cca79d27111d5bc31806cbb86362717c4156bbbaf74e9338b8595e3f7adec1a4507014c49f6ef27036a80970206644db95da4168812dc45daaae9105c8fc3f316ecac04aef0f0b81a2c86ff231c52565094a5e15a7213cf23311c52810d71b624faf394318b7fd8aa9cd097dbe4a0d1168b8a5e152860a64fe8929c67cf9e46d3186959ee34e132ca617810be2b5e694f774d6f2d5ceaaea9066330f88d1bb80cc262863b926ebff3ec6884324f8974f55cf85beaf9e0fa590a1617c84750b08ceeb85afcced2fc72ecdd96abc51ac19275f264b43682d13e662f140cba6b183533abbb0ace03ff285fad913ea5603c7e7785478e2de4db2a1039e282644328930261f4d45c94827fedf20c5856029629f0d90117e67501184b56813b8c571936aaa5706a48505c9dd1cd905452702296db73b5c20b7feeb0ac30d5e15d11ab4e560ee006f4665f4d3fec1df51aca11370134e022be7596a388768acd554f103b9248fdb8aebe464bf21138b54d5b1630cc668246e04fde13266ae03e8535b283eef0cb178ee6442ae42f390d2e45bda23f95dc3563b6eb5d2d856a249bc9a653fec60e51335ff75465ae47985dc94b8a00ad24faf5b4fccb6adcf070f31ba5410aa54d14d7340c9ba2954b4e4a4ce28a2cf6b16387dc489026b44672380d52f6b93915baf19ad17b28346a98c815d854bde4a662810f4f6843a6e215e3452314533e456d3395d6a2847716e806f7c29bb9fb9e0830cd9b26bf0395cfb45d5abc964f02824543f3ac3ed198cebfcd66845f70b2ecdc5acef7160e90932a6897ba7f4e3311c55fc25939777580f27a074054f892cb7d70aa3699604cd17e797bac4dc6e40d839e83e74d443d7b154b81f9f15a8d9c50f2a9b595d4b954949b3ad8fc4fe908c58d8e49c83309b31ad4c8d2a09dbca23758a1fd8517a899b302b06ebb29af290b9f45418a94f39bc64d9aeb659c6f776d2e006507c20030761da1ec1056ca720c5e7095be0589cb3e91f449a16f64b82af2926ec3534fe7e02ca454447cfe6d07983097d53cb9135b998084be280cb16e11f14ee097cbddc544a5d0400352c2a7b9c99be875f825227c0289a74abccff8bc4c8dc0b2598ca1e954543e0c6b051d92249e9b1ce23da2dc933750c6a3bae50ea272c6c7a533f16aa7193b477a32c14ed4e3340b90dca06350f12642f9ef212e269af5dcbf876cfc7841651c28627a98307b222f109fea54c02621b413703820965990aa6dc30babd855d62f36104154d97df560d5869e54aea142beba8f407ab92a48a9f46fc2d3e431f0d93392fefd36a2168b7a8a1e7873d40c0139ae409f269ef7179ad50ba5a0066273ab9f494ae418dbd0dc7c71d70d07250d84184a4a053bf43e06b218106056996f617b4c7dbec1b06837515bd50a614e6a1186a15473ff849bcd9a52644af84ce68f690e8663022a4ac7083f7834f5e4824257d41b3505b93f5b5fedcfb7eb4ef71a8407022fd9d83e80e11eac1f36b74248401064a41e2dc4bf464c019eda3acd340199c20c21f100c64cb32c1024188f76a9d50c66dab0bd7596ba1b9bcdf88a24cd07aa06344008f419fcfee3836950a0e42cde4d281bfe21c6703dfd677c7129cc593515e7b6d47768e4c1fc17f772ef7fc01f7795ae2989cf343f512fb5cc68e6832c3b5e0146a1f47c16e1e1d026de28f3cfd0e55691d801a6d2fd9c3928cea56adda0014a2103a16b4506de81eff5cb99882828a715f0fb8a7cc6819f7476104e3f05d0891a1ecf5b30320cad0f89e0dc373cc7d840cf4e1748250f631c146f1ef9eb6af840fdb91124957549b2990058557a2579873df9557c384d957b4d04a225f1c414c9a0206c8283758a9befd5ef7897adc2fa52c32a11e05f38876060c13496df75fe4c1afa49d7890cd0baf0ea8746d9d8090d16faa65e1a922efe81a5375a22ee44bcc2ef20ab89dbaf6849b48c295602adf90bc0a8238ba1e62b9c72a22594c928542a601073e1e02193326495475e93c944e13c312fb64cbe43830fe4cc6156384ed1a9e43430c3ab9d78451489d3ab533dd7b7015cb35739b86ec3b235057285d03daf0e1948583745b863b2e78f51755f1bc59c5cfd59a9a89ccdb2595b29fd30de4b8bcab350b401cacec38ae0d1389401f02bd07711509b3227c7257aa8dc62d736fe25ddf6de34d61b30a9c698c0c80c31f2208dc7bc640021160a6cddf60ea9dae52a1139fdb5638ba22b4e0c7270928713abcc2efae97aae5fa1befb5ec0be1463d639c902b7c1d9de93816e6f6c90722185e965d8a2b1ea97c084f08e15cbf3b1ad88d8aba5a865dbce42201e0e0679528610c6462635ee153c34e1af8c277a75617fdc0d6c7b6139ace69dc6e082b2062363400f3d15b1dbc7b413dfe3b09102dee17e28d58841b9d84ff993ff35569ce518da5ea68c9973eb5afd7ab37baa8e94d77378a0f360bd0491f7fc3289a76ffd6e48aa109e80f5755dcabc42ab00a29f135894f3c98d832f99c6aba833f11610c5556b4b5dccc7b7ddda1e6ceabf86316175ba17a2657be27fc7384f7594202e084f055554544c7b183423bcc67abf0667888a6f4bfc16328e43094b52ee80709a461241b89c80a42f72c3dcd1a7d6332e42554574e610194b625efc18735ef900d2038554ef99b55d635f2675691cb1c33c37e3ad7065120631f28fc0c024823f5625a70fa507992dd93590df311c8c7302b388976e05650b589a77675ea569e5c6178731c508d0e3501dfa4a01b98c4e2474edb35677c22473a06f482b64258098ce1823985d55b6a1f673f58978a7df5b00a8e860139c46c4265d1d419f80541bc4c07ae44c8af3d36a0a6f4c66f57d06442c462de80a34a27289b8550dc9de93c15170087269a5d701124f53a593e07e1c109458d006c979ac1329e8a6b67a3c6482fee0558152f80318d39316396943fa04b39d05f5a4545c96ed7f6c24232956a3edb184d303b0233160c5124a9b7de0ba6197b88d7c559783b2d454ca15715492e5a03c5b70749a38ca04aa0f93178cd16db4a86ca40ecd70161d8ac62bb7a3b6b223e4e0dec3d6277a12501bd3934fb955b95144ac4e2a1216a8bf5429a678cd0030faa729aab96e925ac0723fd6280146ca994f02b396a484f0f7da6351092356c485081304d985285cfc947df06860da294b378c22ec7d434da231656f1d8e11517bd4faa7338e0b0ec295097525f8dc8033dd87ce125461cd6c3023aac3636038b2b5ce827e529bf6eb1299efdf851b49bc21f08dff7b8043a0d5c0710c6c2088a0354116200d64e820a0d97e7060ed04a46198f972617ba05325e38e655c1d085044dc439a570d1a6c290cbc8eb06901d4aa79e9b56b5198e4d4cc705c837d0ccb07d98c40bc1c56bdfe43a08e2c6b54a975d3c4cbf79c1c7863d9e2c9d9e42cfc8ab7386150128b129870d4a35a4b9c953d3383abb399b958044c6c6f5d5cc9e2f3c2e11fda034fab14757a38d545c253e1efbade8d4c2760c503c4c0c5f87f359109de20e1e4b5f1dfe3e6de9d069239f920d7b370c5c2460763ffb96b49c592b6be23a86084841ca81e34f68888b484c2dcf36b0c3df6e809e10a5bffbe4a5d74a6ff0cae6bdb9a1a2075d526da2ac3ad9097b31aa2e85ffce870331fbb3a1bd01e3a36923545c27ae1ead8fc73d3451278b2542f725e5a51ae4aa87b002c969f186084b9a50cc4f40cacb72b6ce122b5699957ad128fa91372734340ac72511aecb13f61da00ed925dfce41366ac2545d6130b33a0278fee2ea0a7c32af5d0b0c71c48400b34a91546235121217037c32eb020236346878306bd1e76d487ba485960649fb30fa33230bb656f12f7205a82e84ea29bc373295a35c54770bb7012666bce559981a7e0d555dee2a07c5a2f60b8d9ba2ebf83d14e867d89d6a9ac27e0c83f33fab70e7c6628e5d8d6a30881917f643e58903263e37283d30f984548366967fb911a82d41ba1868b61ce42da0b572206e89f35628802abc2e4bea45bda1fcca63a29b227a0f46e3f53211f98f3e1c1f441d0486d5f87b248984b71117b6c9eaaaf415cd1ce185def9b186f2685a4cdc71ab7c55129a9206edb5686033f9222893efd72e4ad683cca4ae5587e1ce97facfbcf8c8e4465f117b4f4a85b4e49aa8d8262d2cfeb9a8d50541dfcfd15c3f391c4461ed2904795788abef4b1acbce69183e4127bf8f2724eb34076007bc825ea9d85e6245e5fb173e5ac02696160bc5413e3753ddcd582be962137f2fc47873e01623a5337602857a1e3268897f2a6565eea0fd5668a7de19e6eac167c4f0c1ba9d8e46636d2aad92076fad602a24093a7e76c339036058142737214ac51d0d2aad986a66b7a5634e6a566eb94b5c918eb8bd61563569b883044e1474cbcb6d7fc7e6ce5524a9cbdc998b5e1b377ee616df9017c71b1aba349bca8a4661f9bed53952150018b0da1beab3832ac4d2016a34076e2e5bb7fdb6d9888a0f6d86a89442d604b5dee5492001e5a9a7772f3333e1591e180ac1fa9e7db83801c0e137469e8dc1ae3f3d904f97e387f68507eaf57a1e8a5865daa9515a7158861d7bf97cbe4767f14261f3dace443ec2890c8bd38669f33ee152a79d250ca87d14c2c647accacd88cc59d32188721f73485cf0a14f5e780cbdcbc6ea66c887fa8b98b37461968b4420dc8ac14cc768a01c4caa47bc874394331532226c52fd3b9a423f72c216802e5a55fb017138057182df99f812166bf17e4e35db549d4bd600b95fcbaca21446558837bd3cf191573e431e74b7839bc381ad7f2a48401f6a3fb2fa7861819740122732ca7231471ad205b62ad68089687bf42d9358db211b286589230285bda236244307de6b8429cd38e22fe2849b2eb157ae4e80aa34375ade64525a2efa2928a5e0890980a7d968d0e0f1c338058ffe312c4ed081140456ce3475c3389a02fb1852126f61415e01358c7331bbb067e4f643007df24a40a7d76aa4075e25d59ee8181fe79b5e9eca3b98d5330106ec63efafe35378c7cef87a5e64237a9586e9c649d820756bb388516de10749f2102dba6d3264fda34c68be53a83b802b6477b30c041d45e6fea1767b2867e081520bf2762125e394e232ca002af9c9ceb0683c61ae4a826df08b5379ae00c06d08992bd0b38ca495c47c223fd4a26bc00de39031c12128c24fac105c83ea3c93347e0af0efc05dce46f6cf092eb30c77697121f71ab7758488cdb3898a661ad3cb6e7d6357351b2952a9ef96e331422a5b73470c7f6e5fda1370d315fcce683ce80e68dcc87bca0028ea21f3fef946c2d61996deb1afe9c3a58894fe0c0e6f241bf468514f21727992f1e523c3cbaf2101018c8635e1f776204738e70c9c8abbfb3354c36c3c614db5eac0530dbfb7bd694579929597e44bc84acd617e892036c9e36d0609c1ad50bec83de004517a53ee2c0596dfaa0582d660f9f3f7f191167bda0328a10fcabd9fd5c5601c693f20587983fb570f5a68dd67f0ff7cc9e97c1c2767701a1c3191c88e24aeaab9bdc59498f0f65afabb46e10b39f5aa5e81cf70a4d09de984f2bf9d14119d4adb205fa3897d8c9e870159c49a33c6511ba633348e0d93e17c1a88a3f6259568f2b545b94c45add440e6839f0a29872192937c50f2c1267d965c932def2f99a42878f1d74922f2d8659f3dde4bd40f2949006d402f366924f187c1b82f1c64031e753fccfe1f58d01ad244b6c377eb116e14257cd88541e204edd5bff6f6b36f39bad969c8eb789267c13971c26562f20ae1e69021fe4accf5004e12ac9c89e033cbff3807bfaca5e62ead71c00449bdb322d94ec3becc02f887b4142c8bc5ff0d70911466ad3fd0918548c6ac6c021238c2227776b8ab3b6cfbc13e16103713a6799b0c665734c51ee2dd4c112b16bab7ef77181117d5c06208fcdf9a1c516ebbe080c8e99361054dd24a03c14029ba09d2f57a889e94deec7ede478715cf84e81128adb840c43f20a9c2881e7b44b89f388a727c861da3a161f2b53ae6c240861f2d4db5689d17fa41662e457210427de6e1d02a1c6d2dbefd9a368d4049a019df4daab732e47850efc85f6e971b35209d48f8c843d88bae15bb0c24ff791a383277ba6f386e42fa01b6680138c3eeb1b0c628b904bb4769612d7c7b100a9ff1fe663ba2a4fe9e42d342628c971d8cb2af63c7c20b70b0c970392cb3349e588276ce52d785de7124f3871c7c703444206fc83ed910611f4a6626a634093c22f11838f25e87e7267e0e905571598300682433696cbcf1446b453eb8d5fae391cc2e3a24b37be035a9609815d80b04ae00040e00fd4b5bcfe63c1e63094115474d80d02977a36de78565aa2b7b3c39f83bbbb4866960d276f40df8671a03f09dde196d425b7f5ec5b9fd6ea3bf9e5754e19a4c3ea666a392dfb799319a59a84be306b7e38d3eb5896f193fec15ab6b9902b8b7d6f9287de8aa612f0f062f2cf1e338ce677b14cc8f64088608b04df666151bb17ef395f183ea437635de922492c78c7b2e51e93b5a36c6b51964fbaafc8e948af426461bf44f2530cd5799236a901e958052017d4fe370acb2ecbf13f9e62a16b9d6fb08701e78f0d119ed2919f52fe41348ef25c392dbd5b59fb950850e95b269aaf98c9b50a14b7d22a88b0c2ad8fa3fbfb28084fb2d0c3a5333399be4682ceb82449f1b725f109a1f31187a6346395b228c6652699b113972129c5b9348b96a21bd065448d123a5a7ffe4024caed0c74878b164c10968141ffb978caa1ce1521e9558c8866d2c5563a0582c08438fa94f1d1925c44842ebd7cba95305362634356053f55433226e0967ab2a8683973b94cf4f8d570d86d4a80f08a29b6233bb91e8e9d17710774e1550bba0e30b0e86ff428de7f07a1dd6fee4ff741f20916eca046ac767cde59ceb17833d919f2bae14789202a4a72b17b6407a88f919cb9e4349db80da083bd0b91d3e5b0026feed3176e6606af71139dfc2fb6bef0e8c043062b2c43dfc12d55d95b4b997891e10730eacbad463d3b3a53de0cee56dafdf38c9e63acb6874afca66b2f9ce857e667b55ac0f6628a8cbeddf9607bf1821eaedf7b48ce3e1d8ae8b0fdab7fcbb5970f8e78ba516d5ddb574903f54a694e96858c507c3a20c26140bd826cc06145def7e9fbbbaf96b41506a5b4a5dcc9c95151ebac1018138f020212769030c78c950408bef7be4be79307d9ba30bad7983fc331cd824f3ba41fbff8a2c5aac279f7467d2646a865324e9c6b31b1f5559c8e7581f8aa173fd7a490c7b0930094ddf7243c68bbe81d878cf91858be49b9ddb07a4792ce0b29f78f121c5d9914ce8c0af652f7bdc917055e3483aeb1382d5aa456eeb4b9543f3136781a3c0527625c7ade4cf034acd49b52671025889b6e45df77cb08f5d87132f74ecedc6a5b89d3f9e48857fee5c20a4fe1ae05ebf22f28fa196725e376d82156a712652324283298f4b4db5a69c577f6380ad1152a20e69110e55f3c14083260b274f907aacc5337af4877910a67adc838d8b97b81309ac1c8c6a6cf0ebd1b55bdb4522089ed72d52042bd51494c94e90b48333f1c6516be07b2c58219c84de164fa40b5dde8482cf15216abce0995517e0c017d2adf0d8178d428456f773749b552c63b335332c23257532b4da802a3ce01c55ac720a737fe7fc52938a8e49603eaac8be17552108af1197ab89b983b40c8a821ea4d38f2859cf1b9b636f8e6dfd998c4bf16b6eb3eb985a318093b9859abc8b589b930e89749f0242f191a819c7f8a3009fa5d70342501d4726b8020ac8fe59afcedd50f1a905c8348d3ed6f64f5d9573792968732ae566d4a1376cd5fefb413307115c07701363de570b5dcf585f915a223754b7de15ca113c98c9a223c1f182ff1f3fea1b84a8e887efd6707e487078c89cdcb9a6f0db65ed8b7a6e1da3397f84383d7975ac4c2f6a842470d1669a3c52b6c311453c4a4f6127b608edb584a3fcfd832882a8af0f7436e282599cd74f70d2e8a8d9662cad1df16fe3ced1c2955320ec41484bf7da216c53b929f968db7a6c1535b58ce69561453cdc50fbc13934b2a704f1c22c4bfb4fa7346776fa2fa227335611f3d417991d0233eb3aabb68d4e86727f7b1c17edaf1038ec88060c0a18604730d0a2d897ed51d93176678377f77ac04d4938a3fd979da44b85bdbd7d1627d20d1c40dad0f97d3f149cb61cb0434a817bdd967087bb4de844fefefd8f505fdfaa8450001c82443c9794388c4b4a1fce7373123d7543c9bbd977c6e4c9f7893921c49dac76d89f4755a3180017f3f823c511cfd9c55cd29315f218ec4d4dc7c2240b23347029fc69722b5166e620b0a77e175c21a008955684310197f3aa40d4b9b255cd12dcd6b0efa41cdc044f63a8233a8a23f53bc81c89cc1ee4a2ea420fcfa023a7ff87cfdb3b4e202ac350d3d128b41f9a166914b95bc1f478c6cd1fa53921f8ed1fd1079cace20f4dc59fd7977b7a9ce85c5c10a3a29688c39eb0deeb43fe57affcc133a72538968d3b249a8c52828ec54891c64cd05335c1a8ca76112455957b0417655525ec39145615016b73f02bb29e08c4e2e4c4f7a2fae4002d9f03355afaa748033c911793975c2688c6f3d94038ed5db3150bebcbf058aff4222e03b21aad5bb1e8072ecbe715130761747316c3f96c96760a3da457e049bed5c1d402f84fff87e38335985053fac75dae624ffe4a8da6ef9a9a99006cf6be80503e7b1aa2b3483ee708490b1a1b9f75c514d1dc8c844f685f1cd2cb4f0f719c5df87d0adb78a025d2d5d16628ce01d0ec24a95c202468b09dd973389f90ebb88e19cebe8b1dec8a1c9ef5f39fc1eac4fb69bd0debcefd884ad736f748f8b087991b45f026586adb98e739c1a5a740f75f35e397d9529e81aff7989630c87cbb223484c7a69df08b41a815e03a16b00741a014d83a0d3486834068d86a0b1017434001d8da0ab01e86c007aa181cfc48d545c1dc9deeb47eeb64c48827c24eef19a2e4f252098a4c2150882843190a340901efeed1df07d46ece913812b96033e43accf12f319627c86b88f11e723f4c967c4f988583f1113f443207b48ec81533f781c18eccf0ddd60796c92d01ae795ff3f079cfe0ee253e2f8409c8f11f333e27c86389f11ef63f1e14788f900b13e4adc077ce2033ea6afc013fd4f65d6f81734601fc7083a0ace80677d87d9dd712e675e8c4a83900cd69a96703ff3c1a7d377d867b45943b81e8bc86f45818bff5c74946781d7efe38670a892cf7c469c1f88e1136291845f20d8fafc73e27c402c1fc187d29f037e86980f1e31f04ddf6206562c23f03afa1a98a82f380482096fb458c718e2d9fe2b9ac0009833a0a61dabd7f61373f27b52097a1c924136730ac6afbf41bd528a106848eb6dd3ee282004e2d4bc2033d32cf0528db1ca91c7034280a35a675866b4cd36c2c5d7662d6950e57d7f5717e74796c6f0100790563758814505e6f49da3dbec26e33313130e987d56eeb605f7ec5c229bde5da7712c15254902e7940636a86254b733ea72b9de483fa423de4f01356140d2747e24c8d62d01aec7937b61e3309d0d1281121043a7f0b70c8428556ca2cecf0b9a66221f554da7878afc0de7d6f463b968e414a6ee1e608b62797e8eca728997cebd14166b3a93834397a3c013d22a58c6b9240804da69692eab9771ed9762b0afdc5058d6a417a32f180810337a5f5577f8d3330a82487042e722ff0f9049fc44ebc0f8f8466d9fbfdd2228fc34a8ae36109f0690fb4e9e0d9d61612a291d814da96c3e73a4086595c87e9b9db02cfe50eec48a6e716f3a768a93c4089285a81dc1c8dfb6323a0e9c15e1ce102c2532e1f300d0203211e1fa5b818351f1232129b2b96662f90b55b73d388dc8fdf9ddbfcdd245c365150fffe71c80018eae1443aa9deb300a18a263bc141a8d44044d3ea4d432662b676cd3866f4505e647bee1c2b13e99e911084b61527f4b299eb47ae438ba48f124abfce86bc5bf05d2c2ae8c46c0eb19bd5860042e35ab006399d5de4afdc456ee0ea31e2afbef9366fbe40ccf18c99b86e97714a5915f1c7fad70258819652fa88002543777080320201f845b4af6e16598f2153442c0ddcfe3e5caf9d0d4fe46242b15736db4d93b3473d50256a674407bfebb095cca21153a90262e7cafcb31b485bfb905b4baf85ba56af68526434395ab95d5ca255e7490bf0794891df97ec16b65363f12157bf93327cc96e4faa67bd4dd0268f476bbc1f52537608751b604f05b27013dd9423c2846a7f18a87a5332503fbc5cee2f7d1aaa3eefbc33ff09f5c03c744b6ce3ca89d069b3256e34e90da009b4b80e6692ca453a347ae8ceac9f4109435d84bbaa638030b9f52672f827e7f8f354738c256c9857325548fa6010f001c4360f92bfc0944fd7ce3956430ca7f8fc6906476e0b03382d50df95d514788f9786b35c1ef91574fc05d5923f8717e53b4916d264e35353da739fcbd2c733173232a904c7f35a5ccfe9a3a47c25b10eeb4b70eb43a373a6a4f151f7bf541d3ed2d345192e3deb9ae71f549740999b331b5ef9dd5de3104274e09aa3da078a5737dd47971ca85a0e204b61674feb8a0fbef68439db5d8391a022c6b29a2228577e598dfe47c78e5b87be57116fd43cd678123de94f31a76eb5e2e90e7e0e4d7813048d2de33e3188b14d49b110abd413720460f58acb29068c1b97541819e2462491fb0bdac2b4b9c7ccd0510bfa4ca56ddf7e4eedc016b40a0e6bda47ef15ec3c6ba97f5b9d3325767331033cba04e4fd81976dda034dcc65b7b6559d3b84c87578241a377eaa30675be3a9e7fd4ac14bab71445b09ade4545ac6d09f2251853809420f2f213e3431c30e3c53ee07659483a82f100d9394ada5725f3aef2971b43b1c4db576075c1498448bba65f544d52345f2ce6de58e5831d483be3bdba4a9721589fac4b548618529972232afd9eb1a0890ca9e34adfd414858e380713426a55cda58e7bdb408daf74d84255ba8edb373a372687a322b3578c6005effcee9d788259859e4f5426e8763c0bca56a3beb0f2e45206ac3f87f3ccacc16048fc284319fbb2994eb1fef8e42e7bec0a21d47724579ac46186a14d8c0fc1ca7f5b0e813e41f01ff74add22300827792e55d6115658a1b732936b50684f020ca64a47475717738b7b4c64877eef2e780be6f04030e88edc68c61293d814dc19b5fd1ac882025151e01fc15c38ecc0ac4016958c39cab4772766089151308fc9231fb8fcd05f6b8f2d831cc502f19f0d32ed9de1a0cb884e0a021445529deb97add21487bc3d25fe5af01cc741215e16e5cb557d717e27e21acd38e717e7433a4502207af456f4a3f9cdface82d41de38c59708193d5e70fe8610800b442792255dc99dd2085b545b1decb92a0866a3846bd559586efde3b2ebf5104967b8ba42ee8a92aa2c09d0fcb8015d619c05991d4b2ac7091ad461865471277eac7863006b35848b30fc77af1563460e4e56e5f0820e0ae576258b469e36901b6b28b83fbc1a28683c5b628b19c70b83cad8956b9898a1155c6c4e4ec35588e395a515dca903eb781cf4fed6600376de15c047154e6c57e172f21b02bf0da4bd07e01a450721036457e7f8a3b1fafe66b9edc2ed256d260102adf67a040a18989f37cb54b20f2733902157440da253cf0395daa3e6f9427f860771284aee48960099a407d42e92bd9133201d743e429d53afd6b10041a62989a84143ddb84d47243031ad6bd287318fcc213783526cde764df88074a650c5eeb3227d4987e186370890b800a3b5b70bf64ff2b8d8a7fb28d28ace628c6269dc1c10a84022e1aab225caf4f42e2ee1a5951b74bae42bda0b6abcc553d87f9681d9ffb8310230c3408734fbf2278a07049a3ea8e9eee32a5c0f081de390a82b32971bd22687d131bd85da1626b62e25699c28386cb8f52a56e2ca20ce53682a00183c3a34037900cc228e3462c8e3eec577e4dc07b870d8c05e9c9c0dc6119df3491e75c82324fff385473ae6f9081df1042dd01297b5a062b461d29a30bbafbca0aea2c4c888b3330b93114c79a3db8ce7cc0d82a3176641b4abfeb41b566c90c7fd474120bb0bf95171971e8bfb16d546a4acc5953c2585821bce615c5598f386af77fbd49ed8c2b722f2f5fcec73c6618dfc9e37da961a735d71170b77d473f58dd63f9f615e3567dd9ad4b46394a21ddd6cb99738df200352430e933e215a87beb72202f52adf2bb8d0b6ccbe6ecd22c63d27472e556b3943250b396c55cdd3d5da5c06b24af50a946a3b0a8020a37d1013c21ab8f2100bcd945b8746ffbdf764afd35454492adbe0ddd039ae45865002afd400fe72d38d6852ae02e9a7308c914922ad896ca5831e28f2193bacb057f6c16ce8c14603aa51bb3a116fe9b0c9f2f519ed7c28645b8f104c39d0cadcca92c716dc87bc829e8ccf70fffab520b78b06f98276d9b65348c08f05243ac6e5ac34b75c3df8b91e85193172390759f67fa6868bdcbdd91cb10eb2ccb50506b2b06589b4e43726a31721a7d5211eaad1c3913e178a05910ed957b344e87db37b55a20154618216afdc0c37daf6ecc7ec049ce731dc0cebee4eaa87481b0c642e8169c843f175a46dde79fca51915d50d306cd0279c58cd57d737fb12e95330e782cccf2a64b770a005c90b8d60c421fc8693eb4a07c844b9fd4bfb90f0940ede823c849890e6d64b3f73ef5414f84ab7d91c9d21f2695a0392c973d99d061eccabb3876d70ec2b4d916693729ae349071917ff7dd84d22c659bf9e0d8c5e4ad74694330bc38fbfa8e825c13b02c999ad27b43484ceedaf43f57b94240023db5d2889cd19cf4208a9dd41b5ca82e0e82bc8db07d5e9b2b8c71b7b8799d84dcefe4f8bb617ecb355618d2ff081027ef768d7a002555274981cca571d6c90f15b89508cc77b13b28a7cfa80089c2f160d916343159473c3a1f87e862e222e820479b20e7349e8041aeb631cbc9801904389a4b1645579cf6ddcb3e57899f1d6ac2fcaabdcf84d2a978797180fc524c16c999bd2526464c40940948348662cd47dfbf971a5241fcf3d2bafb388560d6ff0a3b24af5c0e5baa22dcc659dfcdaf2010b713ded2783746fa15db3891b160de36ccf75d0bc82e8fbe77135bd3d02fb2fb0a97f1c56e44dcbbf9a1954b8c28a7bf71483c1d8ca8b013baf0c061f0a7269764afc7d56a58c2e76dec2f7aa25d5db91759b0bd95c736737953f298dc3f859cb380208d507bca2cc5d2f07e68f3347f7a9c95d934a72a2eb05cf9792ac58d8a4a4029f310a96a054f3e4f059c9ecceea0777d0b806c297914b1b89295f60aeff1341ff810b009e2a323a168809c7a3c9d42bc4c1f9728a07fe86f1a1346c14a5d5f1a58335ff14c07a600b8af82c6c1fdea2dd718324b254b8d6d630d91234c5012e4571ff0088a7594d7a4e1d6f4ff6023d817110d46ceffa008716cb789fff4d349a38480368299b537fb0be4566f44fe88e111570723a2ed349a8fe7202d2d65c89384e19ad119ee1545275cc81f6c022bdd373198d3778a4be169390abf188ddcece243e317bf13c56f3a34f3322becc1f388522683d11848c88371bd3757d1f3633e5a6d5d525ea4d29ece3ad2a993595e5252770e545477a3dd7a1950321bfd940deeb9f0434833e89360560a08985a0645c1dc4ae495ae077f3e33fd1749ff0e3c4ed2ed811017938ebd3f991a1b7d64a580dd127705bee704f28d301db44b0202758d6d2af900b81895a23e0cad87dbda92082cf5d1a0dde34b571fcd3692bf48329bf9f70743a9811415cea894b929928650e687efcd10d4496472aeb09b4f183d3809a3f29551e4427c4b3da0b7890406213ec2eec08bb3b0a533bd9e12e96047c88ede40896c34b681d66d0cb2e1e85693f492140461b498e185d012b20d0c154c5533ba41ff2e847f0fb03e6c95a5b967f92be976d000020219c386141461afea243976e0a11e0ac557eb5a7389fe5d8543b0b6f03d1201297d9066a44403ac96d76faadf67a723f3a56278e0393ed81e74f25a2790109309e3bba3fceca3cd92564424752384ab71b1686e233388dc0b5e41a70603a43e99e18914d383353bc4de6a3f0af68f4a4b932100c95806e84822ba8d2218db90f871130a4b63e730d51999a7061678c9f02bd454c7640555be1528bc827b4986614c7de3a2e3c3a530c5a658db63248852f0fdce470c9cd455c13385e2cc5d3ae0b81a6d715c2eae89cc0a71fcda65939ca0dc2aa0543fb3e01a9df2521ec6934f33e8816eb3b384f1a89f8061529a487de551434582cba24daac4bdf5f324a3116a0544fda44ce4736c704983642065ce2e5cd10002c8b6939ecdf45de110f3b9b96b7ad8fe24735291ed042b4fe94477a3b91edf68bd490f6e6660d29f2888879bb5298cdaa84719593347e24b50dc2456c86e4c7e153944e70d4b6c5c9da2c69bda99e6f85bea42ee64a8d148a11427ba65ac01d88a5da618e5363bcf8dcdf92dfea2a7ff43a379032b7d8acc470eebb5618adb1f484c0fc0c1e530433e0e2750140119c910b0f6ffb029bd896e64ca2f2248a2f940d555ae169541266f60d75b13f874d4f2e13c7c085d85310b8a99ac048d76a1cb23aba4eeb9ba0857ae142b192884a8448a0761c1716a893de1285171ef9437b510b36fb72e96500103a19ed82c4b6801f0ea6bf2620cc917b1945791cce1e759bb551ff0db09fc16b7ee5aaa6c8c2f142fa67d4b239aabe5b29a0802210178267d1af69f3bb554cb50f59e4613e2c0b1b07f0c1f98d3987ade2c949d20ac8dcf9bb12a2409101c19eb826f95308c6f67fb2f9df3bead24e1daf97db51047987cf03ea907130d7806af171c1f718befca9aac89c194d461f7c71f155221cdeeefb4dccca86b5b668c11b318488b055948d8b339ffdf2afadafe31970f5011461a21e945d8f5cc1fee2a3c24b8da053c61df51152c65823c6040ca4312f084a16f5594cccc952343427885b924f496efc8be916e1cd86de2adbca94c2a4c9cd097fe8b9b28aee4193be84391cacfc94b84f9889f89c8ed790b80a787c24596174920285ab5a8623f44ed5444cfa87fa1cd6a077c53f1442b527975dfce4f933af673c867282ea82fc4d189dce0ef377688925678ad623828296136b319e2ccb6bf3406f524d009eb3ec74a40bba5fa91ea4f69a7ae2ce41c01e368810925f0eb3a1d25c71cc08a2bb31a8286c9749d483a936a5c31bd6c9119b70203b70b38fa82194db9d6a1a6b781a954a2929f82fa92706536b323b0c7cabe3e8540af97d9ca372f43b7b9b4ae58498ac5c595bdfbb9c53c90898503120ae65cf45f78df8b9ec3a0bde777bfebd6cbff0ca60d729d778d053caee68a1fd5a0ababd7141171fe66e6bd9f7085c111ef2906b48cd6fdea07795d7e15d536ff878146bf1291acce8b97020e9d26a8a97830269a41b7c29e7fa1bccd4d3ef48855f4e054a5154bcc2dbc3c5539228dd1fc06261332c27916bfdac6ca4a2bbec0e6ad5be0f538e63083113dad98cbba7c3492ed05d237ada0d8d31b98eb808044e8af81a19cfb3d2e86000a0548fd8b466ebc22d2a562e4c80b394e646e03663b958668ac308f84ff78912204cace66807f341d49498b9679d80c26b4aadc9bb10087f75a053c07d02581c7e34598b4cd410a32fbfa1c31cfbae6f8a13eaddcffebc9c1c46b0e2d937044b4ebe48b3c8ee6ca66121f48d738b8d0a0e34fd89e2b49ef3a5bf5982db05db458860d42f303e95ce929791414b4a3af1a45ffa73a4a1173e810b4e44320007837486961f0549be2472025c3a4bcbdc17288ce9e7b2cc877a28f817f75f5e98d46ec4cc48f033776abc5e3f311239b175d391f033177d2d3585987b393531ed4a191bea21a87011b104746be3136e3bd09e8220f86b73e385ca400e4b4e1dfd8e131ea53ccc170ca8a664c47248ea922af36b07605fc93bb89c1ad23dd889a6d4acf0a8bc7250f3169ca12fab45d3acc082061d9bf16721ae85830b3278c84e5a7bf441823c6bdd71580745210b42c0c2346b1e5785245fcc911af11eb990bf52159827f2a62f251824875ca2d5b4a56806a0615e0f87ea3f713db830d38bebe541e91c8c5c8b7669bdc9eba423bebf4db0995179e67f91b41bbeb848518d64324c7e8fe1ea566c2d7b16fa679736eaca19ed184d4fbf1f9fdff38e74fab13df8151ee49f0f83d24f55ffa7714e58a093c4867d4825022eedfa5107199ce4adc197fcb67f2d2203e11e3f39113c527c920c775e02e25644a668afdebd0b5f233620b60b6946bada6b8343dea04509952ce88c986c2af469c3465009278ac27474041c480fa23030e3132f9646d92d2a70a2a13aa3769b16a63a74c0e234dbb0b286e1230cd3b6a4c3ecd520b2cdd26094dd4d22e96430833839523ebee10778d99e6335c639b648bae7d58bbcea148781419504cb64691393502b1c6d828ac0c5124fe7ecf92ec09a5c99739cc02870bd06cb49a29f09269322639437bf6fef548766c0bf2ed934f01ed4f12192e42441a886892bcb1d1130481ed1b456ae0170271756a926c731c2b7d726e47e7418dae758cf4a5ca9224d8ddb4f4c2c4ae249de5a2501ea8f393f82c7859d22d83ae656f371322dfe85cf6ddc957b9e3606fdb1747a2adbafa585eb2b7078590944ff1b2fc8fcc4386470d6087f7927402a8dc2f59e9966152231fddb0b73904d1685639ca649fcbb783635c68ecb99e05ed3a7b2c81451f5888064b33205f35171efa3bc4648f06a1e6fd687da362780d9efef411dcc21453bb70538f8e094cafa728e17fa371f7fc0d631cd9052eb09f15a3e6fc618285119843a5d5e9d57dec79395555d6a7e561cb6398e6294c91a7e650a63a8050d5103a9ef3b8b680026997b6e50ffcb48ab40a43d5e5ceb3a0b5101326d83956e4dead124d779b174ba436b8c1f6bd57de678fc39ce0696af7404074214d903ce0de2c1cb264fcdfa5061f13de981bc0279eaa6cc51fb52f1f28e8e0954b05e4182606363afed3545f1ecb396413c979849fc979901df3c4c067a1588a6c88d5ac067b910ce5a3ae0350d98af3496cdd0864e5bf60cfc3e8d6aecedce5b6c6d5617c0e5ffe11e609bdb46159be747b692192bf7eda536b844a5d1d96c48e53334570e9160f63b1b3f250702df289612259d83f79f2d6721d985ec39253aa4bc5222c490e2b0c2c1c527cfeb61ca79c0bc34141c90138407879aebbd2e84479ecb6d5ac80a3aa3c6983316c3e05108ef7055491967ce2c63fd37a662a4c04482d86d984b4c81703727c1e52fc2acd0611c0a81b08a0bbc880185e7353204889a61023bb7b4246749d6dc750844a63662c521c98d0d436c6a16df1c9d9d734b5b9173e9cf86042b1823e9d6920a111e41c11bdddf144de2f34583e47ec445539ffa8de1271568552027eaa0ed7537398ce2fd4c39c3241b02ffc1c418bca72b865460ab6bfa5443b989a7e3f90589cc623da4be990790448cbdec212303c565b7897718258c95bd6f4e7386d01ab9b8a4c91e68b06f4d2c75f1ff2bcd32d830fdf7a3056f69f9238802e7688535f3b67bd178f2e75cdeb876525893bc10f6c10e407d953cbc630ec3bacf7bebea77f63dfeb8a8f746281512c65d369897bc06c43917053db03e8225c65a81ec68cb593b564b8c84178b10118d82a077c57cd730715372ef0241133ba8a9b4fd70cb673647406e473da74db7c3cc614cdd326b19e9c0b0effc115d1868fe97910add1d306e797f967ad5453dd8a8d9c352fdcbeef8f15af6c4eeee10cf89558ea955321e2b30a722c7e982d0d54e0ec844a625283f5ccc70eb4a11cfdbdc6095a62b6626b281e5bec9bfa75289324b17418ec12fb592a800e966a8e7798cb415590ceba0cce53192e55d2c333f62914573ae18c7527573a4a66aa599bada7d9c215e6755a5f2211114fc1ca047c0e3bf55cd96c772c570d098a2b12854d24f529f9d9d5b6a4afe50a4542310b415dc7995aaa68af6884b9c978083c67330a42005630b11e8391d73048864abb9903aa4014a7da4802419af8c4becfb92a45086ec452ef0eec3ac9ce3c2755ef5ce1b1ec5881fb2d72b4ab76ba1f10af65933b1aacf2370df7bb79fc918945ea60116932105fc90cafb80554fc9b0a23473ead989259f2a0577881d38d74dc967120b71411eb73664a134388c8548680f6a97890c28f7b30803684bab7caf40a7fc408b00424f28f59dd2640348ce3b8502831ad25d15d69b0a6e03db3b6269db2f9a06e6399da6221d93859ad8a1394fc3f1027214930da640f99051d173c44ef3cad0c8b8f1bfe40c4c744850f724c68fd1509d73943a64eb1d1a1676be0586bbe0b7e1c9c24f114e746b6c33d42e823100438626726060c468f283840baf6428544b4c7c0414dae6a30eb5b4b658b28609b5d04770d884ba2512718e909ccd96ef982bcd8bfdcb6fc7bee984b12a5538b480a9ea5ba425eaeb065af0e074bd9773ef8552aa0c64930fd306bc8cf5bbdb1ba2ad731a4efe541a31c918dd41517e0af40b1e279185c721c54aadfb7a30ae262eb45e3d445f5c88e4590f4d052c20b623fa177597479d32f20415970b3a6c7c1038369bdf005acfb8ed0f3fecb350f61c19b068857b2e415a82697fc1f4eab229b53b8bb0819238ce64b7d0785aa9d0d8d1034c716193f8a212430006900a576a3c98a69ce54f029a180f4f81b64de2ff32bef251406ba1197b222d35174d26a80df67208bfc41d5c02f607e430a7d7388b21272b556275c38a89035fe3952d7405f9e6ea0ea8fe80cd451e157dca6fbf46c32b6687b7a852138c37e12272113b359a75fecb722b2fbfbe1841b289e5633b0b9a7bece06a4c8a499d762a0a569f3662f871efc1f2b1071d2d095a89b9fbde3c2511797092bee3205137c7078340e543012ba636f5c33889a9a7cb04fa83f3c8cb0a4643c04d612f53f80024910410672c752748b602d6049a033204890696213131d776ebf56145314523a44989e8eea13a0670c5a19dcc60b404bad42d5ff68dcd6d62a114123f9e305b7722052a72a7c170a97ff8e578b5e545b3714fb410394d0c72a230655e723e0731a4c232a6234193ec249a2a544b962ff0798902bd897ce9ee7b75fc6ac4e0f451082ae94e512249d54d51d20a683b3b1c3c0c167ec633c4aad7af2a3e9e9ff32707a944e533de682c67b7e78f5da61e8beef922c9993bd70b53ba3c6dc8f924cd3fc08a32e93abf1fcd487ef6fe738c7bd78412cbbcf3c80f08114197203fe20b3ee059560d00e8110cf2b2419c7235d76d3f6110dfbd22514b3283777587d29778c3cf20411a13ebc1de9fda3903ecf7727fa7f1d96c983f4e54708b5928c02cb3be65a6a73a7d83ceffb7fbaf87308184157afc36f6c5006ccd96409a2a6065dcde878329ee70671a71068c634a14107d0322b4d51e06b8dcaa069ea205c89dbea1785e44ab095f1156b289628daa8cb4a181141e82defd5dc64c46f2d66c1749391d886060acc74af2cf4bce9026c8ccb09ecfa3d8f1a57fa14e80e47e53708f2afc15db7852cd86a80129aa59267dae64671840ae40d7e6fc36cb0f98486d2b361984b127c18820b886493ce7bd6baac62049d022055c512b592fe2c98ac6612983852f83a94f09853b317f31491abb86e4c43a6b8d53f06a8badf38385f90fe05b7c5d9fc31906b5d069e7959e8858ed7873e80da4bf8fc8197d432391f0ebb64b352eabbbb9ee25f7fc0f510bdf93de7ce5303db2db159ff3f5fbff56a302831a07f09762f5a7a1c02426956c3a0441a0cd3ad218ba67d23d52fd3b0d9c6f73ef0758a5b41c5bb3181da05aad4ef0b01181eca235a646a643a77c70c8dd452166b5532f98423b8c69eac6ad3b11235736694fc31a4c10a4ebdc520648218ca5595b33a26e03f8752d97e4dc4747039301c0c03809642e896ba75cff87fcc74922e230c24335d57a9369d98de549ad9ae123c33ad1ad818203c1064fe680b15735d48aeff28aa9644844b244b7fca7664b2e16c0c8881087cbe23ff833851d936ed1674c2481330353168b9cad2d880c9eeb925297e464b60e17582073e4cdf99b0fdc67443313175e5c468f69a9950c495e0b7cc54bd2af68d1c62fa7ea71df343ecfc576856c83c00d1431d81b2d91427b97ec8e97385c50fa74917835f6e78f9b87e1113693e4bba36b131974c24dd15255f02a7abecf7ed15c2837e72708dcfe6afc87f44382e1ddca39b8e238d59ec49575f4720f730b0ed491c84d0bcca6777acb9165d66e77106c144bc078b5feaed8bb880a836df213a9d28a4418fe027bd3ca52c1a7d4018a8742b32540975a3a19f7c66bcfa55e86d23d603e1d4b1d1c2dc7797907a9f125445bd9dc93cd92af4998522f5f09b7789fa340d13e7446faab2205e38967ac6337c4edc922f985f73427aae0fb178494aab1e9e5083c4efcdd77c44f9998a8c9325eda19910f3077d983eab157fd3a09296330a47ca7f798bb3d06547a27d5f0c9ef67c7856325b646e9a3f8a612e9aa1c8308d768199628d946e595e784ac5cba21b3bc91c3bdfce2794812c29e9fa6554639d29a95a8f4059040e42e4fbabc87a896e2e53aec3fda00589e218c47bc1ca1fa91e55aae18b929a350d42c972d4031214261b124c782ed030ce1f48403dddb1bffca02fdce28a954b8585c150412418120e07ffe330f9c7a0979d4adec76a200ee211c047960176580cf3940d90c5ff2d0c28c25fd220228cafaa352e545e2fc67a9a7035214429a94e8c61688d0059d99b820f40b42b045b08c4bc7b32938e43d703adb3852310af574d40975b0a5588a0cd221ea3fa99810b4cd8e4a9246c8179b9e5ab467507b2f81d06a16ad16cf4e30fac907901620fe010d82cec9f0f68b0515cbb6250299991eabc3271884a1160d12a1a920416531cff00382356fa1a8c065c11e61b311bcfc81c44d904cee6ba308c7ae2fde30ef4291a2b22e49ac3ed0c0e2d547077b07ad7eb6829611d81bf603928173fba6f8471088777cd4719cadb9aa55c223710e08c54255f6b4ee3bc700e29e80fd0f753895815243b6c3fb24424e8849ce28b198b0acaf8f67244cccac40b0ac6018b0803c20adafad630fff481433b245ec8e20a058a70f1b0344c91c34ec5b369b9c9b2eb1e710011f05d8b818c8a9190b5d95bd0612e5a6af1801e751ed60e8824a0ed0c8a89c611963ad2590bf098b7a38c4df5dba9d74c654dd366c86a31498ca002096755f3ad1d0cbcead5d175cfd8688a1d2444a1f7358222db4d1317f0e2af3238bbcac55457b7e2b8a4dfa290dbc6a6465b3cd927fe2d324b388db8a416f8655062774f6f782e84dcc34c019fe4b563856d584659edfb84e6e309313f2ff7650c45134d8ed41b47787d3ee0df5eb9d967a6d6de02cd7b53ca82273f130c65d0e8e166eb294d132debe8b5a294696c81bc613c3805dde7939c80a06a2a7a17d9ccefe8bd292d9cb4bdd864afae53af90f460e713d690634e7d49a4c618f0b31060619e6234fd98fea10c84a9947bd70d389156e448636ad2720068f990313a08592c1c3517cee4c9a17352436f7c7a7f8d8109f8a056e5863565fd6525df59ece5a49a9fd4111ed8bb3da3b4b290f98f1c310c2b0ce23f822055c01a0680e23dd8e0834ee0bdf2916add6fdc6f77527d00ff14f9862cdde800797f48e544e4ff3f8b05a004910a4b659d6e8505caf92069728f2eefb38ac7d8366a92cbde816fafce9bd1da5fe5923513f955483034772e1740fccc9a1e6adc7df621030f0e7bafb802c0c61236f5923f9db2aacbaf0bdf88965d6fea1a4b5baa603eb3493a55296836b4d6c712c0494b28cda70a4450cc77e2ec39b4157634e420071254dd5d0f9d9fa3d88cca102c367612132c61d64ecdb19051bc5ddb926e3740152175df906e4ecd725649580ab23b12b58e35a179f72a16a019b6e920273b2af8e4a1acbfa4a0df96abcb17894492d929a2cf5c6a58041acda41612e198582c0168a0171b4c3c6c7a000d56597bcd6e9e64468a5091f1e31b548c31ad84e0dd26fe32bce86f4e182c450929d4398a38d6d0c19005a580b60942629e1562d6570f8bb6c18a28d2ea64c1740727ee76391bf110cd95a706a3e02858cabd8176c0f87c1b6d7cadfefacb6e2c1cc35ff555a35bd2b52e4fdead7c960c7c02b34aab88e3cd2d06a5fcf2186be7a131374062e55fd779436d2003cc8c90190697389e7bb6435f9c16f4681d02c0661176d209d81b7879f3a1a917494d9292f729b2b743881f95256dc4e22374acc9226cd5b494dd512a1197afb9938f4e89547cbae048303902836ba6d2ed551663829011e94ad627498413718f2e18122f311a2a6ee2b2485637f8f1c46b771c1b8162003bb9f355da292fc69ec3802ba4b7201f82c87c045d9213f8300f8cce694cb99255b1d5e1ba9c2812c7d52464f4c7cd4b089ab99363746780cef8b31aed7c81237dca5a87791cfd0fc0b49a23e0ad90d35de7ebe73b35f2cf7b9c90740a6644b324fa6847bde5160184dd10f9a40c8e307374174e6cb9385a89d7504075942a50fa95ad47761b2dff6e1652a5ea9063c874a399b3280df686281e661ccbc70306485d2ebd0b8e8dc576e7d37c1a5254705445a602ac2e2515b069bcc220d110751ac0b77eff861a4d5303ca4cd7474e84ff0f5d0ec89b3cf389b7c7deefa81f8ea5678f5742ceb8db195f3df0a32f27b54a9d943f6d52962e2ab60ee79722a4c06be8a83ea3e80328c71f5f7f6b1e2809cd12cb8e902af42c3511b29966f086a3f1a2e7efabda1444e05fbc86efc77438b9b23a15d37f14b9ea590452532c822b16baee63d24529579997bc2b8f5aa7789d1d2b27678c174fa06d786523b6c10a6b120954c437c7d700e84b526d7c3546192023f4a120d54f490137f85d9720d1bf4c1182895422aae88c211bf9859bf8dacef21ddb0e62470de371fba1f1fc0b8d56219a1d4591c177221e96c13349f44ccf6ecd67049785d245cbaafb577000015ff2f87cffc76c58535aba99fd9f4a41422709217fd419d8c0b9d3e5db179cf191b056f218c52d816a66f0c60194165e3ff445812c626db809ab3aa75a9ae29a0e54b303b12796326001637fb40da964644d1538ee60047d9c627ac8335a2aa08a45075b493dac687b78b44acabd603120281ec40dc120dea9a162849a50336d8c1cefc582b12bc0a5a0c6cae349cd616c82de9bc3f33063cd5a351811dbadab485bb9e1480904ae69c30f547087d14d94c89a7415d8438b622243343c0f465f94ad8121c830b362db42c5dc10e48268276070856b6639e8410eb8e612216e5a589a8661f1a73e006ad232f44bbc2b97994a6de183d32b924f3a0a8803c52e68613189427a28a5b627a0087120c65972285ec9572b1aa19c002314a4c66d8b5084df04c510a70c84e5fb810b87deabecbb248057daff1b62d4288206cafb94815f4155feb53a329a564f6603a43565405b7b40f18f84c9f25827794034bebf1295416e4ba3b048509010a4458ebfea2bed861303bbdae08a0aac05635491294d1ca1064ac49b686fc8bfac8d72019717a8cca059fa2b99bb5f36266c5e2857ac11227a09ef0ee46e8aaceb6da3d43234c8a44b515969646209889f4063b7f18e4424c05a55230fb4a6a14e31acaf8314ca837f24d200c40cbfaa3f7fecf33d5ad5f8312960655839890eb1c94b337cf9481547aba48fbd54d3878e8a28f5f2e51b8ce9024a4ea2745a3098594d07d5dbe39e702190c4c26c372b3656a4389643524da2c38549a898296d9388c65478c4ba5211bd06e2e2f4eed5b91f03ffc3037107bbd19235e123075de3c42c1b987d21978ec89806330b19f7debb9bdc5bca94640a22074e0753075b357dffd56481de2cc96acfef60efdf7fa31380d599efcfa933f0a785eff5d899e95fd336fdd809deebe5a11968db26e04543e3f02a0b491a75d9d5f6f2eaedcd91ad2a5ef67215f8323acd4c0361ef0cdb390d2b0d6c2311d39c6cd9c7dfb26f861fb3d1fe2011d1fb62e3b43112d17b03bb1c5bcbbb60618a854796575696d84d125117bf45417d5c3e4276cbb8ec6b2b3bc65a76182969139be27f8d9579d207c3772a5afaf830c84ff0a114d86a418aa1c5fc62f8489fcc77f783f430d422eca49f301ff969a0cdfc2e0626e6617e832c7006c6441862f818301e005f0c1fdb7cbf6d90ab1fb769fbfa2f7cd7887de1b32ec70bdc73dcef60cb55ee8dd81de0f5c0ca72cd73377ffb8cd8f91191b6a662b046fdfa38fc850f4797a3561abe0fdaedbff95f43e3cc8f6dd4a627c28f1f1aa01712c1c6bfb075272402eb3054b529fef7b1ed81fe73ada14ddfc7f6ffffc3a14d3f3f7e8a6c23f3322fa3def962653e46a5b1320fa3e2f0d38c1d4dbd74a64aaebb41c62d77dbd04d6ca1f9e09fbf7d40b837eeb7ed77b0ddaaf62fb87af97b80767b7f234e7ffbe8f38547accc6bdf652a56a6b63c0e879d8e9677791c5efa60bedbf67211f7f20d892d3e97ef0681475aaa8d8843f5d2d53bffc506047a7935616b7382c21f2fcda7f137e0a71cb87ae7a435fed68252da148bda141fb6da14e3674fa017b66051cb71b8f6dd20fcb2f3990628d427de89fce57cf6418bc156fda278d36a947b2d49302c462c664792881993249e4d51072bb3d2671c1dc62a6c1cccb0e7b81bd3e4a7373727a011fb7eb4a969a974830310dc32cdf87183b5494e2b63136e4a349bf1356da24318a9c10f5348c103f4840455900421a1c2def435ff181260b84110868858474041d881476c86c490cd901882cd300cc3e6c43099fd906519123f588111e213acbc008b617965e5872b5861efdf482bdb4c3fb7ffc57e885fc4c84e7cf971200af1bfa4d6628c8fc4c84eac48b2c74817073b6b161ffb842c895588f848a48cf1257c2411497c2415897f9bb088648b1da17c68e107bb4a1f6874eaf78685dfdf451bd76c734eca35b409e6a043150bbd028dc3ac23a8f0918591e7882b9663c12089bdd007e79a7e340efcc14209212cb29586362de97a73a34df0dfc22e04412019c429058bbcbfbffff78e0c02edbf39b649fda42fb20df6f37b611bece3c7b10df6f2835d0f6a2f912233b2f7c7b2ba24c69af753ac38e8d0a6fe8e3558b244fe1bd9991912233b9f919d1907643fff4713ec8b643fe2cb5a24d619af933e0e39f8c97f764cd0999ee9fa6ec35f05c37ae98dcb2863e8dab8660637d30fed85d61d8b212cac97c6f2e598c8ee29dc12cb66f6527047894519bf6628fb0911f0004fa6d1ca26cf66c20ba5a1f925d8e3e81928ac01218533fc671fa97e97c82d62e333660379d2a7f7467ae4c1d61d146640483b14503f95206d42ff8a120b6be063bb5380bd91f83f3cd6224db8fe888fd522d917c1de082a7215242a48d6211591442c10fe1da46758e3c6e6b29fdf853afaf963102e476c1bf81b641940f71154b971c3e9df1b1b76c6fc309ac9af77e287110134dda621b3cbbd95b0cd307c1e330ef0c73e02f1b3ea81ece363d5034d7e641fe37b007baf33f279c79f77324cd24f56a184ed4a8325ad6e9641876ecfe241f164e639e79c3ee714387ba409b61df98163cda724f17fdb11f638e3667dab40f8374d725dd2bdece4126d5ad18515d88ce7175f7ef3b1ef723ee4cf0a84df7dcc8f5f3fb4fe5d2216fbee8b0fff3ae363cef870af3c2be708c2d9adcecc99f9d19a80be5d618185cdeafd4b99a3a4480a6a4131b71461f99d8943f195b73c8aa79c89071d597945a1e26a457955694519f218a5c4b00c4a0ccbb23949319b9344d2b48da469dbc6719dc66d1cd7759ec702e1d7f9587eafc763a145d023cbdf62c5f2cbf20b4969ad845c3da5572dd42a98c4f2f74f7b9092eee923cd13858aeb55a5156508160199c020155402fec022200ab215cbdfaf76f8c52b965d965f763b8a58585828a5b4a5a5a5c585737171e972c01a258665d99cd92491346ddbb48de3bacef33a8f8585d29616dae2e2d2a2c5cb8b0b980803e30d615abcb870010313135392893232de50a624f3e2c5cc0c0d0d0c8e1163c4f08631a607bf5b03bf7bc3f2cbe4c8bc78c1c16f6686831f4db703468401c31bc298991eb7a9c50b7d79797971e1c205e6e206869f0404bd37336068608efc00638221f5501aaca75423230365a417d3f588f198ad34334c964ab0c42ebc6bc442f8500190c26e8ede2d62e1df96564bcb4f2d2bb7e1e79696e587dc0f28f36f41656f4bca32d79920680b90e56f719db2d8206c399906e4bfdc7a1cdf8e29a0b4892d0c1538340ebfdb1f8dc3bdb4da7f34ce10681d729d5db8c1acd18725110a8c55fe4a254a33ce6ac464e46c0a0b9f616741849e20a48b230750401f877310e9e2e101841fbd089b77f37bfd9a36998848cbcf900b53e827fe0b80e502e858ae2fc37efc98064759e33733d91da1292f2bbfd4c1950e687c4d93d2389a7ceee573722fe65e24db6c7dd538d76bbd785f5b87d2382d4f8a37a571b649af67b3a11cc84f70e536f24b254a1d684e4a4ba5bf26e78c4dfdde3704daaed11b4328ac6cc1cd36c967f9b8cf6b6141b9efed5b3156a0a4c7ea902845005cf49a80792dec3f3e792db6918f61d8433e3d6f02f0cea000a4877df7c9cfdcc79620940b7b830e571c5c6d28eeb5a15e56625e147c9a2fbd25d8ac724bec254db591f6204e362816b2941b2ca7a09757f779bcc04143e3485ae3df603341d0f8dee5c022941e25da15bdd3f612eee3df1b0e876f9f0e2054dbb0f54f07d04f9b628e40ef8c39e2779ccf42708f64e3b6ecb91782f44826694eea2721b44742d248a4e784e81e89d671af6dcf55214a3b48b626f2b9df9e7bed4910646fcd4de3382b042bfb6dda47fade6d620b8d1f738d95e327638c314af9f1659432bacbce9ad8feb68535354c037adce1b56387cdde14080abb0a23dbcd4326c13ad829b041ae67586e0fc2ca02218b773bba1d4512b6c7494177dcb0ac83a760b03dd8e9701aa3e7549041939acbe2bad891112c2254967d6099e563c56eb0db8159f86d841d222d76856924416f8de5016d37760553422590c7038bac048a1c507b3c1c90c7633b09ea47d8116347b69fc57585ac016568daba1b3a73c70023b1bfe810966008411842108cc0719c467ac869a4d913721c9771199771d9b665d996c119596bfbde7e83829a2c66c2ba216b2d966562aaa0fe2c047a4d167b939fb47ae7c4e6d692b3969a36913ebe7ced8675b6c77edb3e68990036f46fb247bfec7af4c671bf7d1c84dcd603b45b9dd12658b5af86d8a68e7951894923402641b8013e1d60c52c24575e4558f72fe23fd62193089b073802eb300797e238e40f85df03b3ddf990b6bf27086d89a4a16d608f756e53f45181cc684f9adc15288f3eda809fe6fbdfe974d6acb253135c4523edd8141b75016dcb2c21bc4d26054e0c19b1b2fe9b94524a2d48ae208410bea83c722be25230d5d3d3b93c150bca3f3bb2ee1f3f6c0259f797d9508ff58f3bd8cb65fd3fdfb99ecafa7fbdf36226087a5950d63fe3e207778cacff465a125f56b9a22bedca86659334492ea86a541bf92afec49747896533bb935d50d528235fc51f2cc3ee64175435cac8579864175435cac8a38cec82aa467974764155170585eceaa280cc939979c269444444444444444444444444444444444444444444444444444444444444444444444444447a11c2c6351b6c8f12cb2649dbb8ce63a12d2e2d5e5cc0c494645eccd0c0e0dcfd35e3ffc25fc6bf14e3fe17c6bdba80114346c645f107c6c54b0b9716198f19f5c0d4aa85b278dd7fb2c7a8e3b67eedabb15d8dd2485d5fe050ae82aae9aa176a1ac5aefeacd3a1d968148d2c7b0c50ae5e9c8891d42233dda1c9dddddd994686c5c6ccccd03a53ebee31467777accb114b259e714ac66413ccce835396cd40943b0b1c5060a9332cf384b0032a508f098b0b7a9234415f2cb37a7a54945a660571644d41bcbe26081e7bff268a7e68b969637648e484a014e068bb0ab67b497c1c7814f19831ab5d439be2e3508f206b58d0ad71b040b20f72437a92a663d61b78a115c231ce7fa3a216894581964a34108265cea23b2aa20338d282d60f56d87e599a9487ba20458c8d13a38a17eb8712d866826d2bb6dfbb1ced34a051d88f88244860628920ec800a4888408b31462570421a62fce18824434481042bae50eddc0989b09776515bd14ba15716087c7801927ee6e4ba1cfe184f1350405a416be3b922059347f33141d64332a208cc3571e02379b21ea0e69cb3c482208c08219873ceb902200c4b421073be9458b0721bb6d8c7e9b213b533e79c33c9ca6dd84f74cec9f2e165270976e6bfcc2c0990db3010472c208e60540537f6658c58866145f0c9b2eca574c411471c7184ea8f589972603747322a30c166d9bf645996654a7eda86816650db604a4a9867850a56e0821547ba2ab4c5f2ca8a1594ac102ab7612b65cf14984bd3a4c4b0c88e39e6987be42c63c6b0ecf92332ed84ffbaa2bdf611e9008d449a8722ba0d488142172665f3ec999979869d91996b6040f96113cadce5b0768f1c21dc81023333ed1f220f2758f0840f3cb892821ff8605db801cc13147241eb42d0159e8be0718175055be1030eacb022e2c709a3215eaea8acbbbb37a1fd6ca3a541c4a7f101094b04adace0841421c01c09a5e1362c9ae8596f4663dde92b18d1c12a8a0d7ed00407200012c2c55e1bd6ddbd63776173779702922a5af04312214a10839e16f6dab03bdd4d5f009db3b8220826a8600434c0420a18040b0eac732403eb0c5b1b3fe43951145020fedc969b18420f2c843e58aefcf5102d7ffc7cb018b9f1677a683656212c969f91606fece52cecfd688ab60cfcb013f296f23cc142d2b2cc42b2b22dcbac2355e4d828f20168c4941b18ed64ff01d80323ac8067a70990ede7f7db609dd614e07dff0bed2f1189e3cbd1c1f65b87e3d91c3fc11d560852d67b2ffa173f88c14bb9e5a31f16bb1cdefbc73dfc583e7e371ac7fbfe9c8de5632122ad2744011be43ef825c8a1fd0e5246850d7fe31238fc8d42b63f26611d97ef772df069871bfe461f1af0370a61c3df48821afec69f23d66961043ec11e1f7f9d090cf8eb51e8f1d7abe0f1d7ad48611d9a049f606ac75f77a2e3afb372fc7520d0f0d75b29d669f97e27824fb068017ffd043b7fbd05f6afc700c75f6762fbdd87753c2af8d43c0af8db5548c0dfd60202feba0f37fe7a8f11ebb034c1a70e9ae16f43a1f3b78d7080bfcd849cbf4d050aeb70dfdf2fe053b74e7f7b06387f3b0919feb6131b7f7bc53a1d17f8d44706f8db480af0b78920c0df3ec1cddf4e62fbe1159804f6aff1173a61007f211534663c2c629dcdf4174ab9108a0b9b10c05ff8c4f6c325f8e4436ee3357f610a86bf700631fc852fb85005b61f92804ffe721b255807c53aa417fe421fdb100973e1f215b6ff027c8a3f6ee3347fd9889d609b9360bb079fe22a08ebf0e0cb501020836d1af6e5efbb68b13130f0291ad5b04e7c2d2847e7f889b4721bff09817a277d407540c52167de50ef6c40bdd4865a43bd322ce9c747bd9301f5d21e3da41febcfa35e19f3b5a3dea9a35e9a23e54f43ca4f736801f5ce9d7aa9854543d61f072cb23e811450ef4c40bd14013c377882fc941dcd50efd4a9971ea0ded291f5cf096af9296b9dea9d38f552195a365afe06a87716a05e4a803eca82acff4d1f59cf6ad43b07502fa5e14033ea95e1ef437ec28afce502a87706a05e6a536fa9c8fa9b862600ea9d34867a290cb5c65f3f7ec27ae2ca4fd2f542bd324ab5de12fdeaa5feff3363d44b61c495cbfad3c495f58f467e922ad782fc2467ea95517a516f89cad44b55d6bf148d664cbd93c2d44b5dd42b51d6ffa55e1976735935a1a83514815a00e04bf4e5a55ebf62615b97afed46493f3a48059180e08b3402520f89e7c8c5956869bb794353b88a5a7002f540950aa25d85a5edc61dcd144cd4ec993c435dcb0a235791e7c1b69b16f4c44a956a9e2de3c98cc87e3214c77d6db5a22cbc8c5c2a1296c288800d612b4ca569b5edfcda6e18d6832dc180b01f0cf5ca5c591cbd8ce6ec2daa90d01a5a99eca705310dd766a00dd98015e404484bc2fa5ff963fd650f683ff68cc9afb5a01b41274e069e81b4823526842fa6881209dab15e68bbbb2176c37a495a0dacbfe401ed5f597f2dc859ca4a536929eb270b7dda609d4600a90928eab5617166cc15343a004e20074cec8089274f9e3c79f2e4c993274f9e3c618209269810121212121212121212121262820926988022c552dbdf1ccdade93a245ad701e536dde0d0ee3e9bc5f64b69c835a61931d8f8378b31ce50036bc4bf72623ffd947d0df611c36a4870c4620feb84106625a0f03d320e12f4427b67dcbe60bdde20b0c75621d7880e68af09fab4a947e680facbc71ec6e85df76f5997a3654b29b12b34e52d9c530262a028f385d2946aee0c95957ec3f286721b692a95e6f41ce84b1e25964d92b6719dc7425b5ce2ca6e2f2e60624a322f666860c490f15fe5b87fbbd57853acfc9a1aaf76395e1b4e73af97c3b0c1c0619fdb0d3527a591087ab9d78bb3d2a7ece56f00e0b02fda968f7edc0b68a2b8d70bae88a0577b69afc67179f99a953ecd972fd67117b882e2799e27f39ee779def79ee7795ecc7b9ee77934ef799ee771ef799ee7bd78cff33ccff33ccf2bbde7799e17dff33ccf7b58e4799ee7799ef7500917ef799ee7799ee7bdb4780f7bcff33ccff36a1a674847dff35abc7fe97278de4c0c99970143c68593ef65c4803123e3b57012332313e3b938a1999189f15a9c7032311e479dbc9089f1381627311eb7794e4a311eb7754ea2c76d917302e3715bdc9c406e8b5073e282db222439e1b6f8d282c46d71c356d3a82d665b6c1dcda6b4496eddc282692c1da5477a2cb7719be68f95dd47a03e79ab15d44ac15ce993bffc158f441f7fc5228e1a2746519f220ae54219a1a434cecbfbbb127d8ad185a243891ec5fa7baa7164dedf517d8ad17fa203450fb2feeed3382eefdf55fa14a715da57ec11eb31437d922854110a4a2be9939c4cfc1ba8715cbc7ff3f449ce1e7f78a571e09fe4a442ab586f71c2e60a9b49ac122638d8bbf6a59e13d6721bf9252a744a0d7dc25e411a67087d21300bcbf243485fa239a89b120d7db16eea5386aaf1bee594a5dc46fe1021300bff18ba1cbeb2fef213476f496aaf17ea857aa1527ed25aad542bd54ab5fca4a552ad542bd5b2f2e3cb4f1a2abee22bbe507e22bd5ea817ea854af989d46aa55aa956ca4a522ad54ab5522d2b4973ced70bf542bd50293fcd562bd54ab5522d3fcd54aa956aa55a56ce392945c9cf507ecae6cc50f60593a5fc94b5dcc64fa512a559cad278eb85b7609c2506f78231536fa954aa975618bc975709e6f5f2aae9645a2598d64beb05ce454bc1bc6829172da5a5acfcbac5682898170de5a2a1349495ff69dbebc5e5b5bdfc492e5a2f2eadade5322624a55c3652ca492918236b4142b96c249493509084b2f26160d8ebf5a2912de86ad596953fa3a2d15e5fcdd436533e53d84c5196192b57acb4d226d486f289c2268a145f78466d92912dcbd7367bcdacc593796993f49337a56de44b8f8a9559cb4fda6b52d75edbe74d11f2011edb154279b94a25aa057ee2b68904e0846cf60420005462e3a4a0b0089b31571eb48f07a6917dfc88648069642f846b64b0c4ddd00cf5d0cd50e9f2ab147788e97a78b580dc7272fec6bf832ed5eb9073802e79f6eeee2ed3dd20ddbd87485ab73a98ae07567b809654a75689b8626d70247fed73db829385454a297376689c206dca7020900e39aca33dfc1bf7e6e5afc9c52dc5fca5b6f4f00fc033b09dc1c22f7dae6a9b0ce6e3feb86dda26da17ba1b60cd91f286caeb7e73d58a85e5b70ad3f5e86a0fd07af57abfbdfc5277837c96da03b4f321d017a487da943df701f13b4d620fe5d8ec63a040fb67bdfd5b9595d380e6ec21daed85b05816ad0454d372a8b8f637fad4436e93cd37ddbca9c4edd26667af6956d3349677d1dda06937b4bf462cb709de1b0b79b04e37eb74fba458a79b6b5cba1cbefd4b778356ebedb7409b32ffabbdff85463bb429fb96ae8756afd7fdf69b0cdc8d9f5ce536d99bfe55b208f442206c08116a5dbee5b76f8341ac036df69e4b77c3f6edb5743eb6efdbe27970a7b4d4e2ab800526bb7c100a4c23fb960f3a816b64dce77295ab5ca552a9fa65dd81d61e5df759a00212079407a6015daca3695fdbfe6ef7d8eca11358876d9641239b3da4d238f1338971d3e85da522dae2c16db21ee2e60ba529d5bc29e60934fe7c5eb9ca855a147bd84018621dedb37f199996961edaea4c0fd16a2f8465ded926d734ae0768b77afb6157205db540059a5d3b54c002aba156914b0445b1ebe17dd71ea0d5de3b1dda93bebb2022d07afd791a0f1dd320fdf65d196e6897b56033226dcafee6581b2f9007a2b2dfb2cede851aeb7cfc0e20b44dd9cf6e475720a40a8940bb55a804c866da670f9504350ed42e7462332578f07a6dd8fe1b130a8d526229cfaffece3a35c3005cfc5c055d2d43f6f32302ad8fb7f2e36fdf8d9f3a4adb649f5929aa62b31b2a43536cd6526cf63156a017be5687c48e92695d0f58699bb2efcab5299be9f1397bcca48c1c80c20925a0405902a5e646948d8f3deca95284f59748d03bc3c6df48a4165297839fb356d226b690f909ea538a47e8152bf1a7601d17448167e824b0467ce9270f9a458ac4aab911eb604e858dcf7536c0c77ee3baae077ccc3187f0b7c7dcdd1dc3ea8c36396cb98b6de21b915e5706487f03d50e62eeae1107f47acb5b2ea54f5ce3dcb8b241a9c6e1db29a32a8da359619d0dbebabf7d85f5befb84b068dff56a57fdc706f876835ad7037ebf0379120ffa9c49db04a954b4f42677f915b7e25590a0aacdb5847b1cedae762f95fcdf5d44a0751f6f5def89eeea699ceb3d4ec5c768d252f41eeff17154e338b7aa5936b69091771b359596d2516cfc86e25578c01e300c89b6bf5ba84db1a7d8d82aa611e31214883f11180464a3b7dc267a50e330691c255827c8c6eb4b7cdb6b9f1016fb5abdd07fecd6d9d00f67bfffb4293efc240ee4037a3b15df7f3c49e3006d900aa7d2420d5b055b055be5541ac77dbee6ba8bdb22d74ae2fc211b6f54a271222c028abb3ea7c234223caa6293c0193c0a5c237e7c9f9f780475c5c6e856e2144c23fe6d17d8f85b8c15f47aab58c709ebc08f10629d0df0dbba575884923691ec90087f20cae2e8e8d33833de5d7e6a95e9bf54a2d45d367aab6cfc56b510ebb043efee861de4414da6116fec7ecf21d2167becd3424027102140ee0d0b1f35fd63c126675446312705bd54bea4f34bacd316bb6131ae3cbe1a78b4099355877c7f973a8c58199d47e370f6d8cbf089bff8c5ecbb351dd9e3c7cb4536a437942c312f3e333373e436612c2286ece5218b3df63c1a07fb1a4a3e27c5624663b0a0f13dc3b01e297c52d689efdff232a1851c7dd00417f38eb65ace4e938c87e5bf9b85f1a6736c6320c77205029ff41bdc28a46d6af96bb292859999b9e58bfcc11eac760fec813d0eeb6d1cddf3b9a3b9b406c9b1b07e2ddd6cb0a74d525009e8c06d37e7b6181b728c104ae3c81f6d92a6ff52a9762da0f07390cf90eb03c1f003c305778d19e010d7907f6958d8d359083f4eaede2039f68da06c6f090e4138d41f8cc21f1c827128caec895d0d462ce4879ef7dce5f0f893df10e955530e7eda5e62980cb6bfcb4536fa0ab7e2586cc2f6776b22c4b0df368eeb74b4c520d7260c8b0d31c7b0374519658c261d6db51a99467c18edfca295ffdc0bcb278abd2636c92fd619e6a72f8de3df722398b78041d012191859660de1b291377937e7bee8236499e5a384ed0e320dcd429717373af046c6f7ca433206f47ddaf83739e8c00ddd93b2462f62c562b1582c168bc562b1582c168bc562b1582c168bc562b1582c168bb5da2253e15cc549325deb87286f4099350413cbac2194d8adb9d214d24aca6ac550e29c61724134dfb271009e611e5db1f0492b9e7905090ad9431aca82b4c2345a9149eca1b2f224475252ed630405025de1221bda6a06add92cbf246d433646692d118be213928ddcc61aa1c416f02907027f630c1ef0373671409b95a8844c985545ee14cd48000000c315000030140c07442271509207cab87714000b7d90407262349346e31cc86190338828030000000002000a880808150700afd24dd1d380977fd1a75b35b4f484f2c049abd401bea2136876282e6c0e12acd4e64f31b159588282b123ed0c1ef2f62a9f9bfd8ebc47436a51bbb890eb8f6fa770c5840f1b73a1fe7ef02c5b4b96c476bde4c532c9e5498739a16a4b585ed3722c495c9dfe8bf56867610811396fc41b526da786feabcab2113cc3676893d4d9b8b2dfd880cec9b8f2f59204ba60831940ec89900888970269f447a43598c8f435bde0fa090106fdf19f786cc8a715cfc01609b322b4d326e82dbe71456b7180643dee1ecfaba8566aec88be2713633878a1f6fd2f40085c835d2d0a99dbb385e4d2ff1c1a8fac69a71ee41eae3a30a40a14dab64766f10dffdeb6050e76eca31ca38a154dbd344d333dff2e4706fb72d296daeaa99a2a0d7c31f203dc5446acec09259c36c3ae0ed7b2ce631daae1957e98b328afaf73f64bb04748324a174d4981d42eba80fe4530318327365475b39f34e514026f9ba78846d1bb85791f070f6c9d5f73575a7b43d8cfb9b9ee72e1d64d151cd1c7de948eab2039857d5578060e0ae8fe05a7093bdf7f201520e9ac2ba308d5026032cf39399289caeb011fadb0a980a3b16cd25a40d6efb4893eebb2312aed305f459fd2d96c598cf025cd2531d9e15341bfebd4bcbebdc8f89a8d1ddd8fa2bf1bfc88b2ceb6c56e098dcb581772e09cd8167971cc4040b811206366f05d3376d06a8c80221c2090e3fe1d40ab1edbe158d4815477a7e29be432998a1ebdc53b5f27dadf0728bce255f9e11f50c2729ab9a05fd8d0aba5df05b305672ba8b3fffe9b9ba61d40c3e4b2b8025bf3911e0db863e51a1fab60d1112be1fb169f7f12ea829caaec69007eb67ece2f622d2329aa7b4ffdd8c7e592b2cb62421be33b19e062e4df4a42c20ae19b366f2e7cfc347a2b03d9f5d3b815e2fcc65e9b20b7e49c435ce57c026f989d94496db0a7a1e66fd4d0851437266a035ba3c4272341d29a5469447c119f5e06f3ac415c3397588cadf601b5617977f578a1d2729a7f93762ffc0c086107c5f9de76419f48734b57ab73a7d3789057f429214ff4f4f60a3b0c9bd5e4d5d11494a8d6f22754da5610d8afcfd2520a8e6c8dafb81db870fccd5d58414b516f20cc673bfde97ed0089ebda3f7a621cb58b12da13c8aeb230844c95e0a4d5fcaaafa15191821ae0b5fd3936350d96e28638dfef6b51c0517d118fdb193d1b2dbdec4e9bb998a622b09982d7def7d5d971c6a919e6e973c288f3a6c84a5e47f31ce2c22aad883cf8a8c591bbb6782dc33b93c8720fba1dafb53593fa842c6b0f24930ee5d7dffd31bdd7665f905986caf18b067182fdcbf50aa4e751f8d39ea7d208b79e482a3a1b753088d37c76c48460a84d44104500f9ce749d7844418875dceebe59c7a8d403e29f4d963c4a1de7007933ad95dbef074d37907f7cc886724e6ad1f91054a095a4022a40120b8e552ff59e5886aa55c56915d89fab466caf5698d1f5d027f8b85364dd251becf395e7be5106afe9ad2f2df16eff905c119dc7b2cc1a3729091385cf05466dadca9a2d0665d2ff87ba8cc536304dbbdf38c80f5cc3a99500a09428ec0226c332e723436c3a16b6efeff85a3b4eac04d2c9b43fa2bf1cc9a88b3348b1db334f325f84ba4766082bbe6a2003a109593057209b6d09748d4b95b71c12bf2c24d21d3fa888afea56956dcc539a19b1fff04f739e67b09a797fa3876297070262a00fc98a4cdc6bc8f451f90fde438f95a26b0311f56dc3c6c9b7c8e1b136d6629727ad764cfdd0847485ba74b6a4fcb033d31d4d588dc956f16f83f99dfac7b4bc7e64a8c6a6832994900e4846e2932454b4a95e630576a08aef29d996abcbd47386f6b7c86bc40cbd80d2ba24bacf4d479ba347ca9e687a9ae45c23525b503d54c379de5431d62206189831a3b135233498095b22eaceb6726244086dd3a597504a0ee8d9d57993d76f63ef57e3e76d354edb1fc5cf04922f3e63ed11e40261fc7615fed52e2b0b59c2246a9f2e55fa2bab5dd20f909ac8df9e843d870798c94c2942aa1c7e6815c98c1b509233744df8e610b30f857e40a79fc552fa32c5dd64da0b30d317dff910c20bd07be9b060c857eb3fe6cf2670909de9b7e86dea59e6f911e6bf4e4de48b1cd27f5050ac87cf0fd719d359debc3233feb4206db2b1c2ceeff517fbe135f0cda2a79d1a32e02f6b6088b272d7e3db5803b4ac0a4fa75c1767a1334e9153ee3c75ef0dfc4582d894d0f272b98410f0e64a9faad40967e195311504964ed6c889882e178386316e37eea47a2b6beab746fa39cebd55a92782709a951c50644d742f494424d5851d2f989dca8ea76324bdd223e39c5af8cc97a19166e4a22d103c710a1118c50a153dd05d170d302883da6690e259a12baa5761e1b0264c80fe571f85536091b836cfc87048e0503f09b86805fcc33a27d17e362db15519133b55162e06c6d210319be69ed31862e62938c99da893becd210fd1dd903209e32437504fa1cee0a5aa7bedc40d069a1fb422d139d2cea35631180a5e1c2fbbe11c270081dc4b91bb9e258b1d7503aa7b5c8f91ba302b24165203c2f22159f18a9acdb3c60114c5653f8d5cb34ba44dd68b41921cf58cf00ac74c11503fc7c79bf5aba370ac409d32cca419d566308d4a906f710ad98567ee499ef8b8ca3e0bd2424bb284c65197421661e944369a5b3a4801f65c6b149d5ab84cd46855ed13d6a98ef1bacc9256e99e81e80bc8cc71449f6548015e8cccf609bf7e399c34307a80c1e9d3ecc921c299c82fe3f4bdf4aec1e3bb311a1c1fdb117c90bd8bac832d77fee30d2a8f4a22214b17ed5465bfa1b1c775d5e61b6b6aba1c9933e7910a3ddd4fab06dbf38ba3a8fdcd6cd7865c19e27a7e222f34d205e1921bf370c2a280e44b1c70cbdbc13d737c70c5544581cbfab6dab788cf9019c52a1b22097839d0a81eeac743bb47426b6df58fb3c97688041f582310efd70cb6a4424fa9cb7d2a50d04148597238f9222e3a428412f363272f062d558e2a65ac0c56a59e878f2e3b7c16a7226375da5c08bccda44ce6ad6396179132dbd0947e4281c1b0dfe28bf53427b7b1853f961135e9c0caa7b4337f176965fa4b92bc0b19c388886d86e4ae7f08804eb6474323b44a9de7f6176bad0b43dbc4a37da388c634b2976960996bd1497e1ce2811f6739404c3609a26bd7c828805fb1e320a0613d2621e6d45155ce5ad25faa8fdb4175f3e592ae11763ee49921bc3031f9065afb31ac5c8ee604047e56ea8fbf082ef7494a69477916b0d12e309c900be37e2cd11ad79f8c249e99d60d5f193402e8904493e0aa260561da668eee86fead0393010be0d7337056ed61ba112b462e85745eb6bbfe4a6a1a5ec8091cf5e3a192c0531920ba28089723d0f70370fba00953c0eb781feccfe8788ffa300cfb57d0bfaa423ca24b3f6e5a20ca155eee9323e46f54330dcf22792a7f4db8bd24cd081528c50793e3d29cf95a9f38fbf83a87ec03c3694a9036e0ff54019219127a52604e8b29b3353ebda624513f8511f87d6cdb89cc7058a4bfc57d2f9c744d3ef51897b99d96cd8b54b5a9a609693f85d2a0c15688324cfdc583b135e6e84a37cf3e3e320a3dcce987b80cea7806948719f807f3c2dc961f74e3d29df0c9aaf2e0b8132dcbea23008980bd2b9eb9ef0c20518d63fb93fa414fe41c0a287542c2259b3739079ede42c325c7a2c6f26f07af2073fed408126e2b43d31b5568874e73434c3c70f70f175e04aa2c2d97df7dbdc7a6c78591bf0060f73cd80f524a2325435f0e80175db8fd4e2ff724f02074b5aa24e6bf7ef693d6d49bf28efb4072a6845b10676cbd3260009e0f31e442b1a098d47d3bcdd2b10109270a64dd8f0a683bc205b8cfb476327bc4b42e72fc5792597029c3205bd22cbe5eaf1d3c56fae19e1686b32565a5a68c2a45f6833e87828a2a612831bf530537d6c0df1671aae374428a9a21973531a7f761e7e8bebda944e668f3e16c466448522899ef9d8fed2de74f4c76202b7f41441eb0490e3d326e323a161fea33efb6042c460c8c84d7bb988a4cd930773feb00f489d404426723097dc4ca913b21ca8706cb653542eec9644a85a2441002e4a08d07efaa58002e6c9497dd30425f8f7c2e952d8e29ad471f4cc9447e7fb562599cafabf2b75be4a083feb4c7d9df6fab7f8d0692a2c4c06f7a401e8c25eb20644a508f8a299e871a46fd95046ddbe03ff3022cedfd235567e639a5412507773b76e084c7f80fc0eb2ebfa686eef9b48ad0f0eabbcb7a9c42e421cc224c5af9feaba09973792a84db292b7cbc705a4af62325f6e5d9e186a624c40ea0acc1c4f791bd2a83b4e33a493be3b4c717d5ba317aabdea826e245f11732d3967b372f85e259cc23c9412c70f225c843e598f25f76ecac47daf1d73f6284a885e2bfb6510098816680a086d6a5198944df19f2c3b60f59f50a84381d95890cdca9a4f8d6f361af8c282d27bbf2aa9ec8760375bafba6406fec3186b64b8482b9ba03461ed29b4da7e16158b3c5c6654f1e438edd2dcfe072e9fc20213068aa64fd567a5602588f66dca3302817a57d1baa9686a6bf071a7384f88cbc2567446258b112e7d4936989c5a68c986decc7d44cda2d888b82fcfc8cfdd40672b4be49146122b4634f3624c080527299272f1ecbb6b48d34fec5236f2781d20b14f8055a7a299f02dde1412d9140617f3bd7d507b0e411fbf80d4bd7c54737832a73d9ddd7a9ea40230ba815dc71e86725d61a06852e17741e4ca6e73201642484129ce3e6a2bbd43b165340624d988395cd7f7f7daa58683641cfdbd1e290f102808827b44c7a4c62ed08b76a1405e93245b5e0ef357e3cbbfb6a7ca46e539c0956cfe8078a6222209e996614e1e104f46f44fd153cf2e8f32de224cab85f515d219fc580b0735e68c6bab9c97b3f1d919c281c682ae52f965b31fb6b175e150fba1f7f2ef37bf33df7dff7fa5a8ae498539b5f6f196d8a12650edf1fa163bec1fb9b4c0f8b49c0b5e0984b5dc9fbb3daf4db9f7761b72e592b34b15fd96cfe31607c6beed2f93aa78fe23166731240a91a221c2ef73351aecbb694019701bc241e9343b25130baec1292a762e4331f56d08f5bc05a788d6c4bdca169c88f7128ae4ed2ebe8f5b8af928bef5e8ef9df92a035942ffa0ea6d2b7a2b697c091a3341b5979f2c0ef2f14137131e9bdcee0be22e20f1a5620dad008bacff45552ca03f8fc9bb30d0f1c21945fdaae6f35d2c3603feeba706d7ae9e2211ee812ee92ea19f931fd8b245fd80f38f5553f347969472b221827b4109ea93cfadbd5258a88c3987376cc298296fd538eaec3ad5c2dd0703418f243d255bfd9f99e7849c759a3fe793fce6f677b7c41e0df8fd341576420fddee677906d8ee6f2b3931988052e9a2742f19643aaf974ebda209d4dfa58ad91dcdaee603391d7d6d37bf312da4c5c538ad79a2ef6dfcd4318b2ca8825fb1641b27bddcabaaecfea074acd6a5acaecb0f0f8c56426c3e7a7a1b0d1807d470ca49fd544d786d9557df9190d03df0067ed84962f61529b9b7fb045172d45a6f208ea78c147a69edf173f400b7b16e007431a580b178f4006f71ee0074f012403171f4006f31372297b8197dc0b7b81b20fa5809604c1c3ac05bac5b007a4c69c0583c3ac01b373708a0790e81a0d369c21b8004462793102dd25b6050ca5ef4cc31733d34d06c9e2ee1d9d6def18f2b527ce36cb11703d5e7f30d6a9caae5af3ade952401d1d29d18f42d6536e5e56deb83187491c73a231b0a88f04f0717d40b0d57078c023f6505d62bf10b00ca8aaa9305a08c37c87c91dfb20cc0eab60ae66caa98fb4a6122250a24c78fe23bfaa4833b85945341a7752a66ed2a468c0282172f55708c46e96b4c3a1a5b7c2220374c4cd5495085e84f496238094d9215ab54d524dde5dd82963b8de22a41eb438f450770f3db1d101d5e1260ec177a006e7077fddd70bbf71b95eed73b4545f344fdff7bc03ca65fc77329ee0fc62d1adbedd636139fe8eca41cb86093d1308c9f30bd6ea1591d28af5e546fd255d8f5298644378de4656a9a7636fba2e0fd0effa6d0a704be6adca9cefa54ed76ec88a6d78b0cda99a65099948bfeb39ea5f6459eec402ba75156755d417155da2de8efa38554afaa6f04f23e2bbee140ee939c42470abd901a74bf1213fe5089dda01f7f53b3ede0fa12d99630004f900e1e3e9a3fabbecb820fe2c341b13b638cc4a0499f07bf9431ff36301a15cab8e79af1352086183197f78c2dec95e97cfb8ec2f2b83ffd40f9af7623b858abc2258fe30369a4749cddace69213d67b8353663b2e52f9384d68f177535f23119d66e218df0dc728f197ee8d15a6f183373fc64c8e908132b71e284e32ab0e7e1bac281e4ace1796fe8ed31258d28ac16ae292d8339256bc7a8748a2a08eb948d8b1d83f1808c4efda9ee4fc85b53358ac67ec7ff907c1427663fcf11c237cb7d239549d73cd100653a09e7cf0910e1225e44bf05e9e6b3f7bbd7d543e7f958c325a379eaaac9b09084d64f47b730c98d65c29bf74d43d47f4566e8dd33f977722b9cfb7fc0ad07ab378806c3ad068508f1bcbcb6e76aefbdf8e4551689ef56fd54603a6a1535476f983f2ea50bdcacee9a79f183529747f71ca5a5ccc29750139d1bb67fb373f0f697bc4c52f9f8c41382f6b1b41cfcd8bff1cd9dd0e4e81f4bb8526933afdfe68d44e906cd77edcbc7cfb7b68bb4cb36f123add91a59fa6aaedbb1e9e03f25adb0a0b6c4a764cabcbeef76e753ea2b8ac3873c655669de9922c5f79dc97627c1817c75d44ab0f09e057d92a85a63d0787d87aa3ab4155691e0c72c22582e54eb1272e8b24df64a06f7fff93243745c130b2783aeeafe3e37794a5d5d4fd8477e8b9acfd610106048b10a4059f2c107c27ecf0c56ee839154399ada94709fe1c3c87062b0c51c3c3d69226a84c3bdcd3d97f5094d9beaffa56f2104d8bffea7ff4f37d2f95be35a168551ffd97b92578505c911c5d1863b7e4133558fdd78a42ea43dc2edad30053e6d148dabec483c53949a8d462febc3aa5839d12922c4d3fccf7760088ea0830b6bf858a936d9befc884685af855754abf6c483589b03963c0068401dc2276271c1760477a123558fbb59a42eb426f25dbb38039d9c870171d6a34c35bd9da21b42475e497658adca0d3ab9e5e3e092a284571f9d3643592014c909b1bbc4c698b45dfa391d55627655986baaa140ce0b4b10c3ab1d79e7a3712315de38f53484d84d14f81739317563829f764e482229335d9a588bca370a34b917ee7403518562d66e59033ae44212b89bc2396ea4d20bb6d7ffb7fb19822558a49059eb2f3ff636df5ace11fb8d48f56f4e8bfd7c210fb18f68c723b9b140cbdb9d4d38f71544709b1ce97018584513a693be455067762d2670c1f60d3e8a4b7f9ed606ca30125806c5e9213b3a7092837ee0542d2f2da5d4b6878f6b722b3788608853c4a5ad71dd2d9166482d282e8387a2830dc5a6cbb39829dc65222376af7d79804827b8259432d03d2cbfd47e5993f8ad6df817ddec170028dfe30f0e4c5bb03cf87399093ca044ec076f3e3ebf050d495f24c7d4dbd8f86acdf9592c2616b626c29f4a6f57c372edfa38e2fe86671e74b36f1fccaf58373b370f12576317e85ae1bacf45ada4b54bd546e3c81371617a7c416e3afd81f786e8b0b5fb289f12be8bc31a517237a5dcd5e8441e3e53854298bfb004065acf340cfdc265118fe8aee6deec72e568f17d30e29f35a4e0088e4dad3c2d6114913c23c08511ab05a2ea3bd8143e94ad2bf90ea072a0de58284725abaf4bffc84283d4c9a2697b5aa8fa167d7f3b86bf3af5a089cfc57c8d7c49e3a206bd97cf911071a25109738fa083da501cac246c16718c4afb94d61aac26e60d9eb126b5d874fad9d701d35571c093152fa21c1a31a2214f92c9e5d4b7b0ac802fcca4b1e3ec978edcd2d6d0c600519b91621d9a1e22467b8e155c4a32db470182b6daa695d7366b09e1ffd78be530f1150ebaa66811b51ebacb0026453b7f315a37b244d489bf9c86c35258f10188b1bba6a51e821e5012adc4465fd24fb4218516f20fe63c0b6c94dbc329b4f921c2afdd9a9be90d6d724ffe03067b505506d2a8886611851bdbe0fe775b6fb3c3ca02ecd98d588114962a997d4f1257127091e104d1af960f6c19cbf60fe0a61fc36798f23c19bdd7d4ca89917d1301b83cfc5dd797904562c49590136468656c7ec4c3f4d2f03a42579ca80a5d379697e9d36fcdcb430f7b2684bcc5c50bef08d8255d4d766baf016a89d9e432e5095acc923856749c340c6b82b0d323bc09384e8d00e2502e3df85287a9781cf206396e090f381f6cc0b62d2b86c7d1f88bfca4fc867005e72e81f7c5cd578120c69a0d1dd802b729ead4a6fc975752280fe8b2a5a5e2266408c5fce475c7d24ebd6a34eddfad4ad6b9d7aeaaebf7eaf578355a9c84f91a022ab13a86e75ea5baf6e9dead6b14e1d75661dc775f2c975910c88eb85581d9cdd5a80d1a4730efd36f8ba47c48ac41d8d32cae408e060f253b933934d3eacfb1274718ae64b42242ee084cec455794c04064ef81075116f8da9e316dac05e093bac34e857460bdf1c6585ea7eae23dacdab1b14e7580308ee78249e66ee6e6a3a29b308c7539c855558a03ead3f2275535e55fe6d15db3ca28a5cacf275027c98e6940fdae4fb04b42dd2e427df4c28eaddf95ec541a2a7074e0bcc4fb673b270ffbc2d078b978e6d3a59b07cdeaeb3a5cd536d1d2e5b396fdfe1a263b43d870ba74eda3bbf38e9d4aeb32d39a760219b1c6def64c1bdaa4eb3ac9c224f91072f0d587a85a2a7e3cb570c50684ef12c5e316a508c408a1460690636a691cc290b282011887955667289e4f37f9f5175ae810135cb6c26c1c4a2b7446100099a71a9a03884728669ec012dacf9c2c1b82a511c4061661aef452ac06a9264a24242f3eb19d3b4075948f389c3c05a463984c2cc34da8b2ca0f1c4cdb854501c4239c334f68016708095185464e3521a4beee4544991f5d93fd94ae17dc43de8d8eaf5bc17d2142e475f403b4c40a4d84fca7048cc604c3575f639f075d4ced6554c9dafe650464b0066f7e207773ec7fa4952e2a64b609697aa54d8f3e1a6487b7fbc3e9307f6890f973bb56efbe5a6188d45071dff9402161049a5a315f523b132461bb15f10432c13381aefa50bc7e683bec4bd1c82cab1fecc6816537df05c412e1ec0d6f06905a4e1f20746fff05c4d42156273e054ab0dbf852f4bfc6cd3c7edc481d1030bfedf3b0ac10f39ff34722ec638bc8925fa870436659bcdeec8c1ad25b73ecdd1baf7683ebedf86d3afea1e48bfa4ab2f04f60747f83168a4903f8038cbc858620151433338ae1ad39063d8e79f5433eaea0cb440e3993ad97779ee8334a514eb9b50433668201ba756255012bb169a9e160a791e0f6f096831f037889897a0f4f0dac9bb26b4cfa4169be8a4505386d290913e80bb1e328746e4e630ea7ec8e6aeb952236171d863403fc54637e425bb44ad6821d102a9b398b846470c7a441eed80f5f6c1c83f30cf4d788dab1aec4bc0aae6d83cbca8c720f0d9c4873e73da4d7027c57af03986931631c9105035b224433fa367d2aa341ebaa2db95490d0d40df14af0c10a6979f9872f01b06dcd635588f58deda59e007e597c71d9cf975c41dfbec38d52257f6b6cd04fe9fd74c32baed3ba589abcc4a5289f054c7c19d797133e84d4e09c651d92b949c493a004d806137ffc098cfaad6ffde4b73dc1faf3a89e4cee378172f56e2295736d8172e666d31b0e4b129cef1ad67e18b1ae120a89f1b52467095c37b43f2e40ce42895ac86b8889a00f130d7a416f4881b04b4829aeb5e44eebab7255eadb765b9db092eede317e523231de141b110495d6d4d76186effbfbac8954f12a19be698a49fc292edcaacc1a80324afd2f9767e9eab8e16b5832c7b096c3747974574ac9cd4d103f10de7b558a9998ae460c4e5532ac7893fa9aead723cae75b5de4d1677bde8342a2afe7eea210225bef6dd3ecce4907f9c446c68de277e117fd91756a56a71385dbd9fe0c1891ce2c1293eb21d5730031ec1b7c30c70f2e7cc5b7fe2b661bacf2e90ca23b3dce40705121b23d7760367866af2ec50dd1f6c762645af1561059f1a819fc916fd017cc496405143a5482b71a3293ba853eb001a641c52978bb6ac749b25eceebc1c3d9db9c7c5d95ca272c0446ce772a1dcc0c2ab4d226e4d88790d2d0bd660d2a3117043e5b10de12612a46db119f7c3050fe18dabe826f717899be62eb81a7c4073aced8224c4708a87b569e3840c556ba90a5f570231da08cd178123bcf1d074b70d9ba73921e4fc58653b783f12257d60d9bcd420a9be660ee8756ff64c453e87f38be166d597ebc231456935765ba43c2016e2ae850e66a14708adc492920fd41af44e6d2f34123dc21e9ef54339c3fe06335e99e77568cbf307a3b34e3f2faf624a36dbdbbcbe28b49414530b75439d78a04da50c69fa0ad4e7fbd4c75a5ab2561d2ecdd2304cfb88c24231f23618d52ce28e41722eda54a60cb24f28dcb17137a88c6ef741a40ef24edd25cb6f920f021b2f5c18277ad3742da0209f12d6a8d4d5679c0dc9231d128d975eeb12e48d06d1eb9c50362f771a5f56407c304fbdb708da29e3fa4057e59498a603d85397388170d5f2b53bea1c5adf6c7d92bff11c5c444305ad2b4e35e4c135ed22925d5c3016b33c184f709a96fab9bf01ec33ca3ca9b4b4058559fd10eb03214d5b1115fd2d0778156f426a3c1337140bc4eeb8396c8d5ef73f423126955ec78503bac107558f71005e09070cfe904ea051655657501c5ae8d6a92ccabf65f71816bfbb8e93419c8aaa5341fe22597048398697d37910c17526acd08d72373de8aa832cc4ee62463a5769f0d1035708576518cc470b7763cee9fc3c7b86f2ccd28169c69617452bc09543ae3d11e59005283e98b94a23ce7b0947808d0711de214f32ca84bb3822a08f231f458e5541de28faedf165602f637fad9a584680557a3d9d37b390f0307a7f4fa77bc898f0d6964443852e72403d5b212a7e344df875093ff7be90305f29b1d7f14809acfa7e634a360e864015c2bc1c96a5192410b004af5bb2f0731cb16463586ede8698bad8501f67cd595c05d55602e0d2a379a0919b8a3558d920500da7198d2caf58842c7cc5407bb9af76c964400ddf7c55a05588006c276973fc5574dfa0a5b79c51dab173b785196179347755c93fc45a4fea40c904d9e776e5f1e45bce5f504de802df34247c17c3bad901868eb0ee5ae4f658a6dbc8718234e70e6b91110307ac27478767678039b6a9ac31bfe28cf3765b3796ad6a12860b995508225c0c1519135e4d8cc87370aede508e01497a55eb78085dde6f11da0f10a3260d3ba354a8c198bb8f0851365a66dec6d7c4105d8779d3bebc18119881567631068f0fe3ef633aabdd4b6caecd7f8cf9165b7bae1718b0a6719eec83ecabb32a8051ef907ec87fc06e58c5498acdb295d1953c90ad5c1cd58ab066a6b823a4856433037cd6efe441102c4be90e58f60bf021fdf47821f086e4e6b06595ba196063ee01617a0c8cc9dea3a6210ba16c5f1850a138a2aa4717e5922591b2156a8de338870688f74e3542d6aed6996388dc0620d8961e83ec154628a4cb1bb5dc56f177131d724149722a23968f0e4a3ef6f8910db4d752dee38bc8494044c98517b15fba47cd0b23a9ca30ca01edcb3d5dc11953d286e499271ff36d8068d050dc79eb159eb50112e45ca6531b56dcdb023d8022baa212c294145c96675e3ae26b0fc78b9acc08db710db29dc2e712e32d8092886bc3361acc46772b89d71031385f4f8364ae0a181ed5e9c427c5ce35598382cb0f8ebadd41b4a91c1648571bbff47e035fd61e806c6ae7c39f834214c531e60d84564df355efdf5f950d1e870d7ac7e609b1375c60de8ddd84deacdef79c934d4c0ce7e91846c0f17a79a9ac29256b1f0c939155c0b25964099d5a5c36c7e409fa6f1a45b581ad391a5b50dddc5cac83718fda9346d9e87987c16b3143ecd4e7bbd95be493bef02b4a01ae897676d16e9fb798ee73bd9ec3d197f8462a9a914812468c36926426dd54d379c2ac6295e294c0190b0daed055ab43975df742e169b9294df280002ba05d665da8d170bbc1d0c35dd1b1c300155aee1ef252b6c16ed465cf46f8c7ec8442609a9fe5330f2bd34ced93c0f2982b4f86027aa1432f7d40e85612e9f335237df95a9a84467f729e3a4dfff39fea66b2dae3365d39830aeefee8acce7b6d4832145199cfd3cb93b21c14edf6841097fad29f941346db0cd1b69aa0556455e9fa694683ca7d1c58a54751b1c08eeebe2372898e151039be35794b0654ff722f44dac98d10dac2e89ac68d2fed728694b7caf9760142cb9c600d7a1a1fffcd00ae7dd1f61f926feda7a87720062aa148153ecc261b784bd9c7a2994006d24f6b95ce71bea8547a3f54ab8a4b6704157c5113c7aa6ff19a376625572b76c655add11e44c94a71868d091fe8749455a2315a6fcb6ba67beec6f8df0b3f28c6f2f9c60d9fd03c9cc6f053f01a4a7d3bbf2613a23622c16ac8600a36feaa55919154b321f1a5ef96e942339311104eb2da18edb3744c5fa30bd85cc9121effc305937a04ce4404f8de98fd24b27a5c65397340feec1f0882841366a47a33f69b026f0a8f9e703765a328499715c56ad4fdf83de3002b889abb9e27292794eef2d1d23fbfdb49759c1048b85f04af393090bd603695ec6fea9a694de97d0ec9ec32dc5f6b5f28176e58fc7fcedd676b2026b04087aded92b54dd91b4cf37868b57dabf93a38f3f524a34ebee8d782a47bf436fe1b4bb8806719544ed06fddf008523f3c7d930127ccaa8e9c95f860cbba197478529463fbcd9085c8319d5ef31fa0e4c5c2a0039d4590eff3bfa36a417a77bf5c4f2303832ef07c58243b9fb01389829220d23db39e7631e6a028d87c9afdf8282661820e6de040e7f3eb9c9f60b4706d12e044e20954b981c9a1a85ae771dc21f07be83345d2f2a9324998279d30e9354361d4bc077bbe95edbd63b008d8fe46911e85179c268fdb9d161e343b2103f1e5d141207ab0f27b1fb1f47f813a845c70682f6d25288bd6b1b70ecaccd06b33d4cd53bc4318847d7c0532a51810015c3acefc207f77e1b31619209328ac66bf1f1b42d0df90d96d52a79a7969292d92de2e8450b0b431436a576223d442f54be59309c84eb53c6c9423ccc32a7cfb68fe31a4d4089f00d3a844286d550aeb848cf9305f1d0528d0f208190bfbf34ee1fd5be51079f2e65dfdb7755e3a42f18f6c9b45589290d96ae9c55e5990c554ecc9ef4489f11a89c1c653ad84b8c0cbc9195dcf0d6b4ce30ddb50a371dff6e6873fd8c10e3edcc14cfa36ae310d377883618c2945748335cc25da50431a80256f70235d464a51a10f4ae57ef0627a00438baa1f975443a5e20ba69a1c8fd2ea120173fc4b49a9fac5a4a0fe65aa50bf324997fa2aee3c247b3d4746d8af083e94276ec193c7ecbc6a33efff9f39844cdbaabdba9ca98acf51c13c0abd7b912f86d8ae2a7753042595c7a65a9503ee1f1d52d9318717e958fbbb621d2760bc721c489bf4f624e8490a3de0303ee92af0a4a62e839035c140ad5ca24d434b1b4d2d16cbe4cc63c8bcb1d56d09c2a5770ad46fea50514e88d6d22805f09197daf5c350a6f902e71ca3ff5960f5a00fe53d22f6858e3d2af7fd311635acbf0e57dec58405f4e24dcd8574e67f10fa31990fafdecc37dac2c21c2fc7cb95265e824dd1601f7dcd8006eb71742e1957657fc09d9ff894c04dc318b4ac0b3b5330115e0c7324ffea20797c4fe324ab10d915ff8a974f5ea9286b8674ac08a8c7db0b812700317f6560cbfbf4566c6fa2110affa3411a62e9519f340e183a8e2d2dc83880ad0b12713889f83990cd64568a7832f2f2706517d7bb1c0923b863e558d8e785823014b2cd790a04a7db3713ebf96050705eab7c69d2593816736100326495dde6932d07712b87141a46bce5c00384380e479a6ac21702f181ffc876b7027933b7937d08f26e353c4a5a3ffb2983b477bc5be30f1dbd793554a1b87b6c9ed27b0dea6b6e3f0eb041c19143099b0c3600530105def17235267238bf682dcb2311ebad15fb333c0e48a79eae751ff186e5e50caa13fceb0c107258fd568ac4c6fd669efcee5e78d8000ee2896db8f667c3816d829b02e07c3c49d133b233a14c01cda87a05413492aeeebb3efbe935ebb890d9810b289a32db95f219067882501b9ac2f0289e2b155bb0834c0cc5e40d924e453a413ee3612f46d46180ba1f747e9d95e85de2ee2bdb1867478e573a3f47d7fb499968dd489cb4b8886cedc0893f9da1b4f7c01d6dfe6cfa147911cd395ed7adaf83b5228267a908d07fe48956d14b6b3fc3db9bb341a7aa8b06bd5f5ae0031e8a28cd0c80721a32ab10cdb5252606bb9747bc7af61b734112341b003453022c4b189ea6a8840cdc7394e90257b6c54635b5f57b5819d65413ce345ff51867368355b9f9245575495fd5021384403b02f8c302ef312f450fe481dc6b4ffc5b0df1984483481457111e65d85cefb36ce8b658ed2521c61124e09f17be4101f1f91c1419f21987e744cd9a2a0a0a3183b4af62a7536afd6ea9c4b309d84047fc3272f2f28edbf0f0e63839f4574d3a52721180368dfdfae695c12f237b5a24b1a06b850844b61945ae96b6458164f94d3ad2a35ee08246f8c58d2532b41a84a1a364915c59db8661663cfbf3cd13f7cde64db0f5d80256341b5a548bee6a5af244168be61178789cbca8c5436bcb1021dbbc3177d2b6427ca74bd156532733b33957a9fdbf209800e830325cb2400719b42752cb53b35da1086867ab5549d9d2db050d6af242a0c9eb267f2b8f3c5b2c23218023fdcdfc745ee2e7be88bf392baa54d6fe5327c7324897b81e8a97a6815c01c2dd994545ef3c8a4d22f90e4806d9f0c96c02de419a715fd11d10f0ae4bd8d7a1e462da907bb675c7f6f4e95fadf60d9544768a05d4a822e862ba86b5da2a64a653e7f1ab5ee46d56c3bb50e8871b2ff5198619f2a08a3d846b71d3fdcab49f6dbbb48022b04022609a90637d2ac25d2459385989fce1e28055d352b1bd1912056b4c0725d6d803de9b74d2cd257b0eefa34bae3654a0e329169c193b4c5b445f659d563bfaed57eb4c6f040ef7823deb1101605eb0653d24600c17ec6c8f0420832e992ecbef2fac71503a2acb1094765b873c1bf2fa861a77f20c41d2572408c050b9a812dbfb6890b0a2614e4b47ab2f1d646768669c1c15e5a5d1fe0e1dc107df3a096e3593d9d9a6217c9c570a59063cfb981bb20e166c768cc8887b8445d4caa9d7fec65c54a303349a9d143f3ee61eab9a4cca95880088b142df5b16713baa9699e4e385751f14dd85e0eaefe357beefb53bd23e070b0629aa6ea13ae41350956ec9dbfd17a47c25354fe0381a5735e8a3fcbada31af7ead171a578674795f5c946266b7943003f414f53802dd0385cb17507c2f9546311970cf7e24c509cba390531224bad40910de5239601bccbe72753469ef0878240cbc789121b0bd1bcbf974f171ecddb88640081ebb45e7e0b014068ee334cea88a8d4ac46f5d18f651a3766f9a95203dae42aaca6ec30cdf95764044224d1bd2d4712aaf10dfcbea472846ecc84be582226e1a7228ce234be75e02de3eeb56d5c1d8d4bf6186c6a9b485ec78845c5c0ad0131464f4b45a80f18bb762da57513923dced30f1824680a9df4c4a1401981039dd4ce426740882dcbd6c03b25d9424f7c70637d4db7ba312a27350c3b65328ff97daead1d1e069c08fa67459ad9e45ac3239c353aeb5782bf46da20342edc29bc2da0a25e1651e08e6ea88ac7d466713dcfe1c3c148319bc4b1c0ab1e4644661e0e5feb7784a8921448fb7f0532ee7d37d10c9230843e35de74e16feba8324e762d79762423851c3dd11609e67655e072ec535a685c9a90c8356f2c754229907e3600be052fd163c8b1a1c39b612bf8b0a0c6ea36928927dcacef57abf262598ed597a02e1cadae315df583616acb99274120f9236e2a4864048bd90e58133643d64713416ea0a83bd22da33b80faf6ac5963d637309ce7bddd71cba125667dbecc383ee1091d6c3f98cf76862e1b7e1adc931a5dfaf19b14151e9af53dcc2f516ecfb97d80d7561c7e355f7e3138af13ee08a1708b580e0794812807e7c3ad9e8ab73720c37438e649b2dc601e88fa2c999da6d5cace7dbfa28ae6bbdb55b940cd5ce4b84d619a396a20cbec8a45a5f7626ff6f683e72813ecf8796c347e2c484699f42e1804290727314e3596a64a66194b21bc62e18f020e4faab15b796968a532a21b770ef772a06a599d6d1023671ab284e7ca9f46e9ec1770f74061ccdd213232195524ace753e97bc88838d6fcc353b2c0a6b403243c3f8db18fce48362d6ba02dd127cfa9c4454a537e079a49ec122c0d8386ac11336dcb1b4eab24a4898acab2827f087c89684a2ec2bdaa4121cced3355451f0bb79d8a334b6983e7e69420d252ac0bd3572a6df6850ba73b8bfc06f5b9a66950da33e73213a89dbef350113a0da1230d4fd88c1117bee89e4c574d9eae846a39434f1c4c9d9480f3cddc26bec4a30aefd10b4c3f92890172a62781466f82cce4755798793b34cf8b44f49dea565cdea0add4d9e177b87d73156a71855d2f4600154fd8ab8020a760d32c54fa1c897fc0960fa138a370c3166aeec7cb7bc22d82f53e5024526f683cd00496becc91fbcc9382f9cf1cc4d9e00c13a508ad6b0c39875289dfda35f724ed28654337ce89ab8337b17fdf6aec48653ebb5f53dfbd17cb9c46e4260537976ed4c49612a327cfaa44c89e7104b68415b8cd54be4481f9bbc1195286ec37f810c1021d618ab7974616ab41050b6f303ff777d0cbc884b042fcbb3061a564a7dc46ea7cc5b32ad610dfcd594ad18899587ea98183466572f4408046f520054ab640adbb90243024e5185272e3fd70f66806dc1dda4d512d100540262b05cf042a39b09d5cf9e1550fa84a2e26f08e60522743ada2438600112d35068456168ec6c4ad8c9365f15c4d6a344c307cb54208eb66b6a577c450601431a37a7e5371d7858b2c94cd6fbe29088510e81d683e0524172e578b0ed5c10e361605b82a07257956e5609b741baf9f4409b1132f4d4749144d3bcb8bf17cbbbd5f3ea50652486f40e11f236c9bf414b0eb1f3f3155115da71fcd30506ae3993e39fc6fdc60d965622b7201bdd6425a1bf965bfb7ab6985a9e2e1d00b7e6fe6f82520cb62303a7c1d5f08356398e7fe3215303597d35509bbd143a6abc36dd3e95c14db420852a058ad9cf7b5c1672ba8767e45c68417d36001bf5b98dabbf01b7815d6d6610224cd32625cdeac0693141c9e8869e3256fa57717d7b191d8bf5688ebfc6ddba6fc9b9432096b387452628f0f851933175221a8c504f79131929f90b92be619740476aa1883c7dcbee3f4367868c84cd834f897a05d917f6d6a04109c3bcd90e6deee407e07128e4f9a52436597cce27dca055a1e322e15f12c8b33bbdd6871244bf83d62a6d1fe25630be8c8b6c8dde38d277ad42faadd4521dbf9e8888b1107c1bfe2cf1fd36890ad267a86705efafeb7048837bde1c3c60967f901a98124e9a95c7c7fb4f80c5090cdcbf757af485d737e9a4cc8bbdc4af38e056bbae863c9201bfa8f0ce0172a0428dbf9c873278d4175c34bdcb8361088b631d12c5540a05f3e169607ba6031f2de5a47755482518b44954db53d7d97be5749d9a92d51c9b8046cac80a10d66cb9cda2e051682a722e824ceceb3fca993a4a921d0410c100d5e0381e21f7bef1b211c9498fe18d559e66b435d84f5d17e746a6b82db787c96f3a14815c7c4f6157dc11d6383600eba27fc9d1add8bd352ddaa2746025c5128412318809d528da1ea790a89bcaa20bdd82e3e27720bdb8436b69ccb44921f30ea73a8b373820160b929b2678ce3a3e2b46eb3bde9e5c6410bb03a680a0cd844d7e375d9234169a26bdddbfe8ffe6073136c208a236f8233c0a2ff3e6d8cf51129cf94b66dc0a0cc70a3a13716c1f907527ce625c1d5c0fd791c949311d33e28744efb374a988e4f86db311f0416231d8d465f9fcb9c5b88e9210c904112d125c618cd8c9b34e211a9f0f30e52c65bbce0a83389e287222dedccebe24a3617af40f0f750eda98165da229511c149b683ac5a4e90aa666536040bddb1443b6a66e585449cdb843c9dfcfa79ed7a466ffe26f69ff8560b8e9a980217979c79f28215314418192448e091872c844fa67687c9e7653b257fa340780ebfa398abf8055bc3bd42cfc47142ade79b800c0cca22cbf044a4fbedd121cd89a7ade5dcf0a7b30d83a9fa2f651cc1bfdad47bc6d73b48fc2e12c201f97e3275914933368eadfad24f237a5364c72c153a35ffe6f661bca6c9575b0b60663077dcdecbed641c681570c768ed1ec46140420c49649e56c2575f806e042e6735e8667033efe6cfb6b25e1bd81df27459cc214b965e9bd08eb914160067ea9fde74ae2a1137fa105d252a466d9374415fb660c207915b78df83bfe30e4dcda4f2e94d9183af13ff24a22fd28aa857ff60f81f67b8be4621eb684142d65ca9dff4e792395510524502e68254b394b34d424dc7b5cab16f01f31fe2911a8c3005956fac52951c40490ebaa22b0a155ad8a1e9bea53ec9e0b0b84c872f7b66d3c0be474f497ab0cd980c42775f6ee2b1b739c0fdde5983c766cb3d682f17814565ed3a671b4dd2c96a3ad26a6411a442eef66cc1296b8d427bc2d32027d8a7e9c9e25aa4a5569d0635cec2083cf2e778c6f6dc161d6c5bca05d5073d0cabd5efcd1711802973575de24888b642c6b035668b6c4e4f1fe8858b23fd021f8bbcc8b857aa0c91ccff44efac9410e57c016262313d897161b5239244c4b7fcbd974512b35134e6e899ebb98e3e15eb6603f58d2175a29d37cfd167016d694dd98aa56132dbff1e5790045bc739e082711561ac4a0d652a3ad528708812a70127f7c68faeda22b7a511aa5dfcdb6dbb4bd754c0d9e5f27d375d04f7ca2e42118dff607888c6e41c63b848f0641220a6f79bcfc8cca1b2b8f0195555ffe198e22988b12434e826b516df291d3374b1701360ac89b31e0d7d58014b389a046bff7be104c788f35de366bca90007b444cc0691fa9861971549be995f2697ec85a25034cdfdd9d16e6658bed87b5cf66477553f11034342516fd06290a816a4b4bca3336608a525acb51dbaa98816a5b7d8b3df6042ff424d1a22b3542863e4fae0197c61065a9a4863b312dedb464d3c5cf3ae728d0a97dc2c9f07ecda9b1a5b0b85f095830e9c46142009517cb24f6842812e90a6b9d0499f1fb847ef08f9bf1ecf4bcf390acbc1baf6aed00448d1b55e17207eaad21cb4465c3b28abc3d8d9e557df452941c291468042413886cbc3fca25ece6f3794e6624335f30288190d46b02b8817bc2e59f83c9ba3d6fd76f34300e0b221785201436a4a4e33b757a4b9a9b46fc6811035f21d7cd642956cbee48378021ce69e92810e60f285155547247da8d1187d8cd9da57ca0d72c06d2a1c60b54fb8472b8c417cc43a1945c27134a3aac4884ff1eda4f767dcc3ecf7e02553d754833c6f0725419a9b8313fe93e080b1a17087fdf6f4134a131e0bf3f00a5e485952177b9545e72855bcaea89a6ac292399f549afafac4e4def05fa12d2a4bd49bbf3bcf38541fae98aff118bfc784cf45f1fb0381f401949c8c050b8723e3082b8e137d3b8a79b5420cf44fffa07221f6cb752484237e4774e21586e4215d741f25c33d0534109d956f67b4236100aebbd30580c6705fa460cb7696f05b8fd58c76d5072d4544d6fbc26ca3db92579a86ffcbcde1a035b0f6edcbb7227868ea16d7418fd00edbc86328d5cdefa9f47475a55659ca11d06e77f5b0290a4e6fd517d96e5b74a2a6fc18729e3cc7ea9727d8f321e99fb559de8cde630330d57759ea2569d720fd395ed9ab431f051fae5d7906f47402d9069c129d095bae824d2dd85aa13d895ae80463d86f23cd8a98e5a6f94320ad8fd50713f016e65daae8b09a3af4f34ecaae602fad34451bf0b4d19c19bdadd7bc981f43207c130ebbe573d050f49f46ec53220014bebb3118ac03c231bf258f99dea3a9a2a0671532858a93079acd2cec35346c8e0ab9cac24d145c64aba11255271bdd0b01d01fabf05a0727da30b48755203a4e8c796652a56050999b202b198254f5875738627b90e3b7c3a99dfb12e2b0e88fb86004dce838dc300ccf002ff7b13acae430e15d264785c1c5479e0179d8942ec1629b7bc943e8b4e6f67d7f47ef1eb890399e33e8c70ebd0805f61906c14f48d0219a7eb69574dc2ca49b46a82d6187095719e3b0612944ffdad3cbf8112c8ab289807edaac83da521d5f142bd32ea5fb2ab46821c6786e55b747e10d54e5347ff32c9c5788324cb44f6db2583ac02a081048a9865c2546be43cae4358b2e30c6179a674005b61e4d49450324d07d7418911e8da08de094af29b1c04d861c13be65e1c23760aae32c4933e4a70393427392393e500fecfef3e8db372be8fcefcf62aa916cf26fe01ec44d29e25cf9edb5304316509b611f98463943d996613095996aecc9e92a27a0ec232fd853da53b41678cd298f1d061e950034a50970b512920ee0cca69eae5d606e438bbcb9c11cb689f7c4c3415258633592715939c8ceb6e767a09d3c0eeaa8cda002e48850204a11066fc82bbc59de98e2af0f8db9a8e7bc4308f9aef811c7ab25a5bcd06c4cf5726a640d91afa9f1617b0a29ef70e5c0992c5131f2b681091faba2db2e1f01ca9679eaacfe4835225b3917bc1a888cf963352beba9b858f17d89347190c91c89966751ad0a87b5496c6fad30e809e4f93da79165fe02c3acebb23f2b539558898b082b99654d283fdd15e5588a1856a0b19db38e819b550652273ec208dc3bf02e3ce846367d0b0c104ea0da50bbaf208e3c19b148c51033ccabb9c1c1dea84b3a1d3ec17a4004a416d387080e1bc49a8961358810f0e7a68be80794e5f6e3b0be99d7b66c077abcb2a73993209b8f89442b7b175f025996465992572a538fd68e46c16be638f449c41ec6416606a29b1b60fe181413dcfc00468e5a9cc46842faf69c1023b0512f922dc185b41d4f39ac262e14eecc93336148af78a6dae2ce0a3dc24a13dcb70e6e806dffe732ec4645e294b5b592305b1afb64f4b1a28fae62d1234e7d35793b2d24c15e5dad7160c7fcb6ecb93d69bfe395d32e7d3b789bab67d7a9634841e23c9123a352206f60357183807389d7b6dec4c981cda1ea040847adba11c8387fe8ecd01cdf95e123b195bede1d12cc49a383b28b6c53b4c19f02efe58c09cfbd50366de8031a1b6439ec38277c5dca21c89d48c8ad0b93cdc8d83cf6e4a87046fb2d5fee4ec77d68f271ce523045258228ff5d8b9b608bbce2ea57abc3c54c0fce420e965365d010688a3c19915146ef7608ac0ee32bad1e7c206cf698d34d22c8d5438b014e1e5ce53177063dfc9a8d615b28db0d09e76269cde9383dc6e5f3662b1cc34639496464bd5f739850a12c6383436c9834298f17f9f0b31a9ab245ba00d3d458f794890927988e3af1a1a58cf6cac9e9c6d02665b75f825a8f7a5420746ea0fcb6a3194d28b56007c58e71dec8baa3b13011b2cd523af13eccace509d2cd9c1df2a8e8bc3109d5eb5433f7d8e95a321255d9d36a1e9088e28e3c51b9b57cc72ef657d096111723f5a87cfa1bcc3df1971e6dd369d2f5fcc4eedc169620aad55d67b1c632d853d83d9ba3dcaf0c64531a001c3ce6b1254e2ac190928b5628966b1c8a5fd1747c59adea91450df748cdb30c22450116209c35a63c32e27654668a12aa56b5f7460701a5e9cb98ee2f2db9239e289a6c34520ee7b8d18a35fcfedcd4d364edbd2bad634cbb14c5504efe40ebba82e2d830042d840b1997047dc70ec759be7b2e527bbb2ca5ec95d2b8e317c35a601d38b7b84c44ca9957cd0a965984a3e74e52fc0739df83fe2f7ce684cfc3a159c3705f9fe3df42874b67b46f19d09a2fa38fb837fe9e009af16cd70da25786bb3b1c6ed5d83889b844047b9bc38280cb4edf4910eb93ebafc240e36f84cc616b4477b16d07f6a7a320401d104e94aca1a2a29592018552869c5754e8486067c12b790451405dc768be23951a73811131acaff94dab0331f606bf535f3b9674324664567e5d0f17b6a8b9b3079269066696c9652356e43a951eaf6eee5d106611d86c1bd3bef8505961580d2cb1b7d9443021fddcac5404cc4490867ca4cc2c560bba08926365d559aa8af168220c879c10592d7777eca1f205f7a4dcf053cf617e27d02176e28500ed3ee282c88fc154a3cc09d37cb57f5b8d846149e854b9bebd18520b8110eeaff0a250cbc0fe9a60f9af451cb5c30aa52da976299cb1b54b3b34d7b4cac32f13c0673f0db59ee3f643a77fc626e8720e46b542e30ed80f1d77333019631a37ca09f74c3405aef9de9e9a8d604b59037f33a390df93a96e6d3e06357d264127c20eb0a0481334bde2e65bf7e730ba944e608fc30d31c4186eb23d228a7e88f626efc0af871e442fa0a8325020550c96a3c63d905f3e9efe6051706429e426a0615410cb3f0f705e3c4b1d511b6cb481f72cecb94822b05f3883e3a4cd25bad12e5321de6dda4be6ff30e09a251840421fea642f3438a36b2175a6844501853fb16238b22587afa34716aa7894d43591e5da27c3f7d52a8159925a35f410c20dc5489281f8e3ddd2b281f5e45a6cfb5813d6cc6a2a73284bbf932289de2d3345e6aa09ce002ca24a6695b509a15a503f90b969bc4ced4bd12d2626a2230266ff3bd13191fab13622a266ac47f0274e44d9c24bf2ba81bad2ba8771a430c501c400017a0f3ec7215d4befca987a82e8f9e69cf5e708e8ac8cd5ab2e5267bcb2de59652a624654206a1067906347a078852f1d619770251eacb98b3dc76c500b95f7e3cb1058333faa7a8c35f9e99f348cf36a525ec303b3cf8c1edeeee08379e172819737c24635bd15d83c44c86438e3082331c724410b210dd79a5430a11849792234f77fd0f02b2c4abbbfaf22bed68a44713074f1f3e2ec8338120a42c22cbe8a35578f8c8a69823b520a47c3ab1c941ab89ba931f67fcd86361017378d8e4f830d7ea61ec7b17c81c0cf4e2e3a8df75074120d0e6c508e260ac0a26816386b042981aa10d5ee46ec5c1d8af5fc5e8f7aba7d11e8a72a08f980fd0856e304f93c7ff31f98bb1c905da60ec7bda83beb117f9f2a507e78cf0bf14ab0113b3c84fe4f810257b932ff9ecafc95662b27935e458c23d8c7d0f9401efc13977ae07f612f40edaee7b35640f67d8c5995eb8f8df0dc6e29670ee087f7fcbbdc7c326579c6ff33d081ff5534e0702d84eca4bf797efa3fe1ede037b0f0e571c681e717e8844118514d4b23e63bd31c6ebbad77561d867b5d6eb5db8f0cbda5aed7facfdacbe63b5d6ead4a274ba8d350bb988c25a6badfd60570d7d5c605756af5ab18b5a944eb7b15e51d86a69a012936a624b2e9066d5fbb92ecc5a6badcdacb5d65a2cc3322cbbae0cbbea5fd56fdcddddaddf68a3750c756ffdfe18a845e9745bab8dc2da6aaf7597d6d62cc33ed7753d76718239f0bbaeebc2febaaeebbaaeebba2e17978bebba42d77b2efeba5c84acb5b6fe95c5adb3bb8cd1a54bdf5e60621679baac5ca499517a123e98b5d64661adb51fcc85777d7551affa59fdabc6cdf3b5d5da18ac85c1dabff1fa8d0ba949b0d6de6aefb5d53ab566bdb5c616f8805aaf7a71f586ebd2b736e8d29e6bc530bb0259607677771c5f71cb350779899b788b77f76f615202d22c77777bb975776cdfdfcbdeb1cf7b963df6d6fea762977feefbbd975f777777fbeeeeeeeeee8e5d7e3177bf28a594524a29a594524a69ad9f5b29cd1e074c532f0cab15abd927c36eaa0e46f21d867daeaf7faf7a23cada24d85aaf7a2f87e3aab5d66a6b96d5fa57bdb2ac7e3678d9acdaac7e2e7b69c06c5cd65a6bed752b36eb556badd5d6bbc12ce55b9b0d64adf43d7fea9307ddf1c495f063fc8ef54f72ec2845594e23587e0f3e8a2d35ce7017ad64297bf8614e2c8228fd5efc783cf28ca5196511aa9657edeeb65662726cb11f7fde007b31c7a71d73e82e461f79964aee18b76891bd599240a6113c3b005986c0b3145bec57b348aee613b2fa49a344a6b5c26cd49f46545f7d08a609600cb9e24a12486c1b595f7a9a66660c364bdd55df3fa969962a6b7fd6985c394f7a8433fa53727c29e77bf1ca6d56cbe2a457e7ac5ecaf48f29aa4a86c65f90ab77203ed32ab37dc681380d64813666c9ad69ca38edb9a440535578e553441591ec3df8bd10326c81d577efb03b444a294fbe6fef67d97f68e08309fb25f84bfaadcf5bf92dbfa5bbf514499e9fcdc774b2f5fed6676f9980f8b849e8d050c36537983fafbffe3e1825653ecf6f567c278b28d7388ccb388e7b8b835c7fd6e2224db69d00604bf1faebcf6fe40acea1b7429c8b36525be860138eb3deabd87776effdcf45fd4994737fa5efbdf73ebc7e73efbd97c36edcecdefbc1d9e77233603c7c62f6624dc66dd88dfb560688dde8afe1da2de6ea42204af5d77643c4810051aa771fc019d5a738095629295812d8bf71bcd844fc503c530654abcc1d9e18301bb2562caaaf44ac9957c61d5d73abfeaa11fdc8d50351c28f8033aaef106497c9d54b1f268fc9d57b8a9752fff5d79f67af8abe7a0f72fddaa4feea47d8e237714665fd10327f1e26cb9f0d66cc5afbb8e4641f36fcece7d02ad8db966cdf00d07629df89de5adbd95a5bf23360374aacb5b83fedf1dbf74adec6c07828e1868859e3bc98f157bddf44dcd97a7bd2dddddddddddd2ffdd946f06519190e1901907c65d41427b9901e55ef3b9ee346bc8813b1bafbdd63c06e34d7eda5ecb41122cd45b964ab58287693d637b9481323d6442530bfb18ce004967df3b8d7015c5b17e9ae3f24f84dad6af5b5cae13defc9d53b15ee93a24d7b6e8556e95cbde7484e3bb98a393a222355f5f5f3d67b3648ae60090703e3c1729f87580d1c29d7853b1d135129976f9e6263134385a1daf8c805b87cf36c73e34d13d9a6559c8b81dd90df361ee40606664372b2eb77fd26a222ca6f208b6cc27570b82ac450946f44b9f557d6aed5e2faefd7fa5d7115e117051c7cbcc9ecd80d8d9319560347ed6e729e79e659e6f1739a5a6323baf9cca6c3ac6f71e1ad73b6b54d108812bf1d7cdfd73a523c211a820ff2c4d13c1977b39f6da35daf9bc8b383c42f7ef18b9f8d8d8d4de7f4e9ba6de3a3d94f80a3f98d6d30156a16707b21c57be4f93ea8c88dea1dacc6fdd00e9e97c391fd8e98a455ee835a25e31ad59d6f3ab2f76e29ec7437e3cd0e9cb36f1b2041444ae499b100cef0a20a200a16812b20aae4e3457b842fe627e6bb758e44208bc5d5b0dc8e38a794380c68e2c213e94e699b8054dfd5b34859c5e4b8a37eb01b514a29ab2c73abacbc727797ee3cd4e43e797878f23482e71478624bffa441e80e24354dea23cf0f7683be7c0cb341e94b6a7f5a0149704e2ccc2ee439dd6bec563db67fbc940493a92560ffea250fadb62ce6a4146b53fb7099c9639a99d57bae825c7150a6551ab2401b55c533e7a411ce58407fd5557dfbb7da20b5accad68af660201c91e28814593449b97925b127ca33461ddc1b90769e092229f01319fa10723800246188f1e0f93611680508610c1583a4f99576f28c0c87c8fcc8f55a976559280873f4803a26d545c424f3fbbe2fa64a4c44fd635281430313894234251ac7ccfc803d397678737ea92883695251488e3ec05fbfea20c2acc981440d9c9fbf80e1613cf4b99327e957debb14afb527e70b52caf718a852611c0444e1d30e9044034600aa682f5f07a882bdec20697e7034ff618511ca3f812a2015eba594f286ac52ca1c53e214adf5939ef2fa77c69493e6aca999187b6b03a570700d0cce1389309e3579fe24125b669238633e913c5f7e4e8a224e7e9247a69c44639a9c94830727c5c9d7b6eab14d933533496c9144e28cf933499e9fa79cc7f47d9fc96459343ee340a4f5d806721e1f4d3963aac1ce834d5e5383fd9552033dfdab6da00ac3340ad2aaf9d52e4196eca70eff61010ac01cb1c57fc419f3659030c50f78298b18033b1801d8c9d7361d6027838023e9a41de0487e2c8952185694c87972ac0db49dc04e3e7d6972520747f267852322382ba614286ba0ac8144f2fc29359056611848ab60685f417f351a25349f81597b1aed40208bf6f33d09e833511082f21d9cdc06d3be665fd31e34af6c4300ccd9df14c6311593442f34903e418ba07b80de8b29a83de827b600d0a663623c68afbd85492c06612e477799168174373f4916b7005970d8d7deeb12e8b3b7313bb600d06bda8360b63c80bebf36d250685c1719426698d893edd3e89015b01753494a8ddd88200e26669010822891b50c851044266b2f65aec830f3c83246f249864390b8c9a0a734f071ab2bd3b61d10a50270347f444104a81e797a2107c4a20326c7e3bf1007ac3dd43889b50067af618f3510f6208c9d642c09ac7dd5b86baf6c8b59db21b37d07a2bdf6f22966437ba92d407bf9a04d528047ee0ff43ba2f478646f7e9ff26c8ac9076db5a672f8314545709dfd9ea3604e09a18043ba94ef448f45b84773a5bffee677918e29e7409c17801af7ba26151866afbffea6c7009222dcd1395aa5f324f2c207da0ef2b401cdec69538c910f35214e622ac29e1c90c562d5cf6f7ef39b9ffdd80db3db65b76b37cb6ed66ed56e95dda8dda6b55643fd111114d80908bf32c04aac71411708048ad3489e3cd4e03333333e93031675a7a0f6a0970ff26cf6e627b959a4bbf9f30b69017bb24632d12a9e7f9005ff7c595393a712f8e4fb91e7d73e5ae5f3f363922918d1e23c71c64c5defa4fe1cc47958249289c95d864390d0c9173684517ea611ecc5549e71d2fc7c34bf763845b482d344720f412b0259608681a3f9700bb588543c22157f7c45ba2b90ece818c1e93e9c9594dba2450b8c871a32f672be0e1065821eb4ede86efa0c13f9abd14857e48988a2903c6782d074ddcf9f73f67c3920cafc3b4f18d4d3dfd645a6c0defcf29c1fe7cf6f16b13fe7472e640149d3e4a32ec0546c91269a14cc20f1011008443fa56701f441a0eab3bfa0ad2a31c9a10271b0bb122e8321bf7af9da0643be263f1d13bb5143063d8db608e817480aa980e4c7903d88d302925afc0e88321fb4e5483929c455008ee6bb6861c2b2074b3227d939af5390e797bc077afb17e3c1580b70cc1eec01e9c05ff2d73793949c263d265c8eee4e72c9069323831e4b22c70e9016b3dc21337e1db4d7be6236e46b115b807ced21261dc7c7c649c61e7beba7c9247f9a4c756629f1d737c110e0f971ad1043fa5e00f27db9690fda225643ee648df34e8438c938e32dcb51028128f5b38cb74fea93f18619c132de2e9b2be3edba8f9bf166f158196f9688cd78ab4d5333deaa2455c61b3dd18cb71904a254bcc92883371702a2d40f55f572cb40f5f9cc9f5555555c0ffad3c2aabf16b576ca1ef2edf7a0d5f7b0f46be5d4a7cb8f1c903ce2a4495c784205eee101ad12b3bc0f3f2f7f4a036449808cd500479cc459e69043d9af5ff8fdf1dfb9d17f06c43cb9eb09919231870366560f33ade67b1917dfeb57fbf55ac27acc7aba793de4cbdecb1d517d8f5aabea6b05eff7b0d7fee4be3b8bb3b5568ce5fc94eee0fb0a444d1eeed5ce85271e69f495dd8d2500e6abbb282801e792dec98a8fe4c38e2eb11ab357208aacf102dc0fdd71918b5c15178023f940e04021a70aa183bb4884064e30005b4e2e3083452b90c5b3fc6ed61309802c25dd4112a80e19afee68a415a2d0b74164423b92aa03014477f41f9083bd1a65e28f56f1a71f67704c21c40460cb0f7106ad4f7f85be8721a52ba81f62cb911c3f95d3f32d21c7f7e01499564084b880e7cb481363d9f956f53d6845e95bf57b54b5b2d6fd1ef55a5fad65ddfa1ab7ba55d49bdbbcbbbbbdddbbe2217b0a21420e6a865053e36ff95b7fbd356972fcab03b3655996f5d7ba2cdfb27f9763c4ee1676c32daf51801cdfeb25e4685931c624f4ba3f1090255afe9fed6e741371571170f6579ca9134311e633115adc0f22c8595ccf0fdd599f715d8aa9ee6ad0cc70be29a03b7fee82d039699236f8cba64c72aa3ee53819330259b40ff49e94a2e441a0c736af04c4c91b0dca1b38258e90cf531e0151a295599cc96bc5d845fedf65d2741202eef95a857251f6e074178574276f4c7102812cd58ba64c8df2e6460a9138d61254a74994fb13bfc291589ceb90d1a58fee2c8b83d20451228eebe3cbaf55b222f58a24d6fb4b2f64d06b1b4c96b5879b07e234ce3febb5cdb3de3f1eac21e6f6b9da74c46c563eba8bef395ee473a6e308c802821865aa04720530e8719274028ee2479fa51cab98a61cff62f28328f1a70cb0cf5cd12a4ce4386dacb73818cfd5d72ad6c7f722ad9273adcdb7e855e4e83ad247acde92264b9a2c69b2744e32e64f44314c3ae392c454c55e3cfba8d3a7f86df2cee66b443433a622574420d75b31640576ae8a973f0da7d1117bc1e15ceb703b62be3ece4420b979b646d25dacfa14fbd45dfc527757498eff816ca57467fde5d6e5d73521f4bf2e6bd30102a2f8b7f52736c42cb27c6bfe8db83bcd86ee34ece3178bc49cd892439c115fb4c5e82ed2e8ab874d01ddc10e40ae3e572e42a2938fe2578f5b25fbf8269005fbe8a72a451de23cec82f34417e73d8c2d2ede22215f5fdf057c225fdf62bbde64bbfe8392afbf35e4ebfde4a4fb1f8dbb4637db9e874dbee675dd8dfe6733823e113c5ce84becc5daaeff7010f3a17fc817771f66cb88fb44f070b93f378fc7f577a394be5b9ba781127d883500bad0a7d1f2ed76f20ee89899fa356df097db49feee260d07228f796db0a2ca9a15d02ad6ff000411ad023f9a5a853e8425df035402fa7c09fe1e19ce40d99768df036b20fc2526df4333d1405f02babee4413964f829de0dd0a590a15791213c924b36d0a6659ff817f6d7c70dbbae18638cd7f5f1af18af2bde2b7bb8fdeaea8a233a478ee81c39724487094c90623e923c7dc8d81fc893271009fbe372031902d827e83dc853f21e44e54008b1cf7b1027c3ae41528a8fe65f7f18ebf5abc27cb5ad6d36cf575e975e524a77df74ecc02e372ccbde4b90e4f9f74e28f029e64cedbd0845fe2e76237bf9b58a960359a08d0cc7fd1d31e397ef4d7026c016cc93549c8a49ba2b75375f82b036f16d889a3fef037943a110be1abb11446e9d21e6a7ee3e51802c43e07e026cf1c720cb277edad5ae0b897d7645999c6d034a7152e7008ee69fa88022c7896f084e1348982e0c51dbb48dcde79361373eef1fccc6e7f3b95bbcb688cab60878be2eae9946f2a351eeae92fd77a2f793e3b6911f0e4294d38741da8bfa39e88ba73c2fe745548c429edfee1807630899fd61b2ec1c746e081b5c45182572944c04ba13e1ad77bac3a98d6a947ffef99f4439f8f3d3dde2107c90cada67255bec89468028f3a31370469116ad648dea2e95632a85b54d280ad8f3ef142bc6a20ff5d509c3756d318384e4f92ebfc66df2bcf124e08cf978eb1d170041695480a3f93764af512e82f7026e148c9fbce174fa24a22bb023693fe588f22af2a95532ee83ddc8de4fad02df751c0559a08dccbda26afdd820a95760514c77b798af2de66c8b40cc1a3601389a1f65e015729cf96c9eef0c97903c7f86197ae2007fd88b9f5c4709387ebfe755400d05593e7fbd17b969aaaf34d9cfec29973d88cb1e735ecb647f39af53f0c9fe9369dc27d409a42b3cb694b21b5a4bc0b40ad8833c197b815633e94e723427067591b017c971c80c6874137aa23a898b0ae160d795c527bb6e0fc648806f0fe8003138250a903bf45071bda0cafc86247f906f5769bb2d442f2a095dbec1b0a5985a3275d75d4d2975a5983672a5e09281a38edf7f99009276f82e16e47e938bca57241289442f2edf60d0f7e7e172af1fddf17099f3a22e99ee66baeb95ed07c78060ef92b964ae1f48b828c8a2e5f877a72f9226e1b8031cf5c7b701495749250931849321d68092d294534e09c25ea6eceeee06693062c420c30c3f38116e599585bd4c4e012fd1c27cb02e40f8a877709273d71502f6684d0da593d6d81ec812678c716e5e7da151464e39e56c81bdc8b99960587c0c7b99ef1b0f978b613e58175ae3a3beae98ee5a08b426778c2ef292c94e222c60f9de25937bbac8cdab2f710601f7ef401f4e4e8405966f82bdcc195404a104adab8fe0241872c6860a372efd840904342e30c327a63082e3d24804e902ca85b40426705c88582132e4c68503d204342eb071f04c483f4822e57243415e18c1e6a6e4d2790813a50a5986f02e9023b7c3b38428f165073a003316b3cf946105f4698cc6943ac5f4a702f0dca274ce96aa4b36a9ceee56e07f218410c61863f4ee767739318a04094592512408983ab8730b38e212f66a4be04a1999176a05f664cf0bb5023b4ef3344fbc8937915a8161bb9cb4aa1f7a73e18927434f762f345355a1991090d01732125242c3090509d1846e4238a122559dd56f4f0ecdcc7faf1dbe8673796ab555165f28e5592511d97e68e64d96bb3bdd5df9a2c7a1995883660dc70697c749b562ace1d49059c37902f62eea562d4334d97ffa35b98bda6995bb1383070629c0c093dd3f3413a26971440b53f6a754fb2e4ff6af349caacea97d3e4017ed3a66c373e5ee4e773e39ab05d8bba8d0ccc41ae02e960b6eb27b591339bbc9fe97c749f4fd6f8f93e4bb0b0c5564ff09032afb5733898abd4ce812a2a9a1991560efa21aa2c4776f03d546d89fe640eef75b75843e3954ab446e899d5669ee44cad03371086e4b89ca5cb892294c0fd04755097b29b16716d8e3be80a70c191933b172f77a77e48e949727d39f17c9cbdde98e6e3096b8de7af997ca284246077c94ea6184e3233afa7c7a017b239c4c3d1a4ea19a518a869393a28c9203e45bb30bd80bd5643f0247ae07ab91e5aec11e87d393e973384e8a4f9fa3cf39c1e538e9a2381c8cca1ec7e355d5d5fc5c94cbb590a593b48afd8bf211b5db65025c8365d0e48af364cc647af211851c9e5cc08dc1c4e8c91802e6c7dd704d7042be894cff463bc5e87192f5a394cd9e8c52c47cb02ed6fbd61a14b0d38c645068272d676e017b9729d3f7fbe17093290e36456879b32e523bddada765f9005daced32f9887e6f39997e0a7bda295304c0c42c32f6f38328fda1174015aea73b8a05d8d1f7424a64afc50bee7b2d683454a6afdd922b6281abbf1d44c9349a465f186304eca035e1183fc6da6dc264c464fabd83fb3d5ff700f646a9512ab6741199ca28651aa3c794e95f139318234c3a93ebeeee97105d0bf92d640becc5b9ebe27211b38b502886e6a77647fbc59b81baa39427a354613e5897ebb99e3ec261813eb547b07bc2b68b3224d3f7423599bea3e0788c9e4c3f5e38349c9c74bd2593e9df53a6df8076b1a829d3af3232fd4a0099a6648a724761d59b81caf4bd19272a3c194564faa354ab54f0bd2a7b349cb0a985d277957c44ff03d9934564fa5ee644a6de552a39493efdab7d995ea54c47291fd1a7d12f5c803d1a4ef9cb4eab2c656a47b0bb7647bf8328f4abad7e7592484469d0e98ece2c601a50349c46a94cbf93e0f81e0da796d2fc24b9ca5aab5ac9b8b9845d3f5c62ce58f21b5ce8024fbc904df69fc29a22c61863bc74e82e8a9b5681903201a990ec14e73a027bb4c648a65040123d011ce50092beca4e95a02738b2e244f694211e6d22fb4312f4e809b2c708037607b91dba732c3e5226baf3214017608fd6d8501b9b9a09a30bccde85f10b21183be96148618fd6d09a5661821681b4c65626b4a6f2e8976324be4775b27b74277bf42892ec5fb76b939b8e18b2b135b85e6980fd3dd91db7086bb7e0e21660f950bb2e2bd90fa3aa68777777777753d91e73e44e9698ff4e9a9c6c7fc18527f89a57c03fa474837c539586946026a68f7069869400052f1658e4db8385842e1b74b1d57b2bd6d65fc9c13f081dfca14324b5bac11fba3477d2dd12f2df6374e748e23b8fe83e840499fc1bdcddddfdbabbbbfb4f7fe872736f28dcf8714b00ccd72c02bb4793ec28bda1701f0827e570fbeed8bb5737402640f61a2a77ffeacee3ef671c0e982aabcd889b36a824ed76953d0e19cd080000d314000030180c860483d1702c902371f914000d808c4872561f8ae328ca519843c6180280010400000c000008cc8c0200ea0178ab8dd1d8d0ff6a2a4a0c20246905e98da5d6af0f49af9d1d600e474f0a6b683e6f38aa26ed06104b9c101fa922708987456e01c3e296ca9e7d690781554c6150c936b9b7aa343fec57748ea8b0e3e6c2d6b9322567c60c487976e60968ab8fe6fb81bf5e046b1100a2e770ea6b4ef7a071d0d5c8cbfa3134584fcb31cede45f784646e26e961b6efdb570c4a996b0bc81e340d0934098a2e600a03ea3ab7db93edf683bb8637a1b6a086cb9c2f1a5296a1788ccbc30e4dc91d2cf8728f522f38fc7cf4491f882c3a21bdce1e251f5e92f05d20fabe2951f6b153e9f1d0f35947def349e393e17e0288d4766cf7f1d0cf46bd946e49d1809368bd86db451c3d18351c29999fc4a7bbcb4187c30722469afa697bb3506c6ff8df21be74896bd3923c8d82c7d0dbcc98e1dc87c73a8b490b748df167393a44b622425f2f9f06c8af95ff16af0875684bc821b7910ab4f5e8272e5df8a52efceca2d629fb4027dd168672c08c0ad87839a603dd27379c970d5879e505985ceadf5728982b5b56cafa7e1b6927b91386e2cc2e7c41e720456f97f6d2fcdc8f911de33dd392a683124b47e09c0e2de68939ed8846cad52708854f5121ee3a51eeec1dd4cd4f0cf6bb2866658ba0b14ea71f3c57e252a6bebd5d18048e8f6989029119e5592314c6d7f544953493cce23470a55c7b3a8330403bb16ea262504f81a13878ae07d5d83780aecb1edf41bb7ecf3ef0b60a1944382b52f1462fa73de581ba14ff40d1fb5c984c827e0c6a59c65b29ef2c6231e483ffa79ca7b9434b5f62a381f2e3c8b7fb304d54ca0d98c405d5e0e208ff393e94f2d234b9e374a8ec0d8c7534e4dd60b8403f862e89d7b4908e3e8c5a10cd0f8dcb7ceed4c01f73a3958a5e12a7130a343bd42e41d60e3fab21a299cf8a019fc14143416359c20a665e30368de6a7eeb0280a41c26b6c38027c9fd5b93564fb67fd9e396025822da55951bfd886fdd1550a8661388ae839b887b18e22a23cfdf771cfdf8018fa0be486b0161ae5c5aa349605a9a86622954f7d60b5484c26c2bac1efe36caf2c0b5cda0fba33ae4676007ea20c343564ae9f784401dad715e682c3b071ec611c569b8037738c6d6333c3517f143e0fb38ae16b1906632ebe96d760b2b395133a6564ca6b8b55f4632e93b34ce1973155ee72e07bd8381f03dd3ec22aa776c91ef787861b8e93b66081b2c95ab38a541da4d1ff4a098c59a64aac6819babfa98b9656d710e7398952d35cc8dfd07dbd4a535f2b8fd86c4eb0d2a21ad6eb3f13fafb9152f4ac5c696a32ad52087bb00aaabd47e934f40cb98c5f5fa26036c3c519c738f6439b743c21cbf32cf742cce85d98bb21d663e3a8d57004609ad5fefda25d5f3876a9bd06657208ea39ca408321d0aeb1a19e3febf389a54894f4ba42ed14284612bb4f3dcd1c0b8d3339ab0ab13228bb500efe13e792b12c2c91590cce9722be220587ed96f3e0ad76be841f16441a712d571e31cee781df62cdaec5155519d166ad8ca69a32c9098bf630a32dc71c7c4a84a5950c0a053e377144f2bfbac829b04d9c8fdb1543643c6f47d5c1027d7242151c820edb079024d826c139638fbbadbb00b269d0ec74808b7d3b306d7bd221406622cd82c80088102512cee69117e35b5cf5d83c3eab04cb5e19688fd72ae48fa76086eb57d1bc492fcc43f1ad0817025788a265cba568fa79bd163ad055fcf1e4ef060e96817ed3fe106ceb16c5168256b0d0490d33c124d704ea496e7a74ebc46c148869176fe2d8978c36d1784a657a672b8c568332229175306734816c460e5d93407a96230e2af72617fc2c388458a2faf5d72d67418fcd53febc9c928d52809ec1d28b6142aae49fe0db9aecdd77b84a90bda3a30749568d2703d6014c3f34dad5ea8142ab2686e9019fe244c58a1ea048e8e1f002df5255f7f37994590eb0ec46b72770dc474729f749d34101cb0578b9d688b52234653d67989c22f641adcacc3d0c103a1562038f285d3c49ad304d91c691196094d87bc2610b9bfcab7bb184fdd36f985e622c7c05ab448de7deb530b5bc0d6f9497916b79043a4174a90b414fecb0baaa71e611f4438aafb7c0a5ec606ae38812a065d23d01b95c23f109c8bc51e8498f5b982c297df24883b35ad86c26be3e501f2ffbca632d7629acb4ab1fb48b69715121bc870424423442950247cf6179746c710d4fde2a9b34a7a286e2c45276b363f6d63a7140adcc5cf622cc65c6be3b7821d429ee040b83d9ddfd1128174eec14e04ae14630e39c99cadcdcce48366174648ed79a2a3977b51b7c3ad110d4776a9a9ee3eabc5cc40e982bf65f4c7997af2a9040ed3312fe2de9918bbedd00b4d97fe3c60aa83986b0c3895d7912293d473415ffd0f6783bdfd1a1c781b978d6796459f0d1d84b4f8fb9e384830f95aff169b6131b6e55223d687a1fb5c727270845437ced053eeee5b358d34d7b4fc5a30c43d01e919e7460acebabf2f0b1d3427d68a47f10d64e2b3654c64d1b184e59c2a3039fca652b8e88da95733032469adf353203249dc489e7a279e6616d2fe15c5e618d089e14f26a6f892c42e8bd0dd21e80da1f2f4d7ceb44346b54c9a6ae3618fef507314ce337457bc6077aaa93eb01efe61280ef584a89a58b4026c8fb766ed18665c25455bcd474c39e8a89992d6886702e257825752118dd216cd14b21e5295da0e105a39667d1049137ec3066f7519f46a07080dd43bafcd93a709a6eee84e3536623e355491e44e00558cbbe14322703b9dbc4e60d51bbdf5d39f8af82900de660ef9bf9bd99b3ede1e9f27f32f4145bcec46a5ac5b71fe889bac156fc73745936b11ebc40e4d5cb0881a2e500e5fa2f72c1c20231feb52018fb77114cf4540f9207bbe90361d7a4c488c380be1d32ebf2a3f3b0203ed7c30d35c2e5a3570c0bf3566865973fad9a9a582cb5aa0d9121c14b5001de33aebf8e4821ecf34ed22d4cfc44f5a7eded5886df1426e71b503f5515830c7540ecec201c2d673fd174c38b194266806600ecab3e89e203fe12fb4d271683583c2ee3fbbc23e84417cde508b3e30caaadad72ea090be4964957ff9bd3d8edeb737c9f4f1419189c7d5f2c00fce1d16b2399033f0be688ba67576617fdd5cc703cc5cb95f3974bf5f672d3349c82e6180faaa587e7d31c6abc54cd9c151d88299eec1cf9bae29ee218a0a608dec311322eeb1414e372a6d4c994e12e8027f5d3b2eb36f4b008f535d74c4f47b1bfbcb9a44ffda03edbd07baf99f58d5eb2d93210d7ecc1dd0bd4d1067acaae8284cfe3c25cc36417c42cb658fb80af592dd25dd8e586a3b1202c099a61dfdb1a48f97264c82d42262e720306c4c4da4e3c66a725ea337b710f3dddf1f485df98a764ecfe27fbe98c21f5e3dddec887febc90a13542b2b3701b82116d67640aae88c273718a4e01f3a4d811989c55fb664a5002012125bbefd1af85e14231a362efe8c7eb2a5f9d166d8f0a2378e7c70cd960fec198ec94f2af1a16e8a43fd9a34f5abd61fcb9ed3d9f4f7a65e2512364316c4aa045781101cd604cc490ef042d5a41e1c0f549224a1cf3dafc4d9a247351e9f4138fa2c6f0c7e7638acdeb38829ad53183c77483983d7324fceea6e827c3e1c9ce136ea8332576d6935ed7fd05c241c2137ec164ccd025b3471c18b26313fcd0016296668c91595bcaf6fb17e5dd60a6039e45ef3298cece65124de31fe97cfbbbea2cc96fa8e48b4a49951de9aa5298cc56b198315a169fb342b77279f557e99104a15c673fdaa8e1c365d8ad4b377b55ab721a399a7d3cc63920c89210029d9dffac442bbb3a71ce5fe3cd2cf5530b999cabf53786040604dc94037924c7898d84df69759be1630ac18b54afe2bfe22087fd1acb3bc8fd40eced12d9acf9de6e8a3210b50ca8f504683081d01099d63760bf5da65d32df1c07c5a1eaa69458201cf34d0070fabf3a6d16b446e8ce061bf28e6e8c8fa2d565066bc1da9fd85067f5453ecede99dd90a1a5859f5714b48af46c58f4ccbf6b055a3a29858a5a66925a92f219ddc9c5070641532c2362f0a2b1d76a9b905d193b6869a165a7edbcca5ff799331c58a8ec4bca82de6854e0c6bb5da393ae1ac4fecf6cc938c84abcd3c601f8839e763dd5ccef90820c03de0be8ca9bf578559e51c1770962b62c5fb2ac0069a3f0a5b54f62922ff679f5cb9784142eb527ff4294aedafa876dcbd28e768c03be6c50798fce10804ecc53a60707fc9b0ecb288e91add32792a33a193ffb48410e93f33090e129364259df4537d2cb97ec68ed634eaa92d6defb2e8bcf0fb9f299d166033837c9fc984195b40146291c0dbbb9f52bcab5338b4b2ed8d419d4888ea91a67063a2c4a63e00a0c402ec801661c72651d070018d0b6fdf77006048b9f669306f53fcb4cc0160d62580784d7a59c99af028d462322c53ee1926cb44b0df7b60b01dde8c278ef17792f6b56638301fb2b00497a54ac4257c66fc7200d5258910f21978e1d11416d52bb4b8e5bfd0aa9384377bbfc84d483b8100b22a95e22a198d258e275edc65018b735412b488b89b8e2e23a33c3b65cfbb410d2adcfdde978a41021367cb18f60a73e24c4f695a7e3d7f395738f2632ca62cffe43fbc698dbcd23834788cca064441a14750374d225abc276a9410b0e4e20ddb8c2740f5be55061d045e6709ee3152ece0d7b52f84355d4a18da0d00d1d00cdf1ac7e56724602b4e978b6864723473b0b7bbc44c44ad8024071a3913db59747f5695df4633dc679508fc464473a992acb66c30effdfe118aee6e17191a81fbf2258673f584f6e547428f8dc142831311b62a9fa17aee3270502eb09716881b26032270088cd85951fc8cac40a4ab5384bfb21484a2e82cdfcda02600d0bc642ebf79649f90a479aeeb805224b15cb01c8f57bb34da33119c58ecd5ec34c3967fc9df8de8683b2a99ccc55b9d7999d2c6e085c1136a2945a94538dcef0bbb0d686c4efa22d879d2560eb9d7876759fd24f29a20f6c95be0f0524253b0102ec4181c1c3d864dd248360abccf23bdca859109af95d27f831ac8a156b055bd975c517df29a2e8b3ed00985fcfee45707a746e7027e60f13cf5d18708f0d647ba99777370247601cc618d7881a828d8283c2997a15dfe46218c45dec94415bdcbba9bd59f4292d1be96ad2bb77d1536631c8078b48ab34e8de4d332120f828dea5e76a434929a9c3c111b238b08c3c099fabb1ae4d74af0cb152aff686149fabe10c69c581b9c345fa309193f76820e0971835d8b6b2e8d9398e36d1926227cacaf523f143b2f25f08a948eaac5de2b6d490a9c6845b26bbde3304573f7a1c2482ea47d37059c3db5116cad424cef08b9bcdf9efbeb78cf9f4ed0988837d9700cefadc737927a5429a86c1cff04dbb03b27f1e51a7ced613407f4d74eb025ba4810ea29c33608425c1db89ea2d260f1df95c2b22d1e4a89c237a8ed805e91b81ea2c3b33f9a60808f993fd9de3f091b4ffac06ae35049cc31a4a96dd0d764a451abf10c80704319d46b7996fefaecd67d16feee822422902043b5f67dbac3cb7682806cb22ed2a3a2121b551dd6fce0b6747ee5cea4982edbcbcf0a3568b7b8125b2e263e6660616f6aa92c5cfa468b01108e83fa86f59adc0c9f0d3d260a8392bb2f5e7f0a423d99d56bfa1533c6a4f9a33ee153106eb28c9e3e6db478e288f568183a4a0f9e087cc4186ec9c88eec76e62799adfd438c3a690296036f4295dd5918e18444ad94c99fb191fdbaec6b94d971d0dbcd6fddf9a43a84b0f72e0359bc482f5676d885b3a739cd23fb2c7d536c61264a245981c4766f8bbce9dbe4672baf1b8c9df7509ef4822d4a49904e568aa4c407866881519b934369f7e5564fafb22372ed005d0afa05820794ed19753c5b5537bc0cbe6464c82959ebfc64f1f36e1bb941eaa140ab5cf565b56d452a5eae92a89db6627184d4a15890ea5c664b55c3d95a2f4a0169665999e8b4ac9ca97821713954e29c89e4fbd12c69f5f2f493a77810b066c9d082b2c84e2c37ece1b0858fc138f5e2ec969be1d121548130c7b6db49e57892549f0b130b86e9d063991306c12455034bf8bf4036875fa6332995e4bdb337b3514fefb062350f3b4043275a0f34d1559041d01d7bb8a7d8d12a55b31286de1e69ce80a04342829e06bbb8ab1b26757b0db968938477345225042aeac6c067f8add376b9f3fd514ab51435a627913e021cde2ce31b05c99dfb2e1f4bc3c51a764c2bda8383ffcbc54666d876eb7830cc2e1434cba555ec9d7b050479cfa4a0d48730ce703426fd1abf1d42c73b1c7183e311b8cc6d24e84724826eeec587fff7ca750951b2483e74ba84a9de6f1884fc0a8da6148a45edb12a1bb0c2a7d6f0abaa520281885768a151ff97d46ea2b3a8a27fc2456c89581c6d5f9909318ec8467504750654275e1eb995b05b6323e05fce282dae22ae6b436a56019390d74ac73823b6a58bf9e9a99ada1ad1a38131510590bb0294166e7473dd4007b68e37fd54f67fc09b189d19cea702c2ccd81aa307435f15690ccebf9ae290541e910b945ef5610a14940b8c8511a27cd600707686c089c21bd17bc43fd20ad57c422bde4b0088aa7212b0a48c766a1dd060731ffebe5e55c990c1ce8805a80aacc6b4811b239d606af4dd15e24336e4fc1e7b801cd168abd24dcde8061c576b0ce8292bc36c4f5a43a85e9dbb8017da3fd99033bb71abd84be1481f32911ebd20622d83046f49ac06e6be4f28cbfbebf95090298ead252909602f2aa107342f73574a7b3a28e4036062377d55f26e8e5bda0b3ac2480eb709f0d1c6812dfdff1af18fffb7a71a6b7b1dea76dfcf780337ae80d1279f62d8ea9db64ae6c80ce68cd178ba8554acaa4f838b0c3bdf0dfa2ce368215c3a5ea2bdf32ddf8569e3391459b2fd6f621083e397d4592aabbfd8e89adefbe8dafe34c1930091dc5de20bb88def08cdaa93e422005da6d1d3e01769d4bc5a87f228bfdba315d03e52c291c134687aa51c71e31b12b4651f1c50638a75ceb2f08746c57e54d0ae3f47195978f688172c8f596a2ebf1626655e3c0234e460cf15de1706821342a03370369ef1ccc615832976be63854ba2bbaba02b46a39e58a5784a2c32e3215a2e701f673e53591eda4230113e8a8403f69f0053a46b9d2f5d0ad452ca9481ba0c3544bafaad6b28d16b2663941e0da2102c1a8aca2316e656465507ed7f7e86ec95737c9834f16ebd8965450f255fd130940ee366acf9213b9f7898eeff164088aa6dc5644f700f68fd8f5d0914f2e5930f0aae033f1ca49a65918314515043adeb1294edf4e17967a341f0917c4629833974efd240c11504759c30a1a8574a646a6e7133ada624620f03ce51918f49be147554ce812d63e104d6cc5aa50050d996814078446929c2dc8e867a0178321a58d03f834cb2463517965e6a08ae3694916c649520abff8d471204ee97351f2f7ad4a0cefa429f8819363418996a371486ad469f05b9308b573f58688cf4c1419d9dd060976ef648a3ef0ced078b1ed117121d911907da4811f66bd900f4d2acba509f05f305e3090f45f5186838d10e78d4c9160277901bdc3693cc1efe93e18e0d7255c635d40c634c876fadf7087c7bdc2b491f4bd81ee5398d48718765790a85fae74c757f7e7a6c04e2e57551205ca9f74c8ec67d8b2151aa9a9cf0434f36f3f2f9cc4d41e49834f20b1e9514b15aeef4fea61778c09c3ecfaf1769ad7526b3089a7e8d8440598f44c6d4c52dca83a59232c3055fb935dc5ae1f8d3c08394e9a110a854386834a2d650a3e71f4f06ccfc6414c44be3ba5308fb024a2fab52d7b9d3807a938aea55ccd2a2b80b2230b1a9eea3a6eab2af68e73f4a7cdeabcea474de1f85eed47a30ee106bc56433f8d957c9ff3d1264fedddb8a663d0e611e0ea383cc4d478960ffa2cd1ce8e8426946c10288ea8dd5f509778d8ead4eff3cf6f3686bf08e80d8341a3325042b86944aed99094f11fc8677af4711f393f59d27b0580bb4271f577362fd44c0879e924e76e79a6c42037dcbc452aea5c8213a7f16db48ef1ba88a2f64ac90aa53f8e51804e4a25b9eb8c35c7ff4caaac472752151e746283debbd92b9f438c670cbfa0300bd2e274fd08f7b653b429a77092a0a38e44897f79c50965713aeff9decdb0f555f9bf09074e5d49a0d06f2f5ccbbaa2898df4103347c2374cb646cb3c632d12e02aa8993bfa48fc0fed5366e1e053d9112c7aa32713b62ab8b9ec8a88e2a332552f163f0ca19b67bd21cc61f276ddf4a29c027bb8bb5a8e9e43ac84e4a6c3e5464e1237fae849b365eacf908ce74d2b3734f7f32490d4e74a6d0bd10fa0960c59a682d4f2086ba8256d8da0ac00716a687a229e1d43ac15275d25611d7a385375a3d59537468e242ddeea491a7a9e6ac2dd5cd5e1fa511eae32e5c100cfc594bb2f8a106d99b4f327dd24a31dfb2ca3054c30e7f7f9f31ad40960980e0e656fa1bebdfc065486019f1ba4fd111e3aec08b28aa5b544157809a971cfc7b1162d20f8a50c852f4c7ba4e5dade20e142647ff75db64994b40978fcba98501a91cd6ce16ca5aa118f352d20465a130bb68c32ba4b6c52089dc4ddd4742e5c57caee4169297ebc94d32fcd08642fb53e23277e3a2332b160f1708d89d2733a5d15a527b868b169684da8be10ee2dae274879274052364a4fe984786117a33e7e00d2800aa2f438b51fddfccb528424a235b54842e028b290c32583bbcdd2c1ba24fbba4ce532cdc3ec9176267481e621fe258ba2a2f3b96ee262dfd6d3274f0f8ffc23e2a044164a76ba9988fd1273a8a04c1c3bbb3141492bcbfeff74f1f5aa8628f95cc36f7cd34bef990e9019856e5d7cbf89ebbb1e045a558cdd2e4a881416e7400e9d45a6a9f08ba07a38c2efb0548754138b93bb993c22a92dc74e78f41521d3efb69c1d01dd3d3e6e6663e1122777f406c7d9ebdd12998316e7f8442815d59c743b36f7506d659b456071d1dc9fc4142ac2c3ffa25d640db93b8799efb8307384d37aa38b9e1268645d207657ff4da0119fd72eb62ea42b1168c4eb8b638334f4bff0a654a051acad0f0aa3a14b5efd059ab31915c73b6444561ed959798e82811a5a371c33a321e8720d801fbea8bb2471a4bb1a5baec5d54d198de057e7ed7ddd6c6eb805ce0a30f15e91389a51dede8d123750b1f0b22552c034221ad41995611d2ad8d18cb228929ef375e7b3d9aa47653f683c7514e61a2c27d090c644ef3b4753b8eef952f02395519e1eebab6b9a288746f6af26c8062cb2d1251a5f604e049dd93d0d152328962bfccbefe5f4f1f725defe1b0a3fbe54c0af9cf1d3257ee1e45d167b95dd2bf6df635be0ec71ac17f396c3d5161334450c06d64880c02424221e4468e051f538d8f0ea13c6b1a1106eb476d446a7633d46a46e5241e0aed5495de73950a178de977e9bd1ca95dadf784115b583f0a605e8ff2ba2bbaf30d939f8543e9ab37f3a7d0f5aa5fc6cea1c458fa8e9ccb7d8d9d7060721fa3208fe42be16609191e6bbaca0959f430262a6b64e9801613e8202ce1e33e66b46e4e52333375444daff08d959c325a9f1b607eb5193c540dc80372b557e0384dfdaef5310f1c439fddfbda33fad3e0422b626aee97df5405e8c68104902b57bba2313777eef95187e036fed9a7ecf2df5fa0a26aa72f7eb3e7a8d1b8aaa17195e827edbda62633a0d01332652df9d00ae7a2de12801da01ce67c8a63fc37081501492067426608565dd63f733ff76412baff65e33c281d95cbd47318b85dd8ac5a1d97c6154e3b473a3c7049cbfc8347b02e05a9064fb8f0195c6c7643fc935fd0588372a11d66d7c78b96aadc70b1cb82f67108494ab216b30b18075f53de53ebe4462a82d697b5a3e1df1fb505046a72d8ddecc285d7ef9269d05197c1915b80b9b4fa4d83d930d6e9e7d155c3862c37642759782197ac7d32f95184a6610453133c96e523121ee22491915babfa54925d9a0ac1d3efc3c2d8886ff8095898a390d60cee8bc262e87ccdb785f970bef451b6c5b632e49c250eb9698e40c05734b13061ce48a729e3ac073219e6d9a5765442c538328d3e4e92618051644e4946e3b2540da2b4d2cf8cd771e5b037133e37e39b04efb4edbd3decbc78bae81489244c1c1be8888b225a6978a9d94a7ae07ec64fab54f2571a8311edf7e35e6aea0cb460cc562560fe44c55fc664df17d8703f9489a45a2898235c61ce78b0f57061786e01a33bfd6dcbc464b96df3ddb30a7cdcb8e36bd993b8489ec8d5de8ca9c043504c280c28728c4e04e608c7f1339aac8c45d576449642e5745c2df88d80a8b55698b4b3c4cc498b6f0e13101eb62d847a65da4de4223141316a786cea3b876a2709844e53fca4e9970ddab74b64cbe489bc2d6284feb00ceb22c9502bf561cb4361bf293bbf6c8a702f017ce611c851be69d09268ac0a136f6143dcc394a5f25b7ccd3d4404643db2c16fa433508ff64302f1e5a3fbe8a7cdac61e153c4ea6e1a7bad577bcd82e0e0b33e9284e1a89731241f60a53835fd701bc516aa15228ddee1fe3596232fdc64b3f82fb7b9889a360a476a5809003c4716f06090ede093f814df093b6ddd74df3135374479cad9cee8e7290761d16569ff4b6fac725f0d403d6ae8bc79177b473f773e76176b0dee6ee3a5313d598d0814358ee7076377e25d86e3eb643f9ee81fba2a850169d765cf9181f6b74c89f48b1318d2502b4d14c51f4c3dcd8cdf5ffb115afb225195ace4e1d423ee652c0b3bb263a4a80f1d74c9ac730f394d21984bb19375178bbc13f5dfd9628990d8bad992711c79a94a425b809004c2407a035dab8e5db792d0f52cbb460a753056a73101beb1af493b7efa03fd0ef0322a27b90808012ac1abda6fcab2e1a42c4b3235f57a9710c4b5acbdd95eade41856d4f0c879edbbd3037905d58566bfa3896ae7c42051a3541f2d23843b8ebb4335b4b5884f3f6cf955ae91c74f4cacc861370bd8afba07f02665a31add95eb2f39178c9285f0dc820540aa4510302589665c26b608077d636683853bad51f803753b72a8d913abf6863c08afd383a303550a99ced7cf93c20e10bda65acff86cc67b237cc9439c8c89a6216a3e72eb49b06ed445cc0599c3e6ff54d8634c7bf9039cf8fe22753d34dac82eda80e543007c6a44a8528649a0aa8d78549ac0e497f3a4d36703deab180064475d83768c20d51d1f45f5e130b04cbf4d75ea113cc36b26703347f60c99435dbeb847cbb734c47cf59517b0d3e6402d9dfd163b899235e441a8f23bfc6dbb1788e849327d607491083c2cab7c1a5fbf818962c0b7141140e925bf6e3c74ea18ef3f0a47d079fa244161eb4be3ca03a1240e9289683dc2077d817ca1bb4959f1d8750279ee487f8dae19d64cdd44ff8cabe4689088758a2ea08cc7d29dee8ff5a846530d0c303c8664b3cb37c017c9efc1aaced00704675ccc38c01c2288b7e2ed4190fbcec5ce4974a07aceb2e352e2e3a7996d55e59b4d84a99980d2ccbb20f4627cfeb750a1932c901734297a1925b319b44e9cf653a10769f0e87aad15cd73b52e537d9d7cda8b4b4c65f0e85535fb36a76b1ec2114ce67337cfb2505b4c807f424091bd5c4f68551cf5bcbd1cb5a536a4d580e1b86b69b565a0c82a1a6392c25031961b4b5a30e0a1f53ab1d598c8671d99a1591ac44a523dba07565bda892ff2f0ed9eeb5ce05544da43fa51fcee8f3db05f4834a230a8d7267682ebd8d17d80bedbcaee190ea20a2e82c1411bf65a4446f0464308eb507273b60c64e9a5a3636a2853a455ca2ec937732dea90e2091901432a069dae61337203bd607c455bf23ac32e2a0d77cb58ae8387e775ba6741edf34f8a314776069d21e390c120941348933410e9347073d5623898f53b361f7866b5f80612e781bfb932118c4241a30128a4cc0908ef24e8429b248a9c75f1087d22b7bba7faba779218d7353d206e131d1756b754d2debf673ea6f9a81d0a3b30180794b2cfccdc3cf2daec04abb1f572f6fd7130977273b36d04e0f14e720d0058e10085026d7914ecd7799aeb71704106cccc4b162326c9f4393405d4a55934f69978d9da631d9417a968d05536d804ed55199cc986e0606ee69d9c96d3efafd6d8e0f442f183a6ab541d9f4c5487422915fdc8ea52ea19eed62521de42b83872c1974cadb30ea1a001206faedef6ae546c1380de9fd98331e2acfe5a503191d65e9316d9e64e101b0c31c08b16f253f1da982902af9076ff8c03e94011d863103c3c285c1bb2cc0fda41805c8402345373d5da030688151090212ad2f8efdb2aa26cf20462882355ff2957a3742a244dbac3a9fc6b0461f408fba860c50821d8782ae386c4b242b676debd512db04a914f04f203ba0097e0932fd26957422780b15908510d8ae1ff00d6efe5b6a6f70b284e1513a2355f892ee9880326dd57c4c9a9aabf9b15b4143083f1f093c8d550ba1fc7941cd1dd3202253944e0649b33160f769cf7b9133adfa1280364316f160ec99e90240add1a3eff1aae59ac15b6b96345b6c02fcb2700927c7b6c489a3d3c3767973452c2056874e6478614b5c10b44bb7a8e2d83854241f74ba9e4af8cb6f72c75efa9851c165dfac6326279a5e47fe4fa130b8e37f173f1be60ec11cafa53e0d2dc4903f5b8a35e65d51b64a4886986a76c4c7c8910add8b720ed90804fb7779965e4f5b045b94df9d339190166923726b56fbabdb9d56d4dab9bd873a6047f5b3eea6b43b45aab8d2fb79e6828226887daf566f97998b4ff3ce782fd32a39d87c59330148126331116b3cf218ec6cf9c1739ef40020c922c93120a106cc781634bd33e47172533120347fc6bb16ee9365f80f283f0c0bb7a9c94ff89e40f70baef77c6def2fc133c717860702381e172eefa3d700ba25e3b1234c063f9e4c0e3c0214dde724969858936516fad1ae20e7f4d82533798381c1a743b14d4cb0bd9877b4dfa404fa9443f83ec2844183fe8a68b58dcb671b38d265c7e4afc7403849c50e2c25997fa44c9ba85a46654e60d13213d09f162d82ff4b79549d53bc38f36c549717ee4f08f29def568289bb44062ac25d2f08fb83e09d306837f0684ecd83b05b12f807f8e30f3f99aeef1dc71b4a4b1e100bd55a7fdbea049086c81d94003c08b3a437fb44de782cb7c34a2ea4e4d385c939fc87a7ba462477e91dd602ca32b17fd5ef58c1aba6f3b42d76dfa955322841f7774bb903eb2f529d2913d2d45fd633981aec9a3477361692e25cd482c7d70e2ca39d4a8f64eb4d2577b6fcec347c460f4c757057796713f8cc529261108f764fbbb454acc1d02ed588aac0b18b44211a36d6228100513b35e0836be2ebfa589429519479353d72afdda4aed1c262c66a2399dfcc10966db473005495baa7ca6fd4bd88d08de27812d77f65f5be39ff1804be30096ec9e102cc62e21d87b8d0b99811bb1d783a1ea4b8041256d5d0c7e6aa5733c9a7601e60dd6117feb93c9ba002fc4666d284428bb7a70dabb898e81f89e54e13e322e041c892a203b104050408332830e8be1393bd7e3de2d408cb5d18b9f63bc81dabd26337a066e4e44f81ad1e0d40cd97bfdd49372084f5485c18dfad870b27bb48a4ad113ac7fef19cc3b3fb13ca925bd695e61978af6acedd0a3f0c576436a8c8f1febbd8db8750ba44ac5df93c5564389be4c43ba176d60175158570bb40808fdecc61dd57d20cda214a985b4f3f3ce2740ec7a8aa47357456921da7fc9d7027937f2d71ee92b375d4e075d21097667c62a72955a846402ea30094746de52d94b02a83a46c41bd91bd5cb66327cc0d9e446c753b131f57b80102277ed5d2638332f911a9d79473046c41c3526d0fcf676b14260fa886aba327d4aa0a3239767d90b25c06130515e2ab24e5058a81bb01111a507ab72deed79c675763a803c6fe0f0ffd449576902ddfa76df6a4a0a2780e9be54d6e4b86dc513535d6c411dfbca2f6042810d495e8127e54d8dc054e8424811269c4ad5c7496e2be82e54ce9e9e37d2ad2c0509d026642cac8e9fff469646d9cdd776eb8cfb1a716c40cf884c1289c112f23192b297bfb81dfabf68d58b73f48103d1534a6f10340c4d5ddb5fa752bcf8c1b99ad06a2aa4eac5b082dbda902eec764cafd321b4be815998cad774937839ef4ad535265e2bf57fa0ef5454d1bf383500d7e4087607a23619d2acb7686d38d00289fd5db1d8f6e4005e8156ec3be95918467a90b8c366c31495f30d3e36f1cddf864431d00fd4e1ece553cbb5914b435a411ec75ad1c938ee417b496c614a52896e534dae7c876a97644d5a8668d12472d6a3e10094f015c0e8a881c3022d2c9aa816e2c2c25c91c493bea628d5563ea6d68cd0e7b10a6c7697cac328c21ede1a40c408412bc859ef0cdeaf66076679523543c934c32cf8f7c8ab89d27e95f8f58cc64d3e9b84d162a85758561cf4161b60af984f94aa5c255d63df4f630355f7dd051ef12a37750047fb1be586caf52741a45845655b88deca1c3b540f73c0e98362eff038b133624256dce749dea02cba729c5c5b0de59e042937a2db9efd76a3ce751a229a2ae5fb07780508cd2820c4bddbba16e67382066c7a8cbc0b8afb4295b1c96b4952b3d6cc55ba64494967641d84d32f9333eab5e3993f676697c39e958b094df1136d142cdd8aa603825cbc25fc2b6664332fce335dee6fb0d545de30da8976011162381f47cf13a0509aae6bd009750cd0d4fba04219a1dcb6041bcb8eb58bd651d7dcd9a6e99850ee1cb96f0babdb46bb2feefd7b497ed97913d02c82ee06647951f09646ae663a8d4f187b7f6b86b16d782a12dd9e1d48eff0f7d5a037be4b1102bc4b3ba2f04d5152079060375b002c303dec212271e6ddd7b82b333c7bf31db4dfa2e2ccf75a1d0ec386859e32ddf01c67cbd558b0eb0a6c54846261c9985c1a0fa50504ffd7727ef35f1e656b57f11d035f1d4253a13d7ce297cd023bab2c523269e2fc2139516761967ef3fd1626d4d4b99f136a613753884961b8e8715cd2a22ecfda9310835054c7a2160c9f32d2c3c8c0cc9673926647954e3200dc36dcd5d8d39d9252a4aab4c4b7c1ce5e1f4d1158cfa414f2dd0e67c546114386ce743b0311b1edd55d66efe6da558284838c421f0396db5b063234cd7acad2cefaecb43cbf807e05c3014344ba164aa7c45128dde4486651448450b52e7338f98d2cd4dcecba899e429185b046f472b167564a782db134fe2dafe110f7c74e674dc37dd661713cf1a59e7da662c55ff29a2a22a9b6936a720c6bcbac90ba4acc21216010a3f46852557199b3d9e33a0aa8b6f695f44600c0fed83b6b43c0df54ae52916d28123f6b9fdaa2ce7ca890b16598b5784a65b09afb2071a7fed5d56b699a8ffe0b01bd0ad030926ab6e84641233c81d56d342c1611e7cce41e6bac0747715924ca4bfa6506885d12fd4af85f08226b2c9e74a200267c452f3f0e293ff9d5af974f1e45897e84983d4356c1f3f397ef4d2dc90ba7be1e21542c59c7e8c99c4283653ea8b136e74d1f0a84c60d54d4c1ef0f1f56b110fb5cb7e15ab800bb79e87084f2e24dfd1251419d55a5136a4e7cf6005a146554033670c662576d6bc203721f008a40fa21e4ba51d4c1c427080a2dba47e90a6617b0aa24c952c63a6dce4da440a31d8052036663e63d156115ab727d510d735e1323b256e8ba8300ce53054da5ded048880c35354f527ad0f04c49966b1486f1c3b2cef293c722748f32211eb92ec2cd7d589d4219e2e0020a51c2968ecd3c2b40783b692cd82a4f27751946e52adfb55e7b5f8788ad078b7d8ad0c49b40445f802e38bf97f29bb200801e19407848786055ea30c18b58a50ae0d80b115b83bd63ccf68011247b048d52e1d5d465b6f44838c1b7bf0063bda0d51f666b19c7610f2eca680d2c9e1c661ba01aaa26f9bcf53780351b97d031b6502c791e50b87fb53278e52d2330ea138470e7575aa1c7265c91c826d0a98ce21e4237404543a5e57d4f1817510407957ad45d202b61e66dbd132043fc8790d8ffb7e3786866a32f14fded19d6408d1b5fba91129a4b978e641cc1e67bafac508180f61f691c20ad6e33cb3c3e7e5819ae524747c8357040326069097d0bb455a87c20c12eac3a9d3e47c062747d0b7c41dc41c906d7c4323945f7c43e9ad7003115f3fd7b718ea8f8b275a7c1d472cf0bfabddab741ccf4ceea1c112a4193e8799873979844437e09b4bb9ec4a36923f1e27452c93d61103175e7a6e9663d3438503465c7e56794fc9f710b3894900566e6aaf691c638dc5ecd10a640e443bb1b5bb997276b243e03d717bb2d8b657b523420e1403d8cb02dbf26a6b472a6ebd69dbfc47ec2042de16a4a3cf4a619629895fd58e0028934d700e72660c4bf7f35e7ee700aa614704601c6fb8c49f50d7baacf9c2ed3cc01f8ba9aff8ffade8b7bd5d6e9945d94d5f2a192284f6bcb77f0001ac8af44b820aadf03a98ba50426d338405c8bc1dc36254e01266adcefec890e0134408ecdeb5e5c5930427755d53f034dd909610b97e19d5599906af37391f8960cea1279d9544a505562b688dc4a61d4406803a3d9701593a2243bbfae8551b34eb731aaa22f527d73c5d0f7397854107c522a2a87634d599e100f290beaab0448b477740464026a22a5349f7f037f723d7ba5879d7a0d236dd4b2881ee02ab19a63f7b7f2e28e638ec8b2b650ad620ab857588372a429f46ae9c60ce6b812c8cc5f75d41ea8f49eec4c6804cfaaecfa2dc27b0d07e6f9e5d21b8ae1358817061a66c41a10574ebc53469ba5efb35070eaf2454a42558ca934e6f32e67481549c660907520d40f00dc3c4aa290786248240d0908294ce7842ee820d0c135801267614be0e8ce424098f5c401bb433e4989708033c550c1692f0d50e5d264f8f5a3d4189af52940ed90863e8b6a9023299856b59786d36eff4fff28c74c686c464b3bbdd2a9440427aa129efe4e1439a54c03a117943ac9a31c7ad6b6ad033ed91d4e16404d9a434b8cd9b4dd914196b2f2d90b6467c3f2de8c4df8d0d126a4bd3507b0b140fda4e3716787122a7b36294f18bae2240a3cd5b955d8b04f322e0bc2a102caaf983cd272e102bdb329ee3ceecf99f4535602191e3989fc1841662842494b6c55f5375bad08e73ad88fe8e848e12eaf8da644329d4607d6c0c439d88479b53029a90413501142b74d9f6f03cec873d57ff2ee7abd6d583d847cfa3141d8eb395188b79e485ae18ded2d79c7c3ba18d92725e52ab9e83a357b25a63204daf438d985efe3ab1146b12b1d575aaba2b088651e7bd9ed99d75efead8f1987211b544332cfcbbd9dac31bc78dc62dc926faa3ff295c378e4f2421853d41085e8ed6bc43a82b9026c6fad198b1dbdbad5f8db0748cdebd865ca1c69566ce5862e7ac4ad638ca6edea19e98fef5760120ada7fca78373b9e84d55f11edfa209278000fe4be2c100967a502b66a3bd52edb49e84878fbcedd76744e473b10870bb3fca94619fa176dfb71f1c1d785b9009f9cf9a775145b15183351433cb762c53ad5847b5eadf1b897faff6da8cafe89d9fbeb1011253bceb37d507650dab95dd79980d47cb9a0f4ebfed8d8b73b29e905ad74d34128c662ff1675fff298edeeae17db0bb4b2f8c4711b89f1dbfdd4f090ceb71c36590b0cce247d859e1f2318958bff86f3b31039b9814b83f56e22c87cab71216348ee6e36b2248d9885802b8e94981a523baa1a589b99be37c8744c69218e4454c65c218a8f4110f0dcfd745c4b97b31be1f2818ba82848d1d43f31e7be5c16d23c0d5e2088aa6131d733b2bec8647a318860290a737a73cd1235d7425aa1ca1026a0111a55622e02291471051350c14623b2a787412267ce58cd5dc1c31f9bc1573d3a18daa8c445103619d8b3663468de5cc67c139a3fd9bc7b7664bd275e6707824d60a7ad9cb45f54c18a4c87841c21ed2c81a8e3867b9fe1813e7392045b472eceb7e3e1a53504451788dce36564ddf35544d2bab2cb4fd0ae5a4fff79e0434e96fe1b777b754b628c01cf1c4306cbd589936a0c70d4784eba5f97b2f547179a44bb6ac29ba5da319429625ae568447974e4e971f6f4d368e7819268ea0a676e0442a01402cba1721b439d3ed8a54554c59d4c422063af85c0d6f6f776211abd5b056dc9437c841b414e7dac4a2dac3efee14a826779f30bf3e9101234951944c2d8c3f86ee41e3df3c03901c580833173105b64ca10140ff01e4a00e1abc1ec68a582700be8605b62b357844772a38f3e0246c5de37f21460fa9520da080ee2ba84fe9917105ba5a39121af5f84010decc5d14105c7c007e5db664510e42aa1830d186abc17b70c7f91abc9d7be41f2c9c2eca09dc87225f8b5bab97c1046eaa68da092ecb60bc45603a628b8acc3d95e7d4657a1f0ce10fcad322bee945b95bb54adada2e8fa9dcd4ef7ba31d1e1824fb42098ae704c0aaa196604654cfab0b33b064a5ae831e0d0ff095bcf04cbef00c25878556a95d02d68a04c8e4b6969b9026664f79fa544fb2652d1d14d509ad056768091e28346e9a5c199d614ed182df1d5c5b623d47615a1bbcf19c2c54e2c18cd478ecd853eac5cb8e730623d4fbf7f2e7eef4452a553380363e9508edd216965eb688103763306cf0841000a1203f711549792a190c49d6b09600190ca5f3120ae05509b5dc35b302499a5db58249cc8841be44ddff339253c6c9ff4f97d9f8812cda668f13e242ad8638f43a66983967f2ad550af347ef9cc9b16a6cfdfa7633898a7c8bf2ceb757f8bf313d599198ec58816f6a893772ef9944bb420cc4fa2fd7d502675dbc80d817cb69cbdcf0395bd02b37bff0c9bd2d4b450d7c63b1d8b6c2a11952f79b53c41a0ced67551c63a4ce60a8d16479d757f0f2d4ab4458f78319563be7602b7bb307f9ba105168e60689b98373e6a6e47c51ca43e227607fcf4214cfc97cf98336a7f107dc64058679fdf96d12683f18a7f8812cd244470b401cc45fa360a14f50705685f9b13de42f063d7560f9b67279e96382a20a087473584313a84e5030fa406980a75fd72628889b5f8fcea476fb4f8a1e1a6f0115f0713c61919255669c7192259b49feea5dff043cdbf3e2d447f29dc605d5f9bc6a2d9caf7e5b3d403cf443a79b07916aebbfd2ccb2d2b821a717111499701d784431cbcdf7e1ed2914a41ac858ccb0184171e42f1f15044a454a8871dad02ea10c3a4c5391f6b193ecdbc87c1095271753b3fc0fa527bffcd2d7b88fd09509cf04ff818a79fcab93860bbcc59a04058e389ffdef3e99fc6a115ec2e87d8d7f87988e5f5ca7f48a2abc1cb5b02d54fa629344dd10fa4c310335b3df2b1118d992ebaa4386a5618ded82fe8f1dcbcbc59f1e738dd669a63761b16d9499f2dcbc7c4c3c6a3a89d84ab3901283d8b608882adccd0a9513c116099fcd5ca2758bb1e9611cf768a30b351381663e3048e44556b57273e19d59d87eacc8811fbb6d19f21ce08718ff63417167e4a0fdbe19fd3739641e8a50787b07fac58a7605095c5c3f7830061e989d13d4c46d9e99a3e20fa2ec55c7815f877817421c81870a8ffd88c7cd8391f82a99e3fd7c30bc122316ea83a3181d9c6a6c45a28389b4e64c57a8df374dafa278a090c7c66b7be4582c22182db90c1f589111f34f90f83ff3b796bfeac9707da25c60cd3a3d936c7216d92d2dddd19e951187c0a0af5dc496f4c3abf7f3922d6fbcdeddfef1d44366695817491519545097ad41ad0387185a10db0c3244e70f8c031f3037dc2ccf30111003e018ffbaa281d8fbbb15e7bd647417a115a56f2b4c1403fc99c2d3b34cced615e6f13864a69eecb34a0cc142835df359678a0b4c5368c208370cfa9ece700376fe6f443de1c087ed4568ef829039cb63538751083cdcf5547d03d187b3f846f8b86d20476a53e650f0b9ba6b64aabd8b4d9b39f6f949a48734c1f9b26be070d6c2960e8e23880dedc9b844e1b4cdb14d994cb4bb2476d713bc5dbdf8698ad87701552ecaa331c051a5f5867c0dc1ff7237b76d1759e73b80f8bd38ace4c27b18ccc5cce8e20b43c17902ba4130fcdbf80709e81e9ccdcca7e4c622b8f7018418e07a33e06dffde7d5370c0d0c984e8b3495adea5bd1b99c7589e4c8535bc2466e4c25f83ce02c2c0156b8753cdcdc98280cfea48e37dbb3142737b1eef040b7979e70f2b21f5ef30947709f2bf9ca886e81e98fb99d73441fb2822704fddb7de55cadc863ffe2f191a3d6c0b53bc1e4ddadf4e5ae712a49ec86f749c9c60c29d71c98c373179c9dd1e53245de6262e3b79f69b96f89c6d85f9549ebee9327893f79c6440ae9493b02f559af86afe1a8a2e168dfd59d944afb6200de0b03cdcd3a4cc78086c694465e6924c65a737181008fdddc926e630dda700a49adffb7b9659c2b2d113bef2d25d46148cd6a420ee57dd43e23cb761084bafe845d6f4b3154bb5aa8b90f3adc190fdf43fac694cef43b4683e784d6e82773ad3d2dc419b4851b9b61351bd053eb50554095e56df0c9bdb6775ea77fb58ea2b01d1328af49f86b022d0a5e722548b3a09d32134ae9ec43f274830a429f630311e53ca5fa1369adbce0c4f835eddc555688d9a1322699b8bc9e8af6b87b6f2ae157aa0538e483909362b1b11bb6dafa6d312ff3dde3748b0989dc5fd95bc30534b3149723831d5b38a74fa3c77e6f53c8e01d14b5fef9e857a8c0c3eba49907e3bc1ff284e1f0370225322f4278b278ec00e73e4bffd24ede3dfdb37ff2c96069004df3e2d8cf48fbd137fb88a2c348e19cdc2c17a43086c78bf714c8ecc4bb3c037e2f17e4920e4529fc97df78dcc9db9e089f7a3f202cb72dfdd450b899f25884d4c444ecdfcc220d679b568ba03cb5403d4d4a509c9dd95213abc43ad0ada934b68145e6b42dcbc55895bab5a8398103d5f0b616744674280d4632a15b1d91c26495821feca1d2f7e4b577fec7017225c655e37a7d4b70de735972f7cc8a97d1eea0e98baa2ddb6cede9bfd4e5ef2aa13cc28db67531d0163d7f4d5dd57420ce2464854605fd42fd42d3f0b5cd2f6cfa499f3f05b412d43533de4a5aeb22edbba0b342c6b76455380482f7dbe7e3fc792d37ad3bbbe3a609dd51d4a44dee8800878eef044d610074349a512be1dbc3c72f027c2ad30a866eb60489365112e9bfd651dc0e0403648f51b7f4b2c1666e688680c14c73058b39b1cabcdfa6237bce88c1062dab16fe5c2f36fa4480e8819096c8c1c104d55153dc8bee7d5b859a6a157afd5dcd6e8a9028119e0069f657f61db879937076dc04a574ab41c2d8129658f5daea72e7ab7ad7eb73b7cd8fc65e8b87a3cc5d251f110b486718385199f66e896246140f93b42e03b78d027b08ae696968132001bbf6620efaf11af913c0f242c0605a95822dae3908921fb64b9aee81e7e7be602f9cb3aa304187922d7886e2c2c96f59c897bccfbdb155a4db87036d5b429ed16277147a0c42303c67dcc4bc26a1599c86bb284703a267196ada461bed3d89fd515875418a8baf52aa2cf7f1f86f89da3cf9668edc3e05c2e787c0bf78a2ca13a5bef32ad304e1c00c575e8fc7efe1417638a1c013e4c9986f42e6277b291322795d175a978d2788de586d119cb086286eded099a09ff508eb8113826953ac646e21257f524093f6d029684af161b7b9172b9400ea4e10195c0fd09c9c386d6352d8512cf54034b5919693546460a4412225f0628052450a61caacaaa018a6e28be73f51df1290e4a04a587d0540cc2d3ea92101e95e1f23bf3d36bf8dbffa99dc319caf3660efaa63b824bc041d69db11488320539db32470c0e18661224a3d47a1bc716a0d1910cc7b7ec715747761bc053ce5180832cd619f764c031cae29232b754d9154aa2e9af1e8fdbb764677a1437def7cd1bbe65e6bbac930f95e5e2e4cf613a2c9a033a49b4782f1f48cd9d3ef22f93d4e228e592999bb868837f1d58d70323a202cdf4566c56ab390cb86910810cfc3c9680125f24a13a796842cb4e23af6d2b71548f18d63145bd1eeb7484b0e2807b9838a617239c646fd0a37c74e04903bb6a064b21714fee383fb9f9c47b7cd8163a30f792fe3a001909287bcd4fdb24a41089804804491d86062a92ac91e4ef3ac89b04d11db92b02b393a1e9017fe9a71ecf6c2f3f4190018b3c936dbd9046c2cc41df29691ef65e9ded92e783c1646cc34a4cdf0a715f0aa1d4d148ae72d01a31e2520d42cf43491175f492e665babc51ced1fe5e238943f0fda521ef900b12e80cbcb0a228324f7c853e0943425b1ff027bc00d43fea765f076f430f7909ade45866cd6abb1f1cfe2b3df4e2bfcab840697c1ec13a1655476b4b392e00a4141c9a8b72d8dc0224482aec6955326da94c59d23b490891fba077c8cba19410d9863b111678b64428c6840ec37c1405656d67d8c55ce6a6de254f7c3d22560a3be74aeb56a606a3559e2c864f4a57c1fdb37c1a25ee6fe4fb09f367d2fa6b609a1d9d5c2473e1fde5e8942370bf7e433d8a34630ffdff8920fe16d980d9efb937708cedcee742713605009480cecc2b90fa2e83674d34f409590182d2c544d584ef100098ca1268dbabfec80f07aaba8a7e7c0b94227289139ea2d7c60b085dc52e9dfb0e9bb299f84e579c3ef39aad15644f075dbf3b7b13c254861ae376647b8adcbd4c899be4fc4617ba07dd2fa09466931ab68ec5e78d5be6691e744e0f23b8d497c4253fd2e6c1dbeedf360922e313b5c45df7815ef4d1ae930f820260c65cdab58c63e6dc9841cffec6de66bc0e752cbc9ccf04edd212c120188d72686235a6096571a5819d15a610a8b380ba5a54a19683f369c2b1a3dba5eea1ffd16368bef39aa20c78eb9ff10a51878e7660e7d61b891214a20bf8cf4454e5103174c3db66832d455f9b46e4dd3efdd31d997760a18533952a94ed94f547d27f10fa368ab2bb12024f6999452aa2c0217d3a1a3371b8d643b46c3e949a5da9e90b502b5a035b1850b57206ce1468abff980219e0d89f1f72348f91dab75e649d15ff94b2589171582027532cdde23afd9f8507920555ae08ffe459c825002881d1a52526cb91084ebcb6af0980eaf93a210446bf65e9f514becaea3a143af75c55eb2fe21f91f32db198b9d475108541c5f3c4bf4daa0953c7d22c9a595e908dc4056daed2d5a31197b4c1e9235e595175904085a5a34f2284148c4e899027107145ab17d76fd79ba1f4b495f2ff47ae7be45f211e21203ad137e0dd330cceba0c71b1030a2365db29c98ec6b5313cdd93cb3d22f5ffca3a3178fbc5ac7c6e5046595fbe124dd99a918c6898bba85a1c33716f3384d0a2b2a38750e9c2208cc817151a1ff0a833bdf42e5454496e699b724167a5bb29fc522072e6f5243b4785e8069338311140a6d17586141e36022891bb64222bf431bc8cb85932e2e7b6638512554a5ca04cbbff6119e5fef4845acc40f9fdd3ad433fd37b02fc19520af4e954bd130426037ac0f690541058ca461de68f10527a872857541e14bc27ee3200fcbe7d902620b519d2192f23822a9584401324ea8774387f676767f20db4591340740479f443dc4b30f06d3d4f89c22f005effffbf90ad6b89dc5bca94a40cdb080f09af085b3eafcae6949ddc4287fee38ccd18141b3af31bd439e7f4a4aa9ea4aa8e367524c3fa32ac2f7bc873ce89faa1d22bb8ecc83c4736329c52356db0c93f08917bbcf7472d6a446d69836c9a45114a58ebd5eab9909019861c3a6cc28ed174cedca423f7fb0c6aea5437735e25f7d75a7b9f153b25444321ef19b2a529f33cb9b3d31502a25f442a327d10896e4d82a16422159919860e9bc70473ea18992233531a4a43c32377b2e9e5e08e0d736a45a31290b79d6bdd2ec85d32994c2693c97cd8d985ba1c8fbc7eda7198c75ce633a7794dfee755ea3c3c3c3c3c539e30ba31335fcccaeeec44fb5a68dbe99b4e2693c964321f78dc3d74cfe97641de72975769f3f0f0f0f0c0ccec3879ba5d100c0683c160b096eb058b4d990f2deb59d3bad6b6bec9af542693c964b2e983dceda8b9cdd98d76abdd6cb7db0d779bb9dccc4d994c2693d9dd9d41a7d6bbc2d74ec35c2693c96432190a3c423e286bb66030180c0683d97b4fa7ff30dc99b4599bb679cb52ce9c7c6f180c0683c1e6cce572b9e9e3c7efec42dde74eb85bdf60b086350c0683d9d6759d5e0f0b633b53366f0fb3f6de130a067318909197836bfbc1775c9652f6cd4577593b428df7f4a1a3c69d3cf62df7dbd7bda7d37f18eeb8bbdc654328bb4beaa8e12e19923afea4b9002a2d9bafeeeeeeeeeeee5e5bb0f6ded3e979320f0fcf100f83f438597bb233b4451b020e598585c5e8042e0513bafac571c4a590b6cef6f3bcda252e25e151879436e1445030f3723876b2d79737e42079705d76a723fbe7d5d5786ba89e47dfc839b4caa9b83cb691071505150475729b3bf3f2ee5c65cb2ecda7eda8ee1e0e45fe57bfcffbbc2a5d4a4f7e5286f34789e54f3b52357f8854cd0f5b19ca7086f3471f4619f58310b9a9cb014a29a5ff90fee267174e4861ebc13d65f923c5cc24e5eec8f2478a264990e28812d02d05a501049212a238a354e5ce64f9130517a659144f6c7069b2fc894209d7975a6ca7c5adc992c7fb3133f2f8978c180daeb83259fec4a058716f963fb125695c9be54f2c8989764959fe3ca9a388274488f2248e376e28cb9f27b927382874de10acbd77c7144305323b1a5b92181eec66b02801d303988c0f94dc560c932398b2d460987c51a284c3dd26482421fd406752d2c468e6e4035dc942590108242335d1e2299d3969e942c7e8da4089501b31b00c26ec4b8485898f86dc2dd4c60a3c1a7e50b3465041a5c107df4f932e301a80764968504306858b862cae2a9658b2061d5a90dc2c96b8724bab240d6c706995cca3bd272872ae92d9da6fe6d4e3e709387eb8f15d5a698742a1329eb899ac5c2059fe3c9105e9cbfd2c7f9e6862c9fd91e58f13397c704fc8f2c7490f4c3f3f4ecc4072bf2c7f9c84616ae2d664f9e3444a49774d59fe387192e4d22c7f9cc0b293974d18e4c7895a4ca659fe38f1c51ce009bbd018778479c1bc1ce695e9ad8275569992e983bb5ed1d1e27c7203ad3071922928d302da26932e28d82553f096e9d7d9a0ac57230825d359af469817cc0b0a38a55728e411dc82009ec15aa6606e62f0dbe2cedcefb6112c23d3a02b7f94d47253981885f59881a5c79da979b251739b73ce894bd9b52449873dad74d8decdb3b95cb619cfb68414a9129a8b74d25c2ed9cc45fb62cef1949bc74459d605c21d4f28d434c552ad67ed673f8f55b1942a4bddad00a5cde8a395dbfbc76a94e5934a54b6e7a4c33665b047d48fb83e0841cf1766ee0b45ee9fd50383462d704484143d588fd609297edfde14b93f54a5a375176669b7965be7c17a7047cfe54a8282d7242215050fe64d9687e2415fb2bc3fc2b247dcef9fd60e726d30a83e480718c4c6f53c82418e6f271f2d2a406933ca336ad9ce39576865b0ca6a0573754a87f67b643d336fd403f3bc91472222513d4e3ab43f1a490936e14a3945645c2afaf9463ff6e4b2edc902fed873255b181d34a498d2b9cabe55e3fe20a58323db0f05ddd18f7ed4b1a15c5265df5e500ab9b9d823eba1b9187afb5dc5951d4e942cbcb64796edf7c87aa6d82a451d9b54d9aff5c12d72a593f5c19d0e0ea4d803eb81ed20dba72422b5c6499e5fc3a40646c324cfa781f590b9e6d72029f680792ff7602e7ea00edc815d606e352745d3cfafbaba9b320e7a1e2a09cad5a145bd3ab4ef514eaeb24f7272512e5412500c19da1f55d092e7ab9045aec0daab8003cdc8f6c72059f2fc2038e903fe24a96079fab0e5e9a396e7fba04951c7e6e3e60397424cfa90f2fc14643285590a5b48d5cbc5d1cff7117b1fb2aff9c933a5932afba23cbfa6267d64e6f8e5d410e4989223db0f1571c71ed80aadac6373b187c6eaa1b9d803e62afbd6f6d0462232ee47c6f59a444407d353d20ecdf2fe842f7a6059547aafc72cdb87c14264aeb27f85ccb2fd1eb03bf68065fb15ebd8b2092524d6c59de1e522cc5faf25e4968384dc7677eca165530561f06409b9d5205cb69e905cb65f71ae428d3d78ae5c0950bff43fb8e38e5bb630ae6c5bf44d22227a1119d766593245418285e82789c87c1d2badb2df361fe9d83ab4efae63a543fb924424a543a1891e5baf685ff4aaf4f67becf754e995e8e7db4fe94a5884c15aaf7accc400cd20d972dbec220a4e6fba9068ea6a29a59c520a01ab782c8da8bb93403c9fd44373f79f34e9237afb601772d56316a265fb52ae7acc4086760c7561c9b8a289c1ef31eb55bd22c37efb3d66d06ffd4aee1f512edd6d24c2e0c74044b63fa25cd9fe8ca1956d4f2ddb1f53ba6c7b68ae4281071257f43a3617476f472d428d3ab76c7f0cb3d5b1b9cae2e08e3ab66c7b68d99e51eb0f48108cfcfe91479994411f7db89465afa8d3af14a4c3faa60cfaa87577943acf1b70b1fdeb07d12bff418aa154d5af790c37d0ab2fd79c2324d737d2a355d5a686d6863db8467af58957be75b1fffbeb46b0beff9ec7c510eb7bafca104c1138b678a80d745847a983d9512b8e00703e0f6aa7c3daa43f310fdee9b0513caa1dc9717950435cbcd4e9f4665357d35a6fe890ea402938bbe34ea674878787a743fa3b1dd239e5536a45dea6fd6967e87752c2ad21db29bb6fa836a9337f480d1dd20f157143ba11a5b77bd44398a767ea119ddedd2d8b78018233b2c7cca97bc85aadb5d65a6badf50b389496242e973c1f05023c2367133ab818d2a324ee2b8fdedf40e99567d24c07460e6c400319c0c0052c50010a4c40021180c0031cd000061859800212c003027638800e0628808c0070b8a1880d446aa0819587cc20430c30bce0d3c323ae5c6841480e0160616705157482e4e0a8c2d40d006c5240e1077207209bd16ab61b2ea7db050569b9f8819f1fc4e5e20f3fff06d6ab2fcfbf89499ffaf3a50f262ff8602257981ff3e00e4c80a7c1a53fe11d5f83691e057cfa1370cdf7c0283c0f7cc2ffc03dde07e6f103c03efee21f5f71297f0023b93e3c78f10f163847178f582416e8a38b4bac1f30921a2bedc2fc1fde075c63eb70d654e970be8905c40990180a084c087704024362840f20780f3c09f7f0239ef90e60d26f008fcf01dc81cf00dec06b0073e02f8033f018c01af80ae00bbc0530067e02b8024f016c818f009ec04b0053e01f8023f010c012f806e007bc033004de086ec033003be015808dfc0230039e07ac804f005ec0ef8079781326c0230027e075c03bfc013002be00588737003ec00b0017e065b001fe062c80c701cbbc0df8862f8271f81ab00d4f0417f90fe0d2b3700d4f0326f24330eb33a6e165c0437e069c1f062cc3c78067781f0cc3bf8063781eecf33df8855f619e1771cfb78057ef02163f07dcc20bc12e3c0b38870f0016f22b60167e0707e075f00aaf02def91cacf341b00aafc2398f83837c0aab3ec4380f009cfa1b1c7e0a18006f836f1ee3141e856d1e08c6ff18f503c040fee2ff8a47b9e3b82310580602031273f1fe7c20b25e799eaff300aee1d22eccef01d76ce99a1a102030204e6e6e3af0f36f662e8e3fff86e622077efe4dcdc50dfcfc1b9b8b1af8f937371733f0f36f702e62e0e7dfe45cbcc0cfbfd1b968819f7fb373b1023fff26c8450afcfc54cbc509fcfc94cb4509fcfcd4cbc508fcfc14cc4508fcfc54ccc507fcfc94cc4507fcfcd4ccc506fcfc14cd4506fcfc54cd45233f3f65737101af809f9fc2b998809f9fcab9c8c3cf4fe95c44c0cf4fed5cdce1e7a7825c3cc0cf0f5b2eeaf0f343978b06f8f9e1cbc502fcfc10e6a2cccf0f63a12c9c85612db485b71017e6425db80b552d95cbc5185430554c2553cd54aa9acaa6baa970aa9c8b39a090787f8465e5f0f3553ad5ce4516705a382e9c170e0c278623c399e1d0706a38369c1b0e0e2787a3c3d9e104e5b4725c39af1c584e2c479633cba1e5d4726c39b71c9c8b04f8f93939174b3fabe96bbd5f77e0519ff4475860f5723f1ee89350428049d8d558f4411286d9c104c5b4625c31af18584c2c4616338ba1c5d4626c31b7185c4c2e4617b38b099269c9b8645e323099988c4c66264393a9c9d8646e3238999c8c4e66e72a99a099d68c6be635039b89cdc8666633b499da8c6de636839bc9cde866763341342d1a17cd8b064613a391d1cc68685287a64663a3b9d1e06872343a9a1d4dd08ed60ed78ed70ed88ed80ed98ed90eda8eda0edb8edb0edc8edc0edd8edd8ea09a568dabe65503ab89d5c86a663539d8975248e869ba902e7838ca007e06df7400df8cf88603f86603f84603f82603f80603f8e602f82667017ca3ab00bed95100df044d00a75a12c029570470ea05019c7a004ec51c8053b206e0d48c013845338253b505e0944d0138754b004ef180533904e0946e079cda1d00a78274c061cb0038741500872f993016cac25918d6425b780b71612ed4853b554be552bd5430554c2553cd54aa9acaa6baa9702a9d6aa70ac269e1b8705e38309c188e0c678643c3a9e1d8706e38389c1c8e0e67871394d3ca71e5bc726039b11c59ce2c879653cbb1e5dc5c359f0038a78491c8bc1143b458a5cf01bb2c6b002855ce55f349a81bd8934bfaf95eccc5999f343f77fc7c8fe662cdcff76aeed95c3ce1270a3fdfc3b9c8e3e77b39177bfc7c4fe7a28f9fefed5cfcf1f3bd201781fcfcafe5e2fffccfe522fe89faf91fccc5147efe1773d1e6e77f321701f0f3e6e77ff3bf9a8be1cfff6c2eaa5cc4f9f91fcec59c9fffe55c0cf2f33f9d8b3a3fffdbb9a8c2cfff825c5ce1e7db968b3b3fdfba5c64e167007ebe85b928e4e7db988b2dfc7c2b73d1859f6f672eae7ebea5b928fe7c5b7391e7e75b9b8b3d3fdfde5cf4f9f916e7e20b3f61f8f956e7620c3fdfee5c94e1e7db201767f8f9a1968b437ee69f1f7ab9c8faf921988b34fcfc50ccc51a7e7e48e622919f1f9ab968c3cf0fd15c2cf237fcfc90cd451c7e7ee8e6a2007e7e08e7a20a2700acc201ab6ec0aa225865035611c1aa1ab08a06ac626155c6aa215835030e65c0610c388401872fe0d007873d38e4c1a188c3150e5dc0610b381482c300e0f0e5aaf92ce07007872be05490abe6ab80533b57cdd7c1295d2a978353381c9cbaa970ca16e2542d8553b41b9c9a0100a7643638154b01a760289c7a619c723d4eb580e09ba01ff866e703dfe87ae09b1c0f7c834301dfdc4ec037367c5373d5fc1a7c4373d5fc1df866762373d5fc197c13c32739eef7e30d8c86265998303aafe5aaf9177b2e3cd2d880e60637518c415e3014cb1d5138d4965e8d7e3e4ae722f8a82d12a5e57a1e51b8d183d686ee69f4de461b41b8a49c9dabdab8b2bb0475787595744d25d2cc5d3a9c15a583e142da41f414c58307faa427a184a07f8405f6106efdd18385e1a9cbf59cb3738a25eb06e62aca830ba373d1f4d3ea5c34a14ad692ee95aefac19579bc81d129bd54bf256279b01824eefcd46da66c79a66a79a66879a666614a96e7dfc0e6dfc46e64a73cdec06a79ded86e6eb89bdc8d2ecfdc986aa55c797eea95670a36a66237b0b7a8f1f4a13c144bb2bc2ceecf468df70626c64810987da44105d040c992b4081c56bef97dfdf5aca1e55518b53483565cff3a75d7bfca4b51edee3e697737955dd762d2f7f75a8bebb2f6942ee700a9df0eed9c338daf7e9ff779f5091bde676db5f5ab8166cf39b4ea6b97cdd3abb17167187a350e91e77b4fe994520ae43111850a681369ef69ce50690c22ff10449eb69546ac851d77777777b79f37de1ce604a388d4f117d9e08e1237810bf46a946514913eb2768c52598f5a612c51df9bddb37fceef7b4ed427f3aad44285ebac8b23a9ea9752d53da4aaab54f5d7531e81c873ce1eb594e07d9d9f67708e7a8e47269dd9a67ab659f9a3cfc2d0ab8ab259449979b9ae760bb24a9f518bf280d6507368156aa4304c7ac42c484877765a98400f304c99159e1b2effa7d3bd246f34fdf87e0ce5399264982eef6d8df4f9648c9875b0beb7fe6dca5457c79042fa43b8fe4817fa6370e58114d26fc2f58a74a11fec9ad451f5d71ab85c9495964afd9f4ef7ba6aadf53f2c5932d7e115493fde8c3c59de62186964f8660cae677db6468aa6970f44fa945ebe0ad267f4f257902f802f1f46ca107af96047213d88a3981e065735a4aaffe25a86659148df26d3f75b6c2baedaaa19f2053006d1d72c5286efdbd612ae385c6baeea1f992157e8ffa41422428d958ddc5fb34817fa43d875bf97a38a05c7b50cf9a3a6c6fcae5e680a482bb5d67ed466618300cd81e40b142db0020913962d9525b21d830a0bb2458d9f57691431a2d0ac98ad56ab1563cd25cc10421b79ca960300923d5785962c7faab0920390e54f15b07c7231480bd9a3c8b2fc8902cb41b2fca9824bb6fe3d76007e8f0e3d7c0f7bd5d9bfef2a48af648f402db7c749ea5cb96318eeb8e8b19c277b08d3e48ed1643647152315bc7b931638e13e671336cfea592dc62f269b48bed9160fa7a557f6bf776b9f087d5dee2873222ab8f4ca82535891674322cf8648366443d2a3e38e9fec937d242c94c0d2abd1d379b75e7dbdf24c5442f66fab61479fce3d34a259443217698f445946232a6594eb9e6e14c2c19ca823ae4fff66b5c71bb526cc39e79c13f7d1be2f7a45df7be26616e9e3bddb5dae2387c0435554cde18ac268bf5a6b015dd2506e4f082ea77d8f55e9cc77c20996b4837495574f27dfc1864221d20e9f676d91ce53a512917698b94945fcfb3f92251599b98b8448fd6e3f51ff90fec8f0e2f01ef470088a1429d2d3e9b42e62c103fd7e9410588c51fa15308b40e58c567fa76008ff7e8a1ac27553d4c17254ab51d287f059817e149aa02eba33db9035dc50438de7d4a3571e24efd89115b96133bf356aedaeddddeddded8d47d7f5405b07b509724e77f7a6fedf97db8f729fb9992505cd90521441cff3bccaa29e575151a87c57b51b598e7125c985e2d62cc7b8f2921d8654d024dc45291d35ce166900a64cdf310968a6a897d25d6299250b5744717aefbdf3b02f91b82cab7ee8297659d6103c748b553f848fe0615112d0fc3d0f1c662c7f28478d43f0b0281de8cbccc365d51fd2d6a27e30e50f15a5fe10f756ae185f17dcf933bb8a7e61ead93d3b07e009bb3b5f9a1c25ab80b78a3b5fc23c516bad158a4ac52ccb1f2aaa6416b2fc813245ce916ab2481151067352c7bfc6ee38a28da08b965d944197d4f13f81c91d6bcd55abb968933e9ecd6314fab2da50fdf33decd8492f35407c6ab7a152d89400a5cde8d3addf54e081d4dacf7e74092a128944a21015894234446bfde93f43d45a2ab47608457114efbfefede751a942e9676bb2c6339b8573f1fbe9bb3cdf6be867e88fb02cfba86f473f9d7f1606d265ed0bec922f4887bd6c59206190fb9bbe245522454b73c2cebae62fc8451a85ca207790c9e4bdd377d1d36c5ddeaedc31d9fb40a6378b3c5b9788041e09076a5d9ef7fe601db1408aa16f18849614e543de57aaa809a25051beb7f643f6f4a1ae93a2d8495f61dcf9b656e9a706101010101010101010101010101010101010101010101010101010101010101010101090f402b4b85ad09ce93a86f397c5d582e64cd7319cc5d582e64cd7318bab05cd99cee2eefc4a83e6cce26a411657b338f7daa99453b6a88b3b7f1ce5f9f473cfd32bc3658bb6b83b2e22b3e7ea9c73cef9d5b12da35639674f5295b2c4ccdd43b9d7c49b0ca7ee947a95fa74ce399bcc68129fdfdce66af77677cfe61f0e7c51979e29961c029a3de75b5ce73a77d444e1a3d1c2b8a5d9b9dbe5c8ce7147cff99616bf2f3e5a15977e34d92b09bea64d29886decf19afd87e112d42379dfe8036f23dc4cae87ce66170406520eeeb4212ca2db6d742514f2f95e026ba7d7c688da55a886a2ece121231e1e30943d0c929cb443b32a2a7455ff2c824884127d4814923693be5cc25ea6d8e699dcf9e3d74c29a57f975c9a298e52df4a1ac447c2c1668aa237c0b8e0fa8d89a1d4db724769936188e6d07c49ed770b624844a8cccc68e615dcf9437ae605773e4823a50efdd0e8f33046bdf24e61dda255fdfdf2d3c9f642cd598f393b9dfaa52dd5daa464328939dc30d361bfbc235b4eea80445c98965a5c10464a1d225c5b4cb74aecd768e6d5a857ee18dcf963bf644cd7243285ace9f3aa899abc27067af7a308a037618b0cb2c194ddab054d5d3665b97bf9198186280d516a237b62e6d4e304e505c5883cde8f6389d91233e9335ffebf12e035e5dd75dd2c42305ac19d2fb790d00832da418e8f4868e0ec407503ec85467377adfd3c1115151ccd2d28eccea772e7d39cd45bdc39bbf3b59879d2e0ced7e2ceb757aecc13888bf1c3d6608713773e8d048fbc3e372491d9eeeea0112211cd4a1932c2a47934bbf3bb24c6740965764fb97742911e4c115d1579a33adc30111c848b07902aff2c2d9c8374903afed2618ae8b6d8d91940b772b5615c1fd5ae7f1797b66859fea901965eda8c62ec8d7be3c48388f9989fb346f150b5561cf332085407be97192c61b984266fb4401f7da871e6cf0380a7823bdec07272d207e6a718329c5f829772958257e5a44fe84df03730174935b5be5223864ea73e0c0ed530db3f41bd4def4b8047cf29408d396ddcc0546d9c20967ba8b95c670ec2dfb8d53fc649abe65b0fe649b9df5e2c4fcf831de1bee7a47be5550fe6a14e61af6c52800597023cce9c022c8debd06665f6c85f8eea09fa4152e9de4250ca258ca434c483a552a9844bac8b91a0b6dc1f02a37226968f960fd7288742fc7c29c407f12878140e85f36a1ee93dcff49ef779a437c5b4eededc4ce9952de2f97b1f2ee9e3fd54e259042fe5ca87081e88e7e1923e3040bea606cef7c1043f0281c1ec606a7c28f1f122c18f3e98a8725207082c97c23c83f8d0c55209be7402130683f0a8353d2c7784b1c1d85c8491d92b9b0f5345fa68b929f8114696022c730c1e3f21f088a223cf47011eef09b009b00f78a4e9a2a68b3c3f081c2a0126011edd8c11e0d102814737238b008f3e92c8f34380c72b6f60d2c7632275e6535470e4f963dd192128cbba843c671d99a246af956d8714e88e36d98654a473458d3d545ca6289b865371efa9867b09dd4be85edc8bcd1c33d419ea68e45e76dccb8e7bbbbbfba44dbfb8bbf717f7767777f7eea65e66664e27f7efd55fc2fe12366aec9f030618dc0bcebde0be7ce92ffdc5bdb8979d1df712ba9710497f9948d498a1ce504fa7fe323333877b712fd4d5a5cb171e9e2a55be787b4ffbb9173b87cfe901f1bc4c9f5e735fdccb176f8ed148ee6c6ce6701e94a280a0d7c33c8ab8fba4737ee9ef35d3ab0944671cbe3aeaa8e34b7654940f35c474fdfc021e71a7bf24f5cfef80cf892b0388eaeed5bfd2ea40483ab22ed7616eea66aeb10f1dcea62332ee0e0b534d5ef78b1a6bfdecc9518757aeccfffd4e0bfee122ba2f4844bce9cd9e4386f3aba38e3a7277a114251f089be7f775cfeeeeee6e4a29a59436a5b205bbc3d3823d8532db5e755bef15fddbe3bb1f0a972b6e08010d2678e1c49589c20230a8dc501164703f2d5f5c181c989e5cd18f898b6ba36071434dc8a628c15bd385c3e13cdc8713e162b8184e14ab7db19a17abc59888bd8c18d1010e57c3c56ab1346239dd2ea8355d2f23467480c3d570b15a2c8d9848aa6a2501a5b84abdefc33a8c6e50f2e121b5569c87e2318212254a2a6e61a7c94eadb556146dc27bead55a8dbc94d45a714658a04409858112fae13cdc17ab79b15aec27760aeff78fd75689b9680417c3c570b1106ad68218b16008d79c3ddc5255ff13e51a9261b50fd2ef01ad6a2aae4df5bcf69495662a3ea08db8e084abe162b5581131ef5a12ce4811703b1ccec8035aecd887f3705face6c56a3119c428fd8165fedb8441ec67edc7c6a559fe4859c293cbe4867191b8a22c7fa41431830b2ee1881b2a8292fb692959b9303870c18d910112778e8b834bc31da73a00472cb8f33bc80d87926ad891f03433a79ccc29174383b93a9335a344f22f738bd78dc05910ef065e914904c57eded719dca65650f149e5e2baa9c8af4fe60caecd72e78f9766462606e69a4aa41d2577f72a3fe69cbda33aa1f13340108928b41b6171e7b72495ae4eca1c0d33e5721096b872061158f261d8b8738c7c44c604156b2801ca195c9a608db595cb53bef1c50e808c90460ca4708235bfba7a35ab649a3f10e60e7cdf3f56589e5f5fbdfa7eda97a2affffd10af3d84b956d787aaad0e47605c9adb62ca2257d48b172db9df64c584a557d24473b1b65aa6da686ab998c3146bd0aee8dc70c10b58fd2657afa618b9dff4723186d81b4db480ca132ee660f59b60bd9a61e47e53cc459d195071e5870948a0e580d56f92f56afee47ed3acdfb445afa817b9a26698414790f409c2a5457abb75c9fd259c8b3497cbe574b9bf14735192410329b6d04113ac90a3c5ea2fc97a559ab978840836722e183071c51522b0fa4bb45ecd1be4fe52cdc51f5e40b146197070d102c7ea2fd97a35c1c8fda55b7f094b89895c51269449c949ee2fc1a44f4eee2f256991c696643a6b955cb99f5473512e418a0b7448d088000614564f1be47e128e9473f1c89337bc8842441c51c85aac7e92ae5773d74fea42ea42ae3c8e38485fe47e124dfae0e47ed294167de7bb2d5ca42dda22c5684b96fb473917e510ac580389d7cd160513acfe91ae57b306b97fb47351ded4a06209601801664b83d53f0aead56cb928791054f3a20c19184fdc80d54f72f56a7691fb49af7ed2925ed1e4fe5116b9f22ebac8b37f84933e2a2b2d7aad4673d17d54f39117b9f22864383f0a3a723fa8a54597c9b6e4fe51cbc5500b5cb003b2620e3798c0ea1fb97a3561a3988b2658e3270d2dc40882d202564f1ae4fed1ac65ba7009b313e124f7834ce4ca891005b9e82e778132ac7ef0b6c44693f50a267d70ee0793804b7a25b97409923e3f721f69e97220fb424362055258fd2157af2617b915904497a02b9c34a94114567f08d6ab4925f787622e4a2db015d48008276eb4b102567f48d6abd0ac57a12d5a0459fd212b212c38e90324f78774bd92479280316b82134ada782388d51f0aead59c41ee17b55c3cc5f1b30336ae14a189285cbd9a5abc8ac8fda2252d8a58fd229188267d3ef78b6a2e1ec1a2084a2c418422b0409105ab5f64ebd5cce2884b8a969d132b3478e209ab5f84eb9528e7e21118cc600b1359240108510481d52fd2f56aca20f78b765292e47e5197d6e9723a5deeb749ec925ec1a4cf29b7fc29c30744ecb8c8c93105abdfca7a356390bb0449d0a0624a115fb0a185d56f69bd9a58e4de61cacf1a4ac8c031e10a56bfb5f56a4ec9fdf6d66fb1f4ed665b53be2d68d2a7c62677fd5f97a6d1be58bf6745e2a4cf0e9d17e4fa5efddf925ed1dc1d8bd52ebd92d28726f77b2d17354007161a152f8e304612d694925b6ad9428b561b4e56f00a02abe7151207b422d8584309599a90c26a2b72bf376b9c8bed6a572e49af5e2d5e98f499c9fd35e6a20c6a2245125328010d27b660f55759afeacc458986115c5ad0822f4660a18535a3e43e2181055bba88011b54ae607515b9bfde7ad55fb1f40a26f7dcb5284dfa80b99fd65c04a28ace0b32d6b0b2240bab2715b89c8b0220c28a2ba070d1e00a3158134aeea7bb7eda854e6971dab6c83d71381aa338e923cafdaef39d8b3b4138620a2753a03c0962f57b50afe614b99fbefae9925ec5e47eb7d2e28cb96a7e0c4bee779a8bb3367acb4511e8ce98f2841c4d5a6cb0faddd5ab2945eef7978b478818021522b87031060d9ab0561e73512a210c2f8890e40d396ee004ab672c77dbdcdf5ee46a263193c8b3bfb974975e49e9e3b9e50d0baebfb5f48a94fb9b895c31406671d2495a9c80abe6e7964c1f661772d5d393e70a6d88318435809240842bacf904a7dbf5cf2ebd9ad2a265354dfaccdc3017873040ae42329cdf6dea09858b12085196c87104952b47b082d54f481d113ce002466e087170c19a4e8e4ca1828b2e577ec0a58a0d58d71f046154010589131dd4aa38918d80841722b491851b4a58fda75ecd26475e008436befc8c81cbe207acfeb0573b5eee1f2971c7daa2b732ee48733496e78fd0b8239de5f9a4332e898c3bd2569e3fbaddd175704960dcd16f107ae386ba986fbdb81506776c1798eb17f9e6fa36619f7ad49752b210319af820f76417c309180020674a2b89ca1da7ed7a19779cad2baa63d6d64ca382b5d6fa3cf575186b6b35abab15e2c10d61b9a121dc297724e5ca5cb65122c50d1a2be8f08a305d01041a4546ca126811ae6822e4058d1434649a907e447068c9f2a99471efee3e3267b7dabdbbfb6773014a1b71b4d953019bcaa8040c988d82d22944230200000000002316000028140c880482a11c88f244cef614000d6eaa52684c1909444190c3300ac320820c018010420c3004008490b1a1150095461d1784c54f9113ec7a8875519490d647a725b5fe24854080e2c5bf32d989b1b55c8f5be6e7197e00c6a0dcdab23b78b8767d9db2d7452566d9baeb7f6e4af58e4b3f6e7affcc7b6e538b3289507ec83218329330584bedcb649141aaa3468deeb4d6eb2dd93b8a705767080ff9c8c09371bf6a68d4fad4153274eaac39e88acd798a620dc244a322d62d5a7e44a6476ab22275394abfbd559f13f9cc5c5d41e71baeb696dfe7473f02a4f102442d1617c54ed5ca357777d3b3fff8b71e0d5b103946df85a0e719c240f4a53f8eff3ec0ade0e334f4470e6fe03f5731732b1e37affa2a57848f5028752fe999aa2569918a68536bf1da77e5e47db9707c2dee6f9b02e896b2c82b8bb505102fef76b25a546fcfadb56c3d4d3202a12557ed9f1a95ac8febbcbfd10ddecaee82a579bc00ee2127b35b505cbbfb6d79e2d0ee78c5c3fd17d61cb8833456344bb9361d3c3850cd84abaa90fb932b7541f019f13b08103bb5322555b9e8636f8989c0ec5cf95d708a828640a3e374206f7f48b43a2ecf389ef262d028bea1fad92c2dd69172c70640a97acb42b403f86603701d81ae5c0ae395ccea77f51c8193c15ec9f3aa511000f4bb9644b7c26359da78169bff2f4955698f50f2ee983e0f64bbc6f308a0fe973e8b8da4f4f0901af254a7bb8fe38d6b90bbf387493f155adf2224c17f7b559be0ea3f7610dbff5ec66ab0b7d20e7f95569600750a9d7a88f0d3f98a329ecc5603b03677920d4638f36bc891225bbf8902406fabaefe69e8dbdff82bb67a01a236000242f02fd00520d55380a19bf808ecca1e3c4b0a99e66cac491f5b9b90cbbcf227a306d1152597ee9fe16326ec672f90a47318fe8802b02b5c8ea47602b9864c69eb762bde4391aed976a9b4bd8134ad5d48bc2a6160f0accac6665adbdf0eae1f559669adbf436a1752022f90b668a86292027e9ccf41f9d88a26de3451b220e7baafd5b36e116cfde3f53d6b57c5baeb185f2997c8d3905b2c0d830e9b302998a4d098b58b9e194b6b5e007a65448e2498fd19664203502fb8dfc1046558a80c90457a3929cd589afb0f80b7edc2f17f301d2957d6c8ba5f522a7b2c18d84408c30df9900b696380cc2458fd1774fb7bb28290bccd05da24bbb9c61ec8522591a3997a7e9b40c8c1aae8f4e9ed4f443559aacc4fc9087f4d744e8bb3450e4fb929f5ecc418553e75754d3c9ebced82611c5870607cc5c9ec0d04bd49e8534649cef38d2399987129a3de8659d76da8aaa93570c38c670054d5ef3fc3ab36c7fd720b6d67d6d42a3f2dd04c72b075f3d14ecba85db6431b4d71fc0b6e7119148c7ad088fdf08a19cf28b9408521ba764512cb3f3b1817480e38c981813cbd5853d3f215dc7995899ba479942532af8cb17e3fb808cc238edbfba1d4beef89c20237be5bf6fcdf1ef0249caa7014e1edf37f83f9b05ca840437c93e2eb728edfb4d4b2da6c19bb3285d696c1e7f92d9b39b541c74d74da763e720cc648b5600b62458781161ba442474dccb4440b0023f9087abadb3e980db86e2d4fcd04ffe9db7c4a01d2dc79f74d7f3f2ce26c8c12d134d0edad9a1241feea3663ca58e938e4455f3ada4b0d161d63036e4ea06c5c2f8c130ae6789e9b5223a64d47f7c62e127f4446c80df80af81dcb0a3b28214c52b165c137446a6ce39f626e837c90796560c2e531a1b64c9d27fccef6ba80235654682af6c77c229d43976835f4cf6fa22b922cd04061c92b1ad72c3ea6741b04e7b04e8fc862c932172a1664ccab6abc7913ae187b05abb1c6d3580476bd81045c4e57b0fd5dbb1c574433d9917681acc69eddeca77567dbabcdd64821d5cf4543178c520e9738304f6dd789df2ba42c19b97b52335dda23bee2dc0b65a38a7c45ca04deda49a881f117676aeb81f5c5d2575117b21ea0fa98d6765f8dab59b2862cd169f5eb67f3ab10ec224c13ee36c4da700a4b62b38a76088df15759240b69be747bfdffd8f1f4d1315bd8145791171d319b145453c23634001c67db91072c32657dea9845d34035504c6e05d4b072820aaeda6cd1f28644201d02b903d6672f7c449292d39c692c81d6f01cb0d67b8735a8f4bbad4b271dc5d32deb42934ec88520b3a863a6835471c899e106d0018bf7c483759bd4f3641dcaa0b6237567c1ca4ae6512acca97b6bfd7a37587ce922aac2f1d444caec917d9d4519a61d61e2c6b94db480c150a3fcb752aa6f269af84d6977e3f0ea301299b488d8302e91cbbbf3f739daa0735633cb9d0dc7e8c49d3d371338f2aa9f50d94247c6ff023239032d724155161d30b3a5bddb4c3bc1fb276006277bafb07274742f94bb5ec1fb5a9b25112a611862c0a7a0c9de94901a2ce704d28fa6827b9459ac1000a7e16b941c5ff1b764168d67b69b25489e6bdc9f4d95142c72851d04b5991006d0429d471c08f3941f73e18d61a528adfc25cf5037551b9f65c292228be2760f88190c8124f1220719768127e5c97742d3267b54bc027e2c70c1ccefca02d6ae36326fed84d86bafb1abda1ac962b6fdd8b83eea700255055c2ad117bdc38894ac84284587ae03303cd4730fb2813ba47ee26e0892689d8ec3c356266a57398603fc023b9115b90c4d1fd715aed67dffaa42b7ff4af3434e888bd7152d1f07f863effbd62fb9559985243c710f0dc3658fb83c81b69b754e19f92a4026c1256f63dc6a6714687b674ff7f7d77d6739ec0619996f7244ecad41de8a05448f65f0b6ad46c93080475c7868d11eb1357d3c10104e083bf51de25ab38a56dc769d29b8434a704d06db161699c5f34bcae894e5666a96d96156db4c6a166fadd0e0e034b9bacfe75956bea9cba533826cda777f7e6cf0fe5da1e25e2b08c2104a9de497a9557b5fd3b5d56b3ace9e4dd8641703da4388d2cf53852f9ec686b4afe790167a4c5784116268486ce3026d1872ca9af8f0a5b83a38db7e4bdf7133506e36765b7f5d01917780126ad9eb17e500d33d2c86252d8f551c3cb443582a5d855ab6f0414e41d8af2e5c87c195b87e144408e7dab1c40a537396cf550382744b8cd0bc1a9fda5c1f5d8b648d9b0334051823a1b79882565ce6923cb2a6bfaad804ae364f7d7124536c9d79c2dddaafbf0df0866326702dd3864e5863d249daebf6d885a723892b8e2216b3dda02b65e388f46896f4c6a9c57e3282492af664be07344342936740fc6c5f445508c78d4c11c6f8cbde619bbe3977c00c47ab3c4b89690eedb6fc50ce3d61c022a6933477af2c3a7db009e833a347efbe301c2f8ef3c0448bb260328917e4e82eb23278a649970d93a6ccf125e98fe4f03b31b4f2c1345733c2c215faa50a148fe08ec4219d16919b5f6089dea11c3c8a3d0403a572c5f971d34cf51edd8a9e4ce534fd0c0e50864cde2907d4f36f645a94ddee0fe0b8f58085f2b71db809ae614057bf99fa2a78c962c47126189a188ab939187b3bddd9132ec49038b1b4454ebf548bcb7ab1145c0e2a994038068d5433bcd706e026ece69e4f3cf0dfa9bff6efaf9514c51fecc978d7f40dea26e2178e13c1b707864c69d2a2c41e0f85faf50e03fd6987ff721db5440b0234d48f35cef0e1a87e6862100b7f48728c890d8b92d4bf38426f867394a7f2cc99f006cef5c34791fbbe3043e9f360b208a2ac51d9dbf99af3a36308138ec1268d354f744799c1fe251d785ed60b94b642e878138c95a10fd2c3e5baf071b50328be6c27540ed0d3e3a23d703e2d4da225530e4bc503ea5ece935a2e7e4150f264ec85225d40a3355cf0fef08e7e93511c0761eef5468d0da5047a5cd1a98fb4af68b4afaee6eca8b5dd65936542c852296fd3df4dd5961cfcc38f3bae25efb16d5a24fcaca29814a1d87a926923c187d80f7d2bfdf84169115f53d45787b0a8d558f165b4535d33ee5d06d905a087bc48426e8c70c0cdea6314c82454dea1ba35cde773e85ccae6db733b50cd580bfcd288a4c4a0e8e091294c8582c0d87ad24538b620f07f789fb581ec90a9fb6cc3c632264ec36d6b0320ae27cb93d3265ab0f65603d9b4984cabfc622acf838c87167186feb985c6a7e0ac60c2541694768c8514691afebb5f0a70f30923617a45237a8b3471bcac26564d3b452d776dad4a60ea50f7d4d20433c0c4fc024e6e308914609b15956120bbc5205d2e509de206be4f2209ccea27ab4238cea9ab61a53ee477c9e1f6e58401bb5062c442d02935f403dc34a144ef7bdb690fc59fbf4df50c0846c395f271f7789881d88e9226127977b0b18de00f792b0da71137026b01877c76121fe2c04f8dff9f4fe417c670b8163e06e48b88627becd31caead49fd5c36800e2735a053ef162607b839c1b87ca2d150ec8616a0fd03f509ee0c46ec5d3f74c89f88b185efa717aba9c4bddbbe19600e17c9a35067495195336ed37c9e925a6c3dd1140d51fc93cf2bd5aa509dccfb7d48a9494495e2b8062552864ada787ab21e78b986b21dc9118db1a66be4b18bf762ce19633b06b6243cf1e9926a7e08dd1db14b53e332ba99d53bec7a04829c4d382800c2c991599e3d84680971f5fc73812842ee4e91dafaaa7066c7d5db620a3674c68b34fa5638cea1b745e001d960d13515cc7704aa7a90a4c7b6e130fee53fd17c612f7e29322bf0edcde157765b902aa6678e40a1ff0e2ea198ff0310ca78cf58358a826d5f362350bb9fc7cedc8663b7e8b2a08d945260fccea9f61dc7d0e07931f89d0fbb2575d8759ed27c172d76863cb473af4fdfba96f64e35f6de19e97b6eddb6217290ef341b922aa706514ff5d3c97ceb087e093453cac31a97ed86dd5a8f230d1d4a9f4dbe2aa6d22973726433270b57537198ce9a8f28a09e2fb54bf25f4b8d0830a5835cd9541b74b84bf7386c5b6fca7ca27021f82ec4d07d7d514dad64f90b9e9a4a05587d8a94ce2d25e646527db7f0229216671463244eefae386adaae40f08f134226a58aef92cfc7538a47be58c067fd618066f45ddbca7397d783aa213d8a266f5b6f6b70ac07f3653335f6d9c968691311dd19b31d5acb985e415b63aadc101765dd4211b07de73729d426ee9cee4c5b28535ddfda154129532ddfeb1037873b389f86719f6f7ea614de9014211d405a4c9a808b050329747511c089cdd10ddb986706264611af73dd6cd7784d8e651aa8c88e701274b61b2a6ce24ae4ad2021ab7c51c48558cd7bb744250a8bbc40a3935d155585f43faa2032ab60966ea6c4a16cda4ac1dbaa8c681741d372c8f0aa000efc7b4d9a5f3447aaaf708876ff1b2c77c58fa41d820cd77f700e762470d0ecc32348710eb50c5a8249be1bd77659dc9bf3aac0ffd3ba3b00a37a2b0337557ce8704bf59c7aec81ef5892d7c41f7e641f8a2738c10e08af99a961303873b8476490ea9ab0ae15b9952eb58d3052b0ac353b3f6149a9392ae91c3a48c7ba00188f4b090f9f2d7d294a021c907424b129e7721cf38353805bf833f740eb4713e5f110b3687eb685c72de0fc892562a2222010a69b1f3753afbcace8a638d71c3137144857ab11b233ae22d63873f5535e4477be0b8ea21e5868f8f66e7604d96fede72708727fa3b3612929341e25230983f5c79dc27ba2fb4c182c6fe30bbea7b45a701115def3d2ac79c5886d6571c58d2f72d4ce38b1f12ba58aa36aae553b0ea82473a7dec7d8746322fb5efce1a00f575fd5c4a3900302204d406f45c2cc13ca1561a3cf268636ab3ee616f19f1167a2ed001bb5a784ecf8bf20a648996ca1a6b1ad60791f149b0eaa9e1c575b31a886a2a6c1fc5d63c7deaa534f7e8c23660d671efee1d51bb48b2cb61542cd5dc70f9bd901d0fed7e251531e2292f8e1698482c19fa0e25b8a41838ba3bb756800579898f906a24397f53040a89c020e5fd7b17dd943dc8a8691af7985d914718d3a9445013f8cabca372cd346a48fc9362f1b063e020c14188a7df9b4ace0bc3e3e4e9eb083fa68987289380df8e15dc337912029d97a41360ce71a88bcdcbd8706330df0b72b0d1cdcfdcc4627b2733cf96b9baee447e598c587dbf6378a52e455e7d9e1f0af4c0a99af5e88dd46205116f4d33e99f3ca902b5c84f01cb7a8dc4987d3f8a9cc9a2175726ed9f31f7474086582eb7bc3f18ac554b79f95b4b217ac17017cdacc91e4026ca5e2381c768f8225881ab4b290c8f3aea64da61145ee8ae2da9d3f19f484f39f2d3490adc3abed1a605ffda23cf0bd1a90e98bef75c9eb65b6f84c8a9a4596eca6ad079f4d336cd82f80b7fddecfd5665091112d9cc84c3ea5787c112aefd56ffeec27e972bd115d02df7a3e46111b26b0b635f0c5c17347f8f45ff8f46ca90af93f9da8aa9c155f6bbf15f53fb1975ea716028f617fdb728838fec4a3815825f24e5d7ebfabce8681664d4caa1f660380af237ddf3365dae223351eef8390b9b468816b2cb268fe5060dbccf1ac65f4a5aeaa8fb232a091e40219d7567bcb0ab1ddd547b42f768937e8426c47f12cff9d9a84d50766b607b553183f5c20b61b6c1f3db82d2014b573fcbd78c672ff4cbb66d4593e070294600a7e3c513a5b225481f7dad4f5a4c0a08975b4eb01b239d2b2730afd153564448a8237567768a037ac3c6addd42c8623059518cc56c41ce2b96dfe40db66d90ba28c1dd23d0de21ef775c6a4f34ff9f86fe39ab8969d68c92ba13619bb9e859d162034db29e95da4b11539a8de0a559a19bedda6a15d3d95641b0118afbf283dd8153d30c7ba50fd1db70dcc728f223afa5147c53ae5db25c81abb2cd68dc8bfd09da5ab8a70ecbc91dbb3b8463a5be2cd492ca8c98aaccd725ce17af3b0e752ad3228c5ebaddc179e5b8c37eb52696c5a4025a3359fc12a1e497019556b2dba6067395210660ce06782548e09f7ea27e55d5026a99cd3279533f47d243f552a77a3c67cbfbae025b389082a57ef93d4a99c65f15978e363fe03f6e1c7542dc8f47edd0c3566350a20bb64f74e96de1ca4b029a16374ee98d5af4e85736b7947cd5671c5203bd1e50119dcdad42d50e48c2917b652db485f0c3ea1215804a4be681d1d26f29589e9940a80181b5e2f28eaecf28f4214366b6fa84169810ccfe6dc9cb15956849d79cd7267d8cd0217849425d9ad4acead8b4a51a91ce7be87c798852ba1a4e51eea1deaba2273224c6231ad467e2b8bf8f7ccb937e448d5740ac1aa0ba2f59c3bf819d0d07fd1c090f0843c62c4141d904c311214f19dde3691c5c0ef3f684540cbdf20379917aaf4c2b8d5c3d67d5aa4ec1db96b078f69783dc7a1bd42928f24b128d1c319517e71719261f5173b60e982d11c5e8867c9bb7a1f2a59073a56aba5532406807946f48437d35cad7eb39687ccf46d88f3e9d63cde6fe3ff3ec7b9efff121ca9b04943bb51fdf4ba03a61e1304f2d8a720691c9f9b58d073dda5b53e5b8834b6da42e4f93f6b5cb69001cc14c1902235bf38d8fedd44a07c0fd9d6a9e7cd7b212d941f4f6d02a7f8ef8d821fe954b11049622175d5a7432c64156c0d65f453d50cf3dd176a95207ba3489c7574c3b7fb15587404310854b4ecff845d053392d184eeee32a17a1212bac361d0ca514532cb4ce8f40ea8bd631d3706f58530a2ae97bfc77bc1b47b456ddffdd94e6bbd907bb561c0c20a0301899f91873fbb5cc8e2c848c9b7be1d62a9515997a438893ebf7bda377018b353121791351c67fffb188182c52f179162718e72d2a46f2d079f0055434d174de1e8dbe09e4654ea689a5f9c17b097e39292d85b1e0e321c6c3ce9c936e33164181d5edb8520080985f9af5e4e8b5a2fdd3c57a6e11839dd46ae04ddd5303fef72a75832e96ce2cb94f9f59c578c6a122cc417f172fe42520c5a4f9d6f53fbedeaacfed60f0b060953bf1a9171f1043b155236d7528238349a90dfe21e8665b3d507823ddb4ed65a29d51156ad6ca1374311e3dc6659bac8797f4fcf870b818953298286b2b6aaac7e272db2ca95472b7270bb6e04a34680253bc47ba27c0b780ff1b9154b4d3de7a3e36e82a8b8138fdbd7b58d0fe16da20cdf0ef91a0e85ec7ed4b65451d850b8ae93e1075228ecb9c54aac1112fe8afb819f9b8da66e66604ae093536ce2bdcfca20c3e97bc6bef0e02959a507d117a43d737156c0b200f31401de20675a4234494586bb3a00717bd6f0b5cb7756e7a91a5ef381b261e48ccc8550abcb2e9242ff9f0abffcc3d4fb1e6da0c9ed59960b11bb5bdfc03b910c7921c1d9960f9ffdfbb25b15ade4ffc2a7a593825a29a7be80acc42ead343c785688f7878c4c7cb8f1b1d80352d8a936abc92537ae8219a4e6e38c6718b8d1db83dcb61e03ee8deae6d51f5f04ce539b15010aa599abefcff4d07784bb3f3491d466fd9b49c5f0e40c7c2180b593465ac02d3f7499a6fb75ff5dd91a5ba1852514eea1ad403ed69e6d4271ff046b16c067176ce9b40085fa9ecdead8fa9aaba89031ba96783704a78619f9a17ce0861d6183c41b6aba464823f4cf8d2d58aec1c3452b205402bc118a30ba0e8a106a2efafe1beb463ec813a2e84013a2eb440da083d107359d112a214a079a10ad28fa0ee913a275a1095089d11b818e318a208a107ad67b88ca754aeda546caa828c5f715a6d148ce0574599068f2c511f3bea864b84a02a984c8f70dde1c298cb9a9832aa984c8d01fbb6e930c34070fa71222be1f6c87e759b455954a880cfed851ca53bc4243981214966becaa8090e12373dab3325209915dca1e67a63c52cd5242e40c31834eb433ca620e251eda68c415c0ebc0d0047d08bdfaafe7a083ca1cfd0f9fb87fcb7e34fddaab0e31002831c3081d421740cdd0748839fc9e5241f83460c62c59c29f2e2b0bed61af54894dd212c974bcbf913e030b559b91cc512b28c2447873d04db3517a25d4d92936135bbe1d0f4d2ddb104a34b495d15d39c64f743030d4e8e682d59e89d32e2ba8fdf47d7d098da02c543cc280d204968545163a0fe78cd668471fe421ae03976d3048008b57341d9bc249ddf142c2009953c446bc50177bbb3ac330db211c347e8f09205a2264058fd36f5a3280b6d5ad1021ca937f576c168612e75100698a252c0cec5a30f60d01631adc9b39c2a57c24a63b0eaab0d00329be5bed3a45ae149fe8c35d01d5d2b7e559960c1d6030b2c3f0a82c82e72074fb704eaec13640ed36311c1f0b76d126a00dad733f64b9466a89dfa72de2da868241f3be676d283cc3758f794edd19ccdb565d092cf4930ded9f26c199481c0db9f39c6ac9b5aaae224448c6ac8ad89474d530a2ffc8d15752d1456fb25f32b08f47113ec1d5bb26e7ffc283c1e10c3d1ddf6f28249f8bd6c8e09926dc95aac5b99778e47744749335c1d411206516b30a852c22ab6d5d0374b4d5336b23bd3f936cef7a25608e804afccc01eeea802e195a8ad910e56ba17e6988f2b53348da8c10c16611c66c88474c95830460666cdba25f9c562887eaeac579ed2cf8ac2cd119a97eb4d7188ce931ba4f0597ef8848a7df42fd1e741e3ac9049c430d683bc66950b7c36919df997cb5882e910b7976d8ce15ef426a608acb4e7bfd196c37b2b7549eba8edf889aca8fc8a01730a23d8e82dad5a751b87d90992d493274829c167ed48623f34aff308150ba26949c6bdad7c1f544fbf71c7fb533110faa3f689b60e051f5dfe3700d2f9ca2e72238afb47019d3610b9692889cfec678fb946fbb4df68146ec504e12ffe156eab0fb576addc27d5ea10457d4ac04402dd37b3a2952a3daa8ace37a3a10f177c456b41fed7a9da908c09e724e36abf79dd6b687611da9d64d51517fd0e87d25b6ad9a390f9de1f1d0c0b55c4d7aa56e9845694433d0e2ce4b6977d8d75ada6516960c928bc3cc56097289d5fe99bb172b99b0d1b4de0dde64a1b681916045c2bc7ca84f952bc9f093a0e387d0400c58cc35b19da44e04fb974d4db4ef6f811f500f3c8f5d3d37f569a0e66cc7318c3763897aeb1e2581fe65bbbf38a687d0d50ccd0ca0c5f2a034d848a53ad17aba4e6291a92d593f02a2bcb2cb04d169d42c55128d50635fcaa1633da561e27ab10aa1154152594c29a8c62bc4e3a5dab2f4719099724e2b1163fd9e3b38165080eece699d1392f224f1a9bcfd38bda2796066ab163314a9ef809f1cd8244ea6d3d00da0ba0240e2f11a2d12edf7f942f32a8663060cf0d0d70ded7cb23146e9eac577c532352ab2f48166f3a550058b1dd2738905d2c721d931f66cce9d8e570122e429a0067015fe0abbb8f9c7ed7f56df830f22b2f80fb3b7c5420d36c63faef1e0f07728b32e1464a1002553e681c1c1b438057b260c757e5a99f44ffef40e0515d95fc60eac27411e8cd693110999504f663c038c14250c6479e63adbba2e08b543fbb89ff7147d5596d5983bb8da62f97e8053331456a319df67d71acd506f5bc7a0b9f74a7c0bc5dc42cc5175607340fdebdf14fcafdfff42e683df1a980e95e64ca9dcef419cb4957f2de7b0b0c1c8327aa0563aec09495221f6722ba23cd84ab57bc5938b121208733ba9453c8e2f620cb65c04ab421b39f7c2c3c07da9d556b2b2bc4d372ac044e786c082d9ad3913d27b195ba8a4b8e761056a9218efee2ddf2c9c7576fa4521e7b0ff330aa218edb516bb397370595255ece43fdd5444882ac20b086754c5df680284b763c1678d6d8364a99a452c89e8cb406b29bdbc1fdf48acd4ad902100baff283a3d79a2ded0adf2e3ade65c24e5da19b74a9909ce8a0775bf0851f79628b14055c1091a12e81e4ef662b48f51452201f42426b610f8340d95d2fb77de5e0324c6ad549b4dbe4e6d79b5b83fa12abfadf14e982f74c0d8290452719c4d39f9680b287bbd7364bc49a1250021eeed075a4a54b9650a659a7b2363b1e902ca06bc084fcf1bab1481adb8228167444fee6f5abfd882bdfa629b3ea964950e0c8b7d975cd876c77c7f8333b4063a0bca7de2f886dd5b6c20c63f62872eb0f0bc5c1d9bbdae8a07e8a50db2e2662e24493a1c77248ed02619124eeea74b21bd6122a8962c61cf912b140c335c3b352451a52221f5cdc9618bde07d9de7ffe069a7819f3bd15f4b9f4109bb3923a201e640a0e49bd25351dfc589b89c2e3beb05ab333f5831ee1f2d5f9a457ced22cf0d9011db583077f7425a22eb4b883a3890b6b1f8f72713e29d9aacf7211739d1ed4b7bae7e4da827aeb8eec70b86c86775104a73448735e86e0d89629c916c745fe305315a128f8c957fe3b29ef9455c25c72b14231aac9431893cd7b2d3ffea7feb57b74292da80f73162a7f0953e9c582bdc9fa6ff6baec58f222d7493f9cde1071614927b0a63c7aa44b8dfc37458e4a8761c297ac4c7b69b530f9d9854d1e8ead249dfa9934ced2240456076e541c7dca63690e10a5a1f9f361579180bccbce93d2517ca59fc310b65caad3bb03865e63435c97639fad94e113bc81fecdaf675ad5f23c7f406fd3ea123aafee694b608f6ac9c5e573052d66e8630e39318ccd2cb6954adaf92705e4580b874a7f0996f4241e139a8cd63f49ea5de725d5119063e096be8cff9de679f77906fbd9662c4abe07e71e8f15769a8691a1a335b0044fb551c1830c4aa5cf10a2187cbe8aae8092a1a2e5721216a6d9546a30e3e3fd54939ca297f4bad54cfe3cd19e4d18bd03904dd8785e27ebc3238f0ee0e16c65938c4480a4cceb12e09d86d5e7761536fe1408e4c6f0cc8776bea87e4a3764c26ddf6180ece14a6171c0e93637afa3ac2d8b8b9cb9aa2cf17b670318a1ee1c49b86a5052605f90394f9457b958110b6206178abb73e46b9f7a9ffbaf36c6b2bbe05022f1670cd1c9542f8597709f399b9a36a11e0651fb7c9309efb0a1b2dc3e1402196f4ed8767a0ece7559de22522670dfb67605ab103ac35375a54a8a3a5fc872e1ce8c87354f4c21da94705e1b552f641a741bd80ad03d71c523d424b6868a137f48187c1b8e2242a433f7c0361a556c7ce24e7fd387cfea0c2605eb24f42e96c0565a403effe1bccca4f140dad1cffe07ca93c9e588f603828466dcbd6489bad73f2660ebe48cf405fcd75637802f9befbd578fcad2948ae61f59747ee619185342ba543a5d3d4c6238e73f2406063724b2f54e6c47a117ff23e9acf0c0d9ee4a3e548af0ccab64285bef30575a0148a0974a5da996c9efa268ad6a6c28c266eaa4f7005da3cf0b0ac501068b89600cb147f0184fe89d14478293ef7fc956ba56fc6c9a4cc7744db037e045ffb56fd7a71897cdf34ddad61f97b420ec1cc682c410f33a075e508e420c552cb17988fc40ac1193dddf79cf0512dd4d0eed49d79cee5d0380ae4c652631b31b6ce8ff45b49b468d856dd3e0aae0136ebad091864d829fc42a6815d1c17b76f9aa07a877f5701ac7d23ced1309266f9533d79677b11bd4a07d2329108afb4969ca5ccbd575f219de5ccb0a4d5509e3b6d4bc6b80fdddbfd974d36bc4e2a6bc892d6835bc42cc7a97d1aa55d8a71f6a602318f852ca9ee7d690fb476b39c797a1ea3be60a21cd86d80e4dd4da4cc8626b58a78c1868fad2b7106f796e2d63051dd148319e8eee7c2372a8b9f78185d583fa110e0ed1b1727953b51a451908de53f93790d44eb08031681ae3a7cd3239cd9c20ed093ec7d8943adf3f21c02902bd69f681523e05f0631aa78f917c1eb490f1bd34e20cf1b3f197b4dfa4f14eeccf6cff65f323393ed2e9974efeecf227ad7f627f26f32b5b3ff9f54b4f8f74f026b7bcf6a6ac7f663d67fb235f3ee9e9cb2effec7aa7063efd480ec9ed4dd9dfcceea39dd790741a35d13150ed944f5288a12c9a8fcd14fccb9f95d5fe03a4ebad2b9aa8b8f0817d66879070bad369f8979e9442cc6311708e613431a31342e4d0d447952655110058d436849cdc8e02c3148c4fb9efc9772e69ccd2176ddc5938ea8088dbf5d940c59fa19bc2f144d484b1f29d9df34d7ffed39ebcee4c8de694d69b065af3edcee4b93193ea4b296de9d995ef6e4a5eefc98c42b17c1d995b434ae947efedf8a61b396fc6f47b51ee56f4d6894f7923920fc47a783a1a40c1c9d48a0441e0606a149433067a42c027f8cb0cfddab05711f2dce01e76d449c4fc5188afc0fb8cd09688755d90563ece1ca31c3a8c0920fc627c978dee59d88a846c0db8aa14aa5c2d982ef6013687ef74e21ac59d058a0b9be8a80aff6f0958d694389368f04f4d95fbf1d567733eab4a52d2a337de18dad6aaa800eb429999a996e6e6092264c9cc1b7b092adbeeca8fe9d35e9c0a944696df312ea8668f980e59d225d94addabfba398137d9170433ff619d2d1aa8ae0ca8599b5b11ee8a313e314a9dc31c7974be8939a7a501d493dabad9cbf71d88ed62b567b66cda28984afaed281b627fae05a2e6960d91dd5f77c679b58f26ef4d8f1717bfc701d3efefef52dcb49a7e0cb20b4acd80f83bf95ace9370a5976c3ba2319bb621a5cb1966ad808e6ae280d2a89177e1cefee841b025c784d8d97cd9ad0358766a314f5334017ec04698c0748fddf74e4cc0b7c00a13f0a02022743c07dffcb9cddbf2b1f93f417bb6a8ff9fb83aedb97e1c6b4f4bd816bc108865c5bfdcf95f915e024262981944db2002f897409842cc985853bb0684a3fb93419ab8ab00d828482c5c43c02d43efc0a4e3378093deb780f1deb79173aeb0b5dce73df7c6d508fd5c4f447584a4437728e8b1c67b312e4f147646f8150b0c4fa66ed45b85e564e2c890b19d1f84e35357a0a84a74d09321b437a7754045465e24456709f783a5ff9809302f5cb402c8fbdab23bb83fe8c0bc23a93090ad4f820dd9032dcdcf12c8b1af62803a5076f1f745873fa7b19ffea9a608811e8ced4f1d6be61c2de5a84cd22ed62b89292202bd6151d706ad0582963e10fea86b412316ac8a117ac7313e0ea000b2ea340e19a7faff5ca09a590d620b587949328db457a52896f488839d93e4ee13d5c8015b8a49079c895f4d93492bbbc95aa908fe0562cbcca22e84026ee9accd68c04f01235d97ff6f8881fa1883345a2c405369880254b7d6481a8ae864cea372987fbc68a078f6bafe7d347131af153508a56d2373079f1c926c7107abd5ea0f07db2afb5f1b28773d90ca153d1c123b53d012d1132c392dd1914677ae9257b97822e74448ad237f312a83159e6958657d2f5226fa5cac074b6d8120c736bf4d1dd9938b95ec6cadd1d5baeb704e3da821dc38e98ab15c1a578c64f701ff5dc2160e1e22259c1cc6f466291979a61222aec67cc65661a478c2c6c6be10e68f7932e7dd05a06395ee632808921ac42189265c22e3d1a6364816e7a571f9e8440766d28943894e9d9c078a27c416a4968cb8e882c673ced55125a34d950b4f8f41e0f5c0fa9fae295cdc7e3413867adfa3e5f3fab081abc01865537c75dbd9d4e1f1645a323f5b1696f70894dc1f0105c608728915c16327a279afb937eaaa441e8acc281228c20b591591cd3267b970eff719d4d844a828db1875d77650596f8af3df66e4a5ee01384170ed93d5247e372a503cadf82e29febb68142c558b344379cbfd0e3d5757788ddf0965d63d34a3fa9e17cd4f868117490d8a4f9bec1f20160f157b298526259360e023dac675f344f178f586b6c945da4681d84e221e5fd44b9c37e0e78ee61f129131dc2ca26829beb29bf5dee0586f32f81e2a30c6614b8cfe13490fd6a8b8f94e82a12a97e8634c045046bd06a27412d8912373ce0e2a30060a47e43ff5a97aab3a3a7d879f83266e0ab5943bd81a68f7aa6859ba87be1f9c99259de5d51f88e0b5c2c11bd9c4d095199a45cbe4d38b8bcb73a26642839997002ca798d6e3d02189aedc909617c41032896899870c6da146033946796763db9f6ac9ded401739f98fc441f918a3235b9465021f3f9a2e6a2943ef0a9b23543b9612d30118dc18fd8c9df3ef37d381e586af1d336c2e11a5856402b0c1c41293308df5448f58135adb0e792c0efba053042eb52f0342ba9a1ff1ca12e15e1e8d30904277f233e1baf94185b7868e4ed05096d8ad5b4f1d65aa18fcfdb03f266cfe203b929e1d378b3a1cd116f8aa3be4bff89e5a856dc638dec7c09f8b2d43a1fe9149190a31f20f65f000eed5c50712d01b7078e2bdcbb78816cc9f8392e0d9535e5ae0da29b72c6562de75ffce4b216c5f96430adf9968ffdab45701aa46e2f25c25532a8943a5a7630f56df342e00d1a68aac0f28a64e2e9246b279ccefe345b64c2e628a4af16ccf467ccbf9a30c33123870bfb726e19cb56cd5f03c9d40706eec540c282c0ab913041c183ea9ddebadcd4d04bffcdedca86ea03624af45e978e128008bccb9bd4004091a7c171cae5e47e5e62149b5f2d76b5e07d075bd244050c34bfe9ca2076848afe7f3882b0fd735f003539b1ac44a84febea0582ce019b25ec078c514f05b89846fedbc1b9a57371d0073a4b78eb870efcc5cf63860ccc43c1e49e839f83c164161cbeb0c7f33949b5cf78652c6f6f86d7f46b6c61100dc151ec728ee6f3d0495e32c5fe5de912c5e9350e5e5f10619e6a62c4bb1ab28905a0093ca38789c3c68f30519450f5f813d9d7a770490f5f4850276453e4080d06bec7a186bd8cf004470e83adcf46fcb2d4f56c251d738a276a2480def2d4f402248e89da4ec4fe029f60c39f0836d39eabfbacb15d79bdbcc61e795c614fcf3252bb659f4ea80175d0091e230536b7b31c034aa269cd7dadecae996069879d501358c8d40f5019d46ff37fd08f99206d7347688abb1a1364f574c356e94e2692d96aa9ffe11307ff40581517fa2d77bac1fd8eb589145c95e7d64b9bf31dd7ed481662037462d5733efaf2103870b83a53f83200b099afd2ff7035d48121ad539aa3e0834a036875ecacc6043f615edd4b549145288de2d60d01e2151b36a9966e181a3376404d277778cdf3a5577c21f043fbb9aa09cbc087fbf8d45319a3acfed9dda958a27f74ed85797e281a79816967335feda7b0067f2ccd6a6484eb184bf9373396d487f5ec4453da5f376a483f9c8c23f79f12b3e81af5faa465a07ee4807327780a0e9231a3f49db5bdc5963229edc5e0d6cd604307773c2f607941ea51c993b17515e347796abd12416e86cdfa47c06f0020a39021e0ae13079d9296778f3690ac972b798fc02a129d5e9d809838ff03f4bbe1b219652933b3a5faedbc37937201ca1dd44b81f9a3b28c9798884415234c9950fed7d1d893479caf0653bc04c9c8c46563ccf4fc18ff4d0215a80904f473fd6c96eb8619c1b1b96057646b6868e15b290c998edc5dc84e4629c3e46da33dfe38776d1c6430e0a9cba9b27903af83b291004cb9138a87d29401c40439dc0dee19d97efc9488726028884b515ca1e899bada646d8060a9a4a29ea4c418925e5d80a5e4dea91224c9136a8ef1445ecb88f0fbe3e547af2ff435b8065a77934dae1dba44a5c9e65edd98303887fb46b905dc9978639fbf382c711de7457488fbf91923ab0e7327a34e226d52136aa8ec566a93482900b3cd7455e486c8a98b8d88c6d9e46d1c111bbe643804bd34d50b1ef14e890c925510cd007002a86e0e803c4f3e652622c8210ffd3f3a08a8db75ace51d09309282abc9d3f917c1807b1b6e4cd5d6d90f1834ed6c66584259d25045bb2f17fa95fe4006586800e4cc03f2a206bcbc8df2ebe7226dbc4980aca62384ec2d54b97855de03276b728fb9b55be04df6d49144eda91e186ee37f08832dd8b60d47d359d40fca5d47d2fc85a617fb4fe320c6268c4ecacab5422fa4c97ecf8dbe644ee71c3f41ee78580afd203aba7a562cecef9d0db37e47b1b9d56af1a5b1b18a9b15a21950a6312de047f4249b0b4f7fefefdd2e72795ba7cee801f954031ef7eea3e0995e1c6c378151f5b45b4a16c47089a0c75821da2f4d3c8cbefae19c73e85d7e51b571693d8d5663daa2718af130f88d66347b4c28d8b19de60e7ed894bebff442d9fbddafbef85ba8223f62861b4d59e4569a5acde1e51a0d69d1b0a51ccdb5348deeaf971951ce9aa7c5dddbd3d18d3d0bd47bf418915beab706ac5cec4ecde985aaa02e5e89dab3ff83c082461cea3a336a4cdabb0b73956acf6dd923ede2dbef3d401c7a693d0c4d933472d06a526102d1e40f0a31f867aacae77bd9efb99ed536a545cdff554f96765d633f3a7a0c53c6f2eb0f279392bc0537705f887f36bb1992a8e59998d5081d1f07f9b5937225e7b532116873595378361a6da00c286e9537a4397eafec22fa7b6427d399a817cd947b081307dece7682dc7b387ab859e71d4da653285df5ece579832b5e92d434f25310ba272bffa36969fe3ae61e69ac2ef4b73a4f0e594f03aff133290c6635e7fc2567962c5e3b97fb118cf11954c3cedabdaf03c52484f307d1c70574666c31c1e7ea9b1b3c0cbab67d193d59083b2d888e62e952331105d0cba6b95846c0e0cb9dfcd62435914619234811e3fd0e54db6287e593566b41ba49ac3f3de006a9d1381031ec055f900f9535488113b65452e5251459602118c845881da6cd51e282cfa6b78a4401f53b2528f37f5453433860eec89266b12d71a93aeb5a45eead1ec161746a46db1eeacc4b388cde615b0db8cc17265be595873c9d168ef5a500c2e2d0f617ba6ec1ba29b858b47c9d0cca0101c521dcfa4beae978a4350154d1e8caddca2ed4d839c03672b358a908c602d9feba1541de58b8e7f2c527400c892827e27b52fc5ef9ceb2fad783ddbb27cdf7145b8edc93afd641eea86386f4a4209a0815fcb03dd82a43391f814f419c33315264032611c8e8ad209969c9d4cf4a2435a6aa5365a2069f00d069e6fc3adac1781b455367a0c3c02c91a90f96236ed23b4f986022b9b829366ca8caa41355de516bfceac6bcbc70019292ad764218b22031fc7d7c2802de4ec465079fd9de6309f36f93f1cefb746e5ccc4338ab76c82afc3a79aee91ca5bfd2ecc1b8356f5c1cabf1033c9a9ad45c23fbcecd0048c54dd656e1a83015fbb513a37025e61546490afc831f3d52f62ce2f1b92a85824a8d43f21cc4b7644f130a834d43deb8d87778804f204de4e8c42553a1ab8992093b3777dd88266d5a22455ee365a20f58495f2610b47a29e00ad52c73ffd21bf8956630805b96540fc9560c7be2394f645c02c70afb1bdc4461bcb9d4005f9d84f98899a155d9483d50b73fd1922cbbf83cbe5b1cf0325ce61064448b323feb00bfd9491c8b436bd5c559253b9d5e918619917372ba8a6f92c420b043f8443332cd9054d232d99d0947650774707b01288c53270e88ae27590ff46d54dade5dd42b59cdb14c76ec536cddc37ff87f38cfb8ef2ceb29f2ed62e9bcb7b20450d1d95c00b2b40ed40b0679aa5788980f47a40d72f4db4b69939112ce6fd92a54222f71421cd046fd01851b51ff628c651e353f9a30c9b59420010959e3a547b17c8b6300b49c8c34e2140d8b16aedc701eb1b1e49780dec8fc108c36a605e5780b51a2df5247f09ac93b640300b1d089366963c85f4238856682f2a0199a966d3dd69177b899af7171dcac58e965c5f071ddbd9578767156f30b87218399e87271d2d391e482135de296111062f605f3db38aa0fae00d6a0147f6a47ad2020d809046c2214caf8b887d91f07143d374ee9ac832df7a6caa340759d3b20b505faea5e4701461cd7f8786840e373823a132c059a195003af01894c0dcbd7e020d6186edf3da1c0e2b673b158466d1d770b2e90f9b9ac76d540002cee7b742732417820f524ccc25ba1bb23d4bf153f24ac702588e34e0191e506f6c421019212fa480da1ff6e397f3404b1372f522a4057fced0bbc9e36ce091477bcdd816577fda072a3e81b786b587751aca1d2fb0dc0058e27f5a7385d1f399a6617dbca536900764fe502dda6ca9ada40f56aec50c1a6c0cb9454a5bebb4cd855e33253eb519549c9b90f52ad59aabfa7051a4ce8b85d04009309aadb1cb507b4a5c8eb215f3e3883e11fbc8b406af7f0ec303ca3ad879075c2ec1d7630e8fcae7765d38be4176e8321a399de81283fece276a84556ac696c302f19e6bce7b4234e3efbab28d0377ed9af70cff65686466f66af780661956a6dbdbb242b2af32e860fe5a423f1022f399b20036102bf462a665e293ac32b5d09b2ad28e5ad0621c9641a582464de0ffce2a49dbcb919f366d744d904b9c8b3f66080805ced6a8ddf5e76b2c9fd078817eb964391d885e01ca882913f8c7687aaef1830141482d38187d678769220f9f8189fe7b6e1decac58917c02992fa7d922c091956ed4d0913bad35847efee394e55144598cdb71c133b8a08a64ab15dda293610b24d30be3c9d05a08b51819b60a63782165e4d42de80b5a11b89f6892dfdd19c149ff98149e0c924b893047f462e1ecc2ea32b53c7e68a5b496b44a2cc70eece57771ad4bae9bf5c563377e268835da6582d7c9b1aa5af7581b88394591394c9cea7dd4524b70c4ac55b53e6a6b4ec7c5ac4f351f6b613125f2fea477ed09b290ebceb17987eb054af1f194a6e3462ba72af118f8c4a170c3dccd30cee7d277f5600e4926e908a52aa5bca4b15d5f1f001762ec3152f379e56aa52d4f74296be95f0f96f33befb71cd2a13784861a77575daf58a9f8c1c99663410a5c7bbb43364a8563e49665b4e61cd5886cffd295c6ea48409d5ab84ef9dc3a69c71cdc2f5bdd8330ecfd9e0ac84236f7b6ff61d9365a1b0c52ca2ec4ce329a2fdeaaff6eb1afacad344154436f42698a35ba8511f36fbc9a808d2f569918c1b73a955124bd81122349f0b654207fa5fd46b2b213af66d866dbfb946c0c8381db881584d429924617f0c261c81ec769eeaacd73a257fbdeedaed4438a2452cf917a9a445e9c9454a7d9a3ac9d5781ff5b44fa3d7e605e3b0562d4cfb591a21f0118d5dae8b373514faeca7d34c4c6c07a49d8e5490dddbff3ac5c3223a8b33d06101010b2317fea8e7d5342085e064be6009c5c22caae253e532ea8335e5d1decb63f492d36a48f42572d38f8bc4cf63b551885612e05892696566873e4fe02a5e5f0c0ff54c8a9548ac771fcdaab787bd974bb6df42ed378eb6cc7bbb6a7b4b3d279fd847acee120f6f8cf3f23a6680de8ef8dd21bf37ccac07377cbd165e91176c839a900c10f44cf555a0495f48572942ea506be0eae3da7835307633019cb069c82521e5514dfea9a24556d5d2d31481089637bd388b3562624c7a8830fc9498de0908612fe4427b92c081b2de038f6854e92b92df2ae1f5e8462002c569e0878135b35561262a688654404a96ad5fee4739255c20e71705bc03459959eb7e06b477a883a45b7001ab3fa49f09084e7f421724896b7c0bf81b77eaa389f136fc1e9f729c24c19ea7cdd62ff83708e95bc099a81e8dfc66d1a3369798f620f7ba59a7de3952aab5a1fccb141d176e410b2f3fbb1f5629ca0e1ac4fdc00def13493c6a90e7673b39eaf1b976196284aecb72dc19d26da124a21b9fe0a3a4c7e04ec48070596180d42f23d2f557fca33896183b12ed3ff813a4080789dbdc7e6287c6dcab7211058c02cc4605a00d484da47c90a0bec4c9c6eb41c53226b5a22e94cc39668250b6ac337e18193d0b3c1158f02fa900a714d30a88d29950394a09f6199b72b6b4e8b71f26275a47a510cc6a8dfb47c62643595776df823db86e3a8cf00115e745967c6507cadc4c284cfed7d535d7fa3a23060c6c736fdfadac78a51eb90b599dc28d5bdff178c52d8d5a48fbf34861499f20691e409f0acd0c579918a6fedb646efe39fb3077c5e70bd4d6234c1c965d32789b191dbc568389edff84b48e0c932618e0b7d94180f41e20df47c4898ea98c6310ca8c7b9930c3588e43d1a0c419c3bcd48430cd8630890c4858a2e4ee0a8b06f74dc0f8a1666da0981811e6f861e020f52c8f8b078e0e7ed1e119a0ce4a3a148c82c4d83ef67aa2b57e79e6572cd129c2427522eff76f157549b7dc1d47a48783a9798098c2f7e15a42501092be102b2339c1397b7da18275498b6667583b78e3fb17dcaa2a37afe0102935373a9d255b0fcc5cb60d0cbc29b61cbbdda5a61eee64c07902f7e9bd99264e2e25aed9304442ecbc899379728b5d1c22655baffb051bd7c6721d786324d16db6862fea79820514831807afa8d67f97533f7ae69d48f6e8275ea10e7fe2e3fcf7b2cb6d7515a0c6194d4ae6391159b4ccdddb822219ab2d5d054db20ce71e2a6a1d6667a0a3e784af2b2cfb492143a6a53cc15f7629c87022d84bd8eb93948c9208418d7c8ffd650c6b86aa4980f2baaed4b4e49a489ef1d1cae1d8d0ea928a13e4ecc7a170851aaaf881cb52298929ebcdadf1575abdfd0c2d956a0b4c664216e164d86178fdff1950096903da672eea4a59ca362e28ad78d68ba8b08bd648c079cf5a33ea1f5e2bb00db1f4b29d0bbb3e3748a87d33891bfaa073bda5b0dd43d43ce36a153c1ebacebe279705d6d4a045366088832ff5db0bc49a1a286f0a4f8cae9ca4fcf6a37fa6e2a888be79f06502faf3bdf89aa5723fadbc7449c56b84c2998c170728f63ad075720da8cbb21d875259de95f28b75596cea5129c9b281f4b9a774c22d388d0c98f5c2c5b9f05a23a89d8828d6b429f8fbf64bfb6da2ead6770ccb0927e3d2e0e817ea8a7ea06863de58701d36ce12ea3511e807befe3fa72cfb7a8470d8597d9d90d54756b5dafe870344db9dcf8626cf0fab2b842ffce52313f7bceb721b0b8761c0fb374c454a9444daf12d64041fb3409a2a872649237d1c80e91d0a58078ce84b9586fb846f2a9f86a2eb6d811ad60f5ad87878a1d3f9d98de35c3cbb5ca5d423200bde0eb007fb7acd26df8612f24353e95566917c181ce0a401ccb3976697c0d4084551b6d9433246285e439bd16b3c3bb9abaf02bb1a14f48721998fee6244c7b065d1259e343095422bcca4e4159d98e57f0fc0d5e8c39fbd8e7bec1bccd77d29b7b361a6dcc1a7034f5d1930dcdd906ea2a46427fdb72ba9d8de23d935a9eb8681ee2b8cf3dd638db9cb97702109f135935c10765c76f92f502c8d92995c9fd8fa12e0e76bbbf8992e442cec55bd8f5420496de53dacf0624bab62e7c787c44bd3c3b4eed79bddeed131021b4f50123de9ae873901eb3c5fd3842898f8cd138658d0b1b5f96cd45dce5859e3c7329ece240fae25832e44becfc52ebb4bf8a02ec4d5d519d0b19c40405638212230d27dc3888bb84e7bd28d14aa440ef097091e46dca188a393c377f93fe4d43cba7ace6b5b5383137980e1476d0f1296f2df6d9f801e7db1057714a6a3c7fd16fe4ebd484146ec32cfbf4f0d88285dad2f336155520a260924b35296b4e73cefa789e410d81b7ad173e0f7426bbf51e9df2b501bdc9cf430a348b3f7c91605e7d0d42403da44663295fc3c13dc69632f8aeac1a5092bc355e322a7b9a8c8f292c685188169aa66ac3f66d5bf23111b051ccd0a3ff83084511503467477ba7dab8eda2742fcbf2f245b5abc183322758cefb64526e2dcb188de2c207ad30c3c6edfad06e1a1acaf905fc3b7c7e7e7d2339c63a21d95d9a8364991a73de448ee740d49f217931c4e06421f42aebacebcd32d6ac3f510d2aebb8127dfadacd0a18ae5160075758fc8087e549b7734f2275d09598da1d96b19aae30a859cc3768c7302095b2468132c968dfdffdf5d58a1d35daa5f01fd119ac8a503bb49ea2ef219280f3808e8a17a7027e3f3accc77871d8101776af4e8905960e8f859c3b5e226d5a578fb7c4ae5088317924767c5b717a2a0df3010c451e457be305544499cf875577043f1667d68c711aa67fc388752622493c5489b89d6e591db02f025c00316c4055206ca7310a82125028c404acd3e67f22d009b28d5fd419fc20392622115c3153e73b4cafb0f5d22aafe187c204c80b706762fd20a2079b53b65ec14c1943dd9e9d1073b1eba17f0015371058be758bffde93311e024cdcad09458b94159f5d2299b9e75801819b3658e70928874a29704aa0eba7ae670b2c3573af6560a6105000c8dfc685b5d2483ad039c365f4c0d8133b7007c74e49a108b944051589e73583d841e498e9d55737c6a5d81a3900b2d5ae230fca4380eaeb59ef08cd89fce03528a42005f8e8b96952af3203d763e9673d630284bda7259734442c918554a7afda84ff654eccca024add295f5b3096aa4d07522fb4e0efc1b3d0604c4602d2903bbd96b264b30cf7ff4390c4f194205941e6f0254914d6e19f7ff6c38360500003d3f18a0fb3f319c002e6ee4e4dfca157307483ea3e0ee3211634fb2da4edbdf7965b4a29534a012b069a05d7056e135941b833b56addb0955c9f5dc3b8eedc102c768655830637ec530ed6bfbb0d0d1bf26b474a8d93423a7bba1ceca75b8f48c4831ae4c06bb06554d8a6e190ad75312696d2cf035bebceaf033276c03de0d1a3944d3ef3d89cdd1be70966c241d22257385a742d1c36bc59562b92f556f6c1ec2090be53873d1d0e3677cb00e03841a525d7fa587d8e9d0cf48b23ace662405a6e168207842770d590ba43b6d6adaf8bd56a32ade0d6907d5fee2220c2dec1597b70e0eaaf9e5fecb99b417bfad9405ffb3c153c308ee6bf64d9059c342fe7306163da00cf9d323aba29a7b39810629a729850e2661593991fc449429c1400306ec84feee4d1b9a18e1bca4c403ef5e2eb781e4e025598a90e8a8ded0e96a5f042c9cd71590a2f6eae92c88266ba1cbd3acac1d57a1236c88ce8be8e7e91159b7d49e687a219406fbcf1861b34f2b34105f2e913c19e7e2f33501bb06fc9cbe5a6d7c5c034f2ab71d492e348e4626decc4c1a31c62fb928c93982fa6822fa09f87a7615f80c438a9336722f6c974cced56c149d86f1cf7f570b07ffb7838d835c8aa02e64dfa552d783829ca2ba78be8afac61084070e753ec37a6d81782e07ab3c37a38897ef2e5d7e347e46169cc8a989acadee6b6d5adce399b9b23ea19a047f4f0bdef328f0f7bdc1f7230c94f0cc6cffcb7e9a739fde424bfb1545b6e0427741c96c2054d5c75e3ca0549b8a13b7154e8486efc29392dc44270d9dc8d7a97a65388af7999c76d18076ea04bd9e745fc883512af08240b410c29d73ded6efecdb08ff85a1856ce4e4a29dfbdec5279b2444c350e167112e96f4d1cc5af63dc18c4abbd07f611bf2696dc871ec58f41c4807b147a0b6efcedf313fba08d6a22b8d439285e350ab5cad1544a9a515abbf99d5d1a869d3704af9f61c3daa1d8475411d13ab0fdbe72afd531fa6a268eba1feef54a87e78983f1757ae560abe572d2df887aa15e7a58750bab82bdb01e075d0d1bbf9f384dc27d0d1bf2aae6f543042fd84b5f48f633ee34bedfd87675cbc1b0ce700bdac7bf5cca2eafb4afa7c46e59356e7cf7bcf5021bfa09c3a8b4b41010979fdcb8f50b55b12d97a568c1eacef86c4d9e6aba8c028d01d062631eccdc7dc218d6c5499cf37521c5cfa7d46a040e86cd5c49638cb171628c31366993f4d9f445cce238628974b80607410b58088bc58a898989f16aa595524e289be434eeb4e7ca75f782fd06bd474cfcc0df346eabcf0c825efb689a07facdeb326ed33ad0a70369daa7036d3e3f5de6b17490bb2178a5e47c1ab16d432e75694de4a094a06f50661d94d2bf9a042b9fbb03d2dd0fef7e682e641ef79ad77d945cecfc33eff39ac7fd16fb725ca675356a33b10e719148f0212e373e2686cbe59d964b88d715ebc4b4b603db18f68d891eb4532cc5a60e94fe00a27504151a1c41630834f48b4bdfb2a08b4be75719073910b11c5dcec14191fbb99cb3a465e716a11964d1839e1cdcb0408b26011764b09193440ea9d7949a18ca43111b29365e806aa0b97868934d1436556c45389b2b6c78b0dd20088a831360380183249280e34242b76e30847cb99cb3a4ca12332a0fbcb86209124690a28a828d2e4e80aae132524220c1c31347ac80870b23b4604207339cdc600729493801106c64a1051157d06089152de5040ff0018b9e1d50d1a30437905033460f4e1a55b0b858a2091a4738c186c304382811c492225e66b4c103ce1648be18828c14122c6ef0c58e135c3869830b233990d1032d38138862783c468306165010418933aeb0d259e048813304ee079fc7db67014e936a5d44cfc3890c6ef831730a00cce9b95807e31326d2a09452ea42603836383c0c2963075d5c79030823a6682c9e5823e4e40719f344194fb48800a4073e3cf14209274ef810c5a586a15841174520e18410ac00c2083698010f6b14d1841e3ca14411fa0a31462c88428b292fe0618d359a858303415451440c96e4000952d0b8153da8c1b171f1a30aa1901bd21f22e44e66ecf962fc98b7c3c65d0c1ff6edf1d7f4b1a691c6e766815bad58759ab5e3e0ccc1da389968a0258780fd8057f02675a97e12cfc547d2fe60fbbb9b407ce38d6670a6e0f20a7db11fd89047ec5e9037f6537e215eda8f7d22885f43bd5262f17329636c29e51656bed5e1c28446fa113bb3154bf5b9efd4b9fd980358f2a39c5af0a8bf0bd70ceeb72ffdeeb70cd3344dd3de453b41a469dceca3458bb42a1058301586b56a8787094b098e8e942618b74ad5edcca2288aa55aadfd678eda116cc83d45b8828944313120c82ab144a2fe30ec5b2ef61bc6a4f8d8c59ee717ce3198d4fdc0542a11a77212e7a49f5cf717f10a7d45d8c7d59a75411a88df89611886613d422ed0dc528a62a997e0917f139d439350b561c58722ecb3267b8aa51ca353ab4e459c44552ad56c39c8c33e704058b0a41ad5a8d6e9288de448a7ba4943695533696666aed54aff19978925a6c1bef935ca49f47a3d92fa1ae560a7e248cade62db7943de11d2282796e308fb2cfbda646ad3106cd8d708cac123de29073b89b532333b5c132f7aa5518d6a1486b9a8b0a48a20453c64ad120d1b9fd96bad953a2159ca96cd5592713907073ab8cdee94d2226ccc1ca4638c52ca3929a518876118884aadb5fbd11f3b986599a669dbb6498ee36a746116b97f5d578dd8fe4f5b0b0239c825c6211d45cc6cad0d85422e2c2c2ca2161d3fd85d5cda657e5858585a5abca5fb21dd71b8e0c081a38505078e90c581030708c7a7c38103079d04685162ebee172865965f8c3d975e97108e230b860da39539e7a494d22ae3cb621856c1b0b5d6ca499308b3622b1836ac9aadd588e5e73a1740f6765dd7fde0aa032bbfff709815cb201710082472d101b2d6da5028146261a12c2da594f2636909d916d0a76b696969f9f4e78fb14e984255cef1704c381687c3e97052b8261c77add68a44fff454abb54d4fb15427edffcaf5609b5081dc145cce758e07d8d320b8b4bde2c8bfc2c01a713dc98b0cf5b3cfeae340b37dda17bff8c95eb63e4ab5d56a6dd3d3112f655ce6d1223eea6ee6962cdd6928ddb796533aa4e98c180b6bc3ce094a61b5fee8899e62899d52c1a96260a5d38f9ee2a80af76739b11471d2e4549c4a85613c2e2a3038d12a819b59b2ecaa5dcaf56043ef7d6cef8fbbbbebd7eb86f60d4c76b556d9d5cf3cbfb452daaa5d936c14a414bcc00654e8214586728360778cc7a5f80d82a01829a59436184b5dd26e7fcc3efc3326d85034df1d1041b0d61eba84dab572e5d6c21e2540818797bae7f79885fadc7920af04f56a9f04620cecce5b806e643acf860ef4f629adb5d66a1fd4d907bdbf77a0b7dfdd7f7e86ee6700bdfd64648f0df63f2c1f621d1b96c8107a96b7e1f3f66de897c1fee7656079e92f06fb9f9fc17e9f1958be50e70561c38632a5ee69401a7f114264890cdd839e88fd388eae0ca08fc8e7eb3ed6684b83b8599097bf7056b9fde1cb9caf797e378c6a5c3d7e30ee374eabdb56ebc681f5bddbe618b7dfb75906e7f10db26d9957bdd99a2e5a84def4dc1b280e9b7fb2902c65cbea4e1b8818511ce4fbc9af63a4b585a91162e904a9b1914102cc62d76c0ef231b0a8871cd8c70859277c8f47fefe22f6e1cfbd404595b04e1096136ee6dad2f1320c0554618a443176f7fba75f5cd02ec93b1d6c287acd2b22d11a2ef125c64db5ed6c3c1b934d896f38d7379deb9b94eb5b93ebdb93eb1fe5a692efbf6952d81717b0db9fddeeba7d39b8833d834922db63ab5cfff46c38b335bf60d0dfddbe3ecc8a94524a29ec567fb6e6173ec78856ec534fbefbb77bf5cf6184f5d5ebfadb572cd5f7ef2a7fc3bc9392caaf1da59b8a7db45c569c0ea19bc973f66c6676771a19d6e7e25629e5047382d0739303054f0e143c5e4e1076c851e244cee68e6bc286d549f3650ddd606c6cfcc25c655cbc0511d3ae4e0abbb5dbd91936940e88efddfdcdeddd8fcc03c277738e6bad5c6badec634e2a8d0141b9a7ca10bcf42fa5b5c563f1421ef63311f3aa9c5ebdd1e360a05f12fb5e85788963bcf4a5190dc4917f6dc3c6ccc4efbed07222ed93a95f7bee0d12878f539b8d7984b435794e27e2cdce2e1bee372dab58f771b56ed666ff154362eb2da388eff09c3cbf76cc9916022286146cab598c1613938160fd56abb0ba94f36b8828b9ee3a52be42d2466e0be0a122ca7739870a9c6b553855b4147039a70a2515055acd2fb4556af685f68a0dd95c07ad6ea80828444417f6882df241e948b91f1415292a1a8acf9f97a1b899c1dd30cf2af151373faba24a2c103178e6d7836ddb73fdd308e386f6358593cb393a57047039678a23d7aa62c952f12a2ae9760cf6112ac2861fd475a9aecbba1c7418d8d0aa9e792e14abd33dc065287cb0ba5b7356899798c6a33063270a339044a184158dc2140a3333531dfa843a71d0e9ea9e6891ee5837966d55fc0f2a96ba283ef29792e2f9a03a2a4e5d2c1bf683bafe4e9a4f3b28dd04d0b0392eafd4e8e1ce0e4a66850d3b218461e47e4eb1d49d3a55ec5e1de78a23ff9ecabdac69a75b75379dabeb7ab4276cd831b93e95c8b00639ed9d933b9cdba91cb4aed00989a5d2732564ea8e74291747857374a0dcd0ba9cc4393aa8eb6fa9b87dd92b50eefc424b3bc6f81efbf08899ac4b7a7cad2b46bafa2f4e798029ddd6a758cb63eb0e16b342c3ed771f22bafddef58b197345cbcdf4d39ceeeeeeeeee3e9bdd99ab157daa5331f137b62230a653319502638c959f9f780516824b98fc53af309f629ffb0974c34ef5e90a711d0442688823d72e35c35ab121bb68e015f87a4ca562c97be091bf102a228e48a53af5a383f82f71753ae5c2a838a53c2d375d1856d4e0d6faa469fda48d7491be5139d14e27f719d7344dd3b21e818e118077000cfa67ac4ab310a861922038f9e082fe8149413029f6e853942c3b7eeaa54005d8028c012ec508691624d02978cbdd3afbcf6f1f4eeb3a4debb4f7ae7bee66bc070522a6caed6810d4e67633943ef5a9ebb84dcb280e8d5d774c5a4c2f77266ee3a92b6e72942bf1931b79bd401004411004634c0c08fe8b449b066878b5a9afb749822db0055a9048376132b5686ee07ce8a15992033b493a1142448fc1e91d4aa92d02f64082ab504a9c14f68e9f5e6e8a25fafefa2afd5e94f0621f3d629a949a081ef98b44a1261aa0e18579e1f763dfddfc5ce88f98f722bfefbc2ff203690bc0d8c1f0227a898f86f4781cf192df6b94801408be3029afd7abc5741905c92203ee666630c6c44422bd43164b4c34376445ecd02c49f1f411940df9158179bb09ccdbcf31344b681dc2fe5d0349a3321ef99b6a6d948c73cbd981b2aaab561a50486a18c23da723d72b77ddb49958dabe5a79d5b5d65a2b00ae7fcfa461fb8623607f1afaeb007f8049b1566623c648a59c3c3d7eee1533ecc25ceca2c14959e561659ed9c08442e94441b23a926a0245c504859aaf5965f6984c2613f64ec30ebe0975e795d95365be1c44a1e6cbe403e5677e2119e8c1035c458969e1bf6bed6cec74f1a8fb9d9e64fcffd9e2c1d142b294ed1058ed50d1892223d3cf3a324d3f7e0f6a817d348047fe50a2f85862008f3c472785eb1fe327539b7d7856c5cac8c8ccd9d5e486e33073c61933cfd9594dcb34add33aadd3baaefb743341a2129447e5b26f2febeee69971b7d619842174ae022e07c148eb6eb355636240f0bf0b85be1b5c669665bf65d9fc7c97755da78475da1ea5fc30fbe8be69f7a3bdee33acfbf109c2913be9843e220028638a8986c80d2f3ed02c798940075abe9fef0ddd0510a260d184deb7f798aa5f1347feb5d6c00de1aecb73f7c3c50b63bc1edfdfcb0bfddc735f88c39d89da950b038fdbef857d3dc0ddfee6b7782f2ff467a6dbdfcb2761f96e88269ad0b7243e0e1787ebc2f6fde9be7624acdf38f8860d6bfdafe1beb056ed0bedcdfa7b6d5108fbf705c6a56dd8eeeaa04887e3780e34a4941285aab5d65a6b8da55aadfdc7bcc6d20c0856d156e3a01391f23a12ff99204b582d870847eef61b16312ce459ef35d9b9d03fb7efadd66cef1d90edb7af4d3c36e417aa8883de375f9f6a7dd7409fee9959a0cff56f5f530873f4c70bedd5bef3386ff3c21d57abb3868897a4e4c84df92178c00cd2dac1280f7743e3d0d216c1dd504a29a594524a29d71dac0d3531774f9783cef486de501b0b56ea4f55b4c8ecf1a889da3878a22aaa8ab1b99e14890dc3ba31ed62da1a7606e004145e04206a165c620e7063c4300cc330f70e4852740aa618615e3b543b98ecb0d991bafe305776ecd8b1634794cdc26261b1b05858cc94526fc8d6bad84b7fdf1717e8edcf46b92c376461b1b078056db4c448c9f591aa004b144015a2e0bafe286ce111052928f88e1da6eb3b6caeef385ddf5164c70d8b7dd01e6c08e382e999210cf6d4e31ec6054365767f493e2f3f302e98294cda4e0ed3e215b6df7e60d0ff65caf597d6f5a9c3c2ea3cd191fe4428110ae6d5dbc949302e077f60124c199625040bebe6856cd26e29a58c32561d44295958bc82b3b03a0a64f4615cf2fd61ca90eff243cb6261498f7a399675fd356f05c3927d302f110ae62542c552f62ee784695dff08c373fd61a688522214cc0b75fd5140418a9326cc0b06e6958289ca96269040a28a571651a4b841af2071058933584c94296ddacc9c84d7fa3f13845fd98b88a9c6db302561fd0ae960606d6ae2287a5324d6566ec3721ccd0469edecccd7ab06e3316d679871aa5d2b57eeee6ac44e3a699db46aef97462dd25a3bc0011ee29d83f3a87e2e2b8ab55315731013908042af5439f9b031016b0c638c6b5a84611886896229862dbe5580ec13f2835f28a851c2d5814bcc29601c98bb70be7ea8f3b34c638265f681d10f6362e091ff1831425e548a3b80627deb884aa452562b9a56dab1d68f91d8a969d5f3ab69599326d055ec186bac22512a954a3173cf0f239b0bacdf90a6308c7b5ae3b8da5aac35d68f59c462ac51fbac56eda3b41be33a272ce7b57a7711a9348e70438a4353760d1b5a9ae2157c73818d1a3575777757ece9a47245e0371469ef9f8357c87edb6615bad23c1445940dc4cd2c5976b79440d80102ab8a77f2630f6364b6ef8fc36a958108027a68b4dfbe1ff4dc50e672ce0f8af8278a23e7c17e1a3cb27f3598b821ebf4906ed810bcee52d62a23d3d2dee02dc794921811d238a1c53c774e46bc545b3c3ea5cc49e79c3d77bae79c734ed68ecf168fedaefe56638761d86f3e27864d6cee787ce74e37fb685139a1850d61e525462ed516d7b25b85db1d59764b2de4697dc3b984feb9848f19dd9cf4fc76ff6c4d9e8f670f10f446b2942df3a41904991b65252982374a9abb6347666e80cd8a26b437b633f3c7ee363a63ec269d9392f1e19d0b9c8c6c2003ad57177155b9fe4c5b4747474747876594524a29e5373fba7210932d256d971e28a238ddb9fe92f607c749b4fb2736b189cd39a54e2cc5c480e07f5cc9a6dd4dbb25c5300cc32a9512e5fa334f4104e77c8ef804216b9493ca0f7bb5fb118296ff11ccc979dd789d147e709838a95d2e2a850a79c5b647a7a03a5ad8fa74e5a07740b4dbdf0785c507553b57e792b53ee95c5cd8b073c1d0996c3a2a5d4fe772f08343a1509d0fce47071785ab1d72ac2a579cc41ceb08fb972573b0651c21e3ca4b63f385d978a905274a8e12ee45792675d11e7a25c3b82b4e6a31d8460776614a3972e4c84c3f4a29a534de9057de45407238b144756aed8fc3c1a12bee8a83de991c049d7b75a6576c8fae60e0d544c3a52da594769482b1c4341ac63df8a23aafeb3fa5a43a74478a4291939593e41327c59d9d0b60988bca87d50530712be8c1838b1f9f3b498f104a4d9eb367b34874bfdad89fe875452412a5a89052b2e4ee0f8788ae7c2abad405a4f463af768cd5c9d6842449922499c20d6c0715c48f3f7f7a610b77b2782e1fe6b8fd2d6fbb7ccbf7852ddcbe1f455fa9fd789dc739e8b93848b5ee5c989ffd4cd4bcf873480b618e4b317961e81624af3aa9e4bb91d625657ceb0aa142486c95d08ded099d4247ec4b46ebea0f2a8efc49d60656e5200d4d5b1b502f78e44fd560d09f7af14179516fb7ea9e38e811d17efb8f0e9426dde4a49c53ca18a393ebf3f3e4d30404aae2654db01d14073fa01e071de4caac5897bf1161d908e25a15a85691bdd2e3a0b750252261ff865ecf099757693cb9a1bd5e8f4be552599775c9c412f62e3704b9ea0b6c586f0872815c71e4a73802b942ab02b9402e99d93d2d8ecbaa1c54c275e1f55c7fafc74954e5568993709cf4c4aa9c14762b90ab7e287219b93982ba2124a92a8e049b02a8b4ad1716b57468060000003315000020100c0544429150481e8a8ae80314000d6e8a3e765236170964498e43210832c818838c01060043088081991aaa4100040a6d09274507de4cb5ffa00b0881605f11a96660b1b780bd9188ca25653be6bbcde3fb64672829fd2d6008ba4e1b9f653ca05ce82aecb1ac227ab7c42185b6cda4283801e3a918d3a8c2838210813acace0ceca3e63abe3aec0140a262b3ca73e9a7442ad454f3682c3f120f616b680a3f652ac4c5c483ad04b91e76e5a495d093d23d44644dc2ad66fba310793a76788cc6fc041e11eac0bc7228aa7ac8b9d919db8203a5ceee42884a0e549400b251d26e1765af5039da0de59c225b8a76ab51f933d3fce42c97cf3c877a349f95bf868e8ecd38e5cc9f38494f42716578b20d40f9ab737b2e264e5e369a89f932bdea73e62a707965400104a7cff570fd3339c8ec69c99fd69e4f233a38f764d09831e3d71d0153d759f526fde488a1f32998af4ef13ba8eed2720a51db6ad76956e4abd6b0260447140f24eda6c5af8c92bc0391bccb06496d452e3f45328e7dbc2618b2e36879c6a1514283e4fcfaaa523976561410aa84649a61ca66ead0fe88a0e4ff811031ae6d44f61899a6e059f3c98603bd19d8cdba9bfa48bfc48fe87c440263df7911fa2ae4b42b51ed59a5d5d2c97f625470d5003187835a1ca97f96b4ae40356421520a495c6fd6b91eb2235b6eb1b5fd17f2ddf0ea2101b462ce1d8ac86d14a6d5558685ee4790bdc4312e0db69c55d172c7642475c83f97e018e935823e58723974880668a97c6c085a3f68d8424bfc2f95b3ba510e2e527695c87f9f8336dcf8e1ef6022351557fa05c0bf49e0ddc4222dcf14565ceead95a5a8b7cdf38bb1be720715e976b757ccf592e68ebac19b5e363a89d11047c97051a9d2fbfcab13be9ec7fac5715e6fd2b19ade23e65e6c00a8a74f56d75e7ca57100deee9993ff3e1a74874f91b4ed187da004b1b163c841ead976a604bc54645ab9320ed0fb82d36bfe6bb163305b6ac7d8769d209d034b0528778c76f3d3b8c698bc3b9df0828c195e8b1aa475375b492d6efa0af3b57f352d5ca95432a2d9cdd1cc483e52b90fab5c1fe6f91533ea063c492c07cc3fade32587d6ba4d9307662a2ef88a30291e5d341404fa82575301a6119a17a7a5de79d5c25ea838dfd21880d8d757c3b1393425f9f901b3b28976cdfae877891450234b172576081285e3118bdef6207c66ffdd21bc850a66ec567054fd59e23bc649754de6589747992e36397fb334ab8c806f66dab052aa0b78744224c71470a86cd074eb9824762ba66011480138cb1a44603200edfac0dd28e2331eb6ac08aff9aa57b38805c5d601460392b6c1ef57775a6d1000a70ff3bbba95d800c5ed89461070ba15c62873fdd1819e81ba9f5bc515e3e7b1861ba914afae979a57ede020e03ddb4249fd6ad8f461fe52c9e726a232831a875e2a967fa27f76106eb5be6eedb9b4abbb405676f273868bd9f118fc20ef6f56ab7905ae7748dae8a220856284a31cc1f02f88086e3269bcd32292edb33c6b7de12bc8e29559fc1492801901383982b415d96b7d0ff9944a0d6484e3e30668ba7336dae58dc6b64546e09f8c69e2e5e24637fab0f0140a6aa2439878673e995c9012d86f3855ec782ef0f6f4e21e5125f0bfcf30c66fc2599cb032d766f56b57f146f6f6184900174f43c5a56b85917b44a60b63ac28ee6cbcc9c107e9670792e41052f704442749b6e6854179e7ed31186f43d9f85fd1699f0c3dba6052fef1bfade1416256571edf75990eb7ef4e7d59d112624848960abfa84aea16df8ff45d80c25397ee66469da9520abcdf3d8271a2a5521c06235aa4c4682f992782867bede71c0d8a91968fea11b9657ee09bf6f492e77d754c02be480a82056d027aa38508a704d2c9fd0e2284f6828d66c89f8200446e04287c683df5e4fe475fcdc0e565f54eb74a3307b13f4611e24925be8d446be1ce140b22c3ed6640a558bd46ad8af56314358c900c0d80922ea9b1d5ca991ad3d1a1425a89fc9b3680af8dc43bb0a12aa8f78b8410216ad6581685b7877a50c720b9e5240fbbe671e2ae8b8a163400525e245bc4bdee6adcfb5cc8b1cf7d2c9746d02c95f8a8442e543fef260fc420202e4c69bf1b4335482b464a1646e00f18a0a01389dcd1928baac3da98035cb11b477d0c6bbcde3274c996efae0855644acebcdfceb7d55249d6d078cc3689e6dcae3d272cd156a46b16b4c45f8ad5482ff7516f9bb35ea3ffb227e57f7bf176a03fc80a633ffa2c8f184ba536f35dee292c78ef8456ead10ee2f7b0b0ab94fbd217c516a4becc077f148a53ee0ba059eda21c671a199f19f181a90c944568dd77092f1655bbd841fd5df7daa157ae93b0cd8e8a11d02f7198101c6f6e88a3f3ed816284400624e99ee918e0f86f26fccdc6412fab18649d459e0e796850d038f8ff6a73bfb6e5583d6afb7fed264c5cbe2190dbf928a498cca290206bb3f1aa29335f252993ba9e6858cbc5a06622ae229246d679c24b1d0458dc7cdcc9466332dcdbcd26a2d5269ba0dab3fb41168f94ff5a55f3165f62561d3a3ee8f731e049bce3ea1e4ab9c14e57bd8147a528aaad94735aa1d4bd7a25f4c618c0d673767a6811b4a4bdfae144448477b1ce977dd407561df797411f9497d2c025a4242ab6fd48eab5404b2edd4dbd68b3d7bca9c02d9afded48a9e12744f309e810cfca0ca3776679cf8cd77ca4dcc57160b9e5d3d9321eb20f9d4cf4bb3aceee239ded35d9ccfa14ee2948f0ff2cff7a11fa1aff55a34b570690a8d5cad73f1f140229e2bd0b1c3dbc48d79bb463b8ba701bc3a43db1c9f95aeb9bf844b67ae7f0f219bfd388c6b41894c5a44da816d20373a0571742747c720535b06dbc81f93f742e970f5fba81290dfe977be04450c04b89435668c3f9c59f0a66e04bd595012273daf35579f7b4e5e15a4845e2d8b0d241e0de3e64414caa53c96ededbee365ba28dc984d299422fd78b4a79d10a5ac02fc4e473a7e0927a9e9653c3a482f87a926070a1a82a58c29815cf5abb733afdae18039b0aea0f4bd1f269a0ad2efdd32b8a282597451223069ba4b5a6634f379e3df5940d6ca8ab87388b53823b855284bbd44bba4e545635d555635fe8d157283f63915f17f51517d899d5deee10334144b1d9a8deca1b62f1b9f2de0cd03309d21987c2cba564f557bec268f4589b470f82090b3a795d6103640b8ac9fc1f791e39a4ad717f15416aa70c867070308c1e520e048b278993750289c9382fbf1098e30ef91d29bc9008cfbb8734adee548dd550505ef6dcec78cc7cba5c7318c81be3617a004f148a5fac70754dfed111e66ed7217ba20fd1aa3f7b9bfc11786b262b043b344e39e72ef3e28b0938ba3601a0155e5f281afaa2c1c30ea144efaac6bf6048f79ac6716694f8bb8c456599433f9d0ee3b8200aae75d0c6576e74e6e41cfcee51f44450acb9780b992122df1015ed3a5506c82b50cc225dc0e5a558a1cf5228d52aa2c3b1100bf452ed966e68ec5c96609445077290eba02374c246aada12077df6ba2931a213db11651f11b99d8cbac16043e281eec9e425b6bdbb003a934542f403700ba2615e24ce449e43254613df351964bbe2adf5648b8b740b44f2632caa7297abb19a1539c920f80fc27f115b1664d7e387e5d8cd05f9423780905bc85d235973d1a63f1558872eef30d2d903d1ece26bb36f517ab9510ab2503b0f10fbb68a14c88263b621c5060a68b2a8ddb0c6b51e456e5142a64bcd0beedd1163fe8e032c68ae46e41be2c2210a175e6c567a1a8f0f8165e3edf68360e37e677d96cde158474fcb2d92ca1afe82b48e8fea66155810b6f14e69c08c00ebb84a335163b681ffa5720c80bdabae5fd687d58306917ee0cd616731ad8379f7da2f4e86ed201f5ab0b76d7549ccf3d1f18dd2d84a86d5a098aeedc1dfe292f5fa1b908769d170de21e01ce1ac2fd31c4994238fc130bd41107a7cc9798607a2be880e7e31411ab0546b3861a608b9069728f7b843681dd2cd0797f59aa43fa53378b890d4c3c8dc2fa9ac0286ba2481c8aa34452e2c915c1fa79dd063e9490b906af5d9752234884c40c1d8cf6e82a8d626208107003b05a105c577a4ae6e57fe7a0c8cbb9fc0f3bb46b78479c85e7df1a99fe52ed85e7c64bf4755536352442a9db985db6e930b96d04f3ee99d76a6f65c2bba6a2f24591018979067b8fe5c8601da562b3c28d4b08ff168dce8cb4fb37f76ed9176d58ece61bcdea95d144a3b652a6876caf69447b375a764d7d54a0596537b2ca36b773e05c700dcdd41b7276bd4ffdd02ad84ede2ca82836620ca8f8de2bf0bc27cdeb8c7f656e2541b4c9f0baae9de3f32909517669c107fc50658c630665e37f893c186e52daf9c4221e08547c3f679aae1b82f4f0b57cdef7a484c2c0eb8bc8dcb724d620dd986da43a5ff424520a4afe7d0e1bc5361170a2c659d1909f6acc96ce68c7898da98d37c210fda61ca3157f189c6af9f65ce697666023369dcd6ff856081306f6b28917073b1e69cbe2b0e380cbe2997dd739d7ac1b8826a6a76712802cfb67cba2269c86f86a87ef4700a06e8b9cd087984d6251ca591e3fb5c3ee53550c5699c1f6e3fd4b46217d2c394d77a9a842b8286d6bb270373b33a3481986c11ccd0e8bdf83e1133467af9410f48bc76c85f09c6065a27492e5f61d5f951fdf04a070548f05f376b163f021139d696f6651b5924ef26b3c8e9cdb431107708f8edb0fa5c2d5cb88e9c4e282e0b5d24fa85e072085603350207adc75766633524c8c2d6e522bed0fb4159872b75388ca3099b61d5cf0ba5ba0aabf7c758e39acea1df301581baad2ea5038bf49c435f22e681f76f4124cdb7fd6e8a8dc9919c612090f3dd0515fa21ddf07c3976fa059e76aef85dffc9e63aa48d16dcf6d3f3fd0379bb1b77aabca086639a461ff16ae39afcaaa576f8bf72c715ce59c06e91fb14b398601be44495c5567020c758bd35f95f04bc47261bdfd0acb8fda0fee6ab44614913827dd6c46e051009bb13cf6b9fc30e3165bd1cb85dde4d5230d98d4830387ffaf23987db3995a72b2e47a80453baadb0b13e0aa8a438bfeb94db4302c0580249d3bc5c3591b6ee7cbef1bbe46ef84055b46369ee1ac5351fa2e2f2c6cc24401f2c2e954a9e7a6d6c1a7ac513bffbdcb6f778bd6ff4a391f73c629fb07062799bde6570ea445bea2c3c7884a87f9b790be24959fb0c66f5e9aa6dea1c5b9cfd79e65cb0736a8651c54215b66099e1e03892557599c024eff338d471f28faa43d98e973bbcd7ace63a0c4da5d0bd691244aef9662acd41898e19768ba076d6bd302347e46e9f7fab90d0cbd2d175dc45a18445de0c46076e1d9b43571a1e0c9f0b8e1c612aba716e3dbed50214567a102d926937b877b27d77ab54ae844408c779cb29699b24e26fe7da59beb57bd17c192e4250525b98de14b6dc8e5f66386b85ac5212302fa3db97cdd313616e14c7d3ed8006980dd06172883a492798e9785d4a1a66e90349dae6ea6c80abb606c3999be76043796c4fb6406e5a9d9304419b6c9d9731bb6580f0513fc0faef60fc72d11bb0eaee373095b8c7c2dfd8513fc00f81fead68ebc8e92755ec79f150a5ae8226d2ea30c16603c62e041447325cedf50437af02625e21978f84e8e3354a5f1c00bd8e0fd84e54ece289ac41135ff9d18f3b1f487556b255e74a0177a6a785a09587b81c50951a249450e39f7785994692020594aa2b26eb6c6f49764c47aac4f9cb9b2749b4315f0ce2843453b72a53b423b00cef9887951d7355233bb8381f742555371ea50d913a96e085ac350c35b4817ae440a19f23dab9d8e359e5de76de1182735d0d760a0a4fca780974fed3c11dde70c06053785afc5749d254489b997ed314e017bd633798a907352f5feee15bda375ccd104a2fa15372de698ea04e3372b9781965494550189aea5303caba51cc10ecbdc47d932b1a00c7953d7b31279585d37ad44a63836d6de636f38d801a686149a3de23c298a3e8fb9c9a384c452c7b396d1c143386b3c62ab93c1090bc8ec1d1df69204794190f1c300e14092b1dddc86c399a00f7835a3d1a7161679105950eb7255786389f83fbc084774a1995dbdd68f34349c3d900b58f057d0b34278b22505cbe3dce088b824fd8a13a6b2072294e8c4d251b6356feac47d92801a4d86579a25f0e55fd880b338badca19990b558cdab1b9a9c5a1642a2e25fe6002f10b2e9e9a2570d02a1da5065a34abb299cf5da279d11d17b877e4a7002158855eaa48680a2a23cbb13aa7e2d4b4ec6a6e18298e89de0848b93e85b22901ca789b0e20f491b6eaf7e52dce43d33125f2d8b38758c05dbfaf8aca7a3cf30640288feec3044a5cb2eef6a8d40cd36c3af40acf23e3782269a9b4581415c1da24fae30d4236701ce659f13e5247868a0b562a37abb6001e7a965535de7899f3a842cea8db44ce2cb7c5bc95c0f3d2dd0d0b02dea85bdfcb85a6358883f24160f5a2eb6cae9193787de1fb1c84173dd7159b7be6e0c0c274769217487d1193d1a56db3e38234e293b7c01a8e1a4479f05e1a7b93b1b725c17d44596e031f75508487f85952f1dbbfd849de2cb6ea945c8016d0c0c4757f5a1564bb4a7c88bcd9670fc2a51f7bd4a7207bb180da1fd889127ea02a7ef410e76bb1f92e45124cfd1f4829092c3cdee4d9b41f35be86c1d9c95d38cd08762f496613a4b16b9ff04223cacda6e2206f7b8a646598ac22c4602a9ad7db856b157c9eadfe1285923c857d4c71b7b1ed4d3208f4a635008e1ccddcb0a4ebfc1551f3c1c84c0363eb972db0710fc2f736f29020fc1d4f2057556acc92c92cd7fae6a0a8fab8791d6e425b425c04c15396c2d9d81e33217c024c9148c5472f56447b4cea88e03dddbdf58afad81958cfd74c36189a706951e6e3722ecbe639b9eec6b98865d33e4f353ed21a80b19b385f99662994f89931174ef7707c8f71300791035cd38a355e25af9757c1d238f36eb0bf65d48ec0634e1a2120b9da0ce92e0e9c5d646da231a1a8338bedf03599e3b661d2ee23dbe42a31bee5e8eeca78bbb6cbc811a99a3f60c3e55a83540cac430c1fd13cb184d812e4b998733f771c7d3ce768f50967d1b3fdd08c57473d7119e94b48d16fef2665c16477c8e45d81de9ecc6da2c7bc6c12a80c3347c33d1053a62c72fd4afd6d544efcd4841998adeced075ea0ce84a6cee2ed6d8000a6acd98360ad85f6f4139b71a02f299eaccce6312034bf074772f68fac3e690cd152cd8b70d0ae6a0d5e5525b3953ac0620db29d74605413f7e76630ab63c132b726eb1253f63ee75328e3fffcda69b85bfae6920ad24fdf01f5b5e7f66462004f54f735686e9d4bb0e7f21095dc93c82b91fd4ef75c8e31cc434f31cc1a1be80caf4a902a6953db6f08e7703d82bb12db4ae76c1af720e8f2a89d3be00e0ed14dc79398c022b258de8c215789dffddf8946ec99502c97e25450a77324836731619fa5f1a29ca3bf814d60c09115680c5b0f580a5bf7082551de37163be303f17e4fa4f71428c30098bec1c7b167d591cb40467c5c550522a0c2c590605220d7dc2ae8faa11cba621e44a6cb35f18b873f2e3fa035b3edf583f79313a6b284d3515be0dbcffd28b7cf2e70125a3d4dd379870efb14dc9402448bd45f7e09202686ee9b178a745e2a9af5d89b0958f0785c7a2ae1aca3cc78cf403b5aeecc1ec918039185bc67d26825493ad96408ef7f178de93eb57ac9004c904f6e59fb89099053de52e8bcdaec50a917395d0edd763b7b1c6d654113aa4d5d3fb2894588ed5e5cbcd9d667100a2dc7e2fd062515995c4080c745deaac229375d218c896b5616b5bb4c7356e62e00fe9b85396ff798bba649e06e807b341c1f0ee42ade861ce3718afbef5b9ea0640ca59eb416fe2b36ac092d5ea5057af36eae97f6f4cad3d2ea4d6de5930ee9b5c1844a713574883313a47b63a6bd290f439a805a334446999d3834e6ca637a23a86df2c4e1bbe97da2016570c85d1e0c5518e014ea5c7596e3a4dc0de966ed4fcb1ffd31ad3e38f4c67e45ac80b70a7860a0f2128aefa7a573a7d9ca7bdeced0af5bc552d7c54da3a31b5ac8dbe6784deddebee2ffa8db68f20229be03c6848f26a48cc9987455a20218c072a1523f933f052ae9e8084664676e361d4472889bbb7617769522a0a61fb3a51154e7cbf680bebceabe0d815d3e84ef7cf422bf890a83027ce32236b99b1a3dce56f04f1e2fad3ed879168839b4756c43a29d7fc0cce47a91888d05624f0ddec475b07201174b155007b61df3290a8df66692173b67751d5d59c58d141b582760ce275f8d22db413d228535646b1d86e5f3000a816171b73fc01f16c33e4c982e56b45a4c5fcf5ccc60d7f6e96702004bdd0e546817336e39e35d9ea6700ea90609ff29e70e65c2c7b6d700b27dcfaebf32e7e5cb5f0461aec80f332609bdc8d74bb3e288ffccdfcaab64548e481cecb6ff6663cb01470fcbe60168b64b1251b392cc9e6c13a3cad7b84800d027c4695e0d0008d5d7e12a8f0162d02a694e1569c8174a7649004b58b1ab27ed1119f0be02d46313247742b2265880c8eed5f96b3c8ac6074ea662fd1fba8235315fbbc3a142104f0fd51330ad1ad837855ec81fda4a463f0fac7f7721ae02c3f87ac8b7981c2a07e6774a0c2215455f7b807b30f01f207bbf6cd8e0dbe3bf380d2fb0ab5db2b9910c8750c0229cd1da120ab45118f40cd0093e6b26423b262ef663adc7f68f0ebeed8d01b0a7f3f3e1a4d39c3f86b897f9d7f13bacac0ec9303be1c85380cede888cc486c44a00452b198b30f0d02b94c82d51807d7d6197c520add863ac05630d169e069a4413a7a7a5a83b212cdb83901f64a4e9625af43f53c9f08619799e82977735429e4406e92b5b8df4aa77376544c7d764c1d242cc0ced80e18a5268acbf4aac6d0230a539cbdb03b99bd4907100359756fa0ec7853ad427244586b59aac0aac0de6ed00700de9af1fa07582e44c079cbae29ec0e9fc443fd02625eae60d438fadb002d763744b21bcbc98a2257ad0d0284622310445d1d6ecb9a7f401d4196c8c7dc735b927dd4b9baeb08d264538b42a281d638e70464e43f811e228f45ae7e0bb8197c802d91e08bde6e21c7c6f2a976ddba2cabe73877bdc96cb031b3ad858b3e938d74487f20546ed3f5e840541ea07225b66dc5931335c8e012b68317d82002b731b09da348ce774ec02dd00d083a80d228248855045c8eb96314e4eb6aa833aa3e00eaa541f3a9f54df611fb70330f1231a79e8269646f8ba24761e35d5c5379398335e7ba6bb33532aa837ba205077de115c5291565312c2f635546cbec8e1b4e26e17ca84de3c0691a2179bfd09f997a036c2d80526d22aa1a931c1685bb5691df3c117280a47985deb4f70e4f7ffe9f50bf9a3d551597d2c5068433e0297fd3d7d21775984d870c4cb0fd50e7ae0cafe5ae00a370da9262577f85579d27fbc5251ca6aa47d3980ee35abbc768a7055eef3b1a491b22c8ebd9d4d9e30c2c604eee30203831e99458122a5842fd215976d5d8a1534bf45a032cde4575fdaf451ce2be026e7b729cbcf155e315fcbf9bab7e3bf557f2dfb8aa73e687ecc0e87c9591e82e74d3af6438acfdae216e8f7c816988a0d86614f5c1318d83c06a65bd954ed94add8aea9f95a1c58aa09be277ed49236e8b6d6e30f812122ec423dc1d11f250d4609ec882d407a80274318298329e720708288483bb31eaaa459c9b2e9ffe0dda81a89b6bbbc9bf5dd9ac8bbc674874ce24281dd37fc9bd3e0509a151092a0fd3caf8dcc513a927aa3fae0991e9c430a0a139326db3f08c5f1fe8f9bd5b0fda983f4e57c93a3026993cacf1441b21d30f48181f27fafa0f6bf12f3f654a5bf68efdc02deedfefa27810b9d039b845471ce47106e08e710f09e788b1bf3770f56a5e0497f8511114cf26bf59bd27bb64af38ae71e27c2cda126cd7720fc9bd1f33dc9102110bf27e138e1b79f80573e954dfd8884cb62276658d23c34061e3a96edacff4a0e62e6ab2127becc955fd46333c673e6877bb19b31d9eaa7e67701aca300d18e769ce1fcdb514310a2437a7e76ea0363c1bf690f5aa59860b91c2c19de6865284c91afa24a522eaa37767932da78e76c29e99878eb9df511045ee7d3138b346fa65de0ca365d93e150950435961f0bdd257d00c52a0a8efc939f28f1a6534648a1494ec00df839a1d072af8618925c6c279e518dc798d78e36b0f13dbde431dbd8918b3382e4a1f2a30bc3219015f084fb215f2e917fdd9a5ff6f258cb213ef2da7d3b26a2bdf56d83822bc1d2de37c76fd874c4e76e0c865a7603375c1a4decb062f642501213896336500c138571d1fb6aaf96e26bd4254597bf68d258911861987a1ee5b50365abe2f0c44c7f47693b180c2b5e6c5b91c1b062714e37b36b1013a482e6b525fab71cf56048986a0a3fa2b4667836eff0ba729e607c8a170305d39d278c320dd4df132d10918105513af0ee158f84909cc662dfece96cb86f4c1b92383169a8da08c0e05a841ee449f1b921035fd76ab4a6f0dbf4816441f74d9c71b684773d5f894770617349254741d77076b830a8c785c4fcc5fa01def37184cf42c5ea0c897ad44b86967e3f573334c627ff5626a98825d2ca112fca307c479ae41e782278f29220fbce0858edf2b58a82b4e696f41eed4565d7a68b20d715af917816c1ddbc391a652f389dca7033b9bcd39231828ef7b1bb78f674274e364e903548a04625e4cce3c3737f258db0dc5a54c10769c594a0cb0883c79cc85354bf66642b6b0bc7aca15c75153b2bc82137f17e2cd81cfd50f69ebb15eb6f467e8a503c395c966f91d9df1b59f83f3aa3ec2055a8d1d18c1fd956f63267a73ba0455361b2f6dcdeedc8ff08a7c02c6daaa720e3c242ff22d3d1d15cf70e49c9263c58ee2cf7750d249c7e5a27857b881c25da6c41220422778f58d14690ea28581b0086bd9de93ecc147970281d317bdc27cf694c90c96e32381ed09540cb05ef46205b96e011ced18ade406737cadd6006750f521b9f35a0b11454f36b5ee56a0b8ade1213fbde1bfa815ba0508653106ebb3f4e2296d84f5eb94727b8418d1e6ca8139abcab8bd06bdfad1965d0602ba0693dc32c9d25c7bee1d9b29a329d03ebc4013d1d23822567a381800032fcdb02423eceeb45bf458b5451832a813a69350305fb4965274bf66796ea253e44c51c10bc098ed92c626f5029338bf78f6012756670356aef2d39e200c79d8d7e57dd76855030e0c0acfff59106659f598de805e4548c37c4af0a1267ae174893b00a72a7dcf385e6ae3e65412c05411019b539fe4974a219776642af9398977bd2d0fb1ffe8085c720bd65c0b441f552cf4984f320c37df05178ce368e6fded9898ddfab8202e69d83a21ecbe2b2b914e2d17f8b955a128fc20608d780c61abf2b8db667c8707e4112f86688c651deb6032aebb8b719fcf048673a1d03d5eecdecf01825e0525ca24769776d97089b2ca13719741af8c582f61029e7e6215d883a89acda434148394762b9c513e390b75e01996131dbb7d1708aa5bdca6ba36209e88600eabbb0480dbe3172163b02206af8bb28a1bc2c251d7be8f60a02968f7d93a817eda7c0007d9d33d09f33939330b2fb8a6016173b5b415f804eefabbd72f81b969072d4e6b5a90f606b7156f6e586e4b01450ccd62913cb07af475d0a97fa31ae6c7dde689c4ca0a14154456f3f55d4e5dc5e41a0222a511810f88fac22eeeb6648654ac4bea0918ec26ac85b33422f78464713c2f8550db53b4fbc03a284a59ea2cb55e52f7090a98de423ae7980f607bfc2ff33d15b64fb7d15bb6fba59b13d1f0ebb694ee7cf15b39e10fe63e45f1aa6597db314ba3cc108640ac28b3c88b606b671db0d50867b5d54b670ecc876db5fcbbe9f6a964a9497f6e63ad17fe0fdde50eacaa105ad467cd662e5d111468c1616fab31c575d6c93dbf8e5837931ad6dd74faecc71b992d2c40137162934d3d966b51da72352303bee453c5e6aed3eff32045381a2f6056b6438c14f211154a81180bf7744508114a3cd47af16c41ea45cb886ea0d5e4e316c2b27832e4c53db5f08e7f831d53dc95bc019998b69b4578378f638b180f7de887ec9fac2bd5e91a5c7fa3020232705eb1aa9c0abe425c80a9fd496be1431310823036e4d258de46a6399a317bd4aee8bd86ebf0988e1392cbc54497e10bfc98249be08e1b3115a33ad51c3dd7fde6eeaeb9844929a4da99937de064a532fd457ea369451b6aa0c5e5c1c48085619f2cee1c8087ef599fc0386be20ba3807f60f223f1a85719cf3016c813b48360b8dde83a536b1890c8149b6aec7f854707cd7c5e6a42683fd1ca22470a93c8f08a8a525844eda518b3e247f050684be9865bb269d31f3eb3e8c7c18c8f8d98a7eed6a91a10063065767d38716701a2f3fea808ed0ca4f61af7005d3828ba670da9ea2534916a4ce4cf04e8d3a40f82ebb2aff2a22e816eaa94cf2b7f56badfdd6c41fcd589cd8bf1f7fee3ca2fb3b5a963e1176caddcc25bbce20732683f60ccbeac911b1c686f660efd8c4534ac0eae592962ff94c4e44099cb7c49ab463d144e6c109cc1711cf34327574276d5910f60f047a90ef10c93828ed7f610c09c76a81734ede15406cab37a7eac71d43efa6408e70892ff67073a57018e4a65c70594f7665cadf331793c4a48c3c47c6beb7acbd509c2213f847b51f0bd8de98d888fcbd8f7258b3521365fd6bfd05275d4e10c74e58a427eccb4f382328ae1d14f07155c36b462d334aed52299ce013e9469020dfca3e9c1ce6751e6f6bbfb4f7aa45f122260af8030a09679805f031957edf18cbdb9d1317542b9a46ea0f8a660cf5eb32281a437f84024ea7429b0405a4434265293de0a11498cd4837af76056ebc03829a0a763b02cb5867803d7c0bde1584047598d2b33f3207d3089bbaffca3fee8916b8bbf18f33a78b48e347ce49e07d8c05c28ba89dfac9a8d224a23a1c83343c3b8867da97429b0141f8480959adc814e83ff31554872612348b6336964f740c87fe6b4b6ce11cb81577ca421e0877bd01c7242fc108c638a26cdddc7b2a81f91ceab4e62179e92de6bf666ce2f2877847527a6e55e211a38100add8059d8ced9de9790ce01c9c4e512b10160bff5a1868125448ca65a20e6c09b91e31758e3448ee57012edb1b9701e284fdc5d81f79aed4183f904e261c7b7dd8e95794e133575681a382a42a0af3fb9574515de9489584525b20366fd5eb6d8552ec92db91c3a3213794b9d2547cd6b3583f88bd191e52d8bfafed8c7aec97603686bff80260c8a1e18f1c7be9827811424d0baf03bb0a25ca40b87371fd432c4358a0e006f33f23f126e29a7a38403ac2b4a3a4d5baf164ee5e2dfec96ad4a0d61cbdebfceca9c50f621ec69d1348b48d1832af6df8aa5575cf904b0654389b11e302582226e1419b34c786a4737e883f6c4ca71e5c235a1732ae7a783d612d9b03bbe9b5d80c03980edad65d27c29e8c9172c8a10366e996382c660a2aa6236c14c06018759f38a69632b7e4cbf3e057d5c3551db964193a9b15450295aca9ecb2d6914150e6c451c0f83d83a23d2ac56023a2ed5ab2f9e15ff2083c167fa55b0a0dc964d39882cf585cd6739ae0c1251c07088b4152cc54b4e3943dad01677af4284cafefb44f311db2577be705f9d5f1227a87a4080f0e56f8dc536c0cea5376525546b9762699d937a5360c8387fb1b117d8c7475b88ccc5423ff9bf43a4b22167bb700978ef24152029050ab31a52791f947e85d85288cb3c3a6a63132d481da68dd473e297957c0491c73fb1dfb62e2a47299893be832f84ae60a1d3de2d5e76d737b72bcb31eb15059341bf82fc96f1def6a0ebde97fee893798723e9b0f714f237a51630d620b9c7b85d316c481bb7b5a13a58b804c9c883c1b1762417033e727bef7d3658d95b58a65e6e3bf21f7230cc3b17eb09c1909fc94b1b1e6e8dbbea661d25019ac797ce37d94d6711e236a7e86a9137e585a76a432f15d4e6419194d2d9f7232d28034204129172035f02adf8ce5872bc1d51c90b232534240862be1f1ce780f47df46f40755023caa66e5cad204e2580ae4d185174409d8f68dfb1a9654c62ce58c24f0d2e8796da26213df131ee16ed06e9cdd18bf3f57e13d78b502e45f736941e598e224bf50b3aa82662b35fafe9f291e065053db80ac225491db02ef9f0666ec36c444d8239477b8197b22cf461c27cd6a48d83b28f233e8d9f07bbdc2ba91361f574467f2a16eaf261573762cb3be17b475f10e9c25d5bda6ffb4fd29a1c1162f264d328ebfce359402599cbbe420d4182697714736f08f4f0c3b1471e51eaa69e42971261c573e2584c0042ea935f3ecfcb86fc0356743c39c2bf870264ab3df41430c9fe43820a87140aa8a05161372aa992e1904ee4a6225032c09987126f8b12427fdcbd0ca688d8e2cd1f4d9ed9b806d7f2c0c75fdc0bf248a93e76e356d75375652be80151a825d929026215fad7eee6791da7cd8127f31e35644a8274a2f7439968141f2faf2f1bf1b6f6eaaaee9abef7daf41f6625cf12bf8cc49b4955f7c7af72886d99c35c3e6aa3afa5ed17a1ecbd30b28f92c6b6ee402f85f7de280c51c08f9577b35caed0a660133be8f1da0adf05552ed86cae2dd4a21485d15fb36fd7a73c3e0599f49fbab096473b38632e1ef0b441b31813cc2c8990e21bd1454ce9768c205e17bd62cbab52ab11151b35abf9dd3ef5c2d5ec793ebe1a606b2569ee4694140c8ccec20ea6c3b4875f51e763ba3227465ddc91249b87229297d1cea737d8bd56a79cec18aa6e282d8d68fb0cef5b25903513ba820495bd436e402a621e209765e8c31100bad7261d9ab55eb1c92ab31f4fe147d5ab29c888ae9ba426269f2c6c9821f896befa9758228ff78254e8380a5aed4b717cf1b56f6760b87b38c675d37954675c51a3cd150050274956f98828ab68deb14d7acd12cba5b562e6a0eb0f7ed8f04938cd61fcc20fa8ea450e42f914632e188334c49c010668828a12d1146a6c85c76511ebb603cebad8d0051decb41da0110669386ea49c21df3ae07c79bac99e55b39b8a08a52e6062a2dbd61cdaafac3afdc7813ac97e3a4d256661886af110eea57f1c45bb4a3a1be108a095bdac34f958762813e8fcfb98868b915692db86f3af8ece375a0423e18b7212fc6fbac659292dfc2b81780f8da09542184839681ff0b86e90b6923c96a7b6a7baa14355ea9022c056e4c2282287882af9b084ab1dceab3e0bc11bb8178a9280587a316900a5e401fdc38bab5c231f46f896f7c4fc9dcfd19bff2d6f8edae43d8d377877de8ff6b5b1ff0877f7f671358a086bfd85d201f3e435f645cd5169204f3f1de64fbd2844767a12f59d0ffcbc50004bc6cc15985616a86d46ad39f5f6421d86807ec6232f17be8c674fa7a3c456dd4e1ecf8c7eacc6051e5ce3e2ef22e56b2b4aeaac0652e481d2cb14aa97de2aced8e60f6b4a90db9d768aae1c1792e8f70859fed9b1696eb3461fb83d70367b6517a4395acbfbdb31839aa9c7b7a5b65f4e05cb7a54fbe5bbfa03b027b05b0b53f2dfdf801487f3a4bf8850c09a635cfa30621fa9d8e8740bf15332c7a7e3de5ee66e265154cbda1b3309d218533a22238a6c3ebd461f03ac13b30d3bb4b0085766fe97981ce11527f3b023339fb15d5f3abde14eb7689f6eca5cc132571bbceb303eb9ec27007868dea4ff36638a75c8110fc97334e6824f2aa301ebdc16eddcfe304e595a01dfdb47d2949f88c77d98ae9ec97d8c2bf745361c65f4ae52cdfed1218f104c777d9bbcc04207f5b9f28bacba2e8ec4bb1609b1cb01b2edd0d4baab756c40da43472e9e4006f2e2cecce3fdb8b932c368c2e005c6c4b8477002a41f7b389d5ea6998e140afb1660717dcb832bf9c763649f16ab6371ff675624d753f600e90da3f0b8165c7e9814605b4b69711920600066fdf3c816ee79b266f38e21d21cd9dcf9deffc52c30a28b3350545a80ddf0da256b9d1fcb0d3e9eb688d8f096daec04bf5839445d5c661df3a7cb2fe716add9e8d62928c1954c4745d4f61d3144ba55a14f44be18bbe3ea35cf83aa8786b55a0ae2bac7618f08b1faa6aa72be1f409773405eb20fc4ed389d6016cdc5363217fba3be19c8d35939f93bb2c1ba80df0c17de6a6edb532c7d698f76fb5d08eef9758c1bf8972b5c6675511a9d95963d99953bdf1ba9c5bf20e817aa2abe6a43464ad9300cff0b93c361e06bf53628dc9b611886d2412dcd880a1e86780fb18a1222aaa8fb0cb245f36e6e1e17f350a2785f1cefbc13d3aefa4c808485fe5cb31d3f702080dee73fe10866d0bb560bc5b22c140006e3169fade5ab86d57a662c41edb39e521f0ba411955592aa7c6d68216cce9f29339694dbca41d35ffa8df8f56e3d26c9d6be45832c0abc36b7f9f1167284596f6897a94aa7d22bdaef9de685286586754478373f1bc0a0252474cf744622d13c9fd11820f6420f06c6397f0e88659008c5dc49b2e8f8b060c1253989c719e37f1845e96a51cb56005c631ed9d8957c0a19c9b4f19e3ee0eaad76c5cdb3150431ba413780f77e04366c1a4adcbb40d184a14b3e2952bd019ba6f5954903b884eadfaa7970c5aa45106ca04ca0a80d732494d260b637d2c5a833f447ea20f95dc103ffaaaefa30b27849ca5196a1f30aab34fce492deebe756c19942e822a96f39a25f49de32551bdefa823b7865f487a8233040ab8dc0a37c376a1787f07060e162d1ff8c8c921ed964ce0fa7d8c5f9a66203ce325b0c1d27178e635a23cdfa1a87408d8d88759e755381854d425f748c2b41c809b77a209a400004a6311dd70d16f24b5c5e4168a2e37a8c85424c1e80d04e106a5b32022df520a42f33730f5ecc324a484295a9cd2685ed7c1746531855fc748e87ad6400352ff47114f4ee50caac11ecf2e41b4ec3e6772b7565a0d32c5b138dc3ec20ce1a187796284e017317f823fe062838fa099259a97314f30f198e6df8a59dc668faa7ed8d9d0ba92b66024aad98f44dcdff5b00f6450f0a1ad8b49772299c02a9cee7647072609bebb5c11e20b94b40042ea5466464cf92d559c0cf40b79ea60c90a00b1ea68768c8a8de1b0d3280670ce3301be5be784bda35cc9119a037bfca042c3f2990c5227ce204f610501fdfbbe609f7cec74d8f8f541f64ce75ab178c6c2a2213f21b5f468aa2acc36aca433512ffce5d19a128ef23b42610438c0040ecadf2c2e8f07162ae110799e65f44c5f31a8c524784f5881d3a97ce18a6d2017686811f808728142e44e2addd412ce872da5192c48815a3838fad491de3aada01491c21537851d811492c5a130b2d3548764fe4e76d07f5a6c9bba18aa112930224324a2a2c2c5a94e6830d2e2d184a0153df7f50d1c0bf25aa4c37e1287b5a65eb77786169002d7c1e3b4de4ee1cdae40b1a0eb2785b09e3a2770924118d6c5bf78d37af685fe596f496ccc1f8f44371764695f6d02df33f834add7b6ebb14dc118830abcfe11fb7330b44daa4edaa3c220378b97d3a07afe1280f468d4229bfbf631b18bcc700cd88dc662224b601a6f457624c5099db02732e89f9dffcc21f9ca1948df33a1433aeeb453e2f0a01e46e60a0cd1a34c17a64f5c6c58f95e8c97837dbc72df0896e58c596246a9188204180dd6674448c51f67aa6588459bca22fcd52ba4aa38ceefdaff89cbfa51f58fde45ea054829941315002edde1fa9668ce86dd517e0fab6b3740c6acba76d6d220b6f7889ffa4d0288b7487feef5342bb21ba0613cfc4fd1ef85294b22f6ff002a77c31cbc5522dd708e5c2af3b45e893e1c566ef0494d2a47883889cc872f1619ed6dda643698cfe0fc0dd8046cee41fec9db5b2d752a8d45f8d370dbfcbaaa794f8b665297d7cbed8726b3623c108b4cf026f28618ea9bc5bdf8fb50ca912c8b06c58e4d1a12d679be9ef770adbdb492c2a2ec30756e7ad3a6c8e8ae66024f7a7418031405332a9a0b603510fdcd42fc550da3c4fede7c0e47a344a256c026c15ef58114e6ee4ef921f328ee40004e409ec0ba9eb538ac0e3c2f3d5c148de220ae88d5d71cf9821fffbb41fe8faf93f7a23771209b7f97ea5c715cb85c994fc1ccc6c8ae4c3926a16acec4ee0b7b326befcb4a2728e25d1b22e48bd431d335a0de910774854ecbb7c66a5eb34731f43ec84956721c4e385b98644a2097033ee9d8bc509a98ed8e04ad81c172462e5948ab012a098498a349d06629e6f04c25227cb60b9d2487edb54a42d3694c53a3a8334f1871654ac78d13e419ee83cf900b4a62078281209621b67295d9b519a9a1567169f6e3673d5d527daa8decd3a7f08e11298ab6f0335c0c7bf2dd9c5340971218a8fa191c6c470a4335aec1b8654491e71df799009e2581f9716d7d7b8c9e131f4205c9320046d9c7413f2b655fd857a041d4f5e538f948dbccb56ff2f7ea0daa6f4ce702d4849810bef133bda07a2b7d10889f860447d893bfa7cd963e711ab4031d4228060ae4d7bcd3934824a6484c6925d38bb30275d7a08218df52426cf014c4e5bb0e10ec6b5d8bea1de0abef00a2b48399319f54dc8f2010908c49ee50632776bfefef1579b7b13c1aabdcdaddf1304a5ebf5bd6cba74c346f2e16d6bd2df47e6c4726c1b2fb38d5db6ad1d8af3a445950dec8b73a91487b4aaf64b9fa85203bf3fb46377496648ca36e4db4e457995cc06f2d2a0bf3a5e28405c5344ccd375e3898a530fe230485db8bf384b11e37d9267d2205d8630313b156a522618d480587ffd9c639fc18a2bfbbbc310b7d41785bcb49e0b304090d94f0c4199ad19bf371a220125d74c0c5b0e281a798aa57f0cc5e3ef6c8d3b22cbb3959e317e9d471f2e9a15ef206d3387782799fb7a316154dbea52cf6e6fbe7ffb60b91478c06aa4c05a91948516c7a46929c23b6c7e7525b11dcad83ac3097294c9ba1f20d4987d83f8abf49023ee9c7f5e03f9bb455bb7cadc780659b13288a060d8d496f4bb20aad9abae943145cad15473b1ebaf8bea5fcef89e4d4afaa16784f857fecd9a4cc1435743078420f5b5233c7282856786b10616b9244c7b998dcd1c324f79c410c3a2fa913c08f24bb2a0b6125ebb789a141776d2a6f9a4d7d34f8c952a0456552a2b1c3cb6cbdf85ea41304f7107dd2dcdff0ba0cc1636a853a2054d6bd18dc68277ea0dcc6d1191f7691852671d74879e0bfc3dea5eabf9ea682a5a1afbba43dc5595f8cc5acefbd65d1f3b9937b98eca1043fe68f748f90c82640343b9f0e7b20dc12087be4561e2a9b834765ca55d5bb67347588a74a6f168bd543ac0d722dedba9d175fde065005e7776b569a887e8bc53e64d6acac6a621f3c151f72d0b58fba9fb6b5a778bf62b7ded6f198e1e0cc62b9e101ffb5e404fc69a2feb9e059a9cec03a15aa59ff6aeef86d2aa8318e4b4e18ad58e2cec0ab6b08d31f17e0550cd22dbcf09a81cd528efb65db408ee9f8e52b0987b4dcfea0b0e23646198160d53ec948ce89d157f18c9d6a24960db69a733f748ee6b5907a1e9169a53debd9f985b4e2e3911818709846b5d3499f353f9317fb8894caf397959c99c4312ac9f5758a200814128bc117be99d8dde2c06c25866d7177d50eb71274577367f2367fd5b7cb5f7d00abbb28ea6abe9ce7e3dcc0f491c97ffc8d7b06fb7fdc553e9a80f05e09bc0c8ad6e2077e77542b1b0b7c117b368200ec44f256f50fa5a9726394f929f71014c72b55c50e1aecc1caffdd4ef87d84162275938a511fd305e8b570750216ad3580a23b5b1ad1ec3592803418f8870cbb665382a757937d8f8cb71690090fa18855c49df989b49d72bb3c4b4fe4586c2297deaa312215793792351adcae7545cc18ff55bfaab0dbba7d967f981a181fd3a94bce880905cb87960d56e98e59d995e72cf55606bbf850677157882d0986c8f8d73aa9f76c49fec909df3c0978bc96319cad3561e59781da0861568e11e913912024174caf241a150722665bee997e544d0ab4d2275521eafcba21a9f56c340450ec6c3dfeec6f7bd89330a9bb40bb04ea31fdffd2e8cf4cd4e995b855a85a52f1060ef69f5821f7885f9b1e0a1e8f84d27ea2a5e5214d6faf64aec3aa9c06fe0ee0e017cbb2ef6d1d6ec82d66875dd0d73252efaa84859a2c2e6101c1d918e9c7ec4b16d2048e365a6b35387d336f16e7cd952589709c236a670ed77731e08304efabe9eeb3b646f308b9080924c87c18ebf590bd9edd985239b46c52f26e522f4ccec62c9e7a21aa8d54ce7ed0279495a3b144b0e07b2fd31c492cde7a89fea16e609c4dc0e2d7a544c895a55a661c67ab2929ddb3dbf45d2a96f1ae0208252c2eaa4d36aff5887c3a1ae861c36307cda1ebd83b5590cf770e2d36c5ca390f807fa09470305bc6f82e5a6d879142f32f2b5574f3848841c0f70ee1ebd6de3d13450cf259992dc81a4bf8b3b39603b1dc591c158c4171562587cfb0538e60c094dbcee6e1ce9d581322cfd2e5b1de41bad2186bad33c94de25ab84c9f538f6921ea2d72415e163f9f9765b975f7877ec12c1e00a9c310df351d6723106c098fbdf45af423ede257ddd2093e8ee53f3d93d56a5666b420e5e67749f7f42cffca2000b6b3f97f7dbda16f4874175616eca94cd762dc4309268fe2735a2cd7303f3dbc10078acf6452a0e90103956e3bb9c8ac78a01c02ab8ce8988eaec858c80346ae60b298c010973279ec7f6054408424ddc419a87542dd4694380864d16195028b43f6a007632f87b00c29cbd81dc97714d6b14126e18ac9ef2e133fbe2e5ce6aebabed2a2fd4f0f7710e500333741d0b8cff3d6d6b2ded0eaec71c2da47aa643a042d7e5a4aa6a94e11b46019f3a3cda0cf5601b2d4a8d050645035c5346de2b35a72507aef7bf147a0c7674049af434973718754c9056301a0e20d3078721d418f64e9234103a0133d20f4c44e1dfb29f9a1473ce5ea9391a1934640017a55992d93c1ce8b200d58ddee93c4e074efa0630c8cc632195437827215a2fad5f61bc56975672947eb671a38a62630b062325f3dd9e50433342cb776c448c099f31c873fd3f6a75948377212fd084ea11f1a266e6a8fe2147d0527b7e8743d133c74af33361137d6a741be658c278df8a6761d93156b8f2d4c31b14afeb230af24b2b69dff7fd298e631cb49b5eda5cd90992aa2b857f15e202c476b24d74c2738c3fb8edd6604b79b2a0f0e7fe8b98493a3a5a121e2fb17636b55dd23f6fff44889391892fb0f7286044d28615f8bce67426ef1e61efa24a5ee03753bb8d7b46b9d3ae3231322b56f36dc2df1f4aa724b0d547a20255bab31e6ba1dd6dfa458b80384f05a413273571f8893d16644bb9be158cabc1dc8882b5a48e71576ea1527808a23510c7197a807ff4353c19ea63af93d54714478a692c8ea099f6947ae59079a9388d1ba6478cc9840b3afe23d726b02142d3aba62ac10f91cf87c002536e66375525f5d466ce1684efbb59e3399fce3cf872954298d79a8a88095dfcef29623ac4de7ac5c93e7c879108ed2dc57bc82ee97bd9d188018e980010f546dd0acdd7f1663cfd66c33264e70c5a987ef62c2216dcec43ad34455bfb7aa2708bc4d0a5651bac35404d96d5a746ec300af237d891cd3cfcdd05a9816b1e8fc539b09913c16f1a05c56b6aafbda655dfa87851c764bf3a25ab1b555d1c61d72012b16a115b6104db8ce064e4f0eb92d842aa8d4b85983dc07ad7f8f60a0a803cc9b8175612b67816f97ece6af005e90e7105a77438ba8dceaf4faa38dc86c1a0cdd4154af6541b6687268c63d88255572898741eb8d56925200b4286ce5ff980b6184e343e6eee40b31220a3e1849dd3a2766cc668cbd9caf89ab50405bf57dce304c18c35af522a70d7e55028d6523aca825710ab63efc7376938b86b4aa3915e7b99fdb4b84e94f22d65466e38fd23c43489d56e5f31b4577e263f417975ae98a66389b946d4a50746d012be01a391bde59a2434e5a16cd7658ab4f78de9af0c91e9a21068b35c3332de3ce9b171cf605369d90561350efcd70a936e85187313a70e647363e618bb7c8f80f5aece7a775cb5a87648f24a1c51cffa7656c58daf0de1fe2873ea6b8ceef18c8ca4dc3d034a9759368040ed0896c5728609f75451857da6c16d0c4a03e6c1c901d3089d25333ef8415494e743a9df044ca9532d944dfe1f8d36bedec1de4de0214db9ab00296d76c701fae13b1154d01b15c851e4972ea25274f22f48cfb36a0d6e1c932bbef9dbd84ae6e3d1cb2c544f27a129e0407c42c770ee89bfa7a3aad3ffa1b04897a422c2cf4b9f1ad62cd49c25285b38fa7632e7930401a7ba7b377586887ac7d335f4f04669b7df4a30cb05e2ef377966ac34198c14505c7f50d35b2a3ab39cc7c677c1936893f51fb7ee8fba3414769fde1b836a7a01dd633b2a372cc80370031a39eed914ef9632d4d4da79ff06a0069e0516c431655adf7a03f3a386a39f9ceb1462d5cd553445d1ad711e09cd4c9b9c7c73814092dbdc3ed7241226e669e5bae7924ccde004555fc876f5a212d22b7a293f20e76a150e7ae8a3c4395ad0385049565402a273b1afc25c5974afcd5552109378733827cbd7594489aad213acffd5dc3ab85824200a9c4a7a8f9ad425e0e449e734d957255033cf614af55c8c48df2ee550f279835e7c76af8cb3e1614b50eda11edad982dfaddc2a202e889c960eb164ac40ff49ec57fc9d059d6977ea638ef12a07447cf2768001de7a5a70c8bded87e321537fa1a5559caf8dba3dea1aa25437d15e333d041e53d1a48b1fd0f0de4a3b8ab86389bb2b663bed382e13c8edc5b74aae2f1365fe581226cd540b88826eac993a9c1d5b06a1e0da034896f0896f535728c518a94fda1903171d846e9df268500c7fd286407acd9e06a6832fc86c601a85399df6aa812a7fc9687d03b79d0f48c9181850be3bd465642af26fb5a02a8520da775540da5f59a0345ab6ff1aa604aa464e1e64b306f770cc267bbc965a9b5630c2e2fe5074d2334322729d5f27a97b63f35dd34ba2d0615a44705ea240437af3d99a4122848806af3f8c42c4a476df6dde2c4fbf6447ad6fcb212054897e70052e0889ac0ed3067a10c6d05d07d27756341f1800fc59eca85ba6f32b49c399e535fc9da977cda8524bc6fddbf0f42ac23d706fc9112075584044ab1a2883e9a1eee5c1dd21a6e579f5f38a44ca582efd9dd0c90fb919a1e7a045a4dd0ec0dfce16374056a45701c8d1c8ffdce3e773c0f0c5a0b95ab2da264d0a16a9869eac5b6a9272855396367399305dd755453ae2c47c1520053d0fb62934e46fb9516525a3d6875340f0f5cf22b227052a8f2f0b36352ce761a451b702a805950b4600d9301ad8dba9e2c17d3d94675d4bd7d8ab9e43fd612afb8ad31f1e89c3a948bad3a5239728682062e812c29afc222d69425956dbb084c405c675d8c98a13aa8daf88f5a09cc9841c8fcc5ee9ba4371c48038c17046ade1a915a40d819a503ee76771350e060974964334b65b3fa431f61cdb052aca16a48b3e9b359fd59fdeb4551163f930201b3cdbc617b0ecca51b46255cb53e80ceb608cdea2d99212ff98bbd486f1b204dbe6d9290bdf7de724b2953922916099f080e0941a8ad5d3ac897ddea332cea774519021b0164a30ce10a620f08cad684fa5d44362d6af4a6b1b350b30a7b9cad349d538e109fa233ce73ce85ba70789eb523cf1e758a4cbf38242e17809497af00382286d45c3eb96f88f4ebea90b8bcb9785d9a11f658d3d18ab0f753bf39a4e4e7109fbe99a4792c203e9d9649154d9ebf0c0aa4618bf069d7a7e94f109f564bf8147d809aaa9a0e3596c292604a9eb3559467cf84be6c05e5c2b97eae27d98a4915491ec72f932a709ee3bcf3acb71b0ee3178a4f3338570e9f72a4f874a3890f9f6c383b75c2271cce4e7ff814438c2f973cffea0b588c2fd3cc17fcd2c9e13964985a0684dade83bd1a0e00970c88405f2300fec59f3d7dc603c0b960a0b64f0f80c7007407a063b464c8d19bcd8c0c32b80decd590a10b123f87c3ed26470ecfa03693a3db81bd1ad8131b6ec3b3d6b29fdaae83bd1afe613fcf35b057c3e16653c363e048ecd5f03954c3b986cbd470af86c3e038b8ca5e0dbf61c31bda7069c34657c36bd4e85c6ee07cb91eb8c647cb70cea365b88f7adc5761e0869e1d00dc771179f618eea342cf0ec30d11ff05eea34f9ebde3be2034eab0c754f8c448fcbe73375a5059adb438e2f76970375a68a1854a884f6cc4efbbc0dda8845443aaa129387c62d4efb7c0dd4cc199923325070a9fe017bfffc2dda8a00405a9a2f0097af1fb2edc8d2a8a4aa5524d29c2279885df9fc1dd4c29326567ca8eea099f6095df377137aa272a2015d014227c8258f8fd12773385888e4e153ec12bfcbe0cee4655650a6a0a4a45854fd00abf1f83bb515151ad542b2950f804877e1f067723058a94202941387c8255f87d1277a3c2c9c95119e113a4c2efb770372a232a1e158f109fe0147e9f85bb91223434d4844f50e8f757b81b294d7c7ca43ce11394c2ef57ee46ca13294052805444f804a3f0fb23ee464544a5a3d271c2270885df177137529cfcfc44e1135cfdfec6dd4889a25249a1c227d8c5ef6bdc8d142a52565256aa227c824ff8fd8cbb5115d9d9b99152854f908bdfc7b81b29372ad4ee80bfbfdb8d0ab538f679f014478e4815f364ebba288dcd832d69356775e7344ea3d8631a7952ec3da9b3fb600a95ad32a12c5bb197ad20afba7956cd4353dd3a34056b28aa69342afc35ea5993ac51619a6b09cbb05f947ed96af5346569c14d6491fa65abd4f3969229020eb1220f4664bb34ac7fedccdb0e21ccb15f74e68f0ef9a343f83f228c30c7fab538d6658c94616c0e64bc5b0038276dde27193357caa78dde907dd2eeedd8fbc21e8ca72d1dc9289f209cd1e1ded2c8b8b3a34b233bdaed72db5122dda5e9beaba3df1e638cf161203f29a58c9df6a4d7e1eefef87cb65b583da32dd32e23b5a2f7f4af5db2cb8e7b911d658f7deb11b2dd1d302f74cc9cd370a185179719a6928c18bbcca41696953a126d5a865dd49a323643981762442d486c8dcc3d5b76ec6ec84bc42fc61897243549e11036d0b3811e2759ac45888979987fe1bbc97d36b5e6fc69b8f0d9a535d7c2bf7c9669cdb97ca669cdcdf86cd39a3395647c56595a585a483062c8309966b8bcb8e082d61c7deb33d79a9bf2b34e7b2183c9608001408c2808bb5d7d39bd95599fb1b6a4524a9f523afb177d4e1863b0ab2076074b0aeaa9b6043df4ac3716160c5446b2f3702508211005791ec21ea804a6760581601054c1d5c3a187be0259761e96badbee6341fda4c104cbcd8af360ec05b5ba63ae65a06e0e7787c87bb4bbb5380f8658d4eaa3ee966359eedb940ecf8b315c4669143ddf920c2e0667a3913496968fc405995f77d4f176535d081a5f7cf1c55716afce8357b81dd883eb7b801fbc079f6af7c1afaef5a8579ad4a26e69181d647e7516cf960382c32106157ef56f7daaefc01e4fd3ec0201a3fb74446fe9be1917c595110f107c6adf6e7930953a047cf810e89860203e34cdcc0c8b43df01a7a65958bc679979161dcfc2d332d08605f53af8e42c3c4d23c4a723053f4382875ebbcf8319284187f09783cf52e18b381a8b03fa1e809b99813de83a8008428afaad0cad0cd15d19fa56861efac0271e331d13ecd075f0691d7a0dbe4e23a3e1b2ce4d17709d44fdf8d75978f8b4751ffc85bcbe1dcb11f6200bcf0ecbc72caafb1c32827a88fa76a0a19f958f2a15844ae5f800a5dcfb74709f535aeb66bedcf4dda2c60941e306d97d9dcb3a7cbfe1efd94d00fe15643eed5cb663e16139c232709aa03a0bcfc39d25d84cba4819049fe4430f52e5a1439e05421011498458782091202888c00f1e792863b3f0b01ce113a4f2307e84db7df577158428676b5a824445c04983033a3bf8b4b4b4b4b4e81ce9009277f2a45f263750f22d90c529b400aa6bda77ebd10eb9205c10ec512171dfcc935c089f583aa65ffd181e8383c12db7240fc225071d2ccccee23c18a256ba8faeb8d5a146dd57e2d9340baab68e47ebba92e532bca5a5a5c56b107535f8f898440ebde470eb21eae00a3e899bf9164e05a9b0c7aef2a13e5b8aa71687dfc1cca37bd35c1ee333e68cb5742e3ac4166fe97cb0e7d3342cfed1161f1f551d5151a9761f7d611a193eeabe5af227759f3f4bf7c1c0f096eebbf10c3b201d1019ddb60c4328bfc55b38fa30bb82c46fe9fca77eebe3639f655b5a4e2d0ea585a5abbbd2e3b01b20f9975f2637d0c02b606bae8e888bc54533a209ea7e74243ab538974ad4ca765e44f28cd4b9e0b04ff29b7d52e79201254ff2210fb9cfebb3780b89cb586be1ea8af3e021cfc205b1c205915f3b480398631f87cd3ffa55de44ddd6d5a75957ba3c5ed2b707b4631d1197778fabdb1aca45e81ad36c4d8d4e0fc18a600b46083014c0cc09da3382d8d3820fcc2864bb343de79c332dd3314ed91d504a29e5d618638c147e8ccb4df022ac9da46df0816f8fd966d3fd44145bc0f0edd866d37d0166e61a5a90b382194041429225bc05b33067e10acfccccaca448bbc6272549be9d548a02896f7f6926515cf1ed307c327d3bdd6cda9ba10346acb08344145b0839c18b2eac1a70e0d9815003283a9e7dd61e41a45164682002a634d39ee8eeee8662e8996de83144091f24806084091b80000353b021234d136bb2781a1a17562d91d88362890c0041b182246c7022078a1670e00914143418810d4dfc40e10322363c012445cf7fd5e7e5046ace396713487e4e27cd39a777d4e2b6c90b8cfc44fdc80795fe3269420652202139c1031b3cf9af7a1c8168039a17966f5a540819c05d0e13f99e3087518cc3453ef2e88094be1d12f9e83a1fbb2f88f5dd710ee3604028dab3b729640bf341201f8e2069826777a10e24025370890f285b0221a1121f7aeae752843d76813c1009e979b381a9291f954fe30346171d1e769fcb0d171e3f81f83fa00bb15f7810c2395df617c4cf1ff2069c017252c6196394d1677710406810d694fa51273f2b53b4c8e1c09845258f19d4e91b44044affe3eac11e07610fc3a696c2a8d5b9500a83f049087b417a34b52c8b2d2b76fd1e4d69fdb99cdd86ebbafcba86bc151a80f25be5d78a9ca077c9e18b40e97f4418bdce40b9d9690f37abd3d89ba154f9d4404a9e49d96953d4af32d1deddb20c7486981533ef1786755f7ba541857f03c332313af78ff6c8832b897a641acbad8dce8eb2a735912be3e8198965da77bbca5ed3ae8da8f1a3472e7ed38e09cbdbe2013a3b8469ee4196ad7f982fe6a366f2832a6ffa6502049dcfa4a6712e397c9b6b3ee4b36df3e85b1781f6ed663f9b1cf58c37d73ceb95f08b77b1346cbb815f7b083de3a8c31ba4a671dfcdfe0f20ed9a47d7ba0874746cb3d16e3ecc2d1f022e0ecb83c4dfc56179e47e601e5dc94fb183e417eb5c7468b7894e616f8b32cf5a9bf08b228c78cc335f2cebe035e14fc822786d8b8392184e2294b6c29c18744bd9393198c24c84dcbb2f2586a45ab3094fed2748a3b58c13466e40ee0eb9b17977e1a5830db8a027a76aecf1c820f4dddddd5d38816c7777979340b6bb0b77172e2fa481ceafefae14616e7721849099b9bb674e4fa14629a5e4292dcb9273ceb0b77bc3b22c6666ae1fe5755d18063129d2a864a7318b17b5a6bc2e783133b307e18f8d719165d92edc850b77b963c78edddddd0ad130425e926d94871af7ddf875bec1de42c83a1a6867f86af8f5de6f7f60af7d7d86cee58b0ff35147ce9ca72b8164908b115502bdc55c536a1a7e0ce861cba87ec2965c532bd056d2353ea5947618e782010c831083176d8b63ef20b13b0689fbc505f90ae73e89e2d67d16cc0be782812cab97474de3e0dbd0e93c9a07538edd02e23dcbf8b0290fc99000bbbb190a8139a6551a4413e83e8dd3b4fecb4723898d30c8f3a343d42d73cdda7618f25f37ed1a6273218a40fdc83511b6d96074db61c8c669392e4e769ff409371bfa244d8b1c40d883d50791df4e45124c6041124c60018fcfeccec0b26328848887dd37fd890a5f325f586b5c0775d86b4c35f3cd7db0880dd4619827956ef13c0e4db30c4b4b371f459e1d321b91529929b5a8640bf305e0e77977b53b3a4dcb7c9354f899c7d6185a0b9f3dae3c86613cf86b6102733000ad1f0ca746c0faf91f926a51bf6cb53b5e5c7672bbc9f190fad7c2e4f92686871d0d1e5641fd6ca09efda3c1f336502f9fad1607ffac280d9e7ac3ecb400fc4c29500fd9dbed66f0abb1cebaa597665efa0b7bd835c3271f43c09f01021ed3aec4160fcaa76f867d4a28404861f7cdf8581cecf248fda0d77d43775ac744e4140efc87f49e52878e9919cff3a791d2de62ec3e20405efaac703a8c3250f21f95011149d2418019193c19e74aa695ed66d7fb2af0f313f2f3a33d8fd8bcfa475b4aa5ca71f64587129301311919c79ff7924e924b3a3f6a7d259bab88c75c72920032c8bc7419bee222cf64985eaebcbc5ed2a62179a9fb68bce532ba8f06edbe52f518dd5761741f7d526775b476bb32a34ed46d5dd67da5b78428e2296f3696104ef1ce0afa401e298510f2d67f453c14c27a4abbb56e4096d6134530666146b268f9746a59965b945a256fdec97a5c74a68e68c972cb5b9f9dec80ccfc2ee426487d455d6f08bb8570927420a5945df77e419a25dd018c3142ff11a19c3b219c703903bb31eeee6e8c717777799013e40a4debae664b63b7a496ecee97393b1a2508bda2b647ed7757fb9d12a59452d26fc618a3f6d2c994f39b9ecd9edddd562f8d137643c4d8d1be80f409e4a32f27a5b62fe9639c73ce0e08e963f784e99d6596f715b5bdfb66ebc7dd8ddff4287badedbe281bb21532b9c5e810420823a76d3a6cea6147bbb0a6406b408b94b250a16b33a5af511e3e5791a40acc43ed99717e67a0067860e0cae258b83852ffbd30f141cf7fa59712d3dd5162cf79896c77bb5140b220ba03b9f2a9dc284843c63b809179999b00a5d4255012dc9d51c8c501bf2389d4d2bd4a389a4a17855b54e811e6b706be94fe441965942883a880224629a594b28b714a9451ce20ca29314a29a594314e81b0a7896c6120f77a8f965909996029e147ee0eeba24b841e2bc3be80a5e1b14678864d2a74ad69e8b5437a9768b03be28ec5302cebd28bc35c2bbb322420249a518deea0716a9aa6398499a7f58dd84519407b4027fc6c976832e6932d0c843eb18b66daf6c230de343e504aadcbba2e78a3690e21ccb26ccb36c7a4686c8948e380642fa81af5e1072139ef0308f6d87dc02a10e5c30f0e9852bffd9608cf7ec11d4b62affae88c421dfa2df771f9d6d5a53f609c6ffc1c22101faa920e46a5b32c55bf057a1f7cf8814f16631876ed4208af6b23add77242ecb5fb6486df1a7e53bfd8e5cdb85772af54628a49513b6083fa2d10ecc0b3f329f31d2b79a01aa76d3f04f940a4926afd218a0f4ea8dc0f391e900395806a0a2a0c0fc920e541132a2fbf4ca8187a1de8fc326952e4bf4dc9b3b7fc326952e59724d181d07fd98a890ea868280d870a13a8960a13043d0dac592a749070e0c8fb2f1544763ef5453a76c72c76ec8e2ea765591f091669d92393f01c79964dbce4618f57ee5ad0a0a11a72c18529392db410f4f2a252b9b84cd9993143056432e9944a53503264a85631624809820123874452f1b4b40cb1b0f8acac4801aa55a5331afd8844aa6d93b2d2b49dccf779f08d0af598ef2f5379f620f6b83bca934a1e699048eb0cdaa94ad58ffed0209aa24da80fd07cc21e4f209e79047b8201614fb09f0c95e1602b4c08ab82ad30a11fd42f5b6d93ff66b6fa32958a4a06250b8a0269c084b29edd91a2a95456a5755a867594344f122cc51e3bcc4ccbd24bd9614bb026cfd79565d3fbaa12240b3d20e8e2bfa993055151e488ffa691a167a7d6cc8658c9f3aa69da5d181089e8cea5016d81d8693c2db3d34360944fbaa08735944f2fbf34d33f19f4a2b3ab9651c9156aa2fae57bc5a7657a3534b3d510711af95594916a8fde824ed34c8f2de8ac0cb5a02323a5f3ed327687133e4117e91a7bd96ad5054916703fdf2f35a90c49c693ed745139460f17b5aa5e50ddf79253e10baa65da4d317abe63f4c4501263c90bea7bd179417def7c5f355cb7a0f42d83fc3252bf52500d2fcc2e7dbf75124b33b54fd6e2e80c28947c77ac0b8df29a3729d67f62a6652483b08327a492c378333ae4cf57191e6e2f6cdd270327da917f7dc9283a6175738c3cd37cb7edbb4abf6305d240bbdd1cdf0a847bbad6632d621913da29f3f54ffefe4743c6c8ca28851c5d1c2f73a62dcbc984d382dd677a083b8b2b790ff75a66ca4fea7743eb8125471707741333154100b8f05cf3c578f68534b47fb2fb2734596f69e8ee9fe0b339be87ad895d944c7a0b55214459410294b8424215d9930508e0e888234649d06a8514d0c08b238636a0841a6436e8eeeea6ce04084a8ee083e7233851da816384500aa8b8821450300db22a4ec0237cc0250ac108499e00bf4c8cf0f37599b00e708413d02d875ed496599edd47aa8c44f6899e5ef1434c1104600e2ec2cf6f15bf4c8a90f319dc604787503fa844160105b908d9f26902f37fd417b2b834be70d4ed972d4cc718e79cd1212744c7894219a9a81c88d384ba8a2ba7615d50ad1f227eeaf3dfd64abf30ef516a3efa310e01bf7db389bb637fdaf4ed824a7f88f89eb73b72581ceded324b830028d35e2ac2b7b787c3e2e8de95b6dd304a24d91041e85b7e6130878cd19252bac509d13ca7e5cb59bb5f098866b1538b07c3dc9899e19a41dd08841996cc1d23b528b5442ca8ad335a41164afc94cc3342acaeb0c4124574a2a802144fd8a2094b38b0a40b9c1a4ef2ec5e161c78f61b310b0cc8234643c8a290c2872a4421892328232fa06206221c906c20da8002bc78820a23046105475e20031b8c7c388a2a70a0facf610c1e568103a0e71a58c3c3d2b47c022941afe50497f3b545210b24bcd4345e9780be4091ba0f088ce7c597c9959ea7c002174928a1022c70208b1aee7906f2e259722fa522f5f34a5c1968e8674a4a2948defc5f091e916551c4224b6812638cd7bffc11404fa2f091520a4b2f573081820e57d26ae85b7e99ac9af05f75c9021ae2057e708bebf9599ed56797b67cfa0a40137be20c1a0c5d194b35f6bce75efac901ba0846ae06f6a044b544c59ff6dc4ba5cd04f513b9749e2356a9c676da94d21863ecb5bad336cd3311c76f756bfba29168f4d1ef1a45dfa21c460f7930e4ff2cc0ef9243ff57c493fa45dc47bbafc77f40665ec3344d8bf9666e1e967924b5bdb3dc5a3dad955e940703a51e9ae0970911509ff576133fce7c7b84bc318a5c72e86f6f917713ebc993ea22cd454f5ee4a37681e65692b75cf48221087dfd91672b239616d1b6b5888b2f638baa6ddd1b075fb4f1d0dd967f22dfdce234cbb32d0791d52ca38b3abf91affc1879dd1d2c428c44f045dac683c86b7591a83a0f66e1f64523dfbc723f462e726cd34173915f5b0ea32e8ba15de031ff4a7c84c708153efbc8254ae2c89c4cc4c19719d81d42c0d5631b0fed22cf62b8c549195b0eeda2ee853dd16ccd4525cbea6a48c2a74fe44af8f443e496b3c79e6bd301bea0c96b3c686e6d39885cf463390cdac25b1ef396ff34cdd6c41f5e1c968c2deac8a7c7d86ee007a289b8f872d361f423986f2c56bf5bfc32c102e7b3199a0c2eea07df459341a54e7fd16470517f2c7b41e0ff58f6bcd9821491d81335b3e8b3c088638b925a60b82d445164cf72fe8dd3b8af85ff9887a7f4c51eff0199f90a23f20a742734a073819963beb46566128c4921246166d931c6ebbafc66fff2219f90872eaf4939cb7783431e5aed53486f206b22a0137a6a32979e04ac71e2019a4bd73a9718563544c4b0aaa1ed019965cff27e990881c815557e995ca1f3387e990421e8ebcad2cc28d979c28a12407185504d3bdc74b062f4c93186617eb38f51ebe23ee89281ca2408409fb176f190672e4847879cc57d423eee4c2f314511233fa48cf6e4645996912ca7dacfcc91a1426834825e9667d61529a5945a975bd6454b108369f99cdd5a1d8dc889414e0c7e7a2cc481381b848401edc38c6c723d64507dc867933b62b9dcd06f393cc21e3b101cc4616fe63c6f0ae01a0bb5a47e1bf43d18c0a7e8f083934adf78437b4d6fb007b1a8b0fb6edc804dd01eb8c642594bea4791f8583d73073aabf9c551b6ba89f8eebecba9674a98bbdbf22c1d683c16bdd7288d3a5fafd955981f7e521d686450ae00e6c01dcf9b5999e20946547cda299e5064a7e8810f5779f67cf06e6ef46603c4a85ea2b8c327283ac2a7e840348d905ee930de898c843dd61109198239a53a82a29c2892395214b76e72a597dca7f111d6d1a87763865215d3b0aa65d829a55d7256b1c7f222415515611e3ec1a8c3479e91340d10dc84fa6945d8d3616f873dcee6c38e8d2021b14ed3d81059070868a454eb2a4a10acc234cb2b9d8df24dd2bae31d66c6aa134a563333423aac09831a954ccd87a6f90ed13838c41e6b9ce6cd7d7005b79e536bad8343b1f419a7fd075750e8494abb4a31ce0e0e355cfde48e70085679fee06a87e00a0ac9e4c02128f404ae646b2748611e4bd69cb3c26f9a45abf418295ed432c1f6cc39e79cdd55803fb4c70eac380899b3833810d57eb303dc81fec11e98042a814df824f2cdd9a1133e69ce4ec4d611c11e6a13b1b305511dc461cfa2122a69d267dc078bb02f10a5b5964aee9e4767745ccd3d3d5970d1a1fd47370389440a7b8a50b97d422008a67243e3815a390d44101668e661dc2eb09cad46398d45b4b36a505d72b87ce4a38bfa475fce3967e5d13ce44aa47df9ee55fabe6610f994d2ce45879f9d4b0ed32db73a1fecb50f79d955d3d200f1d1327c32df6e035796875c895dca628105976a53d46f817a448dce4088f41d22177bd91b42d8ddd6875cf5a169dc811cc88170d8a187463d528b5ace56c73ca036638c1d638cf1c5dddddd0323ba300d47969c51528168ff66a6e002c9145b34f1196b70bb6106f6d806c37cb9cc23c7391ce1e663d4a954250837ab1be6350ef59136157b66061d1ae9c5e27145a6ce30354ecce911799fb5c43840355d654fda50f68644f6606926c0e25446848c4abf5cd2605d6499863f2bd3401a6cbb3ca31d99b05c729165a477dfc02e6daa26235b912dcc47d79ba156b3bbe73164c8f0a765228434a7521974d9c08307162c257c12d23a0e334484904b4814b414b5a68cd49a32461965a421a81ffcc1611ad864a8dd09111ad804cb28699a4e22a4a5483e2f3f16d0df08fa9fd87df11ba29325c9b2dd8dbb4ba306297777c718a394526e36ddcd39a76559164bd9cdd775611886655acc322d6a9a46276977db36b96d36dd6d1b0ef047a3f6d166d39ded6ead7565656565ce492a31330b0b4b4b4b4b0b4bd9cd5f04b2a0e7ee07c908a604a604a604a604a604a604a604a604a604a604a604a6048604764476f9d41d2d959f618a258656dd532cb1f3dd8d051675bb0fe661e03a94c0741c24d6db5d0e4853cadcddfd52f3d1e9d327e6ec33ddd575f0c0a25fb134e7f48f4eb7669c73cef982e239e98c73ce39e79cd37956f6a64f2cd49d9f9eed340d75daed55c3fd40b68cb0c1f3bd560b40f58b59b2248bfac5ecc4ec70cc8e8c5912658d314649aac948fa17e324e627e6898c810223859705e78cce0f3b29a7f3420b5b38da4b45e533099986aa407a59ec9554472fb054daae9333422815fa5e3ca89f2995c55592dae2fc2dce834d4ef824bb162799f88733d3924a79a029ca34a55665a26299844c43a62a22d48b7066e06c5596db8636a16d654ab9388ccfce1b6d2c33945a3a742c67d147fed5917f259791efc3b0ae7861d7d5b1744d6090565a628c71976bb0341be7406a9a84a8dd91c5ec50611ae6e1814294c7b4a47edb8af9c72f23358a8af06934bb6d25cad93132121d11211135f1a224b4098e22a1477a1bda1dcbc38694d5ed80c38c29d4cf9492aaa85da92aa59453d39030d093275090f8d0e387ce39e7b68a305a4fd3684e37df2d4679a8053db4bcd082d8839aca946a9a6dd5db6a73918b3a22b41e8df3b1310144cb90a046e1a8642963ec8112b323e3e9b43bf88e37538e34b5b49c35f960cc2cddc7f4c404f4bc096d4296b42ccb47d6d57da69f51e44c4018775c4363a765da637c14ea6acb76003509f470d56c5d0f313114a0790f9a6fcbc48e09cd371b970c106142f3d8e948962ba5947086e8a3c0478f373112b889b989f9d8b3e1001fe5794973f64a9921aa779afbc2418d2cc4a62a540ba8e21fd40905638e7c7b7b308786f2501e6a858f7bbe4d29934f6faa9e9e2e82aaf0b338a8a83fbb233349538aa63d632fea10d194e2d3c6ec34cd55d3c36885a5059a5c8208b2ad442822d86b17b2432cdaca981df6a694fac5ec04a1ae16b3136384e9113eb9ec64d1b42abb036e124a6c22f8342fb42cec0ef85a16f81443464946a934b7cee4d332eda22d9f72506bac9a46e475e7a90d9defcf464e8da1b781fa8e5ec33fdd896c7a88a248663366ec7ae6328fa8f065225121f46b07b5def87c16d4820b313b2eb8e0428c118ee3b8cd068bdd5781211a3b4da35d236a05c58bfbfc4b1617c45d0c4fcc8e115f8c0b34b46db509717731a922a71c6384bbd33624558343d5d484655ad4699fe9c9b7ff0c29d45511c96868974fa5b1dd307a25ec752ff9f6a91f44691ca7719b0d77fc64775f78e185176262622e8fd96c2e00ecc200030c306c3623cf6276b8c6ae0d1b366c6c36d8288618628861b3c1663cdc78606a4d798db20cb3a1ed66376edcb8b1d9ac380e0d078e15c7b1d9ac7866fa31f9c816126381859c525ad29212e2401c54109f32d34fc6ed9b52304c2918dcbea9898fe989090806676ac2999cb0d7261fd313f6804ca92a2a768d68c4ae116571ec1a5112f7694a3e202c0a6059b1228a925f2b9850e405f06b4514249e4e81832a6fe3778a1bc870b90c328c5c86cd66e470bbc19cbb19abaa394919a1426fe9ed18e3a600aed13aa066a7d6e594fba1ffc92025dfe5d866432b30f4d2a3a4429d4e2367779852d8d6c3d648a06db55fe71ff6d804c563184126151321a6c1d950254082f663e4db4d3f4dc335edd6e419dfbe55e193cb777488e2d3cf8c256a94945ad76545fd62760420852af4cd4501578dc8b755d350bad251a2313b232d1b69903f764c54b12b6eab6d856d5528cf56856db5496169f867c3c2b777d6d2d2d222426d43241289144da9fa6dab989d24d491b3638145cc8971c6196384bdf61823db4a84d2e1932542b1d7bfade8b6096d43229cb9096d55b621f65a8423cad9563aa84066ce47e78e7f1888bdf69f6c00da5545d5769ac68501d5a5019b6bae7516706142e49b6bbef9d6d44e1ba2712611a7ad1ebaf4981d944bec7280463c77313c0f1f7352bf989d989d181e3e6d35f97cab54a614ef7c9b52190fead7f2f3fd7c5af9f56c86466505a9b007e1aa65da356ee81baab8a850b5ad20aa019a6f1d44c928ff999c7c6f2b930f7b6d8240a83106b5514ca9e7ce94ca41fdf887f270d9495b3ecde5938b02ae1a53ea637e7dfea0fa6434da420c2ccdd66dcb74c793e9d02efee127042000013027c06683c5c844a89ff6a42ea4c29407aada45de9bb7b5ad8c30a5dae2a27ea694a989c9874f995f9e355bf16d4aad898aa5a13ccbd3836fd7361bbe6e50bf981d53aa6920ac91402dc3ae7152d5422a6dc24e7e848c3ccb86d4e47c4384b09ea90f75022bdc16b147a151d8eb6813f69a874ffd4d9bd48ff2b8c59952a614cb1939322152c2c4e61f4dd18e2743e998d0509008c4691a51676ac29b56336e68c7d5a4dec43c77fcc35e3bb4a242ffb6158f6ef1699923f5a33c94871ea14df844797826e5e149f93879da40df506247fa4ca928a626ecb5296572624a49a1c2ffb615cd68d828cfe268bfa8a85fcc4eea6715b3b33bb267a0a6d1509cf350d3d956dbaa69ae9aab665ba6860218e0001aa9868d1b38643c180010438e191962022080190670827981a6d341801d34703514c0000798325e99b68960906298a6d4b7bbcc694af1497ebba949926f37ad4c55986600a97693133e2d932534f1bdada0fc7f269fa06fa881ff4caaef14132588e03f93d0b7e9e7dba78cdb6a13e21347f16d83a7875c128ae00558b094cccccc90afb8023af3ce8033baa02cbbafab8899f1bbaa9f5429699aada9aa2ce839333db3298be2d9339fe7a52295f053f62495b09e6a77ecb30e41e06b3fec7d4e520fa35fbe352e56e7f280e8ed0d907e7516708201d1db67f765430fbdcb564d6381d8b934207a7b67aa96819e4d6a31152088f2cc2e412814ca082a4490c27996aba641a804eef555390bd18c00000010008314000028100a87c442c168381c28c2a40714800c7e9a48805e9c09d4248661ce206300000000000000000008cd80010165b96583d86b58cdfc65ef9543026fa581cb1604eb13cae7eff8c37ec8a13d9dbb2340ee80f351a6d75f32580bf13c809cdb1177b8fa75e66ae12ffbb81d4abc7a3eb17e548e731885bea8d6e42ff3417af385cc83c8850870d01d274356624fec380f1439c2b58517cf1e25a09cd9cecbc81dbb6dae7974cf8cf241da1b2559154b8a961270c26c7aec651d0c556b9adaf8a889b284584e2e44d071ad85eaa983d6f3efa6a0d7cd42fe84a14e55c2d90fcfbe267cf6c886aec3a25e0f81ceb5841503e35107e46834add319541f197c66b1a60f57284c7026eac10753c06b350133ceea95cc856486d5a8da48f9290e343d19d02dda456edb05a11bebbe7580fff1e036efbda08b9822ab099e394ebac0a5083f9887935e54007a35038cc4044b8060340b2411dc96b87d22904b636f39228c50e0236f9c630d5b1d21aa8a129fcd574d1ae4097e41703efefc9670f2d444f2ef7052d52e723f9a638e3bbb95649ef275418a3dbc5a45f00b2b475b1f39e816446e42f62c4d9639abfac54136581fd1f13a7b07e8eb4019f93feff2390e421ee90dffcfe2d2708eafdd4d9226c20538544c728938e3c2e5328b6cd9afaea2f2783364c57a1cc84993da0e721a9e52a93eff1974ae971f38bea953d3f1c9e81e11fe4bb186ca1eefad99d8f85df26b6b13a20ea5922b700dc40928b339c11ce338d7162cdcbfa286cd1392a1218a009f51035beb87a5ab406b685546f75f1d839d0bb3a438385c0c5c5d5bf04458a240107264801ea2be421df06980ee8050a3f818a72dff82009ef72998da8c235afd26bb2450d5279f5a72437e03b43ac0a7aeb41be4c343643bf114762361d30d5b6bc8e59796fff7c8b92e9c8badcb8abd6766a6792670af455891650eeebad94c5e349db4865872d9a50612da6d84df988865b0c27c663223b7c2eedbf22a19053a9a196108dca634249216c5c54e9da4a867272bb3b24ecbc04d9f51d2cfda546db13b560a4cb703f87bc7fba412d6d76b49c2f5b78784f8f179ee140ae6dc1a95e43181b33582211f26ce2a125c1135d335e808242ab506a8767eab14a80481c1fc35753327fae7c90ecff8fa39e9e40afd88229cbce03eed0b40b799c00057b44d2f689a9128a5b400244edc026c4e05440e025094c7cb3598c5bb635a967d455361a576b144314d1081db07e1e3ee26f7d188e85cae85c1c046dd04ec632551a1c0e3cf436cad7cfba0a885b8332fa789d327c8de392cf48af5230abd693f3eb939e0ec6e05eb85fb354740efd0ff01f779ed55e087ca52c9472aefd311693a5579a488e4ec402fcf037a5fd8e4159c78e2d24e4842f94d3dd878c055ae62d2043db5d8a7632e73cdab59cacfa269f0c140eab56335f250b85269e2f4fbda6773d134f8656803be958d4b0b5fdd3e38e8abf7f1e4159460ef78c21f856e879136bdb21e83a1a7ba92370e161662554904463d231073c98bfac2b3e620912c6343c6f28b4eac1c47ee34678f17605760e44024f7f2d905a48660d3f2909525584deb0019d01cfa18f1ce64072a0b53116783795b61b5e359ed4ab17a76d8509903f192984d0a419ec03436d4f86d631cd115616e0299d5f7c4246ec62a7faa3e6e4375a9a751e1900595f17bd7a4d07e46ce634910cd456f0a24f038ffed555435d4219bd4e9320de96741ce17b93e7494b87050d2f66a885075693f98d3f7b93a1d4910199c01d418657cd26b018f33f09f7332c49c2418d00581c9ab551aceba41177bc9034e8693c2319f75312c55c626a1266a9bc6f495c0c12822dc320d13d88f9d0c40e0f614bef376252d932bff258d5898db1030879bcb2dce8e0c5423372ce291b55334fb81d12a8b8d29e6f973d720fa47b1dc6a8f5157c8cbc0071ab7c6b73bbd1012bd0a5473f3a34e128c6a0f0160c1f4c9bb00e3a9fe7483c46373460b421aafc3849b63a05485578a262e4a94c730dd4df202df84b0e78bb3e2cb2b04b97e448768d5bae9673ee6676ecf9a1a6bd6d7d866c2692df3f56c4410188bab74ed9d548cc79a11d61bf9bee9ef225590772641eb2e884a0e0260e6643715ffb742dee6a5ba1196aa46b627efe18bd9a37c638d4c891d1db651ece6d3dc2d9a0adedfe79626ba23c7dc334447916a951e4d59a10d1060aaca3f7c4e8ad5413ef3bdbe75c0b84932a9a643ffeef8b2e28b185debe03199d457cd2ff562d2b459b2a610a1f63349e6b3432bc131493c898113d46535ec0170032350585afdfcce0ce019f7d96bb83ec59542c8a6b28c59b6112a0f6a4fafccd00c131dda2b5acbc8824a17dc20bbbb23bc46e247024e02a256ace3cd883489beebc328844f8fe153fe10070ef009d14c80cc7370729dc4e2d12bf648ba321da65f3d1ccd02edb3d034fb478ca9af3114d11e5eda07b2ce220c4e352921af55c66c73e51ac307b835ead9eb8de2b75643c3009fb9be685271f7398b9aef0e0b3306675861874831b95b78ffbf90ecf0d062dac3c0afae3b245b1f58771b2cb4e5b4a072318af38d144073ec0feb9104ccee2d97af89df3c322401191e0f12cb7f3cf8b4c06ef1583e43d26092a00be27e1af4de2ab7df9f928c8059a9a3ceee862fc9ef235684ed8b368889b8e8ea71a0997c23c135aa23d5ad2a9436da88b798a608baaf5ae952f11e26f9717b78fdb7613f940af82042931b47561efbe0de422eec65c882de17517ceb18255578320f2672338511d808a5291f32a460664079f2d0a309b270cc050728427b50a29bf9a215f23fb6af2640ed70260d7f2df8b8617a056a0485f9372f3504232bd5026eb60fd8fc87d1e35455cdfd0f8cce7b8a6fbff469de21844d799e6490dd91b54a51f3759d2353af09b9ae5c51a371cbc40c55ecf511630619c0d2ba74ba85f810fe7c260c5addb367a94a4cc66ab81bb214d4b7d44ef1899949d8c5821e92e8ba7a4c45944c547c96ffa59e779075a49c6d1a84acbdea791e308cdb5430789ecf786305c4d6160c751a5da34d987711306b6d0873841bbd85f7bafedba248464341d11681f253d19356c4b039a8a543c3083de73ff36cd226488fd0ac31fe093904351760f99db6e64cb1acd34c076e289b89090791701f5e8d3f0478af10863825b9af1e546d92a9e18fbe95fa2585f41c98b75ef4535821c290eb4b1080da90b667922e4e78b6e98f1e81c50f244f20829e6e167f07fa5138c98a78ddd581b1e5443321117d3aae22254c212442ecdce2e24d340406f21683845ca152a2464044c7c2bab61150009ae03c2180cc572f2905bb14b74b7ea6c280b9cb46834c50aceccfc7941c6d5dde83915f14e29770c8bce6a1ddab2df1db73fa11d4ff78b54dfc98c66c3967c2a2aa4515c5416af4809cf9d25e11c705747150ed1ecd2390d8c127b4821a5d8e239085ebd901ff8eb547012aec1257f2cb356c6ca6ac2f1eacdc265efeef68457e89239882d2639494844e150f2fe06f515bce684871ac0550691107736993a79f852b0bb9e7970309985f2611161f10e80dd09c259ff95f38cd9cbf7d38525bdc1d8038b088921cbb72b1acc384eea191a7984be30989c61b416e79fb98224ebc097fc069385a1c906c5cc06488f7cf935dc7ca15731917c4625a47b13e4b59eb7e943c95ddfa99f7ad02e01424ed6e1f8400fd0a3adae8695aa66b8a876036d9d92dfadcf4cbff01ddf989e038bee81feaa09fef6222cef1f1b26ffbe77fc505e7d9ce7be17b685d74edfa52e80268deae75caf221c86b32bacd6ded337fe86c004787b3ef28a6a21a90d9a2bc33b2ad4707823ceedfca68266c8be69d705fb107118c65bea15584e7381a9129637c5703d2fcd608e7cbe965b7425ae02c6bf59f09477eb480b68605545962570acf348113c1896f10525801dca71c5ec93899ef3683008dc570fe230ac706d59fe54d3085c26315b5d783ff09eb65d7032637b045947292b2e96c1fd3932820f79f3f101bf8649f6f62c6837973f987332bbd7430e945c0ad4065458a8415bd50eb81cad0b137e70c6bc61c331e3ebe4986453deb2f1642c510986aad25ddf8118b9ebee77a3cb98860758f7d70ab0f7f5ce76c9199cd98ab5f386ec34ec596b378ca667f9ac0b8e048998594d24733a8402c24020c951cbaac2a6b69e216e57e6cc2de40ca698c14d08c5ea454a82f22863f0baf5c89637325eda290b26244102d43def2514ac005e0583ebb248292f836e7d8d2ca03b9ccc22471455be36967a1cebacd4424e017bdd5f66a5b496668ab2544838868704a8948fe3861bb39028baffe382a0d7644ed285fab29dc353bab2590069972c27c56c56a8a6441e85962409f49abe85d628821867cb9533316961713cce0bc6559e9e975172d3011fe9e07c48baa14d938b9aa82731fd23f86a611e70d7208656e6e262188e8eab65d1f43f561cd9a426cf0b34654edeb575b121ee8e653e05c487edccff22cbca879d7ec7dc808c4a7088a9a2a55d4074cad335655eafa3dc9b91d34497d25a6ba0704dfb11d4d61c83ca510752784d201fc1d4ff3fa9c4e87fd53b51b4b0c8df33136e1ea91069c1fe9cc0d3b2f0a876f29188c23b08cf1ef26282187bf07470a8dd8f4b99487e1441b9c16f4929905451e48b7c23d4718678e895e8a1ccf7403a79a079e3ea2286682880e675a6497bf023aa114be48c37f764408e85c0e823315ac1d213703b0ad518369c855998fd3e82f9574c48b9514f4e6929148f723618193835afa13853d36e672db5bfa30c793d38c4d96ea791f40ad7fe7c732290b5e86d47cf589bf530b1c4142646ae6318ec33d86046179c7e131f7be54b3b82ed8923e59c1672b532069644ff181cf973b0ae41c93b63f13b117412b3805b1396138e0873a9bacbe5cc44ad7a768912df58c7f8f338252525a2c223c8dd7ff1e338a9b1ce2938aef15dec861f0a041443598b36046c2bccc1f98d1a25a7309e8eb94cfd2c93063e03cb846bd3a18866717a8c0e4557366392c5e5e6bcd106897fdc243151a9111d8e4d0e4a53041fa55d2da20d004832082e7c63e75ac9a5e99ca8042e921480b24f64d8dd5f8fdd8aec33b991e1c1736b0a6a842e3cdc6216c2c87354a16e18deb05a0d7b86c69978d04d3dcca492338181104f131afbfcf5bee0b5546671b0c8842c8f83380e3a7aee499c3bb2d60b77fdf7400764146ba7b8052999febce2f50d487ba1f6b9b5d03c0091288acc4fd356e5e0be6c494dad90346a4449655c92766cf0de83279b7b3e4fc9436af7bb58b3a41b558169d7bee77e9da7d1d67441b07e61e7f07b740bfb3a16fd8e8fb2ba60227ed14585ba6c9d0df9d416bcfb511573388e6fb2e6beab08ae6008ae039b73c0c098fdc37dcbea7b579493a8f156f1982e168c4f2af706c09c03fa4347af606879c5649c81c0bd0ebe1b8a808dce34309f95f2c4d4fe9a2799fa9114ecd73a967a68159947eb89321f1ac15657094c4589e03a95a1836c8befa1b85e0910e2f0d2bcbbe7f0085247d15f8b4683753982aeb68045816fdb25bafca3c262b64eebf66fd16ddcb8310f5d7f07a19dd75dc2567d7d791fb8c51a3eefc1f25d2c0bb77a2db63b5944df7261c75c813e6b3ab25c45e213e1e575486f4dfedc24a9b625fd0bb38908fc12721865b00f8001590235e7a72f7b08bb015305df5acb72c576f12d0d794e3da63143a9469304c1457763601b99f9730cdb701faa62ac6984e23d8a1a97d571e4c4a3a0d27fdb710525a88adf4bdf535dab4b15091c58dbab4325a99c42f1ca690f4f286bce1fad008f236720438485730bbb57448caaf4f96a5761882898c385abce381c25ab4a77c593de5de1c866f63bdfa7c9e608ffea9da94eba465cabd425afc3f232b1cb8aacc9b4e67226ba24a9ac897fac13234aae782afb7048e48e07f8004b6e139bdb638c58435789eee140aa733e22053307ead2afba26d43762d3beab142d20d9bc986b36623508ea72ac29313ba9068e586ca730346c23b772a7437bcb9479eda54024420245ec8e3bda9033e6e97a873635683bd98e699f431e2a0d737f67e5de2239b114b3ff8b939a87a7d8e2e68d52668cbe1ca83cca66c6def4dcb4899716467f99336471a96d6583b4c93b11646b143f3917f155849041ce1587ed718598b8b26130f93145e66dfc44b4e047c945b5d322dde19cd5a5c40bd93c0be745853e51ca2165e476bd00fce14af72bf90fe375082e25da3fe3a54b103fcb814cc027dab0d34e723c9893359511536e2fd6256b15e301b0f90474e42f100ac1d75eb42b606c4a009ba61b180960d93104682bb3222e2de2f4d85b3cab837387e4026fa2317bfd080f4f0af267517b11f84b8d5ff6cd75770c3698e628625e2d5a2114669eb95a051be14464182eade061d840a9ec31d4193e6180cad5284b69bf4bba2eed1489617a5aeabaff51162933cee16be7007e08f856ab687b7bb3cb0ceb0c3bd8b196ac4a4b14c4f49a361c1c722e018eca0bfa691f82b70c7ac7695f62e689208dda68949a862044e3d19e822822308c6299b402e061350afc13dd5df78790fa243302535debce105ba5c096081513573ea58179c6f923372fdb4066c6ca0ca03bf8b818bf5bb8453b956aea51aebdd7b0c06de9817e4de052ca6ec1cafda7483354d655ba5e9785f99ecd55117e9a7a409378952dcc807db7d43822b2ea72003f53b6905ef4ec7d4d204484a42c6a1310d0634555c2806c57c0279f79e65cbedac9a3ab3b8ab80ce39e6cc785b2dd5b94e93cd331844b8402cdbd7bfcd47de6aeae19e208955f908948d2b267a051c5d95d7dae88642b3210833b466309d4d0185d751bc5902d11ed5030a90be5c5608d46225aa495169da2baf31de9ace0081eb9f3b65d31e27a00e6a40fa27c6f05cb008917c417f82e5f9957a3c15d3159abecadc097afab42bd29f137b7b34f2ffd81a4e7201d2fc252f3d81d7423df2a289f915e68e28de804b8f320b1b3da2c08908a2bc656e88202081332b547a5d159337d6371b9046d74e036fa48149373390ff8309fdf51cc0844eccde0fd633214d60909c3c5b462cb6483986b3583e1ca6052d763d19ee9796edae8eb4b00583aed0dd4f24c98c87a94dfeb4b6a5ce678437877a911d2fdf889e8bb0c24c7bd13b62d35b601040e0afbb7d7fe0fd52bec7a7b58bee21f187831bbddfcb5e2f734d57d8a40e047088b388f10b2d7c6421df3ade445194a9675d4e812b03d3d5395e5b1b134de0b10cbf3de3d816e16984acb281ae03fddc10a1b845068797cbf789ac2467a9822916bcc6c09c59f5f272dc2afa7efad59740339962cbb6f6bd38988f541df8f8a792201d17cc855033999678946f4703f9c0e08cc6e32a61dd51c5e7578a576078e7a01059e9175e94990528c59e585713ecea653ac3c339659faca788e3e70034c6009a3248c1bc69b1824ff0bec4e3f640181d841d375682149f6f9a6b45b56bdd2acbb491728da5444936bf2cb0ed867a5e7da3a5f6e06945c5412cb1ad10b3a157a6e6a6b8d5382cbe85cbe437b28c154dd7651c82a062876f5034070fd84b93eb019477da5f2ff81be658b6c405b9dfb4bdc18ff28a62fcc2f5a2bbf523840bd68945efc25114970c8195e9bcf91f51e48b30cdc070f4666e89ec22bc131a302e8c1f8555e522394794be264435990fdbe950918371f8e6c213e8461d804bd33c5df5fe2876b941c4930bed62633bed8bb490e4fe35641e35db6187be92cf174bd40aa4ec7e657054773063b86c4f56ed2a7578e439ffb624d36a5d5348c51b572bf175e2c50bac35d3e2a1ebaa4b9a320d52733c62f61d418a72958c4310db8280d8c8c1579a0c234e9cbe7431bc1e7cac23f73a4adad4b149583558e1e65fa295119f59d0e2e2a6bdbbb26203baf8cd10f35d24e02846e354aa4dd8938a0b6e378516e790cf79ce1cffee0aab270bf86cb20d920a84d43a902e0afe208d10cb6ded0eee3624333e63de62728a7cb39c41a0c28f309baaf4d359d4eed3e36e6ae99b6db3920658ff78c74bcee9a57f665d1d3198cd37547102863879f668bb8bcd8c85b6062f948e8afc73be488ec9324c04c35be4371cc7640bfcddf3d8347a8aaa7d1b359e6111b8a71f646bf41e35f0561c05fc3d5e7f4b345c36a62a44cccbaa433f86c6e7dda6c72bde822732c1d8416495aeff2ce1b5ba78f2199639f80656e7821ba61fa167ccc67d180cc02b9db21b22ccf337390e9cf8cf2e9cc4a5a9942cce7e661732adea1b82ce5b3e46a9a282ddb5d7c560b1d841d8c86d91aa2e1855d5142e2162cb4fc89c850b0dc7807b39f19c5c8fc3f36ae609e5bbdb0dff45a453826e0415db9586b54234d203d2a8534555c9d8948f54b0c0db7ba906ebe09681fa5255b33d421ee14b4f05fd280ff3cca901ffa92649edd5d1772813ed65653a7a1f446a288c9adb9841cb22792c5878a6207e7936ac0ecd2b2b730667ad44209c3123427094c99194e2ea700d07cf6d8ed75e913ea7b0d72bfd9d2f97de7259cf6fbb0c1d3077d065c51f60ada8752790d5264747cd631a027a290aae4f6901994adc4b61a46d10ba685ec5964d80ecd4b1637ac6ceff14bbbe64f75c3b956705a9d80bb47764e6a3f23a520473e17237a0ccc2dc0d15b3524ead7c9c033518bec435a11d1c9eb1305454009ec8fa5f483fcbd1573195e944134fd0454969ec2fe43c0c04cbfdac2550d74954711958c91a7c0941ad5911519053d2a2222a548e70c139aa5f30928c7470f71982abce13365171ac4661ed145f36d97810b1a65419a4858c8abab4fd72da46f8f356da183d755cdd5318a50965170b670debc28c3a2bdc196cc2a833777d863735958e54f481f7099982d5afd8707de55a043a8d2b823d24c39884f433976008de2035e2b4572065584ce1fbe37eb46cb13ab13308a603de7c4e38febd918cb61e4f94fd9411ea1335d096d4b1d2781edd72a24103f7848aec39e2ed00f2a592e86fc240265e848740f8d8e56252c3db555929b0ab3b436758d6b36b3cf3499d21a02b74545767cd5c7b584dc92dd388ad424176369388c521a827a2e006fea1c3ca95c28388a155e04e0ae36016985a151eed010bbaa889cc1e3c864eae74619595e788cc816b67db9c07d37ba185d9166b84f66ef5422fb5f416b84aa5ebb784e3a8414e55e25f439e494e8694b622571dc18c2c4d0ed55e42a7836c8412c2e4c0e172dbb268266aabc91f04e62c846ca389aff832531cd6e0fac212e300c635cfe3c9442afc317b8bb0a254ff21bfcaa1b298f1fbf8555fb4b43ebd6954a5bb632d0bcf0b0f152eb0ef4ece975c38f970ccc7b1a7848f7153d024bf005b066c43ab4520b5245f8b9a984f86321f611ab939145c8d70748462e60bc0a06acd9fed1cd8d8615438a2d74ada5588c5b766ebf88707535eb2394d7d9e3f27b4961375c62da2783d2ae0a0c3dd1c707dc4b21551ac9eb52229733dfa49213b583b1aa887e91c72a5eff2b5cbd2721f58e6d662ca60c0bbc6db5c6cacfe703ad936c211c3912971190ee979650e0f5804bdb20343672decca3d4902cc4f6ed4413c0650462c047d0cd25e328c947af34f4e30d443c0c716845c006908119d6e3dc5f2db2207f069239c1a852121d213b4d6d16f6bfe0394e6c9dc907767b277ddcb15d4508a7ab2a27cb476aa69eaba8e5ba98a412d8d863de55618b1a7227bf7835e0239ec86539a32a1435d0c44f6a91ef2e79ec0a1f7f165009bbf68cc09884f329146bad7466a4784526e0f51c29aa59a1af0354c13668b4d828566dab47e8957e01a7099ae3c4343807de8178277f114025cdbce793cfa277ba3d9fa15b5066cb8174a9b5cf0ba3156e0a35bac988c596fcbe186ed3de05d3d768421750d4232a73dc8670705353e7f8ac24507a4f83abc95cdb4883c5dc037cad478c452d131ea536d0a81ea0b5b84a94105ac8413f56f093a53d9309cf96bbe1ddec0b81aa368cc4cde6e0e082a9624b1ff44495a7edd9ee448c6819d38ff7e174b0bb51e482bc03f415818191a44c43c6016914aa1bb852206c480b1e8a79fbf2e596596814f91e41a8c730f1edaa0c3a2bf5badb2a6d0d6881e4b862000916ccef5d943f0d4158407e28ef85d1cc74a606e7e811337249b11a76b4e6b875445be0a99498b814b86b58684f4de42ffbec3299c232b6143c8168ec44b7f74f03ffecc8fc724e8fb3cc5b91c6ef1664078f1fd3e6e9a6dbe06ef78c9532760ac189343e8c14b199df0b6e82d23ad8a9f4c667ad804a1750b598cda912b67e9ab02f7229901d4c35a4c9b52e77bd413f6901fa125c2e00533680dfe66593cd91a9e95fa430a68f44d8846a01a923505fe680e18ddf3b785cc29468de495b8b99710d74f1e53858ef9caefc61c04088c63d989fcee096b4c840bce8e346e78d4441a627c988fdfa7bbdf89e8654ed7471e01d472a45629ab33a7c4013670d049858a6c8b1e7fd5fb3a2d14c755a9552ad83694bb5b0f5b4a2bcb5f7e6a7039c144b2cdecc482fd03b00563ec5825747efc0fc06a1c509d0f90df052a2377635107e6a3df3e3e01bc953fd3aece849c7a4dbd21bc9e87006b1585a22030083dc62f2b24796939f8c0c5cc26d4ff7d71b951a4cad985b784cc16f45f268dd7902796236ed7f7eb6d591685254062404118b0382814b987d08dbd793bad61b9fc6e8c6dc51a129a5b8368469d0da7ce788a0643727717d17fa3be2084a8b56de6894fd4b7b2b927d1ec987a819e0c4496afd24bc9ec49d0fa8f3d3dd833f772fd27527157ab7a3909661f81ea05f55e7db4aa5724803dd397ba2c3ecd2ac40ad8eb35e59c937812e9128a639e2ab4ce889b2e3424041dca8e34f18f434a207201f686b4c660745a3cf794bffcd9b31f274f0f3424c7cb3f4e96f86ed088a592b2dcdc5790723fd8b3198739076389b47c060bea2cdce8066880c95c73addcf5ca3eadbc65c9d678ab48449185fe89833b222f374f3b0e241e11150e4365a5f0791b5df2d7821279d6bad5d5c03ed6cd0c399f0195627e24ea15f597cc17e224e43564c0c19de52a4b0820f173be533b4609f184a88c37883e78dcac1d33bfee57dc7183c470d2a600dca2bc830ac9475e9f401cf480620194de71d0bd0986640521fac4785e76170f7fa3523589d79e1a28e33bb0cf1d3aa5bf0d6cf364f051759d7366f995925d7d63e4e9018998539e2fe2ec33808d6bb97f19d1368011fdd3de7ba0fcd0c8d5e569a9d6d37b291378f453ccd986594d1e5ec915754f95769a3c8c32adca40821538cad0eb9f713d9d55450669a009083f6aa454928bc01ef6269d6f6b2b3e3bc3102ea8fc8bde48be067a0c78307e32da311681da1aa34e9c9773e42fa11c4ee901b944876016823552c8d89aeb4d7d339f116a8c22f9a7419dbea813de20f07d517c75e68fb625fec77d1cabb1c9b42809335736e3c229d8e52eb03b64ed3ed0eabab8887b4c40783b8eead306296510c30c7a93543337857ff85c5fa920caf88faa30b621b4af919771952d834b1068290d2bdf58983d8edd1da79962bf3ffdd4a68f1a49cecc928e84d6050b083cbeda878ce48a6d1b775f427bc3ec0b14aa73782def8124f2f39bb1b468c304d212a5a28603548d1d922026981d5c14ce24e276a77112dad6384005d8ded87cfd88a245aedb5e89c1ad45c4088ce71b1360c60ab78f133541fc86d18eca8773350cdeebf62eaf39c5e83624e8786ffe667972230a048fb36c59218199b0260ca4eb934246e0b0f514d41b6c063ec4a780b8438c4e4a3df55403a6ac0c57dcf63fdd471c4b4d247cf451a874daec7ad30585200346031e9ef7cb3c914e631b4ab82818e254ac16723308e32298b33c2098fe994b9bcf95dfc34f76e52c9c09c8b7e38130477e8d275528e6c4d08763e312059f24762c47468ea46301b1280c21d6297444ed1173135fe47363ec90fae82c771f66ba11769cb3c88a49d5b7e5340d986c7a93ce0e8ae0f18a107a39d1fc45b109564ef403b3740e1bc4b511b2507be06961fc0d7cfe0c217c732d7e70929741209f0ccd517187ab400f7d7b2d4b890e06b3cd355e4cfc2a0911e31053ccc94269fe67df40bf7899fed238748d23a239b75ac2a8ffbeb6f6f6b4099b4ef2472840768ff4d9ecf2d917b86d9f98f8f8c720e0d3f0f11bbc3a4f237453a30bef5223cadbc42a9a5ffc01a580194f19c1bbd5fd32e2959d86681ca1e191770c639a07e1689fb2a56e661c033eb818d6bb822cea50c1bfaaa9adca1745a6e86fa2432601508beacd6367e555acd7c4cfd4fd419f1e8ec1d226b167a5a0d1a6626f14c1fdcf23a8f696ac77135e29ec1da5b8615a82ffefc973169bc6574b23b08c820b024fa168aead2ad6f1076a09fd26965fd36e463cc0fec15f8ddcaf57209d71038b6aac5c150bbab2263f25e07d44760d16182b15ce35183b8ea5e91eeba8b475c1e1687bc5a613695472b124510c0c732f4f9bf3045e888bf80f31348905d28f330b4ef89e5e8067c639588e1de83d39c908492a81d927d6dc42776f718b843b487e9b47450a84e2450dfeeaa90c2d4fe496e18bc784a72bc0b78cf39f0d2e37e0f3d2931716d07d7cbd7c71ca5f645a5c3f6099d385705cca448551136eddcedf0ded1c8629d24ad02b367fc030961192dc7d5c6c021f437e0e16f06bd8edc95178e3586dc90af874feaa42278397989f7e083892376e56340ca256539350c60330ef057aedc179004c015fb53e29230d86bf9ab29f3895f41791fd328524d0db0539821ee2b6ad13efd6138928ad4682ce37cda31fcdd81b9959436a61ac456c315df70d151d9810c32a1497677e0da6a1e02c7eab1c12921a205fc7b06ce0771f47338ab3c6d705079812a9e56e76692d2c57fc1d0e84137300d00643a67cdea5c460c830c94a4c26ca94db0e50afcb8c64b227401cfecadb877646575e878037dacac0273024a077a11706c816916282bb87d498a4c1296ae29d5c60ea2107f7e4eba26f92472ff60621b341eb351c78330e91b08f6c258acdc5957f92037238e4581cd4f310d2c2bf0bb4acf425ef13c0b2a8779903fca696399fc50b621135833642d3d5557a5ef1d8380bb165bee7917b7afbaf0158b46cde3c08cc18c2ab699d608856bafbb1c1ac8595158b4607e611d80786ff196092a13cecffae7d3883c6ae88a9af6100ed0d30a24c2693e6373c7c2ac36fb675addae62283afa360f6dc07a52656243b6c350d5477f6cbe3505d681ee58b9de43f9f23340a99b015a060315dfa47c4550d975e20813f3c29173e8f6afdb9406e14a8b157fabb6f2fff2592cfa0774e582ddc60f897c9a90bd716c60c34c08bb1e5ce0485a0b7a8f1828574c5a9b56d7dc13fb86a4b67dac378022df0fbfb67379a59714eb7b2bf0e3b4021e72dc641241fc4d09b3540cdac762c507faaa2548e4aebd173c1ab2f3ec64541d20970781f20ba0d81ec115e785882b14ed03b49512e9fd80433971cebc78e4d3cc495ca8b96017880964625260686cbdbdaef43f2541121138ade4ae2d528415f2e234e8d9a5dae52356e6d4ca826c5dff2f094f4e55740c5cae3a4c0f7b77e68608c97f8a8a52933aa43140ba5ba0c8350e3f6e9d73a7ebc559437b6e7c2ae66f574cb79bcd1ce515d19c36bd3894c44582807eb0d991cab6f65e9c8a9aa3420b3602a10c965c09ea086b42d23990e5d47c014aa0ad1d2dc70727f70e4e043511505c77182b6c4ada3750b719939622af75faa21ae0d582987c9dd86a4861e807fa77dc49a9e9100b414319d3e02452e0ed22bcd6ba087cd1d52fbba097a3617371ecd7d1baf839ee7f29cf5ef54a179087423523b9bc7e6a909f230b9dad3f9a3c2e4a55e7dfa17c0f39e94c0e966b6b2a40132c43018871431467ebc50195e0f3552569f1254882e127fb348b03a780783924258d7184133050a123cc03208cfc0c78f20b9f024679e7179ebcd3b87eabdbdc84e07fbf6cdf3d0e44ee75b567a793226bd7f1a93b804784186b0ff705ec79cecfee9a590da51fc003f48378f76d8acf0c2e59958a619b8dc1b08b0a419fb97f9c1fe8fa0a35f9fb9e68a19ecfbf5517fefce3727e5a532f307a1e94d5f6aaa02605789f2aa49f519fd6a0e3061449a5900e532dc563c98aa1821d0d3a1d9e99fb1adb21903ac05962a980e48444e02804a2920fbda0bcc72ac59b32f77eb08f43599fcea80fc2696fc3b0167812645396c75d69faa8a67e8d27a61518019025c882413d399945d954c580d9f4c172538251275542d569cc4c94ce995549680a3753329f6b96920ede4518ca46d1c5c0ccf6e5841d99a7ed2ec19d3cf6a5d31e6c31073c946c3d454896daf86edbc46fb15288de63a451721bd1823eaf47e1fab7970364afd76e984a92a4e432d3d0e355e4f63d9d50dad94245a3feb68c3daddb4a7216d932c9a45510bc150d10f604c4250933edb3d8979ffcf5bbe2090aa4ca740aaa70da97000d7e0abd22e4767d6c4d790d3a3fb1c49565e57dd9b19752a3f68add008532b216c2458712b1daef7174e257b44f83e84e0943644e870af8cbbe8c554722aeb95bc18ac9580ab491a02697c09e1e200091329986bb214768a9e088a7ebbac9e68434c6c4a9104cf4fdfe0d9c6fb009471494b1064c5482930b91f2e65b58cd94f5db34690c022933d970f5f269ffab895d1c5aeecc6c004983b4966e2788125b0e9f128b8c1356b6013515f3c8242f298a6fc3a929a32102056b1855952b33f1832b564423f2eeb4d28533f5b19a51c9679349f069e8e1c40dfd5109aa5f60dd6030d3d6847f301197b32de977943e0d2f49e4ddd3357c85070a5ba293989817b1c04ea90ced76801a33004dd048c4383dd29a21d0465640d8c1303cfd645cfd91393d418b6cff9e59926a01da81cf50cd63d6037e9f319fde505bb3e185de6163fb636ee3c04975aa058d89c41b6218102ff759865ab90c34025ee14b029fca5f5081c7bd957b1f439f1099dc9daf4a5b83859b109ed0c142af16099246ca1e99850b53c92154935ccdce7c48934d427aaa31c561475a603764b257a38d27761a57ecf42be82038202f71102f3220db9f7be7b79a9e19e9390ae0328ccacd7fb07a33687cd137b552c9f6c211d31f681fe5032916e525d1fa82d75c5b8589b9638935b53cc1ec6923e2291f94b096b6f82b36039018b7f4f1201a9c5a94462d334a7c6aadc4ef5887f5d42d6ac240382381406ee239f7332c121e5226e8af3a441f8784cca317acfe2f9f5e11bb859618d2036bae37db854a7cedd70c8db08bb55a8cef683a47a1d541f6862cda0331e5f6d100fea7a4d34bebc2a8f0a1b5d322ed5fe4e962043cc547402127ac4b18289d4277fa3e78c8868c3b576ac4df65366594a106ed28fa3e044037d6962823e291944118f9f88ce816c5298ca4d072d9bfe05ab48478f174d86ab4266a80d6e3946aa7dc99433e909e66f39284bfccb4a3eaabefac39656d944578515388155c96db40fb2cc0e8a326a362f1808a4386224b26cf6ff4ff339fd126c10624226183af50ea892cd5150e822733d355f5286b9a3c610354466a2bc5201ba40f557a4751b54c4b1949fcebd5ca559383cbd9b65d5f1b1a3971361a509a01eac072f43b78cfb056e24caeafef5ff3a996c22cab4a6697148fa61965ced77c21ac502a966f8b4a2148375b217a620be5c9af3d0cf3f3a9bdd3086a4bb6ee15e9f4bdd955ca52cc7851b55cf05c0ce984bc826f7d8e8ace2d0b8aa52a60b1be625d14a83861b038903912ceea420b439b2a2deb8fc47f3cb17466290a60307f33e76600eb9424b8f450fd993c3844678394c558ca3969385f7a6a79369799a130841a1fbb5d37a7da791c3be3cd009fd1bc0e017f6c1d2da922d6d98602398c00217ce2e898d3ed17887755670d754ffd045cc0364c0119f9b4d4755cbf5cce65055ad4c2e9b98ba82f2b465c0bdb814ef082fd072e927b33520b50253cdac9401299755c21612a7b06fcab86c72eb6cb1364bc055a98f8995208f400bb754ec8acef2dce45153367ca779f754929eba8805a95b3a50184ad888f5fb84eae981e5e159512fc35e626249f305d46c59e2062fb40ae16aa7793c025f24461bae346565fc937c2e8aa8877746ced3f141f3cdac6aaf416181eba55b1b1692d8bf5446814c82ad9d6bc896453eae800e38312355012ef4f2c9d559631bfc32cfd688c33f6ec59b004100bbe08ce9af6419ce26633fc45d2049fdbd9705788c5e2bfb8d1b33a7911d11cadd530d397eeab5a9d4fd8848c01e2d3b5a27b5c48da17084cab24f5ee418cf08cbbb2bddd1eb5bb718916aa08c1a5aed290e8dd016ffdbaeb3b528001e919a5392b6e27eb8afcbe07434d55f2d549b5e0fac7c765bd474ea3662855ea95a0265009e204d68289f2b8cec26b83a3d302a4433b39248df4a13343682fcab510f9192dcc379b2b834f751734e09c0490a99262f95313d40e02f525660ea07f4886fc598541f20ab45be349aaf00b8810b85c3dbee0cdcfc9b5de962ff026a4da3b73ef1bb66eeedb5b84ab1c0558b5c7e5825d3a36ca7841795c380230bfb5de40e07413656cac0b65783aa59d27b303691124f8a13e09212ce4ed44ecd4680def15f82fd6d64b246016d119e04aad505177731777ecea486b8eaf0c0c9a846629c3447766a2ba14b0b48764a43756d85e9b3b7498c46909e1b11caba2e23f2d9f7a6a32901758bffe3d842298e354e5820f78c199e6eaa4d8e170287f8424332b40c17124dde6d658ab82c918151bead66fb2066fbc3ceae4153eb33ab87db14fdc8cdf3dacdcf766ca25b5ad72bd1b890e490bc2a9cf645f107bf631185d4d5ae5c4c2693aa94877917ad610e648a926a5eb547e2fe211ad238bd0224365f45061b25a063be6e716ab644b659415060d093809f6d0204edf01ad78a653910a45a643561fb9f8c066c937004dea733bce8e95f6b5daafcb75e561c5b9ecc575ace498267b63d6b1b6d081bf0c3973bc410f38849b3a3ea4cb427b10ed6df6f43ff1bdcaf9202d1f006dcb5f023dc49c74f22a4702a95709a1246f88240aa5771c94261fa1f6cd121cfae5001cb45ff80bcc0744fd269c04772e89c1b6b0912978df86c94d99ae0c1163318ebf3a6f834c0dc53ad59e6a7ec0f8cef62393dfeea0294326671b60004597460cbe2c096590c0e7122896722a06fbc9c2c4839a7ea4e0e5d2995b58f821556392a2b08f3c739a5c2e905808b4c0a6222f92c7a220a5eb26cbb74a625f8fca6d72c7d68efa5a9801b5b6b29d1648bdeaf66a8b6586bb045fd8f867ef29ee810c31a6c233c0dc2c875ebdfbfc073ef62b02bfff2aea29285b8cfee3ad45afac12915687e8fb9b03bdfd1898bf8e255010680e7eef5224f408247863a65bc03e1e450d1a19d2c50c1659f5690fa4c2aa4d1cc08c1415783bfb54ecf47963ce7a5796f63b4e13134298fab6aa676e48aedcb0911920c911622b05a868f134dd7b4a64b7cbac0b7a4d5619c10d7df7a3aa0e02ae49cd0af52a0faa0b7c39936a39a80b4ce1987be755eb0125900d11d96002ccb15aec4defd2052aca8d4bb796766fc122325c687566606068117cf4bb989a527050af711beb0125376dc28f7fc0133bf47d59261ea11d6dc37682b1724703c7d54613d47c3603ccb0115685b31eb849dd625178f5c58dc0a9e0dc490cd38ced7c7841ceb6db2ffec514d3a4a0b223da294f5bf91531f0551149daae7550b36bc0238796ddc3b07559412db95418f9fc2bcab66d35803cc2a6d5eefbf10dc11589cc9bf0eb87e6b211f8fb38e4a45c36be6f40b207af4aebf503cb41119bbc12fdf6c9ebab791cd9510a46d6afc43636c88d0d421a640a56c38ce610eaacbec5088640429ab2c3c1fd62f80f8fab4aa8394e434300aad3222b449d446ee46c78fc2dc95a7305283de48227154003716cef9aedae223bd3b0cd9075598c2223167ec020d1c1af6367cae9610a1b8b10e43fdf0e69cab19864e6bc6ce4953f8b258c50203e1c32e842da239e5651b815bf2444e05e2bc1416c340bcb48c4cc09e5a86d57b276329d6dd505840839c4a640b326a39f1b796e4bbbfeadc25568f93021100229a0c2e4aea7e58ed025a08891bd3347b7d874cc9d448be33c7c2c257e215dfd6438e62509f1001639b8588b5f22ac6816942428a0722493ad4695e4e0e7a8755ff3a27d1cb85cb43c153a314c4161269922734875a88ebef516edde0d0f70e868c5a5598f23bf29fa80183c204bd184276a1e39ea92178049b94f188a18d2b29686eacdfb08f83bfa7a9c48d1e0402305dea31ccd34bc8f680694db68709ec85e032472ff3603ef076c54b5b029e9f62eac502d4aadb1548ba9a25026a414915c87b9231f671f1562ec67e0d76234348cf11d3f756e01abeafd303edf79fa5eb0d513ddc69b5685d61e4427e86f08633294ce250fbb88a843dd0ff3b2e4692ec6bcd971cbf55eaf1eea40614e1469be5c5b7f6ef3a0718952543b1a8c8f6cda78b11d719a95977078d8eba4e808090d97d8441d392c30b0c217a483e22751698109325abf8ca90597a61941b6de620f0e97a58dd14625590f767a61084a787fdce763d2abe6fedf8c179a66e808ab6def6ce20161b6b1443c0bac3904661945906103ad682befd0b59c9b692c0328c0aa3a7c68e1cb2783f7dd8cda89f9c91059661a25d611e0ca70ed9deb0a3e6e8b1d7a3e650736e7701db2a982f30b6c67a0e5f38863a176f6b310a6468a06f9d0bd776da134cdd68a6e59863a9cc0216fb38f05e823c3012b8314f88326fca9020df35903b7029433e44c16d4c6a7a3db6b294b77525d13e3e5d18abed547315774691708598265828eb93f93c4fa276a54609a566a2b7acc6081cea4052c53e92784a01c34717a09877f858e5c056311241bc5dd2a554286608c743a45f7967c04795bd49fd7df4cb9fe9ee5c6959c77c0e77148e8c5bfa127ce882c5824730a4937864ac2e762550572312819caad631e2d5ebc640fffd3f04961f12e2eec49bf7b425eabebb102d28f43b0f8693ba63ff07e00dc0938262afbeb439c70de6f540fe13308770993cd46c8d14ff64e60c2d05f42d7101c8890e791d4cf4922b25bb6823a5f2d0490066664dac9f2cc11b7e2585b331a343d923e9052870eb1b1ccbc358d3adbe06b27edad3f30c530973d8c6cfe39009dda84ebdcc978c8718e93d6950e9baa24ba012759c79ca8388495654e0972374053557af47e01875fd03899c0683c791a44b712ed1d31543dc4e488df0dac5c1855d51cc9006284436ef75f0452e810ad2130ddf455425f9abcfe526f673ddc51ffa1e6be786505e6d8df32cf9d03b56cf701b7542ed74838643569e85290c0072905c6a6f679ea2dde8e4cee37b6532263b78e81c750f0d7cb8c440bd7da55286a50134f82c04029905cde45e63b28cd721436276ea682bf00da6b277f9d2e570784424323ba8d47357979c18787168af5382d57aa7199f2609531398618818bbd6c23ebe235622a4392f3bbe0166b613550fdd360ed896453c158a866cc2c43150669abd97f29c4f701c0b8dde4ed38958be45cc48dd1f2a8cb65caf0b847334c30c8f6578f7cd7c763cb523ad5973397af93c5f1236b8a691135b4e887fd3e63f644926ca4e28d3287b60980644269f461f0ca34d15734f94cf101a7da1424e16d0fd58e5131f0bf0c856cb1768d6f97b15d337b2c59b72e2e9ea6e40f826d393f219bd594110d579a57c863aced7854d41e4d16c917d06c7a3a935ce90eab0aafd227d29728a2bd12e259998190ee2a1c205eb08d851ffb3b828ea1f676c684264283e2e1e1a1298a57130ac23f1cbfd7b285faa1d7d0dc72c1386e28a57782fe01f4501a34b8683921da5518b0eefa2e6857ac49dd9027cebd1a4ead7b71a75697e938d5d26b0eac52d71e652dcdf2e26640274f0cd2d5379b85527fb34748ed42402038ba4d42d94eed8fcacccd0cff7cd8cbc245f7801be090c382a47fe9742a9402f068bc947b195a29c13f78181c1f469ffe6811ae70c017476326572868ae9383799e060c9f7faffe2d97e6222da70dd5ffa5c9204f7655da0628b90e759878b95e8c3b79684045b9a9d03c45338a4536531c6fa2a42dddb1070825364c4985d3f76f294a13f178078511a7851911611bf66ea1e25b1f358432e9556125b73ec7eb0019b2b058f1f091c296a3480f0c0c262d1adbde0d5764f0cf9d3919fb501bfb5dbed10ef086e458134afd0ab081fb4cb08dc07a0b48cf1d5e2bd2587eb22cabeb43d6e0f462b5e20bdc897bfd3fe64b4b0f5000d6fe8cac6ea77f7c5004aa67ff0faeec93773bf6910bd57da954ff7fd9eb1796910f7225404d8902525d293291336e84f8089e61c41bf9778710fd5cca4574c9824f0cdd1fff2a0fb30097cd7cb9294ef51e020e27231cbb4ed624606605d53e34bba52943f3ad4b8ee7529c922bae9260c1d1924440177a82e16bd1102dd61bb322438277ff332044698124746c02305db38d197b3e60f6ae51f83cf4e4c84ed26323bae5edfa13fe18e6e4947aa4f0cf206034e369122cd776081fa99b8e225176268fe14af9fa0090627c227cb9f042f2006d7aef67b0cdab02028417cf3dff5e511cd416352d331b878e7d6f1188446b37dad4591401adca8d8c6e36122e8af3a4e37e204c4d33665fb50e4ea470177e9929c175d1368bf7ba14a18bcff2065a1aaed7fad81671498b161bf801113d6d7f9bd2d5cfbef5327f49d70b59011be8b289522f46c6401a98f3a3a2516e90a29629b8e3967257c21131f4b10026461a631f52a0955cf07e3672a33c44659b0d00d9046ebb00d0d49b1f76f97a904f8a9105cf0a23a9115dbfd44a31440aad003814d1cb2f06a3a1ccefa4608c1e5bf4fb30d6ac40e58c96bbb9acd1e3dc877b0e771f09016e11ad563ecc8392e14aaf44869592f45215edd69d418bcb966daa8dbbfb8e92d466ffa19b42bbcae9edbbfcb07ee1e021d4dbe0a67fb3aaae8f9b491008c41feca68ce6216e9cfed952511d4506f3d285a20458119f872009dd2ae9ec7e0714cbc80598f36c2fe6d38323331aea7b4f4a553e55870d4bd385ff5f316745d302d8445ac4f006e52b9b4ddbc7dcba035e6b75f34c40d62daa645f2b57a59acbfbe5f57c7ada4655a57b416dd502d52ff5d600214d90123116f9711a01ea246e2414739325db16720eca076fba314d0e40418065e63ba717bb43de59a9e31024b9ff8b716f0960805066e1e08e335aa4e4e930f1a914085e9da410083c2f7ea64cd3e40265e8bc84a0f7a7f1c6ab1b30d57fd14b7409c0a64d5eaf3e0d8209aa53d73e81e8359f9cd2296b3825e052f00de001f16285f789c93cf42bf0f217090fec1b19454f3f9ac0f7eb4e3bf0ee3cb4545a0304c01786c4df2f4ddfa48679b70816c5759d75648eb1859ecc8980709d73cf3df3dbfe0de5c7f7f955806e414a81b77dc1afd29781c7b1c36be65919437ea04081cf002ece314aafee2a43b1f744d5bcfe22e6b1af6327d55d662482035b7f407546251fc256f4861cfd042c3222c47f18ed89a2a702b1d78126a62733c9cdc3bf6a59980af55acf513fa2432c0a641110de75a6bbc02f04b8081514cb2b3ac29261fa52839c5ee420ff06f5680a9fef87521250fd3c7cdfcfdbb3d1d2a0d9473f7b5fddf4ff223f121ec0bbafbc61f20f3798f56c8e620d40c1dc6f65afeb05200f8a4ff4c06df443a335e7ac76d4f9342cead3fbc2703c56a219e995fe294bce768f29439ee77822122efb954cd18f132100b08dd26d8379cc850266c50196f60c3c4a5ffcdc0f0baadf2778d1a805025764b818210d81373aac89cf2fc298b51e293bcfb6e618a89b09e417eebf5b1d9fce253854da672f1d2caa4fb986b96303aefa431f59e2fbd2a3219204703078a3bbe06758121c3e4bb80a788648614b106688578cdd5d49485408c1d40f5101b386ff7e9aa179e0cf2f48e2936060792b637cda393d10705af888831cf870cdb39369120b16063ee583cbda9fa93545d53b8d7d3279f89753035d58b1a292e30dd57100822a07b2286e3a9b78c03e23c1c7fc3cfe092d0bd68d500cda973be8d8f2540f6143263389e012e9357f46e7d03708586072e53807f6c65ddc55c890f148aa2d68dfe19693be26f7d66d38a62406e3a1b9a7d02298e0d2dd25d6a1972bab9bbedce51e420c83ada9fd1ebbe612a9b6baa6990ffd5e5f5ca151046e1616e8e225633b849aae423de92a7ce2d27859ad0dc3abec5f54df247b84e47248462bdb34d86dd3c7709ac99f3655d4a41ec5ed523b0fb05e3fcc2ae67d92290a8d4930c566c2c7e3d7430566e6b1c20f185edef9f74b0318624b66cbb578644bc613099b94c17f03feca77a897e6e5a0497ff9d2fac71c94a77e6164a9caabb224fc364278e25e88b0ee0f0973d0e38623b82476b6edc7429f86284f7342973ea19b05a6a6682a374fb60b9e0fb2e2ca52b877517a7864243cbefbdb5f355703e6ab6dd95ca860f1bef2420186c0171d32967643cc3ab53a0e8eb8cca6e3dd7d822c3002b8a8f1e9bbb8d342e8de55fc90295242e8bc86cf0f4511fc94d531f4d6670389ff5a1597fe854c8d4b1fa6a92ca0a0a1c6a9aa8df3f110f52c90a0a70e902ba1554aa33002784346204d39b0c97377e16c6ca12c7ad4cd5ac98f5f8eb98c5456d9cb4ebe9d7ba988bff723ca44575a72dfe41ddee75487c47a2b86720aa1f04d9a2d428d86c65dfb78771f4ffbfcb0bdbd6d25e30b2ff9613228944f42bd25e1895fcfc00bad22aef9485357c4e0dac0771a4eddaebe49b064bb4c85520c42387d0e12e14a055a041845f8f07f6ce55f51cf1bb6633a99b1710a1a8338ef5bbac50ac4712cbe315b1e5537b4a13137e815fde034e8e606c81168d07929838e53604d11581401afd428c1533d7461e6b125db600bbdecc14aa48a28bf503907b92b0015a742ac19f9f18a346f2e3d223e2b743118ea8693ce599a9e610317fb22caa3e68919f738f5f7b9bdf396bcb73dfa8cf06a466d8637eb8e1fdb0455e977a14a70a1ec097b265fb0e72af921c80c1ce09ee22afef4fa19350a3c0c80c18120b6c1ab1e0a07b0f11498317d033a36d45dd5628e3b096e0ed2be14cd3e1ed231cc6eb02720e826de3848047819eb3b1ac53380d4af8a545752b259cf9602c83535111d72fee41909f529c9a64c50d2b33bcd7485989133842e085ee5998a45987d39ea32d46c94b0d704fd6a9c0f6bc54a272095cabace9fde1a4591d2bc24dafcd82cb42ac76e1328b069202cc50c5c3e41ff69cf80c527f138d5a82a0439d9a4df09a649e5f70cd98fcedfb4042db9acbf7c0841620679f1cc18672db1d0c60af0ed4d7f5486dfba047d03aa9087c6c6521437eed83e9547e8df197a908fcff54048f04cb0705c7357c4351ddfc065dca10cf5592286f0b3b9cc7ae4e2c72c96e3fd3f2c5e015ff27985664094f307ce1aba234250253cf4e492f885de314fce400f3500a703dc6ef39a8bf727fa660f09b67b7021cda699dbc742382107093dfb7a802e08ccf7709d0a4b3224d13f3ac7ca1b5cfe7ae60ddda49342c6e0f76887c23ac900d22d4f98b526e2169eac158f3fc6def2913ac783a96003cfe214ae91bb26f2dafa49806820395fd079c639eb963e19648ae21301ef53b4370b8a1d659609147063672e5de90c74b96cf4e7bc69b28cf87a70d45b90d588db3918f607a56c4467646a961d413189556a5ca6fd5a3f98ca3a2eac7cc5f461962faa59b9ec284820cabbfa1246708e7ce5f6a3fdd6e84bb4c3ddbe30e84255c07cbc96d7dede8593c6e218a898a3d8def450c832e128214d5c3f77c81f49385311fd93f9f0a9f48140f7477fa7a5b392a960622b60dd0d43ba44da0aa46ee65b7dc38d1fc57a0f2c40356201663e5506cd22b95c0d0f3b4838486fb56ffd5e8b98ed943ffb888970f82a8a11c986382ec6d9ef786f0e9405250790a80d7c6fb3c1c0080cc160b063aa3b0d6175078ffe9f1f6db110c398553742f021758c808b695b7252161df0f3423c42845c6082f38ca516d3cf9328c0f7ac87ad7e6e635fc14d1cc148709e3b9aeb4ef058bb6001839552e1d808ff4cfc6a4c9d277a2fe210766e0daae104cce176afe7b9356d9d1b25b8ee5835d4843b8e818b1766e5f4378264d30b68f9eb0edbf3c970f1a48cf3c49c1fbfd05ce3d21c1e78ae5b26fc554ac35648a536971925df9c904e884640bfa8a4ba22328e27c3ad88a0c7b84dba751921ea24802c82b0d680d0a7375ef879d38ef8143561f74a2d99a4a5a7a10ed1bd3263fa36113b53edbb656b44424ec612b882a96fe299ed3417a05a378268b45f61fb23c3438ec9e53c7a6a445946a0a9ee59f7b890d5705f2fbba96549683cd4b2d131d07ba68302613d78c4e25f46d5f3cab2173e793e3c84b2cbbb7cf8894a877247b53fdc0636dfaeb65ff45ccda409698ba053fa4168ec4aaa42a500e7da71fdce6fb72601ad1134408faaba2c2b350f898ce15477969f50375b626a1215d01d2a1b5c6aafb66eb3917d7a6e59fe659af54006b524662605568785c41f93ff8fa5cbc03180e8c565f301fd9f341710eb21d7a05c3707dd0c6f233c96d017a282942b2807c4d5cdd6de412697595210a975925c5fb0eeee25a4fead6e699b758418abf03a68926467c8849c608d4ea42834400fe49fdacdc00e8528aae35830f473dd407fb8ee027573323dcefe6b159150fbd4a40e8f389505df6e99e02758a2be77b5365313a16178150a9f827b94032f49f1609bd9d4e6c353f8dd404b4afd110fb342355054cbebfe4f2922b23bc33f09b951fd1fc44aecd71062ab84bcc17a2354c40cca7f352ed03d46a9657f6212ab10973e20eb84a29872ff7e1f85f2006df49fb067a62a24b552d2c100eb8d714d64363c4fe5f6239d4f89c6c5b2a501c4b1bbfc966f4e403000f4d8f1ff9b7985978e70d31705bd40486f894ab60feee7bd376a114f6aff62d890f69aacb8c2302065bea3fa83154f885ad0d3bc2ba19587d8b70a40673190213b8eb19e73847ab5b2bf14add0b690e7fe06819ee4e94a42cd39dfa2940eca9248bae59e80b4223daaf39fc50a264cc5321bff255a828f88adbc5114bb40801b4b8dc39a9845950f28b997bf425b945db97da046d592c1e798c3c96fa3c4c501d37dfb9ab6e83f591434170c7bdce3e82efce40eca4940558f2c321f91b32a5d1fb26ad05a78a53587d9f81ebcef73ede2e1e2c85add72648e202897b70df94bd756a2a694c684d16ac9804948bfba14e4ba069bc3984c25f62a6fe8b43fe0108a3ab2c602f03ee189fde625c9fc2775087f78ba12f941d2ede78ccab32c107b087fcda89f6e6b2ee439ca7f25a5870f2f6ac776964997287e36fd127209419a9eea964f3c904cb80ee1b4bd58cbcb9b460a23117762a7f063101349f7d53ee73f256d2334a67b172138b25b52484056c7eb8c304aa1890ebf4cec77c72e87fb41f95d387a8b4102053fd0263222fb11e3ffecaddc464069f06995e686f2875044002ce544b046c039d4225561f0deec9598761f878d214dcf6d683be664304b0a741a4bb1e73bddd6111b01f8011f81440185ba27c8586607c92095063e48d9a73e82d585c75c739826530c49e93725fac41142a2efe387f0e2faaa97ba21920357270475f438b4e4352949d6906490459e59d016e845c0a2f84f3318d706a85912785e10138e6447fa9ba466541e7bd00cdf484b31fd3cabe8b107624ac2bb5f768e86af36451e660de731a681d151c2ac44af69e244d30b58f5ff4b34620b852760b4a37a34a39bf06e87f2d2423ee945999618f33e2bcc9286705a855ade411ad8d32224508bba60279987f5fddc11d5a91dbea1dc686e7119f91d06ef8fc27b14b86a447f65f2d3fa447c3280b7595d5eae4c0a9ccf6f1c59a03ff3d31b88b3e09135ebfc91b834efea43c4b43b1fba52e96217d333bd750d09df6ff0cb7efc0538cbc6f3fc4301370030170291497790e984900db4f2c95a58f7e5ae861f3845cc93e2138ba8147537351696a904c6d961d6c7c9e94399d2e0e2e799ea92ae15d67c3dc87c0d60615dc3e3ef6178659247678e71908ac051690ec001e6ff6ebb0d426450137ec72ac6324f7f8c4efffd6084bfa1b64e131b0aab80ce9f5e3e48e80278be486065d507d3781098adf0892a3f5015818a41e8c71c6043000e033a598347ac6baf887dc9a5394539c18917d7fe0139a8571f8c9bb4a72221194cc5b1a1386ddc127ed8fbd4738534bcac0de30f873f69c980b329735080f884ef5b70a73a80c915659b9d3b4e933b5a36745cf0958f6791cc13aee4548b9c66234c823925f770621a70e098e19f41c969f05c541676e31df234b463829a204c3c41f52603d30bbcd3bcf4616306360bcd14e76083756453480201a6fe1fbacacff830a0ed3c0a9d3e1642d12702303667ef97511aace1044180e80f884c338fcf48175e20d7c914e8c5324264c1a339b52fac5afb42447ce3bf7d0bb3c159dd6cd2402e3bd869afab9800d5a3caa00301166cd4af78c01b0a3ca4d26df238b9837fff7f2de90008a626ba675c49beaed095760cb84ea3fa0450c857dc4a0e263e390c4b41fff2a06033a43b8fda224f0426e1d637b3eb7772daaf3ca1f90d4eb50da96a9c313ed05a8900b8f9d2a94d500e2aa4eb73747adbe8c652e5e056d7832ac973ab999b2e63fe81bd1721f4367d82a341dec5b6d0d472a731d93c6a15d91d0afd1ebe6a86cdad7678bf54460cc64cf25acaa16048805dacd192e36140ab9443c864757ef999fc7917325cbc91144cd966deea68d2e5792a3e5c0f10117626c3b7a4af6504e938a73a3c55d542e45c43e8481eadce295fcad58c9c4c1588e081e8951bb6f9e9d62136eb6365021e9708ad01d10ed28c129308802798a636ff093f6317d8c05193e0cb3c7cfcbf1f9590e1a2081b87629512701a5620d2d73e4a246aa132e74818898f088464987b116476b466f08dae5efe487e9844a930f0cf7329d97b0d364af1edbd8e4d3bf18e61250cca4d55c61a1148e62e5a4634b97201f5157b2352a2996bc0fcba3618c45deae6ae9bd2c08229e8eb25346af29b0009f5f1c79a561c7918fd2dc22f3551a6b09d656574647fc87677b2269acde8400b84e245e6d6d2877f73b318ce7041c66ddc78c177d6b9b69fac509b23ab24e28697fb2181aac345a77e490ead1d204e4bbbffa01e2f978916540ccda05cbe0bdddf32cdc4d29a07c48691f0f8b6237f4ac28f8d817605a3afedb8f9dd01b093e2b7ec50ffd2b3afa033c78ca40f0a29f8c5af960562629cd0f6ba885a2e7001429362d7d9ba73bb783f5055558c118205beb244f1594deec162391888b8a9a34b79686e5b2cb3bf5856265e00fadb866480dee7fb68b3ba3e5491055a9844e52f95fcd07e999ccce07a2e8716943eca3f9b08495c4f42053dfd4fef9717a3ca67b90418863b3f53f4604f61bd48b1425c158ea414628db2054b426a2f6624cdfb54fb346a28dbfd0e7e0b30a9572162471e77fe087836018ec7a696e1f5f94f67ac6434d83fdde7ed7102c046619535435b49c7832d7d905554685e5132746a894654a5c60191585a7e60b3e1af3662e5a9108e3f7023eb17f751e088edb2f1b114725f80ea50c7d7a16580d890c2c5a60d87b7609f5d7ad4a2cbe770a57f8c36cdaaaeb154a8ed4fabed5499647b5d5db5ada1dd7867cbfe920b9cf5f5e23399a8640e60cf88954d5ba3d0c693b5056ec563470ab41bace442e47d9d090af0f985751a9abd5d48e0bfb103a75adfcfe49f56451f2dc33d156d4fd6be560c67ce3e73d8d9f8bd35795417c84a887b14199a73b30ee4ac88aacff9621766493767a3010901227429e4876c904b657202afd05e7efa8d27c143b181844b59150a2775fb050571c674c19b2de57d1db63a39778be4749a6874ee765eb39c20a57aef78432da0c9240d35b5afd6764bd37a52e9acfe97308802705a9e076cb0345a19bd2014e022e3e92b50f682be8b77da1c8fe6fe2e0933c18fd83be74c3a0017908cb2b8be99b418dc6819fdaf86bd057918c648c33ff028d0f9f76e30bd3a2825a841a7fe6351a0f3f6de3ae405e0519e91907fdae8ad5afae22988918477ec58dbf8247c5cc3455016cc28c1f4067a85004fee0338749a93149a6bc0a3d25d466029202a6bcf8893301df79ceeb2684982330bf1d084b6b05877a8d6edd423020caf704f52b85d9c1ac1fab66306af04269efe8acf883f94b56501330a55392bb4895f7019bfcf2fd4d9bdb1701d09448328a946596ad5a927355c62e10a378b1d2b02f388c2d5a7d6a795b38077618067dfe5ab1e7e55b01b7ec720c0265c4b4c5fcac6800014ecb498d9bb0d5f72ff4a235edbc62503fd47ff83cf9697c5f2baea1f2326c74a93cca0e01a009b3c7c4378daec64bbf11e0b49c3e470927abfa282e88562950f871754ee55e4515ba3ab0e91c1faf461da49e621c292ae5c2e7d022daa75b03ebc9c6bfcea793dd719603697a9981c86eb954e79cf8c446efbda489f0d12724ea021803f9a9645a8bbbef2799cb0bb5bcab84789498a2b80480f340e9afd5012fc788b7064ea348cc686ceac4bddbb1bd1324caed8f6c394cc76331eba06049ec7e70ddc728259ace9f03971900ba83996a90766766f39974a0d05d39278457e67a0cd1f7b3cea1975c0ccabc0c92c974567225e8161626e4b9bbe3ae79169e857af200b6fbaa6b35db72904fda19615adf0722342e966b5640c1f177814e394aeb15ec9c72cf9d8ad7fd720c0293398a2abd837f08ef0ea0bb386821573c4be9c44f0de10049b8bf34f25c1f9f741aa8df89be3815ba7c85242502b00fae7805a9fae794785bce604580360c9ddd39219dd7888987c56f7022135c052e309241202bbcf2208a0c845daa13c8689e1c40757769012688fef5d43a85f52bdd5b4f3dc661fd0aece4304ed0b0f212403d837df32b89705aa6b13f7025d384362aa9f318b18319b56525d69354b426ce15cbac84af82ceff683ef28061dd42c01930103a726781edc2634d7f92c040bb91cf47086e8b8844f26af2e59cf4685c730fa10eb4abf553cc1013dcca1050ec93d0b254398c468ef4d9f6eea3b07502e499276e2d49f457e5e9bcde7f083496aef2872f97ebe4b9f529305d2402f23f64a53962e2923ee5d9b77c35b1d5dd8a1829b701636b626f28f9f37678bf460b14128b05f82bc45e4861120ef857dea628f6b6fd345c9b3a09b35b4b05949a07e3ead6e4e668beb065bfc3464f92134ec4c9ed0e7c274c6254a6969f77d0d776b2764b25dfc00dd642ef28b48189390107c97a1ca441be9a747c390cadc04ea9e2726d617698989b707f2cd3a3fe2dfd30640a93c9111bae6b88acffad6a9fd0657821b150c8fa18030b440a1fcf686020a6798b801bd08055d80e8aa3d56731b0792cb485521702bd2c0b9fd1d4f03bc6716c757bdc73f7ea029a3da08765921dfc14067ff2ae51b7f8537a807ba1cf06d3f6fd157f898ffe967ee84e89437f4a7d7407255e15a04387e4047d8c3668ea4b444deedc2e3e2f3599069f0ff44a1e74bff4a3fb253ebab7f4a1774a0efd53fad13ba51ffd2ff9e8ffd217dd9312f940348f859db05c559b9240b0af7e3839120cd75f117c372f16a04b666bc5f9a623f847094e912173dfbe73b47d887fa43897f083cffb6c52b37ade5c4eeabb2c408d7c54dd5a416a1ee7cdfe76dc43679693dce4eadc34a87bd979f64934a55ba4689125d6db722cb32c2c1763a6bf00517be70ff5dc7e92720d47fbfb5ffd40ff4c5a9af564a2d772fa0621ca70a50049d2e1fbf98db9b9e3a928e2486efb5761f0a810eb4b6391866a2aa4de3e533c9553fc39914783674cbc309ea805b0762d9b91539b7d004e291861047971b7629624506e40eca472ac3e290558b506b5da664fb69873bd4c44e25ba85bcc084d6ede6641842ad00178b6c1464dccb298f53ce910cbe7ad43978e39064093a0974a43fa717574eebf011a91324a6173ff214d12b336472a37515040f32ee2662e005ce59d28aed2204244fa8e02eca73f21a2a83a8bc7780e860ccd2d4ecb95a5688954f5a7415756a7e715b743e71d76b6a491f627758a1e9f59e7a3ab3850e40455aacb88c4fcf7950dfc71964f8144c22df0fd73e2ca9750124aa7e748d6c23b40f492fc3bb42c0f31d787e84e7bc1abe061a9bda9b47887b6745f7cc167a72af4b0962848231d97db5bc9902fd124e41b459589bc2018978c491ea84ac70938d4ba4382e5cfec133412ea5e78df5a985d83df252dc14bba2129db73e77e5e4671d31c6bf6ae66277e9ae162d5c1e4f33d4b6e5aeaa77a09c8abdb969a93e97d218e8ad41e578e8202a4b9ad69c32961ef7bf6bb31bf674bd0e297bbe8b5fd9e4d62d773f8fde334ce30262397c1efd93a38a5b7a15a5324537152c3e6c7cf66fcdb9e6645562f5cdcaf2969c1ede7a97f55e883a36b4612b67a4a0b4a9988482b1fa4d04ae583ec146bf575e03d2e7fb2521b3a0837ffa7e219ab3262b58e4a8f08ad18780f0eac3f1bab50f786a5a2f080645882d0153a90a1df9699d8d0f6c354ada0434298c96effb055364ff53526d573874973cc4a337e173ebac27d8f4205fe13a8e86b47a25cd805f053f8a3c394609a0bf8a78d26ceb67752abdbe388568f8f4152eb052618fcb7e8895afbcd59865e541146be6b2ac818ec58369c9ce23d3b776fc2305be5b2632e177ce08e005a6b70573fa0e0e182a0838e1e26fa05e1a75f7a000935fb08676ed5f323f3ce2023f022064f048137b77c756f0a73a5f7727064676564e3c9fa0ceee1ccca249fc16d8ffc152ae8fb945e14f07c855613fc05b948eb06222a869b6d8f86015d91a21b1bf1fa34b9ddedd31aa7461150147d452aded9237a0eb514c36144bc1b850e2024e30caea3c1f4e2772fa6aabe5f0b690050d715bdbfd915824dd2c9856b27732e665da53b40bd5703a4286db430913e3969e358a01d2a9db7c98d23f48e2371b37006d6bbf24c3ccbc0147d99e489109773e26ae85bdb40611959454d24c59e12b315e2b10734c2e0db5093d496d589b4aef92998d6f88e6e9c5d5050b139ae771452c475dfb396990adf6e508c9a1f5d9d90fd339879b88acc1a6b4158614713d80c9d3c1f80b3e5f79c2cdd20964c63c6ecf9648389dfd1fa9e23e0d96f9afe45404ab1daef8166e45b80091c01bfd79993fb0e0949d3e23e92eb9f61a4ded11085ea01b9fbe9a1614f8236ff5c495e941e1fb0e72370c4068ea6ca656b2cb22c76a6871f0ceb63faaa2ef1fbe282a5af19135ed5829eb0b2cc61ecc95ed4c153b9596167db5ff8c206acac59711ef4d6c1a4f33c9d8941105e57a748a21a060c5c14ec60bd1655eddcc60a1188b6b781c7b8619840676a3671e31a5292a9734ea6c91ae3d66183b56aba62ec4adbab4325676fb097cb2b5a79c46910c804c6d2c43befd798ba3daa6d1484118109f849a563e056cab539c6143530ef4eced43bb1ab024bcadb1f6b96879b6f2490149b112cbe28727b0870dc1ed76a18715880018d2c24f4ab6d28144a4bd18c842dfe0f9cf425123974b6982ae9667aa3f47efc73864ba4988eeee9afe1ea576cbb0fc13678b79d131fb740f7d813d1680f2b8446c4ab2c06308f7aa7c75102856e96e6ff79c4436bc2e803c110092cd6a608d8eb891e91eb7a95c5be0123f149a34d5b7718b64614458a16ab91a5393488ccf2bfe84c7f3edd6632d9c2c37538a6078017962f494417e3810b6d91c6c48ed86df4b13395b250e4f6b91423def7229c3fd02a4606655149bbeb49af87e42f2221c60d0dcd5e5cbb946d59f9650fcec0f5880b4936b2e37e8062d2078af8ed7ee26f5a8b73e2174fd4f50e07edebf799c3b516c51637015907c8b0c965e22d8ae6ddb35b53fd5462af7f0204c4e021998cec8063ee5e618b355b1a14cc87bdb8a5f00728ff00b78e3b80ed4483eb6cacf03c6859a61513512029fff25ede90b53e5a8a42148970da2bf422166cf565d97136541ee4254347d826bafdc31c932315dbfeb7bdd332e8656a95f1025f2772428e0c90f8b799b755e5a9fd5ed7cafe657595f6e6c7c0aeff87d01e03cbb433f7aa11a2d0cea902f947e7a657f7c6ebe3e5ef4e8e606064c0a48d9283985a774b191c2c07ab4b669401f16ea1e87fc27679ccd8622423c8e6f888bc6f1882a2c8fc5ec207c1a098e8e33283cebc12cf079dbd7747df5fd277f981096e1a284db19b19fc76aa936ad6436417d6adc3dc4f2b4f21e3d2cac492474e080c2f1111f21d2fb894c32b2832fe3e772c754fc70fed424cb1a70cdb3c8fefe74dd6f97e76daa82d2ebbfc0b56762c2733cff4decde11062b9baa84e64085040bd76928dc35654abe30a2b56f375dc5432e120908cd08ee2f4bb6e91a832f74735bb8e12111b0634d4dd51ca2f08b18681b6041adc059ce55e2530899ffa2078324b5709ce9dd5e17ef561a25cf9f75ef19a4e1530ace8003eeebe5d43809b4859c94d0fa8962d5e5cae33c899d86dc5bcd2479f106cae265fbbc05550b7dc2c2de0197a20e98f7d52a181b0ac0abb93528bdea830c28127ee037b45b15fdc9674154495b21216e7a24d0411a21ee34630bd7ec44443702eeb0bde218e1dbfe18c7fea471e94146b0edd53c621b43229cb749effa06ca2896452192c6b0f3811398001444345e83a93c9b061bacafaa75c9195316d26c2073bff87cf320897b6fbdad914dc5d56b784272adffc9b33402a1e33378b075f45e021f7f937517372e68c8ff96be095a0c85e0a3c87cad35d069b01d26bb9b48ede5fb418fdc6581930e6e66812dfb18680be0ba712489917f1cf975a857168480a1d5e5937c41399fb44152f2b7f196df516303b0bffdb16c821c4fe9ee9c45bcc4d8280cf0f4fb02d2a1a0f258d1c48b11b29963c422235016e4010fff4d926681f444308180bc924c712a4f21308e80d7227e84d898da850b90788cd776558d033444064150b0fd6eb4052e03cfe84ead646f2bc3a7866430ace2bca713c1bbd414c2120c7e8b724c026b0d4ba7c594181f67edcb300ac51ca5ce266faf3785bcd0211cc20a28025583d95b9f498e0d4c4d649dbefc557823381e3d9adba830ddd39096434264443666c0b78a9db55dffd2423fa1acaf49230d2a6fc4eec403ba010380e4fde5ffb258332982d86f53ecda4dd8d39b82e662c9c839b1b07712e3d1e267a10a8e633cd71ce988a82e4f2e191fec4f9a89880a406782d65d30c1420a132ee1281cc2e58a259c123374aba2452666cee7391a876047cd006fcab3faf7ab42e3bff497abc12c0960f849d65dced2598c9eb6f9b43aece606881b6bf1f462023f129ea5a320e81324ec19a964ebf337d406e3b134849872c33f49ec6e123fae9d89a0f12c06faed587c268242f5636bbdfb51f7448a84ac84902a9557217de4567308f80963e057e66556298fa2918307492744a2deffe522d071fb572dcba1bfc98c19e47088797930c8e5da661d12f7f707799300ff9d91013c969c14a474d5d067f68cc99e2dfb8acbacecdd7e30e29f823929b8f5fee1349b0a04229fb8dd4f184b4cf4b0d198d70f85e644201e9b73fc5dccf6d1f828f2c7565db6b3ceff98c797fcb1d3306e5a92914b8cc3a89ad81b359a7da9317193b30231e7b9b5eb1193481f3647e02d378a02486ad3f07873e687e8541a9c72dad93922e23d5e0bc3af11803267d7e14a53e7d4813d995efd124688e40bfe3f8b28479b6eea68d9391408404db3842e634d2d870fedfa91d5d8b0fcda8c06480043854193bb87ee6b03a8fc429cb42ebdf59200de3b9930b89d7168cd2de497d91bbe6dcdf7d46dab30790fef7da5ac4664966d88a25327a16f8a21dccfe936b653d3e0ecbf1d720f3bcccdf68275b7cb110b97f21077141af88f5d20b14b4d203faa0a2825730da55a50371802a85bc59d4092c9021ba78ebb2ea33118d989f2037b72d8a6a57fe2e887b4d8bad2352d3fd883318800077770884b4d8357b8d010c6466d6759fa6d26f7daf99a21e31caa28ad9bd37d9724b29534a295c080108e407fc7df3bcf233c46bef6868746cd16407b3bd33fdd052ea1498b35c666666668ef07164c88f999955f5e9eeea32abaa46156bca7bafdfee5e2b331ebfd7efbd27e6faeae0a97a4634b7bbc16630d7fbbdd7f10544fcc418e3f67003df8f889f273c26f66552f5da5d7531a9cd0ef9d06b16dfb93caf1393131daa62aeab0d5d36f29448a930f0a164de101f14c4584dd3ab900ab67034579e903826306047fd740c25e68c5721151c2109a9c087c80ec98111a754c023a515a1c31841e43ba78956c839a7343d93c969f6e62d8ae6043124339bc030d253c4b7f740e1db4bed6eca98999999a1a80d22b4f8f637839c71f4840c561021c4b70340cbd1f3a2e185166aa3c64261882c0ef02d34c4ceb79fb41c3d37318041e30a21866ef070f1ed33a6cb175ff0d0e07b8a6f3769395a3207b10a333333b33432fd91015eb41c0d8b480113629e9999b52fb4704109a5a0896f2f6939da34062f5a12f0ed328a0d424108e1db59b060820f941f9464e189b9535650848915a9c1368a82a226e46068c48e101ab023862b50b0323dfbf46b074510031249604216945084153054100309cf2eb35932d57018948cd1b3abe000c4041a2c61892835088308329aa8768514589e7d964c359c6000212050f0ec19e4999d040301423f20c922054e38011606c8c134550051810b21a2c085177889c948a552a5a0bbbbbb9b3486203c3b9972c4690a92273c5908a9aa600ae3c9b35f373f27cf602f8458a95284d1826fb74a586cbc9112856f8752a3ec2411a2081e700174847908cccc7e830373c6dc7c357c4b41c17712bea5acbedb958a6fd34d1834b8aae0c51114be60820ccf3ebfd8c2156044c1d3649adf3588e10a9d09c754230c613c6b218627cf46cf47533c6be1d91dc60b203c4fb9324b4c29496030b29c38bac020e58878832259440fbc0190858412af64c24150fc4112adf2c924ab865a8eab6da65c05f1de2b99901ce118fd0b899c61b062c81027823a466518b2c0821c49546c4a19e1d331fa52964c35848a504265884b2ac182e80a581c39918510318ac84206047865220529d6ebf72ccd62885111265f283283d512d8df5daee961b95acc5002ddfab8f1db62ed968e9df217a763fdf271e31fd7b507599d6fcb72981991d02178c50f8a5eaf780dfa41cf6f3628810741d3df94a9380f90f2ea3cf4c036a45504ba78d521250af24210df6991176ef09d76d13b3b268cd92dcfab922974e394d9c9f5f102a6705f14a82de43e800b4890cc052e9e9f3c73918467293c1b3971e351df3ecdaa11be6e6d1f60bbbbeb25f87d114228354657a818b436e8dd95bd17c498b25595673f86cb5f681940dd18dedd5dbf4e203161aa771b8334c5aaaa8d7e3d98e327028b71717466a3de88dc7bdd9d76747e279997ff9ce65131c60813638cb147cb78c24495e68baeaffb4fc613261f613e659bb588789b83f64ce1831807047ece632ab01e7cb041093c08721a869134dd5c37b5d0052c54a9dd5f92041155aaa5a9c97b2fc97c6f7fb8a0e495091790acea013b40e88d5412d621e57b9b3e7e5cc703bcb298909b99bbbbbd2de7e139e2b4dc7218c55ccefbc809e12108f3c483ddb153bf0d8cef2840de773442b2751a29a7c9e48e42d10841e227478d76873c0f8c9a8118903c63f1bcabec89b7c59c6d5a9a1d6c8b8a7e7efdcde5f9fb024c61bafe79b843c7ecc89e3984a8c8abc340d8ea2608d5af243449924868f3e9b25c2f16cc6e9f1461515e78e6f3c554ba385a0ce3ba6582f7915c6edacb10f371dd42d9274e968851d0a52062f51dab14440c3d747e02a33c6c6ea12c3767a329c8501efa0ed0574574031dfacd0d0d0d0ae5aaf681ce5eacbab7f0e46d786512e5c8330fa32054e72293f7a8bbbbbb1be58477d09976e2c4c96a353424bdbbe968a477286d968e4979e83bc0708dbe44498cebbc9d74cc1424164cdf4f8aa4c393942513dcf1e19a181d9ea2eb5f513564f54fcb1698c274eca38a85b883dad8f6cb7de0f00f9392eb7cd05898dc4d72da5eae5d8eb9073a064224e6f36e8b3a9999b8c672e8a8a28d723924b95c23d9fa85c3a8dd4bfa402e3cc625a421f14ef876898528df9e65018a6f2830d14212a83f80518a7d7967e53ce27a5ee8e0bdf77626cab3b3945d0ddd8d7f7ef3de7bef354cd1d06ce29232a802ca4731e284e8df2b132d30a1a578af08a699a543b2c5cc1677cb962dbbb9b7e8620b28dfd128932d9efc7a0d38db64a5808206aad4c362ae3fa279b96a17b8fc4561eeb876e6253965b9ae2639e9e397676be2390cc7acf360fa773463e0c1771a64edf48b52c034a5af0b25af4cbef0e4bbf98521fc12060313bf4045de66a7c062767cb44b8e8e34124655c1355db7cd8e65d445131ccfa91db9fa42141b961f266020621642139723619bcb492ebb9cbe366897b4f7a9bde3a3ee79e628ed63c4a8c9a8538cf19549174d707cb43a478c2ccb79fd50dc84469262943246cbba41c3584b290b6401b111d02ee11acb83760a2ee228dc85ce746176738316888f5ca17cb3145e62c60c5bcdd96aa9a5167783f1a47cf2bdee961810d09c966559b0832c6e6101a61a15a60a1d117404901653858e501d6952982ae4d3e4d9d7a1eb2bd7a8908f3294280ae549108c56e4ba52d474648c821e5d1995314a4732ea418f9de487f341d729cd3d29e587bb9d5f43ef079faa764fdb82a24bddefe7492b616ac73e6d846bbaee76f53d6cb3434376b57a2693fbaedeae9a449d5c13df8a31fa8b5376cb17dfcfd5232403265e85887084082e782857e8798ce21a28b5c728d819d7b0112638a669d46b88718aaea90be6bde527cb3545f2ce88e67d7a1cffa894ef4c6018c9560b92e582b7cdd217e60c2f62dae3c63f2c7811c9f4efd6481834f8c758ccd8f1bd27ad6d2a31336e669cf15b3e65ca9431fce39e949ff92a3464092b4479151ae2e44daf4243821ee3077b78eb49ae7b2f2597c332b306ee668b9a336dfbb38451fe1b047777b708aa709d9c1345c3401a04f617c9febcfc0a74d576bcecb5da2a7ba4a998cf8f702d85f9f22a44c4e7df8dafe1dfead538f21c9359035e17f03a233597d59e4a90c675374bb345288ceb9cc43557b482d969100422c0380f9052ea34e824c42cb1cde5d0a703a477325118057d8f483759a6c9151dd9224641ef2be6cfd3d93601b1104298492955f6aeb2d304610a037b3cae59f68fd1b26c009233045dcf5920eb277d41948f6e8af105aa8f0e4313bd06d7ac7c74264f18f231fac7ef4ad09f5f464c7d92478f9991f9e843f4eee6c7be8a8a0ab632b80c323c3b8ec7b8c3f913c9f57b5c2e1ddb4e248e87035e3a101dd55fdc6987035e3ae0d9759e784c4f278bd3f7fb415c908a50d70855b53b7a3de6f19d55f9e8af6a876b34ef1da2aa4a34798751568cbbd7e5962f84ccccccccd7bb300c7bd8c31ef61e3fbf72b08a519d4eb4c264225cd3a911fb9084784e0ea93b35daedf93652231e7284b091ab35242ee65555558ee4bce5c8970fc3e232f3427f6fdfbe7dcf79499b59da23754ad429d1723b6c1e605477e05232bb2552378a2e6841176f8057a11644e12f873fc7b91edce320a739fbecd0df426ecbf4f9cc938eeec7e5e860ffd1bccede5cc7cedcb217cb01cc065aae7dc3e31ecebc4a50d88ed370d3f71e5618d7d05ccfba914042010988eedddedddddddddddedeeeeeeeeed7fe36aeb60a84b0a89b084208638c511e7dfbb432cbaae16eda5d2965f654302894d7f01a30a6abd7b4d9ae02313bf9d96e9380e8deeddddddddddded6d1210ddbbbdbbbbbbbbbbbdddbbbdbbbbbbbbbbbdddbddbbbbbbbbbbbdbdbbddbbbbbbbbbbbdbdbbddbbbbbbbbbbbdbdb1ba7eec35ab10074700d314ad9df06c4f3a8ffde7bbe1b7ceff9b212a229a4206af22a059112a223af52101179f82a05d111afbd4a41b4f38f1bb2c339a14379c6ae92f6d95d6ec246b415d7d0ca0e102b28d3722c40d80c1d534b26211730f15d36c3c42887e918a0fae898aa1241f9f88451ab2a568c8a969326aa43de648a8f6e85154b943cd0c76d08a3e2e633e313658ca2b3cb184599a38f333b333c7e8451d191b4acda32808a0a3c6b6d3eab28340a2dab6eee86886b66886df36638fa1c96636ff3020d9e97319292a58c943232333348d826c647464a37d32373343333c3c4ccf646846b36235cd3d1e0d986700d1be16393efb6221b137ee4812cf9e83f1fdd022413388f6b0a9339923102637632324632473bd09f8c91cc51db40eee8bb16a3875d41b36b319231d28c7c177f70d8c5410746c500bcfa8c9f2d8c742d462d47af2ba96e8e8286188a8f5b88127f7865622c436d73ca01f6009feba4dec6e2a4635a76b485a765876be959cb598856b6169f96218c8ade42846bb48508a3e266f9cad62d3d5ca32d3b2d3c8c8a3d3ff3b316a38e89deb2e2ada568a885a859866e28f5441aa72b1475cb8a853eaeb8eaf5c5646b3122aca5a865d53637442d2b9652cb0d4ba9c432b4033012546a6131e29aee86e8a60baed1287cfc6276322d44d01fd742d431519339ea98a97552957745a523c1b718718dcec71ba2b6b959754cf49b159100fe6625345732462d2be93246ebc307786bc59b7dc55b565c2300b8626da9a70a473e7acb11d7643e25224cc0acaac0f3d14baab679a9e8dbd6e6139f997c749923ae89e1d165a4a8b806db99e1e11a158f3ed3137b656e39007931b68e65e8a3ab6c1d8b1320fb5a8b118b928f2c442d48d606276da33eddb2c33632159d652833c242a46d34d542e4634b0fc6752cab8f2c404746dfb518fd7c44b5a8b84683e4d0ea9db044f9c872a44443961666a743ec12071dba9b0991d10b11932b0cd1e8b6f887086815a424c6251fe5bbd8e4e346c72107aeb93cba0e0e81b6811e1dd5dada71d761de1695ec789625ddda7c6044244e87e724ce3bfe6c5109a3a2f316811815638b518b51748cb7cd8751d1658b51f4962ea2978c442f2189de2225ca947a3eb6187d2cf97c2c11f95862e26349f5d125cb18714db7f9c87411adcd876bbaf8636d3ff187013249cc7085c168b86a3e946432bf9cb7abc6c59d5d8e61986317e6190da31e8a5137ff9c6b4ed3db65f82315616244a00ecfdf90d1c7bc0aada0c963264c48abbf4035e9d052ce795dfe7239bce40b94ea2e1ec345d98a31468d00a3decbd649e9b2dd306d9d8f1bbfae33b616c23c4946bdd931cf63b88ac720248d875cdc4c6e6d11d06e30790c5f39c2cc5c8894dd5c199a9251d1575cc6767a7119eee2733bbdf8f47dec7271d2d6e950b6bd24660dcfa516d7e6dd61d47321afd4c22282897a1ed021033b74806b48965f9b591649734ce34e3b34c75c87c661aef3bc759b6d1a89b4256069785de92d08985e362039a6f390e12e2a3e37fdd2d6b2b16c56917f2ef563b0ab780432c031cfdf0dd674883998feaebec2ec7688468543ad60767a5464ade7be0225af55bc0a1d41f4d7268451efb15f9ab62373cc75648e695cc7982bb6659b32cab18bd9afebba9cb9d3e981514f95630493e442da66536f07e30ddb1688513a7f394effb0acb844b41c0e04ba90e5e19a8bcbd1c37b2b179601030cbad5ddddddfae4d4ed87aef4a72b04fa6d46014bc6f7dcff71caa9a5b5609ea437155534a1892a05dff572de33b422d78e6a9b1d1ac3ce196a8651432a4cfef54ab79c82294cf714d53148d446cca5360540996c7c7b02be3d07aa574650607e92ea59e68ab9a2b8e6045dffb9b28c4585d9b9b7cda65ecb9028697b33bcb8240ba60ba63f77c675341eb40f0c3fcf37980f3ec6196a3b9a9fb939dc204c6728b3c74366eb572d8744c9fd1883e9015912dd16e3563e30e125026cd394b5e5f02e6e7501d0da4e14c81c731d32ce75b8fc7482f9438fa46bd1eba47452ec18d7edcee573dbc15c3b867442c1fca1479fd31f5d53aa08b841e75b53d7a629d2f6ecc3b7f4a8f150c0152184edfd3183da73ebda9204e1d8b0bb9fa622b920dc6d3d68044c880991cfe99fa108a66d9324fa7e0e6f0171cb615529c9adc65c2290724bf2e07b49a006df7b71d38127c98f9bef91d36f39ea7552d231d5765cdeed8ef42092fcb8f99c4640eb939ba660c73e3cf36879e89895ad3681fe6f8f724b1284adee9650c5e282f0c318b9d84f064db0dd0821fb5adc2a0a42b65ef4a6817d596b7fd90bc2d1f2b8fc8464d599206ced782a954a85eab409f4f0a00960124bbe5de75bd3a1f3cfca3888cc5adbf19ab755a4b726011d6b9b88289214798d02c9cfbf468164898fe10ae38351ac6a5432f97b72fad02e94b7d71b76ef90430456b52d0f2f809de5c9ec7689942a52b5d2a452c91b4e8956a51a5b471843d779a24fed1dce43d779d8dd32b80c32e0782a2a2a333c862984b0b79df2cc2aa8e5606d9507b7fd1e7a6a8e8703989bf2d8cbe41518b4e445ca482c98eb9677127b2a4f6b2566788bfb3103b6da0dd3fab1446f2d7c980dfc831533cb728cbfc7f0cf5972cd0dfd5dfbe597c6037aab26010be3ba76ac7940b7709a084fda7a48d7bfb61e923bf1780ebfb7ae71bcc5fdc0f196f73eeea70202fe20f1ad3801e8cd03728fbbb87859980d501b94de30406b200d0f1d530368a780873b3c8e9b26d7addf6c38eb377072200e0e0ea3a06f356eda665345d3b9e6b4231661c4847577c628e8d625373da9dedc18d21a2a264f801ec29f274f20195551c4771ae5e1d6c0228c3c74996e4d02b108231320391675eba263be4e729c3e2d74795aff99fedd0dbcf9ab0553859078f21187e3b347958cf0adebc63ac3abf2087b857c559b7d8229d7e82fefeec218ccf5ddee6ee70a5ceb8487edaeee2ef7bb0be51630551bdcac92555a0efd79011e6e16f866d2c720cc3ddc34f0d0559bc0f59d0f0f39fea89e3d6f3d687a891897d81526dfdb66bce95f54804819eff2728b1a03551e02f9b6b652cb6356f63696c764f6b695c7aeec6df3311296a93c96656fd31ed3b2b7658fa9646fc31e8b81ad6472d3d4dbaec756b2b7c9c758b2b759f1b152e682c9c05e3253f64c2f3268d050813a039d04dfab0b804f9ede877def0dd154bcaefe3ae27d9e7d235c311c4a62aece44efce043072a7fa080cc8f31c0924473eeb4cfbebefd6da55bd99532398c27470a536a45462f74ac50839526a8bf01e14f0716f6580c912c7f072531a41003f805718de223e60f0bdfd5d2030551bfc3a70f7067031eee9adf990a592ac384337d5704d86e6b506168c05c3b07ec9bd8c073fe6986a15d081c1cf0f5f704114499ea0450adb5a38d2d66d5dce4b89d32b5b13d3e5e623fb26783c35b912c7f4f031627093513c845414e68a7539daf65848b6a9a230922b76bd692262aa74e401b6fe38c4189555e3964939e7fac88ecce883e65fc78edcf11fa31ea3a0e6d0995678046387afdacc6df9e39a2bc394afed726b93cefcb4c3e1f76d73cbddde8340879dd9dfabc63acd598be5ecf7e8211da7ffad240d99ecfda4a5fae7180d0899889cd35720176311a8695ed2bcb4e4db5d1c73d976d80e21740dbae6180032172fb9b8b80c297d9198e2958a5721224e1e1b806b37649e43804c5130dcb6b5b8a954728d089b41e3b19a2683b6a3e41a94f90264ab3622efcb8c9719dbfe0c07c894638c9bbb0c0000000d17ef605cfc72717171717617937643e6311c2b40c644dac6649a536374d2f64cda8b464441ddfbf102fa91c92876c135472f1a8f76d81264bf85ebf63b93b768b1886b7a6891886b5c341e7b03e8ab49c072e8a54dbfa4edd04a0e5d82da668b3a2967742dfa8a76037fe9e5c565f80c8f32dc456dd0f0e86f076aa3e4d1b152c967b88c8dc6b6bfa249a0e42e2e5e728c1587696cfaca9d7eec671ee4fd8bff88f12f5efa92bfbc383b76008c866c5fb43063fcf57e5eac30bb67e44b8ed1bc4031820412d676b4bf7041f686193154b41d325c4a25676d47c95d7cc6a6dfda8f7d195eda82eccbc8b8cee499ef971c6a3a7ac8f09c7d1737712f50a2bf235c238333c2352f5c278309ae71e17ac870178faee2e29ab643860b02b2551b37f4c83c67f505ca4b1666c73c0f886bde0fd7447f4ab826baa6f1b05c7aa6ed901cf72a06559451b2c49d54db01dd722b13c043003c74d5cee5a1f6307b082184d055482d9b7ee62bdbdc72b68b4a7c74150b8771383afb700d0ff7c420cb61e47889b615f9e731a86da2121af3dc07a624ff5c72ddacc1bf87719dfc8e973c25dafb61d47b3fff9c89bce71b856ba4ef839cec1b357ee7c56138d03c8c328351d752ede68524a6f8e8564d7f8739740c6e9d0f3ff4181e3a4baed178441e3d06d5773efcf070ca639cea4ce7c3675cb74ec29cbbf5cb5be3a13365cae33417f1a44de5d6c3e24e3c22f79a63e8eb909655a0da588976d9ab110460f289e218f68602734019b6c91c436913ee618ce3226a7148a07c87e2b70a7a2b0e6af2829a25b7ea1f91c590094c61300871d4f02e5dd1e9d87bfe84306a075a1098ac13aabac393fcb4d2ec717ac411b51cd39dd09a7668a3e2aa0e90bd6d141616967ec2ec1e62b8663a3aa707e83cfd30ae33b5b06c4056650302bfdb234076cb81dfb2d1d4c028292402fa19a3e00fb3db22295909f339f6322e1e16e96a613a8cf162c89fdeed171eae638ec3375c034d2a2c24aecb81374c04104218218410c618a3090296b5aab13bde1dad68452b46668859379665dd80ccefd15852aa919496bf075f37777777938630b19e82bbfbc9a92e97b31aae32cbb22ccb18c60caaa65d2998cc75998a4a8c1899a692614f3c4163730424b189d099e75db7104ac3ebc284be4898ed108c09b94e897cfecde6d199e7cf572d987c5d6f0a463dcbb2a2252d6949cb82dc1b9ac1ecde50f3fc7ba9e76f75d27fdd73f2ef0dfd7b71f5c5aaaaaaaa323364c890599939d5dda937736f2c2a4cbfa199c194614cb804ab58a5367e0912f5eb8ac9ccbc8e13338f996b3a44ee042377ca41f620b913e470907e651c8eceacab8280fa0811938f8ac74775e4276868a52a622315cfb3ca4d268740c7accbcdda3488e1db32b9cc7169063d1df8a21648748610118078604ea0a854480a24cf39a6679d5d20966481ce701fa134b063fcb4d0deaa63d852593f583e4f0b4b88e744c32112750c933e6b62766f4514571d1357912876a8ef2291bba97d22d133c7b54feca1f6199aed5d5c75a607637215fb339f93f7c33ccd57212770e1d997c6290573817c74865d303b8bc88adfaa05d3c8e358034c6b669428ecc33e6a83023ab3fe24c6760d466d6cee6eee488aa4488ab1e31639534683e9dff354538b71a71cb0cc31d74969ae93621d341cba8facb30f1361230cc4413cc42b76c244ecc347adfa751f22ec73719d0e459aec8aa933989de977c3a2cc0c302a07263e99dc999bead4c9af3ef9c5a2b30efd931073e52f57264d3c79d574acfca99d7d6918b5bc43a67f879222e5b31a4cccc7b37790f9d086e819d75c9747ceb4c97862d6e01c8824f98b0e372039310234740bbb4bdf2b126b52600a632ad5d829316466c8dc2d5bb6ece6e676e7476fcf11234d4069e209067b746c31240a273c79a59945861c597db7aa2343ba85196502c999df3dd099f5de81d2b04b56b52ab5d13bd0a670645dbf064db683302bb341d10bd980e8d75b4907751355defde773eae89310f33d1d3afadfd3a132b2309b831c4ef3ee70f36492cb6e30e58a8ea2eb8af95cd3c17cfebe989793f9fcc2623ec75e12a62a73324f16d04c70b9e61777cae1729293b81e4e7c71a71cb01e4e8c7127e670c03c734de9a474521aa71dc340cbcbe373e407286868b5aa22a3239e756fae3371b63550c7ac635bff6cfdd3315107d39f5bcf9c98f21a8230477e10d903ba6579ff6861aecfe59dba3857a397feb6d95c8e101e740a46a91345fa677d94a89013437e7787d44495ead92ddfb6070fe4609d8739f235c7836ee9a76df6078d5917e291f8691b4d0105ad090cb3d359cdc2547f5047f28c30acff76964355da50304ff6b741a8810a0a234a64a8100dba787cad62fce1a334c442d962763a14831515183fe6c7feb8f7e4e97b33c014667b0ec0a00f264c610af35cdf737dcf155e61ce47b3c214c6f24c531b15d0d48324b5219f442291bc06464112d7eddf74373434fbd92c3509424822912034a1b08c44c27cd6c048369a5a276598c786b213f2a49310135e4dbaa273749c9652a3a633104a418326424650f0e80c9cec4eb561628216df3d28b13bfad635d731d783e3ebeec1dedfa37f7737086b9592404c99201c44a32e78488d9ed24e16a5c86d9f7e2a00da2125482c2674f5a0e5c5ec9667884504c687e779931cc33678492bc24b5a11c60823246276cbd359416f148f540dd3569035f4e20a968cd07c3423b41ecd08cde7b520cd47f301025aa0df2220219e54109cda49a9524108f1bc0a01a55497155715979327f134c986c87a3e5b92f50cc1b3432c94e7210b65a1e01843e39082e3e835e807c7118e2a09ecc346708fb211ecf31cf43e4db04cb9fb6aeb6ac932ca8efdc1d2c00d9fc7fd618cb3f89e43c8fcfac1a7edbcbd47e4dfd0fe08213201eba10e1146bd7e726314cccb9df1f5d8cb5aea8cf6c3f8989545f898ccacf7d895495276611929cb3012a6d2faac40e98cda405883519a4197d0650ff97c6453da48ab6378c0e4bba74cfe31f9e7dbe33dde0dfa181402681870d303068129d7e8d0bf5632f37b167347d507e16b4d51caedcacc966c65d54b5a96163e108275730a8ddf70981ad4e2c2e242c71c0c06827c9024e149164d9c90e226b0bb5c36324485912394e684e167361224630e848cfcbc0ac5a0c927e155680931fc8ada1044feb311c4f4ef397b89c9ea9f90909110983ec7f43bc37799f6de5bce5cd7344e41cce67abc85bdcd1a02733ddafb7b70d7479e4c21b91fec6154c3c7dd566487de1b7bc3f66d1dfa4f877e734f9be81c30f45dcedb3a1fd0890a87141c4738c680a3c25101e172725571597139b99c00653d194f3644d693f500e1885f3bec7c642f7bef590cf45d0ec3854a9686d79bbe84f039f6c37a0821cbccb986f3f7836ede6666e6e614a80ee60d65323bd3eb493b1afec1b05556a883b1d69c433d468bb7684702960675c85b3cb1f790ef264fb334acc7ad5b5576dd12b0346cd4e9218cfa613d3fb6160ee92144d13502f101c309e82155989d3a81c1ea053540a1867fcc0d27f7bb5128a86a8d2704a4babb351c04132553da1fac0dfab627979854d01cd540c94461aad01244be332dc1f39d34d540c99c5895ba7cb60d747689c51dd11bca6d8747a6ae8b93fe36b8f1f0d1b2a202b678e59a3845ef41835223783e02f86a5eb3478f7e520e07e891ab80a6184ea1c4ce179162aa41345548892221258808294104039eef4c39b02a05b7f6b8ed80dedc536fc2e29e760c8c3d82f4abb7bc6a3c08f0ca35318257f39a7bca141ba0ab4e99927a396c011ca9c769c7b47b81d9bbee6655556606c29fc3af363cef9a79e89429fb39fc8ae3b587742301539824965f96a5298c03a3dda8340e56553d0a118d00000000a314000020100a0785428140281ad676457e14800c828c447c58198ad324c8711c858c41841042082106810198119281260062f724b45d1ebdf7eba0749be43c98665c9a8cd908f36729ad3985913d2d0f9147db4ddf44cbc8b28923a15b44d9c8d3bf4f561784c8f68489b4b38a4f87cf06dde3535f94f98ed5d20b31d5fc8dfcdab89a928fbcc8b684379e1d251110eb832a4f72cb14b07f9cfc3c1fd40f2a123d03f534d0b0b59427e98d611aefabd5e59397b84981859f5942ae3e2ce9ac160df315e1dc606820b93c7e98dc8931bf82e06deae20942b9b40f147186568950146a8f7e5021914433756219d67d022935dc534f05372a2ce5b054d035bb1a7a4d8ef575f5f5284ff257c5899eda7741d0e994a73df9c9a0dd2879e22606245f0b7fedeeb029fe0d807426bf36c49f0cb35cfafc9cdad1fdfd66ea3083ccd2f3a312eececa925f5e1ea97ea4fb3e9e230a6d9d1f9762ba0706eb4b76353297dafde4b389624406e3703e4f80204c71db0b27004db232a253b92a2cf83841c103ecea249d8b94d38eb53a89de0d93bdac4eb292341e10c085c2cb7fe076b1854ec6b180e000f2d579879e890cdf50d69b30722b5c1b42a30a1a0b590994a64707a0a00faa1b90453edef5f515bc18005230f88b32f502e420b795165427e9ce803d7abea74ea2f98d09fd8a8b53b6b638d07813b79f805471636b6ed1319498a9a0a7ef70f631cb463a6f4aa80cf15bcc20ebf62854e8544529c5dcd16e5e2c537f7eaa7e40a1db72299ee0062a81bc75aee38d408de6ae13e8ea408fab30d746e58bc1805c419acffd7a3527a05c0b79ab82e303b5e252db8cd2abd9f554749a028f88b0af42a87327bc193bde081b9b3e71a76be1dc314bb4eedc719b9c7166df759704144a05b3d6fbb407e4a5f136f641f1a95269db58923ea5326985a8a16b36094563d78f27ba30674ab2510f83e03864b38fa9a81fe1dd98c316c15001040c0e650a54a60d3922a1652dc6ee9dc44f5e7453d229e905f17f86089b599834a131914ec4b45588c4bf64448f548bfdd912016ce3bbd55f4b23a93531725bca8df9628937b1df2693a5b41c5bbb89c820ab1884ebbc55ce029037afd4f03098e793dc88a1abf8da9aae7ad702c1e5f302df96a29ddab521d15b729d77dd2ebcd48c12106c70e46e5cd110ae9454ba637d5bba249b3ba82c8f0b7286577d4b6d7104eddf647730f47cbb4e886e4796446dc091b383ee7e4bf156db768808dc94f854df72c9f4207b4314d7407a4306236792c00669d7c2a1518de007f7fa2e8e196f305cb55b09bc788cd77018608c616f70acb094e10488459a849eac699db66f49c71210c952e74f5bd62a13ba6e0eb4434a3b621bde1240db2eb39767774bc3e94bd17d42984ea1fbd2d80153e4e575c8267bfe46110c3d435b4b50db8b0635a7c5c5bc84274cc12b1ac756b8cd1fc50d7c0bc07f7596afff9e8d1a2bc664b5064c5943624240113c52a6b19a273b918ec2833fb045e58933d6b37b14df11f2e046043fa716f3b498f8406339d8b764ae1c54f3e01f528a53548a5679d927f365e69606253d91dbc271bc5544754ab0d6d85b2ea571fe40f30656f9047f74e596dcb8950a9b7134669f7699811213c2580c3ef21fd1b837ded817e38bfaed8fc8fb0b6cdc23fe18153c72ed0b18242ffba5b72ea9776ded687b50dbdeba6388f7ac1e27e0a3671872ed31f7297133a8631c1ed30962b0427171033019ebd19bc5487964d9975a9c559bf166be62ebdd3c80f8ac340f03b334e3513d2927dd58ee9563462041c564b80ca41e347f6d4582d9c46a045731a665a63457219e1b3f71a3ed9efb896e2c062cb5ec65a21446ba3d914311a3d1db3afdd26322b67066947fad2837b3c16d22143fdf4cdc2268d42f8338d15e28dcf35c722a42504528e272e42b84b830b165930a9b50d6a14e5be10eb313327fe87f189fcc0028dc47fb76f6f090b43e7d2c4701dab45fceacb582ed3b947c704745160113ec219fa24ebad16a51f351bf4884f4a0a3f87449f59a60bb938f6de675e8a41da58f50a2e07b3cb6ca8e88dd9bb0f490f77a2f7e245ae97d556ba4e0017ce14627107c45e7b6dbc17b30a3617d45571978b5bd6b62cd919d68d7e65918d0a627b0531b76fa7ff592a4b7a5d46307a48fb417f5c494d1782cc6a26fdd24b27dee3e8ef67c932fe570aa1350d9d60517334d817f7a9f613570a1528c7b392a56acbfe5bbddfdfc44fdd58696e9c19e67b4edd8322e3c7f1042bf4c16e3265696b30fa5d67bbc56106fad20dacbdc2782962c3d3f7a12914f3ba93fdcc333afdac4e098fc4f6bc63a7be2b2d61741e6acf2a56e7ac66c09a96934ee52502840eab814f1ceb485150699c463bd300cc34ae1d811d09f2ffdbd62ef394ff2ab97ac564f3774796554adbac7a5db3c3ecd20477e30db7a0652176ec0e697741931e5359368be9ca4e0a1b6cd0cdb8a2d1923f5c6cabd9ce48691dad91a6a0bc4629cd3edf7de6dfc20897c6806824254ec94d97eba0cec95d6f975438819e528820fcafc306e878d3ed7cbef823f185246fcef5ff49e85e343654c425a1f87bd2baaaab008641dee552b7138b416683d685b5bd26a037cd9c7deddd54bf8cd47354a3e23ed96b4627c1dd5297cb7d0b5bfe1d9c99999b686e371c3e05f577f0fedd780f5dd165aa9af28d64e03baf508d7f389b088b993fa2bdbeb78eaa7d44b9497dcfa270a61ae7f4ffe81117a5ff8c515f047d6c370ceb112804e04b7abf163aab108e05f6a139d26ba0ee7063ceedca10d465b8e04da32bdacc3390ec048112fbe06a6f4eef338832443a3fbfc1606a25f6f3cfecb2f197a310606ec455c8014f077cd946420de0d2d9409c421f53786bbc56553a95c221f884927038de676fe3e990519e82a71f633b7532b05f91e10b115364223e79cde9b638efbc1daf2bd5019621392fef0b6e8c2829c0540438bc1a0560b88cd0d3916dd0d78464338c64fa1f1b4517e645cd092255f4778bc3b4b14f36981368bcac2b33cd18e00fb7157bad91983d95d7fb06f352e333581d6726b110e3422113dd84b545bdcfc4d6baa2c6a6849532bb6ef0b64b19217106f36cc84d8bcc669dc5cc27155022c19dd5950ce6db15aff1b9f2fea116328c78bc709c767dc02cd30a4bdad7f5780c6309421e7260fcdc152c689b1cc1c108ee9ef4b2b2227f003652ca013f5978cfae64a0e9cc466e01caae8668ad42a2a9b63455ba7b8308c2f53df10b01ad2bea1bc3328caa584e2fa756cebb6e692ced329cf2f460907a69c26399db6af1727be856dd61df8c2ed83307a53135a222c540aa57a675834e6c669da502ceafe0bdf7a50ba1505ce92ad070aea699210ce0a298de7cf22ca1b48dc574361c8c5740ece12e8606f2d186fff5607cfb3f017eaf54a67e24c67699b0dcdf8d16400f8411c8a50b4e883c27f2a6ded361e0a8a537e1f04e46fa1551632dca855c26c34872dc52aa7d2d1af35c96c16739c93091204dce68cd7e7fadabd9bffcd49051d69880689599593f02d4ac67b128cbcf3b4102e2c00a5bd5db060cefe41d148f22844dfd078389b917d6eb636cf213077ef63e78d46cf6db03bbd8a5cb60cb67869a342e1788a4441287a2f70ed3bfe38b84886a5cc1ea877986ee749533e3c17f24cc4dca004d0088a78526b333dbba81b596fa69b941ecb63299092a7ab4da1e4433b298789550a811fc348003cc75f684126e26bb72b0c1acfd38cb3e506fe7a138e7befd0490123af28df5163b1e7f3056a9e295abc13a503c8eb0ebd50dae9659123747733ceb01be44c2e172e53c1173111526091d3186b8cb33c327944e3ef5fc9b542a3cba53c8b894b575cfc9ba77210b3c464bd0f6b4949ad1ab3fae6ba9190802baf8b61bc2cfd1b4b26d34ecbfa1f1ecd140727c5d88148aa78c8343347ed03d43e4d662152f2839da17036727e662611dd7379f6bdc474461801d938f8e719c835b5579c4ea48e08b0d3ea9f17eab38bd0ff8139bcb3820544a38160235521b9771272f84b1f716d40d68f377d63f81e49240320c64e664849948910164b672487376d6ac0cda70c4c01b1a67a5276b77403209296d4815a1b721b2d042af07b63166d789dfbd5090eefdd271eb809afbf3e124de0839783c898868e65f297a5eeec5b0e0720aa1337171f8467ee2fdbc20c8b8b9c7c84d794523fe55551b742f3637f01b65cf0601cf0c6b2ce9a2015497bb9dedcdd3e819a38a2e9e414496442ecc72d7a5f7d56ae5fe9fb848d39dd9d55db461d905b7f5fc02ebc9f5b71d2ecc6d556cde56e6f736d3e3b14878a773f96394f0c20602650f5c7a427f9d0d7d62c0e2eb08f2bf389a3e519a59795b584e06efd0427297d4922bb579eafa29e9a439e3763089d302d7071bc760e3894877754b5c1090f8f0c6d5c6392613ce006b525a9e064a0d4848769a82d1bca1afdc3d0df20e814166af321f38bc346a2807d26cb59d9b3f23460a8b9e9e57203e43a7d72310078b015a043db5d8fb592998794b6554cb089698ba3a89881cfe7d1383a8558b858491d8f8ab9d4e3ca7a155d9768e2dc8e68cd658d4ccdc798517b970b21819e91ba6e8a9f2d4435c5ff3f5e1e631a9f41447ea4eeb196fb2d0d79d3fe8e44f4f6d533de9958bf9c5da71e1fed44184a350157d68efbcb06ff343095714a79b7400321a3c870ff04241b7ab0b202a6c0fe92ad0441f30346470ecb996704b496dfa1ff78f21a64a07c8aafee21c0687d28a4e48722dde3bdbe10920c1c3b0aa4a1ff7bf2c590c734c786b0d9011fcc7ffd9a1c480d64012914f7e1f2b337f00bc9531fa8b28e5ff3d2ae38f1ec04a46020c3d6ea25777a8cffbe9aaaf646599ace16f23f445d376cf407d01291b7602d1fb79c45c90bc96a021856e2f34e941d97422017e638bb995724b563b7c45335bd81e002121437a30b9f2d3f470aa0abbb068cdce3403c4daafa0f9e9170e9d5740daf1f048ed3f759ad330d273bb48cf0d4ed4f6f78ca4d37967ad2376ddbe9e663702f04fba0eece665ef056ebb3d908d64d73808fe5d8d646e17b648313a31a10635e47f64063883a9d9b69b91546a6699c3a5cb52e0b969d119bc841ab17ee2e5542e94ad6d90d6252c466abaab40092ca7dd267b170133ac0383fc5ae7308bd31f99f9721a07db4de591eb8df954895e21e570df5be757ac9c06f657c132e3c65448a068710bad1d24406c589da9fc87a1b7fb84974a1eb4a8a522f6c79dee71004c672ea703339591d563b682dee4540a908dd3b0bf8538209557f12e5fdd0eb21f32cb6e17f7ef4c45737fdae22fd80869543dca7132f3c5509140e62a1fd2295c0c3cdd3b8af65b1531da6740a9d8767b3de16fabfc109acdd20aacabc220cf53d65d9c15d30f182932550341886b8b1361259dc6f1a7db89d686096955fc6bff9b8157bb1ab40ca1de4d1d2c8a8586fba2d1756c20730df47b1ab17a2b75ffff33204b48f7a7f98ef8adb62697b77a6062bb9ad742a79581cddd14d796413c667fd5be9b04db80b0df4c3b4a26eb59bfb22bd6c847ba2b2cf6a448a5ea27ba89ef57602ba16b1dbae2a090226f5f1225695626b0184db0a06bc5a1945d4bd434ad920c1675c5ab98fdffa75125945d7693a2e2dadece80ed8ed32468df2b3215acb45ab833953ebb6038f129a6d5b64ba45b606f470dad2d12f98345f628f70301e28c6f41d74135d43e32f61382cc557bed5a0390972db4c58981ed42e8b3cfc7af181ad541a16509ccb79fd2b7b47c6e39108bee818bf1bacaf2bd32754c1d484e591d0259aa1a74dce2d842bf554d85d7ef281a7c8a666cfa80fcb735aa8948a0320c9a7bb59791bbef42f244d28a6bacce07f9d30d107db97e61f06e8bb772aa17d4e87296763174f0aff06b2fc11049369bd8fe94f61ebafaaf6373c4dfff80de4a51691909f58465636d6c29d9a7d82df1e53bb9b0a4d2f2ed99b7a7cc00d026f17fc65b987fd1f172e99788d85cea1c09da1a43c3fb407ba7db1479c5e34adca952828bc13bfcca3502533d9bcbbc22352999f7097899f31a149e8f60e6c754426c8864e8524c79aa8916987eb816ace1c846d9495131abb1c3d33ec3442980d5e431b30e2a5438953415276bd65c03211f10acba1193440235a9b5c27f0cde0d35491ae8e1888d10991eaba49f9745cb9d93012070ad0bfd1c9751877d2daf5deb127b922667d80dcadd1b98093c2de3dab9dccd1de97fc9fd2f3a319af0cf07f2bc6b30f50967f2733c9ede7324364497560d579b22d449b60a2b288c78dc303aa3ac7189bfac920ff748a5d954fc9dee62e9b779521bb332ee88c52264fc7e950e9b927f72802770ffe2d45983784600581e4578d1bbc1a40af4b75db93394881b843c27ebe20ecd086467351cee321114598a7c47af292fc06c908658acb811b008bbd779939e718904f9c7953dee9be6352acfb8b8730968df314c5414c2b0bd1afa090c7136d7ec6e53c88ffc99230fa5935f967fe78b67ec40203ca117663879e0853fd56ef73b731fb03b55e1655f5211805a7a684f935ec0326fc04507e9e1114f6a003f629301b622016caaa33264891c062513937404510c0986ae38c8e8c67094220508224a17fedb0fd15de02a816ed199e00cadfa535290610fbc177f3e1d808e57ae7e39f265288fb0d69b7de91ded7dd267a1056c9ec5b343cd0cc0b3208848edf6b3e3b8ba5e870f10b6d95faa24107aadc01cf05fc7c030e8422ddb99e7c83acf4770a5370f28d567db1a456074ef124e2632da7fb625b67e6cf10bcd8825cbef5e78e67fb10b17198defba1084af939a2863a0e433af60ad63dedfeebde28b6e493b4d9731f951e63c9ba4d61f34f8f85741c02f6c67299a9c0a3a65103f79c6d0a153792992b37e657f74a6f59d6a8f46fe7e069db14854827cc50ef9b7878be7ec24af1b90c5dc0e15626b8db18cc4f0b41a58fca7b53efd440c73193ef3a65a170dd3dc1e1e89e80e6f9085960e0cdab30eaa7225f6f5648493bc93537c405fd2a73b948495bd20ee232c9d1da5e1e9074d630801c066c038b74d13a8890a72c69c09981d244050215f3c567fca6fa03b192a49d5689fe960e33393d4c5e953ad720e30fcbaf5faa502cacc578bcab654f53e023c040d90c98a06e11de440f979333a4f93a8d29d9a6d6c031339eb406078551a8c408cfc233af1927c83e4c0c8ba6add176fcf01c6773d1750546b9fc517f812aa8b9e854528ae6c818e539381ccf3a2a6d7aafb918118412ae7fa1c7e8adbda6dc0aeac528fda2801dc8bd1df2911a99e9be6a085194f70929eb2ab665ed3450f2838066195688e0f0c381f955876b74aff6884cfd15fb97e9b062e81bbcc727f24b4f06edc37802b68be84e4f0f10419496a914bdccc13a43df90c71393207a9b92b9d258898f0059cf62e151061959a5f52d152f13382bc60e0f222c3383b3830ea481bd83e7a862550b76cc5f40446a35c1a7d2dac9a63f01c8ef30de51f4d604b29722065326df2dff0d024271911c6ea09e4d024748ce4029b573aa5047c57aed5d18868e1d9ab1c2be643bb92f7a62a2a1a9e1e3e3b3a85a2e628e3abbb75a48f55cf52f188ca80c9881eaade02fcc184c986f1afb9acbf11a4c3e22a5fccfb5069d9e820d8155cd998554ecc5e1d0115de51cd4779eed882a9c9214cc0850957d24892aebebe4a7e8beabb1e0ae745cd0edeaaf5fd47ef34a0bdf1a90bf3611e533430b40e90b1ac7f37bea0c47bbd6bf89089dd557cfeea717d5506e8bce3047febe8f587af00fb8654d74ca8c1fb97cb492146771048d0e7e65ab28f05f444568485c6c4e75f29c70428d6b2310eb68400064453750ea2cfdc3bd2761a4eb434530900f220b00f30afafa8fa50ff1794584e5280cbc1111abbc9fd86e7b89e9b0395eb63710e33d09b288d633ba416bd4026a7384cc21ab61ac80daa7a3742f5db8ac356392d9fe4535eb78b9e46c309310e948319d290523ca256cedfac5e3dd6e8166c0382735c84d7c8be8b60bf156b1d52b4e6260a86e3a65bbf1d37b5d055ec2bb7b28642efc5f6fc1fd96d3458dc2c82180cdd18f2b10446fa97ce3cd75c5d3b26da1bdb5fc6caa3afa3f91946ff28c486a03d76c8415ee5b4e78e4e3ad874e44dacab0b72137360148d695a6ae6f44c1a942015820fc3770af341e03432801eb8d2a4f4fa7966b91f06d6f1ff4b22a60990f8625b704ed05a418e7767aa22dbd21622108f8dfe3b1d66d154a2b26137830788ce3b0c172ea0b4c25de369117cd86cf01cbf5469a45699e0f871c11cf4fe122613269e44f843ad3fcf17356016b71c939e9cb22be34777c636af47f20fd3a36ae887cf05ec8ae605ef8f936a28838c5bcc5541042990c2cecd2afbc158974b9cb6b05883e5064b13805f8c268b87aeb82c37438f713035c0c4a79cdbd6e1c242486b886c9ae77abd5120ac5cc4c4d20f43cb136e45d3e45130122f6625081214b8fc6049f39c79ce194f00e38058d979a6acabb4d958251dabaa401c979356041d2102db979a7e0d6a2a5a0fd80a56c20e59be081298c8b0251a7005506edc09f560c3201c225121eec2c9655cd1a1a242336b46949f8ebc309eea75e625115f3eed499ac9452c8213043c08288665ddd8ec4b21f3cf527b07a63a0d73cb8589448f4e3a3595c55db8a805c4a56d62009a0a41025534b4d1bb000dcae93f515e419f00cd20117282633624b8bf31bfa995ade42ed20ec1fb795e58af64f8615443b8b76e0d0d9fde6a2378ccdb3188d9041bdaf987dc212e9b65495004dab12b210190c90cb1192b8aeb95b560a2b857a902dc74f917798b2e3ae64ea40ccddbc628c0606b971918563d1ddc93a2b9d457ab6ca6c7e8f5a10653d83473d433e89087d5b2b9775064c91ea305411990f2fb3310b36c6481f7a9a8b9a56b5b2ae0ee24a229b468ec4ac058d1b608555c22344b8a667eeb41100219444caf20e666bfc4622e72feb1e22847cf72c2f3d17ac29162df687ff202993ae9e004fd37ea949b0c6f922f7ee9ff212e4c3581120d5499a6064c65cc41f679c86905f574cfde1bfaef4bf00362bba6906d09ba81460a7cb473c6485c32e0c46a1148950277d7b0a72528bdad2031e72c6a78263c90cba40b813b3808211004b7f7b71953236cbedfdc5fc0bb48efb0351e3bcd6fe6b1aed2d486fd97c4b02dc5afa3d7bfb0e706ec580d7d7fae210c811645c700b994ffc4c1d95f77c59646e501dc2aff7a70b0c6d1161c2da8499d957aa380327a68325b7aa0a20c2dc24c17897c0ced5a57192f472424a80b29e2ba4cf44c562184998f614a537c543a27edaf454aa7c8bd4e240172eaa8e29760960d7eaeb3f4f647e986f45fbfd5fff62444047b5879f71c926077187157679c5095686517c4a946c574a1d1860194de8003626e72eb5375c990deda09bbe188ffd5cc468cb6ed074b2d6d5fe4802bda2854125d2c19c478c9f718a80456db775b54a12c2af420cdbe88ff302f029b34943567d1c2efa73d4fc34d45c13cdea7ee354ea6a7bebc475365b0dd225716ca62f02f8804ee66845b5e58ae6898127405d18f4594eb5c2850bd33d7829c51b2a1762b07846422e174a8c00c5b2a2300949a9e662201af0cf2ce6b1a2fc4976788c9e445c19b39691aecf8d92a74cc2d5c4644f7f52f35a27fd9462c9c514543ca85b262a759434d742266647f1a153de1aad497da67c96a978dc735fbe7e64d2502a5b10d46733f96d469eee883ba3b2ba92a6db6d861c59cad59dd9212508006ea7073b596de714074b024d93d2193b968c8c7cb785f91c2eadb76f9759f91791998aac20131a96a9e0194d0bef4e68c87349cc5bd3e408e818d3d970acd97a9f88613ef6732b8d02ec78481874144b6a51ca327cfdf5dde48f83f5fcbfef2f05fe67cd9da0252e9bc1047acba68b6283184a78b38ebea8ba4ad741895a9545d1592a0e37225e5536f0e43a1c94799122a6757b31becdb58c3b2e5f01b109e7e68e9b3c7b190b694cfb96eb75c725a4049b302593fbb728d24748252e6baddcb3843defb844669dfd2de02d233828a302fd1db8e60c4a7a3ca2e11cff24cabdac00131e288252e66c0926fd466377017a9c8ad6dc8c6d36e837e440a27170b23feab03407163ecfd6203f34391d4d7772ef6bc99e425cd21b8486f0518f98a66b6bf265b5667627aa9a8eca781b16d6f7fa351075840ba41125dfe2c38acc082e46c1347e36fa4830fcdaa1e4277f3da12702fce972439cdbedfa728112c1112e85ed79af3209fc65450f47741a23cccbf666d2f8c1a9cd646177b073981a3e9dc2f2dd546e15cd70a3bb98bcfea65f08bd8d3d1b1c85092346da638382f66caa6a78bea6db3442d88d7cce2a9b0f8ef6bca689ee99c5acd5bc70c16bbcf5a3b38cc63a63bbbf3402ec3c3d2451ecf1637f34ae3c66828d515e1f77bc8b7f456bd34db5b57ca124cc395234d8402a465b7c1a97e0aacd08c0b2fe79d242036cfa973b59826e0d41042d614766ef672cd209e70c09934db9bcf2523aa5596b427ee733f935bb26dfe59054904e7569e60e2a3142bbac9a0375b3a0f40a6632a93ec320a97cb314631fec14709e9aeba359815e6a7b3f82c6b673988d50b7164eb1580385fb084c0516849ced59900e57f873ef03d32a5f6c217b6300bc228e73ff0db56bdb8649240bb076efae226720b28800d123cb6fe20e8a4022f7d38b70dcabe041bbab1490497de8e7b57d823b273516ac1eea64e8dd3d82e7b783f8012409d4dc93cb5bc6f54dc5ff743ab9206e2bbbc433bb3f4068adae8540550f4ad9f6bd3250c568741f30260b3e18e1db76aa9a3afb26c2402979cd2257e0bcec322d2c2f6170a0d2f67efe2dd42f58b7e41a4efa158788bf0b99c4838515edea020ce9b439a0899bc7aa06c3154a3b50a43068880a9ecc5080ed3417c89ceed4e8cbeb1cc77c263d0edc2c0893508e7319d72bebdd37d11f6f7117d17147c78d24185f5da02c29265ca1d2d1b6f9be275186257a6f5b4a2b22c91284238825cb661468c1ea8647266e7723f84c9fd537df45188ac8b28bd62d04ef94a554e64160e144a48f7151858bffdc96143752fd5b36b49b1a4dd4bf4f8240a3ce91f17811b5f05dcb79205bd53fa9b78de382de411785bb9599473dbdd60a891f3b1946c2fa420bfa7343f87ef5bebd8a7c85a0cd48e76383b5bcc147f04838331c2c32d17e76a26dff7bf55c64c0933eda523affe5198d85d90b55654cf544c287fcf6c8ebcd56f108b3f7f5a7c348f0c34c51c4f6f9cac2f9e4f8a5b5823b0dec04a8673bb202ed9a0320cc85c339140fda70996355824082495f0f745ed2764e570e395cb144f52a00d88f08855f3ea622033c02d8ae9491fecc2c0628ce64ea65a51a43794c4e19d4273a03ebe5efc7473da38537b82183e38e95b8b75b1e837db538f6552d190380c168dfc292db0e65f66b440a5ce5df6be883db7c87021e2b2a2d9feb09d26f4fb0c71247b409f626121f341229059448a5e08e612b4dbe1cb575acea3b92ff75de27f84b08a6af5234f5d5075135474c5b9ea5c8c086186bf51fe431942226192036497412845dcc738ae914b84d52e3858f31085cd872e3a52511ab72f8afd8a4830278316717cbc03a6ae2d0c375a12eb89ec9816321e36d4059c80fd92f1271161159b10a2f7aa12002db0f524bb846c4093232d77b82fb50143b6be04aa08d3987e02b6eb2acbb17e4ecc9c89baebcc191c068b7ba87a8938947df11861f3918bc75da83419489a4c2f58b29da3e73a950ac2dc7cbe0d47d226a75dc755155c27c266feeadc5b43348034a227529217e7b0bec9b9d8b6fd78fc21602bc86727c91f9b6b6496aaaf67a3235a15959daffdf6b82df1fadab43b0e3f0bd8a23ef729a5cbc86ada6047bbdc2e285d5e8af80902c9caa9aa59251d2016a702c3a3010777d8afc276652399b72902442333ebfa8c37ab9ab3b89c54f87f26c7bd51b12d8ba912f843c149eee4799f3ae9aae884da7ebad2e8b62a761cfdbb2b96f8ff1afb4b37c46333a9956b19dec6183f51790bdd28b1d89e0b1f2a2d6c73b04f3624499cc92b75c1d039f7af981438dd076f9f36dbd800942c9eac0a437b6706d0d92ae206e1c9eaa09c8d2646c7b47b2d70442d676051ece1a29f6d62848b523d1a4b77369e38f74710dd19ed89dddf84886a98e1740e99fa408e5870377722bc047e3a3bc86045bf0f91f3a4f7141e2ce83c1382391cec4b5f7b88f6e69fa3d800581896acaa1c66b352086db7f56cb4c8c448dba02c5523bddd40757254f52bb52bfc702059ffa50226dfa5e4969c06a9ab0037454c78d727ac2996c6a624443cbee585489e678ec42abdf91dbfbd2695b79a5241064ea06d95296a8e9a8b1a5a81a1155fa0a2ffc0b909740235e3338f0187fc9ce24a4f2202adddc23290e87e95bca7c9e8940a7de1d0bc9b1bc77737a5e0109fbba7c172367d855669b8b4ec4d0cc7843fe3f124a1ea73620de99f7b96d5e212cd57bbb5457fe258dd1a790740b35da444e05fcc6f2180d1b91284d5d6b756f8663b14a422bd19c037ae23a8dbb49b1e3bee8fc0bce85686e577d0604183408ce3f1491b5bf3fd07091b8097c564a5bd1e99759b05a6ac3dce6640423184a04e9f7678c26c10ed1e9eafa219310a64945ee441f63520caa05f2bc37071c2a224c63e44cc2f9be2ba532886b1c779c096e0596f154d4a8a6fd4a6d01945cad4b9eb08a13af6c455f43de3992ee87b6a7aea9a23207acd42f6245d08d1a0460d17542ecdd6cfd2a0ac6c618339dbeab03fc246286722c3105a88dd71cde64b995ca85be20470dc063a8a447af16ea7fe8895edffdf88b71914013761e9cddcc0aa611dfdd2f41474a78cfb5e7fbc9c6ab8783b97c34419106fca13f96b7602cbf33057875e70f08ee9395f2608d783b78492cb43fa2e31bf58983c5336473dd9dce782b38360f36f758b816cfe126e78cbb3a3843ad40b22013e83c69fefda1bd42a95e05790a46e4948fd52cc0675f223b0d03994466e02d487a79c54047d237e92a15df38ae330eb087894c6771a47ec9e3c173b4a61603b3d5d013d4242db9a8a624ed7814c5f9aad001977a0a98427128339c9896b09d4d8944196a5c6019dd838182c4da85223e67c36a9c9832d828b1da9e503f00345c143719d85d5b8c43c374d5e410b68fc3dcafb8b9de7a92a40ca557af86eaeb1c9d09730365cd0ea7507795d964d704b4d3a6eaf495e80f4a5d452996e86245dc838428d5abfd0aeb001483851c85d928bc3f42051298a1e23b3e0dcca439799b9c084d88ec5be5b63e05d3668c02937e2090b56c49662ba515ad161d46e83e268c94a8a30a0d682a8d20587c4f3b4fc48769eccffc942a0d48ab8ff5c114c9af3f3872ab61c22ee0b648d72b9aa100ad18bbd604daffaef6f77148264b45ffddef0a0e67f4094f929afaa3e6b37de0905bfd27ce8559f29aba7503d88ed4c6e00e55e4c53ab62b4078985dc710efce1a0805871094621c2c2f60d96a2f1ea201ee50b9ccade0d01174cc15686b8ae4b97a0aba8711072742820a306b0e37af509f0005d4f7c5e040f49ecc6f2dc0aa76dc81125fdde2cec1675539081739dc539162ef3b897cfbb074704fd5b13fbc97be07077af47f783b257066be8355d2d007b8b7e38e90e900b5eab297ef209150260184a5e7efdbd5b231d1e3eecc8e9f0234743f96126beaf0b6a261dda493322cef9d6cc1ec4e28e7aeb1fd8ac488b0ee428878e4b8f4eaf3ea3422fd04300b0026674b6045ea8cd64580f597cc2b91b50ea0c2bb1e6f791fcc3b9abd2b46b0d265c0f10aa0ec20a6cc014da36ca38ea1f351e52c93db0e90fd7ba8dbcef8b4881cd46224d0f29f69c4fd311efc8d315b66293aec8bfd11f6cba02a4a8e73c6b3f5d3e60216b5e0b8d21ef10264c969cc582d669e5d8d1a6a363a8b2342b78ad193d9117d8094dc05cfb96ecf792696dc2b928149bdc778330411ce434468b7f10941516df7616e6b45291f4a7c17a7c2f68e62bf9a8a8d33e60e189c00fb2a90267dda88f935c2d7508e0e5146ebc9aa5091ac23374114e8fbd0ed0f31a2a4e8fbd0cf2e3fae6e2d876f11562b88180d8d331f51b5c9ddf8b06f1f0db0ac7f5ad149390b618e13d7ac6424aab66ab82fc42c431d1db93ad6ca5fe686679989c9db164b909e6ccdcbe4ca52aa98e778398723828f935bc9fabf89a8a13cba84dad3eaa50e7e64492958ef0b864b66e3a7d4a2eb5b07524c1457b2f5296d079fec9128d2111d1345d072edc76f3213e6495c8fb5652032af1b8bcec28f83dda044e2507d60774f13c47afd69557c77c6efffe9d9030267e9829a7b493ce47db6fcfedf350e5713652693c113521cd28b3a5d60d2f3398a95891dca915fe2eeea4d32e60cce335e9756b71d1ae5e6fe8400fc6c0637f5e6b8b403f4ed49598bf8118c55e73042d4e47ddbd21d5271921993af12584caab82ec917314c7b1f7006b95d657f004861fb4430cf437f23981c3dbc59190b64222a9e0f8acec8d41b46cad0dfa29eb1b6a047ac088931a537fc6e27e901530634a71aa351801fc19ae07796a10cdc6400c2304cd05b876e17bd381b9647d711d507de7f087a9696c1f0627edf4fe9cdcb17f1ac23038d67cb365026b47b88ba3b5e4182cce56951dd8036804c2b44b68509f609af6f5cf43a5cd5014fc5868848a32c65bf11b8477c8df9643f6c6c04ec5736c11060df884710a6bb518fa01296d91768d2f331b381d988a8f955624d9a880b2e1e46e46ba5a3459cb5cd61eca562ee69ae5d0bd1c78026138b680c81923bbde069b8822f0378882e8154e48521c82be68a212509a73c65b3359507195df9feea29d1860869445812426629d094f433d874740121110f2b4372ba0c77da6c621bfba4fbc402d58eebfec3e6305d362391b217f5ac16a176298f6ef79e5c5a23d98e70d43550999b6543c22fc07e5133c639994443130fe0945d091b3abf8d12b62168ccacf2e01a4ca350a6e2fad2bf7af8b8e990bc67fba038990907077ef0ab9170ee960b41fecafd09370f80281af6c0b56a456d2aeccbf32ce8ac1944adbc54c2ce27b2421b66bfabfb6bce2508fe0970e45e2014daae7198790f3be5aedaab867e5d4c83065a0a0dc7e8c6990424095b7ab5d6498ba9b253fbdd87fcaa7d2747dc489a098a71596090fa4d5f82730f00206b8283b6cae659b7433c5ddacf3a02230c26a204aa70f528a8be3e4321793d2b475daf06007391aff7cc8c62b2e8267425cdf45966456100db536ae4a1f043faf83df266d58253a828941631a48d6a7002c4c1d8e0d831b27bedb46fde29148da691238169c1ba0d53286e6cff1a97397c4f40e138c418c21113042a545da15e2cf5bbb75a76c24e6e11f5b0ca59e2a91ff8fc07976841ab4f55f7b805082c221add42a1b77f01f10d5c75cb7a2cda659479d4520c163f31865b30046ab2414bbbfe4988200d5020b07e498a94a5c66074ccb2028cb00b89012129756b63c9720f977bd9ed7191959e8feda7f6de37cd8077989a1c6dcf5badf976c20e39e9a1c3cb3a95730f2d96e1300031823164d2d33803c62ff97de1738cf0b4998576dd96ac378f65e5a74ece2fdbbf4b0d98cb9d1d2a100f6951a14c1d1c08e5a85401ed60f812e8d7e9f38c2dbf9b69d7b818b834d7c0adcd5277f253feeba6c7bb3b85599a3c55b3da100d96a141e7092f873d2800c220bb0be0fbf6ffbed9a002a10123b43dfe27348abb7ab653a9bc590fbe299c404a0c3a2112a65c17a594689d2b4881215445107c59abc4598a2cd9aeeee11501f40184453fca6eb936fedeec031bd9a00c3a15c3ed58f9ccd54bfc41dd4e6775f3f2eb1c416dd6f0cf1d5dd92caec0cdf6d40b50502f0549e2bd53c472930da208226cc3c4bdc7a571bca6e7f1891b24a677f03b4bebcac6eb27e2e254b2605ddf407a782d60d3f2ed16939d974fab5e76d45713d7423c2ddf4b244020190e64c5724506c8febc79b5be0a1bbddb2dd232be05543811a0b7f9ca2bc020b5d0bfbcb51e160f6a83b258b84dea23793227c1b930fd8d816b92d110aef91b2e4e55c71158f1a018f70d7881c7ef623c491ee0b910fba5227095432508f96ad4bf636e11cde5c31366bbba3432cff414e32f715806069781b295a9d7191fae547c41283b5bcd96234141ff7f542d81f8a25d80e839df60dcbb1cb9546c535062d941ae0899e3d708747180ef4a96fa6b5c8a0dffc73ec91077d115008443430354243c41592b8cc58d7815ae0f7830303a099132c6cea9cd0683e7943af3b14267b7d37443fa623eb79f8442201c02493070c0b16d21e6bfdb4efbf9a0ac9679df10f374ac8e1d49ca2422283b13e903880b91cf231bc4604e08120cd2b3424858f6a24fc4a2940ef0a78b91fe2f74921b2f92c3f1102a9edafebdfa1780bc3eae1d62869816e947c106a9264808be0d42e89708590aeb0d8f2c264d507ab5e238dad876992afc73ec58b196451b754717267fb1c9b841f1e3daa3728f3e70998eeab553701d336d4806c8ac12fa2d3a38f6bdd0aa0f341a5ddf4f5deeea60df95a91f708705229ad296aec0bfa8689d9ec50db0f8306d9244b559b7d935e757751ea7b9b449e19307329942ac322cec47f45ec101edb2017f1c6b1cdeec5b223a3cda1c0c7a45b73247f64a2576273c5238958322f3d21c2822c2098d8e60bcf1f818aca902647f70be3bbc3c9b9ea07083408688d46e0f16043691fcd562c44a79c20adbcd95ad9cf1d774f0bb42382b2d554949aa85adda5fc9ca4c4d8762e15cc5e6c7c4b5d0786f094bbcdf3863aa4b1270c549e5b4e6a73b8f96488c8440cea52d490a5ca6141a0b03d776eb14f0572f5af537b4775f16dc8b2c8585e601bce28a9bbeffaa0977d65a9506b264260352efff9dd6684fbca1b434ab2b15ad2a704f9c8258deb13e146aa44360453f7d256c20a7ab95c4305c7b39ab9a4d7c8684a97c00de7893a0e5aaafe328845adf5ab7397f2cf52ccf3983904f617dfb7abbefcd1f4555138ba50106f6870a678ceac5752c288615a60517328ad4ea4d41c44c3863013814377322c7a4ead7a287dcfe8bffa9c1b393614afc6bd776ba5714dc0360cc40f5a576d1d406ae8f15b93a1284be9d56f2c7ec381e97be718adda762010b46da5b743979dfd3b7c48d152c0371254bea04b04643033f9a2c5c8d213a7ffb31cb60869120197905b3003df0ea06b4f39378e7242b5045d0c12442b569dbb41a5fbd27871f234fbe0daa2b8bbe9a7532e95cbd4dbb61a448d99bb1d7def0d6e737ba8f8918cda398c6b66c41cafe1a42573caa8ef39ed0b32b11ff4899031a657c8daf2f560947e93b01a631837d77e15b099513b581adadd6d23447e8bda27947f347ff6993e8a2a3b35347344d994528f8611be14365bf6d4790c759cc241e37e43f90d37f7e9076a903b5f73aacd51b5e709e2e63acf27e4b8be55933fc38400563eb30970b03f589d2183522b32c2ef22eb4f7c48e8678537bb9d5c00a955e5fa58c4b6b828190bc38525879ddd4083b79fe2d6edaa9b0478f4b1bd8e50aadf12a3dd2f423de98345a49c52862e5ed89d827f9f0ea9ad1986d4bec01a72ef9973f6a2bb171b00f99980588fe2d70a360bf1617c30ccdcf909ad3aef8c95a9445d2261bfc9d9054c6432c16901d1bde28707a41eda70f7d2576ea9aace6971f523e2c4a31b0e885a5a9e17da0516d80c5aed3a95898dc70f5732e0bdd9ca6e0578e897de20368b29ec2fbcb5840673299340d9beadb3521dd537a034f1884312a091cd3ea219cc4b5e4da3e7c665be54cd2b195e3c9be2ffb1bd36c9c702512eda02ce25cce1bc939ff2843e8f5946d17d892b927220a8471fe595250916006637c3d44962485558553fd15381d02906e8167a490c8705d5445abacc2531663de3990182a1de9b1afde044e94306ec427bbee6019441c87d4a2ea014ef1456cab915c87c8f1d3107cc5cd971aed0ea34865c81421eda14f94cdca1ad28c6b8b6e516c8a7b10a2645616b2aed3a32bc591ccceacfd20ffe111c46f9ea2856c879e95b66140ae3636154a101b87023222f8726ca07e64fa6246bc345bf2fbb530eb8830cb7c23a8c0014e52fadefe644e4f47ad9e439dcf591f033e5526c28631abd6aba5828ad26e0e7e7dba5e47d1197163168970cccf9797e43cdc345e4d1bf45359ca76fd2692e699fc3da72bdd9e416f8293a11157b4e439bb36dd8b72f3748c7604cafb5c5eed045a18e576b8f0cce353a047b571089b63865d1fc341bbe6eecf07a8531a935ca194eee0a0bd14cc88f6dba1372133c8ad9946113ff9eadc20c3f5cafa323934303cf1d6c121a7fb0cedfd91b35514ed39f7897f25bb8d4d101ba9e9009068a2459bfa0687d2ae2a549933d7d56cebc6c1f7b8e2edd8b05275660bf2399f8385e6a9edf312694f4d95f4f4b47c86dba619b3bc83f871c9eb24aa33621bfb1fbdf5008be194a8b8e02cc119ea43ba65c8423c324d92fbca73e64d0657448652117ea7525fd30222180cc46be7b9842342ace38733832f0aca0faa07d07699ac0ad5463d858710801a54152aa356ca24d8b9c78a8a8999454943a5e7c2773c7507b7849d868fa8e82effb955e22698eb2f5efd83492da1ce5f3caaaa3a95554c68acbb22168f5d01a58670f7971e54ad96ce284db7ea594cb29241b9c07160840aae799ad76537a3dcfeead92dd8d408ed7cc183ca8ad20cc5c426f520184377ea16fb4bcff27d4c52a82f92322200e1a5886ca6de49e7c9ddb81b1ecb761d456c973c015a476f5b49781836352692334198a3931da4b36a47f0ace1daed2375ba13f0a6e9b4ba49899eea6a9fd4dfb6f488b3134c34eb3f76173291c1fa84201f054b987b84ad331710f41da88e1068ddb0170c99b2bd3a92c5ca92f5b1eba3ac2ba59e2e9d254a2585d62b888ed8b4135d4c2bb4f50e891538991873abf481758d7dc47327e52a32c96135f91c301ff64e51ad8ef107a390966e1d81e6c7b774686d4fd48d8457482e4e23ba78924882de048d4b05ac3cf230cb48e5d74e202f906936c174854108fda954f8af361d0a4ea8469912b833e21fbc2d0196a2d84d59a7388fc7dba07f895ef6bebd6710325d82091745a7a8d1122cbace1a173e0ad937be339eef231c82695ebc75c08161e5753795a225f34b1110633051ce801c3628b4085c17af6c2c123f8bd1124858ff5806a315dbfbdcacac8790fc8a916cfa5eebe2902df7ca981ce97d4ba345e86a37b42469ab1f0c8198da13a39bd5270d2541f307f1da3afbb7916076082a1ea99faca5f149534c2474b561f5e44d4d88192d0506d3db03feab9ea7f20bee845db13cbd6df587db956d89aeb02260704b51d054e8d0b4e96bb0567e0a79c7b09268e73facd9ad703aff7e7e5d55a78e8a9c6e220be14119b7ac575a85a297cfbef3044e9735e7ae19c3300e2b57f67c160d921e510cde4f1092c98c72c013c6a1ae1a46d65b82ab5807f4337311c61b05c7150f10915272931e51adb084224a47a2b94c60051c9bcacdd76033fbb981671a1c51748d372e1fa28fd84bb7f5b2a031392d4aabdf25fdb5c1f90f3a0fd4ffb09a33fbd47bc600d9b3beaf428c55050e1b2b4fccb609e977a36997e807afb2e195cfde5366f16b7637fd40cd6104739dca059462722bcdef52a3b41ac60eae6fdb00e681860b20473e72b44a4198977aa2dd4bab341c5f1ca27046198eb6e16d0d8d935789703cb179457d2e87083529e970caa6dc33f90c7509e106ee076c2345fb1e547646d0412d1b313bbec3b5eb811cc5cccb30d9a2b778351d8816e14b8f2dccf9481e25758c3c7aac44350d8e1cd2f6a0d256db0c8de39fe5cba6b39a495b8685be4bd16e4fe3f4d8f3692d74f1f65966287141caefc80e4d444e71730e63a7a584d7128425a3c293ee9e440e1234bf8b42a6b4048e82406e399ed206993d08108ac46aec68391d89daddc533e176df54298abde425270a7890602c9fa0dc2712036d5a78742f5da6a4cfdaf4c91bf5aba484ffd6e33d373b634fe5370ee3c31886008b8d77d607ab428899ac79904f60f9df099c4f3284490409d20101c46d838b0acdb3fc8fa4a187608318644d870b5978689291c3f2515a6bfd8ce809c9aeabdb0e48f497dc268a8130297823776451eba6d8eb2a77597130147d04c93d01cafeb3be2156483e9a3e155cfd105d661c17fdff186bcd43dc0bb4e4991c029a66401f667caf50e53ed1f9fab1d4059cf13c685b91727e9e85e98e401c0fbce969cf28d8d0b75a3d5ce28680ef187093bf2204b4f03dce8b400599a896122b914664eb7fcc3b9f16c02543826ed1079d0e05a67ad5c82a01d565f8bc72f2017bd1385c780991baabcb7627bb4485461910423e6b0ad93016c1c1e3689c951942c934c1021aba6639cd6f9844532455bea1a5df69b7be5728cd0e4240c41f27fb03c21fc9cf99b859c729ba81309f39344d587a84fb7da5e99069874568ac4b8d196496415620be1d9e5b7aeb743dff7bfc63c915808d573c2faae228a50d7d98c68acca8d560c25ce65b57ae326e95f9a6a6accc6e297271e1047ba621045d050005dc0e96b1f24d0e6bda41d66568ae4a05ec909d703524b546a7b6a7f19cec34046ea9c1186d9883e1e368bae330a95907333bc49cf7cd82ba7bae17a1b94a84c71e9ea6ab70dfab57ee3bcaf641c933ac2d42537d26d03802512c2d5cf6a76baae267341e3db9ec765b74655624054f103c1468dd73d88258b18b7b3160539e071e404859d9ba23cd9f9e9409f7917c2118e6e65f7c82aab21f7a16f87759934858af17529a9a24d4736477811ecf388f3146e9358fe63f3fa8d743f5c8b53609ada12442e4f4221e2701e1a0c502df4584eeb94222f894f8ee306039935bc42eec0bbe94f01300afc6681439be0b081f6c75a02d3e05ef7a93228defca1a5f1706017930447d932e12dfadc17d82f1d74728d33712c84d9329accc2c9c39faa37556b9c4cbb95ffe45ef0c70df16417d670187f6b950e9097752ec8b1e43df26d8a980cacc531db0af6545e4c1d211349646a25755b3094cab73319c9d4e5e07779ebf9ab79c5feae3119c7883e69e6b02a5e1babee5c903f02e8f89a4b84bfaa0a2d9acc92cd122451a3fa321a44ba32364556ef672e6c3ca85bffe5dda6cfd7a73b6b5bca6c1a913fcd887c70bdd4b066bc527c1ecd148af3acf6e98dcba51023300bfe18a71bead7e996f568bc96a498c7bda9d967c6e1193ec498497a9c2542b7c4b0a30c9b411f32f4b9b43414cb8e7756e93043cef6f04a9d00b34222f31d34ddc0cbdbc05c76350f89ca247bc95f257bf907da81910cae6f5fa786c2a7329edef72a0cc3a7898979c63618bd94335770bc68bf88db9b40b7fd18f19422994e3f4b918142967a0265f59cb6f849ec2313c7c79eefe45909e932f9a61a41cebdcd4f233de106e56e2096b0030648b1510a78b63118b84a5774d25e1c5d824930b325a608c995569e27a5302a9283621129c2e64f13428b354d0c3e43f4e99182c8e9353872d6a30c7e046c23696b60bd33504cb56403870811d1bf631d9ab3be0e202d3c60d18d7d86e463b2e2bdf8c44869fd3101da6e14c4dbf646b7c4673f0bdb42aa5fcb82c06b1d31d6f7a0752dfb5b3d88b0e021f50c0b65a5bea217b426ff15911f0fe458d2ac25f3ff1b628e1888f502c5467708fdb86a5ca79037e0b116a17ffe1d625ee5fbd5e0bf84c51bcb04b56f38a0bb55b16e1950bee752b8507374f51b809bd18aaf8c3bf7c4b07f0b23c4011078a7838fceca7d0d06ea8d47321f0489051d3e4e7a1955f0d2e0999015674446318ebcad9f3dddb8ab06d95b0633a80359dd67826526bd66384f4fadb808f47095372539cedb01e5fc28e66f393dc038318b90a18bfaa99e08924a1da7c00c0a46bb2fff5964a80befe2542fab305fdce0275e5264d1f99ec48fa6625622318c59b850adb0d928145c36c80cd3ef5c9c7ab5307a2d7fb8bffa40316632d73da416f53987ecda087828435280405fdd22533012c45c70dd31a857f59163a01b6b6a34418fddd391e005b4826d90d04984bc5dde851c44a399446022291cb309eee8fd933d8db71c0fad333bd155a3d241606f338d29eb35b41ecebd6aa3555ba9d1d0b8baa745703cc0c2785532d21a998e41ee0f293c0e6b0779f0b1fe90fb4690d59fb3f5cc975ee513892055b66fd925fdff606baa74f1202421da509b85c8d87b1ea5c01e1d83d01404dee1f3e9d2c04bd6913ff3f79d73e190853189b25cc229473bcdb78cf984404e410b9c4b35e96891459f6211a1e7cf3ef0702123e427d92b97680ae9d2b32998014ee946b05315a9541cf21ee715ac48840a5941a96ad0989fac4d80e7cb72899afcca92800f461353ceda3818b6da107e7d618873fd44e05e252437c1cd81264739f24384ad6f23546af88a0c5adb7c951caeab99d4cbc5f15339c1a522eb9e33c3de84cdb23e729c8e1c0898e8e1350741cc3f87f23a39f462c5db4f4531d87d244948376108cffb31929633e747052640559cf54f01b9a8b4f66be7b96a9aec3034088896e01ab057be8c8502ac4d228a00df620785a3fc7bf0a423fd25815245516b467dc703f2bfed0d171586a7c95eb0b586c1119da8e2530f34f8ff2e02a19a4c95587a9cf2fa07ebe94899a32fd7e227cd9de74c9444452f64f8cc1900b8f95b3da306875b049320480d95ee47fc4aacaa0c41340c1a6bc53e775001d3749347bc2cc2f8b8853f02133606919ba517d860dcf0e435d6ea89b24b5b1e8f28b622071406d40d6a34e5369548ead4800a24005380cd66a54e4409c49c322899ef718bed8accb2ab23856a2ed61e2cfecc995f8dfe9dc56b560afef5a5d72e35e88c73ce033179dcac8e7c412263560e1115e769325b681ad7f323a0f9ba3d50270840cdbc01bf220a92e8ee0a004d98241698bb45c293811f6120a36d3600b16b4bf288a373fc7c04ac159f2771e728c22a733e9057d4ca28a7cc4582b802c714e9e42d57e3581e14f6b6c72026d663e5e305fb4eae3888320517099701bf5a3b02b45779ec6be1d2c01861ae2b9d91b3fdba1c395efa640916d9b5c53881eec63bd60fd86a4aeede7c88a9a2cd130311256de5309c918cc557570e64f18ac3c63147cadec300d8d1fb264ef683c5608b7975403073c35ba6ae67da9860631443818da22149af8e72b7f765fc6f900207a7c1123e28a7cf94319947e9469422902d4181ef9cbeb206d99f44c49284ba6bbbfd30b683fa45836ea99bb0296fc2b11c35c708dfacafeae675cfa7fc3db3caabe703376d8b2e7d3540c1e8e65bdb7672bef08dd240e78b6f08bc404886d0c16c1ad251bd387c73e84478a7785d09e68656c1ae556ec9a859a8d38b4bfac254624a42a691bbe5b54c825bf99ae531ac5966cf86d1155ba1ef404bf0f1c3735340318f46049a2115a245bc19475660e12997300061f51482951cbdab809c887ad39e4336a9b4ec60d704a9d8e1ec969c6d1c5eb6441bf50a3d15bd1e06be10af47b82fd2d5cff52853072c228bc867cecaa62178d1b6e61b4ea9b9804e9441722e20adbb162be68ef3ed35f403e55de35fd3f73f0b402428b3376950e155fa4265cfc61c2f79fde785e8a9522e3879fe944c4651efe68021afe71380ec658c3592dcd5ac87c7c732459978f1cc83433c4b03f05f7cf1a0b00ddc2b415aa4ed1b0afa09b66cad33200b4b0e70c7b35418dc377e2a3aa8bd882c6958c7a087086a1b1d3b9973907b7800f0b2e4bc29b0beb99da4eb6451b88189320f1032b50eba63751b496a4eb411b453f05b764ae5307140c9326a215783251c667822d11f675e28dc141dd949256416bda58f0da812ab3843f6e3f28826c2c84c7f1c6941c3b282dd280ba1a573b324b67350d3705a8126e6e525d8e45b56aa37a5fdf72e06cc25b16d0cb206a6c556f24f9159c8e2639294f0fdcb14f0e13d6b4c563fe5d4fd5f73b7af30d041c88e8b3b6e09224876e95740d940210758c169177de7c86afa255050c22090e99473315885e37483f6735dafbadf0f12b2c0beeccba2479283a099266bf65a8205e7a848b2ab17339648dda64c118aabc7125cbcd8d402723eaa36e969d7d5fafc64bc6f2bae17c6f20a2662246872d495ae3a53c9ca0b25585b2b46f4b09d971cafe465276da79ca0f9de5a64a048e30af56898b031294a964a1bf6a01b0a55af97bec822a5bbc21e115b3a60a70fb04a0833b8efc8611efd0ececb633b3a67113fcb0ddaacbaee8d3f64c04937f2e84dda793e2970ed8caab54c7418ce01d1eeabab5c063bb33ad9e53f9391f7bdcc1c65317cbaaf9f42b489bf60cd651e12bbdf2c7849c175bb66c0369fa67b0ddd52541bed6a647a46f258654c26507c153513ba0fdb599f107701a4acf912ffe114ed3495a684097d949b2e0f8c88bfadf4d5d0e72ad962d519346ee986bd8a77b29cd38b654fac3cd6d8a5e60064ca9e2b135e91841113bdc1ac269e348e86f419ba8e650497611263667c62e151234cdd514a95f094826cb9571e3a97cb0cfca135f87280ce499fba2307e2bc9bb1c218e04e45cfc92fc33e74db0cf790709bfb9a26425a98df08c459ae06bd53df2b4a0a24564924df213b126b50275f580c09f2eb74637975dacae50fc81c9b6afe637dbc4164119eedc5c63a3299028a65e9eebacac977d6e3c590e9937919ea5d11bc7b678353a15ef697e646036d38892336fc309711325fd917b5701c4414ed520acd7d09bfde2c10b90fc63ee7351eafe05f32aed700d7d68b9b45bf2f324f0be87152e720ae5fdade1e1c5d725834e568f28ef7b813eb420b0570e650af40e653cc75a9705f9abe6741f96973f7a942ad95aa26523f228498e9e1c5e38d867815d2270ee19c689b88d2d55ca757a7a00ed3f49642d83a1b3daf4df515191c842e95421d722a3bf51e104d3ac8fc8df6b8c2972a670b94ed19831f09aa7958f8d6447ec2ab821ab58c11fd8fb919a68c2880c5ccdeeef9cf5b385c014b5e3f78277bd063bebd4ccbaea1299a9a872d7c72c1b359605f8f46bb92977802e08d36590520821263cc4d046dc0e8a332a93d87fd8faa897e23f740a0bd618676e4490c17478231f1849302ff8c9c739cffc9d6d5d543c0b12eafe169d185574386a55952d18fc9444f59acd37e60743f1a80c78ef47cb92deed032b56c269442081d2e2cb7062646f2cf37eaedcf6396ddc1d871ad733a154c8718dee89807e8eafcea42ae26a1beed0096d779dd3cc00ba4453398fd4fd7ae226387c442590d506478d15038bda42af1a0d989b6c2a1c482a89cc3bbe8ea292a77b0fdc362397673b7ccc79e00a155184873b0ee8e279d381cc60663a19d282ff151f1541226c9d03ae24001c4c44d11345f47e4d61576f54c00f152c7caf41e5a1e20c3670fc4379080e24aa241e105c973c7f68dca8053878456760b2d33c6420ec210884153e881d19dc807cc94d02cbb1cadaea9348d428120f8fc689ebf58934f387c7dadda3af7e5c957e9c2cf2c4a0ff44fb33b7ab420f99e8428e7c2d7943958818ec36128e857d44684a0b93ae874964ec50dfc4d5e80561cc3de562257a79bae352dbcad5ba708aaff00e336b67427cd11858ae5b44fe74d5da52fbd05a3f5e2b9e17f0cf9a2eba1445e85cfabd221f3845854aab32d9cf15ab05593b7d48f448272ac88d691026303b54a26ecb53a82971f38b5e0d9813a483e7782a2c7d325a131187a3621cf74a735f11807335564d0ca800da11ee39efb1000c88eb7fe10ab213f8a193d4a52da13717a17478203bce4736ae33357a4699cb0753de96c280ba67eb9ec215aa77e06faa3589cac38b898ef2b7cd328e2850e24e7f67e085975c3f8c6dc5c0aac117897c05d2a50342a69f6838e07ec4ae108bc25863d097194b51de33defaab723cd0ba07426210254de7e16cd7d5ee5fe1ae4e3f039bd59c585650233b856870ebf268bf3ba942d9e3bd39fdce8937467524bb74d7f2ca72225e03898c48132b6e79a084ac7d06842a2202b5db124dd68fde3c1988d5edaeeb8298a27a84fcb3545332f0fb29a9ccc951a534ab1fd8904cd8ddcc14d497f3006906ae6e73a844b6c012600549b5a41f17c28c51f08f96042d9f47ebb1b676434a32bbc1035b37b4e5fa2a5b0d5ea18565e3f76681ce68a9c81a89b0fe37193d0cd0d0510b801e69d893a8c8dd73b5357ea70ddbc561c757a78efaa33a1db9b973a35545d8026a52479ae48061ddf9bffb04a80dd6be6e9b3defb123e289812aae63e6df8b3c1293e997f12c4ead9cca603c97560840fc43f5d17677bfb8d53ac07a719c7224fd33acf4b10af037261e61b378a2b86ad22640c5b96585fb65a642113431fac9c3fffefac60728e6e2f82fab26cbcd2283fd64287dd5951bf307b5acadc1007717c68e1cb0ff0e78a1e68ba060e3153224165e3da9916bc14ae0db4bf17db374e634e4035503464388134b53257f39a1f51cf2537a71e4a21814b26fc039342dba4c276ae33fb0de1cfb1571f37722be81f18c452472e46d1b8228e6eb269fc19e95ade188772456716754f9704ef384a9f2cb3a8fe9374002b4248e8639e5b4e20dfdecabfad60e3178f2bad4d986b9cc7d538757e68eeb12bd5dc2d25dc9f872104c5bf01969573f13232e220e033c0ea0a5732b8039b548964e53ae597935fa2c19f641a5a354a4c47b668b1d5d01c5c83eb28a9c30a74b737e3a0e8767088d13ed898c33fce1c74f330ef21b6d3ddc56f5039648e259ee2b685300ff5bc789f6ac454cac6c1615ce1390817f1b1dcfd320ceb8912b7253474a312aa1402f69804f4a358b8e9bb3ee3842734f9a22c6396e3e7c3f9aa8cda1c9b28efa35244c134789dc16ae9777bca37c601a0d7efa8a1bdbf7a9a6d0cfc9c0769f82a098f1a781ced5393421e4ddd11107cdc65e8855a2517442c4bd27b797ff9687052a50cc998e31d65115e6f308be998923434ea1993ed7b6864fdc41429d4c50b04a38c33793426e8cc8ca33653aa163cd8adfe6981883d55da97d54e1e7916377d9db3118d768955cc7f24ff3a6dec90bd7c7af8bb83da5dc65035ab126253d3aa042c2f56889e683b6fde58936c064f7cf49944e59092161b74ca68f26982e2daca278309481ae9d7274d31e8159613ad35cc4d0e6036cbf82b3995018c0ad74d1b09c4bf2c69e7b896856e9c490d6cfafefadb7adf7f87aee3751603b5274b835b60880aaad994f0b428024f69257a9c1ce2d00dac506bded97fd44cc372b4b1cc0a24d0ccfc8989655c960a993098229b514cd586ade54e370979c540afb55ac75c4aeff6d99e2bd8029b2aed07be7bfc5069c7330a49b333a1433aec64b9668b4c843dd53db1bdb64518251c29d6663e35449faf3ed468b602aa12cfd51136879cd305da02678a20b2ee533bab3abd058e1cd51d94e7b9556a50cef290be4efc9d0dcd678bea4ee186296e658b0a670a206822368b61c961f5e5f403710d985f93de7942343590bec28b13096b534ad4cff78df2cbbce861e17ea28c449b13b9bd9c5fbaa77ee5f4adb8479034ffb84fe47ba6fafd90d130b8e55313851ba895bac79b585235af3041dcf78ab10047492685aa78d9d76b62e1f9764d83efa67b16083e16bd06b468f93f2b393eeca412c2419deb3eb158ab6a7602393068b49c1ee2c8851a75115eb89328a52ccd604465b60d090b5e366b00461439b2281a66cac60087b641000cc10ff6016a8d054106b4e07ecc91a540c0db9780a96167b71c1e42af66d5cc8aa9afc23efe1599c7275b2514f4bbd94ad0ddc31f5820862de881745763ea59e093143396a35b120fba8f0a5a9774dc112094b06eeb9c828c8ecfa02f1408ceaff43dc0edb46cb596a4c1b1146ad71e6f870d027c683a1caeba253f9a3f20d354a867aed980e3d9d16f7d352e00d73b97142fc714541c604be2915193934800f75a684488749e32b4ee2541810645762fcf967e7953125001cabc0942f6cd1b9f0f7f4a8fa531dfd784db634ae8057ae9044c839e13c3d47fde170cc01f93b0ca6b24b6fe18b1f41b18c3fcec52017a4fd264432a17ba16aa4e4d715452310fe4a6e21924ee682b420723004e5a1dea8a2a64d783ec650d4ea3b540bbce36e17cd1596c32f3f762a886055370bb47276212cbb50f656d4f1130de3d12b98da30f4165b5eba3b6e192f639d8185da29e55ba34a88cd9ea6c98f82ad48525b138380718e84cde8b5aeffe6599f8b1b63add37bb10bb7423aa1e56399ae0ce2989b3d713b640abe96f78324db2e2bfe467a1f618a59b7527d5a29746b2b070baba6fe1e9c87cab186b570f799c032b0019c2f8dd3f9ca9912fb0f2b9d9730b782378598e237e8fd5c98ca345cc279df4e340c18bd84af190cd3ad86db511f58fd24cad45a64ef814af5406f4db2b2860a6acd3f5453f70fbdf02378fe84683992e09ca922958e8d30b6ff6b95c0d9974ce93dcae82c6367ac0a6cac0c0c6bdd6d4740915a52a52b4d64fb4a89e72fb16aad104909667b26631d157debd90c7e78837c2f1ed0023395fed2e51bcab7c17f82f1344adcd324d443d79a32e96647bbe0fadf8099cda3c3d50c8de51809336ccccdc8cbf6b08e75165fa3a9517a9f24f58299cbf0ccf89489550a1af9df145b6bf63dcf6b8d9388632b1650d05a394ec0800fe40365399bf3f97b73e7d73142f14c540c08e389d7e7eb120c44bac4bfe71683d0a8443d8a8e7561c3ef0bc00fe7cf756b3a2ef3d57926cb3b1b2013f0eb517101ba2e6e0698fbfcffed6cd8ff869df08ed3751ee54d61f9dea783e4eab08ab57ecfa5ce4151ae68875433f461557e91931bdb11fd3809b726f36f63b09894941d6e2bf50ebfb419dfe057eb1eb34869e20514603ac4d78d7eae1ee65be6640aa7837c2a820cdb0f8a4148fad2cd2c5d857b780e0b8893abd669e3c968c2787a84aaac86e72c43b8bfb7dab81f4a1430af47b5f03efd17969b13de190a158dedb4854f8a2d8d62c664100831a3cee3802a8f7bda333508a60de03a2ea4f844ed1d997d06909d143d597d090d356356962f9253728c9ecdfa653a21332b31d8b104a2a554a22cfefe08f0b11475d2458a85dcef8242d197e37d6b83ff2dfc5394fa0d109bbbdc316c33a8df2047ac75c77c02305cf8729f6bf9be2dd4a2f1e5defda5ce2d6fa75739c4b5cfb193fe6d519d64c61a460030b9efcc7fc43dd2e0038732b5a9cd1496347805e5072221456a1fff7c5d3d17837a2e87841d56a3788b57693cd16e1c5993d19a8665c558dacc751f441150bb58ba14c406044e690fc23fbbb26eefc3a5c721608ddc7b54d073eff7f95b9e53a21805dc925ffd2d084378d0208bdf1330dff69246d1d28703533a69022c4df1fac289a5120c48f0838667e0ebaec0a71627088dc065201e3b752fc27212886947aa6385e608a0070415e786e14ed832410ad846a9b220a0bbd63e9008e73520b55501c8c8f0e033f86e9dd0b9cdf1416e42543d1e35bbdd6f9a0c465091121383560b489b367e4d5a664e580017abb705ca6c4e8f96a2ac732f1138a67fbe636ee80d6b787bc3bfc1099608e188d4431c0acde36294eaa19418875f942ce30d142c7d26ba13a62a920231a4f6018b1ea76c9d497b3c21310edac7508321ab7c08331a5562305b1f49f0e9d67d743f467b54b4f345215694d4e11837ceadfe3681da2decb8e9cad70a2c6cb732c00d5d38d394a677f0c7f16d47687cf43713c464fea82672cee96ab7d786441291f26e8075a057ab133e5987301b4cf7b47d9e8472e7fcbbdaacab11a9ea0d8ec6e4272fa25da299eca9b5e32606a68a80943e51a62f4d9db0d42ae52fc7d56727a5b7ac8c0f06a55c2fa14b2faebe28dba8a9c84c9c5cb4a7e8ae2fb1b83e220538993a4552dde89bb814cb6c67d7cbb007431701b2be4e55a8ec7a95eb047c954a251e218a92ffa6f5b9601aa00c62c2dbb04d5b3e29913907d893f19fc4cb39144ec0193295ae369b98a03658d0c48bf2ded208bb720713fc8f1deaacdbe4180975cae3f4ce57a7c25d0f96087b809b6c11d9f5ab7dbb984bc67894ba13bdcc4793d5f9a3792474a27795e2db419860254c068ba8a3711edd90768db32903dd469465ef80cb05bbf463a6fa7a4035b7cf6bb4d494f5812130928ad6c1e7e1c1be37ab006d50c754eeee473ea8d77aa0f963c7dd046a14a92e786a6307d2ec1fb93fd39514375ecb1a31100f272eaeed1d246b672a2bc86dcb79c10d7031dbc2790d1a3afaf5d0d1fc26197515118b336e4bf379143177af76a233a7d6c59d41cfa2f3cf33d753408682f0aad239b43dcd91985eadb959c0d439fa937887a7056cb8ae11b146b5033e764eb7106f7bdc95e7bba5513e4e6da874cb338a9f57102ca9561d4261052b51714362d3d4a3dc45697a7c514fce47a0993ccd124fe3639db272797e9605dae3b0a1633f796a8d1b60c6e8df3673b93c1133b68a4c980bf22e96e96e53efe6b23986511aa4579f0d877cd348ee5fbd9827d5625b0d24ad52af100f06c1e01b1d3462cc320fb1cc62e1c3146d78ec005ee335481c3aacec1695d324112c3ff144feba30b198a8d1b154d28cb7e3e4b3caa95e48d2f25acb9b6b39b57ac6e997e084c09545bd4c64a20a45018afec6384431dea407675ed61ab712ea5e15ee12d68386a667fccb0ad5629a68ae9799aad16ae4d01645bd57574525d82b1110587e5909136da141ac331b3f61a623c62c0c8d66b8490de4e896aa1525f19e1c1a42820ba240ec74cf9c216b2d5d82a0b75f0b88909b6c7937054430d4e4091f147b7bd6f0ad6de8beb2e36ca897831bf04d861e015efa951cd0336ca71d441f7585279e7d908d77b28d38a8fba43038d8cc2f5b9612714357dcfe3548b051ccbd3e1ff8993dbf976cc84d3f9daf0ec3aa641770d1ba0e385ec670de0b896dc91e8337b2f1c6bfec7dd57d8c7ab49ca74b10e3d2a3f6201ffcc31489760762a97343613a718f2cb4f8d0d62b114a05966ff9c931c3020c0a0f1235cb7e76a6d40310056dd595796d5691e28c521ca63e5cb2aeb7b3433dcf3475514cdb4a306b86b5f6a7ba1a70c851705c5b84cbcebe4e43c28b8f828da008c0720fa8ca49e8b995f928d5df04692e39971481168189348c4a5a29b73be84153e8c91e6e5ceeb276151c5d630488c6b11cddedddcbfab94dbbabb3b854b4ccb6f97c2559c54bd474daf26d6d04fa31ca4e5a0824e0dd4309930e2de35031b9dd2b9feec12a80fea193ad4de272309212c5e96b19b7b0218d77dba96b4f494fe2225056b6a008eeb8fe536531ad50292633938bc57b1f772398a6afca7189ea39379b46c6c0042b647edb4c5dcc4772b428da5c5735db20c0cf8ca96d41a38bd908723bab33fff8876c415c8f6b4a233df0a887216c2c3587e0b2a453d90d54d37cb2a5c6f36b001a25cc016f7c70e19611272337fff7e1bac47cb8d0ba3d282a92443dd63ccc4c3b19f84ee49105d63c67814b1e55b3f542dc34aa0859d74977975518fe78b19050fcb5987e285377f3e83afa7eaa9c929dc7ba99da46d5e67adc0fd5c16ba88869f9ae059cf5d0c9d8a239029950152dcf586edd2bce920fa2064eb0c82551e0dddd2f77cdc27c3080d90307f1509610fc0e49f5a6290d225bfe443c6ede6e1d2d1770d0edc4742026c318b288ed7230a18dc5cac65a0aba662d3adda070c6c9d63b5ff926edaf6c2ebb64387457f0dd73a7921ab4e20abc4b8b4f8b4be118c20cf5019b935d4f80a210d4be6a6108070bea7efe3986af143f694ec93d4e2eacbbe5fdb0cdf439e14ca1fa5a119208af91906508591534b433a1cd82557e0843669e5e67bdde0b3ba8bac203aadf98388f0279b95f5b0013c80187fe8a3fc7646355876e1fe901d6025505de21c543419e0d2243480e9f9a37ee0d2807f0211b3e220965363bb03c4eb1c36403d5802f4718b3e6402c2dedcff41ff68f3617ed28a5740935f07215436f354b14566b9d387d952a5598a19fb7971b56e261036a758080bd72ff640437330661d4fceeb3e2acd2d307c43e13e8a24d69153a53d2a0cb5501df5126d27ace17b43945cb72d0b90900b67fbb29cde646af1abcb5fabf0d1e7a68e99b0c8f138624de454d01c4ce02eac6395a5a400e334a0745c3970628957f26ab396a7d9e1b1d3e4ec24de31ed24c8fb313b1363e6c1d3ce4c894d5851828781e22ce3c07f901f94fcc9cfbb1837e2b0fe41b2e2a3e791a0c8c8d6f1a824564aac617d2c8de644b29654a29a587055c057805a8ef9c12b4661f29266580719c17e9470f0add65de18afb98c78dc3c405954b24c7f20ee464b6032a87c229545a50a7692212a778e4ed7f477ebb4e6260f7ebab6da06acea1bff1f21700742b35bb97c0a0716c9bfbd6b99a10a918c514e374808e514246fec10441235cc4d7e91248ec0c4204b535953dab802032fa67c396921e154d614a42947525aee068c99f9dac8ddb0a7da3c77aedb1f8f9564d69899b9e56466fea19dea6e8e9ce3fc57eb76fe2d673363cc38ca905153e7dbddcdad6a2e72579990c4b8a9dc930c2154f89c1f81b0e111089f7329f0393fe21c3a97e2fc393f029ff323104a91028f40f89c4b81cff991fe02539a6d55b165667f5a635293e903394d1a59ddee0aadd0beb090196635e79c7bd0857a503001a9c73d0713afd62215da203e8015cba6809e1ea8e366498daa278804200e2e11125a1893d6d38b2568b9c89e20c83ca6aee4abaecbe44388d7e1c1c367ff152f75bfc1a9423dbb837f7332b55552549683554277f0333fcc329909b5e1d6968ac947ae7bb065ce880e34b193eec77d8973639f4675c343c74a9cfb3f420852c2be098c83fde060ebc3c21024a098f08a0f181f04a64302c9749f314ece745aa6531c064cff6e0009826647cd51f9c36a4621a2bb2f8f0d2860e30a274ea821e50ab7c2dd1d859247506180235a4e8e6852e34372e888e9fe435d4cffccbb89dc9c5e34a6fbc74ccaa0723c8d4ac74d0ca6ff068cb346a63f8f07e874f023042046a67f107f122c50c29439fd4d1082c26afaafc038a6e9cf02e328cb8996e93f847174f448319d00d3594fda68a017a6ffe2b041428ca32c284038ab882ad3b518ca61fa6f146719e1657ad1745fcdc838da061b519e2c028898a21443bc19228b1218740da1c50cef280e21460e4e080ec1c6119f02e819b1a13bc872e4826e60199414435da3d1c9073ad072c4226295a483295652906450396ea2b46eb8d9224f53593d3439fd20b3244e482400ae9c40c24c65f5804513129bcaea010c17c83895d5c3971905a96b74c2e77c878fb264faebb8f16105ac28395cc19ad9b90c3ba2ddf9d1aa44afc322b476f7f66470255d2a632249609cfe1fad3145393c2753ba508f7de6eeeeee99636e7277770f52093d530a2b5d11ca2c04d2047f21d9c5b3ec4bba4612e12f7da7884009c25fd288f43beed23fe79c7b25f83aec9c730ebe1277b683dc72142912ff7d910b48e94d40a16bbce7014915ab9ccf90ccec54e08fb9aefb78696abd01cd89fd473e4026117407fe05485033c630f2017ace7c909b1a91b07909feec70782fc2d27befbdf74aef7ad87befbdf79a8750975e11fcc8590fef6d331ab080c27beffd8c06dac36f14cde793cdd09ce5232ec46077390a3dd4cd181ba18949169ac3a36b5efff3d5b6448fc71b9aef3da645a28e71894a1a90ebc852b163ccb75ee6cb62be6d93e6fb564f1056bdd8e343c2cb7878cf5918a78871361cf3fd532e4b14c4bfdfe3b85134f5001db1748e36ff26692213093f4678bd0e735f113e97092dfb10435421422ac1c74a6948ded28c08eff947687eec47e8cf9e1f7bec3b95cd4460e74c0a93a74e1de6d4cc66a9991d8ce30524760d84f02384106a27688b9170de611313e9fefdbe3501818f6547640acaa823725df00d5aa1e91f3d87774d1ffd12b92992098c934348917e96dd39fcade682c5cf7e84f7ef338d0894209836b3f39ec8cc6a3340b2a780ee648f7da61119026584ee42060e3d3b18112841de67daccbe7b9e1d22f167560b923df6fb983604ca6a41b077da90ebb11f52faec7976744777e2eb0eb6261fac420a20ecf457c107cc0371afc31b53336b8a5c66bf1f21738f3d9199ec47c01e6a449c36b3da8c08f1df8f00dffd9b32909219aa147b07d39d94ee4017cf909187dba7c0babbd9f463d95d0633c846bc9a5ba624dd2eb3100c164d91eeb92765f6adfb5c462433f601e9eeae01f311594d57e2992ff57647249af175fa6a536c57549421e7a06afdbe5f5f2e3ae89c7b4fdf7bce71c09466bb9a0a28a66a444e53269aadbec50b684807c084f4a9014c69b6d5c7601ee6dde92455a6b6540aadac9da0cf8ac5d78464534baf25cdfbfdfbdd7c961c4b69dc4a5fd256e013e27b10b56d88a736592a615f7a9367299feea53d9d90c2107702bfcefd536ae349a1a7ff2bfdc6ccc1b87682be964a9b908dfffae8ae7f3ba1e9039efb4aefb2526ae3bf26a01f4dfe2f4bfff837c5ac6927e8eb9c00bf6a27f0e6bf2b4c16d2933fc0e60c08afcfbf422d2c8f37d644bf94c94777efba8fd97bd77b6f6b61c69ff81d17c9ee80fc7e7fe656993a8cf1c3ee143f16bcdd9f6e7fd5d5ae7d5994ccd89cf8ed80a90ba8b94abfc54cc57ebb60346df75dd31ad4a769ffa5fdd4e1f6d16ec76e04ab891f017c213321d25841680a76d40c5f008b38c8855da0a1dd21868c8820875dd9839437c8ad85d98e89aa8ec0397595cb5c05f3c06b359ebabbe6647737c9d0eeae0e4357ec9a000899451999b3c14d103747740a5a606a63dd398d4a87890c1ca2903c9555021b9c46a5c3513d95550225339bca2a010c3d74a3acfa7325ab4ad274da26636b9bd4bd612e484fac8eb6ca93d61341016082054e1039d1a5003a9888e2c48f134d126052620934aef410c3d42b3cac01e735533fd42cb1fa9472d0813f008daaca5c30e66a2bf03ca90dbf02dcddbf573b61bfa7f26c4da258c51368ed847d9f27eccbc8d550accab0c8ed74d21efbfcc5dd73ce699b73ce41771d53bf5dc6ddb97be69c735bb4a13e1db2df7289233b8eafaa17a1d36458d5ab9b0c06ece8c8152eee2e7f0059144628005ce8e28be9578860fa755d572c5d973b4186099aa051c40c408c80b960e9e205472efcc3e7b2256c40614bdcf0344ac860e298ca52028cb9fd4d5c82958690328bdcdd47cbad3d184a89daffa836a520a92a1921b3339728ccfe761c9bb78d8852653260aa95283e133bedae102b84cfefc8704dbf2442fecdec24d9ef1715e613f83b21e9eea518612946777db7fbf79e738dc3be5d98daeb7761eb5b2134b3b51460eadb4fd60184c6c4f1bbf3aeb9e917ba5b35f5395032328baebb5bdbe990f43193f1996c30b7155c63d7b5b530af8d47e72bddec0e55f89ec341754bafa92da2622669a66bd7ba1c9eecb210ec9b86e4efd24a32372781f9ee3a23266eb5dd6d9e5e6ec90f487e7546535f55db8b6616dc0c8d18c64b169337c82126048db0201beea38681125cb0f86001426699fd65dc39db80f12b8d3bebb0cb38358455eff9e8c67820c61a58cf6c20d8462724c9034c759867ba7bc733afc77020f170c4df16e320b57397c987839b997c2c972dc27090c05d98aa63d0e4a3023c3b188e23e46e1174562d91094bebb0db5516b9ba74c0d410cee1d11be5890e9a2dc7fe6c111e3c786c37372ad56b1b4a638155bd85ec22f795b583cfccca4855cf8aa13f54150392caa272148471e0c2a7611cf8905f75033750850fe16bc3e7571bf89811b9af7d450d495e7bf8d8d777334792bac63dc7eeeeee7649ed92923a679570ce319012a3269c94b4469db33e6be44fb609e3f8b82bbe4d5a85a9a76dd27a91d917999a3b63124e07abdc021c309d5328a6abd11a95da1895c0384e086780c8a1311d9020ee6d065fd5391718c38c07b512d4c6a57802b516b9ff6206e49c73ce391f8a235b5b9f2cb2af76aeb990fd3fde0f202390fd1e8480d3cf9f53407e302179a6fef811023485c0f380b94b0365b66bdeeffefe2c96da1fafa46d3c7c54aa7f148adf6ea889fa1d1df3a158548e66c6385a5402723b3119b41062c63648a7efa2bf0fb4c6bd73ef9c730fdd0f234d05539aa38f51caa1a1ced980d6f8bbbb0b44d3fd8706e93a14a394a8495dd3efee925c124abbbbb78465cbdc7668ccd5ededededed750e0c56a969777777af6ef76ef74b77ab71474ab36f9d8c2d5bde6ab6f20c732634fb79e4e41cb1b200cd4eea5ea1248ab258716e0dc661230924b16a8d9f3d3d3d9258d52ea907155705566167d2136a16f413e26982b969737f63c0025035f2529b7eccfdd2de18af990db507bf15f48269e734bffea53db4b1cd7848cfa002255a3b435ad02dda10da516e400b37d2381acab1acce467c70d2b3c3a99d1cc0dadfe67aef1acf8ea9088a20a0500206eec51600c4a11b848aaed4606589005c425b58444e840040afcc68c19e3370708203002ea01e27beba6283143f518b242f5a69d1124ad247ab2e4d9cbce0ca902ec55848d76a99c0b7500184454356ac50e30b8712c5666505ca132b50a2e802bf105d435f56405fb8c8bac45ca1814b035da1e18ca22b4966b0529afa3e05e85943a84876b4921284eafbc0a5c8872c2d1f56800dede0a3cfad23361e4b8a1b4c53ffd463c58a2edd63c5e86865a50aa16b09c9533f07d0d41cd48a8671940585cf060298db4fd515d0c480a200626e9aa579a50e60b61e60081eb095dc5458f3b3eae6013307ab3843e1c0c8908c63e20021b26abdb9e86d0e148464487ebc48a09514e7f0ba685499bbb292c9873f6692c00d08373332c087261f30c34c20dc4c7f78c5771f77c7a6fcdfbcdeb7b5ec5aff9992292102185a23c05c779719b2214faaaaaaeaaeda604c7de7dc7b2f665ae8430873fca3503231c6cc7b6854aaff1ca86e4dcdb4901b6aaabb91ec47ee67eb1ea7c8d53ed342aa9e4c1f9c4e46e46a9b8c072ba58c8989d91b3758753f023966b8788179c1bcbc571acc8bf7de7bcf7de95daaf42fb5d14ca8a9299b0fe6312fd2e7bb52a4674a5e6e18985f184dd5e67d54a15a1555d7a35017a6fd609508a4b2aaa4e9673320c8095f4ea8c5ced667f0c20b9d82416eb1c8c59f424f212e6d46b519f83b505458a7a9a86d34f3a7f644a0674090b33fb509a199fdfeae0486a21bd44b152ad4fec69e7f76477c4d6dadf19fbf77e6ec1ea14b39b71998d2e4f8afba4c655901d2a6b2562c98376df2c7dc7cb5e92f75b9b9e9978a5da3f920f7b716a84c6e93073ba34c7ecc79e6d0e4f71598351458c5399683ed17b028c4fb4268d7fc20e512de5921ea12a9526d92901b91dc0ad5587f4d70fd1cd7cfc1020b1218b51183e0c84d15ba4b0247345d92237244488cf4186976903f4662a4a7e9ea3050749093c0e51fc8491828aefb2a2587c494954aa592fb926902d82c65a552c9f4a6d49621c1909cde4ba65f9fe540be49fe95f9cc3e7b27b56da70984d2cc3468f2c0e4c32459d532a7144a7f93333298764d3a8d2dd0b54816c972f0e3aa6c91dc9ff733d7e47e7e0c4c692292359066134930dbb20913df9ffe4d009bef7241c2fcf6d7a682495daac31f605a4c99aed33f2aa7dfcd77e331a584f993b6ed84f9fd6702a13461f69dc9039887539cc9c77b1a534af5970bd2bf05a279cd5215938fb85196035d2a8cf31669c2946e914b654dab30fb78483a1531f23ba5c7be01fba554e406c4eddddd98621c8fb0678cef8111e38bef2118b05b4d3e7891743347305479e0736e35666693dae847ed78f11bc25a559537e5ceac82cffe1c19ee28ddaa1b3230a5d922e6ae6ae31737996e42e75cefcf3dda63dfb949d7f81fa92104d399486b23262a3272dc64735c5865c4aa2e529b7654baa67f3acd57afc88b20151be4e6abd9bbbe722c8c4314bd487f74809a2a1e2814e3b8bfe1dde8a8d9bafa0182ccb2238a9a09b6cb82507f0ff2a673f4695428a25537f6da39f71ee874cf53c26c979a3922bf9f67766a0537a3f676c77b4f35d4e85a6b4d06f65e035f229917f2b5597fd68f7df3ccd27ef6d79716c9ee80f1190ac7d677e81bff771a950e5d61eeeed111c18f18f63cd3a5b62f215b9e11159e600961e1840f3f38094389109622405caf7a9a772e04a1c4898b419b228c211a96f8e14a16602879a2e4081480187a414f14569eb476fc8b16ec7af15e8061a7016830570db0056033e1d85099b6dd38494cdb4a30b2f6599665597645f8ccd1b4ea8eb0f5273015b29ee9be7b487bac196ad38b02f97393b8b7b59a2988fe504b6dfae350a366b769842bbe9a48e899209849db953613a43424d396481b41bbe6c2fee2d9b9debd9b82607f6943ae5db54c143089b03b250dd535fe5de38ff9d066d94c101dd22b9e9d5dc5d79d19ef19d780ec0c95529bd42d0c0b956d6dab8790da8f66bbd1ec1eea557727cdf69ed91e34db85667b93d93ed443dfbcccdddd43eead13136f698f05a8cd663daa20fbda1bb7a6ab06dbb66ed4c6ff860c72db563771e8cf3df64cff6df96f52738f0b4d6f327d68babbbbb7bca59a1e3b85adea36cc07effb9fbb40be0cb6cc6ddda841f2aaa76772184546f4cc1e7a4eb0996c65066211217812e57a115e2fc28510531f197477a3bb2b6cc5fd61e110a49262e79c731109b9ad8f8ff6b85e84d78b10c2f5e99a4eb1a0ad35163a07a1bbfbe50ea1b6da628155eddc6e0ff2096cc238be9aedea315534cb9cdb8bc4f463ea6729de901a4cdd529655552d691b61988a55eaccec4838ee5342ee6b6f085aea7c7b9ff323ed3ebb481a665a645db093aec6a739e5e07707bf6f5c88c35c0ef688eee047c519de599b4cf6599fec47f64797da55a4b926a76fa369a4e9dafab4b6ad776c625dc6dbb7b84e7777df54d1926529581052c9905b0c15a1a97599fb5a17c6e926cc314f82901b6ab6e6022993638418672602cd733fa649c70011333437c66736614d865531546288e6c650d18eda0c4c05d933f51d0d09675053084d7702f81441d3dd4fee7e3485116e2a9ae20a87afbd98fe6cf2313320abcd88b033338223c2ef227f11c0d41bca53c148c5ab24632ef9da05981a4c4d63b22abee93dffa5302f7f230007aa3bb60018603e8e1ea636a67c1d3e9d4ea7d3ab294653993aa5dc51ca346dd4a674adcd2bcd1b1acc95eb5a407c200c628ccb499d7167ce3a4bda7bbf4d98fcbba8c93ae828913ba8867b44b45e84cf7984cf3974ee1c48511b725b9fd782310ac5089ff3089ff3cbb97320454ce4b63e985e057862847b6af985ec1df543ea1cadd91de1731ee173eece9df38837c8077b3256c360053544214d53592b2852862ca921c7f83203bfea69543ef4300c0d631c99623046d1184431c2e768543aa28bc2e8cb49872ac2382521dd54561547551821ddaccfc08518eeee8ebd705dd7a58486ebf2c1929891448dcbc5a004044a76c0aca862faf4477161050ab09ba7b1e2092b98f0ab099494a838f4e39a402caa2891282983622214a5b8a13df2d0721ab1e082075e62b4e4680992a3b090c16af4b803d10e452d51f001253d54e9010bd7e8dca21c4348cac88f985144c578a358306fa28ca25946971ba217a352195f6c8061005d2e24794754a27c253a38899232282fd6e01a2df2035143d0e05f5079538e341841ee4b134a68541d8071e4ff658a0dc3a8bf78c1e13b1023888da418e17351568131e304ad211760a2c8ec078625b1a0d38fc42102d30c8896e82162b540bea92ca2a22288b09099ca8a024805a823a8c420e1541615471851b10466257d2a8b8a201a950e140c1fd0a2e50b794d6569e9e2648416a453121980a92c2d675001e45cd9f0791a024c656d614d9fca2a2ac1dcfe6687d88b921dd0002451541cc1e71c054510d34d6551d104f422764c6559f1336f30a1514a19548e7ece655039584556160153595318a165288b9ec8420926002141e7a66b9807abf8bb983c21d0010b2460d001065e88d8612ff3861a26882205135b5469c10e1ff110a1c3134f58c0031a4933d8e12e7866687206174160c1da228d1de662f2eb744e4e122272482a810d2a086287b7c0401153ba80a2093272786187bb4c6ee9e0c5931788c43802861dd6a2250397a1153041811765ec700a26ef932d5bb6ac5b7e383d4db6c8cfadc6ce9ddb06acb4f83c21b72e0404e370166a02813d9b4fa8f40d5835b5533403000010143315000020100a0704027140241a9297457714000c7286407856349647633110c4288661188651c410630c02041883884235740300b6a5c4bcde1c82540cf75a53fadbede0604b6d44a7e44c9984f2b7c4d48d87542d4c4d8dca3c4c605237d92934073b22d3df8d1bfffdb53df3cbab1dbc5483f66094a1ead6a5f2b0931f64f0af8bcb973eacb8ae1097edc9cdc38fc34943641ba734167a79934a6ec12ac4180a230bc42508e81b764dfdf8f56528117e3d6b1051ba19caa9261dc78ccf2dce743933139b90391d5fe50bcd70f13219a3ba0870eb272af211e15e4f8afbe6df73f7d9e88f51ca467c21403b966562d1e6f50bb6d850d0c1c986a308ee0a8411304e82eb9904d1ca0532b236c7ca21a8ba1fdc1c502353d23fd332f5b8e15cd7a78e86ceac41cd106e3889261b58a6a332944f7c67ed52a5e4f08634c2a21fe456e069c271a920c12efc6cd418ea5d02117bb0411cebd1c969593a28daf84c8cb789ff68a36c766def755d3182e70c3da8e0f9f649e6a43b44b00fc99179aa9c510363ffb1775bec2f18b67e0088cc0e0087604237ecc27a93fa3f7f751d2c18baff8cc9a199134380d0ef6e53f83202f564cb81c5e9b1dc35e8ba9f0fbddc0a723e2c8e1b09b60c62436e7e5ae26f62539f51b5de4e40486ca22c3323d6fab9248a856fa2a00a8889977a29e7e119d96f566f15a39ca4e7f57293e16ff8a54943d2b92ef3e72189504516102a74d8793acc361b9d8fd8019bca902ee514a4ed3ca6312e09f60b70ffa07ae0ec7c71135d490aa3791b00e6d9830376e1f872155b80348d1d87ee4b84923526fbf8e03a16f2f596f828f3281a3000229aa40d38a735d169bf813f673611bf805352f3e5c776b3cbfcd1e90b69e17421946f8973435094b51c46923a2a9a759653db8b7765d3c5cb118f9c158108c4f74e570130511712e1c93fa26bdfbac155df237ef3be060f4867734ad6559e8766b89ae3b6e9b4ab38571bdd17d41a026cb17b433978495d1b07a19ecaf00df4092cd49abfcd4d7a00541fb9f19fdf5d56ce86cd56dd77fa5726fb55af9212b48aebea73e2c9ea609e16e801d9117b4f456b0c9ba5c7eafa08ed9667cb3748361b79380dae6cbaa1dec36afce4691fc154d9339df804743f7c35135cc4d5aca5e4ec142f9ec24c2417a8e6d715f22cc700c203b1633be805491205d917dcb18110b22ff0b8f9d4200b90529fdab6c27e7b203c3bdaa9f0f8385a36322d5b54394748a6b70b462fee8c432085a70ba4f0067ac14aed82442c0afb465fb70baed430291c105ecd8f3193ac146cd5fdb739a03a10ca0dd3eedf7f2a4de84ce663905b968eddcc862928bd8122ba93ba4adb53e0839881f7b522cefe35b4136dec1ee33b7b385cf3bf97e4f92d97acb99b5d3bd10b252304d8a2b400c63153b2314d09c27b2dc4d0ac29f7b18b3ada0d1ef52abcaf8a2b6a6584f77e36187b272830d5ee49dd70055bc4f514742d24099c53fd0fc5e1ff9484af11439034ffede4da549c10efa40b06d524118f9a6774ec31040f910cbd0870c923b0f9ad084a62422f766ca08d824b56ac69cee980d8bf293cdc231f5a455f718e5be9c903ba5258f1077324e964e99324e9076bba1e1af450157fc0389ba9cd681a8864e4d4ef15f414941012a19675ba2014b3c9df5e2d2f16f608cff411035e3ce187d49b4b35eab253d8017b1146c195448d18f7a317e30b59c6612a12b322d96c79325c53e00e4b91136d6abfc3e44eb798c9d2693bfc69710aab199e5e94664c77df4871d1a2a0c2ce939de0d1738fc6143bbae894fc57f9e1cedfa90890870d1c48369bad1ee1a5e37dc8b96c70befd175b885ad81732c581d241c691d0389ba457960072c314fa88e1d2e5d65be7522ce72e05e6e1e92bfaa448e3d2b2d9c24ee65f8a22a5e6229bf6728aecb83e7e9a0bdb5164c58f22994d7392c0f8d63a570fac99949305b2c70d0d28635546b4c1fb0ae9941a25abc54e87ae0f0d42ac3912baa0cf566b6aed7483f5367217670734e61020d3c0c17f2107842e0c39ba81de691ea6c0b6ac173c0b4f1c93fb927a0ba33cc5295705f793aa4de911700d8ef9d8dd991f5ea475d15fb98fd18aa4a78b93ccf9f5575ca77ff02d70017d68349477a08550f39cf95a8e3c8411ce6433c3dfd1e24498003e04ac84cdfb38a3160e963b32f545aea951e0a208262d3a4578181436ca9ba1ff855c38b4de3b04c2e364888be85b3688147e4216f1ef54369a69541532b05aef7f74d99c7af71f3265b4f731d784f3b5af913a0dc74fa92600f88f078129306321fe89b3ff1d96f920bcc189cddf3095c483ff5a3a381e026ef6f1a6a17581bde4ac208e0dec28e0aa495c5defcd6016c0cfef065f2c15d6f1ba189b2d80506a95489d0d1819ad355dc8f71c96f4f5c888b682dbc3b7f403d27f2b28d63c3547d8cd21fba0629ed22b58554a48deb2bc47474dfaf5d4bd8833f412461f7b9cf2f154f4c77919f5401f74ac2a488dda4089128dfc6176c08a58f6d061e579a7bacbf58eebcb8da11de160807026b1092840d4fa81b6bd99696da9be55ec7b3670a793b5507014014788c8c7368f4e8d452412bff3ad3ca540530f59371468cbe49d0fc9f4a996cd5220e4ba73b57addf99e0f029240215c5e710704b5e0180914235f3a095ca3f03e88a09f208a561344bf5975d1fba23174ed826cdf3f758ce9a8168dfa737061de1e32871f63bc4ca2557395cf0ad365a40fb24db3ae10a74ee413e8cfcf438309d5b09c29c1388c8f284631a07a5b8c982296660406272a82cf49d30944637e0f153a5140226de3bf20ec27f836b1774b26d07cedfdf14f17561427b50a10ce21891168acc81b37fc067a81bf600e36ea765861aa32dee1a319d3f30eca35d9a93d97112178e3336ed99751452ff37f1c4d5917f75d4c3909857ccd79a1f97bad4ccb7a0028ec0b196a860d764efd8718cc17f167bf0b0a1023b9bea41a712d0e8c1e8a6d2ed25abde3f1a0de7fb42a245d97b316a69b97688f73e4cd6e326a7d32c9ae7be76548277db616c0778d352c0bc6eda03fd8634e40df84a39bb05f24dbd418adc47b8fff8a5f132dae1b7c5329802868bb19a09f452cbc16f6bf075d0875985015ef30efc403f4fed7e7bc01bd1aeab1bee531dc8c3ef814f14701393182cce561e5a08286b913bab94f9dfe392cfb6c784d3250b79df3340407c9a241d7a388ab11f7c4f6958fb27c6056638fde97b2414f26b940cb5b0966cf60b51b5af8f5a96e86e4b0cdf15bae40cecbe03ad2060460cc62776dfcb0346b2a120c3552c1fa502393434a5582207111d07d3c4b68115f4001dc3a67f428c1c588de22a4a5d6b9ea78fad982ec31a7a8413cfec0d4f13444a04f796065a472a483bf3691f9698726ff3bc45a8b149e8daa5641e773b4ee97a52a119777d0550b53b5ed05c9c2edf434da04a867d999098cc03c96923436466fc965731b147d1d025e4cf71bb3cc76b8938158c55004fcf55112be6e9be9a7f1e291efa83263fbff31d1abcfd6d2dfd88020fe108dbee2d5f1d8f41ce157f66dfd0d77b64d806eac1c826e025a8a9162d51f1e001f618cb92040044e48ff1da39c7bb187277a053d0cad4412eafb582ad72925787a8edf44221b24cc8d2aa7b4484b8d3f0019395e89b94bd26a1d62b1f5647f6374fc3385f8e7d6c8938ac64f9053bf772dc6181c607763b6d16984f0224f66c592c4471081a9a5931c9a0eb98c2a81b013d36ac53a31652868e4e25e22443fbc40804428a7d874713284cd05c3277d9b972caf432fbf4b2fce88727411c9a85d44ae33250673ec4a3b0674cdb9b92ca89427f2eebcb24de3a630583ee35a494893a863f62e014d76277e8d45877a0f9d1373659e1a6fce0a86d8807c196b3e2d96da85d7a6131e629e2b606dbae52d60b1e7a0dfd1735a0f096a27807e00681e15439ebc5ace6d7ab16c5ff3a8fe94a898b20fe0f42ba9500d0af1502942ef1b6cf1efaa4ba05c4da07ffdfa21775f65a165f9297a52da01826ae0ab3b4c9e7c0ab3ac0fcbe63649b4802473981223dbac8dfeccad5b9febc0b5af0b54a47a9422557fb18e1497bf41c733cf32f09125087ab9bb2e6ba96473b1dcb7702519878ec493dee0ecd7864c3a0b67d58ce747772dfd4a89fe69cc5ca0dc0c1ba7f2e21e2529f5bb81d976fbbb95931817f1d989a99aa287c244bf2795c0d71e967199287ac6973ca89c1a4554d7418f2ef32c7f05b1ae31e618dc7e917510db0dbbb89501ac8aab5fe2a66e766750c3079717d3ec07cf2eaa84063123fb7f03028be9c1ede4096600169a1d81eb390062ba87b452ecb91ffe7100bdec6b2406ca11b7903d5a8253b09c7e20cc2ce6ec9f257162c1c9bd20661cf2c4b8b4effef42d4d588832dd8f4b7cb3d0f7b86c6fcbb7bafc1a88ef13255b3d2ad009fd76deef805bda8761bb7c0ea8df11cba31a373281ea1d73538f7ef3cd603d804f02548efb0402737119ec308d705b6d1addb33317dbf4b667922bf2bd132ff3236a07446f57c27cd902a86be01ad0d428615a96d9b2e758392e20b5835c1e698fbe0ca7b7fd59ca7a0118b739eedbf7e72d51fed5a764e0af649582119f1836730e1c33e1a8744f32a432e6dcddc1b8dddfb20f84ae7458942a16b80d441956a759dfb541246200f3944609e21b19f8f88386ac8137365e07c74e6290faa8541faa67e36db620eda234323732a9162b2cae280c535cbb3377e542a37fd6a254d7d37ac1cb43cd9948375b9d51cbdc017826da0dc7dcad88699f5da7d9a6abf91d6710fab29e42b41009abaa902014757b11c36c8f860290992e65417d1908fb62278b6800daf9dd0386acd1dfce6f22f3d0870ded2430ebffc40b10361882829d90c9c00f5b6679b9cd5dd04df5106b3c0976d387b1e391a0b53919231fbc6d868897d581b579ee55c4617f38b1c444306d3dbffe7aad4b92852c19bdfb9d72b0cab0bc5739225d08d1b4c6a967ee982e8c4fa3fccb48ec0e210c4f5e079044310c801e1d0859c550ed6e7c8086e1e34c7ef8603f0562a16e932c4fd4678d31e7a2cb5c42e8ce1982ca5dda4f2347c5f385cb410021994d76e283e8a27597e890689b8455225b1bad974c1c711e4ba21eb2780b0bf93d36b46f2aca5a161128908819f84e250735968403cf346f8780f683cf64a335eb2cdd758a30f67ab079283272102f28bb60789d618c7f81c8a2a46885313de6aaf9b5d1945376b31047c74a92b6a6d9e8726c678e376acb193be94f639a3692f400b4083c37ece128e83f4ed8f62c53c423e4ca627e8ba47f1c38639d34d312592a028f9ef30c16d039bdb624fd89d8fb9b9c7652d6ca259633349d9c57797fed0288404492367c42336e9d9015627d59e1e9da51638f546b222fe41753aaeab21a17193b6eac6d560df74f190087a089768bad17ed2e0cfc0632b540c115869eb9ee000ea620c5b69e4f188774a443cb78fc05295d6382d0cc91a73cf558aa9b347807436f4790beedd360d04eaa6200af7c9806c37d9b55a3ee6a5e64b92efa7109150014de209c2634fbde245497aaf21b04ff53de251a8e62a9d8b9a57ec1c9c13d74209eaf1bb5847dc7ab3cf63691bba8e2b352a4c5711003957601c51aebf598fe8edf984e2af70c6cd6237c32701fb9926a4de14d16a2ab781761542cc30d9b64a8b72df1d484c5def03ca699f045a1c5ca24ea9fd12e8faac9b3d611c2e984f3f08cae0b39577ee06134153ffb4fca02c2b1f7fe55331438520085a8160df79bbbdcc2d297a2ec3591128032ddc37fafa18475e35f694b211e28ac74647056f5b3f93566d8b91496c0baedb2d221c15ab168c20e81e76d85777f87a48ba5acb12b92cbd5792bd93d7f8093155d83ccc26c3fec3459b357cc81707132f6068c4692fdde0270bc5a7f1735214a7fd9571a64a71684e6a0a5d7f3feff18ce4a85b8e052db9f0a6788fcefc8ae9c3af07dbf5c3ccc14b6c6bc3b02bd3d93d2a98b32e3a74de3a067b9f456a26e552cf2d09961bcbe48710418edc8b4a4e8bee4d93f9cbba57c93fb957133dd0f2449da379d1319ec648cde51dfde6b71ce1ec6c86023b1c1a428a9af3e783b574cea419c1870f324a5f4fe8d633c9edba82771bfd0de7ea2f3583510856a6ce14b25c9ad75f51cf671c5e2c3737d6ef9f21ca6598d220b34327fde53fe2bd47983d3cd649f6b8a8ad0f60e5397cb2f4e1481d3f53a9c87605d68b841efbec36d03919c5626dd306cd044e5c753d99562c95adfd6b12fa374e830778406df65c1a89a0c394ffa167337b8b2fa06cfc28fcf97326b9e74064bd95a5aef114b6197767b9b2d0cab5f929b24b30d3f5adb299d9e87260e7f1d34399cbd81f170847e9022b4db30403192e1f653d54486687b88e57ff5b912391cc1b4150028e44a57feae9debb69e9af2dcca367de1fe1aaf682072d85fbc05ed9bfc79abf3aa35688c31402f241dac5a1e073104db08d22543100b546a8c8d3555372bb5581d597cd0793f655992ef60293d4dcbcefc96d0a836b7260125cd2f2ca0d38e3304873808a7f26ddca43e0be040f5d025dd301d042ecb551c495d85badd2220d87846fe45cf24183ce0cc11bf7f679b55513cbeed7a2cb0c1b2d22fa2df3affacf5117c645d9f0f2c6c45a92153d4047dc9875516ef2d30c0aa8cf2dee14a87600816543b71893c5477f6a75a3ee00f28d757fd5eb9ae67ffe11811545c85021ad0f7e9e02442b56e7a2a55e8696adf895e25e3e1861f3fbe0f1c28fbd8a96502bd49c1fee55cdcb00e74f19608af9e8d19b74d7f2cb2bc0f611a7191cd33fa7305caa0b389cef8afb260ee9fe1a64b138045d66849bc79fbb97f2993dcdd2e7ae64d1ea8db2f96e4123aa7f3263aea7c5039e94b8ab6af5f8cb8594de54c7dad6283ab80175eb3b81093b4e9ce9d7ac06f08accf724dac89332d7b6d29bc44ea1bf6789e32ad0de8681231ba0a2fde3081759ec45d36ff30b68f3841191d75e3c7850f153b0bb0abf8509f7339e94be00675febe13f4500436e7380076950fd36de18a3e84b0110686db04d5a157bf1a4c8bf15e0b8dc982860caa08a379147ecc0391c6a446d38ffc699e58f3c1a2a37039bd0d49dcfc19b5102f18fd724346eb41f135ac8536ce0dabcfd7d9843d72e99ef75317b942052c81cb87d50761fe1b780a11d4d581ea6530c0a52e48574ee6d9c5bcfd16a4634e3f173ee8068a363d845140317443308a5d5f48ea2d917df8c2bac2948f81a5b526b008a7c3c65e58f064d698bf69793513a52d46826b05a051a288efd1f244536e626696dbf0ec010bf2791bd3f01689e8809d3ac28e139f67d29a2ef4f99fdcd4d9dc8f9683034f7a1dd34bc54c920b01d2f6f502371b92dac1da9f719a85fb791fcca9ff93ef278e05b812fcc6504ffda05406fac9eaea7a6fbf32f09dee93abe358e42ff26c0b75d6bb73212ce6968b10ce1d8ca8515d1bfcf8308af7db6310085d0b469fd4dcb2cd66f2e330c1b4012ea541297b15b906d1db378c425bcc57787323d12a14082cf164db67f02c4b9a82f112b6096cb0d38a9395603de25f39bb185bf26991e0864595f0ef7686483ff1eb3adea39025497d4357466114857106b4d8cff02dfd8d06d037a55257a54b890661445f89af6e7382422bf003e6658bfcfe7f3d7a901f2dd95bddb1377d76978a3e305bf18dc282cdb95bd55fa3c6876e45a5c64e1b279947dfefc242efac483ae37a4599d429bbed46569837e5d2977d08bad02da63fe556553eece3eb92b6d9073f70287992028910187bf641d60cbf88df0fa46b44f20364b9e2414bbb8552f46390a07c02c718572b7dd00a8b24773589714d988cad11432cd8b5609d6368fe8869179c86cd0d556cfc969392954f5c949b19898c6cd1567a6110c1c7c17f4ad30028e926a0c1f13cea87229ad1627c0efbfb01e4ad6a47c96b125798d854e96609dd8e3d61149db2c830bdee05f25bb137303015f27816852a4d6078138007b82909318434789d892b806084e86c7669a9d8dd7ff0ca134f23bdd235b2a1f6d4d91e45de6a0b34251b3ea8ed5b6cb85cf402821dc5604085436ea3f44b0d2926039300759cc5e9f1de6ec815cb018977351c28cef03adc289e713538289cfd0adc519af56a38289ae94a3823a577c6ebe140a27bf5f9f10efa42f4d1d454edd5e04e69a62b704bf18cebe0a0e4cc57e04469b657c335287cb3af04478eeea93b1c782d679f22083ef6633282085ec20782c276438f16b10f4680577e59ab9d9a11a39012d7bc728a93a6960bf41c4c1bf07fad0513e6968589b633b609fe34f71d8de46d2261f717b648e8da1d674e1f5066992a94368777e799aa131aa7f7819625b7e337e1c97889aac0aae4119df58da5d2a3550e72e7aceb1b8c1d814a9c5d7be377f33ad290bbd4521fdfb8d7f6ec1b412b633353c140b141af45e4c128b1ab7a8e5bdbfc375acfa99b38d22dadffd1666020f406341968423e46499538c70e6b6d8bebcfb216ff0651301f14d4a24a96049fe0cac89b19f80c82f40ba26fc9f088789ae8e572357e44612b6dc20186a61291ee503c7367db741d44eb9e35c4113cfe519ddb4c7619032e6d6c25fd10094a8edd95189bb13c045868a2a747b6e350b50115e952b03986c76d7d84af2c6cf8c0de16c2486ef3f7a2d14f4256a2aa6b4c1181f1d4a45b4fa00ebac0d10b597cc785167651ea3e2c368c3002f8988581f5461d5b97c7cfbd6499d9967ce00b69f0d9f0acb32853e3a8cde9ecbbaa84c934b3c153b68d2ef64b4f8cf251318e2bceefe1382a7061db8d3ab357cedc2b8372953e62501e802daa0b4f1a5b9b90453b0b94d1d1e9076bfbbf53d1644e6ecb034d6a62b913103b91b0610a569882d2f7f037edae0ed862cd6dca66672428dd6a8aec7c1f3d51dfdd53fbd4836b403f06d7e9d7b13ad50119d2385615d27301ee92ac706b4876cf96e7aac50210c73a2403d4b9e06c5471df7dac8524e0ea14b5fbac0c4e8505ba4559da4d2086e10650b8280446677ac32a4babbd513806881788763b62f51af3f8b10a5f2d5a28769c27c04fa9d333cf66cce4a159f36b3d467b5a03b05697a7b8162f47061cccb531006077883cc93d07f33c925d810af200f0169f4c5774bcfe2eda1953fbe357bd69932eb928a9b7552dcdb6c01f6cc20d2512e25da0140422a74af9c3cbbc7f6df93d55d08476704412d33b86bc88092c9b368d91f4f5daafabdf7b6545a1b33bbaffc5756fd9cb8e37cd1dc4c599b1b51a2d6b7e1c0821213397eb8780ea4d9ac03b1de75b2d7b19b8cae39505d34acffcddb3a490defed76f3ee4a107b8ff6f81ed58aaca6a7c9847a24008afe26e2f3c8017d036c110277178f16e028c06354c29e2ab185119fc79f4346308edccd83bc1f0827f76bb5974a0231bc372e35bec0f99cea4a55aa60027dd151be4d48a8648b5dc001e379effad856782dbbf033db771ac34dede1f7c06df644bb548014adbe7e474fa729f637633b14e0c5f3f9f6de13e8370e3da92eb33db1c8313f4342ae018e724d1b71d8743c9b593c6215bd4327f229e03889617c0f7050e04ed163734f98d0958cc924ed58b09982aa79116c49a8132eb297c5c2d516a01fa90d1d61649a29b0518fb7968e16c70fd1e994b4f58805378d75a31ed276681b0c10d9d023cb3026ec3ace34567c112df982a28f16f01d60756caa912467576814907b8d26aefbac12f4f93225caba52cf4211c7812a0b9ede9120e9dfbd573080d2de0977afae545129a63d603d66042d249d2d7de3706f4a175f04e5bb477e3482e8eeb61d4cd3321936efbb924b653a8b298db0e09eb2c0eb008ec9100450272b90eafe6c2442d9500d7e72313ce423aedadf67433d7c1e16b4823e41bba6a9bb31a9dc5708139b9baf5c91ebd07d54c472e212e8df244e50648574eb52887429db8a0dbf8486992486edf0825e0794f1e9f51b2e35329d233a8041056cd28bd4867d8940cce307f8e9c95239c02a3238001a8b32dd273498e1604ac41d84d326252120038b75720f4bb93ab4db9c7a7905a3aa9dd6abbe6480bcbb5c735308cc8898d6e0330af0aa3f402042a9b84a90c925d662977aa068f9ce07979036d4f1c5ce4585d01cecb39628a6b20deabf4db9b63de50497d1e47c25058ac1df1f62517d3bd8335fa21cc18650f44a9dcae1b4bf12a8ad019f1d4384b1738c6c652b7d0aa75d8235974252d7b36e87008c35676317334e2dc5fa91f7d26f9295efdb8b2c13ad057bb138c2128a0316e7c155a9b392c80e31fd924b668579385fcc2f0b6aa80b4b23e3fd3ee093e809be88510edfc6a01f537d58293fcdb86a79150a0b1837bb22a598df5278b03f09dd20d54be0b7ac63df5c0ed544400764f9f87b21ba7a59c9f7a60993790f4e078aced766f763c68dc0d55125ef07a3b378f923ae37fb5de8547d88f25c2a6d958507bd773b80636ffb9dbba0f0cca5ec32d6d13565794cda02d3d551a8d8fa557fb50e826d73488ed8e8a81c0c5dd44278c6ed96eaa44f2a46bd93e2108c1710afea7c9135ae60d10971eb3820354aaa7b7803cb79bde54eee52cbd8308a3c57923129b3b2a4c635668e9ae526a2e53a2ae5aab7cd8eb52dad5d442005e248ccf9b88e9b2949c311bd3aee46b29a8a12f270c0f52c359e8ca99191a31b6780f0e521a8706b2d1319331e8c4ea69e0a989cd8ee571b50d3af657d63068e4707a019fbafdf530037cdd8052039f584dec3c908ae820fddc57e18f10628ee8c4ca908ae2df7ccb42041aa663b93191ed4b1046ddbe8b990a82373444e186a35b4b168624ecc0b876df665e858d13191615366ac34067580ae0c838b5abfc0552a3e3e34f07b528bdb13c1bdc686d3746d3997da0e990bab503b799a4724885030f8034cfb3d2703c4179249ac1fc11873a65d13c91b521bd5e2dfa61af04b2aac0394c3f3a82565d81a51783fc0bd2a8ac36447ec5cabe0e5c6b87aee4102e1fd122068afb4d82bb595b9b081d845d0319d385f4759f5b61ee6a8da6f9cb1588cd8c40a06b39931f5386f2194ce967d9e8b2d70528c53135b34d721cdc6a4e75c92d21295820baaf725a28d9979c19021ea8cadcca18a595d2b58daaea1e9db86433006391efda745aa48c2794c1fa8340205c9918594d18848cdc64c7b7f78ffa1b9d8f7b53b3a992466603d46f6daa539e43ad217be4cf36a27c8f191cac01e4575a41a141266a4c10cc7c8ef8d8e670bc8bc460b943b8507c942db57fdabdfd52e4c281c9aae15b080e6527e56d72ecd353f46c2a73a02e9a3c5aba5ffc30f3a1a99d5714cab11c22400be8329b7c278338058e94e2b3dbc6f1b2e188d91032a1cbfe6550e3c4e47a342c54dc35264995952faf6318ebc176355efa01f560cf53146ff12cf7b3e7bef2f9b991514f91ccb6c0712063c94977482ffd463f874a445fe1696c02be0e9b03e8534d92a740d069a5fb9ba601d64de9bea1c7836830b458a1ade315ba97854576817121c497a6eddc53eca580e8f202d0348f3aedd7c0e9574a96eab1bd7e591ffa03e971df27f805563f6b615a6ce0e47ebece85f13e6c97370054a6105984508a3694e6a2c5be0a38bbfd3027ea2b58f4a8b22931bdec74c4e30683954e9f9cdd6d8555ba37eef9795e9fefc351eaae559bc2fd4bc279d1cd981affc89a4751adc0a09c81eb510428237d800c3e0af71a03726c90f4381683e049893ef81fdd156c2b626e3fd5984433947e42afeb96a0e69c55088b182083e39964252d347c3797c6b2389ca6a84d18094f34ad6c7554018376a8ad5e67edc0bb247fd2b02e68cc938c9805eba238933c6da48f55ae4e3d91f69807dd298ffc811aa10f51103cf72a4b485d7a8f47ffb58d0f30473abae8c6bd30810fe6dc4e04852c229daa9b6ee1c07d57a58582916e918186d9fd1a1f8628d15aa5c566002d457214759affc07978e263d4029dae658fa5687caa5ee0b26a018e02419c6dfd4aa1cb5073536446ae0bfb4ca61b7e444420314d0f8db317a1e9b78dcc714ca2d055c32957f9cf082cdc848085aa94c1c19e53686fb1d6cda3fbd71713724587b0f0659d024876ad7ee1c98ebe8981a750f1a62c55c02235fba13b18864462ce221a90099862feac2321972ee5cf159025a63d0736400c8ab3c3ecced6074fc35b7ecac7c86a205dd684f8cf36ba41d99233c13c7b675b01a4476a7a6b525335fe6b03a153c24ac6b5156277312cf473258a83e44a1a7176dbbedd68c220bfc47e891c10bee02c377d6d86da7abc57bc5631d9fb5857e923f7bd1a7bef98816071791b53fe0f628e88bc4c2861dfa769aee13221bd89c3fb5bc9af0c1628d1f82beb69975d1f519506ae71971b6cce8479040b438d5a6141e3310fd2db2c7d707c68f68b538c46aa2289753e7465dc9dd84c20ab034cb59e63e682088dfc7235c9fedb04245a4659cf311f5a302315f27d3159dfad423198988e847e0141b60caac2e9d0bf9622da41ebfb09edfed055678c05760074ecab0ade492cb97213cbb8b15689872bf79909a8f34c2f5a2dc1ab144362f203388f934e507ef20f95910e08fb584ed65ae6fca6c2e765785c46cbb4bdfa05c02414e68c80d0d6f0905bcf3e16db2443f296abeed16b48e7d9b25513e2061cc3addb4e47373aacbf150c0528c370a3e31e208bbab2eb675dc45102e78380dc5bf5233fc6c7bcf8b6222de24b1d6fe8eb811510314a03f9b259ccfe756ea83f4a0fa6aec9a7696ccb8075581227c46f3fb6284dd2f27e50bae82b6b1836e2a0705cdd80813ea2079ab3c505f0f9e92802fb0477a2c0f0427349f2e849ccd2044ea5e3a849388feef7e678d979110db370e1716b8086eea4203d94f0813f1c842b84f364e18ac804a23ac4e1ebe2735bd782e54a2d983dc7e9f333ac7bd2f28c98255146234aaf1778510e1d4f76687142843e4df0c800abbf908ae9f7cb80ed843400b0265882ee20d1978c2533fc8c109a4d400e7bcf6a22c6efb7504cb63e7e48e7a4b71c79a731c04cbcf9de1f6452a2b1bceccb325020f83ae5820d396000e02afdae2f2ed7a6a661b0f61d76fc08c7b065cedc5af29441d95258c97a9d689494e314e024e4b48265fff7fd757cfcc2b408b7da31b4e265067e7e2ca9810b5e1affbd8fcb4ba143a8798150cd867484f3868fb423b9a1a6a2762cfd57e5c2ada47ebb908f900a072ed08bf8ecb52f2deb6e4d994db80534c4f09d379e747f407879095fda4154dd4107a3b936c506566d516201ff110f4ba1b3d5a50d4b2d1862b527a96db8b734b3af1b36bb9fa61ca60523a261f6d354f79d8e54dca9a89f3e90948be36a1e6e755d476ad191fc155509dd480404dd792532e431b9e4d3df2cad38163b1b40224aae331c24879043032bc370f7977303341beb8d9ce2b359748b25830ec96898545c3123212329d256d403b03b86284090df9872a5222a4817acad9e49a623fd671facacb8957dbd04c021a5b10c32d6eae9b05e26112587f620a317b1d2fec2a92f03261d1ce170fbfdab09bff3266051d5695dac95dbc8039f4a362f17ecc1bddf87eab875676e0b7ed81fe77cc16a5ae12e2a2c1894179c2c4d183610f88d90d3faa4825b4254aa6ac95046fe90948ce5f5220a94672d73a852a5e4efe248bf6951d61816b876cd50349efc4b57bf44d0100cda15561ebfa22726e6b523ba22b00971f462a3529cd82eba951ccdf41466a90d963d97541dee8c1abd61d4a8d962d81c644877e7193913d24289a39a9065af65751e2af01ab7fef8d667d0433e9bc5126a5738f35c7fd2aa70daa7b2379970bf8b7a2a5b45139869db041e093fa9c5a20c56bd02d44d697a902aeb40c5cd5a1c160565480ac3f269133725ca44d4692f3b1e8b21e1ee61e0bf13c433d1601498bcc7a7a3896e00364426c9713139b8924d746a4cc1e8316125f89c59c1cba53c0ebeeca048cbe20780837444566c172e581bf4e3ddc26731bbb0cdd4c4fa3a8fcd7ca85c5998d71bdd9658eb31240f5f27900ea8c856e526737e849931d1cc13b66af8caa18d36021bdfb255dd8e696ed33e8c9a1b04c55b6a5a0b002b6e653937179ffb1c554e41e2c98877394429bdaa3e954ed88f9d6f8fdfbc824343b804d902ce7195b45447a4bb84137b23ab22d16fa76f190a88cc0087b52f6bfcfd4710e8baa15d0f289e2e989d7c7adc35e3b5f51e05ec8ad1c677f61c81b98fc553ab7904f9208cea88eeeb71b7725dce96d067a7dc92c9d9a39532be2f93b0bc7378e0ecf5e4e9eba693d861dba42cbd2773dcc09847348395e51e235557fb3fcff74233195454f7ccbfb62cc8d349c8f467f1f6b2b67abe4b6eba226e36d6650a48492897c682dfa36573685d9269fe03b9c1d333cdaccf1ab39812ce7ddb700a174cd86638c70d579d4d1140a2163c04b0269679706907965d5346e76027c5feb230724d518299f466d20070a780abf5cd0ba58b7dcef9f8706145bdd924d666929da33ac6d081a77d7880029b8ee9a1d9eeda3066ec6e70dfd6a7aba520a0d45fc67bbe815e91434653ced825f27c68362d49f4579be1559b4def0c289c53b06eac99c7acc92a9bb32bc79a3ed4fb13ab0d7b70de6c1467428c82cbaf321835c2a1a0b44d8f73e58641c7a6951ea99e23fadd353f1aaf27dfdf818c13831aec05cd2c181d091f7952981c7e1e88cf8e60717aa7b7a25c78e88ed8cf726c2e9f1f20c639bac2996fa3315f1fd1cc0f5bcafb659772276fef2f0b6ef60c00b7a236c5e3e34f75e59b7296b3148f7a657119cbe2483740e280f72f6ba47c308235abcc88460f420923e6b965f8336deb60f3c9fab5e222186e0534cddf7d8a8c8a6d541a2fda6bb3e6dc321ffe6117b1d1e8214c709bc2a7f575c8e12800e0909c2c5633d253cebb77474d57bca08b00a3e911d8f4ab73003cc69360753054160eb0bcb359e3d3e9bf70d7c5b65986546e0e362aa197d6818f52d5f7f58b63685a838dcee7103fa7540345f891d18a66ef4c58c9e2c5e5cb4e68a3bfb5ff66df815068988e9469de1e314d3d9b21bcccf5790c30ca0e5efd580e40d435e328bbce21d6402c53528d53f5381ebde8ff29f35d1f8adca3f1cc28fa217d408b073009a2968e66720cde2c5811e5a2d3388f3f05c741182d79145cfcf10bebc937e9bead10a1fb944014e3ecf795c54cf3e06e6688048a339b1816d61c8a9cb2949bd0e8232eaea4e509941cb9b6a39ab71804df3898360e0e4d46bab5d3089abb6ca59179c056dee443392ccb644ec4703f3952fe1a71d858e011672d4026200017755e72bd3482e307670d83bfd67cadb8f50ec9535033d4370a00448318de5921db3209fab908a31a46a298705670172fabfd28b6c0880b2e8a4664b45c5805739993a9fcca6c879a88e1f082c1f2ca4d9299eea838a13bb71c0cf17bf94d82b98837a8863832aa05aa741b587eeb33547505d36a5b1b725590d92aa3df9538379bd5a6f7a750c4b50528511067fb2148f7b08a9d8372a24f96f49818cab9436e6b084dca0a3bea9bb7f4e457d801c2acefa5693b5b21c064f29fcc01fd9e06d424be982985a417192158f96ee8d26d5d7704e0afb1660e1ca918b5a2905de62b0b78bb48e4f90146549ce4412a58c81ea109d8882a05282abd29c0afc732208701da8e24b29c1ff65bf2a94ec899a719548bc0b217e5af929114813ce2215853671ddc30253615042e609479e5b7b9fecbfd5b2e829c65b19b38ed16ca8063344437b66cddf70ff19a65fc44e451244fec28bd834bec2bbd7134a085626162fc85d0297dd147481bf597fef5ee37cc4eabe4bbafce94c8fabd21ab7fe53681d99830840b4535567e3267cb7bf3b5857818d4d85a71229c3e14abbbe7542297396243980fccaa2c88f71f36b0ceb335394150dd80b4566dafb62c2c6535cb7a8b3d34f0472efe27c03126e5fea6b3c29a0a99100f336c238df4fe9bcd8cabaf1d1a903800e591835609e0e8678d448cc77720258057dc195f8f59853a20ef524149ddde8b9148cc2ddfc80c6424b405ee1b69302110736889c427add1c0fcc113e0c803894ffd57dbd5e01103ad6e5d3d18ec68f05555c93e9a9bb277fe86684aa435c2ed13a604236a1736581218b15587d6aae81a37b83da96bc504e566dee87fad29ba5f17cc7503579c18fb138d043e457f0f4c1202267209a842081b92a86a58c9ca23b1492190afc0334452d7c4f18ef0f322edcd615731ef476994562077db6da4dc816fca09ba9c47fca0014f4b4ce0bebb8a7f985967f52021ea898ccf13dab6b2bf2b541e3c289a61c809a29e1ec94fe085790396215257a0c73cb79a452e532452ea6ea32da0cea8e878358ec6ed3f1a084237d4fff0264aab712a1551afa117884a2412749309c4bd73a85e2a9b87d658762348551d1d747971887f3858138cad5c784dddd5670e2efd173792da25b5465ef600d408b2b10e168d795908825f808c7d636e2b1c8fd3b885fd0dfe6e422c0e04b10cc960b7b8a8c6c743be5da2c6b411d5c453488fad414a5641a0db5447a1fb8301773c75482490b48fa4c1fc3e42be737854e399a7d04c863d4cf8485ec5b485271dd2b87ea43102a9f3f6e4084bcb017ae2953419ff2fa1aa42330b2932f28a73865a9412dccd4abf4ceb203e2c11ca206ad13fe632400e0c2a1a6769f16f014a7bd2264760391499cbce118238dded3ab3ced0a1820fec33494feb7a6fa900ffdab45de59f23f15fcf33600e194a9b10f8dd33c8e1e623b742c53af378d90f8823dfb92d281235cfbd05a4485f1e5d04c28a47aaf1aa78da4554b8d47c691225f8a31c9ef0ff65c964fbf6484a19a074b84e6738227bd91e0a0b79d014ad09beb1b5621a467fef8b7cd6320c441fc56c1e41b578b52fed0223749ae6616d2414a516e6554a0019229dbae9283313c02625b8f767410b509f2e0306593efc311d06d7eab78a97d1a3aef85ea5889173f01fe57a2ee3abf8a4fa43c49bf83da3e2d23f24dfc75b86994f33e31803d3e59a334390b1e4315085d6f560615a92a22b3b33d3c0e4dc367c94d7e48c65c1ebe08529f0f232f11394261e6a94f681599ed7b28acfef9d17c752b6e3899426ff7d374c255bd7f6dd900c290433baa5f4fbc1c07ee9c426027f386c3b59191c25c1dee3f307785828febb3265f1cd6f359d4a97c013438e8c2e5cc47d9398af61dc359e3b03107299fc0d5171ee950812273674b8e59e7ef7d5f90f929682fe559c57ca029a02ee81cbd334b2147d5a343d80cc147184d74a99241a244bf7b9684198dbd53323acfd0058e76e43c68c65ac5da8d5713f07845985972734f75ee773f637bb07614c5e575c5bb3b7532f2917e6ad4485ae9d492025147bb9afe8b29f85e19bcf758fdfab56e01f15ca1da349666c16106d11b3606a277320578fbb30aeac7d618eb8ed9984e205c2c4bc23e43f212bbe6fe37df2a4b95fc0b3b6c8083bbee592f14e421ea12713098b984b8f07ec100c0bb0a7db3762d22d34be36b2e32776a582bc8e0f7b03a3c49be2940343a69fa0f89a5ea4f80482d5543623f732365b3d407c1393072bd89f57105d4d9b1bc0eca800a496405dc777c3c4a7f3e4843f5ee19ac26d746cf1b3a66c67fa52b6f93c6a103e08255ce7ac52c64de026cfccac1bf00f009c6f7081895233c638288cd37b5991df323714aad23e4b67d4fa56def8a424bc03f7caaaf15725b6cb7de0548abab2654a9e6ef32939876eda1740217d803aa18be2acf0cc8e5ff01f9f5373a9cce004de266f0c469001782d41c92257db2c47042db3d129dcbc406bc1872c1b819b395b2023054132c003d785e292a0acf61e82d759b3a10b1004283a150f44f2396aa3b75125a20897bba206c2b2bce85e3db6f576f5970a95d83c217467348b2f078508248c34875d434256f6087b572e531b86624e6dc135507e8aff0e39dd1443e95c06a29fd3ade0d7505ca353f93a6d5a68f88705301f46f7903f76929d63aaf3d641ac09ac05578f480c9c1dc0560690c7fc87b485861dd23d4f8e4d0a582fa9059564e64801a3eb38b6136f915e7db440de195c7f381cd6479a07675d3b34572b20c3e76698f02c3c500a3046bc6bdc6bbba66a7499307cb7a073047077ca98ffb1dd6170e82d1f99ba2095c5969ce0e2d7b0b934479d81a276dacf9f24e31aa3aa0904c3139a43ee3074242e85025ab87957dc907e43a5c1309b18d3c995aed8b77e744911901e530c03629f78dfad0bf7f272276a1944465456a47aa358468483ef6339b64341ecd730b586b5ab40247802198e44239afd05670bf16f7f1a396e10b8bf631bcc3cea05b29a64bfb165db99dbccc0a83913b01f1cc1948581ddbe76daa2ea336f393ab4dae9c2aef90881047826a7e262dc03c07c0cc0d00ab465a766eab432469931d7790a60c3ded4d9f78671b3e113aaa3b400a33be1615acf194f93251a55faa2f80454cb228dea72adb340fd5a3e6a06d0a5fbaed86c1a9547a7c3e99294ae362e45f39113a961d3407d373c1d18006c0655a432fd292282b81058bfbbc3c5bea744c086356d0716cb9261380e359ca1e7c99fc94a5c368a45b5c0787fbecca40c638d204ce7ff75867b1739b41177034d8b4468878a6af536564289461a1ce546a7aa3c3bcfd5c4e6a912ddeaefac97f66f50df9047b3d6347b4e218212e4ce7280b7d61696dfa3b1dcc15cac03b757f578578538def21b3ca073e9f3e07d49956170db6099aae195fe58bd27428ab52a579220ae79c92ebb9ebc4463d954cc0623b16e452a4aa476f26d16e5b9753fbe45742624b5c841dc067c82c2a6eaeeb3987b499fa6b9eb2154b03cf0284e2c5cdb25bfe3ec96053284f0e18558bd4e7be86cce00b8edfb8c3ee76f3c41405832d464f83e968ec52a055590630efd38862bef1c21dfa21cbe5991d5b9a888898aa0e4bd15a09737175fb111f356f1e527f225079e39b7d62c41b157c78d4d9ee89c187e0ad22295806940c4641cc4337e79dfcc8693a84b1aee12013fd2f9b1e0641b886f49d7b00214731f88afb8ddab263e628b1caad7896ee75efe3641759fa11fdd097584cada141d603661f9473935661c11f491975e1c8943d58f223a5f329472085a4d0166f873065fa8ee43162d4d9be74a60562145cb9e7701f0d219d2850fae5109703ae739be0820db70a348c57de42a779c609735b083f8c03a067921551d94e28094d2b25359cb20da509331642c49c6828d68fb78f6e96f45e486661aa04ff86b11533d2ce068ab01461d2d0a8c469c9f57136a466b1416259c5075368068e86405d5ee34122f74bf669d776f8b7f435786cdb4265dd808ea2ea8d93bf0179d7f41dd18a30283c354a61ab11744a22a7149d19529cecc95ae5d035aa49aaeb960bc00a1bd6bc0d43443a8c2312fd14f199962087d51df825de6021e1f9c5476bf2a8791fd1f0a622de8838d36c848678ee8b61f93733b23b16bc5a318b999ad295d169c3555f1c3ed00b3c53a152b8bf5232772f61016b123191c144d479a948e5ccc197c30812088ef1af120b559e8ddf769ee1a32fe387e4d39b192618d1ad562af97dcbe4460045c12a17c58624bf864209fc05a0ce21077f5d8690d4d3e0619766eed1a10d95eb40f39c1d71236c514192dc58e6c97d26cd13b9f04629908453641625f3907328d83539350cdb92eaf9b0708e7bb3bed0dd9dc416788f2b1ce5492aa8c6342109edc2568f02f81229183272594c99a516ff461bde96c357b3e7d47c6d0baaa2330a70204070be49e7679a458bd53aca376ed9f8f4d3cb7097b9e1cc1f0ce07eb68ddcff00e074b16aff71e269a0a4c5f1c8777dde5d8896143472d79d4036ca1ae1a6aaa241a8da45b053164f50f69025997df194ae76defdd00c0e30537b70d90c6ee1b0fe786ffccfd23ee90a8960e72d059b6c381f5506427c3b366c71dea2e7b2c29e7653722e0f31ca398dbba5c8a8d3f799d743df59bee3fb2e9f112f164e0f6cdff036e3481e1793c72fd19042ac717c3544e094142d48526704b920f5732583e1cc3b8ffdae4c4e333bd97859a49f452fe73b8161ba8a2137d65814955f77cd41283e5c87d99c2e465fd3fa3e03399d20ab797f4da97894dfa6765dc91d387fe7c5cc50d52a5848f50e0a4d26d71728a57c865f560e698b5ed32abe1eddfc9f108e94bdd7255acd464760e393d33b93e4612eea85efb9c548cdd7784e80211a489b095d56e284fddd5e0798d5f5ecb6e99f17ee62c56db873ad6fe7a2450e7c75388b6ccf31d602fc2462af8f3f942847a4effbdec54f345ac83bb5908b6531036e0ba4e2200f079fba830d7d2bac6fe15bb861f4ece29c8b28cedb9b809b311ea720e9a27c658d69389bb80907e0b0815701f5709080d9b62bc003b2784f8af4f1e93ed1ae164905b34f6332a7c3698210f35a10b915e92dad525aac7f176a35d84fb4bcc21c223d6a080904a49d82900b683992120b31b635644840fc9c69e5ef4de04ae804dc6da411062edc7d05d1dbb64f6e94120fdaf402d1bd13efc2c01198611d5678c801800f2ef1ac894ba258618dd6b6e0321389507005f2a61567b85098d8136bb12243f1c656cb0029379c6147f59c67d92f66590cea6ed36d72d05e880ae13d82fb28386f8163bc546275431c08746cbf3ce536e1c866f85a0424d5aab5b52f8cdbed064a8869220567f91b18766727cb464950388e6749b13a762c3c825459134b7f5b94239bebfada321e9f9fd8e3ed33aa3fe93ed8f3b3240ab0ea2a1e62ed7d90826f8b976975e606d101a143b8bb4cfc6a9746e22122a012e64aa1dc64aa0b9cb0e39c401771e8c00b1da0c9b0dd817ee90a2bbac4918b50f18d68a015ebb79259c6a5c8e73c02c9ce2625b07ea808610070c3bc4d88033c53f392b2bf7bed00838e25126bb88bc0eaf95e1115bc4c1c10a9e253f46fce00c881646bb2045324dd19f38e9a65b787f419ed59f9ab67d9f42edd9ec3ff0583c8da5790ad0fc2003a23b3400097b7f3c89374683d78f8b291dcc6f252dc259dcf543fea0d891e79a62f39ea9efc278de231ad8479647c2734a4a6b07c08709a3dd719dc5c04fdf653269325d076736be1ce8259463f72266dc8ac1bace34e6ba2d830921d02e848e588fa4e8b27fd4e2f448236cb15955eac4f35a871d043b17e3e6c0dd818ed4dd18bda61c0c41f020acf304c30e9aba3608c0419895e0ec386e991ccf63c2c0c738fea2629c7cd24835c7b109b3551532eaef8be161756d0eab22a0f901f2ab916dce7546a804eb3f57ea0e8a04e04bb7b633984ada7747f5cfc63f84d9e43b95075d5162eb926af67cf8cc66daf7247c703aba84981118a51baec475336bfcd5c77575233680ec2a33aa694cffc82a6d5d41042b5304721599d98ae6201c0fb722ba4559494c127aee763fff3aef69dcafcd83522abd24c27cbe1198a40f413a33d8dbd8695cd99eb6d30650b3b1a1eff2ebc378818e42d8902dac326800251b11160c218788d8515aeb00236c602e9df237837914edb48d7eb3fb5337f18233cdb5f5751c63edc268d864538d2b084d2e8e7033260e16c260ebe802e9bf2d08e156e7216c570ab33a0d31b6c83415c6c60aaa9dcd8b81644fa8f836d6d377adbcbb946f2d195429228465576d23ca9b53caac25452175409b8940eb2835e2bea513dada38903102580adc63402159a63dac82e02ce25aa3af49e424684c93372e29fa0cdb0c373769f68fd8806064eb36496c7788c686e4b40a5036342b9d6a672778eabb49ff07e82f481ecd8f60925d3022c80c603c6b2e32ce2b92bdeccd157f0388d3ee16412d381600e16375c8476bd25fa63f9b923e1ef96218a3f717c4017dac066eceed07f62e4ad22b256e2f33b4694896e5c7f0cd5fdf05c2d43b7b29e38c4a6b319928e105025e748556d2b0ef93d9501f758f858f2d76050e648afe03a0fac9a427301dabcb7e348aa789ec7b76aaca146f9bf6660f9196d7eeac5d10e818a87182b7dbc22ce10ade200696c1da0c5f29c4dcbc7e8db3267aaa10df2465371908443cb6c74d9871ee56d2939d0ae24b13d37278c2b81347c41e73d77b05a8fc24f9404272ba9b504a509647bad829e2876712e34a61461ee9167683d52cc0617cec808c18332afb54a6466f4d7258dc17158c257177efd75fea01dcaa52cfb1918f9360974c453e62eb46e148cfc448cf2399b3e52aeaeaeb9e9525008764e9a7083d887ef5aaaf7e17cff1819b2b42fa075da05ef38dc58375ceb02ca279909c62dbf6a83eed34215bbfd6ac6b8157111c3073554f4c220d5e1abfc37dbabf05fd4052ad73882d670e4d7473d433bc7873757061e423534342ebc2525d6d95c8522ebcaf95088668322fa7e6c1dae9d6919fd89a7ac4d4736360240de799626e04751a25bd134ce5cfa99bd4cad7e7a74081ca0c16b7b52f13ed4d8a30249631cddee8b0aa929b55af0b46c60d411dd269e662382fd1636110d771914eb8b140841296de61c6cd334799fc23e268226a7b2e6ecf6238c34633e21f33e85eee626d5f0bd3ec928dee7d5d05424162ac016aa76ab4fa476bbb12743e427c242179dd2d2405087d432c35fa96cb50782f7cda5ceb8ec936f9c7b46fa29c4d114f0861767618764750af751b85424ef6d1bb1b20c96d1a7d791d61d31a5e40786d5bed108d4b10744b4b6e169d3bf1fd086bd1c9476e7b6bb7ecb0d5488dedc30c3184ed7abc11f607c0c02cff6cacd74d8cf4a52cf57dadcbf32a56c6f85b14642bb6dd683e51f0d1817fe80fb02f5fea0990f3b71720adb67aef31722029ab938d93d8d0fda3ad33cc291dc04abf09f82e827d6840387579f575937cce28072ba8e58fecc6de0195621c32d64ecfd712b3bd80ab2a0af00c598a334e878e482bc4a7c58b486ed17b67f5616b10a40efd702b6281af5ee8a1f32d489e777f854c080a181986f22256c1b81859daa4fcb7dc5f9d54ea804e1189ae3e1a3daece9691f5919177c5871f1036efd93b0fb795438374527f298755e3d96ca82fad2b83caa42b55f00593af9cbcfad4856585d78fea9e762145c0a5a9714a128c42aeaa2c7a20909d2dd9def000c0586ddb08dd36f6b0a13fbd5d4c11e0224a845b9622387e35ad81fab0e092cd1d488495dc9dcf21511cb9f4dccd0679e17580834c26f73ac2f54c7c25a136fd2d496f28c3e960fae1de54625a551e6b67e3424b3d9ef2e436c5f02d36e6601d1ce7d790b862262760d7d7993d2f6616c195ac8ea52cb80d93593368f444133152d65828215801e13c134a5c3a75d23f41975fe0b5557099feee066a1760acb8de393cee09a2240ee70e5e77c92b883169bc3568f86ed40781444beecefe061f3e4a8f9b5c0ae6793848b64956b243d15ed68bee1059dd28a343d5541f38d5c58bdfae611b119ddca2bceb203ab3b8071835dc56da3f0823a8087d1536a3e00d2187488afd06efffeafbbe80383383da37352d990772b728d3a37d88a7690114b55af25c90c3f2bdd92c0611a36961e5363f484bcbbc48867234541cb6cdee0b29534e0aed20ad49d80f34bab329e88f0148da76146e8d29368a4474f44f53f74ab54a0d1b5ec9fd1ba1dee3b051fedf3885cf83722d800074ad5e2efbba76cc473b02f234a1f5a4b1c59f490a88ac6ea217a654650aa4144ea9b9256177f877e3deff060ccb99d46c24bc3f7fdea3babf6712a41e0d38e406bbf1be48582319077be5908fd00598c5057dd14722c5debb96b7d74487e286d465816a8f54ca0dbd067d784c2fc42a7977e6468c9f857e24971ef8ddc064eb1aa8b698dcb40655bf2429dd19fa7cbf0875da9afb9094872e20f58aaa8284e79b016acb98d005b7944f0cc5384b9049336a83591a0a9f4524f3aab3896db36d9883af018a03220e72a358649f217f6c355c87a4cd1274882d355ec683be6690c72be61b76bf1545d0219623b712d346fde8619ecf27057532716db253c70d2cf6fe3fd0d68ac09f756708b75fee12faeca77e91e15de2083b87f075a98847d86670d1071ed4ad1365b63b5d38253b964dfa11c22782ea8c36ac5f05a9f05e32c45a987ee5bde6335c49e5108498dbda3478faff27b5f8d9506610d4830d1305341f3018f39727b96c9d32ba1ee88f0e6a6b52cc3c9b1b001e01bc0f04bae83e925820da554d07ea83865c3f5606fa78abc0e385f86d38a78dd78ee1d6987be0593a3e5503586910ff837384128096211ec601073408a27491c7913a8857a8a7269490d29f2ac77679a7b6d8847513926477db7bcb2da54c49a60f092909cf08b7a68734294dc0982e4ae189ebcf492258b080a504212c9a90028b29376effb7931f25381943022e26dcfeb8c922573451e3d23985876c4914c0b8fe5489281cb94d4537414446016eedeeee6e3944c483eb1faf88d245114240820f43f0a0d556d8a08ac915f563b1200b1211b9f6fa3bedadbb6e283001509882fbc0c6918b4395c4137c2e2f112aba5f14baf06d5c5e624594957fbcbef28faa58f57986125596813e302ce33c372c03859c79a0cb8808e47122cd7297759e66c112d0e0c6a7e1a4453fdfe02ed07966e0e249b3f866456a3fd3864d3c28d4306e41b00b7c228eb8f0bd06370984429487714024f00b8c11eeb40c4ca2a88a8ee1255618a9b1b5be67dd68456f9e8ef9665c1df3c1d053049f25493ae6e3a00bff6326401df3f1162ff810f670e107772e51c77cd007fa300e226e04091e2598b8713fe761d2c48bfb7915171addcf9d347fcea3e2812ec8e332dc725d087b103ac27e42a0171b398f50083ae68623a31b200818076783baba81c969ad6e2614e217c8135f1128c298c427f65cf8cee32eef819ff3f8d1853cce037d7c60923b045fc660e33270480842283497d8984ca0a66a2b7a2a96ea320e2e89bafa9687a163562c1bbd8281e1b97148ba45b29192a84e9f7a01cae58d2116ceeedd0029ba6fe81868571408cbe860977ed5cf0c7435e9425f806554332c2adc762f04cdeae715dff00346a1d31fa40a246e777fffc2a6fcb8c9fdf8898e2e6e73c13604f4720618c70c7c4f9041927b2b1efef14d4758121b334348ea7657d5c0ad2ae59c1884c1fc31c7bec6a4ee104208614308e177634d18c29c0bef10840019da6666662caa5f7e8c6a34dd6151e5973e7ef7dcd3342c7d8bb33026b71a512112cf75ac018537dec86dc40c5d34f1d811326caf36abc3a89163921b8de2b3e5da3149d4f72a87bc686c542e15cfebb5f2823af4551ef62cde7c9595148f8ba6e1eb753a75afd7eb958ae1f52ad15377695e257aea4e29bbb3d32a9577d79dbadf21bf93749a94c41a2dd79f552b0cc8ef9e07fdd38d53473303c3cd82b9d941ad30d075a6c741ed28fdb438a72f7df7d32bbdf4e6530ffbd3cc227704b9498cab33a79f8f9d7e4a4c9a3089493775f234b91a67d06051a9aca46878f267d067f1b05ff1b457f1b24f9d9ea6553c6ea5a2a93b4d8ea341490d266f6787befc1decbb9f27da61df7d4e8b855adddd2ce5067cc02c3d4e9d5fb23b24dfec907ed33ea38fcd97bfe540215a4e476e0b1bb76c7dc3861e2e53e3dd9dc85db007009efc1a1ef62baf7b1a5ee95533381613e76e03511349ec2786bd0e138b4444372c18663b407400c7e606f8c5fd8499ead7c02d8ec5eeec742f7fa7f4589cd5ee903fb1d206652acc8d86a9bf3909063ea079b8d5cdfc1a7a9ba448293d95fef4392de9cdc7bcd3574fdee680e480d7f435704b367723bfd6b0e1552a959f41bb2a7ef1ff548d81f9f2799c1e7bd3aa25bd55db9b1b6b43b4f23b34dddcea66d552b24b5add25f5f4735ad58b97ba8cfc54ea5754b097bf03ab8f837accb257bb433eb53834655718903f9f87fc89bd0e28d34bec4f56076ad9aea2e7b7460f4c87c7d56303d18f6f393dab155e31340bd2ae03dd7fa6297c34e3fd450344e368e5883b2a1d998ee61176549ff3547ec5a35f324d6cd52b251aa097108d100e9db5a1594e24a1d4fe4d42a92c2d28336d90245c8f47401c88eaec2748bdea2107a47b95ad815b29bbea293fbe1294055203b7767654fef43bf4eb77f395c42fbd5df5af7c4e8bc5ced363bfe3f4d8b33cf639b57e4905a9f7c781882052ffc11d8eba30aabf6a580608c7c038fa9da7b8dec5f5db16077ef1d7a2d4d3d7677e41bde94b9ffa8f8f6a5637384a70e6ab50ec79b8932bb8a08cd64a54c26277d45fb138d4d6c751b1ab78fa94474fcf63fee97530d9eea75d451ef2abd5a1647915ed0d47d3341d73d0e171f5f824f9c1e1a4232496943835853e2e2380b7f9ffaa2a003d3d3dfc7a4fbe00bcf901f0561e005ee96b78a85f792aa6a7e9f7b8b5d9403b0363e2685083a8396019550d5886f441803db8ae3557036c65bfd9dc34ecb999f134dd148032d342249a7085c323f8c555f433d2c64653d2d86f627914a86694be658548b454905bc5f8188b8df171566c2c591bac8d8a97f250272fa324a11993c717c67b3a989bd233077b9ae53f0512e91868e4c665e6fbdbd870430e2eb3bd1f419d21a222a3239d92edbc483d6ee9803e98030d11c19e9bacb39ca6a728f86fa62a4c4eaebfc949c7c0d6ea615c55f533fcaae1cd5f79f26978f467785886f2b64779dcda60dc1dc667aedbb88b9b884c434239f277cc97cf2f4d38a79c96909c303033935a1d4a2f4b3a74dc724ff5ee5608f5342d2703a08cb43a268eebbcc46b4ace52f7e25e5372963ad3a9f4dcc714caee34df6816d5c2a8f136b760d7adda761dc55e7e4e6b7aa9971eea4f5ef726aff45e70168eb9ebac30303ff53be6a7ba9fdf76877c94c529bd6c993c6e6dd583905bdd945a25aff3b81b373c36eee251c854048560b374458cf16d5ca6c6ab62acaa67c1dc95ff66f805008f7e0d2ffb95579f8677fa19def62a4f7b168ffb158fc4e2716b25c618637ca53ac6481f7b1e59363fcbec8adf631a238c372b198559fc48ed8dcf30e63b80c7b87587cae834bdfc1410954701bc2b7667a73efd9dd3a33c6ea9d889fd0efa184e7dfa6d7764279abe5bf68dfa9cd6a6d22a2a2958c5cacf9223a4a8b008a3ff0c6111493e4362c4f82aa91e6a72633fcae2981ecb2c4ee9a70ea5a75f3d1d94ec28d9553ffdfaf47530953e7b1ed5d2cfbe647feb5a9d077776b8dfd9647c8c6646242ec648735a4897d3b1e3c146a1a8fe3a6c62a0a90efd21121d63e3320110c07f550580ff9ec67facd57f30aaff66667c2c004f7e00bcf900f056be86977a1adeca5b4595b78a33bc551480c7ad0078dc0280c7ad1a1eb7561eb76878dc9ae1714be5716b53b9b79d8181819cf75af1d3779915f3904f7f3e467fda15632fed8a9f5bf2b9253df945fdc82fd54399f94c03ca4c1b43bb401969edf56718132d1fe0bacb9c58fefbaaa298895f2c74695a8c0a9f03129fe356fd2c9e921ab8a5b23b3bf2577e673e8bc7ad8d860351e1c355db1bcc522ba5cae3ac58d4abd81d3385b2dcf294c43779ab56f556b1e4ad62b7b343fa1dedb7dfc97e077bc9797ca767ae43e32efe100aa1394d4941d836861b070988320419c4922831f025c0f08201b79fb64a7f61081883c50080a1841b7f7a0f524bc010220854e4400788c9912b86d0bd85e6babb6377f700b6d0c590d41680b080db94c6cb5e18a28447e0e8074eb45064005af0c1155b2072802c8471fbb9d301122f9280048991d9e36a1684ae9ef82ee31fe92aa142178450cef98fd939a7b4f0b5a8f1db53f10bc31f1b904145111fa8284205ec8681c160b0f830375a06070c2ff8e226b49fdc8efeea1858ff7e3040b032ee07af3fcf34ab87fac1c060944e51a7853e301d336937e523cdaa2eb30076e97ed66d1898c487a86e5b82f18521ee576160364412496cdb16050c32548a6e8c44514a8c441dc3373ae998f945373e1466969721d48926af07d39fe6349d4cef3edf6432991e7ad3e4c529474cea6e3199b439654b29a5849e3c52336e8333468d71f48d1d2d31c7d8a3e20318da2149040ada218ccb413b4021810b813bdb9b36121dd568a459374986a0d3326d266fc5c3f4a73f599c1c9fa1293a2d9375a2d62655585654585854dc003e3bec782b3ecb8a4a7c090d4526b149c7c02a3a4666ed5d7f9685fa45a24804a563eac78f45f14934ea181523cd8a2a36361122ad3c46e9980f46a2e8a46378491936b8f16351c7d0902275a0b8f1254fc71029c3c98d2f8db40c017e384887891b3f1af984421df34523377e8c404d3aa6b57063731588408c231681cb01b9b69149b35efc82455163918d44cd8a5f81fa02c206575e2e420e98dc8e3d2e93fad3bb9d298baaa13a45d3f6d5fb52a54814bf70a30b3551a430eae810cdfb41212e126144a89f1fc523ef7199d853d4ac48abcf4ccf8d918805f32cd55727b0c94ca2c2187b488861749522ce0180a40c24dcd233894a9bd564d4396d94241a6ddb6817f8249a6a02db7e15ca46a1a8fc3a6c6288a129b2d18e81cf27265498516ccaf6a237a55b46ca81b0b7801cedf759c6d4213be49e0b4d2e9eca0fe33511b9d099d9b4e3844f45eb43daba6245fd2a7f0f5a10bed79cb8eeee2b1a4ad7bf74fdcb09122f129d150fd8920f5bd2f2905ad152777177f7ee9cf606629b9ffaee49af3dd69033f24d69ad2ad5fffcac2c0d3bc3aa2c8b5db12a366551dc66a9fd62128752c4d8185154ccc7e097f9de0556c0fce66a4846aa7cecbbc00f38e95c9deb63300ebeada3a3a3dab4d67115b9cb65b29fef4c5a26a3f355f45a769749273667e6505c0abfcce8844f55349fd25a55aaa11f9a2b1e3a94a448d18187ecebe354bbe251fafad5ea50bff4dcc2c9be5a76ea2e50c98a4ee8e383c3d1110ded2aa7a28fcd27f1ebfde532954e157dacdeb922545d4ad4893cd16793a24901aa9fbbee74f9eb5dc86572725a3ee446137bc59f982385c01c97281dd6dd4a577c018b9fedc29f4ea4d02e7c29852228298a5320ed800a32e82efc864f217c61c1897f95ca7fb02ef604cce2c2aff055f05f08f7d435d96a7c8d1629638c3146b7d5bdb071cb177d7845a8b64ffb683a9793e12ceb55a60a45b5cee446eb4d3a5bc58db6e7c628ead757e097f89fbb17ac8068c42f463e1fa2b456952a7eac78f47abd5e2ff8822fe8822ee872411774f5286277c945303ea546df7f5c66f3b822548d8ea4b83e951bddf8fe93d38aef2f27621cf0a34fd6a42cb67899d425e0f257bacc2280cb2d32972570f95300bcf0a10dc4ac502155b91f4ba573f04b3f0dac001ded0281db4e603e08e48043b33a8ccaf7e3a31d6c27f8ccd31e421b97e9d606bf87cbacfae90d2eb33d8ff9dbc6aded75c09aa7ccec57b567aee6663e4df7775bce05353e6b96c453e3cff0b430eec29e036384c1c62d1b7492a740c02ff303ac80d9835f5ae0e2a422aa74899a657e39e9dcb6b109557e7cee067be6e62caa0d7fda074442df0daa4b7feae08bc4c4dc094a5f6bb22c2b79f06ef439161e54cac920ffe33e485f9abadbc6bde46ab8971ce578a39d4926b64acf7dc9cb9ea98d9c17b3e47d2419d3535aa24490abb6cacb795bc9e3db5cc9a32fb90b4c93b763fa1fbe65b24ae8d328d574a4aeeb4a1d4d07f960743968e7753fd69d0f733968c775b78c542ad9fad4f356f4e1a56ff256d4726b125149a4eac15b7ae66a4aa58e560f05f06e52d420f7ebfe07e9d2a730769de77da9cbd41e513f7a276b94ce87a46dce8dfb9c4be23c582a957e06be25bbaaefa3f4f44b96e6dccecbbc8f74b3afa5ac7bae23090115b07d469a3f67cb66a3461ffb2ca394e7d65c0b68631886d14cfbcdeb6193db6bbf6ddbf6ed91ec47e5f6f4b7a61a9661148bd09bb03451bf0a3fd3bc1eb4e748a4ed699ad348af699af699976d9bb679f3b52cba0cfadb637cf71835961f44969292b68da6495bf4b6e86d9a9669defc6cdac0eccb124bd1822d2e733494ee8cf1878dc444fdeafdea1ad8b8c571e3e7965a6be58ea91608840f60d83086214b7ed75f1f3e842b27d841edc29fa1d26866ef9300d09861d44ff2dce1c2e9c385405cf8f20b2f381ca5975fb8f065189f2ce34208a13ce25b7a9ae68e36abfbee99ab96f9a5e4759e2c22792f9947b179f9345dd80f4ea16391847d8cb1a9fc7e3cbbbbbb24dd40a5505d7e19a374f7e9ee4e630544757777a79c915c437edc87c04489ada0da60b1e49c383f437e86fcc8213f105a2831297406c30c6703397edf68567f7330516241541b2c969cb3486d794cd6506134a66acd1c32e4a9f0ba65664108210ff9c0cc4c6bdbcfbb50c6eeee66663885c329d88b6e9fc80f9f748c6674e4042edcfeded95c8687ba5db00802ad34210a61489105161c01a8d559b8fd70c80593ecb0a1c70d3818d1ddfe1c707a3219643471c412b05042143badc6c2ed873e5dc4d06be332aa1984e0840f32e002085c14f145159cd8a89380065c384254041451106935942aec70fb535d1871ddbb28c2f54ff9ab8a5c6f727d891116d76b5cff8cab71e6071ce06e4fb99aad5d59b8da6b8f7135daab5990c4040a1f23ae664da17eeda2f1b58b8b1bdcec633eff76754f1b69564fc778cfd73d2ed831bce4ce3f15d19b2ab0e01ee09e8cb4ebbada48cbb07474747878e978d465544fa6b8019750ff55ded7e81e77f94fd5aee76ef805fe947186dbe7d274a5a2d2991c4c99fda4238308b3d8273bb8fe2baa2f3401aa40e1fa2f7972c4f5255b2cb9fea9ca60a8900104514ed0220bd7fff4042cbae0a9421033f0b1c1154e53a698acd00206d7bffa0fa08f8e8e8e105240c2147a4080293ce1761554d00113a83086036e3fcc8d9922266e3f0c3459c4b0248b2b02a5273de93d8c647f3ce67da99e1d244dd1252198911098171031d45292735d0c2f8ca210054c4922a5c5dd808f14e9175086d0d38a1ec451f238425c4788bf0e42ac0e967577cf960d3b76777777777777777737eb36adf53600fec610d879b5fcc723e989cca30f5882ced06a87f3125c2db7392d39468d1d2b84220a20217480a400a28a8b80cb52fc002484abb91b3b45a090c265e4b214431cb93397a56081171772352c482adff89554c3b253e76f947a1951b3a8f651988e8199f6d96b9a8548643e2c38a87c673d72e1062317479783822862235c0e0aa227081e1e299783788ce0f71097837e20ba1b73352c41a83f2c42a81d638c248ef428277db3624b9526d2a3bc1e509f3a9d50cf9771725aa8cdbdd3a35028d4bb77b2288f19251fe5260e85c2ec9fecc7cffd68f1df2857fa4eed5742a1ac10d5c94f5e7a77f8305e8f66c1efbc1f39dc388253322854783be7d4a41b4418638c48d85882002184373fbd1ee66794e2e4b4e6cf393d6aa79cdf3f6547e8b0211236962758802a6bbdb3e3f1878ad53b4a6aff07a75c9dcbdfbd233d060e831a37d932f5f963aa107dce6b287dd4516ca02693b84c0ba9e46f9554495fb99a2ec3747035ee5a436921975b0b95a4e9353b6d1bf18bfc2f69ff43a57dd434cad9d0f7f1d977bfa5b62e7bd69c740c6c8752b26ca6591286939466c9cf843a26fb5a9d4ac86596a6593286ee7ca31ebc3d8d44fb344bd21a236d847b1a8f9ecb3f791f9cd271b4b38eb1a67cd3481907bf7a3b5649562ba590727c212703bcd0c55b77aa1a3a721e52ae063e8679def2ea6ce8181b9a158465c339aa23a7267b8c6294d42c9ac1dc904132ae660544cdbefe0fdfaa34cb32fa3f5493629452cc22a996db853e54428912b51f6462db7e68239b66c9a79e0e57e9a6597247b3e46b46a8f0834023a2ee39e32844415c17be4812be5ca68d5e0d45b6942b21932618b64246d5befbd2c31ecbb7d3384abaf03564da406b1b5dc9c4069781456017f99409350a57be9c3636cb5d9707b0f9ea071b0b14f5f44392188954ff18fd795a4895f95b300e7bac846118a9a4037b9b8e213df6ac964f615fa3e4751e0b106a3fa907a55a2d95baa7e94af24a24af6439afeb3a52b6518d66148b62e31652c774cff53beeffa32ddfacac947d66ca32ec4d3ab2b761b5dcecb357d53039516357f27a28fdc9642a3ddf52e799be542a95bef34cb6546ba97adb97ba96ccfae0a122c4abbdd3ee74b2379ae5d9534a297dadebbad76c62d8a1f6f3cdec0c4cb3b217549c8ef92a95e15643b9fef49be59bd3f4e6b1b8ce7e39b7abffd1afcd0252baf5551d435594dea0522f05f1664fe399c75c4d97a99ae5b59a6a35d92fe7522dd3bc4cfbeeb3a759f69da63957ed29ad9dc79d5722715e7ceac9d7bcf9417ad33c162683ca37b6a4a669b69d66aa4d0a952f8d0d022f666be6fbc7d0c3a35bd45293c010c227c61889a811c2d81281d8b845c6e99204a107d98396b78d3d2716484229a58e9ccbf283dad3d3d3237b7a5c060af540a1d842405000c1308ebe125af882403dafe832a316be809a151f28c31cbe80200c0baaff0785342c3e74726351361dcb5c28d6ae919984854a3976113fb9b40ab8424695cfa4f61f615d7ff0438f9411ca28a594524a29e54b29a59452fe46a4c69792155d1b7bcfc62d4d2241787d90f2030fcd9ec4d5506fa31d57b36df0352f480e38361c0e436af7ff503d89ab71da1d391bbe9a5df9fb808fbdd38729cacde21e7a37fc889a116a0fed699cfbf8244f09c4b00dab89d8933a12b7bde17ed8ff505d08316869586bcb30a83423d4ef558c83bbc4a7710bd231dc43b5d14137abe3e3a11b1f6b56fc21755a176908cfc2825abd4212508d2c4c54edfd5534b6af1db33d7d871be6718c31ca896d94522a3fa3128b33ca0805226e7958f9c57fe399a11fc29dad552d25eebf753710d85ecbc42a24e526448d4f234aaec6a393e4fba08744ac21c718b327d48e0fbdb8f1a76c58d3835fa2a5ee4340a5c66c6c68372566a00362f4199eeaac68a3592c3d60161711a1b5f4fc5b89bffad9d366d12f792dcdea2ccb5d2867b722f59bb9f4b19ee9984f0543c77c2dac948e8ef9de86d5f229558d2fb124a9b159b4b36761a26696bfa587a66dffe3e692b8efbc1eba37d5da3d9b3c9c9c56f79cb7655efdaeebbacfbc6a3baf54ea4adef61dbd9b8d6669ffcdd2345bbabd0aa27afb904c626337a2c666ad2484b03b669994592633299dbaa4ee8f49e9ee446436a9fb8b362b2641962833b3fd7ecc19896851c3b0672f3e36b99ac85e8c1212993c3dd6a88d3f2736d92516b68da30c3957868d9b44a4ee602b61fd3ee2cbd8ed4529175e788388f0dc5981e7d8ac1e6274f9ca501e3619bfad7fdbd58e8649302149ab3fa7455ffcd8634c05c7550ebb9aef91d87e30174e272aff86c17c9f35a1be2abf7bdc4fe56f2f735239462c6cd25bed9038bc9964e0a01e78563c608b37f7be580374999b81feb7b10f6a5734a85c7f95eb3954ae0fecfb6790be985de5b029e8ebc39f837ae0b92d3d6e454fe5bac7413c3071dbc341e5320f47b238e2b3c50cacde669cd31930148228ee53c6999816430c0fe37edc8407088f1f9b8bfafd6d8b035b5d93a8f3f200016ddda27e7f3b603bd02c7f52bf87413290721dca2d8e1cd998794ba2766f496c504a191fb9840c29654b099580b1bf4546b99f8d6bc338e2df687da83007d8c32ea5e46e6cdc4512a17e6c74256d97f852dec0ddddddfd7377779712368703185b894add057e6c164738893812e39c73da2b3d10fb4ae82ea594524a96ceecde438cee8246b8f041ebdc7e1374412358b5047fcc9406b79f865fb81bd0609f136b2ee8eaee8ed332477fcb91ddfdfb31310cf3c9d0c5d5c81d8f25370939960b1ac1aae0f22861ec3aebc20c4ce422865df8629b320677bb20e54a18ae94d20b4c5c295f35060f577e0df93f8613aefc16f92cf936964cb9c1954f608ea13a37be17418a0fa46c81d585235c7f1b1d63002e68e1faeb468e3ca042054367768004d5ddcd5588628816af258e70448906a351dddddddde2c20e37c618b9b0831b8f501355a96d0c2831c6183796dbdd51acb85d451839b8fd35c218e33691184e085ecb6b35108192253031c4c40964304184153f563c312f07bd400b318042a3d4291d82e6970d7aa3c2a062f24d58555c7632d32281c93de724a56ac4282464ca652570c763740cc354ef0ef3b5a654355295dbac784112555553b56e242b8e2c511ba6a6b2cb412f30ba5f7d69036ef966ea3ee109eef2a1c4d0ac48faf83948ec9d44f2e722fe54be72f9d46556debf237b2db37607c5e9a79652cb51728ad5acf8d472bbd08862c3fc994926d07cddd8fd6a0f9358fc0ec44823043c51b8089b12e8c66719e17e366e9445d13131be6b01184b40b14412a60fd9123d58c289ceb5849325ac30bdc8123758620753061501847bba1c54c49129dc551de17fa4eba369d89c73625b93342c8b51fba861d2690e143b220b29b7cddddddddd5d92f829a9a6543c4034eb558ca33b4dc31e661a764403bd98c5818bf223e3684971685651d633097420d0ad093c870966e0ebe3073f716b027e7273d430f1c4df3d13f803f132a6dc1ffce42261277e2e0735c1240808515cda311ce4840c6e3fc9074840ce91c067b63af1f9391eb08c16b43ad1ae78b4a2c5515203b77410c24f5a56879fb4381ef0063eb9515020a33d41dc58011e1f469bf18b4bbed04b41df8802786dc62cef082d10bfd1527ef195bcd149a619bab8f27ef47af4f83e61e3962fe6afd8ebf68f1647be3fcefcfe9c567c9aaeaa2f6a737787047b9a86b0b6dc7bd87d45fca2f66ff0f9f9fb255ce59893099dcb413f38ba08b81cd4029d5b05134cdc2d929c9312b300df48c22cc077c28f8c83af84dc0cf363bf27493247cb8a86befd2918c19773fb732e30391afa7dcc8f5bf4a494b6e5ba60db2209729c2f49927a2d97b90b10407e3442ef83f793c1f9b196684933a83e3c8a3ea23e89412e78127f6e6cecc4610d46421c0235220820364c8c19c601f92572c644ea07758ce6910e4f536c4a8a4de952eab88b3f4db7d71042487beac745416e840026f69783b9b031b6d6b6518ba37df6d18bbfd1e6f888da0f213757f3342e9fe34f6b6b6c88052a33294662950f5397213d0f35c9db7e73480aa26235c85841f81e5cc9580eaef46eef90c027d9c860af4db3ba052d8dbb48a9dc823f8db029654e29d3c8657a48574f0f1242551c995298e8017a35b952cbe8075d57be04c095d80e9159c438207cf93848d5ede131f1068c7b12aa9413eb3941bc58cf14f56a4ed8e40c885cede7d6da9ce4ee1886711a86056c86fb7e183ac6c784199781847213c3beedb39965daffd436fbd559b91ab845dda59f33a27ed8678f719078351ac30ab394d444b5a1a12d92a57072b1c9a794d2c9ccb363ae67a665c2d0ac9e747e46d32c8ae345853a9687dad2ac6edbb62de3948a36101c6ef63ca357efb41f0e37631f347b6cdbe006d3311f443dea63c7acfc86f2603c960b2aea3f960d0bb362519bc549828a38b449c5e18654cc7cd307514fdf0f7f5c86e5a18e4ad5cd7855a3502baff2284f05857a9a5641a1502b2baff29b8aca33578302c22b302a96c5d5cc40d920f1aa5ee32e7055cf5c05ea6b4fd333bc7855de2a87e6a3bef67c7970cf627be0c837a2aafc7f33352b968584fab1941c3a26f59f319b84aa8ae576995ffa94877a7e4affe499de44e355957d995757fe04f0594248a71529fd66750c7fb3fa5b9a156f667d501baff4b81754f7688f4185fb419eeece7121b0b3141128827a40e4ceb81cd4039f3bc4382011fc327ffe6a75a33ddf1a725a5fcb651a7fdb9e5a9c557cd6f638f41f47e59fd3fa1cd2675f03b7aabb5077994fb29faafe37235bdc22ade2dbe02e33da13d4c0ad1b8b93d37a6eade46b9f337fc55377502526406275b0df9e5b1ca0affd8eec4b2fbf445fb338b0453f16bae9ec8a87b6ca6195c477ee06b32b1edb8a076c610f5bd86fbf45b879d0c52ff34f40d4c813758eee34baf3062fbaf37b687e1375d19c3a3c5ccf9dee73e79c73cebfc1657aa875b21fe431d94fc55bfca2c6873dd0a7fe2cfab99308723dd4ac993d7dd31dbad9d9d9befb1ded4bf5735a248ff43bb66a714ad67970ba4df3364fca2a3ee9735aaeb38a768581edb5dfb1bdf6f1358ba324da1dda93ac5113b9cb7c9cc92dad87dc65421b691c9a39506e43a69f7e28b35a44552ce44454d2733f37bb72bb727f1c7eb73ddca5df5bf177432bfe8eb7dff1dbf69dc5b130ee221f476e2c3f42a5fc7282ca2fadbd73246dc543e317ff3badc1f41383ad752ef49750e6414e9352f9ae6e76fae19b2ebcabb99aa4d77eda1dd9e6efc15b73b3b3a33dc9ee64bf591a77c1324ff36056726625ed0a03da67bf43fbece567164789b43bb2df325bdd05d32c7b2bc3fc0895f9e5a3d27bf0ac0ffa34e7e65ccc3d08e3d978ccae68c03e8c522ca398fd41ba48e2bb4ecb2d77033e0f0ae916402f52aa1a3f0c816284280b3e37d8228b217a8a2c70441162c46009145d10c1822aa8f04388a8e002108a24a10c2244303f0840074d80020c1f984c49c2902953aa1af10a58537f3988081a5ced72101145dcaf3ee645aacf68ad29558dcc4b55221c052aeca08521f8d002231d2190a0e2464b8920028dcb413f42dc2fdcaf3e10a74c358572872edba3c738879873aaf888b2dd87fcc820a67ea4a4942a538a6560bc064c84524e96b3a31260be8565630b5188a8d9e520d7159054bf1ce432faa1cacb41ae2d8c08726531657435894db8ac2005b998b8a4cb41ae244760684663f3a0dd98655996edc063059122b2223940486109a39f22aa90040c3c882aa254f18508838a30c2d6c510567cc1054f784104125268459e1b838878ddf831d8c4bfb1218b7b8beae5a01740e1962e07bd40089b81165a68e14343e509978eca132a4fa868a1a28591a0223b347810540448e50995277e08d035f5c6ceee1dd63d474f72eb4511ddd373457a2e7cec87f05cae452a070d11da34127333f0eb96345795e9e12e93ee50e5c3473d7f8f137cd393381a506fb25ff728d44fbe9cf7c1cb7de7cdd0177bad48fdf8451343c774dce4ef574b5c4d476935d9af6a34da53ae46cb9a4483797db5adb90bc8487a6e867abb47799dc5bc0ffbd205ea4599ecf7e39a50dcd334c65de0c43d8d97bae737793e3afb2375b1af8ff18d5c0ef8980ca41fa9fb73ce39a7e7a39ffbed9dcb417aeeb78ec432459defa3dff45fffe97dce7932a13c78bbae7be76aba92f7b7fba71dd33d771e0b126af74e39af9fe6e8e71ee5f1e5bc550eee4d6f7aee3793574b8f7a93f7a1ac0ccc2fdd33bf94def427af76a66a573902a4e1a8f48db6543d2b15cd08000000008314002028100c0744029148281c12d561ee14000d829e4480541749d328c8619c52c8104308001010000010919981280840c9ee358049f51a9678b535a90846d0f620b1b7c2c2e1117be99c7f0d56570e2913e9cda1c866d0d5025495f99510d0af8f0d78942896c572244b096aca164a2c6780993f265ad0171c330a393afda9097fd5c20b3c21a32d04ff96d968d0f24ed8fcae811ae6fbdb43557c0f6cda5bea1336df2bfd0a029b4e94ace2f3feb735c00b3afd627c82804711b10489a3b3ac3341eb42b9fddc5487e98441dfac135669b7600e316c1f5c2eeb288c044ea0583dbac55f3f9c1b8f988eaf27e05451129e01c34e74ff45b3f7aa37051b352f5330af64ce5196e047c187bbb054d76c685169d936b3bdc57b2d017159b3724ca968cabbb36282308c013025742097e4645b647152ed800366c6e80474878d9e9a6ff6e421a8d5e9060381327d62c1a6ae74f32381ad6e7e2581868e2458c8bf8104f2add11fd77006e2ce296be7a57565174e13d4428ea3f608d09c7c508be3e53bc802327b241ef5c9746fc2d992d596d81137c1a289ab42b95383796ffb7c4d903d731a2a4cf3717166c74652f80b32afa6a31779587d0a2301183cb6f119072f30d946cee847df9523a11081162f88779f0587515fd9624722e7d0d680e5cbbe102d969736b602928c4a1f6b00b62a82d897b51ac8d85f96fe6ad7332c00523f567069d2bc0266f31e5f11274470e993c1d22d4166bdf6d51ab246ca686c9999c993a5974ef5e40d1d1efa103e2dff3ad88113ede0ebf1efc75b0952749d71d5788ac99ec8d3812a23eb90eb21001d2685009e5de325d1fba5dcf7b88a4eccb339089e85a11a2e8b2484d2a7797cd78f569c7bac40cbb11baaefc80b14e03cff629f2b18996d8999823a78b2b504977cb3983f8b80090ae2a6fc66c4692c08e42f73064a96e317b8b83a390801df20781fd592a30f0982123c91b7d74676f42d8425f82ba59c40ab72c9fb034f6ac6fe4bffcdae0c7dbfda99ffb123a4edbed9a1efdfbb8c290e930e2299c3a8906fbcab928b3a8ef83f772c1d4651387ea14f6dca4df7fce5ec05cd6bf4bb6b475f058982868462f6ef5412a5a454d6108b5e581cf13753f282718105f25f03167259fecdf1fbd922e4beb595d4787fec69e48511271d182e6558fcab5d9cf66bd6322a8b22bfd7fd8323f2a7cedf9a53830cbb0832e0ea2971707e2a3246badc7d0fff733fbaeeb4bd6d934a3c67cc21d3e4f851a2f5813c2c889cd0311b45ff476af0ebbf0b9ad7bf9728b33428013619dca48c44a44386ea78a7cccaa5a10282ea642dc77ebdb349a635097d1ea1e8f581046268b5ab0bc18239db8061cb38aaf71a11e5b3b069b6a72d27aad41de3f237480e665d94f1ec190ae35fcdd85481e5c33085ac48e409fca001f18b5cef95a03157d0bae5b948b1be365951596d25acb9064c2e193a209908d2a3184cff614597d69dc3be9e1f748d603697deff1eae977fceedb5a0bfbe2da0a5ccc8ef346bc2e4cf19f2265eba511dc87bde5deca277b77d6910bade03b8e92b3a1ab67a6169076e9374d70c79d2de4631f7f8696bb171e3daec48782f355f669386b4ca667455f9169d02e6cc7bff292894dffd163ea2210b52bd04722a66a67128045158ec994ffa6ff9da0f280423ad3d7017f730c51ea169f4e365fa1b7fe8b74fdc7a0960429cf9f9a0bc9168106789d6668804b85b4ce2a8a900c7e9e112351aecd1f2e3bce05011aad5d437838d14b68847e2adce4f82ee7d4ac0b5a09bee704dd41e214504ba5bb51d864347c795d4b10862f122454018bda7858e21dc0c013cd39dbf5b71419d16f52f3adcbbbc2aa5dcf614e73336c7230769133e5dcc0fc4fb35544aec0b84c0a0a7eb5af1eddb93426d0fc52c64268e8119ee4e65e446bdf6766e7b12985d96f627ffcfc3305435287d39ef10e4cc088a9e22462162ffe6ce93dd1c0442e5bea8f340cc791cacc6504fd900dcba499301ebd2c6674969a41305c89b51ce484d9e0722352b951ab0e741a30cb43a30de1a0f9aede8978b5fada682f2e540fa88ddb7c68d8bcec378387ea9f9a01594f39fbfa3feb060a382dea9da6eadcaf7c39fd055d22a5cefa28a6d61b310d5996d6487ad630f5646b80a430b6e6a636e89c87c07de33086fa3b1f25c3d55a357c9f0df02eb02b1545b1823f65565f302dc75e0421d58f226feb880175fd095ddb3356070116585cce8f4a9a7f7900fbb8e2fe1c78d5d43a733b8c8ae9e5096b647d123d83f72d2e01a422d627a6253fd4179b3ff512b387177e4dadcaee3d0624769d944028c06045e3ef5c50afed0eb1b9112f99a4101176fa3da1301bba9ae25a8da017094e951854c7b551bad143d857d80a96c8a1714c3372f3995683ceabb67656f08134843570409b541cdad36fbf13193932f9622b87d64b1772f87b5f9eda80f829710cb9e72d72c5b6938b3a11831f90e255797586d9ad0ae58feedafd934d6db3a483b7e52e007ada8faa84f1b03054568d4bffbba06071d151dcb664b5dc4ab69254b871d6c14011aa6a6e9aec585d31608857ee2bffd72331bd60a80d62bf20c239a04f8a1504ce62ae582aa804c63702d3130b0f0e1ab528c01f90613148bfe15786f127f24df2feb6aa16519dcfc67b447290eeb3071177570b0b147949326807892d0d51cf861b43686e60753b18c2df9460852bd3500fe0e126cdec0c6996fa24acfb25bf8b8133076bb668c1b0c19d74ee341955f83fd33f90e0e5b5dcfab214b325179ea17a2799d8e637adf3b123a01f0fc1ef9261640079c5def93f7fc65e8635b7ddbbdc073edaaadeb53e3fa2638ed23b4da1823474c10410e78271a0e0b895f91b64add6de23583e12c5e3d8f3d569057898086a6cd9636270e552d028618c54832d8f1320ade8f34a9d0bbef7457c784332a63e0ff832a65e4a4fa4be885f585d2b4cb35c54a216da31ba877c66e2fde3d4636ba4290addd9feeca0edabdb36da4b7932f9049b8ad32c0e61fe37322fe3335236fae6db062bc4272f48d563a4f604423cbaf7f8d87dc14cec2ec8466704bd778c77f0ab63f8aeab37cba936f24cebe547e6e422aa637baf1d3d684300e278ff8d292ac4a841f65c67c21b00146a56b5576545750fbd9652a56d9dd0b782be085af258b263fad8d512a71d342e94ed758962e7446b8a8e07d30fb8122bc34a01a4b7dddc29612241dbbca7731e9f1ceba55c2b64581fb78788406d400288a73816090ac56fce449cf4039a760e2ed9d444d4d275769b46c72d60d866533e892483a1bab3eca4eef95e3d1084b6cb4ec9cbedbd63e0a92cd729c1d46bcfdb673454c9927211221dfce8093e4beca29c41c01c199adec5da53775f4b83bd4c04484eca0fdd6e2d3000aa26df6cc551f73e28d01caf6ed004ec22544607e996471417168192cd67a69fb0614e4da81f19c857eb0c5847fb5f14ba5bf97f2feab89197ec127c49a35ea0a423eaa45003a4697b71df6980dfb68636b3f573d02f309fca814d6f8ed805619051f948a137838a3c4c345f31900494156f6f06221acdc7df6d728a6a78829329ccc98ff4b2c972b619ad5392b2f17ae29a1492581d77e376241352d9ab468d1dcd5684128496e44a006d2096d9c62e665c29c4ca3912f575de27bf7f8d89337028685244f329a79b98538918b58c2dff303a43838e8114843b1ab69338d26f8d7cf4bf400c2989ad6b10083cb66638b6cc66234e5053a27451def916b86249a4d9cf6682a1575f78f5ec71342bc6fb49ece56290f16453980c4f2de0137e44b12cc04ab972f03983bde150b6c02f8a81dcf73077e5040a069aec26cdb91c7665cd8bfea5ef209e66f19b314fb830cfca75887e3e248a2677eaaa536018e3228fa36c9b48da1d57363e820baae34c0bc0538a4627cbd192df06a10838e5cafdee0b2b4c37f12ade13d28a0ec3ad586da6188ff4ad71eb86a26e108993ac681bbee68484cad80a6c750986869e551913b3030666f4aaaf5a8f4db2bd7fc08bfb6602465b41c7a9b05b4586b501b94b2367d503f31e5f58406407370361c3aee0a7de1ece75a5e6edc105535bfc696526566bb23d354c9682ce93fd40674148afcf039e7581b1694a09122fd4d6c5498af250411f5421c9f91c46e67f7c60056521a35a914137ca7b01b1db596c932da332986a72529c176fccbc14a2390f8a28f05ee5e33b448b76028648bdc1e42560678429daaaf4835d904822a438f9446ce1fc2f0cebb2cb3595046368209ed1da2e8df5c1ee608add1e9ce8f07e269c8f5d5660049dc01d37fadcb54c7867ac578013b8d76a246f95ea8a02634242dc5322d1a3353c71bbac0841f0d05a6eedb626d75a1037f8255d17174d591b00eafa2b3ab65bb2efbc15645e40bec7edc69bfe144f9874fbec48b1b862be00db7a5b1e8e9eb14bb2882e5edebd72e7d483c5e2a41ea124947fdfb15af3355f3d2cf08bf58315babf9f8e8e4de72718b72df8440b973dfb4acf21ca1aa9eca8f75120f97d431fbdcdc98341d790ecd7d15ad1fc552221c1977861ce918b72422f7bb51719538c701464794d317ccad2bae5d1aab0746724da81afbd6a8ae741e37ae1a07c715f5a7b250efb6534088a25d07b1af62936fb4247160bce26cf0220bf35f28a345422a5ea706d16a4dc4d106072ce2d48131821cb36e08dd4eec41a25d679c8cc0f90a46aad8d105e3d6dfed513484ad3ec974dc143cb6c3a475608bca480a793676d0472ac4d92390344d7a0a2418d1cbff3434a00dc12e1c54192ae1c0d674b7a8f0c30c0eea2b6fd47b452983d038f217874a0b207b0490283a73030f046ce043b91a2d8a1b088d3fb7f8f208a9de83c6b752e737683c3b870e3259fd306bf6241b2844f543987654f507d3c68178283364579390b26298640d68253c98c0f83224d7d80bd53168f1d5120c09ffeed527a4d869b5fc6b60cbb19d9d792de75dc10d58aa07a127d1228ba0f1977623838d1f661d97ec146e1408be3657d5f7e5f238191ec7d3d4c7cc6298e1c20b9d549c20513a2197b30b8fbefdeec10e274ac9f226347bad245a5331ef027977a6e64ab38e1e0f607fd92e748468b872b8d1f38f3eec0c1f8d9b2a00fac092f413fae199a737f90f882fa935bfd7d7f16e245321ce0d98d43757580c5cd3d6b076f067c711c904500399d01108d945e2f5bc6572b201143af4267efc994d87dd5b04147ba240cc6e7ac4e62a6aa68605fc55f03cf49ed3869369717e09d6f990f127e130ead9baef47411db8e33d0bfc0d47fabc803b0b1871e780cb29349171503631184b3647e06172c95279543b97508794c5b8a3193f36e9ed16e40f72bfad7aa04c91dfe294057eb3cf26e606906fa5565b05c57f834af8f7e353c3e2a7bb1fbff65cf5153e150a0aed221fa393700e3a9dbeded3d982275430edb94b2c595570c14b20de50246f3a9175e09f0b57ce4bbdf9738e5fdf57e2428d7f7c339331dd0303b47f57e98999b2c3c4d5339595436a93bd40372e059d31c0df7162eedaf7148ea95ed236456fc05ac6b54cd81dad0e56847ea46c7f79f817832320f8da62853e708be71d0148d9a7f8609479862fd2c383d3d36a904062eb4bbbcb4e4e2d4b7c750bafb44ad225ebd2e3d08a175881cfa7f0e3ab37ffa96c24e7fdb21e7dae3c20db2e19cf1d0df43a5bf8a0b9364a2e9063b06399751473371bfa0c14fd81600f094d199c6d55f0f6db4ebcca2456d6c467f1867f9479bc817cc2939d03dee4370a36f61b8be4a6d2a2ffeb6507590e8ee373e4c8407ea4c74537177a9febe6d6d07c5cc650037d2511eaed3c80f033d477da102141d4bb62d4458545e499cda99807d1237a2f7d4605eaafe7224f9d5f45d29d0f01886c0e1ebbaa0e91540bf0eb9e1d2e9196f4d44aa43b052f276c0b5cbc1b75e5ee12e2810013391ceab69f88ecce601a33f105b07b2c598108c5cabd57d80fd3e5b1ef483b2a13ced51acc5ea111b88d348d470441f7412e60f40809b26d49fd345ff9a78265e6896608756c8df63f84d317120d1bb21f7ace1c596bb4e0094bc5ab0d2ed591131a677dba06c26cc7effe58c9f416bb57b7b9b07633cfc2e54c31f35c8421f419cf7ce34bfc748da956b5d8d43544a6dc69239ec6d535c02dfea042027231f8b00404d86ed0d2eb3fc398bfe24824569d8b8463a1e961a71141a0b2f52b9558a1f36d57e5a11703ed28f26e6eaee19889e707f75eb496dbbf086ab430934c431b4e6827be7119f74a98c632231f1389961ce2f7a079a384492c33d20189f6e3169839136681f54cca39629cc4d4953153445651c2cc491d60d4405ebd008838513ba128631b633b587df2f00ed393d586dee3af454422615f04ba148689302fa7d6b8b56037e1f07b18d88a38948599c09f02228afecb6132a66f7e67fd2108ad7af5780fec4bc1373ca2e348cde4cb0e0fe0f70cf8541a86fcca40a076e6ac437cefa50c0ca24c6d55af86e7156ec84cc8d70c303993ae1a8ef74b2426098dd5a1b40541a111bec62bc8693e2cc71a1c84d35be65e308bf779222da5e26a18532013830553d52ae06a2dee95b411c7402b0a4a5ac545dbfbb64df13baf43333077b120745c8b1b11b995e78af659034984482b016871f8c407596305d0176716bdb6fda1ed07810131ffbc61a33507926ac1b031fc3d79c53ad30ba1f313e16cc32d9778832fdae130b576efa24564fa581d262420b6e5cdf63856b933b578e69ec4510a0a58d569b1262a79b317cc0bd75f0be2b7553e5d4a0596e9ed79826aff75e4c7cda63b81c0d63d5e123d645a5fc01e14e73ef0b4298ec77621c9c398476da537a0d8699083a55d288b0ec1c2af5b0d86c9e00e4ef1f62f714ca2b67848d7116f905bf54435a37cfa394ac06b32252209b59a78433ba067b96505048fe76042ccb3ea6ca055054569402e44795e9356206aad67010f0d178331b7c8913d44addf67acce77ef2dc0b7ee2eb7f0ddfbb35b9a3189bb542980bec08a202301e7cfcf67d3c02d82c94b4f50bff0836c017eaef3ba574e1bd1932f32916625cb9bc00f6b4ff53379bfb317f3b0930765d62052eef9fcccbf68e0dd6cd5b5563be5d04b55c19bda9d4e419d1d6060da218083a2d409ce53abdbc7d93d39a086bd3cda6b57f49a4eaa5ef961f3854be4e46e0b60b08a3f17b7d970e413e5f3b684a7002994fc09a1bf7da8625b078cd3172f7c2f94f9f48cbdaee65be7cf708d1e7a695da0a6a301ac6ecbf75f8dc65cd57aecff494e5583fbff1de544d4e47ad2536ecb5a4f8a4a53df1ccc00673f549582a5dcd272f8ab77b523f87960b3542d1350a44da39638e0876085030b88a982b7b4eb13d81a0b884120e0bdc413386ca039eb2a63999b672245d92e126aaea1640a26a1d66b861865ec79c0aa88e940f4ba3895ec981abb5b1fbd52484e29126c62beb38f696ee9609fb29e31bb764d25d4422a9f02a9dbe0a3929ee29adccdc3da607b0ab49e1098d5017ceebfc73a960e5276449def5ed3f781a6e621add30537d7dd66dbff331d15f16eddb16900562fde5557a0b153d8426134d1c1a67e4ef578d4641f9f05d6dee2fee1bc3ad2c4e678cc7f3852a2b44494609ef2c9ee370e5475d4811160b20cb605535d852e413ac5e23c007da406b1c95e07bdbb04715e2708318bca5ead0ea646b19b099c248e65563dc1957d0dacbaf2bf00383cb1bea498e4d3987c49015f5b9a8892d5d427d2489b232fa49f0722591cfae7622700b407ac4dfa6500b452144829cd17fa22c8def53450af8e026cc03ffd946c33bdff3ae3b6801e71116bf845d7344c4f5280083747a168ba8cac840da1d5e90e59609123f612b526445f8a90d5d1a5871ba58b844a8320f22991412478895d4448de308c29799900b4e0a8fdfa7ccfbd471dd2c3afdcab3beae63fae2d0b3689aab633c5b905a8632daced779fe3480a6bc8a44d5657ab03d6a9c715a477ec7217a7602395e80173a0ca74684138ebb860700d22ad06cf74a21f88238af9e894948ba947509b6116624742f6a542316980fb91f9fe67e2764013fa2087d3d715ceaaf15d6c17180cdcfbee904c5a35e3e5355730d844975b3a80cc24f26cb16f8b0f1d60c1d4dd76e23f9aa64fbfa1b1a36711ad3e3344987704e670b9940588429d539c6ef279de36e6604d024c28f868918390f273ff0312a3a2f1081831082d164b2c9cc8dcd9836652309587113e288ac55a22d5e3814a827c21c44403f449ce5a5722a339fd9ee3c498d2f7c2e415bf7cb06f898f9a09431087fe21f901a180c3d81d3950cfccb32219a002c791c7060aa2d297452693aede78fdbd350c83be891c9bfcf269d8d5e610fdd1ddef2e6c0ae06f6d9099a7218b7c48eb33c0b65e7e1dc47fbb209c75739e40e3408675921a76d80c22f73cab1f0600230c29439199370669688f280b8490152ddb688a8703739a16b7d1edc3a1150e47008526171d7e51f571a3c24988a2f2c3ad736919611c9125025540d96fb099dfc35f1eb1e1c720ecd84f169a9a4cdb7b22f6980fd7f75a24596a6cd750317356b0d6726cfe479d7e6a7531b89f4c861883b1ec3e351dfb2e5dfbc6164bfcb8885c6a267779099e1ef53b3bb7bbc21b335657a4ea0802c0c6576081f1df7214b3451514a646c9695ab8d8ce1b7592dc0a2923cfbbc8c3af1f0a43e995748e6321775d4e37df381532441e6073ecfdda8ca5956dcd78340f4a231dbbdf371589f44a096a5819db3bf1283f1bd302259855c2560d3d800ca1976d81f31754bd1e951a2138937943f1f13ad9e6ed531fbbd333c94872997b1c74a9c4cf84022d6ad9e61c417795566d03fd270e15fc6d9a88c5112b9ea256bbf871e0217d0384e2e0f8231e90a8c743586c734ca10119d06f4a4c13f73e7f30c62a0f8bcfcecbd4f87401749e7375dac8df017869b8f1206359670d9c5593da60d180b8e8c8822dcd35162555e31effc5f758dd2417dc5390a7c036c76b563dd1c50dd2cf18c3a40a79ef7ddb3cc2ed71751bc8ac35e9e8c8e9b3c0ed6550e4a0b83a5527995ddfb24df8bd9e0a71e0308dcf1a594e0eb5e15d34d9de6fe95be402a5e55a0d69caee259ba9b8f4f367ee7658f13c854dc7395a47e42e4ad31b9e98500de6e73498b1791a10bab1b68c1bac38d7f7971422e4eeaff3603a841314d1125382f5a8aaccf8e5e60e911681c37d24ea149dc09a95d9ec66fcc2f26b26c0ad8b98d53ace4b145635dc2efb5ec2ff6959a92579cd14c3bd39e758d867ffdc1ef05dea447379543e4020d2c91d746652669d9b55b34440ff579444f440a5b395fb68965648f280dfb47b5408a1296becc83fa0979a339513a2a953954670419b594464e558f227aac214e337129685bebd7988ab36528a05785f079d5a8aaec9605e1e44e8515e45ebae0319f6562118042bdd1be05cb8206494f82ac83b0d78a68b1991f063a61880448bcdb3bbc3149c16a7f9ecf7191365a7cb3035093126653974db5e3fcf99bacb8781581bcc1560cf8a34a20e07489185308ff75e3235e4f6b9d5b23c8b92d09bf0013fe0d828c47a925228f9849a16885f0819bf8890b5b116f574d5e7fab2a1cb91c05bfad96d38bb73e0b885c495c06f0cce882c11957416a4694659d1c0b019fde342bf0efce1689768b14a01ddd51b4538f3113882381810840a5b48ddbdb646580d4a9d40955b920d0196150a2e25fd9cd8627b6d39525cb31367717bcfddb2ab6014ecb986ad47656796ec9fe769373d0f28e38068de5092320aae02357ade792ee685c4392e76f8bdbf72d39f4bf0225781cdf717912233a7fbd677f5def55cb104c3bcb40b7a1bd153dc9cbb54ddd50cdff486d7d2aa590e125a6cb84e5748acd114ddadd51883ebb1f1af5eb7b699b3601aa9175096fe1e6480a62b7d6ec4b6576023083708b23075d54a765d66606f21c4ece0be10c265332560531abae544a276aca2cc4290258de087422da0770c7fc4a158e05cc415fc2467248853dca3dd6df3769e8697fe5f4911eb59a2ec85576ac2ce223a9f7d107c52e92733928472e53eced99cb338ad444dcad050ead3b68934550cc57cc4ad5fab746596ecd54631c14817f99ca0de72a23b77e1cec988ab89a1fde2d80c1e0d12249d9b6991b260be447d743b2164008cfdca5898bb6258053b12838a1561d03754bdd4a549d700368bfd7d594e7687c5842a6a958c7e13ea55832fee22073ff782621d24170cf052da208d04256982d26145cbf2d29ab27628c6145b636e73e6cc0b923ec4b087b5cb0774640b93704dcf3d0249aa888c1d246c64bf8a10acc4770a895de345e1bf0e5b405599748edf63e3d5b8d3e190883813ea258262d1e02c9a00f443e92a119c5f0d7f83ea356fec3067aff0a67b4cbe9a27c9ea8143c33302bd819634a1aface816a787a8a4e5869f5a02545faa91b9de45854abe9e3745547a1029c6ee7aff2a73bb02f4e8aa074948a90045fab16eeb20fd3ff280284377d600d99a22b1628713a51a3399113f087f2d5ab314493d2ab5c3ebb4fbee083cf702c75838a6755a378a517c8fa2a15cdd9a36110105f98bdf408466de56394b17f06173e467e976a6f8ba63620581bde5febafec209a4aef4ef0fe94909a190b3872bc96edfed783bbc21e497d47b5cee6307b2a234dec4aa99b0adc7eb1e200c1494b67a6f6f93a38e0e867b35963ddc66cd927932e58eb92530a6361a8e491123a62935aece4153254dea75f3b546a8ba8b611adb0946485b3b0ef132188746fb5b8b2042f0cc8bb6ddda9d9b523ce729712d19cacb83e3e739582de5ff2cfd0bc42c1ef8035221c65efdf482fb47edb406cd168263ad848611fc5aaa26b359f1b3037fd7db2af5ecc614e84b0323d2b2ca234ecfbc86584e32085fd7eceb9ebba4199ec86e4293105161e023136778a282ee1ab8c7fffc988e11f6f1b9a996a4ea0d6698b13bc80a549cc34b89a36138a4f1ff27059f239d06140d06d7f549fe192335009d06665beb5c3b91d5a0be2fa910b72f8a2faded57354b35d5d47f398d2169c1f80abd427405ee4d58d850ed452c207ac3066fbe7a1bc621ec0f7992deec717dd397cc091b168069ab117ed3b80efd65c8f69f9cbb9468f96641e52872c5a79d53aea18210c70a288881f0ac7ce68899745aa44a6db9b42a47099b69f0cf16ac1bfa9e8561c63cb432f4a3330e392d48bf9922bbdd10bf3434f313159a7cdea4a2a67cfd24904184c5d1b8246903d35996dd5e301a2a8b88da5870f82cbe2403d4a491b9e20a0b9101266904059ca09d58b0117ca51a2fe022c008ac8ac751d038c25d80320a449e4b2da0a2879bb06d940053cabfa626c1b44bc91564250c970120256f4226beb4ee40d702dfc7bfabeb00a7e76587a065a06fb14e3482c81e838b2f987f9e7bb3ae95741e1e33ea6b04ae66f814cec63c12e64252ca338fe4fa9a4ecfb59402b8700dd9f4a3aa9cab95d240d350a1c840040986fbb499a7bacfd5e29777b8c489a4748881472405b1a7d087468e153f4452aa6c5455becee82bd20c66319eced23844d20ecdc5a4449710f2e22a7ed99d105fa0c2711dd6e2b9ab753071c42883fc5eccffb25e33264ba5c8321d34ba21e3421cef23deb58f92f01484d4116aec72c00d801cc2494176c27db2ec452c4b04c3916d7aa13ca49c308af3e10b9948914f7c2a7a3093bd01a182c65c388b44e56d4829e3ebeb16415142fb7e9488b73a71836af2b7ed7e688e8da96c1b8402629f495488c6f98849efbb13a220aafb405779e2e9ffa6ae4234700d1be275a9e766106876a30ba8632d434d8ea2c91f8404822c871a75d9287fa5296f2cc0f74a08ad258be410ab1bf721c6b2a1e066bcf5edc93d9b157624581c0f48cda390d006fc39e014fcb32ae63bc39190b0f331222c4d4dfcda05cfb4024d71f0a0aa8bc129921ac59f5a538117cad0e04b989df09af9f134a1309e53a5b99d0a0d7b938e1907a302a04ea09a057adac6f0d3bec07873213fc9b1d81977257a47b7b18022b125b4dae8125ea9aed6c2a6ed1cf0b300e17474430fd38db15e9f3c02c8c17d8bf77f0fb85b26a2be919d4bcee53f69e39a2801e2df9331a5fb7acfa5bedfdca294657a2877d07fe5bb0910f13305c3956c1847b82dacdfd8905fd111a2cb04c4771883b20bc2d435649c6a33530abdee46fa00316efea876ab07454eea8b904fbf9e6e44bee7982f6a0489a59ae50d966850d532f59575a98d30e3f0e1547db8d0ee6cf4fb7188be6a4ac1dfbb47ca2c08474d0cc2a3d7c68c1d8eb2e279408c550b7997405dfc4dd630f99e63302ed046bbae3bc007b2eafff529762bec25b080b5a41d4a02d770fcb2d83179387f3ab4adc0958320d36322c97055a661f1b8a0d70a5d2af19f91aa1c5c56774c729f64afccfd8f2ae2e6e054d9594ad492086e83f8632782ee23c32598ff0754ab938e273789ba3270027ad704d9d7a8f929c6da55e335afa122bb98cc632aca1f05a70648319a43667f7fa47c35d15bdb804cc38c4aab2e7fadf9048bd7edb2e458e525cbac0746cc6588ef5ce0236b02d8e665a36f5f916010b01364121c5d89f2ddc1a1b2f7388abce9ef5fd609a47dc1084ad6ae36a6264e493ac2b36fb80745515eda44a9733ef259d091de92ea476360d82e878486f203b807f6239fd61a5e7ef6177fcb45af59de62619aead386061c74388224e902737674b8d0e2711c21ebb8c22521b76d02ce284020292be5b674ad5522d2794b8b9934ad6525bbf2ea625bc9c921fec729bab4f06ae14af49bc2ab0990f3641007e34b66f36fba5cc5df5500b8160aaebb8b374c616206626f406f69fe9004088010b28c1e94e4b9b79e42f3ef115d7813fbbf8a10fb57cb4e9188a1d01e27417be221b46141a22c6c8546051aa1070e35f64302729567f32d68928105205f7fca18288fd01ec0eee45e8bd6e98720e30ddfe55df836b80bdecce070c727699f6c991550058b3d79af48b0443bd7292c74a225f996e8ddfad33e886406bf88cdf06cf156b9d814040f550b6e3e176b19367500f66de6fc99f7e7398633d503303edc48c8eb1bc8e562d00fb0bcb62817fedc449f9b750c5a37613581d4a902bc0e6521779c2c4bca5ef864b06a6d67527255655458c6d76db62ebc4e157d4d90a73a2bd71ee5286effd16a56b22e2c0963493d3b0444acbd1935861a98716f095bb501f44b91121f61a2283047cc3bbbcfba8c3386246f34ab4993989511320f7174ac1ae90e1acdcc0c18c0c22ba33399d9ef73ed3a40f65b8d8b13b18a2f33b3a06ce2d089cb0e88d30865d4091f3ee7c63989830e6d63653e333033b3f1490c5099a9fea7d631e2e9dd2035ab6979cb02ab3ac3d24e4f8df7a9d453136d99628b64d8ec2374b82f15da1cec8ca041afc96a354a32b4cd3a792244872578c4e93ef55726ac39ece474071107f2537fc6acaeb34782e00238bc3b95e05724af9559677a1bcce59b051b57c8bcefa40a689546ee2cbfff1872207b042346df7d7a852d115c3c866fe84f3690a749d029905e89eccb69093eb116560d6bd476111a33d3bb1f92287640dc5dbc3c8f0d2269cb7a0d6572c307960a231a8c7a00c899525de9b9df9df501c9285d040c18c5a0f3521ab3c32bfeedf1b7c98b0e24d44bd63ee54d6293d009b2a6b3f6a44a6401418f56b0765cfcd6a7b92ad5dcf277fb614c8482775177418a03432394593ff887dd43595fb0b41d1a914aab438f1fd229ed1165ade7664566eee5ad65558368a8b0fa591a5c07d167bea66337d329151a61b4f5802c2a2443676172fa45e81a95735ad80169870f97cb1fef04e93f79b943a4bcc2254dbe2f1f2a2d1ba415885c0a0083b1e7deff04b43f62be8f3a45e95a74727aca3782479ffc5fec1d2e040ed6d4a7ecd01edcff08c53f7dae21e2fddd53506cbb5a0ec901628400fa78d1ef367d56bce937c124a06690b75b59a04b81bc0d35704b6c67dc55f2734f3489afcc7f4805d8157e186efc48a87cfd39590d2f5e8690bd369a9ada766964730102478ca3e8d25f08ce3b40420176852d625819d6e1c431c48f88fa26941509c99f7aa4e0f40fdd8ad935fe799cb5385430f1d1488f1df64ac8cec7d96661fda3b8b0256ba9bd6078b6eed95d03cbe1f47e247e15290067632cddbf76f0f30783ee8b1b7cb89a2f0198b45d4dadc43f6a555b82554b78b321d73592b51d28257835d84b520cabfdb39cb563ac0332c21bc4e1ee8d9b7d6a0a1eb81cc88e7cf2f90fa3f0934258bcfb9edff87ac1b87379898409124b30336ccb818000215746aa442cd0382eb747adb78aa53eaf765197e2347b39750b4d0397f78eeec56a7ea069fd61a826c003b78e5265808a0efdecd0c9abadd636a1971106795e0d12006c62f26caf42c0fd0bac10d57bc89571498b44250ebecd17a32c520dbd9d5f34de40d1d528de42e21393e8216a0e872b3df116e293172bcb44ec9d0891ed06db4326f3e1b38475438655394e1bffd50a8e3854de57fd96760f6898cc3596047f594af542bfb9c58dfbe68e614345a0424db6698062252bf99eb1aa7ff03f69949823bdf5952cbded388d119bc2d62be59295926b68d8d90302261f1e5ed6608de496258915257bc8ccb826fc4829397b11096baf11fc30613c11e997e48f16ef233dedc498786aff0e40f4913e7be306d2bd9049a6e2b554659b4acf1a89b30afd5f49fb20ec792fe129c98833d48e99ace3c8b5d294b4518bd1ed9efc964d23a7fffc16e5b0ec739d56aad06c4d10026478cfe13d7e2a0d9a84a567780a16361a609d38dbd34849d2636244244d5c0e26d8708601a5c4d0a4860a2e18e3266c73d20a0146e1976ca6f32e0dcbc74601f12e2f12da1d0dc2efff00ae57326d43428d1a79381a8977469f7f44c968e87b717a23ffb14481a2e486994bc90da72da004385986aa204921628065ceaf155f30e0bb58522741588fd83abdabe717914d92385c40aa826718c61b29a715c658606b1b9fb24a9f39459fe7c7438d0b1bc98be21376269165d2a5ee1ded5149cef2262eb8ba409b4a2010c4fba406cec45361880fa7f3a1cf9689058f21c41202c3de94872357e76a36704a349267109ff5c0c891732faf903187d741632303dae4e310f4e03a1b684c80627e7385600ef141a0fbe430f3f744adf23fa083e14d9040177deb48ffcf9afc70f42f62eff06c2fc635d5c6d5d9d33012e898ba3749568733512384374c72cfa05477b1d98e6d2f4a6a9548731d9ef871ece8d894e40dccbe86cfa014f7131dd29882a421747296f3ebca50b77a353ab6151d8cb44ad846b9770d83faea21ac52de36d8464f7728e7a564b4a34d56b748c246624b31dac69fada89d08222c0408b140ecd2409598480784306e85da1fbffcddccf969360f439780703821ec1e4245e296243f78ec1dfef6c1a05af6e1c517e7a2c60bd3d44aaff0a76a850cdd3f785a462c3c054efd10cfdbf0c6a88db3ce5f9846adf2b00966bd40d9901c709a33ed466ba0cfbf456cae93ee1ff5e0b78e58f814a93f669ae026e49b56ca87aea7e79138dd59c96f9da4ca1ba05b7d5c21041589477d60bf8285f06046fabb579ee442df9ce3730d61db9c89beffae128e20f488710695f020908ced91cdb6073c8011664b65551856d33c45f2c77d1c8ead8c03130322905bcd21e8fddf70a9c21a1607c90ae06247f6c95fdeb8d357c72c87f3f4ac99187c6b351ef05f2d51733c57b0d706221f0265bbcffe3b7e203889ed1a0668702bf4f5d789c149b5f8ddcc73630355b24419e89e87a9a3956af19d4863d1472f0b22ab31f849471928201ceef665d35e35ce92342c7af37e9418d32c11f1fe557ed3efccbc6581cb3786ff5319e8f99cc88bab885a9a1449866454f89bd04941284d24b34cb821bce5df41ff3fea23260ffd263735ce8f46902b8cd17c7f93221f419fc5c8b009fd5ea2fdc730d40602b8fd411e53959e035d61923c7b284128cccc5e878a89a38a75be212fe593051a165c03135b8eab4e2452d49967e62b6e2baedaedbf0768dc183502f917c30b1131edc93ebc2173a5f58f127ffdcfea0f244c5f996a97c530cb645a2f6872e18ff77cb3bf6180601f4b8f4de201538c3cc789cff602388ceca2c283c1933f05d4897d183c812a2f6ae01746227e58cc88a1c36ebb683f8e6f17b2d2849f5e72a0429b9fa3e593dc6f92443bd08480fe750d063e31c330037a73a9bfe9d9acbfe9eeae9beea300e916617db7c299e90df3a2bc2c1b59c6e891693b7105e787666fc8ab11f22fb20a407a698f1ed8ea4550410ec23a0478f84f5360aa51879f51c5115194a23b1901166c1f716800890dc06fc4383fb5a0d6e244dea71d345818f7fb83e6f0f717dbaaf9fb5f258b9f8f90bf29955bc271900f146f06e34200eb4c19a031b1a66554f690cbae756708d974f1a047b90c14eb2b07da030580d1053f78a96b023c7f6fa6cdaa429630324c2689873d10209352aa95a239fac4dbb9df3f28953d1f19b85120492119d27f3fed23191e2a1f9be329125f6fa84693c161016bb779b3a013210328079406a76768b3c595c94f2bc82e9faef9263eb7d327d08cd5b4e7922cd5325119dfe8030b6e58b5ef4d53a2e574d36ee52ca2c44431a043c827347eed101d0cc65ff43ca196cf7c56788e4a74a3acc6c31f44a118f62ee6b047cd5f4782b5ac3cf67d1f2585448ea6bcf0c36f42a51d44d7d519ee8dd6b9fc0a1195ffd80374cbc6350c838ca710874eaab09801185e9a663681034d3ff7b8327a97b4122b797956699032ad950b73af04048de1081d73602fd8bfc1a9c330ad65010885eacc0ca62498de1af3c6e08291c762ebcc4c4b978925983a338ffdcc26e65970f29ad91be90a681a72502785a9d2b3a530797cbacb621c28045741b05c45875aca6816e6b1bc21cbe225564d259b7067dd2f1f13c964a2649a4dcc3163f957071a1a312620ca55a687c51320cda2cc8c1e8ec48d342e211e8334c61eebcb737ab7c9c7a60c53c93a9890d50e3b943852663eb833d9eb2b306716406198eb871f705d0542e824f4a52b5eefc16f16d05a31518f28cce04e8af3230148f1be27f5ec7b3e4a6f14a2f19afc27fadc877d823bc54b7b64d13e977077fc67a92a37bb4578c847ab63c48fa37eff739a50deabdd9551ff021d4cc87cfd13f57f10bfb0db167df8542ea660dfe8dcdf5deb66bf2eb2fba46ecf44a9ddb18a6f8ff7855f4a7ef0ce4f1404c9c1b720f4d9d91851c529ac8b87d1c5caa25445eea92ecc8922e3a07d27747fb2e9015cbbd761843c9a64d820456c3db00ee2b610d027849649989216e1d39320369ef62ddf15c8c0855de43f6158c5f9998c5f6f346c010ae307d9bd2ed987e957827bddaef49291c9f7936016ac3f575b5a1a97d1bf43bd44ee20a7ee900c67bfb7b733dc57da77d1d4b52ad630d3878a5ef554628ed4553c916a2c29f5ceac0c2d2e0e0d27a1de85e48cad912a3869ca148273cb844dc391087f5756cbe7214f8d89a31e216bcbdc2935a6c207e997d4abba9b2a737088ed589c575f2d0ee276152da2869e802a2481e03b4d712168780c1b81ebe45dab8cf0c4163521af11e5f39a76e44635653bb8d1ab4b041d441dc058eb6ff50006465cdca112ddd4029529cafb332d6f580d468a64b797348027e3f81b3a19492491d03620e1bb79d7216f49ee6e348fc4470d9526c266c348260f2b98045ef14abe6f923d3aba0e323a1c855847de0fb5db13a96f9a58cfa83735b94fe262cb3c7ac8cf4e92f96f0e8ba643811d2eb09c009083c15340c6375fe92459b950f0a4a72129919a0ed8e00a06f01f301022c88ee0329ce1d54c1024a5db96b5798baa7283a379cf688e3cd6fad74f6780765fbb729415afd8d99671a99887fbcf4ff9cb5b122fec09eea36beaa9ca834572a99b78d97832d546d108d6d617ec55b483ce8e00749dba1801aca493f43b142f1207d42e440c718f9892323840ec0da87437f19873e662d01207845e02f00063d8a30940718bd3549f72e80ab36d45445508e3fcdec0ac65de0d1443f313d1615af8557f0ab2177ad4ae3eef55677df648d7d5f647ab0086fc605490527d430b4c3c2f7876dbeab7bdfba3c6d10fd870e39b4b00b2deb574a571f95b0cc35a902d4a32517cc6846c2008c2f3278e3d2432ede019510261903adb9f7c1eb84584f120b34337f00114ea41ca16c9d9315412c2b919aaa5c263782c2d1c91c05e7be1667fca46e96d87f423bfbef7e8a9be57b97bcd91a59f10ab3ffb6c24dd8e745789079818261cfb039dcb5e12444873fad64469331c91f8ffa316bdfbeaa482d992cd4e6842c75ba9cc2a565d90e568f27d14b2eacf4dc4640ba8286fae4c7c59d6320156f3e1917ce3e68ec81da90ab45c572f21c3265da1ee15468ceec8811cf58fb42e4fa27ffcdb1fc3509a71546c90683a5dc6d89cfd98713ab4a4db6c36f59e041477cd8f1404d702e65e733b712f82ec620e1f59c6c2b28d57bb19ac70841b0158e8de6bca43ce258efbe674d006c6e701da964f5321846134a1dfc91d5b2be502faf2e04af482585ab2db1aed52c1eaaebba35ac4f590e50fcd4e8c06b6f0df93f580c4860e719b665db1c7cf776d1d4caf210c15845f4e953b36b469bb2e46b5717c85e59407b3c0d7a4778009fe1be3e1d5129b9221befdc9de7a2c467b28cb0f9a3efcf0c8917fa1989a2c8711450447f3c097750f995b0b49c1992f39da45b694c1136be167c7229e6a248209d9104b5ab81c8a9edd74833ed8d949efa929d3900338c05e6e87cdcfba61dd9e102a7a13330a4083c50ba3369b7a138c935e120a21ca54636131c1951b9c791364a8001c6af0bd58d81b8552c7717228b8128a9f8a14f70dee015b21869057cb83b9d4f3cb2dadde0c3238e7324a59c8ddc15c7bede2012fd398025bf5b0ee74de4db6a40a8887547f1cb40efd8f84c906ba0769fa3da1e2c8800ee5d257d78fad92231fce17374e1c5ec5b6b59abaa5d6d8da98be7fa86d30263674cfed8ea851244521cd5e272c79ef3ea1495f96cae847cb72ead2072480b9acb122489ee5bb1ba046bdcca40e1a9c8682a660860f51889fe989d598884b96d8dddb0e09afd2868c04255df44666cc6712a657c4496ed4b8e320ccbc480385d9be0306965a60941882025b8705a7e79281411382304470d5fb79894573c03a36407856e38d7e7117b98c4f7303e0d9288c860ae57806c1bcbdfb3951b0ad9cdf1797473cb9fab7d5e3c7aa61d4b97b929fc33b8161e9329a365752ad5721ce9081cdaca5939a5623ed7690c95ad016196aaa3d0188c3fb9d63d051300d7c0a94d9052e15d05cb9ea2c5d3bc3fc914b13ca13d8a3014582713eeb62f8a7d24ec3e364d15c4f514ca8bd9b65f67c09de4bfca22c35b824ef17c1fd5b9401e72960ac012c9d12da1a2d4c218c23bff2a7df995c6f7694d28ef8581b14da3079aa4baa3fa6c6b8ee8007b75472292b642f887397ad1936a05841325ab78cd4575dc8d4d3e8d343eb4cd62a22769a59ca073d6f5f1eeede4b52f773dc4724b7c90201e866cb4429ca0bda5aecc948146add35776c7bb0c5c32ce79d61824127b7599c08bcc1d791cb353e891e7cab867d803250a22b516bcf6709c8320b74063413e5acb04c37e5c41ca091f0dc08108a96656a93e2369a3a697c98570a4e80c89ebc14833b8b560c1e442eae3a8f0d22f0bebbfe71db30a6ad06ebede55f6d59aee8e63dfb3e4762c923138aa8f2acb7fa8c54048eaa523d1564e0573d9e300b6ebeec8b160d7148aa3aa6f8df5f074bff3401a9dd2a5eabc5c1eacfa029017520a59bdeaf4e23f9c4dbe20ec5648b71240dfb21d561b990465657eeb480c326abe922fb442fc0c61fd97c90dfd434d39f9988e2ff08ee4f783eed1660ea57bafe8ef3310a5bb2d1e9a54844ada7482c561ee7324c453519b6118f73ab38f77021bc2fb378a067236d937d910382c1efbd3888984b936c83b3a77800e9246f5e3bb4431d7de5413649ed31d8cb115b0cc1c5a4e72e269f76c0b1ef414a3888ba3858b786490c7b3a04881f48d0f3c80cdd722b4193745dbd35a70d66de1ff3405afac4daab6e0cf4153ee87b98ec15b6e221d10a2bb75ff906620976acbe22746230fac17391f3dba0bceedf2edbdc95b21624cbe84e77fc4fd31101d3f7320fa71b17109806feb30528bf27bef586b26eb6520788ce159deb4fcbed87d3541e40824e2ff69d928e4e8dcb149cd53a65e26e6e9ac6896bcf92ee34ba9b0fe30b4335843da7f4e0c6bd30470dcf2bec7aa33d2c39485f63b037bc6064b460745630832918502758dc4bb0c6082608c112adac0d6eaa85313f1d44a4ca7e414282fc210138ce038f1737f8a2fd15313c163e4361b5af1d4b10854b41fec157ffb3d42e0a470ca1ebab6b92fe2d2d9800a2989b2f9a8fd91552c85a887c35350fd887440b6f85c17595b99f38b86cf6f71dbef70bd03975cc2c9936a08f52991ee50dc169e713fd6461cc10b59e3fd78e2386f3f7f703bc88e1c2dcbfe7fd9f8069f51e860b76f7c418a92ba0ef98014059f9d31180bee8cf67003ad19f01f9bcff1a7f92511be1d7518316ffed7894ad3196ce66c8091b8b65b039d03f61b3b6c8479e849ae0e54fea6bffb03d813e71b77b3e46eb3e696664b906e272354ccc332ebe84d3f0752bfda95677ae4d21026777351e48ede8021b53203a68a36a5d76514aa4661ceb1c0f3e69afa04398e66f50ac81ff71227c178efc9afe3bd0e7e9ca9d64f4098007c67928caa42511935ff1796beac01715102ad83429cef018eefd3e6df56be06d68334d1621c3f3d7927b832b1f06a29c93351c272d26f6528c2ad8930dacfd285bf29c75b11d8c8c133a1d25fb612d422ffb9cac759c1cbb3863ccdb138d10ef00843edbe5174e9d90ca85eebe3bf031c9d32240adc984ce1dea0a903f206c50b376c49fe261dbad85c4c12027a39d6fbcaba71acce42ec2753d191965557c0f0d332f16044ceee7b4f96b561171a81aa6a602a20db87432226dc6688a10d7c52606da7c8b821b12ab4aaf4df76660c3179f10915abb0700ff389e615cce931b705d874186aa94c5ee63f375677512e8dc71aeabe85e3f0a457d0faf966eec2662d2c34601008771972a56adb2464e36f126e86fca3055c866f67dc93aae748ee9e7cfe19047b182dff8fcfcc62fe58b7c9c42871ed1d2f8c4b4f6d9d7d72e735fa86d385e85dccc35607d21b5a6a1c4d8f0206e3afc0f97f948b708517c409c0312341d3387811d8ea987e9b910732cbf62a56aff385eb9683d169f1deb4a91dcd64e196e4520d491b322b294afc0c730a22c82a2fda7a7123250a47ce997e71237b24c688695080427266ef0f79587742ff369f6f490197034397f459a3972347df2c29249869b89b6bef34464bfb2db626872a347393c38ed709d227314b75af58d4b2414c8f79ee03f23299afe72dbfc2b9404f94810c63a34283320d9b80e62de067b26d102b6287f1d10613e63d7e4f5605babe684ba9a2421884484c02e8bce9e9b0ec019d7d705491becee5816b9880a4dab60b5507a488a384243b01630c71a2eef02eafc547c7116e2038e051c3f6c575615f6219c3e5c93df6bf00c362b58f0b5576883d8784187a49fa2c8a7089cd9021398e4d985448778a9dc24014104c264e53189c8c1a4d92b37bb70e83e7594bc8ec1315d37c1ba7e5e79c9578619babe359557deb177a3b9bf493dd0c295fc4dd4e2912ef356e050733cbd4a4a02765253955612881d23ce7cb5acf0379fa9759a7ea03323b3e6ac65f1748518665649f6a6962deb6884e6598cc215c55980f0b4f929e100e829420612cbbc76a111be46afc6db2845015399e5f5cd29b969ea32af673d78c742fe6f68cd31f3f51c24cdf70a5a052b9c4ea07cdc64a4cc7e2c8fb54bbd74806f36e42cd34dada530c4e62857a80b7f0bcc59e68f022d07d7070d2e3dabd529af062dff7f529c39ff6e96e73cfd3cebf0452091f7bb8f26d12d40fdf66b4e5a5f6ea3b18ecda936d8254eb9834a5a6c21fde56574952575863730acfb0ed8a0084f2778836136b4d63bbd0937f00f5c8d425c6cf08dcef2b2e03f538e06343e5675f41ead2cdbbe6e2361e6865ee439beaa7906606bb68bcd18d7d6baffd65be91b613da1bfd002f72e94300866c1c639199beb9ec684a6d4f4bd75550ef5ffa4653d9838cf61244a157b26daefe2eda5f10492d09191c91a2e6aabe635a9d6952502c480f8c38edf59045a87f82017bc4e8827a89165a6812d8408b1eca3cb4291aab774347d1d99a1e490e5577f1f40d1c01580ba98c2acc02fb5a6871946c0aefae4ad06dfc2a9f0ad06cb57d823e07df3dfdd4b0f7f4c2e0d34ce3bf9afbd4789e60077e93502aa7d1ce32c3a3adae6314da0959ca3235e3c114f8fcb0d29563cbfe663300f12dbc404a69ea78a8470e8a5ddd8607d1cd31a62747168309655d2bf33a8677dfd4ca4f156f5123705e5dc1ffa0f2c419d3f9497ca064ed6c19279282390e49f99d47daa936324938fc1d550e4bf2f2e17217b684b076907b656809c7783066b29a662318856ca31afc8ca1a5c0c0e22d7990c195f331e6fe8bf81d8254dacc8da87014832c15bb27846b95ff472ab20cd706f73911a678c10500b1d18649409552a3a96c82d216de12a3e067c4eac382a9dc79413409f4385f48b414e15041b1042e7c18c3fccb71eb00689b261fed29f8235ca20f44204f60bd0eaa2b5798eaf2e41878fb17751b05e47a289fe5601aa2d89ee921d9d145057318024010b8b3bd68390696a09616d4168e9855755d2943662651415eb3673faa456ecec86d8a76f6f369f52d712104e1a2e2059f0c8f8a783fb99a7b6276c1c499fd33a29f15c10ea5d74bde1aecd2f9d6fd100f08c008401af70bd528dcdb692b3ba778ebe644da08cf2d2e1aab49b181c2c41ec071b1202f3c57cde66fb6217e8d171861993a7054de04b6bcc0abb7ed3c5e334990deb1fa7bc6896f590f7c30b480e08184b3db69e54ce93c1eb3b6f8c5f66416a3ae5a476fb3e3edbade30e6f32ddf169c0879b198ff6c637c741fe7ad63ffc513076f9be1c137d0ba0e5cb89a29b86665cd6b1606cfd1af384a0bae6f12722c0749204cd7ddd920778b00e94ca51747ae668ecd574cff8ede590c5c7ae28e45a6d6320a7310f137b1904decfe979dd3848e30e269a9e02a7f70dc2cbb73c0bf7f6810e19e721a8cd2b422b1dca31fac2ac7af0d23953ed6c4dbed7addc795300ae8b9acf7ec54bf08c44bdfc16ed86844648470ae53d03c0eaf25f4c6c229b7a0d5c642d4defecb6194fd5cec76986d854fc15246b082d6554ae5f5ca0fe41c478b1e537298b283291ebe70b815bcdae461e59bfdd2fafdac5499767eb2457921035f307840d4dde4f3d9d371ca08ed4c8534949a206be191429e94fe5775dc67989efab1c942b535f01e6324c620f15162dfe92b9a2d90f7e2e921bc6e879d5accc4f10c6ab0b9171e3530ca28c106921795e45836d946a04799e4e4eb1ccd06dba15c634fab6330a0796dffe2b1b2d6f4d4424a2779391f309cb9f8e28d5f8b5b417f3f5acd27c9166852b0aa7a33f6ab09674e28bdb0da68e99a5cedc35413c9afb99c092c71c3aebd89c0822f13862eb41ba5c41648fbc5cf425d136aadd2cc2b7998c51806ca0e047bb829ea8f1309302d2483b818ac8e1005826e25086a307de48193a2c18c76fac9cb5cd6f733778bae39a53af02297c97dd8e7f82523e3457dc07e052a3570f60892e8602f496711062b9408c4b56eff2f547e50e89640808b4e8924fc95d3c8a072ba52a4ba06c28af937070b7e4f8b2e9476c3620b956cdc643bbf2a9b6004e5dd0da86c9d49c686ba09eb437ca6adbd6794f0444f41c5b174f59d2ccd5ea9c2302dfdcc040de94cb89a782a61f27b7a87506239fcbe9bb35051950c5e51de4456bc965790abc9bc437a726d2576037079e5dcd50a0cb6dd857f70ff37ef3610954a7420f32a9cb866a97981e32857d6c68786e023409a93f0d56a87fd3e64fdfb9b65d9186c852eac0ca366c45a35e0a230eceeae08c6abfbb6dcf038206453270941d9b5ce69a42bc2176db180f33c76ce2bacb26f39a0815fcd5c7c128466dd6ecbba91de0d7c78c1dad7623d9bcdcf8185a1859607546e966406bcc7525d470f04188213fac349ce4f0a444b9c33e9b7929a37b410c3246c46223298150a72427331012d79a91cc5348d8c1b55b933fea0d53d3569aa7614a09e95d6b1b5cd438b216221194483cf39a406064e33236cbb449a3730d260a4d8917310ed808bda503f1606a43101c08d0b25066069b0fd9ad27987b233d048ca65e8adb6a58aa47ac24ad5c1e14531da097122bacda64993f90cac4ea5ffdc2678ba18c19b4569cd8c627be58cb58428206750792645e19e8761cc7991cebc4078c0be0087c569eb71174c4ffc17106be584379b5b9f08d582fa503a51407025ae727d0a5f7ae45a3c7ea948e81521d08f08fbfa876cd1ee71274b40f1056d87a82db244224994d04716db5dc28e03984d0a31ebc481100c07256a207e89ccf3f3a3cbb68c304a6855a36840e64394882305afde52335e91ca7c7e49be89ee9a998d133eb9f6379a04d52f3d15ba299932160785037c65217a2d61d36547d27b08d0aa75af906c4ce906b5f98967d92d68909b00731d13e0e058b49a9382bf2ce996c8aa6e9b580038bb358dc3f1e23455617e69ecd4541d623e8f57b912f271f8e62f432734fb23b3bf41223a5e9118fc9851ef6b9ed3b298fd919df41033656b7149e7aec2e4a3035b03c01f156192e48d98b3a131907ef7bd20796a7842274b24701dcf8a31b47bfd19905dd60e8b8a802dc6e699124016505f0ff810250a69bd82b7c0399c05889bd899adea56a8aad648ff54b0bce68e149181078344869128facb2ce5236e578622bce4f4e1b8aea059ee9a6f37274cc532db6d75ec78dc4384e54005ad8fe4ad9878db35986d256eff15b0711596512641af9965edfbbf51270e9878dca6f6cfd82f3f0354a0d0b3a2ef4c705cb2eb9c0c16a268d94452a8494553dba882ccd8842a5d77a43222dd863c6298634358d81a0099ec81cc0b14de31bf03d019859afafdbec17327f2b1012135c9250541559164ba655e777821c36cc8858ed0132ab27b3d5fb0af462181c42ed6e66a35e39b1ae490f49175aff22654715f8e21e328ee6febd49f3fd8073ef651af347dc4c2b9e77076a1cf9ec70c7301f1e037acf28036b5e512d9e4255bafc72e46b5486006b085bd1aa718326de8c36929690d96b59720a5590f53d8b1bbe0e9ed5949e40bf4af02049d54f438041f9df73255c34ee4020f142eba887c2701202d57d69d9421b911ccac3b04a61c69e3be00228fa25d3a070707c883f399f2749dbeb69d78183c60c63624628cdb57f7dcd50743efc81c892244b5de01fa376a22be365ece7961a2c26c161a0fc354c2e0e13db212bc0ed59649318e428416051a9bb0a36e2ee0d0ba46b6202d95b0a239735e0cf89e90c9fd0f99483de7a309b5b42317c69354c59d8c187c487fa28657dcec32cb59cd3bd29d65f6d7e87ae6f02636accbf468dc7ca9410d68044625366259e03b32c25bef36cf465a77108cdd1ebf045a7b096c3a568f0b7f94b0d2903f6c301e35d988832a39d85817865808242768915d34bee874fc365ee0c75f3449ee9dac9a7c5122f11aee7eb927af892f6682c45907b034d06d381ba4ea10ec140927925594b0060f95fa568cd0e03be3a6dcac152c9496327c99d625cea3c1fd19f3b56b0f6f5f8d53f66e650079e21af4720e703c02f07a89f9f39dbab9a2e4040e108a747f69e03b8db71441f5c63b3e2f441be04254a9af631145457b245a72e4df6f810ec8cb3eba7b041a562024c82fbb98c084d80dbacdd279c00172e4c95b0d2b11f8401b7fbdc6bc216ae86595c0c089d29d0f3b300314b1bb106520cda88aba71331a1b0357c739b4b54cde737297d08b4e40213b8c119942362beb33932849f8d8f2ce3832bb87dde07c11006b108054eaa68dc0c8052a274c821502ce25730700d44d2c99341efdac687f869a4a8015a5e5bc0c14d1093b445d8d75b50529dc18169e1681e2c935766df8986a59570019070dce3a3a85d58641acc1debd6bddb4a30f08e49d8a7587ec7b5bc9a85c04714631029fcb33a7616dcc854101cc0fc8b3b1706badf1b2a3c20dadd80717dbc5e24d25015d2b3f67b5d1e8f08cb795cfd012684997f36051ea57fd69aaf8543a2c445da46b2fd2dcc7875f26ba83cdffa5ebcafa414e1c39fffc9f326914ad23f0e23d31f49efe520c78437d67e930cc9682dc18bedbd11a27e9323608711daac633b0210bde4bc92d3fa1ddc040934a33d549628d7b219f05ac2b6c7131786cff32018dac973debfa222470ff67d21eac55394c9b4bcefcd676fe8016d1d589cc3ce8228ae7534a78d7b5d836604fe01dcdd659a51d69587569979709b1e1fe98eee9742f05f59ebcda65532777e822ec0e494b5e434c3150d4949ed6a6db8d870270d8f30211f92dc9e04aa3f2f1d0cbc6e218931efe4f80ebfcb379ff21f38991227af402ea76c01227184374759f32319260e1a7c5a886642064987cabfe3821732cf30535536b9889aee79a7ccc2e7b5cfb311524ed03793e2a883e1e02a30f59b6ce1aedbabd0851fdf27c200dcfaafdf4aa845ee11c74f488ea09856b337e1a50590a2c77652b31ca6d4ebbee154510a98b40e1538685234397490e8738f7c417e4b14a84523ff58474fc84020b9b1d3b99a8620acc4874314e880614791874c2c19b7bb68210ac6e4ea185c9f2a3843ffff52d1ff2d61310ca60f2eab81ee2f77cb23a611ff9ef42fde2e009e5bff451b67bd3f0781abf031b436d4aca8b3889b5ef3e2a09ba4f9bc8e6988543de8a8b7d29702f2625bc1647ecd9cdc471d5986b087acf857d0f0d45f0fac605f40b26d59047a66d70800eb81ba57b98c6762c35d31815a672c50521bd963cade9642e67bcd424aeb335672053402af0e31b2bb0d70bd4e03e517febf8b9d95ff31a3f4c3f00abbaf264b05d2da2c169ee797583361935b7fc599ceca893567ff49b956f40128eb32563f1843bdc58abacd6c43d7c6af624e86a59b9a6479851a39e40ac7ac67f86d68861497da8b448d2decd8a40987a73c31496d43d239fbc29926c66933ffd9f1e700eefafca276a7095f8652074cbe110409cd7a7d798f99f4de1153dadcaa22487520bfafe7f886b63fbccdf10ef7c94bfbbce638be4182b0854041b2d43d37be91d3d32ba4ea10c98e372263e42621eceecd029c3f44ad64afce8738c53566d024a609e1bc3a32b5ac16e54ce432ae8e8285466bae12b1902164b485a1a48ff30f6c7810af0cd57bf0cde8697f2dae7a691114d5e87e0b48ac5d288174f89924778cdbfb5eee9d30fe5bad769d4487256d9e0cbaa2b32c4a7493a06b5538964334875773b00b777e4460f86e7401df5b60899b3725121756c6972380eacfe181aff3cc0016c205028e78cbd704da324d19b03d9b47779a180546a26250973d16d1c3cc4a41d1b91f76b2163eaecdab59a0dbe2adeb0bb545f59c8964f0a6d31f275ea35969e0da21b85cf12cebb35d14ec557ab1e29c53ea5847283f64b9c2fc9f251bd1ae83fd6498f42e91a0e351238fea45ecc84a71d77420de007d0e7525756f24e1490486a3fea79094c55a8c1ce902010a2d3206ce2e8271ab13afd9843ca7f8b7cc563187c2a0c317cd821f4996c4f962f1bf4332e856081cfb976ed30354e15c2a782c6e4c852c22092fe6c08cadfe1f81d0288ab368e73c73a42be532de4d195df562175429131c8c881b2e89e4c5d6473d0e4ba0b61f4648097429c73a6ed58e6166b6b491d87c77ea0f4e4ff80f485fe32557e67483f3cc700a20eb90e6c10c5f17a9f3ce14446bb1114e155934b011ce2872ebea1d426d6f14a8a891423e3856b75b60da57c989adc91f2de5f3ff9111fb8aeec11cfc1e2a5c3fd5dc212a8e50808dd69fa15ad885afab7f0546944e85d81f3d128fc1dff7b4694717349f1d0951e92d8998f0f9f6fa6f0d603f9eefac26012688471eb836ac54e726d7bcf097449070bc2848ec111bca49acb339a6c244fa29aedd036d04d7d5eee252274a18a3ac6525732cdc52fca668e830bfa06002fd0f3d40481c976cd5edfda24115b065912583d67d48505c38f14c03d610c535a84da944670a326553e982f7459070011a1bbb5e4a1659254a88c8ec7d2997ecd6de8c4dd99c62fc2043af5a8ee16dcb71a1d0251da6c5a03edab99df584929e3455c38720cfa2fec0d029acd382d704d34b994b171e04f27964fadac349041d7c2cd6ef3505bb4e8b8a3aa443ca47fa9b3e438cd8565c10d23c01700961f0acd8eea64216531116bd921a2ec599d2f3615a8aed6d5dc7c6c7887c4c27abe3094525b9e0ae349f162ab37a7ef780516e0d82c00034a2a482eca75a2724c77c462b6ee25a0be4f868a76d5d7d1cd20e4dc67cee9c50237cf8553b666f0a7ae429c9c66b30efc730cc30245bfed3b09d466a9a76212ca458041142554466e943e3214f7e9ea8f0c003b36b92a63414dc7a74721d4d777e004e33f17791f12d8eeee6f202ccf49fb37ef76134be4ef68191007ce3ab2e3258e14e01f54dd8c027a8ece2e1c98e24aa1f54eb7c9e11130c97729e9e83d091f932724dda16ee23e37ee2668580aef0501b6acb65f3a3f12b0d66a712ac200ece052a28d3c85103c0189d82ef1494a3873de0dd51aae6dd8a3ff641b1fbcc9c0cb949bba18fea3a790581d4d2903bae2156772f21a899c2cb32665966e394a152cc986298a881071d21c75a88a9ffb681c76e7f243b216a035f4c99b98b6a1331b979d6c2f222df405f3af73f5d4d9da2d023cef6ff69c7d4d6e65df8fce805194b72aeb1d815c791ddc6234b3051db291c6452e20d771486196ee135410d7b0104620d0fb07846cecb0073c4fce0e32ba7c3656fb4a568e52302e62b3c945e34852fade76c592496bcc09925b4db628fe095fc41f6d5b76d3f1fb5720805af41ecf54bffc73b7e6750d6464337c5ea0b9969d44186b426f5ef5b58127ac3f751c762034743c482d6f0ede5baa3af2b00d589fd252795726ddfd0630ed70c20d08417d788ab24be2d7f56b46b0f5b621a6acd5d8331da4598305803ec0793dd7c736d1731f2b3f6f8351b4d6d234f473a0abf5fa30bbfd25b8c754e45ae752add0ec51ece5e1cfd38fbf529b67770dfe13dc01abb4dd840822158541b172d7d2e9673513218fe102c2020dc2de49c0f35862d29ca91a84584db59d79012eee1e4860d7c8f7b3b1d5bae6f059383fec0391fc29b072f770c067d0dac5966b6fe7e539c9b85afc02fba7757667b519ae0deda6acd0155c82b41bd7d9f78e5e2bd2c91c084b0bbb4d82a0dac9ead9c448205630250e1bba3a1d860a162ab87094d2e645b9234e186e1f67c02aa31d92be3404126ea0b011c692962c46beddf67a246679148a31433c36716b8a61b0052333110dac5013cf61ce64a9e19e51d9fde8676b67564b73bf835a645a820a93246b8aefbe70400f550f037613a2bcd26c0520382a3bc63d1cca8f37105287e98dd47db71629562ad16e494659a724805e094ae1bb4ac583e76f6d1f92377e0c5f2a7247f327121287057909021483ddd504c0c081f22c1a76f4475c245d395c21d95e12f4d69b7ca4edf0f4bc23c45cce36dba5c0968b9e0d370c78e4adeb565c5ec33d4806bda3763d1333daa4868ffcb0109b9103408c611610ad0ba26b7dea54e2dd4a094fff7c3905050c950aeff4b1c0da84090bc2a2af7f29bb20c92a61c2369b4bb49a28a998d2eee05dc35708b4b30f6a8f7f92581b26986c6ac41312d35ff62c44e36d34cb95e77c4c7cdb0b93ca2e31b68d9e42896867159af3c2ec754d4cb3a8d6e64c79cfe5a24ce8d30898352817709e130eef06cdba2fdb70422806bfa5265e526456e59d75e7cc0f52f62c6bc32d1397a180829cb3861e4f555f2d6a45ea15c378a464845a2bd3e9c0518394e76e6a43d5cd370a473fa83354addaa4bb027c990770eededc2951befaa3d9d83c2f34f3c20a5eef49faa90e805f6425b106901eabd12ddfe09e1f7782fb0f34f795eeea5a66b1d64a96718e9c718a7c7ff47c07e9ef8cefa99cfa0f6016b0c0c42b7e8288cf15a69abab0797fad20e74743fad6f895dbb7d3d0e519ab53649bce22dec3a28a3bad069ad51fd0d96b09dcd78a03316c518607cf36a56d0a7983fb4431a12fb6e826ad904a20ee8bb9d891d8a00b4e57a79538f0524243aca8a80c00b252460d248572c240e0debf83dbec860dbddfcbb1788dc0777110c3d03d3036f083979d90f758c646c1e5e9318fabf08a59118e4ed36f20e54d8a15a2de40fed0066eb5ae8466c087bc1791bd9bdaef680c222a27836f3931a724e9c42d4710297573fe3b4c35eb2321a6c63b173f50260bcc2555a1f7bf88f6e7570a446600892bfe898cae286e3a02f0688fca2426a344135649340e4179cc6debd28489129da451f953b93b5d3d03f0660b9de164cac401d232675936694a76d207fa020509372d06253224e080e7778dff71cad656e549279c0e17caf915f448b20c0c0ffceb4e3fa3e7d88d810408311a3f92a3e2c4a46e064a0d3a1f0e277d17e46a653217cbb97bdcace1be8ccc4ce64eaaa14cd3d22cc0e38933bf8c850af95d9f3029272b210d70ca536345a750e9d4965793507f026a2a8f1b74ca27f6782194b99bde9a70709c773839518fea94de3fe83f5f8ac7c2b898de7be6b7e145df0a8a15684aa93dedaab16a0f70a8537e3d4c497fad0c338642e48bb8306bed1626a5c866bcf71ece2dcf8c78caad87167a72f89ba8110dc787121986233171cf6a5c3e236932ef3a41976ef3582edf57568e4bdecd711d70090a2fdfeabedccf3ad4f45a92e94219508a9f539543050219201c414df19a53c2df1697020193984401a908470f1743adee8dd61475593c35b09ff1f4734ff230d86856b2d18a324eca318f129217591caec96c99ba62e44838bb8157441998b2fcfc26cf816077efce68200f48dc7cd9f2d99d13732a3d2cd9089d703e3c5a8bdf9b118b32cd72bc94f2e331a95e4cb4652f4f94567ecc9496ae1445d2e1b609878f8bda35cd4b502f5f008c09520f11469b372e0f5029af8f9a9b2a895f8315a98407afc605f392fba4932b08a64a3159525e98f4a9e8d4ceed6a4508f7a8ba9168eeb961c39455ec02701a1b1058619195a9adf37419d3622140324098ef5c345e886f149b704bce0c035a314c050c2b29c08d09f1fdface718c7f90fd0fc3babfb4b8cf1e08aa909f9c7b8d03c356627f92da4cbef6a226142616f643428704ced6ccf9c5e3c773b86bb3e47a7bf7d91420dc9ee30ec9e825b465edce39f945b4621a1444c54cc82e19759e9a47e69785c9c1ecee9a8469854720a941e1f1b923f4b8daeab88142c6487e0bb0325ad713ca6bf851dbf72f553345ec15ac4a59a363581e0e4e372091d1a9e0762117801c24822df476587d9e56f363d18a8a688b6fc11fe45bb5f236905683498aab95b51735184fe2df922d01c0fb9f6760b0c0c93c51210348176f12bbe5f767e52f0b4812b5e56e9271ece9c6d093c7d3883373396805a53421a5797fab0d61246c4b41bc15c0d2bdb19fa96af992dbc29fe02ed241962217eb807a14ae97f1542bfb8ee50edd9c6d3dede13f626c44d65c9192ec1d655b27afb2a1a8d07bd458894d7f690d756c82cb3b69abfc50af7828a864049b20fb0799f43f4b13e6599f469a8ab002df3551854d312d095efb9620348b0072077e6dbe62bdd2c37037a7d77085bb1f7c22c7adf4bea0913bd17c9fc24cf98413bd31f5520029f9edccbc08c57fde2f983dedaefde182d4b0840cbca1b0c7cfcfbdb2ab0e6260bd77891a7e42577b68ddfbb0b0920c1330a780985423fb50255896b386bf42c352a70243fa07e8bd19badcea30ec058147f1f75e885eaa3fb0210a3f61bea36d58013631efc42571abc8851ab0e6d45f90b4de101b8ba2510268997a9f943852c4b0709fb3f249fe1ec43c35d86e2dd456f7d2abf8f8d0c263cedf8098cc5f12dc8312394ff0f375d30a9d85ad64d1263abb16b94e64b7e7e02dbbcc63c4aef380a75b047db5c27266f61a8cde8b5656af24114ba6e80651a5b98bb5e03cf5512219ed0d7b5b2451b0b6abadf8cba193e48dd3d50b2145971aff602a762de567e2f19656892a1859a8c7bb1c980e2f7088172f928074c28820b87c6a368e7dc766c5c97634f323867d4f471456ef873218321a1649f114327fd73a5044698d33930bc2268c2fd81423b0a7749937fa0fc4665ccf0a429107849969531592cf322d6fe35fe3a78120d630d674925680ee0fdd3942282a3093745a2efd79486e01b0390fbdcd28b2be55ba5fb901ba43a04d0d085a067e47723d6deada179a977ab81bf6eb0c1e6d7a7252af6f7e693f621920ec71a20202ad01425f647ce2e912151814064e31304a7098cdea4a68cab85ee0fe84fd5cb1571883379018a814be8283749b40b82913dc3694715a15b86fd147a10f26c418983ae684a15e150b13fa250584de46889574c5c192b371765c7a30abd72b51ede260e89fad338803892522cf79f5354283abcc4eb0460710260ff0e86e0b6ef3b1b9cfc3bac4d48f26d29ce34bff7c6c0edd6ff4f9d9f931947d752298563f6dedbea581b619f03a03eeccd1ffc42315d5c81212c798e7b2c7605a00eb166c47ce80ff49eb4a98261530bc0b6980dc7b57c1b89f6a412cf07290c0fbe5854038cf65d292cf9196693f43c8886145503411d177c40164cf0db26ff7218986bf322ce9bd396308f2fd39f73c6dff98282da845bb62a2c13dac088d815e214fb0ecd419645e8baa12dc1a151697e78bd46c054799478a672c57817ae706a0c89c81829af126683a580a80322e8eb69ca06c75bfeb23280e6f731d9bfdd641d1f91ab597a3d100382535a3fff82050f5cbd042b1cd76390ed37fadce7124a8de96612a502a50d817042b5e1143e6272eb16165c3d2f84d284b10e3201c220864800d6336e620ddf47f1a83aa76b93c55aaa0d62d211d1b2ffed7fa84b65221d048e1b07b5eb5c3ee2e8e779259f925a1922da12715533f2bd3eef7df9bcf8feb97bc5a3921e4e06d12c54695cff4c458c3203cc97b12100fb41b6cdfe695a68044f1cdd50a3377695b43671141d5c91324df45768b493540eebd4c8fe3e9c439b614e9d68ada568bd82b1f1fcd1e87ce2533012741f26e8682adb7c4fd20719f880e8274eca4f2625d6661b5926cbf689d7318c86cb22de40203bb20a53d9cf0133e0817610e997d6fe70ac2d17cf87baa10e278da8217d2e8d999ce1623587af3c494be0117fb032dc9dc06e68233cff3f0dd27a2b32af962025937847f0ad85a2d2a2bd1022817bc11e8c37022a84e9515c30ced19a0e4affb1068894eed1e4245c78588df8401463b8e8c48d076de0cedc159f371ac808f0549301d1be82367ea9d2367dbf387ef4d96b1dedbd18da6483a8ab50c7d2c7ae4e24fb6b1317f1916d7bbdb5b6f796294929a59401c804fa0438057196f3349933f1f2819658b4fee681965880df7af04d8440f26f42a0251637ef0a81e45488051402c92839054681532d69d5d2f2dc90a702f3ebb3782b2e49d0a4c1d56ab39c37f14b2c74fefbeffb9c67f1dd5aa63764f1bd891068d626eef63d6dd6be79c3514a29a50f7bc1ba16a9ac1dccd1a8d6c759eb69f4c4cf292aa3ff404b2cbed779a025163bffd91a3033f13921d0128b9c3f11029df89cd709819658e87c4e0874229c5f083473a1909c9988baf938bb79daccdd843cf75bcc7a46cd5c21cf0d55088a5486d562d626a2e038fb9bd7f9251639ef7a5708b4c4c3c2f5373516aed70981a62ba4d4ca2acec0a74d2bdac492a115f2f4e33c7e30e4e97005fc388f43a0251e15c0b7409cadd0593b64f0e3c7ae569bd11b95d107b2cdd9cdcfe07a9c904785d6831f671f7d9e201c679442d5b8214b96ab5a6d166737a10c33b8c2daf71f7d1d359801415139f29069d4aae6a88c3e08ab4f54462945f3146bc628c5ba54c6a3420c693e391741a0f45d29525ca6362aa3162bd23eca349729a5162bd3de92695b65da61326dac6aa32a2cad4af5c8c26816955d9d2c1c67df4ce619b0acaf36f77a8b053d69b5b68d70ad754a29e9c5b5d65a6748cbb45c20ce0dce4d1326d783e0cb19e802fd5b2ef0fbbe2dd0fdfb3e07ffbedff77ddff77d34ffbede0d6dae8474a1b65c80e1ab51e96027a47d20e9f74412bcb737b04b65386ee07ae5a64b03adffc219785690b3efe5ec0b6dad59eb6d7516f4bdd09dc918c27d102667d3c5a2ce5aa1100c9cd9f77376e517c618c2bd337b9f8394dc2fa9e408c42808d09f1f1f9f58ccf3e494dcde8d7d0461c6cdd3fcd7522384ac086893e7a51de34591270cf22543c50c92932558aa82a044cd468aaccaf36b0bf18e90b0f5755164bb60b9918dbaeb20b40a67976ecc2aab1af1939bacaa5159bcba1e567460dce5865f27fcc0e11d1d9252a6e16f61c1a1c25f8eba1b4e2720e96ecc4047c39549e9bdf49b378cb2e5f2e9bd342767fa51adf7e59144924e12279f6494bc222f35c1478412a214a5524a07af3ba5b7d6da18c415d75a6fed7bef0b570421b8ffb5cbf5a0cbbaaccbbaacb51d91dcd0f08987b329cf2436df1061996f1eac2d90087bbe817ddfdfdf91b819d3d1795007a66379aca5bb9942064d15304a05359f46a728569eb44d9eb6d22c255a9b29687337b1e64f3453a723d368509ebb9922ea7890c953de0089b09d9452587da499e35d0f308d844c1f164988f4463f8c2aa62dcae6cf588c3eed7b52f8320d7757a67633b68b3b22dc2894805279542b45214a651b819c9747ae89053563138bde8bb1e6c4c2348a3ecd180dc2b44926cf9f646a0a39218fcec79c53448f6e3336e73b181c7360d5559f462dcefd7b5f1907b6a3e1dc8235bdd1bf30ef41d847c358ec852895efc766cc81e07edae7d9a111fffbd695c2976fd83355f87bccf3633ecd033fdd99746ef0aab719640b5b3b28034a09ab0600e3ac5ac1f2efc5527acf391b89d21d0a524a29a5947af41863a40e23019d3f547ea4b49f48e9ffc51e9640668a7f600fc3f0a79be40f5591239ffed090867f22099147558e3a1e577439ea78d8b29492f6c1127e5a405518d010cc0f5f60e18702ad4cc310ccef1d59b9bf7f4265ff299529109fbe10a57287254c190ffdda3c7ed848c91f9568456fda4c249ef274e1f3df13fbe006f1b6a88b4ae6078099524a639f132c3f9c1ff366bdac1c1b8a1b2c86acfa54cab2c330e04d57049ee6e59a822c9fc2b2c33c90ad6df73e4b2d1365b24c94c932512626cff33c0f36671f9317e308ad58e9b7f0dfe77d31efffbf0f9446a4620cf81140e8400027725cc1e7c5213410d580b1f3c2cef5b6f527f6b029a379d4d06bf2b6954ec576f8482584165185cc81de278de42979e49091e7c56f9573402b4bab08c2fc4f08964f2953ad75de7c6a9003949356ebe56ca1c1729c1e6487ad30e787821966877580d62fe8a3d5867da005ab175c08d330162a4cb19081152648e0ba2050c2cf8ffc606d67754667606d7db0398309bd664b2670c12067206cc6113aab00045b71a6b07ebbf581283e0b7adff781b0d8f7ffdf07b6e82bfe842083a168b5ba6bd7ae5dbbd6dafa5c37fe4414b0d88bca309531f1eeeedd7dbfb00364875d20d7264076180772f55adb739c295cdf4da4710121c420cbf151b4602f63885961cca2314bde8b711c48dc6ece49f3be3efd03e40abb40761a65f65be009b22b029700b9c238e0ba710f6704c1d26c6500c13cbd9b0f27deefbb33005a6a8c31d21e1a638c93524ae79c524a197fca39e78ce055d7eeee29a594b4a5cb01b536a534ea9620020b997e85491fb0fcfa424fa635c676b5d61e02e5d4c911dfaed7bab7dbea2bc8551be4d65a6b071b88d96c2d6d3392522a658c33f651f9b4adbc4f459893f63e398a3065d3a74611a610ef6121db0a942bfdb10497e6a60a164010ae335368e09d1c754b6c10b26468c9cf7cc57cc0a33982a83636ace9310325ac19e581e4459e59458c1acaf6ad0e890b703ee86c38028b2e490e3f4af42f984cbf85e9ef2889c9f437381225d3efa15467072ceb14900c9918c400eca1e9dce2c754d036a875c67ab60a1098b1ded5d9f7bb17a432d56677c114b308aa16a88a8b316d123420cb076b8cca19b92289cc596bbf0b3385fd2a650ebb10fefc5906dbe8fde29cb30517664cda6e42dbbbc2d8fdc279fb87727790dc51da2208ed42d8c28c4d33d87fc66c37be48022df2431ae55611b584a6daaa186090d279dc6dd246bf7c929a2a16a0536295fb234ddae44d42f5cce96c61a68855b498ac7d60a64091fb63982968914cee1d9916d16c6073650102466cb9bf355310205b6b6f3b30c0c45ed2e79f9c78d2d5f034e5644789b954e4459e4588045024c7a32bc28302f29c31223a7444a0a6bcfa4127e4890ddd90ab2058f29c2ee6deead5ea51e1568e3a5c0e8fcb72649378f21f42874322479b2c1b511b1c0d484a1ce98238d9f16da67edceb6d6feb850f928e3358885c2c8135c851474423b6e496eb7fe04384f3f12ae3a6492e0b0e880e5c0e2e494bec9054e66607ecca5197647542d2165b7b621f7c0f10313680d8fac4aa4f8dd120269528e49c737e61c03b53d02877973699caf5eb912939b97aa02b536597040a923133daa54699866badd5f5443e902538cf9f178c0f348c82941c315369517555365f6aadb5d66b0208928fc5726ffdd166c6114f4062c8406365bd30c13a39ea86cce8b0010be1a8830e6d668aa8830e51872a57720765fa5016cb6245122a93e7bafef72bd8ee0e59cc6f8540402d6050a10513a2ac6e1efc0e59b45cdfc2e2e90fc6d32b7cad5086d6cf0fe7cbc0d3f87d701e7c1c02e1bff18938ffadfff0fc38e3a900e8fad6cb70f3e0afd0727debe38ce6a301fc3c7d03e3e9b005d379109623656ea776220492375cee569338133a39b25593380d6e007814df62f6fa48aeedcc0e252b382526b7353830d181e54283193918013021b4941dbff00ec65786bb9117de8b2d3dc563faa284f41b4a5136483412e9099509c4e2949cdcddbdb4b15f8230075274319860f178aa429f74f885773096f1058ab6018d6c528c9e8424a1aada419d34251195c8eaee9a17bd20b272379d214496106cbed4154be0dcfd832c72ca5319277a228a22da114d116df19c6ea80c1540099242c38f2c235a9af8c8aa4f3ea6ac56f23eadf4eaee26c25b41c4abbbd5c8b29e724892147a90453e794a6a31a168180e11a00e209334eeee4cb25b186888647f1fe92c00910c416273b73bc4448349077ee11d8cc15c0c0d8c24922a4d92c0f9d01569a67a1e478d998f7dcf3f56e3e59be1ee6aa048a9a9f23e5689da46c083082b5da640e181c30d30b2e2aedddddd29bbbbbbfb008c32f0144594cb22430d0d4e5092ac104ee28edc6f9c8254e9c18d26f063cc8b1d992623849850e130c22fbc83717577f727d9bd3415ddb6dddd5d2bf26a489ed7dddd3d84cd91cbf194232ac72ec7140c500695c80b61e2c3008ec825273a7c680f382a8bf7f2a8b92348deddddb5585159bc4d63891a2b563236e81e82ab4153ad1c4f6a6892a3cb0d1b617ea04d4a329c745c434b4d3868b1f160864809ca5403132d73c5909585e14de50662726068898d241c0ba66ec841452bd9dd1da57a3ce5eedaddede32877172f7a9ee7fd8ee7799e07656acae216ab30582740c96a9f4bc3922e3d2451221acc3883e34c616e436a6cc2285124a6c84a49171758d442684cb96cc8a4f0306607aa1c948acc7ac966249b4c9cc4d81346649e7a20e26888531226604cb46ab40dcc9114a426204536a8c90e83068aa1265c80809172fa0189105eb8d4ff706e53168f2e6eca621aa5298bf6f364b87b12f8857730de71811954882405412244d59123fcd54757222044a46a4c58b2a829822ad9d012a5018c16b91a8c9098783182238a1126391f3e8034c21c6a909eb0aa9ae0a8c1a9c88858244911270af5d92832253b0effea1b1b788110a6a2226e4461428b3041e2020bbff00ec6ddc207550f573d8e7664a14144d783148cf1d32a397346b1577777778daa298b57970c46f3ca5e0ddac7a8503b15819ee3647777ef81fd8c79901be1e444caca1235e2eeca1531504f8a2013832137c8907aa1a4de3a408316207043cc90ba3173214d479a68d89abfb2cf1d7777776163ca220a2e8aa62c9a303f58cf8cd500e430616b311da9b5d65a3da1d695a617643111f57046a756e5c58c5aab8f6d42dd460709221544698b8e11bb1e63765e70bc204812cecb8b2808c9a2b2b8937ba8bbbbcc8f18418091d61bdd5c9258451db9614b1219cc1a97bb9996e47a67151749a8b5d6f851de08420ca7b2bc8175e0a87312e5c56b03634fd7e9ddbfdfef356dd98f3553570aded727e07dbddc46beceb35e987daf7af66d0669b7cdb1875622703e4a1f1d932fa0b5b1e2c4c88255182a1556c4a9127e3e90b487ca9ff1f5f35b744ad939cc18e62b6fa65f5f880d9b51b85a5bab7dfa82a541c01863c08936467f8c0a88df305afc184788dfb01847a001659a74a580f30c6930d418f769db3006fd18210d860e7372186308f1a7eb0132cf4f4102d59ff19ac5a06fc3197716e352995c9a4fc31863b443b4ee501b325557a022a681ebd3aeae2949be1604514a12c038754c575e3ebc8078a1c8897107304612c00ff4ae370e4511a38c6529c34053f93d8eb5d6c63a5f52f0260b18e527bd79a9d7b6b6abd31863fc3888cd39a7847df66e0e46f1a2947ea05727a53bd8b6b4e0ced266a905d77cb560fa39d18267964fb0945fafb7957587e21d13f47ba231c618638c3f395376ce1489724b9a2012972472c9d40923306755257784c4e1ee44f88577300ee21244e5ddddfdea68cae215ee5e29e1d71025f88577306e777797373e1c381f694565f17eec7bfeed1054eeeeee3edd02aa298b2b5a5075b18e1012b92154586a4d4d5c8045082a32dd309811641342847677efa0a62c4a5d396147e53d15f2a92d1a04993a1714e92871211b09ac0c1395293fd65a6b26eb4ced8cedccedccd119dc1925ff711d39c0ad8b1256bc50f9e2845e3903c4992b9c233f933b33054cac6224cb888332462c2db902b2e668d69487dc3fdcb6dddded4911a1c9ddf5a88b974565f1d2ff6cdbf9f677de5a6b9b9a9a9a9a9afcf3aabbbb0f312bc5e7f3763089dc94c5249e6e1251f85544ad64f71741d9bbbbbbdddddd835e4c05f99282d490365a4c115ab374e54387636a87a0a1dbb6bbbbbd1a57b99b76776ff95cad56ab41a34e0bd4962c845f7807e32309d861391521a6098c5d769d0f306cd8219fb7bbbb9b899ab2b8333385cd6c7999b1fa988c23b283eeee6e7777e222bbcf08aaa1e92a86509a255758a0c64989b8866a4dd59aab3564d6a059d3e6048e164932186aa2648c0d2261746013c4a6c7c7235afd806184c88822a66aa0558cb061ba113267c066c7e607ebc29d884c326531c9e92641e1d7d3113e492df4b1eff9bfb6145bac902802cd922748521ffb9e3f2297fcb8a1ca8335128a8ec88c6e1caebbbb3bcbf380b05154166f757777ca2ee4bb2a4308fbeeee2ea6ac8b2dd685958b30af777777a489425298da4d044a6eb9a3b75aadd6c3ddddbf5d8e5255c264f75d6edcb3e6a836148ac051439235350494aed6dd5d1486cae27dfcc23b189f08f2f486e6d626c84651cbb0522446eaaae870bb11f23e7289036445910242876e434743b821a5a11c28832c1fac5aad964346dd0e57d93d2bdf74c9dedddddde3072a2265ace0b8a409ca418a145814c46ddbdddd3d765316a7b67828f4b0f23c21a718d36b19ddad8425c8930d310c9a13c2b439414c505177f78b2a3139f3f376497584c33bd8dddd7dd61d58867b13ef41899101aa342db100092caa21546e7777370e57a62c52dd2a7cf53a0107329fa3a3fb6728e94b1a2a22426a661df58407a3784583cc010728ddddadeeee0622f7d2dce249b8bbbbbb0c77dcc7bee7bfbbbb5b880070ef79bbfddcdd3f1b99ecee59777777fcb3da6892f4b9bbbb7bcd3665f176845b51539ab28e5128d65a6bddddddbbdbddddb9dcb6ed5efc92c4fbe284bbbb5b8816625fec284266aac8eac89421703c14e0daf052c7e4c63c8d891ab3731c5edcbcc021b7bbbb7b12438680d035b8d882cc96b7239c57d44509902c4641080171932285258503899a23469a50d5afbbfbc78edc4de21151dd7d5d909b4e45531691882471f2154e50eed6ca18efbdda56cebccfb44878abe907f641b3dcddc7dd1d0cd49445a92bf60430546ff31664a7eaeeee767777cf4daaa84975a552edec6d676f782026082d516c74a0c14a141f4f3ea2dcb6dddddddddd9d642a462921acdc3d558f1a2ab70d1fbc447ddeabbbdbcad19445dc55c2b9d7d347c5765316575899fa31617e2d1e60789ef7de7b34dcddca165a546523cafbccee5134518811a6bb4fa4914a51966032c405162d0176ac2ea4ac5c8f2d00467e382228490f57372814a733496f6d54162f7ee11d1c31ce9105cd910198914aa6bca06fb86aeba82d554170e043dee004cace93b7578aef3d1d5698ec2e63061c14d5c0d45426aa88922c3afac449e7eeee3ba8ec8e2abbe36a0799d73b88845f7807e386e163888f23f867654246da20e5081fdaf35f9757a8042ddbe94987664a000008041000b3170000200c06860372244ad3388e393e14800b4e6c4c6c5c32938743d2301c12c41084611804610086611800621880c2784e3bcc04189cbbeded30096d7e807a50e262eaf74e7fcd97e46448e01cce17796c12d62dddc7bd4a265a908fd0e15de909197f9b9620550dd7cdee4bcc21cf2b7f52ae0ccc5468160019202f220abfd1a8dbc8e9136025c943791f88307f4b8404aa9d2c23b1bf60fc23cf5139dae73e7a2502288ba7beba76ad4ff25284564647d74514b0542d6026902d45941b3c559231527ec4b685b7e58bd223d3eefcfb00f6f782355578b5670ed313d80623899f90236882004f6fc28f03117204694e726b19dbdc5375bca2f774dc302a6edb29ad40efeb73687b0d86167ae4f19e7ccee4b7e880d37d1563d0ffe23bd3411bb28e46becec4215e12c6b9c2c5122d94f6c5a392de2f1c24d2936bb5165e494f7c955bb49e6fbae3eef244eebdc190ea630196d97c1389c5fd8118504628da7834da0abe4c52e57fdf10dd43031d81e1440ba7d489bca215491cb24f5c38e8535c78ba51f4b6e4357c875cb92504f685a7aa4eb8740ec916d89250e806344f8c3bdd314d424b98a573483d36d380a0e585ab1730eb50eba47543b94f775c83c7e62c5e16a337d51125361cc8fb8c0aac5e1ad8292f360242013f7ae8d0d9f0bafd33979fe1fb0c91399679e573c5903b1652b9eab0d62499cdfd13785cb1b8d972db0a101792a921f95fda5e6410ce97dd8e81b5c27825720e8e9e1f201c94a620d1eba8924d2384a17a4e8ef937516e9245b3ec7828ee8175e2e20ab1ac58baeae1064734d6b7cd783b14e3fcbe2c8fac3d93ce03c6efe42328ca3227fc20a913d5735141993c7601dfaf721d6ef48c4a096ad913d8a5bf0178c6b807b5258e4a3a9e2e40d7cb5ddb4151c86d39a4d89f582776ab4c17e716f46a72d9bf60e67e895e80dc56cdcce8ed8a5c20d3ee98c6610b8b2cb3c705b2c20b547207957c47c06320ca5b7b94b16a270c1986a662a26a63ea6db870256d450a388f5ab6cdcfd6b8922d931950009e6ade2186ec01219bfe7e8ad5547f09012e4fc902f2c9abac0f47266ec4e5ed04c28645794847400a3f5793ae9f008c583d877eff0c48bb0bc33ec2e86b726c0845dc8e5ecbf1dd82db0886645f26749af485c4d3b4f6f5a4528d3fc44f0299dfa57393dc59be6c4082c291295497735a452839edbafafdd18df7fd0753970a4da1719196e5ee82c8e2eba391f60d8b9f974cab333f0a0dddb606a10a3d43ca7661160dcbfb3b94de91730d9ca3676ac5d758c5b7db8790fe502e6a27309c4cb2cc686e2b384f45917d81ca6899769328551ac94f3b65fad7604eff4e30d9094c086979bd02cd907ccf65136e347a984acb9752bc1f76fea3591a15aa5024a58cf7b74e89e2b3a9a250e2200510f917afe871f4a3f5e8ee6219d817d15d0fa4957e2ba71f2844a3f9fdf800b5932ad1ea8102497ea0a89b0ee15998cb2ad8543dde82a6ad366f77bb6fc71edd0228d16a150cfd56c7c6e8b20b8afba9498c87e41aa05ba3cbb3d1ac82499da03578a36878940e6f36a0f44005dcfc78257befdae0a1fb7f66a80d6e070f478cda8400415704b755c1abf4b29e138100b1b35467d73154eb7a789735578912bab30023b12d490cc4ca48eee6801a8f585c76e7337411ed71721bddeb1b2864ebd18fa861245bfd672238757fc739ae079242aa2d7f7ca1c729c80a888eae6b17d54b94970ea76f86e6c127c038153925d7f50f9a3f7771e17705a23f5a3adc32682b2c265a9aea26ffe21aba70728c060b8d9c2c4d7e53437a13f7f0826c7433620ed2e478b998b1cf4bb48df00ba7d85c07d3440d4a7205d2b428ebcb80b742254d5b09414ef1ce48dee71ba523c2009d9944af6a4204a426b30f78da09c8a566404e065702735e98da980b5645e6a68854021eb71f59e397b4f43cf499e812349ac58990f997341162aaf10df805ede87471c90a4848d2e96aaf01eb95a5eb9a0c31c50287db95a850072fe09f4a4deefd5708e898e97e763cbd0c36c72e49c01fc06e4700290d434efbe1c787a1db0e549bf88015c7b7f8845ba393781ea24a1264230e1de7c5a55f85a17ab1f45bf1345abcb246687b7b5f50c2659159f685aa52a609422f84ebe885388196b73035c0da8c72ecfef551665d22de5151546a2c053cff27de1c51d2f95854e29912336afba52e296a4a6529c1daed34c6bf62faf1ecc4d9f7b337de4ab9b7283bc9aa5124f1d5eaca6711f4ab12870ad33c59b6f309e7e72cb4a97639ed99589d1a7e5b03fc96c3ac01b400ddde021178e720b213b87a20436380c27f5fe788380a449377b93d533d0d69f7c603b3d5bfb7e1429f3a81380ce3b1e1f28bd1b0146f4f9c66ff12f8b7747e65ddbb08da6788783ac957e5da9c816cefcb7b553a3cf716985be289d9b45ddf41a63d05b37aca76d2c3427badeaf012d6366811cb108629a93d57a13d2e33980a6c5c1f18159ccb5c8282b5fb8b2cb3cc7a2e62bc16b1f9e0552344627c5244fc08f0bf376e694c5da344a4403491aad52e2505ec839242427b1e6f19b00da8834f09b4ea52283a2f5bf5315708a247815acd5be42312399a002cea5cb007777b69e1b98409d71d3dbd2038a3b40ea8e632002e6420980564d651e164257ec87c94857fe00644fc4f1938a2e06695ad25579623718244c83785985f7b1fd7934ec438b7054af47d4aa0a29946a3a471d2a66492411ad5643d2db69e8f11be824fa2a4cba830d94e8f03bc0b7dafc7539c48cbc68a73507300596076470bb92ada311fd0acdfbe66819a741734e786e97eebf381b2a59e8d7bc8d7f9dd8002a05fccbe944cd19239f02b804ca4e1203375ab816afc1a330831fd9739a8ff45a316b293d6d85b3df9e60431cc27858dbcacbcb38bc0c9fbbe10c312e776bf8868d0b9dd3c7d161a03a329598b5ac8715db3b059e57eb6cee5dd5e745af42f74dfebc7d55cab2344eaf29d49f969eb982a7a8e189e2bc777eb9784cc6d84fd5933289168f9d6f4204b3f165312f397039e5050034880da9bb1fcb729830651aec0a6734bb526ffda87fdcadbf86f218997545727d5c17d29b2ee0d1415c5973b56132fb1f20161d857395f95a2f9367d1fddae45113ec1c3112a100a716f7a59f650bb0280d7b95bb5c8fb0a38e21fd3a0cae5e1f0714cf21b483a98677ea7750aac69085505c8d1a5c49cccfc8b80872bc7c8705be6e46864b14aec0d342c94a2ce40e65f67642ea7797ad60b1c20db49472a853880b8ee4036d0b60165410cc35942a79853415977f26b22afc618eab7d2c816337b9f67f06319508178a12ea950b3167676ef63439857ba6247b70adebd918a274d09564172e992da792cf1eadc392318d62dfc1ea762d53bff122942e792a6f1334385041d489c1f90f271e1ee399ff41ca16b356618cd7520d3efcebc8b93bc6c3f35790c58d897504ad7a19e26eba5369954282e5e10b6e96904e3332c9e0f39f779af3e711a281db2bb101e1a7a29528a8e641d32f62931ce63ca901ee85af63dda8593ac1e6577053530019f9b522958a14021ba444c46abb14a3581c8e7d82a132584aa448f415834a2edb06829606db422952719a1fd59c922cd25b3294b64bcb2d6337d69dd4475d3c40b8bbc3925b41310c1f4e28a2ee349f3fe0460c68eeebbdad909a4f54f54e35909ac452f0716d0fb449f15b0671bf8e3f3f488bd1b4aadd4f572badaad63c7a794423fdd575629757244432b710ca94a5a50e3562c78c1fa85bdf5d8871eb469fc119d04490f53c678f139f41f02c138c1ec724ed71e3874eb22ee90474d2feeac37d9e91ab9fc9066a10bddbee12a91f7e9c19db15a4a3c106a8851f6084fba3f21a18703d5e9fd4f76f5a2ee41d98e3537a20aa0f4666393910a72ca2f60bd26a829ba2f8864f3af656da045ab4937f1948a811e2983a81d22fae65f53e25c013aa872fde3efad204ebdb826369958731ed6ea2a69e9dc190278b3e048aa88bad8d7c43dcb745314247674ab97f914ebe3c7f6a07b4d7c47873492a3f492260b9d918412f793042da1181d1fa8014742920239230448c9737b2c7d00f97bacef20da4c9cdaba5248c622b96131b338f8b29980b363572f6f436d0b6aa64bc6c0abb68bc40f6e5d376e5981fe46bc84aacd863aef5b78ae8ec6dd606f18d6f769b2c712d0006eb1d00fd7bcc09a3680ec8089e67070b7553d4f0ad2336f0b169253b6c9f857d9643534554b4e23a67cf30d612e6e13782231607aaf7c233f112ff08dac95b95a0855a41b4d6b07d9d1285c4d3cc5fbd6603ba724f2d4cacc98da8a8cf61491a6c562888a3075ae3aaf206c3afc5b72454c8b494c02179e5ca8cd027da6091cd34d00e4b233e1ce9cd2327a5e335b0e8e0afaffa34aa6944f741b5f5b9cc62edb7f483ea68b1aa39a269f32363f599c3c468ff9a5e1e3afff265b9c66de8432966693e74e4485c00ac7e274ed358e3854220a15937868d1ae49337b80f7228f00fceefe107c097d4ee504528228074c7013c55567081c9c51fb55c1f9f266639822831b88f99b0f77b38c7753912c600d87e4ad7f24c5106a879a1d289bb7e868aac4bf2d228e22907d9c5e9c7134b1efed40d7a58ff193fcad6e81ace99fd65742126816d8b4d0dc471029fe137864494a5ca286746729cee8bca137543e4a8a0baba054889ab394e1521b362e58e03b1730f3342859cab82b399199d3a51d5bfc9dc61cd64f93ed90859aad1d640021385bd1ccbb6bd09e74d9aac83506f6c915b22feb5e41231dbc4174f4584bbe4feff36780415504023667be83baa01514f124206aaa2c7e9ad251458e4e92890678749d67f227f838a1e04f6d6b0b3241fdb21b7f3d4dbf6121b74c4dc4d1dea3208a54fa4686b7bd30ca5e45b2a5cc9fa4738740f86d12fac80d0335fe4326747ada5c8932fca68581e024b8bd510a1f2168308d03ed1a4d1265538f7c55b06c96bb86a8d3227eedd02b3a3168cb08184d4585f3650485ecff101252e2e1da1c8109be258056165b0fb66d57c39b8d9093376ee83f3bfd6ba5c047441ba5e2bb14c0b96d1f5ef1a3fae06d7c25357105f9a9b1f04aa8da9f48e0a75adcb1236bbb09ad59e0b90dea2117c8095a474cf14dfd0496680dfa8ed75e30448b2945816f4344fa24df67d140817bbbf2b744cbc34d3c6d6538862ab10dfac5c5b3d7ececa492d178efa97eea94b65701cc569223d26d68ffb2b41586cea90adc8b0ce7eae5df050ecc11c261610942ecc032cb1e59b73513f2550836d754e597b62efdfc541465b9dd2aecb9abceee8eedb2be0a39373e6ebd97439cd04832dce8baddab301b8cc168c88d6cb6412e52e09b2680ce46b48011d4ad2ca896ad1b227cd447c8ff1440cb4eeb1312716de03319697a57ab32ec82d41c446f64e514e19139cde2edc13435e9b50f33ac35fbe9fd3c6b76fc246d0731c6b8bb7a9f76e978e200dbb55da5d92df2895f8a10e1a8f1b6496316292c853aed9673ceb56a17414954d0efe86c900b54c15f21009b132449209815da846cd6b61eb83b834f1e1b6efdcd10c4a7fc49807e64c391cebc8159f004679db98abdcd88d2152b7c5c89722593e06ccc39bbcd94798a65a5070fb15368b7642f974139b2ef5830c207503010fd12d1b945af02f2a4ad4761f80f545866ab160b2853aca6db8ae3744b6a8f6d6ad4ca468249db37d764845af6e2a68632d496ac712663c61be95f592d6cdf51d1681dd2cb90ee0ffa851cd3092393bb85f72156b8947077b5c873e6252c32b090b8b8258b97c9c71148df4dd02fca4ad958af4683ab0d2308acb0548cdc0bfcbf0586950841cb6cd2ac370100c92caf862a989121d0847541ac490316b7f87f601c443beef75c399856bdace24e17578fc986f12509f4d0300fe7c541c41dc840770d54561dd83f9b0424a54fe7c139e021be3ebac1be033073a9f0cff8da733402615b4abc90ff4afb6448b4988ec2178b0d7c51a8f05944e4bf4d268620ffee505db97df5c8fa6ae61952d863a61b6e6627dedceae6d617de2082fd4cc22f05e90f5779079f6aa96974a51750831deb60ac3e955d7253a0b14249f05bba6f356cb9bd9e15e786c49219923c78440004c79cf48c5eab1276c1a59a064761fb118b3a09fa2fda8f3e7d33c3cec9309a45a7a93d2dd45881e382c50d519db36ec80857d0125663b7167addb3597cd74bcd16bb2f4a3d3d626bcae047ed7ccf090ac17ff68a3e6103fed68e832de9da67e13aecac99439afb749d97d08912b25a79c862d07b40eec4fb13cd7310ecb4c0a96fc7fc2c744c1bc40349b8001ae8388c90590a4b60ea8370c6d4b4a28561100bb4a48dccb410120031df1eda0b3e0490ecf1a93dcc000ad360f6cb515c9ed824cd1f4e7417943f6c4c51367d78543d059874cc4699ee821ffdcfbe145c8ef71b8f8ede9278f806dbe28e37445fae12b281184b9692f3da54148937c49598b94e2047bb8d7c038e480548963ac41f584d797ea9493130acabaaa85118be5d3cd126e19196b43978e5649791a9bcc12f80d7d425a32a2b71694a1cbfb8cf5b89bc45e4360f518d544eb9b88f481b824d55544d5ae18ef792166b40d2d6f16941a26bafcc0aae24f2f4826869a6cc4a9df173873059852d878910c0eb096b5f5847b65e08915a49d032b9f7cb7ff65a34117f40b15c53a5acfb20f42bd1c6a3b929be9442b9b1f79515908dfdc4d63474661dcc078213bcc89f875ed5f8bdcb867082221d5edd14ee2c330aab1186b1d1e3cd2db83fa282751acbc5338a65bd4c59060c289e8c190e2d86d1b170f3803e9bfa70751d0175bb62e528723fcc6c2a642694c53e9774a8e64f285638f6f8557024959e6894f366855fb92a35c25e0e52a57f5036a0967c2bbfa90fa8d86aec965a0fb56ac9128769554477cd30348c4cfbb0d5009ceac358a17ef441b5384ed59c51264bc63f23b01b40d0e66604942a34544d3bf6515e9e182ff1421c528dc6713a10e8a654ef65e4e717636d8723d64265641d0e40f80a1c94484a7cd43daea567b9a7c22d4326b9ba92e0d82fcd0bbad52490d4ba25e337fab545c0037cc1f12007856c4dd1806c14c35abc484c8b47b1d0af8b9890b5ccbec1b8535e7a933485ff37b4a20c41c6c80c4eb4d78c34aad839113c90ac5fe50882b3405a2e50583b76595887915f103066ba682a10cb3710c67843d58eb4b4ec00c98b8b928c4759fbe70ac9892c473921abf01bf4a0031e57962a321ec79c0832b8f5a68449b3ba0a517fb28f22cf27d119e09922d04c3a6ca4d845a2989b6b0c0aff4cb251a0cdca2c3a0d9a4c888202fd2a7f7118c82e41821e0d31f030537e35a480155c033ca5cdfef3bf7ece1d8ba221de6c28a8b5f59d9c34167cf00c3d6a295f2a7d069610dde9e3fea085b61a59d5e4bac0c5733998eb4c5d9e20e55a91340ba67d5262983e201d24838c3e7bd8b6f69d90cd171c60ca63a51a958df3677bf26f3ed7fceb7d2ef4b95e9928cee7bd9d005c62b9cfac114a241569d2172c631b2d99fafc329409f625046a2133c8a60f8575f934040e3b6692b959c95167ab80cc790342e19f60165c15264d7c443b637cef5f81f0f0ba2aacd0e685ee168c666eb499db7f02f93096cfc9f2a933eee32c3bb6e76bb109923c50249fe31797c265763c3e2dbc192e5c076d6062c247de7fa8db623da1e1867b6e519d16c85559035c83ed2c0bc6d8b7d11349fb9ba2c1772bb74b2f3f58c1c59e247dbc9e6bcde652199784b2cff68da7b2a5b386aa4861c21c51df5dc247881566cc5000113d1228ec728f370d3e3f1541f6b8821ff74c00ecc3c638c16f9cb8895b6a3099387ef9f3d483e060566387a180e2c82f30d33c85a139e5297dbb712c32ba7a485161e62fe404bb268c2ccd481ac5b24f058cd211b4e7138082a4a6a43e5c3ba87f2992553832115068844ca04eb0b7d1ce60c16d8349e4f6fa2001e82ac921b6864782bc1ad666fb95815f903a4e1ee8a7329a6003338d72f495de4d484ad1008c5c6d8af2f286960d66975b3eb93aae116bfc585ac43be912b9bfef34001ea86f687155728399fd59ff3f26483e2c0ce5f1933feda241a4ca2a5e914219b37aa3ef4fd99eeb455cfe45c139ef80ae67a5b8f4f3b790c18793fa2efbf35a95e0589963aa508e236859045706e19576ff712775c515e149a918d151f9c27c914cc20c759aaf30dd4540c7f5ca32c0d3492ee61a74178d0681ad457933ac2aae9b791abd59ad0763038226668ad5e02da0d60f380b6b878ef7a971bbb6165cf82c426c7e9ac54f05788734b35b9f4641594f04cfb8ce5dbdc81f0b1d9a63b1f6644002ac7d4449c7c8718d1ee6ad7d78db3f34475ae8d953fcee1cce333fb0aa5c02621a8e113744d18e5df3c4f2bedadbd638043a6399d57bedd6a173c6d2924fcfb3000fb10bfda2c452f46c43dcdb4a98718eb42a9c2eb62c61749105f2ad6256f3a4f0b5973070e9706cc716e528c209bd8c75b00a08d36970492c9967246eb5ed1a5da4c508e0b8510d6efde0c9b4401860eaef68f937747ef5856a7a2b2fc9e3ea533745900cad2c4fa05d437a4b35373252b823e0ef8130846b028913da8b2af7492df48c611ac1b270d4a4b3a410c147c79729652418b7f8370b52db8b224e3174601a7ca0dd3faa787e937c3252f5a6466edd2a35e2c35ffabc3eff4e6a2df96a4d5ad90b24f238829eea5e1f22584c543b06e03cb36a812b7288034390c217f5b8f2d977f875610768faf4bf4b8fa1a410ae5cdb45609c59cb12572f2bbd3cdb2cbb15d8e45b857ef4e78efe07b32d89721740d8ecc65f22251ad2ec421a14fd544223fdcc5e0eed64803072bad3da0a761fe1c5ff6519f7b10479b5241fbe3bdf28e0c4efa746ef19ac4a0614091c6dcd284118234012e618449f9122a8c46d31e6e087026bb664443b158d2a82fede724f51d79afddf283fdb07aabba4a648f85fe577efd9cc34ed7481ef781818224694fc341d81a3d518631c577430ad80a0c264b97adf3e2aadb634e5eb79ff1f57945ff2918f989dd31149ac0baa1dacc1e30faf311e20d246b8f00b53f628d0c5fac7e0c1264b6045191479b4bc8af40f62a900c156a35e2946df7735e34cca7e30306170b5b04ec52a8a58c7bb8cc0739b27872e0366ca4acec7afe6b96ca008e4688786a1998f6a934eac6325162bc87aed93cc5972f64e67ebb5a7416c73d671e6bf06b1729188910a7d30ba1540d5fa562753837a3436f3cf7d4d15fd32c930c19cbe1e1e78c00f5371c03454d491ba49f3c91414eb461595e404fa79e590b442ca6ea2ae3ec14fd97a731507a4f8acc80f70ee6bf5dd21d7ea58783b0c18482ad0aceac89fb2e085773239b3b476564c94bb536c4fffafede0c857d091c99a52b3c631d96f380ba51ce052e5484d038ad34f3ca44a58652f55b28da7aed4927084df7ef9d1fcac7a1b74801cb5957bba1ce17be31b91c66e587b7df5002de5a903816a7de2d0b17340e4ec84ce5ed6350d8a6a49d90eb7e1bccad4e5917ef8e5c7f919e0d88104e4f66c15b1556618b5a353d58bd3a54bf64ad8b38593ea61479b16a6b171c1a38a8a0acb66cfcd283c89e97c744d903888b0c8a4e892af6d3f9df6000566f41149c559bdb1417f3d4b4979469e99ac1254adb0d0a54509b2ac09a383cd7ffa98e0704d68944b5802b0916d565b33633b6e528eb91430208ac44295edb8e1edc33e030030fa4ff58844806cdb6090ba0edbaeeac66a9110829fa5144d1cc3dd808c4523cd13cf11449dd6035dae41e7b7b1584b1670ca74cacd13b9b97dc803b070e53f05f356544c36a6704f34a6b018ba795af13be0b5e62e732bd08e86819e190f8c8fa84772200021775655740b28ad8c9ab7187e7e95eb679a3ac7734d7fe93de257f366f883712ddda6ce6b689651414b0bcb7bd12777987ef8d50c246598fbc629ec3f2f4876fb7dce5e80ff901a98fff7bb39e4ad2902045bfd409639ba072f2779309d65615a123cc95549f24b3938f96587fe326250be72d4c862d62dab016596b52ea56578ed34632cc47092d7993369d26ee1d660b38c444b69f9bcc114b080dee9241a760056d49cd14f206684e4fb8466efd84f1102e3606653b49013ac672f714a2677c772f50dda8402a647018a8d0c31dc93887ecc2bf587c7443641bc43bd16e6cc5e02672a134af59debb424c44c022c36de7cf575b5ea6062a2ca6424b441d88ce7edf9c6172d58625e2e57815184f72317cc5c38513c861a4990891481b37737b59f77324e5abd436c2eb329a4baad3d2012e9183b79220e0643503098bbbd66b3b105aed82892eae685c0402f483d54d2c30a3f6a3c9a76d3549dab3545b8b143cf714d00fe7e33ca45cf651592f20272c8b9fbe74252d1e06f8c461bb240b0885bf0c979c6258e324ede6a0e588e8ece7a337b7576b068f74b283494c601e492ad026ceb3c6a9a20cda73d2c6f54ce60d990341a44f8a40d7c395b8a75aa4e61e3e1513ef35a2e7777eff00d3e7740ae716b00568315a2980ac1cfc3d2580eb74816e658999513970862bacc54264d004cd663466b04db126bf4f6b35b67718644258ce52f784d46b8f5f5177a4be2382bde1202412e4a3b74aa1f8459b340f3873a2549c0ad3fcf2313c71164c41797eab8697ac088f2862e8d87216ed54d4b8ad12397183af00a8aac5c468da084422a4920e60578eb5d5b6c3fed2ba934de2655b302e89c72a039fdf98b2f3952ed63de33bf50e4a92e52af49127eddc476c6749bb05e6a015056a3114e6c9ceff5e1f5d00021182b08b64fd8621a01c70b4fb7144c2a41ad7b864f62ae51c42916c70c4fa6a0c6974e9bf373591d3c679e428786e19d12e5dcb330bef8e0a7a4be1a5f61855648c1c09b18bf99f9faefcb1f855d55b736fa81f81678dc470bbf851f863fe1df2b364ce6ff5716cda495a41e701d136d9de08dcc37c069bcc783bc93e263c0b45eb0fab9e402271f5c60e9160c221372fed360ad83952b2057ee35c073bd93461c1c606be8f504a95aafdf21f60b314bd41969c3bdfdee90f5dbd8d9eabfe70a7cbdafc174c958a539916cdf7ff83272ee670708f176f44949ec9a89b69684c22ed2e5300e572aaa57951796775a82a8bb50e3774041ddc72791eaadaca513994f0b3feb6e3a09878719a625089956d351e113b382612dfece404728bf1fc3d032936138d95bbdc8caa3db95b4c04525657c90473e828ad9c82ae412b7087607f3b0e418b0466ec83ad9ccef083300cbbd230b6b23478a27fde60fcfd79919acae8139415d7b405e0d0d65532c6e3bc51822d25ae39e504b9de26aec2e06447f1fdd06152fd917f0bba98707ad72fece73420d852f0545fe2d633fa660c9e742cb3ab2968499078e729604866ef9f2d9883eec2968f3f6d27d0b27a84c50982d216c14b401799268ae169a8337086b91895bd183d2ad690991d6780ef32debaab402d20ae034631714fabb1540ac4b030e89846cb2143ac2ffcb00d046507835cc80f39c1512e6b1a787d728966c0394db1af49d279b2ac20f81619c41209a542ac17fd8a9b5c2d5df5c095d26e1c2ed71668009392b3d9bc172952ff067edcacb82b3e21ac99ced6bcfd5ad53be8531b8f6170a86f5a9e3949faa05e4790381bc1adec4037db1ea51187ecad9581739f8abe2771b027c35bb5bcf330b34f0036aca66cec75c2f343454463c5ac38d816fc13b85acef6810b63483740dbe651f47930afa9b9ea35853f9cb8c59422ef327004726dc4c30429829921a0a2834e30084e0d6f708e2fce12d42ba301299c0f026f5076d7dca6a3b93582c03cea653a1e8e664bf3c1c4c57c6183dc57f0888ae2c07ff14f49aada5636bbbc152cadfc6dd056150f9b00531efdf9f240f40526ec5c095193e0652c95f7c8872ebaa342381b30ea8afeb1d47f1573a489952559e187990f3d380c86bddba72401ba7f5e40f6954053f316ffb16ab63be3b7a70ec0754aaf4451065655189e20633feba1db037cad232325b5ced32ff6576b12050070af42e5f4995e103951018cafc20ff0cee8ea925322ed004737a4e1caea4fa1cc93327d249b06486be117d5436af09a4e591285748d9b2e28c2586cdedbc172c8f87e6de737e441a403a2cc524746156d85b1d488a5860ab3c0c2e1680a871d9edae1b38ac4f1781989bc71ddfb3e3613c930a3bf56366684246ee1449315d202bd60253f820b34b19a794cd2041a704f65661afaf8cea5516b732059ac1f3b7c003165df4cd599c4c47a4ba8ab8011b27cf2d02f864fb1179adeb8d50c4381112dc138739149f241d1895a7af1d801fc7ed3d5e58481c63760a4c81afd48f0929a6e71d703069e6bdd30ea703c86c15a8248b3a3b486377ec2530cfdc8d7b65b6665640c88e486f03ba7f2dc3d8863e8108c84c27b0ac02abe1141419529bcc22e4ccc5d5a25fd8b0592e562ebb2e243bd5a11d1224442a4f099ac7a88cae8aa1e2d6496ea25fa4bcda80dcfc6a19e71144a6ab5002e24179cd921bec9d2d84fb020afe4d02fb4bb7405985d6d0a1aff90d471a7e17a367ff0fe1d91a8b449a5bbb3412672ca84602ffb505cfc3b5c16442cf10d81dc3b2a9ba3952c0a7468e308902b6a2c5159c2ec6d0703c846283be519b0145c7ac5b14beb97e7685bc5ce658efc964e4448f89a6fc846fb5b892c685ae4bae52c38a0364d556dd9d67c7ebee0a29245ae294243cd55beff4521c5ac4142f2483d6a69bcce5d95a2b50d19a12b3111162c05eb8e818ae73292a5bcf624d6d01dc24db56d893150112ca3fb987433719b0f220b224806bc75588e1d5d91d20ccf958ab06e4143ca69b5743708e352929913beaa115c1e686f73217a5a8dd2b3e8e63634268811af1c2f8d4340720c9152c4b90b337e30add11035092ba8091c8729c405dfbda8a433802414fe4701569c0d92f6e9ca6dfe2bdeded5effd3a030de03457dde97c9e2dcb0e15896245d994a4d1bb224fb3564db88f9b40c6200ac45ccbff68c92000f2cfb49f5a7013fb9c956886d75fa825f57660659ece3e79f1b3646b04ce65565e5f75054a1627a35be36f441f88c1611ef7fd66f23c0f30c0d544cd5b78a7782241d7ba7e5c460152db18dec7ab3ce94fa5b2d2a52d782901556a5a8102470cc94a3b9cb37ade480a458e2f22932805c07e3b8abb0f8ce3c04af60c7ba1974522e3ccb2159ec690deee35dc2f6973ce1d5890e705df2ee2b7158dbe3de41a0b734a03233cbd14504c61d9357de5354efed5444d03f3462c2b7a5f75d7c647f4cf3adfdec8599b9e2221bbe96bfe64688c42f61389e7eb7316af7d44bfb42478adcc0641cac1f828a5086312d1b43ff56dd1481ae5f8ee661790080477d4b1f1a6219aa9fa6bc6fa389d3307cb57c5874c224d21767ef1d6df9a1622a1b4600d026466788de688f11f54749e3e7ace45acb5b826df6dca657dbd6f40e8fe748d95b0c4e543eecb006064c7c7f8660565ef6da962cc92b9bfb05f4340f3a538273dfa02f89a45abff09f3968253e45558a3c2fbc522df96856bddc9c5150d27be331258ec21dd7df96b943e57605817606303ba1b835d79309f907602fc6a8a29ddf69fff392899f2c18cc7b00e319aab830e575201a2c7a0fd6b355b8f53db34247a26097c45161bd655b6539c0700bf849e438f257d5b18215a0146822eedac3602d6b56b58a6c775f6af926bd21b60099939dff4ce574aabcfeacc4211c33b90cf74672896e836ba23a70c8bbda29533b6693938eb4a9b4ca4668282c8b240783883bdf00b4401b6a496d9958c7af817f3b802a4e6aded5f8b75a0194b71f86188aadabbcdb96517032b2c94fdb80a037146d4b5213c27dcf6a0cd729d8cce45bc22e15d3153d2254a9f89823f15ee01d9eb3a5eed55c0bc03e782c56f7fa5368f112c8397a61682d71b63c409b4acce2892581834dbe62401bf7339076cca5652a9adec5cb9c7222de6cee1ee670f03b85982a782cf59369a447d714a519c7c6d9b0097ed61796b4b106e90cfc1e973204fc77501c16886e0502564423b1d386a634f97944726f948258018c7efcf7f352b9a62865583505deee1ff91426920632b027f9a781ac88ed06be4b61d02ccfd811e08d432733b6e2f673c4da7fb500cf88c55ca87c06a78acd8a0feb6745c660a1b8878a3904a530d0e9e52f25ef1239a48b7ce4576ae45e8f7aec1c5d1bdc1219ad174da9cd8da347dc5228fc1a5e0e073716d8163522f0f26191b7c619609d7c42482b9eed93d5d142fafb8540dbae69c858a3d368b48ca51229f66d20885a2d07eda98776d63f4b6882c47c819198af59d810102ea504e5f8e9c03200d07c20459ea99729511a7556faf50274a3efb8ed237460bfe85a37211d000105be679d0ad0340e222dac91ad7a79b1e4c674132f482bc996c706931e21fd99d858eb2a05bc4cf08651278c89140aaadfb1c61762c09bab46a0f0d0d1014268c8935dce358630a42910867ecac5fc11ac557b309aef4c38359d4eff9733009bfc803e76c7c191dc38def4be1e8e1a38a080c2ab3e9c900f8cf5286236f47d3bc703b368649b08f808c7371a75706ee6ec8f46490e8ad67e390dd7400dd2e0c8dd78624ff8b5ed227a1e99c50ef4da1c103a75935fa0a66dfae8fcca998ec8e13d79fc47c4e6fffe68630050c0c3c4909169c643a6ce7181e89aef048d250f3903d2f5c59d8defc434edae0e109da50d3b22b915135702ed0c353506fff78404e00011253af30d5d43110cb09cf1f951df7180879df62e04a24f149f4979d0a353776151109c04da762a4477350337b5fe1cf00e58c2d327a83220ee54758c7a9159ad08a873b8331d79517d6397daca247b1895977940f745f8420165c3379c66d32da0532fd94bbefca52f1f51a29b111161b519ddaa768a6e6fb727d09135d64ef506ef7568175090c35ab0cb3b635abe56351eda18a99caa0d57b836931e3192efe842a779d3d5bcc951ef827935d18cde2fb425c127410ec97e5bd98b196d55e062264ac69c08ae50bc06dfc687549c1f85801dca8ca692cc6b24d2b67556f47f4382d7d8160f278586c531f056c31e829735a055c2fe1e241695d37093c91e07f88c75297d51c4e571a9acd79bf48ff1e69df519fea32c6d2e5eca4725eee1bb74a27259bb22fe84bc9928d73535afda00f0ea260f943bab5290be7db716d49e729f9c03f63b213aee1f267e467ad050a83ac3ca1c267d605d2ce6002f36bd079b2ce52094e1242b54f8d1b562fc7e0641259c76613b89bb8febaecd9db31e798dcbf9d1ae225db0675cdfbe88091ccf42057ac38a30ac0aec7c7e1619d749739b902ccb0f2a2ace456f3ce51f43104e2b6d9c53ba3a3afab8854a7c23cfeb4881137c4f03a2b3f11311bb5c1b8266ad90e9ab36f1540371072b4082634472c0c3fd76df28b2a9cd3baec9bf6494de48689c3d15f4008f7641a012ce8f51ce3ba142a93d11a89a4fb6c38047ed3f9d2fc8392c041482dce85931b1d6ed5c83989ca695ced4a186facffd26c350a5d0c57d6f5ea28a6561d4c4afcde4cf6b742c2b267cac57fc80cb4bc6e6e628be67017945cdeda41317e6ab1ed50060512624d035c4345f944da0931c5cb47e5c1399ccd44085a0ad8772fda6933847675b70c0d47e4a1dce764205276f7bc6480628cbac43824fea685bae4a59c52ff560c195d312ebb61c9bbc5fd8f3ed0e34c3d6f32ccb4c74fb7d339db9fd6f0d327f113d0b881a472c52973beff95dd37511a6cd41a9e82ae16781f0aa9f947026758678292e331fc93d3b2f072f8a33eb5ae45a96ed5c95954063bf81d2411d6fee31755863b5eb140c3a153dd86f2ce8d6a727a16a39e9b62c01436542354734fd390b2d4fd6e50ce1e9d3e6350d0a7e97af64a7bc10925ca8ffa86e68f7d0dbf407a3e10c5c3e2b89ad23acce88a4cc23b259df168cb852323de2a77610e4bbc9d26a4e1463c5a022c071f239bdc2d0cb2252a4a99c3fcb085c829c374b53dc040f2154d9b92364189c245391b6b8bf3cadf854dafe4f8f0360ef7411d026c462783065b1577cbde603a0a194feb4c3c82688ca95a46b6a2ff9687d5ca220ac385d40be8b7b00239764a5ca1d7dc54ab5886a5f08c43ca2b4191d4e9c0d02790f8de1682e80d225c84bc1a516a6998f06923d3d33b320e03ac7bb7b80d9ae505995a28144c8ae74aaacd5d2068dbd0689700459bb9eac474a00c124420a2d80d8486ee4496416173916122bb79119ac4922e07406877ef96283f17c69ce777956db292ee4ab3f380c8fec182961df7bc133cb5dd520b6360d3a91644073b5411f9bd43cbebe4b04f213c882eeef6e44dde151997b10d650eb43f7b02abc813121ce171427bd345345e187dad2baef8c646c9bbcbaf25757832aea9a926e05f8d59565844b4bb59198739a05e50b4aa7442f19164efce9667083969e885dd1931fc03d419ae61124fc64f68949a6eacae1d46a1ac2f5a28d250929890206d184bc992fdfc6a15a909f8113783639801e9f9ba2fe524bba4998f28dbd569e8881fa06e5b6c480a334b1aef61afeb84aae77baafe06c246827a00fb8efaf26ffb677ad27afe1359cbb7f4943550535811b784f094eceabbb418c5f3b74443658d0d9bbb0c002cc17f0029b6d082fdc21a7b855ee7ea06960a57c6a7898381391ce96a059bbafd39693b76d7047f5edacbe3210f303f063e402737103af3ed72b1fabac279c44a2347de40bf4e6ce41c4d0875fc710a807f71f232e87331e9029d9ef444edd56aa0ea93d84bf7dbc70f184445e60f6da830886ab8650e033022bc2d99385cff187a579b8ef5c936090ab521e966e94d85d3381a2795b5d6a6e5dad644407d380006a7227351f093aa02ac795c93157fb0264322fcade1f0b252e9f8389ced158a252363b54f3d55f680152e78c5088e78fd6476c9c527f423f76dfd0c221a93862c1f96c5880f6ec5f77760d044f06eb11074e2b003297fbbee1427c08ab0b03d645f541d7167bb0db210d34202772506c0555c2878bf9527337ca3e6e3af2090ec5a797de947def23c247eb07b570b0d8e44a91568990cb800d7353ed43ea66794e8e266e1fd5eff51c3eac1f26090ce07a4a3c7781141a58a2c573a110297404a30b54950ed6271cac6272a938934e738067acf0a48e8bc63b3009111304904c3a4003667564f5240b11a7bee5ad3832c8750d15c9106a141d5eacad98a3cc845f2363ded55988f706afd67b3edd31d0554d2a2521e8621de8b3743f23d3655ca3ab1d76693372a9abac9d7a025d505ea6ab1dcea8a40697eb17dbd6e10a2f261023c0fa3e73e2768f48eaf9a0c74dd1e7033a9e770aeccb4726f4fa30a88c6e7785b10af9454a1d72c1279b2c2daab099f84fdf4194e7bfcbbe83eacbb8724037cd6329c0014fab0e60b80f74051e061155211d41a2c2bc74cfa46eb528d0bcca3fad0a32905a42c0489d0ec8dfd2f51cce23b09e43a72f50cec7790c0bd0dd1c8061cba11b948b98e40a2efcdf3d17395439d9b9ca0a407b56d166549fd5376c1824d988b7757b7a4de109eeb90d128bc91b7c34de7bfe8f53b967584686cb2cca529c8a8fe45b22bad5c51c291caa8f96bcd81e32b0da141b5095792516e249b9b5cf7dd2978deeae8a079c5cd375746973e279d7a780f960c1d4ee1d328f5499174e0558e857adb8b400471102767fac00445b870ef56560250fc7483ee4b2b6347ebff84b516c18cb15c660577751d141f9d412b2f12203c38a6c2e84ea4312fe8efd9e8393a60f5e9ab09dec0a9770d53a02cc37df0929a6ca010dee2fca1cff42b693e85d8106cf85dd61fd7f19b0c7e84eb3122f3e4a4d18d8b484d788ccbf64bf3fcd702c0aeac250807623b99407f1923847af30d3562c43832654c73efaea4921dc465fe1d152fea0591926759b88da9f22b57d573099dbd013376f3bc286ada71917569b82168e3f79b5f4a256bd0f437fe93ce451572b6df469294915b1300865f64765cc7b4952b2100df38b2ea83b70db43130db9f48a3e1f96e0d76486894f35735c8498d8f3326ea022cbffa64a02f4ff02da434e755920105a4a076a112722e48449c1b2fec1816ee22f2e89068084380700d0c339a5758753c0118775e05a2dc2e1c9986a554a41a973904aae3ac44de152c19450f20e88ccc383faf55eda1b691da4892464efbdb794524a99640a7607d206e506dc279b3d30894264072d30a32340b0f8495ac0dae888c3ea2741002c3efd9220004664c5477ca21e72c6fcf844567cd4a720510589acf8a01f41220a760e907c9932450d78b8811858c3ba4806305a72c8614b1916bc8135ac3f7cf978990c16e391077d10e58cc275db085fbfe598ac13044145932758c3ab7d282483e082e17d158a8080ca15bc1f4a30240ad3245f4e480ee3cbfa2a1421e50b8203184d306c214b10741827b97e48823c2f08824e932bd8834a11219031623651c93eb9674d4af214a27e214548302b7b7106882252c864b03845d6c10b298092042400493f78910dadd069f2145908648c90053980d1520bb20ee304d61f9220d79e85d99287629e42a4d3c06c8344fd424090830e3418122593c128283392c9600d86de1e044220b3e50eacbf3d08ac6e7046055714b97307d63a5859824b9c9cd15e4a5ffea56f29add3a54775c82d77d1ea4e5cff95f1ebcbd7eadfda92523e4fdb701c724b5bf5f44c14d83a6f034987e2fafb67d2b611b308eea26162c82f8999e0fbb2c6f6dcd3f7b9df401ff6197746cb675ced79e0e450844c9fa7de97b967619c68abeee5e79e21e1947ddc75eff7d897d9738f05676168c83d0b73bf4eba2404d72a399852973804ae5570da65470f47bfbfd71ff5fdfbabdff5a387a3bebf7f05c3d861cfaebd90fbd89bfdedddc3617f7b7b82fded04edb7e8c55a302232449663a108ead8b99359e8262a5096e47e168a7283dbdfe8f1d89e935dad95523ae79452ba671a1be8520f47ccf6e5fdb61e8e06b7a71e8e2d7f0f0f6bc316c01ff763443994f9fe0deb47ffeaab2ef72ccc519e94c6991fb6e84b8434351c4649c814b3e810ae3b76c7d7588f6457f3a9d71fe186b8031e649f043b436229db9f4097c9360a1b450e59fdd931b1896c3db76c90edf75bfbdd8f954a3d6b6293c31bce9eb87e5f8509e5f303ad9759b2efe09a9504776098b27b09feb285a58e15d7a917dc8d3e65c8c937923077b13a0f8aecdf39a0080b992658aa90414d1a1760edad3a774da9b20287ae8c5d60aa647f1a2464c71e24d374b96760907294475c6324d8bf1300d7aa3bd8bf4bfdc0e59ec940ca0857d4c0e5055670485202d61dd71f0ec93c77ccabad8664de5ebe8f0759ce6ada6faf59cce2acbb3a347976bf75cf45ef2853162b7998cb273718d6970dc658094334fbe7b5b6fa71d79c6018732cf687bfbee15226dfd53664969406f92a3a2a2bf0531920b57c5474707c0fc2b58a0adc750cae5d892825c0c4e1ba2b92311acaa90abc31f19441c8463d5218c346c2640ffd198cf91fd7053a30832a14f6c40d9ca8fcf83d3ba66c668977b887a39964b2ec20905843c964798845a62f674c94d1d185b82514674c60c9f345f01a1316bef2ec31ac2c9d72d869ba4c51580148ac84ec0de43f5463fef1a009a47fa8f2b040cc831cbf1bc83f8238a26742110ca4169620d3273281098454210e86456dc1d0ff470543f9fda127c11e1ea109dcfd93cf454f87907f7ffcf892477cc923befb7810c54ef07c7a049838864b9e8f5930c8dd34420a86d14ea67f45d3e88f779a463f5d40110cb2cb9a86ccededf021396a36e05a05d52931557ccec42039cc7df20a5cabda04e123b2e4a72f4cd9a38afe8c2fe7d3afbe05a5bb0eb32f5732cd3df36226378ba53843c0a9d0c45914e30d2c0ac0c0929254dddb3f1a8c09c9fda17fd432f3b2947be6252983907b160338190f91325790de903d15c401def83fd3064e8506c70fe57bcf90ce21e3cb9fb4f6e0f821df853c6ee15ab506732a2f4db9ffaa54378809f9063b035cbb7c29a79c524ee9ddc7b5770ddd9830d65a6bd938f98c7e4a001b28dab2d65a8b5b9885f151bd8124e9c819eb84cb11d9d65a6badf588987fd5268f67a89c517ac375134b4c8c34e1458c84aab5623570ac88b31992a652190a44146aadb55a927c4653a10ad0a2d452c16bcaa1f2f3aa36effa9f7f2827994cc6a49952000545adb5d65689922d6e6116c6485a645b05c949924c2683e13617304c4e906e0862692639452fb5d6185e1a972e4e7ebedff53fff1c945a6badd5d6b1758cea1cd5815207a98e94eaa22fb0c3228a586bc53b2ca1a2e402399452aac58ccfe8346ce024404b54028a30b2af36d9565c6150c2065088d9d0d48026090d9527cd03a828c46c29098d767192e25089a314074b9ca5eaf241d3344dd36aad504b5308163c3d35c964b2177a36439a2717342aa98043134045149411863a6a3d13123b4c6ae09844a1e2882f6bda284109d1c4a408265fb629e20eb32c513ad983b5d66e6f98b2b51aedf04f1740605a922487d082df2793f7a2bc443545958972a2cfc24b383a36ae71f2a2f9d00224447ac8c193304f37c824a05e809aa19bcca148d8cce0821a1c81f4660837616ea8720310de26a2808820d69c60062de4c8e42491e3a4aef1b2a6694d99354e54a36fcabc717af3f406eacd9ceab2d66af488b5d682b6d65a6bb5d65a6badb5d65adbc685d22b5e6174c5d115507e1e49d29c77fdcfbfadb5424139ddc16badb52a61f2190d668c12332d2569def5b2d65a3a6320c1052b366052e3c40d685083a021c93b91c9644f6692dc4c443425c92cbcc5b9d3e4838ae6a5d17ae91cf98c868224a526804e955a374dd3344db3d65a6bc1aa176470393a377807c76f0270acce050970fdd3de8a089e42ded3013336b33549e46ae70f4ac19e1df4417f39c1b97a82fd238e56b8a516bd1433615aab5370c843c16532996cf6f0f020d289a5566be9f55aaa237b66967696dc0e096e878478244e8f8a32b717a5bc70ebffc74543b3af851af83feec24db43543d63cc91a185e508557d634edc37be9b8e2bd76d35cd0344d7b9c266bdf6a93353054e1f51674fd8f977bb6c6881cfeab0ece32c3c1d24c0a32584beade7b71d0848333f8051767c1acd91a1ef2967bb6262987f8a9bc5e9888d7abd5c243b45acec3eb85b14849a725a5a493bd951dece1e14fdfe5c4585e2f29c0c418638c317a03f9c790228b1442e427966652b02087b7df6bee991455722b680ea7a283bb960c64d9dfc7591ed4817e7735397cd94073cf9e686a02f49cff410ee3fb175521797ea89dd0687205a7d4ef8f7efd88f27c1b0ac9f55a10082818058972a8cdef83fc4176e94ca8e8d02073f4b110b3bf7b3a3a6be00ffa7d40866e8ef51d1cdd6f1b0786a005f6e6617dfadad7da731cd5a27b3ca6f6dd0b790eeeedc78f931e8ffbf763e8606ca0f8da472dc81d8a4535ba1aaebd46ca6bf99db1286f9ff7f72bca1cc769efd5e7ea7cca7145b1d3348d7b2e7232739d53dcffe8e131adcaf2b02a7b9fd3b6ff7a3610470d79fe3626097dadfbfbc5ac6d1bf8838522145828ca1d0af32d18d6ef40ed85a476694894bbaf5f38a51fe926408419a5fe483701ea479890ecbe1851ea6bd88c491e1af8e3db7bb983fb0a86de73600ff7f33db0870379dcbabdfd71afe571edd5c0b03ef71cf785ddf768f5ebd1401cf4b9e73a2f3127cfffb17d58bfeb3af77070dccbd7c058bc533fee8564ccf9f6a75e1d952596a4ca8d6a0a7f8ec3b1b5d5fc0f6aaddd119c529f9ac9f533ea251ae755ebe4fa19fd27737f6d27d7fa1caded33ce947d21a71fe30b494e662e52c1fd42de9c6c93b94969bd81a7e07e7a5da97ee5f879fe204b5fe2b00fd87fc83dabc151f6a4dc33326cb22bf78c0cd28ea12250392cca2293e50ec19318e4fe887dc05288dcb33349e4b8947b6683267900b9676688c89d6343c229c72e90fb736c5039e49890540e879e38d342410e87a8a0807b2287432e3c8102084d049143166810446e99acefe47e2fc2c562f62794f2e7e766522065cac53b6186a64c2eeae141b9640f83b8881e0acfa328880b33d983f9512e39fa1772fec3a9047b7270b327cae421cf547e96dfe7212e27899c0a74f95d18f8173d7e0efbdc6f1ff7da1772ffc39ff3ae7bef5ec8398b53f2ef97fa26935f9956cd634baad59eb1ce94bf90e4b8dbd5bf3fede4d861ef545f4405232a82a9ee07e49563310719f2ece014d5735ee73b4e7ecd6ea9c8059aa89ebb57d571ca94f766728ed4d4be3f477c4df374085d79bd1df7b5e7b8d4d7759bae2273b24b7d0d2773b44b7dddfb27b3ea336a166c0ee299034062ee9eb31ea682bb6f0f4fc1dd0bb999dc3dd754c6383d462b23d5fd2b73dcf6de81de4b99b7efbc97db0fd52cf3f6dd0744fe50e56181ec2fc36842d3d86efc8edaaa82609e426251e7db0e9ee7795ef7dc7b9ee779df3f5e14c7711cb7bdf61cc771dcf70f1747d3344dbb6f5fd3344dfbfed1da586badfd1818066badc5514dbbbbb73ba51f03c380e7cbd7dac60dffc64ab0fdcba9aa94554a696bb55fbfae81ec5fdab3a6a84c7f766da3be7defebe729e0fa87ebaf6fc82d77f509b24a7e37d0d73590ea63e027dd738f9fe0edf1135c5fa57ad57bf153397152b5a37eb5ef1fef4bfbf7860c1273bd7fc1a72faf94d25e29e58df58b11fd75ebd7af7f74e99f757ff9f55b79ef0af90259d2b82fe4f7fe0ff956ca0732d4e558094335c74a902b380a17c8379a504443e2a02fc1fb42f2ad9c608d560bd7beea54cf450f4733451fdc7d2e4ee9fd44315548f49cb51e8b319923187241705661ca86e4d03f66a386dcc941be50fb6adfbf2e748f5d23224332902035c70f62330b451ef6d7dfc0588cca43deb3232e7dfd3a8c5f5f7e413ccfa6e15f3dacdf34fcdbc3b18098fc8ed6bfd0dd9fa83fb25094fb633226f3fc217f66f9e08f7e07fb46c9c0151c66823dd5b3262c399cd963c48f01cca46b1bf13d776d03485cf2a4d7a5546d034894e5580939caa21c2ecefe961f16c5971fbba5fcdafbbbef1be8b5e37efdb1e5aeee8f1e0eede397b51576d7f6da7361d1d7da3e2e8c4162deb6e951ca544ce61e1c326baf81df40fd31df6f037fdcd73ec6ee7d0dec16c601c7dcde6b110c89f2941e2e20bef6423246e4afc91c935962242cdf81728d1fc15804215b319c3f9e01ffb0ff410e2d188fdcd5f948036d03c82b4fe92fea283e7e374841a3234030fa495ac0e24f56a8427e41d1c3883e141c3614793e513f96338a601d07c90528708192934c06a301d3bd03eedafe7b5efbf1efb5d6da0ed3dfb66b353c7b4a29e594d25e6b2b7e0b658eb4d6ae5c9a77f450b0ef6ee7bdd6ca56a8f6de5baf55ad7a544dabb5d6d9de8edb514a3ae79c744e5b2b8e1965958231a529b84b6bff2ea5ad18f1ada5757658d3a6142bfbc6c698526b69677d8831cee8d19f1f0b314f5f0db9bb0f799e9c4750d28974a27774ceea4372cae8d2630da491d5b4a9b1522b1fb94a697fdbacb576ca793bcea96945d7da2d524a39a7af4898d35a4ab959ef5449a5a473ce49e79c935249252533a7e7a8eaac2b21776177c9fbb27e8cb5fa7c29174aee191da61cb66674aecce818e59ed16193c3d68cce186b6bb5b5520aa74b0c5a4cc1f24e2ec24109111e88443162430fb0fe7601d51c0c32fdf035036a2af50cda82aee1577c067dea59885879a052a99ec35f70e84aee4aeeee9abb928cafef4a3ec6cb4ca5a934af4c250d88ae8127dd92a97fc9f45b03ede7323e3cb3cc312eca843ba5fb44eea2df420ab2c64c9233a203e14aaee44a1e7a9a31d9cbb8994cff850cef1c8b12a683b7e9643af1a070264d28f1e8858c175f8f8c99d4428c3d679babf83f1e047e7c1aae14e37f53c5f3c37379e2f33cf8c9768510ff7e3ff155f1678c710bee7c5fabf72acd2b9b9292d20c8a00e8a0470f77517777195734a52903bc718ebc944a864a46ca63eb3d8b07cd25a6d9034e1497524d29eea2df49d80de6be8307c0cb0fa792d254f2a0003cfd79c56bfc3cfde965367910009efe2c33b378900726d39f63325d9a4c019873ce39e7c492e9cfef47ceebf3a12bb9f839e70b0de44983c3e944c2f8e94137003fac1633e9c594329366925f712cac163254f6c69904821e7c61cc1e7cf55ca90a8eff1fba12ed516a1b718c07f53f8d9f420c9c63b4720cd6f711d1ea4af782217eb0850e3e7425007cd873fc05585cc9af38160fbece00f83af858eea2eff34d517dcf0724de87f1855d0e5d49c9e6b0cb0cc5ec0234c15df4595f4f8b145880e15d81b88c0a9c502c21b168a32e189066728eb01c9c83591ca4191c363338b2d91b36f8cb667fdbbe7f36f04794d5383988ca9d0338b9e9e4ae222a77a3c0b5fff4a8298b4f665e394a24b27f3823c7a8452789c44bd9e4e4b235a7ba528199f8b4aa82db5d617ceae2537c8a4fdea432424537d93fb2894ff129b289509dbb2c787b4e19180ebf99bda1436ca10a3ddab38ec3be7cee5e8cbb59981ff33630088c9f3fbde9fdf633d5f574decfe6f017f6c89cf691e7535fd823a7789e83f11b8c0f6fde607c88f3f6303e6ce50dc6879f37181fbaf2c6d3ba776e3f3f9937cf9b7b2fe704a78dfbdcbb83e1fd548fcc7de12b6f77bcaf1b08bf30fe98e599dfb2cfc28ffb3cffe362fbe1863730667f7bfcf7fd6edf0563139ca2a4e77bbeeb7a5a7cf722c6b362bc8b37c2e28db888a1e281c1f3b805863ca03fc6f8398cefd66d8ffd77dc7714ee0ebbb9ea2317ce1ff5bd9e2338f5f14e0ebddfbe7a37e67773f43614bc77304c811ef88259e15ec87b6e9e99f32ff2edfd37f08420316ba0bdb32a96b2994cbf6b2d654a2315426d651105d73f3a547072a54c8c4a6a1b4c6dc38f94c6c83913a9857040668eee02d35050b0443332295e9c8d932b6564541250516e8e72cfdaccc938f7ac8df61cf7d80a1c3654d6fa5718f2a0f9dab7a0bdd0eb188184d693a6690de64b7dd35df4ebecdbec3db77da9afc77ee7ab7d8794b5c7aced359fcf1a18d2c8da574d84ac3d77836a724e39e3d19c737a37e2ac72ca94fc19fd5deef239539dea9eb5ceecb099453df1c5975fc729e7b78c71ced9b90b0a4a0621f78c8a2417b33c4629639431ce1438a59bd3d331e774f1a18b3cbd3c7f3eed1a3d24cb468b9ff33bd3a0c1e263f1c316f204637d274fd9dee5413fe4f3f0bc6001fe60289cf1fbb71703593c8b6fcf847082b1f821cfb3e801c6e2cb67f1f3795e48ce8ee759bc90f7f016e88282214f0ebb89c573f16301863f5986cf00906b198b1fce0f22f314eebb48f7dc47822e163f7401ceaeebbaf92c9e078cbd00a7701f5d4ce9be45f754dbc02430601ca884e3388ee3388ee3b8dfde8806e237b2812db982acd1812f0d0a0a4afe2bca550e3555521fbe6e7823b8abeb5fef86fc178efa121cc15d5d3ff5f369048a60b603877c86bfea03425780f2c97e676eb0ccdc28cda6588a52882da232060d3dfede637bbb81eef7be6bafd5d7c0ee19a15f5bfd3a9d4ea283580a3c3ffe2cf3028cc68f306bce5f29c1a12acf67b5a49452d536e45b6aed4a86c3cfd6fedf1f4b29a5b4bedc48fc7805193ac15e82a54b77e950cce904b326a5f4887a6fac92b47c7e2afdfa1d6e1ba38df5ccad33d6faf62df8c33e48efbdf7de7befd534ed955b867fe1a80fc54670f89952faf520c1b1463753284d6134044ea6917bc6c64bee5f1935c11e70dd6c261038822bd0be2a42e99b80f35f21c1611fcb5d51c9bdf776592233a285594bb89cc0082e2d5680e483142c38e440c30ccec4a0844909196d75023545d4604e106e8810e30606253528594196950c1a9224552829924545c37c92044c9226b218d165490e4b94f4e0eefdf73a0d1148f8f139a5bb7fc7f2995afca75c85c1524ab98649110d513718e1b2030c51c4007332821517356f7290a4c9d21259908ce982c409a699952e4766e88244a9cb912e2b23565cdc294c1d16188961064b6693559695115028f0648311a4264eca3c994c646801c5ddbdbb1c39caf13b1f5eaf225eaf564b4babf542ee184431c39d2844983055693105a7f251f6f142112ea0a20667a0f8c022820839898851477371448bc2e94893135ebaf7a658b02a629483d10f2a3b1a9b0d8aa6699ac61559c2e8c813f07a9aa6bd89d3c588c8bdf75efa42d1102d46be7819a28d134f2e3c694ebc507365b0bad285c89c2e449cac5a8079f04102c90f75ec849101ca921404a1699aa6d1a30dc09901942e759e5c419a4aa0d8508f6845927429c264e5838bdc5d8c88b9c29b6551723fde7b6f172343e42bfbc13fe0ce737585d5c5880e5d8cc89043fc480e420c4e94194266055c98920cd16aad945aadd76b05c4eb3591e47e82624c92284d34412209587fbb585991d3ed54a72b2b5be4a983fde35be944c83248ba8c47e54e904c60e8e32eb9a55a5ea293dcba6f8aeab56f9b55fb59c150037bb4a7d28b06deed3daf294bd964e649c2911dd52045298b97314e733cc8c8833ccbee8f3cccca9ccf03ccdc6f31fb775d88c872f79ac74332b9ab0353180a07d221841c0fe761022f1ed4bdfc0e0c3d73a0640ab72e43a072eda277822a77036d31a9440349ef82b128bdb84b76a43425c77868823cf304655303c94f83e592646a27b9655c251e90563ce027fcf433af094d9e58a3242a8a2f47303f42848d92ca6869bc3d797605d42a05ab286d70d85aa5004a96568c150a301538d492564941ddc909d5b282e39a563be02f2b1d563ae02ff88b4c66b96564966be6aab94eb74c138de69a71979751010ea35399ec40e6aa0aee48a797928959edb0d2617685d32a29faac77eb5298858cf1c3517057074a1d24393eee628c2e5f5ca1dd9c73d2ca7384b5f66a2f6eb06d1bd7cdeac849759ee77929551d8231c62b162c90b0ba162d5ab460b948d9c0d3bd78f1e2054f6b4e89d1c1800103468c9e550f1d743ede8d187dde078697a3032f16efc8591d29990503b7b4dcb3a82c5d37e33a6fe126eaced31d991dd51a0def208225cc442d35f981c80f445ef065d63096cc2133fc30eb9e7477e87491199945b90a907b668598fc22f7cc8a2c5366a63447c7cc9237406001924c4a1053c8a9a27180832d3844a1810b9426da2972537e3d1687672a6331d3a75fcf0ecff13d63ead293206b498411c02c8d204297114104dc3524133220b384cc0a1a2460feadae5104a5e98c17188468c1106260fedf358a4e90039d2bc61c21b2821c60feaeaef180254954393ad3c41056c0a89072be9c9fe7234e2b90bc08ae7f26f5cc79b577fbc9fded92cf7d3edd6a56bbe4df0fb74bb64b3efd66bbe4c7af3bcaf76965568c1394524a39a31395ef7cc1c6188514b0c218e316d13bf71bb8491081764ceae2900692527a37bc2d0f6920ffa9e50634903fcd5d83002dd49ad10168036cdfff41dbd0561863ac43470fab8550ad3a14a0a345cfede9f124328805ae543a549fc323ca2c7e8411dce5ad0f4778bd60c578186da383974fdb86cfcbafd1003d24ccdc828b8f60d440fe3e5f34f2c5233104ff18fe30be78e42eff1eee72f1804d2fc0b0e59405da19582c2a70189b9aca38758d125cb043d30f5600b14508987f7cea1a0790bd59a28a951bb09081f90b29c0835abc906cf1a59e7e2c5ed808dcb97ef85345595353530f77b9c13149fa709787dd24038e322fee5a72d72b36c518b32889e20a1350c86ec041470191458cac07264d58dacd0c55846842254d1b1cda4b152564513d242133529f7e9e24d3eeeb6e17058263e61851aebf63e60a4ea15996428f8f4d714c4c139b628c35f692d0912ae4d8007582a82d718a6280410643d4c89a92947c3489c326871d9c886205029a907943c58626de6c913cf85630c4b1e96b4dd66898131ab2fff675aeb9fef6c50ea1bef6c906aa7fbfd940f5a34cd6a03da3fad0e022d7f77ccce9eeef9fa397b8c4220e0ebbc947063ca8a99d72f8ca22bf469193364d7c5801530bdaa081f9c7245da39b82083954d84047ca9bae5184c31338d8723445cb1548c0fc630d5d430339446122064e7a500206307ff9c5247f21241651e9c19d78255111cc82f6fd9360e836be8d34face9cfcd41da5a0d3696236784e0a3ac9a4cf28b8a61486d45ab9e7ed66cf981e00e5393f1a20ae78705157a63de40cfa1de86a7265da434e3a2918bac891863f3a7aca415603c5c7dd75bbda155979e2eee298d9c118d18ee9c347435918b8fe98bc13bb387dc6741ea494b24e2f7876141101491e56413a45fc29856be794a864545136680f70ed3e964e0db32aa59dbbacadd5d6792ba5b789b852a1a547265aadb952001a89eda7c0aa3566dbe16e3929955b744c6bf17d3359fb7a1683a7ac7d3deb1275d960fb73ba6b4e3c2bc61669ce39275e99e0ae3ca7f476d8bfb77a73564a29ad94525aeba495561de69cd3f65c65805e4935ad3690043d7635ce4a29a595524da3b4da3a69a553cd5c4dc05d1ba771dbd43a7c69adb74bad7275811c9d65fd7b6bad36e26b6b85c2be9de35a9b51a8e4ec376535ea18b2190000001000e315002020100a0644229150142541bcca3e14000c7c8c3e664c321147638118c7611483400c832184106210300618a32082c66c00fe42c7cc9bb80e75a3df5054df62271c071722d5263ec553e45da32f15fca1df00427ccdee556e9100958a6a2e58cf84e137f009d2bee5d99409f6eb959d824194486507e3d9831b95999b2f9c48847873d683023b292811404ae9f091a962595d988eac2d94265751a55aa272dac393eb148a2a7667e0962019d3691b3c38280def34540e676cc8b5be526731735345bfda653608d47fe764b5298e9bdfa13a370d31c074a4c83e3196e5d7def72eefe67c86f2f8fbf112af620e8df212ff18f8568b8321bb95541e2a3878868fb0ca6534be5a0a466e5367770f5f126d39573bf6555dc7f9aef7f852951113c56c856387bf75167f0b1a9c07b98f439c22d2e7306b298be7aea7f4c18bd4d302d44a974c30b44970c7c247bd6b96c639cb9491e19c9fac4ebe0759732ac125bbbe9c20fa94e1b6e1d49d1f702a84a32e02708c4b70c88d4a2926461256dbe5989a0843d418c69876e2968ceb2b52d19747e4dfbdda9c33092963c36c52b50dfe30397669cc068b621ab6bc6140710dd8c04ea8a11e1bef024ce138ef48df3f38ea612f93600935e8eac2f82a82136a203eb60c608445aa1a76daea43b525f7c0a86c4002f91276ff3e2a950d41af82e4ce94528be9b9cea73ee9a545b4540727011dfe9fa730c2db1c21209aeeeef9d6635eada8d41d1ba37131b99243ee6b8a49f1c934364d43b8163cb2af36b6fa1d6609d0048c77a80e6c82be944cc7b8aaa5ff526dd49413cbf1ab2de6ba44070a967d4505cb55450973f87709dcf9c458840dbeb0040ee4d5cef4685b2071c9090ac44bdb42d1baa720bb5ad3a7ba69cfb94856730b58ee02187e29930cc345cd934d689ed343d85f8fa8ee782f8c89805b51580282f4ad8e741429e6952cc13287efa115c2d440099a7e099d08133762089a8e0228e352bee48d2f05341b23c8c4782e050b48a673a2d447b789fb72461bea7a29bc52f94158d4279fa6012abc36677e11e18ba54f1a21fef35260da4f6b9385601476e883ea1c5a91b8f4d05e6fdd60f6901c9d6e663e83e09dbb0600da54a9cf8665943bc9d77e5d5124c977622f0a782a0b0637b546c45ec031b1e829da83b0fee5a7f119a722b1b8786e6f72def286d9e1358c3f5b7a4efbd685ead230a0399862db42ebc9ea273fe9d942d6c14a3f2d478f8ea6bc013cb9a96045b4b520fe000a856c7fad4f0d9e541cca4b723f95066224b7fc954bb1d1d971d5270057b58b05dfdc03e5bc1583eb2c90472595d4920a0d6dde02f34dbf8029d57ee60ab61a3a289c9a34a4f523a80063a000ab21464d1313df19cfa5bc610dfc1a05e34251c413508820e8b9d769d2eb82e61b3d57b509b90458e3459c3a37ce6b113d2593b7e484899e74a2458813155a49cc2f897003284edd24e5ee2dc7b895a029f0f9d5ad544e5e169c4596d7274ed7a9dbfa6c07a25c62d49b62e93b5c46b197f1eb7d40e18512f8074871b25e1ec0e47808880303d1421865c8d5a5a8884dea5c1eecbfbc1ff0417c2d0844fdf09742db2f683d311b8f95bba22960601fb8c58335710c007d4e5244b9deff159683a712812d8c18fd99dd5214ed708d537e44665bdd03350bbd9a9a81b5508f219912fac91d7f6ac40cec8a680bfc11a1c78f3c2d8b256c3a560cd998d33a5aa9987dbc0385da7d655690a1ee55cdf7640af016d4ef227699819ac65b7a57b6c80fc8674630ec09f6959702b42bbeab2d610956eebacceb31876257adfea469b259017396ce7036047004fd18d4a4301858c6ba9f72f5dd5b4cdcbbba6600882e8222439d0326cdad81730b317dbfd9108ec8656326bf105a0f93c06a22ba81380bd3766f186e33855938c65116a44c544d02f04c1e37d82216691ba725dafb51ba3075491dd6dc8702f8539b7adb2ba63f4a99a260daa34c0efa11f40138f5d285b655a68bbe8bde38dbe455dc08581e2ba2c30c4ce67595e93c472bd78d35026338efe562cc32a13ace85ab85f8b64d208e4d75666a580199eba62fb015a0d5e237dad19dfc5c6cb45b3abba5da2b851de9ec93da9ed4ed4bb58f34fba4c2f69ef4f648fbcdf415c7d9fc0a447fde62caeeffe952216b1e31517a4cc97b3dfa2e0f37342328d7a6ec486d9fd476a5b5438a7dd2d92dcdeed2ed4ac79e7476498fec7d69ed92a6ccf62b3cfa759eda38e486ddd009f17cdee7e57d7eeff2f0869ff7787bc7bbf7bcdec3e73d5ede477cdfbbbcbc29f0fdcde319ae1a5e1fec0e2b4122d33f47c5070e1d81485c3b303029e0663ba976a9be2b08ea41f257a0a4ab190a991351dcd557e1ce6f9ba6b39e208458ebbf7a7ad51e3ffbc32fc278baa6640327d931ac53821edd5d5a049d5692a8f7b0c3fc9ef7bb418451a132758bddee137079e8f35471fb5ef707c6ecb7017a54c2c61576b4318e3b0607373431b822585ad68efa743b42cef53a5584c63239acc31dba831ddc61ac435df46cc655f2bac29101ddf01df99f098f0db87bb5cf05bbdcd43c042d7bffb73434cddea799a57a7b1e9f0e1c555a663d213c1ed4dc177f7969a7ea00ca1088d48127e6ffb29fd5107f202012131fbceb55e71f486d7db5419abdb1efebe6a8ea68f510a8071da858cd1a717818f4d917e77ae98e6a1808f861e566b691c2b1c14fdef1f76b738cea1c0cd0e21ffcce912e2df05d1cb0c6cc0fe5d59e3283e5c0a84b62777b52e522b87bccb712f01f01cc4da1e8368735f37359061f16b5bdbaf90bcbe2114cd3d0ce7346dae4cddc67d7388ee4d1ee3855a50fbd1ccc456caab40371566c5fe739d55574f21b084d29d0e03f7235e9fd2887cf480337c8feff53ea8893bc27d4a01f7613a1d2d2e9f722a54fe8a109b9014c9b37686356dcab68c63fbac15e75ba550db5b277068fc588087251a4ccf58e2bdaabf1791c74ae62ad1aae166a2512896652e2a3729670b1989ff480eaaac7e60970b3f6f2c77faaf589260c6980832e4676d09bd9091241ce177d0e429270f1af95f0282d714b09cf30697e8f5e80b98fdd7d63dd96cef641dcd7ed1ab31899e5ea21f6b9ddd2a898eba0b930dcfac73798eb43da821c8c7369d3e54dbd35332255b4e723170b210428991cacfd8918a0b8f38afe9e071ecab167ff9528e194f71454b4c885db09e9a684604e0e72ff211e18424c92685a51eb4cecdcbd7d26892630db9020c3361627e0895cd3ccc6bf2c4780fa0d23c112adf3e24f6c1ddc280d866447d37bc2b8961e3a6b1691a96df9b75be8dddad6728623d367d862c9ea1da99b454d686b4b81ea136555969c51df1a509159e49e41ab230c0888a164523a69114cd8a4c70e779b0afdcab42a62604783079308e7e5f36b7be1a8763f1007de1567ffbac3f7be843054fa42a47b4d811be960c67933fca09df07d312ef852cdcf2fe75d2b715e9eae845dfddb5f74ef73b7d7888292ea159fd307b3ca02c10d104ae93828588cd662b01e4a1041289b39c1129b1c7b7fe91cbb34d5737374818d73e212a16b779295a2386c1d9a98ce4bd72c7b70732684e30925d8b91596ac311c113821af82b226b492595360a531faa72fc69250083913105a6062ca633a0bc13cf4c3a9031b7da57dc04934422dda53a9e1b962d48a7b0773b363bb09288b439782da459bff20cbfe1358b3d8e06134a3df62d7533e01f8329c1e1975bc12b56029e4e0750f069016489ed6c42745dd3cb55eed43ad55581894a1439c04d66653e020cac42569e8bf49eec603ebc7d91b1a73b807433cc02a36ebe6ba187616a5551ecbc12df8bc386d57101c846e63a733c8101af74b6056697ea8285381e18eaa4b34033b664e95239fd14265b973c048aee7414230773070ea3a45dde1b3fb6f4e9c37efe47c35278ecc4004f67b0b1ac27aae6e48c5c9b60444038215b804484845689d29c00e7e9b10de49c5778652a2d731e16d978e725e98f5ab2c1373a1a378ac56e0627d0950e7a13598e32c5dbbec37e7a10148740b112e2804686f7874ac441c1deac3e9d7749d1fc855da0c378bace8a160a95477a3ff8cf5e0df608e2a77a4e7c9238528bb393091ce8d06be87ba3e9836fec869525c08a7725a0dec17e6aaad8c6260635dadff3f8c62b01f404d70d0107368ae174a593320a1e8bc3428f38b20eb6db1cd543448c124253a76b19276f43e1977533c3bffe11bbf573a0bc0642b9c995c7846e4c3b55aca6f6d74ecdbc893fb65a95d69527a327372e2405770357282aec21409d9dfb2535fa8f9893a8a085aba08db369c00bddaea73668fae5e73af95af6fd36fa7520f443860857ed01fbca1c7f8f22aa5300681bb1618b6164fb33fc93b48b534c422d3195cf029f6fa6ece16eaa2789019504a3de80b94c47269281e6e439300ba0ee8c4a221c830dd89083948ec3cff7e90f762d9f7f5049111147795c1c59e793534d76eb69e30eea358fc0675e12c962077fac0dc9515822e77415315de62cf821422b979fad1a096ea46f5ef9caadff70b8fc2633bf2d66aa61f7db5aa60e5a3472504cb45c91cb6ed32084a744492abe049aba5fc179d8e7df64f5588ed61fd8e4be195f8803c11acd4ec36c2a044fb1abb11b6e1443827a487ce1a61eef17f912f2af812c81f90da8c6ea0e3a092a1802ce48e3d3a644b00dd417ebb81bbb0ccc7969e149c5b2a03c57b163e8773057113a7a8449dae06dce48af05a51158a19059c3d93697318f876b026372830442aca7f05820588298d48a0ba61b29174babe6ffffeea1eca18e7ec4c021c03bcad0a474139b7f3d48182a01ff98611a38e5f209f32a35e70514cc7e7c2fd2f2f8226eeae27e475b27cada2e82bd668419a0a98316c8e5c6b2b0018b21f9a84caba46d5c6fb6c4fed0b371356d5c9237912c46ac6734b978740fcf8547b9b73a4c80be2e18b88d828579dd3f88a166b647614221493d9555a2f382e289b9055321010396e73203dde758395ac6287c110cbc64a43c062547bc9d2b01865fd242ee4821a044561d9527c801d296374aa5bcdc6cf2f525e768dd15b726f7218ad7a61264abf9311597cfb5c98e525289f601577c9bedcb8997ec5f7bee01748bfa8eeed26ebb682ace5bd09c447227a872e9c0fa6832c02b5a7d6009ed8b6738a1599a88e5adcff86ce0ce18ee9543a423ca8bc7a0614fbc5a048e29695301f9230ab177db2332cac64b05492d2ec15a9a000c16e9c188f574659f2907c0086347b9d222d70e12c3881c298e0e37219f4e28086372c176e5df78c740461807093271f38f3135c3a182cb454aae1cfd8d3610604df55b0a7ab3024a3d7ec27f071175ddf14c3cf73d318efd9c686e780bece93cd7423c78d7888e9fdee2417c6208db48c847c45b979a3316bf1691c4d8c2c4e65d83bf1241fe2125a03cc515b1448c6cb9d0c778131b28474c76082fcd921d6218451814c519864a29bae347cd52251c6497b93b5350e7a6a78a1a8cf6ebdb15827605dfc098d465c5075f1626ee76e1996b2703e21e857899f4f8ff0944975e4850886dc7a58119123f3a1cfdac665f074a4e31a4268086357ee3afc76e1a443ea6320963d4575f68b6d8631ed44e88fa3e0b23aae2f7d564bc76d12f1bf18e3e0c5688d449381501e5b208be7d0653a1e6420daf9539833e9bb7eafbbe5c1cd1a3d567044f6a8588c3e5c25c6ba46c2fef19e77a4fd35a93878cee6e66925bc3ea33cfe3a4210305418b35440aa09f767cd521174eb723f891763da047ea9fbb3d2d2ad8c3a5b12cb5f52b3257f09657f7335450f44d6a73f4282ab4f1f3938fc6720fb7133bb5d22783196176ee494217a5c59dcc3902d2e3353212ba33708fbd40e93039d2e8e6027e92cb4a4560e681a8e81b67a447aeaa7d0fa8c54feb5f7661142ad8ea9e219a7ce0c5298512f0939d0504043e39e363a0d5c1a4ef6af8ac4c8752fc5cfa6f3b57c9bc49793c6abe1f81ff0fdfe3d7abdee78060918dae0a6a53e698ec8b4a8a18cd6b446c8debba1365d17d6f1a8091b3436f9414587e08b94ed1a5ec4385a63e48d63e8ad062f551ee2565642c7a5a0aeae5e7381c22c5d443d91cdcaba525c1e27c50f9d5bf7c66024e50bc2198246ee25c0725c545b2d531dfa22d68592ed1d6e1ed5d57ff704197a6a7694e0d2b8b86726c09348681a6a85cf3cf3018e06e79bf78d27a08ad9f0a0a570550ec16e784bdb51e0c808296384741d83659cd071690124514f4c6a56c1565f683a9663d46963d1c5433a32e3584507fdc625f9b9736c96ad87652b68bddabe45547ee245a66ddbe27fd74c6ae5e7cb95989220402e835ead8efe755754c49ad10536d67933486238f800c6c500b0d688ff76468695dd6a1556c7d0145a07d327ab10096cf94327b64bffbc49b1a775c09d0bd74a65c5be42c2400685b8f9056277a33d02e0774a3bf06632a5a0f34c55a13b9dff1876ed7d23c5fbc028af4863eae503ce64b9d98c5a6b7d013d93f1b18592200b62e4b61b342f2a3f81c374032768b694b15f4d0b4d046e54c624f14702b400c1f4bbac5e2a4e8669dae885ca3b475e8e36519dd2d09fd496532f1b9a717eea5bda47c28657e0e7a392a15408baa5aa1a5b698179bd557252741d602799833a6699b964c44bc1c584d78753142836408d08ae67b632bb67ae282c1056334ff12ccad41cd8d489d3f48446c543e552e46766c1661f2b654609a59b936628b981b00f602df1753be5f0e4c3424d476494509333fe7f510ebf7095f9a19d58ef62fe244482dff95b41455e450f08af170f245c5a6e3450f7c8dba958de12ad5b4758e288d3d828d381dcc3529d500e3255562370cc86e07f99064ac94c66b4596de88e9f73f392a3c49e300213d93b33996cbdeace17bf7c6197f3101f1f6aae7dcd0fff0974b96fe020e085966d75d14bae1499e185a4f51090e390bdaabf6fc2940269dd74e7e25a4a093dc44b0cb28374662133b23c4bf87a5b28829291be6e37d2550b94084c9479223deb190edf97ef97b909fd2eb0f95c756254979e00f97331198ecd9df665d6d60a75a2109bc8a7efcc461f9fcd1e0b4578aac7d6be6769d81b6e28363d23f0fa923006ec2ea9bda8979217f5fff41b6cae0e34b98af46a0abca0133b98a5d494c4da14c6138ffccffc1e5f6ba3ab34705118a98d56f4217cda078f26899e206d1eddcd75e8533c2c127b7afa1d6e461c1c491b8ed35d15c99b4dd954208123770521d66bdb1ecfb42d0327b150663881cd5c3b9478d2dc441f093ef4f53d34127d509c1daa416b15fb3c7d88725d3bd5994556de2e977afe7c989590ccf40cea5ad98579ebfacafb58e6fe304eedb90282047546960ba44ad755854c91326f30ee28782f783218942aabd8d68883be1280fd0aa5845fa0028b89c0467d86255b5438422a9d6643db43854ddf63a7784c3e63e7efdcd7fe63587a8fc9a6ccdcdf86fe6d5b612561ab2c8c279228457f06e05b771bc3c9aa251c1209c07504c500df567082d05fcaddff65080e1445ca4911156e7a1f38d8c510d355813203c596ac7ec80c20e6cecb4d371b7261db1068d4058cd733e8e5830f6d263e393177529a6deb3030ab5f174842fa4a24d8b29d515f76f76a8c356db9ec73533dd8e716f6ded3e43d25b70d1bd36ab1495fa2a169a4a4f85857ab88f844808e3054b5c3cdb2e4761564703c126184f78fc1ef9bd2a07da5358c31634d8b0472f226ff9d40c9204655ba310a47650414ec78012d653e2233aa7d82104107971811940fc70d50dff601296da58188d197f3acf5f9b6aa2017bb4155077a515b3b07b146d76178ff0a2158a9f9f46b3aa542e03fe4a14811a54f64f5946fd490227a59f693710ab0c0ab033ed8ddc442d3750c980ddd6500dc05e93de89c85c28fce54c0e6610b64ccddf229fa53536765c8103201196d89ae494348668464cbabe83993e6bbba1254e33d6c071c7220444e0745c13a28aa86bd7787da41590df606aa5a2781e07243047a4742b967a28cb73f0fb782718d3558acf7b91fb5b81f1531926a50e0c9b4f8ab2aaad11c5a5b1d56711b843c9f58cff8b5196fae446f55e430da1c78e027bb5907dc3c5be4309884985d192be3ca6c8cfba63089f5f24071f643c3f5c51530a043693f2a749af0a7abef2a23571374583411bbd54e94d1869682bd1e51891220cdee9ecaf40f6dd4dccd781b02a40ab041323707b594b0f7ae8a794ce0e48d8acd63826ce34b0e16025a314ccf6be072007a294943b5ef281a0b9c7e3bf71565c0a5ecbdbbc05323cce48e5789e56795fb41213cecf9165ce0eab871f618b46e3c87f9ebc3df7c049cdb18a89f744bae2835eb3ba4a381a9fd1ebe48586baf5a6082385afa130944dda0949edb84297abca59884e9ed260ca038002c7d464aafd60633f44641218387fa7d30a0d09e780d70a42af028b0fe18e2be708c1a2c998a42a54625d2f01f7853f04ae9286193f86a4091fe687e0942e2064869d58e9c83bf7692537341331bab922899eed6d60cc1c0bb30523b4c57fdf21f87f7058d645f55082ff74836d17a8a3a6eedc1bac50e61856e76c792c46972602edc3b1cc7b6db6d4809c068788b657b18f2e6f20abc43dfd33a8911b8683d3436bd35395c5e82761b5bf41cc1111c71b49e8b28c4d1c10d765e522a088c2ab539d7f480a9d92ca4f5e8ba434928bc8cb28b7d74d9a76c9c6681dafec746b22e28f364ab55cdaae040c6024ea95155d75d19af42635280b41e3ddd7c529e4c804778a1cb68a90c8152b832714e09805c320c3129a4f55c57bc4b27a873c0cf1ef3ca79055455ac3177a768cf2280b49ebedab3eb320c2fdc683de501541db39b55a9bb8f8ad6831ad899a2daf11914c74b1ed8ebbcbb63283ae218746a6d5321dfe4e9ae0f4035b9ea711b7dfd53a7533d8a9d0fb4f206b271cab01e3a8da7f6350a1334eeab066ac635cde4238f1390296dadbb265a4f4701b8c86e22f9401f28eeb6d12a3abbc884c01c9cb57465f78212ad07dd2a2a58f4e9cc9b0066859486d5f94011d6df15fa37f14eb41e40ca5d8207af814512893ae0895bf1d73cb5c79914bbfdd1e1ab198854cd20b5e73b93a0e24747c215459887bf047f927001f4b91950389a8d1e14637d1725953ead87535509e09100b41e5a248c4b140a287fee81c2dc9f41ac82ce59ef9effee294ba5e3cdc9f2f5b067c31b9070605ed483192cfad4815ec9f0f67d17af59600296b8c453ddaec40b3178615f15e1f16d468ab79b37c89ac538aac90956e8a7b4d6583a5884cf653f4af5c11d35cd9376966cac448e6cb5ff05c7bd156efd1c7a2da1b3b8ce8a8cbf47f46e1c4ba87c44222abc589e59fd9af296035484a0a216f5bc0061bc2898f7d07a9c4e5728441f2c4692f08674015d11aadc961b9cccc5854d20639c0fd9a124714531bc823b892d893241ea528f104556d50d153d635f12777e683d65a38bf5e1d600854825832821ae31ae55dbeb5bd8229e648a3eaee8ee3ce45dcb12a4bad7c3f60d57ef8fdd94470785fe9001b590805d60a01fab02aa35f8160c56c13945a0bfd9f5f20247a1a55c525f938eb998b4f81204aa31402a6ba1bb2a004816682139d3ff019ca8ac2319340761ea85bbf5b8c67bd33b37fd035f18b27c31ecc9e7c8445971441a204b6170894b5a5ee2b6816c2988ceca6d47aafe8aa4cebc406328b208f1e3a9642d4acb4e2d002f9883c773f347e28ca722a071db42058a029d6e6a8524fbb2c48c8e0c7b2554a49f49f4945bfdc4406f38218ec89c5e694c5d9d6ceb41813f36d53e2472ac8399dcb99d8f164644c4c7210f8190a05a40c918454f7cd67c51450e6629aca39e5503a41685debf1c956a8f07134459a3a62ef060a6f4eda3646956359ec7cf41e7e2fc5321a9596fd7992e8fb72c5c051770fb554525d572e9f3d27f67c9103490a4ffc7409ae720e5d46ccafe6b34867506e3c0eda076827a6b39a86c21f64fddab21e1ce6271cc5327eb67954c6f617e2a3172fcde18ada22721e45eb6afbb1a95bdf67ae6710b5a5be53df6d6531e7bdc92943f523655d156d155903db7218342042b608c16e325538e5b4534e22f57c82c66f880bcd37c2c5c334981f41be128ef696838146fd3ada2a8d5b08687ff51d17ac61f21a3ff4d991613dbf945080b5211ff0752c4c7019926e15c51ecfd0a219b91880aee10abb221e1c25201866716b0d0c9b1b7b3f114a990922e03046feb762aa8ba43d39555272e586cce660d1b255e40af60fb32371ff52937944da7f3d808af74be128c55102218c6463de71351f600ab87b53714278691a0189d948a684c32f16ccbbf8cf310487fad422a4b621bf0a8e11590f842d85aaf4afaa419f606ee33f17ff6e21c1d9c963f2ad32604a2fa9369cbac897a17b6a615c864f851b2340e114940aff28a1b48df5bf18bc8c570ac6710874832916e928d271b1b90928f4c441a9a6928d34c0bc50ef1c735795e43e4d9ffc09b78010cac8455bc17e163a8fb215f67f93452604d9c12b87103298af65612a23d5a1b6ac52f8a5e084621f1291785952fb1d95843fb877ab0b3cb5f9d08c6cb9dd64ac52ffb46946f0312ad6f80ed94c45fe40395462becbf1f32189179086c6eb1d014da46adf2606a551bb57c0d870e0752c0a995b8e3164556fc6c3a688740a3494448fc76a50435a6c7ee5caffa58d158b94d9d033e26ead00f02ecc5e6be17c2170c6bef54a226d35998b6f809162f33ff655c6e3b366b548bec8765ca790a1c3a4aa4f81612c09948ba2b63ecffc7d4b17a4a865f5e2b63c8d70dc757c9b9ee3702381608958a81cc0e4e4cc32c9eee876d12e8a8aa5cddd752744b1cbd96d657a222891a14c401c254a66f6a72bc88028ed9acce3f369ec171a6ba4ecd2eae11529bce192136dc228b6172ccd829711a526d0943c0a38b93149f67ba7acca6b1012d3bb04149bcbee31e719664ecc83ec634823bfc237336e409e80945c9596b4d6074f3ee48cc6b6e3f61f7d47dc35fce658faa4c6eba62861481eaacf08ed68c05e18798c36f6e4aed3930b834749d4331ce18b799a447f123eeba90f175b0d6e5384b60e8a118ecfd6b550f14d02658634e9e83d578a9b7304528f7ea8c9bdfa32e33a73c2c6d79a560da078d453a8e1c5490a4920fe537cfd0b339fd5b979a86521ca5fb4f772c97437699c554b00e114b543fde856991be64176619d46092db10ee8378c94e10d906a9442c2b8d3082408189eeba71033d9720ac44fdda38f2d9a62c4b15476f538fc2acc06c8875fff3a1857ba44b8475d849c12348c6d220ee2a0523dcaaaf42ddcc316fb41cb683d751dfee28825e142e2398c3181f12da05e87f8652d5b3d8a3d85dad4baa5e1cff61056c0efabf1bba0128f9ca79821fd0ae8f6b9f056c929079a05538e39fb8195691d4b6891726103cfa4e81690bceb0046c3ad8cded88f428f3e352b765093b62b3641e6ebbcbf006cf39a6b4cf8decd17850520fec177c8d562f60f80e95c629d3838ca299a141144440ec0aeda47229efb5928cf3f0f9ea9a99c2f0d7a5e7bbc348309a644d00df31f28c9b0a69734313f51e9f59d39978a5befafa17626c7bcf1fde6a27f45f82b8d9a6d40d5ae58f7293d1d42fefd3aac0cf8f37584d920936f88400be852ba25271a1f9db78c2405601766fda5f150fa254fae680f0ab29101b4f892ec3a4b5a4ea339fdf63514693f51a0d83cd60021dff87c3d09406fd864c8732e04f03551257e2127b42190b873a9f2f1f490c96c65b8846933d4b24768078b20724fc01f62d5d888e29d09f3c7a0a1447184c9e57420eb05311c305230d36125a4bc93e1e07e98286a9e97ab74671bac2fe3a8a4709021a6e1d82207ae65f13e7543884bc0446bc1602dc7243aa2d5a20a6a86cd77493de89c0b0d36164b5615f792cdc59eda4b766de325d0986400d67c2d43373f7d36e90f14b461ccd2414f5a0d5ff1ff81bb64e7122dfe0ad7b39813d66c27609e0837f5385ddafd346a804630ce953365463ef07624a309b2704e6a461be22f047d81f512d32440a7bcfd1f27c013e992dcb2f457fb746de412fbd32ca8a35ba971dcc4afc5f57da60fac931e889615285efb6efaded541d92ae7721ce0b71c043d20696442b82bb4b31515d53839510b54c7878179fdb071de211792ed3cb44176c5269acb145c8f6bde8b439781e242312b876b8900c73b0f569055208653a61ba7bbdeeccb5fe932e8fa629a26a4bebfd8665b1f4a748d1d3c484c29f227d40ffd7f4a067eaca1a3a2fd18aaafa1990b921026ef253a498e6625fe5f83b7f3b4a7612a0168d580571ed3217bca1fb5046931006c8fd14c45fab4e87840a9aa74ea1aa3b521109a3c5dd7d0069fa3b52b94c344d2a8a2b7ceca1e8212758ead0409b66974de892227621f348a5588b1b1ec7e83dd23a92ab370183c544b93e35f3cb10501c228e6d2a402357b682c6dccdbe220577e5650a8ddb6788f444e2ab72e6e7e066fca7f67ae4c5099ba76ea83bb286dbb7aea0a95016a59ad0d22816fe009973635321ea6a8e0e5e1b5703d65ab00fe8ed5e9499d3a92cbf9a4c66c0d70cdf251fcfbea6aac1d073cc70fd0ce704fc46f7c2b6eab6d432051030fbbfb1f7bf9e6984188c0177d864ae01ebb28789ab59c3a6747f1567e03bdb37fa37f3af62b42df3cb4d8c56fb2540cf94fcd56e7e28b8be0c0929b3c42cf2e7ccfd8a2da178841e82a1e0417a0ac76f9f573a7eb91bf119f0216fb8ddb0479d1631893b1f09aaa5533c477a751e7abded7862eb04e03d4f16c63aedf2952e3494f24fbd40e49781c278e04228ce723319fde3d7165eeba45354700e122c42d83c0534492f1fa7f506a1fc4de0404aec3ad87c6ccc1f8c8afc7a83cef15691b11e55728a687b1bc45dedb4c3f2c219d158662d822d30372a60b9954a2ea66169906ddd4393cd7791692aee9694adec486589fb41bb0316ccb8005e7e9ff1e818219d2a25332eb60c18106fb125a2490d248d64a3f416d59c72b1f93f0a257dc3d4852fcc74bdef6df2919c6a09639e1d6252c7feac3c5b1a969b950db2075c2c5377b0231642dea1ae57853f0c8a0863164632f01a729a1bfd6b7158709324a20c8392451d1828e635e9605ccd5e1478def5c803be80a782c04a06704a9419f018c0368e24d55ba47671ca84249f5b7c299290174a2c121f38a9f8d6f600789d60d54a54a7d88f381de9c9189e80bb0f0e8476b886bfb71fee32c7c8c9baf339b62a8210eed4f4aad4d2febe6f6a4014de3bfc4a33facc4cb22c23160800b024dab628165e9121c34c43849f289e06ba04e9b668bb266984de9caec4fd2d21b018322298e331572dff52fc2530065e3cec618b592018c497a6553f6f4720012b47c6767a825d5d7acd9dae612c933af0b6710ad3a75e6d094d0d9a4a79a6d66da1df253a12bac6d2eb2a016147951c515f93bbb189b202c12aa4a03236e48fb36619fd32e9b626394e1b5b854dbebb0123f0de2a5e23db7232ca134dee5f098035c8bd22d8face40617c13a1831aa3fc051c8c9325b47a9615e9d90c9a668f7843bb9705df379638ce5ee1c0b4ef7493f605f5e0e75baa4449261af9e28199c34ae08dd9095a4c2050520d7ab548674f622dc289d9c401105047f93c834e3daf2a9f138baf8a742ebfeb9bd6b0a6351dd30a32491f640372d2e94778ffd0e6f371024673aadc89bfda8a4d4af7eae95dc9bbcb978a72add8848cc4ce69d12216a9d6a278d766553e26205f30bbc279bfeb6db47dac701e63950c405c3414ed7d4ea99252fd76019a9382d592a6f395ebf34b7aac1e3f8b0637b8f8fb6408f98391381d5c63a7cd7b62b4ebb4a60590165399763d3c18406c37118d542c4f43dac84d914a836c9cfd54252e9446fe7a45e07e7be5a6d282283832a3e28ac17d4bf40b91957080cd29625c9f97285e092e4b12c45b7e03cb16855a5295e3f53118e0420aa70c3491e9dd84c502165b5cb74575889ae41ecd5e22a7ff7f64035bef81c6fbef65af2b225ad26cfff023b44734b8b555d305bb52928095a96fbfb36e773b88aba60a12ad97a033a354cfa539ca5f72789527a61855ef6ae16f39b3e115af5a4f42cac1ad92ab1c607da835ae92d59f7cd2738bac072b7dc6c623643ec22193aed36ac3e31adff28f03b8b2055a000fd6d52037f381eb6b598a7a10271c72f139c0fac8843f5991de400f93a05bb0d754e2d765768a7b275fbcc28da46f9fa21df7b5afc5d1da2a09e40b6280ab2947fb3369d49b7c6067c5ed2779dfd1fe8fea40d10564cf7a2f4a4a18dd762ad98e68b0850ddc2c9ff1bec1b535486c22e4f3993883e43e2408f6c014fe7fd08d5812827016b273c969845a815464f2435c040f2fb152ca907f8c73db55e6de6f14690957e4bc72a944b44e76a78e130884bcb3d4713217d518fbf8ead894544af1bc60523926cbae4dc6e5aeaff860717a3012cd343f145331b271ce9f9f6aee6860a2c00f2f13dcbd4b6fb61d740ff74f3ad68ad0b9989c699015755c3031d2cd865aabe812da9cf60a30d914095d566e8beee9f4ced8ca2b4d2518368919a4ce22be30c31087ed19e2ac6044b3fdf96d06a9a275807141b7d70a2c62bb29d257f9ebdd49a8430abd9b637721fcd20f7bcbbc5e234268a99b54331f2dcc6977950b7d66c62c92cf9e55e2ce052930448262bb16c5b3dfda15a198d3b19aaf19721e489acfc960d2ce58c693ddc8965e66ef7b801c5bf8222fbf3a29d325be866c937ca0f32d642ecf3fa4fa98c06248ce98a8695a55f89c8ac9537461ef74d60f722f446cde45f7909628af70d47e8a6a0c7b5e9b088573fc8fa7264934f75ccfffa6fd27094d0c2992d06ccbb7962304ca4204517c209b9e0121565e68c2336e51b318cf69a9225597457196c4a554b2320a774b24760697459e2c24febb0fa28c37ab0dd3ac65cdd7323151a3c6465dc35d130085651d8378fb1bf9868079ce2ab6f20da621a0a2d43ec8fbc54a0613358ab6e4e73383e120444288677c90c7cf655ddcab2dc29e36783cc32b8131e1a08fdc53a6c7f8cb323da8f8d2601b516bc218c681f3d3f5d5a3d195611d9943e8b6ca605d8b43d6dc387aadfd9cc2822d774d2c866bf3ea673dbacdc0d9b9d1f8bf8ea1ce69a70f4bf28ba110a5ef4aa7401a159c24b74349da136183236ce5becd515c9cd55095102c6bbb6af3b69f7e639d7eec9de0871a5b6c1b08d2e17e9be5a0c19e57978990cdd91ac12044a0fb7e75d912a48f2a309d8dd3ece5ea83ad9ed2cc5c56999c8f4dfec34d91292de3825cc65269b97f1066dd2a53902612e532f98a9d11bbd50a1b1ed13946cab887f7c54dfa5e8adf2eee5005336a9321c42709c68667d2c6c284929bf2f0dae7c2341cc058e488575d1c0d87a455ecdf1e7631c4efa47970ae9ba8136b31fe729b6bc3ca8460b6ca23120d5e7a9e4ca00eff1a5c3d50d93eb7c3975c194955a347bb84b70413c5cf852ea598966ade81e5febf3518cca27d5c33aaa4d243c4a19ea4eb3c0d957ba7367c04a6772695fa3f693fe489a3a752f7dae978857e4c64a1ef3eee96b586663246106a93048a209c61a76ae7cbdef2379a85bc730ccbfbf6a739ec4b204a41c66cb3b96dffb04cb2f3f735dfdad900da63f28ca20ee18771272e50f3a4f808c223a793b1d77a5ae2daa6485d7f0072af67327f42ae8044454bf70a4f3156e15eae82a0621df60d1184aefc46ea7c6d1334b94479b93833fdf0b7ae0fe82ac46685452a8028ca53910f1e98683068db164d4d8dc28bc46b9b4b6ed1ebf37b5400ef47059e04bc80e09691e7b44ac857e47709cca0cded996d920765c4fd735c17105d115e7a1d52fc8e820eb4b82e6bc23d7124c67868e805d5c002addb07675c03a39fb7d4e989f93d6789ed2322a22aea95c8ddfa1ced13e9e70c1652fa145d814b13b5c2b523b359f40aaf0f1520015df887f025d74c1b2de68e390d49276bb6c81657ad0862063730b05dd7041f794fc71ab70eb894fbe2fe77ea624bf5a8510a9b7d0c3dd6a3653a07490055a7ae2bc5a37127542fc314d837aaefb5de56cd2bf4cc4806d2e0095d2eb01e2f46d6d20e3cad42029316b6c16130f2c99899df1d282d63df7596bd04d44319c2bf62c23a793ebcaac6fdce7d481d83817984be2715f7930ba9b27f48636d062cc868ec1fb3c9864f06168c504b3b49da74f248b700138492bf01e579033bf8eb24eff6595a5f1c1f0961409013ef425792da8788f0c02112770352d70387eeab7f35fcc4926c40355faf952d2b25e050ea4a8a5d82f932e8292b4aed244a048a042c83831a5f408466d2420e7b00e848a125389061d447a0c4b86a7582414486ad819f83d51c84881f03990782891458020f29b257c44a03f55b66942baf74c631d80ddc3dfa10285ceda9b095b0e3ee533a84d30a8160c8a71b4e6ccb7a449cc05159051a0a71f207b587e029b58015f31816d29181b58e84d40cb649bcd28778fcffcd84a3ce9c8f6755aa69431d7a4eda6e11a962da696d6d44667afb4d177504f37d7cfe538875c1e37ec2d501b54d930f4ac3e9bcb53035a9d9395995df04eac513fada1849d7f673dbf9c0789937f7d335c965a121ea68a0fd4acbab9e4acdc8fd7ff5dedc4cab0c51a90ea15dea32d25037a4349a0c042ab72d7e4a26c7791ade5fdf27c86968c212d1ad2729376642a407205e613ec32eef409d15f50fc77c564d64b5426e65cc74f2d60d08eb9c892ddaf4181893c250e422dc7afd996c59312e87d92c1602df110b292d880b2f0be69ee9f2be5dd258ea25284f1ac32ca2c78b4a98a58de42ba6d7cecabbe3ff0fba1d7294558ce75b1fe4dcac98a13cb4fdbca461884378fbd03090c94b409c46aadeeb485dd2541528cb920292f0da43810c2987e82927b1595871592330846b54cf20f043ff9bf73a4d694f351a8945547bb68102db5df2be729b46628733cb6c242f9eac671d6f524cecdeaa7315f1e17e79383bcd1d4d6f036247729e3d7e175e1afd94779832119587f825ab621f574f24ead8ae8a5f248d348a5a5749d39cc3eddd502d6705796841983aba419cfeba9bbca991440464440259bbd413dc6cbff10db90e44f29c48ae6d64a8cd6c7ec9b0caf534cbf9d50088e4f61edc8ab18c013022b0429fb771d8775a49b3d0450e1c7ee7896e10eb9a9253fc5edb31eda2fe3d8d069a7e2b88abcf45f14e4198c3135e745a78b687370abb4bca94ed17ce0b37bce7116b7e0659f2de16d0f51d30c97911b8bbfe2bcce45fc78ef4c4cc48369773cbb9adb7a79b15acdc7c5adcdd28b416981f3cf6cc677d6e9f3c0692c1c7ceab86c8aaf443bbcb56f438aa0a7d9b2e6dd498a3be2b07f7cd050bb246b5acaa03c2ae85d55c1e1f37345b9e16b4afb20a696240670f3b81f3853a17886e4adcc86912a90cf3b17b6d9a71826b15502645851f488e6d56ba750308bb07002d9ec35554e9fbe5bef4ecd2c5442ff336aa782a329b5c962279322310f8dfc6dbe0b41a1ee16535af4347d6940def2f4f7ba9bb3cd5fe3080354ed3a9d368184b1b84ab86f8edd6f6a17ac39e20a84520154e33413ec460e5528106927a7e6f5f920d09039515601bf2df24bf98f5d95d9ba7911430750ff32b4f8b3dce6e63bd0cf9c66b9e3df251f0a6cc842a96f4550bd558349a505721b2a5bca54ce284c22dc5d4b81db1c88e3cdc44eab583ac25f32f58d929b94e47c1e8e225994878498a7a417d25f4bc6349627254a0892dde21e5533b016b4921add78fef4e7807562356b841d5b58b32c5362ea3e9dc0e8f47faadf8580d38a866a954bed45be233cad9bd845407f026974e9bfa7a5bb05f054d33800ed29762fe0966be6b13750b15b344e39a45c1945934da42033814e1adeb2ed19e1cce828d66c932ee034ac0265b8d34aca32f9332d9516dabe3a2b5d3e15eb1e9e4262791d99786922ab8680386bdaec0ef62ae376856b501baa52c4c70a4f51df056d4b6043eca77deabbcd0bae3ec1e557035a0ed55554611f7760bed0417f927071fced304173bb436b5d3ce61789ba951ac3150adb632e0493a39203d7d0d1c32a2ac9b395c0db5f5f36f8fafe0faf6daa1e4f969eda1bb610a83d304c9b8ad33cbc6094b8bd4d6ce1fd5f9bf0c6d3bd5be95b8419efc5c9d327f7f0d0b6f25ef41153c6e1ba8a5eb632cf84e7f8aae5341715654d3af81eae480c42682baff0180bb5511137b526061ed694fd4bf8c7f107510b7cb5d5d996f21e4614762dd0ee0321e62b6b7f6c7eccaa6744c02afaf6f7917a4019ede24252cbba8e057d641da9f1b0d5d2ea90ce826e4c98755154c137439140afe2b3b71ace8b18468fae401bb09381e4bfe69093c49565a523b98362a1af9c307e77d5061a8c73d8a3b3c67b8c4379672dd35a39b3f29369d826a2b3030c042d8a682c75c7d33635a242b164eaa49568cd813310cf47a57c2fa8b6875adf30174fa7212b9ea5ad9bb0d837266e2d0578a6f3b178caf4813700d9166b7a7b0e03ecdb2dd1b0f29189ce4121e2682a76f1caf9a56c496ff10bb3b4b45598a5adc66d6aaf962cf91f15e8e90410a6613718445f9c98c88fda311c38a34e8e24cd1dddddfa38b72155a8aa06f44c2444f3f715f42ce784b48ec288205be3913af4ece5bc9cd88a439ed470a245f48ecb191a31770b834ad6825d971d4ce7a3fbbc87a6130c91057e12c305864bac6777f8f5a210cb19ac66caa36d07e8f137090b814420fef8effa9d8a267505fa84c4df7f740152fcaee0f74156a97a70803624416e0601a71e3ff42f3933801f26bde4508b056394b8d145972625adcdfd32f925eb07812ee53d4836e44634f6321071e5a9600de3b5969f873b07f2dd0e36965f5e4ed195789534f2237f4129878d7b0a1e570329e20bf5659033d6fafe5cc4e43e7f296600cd220567633bb8104dafd2dc242100fd6cb277aa21fa7e1b17fdf0c21e7ce300e090ed7514a5bdda75d489a0b52f8b1c7b4b319f13f024ea6ab6b374a16994f54c0def0a2842ff02cdc3b12ae8cf24652465436730b68bf8f19ad86ab8df4b307a15423f15a39818af832ee0adeaf4aca35907ac1770440b36fd0cb59d460d3911473caf24c25e32a5f9e9786244a234d90e14ac45086b9c21d9fbc916918a2f0b0d60fa8a40e204fe3c9ae50ec1a29d1e040730e21460b21017a31b4c98ba7694bb51897d8e0d9e4440ff246328eea284991ef31d4b9c69efd157f0e28b1d02a49dd72d6027ebdc745fdc6147c66cc8a1acc585aca94a10f718c51185d4783f8aded52b58c2469993ed461fd3b6d2db80424a8d64f68395245b3c2d0a6ccf85c8163e747ea823e35fe9b3ab38a8b1bcb8a616386715bbdbc306606858dddddbd755b9ea47e0e99ba5a3f36c7355d2e165b572bece21292afe8c6f41aa670ec18d78cfca04ed2b203e1542ca02a09727fb51b900b7c5d03f41fc35e3a43007da0f2627a52f4d23035e10e055a843c716ad2ebe1b75eb156c836ec8629339150aad320c6940864be2c2db505a3513983104ab7b537c678fa9a8acfd795d2f44c1899c25b11479effc5eeb4ccbb5e92fb9b83769c6c3c7b7750195e825186d2f43a23f9b5db8b74b2b30d6387a9252696780ba3f7788050045b39fffa9e72336e70966bf5338ebb97290b908d7098e29b32e9958a1dbf40797ae94a1b7f1a705181de921f0e2e4f2fed6297466a66a9bc5ced1938a4ee25c9b0264fcb3542c1632c04fd0720f01ed73aaf8145143f6bb4e615d2dbfc8a08bc8cc7bb7964648a45358a797cc59049a612516a321a70fb61659585ee2d90fbf9dba93da824c37affed964806777e12a9d8fadb1cc6876628081b5f972b5559d831738f713f7ada51e86d7bf3916dc9033750f31d4c6ce5118f6a5908bbdc10c7fef4f67445b2c97756bf793c0078d89dc698554a70afd0510e2ab5019a3b4205253ae8322a35de0ddc32c60dde5c1663b3f14de4694627745bb5a4857736d881fa5db9c58dc7dd2cc29d05d17ffeebb968fde639df6ae9465041357843858a76ce6c26fce7e15259c65fcb86c4b90eeb06fdeeecc98f2e7700ccacadf47217030008bbe0e57e4dd3e8b7f7acd66bcaace611b82b3d78f1ec3d2975e2d7c149c93ed2705f6b2f231080edfe8755f06e148db790ebfe82e504555f4e90af55677fd7226fecfaaf96471996367958f722cb65508666d72ae9b41eac95b502f1815957d11e7e3d6023e5e02e28026ffbd7ec507bfb75692d96333f5fd5f4f6b1d6c65c0eabd324a682919acc669562e26d1fae8e9d58597b0e29b425df7180e85362737991fec6cea64ec1def2e97b9b667b8d152d2fe457581a8d798abf9beb9da2e5d628cef0886afa9ed9e943aeea395654cd5062424c4b336488ca9f6ebcca645b7fa1479aae523ee252156efe07ece2e6b4e636e804a42f1e2646973349caf2adca9299c93cf87e14a43c99f00650f38d6e325dda5d1a5dfc7b7d11cd1ceded78080e8d6d5ec0b36d59a7252c9ce7f2886988a603ff4071f46a4cf8f2bc0c4d3102f1fbc0a20d27f935a5690b403c574eb8d9e298f1043255a0294ac0fce71592ed0e5bfda3d09f0823b6c1b470ab2b25e499e539c4224c0224e653d10b230be1c465002c94f88a730310be0d06924e86af7d53035034be4d432c1a1ec8af4d4a9c4696ded98b263df0fcf140c334aa6b6db955613266cabac04608f18fe21893f92a5b2b587021841ded5df3342ed38939d55a310213d5ec9f0b7e3588a6b7327c0b12672d22817a7d929474f67e2d451eef2aab1429651be0a6c02d98169651d187ad3c8630f8e9233d962a6c225bd8bd191aa4726b5090e83115d3b4026ad361a9a22b33641fd487a2876a67c61f44a16eac81d578a661c53b23c02de806050c10347d579a61648dfe9a73b6f3588e8ba87fddc40ecc472de85e66b4e982f620b646131f16fb13a308d34d98e5c2a45c41c0f9ebdcb15ba099e73458532616f6f9f559891925e671ce011523ce0eb26ce5d5a0c76708f6df30a765c203a3bff683c3ead84a14333467309aca330a677f60e89e9466943a58ba6a8c55feb68ebd4ef4cf610e99332f1173135630685d09d01597e3d6723bc9f595e4de3a3b44494addf4d266242eb5b7242d010ba3c11a6394f731595f7221bb11a168446514b64a76b9acb7136d851cf760465ee6f97325145877db05820c6388939b1ec0b651a63617db06dc259e3cb430bafc2c91df21f3e7fecab29d78a8ee5acdd931575082784c217e2ea01481b063856de5dbac052ed20a9ef68589a0604c80a38161464ac5f9e2bb6650bb36e46f643375c6573286b538206edc1992a15e1a4bd59ca73852830cae947656426891ce8e2dfaea90cb3b84fe7615e2d03f925204d594607bba2931e54437484947027935850b1c6a92faabc4036a82c8afb038891013a40c337628ef12acd4795a4dffb55979eb49c405f5bd861b6768889d6fe033c935e017bdfc9b0b7c840d9a2ea451096f27497bab5c7e9d6fc2f37f2c68fb9919d9d6e9a92a17e4d45544c2c221195cbb2bf5d6665666aa90eb4761c4cbc608ea587a41049e7290f7db67873bc7c100e57cea881582491781727c57d44bbf23279a4d529b7ebbfa6aa902d6cef28e1c8c3007459eeece6944f3ab21b3b90d6998d8dab879cb513c0d58f324faba3fdbf94cb5443f58ba6ddfd5aecbab634210e6f0392304a4e16885541114fa86cce0d30a6850238977e496ce59fc4b05c3e3b5fe335ea5ad72b5ec57a59afb1c0b392d841332235ece51c69f824863d99836a013bc15d52b642eb987ef5273fae6ffe8d74ad1f81d83f76b8b2e5c3f20a69983a002d96ac97cdd9ac033079e0af59c5a2a40b05abe4048def77caee86a5157404a1233546e4890604a28328c85131d34f1f183f22e04c1e2ec3a907aa1af526d34a8eb41787627fb225224e0bd1d6a2e9d3bc0ff7143903362ca61774ae739a1fbc2f087accb9fd47eb6734e86909c1c7f483d4753620305e8e8486435f51e163a95921039ba0dcaea74b4ef1e74dc64b4fd2d4bb8fd8a5327737d86a4720c8c3d1afceadb02e58d408dbb33cd0629a0a876ae2ae4e2778c7bd27a555bfc48e7e5ad16b8c06e239340865c01e73443e4d5613e70da842762d7bb1b328fa6e75a76288b3ce955dede62e9e77e9e728fa1751d7a76ad41b87c14b8a796d3f5fabbea0367aeaa2e23a4003a02125e655cb26c01c68f15d391ccf84746cf155eb4d8054575465c07baacf011fb78d36f00dd34131489962f23fa560e5d1addf3fac0dc50e65ad0e9fb06ab4783401eb42224201e2e5b363e804ee0d78a76ba3a3922ee95e2a047390e48cc76ac28f383cb5e396071d3a8482d6ffb15cb76d8a796d6955e583272af68b75965d53f39e89cea3f8e36724cbf2e5934fa0015791df495ea721efa063f20d56dcb9d903e06a4c76e74d0b81f88fedbcf70d4c924263328f1371a0acfefdcb3f1b7b5bc7fecd6d886b7d6fa47c7cc3b35080178f52de466b29967a848523c9716ee22bb3687ec21992271d39a28b495ec4a75bdd17b9a2c1de3168b19a81664a3c7e6c893db373a6646dc32ee97194774d5dea248c8801a98e9700e5ad6423f8abcda636ce35afad8172bbad9941ed852df61afcd1a8d8d7055be00158d46963a8e3f3bccf43412528afc1d5836cdea7d9894943ae80aea65a098d8ea98666744972ebbb01ce04a0042660bd600910d271e2b427b6332fc8340c4408191ee6ecdee6ef29c6f72543b3a7b036926120804d331dc157427e66a4342fe3b90e0a12ece8262f1576194d4465cd10b58027c27325a97669a08b937a2112a65ac486a6628161c238b6d260c4218d68b2d127f8464ed401a90ffe7b672ff6d77bda74fd8c8570d69b4505c4a125240d71c8f48a8c865be67f4938a79e0c4b674933bcc48cfd4b4760d3a7de1bc074c5df4a16094dd54b26b5a362ab11345353014af4525d307ae041e853923612a8adeafc45de0c68658d685a8bebdd6da1ec28e471cb10dcbaf7a6c376faa12b1a5c47245cc040fb528cd8d64deef79096205bc25bd355dd41a3ea320370a714bf7b7065750e64d059135c0b0ff03179936a50b3744ba99134ee85e33eb5f76b8502d714b04094a2f3f6195dcb586a0a385eeae48bf91b4ef4286833812eff4a6b10d6556536f2cdd4c53d59a2d160d945c940cfe1903a80d0d63a12650fe7ea156534512af69958d24abc17d1144d514b72bc3a91f7c29beb5a86bc51619fda1abaf1f6cc9bac1eb604155cafb7af1001a81481fc2716803e1014a94ab4f050aae6da9bc7f0611836d6c6eb89955fff8baccc5d6e5192d56d824c9e9da523021766c3dae05cf6a87afcd3256cb6f6c5257873d1658494042d827989a74e23e88ed60953ee45e7f2ff96a41f50b485a3a3c446742139270ac03cba47bd1097e208d16b19ed4519f0518b9825fb7e9c2ac392e7a8bed38457aee80bb7886f88782e384a1d5732df4e8b72b5b017bee1c970fdc04e33bddaabaffb3bda73acc5dda3ccaa032ee68989b6f62332830e014df92e12b46e6876b9a7d12ae55f7a253d90a3d20dfcf4e438f291ef25d9d8952b4bd28049519ec23366aa293b4d0caf89973626cc9156e7ee48a2d52808484733bd5eda3d43544b3965420a2e1445748548b88ec11cab4277b4d711263da676b1e2b700c8d5dccaedb2b583f843433899985e781654b5d0f2c532f51c8801e616d72c82757c5089a12a9042372b5c64d3c1d981b07a8780359bbe0f96e51af28c814e54bc4c7345c1c97a89f6dd25e2909b0f7bf396580e69577015324108ce482901a4af86a7382d919008bf8d5c4886ed6ca88680336e00977889b2fd80fac7448863faf9bc431dfc7397bd5ccf0020af331645c4050424a074ebbcd882aad8fab7ff3271dfcfd1539572a6a5fc5e3fca760bd70628d3d2574cfb39579da6684bba974b6bb407f20d5cdd8fa235f84b614cc70d234f34a33e26eac4082bc3cea7d8d4ac8269290bd25d9726f296592294e063906f305a1932219cb1dea70460732b9a9c81dea90c4579ea33efd1a39d571da71688753af24e9b14cee69dbb918c2262e7820bbe93c6c1284bbaec615468e9408253b88710176bbbbba57cb40906599931c9ac8c1cb980cde9f546015c34c2083df2e4a91310cc3304e867bca1d3241ca7315320999f87084ad8ec33dc618a3c718330b05479425c9fe59fff6c9d5420e792ec933ab15bb688d33694440031a982162053b530726552293a27c73874c84bc5810cac4445830ea37e2e9e9e9776cc2f7f83e2ee8fbba8e2bd275452e2d99eb40c6b560331d22f0040c412c29aa82648b1c72b021871d9ef59fe5f0012a4c391cc121860cfe77e6bb5d4d9365666aad9b0c049821080b26bc00d102144b965cb164cc6949134ba8683d2b8c1e6e48e290844b82421c94704492860abc60065374581ac2923471f0215cf203d7028724bc818bf0062c382317c422644382942d1b4c52ee5ce215ae97bde3f4e7ffb08089c42c39bec4442e6fc597f32f6cf380b86343c2f5f4ddb18361638ef4dd3f88e315e8fbf4c730ecf46d82207d145f89d39f011448984f3f08fdde48b021813ef6185e613ec5403b5d510852714b1c08f4ffb0b0d39b109b0b076989f33371741c59987212d9f6efe388741d47249ce1288b44f5c70e2202bc745731856dc7e117ae18765d94ce29a53b8679ae97b2c7f5727a3b0e1e7f0cc3ae8bd23965ae3a2e4741d24f0128fb3cc1dfbbea6e56ddcdca5b5406ec767775ef54f202eea29ae9ee4a8916d38b35539454c31819d4a03e4325072331507879d2c50d4561283192d118728e9820091494f9a10a1ea60d45648a5ed045bd8a948ac47461a6f52c2d77a8c48a0cfe77b51047c0a5882cb02417fc768d29610206324818d972874a7ec8e0fd222520e880121a8a844a70c83377a8a428045e843580c943610892a4ce5cf041ecb3afef2d0ff36a996bfa0662fa68c2dee457966511833cd8c76dd23ad3745dca055df77da9167c5f2b8b304998308917210d49a612d91e3b245a0a3f400406b69d688c1019a22c82c8980940ee902809a22b5d27b9e0c22917d3d2b03902767773619b057e2ded314fc360ab55475addb09b4d7b0d6bee616c0acc056b6e8b798dc1564aa1e0d6075b09c32b8a44a316d66218d6dd8d611886610da6c160b83d70476e77f7d69793c2e29a1eec2d13042f56e808a541b2e9311931afe7cb2613bedc9807fbfae057b1f63ad42a839afdde91fd9518f8602b37d66a533a49d031ba4b3927655dd7c6a36550112d5d1557627095634eaa0457c62ba574fbe949f6e69ab4d344b3ee29bbbbe6ee56214ba941f95a5db06bcea03f33d565de4829535dde6fb012585f7a5879fe4581a782e6c9ba9152ca13f25481cfa41886cd97d50acd39db5ff82075c58d3b727767a911e6c69772b666ca0473ca9cd40a6efc54979c9aa5b7cc1c398e13735a0766521d5d6bfa59e7eabbf87990b611ffbaefd4b968da6a47eed7691d434e5a4bdeb0e815b39bdea4c505afbc9fe9bfc8fa2282b79272cc288913b70c6faaa35b6bb6da3e25e682ad568ecc1164545ffe0760e4eb807093a586c1cf3cac6bb867878d5f5b4bc48eecd3564773e4481cfa2a1fee45858eb8aaca55f170c15854e438a260a10faee2d00932c76d69c25de7a98e2e7d107c486acc055b890216701cf695889ce03f8e03ec69e5a882081683fd25538b7d668ef14adba0ef9e27005c653016a98e62d1dce29ee043a30419cd37f23b4f1c07d83377e4e8603dad25c79967dda1356258a696ea123f5e2ea9c7a496524a8a35a5f4468dd6a7ba5c4a23a5988c1785814edbe81d3925b0ace861657aa3c5ea12bf6f6c2a05373e8d31c63ae79c2da094ceeeef9058abb46314a3084e6374778f116c6511bc9e2f471dd789dff46d83c9f1ad96d8d36072bc45717c1def76015642be587d936bf4b072af22a635629a4f8b3d3b6eb79627d6654e966f3f02f17e6f0c6365ec4763b8592d20f2638c11ec72b7a6cf19bbbb654c93260dbe966a5f57f7cb66c1ed9262c18def616c950f76774f39fbe7a4294a29a5292fee840139098d54d2053f67e127eeccff313241014148593e0bd8981dfab553f16bd67196e300eb4d4b7a112956112684814938c4251cb221445214ca30942241cc734a4f0b73e54c188396308624e1109930862774956526214ac2e50e5ff8d27dd77b57ddcd9cb3ca8964ce3993e42954449e4246e439efc460b042517cb879210c1bbe200606173278ff4936c7efaf77e56ff2eef4eadbcfbc483dfb26efbee6f95bd4af36fbd64e9cfde5813703fd0c9138641e0f669272c8f5d9444993e9e9957a308531e89ea569cac79e6690e20bf43323b842a6af232768dfc779b62151287a107fe2066392cb2d883d51b65f9f4aece359fa78c630959867fef634daa7bf7df4c0a88d805dd806fbf8f6c7d3bfb00f45a1361fd7690342df4e14b671c6bfbe6d3ee6f35c18f5da0644cb736210c5235ad9f9cad806400ee4f934538f661b81d03c371c856207f4e9e6e39a7888fd788a5bbc36cc33dffe85a1de71506a31cfcce20891fa0f8b411f6983fdf6f469a41bb639727d648e0ffac86c9f7ef6264ff391f97afa3c169b7e6e40b41caf377d463433f603d36bc5f77169be4f4504ecb8044dd7a191138d04e393607caa22425c09828b2ae848082d61a88a5c2113c416cac106390824d6943f719493c9111b350cc619040eb9860fd95f933a1eb72b61ae80b9e9be39e79cfe4d77a4bff47955465c39e7a40f52ac010e32d2952d5fb2fc55084498c6545200ca1ae0803ac07b2f082785f8fe2a23aebfdb08a5301f9c58031ce4f8f188f577eb1e10d959399f6e3fe83be6f1b791f8877ca00c94e3c6c3e6c70365e901711ef91ecf7ce9ab193d8c1fb4001125836d254cf6ff204816d9bf70652ed84a1650caf18d2427c7827b71662ef82fe57f672982b33833d75fba771d175df77d63be4f8cc66dc19aacd842626d61657f96b6ea1cbd8417435698e922a98a17ecf8df748e57824186212e31bc10c44e0960500504559088f142ab224315214e06b7335823b2ba8e1fbf7aeb4ecd5bf2de2aaba46d32994c2693c964caead7eee84a3a9572a600d4a366d16432994c2693c964a28f45d3d78951c0324f05e9a70094e77716311b300cabd242c6c478d119b92083f76da8d9509655b112841523b6c84c56a664569c5c2b339cac0469475fa872874661b2325ef840a44a0d31e894b22ccb321b188e8a38a2021f15b1bca839418c2d55c60fcffafa956bb2c556c40710843e10715b78d132a29267e40e8d96d84aa085165a6821c1f824189f168e86e568582a70f08646eb9a530e72fd99335c3176d189e35c1557f396572e05373e585f064d01c11129676af822258ba22f538a9722b5d61b3c7728658a2ba84cb1c104b556ec48ad950a95249c972b36c81d4a0142ca940cdec781def096a41f98db7ecd9eb1cb62767777c7315d5782aefb3e2abe8f53818c28989e4317ae4b1747c372342ca75c97245a43286508096ae525ffba1e285bba8d10d3e488a76bd1038b6ddc137766509bb475d4a7d76332040ee42b4f9eeba9e7fa36d3e28cf24429fe71bdbcf09541d86e1c743e9538e694deed817f06e2773b0de84f119cc9f3a50567dc7ef44739b710e80ff9d325be787aab717b4f193d04ddddcd4171bbbb8b889a08863470c78e2951a2e4f01d9f53327823bbe6387caec0ea3de4d3cf93676a8e83cb8d0f85523a5bca298dcc288fe08ef023babb65532417edeeeeee239cc6d8b1bd35c7582e0b9785cbc265e1b26491b59522162e8b129705ab596da42fe2d1186f0246124d7a61156b0733e9855d8dd18bf6984967237d118fa6ec2ffcbd81880d5463d6420825698913e27608652984a2d443181e96b8216e875090c21e96422858be3062ba94abcb59e1ac7056382b9c15ee08ce0a3702ce0a678543e290e08ce08ce08e686f40b78c5205dc12b7df65cb88e294b8fdcd449428210f4547577e7f941d23b7c4f5ef233826ae96baeaee9649b4c6214d7a572ad971487061774b2e64558d3ba2cec072576e63d9a24546914f96c88c9173527a5d18bd2e0cab35bb34acd62c33996c663259ab69db699e4ed2e3c96adab69d4ea8ed244fa83b9422c34d8e931eb92d60be60f12b7ee444671c072772e60b3063b2d8a205293691930e45a11e23e40b9f062324ba4b3927eda24b3927a5d7a4d78561b566a66932498f26ac6699c964ad66b29ab66da7d37642a1ee4da5384eb55ac5882143468dc66952b5f988f95bad34192386263d199b8f986fbce5fe31eae97acb5f725876762818aa54830a05703ef0404307a6f030e4c214a3f0090c9e7041458a9c522217e59a72874f949e8091389d310c4aeb593b9460872932f89f50734fa2f4d8e1681bb258c21d98e078e881dba1ebbecfe90e3a704ce464e268588e8625c5c813233577e80407274a8a38a121c8c90c45b8269c9312ac4fe3311e1fa3dbc667a88f1b90da24633f9320caf6af8dc78c979e8c9fde0903625f6210687af1ede603fbd5576ff5298fc7da473de7d9cce8ed877c2a6d667cf79d4d34dee4d984609fc6dba7816d66e01e40997b9b10babfdffdfd3bff7a55adf53b8ffb1a8f7b191eb87dfd181e4feabbfe0a8356c5792c8fa7fe05bd1c9ecd2df68a6d5288325bcab26651294b59bf37077c1247d901404848da83e297dac9bb9240caf1fb1d5063bef478487c7d03892f6777eb53d2d4c36a95c3b595d0a5a122baf36fa42fc05f333d7dabbdd5b00d09fedb43c03fcb26983dc5b20693331f9975e4254dee35b0ad1ad6a53bbde69cf3b211fb53141840819f73cece87b25c9b7de3c5c436ed5899d618dddd298e1fa56702cb676067f764091de92fc67777afe7cb13bb09acdcb8f6bb57dd8b6d998308f8e90041f57c4f7e17afd83a5a65e5821ffdea43e9940bb8ae8cd7348195793455940b5e0d885b9dce49737cb2494ffc48e78546db3c30276f0f428c31c6b8e90c5dedb5133ee851734cd236b4d7913959f3b44e0a2273745d5297f401089562b0fba718fcb20a945228f33d67c7480275db505db92b6f752aa30b765dd775d98fb20b8b137cbce575870b462227bc7063c640a01c67965ecd71f544e694de8db77c53ed70c14f4a0a919a64faa0c7237fea78ebe3998ffd8fd611b71ef3b1f8818eb7da7174eb8839f2d05a478c2a2637e786d45a470a315f1fb1ea314bcff4d2f4597d1dcfae9c65207b9f2b9bea6758c5f2a7498b524a8d6edd36c053e6f1a36dd0296c2780956bb5947e8c96524a37950e17acd9f3663679529a7890e0c78ff437cfe6c8cd7e7b4b371e1b9632662dbb7ebe8e742981f85006ec006d512a51fd70ebc72b72204d9a3453648a63c29729e6f922188bfa81bd3d79d9df388e6cbb568ea322b194e5db8490bdfc0c4bd0aebedb864ae5ad3c307b0eccbe1265f9ab472292a976b3f1b83ef3b65f695f3f76db0057ef0fa4023969bef1b87ea532bdcab3b9fef49d7bf3917dea3ee7813621d0bf4fff629beb6d4238bde94f6f0a5224ee08751b00ccde1ff51e8f86afb7228f6925b1f80bfbd92a11dcae2b2b4b1ac39ddfd9a909ac1c31cf856fbce55ef194172b6317cb89e83a150c5d07ceac61e0a499eae581126cf4804a20ccf245066788840be1115f204194bb7624d6a2828a683c5454eebc62ee98c5115172874664c907c81d1af142b641456aad547ef350c9bfb08a27063d950b773ec555f5e4061132e41a5dcf1d1a2194ef511adb4effcafe8ad227065733c330393bcfd879fee55d1f35d7681b418cdc0bd47a6ff7ad310d7eb3b7e4e46897def556931660111282cb105fe42f7738441339cb981184c9436867f3870c21ca3caf4a1c9f7875d3f2c0ed0e23e59730447a2b242aa70b1e3d0a16cbd1e4389a1cd511295d4a29a5a391d2d1f874348e4675244a3064946048d964293bab75dcd602ded848c07c70476e659b4b2f7b9347ed066ff2e8471a7f79fe14780e0000d62b379018e8870532d83ca3793ca86721cbb618a6c5d73a8b75bc4cf460fbcb62702e0141bd8641075bc94a29e50cb9a16e5e3ed87363bbc13cd8a332785d3802fdb0a0029d6732e8efe33c35cf42be3c2733bda01805e7d2f5357229fb08358f44e95a31c5577322d873a3c76b9ecaeb6bde5568499cf9f5da3be33402eafad8619beee97bd2cd3f066b663c0718bc1900d806d7e0117ec8bffebac8440fae8f78a933c8c1fb8373e9629ef8358fba68fd1b0cd6d89f81c18b6d300a27c5777933e337b8feb4f990a7dfe4552b00f015ff06db601a333038b3cbf8273ccb681b0f8abb534daedc799db1ebcad1a339d6f824133bb8fe724fbe8e8c353a2d9923263d58e315559f0542cd9ff2403a1f755493e3188933ff8e99919332868c075b8e7b6eb8d71267e5b594382d712a8679e86b2f51b34cf4e04a61f0cbf422b92bf5dc007b6e681f4ad3343ccbc40ee2632a74dd7fab7534658e2a71664d0d9ef8a84ccf8dac3dd87323a3dea43d38afeb756d437b1e140623b601d1b2f62ac81c33e69c37f297af6d96891e68b3cc5cca19866115c3b34c752214e824319252d6f2f51389882858963216242c455892728f6fb6dca8d18fc1f695e2eedeeed976f4284602e58e51656655e54679faf921dc79c5eaa8458001cca8b5076a02b8fe2d716cae5fd170b1546259b7a70d4084e2f8eab0deed79d16b1582fb978a20a594524a293968e1eb58375d6774c1aebb79566b83fbc5306c8f980170737bdad09871838cc09a7d01b63d9c82a8c19b25cdb1714c6f4d6ce3b833cd437edd78ab6a542e78e5bb978198afa717861267fc6f693e0d6a4f809c94524ae7eae84a29a58684bd443a56ad35373a76ec3a19426cad31ee0dbbba3d554d23e66e9051e709902b293786b63de8d801f8df2b2b176c6559e3bb9123af79d11415db23e67b1bb33d62465d1903e70ea5a822f706b6bbb1940f315fbcd55224e5985747eed5e613394ab125c767ddc8f1477ace056b9e370e41e914a07c638c94ba8ced274a371f4057f41b6c0c938e72872438ca3772875280915138ddd6b1db0da588927d3ca380e5146a504ae94529a594528a6917a59d156b6f90d1a48f6d85224d57c61ddb2cc751bf1ff35621b8d9c53ec8885ecfaab55905c83c58ab2917ec3a6fe938763f176c8f982f9b700a22b9c3a4a55c80dc611212f6dec2b0aa92c1fd6e442f0331c79f506a875324c9314c42cabd39a63783361477dec27e662d0719c920a3e8ab1aee751c146904764abffd7dd978838cbcb37d838cfa8dfc09757318cd2d04f9d74b77da330adbac99aade5afdf456462e58796cbbaa4c14495c27378a2c32a8658fc24b8e7fb1bbcb1946e14528459093b1edd845a774298a32cb1dcb0c250c61a68b2b9e0c91e08a113e705992e3b6aabb6d727c5517511071e9810b240ad042e8090a8c4831855096289a6cd105170fe42b0d105d37a6eb5820468c982340342cf1220395263c48c152c4126a0b33a82db20da345105c8acc00850a1210318313135ea86419620c957cf5804318212866e842070f3881a4872758d2644b965cf10418275a10668827c674b922842206288e7cdf97eff3a2b2627e0792c8911ba94852929caaa4289cd0b2e50a298a38f1e589284227aec83377e84415377c1f97efeb3a28bace89ef73a2eb9c68620b91266808910c59010d5f783083948219a6105b88966822dc72255fb9c32d5598f83e2ddfa744d729f17d4a74dd08baeefbb27c1f5250ad352854ca59eeb0892155124b78b1c4900a498b90aa0855124c88800925b007bb2c7eff5dc17efdcc6befc7484866adbf7d1516727a897fe29f3efbed6d84ee15e7572cdf5a97f13164788c3f5f48fc393fbe0a0ba9a9c141627cc43f5f8c978163e01f1ddc12c7b15f61ecebe937cfe601f5ed0ba96ff18f91ea7fecd7576121a63f616fb2f8e7048983bd7f9661c73f910826f28ff8c7f1cf8f9190ec23fea1c0f6a8ef7eefb412f7bd13b1b711babd93fade89bdc3793f4642eca7f04fdcb1affdf556c856301292e1d86485ded9a28dd08d471207c3dea68809af402b5e817e635abe608228dbf6f61314fa3bfd24c41dfa16284e4ccaf7a988e83a3955445c214225c86470864a64112a112654e2cc1cb2ed95763b46f3b3bf40ae375b4dcb0e76f2e377dd4cf433af7a3d5dbedcfffa4a59d2241d0c59317b93510f022077b35a35be2ccaf8652d6b4f1e76e58d66d4f57e7b6bda786c98dcbc994fdecceedd78ebe4c9ebbdb74e9ee7cdbbc1b493179ddc93d779f3a84a877b33cd2e96ef76dabca6afb967b1e8337dd895c9dfbd996386639cb9e56d12c3a5356190954de077d9d295e3b84fdfcad55315063fa39e6b2455f6944b3df7947bfa395a0c0cd64c15c3feecf9b2b556e5cdcc79edad946751f64f9efdcd568b41d34bf05e8da6a2ec62db300c93529e50b637cd62b0f559974b76668a75ec9885956f69a83e70c1de02829459c51c6cd9761c9a167ddcebd251ef1b0fd4c6711cc7a5521ceeedf4d4070af572e381fa54ca87e6537c1de7381edca71e859806dfd6a3f0c9f3ed370cae9ad334afbd85c1d46f5cca036fe4d4c90381f25b94173f6e23c8e7b6bfafe339e5532676b0bd8e3c6d9a9723fdb1aa87a20e045cddbb4815c4056311a8fd38c5a9eaad2937e59a102a29b75e975feed82c3397326d32c9cca51d86d048bbddbbbdb5d08efe912847a421f105a248d4f40361115f4222b0844a70099350e2c62433cbcca5b9349f47fd0c850960a62098a4af238190c8344020268d459e3c8d5390d6dace9b5fe3496f4524636f13429bb5a8e46df754ad848a46000000006315000020100a0704e270482498a792b47c14000c7a863c865616c962a12406711005310c8410429031c000430840c821876a4700a6a92a2475697b91446d409535993146d72399a161732cac2c7d4199355abfaf3750e413bde1bd3ff5040b1781c2863f0bb9f404037e6a01f1bf9329ef4318b9057fdef5d6592fef472da03f1d9c0b58ae1773bdc5304a3203545646ee11bb15774d8634a13220201842872818ba1a58db3d5c95f6c7c17308f9c89b4c55a488b34ca58fadc178c8722c9f954c5cb95f8851b30ff3d5670bccff87793b9ae9a949c68910e44325aaa3003e1558f4c177d06bd8dd71f71237a86dfdb409a7d23e1e0e8787a982537d61fc4c67d9557cefdb88c470e3ebe35088b477ebd5a017f0a6db69524df7649269ff4ae70aa991ea49fe51bde9bf5404a0d75323765b2738fece35e0882d9b4e46b9023e1b97dd26dd81cd46f65ce256211ee280d05653501d01abcb1960ed04b0f4326552293860c9a448da33578d5e9f04a0812ba5e2ae1c42e1f084595b42c12916c9c085d9c1dc5505c0e03a2c4cd26cd7b147f269ae016cc0e1353c8518b10d20616f8c73733a7249228b40a5dcd5f05d360bea4633d0399023c73fdcff152021c65a54e446b7b8d23519f68a8c02526bda5bba6a29d5b4f6dcd4586430f37ca73cfe70c7898e1cb1a024b5522122a3f3632ce2c89299e4736067a5d3bdb69675acaaf78ad6ad0b7a746cb290eb76b9f6d3667481646a546c078fe11238dff7ca40badeb3a507ee2507a4d769a9756cd31897a0142322bd7eb1c897d06420e1d8f5ba363c24a79d8de39fdf0a56a7c9417d0c57328b704517cac658f330cfdeeb7cfaae5a46dd7a9716380741d6879bffcf957b1e51ccd9edc7032f908f9e0a838c46b838347dbfa8945a0cf11b31d9921151c98d3df48995c153da26e97b18a96d0b00e648de3e224f2a728919308709f3c62fe46882fe9e522af64a820f6927168606b41d6c4d8caa7203af23cb88f62c36d3f85c4025bb602b59492df4d5c9df9beb6980545a82b512382d0551da8479f64b0764eb164ead8c353cbed9b3bd4e0e1363a3ec1bb05841b96f4bfbb52f6ebecf6c31962807bd71b20564a0898bba9aeee899556916ac82c818e2e428ab3cfbe28ffff9c677a0ef43816d38262eebcf6af4ce88a4ff9bd5448e87c8e335bad22e9ca07d72c6e8c97039a50ed34f405f0e104a8e57462cec3c111e5f209018cd5c1648621f81bcd8e20695f077361a5c4bb04c5b5b576a709199505833de0d1ec920484daa7eb8f7d199d96e2c02e39f248286a3f60a639a1ea011163c64208d0d4563a6bbd01738483e138797477a849aced6142324fb30f9212eda7c07a62c269b99ef279f74674cd2f9099a5890a59fc3af87052043280281f2e6723b33fd346e4fe142ff4c90b8586fb1c848cf50b4e9969a19fa8e23316fd54d5160c5dff6caa5bb0534812b6836a8dc5476b36de1c024e9b519cf806211f5dcf57e739e0ce18ded4aff3545905dc59bda11ca9110ca94e4162e3f106d96904b5e78e9d88b0fa2c304690ee6f98aa8508e78af064bf1d944b46719df405b6b97f0de1f3528d2153ac94c0b612c4d2bd1787d7df335f3b856d408bbce727dbffb02084092493261e2742f76233b44df6cb881b1aa3714411e44e4760cbf0cf43525aa1356bb1e1d3a69981aef664110ef76990262930dd2d22bc218080c06f724287172064a6aedccdc144e12913c25147f9599ad16102ae505f9ca1544cefa74e8420abe411aefb73854dff1d22e4015949120be83302cc748cb05a63b28e28f80f688424232775edfdbac9c498899cc0d3d47051307fbbc52093e17d3712ea2ee77809a2063b723dee36a13842ca2c76e901358a619244780f08661d719290dbe927157d6f5cb56a7c6534119d7faa71f1539e58424f257b83474b91329ba15df62af72ba4e61764478cc4724ca438a22affe01d5f3d51523eb4a1a3d6b943684eb7f0dd04bc0d9299c97db9b9ead528b42789eca06259debadda1b656dda2217943ab864659674cd1a917cc219205b4e1f5762984ca5b096623fdea57ca0e89a159864b7bf4e3004ce891af6833ddaa7604f77a431d2dd61c4c82f5dd23828b66f1046f130b213a6a29641f7e6ff1ef687ba0196d3f829c61ac8b1f6ee714c079590e7dd17f8066e79f6532b0ce3fa4c6dced6facdb15ef14612c302e2dbfe931deb3493b793e851c9b7929f3c794e68657aca03e1495fe968032961f44f665c0fae2cf8c04d055db51ebfc711bd972c964966a420c2f4e3007a6efc17047f9c3e70ed6c413395aebe3ee45aa67b803d2d18a519eef317284c6e155e7a13c19b011d746d44f9abbc8c5ddd97b95fc7ccdc6034302fe9db3a31f5282c7851b68e347bf82de454798c9c89ed4a066e10c4112c3f1515acf810aae500e80a228200e2e54f1f17a25f92abfb081090c89a80eb50283136bfd08f490d92357c1ac32d434f69279a8c8a97eb8db1a2e94c910d0c6ef2031a4ca81a5ebe89dc6d7032b99b6202086855ace1402f3d605dc08112005a5357206080e5a067ae9abe4ad846e2597ef6ce4b970289827521cabf7585a147c0a25a06aec294be4d86f5f6ef35f96ec97eb90236a4d02356041af1593c72d24454e8622b9c1764248dc10d7f81a249aacf2bcdc920d2b2c5fb11e30d72f216c6067422dd2c547e6f82b32c2337ef3e022b25adf5ae50625721e1b6ed4c11b921b2f4018baf326297d3404ce35b06288dfc10c616c8c47d180fc844bab970a27a59dccf0f289bce6abc78c3c8744e83394db60179c8a345e803222587372fde09baf9ffec630407cdbe3f0985005a0ebfc2bb53c8c12cc1974431ad2f9cb5d5b6285ec533975e93a3c38d4aa2b43e91bde6aba3b0dbcbe249615eea46f5574f478efec2be655ad1d4e913542c859c26ea2a81327b0ef77ad82de0cc6a9fa590a5b971a22f287db21c470260b50bb8ac81da79b9c040e9e88d389a48aa490c8d8058e5096639c5c27c722229a95b34a00eb9348b97f2cfe24d0194b69950095abcb7869aa55439237eef703414322ec8ebed46b24543c12a0ab362414512944a93820b44df97795c434dcfdc4633420ec10807a8ddccf7cbbaa43cabc851b92d6b43f2f88ebeb8cb23a5fc0132cb897a3d0e68c0201f9a032f07a7141529351153fb88c30fec1005fa1ce8c46f3eaae6023fadecc1ada3a3bed3b7b9a5818280477de2ee80df69d559760a37ffa1a556a609d32b8a1be51d8e07be3f0a0beffd3386292041d6e0740c3050642dd049dcc2b808f4ba2da595f02e1f1a864d63179a5c0afca0fb47959a2e5ccaf9d5ff2595d7578f33d2d4505fb98fde68743f0b2f3e3b879d218013befa01b38fc350cfd57b7ae1c4a82d95a097b4bbd6eef7ed4caf2a31f132d32caf43b6c33a4b2827a1a5ba74fa37a0042d58fc2c96e95f773f2ab80ab99b792bbafc3cfabb393bb4f55788919977adde306d6ea15d8ddb5e5f9b2ec4c01b390d07ac64dc972ce9554e99c85b1039dd55e1349d24315f0d39a178e64ec874544cbb3550be6e9827b444c60411745efbc3e59efc850b74a47ffc601d1e4eb9871e262a98f11404e1c7cf3aa63ea3e523b78d4ddada02b27904eb0603efe611f3bbfb52201cc3e978c0d0296a96fcbc17cc9f6ab8e953e2de44a581c1b7de258327e479713af4e6a28c3d351ddf165fd496d2d433cf8e42acf6c768cdaac0974d9b7c6e773e120a52ad3b353069359034c9f13ad4ac5718049145f704ac2ff5d5a6a42fc876315951a78eb689247a468a165a8ca2e82b57a84979c2fa8be7883a48d5a9fbea280a901c579847014a579c8494069d728cd0e2c77876e489575b70c35e34f4e0542f8328092a11e1d95784293a836b0f2507f8d2a90323448f3ef4863d59f0fa2f8800bfba4428bd48378321c3e5dae7a3a570fa7a69d5928dd93cb5567b68c5e5d47ad876291ef6a106ff47c67b8642f79fc2393d2d76846a793d1697a3d84166130815f700a6c4a9240194d746ecc6c05e1ba6602c12267c21a9142d3aed06de7c125281e19a495f4382bcb7033ee914732335acd568d886c39380f360bd0cdfade01c37aee838cab8423bbcfe7115ba262e37c425a6b7e09424e58ecaf7ed32d84478e77ce93f634e15df3ba4f22c7d4ceee80ef2368011e2454c071d0f6355af124102d83e9cce5cb02441442bcfafec9b5b89cc59ce30b8d0c54d11f8416d0296d379e5f54ae8e5f12daa74eb7530abd54fb0f81e05b0dc74aec2de61b032da7cb4db05ebbae35b3158e2439b09c68bb155deb8c3494c96c1f22ae5e29d8a798369b02421c7281224cd56ea18ec5746d5234bafe2d7eeacb87c1459ab8a15f546d32a0bd8592e20eedc7a1c6951de0675f8c285c9d1aad07b64467bb951ad57b0aad251a4e2ba4e78ecb0350ed051467273ec17332c7e034a2b62116ffd2474c5a1d1eaa2982df59ca0540707dd4c38f50195f75c4502af16acb33a2025970e77333ef8260b1b6e1cc8bd67bc16e27e666c47d9141edc623760fa6b0978f3ed083cc990454c0b7e3a08005f2c099ef75956eea5b991b317202b44b06bf6d0c07436e449090866f71819994968403d769bb2388e15187a8eeda1d09e9d4af887a95e8a6d8f8f1cfe4946e924d554dd67faa4be1185abdc0a579efb2aa93515dab3dab13ee3a3bf25b9edd5856ac66d5389ea675e89000bd61e6396b4d860395d3626f893370a16f136c0d4db3f770c1f00d064839afc860e91e3c42e5cd275527954256790c3302447601a1e879a6935bdfd5fc106a718bf63f12f4d4c334264ebb70868199622b95d0a9095af986f495dd5f411730055f1a41f5949673d1f547676e2459aae282d75d499edb44e5d96678600ac118b11aa5d82fed118949b3af06ca9339f09dbfbada5442410cd056256152086f473a82c31a7dd49ba06584c3c7af8d1a564a67436df0347ed26f42d7372b12dd432c6034a6d06b5b4e15e741d6372be05d75a9787b82f32124ce81d8dc4b090dc836090b698a171b99e7d1145f6098ad72ee2194700eaba89cac1405cb83cb71d5e975349a077cb42790a16bdd6fca6e16852bdc03b17bec84a618f4436e8f9c8426efa4d6c75289f81fb81d27ae1949614f2844c8855314e5754a3460820e14c1325dbc47f19e8dd0c4103ff4c6d9d1f425557262c273b882aefc55935416533440564a7bf056548c46668ebffc24b537ddfc4ff564327b06c3d100b43d296a50801ca04dc9dad31dfd14d2955865902c530f17f3a33a40345429b979a4dfc2edb3913fdeb0b29311f9ede34f848b07249ef7ac210e58186625a0902a1688dc0a42cb700ef73299ff84cae0e567cbf58cb24a2d4a0b610c8a38889fac13e545db41077c55c4e322758f50751333196b18796e984cda305e16baca79e42211b2a7c40b429933aa2889c4e4417287229845aef8a5a44a68075f329be3322ce7ab4fd492d34c35ca1de005adec462d04eb20b306544f1bd532b4b4c5b6aa4b78ea1fa6dd226aa9625eab35777e9ea527fb46f3e4d142157c46db65df8e2ff87de8e94452546889d9cc659e021569e40b8daf3659e5b6070d49b72f36ad6a81751d9d83656a42044317c9970fcca54fe27f1149869c345105f0af492f63e5eb31ce545cb8a87ebd3466cb64909ddf0b2fc50e224dca3cde964c5de78006de3591b9deeacf484fe16458737297235d16d8e7cea2254590cd3291a8447c90ad10360c8daef55e052a4841818c8255d5b6679ae77d4d1511dc2d0a21d50bf29f0b10172377125f9e9c2dbdc6032c49bc3810b57842b1015650f529e480c9c17abd60afa169feb205720dd601c12b248c60fc2194201544a93c314d0841f212f54f42b37dff51e87dc387810c85fb0f62ce4d0dbf91df6d44cf9a92317ef375ee63f3960a5cdee3dc7c40e2a8bd14b0a825c41e227c34d6497767d9c943ee080f58fcba41ec2f6ee71220d86c7304ea8a5416a8fd50fc08241f01bf57cabb9cc313e093f9a426e04b8c5ebb21d7ca59c1e67700c61e3da989d9af2e7c1dfd61402e09ce8e4cc4b853c5fef7af5397192610a31bee76b1749d289d31b15802c3e1bc0074fa1c004459f1b274d899000f82031c200c8359788067fda4db9028490181fb519252341581a893e11b0efec22539c268302c6c0fe346aaa2658d66c60d27ccec55810719911e84ea4f13c998c2c4d786072b76b06e985d80d257045e9a090a901d3aba994b9846747e86cf48f351ac4da7cc503b3a91c6d2da776bfc151f5f95b8ecbc13a8c99296d6e3cbba92ce5dfae2af480a5bf703794e3e35546c1ac406c9396487870dc0de299d6af88f583cd86fa9fbf4e59bfc64f62645fa4444e08366669fbf2044876ea50d62ac4f841fbf8953ecadd3cf8e85938ac91182f91591829d8eeb76a64513926153a85764a49e04f7c0dafcbf351407d9f10e8bcd6dd36a625dab6800e83b45f79a4774cea6a81e47c645c60a89006dc46553cb567db46cd716b2d880994a3043dbbdc7467271cc909ca1a632eb1f56b53ed0c82c5711288053b2b86f993ce9dff961c50f64b4f1a870bb5524893691ca40cde022a7cdc47388f2786f26280e0875d77d6d2c9a4bca0729cbbed9744794c8094a5be5958071f600661c407cb9d59eb78c28a05f1bb4ab548a65e31ebaa38856c1ee0b690764788f6db929bf1bac76de3071375827d49136943ee968caa7433b02fb47075910dcc215ba8fb3f5e65ae9133e84cc9a537e9aea8f0e23c1a5c1c4276d405cda705a2ff7d3ea2d99167deedd2e84ec9bfe185eadc6fa9cdeddb514a9598f53f83cde95cd0d4ce4b3fa7701d11f3fccf214414a866df5c4a0f4ca3ede8a7829e1088788f2cc3567fd107e5c976b32b9b574c2003d5c027c65018678eb96146b6356dd8611992323891045ef1f139bb4eeb4a886694b8db94fd048059972fcaf938dbae4590c6a87e2269934164bb7aa044b3c9d65b40920de29948c0946253fbaae30ed814e1348925b964907b39d7096f4834ca91c36ce7be54f0f5dcd5011207cfb02d1b15952d5a1563a9df481e7f38f365999caa024af67ceefb4c814d94c946743269e864672dff47f0bb4466461fd83385639f6dfd42539308a6de6366cd2e06bf13f39d43b2afb07388b5055aa3428716b131fd1d38b194798ace8b2177e86913fa8a188d7b4d41c891801cd0d51e075b559ee4b18934fc7b495f61e5de9e48da2e249a9e1d9af515baa371fb64ceca02b8f05375c8828d5f036523c35b0e8970cdcb469eb14371931c42b1f2cc02594c59c58b91e283ea2362b76c1adf0cac6ffb01c8b791b10e1e45229bb88cf5ae01ba1e8d0dadf016752244df78a866eb714df06cb9b3c0ae88ff19846448f3726b9dd48b26c5fd6ceb571c8af1eb4cf95d1b7e20740edb8df49dd22a097567cb60d30b26b5ed54753c9248a07bb04e0ccd26e1753e21d1148b5bde664245869d2622fe015f0e3067c0f74b3819cf6902b36e0050d6a815246787a38094683c4f8bc2e5129b34e718f0533ca061d140caffa148186c53fdf05064169b7fecd55d5f6a15473304c96911a72094df089891104e908b50af387f9c2ee1f759e0df54cc6bd4a914c4407e55b9235095f544a992618d0591820693fc19b843dca52942924d1592a5e2a5013d3b8d4243a46b781718d2bc2effa92fdbf52088c31078a9bb98e808d9a5439383739ca3b353186f974e0ab773d994154626e24813446065c9c85e44aac81201594533d3eac8b3a2dd016864244b4a2330f97dd48d4d6a5ba267d11ecdaac715d79486ba2152e857fd297801f06ce0191e74eac3ab6e74018e938c26514207e5d9287a6bfffc68561cdd899e8259bc998802a98ee7109f5e58a78073d10611ae4d2ba89e89b816cf6b4f2c1cc4261ecd5f71cac108494490fe5b38556bfecfc9dbdee3950784ace1d7e5aed9c0e8712f6aacb721aef49929b9a1c48c6ffb0bd1d0c8f66ca57d88bb8fa6bcb2ac5b56cebfcaab3f42208dd353e4c8cbc6b60ddc6a6c23e31cbd76d26c6b25a5be368a5d1e645315354158b223ff6388bb248975deb77a5f4ca4716c277cb5ac67adb0a3e46738f41c65a44c548f2cc59d0c54a3e6b4eaa3ac4d9868f489e327b7a37cf797c19f221710baff037fcea9fff6f38ff70a380bbd294352ec6cdff27446780de966cb527be40b651aa9b028c190a63cf1e584b25018a1a721912e5f93799e39672b858a70e70102f76729c0036471a91e6e11c71eca7600374c324af4eb8e838c84c3a0d7f6ebed987052fab42df9868443813fcf0eac0dfa11fe0b5c8f95d8f4dd913f41f696f0077369d3e1f0be7ffabf838140865b7b41c588bed674baffea3c10d6657175f910784c6664d302ea36e36a9142978b9255b7b9a62965579a0e6a1a4a7f90c08de1a29ef539667f106d546091408392a233072d7c1669c7924cbc47d69bc2f2e1f71e137d8c11246ead5243f7f1c7f93e7d4f7762c23991855781bd3082af840ba9e41706927261c85ad1baf6b229f0f23ce467d3ef581432a8fb0ebd69cdb95f883ab3778db8f2da1c461488022c3ff9b84bc15e088ca58b428eb720568929dc60e5a098ab0b4d78aec8574bdfd78d098627e0e1e880121f4dd71ca012d82f06817b5e5d60b8c587e8638cc0a3d19d191bd4a99b6aa8d58a0082f38fcd2764ebb94e4c82b145fe828ec8f6514d841c5427705f756b9c8b4fca199da03a7b51d85d93b938dcbc438e190795306b335d1c864d15177a1d88dd6e5cbe6d8688262c6aab1c20404bd19df49614d37657e6745e7c3ca214dfb3a1f9ee5c0534fa9525cd0ec989c35c937363fc5503662db702a1c863ada65c9e7e10ad37733488d44de39b98837f484736261e4d3801b390707808f1d956df13715a5016a054a72d4295ba43e6f737272a321d3e64b9e8068aa43204aab69866737d842774d73a91581c3f7d953da90f2b51ece2eb907d4419b47e2d460e89e6a992bb786042dacd9ae7cbf99a025268adec398cf89445cfb746662ea2b53e17d70368cd2e7ec7e6ea5d0076253337496bf9c1e17f29c0e5c8d555728fa8f3d231796cab8f414092bc65aebba6dda76861bda60e93dd6c061e0d4a805b19ef9317f00d916b616a971334854fafdfab54e953f12a6cf4b7cf873d3b20e502081b4c076b049651f8a1754bb9b9a80e12333907343dba98022181d14d9ac4fda10bedb611ea2d05c28650864c44fc130d5b0f4ef1ecc83c0b9d87b2ed8444d6063656800a547f992853fa2380782d36660200d820886643462a2579fcbe46bea4c4c8ce6205f56ee15f87c8b645c5fa1bdd2b721d0c08bc25875c17c60961f3a7ddfa12e5d868c22cee516cd4cb26390d00d626228f5152b268e80864873317a7d001ae52d194d1a09a125c221534d3cbd81fe30a618ee0e5bca2e1fee789769de0047373853e91e0e461e0ce38433c2c562e8fee0e79ef6aa954f3644d0662361cb0b40a4a0c59b6b5cbf39023b5e7656d6f94f7d2b16009a19eb9400b602152ea4f8cc9514ae154dede0052abb7666861489cc401bb487fd5c63ae8c2da7317532bbf208da5a0f7d779b7721ecb0f22dae2880e812762a6a21a00ed40a42e8b906d120ae4302728cebec015ed081f1e2b412fa7e5f3c624ff8ba96703dde2f2b300e8caf58288bd4fb66a0ba2e75dd8f24fae01b82420641fa4d4bdf61e2f80fc51d85cbd872c6f2eb6cb932186cd8c634d48929738f381014e360b01984053d6c8700f9c40beaa46c0186049048391246150c4fc5776e0dd1685913f876a14124ea3280ce41ac885f205bf5754909dce69a9681cdf8fc43a59d071449c4708556955e61e0fafc727daaf464f0bf2ffbed71a56812208298bca0d32724cd363474239616c62764dc8a36082089477e6a584aa99ace487df55f658fa36df53b444f98821aa1fe7b3054f117f9a26fee2d678c2ac710f0ba9fa45362c71ba9d509cebdd7c0b31ffe50e407ffcecf9077502f8c1c4c8b27f89d356342db4443387a44bcb2ea34e94e9578ca9b84706be965f364d8124884787cc6592844d1e1d3da18781534c218a5c14dee97b44871c0bfbe9675637b84efbded2b0902dd93a797895979142e6ca082aac7bb2141183fa1f69e1498221fd16492d05a8c96bb29ae86fa0d79593aad728d6788ba3626e29114417d28d309382837945cb88cfcfed8e88a956be32d464da7e8178ce091812726982b7a308e86e4ebe1b12c5eca3f67bc857015f6dd9e79f6459b1e022ada595af42f490ef02459f433399777c34090e9da804c969a50c85ea45a1453ba07e7c360ba62078c9d04f5b7248ca6af097dfe9f8e14f10ea5922f0a9df9509d7e7f4bdfe2d091cfa22d121e55bc1efff97778705844ec00667665d65b2ebcf861770135a35cdb4099c0d256884c0953725e80e853f03962587311d03e11201947022ec945394c2b4be7540cc5791bffd0b65cded6844c00d2fd37b26c780a704569ab7c95bb8bf25dd504b849afc11d96135924bbb9e54ff02c9395bc90eaa8fb5cb13722cad7aab13bb92a2171e6ae880b9b327b69c87a10950f3165eb07cfe5b4f330a0a6b65daea65305034af63383d19221b50cae44afef6d26c23760142fb53dd1589413542a5eb4724abc2c3e7c4decb14d2fdd059974fd18d048ef80709df57acc5bace9fb184db664af0e350424215fa2550695b04bfef8a58cd4a3060161aca3ff763d84fc64ef27bdcfc43f975980b4e26f130fd1f8722e8133e641ddefe1dc7ae8677d6de31e20bdeb7f0418ebf6c6109384e18d997c4db27a3adcf611e78fd7e31ef61c7feceac6fa88b62ed893f7b1a26eaeb1e157985f669a283b051446312b6876265baca2faec358fb1220762dea2724a03bdc85b659b8f9620a7fdffa07e6c785be3fca2ce990ebe1aeeafa9edc5c92915ccc4ae5ced2f986abba9707a41a42381f1c80d025fcb38a901ce1ac2e93baf0897e8b4ff5a3311a847d1795169f785690ff28fb201cc20059d88f101ae0f6d32343c4c20bc3f8f82a7a30924051041f02c165e119b07cdb139dcf99c7ea5f1588c4bd93a9d830feb0e8e027ace639ce840edbfc0593258503632400f84f7774c691f0cb502667ef20d6830d797f1087d54c5b698e915e0fa2721e4a7c239b2201b917d91715bb8d0d42183b708d2d826d8011ed0930a8572487106d3ec9f687e418a86cab39b66ce9f474f03ce9edcaa4acc86843d163ac9363d8983490ae860392803adfb1fd8a90743930239eb8ae37dc1b25acb1b67eb0294f4a6ad94e8a9686bbeed08190c40242b77160f769f49dd0320d9eaeeda070851f71e737a0ca897ab832fd4495e77d1a74b0e21b7c9f8992e8eacbf1957f7fffb3053500ad8eda85ded6fb2a79d67dba4e19b64214c0148fbd0a444d185421f844ae04bd8a285ff4c24edb5692785215324b6ad9fc3f9677b772d86d6c844329f3e9ace8b3d1eeb7c8957c3abb6ce8d380178bb41b8c84f0d5d136d5daf00e17aee210514d994a0b8d9dc614a72204b2d4fc115217f2f950c79cce03843cfa0bb406ab26c2c08842990f106abf8208732a7be0818da973d12be0f971c61668a8c2eaf313f25a74a78b5dce6e086951987a241c0833d138f1ddafa0cf107fd05200b8b3e6d0705d2b136dc87c298a6247d93fa03b979a86b07ac838fac2857af8bb8a7c1aa5b492f77e01fd0ea00257d12eb6c8b1693b4187343fff30250352871c7ece3540b27e7e22f047c59ea90b51659f8dd65bcb44cc5b851eceacde741836755cd7a436e72fe7709727dfccf10ca0d0cf59db85d15fb8876b728a593060498c8321b5d965ed3c4572859254407e7e903eff01dcf4a9db9773c5576a6a84abf054866f66add9e5b889f00b4fd489336c7942bb501d8740cc32a9a7a303010219057dc0c1e2c12b4b0eb448c337d03abc0f21292e0549c193a37901218a8af6d5ea6dcf8175fa39bc26866b06b8201fe95dbd97ca2b3d503ef7199c1a7b8b88dc40517b7e305622e7d318a713998e1b92e8ebc2122a149488fa8ce613afd741b38416140da3d699c936d9b02b2c72ad8608142848547f63af45f86687e53d006533d2f60d5f6c740f7fa368cb83f8dd7d2b5b67c7577f3f343ec1226c0426ba482b48d2a878d77444898b7a2931590d4eeeb7d979890789e8c2aeaae13f0b19fb7184e0de2ef5195024a8712e4e714b2190cc5afb086c30916f756c6b2ae8a9b5dfc803f4bd5f25656e7de6829dd012c05de384d02a8c9f588ef0e519177a029aa1d9b03aa4410a1e5cce07d35852c777c73bbb79967523844b99872780f2226b6bcfbd3b453c4feeff251d76accfb08cc6c2485bb8fb87f3e6245496577cd01e86f5e1705e14439425065a0d2a6d013f4bd0500c49697d59820b27fe3a402193f7b928b87c593696ac0818d15dc7f4f397e6893837fc04471401532cf75b8af5213fa785b1040e2fcc0f51f76026ac24a083dfa2d7e4629ef3db936318cef606fa85929ab20722dd36e7431526636e549de69f9f4ae7345fb5c974f2aefe399e20a5ff6a6e594440e4efa04b0af0ea8931741d41c56d131fe529bdd89a122e535369b0f6b2c982e0f7e36d5daf99ab6ce55332b582c7ad46794de5adda8d286997268bf61331670c43ffb372cc67e7a0968ee3a87fca1026194fad16d5ec2a2b1eacc417008963b28405d8b74fdd025c7f1b66639b71f9357bf37fc82dbbcf5a92f20783c103e79461845863a228cc7ed2f581ac85c8ac071fb30c68241958abc58f7651d837926b9024dbe6bf46ee3888141c76eb21b19d23798e6df4429e15ec579dd55ae770a0fff6e0b11aa4dd465af8c59b3f712059e28c370112ce6868f3e7338aecf730addc1468aeadc9e1d5fd3eb56c94aa3ad133452e8d9551a602a9b71a4306cdcdec253e912a973454e909f5c84a52bf2138d4b037f2539be5024ca482341edfa144b3b806108fbeff6b3402fc8a538cf777608d133672d090726c29e1bbb65eca48698b9eb30b0ecc1e26be58980f0ae249476014db9e5eced996aa8cd847388f4553ab0ab5a2d515307c553cf37c893801ba43e20ce1f8cd6e7dc49a0108b12f2d55e9276a514023765c58a344a6cb580b73c7dc8f43decb904276c0dec0411ad8c13f816b17dbc6c899193478060bf1d8f7dfa3cc488a56bd4fdfed50583adb59ec55ce1be0bfd27841cef33d0481eee83a6f4fc3910750fc3718b1e19c88839e803ad843d17851768beb2c52cfa0ac2982ca6ce78c7dbd45055311c5d1ee7f17d3b11ad872ddf4a8badd36aeecb8237c45c876349ef05445980433da4aba0dc234395f01373fe61030bc257d156eef05ad7be288fe27c3321e9f4c2d28df81b69a0fb4510916c7b0d049f215750d7b25866a2fbd5ab72869de0241b1602ab26e582f57495f5fa031c887ea0005b137d585280bf5eab8a20e6a58ee40d9c6c1787fbebffb4e6d3272e8a79e6af82e7b8787839c476440c51848d131e5401b20fd9e8a146a98ffd99e9b7ff807aa37412ca8b2b8cd07903095d478f99e91698e0e62daa2055565cc2977b805d2155799b98a7b1c4a35c62596d425fba628e2647d16e99a3287e6fe24b38f7dea4f96c1e3d898cbd489b243ce45b27b6e696752bfd8ec24fc509c9d1ea9e80d9814922da123fec79d17f93eeabd97e894ccab0c9ad9de394466aef028ed8f05a8f730b87e5e0c63f400f55aac1d441fa5db965005dd7c780fd37dca8d233c97536369c91ae6f384c2d3015e49a9a56720126ae1f15b2fe19d29e1309c09f2b3f443227307133eb5d0411283836137138ec5a472f62e88ebf58991beba751f8f7f709bae30c4e6cbe0f6185ebc7bfbee4bde8d3f33183df6cb993d13b459a4eb9c0ff47c28fffae96b63f35d2cef3d1567a3afc3740fcbcc56aa6cbe6ac87a0b6823db82a61a14d21d13c8ff0a91a161adefd721e12b40949e81ba9b99e852212ddb81484b7160eb85e098427ff1e6edab2f6f9b31bc60da3b417a48e59ae81c821c3bc69dfe168726aec8ac69e4f686358135fa328459232dbbc080d7ee7bcebb4fb6da3ed48879043b4512ec538f3c4d47bdf452b731e4e2464cb1dfb0df14e189b11162e0e7801bd4960e168d12f07023b4eee17e8f875e7056209e24f06c4411f606c3a633e709dd90b6c0b4b9f065006ea17301e8215c7bc8e99f474dff647e568e4c454cd7e49dd0998abf7d1ef5c3b6acf04ab2a748028224a42816058d91c6734c63a051039de32bb8da42f86d508c63ea4d8475941f107bc904320bbf0e2df6df4646fff0787df0a700d54984c76665c0595ef314a6385f038c2b992fb08f1218c8458af6487082d5792d7d12676546928e6388f0221dd0f9870f6e0111ebc6c30ad3ba3d1110d2f4d5759cdf8d53f791358ce9c4581ad29d7e2c74fcc76fd5bb54270bb714725ace726348ee702f9f98028d83a878645e2e336a9c76b38034d1b009e02ea078b4dd03107e531e150600eeafb9f432bae8844c89d53c8bbc21a6101efee6739b1af29de357d901fd57270ea37b6e8d3a06d930f25eaab920960501b0196ba8a084389199ce8712be20e05c4ced43d2a46e57c51b94d41108d27e3815ecd36e71ad000b5eec40b23bc12fed2e06caf5640ba521d092919cff327554fa47dcc5f42bbaeda684c38b1709bad76a39a2556c7b9b044db073ed7d32a7e57bf46568899549b082efe4bc70235c747fb008e005c0703ad94303c02f8f5fce1d19c57d016be07d964c55f0390cd263fbabb378029e7896039c9d970bd5ebdf8884fb12a4b5270beabc6e53e2ae7b0ab3ea209124665452f9918aba24b6c6ed0ac43ee8057a203db115044460d0faf6e5b4d4c10dddb3032e10d879e3f179be29990eb4471de6c725e9fccad2bd6a2f521ef32feaa7185184535dc0ca16cb6717c4c0e108695cf0474a72600c75b13700e3433e030713a282176f8c4ccf6198d3ce3648c41c2f80685c745915f7caf5af8b94159083082bc52df976d4c0d3f16885afe1ce8c6de879a7a04101c58e0214abd09a739abee6c244e3b049b9eacca053f5197f4fb879ed328294bb494b7ed625e14dbe55678375bfda5a765512a49f8c7e85afdeb96b065c919434825b04ccac6fc54c79b11b6180ed42b5d384792154e64139b6cb1047187b7e1f900c384e9f7cf2e3b07c0fc9f612bbbba2117f38a395e644404f57d6308393746b0b05c1f7d9af90e8970a71b0046eaa6ee2e79447b0ae56c64a61a67612ce9a9f24308dbd469122926f87eb3b0c6e1ba046e24de3220788a4c49f30f80db9fc1aea7a3af969d45c04c013aec3473cb2a28afdeb9a11fc4f24192fbbf2f6232ae96d48da710042dc667b80e00db0084f8a3d6851ef87e31f50549be6025b77c81c559bd2fb1c8937972df66cfdc26557fc6cc59848d4f7c3f4bc5ff2973032540bca57a5bbb1474ca503549ed1ca28a5cc102d3ae0f23d5fc10287f7a8584390ef88b854fe049b2cd2f467283ddef88eb07700924656fba41dc62db25adcaca4fb7be3421bce46bc204ecb16c984c94f446db868cb43b08e95e4785554cfc560288e67158b54d0099599c976aa616f3861258eea492047f4edaeeffca570e6cb2d7dfb4b3e32ba842a04bc0259148466a1529733216a4f09129cf891909bec85762d2384bd1ad831e399231be6b938cfe5b5a9f767212fa6366d9de26fc79e284b99e64416227db5e724f5651f16e363468ac9a8bda0315657923479f14a345fb87cd2a520de7ed0f08c2190fd7f87d7249f1f0227dab55bf393807b62db56c3ca4af2a8a525036ff2afbb485dcf7683dfae4b2dd606c9b049ecafac150bd25c8b1d84cb4cb9148b967d0435786fd4e6e6f62820e0aad379ea8de4307a726eaaf69f4b67d6b388300463f96277946ee0330e61fb114ef805e70ce04251292ce5764e26487365fd0415b97df18a6264df849173f0293a4973e815d3d676332d01c08aa5867ff20741f02d137ba15decaed586f8d093cf2248df97a9a871510c6202ca69742ba91324688e60586abc9ba05e97dc2f65d659062e7a503275a9ccbb657b984034b80a0daf96625ebf306a80ec3f1ca7ee1e506dddac77597378aae12400e89ad954751bb2fc40b36d5cf90f160eac059f896781a4cae44334448b6bc180d50515b815b177bd31ed7a6b15d83ea767f6597b24b4c24d5e9d1cc8fcb69c1622460152c01b52ed1451a1e32607f7b0ef816618dec9395d3c5607869d68917e270e6da8c6de621894862e5d81ed20a71fb18f40c037c20851360953f9ba47995590a2a949b00ae139dc51ec03e78f6cc09374a935ae6fec37c0a61048fae488e0e002510a622dec4bac6590560043ef64442e81b9cdd3083dfe437e4db8bf521153817d0821a3962a330d738af7752e246ed9f6012a09119ee3d781b3900983d3ec820470410f58a4c9f5e89665e624ca0aa0b3a5f899bcffd56bbcc59b59dd53fbe04043532514b53af573f50f15a69c524530c871fa251a2cbd9b941d5150f59410bd633c7429fd11c411bd2c493fa87d904c3f36173ca854fb98fb7f0c179b93893911b40f6f950518f901091bf1130db7559db9ecf3edd517027a68f490dbe68a7f7a074a0cca3c12b63ebfd78a72848ab6029a25b079120b21c781cb229696e7db0d84639d277baa9310318f8fbe4d3b2e9bb6156f7e938980d191f8d72269426d208fa9394a232318eec44098b743de030bc8058840514b347c0a562cc259e0e33f4a24801dea399e49249891a05aef9300fefae5ec8a613deb2c390026df861be0ff854e12280178a24b4a70f7a6c8bcf38209a3cc050c4348fa47a555818c81e29c0c44004e6e89955302f7c37f77f59412c2947d0915ea2b065dea8b2fa63181f98d2a65ffdbc1bf5702580b000475ea72908d0818208fb707b5d30986394ac55ca850b00e823761c5f48759ac2a9e584829f964f0fb1d133eb3573978120ee180d0fe14c53062e7c8ec5e952c06a8228f41da6041b1c76b571563d3eee589756ab2ba22485040458671f9274aeeddf5cfe8c0596c0c7b835d961419c7a1616d79950dd64d6c370a0e0fd952366a2feddc168f467517dea65f78f909c7b36f3840d73366f1267304e5602679ca007ebee2f93fa91270464681cce31a009b39a00e3e5102b5bc6539dc287d2e7c8d75c52f092fd2df53ea672bf1960d53bf73571cb45be308fa7ec01be56d22b971c90e4a851a82b139cc3bc04f4f7e6ac40c32132d78f5774de37a88f542093593ca734da6e1df2962c80421a9abc0c8671c66e0685592f938016c7aae1619f5e34440a72536d327d6041d07156036c956dea52819b1b019e9cec9104dcd3114faa798ae494bb1373da2bc9ab65ffe0e8056bf1f39df4e115210f727ae92fd0c5281d21324f9c91dc0e5b4fe01e24906a1e533ceccddad3cbeb9666fb9e892026aab550b156ad3903b6f481f206d6f5b677334faf89cf3af4bcaa57aa2919893853033bd257dddc0d3590f5a63fc998013410936a194abccccc514ae3e1cc0e49d040213f35bb368d2e7732b0cd3280cfec592960b7d33c9ddf868d1b6b2a22204bad009c8e198c3cf8063867a61d1299fc1c5d6a857ba9928332bf1feb5a349d0c5a4b7e15d9fb24c1b902e5e32ab21ffff3e71a71a426c88a647fdba22bf7950b1e34085f2237d4fa10a44ba4ec025bdb381009024dde6f89f55fbe8d9d934b93687896845ec42d48ec8aee0e0ae2ddf26c35d2b56134af746cede1180293e570c99afd7db2d4faec5f44c2092620f00724f6bce34d9114ff00006124d6fc41d0d5277d2c6b24be0d447e100f7459a76c09e56d03f88aba57d5c84f5082c059d0fac4e4728f16df28362a57f466f0445c34d2f9383e8ea61f23bc1cccbd8aad35dd074ca7e788dc1fb9eac3c8f98d900a4a58b5d9228257382d62529b12fa7175ad2d9b57aec0392d849e0579107fb52276dabc79c84a989889477b1b8f92438211d5781605a5a7bbbddfd2ec19ab6054a8f2975114661e0619b0b6a74c9540c5d84ca66e5890431b778eacfdf4b204594cda0f76699a5957092dcf86413297929729b60e2fa5971af32da7f0f7ff867cafbd8c495352185b0d56b13c7f8052e53fa484c540ec5ef6d02af9f39792b621ede648514030b0636b0bf6936c22abe5b895e6c0c1a8666735606404176c6411241aba2643a1a4ae344bda33700fefca3674f40490c8aa856ac4876decc7d6866be0a10df353f8278f76269860eb2165160d5cc4f51d592b49573a766e267db98c98259c38e99be4d4e069bad0bdab0c5ed49ca446ed0880f60817c444c81b67cb59ebd4fd3cf3767c617013fdd66f16474dbc27a7bcef4640ac240ba94fc4d62b44a5b2338054d11aeff16de94167918f7ffb1bd2320a1be4deb9da54249c505182ab63784024e71b2b4533d9429d481449a64348bd4c40ab9d43b1754db524fd213ac025d5c60e86cac0414fa3518fb911b2e0e6ea0fc141dd48a40a99890b0c11f121d9b0510aeafdf6c442576ea9aa4fe75a74090ffda1409693117197eb603ba3268720d861e99de25445a24558639b3ad3f30f72bfa52af3a85d94cebc78283836e37d41e6f8b3151601e19bef54c19be0c790409f0aa4580349bdb7c4697c7c23fa02ba7c6e2fba2851644e3d14f4ce276e443f310833e053844e3f056d1f0cccbad37c326bd3948ff9a72c1c0f8860c094a209640c12b51dbc8e0801543828b0ae3ab100cc7f0688156c62812a6c6536e0a78208474d22cc4ed146c08bda09c18b5e24266580d63dd12c1fee0f92f80f0781d460586fc98e8662d42e1d52919a8595d120578e73b95f2558ea9758e2ab4d4066fa7175af6b01a08d444c98123de5e4202ec6540082d87c6d5555bb6d55c21120d48c0f58e5d22d32dcf3dc7a13c8ee729c0c97c955eab71ba525702a5b8faf0bfa5689bf4b7113756aa8bdafe09d012491b8efa1c5fcb53e69ba0b12fca935e679d306231c2444ec0006c7a264ed9349ced4a3117bec5315a8c1d1ba0b42b1216d33dd22da1ad4ddbc8f4ed20cfe4d5f30233dcb365267e9163de164851b93d9b6225e55339559687df96c154f215e4561ed6b01bfa19d40b8843e5299841a612868483939dad8fa41a794a4afd088a1f9be5de43f22fed829bd03a6d30e72a63a2384c4f5373e2b66ec08b26b659b962065805646ea8f8a62813c096cf2c0c70e1bd52a14cfa9613a58edf3b1e7267636230a378ec11bab88ca1cea42313175fda7b3e0c108bdc16776ad89659a8bce61edb2612cb75b44be96cae6996d1902438e7c12190be8797fa49af2b37bea082b7105079f748550961008ee978bb42ba386c31aa85e2bf7ae0ff6bf8e6e37ac4063dad3582656d1abcb34e44485910d99f88d7645bd9fed3468285806245a710aa0408d60211ea7f80cde31dbdc249aff727a0ef08dff5ffcb4194320f11fdb6e1ec7a40eee55fa8d8e83151311ddc708bfda4c5a76fc3006a3705d6da54ac22ad8a92ef82cbd082d4b264404ffef33acd587c8b1633a97c49345590ea856bb77c6b087bb641c77345b6dadea3dc55f07aeea10de192c4a379b294d616ac983586592064a13747d88784462bc23a7637ad56c244e82d9548aa31e770bffc82fcac357ea9dde4f094acdf6aa6bb58e548b2079a289d7845721208fbd9681955d77400d9eccc127bdef9d3cdfc9dbcc6e73c527445ac90e6d0df424b5c8adcb08e1caff7159e664e538232aea7efafa66a86a62454d194fc43ebecfaa783631ae03402c856bb054b755611f6d45c288927c355b17ec9674d60e7b32cd15e3b7fc26279b22ce9ba10d4eb6a06454c6d0bf46efe84d4328ccb2373c9c5b05996bf9085300ba92c07c6a8c05e2106c32ee7573bb0f1b52fa6f906aec32f59ef19c6cb4965366897e8bd2837ab834d5161143ea14f5dd225719ac346f26439242433c3b9c8d0ea34195cbaa9066ffb9b40a361a89ad2225b7251d928e06e7b90affc27a4fb3191f8f741decc4476dd3abc4a930d008fb2858a21a5c8f44f4289a0888d039e0037934e20f9b9514fa33324f250a3ab194beb2108113394eefd433c32d2c167c5911e07575f179a3bec163f5c72bffc1b38c388e43aa393379efd1bdafc104762f30588f334f4a14f32e80e8bb9f82871c75fc4b1d2f5e40594b87e22fed7296188409ce398f4091779d7b6beed3d4fc6ab615d856affbdf07055a8d924f72763b07439cb64bcdb8b49c6be3645adf840250ce8c6d200b21886f67bba292ee2b04341564452d810967bc3d702da153c15777092d71864fbc058f71038752288fe397c1d6d56af8a71f7a35398c3c1ec194cd3b5b36b88b0576eefb563cbe7ed7d73e2fd6ef455b6c17ff2047ce1c3faa3998ff1152d8c0bfe810eb9118fefbc1d751bc72530ef0122cad04f268546611f65d6f481053aa455647f2aea032efa3e9367c04a9ae1905843628ce30c8b8f30bb3121b9efd5cf3ee928c3d7b216846ede222bcdb718b4d16ebed30c180e69fa6e292c26a608573683cc006508550d6f29136b088ee18eed0603ce44d7156efc1058e83b70611bd8b4f65069607dc4191c8f64a750433552ce294348c5c81c0e137e8d522449a79a9dd48495b47493f8d3a928d52a358cb1522672e4e98b86f19e110fdb9e544cc6313a65bb9c004077a72923da965e01fd5c870494016525810fc3e2c32658570fa8cb8c03318b467dd5ac2b4a3b5599edc913f66962ff579f69c6636a5b6a9e70071730b5120ae2953e64bfa75bf88a567b3e2fa19862f57becce64192bcc9c62e61ad8332e856268e35d5a5169ed6953969b853280c6e2e81367828fdc4554bae24d8c5831e1e814bfe2d16577257ea9371c20ccaf726d6ca0493fb1986281412c3e8e38d71cca6b5318452803fa5563d1341fcb64a8c6b3f204970dd216c7252c3a53aaaff5280ca96888b56588d8a24d2a02e0f4252f779ebc396c6c236d964c8973ed65eb312f7314a97879d326768fe77e54a8e54b97e7f68cf5a81c7f629f851abe353b3dd4667627245183b7405af3f5b71e58697e3f843b2d2df3a895d5bf25a8c98007266a91f4a8ef38b1eb86de730c83b1000beddce103d2523c521a48b581bff16f059295c5b0a405cb32cfecb27879ccc0e1285ca7c1dd44140fffb035f16a118b9ccb46154fa0ecbd1fcab0031ad201f7c136c8350fb408a7cbad9333b9d411115add370a8004bd14e1d356bdc01b881733b2d6eb85409bf7b9fd7f7f235c4ad3bb664536f990bd9011b7ecc5ed6ce09a4e71f8ebf177ab63818e7b0c7f143a07bdc1035dc2595586ec883dff3ad016d33b9cbb318acae20d78b7b57cfe35f32cceef2acc8beb7da0421ad39c4413bb8f2a20898b23133d93ccd50118692cd6a7abaefb79a1d1b2813608fc18cc0931e880f84c03d198ebda048a0a5c4541251688d1cfa679d9bcddaaa26dca93ac27f88738c03f0c47e8c370803e2447483f7c1a344b122b831506ff13c31397871b92383f9721933befa29f396eb0569b91f0eae391f418be85147042ab0b2c4fe28ffbad8559bf336b57f9a64f53a0f925990ce5c8c5c650c37c0d1783c74bad9501b42e3c07075396aa7c2372618cf5de804cfd49b0a73ffcb7f13773d86f6be94dab6f629785f6a32d76f4874390dce5ef45c4e9fa95bda46cd8ae57ec6bb3813139ad20c2a75fa3bd4d2f9b82f321d3e18aca68c29ed88178d69d04645730a633b6bb2a91bbcef7200c62fcbae6eca4fb078201f42da70c646d5e6e54ff5c6c2136766c89355f18e0746fa0787b4a25d8fb70ff7dca8284914f8b3a534caaea29331b7f0c5df8df7fc5e37e182b731711ce82d5b06ec2c204aca85bf8f50f1bb7435ebd395ba8ede9c75ffd4d8234faae9252d275a9ccdbd72d6f0e9aa48c26d32a06a63845d584809903c2adc8d502c3b349b422a8fa54be93ef753c898dcadcc17dc71c348ca91ab960d30a6da24fd63e9a6b2593a0e3f1652f257e3230837210b531c1838c28cf7e57399cb75964a3cdeca54088a08dbed967d96974ae29e52158c4718dac10f69a75d711090f2052e88bbb7ad802eedd6605d86620debc654fdbad8808ac6907616899deffd2e0801f90b7715b95ba4ddee1ce079b974e719343a5ce32215229522b5b60f997e194fc7a2911e7fb4a356716ba8037d13518ab7bc7817770b1347a4c4db226f849c349a5abc1bd0e65ea106a4d23bca1964c3a1d516b763c01c2e036635cbbbd32de5e143d76ad048ef08ecbf5fbd84541bc0ed2295c3e31666efa9f757e6685c3d964606398df39b45c8fae01095c5a0a07f61a0a9bf64e3f71f4975da2279901156e126f1178abc4d9cc36e4b3a54b56ade424ad40bcc3e1871c9f631c5ba57768dac912b4103af5db6f9547b9a1c6840c330e4e9e25120bff8c05fefb1f2843700846cd067cafa52a2eb496d11f1d42549a911045690379ca27778778ca83d04d24b9cc5ee1554d529c283013d1fbfc419cc49945c4d7ebd194310d2008fc6fddd1a61c9b56d9189903c05b9f976eed20fc32adadb484b7fb16b8aeef298ba8b9f73d965a499337caa7064bf074e66fbf2e89aecd9d2904ee7ce3383194582fb222bf5555685e7e3bd17c85eeae7ad9289de6a04d55e33cdbc2597297fd7f67b613a5ff75b4e845cdc95c95898e0cb511f7fd853461f8b5b765e037e1601355caa5d105cc234dafa65ea041580e28b2567a681f211be917da5fe09186c83d7dd27dd98463028de925060ccdedcc2f0b0096d9de59daa015232accc4fe7943aa28727519180945ed061fb63f3ac7877e3017029126f6703c9b93974b0ac34a65a7606afc18e5450fdf848b956080f60f4a74c75a1d98185e3ef95ac176f14dd6fcb76cf579d7ca708b2f12d09f3ecff591770590f3c0f3bbb22e34fba89d6c97e9207c4e0302ddecc59f7254d7b8b774afdf42f41e425d130982c0025c1146ade39e3e7bc1bf26a04e467ef53392193eb69ab96d44f850c772ae9164fbcac93765364746774e4e7fab52595b949f71b39018259e58e7856d62b74e0177cca8a0284e185904b984075e7193e1500c5585d2254889d583a837ac6f8b5caf7babef89347a6158457fce44ebad0b0c0acb5d0a9d70abf8a43ed9cc69e8950616727160da1ba0f8b0c6262be9e59501a133f3b5059aef8718a36685a972794158ae0026262f80c83efb17b573151daea867994cec38dc38b24ef9e5a14bef16d554e2523920ee857a1145a459bc019166efa76b5bd4ec327d1b8649a755e62c97bdc9cdf6827ae34fa39deb5c730f5fcca92869c78c9bbc880607d76f5370ba51effb1cc2abc3c70c38ba01433891112bf2622e502cb9e5a4871b6aee394953f69597210e3021d58c7b837344f4bc738b1e114f2996669e9815a82522d1735e9d69e419ff8475c80f06caef8fdbb6a75d627c2e10c6b9fc4fef7e927c4f0becccf69fac490a7cb035364f8499b5347ed141b0ee03b0ac10a12cfc568e0a31fb6c83d72c31b1a0a361da9ed4af623e45d5e17d22739c1489092ac08e298f51529c809775eb11469e244a0d75a396049efeeb5c400b8b76d7e849342e28128f6783c535c617c92f65323f85b06deddf4c41bbc62b3984120ee4a11c4858a8e3cf0594b8665ce27232631b23b16e956c51f810ed30daf6dce2015baf80a3bb863e6f51482ec02691e26cbb28f9ddb8d564523d46eb4c7dc1225e9dec48a1de7e1db6d96a5658a1d2f1407399fb7ad19f0ed080b0155e4d5dc89c6314f5941d35ffe140e420570ec829df8d06a1bbcd355c25b692c135c978ed23bbaa7d0448a682c5b61d680c05d87a04aaf90bb2348cabf2fcadb6fd7379f50e0de15926e7c8ecc2181995b519768870bb44d8ba8ad7c43dd57478e92cd35e38591ed91943f1c944d867a89902ece344c60423d1f03021269b1988ce5cf3eeb03f9b359999fb4f2d5ef60cfc6b5ba9a716ec12d2747cd6386b39c70b900d1ca9f3fb750e9dbb54cd5388cbfa085faf7abc02381b20849949c89c367d207d6e59bcc16e739a96a9b30d85e488f6f87c7f412d67808f4f20e3aa5708acca6ec6ca76db3498a4262f93244097204cd7163dfa30309b75e37f54e174b9295b6806d574152d73af463c2cab737f08fd1efc94c2cd19349ad905309ce5905832861384eab3c3f3c7942dc4f7185024886228e8689aed41efc2061d0c3444a94f1ddb425c80b8421bd328e4610be3015597b4fef5192e6621f30dcdb42def37c5b636e7bca89fdfc2fd761e823c49dc0d85bd1b624246171bea83efa0ce37fc03c7eaa5960403c804550df02df5004e88e34a73e0a65e88cde61e5b794e85e911dd2a836cafeb23464f7d5b155dbbeed9ff910abbd6288fb2e6b3ce41710a4555c1ad416d7809d38b8d9e15ad6c49dd38b1629519e6e501bf80c5b8f3994e00c1cb659aa7e853307a56e94b006fb42544cd73a25e8fe7fd4f0c0a191e2eb3675e4d2f3cbbdb88cd072e26ecce2e882feebcf0a48e4524d43b7ef36e381435ccbf2aa95a2e14d55ea8c86aa40963a9dbaf80b15441114b9703d24a153c66e9d3ff96a03fc2e69123a8b48f8c9f51a97cc63a2616a76e8a0b7743faf462ce8bc22b711685dad5db33eeb21b6a36627d359a4ec58537c6bcdea90cc9b3566e1449414029752b42471b2916f374cc80a90b3b9d79adcc8bbd5dcaec9c21ac1f841718bd59d1168f7ec75605483bb2f07b3452e52a1ac67bb8f29deb52f5205499ab3a0c4cf41ca51ffb7ed047a552b9c31cc82f0234873c93650021305098b47da3311f6af4aa01ba27193f5f604ca5690eaa54b51611115114901400214d21805522011845ec08ee9567d9141bb5520c1d413b33b5b45684ee0db3c84c3e9b671e00efe01db8033cf0e9e8e80ff1459a6aea30a916ae9a99848134b110f611e4cdaa4591bdb2bbb79452ca2f0427047d03493e8ccc205d76a8c02102098d3668843e5e84595c08f98c3bdcebb416b5238211135ef932b2d022e698c204359a58120314a45ee26c60d4c31d5084f4120707c7de7ba1f0b81707c7e220e0849698982728563cd960e6c30969b0a5807bc02ea615aa0894129e26153db08b875fa73528a95ef237c7125cecc14319f8f63324e33a9acf66d4027be4cd788397fc33d50acb9fc6c06a8f875a9102ffeeb4d674422f1f277700d6a79c576ba5d509cb6eaab5dacb5373128331deb8a7d9d3cfd31048db3446dff781e093d4931396aeaf27aaa7312c4a592c16e7bb022819d48a4fcb538ff3d0d5a1732252e1f403d6033b198d4c483c4966e3928d46ce5e908c24dde021e7793b31831404a968f4e1b97858640987bbfe6d6e121012f127621126a215ef3aad35258d4c376fd354a4cf745a6bdad1cbc7d999d122a6f60445cd29a8cf3aad39c97ae939f1745a6baaaa357569929231259d889c7e80334e28851eba88a78405d554a18063131c9c11080e8ecb35bee0726d277acd4f19ab2d324aa860c248a4bf67ed07bc5a6ba741f8b53a406ac722dc0607406d498ea1da122136701216a54f875b06984308f4efef6045d07e431560ab676363f381df017cd9803920d9e1ba5c598f349edf7b8eccf1c8b0db38873db0966f6085e1e5cd929f5eda74ebaa29d5e8f61300a6db2a04ac3838d7ca75ad5c230faded98d08e097170ec36f2b0636cec189b166638839c10308f9cd4694795c1ac542a8d13a759c279c249a7983e674501cae97362e749140ea11c3f54cc80a4cf66cc154cfa548f6854154345c564d94085146498109d3da795b365429d0b306d3039bbd12c71cebe9c41675396939b4348ce21e60713670ba08aa47c7662e092e38b912e23841300a694a4d3c759e4ec31d567fc9cd9a45d68186e0006a031bf9c484ef2022e0f26cf59e4b480b974be7063562e53670204c00053068a4584933679392b9019154755814da193001cc771dc0c05d89846849c75cc962a3339a032c3f1a509d28d3129a852f419250244a39cb4c9d6755323b4e8bace4befba1f2014c8150d96d0a31a8f850996a49c5046888513a32e525d8694b383998291f3016698b376527166658147f580226d8912c3a9c1acbd902a4ab25435029d0a52591c396138659c2c33c7e9800d2698f33c29608230abd149b5502a42ce0958ab6a8596a9f2c25980106605cc8a2589480d5a44c9c45054176a1930a71475a2e454724ac0849dd5059d21098dc14b07a7f9c38462c204182897dae3c95515adc54987d0254af54985fae0743ab7ae47043bba04b950a35275830b12932e03c6190173a7325d39afce1567032e557a6af2aa31ea0d2727064c2b4aab51b40a08249c5dce8d63ea386b0038c846c07305a8a80c0c2118784be7923ce832b47050a78ca14c48638fd187a55c08bb00e0e50ed8bfcf6d0de874fc1d204149be7a0e7e6bb7bd81bd81fbf85e7b5d8428842880e09e5fd206df8facf42e98b76ddbb06d03e58d95ebe76de62eef58cd75cbb66ddbe06fdcb66db9566b37cc6ea05e5e0b5abb613584fd6d9bbd2451b9b73993e59605599323c1aeb3cf711c57c1e738eeb23668933b4672d86fff5ad98c95692bdbb6cd6e59ddb26efffb6c0da762ab1f7e6961ddda2a92e82c510d499623366d2dad8399f6bc6ddc6bdb9edb36787fc3bebfaf6dab3ff8b767d5d905818988669783898db45e76b976ccc22c77f54de66cf939ef36dc2dace7a7b6923b563f6cbf39fc861b447489b285f5aeeeefeae7ec5c7f830dea1893e87afe19b2b4b0cebd87b7578e70e1ceed580d5177acebba2deb5fce39ef186ed50d32a8aa503961fb83597c4d47909e78897aa163eb411a42ca41c54469adb4d6fa2a6bad24255d362c3b8461a7352a211dacbb024a6fadd6d29bfa95ded07b2bbdf4de4b4ed15ec94a3dbb5dfcb5d65aebd74a5e8aef11e72ebc618c319550c718638c311691f8664308d58c25aa0f442a2a169d43a7b529aa292b43b5e248e0d21d833fa01c61d35d21ce03a690faedb43685a4875735fe410f11861ce34c288c9a21b2f081089b928550a6548461c88c2e7ce18245479418c5a44992f0479128f5433cb242c88209238c1a285e00e140f4f1b2425ce1a5c4a80298508716914714f10427a15552102363d00b4e889aa0101eb1b104c6b8c20c8974f1098dba9c187fac441a973069ca98c34914414938c391a620a2181742155208bb048d3364885b3a3081cb06e1142e28645cb9f082a44a15a81b328827625861c88882470c3842204164128228e5277cb28505962d1a5c8d09a350892a5cd1012514b364dc71e4011111878f51041d218f12683081f0424b0bd1052d3c1a94194798c1842c2115a9f184264a4821ad884883d6c3425874c2d84204b109c8848b31852c3de2922c26ca548db02d220c296191a67049492462248e00247ca18590c9905875238518a19653ec000b6cc48165672a4cb84297108a8a68f424ce96c8c0002424a4da3192a0c2d8c108e1d1cc8a07a20c5762e20b57528c208809c74c2959197a1256611a374022f6d4c2323d72e41863082599f1e5837088159958c38a0ad10332a1972fa30f965146d40c265b92cc50144af921a2b0420b13469e1aa2d30b240ec6b3ca8a10489512323063095ac644116b3c3b1183784e1a43300aad0409a576b8c031d2101a57a0e1e44558e5421c42c5679451d9099dae421c53e2d315714a5408a6490843d28ed15010d1c60ef1078e318550c885468c17230b2e3898e2d3c2949d305725a6c4a22b2444854f4d58248d2d18894182883c3b422138c29a50161a545ea470a181141ff194b213bbfa32259a574225512351139128090423244144a81d610f1ce309a191031a629217221417638d283e230a5176ae5c4599f2724545545293a32471ca484b90d0c88e1f1ce21321211a2ebcd8e1226c41cae703a91d32572296a912ae8cb128234d442501e0d3be675864a9d3597abfae4fc2b2480c6e67e5641080bf9f02ab9f84ed5957ad599b2c7f6686d4e44ce9fd66ed19b2c47d3f59d697ee21c98ec7906ccb366c036dd83639e79c7bfcb9857197b620ffad4110244b0bb3433f346fbff52ac36e39597aade75ff3a2d603b7ad17f750862e12b4b01ddbb1218bf3912b1e57255ca10843d51bd0692d0ccddabff7d6925bcdddafdc7d4ae6609041a71783dba9f720d6c940f3d4a7fa27677bda6ba6b46a9e1ac23e264b2da325c87da961dbd69ef79a2522c79265cdd8626badb533df69fd5b6f4e962c72849ed9a0fd48125dcf64a9d33d32a77ef79c8bebeb8d996998e6d1300d9b551e5dda99ccfb4f96bbce23358b459658571ead2b8fd6ba47f7e81edd73673396ab0a331486c7aaaa1ba0d39ad59115ad3ba0d39a156ca877af127365254bdbb5e7911b86b92d03cb2dabc0d3de8699bf9de791e5fefbf5e73369bbefeb68becb5dce6419b6deeba8f5e091a5d64ff3a2d683fe486c6fa8c7410589cece9024bade7d99bf4d8e70f50e7c2da2d4c901c9b2e2ff3e13f9efefd876b1ceb6dd9096b5b4cfcae80d57ab8501d58db3a5ed1dfe9ebc17df8b3f32e7bb6a2fc7d5a32f4f7f16f69516c8767f828e69ec0b5b6dcd8f86ccd1e1735804414896b6772f92a5252df9e9eff09723481bc32f0e9f0517040e18620bab21f0e3ceed2da2f598d4ad978dd50f35acc1f6ede770adff2c206bc8125bb26c655affb8febd68d7d66e1dbfb66e1f5b6b5f16466363186fdf578c3dcec9ed9ac1187fdfe1e73ed2c6b8ed712e080b8a708a3efde0f7393a7f6fc1d53656437c36566bb5b2ee711cc7d918d4d5016fab89e6e3c94c17aae307c51c3dd405b97eb9e4d4c5b35296ca4ad9495eaaa921cbccb46ddbb641fb1b9795b25456ca4e32535eca4c15c86210475cd7715dbd40d7236aed0579a9d57d56ea3253cd4c5d66eaf573a6a1c94bf543bdb4f01e5d0ff8676a66e2f635b523cbbab7ee887bf17e485e5a57d3226934645eaab57e3c1db4d6dabc9495bacfddacd45e2dfa4a994adc99aba857fcfcc5c104a752bd243b52efa56ee3f6c7e369ed7db5f2f4aecb4bdd2eebcc7567f5ce3a067181a55254ead3faebc2bebfac54b8bdd97f8147a5ee515eca4b99292f5da80e363c4febd7de831ef7347b9ee75d3ed5534354ddf37d9f86f5dc755da77b86342cc9d175d7bf5589a912d2ab4c3058aa6a7470cb3ac8c2355c3c9cf5f4bdfbdbebe32f51fc2e2e976b36d31043430c4bc40454a7444c1c11314123079113a6d65aeb5d817735fe46e8d7f7919fceafeff559ff7e6d9d6e10705d777bbf76278898e0d2c1ab35cc78e9eae0adf6abc596e7a8ee0ab60634c0e038dd79f4bf04f823c1eec5518e7bce711a34e095d4bfe9ea3d95a095d8e9db082a95977c50ab753aa55debb7f8a5a3af4ba773de095edfe646670d26b257c62c03a31ee000eddbf6dd6f33d7da19efbf172553d431198e69f79c88cd563b73ff73d97bbb19486d04eecdf9efbd37e79b378e7b8fecb287c12d53a64c198d5180d452dd533f607b53413cd6d95dd7bd08dabbee71f73abddb1ab8bf3d05f7e27b9974cf77bffc55107c89a016607a33f7997b10bc01c97244bfd623f37b2ffcd2f1aaad6de506769f3fc77bfc99e4b08273d880f7f83d12ebf4ee55d6bce5a5fac1ea6cb546016526ec6a98866ddbbdbaf274bf791d086a1edd13d46f09ddd6b037f7ebcf1886dbaa7b342c8c0a90da94b7fe54bb65f6a672d6da0db49f12189375fbba590f7b083bde306b73f48d88bcfb3e9801b9edd878c5eddfd98501f503446acf0b4d48d1912325a6110cbf341aa7711aff42a309293a72a4c4b4cdcb7499ae930ddb312bce4357079bbf19bde42d5b711eba3ad8fccd175e3b0f5d1d6cfe66c7b649e3473c7c9a104edbb10de3b49f5e5a1abf4fa3595abfbf6320f891a585d15c1f995d7cc7a0868064c8f9947eb08295d47686eaed91aa128195e852ee82a496fd2dba45f74807d6e72aee99a4e0725b90ad43751bb211f0bee6ad0e8d53f5f356c72aee3d1da978eda5ab77789b7ba37b597ba965f686fb1a98a05e562230b37ebfc38d987e69faa5353028fafdcac382a81920caa4e6a91f5c44b0525ba20ba2065d0ff7ea7c6426832e884b06dd231dc2ac6603d5d46c353535bf61351ba8a666c3b6595353b3655f6361ef6ad1b46af8b7de6b61d78eedd88e615e3d1962a07a6dd169edea08de6eeb35e79af3aef5ca29b9d79cd65dcd5f3bae5ff9e9fe80b77da9b72659cf794b7f8bb75abca5bfc55bfa5bfc29b89cd3aeb97e95b86bfe2c9282ab75abf514581afd3333cf5bff258b6395acaec5c9b2761a92c5694aafd39025ef5afc69c812773e42b3c8efbb9f79799deea91fb63033be3ddf31cef96f18df409c6fd83639e75bf6dcc2dec5da4fed66f1fdacf758fb67481bd61ddbb11ddb3156ed9e8c01ea07e8b42686cbc7dd7bafde9bf59a85f77bbf81838a0fd4fee99efaa1e29d9fc5daacd73cf959bbd43f39019104a8942b9bd521b3d100900063170000280c0a0684b220c6520c89e6f00314800b66a4385e4022a24b42611805511002310cc3300883000803000080300cc620227a1c0056e5f46824e78329a196961a53076e15ae8324e60cd7e66ae2836fee094f4c81727c6f35ea17cc69f2c5cbcfd476b8e2ae7d32087b5680b2b6bdd741be07c2fe26ce0b00ce9fd3c76d3ac0fffc959d25961675f22eef73b502c5339992c40e85a7927c5a3f699bb5e64a87ba4445e7bad596691fa6e0d1214826127283b1853231a05fc0d9b291dd15cbefa152b0b5c2bc01343164e4acbd8b24356bb967d68db08d2803b720c73195589b3ee2b9e777497eb83c1bfc0e20e20962f4df09c136350e5f7f3b4c64e73d6952e955450686711e2d88cd5b0e31adf2f0cdeb89018aed7740d4510ca1ae1cbdb2f449a206ddd3558c76e31a4db88d78f9dc9f4f97746466eb36742fc888a097133e84e257d4453656164129bf1a1daca38eefeafd1241fab0e466940a52de39ba79669bda2a0bce92006fe327302b742edf2f7331afac04cb011b4c9571204840a99827cd4a13282a6e093d71d9167ff7c5b4dca0ea2abbde37d61333620318fa2e7576b03e0e92d52db65c2fb56c63cc6f20d1788f782c38105fae86ac8563c41edde1024f4af90a8390834210f8cc45358fb3bcdacffe56bb0302d372a9aa72ce15e17b0c5efdc7df70f4032c89b4f449b34056f906bbf8d37da7a8c2839454267bbcf75a0af4f42f53a62cb27acd6db46df30872b2f1fcd242d6df67f186c03200e70b158734d3bdc20d5de8b541abf58d96904e3267d59f874bdd78fecfe5c02373a922e08ce38836dc61886e61d6369470015c5283d0c1ae7042425d79394f602358d2e3aaaa1104f1f20c542ebdcf9a4341b9657a4059018c7df60c6d0a94fb2aff46cb392da8f01c3c0314ed7b314f9fcd7cb2b04a926bb15d4159c5fd2f4d3d9ceb8e17adf7d96563f7da4839157a74a64115e39d61f732680a8ccfc537c2c0cc627b83caedf6b0760bd14fede67bc5ace90c300a4248837493539358e52b89234a6ac78f8312ccee176a66322d40daedf7202151c9e43fb2835ea4827418071ab9de06356dcb2454cc29a6ba33a970ebb2038d7e99cd19b584c42ed45a1788139ff2c3bc15655c13b674942e81efcf960ad28d110ae7a6538cfd33671fdca29bb710366e6a6254d174d6070d08aa6524280e2565bba873ccfae08a34337d30fcbc9f2b73274c815fb86dac55b0321fcc87c0a44ea44003a42a951af6c00aa14b036b55aa263960486b69935a65ecb1b7586c2aa6581163d6985a500a3f442e61bd3ed12237bf8a0c32b94465f473b2c56bd22f7ad6d9f427ba84957f7bfcc32f11060d0ada7334020efcb638a7840962d8251c0529fd37439b1d37aa2b0aaf193a7dc1d44524cec10f9caf5206a69bdd2858e75600f8d23ac7bc89bb80939d974f396d96040f8eb3475c46f1f171a0518696bf0fedea227c51f043891c171b85092a20da144e2a06c7bb084efa983c3b416f8b7aefff85dbaee12f24514826fb7e19dd949b706d8de641320a32646c24d3f802fcb736c4f5a61f1988c610f2f029b384bf9e52389faa1cb2b7cebb2eb11d47d4c8fa35aba831bc8b1551f7af9f9ce1a4210f64fb19df96f86f4f7d6c4273905f78372424f538194baac31e2f4e2f4fec78d8837d9b8b5d048c5a7fc329267ed21d8eadd6942c5a719699a49f4c90591f87b2aefe4e22e4f574b9cf1ebfb2dfa37d2f30d3066525ea0450599edebedd2267817fd94f7cd4471cfb75ecb38f0f6e19935ad713453d4a878312782a904e0b5a892608f0000d47b7770d0d86b914c192053ad0cb9acbf0008d224dcc440929917c54b4843ca7332da285933cdd322754dc8200fbbedc6d822c6bb355bf87e8c8eafa217e8506c79bf49696308c9e0c89073992d064f4270e0cba1ab123e8d5aef31575ee6d205651d16f07d02ea4bf0dc8dd8f7f963148bc0b1c1884c0774fc42e2cb2688f0893809100ab092e5dc79435fd3722d469a1d728ff3103bb6c9a28135eba9861900d39030d861cd39016f1cc050fbec7527f3ac225709fb183ef9af7ab7b9dbbb0de33411db12d0e81f131626b955ba6cac40b62bbf420bee99a9bebc4c6e496929d525ca310e25d941c68b46d28f5cc31ec48e4c1ea9a6f8e7fba41e267283050921d806b0d2d9c26df0c8e77eed46a78829fe837d726d737d3bbc615b25f8b9983dc018fb27735637fa5b88bfb1ab3d80dc1d6260904727006dafbc5393574fa8dece46d14a0d82fee8026623779795331c520f565a0de1606385835d8a15b312e981170b90e49796b9a442f6e8a8287b358983559c263090159741e6afa74490da3c3c86c62905738a28ed5f2d834983f8c8b454b9237fd48ca8f6bf36ca28cf65864fb7d936b69649195b97c4ba208926ea580875e48007d9077ed22904132aed25b6b444e7fc1d60fb707a923cc12c18b72c4a8054dbe1c0e710b396ad145df23db18052b3c7e61b53443d6502b3ec9e9a58bb56ff0ce0d5d55d225c89e47367841953141defbcf6e5ca16c7a67909654159779760e531b2c8dd54f0a34ed55c4623ce420eb30c8c01af7b7b5ccd86094114350ab82ca7a841e0e346c21a20e3e4218196356462c798a3b8896460d9183fcf047c68ad852cf1d7dcba782e7078f431b49734d4e92e5f8c30593dee7021c5f660c33162c9fdaf90b449a53b1072d147e86d823a3126a88770b11ef7ca623c7261ff6edb2f6b14c8fe8c168b72d015344cd3deee718a97db5328e377b319a0cd5806e313dbb8ee2e55b4a91be2ea1a77d56e5a2a5f5a9c490e46bb422f4e7f4a9bbce1d9ba186f0f2405aec591e35fcaeba0c5a9398f67d846955c4acd10755c03ee7959648d77e7a65d476b1eb655f804b5300ce851a8bd5ed2297ba603e1226277447e2cba9351b00ca18eebb9caad17431243785bade75fe4fccd60429929944ecd187af55b907d834c9129c3ecaf41f12423220e805710f5ca8a6c02d78789e83107b70825368b27428d81207bb213b21d1a44432d80ad3bd586d0c797fa2dceb5c5bd775737cd06f93e1dea1c73c55396a30b055bc08c544bff35a3ed99d56fa7a342494b8030befcbb6343c0067e6151fb3cf5a5aca3b1d3f0fee67ddc56d0b839d0bc8a7dd82ead9fdbe230208806e0addb39350f0ff87e636b1f831294ec4aa43ef35adc82b6df00b1762acbe8026b6e0a9c4e0f5ae0ba1592566e48deac3e27176f4eee9372c288b67f0c84e449693720f46c11814bfb357205263626c7133ec97a3ef954063101ed6247724c23ff4c9aa9025fe4e2f865d359a3177eec10af201e103fcefac308f5ac78c21971e927b8998c0ca951c4af693b6496852590728563cda015d224ec6762cbdb945ee94848540347387b438cce74e63fa02c73f12ab8bc54533509d245e1001f9c4712a9faaa406d273a63834775e1b8bcf3a74ceb284dfdf5fa0edd4d5768933a19fbe3e8bb7dca91b94081cfb0f90ef62410f5bf96ae9d27077fc279304f05054351eaa57886ffa0710502b4a74570daf22cb8707e3e60c4d4773e6e836851c686a640ccc0f1f9efc7997178fe64349f14a064116327e3f4f54ac8041ecf7b0b95e7b1b43ed326967c73dea5bcdb1e231dbe25de076073891e697ddb4d4d3a3697e522bd2c09665ef9397a01ce47fceeedd76c6e457a647139b79f6c1f6f24192c96673864b3387f86cc275d777707bcfdea1ab03e8bb80d520c3b9a0dd2d8309446277addc39260e2ec21128458598e15de42bc37b3cc8bfaf78d8292c1c068747f22b33770f14f014cd46bbe569e00b66e4f5ed0fe07a011b9c6f2866e52ecc330503a53ee7773fa3c495e58407b0c38d0f22dc254b6b1b52ae34b9fc68ed2a192875e0ab7af67f7170d64252abf868dadfe259a3a02678c8a1ad25f4169e4c94260a44c8a89e006479103eff792d07ade865fd505d1883790a9c505d54207cc82355e668eec1b3ae3d7c502281894d0708001884badc40ad0386f55d78c4d48c378092c4d6e0e85aa8d533e41801969a96544f0f37895bf3054b429db7b0e62fd3239a616415a94cb663eee68c4d863f50b88ffefa868a41e9a9cfc5e5fdc3f3697a04939e86614910a63a2dee7cd946eb7ccb066bccdea0b6a64ad8ab7a155d97d5c13ac444f44f330d452c19d450361af759af577e202b07fca4686ac9f9f4fa8d76c0021cc5ebb225c49de58ecdb4a3f950a8aaf155df24523f5388760716a9151c82d8dfec1af5bb7be1a55751462cd1000726c24cf8300b5adc6bfd23471fc5f8d05976017684646597202e1442827471d23512f801c2ced98a66b38f972e10b2173ac2d1d266acf0eb4fa48ebc20cffb441ea3fdfada29373982aea9b21fa43775c941f44cb7b845caa490bb11da104e75535e0cf18005c668b801357e640ea3101551b5d8bc243a2f77b1df238e5c406f384c36c139820460a59728a6addd5fd4a8a642fabd89207398da3ff235d72936d23c7b714ec4f878c26735c3089c4691e1f5b9cf54f0303cf0c177491b8689a84cdfdcc87540710cf9861416d697569741ed9f6862327b65ee706831aa187e0cd96220ea7a2166526486e996b321577e8fabb243f4002740f48bcad2e0032ac436cedff7ecc6260b038c314f78082920c933c92bfbac2d4c647ad0cbcad8763e2b9ec96b704d076ce6e5578c7965ba51d254874821491442b487bf90aee5456361dc23693bb62bc3ca05c39180f3ef890f74da3a2dae6cd9b0486558cbac06652b5532823764567f1a599bbefa96208b3ea167739106821ef8612a56cd3b7022718df4b27a480ef57efa74cec1fcdd6768fe99cc281c2669b0e3d7a68e24e1050178967d584b86582a0d5c6d0327c06823398fca16e85491ceecdc2a0f4b45c9f05f1014d0d0a6266cf985aebe7edac9d82794ea6565a796728f847dace50fe0f522702226f369f80a713aa26a92d323f3862b58b6d25e636d82aed9a4f87c450009f194ccf7af74db987297621828c181e0fd65d1fc88b76f1bbc40fc6e8f145d73f6d69e224184215202be68fa6b76bb05a3b728c4fc8a9a454d1d0f5aa42f48951150bdfb0f591e0a3e8e850373d8315620991b56b998e71a1c6c36d6136c06e3b031c110a5e3351f7ab4fb4096280c8b795d4b0a0353864677ec5f79dc1f8262868b61c2e052c7023641b06ec2ec0e3e526f93087fbf587bef7d261742046789a9a9f530cd3a97b9b5f59023f6befbcb2f2ce6586645511381d93f0bd4567b95f12d9b876d90efc142f0a92c189b64ad7bf13a415bd4ab37a33927e4c0df59c4aad17c33017e1ffc50e68c32b9e38f7604fc5743cf3640ce78d220223781586627f0b67a000ef8331e149922ef0888eb3794f51658d14ac5989830de4643e4a394dcb0992cd8c3d39c1abf78169e678d0442a397836b4595c51ac4f28961332659b18766c4b594c794ea3e6aa65487b512b81e2b5ae02b8e48e10204cab69b84d851689e6fb5f1638339906b034df1177677808b1f4222ff183fab0807f50ecd55499966c1719e418d7a83532fc3f7ab9e9a1cf059d8c5654b7ae49b8a3c062f368972779aa396434c95f9384b93ec0c2e11ea92a7799418ecd1e8f27843157ab031368a80ea6c0a91c2f9d0e60cea2d93a7f96eecfb00214ec62cd8051900e2f2eab7bf088c5e2ae863c62ca782cc04f234ae09cad8d7d9e99d06052ab5f7b930f2617b8aef8a594a09a75e5f643190501da7bd9cf62a23aca72995f0761381da198090ffe2de8b99883884e67fd80a4a36472169c063788177dbdede6c96897c1b017f405ed4868283da2404d26ed321e83791a777bd6fdbc1a5afe4ef004d16d85921e4f08f0b8a082d9327ad00af07fd1d05c0351fb40d479469172487d833aa29bf2143aa2235e885f5c8c70adf4fbd03b51359845c70b7fb1ff21dcdc50b8bc224bd17054c29297cc9384490f4f7e86e6b05877d16f18f7aabb6db85e14ae9b0a4a62cb870c7d2dc05272e25d876e108c71705dd5c43a52b87abfb3e29f205e235fcab92a88c77b98007ef37db0b615d70cbb9557cf0e41b6340b71c1e5e63205b4a32748895d91b336c61d5e412000dbbbd23d88728824f863d8afd32c305723073b99826f4dc55c784bfb83c71a596929f8106b623f0a1c4d693b27ae63f909ba290607f11a1691784735dc9873de9bf2082d3ca5ccdd094f86c4bc9afd0e727277cf724477e6ec25adb6ee22ab1d34a24d6d0591688dbc8483422c6002c993322c666f8c01d46eef277726d41166a4276a45cfca53e1e0dca1ecae431d122929d49036a228a86d83dc7e3fc9008bf27ae164b592c27cab7e713cbee093ff3c28a43d774b0144312b4731d0a9d68b6a1d6939b0825e19b98c58a921373223c7b097cbba32a89db30c034de82fea9abea0dcbe5713012ed12dce96ba825da0644e1f0701136796e1934154596f4d4315f21c21e6543f0f7cc30d1d93b1304e3619ff725db74029507ad3157c59440ff9db1d03bcba842d5b0c2e5886a36d8c4c3520836e452afa951e7fcd91ff86b1acb7e4b82ea73e3848516268ad907fee4b1dabab3063213fabb3211ed333d452011d679f633e21145618084866f85a90555206cef2f6b469ef62a43f8a84c2bafe5cad3ce880fc24abc0b22f01240cea8ca8a9e390c2786a09bc74b57974ecc9d70e0354d36031489af18229732c3229f7b9244c2a6cdb5772139dc55a88b34f409d3e642ac07ccf1b00491079fa7e4026e106503f143a515073cac34704439a6e8773874a90d42913fa1020c6d85931a1c91c2eed0fc70e10c2f3f8978380385a07506574f4a71eed1c4892d9483ff55b851640c494eb8e0469bca1b3bd8304c70fbdf62b1ea6b70a54a08f3c47b5724ec908f9313beae6d54c0603dc9c5355938f8c72a906992d333a22353e2f0f883217274a6fbfd57f0a7c923983b8a2ad9a044ba9c4d74d74cfccd547372a5b3fd9883b2ed8273e7a91bc970b9e55be54b23b16633c230edec6074e31aa8e03040594dd958160a967d23dac098e6ae11d2400c572aa7ac54af82fb456871f511da924e1436c6e992330954bce5bd2ff6f6060dbe1a2bc70c6143e6a7a227db06db0287c192267f6fea46d4cd7dff50c42de810870ed0eb2724e757f470f3145bb85bbbb7f782fbc35c0f9537916817d3faeb98c3a36ece50fd0b253b9727ea52d76811e81807fc8adce91328b13a9091304399781983aea851bf43173c15f67084ca5ad400399d2f10b361ab772f60db5e49bcf4d36ba80a3250b08f0668a43ae5acbf725896537fd6a5a2ad6b3e17dd03f8baa01676a44017609253244f44e0231119699f2253e1c25354cdfac1f8ea571181d79076216330ec6d5f57db94d07ad019d31e58f6ba48e1ffae8791068bd3c1e451de0bf5719042119f1095a6537617eef375b23eb7711499173343212a25b829c80c0f9525bd6e1c8fe961f0c82b9984bc0726e453d2ed7edc9eddb815ff871a7db54752c6e5fac2c342526029d4f585da467644c0f31be3ff61c7b092a43fa68de609bfd3865cc11ba4305f61ba84fb735e79903c87815791c4a47ae75618164167177a7c17e66af0c9298ebf4501fc8250dbc43c94c80ceec773edb5a41346b20b78086a639ee291c6c3bd33c68f3dffc96f072cdc2b17c8614d90b41b8220fc139c8ad262e6bd6844eeeac30b385e8cb967f838a01f5e941c89c0572bf6a855a038d401b7fe5e5b93f6fe520c5176553adc757b22878cd6c4c4347d8b8201c7f7c356182f4dae972fba80b60d5018a8f8fe1c7f346fe4ad1d0f661790ff63df7077023810550cfade5b98f3ddaaf3e038cb684ceec507d983385dfa069cc8bdc81a09fc7233545d43e243befff92f341eee06443ddf70bfa70bf17bf8a8114933a0f05fb198276fb2d84e1222e1dff818487765399ba95e6f64e584f1bc8fe13fba37c68a613938b509871bcc32dc63d1cb5b86d1b41b3ce765dd019c7262e2ceb89b4b0d5804dcdbd35ac0230c81693ce106350a7f70dc418832b41fbe4782f0448e009b83b310fef40d86713cd43c1e28ff496b25066fe1de9d81a1dac02f1e6aa4bfc84b09925a0977d7b1e051d2301c7fa985ee5eec0456d3bf6b180d4eae7077b7e946f742e89b1f1290097adcd73919aa99816d1fdb08561799f70f312f4c03df11e7a46b4491e7b5e3d20073acfb6148f730a8de000e6dc32228de0e8e0bee53a56e19b2d028ba31d8408a470c480b6d25e4861def112397270dcb331cb7e19418a4b023e56511478f1a3f5548c00615636a12c5177c0c4ed3b64e315c67c8395311b9ce9efbe2902c7b41a4d6d5b6ad0503ffa0678c127419061d9f88626cde1458cf18662683c022cc574e0260a1ecc75399af32a6f1cc21c842b8bb33dc562f7d31db4bed0e4eb3f97d709f9033f2e286768c075851a51a4b597cd8e20e1ea53f54cd46c669826f3db24b467c9d9d287814b8f7643cf1482b51def95ab85e75cb49b6be9472482b93c24a5b9ccf2aabe718ee6e9a0d1c89f3390987ca823c3a4bcdfb8c541da240d782363932d7e58089c1fd526856dd657418e840048796a7ac8cc19d77dad1f472c7ee1f20f5921c7528a53ff6a6ecfa8b1f300bf7acfe0cb973e40d1917c4382a9139bee8f7acf3dc8c10970b095ccb8052f3dc6e49e9780782c70171a058dc205795d9df664ac03cd6d748c2d8943a0a2272e591cedc1d7bd6e96b989d0f838263f6ea1d05656b40d1376668de9e4ad8d745d03cdb78cdedcc1a1a0aed77f9cdfbc88ce8774489154b229ee1f7ce681f47206e198ce1eca5374cef1b5ee894bf76e04676370a0df7edbc1709360b4843f555a7f6bbcc79eb3e74ac6066d245b75cb4ffbb1830f0a947e4a4008e20397738a56b14ef5c2f7a56e7353ae8643904dd8d0e629d214f0737979bdbbe393ad886d6ebcafd06a9bf41a72a06c56a46d3d3fe6bdca61530615afd0651402b9655fc16d48ae836b13ba0024b3d0374836c118359460faac12c497e739d9ed4480128d1d5d9c1104a8a62739f7117297f58ac3440e90f1845c1cd7353a710b3a44bcbfb4ab101cf849723f4fd76437610c3c3b4d315dac6a44f9042d4bdfb6876878628d9b4cf664177555493b10d44a73c3fee2bd941d607f037b56be13ee1c54b9fc0e250df6fe3081a8c20094c229673cdadface0d93a351d9a7125ca9f84da8481be8bde7049398092b77f1046f71541c6efa009759559d60b18413ca9754c8c22754c956b76ef9c5165b71476f9ffa13ab1d6fde39027a233c55d7207937219eee3f9c182533cd7f70d5ddf45ef11f6edcf687261f9c7d7063df7d22aa56a0cd9933f8b1041b3d47e23d93d1944737c07cd81e1ab9d76938edd17d080bd50bc0941a7f4f49145982c58ccb5a9194d8e2e9dd1a08a0541d738d7412397088819edd4b664c2bf0ce980a32ca102858589a9406433185fdc7d121690a4fffa7aa855915da3b2a0fae79497ccaecfd481252b1fafe6e95e269bcf4f202c5645228d3dc0fa08a68e2624def0450a24409f7f219124d8763d94c02091a1c1048c712c5a0399ae1660ba35262d076585692a4daa3c3b9b9a4698cce0bab4e86794a6e0c32ea11267170be9664697a29cd40f1663a081d084243db374f19820a5618dc04385508242dbe1d9c295528657eec0ce761ace47cbda7360c0ad29cd951480c82171d3dd279c815b54f75a226b330169a7f8302d180070791d9c64b1d3042c49b9cc19c4133447a2f9c09a5e4384162de119690d04a775e95b8ab63d9c9bd81214a06e8e769f0b1c25250df516f31dd9be0994a105c5043454cd34bf41410042896bba319180e7f8f54a4671228c3eecc4d48b922c14adf35df2a294eb65c35ed29b8f4f54285415838741c1972cb213475901b99cc8b34fea69706833a4662b17f619e87cb7051e43b2deafc51188c9b2cd7abde771b72bf18f534c365dae1ffd996b632da89a0fb1623f1a56c6c02853b27f0a235cf010bd28bc9f7340b46fe699a1dbbd55bf705af13dd983d49cdd9e7d5cc4796f2391b672769b95d956e20931c9f89215fbdfc400e852dce407c0eb05f4e92c3a3eb6614d910c9b809c378d7b30070f164e866d047d985cade96a196d5781e99f9152abf9c9830de551198dc2ccee27ffe9b7fbb6272b1096d0e34d0638f22d0b5641ee8e3ed579a711231bdb7d390939416f99871a8fb017678bdd7dd3a5acf124261688c3f9e0bb366f2801f8cbf3c4bda70d481a733fe16454349a4f9bfbe83d435c1389609158a5cbecec8f46f2e94f76f69ddeb717b0e87e0f7881d949de10819a4d7c2172f10f21e61362580136f1194ebaf1ca328424805d5d3e7c9db6dda019d595b5fc14c1dda79c97f990c5a82134ba6784b04bf416648567a98241ecf62ac55c8591e252e3b5110184a07468e50f343b5faa292dd8ee75e1d95141ccdc272da58e449ff95c7bd5756a064b7910549832deda64cc1748297e8d57ae6dbe00cde9cf4558f7377f5f95704ee9f2afa48dd77b9e85ed619da71b401db7ea141ce3ea31e902c243f152547506605acc8852f3205b0bb8728e186ed2acf78ff8ea4602ad7636df37bc0d4ec9465c7fdfe6d1d02c07fd04310d97056467c4f2c5b5f79a0db67a64bea83e0604d80dcff3745518068ecc0126619d66dc258420d75ea255f1a06953982ae3802dc0410f3b538b39ed11779990f74c10e3a229a0b32b973cac5c10bb1336d7b4cc801ba2fe86b036ae2e621335bba87bc0b0f1f1d92c8cf672d45536757c2e740d7f900f88b8891a5e1e10aa2150a99fcf32ab4168ab946493dcd661c455e45d18cc2c0b48d5df8d87348d8d2a2561e0023222dc2125e73e1985e115de03f1bcf386097c8daa16bd18efcf508217604e45e31363a2152d45c9a13f9e902c1f6b1970c624bd35850077c340ba9bf09a81d167098c9cc7b82a7d399502807d5f611dff75b790a0564d56e3a59321d97f7c698d1746f034a59d0fcef3eb1e2fa38d0bfd90796b85e553fb42620a01fea9f020410e600b1bc515f4674d19b14fd702f7475b72260a78d25abf6936ee281a29218f7b453eca11e71a53369969a7fb113ffccf83658e6a61094123d1d49f043e35fb675c50fcb2c5f6ba514f6fbc3072b871b881cca39fa801213a611720f0543a7cd9aae9c866b016c1c014ae7a076507cc419d6a3c03e9422dcbfb2cf9a891a240917b424668f551d800a96da9bcfe4f07da370c79ef2405bd2884d2c060b21f15081f68aed8cb4d560402e806e9c4ce9bbc27aeb6f494cdb027bb42250ec0f64e827fc7de8fe431bb35577b4d090e4ab3deb900bb48917da5b9ee8e88e05151fd688eb9ada52054505a02246e75b4be21794377de1430fcfaa3e33496b3f6bbade0bc5007886a6bb180bfab84d2a699511188116e3847201f62b1dd092113004434909481805aaef164bd781312625d38316792ddad5bef2ffd83a1230a0a6f7ea44c4e7ee8083a2c91447dcdc144d82288dfaf82c33c0b7440476a1132b24a171c082a023d7791a28747d7de92fc3004bd52efd14f4efa6071ed3ec4a2066acbcc9429d96730c598a2e614321bcc768ca2611a468cc732fe749b4db65a716889e907a5e9425e0fb3c64b7ed73151ab89e46b548c5c783bc890e64a38d081b10cb5914004a1b8117b6509239d85c8677c8493f2de4790030c2faa094da71304d307a1b92d1c1280cb569cb800cf9c3697487f8dac2c44b097f02ba041109da89cc1258ac0a2c2fce7a2cc8503c9c86ae90f8eee822146bb35904e3ec1d7863c5d04b9d06e4d073f3ee84a521a19be146dd5814646464081410e9f47ee118c979c0d632d0a183f36211d13575c3ed841c7268bd12612464481426ac4aa026f4133427b813ed89d744fa2734a043dd70cea10c0dd3c13885daad6510439e74474584d0d932ba213e57ca895cb843ea96f212196c21671d52a790dd114d0cd7187d39a8e60cd80df55c29278ebe844ed532f2a10807d61306ec658371362a8e1c515fd773f5c270b1ad7e27e235a82f43194d21b1374ceaab833e61b0ce79cce6bc4087ead0328a43a50e3281a2764a20e4c9aeaa2564a56e8d796890e5fc318b800264521bf7ea946b7364418770dc6e5e0c4969d0248a126a81b20a688d285139890c65e86c9c46215413275744e8b1711261893997c82086fccb69b4879ce1e852863eab659c426e73ce2986a6dc4024601442df1ab726141d0b3d374e6282243264430e8e53624113486543cf1a3741208ddcae43a6b54e2c93d024c191502ca830faaba3d06511fa8cbbd41cba6b54144d9d0ef88c056f0e778ed4f7dae7eaa765d58a93c808096574a098d0d8bb7971313c9adbc4ac84128900f3d72175326c4d0df6f952f7eede323095d02458fdfbc41b11b6f5749eb70d75a293846c35e877377c9bb916218ad114d3744e66a94a6495fd3d82788e666c8d4ba776295d6bc87318865d3141c59328689077209f1377af08f7383cb7c0046d82f504aa849ec446a27b50df782dd6232fe4f995a38b1baa5ecb580ec53a304d40671b7a83964108f5c4698408e1d11ded84f29d951230dea16a1ade44ab845b096f22728247132ca687bc2db51565b0e19c8d5a0c659d796233d131b135f19ef027a2264624e8272c252c7522862ed436073a31f4120e0644e81e2d83133a7e37d73294696609d21465bcc1e9ee4514fad4d1352224a733da116abd96a10839d36038856c1b172934f221f26d8e44a727e8da87ee1b510237b1544293046782b32670e10ea9b7c64b64b0439d6d30ce42365a61627e6239a1558283047be23de12d015982c7135fd084be8e85448b26eaa285eaeb60b4091da3652c4315e334cc43768db0446e628984d6847526a093ecc070ce0abf1c194c22420ea71d228bb6e722dc510a659d798266827b824de2a5c4770232219aa04d584e70aa355a20b71b0a89eec4b612ef24fe0941ef91ae0c677105c41cbc8b2a0ec59b5f82ded83227f2eb630e04b1be3bfa3274cc96b108c53a9312b899d0e80a39c2756109bdec42970b193420d173e34b804ccc26081256255049f06ec28b4da80d5a864248eaa24b1493b826819ee0fd84c6265487ce6821346d1c2a91410cf997d3680f31da52a2df2432de17709d7ceee9057497287f66c1d4d43cd1a70b3bb457d40155036a8bffcd62d48e3711223137b140222c441abbc00577813613a853115d3c85171e3249105ff76d8429f8a5e8f96ec48cb2a7a6a2da129327296a8d7043db00e67d1a0989af58daad29460c82f14effa167a6ea2faf8cb7bd7910db1185d991a9a2742f3c5304d1daad60bddb6db971588c628443b4b8b9cd389f9dbd8a83595e48916d8c544c4c0dec2ce785f5affd4600991e516691dacbda52abcf8abdfb893cfbae0c03b79f578c48e0264b933ee96337b8035c1e9f6aa5a3a29f18797e6ee9f23ba2180fb32c9a82fd3fc1799a5f74ca58f4090ce3022ad66d5f5e5090e9a6039351906fc5e78bcb43cffef58880df9df920a00bdf5e603e0d3bb2100eb7e816986d93e05201d7bed9a15fec7c6e1c5beab91d7a10c7103ddbb9bd3bbcf9d2435c6a792f151bf602f65a8b501124a4ed2df796524a299394017805b90521066f2a759d49979e44caa10db7e5bc695fc518db2cf2514e34fdea2eadb5cfb95b4db3f6e65c2269369b64cd9752fa531c504bb4fdfb294c33e993af43fa0c71c3387368ca6c98e3beebbacf256d32994ca6c7a6c76f02a3a74c8fdf83df1ec7d65f283d30b1987197b897a42fb4d91ce5382ebe64edf89bb4586229ebe33b634f675f7077a7d153d6e2d834565a69ad9556f71dad77840961edea1587af5c80a60c6a89e65052747c9ff457eca88ae0bfa7e2474f4917526badd59d73772f7d2c71ee9ce49ce338e76adde13e7aaafe66adf62cfc5ebcb198d77ea5725474a442d18eb014bc6c9ac2966dab07ee83a1f6806c81e709590007435c5d0ed95e1d0c6ddcb8a3278be9b328474701cde9d351409ea7c3c05820aa2825b452ed632ca00154a31aa598528b574ced2de06dab699fadb56a12fc469f547f63a30cf9bed60bf0d4fd420f638ccbf2ddc2887bfb6c1d7fb6d3dab780b7ada6e16aef9bf05043522b50c4d8a993d71e3210899de080180b8845c04787912826370ee78cbd73c165cfbe7d96129c130522b109a240247682033ce5ce752f9f44da5e7ede3e4792a4b4b14b1b9e33c4526e1b0b8f0e626e487456fe6d4a67edb97d32cb5c448731261f050eace1427ee83cdb26bffbf30b6ddc4d01143ce54460f413f45da5512012731c769254902b1a1686b6f46147d88fd8ce8e6ae910a37c680518d1a1caf49bd49e855349272d997e2b917ea326138944d2dbf49d3ce7fc91e029ff384b6ffafaa18248a5da447bd217da780d9659018d9eb3baa8fed549bf99c0b0f4db36a3967eab1f2a063d9fc5046bb898efa27e68fa4e9ea7e687b73aa5940f236e09d678f039f425f87392485f2aedc89d6d7af985f5b769faf0d61fb74bdf69933ebdeb47378bd9447baaf29bebefa71d59c0a0433c31fdf9331af0f53b61beee2c514129d19ff19701b95131e8eee3971e77b4fb59515974fd7c92916e3721b771bf711d89447a187193c09ad2e790fe3e098c9e22fdfdd26b8fe32b91be10bfe89efbedb1c69d4c6cf85e7cab50fedc93e56719df8663303c6d8ddb5a8d1847ac8558ca9b3f0d4b89e3c7e5f849e92e6594516af193af61770dbb477719dd23183f7ea8223ad43b8caf4fda96657cce3b5d8d028126611bd984261ae0a9ca42019eaa2f31ae35681a8d4fae82c53c51c31578bb5f3930c7d0de3ec67801d2f670461a70c2967d424c21256a870ea31104728cc70f9e1a514f74fc17f4eddbf093fddcc6f7f8d918a38dd3da4f635badad3feaf5baef67b7addaca325757fbcf448e41ad301f85445baba14003b3b795e6e51a3942f9d031fcde7bfd56cbd92bb3fd6efc5877d09ef3784fdc810d66443a9c41a3c30d8674b8c16af5421d79b1e4d6e46aab6f719fd5f55411700c6d3eac2b81f604018d0e45e05c8434f5458d1071b4bf1d7520626855397f1c1c8fa7684c311fcb9e798207041de8b0b4b5f9a5bcfde5be725f9fabdf7dc7e5df9ec5bc188cdeea41bd304a8b0ef145e1376f2335cd5fc288bbf4dec5d8b62f819becea77f54bb5244960f71cf769b1c9ea5b508e33d2255021d0610a73faf4748bd9bf10878baeeb1e46dc1d5843fa9ceeeb77607d12d739f7196f72f2c8d13d07e23c63b27b943c5b62913c5ee8cbe700b576da69efedb145c884dfb80d82e3a93204dbf1b363c8ae0fdba163d7df749cc1b9727a624e7b6a6e60ce9683df7f7632b6afa02e117af83d12d3732f7a1147d7d086e76c403ac278b876fded613c58db43bc3b1855c656378e38f146f39b1abe71c4899182777b99e34ffd857d2ae5016ac83ddf849826cdd61e833532a4e63b7732b0ef8c5fd088af5df375d32d3b19726767e31748a309f475b175c9d1e98fbe1f77f836eeaee9d3bd8fa526a3cdad125f5a20c608c6393fa378345645548ff68fa03c31a9cf496da8efee608e47540b3ac618659ec7057f43fb5686f36d8e7c5b3b19f2ad9d1f61dcaf9b61df050a143aa52d630af911bb8eb6fd8a669751528465c71d9500edfc96a784c26f42ee73a7f3dbfc755faab5fb52f7d96cee0b6b95324ba943afdc6adc30b16d78bb7b1b746baf33e1e1eef147fafc6913e93169eb6674332c8867d8af234c1bd670cb35743060bff437e8b6f5a5e46437a3f47df54bd5dabfff61fdd2e75a6b247d8d3145f7f9d3bef4f5eb9ef4d9ec0e8cf96937c3729e92f9c3db17d611228862b3929394d9f23dc9d196eff5c3ff1e4594cf62e6f87334fc1b617f1294db41143e449e5fb4c11d61495c9ffc744a9bea48d9a6da674d56695fb3b56e196d76ec5ce8a0f09b982b8f24ac8db323ac75b4a4b58467479c1af2634c613fbc6f234e68afd760bea55ca4a0cd461153c4f72e488eb6bf2cda1506fdfbf7e327d36c8c6d9a1de3c390e0d43e82353728ed68f84bf0068d6aa9a560ced4a4ab606d1c29daf431f739d32d7f97a56f5476b9a6e338eee5471d3983a3d904fb1b8a19cbf35fd890fb3e6a8cc6ada27d3f23c1b229ddf273727329e98b1a0e0cf3cb4dd39991c11b72c7bf3f3b1af4317843ee0db4dd045fc4b7f25ace3e0a4fc918f5ece2fe1368bb36f10ddd8e5cc4712bf4a9539dd2f419f3e976fadd08caf368d7bfaba26d3f62d9f36910a0edaff9ecf9942797f0ffed60d4e8c8adfd0dbabd8e94edf83aa51dc1cf6f3b1af52f78436e0d0c270dfb112c85f66bc8d03b03feda5fed3dee6bdf86f61a98a3bd7f60cb2f7edafb67893b9dd2ce408c29e6a4e1afd11b723bc5608d09d3df86dc16cca92f74e4d6917b933428d3bf65f67d4b1fc38ed0771553e5aa988a55821224a1040a8d56a5ba686dcf69803d33d5f146d67869685ca871953a95e1e00db9b9ce059523e0ef64d8c73f3b1db96fb5216d0872f41b1d7a04c410f8e16808981f2490c20c4e10c38a081bc460884f0ca6cf98b903f503ccc816580c47a8a38ee4030212681fd0a0033fa63d9ae470a1053cb024518cb4d05c4032830b9c4f0f530d7c74c800b3e005664405606ca53fa4de06e693824dda11e653b4c3577138a410b1b7b0fdf503780822ab01264688100851a9c2a8549e9702cff33cd41795ea0beb88080f2ea069610854110c061e180c2dd80b3bb02132958a042a15aa04a1e680e43c2c614715b8457c94976aebc9535b15522b8e2a2398bdb0c39531ad282d6acfaeafa5ec0a2392655730a49ec6b017966c6e47d80b45c0c088fcc060b0e2a17a4079a9418874dce1a37ad0e1bf4d0a0590edaf59dbffe4c15ee8e141a5fc598b78d0f12945f9a0e9c79b88a33dfd2ee2dc1057dff9eac81dc2ae1f7e08bb7e08db3f01bb067fdc39f7eb6b5f0d064d40c0ae17ac9181805d1f01db3fa6993407d82cc29b457828984ae593eacfbfa06023a0c0747ffaf5dd0bfe219e883b2cc168a1289e88db82617d8bd241a376dc5e812ddd7188dad39fa04410b3de43894e9e037120cee340bce76d6cfc65435366a456c597d24f0ec43f1b13b17a447242388de519ef052d286ff41b7980fbcde8023d1b4fedc8b4cbd554c1d538230f6e056eb502c7c36119d21a42f43f34d0a1241a72110d0db980843c2d5751aeb8154e263c56a5052c4282f6cebef41d7f78dad631184a325b7e48e3f6ef7afc0668b77688370aa1207428634443b12989e69443128bfc18f5c9793a209fdaa933b81b0544d39f77479810d78fae8fb9eaee8aa9780343480d31355d8790d56c47580f991d6ad911d6f3b3730946dcfe67249659c491b31d1b90e70911479e1053b8a8b34e561c1271649c3164ff213d3a5630d132943fd8051dea3fa93c9b9a927206944bfea8265a6e5412bd839cf199cc53f28ca7e4952c309e927eb482c68f42a27b583af499dc1913fa6ccb77327e5346e637f3514cb4d43e94ac148a9dcd6dfb8296b2fe4431d1724b91910e492c72480a4dee8227fcf7f4799eb26ff157b7e5ac094fa15c90bf2286dfd4d8d38d2d6a012ecb696b8cd44b8c36d207ca35bbaa6d99a4e19c27a9a4b57e18716bb0c6f4391a079a19994c46cdc866b2996c269bc966b299ac7a3be8aec4993ef8e6675bcc172d429368afe2f99a5a481d97377a2d97377ca76d555a84cb1bbe5e355a84b2282d42599445599445592bca8a29fc294b66ca421e9c40f04a042000dcfc6e8b2c345b31554c2d5d74184fcc7620b64f99ddb250869285b2d0898a2fa9e3f2466fcb7279c377da3ab57079c3d7ab36b5cc178b4e2df3355ff3355ff3355f3185ff7cc9288bf376d05e0913213c383fdc62c58a152bb6d98aed8f81288b325994bda2cce572b95cae6d158bc9988c19c9188bc562b1581acb68c668e446710003d8a1afe20006904afd7b1e76adb295f93375f335da8a98e95969469a75336eb3b3ed1571f0fb6f5a5eafd7eb856d6b5b459cfbfe9b8f6d45da56ddb6e2b655de56db762b9151c4e9626464646464651a2be2d4f7d78a681c189a4c190c4968acf8692c8da5da1a6bfb7b8d451c4d4c2c168bc562db5f6ad815719c4415198aa6994c24487cdf89132ad70b6d53300bc514fe697498c908e14c86caae1165d9d97cd996f7c87c861082466ac1bdba0df0e8602821810f328c61a18c0fd11424567a932e3dc4f0d3a22cbc488283a3144009e263254526efbdf7de7bef7dbda0d9cdb52fa702149f7db5d63f53205bbec30af1606d931e9004893274adbdf7de7bedab342bd29b74e9418679afb5d65afb85850ee480e50439c05e51cadc106484a402d140455e41308009924581a2034c574c563e86a8c19734678ca0f0b8405116274508716608178486e01012640b111e5680831cc0204848141894d47befbdd75a6bad7dd55d59244b7e54e036072512892359418acf0e64395e40810e4f5a48302d00508f235d86944c596243500c4132388fd593590e38d8e1e5871d0075091a3243cb8fd01348d023082b2f701204430d415264901905f633832ca1065790168278a2450534b8103f8acc80e001882c4ee6953a83ac2671496b6debfda4b5684f9f74d45a6c93273dc86618a182ed5199046199f766f9d4dbfccf20698670820217662e31414b82c6ec9b635f6badb5d67bc4a0c10ed90e2113c4a0ca4f972015d0345287b5758627b296b5f755f8870af5cc0064c5440622b881eb0557ba2831d26221280725d4beaad6a0479220264270288181941d1e57b884c9d18212e50848970211477cf852454b0e244246680a4f6880d2b81cc9a2a41e113aca32efbd4784c264f9a12582131014f9410408172555862767643db2ed6b2f4fb20079f5f921f6f3a4ef35e202185c1f5a60422bf862e5a84c0a2d6e0cfbbed6027b42742485446982efbdf7da7aefa5f7de7befbdafda7c983d8a04d98004880d647096989624f1b33ac9b60811c1c48b989f4a0426a48584124a3fb933186a36f4ecf97267c71d65f3b10dad3d6fe8dc6d22a670cdc5e45ce8131172bfdf7833dabf32d4dcfc42fcb89b817fda93a7dc0695728b740be5ffed667433e4772e3050ac8bf55a90daecba9d86fcf927bf893fe5fd61ed6ff6372f5ee4cbc80b18ad855bce6682b4d67632b6b79b7de1db3b1326be187fc55fb7e06823ad39f5f167da9920cf7432eaadc1bf5d60df18bfc552b6d030ff8ea9e13ea6617eada1be3579603269eed4bd90af7d6fee177e39a6ce0489b1ae60cd0bdff7f3b39897f36c5425c43801050b7040478f8f14bf19e11db785d330085cf49bed3d0787f6b5ce0459c3993dffdedfb0d6b094619dca382be94512226b231a5e3bc29c8c7132d45150c7033b9c15d4800825586df92e27ac2d3f9439db77fcdacf50db57fbad81b3de98af5dd07e604bf7f81ec4b3e376f978cedff6f3c3326f539ba51de2a85fa86d3a411ac6d9852783416164436462856a6e697faef84af094e3971a96de8c0353050ab1d9fe2fa6a8fe28333a8c3116b384eda1aaa61c454c45800213031187848a2b45e12910d8689f2547fd07cd8eb01fd60c6536e73793c9ce12fb5f3917f56f88b033c0004ba98c11b6ec18f0c2df625c6d8ea06ad06467d37726b24dbf36e9a99cfce4ced467788dfa3774eee6620af933aa0d9dbb714c217f82dd8cfa14bcf1cdb0e28e067d97513f3a5170e5a366a0e747cfa92f84a68f9fbe8c389a7dfada06fec5f369a572c653b4e74f2c403e463b841d614b84f6672cad94f2e2d8452e6b8f3f5b9b3b17d696de6e8f6dfe4bfad20bf973d36ad0076b37e3ca2b431b2c168be572b95c2e970b03de72cdca62b1582c164bc65c2e97cbe5729161c55c427848bf3ca2374a8151c5582c168bc562c9988cc9988c49a199b36658ee68b9923d29d99a2b168bc562b15833952c45e6631e1823232323378a2f0f4f15136bed79ffa9944ae50d210034a57972bc7d53bf21c1a36bd0edd2562ca59c949a3c136f33b18d09cfba86a9c94422aa28ddc0d4041dd750e6ca981d6157bc5c21da117665087645e80a932d9fab46d4c6c6c3a785adbdcd6b3d34cd4705283f320b5a941f5b53827100b3534513e9341ab503cd44e350a403072325145b89194929ddada0b1221b028303cfbd17e39f920bdbb6e58c03161ca47438146dcf535a0a82603c01223c654640e547119a1658041bbf1623b1a74fa1fe3b7d68d035682b3e3d7ea8951976f82a189c706bb7ac9f0aa5ba831ca4d936e6cb2dbf6fc49e236cd9c128b127582be7e27a57fedf8803829d0f3c30e912a9e3f2a6e16b2b1d234208003051538284870261c4e9030f4cba44eab8bc69f8da4a613978e9c9314a29a58c5296e89151ca122da4a6e7f8baa5ed0e316cc82557e8d716d502fc26b4f168824509264cd80c81d92109241f31ba0f91a944acf86c6a0466e505b302c50647da172827c830fc986a5072d89aa6699aa6c44a0a7bdb1136341b42a5bd7f9b148a7baf97836ea2a914f9b8453412bd82e6d19c8f0efa8a5ee92a39d8e1abaa8e16740f6d830e7f879e12c48ed4db0c01848795fdf2a3b6c0074cca0c30c618e325dad3a2a59ef2f343ed8eb029ae7b05861f5b921f1a50107ca5856b021eb6871aa607d5623c66c4f7a96bef7a22d050f40926258b94293bd49f6139775c0d1b784faa7de83b92b0bac9ca6b71bf7e3a6a01f04faefb48cfedfbf1f05ad88033dd99524a65a49744ad578a2137ad9bfbbefb9a5c907e530511d2a44993264d8b6be231f670e474f0f8163112203c1520d458e32603e4b636664a223d0be7483f3f1298b77f463165f44949796f9ffc621c40f891f8fc06de14bcdddd38ee07268e392f971c3dcbf7e0893411790e7bca6b4a98edc5c9a19942468cfca604715b90bd309e619839092d01550fae586b82dbc30d13bb0106527af60d676037d02095b96ddbb6cc016dd956c9de971190600731f2c0a6e040a38285cad0a6e2da21f6e1a814cd3927ad3e9b0f97b3d6da7ba994a1c2024dd3b60cabc243054d952d7347589517e7e395e48898c27fa74a0b4e236055bcac98518e38c1a4f8ec08a3b2646b213ac2aa085571ed509f224e7cff1111a74a0cb0df7895214c51b2e7839498879abfd8166c0f26650694d25a6152d0c0a49cb9f75eacc1a60c814de1814df9f1ba247b3d682f47115e510e76a8df051decf07594286976a81f488e1dbeb6a2c20eb517c30e940eb4ef08bba107ec061f3a68ea6db41d6137b0b0c357dd1594f8017be282d9f02ac1837d689aa6454868e892849c6c8d8492123fa2b860773bc2a2a0807b09892ae22c32934c2713cab4e261a28a15f3843f1603c40a335ec4ccb162b563c480cc9dc81c8acc8a162ef12510d11f9bd1b2082fc7bb0f21ac1d4b85f92e1e971dd3125f4289ae54922fc29242a40f568e1dbf035185b3e6097f590a881713bd3051778998cb8e2b5204f813dea7499876543157d2b565fbcf1705c246dbbfe2220b8463180d9ead746032788cc6ba2ca1cd078b8f66a495d1c86833ed8c0646f3b271ab9fed4f6a957884b6bf06ead978b6d776b481d9c86c6336a26d4bce9ad3db6cc8f6f7a0c73f98aded7ff2a1796743e329cf42b928136530d94b1eca52bc1d74385f1fccd7880906844966fba3bcc812f2392996d6f6af3e9e726dc4082ae3d9fea8957bb3b2b63f893a84d2d9f62f41cfd4d09a9ae9b20693e883c081d0c540a1b8f9c916733555dc2ef31a896101d19ad78c6b17b1fd77aadc6b146d0bcbf62731344bd00e86dc5f685ddbdf4407436efb130140010080e9120098449f0409dbb245c413fe37c79c73ce66b665934415b688996dcd620aff99b783a635dcfc668b4a649340d1563c2ddbb958a35a892a97a8c226b12d6a93d8966dd9966d59db9a793b684c5b5059e552bb682f7c3cad2ce254335cdef0f5aa552e95a812b568e552892a5125aa4495a812c514fede0e5a13a1b6a0ac19359343fbf084fc2c92aa2d974c4659b4485441cdc864d58c4c2693c964b2569d99a8048f3635a564232400040063160000181008088442a128cad21c53cc0714800b707e426c663a920564510ce3308c61200c008000008821061060983386d2069e1f4c64de4f08c332977187ecf557a73df2adbe09637ec1406646243280c5338ea0f6151d4d458bee17e6a2bb1ec3e8162e66747f776ed62b5cf830d3ef983a6eb4b7e3b0e806367708b2536712d9ab7ef5eee1b793e5b89d988edb9d0e6d86bdfa41890d62c030d317a351aaade4567e2310e8b530db2f5461a58119b29c80e88406389da3010a3422724b483f3b7200fb84be47fe8aba61ce019f726eb9725b037e2c94250014bc0b228340a20d84039c3f98e5f0c118670fb7397890bb77b990461334808911f51244822ca9f23616b1a8b119efc50a397f0073cdf27a0a1b6a9f35ecb98a1cb678627ef2c4cef6715c9690b95a2f02cd6b496f056e88b9bf42cb410ab0acd66a495cba80e944f19cc4059de548c255232a5807404038618759808b1500d86e84cff09f2dfdce2a9176b1b481fcba9983a54ced102a522b99449e9199c52eda636114525868b44c80014dfa6886abf36b32cb5bac73f18689c8a3044e09130b2c0343c69853b3295accbc3f39e95f3661cd3b0ceafabc40d21e269f170643e673a727bc8cd26c154005cb9a9f349a42e0bda38e107227641d204dac0f03884922a8346d240559ea2069fe78f4d361ab49cfd6e6d17f91e899f12979a095345ec7463e0824ab651cb45bae81f070eef046ead5871b5c70c37048af73283d435f3c5340715f6fb1c8e6ab688bf109bf46b42276eba7574bc5c4d438fbe894b458fddd20f8e4f2ceedb2ad354a7fc83c575e9f3f21020523d191181a3a92d06131517924c8748ad531e5aa640267239928c5d34e66d2da3aabbf5ed61a5187865f7d00e753b4050af8d51aaad2dc431540f29c3a204b93e60362c7f3c3a3403c826408cd7a8ca5676ce135c226f11a29fe1d6ac1c86ead57b83df06af829a21d459274b98e0ddca29b1c193d8971e8b1cc10d346b5346348bb26efc3343cbf7a0c12f3a49d628f54e58b8c538e15076fb1e0d41adbbc94f7ae5c99d9f771b083ca2be67bb08697488fd747ed720855b2de955d1a70915d9a47f525b9574a790453ceb4bb806204cb3a78dcf5afb5eb37aa46d4d0003644517e2def6b2cb0f1b80648c61a2cf807ec1b2b0df4face8e460089c81adc9d08daa6001e1de2c5b5a725fee50c05676b839b0d1efef638d005f112242f76c1f8325cb2534811175fe8208776b1cfb4560a69d4d0a95a1319feb63ae10f3731176f164a5b8af98310879d6b6e20a2c27e6081085c4a50775ba0dc0034a59042348b7ed5f8b09a631357c40e93d15bd6b946abf1e61781b9c6e5a616fb2345a8f233140e4b28aa1cf61f347ecd5d7a7392f9a9ef64ccb31e25377d91af4fed9d88d29f89d3b2364f5eac9c0b60fff469fea3e022a4ab2a98b1ea650769f1ad4c8bc52e2ee9a4324ce8244905e9e86cb060f38c87e6a0619dbe8ade0bf4d357171d049d8751b7accee9dad445fe2efa3a99533e0d52acfab5c646e665d3ea993eb98fed9cfbe875f1c156209c2a1cd5aa330c239724a8940e8204b500603c0359994372b0f4619bb73c1eac9cd80ef3322b801ffaf23136699f62f5a19a29e3d815576b9fd16f26314438d1bdbd02d2ec9aff5f81c9c19c054311bcc3d7ee09f304f9e25a5befd63b46020733d49ab0549d51677809b5e5146dc392829bf794b81bcfda6ddfb1ee3913ee7c0ecc5efa0aa209ba40ca9941cf69eeba7b763bf0676c07cf8c36e0cdd4023eb69a65a05f10167b0f7ae783e527eb36f036d8728316fc64a71b7ece5288b71dcd0f52d7c9bfbcc14e523906a828a5eadfc4cffe19470d22be7638cdde0ee7192de19ba5052e335ac0334b1b3c66b48367861678cc6a03cf9c9c008bd9720353d8070dea704163d329dbbcab4e6efc49a5a13d4032053a44acb3e83c14062a0448e2947b81f31463cb738cda763f84628e4cc717742752326a80727de34cb63a3a6ea016e86144b7da94b193a16137476b06551b623476d8b1c2e0043520082317fd2e67cb269638e4fc78cbbe406893821247e9dee2702c52d0b17e0dbfcab3d4b50f41fd02aeea0a8dae048f41646a628d04288f6cf11fa5ac36bd1667d81456eef0d6eb9862096370e475f3f0965e0589de29e8b44e2df0d28cdd931de2a7bb92a3852bcd4230bd8fc5461cbfeb21b05cb46542a919b2aac8d2a3ce5d5cd0459af4e3c97e7527bcadf82ff1d233574819c0efc401893c344acc938966feae7d65539a13cc35620213720e858a3992a0010b462af84ef6e1a5c6bb62d7dce2290b483b2da385460d1033eae8e2ba1320a3d8971bbb7dda3929b8eef85c740ba852fbf32818024759e8b8561aef408466289108972b3084d892bf745b62df80bee1ed9bf86785f3d6e75f64f15b2dfa078715593dcb1121df0b780d9e2c5802099d8245a744a65a08ac532ce1e3277bd7002f17ff14cb0acdb501b9aaab43d7e49172290bd6561d16adf57d60d585ef9579abf365171c0a0bec7bb5cc7b086eca98101addf7a063a27342d4433b3f4f88e72140338c8991b3112b4cd014f1c85675be32382694bd0f66c0e38740d11b28a5a1ce3c37bba16dc15cf57550bd05980f5035e3bd5eaf5d733af116738b90d37f23c139c08da1dd6262de8867a6b4ec3c0da6c8d055821ee6927732838a471b668ff35975f98d7af1f7c135d3d00079bdf5fa9cb9dd30428d22cfe0872adaadcce3ad10e04681d3590633b73510ace6638df06fa0167c3e80d5e86e2ab77a48e2812da2633954261e044d405b2ce2efb76931e03087f433d58a52ccb769d94d4f03f422fc15503935f2557f4f04c2c9760d18a164191cfa04c64366c9ff11363704111f3ebfbc7d70378ae644fe5d6c1c11c4abd7715ede3ecbb6991b89511db0d59093b6d54045c097e9915162e66a226e3048dd9e3af6cbd996c0bb70b785104ec142e940431c57ddf8dcc071644cedc01ac95a1dedcc742c08c7fe9f39e9bbb726258f071fcb7589e293d8c578990dc1e68c23044afddc8ddbb6110e60e52fc627b6d8cd4473e5aaa25f5af06c8f88813c60ffc9f89b6d5226220ad877ead741bf7d3bd6b7dfe4fe199d3cde4d68ecb56f7757821f485a7520f46e9e05217ca90a39d05034afe3d908d11931810127e7bffbe8fdaf01f8c2e087652d47c0cd3504b32b3a5865da510ec5654a8f796770397974e49024e4948068767af51ecd2fdb470dbf7935580537113a547770a34c8467a4097e9c8182f373e07c1fa11e4fee9900e81e56d6501569c4d9ce627868fd576570dcf4b978ad38fdaff70f491c5954965621795c7586d1e527f4394ec1c0e85bf1b674dbbe4e94de86dc0beca021205b16c991905d6a7e4462e8a8b197e17739dad4f16f637ffeaae6b891bd626ce742f3b8cdad2ee36a46845521610144502097866ed9ae0cc31e0e670f86670f83b107c781f603dac368ae974de0787a078304fa4d3a0d5e61ec5976ca1a18f8bf98c56abb5a73de5f6f69e1e9a117fee257132a4b1b971e735ad9974cede204664a1bfd2862ed34ee053a2d7c5df4d35f9b18cc2adb3263409a4bf46853086ea20376598b278e6d990314ca3cd2327c156fd9655b205117092189e2217d7d2ab8e2b35d07afb2b9bf82cfaca22e36ecad9cad0e323f4fd28866963777f607e62589b5989cd1c90ebd50f20be71e32cf5ac494cc7ecd48cc79a618eaffe4dd376a8c4ba4f7ca37452a990cfb2e62c351b44bc1ad77bf7f914a972827091d5f4724637f14f50db4aa5bb62d4fafca45402a8f26d224088415b967855878745372c5722b0e40c4870c8205f65758973fe73c181fcb40b05b38f66795ec5e61ddc89cb5c97331908f879ccd0e4430f87c8a074cb2f883cb31b42d2002a74e67a1d755c714ec215f931323916f70bcadb7ba85867601f5ef2dd773c90102260b06f3644ee0be19b2abd7c08010cb9a7b68c6144dcd8e4b2bf7a608a626c79dbcd4225a797022b8e7c8dc2b9f8be2799c9985feb2eda80c264893e4d86bf0461cee331b044d2bc20e76a45b90fec767e09d90c95af8189362073c14303152a8e962e16158ab48c7eba3420648dba0027530ec800ce20858e0d8efc347385472df40a191aa1c437294daf02b5239f25a314d096b372f30dd56bda5abc9085022e384042c6f4bc7f5a6a6200016606679fba2e1d97394cd80b82d115ff9127443a0f90a705f08476fb1cf0dd735fe9f901811d30dd29efe418c2a92cce93262bbcbd9a0bf7e99611a53cdaacb642f424471b21d65286465adca4936d6937c3d0d4533eb3f7a27f1d0d74cfe43283766adaa048d4bf74faefc32b58deaa46b716a3edb43422f2b5e35f7e306e2ca96ca199415c84a1907d7502889574f18a3701b240efd8730e5d205efe8a93e6a6e3148626d5c3dfb1a8786d850fe076e97f4733421c510e197ff5d364a284d109c7bb67d7a7b7062dd32ef221405a6069cacc17f6c2e9aa7b5100fd09b56de17cc7da7fca342a831b8de7e079b5828296777c04bf01370bcb4075fa0fd80c1c7c952e253f1a52b3522624f2a12000c5b1e64d28cc518be37231e8e82aea1d4ad46a7600d635269f4bbe0333d32ea3b941c1311ee5f1056aa38e5d3dba41ab8647e0bc60f454bd27c8a3c968b75f47ec1022fbb80e66670ae8fde045dc817614b92639a65aa5cdbb07e822026395686ed1ee7566a8e10d2c55fe3a97d94b6201d1474b95956a30990bb5e54be8d743eab2b451ace799fd84eda449892198513bd92b4406e621da3c476b4bf6181233b2a6ef68afdbdb59af149d553aee48baee535e11b06af93a432baae01ec5b25707ff2612a250deecab183892c35c47dad5860d4e2112ccff53bd4f10c8fe2268a995433cbbc078594cf408c3161a43cedd286b5018749e34742c24730c57f0d741eb41e492babfc5a87d03224e20e131cc821f14e2a3429cf9940969a4c42cc6349f44594550df6f2e2702699e75b0b9da992cf76645122c0fa134dd3dcbe19ecb510d0e90890bce2e0a41711786e22dc0fe45a49834d57917dffe348f9042b0ec679ba061282d120c97f5d256ef8035c0ad3d6ab9550cc008169f86b4d2d46d36c2d9744fbb3ecfb961ccb72a67dfc08cb33fc00ff2d915db114959cc6a2e4734216fecc9d8745b39fa526a341b73dde08228e689ec5e6438a094f8408832c70d812349b76f42510e4d52cf9ca3575b42e29c1f1c61ca515272ec9614c788ad5df4edaa3b60940709bd7a417c8a8d3509001b924becf2ff08d8730819e730da639aa831afe50227e1db61680806c013ca65150a3b35c0b791712c76ba0b96603eb9807be6b68400f5b51c49db0799a62dc3036e3ce021c2d07118f702c0aad6bca69b2100b5320452ff89cc7ad7e2c49d1fbc865d8cdc0e5291679604a7886120ad08f808a644ff39f614e946e93f2f8b4f64c3d632220d92964ff1b73341cfead3ee746db136aa6c3848635e285a4657aeb27f912560bd06cbd1a179600cf25b2e8b76c7eb5cbe399cd5fd81c2fbef71c50926e3a402e90d4b2be9a4feba63ad61d195f07c21f62005ca0b986a1eb01a0ab7c88e7cd6d0b2da1245f87e60a730a0ce073e2ca53e510e8790347997143890048f00c8b7407c53adf6e049bec437d269f4f4e11145c8a27e11d92c2c24da6ca9c50fb24850711fa66194b2a69937af81bfdffdf786281bbbb0aca6850874b9eb6e2d774b08d5b69eff1ab91bc28bf2376f43d2635f139398a9e0856fc82a8c577a52ff8337ed9aafd6c4e9e89735a16919a06bb25af432cfee26fca0cc6fffa341348661c171909b81a3fce15b3b0c60f75b69f8735a489cf33796666af54a7a8a1a0054afc3b2dc0203115e80a67c5b8ce1f8006502b92d0445662d2202dc2d124c3ab0fcab79b72559a59b33254682402d0066f11ac4383207e4fca6b84528581a05211b4e081502ab4a63e094711e2599d4f5088c4ee75bcd9a695a277bc0344f66a8b6ca2443a52eb0758c3c0e2be28de29a50b5b3b4020126f42587452a444472872e2f94fe3c9d2272d35e76bca32beab3b17f10405a0a49b3fffa1016a0a0462c218b587d913088ebe3803e03c24f370fbaeb2a00dec2c95a270e59f27e10dbda2e0b03ee5979e2f2bd4555800cf0516f972d835d98f6cae427ddd7099e0b226bee3b372f652fcdb7ada35f67ed8625ada0482bb6269acd14aae260d00f88fa3098fece7c38f7e75bc6b9fe2a43fa92e3e4a63c6e66d719338e0568d46c0a306f5ecf8dcef552abb45d74c15acf5e574b6c2affacc698b627a736da32893de69d26cd6f5e7d7b73dd13b1bf507099b1a8d81318e9f245683d40af4298be05dd94430227e4e9234786433dd9f61f04f32bb17fbb1048e2acd0198490adc4932cb40edd0d9190ce67476d62708c7d4c93010c943c15769a9c4ed6192b24ef2d357413654f7900fc8f9da465d0c1302600081648af35504a7ba4c044fcf6f468f95cce91a6235639c7ca6ade25fe1ca0b33557d8bf187a3838a80be92cdc2f7ae14fe2b2cd097855d63f40ddda41758f270e3b345d43cf1b13eca1cea6acfc9f1475cbac0e8dfc78eae4a8be52936e18f05874d20a2c4136dd228b17e1ff9cfff8f831e487a33b003f214ad0f35515a325096817e79d0c09df2536404ebd1c18476df269c1cc2c35a4b1645f8134cdc281cf4a5c770df66f890fbffc457d5a0880a2da01e44594aa82aa26c0b9830e900016822645aca7268f440b4fb3c58c9eb828b1ee676e61c0386e2cc105c1e2cd7691d861805d8b1b38649281323266789fc336d485fe922af0096076cad65cd171971ac18026d901dc102134fb5ae71ac72312916b11aac6c707dd0d2871a6911861262f8b036ab29042d1762f133d8031aabb3568b1ae12b187a0c8d81760e6178cd6040b7efce5adc4dd662fbcc1b2d4350500d158f22a5fcf1609f9285e344c9ddec7f731de765e8772dbf1a29009821a5f2c6978be3699cdc94eb1db336025b0df0b67a9cadab22a3b544026fecd4253d5bd6bd1d4bbc534c8bc848da3f1d4ba5db507cb209bffdf9285635e98121e253069ad661292e3fdb9e9902f673dd41090b6fa846c31f70d84e752d0149e8135b785f1cec21744e89082a2522fa66f9b02fb80f017e58454b87b28122595fa27fb6c22d67330a573c3480a2f08faad66730baba431cca19833d862e23f1da84cdd9ddc9416dfe141f7132256551e9c2311740d10bd12c3391652a982585740dc6c3db38a18a3eb2092ae157b0ee8974ca28e40a0b5700d0f2592d5913308853ce6308d5a027b8d14e6dd0e7c5f104a8742fb01c94b086459e85155f22e80ac6d9b5acaca6647048805b2a1ae17b61dff124ed86f897b10d80a00de1ad710264b1a4d3217ecf9e6f1e2ad3ab62cef2c5de0aa07236b3612bd351c4850aaf4943cf420bd464a114eb9764b90ba6a590eb326859d68039191153c82aa572ea1e6737c5560ce222eac736149f64959c44bd80d3b6801be6848244d32a7801605370f64e718bbb90396213ea4ffac46811163e9365379fe2f4ca096a6e3f82bd1458e7aafb48b5235936e7e15f1a85641d15fb68844f4ed43194b963090a7e46e3058d53c337d4b83f35de6aebd5167db41a9b9ee7fd4768d2dc1951a842ee54680d4377fe3c7ab3d3334da818aa6a4a9285ce36ec87fb6eaca23ae86ec956e4f3e07908187009e715746e6a58c66ebd198aadc5320ae2be8e8292d0aaf0bf2736410222adcbccc518c71506f8096ef1e0160f558016f0e5d5401b88730412c8bebacada68e96a20d609b8b8506445ae397d9299def13b4c1735701f1537ba040620b310cd329359a68259d29b769365a527fcda20a7c1e5f60e59c42af66ae266d352dcbd8c37cabd74defbeace9e1cc3c1b6ae1c7eb5e2fb716776331a1d751907401b3e666d5aba5b80254d43051790bd24839ea526b0491884424209fda3bb475c5625143c14f77ce3cd96e31d0afa7fa3c291e8ca593a6af1814311a7e60e441289677fdcab1f505d90ceb328b41e47c5dcfae725e123e38d70eeeee28877ca313deff441d9345654cb941b260b963d516879b5d125dc3c02067b248582080c4b5002770155fdce47debe6765548d9132f2daa00378645482664415151a723092e6cd07542b5b18fca0e16b831613316dc0d5dbab7abc26604edcea05aaccf3725d0e2166ed7f5eba3c4efa512ee1075264fec06d91ae3629345170b31f6556c897924c69963b605a0bb92c839e65ad3013cc348fe2a3ec649eff2c98daa1d1b6f4db2c68890706c8b7d6d0850c2975d7dd8bb8683039f079118583631c483c77560d6b9fab83cacce7b76705e856527b71630670b2b485309211e81197b6e3a69960f47cb5cfb6c4be3b7ea1cc5a086864023c3a35a74e832ab4df20ef9ae243cfe79cc7f73135b59fc1bede16fceac778c9b693c1a72c369c3610ef4672b599a031a7c5772d66a396419d942f0f587b1ec84ecdc65152d3217fbc050cce1617be016a642a973d8be821825fe21f967adf8c1b280b55fc781db11593ec6257b6764b5f71e1e6425a9c387a2f194820d883f06d703bcdb8a719fff7a2925b5a52ad27d3f5fda549a41b84df6da763937c9e3ea8f3abd448473f2526fac2517d378ce68538d2853748f542df0d1903b3ca74915d732c770b11b715f48e8b9e349a4e394e6a1c8864de5ca036397e667e37d146723c90ff42bebbd11538dc1b72600a628f2d39d41cd5e7e003168599b8f47a0b840a781ce50923463da24b8a04076578ec37e63094a99274f491bdfa906eb4a7f7e07453ec6b8c88aa655e541ead6247185bc02113af995bf0150f1796b72fd1cc3e0b2f77a2b47fb343a254e0b69054ffad6dc0458b7a76d20421a90b26947ec0246444d7a8679ddefbdff540a9b9452f3a7064d04583f60cf1bf922d45ef66a4e58eb4e7cf9c513ea3efa682492a0548f38932862256e3b41c2c19661ad46321a1839cd6424b11a165c34a81bcea55c52958324c715a62a42c04aee9642430ad9fa3b29e4597b6074eeebabfa3b0e496f11909f9786ab26d6561a0b7c61a03a8890d7866366bcd28ad7c072646dcd90c979791f72084229a85aded92126f1f04e8efde134192152cd74e742c150da56b2ea362a08de78de2385f007adfd1240d98c7390e761035628fe3ce24e7b766db1437dae18baee26f735c4d58e65aaef5649d186d200cab02ca0d99c8868218095aed3dcc0f25e17b0aba76568464a37fbacb472b25eb0da94ea2636976fa0f715a57282aecc1399a4cfdd32e55072b8d3954a9fc2acfec8a7309b28625101fe2c153a58cae7891e4f984286b080b0dec2ae5a493151d4843c72acc34487a81664ba37f32b569ee55e1145d26b84e78513426f517609b2d292c22efdc8ea28b9e8c2e0314aaed1043d072fff49a45bab8e954b783c191a7f0302f33d681dcd13ff43cf604716541948ad43a6857b05a726fa54fe4d266bd439ca81f530d5d54a7e28cfbc4cfa797ae939a9f9eca414dbc50ebb38b2600a347ea61a6ffaa2d264b6bfd8cf2d9b93314a1b8d3ee16941bf1cc0a543abbb31bcd446abb98e03c6fde2cb256a795858300a198f0f1cb3ca19b868f206d6cfed85404431a55fc1eb99a97322b7c1e8013a51d7e80e87fd2c031eb66f43e4d9de16185a8663a604fe41020b260627514cbe1fb8cf77fe562e74334c8591e21f73a3183a4a2f9871c90ac1e43b16c331b00a4b1725c08426972f4a84d4a6c665da5fe2a1c300aae85b8f25dfd2973ecb48e476eb1bc25b3de7c0380332a376bafc37bf4a099176ec8d56293def9f9b43d944a8d9ee0e4de2c4a28af4e74dd39296772e8b9df0e293e26f64caf69079129dd32ba6a1279a451c5a5a223dc28547e60ea85b749b29540ae8b043a73710b5e2031723b4961fe1d9702be6228d539d495c983f0f3cfdfda24ea8830cccb65d0acd89ddf6ebc914a26c74941069cabd1009d5284066d9734bfd60332bc1e97d31003b0586ad23b19af576c29fa64725276b38140e4a5f3c0425485ef757a5277d9bcf0210ba5bc7414cde64613c6e5db3bc6e2c0af087dad9e8d26bbe275bafeb46f0061574c7aa49f2815cee656cfffb832dd3b482f0370b0d352574027986ba6d8ef910cd1d4e58a5cb7d2609d6207fb52000ee5f080aae46e212f6d35e94ce1f469aa4b3e976c8e5266f6c97ba63a2c1a0085deab15caf854f4b48113b9eaad9ea31504c7a15dae0a5c7524ea015b64b1320c185c28a42bde10211196c98da77de9b25643a36629a2562bc7f425becefed12d5d71b380496312cc13131d61ff6b1f1da88b89cad78af17e37d3fc4b3af07a5aff05c68bae833367fba4075e8ec5512c743c6a97cc68a15649edec01ac8d82af12d12b961f9c0ce2a6105023cfdaae9e1f24b6b5769c007e32b88891644049fab5f7e2165dd4bce7b7f62ab9df1d34e0175ffafffe56e62d38913c2aab56556883ada12acbad68b3aec44ae5cf70627c87e088676c2ef41b6a09aba9ff061f20b78bf90cef72152df4029df920ad11b0cacee4c5dc7a3aa98fe0789fa4261e7ac77e9387116a5e9e3cc9b4321f7f9649a6daf08b73929a31b3b35df43f6893d36b9b18dc704d67973af6c4a8e54d11e05e9098dadb778ad510539da99ea28407453a53458d46ac4f183a363b5a50d45aa7d9ecf6d8dfec6a09963b820358d7342c333d08cc7b1f4e2df66794e309a29e5b9e30033f8430a4eb0c89487e92fad0c0a3f7a893a67858b22c4c0b5bd43e0cb46937c810a6588b27631f6933ea1a1ee1f34b11b82281d444046f863c79ae2ef303cb18365a6122144ea96cfb412ee43a5b5a55108270a04718748dae4e7442311ae4da1b8583f74194810c571ed0e25ae0043fa12aac88ed410f07e7311054ce7dba2e712b0ba103930e465fbd88dd43e05e78721b6c95016c22b4e4d231c7cbb96c5214e82e9571a0329a49adb303abc3a463a9f798e6745e2e5dce6929bcd7ffa5b56a341150f645468df74f05c0c6daf601d6f5f3713ce32077398803d31168ede80a7092357c94ef0a64a15d712266811f300e6ca2d300dcd6817c8cd1d400089f36f3caa86e466a008a763ef82961059e81175d3a6673253b6b0e846fc9f08af3e07ad1a5836d2f0025cdbfa18e44512be2a9ede429e437623dcb633c929430eb0e9bc36be521d0389df0882ead561e46578763ddd95b995eb03ef50ec0aa75d17c0972323e66398564114c0c225b780733270ff4f0efccb09637a80f7166d8b98cdcd04ad0ec69dac42c1b1d8d9791dc6aff3aa9d5ef5c8cb9f918b5013da4d59025f5d2f6656c0386c4e9d68e8389b77e81ae53b351824104e963c68aa0d87a708111dc5d968bb2f889779b9633da9e9dc839819828777144eb2c1d0b7d60ebdc0f855e446bc82aca7ce1973d35a16629aa465814248db3fad62d4a5b3ffc1ba8d7f2a0ae7642638ad9a5b22c227dacfb1bc082e078f44e149a9840fa917c8486fdb68eeea06a5708a805a378bf0631ec1556b3c1430a7f210969061bccb2d943b138e639e6429e283a674ec98b47ace7522ded4af89def11aa7003111522d9badc3fbafdaf8ea9eea016230f42ec776a5276f252d91376eb174d4e1998ecae7c477c48adc75d2c002926d5f21a9d513fcaf35cb9c7d6f3abf376694cb064a69535a58e56e9d02816fa2cce1bd6f0223549680e22e7c2aed350a26d4a69b1050328205b73abc80d609c90216e2f03f734383f63b99eaa47392f4d55a52fa0dd6de61beb65949233ef71171f9bf387c51241e72a124fcb9b9ffe3dca0d97ce3dfd9eea29241744d701cd389462f5b73ae226064c400f00e44c04259d737827543398d4226da9be70ff30f8e15a598fbf7f676f1173b39d6b47ea082101de817e85388dbb25b1baf04154a52eb0b2c748ec2b05869c1381f552b2c762ac7a73838c933f5af68d20c04b1d091c043d029c57c6349c87b3b181e8333501abd3b4f9ddbe8b0296976d159b9c076aa521b6978744aec88d0fc72fac05094c809f377e9209e362ba86182ab25ea5648c6f4f79180a3b09310c4a4cf64267546d93b2b9a100b482239513d87fc614df7a527ab6b6d4ba3311cef7a6968aa75dae87c4b49eccf7990c2f8e445d4233425236f094d4dfca6e904c9281d04c0b5bd66934fb1fd7baac838621d62ff27841d307cc7aa1efbe03ec3f165ea5aa30812e4f12822b129fcd5518c8d983c5a9341f52077fad0caab2f42d07a837419e868fc11f83c59025b76c7c3119a4318e67be02ed141ed326d6db25223b7e4795fe913cd340b76df777caa4e95c952107c6d2dad88f28f486be23e94fdb50a42739cd5639c89ed80f7dcf9f709c4571272dddbee2d7fc0ad02fd58d80a871cd76687414def08cc049349a98dc2d20187b3d0d5184c9df89dffcf4a6556659e97572a74877e32741d080db130b780c6e7e0c5bf6eeb3322cfb9ffd67ef08990f2b80d24d22aa7c42b8d805eb5c4b5c5fdd5515d29b2b83b751aa567145ad7e24e5f0d8b9f91e4ac9e202cdd2c35466be9ce0da09048d3e0c3fd200f15c3590023d67c6fc73c60cc159d278138de92af0ba8071a8cc925a8382b6c2d0a5b4e918f46df41c22175f7e36ce61ddebe94cc1ffd9c9252ca0527f11d5dac1311bf8d56f8042a394c6774fdf1c4e40f13b04ad7853228bc9e7b9066a64ed90912ade7586242c8ce14ce19c028ba9e8755c90055d6bd93beb54aed5a9684f87a020d397e3f5a690206d283a17c4b26e895efddee6789b61c66f0f570583f71957d469236a2b1ec51081efa3073f46d1aadeb2411a0e99c209c48d453411d63c269f68d86c2806cdca410ca01f5221e2c333bca5c566288cdb5cd5c1f431364eae3718bc1d40279171b201172283f6b9281983eb45a305606d5b56cc1b8208d687ef9fd56627dabdd11b6cf5c456e4ee245028c7b58ed9221039a6ebf83a139743f1444d44b8d168b95bd011def9af51be144be3dfb3f8d4bf9beaaa97592ac37ba1800c5a5e24fae2be27a1d4fecebb78d38efe5f7ea15a913919ff43932cd6580eb54bc81103c1312d5f661276828601bce0ef47f272a5f70673f12a7f1cd0d866f43f0be2819c1e54c7d1fa9fa146e8c3965f1f52fddae0b383b0c623b739084ac871bdad0594d26dd3226ef4af0121c51645b9eccc97cd7fa3a5400ba2a9ff9c01e06741ec3378c24d70c752ef4ae7f56d5c1c69b07128f18f09ff2d2550156c378bba4dea111c550544e66ae2251f7300149a32d2d8c6720ded88a6034064c52284262b815776868e29882fd1ec42dc9215244a0c1b72243a38634bad4c5ad082f65d81c05a4032aaeeb20b28091b08d6a933cb837829c212af64e210c84763b405d189e01cf6f26a7d39083547802994cc76d01bd28ebfadf26bdb63bb35f6f8af6847681c636ac05a8f62272a36b6f27175c1f36c2e7bc31e8e6f185b36934503006bb960ae7f772ff9c03e8b3075e20456471aabb8174163de8f429e04aba8f8b28d175608b91016b7426649ca4109f3f69d672af37f790567f4ab26666aff67f2e7de4777fb670bec531f302d0c8c04b61499b8bc1522d20cdeaa6f9c51e85a851f043675973eaff8342b0685ff7f7e36e92ce7de24983a2e5367509cfa2754ba02001360e7ee430d320463f6c11d09ccd1ef419f50d38d2d3e8e4de9e71abd2dc9a63dc16551159bd9d3093be409b7bf7ad0bee632fd1e13efd27d7da4e243396f73dfabdfab49779efb2344e2d667f03f0a45f7c6366b667837a1e2791437d9ab86aa394326c4bbe9ec91dfbdb36824cad12b7701bf7f16a41009f7def545dc526c32fc6cd50cc2ff74da20af8888416cb8d5974ac64b7a1a82be0720162da3fd3f115812544c14090f68796186a176430034ee19187176e6f7ad377c0012c06133484150f40ad9efcbdbaac6fc2917ef7e023ad0009c6c380172a476dc241d2067dc2f90c1bec59e08238453e3bba4b0d621e1532d19a62506d006bf996c9b133bdbbb8f7d0ac16d63da69b9fd6c68b5d20e04f20b9776cc440fcb79fcdb7d5394c1dff9465da1f26dd6c67ea70f14d5cf6b13407576e34b326f117c5ae82d7c2894a6db0a398be0f172bccb4ed52e9d9b6eb83c69e01bedf6fe73816deeab21ff602eaaee512512f54be744f97d64654ab2b05dced1b2884ee19a8db1df65c9de5e6f522c1af36c8f64ac104272644bf15b0be68e549d53100d081f57708d0c7cc2be718c49fdc6df283cc5cddacfdcadc615497f5cd82a6ad42286de3c308a20e692a9d0c384654883da746b778dcb2a036a40e66a127bc65e3ceb4b9b43b04859bd9655453bda5d4cad952758f7ec48138168551707ddad4344e07c9b3ec66d158c7c0bd836f4c8da3f636c0c48c7e4af707442cc18fba901885c4ea0f1c91640bca41ab17ed655c01b24af953e3986758454c2c22685d65efb86c6a2e1d5645d560dbaaa73936dc565ef6a54130b2f63f40f8001423766fe12b349cf1ba72f15c819409bbcc488c32587c6d024455bb6781a232ac54b09501f603d2883bef591ccf72f3ebc64674a59ffd278cfe4c7eb142b03781463e950426cb0ea76aa6aad8015620a5f8d1eb6c71c2212b66524e0be6e4f86c825dd312d59126bc7123800a94f8f158b7ea5d3fb930f2f581e82db205bd62b9a4297ac59017ce87d64ead51177e5a2572d355241119566b12479e79b00bb455c55f17fa42bc83e284146661df197e78133ecc122dabe4dd88dd1659909a2e37ebf41b2bb427116246b2507a5cbd637a6e993fc09987f346d58bfb45039535cfd487b591c54bfdf8301beaec17bc8329b4227d3f03a4b49fb408530ae0ccee7a931a9ca07693970eeb8402d3861a56a48b9f85a15d40d0592a9dae84cdb93872c4ffa028a13f81b948844975410a4da85805f861f93656e025e5da1005b15087d667fb947754887c90dfe6aeef15aeef79544c155efcaa35323ed92cea4396b9841f7f2e6a626807517a69ea4be192715caf59af4711f5299051455fa6ad27f82254a8a8d6bf1af6662f747551ef34a5a8e4ad1138839967597868bded57a254fbcfa69d4178334c9188ef076990a4e12d0dbd5e7919006b8c200f8600179914fb204095abc09cd850fd52859ed3a0b01d99dd72aad90b3eb8f52b8cae5ade29438f2f964fd0702574ff546383df8fbd4b3b25fd4d3066fabd3f1dc1a1cb174fef2bcea5b91cbb366d28868edaf6254cdfbd5d6aa179f5ee69609378bf77f1ebd1b6b243ab40d00adc59be951c0105d0bf2bcf5a3262681f56c227063570124df775c8b92b48f54757be376799fdc3222912938808ca75c6f79fc898f8b1a5c2004abce27395f98ab802857ccfe6cdb23e8b4cdb72b3eb2d2127a65f624e8e9c412b416399d19d3aff98b985abe1d2cf4d912174a51e1665ffab097ca4444602cca6e8f321304d54ff7253994147d3cee608a7458e807ffd6e47a1d146222fa394e9e2326fd0214c5a866f9ada948ec269ecb7e7f29c93c9199f13b0e4994025ec86d174cca80cabdc335015d0560469e9069b61642501bc2d81b24f3313253f3da0dcc64a23cb2e2bb937a6a19605ad7d4cc0bdb5a2924a020099627709c172529b155fa6c6bbbb68468e9b081b97368667cfa519bdf9e9ba999548cbf799973ae52ec485bd982161937f0d3e02bdb57a9e7f2240a4bdc339057e29acf3e70574c212797b19cb1c663c796d7114b926f9c885bdc352c38b875f59c92872b302fcfe6ea19d56516d2a1c15fce6d307058dd79f525a60a2c6620e8925e3d144b74c4e12c9dc045e924760bae39d3e33fe1ba2dd72a973e58fc6e2148094381a85557d4a9ae7cdeb520ed4d34e2d189899ace2272b3bcf89b8e01febfb597c8432e643628af8ed39d84b30416f16372a6381970c5cd8b55fae5004ac7f68a1257c411ff10956691a96c3d786a2de5c446fbac1054458c4eb444745a144ff36cbe5980c9dac0014919457c59e6455b7c683cd3f9ac5b0ed740bbba923decf82425fdb76ae3644814878dc98c9e5f84b5750c78adb3ef82111c957230e2760c250cacef056183444b3045f0125fb6cc49223b8e9aa23c3683ca8e7eccbae955b6589048fff3965748dbb2abfa48d4a667979bfe4788d7cce7c9dfe0eeab8c7c88ac5ad61afff8f2f890c9f35a6781585c2b9c753c9fac92b7cf1d4623d736f356722af7ae1c2108e3e6d9c7afac9f02aa480d722f90e142f2e8b4976a699c775c1a76a6fa808f1450c9f0c9c49b4082ca586d33bdaa102cb96bea628c511c7542a2ff856bb74235dd4f51d833f00cca2d3c30c98983071bc8c30618c7be65dec3c0160e4748f6c72b2976f05182ca1ed9e771a3cd8c86faabbacff263cc4cebd523c6aab0146564cb905ca9d427b289c031a410adea0b37f4c5557afd4e0dd75e5bf6e8e7fffdbfbe2060062dbd8cb55cd72b6b21b4d6e2eb4e3ecc4b77df77e83836acb38a25d48d2a5afffc4e4d51daf6c478a2e4c49f7663d537b60e158818be8d45d54106dcb6ba76019739e1bdb34d6754f5884fac1e0abef69423c1bc8c5e875edf2a29127045e557ebc8e8680978e9c6e2d244b31b1ce5c56a7657737dc278a7f83943d4b5938d5c3766340c4d7ec3d947db261dd2ddcc5453e9614bd16ad087ddf99f37ee8f9f87fbe13a67ca4612f58030892134ad03c908f328268b66d9662b620e8161f384fff3ef1917201fde85c12205244cd846f4cb1b178552558e7f7b326a83a5dc9e2179ace334963a2310487b32132b27f55aaf074bcb094cecd6f168012427e2a086829245094b563cf650c88ff12a22e583efd281bac8855191659d148bb043c95548bbf9331ef4b4b9375c9c9f3252d62349d33d47850b704eb589be56977c3401abe69e812764470558169355c7f9bd83f842134d028feb1029519978789203a67f683871d31aaf90a104cefa5dc5ef469f81cb2dcc2095316ae4007f541f6bcdc506e389f2d587bac245045a92b3278882b3cefbc656e0bd27db99b47d9b1da08f43a33f3089423d14b7695e76c5b53dab24172a628a7e5a8b6e7222fccb3ab9ea1323ca9c2fcb82171d7825c2e0ac60fff308c5756f819c32abb1f9f3487d37e5e3539f5a2be99cf683c6d39fb3f5c818a0c312b0f80701b9a5537ae2461dc3e58a9641a2b301f8523f054f927fb94abc7c49ec3f020e5088e50eb45402a64eab295c6cac2f8bfaafd8d3d24131edb74f2906b3a2cb1933da99bcf10bda6d5f70f2138222c7c396915228a60b52227f3eaa84e3a97ea970278796b917a0179acd9ccd3570999472192409f1c102a03484c5872f0005a0e90cb1d7b0a6b6df14fdf938524c3be6a38f25b8b3159e87e8810ff31e9e57c62a8377893b3a785b76416fe7f41f7dbb2206ff22d73c31c003cc273f9e87ea74a67c38f49e75ce9a1149efcfa72749c231a5e3113a8b2caee97472040f6d08fe88a6e1a87d5414fce80297430dd7a2d894a3fcb1b1fed0a73918f20313f8c3339891a3995b938670fd312ebb6ac810662ae038136f0e5a4df6a7e781ab425d449a5f0797e4d36c216f39f817d0ea80a3395edfa2e4b6a40b282934ab0279f75a40ee282519a743dc2424a2cfb3164581b28e62671400504549af752b3dee58719ae0a701d4306039454bdbc0a00c20e682dced10a62d67327ab72c960982ff1f35421fb4c040618dc9cfa29228fce18a4495d4195efbb10cc278a2d80ee5793353a7b31dd33b30c84a4678d5bf36bc084351cdba1f6ddfd65521306904d35a7bd7cd363a6ed2270b00273f70a170baf7e894b39d4239d214d39d5c3c29ccf3385acb2bcde71fc000f524363b4742981eb36d3528fcf8947631e8ba26c476c92aa4c53e049e81524a11288b2a8bad9c14250e7ef1c238b1862cd3890df6593c3092dcdb0047a1431a7e6010bc4ca9648f1c5509667975918d9a95e8df66437ae995ea319da26a5820f4dd853d55f612a8f30df7ebd7d4de447686b46f4253a6f824350ba7709a03053656d8113f1d241918e1e146eaacd0b52a8c4aab62cf9ea6544995fbeaaac186f44cc501c6bb11f9d1480161340c36b6456a1697b2edf5e374e343680f24b60ef944de98e4bb5cfb1ddd7d761b416ff0a76ef05ee907e3e8da75d4b87c307ff0dee1c0491777d4a038599cb01da42efbcd9ccacb1104070ebef5e2a32f17850c28e095dfcdf54d1ab6c72e2792d787d3a267ec711437e4b83c99eb5afdcb0c4ae54db242f8f716e93ef6b40bf4a401fa860ad0ae5bfcd174bc00526b579f63cd38ce245d4f4b7736362e5b170b52a86ddbaede480826b3b188e6a352beb36e67b3176d9f6f82e3c3c4fd33146350ca00fd798962f3a12a332932ad30cb6d4062a4c211782e6e89377d29cf4f2d4fe81e5cdbc3c85a8a604b6a5e9e91998456a2ad44dcd3afe415f0b452de921151fd12d7f46e4d2552cd144a35429b226a40d84278dd5e312762efd6cbbd7fce5f4cc0c8c8b71c4f6aca6a64b9ae70db78d58d1c110ea390973fab6e29fbfc0ba9f949a34876a63ce40ae913e47dc4810366014d4112a7cea6fd2e3639834064ff94686c81f3ad81f004c0cf0dab9c62d29b876aef0572f67c1559f78b72f9cd416bbc6716405afaaf7ff92041def8e15bede77b98868f0a15ba5ef80378ada40a9d06135f653bf89c0bbb3737cfd6582fedac1e053b3daad0799b0f4460b451bda4c9f3e2c6e014a0516ceb1f16d7d53066b572926be5fd433185975c4c7e91b6ee9c3ff8bd2203facde85a561c7f65cc496c95b741121817b539373694f3e04555806464372f6d6633751e965b3c2221458b64c7e362822e4438b68e0dcca1fd0380f15d1f79213294daeb5d7876ae85acfd303be9fccaf3afc4f568b520422355e2a075c82191f6843ac1e32647855c978e5c368cc624c423cd13835df9f2d8034f9ccbcf6ac272b80897ce6239ae4bc0e0c4e5c3754fcdef2b44196e41d0a27b24943e7569a9511fa18a945bdf28f2f17605ba7019448bb7e8ef36b4228a488bd0546b7bb7d79bc5e30160b1fc8b13da2baa7dc002f03b01609df64ab8e4b921c779120e00568f52bb56b94384be60dfab1525b559404999bc64c6aadd4f7aee56cd9241f7d2868fd895964e292e63421a327bddc99ac7661373ba514f555b9eee6b5c59d21a83188bebc625b6a5acbd7c5ba468c00101ea7cbe255e3d201310b7c40dbda42bac2ac222d18d38e4d030eab247fd3c0e4a43ee37d709bf385ab366dee2d4013c4a73d99227ae151aa140574736bae551b2acec018425cf6470c4aeeae901ff6d7436258be60e41a22964160f040cd195947050ab3b177cdf724482f05f978be3a48cc68f0ce20ead0bbd178c14f98d3eaafe893d4b733cdbb43f1867468fd89e342fe7f0d850d4eed69ccfcaeec9d701489acd30826ffc824633f22e8d4c8c7590c81feb35f9e5681c663e88826fb7557ff4d4066f692f2e5ac8bce0db08c8d10cc492da0745a4e1c9595163f34d4e06f1d77c036b47c3fe3b908692037156616547339d8dd9c97d73e2ebe0037722a7921d37ac7472a90a08285f8e453a6c0e32d0ea4ba22558c16fbc0f49cb079ad4518c4efb6f785a79874b25bcded7b5fbfd772939856212de3570d652f15a1015af12c96bc53bcc512bdf4f65523249a5175a96fd503b0c6df7239fd94dfe03aff7e3ac0b9b1416e886b84ae7b00580a4ec9c9fc8764533d3253e97cc118acba101c75e2bf374f31cf333c3c51528ef39e9631f81eb429f9cc01bfb2ebe2189af14c6a6942340e083375afd44a7eb097ef75da62fd854ef837930cc951307ccd4b63d01d58aa5161c95b05cbcdbd5655f08fd99127435245f073ff87d239dc7bdda04ab0de7ee3a7987a87fcb29d4435807aa14044a101a68e5512fb800a70bf49e1883dadd2ce2eb3f4fa0c7907afa2dc4475ec4a614413e6184fd2bc08b7155065b24a8087c606a5fe026859780d48328ef5e2f38c04ae98bd31e9665ad4a835c37ab08f39f5ac7e679008fa7c7f308333ef0597b22d89fcceac909a35c5faebc522294dc40ead62f74243f4b7d9e17e04637dda8437998251528fbcf639370afc7be966c880cb505841e0890a32ed0aa25106e3c4586e68a4a142ecbf60edf07a96311b44966fcc5cb5785c66693490b583bfd2083d26db7845504dc2fd8a5089b8e454f04cf6730612bb5020e9f032d9b44e34f7e4d13c702d2f1ce54b83501c563099f9aa6d4a63ca04caae6950e802cf5d051944d6227ff092bc9724cdac6c29edde2822875178aa3cbfed2fa8571c781c0aa0e6a06f3002092086101190d67036efcd75033ce962be7684daf41218235da4c42f4ee70047c3a448e57fde68ada076407f97ad6d26eb25f93067370954e9bc78602519807ac6db5511a7726bf70cfc1291aadfecdc75d17378c90119445e98962b6299b2a8fe5b681f6dadac26bd21724ca1697dd4cf4538b675442898e7e3716d73c0d8eb631d941eceab6a6c0695856637fc339892afb9b60aeca5e98dc0b3119b4f258c753ce644806ae41cfb889878c2b1d0adae2aa00d66cc07d425bb9228a3ea9c02f8b79e311fb57207bd5151dc80ae2ac63666ce93cccc40657d346dfa5c325d94ca4afab6cf1c3ec1aa83dd067d252a9092502eb98d33c76bc7293c79ab95855d9f32a7462be5f83a461e8ab2074bac2f7bf87f77bf9e6df36a99f239107d3f8205289869235ca71fb7b170e719d77d9e2a53f993fac50d9af52c62de3f1f712c2a55775c145cd61218bfec17ffd2420650024dfba9f1e1938c76098668676c447ec0a944ed8d63070b606f895a7d20a513c145b70781b4d4e6edceab811ca77856da8e2562cfae24716d59759445ac152b831e66958a7ab6d370c4a4a5a3974479bfae52b4944d69f763287000fc5cab3c0ec0048e656df83b27c06c07561a59b9c55e71bfb90acc287d4ac58a86395a59438499b2a9aa6e007f7b40625c565b9b0f4fa271c4a96541c2de6d8ac9c045a96f6a14464b9a49c13753f20ff1e021160a5af8677336b97e7eeaca1c4735e1d50f8aa247d9fe13a876b447ac8562d4b356600d201061b54bce2a1a165487ca686193edef52cf1ce4923052ed7aed4d276aacf80e5e0c6456142cff0fe749cde801db49de92c8813a4bb975ed811271c4875ab215de34cbdad04d6c0c101152b37c9f3ff1e6bff40506afcf47fecbf9c0973843ccf03e073270c5b340c3c311224c5753c2eef8e8585c66b508bf418dc7d9728dee42d6f95f9832bfcf9252f3766b505a66e2ec0e08c8677c0d5876d984a4d023f89401b4784fbf558dd3da0902280ba9f8421bb300d027140ef614c7b6a8e23785883bfc8d2e7979e62bcd40c11200793cd69b0618e663654f487379d33e14553079f242917894c1971ac3461fc5bf6665df9c46d618e379a420e5a0fd65b1ae9bc6f98d5ef6e0545cad8f5917cd8d647fda3615cbb5300bc27e390d74801193b426b1180352d74600304ae28674a4903a3a859e22c8cea43984395617133fe589c7ecaa13d8c633f98d4920fe52341f492f2cd83e300e3f07ce2c49260de08d51868bc4e295cb1251563aea6b9e5c214b97bea9eb88e051e4b4a5be662743bdedefe5182b0670e43b9868eb96bd535d9a33b407741de08dea1cd9f1199fe2bd9825028b396288825058510075d4370427fe60c094bb6500e69ea41aeee3a64f9f8a5af73bdb8ee006b49dd38170235fe6b20e8635e000fee3e2a8e7873defe7341b589a5ea80504c8aa077ae01a8d5333582df4848819a51da1e5b920e4960b177e69fc27688654a7d38f9b98c73bfbacb3306eeef0962d69b01f8a00bd52220b9360a5e053f8926441b417340523d41c7e4ad598f72fb3eba3b52dd340baadc3d3d8bcf1417c9f7ca88421f6c283a2000e46f8545a45faa733a0ff913f407c77e9873258c9bb1da1bbb0a0f53ae0f5f7c7e0fd2396c730266bd58e6dc9a6d814c82b9b852bf2808481d5e20ac9b9a7bb1db58db4b62e7da462b9812fda66bad4cfee225507a9649983cacec1ae92aff1e78ebb80a22fd08fc71f33ecd22cafffb750ef5b826538e984561cec6a43624d6bcd8e6781a46d39bdae0564d68826c87cc4e956a01a41c35e99f7b95c230c96b29e663d82194305b1b35fffb4df159f8f0e6c9147ab7bf82061b8103fe32cc9a7e595fab856f36f93b624ab2a170208e10a87c7f5da43b846ac51225bd131d805e43f674e3721e083d3822dbed529bea37f2234eab19d11bb687f449b1bbd8361c3c90f302c1eb665a0d5058184284056c1a5c0023ae762922beeab4e8e9eb263ff9e65a63460904107ad0ba19de8489a45f4ea15f4917e6e440720c559034949b4d93a0bd2de7ab3e3c21dad0ae71536cb3ba4a26bedacc228aa4a4e164300d0c3dd9b66eea988c671febe5ae6a44c336c69765e0012e66588339ea01eb38da181f6d32c57017ce05c751f582622f7dc1bf2e193e5374fd927b0a651b9178cecf522d1f4eeaee3f206314493855cc30642beb093f8d9f47b221b0fe03ad0a8f7a62f5930c0c2d19c7de7faf49685e884dfcbd0976ff67718e95ab89b2d8d49987f07633546d182e3b35b1481e658a0355d42941567b787ef09e50f3fdf014ae84890af5e9d40462921565e812445b11658277a51b12eb2c8e21bed81103791695d94e8005cbbd44fce7b83d4361828f3ffacd12e8a1d45054de0aaa8e511be3e790cb44d92f9d4ae51294ad3d01fb8e3f52b080ed08f80cfb8cce78f6085922d51a283a03c53df19fe22565eb808440822acf308af578b7c40ecb5d03b7838ae6db6f20cad88a50bc2b0e496cbcb465bbeffe825f37c41e118bb9eaa86ec9156b6c1d2172a9cac4782a5861595b66376127f9afac98e1826f462bd3f4b0ac657f8f3c3590a5761897a27033eed626639473f2d61b75eee147d81b9ba54f3163f55ec9d4f686eb170ccb17ed487587102f193921254a1ca819dcc3f5c36c1aa69e0804389cdfcc1298514c8aa4c15ee6b2cfd3368399d477c444d885c42c1ebc08ac7a0fbd8f9b09fe3a9506844d9507fcb4a26759e9f551a5acfad74fe2f45082fda44324278a35b8dc39d311c68d8783e3a3d88ebd0a44a04611970002bcca9314325c65f32da9f7cff4022a1d7c860f48bc68b9a65dbfc32a9f1090461c2d7bb843573211d681e528b66a86fc7c7fa1c3089f573194aec448a3eaa699fef61423e6117f17e00185221b489913640944c47e8351ff62e0fa38df4d59af26fc1affdf31b30ea50d8b5eb555b96b96abf27363d231ab27fc2ec42b85229b66644006c9a565e55a4313794d9731da3f3032c9a573a7c1f8026187c04ff2c307da143ad52f03c07e1017ea2681763d8e4c40ced04a51409b5dd22ae8588c17a7c63063cc2fecf63ac0a4aacaab80067b4d2e9613027b6cad312669a8955d4ff01dca5521605a6def9d6adc25b66f08feeeda078a9613ec9132330441af3cfad7449424d3e798b8a2d2efeb97d183d21975104e27a1100cbd1bc97a0c1540e1468f37a3f62c87b0f803c54f54061cd31335b48b0f2cb5ff646c3045b1449fe170eac97de09f2a7278014f5f57e4ad31d58fe6d46ec2de7a5f318b65ce912f63d4e6e4245ed82501e35fc690a62fec1fa1c8d2b795805a31958ca5a3cbd0bd947bad5c4609bafc76a97317902617fcb1bf8477c3c68480571f0d221f818bf0d1f5193492636a1a14255350df48bffe435cd4c946098bbfdb8a9bdc7fb4e0953ad2fc4564725fc05f81f46025d2835605d27d9c2b60f3e7bcf7a6767da7e77f0d5890f8a5a14a9cfe7be36566f769ad25271ca63f7258340d7368e881a4affb98e0adb1a388c123615fa5ed5a512e27992a9787ba112bc2de0dac4811fad59ec70ca462dc791679d165994ff43d4a9aa46e23a3d1f4c15e360019d5b9657304929608aafb3c39f6164908ffd3284dc5a67a05a81055a9b338bbb0b388631061947d03c2a63a5a732333a49e6e44be0f508946edec63e2f41b21e4a666f08b5d7af031f31a2eee8ae27dc18575a2ca1210ccae4a658c532513238eb719422a56cd6b58e24e34f3c11982b0acbe41df6037008de8678fd2364a096c9465922a678732661582dc98048fd286f25e7c02d2e53d32babc75a787e7c7089067b0392cfca74361175b4eac624c80a2980ca53728fe9a815942be64d2b1ad4a43a2aaf39317fd4abfcee1cb5e1a972666d47d2972647ff419374157b298da8a6cb8cd7906ab927d847c4bc09ff58af9012e85694cdadbdfe888932f048c2950c3739a2bb839f9d57a4f565a45424f108793067244491f1f479d4ad968b27919f30144b6e4f481281d0b2a6a01a7136a5c3a9dbb725a289439be68504752c4ee1cab13987444c90ac3311739f567d36817238f833ed534efad1e52c06dd6f96bcec75ae6f2dfc21cc33118cd73cfadf51b1f45291dc346bf8a6037f64871f6584081da413ff084cd1be3a92ec59fff664eb81619eabe9ee7c4e8a35823072a1a79d176cfc10206472067173a09a56fbf1f400f1d6d24cf8c7b46ac209b4d07568f52330999ed33db3db6d2813a9b3cd281107c7f74a579d8638f0814cc024aeb29635d40d619a7030d3a6818c258481c978f052c63c4006764e129f2149d57fca3afd02c31cc4e1a512618709a933a82f3c1161a52eefd095b77604ee4bd4fb4d278002b6bc1e403342bba53c3bd9282fd6e61c54f92ecbde5de524a296592326006fb05ff05377b7ea5a134f3ef46bf4933df691c773f2858f192591d90c5282940df7e58bf6c9a3030e4d28499df828b8ad0610b986acf0fb30b166c4f5864e1eef92e5831c6fc21b4fcd0056b0552816ef63c79a91d57a0ce448e30c2bada2e1f471656be7bf91227b2c079cf5c4516b49772d2ea17833eeb2fa4f25d1a952fa44bd8f2670896bdca37fff3d927b6ffa7bdca47fff3d5faa1efb0fd435f7e156a3ff427db7f7b95cffee79bf7c32964fbd72df471aff2ddff7cfec3f60f7dddab7cf83f3dd8a00f29151867ddb702e20f04ea4fdb9fd776ea598514b5a310f60cdc874d4591900e51573baec0cdfb82ec910331028991d1820b305c80a7891358486f984629a57b13a318192db800034f138a0ae90d534a39403350ab0636c08a2b1931fa87f035c0810c6ce0066f31325a7099970228e07578f65c85f486b325bc100c7a5ebc90e6c30be2a7172f2bf1f284c479899f4e42e36cf9ca404de66a6490596c981863e3e4c7ed1bedf4d830607eb443830cece4c05482c8a21478cce3d8b2c3825a070ea65487768e107164c76422f498ea4a6b3d120508110e3878365a85875a7552bbd65a6bad55270650ec081d5959725211020f201d25ecd03a50c8e193c3a6e34108ce4710db0c7ec84a7ad07c1044660325ec806d20a587ab838de541446dc2074a64d75a6badd552d7aeb5d65a2badf506b05d6badb5d65a6bad3798b26bad94eee4b093838aaab0800e21a890028920181184ca64cae95286ece242dc1174f1b7213fbe832e365a82fb86943439e7f8a467cb8faf3d5d1326ca205bbedff8a43367cbda63cb89eaf1d7a495642fb617c74ccb99965b79e3481be784eb723ef4f3992e3f1b274d1ffdd12092ca0acf4a8885b2b04c972c4f56a46cf9a110cb3449174f1c88ceaae53c405c1543f18802cb69d2ca7939c9c19138640f8952491f12474bf2cc19547a74d259adbd38d3b296b7cc759feea33f209595100d85a6cb90ca4a6885a5858b162e3c8e3472c1cd10b5b8bca4f0e215f01f478a2005c1e912c4dd4b01b6fc14462fdc0c155e28e005eee2bb0162397a9c51ca9c4c4071628467271a39b283b80ddcebb16364c7c89102c4284065c4488e1029577ac78812466826ee8e3b4664a0e3899cd6ce91e226670839507262370cd18163c80f4986dc0c11328467888ea5b00c6fd8cefc117f3f342e8291e6def001afd8e576ed0cbab137bfb9f3dcecbe5f2a561d6ba53b73a41979d77799db77dcf8fba15dfbe188be8070a39ff6b97e746a1ffe3cb7cac4fc4ceb8b98e870342b9e5cc553ef29f3d45cd68b69ccba787784e135f8bd48e4e843803004e6ee3ee78acdb6f5e03255a7bde06385e8ae0828e74025e483959d1a5411b2816587169e8f40201048456548cadb9c1075425a3412974e0acf4744ac70d95a5a5a5a5c5c884c79d102f52c4d7c2a47e4e7f9e8fbbe0f045726961d77564f60acb000c0474700b22004d072c46893457060443bee144132006fc79d2204d8e457e40905d8a47e41f6a6ece4ece43031040840c01802c361051a0cb962471b20be1e3b5e65f3e20539a82044470a457878c9810e2e35f0018f961a540184c8861d5e24b2030f2e8ac02d36c618e31468bae30e11176cf94e8484f0b082e4a442440a1e4044a6d8a1895891c387c8149b0e2504d74410dbeac80f79b5831eb4d59320b215163b601d3ddc206cec1144d422383ed0224886984340ed1479c20e467030ed18e1b1eb8e3b467ad8a17e00a49418121a02083ddf5b78e8f94e4be033368a84889e30435aad7f8c31468f317a8c20cd020542a4ec50c720c7861d2151d82d76dcc9b9c186527bbe9472ae52b4da2bedc532bb9bc49996a596372e46b96d1ce7ad2dbffb42bf8951461973f7316df91a14e5fb6acb9b96ab62b0d6eab5c2915215638caf49276ccb1859b12e893274bafc86389af1e79228e3a588696aabea40b2a24cad2d2fb9cc65f9c86f88325749cb545b5e75e0f6fdab5f595589fcb9248ee4d7ee7595b84c27451ccd9ff9a402ad5db105af7e5d7b4eede54087f9e48aed70ba5a9d14736efa61e764cf5fed9a4f7b7e3ecd1918e284a94b7096695ace9b96b78de3baeec3759f8fd620908a06a9a8acac84422c9125b670e17d9eb74591688b2d2e2ff4e565ba7c71d9a44c417ea1275fbcd8e47ff4fba6cbaf65fb5e5e52889f0adf0b8ee4f23118ea2d55c09df40fa5265dc044a5543713673547f324e3ba5e3db0d82ca9542a954aa5fac0d4d39b2f2346864b05a351f90dce8ad542a1503337d01f1717f68d1db91aaf5bd2708e845f466bbf7e8efa3a068c2947aaa004658c5139120d5d9108e3d5ea56efca909171966518b0d8624cf218336e822ea4b81dbc61bffe8cc182ad96cc38127d7fca818094377d52defeee3f338e860441ff3dbd067a63ba56845c250cd6d3233992a4ce911c4f4da2db7efe647f63578e546d0686b5bee6fbe7b85f81f9fe31cc182e585f917e057f54577571b9d69034ee359cc6e7ac3169fc29a535288d7fadb5d6a834f5b3d65a5bc3d2dc7befbd352e8dc7688aa626cb610e73945cc955ecd9a0084787ba06a6f1cfdafce1609df5f347660146b002918626cb6d7235224d169b4eb0ba264cacae0923ffc6a49bcd6bfa8c4c5542652acb47f5ab3d883073a606a11547feae83edef4a98627b8b8b0e6d63feefb8c3595ff83db91a190d056bab7bfd785a2bc661924133411d982600db3fca04843016805b220b22d05b4a708610687f9728935fc49999e5ceb3ee180f63e8b6f6713031830c328bfde3477d3ab795a186d20d320b1adfd9de6e03b72c1e40fecd3874c8a0b1a009d35030aee80d396915337fe6679718f0dfaf81c11b3b52a741df9f44dfffc68edc8c3c23bbd4b7ef32ffc6b620082a48ca9b82a4bc1d8c92488e9e55c0b393dc6cf91ac798a449922731c618638c393bdc429ca48a6a05ada62c82419254121e4970ec90262921816d479284ea4262ca53a494eea82804299ffc40ca4e144a69b53c76441610960f960e2b07b39a6447c822202e5dee3ca1818e487890189943983aa4a0a3064e2461938309506832d7ce9c9822c332cbb22c0342127887750424448082b5d4344594f23c9f02c3a7c09053ce9818afa2d4c4c29aa674abedabeaa4ba746a4f7d52593ab1c64eb347ad1227c88d8eca472ae67befbd31d38d2e61881da37ca2c85ced40c5133f4d2172063f442af4d71bcbb3fdaf65e120d6b5fd33db440b9269ced365c69dbde16a2a576d0f8ed26987855928b647eaa87cdc94b5d67e7167bce19bba39db5fbb38f9eaf8f091df54c6d5d0ebba512e0c97ae94fbba4eae4be36af4eb1310158a074e10140f7caab5564d02d9fe9bcab9897170108a5198878f1c9f620cffccd5684e77a6ce4797ea6e3a20dda9dbd19972e4c1ac4dd24c29a53463167eb2fd39fcea306cfb7f30148c794014049a2e41b8c35c4dfc1c53590e8e8f8c95e964aacc4796e2b89af85a6bbd65311edbbf3b7d52db5ff72865527ce41947d2b66e69507a9ac4b4289aab898f5c6b714e7fd05fbc5aae5fac2c5bdcf68bf58bfb657e727bcd40b5cef65761057a11e6d6f67f1ef97d407ce459a5c287236595ab542a954aa5922aabb22aabb22aabe637abd69a30fe186bed79ff1f9a55593567b2f06956d127a08dc5625866b06a2dada5b5b496d6d25a5a4b6bb162b1582c168bb1582c168bc5b2cdafea2fee15b25416cb627306b358598c95c558598c95c558598c95c53e1d3e9d6cecb3080018b3b6ac71592a4bcd990e6f592a4b65a92c95a53a2ec662b1582c16ebe6972dc61476009c23693beb4c26ccc22c8cb5f6bcffd1288c91777371366b19be768b65296d6fec8dbdc134ae1884ad249fc8aa1db4566c0af910db4f6eb9e3f70940c327dce113969f2deb4c9d69ce5c5767e23ad3d6997267d23a93ec4cdb3fdc4adb1fdfd4766f86bbacdafeb767c2f863ac3730d49e67f1879ab537386bcdcbec8ddd62db2bc65a0343ed6560e8fd05c391cd5233b44d41aeae3aeee3d41e9c416e3943db1ae46ebc1fe87073dd6cffcd35673e608837700337d7763a6df9289cd22a0f9f70f069854f2ed7cbd5e382b962ae59724d53ca53aeba29bfb929c7b9295fdd94b36eeaf6481896316deaf1538fa36ee60c0e5e6996d77ad7e8467e099bf4c9437d6a14dbab2d65adabfde245b7eaa48e938ece69227523031838114489ae8156e99d9c9d1d243b2bffec1f8e2f8973f29eb9428514b6e3afc0d5c03419060d00b095ce2e244100b29661da85251000487f3aaee691e7a2054b68c576e10960fe850a29bcb8b4dc2eac61003023010400003056007117ded891009d7c4973a8f66e9f965d4be35f69fea3d53f7fd3fae7cff3275d3ddac631067ee19c7a56a88a459d7245e334b6638cf131cdfc4b6369643983a0f6c9fdf96e48faf4b7ed6590db6f3e8a0c88b2b70f999eeced5524f782cb6c2e1e4b20f4bfad7c8bdfc09005b4a13da77dda6f5f6e817ced9b6ced3d1ce8eced43dc6b5ff75a47ee85fc1be8a2fdf69fd71ea481333e20080c69886deeb9df7efb32e83730cc77dbbefc91a3c1bd06926416bb7b170dced07e7bedb7cfda83be2e022bdfadbc06bae0a0b9cf9b3601b9f5e7bb695ff72bcf7d076e1458790dda58f90eb4e1bdc68120f53ebe732f481a1e185d8021983bf4dc6bdf02a42c6008e65679ee436028b7ca97b5cfda27b3dd34fd96a3412510769f3f0cc1dc2a607882cdd19040063b30e43e6a1c47c35d66d3dbe3edd34ae8cd41ad5da07d902d6988659f5b565ee5f3cac739e839fd7d7ee5e3401f30d4faeb9ef34e7bae7c72ab7cfab96fe15e7334b6cf46f69a7c0d073eb6f6d9f671bf7ddc6f5bf789406efd024723a321b6410f02b38fcf7d610772af73177ec0ab85da83208bbd6d9f3f11147afb90e3b4af61060786356cede3f6f133e9811e10c06888d1f8eb5a2910452059b67c9b62d14ebc2298d851e8d8e41d77a288e24e262eb28822070ba2d881248a22a2400247144bf0dc80c7c54305cf1278623c53787ee0218267099e2678a0e0c9e15929b142c7548285c975f0d8c0a3e3061e9b1deabf158892d50d934625476c94f09c9438d1e1832a81a264081695e72a3ba2844789931cfef223fd9371243f47bf9d0972f493121c3e2841e91d7794dc04a197d04de82456dc90c4093bfc982740e0882b9844e108ae94115238c184243cb020a269d08391eb648a0e4850261fd4d3a11d242e8842a374de7107498a0947681fbaa477d03c34ab89cde861b08020b2c1474a9f2ea4fa6e257571617e7cd25e655225d197d2390a4c2bdf7eed6c7c4a278e4ca9e428e862ab9c53eaa89a94d3c7c4713a593ad607ad965eacd14cdb28cd5be468a7a16cf929759a7ea01df40b1d8a07711e96bbbc89e304f11b295b9e4a523ce53dbc2425a6e3644bd923595247aee40e7903449ee40e69923d3e63515c2de93e27a555fa9c94d66a69b5f65e8c2fce324dcb1bd77db8eef3d1fa3dfa852f9485b6a02e3892dce1afc4ac84581cf0d7563a659c1df6eb73ceb9517bef9c36b356e2c7a103d3dc693569dd4aaa59b73582149c3d3e92ef372a9d8192238c5004dbfef2420ec2a103d3dcc7f9e4a3aa24ca6420f2c965b42b031165f40fa49e22a3e68c568276b98c7ee91f68226c67218b2bf44bcbaa5f2d54fae5b5506dff16aa1640324ae3da08c83d50144569eed18a317cd0a17ec15ce6f5c4657aa0d4566421521f45116cc715e8b42bc8f46794b995048dc1dabaac7889d0b303d6049348c8d46fe48f9421c3dd3d14444f77c13787adf3dddf72242bda01453a3aa45874248b32d33a77a131df9f34c11b9b0a91f3c79a68fd1af935455068fb1eb78b8d8d67d23377cfe69c9e514aa967b5d65a3dfb6cb7fb596badb59ee5524c53edac1583e1d465c14444746851db3dfbace11f1948baa0ebc6961c7e10f8db9086d8ee80356dff29b186822c575e4351a81a568d882562d5cc98e13a728411c248c988b704eb2bacb0595d534a794d38589304436bda4a13c6371550a539e38558bc1512babe63179eaaeb9e050c75f72150b5da0e8a655a14274cf7393c5369c278e8499e0d7134f20f71353c1b228c3f088b0833774887b8cfeba7a1cf35e495620cff10088aadb8d4d7fe064df65580fecac7407fe5ebafacbc069a40fb953741b6a150db7fe503699f579a30ee7df649ae463444571856de456cceacbcbfd722bb26cc674b201828d602865a048678dbf7b71e18d69e1d6ea87084da2ec010cb67015df24320e8dab2a4f2859e697b69fb83c20d1582b0f06cd80edbe1860abdd3f6e7e67dbfbf81b6b4effdecbb3fafc8077ada1db94a278c14be9d1031d4673687f18fdba5b43e8d42a8adea9a33f8b18f3c6a99aa83386ae91e0ff635b6e3ae2f29a5d360b0ba64a0c39c9a2f554f4ec1722a9653b494513ed24194c9457cf94784f1ea9a33d989088372999c8aa322b212db89f46c28b67b4ed1177ed794ee49e91e0d65b64237945a14cc35679c66565112f44ccd99faa9d484a135a736a51a2689e4f939c62cf31862dc9d4a8ab1284f467b3e66e9d010778ee874c09a2ac54112fb934862625431319e275ac2f35a30812400c683274caf275348e9d18149912498d880491448307101931b26315842c592284c7af4f0822a700c8142891ea48a4ce1e910131f76a8ff4e237074a60215254b710113204b7c30042196e83862490e4b7c58825a72c4121f4b58f0a387b1040694521f8d60c098184f4a3d8fe474fad3779082330b8ca3749ff3894caffdeebd177780bafd9a8962da093afbbbfd06bad8686ddbb1efdffcf2e677bff9e79c57fb28a5f46a5fadb5d69badb5d6da9b6f6aa27a5ef6c65531d80aa79a64a9b5effdcb89a4dc9b7f64f0b4ef8deddaf7f1d540171adae3276960484dfb3e06770962bb03d4f4ca50b2a4d22b8296d29b21cff7d12826a6021363ad6be8cb4b9c01038e1c13a6c6c546fb36c5c546fb4797dc0caf48c8f4b1d473e2e0cc5289528a83536badb1d65aab52dd7befc5228678bf5eae72150c8683234bb21446d6463f2c6680352e367a6e710637e36cb2433aa79c2fcd894ce9fb6824418a63f03c8c9d7a8c21aa839e9e3fa57f3f87bd5807174541bf78f2058b74d0f4d3381a934e8a23ce3a7f6a6dc073e239b54ccbb44ccb3eb5665a663fdfee5e70e239f19c73deb761bfbe05af605dc1ba827505eb0ad615ac2b266aa25e2f57b90a06c3c1892cd61573c6399d7236e098651f29c2f85b19f1060bb05fdd234c37c673cbc85386cbc8fd8038e322c2f87f6c98651146bef7d9a18e31a28dd89a51e6d69aa941974a251b689898cfd5481bf57f67c9cdf85dc1b0c67efd6b8a316c72d061c70e3cf470f2e10720780421440a50431051440f238e50410a892494f0b1448ce13512bc2b8831fc6be4fd1163f85f93fb45ddd49e2b9fab09e3d7744d726b5d1d394e150e0af69aa597cb69b537da8bedcdb4ec62f5b0561cddb8eeb3c13e9fd28eaf230887e52a4f79c94fb01e960b47dec8934cc952cc357b368c2aa5fb9c9456e993565aadbd17e3ece24ccb5ade368eebbacf47c7e8104b0b17af63f48e14c718f10da0aae1878f249e04c19102238cc08224f012224c70c8ae0e792f37c3994d96418131c6b805db4d7cb1b25ed4434792e4b6afdce46ac070814a47315c0d1913667e0b96bb5c8d1819b6e2c9b9d8e81087b91a2d4c98f9dbb67d7efb8ffd80a4bcb7ed7ee548746f5c0d0c39f484a93be32e47b27bd3b2b66d1c89fbee378e345fbaf6c91a470a71a418477194fdc691b08fe2f61a48638cec3147c21957b371356226cc7c191326bec691e4963719bddb9e84b3edb316a5b6fdf692ab892223b48d1f336708d8f1078d19e3c8f2639ca93943dfc130abf6cc29f963b5e7efb47cecb893031c3bccb19d1cf0d833c81c8b2cf8cf2bdf3f50b43956840e555239d5b327fdec55523110923d9f7e29578a155970a1515b3186bbbce05fbf8232c89d6331c67cd1103ac4b13da3977648c1da8a31e6bbbc506dd0f0c7055d5ea06fbf7a2b19e4e8de141e153dde115e3ba4db5baedd626d6f156905a9aced19216631829c71af4d7fba2ca56088b5a7dfa337d40a1dc697ad20bd69023e61d39cb91f625366ba80cbd89f81d21b1f89a2e810dfb84c9b7e8c7d6a5f0343199b7eb4d6be04e3b5d6b42d9e34dcab561e727cc51873f69841e64d8c13d5f3e43529add5da5badbd17e3ec629c659a96334dcb79dbb8bc6d1cd7759ff85dacfbe8e8af1823be03d159b5785c15c577b82916c5513c625160394d5aab9c9793550e8ec4217b48140e598a4f3230a30b3146fce873525aaba5d5da7b31ce2ece324dcb79d3f2b6715cd77db8eef3d11a0452d120159595100b4b0b172e3c4f246a915f18da72d4c285fce4c791422db4a565ba6cc19d8e3122f65174c550b2f2ca4f4f13863eaa2705536d7ab36975899ce810bfb2cf8d86dd30e4cf740ba914da69cee89336a13c6df26e427acaa036c9406b3aaee5b8cc0c6203280fe401e8d3d76ee64c7efa1a4e64413ed59c6caa35d954e3d934d4846c7a2385761363d02774a84d9b7ea8dd6cea32ed27dfd4befa1467ce4c280ec303d98029847880faf471ecdaaa33caa6ab4d89b07d8794079bced784a1a65d4dbbced8565e77da528e303e9f95cfdb67a37bdf39476e86feed037dfec21b333a30bf0602c36dfb3e9fbfb86283db3eb957029d95a8b42e1606ad6434030000080317000028140c89039224c7d1408d63fb14800e627c406060389cc925710ce4288a0221638c21041002800100332445645800eb31c940fc3c299b96c2f4a2920e69c20fe14386e55c7b593dc3ce46d3b01cc68efe95b60e4306826abc1be1a6a13a158b335576f5f12823df0ab919f612d672a740ffab9c60a720f64e4e051ab55d7a1fe23b452cbfd59e65c08ffdd11e07dc6cb8fdc18ec5c3b1941a7cc2461febe441ac9b062f1db22bc79210f380911678973eb4818c305e2e0f8f9cc13e1500ee41194b925223e01d37abb0123ff8d77e808d33f9a987b07d291e89117197ca8735997158ecda04cd32993666027c3a0419a008e4c2f93b9c566f7f49bf88455cb56aaa5434257f8e9b468d153105452b0e79ac3204e30fd91f4262edd57645152fcc6c23028271c70ee013218d9916bc3dc42c14554722677a07d905a8e013d15113cfe48d396498f62cf87787875f72600ed65d8a99d244926fe6bdb3b107b0700270796feddce345be7b57ea5c0f8af4bbedadb3eb1b8fe8c413b6276f13a876545cdb92cd0d78929d26d391a6f505f873ab716308c5f2c0da6076d435950be6a981f50ccf0ad26246251b7f42cad864e892ec877af0744b48381bfbc53ffdcbdad7f20763c36d4c639cf8425a8ceab9d041940965baaa7fd0fbe0d53d6c1487afd078fb27d7e9312b0eacdf07da49642a0c96a974d6ce168f33bc65037e6f063ad53b2e4b8c57b812cab01b291235d11ef26f868969c25c013f9cc79fdc55c762ea8a62eebdd345ac2a469e2c11dd38acdedf7428470ce33cfa8d213da48bd5dd60fe3b692b8e4832fd574268472db683318f1b80f0d5df5e2ee9922c49f4461cab967caf9ef77803969e8792d3a2a779a8234b4ff64564447a5a4866fbc9000d2ea209a3a755090fc011bbbc41c8b50cd0d342f588536be3290c3f88758e9e66dcd205d011ccc9f91bf3a770aef6d3f3d514f6eeecbc1f784ae0a359484f4bcb303b32508d2b72145ef21d7ddb1ffea6d37d240be4baa90ae40846b127f43437e9673daa1f6529cdc713998591856a8cabf4287c155733f2044b4f13ff4300c7c4692c084c26905c307fee3869e2fac8ea34b1cf9d6dcf5ac5451be269377ebb34a3f66de4037cd3ee150dd7b1831190f833e569e2dea5713ea954140d8e4f12fc3b3605ce7000bda0c943ef1d0bfea5b2728de087ee57c84bb5bd7a9c5a6d5f70d09979e1042a7c87e5eaa4ec24e0e7e2eaa9fae28ea78180a6101a36ada79d45b426eaddda66a75fdf3337beaea929056efe3c732ed0103ae8ce65d58f27081b8c3294660d117f2564188f110f09fd595d6f3fe85b313ad44fdfb65538c8442429709f0447bb42d14cb59ca222005a798030e4c9f86512a3feb77c47a717a091aaf6027882d5674f3b1868b6e7e82e1b8ac7a6bfd48059cf205cc2231c9dd03837d6b8e62881b3945951c318bc02c2d13dab85550204252efb6ebef921dcbee309e976553729353c9c71cbd2db861a12c9230f4af188ac34190d78a9c64e4735937dc743abbd9819410f84063a614ae77e2a802350d444d15cba060a2373bc42071d5fa24750d6942ad7c44d40cd9e6c21b7db8d55ae8d533f54982226d2c4260626be9a50e7fadc1ccd84c3525721a409fdc8184e25cfe19d6111402cf9135b396f5d82ad20a47a5056756148b0808d8e99d6db91287c288beb4343fb6f37db84b39b3fd4e02d86bec28c88340d92528137f39bd463dd600e52b7c9bc685d482401b091bf803aeaaa304d1a214275116dd4b079c0ad0356f064cd55fbac4e81ef7794c4cead5cb5dcabc007d7911e0e8badcb71bcaa3bb8591055d2ec7a9ec2b0119a6edc860bd01bd80d025eb244baa94089b678f52dee797079abb7861dc27b28b17a525d561d64db8a04694eda4259a6f3eae16d36a4c64ac07f5da18e72099820a635bc5df4c0b8d57a521a95e4a8e3e78f489ac9fddd01344774372ef9478026fa7df8f3a4a447f44951211017eecc29c975374c96d9f3179a3a4a9de0c5b7c44f07162a23ef399de15781bc6fbc601f081240abd571e17da6048269157d86ced1371e4b2d8163b29c328ee1338d2edd12a581d5fc2afdc6f0c010afa0dc82b0532c7ccf9797fd4f238f27d06681426859d05ea2331f565f6ec73bd9b8374a7275423eccc484bd6033727b75e8bb5fea7e32de0089c6bdd194b9c8888d6bb09cbaf2c7cb89bed48955be0094b278ec1540ebe17d407701566590c5b3aa586e3c65f17c0dffd1e78757943de214e5e7857fa6963f4c59f2b47c02a7ac9e17fb7b6c2e8779f6d636ad122ae9137ef892d4d7093d476e7aa48145aee88a8e63c1e9d98bb361a9e72f3e31ab5ec0d3fad49ebd447c8fd340831104ea47269d8e27fdea917e2c8fd284942f0120e67e0ccfa897556616ec5fbd2cf13f406c569cffe4086b8dfb75664ee3c8995abcf878f679feb404735f8fe9ea34e2000cfb83d9f2e9e84b3e367bf7055f8d2242a0c6d3f63b277c87f24c2cdca38bd0d00d86d4b292aba9c33d82dcc7fdfec6b54705363b6b96cf133d77d41592c691a52a96c66d79568041dc66f638aa5073ab5bb568e332175e8e78850f41af3d7e2eac192f0c88c1c84429bf7131aea75aec570fb10d29688df8b1685cd258132b06bd4b3aab45c7a077816e35b1f8a82ed15f29f6e2a400991b35cf6a90d83f69178cd3b3d5f0f135ff338da8f83806f02fcb804c4e15d115e0466daa9d8c81ef423d58a9cd19065974147654b23bf5baf383086959b1bb8a6f9c767c9c5ca31b79898538c9a9ea15a990df2bc23fce39b9000b2f0003537089a25a40dd7722b7ff48ba564ace09fe8809276ed9fe26469f8f6631f1a84df61e2f31a9e409c1113d6842b0ee3bb3d7cec3e7b8f9913b14cd77c7cf17dea2706a3d46b292022819b6b15e8c20ab7f9ebcfa7ed59f1d63fdb843760f51ab718c43f83308b786e0e7cf7b7a35683edd1e8b098814691d3418170f7c85552f3b57d1d0f66137697b3e115357d0284043e24101ae8ffbf7183e01a6a70f511fb9f90fd27deadf7a095ee89dbe496abcf4255177040e19f1ce6e2e9abc45b8440d3b6ec31d2e7257ab5dbe8d5217ff074201cdd1547002a50f1606b2e7b448eaaf181a2ab60cf665018a990cf9fe1d96d9ada97c38275967129393287c353f547d1b1758e860b1350b417f68986eeaf2a6b89d97673a5d9baca21b7dc33cac1374e2358442432367c817475502a5fc1d2819c4aa1c7a12d38e4f5f502f025b398ef659fed6d3c61775678f0ea3355464b22d887d3b5788762e74f81ad3320d3f198ac736333b27906c76a5804afeedd6eed29a469f3257a3c0e370992b6f330cbedb45cc51fa106d6e33a23d7f4154131421d8e365f73f1fe9b64f384a27c274b8952f36ba63ebdd17742c95d9f0946305714f88f61edc6c26217865cfa4c398193ed4a797fc4123ceda5abfb23cfa348299627dfbafd735c6095310e91e1bc518e5a2bf3f000f0aa4e45ec7c4ce39e696e9e392c366d9e6a955d0c037cff4d519b59d526781299aaf18efa6cb652db2c88647ace0f2f231d8403477168d3615c50746c474c34480a60d88947189b1a95186f6372fd33b02fc0c4fe487f3c9cd953fe6a020933515f98d96a3c6887a9430c53fe08a50bf7c4003f0001c33b8de5083d7e2b834c8fc8a3e0e69b54b5ad6d1dc74053bf87a04914ee92ac096392f09243bfa2506d9a3b830f28dd1d101d5f84f56ba7be8a46f057710807c0032d0f26354bd3b6c01189bbbb7c7cd69403cc10f18753790747a933f91ed1e26202f972ad7bc069fd9b1885fb18928eb83abca2f41702f8fd1a7aad2c3fc61b3a6705a11f1e9df66dc7e99183f9630268043a0987d9e239d2aa64a6c895fff2bb988060048c68f985b02630edca727bd031162feffd0425f7976006f2bf7ed7423fe852c6a174cba6379b5e498dcced8e928371caa0c21be998f0aea7e58fefd7beca45f5fb07525871055368683ac5d2a0775272846a61d38582df1adb8306701462519f44b69254230425e4a57cba405b320688c83865c91d2dc49a37e2bc107a6d3a274976b5a2c1d8e0c641df63e10c6968345306d11193a3f62e0d027cd0bc684a70b69c34871228ace8f4bb78c06b0f8bd9f35900bd0ef864861b17500c013048778739126fe460323694f71513d440b0c81cfd3e61280dc75f183e744f3c441c800eee5c11307561edd62e5a27b7c6481a62f3016c546f96016549afd10f43585b10da08ec2c6be883c8a7313aa6a8e1d6e002a2b3889d24226f5bd722fa375e26bcb79205889e30b8c82d0ff6e85d8a354559dcc34f48e81ea4b66adca5fc638edc714ba4d3bedd3148a7c67ae7b64eb73b150094c509ce915bc935474f3106ef669f4ab53c52793c573682ab69715b0ce15545fc638cb4d7dbdaacd037ecbe55f602c36427508a4650c33adde290a8ae2400094d47ad12abe8c79453025be58f915158d5a66f139dcabce6f5d31f2a9fc6ed9d75667a7b555ad484cabdf1aa52502232de2017f6610780aa24dbb8a1aaf2569784a29bc1bafbcf5bcb3fa1987799e97fd4776f2eb789e95e4c2147fe74112b1c60c9817edb30b581690b879e5eb950bf0ce9cf46b342055ebbcb80393c4c77cfcfce1a3098a1cd065b10aeb723457a2a1ff20868b9332eb2f192ac24c67397ddca93a6729591e508ec3fdb2983e9677c7bf1366f986595b0743b0ff63156b464b1198249ffad559dc2134ee6623a488c30ecbbf370aa2cf03b7fabfc52224e2e41b15de3bb26c08c27d04b607a8a286ccb3097726e5e9a48195df70b2419d32b55723341dec8458e75b80d69ceddf406e46152b9cdf0dd2cee1a4d3281f10813db26bf3fdcbcb9a15586e7000ad22b77abca3c5f4e8f282bf074f1661d1ae937268919d254ad2f4e9d9debb498b4a408dcacc3c6554745dc51ef1f29e7a599f47c603832a172d89979b79f07a05daba0bcd89ddbd113fd8e2e9f683b84d41c3f60b526cca9b5b86b64d9d962567710f949e1df6353ee949362497092808d5668280f9b8aed57d0536ac1bc5aae5e1107aa5f79497fc19009141ebc462ffa67d62cb24ac8982c87c78624f4980001a886c0b7336481f20e14554e70bbc5716e956eba7bf039bc09a6d80053dda0d02dbd0bac83cfe61ac6c6fcf961a3ccd22962d5605b286aa405001879f3194e308441482a592b9d75f74d7688f520012553e3e0b91ce112d9c85e5918e41668d5eb8a3cacd491d9d71302623c09393da84a323f90b12f2d04eaf77479c99da993c0579015dcb51640c4614ec35ae3487fea8cec68cae1009bfd220f70fd1e4578961ff019c0cf895e6ecd6ee48821521753409884bd753553a22d35887212e7009e4a0b140d3e590768bd20cc9d55fe332bf014a04fee2263e409f896b3c44e149d1129872267e74d0b4221f8daf6c937af8f5cd319a009ea4d47dcf36150027098b253e7a28305b87482eb266cfde3eb77ddee3fd4aa6c200f40e73b9b972660f18974f96e9cb9a5b557cc0a323df5611129ed4f33289afa0e1bd4f68c30f5ddb9f1c6c19ab64f1c23efa893028fb462d15efc07f55a00e1a650e2ccdb9023092d9e0c207fbeeb5a0cbeb5027a2c5ed1aac2c228f14dc2a0b457e894b45aaf7aa8abf97e9a470b436f434cb57f571aa56f585afdb89fe70566d2e3c46fbadbaddfd1b2d635a3dcec889a7115453804a188893c7583f9f4989f417f4fd3a704051be5621a1372596f25fff55fe2cf0a9fe00d5bc8bc4659a68e39d21fdf0a30c845ee9927bdfca6d4e1a2414be17ba45ddbcfd3c6e1c6369f5ea936dfb35eafd0574f39a8766144079b506cebd418ca42e3f62a1a08f706b94f1d3f06cd09f51feff0280855c428026764574836c5412f2a871456cf7c39f3f58f4f3655f3d6f5742fdb3405c20aa5ac8f0865b3e39f716d6979a0bf26712a8a075e5b5da75b0b87ecf2438f1da8fff10d56de829c336275708492623f9b22b94081daa4b8c865e992e2dbdf41f65481b21e0a80c661753a22e798a7f5e192405b928be94cb0c24638bb0b32f3b243242da2a641af2b90ace916b6b9d443deefe30831a41df3cf225cd4f965cd5ee9e210bfa33df6cec60f3164a141b022c343a5ddd03712cffb2dc234dbc24701d5aa8211d9db7ffb6b5aaa06f25b1fe6d54d80b2b72a934f51302ef4445ed4cd8cdecec4a4a97368012beb165b236f71616e270f93361ebeb559f698b1e3e78fee7f8824ae6a82397ee7758c09a7ef09767d1d674931da7d89e9b4fac278af3f95178918f756ebe9fa7f7623329694d935d6ef20d646de0ca173cdf3d859b070a0b6cf14e5d5d2824b433f0239f631146ec8c7bd53720a0cb9d949be29e089d08274663a04cd908aad1ba9618f94ac7e0f9183d6bec206f6cb49fa43275233e7a29c9a2ce1d2a21a5f9c44655bf6b5949af186e07a234e1fb573fd1c71a66a92d9b2166ece11e486fcbd99f1d05dadc40add8e928251fe97ae89b3fb0dda3f4a023219936421dcb685377ce0bb43f1561bf7c7fd4addb3955378560a1f5840290a49df8d626fba22f3ddd5196e6835d0b81f814c3e0c9042a8f0667925b9475946b0d09f01ad138f6d3330b7a29afb8ded1cdef477d4516ab308f1b9c0fabe57287c029322b09c69862602b26171ad548695318e09ac151318ca9ac46ec0141dbd25356d3f6070915291f86b5ec95f9f8030ac06d8f94625f41c7afa7a258f8717a831c7ca1f283d8b34b081698a26223e38e5b2a60bdbb062e3fe36c82743aaa52df33a3be6d4347f0c8cdfb5c636a8798987ba42a78a01e6f7da007f8e3423b04a9e179112f1edf0358cbc399f0876a7ea7ca3f02860981de07183d9583101ee2dd7506c053c0d97231407eea92ffd055959ecf423b7a1a6e7d2c985e3ecdb40e276605f53a18241e398464e3755d32ff8f92102e3ea992f7cc8d84724e76f41d093e67edb3909960fae122d2a10b89f4ad49203105d910fa0b857485ccc4af10f87b9291680f10a542f7b2ece72a2e5ad0743d6fc350de698e45c087d7c012cdecf050c24359ee603b90fde0a11a07e27ef145287dce023a589cd4a60f3d12040aa93e739c1c1477e6967b899fae43ae482fe856b0de7eadc1281bcf5c78c635cec4c94daf8e0a2ffe71d0335851a5500347673c19f1c6141a06602db3cfc8339d691ab355d37c21c36597f402cc184360d70e3aeb49e06e8836e65d9021105402817347a8ad4593f932f4c60e03fa9ec1808f5a393094869687478400a3724738aa1c28ac91ef70adb1663aa0fe989f909a6a150f584c101ac5f155770f3929db71c174c04785d8fed860266e2f5de22c71acf412a1b7dbfb04848e20a24538df56100dd74c8f7ae9ea5e05a7f54886e5e2d5558443d6060657267033ce69eb7d0c89b4e75f7ef4493f521e8e86adb3d969da5e1bf5ea80604c2fa9c48340767c0031085025c66bfc57eac67d322c91bfaadab23343a39c8fa88f72aee817758967a6ab94c698ddc323d5ade03e15a6a89d81add8e3f7f248e82f2e2d2d0c18910f415496bc93b37e650780db03153247d179541e5a5cdd2694d4f3a5a2086bf03eeaa563ad379353befa4a15a1a5d22720c079f82604e6397eb72471d2871db4deec302e1ea9a84e604e1917c0d152dafa02db6b5ac86b33690098f17161b1ec8a3a0f1126f7e077c6af64e583c68e13fb3d70a201d4110798813844f2be7423a80dc9e6bbb3a18e27a68d895449e2af67881519bdec3ccbebc50b7cda7c7574622d10b6eaea00c40a2e4e7e666f1fa46449607143787e8ae2d532c0ba241d83ca39aa824c29da6db20541b7f239bc7467dae9fc55d5c0900ec3cc20d39e32dfe0e15c8797c34d290f6072fdf80ba1b61a213837bad1d1ac6326b60d1839626919f93d08ebf6337eaeed86435d79280a393f253a04178e873bbe8d64899cab21b86e109cb39db8cdbc51edd91000b17bbd0dfc757b7fbe1d7a784fdb11216579fa993f6e9061e6692f29a49217f82050f5c0efd53d47a0cdf6c35113e97826ad7e0856f17394d1d4716701f8b218157c0b308b0ee322ead47dd145c7d272e04af3879e9acf7042e5fa058ac76343577d5851233bee0849110728a0a3cc7a67b0003a043376c0a0d2936d574ed017763fd4794bce972e21096f6b66a7adc02c095f50605255a92f6a5df88a784f0aa6c23821a557c7522d0853698856d9ec769d0e837a46e7fce58acaa9f67de87f32a6f32ddfe332604ae2e2b50c6d26f00e4a7fcde3025b75ee67d863123db3248b94fb47a0237402783d1dfa400262f3665792542487f779f0f7b06d1a6e7051b857ca2a29901daf2800a7b1f2822313f74b5427dd15dcf50fb9067df5a66c833f8fc76d84d9a4fe11487f7d723a13d8ec91425cd1af8dc98afbe1c2700d3e8e23ee132928c313e3b8c891ec6d49343904cb14541e9e488a7a7724871947001a479764230688826e5908d784de8b3029230fdff90f18f74db90cf943320f9c71eb469a0c5536732b1382894bc4a5f14ef930e05b1c8f0ec02f6badd50807dbb45c82b8110f869c60a1df1d9f39aa5a4307c56080d1b7ea4914bac311cf443cfb3799306f56dd64e00aca050735b5e3e603662c8a8d2170ed0a436df407cfa91f413d8af629b6b043b73d4b5b2536584656bc6d7aad86178a99c7f9a6393cdbdc474b3d471d0ee2f3f6a3228f8fae9dce3847e1e6a58202a4dfb23f32cd4bd9d3b5ed282081cba9a4745916eb9fdd5b20778279993c1117ff4fc32cbfd28acef3db9a7ac361cb9446c5851af9de285852fab61c4fd16361b62528850c2e0cf6679c250d01919564ca7bc844cfdcd4145cf45e6c93f294025b0b12e207c0cca37c1cce7bb4453a84cbdd3c43ce1be4e9a00ab8704da1c3e4a9d94a9ec346b06ee3d57826329e2d72f5ec698acaf8800fdbcb87dd9dd0b6486a1d76182af26f0596c55a3acf680dbbf626f8c724cb1104aee280234d33c4c74f2279b990bd2df7498d5b637886609203b784a4623c3c9586f3a7367283c7589214efa91a8bac4727473001e14d2a25269468152891d53a9352695dd6e4681c7a8c3cc9dd6b8fae5aae001f039148e07b2ad971d9bba4b1e698c889d2bfc08cb62f93cfbc830d119255ee834307173c8b4337dce18af21154e324cfd68c83ab83d8e9eb5484e5e605c8c29e242363117cd1e662f6a1fca25c75399b650cade19a1f1068c975af7d3c79017b1cf10d7508f82c7925cf333bee0e97e3175a517bb585e69ee653e7de87e50675723b72e081bc75ef24b7a945545cb26006dff0d97d9c31ff99b314c69faa61485ac2ba9b97c9e8f76ad6fa93e0bc8c820a83a3f4fd1df69d445c2e73bea28627b3541d88e7e5cf36bb36a16a628a0fec8be0ba6c36b20e9333f34e73f6ae9aa774759b537ed19dfc7583f9113d4f6dd1b9c53261b6a87240400de285002aaa4de2aef2a4f202aecad22fa65d59de56f7a91c14881b08d975582dc08d17f6d8e6c5f6eb9afa910c09cde6150e3f8835beba263364d3f5d85e5ec2f877bad2b9d395e5db9ffc057e0cde178c065d3907a70851c19c15de3032ad340a633a60b662a85eadc9624cd8a20c35c651adad037de739cb01b8b14e9acfd5bce915a4c055fe281a711b0a685ffcad78e9c3f53fd69cb82377265abc5af3612c81348d8b318300416914c94f9fd1f66315d2884883aba170d2175b5eb5bce03da1b67e0e303c2080128942a06a847363a9e1ed434d6a08877c7981a40fd6beacc87f81544a4c2ba8301f207e1dcc477f3188551428ca9a25e78d062c8fceeebfca48a2458f4200b3e5ba297b1593ac84543437e0b7ee98e4d5a119bae99496acd7e1823cdf8c663e0c95c8de24d530ef58d9882ceda656c96f2fd41855f2ccd520f11a700637dbf3ecd32f8db61cadbbaa6e15d38daa75f8520611e9db637e63482cc555306568f315ee37e8234a3176473451133a5475d3c22006d46c72dda6b391fccb06b3b0c2a6eddbea60b2d02ad431fa982d09ae667207a7c48e9bfe8e1152fd598a9e3f0f340638f2a05d41fd42ec8a620d3d7dede28e19cf4cb0f801ca06d9e5244760ace36443f07e3a6a9fcac0f250d0b4273d14e1035d097217d03dfe445e662cc7e9834980b12ad492c4300211857272c193a48aa0aef31946888303b6351c5686fc83e3d095e7170a08ef8fb09286ef87e188567802233745a50d086344da80c6e988f7b18c1539140bca2891d38401fb8bafe7cf2a27a39cd8aadcc4c26493d4db6bd5bb53e4fcaca6e3dd9b1dc8aa90d9169ddd91760ed98692620a34ae3912b3d8db2a06940097150edb61e67590a695b7d94bcb4d6731300d8bf007aea307d807bd4b58b3ddefd949dd582c19333e75705605bacee9c2b31014d3bdb5a757ca5fdb6a67a21557edf54bfc4191c5582865ceb97f27dfa326474342643a24ff946f8d7c2911b1b7d6e27e5e037304b3a3bc3ec879db1c378db0aed224566f4b2b0344fb735a26c37ed04d78c692ec70617640da0adcfbea1b94986699d79bcdc952eeee62bce25da11a2e55b03a5b8c4c5360176596b8afb0685eda03883dbbdb01e416bf608a40c68df34ea88ca11f86df8c46806bb94acad38b336876f8a358c4c22c597ce3c69f3d804c0b43fba70a1c87d4b15526a4e59754788a342a2fa5cce82064821d32c4498e2cdcb0d78fdfe2fad1d99ce2c8fc4c12afa7865d51be4ca1adf0b33f46b9d07469efdcc61b6ff2225e9b64ccefa115e2a90729fd9ceabd7264b438a27f0c74078298b0dd7e05bf548e78623992320477be4930309ee6eebd2ef79dd972da6e05b906e4018d66835ffb5a07a741b365aaaf5394cee7459f30b510c7e651043436cb6c1e7f5f75c4f6d5ee4443b42ffc410ba7a232547886161cd8633759321f77aff0589bf961a0bb96689b521bd957207a1a5b4816e71ff0dbf5b6dc920ed5d580d56c661b0403dd4841c8c3201ebadbd195541080c49086cbbb86a8d8cc20d36f173c2bd7b673eda062caef211379a9bf759ad41b0a8f944150e89f0ffcfd8f6fe5e9559297648eb43b5fa0d365680efdde566488b71f7d078ec5987ecb063ffd1c4cfa6a06c424135b2a826f1d6d08c6c40db6718e4757c89954f3b0086740ee16d2375106eba295d0941f9d49a884d47d8bc7a5b7cbf08e8d38e83347b1ecba60530d692bef9edeae215a3dc02610c0b003b8cd8d3b0434e8b8f47a09e195d911a50c2db91acd9c68a9de3cfb956522c8a00b050d2ec9282de6112b40989e0ae92cdde1f5a152c6ba58a1e5a6532890a0eebab0fe5bb949c7cd60fa3ec84771c5b9405f03ccb5e208625e513ebdacb9b824b984efa2321cc6b0268f4dfbad391b2b76d04a036f61cc283951971296961e3c53d4855c234ac58a6f9096f423a281df1fe01d47c79343dbd314f757471705bbe1226c2c97af26c3ad7df5fa61b55eabecabfc8ec3e306e461364e39246f20f0dade0620d6cbd69d4c794e07c0b9f62493c95eef6af201afbe736e4bb2d105224805f7d91769776bd71822e46cae4e68dade87484c3b6321b7d31541a753973bbbe0710b94868beb496238033365ac8969b04a8f85323eb0e2eff8dae8d815373734d015653b8e94e085f5b8bef26de31320632da87a385b25c8587e9b0b7b9f50321d76955ebfc6728c75afc0960845cf0e1b3bdc1a276db77d1dad5ef39001fd023234c203be29f9506987499c0a4a4105bd9f57c643b65de2de66872ddab73b87b11c1ebf77f85ffa19f25dd81be39ef051f6392e04ad1afd4534b5ab3f4d4aabf78fc596b4fadb6a86b21c2094371f2415dfa5587ad3403e62c5481c927919821ce2c86585c54ac1b69016912121cc5e4c28c6c70746b1865858227f1b6251b16018cb637f991784a85ee946c7a2a29a8e546abfe1b2c2c1171cf4df753ae44b41f223feb4b1471d5c434cbc3597df5c3ce576ecf960dc242b032fd0e78e77efe873fedb6e70bd3bbdb50a0aeb96babb8b9b323f7485bdb9d53e8b3860903d1e51bb6df2832cc808fb51fc07adebd3ee51afe2c1608ff70b4ca38143e07a50a4b79ff885fd09bcde1040a9d73b88076df0a7f60f80cf8c498a36df1f1d5c2f8a5fef61b537fdad0d05c9c4f50945bdd28dcdfdb0ee7aeac941e6e4b9ce3854b76873e738cbed062afaac722533fd53fb0ee4e39562b3aa2e83e1a832ab65f4bb1a86cbb675599e9920facf277c97bc57df5d36967a2e75ac3583823a52802765cb75ab318f8da27aa86c5a1b41bf50c4a7b6a385883d40d53978d1748ec85b6c3e671a4b485dbf22c58ae4a114eac6e83e1f71db737231bee51ffa5d96314c7a66d67242596c77cb65f32e51f1ea6b61e4bc1ec395c5b24e1026bdb92c2807d95e523ec6087f1651d877a4b4397f604f5a364cc49d15e70e712a718e8c4b35b06027fcbb96ac8869089ac0236fc42a5042eaef949c1683293f0397497ec63623d206e9ecbe5170a96671c2cfaefaf2e88fc30ff47f6dc6bfc914b7780343cd1ed5b1a152b0b45abb6ee34f19d2665825a8434f19c3cca023a775d828c026532774ef7a8a187bdd0e5fb6f4e1ce3d71c01934b29e1da139c856fda8843b69c70e1d94d07c472847164a51988acabfb1199b8f9615d648c1795d9188733d17444d020ce71420e5f7b8294649ba2104fbf9a09b20a85f104b43224d9fb69faf5a61f41a90189e65e0376f24a1bb2c98f64a956dfa347260c22a96b01601962defd6e3faabab5289c403b390e93e3df8b123af1c68e4250bce5c162c35f127a12bba02269f3637e3cc91ee3bef10adddad105fd2b28be4d8b721c7dcc5b0923d98c9e5968d334ca50994a7d073bfa821cb0d94d644e49f6ed345bd041605e898d6f64d7289d447879ef7b82e0109f5dc2ab02614d4a1353097166f6f634e90ad0f9d53215644d22116d4f457440f8e3925c8007500cd5bfead5d88b0b8ab8d2c4d8f5c9bc1bb43bd70c372f40fdaa60b7d80c2497e13260dc37ba23190b140b66cfda3478b7f0e9d4d61b7e21f46492b8b4ba383f8a75a3b7703db9e6e21378968864093cea93edfee3c2d034e4bcde35fa751054b8e56a225e022da25c8495cc1650ae011325f3900bf415f2ef8b0d195ea7f1319a3b064f0fbe95d7cd4a92574ff33740c9a99b84baa265c88d7dec6aeeb06b0043a2e9b1cd503a1e10a70262a0f77d600445b254e2e0578cd8e7d1dfdf68bc97e8a77cac38152cafe84455907d123860a635a913c1d7dd29e95bc601ed52766a107ba73a2d81283400b7480cc6cae1b5926dfec5c934f136ae9f3e135a6ac2158fa525874f6a50a22afa939a848e217b4776e68683449b6cba1a86967e18c17ae528ef08a3a00621d2a8fd3ef883f840fac1310d8a33db0fe7fea792d0960c1fbdf6bc868927037ff3febe104c52acd04405fc8bf22400f71ac629399c69300144a057d1c48f9d65413d67f880022e3ef6284dd7784d4ff18566f880490719c714f13d8e2f86865620050e80abf345fa396c788ab1f470863d75bb04dec472709f1306adaa3f4f67ffed8e91a0128ba6d8c18bde7a71a0e9772e68d1061cc73cd51dd8e1bec9eacb28273509d6841c06bc9f4156da7985afe7482cdf7a1d512d4f94df0ea953550abe01386fe39f48a245fed72dab43cb9c41b6d7e9ec8df9764258a114f2e29f410c52b15385177409c0cbb038841fc96d1d5927d06f38f8d6e3240fee5deeba74405013820011851fa456c4827bdaa553ca468ad50d32c8a59362cb86cd56bae5d7ba343138f234030421809243da4a84b1c22f8caa3781eaf42e21386d242da27ecc1c0638f4f808aa52f6d12ddb2f229474727a4df58bd999aec7267fbbc509693c68bd83b6d23638e32af36e761f2553b7a8ebdf9eebedc60924efb2430c24cc4d7762e7f8b3f60f4f4442e60f0fc71b376363e2047e7841a7a0a71322ed00bf978a84eb78abbb2b61dbdfae0a033f577b3401c8ca73c1ed8fdbcccaf256c2f2f6d96b957c330d987f127de0926178df9c7bbcc6b50560aa2c987c88f184eace686cf92268b5ea64307f208859adb5dbc294c19d5606d28eb214c52287a6b1029073d520f8775dd997df4b905c6f854915f110e5b37dfec6d087dc9a47525ec665fa81e2df0e2eb73bb92dba7f8294812816efaa44d8622bac55bc340c9a4b28b4b84304a195dba557ec86c7c31177c868d3a2713d309f482dd9c80a98ad91e391b84bfbcd73be09aa512991027edfe15005fcf01f68eb70c0e5e6102084d1c7d4920b72084dddd640b34e42877ae51575b8061d65b8112679fc2be0eefd991e0393ac6636bc907e89a052429644247ca1f747fbd19b4b01a5cee957985b18b3ed961984b11fd17c160c7da93a57914e43ed52c8ff0af19acc47e769d17e81c042257203a48228ffd2a27ec594873d53f2635040877ebec82974cfe2683578ae42b639aae498da26de40a083b532338a21e55bcc3571a8e03c07cccfd7a002d1c47337547def11e6d1feedb4f26dfd3fc64b14492988609f328096bacb3d17a013a95c381f948659244a3556a52055295a62517fa802748efe6c0bd87cb2984bf478a3317f020adc9cd67e9a1788e9aea29009a2b31525eb27150384a1236dbcd5112ed90496d9518c5e0146f1b53283d3a731012c98b792d99bcc78c552a432bb42a347e67aca2da7b8e88a21a8cc5b196a59646047485ce361b56e487edb39f8f4976ab16a7fd6d42bad036261496927f6b5a37b421b7a59056776abdd7ceaa2fc0ac6039b1e22d2ca8f9b42c1c005be2c93198a82cfb52324f7d7a0519dbaa3c3b388d79c71f745bc1c2acdad28124e3a9b4f6371c62ffe24c4b9a39c0101fb1c3d8f26a413053bee589bdb1463f9148607ad4bdea117e392e3b9ad48ecd1ee1c6f6a841f708f7de7f12a3fec22653eca6136f8e50653c7b84f441aa171484408de7434ac3296fa74ac230929e37f7e6548a91cfa6c496ea86db6dfd331782460368621e53fd0fa7f879eb8e23fa237cd891f785e316c0fcb3a0b0419f60e406c3e3498af8dfc0a637ca0a0ff8aa0a8f2b718d9f8ccd219402902e207c8d2a9884844e310c99288a7680854f103520d944b9ea8771ea12087b637b7db32369f476125e9d869191f74e277917a823ef5693bc128fe4c1f9210f1571fe46def3192e85ccf7b7ad6d22e45585c57bcb93809fcb1727afab0d26f8fd4f0c0b82ab53b87596bf913b98c839db9e1cb01f447a53d98753d7a24b19a396bf7f3d29cd37757b5ecf90da455136f7f916703d39bb83ca40be66d7ebd78e2260f5580ab114605380ad10b04279cdf1f3d292964b36a120bb4a956cc739ca3cee5886a2958184c3de77a3d1b772ddc472720668ad76759c15e25204e3e6a8eb3dbc89703d24c791ea980721ac7d149d28418b52f6feecf68a98a025b2567220fc0f7c6333901ac94737093c0534bdb7689eb6bcac334a31c5abba000179a4c00dfd07c7b23300c96961e3fab3b2c14f254c1828c2a146d898fd497e403d426eaa1e850c2a653a58f00c81c0f131e0e65869079b2688d9c01adeeff5cfab1840bde832d6bf88847fd638650ab76fb17c208ba232bcfb0b941729ae992c78ad94488f647e1a718f075cfed2fea2af613204d577f05323f31c80cd3352856ac4a3187195e0545b28b64b5da0b4bffc047bf1496d4c312223dd379f6c08d75c0cd1f4daea1fefbc7a43be5162e6528e2e581a73f7cb306de4642db9bd2126c175e2db99464f545b096a00d23790ce02662aa800e44450c021c504f9244850524d04fac9281ac7c573b061469e5e4962df2637fb11a4c3dc15253d77d26d32faec54abcf6203f64ed2506e9bef3f6e6d278518d69bf4a74dc53e55f3c547f1df07ee3293431e3bfbdf29114aed51d2b39fcca47205818b7436940bb32eed1b86b5047c41afdc1be59235bc6fa9dd2c5ca4449f9682cb8aa102c8a2bcaa56b89b9ddbc1c2bdeddc482d5ce5e4a572e1b6bc9a020d84905402004870fa66e5fc657a63f660cd239927ba3db50deec0f4153133c3efdf79873c460c9852127f072f91633c44139e2e57904af81f0e7f82080d4ea857894309fe1389b09b3463d428c12531777d6c7b239ea8211b1b5112bc27763b637421b40a5909906d8bae894505d0be455e0cf9f95045aac962b26cc533c7828a36a6d44dd49f312aa48535d842817adbc180c1b7e9c62d71e1db048e03f6152a03e63d76aa1c6a8e3ce33b8e9ca18b8538502f730abd2df79df06b460fa8fb80a3d2011b753631c4f163e6fc94a12a769c15e19ea1b6b71a3cb37dc372f8311713dc8deda4e537a5d950b5193501b7f3b41150271ee82cd28ee9b06fa29c8ed7686139970704815bb1836b5a3e0c2cb24afc804a2612bd6760ef6a15ad7a9654f794d8a6b3e526ce016287774c072871bbd04d7ea840cd471dee76c09a8d06404141650da2bf142e0f4bc8fac0bb4c75e809ee680be7603616038844570eb3daebc76ec02d02a40e1c2b0c19d3962def590a85cb7f41399df733c444703811e4fd4b4e4a7dab6d9293f025fd35de5f2bed64fde525275b11398d0635b450a5c39a439e57dbad3fc946065b352923087b50d2cfe5909002e3696c3e74215d881852db2c58c30bea5db0808a760dcfa50ac441cc73902e887b9e4c6c62b31789cb272083c4b2f20eeeff79209b5a694bcb287abe212f8cc2b5c2e4baadace792c8106fd6e2092695c33ef087b48814d7305d5d05efee1374080431dc9a697292969b33bc1838c2127a74c5b6a2278e71ced592ef27bf5f21bbd1a08218af9f9eb1d3e423cb9e0c66829194c50555ec74a6b8a841626674417945ab2918a0050a8c212e14cff82d9f7ddc96b12b848600d7078799081ae7a7c0d41a2734c1dd09235ad09ac4f2089606ad33fa3c8ad3701f280c75322a4291da44abe70b013356c81915d2508ef7fc62e09d82623568f0775cfab54d3c3cd009163b57947c2add1078c59de798117c9d2c8630d5084d0519298ddaa4370b5616d680032fd98ac9933a8e4261f3f47e620d1c07cca8e0683812283e95c85c438529c6f76bc803a845ecfa432dd747834bbd87104a383726fb323acaaf539515f0f13b943ce1fd078a8fa094fc94c0d13db8edc37efae730671e5b8814a137ee942e2e1bcb894bbbf4d34e8fdac6cead22c7fdcdbafed71b195b0f08ff9e9b3e3bdeeb9b0182667836d9be8a2bfc4fed2efb1bd639f893bd472b7ddec0be3660794f2cd6839289f5b76c2824b7ee3d4e73a84d2bec44d7e14a202bb82adec8bd6aaccc5d63244746969cf43b82f7ed00aba0b0dad8feb133140afd282dca54b0a27fa8d337d07d82beb9126dc4e77e8e8a5b1c893862405f615256f1f5295fb3d9a8e36c5a8c9b2a8ffbde1f3e127f6bf77ccb1c3a004fab61212a87c409b0886f1e0318c4d6aef90818ed1b149df188aec29b4cc4b400947e151a00ed5e4dfce385cc506002a19358ae2755a6266e2c863ddd1e06b8d08a0b6f22d7245729caa67ffbc3083ccf68320ba059270f70bbfeede1504c575fda3624635967c008bfe7a53380d15c98cd4d5fba0dc55af29e45d29484362c435e60be0254f8af0afa628ee24d05efe9a21c58fde0a3ee088f18323dade28299934b8a64b3b0e51c6a8491b8ce814b305a52608c668d85b5b2c0a4cd07533fc9f3a3342050cf87b2b858f27a870518c8e20d35033d91dd5cf4ac55136f62fab9cb9e3a5ac1a84b58ccb6135f130446500ee6ee8d74690d7afd2fcfc6f53ba86050bbead1bc21dd96379ea26dd416b9f1dce62ee9e8ceddd0cbf8847a79ede441ae38c07d7f07a74a60533c9f85bc42956551b256e6d7ea0efa2af4ea14c10cf57db2587cdbf946551d23ba7b66481a2218b2ac08a6c4ce9335e39f48a6969b281c38428b104eb1f7299a90bc4fd106360870ed5dab309a15250a93b3abcc013638dcc538c62b2039763b1c5c7b11d81002744d4c87e13e3dfaff7f546ab850b7880572af273ad838303962b18d8170c6e66839dd84e982ed8aaa9e38081be3feef16098efb7f33e42e930121aa3185394bf72330c5d3f5ed567b59626eb00ce4bf6eb6883f64978f3357f462cfb3f0974f0eed77f2678ad43697e6416880b147db4473aaa8046239059ce88350ed172f8dedb6190ed36459b1a5a5963e2c259db5ba23835eeedde26babbe2b0c408b4841e84741b34d988454c1a54b060c6b75613ac7d41a597d557b23412f29893d999285a123232f59fa39417750f458ee0e13877881db3f8c86017683d83d668d545d2a426b25da043da04d111ed69becb26f64dabe4aba0a05c77fb6b8ed1a185f5d66120a68e683bf025581b3ea432a486ffe18cf418ed0547bede9262e2bfcdb7efa44241dc1786252a61d05142d99ffac09cc69665925a3c53cc737a0a10ebc6c6a0ab28e1005aed7ad9b224b5edce4b555a531000e4e73dd6c25418f69144036643e54ffa96276685ad4015978f438bb72929b1b79d2cf8924c53123093fdb11920c9d723fb57a12b83e0b48a642102e91f4098ed2a6ff7b3da52833145373882e62f65d9961ea4dc1e07b35ef53d9dfaadcc1823f0755b57fff7a35473a5c80dc258df0b9fb64c3aa57806bb235061791857627d039862bb1be12ea05dbece4c13ddb866bbf49516349a8ff5c3c91fc9a6eaf01e086b1c76693024e845d99affecc51fe9ba64f9062918c051e8eb922f2f4c53103a268a3f3d17d28a3c2a69daec03e06419e01b511d80135cc22b2eaf72f87dc5b5b3847cab0a2b42dd785e7f4115d86247aa73e2628ba7afc207d80f401ba003e561c7e4cfa8477b8e5beeb8834713a99d78502939ca872a874ea5a2565861fcddca64ff36da1385844865712be0a087bc80b60c0c8280315b4a584160b8629b33098bb234ab8f5d85566974d9beb27c53f42614606aae68faf4d4a05a5aa9bb31cde5ce4319840e89c81845765df7fdb4691a692f1d6c22cb7324ceee2d0b9890054c53dffb666851e542e60fbe2e1cd75b8417cb06d58b81315a4603964ceb81162593ffd02667041a6c15574bbeb2ac6bd9e18f54007b3850bb53a952d749ec93ae1ab37f169c0765e47b09e2ca2642e8f5604bea77df5786197cccdfb3f1ae5192e2c90b4e4267dc110a664bc44cac50ac0565d761af39d6590579f5e7e5f95b7f0b5496ba9828f7f08d2afdf9af445ca0401d6e874aabd5ac8b660797cf8e6d29078195c948e3b5609936befeca336590d0b0854015315b7b874b6e3750c00cfe865806ad3af55f8de10270688a79890207e7462feff65a0a45589f38caeb8378f8cd7d962c70b0004db4fc61d53ed1b492ace4da75757ab9b90e61bb3912e9490eb0d35b03bf50eba842069ef1c3acfce0ec6ae60e78851bc09ef2a2ab81053059482ec3d04aabecf1a28ac85fa4332f697525b1e05f0779ce36a1f2d990adbce0d58fe178153d34e28aec1a8b2ab128627a509d7cc99af48376618a7d870c2eb1d373c548239cca0333e20dd0028480107124c3df253782280b8521a0259abfe07fcc0ac0c6200c3f99d5528972bee4ef4e5ca15ee5c936738275ab4321dd7494fc3314f3b781c09e8dba7ddf8d14a81791547c72b2aa22ba69a807d7ce8130dceb12b2cb4cf824d91db24d379964238e758a97016a787536c72d81070d2bb97b065e0c62116472c74196ad17e1be0b8a3268a871400baafb6d77ecb3f43bab7a701b05682e7a3f68fc2c95e87abaa8a9b292eacdc671017d1d0f23b3e0c6b9e38c0d304ae01ab9b9a806e8689db34baf9000d724df28006e89b093565cdb983434441acb1340e6e8085d6470c0d2862aabf4be86ef262b467dac336ec4d05aa2f9b3f74ba3de5ae532e4a7e7cc89706533f3487da22ee53663f4dc8f3cde006a04fa2115f90d1280f089378a03823e9581ea039a59b7af43ba7542249f5e6cf3246152acf683a8c8d96e02ae9cda8e01eb0474aaf6ae45fe8802647f4471c78cc228d606c5209a77f7050487a0c0154ed2fedb2b693685cf5b5f80ac750034919624a2322e9a2ee40a30fe570a52d11062ccaf2972012e225cb26e2a0416438b79ddc7728f0f92c93767159e88dd8aba6e2c975f4f0fe82897e8e74a9244fb4a1956e7cf52b8b4d977b3de77d2179c6d7ce05b39ac29419baf68c05d179fc6e260797c867a2a9aa08911deaf9cc5436397fc966014b4f486601f68f43c3eb7dfe611c10291d8e7909adf1ce098c73f72e6e7778a087f5ba8b2129859af9534a8c8073854aed7648977becbd5b0d7f0cbcadcc98f849556c3960856c3d4805ea03001a6bb47db2811ab7f63e4314b5f30baf18a50d7092b660b0179cda4669a168c21fe782a75a3f8a12005c430eece7b0578f5e5fa03cf487389b1af757d7b0fa9535e83e76adae7352199bce022c9874ce34fdcb1a15216e2add176611dd73a42743bd4cb020c832d3cadf6a49586450ca785577dab9c5b28afc76aa4de9ba5d3dd632564324f0e171dbc37af1411a3907639af4fdb88c0a4d5740f3d510174532a24f7d03a0ab88e307a3e8a9757975f0fb7446e4a2868fcea7a08cdd4cc21dd6b719ab08b652f802624644f1bb78d058b8586e139c050af86bb80b69349fe69d3a9a6e5e630df68775ba6c16c69b4b14b2ca99d0404712d4c85d351b47c4b8392e1c34f72c96b8505ed43b1db6c3e67c68cca9ae778e63c76f37f7d9b43e71bb35c532f4310cb362eccd46ecf2b2f275249b369400dab73b6b92cc27dc99888bfa864e4d8c22bee9cde7b96004da58f4bc8113a7a3128b80da593b776edf6b0b1bb963fd9eba6cc550c6bf9a5e09c1627068b57ed155099fcd38c967190628292a7308c32ec6c6987533e20b21bc8910fc4f2cbf1d02e363173eadb14665c8f6c607f700df8a14d580e10803ab2a7aa0f261fa8f34a2e7590203c968c42f4c175fb928f182de4e115d02fa6f02d864fdab34cac14468222aefe69482dd3724de11638f28a41108018699207979cb80c44745510c385cc208ecc5ec23b80f9d923f043189570983011dc819c4f5f0bd842713c16de90d4bbc902621c6510440e091338678fc94fbd0ddb7dd88de5b1fa377c204b3f87172656207d0b834a7b5ddedc786f9decce2c2407d6729f3f94f00c6f382d916cf493a7dbf365315bdaacc2c0f4b17528c4914749ea0c25582decf3623758a8aa23d840c1138b81e0230eaea73bd70a0d8a01bc536d56d5c31df3e2dce73ac7709cca98410bcec6fd0f604565c4553edf6c8631f53b062f13913055fc5fdfadba0a8a8a9bed8eb30147b69cc4c1c3a37d00155badf471bca459aca37b716666921c7c805d1e7261a589528c11518b81efe318ab0b57550a7a1fc17db8a6196f96cadf4472366479d988ff011ce6673e082ae5a19b68773a71d147fa2d2b4a368ac65ad0a814bde8149ceb12842fa267b8bf6a36f7f51eb54200e1b2951ba5d29edc7af743c4771c44d3b23ab9ba0f68b572304333dc0235669e08d895b8678104ec9b3a3c0c6f9f4bafb2010fcf28d871f7224c92f98ddd8c8abd5939d58706acd603f889591c3089f52c6c343f80a25257b70112715a17461c5bbc91b5d253b0f67bb334acf3d89ecd125a7b2b48e537d4af50912db669cae32ced28f6de12fb45367abf10c341e2d139f034522ade1092255a267025cc061f16276b23bed137c4259979e60efb8d47583b0273799c422502e9d9648e676718383df86899b3334fb45e520a1db0ad905a58b1d5d9370e5915c0d5a80d3efd40c31f83b38e70b9ba5498c1a9ef8e215785acc86f7c0b8b44fadbd375bd5bcd1935ed7cb28d59fcd21b498fdec7e2b77583c33fbbc0921f2082811fbc773ae6896917b7a31df6779747c74ca268b59a8468da69db1c5fe4a46480e57600dff5008cd92fbfa569ef9be951b947434d06e7741ecc4f4c0cfff17599d594c7e4fc7a4522a956f5256d16e1b4e9be017b505843d348cfce719a7729e26fab000330b6f492379da4f9ddf84573f7ff6aa5fb837e6877136437e25539bd8c989ba74f3e5da54c29cd3417700d57f2f13ec5d21e2a76256d20a55cb344da1f8437fba90270d21cd7e7b519d2524398a3df57943a5f563caafb8fa48bbad18f07ba60bfbfd5b6ba56b8a5f98466545ad7f0da6235e045f0fad61260fe099e86717cee1e43e7364ade447a50dd504d2f3f10850ff0c66a56c80e2dba4b8afcc02b8f7d57b5b6c0b0dfbbfffcd4fef4783613aa487cb631d200b896cdf86f009cda4a23ead056585ebf1b8956ebb165386a0038dafb1ff3d3009a855158235ba8ce20f2facc54159ab82b1635e03f73b976caba30b7ce49bb5950da3fdb61c2013b16fb9e902b05136d982ae1e4054a1358acfa956cdca4e2c72b2695586d013870d4f3949cd80ede6ad87a09d9a7da908804529da26663b010f43ab3a3a093e70048513e967669847e121a35f92d8b11a3045dd6c6ed0513996cb1a6da0c97b843298d0c9da2e6f9e50d5710ce672a07a62bf66fa2442ca01af480e9bd55c70e7b29a74ae3e7cc9ce63d5bd488fac512a82ac21dc5de502ff5366a27563dc99eb3a441cd68a45399b4e1844d510d4f83d88fb7fab1284729aee5cc19f165a4db69ae6feebe89d2324445292797fb3a4f7849512919447b744687ffe188248c44f5b7530c2798513094982b049c81afd12d37f7977041009ebce7da9f3db4f381cdb01b4a57fb16678004ccf0989a013b22ebd208daa07fc05ae2157d73facc1f54fe7ca2ea4e9ae4bdd28e239039a525a9968418985948f15a027744602e7ef51782cc3ff30dd913f547de0281a940419809f2e3952b51c09d821e3344147d495e7a742b4e939bd8374036a4e2c0318c5eb675f8bb7c8a8014b641c2a6b23455f1f2e6ec1305b65bdd90e072169b685b80025dfcbea4a2f235c6a34e2bba1788af8ee1876a700320840cfc214d10b5d7e817bdaa3af418d1aae5ca396330f7c8fafff071191f7ecacb77deeb088cc361d22103c6808fd2251c81cf8a603db92e14b6526366cb1612109fd6e8ce7e098c68748841f4ebc75d26f8d3ac0af6364f64b66ee6653b305f34fe25e82881e3887dd7a931058c3026e7e15447c410827b3e047180c49790754fce91da6617b68ec11b91a90b4bf0dc8b1ea069ca882a71805b8ecb542c18ce35e1f1bb82ddd866dd42a7cadd58b57af88aba00cdd135f6b5ce3ab9d0d919099d93414ece8141bf26a9c607a0939f8a9a9483ce93512062b94ce04fe45c4300e5820945dad0e8f1f25ed34dd7a70ba3c973dfae6bbe6b5979e560cb4183c931c1ca5183429017fbe9b70186445746bbab1fc50216269f754491255b66757476ff5c5b20a2d9e0ffdf87e9b9572648e40abff446664bb9bc814740e9e0e710d308c1851a47a7aef5d4820986f21815e7c88225f245ffc78417a0cb9b75286477d48a62e48ae05b9b792c589852a25a23c852a313c852ea796173f6441a64394a747bd77fab195a7f7531031fc169753783afde924fee9744a39613c91a326b3856f6a69f92d05f32c3e8451a15e5c54e3c3a8502faa914c5d5c542d8f8aa922de9285cba3503d50ef820a4fa1680ac337a1fea442fde934fef72d2e220964fa303cd2a47cf91732553b2ba8262ea877115bd29104d3f035ea747a51959ac178badfe22755aa762da9198ca8b0b3529efe6432ad534adf508e34f172c0b2b3e2cd34183f80de7710f848ef721ab88d7f3fed9450fa27e13a6dfb1e1cd91d75479a266aaa54b7719c1167c475a2a6eabacef38cbc4fd454dff77d1f6864646464d4a58c4a4625a39251c9a86454322a19954cdae9b7efa19129e8488d3372c7f07a0b7372b49c6dcbe1382ea7ebbacef33ccfcb19cb9b939484054b8a06e17803262e2da5f14f2a32f5665e7229b9b838470515a0830ea2fe14a24c54a0a888a2445111862614ea5dfe8422534c1c991d99a14ce38ba40945ea5b868ff26084648a891125bee9736ca8f04d21ca84528561380b5133d30c85323dea439409850adff42813ea439483a0a79c308acf815450811ac799e8d2f22c5422ea5d5ac8f434a6e32c27c5c428fe894cbd5929cec6198a0a2a4ee36c361e99cd50214a2b51b31cd48be2a354292666e12c4c31319badb33d3b322bfd1d9c3151927a41138ca64842f566ac228e35e39e06da87dad5b46ddb388ee3baaeeb3acff33ccffb58dd872d4949a046a66000b2ba0f4bda2d53d06d9c7b0eed07476e9aea8ea1fcaa1086ae89b7f45fa8fddd464ee3c01e5bf97d0ff7e165c97b0ef5a7878008e307d49f9e07459e78c40ee943f0fbd20fd347765e22532d18bd0e24bfef589d12caed89e0927cdac684b04a67e57225cef41cc875396637bd20c77124c775473857683849385942274229aa108a51d9ad60d47e056377d449d12185556ee7054f49ea15951cab88e3384ebb57236fc769efb134d6925ba32929bbe78a4a71a6c974291578b34e05e34c8957a4d97020934e04e1f69d95ee1e992e1982d309751ac1987658c2a3f0a8c3121e75589c8447479a8df836489a8d979e152116cde6949474c589c4b942c449c2c982931442114e09ab4cd16cb8efaa6836dfdf5217564985a0d4954ad06199a26456e464bc1d17baa4ee0a25b3229c2c52948453ac68366938a5dc7e1642111e85527458c2239c2c385760c1c9027e2a139996c81479c5d9b7892308b7f766f7c68c23084230458231f59a784d361be2932dc6f620283e69921281f8447c222a21426142b3f19ad06ca46836e1942950802c25acd96da2c96cffa9442762e744b3d16ea9139d6836e905c1fd52574a9960fcde45118c17fcedbd243c279e0934cf7be241612db9354f20c10ab18966937a4d4423cd26159b944d9278a2d96c2f26213ed16cfc09244c29b7179548bd26e213d6129612d09b782952ac124611862edefb7d49becfa1e1245db13b1a47323d0df18ac049c2c992c36bf9eebd97f33cef7e9f8b9454084620f03bf05fb3b924e875583a2bac253a2e6827958ba80aef293c729aedc3534e4aca51b9755e11e57637bfd14a2f3cf21b1046116337054f3b2b25f8dd156497a4c96c0fbee9419acdc5a8a43a4a2a2f12f2eb521729a50b60138b52201881beef4aaf2b47cde63fc6f35e84712e9c69321e498437632da1a4f44a7763cdbeed5375cf9a7d5df77dd76daa52e97edf072a612d61157daaee722293924b31293def725c477a4c9cc6fb1c5a4a07a3f7a9c76423bbf717262526629884a0c9132750923081124d70b228d18431158bc42a289c249c2b709270b2e498a150384938597092bccc31c349c2c9e29b8dd3bda4f7625472555e8c7092bc57bd3cd962702faa5ea0949c58058a31f59a88559460a20927a038a249139c2c1d962e6956c4248649e90200b73004b3929be288f245e94f4881a50caf9802c10874bf2b4b23eebd293be89af09cd06c3e294aee53548ca96854724838220a2965d88948b8d38fad14939046b652cbd1c02f81647ab5d310f0964af7963ec706aac4d20d35d5a7ea3e53f761193e6bc65ac2ea7e84e5167e228b59c9a52c66da29c7f3fe92a1aaf3d3cc698030a6de6956722f4aa155517256941c174aeeaf2839139429c8bd07050909cabc9763238d6c0e32199b941cf7c21484d1058542a13e170f857271214b8fe2bcee847a94f89c8b8bcbb394b8a0bcbc2f2e2ef7a25e145146b6979677974fc134bda86ffccf45fc96f17226d6925b83622d612de19698e0092630195beeb7b4dc6f19719270ae18c116af6b19c796716c19c796716cf91c5b4b8b0bf7d2a2a4e5a5fb171630eff2f28d228c6f315e5c3e6e6cc1c9829305274b17c3b5689f164edbb66da8ae7b8db504a5695743b5681a0b253e6a0725d2c8a6a1b6d3492592ac25ac25e56c8a1448e50da53f210513a66ddbb61615101165a97b17c9ae3b757f123d9c2c49909c4e8f7345e94f2d62498593247ecbe9249e5aa474df827a70b3e1f2a896ed5d542d2d248adcba548b2fa66a77849305270b4e960e09a57d4ca07e9b69df1dc1ed3dcd73f00341ffcff3bc6ff38e70effb3ebffe1eb935edf33ceff49a0a4611e3296df9440ef5df8b2adf627ce4e944a65f0ba97647a8d3b7c530919bc3c91233963810054c982d01cb12a62cc1a89c51fa134b50026ea077fdbbdfe7dae6dae6dae6dae67fbda471da73475cd3fbbbc904a388d1f34013a97a3310fc62c6af88104a5992320337d02465a6a134328561447714fe063e4ad5439512f525904c4b6a77d4d242a6257223d3cda544b53b2a3f9389e37a98d2e164c1c9829325841272de0794c0052540a184264a1b4a7f42094828414949a3f4278eaa9461cbc7697714c1fbdaa6759a46a6b724fa47c230c29b7dbf79b33255bd99c944829a8c93aa372bbf2fbdf75b89dc584b584bb6d9175aee0e8c4293e8cd3c255e9177c4e7df8b2ad37712bd7bbfbff7433d1165275e259f912d8726a2fe3e6b49e93512887bd48752bd99773f0cb5244888e04acf52d27d89db01122550e829577aaf71af91db115e490c45d6928ba34bb5322c358f2bb7ae2351da879ae79130c2bfffb1662c251787efa028dd9aefa0483cf568192296dcb7dceffe2b99c8eebfcb3d6bb603a350d3b410f53d4bc9277a4c349b93aaeb3a2fe5389235d364baf71ec0437480f700208ee486dc97221c506ecf5a727178decc9b79dd18caf4a58872a399b196b096dc9aee4bcf9a6936be83a212f43ccf632d39dd910b49a558b2780fca1347529e382a7a2209474f2401ca1349b002b6402abb0f3b2b9d151d968e0baa7b7ad47df0f42daaed4715f7db188257849a0aa8f444941ec8814abe105542e164f9d2ad6dad7c7916e447a65aa9a15064ba39efe553eff3bc171248fbd27bff814990dc2bbebf2ee5889727a2fcb49b72e5e9bdfb9e9213f8723a69f7f4e066a3e5bfd7fed34820ef5ffefbf4347e0f55ca8f4c4fff7d2f9feaaa420d15a40492298cd2df3f7dff3d4e12ce1517c7f72039fbbe2fdd5aea5266e3a9078b976f390d51a5b7f4be3fb1f8b48729a74de5dbf72c4e8f3af5f01e0824d37bf2529708a827ef7b16f77192424de59fa675a5f7dfcbe971aeb835df7ba71ee09bfea4c2494ab9b2eb92341b9710064c28bbbfef028a60e9c10b8aa7074b648a93e5e2f85c3ccffb8fe33699f630a5fc9e04f2c2bf5edac3947bfa712271b27447304a2ffef72e1128bb4f4318941df8dfe6cafb240c2346f5f4df873f8adf2292e9e64af03f317589c009fcef71b25c1ce2878f93a4d97819be77ca0967a5a92b4ba0caf33cae2c81244e965bf391600b24d3065383f0fec6714c9a745d77559ee779aefabeeffb521dd8521dd83629db142b589adc9bba25c8a39be2349bffb66d3398a0d436284640a246a9fd8d52e39cbc405bc29f308211caedbad65d29352a78090aa9262f84f0020b8b67e17d485cca1595a64fd2e148ac4f372ba56f47321a1170d8a248b72a4e9ae4f8747382f3e9f6a4874f3728463b7cba49c171846405cb8d4fb5241b9f6e339b4fb7221dde6b7caa41319292c3a7da14199f6a556a3ed5569fde249a4fb5d9cca75a91c6e486cd05f0e99552c3a7774a15027c7a8f2ed2b56234233fbd45aa4f2f93fff43609c0a7d7090d7fa1cce0489ede283d95714947f7be7bd36f493a40165992e998030e6ea33de764cb41e2903d903c90e9ef40e2208f36f0864cc1b100643ac6dc20d3181b64fa36a40e64c7649b02d6205370a441a663cc0c328df91cc8f4656490a94c0dc9045c912938d290e9183343a6318f0399becc0d642a13839c6d36b4269ed16643932143a632646c2053992700997ecc00c834a60652002448922938aac8748c79328df90090e9d3400280f4b06c36b6a2efc96643c60c642a4346e65364fa312fc83426864cc7d205098e2e643ac68c641a437e4f58904f6420591c93cd8692255a1798d79e3b426bf11390e1b5e722e0386448e28afc667c925404a362f1a3112258bc7f8b0f4ba79c162c58b02055a3aaf316557c4fb81957c2a8acc0e2b2952c54ee342d48d7645c545e6a650b5515e57d1954ed1ecb9a747382c3a8be45e5c2824cc10bf97d287f27595c91b6a5dcbbe52d6f6c3638cec90d274e3827475b0ced41701c6362fe65647420388e3131ff4ca66c31b407c1718c89f99791f18cb618dacf9acc9acc9acc9acc9a80638c6624c3c3f2c9902123f31f13338e1e9622101cc798987f1999ef8906464f39272577546a973bfa3424ad63526a1a134f7b8265eba67458ae1137e38c3ca352eb3ca3adf429da49a54a69604cc727272f1d4b2594479afecbb185aacda4aa91822008aac0f2fbc2fd9072d8be184e18454fe4a61041f07ed774c30d051cc7711cc71521952928459c4204b96ddbb66d1b398ee33c4eca6643143991133991e3c42944502c8193f22546c6bd062983c80b268c373419edef3674c44979c9b4f45e0ac65b76338426f0075368a5dc54e98b93fbe9cb94710629399c9439361b9e14cf9322e55eee4a605cb9a212e8bec8bd2765b3f1f2048462546a2f52a4a49e958dc5912f4c18b74f5fa6949af602967e90063bb8fbc9fd03c2d5e1c2c7db7277197cbc4edc7da36a4c7fcfc63de365b97b0b1fef8cbbbff8788386f2b9a2afe22016965b1e74bb3a59588d998362bc46657ecdeaeff0d9f5a72a635ffa63b87bc95d3bdd3dc6c7cbe976b5613e9ebb5fd7bc65db36ff8affcc6b2bdbb9fd954537de6b86653506437fa3b8ddffb3a27b6358cbfa3cd63c779fc1471f7277171f1d09771762559f3607b14fb45113c7b0a9db9599835a42b8ad315b5d63b2aa8a6b885adcba50d5ddbb8d73770d47c9116420831e251752948841831f20d19902073c3e43a8410551cd915149e146151642e00a0c4dac10a246c004a51b640c11e45085c50b09acb698de51034e5a0481128046f8a48044e85266b3d2b78f0a4e84e066c3f41ee38997250e4184f61e39d3fe86e183e157e2bea429e13efcab1a02fcab81e22701554b7058db97544a8a2e6bfb2538ac1fb8179f67fbcb32995e14bf64faa2cb2a2909df04824ac2214a7f41728825784a7f7f08d36ba308e37b9c1268fb141069ef894a4c5ff4acd27b2a25e22f7956e9af48ceb6bf7f49254310b17d899c6d4a963c4bfced848071369bcd58db8baa2188d8de44ceb6d7ee89677b4d2ca98a2eabf4a0aae8b2c037a9942cc16195bee8b2ae0ac60fdc8b8f80edc3f7544ac02092b0c4fb43945e3584c9e4a99c55527d7f593020301af131941f09940222ed5340c43df71b8c1fb8d79e87d3d1de53e96cff5d92ebc920843143828408edb727e23ef7489640a2a4e8591d4fe9bb27027cd33b9225cfea482444786f228b9eb57d892402248b9e7589f0ee77e4f6fe4478770d5362488de12e59c208ce124e49d1fd2531e38521811d4270b3a13debaa8a963082536e459c92228e5cc208ce92258ce094409b9f8cb894720b224c650981bae734324d529249b66b80d08d6c9d0a2a71401125102fb5d7b82b038ee3b8bff702dde77e53a5e07f1f8648d2fb250f4990b6b747d26d1d92d20492e9f71bc8a9803a12a8fb2465aa3d08049246b6927b8edbb68ddb388ee3b6ad454b351484aefda6a98c845e7ae7de2383b4f73c4e6524a604d2482f4da4112465e96f894cb94f41d20892f22381b607f2f79652d39ad8a094f7431074f7387f4e5bc22adabc238ef09a2431a61e94fb29279b0d6fa6c5f0d734d6acdc349577840845025250524552d93d4e12ce15d7ddf3489c2ca0e324e16429258157949bd755a1c978b30f8cda5fef8eb6ce63c2813d4cd96cdc77cd4918468cdd7755745294ce31e1062a6a68325accac0c3b31c7b6dd80c2784bb147b9d15085e3388e4baa526a7b074d46fb985a04109c024e01a7805364ca1b3bf81561382b73686028b51aaa800104bbefbad2a735985431492fae30a63554f15e862633138698a4307c610c6128b5f6965f4c5252a9fd7df942a97d5743952a1b594315b0862aa5f620088220784315f1891300b895a4986653224da6fb7ac2e856d4fbd704d7c94de296be590e2d69e8c68d1b2fb53b721bdf83ef6ec30341174e4a910a2a289b30be48711a2da6c9a9c9685718d398265c4f1305dc920aa5f649394a45a41729dfcc696edcb871e3c68d1b00c0c26d250a3029bfedfeabd438f051641a2bb5231728b5234c7c54316e9fde243499214de626d1030910cda9184516a712adfc75726f8812916832420881124293c15296a22af52baca47c00b92521b618f74bdf95ba2f956a50de2f9534ada4912900dfbf5053c1f8a1bd97df6bda89c756d6a0c95c5667054b795f88a2f2fe6956defb31a941fa31493561bc1f13a7b9afc9682726261254610595842a5aee15abbc3c5832c9e00293a3c52da96450b9802dc616f7b680690103a585f8bdcc664c5e9c5c3125c578a1a0c413a168321b2956d1645abc6f366478df6cb878292823cc6f1f8a50361b97e59ee74c68329b51777d436955349b3405cb162f832a1dcb16efa24a63ca160faad22f5b90a967546e0fa36ab9d213a168369aa6bd68b4dd2946ed7ea24c0b1898d22907fcdbc28369a14a4718923b959c264c753499ed15a004b21bc29863eb896936daf6e79d6d2d3e85295ba882745be8d7fb928b4fc2ba31efe27962de858b8fb91ac9f3e2633e864412f32f5c7c8c8b2a5c1c693652b840d26c60fce655f1327b51a2d914410961a8bc6fd1e261546912af458b169f638351f9d064b61650ca4d7b51f252d482040243fbd4bf78172a181a09e307171ff3455e7cea63de454af54215a3e2dec5d16623e65b5a5a7e8b5179465b8ced5da81ce9a5e865e682d1f427138b17cfc573f1de05a6c5c3b4e8baee28d582f1bef77d2d3ec7d602e63f98f061542209a65fbafc8b2a8d295dc8b43b1a5b5e66e9cb11a2aa4df1adfb7bfaa1951e10cd86b3622e115c12982d260baaeb292674e4104b987e88ee51ef9143787fe2f1fe74f52d4dcf69362e4c6a096352da6da6cd6422b9effb3813c77d2a24275670dcdb786d3138948fa70f37adf426d2b7181bc903f3a624ac30a4ce85cfc53c07c5cb7b471ed2a9288c51897f3f87e61d852f9f9e8abc234f0a0f2924d5a3938e92d29b3e4c3161047594c0bc0b18728825784c0ff343944818db97bec5b770410e6122616c248c1f5a7ce98bb4208bc0bce94d0ff330a4086564c1e2bd231a8c9630ba3c0d46459b473a997648e511d761291243195469925286df4e39327c286a9ee71da55a306e20286eaa500c1f0c5f930969404a81766150ffb148bda3a3544415b5708f525181646b321ce759f192349b23cd46b3517a8f5152f188624c69e84e391e0d489d88d4a5341645f774d35b549e8a58149d8a6808ff87567e1fb228d24e22928824562162998948482252c95d6d2c39a7d14e25a7d154de91278566b33d926705969726c3a99a0cf7dc555232da627025524c69614cef518a8a313d158164e71df9a66ddaa66d3420793420899f764928d318a4f68a504aee43902cc2989e8a4e454db83f3d014f4994dca9a8e47e53899b7669400a953096dec4fd1152e97d1ad364b847a2c9702f42d96278474418531a908e4aee69a0a10a1ab068361af70198690f6e342095dc832aef4e41bc4f359204cbcf35e7501f784f391bc7a522f7aa66c3595c0d485a92ee9b01059705c38811697b2b563cf566be6de0066ee0066a1e4a75528561e8daa987f72edfa9bcef8a5c0655088230ef9b0d1620887482f143cb9fbe480b29c31741892d546992b2c57fa79c161f8657d3b4ef384e0cc5e754690f462d4a2f030c8b170e49a73bda621025159dbc990bc6fbde7f62681a552ddd9fba2351657af035cf4d5b890a610fc6747c2dd584b14c1961fc11867b19ddef6022614a998f4c6f94369ba7e25c0ba9a3310dab74553aa4d0289472c9b4439a727ffb242cef6ecfc3fd462259e23e92ed39d2379ef0b9e74824dc876412d6970ac3b8bdcb1317265c94708112568942b3f1c2ae2b5d93c974946ac1e8dddb8311184a1146a1c984475d87d4b910a125b95cd781d06bf0299be653c454138478f9f00184db524818533195842b8c9d4ad49294a92b8cdc839f84652a912089e44b592155658c99a1ca98be102f1f407c709b6fdc7bf9dc6bff5c0def019af657b82b57b82b57ae7057ae5cb9c25db972e5ca15eeca952b57ae5ce1ae5cb972e5ca952bdc872da92be015f00a7805bc025e01af805740eec3d2e5aede48ffae90b292b292b292b292b292b292b292b292b292b292b292b26285155650410a55484de1ba6b4545b31452672585c49ab1966cded66da0f6dba66ddab66ddba6793208356d4bbaa2a67d9af67d5e769b1776dbb66ddbb6e598699aa66ddabf6006a631385970b274370627cb2954a5dce8f29d4b0b87e2ae0bc9715cc7054d66fb2ec97b9369f332ed210727cbadd1baaebb9f38c59863166ede154fbba29451fa134780a27c0217ba2334710a622aefbda721ac255d8ef6b19688ac196a23d35b9e7c30952792b5e4d4a3f39868329b878426b3bdd724c7166acf9d72584bb67bef6f292946569126a3fd3ddddfc4db59b171a8f70f97f8c81c33134a75527d25b8fd5752dd8f7426e056444b0fad245945dafbb3604c0300450c80dfb856a69618ef73a5d7a0dd2aa494dde9e34aa95418460e8a66d344c8c47f264d14bba3540bc6702c4befbd8693a5a44a7b30f2a0283f55a7a990bc1b4a295d00dc164574924a5d31a6deec08cdc6bfe8081109502c536fc66489d1b950a6de11de6c2b72279a91a3400343e8a618f7042327a35d6d7b2da61cbf0e2c5933d692920abcefa9b607841efadd92941f48aa522d18bbd7726cdb739a8c15655745e99ba60496727b27a5aaf427907045195ef1c31299c36b494a29f17dcb461ab9630855a9168ca6d778941e34a9ba2f7d2a8ff4108b5605cfbbda0c4f8a8e60d2594142065f7002254cc13082359b12fa66a3a491308ce84c1c67e5c9a7796298f284a2279c90f404921439664270e124ace26274aba4619530742b6195b04a67650a2e469d15172317a3208c61151723228cdc7761950e8b8bd1e55217230f89f27e8a0863da6109ab38e16840850a152a54a850a142850a152a54a850a142850a152a54a850a142850a152a54a850a142850a152ab317e75277e6a228ce9ce6def672cf7a99fdf610f860fc00fef63ca095cdcabd1ad743d33670e3c61e5cc971dc87ea3a2e0863fa32e331e3542fb322f1e5ce949895daa78a30a62928a2b1b67b7f8b998120744d0c2d5e83cf5229183d272628b5279b125d4ec7a9b8188c5b0a0ca3a6bdf6da6b0f05091e5c71764f3a4a2000810ec9c4c35d86fb4e099ae907f718dc7726ce643291dd514aca785ffbeda65e11a4372b4d45789177c4f6a95704a895a0894c4ba407925ed19663d35efb254e38e18413dbd6a409c7714e745dd739e19d524e38e184134ea49c70424b39e184134e38e144a94bba3548e5207583940d52384835f179a06f31b66f23491f448912254a942851a2448912254a942851a2448912254a942851a2448912254a942851a2448912258ad7f0a16a1c4f2aa016973fa95e5e24c50f5fc8b4b30203238a24e9df79dd763a9d36d3e965383d0bcde67462f1a798074f4164f83f3de7e24f64da821cc34785a12afc934a0cc37f717a195a1ef5a1aae55d54e991269e0fe29f4818e19f7e8411fe4b48f218c917323dd2a444b97c8ecde5135ffe44a6a2da596979182e248f3b86b2850c4f61cb8bcf9d80c07ccb731f92308cf042558bcb879f180ab92d64f844cf159badbde6bda2ad1c84b39acd951562e6d80eaaf39a7b0cf10099f9a5236b9a96b569c2fdc746c7b70ce63a37f7eb9c4d8cfba94392aab2666bd4410a77d72a8a773e5b39fb987cd4618a3bebb3ea00c5ddb3b9b26b0b23a1c376774d948174d66d5ef38f02bccd402d2bd4f2f16175c89137d641c6b50ea2ebd78a8e35b8e03ed690c289d4580cd780026f7ce29d6135983882aa49b87bae7184afacbb873ed61872d7f258a306d6644db6a571c95e34ae1a3dd610ddfd6c6d56d5230d2fd09062cb622bfa27665dee86473862040425d347194c68fcb0325716d56bde43f959214e04948b842b3cf071460e275af15e739b7d7cd0f3f1e720fc3a735e67885a169661cfee15c5ac9a5953a3af2dfbec5ab1e9ee43b83b1277e78818a03dc081f6c68f373e5f13612115ff9935fad2f8f5f96457dc93652a2e727393cf1fb301ee7e84c8dd7170f71be4c8818bc98058567523dc9dc7c7158f935dd1765fa05146005cb72ba276afb92686613552ce160bad42ec8e33ebb566e544b73447ce4d809300a724c69d9b3927c4a123cd0c942937c845c0998d3c8e7365cddcc6fe645f3bc32e07b4ed26619cd9c1fd64cd1616ae20c51426709d196d778d192b1934ae9326a3edce81bb17f111871ddc756bb3924173aeb8150af2330bb16dcbba4e14ebe4cc6365d195d631d4c43118ea83aa3118ce42bf65ae9fe9e959d93fb3c6ae8fc164aec7bff34f66cf1976e757df134337bb9fd52a7e9d513516c3df6634bfb4ecb3d067734575deac90efe9e9791c3170781c6d4683e4cce36f781c3e679fd6caa25ae679f818ba5b3bab3862c4d02d63aad86cf1f0bfb3dab23cbea6e673cbae3fe8ccd6b2b6a7a7e7579f5ffb67454f996aaee813b97e26460cdd8f0ac9fc1154d310a92d5651d82a6b69f6cab63e2a86b57ac519f6fbc7af5499a96213abb29fbde6958da1fbf1796215bff076b1cfeeefc9d9a7a5e215f5e95163e85665b0d7e86bf4c5f33d3ddfa29a655dcfee2763e87e15cb768b06919fb72c0663cd67f7eb1e15af6c4f9b5135b6515506fb9c7dbe7cdd6634ef681e221f74b3424602686145e4faecfa159b7bc5688b83f48ae2bdb23ace9d3567af7967af79e76cb1500c888d612dcbe3c4ac13b35414f3d03a9ba66cc7dd5f3e120000a552c974e6bd5158fe047c7ef1944a2593bbc37c1c00084aa59269b5b3aa964a25538c1a1abdd71c946bb4c99e342b8a61352cba5a69933d6b6a6e746cef97cc0274021ec66e2dd3512a954c3c2b1d4487102bfa3f2b7ae6d7e8ef6ff36be75710bfcab9f551511c847f0fe515bfceaa4bc69ecf6ab5fc554fcfd9b2666ebfcd41f9f5fbb0dfd3f3180dfa339f4fa4aa58b66218517eed57d1bdca3e86612bfa4478ebd7f9e794b9fe8730eccf7ce66c0ee5dff8b5b589c2f04a746a956df7ca9a2f33f55afe6acd41e8cfe35fd1bd66fdd9f52c8a85f06bf4738b7e96b5fb35fa2acbba5ea3affdeb0faa9f68a32d8bb6319ad70ffa7afce79a755eb3c6e6afe89fafd1c79157f637fe15fdc9aed7ebb7ac4f9665d90fbfd2b0bcd1d781860e3ad8fc8a5ffbdbbc716e7f9e5d5f886d7f5e67984c6bfc39885ff5f47c0fd0901efcd9f55bcb609f5b9d3736d15ff36bff8c8a6546f2e716bfce267aaee8b342fbc7afd8df1aab5bb7a8cfb9e29ff95951186b66d7fbb0dfe6f3695676fdfce2f91590c9aaae9a6f73ecf3ca7e763d91bab2bf62f377def827b73f3ff87fa5cd47ff5cf30ecb03338538316b289feb8f13b3360a4381b439684810e343e1fe33b167f7f3d044b029806df39127727d7e76ff8ec1f2de98c746639bf1376e82e810824627880e21fe6745b5ce26dbea68390070dfaa04a009f7d5cf8ac6626c7b3e91bad7aca2fa57bfa2fff908fb373d84fc1042a40c62cb86c460a610eb0fad7f7869eca3cdabd010a2ecc310bde6a01cadeafcd218c86481681f3f3451f6610f6521accffa43e316ebe08d77e8336f8d81e8fc8375fc0ad794e45be8dc1df59106d49a814cec5ad19789613479e3add915b77b2827b7b28dd71f744755b10ead737b9e656ec96e6e4cb4cd6bfe41f58ab67b28e7cc2b100050dd6ff8088099369f6810dbc6f24be84f9ccf273a716e55fc2d58de9a488f330cb9b71926d32b6bfecf8a9ab233b7bfa27fe6150813fd61816c60061ee30c337ff312f023dc03308e7c28d3e056866134e1ee27aae2a0f5874ab575fe41cf5771d68f89c8ca15f5d1e1ee2deefe2385e3ee44ae3fb3eef9417d565647cf7fd6326c66d77f7eedf7617fb738f61a7d9a9abd6618118eb570ea74f76f599f67d78740124fd0edca85aa3431983954a36bf4ceaa2acb3223b19696a93298cca479110577a7c1c7174f8c2f96bc48e2ded3f39f7f70cb6efdfb75d0f7f43ccd3ae45731f2dfb05f3f28ec867f99e84b48e675fb83c2746eb16ae6f3df02465870e28127b439886d65130d6af3caa2e79a635aa6636c7bc2643ab7a7cabe746e5f9b089bf96c6d136db7ce6d4bc5b2130d42cf156d0d61582b0633875a79e3332673f7216d46db2c33222488aa62736b16d6dab8d5a23223b97577202b0ad46a0d61583e82ea16ac95d1186ae6b6cde79a75cba2442d464dd427afd9a562183a84617bc5423908fd697d30c04661a8dad239fbb8fb8f9d814c74ebd61086b9bb8f136d11b55966c4dd7bf0c8617d56b4d5b6ec8ab65614d6b22986b5f2c6ad9961786dc1dc7d4786e1d5dd75b83babcc81b3a25bf700d4b2423baf6d96edfcd3e2dde655886d3390c9aaee0e63f3d0da68eaee3ba470dc14c0dd3546d5d24463ac89c6589e9d77f2c906d139f3d87927ab2e9c372b445d8100325920f9076b9d83ec9655b16e7310ba02a1651bef1d55c53adbccb1a19cb595b168b82c510177592205eeb24413c6e02e4bd0b8cb123ceeb2c4eaee2fdc5532a4dcfdc7e0eeee8e00773f808f2f45ee2e845b1aab1939ac64d0ac426ceb7a69fc7a39f7bf34f6e12e0047f9f8e82285ce26aaead6cf8ab6355966249f2d3307b9bb8dbbeb50c3dd69f4b8fb0c77cfc1dd653871f71a775fb93b8dbbcfb83b0e2e0a185d7462b8bb0c6883bb13c0bd0617d1dd4917775789eefebec39f284caf4014c92eb435b54bb61383a158d531849e6b8ec5ca9d77daac9e2bf69177b44c8db5b29d7395f964222b8be610c9583f2b7aaaac8fbc31bad1f5c7cf8a5fe6500e9173cda6ce3b86b438ab3a7e5654b3aa8e33ef5836cd526799914c64cdb1216a5e57fcd2219485c460ad8f6d629eac5736a63311a236cb7ec86ae6c9416836339133efc8416dd699c8ce5a9741c8daadd71faa8a75cc088299428c3eb6807fb3753e7fcb585415eeeeee9e8f281ea06c30a292d83ff33730b6f5f999bf69b38f5e71bb5ba8edee42b83d5f1b1542216044f1605f423fb3b3d6b8954fddaedabc63f8c42d2273cd6d2b334f17382171f7bfd9f825f43368abb56253003e9e7cb8c902c16891f507cd6d0e62553e9e60dc757e565475a13ba48fa2137c068df38759eee87655a36f6e5a76cbcc3c945b3073a825beb8d9bfd9d5d4ed8a06660ed5c0cca11a1a39e01932ceb3c665c3746197ab460e26a691033e0b40d3e616cc1c5a5954b7396865cdd6cd0dcc1c7277d0c7d07477183e863efc6fce1505fa99bf59d11fb4653f07a11b0bd1e58ae2970e2d7473b35b361693e95686e1d6476bacea78991bc734baa3cd3af964cd7cba7b0cdc7dfb689a80bb0fc047530b77ff7c342971ddae6a6acc56d7e81c846e1486b6b11a9b1b3568641b3ad4d0c0363564c8c03eae9c69cca851c387266fdc6eed92e91abd652c90995f66cd467f4cd8dd6bf0d1c483afcebccd1cd3b9d5e87f8eb1a6fe15fd96dd660efac7efc3fe6bffac59dde1317350ab0584b3d0cd8debe6469bb9c899cf55c6b280700e42336940dcbde563c908dff8070dc259a6e2a0efc92b2ba4e7f1afecf7c49089e1566895dddcac2cba63437afeccaa8acdeb0285f2da0131c4aa8099191e7ae0e1630c9da8ccf6c9406e6ef6eb0725d20ed93e19c8ffffaa8006fcd089b6f2cf995b40266ef7bfcc8d615be0c10eba1828e1a1079fac760fe06187dfab8c351f875fb3dadaaf1fb46d9d2abb29b101e78a7845d8fa7c1e78f818326b565766ab83c8f81e02e0ce1581858fdee8f964d727da315876f71b7ceca470ff9b7cb2e667d5855f26ceed2afb33b779aff96f54bcb679afe8048cc8e1697cd4ac9f071e7ebf7ed0fc5ae59f331f71f704f8d8bdeb76954f76adc9b9ddae1abdb2e836b3aab2664d01700e3635b00d1b1b9bb300336ce8a0838d1b3a601a260d1a2e97cb358306c63af8cc982123fbd8a831b796b53507e0f9a1a3d42975fc0e3f74a27f736615c53e6dd61abbbb8a612d2b249f6c909f15ddb8cdb12126aa312b6f15fbc83074afb2b61573b56cdbe6645576b241f2cf89b6ac33af42437c58b6d5d127ce3e6e6e72cb9aadcd9e18486563ad98ebec61073317e9e1866c073317994143b64dc1faac3f708b75346e71ac1573e913671f3368c85616dda86663adbc5b3606435b7b28af2f8d7d9c386f9d59adadb3eba5b10fdc629d97c6afd689f34b0890c9aa2c22ac66226d76c1646b0bc86455578b08abb96d6d1ff88ae297d96af389f3ab1573edbc73fed879e7445b3bebad512044a5cee8465bf6b565d8dd61dcfd06ee1e848f9abb6e57a78aca5c325633563368589f15a5c92a36750d8d1937cc5c00d7598002ccb031cd9c030d8c35572ed96ba33f44edd6e84f6e6bd89f169b352a3659b5a6d5b29a33af3fa8d6f837bbfe6b639d1ece1c8ba1edd637308d8502270652d91813c624b6bb17b941dc97f1033cf0356b9d5beace993746db8d6399c80a44ded12c90ad653bce2cc49e6b8e07bae00199bbbb0700f063080759877090150cdd3afff99b15f2abadd1a19c63ecc772067a76cb58c958cd087af6cc302ce457e7fa33837756f65f8ddde4f00c11f22b2275657b7ab4ac87dd4fd4e213d53f2babe29e9ed733dac787cd276e338c3565ac5ff5f4c8d8030cf1df430f3be08d77fc8ccecff4b0c3cd8d8f9a777680a1af9d180ef23cfcff37e0bf657dfeb38ac660acc9ae3d3d3d392f805fbd0ffb2dbbb51a83e1a1fc7aa3abec35d1464f5466045d7fe420542d6339fbf0532407e71d47142852c200247adc1dff174162e6a5f1ce0017dc574ec880d1ca454506869490811b3b8880012872103a628089fbead49a0d1818805f00e9025114d1c40bbcdcfd7e17b8271326849818c0bd880f311c24e79687f51688a208208ea01a26cb3a0855712b963390aa4ae0010ca88013cdb6f9485671569fddaf378a9aa88f8f4cff996f101480420576c8a0c18019442b7e9d612d7e1dddaecc1c56335634686828f014c871576e87509fa7f94cf340267efdcd901d43788836aaa25b46013ff3fdc1fdeab8df21ee57c8ed81011c5481e2f671b07b65df0593b5e6e3d7f9a565b116bb57b475060571f7e137ad53455fe6cdc9fdf4710245ba5db532daee71023c196db709c3b9ddfa33da6ef70bc4876b004fdd1f3dc02924f072f7197fc3c7b0d93a5536864d1fb7876e573482223013c1f3c0c3b73273eb892103c4b634ba8aa11f498cd6cf68190220f91919a20cd42272b55a2bad756e7f563407a68809cbb663118fbb13d9c42201bccc8d613a3a678b4d8dee287778180180252458ad2cbaa29f5b167673d39380cf79e7573d3defc36a6c7ef9abbcb2333bfef36bb3eb8afee4cf42e78a7dd8c7dfb23e1bbfb44c28f6aa8a6efdb9c56dc642af1384888f5fc584fc4a9bafcf1c84f59f2bc6ac7abad82cf4a7c6421ab76a6e61ac7ee9185ef5bb30aae2ad316c2636d36a54a3b1cf3ef877d6e8ebb70dabceecf91b7d99689b61f915a3313fbf76dba2b087e58da267de0be8e949404f86b13b3de88c3e5b5668fdf12b55d3e8bc718cfdb5fc55fe67f7caa26d0ee257196d77cb6ead6dd8686caf16d0a351dd669d9e04ecacd967d79789f3fbb0d94543a4aeacb9b189eeacffe626a6ffe65799355fa3276b2e2067c7101d37372d55cd420958404f3e59f3a6270159e86768d81fd9eb1c84feac2cab5f97bf3ad58cfacce8c73f5e0f21629d79a32ef4c5b63a3f288b6579cacd0a39b32a3bf3cbdcd9ac1056c8b9027124094798f09e9e473970e40167d63737f8e6c608222b687a8c4440b8ab707a744bcdebce3f3dffb3a2aa7a73330e61c073087a76b7b2f3738c68bf5e7dda1c939d3f5aab9eadf3c66a1156a7cd41193d55964585f467a1bdd1b6c52bfa2f139b416ddec9419015aaac731bfbecd25ac62053b7ce4c84d53607b13d9b65db560ffef3b34b668495cefa7f6567702b2b573fabfcab35ebc71a056a59a12752d5d732bde6356bf6657e2b630ff02bdde2580cffc9b6b11b6ed85828b37ea5630fc3d9f9e74477fc2ac3d01d83cd395e809ee29e35deacba83011d5bfdcdab56f6cc2cddf337da869ed151a7901180bb60808fed11eeba55b1c9baab158840cbe5b0ca8146c57bcdb0956dd1a035974016826c8409ff1b22438810c979954a351a618daca3111e2340fcd59aa9f99b33b735baa5e2cdee9f698d46649e071e788869c1377ae6d890d80eb11c62628c96ccffcd1eca337ff3af5fd1af408b1582bd59bfb06ac17dd583b22a116e65b895e11587712db9b9cadafd335ba32adbe623ac4683da7cae2886e5364b7a92b5d853a65b673e829a78cd195033e09eaea8b64183460ea826d243b9855b196edb1ca0213944328cddc92b2b84e772c00451a0b89fd9080ed585b70c06435f63a1df28ea839e2cec739b855ec568bbdf077d198b06e536f63eecc7d8fd1bff99373a94856e055ce7adba50add9d813a9ab86b5f87310ebc29f5ffb473e5921ac1d196d79fe5cf1cd8d0b266b75dc22c63409ddae5a76e3366f34fdd959b7328c3ae1ee51f030a07f7bb83b912a2386b786a15bb72b53a3b01a53a3b0211f7fa2d059a6355af332f326da688b6335bf71fb59e3acc61ec799dbfcf383b63b07a17acd3ef97cfc6334fe18b9ff4d57e567a6cd1bf5a9726bc2198c3e3b2d6bde15cdccc5e1769703b08b84665f16585914c97842e125dbe36756d4e75799cfb342aed089f30b3ffbf3b20bbbddc65b88f347b30ac778e31deef706f71bc3fdcab85f1bdc2f01dcef00dc6f0dee5700ee9774bf2af7fbee3700ee9706f70b00f73b83fb85e17e53eef785fb8d71bf2edc6f0bf72b83fb8571bf2cdcef8bfb7571bfa3fb6d71bf28f77b72bfa2fb0d6fc9fd82eef773bf9ebb737e3b0feac11b9fada0d6cebac7dd870b0a5fe556f6b3a2ac0e1d2e27fed2788713e71ba3ab0677af71c0ed460c8315c53ff835508b612f73cdae5f65edcb58a2c7c1f33dfc10868d18070ce3ee3b13fd76f71bf4f4f4e8e899e989c2d8c384bbea7c225595f9f448c0ff89dafd2cdba335b6acf8dfe836ef0cc32d8efdcf8a0ab1aacafa9cbfe6ecd221c2d89ab96b15a37bcd322359ff9ab3d0ceaeefe164d136ebc06430744583ecf05be3acfe6ecd151d420b66648014e6ca9e2ea21d83e5955d5f0761808cfbe3f06c9b8facecaadbd5cebad5e6cd06d9e4b09241f34343b4512093058237ded166d4e78776b5f0c63bc605c8c0172084bbaf8832100edf73fec831221ea2ec43cfe717510622ca40b8a7b5b23d2dceaaf97ae79f97d9e2ac9fa8c52bead2e80bb1faf5ef58063273d0f93eec8f5f691bf28ac2dabc5dafaaa850d67add43992803e93f33facaaeff59d1df6b3ed1375118bbb266fe9f152552797ef5aba04c947d38d5986c8726ca3eb042842000bb6fb6bd69b5840cf9b14324a7482ba747496408cf4eab7573bd50640777185e1fbfc6af37f113b53928c7061f0f204547cd4119488ce5d1f907c76238bf6e786007f0e1ee30b6e571a238b60221c4aa6a59c401604621a8e0ee3aeb769585f05e6b1c434fddae749bd1b6c5ae207482600531630ce285d99af99b9ff91b8dfecc46cfdceac84098e7a5b10fa28daa78b76c90221b20ca40adfcd3ca62dec2cae19b15f694ed1021124490ed83be7676c04c2174c04c21748672702b2b712b2b7774943a9af5597ff0ec5071772140e1ee41707720b8c62dd61977b0f81d77a470bf23c0c10a23224fdc200aee7e5d94c65d0320602082bb6b2e9407360b50c8c01577bf4f48a0021d16369862c3dd2f0e06123084490440d084bbdf0954210031da6e0716b8bb062407270a13b061025e70f74d060b54b902079a34510477f73ca081a41bc54d961cee7eabe8c205905cc10128d87077ee033d5f075e4148a970f71b856b460f1440012353b87bcb257bb53451f661678bf10377bf3ef8180c6f73ab814814ee3ec58765dbd8e7d7deacaaff716e9f10c2dd7de01e43c6dd5f14e023111eddaed41a1a413566ab77840cf9c1a3430810066809510641db33b77276e87655f3d2f8b5ca6e6e7ed0f933ef98ac6d591d278a7574ce3cda1508bc775414f388c570cba33310e6c9bacd27d19951a213ef72084c88152dd9dadaaa0cfbe4d36c591fadf7b9661d4407ee3caa8a7500915b998a621ee79a77cecc23ab2ebc02915517fa63b3427610ed182ca7655fe68a3391cdae2bd6a14fcc62859c422c90cd0ad93948e671ae3296c766856820d61fe79af5cd0dcc1c025201204533343f5af848f241031f50b4157b28b0c1a3f18258fdac6c2bb4338cddf99576c9f4cb5c71cbf298a0319969b2ef83feceba75662da3617be9ceee035c132e6c21864c4ba32ba20cd4fa5951d5360620b81c1b1c70f70d801f902d635f1b15dae8b3fa67f2c9ae796b9901dc5d03ee8e4406dc1d034c94187392dc5973cd9f5bd627bb84b21011e434b1a2ea8a7e76158932e69cab96f529af02dc8fb0a30aa118323b805415ebc8e68a55cc627dd61f412c2c1339cf52885016ca42744040478985e49faf94d1068bbb5f8089bb8af5467f5aacbe2ad3b9fd6dc38acd673f8661ad366f15f5590199d8d53e919a3746cf0c83a13d3dafd7fc0a027f86b1e6b31b8679fc6a9bb96d5973013d4338c8cd4d027ed559fdecca27faabad31905ffdc738f3cf106d7495c160ac2973e621d4e767502d9479b02ccf2ac4ae402b1028accd3a9b15a2571c846579caf3c7c6a8c96a5db242cedc66229b15d266a23607b1a6467704819942641fbcd38511a70aa7f1f1e3a5b18f18fad22d58de3f88dabde660209a28fb70ee984103881d2f8d7dec35b758276bfcd239b3eac21add21c40af9e19389643586b63c2ccf49947d3871f6611a096204b6c35cb14ceb1f2ade2c8f5016c2eaece493d58137de1183b53e7446f30fd6d94359c8f9a3cdabd09a8168552d35de78c789b30f1d5bb33e34508b616dde388818acf5f1d2d887caaa3bfad471c660a6107b078813ddc163ebccda1a0339f311f45c59968e7ca26d0cb6e35c65ac8e96d5d1661d7395b1eb5ae693d5b1b268919f153dd7aca3b36aae40ecc81bef9d9f155d715056331115b328917ce2f62706c488430338547701fcc6ff2f9457fd42797d58d61a4760c44173662338842883dcdcd4b0917d7eedf555d5862cf45af6da387486a1fb89320cebbcd9a0cf421f635935bf368e9d77dabcd922e69ab7468772de2b6eb1ece7354af3ecfa1afd0702a4f4611f264b6f6e4cd407dd5a66001c3b57dcf630734cb786f60fcfcd8dbeb9a1a1a9d135bacd3f78c5b0ed83be746bbc8103ff8d9f28033d51066aeda1bcded0c08ded7ff31b7f8c19a20c24b375c0dd5d3eda1843ab6583055975e136afaa2c68d5799fb945753e5b3beb73655d2d2273679d77a6c9a8144e71a6a120644aa1991148000000f3130020381c148a05c3e1984c51d3a898071400098d9e5e7c481a6759960429650c82841802000000200020321824000f5fdb4d80ed1d498335ec2374a082a494675331829f5cf9bf2adcdc521d9c7f0d6deceaf4f6da23385b634579c1d1552115c5896d3108e7d34c6a204edcd54d9ff3e9b5139c2fe01fd733b7a7f4666f17ea3dcc9f00fc62e05e4c987f8a200b5f6bcb91ea0ce3fe2a3e33d323b7d5b22fe8e36076d0d7a95c0993202e045e5c3d018223d3d49de801bf44cb66e384930b606587676c99ef900992ce2ccf255680262dec8e395bcaa0070588dca909f8d17685bca8dbf1cf21300f72256b73b521e0ea2ade6661f6bc9fc317b25eca3a866c0104c77d721fda1a811efeead0a74797d8117efbe0f08b0f7a2c45e318e41f2ec188a6aa568927504a6ff812f4cc4893aa567f210d8d38d9575ce4084df9cf19ba59ea14f1a49933b1e04c70ee0479a5f5b02f7cf3e05c2a0a6fe27035776a2b54a83f11a49af8dba9349b97a00d3ccd904f02e41fb46a42af4a3d710c8990ca6611003dcba3495da20e88cb763c60bcf2a08380eb9dda712256f3d8f9fb934bbf5cfcbfcb06fab7e34d59a75c65781767f15dd73e6579af65985487ed724a3f55055cbaff39708c18e76a1c0d0620727267c6ec5535961266560ab723d5f949505868aa89a1511c3dddfa0e68ba4d20f60faff1b5c77b87d3b30afd636037f9350cbcc97c72eb75ee6c5c2207a91d9148ee6ec82063c4a3aa3784ea7240ea1770aa8d6606eedaecd77ec4f92dbc26852a680c0facdfc03658f82ffe6f126de0f9198539f03cd91f1c501ec81b059b85f17a4b56af75f4e3f5186e5c95b404f4fd50c41195a779cf9f48d12b85f4b89cedf55ae3fe4d7c940c10831ba783d445b699f98b75c7944f8126ecbb297d22eba7b2fe727d0818a3d77c0878abae1ffb1470699d8713bf3f1cb5de8255b5ddf4ec493aeb0e98e780683fcc84af2cf7fc2b74dec3c239c91cd996326f28796d12dc3f0926f9e1b75f0de14aeeeb697867a0152704e37e47162a9778a6ba5eb45dacefddcd504d6df83e03ef2fac9d5cda1c701314c906f73e53467ab509e645af3cfe40046e09d75309d8a607e86802f201c69e80ee210e9bead3e053b9fe0d873eb8aaf9f4e3f6bc306a86f0faaefaf17e78956869b28e5974d71fb55db98cef36a60f64b59e552ca9d012b75a3cd80875b6db17dcef45fd20beaaaed757c5a50d7f9fdf00c17fc0d187e5819576e7fd8e9065da10c6da06364143768d4a512f01b69ba9ca0417ece56b4b8180163c003f05fe837bf273912e31f1fa9e9cff81ccf764c1baba1193470782fa2f23bf5fe00b98f0d3a36a95633c4befa9ab15e0f133f7a3e15cff2bce22564067d1920ec8be97c51765e38d5dfbdaf4344a09f411dff37cfe5521d8cf137d21d488c49785964b9edcf0d37ed82b82c5b080675a8b0036cf6d2b93f4be8fa3471cfe970a3f453687bf2ef90ab9ba32660fe95f45d62adceeaae1d0a4c6fa1cdf6805c34ccb4eb27c81367dc91a5e0d99c9e2fbbd328a272d979803095703fa4f4002c509e977c4a241e798d276f303f274c0f8b10c0ea999761b0b9a86830107f96e54fd727a3ee65cdc058527473de67b8c66223ccfd18aafff5ed1d29e9f89c1881822184661583a7164ba2158c609c7e95a3853b6993e89bdc95b21f82b80d2d435d5a2397b4d2fc0dcfbf25217106d8953d8e8aa0cbcc0351abad6f78e91e16b35610e01713b3086057a814c90d7067f917b84c7540e846ba918152af9f565f13dd2e80ec5a4207c78992cb9036b5a9dbb41205a363f24ef76e9428f5ae174f46e6acbff2b5083022e728830d0bd1b8c5444f9c8221b231a3ed024b08a46e26a658b48a39c6171891ee84849b435c618d3af3e73cc7e3471a030916224e81a73098f477127d7f6569cf86e0f046520f4f338796ff955dbdc57749a076a8ade6480911030e8575f337c40d711f40507502456aa9d95e2f008650ad4b591dcd5e82f89bd80e0b1473ba3efc4ca7580cc26070bdd810951e6e872e8f8182911d51d3960691fa0b0ff797c637305ae0020a50cd78e6a7a68399b2da11d42fa0d2d7971f5ca0da5c94c210eb4c6bf2de95a178a5e7ff5c563fbeb6c3614e921775dcb2183c8fde8ecdd4d2dc5017571afdd5446d3f7fb853d2f42f18560fb6d5dd7b19ef4f78036f8ef5ddb9ceb0f41ac9ac27fda7e83850f1ef135e78923daf37add2acaa4900bb36e725e614e648dde9a29ba27587d2bfdeb222482bbab3358f7608c0bf9c9f82a9a2ff05113a005b9c43103494be74daea3f76ebc2fa2bfa84e99e39e8543cbb5541d3e637ddc62864441cafb1365e27968108f8f200c4415c99ca1582c59ca4cf2b793366736f63506f1723636e7a3000f20d2e489b9265fbdd6c5f4323b0e985bee96213ffec04dfb552cf8fff9bb5df9a7fd3b46805dc67f096f62818d8b963f1a9ce5ae81851b9ad70faab9d3c4e457ac35df32e32932f21f4043c9639daa35e390e9d0a80ff4d1b0e1cfb09df991c145ce06f79e4d0deac1ea9ca71c35ea994d9bd06317d659818c81f3f5b49d14e260a4c345aabae914ef9c4c6310311830da02382bf2431ad089cc2a8cec813b81278fde3067ce1edf1fe9e9e6f62deae55450b58fa761374ec204733021a3b12d3f149b9528b6db579beb26829c3c165846b3f59621fa25679b6e08c68cbf15a66013b84c14b99cc852193f89fd30d9405c7eb8515f6566c9bd806dd75275979360694abdb1a772f4fa3ccb4eebab690617dcf168d568bd99e9afb4105a6d2eb9592577c674aed04ef5db500d4c0715048901e98f9a8b291f73c5dbd3a52363cb07e3fb498aca0c45721a2b6d4144fc12e4352392c5cc538d3599c9edd1b4a2d0a2909423138e18ff734999bbd72a4d562cd7d30d37a5317992f65cdb3f5e45b41a3f2ce1cebd0bd6e0b154aab8822efdd6e769a841dad7c7610382e33af931a17934fe5b5bad365e228b6e4b79fc7889a8683a14277776d7a9adaffe5977de29a15e058bc08a5b853a8ed30951a3e34ae74ce8be7204382c4b569acd80e66a57ddae7a1df995ebbc08e9e81193255db7a31782a61e2ad33102960843a5232186a9ead77ddf59e48bf19596b7d1bbafb5e35287f0a23dcca253e21c56381c902df279b794d93a2a1606336f875464714b712588956ff21ed46ca05506c234ca941f78720ffdc2fdf02f3e0ce6f7340d531db20437b968e2802137a376325c67c6405bc643227d50f5679747b45465a77f675932bd9e6ac99c1470df0617d013533e97ade559ebd1892baa5f9f89af02453e485c990e6870d9978683f809e53e252a48ebb97c28420926f9df03472f02bcc6919e06f554ca2d139923337043564dca7aa53e6bd3807b82703e190ca8ab4def80f0eaef429401672cf2bc152a468b6e37aea53e241c698d6476b5bbe000b62da1fa72cb10704bd4733cd4a4c961f856df5e420d183b9ddd5ae527e32b71f97b60c6933731f6ba74776151cf80d70d7f08ed4cb6a962365fdc6fe34bf96ccc9bacbdf5e238f61dbe0fdb0cd6da3eb985c0f13a823e8a0fde770d10e0cfccfb466ce4966bd45145d44127146fc60e1a48dee00508f140e88719247c5c83cecb9d9d80a30cea65746eb9a9d424d1a3fdd2827860d7701ebf561830a4bf69d706df7999d202f796bca3b87bf7cd6be7bf1d0fb97cc05dd0f447b62cec30eb9bb162e97ecdcdd7c51c1ec6e77e493dbe22859b26b5864b6c9b56b131582acf9623833ecb3aefb01419d5e25d7291238ba3ca7cd1f57071501ebcafc7cec8fca65002ef683c7d8b78b80d7c7d58fac92ccd42870fa2a7c3051700534f913842bce840dad88dd6805c7ce4fb15ad0e495febd31f5d3998ae1620358fb1c2a31bf801074f89a14ac5a0c165d9067e15c87873670b1dd6d4e13dfd645508751b2710a1fb0cc141c8641e7a6fed8f47471a4d3143c930d881c80828f88b31bf9b93ac63fa8e7eb7da2c1bfe181b8a3ab0b86986cd2a1e005a4288313c9316fa4ce4c3664cd97eff698c2aa096ffb0211bb5585902ea7af50d2d77effb1f83f5068e0c4302aaa2d91d721b5e92b29d6777fdff2dbbb1cc193e83bc7a331bab3e51acffd3e7c74c66a6ceb8a300b6eeec69c27dcfb0177e23d0086a08161057b0f13cc4cc40298724fc619eee32c4d5b8e740cb17228bf32d41bf78d0a8532ddcbf577f428ac518a7af7797c56a5d1463311a95480f0e0454599c22704215b7a8702b60e256d9815e30e3411f64699ae0df12c98abd454f22ff1743d61c029463a6eae4d5ac1721bea2e43470643b3bd3e87501a8e1ab7205d97ebaa377ed7b986eda1fd4335427d0e3fe7742ae1c173a135b8d5855f2c75c55b8f6c55b0e153173b868af31e078f638ee4724bd3382b49e5817e63e6e7f720d814447d0690c908ccad05cc1565fad8d43d23ca33141bc847415539d1a9b6ac6464a3055e37f3c94ff581c590ecebf6e9e7c6527a54c4f4d2d4dcffd2988dc47c1ba1d8e3b8c1933a1499ae0204fb88c73f90b57baa53f84d570955e17080b6e0243e4fc3420a84cec9b2578bf505d9f57652fddfdd0638d2ea7fdaabb364fe7aa81b94bbb11eaa932652ae216c98b45811b5c32d3d3314c051ca38c9d8ae833c301e5b785f96739e5fd4f58c494535c0ccdde43e2f5bb744ea4bf2d75fa678022913d88a7e702f673708954ece3a30449689cffc787a9743a4c6fe960ea2426e5fed2d6f5b59246e545b9b7c02835119fead4764837f8a0b1b2f3ce37da7983807e1e229ca3925a66e65b6581bcd3fd9d68431c1add3326029506a15dabf1e7cc1d6094a0a7d342543cfe80ed60000dbd06a67a0bfcd419b82834dbe9cc1da501cdb5451e2dbfad9b62bb52bba9c3d18488ae387cd3af870c4f98dd0d408699db6e98f5de88e33640cc771d678d7b49f9006ede6c6e4cbb38fec79b3827ea11c4e8bce64a32ba80a34baf1d9936acefa10aed5b757a4859bb3fd21462a06dfb709dc1e61e29ef4d20ff738c366b791b52994866142608a6dd898ba6052df408010520fc025d60fbebe6e7e53b3cf6a5edf756a9d5dfd9736f1c55f9998d5f4a8ae2a29cc1bf493fcce8ac607bed53d8b515ddd255bf01105e71102d282f76430d9da8377ae605316dc61e8b5cdb38800a7f86370975603025176e4c37b1d46c5d39a50f092a27eadafb1d924882950d05a08f4d0aad96f9988e0f7000304f2bfc66498ae84c5236e000e0756b948b97a81c4a8d36e9ef2c02993a156e278be9ec5bf6c0456380963ef488af447f2e11b913859b1bb55b5420f08a20b39db753cfc04efd9d447a4acdb04a2453633f3f834cb7512226f87d30c1bdc0036285e06b7f80a1064c3f490fdf7652e9974fc7daeb9480ea50f80f0fa0997b16ca6066cc5ffa2f69f235d8cc7584552869bf03840bae7f3ad6bdab2439449ac5f51e37ddabbab3b68300ecd2e603c6d535acbb1c7c38c0c8e32f29b476801852fbe27b33bfb8bd8574dcce0792277ff16557e5eedbf636a6cd4e279377edbfcffd53a8d0aa6860df816579772e0e5ff9865f0f2b0148463960514c57790b91e9d2ab8872b2570fcd26ee7f19ec18a1f38077c7526ab826f819b251be005f4bad7cff9e17b0e90d60468e97b7fcec6a76bffb614916970abbbf6296558ff73e2ba42488f6d2382f02881c7cf1d9961621a24f4a16cb3a464e61a2759dcefcdf58d08634215314a3528141db7272486975b81b1f9c0cf80d4140cbd00ca67a46953c4bf89e9801897cbad4d3f37548498e74f2f77a3cced37be6fc449b44fdbf0d70c0cfecf357c43e6383b951891f7e1591acaf00ad6bc4eebde549007c1661880d4fd59dbbfff499caf2016f2e40b39e5f3f091ae833a29b53e189bdc7a1788f70dfae4edb93558a92a6e2ac5fad5c13d89d7ff282d63cebfde45a37cf369c548026a5516971d3889c76bbc6f660fc7737b63962e51fb280094ce3de514b681f94e5393d6e5268a8d8d17621cb187548a31c29f0c60df9a8564be095d60c4e19253c8eddbbd9b75192f73edccabb0bb4b60d53ca464102b34e6baf556377ca8e50c00d6f9a85c49e0e6054b59b2a1f50c6da6432223af5f08bf5e7fa76947f5234dfd6ff508a9d9588fb5f313064bba13b7ebaeacf3136a5c3f6962932606ab9d1213f6b72a999fcc8df8d6679ea7d4baef57fb41571559a953f8dc0c7649a311fc23286bbf7ed04f340ff5b9210b1660f841d317c951a35f2feaae5a1b3ee5ac778a92b7e52dba05e13f628fec2db24015d69f4ef9122ea0414ee3ceadab094bfc328611bc148863b711429b69e96bc6e8268b03e1e9ccbdc6db60e7174b6fbfd6461f6043fadbc852cdeed336fd0874428149ad7fad96aea8320edd30b58ba3f017cc83bbf20ac184b8bf4d7ec0d9c21709ad7241c5ea4f7c38681b75d381264fdff3a9680fa4ff43143989fa4712a9efde521d1466de75fe3c93a9dcbf4bf66ee44c696dc356441fe56a781bca7d177c5fb167a95bed6484c6ed1e140fb67ce3f3e0c4e463b5f81f0ccc3ac40ab241fbd2cb3dd75b2e1f0f3d0f76ae1673b2300d403def4a0f78735047bd456532380305725fa950af82dbe6d6cef7c8e9c35ba9b6e05f4f43dc2dda67c449a619a4449260929d1d03efa8a93e17606576da2cfa9f5a6ebb7883f33760f30918549ae0b7167d5acb2d71f78ae7c0d57e62e11e8986c6c149c21aebc609e2d6db0ec25584d1811cf3522bdd85b97dba88f062e71d6f45d807c2182dca898fa1369ede5d1bbf7ff80aa73a8ffa475c29d2992fa2b93dea7511dd40310bd8481ed38c8b9980dfb69a5c6182bf6aad0bdd308b2fe3abfa2649eef3a8d5366a254383234c0f46ff6e3c745a049da803b6e96a070e595118206489429debf85164c8667583c354ad09684c664dee26615d1b47cbcad1e69d48882eb69d411a97f0fe0b67853c97b65ad631dd669e8b5bce0b92e6f73601a76269be5ed8bd12a9fe67282af6ccb4ecdf5d3f2d45cf45d5eac7b7845464aa2a96b3bfda99f564b20427b3d95ba77ceb6ca62f4ab2129b9f3e536d11704ba270291fd152b5195bf5f9ab94855f1d03162e13cdce9cc54d2358aa797d039a6156716bb23d82b3959beeeb85bcffd1702804c7ab4ffda90e44f8f7c0c1ab4658ae21f31dd48ab2e13d8fdc37d4416e5e985b1d57ef20ded2aeaab1e152f9d1f2e86810bd7260dcde40516c012c66bbeb2b541be7cf4e46f415f587a75a1d14186f6d05761f0eca5791452de590e4b736f4be58e2305d5d64f3f9d44c7b5cafee62c1e6d78bc7fc4d4b70be38633094409332a0c543e10a408086477467be7c9eb185cc1aecac1d249cb1df4cc0a62cde505ca221e0eb5941e3124a6239766b0b0ef0d58d59b56b659b9cc359c83bab785d8ef9af4e556e42744a78ee9cd0c462fd7c058c24c15c6b78c46c708814350626589739e0ee217c8804ecb8bbfc268f54bc38d446dacd266445b0e7b8cc495437e98dcb1ae656c6ebffb1e5c60d4fa690867e089333c20b7e4428e8d62d0f5ba24442c2518f00376775dfb07fc2751d45ce14c3156857519b8d384751b5a415c79826446e4c3c9400da685b19a11dbd3d07ad7560843ae5ff694de590c9e338a28d5008c09d0af99f391bca6149fc8c9b7f3c935e0fc45a33709481c5760d66c1f2c5da587f7236b151b3171b513ed782f807efe4d86c074cc7faed4b93a6af3570da017ba5d84abf57f85f49ba5668ee4dddd338dcbe35363b581185afef719f606d2bfc638b0fd5d1e3eb449773f314f717ac3897848aba9ff932fa99c18ce02ae95870629e4ba47c0fcdb99f086381318bd43273ae775daa92a66b4df4529c3e39e12dc7f4a9a01aaac9a8e5b998c807993d208d6446c54e709562a8f8cfdfe6ac716c682f4bb420f01399ff6fe65b281707ac775f1f85acc741f1566fe3041708ba027dfe1cb64f293a91b0cbeb72dd7ffd7eab8125dcd70553b5f29b1d4ecfa8c3d61cdcf17848c02115c4cfa960ffb757848124165f4c8ff84afd4cad3190f7d820fc20cf8db71208cc7a6c756c4a166eb86b543e526d8a3ab2703d1637b42cde1739c02b300262298cf4125d33093496bfbdd73f8331d69bfaa246ce33d22c8ebe56ffbea0d6a9198cb8b9062866f8db9df00c18e4354ef82a16dc40fc1b919f1440e26dc890a9a0935b4c7fc87cfb66f8646dc4d915f84b6cb24e53973b6cfd91ea15ff40f806bfa57443114a46329abfedcebdcf99bb750198cabe313ed3785c2f44de81df26cbe675767f8a65161d09ca7f34e53e39c0458a08f478cc05a1e52ce2679204607027a350de2463dfb8436089a93d48f7d3e150e343c3fb2878289977337152cb1b593dfe06839d423258cd99c9ba6f59d1e63b84e633aceaa579f5a264fd4ec1a08c4e7449cb2a669fd9ac9f33ebf979264f7d887f4a76be24ae31fabafc587d94cbc80b50ebe71f484b16e3390318cf5ef82d2ac55f55e9a46f5af149593c84a0921b545ea0e9c0ea56ed1f46847818975779fb79c4dc45836c0074775bf6bc41d12572fd3c2fe089af00f0730f17474799851fce547d9caa84a0465f02bf1c04e9ca4cb9ab15bfaa5495f3f2843e7cf862ef7249af66bfe2f75cbeeab9d9d2ece0dadf63aa5aaa29bf61dd01a790c70852cbc1d8ad59c5cb8fd9f8630b20f94f686042438f56da927659e8cffae12a981c153c18ae53f664bc80a5f3abb64aff878240bc22861744f8877887168eb156558923959c672dba43c01e8f26757a89c0f46301ec99515906add0a22a356c1d733a731be8a49c1911997bbcd9c471d56997e694bf6958512b0066b4e5e9f96026de44edaad85cec3ffbceb17f255471ee314606786856a02ee923898925d413efdfae652250495c706b2e76e328927d0fb035837cc48aea3220fee5c23210abe0b7d436fb6320604e9a2fa6bbd3ea458481f76e8d43dbc4e7fba8b284ddb7865788f12af90d3c64a86d97c50e338abce0291b9c42e11fc4caef43f0068731cb07f8a94012ec00b1218070ce829c5a8926f588c5cb5fbc044948115e81a93c489bec04db3153e8bef928f89dcb727185679073ee205a880ce9481074109dc08cf7bffe7930f3d5f205a04adf06b9b5cb8e17d42db23861d698132c12760dceb1d4d17b1919a9c1db1df65f9c2d4d57ca08d44d7d78c68a0baa95e3b6daeca5a200b9111fa97b186efcf306e35a34359808c6e56da46a8126de8d3c00d80e4d9bd4613e938441ca079f014122dde3ce75dcf11c47b0ff3392013e0eb84fe3af231e71af85868a972f35c7f1d814780cf5b92601239cbca0d4fb7cb5132474c12008f38edb948789f408bd80c91c29903848d3293b4bbae4d555a51b35f913a65614cf4e1f8f08fb1e8c06d9c8757e12a6d072ea961652e0516c4859282fb53b58dee4f11f9523774848f261288ce707a447d786504efebf899e6e3251da0ccc456e7f6546fc23b5e90e5097aa78bde5cb12ae06a3724340ca3598d3022d64980f7ba5a1cfc59ed1e7e8b72bfbf9f0657c687c4693fbfd0d8e7ee3c50fc4b8136cf76351b357876ee719cdcabcfd3cc307d21441c0b104779817202d43601177497831e29e571a3a6091aa93f55772776a31d83cce3dfc39eded9787a66add058d171e6dd37ad34d46edad2a2103de057a908f1ab0e61ad4afa3681a3c4fce47d541f944d0ecb3b9382a53fe7ddfab6cf281124dc12377ff94ba36b588ba1adfd6a2c8154967d9cfb81025bef6b4f831609204ddf94eaa1d005322ac2f59ee3028752cb601b538adeb0618fa57d7fe2f75e60255596f4d2d97fcb0feddd48e5b2fbff7fbbe5ee005f9122faabc051cdd790d785501e34edf57fded32422d4ad6bbb1f0c4f9940aa22e5729786cc0f1faabc09126f6f390c7eb5bc9fc815cff3f121a3bf249c38fe635de69b8a80ce509fdbec126d8c1b4f0e9064e66b801e16526659f776597affc03df4f1e9e29ee7ab8ea7ba2b74daa96e253d9dccb8779a216369eb83215cdf365bb2f964c54166db7fc2b966c2a7c0549a3a9ec895adadc11245fbc881681e40dba5a4e606c541efbf0e26d9f8f3fef661e4873bd77385f1d9ac141681eab582c72c70c53b1c831cc5771ba535c3fbc18fdafd18e15f2cd45bad2e1ccad897c107570f6c37ded853df9961cf07da319b9e93ed3eacb9842e58785ea1d884c19d5ec7a5f1e835e4389576b4d30a3561f9f058e01c7b19bcbd1ac4b73fe9716bb3866161a47694b1682144454ab92ad74c70287d60fb24f700dd80cd4eb999c1a5189a0a8c9a1155f2bfd80acdd7c78519c2d713ea0d6107e7b2438038a47ebcbfc6ec9427303bd3ec8ce99b30245895d2e95aea6ee26817becea9892f5d7f5bc501abb35c0013d997563d83aa450c9c626af21d54594b067175061c73be5732393eae3a93eb63411d11b286350747e356852a6390a58e66b3d7e428bc83d0aa2e3354d5d7896549b489a0cacf6731bfb9ee1ff593636019ffd4c5e16143bb7a67aa40f2b855a65db0bf5428607f4176aa947aff299518207a6d48c6c78974cc532a215f8dc011b85cfaa4f730fa478741ff785c2f003c7bb38915fda1334cf77a7e50ba2111644ed4605969eff4d62cb1df77a5f7803ce37689cc9f83cd2131bcf5ef6db7be3311292bb17fe4e1fea47a73fc37c57db88fd2db0dbd7f96b415f9b94154f1ffdc67c79ae714ca7ef5083c431b52ddb5eec87f0d1310071f6666c993c8a00ff0f8c79386e961a4b8bf71d7fbf201800b181d9eddd7174b24daec7eaee78f27b9b1ef797d52279df8d61c6010287dfa88e6b83112addf80e0bfaf5042a55c28cf7f3f62d943af402b295165431aa4e8acf37f039367d1d04e22cc59a77da1f7aef61e4d4302f47fdade652c02c849ae5a79c9912b5ef5d6ba7ed277fee0b2e7468003644a4f0f3a1f8ee2bd4950dcea0aad5c8badb398f287d31c183ffcc6123c4f4facfb5f5aff47695e8cb7aded69e4abed75f30a5a5e4bf5a62bd7f73a82ae70947dd7fa0f4666243fb9ffb5f0492ff8b1f7d6e9c968d55458c8e05c03ca5ba493a4ef33e6e2f86832d58bb33ea23ba0eb0bc314e4963391b33d91406253dc5002e832fd15c22d8117c15cd992ab075b5c008f3a4dc69d0c5328772d1e9297f04c1162077ecfe80aff7631758276c3f5ad7e8fd74a314f13644f20d109b0e660eab10db993d776c747ceb6dc439a5a472af2dd9ed9ffd903d22b4acc067a9c826fe9bd253233403b97a5b76ca1dd2f499c32403e80dc48564bbc21f8fb4b5c85d756ed417a1648784c2a8068288c9c8ac82d828b5913885aa5e4465a06e79c18b0468b665f2dec35c901c455dc388bc220b0188936a6c5f900cc29e950af6e3d55e700189727798f8a5ce28720c4002648c346405deebcdb1b4f5e6625ac8144f6de83524a93b7b754bb5da8b52f1249920c17a0b7b02032f789dedb42c39442ae69ed3099aaaff4f658d5908ec31bc80a08715650e856e8ee8596ed81d116ccc76ff3c827a053688c910d4d354fbb258b2c32266ff7d99e7ae288969f19a53ffc3e0afa243c3c566aacaae82f05ca071b7d373eda531a21b8000641e0a506e7def633f8215250c96f449fbef89c5ae48c6df8621844302bd03cd925bb4d21a1e47dcee625189e2ae0ee7b77033433304134c73eeb43d23a5665cfc860b13c480ec6e9f81f055b1dfe0596c2376a885e7055d58d48a658ede82db333d7d8fa950b9fbcaff7ddb7c8248937fd68a6caab06e45dac4d7f2336a853f5ba5a7eaeded880c92f7aa30fd6a38faf63fecf0d22fc2aae954cb4b2146a6a68ed1c4d3b8939474be3cd0360336226363867b98294783e544570de18705aad3dc03f04f860010616059bbe5cc83cec0d4136bcdadea0b5190b8ffe5ba1a3407a8e2e73fc566e8688c1fb441925598ec5bdc224f9b01afc25379e5a69229001059c78a0e01f0b2c1441ae8e15621015fd6a38806e43631f5fe10a936a7e0e375f9c1050eb47d6a2ae0c639eec9eb77b5c90b77232c252dd65a7f144a81e8759e4803be7d186520d54f93a8bccce39859ba22cbac0dbd8b2ca2092ea9069a5dbcc479e1ff5d19efc3832690e58927ec470a7301a23cbc2d814213b83d0c913a4ddb1cd2416de5f89fb64d6005fe487ebbe0ea7fb29f49bf0def951dd70232b18fd8bfd829cfdd516d3fe2a9498a3b1d8ce80f54b3f434e5d55d215e9d6a0190cb4ed0e1aa063703a73c84144f892d716986afb6d2255be8316957ec9985bd640bfc35807df3d75ecf0bf29fdf2779a15713955a5487ed966350d763383a6ce22e7ac7f5c31e99f6a8418239aada6b77778334cf497e6e6897c4a54861955eba137f58b25931002ec6c92002c8ef38a5ff3a4230c32294b77fea36e14b4af1d6bc7ce35c8f86fe2eae41d9d5f66c0b130ede186115fe31b9bd3f1f678553a8bee1afe0d59d7a9faebc1cb524047d0d558ef60f7e2a5e73d41c162b0956bc95339de8957191746e20e623db56b1a02b213c320297cb8075c74a81b128b40f85cea43bb98844cbf3ba1d3e59bec936d43eaaafacf89983357ff69d792a34ad531133dc9cfd83e567a8e17eda687fd1ae98aca665ac4ab394063b7a759d7aff550e0522427df25c4e113363a94e03cec79db053db22719c7e6320c224c397d00cf53886749c4b1ded3b8a7795643cc56bd4e0d369d6fdc5f0d7f53c5d1342c2594d259c488eb150d484739e0391eb02239a7b241cff440931fcf6d2373e0d62b8639efd916b561e19e6aebeb53a8fffe046abaf7ba597887be1df42384ab88a1b9c04b8104c96cfc5424d4e8da379752412e3b5d81db51dee0a29a54e3d0cb2408410bd2818e24a0d3c9b8f615478732208de4170d650c75cff8d1ec92a8638936aeaeb9c1860ee415bc6647f0899c202d1049590259aabb38eedb895451860b24888a025124f833c71f5485701d6710c3251cc46461a54d28ff2ee177919049d9117eff43e8333954a0b1fc192e79727768805a839e0b78d695ff182d443a40fa9d15355e0788629e62992117c876975d39a273865607edcaecd34d69f9a685c2e1233a1d50cae233b4bab9080dcb04edf74df7bc4e620ac2b6ee77c6ca7eea9097d21cdcad42083c8642ea23060c21bbbdc24aa1f3f7452f220ba5b3dc8a07b0f1262ef6e12a0b382a705d1d8f4d12d981ba4212d2e36ae9d7754492098d8e9cd0535ed4b9e757a81b1af449904ea37da9a7479ce44b96e690d4656ed9c3f88df3ba620bbbbb87c90da8597ca1e8c55a38513232063250814838b82b406d4d995abed7e1f7282c42d2a2d0b24361dad46039f8f70142808043dcf71502bdc30cd16f566ab9384a8ee55c52155f7f77c3a36ce3e9851280f3e490681734cc37c05341853c8caafe6693e099c212c961a1954a4172b1b213aa3ddb4847ff8b64b7593c34fc9b11454841bf841379d7bb34e78641d0702be6dd81c87aa3942a08b423de3883320528480d935763128bc1e41433ea4e2e8e08d93ccbe551ce9d8c2a3255a2f4e9990f58abe2b854875755575517907045448cbcb18f267b0a1c3439ecf7f3adc80ed46c322005d4fd13ec915fbc3ec4b4cdc401873b63ce95c43d533e3cdcfb01c58d1c5161d9605f182f0a296ef6c0f74fe6e3dc1f82f6d514c2e4c5a8382b5ac8ad5d59e2d66f5bf91c262f6f6d50568e69f1d0be8e9f96677916325e11a447e20189322733e6bd416700258288c2162ac921e64182644ca85ce9c96e447e23123de12bc5efa6c15333304e73f4d4881363352651eb701db2d1d5a43b5db34aae4380c5b9f3b39eb49754ff1713a89832800c1fa6b19dfcd267c3714fe715bf496e4fe4b205be43ecbd2755991152e0b7756b0b2bc0415bc3529c7651f5e86c2a4f14d61248e148c11610b50bcdbdf80c6369ae3913ddbae816ad765632295a292d39149597c4d28a2bb5f0f8ca72c8eb07f92590e90c08c4ccf78eea5186a3ad41347f42899db9cded067108ea34f982850b06d4146071b6a01a8c1aef0323b1aba6d174ba27ac571344ba506d6e1b4fa16c024e5ff47f58dacb02c1462fd44521ffa40eff55d5a13a50a72a3fbf73896edab85f491c95e5207028423c46b0136e2a047b10832d3c0e609a9e7b711941c7ec2786992ab05b8e2cb0f4c87fd08ed57e2d6afde43116d603530bb9db05155e879c2e471ba3fb996800f44a3caa5b3a8315463ea7d650f2408440ad1de69036e12e12c7d9eea93a4fbf94f720bffc584076f77efdcc717c95fd5f1ee7518365e5a00876bbb8c4f044a5ae414e7b0c984ee22a9b8807c3e064979f157a1047ce2e0a4650accee980086aad0596050b33780d084f1bc4544577daaa1f9a5b075004421562ab649b6d0eba6346d805cfebee00a548d6fa223c04aabf4f810af1e805dbe2d7b0a90ad99917b2edfcbb215e1d48198259c8c4d6cfa90e955f9265df416b41d660d769a4ac3d98320f4c372ffdf77349a900a16d063d03cf10bd8bc85634d18ad53955aa8e190981b245c78116c667a2e95a2ef348dc83a3435f5a1751951839e35e786ca92d4e1a8d40580a17ae2834c73b098adc5f7b71e00a31fba3e41fdeec63d96e498ad1f4ec421946420a96e1cc87cf1fc413e9aac0623f9c1d558364f4836b3ae8c5038ed498f3d515b93c9464bae1fe7066d7a8b097d8dbee21c8cbdc100a5fd32f42323ab2155000f01313deef9274f8c0c542b72029f29ca167fb31cd10bbba646047665639c0a1bada37f7067ac619d7632459817cfcc75bacf41196970b1ba2ffb5fdc924a367fadbc897831608ce742f7a1818c4b6ce056b3e95fb900c20b0d3b9a1298e536bd29926f9c43354af292542d203f5fc6bea3fbf4225ed41807b102a6e059eb3a70d4a037ab1a0b58672beafacda03e186ef40ca9fedf8fb7fc67ec077eb8b895fa8d377dbb8e4557fcaba007985494871cddcbe2204ba15174846003c952e0ac0e9a92c9bb875c2ada6d309f1663c86eb47428809c2648e980f4a2b34d0ef20ba52a49cf8d4148791a3c31b40f82f744c3881a81718ee35e8cc700b83e0d0761643021d87d1eb40cbfc390dd497e4db6690b0d904075ae8840a7b585ac408eb30b8a03240697a345559b90905ac96da67dd7709107d3259cfd546e9405306d931cbbc3468dba5040c1b19ac91a1958b5438f0339f0622ee9c709636e77880a0a4cdb3d9dabebaf02db1882d7c9081a0f23ab2017857d5cbbb576a549d27bc084e13792fd2a0a0688090b389495f93f6cafe8f1cdad9132c3e7be6f3c8d5fa90c9eca9b0eb80ebb2ea8c063c592443cad155ee2bff7e1a1c5101429cfb866c7e0ec810c694b82b1aee4c8b1b9a49eb94b747a994fb43b07015e05070f32af5e64b2b92997d2d73779d11a549dcf995c738956f0ea266240e32101b74d722bba93b835b44ebe5ec89eb3b56c0c72a67c19409c3e4f75c0230d3a140c0720b92ea24fcb891ea2e9e74366c27e6d173cbccd55d892b2b39229a784077d985207b794b84f306b369eaf4e33e21c81a906afa0bce296959e07f738932c0dff865af2a34514ae9eec67c7cf7d47f340e876275c60f00246953f763d593dd40a8e926389bb923db16475e660a60402ce993affa0c4dcc5677cc95b184a7620e1f8cd9b8d914567df71f9097b89902819c5cc5195754d2731047acc2cb71e1735ce107c0794006aa322edfcb8cb9543269e1ae6068e2140377cde5ed41544283ced6d8a8ca8ee5b67717d116e9d67ac5238049c5a17bc3760f3a731e96604f4703a7303eb25674664a2085eb7c03ee616046295f077e736b7f8e6f81054ead2850f149b0983759619a1d184c67003519e0babb4b232b489fe90bc3f17591fe329751f5e2ce9c53612d02eac9f788bf0b8b97b5585154efc79ef3b9b53ba4a2bbfcac9e16fe616cdb77e00ea28a048461d8b2a03a79f8d6875eff454ce3f623442c5c0e09303bbe648bc831b6887fcbca6487ebd86e09873b3817b302b6c011948a72a7d9e8836faf50316144060e01cd7d30d7e4d8580d51b9274e32a284b45b33267178488e6df27461f3277ec71920ef96770b743b79e784131e5ee012507d57d9f876cbc12b48e69e50d3b2a8ade9b757ba7810749c87319d666a09c30a113d5098ae54a419af09b4687c29ed5b923dd036f86702f9b41dd290cc7ca8875cdc5957ae2559100d2fe2cc75d61cc50b53f4d75a8ef1a4c8eaa830c7b58478c21b66fef48131493ace203c3cefa52240ac7f7cc904928d6597b7dc57745d4cd57f28d27ab6b15caf0e2700293c2a6f28bfc291f1df4fdfef1c9118c30b1c4d4c4b27e8d5c27833cbb6e32dc6670e99e2895ab90d68608fd4a68929601cdc89ad19a861e8b9aeec7d9dfc6d041d6e01167f9671fa531fa84c938c599cc7330b88528ed3a151cebf4408b6038ea92670a37c97638801f4bfa2b88d3549c95aeded6c7a5bad839bef1e7bec00e63d4803b54e382966b743db3e3162ff24706bda27d4c68d50c5bffbbd21cb54300f4a574ef9e8988b9e287351c8fb6521ab7cc45bb2208b77021e8f4fc60bb327cea44280f9db217d2ce0be614fd13f8c459480e122df0e985d66f344bef116939168f2c5875422092663f58085d9b7d29ee1d6a3c950e339da9d10df9614231559106720854a7a861d092193999b105181d52e0dbcd0a43eb1626a9fd6d000bd0089708c4095d1caf1906c8366aebd48bb0ceb9a61c82f66155e68591c3b310eaffbaac20522efe065e51d398bd2a32839623a2bbc9b68d5149008012c54e044f00190129d2d827d123ddbc047ef943ed70b1c67eef436d7dda00e705f6336fca1e09a286842c8ff837ff196ff1d813bdce0c725a55e0eaf66cd2d0b85a9e6475bb3b124a8c95b0f7a65d32c46833bea4c7a46bf9d497a74022c5943be34ba16e057370b16c96bacd6124df6ac60c404440f5b5c90ea30ff05e767d318c8045098a092ce3387e91801f23462efa5bbebf4ffc6f481d289775b5dafc1540c9030bfa09353425c02ee8d041ab3c005acd9ded1230ae1c76f71345ab57162352b384562498a9c10cfe48820104edb3c23f71f6b0b334ed8e90f22108b87d089cb5528d1a7df71d355cc7b02f83e996302fdca0da4e227762308a2de7f3e1d8fb12d09ee696bc52a1be617cf8950f0b25cfd73726867f76ed1fa85c00551c9a8289508827694d8c29336b6146618d30e8b68782aca7296f62317e81d1b2f39edc4632ee2381e315f2eb708774fbf9db40727696908cbd4fc6ef6bde50c28f5356b2dbff9a9275fb8186ebea4b00f5da227a126cb389b655f280fc34e9c6f0cb14726d0c5679be4f54cad8a603d6d740f8bfd21347c993cc70bc683a65339943b1def9da6a5393db8b9b709f282f35ba0cb60856a78c470d1d3361b8c294fa5802a69575083f303f30847fc2466c0c3bb09eb93cd03878ba15e408dc1a79ec3e9c55e871112116ee7f2193860c30d981597e0a0899b38f9431f7607d2ba161959348cdb7311adc4d32329fd52f1769c446a0c24bb74a47401ee3f9bb81c4a3abfc9801e0b62b852169898cd06a59a6a60c006cb98174dd8dc46fe9e41dbb4e355e88cb23c9050df1abec53f4f3a6e9e44ec42b1a58c5c5e40bf3311b49abc252dfe7c6156d81d00bf33b617b2e6d897df03f7b2a840a76b004db096a50d0994d78898565167c4cc1f1120b5007346049f6f6adb3f597fde3a6322be5c8d09ea21bf45799d94f973bcb4251476cc167950a141585bf9519ad3f00e8a81abdb1c7973e9b7a47cc721583d0cf139f905390ff1c4b0e53a605ac5fc18233ea53cee34e0445e117c69ffd0b2b80be2792807503b31f92201cd5d5c9aaca8117155412e81b9d1545d71d010fa2313862a7e444cfd06baa6a39a9060466b9c95ee65b291e7526379990e92b168c8230e2d3fc491b6d875faca3db0745e9e0d7b47a5a7f08d19e49c88d28476a738da81ebc65ebac6120ee8963d4ad7ec55d942a98cb8106d8af77f4aa6b0bb27fbf872cc9b9e1ef5d0d03eb879f1aa9586f2e1cd0bae5e71681f6f7c72c48343fff8c6270ff08fe0915101e7b1d085c26ea7c4121b7a0f05172464f2582e6df7910c8667a2d1547c41d379ee69391946d57be6c0c44c5f686ff98357d64176b1fe7271e9b2b61521b46b44980ea29146bb61ee3852d9bccdec1b914d1d0cfa571b9f96e3c85ae95aa9da53b9ade82471cf74ed4b2f8c9c20e551e91fb79c1b42f5fadc1099a50ae6f3ff6c1c7b21181bff61ef8124352e181cbbbb3348f829f63a60af052b5304e805ac57a82b27b117f4aeaf90f2ca52f520fdf5dfd02205b1bdbad6b3512fa379d75062c3ca9bf5f2e0a285b0de8872a0742386ce28b254f9b14fad4fd7b973ec5184c6184d49c4ef9ff2c0263a1ac81b785c066596e5fb4be17239cfb9d8c473857cb67f01cfe2e6e90cc1232d71b4ed0fb7d752beb3b96fe8fb3fbff92149c96bc0d03d3347487595dcc9ccc3e3bf33d9f78e2ed482f12ed0d71303fa6c5b5c3fde6967b6611bee81587a556968a716cd9f6d7bd65dfd4847e15e45a2c30346d6dfbb7612af180e29acb69a0ad0af32c19c3e73d8e666eddec9b3346d2c8ee295ef4dc6d8b84924ffcc6a90c16c5b35105e36e63167caac703aa19d85f0f1023dc9ace016b7e29d7aa3b765a82876a1d54f7c64946707b1434be4b1213639126325ae37e7878b6154d626684a89bd6c04caf4b743f1a1f032562126f896c4cd5113ed50c2f8205746c93ca1831fb29fb65daba77d6e60c578999e67773e2f36bb7520becd9682de8c77fd0c50e3709ccedb91ac5437aa4a4c66647a1060a637278198493205aea87b5cd25320158cb327f59be519d16dcdb87b90000851b023cef47a32c04d8cfb84445605755d4370b2dbf0ef6becce8e8fcb32b76e7116c9579c4209706d28453424b265fd784048a053b57f66af1288b7dae7b02f7effa53b469b3360202e1953ac9ba24178c007c86920366c854f3fdfa1e2dfb4c03a23a8debc51bf91a17421a2b5b03bb935649ac5c87ddc5a58219745a408df8ac2c506a089856e0de3401f3f88bd9aaca8bf1e832e88717cb178f6ea33a0a32a5605bf52be30b345f5f240b4f7e57d20913c73861302087f50ff5ed660266446d5e41b44174ad19370bd0b0ec0ed1c2678afe045143869b901625a00c5a18f08b937ca6884dc3fc21e9f3112472c459b8d221b09f3c16644410460de291a00d59f8703e8eadf53cf9edc30634144829eb1a1c89c052e210d640a10099313dbff57c6ac71d38e5e78414b4d6d1bdc8352479193e58d0c31e878ab6cda6f780924811fcc623b34368127d3f29045a092ad26030be953457f5f9757a347f945295321c385f88211ab68bbe6c0943b578135e4cbee458b94f7ea4588455f1cdc3773fa25697ec7ce0f4fb14b2bba4b2df3727a181b560d2d7a03851767144a1dc160ca7fd7707d0bd05e83898a662fd24420ca334f9d41bff9f0b4148cf32ba2c34fb877e7f5c705da0a177816b08d53bef3c2dd10a2c422b6c2ae33c50456b6317548cd380cd650bb76400ade26fed398f34d3c980a295cb1f94483eb41cbacc5d17d65a6e30455253833b6ad6107cd4197acdc1a31c26a0eca85b69abd6fa17296a5d584abc044ddb3de8fae493bc7a7ee348e895acf594106e2f5a391eb548a703e4f7eb48c57571148b4a5e5eb45758ca5c5bdf452bea7280c615a923ce8d50b359108d95ea456ca9979d7016f28cb997224172f02cefda1b2904f20a894d05baf1f1da53428685bb2e032cc52928d686236053fc37d15a9fbeee9103e4f788242207ed0b431fa191c2ec143a8ef221c582461209bf0c75b3258e17501d9be94a1d065247ba5a3c513e976aac0ce34115c40ffc32a73a8374dc9d7f721db9027a0d3ef916df56e7c9a89441177d40affb09cfc2fefb78a6a4036cb7d052a92b2b321c203aeb11a187dca472edf3db96ca181906086a5714cd8be657e34d160295d1af67c1446e42f53cb74bec6a5ba48521032fb7b301cbfa9dbf923786e25cc6285f0aa42b0414c3eb7ab7840a2b7428814f15ebc9f7fb90c89f187ddb24676a1f39e696dd5ab0402c42e8c556d751476f418eea741456d95d00973804784b5ff66f35a8644b23191887a49a0d007c608fc7286753a394335034243e9a382f398ccb3cd1b4b234538a910fa40b60ff203b882a2ef09cab9e735a00f20f2865183348b0c63927ed8b868992900a6f0b2c915fb7cc36983370cc9c438234b3f2c5a8fc6e5c6f7e480600fcea35e88a3a5f7905c20740bfa9ce2324ba976f157566998e1999a28a248b570d689e15bff377009025af129fb2738b3691386ac763e43e15b619aba87f7e0c0d2ad20a7683fa4ee2a797f53ce2e677900f99cc2de81fe2c7512e0c1105230be119d2014ad96789bc1f4c34e909d1367d61db62cd694f7a632255d5469a42e48dbe382e979334a92cc6b5cf381e7254304ddd01a339970239260225913e1cc9e070254234e6ed3001964af4d6312d3323cc541830ff484028853936458215d9d0495ace0cd8c839eff24813778a9d2c9d44bf614c0362acc083bded3d197ac8b43deaf14976bfd29d1cd8b8ccfcc199e35199324f5bbbe8f7c8533e812770ab1796fef65ce2538c5f3d48d7ef87af96485844afb80f3ca1a62da3063204fcc51d482201cac808857aa01d8719aeb1d62a7ae9a02c836316534c1a693704ff4942246b41057fac0f6a2a5decffd0485017b38f9478c913f6a178bcaeb0439050b7fc6209e99bcd77f247220e02152337c363d4e2e8ccc99e6d2ea87c6fcc8e3b88852a9491bb2680d4c90a741a5a6d538353b7259c4d88a649f70472c5524856e78febb783fa253a3e8ca0c516a563417e513accb99038f8b979074993c367442082c92478b3d3b73bfacd88d3369ee358b7b292c1f088df26fba781b6c30518a59d750fd6c61594f4e25ee315f4d989b009c315933c85c92fd1a43c59b4a611fdfd81011d56f672546b0c6e19e144b318290eab06edf32f04a152e175941d2f7fc0a14a9befbed3d683740f13c578b33755b9075c96afcda994df1d640c105da4bfd6c240e0f6d2c0e8f6d7422fd363a69000fbaafb2503db4c189fadae4b4813cb2c909f4b7e994813cb0d169090ce8a77c5cd12f0b5c37161c35722c5d6693e8068125b72e3e7e5206d72a800105248c306cac5c4d12b33605c19ca156dbb9712897e5d031eccda80883248dbe789bd172cad5ec455991e920df6dd36d1072459af11825bc3556a4d8c3ce3b233994037b2001af6326baf10931110b7fd0cde79895449734c30d286bfc9293711d98d395bd2565941c12295320c548088200a63240cd8a448ee292516620acba4203e1322d9bfb906a3aab6f500bd968dc857e621dce7b4a84572438386814ba3f82f09d001cc01ed1bca6d6ce0a261e54f2379d70fe58e249994cbbd407d237567a2547516a90ea129c957d1bf92968455b6281e4c90630e50046460c0a50785422bc92eb82209e692948e53f7ecdf3251e92649e9078482d42dc07d3a197e43770f2e83ab298b9197fa5471a807aa3091d8c16f79c14114cfdb039eb41b55444546d9ec8d3cdc81eb20a100697b85f9132f09df3201bb0ff67149f67ef0489dcdb1fd9049588bd55622e3af66aedaa33dabe0a2e95cbf51a8d4669c3a1207af939471efff3964606cde3f774828cdc7b5b7e1d05b96dc824760a6f890a7a3260832ea1336eea0b8aa94590131a0e60f620b38d8f91db3726e0cbb48bf9cf9c6cd440661ba377789c4bfb7b194cc4ea6c2cdf0b484cc8ec509010e9a5e7e6ed1100f1f847d3b39e900a24441fe38aecf63be9d25b1522b0a95f575ce62dc13e08502834d64fb5d2f752a1d1ed2707f1b27cb74ed486bd38b378850d55cd13ed40740ae452d5f16f40b0a0a8bbf1a13421e51877ebdff4dcbbc38eea3e6bc3aba695d8df025ce6adf0e4f6c79828c4f129d1a9003101b167b8a80aad7a0e909e0e127a2dfc4ed12d276d763aae7e933f938a2a6ef374f8915fb42fe41c8b57323f1d5e418839d60b2f74c968d3f49d0e25cb7b07020e58ac32c6e2a9a85ba7234e0391be1b55d5ddb84120678b5d2a2a63e09f504859c07ac07766823ec793c01c5aae38c812c88c098842f67133d8f9dcbd07e16622be3963e9b520f2d77afd95f089de07513a35f64c358f7c2ade8d140b0d39d8f3d5eb56f552d82a1a9e90d47beb7beb5ba3b939a85e766e945664d225e74f20f46aed96732d0dda8923978afb3266a9471fd5663d50cacb152f822347690b8977d47856917fd466beb30abea5b4c69ff5db576531fdb31e7998f29c34deaf050dad5c776824efdb1916b44a1fa5b589185569ebdcca6989422e4eabae12a9570327a15381bbc3ed530aba7c6a6cba40f2afb1a9023bd86e0a41edb8e626ccb786a605eca8e626caafa6260adcb1a6a6323004c34b0dca1e5927ff1c64bfeb23bc3442e90a8411fd184890631bc3561203eb4d82a249d8bcb000bb4ebc3bcf082451e344302dc3436c3bed7ffeb868a9cbc4ed8c1da0d2129315153c51d535fe23f69aab77fda06320eddcc6a8222c0a14c1c33110d0954484f554d8f7aaf1869b49d96b308b3422b94049619c0548e17bae0335d88015214618f8ee020149a32802ba376e781cc5a3d3ecc68e3ca0f901c275d62c4cbeb6a5b94e3696b86de410043ba7255e370110a1913a0602e22e9222a73c7459aa0a5d2c58ce0d0e429c6e8083c6a8f09fce1c8b8c0b4470e46d760cd4ce4cabe674422db84b9f547025302ce77e86515afd1e57c371a9aacc1d9c8b454808e70b2f0a84500ae323275ae22d044d94ce7c1c3f93c9b398a7a91bc65ea00c4a08aa602fb89c03c74051df1d7d8dab9797632031c45f8835c7616c1a299f86b80e5f5ed32ce64607ebffb14ecd00021d34da300439284042db2dd5a30c82ca4d070bf2d33b53802c9a305c24fa27007af031106cac81aaae575f7780b7335cbf37c6d202b33119dbbd892e5ba493f2943e5b09dc7ba64fa17efcd8e7846a46eb960f20e0416fa7fe0b62027834b225a7813a561046faf29916ac2f5e9a6337ac083e40f460edce31505a3594ed20a8e1190ef1a530e8ee37de55805b849bdf69c360fe05011fc955d43b51962af15cb036406acef1f41a1e038d9652c38a26bec66a414dec49dd9ce4ce80c8300a6cbf50ddf02773fade748f40b44282058edbab96b34676096e961e57c0c30ba807d36f0b08041c038dfb85cc0ee34d205560442950f611fad096c9b385e8d50db509e0780ca4f45f6af1016fc54293d17c1e2f16de09a72da2b0e652f2baf323b7c7c40c90964f7bac7b67eeafbd0e533f4cd41a3e5f9cb2cd642d604a430bc75e7ec8598c60a6d3e98d3b7b0549e7cdf2c2c4f6308d09d4546e60cf749eaa4633eb26867e439668d13fc089a1bab028d8d0773a2408790ec244eb86f7ce25868a3a27c8bb0b72ca50186183082cd115be5d3d8381937f808f6e7aff3c315434bc893b9486e6914d9a92230a9c182aaece7357b1e596dbd47914baa8b0d32db1d4a6419c870aba2288c3b41d0fd8063c552c5fd7112182fb17b4b37744a13e4717ac999f8af9cd301318b73cce78d9bcba467ae837a334f6bc299ba88c24139c35981e8c8306361c18c6e2dbc09cdba894984f39d0d6ccc21fa1cd3476b029242e19a9bf15869183186ea1a8a522c29fef3c6e9059b3f4e8dee981c1cb6c5083ec52266425fa0577b6a0810898848890b8360aa132c7753484f0ad43c290256be509737f849c5b76982d470e088fd246156c2522383c6c4d7e8239483007624c22a270ae82eb8218a90263219db5dc3194717daf02c3efee24e87263473230ea289d29e567c0c66560087531450fab14d0ce81b1e96f672bf8832a0c3d191058c40d12bd57a252632df97253ed1108e62c784d4f4d1f87c0020e0e0493387ef05a6213aea3f1a00f40307af7fd3b9ddb0a4044046a82dd4830270c42402498f2612d982b0cc1da36e0850dbef145e10b10579f035a99ea7de32d99add64a874c61918b6bdfd8de94e857e49432ef67aacf9ae5f1da9b620aa6ad0129fc0277834983154cd7fd6bed8e28880c835d6a057324840eb77bd81dd1b97e94711ff43268d74726f9e62449bb9c1c32184bf9c759ed6f5dc686249b05959582660da6ec55d35f211048bc151720d66084555bd9f71ccc392f1ae6604ab34f1087eb08a00e894b7330ad5642ac67eb79007a303e248b722d2449ab8692be222608238159f55e68995a54f89c344dd765174afd096eeb4ffe47bd7ff5de3f169bf04f78cfca52b160837f12147e6c7906b3aa43edeab03ef863766e7f52f9c37673e1611f4e555747214872a9b98324661e6edd66173843a2b5374a3905eae23a3ee2b4c610734e19c8977572441816069879da801eda14028d78cca8a423554baa56aa565a6829d07d258a8678e87911ad016d2484769746b6a66c8d64d7862021c3a0222af6436898f709aef107937542c9b8adaf44e0eaa0308c5654f426896d5d6d429421b7bb45d82a898dac4f0bd9ba0281807a925ce1221e73afcd2a82f82c83a025db80d9062e35db5641699e4515343351db7e90364d913fb3dbf106584dc43606b89860306e6505f14889c5f63e5461eeb82748a99dbd80ea265a3b6516e2339bb5202cd5a22f3aba00575cfcab9f0b49683dcd33856c9539455fff934d36ec6bb4ae8352c48d7d7e9d684222ac0a264ea6d2a11531060f4b469f97232fba96658115609cf00013ac2e37091ef09d8a52a725b935328982a21449b0727f7e2c0ac6b9337e1f233f8f78a7142223c499eee2640239074487f613c4922b8aaccb72662435dc032f3b10ea020df71389572e7b75deeb8a3d0b02b6f224af124746e4ded03165212969ab3e4e4f86293f18e95c2bdaa9078a6da7e918a6ec219fe213251a106bf60bafb7ba2a4d37387b3f7daa2086d52e45d5cd90e7bbda870cec0eba8c2a22793be7104cda7b559f94f55d362fa9c6f275303a8fa44b1fb65429c59379246e5108a57bd8a033f1b538e029c33a58a0e4f5e04e8b9fe0565c809d6fcd725945cecdcd148e61fc3dee7840cdb9b1bafd9e4fc7704845c824138b09c5d838a9a5b343574b88ebd5925254ecac2b92ac074ab4574cf7a1a2f9e5354a1bba22e81f504e56abeaa8e7cede36688d92e58de39f69d8885b206b9419d26dcd850eed079b88bbc788e8eaab70676984f1b0a9fbcb7e843309ff98e3cc09fc92b460e2bd019b311466d07c0c52139cffae2668cc496d2e53af08870c131a4cabb99ffbf681d8f9bb09c3df34de9521a609f553d3dc0253effb9709172904219ad894299f9f70cf5401c27109fc255b72f5ffe08bdda04d367e7e5523ad2e12f04233c48ca52d290fe870cab62cc15d29366174f7bcdd3c3ecb359f44f6661ae6c24776e18915270d0ac4991bc712e31c64a05ca44739d2427e01f33027860def737fac94e02cd455c90f5b8073e25a888658a38cb185967dc8cb19bf513bc796c1cb6ea70f0b663f963ed00dcfbfa67c329488f75d506ad62decce209a96ab711029474f43a006fea4c44846f87968fa78759df056382fbf0343e8ae463074a769b6e0158daf4f5001e199dd12c82f69b91eee05f8c712b37fa328ffa3c268969bc6d6b522735e3ed3893cadcc228e256f94aa4c759afa90a06595fa9afd1039ed9a4c99ca2d150c863a1a9e50e944f5fb030719e264e0375bb45dc7f94fc356881b50c371a98ec0f12e7c0d11fb94824a10515c1b655405eee52448275b56797de082e488331b241004b91148d6404f208a1eb11b39a34fc4b8bcf5bb70b2ff8237b4c4d44f686cdfe512aea5fe740439a8b41a5687aa51c22250793af7b4c87e1434bc0757ca492d1ccf470221e7fe2f50674e7a9bba5f6f09ca131845efeb9789e2647823c86f5294e9be4e842401db80fab85d9a96884cde889ebb4d451ca284cb1dd63c27732075d0aab60ebc90e513cf2824481b1ba78d5ac44ac673327980b04da3c49df1bc80a368bd8a6ca10187db91933b3a88087030fb668bc43ae181bf7981557eca39e5b9f12da4568ac727fc916957fd1460c1901dab821616c77c941e4e6a92a3278bae2d846865d0b63d386c743034f7847bd52cc913a9d75388b4162852511e2589eca4f050bf29631ac8126042fd372c867edca0c614acb63c9e3ad494017084610397aedf4a4c8322a79506866b706c56879cae07500b7fc05812c07b6444f3d90e5a58277b6e6bd657641802c2d57f231aa3103c3001a88ba4f9c33eaf3277ce66110e77352b1acd9d5f90b2075c765e9cd0e4d752cbbd0ef505900c6be3de5b72f2455ac4efbc8cb68d6639086dd5f76c806249551dd3a541681eea978231174988735f6a0265e82d02054d1ddd71c9b32e82509d0d5299d4605e100f24451132ed56764e03a5dbc382ff7eb5150c302f3a974b8f2f686ee64a3cb187f7f2e8fab6efd9afedf310594eea869566e0b55634461b96c8ce56764bf17c93c52c2614329588ce7ebe6b0a921c604fc7b8aae4e2f82641aaa1400d96a8d67b4822783a0f0d2880ce233b9614888360e6b7318b587b1b0cb035c6ceca30ee82e1691f28a97100fd567f2ffff1a99d88c1a73c56751b1fbc674110dfc47e13a443c02cbcdddeb0271174bbe4454918d2fc5f51840806c79ba24c2775614f98710ef25061e171de02be5feb318d8ed4fd528aaadd90c5806c0d37e83565a41ebeb51f134251c9e97f953cf9db0d69fce5251c53d7aca79b9b4de74fccb12c21677b8706db190316654af4c2a49c5825987bbd3a4474ecb7f794683f2dea42317274761e17cef41b4e2d97b01b6feb63b8a19f7b81c64eb629dc644665d8c4247319e8e41eb8766c59990ecf025de014e4f669642fd16b76bc62434ffd39583b7b1f251ab8054d8b519aeb08130dcd18527b1146e72bcefc9584db087eb4a0d7a602a149a3063adcd96b4dd2316be558e6b1c388b8b0ec51bbdb2b7b19846356e317a781318ca8d21c947c9f29f13ab913813d86a5e586d7024d2431ab38c02e79c3dea0bd35c03451dcb867b940b5d3057f7bb0d88c07fdc740c47d1be77eaef61c04014a39eeaa8d4995b157e03cb70e9f67d72380bfec883ddb2390a75cbf4a201034c1208af35b520b6268658f5620a5530de58ad9223b2ca9b4c21e4a5511ac5ee57e4ff080d11097a1417c6128172135cb548985e56141ec6a501dd929961f2c0532377a0dec6c0be39460619056c2035a30befa371fb7fe2f92faba9563e99779fea6602ffd4712df2c26f25c6a6691376e2a45b48a10b15d3364455efa15fb91be5e91f8192d8b579544cff94d2ad183852e9a8c9730b97cb73b425f9b0977dfcf0adeecb7bc7fc4bfbf3755e744e5fe00cee9905e30bae8a880f5877e20ad0d92f1a4d210e33ff0fa0110bc0606de641f795c91e2458a12c7c10c2b206844cbe0b0fae23118e4f50df0396afa822bb0cc4cf5e5632b6a07634d4387989d8df2ce47da66864b10f8a680c44d318a54dbb027a77d064d48353508e6063ca10e63a4792883571d77e6e451f2d762261c05c0cc11bbcffd50f591917440d4f42c8e459e6dc5087a86a3f0b980f1fae9f3cd8403b3203dd0c913d709a76433018ad4cb6e0d469a0c2e0f1acc95b2a4a828074c13ff94a6505250dc2d26dd6b51963dbeb48f90143d08d0a64a4e4d4e1b0cffdeb9b3cb9a448faeae376d72703c1e61f11411f7a2546f821ea00bfa4e9f168724b2965b92d2c656d2961099d4b800d25b405c2b7443c908b1358131b994b86b787291f5607df0bf9bb60ae592f0f2a3c37df479016c1067e8b53db9d7ca8f2ca0134929eee6b27c68ab0a7b636858bb83e2ef3e7c676702116b5c26ce4a7b30c4b048fb76d2641b8a17b193abc57abf4a6a2347749946a1d1842ddf53cc64b97b068eea540ce71e6d1b4d66b175f11811d35e8835c5a4f624fdfcd992996e2bfe8fecc52febfd82e3c961de7904d69a4d5f385d68846b7141f4ead948420a02ad7ef38850059d8f0d0b59d705e40c3ed97d69e8d5cdfb49f0acb58d480b3729e1052bdd60c060702fcd2a1894095a4b6e8554ae41a85bdfbcd71749987c8192f4bc48924e86bc2b6660c6a2f8480e220d23bf02d36bd874ab751d4139fba0fcdacd89581d847dbc57ce9bea9f0d73bafadefa0469c75b5a321741db0933f95b477cbd9cc7df3a329b3fafd88b770bb880b92ba411272521aade407195b4f7c522017c1e4620b38328eb48a107c5e2668347d201577b07e2dd84c549737b228b958875c4608e73d5741e5b15ac23fbcab7fb9e31962cae8ef477dc26f7b0b2f8df3b23417185659aefc99fe02e4560bc289be058ffc87b180bc298ea48fd07e0f21a09dad4e7fe21e7a4c36bc483c496d4417eeac88cf524a1eb112b8fe2dc4d1fc1588b8485289b98428745e802f1ba62ccb7ae648ce1c659d3495a1d135168437a02434e18b30d17eeacd708635d446032c6489c8a08605c90a4c8032b52acf2cc7d31c58ae014fc108003848864e0302ffeac5dbea058ef6f992f962ff6cdbc4eaee9e5aeb13a52f20ccd9ba0682bb243afdbbb4351de22194ddf5b0fd37579d5228966f0c48d4862cac7c840a9d674339e7ca06f6a6171d099cf9389256b754d18ca81d09a2ac2ec38585a6323a3cbc6eb74765bf806b2498e08740566ee9b966b0f4984d36cc8a5719d9cbbce6d8972fd8500c17dce1e689d11a681bd25db4976c9c85c2e79885485a1134de0cc2de1a3fe96f962b962dd18cbd42372660c58aedf3f2dcd1286473b7edb1dc6384c735d8e443d621c3e1e919c8ddc730af197a74776bc886cd66bb0154cfd3a3e2752679fe9119a31434863673c4d314346a057313f442ca7951e39b05d8ee9415d4325242b374817f4344a7a040f343abe33362de5407a644492f4d699801eff98a77bc0011f4f2befbc0632540a74b9536964b8bee8918731c1336b4812d90203550edae4c5900e6c6e91e2a30209bc169a8edaaeeeb5acc6449ec71414fa1d3d5a2b60db5ea73e8f7491e7c7e614274e3484ba25d6f7c8fb8515f2e0fa0fbb5cf369e263031fb51de34730164af6dcb9bc2f31349732c02f71db0a5ada6807796c6e7c04be7deb578822fc10dafacdbfe5c01d171e7f447d8eaa0ab653a55690464677a6112dbfa355cf6c931ab242ac3dc745e3fbb73bf9d5e382718503c490a51e291685dae3394322a554683da9febe5a413474e4726559a4b348ec03ac0b0c5e78ae70c8acba418e07109879c0fa978d75487e93298201b95ab222fb84c7bace0b4838701eb793eaff51214f1eb72a29e9e57d351da303a60474367e640ef41ce266e5d8de39a44496a521da243f2f9b44e42698192c4bd2d590fc4765a50124a2a95b193c864c228084fbb1eba1722e36b3b75ba39626c876e3f4e563386cba11b8a0050092f0770728fd982695d13bfe3ff288890ffa9cf275bfaf17ebf0f5b84e5ffdeae4353ee6adff482be1a63906557732c5f5fa1d817e16b70c69910cf11f6139f30469cb8e0c4e52caafb4dd99c8a6a53f64b345331619f866e29ed3cbc59d3845773bb9e9b216893ea32cbeb81ce2c2429c2312c961d3aa98e9a6fb18e9e8186b1a871f74b332834dafb51b5de7ba290d4a68c81d8e7a01ea792a07297572aa942b9ec7fb81146132b3cae5969810c2a2288e895a30c8a5a52d5ec8c05ed8d3d548be281be7f21e505f080778d59a32318c978f70d362d19aa751543e5aedc648b50983805ebe2872ae2e85ac6cccc7c1e8196751d97d1b23db1f9e8d3b11befe53fa053d5ec009713a985c58e9d580064ea7f46422394b482923a32a1525a4f61f4dbb97fdb732ae8684c90459164dee9f0425370fe77b45526512485d917c7fb25064b32adb35eaf3b1445cb7b0af1898d1707650e5178d766acef24e7fb322318c27e320ca9a4ec68a6424edc2552446362ae72de1441fd1414c00d0a5327a7b6c14de67cb77edf4d6a9c28b4b9128f67d728f0dbedf03d8ceda9a1a2541cc0fa2a320be3919bdb34f954e12f45449528561c9b78578232384d7da37f6961968ec10a60caef73fd6b43030a0ec34d5f0a3d667ab3fa7284b1e2185b8db4acf4ce69677b1da104e4ffe868fb18f258ee3ad91bf26705bd6da2ab205228359af28c8a4e001b48a86dac4fe77169210a713aae090e8b67901c583d83fda57e6ff1a1bec6eaea96eea9ad6f39597f0e964e2540c57d3c993c5f17fa11b8a873e708fd93ff32796604079cb23eae52a7efcbf3ec4cef7c2b432729aeb5c3513e63f1a000bb58bc6e0d7fe79da5df84bc6988675ed621aa1b3afcf7f75edd64355ab2566dd2c4e7c8714b715d18be74e18b789d654f23ceeacd6b9668a84889c88b697f464d440c1f0d5d203ea31f3ba9cd518468a1024aa4b4a7861c8af7ed772e829172271400aa940aebe0b28d190024b9519e4bc20849d33e0606a0c410f7b8a7ba9fe9e3b32eb44970ebd727eea38fea6c76a6eecbd7e918936feea9dfa54a1c1d8cad725d5c5dfe19332d54fc34515202729ac116825d4adcb4d4f22e5afec0fbb7299c10ef02635d585ddf568c105187f717bfe19c9a572c81ebcd2a64d91c347605cbb8bbfc7f6709f82222684a6f1b7621793e8e15af7d922cad4790873296ee18dc8f4db6112aa80c43e377ec7b1e06840819c20f35ebbd550c8d13c5d448c3a3c2f074c0b19d224814e53516559314c9345e05a2a7b81083549655ddd0e2b110c460d782dc70c5ba10a0579397f7f03e92f34a6a3d94b76d0342188803ffa530e75e4451569003fdbcfa59a28d359c745dea3f6095d36b14595c1a9a7ea7d648ee2748e0f8aae3fa5b66204689bb30bb69ca164b748ced786d6dfa5a50d4ae7c1adc62a5007131bd97a32dede7bd1013667ac004189927b2520b17e6ab7da839915b01b527464565b70f86b011b64fda27d522413235c18732fa67c3a36187cb707766b9326cfe87d7e69df9b4f927f1b7b6ee0dd9599f5e6fb1c074deaf43acac52dfb0d271fb6625d2f4ab26e083aefd5a5efff755b22a69509707162726ae00a14ac5b3a9b69c6253a6fe2b4037b1b72d52732bf1b235dc60ff10825221463ea692752672a9e0e38248eaef49c952fae979bf4400dade605290f34b5683b24a027ac3680b83defcb81721e1bfb55ed795ff28836ff01857d2f3d047ae313a0cee0f5f1890845f686195e5b41a70e0cf927d60959a5b0f19237aedaa870ba7a6513567f067c5f77d21210e821dbeff172f7ab94d203ee5cf50202cbc561481b04baa4724437e79e56fecb36882aaed701426cb05e5b343fef082e58e8ec742e21824d893272d8604c1fe60600329aec88ae417d7b40f87545e8c2f056a7741f7cc849c714fb03e89a68637c9d3517656aed8f3ac0ba326b1a6af28626632261006ca9739c4e05382b5b244eaef039cd926db3295a843be1a4c6e06f3a2abf435bc73793f53c48f6ab054195e49370b1bef360634efb47187b24875da534028767d230a9b8240c8bc91dd9bc21e84a68472cc91b5e02099fc17ea71abc1df686ee4af38beed96b71237cba80c1b72c1df8f369762b3cc59057da00544b7699286f19fc2296b0c9ec13526a6055faa46334b09c38c4dc4dba07b26c9ddee9f164d90da8d6d7f13dd9136ce0db577623cc638721dd115ca8c2a020531caae18285899d0bcc1b8794d8c464eed82fb274016d460a32186a138331be5b9801c1b3f0dc772c7288bd6de2310593b9b12c7badaa8afaa330c110ece2f5e319b1e0b8a32dde0c4372edcdfa7cf667c6b8188e949d9b4bbc3a0d06a89aac99054bdef65568ac11018c2df0db2e5feb5b05901f422f146f912bd9c9b4d472bb0152433216b657a49dbcf53f086eb99816d9075318dd95502fd3779657ed8d1f458b01fbc6a4678b39b5e3e1e1452e4a90d1db4b0e8cc1a5aa092f729e5d515ceb97bbd285c661655c0829cfce9ffca3cfec671451fbfe33c44534b995ab03dca30f13665598e5d7ce105cd8f3dc9b93d9fe03fd1837973f627222398d0c3e14f3fed8d75b70fbe492bbbed8aa1ffed11f9fcf9167c110743741adfcb8c583a4d731c76282feb8ad91f0248e1ead0932710f374fb3cba6d03d35f9bebe2e7312cd1b43f1ea31c340b48578eb77b65c25c6797601f0788a9f68f974817b9a2131553b36b805bb931f7d2da9c6c8f56e817540c945ca109e8a2992671da006bcc090b249e43ed9538e694ae9210eccbbae3143dcb3442f1599aa8ea7ad6d735a3f9b4e0fcc81c62cb1b23794fcbfba44ccfc7c269e57525a99b61b079b91c5426e10716919bfab4c0076659d9c3fb5c2e917f0910643e383a22383fceb81b9cec6c5e4de6a31c8a8e05123c28f01aef3f2c097256e265b418c98cdc575c396165e806305fee058a29b46236ab66ad78c3f82c444104b302a04886560f8172e9261428704662cff2eb4246662dab335bdf6735c2608b3b2f6649172c00d1ce448cc68027e8e0311adaccd12d1caead81c4a4331fec1e3ea9aa98abbcd1e886a8201a1aeac0d087565edad817711a6a01501d5913eef3d60c1f4978f531bb2ac9707d39d8256e2ef0e9512bbec987e211fe822f5730a7a657d4cd7d3c4dde98cd485b83b47a5474eae72fdb7ce445724a2ca30057cf038bdecf62335060ab4c7cb2a9cf1882a359cbb5c7ed1d430e244df68487ab837f83a5072b31ce55c31b85ff1174ca45c92249fe3e13c078184fba4cf0e8364482bb55680e3f066bf356492b55103596f5b337a0f05254dd67f1353f96abc4d2a1de2a4bfb08c02814f32d283f478b01de623ee8d6747279e3fe606a6d81b497fb19a633f91cf16008e963878631f3d60dac06790e801040d3883831e5c093000387a083260f024df79c0bac013281ff9f7a7f2244fadae3422a7ffe2874afd80f780fd935b37981801a89130449484a01fea07906a3e11cadfcae0dca2e7e6f968b48e34e2a74fa7d90e3091f7e18ce1babcf5c3179de1b2eaca6a406addb69a1ec1040456a54fb540a8b874e385f976c42d5d3b91364323f2b37d3b0aacf8b63f7ee885373eac73c27115ab45cfe92fae47dd0fc039bf6ee87802ad1a7f9ce97ff01e33b3bb3bfe28e459eefa7ff48e5fda22f1b3c1bd4e83790a09d2e5076cd9ff3c5860ba6cc549231b0720410e969b9fd1f8f0be037435c81316bd93da17ce1eb9a8ef2951a9252711529a0f993ab2ed8e24194947d46787e7a582919b944da8670f62ce8f9f92a3432d1900ffe3aaab1aea8c09ea7413dc0e4fb7609bfa44f25e935530d8b7f928ac24254b39faf903c2206980cecc1b1c13bd679549a3d06174d27657cdb020d229503b822da2a799a698393b26a3d7a2690bf06ad3aa3e26ee21f84341049598f894cffeac4e38ba7422d72f17e3b030f4208e331481c9743b48ce4dd70f82aedf2c525048850c6a0e81ac4819a9200d9fd276214f2853e55045834d1781328f8bea346b477e31f97471b2b8cc3f41e409f440ff602c8923f337a2938cd806944c448182ae8f7ddbecf5ceb77740c8f9ece4ed2a3ddab711891014a09a6b2410088a7e79317be2c78b836797e864e397cb4e2eee8f3ed11ab3d971ec27552b78555053288db27596da06d39dee5ab497078c0f5a827c043e478fa97bf22700cba544d01b0a2315724207c3252259fb3546a585903336799764847a0000b26b805317a276ca2ceb891289a396f209de9f57070ea2071a935583c55a0fb47c9b0d95104ae6cad326c3f40866ea36d73d3fd651bf7f2ba189d9409572a9451d11b26be8d573b967d3ed1565105d7e008187388d448821583fca8bacb5f15ce7a0ed46f0ab0354f46790995c2939c9a48abfdf9211e50f324b8bb4186f893415e036e834412a18d12800d6e8812f930da3e8957ef13a9368ec1c0db6e7c8fbee851da23caa1fe560174a3d016549c8ac8b94e760ec8e978e0172a60a0b9d0e208b605edd2defc029fdd85d50ea53240827ece61d8613435524692056ee4398e070a21cd502601d26eb638a0cebd6c5b5d0e303e821aa405c21924ab93c1d9badb33d0bc0d1066893ef34bca250de8a3c45b1d50c55f4e39f83bbdf3eddcc3a4a9eab3751a93bab4661927c11daa4e7765d993e489785e1281a6c09dbeef30c6e5a00e9fe50858e577ebae07f7dba17263f3cb6c70bfbdb81fa26d817149973ae4781d47b78bbad680729fb411dda398445b1b83c0222b628d1a8ed24d4a70954fd147312c5cad981d4e94c6c38f9dda86e904164c36268f6f4fb267237609e53f36abd15a101b23e09576db946ec461c52a634c90f305aa31888094e43b42b803a603af4f15fb303882d43ed464fea417e457833b37a2365e652e96abbfc2a66b776387e3dd7f783107e40c45897677402cbadedfae68fc677905b4ff9d694e14d939af7b41df850e8d787f2d29695f4d22bbc8dbafa9bad9f57b0beda0d9ac6e95e58064afd8fc5bbf08d121cd487b5281154613eb04d84f4e83860904d73d2a67a28e41313729a145aa8dc3ff4b4d62cec0c46628054c1d524ef048dbc070fad7f3929f0e095e2ebfa744c87fff189aa970b0e48dfe0553acfc99931259dbcf4ed47e83c7981340b54864353f53ef366f1ca36414d57d5c9b2d506c77389dcc35ad87079c6d751fcbcb6f96e0248feb967b73cb6dedab11421edd478dfafc9d5ed6c02c6aebb5b998d1b01aa0084e0913058ada67de1506c351bef04890a4430cf2b33c09cfd3cc6801071391dca0c81d09ff78059fcd0301943785f0da9ac8ad1accccb28d912e6d097e204fffa7ba4ad04aeef046a10521050abb80dc4ebf0d7a801ae803be0163831eec07bf02bbcc21eac32558849cea8f1d57cd9be6fb4fd7f1b3333336b69a0e910cf3dd6a13806a10f9f2889322993ec0bc4a0dd3f34e76036738c07fed168a74b115312560f6216eaac54b23629a6846ab2cc3945318754934d35a3f861d129bfc94f45430789a60b538d16a241d58865469375164d36a7a986f4fe1399634e1211091061b26a95a52923b5900f9120a258ee4087911dba98a0de21f50161c972483b64938749d2818e4c8711d8f87185448706cc39a7988962d9857c088ae2f6bc1fefc77f12f2610e75e490811c8072980107367008639a5019f9d5a068b2d2280af9b005aec471b5d2590d0af599465f82fa8b3754e08624483704818421570cf948436cc8d0c84a40ca3290a1b21e6cf001c9862c6c186d30d55004520d2398d3a4229124c9c43013c9f0039f6436a11622834651283ba17ac8027549b29244e91e0435ea24e4c353287a5ef82423339b2c33b136ea146a7e42653659e64531b1b2a110d43626ef938d7e259640cdbff21b79c9da2a16d0a86d4ca38842958444908869f47e5c820a49352c51439569fa79f2248a8ec9f39d69120d2ba0a1a4010911844440888e901966a863061dcca0801934197420c30164483271c83253498e1f4964f4244bfb31abf1acb1a4c932b1acf124a83f0da9533f03417d0ab50745d1c6c764e2a5cf4a2572f4ad50ef951f57a5ee7509fe6426bf4b3528cf1a4b54167a2ca3f7270f6e9d8d5f19facca7ca1892a0450c108821c93481baebccc6b42a65a4ce5a60c9f59092207390828880140403417e4c3880aa326532659ea90cb550c6125b3a749d29c8eb2b2814113e2419c445279cfc54de8f3ff44788e33f79fffbf781a7130e939b2111094edf29cd94b292e7f590258e3837dc4f3991be14b1c010635a7d29f285385a3ee80528e6349524112c1fd8f24d5ea8e2474d64925ea09926b1c705d285ec820aa416c420b52005a9051c1682c08214241650b344fa552bfcb29a92cc6ac451932c9a2c4bb2937d7efbac80c60a1920adb0c40a3ba4157020c141028344428274492328cc39bd4e6527c96c50a8ac84fa4f4a288fecda47d949321b530d0dd9bf53cb0735f1bc1f5496939950355527b33171ffbf2f3d6942912d1f84f2f901f5288ea34f913a25fe7027451a3159693fe69046d99c73864f4673aae0c69c5a2887a4421773ce99a382933927d0c8ca5268c20f298530a689933ad53ffeb17c4829ac262905164840dc98739a50a3388a3a28259e0404042420dd84d3f2417763e1b47c931beea780e0b7470fa5e59bf82e72d20f05fce0804cd28f22241f3820f95800c947696a8d1e4b8dc7f29161467e19caf3c4efb422513419aac687e6092af3c4f2146a8ecafe10ca07e593f190917c60332020cd8c2cceb1882a3f8ebe87ac8c6cf9a0cd2245314a8b4a56a44891222610d43593347333e72c3f9527c9c8419279424609999124230449c6448aa123338120ebfbb1a4f2d5ccc65483aad1a17982aa990d4a0795f9b1460725f64cc89362ae9873a57d2a238964ad9ceda9f890628a803227aaa6d23c416536261fa4794831347396de871403825f1318114c538e27750a8a27756aa584e424392af1bc1f9cbef373eaa0487ac163ce498495cc14248a39a4972a7cc8227b68757fc5cae8c5ce2289b092f9d40fe96535857c3824220141cd717e6efe13277d27255403928b1a2e43d3d4f2416267d5a0847c88a2c9fa4e4e49725181d47282d9c2915a7ccc399b4c92880cd3e8c3efdb985a24ab6b5f922149c449224b12cd30e70c3249221109cb83841d81848d41c2c639716e321c4c94d212ad190fa0e6282cdfe83b95ccc6544393214992f9f0496663ca6c4c3595e64966837393e1603a9d32bf51a5284629439f2a5939bd64ad52a46682f333e47b7885da2b56cb7ac82c22481714f389e7a978950d5b263899459a08204d3ae7244bffe598ca504ca5f628630f720f2157ac64253de0d0c38c5219fa1c3d1e30d22375c54a89082ba6951ecb2cd43e7a1489c213a2908139e7152b19ff4aad124b960ea3108028c4e401826942d564a5be93955024169d62f51e5020a8755099ce230079e840c10e285431e7152ba5505c6db11c351428d9431f648e9ea084279831b2c44c2c33d5139278c2ce136609b26a7c388a9b262bbf2f966198337242144ea8e2849826709d954a90d5024bf26b420b4c808309633021354b6278f241ba4465a9f2d34cc06109692c61689a4a9698759f2a47312bf54c03859fd64b38a2043aae58c94a52e45989082b26926465e44ad417500294690241cd4725fce841129e98f38a95d290ef2552a7422d44127e126ee6152b254b0b21c18d11126680042490407304378e708023d05922c28ac96fa02d8eab8cfc428f65f465a8c3d1631181329eccd47d8b6489a3070a752f59624b2c575dfb91c6d43dd91a7b0b0cb3adc4087918218c79c54a49af3222ac986a502a5ea587de07e5cb2fd2f82546608011accc79c54ad6bf8c082ba61e6a23d080871e787471c54a564ab293652522ac88a416d2a99c111e554cdd977e5c811b8f203f77e031a76914ff1d66984677ace69c367ebc23235924f9d99104fd324776e8c08e25e69c2d2d94896189d442dd8f7658b1e387a9a68fab21ffd114618d220ccdc9f52a886b30d3e44aa7460f2533953540e157b2c491d44245c8e61c3dc92282195e358a65fff4ea87085098825499289644404d133922428c2914b314f90965ad1a10d4345928248eea08a3e5533f750c4d2275d039e7e8b17014ea67348426783e840ccc39fdce7270704050eb781e0ed9f2c9489dfddc588d85738383534373a3e255ca4ff539a172332422c1217dc7b9191291d081003a78a0e385209011041f04228210030436e69ca6225e2592453cd802492de48b94625884fc40d627c91c93ef5a258641baf4a992fc7446961f0ad5636a81a01e450fae541b0544aa505f98632a652550671e08f8076cfc80033fb032f528fe394030879569d220a84bdd93de7f1f00c1075598c896d659109933e714628e7a80821e88a007bc073a2339dc90630c39aec80113c716710c11471438f280630b38a0c0f143a7f8c85af1e85469853ab3f1a5a8f2a3ff59d6d22b952f6d4c2dcd9399bcff2d5e7a1b93d5429d657e0f65399987426536269a5a0555536332d164251a5b05056afead5a6506821f95cc9479a892f41d95d998429d7d71a5b92988fcc6acd421f9a1502dbdcac472c5ca4a28952fbf93f71f95a148dfb52a233fef979854220f20c003cd83283c98d34466a8ccc6d47d273f1e9d2ad91753402c714c95202896434a767081d10ea6ec60c9e88d244c9317ea590b14cb0c95d998481e9d2a592bd4a306f26346b2460f148a6026863aca1b0378a3bfd1476f6c3a88639a3cd46e69152ab33195a2188527f350a8cc669a6a582b9fe932f45c546d1e9d2a345f933e735e13c8020b98560ed898b35cad50e468224fa35ff1aff42a293ef22b353972adca7a400d69550ed94d5633a4553918318206fc3ccf8c6446a00a8023e4c38bce3957f099231c3c31e775454c67aa36dc630141ad228374984c1d095c56547141c18d3426399a52e5376a23e44aaca179c024322746851b39cce94b0f8ae47e9222359392873b5d7b261f8f8fd27d2865a70d2b6d4469c36b4348769389e50d8a708313dcc00266d0cc39331f7032bfb35267a3f8bbf6291028fc4813ca06557e244a95851f948eaaa11982d2a35ea1b21a3ca1066f645e0d5c500330e69c9958669d88f628d2db649ef8c32f33912a9f2a4b518c52922c9d1a52a24668ca863c100cce68d2a427ca122346697431e71cd2c572fc49c346066bcc394d28213144a16ac6032af3a38df69d24c914d0c744147fbaef2c907b9087523af8a47b9f09f95024c9d2a7bbb82250c54c959f90175ab1b21efaac2432af2e747afc08250641260cf280811b33cb4aa7166b7b1e29650366a8604e266d94e1634e151f3a838c9979641f4725a82c8ce281469dd2a1d66a423a4adfd166c494f18359861265a45e9082c943d6a9f46403e69c45ccd10bb28ad4425c7f9b9538863e48973594247001d102151796d429658e5c40e79cd4c45513b52945719c28c05060020f30e1472a5e60e042c09c264f8a20cf864ad2b380322a323326321bb3522f91993191e5a752895c0b6540e117fe0cd41acb15801d26cfd5a570b1308436462b18f207003ffd95d09103155c418e9701848694b8b2cc79158000735e03b89098f33a62ce6ba835e735e43b1f59a20ffac08c0c124527645c31c61373f664e49791410032463292cc999158b2cc4b913a9571bf22cbcca7c82042868c8d4ffd8c61658c2a63e499797ed442be271b928965968d81c318590a12b0b3f2be89f8f38d65e8b350a756a398896519ea920cc592a5798ab35419a95be04af5913619e9cb2ffcbeef3623d0b81abd07b5494814431e3e29bf5168c52a81a0fe7c92cc94a1c85004750a95b540200dfe4cc5ea7ef4bc16d7231755be67266ffa684823259895dc8f5fcfc470fccaaf04754a67265067395092f4e44401e2cb4ce1d7c727fe7fd6ce723213e9f74a8f25598ae216c7158b0c7d2696a1e87b467ee42786590945a232b2fc52190a4592e0939ee2a556911f4f298a514a9907b246282c88800e13b912c34f8f493a600211586024029a39a7cc4c288a640f75284e6965610e1ebd5cc00c241230918c2ee0801bc44f140f825f1321dfd37d177540d085c7028224e69cd3074ac63e3e3a2b83b42a134b2f6e9d098961502892da47c94cdcafc85639fa9538aeb21b3226c91bb806577ecc32f6f1c9ba277d37110122db494c3e48c4127ee4d6a5ee5f4c919a49498e1f6b5c6525e93b28d2a8321e27415c044d24e9913b649929b5ca7c0ae5c7520cb3ff653534992741bd2433a1acc642a142bf5ae99457e91055922298f91d66a4c8bff07fa89a92e7fdd0a052a41f7de9fdffa28afc429de29ef4583cf881dca64c017d60460a8920a8f957921f08e6dcf82fea9a14a99978de8f1f6932feadc432d33c99a9243f3043e1fcdca0545dfb3114bd4a248bf8a24dc9123311282bc570484492996cc22fd3534428e427148a650f59a91f32339556295233c94c28944f66da99a94697ba8b2549845c69154d9699529aa704249659c95a81ba86f4656b67e1479365e24f664ab2c33f0d82acafd23ddcaf7c76c41fb27bdfc3c51f95a89324147b4050ebb8a02e90438ad429cfea624f03be687d51c417a92f7ebe00e28b23a32fb82f6cbe28cd3957224d6624abd1212be990792b312be94093655e76858839a7688ebe08e2851e33f36a68b4a7248af8c3d18ffe88930a5b9060cee9433109992376964fcb37e95fefa2085db460ce2976960fa94f6267899d45768144ec2c1285123bcb270a399a30003367e6b546b1d43dd4aaac6b24a753f9a9368b3c959e3c8d7e8850ec09e23e35e2028a2f7211665cfcb8c011e69ca64c546540a18fd859e4162b258440f0fba71549962cd177b1f4e4d0f7fdb83a81e017740a1ab5ca8f39243094304919686192322025ca9c36ccb9c384a1237d519aa42f7426e90b2a26e98b3526e98b3b26098c1826098c1f26098c004c1218ac4902e38a4902c3063f5a38028f393a228f394202c81c2111c31c21b1cd111250e60889297384441173ce1758f83167e61c1d39cdd111d61c1d79628e8e4c608e8e7c608e8e8c314747d498a3233898a32371ccd1113c56f831a4658e86b03047437498a3213cccd1106ecef98305216b2099a3357ae6688d3f476bb4e6688d26e6680d09ccd11a62cc397fac20838c1850e6480c2b7324863847622431476234608ec4f0628ec410c11c8931c61c8991c61c8921470b3f4668b4e6088d24e6080d2ae6080d2de6080d0dcc111a259823346030e7540186155c98028d399a420773340510e6688a28cc11152f7344459039a2828739a2e2c80b0c435831474358608e86f0628e8610c11c0d218339e70f16640083ce11183773044693390223007304061273040615734e971f241f73024961cee9b2820f16cc911333e6c8091b73e4840773e4c40873e4240a73c40333473c2ccc114f0e73c463e788c78739e201628e787ee68847cf118f38473ce51cf13c31473c59cc110f1773c4d381980be829f381253401e4152144e79c33a6093a641ca185384afd331f8a3f3b5020480d4e988004e64aab261525d86192451d53489b62a7a8f89076d2832c6e2e511e734e2c5ae0b158821cb180d20180450b73f630b1583caf1403a408bc117ee2ea14811144e0a78a39aa380327456a263740644b83be8742620745919fc6d449c50a87b4aa8b56634180068ac5e27933901e70e45be99312365663510186cf1641245d898a57f1a5e6a1a8d3773ccfeb54608a2b301da6c8530c9962ce39331f6eb2cc43a1b292d42b2c5d931c304492a209734e92142398132b32b11a9a69a78439758a90ef21454184190501a228490f04c422fdaa55923ad5434ff464a7d0133dd529a4273598c3c98fc7fb252f198a24734eff331419b24e588250e0d6a1ff469b960f9a5c355d13b501124122980e930485901ca00032e7147fd80257ab9c1fa0f01b4b9f92eb95d55838a7d1af86b4ea6648ab3ca9378ed55837435a05ea1f0614997392a406c905b47c3da04ea37842a5c82ffc7e86841c4d64f9a9b06cce796136fc7087a7a5a1901e00624fd77e245b1a8af7ff44b6f48a2435c89d7002e7c412e468ea3d38d142781a7df99d54e2f88d708f45015050c00e399a40ed25c0679a4c2622a50749d063f1408fc583de9c58101826a9532952a79a8062ce72145364268aa5e7c9d163f123f6820bde8f4bc8d00789a507484ca860f6d0b3c24f95e1643df4293d969b459298e048071863cef0d32926383837a02e43f1886f14533827cff3df589e56e4c903912a5deacec46aac1e0350993349f7414facc6d2e05659a028ee53cd92ac94c410529623e6f43835393556637123542723547d16200a1201f2201160015dfb281df4f1477c3ee1ce4a3532d151b2528d4ce6c45cb09693e79d7c8a700116deb0c10f1510c20fcee802073744b8f052809810cc9042a0461499098ec042248840c8017c30021d54388093080c810288a5020b1080c0e608411e10240d1b7891840034f851800f3c7811c24802c8818444063ce988302e3de6bca2703de1ca63ce0b0a4f20b59620b5a09086e6200d91d184392f262ce10ed2500e24a10f4c2125744f122a616163ce39844c021496600ae27a8b63e6b7d8b3030b09a6d293a0d6a54e72eaa1f7401a4a49c7758410e803b53fe2234b96d8d24e383cb2a0268c0a66b6f8704d98e942ba60a60a97cb0b570a578c6de1a6688a961a60b098189716d1754d1830d3c4e62544cb0fd784e9c16562f3ba2e11c6e4ba602eecba442f47601776c1602f44b9ae970b6662171093087b79a199c9ae160c6b81e26ab92e6ce6520276c94c97ab0513f9eb2a02bb2e2c460a360376612f2dd85507765d2fd825c405c37589b01816b8886052b85eb8aeebc23acc8518d14581eb05bb2e4c74bab09619998bfdb870622e971c443e5c3018d65245331876d5a8b05d48b4541f11c066cbc42e70b588685a5cb04b345da8b87a704d6c8e46a379cdabe5c22e3bae18acc875612e1826ba2eed3272c55c3f300cc35e742e2218d6825d5464f21143cc0e17901564c02070b960315c2d97e89a18142c9bb95a26f6bdcc0c752d403487b0e09a475e8089ae97ab05e6027289aac0b0184f7465d185612d19f64386e672b92e18da526da01736f312835dd775c15c977761980e442f970cd622c272dd80b1ae4b6473c9f8c0ae18980b3b82d580d580d1209aa279a970bd60d805a465246ac162c030ec7ab95afe2593c3e5c305bb5cae14b819b0205e7cc860d78ef901835d425c31d725c2ae968bc94503d66142301f180ce672892eecc22eefa201f381c1b45c2217d1757197e8a2b964ae1fd775f570d580cd8860ae98960bbb2eefba5e5ec05ab85ab0cba5e5ea301f9708abe1128930ac056bb93a0cbb64c0662e992ba605bbaecbbb68c0666044574b4ccb755d5c760529127323e2c1e5125d32574b904b46c6f341c40c355c38b45c18f682c1f86be402f5c1e4bab00bbbaecb66c2ccd5a4b2c7850795706653e4a0638d295af4600352b41cd4478e386694213998426831032f84566342d1e204584019223e7922471178381952031280904308edb40862a4d32980c1c90124076ec8ccb5c971456143ed91c596e9314d2828d46083ea022bc5c18558ca68111e2850bac30654f6c3860c8615260558b832512e51ccacc500f33202ce9c338a112e2f4bcce810832c323031312f33332f2e3e4434c8c0885c442efcf849a2e786189a979998dcf24385abe5070c007e942220a2918b1b06b022cb4b0a188c4c4b09080d79035966a6fce82da51492c0ee0c2e525aae979b1564f1c149129201b096991627354cc812c712978b47b2e14812588b0c221b1b64e1418e2c364059c20515901c400444124c64894186ca8f16acc8b2c485550c39b3842c37a50c10282946303222202e412d09782289981692c8464b2208159029728325629a008114301f2e4748305d4b134f44184ab0e04233b3646687172b2d45603a2548a005104acca001469600c29d28a005597cb0be040c00c4aa70899125862d891998ee6526260511b70384888699253049ec00f189613283922533324c7ef8f41b126481a1a5b4cdbc782c007999f1a1e486007091652686490ddb892c2ea291cbf522c4cb8c4b0f2f45727ef0c1922112c3e4650626062049647268e99181a105213bc8f04291189a9e283b2431136382d18068dc0e14981f2d3032b878a24de6470b91ec088c0ccc4c8b1197262df9c8e532836888cb0b5426868b3a253d0489997941864b42a7c7c78da169c9a12587972fb21449a125890b0f2e35c8e4d02244852aa241892c262397e88b6c5a442e48dc10000788210736b006196170e08b2d145080010c9141df735400ac0c51658a141f284e8260e2658d66071d70c85a5ee0823dee2042107ec0d1e106196280a1800cd0c000174228f9c1a3c1851f3e5e5a70821268a00b0c6c61012d288085141a22464c12b0628f3c92b087b8c38e314ea2140df83e4e0f33305d44010527830337bce8c2eb36185af0311303236406314460c5f7273d41ec2c5192050312c0c40a7160147060035f1480ca146e0bd242cb0cd640c2141c1f361998970e20a0041ea8c0031ce0041213c8228b063ce9c1e981c88f0b0ad813b026b4e091d981d1d112044c0e9738ae1c606d603790b1c10f36b019606a60271071000b6a29e2ca21860617214062c060c05cb85a20b1e0b20246ba462d29603e5c66309998180c06c6056bb944d7f53281a8209a4194822c2d0bc8d2e2e4a54876c12821891117595c5c7ec0ec9083245ab4a82d30fcd8028c170564697122f3c28c902006c822038096214435f8285f7ec42c89c92f33a21e82ccb8c4204bcc08b2b4602509194e84c5117ec091c5658f2c2e3f64b841961fe50d29c8221324050ca60717232e5334e1828f1820a044cb13595418a2c210910dc9484c10d21e4ab8f01093616e442a886e62b6c822e2a9465e665e329045e4e3458696928f2aba5a7eb4946696b8f000038017195e665e8af8d1c28f166482c8bc0003005114947891411486122e6664d1a188165576d2818616ef870a2fa4964b6423d2698102e485170c860acc0c303d2e2ea470bdf810b9b4fc1089449808461423929911d222829102b3820a506648302f309708a6e5b61869b9626210cd90848c0d2e606449210349905e5a668849a2440b1c4adc70cae61e7a0401076ecc608d33cc6001f944018512243ff8c089e11d761821325221068688219ef4ec91061b34b0a101b40a081cb1028e36d428630c3138800123a61d1ec02a1b408e4a30021e6fd8800d1784a0000408809521aa3ce90162890d35c0f0c2094c50000e6c80000338d120440c6d48010e1e18a1cab2871e4c58821522607881d446192f50c1186288a0030d6040024e01000095273d3b6aa4f10219a0a00a263ac8811a6994e1823152c0c5164e1cc00046c0a30d1b8c9182137860892cbeef282101c1e30773c071860b4c5002107811c5027c3f05c00a11547694f4e0d59080a891c6195848208a257c0f40103b4b94f4e0a500a465fe600e35d280c10b4c50021074c00b2918b0c49520767ce8c1e36ead5981d432f7f8c11c3c80c1192f7081094ad0012fa48882010b58a207a72b567e3062ca030a4e9aa890c20e8440f1841355a8d00a2cc089d6900664f610e98145c1050ad81362c068f902f302c3c065b12aa297c9878d880751f6820d98906b06181944315c415a60b85e906161851544a3161580a4300222c38f187c089921c9c0c4bcc0602f2f2e2ea24b86a03ec36f5c19f15de45fc82ac0cb2405ed105443114d406534590fad51eb14cff400c422b828a202454c3127ea03c99acc879b8cc6879b6c06154153844f114ce69c656b5f31994c44ec30992f327c6e84a32f4bbd3232961f155f961a2b5f94ef6666a52e7546663bdf8f6cc8777f536f4c72341949612045c59cf30347cf02424c0c0e2928f3c263ceeb8e392f3b5a5ce6bc7e0094c39cb3ab194e368e2c5ea4eb72c7755b87bbdb699ded6a47bb2ee7cce52de37cb3966dae99e68ecb1cc76d1ce62ea77196ab1ce5ba2d6fdcb66d78bb9bb6d9ad6e74eb70c61cde30c6176bd8e28a29ee6ebedcdd2ebef76ad7de7ae9edb4ac71daa661ed6a9a66b5aa51adb3d97276b3d85eab596baba5b6abb97275abb8deaa555b6ba5b5a3997274a3985eaa514b2ba5748892a349c50a8d94aa7f85889912cd0ccd00b2016448645969a634834496655936e75584392f22cc79d521841f47e69d00f580af803ff44f2854e6cd9450a8cc2bbf07b36c06559365e5f7200dca071f7c1842fa142fb56ace6b084220fd1a9aac448448d09f737a7e071e0533233f0f7a244bcc583ca3388e4a90907c15739a4c244f002fe49dcc9aece626ab34d9171d8fde82a0f01b53e437c7ae2f1ecc79ed40a40451107813735e6fe8605e3998f3c2c1d5c665836ac3c69c570de6bc6830e7143bcb274a1415af52ba19c525339ee77927d2f753e87bff52a76fa54f2b92c4b1387567863c799ee7e1f00fdc2c28ab55ce8fe7e590913ad3232be7468f2c5d865aa8eff404815353b21a8b06d42a1d8e46949e247d10ffca9d1e7e4dfcaa559e48efbfc7f201a9f81039a4553837dc4fb1586e54bc8ae76958ba3742d54f55825071c34465684e4d258b2595adb3296e90a6343125cf3981c20ffc524253629062c6fc84c82fdb22b8572429ac51ca9139a7d82365ce696a815f26f6fc98314d3f41734eb22457be8bdca70d9f06f81041f2e9a2088114e50c9357899c55ea39a569f59551a888f2830925a684505fc9fb9f9550de7f544e0fc53da45542ac214496690277cda6e91e8b4ed5681a2198d4d06450e080d20414254ff098d3844265628922c38f07959125ca148a99c7e29fb460ce9d71d2139d39e769927ae6e8a1c09ca6f1d3a9520b8d40dd67a5ce31f991059402fa52ac91fcfe8ea9862c693232fbe2a987e2eea19942fe67a2f8d313c343078f1973923c61cc39799ae0a932270f4d929d8c67e49dd031b35093529c28c0499ea8955892591334e63481a526438258238856105b10d74e1c1395d9802a5e65a555280f85ca6c7874aa90a49d91b423c36404734e13aa665fccc94ca1f88dbe24f51fbd90086639a614d7e2b85aad48263f4c704840b831274a2cb30e8a2510241099b4840d944a256a55a975f97156f7236b49124bec929639754a9668c4c4a353858c4252220549c9968408739a502db0fcf807ae867cd6552290a915ea244c2429220914924e1d73a24cbad449505e66631a7daa1c597f258efd8ba3388ebe7b9255ea53197a1227456a262952a77452734e1d283a344162c6a35305091e2632d3610e9235900c21a982249bd3c4cac21c24319347a70a29878e39a7a96b243964e40ccd692aa1e4e8393512cfe31e8be7f5923cadc82357cc39a77804892339cc394da41ff2f8a18b69020a7dea872c3fe09021f48393659a80329c2a262070684c64141f9a30a7292bd1e0f41dd207213767dcb06eb49b96d6f61b85ca7ceaeb91b74470e57b56f2fcb341a1505f14572bd60f96cc873e40e1d777beee49d6a907d49c26520b917a1801859fb786e7e58e0211e87eba9aee86ac83ac453622f34c53f824f350240b9579285466e3912dadf228211f7adee8572cb2fcc20f0767272bfddc8cfe66484472e2565c906d05dbb8d1845ff6774c3d144b531f45ce75a6a7600fe02b18e60e8174abb8248d0e5348ea154b6c859afc74a955a36f815c8fabb1ccb449480c83bed293df1e75d60a75df3c6c9199586a249a57b14a5033d1a86848e66ced96e631a15099e7d9384c3fb68a394d366522d96cce69eae127f453c99833eba09891750a53bd348e9ade47dd435d9236a11892da6b9a6514bf4f46a247e687ca3c75619a3a1671751ad22a5dea24da87a72bd9c813261e9d2a24239f913c49a6264c13cbb406c954c59cb30c3d273f53ca04c5449a68e6678a99739a48367178decf0df753847c68a3058e4ae4f1bc1f21312cb1a0f4811211251572c0030c783882072545f02015d18109557af294d5f8205d7a1e2afb224d5655357ea34aef133e41699a0cb58342a15a5bdc3ac5bf50afb21d1e8a3eeb3f38387d272bf96425262952f7f8e0f464252637abbee3fdf8c552258e9f54848654c3849a2ee69c269214c1ac6405b66adfdadfe5aae910f0cd7134637bffbed9f70e04bb777eebbd75b65f7bb59bf23cb13cfd707da06e9a6e94ebdeadd67fd61ea85fcbb8ee4d53ad51253757072aad56f7ccd9ce6d9e622de5799ea744e70a836eaee395cbdb725cedc639a06dbbedeedb5db98fb9d61bb8fb6f1adbebbbce5eeb39313c690073db5afeb9cc59ce71fd3340ed563bf65ba6fe579d3f189a6edce7acee79eb74e6fe179aabd55eacbdbf1bddb4ef05ce9877cef5aae19eabf65d6c98bb9dd332bddf6a3b770c68f963fbb56eeb74c5b8762e2add38ef3fd5eaf75f6f7e018c37e5b8bf69d47e8c37bec5a6f5aef7cad58df37f31b7c0d65d6e739b2dcff76b7e57a0eb2eebde759b7a4efb7a6b517be7b5ed9a739fe3aa3705b65b7fd657cbfb6a8ff39e40d5b96e7a6bdd2eed3eb7b3d0b4ab765b6ca9e5baee6b2c346f2fd7f7e6aff0f9d79d3fd6b8be344be06bbbe66f6bd5fedfb65b91b7e51ed75af3a774e311f81add34bdb5e53cd78dabe27fad6d5e536d77f6ff0d029d6bade6db6d1df65ad5f40376d5eeffdad7f562cf3d15b47fce67df75ade31ae36e8afa3de73a5df3ee38ef9c03feadbac31aee997b9e372934dedd72dffd4a399d6d8ea277f57675f3dae5b6963b14da7e8eb9cd7178fb5b3780fb8cabc6edd6696cb58d01dcce9be2bb396e73bf29b7000ed3cde6cbb5dd32d5fc139ad5b76bddf5fab5ca39b1efa59cfd1977faf38d5380e678dbeef75b7735ec13a055bf754d6f1b676dc75b139e6b4afbcffe7a8db5dd98e8d7775dad795b5f39ef08d836ad55cbb9b61ac6b9e203d0bbb75e3b8dd6ff6be7ef12b5ebbefb1ae6b6a65fc3dc0074735dc3f9666e2fc6fd2ad1fddfefdd2857b1a79527716da759bd374e57def14dbbb250ca6beefc77effb963b5b007bad862bb7f9fe33dd6c2500ce146f9db59bffffda0f00d3df713fffaff99c711780ff96eb32fdb86b4d7f45c2d39eabb6bed28efb8ed294e7851e3c718fe5e479dc3b9d728ea01feb9cb97cef667dc6ba7535ca519ef7d55c77b156539ee779382ad50e16c650deace572ed3aeab9ee6f5ea8bb35f7fee94675eae457ad9f1260e95af75d77be7dacb5e53ae579588246c0fa9dcf95e399df5b69e771caf3b0049dbef6e00907674a13ec8b52e7ba69556bcdb77c31dde4d537578d53ce51bed9afc70e77b6fbd4ea4ed3bae734236ebfde675e39c7b6a394e2803a1c9f5c6fac6cf6db63dbe9b6bb7bbaa53ccf0375df458f6b87bbcffd6eb7cfbaa73c0f28fc8238d0e855a890d3fb6afdf7ef1d776b69caf38850e96ca9f7bdc3dfd68f3b9af24ea0f625790a0259dbf370a6949adc0c01d2cd76bd7ecbf7de78fb41f56f7ef966354733a7b7ad887b35aeedd516d38ed2ff5d7e2bfd9c5fcf3f976baaf7ef7dbd5de53b779562a0acf95a37bde9cdbbd7abfddbe1cabbef946b1bbf9ce7dae631e6f86e8a39bd36e57940e1478e2ceec793e7e178a0ae04e76befa434a5d4a454b2783c6ff441a3567d1fc577b18789e7f92e869ee779d8127d5bdfb79a2ddfb4ea79e63ddcf3bf6dbcd24efb387bad0bdafffabfabb6755f6bf4627ee34463bc55ab79772bf7b5eda988da54abdba7bff24ddb2df35387b76fbfed2aed9ede6d03f031f5fc6abfdbb6aabbaaaff0bb596ee3b9e75c7ba5db8afe5b775bffca5ddb33af4e10d17bd6976a9aeaebf1e5b0f64c0e3004ddf0d6372d67ac379cf9650000789c35ef3bedbcfe7bffaedf44a9c25daf594fedb62fee9f6e2a5fe3baad72ba75dd615af514ad5d5bb9ed9de338eb8ba56878c379e3ee71de7be7fa83b5cb6fd7b0dfeeedfae53e18e38e5ace697d3fe6328d627757b7e69dd51de76c7785e8344a3b4d77ab658bbbbc41e932fe155bee638bfbd71cd013aeeb68ae95578eafef547b1e29f6f09d35df388777d5f6deb694e7b57cd049ab2e8f66adb595ebb6b33eefee9dd08dee0dffeee79b35c7d526fefe4eb7bad99c35cbe51f04e6ece63b677ff59db3bdeff0bf6dcf58db5fc3db6acce4ea6b75fd5cfb94eb6b3110bae2ed625e6fa54bbaddafe6d8d7bd71b8c35d0d92520ecd14259a7fbfe15ab5ddbc63fa4f6292aed5ebedf5ddeb8abbbd35d1d1b86cbbb637dfba8b79f5291c20d6f6493cefc60749d75ebb5bbeb5c3bcbbfbe75c5abfa79f73d9db7aad4e791e0e0e8e58965fb9a312c725dcaf7c3cefe65ae248d6f76e9ad5fcd36c79e6a955abf43c1c1cf18719b8527d379e479ef80f747738736ecb5a47ebb51e07731dd7efa6b5ad55fb2de743adb46a9dbb5a5bee668d1eb9f1ffef9f3df7b196f5cdd9d483c6e1ccafafbf7376d3ae7ee08accf1e8e795d39972ad615dab4e79dec9f33c165185d361babb7d35deb7dfbf559cf2bc535689e209683c9d62b8ad79bbd15ebddfbadcd59bf2bcd3ddbcce9c6b5be35857bf3b9fd2f0c69cdf56e3baaa39a69c4b799e90380299eec5b4dabc3bce739de5e84d79de58929aa671fd6b356b1cfefc679ff22cee3a4ef39fb54dd37ad639e579a71eea93d83dafa77eaf654e679ffbae9cdb5acaf352246b751ac57ff23c9c9c92908ee2794768dee8c6d16da9df72c59a4ef193e7e1785ea9fa54b292a5dae1a6072395d3bad7b0cf7ffbdcce34e5793899b5c38de71a5c9dc2cff3c672454d17e30ed76eebbcf17c3f4f795e397a9ee7f960e3ebd6b69d77b7f9c65d8da7c493e779f8245ce2799e47aec4aec437cef5ce6d5a77a5d77329cfeb3ee481ebbace77dbd37d37aa2f4f791ed9bd274fa507f23c9c8cd43e513c4f25f6508f297d43163991acf17492a1e6069a5fb288d4934f9d84ec209e366c8be830ea92480e31e020c30d415c6f10148b0c9121abc186186a1042c3a7528934426e9861061984c470371a820c8942650d306cd8662f645b4fd60769709f80c22f45ea54e60291162c0e2cd4930e2b5895c84f7e3c69b2737faa411bb62bee498e94e2a5de279ad1e984830a435a65430af5248227a0f0030212c1188068f407111fb614c599aefd4823538a01220253c9222f96bad4b4a86a108d62a98560d9554fa3f6203f9dca217fc39cc10916681082cbab74d8c2020d5230af242a2c21bf95fe7161e1639621842b7266a802838af852409f6f8d3a05e08a1522860040152a53a4fcf8441102ca931e1e274d769800b104538225d1e96192a0a0992428a6b890206ac2c7112c60843be69c971346d8624ea0b0d4e409d4e46822f110c5cc89dd6184205ae0c1c49c981d5811ea3811a18e39efa89203c98e32ea8052879021a0e64908091002969979393a9dec3bf6e6ad6e1144a261c17e302736c61ca420b860ce89cd4102821b62f8a0fb569863474fb246bf62f9a87895337a007e544030a793604e4c8eee9f8060ce9c581c181c21ebcf89f1a07f3b30f0a51f79ce9cd80e3c98821c94e0be31b30eb6dad2e3087230a7d55a7a1c73c839b1366e30e7b4980dc6c0d8a8c19c131bc10c60303113ac317de94b0f0a89e00fa580206bc9d6624f074526fe886f48f3904a92cc89a9b112c7508aead3a0ce90e621bd5f528a3d4a44f167a5c71fafd23de1ce901250fb24624f10179d8452bcd823461a18096430e724b529736268cc39b111c460c89798181d8381f8c31d1064f5cc899d3127660656c60be6c45ce0d3fd8ff85b809180054384c0f0c68f3762486f88e6b45adff9eea682960f9a132383da966f828d312796023dfa2620101dfc523a2d307c3227868272d4ab39b113f46f648150e6c44c00ea2d824078fe89533c4f282e2949ed9d9439e24a1ca1843af5e34791a7ef7c25187dd0178a3c736224984a40cdbf30c5fd0f487e40de09a83ffe75561045b490cc898d60ce3931314410348a4b5a48b44a93ac263df425e9a9885ce42215fea9584afc28a648ad137e222f3f24a41f3d935ac556d1aadc2ab8ca5685ab92ab781e69352c9e67b17471b7340ff75370c41feedc703f651471fa8e4a0c7d57892149921dfca8e0dc703fe5030a594b42d6ef57b69acb51436975114939ea55df697511090e8ed56eac26a5244351272b65d64db69595592d1f743756ffacc6c2b9c10c76404280268014e0062df32ac265c79583eb04170a6c806a9941163692f0c6036cf88109364a73cac42421d5208c0b73c28d420d4629a001cda42285096471c5154f30c1440296c839004e0964ce8fe787f0cb7ab0990e59e8419af930240b3d88d52042cebc722e52127c580207347043164822000594293f084923e5c1bdbf1f215167456616f753c86f95437e2b0d14822bd52703d50402070d374e8006188327cc3949dfc5300641cc3961608249fe94de8704831f676061460ea6150fb898000a7d134b18e1472ca432ee858005ac7c490691a5cffa8f5f3d117b2e3f8abf2423232333136a24ad72fc58590ec9055764416ac11b2416cca1050bb80e488431e7cc0147b1821fa72c0298130915f4a94bf20932a0cc795d91c3185bcc3934469539af4c520a323086cc1cea2139924cf004c904554825b862ce0d5b51e8c70769108912507b9f118a1ebd1e95608c31c6f8de7befbdf75e4dd3344dd3344db3d65a6badb5b6d65a6badb5566aa9a5965a6aa9a5965a6aa9edbaaeebbaaeebba9c73ce39e79c398ee3388ee3386edbb66ddbb66ddb30c618638c31bef7de7befbd57d3344dd3344dd3acb5d65a6badadb5d65a6ba594524a29a5b4765dd7755dd7755dce39e79c73ce1cc7711cc7711cb76ddbb66ddbb66d18638c31c618df7befbdf7deab699aa6699aa669d65a6badb5d6d65a6badb5565abbcc6df86ab6f2a8760010c615a337c23f29e2e8876821117bfc97a4832c910a8f90087a243afe08715c22654e2c89d1a7ca275d2399134b4014b1876c62ce893131278680282dede400d81273622d4d76ae4449a6438e624f9833b23817c39c0fcb972464657ed4feeb5b93a13f625cd27d28a45764066a1f8a3d7362496059e6c40a3027468001cce9cb2c64d5b4f4d6e18e1f5925ebfb9155ee2c558edfce74169a429d812c52fbadc11c13e94b31247b00fac82f72981313c09c1812242ff2c05a437396627845c9103b3ea7244a923cd1e951728af2244a142527254ea42479c273aa92c39313fa20f127b37054bedc0112c1bee3c30f2854a2d345076a68a61260f75717182063ce7d2249bdf2582f431d7eaa6fe4e4d733152bf4a48ad44299c80428fcca51dc3999c96380098624674e724eec08f603ce9c3349f7a3388e4a405dea255c70ab95f6bc9358ae5a23b79a263bf77e6495dd7b9e1faf5cbcdd2df41041b214419fbbb1b6004fab95f6bcd38aec2ac676242dc0c59c33db2a25068b0aa031a705b639a7e79d56e47c42aa800f723495dfceb69ec8d1af409fb21a2bdbcad2028b273a7362181c47255c04c11c0a1401879102b445c810972a5c9e98c004a64f8b34819cc5d0fc59a031a75e196995988655324a06e4c92c55e678de9c98ad59c060c1225de103098056ec60c396891527985c64e9c114655654b1a2ca9c43140101bfdbdf7b8d3790ad46b74eab5df5bf6e5703b8d64dbdd5babd1df7352d035ce695dfadeb2ae578efc0a01de5b68cf5d77867ca7d51b3e6bf6e9953eb8556b9aef24efbddef4e3d4d795e073f9e9beb05a22ebae66fa679d39dd3cfdd8e01dbebf6fffbcedecb2b87bd5f727921e2e2ef6ef7c79b663b8dd7bc892ed037e71dd5b5fb5cee7cbb116db1b5efb3a639eddde6f4f62de0bbff6de557efded58f4515a0be664af7add85fcd7d2f51289712222d6ef7bfd738976be6dd739d88023a6f59cb55e35cb7dc392d806802b9abdc77d577347b7a2fbd80280bdf6d9c7ebfed5d79e7e94e791e0e4b840547b596abdfd86ffcfa0cf60fc7837d670bd11538dbdc716cb55b35ad579af265a881a08824c0b75fb966b9ceebbb33bd271e911599d77cb9e7aeddf75ebc599f892802fe6ab6ebba73b679c39d4e795e19b244204455ecfe29e73bdfb7e6399c77caf390785e8e0802757b8d7f4f7fdf18578d539e17c47deac6835b4f40f400baddeaf1e5bd57bde1ec539e4785d6e1cb51ddfd6a77b7fd3e85c8019dbbbb7b4de3d4675c3d7785480a8b2fc5556bad29b5fef794e779ffc327529e943cefe6c280280aae72dd96e95677a7b97bca43299e2785080a6cb1e6f0b69ed3cdfbe50de81b5bcdfb9637de79de750ca85b67fc75de3ce37a35ba805c2fb75f638aa9866db7454fd00ddbbcedacd1aa77de9d1334ef9dafd72ed679d3db8a14b0f9ac55cafd6e77dddfb62660dbdee78eebbe1aae1eff262cde384ba9ef796b5c7b67226fcaeba6b5adb9dbdda611b0697c6bd8761e6f78d34d1fc0674e7fe57ceb99d3cbfd129b725cfe9ad67ce3faef1b014406a09af39e7fe7bca55d772e5282728db5ec73ffdabd9faba224fcd6b68bbfde1ad6196b5a9485bb9af65ced3ce72fbf1cdd83765973fd6a19ff5c73fe7a7076dbb8ebb7deb7a556dba2d0b7af1dfe1dc73bce62baf3d878de74d5dbffbfb75d3524302878cfabeeb5bb9c6e77dbf7093973fbf34dd36cffb6d909b76b4af5cf9dbf9de51b3701ebab37acd55b2dbf14774ce098f2cd5e7b31b5be6b39e57909c096b03be56a9739bc374dd32e5582ee97569df9e5b876f5e549d8f95fab35c7390e09946ed86bad6dbc76fcdf7b846d37ecb54f79f69af57d3382a651ad6dced67295b3565bc1f0b874d3ad75ba6addaed6da144b756a8d65cb37292981dd613347b58de3be6e95725fb3c3f6bc599bf7d62cbd5eeb45a09bbb9fab1aae5efbfb5622e4acbb9f3b8d77f5f64d731dfb721dc7a9a59c767bdbfa10f096ede538ae657cb7ed382150ddbfe6b7d7aeafb7eb980eceb5e5ecc7db6adb77cc040b82ee68a7755cf5beab5adb0b847d3fd6d4774fbb9cb79b7fb0d19ab3b79c76a915d81c9dbe1cf61a6f186f7e6f36e5793e489755603ef0bcefad76bfbb946f18d31e58cf790e77de59aeefee6f3938bb7d9dadc659cb37bb3b16c7ffbb6fd67afe37ced6ee869f1ebbe7e190439aa7d42a572a25e193520fbf26a592b5b5e59b942a93d20d053038369b3fb6b8dbb2d670772f0f3aaba9fed66bfb7715777a5a613be0566faa35dd77f397db68caf370809852b24d4ab5c9cd4af3ec95568d62a9a9c0dee8bca5f95e6cf5a6b9d73fe5790318801f59a4087224908002d341cd38775bed2abf54578d4be1e0dcf89dd2cf4d1017412406c0727035ac75ea39d7aea69d0e180efcb79a7395de7b3bede486077383fbf85fdee1bfd94bf3e7529e772249bd6aed53f7a114cf93026b83765bb6d6679ea9e778c7a53c0fe789e79141a298ba98c06e4029bef96ededd7bbfa5db06b5f31ddeb66d676bbbd55acaf37c3036f0f5b9e3dfeba5b6dfee754a55b01a686f7def36ea350db0a669d7ee6d63fab14667b0755cee34e76d6b9f7b4e636b6c9cc75dd7374fb57a35cf5df921e20753c3ef6e57dbbbbaafa637de4ea5072a3d103f373703c0d2e8b6fd1abe577bfb2ba7bb94e7e194fc37fed474b18747f13c3e32f1bc12f7537a3c8fe6a60459666032f8b7fe6a79d69b4f799ee739b969f9264892ecbefb4d11181a9da71f5b6fb56dd36eb63a06b6ffcaf1d5bae3f7f6ae9e301868bcde2de36ef3bdc39de233b0b79ddfbaf5e6eeeda84de1f85004753ccf8722c8936066687b535dbfde7ac6d6d37c042be377dcc595db9bbede73cebd60e36dff2df36df3b7b5e39f8bb9c0677d379cf3e632ee36bda53c0f87076b81ff96fbdcfffaef4d2fad4c3016e44ee37c71ef96ab5bf32bc05fa3bf52ce76ccf1b55f059b77dd754ab5af755ab69e8cbd3b6bb9bcb9dde59e6d3786ed7adf36e57ad71aa7bd4d01bf5eeb75efbeb5fb35e550e0b96f37ce751de6bf5ebb9da073fe79dd9ad33cfd5b9b605f8babb673d52aed1a530d2b01d6eed629f69ca75ccb5a2501ee9a770d57de3147f39647c06d9cb675eefe4df1ee3ec4c4f097abbbfb8dcbfdeeffbd08acdf9dd62cd6eeded766eec342a0efe678632e73bbfd5c3508eae6b5faadaffe033467ee725df59afdf75a0f74eab1cfbbebadd3cbd5ed631da09cb7fa7676736a3bd77f185be61d972fed7f6b6eb31d10c601aa6fde5acf9aab45601bf05dde99abdd72b6b3b9761aa0bb6edad596e76e6d573d96018d7676ab396fbd718cef0606de368f7db69dd2cc558c352bd81759c39ffedfd96dbb7cab3b857971efd6aebd7dfb5bdef6df5ddc5e35b7dd46bb8d67ac61cd080c03fc6b1f6f3ffffbb7ae518b71c13daf9cb35ddf3ab697ff0b605b6016b8daed6cae9f7bcd73dca529ef46258e4b44ac02bc7a2d771aa7654bb5bd3b4dc3b4d874dbb5c3d5d72e775cfb1da3c0a798db388d3bbeedcf7f37816c2ba7b7cdd4f39bb77dc447faf1e479383937a30fc31d1d2c8b6fbfd63c7358d368b75d4df9f1088645b6bd6e956ff7f78e56cda63c8ff4e0d63f427af4bc1fb02bf4b63fd7fd4ddb55e31673563009fceaa986f7e77cf794f69e3a919acca98259b171bd37cf986bbcfb9bde11b09c6f98d3eed568eeaacafb60557054777be3f0eeb45f37cda520d0b3e65957efb9e56cee78caf3a2600fb8b6b3ddeffcb5d7eaaa75258b878aada3f4ef7c358cb9dfb74e79de0f3605efb65bbbada3bc7297d31ea8c4e4e6fb2848923029214972f3047340ad75539ef1a631a7719d4f799e141cd57b57eede4d3b7bff8f8256ca599cb7be73bf5ae7a1a85c7badfaafe58dc33dd70670ac3be5f8ede8c633ffb4620cd0b6c63fe7dbfd596bddf205f0dc6ddf94eb4d6f7d7509face03f604b5997376c35aae1cc5177322f7ecbfd739ebaf3de7bf0278ad97d7bcfb46eba6dd8625c072d477cfbbcaddbc39ce86dfa9354ab911cb70a735fe78373e604d684ff3ee5daf9ae7b6bb0d89483caffb2171634ce05d31e69bfa0dffad6fee9eeb954fcb3711398121c0fe8e53ada33ffbee390e8e4f71ade3bff107d45c74e279a51b2548fc37fe785ef8a5386603ec00dd6a8eabfb725ac555d39dcaac966f922235931292243c9eb7fbe9f2025b8252ee763cf7ed77ca6fd506f81cafbf737d3bd62ab55785b30427e7a664f1844b52981238fb8eb3ee32e7aea51b4e79de4a1cfbc9ab58383f37523caffcc22f649d3c4fe55558129ab61b87e9edbbc3b2786edbce739ef76e57ecefcf0e37f4dae3776b33ddb42e576ab98dd343d7ccf9d6afa6dc674de328703963ae7faaadb6d5fa9b47a6b467fee9c58102e53ef6d676cd73de6b1c4f791ece13fc763f57b76e779cf51c4e94cb099bbf57e78a29f7b7fdf94de15c4db0386b8d76dbd66eef7e47c38b095dd5b56a165f7fefbfd92e81db7f3b7cbbafb6e3f0d68db894f0abdff2e796f6ab59af9390a9a7b8f7de65ed7a6d39122eb771b7decd6e8eea4bf31138fd9ee3a8a659df29cdd808de733affcd657af9a698065d7854dd31ed36eaebdeb8ebfd0ebc31ad5a57f9e7f8c6a91dbf6a1a774af7ef9ca55d2e82ed30ce7bf35873adf31d035d44e03a0d6b1ac5b4ebb5e28efbae3a2cddba4b379cbbbd31d7f121741bb59cdb7f6ba59c56b710ba5e7de5e8c69dffbd6e747cbd6fd7a9c637bd659c73396a28a92b085def5bbb4eebdef34eff0f84ae6b8dffdb37deb6bffff603aaabe62cee70b79bf67d9fe3775cc76d9affbaeb1bafd45f3eb0f8eacdb9cdaffdbdf2ffaf1e50dc7dd5386f9ab76ed3bc1c5af55affba5fce5a4e638e5f71ecee7fbe37ef3cf75977160edd759db51967bea9af1db572f1c0e6bb2ddf98dbecc6354e717a6e804427d712d70e28b7dddddca79bc3dacd3ce579254b64c1f5c6e6bf724bb3d7349ef5fd29cf0bbfd30a2e1dd0ba2fc7b7ae568ed334de73c0d94db7bdb94abb7bfdff38f05af7f862df617b39cdbb1bddd6a9aeb8d33adb8edbdd46b75bb7fdafb5edf96a7f6f40bbcd5a6ee3bacf6d8deb6d836e39dd618d6aef77de572bc0c506a5f5f66df5b6d6dedc710d8d52a242d0802bd577f2e08a559e0470d500dbbdb9fd9addf6b7dbe634c0fbf34b6ba7dd7cfdbe3d04f2e29a41c75b6f4eb56e35e67cb6a51bcf0be35a83f22e634d71df72f5fbea94e7e1e03cb9f13c5e73355b4b54890757ac1f9a9bab02971a1dd538f61f63aaed9ded8fe24a63b3b6dbdcb6ddce9af31b7b0ab864c0fd8ed3cb39ecadd6f776539e6784f73c1c239480fa2b572aee79374e2e342ab7e1bbefa75ffb9caf3be579dec82243259ef7e48a01a76d9cfef5ebcad57baf96f23c065c30e0bf57ce6b9cd732b6d93bb9ced09de53edd78ebca378dea94e7b5401c0f6eadc77248490904b54ee9c6f32e272e3330d670c7bdd61cddb0fd39e579655c65749cf377dbfc72babb9da6a53c0f47a75496469ed24d4e8b7bdecd65c6f5829fefbfd66afc374a3ddf29cff3bc29970bb6b675efb2b63c739edadf826d538edbb673dbe27a716e0470b1c06aeb2be7bde77ab7fd9e7d4a3f375705ae1558aa359f3b0de7edd7dedd6b894b059ebb1c77f3d5fa7675e59c8ccd595db5b758db9dadf88eb1354cb98ab9f59ab6d1bd53402dcd9fbbd8e79febfdf94241a6fbfe9a6f673bd7f73e41fff877f66bd67adf3777fb6502fa2fc76dddafdbface7bed4a4fd6c30f11374dae1274da79ae6b4d5bda3b4a3b0938abd18fbb6e6b7e3b9bf108e8e73ffffe351583e37b63afbb8db3fd733819a9b32a37415c22b0dbc605a097a854f1a8b6a19ac41432860040b023092000a311002038241c0c060432b1744a8f1b001400025daa6aae4c174aa46194a228061943083106000008208600620c4355666cb89d37f738b6ea1d3775e3de9acaf18e9dbc616bf623a24513d70f142b740dcec28d9d6187c8b3d5c996985062981a0642278a6e6e23d27fef71632a1b959d2a80ae1abad2ad54d8dbc10778bfa67028ccbb86809ff11f9797a154eb2d18af94c0f7499a2a4474ef48a8d406ef0ce3c6b4b985e0441f40dd2a259078d08211c6b6031dabeada20110e5393eb53497995b28157f24828d7d5dd94d2374607b8fbd4cef455a818bb8b224817a0191fc645c4e45318c98e863ef83367ae8f7f7985e6e18e8de832d8c7f7b836b419c26047408887a0d62ec49fb9214c9e2c0cb72caa78007af0af89a09cc6c5aff4a64d556bfb9f6b86139c20b26c16cd573873507a74d1bf906192d9763d42b133e4bca128f7a495b64534f1e73b51b87d7a4d046d9e88e90776060024eaf21d628ab33402fa1019ffbd14367466732d79ad7f9795755a76c98ea3584a51b03382513289c6db31384a2ef37d95cfd58eb0ac918ae82d2b7145b79e9b805ae1a4974ca3df3e2ec113e822e8c4c01241b78da0db41d07a128273ed4636b4b2ed95bd634ab4dd0dd588cccd23d0b450972a74451ffa2f4842b91be8b906e5ae27c205329d19b50709b98a22079b11a81594c8063d57cc50ae7d172221162b6f2c0cfdaf88a1dc6d3668bb8ca34409377487459468a27f72995ee13ec6aea9f086bb0f0763a38c7f86fc7e2e9d33e62235ed0bad41bf71896ffbd2f9142d7115a63f7fd3e47215a1d63b14ec6c53fda168a6ce73e47f1b52a83ed765a9a0583df4e18ecb14b910f256a7f3ff8ddeffe326265936641cbd4ad375973e8d8691df3f3c46a5985c22a183dc545dfa52e19e34a3a45ffdd7aa6a0a05f1b27cfb62fdd57e418131f5580f939b5f440f1334c1379d3c92fe294c811c8ef3f2f5fd1a0f16d869c67ed5c15173230aa4ad6b5dc3113c9e1827f84185f0913e5d092ea02bcfa29d35323544b31272534fca08b7b9f14067aa110f11595fc1cde0e8032beeb05cd1bf3c3e27bcbbcd615c2caf0093ac570366a29039455c0db34420ec444613c6fb0ef4e088656bffccdbea9728a506d7c583b07861c9deeff0362ff5adede0f846328ea98140977364210fb6dc45c86d6fd8e375397097b8faf8af8669e54d2995252bd7385a0fc4ae6715d24a3a3d74c80e26353169829d7057f4ed68abe44dba6e37331a6c04d4097a9ace414113e57255d5888a6fe4b19ff579e212337a7b03864a238ff92202a2a1c2552b6aaf95afd6118bc624a14774b5a384d459438908404c160ff37ba2f58d9bc52cb187af1fdfbaf125357b81ae032a59208fd0d344221ce8d88026772485ec300f19233418d0945992a1cb1b0ef9288cee255a07860c33f2e0649965c071d7f2d19044b5a2c4b147a2be488358713bff86f547c08a4d239b619866009fdcbacce3cc31665df5f6e4d1c1bff94f83c666f173b7cb80325b7cc8584c7e1cc7d52ee7ebc2b5ea72da5f77b99d9fa369fda076fb249810c264c313c969c69cc94355d899a6698a2d38a36b8aa8c064c041bdf78d0607de228eedb1812e66df22ab54353d959a553fe3e2d658e2567dbf4d9ed1d0f8ad9d22040c78f1718c8dae127889ec20868582c1d667a31a0b456b16d119254b66adce6e26044a1143e58a9a09a79eadba33e5e87ef466c2863f8c18206c54a2d50b624417ac4485aa254ad98743a59c7927ca3f580a4c6918d0deafd1505aa69b1d0c6b3b387fe34e327d83d349f4a6f100bf4b36f1543faa5accb8826e2eac63562f79f8dde97539462e9ac1ac66bfc55726441a580c231ec44bf28705482cc0e819a71568b7f81df2a6bf9f22632ae9d78fd8f382830cb5e24807dd752ff8ecd05c2cc544d2037b5c3856178025063e86c718eb4cfcc5610016195dc0ef2ae3c2e1d2f2bfe2b266ac0a136f30c315698c7d3da0ac2065256d5b69aa05334306109c548879ecfff8ba6113edb7d4c46d96c75b723cebd6f1f2e40574d8f041a928e7dddb7617b4f445ac24078d6011a3f22b6c19f86e206b04e0985c4ec6f41058f17ed725bd9e84d934e55740bf3ceba5e196340910e8521a25df1fe2432391388b5f01782dca4e545cb930527b2c831bfb6dab1fd5ce18e920bb3572df4864e2164d4d65a3c70a7187b8569ed27a34106afa4686e9674ad0e514642811bb3d7668dce1b299590cefc6740a9c4ccc2291f52c5ac45ccbfb5c367a57a968b1e6d4fbc4254d4499db7bf852712dabd3f3e3c9194c070041fdb035cdb094273d7f48df63a30dacf7d6879d9870551664b4eafb4a76d3b9a4ed6a0125a2d56413882142b16ebbcb7275bdf22d27d2cbfdcbf38e825d5864bc17a8df1e0d7ad2604e0435516107a4542140edba54ef813b3d66d1224b0c6e7536507ea4365c05ccb678966b2d054b95da6259dd7cdeb44c80c7d686d1f0250366093d96736e2eb1f9e9643fa13d10402f3bad554e0800403999b8a50d5a34235669ef77bcd7c592a0bada1c4fc83867927c88cb2a9f8fc0a4e0f6443ae7ba320844be789764a1edaa92c38734f169655e47c8c3badd909b385cf4144af20a3e2177024b1558a5a4b83811cbdc569a953d0f9a844946af28cbaa1dffb023ca8d2eec5ee7f0674ed3f111bc155d82bb4973fbb34237cfe1f76a95943e259359aab4b74a0e5cd75385bd7d613f95fbbe12084c150cbaf75de1ddd89207553fb5211062fd73b4240d8ffc9831902c0bdcf87f550d0cd479eca3a0250a508f60e21df38ac88fd1ba7a4e87dfa06e94b91f544694b072829a9b61d59b4443472011a30bd141466332a0621697317a5524da29a03da3bedea2ae72a9c09169eaef0d875b128438f162a8d0b2fef3eec889f0a871c7b2cb1f2246717ed885a8cd480268f763098c8873441af72db0007c8474913dfa265c91142fbe463bfd5da5953b79111cc0a04ae0ef28644d0c87bdfdcbdc9d911fcebb8c29a1bd77428a545af52bb277e8b0643e864ea2b7540d562689c0c6a14b0b803fc739aa41f08aaead517ababb053c0441757016366e1d3066c786de60cd8bf79ce749c377341c81159d42f79b237953d18404e4ce0f2ba7e8420495fe4a523a71802ed5ca8555097becc2ca25003a620b140aa8bd90a3f2c50f9e3a79427f2919f6aacb0603f5d5748016c8a24bc5853f384c6568b6493dd61fd1220255994bfd2f5e80b6e5d06c7180b644123f344beac498688a435b191c5a6596352d44f548c071687ea1601eebf7f103742048536c719e203a8c72c3a555a3fbbd2603bd4564ea9b691f1676e97124ebe43b228ae2757bab45f88125b990a6b8283e0f12b83f04dc152b191239de961290f22be1bd8dfa4a68c0c6f74ad4b7210125b2b7615f890ad8c85e09fb361a5042f736ee2b51012be6e6c6eefee094e076f2fe48f99c2470c47b6eea1fd00037df1fa8cf8904ce64cf81fd03a9c04ed99fb1cf410307a2fb9dee1f48033bc9fe4c7c0e1c38537b2ee29f70808bee0f289f9b83030f4f894c3964205b4640890794cf0d189c7e1cd00d64891f729a7e1535e806f4c84b3e6c3c2fe25505db14b8e12818cdcea3d8fe16ac278bd9427129a08aa30a732e6082b0cad4e1aea05cfcaae67eee1ba9d2fd5339d43a3c624c472a0d3e45648501eaf1cdb085357a798166e6434f112fab672fd54db9144b3d6f371b208bffb19d7f23e96d75da9b4e79bc6f06475b15a4bf9740aa4d2c516e432a03311a3a61fe64f1335e90f48aed9401be80860f0b7273561153b0e431e799337fdfa7fb36994bcfa3476f4af7cd9f576f320f3d1fd215f6be89d5d2baca97f42dda14fa7e8b8d2e6bd40215b0da799be5ad8d6b1fe657b2621c167aa19f3118e103b94f697c4f6621311f84c89e9e1f8efd7a794d94de0195d4e2470d7c26d5d2d06c96c27af23cba4f1f4ced3a7e37951f29ce39b77748438b451b57908895e3eb2e34a371069ab60754728532317db5fe563c7d4d0438caee02fd2fa7c2ada0d3eed189814ee98cb95de272944ee680abd53907138f3d05ba013d8f9586a6c6d5354f0d28ebc00f1fb84dbabfa005540d03bfee369e113365e19f0a4473e7d5583939441cb844e3a429d1f9d35e1ce774bce70b9e6c0bf4446fb6e0bc40e2694114b65f8119d49e47222c109ec1c9f0bcda1a1931e26717df4b612c8ad9e990235607dfa0c1ca5488fbc371c57369d27f55171b627e8a9ddcc8d800dc596085ca67bdf98ae1b1e4d02af619d5dbeea3f4bf99aa6988c13c9afe309c68aabb12917e4053d11376db3e584d3545c5403018804f81313e4cd7e14d842328828f537c79edb87d51f790ed96f388030295e26d7f406610a0514df8a175ec3965d9104b91b0e70395cbff63a9de69986e34484c5c007a54f7913213ffc26015ab8893819f7805d95697912ffa4c060cd19d5e77c9fb16ca357e29fbbb03835a9a6f4da078cf30cd76d858d27b13fc0102cfbd5d958b5c81c35e27d61eaa42e815d2924c88cfa5e43844ba0d07cc47991b459dd6347dfcac20f18108f263d295a04225db3346643069fcc0478c2d523142c7a5baa0cb06afd79b8511004150db9d4e0548bd2ca39f016007162e494dd25064712fbe7c28b47880580b0055a0071206789d09a78f02647bd8e606c3e509a9d833e599116608952400f1c0e382e9e51a176137b96cdba00d8e26152395878d0a4e5abf71cbe357636226d46e68ccab1d4bf8e1325fc3574a0232c1a6c48315f54c375b51808a507071e455b3266556c5402cca1a09c6cae9180c49915d013193a9dbf4e811cd765a3a737a84ac53a740f30faeff85809e4bcdb14b62fc472dd6183e9a1ff96a170a0ac002cebbb08c7909602478bd0c3ac2d24656c7ff7e4007b2346a8adda7c04b3663ec6c3fc60eb9b642a3df0db5898b01c09ff6acc7ad71272240922590dda15960d644a1581d8d04eff34bb0a4ff3c1195b9717083fe79450b015fc9fea74d20638462b1989a0f0a481589d37ad37728a0d62545a5cddca08c94acf4d5f6f20639d27252685ccb0bb478196dc923d57f72a6159d983e728fd21096cfd6541dd972b2604ee4c7ff50344f21dc0e6ede909fc5c17d667e7f083420be310ef3b004a458327b918660f8a1e04061df8c085cf652dfb49d27eefb96024d8e8a1ae2a6e9a81ff6e1175f1f62474c3f92c176134c0a7bc0e374156d69cf4727c73d0998768f2a4e9f8ba00f5ff3d34bed592b1c4549e8a8e5964de52062dd0a01e625e56524ef0d32547e6870e09b4ac22759c94c9fa0d5c6abb61e2cc7edf1946e2edbf708eaec2f1ca8df553f09db8d5e6db956a660dcdd0190e8649a4fc6b0a6eff8adc1dc3b729b4206df56f926de846b393d93a6f76a6d96d223ec80ad3043fe465b14c51f4012b9d91b892bde33dfb7dbbc36ccd1daaecee687ac7c78f775161768a87f56d8f711705c860e90b42bdabf925f80d21b08256dbfc19fd319167339c87aa0d10b5a5d64490ec4d7f79e1bcd36ca0af4fc4bde0654c6df39aebbca0b56d055b6ee614abb249858ebbc53566362d96b6e832da4f082ec5610364e550b1d48db5d6c8e457e818dbbf658d92de4f37c89bfe3b4e3e456a3cdf57f8de759d703da6a9a210b02163e9763908363f30327c29e6396521979f871d8c39f82d27b3024fd41825e053880f15f375e402dc549be79358befdd6ea49f1a8431f79da3558073d2c77e27f6d0d5bdf4e278d863171dfcbd9539787279f62e0f774ebe084062c3914fa21424a705602e6623daf57222763aa593c74c8ba604abb547c73a2e80e918dc44364a6fb6a140f67f9eefc1455f6a8d6fe6816bbdc61bca65ddb678785d821d24ad9a78f6b1f08d92715d2c464f4f44accb5f57c5b8fb4d668758e7a67d6fd5f7bc21b90ba22407dce4ca9500baf19749605702410a0d4d885bcdad87581afa9688dccf80a8e24f89e8082848902f620f73c1dfef2262000b1dc4b8ae819bbaa85108ea84980e42612272841f0d54fd0352c7caa1f38d12a7fe952d71ee6482e0aab2643f46d284a048924b830245d97062bb9b3d3c346ee560363d23c7e1eaea8e74184cd2f19d6c23249ae88d93894003d347eee077e99e5ed4c1b5eeb89c9b9433ae4b712271c6d63f3023986ffc8e8e60e25cec683b74fc43452022cf4d347b6e2e6f2c0c04654580cbe578f0a7e6cbfdf0ae9bef236b449f2c49be569de17fae2d11220cd2cb2e8ae9c5d2e1f714199ba1194606f1e63f3cbe15f14b9ff3537bb4d54693fab8b611efbcdf2b523bb9c67e038596383f6e9313ef5a6a92a330c86c3db3f6979b7447c2e77f254dffbcf0b0e36c1537ef21d61d40886deb4e1ed250a41e0723e5a46a9c9089a7c6c63bb9d475d641126aa073e42077021b36d46074ebb180b4139b46602fb1bad13fa4ebde43eb108af684f8aa83ad5ce5bb34449e8e707a2533400e55d33bf407e1ac61e5a15ac16e6fb223be8aec27b48e6c561d57dddb3fb644286f7f6907b241377905734d8985f59bbcc50afcc9c3ff6d079c5af26e403e95aad7e5c3528ad0d0f002d7d3287f3624de515eb77b5eb43966678326fe289ea4850d0d3f15c825dc7c4381b3d2860c2b1c3a783fb5df30e987f9f8a8032a59fe4acdaaedb4e6167b7d14e15f3d0c74311094cc103f7fa7bca76563249c3527cc7d7505430016e5b968af1e74777c30d4a6cfdd4131a410d4edcacd6331c87fcad291c5919971992341e4df9a5d3bad2ae3e429f7387436f3085b04762a447416a79dd1a277625609cf33fa2a80393d23dd5d757becf3cedb53e73f5a73056746004f37be512ec924e3658742bf135f1e686ad82963768c721d5d379d6b0cfe410f71fb83363b79549a79d362fb6702321832446aa3c879435776e01aa9ec0ec7113a63d19e26a065c7b6da3fd62906476c6dc7a63719b5ccd2dcbe6d08c849646715d0fc1a296c2ef28adf299f4d76eb94804577d44365521ceee39fa12e3ff1bafc1feabf14739d291c4114b9742951fdfa3a1f8ba2ce1d0ca4efb730158e956a24b8e1f822b2fd4bae701f43f1917183151e2d68895767de8c631c53155873e8e4ac391e1a0de2d1ad40578617e6f42c7afaf0e2f842a00da801543e2cd0662d71fc73f51d79fec4b9dde7fbbc43918daca5babd2c2df50f99ad16e36425cf1dd2302b7bbbb2a86bb1c78c30e70d080d183e47c72b272b5b32f1358413d1b95e568947bdda521656dcc43f1ab5570dd23e132d6c34ca6f4fcd62773fad268dd6ef8f007e00da5faf71896493301d5aa8b12fc33ea15d7f1185dcafb31c51ca3ca6386f0623ecbb9828f7a90d1c05bf522a3e31c5c318329d671fa3642317bea6ab8644c69c595fa29adf014c1eef18658d19aaf26a3f05b30aa760374c8ed13bcb2192639884e8eb63c14735664b1a0f614a0c7d0e61acb866200130f792cf2e1576e823a84cd2945590d2004cc2e720f327dbb8f1a7ab0efad3d342a7415fd38023aa12513ce0e2e385acd5035e4176e71b7e726436d76faab28b606a8dce1d4773833fbd641eac0d8377a7ed4ea475d8df7732ae68a8d450b13c9d84cec8b48377bf1b1d99a63d48d727978c6f604602b12a98e99289957ec5542b625723596be2c2775976a6b95dc84fa803933eefbb2d1252956d0675fbbcc1720ae2300445c4b248a4bab6fd696a91f32063a11bc05f1c5f801328b6e2d9047808150c87f3f20acd394a17a1bdab0c903fc65b9f6e1df63aaf63076b71e9b0f8687e2452462de76f1dd62a26945c22293291f0e04e3eddc6f022b49b53c6ec82ba31328d728b1a748a8f0b51ddac8bb2689091fdff4fdf5009ee304b84f9a6b6f1e221ff7e396d757a8c863a06ff2b3b31aec6deedc51aa3108d1bcdfce28fa7729a7178f115bf9b24f2e833e61f3ace77dcec376f03e60b611b0c20ca728723d07a6c7ec41f06a7dd7ee5922d5a13a9dec53687e8fbeafaed897d39a707d50de1d60dd69a55708c91a3b1b5fee1e9638e5a748a39c7743547a689c419a5ffec9cbda1371d77d69734b142a1f820f9cd4ed73f77ceff8bc4f8741399dc734adda5b4cc21d4993330233b651b39882af625033b67fda65374af77466fca3438f26d60d2a5a89e986570dbd55f2d1cc6339f4091b0e9e304ef4c1c4ce9024ae2fc9e7cca6b9126887409c21a5f11616f524881acb5d0e882f3e4338c7f3e873573bb8bab3a53556a704b1d994caaaafd5f1020904ba9b9fe1b4a3cea9137d57849084b2d8adef6971160a94c5ed3023572bf7cc9e6b15d41815c5456fedd454612c7ea125a0a283ad22a6c724f1d49466a95bdde1020ca4fada1d62b7a1c151134d57a4d0ea7329afef6970860554c54d7e22a3f3a09896a5b7fc040212d577be54f98a828c76fabe2971222a5182d9bdfa340a59bbe42831b947424b844d6122f6e68b7e1a90dd76c08d850bf86ca1a7ed5905103f0515d653eed132701435545eabc471b8f8a881ba9f12a1918aaa3d5abfda544e0a89e749d16577341216932355a7e0a0bbdb4b9b4a9fc691c1414c7d2a2da7f2820562a56eb4d6fa1815eef341534be850252795254ddd72be4486549a5f14ed39730441e80c87a46e220daf896a1f93260403c36d042f94866c5d4572ddcbc3242e5552fa79fbee7c2e883905dda5d85b3ffccfe26421269900a2cac94160b254e5134495fdd77a88bbd0dfbed634856ba036bb8c1f8b4a6bc76bdf566b02f0239e2970b9c8a02fab2ca31caff9cab6217687f1911933883714ac9bbd113a40bbe4bda505bbfcdd785302af3ee1fa08b20048eb9130bd67bab8e377c53a1e4b1cc3bfa1a1ba0d60cb7599b80b25cd7272ea9cba90e71bcd86197c9366493eec83e64598cd5352605b10651d0f48080c1ca7571374586659abd5c632377e2d430562df83596c72105c51e9d515c4eae3bd32dc26162fa364456a96dd09b442c6dbef9b3c01c005b905c15ef39e01d7d876ecec94b1b1946381009d068ba4e4a4972b590156e0248091f0d30d7b2060ef0d4e005a072bef5bf7285c48170bcf0d11ae2c1eed6608451c3d237efb1f402bf925b0cfb5634e2f95da94b74c95980c848f99e584d211674ee411a02f68e9fd2283e21154f792e466119bafd58438c60d1005be889adf1719c63180604c80ac17b2ac059fb9c61bd3434a8f4e1e661c908632bc5673a83fab3d73a23083b8934af8c78794aea081d712852220a42b7984797c7332af5fcdffd34707db78b928fc4dd0bd24e2cf7412240f654551a194c816b3c2883ea0ae153a0283c49490660baf1c055799de7ca159a22097c088805d9c2dc3d8224ec6f05be24a58bbf7ab5609f39d5ae4d2ba591fb885829be5e5029bcd40157e3c75c275bc8a022e5b1f372399fc39653f9a6a4c47768ae86e7659bcece9134238c31979cb8bfe7be4a1fe86d14423fd814d26139e48dbfaa8ac5110b85d74da014b3c2bbe974ec1d925c0cf337e7e583eed150423c2dde9b141cf3320b0468ff4c515d451a7970b5886dd9247c7efc3f5c1166ffedd7d49e7af74bcf12a85277d5e8bcbd3e8f520ca218a1d66524178f7b2430a9df80597866d1c3831d34bb9c10ce849b0d84910ab3c44f5d09503ddfa3080d23dbfe16e006e8f245db97cb40dfbaabf56cf1444e9ca3a96b0b94ad6a76c7986d92dd07ae4983d06b6893e1656114d7e003279628429f610a5c18cccfd91f264de837114027dad7a9e4fe99125ad0bd3da9a992e13cc6ca235994952bb47864a221b8de6db44e2f1f037ada9ec62c9632f0df40961584af05adb685bd3d3e57d81b639739a7036ada4f7903ed014f3784184901fdddb8f193fe6a4aa289ed7128b5792b428c6a8bc80e0eae7e8512bc77a90701f48101fd389e3fc2e8307485bd0f00b403eb2fcdd17b92da473c44b539a597582f5ed26024ba674ee6d38c946657b13fc44c1f513803f7241962e7d1d3a85fcfacd5474e2f29f94b62da78f947655379ef28be266b2e6bbfff2302f43286baf219e691ba6b4bb939545f6b2217d832fd38e99f3242fff694b1e0dedc476acddfc8acf6f22871bdfa6fe532d3b49d9b8a88291502a211a32b8dc4b969f09cf53d9d7283f21a5523df59f8ada98df9a67b40e3e7e13c8dd9ceb42a56f2bcd298ca148257eec774edf0fade827d7ae02eefe26873d1e6dce62a5209f73c441799a5ecf57ba724a5d196224b3055fc444c4a66d0ea7c1d3aa1875197294781c928951db0e233d2111b36af585663ae60bc12514e779491e72deb2f7d1bc0a237eceffd839fd691fdc7e1f57cd7b51b4b03678424c334346acfd370a0e6e2caf8dd3fe791e4e19b41f64b297eb2e96f790c7721be4962954536684c01cbc14fe4106e95d0a9908ee9ee3e6c513ea3398c1d2a4e485208c3fb4e401fdc5246f3e9a7ca6e98ad2e101c7b9162c276d0d0cab14e31f32e0eb03fc1ca9d8514fda127e6529d3cff89b80ea67e1928b32239ecc733557172b3d361a2b86c9be03e8451d0fabb65e9f7599cf0ed1d81be65c4ce5a66b3f98083aee944515811dad4403a2e90f4daba871db9470d33d4960219e247ef690ec2d5786cc71e9b13c73644f6d909b82677519c933779a0c9c1fb1920ae9ba630c1f679caa120022546d5aaeec45695b35f6b99342b8bfff7929033ca6dcbcb2dbaabe447d4f84d8ba8d1de496d1e2717ff736c8f271028d9ad2d49630849c350cfd87e6b41dfddd668ed8d3b046de2c8d27c8b520b7cf1c424711f5d8bc4664f01888100443ea9e33aea6c60387d8e652f5ed912ddfdb75d5abcb0235e25c8711aad855a6446aea66edd90b398b869150db0b5ec162f4480d6af25fbd5cd2adf981c64aa5e9f59fbfbd51ec679e3150a8d3bf7b89de247313b71834b95b6cc1357b64de867e22112dc04e5eaa5986dd2787ac9a7705a4198a6b8eebbb877d173f7323faba5b3f22278fe0a1d3f83ab0d611b8932fe8223fdc416c42c7c82644a9595281ee13aaca339e61f2276bcda02994614317a0a1ca6a3214c90381f85144729d6734a81f77b34e1885f2aceb3fbeb718c3b435243876571571b2ddb3f31562c6ee7afa6e8700649eaaa9738d7de2fa25484deacf6c38d7af0e3786323c2a3e8a4a2c1be129de32872dfccf3b0e1384c53755f6cc21df08ed0653055b7b809699021a03f842124474836b3f546ab4bf109d733c3775bc04c000331abef32cfe742340478bac35ee678c7ca42162ec646b943ec0b7577fb96bd74a49f60f1d1f40ff6723e0dc3030f972d81f718ef2ac59de14cb13a41a1d2d8709a1363707fac7de34a83bc15f7f465b1b596c2cd576297c2ff1ffaade83ea73f09083f4b9dd13112b0fbfcf036a280b49ad973ea8b3ea932c43394f2a0b3deca4241283904d0b26ea10781d7865d4044405b2888a4821319a2106e5b0e7de741d41196fa09ef36be36717e0c3f0f00ad8af097f1b5924472f1b69d365986db4263afaad60a1ae004981610ae6d9885c9d8113e211b0600d589297f52132f37c592a3aa3107ec33cfd72f7f0c2a80f22c1fb1cbfb783a9417fa99641e8d07961a709679fb4fe2bc482a00fe7002cc2d6a195b14545c8a7a6b7224d54b5eb063ccd543683687e0db18fd96d44236973ab3469ad60d136a2902e9d90d1202b1c5f67cd7ffe62067ea03f8ce282665066ceb410b11707dc8c06a2265d2b62b43e6f6ac697fee9a046c7fd2aca71bb73b2654b350521788ebd082b401fb1c44db144a084a7710bd001dd0a26ee34b18a6a6ec51b13b1360c08f696155e0cfc9534b3261e71febfdb86d59eddbc2db415d041fba15516e953a5c9ae7fc40e546c496eb277d6c92b5241dc370aa64cc5214ea258424839b5bf3a33b659045bb411cef180d68c1ec319d5f6224e6e869fbaad073a8e9af6debf87b57dddf929c381f856dead51b0eef61e307cc05a9c4f9f78e7e299794b78e3bbf6585784fc70ffcf6924f90196b3361e560b76799ef11bcd504b4a761467c057c6f554fc47edea045ecb06daa872ac984d626153bd61cd25df605e5423a8874493a263af03c99347261491ffeeac6ed97d97eb84cf2c24022c029aed11c93fdbc97e7d441a65659b4a5fa8d875ac5d258cf8117b76d5f0ec5bc09abe750e809cd059c0b3d2f32a6ca4184872e0fcf68d8df4628815c2ad1f245d4ba70e77fc3f8398457ecdca9d1f600d79bdf5f7e495f1f0c5051fa9dba5791c271533fd3cfd7b119bbfbda263d5182fa0d5bf7fa2d3b35abac21352d60d03343227fc12fb46e1a38a9a137906d3329829d09d13b5bdb35b915053bb33d81bd6cdeeda811dbe6d8b4bc3dcaf69b5c6e862cb335a756aeb63181e55fd8c6be4aeb2decbe2ae1f4972af7014d6ea4178efc2e438ffabf0fa18f5e28290b103b954e0419f042181a97d79e8b76912b583393b67a90d67b5f50a3e9fd4d7867154bebdd8a2e7bed7e232b4bfc146909d4ef3a2b1e70db94870061af92511761afe482697e38bc74c7cdf6088d72e6f3b7c95e435e015c93bc6a9154939787887dfbc7115057bef9e10d96d36d5e20cd3fff55621b97f8cddd9054a3c2b67f99c48fd320736aa107a4978119fe2f8690e9f939da36b47229c507e9a7f6ddd471aba7e158415103308f04ab3b18836b0fb8ac8c77358cc4965c2430a7a6f1f8df261db979509f2103b67d201c2122dc593f147d4c035a61b86a49e27169d8e2917d876ed0d7e8c0eb28129863978e9d2e9295686e37198f276ad7f52f1c6ab7cf1896b3a336e4a0f6e33e6a2b698a2336cdebb029de2c2c3a5515ab6fbf685d8f9c07c75fc8095eadafc648772779a048efe5f8111a5d187c76c2ecf77f82678007cb74a931afa9b4f5feec75086cf7147f3fbc59c1e16c5a6282eeb52f9868b08171242b6250a8b9602f45a8633b6675daf77e403de732902692a29edfa278a8b4ad9285c62a1aaa250c2283679e56acda66a1746d5425db1ea21bbf0271d9c8cbe6820888f17faaabaf425f2d844a4c725b21259bbb70af303cef5e8371dd14df4e9e98fa8581763a73e7029bd5b3f0866afe1396a6fb164183bd440c082baf2e7a4651bf518e9bb91d2c9b64875e809d02de8d5c68f1c41cf8a95c1731fa68857d88d7828e0202889cd1120a8d25e4a05a1328236027bad1088bb2bc76dc07ba3b3e5c67d33dc97434c10fc515eec1e127e7a5629650531cd28b5810cdc4f65ce38c84020397960a8eb60ca0458a3f24b223ced9a42fb0453e186d52eb85ff59926e29cf6f589e4ed234590f943d8aa32d5714a6da12dca5d9f45e248ff8f0f5567c2a103ed72e6285984e51649aa194fc185b1b84a9096d640891a8046d01aa1ff649015cc42b81036c8820c37a118408772d4801b654e025ad426a1b110110b2792d695831c3c5f4e4189aa07cc1205847877b1094ed1076aaaf64c190b7082ee4244a22da52eaa42ce4ca6f41b62bee83860c4aa687568cb0c7be3a209dd432f316f5b856ce70173f1ee840fd1c7010a81d08c21b6f71beaf4cb606f943e5f94a363251b603d115985633892a8014a770cd03fec941c14c70beaa97d5f566290bdd836a9b2ad1c35fd3c69d369f5509f801784111df8cdcf29cc0665ec27491ab6abc1f06ba9f14c7ccc13d0637abaea040f5811ba5246f943f9c38420468e5e1923acbc1a1a9123b80b8728b2f7a6bd29714c4031937c58ec74ad3b8582dc6340c1c9b8765a5fdad0a5c831addd2a403a06d736205c6a07c050ccc126cf84bf7844e37edaa2e14a93ca272bb2fa4fcabb365e0f3b3107e282046a26c01a4f6aa551d65132a8101511922e84f9b4b29332c99e5b0226ef48bd938ba132114cdc44cb86892534eb94324e03a5c8f4b0fc1b76176b8323cd8d13818671a28052e2a0d554c9ec23197ae83171a3cdf3571e97a2adf8879c7c1623d14a5fdcdb4dfa978861ab6800127a3725251ad162e9656b292ec6d38e91fa4ab2976a102e2d8d93ed4392915ed5c43cf8d77622258f621cfd4e76d81ff6db7a4b940aa447cfc3eb91053726e2a92847c0f5619c503a40f428235ac2dc2f3a4b707a64dfd0197a8156cf7a759f02cbafca8101208117a3b58e75beef76d547a76ad4791acd89f449177c7d4802652c933bc3b0f8a60c860f1a48a9351d0a7776cfecd41378f663fa897a92f3376ffcc1728894d085f7a36a313d7f522868b3cd48b925f7c4ad7222be7f82b75541f7942104ada65f25f1c07eb84380e332ecade44be5cf64c3ffbc368d4cd95dbda00a89583dc86eb683198918123076387fc819ca49248933f28fff37006f3df2c57df389819403edad153f7617064bd63ff588a9bffd605675ec6b721109584b4bc8cf08242448b07752dcdee033bafb472256209257137913f29263f6454f98109ebf2a0b8033739b71e68bb21b24fc0a51a47fd70c375c826b7e16af3ca2e1b7b8dce602bd50bf713e251e67b32eee6c68962f00a7d1507f83b9329dd40692f59a30944fa0ae3012d8ba1e144ffcb58a24fc7b0b575e46c81d3544f186e01540ef00644e31de978f92cd75539217bd4558e8f6d01c11f59c0cc774e9f17fdefa95eb81e57b3d485ce0c257b4f52536441303687a474791bdcfe8253f747ae3276b33fd744836b273d73d6a47ea3350c1a27c7e04b258760bdbce164ee8849220662877d420c247877503cd60e3311d30c63f46a9f196af8cd586237fe3b7db5e4e8517e22a0c161688f5e76402dd249f459d03ba057aca795bddc8c23b407fd25b4d7c65c2bae86855f0de2fd258bf49ecec45c46637088f50af9c8c54783f574c8f3114173447ce960ef461644c8f138f47881e28c32e16fc6d01a97e5af5ca00d47b95a7a84054512b8c65d302e3f728ff846a2336d91735e6c29a75a8174153c32af18eb5fe149c72233ba66b097416903889fc108b4f2824b2b9e3d2a8ed90c837232fef255098d33c0a6083c2e38dd8540827553a85900f2706bbbfd9cc87c907b42b142c3f9d699d98e08c9b7e4dc9b9be07fedc818dd77831dbc2a4f3674f3e72b2ff983647a0fc845923b84ed1ee6a82a1b4a09b5bc64855fa25f133cd83a57e3d7493c960d72bc2bf84d974e2079fe01118d6006577aa719350354fb04e04b543e8d3f77b5a757351e1aa0ea5809182ac9b2fbb1bbec1ad03c84fd0ddefae823b8b1ad73ca5f6cb0593398b04fd2edd4807aee968e3b8e1293daefd742d34c23fc56c0e350ad9e41088a4678f3f61f96cce31674feaad01b9949d835c3850a67763f0e2dcb61890e26574ea187c0153122593226665a00300f379c1e2480304b2f0ddcd501006b1ae47014a434bbcc22e868dd6df876e06f71ca3c0874fc31572408be74ee1239cd63c0a469ef4a0777fcb3618294cc040975cbaa7ec0d33f7f5a68bea66d2a6dd1ef23289399ff1eca76676d532df64671f6303fe0bdad0a911702377233f811dd31c73936b434c1f9b09e473350f2a7c9fe825a62eb3103a7cd81d77ccef31475b9a0b3bfe6d86802f07cb7bba6e2615995b8931b5ad1b764906843bbcfe720a185fee4e7f2006f0fd1b4253d296a7b74cf3b418f686768456dbd5002fd350c455a7c04ff9145b73e57787120b09d80d893e0ccb06ab66ed8f43cc5c5a13037ee3dfb643411c3cc180d615a5548f9217d7b922e520f3524c89329e29c9b8f8c1e0f95bb6d18b787d88d7d831df7b50fff47dcb516fffa6dd0f18e2bdde9e570d6e84ee06f80a7aeb8873ec8cfaa4e56a0006c7d5c6442503318b186380ee77580ffbffa4dc0b4aea215d0202ba8926b26ffbcbf6e5e62c52bddbd02e50f8aa3839c0cbd18bdb8d3a3919e3cb88bfe35fea395a7e158e48d6ebfcfe968946af4dc9314fc86b8de4645b1647750081b09715ba1dc9d52b53e9a5285ca0d42d0b750d6aa6c45f17c35752ba74c973fb5b30675c6f4825a38fb56217fece623427cf0ae2b1c01540f4f8c3060950f2a68644080352e1e376222d8204000807b7643f386024de96a2e2b75e0d1a60e3ad6c110cde58cdc92f86d34af841c29474babb484b7900180ff19d37e1285f985ff3bdee37a7fb464366e2f93d2f8f3460c6a50568f5ca7716ea0a5880d9be1daaa1ffa814245a5b88a32e419fc6368b3fcf68e3ee6dfe9fbbc75e6f7d7f05e4fec636c0c7ead5f1c31b668c71aed171838f7c97a744f5f86c1d856f177ac63302b9e8b28d10454540a90e446c28dc75cbb3085df648c47e4eb7c94a20265b68ed8d2d34e93081705a2d918367365089e7da3eda20d5b9527b1ef81eb17abde44c1fe0e32e04ed96f2169587d5a770f63e2f9f994925a67155718d575904547db46b77cb10ed6ea6ed0de77063ea5a21931a5b5234b4594c501951d742a31a78fe6836acfa40af3f0390dacdb0dc42dc5976896c37f184d0f957eb7a6e793d827a478c8e2ff44748f97305fe9608cda184b68e7a2d07133ea0bc14c9bb85781df54a0180aa3a7dc44d17aa54a53caa16789858887a621ca9a611ee3a6cc3ba1098025d2ef2156341102789d892465bb3b78d5060ee44abae2d776e9d0c727027307d686060632431a6c4642b70f7666113da345324b1dbe8faf0d943296a9e4e4e0f76306c343669e52c2b382d90a14c5041de259ebf8c16fd6e4b8d764811aa8d931fcc637f50edfb047665bebaa1a1c662e2305f93a22ecb3ade77705bf9bad02ae45e9646c198eafbbe9e1f250900cc3663369286021bd3d58d2bd730b8da2261b0ff9b09ee26bc4e4aa529dfc19c5611a24cdf2c9f1b1bb60e24ec4d0fe85b429087a05c3e9d464831f68af628d40ec355a4d7fb2599eea2fe9b3f521490f8fe33598d333d0535d8dc1fcd50145f67a2644c54a890bdba5e77a9814d2e2dc9272b24872e6880972a9be1be2fe291e44de29bd0ac7d7f00fe6913d9f4f65763ccccd0eda8a3a0940ebf4da996b595f6600612f777927cdd7d6d8ec8a4087447cc32206a6e65d4a250748360d5b85bbc3868ee73bcc4b155d509fb5b0dabd43e19355a6de31f4cc7f2bee26223a949f64ce454ec0297f53c6a9ed276d2f9ef9e31f4d589392c2ea4377020ba002712dab6ee5e91da1ac9791159a44d6b8e72e00216feca7ced7c295eb26ebe94f5e4356031a9e6fc128e71c85eb512afc3f5c2dac203b77981933cf5b2691ed86f1e4888d9e58dc66bb9f3e3f46bf70fb7bb8bea1bce0e7cd44fa4614fbac579051b654b3264294bb8304167d41dee78a841f5a6f76555f9bc2b326902fc1851a30c28668c360218e5fdee8821af8089f99d4759087fec14c8d2c25c325b072cb551bff86f60f82306150c9691ba8d68ee1296ae88cc744190e649aa1346c4a5301d4af00f7b4453f3f2841e8aef0cd71eda0cb437bdaac66354abc5d6f930183f5b17bf1fe85d6124e29a80dd938a16b63df859ac2e4a0b34897f3fc36fac1f57f5d92fa17f0ea7ec656f513af15b6b25defec6bebb784d74a277fe82df6f21f20f85b805a129a0878e344738ec3698d698cc800f3d5561a7e64781fd1d03a1ae9f0ce6723ecc04e4bb3f0494d6dd47bf96c90b0ef872d1f10424d777c6b17e4aeefb08698d5702f04c460c56648dfbfa9b0b320ebfe050e69cd914733e7042ca3418d4b1727fca8610c9b020bdeffc215c21dc9eb42ce16329b81d4e959822c777539eeda88b487aca88611584684942cd05c79eaadf73957d4fce986ff01713e58dfc40771f0f23af666edd6231a6d854f521bfd6645ffe6462c84178091b115f37232f7a6429b86c16c7da4d10087ce4371701e8ee1f9f523787c1e7bbabea20e5ca0177649819a92b783186ce76bab07ca1432538c11ebb0418133da116a22d70260e71a6115833b7906da203c5614d4d92e600d9cc367093ee51f16c32da24e9cff84b6fd4b686539c8d35bf3e66b07abe93fe799ab10a32e9b950bf23e7ae190bb5f4d5148098914dd842817a855627c22a67cc9c55af01f062cab43813d4acd13075a526a669feb3437948409f200feea6c9d1372c07b097254e4c2297438d5a34f6be87808a15bce1260557b66352bf212f3f21e6f95accac866a97348223ee885318b1ad3650b8c2ab9eb503c3bd84a16d75bcaf19c1128024eb43673a724a909172c6317e70c29c4a20a12251d0cfe93041cf2dc1b86ac7f04fc9739c02a068df6d4a969e918e8b1ddcebb139967cb4ecc42c11f3ecd6a9ef93cc7c7118edfe0bde42fc0b20087bc11306aa1ec3e7c756229ceeaa7f0fa4cab68bd022fb60411599a1f5bf3890d221d9726a8cf2ad7da81601d946e3bf2df83964bc3c68caedba96abf75906b7066485d20db074242ab4943c4ec69adcf212597c07d0c2243b0b0eec39ef85f910b5557bdc2e0f1f710e24cd119564b5924723748c95496fccf0278c46ff2bfb0530e00b882b1bb3511bdec14e426d5901031d24b201aa21b4c6db23142eef3455b3225c925c883445b4bbd7b9b5cd59e7a33cf765d3265fd9a324a8f30330ff88c880cff77a994b1ebd205aafad240909a6dcdd5668ac90635d3d4072b5c80323f5ea42a52a76973a365c2891a4a3c59f72e224e30cbbf0cd00fddb25e39a6edf82ab0983d36840f5da00e625a7c6271880c58ce0217274b0dc46495721100fd45823f28f1163923cc1f6bd9df1d74de0a27cb30e4e7af78d7de49dbc43dc9aeb3d71eebc59ca97dd64596821f44d15cbc7971cc1a01da3e9195226c54cf022583c2ac34ab120100f89495a55d1768a7b2ef387f2f3f01143c36e7a562a4d524a1134235cc22700d9481706b8d6f081bf6d1b28b57123ac35e08ee4b778e5b0d3a085d4c0323c2570fcec3873180a33292b95183261fbb6de904c9a34cefac13c00fdf51321c5b9b9a0323a6ee468445331aba11aa55e27b5e412aa41d277ccc465889ff6506f40c602ca979256365f0a8373f68fdc03c196c7783832b618f119b705d8b75ac7cbe5fea86369a6c96c2cfe871ab7a5a8e8dbe881652ece2455863a1221c4dad90232129e0a4b21ee3040f0978377af156914d8973fcb6afccd0450456dd15c4be8496b0602f59a752810c8a819221e3cc0949bc37eae98b361e16ba432edc106bbcb431dfbdf4cfd51ad8ad489b01e104881bc33869c0c0345277e01df58f214fd4eced7ed5ebf3c962bdfbd93f0ad087b01865819649a19173e93cc5069a283ff064c93f2ec0ea0c12da462b4642e61708f93ff489f60eab3fdb3566f61ecfa442b0783238fe56ef2b5662638ec6c215b13d0daf0a873b3878b64031eb78d6c8d7d107611b74b080e625c491e5e79c23d5c2e58031902fde3afbf1e6100323b2ed269fc0b9a45292797f1eaac0ca9672d5caf660068c567a7497cf6463493c0910431a3891e8daa17280fb972fbee82804efb2e3eb6dc63f0ae9926e36cf85665e467845f3788c0d5118128f38116c6d60a2613857229f727ec3b758de36e8800058891221e98e1a437f31daaa95a65fbac71d2f6a68016da3d8746787cb2066ce4f460423d638b8e01ac6a353504042265d0957d7a40de01f48dfa5afa4d9f9014aacd2418e46790057d68b72ce50ebd4752e3ea7632c1b1cc29b1dbe6f0021abf6d8751fa7e89cddc59b92043cc80bc2e6402e2584bd9b5db868cc3b575205a24ce229bce1d5c7b05cf274af05b666f8c2677b7b425207af20a0697249fad3f27547dbb8a05ad350041e91faa19dec092f4389393d432f1373a4c163b2aa8dd6f11a12006e3c50a89c6b5555e978c1ce80be34cd6d8fb8fb5abfbfee4e89e4142906e0a32b9d20e921afc03de8b4813c480656f52ae659917f55621fbd97a077044f900b9ea6224fa4ed46b60e4b48599cfd8635a580a0bb55ce9121bf9434d75f2c4e8c416cec0ea702bb3d084e93682c31971a3a1e2f691bfba4f06f8867ddee3979f315ffc4bc565f75390729d0b029f144db8157e6769782795416d83f4221023799cd30288cc139fb776409ba36dabbac49de4805e9ffb551e463501fb7ce79f8730c89175f1bdbe4fde317cbe454ff151238209d600905d78f3c4a59226f0a6efc018976acce2ecfe8a4699c22c280db2eaa003c1773d917eaddd6f56eac18f5e378105d43b6a38c69862e61ced0ca975db46e439fe69b03b7f76057ea6983d4bd240c4d5b13c4f6cfb06c8ac8cf5bf78670f3033b4b60182e303d92eaf4d5eddd1a8c3de0bb7139cdde96c4f32707a3affaa199d4e3681c6a66792ff229c32eecac31a5cd927823c2c7d13b1c27ac2c0c13bbeab156b21f1d6b4c37c959e3e53f0a8dc5febd39e7ecd15a0809321b04e22b6a4435246e48c6f5ca5bdc308c32495631db893ab3131a01c2df0f1a4f048b2baebc13c05e03239bf86ef3b1b467d190359b099439d17d7b928d86967cfbdee1122030c1e93b2127f8bde7c1bb1b8751961be858258927cefe8142344bf96beaaf89529c49bd20d40faf4bc144006f382f583554412f3a980d4f991b5661f979ac0703cf35f300421f8ac82bed1791ff60c69b35afcb9619da7bf198efe66b6dc21b6c9e3fbb4e2af644f389cdb19e9c0b51da8795f77238d88796c4194216de2128d63396ea7ecd1976a7998f9acfc8241aa69bb30fc7dd477716d623a0c5c9db0bfc870b47dbeee7d8695addcef0050769eb384243d2a731f61031e4f4baef747916f02beb96ee2dd01d8bc65de470225170206575dcab617f231b3fbad037ae1cdec44ad4a2bfbb56015042964661a292de762e2f29525c3d82246dfe446c1f29e5a4f5d05c80f119b5bff6b4b8491657edaac76021fa1415fd9f03681970e4a65371b87b5f28204b69a50eb779393c8a2e86fb44c8b3332a227fc4f396898d1f63ceb51f2f2542d3805b8ed173fd44e128fed360d390624d662b3713188d5a5957b7725f35b3ed805775dce51be748b1d79c9aff4da45cdea2a732cf7e5c16227333de624b1b99192f66e1781488bfe6fc0f047384c897cb1eebde30e6bac3bdd4462a628b722c1b8d63694ba1124bba43a86464c9e6f9d599c214426827ab3a0a5f9e0545a02f75166e7b17b5fa6f34f09e5ccf5a7b522ce6749a7beb8bb4e57db31738d129f1ae51cbf780f288a01dcb49e1e09cfe9f1888bae528879a88d05465db8211346cc9b9943d10ed86ee5097cd85f66f1ceb04e07cdbda814fca631a3d761c00a36aa1798c401e7bf133d613e0edaeef4fb2a7974f960979970acc4faae74d8915b037182d53aa9ac891139fe563eeb6e1970a6ccdbb4f377f8f79113f2c0fb22011f815ec99a25e1400a254aa9c70295197a6e4faf085f8118652e5233554296f90b19496c78882822ca4ee933f117e93de3cf6060237227c70f0c3b0b7e2da25e38714ae8098604d86a0ab686735cbab94fc9552208b6231f9afc86d3a3f49f4d88a889be5639a04bf97a2091a9dc647570d01b9b3148e05b5717a11adbc745675f8858bf19f3f536b9f3b5004ae4cfc8e1e2ef3a0fcaa6880ce46db6d3b1425b07e292f5ed03be4a5313e958c740561656ecf1fffd8b19f3e278d61f6dfff508c25267701f528bac53b915e80fb1cfd95572a2fac4b1ba440fac5d3775de32bda9c3acf8eac03739d8ca2484e393ce9a840fb6dc2fb2fd7986d1f2683e725f0bcfe8d5bf3f30a86dad41cf5c9bd6211a978364ef1202ef698f06c8dc75313f4e344d4aa8268d5c134c37f030b69b4123b3da7625168e7df902a231e8f6c7fb4bafbc739f0f274f34e639b4a7b76eabf2772e3e23b9bddd2187f4f785ffec9efe2d7a10d3690a22d82c236a6bafe2399a944bcd25bfc7a8fb10c3841b9607148b30f797cddcd7158283a0e938506192760a142b44f69a93edf26e3e0086739ff76cb4b6ea84802fe5433357cdf53625452b3eedf0b86bcab2c98ddaf006e891128d1621b7bd9cfd762db17651d9dfdcf0eb4882b3c7bb790b299339e366fec9219ef82c8e93ff70e58d884c5c83d8eaefa56d9f53817d378bf7b3afd6424e48cff85a4d4500568433ea10ae8f02fdadd1ac1c0dfc18e803984275f513713c2507242a0bb3cacd2630867fcb5e7518b5d2038c34472b26e8975dcc8f63df9155aafa01da61369ba8bb835cc19d23f6c7d0ed8b5fce09f7e2e9817645505832104d90a68743f32ee156904f0c3f24ac80f428f18af9df6dabc8470fb9edc0b835cc331f02dadc9286fdb00ee869ae1dc6c4e6ebd2ce8efd5b5566f51fb158c92b61d14fb1b6a0aad9586acefe1fe5aebdfe3c5c3d20175c0253fbafe424b83cf3d9606c73739cc4a10f6ed061ffac530ab11584c89dc602227920a25d5284be6f188fae58ccc5bf901ffcdd46641cd7727ff3c359afd71b3a2a445b84f2e111ce6ef248878b2702c55b1d61d5e1fd88a55ecd853a03ca20c1b23ab4206bce9df250cd624bf95e0c3ac23d14c1b1f955acc506c425f6ad2bf30f175841f6ddb76168c43007f2afbd1818ae872ca9196ba92dee276847deaaeb50759320c11efea784440dc1bdd4089a6811555fdc58840dc6f8bd8ec4978b4ed2cdfe24124434ee72db62bd6deff7e3ba37edde17b3bc3bfc6ce940c50b4ad26de1fcd8e894869a759a9f1465c96ee7fbaa03a132a9ee7aef3f7c9ddc1890d1618a83afa2256e0612c0fc650e313cc7b96cca477a03d8801e645eb1d3980f320be57e057743eba31911963e793aa54d2c286e5cfa3cc4555180da78b911e42255d2b41a16ca697c6afbfb373cc9dbdfc8c2f0a480ce6e7098193f9fc0d77333f7cc659927151ee9b59c0e0086d46ec686a08112024380279bafaee4ae4b465d79d0672433a949376b4cc5dbe22e8a24c1302e219b6bd6221db21194bb79211ba51378b50728a8d58cd208ae5e0b9c554a46c3c7f5da07aafeb74c5d1f813209a337e3fabc14977047018fe82d5805b7ddcf70bcc66887c3540c10e6e077e15918531122ad0351f1c675c349778fbfa0b29e833d4ffdd83c00a46814a8c4bb43b21dc9cecc56477232466e636b88e43bda5a641d5ebf72c5888981997351bce669ee7a8d655fc9bb3d5cb41b95de8265809d7f033a120ee013eea6e50e400b57f27679d42bf0d2c437d708eb74020c96d604f833044c4ec53bd85ac482394265c2a4ac2bae5895122d5f125afbda4101188c9fae6dbfc51ba9c065ff7540153df04807e23d9c12a1a6904af425ad4353c9894ab0230dc51d4ea813b0b46d179ba783b3cf27165f099ab3d21c7c1af6fa8982609598ba735366115349725c65dc93e0fdc2d54ae259f063556c2d19c5317b0e7dabbbf6dc724879fc784e68fb5e37d5f3214276c76cbf18c53c072160a56b683f27ffb0f0894b64353d01de0cd99d329f89a1fe81a129c9da8017030883aed96cfeceae71405950327c09719424514d5a698e585ec296b38e9604ff933d6c44cf1734a8a623c634d850128a5ff5deaaf6243aae0d414948a8039256db9759bcfc85ddb503638c2c2c1c3267b271f0894245a150e56be4647fa2e725c88e8de18396c519829940e5160112d980afe2032040d78a90205fd060546a7290010352041d6300fcf8d33b71dafe7fc157a89cfd16d19d9b23a88dc7e62c457b40af92a7cd7461e0e503ea82a60de35f1bd84fc98f0b55dd9b0d380cbbc21a66c15dab5c574feb16e62e897253cc022cb38ac896c8daa28c18aa39c1eb1d0f7254890efff31629507ff17d58b856dd21c832194d28320b81f1ac1dfc413f40d852ad948924e9892c9cd119bc18deff9159a3a5bf73151f101301e7aa84ef31c5e5d010965f54af0f218b210dc3a5dfd2ae5be6df022f0b46fcbd3930624d7bc0a9da5005ba88ac24168c435c49079ad1a77728036d45639244f78092e3037b46d0b2cbe1690f01dc18ea7ae2d58c64230e504f6272fcd3600779b379bec4899febec139f534c7d42fdc091e15256b92445d7701cb9b1fece634fef73b1cfb8acab74016f54c2e96ff205808e9e7f64e163f070bf635c14d51ecbdff4253c39f24f1aee95566c753f290ded7fb2e25323197d4625cafaae91f7a8caafb00bda6b7eb742a5a175ad4947f516eed15d4396d04cc12d2e9c7891d08611f69f48f631d86ef1f59743c813db36c35bac31c302df5f7842d0423d98818ba49c72b94db52f915294f60ce59fab9f939613aace89487f49542e375a7d21bf887d22273867304e3232fccd098e49a56617e951d21de10af4de369b7f61593f44bccb1daefd9579eab62da33c36ea8f0d965be9f68eaa88c73fa5e60c26c1696dee3e16a77d85eb6cae76ce862820c44d32db94b8577810afd39c18d81122f638a3c5125ab9361bfa8ca72231a74e60573f9112c21eb42b3409975d5804341d115243196d770fe4f02d304e4cb0b21fba77942f13124298f9981f55bf19303c70aa65f3667230d63febfcc74b91f98401c8a6a4ca3d91e915465f6c29eaf5031ad56e26698d2424f12a69e33f30c2c4f03ba6627ab079fb97f7ad55e26f23309eea7d0d87fd4ecfaa7461ba44a46900cdabdc3bf0b6afff7dada0ff89ffad9d41cea450b31caeaadf83aa9a5dfee4a3d2150486eb418a3dfb12f28de97f2d05809f7bff19e97e43d200c7f4f666f9c7c4960f29274fe1218ae01844fa1159d29e4069f1e09d75ec8e10194aebaa95d2e15d2aee639acd2c24add86def6bbf588aaf52915011c66709564bc500b40e6a42768e3b114c7be7070259104600470afa2c0901b67c7b270d721ce06262a4981e40f31bb77fe6931ff3f1ae20d05baa9eeb6a4f3296cd436cd69e55883dff60101c4f8e40d1f0c30465d9bc97b81f6bb89565e52b33b5b4e01e694dc00823940f29a1034edae734df65e14ed01b70a02a2e490a1869cc9dc2ac2271d3d13090ce85c466b98be5a19339229450bcb3da730ff56e02be7371ccc8f41da095efcca83a0f29ead3c517a80dab8f2d1cee442cb5a7e9d6fb7b40d2bebabf6e818a25b7ecd791b23043738b31f741a67bb86cfbde222f79e1c08b7d0dfd265398bc44021404eb8b4b0438bf0c4722cb88cf01e1f214bebd4bfba387710ba01d106c5000f1b88ce2ee41c861ec9e89c79be79a1d7703347f395fc84dcadd6ec20fbe0ba1c49d061113f585cb885e55091243779b226c01b0afa54e48807fb7728e80eb466c814a883a8a95ac3cc7a93053d6600553cda96802d981a55c273e266da4ed86d0af2fc616247559b020aeb192a71a8687b9fc8ef285b8fa43c37577f7e693d87e39b4d19090178638d4c42c875804edf20b4cc6a4ed3d5bf5e37a1abe3581810a74251f021dcbc46b97a634830978b7f7c0dff35b73470099bfa00691f96c78e89a505ec349e560b618b399de6c4c500edd216c6fe7b976078a5d7593bf5016370b8c85cee5a396066686545088ffb10655efdaa2f05348095f56825a3296dc2686c184bf4c830eaaede69b5e790a2a9608e5567ecf97fdc7370b8539f507fd9a0c907ef84570a5ef17b232bbdd4681735185ff4340e1b9ec92b45e7a34dac11eff45e0d63bbfd4f962f8d3b466d32708f640a0d9b88a5b364d497813d18e775ad7ed1dcc4de619177ad6b113cadc198e74692414e19590ff2d79156d4dc6d0d9e8ab427088d3dec7dca570b66d954972180de0acb77ff28047873c10c70018368e335161f68a7e13941a57f33d5ce3b2548057d752fa3e59f5ed86cb24aa580077eec28bc993c70cc9b20e8dc6f0593c3942e2104c3ad8b9a89030fb8644cab284180e8bbaaae620e787c496a217aa6d94756e5afe3ff654e4ea53b465d88adc4c8b9f1e532677b8f6bace49378a1268e4508677c82b9efcd41d340f3c01d3d43ec89de518a3ce23d7efe3e39db9c5f5371cb73d02d92e22c7b3834996989518c09b9f05bc65f885c1383cc40dda319e19ac0c95d3dda2236e710481a2a4b7ecd332d0bc667878aa25709018a8650c3d0a58404473cd313ca832f65a7f530e6b6f902e6b55b87a234c9634963a2bf0cbc8941315d0440c575d68760f6042e0ee3733775db90d51a0a957952573db2f0548ad119944a37693f4c81124fa5ec93119a5bd1a7aa4a8f022b9db5b3191d2b25d4509d4d113d87cdf1ebfb00dbd7d5d5fa56b13fe61f6155efc96e0b2e339c8507ec7a4184ec04dbeeaa42381f3e41efdd297407c01e743070a9320090a1cd00cb8d52e8d0a5a1c1ae693957f4d69ec43b233a2b864a12efd58dd8b815b7cd9b1643dd4d79ea578fd3cf412c8971080fee69bcd253ab3304b74262df68c545a0c94e5560ffb925f08c2706ff065d850ac16f6ad48858b13f81dbabed407f93189744775695fef3417b2f7b2c0132c629a2b4443760465dcde711ae2fbc820bbdf6be730eaf74c96c763644ebc72712cbfbd89ba698dd65ddad5c1941db117c4fd1d7f317f035616712ab6054c6065ea8dfab6efcba848143ce4e36601abb5cb86c48a9c1ea97a00f9c17205091d6bc0e95b05db42b6d211cca4c7e645002d670e39bf3042554a0f84b76c263349a00566886c9935af571e7a7b00888b71cff9705d38728eb3cde1ae0a776515a142c2815b9f65fb0b0a34ab9087f02295277c857e5b6572bc890aa258724126845a6c349863e6550e479e8c30e8c3eb3ab9950e64cef55ae7bc0e1d292ff944e53add96f31d5375adb0df1a529a9ac450f93a8e27b617dae1700aa7ec5f0d85a68ad81bc0064f9acfdc0185353d9cb8c0452a4b5f6b998385a3c80947711240048836fc6a670ef92780c3f01431d080134ced417602a2acaf94a9b9659a23d89f63216e2254dd7f10c9d6a76607a9de1f0cce4af22fde5bde728e575147e1e7ee2429fc77552277670c07e52c4c26f120180c20c5bb52ddb5b6facc40ddc94c1d72bbe8d38f90833534c57b0f30222de65ee030b4f8685325187fa6aa3d32a56e7ffec791151568e01a6c75d2ab2c93d8104f8d72b1f238808bcdf51d465669141c1e08870c458a4405842ba6486288e0296f8500726d7067f930195e14d53d7ff4e4b7fa563403c9733fdc7f335f2b17dfd6ba3690fb662a0683f30020869e2246219701d4219de388d7783260c982cbdc9491accda9ca0bfe0d15619a67bc743ff10e8ed61490317d5c53b2f5d49219d33641f5a0776abf7981a44f466d5b3874b76c2119afd5b051ed43fac008047e63770feaa1660bfcc04f2ed076e85c2ccd3fb29a2ff0b2bbfe7027c53236e83f36d17e9173f2587daee3c2dedf4a8aee48b565602b1582c8f30ba6427603a97100d40cc2ef0a1485b68a3bbd078605262d6fdfd4796ed2f3fb242349976973e5ebd9d60c69b7aca999935b36697e0f1eba8967d3c243df69200830b1b5f04b144001352ab840793070bda171b5cd426880922bb208d28f150425ba57a02a363fbf86c20509b885a6c8e7044095288c4010f3dae4f07976c657ce783ae500d07cba1ea73dd67962126cc3222c822fdcf805b5539c45bf17e9919b666e262b4029d0cc3671179bd30db439c205125755499e822a25bdee43c784812e71de143fb442256d906e639823f28b63b68f933ef9ef90f5b07239fcd20207f78d20a0f027b2117d9776b35592b8a7c9bb508f9b28ded4264a5a40f5c37ab64665bc72f0bcb29db06cc49935691c1a0c47f5d8d5c015c3426f04cb2c6ba957b9b4eb778b9518630a1094690852b9117f1166119c24de4fa32a7859fc04426467a674c29bd56539dbcc9c06cfe18a1162f0872b016426233786e3ead20750ae7fae173b83cca3ce7a9d07fe2ad29d76568ed1fd2c2abe7b574c5727ce39e5826c64950521c9cf7003330ea00e53526a0356cc55fdc3bed329e0db188bf3ff76f53966374959b13e5821c039e2e09bbb951ad6c5be9a3c42128b87b3d4eb932ad82b5f62c272274be133cb7108867dc0ba57b630cd65caeca1fd04ea4118671859ef4400a66a3757a1c7c2660f57ea0371cd8b24ea558d992149888860d402e3a98db08cf4a8125e09f4aec8f7decc2b2898f3026c5a257ff6423063204cc209847f8b263b30e760443c3f2c8db4ab3ef5b58eacd40751200cdc3d23670409be3adba86054b5adf1f378782bd94b29a8b9a18f6a27c2a465b0c7aa76dad443d42150eeb0ffb44bf403d8e04b255731bd7e8405a65d89908c76fa29e59935c902def06d181e8e5faa63f36e3c9847065d306a547b8d7fa5d9196f2305a7fe97de608a707d666960313f9db3e9e7e691c0b77cc930541e5b7fdd3152c91d0b7f0d4b10a13179812993ffb71eb46df48da247da832a10cfee0b2cf01fe2070ecbe1c18da2bd227ed4fb1fc4fe21fb923092b7201decd911311aea492341a0985d2e102a20681a8b78641f16417e6f6342e04b5a6f86ce11e0fd07e72533cc56576d771980b283c7ba19d241a474d4bea847fc33296fcf8f71c73db894676f8b79c9869f897a6912663d5799dbbf7c4db93fad8b0954d0da10f60c66a6db7190e77d582ad4ddc2d576abe50fee5040d1b3b825715980d9f4d85cb32468fe8d43b7c1e671a263d14a065944c603729d2a5fc923a6760731e074385e918a7024c43f4c89b408d1c4cce0fe9252b8c4eee0008d5d1739c474eb33e8a67e95e8f5aebb38b80907dcf827dae646d5d2ccb761799b992154ecd50869c65aa788c5cd685f71d7d0fc71b0b50a3442765d3d888a2fdb863668f6effef42294ca80d7a94a893ef887941a22515cd713e3a265f1ba3ca820a9081585bbe34afdbdced88712378368c2c97ded2cd9aa815487783418886ec424f0ea5c9c3a0b549588a77db4659505c50c3ae0a2d015fef6b97ebccba9a3583dfd033dcb6a7073c9d0e9dca176c583338001aaf8044c3d70b0fab9f401e0a9f50cbcdbf8a738fb5f9c584d36b1a0b3189464cec513049dec5ffb4a426dfa4495bb4a274400c69257fdd8e749de73719a02a8366bf937552cb95f70e16de02596e3a09bcaf54cbd08c95d58e6e70747a432640087694e41ceeed2380a54c4030b993a91a10eda3e6421b4021c1536cc1f48d707945113c97e85983ee40ef8733d69f595d8c415e822bce2a96941916a817e64af4ebbd9c696b88dd2fb76fdc7c345f1bf20ead848e3d06bc398a283e32a0e575848094df944a1d69d9a390c9bc2e906a5f3c82de9a16d00e61999bdeab2a4d9d710a25a874c27803630e184eb6c41d2f82dd7ff2621b18a32013f026efe2b95c2179076d9463ebb2a30a4fca7e274fdb6e4d362cc429153860530b493584374aa9e580b9892ccb80738dd8f7d617a22fe9e98367457c6b801e5b6e59cf553c470b607f7f9421b3bb70472bf9cae3ab94a307f66461ba75793816ce2606aaba6e0d6ee06ef477fbca6d9ebe05c9f734cea9edf91b2c966901736d0b911ebb2a91992d4de4478e63847828024be86368876d59ff49cb1e30c98ba21563c817b67f44c2a62b31f604753f1779595ad7b552af2727032a7cc0108ca7fee57f04bb369e20b93bb15a41a24d0bc6730b41e7209025b1bc8292ca5748b4e2f72cb6dd8a97c284da20b02bd6ca1076994360b331676e6bac163c82c707352d61d199d70d2a7a9e44311205dd576cde7ab6a65071c07b7ed9fb5110514d8468016ee773820dd1b477b68bde160bc2cda7fb5f40fa5e4cf05be53e94a8d59363c197688e28f9b4fd2b2920d0e186d693a28acf3b18ca715fb46f5f18256c90127a203185630c1e28f3dee29aad8d0ca0f6465218ad7409fd4a31c2f6f2007c7581956f190393bb6155375ac3bbe31f9371ffced3ed363af8e4314e4d2adac115abaaf7ad6550710cdc554440a8fe2301c79bb2fb468a370ae930bbf3096a3f91346add1c791bf02e3ce45708d9679fcaee56e41967e0701da823f9e5cfa881f88af11fc5f004e4f8f7957895d179e815d40aa726716a99dadc64f5ba11a41b539ec1f6c6e589f68c17be1a873237e479e4eadda1eb1a6742c9734e513a449834a841cdcd196daf6897429b4fec787ee331dfc147d7ef3fce156f6b731c55af0d57fbcbb95069f62860350c605c56f65c18453af5699fe9db21ea85b40f510d6f015a11bdc417cae5fd6ad958338f0b8736ff10c7f38c26d545434f7b55e7d0d87e1af96d6acaea43eeca6111cd2fc66ddccf7c9871a10cc97a4fe4a4f43ffd5965c1ffb38bf67ff4ef4474c91552325916b9a8e1c4aa31f1d187eb0feda6ea06d44c916db3168f0bed60d5e39fcce0718fed93bf5dba83932b84b99360a7f95e1f00cfb56d3e16a3babdf13ce8d1d3cd7cda0e5e2824efb301727de6bd4ae3e8965bd4fd651448cef5c80e94ff02be4a85dd0d3530b026c30100000000000000005c2bbe35fb66df45bc4c29651d529ecf869d644a494a99d92a826b59e120e00206883475773778101b0e7b0db00ddace7b98550bd79a8aa2e89f64caecf9c54654124619408268400c1ba1d0bb4cdd4c691f1d5020f5af7212fbd698f51308a1495f1217be7afe124fa082aba99acbf1a0e910d8e8c467b96d51cd66e4063981e687cecf58e641bf9b78664577f43acbcbb4643239034890214244f00e84049a5854052d1acac2a47f76e02365e2a4b3567125ad82ed9830f828b33e25456a124b26131b97308893d9ce6645688e62f018f229501b96b035e68b9fd029849055e2343ae58baf0ebac94489d7d4472f13eda084a6643201d2c3c624cabfdc68f966acef954479a253a7b01ce5d7974c264180fcf0d163033622718e39089de91bf4e590e0ae838d27f9122d9e920bd878c4ef655a94f6f6f8b933021b8ee0347de65cb67cbcd788ad472ed47752cae262011b8cb8feef644c0b32863a2da25febf8232f46e652604311056997fe44eba7dd9888545d9dce18e6faa62b4144ea3c9cff88fc8b493d04fa43a74c425ace2023437c498929910b715bca42a832ba4e7d7997fb87023608b19d18ebca0e97944e07e15c34b19a492e5947108e6712ea537cd67652033602a1e92cff1e569df386259349056c00e233135ab3dbfb439ff344a36bda79b67ef05485551ee1a64d08f521a54909cd2baf7db1e58359acf39f6ffa55caeec1f28bdf6f2a7610557ec0861efe0c4fbd9fd9540a9520407a8461230f89151944a356756b28021b78c872ded01cb33d821d62a0c0c61dcc6933438ee6946765c964e2010f00000f36ec90108d9f74129e84cee6086cd4216dd0c1c61c4660430ed8a51799a4ff8bb68e0736e250eacd6df9d4e794e95367d880c3a7ebd133ab766a34df60c71c943e7da71bd6d253d1f24bf95655d22b021b6d5884a51c3275c5da8eb1c18672a6050b6a292c96cac61a8c1a2f2795e3e7e8a153831d773ac3661331b234dc3b1ab6537d8871d90336d0d08912179784b0e8ed0d31630436cee029fda0f33ab3618694e7f6b42f794aa9fd0007f0281b65f84ae676bad42a171f19ce7eb731a711fd1e351b6330deb865bd974a26131b62b84c9be63c4b362a898360230c6e2a25cacbdcb269130c99c5b4de29fd4aaca664321122c4c617d2514ba25d93ccafbf176c788175f39482998e666f5db0d18574bc747837a59525d5820d2e24749a96cba7f378dcb760129dddae936c257b9c1164880a6c68c11017523bdf3726155332999cc04616b0d0cd79a3a6fc3987a260030b5ff434eab5c6debb62e30a8f1c39ff217a77dd52c18615ce9bb941c5ded355621b55e8fe7f2c85262f19990d2a5cd24c25151b64061b53d0ec3b469716f94d25296c239b1e73360b6e7f14927717ced32995af64281494dba9a06bed9bd22730dae2cee5159976a513fc4dc12e54d00abac126702f66ae9dc45b748709c991c1ce52294b33992558a927f23cbcd8c6943bd85002ea64c7103a26a22e598c196498f1a3033692b0699e1c4f26d3694c2ad181186c204151d56976cfceee63251db07184c35dbc970899f142c908f7079de3a77449c9cca7c14611dc6bf7ca297800f0051b44c8d24d3db6addd8cb844c87a00d91842a971d4d6750a19abe3011e0050820d21985e51530ab7291ffb60a4f79fb3de566dce513172500318a57c7d492919a4c8b88c1abf50df4bed35a6aca9927ce1ca89ff06dd77f7782f94ad2453837de8db382f4eb272c736bf09b5f52e56fb94ee4a09adbab12ef43ad1b64974b8cf6f2ebc3b0d769f99a2e2e2e2e039fdaa9ef4169d950ea9d14295166db107f5412bc7d462ccd5e2ee4abf33ca35735368511a8b2226747898d22cea90b129937caf4dcb82f7783a794691aba2587437feb57d41f44c58b4365661de7a456ed1c3870817f555b9225199d4c77862566ad48af3f205a5d49dca17ab8415e894c4b7d8e5dbf279155a6ab21251263e8aac8a4fd79dcaf6b8164d536156b99e35b9fa994d54146d9452d9846aab4b9e22f398f1d9b0779b82a660c4798bbeee9b6eb70b354ac1867513f6b1b64a644891cec96a742bcb5dbc8c22d11d57dda24bb46da2387b7bdef1bf79ef94196584c2725369cc2ccbd5e5a0486efacaaffbe8a9a29f48b52e369adcb431351bd4f04499d28c6d94d169b4dc89c52a9af418ee32a91507353871a7ea7411eb309e64a850631359fad6ba6c55bb2254020ff040c16432c40c327850431387c8e9a78c4e1dac93821a997836fcc9644a96fc30964c264084fc0092010e84608707cea8818984f56c08b51765f6bd849e4b2fd96cf8d75cfe28c10e317650c312c8f80afe1f4a1ed4a804e62273738ed671295432996c8007106a50c29474909e6f4283e8f8c30b3526a18cce660c2d1d9593430d49b82eaf27b4a714766464a811897488ed334fd9bdfb86b0a10624faee7c41b48d10116d478990f5c00e1e50a8f1884385da73d7fcccf905927e7c200d351c5186bbcd7162f2ba74de8c31968c1a8d705410234f6eae887e08901f3b1ee0a3062358a0801a8be8e1a38c0b9c31440135140124fdf840063200811a8968400d44f4f051c6054ca0801a87f051c605be7f0041400d4324a04621849cf13758400d42f828e3022c28c1026a0ca206355831205043104202214401350061801a7fe871801a7e10408d3e14a0061f0650630f2435f440801a79e8e1a38c02d4c00352f5b4c45d94ce55d7b8c37f4a281f53f79b93dba1112a089d4de42ca9741d74cf9c828ac94b7416d1c1f82a4da631524dc63934565af4e5e4a2b4de838c1a7248664a3194b0a4921a713857784bf59be2266595f48f0dc270387b1e711957b3bece37e0aa6996adb2745d23846bb861db1436df6633ee37259309aad106fe4d99bad920b732a6061bbcfea42235c5d83215096aac218ba3473de81cdc94e77b811a6ad04f69ea7c49e62adc6928e720dcf494c7ab3842831afeb26f88e878aacf708e481379496d0e76965c0d3394b335a2645db05297ffa103203d800c994c2850a30ce726d1cdfc8f1da38c1e1ee051830ceece5cf6a89b827dec023bc4d0408d31b823cae5cf52c6ac5c0c6831f9bbd82909cb701810d5aeab999f452d080cc8f3743e961d53bcee0ba79c343fbd7c7e4c1f2f349ba74e7cca74a1a42f76867726216734e4c7f6ae408c1a5ce8deb2adbee591a763c9a44710202bd81182c9040535b6a0ae076195414b8a553850430ba9264b96f219e492521652956d6169345e368f05bcca6694a6dabfb8af50b9bf7becac952aaf15ca1c39f2f4994e49b8554044efb9c60f150a5de9ed4f3d2651d1293c42c6b2ab1cabc2475240f654b20a2a768c470139f6f6290711d5183350030a975a2cbd1e96947ffa0968f71c1a632b6ef1e181359cc05dcab165c3724613964c2667ec1893898f1f5b46d66802b2b2693a79612f090f13d64bd1d2b6a73832c6ced82041b6033596504309986acf6142fb873dab643289418d24e0df31f88715a15e4d25934918324053c0046000841a48f07416e197f635fd948e90664e0beff9c3520e1a637f9461468f304eb043043c5c50c308e5995e8a49d693deb808e83a93613afc7cca1c193e4a844420073de881076a108133d96917748a88abc6102e93733fd5d4aab9ae2184763f93ea28e91ab60dc6a1298ecaec7a6a5f01c32446ec3ae6fedb181252041abfc882b2face99ff995d5fb81bf2a2e664abd1a241a0d18b8276cba8b399d7728608347861cc54f94d65a599cd2981c62eced55de243c88afea58b83888c49a798f36591736112a5d4e67c5f71455b3299f880062e30d5992df3bb192e060047a0710b7d2fc7d3fef76174f603882d9693dff09e364756aa16cf0875ad496ed0f03b2dfc78ca747c3e7b576508346671881ad39dd59451e42fd090452364ee3fc388bd34c742cdcadbcee91d2c4c58a04e26bd71ddbd7ec35798315b8893599a4eed0a37e9f0b1cdc4e69472ad40c8efead9f7f6d11f5624639ee98dcad952d4ac22edac285f3ae7bae1a7a10a4575cb560a2a769ea6e214cf7d6f4d848a4aee665c12b731a72883de3bb551d1f275a640f96f1e1daa3c27e929453ae5706195c2bfa81852a02d33d4e5cec2651b05fa83fce4722af54783001102011aa24078ea0b9fd45a6e463fc063c8776032e94146efa00c1aa1b0f572f03acd3db6393e8288e1021aa0309a3a9341e6744c5b29994c6ef02fa4c78f2f19d2c304343ea15bef5ae5ad4d9a691dfc8fc98403343c715e7a2e717fb1d3bf6432e91104480f1d6c2710e95e2e36d7a9e596f4904f93491822e031a1c1095489cb090fb9972e6913e78c7cd01c16d56a0432a447891033ca10010d4d9847a36f10a7737ace967c804626eeece33b42dfb32aa56432994cc2c8008f1bf40e4941100d9c80062638ad1ecbb0315d66bbc790ee61011a97c83daca8d3e097f40032c4041ba06109de76b4cd474d5d972c19638ced5132a48c773178348d4a94a2d9c78bc13694528901031e1ea0418947265d9e71a31ad098040d49d08804e2368e1475fd6b3e974c262ea0018962c588dfbd92cf9cb4150881c62336312b76aed672a743c3119e785df932254aad1e091a8df82f336c9d5f291102030d4660e115cd5b3d261934051a8b40759bde1539163b1a21437e2c0c6828e2e47e4108d13df130a38046227237ff98d3f45fecb764080d4470dee9ce37c6d8e4641206101a87d0845cb39223d6bb4c5ea0610844a8dc0f4a372965e92ed028442ad64833b13da2b288108ad506b16c5254cc9b16680ca2a46ec3b2d9668b151cf2e373200853a6b60e5b39a960e2096804222b9dbd349a96ea6845810620509ab616d5f47c640b051a7ff0d566c7eef46897af6432f14325e4623eb605bf5f4b26931efee3078d3e6423bfb19944c326081c68f00133bf6d8dd92f994c7a84a1802040921000a881c61e5075b519261aa7e5d4431bcc54f43e7d6d0c9282461e10aac763afefa914f15079a716e5a363c54a77b074bd3bb958e9ec6f8752cc2f6742ab7ca8ba0e9aae1032e5df3fca4c87f39ee8a49e7b34fc73d8efb2a4798c0d76914331a8f5d38d137e1bc6c134f5a5c301a6121ee5400e2c7429a9ac289a62d28257c86105e6aff5c4a6cd38ffaa907c8dec5cb37183faa8a09ac51f99d2e73abd29b07f3294909b9302e24307e19664e5954e14ca769554cadb50613e50306a2c2d32fdeabc779e8010379672d238a1e49f5144a713ddcb6942499deda99363717326d8f1b76ba356acb897705e7e0eadb78ed155029a174363a9a464f09270c88ea1a57c3e56639090e308fae893f14d8c58e78d8056eaa77817c1a4e7c44b8668512a11caca504b2d87e06d4c39c4e94a0e2194527a4fa3e3efab2ac138988a0a1a2c9d384b4b180730d49439b641fd8f4c2a0f0fe0f84529a3ddc6ca6019937c51a6d5ae9c84fcad642f4c594dada4eba8ed182fec74e9556e6310aa63bb40066f192164edf9ba705cd4cba63c33e2cf45193b657a5dcbf11e17a8e4b1446dcf47374e021cb730785ef2bad9d49b6d51becda516344558b0167fea0a6af22c93e5d0029d59414c75d0a42bf7400b70cce298746fdfc7aa64321963458043168c2acbf0f9b2b1286889513ff2f6c72f3860b188f78a991577df64893b008a80e3154517b91d665db329ff200e571cc7e546881f19bb4e2561a4603249028e56f4e9d3bc8389967aac6432e9d1041cac3845dbf6cca4e5c00e15ecf8008f32760443760c0e4c263f7ec8901d2305c1b10a2ede865c12cbf64c9380431579eb5dd8ffcbd176c4910a6cd547f792dad0a6848aa2253db5fbac16709ce2f33a5121cf47e61299c2f05b0d152d5796e9944c263d26930de02885eb257aafe29abea4ea020e52f0baa7e7e5e4e8a6b41a708c82d9945b31c7bc29fb248a34dbeac61d2164185328faf410a682dad49e6d0040010728d0a9d495d8c8ff5fd227d098d4fbc266a9baef0964c36f890721dfc2de0943463747b9892795e6c4c9cac654e3998bbf89abc733b576890f91264ccdb3176794dd7599583dfa69d8139efe614c6c42ac5b1215a388e8127f65cca4df2aa94cae254ab9bae7342fd427b5129d8e9729c720cff4a7c477dde26954d218dd4920379af0d1db314f2a09cff34c66d530ef208ac479ae53c638234b9f798306a2031c904874126725977cc4213d0895fdff7da3ff1c04f925e070841b45835ad2515fd146a0d62d665a7c5af50c23d64bdb92a37ad386cb22ce547a42e5ea53de28e2106eff2f97e3be89309494cf1b5fc9cd3244a0eaa4a6ac995752d521f679f354592b456736446a5ebfefa73f57670b71b765df4adab3e58c1a0721ae2b99cc73fe6aee9841fcda15938dbda8780c8720ee982f3353ba9c4cd71fc011081c80e8e4c89eacee775bddd7033c823c0c70fc41937e7946d4355a106160c705784c263cc6c0e18777573b5dea7b90dbf701fb146176a36c54753e64ba19cadb7410baf21e7ef5ce51e3895c19e9e17f99edebcce6fa6d1e5e37e95fd253fe9ca278c07a836c0d133a584cdee12ebd319a654634867668cec34a6933359eaec39d493dc988efe841744828ff24e574f6aee7cc0195ecdba35825cf580e977ca8d24dba4a832c0ee89c4795cafad1435e24c0018784a79b964b4988fae90d6db2a0bfe3e9c90f931bd05b35e9b91beeafb78155b9aefadc291f72367c6adc7aefeada837c0d09cf6e2b69dbb377abe1d3ab3ae319ced2e734243e3e2c874c96b4b8683068116b5ba1f65fdc33d8f1313a2731722cb766286ea964d23cb669ce96e1ec790b95e9fded2b19b29ce9f2c96c4997ac6340ef5e8fba9bfe282331a8a937a9106275848cc280ca22c22f98c0d095fc8ff25fcba2fd822932ae8557f3b42abd80b693ea1542e59f521734ad0a1af7a2589e8f0b2591fbc16478b5b7906dc8e029c45f67570b9f960c9df24cbb439305467afd99d2ffd1b482852a85f20e291a2ee75740c89cc60d32d8c8a015529593f68ef765a7ab82eb396ce69afbb01ec34185520a259e2ba97c39f41fb213c03105c4c7cabfb1bfbca089000e293c1637c58690052cd0411436ff60165f636eccbc6432994c26131f656c8f203ae880030a9c5f08156b6c539ab36432f1058e279cd7a2e6f9fdfaf5e10a7a0401927a8cd1ef650801060e2760223e980cedbf5f630f7034414bd22a3d6b76cbfc8e808309e5e857a242da7adc3ec0b1842fcbe98bfccd127028c15c49af2e76e828ab020147122a4f13b30c0d637a2d4141057020a11cc4427aac52413e029bcb4a77ac55e6de8d50e5f5c8aaba08c9be545f394c846305bdf6d3327a221e424abc66b97d38294a1c42b03bd52b08bf68f605239d6263c73931221b054696572be7519583b6fca268315f6b955b43857c910c3298f7bf8af4a0ecc559ea4b4fe78df9c4bcd8767b5359cca73fc55da052f6b8125eb96b92b13f26933080dcd0852745e6ced6a719e7940bc45b5a7927f5ff976660093770510e9accbe3e684d16e7a1834581226edc222bf77c4afbed7efb31c63823888f0d22860e6ed8229115b7375a44841bb5f0d233a8b0d1a4cbdff631069b51460f1d6c901e2bb8410bf5b2e5a9d9d80a9f30c278c00e07ecd811c6031cb0e38c201f060f1ec598c183c70dfa4b446e7163165aaa9453fa4c217f47e5862cfadbbe0c6731e6bdaddc88c517c252e51c2cea87088024dc80c51ee492f08ad3633307f931c6581e32b8f18a2ce7182af43e5b922957b442ab20ce3f5f8c5810b3e2c62ad6acb26da6f6dc3606c8901e434a6ae0e39f0340c410c10d5598b2deeb1aef7a6572e0462a5a8f1d47a80c423fcf1ba8389ffc10f2738d368508f90e32643211726e9c624b3a2653c818212eb8618a1ba5306da7f74de5a72f850c39dc20c597ff9355cad49fe591d12d08726314ffa70a55e966459ef2862894d310ab316e36a1aa506032061d35b5c624561d62c608f20628fe54aba0b5620899926e7ca2f8593c57c7f6f4182b373c8196974aaaa793e8fb9d28d7afe8d21bb4babd9ce8c3d6cc48117d3f75139f8a99ed1a93d8c954137a6899a98ae2299a4c26fe4ab594ad4ba6d40c264cb1b5e2684db94416ac369bfe9217942cf177f04c9aa63c5db412df568b76785ece1ca344d9c3a5b7bcba61443589f2c5112d6f4a8e852e89525c4ad9d27c278dcb98483c3279a718fb14321bb8010993f0f21c43e3e51c46c9c18d47243d47cb7f319756fa8e50de94b4d0b14ee9e46984e94d778e6bb83c1a3302e1d9e2a70cff418933083716f1c8a5c6ca193668c714e17ef5a80a6245ee7349b89188741e55152bffe50622f88e2316ad479765b1dc3844d9c3575c8f3a274677c310852eb724a62fcdc8aedc28c4ad6149be866c7e26dd208469473e28997ac4fa350833cc7c104b1a3a1f3bc20d41ac1efa4408d1eea1e7c08d405c42ebfda891eb93610f6e00a2b8772f32cf0815a3e80f257516cb84d0b5f51c3f349b1f272f5acce49a3ea4934aed54ea67612962dce0037a6d7a4956f472508f31c66600004ab8b18742c685521943d66d7c430f8997ff4b9e5a837eac643219c38d3c78f9d399e7af7966ae70030f87ecf83ea5c1e454a737ee9087d9ef7b7d93e1d9c1d229e59d13bfc9a477a30ea5da389a448476f5890e484bfd3f4d356aa3690e65292f4bf39df6679243e6f13cb69b5750e3c7c1edb92e69abdd353a1c0c22d4830c95ff3bc7df50921b36c58d79e11a77c3297e54b5cccef3a9db60cf9f4ebbfd7c9ad46c48e8cdf7651fdf54e335ac1f3f45de3bf89e560d6aea33b9bc1dba739b86caaafbd63aab36ad6878f46b334cb67f7ef50c5b6c79ac0cf24e76d40c79e6ea1cfdc95473548662f6eb9ce494d8ed4906e38b6cb9bd6d9231c780988fd621ef94744b8a01dfabeed0a2bb328361e0b27b4e61a363a78260d84b6f86a5c725d1ef0b576b8a794a85729fcf0b8ec9a5c5d6f872f3752171b6a6f45470cdfe00c00837b86034a12e9cd0a2edfa16ce2647a7d469f92f6ba15c3f3e2a8cf96f5016aab91493529684d09c8305b6cfd337bd55ecff0a957bccf0efa3e6e762853bc7fa0b732647a5ab90ed6651f2b64b35a5c295ff825ef24b169d4231bbe88f9bb2769014b8fd511ef2ffefbb1305946f5dfd5957e7a4bf018553f8a91c3ff384e2c8d4d8d6e557ebc1c10d275479834cc2572fe34613cca3c1fadc2e5ec7bdc1044bac5b8afbea41965c32998ce0c612ec0beb9db759cf746e2841d314f27ade19749e66c8e04612aa386b76d7a7833eed061292a327af5fd92e7846f23104c88f1e378e5065864d41998ec260c10d23202a87fd9ef23609378a90af785059c74496bc43840441c20d221cb5468c52a23b3c3c2564f8d831da083786500e5e25ad5faf3794811b42d82f4c5c6326b5de9f92c9248c07ec3868c60e64c60e64860340602318368061885eeae59229994cd8c62fd231bdff596fdb32daf0c5a7a7f6bb2f6ab4936520888d5e9ce33cd794a9cde9cb800d14d8d845afbd3db66bda29a92593c9ffd085717ea3baaaac43ec2593491827b0918b64e56cb59c6c12d8c0853159d8aab05f29bcbbc5f2e139f5dc438e1296fc00628bfae7b4f3e694738ead16a996b7d19f357607959230842c116cd0220ba163b42aef344db3d07a4e2c5e59bc6c5f6cc8023b0dd14f1a46074df6c8808d58a4bcf48c0c97a3ba9f766003168b45d373ad7de93144365ec107396b396af7811a3c09c8f03199d4c0862bf89255d1f3c9a9eba092c9e46cb4424f174ce952f2661d0b6cb0c2a03f4eede60d3b9d5a45eda69ff71b7b2ea954f199d66c29324c818d547c313e96aa3796b4043650918c5d3569a52b7a454fe1e8d7583966e61e9331c5d7b71d7f1b83fec64ba186e8895bcede0ad5fb08220629bc944146cbbb4133532561a8c0c62802c5503df1e39d31c4071b7444e2139e625d76d26515eba10312a9b338275553653b3c44c723bebc0cb31d2d25fdb6101d8e404c56b3e6f246943284bced8c97a961461c837ab0d0d39ecb428bd8aad3347445c820733bd0a188362835569a2bbd9809083a12b1282b159334cf127b29820e4418dafd52ee097572ffd7e0c7a200888e4394b94ef9a6d210a9fc9dd5fb6d649d58884d5ef6d392bfbb672144f57257972e733871fdd031887366d6d9a8743953238620ca57ab92596467515f101d81c0675ffea2568a73018499c2fd82ce18e254d01f0c4f9a4ce58a161ff37e40664f2a7486c625f9e9e8c39e3d9a7e12d917bb7cc00a7684c0013c820019b22bd811021e43d0c18774654e9818ed198450101d7b709414a13bc8e8617ad3c3d9ae7273535f06f5d3a300073af29018ad5d1e2ded4913f150b07613622a9bacd7a06263e8b803ebf99fcefcd56187ce7445936fd1a9beae83d6962b6b16f72064ea201d502327ace25ad036cf21f58e71a7f27b8a222487539e4e29a2b11be888430074c0a1ac762ae6543f5515d6f186f3f8771ef3dc3b1012b00e377825aa72fd36282f7d490c74b4a1e8efa29b31e68c1b6703a77fce72ba1cdeaf5e0363b5336729ac867dc35a2a4b4fbea64dc325d663ea7e8f8f264283d5761bdd72d01c399ee18debe14697cece68318359bbe721a39bd8208b818e3220ea2e9bf34685a6c9500cb1a5d2dc064f338dc1b9a4b347effded5e0ca6cb26d7f9da3fe20f434aeb96d57e25b7dbc1e06796fbe09dba79f717728f2163bc38e59ba35e5893ac1c66c4e918157521357fa5d64a69764b72a158cf66c2734df63f5bf8c2e89ad3144285d5b8408716b0ed8a551773efa8340b755dfa66677c53f129994c32a0030b494da3a6b67ea3cfabe30ac6a534bfa1df0dfe85e8b0c2a131ac499166f943e5e8a88241e7c8ed54e9a0c21666548c5ff9fdc4a86432411d5328e78c7d7629a59825639f063aa4607b908d1936dabb1e3f7cd4e079320923053aa2a0030a3a9e905e3b1397eb9f83ee4e40a676a5282adfe1978e26ac3a52cff45330fd951938780d6c400713162545f63aede6b8211d4bb8dbafae4525437e4c2661e850c217df73f5d35732999cc11bd09184553ce5878f415e2dea3a90e0021d47b83a96fcedeb0d0b72180f70000c3aa0c30824d051041d44e0bbdd4e85d0292d3b256508b1808e21e810022af987c5aa57e93c188756d34c594d96bb0918760e9beded4bc8f259e417be303cddfd88eff63c71033bc4e880482f4478b1b857cab0af27acd33c3620b28b76df4c6e929bedfd974c262df80d88e8422417ee6b9de96873c96422820b4b73d0a2e498858f5e3299f49082c82d30bbca1c841495d4a44c10b1051653a98efb617c3c82482d4a137eea2e4767cfc192c9c40822b44099c9664b6f2ac6264b26132288cc02cf162b66d25154c5a7061159a0b79e2a9b5269108945fba9a49c48d54d32078b7a75b55bdb57934e9941e41576accdd451d51a2fe98aaefbdcd244075915c40291561c26e229e7288ab042d377da9d37135945faebe242c7f33e8531e43bf0061155583a7b5f873c4b7a0019d2e34120920a6eddf594d9e5f598c020820a839c4d1eb7539a785702915378da6de27542353d539c727c8dae14cb66538afa4a2751b7f0931693c210a9ba6bdbd19487a3707d544d769253e929a2287f3389a876fdab168a73fa6d1aa1dd62299508284a0b158494fd4d6a53229f40df345e56d097f5b06432c1838827d0f3fd96648fa6cb8a482790be31c8538db1db2e3a8870c2cd4a3165a2bb4aa8b0416413888cc94dd3338786b1643229838826ccf154ea8aab8f4a9f92fee16383780f914c6832b8b7fdc51ca39a844c262b10c104fe156c3bd9d566be885ce2acd671f64b9318d37a40c412a78f71c4fd8fecb8711f8854a2f2bf749639fc477a6410a1441bec53939bce351ac782c8249ef7ececf095d4764812253f6d72b1ca52562f12e9cf3a2a34d87b9e205197d612239fb289bdcef8104c2663ec8721f208742d8bdc4aef0a768420056598e1af01ff21418008e9518619fe0e80258838e2cab672939ec465762d8834c298e4728bb80a17452d106184c822441411864822d0272c5d6ff36ea8ad0c1144e0b3a3dbb63cffda2a994cc8e816f88f4fc164d2029143f823f4a5fce69c53a30ff876c08e30840c79214284ec080315336347193b02418818a2ea14e386cd79d494ac6432710a8814e2efe47219d583903f204288373767cb89cade53b3194206d169b7cb522ffa22bf92c94403228260e75fee424b5cdcb76432c1804820de689e297c9bfe83bc0f204300514e3b232b878fcba309c223081032ba051610f983c9b57b838a06952d974c263d10f14315bee5b49d2c9d491fc48220d2074f6655afcf39e533f109227c6863063b8b9f3f7d8e6640640fbf870adda093de5c62ffe1234d2661f80f1f1c984c7e00f191830d62c68ae8c14ef152744bfb523d1941240ffb08553e6efbe61af251d2e379e043080f44f080684bf2ee3fe71c174e266688dca12c716d172b8d9e731bd8c105113b2ce3a16cfd4d2566844104913aec796cdf44a793f7f9870a5620428735fb986daebcf177d9c00e316c20328774e8ed5ba91d59edca01cb15da262f55e99cc6a1505ff2457334abd881435f9d4e8fbee811577943baf623fff3fddd76c3d5697ac182fbe98c9588b4c1cfa5df3dab272d436c282bbed774fe2791de1a8e2a26436d12dd292f2c105143ad2953c4aea9f84cae40240d8d8d7effe4abe97ff30ff000801344d090c7351d1d43646e48c560400a44ce2062865544789bec4cb9ef922152867456d3b7ef5c173ea5c40c1132584ad5dbf46589ada964321922320611319c4be7bd8c8fec36a53054197d45d6a64ee9496040c830f522b3851fddbf806b9271476b0ab3197bc1dc21d2efb45d282e765cd2f143c449b9d0a5b0afbabbe1b1cb2da4a5a25c9e1cb560ca23abb1d4a7345316ca23c22a2e7a32113a58b037fc9dfa53f964cab90266f3417f6494d0dd0a59d6ef1c63f941d6a50a7bcec15b53863b530b151253b110a629542a9f9452b71e4624853b2be62b69d6c755251f68912894b3dcc7ced430425f3ec02250c07eb5f3e5cedc614d4f3873f509dde97b359e64fc900ff8107182497ace7cf254c5cd7b137451318b95ce36af2d133cbd1dc4f8a7762d511862882cc1caf8926aba77e39394c0cb855e139e4b7dcc93d025dd4f5d393354d5484057f2143f93f07d9b8f7057b07c79cf59cb424670e42f3b25bb8a3996481116f74bd91e7b22a43abe1ec4758c3df910dcb9955791bfb4d9161102326705954c188cb467aa393395e232070ccc2b4e544688a765bfa8845fb6e019470953f1c55e9d9626534ba954f1c0408e5e94831e9dc434c69cb2ce8bb2fb8f65eef82e32752aad546c73aba20b4f5aca986ed5844a5b181f48428e5c9c73f39250bebe41e5940cf92093890472e0a2a0e3667ea997d3fc1cb7287988db185247a954db8251f93388968b1d460ee4a88531fdf6aa06a995a5428bc3e71af7b81dd3a2b36094c7cb993d96811cb2388cfaab8bb14dc9dbb138a7d3dda675d2ae6c58a47eaa1b76324ea9f91557d4cf9dc66457dc3f6649462b5d27a3565c6e417a12f55873650e56fca172bf9ee6d81ec71cabe8e5b47f780cfaa9833954a1e8d254b3931a45a6409023158fd2aed06763d263ce818a1ca7486658ff74a663c5744a1ee00015e430458f1ca5f0c2c9a7f5dcf1417c39728c821323739299a76a352b0a7b73a7349fe692ec68280cc24789facceb9a41a038a6cf1de3e3f6d3743e71eb556758cab151533c51e8cca5f568fa3e5b8e4e1c3ec6bb6022637acbc1895484dabfa88c856f3ac8b18952fbbd8a86c9073934e167b3e49e2dcce3ef4ca437899a114268bf71cfc881897dfc4b9c0cba4c4f778974d9c68698b398efb343560342725802ab0f572bfdb7a5e2259ca312b5de7534b9693233c9851c94c862c8d0dd243f8327719687d8061173636b0e497417ccb25dce4a55758e48fc9e94d6de8dfb8a411e3920b1c51c32e7f09fb539e91149cb7a4a734e2dae7947a074c88ec13b8d308cb8f5f435fe59933918b1c5b10f93a93223c7220eed1daa3a844c1d838ab0ee4ec9aaf809428e44f4614d537c798c5e13229c75d1fa0f67e9af3e84363ae5fc4d77940719c2af2443e756aab32a7d90a310690995537611134a5e3baa043b8a043b6a043b4a043b2a0453c841881c837053adebd7b45be6680e411c72214ce8fd99b4d63fc06332b9408e4070362a6ab65fee51ef167200c22c2adbb9a75fc37639fe90e6d2a5a5b488d6ef73f801d1286acc2a53d2a51d21471f4cc9b4c673df6bd0b1070772f0c14a4bffeb9b99b762cec8b1074e0679f66f7283bc981de4d0437a1e4ee78cf7010ed8408e3c5c3ac39c8a0be5c0433979c8e47ae992dfe90ecac5d0bef1f73f45931d0c6e77ad7d3188ca7f1d0e1319a31f9f940a3d1dac52ef2da75fb4be9f032294c94ff51a843ac901f9d69dc53b3e35f538a45d77dae1b3568d1c0e5fbaa834a63cde65fe0674acec1fa782920d6a37a8ee9f534aa552da4db7c1313b1d93b8be0d5a66c3e17a1e45676d318fd79092af5416da1553c76a703e745f5e56b3e8a7415f538bedccf974b46860b53e835e13d22e65cfb055c9a0f16b3955b666487fae7ee4ef828a6b198ef29dad4b769d9ec8808fec91952c7d4af9183c53e97b21e7ea321243e6f1736f6d76cc0c0a03734acf6fb08ad115020342bc4365b6cecafe0b65fdcdd3bf17bcb3e423527b63b0bb60292183c8ccf441c75cb0b35b78d3e695179339b69050fa94f4d0e1736499430b25cdfa669d4f4cc498230bc9d1767e05437688901e6508e12103925fa45fa9b7ee44c92b55c964a283fff13b20f145fe3f428ff82c5bd1954c266690f4229dd32a465356af42487861659cbc683bd74e9b92c96417e56c11966eb7f5c62b994c74612657eb10cb92cb7a10925c3032263368d8b8286385dc5cb7f31e642593c9197f831e40486e81ac8ec83fcfb639555bf0a3d46bcae823b4a79041520b2f4c56c694537b5071c728630c125a1c4e89d04a99bd4eb36894ec327b51f9f44db26073cebe0fe71966203f7a1c92581cc6c63c2bbc3a4c47031258d4299ad21acbf4d3e7bca294e25a92c973bb5bee0624ae3866337efc926725632bce9bcffb4a93dce6838415d689d5c69cae17a75ec51fee549ccb1e3c66be0624aae863ceb6e96f94fc4f0b490576263f7a19a617747c8cf1437c94e8a0c790af012a90175ea3c3de5b74ff14a9911bfe7b722f35a6b893a9fc2dad9c43f5521cf649e5c6c8bfe74948f149f918645d4ab1d5d324a3e0ce554bbb9890a5531251383293bb9c0ea7f145a1d0f2e8dc32795b6e01194388183c3ac8052693fe7186102301c5273c41d209ac62a8a6d1963e5f6c40c209f3db890d5bca455320d9042ae97569ff1864b2701533333363668a128fc6114834d19ab6beec3947afee4c18c235c7390b3a27a11f437a9c116488084830719ad3ebf8bb1533e8128bedf79dc5bc6a2924b184dd9b17643cb541c37905924a5cef3185d85252e2f14f49a387559a7d54209944b6a562d2f461f126b7804412e65827938e25da2edd9168ece7531cd94c199912904002d9cb27f48cac0f267fc4166c6ef63f668b314b261347a41b3c94a874b249c48d2888f8cfb13c2ba90009230e95ab74636ff6a82f220df7db77d984089d5452468f1e2f24c896c1031245184a9fd06842e6dcfc44b4394686b1b03fca8337204144353245860ca3b7f13f44aaacd2855c9021d0ee212e8af2789014a2137b1bc467528d3e21b4cc26a7fbbe437f3b08d772564fcb9daee22711c423b4f8765797d6a8974c26814873d8d097f4a68f8f79200184b596e4eadbe898ae2b21f9c3493feb6258de58d5fc60d4525d72442a08923ea81fd463f5bfdefbf303091f4cbf5223e25a64db7e40e21dd307923d182ae8f896f7d6838d4a269333008005123d28d2532ea552d401923c2c63d164a7d84a261b20c1c35749a3f85a90cf620ab23f465f80e40e7a8a356ee9921c5db56432994cc24824763889509f2f16d73cab00491d5096a673f8e911212d2474702c2ba987bfb85c0ec91c4e17dad2c69295f97808247228c7346316db2a87d9e3901213966bb3458f3d81400207cc92c68d5a1fde6b44049237389b4506d778ca723e4740e206b4696e0a7fca64f5d700491b4c9ea14744ae6d2c6dc96472021236946b7de39d9554aa60c964226449d640a20663e998139694cea22b253ec628a38c1e2469d8bff35d5e8e3997a54b46408286afd2f383bad431a32fe4839c81adaa0f7a4fcfb9652593890a48cc50ea5c9923f43b969f246548dfe6f568563a950e954c2629202183593cb6598af8fc218f410d57a24b4b97305bc580a9d058f1bc29c77761488e902af69d03865d4f75f6f4de6542f3857bf448d176f2a976f142a7c38bda5dc8769275a15bf9e45b1ac66b54245c289c90e7a66b41e77ced286347e0dfc33f089021425c41b205e4e85eea1297a9c38f245a3846cf5ca7c37509adedd8000f922c686b49a58db733e2e41120c10232eea67c90b794f2e50524572836a9b5e9b8f8395d2593c90a48ac70cce9f62a46f38b1de3214286f820a9c2a2aa6df1e3e5de67a9c0b668788c6572d74d0a24537857f3e64be69d47d42452b882d2d59ebf331e7f219044814d5a6dbe5b992be441208102231e1af46763b8479f40e2844b7da9bb0cdb39772369c26b723d68a7f41f362461826b9d2fda99c7269d6740b2843c9aae96cd24a4280448194320510227e4e8144d53a9b09533820c190204080a4892b096272d3a3b28d57f3a020912ec70c9ee3bede9391523901ce114db5a29a66869ca43628454aed374b1542445f077dea366d1b4d2370911f2b49027f4544886a076ce569531e22ffd3b7800493f488490a5d27cc1e36a90bf1923c1a8bd5456bb7c615793c070adb783c5c818464e43467e915f095569e16916eb2023beb0638c9673c7f04a0f2a61a417a8290d2abd92281dba115e64ff7deacf3ac87c590f23bbb82c3654a79ce3a1426118d145aa3ce7ccd233727bf4000fece8c00e0eecd8c00e0decc8c00e0cecb88003782417c9fa8959cd2978698f0b3d7b94cb7876e104466e91ba3d4fca2a9ccb6f0b7367f9f4e5c9635c3b08901e41fe870e3830528b2a5b527d23c5e28cb4408aaf56ee101eae2b646c902f038806466661d021ea1d833e657ef6604416e557d61deb5032e7782cfcf5f3b8974c4f285d8f238cc0e24ef71cebf2a86432711f3dcee01e6108791f41ca10e2011e4376b0819157b4759ba2b58d97b0f8013b187185418c079974bde898a5156ec56e7eb611964f25638415b96ec9db1efd195f16a30723abc87ac54bc5bc8fd7255574ddd14e6777dae753a1a7f4e12b5a0c9b3e0b8311545c4957e86d0b3d19738ad22c8bef88bd541a9ba220c5525bffc89d61ec60a414a41819c52153cac9ee83d094543dce0832648c115198a1a9832515c4c51187c298b139d6ea6a5f0e0245999eca643a3d9ef33180a492914ff01e7f7f723725991bf4970019f1c4ad4988507b163f68071bd821ab011e658c74a20f624145ace7442bdb964e9a76b6106dc28c37552a9a674f21d484dfa5fc54bcd8a6b29e0984ca68aad358e81d31f187b473cbd195fdf0125acaaf1565a39fd62c516d8d30213ca9a8d755c28b576a3c090b25387dfea3c64b291b27c1265319fbfd5c0f329228e913111715eb31120963e9d8b134a77bfc483dc810b23ae8d103c8100a8c4002a5740eabe13ae792f7115fd9e80c19b9d16b1c71049aa49fa8f035ee9b32d28836f454d79c87ab6f8411c8daa0ba63a81925db45147adfef57d4d24448112779f173a60bba1d8489b075c36d5fd6b64b0b042388c8d2eaccc29d38307208f69272598f1196a98f18a28fa2a1528bdb8fde8530c91873328397dc5b4708f1767b5f902d7b536d498f2043ca00126432f9f631869032320854fefc6a5a172d9f8d08a2c748203e25c4d346536e513a648605460031f287113f142e3d8fd231c6e0f519e9032a9b8a29f699cbf93dc207f4a8d22ad9541f5333b287b3838de56ab1f7d0f550f08c257ea14dc7d8d860240f57da0aa71dc3964c1b0f272d25fb3d736c71f30e851ef5a3eed5a48b8c1d8e26336992f696aeabc1481db4d04d666ee21f2374b8fa343f656f14d5cc0d46e66052296d6d9cb6d63ec718914325555493a74b1c125ea355f474fa8b958f1138d4c14d78ec718fdda737ac61a3e7d3b2fbeda00c46dc60ea905a0e46da605a86eb9884d0fe16f38611369c3ed5f4ee67d3c35b43665f3288b27d0c236a68aed62c973c133a08d3d0b8a85c2d3a25640c2368e074522a7e051d9634e761183943b2ef2733303ae5301977ebc2481950ea2ea8a6ccef65bd1746c8b09de896d410a12a8b5b181903aaa12afae8bb8d2627c10e0ff088c28818b85c3a4be7858dfba32a8c8421b5ba9e9f395c722085113054a22f262ee67a886587183cc8001284037c18f9423aa5e77dcd315e1af142e1f38bac18eb2eb863f595644b2e94b2c35a4e7c5bf04b25ab3c95a285745b34b19fb3ccc5580646b2b08b7a6cdf2acbd4da081646ae60cae39f937656ae8a46acb0689cdacf31e5d23ca7305205e39ebe119ff3a624f3c20815acf5ee0b6795bb93bc44062353487d33e77e56af16e487f8c8c18814bc1e2ff5174da7bcf551a2831e42cef81bf40032c4c74814d05bf5b1c3b22bc7a1f06919957b6dcdebf2849355d0933b9f3c68b764c40995785f67f8352dea197f831f658c34010fb355592e7d0af708132a29e627f4ba7c776c09cf79ec10db61efd2864794309284f3cbe9936fbd2ba352428311242c3f2a8cfaf7e8b9c42347505574d4654ba8aac78411231493b20ebf3e8d2bfa4811ce9aa5b16a3e4430880d39f7494b5959468680ae24445cc53b85ed478480c90dc2e4db9d8abd3ea02318a7cf6646cda3926de6073a80c169ca5be1e1d3f10b84cb7d144fa6935a42cc701dbee05dc4a59c1a751eeb92c944472fb8149ea2f75690cdb36432c1840e5ebc36fe1b72f363f0fe18bc96d0b18b4cceab75909949d6583299e8a2ad187784a81cecd25e32990442472e30751ebae3e7d8418ed0810b83fd767e87f1f0fbf9c00e1db7c863bc7865b51d948cd0618bd72a467bce3f95692aa9052a73d4246a9a2e3d5932998cc11f984c522274d042043a66616cf69777e8f862ba92c9840a3a6461484b966144cd873a8805e7e9cb45e8a01227e8804541dde87ca63673e8d274bc22f1a6f38dc79b0c177685962a7bd2e95a2ac55a32993c41472bceaf7cc2bf4e631e53c96402051dac488be5307a4c556e8a4a26930c940cf1c10221638c71811d3b767c0e7c3441c72a3cd576b249f84fd0a18ac4ae5355d83e3d0a32b0c3033b52a0041da930bc29a5415cfe4a874dd0818afe4b9a08711fbda49a041da7f0d32fb77812e273ca01a0083a4cc185133bfa442fc5d6525cda4a78e6d266959102cb98b36579df4c0641c728ec1483c96ad031429580a0431466d518663bdeb55a0cc59bff37c510fb1916fc810e502c6b39938ab3993e4e0ff048018f0df040c727ac9c5b55d53f33cab0800e4f946385876f6749d0d109a3a7258d9af45a3ed4c109636f5c934178caf96926e8d8c4c9322815b5582f77d1446a2bac7fbff42f9632e1792c2fdf24d23fac984875349529c9ae8f115de2d5fc4186ceb6e898b104a72fa771755dcd1dae046f4ae8902b3552944d894f9ff876caf96fa46612ad7c4c39269ea3d2b983ffd1a3063f7c9ce124d021093f6c09038e4da824f1823161180a0542815010000083a42e0d00c31208203054288c848201b16c9bf6001400003c3422524e282a2c1c1a181289c3c16038180c8602c2501810088643e25048244c444d991e01ed3860bbdd06ef3dcb1caa0c37ef8f250aa03ed448c1e2bdf10355e3d778d1345066d889f28f16b370526743d09735b57bf51bd1a012020a30a8bad8701e3e1c7856c639ec88abcd1bd9d72c73715de090fe9151297cbe8b72dd3ef1a807453bec9191807a70c5f96c3d32fed108aac9345bb0e3b339b683f30cffb9f43d0e3f2a918f41c6d9f8c4ab4393bbd495d079a95d3ca07746bd6672e65711b3d64ebb267755084f85ae96e6b093505bca3a252c8fc57edc202bc8179bf9c9babc4a33dfbe3275f76ae71c79f5461e6a6bdd14f2bc75c0a42a27e5b208600cd232410d718dc413f2a0216b8c3317a3dd94e0f8128c44e1828c803337b5ca4deae7e99076f966a1b204e7eea0a414f3efc09b236a880b3f437144294989609487f0f0065c3d017719a282b9569d975a31c2b7d347027ab53b16d4972dd82aabb96966ea2ecc99be442a0b57ba62b2565c8656e0651203dc6f66a3fd465c2f35ab1ef556dd53a3cc3b276e52d5ad9820d96e93dc0eb39c7b0c985954eb7d07e61e7b6fa6b2554c016aba076f4e1a771297b826282b44e6f5740c79e6820c9252d0a28f3cee2f1e7a1e668fb19aecb999fb6b1c38bafab486bcb0b3ecc1ec96d7a75392cb86846b6c5046f7bf2b2c04613865238b8e51e8d7bc6cb982324e01fcd7e7b4c60bfdf9a4da8afd24abc67310827cc3c8226ae6bc14c1043d577aded3071c0b9beb29eec4dc028b1fe0c1ab4e2e837197d44e31f77b1800bc1d8ddc4ae4f8da3d3926946852d0fb861bc0c4e97053336fb488a9711cb3136c12966835738ecfbbcb897b0cdad162cdf0fb5b643b37c2e750083f0ffc1bc5d95d8b68b2c44d7e209215074497801f09bcedef74e3c683d6546bd38ae9327fe74bbf9676313b0b0eb6185af5e54a50ba633c8e1e974fa9cd107a1601585c591487789cf07389b836b81f65464e5a97eb303a6d8ad16215cc08004922ba0de57bd0bbfdda41c633e3b955edfc91d96e7469e0da73516b6e04f91688778fddd7fe117ad761e3b0670632eebc7e7823d8ab6958bbcc96d816f02bd3a02524510c646dd7782ca334dd3907ececaa3a33c20b1ef00c41d000440b09549db74a688cc15626a588e243820571e340a37f3a64a9fe4eaed54d6d3eb582759f266268034a8e300000e716cf83bb09e0b0dcd2cb1d3109775a6ca6fe7bc8a5beb97037775efd73814603ae09330f3b7622034a33942ce78e178ac50c118e1860913c3df9cb7a9f2c14043bcb2d38aad2106192fad710e6dd6eb8116cd5f9dcfe71fc18e3fdc420b8606d7b9c543070261fe6afa63f6b6703e61f57d0b1e4e3d289845e8ac24063ebd90b8d18bcd040909e65eadab6a44019928ce04d47e8804bbb5b2c4e792f4a4a9939ee12eca478ca58bc49da3174e1be06181a775ac78f0e4b0166864594f503cbc8b7a09b59518ba3d207477871f258020eddcb064ddd5baba3a44a9031f34b3253ed5b6479b2151b0974a00ddc031af55e38530f7891555b1352c22c231b35a8e60f7ff6d87ec00fdd1ab6032aedc1a80e88c216218a9a972ad6bd466a6c027923b823465d2a50ac5984b414e92dc32fe6613b82fbb2bb89cf1805d64d5e1be5e6417ddc1cf3ddedb5e637a296ce7b780380765f42679c61bf01df0ef2ff03ae875fc4ccfc65e0f5dddcda39fb2225fbbc1134a056de2d0ee826308c8af7c79db407c60604b51a3fb5edb130d3f3fea6ae3e625cd0b01dfd24d05ad6be709abde01a4c344a6015a1605e91efa600096cdbaf60a4b3528a97fa241288a6c4b5eecac5fc3ceec5908055f0afccaa61549b26087e44d2b32bc2a4d94c1961e6d1db7bac60611826215af6c22248585205331d0aa9619db503032c7ec84bfb6c174cf5dcccd4d65a7e8e208419f22fa94c403ba2e23b890a7c635dfd366e4edabf4aae19c22b8f30c81e8ae2f40e4667903ca902ffeebc83e2b3438d511653355567eeab81736d343d4459197f07fabf380900b51471f3a8bf50331ed8e517b424e2793634bf63de36c497a01e6a79e53c143edf2be43e5e71e6f3c655f19f4e8fa20eb9cac0538acb9da2e54b45a148d463efc9bd8c8c5ccebff328adb95ad9b532747f156d4c80cbd42cf3a55e546a2bc3a1eb6ba4bf8b1c06b122e657acfd97b79001754b832011ee4814f5423cb8d9b9b7469d29928a016ce5ae117e04b23ac51975b05f1f575b241bc61cbbd017d3df6f42125fe1049a8f72cb77046b92898becd9334ae597a7667167323a0abf120564fe81471a7aac4b24ad80adf33d92da14b608df61b4626fab4f1cec618a81cd10c6b82853fe52b37efb8ae24195bdd6c5970d3d99acbf6488e3cb529e7caaee9c424249132ad0183d163595e19c07b7a4a6dc913bfbeb67fe34881a28467e0ecdcfc17069364e072bee095d044a83191849244b862eb81b4548f91f7b21c4887efd81d47574475e0b52731020f4005220bf2d9dd4e85de1ee688a0fc753b47fa1fa850f1fa62c14053ca86949c8269c2d067b9ce650d848dcacb65160edb227eea3529fab78cf81ae1e8ad9926229f7065eb6c5ed4be9c26d919b479642429381eadb3eeb80e504465eeac306948aeff40020957f0ccd852aa6d73778d83cc6512976565ce9db2aa07ccc795ca8f056c9286c8a875b9e26ca46565f243515462d306b73c2b8944660835ba7484c697b61923537d922aa1dc041d7a96994ce7a0595d334112ca8b833e5cd09602e9edac8ac500291ca6a2986076f228292f214c2873a613155c2bead641fedd6f428fb41588b16a621f4e8f7eec060fb1f1cbe19f5b846cfd48d921a452d6cb05b88a022be56f2ab68f95e4290e34ac2e7a0538eccfd3ac73bbd20a0cafda8ca7d3172dd723f8da3f3e40bcce2abadf6fde9d3d299bd3e0a9deda691486eb22e5cdaf43276f91cb734ea0149f72c409d7049efd88e029090a0f4197a865e2cb1ab6d46734c196c07cd468100c1882ec21903620c47ae3560a3553541ca5245dc95f5f014ffedeb680e4141c53f8c191664d23bbb2fba152501982999788a4a474109a3b011ab06a764780722e356322fb4019d4406b0ea1546434ea8bc0f3730049f971caa2f812b050f1863cd4a3a3fdd8959d5c27e999e8f1886556f4210e29f9b52571321a5930dbc533b44e5816c46243113824894d2bd05edb368df39172a310e24826e7c6c851b5c72d6a5ea935c3321661952078d550ee92a31ffd00a8761039ab8940b54f4cae7af362fe85d100af44bcae49d6bc1eff472fa1af0041b649d99ba64440a700930009d0a29f7a73103a41ee78000037117202682555eb20118df36b4d1004c624d1baabc4550c05d1f2b79313b9c8485b2b3297e3d0c9da09f763e8a410356ad40521f28b1c287b6cf5a08884ee65e1e4374f0aeee5d4b42513c5fd3939fa755240901f85a9bfbab27f72a8c0148b7ab98a68348ead0327967594ab57d442d558e30d088711aa2352e8dc1b3d53033ae62a68d7f9d52b8edcd04f29757a654ed7385844db2fe35f525887c2fc6c691a3ecf4202423f6af81962a9a42374271f866d9320c342fd2781159f7f869c3980e84d636fc1ca0e33a501327636557e76221d0cd47e44416e26aeb3c9e4e9430f329d8413a59af77d0929d26ffe05ed79925c083b967b787c1feb0ddbfe1b9218ead32e00de0be25236088f71b910631191ada584745fa445dfb9c9697b080e99e6a83b1a77d6d92bb8a8922344fc1ee80a558ec29448357e4ab9099d79386a53afc3a3205608dc41ad05ef84dbd6067e2047ef21ea43224945645a4caadb449c2e592c4689d2489808a08f59af975c01b505dcc67aa63c7c0d251059a53635b91fba514cbc6e925a6229d71fe8b5307fc72f690f0d1be2e16fbcd3d9dbd4ea770b58d41f03b6edbb894d4734e28a5519cd56f35ff747e3c837c496b12f6a2969da21656ef41e6e2a56e340bebbc301301e9b644848b06720fb85705a00b595d3710d42e0734d63040510f500ac700aab6031d006c2475a23e3da79efdff83d7798c24f095f32d629d2078c34664395e3920e06eb73a01efce2403315610a18ea27a467b860e937bc6751c334fa261a8a781b0292a7e2d48ec08059f72708d0a14b786e0aa55ed30ae7fa23099d12a94df8475a0d2dda7eedbed2e6359d972d6cb366f7371a938fcf1e28f2c96e455cb3f72731e805831a711223573f4756afb6ac7f6190085324a11e1f6d5a6ffee605e34b353a1f234ac4d86466e01f51049081f85c3aeef073916f82e40a411834f6418532caf91d5dfe79f47d91eff57426656adc98a38412e2e9d6e7d31b9aa4077ed704e768027c913ce9b24bc6b6f6616fb0d12bbc7cb59f5049c50bde58f42b304b271bc054a53613571f22f22bf19df66dd74d7074ebda9025f241eb72763b7b8eab89efcf9612447c2ff04ca42fa849896e4ccf58e11616546f7ad39d9f40e8d64aef3a94d9b26542b1630aa6aac0e13e810c4dee21d892a796ae88a65131c95318f7a87cc37ab87b2bb720490af143cb173d8954db48b4adabb199186be6f20e4f95d62c835ea478a4a34e7f502827dd111a957ab46e86be0f83d69d5b8be5b4b09a7899d699794f1897af3148bffccb8a190077747f222ff7268c93cecd7205e86390314c8ecd72d63fcc59066c9746bdfb70097aa765a0574f4924e9be6965a05e42d3daa5d39bbf52eded864c37bac8be0fdfc1dc915a3cd49030abe728a03279eccc612473bb13e3198987004d5f871433623f12cde1cdae6a975e65afda244ba99a222fc579eb2a0c5e48c9d64ccdf43a1f2fc77eca4c218aac56a249c59cd05abac4fa09dfaac39cdaddf48804efa3adb3ec67e9b577a52f63e9a8907378b8f4fe191da17715a758a554f2f11eed6a148c6a6325dbad70b44cdb6c62d500a27718793c49c03685d90a5ca6928bef6c0968e1d9905bf8aa259e1581a2814d22e29bd42016ab52f295305e9d079f4f65b6e8299c7432b37b35a244cd52467eddc653f8675fafa7ad98e32729e570640d247e796877f011b7ed3a0fe0d8a38b7195b78b026f6a47a759aa7c87315f5a20f6c58a555b271de67a89258680831690e5c242cf24edf0a1f2fc82385b09176c28be7a2623cdf3fe45e4baee4f23b242881c719ed2fab73c249154f114407c0e9073423aae4ce0d15d61594a005ff129d2936dd35195fe53e85f2452120d2370144813ca36adcbc60065edba5d26e66095de1b5a386c914d4501fa8b52d1a90529958a1350c5646cdfbeee01e1823285d4008fbf7fad20902ff92fc8f4d9403e2ef2a2dab23b98628b8654c642a6c0199b864df10d743fe54c07257d61da42124c946f7c270d21cfe9dca292b755a117b2332a3ca6c47ef4021b2d2bb6871577e9bd4f96bf2d357841a817bab2edf093188d07ce8a71ed60bd346243ca6e320cf702833747bc6f3c8f6c90cefb6b610786200241297fa419a3969d26598332817100dd0612444a8b9ef1ae22a77519436e5e63cbf9c4ae18233152fdf8ba0a6c46f0e47430de6e423959a336d687e7e9e503d9283b3d9a6fd2ec872b336019b0d1afe3c3fcf2fe742f99072b3fbb6674abaff083586b246a7ff16b0151124c866886730c4eb9cd956605621f37b65b1b942ac626836f454c18779cd80fe087a646101278764b714f72741b8357bf2b7133a380f3dec9cd309441b8a048e3cac19ce3983a1146a3845aa894c5b0f8ce01ca7418b25c36fbc865fbd5ea3ef5d3a3eb55ed5c87511207f56a43672e9e85aa9700def4c97cec9a4f9c28f7f94097d316f5f0b604b6445142d8d6a220371c318bffe25020622c223cf66645ab3d4eda710a31eedc4672bb8e81298360179b127bcced23287a32dac765c4bd381e893ec3836426fbf11b2f3ddef13523f21481b71e3a2bbdec768769da696c6f89c2eded81453fa47f8d7825feac90ca30a5d64b63bd9f29ab5a27d0197c14a7bd50ef3d357aad8304470be45e6fb32048fb230ddd1b56a72d1769b08fc40631ec39ab8c3cc4556944b3a152b03f0f799471918934fd1c38a8138b1f6c9e13d8cefac8373cbaad214d6dd9bb3770ff23350527b281bb99d85e6499014c4ed9b0d5944a1e1bb77cf72e904702132841b23b35a063146c328d0e865d9d5799530dda5a092942855f42de3314f6bb83a0edf1164e829ba1f098ec899e8b742d4dd0c9c53d393aee48553f5976241f13f5e4c467dc1cdf6a129c99fe9896f1d14b83d37d930c8bdb3b1392529005bed3ac876f8e5c1285ce097038edb0902a6779f5f28c23d8a6450c899dbcf63b3d713f19315e27c24fdf4a7c669cc9e2fb149ebe08e26b4ed3d5ce44613b12042979688af75dbb18b600727b875f0aa96fcbdb2254a25b2ded00af64275a045338606d9fd8829a7b9e3a4652b8b2c4761b05c2a5feba7eaabd9e526bac58b1fbfee24c06ef3de7dba2dabd6f50338bbef06d7ca49319d914e37210bc2173dc4e3972323adb30de07e4dd00fc7f7fcba67623167a191019f40010b787351c351a91a2a2d1a70f134e12bd3e72d77874d5a02a7dd619aa18114ba592b00995c9aa631733a01a04ab942871608c189d3476e85d6b1e3486a71ab1c59ae21a9c1f2524ed65b70953b2acdfc818b2a4c443a614b6a04aa2743b90de4697ca21d5a30d16dd23fb9c063b2b30d050686f305a166ba4f3452f36c5917de0241132d3dbeec2a3b0840d4a47f28d8de93d6d1de2325e123eaa7b7e657f3d47cd7dfe1da597f531ce101806fe44d329309a74f0a4eebdf5ec61beb41666390ed3ddfb6ab2df1c980e89b71474d121a7963f975e4f339a8a9dac6cd8c176e3f4dbc0933d2870cdee40d6cb7098ecd0b776780ed6f1d59dedfb689c45df79c81ae74b153996dbcb7972c17cd1197ecd3969cefb839ff1e0ca3d304a6339f900e9d3d0f3490328055800773eb160cc96f939568295a91ed48dc4b60f419c14731a9a148c7b95b27bb28952be432de8fb1b171e072958b9dac690be2622cad893f6b3e3c374796f69f22763f8c6f175cbf931feb5da11f3421b30e0ca0a8f12e5a6b46e5bf9f98fd3c6ed370d79ef52efa9b8f21450802168e44cabd964dedd4610af4d4aed1ad9b204f4d779e4531738ef879eeb9842756496a7222cc972eba9c456064a3d5de335fef30a75ae90d8069ca6c89f4cf16ecc00a5b93a977e163001503dc33da124eebf12a5b08f805bbcfaf010e564a8f557cdac2dd644db69051330b216bc13a165652bea5c9c4bac4049244fe5c0af541ead77dfd3c631003ef86a69e957c4ba3e3ae3043ca4eb621ab236c99c5f337f26bdf1764bf045f5cfd4c2a62b92f26a1823825e1e90b2df364238f1719bc087f4773f2fcc1293d0d133c66758abaee5a8dba4751f10070fb886110d46fc16899cddd31b7b291e71d7d5bda7ee944f95cb6d00c759a99ebd4d6e0bb85c8d520aaed17de94b8d98f72681bcf9e6cadb941d6232f742889d839a1b0dffe41f3a3364083f7a9b9ac9a5fbe3200d0ef8435d9030581d2e1ef973cc2921802cbb3c19850cff7ccb381a48b75333a10576d939f35be902e5a07e632bea3dfda77966d9447dcda77fe6d6c47c2b5f2cc6d7c47b7692be1c1bff6d2978e8b769f521984358245e32e71daabd96b55fbe9284dd0573d4c97106f3fa614a2f4b98e9f941b2eaffb81fc0bdc9249db961106f6022d9f52d79c2b58dda5b8fbfb7226865e1f8e30db23654969db1073cd535022811dcdc148f2cd07ce85530390e825e80b9a2fb2d6b9e268a6244faaa62025a306bfc8e65c2b6cc9d0d0aeb8bc94e7763af7fe4b9b5c956b3825833039862127a094e26c99d9fca100dc5dbd034689e7e710e31313b5c147cdc6dfdc1efb1e7cd61487fb0d458a1e6e530bd0262dadc04e17ce7482f9e93a8c5b6b895a09308dd396c8d894f51944ee968409e9fbcd177c62923eeeeaab2dbd34420f488f2be87ab94d11db624e7fc85405439c4afbfbec0ebaf34c7028bb544f1e9c340371508bfd0a628524676e214db7126c8e951ff650a5afe08a421b4b67b43a09c3ea9c18f1d9a903fc8532290821541296becce589cb4d6624e08ab9d5840a3319047d6ae3aa884cf16f6840608c047ad44032e18917c66e15c25054dfbeb7e17e3a4f6ec67ab43c56495103bcfd9e5823e15c7105e1ab8040cd848a27fb5cb188071af2a82ef70bd3f27b72df3cc66c395fc878e7c5b23ab9c5bc318739696b659c0a0652903d331b68e89ca18e105d2841b00152ea422f26b7d24941c910902bd05adec09ce17a86408ea74db23db75c8a079d92838087a7d074fd92b646d9a07b7174ba84c78603054a8687ecd75a9c955101f56f026cb9d0eb44d9688571199578188bf5f5e9095c4fa7c23f312acc76ceafc8667da9388e38af04dc80365e4a0d0e6cc734e68d78a26f081c9476edbe065b24b5eb2d677bb365366b0a2677dfcfd157fa98a57aafa0d3640529cb5c3fd40fe46fe107f7973fb8bffe83ff0b3fb8bffcc1fdf55ff07cd2ab20f50607df0e36fc8b181af187e9cdf8c6bfec0fc15b709b30ba8d7c66ba1337292900dc458d664e6f6951ab718bebca656b48c9fb12fa2eec02ddf4394541781cfaea000aaa472d263f8535d174e6607472e341700c4329be704ac017886266e0225a56c14432a31ff7f055fb0104d61601fa0ff4042a5ba5a0cbe38da188860d3f1139b85959080a2348edd1c00db598ed484a9d896b00d3a2a880ea040385f67e7f5a209d07c4432ce55f49d37c541fa1517e1811f0571140ab05ea37dcd5948e68b841263d4f50f5bc18272e577594160570ae88984c65f5aeee0514e08b687a1d9149efc25b16d4e4f2ba876a0dcb946721b28bb83e3b1cfadef59fcd0ff9eff475f8ef3f2eaf2336163766095a95b65d54847b42c7ed7d637bb67af886a002aaecb902ed8950bbb538d9fa0fc6fc8245a9098fd68cab92781a391c587a15388b3b92efb63c06a8b13e0a4ab02daf3ac1b8d325a1b76f589a4ff1faf620d5c9e18dce331107d4ee1c39d7c19b98c1199fa004231984721078ec9a0946cd26f00f434ae50c288bb7b86d1cf218f082d2f9a70153bac858448d6a521c3a7346c3e70bb8761fa5d3d0556c766fc3407b42f584b829b9bcfd54fa5aabc4135ee0892977fda2b377a1fb3e446fa35dc01fafa74c86cd598cd15ed6ac0049e3ec5005d2e84f7d6dd741cf9cd0b730b1287120236019db66226d4d6126a26115a069383e251c86384fc13ec0fb6e15efde32d324abfb8af3b1100324f531b6428d9038c9b5f2049363dfde74e7c2cd0804857121079492a7ad96c7aad0b7d98e60b662601e5cc60cd27352aa2f9952ca4ea46f6161ea4ac7b36b08341a43bc9f88bbcfca2a9cb706be19826d5ffe5c40151424e7e7b9327f8ee0b2336983f160bc21296a49903a065fe39a044f2b4d9e188bb3840d341d2de86d398381fe90ebcbf4a84082f058064251174e31d1c4f5e675743be09d63a763a6129065ae9324551f41bec81c6165cb644d36dab263f70acf1e6d78680f426a6cd1c866b3aca86d87914612316f30de58aac82866453cf7621632e71103f4840765188ad90e0f3037703d9300aacc7e1f3bb4bd0672b765aa2daa80a85ca76688b84d2b48c6fd5cae37c3fc004f7397723822a139ba06daf6acc6c79b1e3fb842fc9d318b22b700f9ef65ff2da8f6c47668a7ee13b58d6ff01783d570be02e9e33af14315e47ef4441b5758d3824ea7dd73963fb62bb8297da841e7ae54e962d54b00eef65a755ecf3408b6b6d6986c2fa6980bb8c8b9284a2424137cde447540406ec611080095885e9b1cdc6bc9b74fddf7b40b7eff5c38e8bb15dff4fc497348ae4f1dce078c4de911bbdbf04b5517ff2fc791ea2dae97e1afa17aaf190c5a8ace845ff5195253df17cc76726d0296551cd72cbd326c3d350518da7ad56167d938ed366955b2ea1fb58d6d9358d175f1258a96815e93bfa1ca40159df99b6c736bd4f3ef015a4d9e82332f72340346550cee10536d75a08849fe7e02a44603a8f0f49bcfd0ba7fdc133f2fb778615ef884bda137cc0d73614ef8087ba17f980ff3c33cc2b750d8197ac3bc3013e6866fd81bfa14664f0fe39bfb56f45e711b1be62685a0530fa4e985ea012618c89d72f81039df87c5f91777de902779a736f357af45939c6cd0cc0a5fe04b9ad69bd55df3e971f6b214b930a26d064b230d44e321ae4c392990def03a1619cc11ab96462e0e1af82543a5c38447b2f8b85ad03a9d3c8d9102da0ceda6abe20e61c69b0a0e589648ae85ee14d5d516303d30ab394517d035319a9ebf892ca5be765952c52ad053b3f6e645fdd86927bc5391fb35a6c2ca1caaac9b06869d454d41679a25334d2c94d0499d940c2598e1b0230b85982821c81a5d55dc798d041004a8dcb95d57d378444ad5afd5e3b1dd77a8136c4f353001daa46374077d6b1f9759513e60384d2291cf905455ede6508abe86116266727bc094f65d606a5e7990eb01de072a4a0c2351d46482e3998e9448fbab2f6c993f88ca931e90719e9b2883a7a594c86f3ec588c1fdff024f411059c4cc0ad17efdd091c8aff2938ad758be05837e1bfa1faac649b3a0c32b0c2678088a90d3ac609f5e2cb1c4689a101e4662cc1436dd3beeb279cafa724fcc2c65efd221529e58e3eec00d5c6951edeb781c6a3cae2483171d07c1be9920e142f9d74ce5466ffa2f5bf531cfc6fc2f809e1ec3a6e8ac26bcadbd5c2ff971dcbaf7198c7a419e8d063619601c0b688e1945c2d58b8e57bb9217dccd388d43a1c8f5dddf223550aa18c3c16225f5d4d16ca1ddb99e431e933bd5911dd4b69c80181ffa94e8b1bff73350134603ce13840103ede52db660a32bcc4e7185d5d33989ecc218a8c0947e167c0e20419227d1c378ba51f7fe88784ffe117745dd7dceba0af2e8a418a111c4ee19d24280287102956e4854ad99e9c60550c08cad8839e4928bb3649b72fe9f0204e9cc209e9966b15a862af27b2388288aaa084dd8ba78f3e35c32995afa11b7daab27e87ec47a09e7ca3104104b9660b6f6d35f554821bbb1adfd4fe2aa459255a0bf92ddf8ad64bd9b68f45e180b8ed8c0858e02d38c714e2f67031972516b174616fe8d22878436599e915119960d07d014edd60ef5df677b3622a174e090a059847669ec374c1e0f1b4e1979a086585395a00bfb982251bbb05aa1b40e5a3ef23a40b406e350feee18a67a9036d58285246e95be21b720cb748d38a318f08f2c31d8c6f04ec0cec2e6b25a892a6f114985d0b375553685465e3d59b34764bea92aa2a271092096c732f168b5a113bcd334c5ba7880154dfd422c2182d3803d39e3749d079420a4ce3d2fa33fd9b717bb5355fb8e24cbc5726deb2c0f085a96c5ae93f1fae0dddf7910427377433409f07157b113d81958ba3fb53fc0709497aa9a8ab8b34b87fa698a7059040e3cc0f1e67381c32c508d48c0f0248378d558dacd5b71d9d411af79f372d7c5f6c6af3104a0e03018051e519f8b22b9b3b3004b902b80a6c80191bd2164dd3b9586e465dc77d05fc343efdb05f25b323870d1cd2d9f463118380fff790c3ffcb1580626442769827ac9839ea416e78a01a2bf2521b0f7e17d4419fc32967fc27d81f9465458423d4a08a24ac53a011aa5d233504fbc19496ff3a8bf08a31508c06883211ba9f407f711d586fbafc94c6894227fde1f17128587618c542d9d480be3b1cae6b3741bcac73d6b57f421c5d7493a66a47bebd3dae05dee8f5eaaefd2f55dfafed27b2cad7fe9f796d6778917fa34b770430dcb11cb096dfe66be3bef09dad2471b4dde70c832c01d12176b6cd084d1e59c68b49e14c08d00a0ebec9c2afd04f355efd247c08c40f2a087557e3b7e82e17a71b591cdf78c89d134b9aae5128649cc583f95afe20843858710889bebc5ecb797b2c7f631d02e9b872123a512910b3d6caa507b69e46a749afb99a806b766842d3485e70b8f567d6b08345e6b09fc09e2f8b3f4b7ff8437e294bc14c8ca72dcd5e1d4189f5fceabe79bf3ab7cac3c8ac083a3178b5a8ad9dad0ccddf139fa10fd4e4f977bce118336cd2a7cc6561bc0fb59229e83119a433d2dc7e314f2bc6df2c4313798f0b682f50313f2be00e9e4857976514bad65acc7e57267063fdb9846ddfd39d167264c385e6b1b10564d0cff07e1dc26b3f074a48ea38e0e8826aa5a704d8bec4145843730ccbe9288f01e61e08f7337d6abaeac37b840a20804d112a8feca2c83e1889b321085f31caefc3f64c41ef9581d9dfd5a302caf0dc4867791b22589be16cfcd3ea0f8c737a76cfb87e20b1f00ba9c590a61954c081eecfca0112680b78632d96380083e4c989e579149f46e58f2490710acf7010a559a9f14ac1ed9a64b98929b11fdb092cc750d7c096e79572acaeb9466b89adf283d65544a390f1055cce912ae98875519c518044944219f851a7a51d3be054b5dbd30ce35f7f594e1d627c4245f7e58b5ae7cb787d43c681a110f4cd7c487eba9356f81b183d0f53e6acaea1a88af857340c50c8b0ac9369bea20b61bee565de6ede1602a5aff6aa40beb03cf36c92ce0849f18c811a93d388c5bbfaeb49a1bd8e8dc91cec8919a3430f1c0ae0722860725e6e315a33544fa69b4785145bcdec343e6a7f5b5d12bb8224af7220d68a5183fcdd234a47740da13ec234ed706ed6ed25b67ce6e54e70d75659f44421c4d93a1cb72b8f34444a0125b9f9624e0db00903e59de499f6a70fa34b8c38990cf53718d4c498c8c6d1b22a23d96f01d97074fa058411b5515efd40e2c6889944f86b6371e7f82903ea3b39437abca57d874f4fc01834a7cb40612fe85c58f6373b2a46da0cbeed3ae2efc645e7f0386cde534b46c23579ff3e6792e363ddf2a4edcd6cad61eb9dd6f27505c2e9313139ad60e338869b8210e6ba04821bfa12ad0159b879f9f67f813d960ad3369ec44cda8dd0c4fbb64e800f30dfb4cf5c5c16840e05d552498fed2c9aeca5dcac65e3090bfedb475c8b16b3c777505229b1ec24835752faaa0bfa94ff8517fbe18836adec487ceb443fb47b1120e26434f7557bc0082ceeaf7bc679de70fc98eed29bf461b46a33c891f8d10566076f14847c22e3f6a0f0b8130917549038e932905b300ec4728fc0c21c02d8bcc116b47c561f5a0ec58dad09f621faedfaca55d55d966329641d86ceeb8955273324fee8d29b71c188b8ec1090daa5d79eafbbf469edb52c2a6eaa6625998631ea9ecedfece0955f2d7b8fcb2cbf76d9f91a10072658716424fd51425a80adc8e535dad4e653f8a56cc8bb2c42f0fbdaffa89ba39c8f6e2302431a14a4781c92a181a5dfa8dfbb7ef77877790d9c7cdc1576486fe18ebb35a65c33307be6732181f2d3d857088b29620cd93b1a8e3221c1a0f5d5738c2a704924c2038a5a250417e12a90cf6fc21b6fea56b787fabdebbcaff6e5c8356ea4217091c4556bd3416dffcf1d57a1a8fdae7dbd82f3fb8f4f94a9e41b8b23d5007941694458999261c47e4dd54f9e8b8c2234eecd570625cbea1399ae91c123a57b119ad8a6053e9c48a44a865b343f81efd6748a2f150b29a1147ff9513e17612b7e38cea887f3e0d115aa2c6a9d0640f2d7e614f5d24c34b6bd5803e9509dc944f47d20b95aeff17682b2af7ce58bbfe3ddfc59b7c98249e859847bfe0411f18ea607c03e230dc8c92db4528ebd9470f14af110616564d3c5c9f4dcbe08e9629e1da9b572eb81fb9c12089e124690d5c580dbc1919e21e7d81d5a0d80450a719bc7ed0e155d178588c0cfccbaf892b89e3f25c7806e208627d030e89cbcc652890225b8a50f920162596b8e1d7d8bf8c989fd0a6dced064609c270cfdb725df278fc75d7466e6472461e9c068a7a053c4cc0956b8b6a1f609c5ddf965edf58bdc98a42bbff511dfbe2f10d4362a9490d75f9822725bb9e2e5811e7db4e196aba039edbb3ea901ba81514ede0f130c1b106e667f026e51db30e7069571a6165f5e85b45e6eea69931ac84e30e35b5e82ec61a080ab465f7f1d63ea6e4a96a596585041843fee6b2368de54549ba11216e1fd67b23bda622b583da0049d160b16309102b1fb1e8ef630ffcb79d2a170a0db8ba2f9cdee0d39cfcb343bf847dea42e35edfdd0825f4e8073d592c12f3cbc751070a4ec132c49135de457ce46c090c39c8418f170d81ec64d2cb6a51e11acb499d6f13aa390626f0fd4f10a3a08db4b819f287d954c402563b7e941c2f4616a954f26b0a3618a0ef7a284ffd9f7eddce415f411c944e8276d2f9e172db36d8f02084438e19d4b31a4c996918ea80caad61b8e03d1ba731c49b0a9f381b1a7880978f58255fea205d8f19887600f25cc5214e39d34acd94a51310c309e0f808577f1ec9843313d9254bd377bf7e6f37208b120ef6467f5bf56bf36e5bfbb2a5bf5b6ebca1dfd9f406bc09746303beb3c60e113530aea690033cc3333cc3333cc333460f9a7fc8227d5a5352443c43bd206c992477ee0c1b12ee1f000018b68e663f32b9fb83fb0704f0104811ad10ee3fd250230579830a657dd09b0e275dd2ec66bc88c8105623c89032446e4c61b7fae83095262e05a375ae245f8ab7a538222272811b51e03279e6a84e7cce950b74102fe30614dc8d61529a68512ea62170e309462b89ea70696a9dbd00191ab8e104d7c3c5f8e60baf5572a309d5a74dd9d1a41cbde4e406139693742bd95856b0dc12fafc1c245ee3a526e40677430996874d2a615a52427823097b65d85b8e65dc1c1db9818483bd96774e623e8f01378ee074499896e027ba9d18e137d1643268e618933f430d344870a3086cbce6cef22dd1394c84b3a685cd26f557597a6308d96ee85cd906e4d1f8009b81869a800c14dc1082bff9634c4a62df98f003c20886aaa151674f1c026100431f5fb14af26c658f79208c5f9c9d3b772d96449d1211911f3f9cc3f0c5929a5eaadd35d0b88088c81a686809a31777ee2d29d8c7c86cb31941748080307811c62e1657939345c564267976d01f1011110282307471e52ea1f3872dd9c70fcd81fb40e3c2c845c28922a13a96b13b91910111917633d200c2090012062e8e4930efcb25470361dce2cf7679a5219673f4b648c8b8a1d3bc4d7a5e0b4af6a43177adf5e316088316e710a9b615bdf3d29bc5dfb972684ed12f7e4910204182042961c8025795970ee5890526be873a71d2a379e8030dffe1a3d1701fce66fc1082813060915dbe3c1a6c0ae315d954a79bfa983181305cc1873f297aee106fe10259354018ad3065d23a4997c7ce222b0e95e52eba8a27117b44446415c60bfbd8d1244c8dae8a73d8922de6d7f856d1c248c537ae41c42429f6de405061dd998919b55937c5bb819c62d98cf7db91173f6d270c531ce5637e464364ace88888c88f34ce30c3477b101d31619442b5323dd1849793d39884418a447b999336339ca775444885310a3a598ade9e0372069020695c18a24822aff3c6cfee9e93230e082314e71333884929e95df8233e800c814018a0c84dfed3d2923851d1079b31448708111111e243d6d80784f189a4aaba4c93931686273af5246f97fc9b9befc449d3c79fc99f13f87e060de97027f76f8230eb8f95a348ddfc9ac0afbdf56f73ae347f26fef4292779c5d5a88f09aba2e2cd77c949c8fe12764449a1523faea5bc25d0b1d0f5ea2ba1650a1ddaba6c639a12a7147ef64a8a5142c924daf512538c5529f2e39250b3a6ce555bb277c7234126e17e5aa23a68c72141c625d13e7d4e4ecc1fc1e9b459c6247ec9993be24d27e626b3928369bc11ebc4560aa27f9a2ece08b5939d9c448913165f843154c99bdd4abce02b620df95b91f143e49e884493c42479a7b64ee28838fe9e2062e793b93e44f39e843a6bbb4bb321124332cab97cd2740b9167d64fee5c6bb64d88a4ab1eddada479d21e441f4359dcb42591c91604172da5b98919f76007c2248c67ac33fd914f03a28c278ab97dfa0f85ad0fb3d1dff22aed87c2da4b92c3b57fa3ee036aa28e97f852c9c4f94089f76249fa94e1f31e885293d7d3fabcbb7a285b8a95746d79379b87bfbf63489570628c7828ad4aaca8de1dea249b7267f195d6ec606cde7f175de9a4d5c1289eae3a3e89d3313a987be365eacc25f7660ec6b1cdb1dab01c123f31c6e4f3c9ca701c28b75a8b1293946f311c8add4a829c8949ce167e03fe3935ae2dec86f34ebacda85e680bb721a12d564939ecc74fcc062e4996ab96f3f24cd6b049f207932f295a2e5103e272964b9c92bfcea601f1f8a91be4aaa4b36838c55fcaf9db9e4113ea46538e8b36a9354392c70caf559e9b962d4397423c494d4a6cca9201b7309f90cb7d311dc3494c969265f2e725c540c67c5d395f1a065532d4c4ae1493245130d4394ab62445d686a75ff84366aa6c57ecbca91770792939c66c9324d32e14566d3da7ff7757940b7fa9584d4fba0575cacaf2564d7a522d58b1631acbe9bb71d22c98f1275f366a2aec584853962d8d9a4f3ae12b2498b82513cfbe165b810f7be9a5b9631156c18c57e69da4adcd245448dd2bb46e7df02a9982d395c72bebbaf32605aa43424fcc9455b428547a396325f5fc6aa0c0499ea9b3d984de4c3ea1cf3174a76b75d8543aa1d5a8eaa5a92634f6f2398d49e15f2668f727c76ad49895b28424326477ce9fbc502a21b1f2fdd6e275fe49b8c4afde8a323d9747026b21365df523589ee4958e1be1cc8eb5d12efd4b16a1d439bb9f9c10e1ccdf29fb4f0b6308e66072497daf692e06c31042b973926389d351e382c1d083f496dcae29630a02c32896e4daad4ba7fb2f348faa266682c6c7be484a72dab0f1cfab6e2fd0b7d0af31ecb4b9092f94934374ef59eeb7c92e34b53fa9bb2f3eb5892e0a9d622e0aff51e99439860ce262ab101e4d4a624a94dc62bfd63a5963deec39892d18118d2927d385ad925a1c1197eed67f33a384164698dd678fa633791685383ff9a3e7e68f659145cfb3a32299e21b0b835de7acf4c2a2fe247293362585f7159f7f5412e98a2cd515ab7a5a561c5ba1b60955712a65c48715495e25557792cc5f5bc5ef513da63ca94d8e51c53569e25ac65c2af03c394a9749db561d2a7cabe05912dd2934bf942bee847df5a6286aef322fc713ab4b9120aa25b9fbe8984b8a2ce70a17b2379a47477146d56a13364f5e2a8ade3d3f977f0ed6c1509877e7b32a94a70b415178c9526bd9f221fc841fa2a67f323c9d9c278a38e1544c96ebeb7422f91393dcf165d3b7c3097dc35a652f9fd0ed6cc21cb36dca5af5d399a309dc2479d763ea648228a1840a4f73162c07137f07ed9ab61c2ad3b9c49ece93796c3e137d4b586ab2689acabdcf5722e165662cb5e8cd4e89e225296f747c1267c6f97e0a719a95c46d55fde227e5a870243a79a3ffa6ba5f86441bb34e579930633ee2fc5937fef89f55e908fcbfe236fc04bd116a8cfcdc1c3f7e868ce83775eced5757731154a577b5fcd5fc144149beaae3d595b22411e5124d58b9e98ce241045b527dc6ce90c93c44f94bf0ae134faacf1046936225c4bf726685d842a664399774ee1442144a67553c83e0425dcca92113c46361f2a6cb07c2cbaa25e61e31d16e038842bf592a5f3fcbdafca1befe3ff714d3926bfce0a6e8605f62318568fad078f44ae7211ffe2be95292523733dcc3d12f5ccc4acff6440f65f394233b2ba3bc3cd4a9b249a143a5b91c3cb06216b3fdbdd7d91d2c1d13c6932669246387fed26ef0cd5cb397a983f1ec4cccf11d835e0c1d1264c367f3eb18c3c5cc414b428d77cca3f315238724292d8ec726b79a4c1c8e2161e6b939c9f9c3c10cab55251d99e37f032715f2a25887b7ef863ace72f8caf0fd7c1b4cae1a6e92cd863a9be45c360f1fdd359c357d7493e28410570db6e612f73b974ea669d0362ebca5cf285a161a12d3d5f8c7d01938a9f56357493b2589193c93c7737de8f7942b43d9936b7ec4df87870c65924dee1cade7b1338642c5f8f973e88c392b31b0399b9a6bdea4d454188e1317a9b5d9d94c81010fb9f1fe1335574c7d21d1d3c7a2f2bfe4555e603343538afefd665517cc5bed174ae8d24a15178a4ea2745c0b1eea6fc1281db570712d34129fc25af697e859d8ce2eec73ca1419c742b13b95b82bf9e1f7155615cfcaa1648974ad90e439a65cd5b75db255e0734aad269f54db2d154a772da184750a7425e9145be6c454a57087f99c268ce889c928d896bb297709f61f84c21e67395ee21392aa2fd92574db3d4ef843c3a4b6a7096a727bb7caa19d1b13b21cabc45c527c70cd12b8bf18b54f104ba6514252ea2ed3de55d32b09f576c88d17fcaf520509d55bca12ed97ef4b21c011306d4b37d267ed9542002314f455d385264f5e2904288226ff6b9f5872b0a8140210c1b328eaea7ec2c7a81060085a3cd13e5892dcf00920049334954b93243d9b0f4639d67b845becf238303acb9e5286923a56fe8bae9345ddddbe58bbbf8478994baf7b8167949c9a84af4b352f4c499394f24dbce8e15d74a733e27ae286adba2846fd8d138f1f93642ed05799cd59e2e2b43e9f0d5adafedd82cd9824d12bc6d7e0d942fbe85193259582570bbdb75a5df432a78716ff9f76fe3e99459227fb13f760b2cac292c41ad7c9bfeb582cf098a77212636b8d061679fa4e9d5e4e53ff0a63d3bdc5ad4eb25d61a7c6bd9892529b1c8888085981884862a315ed89394253ecc3062ba8bdd42dcbede57d5985e12f9ac4a839c63e33f2e3873e1a6628c0862a98ffd825fb5a8a57290536525186bc8b5ead91f3282aead73da172544b3ddb076c9c8228dd54b1b710cb9b110dd830c593ddae3f97e33a65364ad109dba1e2a3b4fac549d1c9f9a959d4c6c6ea51fc1b5ab6ed37b4f52c0a93a78af9e4ec6679a2a1a8f3e94926c6f425d706149d475eade55446af3e71d6736b1d13e3b8c6ceb0e1094d3dbb58ead6ef6d860849808d4e946351a56db6fe849c076608f9800d4ed8d8449663888f11b226ae2431b261b29189c5badf24139324d8fd98c8db72637cf905b1d3181ed8b8442327c8b8c5d0bedc7f60c31274dc2ce14a8ccc1f1e1fd8a8847d6b779773f2a8498e1265f41424fcedfddcb2868d4950173f8d6f566d48a228a19b2926d92fc64c176c44224d133ded1afdc28790283cc6e8981a2fe3765ab0f1884ae3989614eec3241347e869b2b9a24bc5b49a4610f1a16233fc277d0d0c6c30e2ddfcfa37b397a470116d7fee8d933f1a3c654444240d3558604311e989b904f1e06ab1f28920757d437f8a92eb521b88404dd0b27c825c92ca3c04173dc222cc3befdd51b0610832c3c34fae7e2cf50bb1a546f5b025d5c15e28d82004517f9e7ed169833e8833efa3fce43539ff6e4310e53cd72606cbccf1f62dd8084431de6654b096730d204ad1d3dc4d674d67cb136cfc815b4f521263c60936fc6034ddaea9ca71730788880419810356e080321c70c610216ad8a02fe0ce817643d8e843baf759b305ebd6d4dbe043624e69d59d39ad6866630f8b9cf465e6de5bb26b430fc598242976cf7c2ab1f3d0c5d64da66fd237910d3c50b9c7539d1cb671072d69499a7819ad7d63c30e054d726bd95340b05187259338679ee2d11304481a6798e1a3d1500ad8a0c37e19d53b95699fe532d89803fbe51faff2f9d47a35d890831be65f6c4cd2461cf80ebe2944775a74520d1b70f8324ed62bddfd59a70936dea0c6f5fc90de62b92b0020820d379ce4380f1166193237426cb4a10aef3c92e226f7f436d860630d09b9b333ff73b9d12029708003da0164a8c1861a8a12b387d09c3931657660230d66ecf05c8d9fb4933c820d34e8799f771dbed4537a062daea7d3e0d919ec23820d33fc1fdb5e4af7329426694979f225cf368d88880c41c339889b4044440336c8b0941c65bdc9c247194786d4000d07818d3164e1a3d1258fd810836f9774ef5d1e51011a251011a9818d30949774ec3dc686e7c9b1010653ae749d36c927cac92322224d813328b086060972810b90414693c1be821f3ff48c324444c8d0818d2f149fc4d4120a64800c327a60c30b869e3cb27bc1d26bba0bb6a714d9339777f7a40a36b890989389d1f3bcfc940107a4808c326c6c41edf8e144afa9aa106d68812ac133aaa7339494648d2043cab09105f3d64ffa49dd89b58c98c006161c350f276172528ad238a3352022c2041b57c83ac96c3031a78889ffc086154a1dcbb7569298443644b051855a4ec54d5556de3743b0418544b1f829cedb4cc88160630af6bfa5be983cabb52d85fa4ca5ed2a18053a6f9ea4aa9a4d8e162878297c58debe8ad89f60ca79dd54314ef098ac60c309863e31693ab4e44ccb0a369ad087c9dcba9a2e6476d1608309740eb326b856c4c673095a99c5b08d8bd949aa0c3694e0c796d8cc9edc828d24e859f35758cea555e580321c400220d840029e35e588f89d24e7368ea055a7e6f075f28c894ac08611cca89e53c6a62bb1e2368a70f8e88ec7864ac9930d22d441b37a893977e5a69105d818c22669e5fb08550d6949c38610bc92afffc26609d92920030806fe6d7993f28f8888f81800308a29f30a7392247a9286193a08bde8e70fe9c84272ac86adc9c9fc9dfc0104c8aa400716b09cb25ae67e043aaeb0a5d4b5cbfcdaa0d6888808193aac604eabd099553a4927958e2a1824fde2eca42343d40422222343340d11913586e8a0c229bc25190bdb25f73d05fb735abd92231d5240d4a2c5b40a8f1b71e988c2b9dfe367aebc614ed4018522c3fa8f58b6de543f81d0cf134f3e93b431ea04743c7ab55cd8ec9b60bc3c6a524d8925fec50445efeb2f4d9cbaaf976088ed454d1dda75420be850c2d1d3d585ca9d24962410d09104ce7277e78d8f84e439ed8a3049fcf8a18e239c21ebea157323fc52d931fc97e053253a8af05aba64ba29d2fd3c3a8880c919e3c674e4dde7750ca1e49d4e0e9b246fefd72184fc72688a4c791db709c6e2aaa1d32e6f49d080d17f5a8a15e7f3c5ffc5bb2a1763abc59c5bf751831cbe783ab5b325c9eed4c43790a317e74d6b59b2c950f66620072fec50f94257d677e01e08e23a002202936317a5a7922eaad499cbae0b4662f39b1826be92a00f111117e4c80527adc99a2b09339ed50f90d1ed6800f981831cb8c03c6974b494f2447b72dcc2ac392a9f64d2b6d824217affbc3442db5a9472b2591e9346bd345a648264e449a13c76a57ca47146909115e498057e96991a77ca42cfeb4d259b9cc24f2c16fd6c78eb6469392f990316095bdefddded1172bc02350b4be1c4ce15d70409395c51c941634e2619eaafcdd18adbe4e8349ae44e72126705e35f6fe2e4268d76ae62ed3039259ca8be76a20ac345eb933def547c72781af38ef54d0f15458d35cfebb16de2788a2e6cd38ac5540e53f03e96a93ad75d2c739442d5f5ff549d49e16d4ccfd173398af3e8f4dd6aca191a444dd0851ca260754d9c2ccb512147280c8f493293db9097ac22e400c5a95e4eeeca8ff31f2e21c727b2d02458f81c799a334fa4d9ad5463bc850f7e8e4e68a9836cc7fe0c95737002f32a8bd1b3630961e5d8c4e95fdff2c534b1a9ac652b49fcd470e5c884af1999337a3613338c89e494f22795d2f806eb12a518a2f5b527c9a14c2cc1798999193e5f6fad4a949644f66ee39964614a746729780eeeb937f493c03d7547b7eb89a177491076b9dc677242b527418e48ac214bbf62d35e53d0043920516b709d9335cce5a8e578046d7fbad162c70a262687230c0b626dc2c87f06a91158d409de9b4d663ac7063918b1ecc690daece9dd06722c620dfa57f1444f96d41ef1e14344840243d448038d0f04012224881a1910e243d6c0800f72282247225e132b6d633ce9f385ccc88188e389a1761acfc7c31ec971087a35676dd64b377bc96188fc24d9a4fe945275788290a31047cd317fe2c929f3a49a908310c54d29e7c84cf251cc1c83302d4d6b5f45337a9b4310e6ce59232bf55e620d44f2e85d9bf8e7a07de70044927974b43bca8bfd39fe60ae56b26c97ff922d63fce06e7e4ca761da555d8e3e50a92e8365ceed5be27c30e76ca193ea5a8a8872ece1b7ca41dfbdf35eb61111911c7af03cd2cfe752b8fa2f471eacfcad299f878cd3090faada989c92ecf92ebd911c77403665a6069773d8e111799dcd52f920471d504f277c928f95a48708f1212a822039e89014a3e37b593cc71c0aede92db366dc4b1339dc75498cf04ec5c1164d1db7f50d21071caa50296ec9ae77d2856fe0c4bdeca639eb4beadd905ccc457b7b50eb9836b421b5739b2cb3e14be279b76bc2b5de39c8b106aebbc495d570e27a6a3049252621538e6dd2280d7fee127dbd5734d0b1dc842d6dbb19f50ccec58f94ecd1efac99612959c5a4ed7463dd6548d2bd9b0c546ffc5bbf110b568d210d162e878ea218ec123a65d32671e9c250fa6a6f2aa9cd0186664a1245322d091a3714d820c71768ed91937c4cf812af111f66680ecc0892860f1f0cc8e1054c3ae21f7f39bab089eedc5c0693f270c472700111fd35cb1cfab15a0320223880103d00e6d8022678ca50b9f2245a6ea406350022823480904044a406407e4820871658e992c743bc09b2d3488e2ca061d376333b436a252407166efba01564a4db332644474cb039ae9018fb4c3afa244f95cc6185aacfab526d4b899b56e1d9edafdedb4eeb961c54f8ce664eaebc64f3438d337ea8310211911f3fccd81c53e06e2c894132a7e8319c430a656ed5baff647965e688022661529be0259b506f0e2890d16c3e93c65ee6648e276c5ee1be3ec6a7cb5c0e27941ea67245a9edd49f3572342111b32cf627a524eaea831c4c30e5cf942b69f426b314e45882a299375da95a668d7790430977127e83070f63f9620ce4488255f1cd2a321f1111a9400e2424a734f52953c29920a201c40c2165e43842b9c395fbdee68b2999c308c6984fad8c5d33f9c95184cab66b5473dc2aff1c44404af82426d1f3d307cb0239866058d3d914eee510826b7a6f5b261e8c3d252943a5b5d6f41d609427d4424efecbfbfa85318ac67854aa993cfa02fd0ee2e14163ae8e42c0d10bcc4bc60413a23a53f7031cbc207ad3e7d0b7d162e542f4470670ecc2d413435bb264163cb5869a00872e38e92e95a027892bd1e3c8059fb5a6d3fc65ba5471416a87bb5b8e6e517a3d293d8e8fc316c56dc6df94e736f942386a510e1b976731bfd297c800072d38694fb08d3bff33d5153570cca28bee8d49cea27235e290459a3aa568b98275fe742cdee0394926e6c2622b2948ef47e64c42f50af56fa3a7a50ff5e1b982d1f063d924ab1596149d399b2559b186694c5319993e7331c0b18a2d2b4f6ca6fd875055f14b27b1df76532a87a9d842efe50d693d7526a8c84ec3832671b2b25f384e61a62c5e2646a94a2177347098e2cab1e1a36efb6f48d5c0510a7673664c8256c6a8a79f91c61a2958630829d0682967516dfd3c1b29e0184527f9a798ead39a5e0c0026e01085294242f4aaed3c774270848292da2cde7878a8d2f0016488ff581d120488097080425d0bcfa41a2291192b2ba8008e4f24fdbb899a5289c313ab5cb8c6132f3b895e14707422f7da0d0d25985882070727124d223c867936c93e035672806313a713e7634b30c95bd216e0d00421a572bfff2989de67a22ca97b5dc238309124175368f8dba0fdf50f378303429080e312ef9c9c369efc9fc5336b0c0100137058c2398deab1de761bbb127e8969bb3143623d85831209d5e16925de080c704ca2bc24ab87cc382491774f87cfd9704422cd5cf5fdb866fafec00109cc4fdecab85baf1d5901c7230c5b412f98fc6669612de0700479ab9dc42e2bb50b03400c381a816be830d970f372393818916887cf21dae2262f8b286649553aebaa960b87223609b5d0a99325315d1c8948be6872c86c3b6519c3810836a6beb568322611e338449f7a4f4cd74bf2d605872190b86c525869d4a660215cf34d91b72684a8b76acec4b83493d341e4b91e2c749aecdf2682e8f44636adde63764f204e3956fb97a4ef911240909ba40b97d6ad289f3f587a524ab152542539ec875b329eb8f99e6a34d60732859e49172cd72e8f041c7ce8a491f3ae68feebf21370ecc1941ecfd7f65a27893b01871e4a37cbbfcfb124cc8d230fbb4942fdb6e745c08107d36eba93eec4b034e51dee34262549276a07aee4d412ab321c7568d22c652d2979a438e8c0855bd9abfea095218e39b011be3963aae8be1612e090834994ff174d39c6cbc781932429544ab182030e49e12dd1de49d0f208c71bf814996366f350929c23222289c30d9f2466cb26a50de3156e031b8a72c998c924b76c08c71a8ed7e94b7c2c553c1c6ae883fa689a0f571a1d1c696054c358673371a0c18cd0acecd1a63967719c619b93f65035254bd938cca0c79208999a12bbaf321cca6aa395546fb226c1418636345487ae65cedb708c81faf5ca7bbd235f3e0e3150d2ef47578ee7b6151c61e84ee8eecd6937369361031c60c04ab772864c72081c5f28cda7edf092279e5641c0e185d7dba48fc1452dd489fa88fa0052031fe0e84229f6b44874db760efe1082830b5b2e4bd7612cbca31f1111f1d12a041c5bd05453a8342edbd5ef8888c80f7068c178974c4cd217e3c8c21fb35bb91b22adc7026525c6141b1ff113a700c715cac152471372435c92471260021c5670b424693266fbda164715ce9456a3677b92cb250de0a002361e4d3b2f2ede53b0423744e6b44a4139b9df62d6340a75beb9de5b9f27c984c249a3d7a73fec7ef809548f772e0f9ddeb34ec02dc8743ac1342b884de863a7c95bf131c1cbccf41aeeb3043d66cb945cdb9284470946b52cd982c4252b394948caa13e27ee61ee1912744f929c2dc5daa76647f0733e39e5cd1b239423933d552789f3a9229853c999c4ae8a137f229c2d599656c9cad91d82a2c13cf57c70082139cefcfcdf969c5582b17974d4b7ce1e29d10d60acb21bcbf74a7ed15f4ec25f4ae48e5cbe30e716b3ee0c927157bed10b2bc5a5bb24ef29c37c83176d90bdf85e62821bbb70452ed74409f917f33774b1aee55fc7790ae6ebc88d5c98f5b379a1926a77081768c65425f974c34335624690143820007eb8718bc4345f29153acee26f0bed5278933e49dea8857362885dd5db033768b14613557ed6d2c5fedc98c5bf7933cbf3b75f7b6ec822ed4b662757c8b5bbb128f233e7eca7a167c04229133febab7849b1dd7845f9e3427d5398dec66fb8c2332ff1c450d28aa2a42dcb33e90d569c842ef1cf3f9d5831bfb18a5353792731d368e6f6862aeaf392d65d5e5267be918afb53fac91733e5a242c51a315fc1a48eb379b9710a2a2f343d9c374c91799233656c0d12bdb9518afbfa728f27a9a762e60629ac943b9e8de54aa2ea28cc713669896215b33537b8218a22367ada93f72adeaf831ba1d8beeda3724af21982a293648fafe3f52776fa09c73f57ce55eb1a55f2c4b93e4a591e753b7ffc30e3063db8d189c233e6f1754fa2fe1972831389baf33e5227d63f0bd98449ca7baab3c99527ac09d23a4563e72b137fea9cea55ac6c6317720313a5d62445afcb3a8fe11b97d0a33a7c92e6bb3b4f416e5822d9dadcaa7312b355674444c48c1b956036a65a3cd4a5d29612bc76a54be9c6336e4c22a9eb3633b12309bf4ac85829d2111191bf1189731473c92c79dca753831b90385dbfc74ca9926534a9c123b695cf3293daf40d473c352fadeb11a396644444440890111111bfd108ce324b78cbb851eb4744447c888888e002182881083a202282821b8c30b7b2ce2649ca8d45f0257b1c4b529f5de08622522b29f545e60f174d129118aca28e6468d9498830c9951bcea40fc106f78ebe53e1af3943983bc6f0cb715943ab42ac194c74555642dc97c49caac4ea5d1f2570631097b9a7b7d6d439981c411843365f2af55236b91b81a0f76dac345c47b713841b80d0c34c0c99b12a4d8b37fe60051355337bf4132be5861f0cd62979a30fb977dda9598977b26ef0a1a09737932495a9ee59e0c61e7297f7144a92daa7bda1874e2d54e8709a3c34a53a3ddebf9d27fd811b78304e48bd38b5ffa4e91d30efcd697ff6ccf26687d56f23a3d74d4a19a3c08d3a5cbe6252c48c454b2e1dd09872f213bc4d8a4bc111dc984327c9a1e3493f8e8888d8c92139a5849eab2a3199e53c3043880a44444870230e7daae499354e0a1665dd8003965dd3d87d9f74de82f80807512f23c890324444d048e30d8d788a1a522674b4714444e40c1f68f88f3480b01b2e31f54d8a1e5d2efe23222213b8d186cd652f2e58fadf15b8c1862449ea655f3d26c95e47988cbab186a36dbf68728b55de8ef810628688881ace496cb5ce711355e9110d520311911b69c8d6ff4fe674d3b765881032ca0d34145ebf336f759d27cb3983270699dbb592536c123383494e492871b6c385923565e8e6427ede7349a95d91c1d7b13e212ea6dd932c18b83186ecf3679fb4e630961422864376aa70673567a11d1111313e7cd4408df50e435de29ee267799f1d70030ca7acf36ad279b9bb9b811b5f2846c787f31ca5f33337bc7050539334c91deaf3d9054ef3494f7f8c046e70c1b06b1f3a5dcc6b6e6f6c814a7aebb6b7221de388160a62daa97b7a046e648113b4ff92381525e583d4c0350d1191338200e1c0b98185a3a59c3449d89c0e5651c3c79033be0137aea0b4d5797a8d9bbcb753c30ac927c6da98734e315670a40a7b099b6a362a7f3e01ff27d881082b681253c5f9dcfda6043b0e51e88a7269c13e9ce88f327618a2db3c6ae3be2b62f911da5108265ccddd24e1525f4d883b5c8aee9de4259e8338d48798daf350f2753b04518ce253d91e0d44dd9ae3e54d5226bd0920d012c552c578d993e5ecf88327dce6fd916e871fecf4f94b446673febf0f56bc1d89554f25fccf07b3d5f34eaf7c9df4ef21694a2eab884b0f89a9673f3b595b2c3f0f27d984ade8498eb1c9c78392739b67919549f377783e7fdafdc5e45ede0ebeb69bf5e6a94ec275a03ec77b55db648fd2e16cf249aa7b4c56e61caa888de1934fec8972d8345344e3a73b11e35074ebfef7f18e29091cb6f0276bc7bc9eb97b032b96eb3b93609ee4b8e1dce97c71f3477bd606b604efacf3e621326783614f1efb5a0b361b5f4376326f935631a68cab8134d1ec644ff13418d6e34e8c9f33d9e468d0f273a77df7c53f833e9feea6375dbe6f06c6539b6ca7d191be0cb966ccddb993e124b758ba27d964ea31945bb118cc95ce3e49a25979360c7c95785134f8c53bc1600a267bce14d25ec42f6c3b9d5137a7248d1748dd5469b31fbc4aba50d25aa9a909ddec7181d1d8bef47b8d790b5bc57831598e1612634ab2e96f9d85e753bab069692c785df75d2f265c4b7d85d379de24fdabe510b5158e49923ba6fdb16abb0a7cd0cb35b950fe692a785765d2b74cbbc5532886de314deb2697ac14e8b1d6581dcb4e0e4621e12e6c664fcfca83c2295610ddcaadb98e9fd0dc6a74a818cc530e3bc1a4270731dbb8099cbb6b92847e5bd79809553c29c5e4f9931dc34b30ba3786be27c9c40a2bc15b8dd32496247b8c380966c8595dbd3b217f2428b2fa79953388fd473855bcc41c1b411d97be57cd9cb92fc2929a1d1795af4e9a08a5b05e13bede4d1f4239ca97ddd49b70ef10c22786ec7d8783610ae229bef92df60223b17ffe426cecd67e714b8692afcef35fbed8c2ea925ba70e53f6e23c17bb37ffe64ff202edaaece33d3627bb704ea8d3121fcfe874f147079d4ae7e230268afb6b7051ea9c72fe5a7b0b5ab3539e4bdc05696d61e5dc4963fdb5f8849feb08cfe0e3d382cccfe2d7fa2cd8928394944cd254715998ac4df43049febc3716790adfaf8cc242bdf0f770e9dbd97c451fa6844a226fd749572453e25b8c9d52b4b115ae67ee1c3c64051329b69aea41ee641579729d552bbffe5461996eee58a26d45960ad2dc37ff532ca651c1e5e46219253f87cd2968893869f2320593214c9c4b29b864da27b9caab5b9414a47cd8ca61d251a85a5144343b67f3a2f8f250240613e6777c6b64507452e8e4abb273b29fa8faf5eac3f786cc3d615d0a9a254fc54ede09d3c51852f5c9d7643981fa59d491751349a51a544e90be3435f1c61bcf7ac94cb02596f44957bc7a4c9c2439bd2fdafec92597e8dd673ae62496b8aa24793a9d28b7b94aec49453a5b0e25e8101e756df59a633989663ebe438949491c25d1bed9f725fc48709b62c8543b4282ceacbb78b2c78f1c1fd1addf45cf15cf90d111e6b5dc20935dae2e36a2d0c9a36c9eacd82932629352d8492fdb12525c84a922730e27acef9ca888b2fc87cf7b1ba3879888223675a6fe10115f92fd32ddc894393cc46671c53dc7f0da0f1ae2987bdae25f83c4070bb1d526df9cbff2e59f1087f68b2dc12a79ec83484afa1cd3c6106b5d1085ff145fa399f01a0f84b1e1c3b9c9e7967140bc96fdc25a389fb1ffd0c589d8a8bbcc20fba1f3b5b14de920baf661cbb127f9a6d142e5031e2fe67d13937cbf3d24ff68d8fe4c0f87b5dbcb8f5af29e07b3da892f711f2d67f090985573e2a58917ac3be496f2b9a7c323b74362a2c6988325a903baf369c344091dcc1f36a273cda7e81cfa93e266a530afa51cb4942c2513af4b361e07272bfc9c6c9d4eb6e1a06d36936fdf6027b1c4c8ac1bced1e71fa9b6a1f658f2b2e3217db2c17cb27ff2728bfbc935e09eb1b963a206db4e4c92fc792fbe240d476b6ace17cbf649d0f08d79fd7592b37c770656aa9377940bb239339c52ce16dd634e9d396538790a699f3a64b862f49cad3709f9da18e84ddedb2e26496bc4d076ba99be6138e87cce7c0586538812ecd3745fc063ec1c9d72ce0b66ea66e554a7b7772131c9c5b9f95cc8e492f33edc42a2d9b46eccfb1d6a2db81b37e8c75825bb59b0deffe23aff525c2c14dbeec249ea15b4bc612cbfb702e39d692c5aa5f0c92a986babf5764285c4f817532521a64f92292425b229573149fe914262a45b8685d5b72d0ae62b394ed6e4933b83c2f5d229845e650f3fc1abfdf78f9fe973c6099ad8594a3e26d13ca6095e4c6db934c304b358b6ac193ca7642da16f17cb31b9625b394a40ebf6d4b37292609a9871bbd6c48c3a12d0a4a1261d3f827d9249a58c2f795e23fc6e92278b3326ce45c032625c2f2529bc4a84b38879772cad0043a052caa9fd74e993a015400806b124d26296a6420b8626a5f6e7fd28164203c6a639473dedbff524f60b4ccc716eb423263ef3c57562c624a6d4294e65bd70df344a7079c1ba6587cb9a68ea2ece4ff163e518d3a5baa84b43cc76ba4966c945319f9b86b0b3b28ae1024b5791a133774c21bbc562792d7d496c796f8b2431c4b767febcefb578364edc940d930fd342b7b8fb98c4dfe466a1fc6e8ada494aaa595918dc53c692b7355f3616caa78d7ec989ca24b0206db4a444556fcd79c51ee3ff65e6b8628ff79d2f9338fb9b5694a3a3ef6f6358a179a456b08ade99625691bc743a9d5d15a57b6b864f7f5b792ade15136345e65f6d541c2e94bc9b4992a2d9a7b865f26226ff0a9335459bc4306f952ffb6c4a6168a53561a4d29f982285769eb396e5248b5e350acc92b49636748c9f52a2e892fa79aeec963ea542516d9e381792d75583e2ef4ddf1f3f55d0f0278a2b71c3fceb89c3b27f4e679dfc643b81a72c2f57a2b11ee5c45a312116532594b98963c678b59e68e23fdd8c5199d5c9cbc4fa9eab6636986863f82c93ab5cc2a8dadee999f4de2a96d04e34395ff68aa256a9447fa268f84c51a24d51369e245f7ca54ce272eb1c4e0e7af22f093f787cb1126d7e8f841ad72f6ffafc39a10289f604effff89d3b43e51106cf5117e592688a8a23bca898bf6752f8f91b41ff49be2e651e52678439cfa4df9038f37a11874b2594bcb2224c31174573f050262742910de939bb344b8a0873f05ce3413c84f7ef71fd3d64fca021f46472febe39da868528b483bbc554a2614208376b08f124fe27758338aab44e3aab98334710bd4986d63fd953cc0944de31e58b099fa93a80d033739cbe6693f33f6039887c4e597396fd60ce0e8debab9d34d6072d67aa4a6597878c0f5dcec9234bd436647be8b2c925486425a9337ab037e7e7d3649e43260fad9c341ed84f7973aef6e37e8793c792e358f4bc69b143b1daf5929670624d24461d0cd3413aea058d93480c3aa47d276eb6a68c963d0cc498c371f27e9de6d870398a21874ebedccd6f611b88118772b6f06da9a3e498bc1870489a8e5142f6cf4fd4dfe0f45e55a5b4adf0dd709afd94c4f57781186d40e376f4fb13546a13830d584ed2dfca5944df34b286f43679874b82aa873f440d35984bb8bea9ebc8a4c1ecd6de94c4764444040d7da68a56527af9ab943350eb59e9c4b40831c3d1b782bf5c4c79cdcbe0af497267064f5d715286186430f44fa5ae92cb85950131c6f0c97beeb270d37f9a18b8c82bf14ba368441e0634f36fcca6100cad8af44cf8fea507bf9027952cc993e861d50b81185e7063def679f11cf3b206c4e8429e849bb7f8b7185c20f3dbb45aa675a41b8980185b10430b5a0c3f9a6db17e4d3a0bbb546ce7bb8b9bc4f4471a677c600d0c0811a226488188c81a2b0616cc74d19318a23ea48d66fcafe0031ce000903384a80e7ce8408c2becddb79e7f63f2133e31ace06eab9c68b3f761f31c10a30a955f0a796252b2e980185448d7abcc240b8b0e6f4444e44e4de1f9f3e49d63ae4c948c8888882185ea3f3696661eaba39068d9d473e518282426334f8da811f5f488880809c47802faa6329943addd3410c309d7c797374f5f52b83b2222d284da240bbb1b96651381184c380308100188b18446c30c1da0a1868f2142d40882430c251cd3a7b8f1cf4aa32ae487fb0802448c24b0f1a4e96c4d9b75d6213fd410e23f7e98110211112066d8400c2460b12124f5bd1111110f8871847bb7e41ce34ec66a1903621801c99471662ecea7ae5d204611b293dcef18b171e329440c2224172d6b3cb743b0336b4ed4244f20861092d5e32c674ca9966215e808c6e9e4b34faff3836e26d0018ca4b3c9efa169d3ef2c43c72f3eb1638e94db2ed0e18b240dcf9d8b0d053a7ae186a70553b30feb8c0ee8e08511275d7cf41236edfa1bd0b18b3b2f23e72f96acd57d40872e7e910a3a25a7c85fc8a311048819b978b32cdb32fd3a706177df59dc6c62cc8debb885739ed184fd75dd7175d8c29230796b57530be62cc7247bab9898355a1ca2ac2f83aac59beb023a6661eeadac3757fff0ff888808058e0e5998e1f2463b3974ccf263414dc963bbfd76594f74c0c2d0b1194e427f855292106d67154e664b230ded810e57bcb1be1ae3e48e76d20a36ce3e5494d5fc9c658527ba2693d3774db75c859664bbbb8b1fcc6da38adeb64f08e96e1d7f5361bafd09ed0e1585aa4c173f29f5e5764f03080bc1808e53f479254ece57fdd0610a3b7dc8aad09dece32ec5258cc514c6626acd4b99fe00902169ac1184c900810e52f09a6286556d2da9b4818e511453eaffccc977554e14866ccbbf6852a120f3ad75c4745e8c221a3a409194b539cb9d6bbdfe37d0f189cf2ba7a3895727a5ee89f3987c6296ef982a7b9d304f5069cd948f8888e8e00469a92ca79c3b9da6a88e4d6c259cfef589d18429a364edfb64c238fe71ec7b4cf4f91aca6b4e5b4dea12476c97e7181225776c894e4c2efdb43811a556827fcdb152f7319fe628919af4d134454b9a6312d03189aad246d5909b924453126ace55256bd98493a521407e9401e4879033744422bf34b9d4ff556b4e20514d6f4ec254ca23b28ff2ff733aebd63a42cf2475ca24c6281735a2b4f924379c3023ccb623956eab3e797411897be8d4a9c45453393a1441c997cb72068d7d972411e64a9fb06e16222e937b2e79da0f91777f5855c8e57cad21f6ac21a71f55bda25d0863e7cbeba371421cbe73cc99b404490d07c15ce7599244abcfc408829693a4b00caba49d0500117404c2e496e2e5b0e1d1f406820e40b0ab162c54aa2322223930a343a0e30f6c49d56929f3524eb61f0cab127b9a6dc3d4a50f46dd4abffc2e37f7f0e1b06a317b707c5e63902be95297e8e194b5a32e588bf8f6192003043af2c05d8eafff77425d2e011fd08187a35a8e231f6ad2a4cd471a7a860674dce128b7d934c9c9d51963031d76289467ee6459a2d9363f74d421f936a8bb98594a93a58362bb96deb4e4380b75ccc1f610265f888833880741810574c8c1246f7a52bb0fe9e5eb8883e2b91b3eaf09ee81c325e62485ed5fc71baecb5b5992be232222427e9831440dd4e186d3bf9598859282c62e08101f413c033ada502529b75412adb5f2c8867a2b5fc99dfba47f53c71a0a974bd2c57e71cb87d7200890171171810e3598e3edea4b1849f5ca888888331aae060ebc8c770dd440471af849b3f6d098921ce3bb063c8d3552a03ad060dc8c31dc25693fcf6730e5dff4e73cb1d2351d6620b3c7cbd096b12f2b26206388193fd22823c89019e82883569a61bf3e26c3b75993181d3578d456c718ce2dbf9da3d679b7a5430ceee534b144cbedc8d41aca02203a4881047484a1ef64296df767c7d0c1e0ad49497252ccdc717d612dc1bb6673a2a4cc18b0002fe416839ba7544ad7f12e1c2fe56029ecc685a473aa7c9eb7a5e2da022776a7ca56eaec29a3430b7ea8ce27189ab0a39d5ec92dc2a047263e4bab3979e55abec4053d3061cc92d2ea4bf54bcf31e8710944ff355dc8fc3bd944063d2c51de6fa864694aa9251a6684a07a5482b63c6269c2ab18f4a0448f49b449c81e134bbe073d24a146946add4659ded5231294c5a0a72f24b80aa64912f6526e973c024d62ce5d922f7e25e78863b01822b252ee4bb74723941a8fe26326ef9697831e8ca0caa35fe8d3d03f961df4588422d62eafbf4b2b655f2722e1eef3e5ab1c172f051122e87108ed2d7ad49eb81e9949410f431c7ef139beddb37363418f421473c58f2ac943ec920c28a3860316d0841e8430091716cb5532c9fe73e0083d06915b6868d48c3e811e8230ccf2524f651e11cd0a7a042259cd95c964c369d0fb043d0071db58264943b48c883dfe702ab14cbe3c9a64eb68444484020030420f3f68d9c40a4d962fa674e94321be63213f57499be483965f525c99cc6a7029d0630fd6e651afffcbb4b0871e4a26eb9960a2aba798cc4392dc932fe2153456aa84f4c0c391f193d059610f7adcc12485750edf99f4b0fbf8a1660ca140072981097ad8c1cce4e13977f7a843a2e61727b535acc54ed0830efa260f969b265d5d3e073c5a7b5add57a6a81cb8ec7f726b7eab198e839ec2d9a5b471ebee8483b7a5c1244dca7b71f2864db33294c99750296e821e6ef0a29fece1de825f5c6dc82aa55e935c4dfc90d8605ebdaa19cfc9c3e21e6ba84eb613e62f27497f570317a96ec96298d608d3e07c8e095792d7ed5fd05065bcf736e1f2a63491a0c719bafc1f5bb74983a7d60c8b75d77fb89d184f2d03dafa669ae332a53de94106ed2b8cb497c67bfd7a8cc14a6da279d2eb97971e62a82baadb9e38b6d12a3dc2c0ca676ab890f7e1ca203dc070491bc42ad4c7b612b51e5f684ab8cc7f3d9d9218eee1055dad53c7cf3143da6b1f11a2461a19e8d1855d3f58ca4c93bb4fba064174880f332ad0830befe55d6bf47831b264444444023db6f0c5d4b952c80ae1d96424013db4b0091f56937bfaf3d887a04716588b6ee92ed9e1b1c1423147bcd626297b7ddce30a9705abbf577b5881cc71fa42b377cce955d8e63cd55ada958f692a189e2c945637054b336ef2bda0199e49a12973cf26a78fa1a61e51c0b2b59ec87fac1c61550f281caea652affffee4b9c7132a315347a9b1fcbd5c0f277cea19a22a2549a9cef76802529ef97b9218ea840b0c7a30c1ae64a323135b13795d8f25e81fa3c4649bbbcb2aa1686207135f630c9ee49370fe5dedf20d09a51337ad97981bf4c21e4730c592d63c09aed1626f0483b8c5b974f0e41e17610be295a35f8aca8c8df4208266612c991c5efd4e8c0d7a0c818d9fa6d1358344da55407a08e14c399f18e521ab91a96000830e3d7a77fd1dbe2b1a18e317fec63c9344ceab4ece17f9fe27b9fa2f499e62bd38cc53cc163e965d5e2363f0a2cf6d96c92e4ea6e663eca299cfb235977d8ca10bb63dae591c3d0463e4623d2d395456ca857c845cc00463e0a2ca3cd138ad6f719277b57e7f330264081a1f40c118b6b0d2fa52864c4b31f932c6a8856321c3a6333976773c32823168b18bc8a5cb363fdee905c698051adba33ed32d847863c8a2b6d4f9d762b2ed0dc1608c5898aa3379c9eb39bae30a8c010bbaff2dc99d293418e315c6c9703b761a1dc315c6eb44c9e6a2513a2a83315a91ecd1571219ff79362b8ca6b94eb0165791c9b126da3d9fd8aa5105dd725596632615d44e8f759267e74234062a123a8625b92f3ba3ca639ca234c1c61153245bcc26393534c7fc4b6169f4b1b426947d4e2105979ba67e33bedf854681497159df3b7566dc65308628be9cc1de84c88bf1eb506c19375b6b0c9afe44c70005276f2ec964cfce18e3135be578f299ace3cc4b630c4f7472b935f53a6b5749e1608c4edcf1a5c3fe6bca593c6370e2944f316ccebaa174b309ccaf84ecbcbd8db13561aa5d1342aad26c1202c118993849b2aa5ac9276cfc1513c7903e97e4205136f307c6b844665296b09b56e65b6389f2a4063b304625d4542d9a19b2312871a8c91b35cab649e1e3c11893c03366dcdb6d2aee07c2189248ca936e376fc2c5cad6182312e5cb9b425afae8a6fa1890284b8ec12f2d589eff117f8e9539f3625279473c1e5a4b3fd8559cbf11580c66ebe2f3979567c47a82c737f1b2f6a65f84317c7687f8a7f2d6156166ad285bcf492acf134144c4a4fd5aaf6e8e089394638249b92fa6ef4324f8f4bea63d51ce6e4350769656dbd3e55bbb10d964f2f4a1d7a37942747ee6c935b6b2ce4138979f42bfc882305d3532ca5db2f87120bace7721fab7536f0c886aee73e52cfe031b9b295686589273fdd0c8892605354bd370fb50d88744a8da446b960fb6ac6b28c1b27bc0a7b76c4f12aa2429ab073aebabaf3ddc9f9e07f344d4df05cbf8613ca097b2b99571714bf40e7985cd477759c8246a074b2a39c8c50922b1691d9e753531d69c9c45523a781a9ec494dc3fb83e872dac8f8713e6c2ee94c3a3c9457a3264fdcd38e47d9e333ac7ed94241c4e6255deb426266f946f703f054f16ac24f7de0da7ebcf7173c54fdeb72131e325d124660356d31d3bd6b377b8063da488745ac9ac4f0d653c8b4f62be5062d260494f88864d6c7734f5331c5132973ac6ec122766c8d3493195d67e25d9a40c5aba60d1764912a34bc870e7133a567c0ce6798e0f5d766ec227063d63cb76fecc8bebc2504c973956670f188ccd29a6d464b29d79be90bbfb7ac70bc789d7259de0f13673ba8079aaddcb1734749c0b677dcf2477e8a8b26ea1245d06af282569fab4a056f659d1fff1edb260a51ecd7163c1750fb9bf1a2e4cbb426b53625638865cddb831358916ab8296b269a97b4385b7e4b8cd72b192c4cd1496eb0fdf38492117a9be68a28d495b14fcb8d8fb8f5262590c140c374fc172c2f3869f909c65ef8d962b5cd409c5bd6fb2144b8a39d904c4f493899614133ca9435a9242b5843d2e84e57025bea6450925b1c39325714aa3a42481bed18ba651a22a8f84cefd3b4ccc116a4d6d319ee4d0941be114958c6e3a164f2e42c9eb77ff04a9382e118c39324d6cb31c323a84a4149e9ee6c153c73184d09a94df346c82917021912749b95d53808147ad8d7db6454bc4e3179925a9a49c2429a45e43de8c11213e78f8e272cdd1cdeb96f1f368e80f13141ebd70b72ed57c6d23c983172635213fe6b978ece2934cb4309d4fb4d2cd431759308f8cd6ecdd4cc981a3404424071ee479e4629feee89e9faa1d3767140f5c78f27db0347f7ded3203282083330e5800091270051eb7c0c2ebc9a19379268bb638d9c660a2786c945cb5a02c464649258e494994164a6f6a3439cd7a65c40132d0e0c0194082704044048df721c439881a4084f09805f91ae735bfa24468467e68101eb238dd684a937a737e5b4644447cc4e298bbbdb19a27060bb580072cdab7b7941ea3274909d11f3ed20012a463c0e315a7186ff7c2a7d624b20a78b8e218bfadb37b48bbbe69888850807d0568ac80472bd05025c48cd5edca344281324810a40442d0f8c18315d97ddcc8089393ab5a060928008027f05845319fef848c3f22441587d8a7f69897e42679018f5464e2a7187aec2c8634e0810acf4bb0e095d35b127418f03885df596632c7254d160772c6194058081accc31468582c51ffae7fe4322222e2a30c120841c37994e212d43b756be80c518f888850a00c12f8e0410a4ac7cbec2fa90320191011d1019020419ec7289c9dd64aa12d654d8ae27709994728d2a9ff8fa1975c36ba021ea0d042eebe47dd8b96f74f9c2a49ee26baff46e99e4810bf88f0146c7c2c9d28b6fb5778797f4b17275ccbb265e269f8bed026b4bc3975ead568139326bcfe11d9d0e998bb92094cdeb2fce0e1d5828d892f5890b1ec308f4b54d23f17ca24b3ac938725d69cf34c6c8e173b2c3c2a51c5e66efe9c82aea77950226bb1fc415388c72490b390fa75dfb2aefd808724d6f0a8b99f19f6fa241257feb8b9cc4ca33ac6073c20715b5a7c0aab24dedb232222affe03063c1e41598a56597684d93ba678deaa11495797aa7b2b8cd0cc24b977ed7783942cc2133a76bce335e3731461ea4d4a8925e45a341351485e38b1527b07f910f167b1dbdb7f0fc15ae69eb06d2d6fa67fa07f983192010af03084ba69cdbf3cbe935816a21436afb8e64f884f744f5e92b40dc2dd98b9713324085676de84aa2df36492021e8130d8abb7fc8cfa5e0b88deccbf438fe769a778fca18d148fd694f656563fd492f934f95a65657d24880f41c3fbe0ca578e279778e247950f5f583a8df3d5d7b266c0630fbec85db6cacf3cb5ea21f9f7b793899d24ac1f031e79e8fa6a93209aaed633233cf040adc60ae366df215318f0b84339a92629c778b5b6a81d724d29ae6c7e3aa9322ae05107ad32bbdc93cc021e74f0efc2a764528979cd27e03187e246ea52a8932a653ce4604c9ac724ba92bc9f9080471c92e4987fbcc23a5d7b3ce060dc2dd378f3144f533cde90d47de222a269366a3cdcb025c9ed62f0df183a4683471bb8eb247d0e29ad6e260f36943bd542498579d0fc1abc5029a7d0687e4249101fd1818f20ea3f98871a924f6234cfaa72fe9606cd4fcac173667da5063cd050184fd227fa7966133c835eb2b776e64bf5ab98011d93afdbc4d08888081a3ad034944719feeeaecf9c27145801192ee041862d3eb947efcd18ec1befa97c5fea278918f254bd34a7a21bc5e31186cd2c68ccb82d7d82c53cc0704cb9ee841ad58edba5c1e30b45bb87763df77457f18217962c5aff8dc68646ba80e6a87a123ba63cf672c1305776966ae9fc4db6708a64d89cc653a69403021e5a704ae4bfdb2ccebb9a85a7734b8d761093e7c4c259a9debe711bc2e30aa5f3287965b61b256c052c6e09dea2d1a6e239448d1104f1211fc0018f2aa86bc26dc5477950618d88fbed9c08784c219d7a93db36e7957746ccf0b1061a1a04880f15f090c2d5ff7143ee1a1111a1008f28ece13a9cffabae668e88080f28501ff31fd77f29a65f011e4f38e5f3184caa9f8713ce592e67fceb2412274d78a3dfbcac6bf37bcd04473fa5628e39a658138f25bcf16a763c09f9b5110f25587ae9b21fd4e4bf78242113a5c4fb2a5645077920e1918e276f3ef3f114e77184f3a74bd918133af6f1435730071e46e8fa3ac5d49898370a1e45705f4353bdc37664368d34d420425963578e25775aa6b0a7b1460aa8c06308264fb79356631e42d0d25e8c21f6234f5283a18e669cb9247d97653b8051562b7fb1e8a794a72f61624d3b7cb1cd96c5e6dd0b3fe5b826e5acb361072fce1be2630e99e46ee45d68b2ba5b4f94f40e5d741e4ddeceecf839b45c94fdf7557ec258540d0d76e0c294349d10255e7aadfd16e5f850e28907831db6e0534fbd3556ce227b2daa94b564b9c717eca045a52597a85333b5a2cfe2122d8ae9b454c79295459b7d233cbe5daae0c582a812cf8f269c94f3b603167bd9a7948e5ef3b7ec784571624b555acf73ddef70855e314931b49dea698a0686b0a315cba5d92f49d5c081ff503570b0811bec60455966a24d6f8c821f3b56c1bee624e86e09ae25ef63872a70add2f7e8add17cdd808888067115ec484531e7f08c16a152198eec4005258dd44a675b933ede710a4fcaed4c826bfaf0cfef300573529e1d0b1fcd9f77604729caa01d5a627693e6e40fbdc10e52245dee381373a8778c22f172078d1b9ff1624a0d768882db8b3e2968deee706b4728cc72e17ee77f92580a0ad2df82eb5bf213eb5ff68fff1fa6e2c4135baa4d29da2529dda8067674a20bab1cf2e7dbfda43871564e9159cff176ddb10993b84fc9f1a652256687264a99ebe432694c8ab265c2db9c4b2569282979da8189535d92d2efd4878ab7e3128c76dadcb4f6a5656508d11d96206e4c37a664bbb5cb48258a5d67413cc6acf8eb0e4a281156e7b9aa1d9370ffeb35b3c7ec90c4fdf1ef3bb77589e1b223124db50d095364e6f920635b6d3ee28ddd71849dadec5f3c654723f0ba8a9e4e12991d69072374b377c722084d9a3b994c47c38c3d03088b8894b1431167b69db75cff99c96447224a96de9aec4b9a328e98a8942d63c1816191482010880381204094dd6d00d31308004850200d062391348e75710f148001582c1c3e36201c2212102010100e8743e15020140c04028130281012868261f9340d73690dc7e268ecc132f3ab7d08f69452903d13b378a85e883ec2f838dc758fa538096129cb4c33b99130036d228a701aff7e3b5d9689839ce908156e8876a96dacd63238a23ce81642259a5cf88c489fbc12573add7200e1019a7ed01bee7423020455f781a8b7cc0d6847d600a6c4705e5f2c8b78547e7340787a67180d25522367d229c3932dfa50e7e91cf38aae701f368ed0b804d6db7f001da6ff706179da85ce187f75f620017e6823aab5d87a13afda80f56b7a7db383f7770d6eb43024a1ac5105a687d458c79a614f2291751048bc07d81158221db1df52e851d2365d249c1aacae71662010ccea19ee3ce33dc8eb9329ba276c6b02e4f5ee949b4ecc74862dfe2714855681643c08b820edffe8a187b308bd74cfe8d6acde7af658757aa927618eb6ffbc5fd44aeb62181d5650eb1b084c4ac89b7ff9671251e315491945a05b43a247002e416c6315cf4126967456b8c5f57b96ae042d85a83cbf74c75dc0c1aaa0d27c15e3da4dbe4e979e6afcd9a1ed0e76f79ee74e90b765097f38e0871874b53800012c19784fb6323090530079b2a3d8510fb64e3d25529497bd80edaaacc7796a665aec52aa1575c5d556bfc61809c2bef74c2ce8adf722aa24da578cf08602d71ab8e1024eab0014924a2b5b756a9d513fc9c9d64e26f2ec22108e177d7454fa59959bcab05eadf1b40f7029ee0265b2979a7aec564347abd3c4c215b72f5a772df5022f4a9c1dc24d7e43e35c87fb18a909616f227f1ce9407812b974224e6d45b3293454674e6c616ea8ea3cdb52b4df969e571711431df1e71f50acc95e85d84c28f5afb66f78a3b7f8e7a6a5a254bf41d082fb031c92a3f5d69c91072a90b2c081af32e660dae5b2d27fc07804a7fbd48b0749691cf42ac9a9b1aa3773864734e31b509d79980376459fe0fb0ad375f269f36ebbc58dcc25b083363feb323b838591210d99046f0da9f93acc6adaec55355e7a5cc4e2c88c1962b3253a64f29dc432d68ecf0b6585bac46c33bd55f6184ee8e53747189a55e9b84c2e5fbe274c56c3a84909e86c23a6a824cf083e9788ad7d66bf107f45d5748d27db6ee87e71b61da306051d21aa0b3c4b44983695bf884c9fc6ed6e9480e05023abaac13f7ddd31a7f7376b2bf47310ea45eacc788696eaedd5e3128052e2d25abab83780425951710469c3ce1f76570d1fd93ead37b00e3d1ea9c5df25de31e9cd6d6e15a5feb34165b585a81c5c90eb84a4a2296cfc4620d1169c4f099457f765b3fbaf8d6330239817b449e2d601641fe524a61b8ca6e3cb0e270316330a803327775d41e721ee24d097c8738348064230b5d53aa8d88e3585b78a7c10a889d6b4f7414edae07c077ee0b6222bb03fd035b670722a3915367380747b7dabd01513dcaee369715254f5ca17503d4658da4b846e8c5706faea66b16be2a8ad0ec466ffdae459d4465da9ae219c6bed6becda642f146a1f35728bb2a774c947116b656fbdda92b2170ef82173dc6a4a400280c9e250ec4a24f62522dddc96fd835b18f7aaaa3100183d8a06edca3d9bb13dd280edb94e9fd3b4c3130c12078f221aaf99a0f0a081ab14e87dcb35e0961997e05135fb82d6f2c2fe140348da95e252639f7e1d8efa439510176f44324ccf59d64a39c78f0b21005402ee3ac1b0502912665538e4cdabdf9302ba6162133fb79df65c422437d0064f00796e189404d09d687946eb4b175bb87ea45bd952bf352372e91d50079ec6acb342758dd5aad4496ab11b8b492936506f5b9ef5d856b1497a8bdec396d6134b1fd349a2377d304cfc1120dd1eb560853496ee75feb4ebcb05cc54fa331d1e0c00b12d0d3a8a5a33c9e79ed2727ad59358c779e75f6c8fbdddb1a2107397fe7d4d68d3c9c99d117dd262a1deb6fd7e09a9a228de768be1800abbd3455b22ee856cd9392565bec08a7fa65d67a763d912bd7b38c8b0e9b23eb6b2a4d2beed3305fe029dd3f81ef0c9975846264cbfefddfd9399eee3ffb78d98f41896c5fb8036943969b017789aa33cc5ec0dcf7098a693468b4d572403eab50601c55540f2c834d57dacdc41813e538d3e8d8d1259d2ee98fd605b782740aab1d4f1c44c8adc260d09bf870ae04d50e9ed4cd433527d2c07bdbd3a04a3cae1a492acb767e178b0ac026ae88fb37275d9964e429a9bfa0e86ea9c33cc27632c10a288245f2cda988c35475c04f16cd7ad6968396109120f286eb6e3e8329c1ba03254782d679f1a255a74bb2199bc4ba480ec94f723e338a5aa06427332ebe6092c40220b17546d0d041f1a2c71efa8cee5b412af78e8fc484948a6513c037fa2827ee66c3bbf70aa869c02172828638acf52deb1c9dc432f06f32a75781e8f38a22b9c0eecb925a4b26f5eeeddb4d21d8ae6be7684b7e4e8487914c797fb639f879d578cb91a2860a8565e705209b6841da23a1932e3c9df221fb1441e10fc70c852d7da8c24a22238570c43c08c66e4c6e6f752b501888c76094ab83c82cf164a1c9930423b5ea3ab8fea16c2c1abf648c3f2e922cba67dfeb394096c4f96de262ca294c2b0baa1c180de6630c7e887268836a3eb7245dc58c029dbb80284fb2a82462b4e39e8b16a7ba9d370e91b5758034ab818da4acf783503f9dc01cbf61d7326601e25cf2e68a9a8d98bd4a1af400fcf3d70869f630a312fc5a0f10f1e83c49bf2bf14a72ba8142b3420857017b07ce3243e219ac64256acc6bc989840608db4b7baae85a93c07845ed7e8c6cd484a57da27cb2f421f42f9f04e70299ef3865947c3345ba4ec3dda370374659d6919dc6a0ccc7ec08daf58b6b2f200dc01cd21a9f46ba6118a14dff40f5188182ae2ce902defa845fc20e06d06e28e9869188caaa07e2c86b5f87a83bc3a824448005b07e358c9cfc361a86709be5a13e25cd2bd4992711c363b574bbed0939f980de5a584b135b0e2ba36a33de483982a2501642034b1de6d76bf5ece0f99c9931b37656866fa058ac8e38c351ebca5ae68ade244d5580f160f9fcc00fdd2468a854857378cf3c51a307f03053da813f732c2c5a7b0161b95c7d4a4e92b465e7e87d9c64facf724068fa161594e51abb945b480f498ead4fe08847f8de607d80691886bc8c357c773c17f0ccae9bc34f9f267312b4de020b843f4fcb0e81cec429d623f12914215b39c27218b006a47fa6346003bc6359011b923251c8ca478cfaa70d89f864e22526710d457c17b704556be2a4f599e13dc2caa571ffa4006f1b56875d5a40b028d3e7570cd174a163a15b36eb2ad57ca718714787ca2eb9477b4151bf23fa3adfbcefec9d1df47efe6e1286b1d7594d09fe7d3367420ac838e4883547a29da12edd7af1c2e6786450c2a9786439f4436a2a7482e7fae0c2d659f11b8c092abe78bd6d6148f57bdb63f6c164ccb3d9350883fa88b84108108220525b0304d9f24310b7635e59be0221683215097f940b0b6689a808a0d2355378fe534c26ac9fd7acf9807dc66ae05fd66d60330bfd8a0ca2ec3abbb63301bc89ea5e55cef0fbed7b61f5cdedf578b2b35375405532f4cc132e0927511bce9baf519e527fcdf4f6018e8dbfb0b8777de88817f29b0a1800534573981c844de1706bfbd02265ad88656f6412579894da351251c1266152a1c6874aade6fda516c036d53df166cb7821a6e09aced76520d7c3aacc309e665c94ba16780caaa4a78915e8f74d67bfa82379a7fcfe64787d7f553b623c3c5dcfa7e0a656c76eda0dfe3ded62f19b830c3dfd88f1fa0893711c1db92f3345ec6c1582375543a94be26ddec6ee9a895ea789a85026044fecf6ed0756d32296d290355aec97fb772c8e05b927f4c9b9c4e1371c00fa6cbcb92d0e0611ef7bdb2e5d895af9de6e9664d035c1259eb2af33ba656ddce58d5baca57c4c49e0bb57d63b904a7d25777d6e43a9337dc09200e058f76e32e6d8aeb095805fd9befd8a8b4b640887282c09a550285559982cd7ddb24733c956f13ee9ee0597ab2ff4123ba5e2b40c0a02d50fd9aa900162969950bf28756e489718d908d47af00f660afd19712df237d6788db0e78bb29cc0f3f2044bc25781a8536e076c4012beada05ec124e9af42a312a1b106b1e12fa7f817daf99d9cb0cd782124b8c4cfce1c0a4597016985b9470aedc2bbeb2b5ba92ca33d4081e42533efd7904e3fa4225d6483b4b3168b95c87b390b7652415119b12b54d735218c0071834965d8d0aa14132ce344411640c1694aa5f35a250d06dca606f1e38a26579996d2d2459d047dcd4d2912c94be5325007431be517a92d46b47bb573c23b3ce081eb5aefb8faee23a4b609d7b50f5afc64403f1e54892fc66d40a9c92c76099513521fb60c8b8bd434dc56fc6057dda0b384842d904ab9039d9b82b72edc1922cdde0f49b15f33e7a0c6f614ea064d6143c0e16070031b9db9568f146d55be675f291801083d52990d45ab7e49e65121414aaf3148edfdf193250d121c7a36c5221984b7042a338db8d1937eb73a6d79bb7a406becf34f2f9133e71531de7d8d4158c0def9bb41c4f32c83598e83740535a2de6bfa613942559dab3052dc303d6b01f1dd56996f946c917f18313e936cb167b4ad8c15f6a479c713d6726c318ac1fb86835b373de711d3e052a43c6dc48fc4047576164be55965bdc5ca07e07e8dd9ba4f7c09d55e6d803e97c7a2c297cab9b4ddb3993faea11b35e66c2dfd2a67d9e6c3aecb42825a374d9ec925c971c5931299c0f8324d519f2a7db30a2ec8294b4ab684f14cd399c63a5b7b333e7dec2c0f4fbda5d4b8e1cebf535a1841f27b05f68ed1afa97274ab85859b431841ef88172a787b3f3695494348d3e4d9aa9c8d95e3e62214977ef99fb114472fef40e8b825206d7553cb90b04ea6b044bd1ce42ba7f42f96d740e50ab664ec39fb8405c87142ffd401558d17b63252eff15ab68b3707b579f971c31b98e4c73f16cc99e21b8ca3da1abc8ca9b9b2858a2def295a8974fa598baf7e3ffa111af5f39f9d46888db008bb46cf9ec7b85fa1d1c058777dca7aa5be535bbf6e0e7c8b715a9e5894dae7ce40ae9aca2ebd8003681faf4de01bc652f9a50313376fbd02bbc81315a7e82304c960d39c275841882f5bc724b9cb084c786a705a9772c4dccae8aa19a7c5aa6154c1cb7a82b955841e5dbb0549e9aa16161b33d2be9587267ade345aa7539e8ba23d53ab6c4263413b52161edd6476a5e1f8e86100de2bc090e9a0088284f23f2d6d10a73ccf90d4c723c5e694af9cccd73d844f141bdb8b29c347f82b44eca642d18354ef760be430efc65c2313fd75ea656ed429d66198ee85a5048956dc6c9e08c807ada15ac7b114c0ddf24a859b50b91b5abb01a149201d3669d2e1428a260f99c7e9712f9c9ee4b0e1eb4ae29facfb967bd21e76b0618f813633ef44c0404b57953851444d6a729084c98518fe0f9b8a4bd8ff083648ac792ce9abde5030c4b27d3a1f22ba0186177c843157553d6369da316fb20592320f400a4abb5828037f0b858d14d70ec87fd10e9acf2b4619468c94eb7cae75352cc4bcc7a60f649e908fec31ca64ff4e16b5897ebca61209fe4e3b39b8cb56a576cd60c9365764dadcd4c9f87c5ace17f7a3e493d9f72fd9498785e959c2f0c179b83f29cc9b5f69aae832d840a6c036a0d9b5ab3523b6f6c26e08dad66a93a675b85eb5940e76a53fa9aae3245825fd2ca391fc4dac4ee6ec249ed77e7a8ecb073d0070ccd378d6b7531992582e8f66ffebf2e30d543b16a24b3e8ed2d9c86d4e8302cbba1d7ac79c10d81d3ce748efdd9fc68de951a00cad7a9eccbe125002bd3ff9c64c3369722745d3c96146d0c6efd37d97e34deeeadd6d68ee0fb6c2a810d58ee3f83a20094664d166349ccd24565c56d3666288c9131d6b9e1bed72b378ff3979ec83b57f6309851cb29e0c7acf1420e231ac387e7c20c2fea925d9e45345fe7c00d38f8a2c08b5ce24d14a1beaca2523d44d0d17293a6c8ec93348bce1cfba834bee3ef1d0b9d1549e7fab5a05aa6d59fa14a68259b53faf6c82d9bab4a5648a26a6a53be095b0eb3c6414f24c0bf0a62ba8880bf67fc37dc81c29f91abd24438de0559178563ea13ee0d69992ae7fedeec5590280d2c04f236b3ee40047417d63d1b436850bcec390915d87dd07008e22548aa353fbeec4730bae0969effec2d77a988c523e809336820e891f24d13dcd09a59750fc2ce3eb4898d1e59090272dc265ebbe6817615178f2b3ec69594e9c4de25f0bbe749a09961aeed4e049d3cabc6a1d3058a172d033811597fb1c0d8f9a67602b6a5838f0e604b60b1e36a5d37fe8f56c96c1d47b42f2afc3ac56c5c8efc5eb234da9a097b6429e30ac44c160b6c8ec9dc8888cd57a92c464d822e50c9818b2cf5e18afac24c66101ccd1e585f208d59c91ce44ed80c5c3b7ec5dc6a945f401bcf0fa2245d5cf477a2161c0dc56e3ffabaefe5a5c8deef03fdbeb36cbf2e2d5d30ee1483e82636eec94d2bf8ba06c0604a4e20c88990d96891df4f80720d8e381cfca79de8e34aead3fc39344f6d9c865d2ecd3a104ac0f906415c6cdcf588f81dec29e60c5ecae2508eba603733dacf4c3ba008eec25e0494844a5a6455ba99aa3e02e32224d62e8ea7c96f9da05f7c54e24263cdebba508dd163cc739bd86d0f2122f8f14903866e127c3f7b9839be7bd90a23b28615de6987f1a5c9926e10582c9ce1f0eb5a967750ccd8b0f264dc0e3805545d420d24c927ca3f7f00abae8e6f1571eea75eb09cb288debfc3b12d59eaa65262990ca4a98492b9cf7bf659cd9d892ee1ec314a0dac1d669e70e13ce8339824e9f95bcf6954aa41f620c8f59fad235f23c40b4c6efb499f25a10dd60a84cabe00e6f6517b72c66206983abc642ce3135845fe84ecb5f930fad10c91a5b82bad14e1205d80d514241431f468373b6516af35b69012e2faf84f31bc0a6d37940c2bcdb03b63dcd681e42148bb6448087c4ddb408b826ebd534c8b25b0110d1091b923bbbf811bdebf921305e4620da205057b32682a1354a22d49acd421ec37b5345edc689cc4ab35cf11c1e359cb4119dac631d07b8bae92f75446bcbb08fdc6ef779c356ffedbf2bab52f10011c717224d1391011d217a2f7cd2f0896be8020c47471ff4dc9a7486597eccc7aa4650838fb03911a0f3e0d151846ba5e2cd82a676fb20dbe5f03d7fa825d01263cd9970a102934885e2f73b8501d613939b89d515a8ad21a3f7693abac1a1f095d57e501872955f4408f4dc24145459ac08b714c4413d27d4982eaec3508475837b723645161ba786ba9697697d65212d0c7c64083e0fab52772cf582d005c4e56f8737edeae0963b8c0a2688a7f3d97a5d675c8587618fe9e2aa336d2a8df27dd4dde9be8544f0c1a699cbb7fece84af8f564ed60516412075457fbbeeb6f840a47b6ca5dca91af241f871fef4c57eff6b8d6dd88f59b583585655270ac26011aee76482def00f49272ab0d8b0c4430c1bcd5a27d9f4a34bd5636a152945fbff8e2d7cd8157e349c404d77dc30ca3b6a974c629723883e1712f2150e86acaaf790b2080ce14de9af3276c61990d0311abdf1153243770b8ea9592c69ae719514e43519b4dbe362463e9b01af21e04d8f5a6e897dad776a4babe510dfe66d275e1ed3d456e702b8add06aa55efcfdd1c4200896f195f6f6e768f192dbfa934bd76c31d629a2c8ef3a70998041014da29b2dd018ac2a512e3745cb3bcb4a75c535139caa28add76763a16878d937c13ecf397e76f27151cf6a5442e61da72ce3793b5e484e3b0d7729cbba9422c8f7d25b53330ee28df39c94a40173d1ab35cc35e35676b3a4322ce41ce64d71fcac4f57c42e991eab288294abb159640c4087427f26e9cc9539d12b1886a731d8430515ff910f93e9a1ddb74cade1a484f42e6225a6aed443f027f06a37237c1e1f07d51f5f567e56ab52fb8a9cea3ea5ceb61f4813edb06f0e3645c393e86c1d211a2356ec10f2e5e97c3224c37659379dd4416ca4f22ce6dd991047679695791fe9359c8aa240c6aff3ee2d3b2b81b88e10c21eade7416d0ea4fcbde42eb07e467a723f1d8269711873996ff812601d9f53820d55a0e51ca80d311d9a0a0936df2e65bb581721d7af3c804a066331d10add05538a4375ef7354b01fb8de7f3b98bed82f7c58fd3994739181e22c1e4613912c6136f56dfabc53957baaaa5cd308519b7e0a48a68cd2636076dd31be58a4b7f69f2b1c15d863f77b3269a36393adfdeb38ac00ef78465f954dc7b77b94966ecff8f639df435eecb800707bfb01c41d609fcbf3894540de39093e1a5467dc15465db1643d245cb4d199cf2940e771f4929c1c7b41691fd0895340168399de58f1545aca2b974cdec287786860ea3d28909e912b78a44a411ed0a5c735113097037a65b05f500203da2328fc63b016bf4ae75cbf36b01c37ae2a01711ae56a8704121d30557c173a24bdd46b5263d18c81f3e89e5723bbb0840e5ebe22630ddd741a83883fc2665f2be005a935a3091317e02f44291c0c71ac384ac4077780d847a666bf311c533bace117e96442aa34554169c9da05840f9a76b87718c788d296ffad4a1103041ac90c69d3ba86bad06fe90d9c72bc5c5175f4fa3e80d3b4019a24425eb3036ef4cf38f3225dc32e842ef1f13b0cbb4b0aa1e9b1369815272db7459cec8a3561fd012eccb01017205f9fb41a7de0be8c8343e7cd7dd5e44daa88005192d84451d2d15f0469f7d4c2846f56dd1048dba5b2c6c6ad744501bc9f5748136d241454bd162c908c597c242979ebee80dc92944e3701caa966f2f490ba240b1a227cb8d0cd2be1b3868c1763acf1b1e078f8e3c570e3005851db89738311df1367871d34b17ab25b029d4d492e10325bd5a21a7f44fbb11407036169ecc6898a9ada69c41213708b86ffdc0c36b4c87b83dcebaf5e52e2c7f91b270962403e7cfb17253123ae779ee092f9c8f3ec4c3aa1b6ff381c8399115c7a261629db79673412abc04b09db09ab32742cd2c3c7715b1930098098329eb6a96ed32102a9e8df40c1ccc6a29ac1abd38806cd630ac40d4b6fcc4a5d9845c87ed77d433ccb11cdf01f75cdeb0a68771235560e963b14bd19d8067f3169bbc7ce958aab154a13a8658540b827b6402e40fbfbd04b023895a59b887122a71836802a18a2296dec88815d0f014ed3b8a8ee7bf3fc5c30c08fff6fbd50922e255407e2bcdf4aa0022533aeea150a49dbd24899c1823620a274b35745e3a3475b44cd2967f3d7ad96c0e5eb41076b823de0fda4397933bc4809a394f7ff2b5d67b8d2ea1ccdd7c37c3ebf78e0e92f96e6c556362c9745eaa31cd14d155c82853f52570ebf8dc80f29145434f46cefc54494f099c49c4d0f689ceb781e6672e882ea61904eed345236a2d9599d43c5d0b5896b42c16d1ccb2148fdea8e07c025bde498db5e4c7cba50d242593b4e31436e0ddac99043477e38d68d32c1c9c7338b4cfbb5268de88036c70cd09ffa545cc08d018c77901f8b329f7a22d60fed3b27d5a139919a8b46a74ba0052d61e1b338b3676a22284bc90cb2f4c716064d3fd2a3ca6f1b769210e5daa55c333b70b8a966e8cc9077727d88c32b5e5880f879f341a93bd7d24f00e5ed0a14e9d611f7ed2179caa2fdeb69571674e03d861bd4239619b9344bd181c304383776af09a59219d16423a37c5c5db4b954ff0814042d37e1194506ce4d24b7b01a11018f825b80de24ae9e59c7789d1bf74d5fd395a947672518389258954f02c12d0ff4c1ae6c6e17f9590ae9847068ed25693fb074bf69bad935d7a3f75b5d363288189fd90efdf29201903d08bdde3e3825b499a975f3588250cd486ef4755d1c6b385ca143d9f2495eb71330dac2da28d845c88494e495898a058044c059b90a0dfa806185ba20d39ade68111f119a976fa0849f675eb44b747e7fd66cc13b68325a7c8ef77d2c908bba748506b3e3b20c84784f1a2870589f1167c902e90b8741fcbcd87ac47aa0afb0358c262a780940c4bf50b276932e73e70f5974b93d90aeaa04326d071d0ca30e6fb7135f8469163429744829cde6c52dda2b8bbd62d9b515f2332d28f029828bd1147e9eee5c22cfc57a62006d70fa47b3e04a68114e74ec7889bade0779a19ce4e791022b4ae32f419345adc89b54163502f6c1229af576507cbf2099b78d2c2e0881360e2a1b4f7a72ca9410d4c0d0287a16d7155ebf167389da3b57e8712db89763537f32826f4ddb6d032e70da4e51ea75ff300598008fdec47e8b81d3d840bb180457a8d5576b269899d79db89f638664ae1c233458d9322a3630200e9d263825ee75d51d5e2975955278de9da08d3537d426f4732a1c94cb5b16c9b0c3567fbea61dc78f31952125e012acd322ab4ebe9fa14964dd9e64febeb547092f0a2eba650d0c685bf04e5c62fea63377131a270cb4d2cb37d6b483cb80b37930f04cabff0128428474091865989487cb62fa043297bf6b6d0e11c86dedb559321b2b11c3230dd75d2b07acee41559e73983289939a7fe14037fa9a3453e95fd9e670f6a35ad61f0ff7e8444b42b6c8da30adf3f62818ba1b9fc2c2744ccd14b44a8db1da2749624480c7c07d66b7f605c587b134ec08947c2cbf2eb9e42b58525d18e662941b7e2e343b0d4622ea31b59a35be70605abf5365028bf6cc4ad7d8cc95522a023a7fb15c339c8bf7e080a46b812abb24462adb069d0e7a7f20fff522dac70693115c33e4bcc47b359fa281b310f3b55f1b283e3ffa17e921ab477f13c1add4665601224c7ff0dfa92f3eb154320c84cf6dd8abf351427c95807458ad3b4bd7d82da5e9e90b4edc8223467b79cca28b83fb7dccf827a9c90eb0cbc0aeaa4a1a96d7aedb20af4611c13278aeb651a8e4858c7e56e9d1775fe034a84001a58c2cddd63a34a7b855733cf1b08197fae2bfc22a201b4017df6d83275eb99c1b0b02c2e34d848b661f8a8f35715c38659b5aba58bd86d055f9ff9c37d97eaaa1bf8e1532abc912bac8adabdf34f05456e61f8571ae9366757cc544ab5441dac95b530d20b178f42355b1552a1358e30e7d3eadea9b06a26ce4b704325dd22c7f0875fa538d33927bb4d2094badfa7a9d78c3a49e4a8772aaaf7b83f1ad3bb09915df6b4db2e324d5f24124f8bf814cbfd148a1114d889d02cdfea89ff28dab85a7f0919d59c896241be4fb1e73c81a3649014123f174fdbf0687b4e386783ab39757265dbaf38aea7de1508a730414766d1b54929e3f82b029961d51600e9ee30631df86f96ab2261908ce2bef1dc51ceec5f09e20104dba31b1fb0c7af2476fc24a249e459cc51823513027db973ac953bf905ddd7b0a04a99140f52796f4124dbb101672819f7b0485d631e51ba0b7fe4f41cf91ce65b82636ebb39d814eb754893efd55b840196d47212a9e202f6eecd7bbc00f2447e863490647bcadccb16d70dc0bdaa570879d3b8452262cfc7a7455cd1f20c96eed62e65618e30ad051ac973c34e23e17ab20702aba59dab0b19b263f95a828c430e6be16d4efb84692171f1efbb695d3a4bebb42181c283ce0410e44bd0fc6967b0d53e4cb0558340d27c26efcad2ac95541d3f817ce9a78b62e9fdf129000ed331c37e358df7ce85bdd7de32ec86e5722de7fd3f13e32249acc17d79afd760cde4fe121082f2f12b1f5b64c7c50e3ef8e3122e22ea5d0169e886e6023e47c780fa2b3816e949ceebba5ba51617e92f1aad221b02c3888cb243d8adeab19edeb07226bff5a15fa642499bf85c05fd9b5e8a0f441063706545e3f87642b6efeea696736ef3367ea054774429bb24f2237dccd86c4709695532ca97dee7a45f8c3d7b54b8c2a89d6e45cb98d2ac8399510cff3498dd6ae72d66f0c74014a6daf7809e552b4fda7da727c77372682b01210da4c8e278e83ee545b5756c481d3aab87e625ad5543dfa13395777976327fc7d462cf20a94908d0497c4ec3afcd940eb24c615da9f1c288a220f20f784cd21029e9fe35618535a1e30c170541b1f62c34b67555e10a352b01a6ef7ae1420c0a979e0506b6ac9e2587e2304f77d1bff386d8d7c16a743291154767cb44a1585dc378a1e18b395501e2eb2c12c7049363eec2408bf317fd5f1590bc1355c714bfe66562db820d89d709c0bc0d7b965bdab4d2cafd4adad771869e1344032079bdcec1ccdce9a72adafc6f5ef32e5c0b7ad0ee28585ce3ec9da7596a27e95e4069fa669a385ea67a68f035884c9331c477059bcd143f068a32acbe63597447134cd5a95d97cf2d31cbb62b47f35ebd465a66f3d2cb04f0fe4260eec6a9c662ecb460dc86b5c32b568bbe2e05d489b97513e6ed4b611f7d1a9b45eefe80fdd8e3e10c15c18854d5119b9ad06c8ddb3695a1d9d2d83dd873c980894e90b57ea45292ad5501f5454d158f6c19fdc9a8c316ede23af0d1d5eba28aea43c605462a4392d7923bb7d5d35525acff07967ac5b661aeaf7d19d60fac545362dd7225524c33ef810e4be8e49beb8625b48eafa3dad14ce0c2c1b1f3d6afc6138fa07de67169a055ab94e1dfc3ad9e43bb3450369e981a144a1e86c9af330a67e328d0e45ca7555932a068cb6722ca1127d5f11728e19fe1c4006c19976c8a68e9067ea0c33cfa3bc568ca39db8dac9e7bcc648ddee9e80e9680503ca55e4cbce0d2e105aa43f0d5904903a8e4b98d9d9efef8783cfd63d613c44e61c2c10d2eb3bdadac898ef75901811c7ee8e2815c4b567eba5efa285b176698c49e50edb357c8dbdde05712773b9707385fdec9440030f9a42bcd556b9bf3fbde367ffd4f6ffb1f905db4e56a53e1ed0bd14c0321874f00a4524072004de38f1d2f62952f6c3e4a2820711a52af68302c26297d23faf7df7022c83bdf569ca8e6c94efa7ffe9da9dc8b616564041aaea42815b40f6a303b42a85da92eae710de03e25e60037fdf8119fa167eddc3c8990fa479cf58997a01e707fcb8acd5a31a2470afd44c8c4413099ebea74034b8f4524a6116bc93403cb323ae14e6041785493656196c1f25bd08d67a74592227de5f1eadbaff438fb38f0546a58e69e3901de2e3f13b2c12e53be8f8858113a2cae40cb50c8eef3df98aabc429130136a3cf2f1bdaa1851957bd199898fd09741151e00c2820f9928b7112b8c1dc395d76b78a91d8f42a0ad9ffc291b10a8fb1e28450922056cb10eb4c5e27fe0de5879883c7816ca59c232feedcd0501fcb349e3b23a47756fb4153d3c3daa2d882a8c45a32b0c4f6bcf0e1661af8d9fcdca1c84ebc232a3f97cb4a4a0055b6d2df0d181c39026aaf511619759e6c803e7aed6b8eeb593ab796b1fd0a8ed78ee8521a5c8d498fa9e038a5cfae23226eb690ed8ff2d2281541b4523361f04607e1ace934f7cd425c5f0ed559276390d7933810716493f6553c5a34c179d9c294201fe5d22055dac0bb4d4eaa7e475319aa13b12a581b860b7d2bab77985892729da53200cd5fff03006cea3a3af7db8f1dd26ad604f44905400f84721fa3438c4e377e0adc78132761f471ff83f4dcde072933357a6032ef2305f43db18ca40ea34618f51738a382d38e5c88b4dffa8379f75cb1fac4b644fb3b996a1f345150f911d182a20c6a514b988ada207bb69e93867e1652216baab23f5219525cff85197d33cece76f55f45a9aad407ab5589ee01c654aa3e00c21f6f2559ad32c2ce53e8575ed9da92876bbfd4f4e1b8f7fa31f17571a45b3bf187b1f106da34262fa06d7900f27031637557d7e8d4a9ffa0b07b8b012bef6023bc457ec29e7bffb03a94d0c3ea035089f22a6f19107354fbd0d484f5042cd462e865e3a09ab19b62e0016e2c1221c83c675652311b1d9038ddf29ff28cde51de2f8d2e416a96ca9f8861536ec5456f83854b2f15ce02b622f95bccb0c3021ded31529010be97a93d0ca23dcfe7dc2c25537b76f3ccefcc86cfe8c1a80dcfe695aa10d867010273ab3e8675bfa7d437899165c6f627be19b8beb69ae74f21adae163d853488507c4426f44a6521060d10c2f69aee0b6c16f67258d3eb3f1c2ef3088596d3bb3d5833f0c7a4e09cb3553e5fe4f09de40b05e1636d72b67230a6b3bdc1c320552c7a1f4b165e2639e0d4fcbbe6bffb5d58ab49cf0ae5006318ffe0a01a3262ee5caadf50ae62e3a42d41332f3a7729096f9459da8466fc1d7b8157c31bd089b1e84a20491483473af4197173e46056f1da8d60f11fbc9ff1bf8fcc31a610fae15f8973d03d2442a9f81d5ad64d75457b7833d4243f026a3aacd4fce548808c0658f54a68b56f8e9a29141d0405a6e7d8166bf0e1231bfe26b0f5808672484b5e8d2111efe00f89fb801e347bcd2099d20d46b4836535c081d5e00faa5fc1b3770b62c0730c30c33cc30c30c33a29919f7be1721246bee4da44c4969c7b596edb4d3ef764a524a999240693d1954ce89468ee4d8a18806a0019f01a30168375d4b6b195388de4e69ca53777afcd92f1341e0628abaff6bc97621a5aa9202975254dfe76e42d9f6a195490a5bbb0fd5efa35aaa758ec2a92bf1a974a5a2a0d695f059a263a7cd194a5a972c95613284c192050b4c8dcccb0b284cb9978f8c2be5274c29b63019534c4f96abc584ad29353be15fa93e9fbf26279bcb9da9324c8ded5a8ccb26287dbe53aa5b7b885aa626aad131946e25f7cc709de0928923bff597acb4a5afa52e982c86ff5855e2bd049b173e46e930bdf6a3c0c512c53cad2be4dc964a29b5381a2d2f2f15b85442892b62aeba73e8fed19bc4b96dabdbac9c2ae55e12e46648edab6d91786a469b50396aca932151bf8c9357225695703db21ef1faf6ccfa1263e54837dab4b870d1f342aa91b72ef755634bae9f26465ca27a4a29f412f63f5d04a5f7927d9d430eada78abcda96ea5a2f64970d1351eb5272083977db768e886b4fee8adba5e794b6439adb9696dda5fee5685d0c69ce5e6d3f628bf97b2ac4356eaf9e6ddbe7d5d08510f75a33db7e8c2e8330c594317c9bc92db7ba08b22b35d56b978e25bf0fc125905cdc542ab692556bd802faf09f9e285e4ab9decf1e3e64fda0604c2ead2ee2a5ac9944b872d308394fa0c5a86cadbfd4d8a172423569e6ba479e1e4b29e645066692d3c463eba1a6fa6f6a55352616b6f4ed2cd15a3bb59e6a262b623e4a34f2eee708bde4905a7492788a6b3b3da2f6d59e0709f56891f13fd7ec1bf11cf16f392564e59e7afb7c8ce0ed5ed99bfa35f49ea788d7d6f7a3fe7c69dd5a2292b79f3e2e2b6384ea0cf1305f39aeacc122d33632cd81022f2f6d1c21823840a0eebb84dcbecbcddb0fbcb29be93f571f4e0febd6a3f3c69c0756a74ef51bdab3435a8ea1a67ead362d6a1d9e6b6eda4e71be707260e9acb9d65edfc141e92ac7fa11b9754277835ac7122ee5cd8c9bd686d41efa956835e73e2d756a489816e1f2732bb56f7e68588fdbd2f6127247ddfccce0accd99a12a27034a64cb2f975373678be19d2274cc9a5aeafd0b437737853879526cfc795e7075c4d9a8a999c70584ab519fa96ecc35f6cc2da705dc964a977b4b21f6ce84c342b35dc4dee5a6b7a8a915547fb9a74a4ebd4b5d128e0a6f3dbd742c9be13b849934278544fbad0dbd9669f9699670500888a42bb23f7a6eab90fb9055ad794beaeccbbdcc8f210857aeb57cb93a57ca0bd1a569dfe32e3c843063a59463f6f8dd433b487a9ed2fb7f65b7e854104eacb63587abbbcf5920cf9e6e7b8e8800596e5fa9d55efaa7bbffc7db84bf7a5d55d3c42de6f8a11cffc36feafe82d3c726b35ab7de3d5d73e6e0f0c19c164be547dc6cea299c3d1e5f37132db5f4689b182ff54636270f4cd6e494eb6288f8a9110e1e6f2de62869729bde6276eeb0a38ee6c91a312355f4bf4ec71cdc1edf39ea46de7cc859a1711a9470e478cfa7fa9531fae6d44e1c2b39565dce95fb630d0e6cd9da69b364d74bbd377c21775fe833b5f7946ef06aeaa36ca58b5caed3061b6bec42ec595d73cc9b4cd5c0d4303194de63efa479d248d79c1af3c6d934d1a1a13261bee688dfed0d9d334038669471c838638811861e30bef0a23d57bd4af868a1c4ee621f1763daefa59a080e17c898af4d9adfd05276b6d0e264b190a94f5f2a25858c0f168ba915afb49aa91c6a57b4550b93b97c6ec1b1c23d7fac1691da53853b53c5aadf43878a44f6dc987b48995bc6678a67c9937233d4478aa6cf6bbb17030da4acc2b04a26a6020c28b0e58a4d8c15192c55d081b41c400bb84dcd0d0d01016c6955de2500804dcd4d070304e0acf48d152c7de500016a689cdf7c161a2d087c00003685ee9d2a778e7dad984d74f3f232e36860489079980ca8c8a0652606858139800a0c5bb4f80b36ae66c6cd1440c50595b1b91253d32da88ccd67996141656c4890f91b181804a8aca0323634313557606010a0a282015452786f396efc902e46ad65860a0a895f5b7deb1875a77f35a7f4f36689fd191536d6794c61cd1eb3de978ab9ce16f3f2b2458bcfb81a2c323657646e1a0c8dbb22a365268ba353ca3edec490e1727cc67f4871d5985d23b68c72193aa32026a76ca93551d0a1ee44b9eddf555ba1f05a9ee83dc51e35720914c76dcead39aee568a54f3a5d51ff4becfaac1e83e3095b9bedae30f9f5bb9b49a713e4c5b05573cf9dbd95312f2feda6e681c38932550df17adaad0cc1d90493f17bf4b4cfb5df044793865c3dccc7d50ed51d9f4cf43df4c997bdf6edf1635e5ef660d24bfb29e512bd941853129c4b1ecbc6fa9e127137af2595503289249140f28823ce0ba9cee6bada9ef21827427b701a79b8ae5fa3cbf5369b3b8c783fdda51a6acd385d3b8b30538b9962ff1cbb6fbbc15184a52a3fe794ef6e313c8928c7effcfd63557cbe4e819aa85121228410420811110992064907b1848410861032ca3154c4071240e02886c430084328086210846128062186106408318e10438891e68c1b0568dbe63fec3b345591660751ffd0cb56f3c31031ada541c1e54e424b92b4af1c60776732c20296489808140bf3eac5b2d039b03a957d29282ae0dc89889ea26101ab60e5db59c42fe97b1fe32e6e459a7d7ab7e967da2da762f68d3b5bb9fd61b05effd0be64d33abc26baa4be23a10350e5797228e080315f143a1f9fb1b0e7a5a5e51615ec277c97c52500e0a1e216be80167d685881f03a01fde39ce4e6f8c83628d0cbcd85d6b45bbe8898a2515dafe720a168b753c31c64d4beab923ae69554e722febabe8f7f0f7f93c6b030ac568ce830d678fa75132072e20fb98478e99ec8cfa74a30d2ad42c692eafb64981a293ebdfdd6efe10e46321b377a78f248810e2a5074b8608da9368441af6374bc3a8de7dce0326d86160bf4287268037ebfecd4826e5479faa0d633dc530619b89488bb62b85ef7b430249b39d3838d884c3b3cb3b3228371193cd43e1cfa06db38320e2998c15f503b3125547e38fae4884ce8db9f69fb24b9f3b19a8464f0f8458b30449e659f5ce544d44a151a154762026d4bd1527525c18349f805b105afa35ff993cb736a066e7f9e9d89839bdb89d992d7ab46ea656a6c7ede706b87e59cc97873844b3fbd56093364826d1faaa3825b64cffd5836776138e0ba9ff22151b7037162fa753feb99a8bcdf9471739b0f56db1e63cb5e5c285d86c75497ff5d491e1cce377c87bbf2b8b6f481ac9eecb4d12174618f906b741eec42f7757227ef08cd00817889529aa96290bc6bddc9cb1462f6731682e40565afbf6cfbff8529ed0a4e5675987132ed74a25d81869ac94e90219a6568574073e6385209e64b91f6018d5b39ff245abe917f336e944d620b8a260aa030231e89d1edc2e72d0ee0f60affcea8c5447460d9705609b93067c86129534a58c250421e09d84c5f107a9fd344f66b0ee5b6e95866e0e60704fab7e41eeb5734650492cd8a678203b77ce8e8face5807efb6a6489da3cc87804ba600163100933ffe9dbde4b02301838a349322cb300a079b646143f1a618f186f170200db9df5a5049f08b99928a978f0bf71e02a3eb8891a496517cd3b6878be5060c1830432a1138e287991248ac1ab7034a7ccbf6b5d7523001f5d1649b9f75e107984204a6f05a50b4a4c5650c817cc88817133f1eacc11a49de61c09084c18673631a0a92a15deaf2352455d930c9fb9c132a1acfb061608632a69cd9c350fe8ab32a6be956ea4ed87618ba47844116e02424e325b23f412e43355c56e6e87fcfd745f36752408f3a1cea16e8fb433f8bd6bc22ba08c8baeb85ec7699b080d882999521fad304c2eece0eba6744159300f9928315e23872885190c80e9653107a17bb1e1efc8d0ddf63f178cc3b34a967b3a61f864890159ab75e7124bc358e72603fc35b233b4d94188a4eb3b554334bffc638bdb4e34a75596f1c2103ef48bf7ec1680ccc2bd3420a9479e189017d9a4e27a5d6b2eac599d35a4ed93a0e93dfea054ebeb81d847f29c4868bbecfec752cbed85b2f0f7b6ceff12b1c6335739cf76a7834f5c148a0df3594dc7894ad0b68fbade8899c78fc86d52ddeaca43d815d85593ba13689342f448973bed68bb84c860a7f2ebac906fe85de8d0d", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0x3c311d57d4daf52904616cf69648081e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x3c311d57d4daf52904616cf69648081e5e0621c4869aa60c02be9adcc98a0d1d": "0x10b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", + "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x45323df7cc47150b3930e2666b0aa3134e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x57f8dc2f5ab09467896f47300f0424384e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x57f8dc2f5ab09467896f47300f0424385e0621c4869aa60c02be9adcc98a0d1d": "0x10b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", + "0x5c0d1176a568c1f92944340dbfed9e9c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5c0d1176a568c1f92944340dbfed9e9c530ebca703c85910e7164cb7d1c9e47b": "0x52bc71c1eca5353749542dfdf0af97bf764f9c2f44e860cd485f1cd86400f649", + "0x79e2fe5d327165001f8232643023ed8b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x7b3237373ffdfeb1cab4222e3b520d6b4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x88fbb13c02428a6ba0e3c362f503d78c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x9ba1b78972885c5d3fc221d6771e8ba20f4cf0917788d791142ff6c1f216e7b3": "0x01", + "0x9ba1b78972885c5d3fc221d6771e8ba24e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f308ce9615de0775a82f8a94dc3d285a1": "0x01", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00000000000000100000000000000000", + "0xcd5c1f6df63bc97f4a8ce37f14a50ca74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3401bcd1e9f3885b9b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34": "0xb0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb381d03c816fe51e89926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227": "0x926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3ae9e7a6969af6726a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20": "0xa8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3d6668b8260aeead3b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575": "0xb8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575", + "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19500952b0337fcbf1d46175726180926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227": "0x926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195017c489719c28aa986175726180a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20": "0xa8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195026ed82a0e5bfb6c76175726180b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34": "0xb0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19502ac2136394fc85866175726180b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575": "0xb8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575", + "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x10b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", + "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x10b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", + "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xe38f185207498abb5c213d0fb059b3d84e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xe38f185207498abb5c213d0fb059b3d86323ae84c43568be0d1394d5d0d522c4": "0x03000000", + "0xe81713b6b40972bbcd298d67597a495f0f4cf0917788d791142ff6c1f216e7b3": "0x01", + "0xe81713b6b40972bbcd298d67597a495f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xf7327be699d4ca1e710c5cb7cfa19d3c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000" + }, + "childrenDefault": {} + } + } +} \ No newline at end of file From c0c1c1e2a4255905d2d281afd478903adfcc0b7e Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 18 Oct 2022 15:51:30 +0200 Subject: [PATCH 077/263] [BridgeHub] Type Bride -> Bridge --- parachains/chain-specs/bridge-hub-rococo.json | 4 ++-- parachains/chain-specs/bridge-hub-wococo.json | 4 ++-- polkadot-parachain/src/chain_spec/bridge_hubs.rs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/parachains/chain-specs/bridge-hub-rococo.json b/parachains/chain-specs/bridge-hub-rococo.json index 97bacbbf22c..e94373a11f2 100644 --- a/parachains/chain-specs/bridge-hub-rococo.json +++ b/parachains/chain-specs/bridge-hub-rococo.json @@ -1,5 +1,5 @@ { - "name": "Rococo BrideHub", + "name": "Rococo BridgeHub", "id": "bridge-hub-rococo", "chainType": "Live", "bootNodes": [ @@ -82,4 +82,4 @@ "childrenDefault": {} } } -} \ No newline at end of file +} diff --git a/parachains/chain-specs/bridge-hub-wococo.json b/parachains/chain-specs/bridge-hub-wococo.json index 9d354ad11c7..0affa8b71a8 100644 --- a/parachains/chain-specs/bridge-hub-wococo.json +++ b/parachains/chain-specs/bridge-hub-wococo.json @@ -1,5 +1,5 @@ { - "name": "Wococo BrideHub", + "name": "Wococo BridgeHub", "id": "bridge-hub-wococo", "chainType": "Live", "bootNodes": [ @@ -82,4 +82,4 @@ "childrenDefault": {} } } -} \ No newline at end of file +} diff --git a/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/polkadot-parachain/src/chain_spec/bridge_hubs.rs index ce1705822a6..ea69c27903f 100644 --- a/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -68,7 +68,7 @@ impl BridgeHubRuntimeType { if *default_config { Ok(Box::new(rococo::default_config( rococo::BRIDGE_HUB_ROCOCO, - "Rococo BrideHub", + "Rococo BridgeHub", ChainType::Live, "rococo", ParaId::new(1013), @@ -82,7 +82,7 @@ impl BridgeHubRuntimeType { }, BridgeHubRuntimeType::RococoLocal => Ok(Box::new(rococo::default_config( rococo::BRIDGE_HUB_ROCOCO_LOCAL, - "Rococo BrideHub Local", + "Rococo BridgeHub Local", ChainType::Local, "rococo-local", ParaId::new(1013), @@ -93,7 +93,7 @@ impl BridgeHubRuntimeType { if *default_config { Ok(Box::new(wococo::default_config( wococo::BRIDGE_HUB_WOCOCO, - "Wococo BrideHub", + "Wococo BridgeHub", ChainType::Live, "wococo", ParaId::new(1013), @@ -107,7 +107,7 @@ impl BridgeHubRuntimeType { }, BridgeHubRuntimeType::WococoLocal => Ok(Box::new(wococo::default_config( wococo::BRIDGE_HUB_WOCOCO_LOCAL, - "Wococo BrideHub Local", + "Wococo BridgeHub Local", ChainType::Local, "wococo-local", ParaId::new(1013), From e2731be50915b125305052b4beea502946a6347e Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 28 Sep 2022 13:40:33 +0200 Subject: [PATCH 078/263] [BridgeHub] Initial commit for XCM messaging support --- Cargo.lock | 29 +- Cargo.toml | 4 +- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 6 + .../bridge-hub-rococo/src/bridge_config.rs | 263 ++++++++++++++++++ .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 71 ++++- .../bridge-hub-rococo/src/xcm_config.rs | 16 ++ 6 files changed, 382 insertions(+), 7 deletions(-) create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_config.rs diff --git a/Cargo.lock b/Cargo.lock index c0e22540ece..0a851684a5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -713,6 +713,25 @@ dependencies = [ "thiserror", ] +[[package]] +name = "bp-bridge-hub-rococo" +version = "0.1.0" +dependencies = [ + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "sp-api", +] + +[[package]] +name = "bp-bridge-hub-wococo" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-rococo", + "bp-runtime", + "sp-api", +] + [[package]] name = "bp-header-chain" version = "0.1.0" @@ -992,9 +1011,13 @@ dependencies = [ name = "bridge-hub-rococo-runtime" version = "0.1.0" dependencies = [ - "bp-polkadot-core 0.1.0", - "bp-rococo 0.1.0", - "bp-wococo 0.1.0", + "bp-bridge-hub-rococo", + "bp-bridge-hub-wococo", + "bp-messages", + "bp-polkadot-core", + "bp-rococo", + "bp-runtime", + "bp-wococo", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", diff --git a/Cargo.toml b/Cargo.toml index 4ba7acb605c..7e3eb2d88c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,8 @@ members = [ "bridges/modules/shift-session-manager", "bridges/primitives/polkadot-core", "bridges/primitives/runtime", - "bridges/primitives/chain-rococo", - "bridges/primitives/chain-wococo", + "bridges/primitives/chain-bridge-hub-rococo", + "bridges/primitives/chain-bridge-hub-wococo", "client/cli", "client/consensus/aura", "client/consensus/common", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index db023747414..1c7ab7a2546 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -68,6 +68,9 @@ parachain-info = { path = "../../../../parachains/pallets/parachain-info", defau parachains-common = { path = "../../../../parachains/common", default-features = false } # Bridges +bp-bridge-hub-rococo = { path = "../../../../bridges/primitives/chain-bridge-hub-rococo", default-features = false } +bp-bridge-hub-wococo = { path = "../../../../bridges/primitives/chain-bridge-hub-wococo", default-features = false } +bp-messages = { path = "../../../../bridges/primitives/messages", default-features = false } bp-polkadot-core = { path = "../../../../bridges/primitives/polkadot-core", default-features = false } bp-runtime = { path = "../../../../bridges/primitives/runtime", default-features = false } bp-rococo = { path = "../../../../bridges/primitives/chain-rococo", default-features = false } @@ -83,7 +86,10 @@ default = [ "std", ] std = [ + "bp-bridge-hub-rococo/std", + "bp-bridge-hub-wococo/std", "bp-polkadot-core/std", + "bp-messages/std", "bp-runtime/std", "bp-rococo/std", "bp-wococo/std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_config.rs new file mode 100644 index 00000000000..3a62bd829d3 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_config.rs @@ -0,0 +1,263 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +use bp_messages::{ + source_chain::{LaneMessageVerifier, TargetHeaderChain}, + target_chain::{DispatchMessage, MessageDispatch, ProvedMessages, SourceHeaderChain}, + InboundLaneData, LaneId, Message, OutboundLaneData, +}; +use bp_polkadot_core::Balance; +use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, BalanceOf, Chain}; +use codec::Decode; +use frame_support::{dispatch::Weight, parameter_types, RuntimeDebug}; + +parameter_types! { + // TODO:check-parameter + pub const BridgeHubRococoMaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; + pub const BridgeHubWococoMaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; + // TODO:check-parameter + pub const BridgeHubRococoMaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = + bp_bridge_hub_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + pub const BridgeHubWococoMaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = + bp_bridge_hub_wococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + // TODO:check-parameter + pub const BridgeHubRococoMaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = + bp_bridge_hub_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + pub const BridgeHubWococoMaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = + bp_bridge_hub_wococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + + pub const BridgeHubRococoChainId: bp_runtime::ChainId = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; + pub const BridgeHubWococoChainId: bp_runtime::ChainId = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; +} + +// TODO:check-parameter - when integration XCMv3 change this to struct +pub type PlainXcmPayload = sp_std::prelude::Vec; + +// TODO:check-parameter - when integration XCMv3 change this to struct +pub type FromBridgeHubRococoMessagePayload = PlainXcmPayload; +pub type FromBridgeHubWococoMessagePayload = PlainXcmPayload; +pub type ToBridgeHubRococoMessagePayload = PlainXcmPayload; +pub type ToBridgeHubWococoMessagePayload = PlainXcmPayload; + +// TODO:check-parameter - when integrating XCMv3 change this to FromBridgedChainMessagePayload +pub struct FromBridgeHubRococoMessageDispatch { + _marker: sp_std::marker::PhantomData<(SourceBridgeHubChain, TargetBridgeHubChain)>, +} +pub struct FromBridgeHubWococoMessageDispatch { + _marker: sp_std::marker::PhantomData<(SourceBridgeHubChain, TargetBridgeHubChain)>, +} + +impl + MessageDispatch, BalanceOf> + for FromBridgeHubRococoMessageDispatch +{ + type DispatchPayload = FromBridgeHubRococoMessagePayload; + + fn dispatch_weight( + message: &mut DispatchMessage>, + ) -> Weight { + log::error!("[FromBridgeHubRococoMessageDispatch] TODO: change here to XCMv3 dispatch_weight with XcmExecutor"); + 0 + } + + fn dispatch( + relayer_account: &AccountIdOf, + message: DispatchMessage>, + ) -> MessageDispatchResult { + log::error!("[FromBridgeHubRococoMessageDispatch] TODO: change here to XCMv3 dispatch with XcmExecutor"); + todo!("TODO: implement XCMv3 dispatch") + } +} + +impl + MessageDispatch, BalanceOf> + for FromBridgeHubWococoMessageDispatch +{ + type DispatchPayload = FromBridgeHubWococoMessagePayload; + + fn dispatch_weight( + message: &mut DispatchMessage>, + ) -> Weight { + log::error!("[FromBridgeHubWococoMessageDispatch] TODO: change here to XCMv3 dispatch_weight with XcmExecutor"); + 0 + } + + fn dispatch( + relayer_account: &AccountIdOf, + message: DispatchMessage>, + ) -> MessageDispatchResult { + log::error!("[FromBridgeHubWococoMessageDispatch] TODO: change here to XCMv3 dispatch with XcmExecutor"); + todo!("TODO: implement XCMv3 dispatch") + } +} + +pub struct ToBridgeHubRococoMessageVerifier { + _marker: sp_std::marker::PhantomData<(Origin, Sender)>, +} +pub struct ToBridgeHubWococoMessageVerifier { + _marker: sp_std::marker::PhantomData<(Origin, Sender)>, +} + +impl + LaneMessageVerifier< + Origin, + AccountIdOf, + ToBridgeHubRococoMessagePayload, + BalanceOf, + > for ToBridgeHubRococoMessageVerifier +{ + type Error = &'static str; + + fn verify_message( + submitter: &Origin, + delivery_and_dispatch_fee: &BalanceOf, + lane: &LaneId, + outbound_data: &OutboundLaneData, + payload: &ToBridgeHubRococoMessagePayload, + ) -> Result<(), Self::Error> { + todo!("TODO: ToBridgeHubRococoMessageVerifier - fix verify_message - at the begining to allow all") + } +} + +impl + LaneMessageVerifier< + Origin, + AccountIdOf, + ToBridgeHubWococoMessagePayload, + BalanceOf, + > for ToBridgeHubWococoMessageVerifier +{ + type Error = &'static str; + + fn verify_message( + submitter: &Origin, + delivery_and_dispatch_fee: &BalanceOf, + lane: &LaneId, + outbound_data: &OutboundLaneData, + payload: &ToBridgeHubWococoMessagePayload, + ) -> Result<(), Self::Error> { + todo!("TODO: ToBridgeHubWococoMessageVerifier - fix verify_message - at the begining to allow all") + } +} + +/// BridgeHubRococo chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct BridgeHubRococoMessagingSupport; +/// BridgeHubWococo chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct BridgeHubWococoMessagingSupport; + +impl SourceHeaderChain + for BridgeHubRococoMessagingSupport +{ + type Error = &'static str; + type MessagesProof = (); + + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result>, Self::Error> { + // TODO: need to add, bridges-runtime-common and refactor out of bin + // messages::target::verify_messages_proof_from_parachain::< + // WithRialtoParachainMessageBridge, + // bp_bridge_hub_rococo::Header, + // crate::Runtime, + // crate::WithRialtoParachainsInstance, + // >(ParaId(bp_rialto_parachain::RIALTO_PARACHAIN_ID), proof, messages_count) + todo!("TODO: fix and implement SourceHeaderChain::verify_messages_proof") + } +} + +impl + TargetHeaderChain< + ToBridgeHubRococoMessagePayload, + crate::AccountId, /* bp_bridge_hub_wococo::AccountId */ + > for BridgeHubRococoMessagingSupport +{ + type Error = &'static str; + type MessagesDeliveryProof = (); + + fn verify_message(payload: &ToBridgeHubRococoMessagePayload) -> Result<(), Self::Error> { + // messages::source::verify_chain_message::(payload) + todo!("TODO: fix implementation: TargetHeaderChain::verify_message") + } + + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result< + (LaneId, InboundLaneData), + Self::Error, + > { + // messages::source::verify_messages_delivery_proof_from_parachain::< + // WithRialtoParachainMessageBridge, + // bp_rialto_parachain::Header, + // Runtime, + // crate::WithRialtoParachainsInstance, + // >(ParaId(bp_rialto_parachain::RIALTO_PARACHAIN_ID), proof) + todo!("TODO: fix implementation: TargetHeaderChain::verify_messages_delivery_proof") + } +} + +impl SourceHeaderChain + for BridgeHubWococoMessagingSupport +{ + type Error = &'static str; + type MessagesProof = (); + + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result>, Self::Error> { + // TODO: need to add, bridges-runtime-common and refactor out of bin + // messages::target::verify_messages_proof_from_parachain::< + // WithMIllauParachainMessageBridge, + // bp_bridge_hub_wococo::Header, + // crate::Runtime, + // crate::WithRialtoParachainsInstance, + // >(ParaId(bp_rialto_parachain::RIALTO_PARACHAIN_ID), proof, messages_count) + todo!("TODO: fix and implement SourceHeaderChain::verify_messages_proof") + } +} + +impl + TargetHeaderChain< + ToBridgeHubWococoMessagePayload, + crate::AccountId, /* bp_bridge_hub_rococo::AccountId */ + > for BridgeHubWococoMessagingSupport +{ + type Error = &'static str; + type MessagesDeliveryProof = (); + + fn verify_message(payload: &ToBridgeHubWococoMessagePayload) -> Result<(), Self::Error> { + // messages::source::verify_chain_message::(payload) + todo!("TODO: fix implementation: TargetHeaderChain::verify_message") + } + + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result< + (LaneId, InboundLaneData), + Self::Error, + > { + // messages::source::verify_messages_delivery_proof_from_parachain::< + // WithRialtoParachainMessageBridge, + // bp_rialto_parachain::Header, + // Runtime, + // crate::WithRialtoParachainsInstance, + // >(ParaId(bp_rialto_parachain::RIALTO_PARACHAIN_ID), proof) + todo!("TODO: fix implementation: TargetHeaderChain::verify_messages_delivery_proof") + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index bb5b4f55aed..9f4271d139e 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// Copyright 2022 Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -24,6 +24,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); mod weights; pub mod xcm_config; +mod bridge_config; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use smallvec::smallvec; @@ -491,7 +492,7 @@ parameter_types! { pub const HeadersToKeep: u32 = 1024; } -/// Add granda bridge pallet to track Wococo relay chain +/// Add granda bridge pallet to track Wococo relay chain on Rococo BridgeHub pub type BridgeGrandpaWococoInstance = pallet_bridge_grandpa::Instance1; impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_wococo::Wococo; @@ -543,6 +544,70 @@ impl pallet_bridge_parachains::Config for Runtime type HeadsToKeep = ParachainHeadsToKeep; } +/// Add XCM messages support for BrigdeHubRococo to support Wococo->Rococo XCM messages +pub type WithBridgeHubWococoMessagesInstance = pallet_bridge_messages::Instance1; +impl pallet_bridge_messages::Config for Runtime { + type Event = Event; + // TODO:check-parameter - copy of MillauWeigth + refactor + type WeightInfo = (); + type BridgedChainId = bridge_config::BridgeHubWococoChainId; + // TODO:check-parameter - do we need any conversion rate or what ever? + type Parameter = (); + type MaxMessagesToPruneAtOnce = bridge_config::BridgeHubWococoMaxMessagesToPruneAtOnce; + type MaxUnrewardedRelayerEntriesAtInboundLane = bridge_config::BridgeHubWococoMaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = bridge_config::BridgeHubWococoMaxUnconfirmedMessagesAtInboundLane; + + type MaximalOutboundPayloadSize = (); + type OutboundPayload = bridge_config::ToBridgeHubWococoMessagePayload; + type OutboundMessageFee = crate::Balance; /* bp_bridge_hub_rococo::Balance */ + + type InboundPayload = bridge_config::FromBridgeHubWococoMessagePayload; + type InboundMessageFee = crate::Balance; /* bp_bridge_hub_wococo::Balance */ + type InboundRelayer = crate::AccountId; /* bp_bridge_hub_wococo::AccountId */ + + type TargetHeaderChain = bridge_config::BridgeHubWococoMessagingSupport; + type LaneMessageVerifier = bridge_config::ToBridgeHubWococoMessageVerifier; + // TODO:check-parameter - add because of rewards? + type MessageDeliveryAndDispatchPayment = (); + type OnMessageAccepted = (); + type OnDeliveryConfirmed = (); + + type SourceHeaderChain = bridge_config::BridgeHubWococoMessagingSupport; + type MessageDispatch = bridge_config::FromBridgeHubWococoMessageDispatch; +} + +/// Add XCM messages support for BrigdeHubWococo to support Rococo->Wococo XCM messages +pub type WithBridgeHubRococoMessagesInstance = pallet_bridge_messages::Instance2; +impl pallet_bridge_messages::Config for Runtime { + type Event = Event; + // TODO:check-parameter - copy of MillauWeigth + refactor + type WeightInfo = (); + type BridgedChainId = bridge_config::BridgeHubRococoChainId; + // TODO:check-parameter - do we need any conversion rate or what ever? + type Parameter = (); + type MaxMessagesToPruneAtOnce = bridge_config::BridgeHubRococoMaxMessagesToPruneAtOnce; + type MaxUnrewardedRelayerEntriesAtInboundLane = bridge_config::BridgeHubRococoMaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = bridge_config::BridgeHubRococoMaxUnconfirmedMessagesAtInboundLane; + + type MaximalOutboundPayloadSize = (); + type OutboundPayload = bridge_config::ToBridgeHubRococoMessagePayload; + type OutboundMessageFee = crate::Balance; /* bp_bridge_hub_wococo::Balance */ + + type InboundPayload = bridge_config::FromBridgeHubRococoMessagePayload; + type InboundMessageFee = crate::Balance; /* bp_bridge_hub_rococo::Balance */ + type InboundRelayer = crate::AccountId; /* bp_bridge_hub_rococo::AccountId */ + + type TargetHeaderChain = bridge_config::BridgeHubRococoMessagingSupport; + type LaneMessageVerifier = bridge_config::ToBridgeHubRococoMessageVerifier; + // TODO:check-parameter - add because of rewards? + type MessageDeliveryAndDispatchPayment = (); + type OnMessageAccepted = (); + type OnDeliveryConfirmed = (); + + type SourceHeaderChain = bridge_config::BridgeHubRococoMessagingSupport; + type MessageDispatch = bridge_config::FromBridgeHubRococoMessageDispatch; +} + /// Add shift session manager impl pallet_shift_session_manager::Config for Runtime {} @@ -584,10 +649,12 @@ construct_runtime!( // Wococo bridge modules BridgeWococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Config} = 41, BridgeWococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage, Event} = 42, + BridgeWococoMessages: pallet_bridge_messages::::{Pallet, Call, Storage, Event, Config} = 46, // Rococo bridge modules BridgeRococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Config} = 43, BridgeRococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage, Event} = 44, + BridgeRococoMessages: pallet_bridge_messages::::{Pallet, Call, Storage, Event, Config} = 45, // Sudo Sudo: pallet_sudo::{Pallet, Call, Config, Event, Storage} = 100, diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 543962807d3..84a2aeb60d7 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -1,3 +1,19 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + use super::{ AccountId, Balance, Balances, Call, Event, Origin, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, XcmpQueue, From 990041a299bc1fa5eba1c965d1db04e6d1704e37 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 6 Oct 2022 12:15:41 +0200 Subject: [PATCH 079/263] Fix compilation after subtree update --- Cargo.lock | 56 ++----------------- .../bridge-hub-rococo/src/bridge_config.rs | 2 - .../src/chain_spec/bridge_hubs.rs | 8 +++ 3 files changed, 13 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a851684a5f..e1cb2bffe34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -788,7 +788,6 @@ dependencies = [ "bitvec", "bp-runtime 0.1.0", "frame-support", - "frame-system", "hex", "hex-literal", "impl-trait-for-tuples", @@ -825,7 +824,6 @@ dependencies = [ "frame-support", "parity-scale-codec", "scale-info", - "serde", "sp-core", ] @@ -842,11 +840,9 @@ dependencies = [ "parity-util-mem", "scale-info", "serde", - "sp-api", "sp-core", "sp-runtime", "sp-std", - "sp-version", ] [[package]] @@ -882,33 +878,10 @@ dependencies = [ name = "bp-rococo" version = "0.1.0" dependencies = [ - "bp-messages 0.1.0", - "bp-polkadot-core 0.1.0", - "bp-runtime 0.1.0", + "bp-polkadot-core", + "bp-runtime", "frame-support", - "parity-scale-codec", - "smallvec", "sp-api", - "sp-runtime", - "sp-std", - "sp-version", -] - -[[package]] -name = "bp-rococo" -version = "0.1.0" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" -dependencies = [ - "bp-messages 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "bp-polkadot-core 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "bp-runtime 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "frame-support", - "parity-scale-codec", - "smallvec", - "sp-api", - "sp-runtime", - "sp-std", - "sp-version", ] [[package]] @@ -982,29 +955,10 @@ dependencies = [ name = "bp-wococo" version = "0.1.0" dependencies = [ - "bp-messages 0.1.0", - "bp-polkadot-core 0.1.0", - "bp-rococo 0.1.0", - "bp-runtime 0.1.0", - "parity-scale-codec", - "sp-api", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "bp-wococo" -version = "0.1.0" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" -dependencies = [ - "bp-messages 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "bp-polkadot-core 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "bp-rococo 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "bp-runtime 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "parity-scale-codec", + "bp-polkadot-core", + "bp-rococo", + "bp-runtime", "sp-api", - "sp-runtime", - "sp-std", ] [[package]] diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_config.rs index 3a62bd829d3..36a9658cf27 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_config.rs @@ -114,7 +114,6 @@ pub struct ToBridgeHubWococoMessageVerifier { impl LaneMessageVerifier< Origin, - AccountIdOf, ToBridgeHubRococoMessagePayload, BalanceOf, > for ToBridgeHubRococoMessageVerifier @@ -135,7 +134,6 @@ impl impl LaneMessageVerifier< Origin, - AccountIdOf, ToBridgeHubWococoMessagePayload, BalanceOf, > for ToBridgeHubWococoMessageVerifier diff --git a/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/polkadot-parachain/src/chain_spec/bridge_hubs.rs index ea69c27903f..bdb1326f0f0 100644 --- a/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -274,6 +274,14 @@ pub mod rococo { ..Default::default() }, bridge_rococo_grandpa: bridge_hub_rococo_runtime::BridgeRococoGrandpaConfig { + owner: bridges_pallet_owner.clone(), + ..Default::default() + }, + bridge_rococo_messages: bridge_hub_rococo_runtime::BridgeRococoMessagesConfig { + owner: bridges_pallet_owner.clone(), + ..Default::default() + }, + bridge_wococo_messages: bridge_hub_rococo_runtime::BridgeWococoMessagesConfig { owner: bridges_pallet_owner, ..Default::default() }, From cc2a65369ea7acc74f3ae500179b2e0a5a82f55e Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 25 Oct 2022 16:41:10 +0200 Subject: [PATCH 080/263] [BridgeHub] Init stuff for Haul/Dispatch xcm blobs --- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 2 + .../src/bridge_common_config.rs | 142 ++++++++++ .../bridge-hub-rococo/src/bridge_config.rs | 261 ------------------ .../src/bridge_hub_rococo_config.rs | 58 ++++ .../src/bridge_hub_wococo_config.rs | 59 ++++ .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 124 ++++++--- .../bridge-hub-rococo/src/xcm_config.rs | 72 +++-- 7 files changed, 393 insertions(+), 325 deletions(-) create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs delete mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_config.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 1c7ab7a2546..000db6e66b5 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -80,6 +80,7 @@ pallet-bridge-messages = { path = "../../../../bridges/modules/messages", defaul pallet-bridge-parachains = { path = "../../../../bridges/modules/parachains", default-features = false } pallet-bridge-relayers = { path = "../../../../bridges/modules/relayers", default-features = false } pallet-shift-session-manager = { path = "../../../../bridges/modules/shift-session-manager", default-features = false } +bridge-runtime-common = { path = "../../../../bridges/bin/runtime-common", default-features = false } [features] default = [ @@ -93,6 +94,7 @@ std = [ "bp-runtime/std", "bp-rococo/std", "bp-wococo/std", + "bridge-runtime-common/std", "codec/std", "log/std", "scale-info/std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs new file mode 100644 index 00000000000..608193fd148 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -0,0 +1,142 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +use crate::universal_exports::HaulBlob; +use bp_messages::{ + source_chain::MessagesBridge, + target_chain::{DispatchMessage, MessageDispatch}, + LaneId, +}; +use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, BalanceOf, Chain}; +use codec::Encode; +use frame_support::{dispatch::Weight, parameter_types}; +use xcm::latest::prelude::*; + +// TODO:check-parameter - we could possibly use BridgeMessage from xcm:v3 stuff +/// PLain "XCM" payload, which we transfer through bridge +pub type XcmAsPlainPayload = sp_std::prelude::Vec; + +// TODO:check-parameter +parameter_types! { + pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; + pub const MaxRequests: u32 = 64; + pub const HeadersToKeep: u32 = 1024; +} + +/// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages from other BridgeHub +pub struct XcmBlobMessageDispatch { + _marker: + sp_std::marker::PhantomData<(SourceBridgeHubChain, TargetBridgeHubChain, DispatchBlob)>, +} + +impl< + SourceBridgeHubChain: Chain, + TargetBridgeHubChain: Chain, + DispatchBlob: crate::universal_exports::DispatchBlob, + > MessageDispatch, BalanceOf> + for XcmBlobMessageDispatch +{ + type DispatchPayload = XcmAsPlainPayload; + + fn dispatch_weight( + message: &mut DispatchMessage>, + ) -> Weight { + log::error!( + "[XcmBlobMessageDispatch] TODO: change here to XCMv3 dispatch_weight with XcmExecutor" + ); + 0 + } + + fn dispatch( + _relayer_account: &AccountIdOf, + message: DispatchMessage>, + ) -> MessageDispatchResult { + log::warn!("[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob triggering"); + let payload = match message.data.payload { + Ok(payload) => payload, + Err(e) => { + log::error!("[XcmBlobMessageDispatch] payload error: {:?}", e); + return MessageDispatchResult { + dispatch_result: false, + unspent_weight: 0, + dispatch_fee_paid_during_dispatch: false, + } + }, + }; + let dispatch_result = match DispatchBlob::dispatch_blob(payload) { + Ok(_) => true, + Err(e) => { + log::error!( + "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?}", + e + ); + false + }, + }; + MessageDispatchResult { + dispatch_result, + dispatch_fee_paid_during_dispatch: false, + unspent_weight: 0, + } + } +} + +/// [`XcmBlobHauler`] is responsible for sending messages to the bridge "point-to-point link" from one side, +/// where on the other it can be dispatched by [`XcmBlobMessageDispatch`]. +pub trait XcmBlobHauler { + /// Which chain is sending + type SenderChain: Chain; + + /// Runtime message sender adapter. + type MessageSender: MessagesBridge< + super::Origin, + AccountIdOf, + BalanceOf, + XcmAsPlainPayload, + >; + + /// Our location within the Consensus Universe. + fn message_sender_origin() -> InteriorMultiLocation; + + /// Return message lane (as "point-to-point link") used to deliver XCM messages. + fn xcm_lane() -> LaneId; +} + +impl HaulBlob for T { + fn haul_blob(blob: sp_std::prelude::Vec) { + let lane = T::xcm_lane(); + // TODO:check-parameter - fee could be taken from BridgeMessage - or add as optional fo send_message + // TODO:check-parameter - or add here something like PriceForSiblingDelivery + let fee = ::Balance::from(0u8); + + let result = T::MessageSender::send_message( + pallet_xcm::Origin::from(MultiLocation::from(T::message_sender_origin())).into(), + lane, + blob, + fee, + ); + let result = result + .map(|artifacts| { + let hash = (lane, artifacts.nonce).using_encoded(sp_io::hashing::blake2_256); + hash + }) + .map_err(|e| { + e + }); + log::info!(target: "runtime::bridge-hub", "haul_blob result: {:?} on lane: {:?}", result, lane); + result.expect("failed to process: TODO:check-parameter - wait for origin/gav-xcm-v3, there is a comment about handliing errors for HaulBlob"); + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_config.rs deleted file mode 100644 index 36a9658cf27..00000000000 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_config.rs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2022 Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus 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. - -// Cumulus 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 Cumulus. If not, see . - -use bp_messages::{ - source_chain::{LaneMessageVerifier, TargetHeaderChain}, - target_chain::{DispatchMessage, MessageDispatch, ProvedMessages, SourceHeaderChain}, - InboundLaneData, LaneId, Message, OutboundLaneData, -}; -use bp_polkadot_core::Balance; -use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, BalanceOf, Chain}; -use codec::Decode; -use frame_support::{dispatch::Weight, parameter_types, RuntimeDebug}; - -parameter_types! { - // TODO:check-parameter - pub const BridgeHubRococoMaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; - pub const BridgeHubWococoMaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; - // TODO:check-parameter - pub const BridgeHubRococoMaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = - bp_bridge_hub_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; - pub const BridgeHubWococoMaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = - bp_bridge_hub_wococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; - // TODO:check-parameter - pub const BridgeHubRococoMaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = - bp_bridge_hub_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - pub const BridgeHubWococoMaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = - bp_bridge_hub_wococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - - pub const BridgeHubRococoChainId: bp_runtime::ChainId = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; - pub const BridgeHubWococoChainId: bp_runtime::ChainId = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; -} - -// TODO:check-parameter - when integration XCMv3 change this to struct -pub type PlainXcmPayload = sp_std::prelude::Vec; - -// TODO:check-parameter - when integration XCMv3 change this to struct -pub type FromBridgeHubRococoMessagePayload = PlainXcmPayload; -pub type FromBridgeHubWococoMessagePayload = PlainXcmPayload; -pub type ToBridgeHubRococoMessagePayload = PlainXcmPayload; -pub type ToBridgeHubWococoMessagePayload = PlainXcmPayload; - -// TODO:check-parameter - when integrating XCMv3 change this to FromBridgedChainMessagePayload -pub struct FromBridgeHubRococoMessageDispatch { - _marker: sp_std::marker::PhantomData<(SourceBridgeHubChain, TargetBridgeHubChain)>, -} -pub struct FromBridgeHubWococoMessageDispatch { - _marker: sp_std::marker::PhantomData<(SourceBridgeHubChain, TargetBridgeHubChain)>, -} - -impl - MessageDispatch, BalanceOf> - for FromBridgeHubRococoMessageDispatch -{ - type DispatchPayload = FromBridgeHubRococoMessagePayload; - - fn dispatch_weight( - message: &mut DispatchMessage>, - ) -> Weight { - log::error!("[FromBridgeHubRococoMessageDispatch] TODO: change here to XCMv3 dispatch_weight with XcmExecutor"); - 0 - } - - fn dispatch( - relayer_account: &AccountIdOf, - message: DispatchMessage>, - ) -> MessageDispatchResult { - log::error!("[FromBridgeHubRococoMessageDispatch] TODO: change here to XCMv3 dispatch with XcmExecutor"); - todo!("TODO: implement XCMv3 dispatch") - } -} - -impl - MessageDispatch, BalanceOf> - for FromBridgeHubWococoMessageDispatch -{ - type DispatchPayload = FromBridgeHubWococoMessagePayload; - - fn dispatch_weight( - message: &mut DispatchMessage>, - ) -> Weight { - log::error!("[FromBridgeHubWococoMessageDispatch] TODO: change here to XCMv3 dispatch_weight with XcmExecutor"); - 0 - } - - fn dispatch( - relayer_account: &AccountIdOf, - message: DispatchMessage>, - ) -> MessageDispatchResult { - log::error!("[FromBridgeHubWococoMessageDispatch] TODO: change here to XCMv3 dispatch with XcmExecutor"); - todo!("TODO: implement XCMv3 dispatch") - } -} - -pub struct ToBridgeHubRococoMessageVerifier { - _marker: sp_std::marker::PhantomData<(Origin, Sender)>, -} -pub struct ToBridgeHubWococoMessageVerifier { - _marker: sp_std::marker::PhantomData<(Origin, Sender)>, -} - -impl - LaneMessageVerifier< - Origin, - ToBridgeHubRococoMessagePayload, - BalanceOf, - > for ToBridgeHubRococoMessageVerifier -{ - type Error = &'static str; - - fn verify_message( - submitter: &Origin, - delivery_and_dispatch_fee: &BalanceOf, - lane: &LaneId, - outbound_data: &OutboundLaneData, - payload: &ToBridgeHubRococoMessagePayload, - ) -> Result<(), Self::Error> { - todo!("TODO: ToBridgeHubRococoMessageVerifier - fix verify_message - at the begining to allow all") - } -} - -impl - LaneMessageVerifier< - Origin, - ToBridgeHubWococoMessagePayload, - BalanceOf, - > for ToBridgeHubWococoMessageVerifier -{ - type Error = &'static str; - - fn verify_message( - submitter: &Origin, - delivery_and_dispatch_fee: &BalanceOf, - lane: &LaneId, - outbound_data: &OutboundLaneData, - payload: &ToBridgeHubWococoMessagePayload, - ) -> Result<(), Self::Error> { - todo!("TODO: ToBridgeHubWococoMessageVerifier - fix verify_message - at the begining to allow all") - } -} - -/// BridgeHubRococo chain from message lane point of view. -#[derive(RuntimeDebug, Clone, Copy)] -pub struct BridgeHubRococoMessagingSupport; -/// BridgeHubWococo chain from message lane point of view. -#[derive(RuntimeDebug, Clone, Copy)] -pub struct BridgeHubWococoMessagingSupport; - -impl SourceHeaderChain - for BridgeHubRococoMessagingSupport -{ - type Error = &'static str; - type MessagesProof = (); - - fn verify_messages_proof( - proof: Self::MessagesProof, - messages_count: u32, - ) -> Result>, Self::Error> { - // TODO: need to add, bridges-runtime-common and refactor out of bin - // messages::target::verify_messages_proof_from_parachain::< - // WithRialtoParachainMessageBridge, - // bp_bridge_hub_rococo::Header, - // crate::Runtime, - // crate::WithRialtoParachainsInstance, - // >(ParaId(bp_rialto_parachain::RIALTO_PARACHAIN_ID), proof, messages_count) - todo!("TODO: fix and implement SourceHeaderChain::verify_messages_proof") - } -} - -impl - TargetHeaderChain< - ToBridgeHubRococoMessagePayload, - crate::AccountId, /* bp_bridge_hub_wococo::AccountId */ - > for BridgeHubRococoMessagingSupport -{ - type Error = &'static str; - type MessagesDeliveryProof = (); - - fn verify_message(payload: &ToBridgeHubRococoMessagePayload) -> Result<(), Self::Error> { - // messages::source::verify_chain_message::(payload) - todo!("TODO: fix implementation: TargetHeaderChain::verify_message") - } - - fn verify_messages_delivery_proof( - proof: Self::MessagesDeliveryProof, - ) -> Result< - (LaneId, InboundLaneData), - Self::Error, - > { - // messages::source::verify_messages_delivery_proof_from_parachain::< - // WithRialtoParachainMessageBridge, - // bp_rialto_parachain::Header, - // Runtime, - // crate::WithRialtoParachainsInstance, - // >(ParaId(bp_rialto_parachain::RIALTO_PARACHAIN_ID), proof) - todo!("TODO: fix implementation: TargetHeaderChain::verify_messages_delivery_proof") - } -} - -impl SourceHeaderChain - for BridgeHubWococoMessagingSupport -{ - type Error = &'static str; - type MessagesProof = (); - - fn verify_messages_proof( - proof: Self::MessagesProof, - messages_count: u32, - ) -> Result>, Self::Error> { - // TODO: need to add, bridges-runtime-common and refactor out of bin - // messages::target::verify_messages_proof_from_parachain::< - // WithMIllauParachainMessageBridge, - // bp_bridge_hub_wococo::Header, - // crate::Runtime, - // crate::WithRialtoParachainsInstance, - // >(ParaId(bp_rialto_parachain::RIALTO_PARACHAIN_ID), proof, messages_count) - todo!("TODO: fix and implement SourceHeaderChain::verify_messages_proof") - } -} - -impl - TargetHeaderChain< - ToBridgeHubWococoMessagePayload, - crate::AccountId, /* bp_bridge_hub_rococo::AccountId */ - > for BridgeHubWococoMessagingSupport -{ - type Error = &'static str; - type MessagesDeliveryProof = (); - - fn verify_message(payload: &ToBridgeHubWococoMessagePayload) -> Result<(), Self::Error> { - // messages::source::verify_chain_message::(payload) - todo!("TODO: fix implementation: TargetHeaderChain::verify_message") - } - - fn verify_messages_delivery_proof( - proof: Self::MessagesDeliveryProof, - ) -> Result< - (LaneId, InboundLaneData), - Self::Error, - > { - // messages::source::verify_messages_delivery_proof_from_parachain::< - // WithRialtoParachainMessageBridge, - // bp_rialto_parachain::Header, - // Runtime, - // crate::WithRialtoParachainsInstance, - // >(ParaId(bp_rialto_parachain::RIALTO_PARACHAIN_ID), proof) - todo!("TODO: fix implementation: TargetHeaderChain::verify_messages_delivery_proof") - } -} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs new file mode 100644 index 00000000000..a44e9540ccf --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -0,0 +1,58 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +use bp_messages::{source_chain::{LaneMessageVerifier, TargetHeaderChain}, target_chain::{ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, OutboundLaneData, MessageKey, MessageData}; +use bp_runtime::{BalanceOf, Chain}; +use frame_support::{parameter_types, RuntimeDebug}; +use xcm::prelude::{InteriorMultiLocation, NetworkId}; +use bp_messages::target_chain::ProvedLaneMessages; +use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; +use crate::universal_exports::{BridgeBlobDispatcher, HaulBlobExporter}; +use crate::{WithBridgeHubWococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, XcmRouter}; +use crate::Runtime; +use crate::ParachainInfo; +use xcm::latest::prelude::*; + +// TODO:check-parameter +parameter_types! { + pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = + bp_bridge_hub_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = + bp_bridge_hub_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + pub const BridgeHubWococoChainId: bp_runtime::ChainId = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; + pub BridgeHubRococoUniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(Rococo), Parachain(ParachainInfo::parachain_id().into())); + pub WococoGlobalConsensusNetwork: NetworkId = NetworkId::Wococo; +} + +/// Dispatches received XCM messages from other bridge +pub type OnBridgeHubRococoBlobDispatcher = BridgeBlobDispatcher; + +/// Export XCM messages to be relayed to the otherside +pub type ToBridgeHubWococoHaulBlobExporter = HaulBlobExporter; +pub struct ToBridgeHubWococoXcmBlobHauler; +pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO: LaneId = [0, 0, 0, 1]; +impl XcmBlobHauler for ToBridgeHubWococoXcmBlobHauler { + type SenderChain = bp_bridge_hub_rococo::BridgeHubRococo; + type MessageSender = pallet_bridge_messages::Pallet; + + fn message_sender_origin() -> InteriorMultiLocation { + crate::xcm_config::UniversalLocation::get() + } + + fn xcm_lane() -> LaneId { + DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs new file mode 100644 index 00000000000..2a521405db8 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -0,0 +1,59 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +use bp_messages::{source_chain::{LaneMessageVerifier, TargetHeaderChain}, target_chain::{ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, OutboundLaneData, MessageKey, MessageData}; +use bp_runtime::{BalanceOf, Chain}; +use frame_support::{parameter_types, RuntimeDebug}; +use xcm::prelude::{InteriorMultiLocation, NetworkId}; +use bp_messages::target_chain::ProvedLaneMessages; +use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; +use crate::universal_exports::{BridgeBlobDispatcher, HaulBlobExporter}; +use crate::{WithBridgeHubRococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, XcmRouter}; +use crate::Runtime; +use crate::ParachainInfo; +use xcm::latest::prelude::*; + +// TODO:check-parameter +parameter_types! { + pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = + bp_bridge_hub_wococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = + bp_bridge_hub_wococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + pub const BridgeHubRococoChainId: bp_runtime::ChainId = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; + pub BridgeHubWococoUniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(Wococo), Parachain(ParachainInfo::parachain_id().into())); + pub RococoGlobalConsensusNetwork: NetworkId = NetworkId::Rococo; +} + +/// Dispatches received XCM messages from other bridge +pub type OnBridgeHubWococoBlobDispatcher = BridgeBlobDispatcher; + +/// Export XCM messages to be relayed to the otherside +pub type ToBridgeHubRococoHaulBlobExporter = HaulBlobExporter; +pub struct ToBridgeHubRococoXcmBlobHauler; +pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO: LaneId = [0, 0, 0, 1]; +impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { + type SenderChain = bp_bridge_hub_wococo::BridgeHubWococo; + type MessageSender = pallet_bridge_messages::Pallet; + + fn message_sender_origin() -> InteriorMultiLocation { + crate::xcm_config::UniversalLocation::get() + } + + fn xcm_lane() -> LaneId { + DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO + } +} + diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 9f4271d139e..97a5d88ec2c 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -22,10 +22,13 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +pub mod bridge_common_config; +pub mod bridge_hub_rococo_config; +pub mod bridge_hub_wococo_config; mod weights; pub mod xcm_config; -mod bridge_config; +use bridge_common_config::*; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use smallvec::smallvec; use sp_api::impl_runtime_apis; @@ -59,18 +62,20 @@ pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; use xcm_config::{XcmConfig, XcmOriginToTransactDispatchOrigin}; -use bp_polkadot_core::parachains::ParaId; use bp_runtime::{HeaderId, HeaderIdProvider}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; -// Polkadot imports use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; +use sp_runtime::traits::ConstU32; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; -// XCM Imports +use crate::{ + bridge_hub_rococo_config::OnBridgeHubRococoBlobDispatcher, + bridge_hub_wococo_config::OnBridgeHubWococoBlobDispatcher, xcm_config::XcmRouter, +}; use parachains_common::{AccountId, Signature}; use xcm::latest::prelude::BodyId; use xcm_executor::XcmExecutor; @@ -238,11 +243,18 @@ pub fn native_version() -> NativeVersion { } // TODO:check-parameter - move to bridges/primitives, once rebased and would compile with bp_bridge_hub_xyz dependencies -mod runtime_api { - use super::BlockNumber; - use super::Hash; +pub mod runtime_api { + use super::{BlockNumber, Hash}; bp_runtime::decl_bridge_finality_runtime_apis!(rococo); bp_runtime::decl_bridge_finality_runtime_apis!(wococo); + + use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, + }; + use frame_support::{sp_runtime::FixedU128, Parameter}; + use sp_std::prelude::Vec; + bp_runtime::decl_bridge_messages_runtime_apis!(bridge_hub_rococo); + bp_runtime::decl_bridge_messages_runtime_apis!(bridge_hub_wococo); } parameter_types! { @@ -412,11 +424,12 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type Event = Event; type XcmExecutor = XcmExecutor; type ChannelInfo = ParachainSystem; - type VersionWrapper = (); + type VersionWrapper = PolkadotXcm; type ExecuteOverweightOrigin = EnsureRoot; type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; - type WeightInfo = (); + type PriceForSiblingDelivery = (); + type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; } impl cumulus_pallet_dmp_queue::Config for Runtime { @@ -485,12 +498,6 @@ impl pallet_sudo::Config for Runtime { } // Add bridge pallets (GPA) -parameter_types! { - // TODO:check-parameter - pub const MaxRequests: u32 = 64; - // TODO:check-parameter - pub const HeadersToKeep: u32 = 1024; -} /// Add granda bridge pallet to track Wococo relay chain on Rococo BridgeHub pub type BridgeGrandpaWococoInstance = pallet_bridge_grandpa::Instance1; @@ -523,9 +530,9 @@ parameter_types! { /// Add parachain bridge pallet to track Wococo bridge hub parachain pub type BridgeParachainWococoInstance = pallet_bridge_parachains::Instance1; impl pallet_bridge_parachains::Config for Runtime { + type Event = Event; // TODO:check-parameter type WeightInfo = (); - type Event = Event; type BridgesGrandpaPalletInstance = BridgeGrandpaWococoInstance; type ParasPalletName = WococoBridgeParachainPalletName; type TrackedParachains = Everything; @@ -544,24 +551,25 @@ impl pallet_bridge_parachains::Config for Runtime type HeadsToKeep = ParachainHeadsToKeep; } -/// Add XCM messages support for BrigdeHubRococo to support Wococo->Rococo XCM messages +/// Add XCM messages support for BrigdeHubRococo to support Rococo->Wococo XCM messages pub type WithBridgeHubWococoMessagesInstance = pallet_bridge_messages::Instance1; impl pallet_bridge_messages::Config for Runtime { type Event = Event; // TODO:check-parameter - copy of MillauWeigth + refactor type WeightInfo = (); - type BridgedChainId = bridge_config::BridgeHubWococoChainId; - // TODO:check-parameter - do we need any conversion rate or what ever? + type BridgedChainId = bridge_hub_rococo_config::BridgeHubWococoChainId; type Parameter = (); - type MaxMessagesToPruneAtOnce = bridge_config::BridgeHubWococoMaxMessagesToPruneAtOnce; - type MaxUnrewardedRelayerEntriesAtInboundLane = bridge_config::BridgeHubWococoMaxUnrewardedRelayerEntriesAtInboundLane; - type MaxUnconfirmedMessagesAtInboundLane = bridge_config::BridgeHubWococoMaxUnconfirmedMessagesAtInboundLane; + type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; + type MaxUnrewardedRelayerEntriesAtInboundLane = + bridge_hub_rococo_config::MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = + bridge_hub_rococo_config::MaxUnconfirmedMessagesAtInboundLane; type MaximalOutboundPayloadSize = (); - type OutboundPayload = bridge_config::ToBridgeHubWococoMessagePayload; - type OutboundMessageFee = crate::Balance; /* bp_bridge_hub_rococo::Balance */ + type OutboundPayload = XcmAsPlainPayload; + type OutboundMessageFee = crate::Balance; /* bp_bridge_hub_rococo::Balance */ - type InboundPayload = bridge_config::FromBridgeHubWococoMessagePayload; + type InboundPayload = XcmAsPlainPayload; type InboundMessageFee = crate::Balance; /* bp_bridge_hub_wococo::Balance */ type InboundRelayer = crate::AccountId; /* bp_bridge_hub_wococo::AccountId */ @@ -573,27 +581,32 @@ impl pallet_bridge_messages::Config for Run type OnDeliveryConfirmed = (); type SourceHeaderChain = bridge_config::BridgeHubWococoMessagingSupport; - type MessageDispatch = bridge_config::FromBridgeHubWococoMessageDispatch; + type MessageDispatch = XcmBlobMessageDispatch< + bp_bridge_hub_wococo::BridgeHubWococo, + bp_bridge_hub_rococo::BridgeHubRococo, + OnBridgeHubRococoBlobDispatcher, + >; } -/// Add XCM messages support for BrigdeHubWococo to support Rococo->Wococo XCM messages +/// Add XCM messages support for BrigdeHubWococo to support Wococo->Rococo XCM messages pub type WithBridgeHubRococoMessagesInstance = pallet_bridge_messages::Instance2; impl pallet_bridge_messages::Config for Runtime { type Event = Event; // TODO:check-parameter - copy of MillauWeigth + refactor type WeightInfo = (); - type BridgedChainId = bridge_config::BridgeHubRococoChainId; - // TODO:check-parameter - do we need any conversion rate or what ever? + type BridgedChainId = bridge_hub_wococo_config::BridgeHubRococoChainId; type Parameter = (); - type MaxMessagesToPruneAtOnce = bridge_config::BridgeHubRococoMaxMessagesToPruneAtOnce; - type MaxUnrewardedRelayerEntriesAtInboundLane = bridge_config::BridgeHubRococoMaxUnrewardedRelayerEntriesAtInboundLane; - type MaxUnconfirmedMessagesAtInboundLane = bridge_config::BridgeHubRococoMaxUnconfirmedMessagesAtInboundLane; + type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; + type MaxUnrewardedRelayerEntriesAtInboundLane = + bridge_hub_wococo_config::MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = + bridge_hub_wococo_config::MaxUnconfirmedMessagesAtInboundLane; type MaximalOutboundPayloadSize = (); - type OutboundPayload = bridge_config::ToBridgeHubRococoMessagePayload; - type OutboundMessageFee = crate::Balance; /* bp_bridge_hub_wococo::Balance */ + type OutboundPayload = XcmAsPlainPayload; + type OutboundMessageFee = crate::Balance; /* bp_bridge_hub_wococo::Balance */ - type InboundPayload = bridge_config::FromBridgeHubRococoMessagePayload; + type InboundPayload = XcmAsPlainPayload; type InboundMessageFee = crate::Balance; /* bp_bridge_hub_rococo::Balance */ type InboundRelayer = crate::AccountId; /* bp_bridge_hub_rococo::AccountId */ @@ -605,7 +618,11 @@ impl pallet_bridge_messages::Config for Run type OnDeliveryConfirmed = (); type SourceHeaderChain = bridge_config::BridgeHubRococoMessagingSupport; - type MessageDispatch = bridge_config::FromBridgeHubRococoMessageDispatch; + type MessageDispatch = XcmBlobMessageDispatch< + bp_bridge_hub_rococo::BridgeHubRococo, + bp_bridge_hub_wococo::BridgeHubWococo, + OnBridgeHubWococoBlobDispatcher, + >; } /// Add shift session manager @@ -796,6 +813,41 @@ impl_runtime_apis! { } } + // This exposed by BridgeHubRococo + impl runtime_api::ToBridgeHubWococoOutboundLaneApi for Runtime { + fn estimate_message_delivery_and_dispatch_fee( + _lane_id: bp_messages::LaneId, + payload: XcmAsPlainPayload, + _conversion_rate: Option, + ) -> Option { + None + } + + fn message_details( + lane: bp_messages::LaneId, + begin: bp_messages::MessageNonce, + end: bp_messages::MessageNonce, + ) -> Vec> { + bridge_runtime_common::messages_api::outbound_message_details::< + Runtime, + WithBridgeHubWococoMessagesInstance, + >(lane, begin, end) + } + } + + // This is exposed by BridgeHubWococo + impl runtime_api::FromBridgeHubRococoInboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + ) -> Vec { + bridge_runtime_common::messages_api::inbound_message_details::< + Runtime, + WithBridgeHubRococoMessagesInstance, + >(lane, messages) + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade() -> (Weight, Weight) { diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 84a2aeb60d7..c6dead42d5a 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -15,15 +15,18 @@ // along with Cumulus. If not, see . use super::{ - AccountId, Balance, Balances, Call, Event, Origin, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, - XcmpQueue, + AccountId, Balance, Balances, Call, Event, Origin, ParachainInfo, ParachainSystem, PolkadotXcm, + Runtime, XcmpQueue, +}; +use crate::{ + bridge_hub_rococo_config::ToBridgeHubWococoHaulBlobExporter, + bridge_hub_wococo_config::ToBridgeHubRococoHaulBlobExporter, }; use frame_support::{ match_types, parameter_types, traits::{Everything, Nothing}, - weights::Weight, + weights::{IdentityFee, Weight}, }; -use frame_support::weights::IdentityFee; use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use xcm::latest::prelude::*; @@ -34,7 +37,7 @@ use xcm_builder::{ SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; -use xcm_executor::XcmExecutor; +use xcm_executor::{traits::ExportXcm, XcmExecutor}; parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); @@ -192,6 +195,8 @@ pub type Barrier = ( TakeWeightCredit, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, + // TODO:check-parameter - supporting unpaid execution at first then SovereignPaid + AllowUnpaidExecutionFrom, // ^^^ Parent & its unit plurality gets free execution ); @@ -219,7 +224,7 @@ impl xcm_executor::Config for XcmConfig { type AssetLocker = (); type AssetExchanger = (); type FeeManager = (); - type MessageExporter = (); + type MessageExporter = BridgeHubRococoOrBridgeHubWococoSwitchExporter; type UniversalAliases = Nothing; type CallDispatcher = Call; } @@ -231,32 +236,11 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); -// TODO: hacked -// impl pallet_xcm::Config for Runtime { -// type Event = Event; -// type SendXcmOrigin = EnsureXcmOrigin; -// type XcmRouter = XcmRouter; -// type ExecuteXcmOrigin = EnsureXcmOrigin; -// type XcmExecuteFilter = Nothing; -// // ^ Disable dispatchable execute on the XCM pallet. -// // Needs to be `Everything` for local testing. -// type XcmExecutor = XcmExecutor; -// type XcmTeleportFilter = Everything; -// type XcmReserveTransferFilter = Nothing; -// type Weigher = FixedWeightBounds; -// type LocationInverter = LocationInverter; -// type Origin = Origin; -// type Call = Call; -// -// const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; -// // ^ Override for AdvertisedXcmVersion default -// type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; -// } impl pallet_xcm::Config for Runtime { type Event = Event; type SendXcmOrigin = EnsureXcmOrigin; @@ -283,3 +267,35 @@ impl cumulus_pallet_xcm::Config for Runtime { type Event = Event; type XcmExecutor = XcmExecutor; } + +/// Hacky switch implementation, because we have just one runtime for Rococo and Wococo BridgeHub, so it means we have just one XcmConfig +pub struct BridgeHubRococoOrBridgeHubWococoSwitchExporter; +impl ExportXcm for BridgeHubRococoOrBridgeHubWococoSwitchExporter { + type Ticket = (NetworkId, (sp_std::prelude::Vec, XcmHash)); + + fn validate( + network: NetworkId, + channel: u32, + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { + match network { + Rococo => + ToBridgeHubRococoHaulBlobExporter::validate(network, channel, destination, message) + .map(|result| ((Rococo, result.0), result.1)), + Wococo => + ToBridgeHubWococoHaulBlobExporter::validate(network, channel, destination, message) + .map(|result| ((Wococo, result.0), result.1)), + _ => unimplemented!("Unsupported network: {:?}", network), + } + } + + fn deliver(ticket: Self::Ticket) -> Result { + let (network, ticket) = ticket; + match network { + Rococo => ToBridgeHubRococoHaulBlobExporter::deliver(ticket), + Wococo => ToBridgeHubWococoHaulBlobExporter::deliver(ticket), + _ => unimplemented!("Unsupported network: {:?}", network), + } + } +} From 04c67c4d6a1b85f000d04f4729f35da9b1ff38d5 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 31 Oct 2022 10:59:28 +0100 Subject: [PATCH 081/263] [BridgeHub] Init stuff for bridge messaging --- .../src/bridge_common_config.rs | 2 +- .../src/bridge_hub_rococo_config.rs | 227 ++++++++++++++++-- .../src/bridge_hub_wococo_config.rs | 224 +++++++++++++++-- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 34 ++- 4 files changed, 439 insertions(+), 48 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 608193fd148..eb77346e09a 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -55,7 +55,7 @@ impl< message: &mut DispatchMessage>, ) -> Weight { log::error!( - "[XcmBlobMessageDispatch] TODO: change here to XCMv3 dispatch_weight with XcmExecutor" + "[XcmBlobMessageDispatch] TODO: change here to XCMv3 dispatch_weight with XcmExecutor - message: ?...?", ); 0 } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index a44e9540ccf..8cb45f5cdf2 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -14,17 +14,32 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use bp_messages::{source_chain::{LaneMessageVerifier, TargetHeaderChain}, target_chain::{ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, OutboundLaneData, MessageKey, MessageData}; -use bp_runtime::{BalanceOf, Chain}; -use frame_support::{parameter_types, RuntimeDebug}; -use xcm::prelude::{InteriorMultiLocation, NetworkId}; -use bp_messages::target_chain::ProvedLaneMessages; -use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; -use crate::universal_exports::{BridgeBlobDispatcher, HaulBlobExporter}; -use crate::{WithBridgeHubWococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, XcmRouter}; -use crate::Runtime; -use crate::ParachainInfo; -use xcm::latest::prelude::*; +use crate::{ + universal_exports::{BridgeBlobDispatcher, HaulBlobExporter}, + ParachainInfo, Runtime, WithBridgeHubWococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, + XcmRouter, +}; +use bp_messages::{ + source_chain::TargetHeaderChain, + target_chain::{ProvedMessages, SourceHeaderChain}, + InboundLaneData, LaneId, Message, MessageNonce, +}; +use bp_polkadot_core::parachains::ParaId; +use bp_runtime::{Chain, ChainId}; +use bridge_runtime_common::{ + messages, + messages::{ + target::FromBridgedChainMessagesProof, BasicConfirmationTransactionEstimation, + BridgedChain, ChainWithMessages, MessageBridge, MessageTransaction, ThisChain, + ThisChainWithMessages, WeightOf, + }, +}; +use frame_support::{dispatch::Weight, parameter_types, RuntimeDebug}; +use sp_runtime::FixedU128; +use xcm::{ + latest::prelude::*, + prelude::{InteriorMultiLocation, NetworkId}, +}; // TODO:check-parameter parameter_types! { @@ -38,15 +53,18 @@ parameter_types! { } /// Dispatches received XCM messages from other bridge -pub type OnBridgeHubRococoBlobDispatcher = BridgeBlobDispatcher; +pub type OnBridgeHubRococoBlobDispatcher = + BridgeBlobDispatcher; /// Export XCM messages to be relayed to the otherside -pub type ToBridgeHubWococoHaulBlobExporter = HaulBlobExporter; +pub type ToBridgeHubWococoHaulBlobExporter = + HaulBlobExporter; pub struct ToBridgeHubWococoXcmBlobHauler; -pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO: LaneId = [0, 0, 0, 1]; +pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO: LaneId = [0, 0, 0, 2]; impl XcmBlobHauler for ToBridgeHubWococoXcmBlobHauler { type SenderChain = bp_bridge_hub_rococo::BridgeHubRococo; - type MessageSender = pallet_bridge_messages::Pallet; + type MessageSender = + pallet_bridge_messages::Pallet; fn message_sender_origin() -> InteriorMultiLocation { crate::xcm_config::UniversalLocation::get() @@ -56,3 +74,182 @@ impl XcmBlobHauler for ToBridgeHubWococoXcmBlobHauler { DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO } } + +/// Messaging Bridge configuration for BridgeHubRococo -> BridgeHubWococo +pub struct WithBridgeHubWococoMessageBridge; +impl MessageBridge for WithBridgeHubWococoMessageBridge { + // TODO:check-parameter - relayers rewards + const RELAYER_FEE_PERCENT: u32 = 0; + const THIS_CHAIN_ID: ChainId = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; + const BRIDGED_CHAIN_ID: ChainId = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = + bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME; + type ThisChain = BridgeHubRococo; + type BridgedChain = BridgeHubWococo; + + fn bridged_balance_to_this_balance( + bridged_balance: bridge_runtime_common::messages::BalanceOf>, + bridged_to_this_conversion_rate_override: Option, + ) -> bridge_runtime_common::messages::BalanceOf> { + log::info!("[WithBridgeHubWococoMessageBridge] bridged_balance_to_this_balance - bridged_balance: {:?}, bridged_to_this_conversion_rate_override: {:?}", bridged_balance, bridged_to_this_conversion_rate_override); + unimplemented!("TODO: WithBridgeHubWococoMessageBridge - bridged_balance_to_this_balance") + } +} + +/// Message verifier for BridgeHubWococo messages sent from BridgeHubRococo +pub type ToBridgeHubWococoMessageVerifier = + messages::source::FromThisChainMessageVerifier; + +/// Maximal outbound payload size of BridgeHubRococo -> BridgeHubWococo messages. +pub type ToBridgeHubWococoMaximalOutboundPayloadSize = + messages::source::FromThisChainMaximalOutboundPayloadSize; + +/// BridgeHubWococo chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct BridgeHubWococo; + +impl ChainWithMessages for BridgeHubWococo { + type Hash = bp_bridge_hub_wococo::Hash; + type AccountId = bp_bridge_hub_wococo::AccountId; + type Signer = bp_bridge_hub_wococo::AccountSigner; + type Signature = bp_bridge_hub_wococo::Signature; + type Weight = Weight; + type Balance = bp_bridge_hub_wococo::Balance; +} + +impl SourceHeaderChain for BridgeHubWococo { + type Error = &'static str; + type MessagesProof = FromBridgedChainMessagesProof; + + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result>, Self::Error> { + bridge_runtime_common::messages::target::verify_messages_proof_from_parachain::< + WithBridgeHubWococoMessageBridge, + bp_bridge_hub_wococo::Header, + crate::Runtime, + crate::BridgeParachainWococoInstance, + >(ParaId(bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID), proof, messages_count) + } +} + +impl TargetHeaderChain for BridgeHubWococo { + type Error = &'static str; + type MessagesDeliveryProof = + messages::source::FromBridgedChainMessagesDeliveryProof; + + fn verify_message(payload: &XcmAsPlainPayload) -> Result<(), Self::Error> { + messages::source::verify_chain_message::(payload) + } + + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData), Self::Error> { + messages::source::verify_messages_delivery_proof_from_parachain::< + WithBridgeHubWococoMessageBridge, + bp_bridge_hub_wococo::Header, + crate::Runtime, + crate::BridgeParachainWococoInstance, + >(ParaId(bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID), proof) + } +} + +impl messages::BridgedChainWithMessages for BridgeHubWococo { + fn maximal_extrinsic_size() -> u32 { + bp_bridge_hub_wococo::BridgeHubWococo::max_extrinsic_size() + } + + fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { + true + } + + fn estimate_delivery_transaction( + message_payload: &[u8], + include_pay_dispatch_fee_cost: bool, + message_dispatch_weight: WeightOf, + ) -> MessageTransaction> { + let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX); + let extra_bytes_in_payload = Weight::from(message_payload_len) + .saturating_sub(pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH.into()); + + MessageTransaction { + dispatch_weight: extra_bytes_in_payload + .saturating_mul(bp_bridge_hub_wococo::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT) + .saturating_add(bp_bridge_hub_wococo::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT) + .saturating_sub(if include_pay_dispatch_fee_cost { + 0 + } else { + bp_bridge_hub_wococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT + }) + .saturating_add(message_dispatch_weight), + size: message_payload_len + .saturating_add(bp_bridge_hub_rococo::EXTRA_STORAGE_PROOF_SIZE) + .saturating_add(bp_bridge_hub_wococo::TX_EXTRA_BYTES), + } + } + + fn transaction_payment( + transaction: MessageTransaction>, + ) -> messages::BalanceOf { + log::info!( + "[BridgeHubWococo::BridgedChainWithMessages] transaction_payment - transaction: {:?}", + transaction + ); + // TODO:check-parameter - any payment? from sovereign account? + unimplemented!( + "[BridgeHubWococo/BridgedChainWithMessages] transaction_payment - transaction: {:?}", + transaction + ) + } +} + +/// BridgeHubRococo chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct BridgeHubRococo; + +impl ChainWithMessages for BridgeHubRococo { + type Hash = bp_bridge_hub_rococo::Hash; + type AccountId = bp_bridge_hub_rococo::AccountId; + type Signer = bp_bridge_hub_rococo::AccountSigner; + type Signature = bp_bridge_hub_rococo::Signature; + type Weight = Weight; + type Balance = bp_bridge_hub_rococo::Balance; +} + +impl ThisChainWithMessages for BridgeHubRococo { + type Origin = crate::Origin; + type Call = crate::Call; + type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< + Self::AccountId, + { bp_bridge_hub_rococo::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT }, + { bp_bridge_hub_wococo::EXTRA_STORAGE_PROOF_SIZE }, + { bp_bridge_hub_rococo::TX_EXTRA_BYTES }, + >; + + fn is_message_accepted(origin: &Self::Origin, lane: &LaneId) -> bool { + log::info!("[BridgeHubRococo::ThisChainWithMessages] is_message_accepted - origin: {:?}, lane: {:?}", origin, lane); + lane == &DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO + } + + fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { + log::info!( + "[BridgeHubRococo::ThisChainWithMessages] maximal_pending_messages_at_outbound_lane" + ); + MessageNonce::MAX / 2 + } + + fn transaction_payment( + transaction: MessageTransaction>, + ) -> messages::BalanceOf { + log::info!( + "[BridgeHubRococo::ThisChainWithMessages] transaction_payment - transaction: {:?}", + transaction + ); + // TODO:check-parameter - any payment? from sovereign account? + unimplemented!( + "[BridgeHubRococo/ThisChainWithMessages] transaction_payment - transaction: {:?}", + transaction + ) + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 2a521405db8..d8c874b0721 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -14,17 +14,32 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use bp_messages::{source_chain::{LaneMessageVerifier, TargetHeaderChain}, target_chain::{ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, OutboundLaneData, MessageKey, MessageData}; -use bp_runtime::{BalanceOf, Chain}; -use frame_support::{parameter_types, RuntimeDebug}; -use xcm::prelude::{InteriorMultiLocation, NetworkId}; -use bp_messages::target_chain::ProvedLaneMessages; -use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; -use crate::universal_exports::{BridgeBlobDispatcher, HaulBlobExporter}; -use crate::{WithBridgeHubRococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, XcmRouter}; -use crate::Runtime; -use crate::ParachainInfo; -use xcm::latest::prelude::*; +use crate::{ + universal_exports::{BridgeBlobDispatcher, HaulBlobExporter}, + ParachainInfo, Runtime, WithBridgeHubRococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, + XcmRouter, +}; +use bp_messages::{ + source_chain::TargetHeaderChain, + target_chain::{ProvedMessages, SourceHeaderChain}, + InboundLaneData, LaneId, Message, MessageNonce, +}; +use bp_polkadot_core::parachains::ParaId; +use bp_runtime::{Chain, ChainId}; +use bridge_runtime_common::{ + messages, + messages::{ + target::FromBridgedChainMessagesProof, BasicConfirmationTransactionEstimation, + BridgedChain, ChainWithMessages, MessageBridge, MessageTransaction, ThisChain, + ThisChainWithMessages, WeightOf, + }, +}; +use frame_support::{dispatch::Weight, parameter_types, RuntimeDebug}; +use sp_runtime::FixedU128; +use xcm::{ + latest::prelude::*, + prelude::{InteriorMultiLocation, NetworkId}, +}; // TODO:check-parameter parameter_types! { @@ -38,15 +53,18 @@ parameter_types! { } /// Dispatches received XCM messages from other bridge -pub type OnBridgeHubWococoBlobDispatcher = BridgeBlobDispatcher; +pub type OnBridgeHubWococoBlobDispatcher = + BridgeBlobDispatcher; /// Export XCM messages to be relayed to the otherside -pub type ToBridgeHubRococoHaulBlobExporter = HaulBlobExporter; +pub type ToBridgeHubRococoHaulBlobExporter = + HaulBlobExporter; pub struct ToBridgeHubRococoXcmBlobHauler; pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO: LaneId = [0, 0, 0, 1]; impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { type SenderChain = bp_bridge_hub_wococo::BridgeHubWococo; - type MessageSender = pallet_bridge_messages::Pallet; + type MessageSender = + pallet_bridge_messages::Pallet; fn message_sender_origin() -> InteriorMultiLocation { crate::xcm_config::UniversalLocation::get() @@ -57,3 +75,181 @@ impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { } } +/// Messaging Bridge configuration for BridgeHubWococo -> BridgeHubRococo +pub struct WithBridgeHubRococoMessageBridge; +impl MessageBridge for WithBridgeHubRococoMessageBridge { + // TODO:check-parameter - relayers rewards + const RELAYER_FEE_PERCENT: u32 = 0; + const THIS_CHAIN_ID: ChainId = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; + const BRIDGED_CHAIN_ID: ChainId = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = + bp_bridge_hub_wococo::WITH_BRIDGE_HUB_WOCOCO_MESSAGES_PALLET_NAME; + type ThisChain = BridgeHubWococo; + type BridgedChain = BridgeHubRococo; + + fn bridged_balance_to_this_balance( + bridged_balance: bridge_runtime_common::messages::BalanceOf>, + bridged_to_this_conversion_rate_override: Option, + ) -> bridge_runtime_common::messages::BalanceOf> { + log::info!("[WithBridgeHubRococoMessageBridge] bridged_balance_to_this_balance - bridged_balance: {:?}, bridged_to_this_conversion_rate_override: {:?}", bridged_balance, bridged_to_this_conversion_rate_override); + unimplemented!("TODO: WithBridgeHubRococoMessageBridge - bridged_balance_to_this_balance") + } +} + +/// Message verifier for BridgeHubRococo messages sent from BridgeHubWococo +pub type ToBridgeHubRococoMessageVerifier = + messages::source::FromThisChainMessageVerifier; + +/// Maximal outbound payload size of BridgeHubWococo -> BridgeHubRococo messages. +pub type ToBridgeHubRococoMaximalOutboundPayloadSize = + messages::source::FromThisChainMaximalOutboundPayloadSize; + +/// BridgeHubRococo chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct BridgeHubRococo; + +impl ChainWithMessages for BridgeHubRococo { + type Hash = bp_bridge_hub_rococo::Hash; + type AccountId = bp_bridge_hub_rococo::AccountId; + type Signer = bp_bridge_hub_rococo::AccountSigner; + type Signature = bp_bridge_hub_rococo::Signature; + type Weight = Weight; + type Balance = bp_bridge_hub_rococo::Balance; +} + +impl SourceHeaderChain for BridgeHubRococo { + type Error = &'static str; + type MessagesProof = FromBridgedChainMessagesProof; + + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result>, Self::Error> { + bridge_runtime_common::messages::target::verify_messages_proof_from_parachain::< + WithBridgeHubRococoMessageBridge, + bp_bridge_hub_rococo::Header, + crate::Runtime, + crate::BridgeParachainRococoInstance, + >(ParaId(bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID), proof, messages_count) + } +} + +impl TargetHeaderChain for BridgeHubRococo { + type Error = &'static str; + type MessagesDeliveryProof = + messages::source::FromBridgedChainMessagesDeliveryProof; + + fn verify_message(payload: &XcmAsPlainPayload) -> Result<(), Self::Error> { + messages::source::verify_chain_message::(payload) + } + + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData), Self::Error> { + messages::source::verify_messages_delivery_proof_from_parachain::< + WithBridgeHubRococoMessageBridge, + bp_bridge_hub_rococo::Header, + crate::Runtime, + crate::BridgeParachainRococoInstance, + >(ParaId(bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID), proof) + } +} + +impl messages::BridgedChainWithMessages for BridgeHubRococo { + fn maximal_extrinsic_size() -> u32 { + bp_bridge_hub_rococo::BridgeHubRococo::max_extrinsic_size() + } + + fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { + true + } + + fn estimate_delivery_transaction( + message_payload: &[u8], + include_pay_dispatch_fee_cost: bool, + message_dispatch_weight: WeightOf, + ) -> MessageTransaction> { + let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX); + let extra_bytes_in_payload = Weight::from(message_payload_len) + .saturating_sub(pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH.into()); + + MessageTransaction { + dispatch_weight: extra_bytes_in_payload + .saturating_mul(bp_bridge_hub_rococo::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT) + .saturating_add(bp_bridge_hub_rococo::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT) + .saturating_sub(if include_pay_dispatch_fee_cost { + 0 + } else { + bp_bridge_hub_rococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT + }) + .saturating_add(message_dispatch_weight), + size: message_payload_len + .saturating_add(bp_bridge_hub_wococo::EXTRA_STORAGE_PROOF_SIZE) + .saturating_add(bp_bridge_hub_rococo::TX_EXTRA_BYTES), + } + } + + fn transaction_payment( + transaction: MessageTransaction>, + ) -> messages::BalanceOf { + log::info!( + "[BridgeHubRococo::BridgedChainWithMessages] transaction_payment - transaction: {:?}", + transaction + ); + // TODO:check-parameter - any payment? from sovereign account? + unimplemented!( + "[BridgeHubRococo/BridgedChainWithMessages] transaction_payment - transaction: {:?}", + transaction + ) + } +} + +/// BridgeHubWococo chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct BridgeHubWococo; + +impl ChainWithMessages for BridgeHubWococo { + type Hash = bp_bridge_hub_wococo::Hash; + type AccountId = bp_bridge_hub_wococo::AccountId; + type Signer = bp_bridge_hub_wococo::AccountSigner; + type Signature = bp_bridge_hub_wococo::Signature; + type Weight = Weight; + type Balance = bp_bridge_hub_wococo::Balance; +} + +impl ThisChainWithMessages for BridgeHubWococo { + type Origin = crate::Origin; + type Call = crate::Call; + type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< + Self::AccountId, + { bp_bridge_hub_wococo::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT }, + { bp_bridge_hub_rococo::EXTRA_STORAGE_PROOF_SIZE }, + { bp_bridge_hub_wococo::TX_EXTRA_BYTES }, + >; + + fn is_message_accepted(origin: &Self::Origin, lane: &LaneId) -> bool { + log::info!("[BridgeHubWococo::ThisChainWithMessages] is_message_accepted - origin: {:?}, lane: {:?}", origin, lane); + lane == &DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO + } + + fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { + log::info!( + "[BridgeHubWococo::ThisChainWithMessages] maximal_pending_messages_at_outbound_lane" + ); + MessageNonce::MAX / 2 + } + + fn transaction_payment( + transaction: MessageTransaction>, + ) -> messages::BalanceOf { + log::info!( + "[BridgeHubWococo::ThisChainWithMessages] transaction_payment - transaction: {:?}", + transaction + ); + // TODO:check-parameter - any payment? from sovereign account? + unimplemented!( + "[BridgeHubWococo/ThisChainWithMessages] transaction_payment - transaction: {:?}", + transaction + ) + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 97a5d88ec2c..66e3686cf6e 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -68,13 +68,13 @@ use bp_runtime::{HeaderId, HeaderIdProvider}; pub use sp_runtime::BuildStorage; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; -use sp_runtime::traits::ConstU32; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use crate::{ bridge_hub_rococo_config::OnBridgeHubRococoBlobDispatcher, - bridge_hub_wococo_config::OnBridgeHubWococoBlobDispatcher, xcm_config::XcmRouter, + bridge_hub_wococo_config::OnBridgeHubWococoBlobDispatcher, + xcm_config::XcmRouter, }; use parachains_common::{AccountId, Signature}; use xcm::latest::prelude::BodyId; @@ -565,22 +565,21 @@ impl pallet_bridge_messages::Config for Run type MaxUnconfirmedMessagesAtInboundLane = bridge_hub_rococo_config::MaxUnconfirmedMessagesAtInboundLane; - type MaximalOutboundPayloadSize = (); + type MaximalOutboundPayloadSize = bridge_hub_rococo_config::ToBridgeHubWococoMaximalOutboundPayloadSize; type OutboundPayload = XcmAsPlainPayload; - type OutboundMessageFee = crate::Balance; /* bp_bridge_hub_rococo::Balance */ + type OutboundMessageFee = Balance; type InboundPayload = XcmAsPlainPayload; - type InboundMessageFee = crate::Balance; /* bp_bridge_hub_wococo::Balance */ - type InboundRelayer = crate::AccountId; /* bp_bridge_hub_wococo::AccountId */ + type InboundMessageFee = Balance; + type InboundRelayer = AccountId; - type TargetHeaderChain = bridge_config::BridgeHubWococoMessagingSupport; - type LaneMessageVerifier = bridge_config::ToBridgeHubWococoMessageVerifier; - // TODO:check-parameter - add because of rewards? + type TargetHeaderChain = bridge_hub_rococo_config::BridgeHubWococo; + type LaneMessageVerifier = bridge_hub_rococo_config::ToBridgeHubWococoMessageVerifier; type MessageDeliveryAndDispatchPayment = (); type OnMessageAccepted = (); type OnDeliveryConfirmed = (); - type SourceHeaderChain = bridge_config::BridgeHubWococoMessagingSupport; + type SourceHeaderChain = bridge_hub_rococo_config::BridgeHubWococo; type MessageDispatch = XcmBlobMessageDispatch< bp_bridge_hub_wococo::BridgeHubWococo, bp_bridge_hub_rococo::BridgeHubRococo, @@ -602,22 +601,21 @@ impl pallet_bridge_messages::Config for Run type MaxUnconfirmedMessagesAtInboundLane = bridge_hub_wococo_config::MaxUnconfirmedMessagesAtInboundLane; - type MaximalOutboundPayloadSize = (); + type MaximalOutboundPayloadSize = bridge_hub_wococo_config::ToBridgeHubRococoMaximalOutboundPayloadSize; type OutboundPayload = XcmAsPlainPayload; - type OutboundMessageFee = crate::Balance; /* bp_bridge_hub_wococo::Balance */ + type OutboundMessageFee = Balance; type InboundPayload = XcmAsPlainPayload; - type InboundMessageFee = crate::Balance; /* bp_bridge_hub_rococo::Balance */ - type InboundRelayer = crate::AccountId; /* bp_bridge_hub_rococo::AccountId */ + type InboundMessageFee = Balance; + type InboundRelayer = AccountId; - type TargetHeaderChain = bridge_config::BridgeHubRococoMessagingSupport; - type LaneMessageVerifier = bridge_config::ToBridgeHubRococoMessageVerifier; - // TODO:check-parameter - add because of rewards? + type TargetHeaderChain = bridge_hub_wococo_config::BridgeHubRococo; + type LaneMessageVerifier = bridge_hub_wococo_config::ToBridgeHubRococoMessageVerifier; type MessageDeliveryAndDispatchPayment = (); type OnMessageAccepted = (); type OnDeliveryConfirmed = (); - type SourceHeaderChain = bridge_config::BridgeHubRococoMessagingSupport; + type SourceHeaderChain = bridge_hub_wococo_config::BridgeHubRococo; type MessageDispatch = XcmBlobMessageDispatch< bp_bridge_hub_rococo::BridgeHubRococo, bp_bridge_hub_wococo::BridgeHubWococo, From c886877a80e25c225c43a85887a9637c90bd19f5 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 7 Nov 2022 10:54:26 +0100 Subject: [PATCH 082/263] Updated scritp and readme --- parachains/runtimes/bridge-hubs/README.md | 14 +++++++------- scripts/bridges_rococo_wococo.sh | 21 +++++++++++++++++++-- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 8aa4bbde04f..ea552d60a95 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -1,4 +1,4 @@ -# Bride-hubs Parachain +# Bridge-hubs Parachain Implementation of _BridgeHub_, a blockchain to support message passing between Substrate based chains like Polkadot and Kusama networks. @@ -72,7 +72,7 @@ or ``` # Rococo -> Wococo -RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ +RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay init-bridge rococo-to-bridge-hub-wococo \ --source-host localhost \ --source-port 48943 \ @@ -81,7 +81,7 @@ RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ --target-signer //Bob # Wococo -> Rococo -RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ +RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay init-bridge wococo-to-bridge-hub-rococo \ --source-host localhost \ --source-port 48945 \ @@ -97,7 +97,7 @@ RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ ``` # Rococo -> Wococo -RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ +RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay relay-headers rococo-to-bridge-hub-wococo \ --source-host localhost \ --source-port 48943 \ @@ -107,7 +107,7 @@ RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ --target-transactions-mortality=4 # Wococo -> Rococo -RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ +RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay relay-headers wococo-to-bridge-hub-rococo \ --source-host localhost \ --source-port 48945 \ @@ -131,7 +131,7 @@ RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ ``` # Rococo -> Wococo -RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ +RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay relay-parachains bridge-hub-rococo-to-bridge-hub-wococo \ --source-host localhost \ --source-port 48943 \ @@ -141,7 +141,7 @@ RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ --target-transactions-mortality=4 # Wococo -> Rococo -RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ +RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay relay-parachains bridge-hub-wococo-to-bridge-hub-rococo \ --source-host localhost \ --source-port 48945 \ diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh index 8087769b57f..53d7dc974aa 100755 --- a/scripts/bridges_rococo_wococo.sh +++ b/scripts/bridges_rococo_wococo.sh @@ -41,6 +41,19 @@ function register_parachain() { # TODO: find the way to do it automatically } +function show_node_log_file() { + local NAME=$1 + local WS_PORT=$2 + local FILE_PATH=$3 + + echo "" + echo "" + echo " Node(${NAME}): ws-port: ${WS_PORT}" + echo " Logs: ${FILE_PATH}" + echo "" + echo "" +} + function check_parachain_collator() { local PORT=$1 local PALLET=$2 @@ -81,7 +94,9 @@ case "$1" in # Rococo ~/local_bridge_testing/bin/polkadot-parachain --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json --collator --alice --force-authoring --tmp --port 40333 --rpc-port 8933 --ws-port 8943 --no-mdns --node-key ${COLLATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/40334/p2p/${COLLATOR_KEY_BOB[0]} -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 41333 --rpc-port 48933 --ws-port 48943 --no-mdns --node-key ${COLLATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30332/p2p/${VALIDATOR_KEY_ALICE[0]} &> ~/local_bridge_testing/logs/rococo_para_alice.log & + show_node_log_file alice 8943 ~/local_bridge_testing/logs/rococo_para_alice.log ~/local_bridge_testing/bin/polkadot-parachain --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json --collator --bob --force-authoring --tmp --port 40334 --rpc-port 8934 --ws-port 8944 --no-mdns --node-key ${COLLATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/40333/p2p/${COLLATOR_KEY_ALICE[0]} -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 41334 --rpc-port 48934 --ws-port 48944 --no-mdns --node-key ${COLLATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/30333/p2p/${VALIDATOR_KEY_BOB[0]} &> ~/local_bridge_testing/logs/rococo_para_bob.log & + show_node_log_file bob 8944 ~/local_bridge_testing/logs/rococo_para_bob.log register_parachain 9942 1013 ~/local_bridge_testing/bridge-hub-rococo-local-genesis ~/local_bridge_testing/bridge-hub-rococo-local-genesis-wasm check_parachain_collator 8943 bridgeWococoGrandpa @@ -111,14 +126,16 @@ case "$1" in # Wococo ~/local_bridge_testing/bin/polkadot-parachain --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json --collator --alice --force-authoring --tmp --port 40335 --rpc-port 8935 --ws-port 8945 --no-mdns --node-key ${COLLATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/40336/p2p/${COLLATOR_KEY_BOB[0]} -- --execution wasm --chain ~/local_bridge_testing/wococo-local-cfde.json --port 41335 --rpc-port 48935 --ws-port 48945 --no-mdns --node-key ${COLLATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30335/p2p/${VALIDATOR_KEY_ALICE[0]} &> ~/local_bridge_testing/logs/wococo_para_alice.log & + show_node_log_file alice 8945 ~/local_bridge_testing/logs/wococo_para_alice.log ~/local_bridge_testing/bin/polkadot-parachain --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json --collator --bob --force-authoring --tmp --port 40336 --rpc-port 8936 --ws-port 8946 --no-mdns --node-key ${COLLATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/40335/p2p/${COLLATOR_KEY_ALICE[0]} -- --execution wasm --chain ~/local_bridge_testing/wococo-local-cfde.json --port 41336 --rpc-port 48936 --ws-port 48946 --no-mdns --node-key ${COLLATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/30336/p2p/${VALIDATOR_KEY_BOB[0]} &> ~/local_bridge_testing/logs/wococo_para_bob.log & + show_node_log_file bob 8946 ~/local_bridge_testing/logs/wococo_para_bob.log register_parachain 9945 1013 ~/local_bridge_testing/bridge-hub-wococo-local-genesis ~/local_bridge_testing/bridge-hub-wococo-local-genesis-wasm check_parachain_collator 8945 bridgeRococoGrandpa ;; init-ro-wo) # Init bridge Rococo->Wococo - RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ + RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay init-bridge rococo-to-bridge-hub-wococo \ --source-host localhost \ --source-port 48943 \ @@ -128,7 +145,7 @@ case "$1" in ;; init-wo-ro) # Init bridge Wococo->Rococo - RUST_LOG=runtime=trace,rpc=trace,runtime::bridge=trace \ + RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay init-bridge wococo-to-bridge-hub-rococo \ --source-host localhost \ --source-port 48945 \ From 805615009be09f008504cc540d9360eb64b357aa Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 7 Nov 2022 10:57:35 +0100 Subject: [PATCH 083/263] XCM messaging fixes --- .github/workflows/release-30_create-draft.yml | 4 ++-- parachains/runtimes/bridge-hubs/README.md | 13 ++++++++++++ .../src/bridge_hub_rococo_config.rs | 19 +++++++----------- .../src/bridge_hub_wococo_config.rs | 19 +++++++----------- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 20 +++++++++++++++++++ 5 files changed, 49 insertions(+), 26 deletions(-) diff --git a/.github/workflows/release-30_create-draft.yml b/.github/workflows/release-30_create-draft.yml index 38c105d8b8e..6453dc1ac45 100644 --- a/.github/workflows/release-30_create-draft.yml +++ b/.github/workflows/release-30_create-draft.yml @@ -152,7 +152,7 @@ jobs: WESTMINT_DIGEST: ${{ github.workspace}}/westmint-srtool-json/westmint-srtool-digest.json STATEMINE_DIGEST: ${{ github.workspace}}/statemine-srtool-json/statemine-srtool-digest.json STATEMINT_DIGEST: ${{ github.workspace}}/statemint-srtool-json/statemint-srtool-digest.json - BRIDE_HUB_ROCOCO_DIGEST: ${{ github.workspace}}/bridge-hub-rococo-srtool-json/bridge-hub-rococo-srtool-digest.json + BRIDGE_HUB_ROCOCO_DIGEST: ${{ github.workspace}}/bridge-hub-rococo-srtool-json/bridge-hub-rococo-srtool-digest.json COLLECTIVES_POLKADOT_DIGEST: ${{ github.workspace}}/collectives-polkadot-srtool-json/collectives-polkadot-srtool-digest.json ROCOCO_PARA_DIGEST: ${{ github.workspace}}/rococo-parachain-srtool-json/rococo-parachain-srtool-digest.json CANVAS_KUSAMA_DIGEST: ${{ github.workspace}}/contracts-rococo-srtool-json/contracts-rococo-srtool-digest.json @@ -168,7 +168,7 @@ jobs: ls -al $WESTMINT_DIGEST || true ls -al $STATEMINE_DIGEST || true ls -al $STATEMINT_DIGEST || true - ls -al $BRIDE_HUB_ROCOCO_DIGEST || true + ls -al $BRIDGE_HUB_ROCOCO_DIGEST || true ls -al $COLLECTIVES_POLKADOT_DIGEST || true ls -al $ROCOCO_PARA_DIGEST || true ls -al $CANVAS_KUSAMA_DIGEST || true diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index ea552d60a95..b446c74b084 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -163,6 +163,19 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ **4. Relay (XCM) messages** +``` +# Rococo -> Wococo +RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay relay-messages bridge-hub-rococo-to-bridge-hub-wococo \ + --source-host localhost \ + --source-port 8943 \ + --source-signer //Charlie \ + --target-host localhost \ + --target-port 8945 \ + --target-signer //Charlie \ + --target-transactions-mortality=4 \ + --lane 00000002 +``` TODO: --- diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index 8cb45f5cdf2..ff382c7f5e4 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -91,8 +91,9 @@ impl MessageBridge for WithBridgeHubWococoMessageBridge { bridged_balance: bridge_runtime_common::messages::BalanceOf>, bridged_to_this_conversion_rate_override: Option, ) -> bridge_runtime_common::messages::BalanceOf> { - log::info!("[WithBridgeHubWococoMessageBridge] bridged_balance_to_this_balance - bridged_balance: {:?}, bridged_to_this_conversion_rate_override: {:?}", bridged_balance, bridged_to_this_conversion_rate_override); - unimplemented!("TODO: WithBridgeHubWococoMessageBridge - bridged_balance_to_this_balance") + log::info!("[WithBridgeHubWococoMessageBridge] bridged_balance_to_this_balance (returns 0 balance, TODO: fix) - bridged_balance: {:?}, bridged_to_this_conversion_rate_override: {:?}", bridged_balance, bridged_to_this_conversion_rate_override); + // TODO:check-parameter - any payment? from sovereign account? + 0 } } @@ -193,14 +194,11 @@ impl messages::BridgedChainWithMessages for BridgeHubWococo { transaction: MessageTransaction>, ) -> messages::BalanceOf { log::info!( - "[BridgeHubWococo::BridgedChainWithMessages] transaction_payment - transaction: {:?}", + "[BridgeHubWococo::BridgedChainWithMessages] transaction_payment (returns 0 balance, TODO: fix) - transaction: {:?}", transaction ); // TODO:check-parameter - any payment? from sovereign account? - unimplemented!( - "[BridgeHubWococo/BridgedChainWithMessages] transaction_payment - transaction: {:?}", - transaction - ) + 0 } } @@ -243,13 +241,10 @@ impl ThisChainWithMessages for BridgeHubRococo { transaction: MessageTransaction>, ) -> messages::BalanceOf { log::info!( - "[BridgeHubRococo::ThisChainWithMessages] transaction_payment - transaction: {:?}", + "[BridgeHubRococo::ThisChainWithMessages] transaction_payment (returns 0 balance, TODO: fix) - transaction: {:?}", transaction ); // TODO:check-parameter - any payment? from sovereign account? - unimplemented!( - "[BridgeHubRococo/ThisChainWithMessages] transaction_payment - transaction: {:?}", - transaction - ) + 0 } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index d8c874b0721..02df9574583 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -91,8 +91,9 @@ impl MessageBridge for WithBridgeHubRococoMessageBridge { bridged_balance: bridge_runtime_common::messages::BalanceOf>, bridged_to_this_conversion_rate_override: Option, ) -> bridge_runtime_common::messages::BalanceOf> { - log::info!("[WithBridgeHubRococoMessageBridge] bridged_balance_to_this_balance - bridged_balance: {:?}, bridged_to_this_conversion_rate_override: {:?}", bridged_balance, bridged_to_this_conversion_rate_override); - unimplemented!("TODO: WithBridgeHubRococoMessageBridge - bridged_balance_to_this_balance") + log::info!("[WithBridgeHubRococoMessageBridge] bridged_balance_to_this_balance (returns 0 balance, TODO: fix) - bridged_balance: {:?}, bridged_to_this_conversion_rate_override: {:?}", bridged_balance, bridged_to_this_conversion_rate_override); + // TODO:check-parameter - any payment? from sovereign account? + 0 } } @@ -193,14 +194,11 @@ impl messages::BridgedChainWithMessages for BridgeHubRococo { transaction: MessageTransaction>, ) -> messages::BalanceOf { log::info!( - "[BridgeHubRococo::BridgedChainWithMessages] transaction_payment - transaction: {:?}", + "[BridgeHubRococo::BridgedChainWithMessages] transaction_payment (returns 0 balance, TODO: fix) - transaction: {:?}", transaction ); // TODO:check-parameter - any payment? from sovereign account? - unimplemented!( - "[BridgeHubRococo/BridgedChainWithMessages] transaction_payment - transaction: {:?}", - transaction - ) + 0 } } @@ -243,13 +241,10 @@ impl ThisChainWithMessages for BridgeHubWococo { transaction: MessageTransaction>, ) -> messages::BalanceOf { log::info!( - "[BridgeHubWococo::ThisChainWithMessages] transaction_payment - transaction: {:?}", + "[BridgeHubWococo::ThisChainWithMessages] transaction_payment (returns 0 balance, TODO: fix) - transaction: {:?}", transaction ); // TODO:check-parameter - any payment? from sovereign account? - unimplemented!( - "[BridgeHubWococo/ThisChainWithMessages] transaction_payment - transaction: {:?}", - transaction - ) + 0 } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 66e3686cf6e..9e7f48b5653 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -28,6 +28,7 @@ pub mod bridge_hub_wococo_config; mod weights; pub mod xcm_config; +use codec::Decode; use bridge_common_config::*; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use smallvec::smallvec; @@ -247,6 +248,8 @@ pub mod runtime_api { use super::{BlockNumber, Hash}; bp_runtime::decl_bridge_finality_runtime_apis!(rococo); bp_runtime::decl_bridge_finality_runtime_apis!(wococo); + bp_runtime::decl_bridge_finality_runtime_apis!(bridge_hub_rococo); + bp_runtime::decl_bridge_finality_runtime_apis!(bridge_hub_wococo); use bp_messages::{ InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, @@ -811,6 +814,23 @@ impl_runtime_apis! { } } + + impl runtime_api::BridgeHubRococoFinalityApi for Runtime { + fn best_finalized() -> Option> { + let encoded_head = BridgeRococoParachain::best_parachain_head(bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID.into())?; + let head = bp_bridge_hub_rococo::Header::decode(&mut &encoded_head.0[..]).ok()?; + Some(head.id()) + } + } + + impl runtime_api::BridgeHubWococoFinalityApi for Runtime { + fn best_finalized() -> Option> { + let encoded_head = BridgeWococoParachain::best_parachain_head(bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID.into())?; + let head = bp_bridge_hub_wococo::Header::decode(&mut &encoded_head.0[..]).ok()?; + Some(head.id()) + } + } + // This exposed by BridgeHubRococo impl runtime_api::ToBridgeHubWococoOutboundLaneApi for Runtime { fn estimate_message_delivery_and_dispatch_fee( From 234d048c8d5a10597a2b67b38554c4814621d027 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 10 Nov 2022 13:04:12 +0100 Subject: [PATCH 084/263] BridgeHub fixes and cleaning --- Cargo.lock | 491 +++++++++++++----- Cargo.toml | 10 +- parachains/runtimes/bridge-hubs/README.md | 2 +- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 4 + .../src/bridge_common_config.rs | 37 +- .../src/bridge_hub_rococo_config.rs | 39 +- .../src/bridge_hub_wococo_config.rs | 43 +- .../bridge-hub-rococo/src/constants.rs | 64 +++ .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 100 ++-- .../src/weights/block_weights.rs | 2 +- .../src/weights/cumulus_pallet_xcmp_queue.rs | 61 +++ .../src/weights/extrinsic_weights.rs | 2 +- .../bridge-hub-rococo/src/weights/mod.rs | 3 + .../src/weights/pallet_balances.rs | 91 ++++ .../src/weights/paritydb_weights.rs | 4 +- .../src/weights/rocksdb_weights.rs | 4 +- .../bridge-hub-rococo/src/weights/xcm/mod.rs | 247 +++++++++ .../xcm/pallet_xcm_benchmarks_fungible.rs | 107 ++++ .../xcm/pallet_xcm_benchmarks_generic.rs | 189 +++++++ .../bridge-hub-rococo/src/xcm_config.rs | 58 +-- polkadot-parachain/src/command.rs | 2 - 21 files changed, 1272 insertions(+), 288 deletions(-) create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/constants.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/cumulus_pallet_xcmp_queue.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_balances.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs diff --git a/Cargo.lock b/Cargo.lock index e1cb2bffe34..127f1a1c1cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -717,10 +717,12 @@ dependencies = [ name = "bp-bridge-hub-rococo" version = "0.1.0" dependencies = [ + "bp-messages", "bp-polkadot-core", "bp-runtime", "frame-support", "sp-api", + "sp-std", ] [[package]] @@ -728,17 +730,19 @@ name = "bp-bridge-hub-wococo" version = "0.1.0" dependencies = [ "bp-bridge-hub-rococo", + "bp-messages", "bp-runtime", + "frame-support", "sp-api", + "sp-std", ] [[package]] name = "bp-header-chain" version = "0.1.0" dependencies = [ - "assert_matches", - "bp-runtime 0.1.0", - "bp-test-utils 0.1.0", + "bp-runtime", + "bp-test-utils", "finality-grandpa", "frame-support", "hex", @@ -752,41 +756,12 @@ dependencies = [ "sp-std", ] -[[package]] -name = "bp-header-chain" -version = "0.1.0" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" -dependencies = [ - "bp-runtime 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "finality-grandpa", - "frame-support", - "parity-scale-codec", - "scale-info", - "serde", - "sp-core", - "sp-finality-grandpa", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "bp-message-dispatch" -version = "0.1.0" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" -dependencies = [ - "bp-runtime 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "frame-support", - "parity-scale-codec", - "scale-info", - "sp-std", -] - [[package]] name = "bp-messages" version = "0.1.0" dependencies = [ "bitvec", - "bp-runtime 0.1.0", + "bp-runtime", "frame-support", "hex", "hex-literal", @@ -799,28 +774,34 @@ dependencies = [ ] [[package]] -name = "bp-messages" +name = "bp-millau" version = "0.1.0" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" dependencies = [ - "bitvec", - "bp-runtime 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "bp-messages", + "bp-runtime", + "fixed-hash 0.7.0", "frame-support", "frame-system", - "impl-trait-for-tuples", - "parity-scale-codec", + "hash256-std-hasher", + "impl-codec", + "impl-serde 0.3.2", + "parity-util-mem", "scale-info", "serde", + "sp-api", "sp-core", + "sp-io", + "sp-runtime", "sp-std", + "sp-trie", ] [[package]] name = "bp-parachains" version = "0.1.0" dependencies = [ - "bp-polkadot-core 0.1.0", - "bp-runtime 0.1.0", + "bp-polkadot-core", + "bp-runtime", "frame-support", "parity-scale-codec", "scale-info", @@ -831,8 +812,8 @@ dependencies = [ name = "bp-polkadot-core" version = "0.1.0" dependencies = [ - "bp-messages 0.1.0", - "bp-runtime 0.1.0", + "bp-messages", + "bp-runtime", "frame-support", "frame-system", "hex", @@ -846,30 +827,40 @@ dependencies = [ ] [[package]] -name = "bp-polkadot-core" +name = "bp-relayers" version = "0.1.0" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" dependencies = [ - "bp-messages 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "bp-runtime 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "frame-support", + "hex", + "hex-literal", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-rialto" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-runtime", "frame-support", "frame-system", - "parity-scale-codec", - "scale-info", "sp-api", "sp-core", "sp-runtime", "sp-std", - "sp-version", ] [[package]] -name = "bp-relayers" +name = "bp-rialto-parachain" version = "0.1.0" dependencies = [ + "bp-messages", + "bp-runtime", "frame-support", - "hex", - "hex-literal", + "frame-system", + "sp-api", + "sp-core", "sp-runtime", "sp-std", ] @@ -902,31 +893,14 @@ dependencies = [ "sp-state-machine", "sp-std", "sp-trie", -] - -[[package]] -name = "bp-runtime" -version = "0.1.0" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" -dependencies = [ - "frame-support", - "hash-db", - "num-traits", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-state-machine", - "sp-std", - "sp-trie", + "trie-db", ] [[package]] name = "bp-test-utils" version = "0.1.0" dependencies = [ - "bp-header-chain 0.1.0", + "bp-header-chain", "ed25519-dalek", "finality-grandpa", "parity-scale-codec", @@ -937,18 +911,12 @@ dependencies = [ ] [[package]] -name = "bp-test-utils" +name = "bp-westend" version = "0.1.0" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#81128ab75395e256ae8ef50994d46101d0e67cea" dependencies = [ - "bp-header-chain 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "ed25519-dalek", - "finality-grandpa", - "parity-scale-codec", - "sp-application-crypto", - "sp-finality-grandpa", - "sp-runtime", - "sp-std", + "bp-polkadot-core", + "bp-runtime", + "sp-api", ] [[package]] @@ -972,6 +940,7 @@ dependencies = [ "bp-rococo", "bp-runtime", "bp-wococo", + "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -993,8 +962,13 @@ dependencies = [ "pallet-aura", "pallet-authorship", "pallet-balances", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-bridge-relayers", "pallet-collator-selection", "pallet-session", + "pallet-shift-session-manager", "pallet-sudo", "pallet-timestamp", "pallet-transaction-payment", @@ -1003,8 +977,10 @@ dependencies = [ "parachain-info", "parachains-common", "parity-scale-codec", - "polkadot-parachain 0.9.27", + "polkadot-core-primitives", + "polkadot-parachain 0.9.31", "polkadot-runtime-common", + "polkadot-runtime-constants", "scale-info", "serde", "smallvec", @@ -1026,6 +1002,38 @@ dependencies = [ "xcm-executor", ] +[[package]] +name = "bridge-runtime-common" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-parachains", + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "frame-system", + "hash-db", + "log", + "millau-runtime", + "pallet-balances", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-trie", + "static_assertions", + "xcm", + "xcm-builder", + "xcm-executor", +] + [[package]] name = "bs58" version = "0.4.0" @@ -2930,6 +2938,19 @@ dependencies = [ "syn", ] +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "env_logger" version = "0.9.1" @@ -2970,6 +2991,33 @@ dependencies = [ "libc", ] +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash 0.8.0", + "impl-rlp", + "impl-serde 0.4.0", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81224dc661606574f5a0f28c9947d0ee1d93ff11c5f1c4e7272f52e8c0b5483c" +dependencies = [ + "ethbloom", + "fixed-hash 0.8.0", + "impl-rlp", + "impl-serde 0.4.0", + "primitive-types", + "uint", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -3081,7 +3129,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21e16290574b39ee41c71aeb90ae960c504ebaf1e2a1c87bd52aa56ed6e1a02f" dependencies = [ - "env_logger", + "env_logger 0.9.1", "log", ] @@ -3113,6 +3161,18 @@ dependencies = [ "scale-info", ] +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fixed-hash" version = "0.8.0" @@ -3990,6 +4050,24 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + [[package]] name = "impl-serde" version = "0.4.0" @@ -5101,6 +5179,68 @@ dependencies = [ "thrift", ] +[[package]] +name = "millau-runtime" +version = "0.1.0" +dependencies = [ + "beefy-primitives", + "bp-messages", + "bp-millau", + "bp-polkadot-core", + "bp-relayers", + "bp-rialto", + "bp-rialto-parachain", + "bp-runtime", + "bp-westend", + "bridge-runtime-common", + "env_logger 0.8.4", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "hex-literal", + "log", + "pallet-aura", + "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-bridge-relayers", + "pallet-grandpa", + "pallet-mmr", + "pallet-randomness-collective-flip", + "pallet-session", + "pallet-shift-session-manager", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-io", + "sp-mmr-primitives", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "static_assertions", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -5585,7 +5725,7 @@ checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "pallet-alliance" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#4e1e17cccd499dfe49e8c1bed01957953aa4c839" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "array-bytes", "frame-benchmarking", @@ -5606,7 +5746,7 @@ dependencies = [ [[package]] name = "pallet-asset-tx-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#4e1e17cccd499dfe49e8c1bed01957953aa4c839" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-support", "frame-system", @@ -5623,7 +5763,7 @@ dependencies = [ [[package]] name = "pallet-assets" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#4e1e17cccd499dfe49e8c1bed01957953aa4c839" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -5797,6 +5937,94 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-bridge-grandpa" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-runtime", + "bp-test-utils", + "finality-grandpa", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-finality-grandpa", + "sp-io", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "pallet-bridge-messages" +version = "0.1.0" +dependencies = [ + "bitvec", + "bp-messages", + "bp-runtime", + "bp-test-utils", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "num-traits", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-bridge-parachains" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-parachains", + "bp-polkadot-core", + "bp-runtime", + "bp-test-utils", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-bridge-grandpa", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "pallet-bridge-relayers" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-relayers", + "bp-runtime", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "pallet-bridge-messages", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" @@ -6424,6 +6652,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-shift-session-manager" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-staking", + "sp-std", +] + [[package]] name = "pallet-society" version = "4.0.0-dev" @@ -6934,8 +7177,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d32c34f4f5ca7f9196001c0aba5a1f9a5a12382c8944b8b0f90233282d1e8f8" dependencies = [ "cfg-if 1.0.0", + "ethereum-types", "hashbrown", "impl-trait-for-tuples", + "lru 0.8.1", "parity-util-mem-derive", "parking_lot 0.12.1", "primitive-types", @@ -8072,7 +8317,7 @@ name = "polkadot-performance-test" version = "0.9.31" source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ - "env_logger", + "env_logger 0.9.1", "kusama-runtime", "log", "polkadot-erasure-coding", @@ -8711,9 +8956,10 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cfd65aea0c5fa0bfcc7c9e7ca828c921ef778f43d325325ec84bda371bfa75a" dependencies = [ - "fixed-hash", + "fixed-hash 0.8.0", "impl-codec", - "impl-serde", + "impl-rlp", + "impl-serde 0.4.0", "scale-info", "uint", ] @@ -9148,7 +9394,7 @@ name = "remote-externalities" version = "0.10.0-dev" source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ - "env_logger", + "env_logger 0.9.1", "log", "parity-scale-codec", "serde", @@ -9205,6 +9451,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + [[package]] name = "rocksdb" version = "0.19.0" @@ -9269,11 +9525,6 @@ source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm- dependencies = [ "beefy-merkle-tree", "beefy-primitives", - "bp-messages 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "bp-rococo 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "bp-runtime 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "bp-wococo 0.1.0 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", - "bridge-runtime-common", "frame-benchmarking", "frame-executive", "frame-support", @@ -9901,7 +10152,6 @@ dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", "sp-sandbox", - "sp-serializer 4.0.0-dev (git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges)", "sp-wasm-interface", "thiserror", "wasm-instrument", @@ -11296,7 +11546,7 @@ dependencies = [ "futures", "hash-db", "hash256-std-hasher", - "impl-serde", + "impl-serde 0.4.0", "lazy_static", "libsecp256k1", "log", @@ -11607,16 +11857,7 @@ dependencies = [ [[package]] name = "sp-serializer" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#4e1e17cccd499dfe49e8c1bed01957953aa4c839" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "sp-serializer" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#aa7520bd0a2094204a6c0b33865aa264e6d686a5" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "serde", "serde_json", @@ -11679,7 +11920,7 @@ name = "sp-storage" version = "6.0.0" source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ - "impl-serde", + "impl-serde 0.4.0", "parity-scale-codec", "ref-cast", "serde", @@ -11781,7 +12022,7 @@ name = "sp-version" version = "5.0.0" source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ - "impl-serde", + "impl-serde 0.4.0", "parity-scale-codec", "parity-wasm", "scale-info", @@ -12472,6 +12713,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -13464,7 +13714,7 @@ dependencies = [ "sp-transaction-pool", "sp-version", "substrate-wasm-builder", - "westend-runtime-constants 0.9.31 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "westend-runtime-constants", "xcm", "xcm-builder", "xcm-executor", @@ -13482,18 +13732,6 @@ dependencies = [ "sp-runtime", ] -[[package]] -name = "westend-runtime-constants" -version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" -dependencies = [ - "frame-support", - "polkadot-primitives", - "polkadot-runtime-common", - "smallvec", - "sp-runtime", -] - [[package]] name = "westmint-runtime" version = "1.0.0" @@ -13553,7 +13791,7 @@ dependencies = [ "sp-transaction-pool", "sp-version", "substrate-wasm-builder", - "westend-runtime-constants 0.9.31 (git+https://github.com/paritytech/polkadot?branch=master)", + "westend-runtime-constants", "xcm", "xcm-builder", "xcm-executor", @@ -13851,8 +14089,3 @@ dependencies = [ "cc", "libc", ] - -[[patch.unused]] -name = "node-inspect" -version = "0.9.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" diff --git a/Cargo.toml b/Cargo.toml index 7e3eb2d88c6..2939fd905eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,18 @@ [workspace] members = [ + "bridges/bin/runtime-common", "bridges/modules/grandpa", "bridges/modules/messages", "bridges/modules/parachains", "bridges/modules/relayers", "bridges/modules/shift-session-manager", + "bridges/primitives/messages", "bridges/primitives/polkadot-core", "bridges/primitives/runtime", "bridges/primitives/chain-bridge-hub-rococo", "bridges/primitives/chain-bridge-hub-wococo", + "bridges/primitives/chain-rococo", + "bridges/primitives/chain-wococo", "client/cli", "client/consensus/aura", "client/consensus/common", @@ -89,7 +93,9 @@ frame-system = { git = "https://github.com/paritytech//substrate", branch = "sv- frame-system-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } frame-system-rpc-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } frame-try-runtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -node-inspect = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-alliance = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-assets = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-asset-tx-payment = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } pallet-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } pallet-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } pallet-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } @@ -221,6 +227,7 @@ sp-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked sp-runtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } sp-runtime-interface = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } sp-runtime-interface-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-serializer = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } sp-session = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } sp-staking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } sp-state-machine = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } @@ -296,6 +303,7 @@ polkadot-test-runtime = { git = "https://github.com/paritytech//polkadot", branc slot-range-helper = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } tracing-gum = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } tracing-gum-proc-macro = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +westend-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } xcm = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } xcm-builder = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } xcm-executor = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index b446c74b084..1509df525b1 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -31,7 +31,7 @@ cp target/release/polkadot ~/local_bridge_testing/bin/polkadot # 2. Build cumulus polkadot-parachain binary cd -cargo build --release --locked -p polkadot-parachain@0.9.230 +cargo build --release --locked -p polkadot-parachain@0.9.300 cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain # 3. Build substrate-relay binary diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 000db6e66b5..3845258ed6e 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -47,8 +47,10 @@ sp-version = { git = "https://github.com/paritytech/substrate", default-features # Polkadot pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +polkadot-runtime-constants = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } @@ -128,8 +130,10 @@ std = [ "pallet-xcm/std", "parachain-info/std", "parachains-common/std", + "polkadot-core-primitives/std", "polkadot-parachain/std", "polkadot-runtime-common/std", + "polkadot-runtime-constants/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index eb77346e09a..85414d09bef 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::universal_exports::HaulBlob; use bp_messages::{ source_chain::MessagesBridge, target_chain::{DispatchMessage, MessageDispatch}, @@ -24,6 +23,7 @@ use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, BalanceOf, Chain} use codec::Encode; use frame_support::{dispatch::Weight, parameter_types}; use xcm::latest::prelude::*; +use xcm_builder::{DispatchBlob, DispatchBlobError, HaulBlob}; // TODO:check-parameter - we could possibly use BridgeMessage from xcm:v3 stuff /// PLain "XCM" payload, which we transfer through bridge @@ -45,9 +45,9 @@ pub struct XcmBlobMessageDispatch MessageDispatch, BalanceOf> - for XcmBlobMessageDispatch + for XcmBlobMessageDispatch { type DispatchPayload = XcmAsPlainPayload; @@ -57,7 +57,7 @@ impl< log::error!( "[XcmBlobMessageDispatch] TODO: change here to XCMv3 dispatch_weight with XcmExecutor - message: ?...?", ); - 0 + Weight::zero() } fn dispatch( @@ -71,14 +71,23 @@ impl< log::error!("[XcmBlobMessageDispatch] payload error: {:?}", e); return MessageDispatchResult { dispatch_result: false, - unspent_weight: 0, + unspent_weight: Weight::zero(), dispatch_fee_paid_during_dispatch: false, } }, }; - let dispatch_result = match DispatchBlob::dispatch_blob(payload) { + let dispatch_result = match BlobDispatcher::dispatch_blob(payload) { Ok(_) => true, Err(e) => { + let e= match e { + DispatchBlobError::Unbridgable => "DispatchBlobError::Unbridgable", + DispatchBlobError::InvalidEncoding => "DispatchBlobError::InvalidEncoding", + DispatchBlobError::UnsupportedLocationVersion => "DispatchBlobError::UnsupportedLocationVersion", + DispatchBlobError::UnsupportedXcmVersion => "DispatchBlobError::UnsupportedXcmVersion", + DispatchBlobError::RoutingError => "DispatchBlobError::RoutingError", + DispatchBlobError::NonUniversalDestination => "DispatchBlobError::NonUniversalDestination", + DispatchBlobError::WrongGlobal => "DispatchBlobError::WrongGlobal", + }; log::error!( "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?}", e @@ -89,7 +98,7 @@ impl< MessageDispatchResult { dispatch_result, dispatch_fee_paid_during_dispatch: false, - unspent_weight: 0, + unspent_weight: Weight::zero(), } } } @@ -102,8 +111,7 @@ pub trait XcmBlobHauler { /// Runtime message sender adapter. type MessageSender: MessagesBridge< - super::Origin, - AccountIdOf, + super::RuntimeOrigin, BalanceOf, XcmAsPlainPayload, >; @@ -115,15 +123,16 @@ pub trait XcmBlobHauler { fn xcm_lane() -> LaneId; } -impl HaulBlob for T { +pub struct XcmBlobHaulerAdapter(sp_std::marker::PhantomData); +impl HaulBlob for XcmBlobHaulerAdapter { fn haul_blob(blob: sp_std::prelude::Vec) { - let lane = T::xcm_lane(); + let lane = H::xcm_lane(); // TODO:check-parameter - fee could be taken from BridgeMessage - or add as optional fo send_message // TODO:check-parameter - or add here something like PriceForSiblingDelivery - let fee = ::Balance::from(0u8); + let fee = ::Balance::from(0u8); - let result = T::MessageSender::send_message( - pallet_xcm::Origin::from(MultiLocation::from(T::message_sender_origin())).into(), + let result = H::MessageSender::send_message( + pallet_xcm::Origin::from(MultiLocation::from(H::message_sender_origin())).into(), lane, blob, fee, diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index ff382c7f5e4..475adf13100 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -14,11 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::{ - universal_exports::{BridgeBlobDispatcher, HaulBlobExporter}, - ParachainInfo, Runtime, WithBridgeHubWococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, - XcmRouter, -}; +use crate::{ParachainInfo, Runtime, WithBridgeHubWococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, XcmRouter}; use bp_messages::{ source_chain::TargetHeaderChain, target_chain::{ProvedMessages, SourceHeaderChain}, @@ -31,7 +27,7 @@ use bridge_runtime_common::{ messages::{ target::FromBridgedChainMessagesProof, BasicConfirmationTransactionEstimation, BridgedChain, ChainWithMessages, MessageBridge, MessageTransaction, ThisChain, - ThisChainWithMessages, WeightOf, + ThisChainWithMessages, }, }; use frame_support::{dispatch::Weight, parameter_types, RuntimeDebug}; @@ -40,6 +36,7 @@ use xcm::{ latest::prelude::*, prelude::{InteriorMultiLocation, NetworkId}, }; +use xcm_builder::{BridgeBlobDispatcher, HaulBlobExporter}; // TODO:check-parameter parameter_types! { @@ -58,7 +55,7 @@ pub type OnBridgeHubRococoBlobDispatcher = /// Export XCM messages to be relayed to the otherside pub type ToBridgeHubWococoHaulBlobExporter = - HaulBlobExporter; + HaulBlobExporter, WococoGlobalConsensusNetwork, ()>; pub struct ToBridgeHubWococoXcmBlobHauler; pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO: LaneId = [0, 0, 0, 2]; impl XcmBlobHauler for ToBridgeHubWococoXcmBlobHauler { @@ -114,7 +111,6 @@ impl ChainWithMessages for BridgeHubWococo { type AccountId = bp_bridge_hub_wococo::AccountId; type Signer = bp_bridge_hub_wococo::AccountSigner; type Signature = bp_bridge_hub_wococo::Signature; - type Weight = Weight; type Balance = bp_bridge_hub_wococo::Balance; } @@ -168,18 +164,18 @@ impl messages::BridgedChainWithMessages for BridgeHubWococo { fn estimate_delivery_transaction( message_payload: &[u8], include_pay_dispatch_fee_cost: bool, - message_dispatch_weight: WeightOf, - ) -> MessageTransaction> { + message_dispatch_weight: Weight, + ) -> MessageTransaction { let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX); - let extra_bytes_in_payload = Weight::from(message_payload_len) - .saturating_sub(pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH.into()); + let extra_bytes_in_payload = message_payload_len + .saturating_sub(pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH); MessageTransaction { - dispatch_weight: extra_bytes_in_payload - .saturating_mul(bp_bridge_hub_wococo::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT) + dispatch_weight: bp_bridge_hub_wococo::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT + .saturating_mul(extra_bytes_in_payload as u64) .saturating_add(bp_bridge_hub_wococo::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT) .saturating_sub(if include_pay_dispatch_fee_cost { - 0 + Weight::from_ref_time(0) } else { bp_bridge_hub_wococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT }) @@ -191,7 +187,7 @@ impl messages::BridgedChainWithMessages for BridgeHubWococo { } fn transaction_payment( - transaction: MessageTransaction>, + transaction: MessageTransaction, ) -> messages::BalanceOf { log::info!( "[BridgeHubWococo::BridgedChainWithMessages] transaction_payment (returns 0 balance, TODO: fix) - transaction: {:?}", @@ -211,21 +207,20 @@ impl ChainWithMessages for BridgeHubRococo { type AccountId = bp_bridge_hub_rococo::AccountId; type Signer = bp_bridge_hub_rococo::AccountSigner; type Signature = bp_bridge_hub_rococo::Signature; - type Weight = Weight; type Balance = bp_bridge_hub_rococo::Balance; } impl ThisChainWithMessages for BridgeHubRococo { - type Origin = crate::Origin; - type Call = crate::Call; + type RuntimeOrigin = crate::RuntimeOrigin; + type RuntimeCall = crate::RuntimeCall; type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< Self::AccountId, - { bp_bridge_hub_rococo::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT }, + { bp_bridge_hub_rococo::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() }, { bp_bridge_hub_wococo::EXTRA_STORAGE_PROOF_SIZE }, { bp_bridge_hub_rococo::TX_EXTRA_BYTES }, >; - fn is_message_accepted(origin: &Self::Origin, lane: &LaneId) -> bool { + fn is_message_accepted(origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool { log::info!("[BridgeHubRococo::ThisChainWithMessages] is_message_accepted - origin: {:?}, lane: {:?}", origin, lane); lane == &DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO } @@ -238,7 +233,7 @@ impl ThisChainWithMessages for BridgeHubRococo { } fn transaction_payment( - transaction: MessageTransaction>, + transaction: MessageTransaction, ) -> messages::BalanceOf { log::info!( "[BridgeHubRococo::ThisChainWithMessages] transaction_payment (returns 0 balance, TODO: fix) - transaction: {:?}", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 02df9574583..99dd24d6a29 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -14,11 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::{ - universal_exports::{BridgeBlobDispatcher, HaulBlobExporter}, - ParachainInfo, Runtime, WithBridgeHubRococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, - XcmRouter, -}; +use crate::{ParachainInfo, Runtime, WithBridgeHubRococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, XcmRouter}; use bp_messages::{ source_chain::TargetHeaderChain, target_chain::{ProvedMessages, SourceHeaderChain}, @@ -31,7 +27,7 @@ use bridge_runtime_common::{ messages::{ target::FromBridgedChainMessagesProof, BasicConfirmationTransactionEstimation, BridgedChain, ChainWithMessages, MessageBridge, MessageTransaction, ThisChain, - ThisChainWithMessages, WeightOf, + ThisChainWithMessages, }, }; use frame_support::{dispatch::Weight, parameter_types, RuntimeDebug}; @@ -40,6 +36,7 @@ use xcm::{ latest::prelude::*, prelude::{InteriorMultiLocation, NetworkId}, }; +use xcm_builder::{BridgeBlobDispatcher, HaulBlobExporter}; // TODO:check-parameter parameter_types! { @@ -58,7 +55,7 @@ pub type OnBridgeHubWococoBlobDispatcher = /// Export XCM messages to be relayed to the otherside pub type ToBridgeHubRococoHaulBlobExporter = - HaulBlobExporter; + HaulBlobExporter, RococoGlobalConsensusNetwork, ()>; pub struct ToBridgeHubRococoXcmBlobHauler; pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO: LaneId = [0, 0, 0, 1]; impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { @@ -114,7 +111,6 @@ impl ChainWithMessages for BridgeHubRococo { type AccountId = bp_bridge_hub_rococo::AccountId; type Signer = bp_bridge_hub_rococo::AccountSigner; type Signature = bp_bridge_hub_rococo::Signature; - type Weight = Weight; type Balance = bp_bridge_hub_rococo::Balance; } @@ -168,18 +164,18 @@ impl messages::BridgedChainWithMessages for BridgeHubRococo { fn estimate_delivery_transaction( message_payload: &[u8], include_pay_dispatch_fee_cost: bool, - message_dispatch_weight: WeightOf, - ) -> MessageTransaction> { + message_dispatch_weight: Weight, + ) -> MessageTransaction { let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX); - let extra_bytes_in_payload = Weight::from(message_payload_len) - .saturating_sub(pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH.into()); + let extra_bytes_in_payload = message_payload_len + .saturating_sub(pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH); MessageTransaction { - dispatch_weight: extra_bytes_in_payload - .saturating_mul(bp_bridge_hub_rococo::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT) + dispatch_weight: bp_bridge_hub_rococo::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT + .saturating_mul(extra_bytes_in_payload as u64) .saturating_add(bp_bridge_hub_rococo::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT) .saturating_sub(if include_pay_dispatch_fee_cost { - 0 + Weight::from_ref_time(0) } else { bp_bridge_hub_rococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT }) @@ -190,9 +186,7 @@ impl messages::BridgedChainWithMessages for BridgeHubRococo { } } - fn transaction_payment( - transaction: MessageTransaction>, - ) -> messages::BalanceOf { + fn transaction_payment(transaction: MessageTransaction) -> messages::BalanceOf { log::info!( "[BridgeHubRococo::BridgedChainWithMessages] transaction_payment (returns 0 balance, TODO: fix) - transaction: {:?}", transaction @@ -211,21 +205,20 @@ impl ChainWithMessages for BridgeHubWococo { type AccountId = bp_bridge_hub_wococo::AccountId; type Signer = bp_bridge_hub_wococo::AccountSigner; type Signature = bp_bridge_hub_wococo::Signature; - type Weight = Weight; type Balance = bp_bridge_hub_wococo::Balance; } impl ThisChainWithMessages for BridgeHubWococo { - type Origin = crate::Origin; - type Call = crate::Call; + type RuntimeOrigin = crate::RuntimeOrigin; + type RuntimeCall = crate::RuntimeCall; type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< Self::AccountId, - { bp_bridge_hub_wococo::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT }, + { bp_bridge_hub_wococo::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() }, { bp_bridge_hub_rococo::EXTRA_STORAGE_PROOF_SIZE }, { bp_bridge_hub_wococo::TX_EXTRA_BYTES }, >; - fn is_message_accepted(origin: &Self::Origin, lane: &LaneId) -> bool { + fn is_message_accepted(origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool { log::info!("[BridgeHubWococo::ThisChainWithMessages] is_message_accepted - origin: {:?}, lane: {:?}", origin, lane); lane == &DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO } @@ -237,9 +230,7 @@ impl ThisChainWithMessages for BridgeHubWococo { MessageNonce::MAX / 2 } - fn transaction_payment( - transaction: MessageTransaction>, - ) -> messages::BalanceOf { + fn transaction_payment(transaction: MessageTransaction) -> messages::BalanceOf { log::info!( "[BridgeHubWococo::ThisChainWithMessages] transaction_payment (returns 0 balance, TODO: fix) - transaction: {:?}", transaction diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/constants.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/constants.rs new file mode 100644 index 00000000000..4352e3ef554 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/constants.rs @@ -0,0 +1,64 @@ +// Copyright (C) 2022 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. + +pub mod currency { + use polkadot_core_primitives::Balance; + use polkadot_runtime_constants as constants; + + /// The existential deposit. Set to 1/10 of its parent Relay Chain (v9010). + pub const EXISTENTIAL_DEPOSIT: Balance = constants::currency::EXISTENTIAL_DEPOSIT / 10; + + pub const CENTS: Balance = constants::currency::CENTS; +} + +pub mod fee { + use frame_support::weights::{ + constants::ExtrinsicBaseWeight, WeightToFeeCoefficient, WeightToFeeCoefficients, + WeightToFeePolynomial, + }; + use polkadot_core_primitives::Balance; + use smallvec::smallvec; + pub use sp_runtime::Perbill; + + /// The block saturation level. Fees will be updates based on this value. + pub const TARGET_BLOCK_FULLNESS: Perbill = Perbill::from_percent(25); + + /// Handles converting a weight scalar to a fee value, based on the scale and granularity of the + /// node's balance type. + /// + /// This should typically create a mapping between the following ranges: + /// - [0, MAXIMUM_BLOCK_WEIGHT] + /// - [Balance::min, Balance::max] + /// + /// Yet, it can be used for any other sort of change to weight-fee. Some examples being: + /// - Setting it to `0` will essentially disable the weight fee. + /// - Setting it to `1` will cause the literal `#[weight = x]` values to be charged. + pub struct WeightToFee; + impl WeightToFeePolynomial for WeightToFee { + type Balance = Balance; + fn polynomial() -> WeightToFeeCoefficients { + // in Polkadot, extrinsic base weight (smallest non-zero weight) is mapped to 1/10 CENT: + // in Statemint, we map to 1/10 of that, or 1/100 CENT + let p = super::currency::CENTS; + let q = 100 * Balance::from(ExtrinsicBaseWeight::get().ref_time()); + smallvec![WeightToFeeCoefficient { + degree: 1, + negative: false, + coeff_frac: Perbill::from_rational(p % q, q), + coeff_integer: p / q, + }] + } + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 9e7f48b5653..d154f9721d7 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -25,11 +25,13 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); pub mod bridge_common_config; pub mod bridge_hub_rococo_config; pub mod bridge_hub_wococo_config; +pub mod constants; mod weights; pub mod xcm_config; use codec::Decode; use bridge_common_config::*; +use constants::currency::*; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use smallvec::smallvec; use sp_api::impl_runtime_apis; @@ -48,9 +50,10 @@ use sp_version::RuntimeVersion; use frame_support::{ construct_runtime, parameter_types, + dispatch::DispatchClass, traits::Everything, weights::{ - constants::WEIGHT_PER_SECOND, ConstantMultiplier, DispatchClass, Weight, + ConstantMultiplier, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }, PalletId, @@ -77,7 +80,7 @@ use crate::{ bridge_hub_wococo_config::OnBridgeHubWococoBlobDispatcher, xcm_config::XcmRouter, }; -use parachains_common::{AccountId, Signature}; +use parachains_common::{AccountId, Signature, AVERAGE_ON_INITIALIZE_RATIO, NORMAL_DISPATCH_RATIO, MAXIMUM_BLOCK_WEIGHT}; use xcm::latest::prelude::BodyId; use xcm_executor::XcmExecutor; @@ -123,10 +126,10 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; /// Extrinsic type that has already been checked. -pub type CheckedExtrinsic = generic::CheckedExtrinsic; +pub type CheckedExtrinsic = generic::CheckedExtrinsic; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< @@ -154,7 +157,7 @@ impl WeightToFeePolynomial for WeightToFee { // in Rococo, extrinsic base weight (smallest non-zero weight) is mapped to 1 MILLIUNIT: // in our template, we map to 1/10 of that, or 1/10 MILLIUNIT let p = MILLIUNIT / 10; - let q = 100 * Balance::from(ExtrinsicBaseWeight::get()); + let q = 100 * Balance::from(ExtrinsicBaseWeight::get().ref_time()); smallvec![WeightToFeeCoefficient { degree: 1, negative: false, @@ -223,19 +226,6 @@ pub const UNIT: Balance = 1_000_000_000_000; pub const MILLIUNIT: Balance = 1_000_000_000; pub const MICROUNIT: Balance = 1_000_000; -/// The existential deposit. Set to 1/10 of the Connected Relay Chain. -pub const EXISTENTIAL_DEPOSIT: Balance = MILLIUNIT; - -/// We assume that ~5% of the block weight is consumed by `on_initialize` handlers. This is -/// used to limit the maximal weight of a single extrinsic. -const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); - -/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used by -/// `Operational` extrinsics. -const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); - -/// We allow for 0.5 of a second of compute with a 12 second average block time. -const MAXIMUM_BLOCK_WEIGHT: Weight = WEIGHT_PER_SECOND / 2; /// The version information used to identify this runtime when compiled natively. #[cfg(feature = "std")] @@ -262,11 +252,6 @@ pub mod runtime_api { parameter_types! { pub const Version: RuntimeVersion = VERSION; - - // This part is copied from Substrate's `bin/node/runtime/src/lib.rs`. - // The `RuntimeBlockLength` and `RuntimeBlockWeights` exist here because the - // `DeletionWeightLimit` and `DeletionQueueDepth` depend on those to parameterize - // the lazy contract deletion. pub RuntimeBlockLength: BlockLength = BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder() @@ -296,7 +281,7 @@ impl frame_system::Config for Runtime { /// The identifier used to distinguish between accounts. type AccountId = AccountId; /// The aggregated dispatch type that is available for extrinsics. - type Call = Call; + type RuntimeCall = RuntimeCall; /// The lookup mechanism to get account ID from whatever is passed in dispatchers. type Lookup = AccountIdLookup; /// The index type for storing how many extrinsics an account has signed. @@ -310,9 +295,9 @@ impl frame_system::Config for Runtime { /// The header type. type Header = generic::Header; /// The ubiquitous event type. - type Event = Event; + type RuntimeEvent = RuntimeEvent; /// The ubiquitous origin type. - type Origin = Origin; + type RuntimeOrigin = RuntimeOrigin; /// Maximum number of block number to block hash mappings to keep (oldest pruned first). type BlockHashCount = BlockHashCount; /// Runtime version. @@ -365,7 +350,6 @@ impl pallet_authorship::Config for Runtime { type EventHandler = (CollatorSelection,); } -// TODO:check-parameter parameter_types! { pub const ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT; pub const MaxLocks: u32 = 50; @@ -377,11 +361,11 @@ impl pallet_balances::Config for Runtime { /// The type for recording an account's balance. type Balance = Balance; /// The ubiquitous event type. - type Event = Event; + type RuntimeEvent = RuntimeEvent; type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; - type WeightInfo = pallet_balances::weights::SubstrateWeight; + type WeightInfo = weights::pallet_balances::WeightInfo; type MaxReserves = MaxReserves; type ReserveIdentifier = [u8; 8]; } @@ -394,7 +378,7 @@ parameter_types! { } impl pallet_transaction_payment::Config for Runtime { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; @@ -403,12 +387,12 @@ impl pallet_transaction_payment::Config for Runtime { } parameter_types! { - pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4; - pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4; + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); } impl cumulus_pallet_parachain_system::Config for Runtime { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type OnSystemEvent = (); type SelfParaId = parachain_info::Pallet; type OutboundXcmpMessageSource = XcmpQueue; @@ -424,7 +408,7 @@ impl parachain_info::Config for Runtime {} impl cumulus_pallet_aura_ext::Config for Runtime {} impl cumulus_pallet_xcmp_queue::Config for Runtime { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; type ChannelInfo = ParachainSystem; type VersionWrapper = PolkadotXcm; @@ -436,7 +420,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { } impl cumulus_pallet_dmp_queue::Config for Runtime { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; type ExecuteOverweightOrigin = EnsureRoot; } @@ -448,7 +432,7 @@ parameter_types! { } impl pallet_session::Config for Runtime { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type ValidatorId = ::AccountId; // we don't have stash and controller, thus we don't need the convert as well. type ValidatorIdOf = pallet_collator_selection::IdentityCollator; @@ -480,7 +464,7 @@ parameter_types! { pub type CollatorSelectionUpdateOrigin = EnsureRoot; impl pallet_collator_selection::Config for Runtime { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type Currency = Balances; type UpdateOrigin = CollatorSelectionUpdateOrigin; type PotId = PotId; @@ -496,8 +480,8 @@ impl pallet_collator_selection::Config for Runtime { } impl pallet_sudo::Config for Runtime { - type Call = Call; - type Event = Event; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; } // Add bridge pallets (GPA) @@ -508,8 +492,9 @@ impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_wococo::Wococo; type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; - // TODO:check-parameter - type WeightInfo = (); + type MaxBridgedAuthorities = frame_support::traits::ConstU32<{bp_wococo::MAX_AUTHORITIES_COUNT}>; + type MaxBridgedHeaderSize = frame_support::traits::ConstU32<{bp_wococo::MAX_HEADER_SIZE}>; + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } /// Add granda bridge pallet to track Rococo relay chain @@ -518,8 +503,9 @@ impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_rococo::Rococo; type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; - // TODO:check-parameter - type WeightInfo = (); + type MaxBridgedAuthorities = frame_support::traits::ConstU32<{bp_rococo::MAX_AUTHORITIES_COUNT}>; + type MaxBridgedHeaderSize = frame_support::traits::ConstU32<{bp_rococo::MAX_HEADER_SIZE}>; + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } pub const ROCOCO_BRIDGE_PARA_PALLET_NAME: &str = "Paras"; @@ -533,33 +519,32 @@ parameter_types! { /// Add parachain bridge pallet to track Wococo bridge hub parachain pub type BridgeParachainWococoInstance = pallet_bridge_parachains::Instance1; impl pallet_bridge_parachains::Config for Runtime { - type Event = Event; - // TODO:check-parameter - type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; type BridgesGrandpaPalletInstance = BridgeGrandpaWococoInstance; type ParasPalletName = WococoBridgeParachainPalletName; type TrackedParachains = Everything; type HeadsToKeep = ParachainHeadsToKeep; + type MaxParaHeadSize = frame_support::traits::ConstU32<{bp_wococo::MAX_NESTED_PARACHAIN_HEAD_SIZE}>; } /// Add parachain bridge pallet to track Rococo bridge hub parachain pub type BridgeParachainRococoInstance = pallet_bridge_parachains::Instance2; impl pallet_bridge_parachains::Config for Runtime { - type Event = Event; - // TODO:check-parameter - type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; type BridgesGrandpaPalletInstance = BridgeGrandpaRococoInstance; type ParasPalletName = RococoBridgeParachainPalletName; type TrackedParachains = Everything; type HeadsToKeep = ParachainHeadsToKeep; + type MaxParaHeadSize = frame_support::traits::ConstU32<{bp_rococo::MAX_NESTED_PARACHAIN_HEAD_SIZE}>; } /// Add XCM messages support for BrigdeHubRococo to support Rococo->Wococo XCM messages pub type WithBridgeHubWococoMessagesInstance = pallet_bridge_messages::Instance1; impl pallet_bridge_messages::Config for Runtime { - type Event = Event; - // TODO:check-parameter - copy of MillauWeigth + refactor - type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; type BridgedChainId = bridge_hub_rococo_config::BridgeHubWococoChainId; type Parameter = (); type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; @@ -593,9 +578,8 @@ impl pallet_bridge_messages::Config for Run /// Add XCM messages support for BrigdeHubWococo to support Wococo->Rococo XCM messages pub type WithBridgeHubRococoMessagesInstance = pallet_bridge_messages::Instance2; impl pallet_bridge_messages::Config for Runtime { - type Event = Event; - // TODO:check-parameter - copy of MillauWeigth + refactor - type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; type BridgedChainId = bridge_hub_wococo_config::BridgeHubRococoChainId; type Parameter = (); type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; @@ -692,6 +676,10 @@ mod benches { [pallet_timestamp, Timestamp] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_xcmp_queue, XcmpQueue] + // XCM + // NOTE: Make sure you point to the individual modules below. + [pallet_xcm_benchmarks::fungible, XcmBalances] + [pallet_xcm_benchmarks::generic, XcmGeneric] ); } @@ -836,7 +824,7 @@ impl_runtime_apis! { fn estimate_message_delivery_and_dispatch_fee( _lane_id: bp_messages::LaneId, payload: XcmAsPlainPayload, - _conversion_rate: Option, + conversion_rate: Option, ) -> Option { None } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/block_weights.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/block_weights.rs index 4db90f0c020..4eaa2cba639 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/block_weights.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/block_weights.rs @@ -23,7 +23,7 @@ pub mod constants { parameter_types! { /// Importing a block with 0 Extrinsics. - pub const BlockExecutionWeight: Weight = 5_000_000 * constants::WEIGHT_PER_NANOS; + pub const BlockExecutionWeight: Weight = constants::WEIGHT_PER_NANOS.saturating_mul(5_000_000); } #[cfg(test)] diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/cumulus_pallet_xcmp_queue.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/cumulus_pallet_xcmp_queue.rs new file mode 100644 index 00000000000..998f4f660ea --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/cumulus_pallet_xcmp_queue.rs @@ -0,0 +1,61 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `cumulus_pallet_xcmp_queue` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-08-09, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm6`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemint-dev"), DB CACHE: 1024 + +// Executed Command: +// ./artifacts/polkadot-parachain +// benchmark +// pallet +// --chain=statemint-dev +// --execution=wasm +// --wasm-execution=compiled +// --pallet=cumulus_pallet_xcmp_queue +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./file_header.txt +// --output=./parachains/runtimes/assets/statemint/src/weights + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `cumulus_pallet_xcmp_queue`. +pub struct WeightInfo(PhantomData); +impl cumulus_pallet_xcmp_queue::WeightInfo for WeightInfo { + // Storage: XcmpQueue QueueConfig (r:1 w:1) + fn set_config_with_u32() -> Weight { + Weight::from_ref_time(5_192_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: XcmpQueue QueueConfig (r:1 w:1) + fn set_config_with_weight() -> Weight { + Weight::from_ref_time(5_363_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/extrinsic_weights.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/extrinsic_weights.rs index 158ba99c6a4..834374b6fad 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/extrinsic_weights.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/extrinsic_weights.rs @@ -23,7 +23,7 @@ pub mod constants { parameter_types! { /// Executing a NO-OP `System::remarks` Extrinsic. - pub const ExtrinsicBaseWeight: Weight = 125_000 * constants::WEIGHT_PER_NANOS; + pub const ExtrinsicBaseWeight: Weight = constants::WEIGHT_PER_NANOS.saturating_mul(125_000); } #[cfg(test)] diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs index ed0b4dbcd47..504a4280fcd 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs @@ -18,9 +18,12 @@ //! Expose the auto generated weight files. pub mod block_weights; +pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod paritydb_weights; +pub mod pallet_balances; pub mod rocksdb_weights; +pub mod xcm; pub use block_weights::constants::BlockExecutionWeight; pub use extrinsic_weights::constants::ExtrinsicBaseWeight; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_balances.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_balances.rs new file mode 100644 index 00000000000..465247219dd --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_balances.rs @@ -0,0 +1,91 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `pallet_balances` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-09-21, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm6`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemint-dev"), DB CACHE: 1024 + +// Executed Command: +// ./artifacts/polkadot-parachain +// benchmark +// pallet +// --chain=statemint-dev +// --execution=wasm +// --wasm-execution=compiled +// --pallet=pallet_balances +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./file_header.txt +// --output=./parachains/runtimes/assets/statemint/src/weights/pallet_balances.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight}}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_balances`. +pub struct WeightInfo(PhantomData); +impl pallet_balances::WeightInfo for WeightInfo { + // Storage: System Account (r:1 w:1) + fn transfer() -> Weight { + Weight::from_ref_time(48_009_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:1 w:1) + fn transfer_keep_alive() -> Weight { + Weight::from_ref_time(35_939_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:1 w:1) + fn set_balance_creating() -> Weight { + Weight::from_ref_time(26_600_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:1 w:1) + fn set_balance_killing() -> Weight { + Weight::from_ref_time(30_092_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:2 w:2) + fn force_transfer() -> Weight { + Weight::from_ref_time(47_435_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + // Storage: System Account (r:1 w:1) + fn transfer_all() -> Weight { + Weight::from_ref_time(41_179_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:1 w:1) + fn force_unreserve() -> Weight { + Weight::from_ref_time(22_413_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/paritydb_weights.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/paritydb_weights.rs index 843823c1bf3..8083ccb4001 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/paritydb_weights.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/paritydb_weights.rs @@ -25,8 +25,8 @@ pub mod constants { /// `ParityDB` can be enabled with a feature flag, but is still experimental. These weights /// are available for brave runtime engineers who may want to try this out as default. pub const ParityDbWeight: RuntimeDbWeight = RuntimeDbWeight { - read: 8_000 * constants::WEIGHT_PER_NANOS, - write: 50_000 * constants::WEIGHT_PER_NANOS, + read: 8_000 * constants::WEIGHT_PER_NANOS.ref_time(), + write: 50_000 * constants::WEIGHT_PER_NANOS.ref_time(), }; } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/rocksdb_weights.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/rocksdb_weights.rs index 05e06b0eabe..1db87f143f3 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/rocksdb_weights.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/rocksdb_weights.rs @@ -25,8 +25,8 @@ pub mod constants { /// By default, Substrate uses `RocksDB`, so this will be the weight used throughout /// the runtime. pub const RocksDbWeight: RuntimeDbWeight = RuntimeDbWeight { - read: 25_000 * constants::WEIGHT_PER_NANOS, - write: 100_000 * constants::WEIGHT_PER_NANOS, + read: 25_000 * constants::WEIGHT_PER_NANOS.ref_time(), + write: 100_000 * constants::WEIGHT_PER_NANOS.ref_time(), }; } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs new file mode 100644 index 00000000000..6706efe1757 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs @@ -0,0 +1,247 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +mod pallet_xcm_benchmarks_fungible; +mod pallet_xcm_benchmarks_generic; + +use crate::Runtime; +use frame_support::weights::Weight; +use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; +use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; +use sp_std::{cmp, prelude::*}; +use xcm::{ + latest::{prelude::*, Weight as XCMWeight}, + DoubleEncoded, +}; + +trait WeighMultiAssets { + fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight; +} + +const MAX_ASSETS: u32 = 100; + +impl WeighMultiAssets for MultiAssetFilter { + fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight { + let weight = match self { + Self::Definite(assets) => + weight.saturating_mul(assets.inner().into_iter().count() as u64), + Self::Wild(_) => weight.saturating_mul(MAX_ASSETS as u64), + }; + weight.ref_time() + } +} + +impl WeighMultiAssets for MultiAssets { + fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight { + weight.saturating_mul(self.inner().into_iter().count() as u64).ref_time() + } +} + +pub struct BridgeHubXcmWeight(core::marker::PhantomData); +impl XcmWeightInfo for BridgeHubXcmWeight { + fn withdraw_asset(assets: &MultiAssets) -> XCMWeight { + assets.weigh_multi_assets(XcmFungibleWeight::::withdraw_asset()) + } + // Currently there is no trusted reserve + fn reserve_asset_deposited(_assets: &MultiAssets) -> XCMWeight { + u64::MAX + } + fn receive_teleported_asset(assets: &MultiAssets) -> XCMWeight { + assets.weigh_multi_assets(XcmFungibleWeight::::receive_teleported_asset()) + } + fn query_response( + _query_id: &u64, + _response: &Response, + _max_weight: &u64, + _querier: &Option, + ) -> XCMWeight { + XcmGeneric::::query_response().ref_time() + } + fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> XCMWeight { + assets.weigh_multi_assets(XcmFungibleWeight::::transfer_asset()) + } + fn transfer_reserve_asset( + assets: &MultiAssets, + _dest: &MultiLocation, + _xcm: &Xcm<()>, + ) -> XCMWeight { + assets.weigh_multi_assets(XcmFungibleWeight::::transfer_reserve_asset()) + } + fn transact( + _origin_type: &OriginKind, + _require_weight_at_most: &u64, + _call: &DoubleEncoded, + ) -> XCMWeight { + XcmGeneric::::transact().ref_time() + } + fn hrmp_new_channel_open_request( + _sender: &u32, + _max_message_size: &u32, + _max_capacity: &u32, + ) -> XCMWeight { + // XCM Executor does not currently support HRMP channel operations + Weight::MAX.ref_time() + } + fn hrmp_channel_accepted(_recipient: &u32) -> XCMWeight { + // XCM Executor does not currently support HRMP channel operations + Weight::MAX.ref_time() + } + fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> XCMWeight { + // XCM Executor does not currently support HRMP channel operations + Weight::MAX.ref_time() + } + fn clear_origin() -> XCMWeight { + XcmGeneric::::clear_origin().ref_time() + } + fn descend_origin(_who: &InteriorMultiLocation) -> XCMWeight { + XcmGeneric::::descend_origin().ref_time() + } + fn report_error(_query_response_info: &QueryResponseInfo) -> XCMWeight { + XcmGeneric::::report_error().ref_time() + } + + fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> XCMWeight { + // Hardcoded till the XCM pallet is fixed + let hardcoded_weight = Weight::from_ref_time(1_000_000_000 as u64).ref_time(); + let weight = assets.weigh_multi_assets(XcmFungibleWeight::::deposit_asset()); + cmp::min(hardcoded_weight, weight) + } + fn deposit_reserve_asset( + assets: &MultiAssetFilter, + _dest: &MultiLocation, + _xcm: &Xcm<()>, + ) -> XCMWeight { + assets.weigh_multi_assets(XcmFungibleWeight::::deposit_reserve_asset()) + } + fn exchange_asset( + _give: &MultiAssetFilter, + _receive: &MultiAssets, + _maximal: &bool, + ) -> XCMWeight { + Weight::MAX.ref_time() + } + fn initiate_reserve_withdraw( + assets: &MultiAssetFilter, + _reserve: &MultiLocation, + _xcm: &Xcm<()>, + ) -> XCMWeight { + assets.weigh_multi_assets(XcmGeneric::::initiate_reserve_withdraw()) + } + fn initiate_teleport( + assets: &MultiAssetFilter, + _dest: &MultiLocation, + _xcm: &Xcm<()>, + ) -> XCMWeight { + // Hardcoded till the XCM pallet is fixed + let hardcoded_weight = Weight::from_ref_time(200_000_000 as u64).ref_time(); + let weight = assets.weigh_multi_assets(XcmFungibleWeight::::initiate_teleport()); + cmp::min(hardcoded_weight, weight) + } + fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> XCMWeight { + XcmGeneric::::report_holding().ref_time() + } + fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> XCMWeight { + XcmGeneric::::buy_execution().ref_time() + } + fn refund_surplus() -> XCMWeight { + XcmGeneric::::refund_surplus().ref_time() + } + fn set_error_handler(_xcm: &Xcm) -> XCMWeight { + XcmGeneric::::set_error_handler().ref_time() + } + fn set_appendix(_xcm: &Xcm) -> XCMWeight { + XcmGeneric::::set_appendix().ref_time() + } + fn clear_error() -> XCMWeight { + XcmGeneric::::clear_error().ref_time() + } + fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> XCMWeight { + XcmGeneric::::claim_asset().ref_time() + } + fn trap(_code: &u64) -> XCMWeight { + XcmGeneric::::trap().ref_time() + } + fn subscribe_version(_query_id: &QueryId, _max_response_weight: &u64) -> XCMWeight { + XcmGeneric::::subscribe_version().ref_time() + } + fn unsubscribe_version() -> XCMWeight { + XcmGeneric::::unsubscribe_version().ref_time() + } + fn burn_asset(assets: &MultiAssets) -> XCMWeight { + assets.weigh_multi_assets(XcmGeneric::::burn_asset()) + } + fn expect_asset(assets: &MultiAssets) -> XCMWeight { + assets.weigh_multi_assets(XcmGeneric::::expect_asset()) + } + fn expect_origin(_origin: &Option) -> XCMWeight { + XcmGeneric::::expect_origin().ref_time() + } + fn expect_error(_error: &Option<(u32, XcmError)>) -> XCMWeight { + XcmGeneric::::expect_error().ref_time() + } + fn query_pallet(_module_name: &Vec, _response_info: &QueryResponseInfo) -> XCMWeight { + XcmGeneric::::query_pallet().ref_time() + } + fn expect_pallet( + _index: &u32, + _name: &Vec, + _module_name: &Vec, + _crate_major: &u32, + _min_crate_minor: &u32, + ) -> XCMWeight { + XcmGeneric::::expect_pallet().ref_time() + } + fn report_transact_status(_response_info: &QueryResponseInfo) -> XCMWeight { + XcmGeneric::::report_transact_status().ref_time() + } + fn clear_transact_status() -> XCMWeight { + XcmGeneric::::clear_transact_status().ref_time() + } + fn universal_origin(_: &Junction) -> XCMWeight { + Weight::MAX.ref_time() + } + fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> XCMWeight { + Weight::MAX.ref_time() + } + fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { + Weight::MAX.ref_time() + } + fn set_fees_mode(_: &bool) -> XCMWeight { + XcmGeneric::::set_fees_mode().ref_time() + } + fn set_topic(_topic: &[u8; 32]) -> XCMWeight { + XcmGeneric::::set_topic().ref_time() + } + fn clear_topic() -> XCMWeight { + XcmGeneric::::clear_topic().ref_time() + } + fn alias_origin(_: &MultiLocation) -> XCMWeight { + // XCM Executor does not currently support alias origin operations + Weight::MAX.ref_time() + } + fn unpaid_execution(_: &WeightLimit, _: &Option) -> XCMWeight { + XcmGeneric::::unpaid_execution().ref_time() + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs new file mode 100644 index 00000000000..900472b6c9d --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -0,0 +1,107 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + + +//! Autogenerated weights for `pallet_xcm_benchmarks::fungible` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-09-21, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm6`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemint-dev"), DB CACHE: 1024 + +// Executed Command: +// ./artifacts/polkadot-parachain +// benchmark +// pallet +// --template=./templates/xcm-bench-template.hbs +// --chain=statemint-dev +// --execution=wasm +// --wasm-execution=compiled +// --pallet=pallet_xcm_benchmarks::fungible +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./file_header.txt +// --output=./parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weights for `pallet_xcm_benchmarks::fungible`. +pub struct WeightInfo(PhantomData); +impl WeightInfo { + // Storage: System Account (r:1 w:1) + pub(crate) fn withdraw_asset() -> Weight { + Weight::from_ref_time(33_878_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:2 w:2) + pub(crate) fn transfer_asset() -> Weight { + Weight::from_ref_time(39_130_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + // Storage: System Account (r:2 w:2) + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn transfer_reserve_asset() -> Weight { + Weight::from_ref_time(54_404_000 as u64) + .saturating_add(T::DbWeight::get().reads(8 as u64)) + .saturating_add(T::DbWeight::get().writes(4 as u64)) + } + pub(crate) fn receive_teleported_asset() -> Weight { + Weight::from_ref_time(6_586_000 as u64) + } + // Storage: System Account (r:1 w:1) + pub(crate) fn deposit_asset() -> Weight { + Weight::from_ref_time(34_055_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: System Account (r:1 w:1) + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn deposit_reserve_asset() -> Weight { + Weight::from_ref_time(50_905_000 as u64) + .saturating_add(T::DbWeight::get().reads(7 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn initiate_teleport() -> Weight { + Weight::from_ref_time(26_715_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs new file mode 100644 index 00000000000..79066a2b9e5 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -0,0 +1,189 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + + +//! Autogenerated weights for `pallet_xcm_benchmarks::generic` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-09-30, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemint-dev"), DB CACHE: 1024 + +// Executed Command: +// ./artifacts/polkadot-parachain +// benchmark +// pallet +// --template=./templates/xcm-bench-template.hbs +// --chain=statemint-dev +// --execution=wasm +// --wasm-execution=compiled +// --pallet=pallet_xcm_benchmarks::generic +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./file_header.txt +// --template=./templates/xcm-bench-template.hbs +// --output=./parachains/runtimes/assets/statemint/src/weights/xcm/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weights for `pallet_xcm_benchmarks::generic`. +pub struct WeightInfo(PhantomData); +impl WeightInfo { + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn report_holding() -> Weight { + Weight::from_ref_time(1_305_689_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + pub(crate) fn buy_execution() -> Weight { + Weight::from_ref_time(8_843_000 as u64) + } + // Storage: PolkadotXcm Queries (r:1 w:0) + pub(crate) fn query_response() -> Weight { + Weight::from_ref_time(19_216_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + } + pub(crate) fn transact() -> Weight { + Weight::from_ref_time(22_708_000 as u64) + } + pub(crate) fn refund_surplus() -> Weight { + Weight::from_ref_time(9_040_000 as u64) + } + pub(crate) fn set_error_handler() -> Weight { + Weight::from_ref_time(6_222_000 as u64) + } + pub(crate) fn set_appendix() -> Weight { + Weight::from_ref_time(6_411_000 as u64) + } + pub(crate) fn clear_error() -> Weight { + Weight::from_ref_time(6_222_000 as u64) + } + pub(crate) fn descend_origin() -> Weight { + Weight::from_ref_time(7_112_000 as u64) + } + pub(crate) fn clear_origin() -> Weight { + Weight::from_ref_time(6_340_000 as u64) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn report_error() -> Weight { + Weight::from_ref_time(22_943_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + // Storage: PolkadotXcm AssetTraps (r:1 w:1) + pub(crate) fn claim_asset() -> Weight { + Weight::from_ref_time(13_178_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + pub(crate) fn trap() -> Weight { + Weight::from_ref_time(6_333_000 as u64) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn subscribe_version() -> Weight { + Weight::from_ref_time(31_798_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) + pub(crate) fn unsubscribe_version() -> Weight { + Weight::from_ref_time(9_728_000 as u64) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn initiate_reserve_withdraw() -> Weight { + Weight::from_ref_time(1_583_652_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + pub(crate) fn burn_asset() -> Weight { + Weight::from_ref_time(497_448_000 as u64) + } + pub(crate) fn expect_asset() -> Weight { + Weight::from_ref_time(38_383_000 as u64) + } + pub(crate) fn expect_origin() -> Weight { + Weight::from_ref_time(6_308_000 as u64) + } + pub(crate) fn expect_error() -> Weight { + Weight::from_ref_time(6_327_000 as u64) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn query_pallet() -> Weight { + Weight::from_ref_time(26_011_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + pub(crate) fn expect_pallet() -> Weight { + Weight::from_ref_time(8_008_000 as u64) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn report_transact_status() -> Weight { + Weight::from_ref_time(22_963_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + pub(crate) fn clear_transact_status() -> Weight { + Weight::from_ref_time(6_378_000 as u64) + } + pub(crate) fn set_topic() -> Weight { + Weight::from_ref_time(6_313_000 as u64) + } + pub(crate) fn clear_topic() -> Weight { + Weight::from_ref_time(6_324_000 as u64) + } + pub(crate) fn set_fees_mode() -> Weight { + Weight::from_ref_time(6_336_000 as u64) + } + pub(crate) fn unpaid_execution() -> Weight { Weight::from_ref_time(3_111_000 as u64) } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index c6dead42d5a..20e72bb3040 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -15,7 +15,7 @@ // along with Cumulus. If not, see . use super::{ - AccountId, Balance, Balances, Call, Event, Origin, ParachainInfo, ParachainSystem, PolkadotXcm, + AccountId, Balance, Balances, RuntimeCall, RuntimeEvent, RuntimeOrigin, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, XcmpQueue, }; use crate::{ @@ -25,25 +25,19 @@ use crate::{ use frame_support::{ match_types, parameter_types, traits::{Everything, Nothing}, - weights::{IdentityFee, Weight}, + weights::IdentityFee, }; use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use xcm::latest::prelude::*; -use xcm_builder::{ - AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, - EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - UsingComponents, -}; +use xcm_builder::{AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, IsConcrete, NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds}; use xcm_executor::{traits::ExportXcm, XcmExecutor}; parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); // TODO: hack: hardcoded Polkadot? pub const RelayNetwork: NetworkId = NetworkId::Rococo; - pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); } @@ -81,25 +75,23 @@ pub type XcmOriginToTransactDispatchOrigin = ( // Sovereign account converter; this attempts to derive an `AccountId` from the origin location // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for // foreign chains who want to have a local sovereign account on this chain which they control. - SovereignSignedViaLocation, + SovereignSignedViaLocation, // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when // recognized. - RelayChainAsNative, + RelayChainAsNative, // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when // recognized. - SiblingParachainAsNative, + SiblingParachainAsNative, // Native signed account converter; this just converts an `AccountId32` origin into a normal // `Origin::Signed` origin of the same 32-byte value. - SignedAccountId32AsNative, + SignedAccountId32AsNative, // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. - XcmPassthrough, + XcmPassthrough, ); parameter_types! { - // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: Weight = 1_000_000_000; pub const MaxInstructions: u32 = 100; - pub MaxAssetsIntoHolding: u32 = 64; + pub const MaxAssetsIntoHolding: u32 = 64; } match_types! { @@ -200,12 +192,15 @@ pub type Barrier = ( // ^^^ Parent & its unit plurality gets free execution ); -/// XCM weigher type. -pub type XcmWeigher = FixedWeightBounds; +type XcmWeigher = WeightInfoBounds< + crate::weights::xcm::BridgeHubXcmWeight, + RuntimeCall, + MaxInstructions, +>; pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { - type Call = Call; + type RuntimeCall = RuntimeCall; type XcmSender = XcmRouter; type AssetTransactor = LocalAssetTransactor; type OriginConverter = XcmOriginToTransactDispatchOrigin; @@ -226,11 +221,11 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = BridgeHubRococoOrBridgeHubWococoSwitchExporter; type UniversalAliases = Nothing; - type CallDispatcher = Call; + type CallDispatcher = RuntimeCall; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. -pub type LocalOriginToLocation = SignedToAccountId32; +pub type LocalOriginToLocation = SignedToAccountId32; /// The means for routing XCM messages which are not for local execution into the right message /// queues. @@ -242,17 +237,17 @@ pub type XcmRouter = ( ); impl pallet_xcm::Config for Runtime { - type Event = Event; - type SendXcmOrigin = EnsureXcmOrigin; + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; + type ExecuteXcmOrigin = EnsureXcmOrigin; type XcmExecuteFilter = Everything; type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; type Weigher = XcmWeigher; - type Origin = Origin; - type Call = Call; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; type Currency = Balances; @@ -264,7 +259,7 @@ impl pallet_xcm::Config for Runtime { } impl cumulus_pallet_xcm::Config for Runtime { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; } @@ -276,15 +271,16 @@ impl ExportXcm for BridgeHubRococoOrBridgeHubWococoSwitchExporter { fn validate( network: NetworkId, channel: u32, + universal_source: &mut Option, destination: &mut Option, message: &mut Option>, ) -> SendResult { match network { Rococo => - ToBridgeHubRococoHaulBlobExporter::validate(network, channel, destination, message) + ToBridgeHubRococoHaulBlobExporter::validate(network, channel, universal_source, destination, message) .map(|result| ((Rococo, result.0), result.1)), Wococo => - ToBridgeHubWococoHaulBlobExporter::validate(network, channel, destination, message) + ToBridgeHubWococoHaulBlobExporter::validate(network, channel, universal_source, destination, message) .map(|result| ((Wococo, result.0), result.1)), _ => unimplemented!("Unsupported network: {:?}", network), } diff --git a/polkadot-parachain/src/command.rs b/polkadot-parachain/src/command.rs index 2845d2b2ec7..575db58224c 100644 --- a/polkadot-parachain/src/command.rs +++ b/polkadot-parachain/src/command.rs @@ -37,8 +37,6 @@ use sp_core::hexdisplay::HexDisplay; use sp_runtime::traits::{AccountIdConversion, Block as BlockT}; use std::{net::SocketAddr, path::PathBuf}; -use crate::chain_spec::bridge_hubs::BridgeHubRuntimeType; - /// Helper enum that is used for better distinction of different parachain/runtime configuration /// (it is based/calculated on ChainSpec's ID attribute) #[derive(Debug, PartialEq, Default)] From ded04c2c5a417a7b7b5b750abd1938b22ab7a8be Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 15 Nov 2022 15:24:17 +0900 Subject: [PATCH 085/263] Add 10 message processing limit to DMP queue --- pallets/dmp-queue/src/lib.rs | 37 ++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index dc1905166ac..f26db5f593f 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -40,6 +40,11 @@ use xcm::{ const DEFAULT_POV_SIZE: u64 = 64 * 1024; // 64 KB +const DEFAULT_POV_SIZE: u64 = 64 * 1024; // 64 KB +// Maximum amount of messages to process per block. This is a temporary measure until we properly +// account for proof size weights. +const MAX_MESSAGES_PER_BLOCK: u8 = 10; + #[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct ConfigData { /// The maximum amount of weight any individual message may consume. Messages above this weight @@ -200,19 +205,32 @@ pub mod pallet { /// /// Returns the weight consumed by executing messages in the queue. fn service_queue(limit: Weight) -> Weight { - PageIndex::::mutate(|page_index| Self::do_service_queue(limit, page_index)) + let mut messages_processed = 0; + PageIndex::::mutate(|page_index| Self::do_service_queue(limit, page_index, &mut messages_processed)) } /// Exactly equivalent to `service_queue` but expects a mutable `page_index` to be passed /// in and any changes stored. - fn do_service_queue(limit: Weight, page_index: &mut PageIndexData) -> Weight { + fn do_service_queue( + limit: Weight, + page_index: &mut PageIndexData, + messages_processed: &mut u8, + ) -> Weight { let mut used = Weight::zero(); - while page_index.begin_used < page_index.end_used { + 'page: while page_index.begin_used < page_index.end_used { let page = Pages::::take(page_index.begin_used); for (i, &(sent_at, ref data)) in page.iter().enumerate() { + if *messages_processed >= MAX_MESSAGES_PER_BLOCK { + // Exceeded block message limit - put the remaining messages back and bail + Pages::::insert(page_index.begin_used, &page[i..]); + return used + } match Self::try_service_message(limit.saturating_sub(used), sent_at, &data[..]) { - Ok(w) => used += w, + Ok(w) => { + used += w; + *messages_processed += 1; + }, Err(..) => { // Too much weight needed - put the remaining messages back and bail Pages::::insert(page_index.begin_used, &page[i..]); @@ -280,11 +298,12 @@ pub mod pallet { iter: impl Iterator)>, limit: Weight, ) -> Weight { + let mut messages_processed = 0; let mut page_index = PageIndex::::get(); let config = Configuration::::get(); // First try to use `max_weight` to service the current queue. - let mut used = Self::do_service_queue(limit, &mut page_index); + let mut used = Self::do_service_queue(limit, &mut page_index, &mut messages_processed); // Then if the queue is empty, use the weight remaining to service the incoming messages // and once we run out of weight, place them in the queue. @@ -297,11 +316,17 @@ pub mod pallet { }; for (i, (sent_at, data)) in iter.enumerate() { + if messages_processed >= MAX_MESSAGES_PER_BLOCK { + break + } if maybe_enqueue_page.is_none() { // We're not currently enqueuing - try to execute inline. let remaining_weight = limit.saturating_sub(used); match Self::try_service_message(remaining_weight, sent_at, &data[..]) { - Ok(consumed) => used += consumed, + Ok(consumed) => { + used += consumed; + messages_processed += 1; + }, Err((message_id, required_weight)) => // Too much weight required right now. { From f9fc7c612f548691801741c643d975672b731b8c Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 15 Nov 2022 15:53:13 +0900 Subject: [PATCH 086/263] Add 10 message limit to XCMP queue --- pallets/dmp-queue/src/lib.rs | 10 ++++----- pallets/xcmp-queue/src/lib.rs | 22 +++++++++++++++---- .../collectives-polkadot/src/xcm_config.rs | 7 +++--- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index f26db5f593f..8069a962139 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -39,10 +39,8 @@ use xcm::{ }; const DEFAULT_POV_SIZE: u64 = 64 * 1024; // 64 KB - -const DEFAULT_POV_SIZE: u64 = 64 * 1024; // 64 KB -// Maximum amount of messages to process per block. This is a temporary measure until we properly -// account for proof size weights. + // Maximum amount of messages to process per block. This is a temporary measure until we properly + // account for proof size weights. const MAX_MESSAGES_PER_BLOCK: u8 = 10; #[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] @@ -206,7 +204,9 @@ pub mod pallet { /// Returns the weight consumed by executing messages in the queue. fn service_queue(limit: Weight) -> Weight { let mut messages_processed = 0; - PageIndex::::mutate(|page_index| Self::do_service_queue(limit, page_index, &mut messages_processed)) + PageIndex::::mutate(|page_index| { + Self::do_service_queue(limit, page_index, &mut messages_processed) + }) } /// Exactly equivalent to `service_queue` but expects a mutable `page_index` to be passed diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index d430604a822..d803b9f0bad 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -68,6 +68,9 @@ pub type OverweightIndex = u64; const LOG_TARGET: &str = "xcmp_queue"; const DEFAULT_POV_SIZE: u64 = 64 * 1024; // 64 KB + // Maximum amount of messages to process per block. This is a temporary measure until we properly + // account for proof size weights. +const MAX_MESSAGES_PER_BLOCK: u8 = 10; #[frame_support::pallet] pub mod pallet { @@ -658,6 +661,7 @@ impl Pallet { fn process_xcmp_message( sender: ParaId, (sent_at, format): (RelayBlockNumber, XcmpMessageFormat), + messages_processed: &mut u8, max_weight: Weight, max_individual_weight: Weight, ) -> (Weight, bool) { @@ -667,7 +671,9 @@ impl Pallet { let mut weight_used = Weight::zero(); match format { XcmpMessageFormat::ConcatenatedVersionedXcm => { - while !remaining_fragments.is_empty() { + while !remaining_fragments.is_empty() && + *messages_processed < MAX_MESSAGES_PER_BLOCK + { last_remaining_fragments = remaining_fragments; if let Ok(xcm) = VersionedXcm::::decode_with_depth_limit( MAX_XCM_DECODE_DEPTH, @@ -675,7 +681,10 @@ impl Pallet { ) { let weight = max_weight - weight_used; match Self::handle_xcm_message(sender, sent_at, xcm, weight) { - Ok(used) => weight_used = weight_used.saturating_add(used), + Ok(used) => { + weight_used = weight_used.saturating_add(used); + *messages_processed += 1; + }, Err(XcmError::WeightLimitReached(required)) if required > max_individual_weight.ref_time() => { @@ -723,7 +732,10 @@ impl Pallet { if let Ok(blob) = >::decode(&mut remaining_fragments) { let weight = max_weight - weight_used; match Self::handle_blob_message(sender, sent_at, blob, weight) { - Ok(used) => weight_used = weight_used.saturating_add(used), + Ok(used) => { + weight_used = weight_used.saturating_add(used); + *messages_processed += 1; + }, Err(true) => { // That message didn't get processed this time because of being // too heavy. We leave it around for next time and bail. @@ -799,6 +811,7 @@ impl Pallet { /// further. fn service_xcmp_queue(max_weight: Weight) -> Weight { let suspended = QueueSuspended::::get(); + let mut messages_processed = 0; let mut status = >::get(); // <- sorted. if status.is_empty() { @@ -827,7 +840,8 @@ impl Pallet { let mut shuffle_index = 0; while shuffle_index < shuffled.len() && - max_weight.saturating_sub(weight_used).all_gte(threshold_weight) + max_weight.saturating_sub(weight_used).all_gte(threshold_weight) && + messages_processed < MAX_MESSAGES_PER_BLOCK { let index = shuffled[shuffle_index]; let sender = status[index].sender; diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index 2cb82656c37..fb0bd3b4195 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -31,10 +31,9 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FixedWeightBounds, IsConcrete, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - UsingComponents, + FixedWeightBounds, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::XcmExecutor; From 61438f597e5dde5171d7017a8759001e009c9e3e Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 16 Nov 2022 13:27:16 +0900 Subject: [PATCH 087/263] Always increment the message_processed count whenever a message is processed --- pallets/dmp-queue/src/lib.rs | 12 ++++-------- pallets/xcmp-queue/src/lib.rs | 12 ++++-------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 8069a962139..df2edeeb281 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -225,12 +225,10 @@ pub mod pallet { Pages::::insert(page_index.begin_used, &page[i..]); return used } + *messages_processed += 1; match Self::try_service_message(limit.saturating_sub(used), sent_at, &data[..]) { - Ok(w) => { - used += w; - *messages_processed += 1; - }, + Ok(w) => used += w, Err(..) => { // Too much weight needed - put the remaining messages back and bail Pages::::insert(page_index.begin_used, &page[i..]); @@ -322,11 +320,9 @@ pub mod pallet { if maybe_enqueue_page.is_none() { // We're not currently enqueuing - try to execute inline. let remaining_weight = limit.saturating_sub(used); + messages_processed += 1; match Self::try_service_message(remaining_weight, sent_at, &data[..]) { - Ok(consumed) => { - used += consumed; - messages_processed += 1; - }, + Ok(consumed) => used += consumed, Err((message_id, required_weight)) => // Too much weight required right now. { diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index d803b9f0bad..93b5e2e5721 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -680,11 +680,9 @@ impl Pallet { &mut remaining_fragments, ) { let weight = max_weight - weight_used; + *messages_processed += 1; match Self::handle_xcm_message(sender, sent_at, xcm, weight) { - Ok(used) => { - weight_used = weight_used.saturating_add(used); - *messages_processed += 1; - }, + Ok(used) => weight_used = weight_used.saturating_add(used), Err(XcmError::WeightLimitReached(required)) if required > max_individual_weight.ref_time() => { @@ -731,11 +729,9 @@ impl Pallet { if let Ok(blob) = >::decode(&mut remaining_fragments) { let weight = max_weight - weight_used; + *messages_processed += 1; match Self::handle_blob_message(sender, sent_at, blob, weight) { - Ok(used) => { - weight_used = weight_used.saturating_add(used); - *messages_processed += 1; - }, + Ok(used) => weight_used = weight_used.saturating_add(used), Err(true) => { // That message didn't get processed this time because of being // too heavy. We leave it around for next time and bail. From d1f21d7f8b7a8e3fe35ac71d8259e3e267cd7c0a Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 16 Nov 2022 13:29:10 +0900 Subject: [PATCH 088/263] Fix formatting --- pallets/dmp-queue/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index df2edeeb281..70706e793f8 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -39,8 +39,9 @@ use xcm::{ }; const DEFAULT_POV_SIZE: u64 = 64 * 1024; // 64 KB - // Maximum amount of messages to process per block. This is a temporary measure until we properly - // account for proof size weights. + +// Maximum amount of messages to process per block. This is a temporary measure until we properly +// account for proof size weights. const MAX_MESSAGES_PER_BLOCK: u8 = 10; #[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] From 03c318c297199ae38e57b0694d5533ff4cc0e013 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 16 Nov 2022 15:56:26 +0900 Subject: [PATCH 089/263] Set an upper limit to the overweight message DMP queue --- pallets/dmp-queue/src/lib.rs | 15 ++++++++++++--- pallets/dmp-queue/src/migration.rs | 19 ++++++++++++++++++- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 70706e793f8..98ce03b9693 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -43,6 +43,8 @@ const DEFAULT_POV_SIZE: u64 = 64 * 1024; // 64 KB // Maximum amount of messages to process per block. This is a temporary measure until we properly // account for proof size weights. const MAX_MESSAGES_PER_BLOCK: u8 = 10; +// Maximum amount of messages that can exist in the overweight queue at any given time. +const MAX_OVERWEIGHT_MESSAGES: u32 = 1000; #[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct ConfigData { @@ -123,8 +125,13 @@ pub mod pallet { /// The overweight messages. #[pallet::storage] - pub(super) type Overweight = - StorageMap<_, Blake2_128Concat, OverweightIndex, (RelayBlockNumber, Vec), OptionQuery>; + pub(super) type Overweight = CountedStorageMap< + _, + Blake2_128Concat, + OverweightIndex, + (RelayBlockNumber, Vec), + OptionQuery, + >; #[pallet::error] pub enum Error { @@ -327,7 +334,9 @@ pub mod pallet { Err((message_id, required_weight)) => // Too much weight required right now. { - if required_weight.any_gt(config.max_individual) { + let is_under_limit = Overweight::::count() < MAX_OVERWEIGHT_MESSAGES; + used.saturating_accrue(T::DbWeight::get().reads(1)); + if required_weight.any_gt(config.max_individual) && is_under_limit { // overweight - add to overweight queue and continue with // message execution. let overweight_index = page_index.overweight_count; diff --git a/pallets/dmp-queue/src/migration.rs b/pallets/dmp-queue/src/migration.rs index 8ecb9988d50..1257508af2d 100644 --- a/pallets/dmp-queue/src/migration.rs +++ b/pallets/dmp-queue/src/migration.rs @@ -33,8 +33,15 @@ pub fn migrate_to_latest() -> Weight { let mut weight = T::DbWeight::get().reads(1); if StorageVersion::get::>() == 0 { - weight += migrate_to_v1::(); + weight.saturating_accrue(migrate_to_v1::()); StorageVersion::new(1).put::>(); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + } + + if StorageVersion::get::>() == 1 { + weight.saturating_accrue(migrate_to_v2::()); + StorageVersion::new(2).put::>(); + weight.saturating_accrue(T::DbWeight::get().writes(1)); } weight @@ -78,6 +85,16 @@ pub fn migrate_to_v1() -> Weight { T::DbWeight::get().reads_writes(1, 1) } +/// Migrates `Overweight` so that it initializes the storage map's counter. +/// +/// NOTE: Only use this function if you know what you're doing. Default to using +/// `migrate_to_latest`. +pub fn migrate_to_v2() -> Weight { + let overweight_messages = as Store>::Overweight::initialize_counter() as u64; + + T::DbWeight::get().reads_writes(overweight_messages, 1) +} + #[cfg(test)] mod tests { use super::*; From 953aaae4d9d6e4f84de8ddc0b0b157ccda961ec4 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 16 Nov 2022 16:11:36 +0900 Subject: [PATCH 090/263] Add upper limit to XCMP overweight message queue --- pallets/xcmp-queue/src/lib.rs | 44 ++++++++++++++++++----------- pallets/xcmp-queue/src/migration.rs | 15 +++++++++- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index 93b5e2e5721..6bd7ee31827 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -68,9 +68,12 @@ pub type OverweightIndex = u64; const LOG_TARGET: &str = "xcmp_queue"; const DEFAULT_POV_SIZE: u64 = 64 * 1024; // 64 KB - // Maximum amount of messages to process per block. This is a temporary measure until we properly - // account for proof size weights. + +// Maximum amount of messages to process per block. This is a temporary measure until we properly +// account for proof size weights. const MAX_MESSAGES_PER_BLOCK: u8 = 10; +// Maximum amount of messages that can exist in the overweight queue at any given time. +const MAX_OVERWEIGHT_MESSAGES: u32 = 1000; #[frame_support::pallet] pub mod pallet { @@ -366,7 +369,7 @@ pub mod pallet { /// `service_overweight`. #[pallet::storage] pub(super) type Overweight = - StorageMap<_, Twox64Concat, OverweightIndex, (ParaId, RelayBlockNumber, Vec)>; + CountedStorageMap<_, Twox64Concat, OverweightIndex, (ParaId, RelayBlockNumber, Vec)>; /// The number of overweight messages ever recorded in `Overweight`. Also doubles as the next /// available free overweight index. @@ -686,20 +689,27 @@ impl Pallet { Err(XcmError::WeightLimitReached(required)) if required > max_individual_weight.ref_time() => { - // overweight - add to overweight queue and continue with message - // execution consuming the message. - let msg_len = last_remaining_fragments - .len() - .saturating_sub(remaining_fragments.len()); - let overweight_xcm = last_remaining_fragments[..msg_len].to_vec(); - let index = Self::stash_overweight(sender, sent_at, overweight_xcm); - let e = Event::OverweightEnqueued { - sender, - sent_at, - index, - required: Weight::from_ref_time(required), - }; - Self::deposit_event(e); + let is_under_limit = + Overweight::::count() < MAX_OVERWEIGHT_MESSAGES; + weight_used.saturating_accrue(T::DbWeight::get().reads(1)); + if is_under_limit { + // overweight - add to overweight queue and continue with message + // execution consuming the message. + let msg_len = last_remaining_fragments + .len() + .saturating_sub(remaining_fragments.len()); + let overweight_xcm = + last_remaining_fragments[..msg_len].to_vec(); + let index = + Self::stash_overweight(sender, sent_at, overweight_xcm); + let e = Event::OverweightEnqueued { + sender, + sent_at, + index, + required: Weight::from_ref_time(required), + }; + Self::deposit_event(e); + } }, Err(XcmError::WeightLimitReached(required)) if required <= max_weight.ref_time() => diff --git a/pallets/xcmp-queue/src/migration.rs b/pallets/xcmp-queue/src/migration.rs index 94479fce8f5..86f6a2a0ec2 100644 --- a/pallets/xcmp-queue/src/migration.rs +++ b/pallets/xcmp-queue/src/migration.rs @@ -33,8 +33,15 @@ pub fn migrate_to_latest() -> Weight { let mut weight = T::DbWeight::get().reads(1); if StorageVersion::get::>() == 1 { - weight += migrate_to_v2::(); + weight.saturating_accrue(migrate_to_v2::()); StorageVersion::new(2).put::>(); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + } + + if StorageVersion::get::>() == 2 { + weight.saturating_accrue(migrate_to_v3::()); + StorageVersion::new(3).put::>(); + weight.saturating_accrue(T::DbWeight::get().writes(1)); } weight @@ -98,6 +105,12 @@ pub fn migrate_to_v2() -> Weight { T::DbWeight::get().reads_writes(1, 1) } +pub fn migrate_to_v3() -> Weight { + let overweight_messages = as Store>::Overweight::initialize_counter() as u64; + + T::DbWeight::get().reads_writes(overweight_messages, 1) +} + #[cfg(test)] mod tests { use super::*; From bc1f1d44d39cd14495362baaaa4e92c6c517ad0c Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 14 Nov 2022 15:58:28 +0100 Subject: [PATCH 091/263] Fixes - WeigthToFee + fmt --- .../src/bridge_common_config.rs | 22 +++--- .../src/bridge_hub_rococo_config.rs | 20 +++--- .../src/bridge_hub_wococo_config.rs | 12 +++- .../bridge-hub-rococo/src/constants.rs | 8 +-- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 70 +++++++------------ .../bridge-hub-rococo/src/weights/mod.rs | 2 +- .../bridge-hub-rococo/src/xcm_config.rs | 34 ++++++--- 7 files changed, 84 insertions(+), 84 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 85414d09bef..94c0d1660be 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -42,11 +42,8 @@ pub struct XcmBlobMessageDispatch, } -impl< - SourceBridgeHubChain: Chain, - TargetBridgeHubChain: Chain, - BlobDispatcher: DispatchBlob, - > MessageDispatch, BalanceOf> +impl + MessageDispatch, BalanceOf> for XcmBlobMessageDispatch { type DispatchPayload = XcmAsPlainPayload; @@ -79,13 +76,16 @@ impl< let dispatch_result = match BlobDispatcher::dispatch_blob(payload) { Ok(_) => true, Err(e) => { - let e= match e { + let e = match e { DispatchBlobError::Unbridgable => "DispatchBlobError::Unbridgable", DispatchBlobError::InvalidEncoding => "DispatchBlobError::InvalidEncoding", - DispatchBlobError::UnsupportedLocationVersion => "DispatchBlobError::UnsupportedLocationVersion", - DispatchBlobError::UnsupportedXcmVersion => "DispatchBlobError::UnsupportedXcmVersion", + DispatchBlobError::UnsupportedLocationVersion => + "DispatchBlobError::UnsupportedLocationVersion", + DispatchBlobError::UnsupportedXcmVersion => + "DispatchBlobError::UnsupportedXcmVersion", DispatchBlobError::RoutingError => "DispatchBlobError::RoutingError", - DispatchBlobError::NonUniversalDestination => "DispatchBlobError::NonUniversalDestination", + DispatchBlobError::NonUniversalDestination => + "DispatchBlobError::NonUniversalDestination", DispatchBlobError::WrongGlobal => "DispatchBlobError::WrongGlobal", }; log::error!( @@ -142,9 +142,7 @@ impl HaulBlob for XcmBlobHaulerAdapter { let hash = (lane, artifacts.nonce).using_encoded(sp_io::hashing::blake2_256); hash }) - .map_err(|e| { - e - }); + .map_err(|e| e); log::info!(target: "runtime::bridge-hub", "haul_blob result: {:?} on lane: {:?}", result, lane); result.expect("failed to process: TODO:check-parameter - wait for origin/gav-xcm-v3, there is a comment about handliing errors for HaulBlob"); } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index 475adf13100..cc3aef6aa63 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -14,7 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::{ParachainInfo, Runtime, WithBridgeHubWococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, XcmRouter}; +use crate::{ + ParachainInfo, Runtime, WithBridgeHubWococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, + XcmBlobHaulerAdapter, XcmRouter, +}; use bp_messages::{ source_chain::TargetHeaderChain, target_chain::{ProvedMessages, SourceHeaderChain}, @@ -54,8 +57,11 @@ pub type OnBridgeHubRococoBlobDispatcher = BridgeBlobDispatcher; /// Export XCM messages to be relayed to the otherside -pub type ToBridgeHubWococoHaulBlobExporter = - HaulBlobExporter, WococoGlobalConsensusNetwork, ()>; +pub type ToBridgeHubWococoHaulBlobExporter = HaulBlobExporter< + XcmBlobHaulerAdapter, + WococoGlobalConsensusNetwork, + (), +>; pub struct ToBridgeHubWococoXcmBlobHauler; pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO: LaneId = [0, 0, 0, 2]; impl XcmBlobHauler for ToBridgeHubWococoXcmBlobHauler { @@ -186,9 +192,7 @@ impl messages::BridgedChainWithMessages for BridgeHubWococo { } } - fn transaction_payment( - transaction: MessageTransaction, - ) -> messages::BalanceOf { + fn transaction_payment(transaction: MessageTransaction) -> messages::BalanceOf { log::info!( "[BridgeHubWococo::BridgedChainWithMessages] transaction_payment (returns 0 balance, TODO: fix) - transaction: {:?}", transaction @@ -232,9 +236,7 @@ impl ThisChainWithMessages for BridgeHubRococo { MessageNonce::MAX / 2 } - fn transaction_payment( - transaction: MessageTransaction, - ) -> messages::BalanceOf { + fn transaction_payment(transaction: MessageTransaction) -> messages::BalanceOf { log::info!( "[BridgeHubRococo::ThisChainWithMessages] transaction_payment (returns 0 balance, TODO: fix) - transaction: {:?}", transaction diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 99dd24d6a29..a4039e1b944 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -14,7 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::{ParachainInfo, Runtime, WithBridgeHubRococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, XcmRouter}; +use crate::{ + ParachainInfo, Runtime, WithBridgeHubRococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, + XcmBlobHaulerAdapter, XcmRouter, +}; use bp_messages::{ source_chain::TargetHeaderChain, target_chain::{ProvedMessages, SourceHeaderChain}, @@ -54,8 +57,11 @@ pub type OnBridgeHubWococoBlobDispatcher = BridgeBlobDispatcher; /// Export XCM messages to be relayed to the otherside -pub type ToBridgeHubRococoHaulBlobExporter = - HaulBlobExporter, RococoGlobalConsensusNetwork, ()>; +pub type ToBridgeHubRococoHaulBlobExporter = HaulBlobExporter< + XcmBlobHaulerAdapter, + RococoGlobalConsensusNetwork, + (), +>; pub struct ToBridgeHubRococoXcmBlobHauler; pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO: LaneId = [0, 0, 0, 1]; impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/constants.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/constants.rs index 4352e3ef554..641ae6dfe43 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/constants.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/constants.rs @@ -39,8 +39,8 @@ pub mod fee { /// node's balance type. /// /// This should typically create a mapping between the following ranges: - /// - [0, MAXIMUM_BLOCK_WEIGHT] - /// - [Balance::min, Balance::max] + /// - `[0, MAXIMUM_BLOCK_WEIGHT]` + /// - `[Balance::min, Balance::max]` /// /// Yet, it can be used for any other sort of change to weight-fee. Some examples being: /// - Setting it to `0` will essentially disable the weight fee. @@ -49,8 +49,8 @@ pub mod fee { impl WeightToFeePolynomial for WeightToFee { type Balance = Balance; fn polynomial() -> WeightToFeeCoefficients { - // in Polkadot, extrinsic base weight (smallest non-zero weight) is mapped to 1/10 CENT: - // in Statemint, we map to 1/10 of that, or 1/100 CENT + // in Rococo, extrinsic base weight (smallest non-zero weight) is mapped to 1/10 CENT: + // in BridgeHub, we map to 1/10 of that, or 1/100 CENT let p = super::currency::CENTS; let q = 100 * Balance::from(ExtrinsicBaseWeight::get().ref_time()); smallvec![WeightToFeeCoefficient { diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index d154f9721d7..10a31c9dec6 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -29,11 +29,10 @@ pub mod constants; mod weights; pub mod xcm_config; -use codec::Decode; use bridge_common_config::*; +use codec::Decode; use constants::currency::*; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; -use smallvec::smallvec; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ @@ -49,13 +48,11 @@ use sp_version::NativeVersion; use sp_version::RuntimeVersion; use frame_support::{ - construct_runtime, parameter_types, + construct_runtime, dispatch::DispatchClass, + parameter_types, traits::Everything, - weights::{ - ConstantMultiplier, Weight, - WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, - }, + weights::{ConstantMultiplier, Weight}, PalletId, }; use frame_system::{ @@ -77,10 +74,12 @@ use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use crate::{ bridge_hub_rococo_config::OnBridgeHubRococoBlobDispatcher, - bridge_hub_wococo_config::OnBridgeHubWococoBlobDispatcher, + bridge_hub_wococo_config::OnBridgeHubWococoBlobDispatcher, constants::fee::WeightToFee, xcm_config::XcmRouter, }; -use parachains_common::{AccountId, Signature, AVERAGE_ON_INITIALIZE_RATIO, NORMAL_DISPATCH_RATIO, MAXIMUM_BLOCK_WEIGHT}; +use parachains_common::{ + AccountId, Signature, AVERAGE_ON_INITIALIZE_RATIO, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, +}; use xcm::latest::prelude::BodyId; use xcm_executor::XcmExecutor; @@ -126,7 +125,8 @@ pub type SignedExtra = ( ); /// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; /// Extrinsic type that has already been checked. pub type CheckedExtrinsic = generic::CheckedExtrinsic; @@ -140,33 +140,6 @@ pub type Executive = frame_executive::Executive< AllPalletsWithSystem, >; -/// Handles converting a weight scalar to a fee value, based on the scale and granularity of the -/// node's balance type. -/// -/// This should typically create a mapping between the following ranges: -/// - `[0, MAXIMUM_BLOCK_WEIGHT]` -/// - `[Balance::min, Balance::max]` -/// -/// Yet, it can be used for any other sort of change to weight-fee. Some examples being: -/// - Setting it to `0` will essentially disable the weight fee. -/// - Setting it to `1` will cause the literal `#[weight = x]` values to be charged. -pub struct WeightToFee; -impl WeightToFeePolynomial for WeightToFee { - type Balance = Balance; - fn polynomial() -> WeightToFeeCoefficients { - // in Rococo, extrinsic base weight (smallest non-zero weight) is mapped to 1 MILLIUNIT: - // in our template, we map to 1/10 of that, or 1/10 MILLIUNIT - let p = MILLIUNIT / 10; - let q = 100 * Balance::from(ExtrinsicBaseWeight::get().ref_time()); - smallvec![WeightToFeeCoefficient { - degree: 1, - negative: false, - coeff_frac: Perbill::from_rational(p % q, q), - coeff_integer: p / q, - }] - } -} - /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats /// of data like extrinsics, allowing for them to continue syncing the network through upgrades @@ -226,7 +199,6 @@ pub const UNIT: Balance = 1_000_000_000_000; pub const MILLIUNIT: Balance = 1_000_000_000; pub const MICROUNIT: Balance = 1_000_000; - /// The version information used to identify this runtime when compiled natively. #[cfg(feature = "std")] pub fn native_version() -> NativeVersion { @@ -492,8 +464,9 @@ impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_wococo::Wococo; type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; - type MaxBridgedAuthorities = frame_support::traits::ConstU32<{bp_wococo::MAX_AUTHORITIES_COUNT}>; - type MaxBridgedHeaderSize = frame_support::traits::ConstU32<{bp_wococo::MAX_HEADER_SIZE}>; + type MaxBridgedAuthorities = + frame_support::traits::ConstU32<{ bp_wococo::MAX_AUTHORITIES_COUNT }>; + type MaxBridgedHeaderSize = frame_support::traits::ConstU32<{ bp_wococo::MAX_HEADER_SIZE }>; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } @@ -503,8 +476,9 @@ impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_rococo::Rococo; type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; - type MaxBridgedAuthorities = frame_support::traits::ConstU32<{bp_rococo::MAX_AUTHORITIES_COUNT}>; - type MaxBridgedHeaderSize = frame_support::traits::ConstU32<{bp_rococo::MAX_HEADER_SIZE}>; + type MaxBridgedAuthorities = + frame_support::traits::ConstU32<{ bp_rococo::MAX_AUTHORITIES_COUNT }>; + type MaxBridgedHeaderSize = frame_support::traits::ConstU32<{ bp_rococo::MAX_HEADER_SIZE }>; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } @@ -525,7 +499,8 @@ impl pallet_bridge_parachains::Config for Runtime type ParasPalletName = WococoBridgeParachainPalletName; type TrackedParachains = Everything; type HeadsToKeep = ParachainHeadsToKeep; - type MaxParaHeadSize = frame_support::traits::ConstU32<{bp_wococo::MAX_NESTED_PARACHAIN_HEAD_SIZE}>; + type MaxParaHeadSize = + frame_support::traits::ConstU32<{ bp_wococo::MAX_NESTED_PARACHAIN_HEAD_SIZE }>; } /// Add parachain bridge pallet to track Rococo bridge hub parachain @@ -537,7 +512,8 @@ impl pallet_bridge_parachains::Config for Runtime type ParasPalletName = RococoBridgeParachainPalletName; type TrackedParachains = Everything; type HeadsToKeep = ParachainHeadsToKeep; - type MaxParaHeadSize = frame_support::traits::ConstU32<{bp_rococo::MAX_NESTED_PARACHAIN_HEAD_SIZE}>; + type MaxParaHeadSize = + frame_support::traits::ConstU32<{ bp_rococo::MAX_NESTED_PARACHAIN_HEAD_SIZE }>; } /// Add XCM messages support for BrigdeHubRococo to support Rococo->Wococo XCM messages @@ -553,7 +529,8 @@ impl pallet_bridge_messages::Config for Run type MaxUnconfirmedMessagesAtInboundLane = bridge_hub_rococo_config::MaxUnconfirmedMessagesAtInboundLane; - type MaximalOutboundPayloadSize = bridge_hub_rococo_config::ToBridgeHubWococoMaximalOutboundPayloadSize; + type MaximalOutboundPayloadSize = + bridge_hub_rococo_config::ToBridgeHubWococoMaximalOutboundPayloadSize; type OutboundPayload = XcmAsPlainPayload; type OutboundMessageFee = Balance; @@ -588,7 +565,8 @@ impl pallet_bridge_messages::Config for Run type MaxUnconfirmedMessagesAtInboundLane = bridge_hub_wococo_config::MaxUnconfirmedMessagesAtInboundLane; - type MaximalOutboundPayloadSize = bridge_hub_wococo_config::ToBridgeHubRococoMaximalOutboundPayloadSize; + type MaximalOutboundPayloadSize = + bridge_hub_wococo_config::ToBridgeHubRococoMaximalOutboundPayloadSize; type OutboundPayload = XcmAsPlainPayload; type OutboundMessageFee = Balance; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs index 504a4280fcd..8006fb43e5d 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs @@ -20,8 +20,8 @@ pub mod block_weights; pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; -pub mod paritydb_weights; pub mod pallet_balances; +pub mod paritydb_weights; pub mod rocksdb_weights; pub mod xcm; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 20e72bb3040..c825e780716 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -15,8 +15,8 @@ // along with Cumulus. If not, see . use super::{ - AccountId, Balance, Balances, RuntimeCall, RuntimeEvent, RuntimeOrigin, ParachainInfo, ParachainSystem, PolkadotXcm, - Runtime, XcmpQueue, + AccountId, Balance, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, + RuntimeCall, RuntimeEvent, RuntimeOrigin, XcmpQueue, }; use crate::{ bridge_hub_rococo_config::ToBridgeHubWococoHaulBlobExporter, @@ -30,7 +30,13 @@ use frame_support::{ use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use xcm::latest::prelude::*; -use xcm_builder::{AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, IsConcrete, NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds}; +use xcm_builder::{ + AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, + EnsureXcmOrigin, IsConcrete, NativeAsset, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + WeightInfoBounds, +}; use xcm_executor::{traits::ExportXcm, XcmExecutor}; parameter_types! { @@ -276,12 +282,22 @@ impl ExportXcm for BridgeHubRococoOrBridgeHubWococoSwitchExporter { message: &mut Option>, ) -> SendResult { match network { - Rococo => - ToBridgeHubRococoHaulBlobExporter::validate(network, channel, universal_source, destination, message) - .map(|result| ((Rococo, result.0), result.1)), - Wococo => - ToBridgeHubWococoHaulBlobExporter::validate(network, channel, universal_source, destination, message) - .map(|result| ((Wococo, result.0), result.1)), + Rococo => ToBridgeHubRococoHaulBlobExporter::validate( + network, + channel, + universal_source, + destination, + message, + ) + .map(|result| ((Rococo, result.0), result.1)), + Wococo => ToBridgeHubWococoHaulBlobExporter::validate( + network, + channel, + universal_source, + destination, + message, + ) + .map(|result| ((Wococo, result.0), result.1)), _ => unimplemented!("Unsupported network: {:?}", network), } } From c6756cd37fecf0c9379c0f96a9ca70ed4f4971e9 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 16 Nov 2022 23:16:48 +0100 Subject: [PATCH 092/263] BridgeHub fixes --- Cargo.lock | 34 +++++------- Cargo.toml | 6 ++- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 2 - .../src/bridge_hub_rococo_config.rs | 53 +++++++------------ .../src/bridge_hub_wococo_config.rs | 53 +++++++------------ 5 files changed, 55 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 127f1a1c1cd..f0a310083eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -721,6 +721,8 @@ dependencies = [ "bp-polkadot-core", "bp-runtime", "frame-support", + "polkadot-runtime-constants", + "smallvec", "sp-api", "sp-std", ] @@ -754,6 +756,7 @@ dependencies = [ "sp-finality-grandpa", "sp-runtime", "sp-std", + "sp-trie", ] [[package]] @@ -936,7 +939,6 @@ dependencies = [ "bp-bridge-hub-rococo", "bp-bridge-hub-wococo", "bp-messages", - "bp-polkadot-core", "bp-rococo", "bp-runtime", "bp-wococo", @@ -1006,6 +1008,7 @@ dependencies = [ name = "bridge-runtime-common" version = "0.1.0" dependencies = [ + "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot-core", @@ -4383,7 +4386,7 @@ dependencies = [ "pallet-conviction-voting", "pallet-democracy", "pallet-election-provider-multi-phase", - "pallet-election-provider-support-benchmarking 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=master)", + "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-gilt", @@ -6182,7 +6185,7 @@ dependencies = [ "frame-support", "frame-system", "log", - "pallet-election-provider-support-benchmarking 4.0.0-dev (git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges)", + "pallet-election-provider-support-benchmarking", "parity-scale-codec", "rand 0.7.3", "scale-info", @@ -6196,19 +6199,6 @@ dependencies = [ "strum", ] -[[package]] -name = "pallet-election-provider-support-benchmarking" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" -dependencies = [ - "frame-benchmarking", - "frame-election-provider-support", - "frame-system", - "parity-scale-codec", - "sp-npos-elections", - "sp-runtime", -] - [[package]] name = "pallet-election-provider-support-benchmarking" version = "4.0.0-dev" @@ -6435,7 +6425,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-benchmarking" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6482,7 +6472,7 @@ dependencies = [ [[package]] name = "pallet-offences-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6639,7 +6629,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "frame-benchmarking", "frame-support", @@ -8416,7 +8406,7 @@ dependencies = [ "pallet-collective", "pallet-democracy", "pallet-election-provider-multi-phase", - "pallet-election-provider-support-benchmarking 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=master)", + "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-grandpa", @@ -12433,7 +12423,7 @@ dependencies = [ [[package]] name = "substrate-test-client" version = "2.0.1" -source = "git+https://github.com/paritytech/substrate?branch=master#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "array-bytes", "async-trait", @@ -13655,7 +13645,7 @@ dependencies = [ "pallet-collective", "pallet-democracy", "pallet-election-provider-multi-phase", - "pallet-election-provider-support-benchmarking 4.0.0-dev (git+https://github.com/paritytech/substrate?branch=master)", + "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-grandpa", diff --git a/Cargo.toml b/Cargo.toml index 2939fd905eb..925a43226d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,6 @@ members = [ "bridges/modules/relayers", "bridges/modules/shift-session-manager", "bridges/primitives/messages", - "bridges/primitives/polkadot-core", "bridges/primitives/runtime", "bridges/primitives/chain-bridge-hub-rococo", "bridges/primitives/chain-bridge-hub-wococo", @@ -248,6 +247,11 @@ substrate-prometheus-endpoint = { git = "https://github.com/paritytech//substrat substrate-state-trie-migration-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } substrate-wasm-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } try-runtime-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +substrate-test-client = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-election-provider-support-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-nomination-pools-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-offences-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-session-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } [patch."https://github.com/paritytech/polkadot"] kusama-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 3845258ed6e..25bdd1bcbe8 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -73,7 +73,6 @@ parachains-common = { path = "../../../../parachains/common", default-features = bp-bridge-hub-rococo = { path = "../../../../bridges/primitives/chain-bridge-hub-rococo", default-features = false } bp-bridge-hub-wococo = { path = "../../../../bridges/primitives/chain-bridge-hub-wococo", default-features = false } bp-messages = { path = "../../../../bridges/primitives/messages", default-features = false } -bp-polkadot-core = { path = "../../../../bridges/primitives/polkadot-core", default-features = false } bp-runtime = { path = "../../../../bridges/primitives/runtime", default-features = false } bp-rococo = { path = "../../../../bridges/primitives/chain-rococo", default-features = false } bp-wococo = { path = "../../../../bridges/primitives/chain-wococo", default-features = false } @@ -91,7 +90,6 @@ default = [ std = [ "bp-bridge-hub-rococo/std", "bp-bridge-hub-wococo/std", - "bp-polkadot-core/std", "bp-messages/std", "bp-runtime/std", "bp-rococo/std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index cc3aef6aa63..c242285c357 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -15,22 +15,21 @@ // along with Cumulus. If not, see . use crate::{ - ParachainInfo, Runtime, WithBridgeHubWococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, - XcmBlobHaulerAdapter, XcmRouter, + BridgeParachainWococoInstance, ParachainInfo, Runtime, WithBridgeHubWococoMessagesInstance, + XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, XcmRouter, }; use bp_messages::{ source_chain::TargetHeaderChain, target_chain::{ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, MessageNonce, }; -use bp_polkadot_core::parachains::ParaId; -use bp_runtime::{Chain, ChainId}; +use bp_runtime::ChainId; use bridge_runtime_common::{ messages, messages::{ target::FromBridgedChainMessagesProof, BasicConfirmationTransactionEstimation, - BridgedChain, ChainWithMessages, MessageBridge, MessageTransaction, ThisChain, - ThisChainWithMessages, + BridgedChain, MessageBridge, MessageTransaction, ThisChain, ThisChainWithMessages, + UnderlyingChainProvider, }, }; use frame_support::{dispatch::Weight, parameter_types, RuntimeDebug}; @@ -89,6 +88,11 @@ impl MessageBridge for WithBridgeHubWococoMessageBridge { bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME; type ThisChain = BridgeHubRococo; type BridgedChain = BridgeHubWococo; + type BridgedHeaderChain = pallet_bridge_parachains::ParachainHeaders< + Runtime, + BridgeParachainWococoInstance, + bp_bridge_hub_wococo::BridgeHubWococo, + >; fn bridged_balance_to_this_balance( bridged_balance: bridge_runtime_common::messages::BalanceOf>, @@ -112,12 +116,8 @@ pub type ToBridgeHubWococoMaximalOutboundPayloadSize = #[derive(RuntimeDebug, Clone, Copy)] pub struct BridgeHubWococo; -impl ChainWithMessages for BridgeHubWococo { - type Hash = bp_bridge_hub_wococo::Hash; - type AccountId = bp_bridge_hub_wococo::AccountId; - type Signer = bp_bridge_hub_wococo::AccountSigner; - type Signature = bp_bridge_hub_wococo::Signature; - type Balance = bp_bridge_hub_wococo::Balance; +impl UnderlyingChainProvider for BridgeHubWococo { + type Chain = bp_bridge_hub_wococo::BridgeHubWococo; } impl SourceHeaderChain for BridgeHubWococo { @@ -128,12 +128,10 @@ impl SourceHeaderChain for BridgeHubWococo { proof: Self::MessagesProof, messages_count: u32, ) -> Result>, Self::Error> { - bridge_runtime_common::messages::target::verify_messages_proof_from_parachain::< + bridge_runtime_common::messages::target::verify_messages_proof::< WithBridgeHubWococoMessageBridge, - bp_bridge_hub_wococo::Header, - crate::Runtime, - crate::BridgeParachainWococoInstance, - >(ParaId(bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID), proof, messages_count) + >(proof, messages_count) + .map_err(Into::into) } } @@ -149,20 +147,11 @@ impl TargetHeaderChain for BridgeHubWococo fn verify_messages_delivery_proof( proof: Self::MessagesDeliveryProof, ) -> Result<(LaneId, InboundLaneData), Self::Error> { - messages::source::verify_messages_delivery_proof_from_parachain::< - WithBridgeHubWococoMessageBridge, - bp_bridge_hub_wococo::Header, - crate::Runtime, - crate::BridgeParachainWococoInstance, - >(ParaId(bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID), proof) + messages::source::verify_messages_delivery_proof::(proof) } } impl messages::BridgedChainWithMessages for BridgeHubWococo { - fn maximal_extrinsic_size() -> u32 { - bp_bridge_hub_wococo::BridgeHubWococo::max_extrinsic_size() - } - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { true } @@ -206,19 +195,15 @@ impl messages::BridgedChainWithMessages for BridgeHubWococo { #[derive(RuntimeDebug, Clone, Copy)] pub struct BridgeHubRococo; -impl ChainWithMessages for BridgeHubRococo { - type Hash = bp_bridge_hub_rococo::Hash; - type AccountId = bp_bridge_hub_rococo::AccountId; - type Signer = bp_bridge_hub_rococo::AccountSigner; - type Signature = bp_bridge_hub_rococo::Signature; - type Balance = bp_bridge_hub_rococo::Balance; +impl UnderlyingChainProvider for BridgeHubRococo { + type Chain = bp_bridge_hub_rococo::BridgeHubRococo; } impl ThisChainWithMessages for BridgeHubRococo { type RuntimeOrigin = crate::RuntimeOrigin; type RuntimeCall = crate::RuntimeCall; type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< - Self::AccountId, + bp_bridge_hub_rococo::AccountId, { bp_bridge_hub_rococo::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() }, { bp_bridge_hub_wococo::EXTRA_STORAGE_PROOF_SIZE }, { bp_bridge_hub_rococo::TX_EXTRA_BYTES }, diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index a4039e1b944..c90b3dd29d9 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -15,22 +15,21 @@ // along with Cumulus. If not, see . use crate::{ - ParachainInfo, Runtime, WithBridgeHubRococoMessagesInstance, XcmAsPlainPayload, XcmBlobHauler, - XcmBlobHaulerAdapter, XcmRouter, + BridgeParachainRococoInstance, ParachainInfo, Runtime, WithBridgeHubRococoMessagesInstance, + XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, XcmRouter, }; use bp_messages::{ source_chain::TargetHeaderChain, target_chain::{ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, MessageNonce, }; -use bp_polkadot_core::parachains::ParaId; -use bp_runtime::{Chain, ChainId}; +use bp_runtime::ChainId; use bridge_runtime_common::{ messages, messages::{ target::FromBridgedChainMessagesProof, BasicConfirmationTransactionEstimation, - BridgedChain, ChainWithMessages, MessageBridge, MessageTransaction, ThisChain, - ThisChainWithMessages, + BridgedChain, MessageBridge, MessageTransaction, ThisChain, ThisChainWithMessages, + UnderlyingChainProvider, }, }; use frame_support::{dispatch::Weight, parameter_types, RuntimeDebug}; @@ -89,6 +88,11 @@ impl MessageBridge for WithBridgeHubRococoMessageBridge { bp_bridge_hub_wococo::WITH_BRIDGE_HUB_WOCOCO_MESSAGES_PALLET_NAME; type ThisChain = BridgeHubWococo; type BridgedChain = BridgeHubRococo; + type BridgedHeaderChain = pallet_bridge_parachains::ParachainHeaders< + Runtime, + BridgeParachainRococoInstance, + bp_bridge_hub_rococo::BridgeHubRococo, + >; fn bridged_balance_to_this_balance( bridged_balance: bridge_runtime_common::messages::BalanceOf>, @@ -112,12 +116,8 @@ pub type ToBridgeHubRococoMaximalOutboundPayloadSize = #[derive(RuntimeDebug, Clone, Copy)] pub struct BridgeHubRococo; -impl ChainWithMessages for BridgeHubRococo { - type Hash = bp_bridge_hub_rococo::Hash; - type AccountId = bp_bridge_hub_rococo::AccountId; - type Signer = bp_bridge_hub_rococo::AccountSigner; - type Signature = bp_bridge_hub_rococo::Signature; - type Balance = bp_bridge_hub_rococo::Balance; +impl UnderlyingChainProvider for BridgeHubRococo { + type Chain = bp_bridge_hub_rococo::BridgeHubRococo; } impl SourceHeaderChain for BridgeHubRococo { @@ -128,12 +128,10 @@ impl SourceHeaderChain for BridgeHubRococo { proof: Self::MessagesProof, messages_count: u32, ) -> Result>, Self::Error> { - bridge_runtime_common::messages::target::verify_messages_proof_from_parachain::< + bridge_runtime_common::messages::target::verify_messages_proof::< WithBridgeHubRococoMessageBridge, - bp_bridge_hub_rococo::Header, - crate::Runtime, - crate::BridgeParachainRococoInstance, - >(ParaId(bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID), proof, messages_count) + >(proof, messages_count) + .map_err(Into::into) } } @@ -149,20 +147,11 @@ impl TargetHeaderChain for BridgeHubRococo fn verify_messages_delivery_proof( proof: Self::MessagesDeliveryProof, ) -> Result<(LaneId, InboundLaneData), Self::Error> { - messages::source::verify_messages_delivery_proof_from_parachain::< - WithBridgeHubRococoMessageBridge, - bp_bridge_hub_rococo::Header, - crate::Runtime, - crate::BridgeParachainRococoInstance, - >(ParaId(bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID), proof) + messages::source::verify_messages_delivery_proof::(proof) } } impl messages::BridgedChainWithMessages for BridgeHubRococo { - fn maximal_extrinsic_size() -> u32 { - bp_bridge_hub_rococo::BridgeHubRococo::max_extrinsic_size() - } - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { true } @@ -206,19 +195,15 @@ impl messages::BridgedChainWithMessages for BridgeHubRococo { #[derive(RuntimeDebug, Clone, Copy)] pub struct BridgeHubWococo; -impl ChainWithMessages for BridgeHubWococo { - type Hash = bp_bridge_hub_wococo::Hash; - type AccountId = bp_bridge_hub_wococo::AccountId; - type Signer = bp_bridge_hub_wococo::AccountSigner; - type Signature = bp_bridge_hub_wococo::Signature; - type Balance = bp_bridge_hub_wococo::Balance; +impl UnderlyingChainProvider for BridgeHubWococo { + type Chain = bp_bridge_hub_wococo::BridgeHubWococo; } impl ThisChainWithMessages for BridgeHubWococo { type RuntimeOrigin = crate::RuntimeOrigin; type RuntimeCall = crate::RuntimeCall; type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< - Self::AccountId, + bp_bridge_hub_wococo::AccountId, { bp_bridge_hub_wococo::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() }, { bp_bridge_hub_rococo::EXTRA_STORAGE_PROOF_SIZE }, { bp_bridge_hub_wococo::TX_EXTRA_BYTES }, From e19637747b37044c7a334801252039e8e815267d Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 8 Nov 2022 14:57:49 +0100 Subject: [PATCH 093/263] Fix for missing weight for `fn unpaid_execution()` --- parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs | 3 +++ .../statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs | 1 + parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs | 3 +++ .../statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs | 1 + parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs | 3 +++ .../westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs | 1 + 6 files changed, 12 insertions(+) diff --git a/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs b/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs index 0ca8a414e44..93e12e826fd 100644 --- a/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs @@ -241,4 +241,7 @@ impl XcmWeightInfo for StatemineXcmWeight { // XCM Executor does not currently support alias origin operations Weight::MAX.ref_time() } + fn unpaid_execution(_: &WeightLimit, _: &Option) -> XCMWeight { + XcmGeneric::::unpaid_execution().ref_time() + } } diff --git a/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 20fdd109ef7..76c40db7914 100644 --- a/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -185,4 +185,5 @@ impl WeightInfo { pub(crate) fn set_fees_mode() -> Weight { Weight::from_ref_time(6_426_000 as u64) } + pub(crate) fn unpaid_execution() -> Weight { Weight::from_ref_time(3_111_000 as u64) } } diff --git a/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs b/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs index 512be255779..bd6c9bbc097 100644 --- a/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs @@ -241,4 +241,7 @@ impl XcmWeightInfo for StatemintXcmWeight { // XCM Executor does not currently support alias origin operations Weight::MAX.ref_time() } + fn unpaid_execution(_: &WeightLimit, _: &Option) -> XCMWeight { + XcmGeneric::::unpaid_execution().ref_time() + } } diff --git a/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 5e35b4084d0..79066a2b9e5 100644 --- a/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -185,4 +185,5 @@ impl WeightInfo { pub(crate) fn set_fees_mode() -> Weight { Weight::from_ref_time(6_336_000 as u64) } + pub(crate) fn unpaid_execution() -> Weight { Weight::from_ref_time(3_111_000 as u64) } } diff --git a/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs b/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs index 56852eee6ab..8429b74f2ec 100644 --- a/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs @@ -241,4 +241,7 @@ impl XcmWeightInfo for WestmintXcmWeight { // XCM Executor does not currently support alias origin operations Weight::MAX.ref_time() } + fn unpaid_execution(_: &WeightLimit, _: &Option) -> XCMWeight { + XcmGeneric::::unpaid_execution().ref_time() + } } diff --git a/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 18761aa0bf2..e6f01884049 100644 --- a/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -185,4 +185,5 @@ impl WeightInfo { pub(crate) fn set_fees_mode() -> Weight { Weight::from_ref_time(6_392_000 as u64) } + pub(crate) fn unpaid_execution() -> Weight { Weight::from_ref_time(3_111_000 as u64) } } From aea33d16406809c33f1bc8307860ce4149a9a9e3 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Sun, 20 Nov 2022 00:09:03 +0100 Subject: [PATCH 094/263] Fix - usage of `messages_processed` --- pallets/xcmp-queue/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index 6bd7ee31827..2470c5d4128 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -887,6 +887,7 @@ impl Pallet { let (weight_processed, is_empty) = Self::process_xcmp_message( sender, status[index].message_metadata[0], + &mut messages_processed, weight_remaining, xcmp_max_individual_weight, ); From 196ad14ae6536f8edcef109b3aa060df17d684cf Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 22 Nov 2022 17:30:01 +0900 Subject: [PATCH 095/263] Fixes --- pallets/dmp-queue/src/lib.rs | 30 +++++++++++++----------------- pallets/dmp-queue/src/migration.rs | 3 +-- pallets/xcm/src/lib.rs | 8 ++++---- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 98ce03b9693..77d8007478d 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -33,10 +33,7 @@ pub use pallet::*; use scale_info::TypeInfo; use sp_runtime::RuntimeDebug; use sp_std::{convert::TryFrom, prelude::*}; -use xcm::{ - latest::{prelude::*, Weight as XcmWeight}, - VersionedXcm, MAX_XCM_DECODE_DEPTH, -}; +use xcm::{latest::prelude::*, VersionedXcm, MAX_XCM_DECODE_DEPTH}; const DEFAULT_POV_SIZE: u64 = 64 * 1024; // 64 KB @@ -167,17 +164,17 @@ pub mod pallet { /// /// Events: /// - `OverweightServiced`: On success. - #[pallet::weight(Weight::from_ref_time(weight_limit.saturating_add(1_000_000)))] + #[pallet::weight(weight_limit.saturating_add(Weight::from_ref_time(1_000_000)))] pub fn service_overweight( origin: OriginFor, index: OverweightIndex, - weight_limit: XcmWeight, + weight_limit: Weight, ) -> DispatchResultWithPostInfo { T::ExecuteOverweightOrigin::ensure_origin(origin)?; let (sent_at, data) = Overweight::::get(index).ok_or(Error::::Unknown)?; let weight_used = - Self::try_service_message(Weight::from_ref_time(weight_limit), sent_at, &data[..]) + Self::try_service_message(weight_limit, sent_at, &data[..]) .map_err(|_| Error::::OverLimit)?; Overweight::::remove(index); Self::deposit_event(Event::OverweightServiced { overweight_index: index, weight_used }); @@ -281,14 +278,13 @@ pub mod pallet { }, Ok(Ok(x)) => { let outcome = - T::XcmExecutor::execute_xcm(Parent, x, message_id, limit.ref_time()); + T::XcmExecutor::execute_xcm(Parent, x, message_id, limit); match outcome { Outcome::Error(XcmError::WeightLimitReached(required)) => - Err((message_id, Weight::from_ref_time(required))), + Err((message_id, required)), outcome => { - let weight_used = Weight::from_ref_time(outcome.weight_used()); Self::deposit_event(Event::ExecutedDownward { message_id, outcome }); - Ok(weight_used) + Ok(outcome.weight_used()) }, } }, @@ -398,7 +394,7 @@ mod tests { }; use sp_version::RuntimeVersion; use std::cell::RefCell; - use xcm::latest::{MultiLocation, OriginKind, Weight as XCMWeight}; + use xcm::latest::{MultiLocation, OriginKind}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -475,7 +471,7 @@ mod tests { pub enum Weightless {} impl PreparedMessage for Weightless { - fn weight_of(&self) -> XCMWeight { + fn weight_of(&self) -> u64 { unreachable!() } } @@ -492,7 +488,7 @@ mod tests { _origin: impl Into, _: Weightless, _hash: XcmHash, - _weight_limit: XCMWeight, + _weight_limit: u64, ) -> Outcome { unreachable!() } @@ -534,7 +530,7 @@ mod tests { DmpQueue::handle_dmp_messages(iter, limit) } - fn msg(weight: XCMWeight) -> Xcm { + fn msg(weight: u64) -> Xcm { Xcm(vec![Transact { origin_kind: OriginKind::Native, require_weight_at_most: weight, @@ -542,11 +538,11 @@ mod tests { }]) } - fn msg_complete(weight: XCMWeight) -> (Xcm, Outcome) { + fn msg_complete(weight: u64) -> (Xcm, Outcome) { (msg(weight), Outcome::Complete(weight)) } - fn msg_limit_reached(weight: XCMWeight) -> (Xcm, Outcome) { + fn msg_limit_reached(weight: u64) -> (Xcm, Outcome) { (msg(weight), Outcome::Error(XcmError::WeightLimitReached(weight))) } diff --git a/pallets/dmp-queue/src/migration.rs b/pallets/dmp-queue/src/migration.rs index 1257508af2d..2bcfca114bf 100644 --- a/pallets/dmp-queue/src/migration.rs +++ b/pallets/dmp-queue/src/migration.rs @@ -22,7 +22,6 @@ use frame_support::{ traits::StorageVersion, weights::{constants::WEIGHT_PER_MILLIS, Weight}, }; -use xcm::latest::Weight as XcmWeight; /// The current storage version. pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); @@ -53,7 +52,7 @@ mod v0 { #[derive(Decode, Encode, Debug)] pub struct ConfigData { - pub max_individual: XcmWeight, + pub max_individual: u64, } impl Default for ConfigData { diff --git a/pallets/xcm/src/lib.rs b/pallets/xcm/src/lib.rs index bd015dce171..4608ecc46ac 100644 --- a/pallets/xcm/src/lib.rs +++ b/pallets/xcm/src/lib.rs @@ -123,8 +123,8 @@ impl DmpMessageHandler for UnlimitedDmpExecution { Err(_) => Pallet::::deposit_event(Event::InvalidFormat(id)), Ok(Err(())) => Pallet::::deposit_event(Event::UnsupportedVersion(id)), Ok(Ok(x)) => { - let outcome = T::XcmExecutor::execute_xcm(Parent, x, id, limit.ref_time()); - used += Weight::from_ref_time(outcome.weight_used()); + let outcome = T::XcmExecutor::execute_xcm(Parent, x, id, limit); + used = used.saturating_add(outcome.weight_used()); Pallet::::deposit_event(Event::ExecutedDownward(id, outcome)); }, } @@ -158,8 +158,8 @@ impl DmpMessageHandler for LimitAndDropDmpExecution { Ok(Ok(x)) => { let weight_limit = limit.saturating_sub(used); let outcome = - T::XcmExecutor::execute_xcm(Parent, x, id, weight_limit.ref_time()); - used += Weight::from_ref_time(outcome.weight_used()); + T::XcmExecutor::execute_xcm(Parent, x, id, weight_limit); + used = used.saturating_add(outcome.weight_used()); Pallet::::deposit_event(Event::ExecutedDownward(id, outcome)); }, } From 331b21e894d825e09ece623a1c9d91f2cd4caa7a Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 22 Nov 2022 17:30:31 +0900 Subject: [PATCH 096/263] Fixes --- pallets/dmp-queue/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 77d8007478d..10ed7d89479 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -793,31 +793,31 @@ mod tests { assert_eq!(overweights(), vec![0]); assert_noop!( - DmpQueue::service_overweight(RuntimeOrigin::signed(1), 0, 20000), + DmpQueue::service_overweight(RuntimeOrigin::signed(1), 0, Weight::from_parts(20000, 20000)), BadOrigin ); assert_noop!( - DmpQueue::service_overweight(RuntimeOrigin::root(), 1, 20000), + DmpQueue::service_overweight(RuntimeOrigin::root(), 1, Weight::from_parts(20000, 20000)), Error::::Unknown ); assert_noop!( - DmpQueue::service_overweight(RuntimeOrigin::root(), 0, 9999), + DmpQueue::service_overweight(RuntimeOrigin::root(), 0, Weight::from_parts(9999, 9999)), Error::::OverLimit ); assert_eq!(take_trace(), vec![msg_limit_reached(10000)]); - let base_weight = super::Call::::service_overweight { index: 0, weight_limit: 0 } + let base_weight = super::Call::::service_overweight { index: 0, weight_limit: Weight::zero() } .get_dispatch_info() .weight; use frame_support::dispatch::GetDispatchInfo; - let info = DmpQueue::service_overweight(RuntimeOrigin::root(), 0, 20000).unwrap(); + let info = DmpQueue::service_overweight(RuntimeOrigin::root(), 0, Weight::from_parts(20000, 20000)).unwrap(); let actual_weight = info.actual_weight.unwrap(); assert_eq!(actual_weight, base_weight + Weight::from_ref_time(10000)); assert_eq!(take_trace(), vec![msg_complete(10000)]); assert!(overweights().is_empty()); assert_noop!( - DmpQueue::service_overweight(RuntimeOrigin::root(), 0, 20000), + DmpQueue::service_overweight(RuntimeOrigin::root(), 0, Weight::from_parts(20000, 20000)), Error::::Unknown ); }); From b613a35c7910a934970dd32951be249b36fc66a8 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 22 Nov 2022 17:42:29 +0900 Subject: [PATCH 097/263] Fixes --- pallets/dmp-queue/src/lib.rs | 105 +++++++++++++++++------------ pallets/xcm/src/lib.rs | 3 +- parachains/pallets/ping/src/lib.rs | 2 +- 3 files changed, 65 insertions(+), 45 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 10ed7d89479..8833b938abf 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -173,9 +173,8 @@ pub mod pallet { T::ExecuteOverweightOrigin::ensure_origin(origin)?; let (sent_at, data) = Overweight::::get(index).ok_or(Error::::Unknown)?; - let weight_used = - Self::try_service_message(weight_limit, sent_at, &data[..]) - .map_err(|_| Error::::OverLimit)?; + let weight_used = Self::try_service_message(weight_limit, sent_at, &data[..]) + .map_err(|_| Error::::OverLimit)?; Overweight::::remove(index); Self::deposit_event(Event::OverweightServiced { overweight_index: index, weight_used }); Ok(Some(weight_used.saturating_add(Weight::from_ref_time(1_000_000))).into()) @@ -277,14 +276,14 @@ pub mod pallet { Ok(Weight::zero()) }, Ok(Ok(x)) => { - let outcome = - T::XcmExecutor::execute_xcm(Parent, x, message_id, limit); + let outcome = T::XcmExecutor::execute_xcm(Parent, x, message_id, limit); match outcome { Outcome::Error(XcmError::WeightLimitReached(required)) => Err((message_id, required)), outcome => { + let weight_used = outcome.weight_used(); Self::deposit_event(Event::ExecutedDownward { message_id, outcome }); - Ok(outcome.weight_used()) + Ok(weight_used) }, } }, @@ -471,7 +470,7 @@ mod tests { pub enum Weightless {} impl PreparedMessage for Weightless { - fn weight_of(&self) -> u64 { + fn weight_of(&self) -> Weight { unreachable!() } } @@ -488,7 +487,7 @@ mod tests { _origin: impl Into, _: Weightless, _hash: XcmHash, - _weight_limit: u64, + _weight_limit: Weight, ) -> Outcome { unreachable!() } @@ -533,17 +532,17 @@ mod tests { fn msg(weight: u64) -> Xcm { Xcm(vec![Transact { origin_kind: OriginKind::Native, - require_weight_at_most: weight, + require_weight_at_most: Weight::from_parts(weight, weight), call: Vec::new().into(), }]) } fn msg_complete(weight: u64) -> (Xcm, Outcome) { - (msg(weight), Outcome::Complete(weight)) + (msg(weight), Outcome::Complete(Weight::from_ref_time(weight))) } fn msg_limit_reached(weight: u64) -> (Xcm, Outcome) { - (msg(weight), Outcome::Error(XcmError::WeightLimitReached(weight))) + (msg(weight), Outcome::Error(XcmError::WeightLimitReached(Weight::from_ref_time(weight)))) } fn pages_queued() -> PageCounter { @@ -563,7 +562,7 @@ mod tests { #[test] fn basic_setup_works() { new_test_ext().execute_with(|| { - let weight_used = handle_messages(&[], Weight::from_ref_time(1000)); + let weight_used = handle_messages(&[], Weight::from_parts(1000, 1000)); assert_eq!(weight_used, Weight::zero()); assert_eq!(take_trace(), Vec::new()); assert!(queue_is_empty()); @@ -574,7 +573,7 @@ mod tests { fn service_inline_complete_works() { new_test_ext().execute_with(|| { let incoming = vec![msg(1000), msg(1001)]; - let weight_used = handle_messages(&incoming, Weight::from_ref_time(2500)); + let weight_used = handle_messages(&incoming, Weight::from_parts(2500, 2500)); assert_eq!(weight_used, Weight::from_ref_time(2001)); assert_eq!(take_trace(), vec![msg_complete(1000), msg_complete(1001)]); assert!(queue_is_empty()); @@ -586,7 +585,7 @@ mod tests { new_test_ext().execute_with(|| { let enqueued = vec![msg(1000), msg(1001), msg(1002)]; enqueue(&enqueued); - let weight_used = handle_messages(&[], Weight::from_ref_time(2500)); + let weight_used = handle_messages(&[], Weight::from_parts(2500, 2500)); assert_eq!(weight_used, Weight::from_ref_time(2001)); assert_eq!( take_trace(), @@ -599,7 +598,7 @@ mod tests { fn enqueue_works() { new_test_ext().execute_with(|| { let incoming = vec![msg(1000), msg(1001), msg(1002)]; - let weight_used = handle_messages(&incoming, Weight::from_ref_time(999)); + let weight_used = handle_messages(&incoming, Weight::from_parts(999, 999)); assert_eq!(weight_used, Weight::zero()); assert_eq!( PageIndex::::get(), @@ -608,14 +607,14 @@ mod tests { assert_eq!(Pages::::get(0).len(), 3); assert_eq!(take_trace(), vec![msg_limit_reached(1000)]); - let weight_used = handle_messages(&[], Weight::from_ref_time(2500)); + let weight_used = handle_messages(&[], Weight::from_parts(2500, 2500)); assert_eq!(weight_used, Weight::from_ref_time(2001)); assert_eq!( take_trace(), vec![msg_complete(1000), msg_complete(1001), msg_limit_reached(1002),] ); - let weight_used = handle_messages(&[], Weight::from_ref_time(2500)); + let weight_used = handle_messages(&[], Weight::from_parts(2500, 2500)); assert_eq!(weight_used, Weight::from_ref_time(1002)); assert_eq!(take_trace(), vec![msg_complete(1002),]); assert!(queue_is_empty()); @@ -626,13 +625,13 @@ mod tests { fn service_inline_then_enqueue_works() { new_test_ext().execute_with(|| { let incoming = vec![msg(1000), msg(1001), msg(1002)]; - let weight_used = handle_messages(&incoming, Weight::from_ref_time(1500)); + let weight_used = handle_messages(&incoming, Weight::from_parts(1500, 1500)); assert_eq!(weight_used, Weight::from_ref_time(1000)); assert_eq!(pages_queued(), 1); assert_eq!(Pages::::get(0).len(), 2); assert_eq!(take_trace(), vec![msg_complete(1000), msg_limit_reached(1001),]); - let weight_used = handle_messages(&[], Weight::from_ref_time(2500)); + let weight_used = handle_messages(&[], Weight::from_parts(2500, 2500)); assert_eq!(weight_used, Weight::from_ref_time(2003)); assert_eq!(take_trace(), vec![msg_complete(1001), msg_complete(1002),]); assert!(queue_is_empty()); @@ -645,7 +644,7 @@ mod tests { let enqueued = vec![msg(1000), msg(1001)]; let incoming = vec![msg(1002), msg(1003)]; enqueue(&enqueued); - let weight_used = handle_messages(&incoming, Weight::from_ref_time(5000)); + let weight_used = handle_messages(&incoming, Weight::from_parts(5000, 5000)); assert_eq!(weight_used, Weight::from_ref_time(4006)); assert_eq!( take_trace(), @@ -666,18 +665,18 @@ mod tests { let enqueued = vec![msg(1000), msg(10001)]; let incoming = vec![msg(1002), msg(1003)]; enqueue(&enqueued); - let weight_used = handle_messages(&incoming, Weight::from_ref_time(5000)); + let weight_used = handle_messages(&incoming, Weight::from_parts(5000, 5000)); assert_eq!(weight_used, Weight::from_ref_time(1000)); assert_eq!(take_trace(), vec![msg_complete(1000), msg_limit_reached(10001),]); assert_eq!(pages_queued(), 2); // 5000 is not enough to process the 10001 blocker, so nothing happens. - let weight_used = handle_messages(&[], Weight::from_ref_time(5000)); + let weight_used = handle_messages(&[], Weight::from_parts(5000, 5000)); assert_eq!(weight_used, Weight::zero()); assert_eq!(take_trace(), vec![msg_limit_reached(10001),]); // 20000 is now enough to process everything. - let weight_used = handle_messages(&[], Weight::from_ref_time(20000)); + let weight_used = handle_messages(&[], Weight::from_parts(20000, 20000)); assert_eq!(weight_used, Weight::from_ref_time(12006)); assert_eq!( take_trace(), @@ -693,7 +692,7 @@ mod tests { let enqueued = vec![msg(1000), msg(1001)]; let incoming = vec![msg(10002), msg(1003)]; enqueue(&enqueued); - let weight_used = handle_messages(&incoming, Weight::from_ref_time(5000)); + let weight_used = handle_messages(&incoming, Weight::from_parts(5000, 5000)); assert_eq!(weight_used, Weight::from_ref_time(2001)); assert_eq!( take_trace(), @@ -702,7 +701,7 @@ mod tests { assert_eq!(pages_queued(), 1); // 20000 is now enough to process everything. - let weight_used = handle_messages(&[], Weight::from_ref_time(20000)); + let weight_used = handle_messages(&[], Weight::from_parts(20000, 20000)); assert_eq!(weight_used, Weight::from_ref_time(11005)); assert_eq!(take_trace(), vec![msg_complete(10002), msg_complete(1003),]); assert!(queue_is_empty()); @@ -715,7 +714,7 @@ mod tests { let enqueued = vec![msg(1000), msg(1001)]; let incoming = vec![msg(1002), msg(10003)]; enqueue(&enqueued); - let weight_used = handle_messages(&incoming, Weight::from_ref_time(5000)); + let weight_used = handle_messages(&incoming, Weight::from_parts(5000, 5000)); assert_eq!(weight_used, Weight::from_ref_time(3003)); assert_eq!( take_trace(), @@ -729,7 +728,7 @@ mod tests { assert_eq!(pages_queued(), 1); // 20000 is now enough to process everything. - let weight_used = handle_messages(&[], Weight::from_ref_time(20000)); + let weight_used = handle_messages(&[], Weight::from_parts(20000, 20000)); assert_eq!(weight_used, Weight::from_ref_time(10003)); assert_eq!(take_trace(), vec![msg_complete(10003),]); assert!(queue_is_empty()); @@ -741,19 +740,19 @@ mod tests { new_test_ext().execute_with(|| { let enqueued = vec![msg(1000), msg(1001)]; enqueue(&enqueued); - let weight_used = handle_messages(&vec![msg(1002)], Weight::from_ref_time(1500)); + let weight_used = handle_messages(&vec![msg(1002)], Weight::from_parts(1500, 1500)); assert_eq!(weight_used, Weight::from_ref_time(1000)); assert_eq!(take_trace(), vec![msg_complete(1000), msg_limit_reached(1001),]); assert_eq!(pages_queued(), 2); assert_eq!(PageIndex::::get().begin_used, 0); - let weight_used = handle_messages(&vec![msg(1003)], Weight::from_ref_time(1500)); + let weight_used = handle_messages(&vec![msg(1003)], Weight::from_parts(1500, 1500)); assert_eq!(weight_used, Weight::from_ref_time(1001)); assert_eq!(take_trace(), vec![msg_complete(1001), msg_limit_reached(1002),]); assert_eq!(pages_queued(), 2); assert_eq!(PageIndex::::get().begin_used, 1); - let weight_used = handle_messages(&vec![msg(1004)], Weight::from_ref_time(1500)); + let weight_used = handle_messages(&vec![msg(1004)], Weight::from_parts(1500, 1500)); assert_eq!(weight_used, Weight::from_ref_time(1002)); assert_eq!(take_trace(), vec![msg_complete(1002), msg_limit_reached(1003),]); assert_eq!(pages_queued(), 2); @@ -765,10 +764,10 @@ mod tests { fn overweight_should_not_block_queue() { new_test_ext().execute_with(|| { // Set the overweight threshold to 9999. - Configuration::::put(ConfigData { max_individual: Weight::from_ref_time(9999) }); + Configuration::::put(ConfigData { max_individual: Weight::from_parts(9999, 9999) }); let incoming = vec![msg(1000), msg(10001), msg(1002)]; - let weight_used = handle_messages(&incoming, Weight::from_ref_time(2500)); + let weight_used = handle_messages(&incoming, Weight::from_parts(2500, 2500)); assert_eq!(weight_used, Weight::from_ref_time(2002)); assert!(queue_is_empty()); assert_eq!( @@ -784,40 +783,62 @@ mod tests { fn overweights_should_be_manually_executable() { new_test_ext().execute_with(|| { // Set the overweight threshold to 9999. - Configuration::::put(ConfigData { max_individual: Weight::from_ref_time(9999) }); + Configuration::::put(ConfigData { max_individual: Weight::from_parts(9999, 9999) }); let incoming = vec![msg(10000)]; - let weight_used = handle_messages(&incoming, Weight::from_ref_time(2500)); + let weight_used = handle_messages(&incoming, Weight::from_parts(2500, 2500)); assert_eq!(weight_used, Weight::zero()); assert_eq!(take_trace(), vec![msg_limit_reached(10000)]); assert_eq!(overweights(), vec![0]); assert_noop!( - DmpQueue::service_overweight(RuntimeOrigin::signed(1), 0, Weight::from_parts(20000, 20000)), + DmpQueue::service_overweight( + RuntimeOrigin::signed(1), + 0, + Weight::from_parts(20000, 20000) + ), BadOrigin ); assert_noop!( - DmpQueue::service_overweight(RuntimeOrigin::root(), 1, Weight::from_parts(20000, 20000)), + DmpQueue::service_overweight( + RuntimeOrigin::root(), + 1, + Weight::from_parts(20000, 20000) + ), Error::::Unknown ); assert_noop!( - DmpQueue::service_overweight(RuntimeOrigin::root(), 0, Weight::from_parts(9999, 9999)), + DmpQueue::service_overweight( + RuntimeOrigin::root(), + 0, + Weight::from_parts(9999, 9999) + ), Error::::OverLimit ); assert_eq!(take_trace(), vec![msg_limit_reached(10000)]); - let base_weight = super::Call::::service_overweight { index: 0, weight_limit: Weight::zero() } - .get_dispatch_info() - .weight; + let base_weight = + super::Call::::service_overweight { index: 0, weight_limit: Weight::zero() } + .get_dispatch_info() + .weight; use frame_support::dispatch::GetDispatchInfo; - let info = DmpQueue::service_overweight(RuntimeOrigin::root(), 0, Weight::from_parts(20000, 20000)).unwrap(); + let info = DmpQueue::service_overweight( + RuntimeOrigin::root(), + 0, + Weight::from_parts(20000, 20000), + ) + .unwrap(); let actual_weight = info.actual_weight.unwrap(); assert_eq!(actual_weight, base_weight + Weight::from_ref_time(10000)); assert_eq!(take_trace(), vec![msg_complete(10000)]); assert!(overweights().is_empty()); assert_noop!( - DmpQueue::service_overweight(RuntimeOrigin::root(), 0, Weight::from_parts(20000, 20000)), + DmpQueue::service_overweight( + RuntimeOrigin::root(), + 0, + Weight::from_parts(20000, 20000) + ), Error::::Unknown ); }); diff --git a/pallets/xcm/src/lib.rs b/pallets/xcm/src/lib.rs index 4608ecc46ac..1bcfcdcce73 100644 --- a/pallets/xcm/src/lib.rs +++ b/pallets/xcm/src/lib.rs @@ -157,8 +157,7 @@ impl DmpMessageHandler for LimitAndDropDmpExecution { Ok(Err(())) => Pallet::::deposit_event(Event::UnsupportedVersion(id)), Ok(Ok(x)) => { let weight_limit = limit.saturating_sub(used); - let outcome = - T::XcmExecutor::execute_xcm(Parent, x, id, weight_limit); + let outcome = T::XcmExecutor::execute_xcm(Parent, x, id, weight_limit); used = used.saturating_add(outcome.weight_used()); Pallet::::deposit_event(Event::ExecutedDownward(id, outcome)); }, diff --git a/parachains/pallets/ping/src/lib.rs b/parachains/pallets/ping/src/lib.rs index 5d509a86a0a..76009fac5c1 100644 --- a/parachains/pallets/ping/src/lib.rs +++ b/parachains/pallets/ping/src/lib.rs @@ -107,7 +107,7 @@ pub mod pallet { (Parent, Junction::Parachain(para.into())).into(), Xcm(vec![Transact { origin_kind: OriginKind::Native, - require_weight_at_most: 1_000, + require_weight_at_most: Weight::from_parts(1_000, 1_000), call: ::RuntimeCall::from(Call::::ping { seq, payload: payload.clone().to_vec(), From 930b98847283c4c2454751b5ba40839c37d67feb Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 22 Nov 2022 17:46:32 +0900 Subject: [PATCH 098/263] cargo fmt --- pallets/dmp-queue/src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 8833b938abf..68e0f4a7427 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -764,7 +764,9 @@ mod tests { fn overweight_should_not_block_queue() { new_test_ext().execute_with(|| { // Set the overweight threshold to 9999. - Configuration::::put(ConfigData { max_individual: Weight::from_parts(9999, 9999) }); + Configuration::::put(ConfigData { + max_individual: Weight::from_parts(9999, 9999), + }); let incoming = vec![msg(1000), msg(10001), msg(1002)]; let weight_used = handle_messages(&incoming, Weight::from_parts(2500, 2500)); @@ -783,7 +785,9 @@ mod tests { fn overweights_should_be_manually_executable() { new_test_ext().execute_with(|| { // Set the overweight threshold to 9999. - Configuration::::put(ConfigData { max_individual: Weight::from_parts(9999, 9999) }); + Configuration::::put(ConfigData { + max_individual: Weight::from_parts(9999, 9999), + }); let incoming = vec![msg(10000)]; let weight_used = handle_messages(&incoming, Weight::from_parts(2500, 2500)); From cd1452b7a3b4fbb63e25140c3097f5892407a1b1 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 22 Nov 2022 17:51:45 +0900 Subject: [PATCH 099/263] Fixes --- parachains/pallets/ping/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parachains/pallets/ping/src/lib.rs b/parachains/pallets/ping/src/lib.rs index 76009fac5c1..80b731119fb 100644 --- a/parachains/pallets/ping/src/lib.rs +++ b/parachains/pallets/ping/src/lib.rs @@ -203,7 +203,7 @@ pub mod pallet { (Parent, Junction::Parachain(para.into())).into(), Xcm(vec![Transact { origin_kind: OriginKind::Native, - require_weight_at_most: 1_000, + require_weight_at_most: Weight::from_parts(1_000, 1_000), call: ::RuntimeCall::from(Call::::pong { seq, payload: payload.clone(), From a0865d8102f92dfdb40482278687b18ebcbefc37 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 22 Nov 2022 19:07:39 +0900 Subject: [PATCH 100/263] Fixes --- pallets/xcmp-queue/src/lib.rs | 46 +++++++++++------------------ pallets/xcmp-queue/src/migration.rs | 7 ++--- primitives/utility/src/lib.rs | 13 ++------ 3 files changed, 24 insertions(+), 42 deletions(-) diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index 2470c5d4128..09565f490d0 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -55,10 +55,7 @@ use rand_chacha::{ use scale_info::TypeInfo; use sp_runtime::RuntimeDebug; use sp_std::{convert::TryFrom, prelude::*}; -use xcm::{ - latest::{prelude::*, Weight as XcmWeight}, - VersionedXcm, WrapVersion, MAX_XCM_DECODE_DEPTH, -}; +use xcm::{latest::{prelude::*}, VersionedXcm, WrapVersion, MAX_XCM_DECODE_DEPTH}; use xcm_executor::traits::ConvertOrigin; pub use pallet::*; @@ -144,11 +141,11 @@ pub mod pallet { /// /// Events: /// - `OverweightServiced`: On success. - #[pallet::weight((Weight::from_ref_time(weight_limit.saturating_add(1_000_000)), DispatchClass::Operational,))] + #[pallet::weight((weight_limit.saturating_add(Weight::from_ref_time(1_000_000)), DispatchClass::Operational,))] pub fn service_overweight( origin: OriginFor, index: OverweightIndex, - weight_limit: XcmWeight, + weight_limit: Weight, ) -> DispatchResultWithPostInfo { T::ExecuteOverweightOrigin::ensure_origin(origin)?; @@ -160,7 +157,7 @@ pub mod pallet { ) .map_err(|_| Error::::BadXcm)?; let used = - Self::handle_xcm_message(sender, sent_at, xcm, Weight::from_ref_time(weight_limit)) + Self::handle_xcm_message(sender, sent_at, xcm, weight_limit) .map_err(|_| Error::::WeightOverLimit)?; Overweight::::remove(index); Self::deposit_event(Event::OverweightServiced { index, used }); @@ -237,9 +234,9 @@ pub mod pallet { /// - `origin`: Must pass `Root`. /// - `new`: Desired value for `QueueConfigData.threshold_weight` #[pallet::weight((T::WeightInfo::set_config_with_weight(), DispatchClass::Operational,))] - pub fn update_threshold_weight(origin: OriginFor, new: XcmWeight) -> DispatchResult { + pub fn update_threshold_weight(origin: OriginFor, new: Weight) -> DispatchResult { ensure_root(origin)?; - QueueConfig::::mutate(|data| data.threshold_weight = Weight::from_ref_time(new)); + QueueConfig::::mutate(|data| data.threshold_weight = new); Ok(()) } @@ -250,14 +247,9 @@ pub mod pallet { /// - `origin`: Must pass `Root`. /// - `new`: Desired value for `QueueConfigData.weight_restrict_decay`. #[pallet::weight((T::WeightInfo::set_config_with_weight(), DispatchClass::Operational,))] - pub fn update_weight_restrict_decay( - origin: OriginFor, - new: XcmWeight, - ) -> DispatchResult { + pub fn update_weight_restrict_decay(origin: OriginFor, new: Weight) -> DispatchResult { ensure_root(origin)?; - QueueConfig::::mutate(|data| { - data.weight_restrict_decay = Weight::from_ref_time(new) - }); + QueueConfig::::mutate(|data| data.weight_restrict_decay = new); Ok(()) } @@ -270,12 +262,10 @@ pub mod pallet { #[pallet::weight((T::WeightInfo::set_config_with_weight(), DispatchClass::Operational,))] pub fn update_xcmp_max_individual_weight( origin: OriginFor, - new: XcmWeight, + new: Weight, ) -> DispatchResult { ensure_root(origin)?; - QueueConfig::::mutate(|data| { - data.xcmp_max_individual_weight = Weight::from_ref_time(new) - }); + QueueConfig::::mutate(|data| data.xcmp_max_individual_weight = new); Ok(()) } @@ -630,26 +620,26 @@ impl Pallet { Ok(xcm) => { let location = (Parent, Parachain(sender.into())); - match T::XcmExecutor::execute_xcm(location, xcm, hash, max_weight.ref_time()) { + match T::XcmExecutor::execute_xcm(location, xcm, hash, max_weight) { Outcome::Error(e) => ( Err(e), Event::Fail { message_hash: Some(hash), error: e, weight: Weight::zero() }, ), Outcome::Complete(w) => ( - Ok(Weight::from_ref_time(w)), + Ok(w), Event::Success { message_hash: Some(hash), - weight: Weight::from_ref_time(w), + weight: w, }, ), // As far as the caller is concerned, this was dispatched without error, so // we just report the weight used. Outcome::Incomplete(w, e) => ( - Ok(Weight::from_ref_time(w)), + Ok(w), Event::Fail { message_hash: Some(hash), error: e, - weight: Weight::from_ref_time(w), + weight: w, }, ), } @@ -687,7 +677,7 @@ impl Pallet { match Self::handle_xcm_message(sender, sent_at, xcm, weight) { Ok(used) => weight_used = weight_used.saturating_add(used), Err(XcmError::WeightLimitReached(required)) - if required > max_individual_weight.ref_time() => + if required.any_gt(max_individual_weight) => { let is_under_limit = Overweight::::count() < MAX_OVERWEIGHT_MESSAGES; @@ -706,13 +696,13 @@ impl Pallet { sender, sent_at, index, - required: Weight::from_ref_time(required), + required, }; Self::deposit_event(e); } }, Err(XcmError::WeightLimitReached(required)) - if required <= max_weight.ref_time() => + if required.all_lte(max_weight) => { // That message didn't get processed this time because of being // too heavy. We leave it around for next time and bail. diff --git a/pallets/xcmp-queue/src/migration.rs b/pallets/xcmp-queue/src/migration.rs index 86f6a2a0ec2..8b73320030f 100644 --- a/pallets/xcmp-queue/src/migration.rs +++ b/pallets/xcmp-queue/src/migration.rs @@ -22,7 +22,6 @@ use frame_support::{ traits::StorageVersion, weights::{constants::WEIGHT_PER_MILLIS, Weight}, }; -use xcm::latest::Weight as XcmWeight; /// The current storage version. pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); @@ -56,9 +55,9 @@ mod v1 { pub suspend_threshold: u32, pub drop_threshold: u32, pub resume_threshold: u32, - pub threshold_weight: XcmWeight, - pub weight_restrict_decay: XcmWeight, - pub xcmp_max_individual_weight: XcmWeight, + pub threshold_weight: u64, + pub weight_restrict_decay: u64, + pub xcmp_max_individual_weight: u64, } impl Default for QueueConfigData { diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index 5cededd7b46..05926a52955 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -31,10 +31,7 @@ use frame_support::{ use polkadot_runtime_common::xcm_sender::ConstantPrice; use sp_runtime::{traits::Saturating, SaturatedConversion}; use sp_std::{marker::PhantomData, prelude::*}; -use xcm::{ - latest::{prelude::*, Weight as XCMWeight}, - WrapVersion, -}; +use xcm::{latest::{prelude::*}, WrapVersion}; use xcm_builder::TakeRevenue; use xcm_executor::traits::{MatchesFungibles, TransactAsset, WeightTrader}; @@ -151,7 +148,7 @@ impl< // If everything goes well, we charge. fn buy_weight( &mut self, - weight: XCMWeight, + weight: Weight, payment: xcm_executor::Assets, ) -> Result { log::trace!(target: "xcm::weight", "TakeFirstAssetTrader::buy_weight weight: {:?}, payment: {:?}", weight, payment); @@ -161,8 +158,6 @@ impl< return Err(XcmError::NotWithdrawable) } - let weight = Weight::from_ref_time(weight); - // We take the very first multiasset from payment let multiassets: MultiAssets = payment.clone().into(); @@ -203,15 +198,13 @@ impl< Ok(unused) } - fn refund_weight(&mut self, weight: XCMWeight) -> Option { + fn refund_weight(&mut self, weight: Weight) -> Option { log::trace!(target: "xcm::weight", "TakeFirstAssetTrader::refund_weight weight: {:?}", weight); if let Some(AssetTraderRefunder { mut weight_outstanding, outstanding_concrete_asset: MultiAsset { id, fun }, }) = self.0.clone() { - let weight = Weight::from_ref_time(weight).min(weight_outstanding); - // Get the local asset id in which we can refund fees let (local_asset_id, outstanding_balance) = Matcher::matches_fungibles(&(id.clone(), fun).into()).ok()?; From 3fdf0785f0401fe2a00b8be3817e82d96fa8b353 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 22 Nov 2022 22:54:41 +0900 Subject: [PATCH 101/263] Fixes --- pallets/xcmp-queue/src/lib.rs | 26 +-- .../assets/statemine/src/weights/xcm/mod.rs | 200 +++++++++-------- .../assets/statemint/src/weights/xcm/mod.rs | 202 +++++++++--------- .../assets/westmint/src/weights/xcm/mod.rs | 200 +++++++++-------- primitives/utility/src/lib.rs | 2 +- 5 files changed, 297 insertions(+), 333 deletions(-) diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index 09565f490d0..bba6ad67a00 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -55,7 +55,7 @@ use rand_chacha::{ use scale_info::TypeInfo; use sp_runtime::RuntimeDebug; use sp_std::{convert::TryFrom, prelude::*}; -use xcm::{latest::{prelude::*}, VersionedXcm, WrapVersion, MAX_XCM_DECODE_DEPTH}; +use xcm::{latest::prelude::*, VersionedXcm, WrapVersion, MAX_XCM_DECODE_DEPTH}; use xcm_executor::traits::ConvertOrigin; pub use pallet::*; @@ -156,9 +156,8 @@ pub mod pallet { &mut data.as_slice(), ) .map_err(|_| Error::::BadXcm)?; - let used = - Self::handle_xcm_message(sender, sent_at, xcm, weight_limit) - .map_err(|_| Error::::WeightOverLimit)?; + let used = Self::handle_xcm_message(sender, sent_at, xcm, weight_limit) + .map_err(|_| Error::::WeightOverLimit)?; Overweight::::remove(index); Self::deposit_event(Event::OverweightServiced { index, used }); Ok(Some(used.saturating_add(Weight::from_ref_time(1_000_000))).into()) @@ -625,23 +624,12 @@ impl Pallet { Err(e), Event::Fail { message_hash: Some(hash), error: e, weight: Weight::zero() }, ), - Outcome::Complete(w) => ( - Ok(w), - Event::Success { - message_hash: Some(hash), - weight: w, - }, - ), + Outcome::Complete(w) => + (Ok(w), Event::Success { message_hash: Some(hash), weight: w }), // As far as the caller is concerned, this was dispatched without error, so // we just report the weight used. - Outcome::Incomplete(w, e) => ( - Ok(w), - Event::Fail { - message_hash: Some(hash), - error: e, - weight: w, - }, - ), + Outcome::Incomplete(w, e) => + (Ok(w), Event::Fail { message_hash: Some(hash), error: e, weight: w }), } }, Err(()) => diff --git a/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs b/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs index 93e12e826fd..9ea341c9c37 100644 --- a/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs @@ -22,178 +22,170 @@ use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; use sp_std::{cmp, prelude::*}; -use xcm::{ - latest::{prelude::*, Weight as XCMWeight}, - DoubleEncoded, -}; +use xcm::{latest::prelude::*, DoubleEncoded}; trait WeighMultiAssets { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight; + fn weigh_multi_assets(&self, weight: Weight) -> Weight; } const MAX_ASSETS: u32 = 100; impl WeighMultiAssets for MultiAssetFilter { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight { - let weight = match self { + fn weigh_multi_assets(&self, weight: Weight) -> Weight { + match self { Self::Definite(assets) => weight.saturating_mul(assets.inner().into_iter().count() as u64), Self::Wild(_) => weight.saturating_mul(MAX_ASSETS as u64), - }; - weight.ref_time() + } } } impl WeighMultiAssets for MultiAssets { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight { - weight.saturating_mul(self.inner().into_iter().count() as u64).ref_time() + fn weigh_multi_assets(&self, weight: Weight) -> Weight { + weight.saturating_mul(self.inner().into_iter().count() as u64) } } pub struct StatemineXcmWeight(core::marker::PhantomData); impl XcmWeightInfo for StatemineXcmWeight { - fn withdraw_asset(assets: &MultiAssets) -> XCMWeight { + fn withdraw_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::withdraw_asset()) } // Currently there is no trusted reserve - fn reserve_asset_deposited(_assets: &MultiAssets) -> XCMWeight { - u64::MAX + fn reserve_asset_deposited(_assets: &MultiAssets) -> Weight { + Weight::MAX } - fn receive_teleported_asset(assets: &MultiAssets) -> XCMWeight { + fn receive_teleported_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::receive_teleported_asset()) } fn query_response( _query_id: &u64, _response: &Response, - _max_weight: &u64, + _max_weight: &Weight, _querier: &Option, - ) -> XCMWeight { - XcmGeneric::::query_response().ref_time() + ) -> Weight { + XcmGeneric::::query_response() } - fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> XCMWeight { + fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::transfer_asset()) } fn transfer_reserve_asset( assets: &MultiAssets, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::transfer_reserve_asset()) } fn transact( _origin_type: &OriginKind, - _require_weight_at_most: &u64, + _require_weight_at_most: &Weight, _call: &DoubleEncoded, - ) -> XCMWeight { - XcmGeneric::::transact().ref_time() + ) -> Weight { + XcmGeneric::::transact() } fn hrmp_new_channel_open_request( _sender: &u32, _max_message_size: &u32, _max_capacity: &u32, - ) -> XCMWeight { + ) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn hrmp_channel_accepted(_recipient: &u32) -> XCMWeight { + fn hrmp_channel_accepted(_recipient: &u32) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> XCMWeight { + fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn clear_origin() -> XCMWeight { - XcmGeneric::::clear_origin().ref_time() + fn clear_origin() -> Weight { + XcmGeneric::::clear_origin() } - fn descend_origin(_who: &InteriorMultiLocation) -> XCMWeight { - XcmGeneric::::descend_origin().ref_time() + fn descend_origin(_who: &InteriorMultiLocation) -> Weight { + XcmGeneric::::descend_origin() } - fn report_error(_query_response_info: &QueryResponseInfo) -> XCMWeight { - XcmGeneric::::report_error().ref_time() + fn report_error(_query_response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::report_error() } - fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> XCMWeight { + fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> Weight { // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_ref_time(1_000_000_000 as u64).ref_time(); + let hardcoded_weight = Weight::from_ref_time(1_000_000_000 as u64); let weight = assets.weigh_multi_assets(XcmFungibleWeight::::deposit_asset()); - cmp::min(hardcoded_weight, weight) + hardcoded_weight.min(weight) } fn deposit_reserve_asset( assets: &MultiAssetFilter, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::deposit_reserve_asset()) } - fn exchange_asset( - _give: &MultiAssetFilter, - _receive: &MultiAssets, - _maximal: &bool, - ) -> XCMWeight { + fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets, _maximal: &bool) -> Weight { Weight::MAX.ref_time() } fn initiate_reserve_withdraw( assets: &MultiAssetFilter, _reserve: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmGeneric::::initiate_reserve_withdraw()) } fn initiate_teleport( assets: &MultiAssetFilter, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_ref_time(200_000_000 as u64).ref_time(); + let hardcoded_weight = Weight::from_ref_time(200_000_000 as u64); let weight = assets.weigh_multi_assets(XcmFungibleWeight::::initiate_teleport()); - cmp::min(hardcoded_weight, weight) + hardcoded_weight.min(weight) } - fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> XCMWeight { - XcmGeneric::::report_holding().ref_time() + fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> Weight { + XcmGeneric::::report_holding() } - fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> XCMWeight { - XcmGeneric::::buy_execution().ref_time() + fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> Weight { + XcmGeneric::::buy_execution() } - fn refund_surplus() -> XCMWeight { - XcmGeneric::::refund_surplus().ref_time() + fn refund_surplus() -> Weight { + XcmGeneric::::refund_surplus() } - fn set_error_handler(_xcm: &Xcm) -> XCMWeight { - XcmGeneric::::set_error_handler().ref_time() + fn set_error_handler(_xcm: &Xcm) -> Weight { + XcmGeneric::::set_error_handler() } - fn set_appendix(_xcm: &Xcm) -> XCMWeight { - XcmGeneric::::set_appendix().ref_time() + fn set_appendix(_xcm: &Xcm) -> Weight { + XcmGeneric::::set_appendix() } - fn clear_error() -> XCMWeight { - XcmGeneric::::clear_error().ref_time() + fn clear_error() -> Weight { + XcmGeneric::::clear_error() } - fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> XCMWeight { - XcmGeneric::::claim_asset().ref_time() + fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> Weight { + XcmGeneric::::claim_asset() } - fn trap(_code: &u64) -> XCMWeight { - XcmGeneric::::trap().ref_time() + fn trap(_code: &u64) -> Weight { + XcmGeneric::::trap() } - fn subscribe_version(_query_id: &QueryId, _max_response_weight: &u64) -> XCMWeight { - XcmGeneric::::subscribe_version().ref_time() + fn subscribe_version(_query_id: &QueryId, _max_response_weight: &Weight) -> Weight { + XcmGeneric::::subscribe_version() } - fn unsubscribe_version() -> XCMWeight { - XcmGeneric::::unsubscribe_version().ref_time() + fn unsubscribe_version() -> Weight { + XcmGeneric::::unsubscribe_version() } - fn burn_asset(assets: &MultiAssets) -> XCMWeight { + fn burn_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmGeneric::::burn_asset()) } - fn expect_asset(assets: &MultiAssets) -> XCMWeight { + fn expect_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmGeneric::::expect_asset()) } - fn expect_origin(_origin: &Option) -> XCMWeight { - XcmGeneric::::expect_origin().ref_time() + fn expect_origin(_origin: &Option) -> Weight { + XcmGeneric::::expect_origin() } - fn expect_error(_error: &Option<(u32, XcmError)>) -> XCMWeight { - XcmGeneric::::expect_error().ref_time() + fn expect_error(_error: &Option<(u32, XcmError)>) -> Weight { + XcmGeneric::::expect_error() } - fn query_pallet(_module_name: &Vec, _response_info: &QueryResponseInfo) -> XCMWeight { - XcmGeneric::::query_pallet().ref_time() + fn query_pallet(_module_name: &Vec, _response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::query_pallet() } fn expect_pallet( _index: &u32, @@ -201,47 +193,47 @@ impl XcmWeightInfo for StatemineXcmWeight { _module_name: &Vec, _crate_major: &u32, _min_crate_minor: &u32, - ) -> XCMWeight { - XcmGeneric::::expect_pallet().ref_time() + ) -> Weight { + XcmGeneric::::expect_pallet() } - fn report_transact_status(_response_info: &QueryResponseInfo) -> XCMWeight { - XcmGeneric::::report_transact_status().ref_time() + fn report_transact_status(_response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::report_transact_status() } - fn clear_transact_status() -> XCMWeight { - XcmGeneric::::clear_transact_status().ref_time() + fn clear_transact_status() -> Weight { + XcmGeneric::::clear_transact_status() } - fn universal_origin(_: &Junction) -> XCMWeight { - Weight::MAX.ref_time() + fn universal_origin(_: &Junction) -> Weight { + Weight::MAX } - fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> XCMWeight { - Weight::MAX.ref_time() + fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { + Weight::MAX } - fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { - Weight::MAX.ref_time() + fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { - Weight::MAX.ref_time() + fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { - Weight::MAX.ref_time() + fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { - Weight::MAX.ref_time() + fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn set_fees_mode(_: &bool) -> XCMWeight { - XcmGeneric::::set_fees_mode().ref_time() + fn set_fees_mode(_: &bool) -> Weight { + XcmGeneric::::set_fees_mode() } - fn set_topic(_topic: &[u8; 32]) -> XCMWeight { - XcmGeneric::::set_topic().ref_time() + fn set_topic(_topic: &[u8; 32]) -> Weight { + XcmGeneric::::set_topic() } - fn clear_topic() -> XCMWeight { - XcmGeneric::::clear_topic().ref_time() + fn clear_topic() -> Weight { + XcmGeneric::::clear_topic() } - fn alias_origin(_: &MultiLocation) -> XCMWeight { + fn alias_origin(_: &MultiLocation) -> Weight { // XCM Executor does not currently support alias origin operations - Weight::MAX.ref_time() + Weight::MAX } - fn unpaid_execution(_: &WeightLimit, _: &Option) -> XCMWeight { - XcmGeneric::::unpaid_execution().ref_time() + fn unpaid_execution(_: &WeightLimit, _: &Option) -> Weight { + XcmGeneric::::unpaid_execution() } } diff --git a/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs b/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs index bd6c9bbc097..f440f7b769a 100644 --- a/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs @@ -22,178 +22,170 @@ use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; use sp_std::{cmp, prelude::*}; -use xcm::{ - latest::{prelude::*, Weight as XCMWeight}, - DoubleEncoded, -}; +use xcm::{latest::prelude::*, DoubleEncoded}; trait WeighMultiAssets { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight; + fn weigh_multi_assets(&self, weight: Weight) -> Weight; } const MAX_ASSETS: u32 = 100; impl WeighMultiAssets for MultiAssetFilter { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight { - let weight = match self { + fn weigh_multi_assets(&self, weight: Weight) -> Weight { + match self { Self::Definite(assets) => weight.saturating_mul(assets.inner().into_iter().count() as u64), Self::Wild(_) => weight.saturating_mul(MAX_ASSETS as u64), - }; - weight.ref_time() + } } } impl WeighMultiAssets for MultiAssets { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight { - weight.saturating_mul(self.inner().into_iter().count() as u64).ref_time() + fn weigh_multi_assets(&self, weight: Weight) -> Weight { + weight.saturating_mul(self.inner().into_iter().count() as u64) } } pub struct StatemintXcmWeight(core::marker::PhantomData); impl XcmWeightInfo for StatemintXcmWeight { - fn withdraw_asset(assets: &MultiAssets) -> XCMWeight { + fn withdraw_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::withdraw_asset()) } // Currently there is no trusted reserve - fn reserve_asset_deposited(_assets: &MultiAssets) -> XCMWeight { - u64::MAX + fn reserve_asset_deposited(_assets: &MultiAssets) -> Weight { + Weight::MAX } - fn receive_teleported_asset(assets: &MultiAssets) -> XCMWeight { + fn receive_teleported_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::receive_teleported_asset()) } fn query_response( _query_id: &u64, _response: &Response, - _max_weight: &u64, + _max_weight: &Weight, _querier: &Option, - ) -> XCMWeight { - XcmGeneric::::query_response().ref_time() + ) -> Weight { + XcmGeneric::::query_response() } - fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> XCMWeight { + fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::transfer_asset()) } fn transfer_reserve_asset( assets: &MultiAssets, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::transfer_reserve_asset()) } fn transact( _origin_type: &OriginKind, - _require_weight_at_most: &u64, + _require_weight_at_most: &Weight, _call: &DoubleEncoded, - ) -> XCMWeight { - XcmGeneric::::transact().ref_time() + ) -> Weight { + XcmGeneric::::transact() } fn hrmp_new_channel_open_request( _sender: &u32, _max_message_size: &u32, _max_capacity: &u32, - ) -> XCMWeight { + ) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn hrmp_channel_accepted(_recipient: &u32) -> XCMWeight { + fn hrmp_channel_accepted(_recipient: &u32) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> XCMWeight { + fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn clear_origin() -> XCMWeight { - XcmGeneric::::clear_origin().ref_time() + fn clear_origin() -> Weight { + XcmGeneric::::clear_origin() } - fn descend_origin(_who: &InteriorMultiLocation) -> XCMWeight { - XcmGeneric::::descend_origin().ref_time() + fn descend_origin(_who: &InteriorMultiLocation) -> Weight { + XcmGeneric::::descend_origin() } - fn report_error(_query_response_info: &QueryResponseInfo) -> XCMWeight { - XcmGeneric::::report_error().ref_time() + fn report_error(_query_response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::report_error() } - fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> XCMWeight { + fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> Weight { // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_ref_time(1_000_000_000 as u64).ref_time(); + let hardcoded_weight = Weight::from_ref_time(1_000_000_000 as u64); let weight = assets.weigh_multi_assets(XcmFungibleWeight::::deposit_asset()); - cmp::min(hardcoded_weight, weight) + hardcoded_weight.min(weight) } fn deposit_reserve_asset( assets: &MultiAssetFilter, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::deposit_reserve_asset()) } - fn exchange_asset( - _give: &MultiAssetFilter, - _receive: &MultiAssets, - _maximal: &bool, - ) -> XCMWeight { - Weight::MAX.ref_time() + fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets, _maximal: &bool) -> Weight { + Weight::MAX } fn initiate_reserve_withdraw( assets: &MultiAssetFilter, _reserve: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmGeneric::::initiate_reserve_withdraw()) } fn initiate_teleport( assets: &MultiAssetFilter, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_ref_time(200_000_000 as u64).ref_time(); + let hardcoded_weight = Weight::from_ref_time(200_000_000 as u64); let weight = assets.weigh_multi_assets(XcmFungibleWeight::::initiate_teleport()); - cmp::min(hardcoded_weight, weight) + hardcoded_weight.min(weight) } - fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> XCMWeight { - XcmGeneric::::report_holding().ref_time() + fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> Weight { + XcmGeneric::::report_holding() } - fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> XCMWeight { - XcmGeneric::::buy_execution().ref_time() + fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> Weight { + XcmGeneric::::buy_execution() } - fn refund_surplus() -> XCMWeight { - XcmGeneric::::refund_surplus().ref_time() + fn refund_surplus() -> Weight { + XcmGeneric::::refund_surplus() } - fn set_error_handler(_xcm: &Xcm) -> XCMWeight { - XcmGeneric::::set_error_handler().ref_time() + fn set_error_handler(_xcm: &Xcm) -> Weight { + XcmGeneric::::set_error_handler() } - fn set_appendix(_xcm: &Xcm) -> XCMWeight { - XcmGeneric::::set_appendix().ref_time() + fn set_appendix(_xcm: &Xcm) -> Weight { + XcmGeneric::::set_appendix() } - fn clear_error() -> XCMWeight { - XcmGeneric::::clear_error().ref_time() + fn clear_error() -> Weight { + XcmGeneric::::clear_error() } - fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> XCMWeight { - XcmGeneric::::claim_asset().ref_time() + fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> Weight { + XcmGeneric::::claim_asset() } - fn trap(_code: &u64) -> XCMWeight { - XcmGeneric::::trap().ref_time() + fn trap(_code: &u64) -> Weight { + XcmGeneric::::trap() } - fn subscribe_version(_query_id: &QueryId, _max_response_weight: &u64) -> XCMWeight { - XcmGeneric::::subscribe_version().ref_time() + fn subscribe_version(_query_id: &QueryId, _max_response_weight: &Weight) -> Weight { + XcmGeneric::::subscribe_version() } - fn unsubscribe_version() -> XCMWeight { - XcmGeneric::::unsubscribe_version().ref_time() + fn unsubscribe_version() -> Weight { + XcmGeneric::::unsubscribe_version() } - fn burn_asset(assets: &MultiAssets) -> XCMWeight { + fn burn_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmGeneric::::burn_asset()) } - fn expect_asset(assets: &MultiAssets) -> XCMWeight { + fn expect_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmGeneric::::expect_asset()) } - fn expect_origin(_origin: &Option) -> XCMWeight { - XcmGeneric::::expect_origin().ref_time() + fn expect_origin(_origin: &Option) -> Weight { + XcmGeneric::::expect_origin() } - fn expect_error(_error: &Option<(u32, XcmError)>) -> XCMWeight { - XcmGeneric::::expect_error().ref_time() + fn expect_error(_error: &Option<(u32, XcmError)>) -> Weight { + XcmGeneric::::expect_error() } - fn query_pallet(_module_name: &Vec, _response_info: &QueryResponseInfo) -> XCMWeight { - XcmGeneric::::query_pallet().ref_time() + fn query_pallet(_module_name: &Vec, _response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::query_pallet() } fn expect_pallet( _index: &u32, @@ -201,47 +193,47 @@ impl XcmWeightInfo for StatemintXcmWeight { _module_name: &Vec, _crate_major: &u32, _min_crate_minor: &u32, - ) -> XCMWeight { - XcmGeneric::::expect_pallet().ref_time() + ) -> Weight { + XcmGeneric::::expect_pallet() } - fn report_transact_status(_response_info: &QueryResponseInfo) -> XCMWeight { - XcmGeneric::::report_transact_status().ref_time() + fn report_transact_status(_response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::report_transact_status() } - fn clear_transact_status() -> XCMWeight { - XcmGeneric::::clear_transact_status().ref_time() + fn clear_transact_status() -> Weight { + XcmGeneric::::clear_transact_status() } - fn universal_origin(_: &Junction) -> XCMWeight { - Weight::MAX.ref_time() + fn universal_origin(_: &Junction) -> Weight { + Weight::MAX } - fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> XCMWeight { - Weight::MAX.ref_time() + fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { + Weight::MAX } - fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { - Weight::MAX.ref_time() + fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { - Weight::MAX.ref_time() + fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { - Weight::MAX.ref_time() + fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { - Weight::MAX.ref_time() + fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn set_fees_mode(_: &bool) -> XCMWeight { - XcmGeneric::::set_fees_mode().ref_time() + fn set_fees_mode(_: &bool) -> Weight { + XcmGeneric::::set_fees_mode() } - fn set_topic(_topic: &[u8; 32]) -> XCMWeight { - XcmGeneric::::set_topic().ref_time() + fn set_topic(_topic: &[u8; 32]) -> Weight { + XcmGeneric::::set_topic() } - fn clear_topic() -> XCMWeight { - XcmGeneric::::clear_topic().ref_time() + fn clear_topic() -> Weight { + XcmGeneric::::clear_topic() } - fn alias_origin(_: &MultiLocation) -> XCMWeight { + fn alias_origin(_: &MultiLocation) -> Weight { // XCM Executor does not currently support alias origin operations - Weight::MAX.ref_time() + Weight::MAX } - fn unpaid_execution(_: &WeightLimit, _: &Option) -> XCMWeight { - XcmGeneric::::unpaid_execution().ref_time() + fn unpaid_execution(_: &WeightLimit, _: &Option) -> Weight { + XcmGeneric::::unpaid_execution() } } diff --git a/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs b/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs index 8429b74f2ec..0726040167c 100644 --- a/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs @@ -22,178 +22,170 @@ use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; use sp_std::{cmp, prelude::*}; -use xcm::{ - latest::{prelude::*, Weight as XCMWeight}, - DoubleEncoded, -}; +use xcm::{latest::prelude::*, DoubleEncoded}; trait WeighMultiAssets { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight; + fn weigh_multi_assets(&self, weight: Weight) -> Weight; } const MAX_ASSETS: u32 = 100; impl WeighMultiAssets for MultiAssetFilter { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight { - let weight = match self { + fn weigh_multi_assets(&self, weight: Weight) -> Weight { + match self { Self::Definite(assets) => weight.saturating_mul(assets.inner().into_iter().count() as u64), Self::Wild(_) => weight.saturating_mul(MAX_ASSETS as u64), - }; - weight.ref_time() + } } } impl WeighMultiAssets for MultiAssets { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight { - weight.saturating_mul(self.inner().into_iter().count() as u64).ref_time() + fn weigh_multi_assets(&self, weight: Weight) -> Weight { + weight.saturating_mul(self.inner().into_iter().count() as u64) } } pub struct WestmintXcmWeight(core::marker::PhantomData); impl XcmWeightInfo for WestmintXcmWeight { - fn withdraw_asset(assets: &MultiAssets) -> XCMWeight { + fn withdraw_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::withdraw_asset()) } // Currently there is no trusted reserve - fn reserve_asset_deposited(_assets: &MultiAssets) -> XCMWeight { - u64::MAX + fn reserve_asset_deposited(_assets: &MultiAssets) -> Weight { + Weight::MAX } - fn receive_teleported_asset(assets: &MultiAssets) -> XCMWeight { + fn receive_teleported_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::receive_teleported_asset()) } fn query_response( _query_id: &u64, _response: &Response, - _max_weight: &u64, + _max_weight: &Weight, _querier: &Option, - ) -> XCMWeight { - XcmGeneric::::query_response().ref_time() + ) -> Weight { + XcmGeneric::::query_response() } - fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> XCMWeight { + fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::transfer_asset()) } fn transfer_reserve_asset( assets: &MultiAssets, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::transfer_reserve_asset()) } fn transact( _origin_type: &OriginKind, - _require_weight_at_most: &u64, + _require_weight_at_most: &Weight, _call: &DoubleEncoded, - ) -> XCMWeight { - XcmGeneric::::transact().ref_time() + ) -> Weight { + XcmGeneric::::transact() } fn hrmp_new_channel_open_request( _sender: &u32, _max_message_size: &u32, _max_capacity: &u32, - ) -> XCMWeight { + ) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn hrmp_channel_accepted(_recipient: &u32) -> XCMWeight { + fn hrmp_channel_accepted(_recipient: &u32) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> XCMWeight { + fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn clear_origin() -> XCMWeight { - XcmGeneric::::clear_origin().ref_time() + fn clear_origin() -> Weight { + XcmGeneric::::clear_origin() } - fn descend_origin(_who: &InteriorMultiLocation) -> XCMWeight { - XcmGeneric::::descend_origin().ref_time() + fn descend_origin(_who: &InteriorMultiLocation) -> Weight { + XcmGeneric::::descend_origin() } - fn report_error(_query_response_info: &QueryResponseInfo) -> XCMWeight { - XcmGeneric::::report_error().ref_time() + fn report_error(_query_response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::report_error() } - fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> XCMWeight { + fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> Weight { // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_ref_time(1_000_000_000 as u64).ref_time(); + let hardcoded_weight = Weight::from_ref_time(1_000_000_000 as u64); let weight = assets.weigh_multi_assets(XcmFungibleWeight::::deposit_asset()); - cmp::min(hardcoded_weight, weight) + hardcoded_weight.min(weight) } fn deposit_reserve_asset( assets: &MultiAssetFilter, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::deposit_reserve_asset()) } - fn exchange_asset( - _give: &MultiAssetFilter, - _receive: &MultiAssets, - _maximal: &bool, - ) -> XCMWeight { + fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets, _maximal: &bool) -> Weight { Weight::MAX.ref_time() } fn initiate_reserve_withdraw( assets: &MultiAssetFilter, _reserve: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmGeneric::::initiate_reserve_withdraw()) } fn initiate_teleport( assets: &MultiAssetFilter, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_ref_time(200_000_000 as u64).ref_time(); + let hardcoded_weight = Weight::from_ref_time(200_000_000 as u64); let weight = assets.weigh_multi_assets(XcmFungibleWeight::::initiate_teleport()); - cmp::min(hardcoded_weight, weight) + hardcoded_weight.min(weight) } - fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> XCMWeight { - XcmGeneric::::report_holding().ref_time() + fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> Weight { + XcmGeneric::::report_holding() } - fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> XCMWeight { - XcmGeneric::::buy_execution().ref_time() + fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> Weight { + XcmGeneric::::buy_execution() } - fn refund_surplus() -> XCMWeight { - XcmGeneric::::refund_surplus().ref_time() + fn refund_surplus() -> Weight { + XcmGeneric::::refund_surplus() } - fn set_error_handler(_xcm: &Xcm) -> XCMWeight { - XcmGeneric::::set_error_handler().ref_time() + fn set_error_handler(_xcm: &Xcm) -> Weight { + XcmGeneric::::set_error_handler() } - fn set_appendix(_xcm: &Xcm) -> XCMWeight { - XcmGeneric::::set_appendix().ref_time() + fn set_appendix(_xcm: &Xcm) -> Weight { + XcmGeneric::::set_appendix() } - fn clear_error() -> XCMWeight { - XcmGeneric::::clear_error().ref_time() + fn clear_error() -> Weight { + XcmGeneric::::clear_error() } - fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> XCMWeight { - XcmGeneric::::claim_asset().ref_time() + fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> Weight { + XcmGeneric::::claim_asset() } - fn trap(_code: &u64) -> XCMWeight { - XcmGeneric::::trap().ref_time() + fn trap(_code: &u64) -> Weight { + XcmGeneric::::trap() } - fn subscribe_version(_query_id: &QueryId, _max_response_weight: &u64) -> XCMWeight { - XcmGeneric::::subscribe_version().ref_time() + fn subscribe_version(_query_id: &QueryId, _max_response_weight: &Weight) -> Weight { + XcmGeneric::::subscribe_version() } - fn unsubscribe_version() -> XCMWeight { - XcmGeneric::::unsubscribe_version().ref_time() + fn unsubscribe_version() -> Weight { + XcmGeneric::::unsubscribe_version() } - fn burn_asset(assets: &MultiAssets) -> XCMWeight { + fn burn_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmGeneric::::burn_asset()) } - fn expect_asset(assets: &MultiAssets) -> XCMWeight { + fn expect_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmGeneric::::expect_asset()) } - fn expect_origin(_origin: &Option) -> XCMWeight { - XcmGeneric::::expect_origin().ref_time() + fn expect_origin(_origin: &Option) -> Weight { + XcmGeneric::::expect_origin() } - fn expect_error(_error: &Option<(u32, XcmError)>) -> XCMWeight { - XcmGeneric::::expect_error().ref_time() + fn expect_error(_error: &Option<(u32, XcmError)>) -> Weight { + XcmGeneric::::expect_error() } - fn query_pallet(_module_name: &Vec, _response_info: &QueryResponseInfo) -> XCMWeight { - XcmGeneric::::query_pallet().ref_time() + fn query_pallet(_module_name: &Vec, _response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::query_pallet() } fn expect_pallet( _index: &u32, @@ -201,47 +193,47 @@ impl XcmWeightInfo for WestmintXcmWeight { _module_name: &Vec, _crate_major: &u32, _min_crate_minor: &u32, - ) -> XCMWeight { - XcmGeneric::::expect_pallet().ref_time() + ) -> Weight { + XcmGeneric::::expect_pallet() } - fn report_transact_status(_response_info: &QueryResponseInfo) -> XCMWeight { - XcmGeneric::::report_transact_status().ref_time() + fn report_transact_status(_response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::report_transact_status() } - fn clear_transact_status() -> XCMWeight { - XcmGeneric::::clear_transact_status().ref_time() + fn clear_transact_status() -> Weight { + XcmGeneric::::clear_transact_status() } - fn universal_origin(_: &Junction) -> XCMWeight { - Weight::MAX.ref_time() + fn universal_origin(_: &Junction) -> Weight { + Weight::MAX } - fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> XCMWeight { - Weight::MAX.ref_time() + fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { + Weight::MAX } - fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { - Weight::MAX.ref_time() + fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { - Weight::MAX.ref_time() + fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { - Weight::MAX.ref_time() + fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { - Weight::MAX.ref_time() + fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn set_fees_mode(_: &bool) -> XCMWeight { - XcmGeneric::::set_fees_mode().ref_time() + fn set_fees_mode(_: &bool) -> Weight { + XcmGeneric::::set_fees_mode() } - fn set_topic(_topic: &[u8; 32]) -> XCMWeight { - XcmGeneric::::set_topic().ref_time() + fn set_topic(_topic: &[u8; 32]) -> Weight { + XcmGeneric::::set_topic() } - fn clear_topic() -> XCMWeight { - XcmGeneric::::clear_topic().ref_time() + fn clear_topic() -> Weight { + XcmGeneric::::clear_topic() } - fn alias_origin(_: &MultiLocation) -> XCMWeight { + fn alias_origin(_: &MultiLocation) -> Weight { // XCM Executor does not currently support alias origin operations - Weight::MAX.ref_time() + Weight::MAX } - fn unpaid_execution(_: &WeightLimit, _: &Option) -> XCMWeight { - XcmGeneric::::unpaid_execution().ref_time() + fn unpaid_execution(_: &WeightLimit, _: &Option) -> Weight { + XcmGeneric::::unpaid_execution() } } diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index 05926a52955..75b7a7a271b 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -31,7 +31,7 @@ use frame_support::{ use polkadot_runtime_common::xcm_sender::ConstantPrice; use sp_runtime::{traits::Saturating, SaturatedConversion}; use sp_std::{marker::PhantomData, prelude::*}; -use xcm::{latest::{prelude::*}, WrapVersion}; +use xcm::{latest::prelude::*, WrapVersion}; use xcm_builder::TakeRevenue; use xcm_executor::traits::{MatchesFungibles, TransactAsset, WeightTrader}; From d6d1a7d7f897111ac94db3e9f75a42be3aa0e6bb Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 23 Nov 2022 01:12:42 +0900 Subject: [PATCH 102/263] Fixes --- parachain-template/runtime/src/xcm_config.rs | 11 ++++++----- parachains/common/src/xcm_config.rs | 10 +++++----- .../runtimes/assets/statemine/src/xcm_config.rs | 2 ++ .../runtimes/assets/statemint/src/xcm_config.rs | 2 ++ .../runtimes/assets/westmint/src/xcm_config.rs | 2 ++ .../collectives-polkadot/src/xcm_config.rs | 5 ++++- .../contracts/contracts-rococo/src/xcm_config.rs | 5 ++++- .../runtimes/testing/penpal/src/xcm_config.rs | 14 ++++++++------ 8 files changed, 33 insertions(+), 18 deletions(-) diff --git a/parachain-template/runtime/src/xcm_config.rs b/parachain-template/runtime/src/xcm_config.rs index 03e090009e0..80a5fba3e6d 100644 --- a/parachain-template/runtime/src/xcm_config.rs +++ b/parachain-template/runtime/src/xcm_config.rs @@ -10,7 +10,7 @@ use frame_support::{ use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use polkadot_runtime_common::impls::ToAuthor; -use xcm::latest::{prelude::*, Weight as XCMWeight}; +use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentIsPreset, @@ -104,8 +104,8 @@ where fn should_execute( origin: &MultiLocation, message: &mut [Instruction], - max_weight: XCMWeight, - weight_credit: &mut XCMWeight, + max_weight: Weight, + weight_credit: &mut Weight, ) -> Result<(), ()> { Deny::should_execute(origin, message, max_weight, weight_credit)?; Allow::should_execute(origin, message, max_weight, weight_credit) @@ -118,8 +118,8 @@ impl ShouldExecute for DenyReserveTransferToRelayChain { fn should_execute( origin: &MultiLocation, message: &mut [Instruction], - _max_weight: XCMWeight, - _weight_credit: &mut XCMWeight, + _max_weight: Weight, + _weight_credit: &mut Weight, ) -> Result<(), ()> { if message.iter().any(|inst| { matches!( @@ -226,6 +226,7 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/common/src/xcm_config.rs b/parachains/common/src/xcm_config.rs index 9a9774ae94f..5f0453309ec 100644 --- a/parachains/common/src/xcm_config.rs +++ b/parachains/common/src/xcm_config.rs @@ -6,7 +6,7 @@ use frame_support::{ weights::{Weight, WeightToFee, WeightToFeePolynomial}, }; use sp_runtime::traits::Get; -use xcm::latest::{prelude::*, Weight as XCMWeight}; +use xcm::latest::prelude::*; use xcm_executor::traits::ShouldExecute; //TODO: move DenyThenTry to polkadot's xcm module. @@ -25,8 +25,8 @@ where fn should_execute( origin: &MultiLocation, message: &mut [Instruction], - max_weight: XCMWeight, - weight_credit: &mut XCMWeight, + max_weight: Weight, + weight_credit: &mut Weight, ) -> Result<(), ()> { Deny::should_execute(origin, message, max_weight, weight_credit)?; Allow::should_execute(origin, message, max_weight, weight_credit) @@ -39,8 +39,8 @@ impl ShouldExecute for DenyReserveTransferToRelayChain { fn should_execute( origin: &MultiLocation, message: &mut [Instruction], - _max_weight: XCMWeight, - _weight_credit: &mut XCMWeight, + _max_weight: Weight, + _weight_credit: &mut Weight, ) -> Result<(), ()> { if message.iter().any(|inst| { matches!( diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 4841ee7c518..84f014a32bc 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -254,6 +254,8 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; + // FIXME: Replace with benchmarked weight info + type WeightInfo = pallet_xcm::TestWeightInfo; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index bb9d82680fe..f53f6c3592b 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -229,6 +229,8 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; + // FIXME: Replace with benchmarked weight info + type WeightInfo = pallet_xcm::TestWeightInfo; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index 4f55696c69e..0e8c8abbc21 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -245,6 +245,8 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; + // FIXME: Replace with benchmarked weight info + type WeightInfo = pallet_xcm::TestWeightInfo; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index fb0bd3b4195..6eebe074507 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -20,6 +20,7 @@ use super::{ use frame_support::{ match_types, parameter_types, traits::{ConstU32, Everything, Nothing}, + weights::Weight, }; use pallet_xcm::XcmPassthrough; use parachains_common::{ @@ -98,7 +99,7 @@ pub type XcmOriginToTransactDispatchOrigin = ( parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: u64 = 1_000_000_000; + pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } @@ -196,6 +197,8 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; + // FIXME: Replace with benchmarked weight info + type WeightInfo = pallet_xcm::TestWeightInfo; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index 4f53eca6dbc..ec0849eebdd 100644 --- a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -20,6 +20,7 @@ use super::{ use frame_support::{ match_types, parameter_types, traits::{ConstU32, EitherOfDiverse, Everything, Nothing}, + weights::Weight, }; use frame_system::EnsureRoot; use pallet_xcm::{EnsureXcm, IsMajorityOfBody, XcmPassthrough}; @@ -104,7 +105,7 @@ pub type XcmOriginToTransactDispatchOrigin = ( parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: u64 = 1_000_000_000; + pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); pub const MaxInstructions: u32 = 100; } @@ -195,6 +196,8 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; + // FIXME: Replace with benchmarked weight info + type WeightInfo = pallet_xcm::TestWeightInfo; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index e8f70c20e63..f0bb531e231 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -33,13 +33,14 @@ use frame_support::{ fungibles::{self, Balanced, CreditOf}, ConstU32, Contains, ContainsPair, Everything, Get, Nothing, }, + weights::Weight, }; use pallet_asset_tx_payment::HandleCredit; use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use polkadot_runtime_common::impls::ToAuthor; use sp_runtime::traits::Zero; -use xcm::latest::{prelude::*, Weight as XCMWeight}; +use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, @@ -134,7 +135,7 @@ pub type XcmOriginToTransactDispatchOrigin = ( parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: u64 = 1_000_000_000; + pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } @@ -165,8 +166,8 @@ where fn should_execute( origin: &MultiLocation, message: &mut [Instruction], - max_weight: XCMWeight, - weight_credit: &mut XCMWeight, + max_weight: Weight, + weight_credit: &mut Weight, ) -> Result<(), ()> { Deny::should_execute(origin, message, max_weight, weight_credit)?; Allow::should_execute(origin, message, max_weight, weight_credit) @@ -179,8 +180,8 @@ impl ShouldExecute for DenyReserveTransferToRelayChain { fn should_execute( origin: &MultiLocation, message: &mut [Instruction], - _max_weight: XCMWeight, - _weight_credit: &mut XCMWeight, + _max_weight: Weight, + _weight_credit: &mut Weight, ) -> Result<(), ()> { if message.iter().any(|inst| { matches!( @@ -383,6 +384,7 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; } impl cumulus_pallet_xcm::Config for Runtime { From 05985d77f6c755e30d8e6e40047d1120514eccf9 Mon Sep 17 00:00:00 2001 From: Anthony Lazam Date: Thu, 17 Nov 2022 20:00:17 +0800 Subject: [PATCH 103/263] Update BridgeHub runtime version --- parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 10a31c9dec6..61063c82970 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -168,7 +168,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("bridge-hub-rococo"), impl_name: create_runtime_str!("bridge-hub-rococo"), authoring_version: 1, - spec_version: 1, + spec_version: 9300, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 18a3ea9aafbaddf6c45959acd0455af082f683dc Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 23 Nov 2022 00:09:04 +0100 Subject: [PATCH 104/263] Fixes --- Cargo.lock | 25 ++++++- .../src/bridge_common_config.rs | 44 ++++-------- .../src/bridge_hub_rococo_config.rs | 72 ++----------------- .../src/bridge_hub_wococo_config.rs | 72 ++----------------- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 34 +++------ 5 files changed, 57 insertions(+), 190 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f0a310083eb..5c3d6d46531 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -713,6 +713,27 @@ dependencies = [ "thiserror", ] +[[package]] +name = "bp-beefy" +version = "0.1.0" +dependencies = [ + "beefy-merkle-tree", + "beefy-primitives", + "bp-runtime", + "frame-support", + "pallet-beefy-mmr", + "pallet-mmr", + "parity-scale-codec", + "scale-info", + "serde", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "static_assertions", +] + [[package]] name = "bp-bridge-hub-rococo" version = "0.1.0" @@ -763,7 +784,6 @@ dependencies = [ name = "bp-messages" version = "0.1.0" dependencies = [ - "bitvec", "bp-runtime", "frame-support", "hex", @@ -780,6 +800,7 @@ dependencies = [ name = "bp-millau" version = "0.1.0" dependencies = [ + "bp-beefy", "bp-messages", "bp-runtime", "fixed-hash 0.7.0", @@ -911,6 +932,7 @@ dependencies = [ "sp-finality-grandpa", "sp-runtime", "sp-std", + "xcm", ] [[package]] @@ -5966,7 +5988,6 @@ dependencies = [ name = "pallet-bridge-messages" version = "0.1.0" dependencies = [ - "bitvec", "bp-messages", "bp-runtime", "bp-test-utils", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 94c0d1660be..5a2a322d96d 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -19,7 +19,7 @@ use bp_messages::{ target_chain::{DispatchMessage, MessageDispatch}, LaneId, }; -use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, BalanceOf, Chain}; +use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, Chain}; use codec::Encode; use frame_support::{dispatch::Weight, parameter_types}; use xcm::latest::prelude::*; @@ -43,38 +43,37 @@ pub struct XcmBlobMessageDispatch - MessageDispatch, BalanceOf> + MessageDispatch> for XcmBlobMessageDispatch { type DispatchPayload = XcmAsPlainPayload; - fn dispatch_weight( - message: &mut DispatchMessage>, - ) -> Weight { + fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { log::error!( "[XcmBlobMessageDispatch] TODO: change here to XCMv3 dispatch_weight with XcmExecutor - message: ?...?", ); + // TODO:check-parameter - setup weight? Weight::zero() } fn dispatch( _relayer_account: &AccountIdOf, - message: DispatchMessage>, + message: DispatchMessage, ) -> MessageDispatchResult { - log::warn!("[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob triggering"); + log::warn!("[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob triggering - message_nonce: {:?}", message.key.nonce); let payload = match message.data.payload { Ok(payload) => payload, Err(e) => { - log::error!("[XcmBlobMessageDispatch] payload error: {:?}", e); + log::error!("[XcmBlobMessageDispatch] payload error: {:?} - message_nonce: {:?}", e, message.key.nonce); return MessageDispatchResult { - dispatch_result: false, + // TODO:check-parameter - setup uspent_weight? unspent_weight: Weight::zero(), dispatch_fee_paid_during_dispatch: false, } }, }; - let dispatch_result = match BlobDispatcher::dispatch_blob(payload) { - Ok(_) => true, + match BlobDispatcher::dispatch_blob(payload) { + Ok(_) => log::debug!("[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}", message.key.nonce), Err(e) => { let e = match e { DispatchBlobError::Unbridgable => "DispatchBlobError::Unbridgable", @@ -89,14 +88,13 @@ impl "DispatchBlobError::WrongGlobal", }; log::error!( - "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?}", - e + "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?} - message_nonce: {:?}", + e, message.key.nonce ); - false }, - }; + } MessageDispatchResult { - dispatch_result, + // TODO:check-parameter - setup uspent_weight? dispatch_fee_paid_during_dispatch: false, unspent_weight: Weight::zero(), } @@ -106,15 +104,8 @@ impl, - XcmAsPlainPayload, - >; + type MessageSender: MessagesBridge; /// Our location within the Consensus Universe. fn message_sender_origin() -> InteriorMultiLocation; @@ -127,15 +118,10 @@ pub struct XcmBlobHaulerAdapter(sp_std::marker::PhantomData HaulBlob for XcmBlobHaulerAdapter { fn haul_blob(blob: sp_std::prelude::Vec) { let lane = H::xcm_lane(); - // TODO:check-parameter - fee could be taken from BridgeMessage - or add as optional fo send_message - // TODO:check-parameter - or add here something like PriceForSiblingDelivery - let fee = ::Balance::from(0u8); - let result = H::MessageSender::send_message( pallet_xcm::Origin::from(MultiLocation::from(H::message_sender_origin())).into(), lane, blob, - fee, ); let result = result .map(|artifacts| { diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index c242285c357..2a8e00bb7a0 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -27,13 +27,11 @@ use bp_runtime::ChainId; use bridge_runtime_common::{ messages, messages::{ - target::FromBridgedChainMessagesProof, BasicConfirmationTransactionEstimation, - BridgedChain, MessageBridge, MessageTransaction, ThisChain, ThisChainWithMessages, + target::FromBridgedChainMessagesProof, MessageBridge, ThisChainWithMessages, UnderlyingChainProvider, }, }; -use frame_support::{dispatch::Weight, parameter_types, RuntimeDebug}; -use sp_runtime::FixedU128; +use frame_support::{parameter_types, RuntimeDebug}; use xcm::{ latest::prelude::*, prelude::{InteriorMultiLocation, NetworkId}, @@ -49,6 +47,7 @@ parameter_types! { pub const BridgeHubWococoChainId: bp_runtime::ChainId = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; pub BridgeHubRococoUniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(Rococo), Parachain(ParachainInfo::parachain_id().into())); pub WococoGlobalConsensusNetwork: NetworkId = NetworkId::Wococo; + pub ActiveOutboundLanesToBridgeHubWococo: &'static [bp_messages::LaneId] = &[DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO]; } /// Dispatches received XCM messages from other bridge @@ -64,7 +63,6 @@ pub type ToBridgeHubWococoHaulBlobExporter = HaulBlobExporter< pub struct ToBridgeHubWococoXcmBlobHauler; pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO: LaneId = [0, 0, 0, 2]; impl XcmBlobHauler for ToBridgeHubWococoXcmBlobHauler { - type SenderChain = bp_bridge_hub_rococo::BridgeHubRococo; type MessageSender = pallet_bridge_messages::Pallet; @@ -80,8 +78,6 @@ impl XcmBlobHauler for ToBridgeHubWococoXcmBlobHauler { /// Messaging Bridge configuration for BridgeHubRococo -> BridgeHubWococo pub struct WithBridgeHubWococoMessageBridge; impl MessageBridge for WithBridgeHubWococoMessageBridge { - // TODO:check-parameter - relayers rewards - const RELAYER_FEE_PERCENT: u32 = 0; const THIS_CHAIN_ID: ChainId = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; const BRIDGED_CHAIN_ID: ChainId = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; const BRIDGED_MESSAGES_PALLET_NAME: &'static str = @@ -93,15 +89,6 @@ impl MessageBridge for WithBridgeHubWococoMessageBridge { BridgeParachainWococoInstance, bp_bridge_hub_wococo::BridgeHubWococo, >; - - fn bridged_balance_to_this_balance( - bridged_balance: bridge_runtime_common::messages::BalanceOf>, - bridged_to_this_conversion_rate_override: Option, - ) -> bridge_runtime_common::messages::BalanceOf> { - log::info!("[WithBridgeHubWococoMessageBridge] bridged_balance_to_this_balance (returns 0 balance, TODO: fix) - bridged_balance: {:?}, bridged_to_this_conversion_rate_override: {:?}", bridged_balance, bridged_to_this_conversion_rate_override); - // TODO:check-parameter - any payment? from sovereign account? - 0 - } } /// Message verifier for BridgeHubWococo messages sent from BridgeHubRococo @@ -120,14 +107,14 @@ impl UnderlyingChainProvider for BridgeHubWococo { type Chain = bp_bridge_hub_wococo::BridgeHubWococo; } -impl SourceHeaderChain for BridgeHubWococo { +impl SourceHeaderChain for BridgeHubWococo { type Error = &'static str; type MessagesProof = FromBridgedChainMessagesProof; fn verify_messages_proof( proof: Self::MessagesProof, messages_count: u32, - ) -> Result>, Self::Error> { + ) -> Result, Self::Error> { bridge_runtime_common::messages::target::verify_messages_proof::< WithBridgeHubWococoMessageBridge, >(proof, messages_count) @@ -155,40 +142,6 @@ impl messages::BridgedChainWithMessages for BridgeHubWococo { fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { true } - - fn estimate_delivery_transaction( - message_payload: &[u8], - include_pay_dispatch_fee_cost: bool, - message_dispatch_weight: Weight, - ) -> MessageTransaction { - let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX); - let extra_bytes_in_payload = message_payload_len - .saturating_sub(pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH); - - MessageTransaction { - dispatch_weight: bp_bridge_hub_wococo::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT - .saturating_mul(extra_bytes_in_payload as u64) - .saturating_add(bp_bridge_hub_wococo::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT) - .saturating_sub(if include_pay_dispatch_fee_cost { - Weight::from_ref_time(0) - } else { - bp_bridge_hub_wococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT - }) - .saturating_add(message_dispatch_weight), - size: message_payload_len - .saturating_add(bp_bridge_hub_rococo::EXTRA_STORAGE_PROOF_SIZE) - .saturating_add(bp_bridge_hub_wococo::TX_EXTRA_BYTES), - } - } - - fn transaction_payment(transaction: MessageTransaction) -> messages::BalanceOf { - log::info!( - "[BridgeHubWococo::BridgedChainWithMessages] transaction_payment (returns 0 balance, TODO: fix) - transaction: {:?}", - transaction - ); - // TODO:check-parameter - any payment? from sovereign account? - 0 - } } /// BridgeHubRococo chain from message lane point of view. @@ -202,12 +155,6 @@ impl UnderlyingChainProvider for BridgeHubRococo { impl ThisChainWithMessages for BridgeHubRococo { type RuntimeOrigin = crate::RuntimeOrigin; type RuntimeCall = crate::RuntimeCall; - type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< - bp_bridge_hub_rococo::AccountId, - { bp_bridge_hub_rococo::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() }, - { bp_bridge_hub_wococo::EXTRA_STORAGE_PROOF_SIZE }, - { bp_bridge_hub_rococo::TX_EXTRA_BYTES }, - >; fn is_message_accepted(origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool { log::info!("[BridgeHubRococo::ThisChainWithMessages] is_message_accepted - origin: {:?}, lane: {:?}", origin, lane); @@ -220,13 +167,4 @@ impl ThisChainWithMessages for BridgeHubRococo { ); MessageNonce::MAX / 2 } - - fn transaction_payment(transaction: MessageTransaction) -> messages::BalanceOf { - log::info!( - "[BridgeHubRococo::ThisChainWithMessages] transaction_payment (returns 0 balance, TODO: fix) - transaction: {:?}", - transaction - ); - // TODO:check-parameter - any payment? from sovereign account? - 0 - } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index c90b3dd29d9..2c80ca78070 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -27,13 +27,11 @@ use bp_runtime::ChainId; use bridge_runtime_common::{ messages, messages::{ - target::FromBridgedChainMessagesProof, BasicConfirmationTransactionEstimation, - BridgedChain, MessageBridge, MessageTransaction, ThisChain, ThisChainWithMessages, + target::FromBridgedChainMessagesProof, MessageBridge, ThisChainWithMessages, UnderlyingChainProvider, }, }; -use frame_support::{dispatch::Weight, parameter_types, RuntimeDebug}; -use sp_runtime::FixedU128; +use frame_support::{parameter_types, RuntimeDebug}; use xcm::{ latest::prelude::*, prelude::{InteriorMultiLocation, NetworkId}, @@ -49,6 +47,7 @@ parameter_types! { pub const BridgeHubRococoChainId: bp_runtime::ChainId = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; pub BridgeHubWococoUniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(Wococo), Parachain(ParachainInfo::parachain_id().into())); pub RococoGlobalConsensusNetwork: NetworkId = NetworkId::Rococo; + pub ActiveOutboundLanesToBridgeHubRococo: &'static [bp_messages::LaneId] = &[DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO]; } /// Dispatches received XCM messages from other bridge @@ -64,7 +63,6 @@ pub type ToBridgeHubRococoHaulBlobExporter = HaulBlobExporter< pub struct ToBridgeHubRococoXcmBlobHauler; pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO: LaneId = [0, 0, 0, 1]; impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { - type SenderChain = bp_bridge_hub_wococo::BridgeHubWococo; type MessageSender = pallet_bridge_messages::Pallet; @@ -80,8 +78,6 @@ impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { /// Messaging Bridge configuration for BridgeHubWococo -> BridgeHubRococo pub struct WithBridgeHubRococoMessageBridge; impl MessageBridge for WithBridgeHubRococoMessageBridge { - // TODO:check-parameter - relayers rewards - const RELAYER_FEE_PERCENT: u32 = 0; const THIS_CHAIN_ID: ChainId = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; const BRIDGED_CHAIN_ID: ChainId = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; const BRIDGED_MESSAGES_PALLET_NAME: &'static str = @@ -93,15 +89,6 @@ impl MessageBridge for WithBridgeHubRococoMessageBridge { BridgeParachainRococoInstance, bp_bridge_hub_rococo::BridgeHubRococo, >; - - fn bridged_balance_to_this_balance( - bridged_balance: bridge_runtime_common::messages::BalanceOf>, - bridged_to_this_conversion_rate_override: Option, - ) -> bridge_runtime_common::messages::BalanceOf> { - log::info!("[WithBridgeHubRococoMessageBridge] bridged_balance_to_this_balance (returns 0 balance, TODO: fix) - bridged_balance: {:?}, bridged_to_this_conversion_rate_override: {:?}", bridged_balance, bridged_to_this_conversion_rate_override); - // TODO:check-parameter - any payment? from sovereign account? - 0 - } } /// Message verifier for BridgeHubRococo messages sent from BridgeHubWococo @@ -120,14 +107,14 @@ impl UnderlyingChainProvider for BridgeHubRococo { type Chain = bp_bridge_hub_rococo::BridgeHubRococo; } -impl SourceHeaderChain for BridgeHubRococo { +impl SourceHeaderChain for BridgeHubRococo { type Error = &'static str; type MessagesProof = FromBridgedChainMessagesProof; fn verify_messages_proof( proof: Self::MessagesProof, messages_count: u32, - ) -> Result>, Self::Error> { + ) -> Result, Self::Error> { bridge_runtime_common::messages::target::verify_messages_proof::< WithBridgeHubRococoMessageBridge, >(proof, messages_count) @@ -155,40 +142,6 @@ impl messages::BridgedChainWithMessages for BridgeHubRococo { fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { true } - - fn estimate_delivery_transaction( - message_payload: &[u8], - include_pay_dispatch_fee_cost: bool, - message_dispatch_weight: Weight, - ) -> MessageTransaction { - let message_payload_len = u32::try_from(message_payload.len()).unwrap_or(u32::MAX); - let extra_bytes_in_payload = message_payload_len - .saturating_sub(pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH); - - MessageTransaction { - dispatch_weight: bp_bridge_hub_rococo::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT - .saturating_mul(extra_bytes_in_payload as u64) - .saturating_add(bp_bridge_hub_rococo::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT) - .saturating_sub(if include_pay_dispatch_fee_cost { - Weight::from_ref_time(0) - } else { - bp_bridge_hub_rococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT - }) - .saturating_add(message_dispatch_weight), - size: message_payload_len - .saturating_add(bp_bridge_hub_wococo::EXTRA_STORAGE_PROOF_SIZE) - .saturating_add(bp_bridge_hub_rococo::TX_EXTRA_BYTES), - } - } - - fn transaction_payment(transaction: MessageTransaction) -> messages::BalanceOf { - log::info!( - "[BridgeHubRococo::BridgedChainWithMessages] transaction_payment (returns 0 balance, TODO: fix) - transaction: {:?}", - transaction - ); - // TODO:check-parameter - any payment? from sovereign account? - 0 - } } /// BridgeHubWococo chain from message lane point of view. @@ -202,12 +155,6 @@ impl UnderlyingChainProvider for BridgeHubWococo { impl ThisChainWithMessages for BridgeHubWococo { type RuntimeOrigin = crate::RuntimeOrigin; type RuntimeCall = crate::RuntimeCall; - type ConfirmationTransactionEstimation = BasicConfirmationTransactionEstimation< - bp_bridge_hub_wococo::AccountId, - { bp_bridge_hub_wococo::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT.ref_time() }, - { bp_bridge_hub_rococo::EXTRA_STORAGE_PROOF_SIZE }, - { bp_bridge_hub_wococo::TX_EXTRA_BYTES }, - >; fn is_message_accepted(origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool { log::info!("[BridgeHubWococo::ThisChainWithMessages] is_message_accepted - origin: {:?}, lane: {:?}", origin, lane); @@ -220,13 +167,4 @@ impl ThisChainWithMessages for BridgeHubWococo { ); MessageNonce::MAX / 2 } - - fn transaction_payment(transaction: MessageTransaction) -> messages::BalanceOf { - log::info!( - "[BridgeHubWococo::ThisChainWithMessages] transaction_payment (returns 0 balance, TODO: fix) - transaction: {:?}", - transaction - ); - // TODO:check-parameter - any payment? from sovereign account? - 0 - } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 61063c82970..ba0c67cb2b0 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -216,7 +216,6 @@ pub mod runtime_api { use bp_messages::{ InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, }; - use frame_support::{sp_runtime::FixedU128, Parameter}; use sp_std::prelude::Vec; bp_runtime::decl_bridge_messages_runtime_apis!(bridge_hub_rococo); bp_runtime::decl_bridge_messages_runtime_apis!(bridge_hub_wococo); @@ -522,8 +521,7 @@ impl pallet_bridge_messages::Config for Run type RuntimeEvent = RuntimeEvent; type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; type BridgedChainId = bridge_hub_rococo_config::BridgeHubWococoChainId; - type Parameter = (); - type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; + type ActiveOutboundLanes = bridge_hub_rococo_config::ActiveOutboundLanesToBridgeHubWococo; type MaxUnrewardedRelayerEntriesAtInboundLane = bridge_hub_rococo_config::MaxUnrewardedRelayerEntriesAtInboundLane; type MaxUnconfirmedMessagesAtInboundLane = @@ -532,17 +530,13 @@ impl pallet_bridge_messages::Config for Run type MaximalOutboundPayloadSize = bridge_hub_rococo_config::ToBridgeHubWococoMaximalOutboundPayloadSize; type OutboundPayload = XcmAsPlainPayload; - type OutboundMessageFee = Balance; type InboundPayload = XcmAsPlainPayload; - type InboundMessageFee = Balance; type InboundRelayer = AccountId; type TargetHeaderChain = bridge_hub_rococo_config::BridgeHubWococo; type LaneMessageVerifier = bridge_hub_rococo_config::ToBridgeHubWococoMessageVerifier; type MessageDeliveryAndDispatchPayment = (); - type OnMessageAccepted = (); - type OnDeliveryConfirmed = (); type SourceHeaderChain = bridge_hub_rococo_config::BridgeHubWococo; type MessageDispatch = XcmBlobMessageDispatch< @@ -550,6 +544,7 @@ impl pallet_bridge_messages::Config for Run bp_bridge_hub_rococo::BridgeHubRococo, OnBridgeHubRococoBlobDispatcher, >; + } /// Add XCM messages support for BrigdeHubWococo to support Wococo->Rococo XCM messages @@ -558,8 +553,7 @@ impl pallet_bridge_messages::Config for Run type RuntimeEvent = RuntimeEvent; type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; type BridgedChainId = bridge_hub_wococo_config::BridgeHubRococoChainId; - type Parameter = (); - type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; + type ActiveOutboundLanes = bridge_hub_wococo_config::ActiveOutboundLanesToBridgeHubRococo; type MaxUnrewardedRelayerEntriesAtInboundLane = bridge_hub_wococo_config::MaxUnrewardedRelayerEntriesAtInboundLane; type MaxUnconfirmedMessagesAtInboundLane = @@ -568,17 +562,13 @@ impl pallet_bridge_messages::Config for Run type MaximalOutboundPayloadSize = bridge_hub_wococo_config::ToBridgeHubRococoMaximalOutboundPayloadSize; type OutboundPayload = XcmAsPlainPayload; - type OutboundMessageFee = Balance; type InboundPayload = XcmAsPlainPayload; - type InboundMessageFee = Balance; type InboundRelayer = AccountId; type TargetHeaderChain = bridge_hub_wococo_config::BridgeHubRococo; type LaneMessageVerifier = bridge_hub_wococo_config::ToBridgeHubRococoMessageVerifier; type MessageDeliveryAndDispatchPayment = (); - type OnMessageAccepted = (); - type OnDeliveryConfirmed = (); type SourceHeaderChain = bridge_hub_wococo_config::BridgeHubRococo; type MessageDispatch = XcmBlobMessageDispatch< @@ -797,21 +787,15 @@ impl_runtime_apis! { } } - // This exposed by BridgeHubRococo - impl runtime_api::ToBridgeHubWococoOutboundLaneApi for Runtime { - fn estimate_message_delivery_and_dispatch_fee( - _lane_id: bp_messages::LaneId, - payload: XcmAsPlainPayload, - conversion_rate: Option, - ) -> Option { - None - } + // TODO: add here other directions + // This exposed by BridgeHubRococo + impl runtime_api::ToBridgeHubWococoOutboundLaneApi for Runtime { fn message_details( lane: bp_messages::LaneId, begin: bp_messages::MessageNonce, end: bp_messages::MessageNonce, - ) -> Vec> { + ) -> Vec { bridge_runtime_common::messages_api::outbound_message_details::< Runtime, WithBridgeHubWococoMessagesInstance, @@ -820,10 +804,10 @@ impl_runtime_apis! { } // This is exposed by BridgeHubWococo - impl runtime_api::FromBridgeHubRococoInboundLaneApi for Runtime { + impl runtime_api::FromBridgeHubRococoInboundLaneApi for Runtime { fn message_details( lane: bp_messages::LaneId, - messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, ) -> Vec { bridge_runtime_common::messages_api::inbound_message_details::< Runtime, From 1a68129afb9cc04150bcff13d669ccfadd9315fc Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 23 Nov 2022 15:08:59 +0100 Subject: [PATCH 105/263] Zombienet for bridge-hub setup --- parachains/runtimes/bridge-hubs/README.md | 43 +++++++++++--- scripts/bridges_rococo_wococo.sh | 4 +- .../bridge_hub_rococo_local_network.toml | 56 +++++++++++++++++++ .../bridge_hub_wococo_local_network.toml | 56 +++++++++++++++++++ .../0004-run_bridge_hubs_rococo.toml | 37 ------------ .../0004-run_bridge_hubs_wococo.toml | 37 ------------ 6 files changed, 150 insertions(+), 83 deletions(-) create mode 100644 zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml create mode 100644 zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml delete mode 100644 zombienet_tests/0004-run_bridge_hubs_rococo.toml delete mode 100644 zombienet_tests/0004-run_bridge_hubs_wococo.toml diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 1509df525b1..d2065553e3e 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -41,6 +41,22 @@ cargo build -p substrate-relay cp target/release/substrate-relay ~/local_bridge_testing/bin/substrate-relay ``` +### Run chains (Rococo + BridgeHub, Wococo + BridgeHub) with zombienet + +``` +# Rococo + BridgeHubWococo +POLKADOT_BINARY_PATH=../../polkadot/target/release/polkadot \ +POLKADOT_PARACHAIN_BINARY_PATH=./target/release/polkadot-parachain \ + ./zombienet-linux --provider native spawn ./zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml +``` + +``` +# Wococo + BridgeHubWococo +POLKADOT_BINARY_PATH=../../polkadot/target/release/polkadot \ +POLKADOT_PARACHAIN_BINARY_PATH=./target/release/polkadot-parachain \ + ./zombienet-linux --provider native spawn ./zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml +``` + ### Run chains (Rococo + BridgeHub, Wococo + BridgeHub) from `./scripts/bridges_rococo_wococo.sh` ``` @@ -75,7 +91,7 @@ or RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay init-bridge rococo-to-bridge-hub-wococo \ --source-host localhost \ - --source-port 48943 \ + --source-port 9942 \ --target-host localhost \ --target-port 8945 \ --target-signer //Bob @@ -84,7 +100,7 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay init-bridge wococo-to-bridge-hub-rococo \ --source-host localhost \ - --source-port 48945 \ + --source-port 9945 \ --target-host localhost \ --target-port 8943 \ --target-signer //Bob @@ -100,7 +116,7 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay relay-headers rococo-to-bridge-hub-wococo \ --source-host localhost \ - --source-port 48943 \ + --source-port 9942 \ --target-host localhost \ --target-port 8945 \ --target-signer //Bob \ @@ -110,7 +126,7 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay relay-headers wococo-to-bridge-hub-rococo \ --source-host localhost \ - --source-port 48945 \ + --source-port 9945 \ --target-host localhost \ --target-port 8943 \ --target-signer //Bob \ @@ -134,7 +150,7 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay relay-parachains bridge-hub-rococo-to-bridge-hub-wococo \ --source-host localhost \ - --source-port 48943 \ + --source-port 9942 \ --target-host localhost \ --target-port 8945 \ --target-signer //Bob \ @@ -144,7 +160,7 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay relay-parachains bridge-hub-wococo-to-bridge-hub-rococo \ --source-host localhost \ - --source-port 48945 \ + --source-port 9945 \ --target-host localhost \ --target-port 8943 \ --target-signer //Bob \ @@ -176,7 +192,20 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ --target-transactions-mortality=4 \ --lane 00000002 ``` -TODO: + +``` +# Wococo -> Rococo +RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay relay-messages bridge-hub-wococo-to-bridge-hub-rococo \ + --source-host localhost \ + --source-port 8945 \ + --source-signer //Charlie \ + --target-host localhost \ + --target-port 8943 \ + --target-signer //Charlie \ + --target-transactions-mortality=4 \ + --lane 00000001 +``` --- diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh index 53d7dc974aa..f6d2630af6e 100755 --- a/scripts/bridges_rococo_wococo.sh +++ b/scripts/bridges_rococo_wococo.sh @@ -138,7 +138,7 @@ case "$1" in RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay init-bridge rococo-to-bridge-hub-wococo \ --source-host localhost \ - --source-port 48943 \ + --source-port 9942 \ --target-host localhost \ --target-port 8945 \ --target-signer //Bob @@ -148,7 +148,7 @@ case "$1" in RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay init-bridge wococo-to-bridge-hub-rococo \ --source-host localhost \ - --source-port 48945 \ + --source-port 9945 \ --target-host localhost \ --target-port 8943 \ --target-signer //Bob diff --git a/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml b/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml new file mode 100644 index 00000000000..e6ca8b893f7 --- /dev/null +++ b/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml @@ -0,0 +1,56 @@ +[relaychain] +default_command = "{{POLKADOT_BINARY_PATH}}" +default_args = [ "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace" ] +chain = "rococo-local" + + [[relaychain.nodes]] + name = "alice-validator" + validator = true + rpc_port = 9932 + ws_port = 9942 + extra_args = ["--no-mdns --bootnodes {{'bob-validator'|zombie('multiAddress')}}"] + + [[relaychain.nodes]] + name = "bob-validator" + validator = true + rpc_port = 9933 + ws_port = 9943 + extra_args = ["--no-mdns --bootnodes {{'alice-validator'|zombie('multiAddress')}}"] + + [[relaychain.nodes]] + name = "charlie-validator" + validator = true + rpc_port = 9934 + ws_port = 9944 + extra_args = ["--no-mdns --bootnodes {{'alice-validator'|zombie('multiAddress')}}"] + +[[parachains]] +id = 1013 +chain = "bridge-hub-rococo-local" +cumulus_based = true + + # run alice as parachain collator + [[parachains.collators]] + name = "alice-collator" + validator = true + command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + rpc_port = 8933 + ws_port = 8943 + extra_args = [ + "-lparachain=debug,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", + "--force-authoring", "--no-mdns", "--bootnodes {{'bob-collator'|zombie('multiAddress')}}", + "-- --port 41333 --rpc-port 48933 --ws-port 48943 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" + ] + + # run bob as parachain collator + [[parachains.collators]] + name = "bob-collator" + validator = true + command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + rpc_port = 8934 + ws_port = 8944 + extra_args = [ + "-lparachain=trace,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", + "--force-authoring", "--no-mdns", "--bootnodes {{'alice-collator'|zombie('multiAddress')}}", + "-- --port 41334 --rpc-port 48934 --ws-port 48944 --no-mdns", "--bootnodes {{'bob-validator'|zombie('multiAddress')}}" + ] diff --git a/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml b/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml new file mode 100644 index 00000000000..cc287419616 --- /dev/null +++ b/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml @@ -0,0 +1,56 @@ +[relaychain] +default_command = "{{POLKADOT_BINARY_PATH}}" +default_args = [ "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace" ] +chain = "wococo-local" + + [[relaychain.nodes]] + name = "alice-validator-wo" + validator = true + rpc_port = 9935 + ws_port = 9945 + extra_args = ["--no-mdns --bootnodes {{'bob-validator-wo'|zombie('multiAddress')}}"] + + [[relaychain.nodes]] + name = "bob-validator-wo" + validator = true + rpc_port = 9936 + ws_port = 9946 + extra_args = ["--no-mdns --bootnodes {{'alice-validator-wo'|zombie('multiAddress')}}"] + + [[relaychain.nodes]] + name = "charlie-validator-wo" + validator = true + rpc_port = 9937 + ws_port = 9947 + extra_args = ["--no-mdns --bootnodes {{'alice-validator-wo'|zombie('multiAddress')}}"] + +[[parachains]] +id = 1014 +chain = "bridge-hub-wococo-local" +cumulus_based = true + + # run alice as parachain collator + [[parachains.collators]] + name = "alice-collator-wo" + validator = true + command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + rpc_port = 8935 + ws_port = 8945 + extra_args = [ + "-lparachain=debug,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", + "--force-authoring", "--no-mdns", "--bootnodes {{'bob-collator-wo'|zombie('multiAddress')}}", + "-- --port 41335 --rpc-port 48935 --ws-port 48945 --no-mdns", "--bootnodes {{'alice-validator-wo'|zombie('multiAddress')}}" + ] + + # run bob as parachain collator + [[parachains.collators]] + name = "bob-collator-wo" + validator = true + command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" + rpc_port = 8936 + ws_port = 8946 + extra_args = [ + "-lparachain=trace,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", + "--force-authoring", "--no-mdns", "--bootnodes {{'alice-collator-wo'|zombie('multiAddress')}}", + "-- --port 41336 --rpc-port 48936 --ws-port 48946 --no-mdns", "--bootnodes {{'bob-validator-wo'|zombie('multiAddress')}}" + ] diff --git a/zombienet_tests/0004-run_bridge_hubs_rococo.toml b/zombienet_tests/0004-run_bridge_hubs_rococo.toml deleted file mode 100644 index 9c9c9c49d15..00000000000 --- a/zombienet_tests/0004-run_bridge_hubs_rococo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[relaychain] -default_command = "{{POLKADOT_BINARY_PATH}}" -default_args = [ "-lparachain=debug" ] - -chain = "rococo-local" - - [[relaychain.nodes]] - name = "alice" - validator = true - - [[relaychain.nodes]] - name = "bob" - validator = true - - [[relaychain.nodes]] - name = "charlie" - validator = true - -[[parachains]] -id = 1013 -cumulus_based = true -chain = "bridge-hub-rococo-local" - - [[parachains.collators]] - name = "alice" - command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" - args = ["-lparachain=debug"] - - [[parachains.collators]] - name = "bob" - command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" - args = ["-lparachain=debug"] - - [[parachains.collators]] - name = "charlie" - command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" - args = ["-lparachain=debug"] diff --git a/zombienet_tests/0004-run_bridge_hubs_wococo.toml b/zombienet_tests/0004-run_bridge_hubs_wococo.toml deleted file mode 100644 index b938b1672dd..00000000000 --- a/zombienet_tests/0004-run_bridge_hubs_wococo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[relaychain] -default_command = "{{POLKADOT_BINARY_PATH}}" -default_args = [ "-lparachain=debug" ] - -chain = "wococo-local" - - [[relaychain.nodes]] - name = "alice" - validator = true - - [[relaychain.nodes]] - name = "bob" - validator = true - - [[relaychain.nodes]] - name = "charlie" - validator = true - -[[parachains]] -id = 1013 -cumulus_based = true -chain = "bridge-hub-wococo-local" - - [[parachains.collators]] - name = "alice" - command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" - args = ["-lparachain=debug"] - - [[parachains.collators]] - name = "bob" - command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" - args = ["-lparachain=debug"] - - [[parachains.collators]] - name = "charlie" - command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" - args = ["-lparachain=debug"] From fa505c0a8188f276013c98bfe6d0de4186415e00 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 23 Nov 2022 22:54:43 +0100 Subject: [PATCH 106/263] Fixes --- .../src/bridge_common_config.rs | 23 ++++- .../src/bridge_hub_rococo_config.rs | 3 +- .../src/bridge_hub_wococo_config.rs | 3 +- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 32 ++++++- .../bridge-hub-rococo/src/weights/xcm/mod.rs | 7 +- .../bridge-hub-rococo/src/xcm_config.rs | 87 ++++--------------- .../src/chain_spec/bridge_hubs.rs | 4 +- 7 files changed, 77 insertions(+), 82 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 5a2a322d96d..fd1672d9fcb 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -50,6 +50,7 @@ impl) -> Weight { log::error!( + target: crate::LOG_TARGET, "[XcmBlobMessageDispatch] TODO: change here to XCMv3 dispatch_weight with XcmExecutor - message: ?...?", ); // TODO:check-parameter - setup weight? @@ -60,11 +61,20 @@ impl, message: DispatchMessage, ) -> MessageDispatchResult { - log::warn!("[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob triggering - message_nonce: {:?}", message.key.nonce); + log::warn!( + target: crate::LOG_TARGET, + "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob triggering - message_nonce: {:?}", + message.key.nonce + ); let payload = match message.data.payload { Ok(payload) => payload, Err(e) => { - log::error!("[XcmBlobMessageDispatch] payload error: {:?} - message_nonce: {:?}", e, message.key.nonce); + log::error!( + target: crate::LOG_TARGET, + "[XcmBlobMessageDispatch] payload error: {:?} - message_nonce: {:?}", + e, + message.key.nonce + ); return MessageDispatchResult { // TODO:check-parameter - setup uspent_weight? unspent_weight: Weight::zero(), @@ -73,7 +83,11 @@ impl log::debug!("[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}", message.key.nonce), + Ok(_) => log::debug!( + target: crate::LOG_TARGET, + "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}", + message.key.nonce + ), Err(e) => { let e = match e { DispatchBlobError::Unbridgable => "DispatchBlobError::Unbridgable", @@ -88,6 +102,7 @@ impl "DispatchBlobError::WrongGlobal", }; log::error!( + target: crate::LOG_TARGET, "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?} - message_nonce: {:?}", e, message.key.nonce ); @@ -129,7 +144,7 @@ impl HaulBlob for XcmBlobHaulerAdapter { hash }) .map_err(|e| e); - log::info!(target: "runtime::bridge-hub", "haul_blob result: {:?} on lane: {:?}", result, lane); + log::info!(target: crate::LOG_TARGET, "haul_blob result: {:?} on lane: {:?}", result, lane); result.expect("failed to process: TODO:check-parameter - wait for origin/gav-xcm-v3, there is a comment about handliing errors for HaulBlob"); } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index 2a8e00bb7a0..383189aff30 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -157,12 +157,13 @@ impl ThisChainWithMessages for BridgeHubRococo { type RuntimeCall = crate::RuntimeCall; fn is_message_accepted(origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool { - log::info!("[BridgeHubRococo::ThisChainWithMessages] is_message_accepted - origin: {:?}, lane: {:?}", origin, lane); + log::info!(target: crate::LOG_TARGET, "[BridgeHubRococo::ThisChainWithMessages] is_message_accepted - origin: {:?}, lane: {:?}", origin, lane); lane == &DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO } fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { log::info!( + target: crate::LOG_TARGET, "[BridgeHubRococo::ThisChainWithMessages] maximal_pending_messages_at_outbound_lane" ); MessageNonce::MAX / 2 diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 2c80ca78070..8973d9f179c 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -157,12 +157,13 @@ impl ThisChainWithMessages for BridgeHubWococo { type RuntimeCall = crate::RuntimeCall; fn is_message_accepted(origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool { - log::info!("[BridgeHubWococo::ThisChainWithMessages] is_message_accepted - origin: {:?}, lane: {:?}", origin, lane); + log::info!(target: crate::LOG_TARGET, "[BridgeHubWococo::ThisChainWithMessages] is_message_accepted - origin: {:?}, lane: {:?}", origin, lane); lane == &DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO } fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { log::info!( + target: crate::LOG_TARGET, "[BridgeHubWococo::ThisChainWithMessages] maximal_pending_messages_at_outbound_lane" ); MessageNonce::MAX / 2 diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index ba0c67cb2b0..7d4f003ac2b 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -83,6 +83,8 @@ use parachains_common::{ use xcm::latest::prelude::BodyId; use xcm_executor::XcmExecutor; +pub const LOG_TARGET: &str = "runtime::bridge-hub"; + /// Balance of an account. pub type Balance = u128; @@ -544,7 +546,6 @@ impl pallet_bridge_messages::Config for Run bp_bridge_hub_rococo::BridgeHubRococo, OnBridgeHubRococoBlobDispatcher, >; - } /// Add XCM messages support for BrigdeHubWococo to support Wococo->Rococo XCM messages @@ -787,9 +788,19 @@ impl_runtime_apis! { } } - // TODO: add here other directions - // This exposed by BridgeHubRococo + impl runtime_api::FromBridgeHubWococoInboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + ) -> Vec { + bridge_runtime_common::messages_api::inbound_message_details::< + Runtime, + WithBridgeHubWococoMessagesInstance, + >(lane, messages) + } + } + impl runtime_api::ToBridgeHubWococoOutboundLaneApi for Runtime { fn message_details( lane: bp_messages::LaneId, @@ -816,10 +827,23 @@ impl_runtime_apis! { } } + impl runtime_api::ToBridgeHubRococoOutboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + begin: bp_messages::MessageNonce, + end: bp_messages::MessageNonce, + ) -> Vec { + bridge_runtime_common::messages_api::outbound_message_details::< + Runtime, + WithBridgeHubRococoMessagesInstance, + >(lane, begin, end) + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade() -> (Weight, Weight) { - log::info!("try-runtime::on_runtime_upgrade parachain-template."); + log::info!(target: LOG_TARGET, "try-runtime::on_runtime_upgrade parachain-template."); let weight = Executive::try_runtime_upgrade().unwrap(); (weight, RuntimeBlockWeights::get().max_block) } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs index 6706efe1757..7b79e1103bc 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs @@ -214,7 +214,12 @@ impl XcmWeightInfo for BridgeHubXcmWeight { Weight::MAX.ref_time() } fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> XCMWeight { - Weight::MAX.ref_time() + log::error!( + target: crate::LOG_TARGET, + "TODO: Calling weight: export_message -> triggers unpaid_execution -> need fix here!" + ); + // TODO:check-parameter - add correct weight also add it to the polkadot gav-xcm-v3 + XcmGeneric::::unpaid_execution().ref_time() } fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> XCMWeight { Weight::MAX.ref_time() diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index c825e780716..fecffebaedf 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -29,6 +29,7 @@ use frame_support::{ }; use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; +use sp_core::Get; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, @@ -41,11 +42,24 @@ use xcm_executor::{traits::ExportXcm, XcmExecutor}; parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); - // TODO: hack: hardcoded Polkadot? - pub const RelayNetwork: NetworkId = NetworkId::Rococo; pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); - pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); + pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())); +} + +pub struct RelayNetwork; +impl Get> for RelayNetwork { + fn get() -> Option { + Some(Self::get()) + } +} +impl Get for RelayNetwork { + fn get() -> NetworkId { + match u32::from(ParachainInfo::parachain_id()) { + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID => NetworkId::Rococo, + bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID => NetworkId::Wococo, + para_id => unreachable!("Not supported for para_id: {}", para_id), + } + } } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used @@ -107,71 +121,6 @@ match_types! { }; } -//TODO: move DenyThenTry to polkadot's xcm module. -// /// Deny executing the xcm message if it matches any of the Deny filter regardless of anything else. -// /// If it passes the Deny, and matches one of the Allow cases then it is let through. -// pub struct DenyThenTry(PhantomData, PhantomData) -// where -// Deny: ShouldExecute, -// Allow: ShouldExecute; -// -// impl ShouldExecute for DenyThenTry -// where -// Deny: ShouldExecute, -// Allow: ShouldExecute, -// { -// fn should_execute( -// origin: &MultiLocation, -// message: &mut Xcm, -// max_weight: Weight, -// weight_credit: &mut Weight, -// ) -> Result<(), ()> { -// Deny::should_execute(origin, message, max_weight, weight_credit)?; -// Allow::should_execute(origin, message, max_weight, weight_credit) -// } -// } - -// TODO: hacked -// See issue #5233 -// pub struct DenyReserveTransferToRelayChain; -// impl ShouldExecute for DenyReserveTransferToRelayChain { -// fn should_execute( -// origin: &MultiLocation, -// message: &mut Xcm, -// _max_weight: Weight, -// _weight_credit: &mut Weight, -// ) -> Result<(), ()> { -// if message.0.iter().any(|inst| { -// matches!( -// inst, -// InitiateReserveWithdraw { -// reserve: MultiLocation { parents: 1, interior: Here }, -// .. -// } | DepositReserveAsset { dest: MultiLocation { parents: 1, interior: Here }, .. } | -// TransferReserveAsset { -// dest: MultiLocation { parents: 1, interior: Here }, -// .. -// } -// ) -// }) { -// return Err(()) // Deny -// } -// -// // An unexpected reserve transfer has arrived from the Relay Chain. Generally, `IsReserve` -// // should not allow this, but we just log it here. -// if matches!(origin, MultiLocation { parents: 1, interior: Here }) && -// message.0.iter().any(|inst| matches!(inst, ReserveAssetDeposited { .. })) -// { -// log::warn!( -// target: "xcm::barriers", -// "Unexpected ReserveAssetDeposited from the Relay Chain", -// ); -// } -// // Permit everything else -// Ok(()) -// } -// } - match_types! { pub type ParentOrParentsUnitPlurality: impl Contains = { MultiLocation { parents: 1, interior: Here } | diff --git a/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/polkadot-parachain/src/chain_spec/bridge_hubs.rs index bdb1326f0f0..96072825f00 100644 --- a/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -96,7 +96,7 @@ impl BridgeHubRuntimeType { "Wococo BridgeHub", ChainType::Live, "wococo", - ParaId::new(1013), + ParaId::new(1014), None, None, ))) @@ -110,7 +110,7 @@ impl BridgeHubRuntimeType { "Wococo BridgeHub Local", ChainType::Local, "wococo-local", - ParaId::new(1013), + ParaId::new(1014), Some("Alice".to_string()), Some("Bob".to_string()), ))), From 2d4603d610fee1495c321fed44d41c114b402f76 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Mon, 28 Nov 2022 21:10:37 +0900 Subject: [PATCH 107/263] Remove unused import --- parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs | 2 +- parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs | 2 +- parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs b/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs index 9ea341c9c37..58823232786 100644 --- a/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs @@ -21,7 +21,7 @@ use crate::Runtime; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; -use sp_std::{cmp, prelude::*}; +use sp_std::prelude::*; use xcm::{latest::prelude::*, DoubleEncoded}; trait WeighMultiAssets { diff --git a/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs b/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs index f440f7b769a..b1a84cb9ccb 100644 --- a/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs @@ -21,7 +21,7 @@ use crate::Runtime; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; -use sp_std::{cmp, prelude::*}; +use sp_std::prelude::*; use xcm::{latest::prelude::*, DoubleEncoded}; trait WeighMultiAssets { diff --git a/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs b/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs index 0726040167c..f44e74648e2 100644 --- a/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs @@ -21,7 +21,7 @@ use crate::Runtime; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; -use sp_std::{cmp, prelude::*}; +use sp_std::prelude::*; use xcm::{latest::prelude::*, DoubleEncoded}; trait WeighMultiAssets { From db188330bc03bc5390ec01f62cc604c9f5e211be Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 29 Nov 2022 09:03:56 +0100 Subject: [PATCH 108/263] Fixes for gav-xcm-v3 (#1835) * Fix for FungiblesAdapter - trait changes: Contains -> AssetChecking * Fix for missing weight for `fn unpaid_execution()` * Used NonLocalMint for all NonZeroIssuance * Fix --- Cargo.lock | 1 + parachains/common/Cargo.toml | 1 + parachains/runtimes/assets/statemine/src/xcm_config.rs | 4 ++-- parachains/runtimes/assets/statemint/src/xcm_config.rs | 4 ++-- parachains/runtimes/assets/westmint/src/xcm_config.rs | 4 ++-- .../runtimes/contracts/contracts-rococo/src/xcm_config.rs | 2 -- parachains/runtimes/testing/penpal/src/xcm_config.rs | 8 ++++---- parachains/runtimes/testing/rococo-parachain/src/lib.rs | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78f4386917f..1f4dedb02ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6529,6 +6529,7 @@ dependencies = [ "sp-std", "substrate-wasm-builder", "xcm", + "xcm-builder", "xcm-executor", ] diff --git a/parachains/common/Cargo.toml b/parachains/common/Cargo.toml index 6dca0393bc8..06735d497c1 100644 --- a/parachains/common/Cargo.toml +++ b/parachains/common/Cargo.toml @@ -28,6 +28,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", default-features = f # Polkadot polkadot-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } # Cumulus diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index c31c01630af..9c37035bc35 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -35,7 +35,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, @@ -96,7 +96,7 @@ pub type FungiblesTransactor = FungiblesAdapter< AccountId, // We only want to allow teleports of known assets. We use non-zero issuance as an indication // that this asset is known. - parachains_common::impls::NonZeroIssuance, + LocalMint>, // The account to use for tracking teleports. CheckingAccount, >; diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index adb771a7ae1..28b4d99e529 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -35,7 +35,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, @@ -96,7 +96,7 @@ pub type FungiblesTransactor = FungiblesAdapter< AccountId, // We only want to allow teleports of known assets. We use non-zero issuance as an indication // that this asset is known. - parachains_common::impls::NonZeroIssuance, + LocalMint>, // The account to use for tracking teleports. CheckingAccount, >; diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index bc4f3e52867..c99b66bb5c3 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -35,7 +35,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, + ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, @@ -96,7 +96,7 @@ pub type FungiblesTransactor = FungiblesAdapter< AccountId, // We only want to allow teleports of known assets. We use non-zero issuance as an indication // that this asset is known. - parachains_common::impls::NonZeroIssuance, + LocalMint>, // The account to use for tracking teleports. CheckingAccount, >; diff --git a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index ec0849eebdd..2cfb3e56729 100644 --- a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -42,8 +42,6 @@ parameter_types! { pub const RelayNetwork: Option = None; pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); - pub const Local: MultiLocation = Here.into_location(); - pub CheckingAccount: AccountId = PolkadotXcm::check_account(); pub const ExecutiveBody: BodyId = BodyId::Executive; } diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index 6c07188dddd..d3b7948bf70 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -45,9 +45,9 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, - IsConcrete, NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + IsConcrete, LocalMint, NativeAsset, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{ traits::{JustTry, ShouldExecute}, @@ -104,7 +104,7 @@ pub type FungiblesTransactor = FungiblesAdapter< AccountId, // We only want to allow teleports of known assets. We use non-zero issuance as an indication // that this asset is known. - NonZeroIssuance, + LocalMint>, // The account to use for tracking teleports. CheckingAccount, >; diff --git a/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 05592f71645..994a1bc3613 100644 --- a/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -66,7 +66,7 @@ use parachains_common::{ }; use xcm_builder::{ AllowKnownQueryResponses, AllowSubscriptionsFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, - FungiblesAdapter, + FungiblesAdapter, LocalMint, }; use xcm_executor::traits::JustTry; @@ -330,7 +330,7 @@ pub type FungiblesTransactor = FungiblesAdapter< AccountId, // We only want to allow teleports of known assets. We use non-zero issuance as an indication // that this asset is known. - NonZeroIssuance, + LocalMint>, // The account to use for tracking teleports. CheckingAccount, >; From 61c1cdf7d39d0efee4d361e2e7337c64ca25ffd1 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 29 Nov 2022 17:45:47 +0900 Subject: [PATCH 109/263] Fixes --- parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs | 2 +- parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs b/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs index 58823232786..eddabcd1dc1 100644 --- a/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs @@ -123,7 +123,7 @@ impl XcmWeightInfo for StatemineXcmWeight { assets.weigh_multi_assets(XcmFungibleWeight::::deposit_reserve_asset()) } fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets, _maximal: &bool) -> Weight { - Weight::MAX.ref_time() + Weight::MAX } fn initiate_reserve_withdraw( assets: &MultiAssetFilter, diff --git a/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs b/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs index f44e74648e2..5dfd526b7db 100644 --- a/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs @@ -123,7 +123,7 @@ impl XcmWeightInfo for WestmintXcmWeight { assets.weigh_multi_assets(XcmFungibleWeight::::deposit_reserve_asset()) } fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets, _maximal: &bool) -> Weight { - Weight::MAX.ref_time() + Weight::MAX } fn initiate_reserve_withdraw( assets: &MultiAssetFilter, From d30d218d06f536ec9b6c439bc9ba9e8abbdee830 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 29 Nov 2022 19:49:01 +0900 Subject: [PATCH 110/263] Fixes --- parachains/runtimes/assets/statemint/src/xcm_config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index 28b4d99e529..a4a018721fb 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -187,7 +187,7 @@ impl xcm_executor::Config for XcmConfig { cumulus_primitives_utility::TakeFirstAssetTrader< AccountId, AssetFeeAsExistentialDepositMultiplierFeeCharger, - ConvertedConcreteAssetId< + ConvertedConcreteId< AssetId, Balance, AsPrefixedGeneralIndex, From 2a8678da909a097c1b51695466fde7dec6d5e8a2 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 29 Nov 2022 20:16:12 +0900 Subject: [PATCH 111/263] Fixes --- pallets/xcmp-queue/src/mock.rs | 2 +- parachain-template/runtime/src/xcm_config.rs | 2 +- parachains/runtimes/starters/shell/src/xcm_config.rs | 4 ++-- parachains/runtimes/testing/rococo-parachain/src/lib.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pallets/xcmp-queue/src/mock.rs b/pallets/xcmp-queue/src/mock.rs index bd71ba363e5..2215bd59d7c 100644 --- a/pallets/xcmp-queue/src/mock.rs +++ b/pallets/xcmp-queue/src/mock.rs @@ -117,7 +117,7 @@ impl cumulus_pallet_parachain_system::Config for Test { parameter_types! { pub const RelayChain: MultiLocation = MultiLocation::parent(); pub UniversalLocation: InteriorMultiLocation = X1(Parachain(1u32.into())).into(); - pub UnitWeightCost: u64 = 1_000_000; + pub UnitWeightCost: Weight = Weight::from_parts(1_000_000, 1024); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } diff --git a/parachain-template/runtime/src/xcm_config.rs b/parachain-template/runtime/src/xcm_config.rs index d02befb0a95..f5900cff6a3 100644 --- a/parachain-template/runtime/src/xcm_config.rs +++ b/parachain-template/runtime/src/xcm_config.rs @@ -76,7 +76,7 @@ pub type XcmOriginToTransactDispatchOrigin = ( parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: u64 = 1_000_000_000; + pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } diff --git a/parachains/runtimes/starters/shell/src/xcm_config.rs b/parachains/runtimes/starters/shell/src/xcm_config.rs index acc02df42af..1bbdf15b44f 100644 --- a/parachains/runtimes/starters/shell/src/xcm_config.rs +++ b/parachains/runtimes/starters/shell/src/xcm_config.rs @@ -17,7 +17,7 @@ use super::{ AccountId, AllPalletsWithSystem, ParachainInfo, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, }; -use frame_support::{match_types, parameter_types, traits::Nothing}; +use frame_support::{match_types, parameter_types, traits::Nothing, weights::Weight}; use xcm::latest::prelude::*; use xcm_builder::{ AllowUnpaidExecutionFrom, FixedWeightBounds, ParentAsSuperuser, ParentIsPreset, @@ -49,7 +49,7 @@ match_types! { parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: u64 = 1_000_000_000; + pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } diff --git a/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 994a1bc3613..bcb13ecef4b 100644 --- a/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -363,7 +363,7 @@ pub type XcmOriginToTransactDispatchOrigin = ( parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: u64 = 1_000_000_000; + pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); // One ROC buys 1 second of weight. pub const WeightPrice: (MultiLocation, u128) = (MultiLocation::parent(), ROC); pub const MaxInstructions: u32 = 100; From b1e8b3765a8625ed5061b43b2975dcf88b6deb20 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 29 Nov 2022 20:44:53 +0900 Subject: [PATCH 112/263] Fixes --- parachains/runtimes/testing/rococo-parachain/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/parachains/runtimes/testing/rococo-parachain/src/lib.rs index bcb13ecef4b..724a0633501 100644 --- a/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -461,6 +461,7 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; } impl cumulus_pallet_xcm::Config for Runtime { From fad4c13942e9c10eeb71ce9e5e3fdf6b9a191aad Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 29 Nov 2022 21:18:29 +0900 Subject: [PATCH 113/263] Fixes --- parachain-template/runtime/src/xcm_config.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/parachain-template/runtime/src/xcm_config.rs b/parachain-template/runtime/src/xcm_config.rs index f5900cff6a3..5d7dc8ca5ff 100644 --- a/parachain-template/runtime/src/xcm_config.rs +++ b/parachain-template/runtime/src/xcm_config.rs @@ -6,6 +6,7 @@ use core::marker::PhantomData; use frame_support::{ log, match_types, parameter_types, traits::{ConstU32, Everything, Nothing}, + weights::Weight, }; use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; From f1e7e18ec8b023be71ac04f9bb48fcdcce75b889 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 29 Nov 2022 23:00:07 +0900 Subject: [PATCH 114/263] Fix tests --- pallets/xcmp-queue/src/tests.rs | 17 ++++++++++--- .../runtimes/assets/statemint/tests/tests.rs | 25 +++++++++---------- .../runtimes/assets/westmint/tests/tests.rs | 25 +++++++++---------- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/pallets/xcmp-queue/src/tests.rs b/pallets/xcmp-queue/src/tests.rs index 8710c9a71af..417edbb618f 100644 --- a/pallets/xcmp-queue/src/tests.rs +++ b/pallets/xcmp-queue/src/tests.rs @@ -46,6 +46,7 @@ fn bad_message_is_handled() { XcmpQueue::process_xcmp_message( 1000.into(), (1, format), + &mut 0, Weight::from_ref_time(10_000_000_000), Weight::from_ref_time(10_000_000_000), ); @@ -69,6 +70,7 @@ fn handle_blob_message() { XcmpQueue::process_xcmp_message( 1000.into(), (1, format), + &mut 0, Weight::from_ref_time(10_000_000_000), Weight::from_ref_time(10_000_000_000), ); @@ -86,6 +88,7 @@ fn handle_invalid_data() { XcmpQueue::process_xcmp_message( 1000.into(), (1, format), + &mut 0, Weight::from_ref_time(10_000_000_000), Weight::from_ref_time(10_000_000_000), ); @@ -96,7 +99,7 @@ fn handle_invalid_data() { fn service_overweight_unknown() { new_test_ext().execute_with(|| { assert_noop!( - XcmpQueue::service_overweight(RuntimeOrigin::root(), 0, 1000), + XcmpQueue::service_overweight(RuntimeOrigin::root(), 0, Weight::from_parts(1000, 1000)), Error::::BadOverweightIndex, ); }); @@ -109,7 +112,7 @@ fn service_overweight_bad_xcm_format() { Overweight::::insert(0, (ParaId::from(1000), 0, bad_xcm)); assert_noop!( - XcmpQueue::service_overweight(RuntimeOrigin::root(), 0, 1000), + XcmpQueue::service_overweight(RuntimeOrigin::root(), 0, Weight::from_parts(1000, 1000)), Error::::BadXcm ); }); @@ -187,9 +190,15 @@ fn update_threshold_weight_works() { new_test_ext().execute_with(|| { let data: QueueConfigData = >::get(); assert_eq!(data.threshold_weight, Weight::from_ref_time(100_000)); - assert_ok!(XcmpQueue::update_threshold_weight(RuntimeOrigin::root(), 10_000)); + assert_ok!(XcmpQueue::update_threshold_weight( + RuntimeOrigin::root(), + Weight::from_ref_time(10_000) + )); assert_noop!( - XcmpQueue::update_threshold_weight(RuntimeOrigin::signed(5), 10_000_000), + XcmpQueue::update_threshold_weight( + RuntimeOrigin::signed(5), + Weight::from_ref_time(10_000_000), + ), BadOrigin ); let data: QueueConfigData = >::get(); diff --git a/parachains/runtimes/assets/statemint/tests/tests.rs b/parachains/runtimes/assets/statemint/tests/tests.rs index 50c0f904eb1..b52383f3d3e 100644 --- a/parachains/runtimes/assets/statemint/tests/tests.rs +++ b/parachains/runtimes/assets/statemint/tests/tests.rs @@ -65,13 +65,13 @@ fn test_asset_xcm_trader() { // Because of the ED being higher in statemine // and not to complicate things, we use a little // bit more of weight - let bought = 400_000_000_000u64; + let bought = Weight::from_ref_time(400_000_000_000u64); // Lets calculate amount needed let asset_amount_needed = AssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles( local_asset_id, - Weight::from_ref_time(bought), + bought, ) .expect("failed to compute"); @@ -144,7 +144,7 @@ fn test_asset_xcm_trader_with_refund() { // Because of the ED being higher in statemine // and not to complicate things, we use a little // bit more of weight - let bought = 400_000_000_000u64; + let bought = Weight::from_ref_time(400_000_000_000u64); let asset_multilocation = MultiLocation::new( 0, @@ -158,7 +158,7 @@ fn test_asset_xcm_trader_with_refund() { ); // lets calculate amount needed - let amount_bought = WeightToFee::weight_to_fee(&Weight::from_ref_time(bought)); + let amount_bought = WeightToFee::weight_to_fee(&bought); let asset: MultiAsset = (asset_multilocation.clone(), amount_bought).into(); @@ -172,8 +172,7 @@ fn test_asset_xcm_trader_with_refund() { let weight_used = bought / 2; // Make sure refurnd works. - let amount_refunded = - WeightToFee::weight_to_fee(&Weight::from_ref_time(bought - weight_used)); + let amount_refunded = WeightToFee::weight_to_fee(&(bought - weight_used)); assert_eq!( trader.refund_weight(bought - weight_used), @@ -184,7 +183,7 @@ fn test_asset_xcm_trader_with_refund() { drop(trader); // We only should have paid for half of the bought weight - let fees_paid = WeightToFee::weight_to_fee(&Weight::from_ref_time(weight_used)); + let fees_paid = WeightToFee::weight_to_fee(&weight_used); assert_eq!( Assets::balance(1, AccountId::from(ALICE)), @@ -226,7 +225,7 @@ fn test_asset_xcm_trader_refund_not_possible_since_amount_less_than_ed() { // Because of the ED being higher in statemine // and not to complicate things, we use a little // bit more of weight - let bought = 50_000_000_000u64; + let bought = Weight::from_ref_time(50_000_000_000u64); let asset_multilocation = MultiLocation::new( 0, @@ -239,7 +238,7 @@ fn test_asset_xcm_trader_refund_not_possible_since_amount_less_than_ed() { ), ); - let amount_bought = WeightToFee::weight_to_fee(&Weight::from_ref_time(bought)); + let amount_bought = WeightToFee::weight_to_fee(&bought); assert!( amount_bought < ExistentialDeposit::get(), @@ -286,7 +285,7 @@ fn test_that_buying_ed_refund_does_not_refund() { RuntimeHelper::::run_to_block(2, Some(AccountId::from(ALICE))); // We are gonna buy ED - let bought: u64 = ExistentialDeposit::get().try_into().unwrap(); + let bought = Weight::from_ref_time(ExistentialDeposit::get().try_into().unwrap()); let asset_multilocation = MultiLocation::new( 0, @@ -299,7 +298,7 @@ fn test_that_buying_ed_refund_does_not_refund() { ), ); - let amount_bought = WeightToFee::weight_to_fee(&Weight::from_ref_time(bought)); + let amount_bought = WeightToFee::weight_to_fee(&bought); assert!( amount_bought < ExistentialDeposit::get(), @@ -370,10 +369,10 @@ fn test_asset_xcm_trader_not_possible_for_non_sufficient_assets() { // Because of the ED being higher in statemine // and not to complicate things, we use a little // bit more of weight - let bought = 400_000_000_000u64; + let bought = Weight::from_ref_time(400_000_000_000u64); // lets calculate amount needed - let asset_amount_needed = WeightToFee::weight_to_fee(&Weight::from_ref_time(bought)); + let asset_amount_needed = WeightToFee::weight_to_fee(&bought); let asset_multilocation = MultiLocation::new( 0, diff --git a/parachains/runtimes/assets/westmint/tests/tests.rs b/parachains/runtimes/assets/westmint/tests/tests.rs index a7b846d49ec..68c6cf670d5 100644 --- a/parachains/runtimes/assets/westmint/tests/tests.rs +++ b/parachains/runtimes/assets/westmint/tests/tests.rs @@ -62,13 +62,13 @@ fn test_asset_xcm_trader() { RuntimeHelper::::run_to_block(2, Some(AccountId::from(ALICE))); // We are going to buy 4e9 weight - let bought = 4_000_000_000u64; + let bought = Weight::from_ref_time(4_000_000_000u64); // Lets calculate amount needed let asset_amount_needed = AssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles( local_asset_id, - Weight::from_ref_time(bought), + bought, ) .expect("failed to compute"); @@ -138,7 +138,7 @@ fn test_asset_xcm_trader_with_refund() { RuntimeHelper::::run_to_block(2, Some(AccountId::from(ALICE))); // We are going to buy 4e9 weight - let bought = 4_000_000_000u64; + let bought = Weight::from_ref_time(4_000_000_000u64); let asset_multilocation = MultiLocation::new( 0, X2( @@ -151,7 +151,7 @@ fn test_asset_xcm_trader_with_refund() { ); // lets calculate amount needed - let amount_bought = WeightToFee::weight_to_fee(&Weight::from_ref_time(bought)); + let amount_bought = WeightToFee::weight_to_fee(&bought); let asset: MultiAsset = (asset_multilocation.clone(), amount_bought).into(); @@ -165,8 +165,7 @@ fn test_asset_xcm_trader_with_refund() { let weight_used = bought / 2; // Make sure refurnd works. - let amount_refunded = - WeightToFee::weight_to_fee(&Weight::from_ref_time(bought - weight_used)); + let amount_refunded = WeightToFee::weight_to_fee(&(bought - weight_used)); assert_eq!( trader.refund_weight(bought - weight_used), @@ -177,7 +176,7 @@ fn test_asset_xcm_trader_with_refund() { drop(trader); // We only should have paid for half of the bought weight - let fees_paid = WeightToFee::weight_to_fee(&Weight::from_ref_time(weight_used)); + let fees_paid = WeightToFee::weight_to_fee(&weight_used); assert_eq!( Assets::balance(1, AccountId::from(ALICE)), @@ -216,7 +215,7 @@ fn test_asset_xcm_trader_refund_not_possible_since_amount_less_than_ed() { RuntimeHelper::::run_to_block(2, Some(AccountId::from(ALICE))); // We are going to buy 5e9 weight - let bought = 500_000_000u64; + let bought = Weight::from_ref_time(500_000_000u64); let asset_multilocation = MultiLocation::new( 0, @@ -229,7 +228,7 @@ fn test_asset_xcm_trader_refund_not_possible_since_amount_less_than_ed() { ), ); - let amount_bought = WeightToFee::weight_to_fee(&Weight::from_ref_time(bought)); + let amount_bought = WeightToFee::weight_to_fee(&bought); assert!( amount_bought < ExistentialDeposit::get(), @@ -275,7 +274,7 @@ fn test_that_buying_ed_refund_does_not_refund() { // Set Alice as block author, who will receive fees RuntimeHelper::::run_to_block(2, Some(AccountId::from(ALICE))); - let bought = 500_000_000u64; + let bought = Weight::from_ref_time(500_000_000u64); let asset_multilocation = MultiLocation::new( 0, @@ -288,7 +287,7 @@ fn test_that_buying_ed_refund_does_not_refund() { ), ); - let amount_bought = WeightToFee::weight_to_fee(&Weight::from_ref_time(bought)); + let amount_bought = WeightToFee::weight_to_fee(&bought); assert!( amount_bought < ExistentialDeposit::get(), @@ -356,10 +355,10 @@ fn test_asset_xcm_trader_not_possible_for_non_sufficient_assets() { RuntimeHelper::::run_to_block(2, Some(AccountId::from(ALICE))); // We are going to buy 4e9 weight - let bought = 4_000_000_000u64; + let bought = Weight::from_ref_time(4_000_000_000u64); // lets calculate amount needed - let asset_amount_needed = WeightToFee::weight_to_fee(&Weight::from_ref_time(bought)); + let asset_amount_needed = WeightToFee::weight_to_fee(&bought); let asset_multilocation = MultiLocation::new( 0, From 4829edbd424440c8e25ab98def33ffc526e19268 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 30 Nov 2022 00:35:19 +0900 Subject: [PATCH 115/263] Fixes --- pallets/xcmp-queue/src/tests.rs | 13 ++++++---- .../runtimes/assets/statemine/tests/tests.rs | 24 +++++++++---------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/pallets/xcmp-queue/src/tests.rs b/pallets/xcmp-queue/src/tests.rs index 417edbb618f..4e9d6f32b07 100644 --- a/pallets/xcmp-queue/src/tests.rs +++ b/pallets/xcmp-queue/src/tests.rs @@ -212,9 +212,14 @@ fn update_weight_restrict_decay_works() { new_test_ext().execute_with(|| { let data: QueueConfigData = >::get(); assert_eq!(data.weight_restrict_decay, Weight::from_ref_time(2)); - assert_ok!(XcmpQueue::update_weight_restrict_decay(RuntimeOrigin::root(), 5)); + assert_ok!( + XcmpQueue::update_weight_restrict_decay(RuntimeOrigin::root(), Weight::from_ref_time(5)) + ); assert_noop!( - XcmpQueue::update_weight_restrict_decay(RuntimeOrigin::signed(6), 4), + XcmpQueue::update_weight_restrict_decay( + RuntimeOrigin::signed(6), + Weight::from_ref_time(4), + ), BadOrigin ); let data: QueueConfigData = >::get(); @@ -233,12 +238,12 @@ fn update_xcmp_max_individual_weight() { ); assert_ok!(XcmpQueue::update_xcmp_max_individual_weight( RuntimeOrigin::root(), - 30u64 * WEIGHT_PER_MILLIS.ref_time() + 30u64 * WEIGHT_PER_MILLIS )); assert_noop!( XcmpQueue::update_xcmp_max_individual_weight( RuntimeOrigin::signed(3), - 10u64 * WEIGHT_PER_MILLIS.ref_time() + 10u64 * WEIGHT_PER_MILLIS ), BadOrigin ); diff --git a/parachains/runtimes/assets/statemine/tests/tests.rs b/parachains/runtimes/assets/statemine/tests/tests.rs index 18caaaf3f7e..c4353f2b234 100644 --- a/parachains/runtimes/assets/statemine/tests/tests.rs +++ b/parachains/runtimes/assets/statemine/tests/tests.rs @@ -62,13 +62,13 @@ fn test_asset_xcm_trader() { RuntimeHelper::::run_to_block(2, Some(AccountId::from(ALICE))); // We are going to buy 4e9 weight - let bought = 4_000_000_000u64; + let bought = Weight::from_ref_time(4_000_000_000u64); // Lets calculate amount needed let asset_amount_needed = AssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles( local_asset_id, - Weight::from_ref_time(bought), + bought, ) .expect("failed to compute"); @@ -138,7 +138,7 @@ fn test_asset_xcm_trader_with_refund() { RuntimeHelper::::run_to_block(2, Some(AccountId::from(ALICE))); // We are going to buy 4e9 weight - let bought = 4_000_000_000u64; + let bought = Weight::from_ref_time(4_000_000_000u64); let asset_multilocation = MultiLocation::new( 0, @@ -152,7 +152,7 @@ fn test_asset_xcm_trader_with_refund() { ); // lets calculate amount needed - let amount_bought = WeightToFee::weight_to_fee(&Weight::from_ref_time(bought)); + let amount_bought = WeightToFee::weight_to_fee(&bought); let asset: MultiAsset = (asset_multilocation.clone(), amount_bought).into(); @@ -167,7 +167,7 @@ fn test_asset_xcm_trader_with_refund() { // Make sure refurnd works. let amount_refunded = - WeightToFee::weight_to_fee(&Weight::from_ref_time(bought - weight_used)); + WeightToFee::weight_to_fee(&(bought - weight_used)); assert_eq!( trader.refund_weight(bought - weight_used), @@ -178,7 +178,7 @@ fn test_asset_xcm_trader_with_refund() { drop(trader); // We only should have paid for half of the bought weight - let fees_paid = WeightToFee::weight_to_fee(&Weight::from_ref_time(weight_used)); + let fees_paid = WeightToFee::weight_to_fee(&weight_used); assert_eq!( Assets::balance(1, AccountId::from(ALICE)), @@ -217,7 +217,7 @@ fn test_asset_xcm_trader_refund_not_possible_since_amount_less_than_ed() { RuntimeHelper::::run_to_block(2, Some(AccountId::from(ALICE))); // We are going to buy small amount - let bought = 500_000_000u64; + let bought = Weight::from_ref_time(500_000_000u64); let asset_multilocation = MultiLocation::new( 0, @@ -230,7 +230,7 @@ fn test_asset_xcm_trader_refund_not_possible_since_amount_less_than_ed() { ), ); - let amount_bought = WeightToFee::weight_to_fee(&Weight::from_ref_time(bought)); + let amount_bought = WeightToFee::weight_to_fee(&bought); assert!( amount_bought < ExistentialDeposit::get(), @@ -277,7 +277,7 @@ fn test_that_buying_ed_refund_does_not_refund() { RuntimeHelper::::run_to_block(2, Some(AccountId::from(ALICE))); // We are gonna buy ED - let bought: u64 = ExistentialDeposit::get().try_into().unwrap(); + let bought = Weight::from_ref_time(ExistentialDeposit::get().try_into().unwrap()); let asset_multilocation = MultiLocation::new( 0, @@ -290,7 +290,7 @@ fn test_that_buying_ed_refund_does_not_refund() { ), ); - let amount_bought = WeightToFee::weight_to_fee(&Weight::from_ref_time(bought)); + let amount_bought = WeightToFee::weight_to_fee(&bought); assert!( amount_bought < ExistentialDeposit::get(), @@ -358,10 +358,10 @@ fn test_asset_xcm_trader_not_possible_for_non_sufficient_assets() { RuntimeHelper::::run_to_block(2, Some(AccountId::from(ALICE))); // We are going to buy 4e9 weight - let bought = 4_000_000_000u64; + let bought = Weight::from_ref_time(4_000_000_000u64); // lets calculate amount needed - let asset_amount_needed = WeightToFee::weight_to_fee(&Weight::from_ref_time(bought)); + let asset_amount_needed = WeightToFee::weight_to_fee(&bought); let asset_multilocation = MultiLocation::new( 0, From d14dfd2ad74f80257cf6b1e5470c7a1cad99160d Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 24 Nov 2022 15:19:53 +0100 Subject: [PATCH 116/263] Trying to fix sed expression? --- .github/workflows/release-30_create-draft.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-30_create-draft.yml b/.github/workflows/release-30_create-draft.yml index 6453dc1ac45..cb8addf26ac 100644 --- a/.github/workflows/release-30_create-draft.yml +++ b/.github/workflows/release-30_create-draft.yml @@ -261,7 +261,7 @@ jobs: id: fix-runtime-path run: | cd "${{ matrix.runtime }}-runtime/" - mv "$(sed -E 's/-(.*)/_\1/' <<< ${{ matrix.runtime }})_runtime.compact.compressed.wasm" "${{ matrix.runtime }}_runtime.compact.compressed.wasm" || true + mv "$(sed -E 's/- */_/g' <<< ${{ matrix.runtime }})_runtime.compact.compressed.wasm" "${{ matrix.runtime }}_runtime.compact.compressed.wasm" || true - name: Upload compressed ${{ matrix.runtime }} wasm uses: actions/upload-release-asset@e8f9f06c4b078e705bd2ea027f0926603fc9b4d5 # v1.0.2 From ac92420a0cf34b53c308207b085ff62e83b2950a Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 25 Nov 2022 10:24:54 +0100 Subject: [PATCH 117/263] Trying to fix sed expression? --- scripts/ci/changelog/bin/changelog | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/ci/changelog/bin/changelog b/scripts/ci/changelog/bin/changelog index 1f3de939d0a..42a11551d82 100755 --- a/scripts/ci/changelog/bin/changelog +++ b/scripts/ci/changelog/bin/changelog @@ -81,6 +81,7 @@ SHELL_DIGEST = ENV['SHELL_DIGEST'] || 'digests/shell-srtool-digest.json' WESTMINT_DIGEST = ENV['WESTMINT_DIGEST'] || 'digests/westmint-srtool-digest.json' STATEMINE_DIGEST = ENV['STATEMINE_DIGEST'] || 'digests/statemine-srtool-digest.json' STATEMINT_DIGEST = ENV['STATEMINT_DIGEST'] || 'digests/statemint-srtool-digest.json' +BRIDGE_HUB_ROCOCO_DIGEST = ENV['BRIDGE_HUB_ROCOCO_DIGEST'] || 'digests/bridge-hub-rococo-srtool-digest.json' ROCOCO_PARA_DIGEST = ENV['ROCOCO_PARA_DIGEST'] || 'digests/rococo-parachain-srtool-digest.json' CANVAS_KUSAMA_DIGEST = ENV['CANVAS_KUSAMA_DIGEST'] || 'digests/contracts-rococo-srtool-digest.json' @@ -114,6 +115,7 @@ else --slurpfile srtool_rococo_parachain %s \ --slurpfile srtool_contracts_rococo %s \ --slurpfile srtool_polkadot_collectives %s \ + --slurpfile srtool_bridge_hub_rococo %s \ -n \'{ cumulus: $cumulus[0], substrate: $substrate[0], @@ -125,7 +127,8 @@ else { name: "statemint", data: $srtool_statemint[0] }, { name: "rococo", data: $srtool_rococo_parachain[0] }, { name: "contracts", data: $srtool_contracts_rococo[0] }, - { name: "polkadot-collectives", data: $srtool_polkadot_collectives[0] } + { name: "polkadot-collectives", data: $srtool_polkadot_collectives[0] }, + { name: "bridge-hub-rococo", data: $srtool_bridge_hub_rococo[0] } ] }\' > context.json', cumulus_data, substrate_data, @@ -136,7 +139,8 @@ else STATEMINT_DIGEST, ROCOCO_PARA_DIGEST, CANVAS_KUSAMA_DIGEST, - POLKADOT_COLLECTIVES_DIGEST + POLKADOT_COLLECTIVES_DIGEST, + BRIDGE_HUB_ROCOCO_DIGEST ) end system(cmd) From ffa4172b09cc80d323fdc27939a80b68df3b7123 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Fri, 25 Nov 2022 13:08:16 +0200 Subject: [PATCH 118/263] Use the `relay-headers-and-messages` command (#1913) * Bridge hubs readme: fixes and additions * Use the relay-headers-and-messages command * cargo fmt --- parachains/runtimes/bridge-hubs/README.md | 134 ++++++------------ .../collectives-polkadot/src/xcm_config.rs | 7 +- 2 files changed, 44 insertions(+), 97 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index d2065553e3e..0920ddfa229 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -22,22 +22,25 @@ Every _BridgeHub_ is meant to be **_common good parachain_** with main responsib mkdir -p ~/local_bridge_testing/bin mkdir -p ~/local_bridge_testing/logs +# 1. Install zombienet +Go to: https://github.com/paritytech/zombienet/releases +Copy the apropriate binary (zombienet-linux) from the latest release to ~/local_bridge_testing/bin -# 1. Build polkadot binary +# 2. Build polkadot binary git clone https://github.com/paritytech/polkadot.git cd polkadot cargo build --release cp target/release/polkadot ~/local_bridge_testing/bin/polkadot -# 2. Build cumulus polkadot-parachain binary +# 3. Build cumulus polkadot-parachain binary cd cargo build --release --locked -p polkadot-parachain@0.9.300 cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain -# 3. Build substrate-relay binary +# 4. Build substrate-relay binary git clone https://github.com/paritytech/parity-bridges-common.git cd parity-bridges-common -cargo build -p substrate-relay +cargo build --release -p substrate-relay cp target/release/substrate-relay ~/local_bridge_testing/bin/substrate-relay ``` @@ -45,16 +48,16 @@ cp target/release/substrate-relay ~/local_bridge_testing/bin/substrate-relay ``` # Rococo + BridgeHubWococo -POLKADOT_BINARY_PATH=../../polkadot/target/release/polkadot \ -POLKADOT_PARACHAIN_BINARY_PATH=./target/release/polkadot-parachain \ - ./zombienet-linux --provider native spawn ./zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml +POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ +POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ + ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml ``` ``` # Wococo + BridgeHubWococo -POLKADOT_BINARY_PATH=../../polkadot/target/release/polkadot \ -POLKADOT_PARACHAIN_BINARY_PATH=./target/release/polkadot-parachain \ - ./zombienet-linux --provider native spawn ./zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml +POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ +POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ + ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml ``` ### Run chains (Rococo + BridgeHub, Wococo + BridgeHub) from `./scripts/bridges_rococo_wococo.sh` @@ -106,68 +109,43 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ --target-signer //Bob ``` -**2. Relay (Grandpa relay-chain) headers** - -**source-host/source-port** - WS-port of collator's inner RelayChain validator -**target-host/target-port** - WS-port of BridgeHub collator +**2. Relay relay-chain headers, parachain headers and messages** ``` -# Rococo -> Wococo -RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - ~/local_bridge_testing/bin/substrate-relay relay-headers rococo-to-bridge-hub-wococo \ - --source-host localhost \ - --source-port 9942 \ - --target-host localhost \ - --target-port 8945 \ - --target-signer //Bob \ - --target-transactions-mortality=4 - -# Wococo -> Rococo RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - ~/local_bridge_testing/bin/substrate-relay relay-headers wococo-to-bridge-hub-rococo \ - --source-host localhost \ - --source-port 9945 \ - --target-host localhost \ - --target-port 8943 \ - --target-signer //Bob \ - --target-transactions-mortality=4 -``` - -**Check parachain collators:** + ~/local_bridge_testing/bin/substrate-relay relay-headers-and-messages bridge-hub-rococo-bridge-hub-wococo \ + --rococo-host localhost \ + --rococo-port 9942 \ + --bridge-hub-rococo-host localhost \ + --bridge-hub-rococo-port 8943 \ + --bridge-hub-rococo-signer //Charlie \ + --wococo-headers-to-bridge-hub-rococo-signer //Bob \ + --wococo-parachains-to-bridge-hub-rococo-signer //Bob \ + --bridge-hub-rococo-messages-pallet-owner //Bob \ + --bridge-hub-rococo-transactions-mortality 4 \ + --wococo-host localhost \ + --wococo-port 9945 \ + --bridge-hub-wococo-host localhost \ + --bridge-hub-wococo-port 8945 \ + --bridge-hub-wococo-signer //Charlie \ + --rococo-headers-to-bridge-hub-wococo-signer //Bob \ + --rococo-parachains-to-bridge-hub-wococo-signer //Bob \ + --bridge-hub-wococo-messages-pallet-owner //Bob \ + --bridge-hub-wococo-transactions-mortality 4 \ + --lane 00000001 +``` + +**Check relay-chain headers relaying:** - Rococo parachain: - - https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8943#/chainstate - - Pallet: **bridgeWococoGrandpa** - - Keys: **bestFinalized()** + - https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8943#/chainstate + - Pallet: **bridgeWococoGrandpa** + - Keys: **bestFinalized()** - Wococo parachain: - https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8945#/chainstate - Pallet: **bridgeRococoGrandpa** - Keys: **bestFinalized()** -**3. Relay (BridgeHub parachain) headers** - -``` -# Rococo -> Wococo -RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - ~/local_bridge_testing/bin/substrate-relay relay-parachains bridge-hub-rococo-to-bridge-hub-wococo \ - --source-host localhost \ - --source-port 9942 \ - --target-host localhost \ - --target-port 8945 \ - --target-signer //Bob \ - --target-transactions-mortality=4 - -# Wococo -> Rococo -RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - ~/local_bridge_testing/bin/substrate-relay relay-parachains bridge-hub-wococo-to-bridge-hub-rococo \ - --source-host localhost \ - --source-port 9945 \ - --target-host localhost \ - --target-port 8943 \ - --target-signer //Bob \ - --target-transactions-mortality=4 -``` - -**Check parachain collators:** +**Check parachain headers relaying:** - Rococo parachain: - https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A8943#/chainstate - Pallet: **bridgeWococoParachain** @@ -177,36 +155,6 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - Pallet: **bridgeRococoParachain** - Keys: **bestParaHeads()** -**4. Relay (XCM) messages** - -``` -# Rococo -> Wococo -RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - ~/local_bridge_testing/bin/substrate-relay relay-messages bridge-hub-rococo-to-bridge-hub-wococo \ - --source-host localhost \ - --source-port 8943 \ - --source-signer //Charlie \ - --target-host localhost \ - --target-port 8945 \ - --target-signer //Charlie \ - --target-transactions-mortality=4 \ - --lane 00000002 -``` - -``` -# Wococo -> Rococo -RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - ~/local_bridge_testing/bin/substrate-relay relay-messages bridge-hub-wococo-to-bridge-hub-rococo \ - --source-host localhost \ - --source-port 8945 \ - --source-signer //Charlie \ - --target-host localhost \ - --target-port 8943 \ - --target-signer //Charlie \ - --target-transactions-mortality=4 \ - --lane 00000001 -``` - --- ## Git subtree `./bridges` diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index 2cb82656c37..fb0bd3b4195 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -31,10 +31,9 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FixedWeightBounds, IsConcrete, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - UsingComponents, + FixedWeightBounds, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::XcmExecutor; From a46ab39ac0ac0b22f293dd63e0723517e6f98a5f Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 28 Nov 2022 17:48:11 +0100 Subject: [PATCH 119/263] Fix README.md + zombienet --- parachains/runtimes/bridge-hubs/README.md | 50 +++---------------- .../bridge_hub_rococo_local_network.toml | 25 ++++++++++ .../bridge_hub_wococo_local_network.toml | 25 ++++++++++ 3 files changed, 57 insertions(+), 43 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 0920ddfa229..75f05bf5483 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -47,31 +47,21 @@ cp target/release/substrate-relay ~/local_bridge_testing/bin/substrate-relay ### Run chains (Rococo + BridgeHub, Wococo + BridgeHub) with zombienet ``` -# Rococo + BridgeHubWococo +# Rococo + BridgeHubRococo + Rockmine (mirroring Kusama) POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ +POLKADOT_PARACHAIN_BINARY_PATH_FOR_ROCKMINE=~/local_bridge_testing/bin/polkadot-parachain \ ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml ``` ``` -# Wococo + BridgeHubWococo +# Wococo + BridgeHubWococo + Wockmint (mirroring Polkadot) POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ +POLKADOT_PARACHAIN_BINARY_PATH_FOR_WOCKMINT=~/local_bridge_testing/bin/polkadot-parachain \ ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml ``` -### Run chains (Rococo + BridgeHub, Wococo + BridgeHub) from `./scripts/bridges_rococo_wococo.sh` - -``` -./scripts/bridges_rococo_wococo.sh stop - -./scripts/bridges_rococo_wococo.sh start-rococo -# TODO: check log and activate parachain manually - -./scripts/bridges_rococo_wococo.sh start-wococo -# TODO: check log and activate parachain manually -``` - ### Run relayers (Rococo, Wococo) **Accounts of BridgeHub parachains:** @@ -80,14 +70,7 @@ POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ **1. Init bridges** -Need to wait for parachain activation, then run: - -``` -./scripts/bridges_rococo_wococo.sh init-ro-wo -./scripts/bridges_rococo_wococo.sh init-wo-ro -``` - -or +Need to wait for parachain activation (start producing blocks), then run: ``` # Rococo -> Wococo @@ -121,7 +104,6 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ --bridge-hub-rococo-signer //Charlie \ --wococo-headers-to-bridge-hub-rococo-signer //Bob \ --wococo-parachains-to-bridge-hub-rococo-signer //Bob \ - --bridge-hub-rococo-messages-pallet-owner //Bob \ --bridge-hub-rococo-transactions-mortality 4 \ --wococo-host localhost \ --wococo-port 9945 \ @@ -130,9 +112,9 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ --bridge-hub-wococo-signer //Charlie \ --rococo-headers-to-bridge-hub-wococo-signer //Bob \ --rococo-parachains-to-bridge-hub-wococo-signer //Bob \ - --bridge-hub-wococo-messages-pallet-owner //Bob \ --bridge-hub-wococo-transactions-mortality 4 \ - --lane 00000001 + --lane 00000001 \ + --lane 00000002 ``` **Check relay-chain headers relaying:** @@ -175,21 +157,3 @@ all into one. Now we use `master` branch, but in future, it could change to some release branch/tag. Original `./bridges/Cargo.toml` was renamed to `./bridges/Cargo.toml_removed_for_bridges_subtree_feature` to avoid confusion for `Cargo` having multiple workspaces. - ----- - -###### TODO: fix zombienet ports as bridges_rococo_wococo.sh, because networks colide and interfere by default, because of autodiscovery on localhost - -###### Run chains (Rococo + BridgeHub, Wococo + BridgeHub) with Zombienet - -``` -# Rococo -POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ - POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ - ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet_tests/0004-run_bridge_hubs_rococo.toml - -# Wococo -POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ - POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ - ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet_tests/0004-run_bridge_hubs_wococo.toml -``` diff --git a/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml b/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml index e6ca8b893f7..0baba72ecd9 100644 --- a/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml +++ b/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml @@ -54,3 +54,28 @@ cumulus_based = true "--force-authoring", "--no-mdns", "--bootnodes {{'alice-collator'|zombie('multiAddress')}}", "-- --port 41334 --rpc-port 48934 --ws-port 48944 --no-mdns", "--bootnodes {{'bob-validator'|zombie('multiAddress')}}" ] + +[[parachains]] +id = 1000 +chain = "statemine-local" +cumulus_based = true + + [[parachains.collators]] + name = "rockmine-collator1" + rpc_port = 9911 + ws_port = 9910 + command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ROCKMINE}}" + extra_args = [ + "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", + "--no-mdns", "--bootnodes {{'rockmine-collator2'|zombie('multiAddress')}}", + "-- --port 51333 --rpc-port 58933 --ws-port 58943 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" + ] + + [[parachains.collators]] + name = "rockmine-collator2" + command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ROCKMINE}}" + extra_args = [ + "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", + "--no-mdns", "--bootnodes {{'rockmine-collator1'|zombie('multiAddress')}}", + "-- --port 51433 --rpc-port 58833 --ws-port 58843 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" + ] diff --git a/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml b/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml index cc287419616..ba498c0a785 100644 --- a/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml +++ b/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml @@ -54,3 +54,28 @@ cumulus_based = true "--force-authoring", "--no-mdns", "--bootnodes {{'alice-collator-wo'|zombie('multiAddress')}}", "-- --port 41336 --rpc-port 48936 --ws-port 48946 --no-mdns", "--bootnodes {{'bob-validator-wo'|zombie('multiAddress')}}" ] + +[[parachains]] +id = 1000 +chain = "westmint-local" +cumulus_based = true + + [[parachains.collators]] + name = "wockmint-collator1" + rpc_port = 9011 + ws_port = 9010 + command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_WOCKMINT}}" + extra_args = [ + "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", + "--no-mdns", "--bootnodes {{'wockmint-collator2'|zombie('multiAddress')}}", + "-- --port 31333 --rpc-port 38933 --ws-port 38943 --no-mdns", "--bootnodes {{'alice-validator-wo'|zombie('multiAddress')}}" + ] + + [[parachains.collators]] + name = "wockmint-collator2" + command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_WOCKMINT}}" + extra_args = [ + "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", + "--no-mdns", "--bootnodes {{'wockmint-collator1'|zombie('multiAddress')}}", + "-- --port 31433 --rpc-port 38833 --ws-port 38843 --no-mdns", "--bootnodes {{'alice-validator-wo'|zombie('multiAddress')}}" + ] From fa2cf8b9ddcd9115507c5786b876af11903653c1 Mon Sep 17 00:00:00 2001 From: Anthony Lazam Date: Tue, 29 Nov 2022 17:06:50 +0800 Subject: [PATCH 120/263] Parachain ID update of bridge-hub-wococo --- parachains/chain-specs/bridge-hub-wococo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parachains/chain-specs/bridge-hub-wococo.json b/parachains/chain-specs/bridge-hub-wococo.json index 0affa8b71a8..e3148cb0922 100644 --- a/parachains/chain-specs/bridge-hub-wococo.json +++ b/parachains/chain-specs/bridge-hub-wococo.json @@ -16,7 +16,7 @@ "protocolId": null, "properties": {}, "relay_chain": "wococo", - "para_id": 1013, + "para_id": 1014, "codeSubstitutes": {}, "genesis": { "raw": { From 7f86dcf9f7ab79536498cb28896127c8e0f6a4b4 Mon Sep 17 00:00:00 2001 From: Anthony Lazam Date: Wed, 30 Nov 2022 19:49:09 +0800 Subject: [PATCH 121/263] Update bridge-hub-wococo chainspec --- parachains/chain-specs/bridge-hub-wococo.json | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/parachains/chain-specs/bridge-hub-wococo.json b/parachains/chain-specs/bridge-hub-wococo.json index e3148cb0922..bf41ef82154 100644 --- a/parachains/chain-specs/bridge-hub-wococo.json +++ b/parachains/chain-specs/bridge-hub-wococo.json @@ -21,13 +21,13 @@ "genesis": { "raw": { "top": { - "0x0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f": "0xf5030000", + "0x0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f": "0xf6030000", "0x0d715f2646c8f85767b5d2764bb278264e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x15464cac3378d46f113cd5b7a4d71c84476f594316a7dfe49c1f352d95abdaf1": "0x00000000", "0x15464cac3378d46f113cd5b7a4d71c844e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x15464cac3378d46f113cd5b7a4d71c845579297f4dfb9609e7e4c2ebab9ce40a": "0x10b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", "0x15464cac3378d46f113cd5b7a4d71c84579f5a43435b04a98d64da0cefe18505": "0x0a000000000000000000000000000000", - "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x000000000000000000000000000000000000000000000000", + "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x000000000000", "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", @@ -38,28 +38,32 @@ "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b908aa810c364ce8c3bd964ff3d424cc926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9f52c4b3c3fd1c798e3843e21a38f1421b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9ff9bdc7d7afef8c14d5b253d4e25b33db0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34": "0x0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x04446272696467652d6875622d726f636f636f", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x5191446272696467652d6875622d726f636f636f", + "0x2b46c0ae62c8114b3eda55630f11ff3a0f4cf0917788d791142ff6c1f216e7b3": "0x0000", + "0x2b46c0ae62c8114b3eda55630f11ff3a4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x365c9cdbf82b9bda69e4bbdf1b38a7834e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x38653611363acac183fe5c86aa85f77b0f4cf0917788d791142ff6c1f216e7b3": "0x0000", + "0x38653611363acac183fe5c86aa85f77b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x3a63": "0x", - "0x3a636f6465": "0x52bc537646db8e0528b52ffd00587c78048e21c5d10f4f107868930efab1b838f4ab956e37666c7f315033ee36fc62e8f5cc1f2729a56a67ad16541632755b67b4c33c707182dcceb49492c72443bd03548e239738f428dd94584236217b6fb2a59449ca94649a119b0ee10e5f4d2b20202028702a8fb7706a8fb7dc3d1b1f3e809203256859831668e0583e3935df72adcb5b4fd4f016ad3d353fa9e683e6be6a2fe984db6bebdab39938eba767b3404040404138eb6fd318b5ce9b8969d66160a0f1e0b1772b2668f0aec7ca9f552d845560e20926618c728caa3c7e7df46eb504973ff1389d14703ab976e7d78e19259e8d7b342e0b22092020a0a0af4900010141f92a2c775915418e63addff0e4546d97ced1a1d79e9a973e19c53eb70468edeea3dff0dce6bb673371985d22b1bf9422a9f9487920f6bbde1b929ae7798e316add653a903a463dea18b57ec39a64eec1055385265fa582227c118420044901065ec071ee4ca7d37b43dad9df7b8fd9613b3fc8db868d437addbd89530b9d73a0df706f335504398e749ff741c838e288838d7fb40a497f6a70ae88744608f8cf61fbf3a63efb90c2dfcdfdcfaa9ab70dd09d5ff0b9f3eb6dbcb1679be5ac5f2fb6477fbe59ce8b7dc188c12ce77596b3ce59bfcd8070bf0eb7fdb7bb65092d5fe7333b33b316cfccec0eba1eac84dd3985e25485274e3defc1a9fdb744d03fc7d8e6e1ec571ef82b8f7ffe1c72aac7a33c3523a8d1a0d0dc4a0552bc5b29a1c657d4b31be0dd4a89d5bb2c47bb614dc338c5a9e8ce21a7a4bff34b6eeee3b67fb90368d2e5c336cb890f5049bf360b22baa4357af48bb320b48fee32c828e82ecb69bfe1e82ebb5c366955fd74b9d529fde6c9adc297eeb29ce997dc6a035efae5328bd624de72b955b77ae971ab8e51492839a248065e7a758caa44bcf41a817871736f6d4b5e1c71c4f1e5a3cbadfaf0d0afc5e4067d08f9726e4be4475af7255db271c411471c5f55dfd1e77d53c7a86afaf59a773aa8e642262e675a41e8725a139a5b2961e5eb7cb752c28a778eb1cde2383f71caad80beb8af3cdeb9ebe16e2ec703a79cbbcd3d6ffbbbedf3e61e728addf9b5d7ba6c97a413063718e5febd2c87a973241d313c6194fbf5b75d0fe36d9fb37597417ac32ecb998c7ad762f0626cb308b867df2b5cee997d99e90dab761f70adef366a83b602e2f2153efb5c9fd33d1bc73455fbf5aa3ddc35e23d7eefbdf71eb3cf673a23043b74665ad91ff7a8feec974b69973b80e67cdd6539ce37fb210920202025de51c7a83a9ffd62cc71ea39a7d6318afd8677838c62ef41abda33fb8953eb264eadb36b9c728c72ee5d06372472f826aaf9cfe73f3a23c473e8cf21f5d95fea8fd6fdf51e4c79fc9d0eaab79b85cbbdeb51b567bf26a7ea8e1928da7ac59e0ba0ad3f77eeb221d6791e9d8caaf09ddff0b5dbfb8ccb238189aec576835e7e897c244abfce9e39214c16e8972ce091c07e9d5de4843059a05fb2804722fe3afbc80961b240bf6401bfceeeb2202010d0afdff0c5987342d6eb73c728e7935375ffb9f3eb6d9351ce6bb4e7ce5da60319e5fcd1c928e7377c31b61b3fe68430d9d523117f9d3d7342982cd02f59c0afb36f368406f4eb18a7dcb3bb2c8805faf51bbee11bbe61b7394639773d98f268ef7450f1b2172ec797a3d1d69d3beb723eb475e7cebad61da7aaf3a58e51ce9dab76735c6e0554e59dbc9c8db6eedc593bbcb03168628dd00e27ec586307187678b1a38b1d58764461870c764471e2b2c3891d2a908942260d1930648620636507143257c8f840e60732639009834c1176ac40460aa73064a89cbe2013049918c8b0e0d486cc0a64c07072e304874c904c133267c8084106caa90b272fec68c10e169cced81124d3458609192fc890e1b4e56485531664c420f305992ec8a071da824c144e65d801744a43460b3259908142e6c9a98c1319322dd8f16487122736645eb0038acc1464b2c87091f942468acc123b9890992263864c19323090c1828c0b64542003a4e38b0e357490a1a30b3accd051868e2ae8004387157458d1d1021d5062b2104346cc1662ca881923264b4c1562cc88e9124385182bc480117385182d315dc44021e64acc14629e10f3454c1762bc00c325460b31528809430c1860ba605ac2a40293122620983860e0807903c60d98364c544c3330b5c0c4021314a628a6274c4e987660a2c2a403530e4c38305531ddc03485c90626294c3530d1c00403d30b4c2e304931f5c06485a90ad3149836c0b001f305660d130f4c4c989ec0a801460d9834c09c01c60b4c193064c08001660b4c1660b000a305183360b480c9022606302c8079018c0a60942879297da12446c98b52124a482819a15483120d4a3328518909a314a5e444690525264a2f28c9a00403d217121ba438486f90d44082838406d28a7485d4041214485e90ba207141a202290be908242290b22899413242290d120c483f204dd1dad0c4407a01a90a4d0d5a1ab42f9a1ada1a4854907a40b2421b834606cd0b9a14342c34236835d068a0cd40a3a2c94063c1a88d111b34218cd8d09ed054a029315283d683d117edc9680c2334465db4238cc61889611486111846678cbc30e232326354c6888c87863584ac0a182690bcb40a4852387d094de199e134060764164e67b07cf0ce7002c3b252c40a82d4c22bc3c90ceb07325b6494909962cdb07a6009e191c102c2c9cb96f1d2d8d104a90d323d901143260da735442684a22013c649cb8e2564aa20a3857fc03ec8b630dbc8c68039c2cb12b190899171c1da41a90dad091a1332282218528bf4e28d4172414e418221b110c292fd00a60c3a7ea0c3073a5c3000016443785e62164f8ac9847641e68337c5f5020b0c8c0818191a1118800302b9ca40113ab890da2805959e2845b15cc431b42160565c49905f946220577021e162036a8155805fc82e4030ae2fd913305e30726d712de16242898cd214fc45282804c5cac2324208286eb1e2885798638cdc082961b9b1830d27345855ac362c36e415425e94c840b28175c555053ac14fc87a00f345d602182962ce904143a90c302d904f985cd809310bcc84c601b606d6054c0c581830366064c0e0c0dec0dcc0be606dc0dac0d8c0d0c0ca809d81a501c302f38279014303b6066c0c981ab02ed811302360596045c0c080a5817d013303a6c66586eb0c5719a6174a6c685dd0b86866686568646863685cd0b6a089a16dd1b4a06541c38216867605cd0a1a189a16ed0bad0a1a15b42cda142617320c19052985521b200e32317017328d1831ac12768b98342c1ebc2e5c858404790566b1aa8054c89a28d9c0b24266592690b4882c601c9cda10d910cd8864c4322217183490a63861217a414279522e3730155c6dc89e904d218302f6e4822373429625bb72b58129119f8850c4284f0d0f0dd9161917d912de12deea31616a51eac14442c7a067d0516445c8b0785108bd71311182c3ba8235062b0d0b8deb0d6c892b0e182c9da57bd0573410fa07ed83d6415b89f9421625931243063986dc82dcd2001d5a1a8a24230b426c41e98be946480a3035804cc0277009d803a8021854aac2e88da7a514049826c044c157202581adc060482cec85b5c62c639bb04bd880e46257d20a18d03a41963144c602d9c55cc2e98d0c8a7c2286cbb5067b81b9bc28d918486980a1522a833405d612adc05c442e30d08363e36028c42d40200b0eeb8d69460c8357d9166e42e720d34214236a619231b9c0572414a4183c8561d0636834f849a84b480ccc44c88c10975019a12e84c80881217406cb80a970143c038e01d78069c04d48332417192f325778714427da8ba482ac02cc19d209305f886ad08016c50215809141044a6ec0cc00260d181a2c20472b02901f062080b644a3e1da777b59c1e2b7d3c40808882831b273041122d73e2390ec1021120a054104a3800187589c113e4d764040e488923781cf0f498a485244920938f4a2082249428024889e9e240cf02962a7a72709030600bb704d7a8e18f1d93182c8ce4e080200a1e0928400c911222660b2438225448e28791f243b23308111221c5e713b45204982848811224794fc4e11489244c0070909921451c4df009f60449222768cc8013ac13531c10e0f91264d4cb023049224479a34d9a1c006b9702538a2e499ecf0304982812136b8723b3e468c38d2630409223b24d82181042a60c48e084460a40203804d704d788ad839a20826b8263e487698ec1c81e488c3252039128219b8852be24813244798f824b143c4043d4a941821c264c7889e2324c0e12922490f0e0fa804370222467c8c1861c4670702249884131ce93172c4e70892233920121c4f11497a404064c7870445ec24b153825a3826491880c123b8263b477c8e304972840f098cc0c02c1c9224477c8c30218111447c92f434217244c91b614282cfe176a70704467c90902004447c8ad86172c4082447881091397518d28a176858f14e75b91510d0afeac9c44e43a76195ac84106008303f000210780033333b21de830f76538b5a9bb2badfa36db545adb62c8b52eb596dd186164c5916b52c4aa9d56df1c285bd16a5d4b2bad96aabdf7b56bfd76b5956aff5baad5d6bb7772df8765fec85fda0739673d239b7d182fd245cf6615952b6dcb52cd8ddab492861b7b56c59afb7ad7dddfc42b043d87b5648f65bab03f042f1bd197b614f29313892326ad02decd7bb6fbb771db00fcab600b0bb413cade577af655996cdeb7ebd0bf9b525e542b96dc1deddb576dff6badb82fdac65b8bd16ef365b0ba55c6ba1e5203f08a1f5ace79ee51e5bdd2b65eb10daee1ec95d51ef5a701b4aa84509b1910c8542efc96eeb416cf7ed42cb52809523df7b96730c84ce0f393e721ac0d1b22cb62cca009cc73ee4636959dab3ac8c2d6bb7dbea0e3d0831f8205cb8fd2486edcaf4955dd66f775ba6b7a5f76e745c988f05f4c3ba77a1732b7ba58c50133d0865c3c7ddf2c151f7cb2e0000404a0877b15dd83e307ab990d51b820a48c07ccf877e6f77d7b9f77a080000b04377335ce6dde56eebf56ef6de5bcbb29ed53bec055993526212c27dfba08d8d1004d874109bb7701976f7a67adfce744bd7ddec03be968d59f4bded03583dccbceeee1d7a33b8bb52ee6e76b56561eff50efb5caf65657d6def5a0680b017f67b0feef6dbdeeef72c8bd2b79c5deff57b565bcfb220907e0da4002feeaeb5d6ab793f9e65edf0f6317c10428861bbdb524a299f84b0350d6a1a848d61ef4909a17c0f4ad9566318f62ccb7addfdaeb69a00dd6bc1dd0e6d6843d9927677d8de87f9d819282526257cdd52b60ed27296dbe12ccbb5eb0e59d6f6c8b2ac1db6f77aeff57bfd963188c1d7af777978dd036801f485c3d52bb769b6bbe57ba4b55001e80e4000baad05c07b4fb6ecf7ba17c2b743f7ae65596b5996d5ddfd7ab7772526b79be19390610f1d74d04187d188beb796b5dbcef56e5b2d174228bbb79f7bafbbdf83af1bc207bbfbc1f73608a1b5d6f65addbb5af77bafe1ae6575ef7b0dfb75871ab2f5dada6ee77a8717769cf170700518c1111f242460424921f021c10d3b4710f141728489122245343932021e6ec40e931d23982421923a00909d234c7c041080924cb683a7082320e8e94922801c76e080030e3237f008214972c4a708234d901861e243c4e7c8084690049583926690ec28516284470fa91b924852021f1c520700e23704015200224076788cf480808808901c39a28991233ce4c3c4884f930000d181878f11263b487644c0a4c90e330ec84e93263b4c948c2049114498ec2091a4881d26448c3431a2881d2205e0491d00889222769ad8cc238af03102022247343982e448087684e008931d248820a0000738001023768824f15162022645e8305d017690d8e92192849111e420e30a2082243d444480e488084490a447c6003d3d47786a4e01d0e180f82449126384911d262813ecf010f1496207090f39441000932b40139f264792d811008c0392c49110ec30515253da11811126497c9a1031a2e7c8114510f1e921819110ec1841e34680c3eeec3009011126497c8c1491a4a7c7088f92cf4e134702c9ce23b902104192e44813263b4810616264a7a7082249ec1881e4081124478c1c514408429084884f123b1409243bafe4d8044c928c6067041109243b4f009913f8e7e7272ba25ad5aa56aa463ff0a97ee4fb51c1f8f3a352a97e7e7e7e7e54aa56a97e7e54b0886a7f7e7e7e7ef6e7c7a97eb888fb51a95e919f755711a752858aa8d6a97e5e919f2da2fa51a97e9eeae7e70716f951edcf0f17f9d99f9f9f57c4fdfc7011f7b3459c4a1671aa59c4a9ac223fabda224e158bfcec8f8a8b38c74554eb545d44b5ac82459c6a552ad52be2545b44b5aa2d1201588675ceed089a187101ce41d823cd6e9593101614c2a8c41e8568557dc8656341524223cac38d3911591bb9167b442b77f7955dec1dbb636f557b9593d8eb5850c4a87c7d1787a46cc7821a93f2f585a2ef7b756251fef9e5fc176beef4d518e5b531b65fbb5d8c41b659208cf64667325f7f1cf46ca09492631cf4b03dd21a447bf4d81eb1202c4a8c118bf2ab653a9847ca13fdf211b6d5fd1679894a89fe60bcbde4994e3b8fa4589bb629a3c33c8755a4a425116d6cdded625bfcc591fbe3d6edb1afe758cc74a45bfeb6f69b27a19413a7f89daf149c6a1dfed1a322e5a14329cf065691f2d031d236a384b6405f998a94d3e54af97a30c6b0ae3e52ca902f6f68433dd806fd4acc89fdb13d58c823d0bffaa83c7458e5d9cc4703ab48f9998bbc6ed08732c7bcc20f5d2edd97aff333af4c65b4414665b4fac8972ea275833efacc30cc891db217c59c08853c6639212cca5b524a893901adbcd341455a1d628c768b287f8d78708c31b6739ccc1e63f45dc63cfa0d5f7146cab38e398c3e1bd3aae9dd6c6b37cb9d0c824170fa064d2acf83cd29e54a918e697ced92345af93127d1cab4cec998740c6290e9ccac0941e8106e106f94b8512c6b8439368a21d7a076f1c843158eac2d7eca5db93bb1b86e5d5bf49bb7eb040fece7faa4153eedd988a8bbe314e6ec9007fa74e9157b098798749d88decfb1e98df9e513a32197983f8cd6ab9faf13d343749d88316e94b7e69c739d783fabba9ea68618a984ba3cbf6a3895b9f32beeeeb6fb30cfe88ab13b8e16c2071f7cf0a23ceca37e508323bfe1fd91c3adceef7972d48373fae5789bee48da08d2d07541bf2ee8d705fd62359e47facf4327ba2eb855f830e4708730f49b07b7ca69c44b4479422e619c2f8f6c1a42ba8956e62e8f56ed1f3d3948af904bafd085904cebc258e4d9dc94300b4287d1e10e811ea963d44238b72cd3890e290ff49163d17a240d92ae4c273acfa579a8c82bd13a314ab26884ae5959ce3e2a126ef12564873288eb47ecd37b83be5bbb88b1b843ea42086bbca35f1774b8b9df0c3eed3dcfa0a4f5147a91d6f910f240bf5cc22132aff3333a237dbd9f3feb8568f4aef37542ce7359212f72f9bbbcce7779d5aed3f3b84342d12ddf2c67b9cb4fd8f1e5e61e42e8371c37f832c2575a131344ce58e6a8fd72cf79ad795783bc737f3623778ef28153d347dbbb2ee87363dfed72b941c7b61c3eda9ec390442f8788176985d167f4b958449f908a50c41cbef4d07ae8453931ca13c27cfd86378b1d125dca50496216adfc21bf61cb8a96f4d8b16347ca13f2cc63bc1e26e97adcaaa672229d23fb62219d6385547c85eec33c96ffbc64e7b9b4aa7e9d37e8d1178b1dc21ea9145fe1ce1be66c88ea155281eb30e4925648e5a1fbf0470f71bf7efd3a447962acab8594524ae83221a25b5acc555d2c71267376e80b4349225a4f414497434497be5638c558b094d5872ba4f2ecd7a6653a22979447bae5a36c08eca38bb6ba1f99e430d4444b543a7466afb014fde5900e5d94e9b0f32c15795664dd04d34b4bd0492c8ad1b12c2762f92bb4b9cfb2cd7d2846b9b5cb9859d0178bfd213d663aeb97c32d46ba56da633d391ce7379cd226e07046ed7b0ad17ae2d82b5acb37140a854494277a16ca6287489f9c82aeb97b36cf350a21e5893e7d5d8b97abc5ae16cbef6af995d34331140a85a2877cb320b2f5a8c52f9db17c5db69f4e9ad61e72e9bbcd40d17ce0af571f38b74aafabc54f1f6dd8361925a2d507ce4c675dee6af199af7ab1f46a09b96cdf2c428ef9f4b512b39c09a9fc1537f7a1addf0a6df1adcd7d8c31b4c5e8371cdae05b59ec0f193bf464b62c5035551eb5c7bbca430d66616ec977e57a53dee6259fd921f328bbdcaaf6fc7a7ec9cb6b9e5faf0a6f3c99fffcc3e1a255f59764e99cc38503901dc2de5e71706f1c68e5773645aa0fa1e7ce3ddace7cf9740fdfce94e7f2cc9ff7c0291eb2a6a6a6e655911b4f7649f9bce63d2a5fafec06af6fcaf4ca6fca47bf1e7cf0c1de497df82dcaf33cf366eac3bf2ecb833ef4f04d6b14e279d6178d99efc5589167e3664873c7291c20905120fba35dd2adc2cf81369d617f3637305e32c5bcf7a4d7c837a5e6f0a6fcba65c9cc2fb901d91fab853c078c03d96a0ed4bdc21ce40c6d9a03c981027957feba36f7cc9bfbebbdebdadee38c02a9796ee1b0617ec3ebe6b1553eb9cfd03a7d07addabb0cf56773a2247a3a8da8c95f44dda381a125209768442af26820d5469b7b3421d1e61ecd94d3e59397cb4933203df4e083bbe2b2549fae4b6b0f1f3da33cedd2eb5e98dc6a0fff2e0ac487bf8b5cee5ee637cfa608ada7d345abc6e5a103d921d263f7f0d5a77bf8f5cb2bcce0e53c190d5d5ea487cfdc872e573ef3eabc3c74593e735f80ec908bd61005f2a6bc25a5944016e8875521796282ab9c1c55e35e83e4e0d5ad70702cf67bfd056b2cbc85dcb90e4e7d73ccdd73701cde56359593e70ebead7618d7db414a7fafdd72b8439e5b2d77a037fcfc86e30e91dedecf01b08303a069e5bf7c075af92dbfe1ca5dd88b88ddb90774f9e5750767afd2994e8d0160f9e54ceb0ed4a7e737119c8271d7f4396f557b9593f6a5b07d3b0c9e1dfce77be576b3c31db2de7ef37aab9de56b943bf8f6881ea7d76e5a1bcbb3c31ce6306f1afbc17eb01fec1dbc69b5a84f57d9a707a47da9eca5a7564fcdec3becb0c230cf3017613e72e7a60e5e35eaf5f4bc79f5670b02178dfba3e9ccf572e89099be033553c323c80dec4d6b10eceded9b0dc1deeee00ee9c6f275bbbd3616d9587e9f37e569df81fd6d759f69dc1febeaa96acee626003ea27191003cf3b843da63651fc0a33e4d51a14ce7398fa400702b1b02fbe737a6181fc0c919e80980d6494369951445ebfce801a0af1d0034ee6e7ff9cbdadc5fbdc5bf36f716736fcdde3bc0fdb1dcbe5bf41edbba9f5bf50bebbe7934d04dae03ad33c629addac9375a4fed18adceeeb4a29ee7406b0d0eb40679e837cf666fa035c8d70dc283d69a476bcd0cad28a615b583566f5a5d86d6d389ba950e1a43dd2a0735b9d5d3bc4e9257ade4f5f430ee1c0c7b6944eb89b4b94723d236f768b2eb4929e565b99475af0c8b4438d7b47297e9a5f2976f571fd0b7e5753a7bc5ba7cdcddb5a84f3f4ffba496578cfaf4e4f5dfaedce5df9cecd8ebe9dba56f168445e3fe78de2eb73a7d3dfaae9e4beb39e60bf46c76f568a08fbc061179adc9bca22eaf8e793d595e35e9400f7d57cf86811e0df420225a6b325a5117ad7ec2683d59b46a5f77f568ea9474570f3d625eb9cb2f467dfa31af537ac49c47625d5eba97c95f1ed04bafa787ce40daac9a7bfe92569f3e7d65a0871e77081677b7b1747777c7e62aef7450c9fc3c15f636252ee8573df17857fd9d6ba168836d21d7e2b187719ec202a03fc0bb151458fe721a139a5b4141e551ef5651bcfce5341f686e0585157ffdc2df33feba1c16ca72760bb97b36dac8b3a522b7d6dffab3defab3fce659a2b54a8f56d5bf87bf22cb45d4bd1539bf12e5113d2f7135c1b889290cadfca29a57b17fb4f6605bdd9f6ff7511e914f5fbaec9708ebc129923b9df79569855bd55ee5e467654dc1b34b9fdfba5ebe46b7369ee93fcf5b7d41bc7f7ea978abf057fad2c75bdd2e5f7d38c7a24bded7fd3744e59f9e2ac5eac32ffd3d5af78c8feec3bffee6bff7a4bf67519ef5e96f25f40adfaad80ab13ebdeec558cdb371995f10984c679ec8b567e39a8ffcba5c068120d6d9d9371b62a55bbe413b84dda28e51d26fde0df3e69e9fab0fbf75c64b0f6d753fe458a603e34c79d8a75f305bdd5f79b9c6392c37852c18b72c8716ad392e133bcce4297f8936f76f73f1dfe65eb4a2ad9fb7fe6b31d1b6ce1b7c970dd1bf6f6ed0fe900e339df590c78d5d6ed14b9bf4de30bf79bbb9bf2e8c37c7a8e8374cda2a7b3bf48c6acfa6a92f758cd2e808fa73af73d2cab407136d8e51d714ed7c75af0c5b277648dd971ebd07a7d831adf0993dd61eccb27cc78c129acf7b11e561976ed16ad1ba513efab59873361ddd651090beb4eeb333666d15dab4579ff7ec966f16c45b67b7b6ba517ee9cc74e9f1adfbb3a97934d0a1bba5fed0f2d883fd7aedc15ee49957f8d2e7fdbac8a3d78df2d23bd359e711d1ba6ff9e5301b02fbf51a914311ad3dd85b9ed10a3f3a746c738ea66e94af3eef2d8fbe4eec90a5b589ea2dbfe4191fdab8afc5ac2dfeac2658665145c1b3b5b5b5c19f415c2fe93ab13f22e56d3a47c3ee7a40eb67dbb48519a69c93ba75c9850dc499ee4ec84e77739bd3a7dfb07b3693d67dc7a83967979f737a0096dead5690850867b0022fb60b2564e9220967c04c21658fafb2a74775ece5e9e56a055cbc74c84de04837710abea4b5a787072091d0bbd50a8ef03138881fe84007f88d1efc468f28685c09030c2e72500528b2685510c1800a9491172b780203415840c9728169cd69cd39e7cfa02b3f2580011eef564167f8f86e1584c67b3208dac2e93481d3a9a6860235354d7400061d2041092508016854027e15128890032b627c51831a808053e73b2d09226a6a88389d889859e282f204ca0d4410e8420a6cf0008b12499041160635e6950734518509230861053d808a142bacc0022b586006d604169a28432787949f2b98810b2b8c718616b2703561a50928e83c207bb7827284d7deada0547100cd0144fe798f5c0f9701b10f1ddbf8a391ee9ef7f3f5dafdbc8778ced3de8351b26bda9db3a97134d29bd6e8ce2819bdba10a3e789de7ec3284e455ab58f2edd391be768eafc3a1fba73369aa3910e698d0efd8a1b8a51d2e1e68c92de5b0f46490742d3de7df51762f4ed3d9c53d05f96039bb307f8b3591ce9284e35adfe8f4edecd310afaf35a8305a3dcb3af93d6d06a4d0da7aad603a7ea2908a7daa53b4e551f5eba7b241d316cd15eb597ee7a48cae3560f8ed3c901a7534d4d1035353ffcf0c30f1a7c67e34040402ac0a913c6069a9bb5e69da39e4d0e154478820a3080a20a96d0b36b7c006dbebfcbd87f298f86846bdd3921ce06e2382ceb6a0fef60c39ed3478736ec9587778ee2d41bc1a1b9d5932d4f7ab75ae20cefb2e7377cc329cba7af9f3865f93a0f4ed59aaf1695be3e3713a3221338634c2146151b5c91460d2c26809868e22d5a25eda979cb2f6b3b595eb537316add0ab974f76c42547b34eb16adfbd3ebfc2b7d808610fb8e8d046b087b10638c715fbf28c419a3e851b10837ecf5ecd8cf8debcb1790f064891e30bb3ad41d7d4ac30e586db513183b4ad9b16110ae6bb79bc747d3c7181d724f8f29523a948c4d7b7af4786b5c70017ec5b5281557c86a2c674a1861f74346ed5b0b81ba3bca4b4a295fc7eee618a5a118d8c57e51099dc69bef05529855260caa30c8497ce77cd42488708fa6be2d4ea478e8a4ca1ab4eaa44a7542e5a1d7d7a507063df4a610023d7c5f1e3e2f0f5f97f7b83cf4f7200ede96af507a9182b65e2195c7e52ba402e16c322ed7a33a2d18467f507ef50667c32b862c89c518a316b224ccc07ce935210b3a2cd2144a8f0d29a4d00cf3084b906fbcd1dd3076775b69b8a5bbb7bbbb9763c73034a4224229d367ca1fa05f9f53b4ba542a0339e7a3dd6512601cf76828f3368f7836d29bae15ffbcfaccef9642eb5e292c65ad783892f29729522a4b817308381f4a71c135197b1dfbc535ba09cec431c6c8bc576f0febc7ccd272369a2907a3d157ed87fea628e7e3b905c52e9b4cb1caac09b90bd11fd4b35ea3aa704763cd884a62a268cd5b830e2deee2e253a55e5c1eba8443833d1797cd5d5c7a709941806f7a79e12d2e2f97978bcbc545879622c418e3741d63bcd2100b58951983dc46a087fde5617b79d85d1e7a737936a7def26ca4435a7b62d00fdeeb37fd7dcbd748652501c691f4f2d286b65e23957fd7977f70d9884133063df61ea4c239f7e68430385d1263ef8ddbbf37cb613a4d61d0b21cf6ab2100633784668c76fd7a536c8c39c411471c3fb593437dd7d420df3eb10699a35035255a6b48b406095d3ec25c64d1ea5fb58cd2d345ad392dafa7f7e9b1659492fac40855f2b5170ddb62dc6d7b376aab69f17218cc72a4654d8fedd1e5a4d22f890d01bd37c93276364343d03687bd35d3c97d01b2b73ff66dead87908de76cc34a1b1bb87ce0cd9b320d8b709d79cf389fc89e88f0f2ff31ae461011ed23cf4ede1b4aa8cf47af2e18a934d061ecd4b3987aa39e4f0f5f4d8e93a85acb9d90fece3f93585b61e217465f8e713d37cf0e1e296428a8b89a13ffb240d766a7c3b37bedd08b6d1dc1861907ce8a1f2c03cd88429d9273ca8c96182a971d44727d18ad268adf95af341822899232aa2d8157af20ddd19df373fdda1da390bc2ad7ebaebf13d638cfb649f3ca4500a0c4b27ecb74f2fa0fb45e7221531c6e8457ca94f6f3f255c2ea54de79c17e7bc7cf4eb82b4a21e3a8f6a7abe78ab739d733da67697dbeafcddaee776178a9cf3b15e4dbfbcbd2d791c2964ae27e8bcc400d35c97057a02066d7df90de7053a2feb2592d1daee6e53e9844c1a682d047fa4d0f988113a878ad1097c195b529ff731ca88c47ba8bdd341d5fb5af0def437d75fcff5be7dc9f6ceef31bd84a793e684408f3da61cc8d3b559cd121ae6d964d4dbbf9e5b16410a2b0ade39548cd0747ac3044d0fa90be248bacbe88fd432b3d09e57d20a915aa840d1e4171fbd927ee0f14d2517e7e3f9baa1ad33d9af92cb3f6fd831d21ea9654a2d0f47569e0aaed63408b15d6a6adfadb79737e66da6da12e78478df146639a79305bb61a075f7947347c7de2c88960b3707ab936ff89dc311d26c88e8912201bfe1f71733084a4d45bd6773d55c7273327047d37e6dced168ae59ae9540ff2ef2d0fd25eda999353fac9a0ca15c36ecaefdb5c319285ac78e1bfc6b8768872eb7992622030101013d0be1c3cfec4bde17a2e332af8bfea070aec8c047875b4541ede7d683faf768dea669367ed220eaa14fcd071aa685b6208c7a7e7a36d67c594e531f84a0add720ffa61093513b310da2500f19e5b84c6e17b823be5deb397d3b34610fa3f668dee9a1bba9030d3a9c5afba492b39c48518c7a3ebbd0d62bea1f13f815f5cf1d9ca753642805f4a841934e3b3c3d843d58c5beb7296f4bde6adae904d9663a64d4290a0dbee7909e18f57e342020202570ae80ee92c77b9b81a2c1f66bc74c131a74f7ed32cb69efec012a98050123cd425634cd74ea18690ff6b0219d118222c1fe3514d2cabf4722d11f77c5ed9342be817c0b20f3ea2737a35367a36d4e08e6edd3d9d44097b28484bc7ddd9007ec8f1e7705f5a6d0a43f226a9e8d9207ec908db23f9e9f342da7abe667a495bfba2c5bf4d0b66f6dd0a1da953c9be968a2ec10e77e4cf793172fff9c8fca5f37ca3f27e2d9ac9447f37c4e4d5b29d8b6bf52fc7492f2d339e19b09ee964518dd9c8ff68b56fa819cb87d13795910fea175a8ee6f482b0a601644af6391733e600e7470b2a43deeca379c11822271571e52b856443cbd40e770b5582d168bcd620b187cf901bc5bbd608cbf468e53b527424e415f9f70c309621464ba44befc79764210b7bfb7e13cd91f1007a8719ad81f903e955c3e827c4f3e21a04fd781d355ffb60a9fb3f7f3a8fae7c3df14c8fe6055ef46e19a98938bf16eeee566394b368eff617fd21920dc4fd7713fe9cc0a34ff3a552fb7ea2fe756a533f4567d6f3efd90c6fdc1aadeede2723d42b3975d12dc8551f22577619494ee17f3e72e48ebfc2b44ebd4d69b0e1161dc1ff0cabf1b463d7f9056270f7d37da0b21ac3f1f7d3476f70d9d6ab1387a21e59ff63d50efdc39759d1e8d13b2965f939a34ad07086e9887421b06b3497743a2d520ff823c9b231ecdf3d9e582d8cbdc08341ecbfbabeb123aa247894f124e16885321e8cf376883360a0c51b83fe61402fef49bb74d3bb0658051cfe55624039cbabcfdf9558453d29f5fbcb18b700af3e73dd363cf8725d1a4bbaf41b68c40db0fc229e8cf7de0d4a6dc3f6f00a72e7ffe004e5dedb4605ff76bdc1f93721a8c5a8f02840c70aa06498253d56d0942c53ff701084e3def00a748fffc084eb95594a07fdec3368be382bce9abeb12f747a4707f2c7971c4717a364146b44ed1293bfdebc568dde957100f05b182bca49306f9b752dee9a0ca16039a9d0562795778b53fe96ec1a849570bc65cfa9cb4772ead9a97dc710ac6abf3d0140519f9d25af393e4d372180bc6e7a3a51eec4b90d69a972eaad8ca75f825caa395d6455a0c691e6acd4b251812e5d9a5169d8d5d57cb6de5ee2e85d26f98477ac86fb88b165db4c82276c9e76a010393451673ce2cb4e0d4246d16736ab1138b2cb4d82e3fbbb380df353fb7fd921aa7e2b5d8cbb691c74de497e6033f7af42ab275d329461ff8d335c8284dd3a6e6fc8637d3992ea29051d1e768734c13a9746d34da32cd1fcd434d4dbab5c13a5fcaea9af4e7daf4a945165a64b13fab0a35cc0faecb613030f0061898090343f29a1d12e3248ff176203b24e622c5d0aafa18276d93449a93e4d33bcb99372f66bbc15d967303adaa87f11b2639901de29ee49be5906048d36b26903785539334616080ccd2ac3ed41e7ed2baed454e252030d34f3a64ae912ab615908a5785a5d86ffaa830aae99bc2a8183af97aebf0f61a721ede333b72d04a7218779c8af1aaf1a03373075de5a032ee9cb3dc399bf6222d43abcbf2edece845abe6c3379dd36336c7a81ae3ed3ebd44b1597213ada7afee18351d669b81a291a84f3f4c895a9e6d3350348dd6d3a4190ce521c15824b774d0cc6b8866fd18e521398caf732604ccc6501f2601c15a8e72e4a03f3d70ae10f90d5fbc998ef4756cdb7756486e8e5171cb1cd3c9dc44eb84a175d289f96ef2aa3d20d3c418797ce4ec874c871f1b8936c8284c933527d4af872890fd792af8b3aa52c31021c648f74b8c74d58891ae9718e9a61123dd2e31d23dc31a45af168db4ee19a590bbcb49ae457f36998de6a512add897e82b95a8639a52a9e4585672e86e9b81a285680ff6a19163d449db0c14eda23eef433e724849be990ecf74f821ca33a275bfe42177ee9c44a275a3f0581e72e99c0911924c4b6e519e394b32c6c5b28cfe9870aeb8a647ef2df270d477cfadf6983efa9c8c9a1d674462faa63cd325ed33ce3883a492324a59f2dd92b7972867435cbb45bf5c56a255f525df2d3ab1677496136f5e692391e4ee9e21618c67702ae67494b2a14de6b50713456fd974adcdfd68b3fc7a7ed16ab23cdbb447f37c54453601cd45ce598eb639a699ae613ffdf9cd1b6d96bbec8790639ae8d9e698c6f26b6ed5f4d3e3361f36b790bb4c27e4336ed597ecc71ccd6d13d3dc9f938fde1bbd79b2b7333c6e5515abea63eccd31ca716b625caaf62c2ff36ef5022b7f3dacab40291e159eb20fe94a6154bb0ba21b85510d7dc78c12d02bd3ce7ed85f778e56e7a1c997cfcd31cd452bfccbb3cdc2feba1c86b619289aa33dd83bc7420edf511ecc7928d4df9487dd09e1e2e59c298ff4fe970dd12f1ddca73d59e2ab9893fe603857b484dda7ef7e0e88de2af64b963c24d837e5817432eaedb33f91e5fdddca09a0bf1a73d2300be2bda5daad6196b3bb51e656f3685ea475391be2925b7bcb6ee9ed9de5f4cd8b9b555552fa6639f2729c6af70ddd43df289cea1cb85bd30bbe4b628e6da637855ef36ce6e6c0a66e65d19e3f775d4ebe1d6ef4e645b839b13fa043ef9bd7aa6fb8b9553f511a5c70c6af0bdeada4688157756f35492412892469e3a9d1a7ff44918844b27c45a6cd8a2d33918b30376d557b95139289e49064f29b671239b6f18cdcfd9a34930973b8434c8e893092dfb0b6d59fe7c98618b9c96f78c78c125a9dd88a72d459ca2149988f4c94e4eb0ec2d2e9a1c79128dbb06d6e5826c4f5215aa3fb0cfb94b15d46e105555e00ef562e50e3af51b6f154e9d07f5a9248de2d1241d2acf02110d7a21592b6aaa99c582e22892c1791fce6919674b97b8b8489444e22b9652d695d9623a24bde677ec3d8567f9ce4372c721f7e4b444bee9cc8395189443317b913398b5ca3753e8b7c44abf62ca2cbfefcdae2fed8d0e626ad2f9f90b29262454a500bbe3c9077ab165c79efbd6ceeee67cb5d6ef8adcb7e50fd845f1dcd9cef73becff93ee7fbdc8779daddbb4bca4c7af616023073e72e8b5996cdccb34ceaf0485a37948564c859fa484a4d935996d1aabdccdc722df351e6a22da315a3551592270d4e38572dc0a20551dcf800bc5bb1208c2b6f80772b1648799612ba6db7aaca74f6ebd20ab7aa3136e774b86e3f6e3c4b9cfc74f72e0be2ed92f7cd5d2a7ce775296c6cb31ce92ec318e54c8c722311e68ffaa36167cea83f1bcd79d27aca7cba68738e66d2ea3f2d1f9d7e4a76349256cd726d738e664246391f6da22ddbae4b47529e90e575529ee52c88f54c88eb9bd67578fa69d5c571853586412ba9a087d3b989539a8f7ca7bc5b2ad615b7e7bd591eda76d4a36dbab6993cb4adcbcdf2b8bd5c20bf3258b162c58a152bbb56fa81f120eca1a6c607ce82b05f30be1eee3ca05012839ac68351ec2f0b0de1d47331460d615a0dddea65216e4f4e7d75ceb9f7424798fcb60af7f9ee7b6f724a8a1876796f08fd5ebfa02140481f75af8acbf588509a18a569d04b5268ebeedd2a0aa0e7afa7e720b526880f3d9c6c186213737ec919a5c8391fefbd1ca85c2006126cd0461950a84215399ca2706b98e1e505523c49630c577080c210a230dfbbd514569071f16c868d02c80f3ee868da2ac1ec8eee1c0af6c2c6b4f95e8cf4075545f4980d01bf695873810665c7e60269350515d7a3f68e6683e6ed0f63943bf71ecf89e800db8ca4f0c029b7b2c1977fda0b033aeded98692f3c866b724a736f1f3be77c71dc7a51a4190acfe1c4486b68d59feba9078a87e72d966acfa1578d67a986d5f9a0a645bf7ad39c937f3e37f73c9fcde2f0a3354d5c4e7bedaf9fd7c9a8f7cfe1d3364a84103e6747cd1acdb9d349d3ea5c97f482eb707d5627a18c913281dfd427fe3af4e279d520adccfe2c440ecfc39136ad1aa4f5041d8df3f7f8bd5ea681f3c9a8754264f6f474382724669bef9c4df7a63d1ae739da14616469922c2d34ba44589661a26b14d22cd22c499868ea1c30e6e9e07db7edcb6032a71d229882eff175c63539b5836de4c4c6989cdaf16f93f9eb616f3bbd8ed8d8db623ec7cbad1d0dcc1efa36995910127b9be9af89bd0de62f0b7b5be9af10f636d25f17f636ed2f0c7bdbe82f91e82f11f6b6ecaf11f636ec2f0d7bdbf517097b5be82f184c6e0ee76dd65f30d8dbe65f26ec6df2af1cd8db62ffa5037b1bfceb84bdedfd2583bd8dffe2a123268709a644d246a20cbb4296cccc0e11743ea0bb87286898e53c274cdf176e20517142b43cb7058dc5d90663696d30feb516197c45c03faf0878f7dcf6ae004c2bcdd74972da1ab4da5d1a0d67e35c777142f61495d0d8683438274515052fa1845b6b09f2a45fd6c1e5092f371370e21a5af4c5712e15c63faf1d06180ef5bc3618ef60fe798579e7243784e47e3cd7f2cee7b369779a165fb5875534bbe67c3c8f9ba9b5381fcf7b9bbcc9ee8e910ab62949692d62c718bbb770aacee6d263f4191fadc01a50c7710dedb9fbda5c2043b854ae9cb850c1a20a0c04a2a2852a54d680cb5538557b8b159caa920a20b497f7ded26e689547171e5d9e8dc3792e0469f57ec9d6f90c860e2a3aa84c609faf168d9df2e8c2e59fd74bcbff40ad381f8f47978c5485b422ad4857485e486eb04d8e2aa42c51e8a0421a838485c485d485a406498b0e2a9caa3cbae8a8c2a94aaaa2430a1d544c5295ded25b7874e18186b3b1fc398f2e4ec84e97d1d949559e8d4579a0e1689ed31b94d0e21fad3cd8b012290f34fc2355f91d95e4c53fafbde57974d942ab3aa8fcf3251bc7f585f3f17874f9079d47174ef53fe791861bfffc062a3750c1360df49c871a3448e31fe98627ff62bef2f0f2ef86a07f5c64f082af3748f927450656bede20c53f1e5ffe41d24a0b164ed5eb0bd2154eddf08fc75752967fbb69f9423a21eccf296357b896ca067d51f268b868792fc8a3418578a0adefcf326b714d4ee938672d64d4baf3f1689decf5f9f5de53f1cff25ee17a7ebded87cc9986468b16146a4712587f963539d361d30533995faf32effc65ecd7c8fd6612708fa6ba153fe71fae1fae67cf69a273fda3cef9d0b99ee9cb74de0feb7cacaff3e11c663a9088271a13b3339d8e994ee427b499e9a06abff3b716a374d82d8baaea2b1ce558bf5ead6c02ebeed1bccdbd15c2f65ac7a299e958969c2111ef5f23f72efbc1391fce07afcce2723d2a898b4b5d0ec55e3bcb3bef28b8947b38168e8310fa0d47a065f243f4769d7d19631084f065f0871885f660545c6689cdf7dec42c0821b4b050777787b02bc61863bc302c5bbfa4a0d5ab4b033510b4025af1b43c2d5fbe7015aeb2589c17e74566399b45aba7af31c8ea784d9196e9fcb0e5f90d57f168c4649603bd19b524c7a85f9fed37ec54efe8d585093f13fe97e9e8c4156ccec7ba275a9d18e47cb02f75e7630dd71727c4d528e5daf2ecd735c6b3b3180ec5ce2bf67a7d89521e0d073d9aa8865663d073975fafd797277179a4335ced570c16adba2fab249e8d96436327a2034784e1ef245a4f4dab565d1a96ef867cb39c905b9665410b62385738dfcdf9a5b9ad9e7ef7bd4dba95cf6f7837e9eef90d275104deac4081f3d1bb99c0fd30811bc24afcb00fa9f8f650956fb7a4582a533a2809e7a3b12d34e84ecb17ac09ebad9a3f4f15dffc725d238701b1bf80d5d91f01b19051988443bea1c1bdb8ac4f9579048d7b2e2e90512db5cc2cb48d9be56c36c4737e4cad26b4d5b46543abf389708f66ca885910c2ee8edddd31084a44c51310524a1eceed1367eed8b163afe320524a1fde3133333b8805792b1e18e5de63ae9199ce5ea3f9d8e76467824c7999979779e5bce6ac26b90b29c7759e96150af5f0ceaf0bc3b20ce34dd4ce47080b8d424a704030ca31bb2cbc3c7d209c9a735ab0a0551fdef9e5a6cba0f3716540ec638c728e6118c6cccc0c99c2d1f32cfb39e15cc19e65394c2d28dad28a8946da88a495b6344718863114add6bc7315fbc7fb4455f5aea39c9995852ef8325e91e86755fc6cd4b85c8f3abd3cf46b179b9a763ab9d704598a2541ab6ec55e7975f36cdc53807b70b1dc30ce5af168dacbb30939745e3d1b25fce5d9600ee7cab93ea0adf268a0ef9667b3563c1ae85bde56e937b42d551e7a5c8356ddea9d13b27e124303e36114b6fda9a56a1bff0ea765cb9ae168a08b619e618d38a0a0dca1368a82763dc1f980d6c65cb81fd0af7542907842dc10cb9dbfec078b085a7561e8ec13c1384b368eb7281603edb9733eeabc20b666389b9b15c3c33dc3c35de3e1c6f190a13cd4f268a087f8cb43c7b6a0877e33e1e51aad3e725176c202f32d2e5a7975a5f266e195f3011d4382f69cd72baf1eba923935cd8296579f5a24adda8b53cbc3a63ebdc01fa882d995cbf5a851ca94710ea117edc6817836d17778e7729b9b7b346ad0a24b7727e4376783e3c40862850238293cf44a73e37c40bf614b091a0fb772ab18747372ab9b062871c236252d6e25c5196e0c87c571715d9c1a4e8b73aa9e78f4e0e1c4234a79a703c32a38873d9b27164e138e73c57a9d57b0576d3a6fd66e37734fec89f3019d09e70302457140a42dcf2654495f1e7aa92175a9242e0fd9a5ab7096e98c04a24baf4074499d5cc1ee447adc75a615881ef2107512f24855384cab02b040961e72583020ecc994876e4979c682b0280fad78e8eb355a11dd39af793618841edab60b035a87eb4b44a4376cadcf8dce32684034ad93085ab5c78038f445db67ed35270453c2f9800e9de46c30152c0d7488cd00f582871573e2a1579987be1906e47c40b794a0618c825eb1f5b8cb4ec2029dd73120b8eeb25eaf18d0439fe966c780a0634fa06356b02dd8c6041d8b12e52116291e06557968c5432c1e62521e7af302c1a93a89e054c51ec0a999872e8c87137a09887f58c5f0d2c2e57ad4d26a2db7ec8515c87899bf1acb020c65414acc1b0b164356d8a08d12b28256b115a68553cedb312e9c9aee9ef9afc5b015a736f7d8154e89bc1d0b03dbf2ed6cb02b4cd35eb119a513e9ecf04bb115a3bcd0da9fc8615fb115b6aad8aa0bade7053177edd8b1452b51793697d79216252b4a4a567cd752150fb90a272e9d9180e521af80e521eae48a7527a148ab02b47cf4a515b0fcf28b3ab9dca22a9ca55501583e4aace7b714038e775d8881e5799b8974c9c6f1cf7f7ceab5293aa8f80bae834fb4e49d94f0d7c318e8a3ef8a07553e521ffe25f29b40de4a57f8ab31de3030fc1531de4a63a0e12f89f1665ae26326fd9a186f2119fc35656cf8789d3543a225a38f2e37ec4af6bbb92ec8c0c1efd8d18616f2cb2bb6da206c855dc1b08cf1150be3dbdfa625cbb7efc082c60446afb1cb041c8ee54a9e0db6c2a9f3dbf48df5d2aafdb585b60b5b79f9e8fd05cb7704d2f24458b4b9d01e8cea8f34b486b66f79c86bcd1321012d4e8b57ab9d79a87142302e9c8ff6f602381bcc094bd3deb4ca7cc556a7a515b3c2b757d22f4da435f4d5fa5a83ad9c8f764b091a8a51eda1adb93c9ae8d6e61e4ded2e1fd9a0bdf4529013b2df3e6a6bf9e8d8ead9389cee2d8f26bacbe2d68351ed73f5ed33734ac7569cea6fc7ae70caad6a00846fc7bc606eb4635938e55636c8f25db131be312c5cbe5f0db8f88a75f9ce806a50c6574c8d6f9c8a69f9f629e30671aaf690c2a98aa2c2a99929a91d5f570ae8ed284eb1b7f3505a3d157ca5298286c07ed934c9c4a899e8ae8b2a563c9c89f4872e7030e5dbd97970caad62f0e4d981a800dbc45861efa1460ff2ab2f4e0df2046a9912c513d10a08080808e70ab78ac1135f5d96679c8ad241a3caa613062b9b4e18b0487114523ccb3cbf0e1a54306afb2a821ce7511e4cc37ec3a727b4755c1e83a1c6ddd434d3890784703601058a2c5f5cd1832514210e1c787a08af3ca1e221ad73427ee8cc30e30554f8c2c60fa0a44187a3a66cf1d079e0d4d6ecf06e35258a294190892b4b74b9a282386a9c76f21e281ea419a643ea3d1865a561d19e9a8710c2e90e5e5c1ce53d5c73a804846b78780507647868519781e6de4f26cdbd078a0707ad294a58ef110511a2c8c157af8953505181030d5c9ac08427c6a041ccb36b39927099010a52a020021ba4800626704ccf6b2842501c4c7c9104a03560948065214217be0c010657384309d382129270fa6bd4b1a5b7c309e194c20b66808083248864a0c1772b1c6491a901074070160e72e0840e37e1bc0207321859a18ddead701064d12a6d4061cad6d0b0338ca468d817ced0b42b88b8683ab6c8daa822041b549102091af66e55458a11b4ebddaa0a136668d917086f5086874fe040e8ee8484bec74ed120a427910fb41217445c683058bad0648430ca8236ab804333bd5bdd00064137684214042d634354052de60b2325b41cef565354e1d747514cc1c59fe4e6ceb07f9a82080f213cfd0ddfb07cb79ae2077fbd5b4d9145088acb7965666606b0718dd87372c0e1061e333b644e3a627298604a246d24cab02b644d191b3e5ee7c3c5f1553cbf61f7a873343fab626632dc7bcf9756e7f03de7edcfbdf79ef3f7de838ec2e1ced9d739e79c5bf75270396733a77c34ce7f52d0f9ea5cce0961afee72fbced79d0fe7aadd062c95ed90bbe1e6ba3da62075ec91c2c7b11d13f890c6651fb0c309dd41778c2a81a1b143e89c63770d61437e4940c1af37840e9274ba4c61144307218410428f5a19ca48c51a638cf30a97a3d0a75fbddece4d27a338b4d2d8314e3d77ee4a6068f0d7d979d621a31cdc2de3d2a183feec93112906cb6182295d215126351ed494683d392a07ad35989218d2489449fae3c64b08e4f0245f2bcf268715d35a592beedc19245a5552688df94a79366b6544eb2994518cd60e58b1b256d64a94d2c4561bdd9c8a514c878a8e7d744213614c62622f0a1cd76cec04cde5a15f720d5a9d13703837dc5c9e1d724f7399417b99a2add7f6d25e9a4b73797845416a6e770a89de6e39211616c593266a70a63340fb93e94d384e28f413daa23340cfef1f7ee89765d1fe86578431a49ca894ea562a843dcc39a6d08c6004000009531500304018128a4524424d17750f1480118aa64c62529887b32887510a21638c01001042000004064468060d005ff9116c4f27b09d0ad933b01a8b40c70a8144530f025bc3986ac94b0c63b5e66d540a02eaee8d39f174c33d135858070a81b591accc5f05fae8dd37dc0e6a0a5b6839d1bf786c4678dac37cc4e002853804a541e38152c6103fcb376cce6ba0f4f59c4c6f03c062dcceb0207af9a3c81cadbbd2a1087743fa36441e9f4b5fa0e0f2960fc8b48b6ebd1109edfce22a2e077b16f221684f287e1006106b2327a54db4f062beab16012e9f1b5f201cd35bb49101c0da521ac65add23b6be00bf106a1e4f9cb377474a614d7ad147f469b54d38a06ca71be6314e51011b9b334a9cccc2b2851e555e0f81283b1e89a20f4b8541de1449323e668d0396829ce2dbd10056927aae828993f4a7125e44d453fddd0f91954b52501413648e358f7176f24dbcb66df0da146c6aadf484eb21afe02c2c20b587aa9081faf1bb2fcb0cd743374e9b0ed14039e838407f5e8503b28a4c87efe8892a5db54393644afa077d3e5c39218af2a76e2ed486683f0115a2cbdb12cce0dc8b85a415df4b1f84f4e620a8bee947d9682baf8818407a1d5a7d2ac3cfd0395d6648666731789861549d4e51c86c3929888c0363798341e586e491f45050773eddaae96176106675845bdadecf317d82cac92716dea34cc9cfe5f6ba31747d5877de65bd5ccc899b2da73b6862d72a5544410464c52e4d4f5d7506b85d5af7b4bdd88ee7e18f455f11b7c0e722e7334bcfc85bc5905efe2ae2a0bd18ae49f19f1b613a3b445c77b2776d2dd2e0a5799d816d5a92111029b5493ba14e3719fdcd737fe8ffa672ae8fabb024917a330234be65bfb3ac755f5f489a77a7abf405f0fdc6394b96c033482d6b08731335b33480d0976cda8c8ae5ab8149cc97a07d5199656fa2efe03462e6e44a02053713937dc2d21602051693dbbc23e3d0e04bfeafdd498b192fe5e0d35a0508ff2618a7070e7a21feda028df13c29d865f9d83fe9ca90221f8a01605e2a78b6a2a6ea9d92c8e852137575883f0bb9558127ded1cda67e239da08c1e4fb83d56b40c165bb8618991108bfce6d202c20102582c43cc8b232011c70441d06003c9873bc3f41bc95bc955cb798643960003d65544c8593c3cf29c717b61297cdc8d1fda4b508f84d5f4f1822fd797e816c188c43c0015d6210a88bcbdfda547282d3a06c2040936d859929a4510ec128e11039b590f4807ce832c6c231cc3b924f6f612c43e8d894e1e911a7e90b839c55a39f9564c4701da5e1be5f890d16e4a35d85be8516bd6f083c1f5e503f91137c424c3ba7d6124ef4b2fd2efc473506239557fee747033b893c66f442b7d920a2c6ab774b107443ee034c839506b3622fffa2983ad904bba6bfcce9613b941ff1437fa6699adb0ff0b70fc52c6e56584896ddf7030c3288ad026266e0084d4b71838a8b21278384c4fdd87dd32d30d42956556635f94207c2eaaeacf7f186c987e223e29f41dc7a562b6b1e0d392854d5f7967d8f750e72b0c0ed89a697a8562edbde903a96bac37b4dd10e17e76c599cb9a43bae333065df0f43b5221a418b1473e17aa3810147890b993a2b078f3d34156aa7587baae867e29ac254cd52a2429bdb2688b47014eccf0c6fbb86328b08933136d76e5c69ca3a23455e554304b8549aa4d508d846aa55429a58a49aa4d526d864a645429a75a39d54c5261924a13542013120ea688f6d093b21e0c02454611febdb8dcf7682cc76730fcd3af8776d387ae78bf3ab15985b14c894c7a9d7e682d89b2d01367a68f5ee5b25152ab8e15bb7d6e634862bca552f60ce655adf3fa634a8d1679c7a38920e33da2bbeef2b702013f74c87754e620e895c67a0b7165bb464b675290ae3bc73077c20f77373b0e34e43e5202b776bef3546233d4bd6cc1ea42e65a5a23b481dab7dd21e4204d29d4c6ae07751207d529d96aa6f834cc58b0d3d7ba5fbe090b3b67857d3ec20af293ef257d20da4a4e2040c9cbf17deabe4fd4723889c34348f47641a3169e680df13a10534910babf0b5bb2645bb476c029522798def4337083fddf1ecf990d0692c7251e0ca3c8afa0a24f441ffe9b84d2a6c8cf3f2b663fd6a77b1d7601847bcca75f4a2bbfe5fe3a04607ea46deb93cd93f6c67415333f8ca5bd43c243f2f5a7306f09afc2228c8eae48b35b27c5dba4ed94b9427b4aafacbd4d359bfe43b5a2906278c435ad18700fa9aece4262df92efd8028134a61bdc95d236ceb876bdbe05b6c2c75e4b5c4858020f80d58b69e78ce33d81da3ac4b74f1e55dc3ac228f30c18a2089f5d8f9cfe62f81ffc357dd50e2e07e14fdca8f1904639555280ae1295caa826c4b0a2153f3a558b003b21b7634f72f453065f88706cd2a0b8316b7341a81893084b057e6802e42c009b86a05e8a07b420bdd6269c7abe11c73f60494645bfd7545b3c613ac3b99342741826bcd432589942e07f6bdc69eb222a571940c2a112cc981f51fe178337e2217c72c1f0ee88086e5304810009507c940d753e6efad7cfceec74b5d44a48710afdb37a42244e935b9f953c9a6330e8edc8a55d3d7cbe21bb5acee8cd380e7a5a40232de79ed7b8a20ed2ce8aa5ba1423ecb0d3c803a09d2be454867060617918af775e530cc3e23aaab485167cd467d37260af770250b9253aa4a595ef25fef6e2d9824b7fdd761a4a6f6e156fa9911d68864b54a0cff06ade8933eec39768e4863a1d3efec68b62edb9b31af0e490af838131e87f3a89cac596809834d55e9dc07a9a15fba5d383c9fd572fc7f632b5766018f9453e735986e6ab71fec86101469cce55af3cded7e37b712c41b087e4d448d6215b80270a6188984465c418534de20f661a0b5d867ebf31b0a41bad68ad31369132158c0cfb3968ab2a2694adb8a85012902307e93b8c2ad89564b863b527b802eeb0e75e95905bdb1404c8e3737e72e111a2707d347e10a43a99a8e1fb175598ab75358b6dd16f3b01c2e6924cb9f0ee3e6d4c13c63680c7f0a67b885016fc23aac541648ff9a85293370db3a0dce1067617fb7afbd03c216ed4661264daafbfe2cfed2a7f44457b4b7d296add1ceeaddef46441280836f1a024e6853a56df5dc1ae1b80316ab9d701aff0dd7569a24e5ba4d2e01f892f4a7925949239df3a53a32097fd98132283db01f2e07412d74306a536997a177f5c2416914690faf2f782337a328f02a558897b0005fd1612ff3b65001eaabebb6e4a61f99e4acf57b38867cf59159cc3ceb13955730a2a761f8a14946753dafb3ac8e7a179272e887550cd6bec7d5c11f36007b63ca6dec405b10ee680036ffedfdee212f10776e001ee0005db9c99e74fa75330ac6395be58625fed69f21820723f726176adaa62236513f7b64ee19c2134a7bc2f995205a4d29333178148695416da4d64de0ace4db85c5c73ddc2e25895cb4d92163d8fd263add83ba797a913833090d0857b1f13609d2dd227b1cbe5e3d67a5e3b834d62744bfad6a19c5155ef832e9955a770e316b3ec39e3f55b4c0c58cdc84b380e2e3785b14ae7cf866e16679ad03275ae8057badc2ee1c5e7ae7803e74790f5af624098653b6687066bef2493156a7becf732421895a47411085bfd39e6cd5a801e4dc141dc28adfca8f01557926e7633893b989056df0228ee82dd5290506f9ca066ccb4d9e25f3768b2ea59c7520c9d2d27dfc0fb8f4a76c39648ca09a226e85cb24552e525aa86ec77e5c81615d0f023bb85d76657262925100c40ceb593ee6edb907877a2d3e1417b7db0bb82e4551472a0158ca7bbbef13bba7210b2e933bdc8aa5059c13792eef0c14614fcbf13b4dd4889ac09bae75da32e2fb2252b05dfab9c8692c60d53803c15a2012495b42d9c90d65ecc17c54963025367714d858b4034b48de3961eee4fe8bc4503e074046393315f3aa2e26c3a2334d3cc8ce7702ae27c752a340f39a1b00b121ebffdcb0f59646d50cb353c3e172176a0cbae44886ccab9bc532ad51f77ce6c11f5c0ce312bfccfcd3c25a6f8ee397b0866dae8799b67d8d967abcd37ebe6032192d3263b12cd9275b5246c754e761d97928a0c8e144bdd0b697f600dfb210850168ca63cd7f641d43eb9c123527d6801f7f68f502fe89efae08c5cae05b9a6da1db2b68b098efa4dfae779e25f313f5d09b628874252665506b46a459ebc482982ebd67b06f82d14e027ddedd67ecdf8beea3823b29bf9d5fb81c22d1655bfabecef7963b5fc9c33363b90672aaebb8b3b3cdf129d5b8423b5c31b3980aadb23c3db3545902f8b9d25f41c2492a02517a4ed6d60f6a552d280b5fa9762a557fa9ea4e6a00ea80e5cf6e7d667013b6e42ec27b3396ff6d6e4e7ddc9fd59d7435cd9e7670a3ac4d5602e49312d92563ab03b231123958b61f54deb206ca5ee689aa9bdacf676f4958ec5641f1ef9eefd38e7fa8d2f864d54e4df8c3c1972d8dfadd1a2b2241026afee4db62523c02445a61bb72e50d4f702eaf0d9753a7b4d999bb03e79c61f38f182fbcd4d21d6e51368130438549a1242acc1f950445a3a5b618daadf2cfef20c101054d3b137979632483a2878158bce5aee07d1dca4a0c131398bad83b6f2dc1e307ea3f11869bb86ab3dd7b9270f570978cfa6956c5ebf06567762fcea9d14ee6d5f84d0050ffb555d1c0f46be44d7c20807f6f0ff872f904ed7a8f272ba42702bf6ffe0699814d219d8e6785716c26187b15f483f051dea488534f2deb809a6c8bd399cc1c0b21520c0a66f0e5e1ac9e664fa21aa4541576546f46fd87d260d8e5af1b7ff06c3a545b48045601701d24bd5581ed06d703c6ca9a6ec3e7070100d8c9fe5585a26656598420c2342e0d9dfde2500a548bce0b92bcdc2642afd8f47ca190f8519ba749c44d0d8c946b99dabc0e10311b7a03a2229ae8a1faab9af3c5c682ca45af7fe2704b7ff124f8be06aecf61a88574f2a08a624c2fb9562c932e52df62fe6ba05366b53e934c833e438fdcafb9ad3ba96caf53c43085265279ae71ab2262a233de2905866905ed10eac38b4f2a24a727579d889a12753ef8b004db33a5e0b57aac0d0c4f1990b4d98ddb3aba021c183eadeddebe33ed4acd95d2754cb00522e9c665e546d226ea2d71467bd41ae0ca4b63dae81cc592eac1a522c27ee1346c6a07026244b19c1d47732ee0ee70bb016dedba11f99b0ab9fd95df4fbe8548a92579a183b9a5523950e239d4a1fb6a356e1db741f314344f6c1a059eee865ecc4c3ebd142a911cad24373be3db40a2426dabd203e36089164a060b3a3ca205d74976a93acac6ef13c8c27b45fb0addc2069690a0dc491544aafc57636c01f3e304db3e03e65d39a59067eb32505bab1930173ff2ec6322b26e14167c066b6f6737d71403a68361f7c5bbf66a408522427d780e554ff83b0d9dc397ca5fe6a0321780baf953f11b301c2e99990843a43f9a1c9a306013ada5c85d0bce338a8992ae096cd1372110c5d8fc5ae6e28cf99cdfd438e3072b7f0abd11e231cc02dafb2c9e4c7c444ae1a6b5c261b40762bc0308f9ef915ca15f296633d659d7f38e36f88b7a132ca8e040c71028831350f7fa28ed7938b541d87e2d31d6fa2439b8481ee1d0286806a5e7be4c45d19cf3b406b68d8ebfc93159024deb509c839127c0e20b0c4011ef86682de15dceeb2f982738ea37a0e858da23e3a0de9b44ea77ed4e94fbb4f9b0cf5632c7134cbe09201b20e9da02750b67a90eb360fe007ecdca17187e6ed7e595e043a7d758851dc2b7505e925360e3ab105179be05213177003bad946c44ad915da6eb9b6ce3a6439c6b776203c878a0aebe9e36bbfddf38de304a24677a7010c848b76f8010d7b1047d6fc80ce4b3333babde7a4fbac6ddb708d46e29c1fc7495386ddf61325d4a4e4e7376595704c40af12d2f22368173f8187934b18ae971b4102b412cbfa63947dc1ed8d984489784aa44bb29b9c9268fffce105b7a2c9d3ce7a05f5179cc227ee1fc50916e5fe32c27b8a0393620134b737ace699cfb29a234476dc9c19f006160ceda0cfb5c8e276a24d35271cf077f2ac1be4fb58acee813d50bc23dcd096b5db8741f6614ac2b56d60b31c966428bdd40424f694774a8a803cc0f8142dd18634207e9abb8dc18a80f407cb0b743028200fa47766ebcaa59c463ae2a5adec0421c4b08cb589ab9b8910a27f0fe12a0c1e2e2008fde3c0bf325b6e554dd8d6a212dfba83fb2389654550049075660e82b9897f533d9acb4443f1b461a797748816b7d96effe3d54af8eb59cea658d76b2cce92232c5e910a6a108aa35fb5e205a462f95a7ea0a863b94bcfba80d7748d715c714d4a9d791a120f8f14c540f571127d04a95b85c3c0fd4b32d1dca0ede44a7b942a8d4c1d6f6aebd90db638fda21d3b053e064f6a89a4b106347471bb2548338ee8951c2ecd5b9a35e826540e444f3caae7e36f20bc95345c0fe7025120481c3da21f2b5cbacb0aa2fa2f6ada1b772f4cf6c5dca59f069da8b1900d986a363ea613dfae065ca6821c8e7cf15321c322aeae4c5358e058ffe78bc4be88fb639b08d8d3249d924c4811a7e1a25897e6d0b58e7ec1caec7f942ece9f304525ee87a46eab34c4a620e88c34e8ac13f32807efec6a63de8c7216d9cb2d7b3ac9a07b288e5cc4b31028b2f0a07c630af095476abcd911885f2334069a0e3f27290a0b3fec0aa715fe65543fad0560aa219781aa76babd57d73df4c044012cbde74a9d60d98676169753bb33d31d82df5d73d4b034aa15bafa9f878998217fa5955d67ead3f07bf83e710ad7c4517e33f985fd46a9ba19ffd5bfb7473839333d961e313436806c9668f16ba12420e2261cf0b4137320a2a08f6a5ed9b7d73c38f7bc047668cdb8338b2d0f6918de0b3db6cb6625678ecb1ca7dc8c91618b55472490f182c82ccbb5357bfe9ef04e0c0db34ed63dff4d8ef80ab9f984dedbec3a0a2273d6b740cbd0c6589ffa4f8d3ecc122ced1bf94fb4ce9723397cdab0f1f85f2d406d0e8f75aec2ba09adca3040d782d75ddc3c04952f44f0d3ea07c9b5a6659bbb440ab1144bcfd9116a65260321e0ed008ef964bce2619e7069ceb697f8fb97681b3ed9e4b112d7761806bd3db03dc77fdd92d7623e143a802bfc89b5d98a8ee50a9cf4a1a3af32ea0466552bf3094dfd8a4eba2ceadc78189e25158fc84250f7439b8e0fa51e9cbd0c95d9f1cdf857cde473f636215b20a56dd1c612b7d36209861e1a71394dfea46c42370222ed35e4e851838546707512edb863bfc0e1fe252d0aa103e62e15330b9ee815f1e09c9e3360994a45f01437dcd3d6353c8639b53b150bd0f4e19e10d08f97b9f19d2b301a2607713c85f90e636e6ed73db1f8eb79bb9e245dd5ed666daf437a184da6ea13fccb49145f49c662686d158a4a5917505f27e79fe9adf58106efe0efb7ddbb9da616e2d98bdc2ca344273815cece6a2384a50a4ee85bc1dcf7715f77ec4593347d54900cea9272935edd02a553fd422442eaf1498e90d0fdaf0423d1b53df573154b44ce174c0d23ac086075441ebbd91eb27fc7ee0276aa9f4b76e1316a8c2474c8035dc249c3887ce3bfe5e2d15c5610bbce33991c3226b1660d13208951dffa1341d983bbaf8877c8aa8be38e5e2c3dc77241b775fda4b88437108440ca12cd98bb3bfc40f74edd67d65ffcc1cf762b7dc9eb5986fba01bc057e4057b69663066f5202a978ee924bef410029a328c826e1b1bef8cc92c801f1bd89f8e5b2090e556428f286b764048caf811204f8eb1de81d92b801977c420d149f4313bc4e8109c26ba4a8d1809bd62749181a4f9d2e1a97699164883a653e7629e7f172a576e571da1af5ed8fee532bc68f992ec236d00cee6ac9696c84c11b1e6bf09388c363e285d0d0d4a976a32962a3de3215231d416574ad2b0499ee67c8010d4f7243dc6f830e1c92d552452bdb91fc0968b49870f90f6483109db35afeba8fd398a8a4711c1792bab1d6ea88580f634a93be92e859b1224ce4e1c3231ea4531e29c005e7de15ac5272848c34fa191148f19be7e1c47e207ad10ae4548b077127a20b2df52a4eb2efc2b4ef9587f8b58e481880856fd1764f012e1e65fd19143213802a518077ad3ec9e8e51f83c6906fc7e8c1ec95fd845933a645209867221e936a96776250e7d508896d03d6b142a243828304196b2f39bcb3a6cd97ca12f0b2b8018cdd35b7b4b27f18b9849bb5663b51dd0a1ad23f805b23e832abb360630cb61f6d885ac173051fe4cb262fd8f0150b4791c6ca3061279dada114ed159eb57d7963b1959a1604b7ef710516592616568a1e5d619c54687b70f37bd18b4d8e05c017d4b162fd838e27ac7b96586b19dbed0ddd8cc3dee620cbcd95356578dde83c623262e08d4ad6bef004422f15215b11e5c50a4db1d1b6c2d4419ffabf96f35534483904a730e8d06ef92e77a349963c4bde9a4c65ce7fee5d3fe0f6023ea4bdd7ecbc07885577b9c0016003d0bf9af2ac3fffdeed79346afb73a68dc9052436a9e82776e8b1a7407337e16de8d7a0a545bad4f2df33af3a1dce9638c13de89c619e506645525d865c66d3db340f2ed7addfafff0a67014e5b20cba81bcd232637b4c65bff61ecf311b1c9f0657ab9a293e67acb50a36fc291a79c2bc55fef8e5967bcd833b8d432cb650ff5198ec2b53962b30385e02396835848d8df8c4263f937a80aebddf80482bdf5d2f71d2acfc02b8ba7dface7f75b3730a2a908e6124b1dae026073348ff84ad107be2bde15a198bb5128eab7181d45aad0c41dfd4b9b9b75a5553005b44d2416b6f5143ce0c1cd721ac370267c63900c5f52fe07511d11637d05ea85be6d9626f1c7216e3239e57cd106032452de4e140c6cea9de46a5a61ea2b7a82d240e346dd11afe271f709e87adf25899a86fa7158e270d68a2af81c01d8760d092c3a7d96c59b4a191658117247eadc14796ca3d921f94ff833a303156fb0079521a4823439a1f732b0a86774e30e654cb0b31330708207d3d75f374124c823e498041ae8345065230d97cb4cbbf8672b25d7254089af454555a476974f97cb390ee620a46a09039ad16d4a6fed38e92b748fc9f4dd9572bcc92408b5637f8860689053edc08c337ae14be910afa3e927b0f5fc41f2c04b67edd4624b2650da212b8b3f10e23ea8bfeb81517704e8e49734ce1c1032e7b481a11d0222d80632f3f5839903d8a94f53102b957a73224dfff7bada878dcb196227ce7d531fe29a38b9b52eda55530afe72f51d86a0601a5c70a4c54c1e09200dafc2203aa879fdcb508e9a1f28343b6ab2c2baec9ae6b871d09bccc958ba4d08784dca01533f0d02a2b5e1b017a758667abab52407a3fffbdc68f5fb4c0fe2c3a391607b307519828c0e91f6dbbd087609e4433cd5b4b875d1eb3b7a5f67c035e4eaf00e476c67340b40a1e063c20230daaf278946d539de8238d4d56979c62908530163ee4e5adca97be748517ee2bd6e0390a1f9628c14fa382ebabaa24977d1abe5c285a2a6145d49421d63f987e8afdc1ac91747e82e491dd12380a4d7e58da6619e753cfd125be41ac867184764a469070c61614c7b435fbbf1447be1967d04235a02c99f650201290919736b895e356e46d1b4ffb2bb82507e88c1d53495a456a85a39b02aa27866844712e1cc4dff000270a54288a29be5cda3866f775b554f4fa4692ad33fbb1931316898283a84bf99efa777146d0b7f02ccc0dbff89aae5050e0177114f669004187845c06bb57001b445075843b9f61194f2aebaf81a24781b9420ff4ceca49d2e7ab17e821bb2c918115d97832296f883aa8cb2df5ae1d1de62c8f5dbbc62f47fd9ebfe2236d38cf2b1891a135bd648ae883d4215b73b4af050486c9de331a9e1adac6460304e1ccfe64fe76b83d0a28810f7c88f3033c6259c06db782c8b21df98f0b2458b012ca7a54a75a48b0a6c5a248404f5ea01446e229258de3168cda004b2704930021e7a603b713885a665f9367454ba1c61cd7f1c965c376d7ce7ed3c24804429cc007c4740fe4a9cfa2717afa4db2f7160740b4b5ddf522f3f7ae0c62c09dfe2dde057068df2547c975481b7e7709b6c7a0fa54857382976e937eea04821a135153fd9b2e20134d43552d0fb731598c74f867622b35e16acf3968facc8309f8005731c805fcb73eb311e4de2a9be32c1edeccadd78679d87b327d237b6d5da55ea5bd6a621a7f9bd95a80f940d8b3d7cbc9caf312494b6a0d0af8137832f3ff04abd4a8601b2fed2db97d8521d3a7dcafc2424cb44973bc14f001d839baaec45fa94deae671677687a62307272b291c502de5fbd21e8d6f26c077d01d4ef89140c513e8858a2ac86440e7f3658c5047413f40a49fde606a21f090b7324eaa5b5c5fed28716259c60c8c161e0be34da64b7804234890aba854683c3e0706c5c265454a0d8a08187805d848239341f76f04d9e7750722a193f8d382c49f0a258dc95736635ff1790fdc26ee85dc6a400c7f84d55572d6b9e4ac5e8e40ec1eb8cd1bed8cd622d7bf022642a50a094876b7cef8bba029b12549f607f2555a4d0139e53fc999d695dad98bf9e1894550ac1f2ea0aadc829d74c9cdfc3ee43dccea8922c09a0667ddc75438e994a94590e8dcfca287ca70c6238c13ad9cbab5653f44c4ae239f92c3fea2c12fa7d9745e31d73a37048423c2f8911ec5d4c20718786675ff9ac3cf2135d91253773627e99cd1cb54243a0df0c5139e716542b886facc2b2a91651dac387d482b8b746950a6b74fc432d7880a96ebc958ec88f5444158c87f00b6164ce0991f4dd4628c33d1e573303648a18b413af622ac2fb3fee37a1aedd889b9553ef086108a2fac54a3514631f5e1578e923eea35e3d4b17d92dabcae8862ce6ab9f394a8a249922a3b0be899922b3d0d07eb3f541695929a2049514331a442768dbb528f354b6ce24b8cfdc2a27de1da5f15ea964661a87242d478250819c1fada81bea370ea405e770fa26a4405cf461df97cec688bb585c4af32fefd87961c0876b7ac7dab643b26dd0294c1ed94a384eb027595f045d4538fb9553ff1fe188b4c204bf56467936d04076e99061018c17cfd158df3218d007099af632d57ab0f0c7b09c01d37c8253e31b9069551791e2da8cb5cf9ee067081d5fd4a690570f1d4aa34b436328f6afa16244efb0d6ef2b4c91d8b90fa517de988934cca2f58be44df9a6f36ebc868cf7d0c3f11fec019647cb419784b1d54c4c3fed09009173ac9c566c634113b97547e271941a73423746a365887df2b1ad4f39573eb24b3831086dc47ad290d9c2a2decc4cb6ad62e36a3b4ea38629b46f92a273334bcc2cfee164749c71adc68420d14a8e5bc7753991f4c8889dd520589c8c49bce757adf937b42ca5d4f1a8c1236a69b543981a11c42431262f1dd0c89204abad2234ada69deac5a96be91044d309bcc89d6386aeb084e93b8a0ca0a3c92e9b563aa450d29eb7f65343abb727958681f9e381ecdd99ee1b68d6677998f67937a4ccd60b3a2d73b9073a5a30c02364f4109a7fbb817a430c9072eb67245ea5128e19786ed7ac66d2860220906369afe79e1c5404b8f54cbd7669e9e5a8f0c19496629e1dfa8578d73bf23885ba7faba2ffd0a830723eed1398b75f9e7fbf271e5dff7cd5bd13e0b990d9d3cbbd0371c86df430e91216dc6337cb033c07b23a6e1eee7aee2aeed51e8cd72495456a1773767538e4fecd2e7165992d8bc55658da708689957ba8d5c7a94ca5a76fe97fc59be376280648d1d56de192331d2aed42eb10c2a13e0bf918f3ecb587635fe0ab492e84255a78810c40b4800e602d8267a765175373ed6b11367e3fcb35ec055fad344d629195feae384ccafee9e29d1b233a7f770c5ba910bd7f51891d9d96120b56ee798fddacdbc7b3de383932a99776920d4fe2878c09f23f336753f0b3895309ea33b3a5012fd3f45dce74707b5d020e0a01f7a5c36ba15578dd2bc9e121dc97315cfe678eab78c777f7669c85a82ecce0489a559658a334abf2dc46362263b48228116710980e526949925e71eac62897b8d9989df50e128798e924a75e15791526834daae99c4a0e1c50a07ade5ded5bd68c1757bb7dd61151c325848b1bc8a371ca12d9bb56a5740577d38c5610307785ee8021487cbf0b68057e1c28d053002987bb96e02a7f872c329495bd5f9b18b7b702787406483fbe371bbf62b0c4cec496e8013fc6ba94123e9f5ee83c31e4b03a5037f848c4d6861b609ea7fe83e06974f51e8d02e71fd25a41998cc53971038b39a5d267c653e4eb23d3f3525a26dc662565668ece748acfe0c2ec8dfbf3efd0e9c3b88a6c7d34de6993a1cb248c5ee4e3080f2a11a9cf54aa29790adc83f6bf8821b99c1ad54a462d78c672e4eba2e0cbec7ec3da4e04ae1fb8632fa730a878161c69401ac044e80c53dfb2f7449644d2c7d1471e51e2732bc51541c3b81074235677c787444b0a9e38839cf2c666db89d8290d55cd101029e893714e340b365f46dda678f72ceedd0771d344ca2ee43c6c8b54295a31b204309dd3fca697130f8b203818fad1c82749fb9bbfe2f6e9d927d41714d880e1139c1e7aebfb90085bbe54f0968455bbd43d72a800073724d7718853b2615267be8a27a05cd5fcb11f2b25cd4e28a37df55646f68284d9ede33acd7074b3e264b933aadb68d47a0862bc47652504eff9ffd3e8504935955c6df2be8fa08a029535aba11f7926bedd13dfb63406e65cf6204702c3499b1d6239676656077b431a99904175935cb8db2064217537d39cb42037f6d18898e1572c6cbc9b592b8de67c0fed8d5aa140f7380a4a4e8817c29c6acb14b8631fbcc48d04d51c64dded9cb5179047c9142ea71da276a66164bb4d6ba1fb1a16b600192c586ceb15171305bd89a023b58618424dd07dd424e570e7fc6b29c4accb7a3a007f4841b80d68e972cad750bd35f4ad69b346c3405aeb00abddafa69e8a87ad6740a36b651fdac45210a45db53434849331dbbab2bb273295e9ec0656eff5e0f10498f529b088a8e32aa2bafdd0db24100bad4621bdf6cb0dc958d1cf63ae115df24af9e2e484593256747b8eb109f7163adf14e271d7679c739e7440fbe58bcd1fe15a9979595dc3d2bdd394b02bef93bf584b55cb334fa2a5a075a5810291c2b69ae663eaae9be30ae847dd61bd7648a3323975d5df5ba029c847352f9bdec89a69015a56d0c5da2ef19c5c8f6faa25d309ca6f9d5238db6910abe7647aeba82677e4aefde65b0e67f4055ae4d5f445d13abdb94897c15ca737a88b6c108f49986c90d2cab0f57dcfa20eac250aa255529fe5efb5f1420ea2d57118adf26f4de93b1e800ef9eba24b99ca437eac3645726bf9e13ed96035b60aae041463d1d8fc781c077fd3c9f28b0e5a4e66a7ef753adba96af6a36c568345905748e13e6a46a69b8441a7098204b07247c3546b917c02a5131fbbd359f8221a6aca19dcf495f9669ea3cd99723390f90bfc4dc37007cbb9e4c371f460be09289a4962021440dc79f1a31a2df59c4e18b2355782f284a34e258adb8039388687bc702044330433c4256561c06681ff80e478e1230d39dc4c35deea7cb81706c2a827a7d2aad5109f289aa631bcd94cfcffa3cfe9b595e28aff8781dbb830befcc602ee801c5605834fe5d742fd59120c19fc932709085b87d7117823df81e4bc37d8cbd0bf9977df71117f352f54c5246a44f72e4062377dc998c431e2b27d842e82342dded405695e241c370e38e665e5f78c263e05224bfa4211751fd8d38cf95d045b700520092c0652a30bd5f8ea1f415cb27b4c048281f5edd4b229af49c753e0c1a13f2082aac46b42679df8d342e3367d57c0ee836f9b017e32a023a0731251c79d273d3cd143dd27ab329ddca62db25200c378bc92e9ba4a77ef5cf3905cf56af857962c983cee98cf7baa707a2e68ce0499aaf7f2672dcc4b8e45e55cb54ba6adcc11a3d43ea45aa2be1c15568c7974313b655e7f7536be2482b6e44a663d501edc2936e1825713b39f8279b639cdc73e5dd6a0a321e474ee96caffc73285db5c5bedcb1d3798045ec81fe3cc162328aec60847e4127d9528683b88da31da9b44eb1b6e8249abd4a6a30008dfded9e6ea3b1d447b252bc534ee0b7fd1867765a6c7c61b57c3cb5c889428302a19f75c95088569629b23c1807b5ade54f24d28c19865fb68b7cc2a725c96b2858d7d9cfab6a3828738f363d10c4306eda16b0ac0319d8da34bf5e84e42f573b9a17ca5a0f4571c3ac7bc00164e01d380bd3ee7fda2a5758e6175ffcb056baec085777143445b7bb84f8132451422a400ec9d52b512e83cfe55d81af90bb7bc428a7bca1b230b2b814ae5d9e0f0fedb20d5f424bd11d713714a610644090b4640fdf5029632ab159739d9552a7d71528f67ee624edae33a2a2b9d7b2f3cfefe82162300a087082568f63f55c16b461fc823284a7a0f189062fc5afaf54da3fd9c86042d126a753568e1b1c6b102c90471e5417ec962e33fa596a21540da3a471f9d3c77df62f561cfff288d0715c50f98001442c541b79650bdc7178af29107519e975a8b371eaabf61238767919700345fec8c0e15af2435596c43713077c064271e1e08838ab440a425cc19090d07033ddb68f883a909ca3a0130def844f375750000b743a979edb4e8015e0d4f00e7f3607b19c54790e8e310babe6ca2b52ab984c68b95352838553f80b2c2c503694b213f4a1878f6a9da0a0e6eec76774f64d448280c7cf6d8b56a58eea27d627b34b63c391d6a13773c6a3ede157538d5f60c3e2640412d2d186ca77937252b2771196c0036103b6ae15e49f2f9626dac76d8e34b5eb6e29f141d63fe8e770a294fd58a973327acc3974eb724d6a3742bec7049652044395b8d9d4b7171457a9e38925bd7011bac4763fa77ec22c08c6a8b8a1b7942cfe7efe4bad8f49a376db558eda67558644ab331f18d64ca9b8918e0b70824174bd84db01a4930b4dec43f46afc35fde0bed6146941701740f532479210d6617dc621ec4f65516a88db800cb9dfcc8a11661610ad7d809489e673a3012ad3ec20f1d3ba39a43b2bbbed2bca87050b6e69e4afde1136632dd09a59144cd5e853d86daa04688a680c1d563838005e56534d32df9a5a51082ac8bed236d190b255d41c9ec8e0ee0161fff16101039ffd8724287195db38f2f00e6d9556e15ccd833b9993776b6d39902b88d459884a54e329db5764035c8f9010ae2daa0f112d95486d4bb9f1a068b90fb797e5393deb3a403a53b16866c38f231ce17012cb887220900fafdca27820610c8f3d4120b02385c5ff4d3d074ee76530d8cd10c36efc328a9b195536527df6ee468bbf520d78e5be0578dd26b73ad97836f2be2261b19b984d73c40f7f3bd08329132c5c86f4187c386c17b3ee56e471cb767d984578bb8a8ecc39404ff37f5c8d30a45a3d82c760a603724875fca5efc8ae8c3150ab10935398c16b0061a41d0f24c0b75f2f262ac992977f793f247edd25ff62226bb1b704b045c92f419e30dfa165ac22911c7c195f900dacde0f088d518a4ea6af5bf4008ccd2051e0b3ccc483446dd62cfb863e5bd84088753e09c592756f6992e16d26b68697a36c111cda07165a79611324051bd1c787842dabbd38537ab6fae0f5eab62133edce6fbf6021a6186431685f50c40879ab419ca7b645eb25d1462c4e706b0b37b841980cc806003faf7e5362fc12ea3a641b8948f979c0d37ab50d782cd9383d5dc62e6dc6bd6ba0f96b83b98fa783a3c8efdbd64672fc5f1d3cd89a36037f7ac027a80ef9cd25a6da32f5af95f16f5091704ce6f61cae9a18f4ce962bd5dbfe2c7a978130b5f43efff8335b8c9e35701841fa8387ae006150f98a72668b989e59a4f9cf2315a4f14798dce73f8459c7172cf5e26c6675010efdcab22a1894309ffe4f8baaa41dfc5e5b168c9d954b0059c2687551a8d652ed08151544e75d959cea7cbe40b067891d5cef74380b5218b564b4dbb14730b4a41bb340c4d3c8d51ac2aca63e6bb7486254b58b2c464383b1cf0c9e60f854cbdf9939c01c29363882d22c40a983ab3908320d8aaa965afeb81e7ddf6f31ae8e333d6a79bfd123e9a2731394fb949fd8dc6d4604ccd07917cec729180f2f150399cf8c164e4d903011c24a084467160e3566b650b6d63409c9f7586f54e1efda3348c80934eca6409e03e130a00afefbcbaa73cbd5aa102424d1ed06f4510d42b7f58e65ef5aa227b0ad5e3c6b81b3b0c8c40e941f4b2bca55e3fa4952c4f3d30120375bf8c8bc4653bdcccb5f7f1b9e1422e536a4670b587fcd871fdf5ba041ef64d402f5a535f17b12aee96cfaa2d5ad9816baf21542a6a860e23d300a5e5d89acb249d12c13b7fb0fa7a29b9938603e1b40a01f7a705894607f49d8fca43a8f3a93c83d5e65993072658d4836054a5d44c38a701ce1e26108a55a2aa53c8a49ae7a34b0c13c967fa8bc2aed683aa504d8db29beca2102e07672451876e0c9c1dc1dd77b82b6183b215ade1d416ae3561513cc8c619ff2c8fca514ebbd5ed27bb4ac94c22990ddce20d325a2d01ba20b4b6b3223dcfed05dcef785dc20c2e32ade294a420c07631ae8d73fd1b7bc6ce8ac2f7734b80368c22451e6681cd17bb62cefa35e763065ab4748d97e47b3b248e3562d369d3122d49ea720874e2f2729868cb72e8dd85b9ff3a2a5471034c54198943ad46cffd00d1ac4ee9b2d23d79273bd94651db42ea51a6f7a27f39dba7db2f76bbac0518620436c43f13d5e1a49974396d44561a1ad957fe99b1074b3188fa22843225e59ed9513f9e1ad6891a131c436b91124de42e6e16c54813206080e4d4b2cc9dcb513e5cb6098202048800804f0c31c3390308739c354ab6a20733af2f85d11fd7220ef2946e2f4072a901ac0e415c45bdc6b2bbafa624b61a8e38f570be7b4556d18b57881002f9c34d56e050a2e7f614415c3433170f25ec8b1852536d76c23ac4fb797b185bef0f53acda303913d24d008f8fc02b77d540f60c8283e81da384ba2e9528b22df5cf8d316084f8169f9762e9ed49a0c811bf355da6ffebaf7ff1ab74dfc64f34088b513f84ca3aaa9bfc9758b4cc9975f1fe0000fa5a1411ff29f6e5cf650e362afcbb71e1e8ab8fb40b204b2f24bed509be4f18a0226f348afdde53bccf742a43bfea02f03a527dfbf3c4014615774ce813e3e5afdcdc3ac252881fe983202bd7890d76fb8f2158623b80d3ccb944ad39edf17617e930c378ccc98ccac750191d1adc6633b7e172065e9cebf663579301bee0254cbcef1a46bc0080f172c60c5a8f93521575a8b48dcacaa828538f9358534b897b1a15a396ecec657ea7aac05792e1828f3e2e295847d10820e6a26ca0f24a426c418bb244172ce6ecc58f35c75829df734393d004f381fcc8a36ad0f4af68da11b86d847bf736df68e0bb92768b06e6ab9c677b17edf564ca093786470e175e0248d14e8ba57dd10c3c53a3bbdb9f56a1d613441e939f4fdc54a618b54558eb335e4293fff188d94892d32403cf67edd31d4186adf29d1306f12d8cc1bf616b285e4d63bd40474656983520395b22b42333490fa406b366f9a6b9bc5e83582584f570498df3b833cf558f19b5b85995c0f29a832c27ad7c29f1907f2b065408e3e82337b68e83642d285e10843d28666381810fc14e6341138966e202fe688818f896fd3d0b932e6573a3b29502d9b45ad17e7080058325421c3da8720c9443052fdfe0c1d0bea4016bd7e30121840dcfa3adf16606834ebc05cfd23fe653c739b649d9473436d407c0903c428952f48346bff55422489b71709653c647b3601709b89200936155a54d58edd3dc95d1dcf21625c5560328a22127d3042bd16ea9fcd6faf30293df8d4675143337394fd5d9c1c22170f5590c48999d752bbc404353ae4d93881672f0a1659e253bea82e02bb100910c753ae106b4017e8e96952cd92470ad8a7e3564f4436be07e698125b27e64cb4ce11a63fd79140a026d50e817e13d2f25791b8c748b685511dfb86e57fa46b93a38d51817612724f14e6c7fbbbda411d13e7a70d93a854b82406b09c174008712eca17b8e1c19e7077115099f3576b380c77b6a9c4c9d9bb2a6c1a4577f42a8cb10c29bfc6a181949f4fcaffd95f91d50ec74c18ad60856dab4e80d3a349119a1d34d0476d34f76feffd34a485dac9ea5f29e53a092b9e1fdbfb46028e0b7f3b1bb95d9b24b1f4c65ffdea0d7b0612d0c93f22ea66a0301c1838638d4237d66a50624a814f110347fc81c092c9a5eadde0834154660d4d53da50e732b444626839dfce3bf7036f18c9bc0b025b9dbd756b0f692630b6c1aa519ab1ddd30e660ca8fea27ccd191851c05158365d738aed2543e8ee8e0e8dd4a88e7aad864092c6374ad817aaf442a2489072cc25c9d4964c8643097a2a6d44daea4a30441e061a891b320c1c21fdad4a3073f07a514bd02d56f3105a99a254479655c92da4f240d664ca0b9b6eb39988fcc18d530ae567301014699817074a4bff6dc929ad4a6b47e1744a14924217819744f58b3270e9933c79080eb02cac44b7a8f71320283dac8363629daec116dd2d5b889a411fd6d06c18bf97afb0bb04215495521edf96090cbd7ea15e6b0c8574849dff3e2daee544b07b4fe4815241a0d15722db7600128feea15b26894422c4448fb713245693e53d8955a8159602941016d43c854af5a69aba65777c51f5904276813cdacb7ec05bb74e9b345ca4746eba5202bc128d448020cae1f8be95662f371a1ad09f570464b111fed151d13c50420b1e3b3bea2885ce3ef316703a95df458328b24807b6086fe9fda4527b29e0ecb6e8a3e46375f128cd5e77f2a783dacc4aa72574c2029ce7bb91e7ca9cadeb1e445849ac205f6406bd56edcf830304ff8a04578949b4eb5c8459a101bb12ffc3bcb9e4e384310550d44114b9f79af5c857626a45242492658e8a01660269844c068d95e16b457c0067b160db410f0a7b9d5a606d0ce8897fb77270169d00ecd64258bab990467fb4adc816437e5c9b835f3d49e415e9255ac8da8a3230039f2ceb1f4c21fb1eafb2a77a5953b86a55af525b3f6a9922afb4692588392df83e56db2f0a0659910300c946614085b8bcdad58e7c744da021a611ac9ffc72d4f37e9e829f4927da3451d7864a7add22961a2ed196f352f5223696dde87037c7426dda337ca3266249f9a90e8374524e752710ac62d9bc7a67c02136195e51a60b45d2abb4789ae8814528c1fe43b61a905e56e4a936ae6b2068bc8ca688dcd78058b004892a16ce2caf26707d2b0316660f18ff1ed501d2bc23b1c662aee8b230831dfcf0c88bf30a0837bc613c4746ec8d437b4eb7a73444a2be449158ea71d5479915adc6becd9c48f65c36cc19633aa42b38bdd9cadfd7b36c4688cb6b9acf58ef8ea87ea87ddc39be2a7abef99b22a89efa8cf43cf81accdc013aec511a393d9da71bc67cb9a1a26eecc34db22c346d5bba9a058473438f7f5445ebecfc880d7c8832316863d1c44bc23ac68ccb613d0344d133c7b0d7dbef73392025443a62a447934ebe1b05fab45fe0bd23bbaaddfd839076eee7209ff3dfb574654dd978d7e979c60e2cf625def8548293f6c7fc8d5e9b521f178716c2c602dc479695dc2b879eb75759431fced752564e62064c38fb78fe5e1865f1790353765b1e6e9923535efeb73ff7e0d67ea0aaa99095463050e729c85790f547db80688fa07dd45d138f86b8988469a3ccc1bdbbf871af870c8bd2e6ef7489b2559ca1c09659faefb09c2741f1632720b53f3fcd8e928e21d89bba6749c3f5b96639b7426597b46cf4bbdff976b5ccedab9166a990abb8e9e3cff978211d000ec3ca8c01381ca1a6e4b465c8d9d9e09845cb487fc6791dc2df60dcc19c979620c7624c2b8a87502760b6e0ca6666b3546da5b69aec95c666b0898899f7e78712ba377694c759e1c148b1c66ea9d91ad24523ea244ac86060ff9984b88cf51630ac5b4697159404ac7c98e70fd48ca5afee6fcd68adae7362c5c60a3770974b43c3f3dd6131b614190a7163606f0d115d3df9efad29ae4b96de49a4c47fe5032d784f882ce00df3034715f5bd76076782f1fc7e8ab78e5e984a9bb2c0e013d27be0646a533ae9cf009675f5bbe113a3eb19d62e42c2ffdb6f5463a301045a7b1b9f8f359c75f3d7b1928391b3cc7a0c02e7a61548969eed647bcd6b450d11dad377438241ebf0480cf29012d072c0d29079a5d49dde14383e83ddc98af7cb79c3ff815f3202708596a442e31171ab748e08b8c0056bcc5d6c9609f53a1756b72d49f78945c39d28c57b975e406bd9a46536c8b139c01e7646abb2a2b5c09bf139638e58e5d800c23d221c1ded8b0ccd91988e485cc62c2df855be30c5671627c79526b079d5fd7a7f019c58fa68463f7411ea3100a10f1d2391aac2fccdae9a3d11ed29e96900f20c2ee90d185cf46cd4835f5ac793cd0da50f1eb84802be652b8f14649e76dcdabb0ea1b021071682ad7f3d0d8605b1e7610e8750e04dc5456d0e7c3a0f4e640a500ebd05582e3418dacfd9c932218162831d78903471b9154ecdf778ce6761fd0238818417ec1aa8c0fbb08663ed1d5abe819a7c71d7a7a0fcc3f29aa8cd1a341774848b36fad296e47747b75fc4f2390105b293d63660dcac5b762c304c05ac57a8434255e2e20fa1265b064b28de92a240cee92c1db7470bcca800501a0cf02e9d568b0354c6fc331a121de99f3bf33a218c8cb0953124041e101d4de1614e0c47c943622a5d35dfa3d192a9cb8543da7af17649f52d338fc46fe183b4762e8f947dc421fa07bdba3c52cdcfcc85fb18cd499a28f4890e0789e03a146e0a001bbad612bdc23e78c6587dccf88c6fa1763915c286b40a6f06d0df61df804f195e7ab77ceaecc1ce8b851612e3adc016e86b190a60e65b461ad87ab931d191e6c76331be9a7464a7442e86b619a4afa76e3b5da5d086ca4195794e53d8897c6b54b8650482d145fde4fec9ab3a111eff157d7704a3eb9146d7aea5b92458c5de62f3800e6a52ca53aca9a5b3730134f506686480fffdcc68720f951ce73a7d4d146b30cc4d8210492aad47e22fc23b666e2401c8eb604a518e27677348e44873e6b0d3d64c97a5b6af71f6b63946bacba0a47fdac1aeb1cd40d8ecc64a26f72491e08811bc10acf9ba4b99e0dbf9b71fad236c3029a8acdf1192d4c9700d934b06f9bf9635036fcfc3f2bad521aae60f05e15fc71e64c550eb45804b3c37f91a449a78fe756c5b499fb1c264a5d43870df25830834425201442708a623e833a534b065ff69fa12cbe854e642ca30896e2871779cc504f01933b412b3f0cbce75773c17405e337a4860775ca0aadaca50c4e844b1e94fa6e80ba3b4f5050d562812b3e9d6847c854329e36828c63527a93d509668cfd407967ec3d01e568c9a2c91812ff1436de9aec322f2038940d7e0d2f74084804c6cd97d2993dbd0f380bd81add940830a5b5c60c50e00ad2aa6b6a49c3de98cb5cab67a460b47820a9d0c5df2adade3171d35aa2154548c90476701ef1949ae613a86ae161e11938674022606d6ce4d428baba8b770790da7da01c131d2104c2cde1053369715ad05f1e872331577b6d41e293ec557917ea1d6271a3c2df8cba83a003d7eb177bcee08203ce0df205ad037db808d809ac9557ff6ff30128545b1134226a9c542aba2dc32fb0a1e7aa894b9d99d41443a51821009b31d1cceabeed4ddc9298e14046b7b22e6154e514b5a1f6d3b385761cef855f762802fdc420aa14d79723b15ba4ad1bd12592b5f5310c440c52072c570deeedf588c46b448a6cdb203e2ff2462a2fa4578fbfac692e4024bb3e3d822500f886ad14540c3bd9b42ab9cf53a376306890e6bdaeeb04184007861535f42d0e33ff21efb934dd31a70aa915e7364074ea591c3249b3cf3737129d64cb40e5384ffb39aa5d554e7e1a533d2db914f937a823d22ac175b53ca4cc0a448fd45f6841487fc192eaa4d34929a72c1f51af1f463a2da50c9c14f9fe93c4b3253a165c538438d0484feebccf3b23563113a13b4dcd0e182a7efb050b2009d0ae44fdc650a709f02d248923a4804294f305e40623fab0fdb684465db30f91accd113389c020f60f491f5408c31719ec2c0c785bfd2adaae37eb7a3cfd68a26de1022ffb530e0a20017fc3caa929fe99642f2d92d53a95145a1d66723c7b00fe1b06f5cffa7bb79533e8ea7d538377bc2340c091ed5dd3275c68e0264b15d6c2c789de8bb190a43c37cf14356ee790c5f66937ae86be6a5e0efe01115709f1db4c07ef925059153ea8cc72b8b8138cbd0a746346e2b8f12a91991c3002e2ed009856e0c573842fc5be614d9dff867576d0faeab659cffbeedc20e167be3eece66a6218dcc9df349d080011d741449cdc7e0c6da923126e9735f0dc847c6bb9960820a0462b822109e3a352e1846e07bccc3634cba20b282a56910ef2236b8009ff1a1d6382bd941ca4baebf6699888b5641d9ee7ff87b330faadb7fbd292bc8258491d116c4c6e3f0b9c842ac2483937ff37c9e1d808e1f01e6e71583fa9230031ce4f5b3a8d5b1f3934242612d8445d52adb49afadf545497dee69c21fba32d4d4a6bc9274ab6ce55be2b594606b200f081993e41fc13c680954506070b7dd0a50833e4569e4fb5e5f5d8dbc22c41b6bb688e3ef46be5f14064a0bee1e34aa1d3e98f67700ba9bd66263da3fe5d246829e64b048b35f490b602c68afd48c2dcd828bd8cef9a9c2c7d20e20ff6c270f6f21020c5845567b0866563657713cdfe8ef6158d10405e8f303f86b194c9a127fbd574eb16cb8cf5eec679c6053cf366f22676b42513a4ac510690336d1fb7130e24e1d01c6b16c467f576032a87656343535824ea912672984854661c3fa5e7a130e9dc39f2fe5791b1e91e2742d21c22d1df0d28f76faa7d21e857891c3df9b8339bef021a0197e313922127ae92cdd451e5a037cb265da2643a003ebd3d61f7fd12281277dbfd4d8029efc37009dce32a6566e1ace22475e33f973d7388ebe75389034a8109732be61079a689884f34cc224f6c2b4e4581e6d7b16135e347b5090fd54ce15643ecd45d9f92e3590ca3c1c8c0ca3319968494490920d9e9f459900108264ea45c349852acaaf345bd4a73a844af538e35fe65a07fabd4a34c5e40af08c8b4fa4d4198b809fc09de3a79c8627a5b0feffe21dc4a855c693094d4487475052aa51b2ba32154082ef90ce77f2026e06f059dfaf8eecc561bb99a30667da381a3c99337b7ccf4b94c58ebed1ab66237d5e7765eceb4301512cb4b1ffda6e2811b449d926d8de87ab282ebd0bc20aa411407548e19db4b589dcd8bcb964cd554180f2f149fd0efcd99458b7c25591d4518c3e5acbe546696f5deb92a6be89f14519db8ab7742ba1a7ad9913955a691f8682dc6a0148c63e2218db73817eb1358fdc1829deb9c6c68928a06d9b4e3507a3e84cc527209676e1ddc80287ebb757ac13b71bdde2ef18a8f37f53054f5ae06db7daed5d789bd3247bdffb768d6c66fba8f90fb446807040f5c65f11110b8063d4c469f5161cc55303609ae003cb82440b9fe7bd1df69dc3f67b3ff87bbe0fac408b02ba26b43a0a8e5272dd087b2ccee4d88aa0c84ccc3ce79239ec77154868748d55e010580ac58b37b5df5b83bdfde77bf594ae4c3c16d0fa3feebb14d5467a2b6449ab23244d0690d58373439068e82168a863ddd01284ba8c0fd6aa0897660ee4e0e39ec81b81a0e711d85b45004aca8f82b09fe09fc277792048d590800ec872a686818369aa2b07ba89122342a17554a918a7c0f280da37ae79e44fa69dc136f418a6943f4b0f689c39756254d2061cb49b2d3dce528f571adb5b481844df533b641eb9f60840518898e45b8c719b03fa114cab3a94bf0ce7bfc421d67ba34b71ac494b65ddc663886e902fdaec42d0e53df239d68089d9372eaa22656f3ef6547332e1fea7077371f73dd4116c81997a96377694ea0deda1af02b07ebdbbff8b56cf19a0424396965cab75004fbdd5b369e6ec8358a4dc1f9a2873d69b9091e3d22f280972db80ff72ebb5ff4d08fc71f6adf58ad3aae47d5292ce2bb108f467ae21c2e0bafc51c050e5aaabfff2dc78f0e2f689ca7c00e8509a62b721aa6f717a251f084141ffef4015d07a1a93375c16f1cd5ce42aefc63e1adac52138b45f9ac74860f22694442142404acc89beada5b74c32dcc707c685eeb5668f37a5deef9aa9a2c61a27d0a724e7eefb7787f289acb96276035d387b7fcecdf85343227a3b0618809b7a2998f9937528f631a860c97fab64a136c0fde95aa0ad210a3c7e9f018c49d89112e75b8e0bf91cc909e1360bb9822a6dab8b121414961ca5d59b81903381d8ab47911db1e888391c88eec7e54f426bf6a59506894d6f9fae3b9aba13e17b636e1cab0483b92791d53f32db255cd40166056c70edcb22f11a0491e0a40e04891d12f2f9b27e16a8e482232f8a463399c8e8b17a17b4e1067d0e69fe225014deccf242179fdfe786e4a124a84eaea3830eb78918471bdf8cd32af3980982bcea53d40b7fd8a11fd6a32d36b96bd012bd17cfcfe576cefc863104d258198fc659839d45e834e233c15d6ac54044331d61a501c33d9f620c8887c19780c250e12cb57f146d24a636fd5125345336f78dbe4994ff63d9c8756d324e6b0e98f19df21810d5791adfcc8c84167419cbe0037bd3056d6f6819f21499c7532b7be110edb737b271943509665ad4e819ea92a84484acb33b693ed46da2ff21b0280daaf8aac0002d31d87a2aec217c83d19459b8a8d82c331fef03096c62e4f6346af932e99a6385cf8d468a6a14aa8d365fef348b04834eeede23ebb7457924fdfbef8ade90e66c41a29425dfd1ca8e2351bf1b6ed5ffc32f1a0415f98a4b1acda1f76acfaa727759027b3a1965b849658ab4e783cb336c32aa45a0bb20b957ca3fc1481edf02459c1f2a6317cdce40d3fc519b944be655be771c677cd8ca8ebed521f8465cb7ec386b10b28fe738175cf22318810d494af17426f022a94da3cd9249495a8c27319f3b1847b21c2e8bc750bf24e1b0684d742e54a4921a3f9b7c86b23fb031ca3fde254b63422c06bf1e819e8a20a2835fd63035d4e9dd0ddc7f1b8ef79eead091cf7ffcabb7fb80b8203eef45efd950a87dd6e027003332d729e30706ac0ad766f76b109473e929427b3ffac9fd176947c54ae24b269fdada9901ab676c45c84650b7d28174ea6ac3a4bb04b489ab7fb08a820300c28a119dfefe6145f498f74cc7471224e52414707a032fd225d859b0cc842e4b6c3269ac9c6b131e81549c691492385412a4f37136a3ef44bebd047dd162e13252094dad8429efe5afc3fcb5e2fdc151e1c84ce064be88d4ac577a75c6be275a7084150b046cb7bce9f096f168f0b9a61415b19daf0c4738ae21a56a649bcd96211dace7304b8525840b76784f176b574c46cdd494d949175af79d2154982e75b5030d62ffad7d5f40aa8439409346949624f89f376103123cc99d50777ca259ca0865f3e386fb3f08d46d6df4a130788afd3e2c0be7cc81db0f98116d378e4ae1c7a1921fd354fb9868b13b728f7f127be417dd7ef55262a56e8d6fd627f5664a724797aeadfe522389d29c8d0e1aa6fb97d3464048b7ce119285179eef24a10f3eff942bec656316ae5584142d217035758b4dff188306c54a70f2fef397e7dc62d00cddff5c335a6db6bf79464e830c1c249faf2af73382fdcaf4cc074080c0ae9cf24f82c783ba66cd85428f413ad0a363c1ab7798fda654eba8fa11c0ea850ebd4e65873699a9f8841e23e833749fe0a641869ff3678f630e112707865b0a7e7647b2a8d4cf19bc46b48fc2b191942173ce4bd439923ea2586c11620f21d3beaabbde1a170bb84f3bb337cc0fd68c9e6a800be861dc80ff59127dd488466ab47cefc6869ec7b8e691e27d24fdc90c0c088f8d1369032772687a11bd7e2ca927d19b7fa8634ebf0a3d859f5daf51ab1569e6a53cc9f4636b1f2eac5e95c34f8b600afecd0ac965bc9c418ff525a77de4efc1eab6a230409b84e838425fd4728b29d054ee83095272382b45541b38459956d2e3d5ee518b508a526e21a5d8959ca44192e7c9e28d6aee9fa38b138cc5428504a88c2f68c21d771bf50026aa82f01744c4431ec086d82129a8dce0f3b2d98b58c5469cf8001e7a072bb26e18bfac22fad5082ea0c7b8017eb34ef4a92132223a153b7ef4acb2a41e2db109f2504f9dd8160686726a1f0d8b48bce9115413948b45312064037203b1adff5df2f392b1ab18d033dc075e6963983d69d28bee2e8fe50d991ec4e6a2b625367eb860121735e55645037ca01738495f13bab0f8e647e59d21347d8e3e6964f1fed9f700e7605127ce6b5a31268b0cb30d24e2169c3a5bfe7c8adcf968b984ef4913f45740c7ce0b94756e1916cd108d79af3e4372d93520c531c4cd5d001bc32b8c1be12fab885e368a4b61c3511521ea958ce82f30adebf3b650f1c6005521b4b065185e9c32044ee8b11750194045f6a4f2ba488962a3626929e7027448ebdceb4623e8ea5342c49626ee418823df0df1d1a257936845639b477c92edd6eff51fb8790f63a0ddc551fe8f5ba7bb469f22b84eef9e12800db4a85ea2086d098224bf6a09dd5da0524b264793c36420686c90fc15311588ef00c66c20fead0ac4004a08ed161f715c3401cada02740ea88ade4481cd9d5e6858953008c121b2ede53a54e3ac760a3c3b3ede7950fb7520bac9f6473878036490231a65bf16806a276125832fc62f6c41684fffabe672c8ac7a5698515a6da952ca4d622a15ccdba24a5732df3beb0fbcde9dd245218d0a1e0462860c071a744e6e2e76669aee36ac6b6d6d2cee2400ca6396a09e6b9b90996e00eae9d5a2f7f362cf947eb30a1c5211100cf4a89a381ed6d9f8a65ff16cf4f3b7b93ae9f64242fc7bc488e835d3119d15d17d2c12ba9936b803d00607ef204e693e74a4aa1f774658feb0121ea2687c912c99009cab44976cc34ce85230eb9a70d7251fb729d9b622c133df14e64f8ff830499a0ecdb55efdadb4fb21b788b70fc0b27ba3b79f95b993e70d0a119ce1f9fc6944969d682cf801da2ec43024e7dbfa749cb1dd5594555344873fcbdf874247a011f631fe1dadf83adbcc46f07cd39f2d0288559a000a70a3658696ff792c6064faefa4a7fbbdabf50698a6db8579ba004b2f9c8de0d0ae3e7963bc209eb1371044f755b45718e8cf776773e9dacdfb23c8848d1d6db4c3829ef12e4e8b09b974d347e3ea76241c1a67aea5237e05a0928b531215419969ee38104ec3f1eb560f629ec42c628503b6c626579573121f5440170db2d6475fbddde46131153f059545270448da7af1e4fcafd26c239d4ad492189f1ef67f58ddc17626338bb987984d98a6ab4fd9706fc330fa465c1fad2ebcccba11bea65f212770e32a41a25b289f817eea11760422e19b630660b106e906943e5d6a39915f2d123e20e367da8ae888822883f50c31150c9b682cd17894e5823114a8455e2869c18e65c84b805291027e0995ccf52864099c462adcd0f09eae9603d90d490ef087119527a739ff7ef13959356916a0e6f18186d9fefb68f3f25d2d0fb0eb53effc7a458327ca6fb6159d12a1f4b804a70bfa184aa187752e824393e53bdcf2ed0521eeae2abc58bbd8f641bab667af748cf523eac6b214abf233f76869f478e6b271a725e62c670c93075e3ced1fd6516526cd6806519b72e30a8b9983121477fa245011bf8ed01b2ba820992f8fdd04c90fc07901e123a0854f2f8bcf5e34fc43de088ee1e8e8e2b239e4865a6c655babdc694284415fbf1b2dd249a4189bb17dc1f4c7c9cf7c13f4a78b1558e8f6fbac0f04c6e2b11599787c79a59d94f14f18b11bac9a581e4a5a042a8d143ff003b74800d300b87aa6e37bf3e567f149df065ec2de024cfcd64eba736f9815dd3f68d7bc21a30e10b49fe480547672be8f3a06663343c104eef09841ff9b070e8f2f5fa1c62ad38051dcb44becc0c59f7e3eaeb71fb75745d4d1a35d9266b1d8d49cf6057208552d4e630ddf2bb6d5a5d1542b678461fdd8b7dda0dcb36a55491eece02301ae772ac4f330a4da5a915882a81cce6c3cf47e1add5775d9a173df39d21084ef316482018ae09bb9e5ef70c6cc3d6a5acba99e41414c39648a47667388bc22e0430ae2d85c551b8fb094c03d1eed5694b8679cb79756fd5e6de8ba76a9d6c007be80bee83774bcfb84372e81d2d40398c086f873fa20e9a1f9dec8602acbfbbc0bec907b8290cd7b17e899d86b8dee523b96edb0fd2c3b0466c44fc91bc1a05c1df5697773067aa8a6bb932ab7d5bf45560e3c218d63ebdc8913984a108ca580161cc0b44fbc15478dd757e11b0ee5a8f061e542e5d8a4916416ae2bd2217040f592201448455fc0491b858d88ccb3bd60595f1ff55802457fb066e89ce8c80bb477190f780ae8194291586940ee5694852e497900650b11f38bc86ef764a2ed1805b469c26829ac3c3404f76e18dfdcf9821a5a074729edf01e7a6a46fe328eccde5045c4786d8a29e808dffecc1d028ad0295c5b40d855a5030544a1ea892df81726aab931ca4aeb4dae98d955a7418c8886b293be89120f6f93044a95a826ba59d56bbebc7d223a77377f0c95b52ff1ca802077817b469860bdd0d84be707692442245065237b4bb9a59452ca9464a4056005dd05d90c7037bf1f07083c1baf47c6e4b93d57794e43ec05fd3838f7de1499137087e5278373fd191618be3fdc6a38d7ef3224c1a6976bbf43adbae4fef780805c087419c1484e667e501c99a2756e3f10f84111051299e607451444ba9af383228a27b28e18bba34ff489d127c6ee23445398428c4b84107d8c1c893edd4c8e18e96672c448379111264758468eb08c449f23d12706922559ac6e966449162b3611830b2a500107ce4b1489c2164896480921922532168958c8224b962ce918a3498cddd127fac4e81363f71122264c625c2284e863e448f4e96672c44837932346ba898c3039c23272846524fa1c893e31902cc96275b3244bb2587107d1470b26478862ec3e4214a58f7442f42122228a314bdc2b9c8343f9c1560ea804aadeb3c0bdafbdc01d7ece75b791fba010f8c30377f2fb2827adb156e85d27adf5b2178b1806bdb10b7bf662d9288e46d07bf41e963d0c7bda1b119d9c604febe0ea699db7f27b9494989202bd5394e4f73f412ec483f88ac8778886827e94fc34ab83f4aaa8fd2981110ebda0ad7b40d9867687304639e39cd07bb64318a39493464aa1378d724e4a6bbd6cb4167a5b5aafcbda7bb12c6619f4ceecc5b02c1b8d4e70c4187ae31e617dd218638d92a2125554a0b78a0af6484febf05b61314593097a9b5a4bc15a537912096bad94d5d031b9ff96b0d63a5cc363892c2cd09ba58eea9379b554473342ee8dffb21da2ce77b1bbdbbbdbbbfd2109ee8be952a4d07b4f2691420ebb096e8227bd882e7bb16c7482515254482ea515ecfd6531614350d5d3ad56f622225ba4894e2d364eada4855aa8a5a5a505e56242b1bcacbca44abd450a7b5fe0ee228a3bef630a5e07419537504ba181a28edfa00805c174c2b6141a945f0b812affce7a3a0854f977902127567e4a594fb39ad5828a81e2f2823d29d4421203eb2638e6eae2227a7f317936bebabec83eb4f2150cd65ee0d75cd0c257aba1983bd9c2d557407878b2226dc3debff6a1777a67088767d972a205acbd7001e30006884aad0355fea4aca757fed7058e089d6fd013828238ecfd555d10c041c90424d0dad1425681a1ec7f5fc06434c0b91c32deb7ea003712a0ca6c9881419b16c06b1a9d24cafe5d4a7efdfeb58d6cb5cabff3fc6c34a1dbe64195cd9d6c791ef578324cd070a60c47cc708b02eebf33b0f602cb604972275bcf57ef22ba22fb3b8f6723857a30b9f716599114822e3443e49cff4c7683fc19097f26c381dbb58573dd454483260b232bfae2d95c44dd884776a1dce1055c3713c9457411e5d65c7269605d8469b0476311863d15aa8a6445d046b6644bb63a9ce1d699724b862366d9c2a275ca7a4c456860701862e8180080c927d8c8d653f94b2b9e4d56045559511659d15c82fbbbac28fb5f0060efd97079714959c118d3832a54c194c545e5eebdf7eccf4f4beede7bcfdf7b0f4258bde6bdf7a65ca15c4c08e17befd99fe6a945c02ff7c79da8f223274da19306d56b51b08b45099471078bf2cb70debf38ca308430068de2ca34c2e84f7ef307b85f669008c2884d08b13c21c417c5be9772a96056894ab24ad058b1d2ea15e5f7f6a71f0b07585428c3ef2a50a1fc7e25c379efefa3fc0e4208e5d04708219c50e07ef850497e0f63dc11756234613d84fbe3db1fd8d50ed3b49c502ded827241b5b88cde9379ef1b110b8e1001eb05473c1b0c6de0f63f04b7f708ec4fbff76eb827116594d1e76687fb62e4cff71ba8f2971aeaddf88d2d829ffb0df4e681ba21e6f731bfed3d99ee1805705f4c8761bb940d1d92404228a1ce112e743d468e9b11533117acf650597ee43e4cd22dd92fc45ef6b6ee0345f93d843f8180fbe18ea8f31e6a0d2184d1a313b1a1c7ae5a253aca1400c18e50620eaafc4dd069c0d192e146d352b464885513fc19dc10af8b566c8f9073e94819bbca496e630354f977b8c3b43a0e54ce11242846195ce534cef99fbec0dd0bcafe1d4df6b721016e43721c031c9f8c127ac751075b0ef5e363ef1fefbb5633de78bca7eaee6377cb61b4f5f080df070cfefa170606d4e37f598ec70f53a14afef61e0c2e8fb7ec0a2c3ff50fdac0e0f701b3f160baf1a1f278e3a1def858f99727bd7c897ecafb784f557af92668537af918daa4a45e5cb4ee21c9f4515af7a2c874d361fa9751be45d3404e363db6caefc99cb6ceb4f9637fc35934ec574a1a49f3a1b281c0fe00440e60d0821e1c0972b3c35322c40a3e925c71842b10b949d950b42e8795b1bfd877d763d763d006043d5e183da86249175f58e10a61dce0e7d1373e726e74e80ddeded6c5c7aedf70bd2be0faf830bb81668fdacbd87d779becfa134d9331c334a7018e8f42c07dffeb5ff313caf375e86c37d40d35d7df41b30e305fd5065764698355ee1bf9d86ba47cf3e7f6750799d3b5a600b9fd50e3015fc608ff65f81d1fc28dc7d65577a861dd54bc7f2af65ebf600cf78f64eaf0dc7a6cbc4fc01d0de4dadddd9f3f777f404db8a01dc2f06395118ae04a4ce600c7772abfb596ee3e76ff7aec6218766da413fba07189ff1fc3fdc3c2d2c8c0fffadbd7cf0ce7c279cec1878fbdc63e7c927c18e17bbbd9f7ec86ebfdaf7dcf706c77bea2f48ffe2f06c21e0d050c82e0c3db8f5f679be8aaf856b8c339de871fdff46ccc8fa755ee8d7e67ff6ea81260fbf5ef7b76837d78df339c9b809cab75b65bd462bd327c6f72ddced16baeeb1fc4aecc7068cc1c907383ddc65e06379273173b773f09f8ef0d2228df9befd3784dd7c09721dfae46be34e4fb9ec8f7bd86defb17529cebde1773371d4f08a6c08756ab094ee001caf6afe9b2dfa13cfbdd29b7fdf7f7dd70f7ee6ef951ce388573f6faa9dd6b9b5b83e19ca5703ea6f1c03ebb6abdb07f9906829c1bece763d8c6037ba9d5adab31624f1f935a926c2ddc72386737e09c95524af9b1bed422db8f1b51b66f25e09cedc1a3756e88c87745b6af95edcb2af0f5ee8e3de7e63f9cb36fffb61535d65863adaee39c7d7905be7ebe8ce2cc0c44fb57eb1a8c6c1f4a24ced99f6c5f6e2de49c7ddf3ac839fb970f1d64bbcf920adcd164fbaee3353b5e63df7f04c9f6dd07afa96fdf816c3ad93e86b5626cadfde7353176d3e140273ea8a20a2baef09d5cff5ef9a1e4777af7dd7017e2895cc3f570c3a8c0b18d70ae66381a7bba35128ae5b072277f470e2b5ff1338d47f6a37a030b796e3972b7e30965ffb206829c9b4c9b9f651b8feca136b70fac72672f3f9355c69ae15cdb1fc0b9ea19429821007ebf1049728cdc9902a6026afdda170f2c5bb8beeddbb1a3c7510bf19ab8f514ced5af7fbb09f8df649aadc4b9fad209987efcfad07e6b1988f531ad6b24b9fe05df6a0dc4b9fab009b8739d5c1f6e1dc4391e15e0faad047e8512fe05bfe3b2a40277ada495784d7ffd9ec26b1e500d8872fd6e798d7fd562abff62eaa6c377a48f967073c021dfff0447fcfe1bdbe18ba3acbd9cd3fd00163c085f8c73efafebba36281f4a58238d33c6186b226d080473957f9e588cdfddf0a3b6e3b630ac45d4e4226957a8d138cf7d41c0a7fb9c46862e47eee0efc891fbfda5c6437ec540a654870e30f77800043937393eeecb16b0c98f1addbada8560c5236705146447a7c70844b0baf19173f39c8b99913f46572208af7ef59741f869adb59aea57ee3977ebd55dd31040eb3c1afe9d0c110b432f251cb2fcdebfc7b66e478dfcfe3e8dfe80df5fd75f5ee0ae75b2bfd6e91fcef944bd0dc7fbd9bd7f5ad4e91f38409b1a50e55f2bc64f835d1f66385d5beb7c3957b50cc8bad5ae816b6838d0bf5a28a7a724d31906f07ef0a2784fa693c1b9b082f4214f00e11699e2f0834c3f8ad61186e21288503966bde1785b179fc8f4af84fda4cb39d258a480bb98e94ade00fdce37fcb264817b74ea0d7c19bcc6bf421b1cb87f93097bf924e31f68731a3a110165af94d229e79c92d229e79c92d229e79c92d229e79c92d229e79cd2e6f40e33475e36ec781042d9ff2a1142bd264c57072adf5d2097ef6e507edfdd9ffc3e08138ff31f2f82e93ba9647e275ba7efe410e93b4954fa4e16ad7c37572caf9b3b442e8b75a6948ec3f7994c39c618574a58cc52eb3949266a0a0a3eb994523a4f78a512f460f09f9ad5c2a1d66fc33d2ea89d582dae67983cc12aca8976dd400d621aeee11c9e596ed894c5ca54e1c41a46c2482412e93d089ce13c181d981f8ff44f7b92447a2b64a5c0b41210e74a3c303a403facec6fa540c20a794dea8820283b3c29251e3bc43362e11eaef2ce7d62b9735613962fade71444c809af82ea45611ec4094607a6872ead682cda48cbfebe60fff222ff25c391ff925dcf80eb86b49d5650f54390a9f5645a4a8482e469e7c26b3bd9a19190d7c8af90a80583daa86a3d88f493e06feacbcbcb4b0a06060606c6a22caa67e84e3f4e2b1fa80c798d7c53c571255fb012cf4b7cc970de5602924abd4f65386feba99fcaa4cac4a2c4d3303dc0e8047162a8c413698907060606e6354dd3b48bf37db9b41e179722e750afe6e48b0eaf6ccb39265e8db5c20ec1fc70d5ff3fad24e49ccfb7d92449e06ddb329cfe2d937483d1d1c1dd69755a411b2b04557e5a9d7cf1fe27455e93726275621965a5edb4d27aea9f7ec01d8c4e108c8ea9f560f0279d4c41d91f6e5d0a28fb5b2becd6a59eb02daf4939c1eae15c45c1272727b0158a98158245980b81bb128f4dc997d6d3f227ad0711f824f89b135eb9a45ad95f4549f647a584b23f2a49aa083cd99eb4530f9fe1a493fd4ba71d164dcb36b89d56ce3961b7eef493fdf423453bf1647f94ad3b05c94eb7ee2424fb9f6cdd8995af9b96edb4fa01777608f74f1a941bb6727f57e2c9fe264d83b9b41e953f69a99696124a053d197f169855f65f4911657f526a28bbca4b265248e48fd7a48220777277fa01cafe2ada6975c2b453d54e29da8907aafc51b413d64e27da8935b79492d312b8c3ab55f63adac9fd59e479f98c6e78e59c07813bbcca3319ddec100bd8bd5043e5cb61506bc1e69c325e6badbdb22c9332bed471ce57582da242910683da05430cf4ca00fbc9f45bed2fad676ea9542af502a303a36363ec504c4c8cd522468653e2b143259e120f5e711ce7820c193264bc80573373a68773fe32198e1d7a32fe2d2ac0dd69b5cafea71dafe9ec50f6558f93cee987d7d821ac9e5a502e2f2998d76c5495cd9bd00ea378e118e164054b4e84e878452120fc55441326987152a40824a61892151501e484073cca0fa808149c0051040f298868b222220ab9c3af041154e4eeff2b11475810230698a4840b30560411be418e20a2052920828724e07b051ef8ca0f680817b9fb1f62c50a50450c9162081196c0363fa0213fc0439830010b1843585e0c01e287f87002121386e88c4480b32f464ac0388b132398250911cea0c802ab20913511228447c80c8210b2f3427c3cf7311c9316141917b8f6e084072615d1040dc0e851c1c91078253fa021bec8dd3f3104167954049ce507344412720c376308217ccc1046861842ee9ee6240a8f1e3d7af4b8753b68381717171717d497505a972347cbc796d8125b62cb779f5bbee3728e7ca27534342eefe2e2e2e28242d5bf01515a876972d06028cc9a6d88441dfdb26a44ec4bcdbeddba9c7c61df61e4b875778688590d7e67f23db3cf3bbb1fb70edbba9a83b5f561f54b43d51c739d8fcd374df99d75a04097932f40f306608e9f935f56438c5fe5bb89da03b241517ed98b999b8eef1c875188bcd7b51c74d07ff97e17b71efc78db81c8f544b097d5ef626e1cc7511f763b9ed0c9dffb398c361d4e99e53bfffb7774471fffb28cba1745765fc14fe54f7a1fa5f74f71f82fbf3cf8272fbf9c54afd1f1a6676131fd15ca2d1f3b8c7cfa7baf632f83230de6e8397af6ebda46a32dc6adbb7ff22ee35b779fe56fc0abe5b0f289d63d211c1d461ef9e83ed4de93b95be78f7d74ec73a8df71209b361d4e79f4a79c437d0de8b86f7a1df75d832aecbd199d6f39d4efe2e3c73ecb11033a3744e6b433caebbaa63522caab8b2891108ab28b48a994510a45d9851233902cc96275b3244bb258b409eaa3858c4808456984a45446a1288da07506d58708f531c20823e40e9e236300aae0e30394676ebabbbb8afb7af078dc83f0bbbb5bdbc1e56efff66d87d92fc389cf216c28474ddc17d391564be02eebeeeb30c618a59452ce396706ff524a6906ffd6cb5e2c1b9d609414155269a545a665868c9728a64c9bda4be0f3cb4cea05b875770a4605f3dc6b2eb42054da62b0d048b2224a544df84d5c0de83e4d4489281125ea1625e248198e9bbbf564fcb323c4887955e4ae5b314a9e129bb3a227931921460b5bc46035b9686d8473578890468265efc583f5676922eb9f1f17b887b14571e0d6ad1953eb7adf4f78cfbfbb901b8e16f268e51c10d50b48480909590bc40b325e64cccce8ee9e81929953eb9d99191d9e223c33333424071e0d1a3468d0985e329cd83430c410c30c00babbbd0800e6ce9c29c85d4f0d0315987ea302c1c88a66a8c98a5e2b0b0372d1d2fc6786468d00a0d0c010001b988b1862b01f4386633f0058fff55dacc1bec3ee0cd1a18f7a7b4fa6d6fbfdd8c70ce7dabac93083006668e8a076a29f7836dd48b217b9401a5b08da740bf62694bd5b5af4900d1b36e2dbc870e2dbc8e0e3294832774d23dcba05b910000d987c42eefad1962dea3f34cc0c401221774d5b5249830110a001399cf387000e4a2660239340ab003337126043d08d3880030c20bb0194676eba5bce2dc1cdc87a5ec68d8ce1604972d72dda26ec3e9212112103838e56d9b3220318c000d300190efc1a5843bfc3ba055544e82b5a3871eb16916b8003d05413feae616f80a8af563c23c33173b78c9091f5608c83c371d4d035ccd8d0dd91880d333466482b186dc460525a6416913480de59dc17d35d56f6bf2d8526e4de4f0d57f9241737393403785930c7bdb148228918312cb4e08269c98d000f0d82386287c562b17a60f5c062cdeba3d73c0c934c2e1f139d8b4e8a19beaacb33c9e03431dc11b6e110f7c5da49f9f25b878cd02af15e440f3df46b17b0668473fea09f8cf41d6277370a5873e7fa94b89b833635b81a3c60870e230d21c0a43925c771d0e600ae721a1632dc6e542335589073ba9c7880b9196e43430d216a1c31279d42b307d008ad268c8004932e9450638a175031040f90149d200b209b21801cbe17fb41081d07bf0197df3f0821b43db8df9f7c33ca37eb0f9b9f9a00f7cb6a332bd68feb93e98736f6c66dbee18f83f764209c715514fc603fd57bb8751042282377efbda092dc74a152254d5a983b10c62b899a25c1fd9285dc4108e103caa4c0fd302646eede7b0f8b526ed8a4efff48ce759f5f0c945aee20847068d68a6384f24798ed8139495d14fc9282f3fe751042a8422acd95f79e8965e5d4633ab54c1ed4dc81aa262a72ce143994df8b0b4aea05f79389270f83450861041a1561f9c37b0f422f324904612b3ff903ee8f8f6d98fc21460c0b2db8a082ef49bce7e40f5285c91f649c31be805921ec8f0fc1fd72bef7264d8d19325c6646cf7460c2263219cefb07219c919921e30515e7420b2cc4c4d8b48749bdccd4d8221ae1dccb2f46efb901218470076c21904aa9168a5a61319da4bd7a10c13e7e47bad3c5bdc648e3d47a50bf9d7ab80ad52a97528a6aa19456128974ef55d17ee449efa57485d24be98ad57a56fed24a405c35698b44addcab85b6b4d0ab67e5add6835a59d1b08c6aa112d37a48a43fe99c20762f4ae7fc97ccaea450b4455e24885d3b6789524a27a594ce9bba52d774b22ca415146d9157cade3927a5294a3f95cd6b7f05455ba4dde62d514ae9a494d27953f5baa6534dd93badd653fa4bfba89528a574524ae9bca9eb9a4e29cbb2915650b4455e8da5ec9d97d57a5c1ed37a5e1ea5a15e4aa4540ab5c272c2ee957a39fdf0c162d745d216abf5901ed37a4a8f6a912713cbca15351e182891b4d34a3be938e78373f4de3badd643299d72ce29ad954da69c734a49e99cf40ab87fca2653ce39e5b5d7bdf6b25a0ffd4b7b4157c0dd69751294e7a8944a5d2a7a18aa1a190000001317002020100a08056369922689be0714800e7284444e4e369487847118474214444108c400088210438c42ca38c494222b00008c68f1479a83754fb87b6a119301b12527d4825e18df1d146112ef6e7e27a856bfa37d87f6120a94806ee05fe4e08db18cf88b324792f332e8d28f141673d641889a110489209eee8ea2a8b55c71b30afdcee0a56e0a5150353a0b25a37b3c34bd3b5cbc68686134c204b072a73824d38be3bbb98e846045b3d6de1157e689a8b75d05d0073ba093333edec29022267c5697e39ffc66fbedd44edc417d396f0de81dbb155a02c04df1d3fdc00e803ff27131125efa17467e7853d042da240ea4ce53038c6a30747ac4c3c567127469753ad7f1e6bca14986454bb03879c049c58df491c7565680643a1283e6287389b8056aa71d005475113982d76fec93f1c6fe1b5910ed948949686ea126c68e9042c9558360820d445966d2b488185ed5d9e5ff3870c6277bdc146684444b140753fab2e6ac80e2e321f22139cf320dfa738cf48380f3f75ad5c73bafaf38501c88148377454c4af62dd87d3e6b8082823a8fc03d3100c3a05d1f8f722ca52171cabbd321fd572a1d37df41ce3555227f220aa94a11ac8a419682c526a8a4c86b949133a4d37de1b691eb06319c464db9250a3a7b030078d89bb5f668a7297c346859e43ebcc84af22473230a13642ff98e75056589c92c308e449e9ca09bd9f8d2495cc6f698f56eb2c7506b1c8027101f2501e7bb26f0bda71daf025e88490b324381d80593cffaa029b3ef0e838b3cdfe3587b7357a6af3220057df560f002cd1b6fe89df9e20a0e0fc3e7a68e277bf729f4ca15401c19da45f4ecdb07e2431214f449188ddf81dddb7ffaac16c68b715a5e429fdafcc75673d10703e2d2ee7013f7cb0492c01371e7f59071acbaa6fc505a3e4d04ba4585d3b324b757c12f2ea0e09c94ea19cd9171a1a877332231344e69402e323101b9e4341404ea875f124d66333a0e9e0fbdc774baf63bf18e11ceec44b7824a04a71a2e8e4fc90a5791a5a8a7bb02f9b9227e4884cfa19b7b812c11667b7a559faf41f0f94e7e5b3064f539609416faba0b2e69c9cc3437f4bec66674184bd8aeea647618157cf99eca13131fc52e3ab5236da20c09348db277aa4508f8304491e69f415794a17a47d4b7f44ba46f8c5452cb308e964395f3f22dc3b15fb6114d7319dafb202a639429745809606fd5a323d13c140b85e512b8a1ac942209bf2a62707e66940d8fbab6245c56f239fbb738b810d6e4c6a4e6c6d922a078fb8621eec7c25ccfbeacfaa4be7e5c253234e5577364e30bdb410d955971be4698a81feb64f25944c2e068ad6cf06994a63497af1764c35217b554574d4449ca4f1bb0e4c24c05cae01f17ee12091732628065a612a5443996267f898ca475639222d0585a1c6e5e0e1b7394606267c7b1760ced621cfaacdee5a3f0a31ccb1e5f3f2b565fee47db09c3f23404729032a1e02242216dc22dd3de73f9e9973aac86c0f7c5a1133e1546be43c1bf302c8ec2f617746b85180f4fa059f9a00c246b6708a2408d5ab148fee230422fdabec101405fe8d1d98c98151cc505588c6163e2d5ff47210b45383869bf70d305a87cab3ed9f8dc98ffc3309d0759d840184cb49d0f3e3f0d837d43788575423ee06a6008eaa53be24a6f159c64b654e1a1ad0b13e78537ca3e7b302b83443bef0ec9bf666478be37a60a4240cd5fe5383409d4319c01f5215911d434828966a070bd92641fb21e356b113b6428465508e22240c381f2724995e6b735bc9b123ebd9f5445a0520c6f5ed11d5dfd0acbdb38dae9322ba47516ab6ab35011b637885e67081664433dc712c34b78082b868d7feba25b5be1c75c8097d59482086fa903bc8584a1bef009d8877fe25a5818fe65f2787e21df63184ef70ac7c0bb17106de4ec4e49a1851c3e98fa54553067bcb35afa131840f35ae1ffd84f9214793c52abbd4996aec80efbdfc050ab7ba37fe6b6e748127c2e0417bd1403e255a8c9b71710aa027e418522412f546678d6ad8ee5fd4367d709a61b6821208fa0c65a71d0ad9723ffe4b16df1e1081259e9bf88811efa671c3bda94b7a248ab062282b4e93896b051e26d98d11ac4dbe28c33712cdc31d927a45ece7695f92cf4501ea41be203d0f7a33ce2ad9702cfac44dc1f4e5690f574a610052f69ba3212923f270b5d3f093d6f4662dea0fd98c54d00a0ebd15859264c2636b6d50bae81ce7accde3556f5558123935288b749b1347918b90117c0654f10b4771886d386ddcb0277e5e1ba55e0de98bc13446051499329260b1b2553a7c67a936e9b5c855ed5e66c3c0b8c768848a792a372f8a1dc3a7874d035652dc6e68c4b2947337740daad9844403cb84cce932544a1d1b0c12f49fc946b1abe74710653c7888a850ac164c1a420441422768366925c4bd5e6522fb4e6219760ec0717f406f12e08c39102a586c7c3b9737ee8bc2270b90c9326a4ff7a63d6d40f8342ff0a59af122e0dd7995d4b0f1a08b9a4ff8333c934c240acf50950b2cd164dd5ae2239b47d325897329fbc24899b4639cc231fc71cb0708176a8c2b26b425a326f3194d0e6e3a157a80cd93d4a4eeb1034bf502221b9888198109df54084ad6f9b23dafaad1c4f064f1695da43ba86b6a5a73ae74dadf6bd4fee0928fde71bd64e6fc0f346871f110d68b874a3c8e699313889f54677a911c6da8b3f36f4ede1d98925e593702f3d7cfb22817e9150577e3cb0f2d847f2e288ff22d3fd99e7544aa3a198337b563a0bfb8e7d6187ea5fb433e34ac66c15d515556bef5fe4eed581bd512c31b324aac6335866f7a8b90182b5502be38da16b1142c39a3bbca8f5acb874c1f7b6c6715bb7a7de45247df5acb21b42d179ad141ff257cf72cae2a3829e7d238388bdeb12e4ebea4346d4efbd68745ab7708acfcd6c992217420623aa7c53e2b6bf43074f585d4989acf1f8b9393158104d1140e193a5dcdf2cc07679477af4ef81897bfe7f3034a9dd8ba4346e35daef10eee248727fb004d0d179f9117783114cc3972ad80647b069f9c081cb86a30077cf2d33cf15b07a66fe849b3a2d9804070b3476cab960f33802b10daa4804856bd20855083bb57cb3c9ac7408a991e710406bdddb8694a7bb85ef8673bab4f5a5b189c66ff5534a2b554ec65bfb921d57baff9f9ecf64196f125f63f38719260f84683d5aa7119a7c2d4c9ea134c8ff116dbcaf4eeb2e19291f1fa1b356c7608b4110ef385f8c82b44d643a5fc532196241692cc58601b84c494200629387b280de3e18d82ff1fc5f9912546af272932bb492521ceb1c43d405aa1f286c26fa8bb26173b179e9609f0836be21f1bb94ffe3e467b366d21cf535ed3bd7333035cbbe3e81e603f8a107e365231b6ab79fb51e1bd90c8038dcc68a31609219a6205470d4cae1a1e368885c13d3dc084d1ebf216b405c0bd930875b06c90922187f031588e783ceb331c2d908da95184e943eb720508fee951887cb00ad3d1040850d6078dfb3e9138f7b149712af6c1b23f5f1f9d635a50767b3af984e55ba5e32405823967e4a39bbbc04084b35672ee4c50be5b3693cccc49a3082feecef365df0dc73230e8465f002dd090dc270ed166f81730188eabb6436683a2e586b0595506bffafacb33d4b51bccbb8601fca1128d710e9dc32ee78b386d383cbd82797d84b8718f147a5d15307a79a5d825434e080202e884d3957d59da7fcebf0eea6e7abc50cb2bac656b3d544310cd819f2cd62ddb42c94981ca141fd16a856c966266b0dddbc27df8bafb1c860daf03453386b38d669c2e3cda0f84adb57f4149ced36f60b216f9db5dec3036f07d740ef7e9b19736a47834ab68b533e3effe30958ad72fb5063e3c49960c9ec9c4631c8927255301cc34716f422a61643d8511ccf2380454582a1ad9a49d75c14e0bba34a3d4bdc37137de631179a85ab7421c65b3169d5702997536f75be3813e148bdfb3c2c278f279d768ee4f1937ed56f693f939932aa41e310503afb6c2bece38efbbb8c3583ae54b36fa2e7b6be10df2f833f8b0e53e60a0541cca27734338ccfabe5342821f7b34a202821761c2c1e424e8a2110bf3bfa801c7dfe764ac82b1530b34b56bf5baf37dc9fe2faa355e33542d18d49bc11587c1d49d5541ee454723f851736637888e5b904842318f0882d9e6e94f2b85f35d877ce42541df98909579647ad3d604c72e2fc38f0e0c13d333793d40646c6c182f34755249f90443893a4fa1fa420aa249100cd81d71d5eceb313fcb1bfa35252d651964807b8205c92b9fdcb121658b3e7b5b30434348cea8fe019211ef46c0dd45184153d79c37cf4df28af1d9ee470f4da86351006b6f01bf923457b556090b8bb838c68aeef67c75af2d9f64667ee0dc78b11de9ee414ec6400dad8bc21e7c1dc31fb7a0b3737cc457c042208d8ebcedede945188c78699803d47c9deaf8dad39028710ff57a29c77112ded1c98fa17eeed044dc006411a80093e6db1b0009e3f0c2ec8ca9c5c68359d8b7b4fc8a9946dc201ba802511c157a12dbff494f9a085beb7fb5a9ec5855dda7000c312bd3f56e539239700ff97519c541925cc5c8cfa959eadb40282c9fbeb2425e1b858d244058224077823f00541577c5f89510cc6f406dd961def12b93a8bedb155dc0a477341dc4d0b9e949c10a77cec0302bddb8b6cafe7aedcf1724225a80534fcbaf90c50a27df054aee717ff4dfadc4894153fb1e4443d98c53fb0bd3e775fd012b482be838e88dae85a162299466f8436e345b752dea9ae02bb7373e8612844a0ccc6929b132461493ecbeaf4a69684de0ac6eb6d8188d699bfb2a18e2627d55028d2bf5b93f0ac14301648caba272ca3223b2591daee6091b6770bf4d89695dc46dfa53fa21a7ccd05c56bb703e2da5e2cab8c39b0c38d48728b80f3346a20a6d0d646e63d0688a64125395b4c6aee281d339eb9733e2fb906647e0af5465c0b78232b7bcd87d9426812516080d7ddd2617061932bdbb058f0b18caae742254277de07507080f7dd22a35f6c72655b1658a9b47dafa0c60f6dc3663ba34d6034b630597c9ce78dfd8e108a3ce14b2c0e0f35be2d5e1ba4cdec0f91d0b31a0e0414b33cbda0445f847ab00f7ed0c6be126352b13a65c8ce5f99bdcbeb59ce3e03206c12d1374fddeb73b4437bb87a2945f08688094487de44892ee67c5e12f41eebf17c3ce7e0d465cf137b469f7be1e3e6d953cc631d86e69851ecd8b6a5c8d17dfa82acf1e5fe435352c04e3238100c2de8011599b7841eda462e2461d8b8f04c2171a2a463644bc65b79d7def79edb5ab4ea66b96db2bcd3bc241a50189141684159ee1bf54909f736eb9fa9931c5cd841fb882b72b487a7af6d5b1f5d75e7eece511df457639e7daab627ab8144aa6357a0deb2664081c23ec62ed4006daa9e637377cfb995ca84ce9294f9e90a32f50cbf367929f5562dc136594990dbb916e3a6c5bb3c51c0e55ccbb5ab0c5c419e8efa993fb6402ed983778d377ca9696cf8508fca19e961445b7e6a149fd53ae350a09b847e7ce034fcb6a0bbe18315be079a7452a9fba2ae3f2b43c04b91331e4689d3fd12b3b4b5d6ceff19bb196b451fa9e44d307776af08a9f8f8913917f37f8ef12e4c6158e0794b1acaf7830c1fdaf5421c224f094b7b2601e6491308c80156e07bf8b84475d7eb8d2a0931f704ab38cdbde2c01170646c447d726883377be919d0020b320110b5066e40552a69406e2790754b00f9409eaa2eb48690d58431629743b897e63a4a86e6574b47511425a979f6e7ef7e6e96f5d00056f59a51f0ecec87e8e9183c462b7012b607710f573863fe55dcb80d33f44bafe6263fc5e1f7bd444e4febbf6b369875583761502997424ae76c5f39aa6a4ef6a633510c7a6c375e43593680d706e3d5ba89357064ce50651b5ee8e7c1fa4fe35ee4be80c61a049cd7b1b2f419a0c1725606d994eea6f8178dd41efa9afdc1f320bdb3820220482c1fa692da6507690669a126804c5ea478e26f721d863ee26e1d22bf9da9e40ea9252b5c527400fdb7e229a216169b0bb7b5fe7bb4a8ffcaa5830f6a8b4808437358689cbbebc28a2d0f868be9613e810b893d70d5b30456223c450e1d4e93eee18949f7bd6dfe8dd99689e9523997b4179c939ce0e7655fa009e272354bba3ae950824e3f482e9767baaea2eda0232303d94a38b95db1ef5b3fa66bc6521369215037e55e304f621bf75b1109eecad58f0681fa458569bdcad963d8be11fae95deae248f342795daa2b9b8f83e291264f01623fd33c5e7e9d77604eb72d1709f871b67b7b2264cd39f7ff2d3dadad4713732add49c5b6f4bb1d867d819265d678d84572792b243652ebb122ebad56e38721fd1e7f594cedcdc1e434f793129fb1061e6d24ea06746dc4d9e3c388d5a1024014d62c9b657374b8f804ed7a476bfcf333fb5460650474cddcb23908469920302f7d655afa0ca3aba6828655e4e0c21085f396913c5d350e67c6ec809a35a5efcac6d4ca42d6abe136b89336d4cad10f7ac5a18881ca070db01a3ab52466b638d97c4fb8dafc40a222e2cf683a121f7628acb6f2bbac324e999572398c6ae90170bed940146b0ebef3bbd5d9a8e93f67b7b06fe89aea823d1c5911df0353e5094bf7d9a2a9d70effb373fe49aa7f9edb88677e70acbd4983e7be79c6e90691ab2797749076029c998ae03c412ee55263f40be92d40db3e8e4caf2637bbe67748f2b7e97f761b6c288c7228c9a8a09318b7384d8f25e00384a01efa45f65380ea004bc53e48f83d7b971fe4ed57f38ec525b22b1b7f4358537a9804f8e236d17e5b281e6f400cca2541288b015d066904ec5c52fe8f4c6733f562df16abfcd45bb7145851f91f579841b05dc7f77ce6c288711e6d9cddf9124f622eb6fba8681df0ab6c9734097b9e96bd358af6907ea3f153c2ab22ed1ae53ae8746f861a22fea0aba0c99de40f65fe5c98ba41f791dfb6e09c9565372683cf6857a1874841acc93100d1e71a534e623dbf66bb6defc92d93986cdce0f1a221f603a333821283ddfa3de4887a3c297670b7c1a8c23e0f86dddf715cfccc39e3bfd7e51d15139fa98ae7dd4b4180611c5530f2774a2ab8fee4485f7fd0c5e058ae0a506caa4b4dcdafb58dd58f2e76361620015375b5e22ac6612b18e45f0598ca09a3f3196414c6d07141bd41d0ce30e2e97f0069e95cd27f3c1e714a77632062504583415eda3ff452e9163d53f495b9d8d428b6e0f1e6c0967f165c8835ae1d8b173a2698858de084266e600c5251cf594b03e30570d64573cd54e51093483b6badf3302b892afa7abf87a9133941b45166f69244694804695524d18cab7e7c42a0aa386604f664be8b012bc5c2869498c2a1fd45e214d1a768ee0a893ee561603890a24375164213fa97967c5219b6f995c3bb80c4e703e58f781500f6110d02388661af058b8bd7721ef88be0e87a9e3a04311d31eff8ada4ce7b4f7a2ac711c76a3c4e2ebcf7264bb1ad5996f71a1c31fc0be902626ac9d4e8738fa944e370ad4741b00c0563d2ffa307b74292eb4df258f7a690fc837fdcfafc1c8431b76c17991b782eb38eaecd4fd787ba2dee765697b8df6f4fad3584a42b0dea5dd734f5b7e1141ff2d9a9f06d8938bb45df04910b97c654b0223301d9240b699b2b27ed3f78511aff43754efdf77569362d20dc61b3e67013c40ddf60e36c5acde68260629500013a0bc2df95cabcaa2036e26cfe2cce65eb86e891eec792f716e764971460c6c4c9ad326f6b8f31e44901797f20599dad551b237ed4c1c0d151a4dbeb254cef1da266df9be4653721a7d449090ace19ff521e19fad6becf93f81da0c998fce844a135b31580b60cb545ba3c023266bc1a07bc96f31527cf3ed69e93d241ca2fc593243faa672fd7bdbd9a25e6f06e08b2b3b2a683410ede3fa71fe70e019106ae8e20148835256f4763c61b687c441bcfb8b681c5a3f0008a6d6c0fe53ba53c0dd7ce73289b1314c7c3347c2766a52db8dd1aad643f05767d5aafbb116c5a3683dae2932e057cc23cac0045071b5f3db3777c0f0896c48186e8ab91add397e9a3c63f38d3c3efcaffc12826cefcf2743b817f938968a4058182efbabb8026f1965405499507bae8c1bac8c9fd033ec565d213f30a43f0fd18bdca1c5e6eeffdc817d51e9bbdff133bed9d619e5ffaf1b84ad11eb466e640f6b59b3a566287ef8c554e60fbf140d5d2e45a6ede902da73728496cd5f33c5ad1136144c5ec3f2a7475b93af53227f8a984b380012e30cf90949b203d48b534d7a9bcae7427d0c6fe49a3f3df576eecceada3dafc95c16ece3d65d085285bf2f39621e7c0753facb1171c1fc083caf7c12ed5104d8c296c20eb8c1266519e3cf17937a077ca421def207bab36316f833359a61f190cac9b0af439437f80763996363a6fdaf01abbc80eb09937e51dc608d7144c0a5d8538079a5374d447ea2f296cf9a911068e4a94cbe21f6f0a489a6905067b85926ea99029c385264ae4aa7ed210344426548bffec59666c616042822820ef53d81aa900f1d3c5c1930876f7d7bd6fb500f3c7271d19cf4b30c0250a08dd3c4b9519733d606c40b7d525c006148bb04682a1fe25c8a2e06b965a61979bd5a010c3aa3c4dc0242b26ddb39d354dfd57cc6aceb8296b0f6b7641200200a168a3e409f85f2435e058d3a50e82ed07e0b41cff7529237bd3d72ac60fdb9e485bd405e0fa9086e210e9b1eb083210c8e76f0a8dae8ea8e879815847a2a6e0e78b06e45af62485b5dc782552f45e6a73a839848efc6ebf39a46c06c3abb11fa354d45076082da215486869fc5f04cf203345ab22dd0749513828d5c3f0384a48294694687aa67141ed9e5a2faee8bdfb95bcf7fd431c46e8d47cf08b2703e64a078103984ab4e0873d066dc6ea5698f595c166b0e5d8c8e1e21854e6883485601dc2faa615aa346dc14dd56c15cc7bed552110bb25ef8ff508fcb466588a187949c93a2441b6d315076bf579518166e52d1975d9536b9279409325d9baf3e1e0ec8a3f4293e2ecac044539cb2874d97e13bae971a1827f03f9bb300810f14a3ef86cedb9bb8582e3aac7070bc8b3d1199f83295096ad98970e5af7f1c684fee53d76c2edb6e28aeb998dae30f83e556806364153df0b6e62e28926f72dfb57bbadae12e6bbf331664321a5ddcb8a1de41c60c5303ebd38730f28833b5272b95aa9c826e2eeab42a942f9c01c4cb516343307af50987f608d6460ccdd88ceff70ccd6839e64e6d64d0079958f06653f34710ffc339a9cdf0fb3423d3ffa9e27d0dad609c819e9ff9ce16f667bc81f3dc34fa58569bbaa76b83d848788aecaf862fabc4a797f5f05b2daa47b45ffa9445d21c91e43776acadc66757477aec48d07115763e594ab0d27317e6045890a31914982a252e2a48e21c0606e3e8a4504624f3ee58d0e66ff17c895f9e3b6884fbdab7845c066a7479cd2a187621b356434d0420fd0366484904d34402b796f413eba44de9d0ad1a32adebde1dde51f2e686bdc7c81b80204a07294ea29aa04ea8d191410820f987df576c47296a15bc6c8ac7a2f9b4555b79fdcd52713bc9586d0314991d1a563dae49b52c6aa551ecf23d5d301d1e2c62d183e60c2572f1547324307551902d79310ec1947535458cbb97a506d66fc20db5050bc80ad1daf4fbc0ad9800dd91565958f496e731aff66f7df68ff21fdb6942898fd438e279f21cb8ac93756a59326dfd095a82549bac2bc448078a66f285e96646310af50da2b9d3251461c60742ce15a0678108615dcec784ee1129ce176ae1f78c518a3e33c138d0761e1ab769ca3ddedb82f1776361ffee8925947dad6aa3a1e87cbc1a3b0e3a5f821d25f151424e65659f931482e58afe32c0feb3713de0c4e83f1358db303ac284a622848c6512a651d4e108f0998ac4c680c10f3cf3a305ef7b3504b21c0f0cfa1e599ad7840d5b4bd0d6e586785e29221ee70f058a74ba3b8cf1a86a5b67a631cb6188d38c3e4a428e822f579a68322702de803ffbafffecf68a91c684c0124de90e2964f8c93daf7eba677835068c9908b70380e0c0b02d6a35811db4ade8077913ab38a98aeec44029a2df8431a944895502a0cb4a821158c45a57830b7160a5e14e8cfa15493c6622a493c0f8ecbc2bf404be3cdea8d2720199cb7b6c0837cb59dc735af89792c7b87a5371ef547683fb09e32a51013db7d7db286943441abcc5e8a25b194649da69e314e3d06551847a35960448f8b78bfc529c7b9f55b16df5040ae2e18808a42985f3e935ee004a7cebdd0503cedfd8e74831daa9eeb5036b4661e0a222fbdc35211723fd6cea2e5782a430ebd57844ce2c2e6259f4f46f19b53e462718d10b07544170d08fed863aea589624c8480b708bb97885104d66cfdaa7af703cf42bc6eb8fc854744b8025326cca9685e05f82d4f3bb69a2a414368954bab1ab0b30c16b2547cac00147e0881d22a9f2452698db2c83beb594ecc86c13b5f4ae0aacd994e8c7aa6f3f42265274e4e64ed537efb19c79112685cb5fe79f13d2f11a630b14f221634d6e6e82957816ed71f0a2bf72470dec24e72d9d1c040c701728db3c0c4321ea0133310889a06ef2f1c42153fffbe59642ed5ae3fbd838b5cae93973dfdaf96a5a4cb3d1dd7a1602dd2444fdaf02674c0a082bb8b53f35da046c44298aa33233f50bc2af9e60561cc701f88745f07a22698c905981774a1d6eaa5c3c8a23801090aae1818262d0fccf4a51b5f074a4c2b44b5a1fe6562c3721790b2e4ed41930f728678f0a112c05a5b82cbba89bd9a2277ee30b9b57e90babe200828a808cde3fc55d7aa5e2622433717a327a36f84615bbd5e6d8a5850bf8cbb5cae89807e971025598c163fed33c30fd77989c1502a36cb4f9aa6770ecc36c48d726e316fe9dfe8521b2be7975f08613b69d00c3a0b049c593b7a2721898bf0c527c4e35324710a82e8db781ae12517a63c4b847c20bd932d499768c2e334edaf55adb5697a457bfc8e87069efa8687bf8db4d3b4187826fcaa48af8b65a3c6d45ac142ad526ee76453a5aa6e7626f15a2da9476a1a76b2388010ddb0703d1751d6450dd581f9d97b1cc443210d1f9788000208763b9017114d9f32a6749ceda32d3c6c37bc85159c06baa91507083c51c8bc8f43e512b1766f1819e29819f4a78ee0833f011fdea4446550a11406372c2349164b1e64d5b2a854e1a55ea8d31801829575e6d13df5c862464e5e685fb28d4cbf8614c931ca0cb5017867fb543dd4dba02360b3a8192adcf49189c35eaea131039e737798e0f2a9267ff87f7c07dbffa264751c69ea53eaff7cb79a04cd869ee460b70914459de5eb9c376b9eeac4ea5cbd8be836d684543a8e96c480a716e1bae83d13136191a195a6bd71bc25e0d27863e115e425026ce350843aebc085862984e68de3d54a2f51a33aaa96d575a84e0441f6cee7fa94619b37ab3601c074b9de0087a2447537754a848c82fac290051529655c003515bea409b382d54265073a24553ae8ea51057a2ba3bec610bc1873848b5d03a15b5405539e5ab9b30be1dd074136e87888ae016c8c9e56b80394ee3cd39cfca1debceff91d3c074e1ed224328e739b5d856c054d0ae9ed4e842c9f2e60a7914fa0d9a7d9e9d539172191d903aace5df89d4cc9b91448ad64d17e1012c4bc6e0766c45f188dce66747cf74a632ecbcb89110d91b828e4cd27737403ca20e83c508a973ef8b2ad709a6f16fe833d54dddc5c31f06ff67eb955457186042310fb12b87be6aadd17b600967e6d29a6a081084fb8353a73d3e46d86073cada0a701c70354e965ad939988c3bd2cb540b1a9e8107515380598e370fc0ee341e1205c66fcd277153f858dfa4ff1ff458828b59c09423ccc8d240a17675024403da1968f57011171830339ec382848e7fc0634486c1933b91d217c00a11841cfe27f08fb580a0292ae29f9003f591796c72e06a4ec093d7968a2b4291d5dd54459a73920b0426957561cb9c54d2767026a88d627b1cae9584b9198a847b323ea4d0e22cbf171bbd788b9567b6de566604966f7a409d17846bc0b30254a79632c23ae1faaae190c089fb543f26f54ff882b6302c4c75e972fa815296b53edd8c64010a8e9f99b636b46d3546f67597f7b5ad2a6b2ae93e0572b7128340b3e725879a78068752819c06d2239324746d355f723b79fc7318ae1ec950c0ff0afeb02e5316340dadc400f1e5d94dad2d11d1a28f334070654a8e8cac693b7b9e924d9808c787d50ab1c31de72148bba343ba0b891c364397e6e7a0511d7babfb5dc1c50c2ec5faa101b87117f01f3a20c6f18cbb050a48b70fedcec480e9e8f82812f9146a47406f8ca4185dd4271f5a06480fb24cc895ae9a9b42b7d4897c2a49c8c6e89c2821fcaedcedf20c2050929a95ad7d6186b6ab8375592a6562bd4555a41bc5dec4ee3d056bf5f4d44a261909f1f73cd15fa7fbe1d0dc2eb4f52ba4198a8411468c84a4897a2da0446eb77f00def597e3f295a177c4d9fc03ccb3d15757b43abaa517a9adeb0ffd554e5e497300ed21e5b89945aaace86f7f267f8a4a789f1555fbfa6716f32cfb7bb6710e2ae9db54bca6ae82797bc3e80a9f96f4687b98f067be81e457296d0f3dcc0c20f7a8f04d45d2a6e97bce1ebf7ec213b0ae5cd7d796ee72141496b408598aa4b784b67d32366a8263df2cb22b205d045d792a4fc832961fded7d77d94da2c24e790e09211b7e127e50f046b931f168ca2218c44d640c56bcb6cab2982d2eb03927ef141d4008572a264c2aefa7f8b89a8b631f2d18baae34eeb1463aabe5369ba1ae8a77a5f0678128c48856fafd847492752006be3ec3fdcfbb0f2c758244fdf7daaea0cf7c08e4355cdb269eab631e7908b45e55ca2e087f271f963aff5e451aac0c7067d693f6f636abd51f534d4e472702214a559eb080c33616b73dc1d50f8918570a9d08000d6d8bb9213fce5e5444de063f72091b7609459e8307bd8b8f4188238ff52b1e65d00c5e2765ffaec44d5c4bfe8c31d4fdac5b46a8a954d4e6c25fb18aeefebad8458a77cdcdab38a4e3e182ed2ada750909f8c92ff07738ca075600e3e936977d61cf64bade8823b46ada1c398e5edd508c236f7bd15bead1f9f6fac4a3afc599286bc389cf2039fb4dc608976789a226326238545ea6c44b27a6e80c4d113b856619215a2ccbb6930fe836ec1a8e62700dbb4a3b2859d72ef377004eb13dc3eea5b0117fd0adf01110ddb6b5fdf2e8c902a74a13f7caa71678ba222718cd00839da69a1f762799688b55c4a9eab1e6f119e247330859e40676db809e84714699b6c8eae06c2ce7b4c8fc5a61256556de396361717400406ba2a84515cce2b943e06f13ecfbca602dfd45404517272c6dacb1958fafac87f61b1660dd600bc07af433d4b934726885bf2e5d6b2fe40bdbaa59efb58eab8cf5f982efcd2e61a655a2822b01dfad07704f1f52f04995a5494d6a31fac897eed0f230486fc1fb1b0e680e6824c39b1991044303cdc22f266f278cea62ec11242e7d2a9b5c936dcd92c5751d8a1646a17a5804546926ac56d08ec98638fad3a419246c0a7471f4641e2b59623365f47e8e0ed3d634db6bba36b6de2e8d6e19d80a4b0b502523d2886cec1ca6830407c1d0afd4712d5169d4ec691810767615cc0a8633e867a30562a40f76480e8d1c16c49f95e4505af75e6f8b8520dba385260089b5ebf1baca34e2b43401c3ba97f085ee53a6376eb3bf7b86f160b18b2c495e8178712e57d221cf18ead3f623addca04bc71e0c359941c35b99b2da025e85e47aa53860280358472b03eb40a00d7dff085e91fd9ad5e75cf5db2ef738f8d654577c33244191a66743f105568501ea97c81e5cbc8e4f4199465096e1ef4b7603c73689a48ef4634dcf6e26f7ef833999fab8405a189aadf4e160118d145536b49e732b719fdee3cbae2d97573fdba02d1951a37028861f8fb4253522af28a75552379ec06b9ff90704fe00d32458bf9d1432172346892ce0a2d0f005963bce49e89399c07667020f1a1b355cb3f004077ab1482f1bbda83cfefdca3397c44fc86b240bd020bc1cdc8c66d76b1033196291496c861f48efb66754a5793187332509ab7f0eb96776ebf0f03fc40cb37716b8d26733f9af3940202e1f43a0fc1a28a03f95966277505464bf4b660270c9c43fb614d64c8b7ec03a9081909ae98dfcec3337ed2614c6551d14c7594f48696ca101c56b5f07e478c0ee1486425de3bd8c00872c61911bef0f10e64b9862b85562dc342813d15238e86d49bb05c912010dfe264ea8cf388588d66739d3c5b57ee71ec8873ec2d21b55e062ee461263b001bfa0c768bd5458a98bc6dd067091c6ed074e51143a413640e6500d5ffb4766c46cde5676fd184b3ff0e6c28159d9bc0ac7194f40687d320ab16360b0a9e2ada91f27aeddd936d54a59a92fae9d5bda8d840faf02298323a3ca971b21b39e84d009045721db6343577b656a424dc9e3c366c30a41e54e846570b311342f8236b54bbd0a60c780eb0006375def128d67ee0c793f4e14d67db33700bdef9a689ed32107964621b3216dd2398adc87d5de2f3340eb4547fc936f9661abae666d8f9e5ed72073a77bcfcd07b55d8dd5ed4305481ff2101616101a8c2b88f1451f654f3015b0e0139b48806761001b36223381cc12bb36381bd09ed1198c24c9f10ae9f2a1a4aceb7e41ed067fd761898066cd0d344e457e7d748611876da64deccc72f8ea009c1ef118de6f85510dc2e8971c5459bc84ce5c1e16d2e978d6302bec2af4180d9782c7d24e6d0672ca168cf3800573ac18674c9203479fa1c36da85484865b954df84a0fb85dc37a34a79a506cab2cbe71b4f07d82f14733f035bf81d090881377fd2f6e4c67eb5c8205a931258f36ba0b468b87f3a8b701acb945bb6190625836ccd84e0875bcd84470043e34dbbcc3342c5a8ebb4f893fcd2d88e23271b0cb64d6949ab1b80c0d6d1647c39e7b060c647af3fd2bdf22b6434e88aec9502bb07985e252d6ce96bec92b070dd34027c3400f98708846332c9a369e4ea3392e5dbb459cc1873fa59e5d4d5bdc7407203e16b842153440b14ab632241db3201266c4344a52a62f1048354ed27d14e35f183cfe5405a2f94fd23bce51a4b25430cad7eefa9e70ec905cf841ebb5e5292a73d12c6be9af412679543a7249946a6c27624b4af977e2164bd4bd285cb4c7896b17d132e262e0cc40a7ea1e38d681537f46f8db290ee78d23ac91903e999338a4e11359d0617e4d81c7e541d899f6e613eab6147ef284a81048142b7af4e8ceebd1b6b40ae660211d8ee3f70057a41f0746409e83a7cf1a7eafb9677e8244e8b70a54953c7528ecf5a1d674e717067a0ea583d0b63c8a518c50c66cca4f93f2b98ae17744ec71cf5029434b96815d916380927eaa05718d2f5471f50c343472e49b6f1a3d95dfefb936074fbf20fe26fae57a2945d1245a65b0b7cb111f5d0c38045a5a7aab8487d0963f58103b91ef31db2cd6321ed222ee0b31230a919a741de745a1800132e41a9091c2e2e16124e443f2c4b1a9086f0712027f570cc84e283ccd08cf723e1878639391142764c26258b4f10ce824243d8a98bcf21ea31a0fab249fd48fd1e24f3802d070b6fcf2186166d0e2bfc33b9f85e96a13e0eeb11905611c53c276614b7b17b356629551230d3064dd00d4a5983cd375530acad579d17d89cea3e0f31cbe1b1b4297b2c200e068f7eec5ede4d964d2133c461991472ceee34fbf668e5a322fcae4ca639266c6490084d4c80ce4c88d5ebdd9e0b29db7e17064e036b061ecf980d71af2864d199707afa2cca23c07564c7833bb84ed4f4cb7d72c05bba8db0993f7439664a908c629d23876f28744cfce50754ee5eac84798141f6414c429fb01f4bf6330215c562a63b32e4ea9ac7076096d4b71e68b5bd2a100d89cbbe0e04fb2388ae81543cd1f215698a2ffca967d7bdc3b03fad992935a20d03a4e078e231c8c290f5641c94eb2d5e39ce12f4f6e17ce69c51cfc816092de623291d2b0584e7a95f5db5400a4820300058068bea747ef7107b8ab0f4e1882c956521dcd0cf172217723ad09dd42dade6d4dee2da54c49064b09e6084c091e5bef3d2d6b1e0f65f3b8f42aeb1ea902939c2713e0616105241bdcb296754ff32419350e2bd830dec65ffe9b6f5bb46a5118160953f72f6cb698c650d6a7f429185c60fadc11e69ea6c1f944061898be9f9030fda63d340de76e86e967ae03922dfadb0eb6034ac59bae81fb2905b03f3dd978d39f2b80dde694e0308b643384a9cf6a6898e2acc22e4cbb9998106481731413021f9c8760fab3dd2dd2918d0bdcddbd8c182d731569fab9bb7bbe67f7f9fed1bfadb5eed6bdbbbd1bc7451b0a5214a3fe7ed772fb3ef1b00612367b35a32ab529feca110d964f6a88ac6d19f5221c9293a4ba73efdcb7e0af23725eecf6fdf15b685992b1ca2fdddc5f72c203bbbf4ec9e365e2f2507ee8a1fc09e6f060fd5366d89c24524a29651561e06ce534d33f8972b4c0f16de29c3d3b1ce2a13f93bc7d8e874cfa3726fedaaed7bac596ff769978e8efc53e8e3847da0ab83f2709931c26d7157ae891967198f82b1749928b4c918b60ff223538d2aa2002730233a60d868eb0ea694345d8632dc079085ea18bbe85cded1384fb71c0208e342578c04359700f1c692948c2606a900503004730658cb01268709635fc8a34256a645c1a1d11845b9ac1fd318261b3cc5eabb52c4ba26cb7d492a3301e46fc38fbec08f709f2694d92f1b06551cd67b89febc266599345b2e641fea2770ed9ec420daef93e92b00622b6fa89c04774d2123210e7fee981c2e70a2cb85ff083fbfb27def4d71dd8dc3f3fd148c45be64bf0dba7af386c5e27e3adf68e75f4ebb56d661f67e9353442d88b3ade9216bb8726619396bd86fbd1e0f859d6a6941c0e4541598a70019a221c70a469118473c5530b1e26579a5b6c8eb4cf49c2e437d37f38739ac013383dfcba1a69a9f45e2a3d0be7897730c7adb66b71aec1b5aee50a362d4d848b40ca6608e811f23ddc86e03c558d457f352b0f41e22df93d9798304ebc914074b54357a97aff87612caaae773f8e6cc9cf710ed8ed08690bc35f3926c9e6cbaf124ba70d897a5e74f4232b47a4d217b6eb554b1cbbf4e0f82a4cc06135d29288381ee770faf81067ad8b13384ee9f1e3dbf803c78fffa38f348ef1597dc43f92c1201dc33ecee10d1cc1749902c72e3887383a9813e000c78834830b6759432003315a80bd769491e5a1bf2bb6fc6b6797bb5633d895131e583ebd2dc87f1c71c459de5593c7fd8ffbe689fb863f717e96c378629b17356091032cb117e10c4cdc8267d77c14a20ec6700108742822e3cb0862f3be6cddc0392eff864b07167ae822a34117646466b2f0c2c434ba0202209ed842348b58603f634516475b92e8210328e65734010220b48c2183a0313ec025568b12e61fbff6914692831e7a622b28fef2e5b532001e5accef4eac8bed1e4b27222e78421683c1bb015d5785cd2c67525b7a315e0fa28bac2c9d0a5b04cf207000020dee9fd1f6d838c6873070e9394ece49bfa391d2d722b8eb4a1e0b5276dbcdf2733cfa577f9ba54fa79b7809941205fff6f533818526f3fbf65d614c1059706f200b1e7a18d1a8ef0a1ed62ba5316d4c6fe3e0e1779c1b39190f738c61b32361f733fecadc9bdedf93fce5ef48d148f7fe4e463452dfbfaba01414d8818deb6742fdd277d182c0c6313ea0c1a5afdfb9576bd7571acb4f061a73b7b37848bb7d3cf4cf81868d1ed61a2c7f057f6d94523a5f822ac868144b42d87e2933119174860cfbd6d4b3f19a5a73c375774723ad2ef480fc3842da7cb6baf9f8a0b50f712ead85362561ff6067e2b8b96d5b57e3d3999b8d07d678d8db76da5ac27ee3b84f7d3a13c57117046b77e0b0c9a16a158e897bc70d1078f8c85663c1fd79c35225ed867a8a3ad601f9d683db632c7e3a4f8e90dea346f4888641062d839ee1b8ef4ca0c41b78668264e04ee2904cdc9bb89f1c47c3e0b251eada1edd993a9627632de93fd5073a6c4cefddf781cddb7cf0aa88bd1ad004fa28f2ba547fae51632aa235e998f3146eef28e729c69b0ea80bea84e28d114dc49f03bb99879e8916d580742d7f653bbff5e970e157c20eee6eacaa69ec87e04d221dd010174e8d95ababba482b5d1cc71d50070476403e64e60373f7274871d775c1fe350d6b81897307845d9eb63b6536d74c50e26d6e6007d40579cb6b22dd4ef34a978bd00eeb8064cbbb59a9033677403729198e3d9a03c016d4286ecfad68b09d914361b7e72abd5be6f794df75ac1ac9bec2cc189df494e7a18d87424870c61b9b838d60a6f8c9912e7446730611c64c742e3e33235b79d0f90a5438b0fd5e171fb43fd838c60b0e63bc64c1718c171c70ae63bcf0e03886893338cf314c808153388e61a20837ad450d74174166075dbb9c74e36ac9d479df298a34b0ca7e31a2ac083a3156e5a53e774fd508e2a961a3868da02b1d11366a6cd46c445f969a1b35376815a70fdcc07103c796058e1c387204bd6088ce6ae98c1caa1caac9139484fdbda0a3202eec2786ca33994c26d50740152583b621850eeffbbe4fc7aa468995cdcac6081b58b5345f2364546b94a8089b1d363b84cc089d99a9542ab50304a06e04366cd8b001028fa17ae3c68d1b3c3e8d1c3972e478b0a403b08617b7c7ed6144b559f4f056abd5aa07cba84e2b583e583eb61a260b7c78ede587c783070f1e3f80802008028958260a88feaeb482b4827099795a369fa0a1f9538308092264c8081dea878690214284ccda1683bdc1909b2143383231d89b106e4208fae1e2c50cb60881480821cc5a9225e2224204890badba3c2142840871b984866a0f227837373737228820c4533f9f96210045021080128fc745073572504400458ae0489bf1601b4602780940005e0f9ec59136f34015aaa5f1c19817ecf57530afc1784496190c268e30b32e3322bc3a01c381c184b0481c69425766ed6b8ac0190107a78833387b91c6c506441b8e342e314c1f46f07a07c7910555f02065e238c2089f0e8d37fe0dce7833443b420438261c6947f4b866493324abc386c0469a114634238ab0cf6844be3f8d46a2ca880783c160468ce0406384114618e1c8911b3538f2e9e0c8822a7eb0cac3239f94898f7c3af1c6ffc8b704c9872388c48ac0469a1144b3148dd468e43a49a34b97cd143469c98c49bbd4263563721ca593d6263543891ee88cce66dd333aa3b319a702235e28c74d8ea374721ca593e3289d1c47698dabdb068212c0dd7d6b68d4d0a8a15143a3bb4b0f5a5ca1c4a425d3464ddb6cb66da61b6869bb61ce289d4ad07903c7713cf41014c4d59a049704c72541398ecedaa441471cc1d5ca8d8186194a4cdaa536a9199376a94d6a06000010aebbe5e6898021dcdcd310107c21e07f1090c7b740103e04c10702feff0079bc0f10846781e0f700ff2fc8e32d08c2a740f09e661ef6a3c0bfa72e3cee6908847b6ac2c3fe1de0f736a0f72bf07b1da0f727f0bb279a774f44b2d57f0334e10fc0ed051c88e20c56819c143d304002e700a50e1c20c201c6016e378041a201a6b9e00a8e347ce3fbc63d01794800ec345c63038c58e52f4e8afd140af552a29638b69f722ce54b0ecb4f8152aa602eef600e054ae130f7fdd6828e7214ea4e71cc711c90bf4e3309f3be08f76908f79f68b84f44a719accb70c4d903a21f18ca56bf07ce34363ff666b2d56ff268352b1d3381310ada0c7c64b112286315dc3cf0e3016d44d8c7f93da01e6ebb2c6ff5cffb3ea44105f68d07fb687080fd666f08b707b46386630f18b81d56efa060983b9d9ca718139decc1f6470e080bfdae468d0b836a4a5661293b2965150f8d3ed6dad520e947f939c43cfe7237723772378ae221fd0dace221fd746457c543cf6bb2ca263f8a043921ec140fe9cb5ba5cbd8a7a9cf2ad3677bda6eb6b7cb7818d2af71453d993e45bb8cbfc834918479edbd16bf4d0b9b9b2847182b3f7bcd08739fdda88fe20dfd0692b026922dfa37db0fd8f9d98d307d79732c72233347fef29ebe23754fbfa72f65fe323dfdee034bef81a5df4c5ba9c97839c4b9893095d5b4029b5bd632ea92baa42e298dd1afe22fca85a47dde9540053d4d24794983638d4e8665d1d75444f108a75aab9232d992cf0216c7d2d3a38a4731092755191eb2b99352d0466570964958caaf8dc5d423611548b6e4cb8894d4b2eee16844a59f229983cd1c0d0b116fe43758022212913f9e179b4b3f587e0592dddd37c723202c3f7367482fb692fa48472268c4579af8a22f935ae2b8713adc1338b352803dd6c94a267019ee50b4bb7a386936ced9b91f5c35993219c513c7af49cb648ee3d724d2a4e7afbe755bc25a7f1315d6ffb38107cbe9311664ab25cc6531976109c45632d192896ef3574ea4e46ce04c255a2b97024a671b1969c54b081af68eae0fb8c98b628c22614c7a8963f95d0e9605099331fa2b27b305ec45feeade0cb6c0c45e84c2dd6d1aad7ddaa7b68f4faedd4413f1f7263007561fc242fb21c2eeb5592c83734f518489c00ee321ad893ed26022117f5f41bcf1cffd85050993b1f89d74e9a72da5ef2f858e95ae151862f53a906cf97f4a781850e1292a2c3d2625a6d9360a4e9adca1722bd359c06538d6c0882752956f72fcfdd850c25e28bd7cd37fec052128a597ff02f777655ac5bb4261fbfa29fc668a496f8a6173c5f24340c7b855fc298d57de43e1bef4df37aee02afe0b42ddbf507aee5f10fa2e94f9f55f58c5bbf22e94d27377156d77a1d49fffc2bcab78df43d3c7ea9b766057dd731f63ab8efb144acfdd1784a0c8afffc2fcd2a7509f7beebe20bf7e8c99b89730ee4d1f6b4544a31b33365b8a2d7a4094c6872466b159c64b0df0c4f35512f6f910f4c31564d890829fd8dc02cf0fbd8881e70f714998044ae0c310134368e10197d87c1c7f4518e0f93964e0f949fc55c2201046e8a0e50668d45083d8fc27fe52e10c3cff01fea2781a8538d2bc7cf11214032b582f61932693c9b4c454aa5a5938c657ffe98797c11721bf3686f22b58634b4ab1a2a4622955f0ac957ee7948b18c7398a51d2479c9081f6a50c237aa23140ff0ed63f36fdfe159a1e558f2b4ca0bf7dfc95c5617d85d6efc21233dc32dc1d917047344bc0f89975c93b1d34c31d0647dc1fdb7cac5f7b6b2ca369928bee028d5348d10cf69f000b1c08ea6c5e0d568a471e3407b07fd780bde25513fafdbd832d109f7e448a4438e2ac62e128a690c4e8bee8ef0f058f394f0c1efac6a2b4293db58c2fa34f29a594d77d49bb9f6abcf12e8c875d6c26ba1d0d8d8dfe435d8dde208d31bac8e289a126c618638c71ce49279d74d6e07927d78505ab8051d2d8dc32598cb228bb2b1aec8f18d48a8ac387234d0562d49e15c37f97cbe5125333da82bac1068e608ab0c224064c1138cca735721cc7d55ab94ae76f9556bad1597ba202147f457f2d3812d6efff850b5946c7196cac55f97ddde5f596758fbbf3e86cfedde32cace0a17ffbd02162e0cab88a6a8d2be3ba41caae2ea90232604e2005ce951669b853f2dbf5341c552a6ba5a4e13afb7bde48fb72d49208e9793558301126bf87a0c0f20a2c5f80e50fe11e9a80bfeae71d1cfb88cb4ab6717ff5fdd3b1c0b66d31c8bc68d99680a185f65013fe8ab42586b0fc3ef2d71987f50c3fcd84192c651761d9b43172892c641361f93137192cbdbbcd8b7da493de7edabe74f35790035d4040c7a8aa6bd0b6a47008c7d52f4538ab7295617401c51929a5ec963d5b764b292547b4e108860834f3888e26a3097b3b7413d8bf9b5841c23a963b087b77511da1364cc3e01c8f82bad019a678051f3bcb685a0d1ee3cd1b372937b96dd2f8ef1a3264c8109a0ace707f69d4f547d65683e590a2e4d4a8f68bf15920b248f26bed1f09abdeea9f290eb3f1fe51fc55f3fe5d8d4f3dad01a37868e34ef11a377a2be56f6faea8d3cdcd83fdbf9badb7ba7ba3b7e2cd7d05fb6f3f96db906cfc5c673b579a5bd43881114c3ccb558452ca954a714a7a71ded5fdfd341775b9523a9831cb78a9542a95be4d2653a964bab9f4a6ef4a2693a9d42da594b2069bcc261ce20a433c2482afb281cd61566db2301ad7e022aef7df92b0d9e52ae2b09a98cc7107dfc35006b63fd65adbccec87f68325e9a8e7c88836abcd807a92b02421d56846b421a31a528dc8a886542332aa21d5888c6a483522a31a528dc8a88654234a3aea3932a2cd6a33a09e242c4948359a116d084b0f504fcb8c6a4835a276975235e9b6715cada6cef3beef7452e1a85438aa5085a3b241781567954ac2aa7fe173e567d36267b5354a2973dd6c5324245bab576db51bd08665f3d97eb20acbed074bf9db8fbf7205f2d72bdb202cb72c5b167f458a44a1307a96ab48c409dfe5e19330449d2465864b052228fe8a65b410b5132728a20b2323e2c729a0ab4895183f4a04abc4cf1129fe140f65ecfe2855ba4ab7fcbeb97e9528e09488147f8abfe2cb8fd2318a84752cc618bf63475ba6b7de7e90a58f3c34b5e48b294cb796c0196cbfdf271e4ad9b2651e768f87b2c75f7e6565cc30840d3cc8e0882d8e9cd0e1c8ec809be0632949074424d1832b484fe4f085498c97baa891a25434df4ba5927337d7ed664b6f6c56ea52ccce0f53c000892d4040c31960c41ae0acaaa935aaa589115aec600c1b76e8811934e8be7841c2463ced0f463b3378e04a0b84d881192f9c30e3b1205b898477fa610362476602256800860c8d25d2f0827ed9c195f036b085180e74d9018d2b347481ba610bc246fd120315a26b3ce18205416690f982c5082e8ac4338cb407615537be3ec246dcd4075b3381227408010eb0f0a1c6c5a8089634c0eed253d0a5624682a7cb7fb9bb10ddcde0082da8a8c1182d65d46640ad20c39dcb9368a4f11431573cb3adc15985270c786084c50b145e948087587e3c658073113c3f89c3a2e348589c3f5dfe2ac24176ce5a858d55ab7a59ebc07419eed8828fc02bf6978da6abfe8af16b6fdfd3d2da51b0d2ef9d0983bb93a08b31c2acbb048768f75b238ebed3de2de8aabffa6b84712fbfe3b87770be9cbfdda98a373d6fbd30f40e85a16577316d6a32921faca922c1a69fa669fa3cbf4a93c9f4f53b13583d34999ede68bad264bab974ef87d33571e6cf49bd50991f9f7efc28a594dc6bfb1c7f8b925e29af4cf979ce397bfe9c93075a9cdb28b7d128b7f62d5d8cc5d9ed9861cfdbb275d7789efd1c47a1dedda5d7165ca8c1a977a106735bb6af5ff0c4804a93264d9ad8765b483df72814ea66175cc0a9ef2528d4f7a783ca32053646bd045b909f0223eee2cd5d93021bfb2751b2d34414ea66ffe92d70df18f540ddf4e29742fd550af105faf163ec03bd9fdf09f2c6116ca48109eafb96ebc14a25f2aada3f04c4f7be630fe9c5364e7dfc746cbcb1dff5e7a4b3d223efa01ed5b5d703c8296209c47ad6d3c03481e5b3be938d5b098bf291a007e4cd3cdc7cb62b4b36af069b3da00d47231b4e851266f3abefef66de150f83b01e96ebf9782865d6e6b3e793faf0e5b358dfeff94898958ed9dc15ea63ccc606ac33d8c767d4f090150d96f5a8f7f1ddf6e9f8f8d457c20e667dfc4cf8e1036c3c3f27f259ac67dd167ed0945cb150de0fee5fb1ae12c72cf95e4dc278e42bd29c40d3f80322afc82b038b5a441a1834b8df1b62a1a4f7e3afecf968f1578c01ee6fcfa7e5f3a9a0e357df5f0aa65fdded47b62edeb0aca07cbffad5b5b261f99d986cc2bef0bd8ecf1e50eaf3e6d3df40398e0281e800572978fffd775fa0ef7d8cc94f89634fe66177b1b096352d098c0f3ec2dcdfa71561f95d121a14d8257d3af276683c94a54f475e0fe8062b5bbee96b3265320b4cb118f51d894c2ba6af89c43fbe6b29320d1a2cbffb9ab80c4b911660fd948a3bde380cdda7f3e33bcff3cfc9ed18a29e7e4d26fef1de77c2c4acefa427c10c38667d17a54779f2f139eee34a91987563bc417d97e4afdc1d1a7ffdb87907a340205ffa74e47f608c75b42345a1f197bcfd5d92bf803c2b0808b892df58fe095cc9bbf9c4b62bdeeadf82ac37bb1e9087d2061bded4670f68c69b20374f6cbf3b791ef8759ef78131d69ffdce035729c4b752df7bef5af95e56fa17e8f7a4e4ef81f1bff8f1937281c6f26bf440a17af4b8d9c3a8af1ea23e7e3d3efaabc7453dea76481eb61289379f78d3efd1605bc8670fa8b1b0ee017941feb2f7e7f75f7f8f5130c684802d30573c04ccf66b40fcfaf2d39157e561ac93efa012eee545bd47e1debfe6e5a3c0ef4f09f736fe0474e3fbfdc6670f888ccd158f0cbda2638a1ddf3785bf320efeca61887678359b9ecfc7fb88fc95b79c6afeca5be8a9c75f79cbeae53dd13cf4f157dea2e3fd9e8880a07cbf179480eff76608f87eaf0b0adfef0d0df97eaf0921dfefd1827cbf17a6f5fd5e91bf72009ec9f77b65fc9501f04bbeb3923f1599f157d6f95319efe8c9f77b619cf0fd1ed201bedf3b63c2f77b491e1a944cf238167fe5027c922cfecaf8633f395a0800544290939901ba34196235d1e3fb3fdafdfe2f8cfdfeafc85f79004fc2f77f65fc958f3c92efff8c8c7cff676684efff8efc95717cff1786bf728eefff90fc9555dfff9df157fee0fbbf247f65f0c3efffd0f82bff03f9fe93cc5f99c7fff8fe138fbf3208efe3fb4f58fc95618ff3fda72cfeca02f8d7f79f7efc955d2fc2f79fb4f82b87f044beff5493302b3356e4fb4f4037c088bbf8ddb87907dff820e01009bc045310f80bb21cf03fc010a4c231e085806f23e4756c985c2a3508142f01374b1b96909b13f0416c98fc8f4bc5dae8fc6045e0da7c4b0242765c1ea11cfe924ba566d62b5182809b83a0d05272a9d8e7110a72f3122e0137a3f04a2e95cae46625d586c9920f621324c8cd43be657365e0110ad27a1e21146ede62f34c2e15abe4669d1d4a749e4768c9cd411e01436e6e3d0a2f24023b5875c78f9bfa213b845c2ade8e2142b6ecf81f1160edf81b01264b9e472875f39087226f16f209781488f208907f7c924ba57aace7117ae29d7033eaa59792a1f7e4591e014e607dea01a89b532fbd77085c197884503fc35f1ea19ce463974acde1e7e4c84799e01efb9c4bc57ecf23246f4ef27ea970d20ff039974a7d72734ef54c48f2d293f2e6fbee497f1ea103dcbcc57b13be1cef0970a9d81c02c83f80bf098f7ac08ddd9ce44ff8c2fac57eb0bed4cdf7515f0af53c42496edef27dec01e1f73f1e9024f63c42ac9b53ffe4de8cfa13be07d8835e1ea1ac803cc29370a9541a3e8f50094e6eeec1ba24d0116e4ee1433a8293f0590de87133eb2f7deb801ea7cb23944bc8482a0906b8f97e8f26d6927033096f04c9f308dd9b9b700a7823974a4de16623951aa0c95f7aafa5d73e8f90026ede42df002723f447381919e179849adc7cdf00f69b7c8f0694f04e4e24d4939313ebe61fdfe3c4eaf13c4225dcbce5f44e1a40c2e99134800424cf2314deccfa126eeef14e1ec88d0e22974aad0ac001f24347f8630138978ad5a18202c24b85c33787ff43c7fb00a970780b0380ac2e8f504e21bf5e844ba5e28800e4031f3e705e37e3bc08978a5dbd7edcace216f01fe820f22a5c2a55c707aaffa1e3c70f1f3a7ef8781ea105dcbc45c78bb022a2e33f588940e479845437ff78958fffe0815c2a1cde729323858ab7ac70ea4a01383ec72abc39c703b9543c7c65e0110a813c8f500a376f59bd026e706eded2f3b1d5bf2e158b65e011c2793d8f108e9bc3c791e3662050128000149e9c7000134a706280264cc000fc121000af0474bd0e18c227010b100371c78a7c0ee87a0280213c09e0009080473c56e48d80ae1f010ce17140d80b148015192b72798402704f350f1b00f7240218d28793dfac8b2e73e8501ddd6527d1a9ac87ae272edcdded2eb8700fc49c200b929850ad364e3dee3f8c6ffbce2fe6d060fdabac1852153e8634d20760d964aa54d6c65e41a6a06b1b619b45715bff9cf3fd731c9cfec577468b1af0c59186021e3906e884fb2d19b8bfc65f2a2c615eeb57d95481460b2bb0e8b07c6b450cb2a8cd065013238b2b68587e117fe120c9f14161f94c7ec016cb7fc205f7c0f25578c00a58e01b583e0b587c81e5bb10a5082c02965f258b3258be157f4d2cbf65f29bc75fb1270b231cfc15c9f890e972c55fd105586ec032fef82bd25ae00596df5afc75c2403846233d51d0d872a9e12f6977964063045886bc4441c3171d9c2c0fbec43a55fe6adb5f7aaa204a03cc17517c7126e513cb48b09c024d0e0e9bb11ab17f8db01caf517e2186071a8e6274386384a3981d8cb02345237d44bce19e7b0e47c2a484f9f8f8ac1e9fff7762bfa2f7090ff8f777623cde0718ddc7fbfb906176e1ec48407236e448591655b063591e612e4b2447628156b6b8ef01760ee69e757439731ba88f17742450ca401e60942dd5ec4248168b31a1b6a1021b63f79fc816f76f45088a3ff85684a0d4f7b72204e5bf72cfe35a1182c2e3efb592235bdcf378f05a1182023e8f6bc5255bdc83d7af957aadbc6c71ff37ca16d27f74d58f311edcaf7a6c8cf9c7188fbb02e1aee021f71f9290164c78f191c9622cc816c73d15c33e8726079c2d0673022e383b5215d9e2be82dc6f47316905c5bfbe7fbddcf7d7b378c37d0d1a2cf71348c2bc074df8703b6e9e3f98b359dd3c7d30a7e3e6d983b91ed9e2fe839ba70c73ef481256a3560233b2181763dc4b99ea22e5e0cef4c08c2964311c37cb9f1b374ba09a9be5ccc6cd72a8c6cd9296ba391ae1ec4898138345143eb298bd5916610e75b334c2dc1564b008238b9d6e964747a8408216fb6e9648decd320973df3d12e648b2c57d77f39b6e56956eb6b272b1890386dee989dad2c91cecfc50a8dbefc4b66bc5f4f4ad94befecec67d7c1358028b70054bbf13b342b4f2bb42a13e7d28955e2b427ea1d02f5dd7cb967c2bf2eb95bf13db8989b1dc5d12712adec88fb118dbae4b553462e38da468d8798d5a32d1554eaa6a95744a221a070adbefb43b8735c650b6e29b88b0fd2ea594d565fda2ab9cfb46e7cbdddd638c305bdbbfca56fc76ffae5faa2895a8d4ca95a8d48dcea753362dc556aa0214ecd8aa930af6ef2f7d4d50a652efdc090537e99c5e2a957660f4025ba38ad3072cca890ed894115498e0a4026bc29146050f3ad889238d8a12f850e143450fc76d743e6b0a2c38bf2b8b6a1f5757d5618833d22081184e7c51410e70b648d0e060fe29d6a21883fdbf5a514822113ea3e67d3a1306510c0162ff3805f6efacaa8406162084d8010c62900128568629aaba76958ce69b3e9d49b4050cf6af4447554091028c03fb971ec7152e3a70b10514445590d1a1e9b162f3c04facdf25611eebc791b0ae351ed3057616e878c5e224349f6b26c26a801e72208a010469684b20d53821031d730311ec3fa90d3694b068d42b3002cb9ff2ab0c58be27df867901965f436301172cdf46fecb8065182c6962a0c04f60e9fa910107199860891e5e101b43da08e59732fa8fb08bc176ce8006592031039f349a88e58aa54a4679b30bd6b365d81c86ae1560ffe92f1f12566335636a7deed3913f4b73f28060c630b4e5200229a34f986006ef00d28c45f4151e2c32d72794c0219a6c8cc148b6a2554de15367ac51d9d81a294a80b3fd5ac241885538c630b35402271916c48f7d38828a2f8e00a306246438db3701122500c3aa70a42111f4b65493c9643294c2e0d29558697ec9718955baa10782bc15bbd4266c186f450f4cf1217e09b3ef92c964314c248af01c416cf68fb7225085e96099792be26c754aa8256c0d1c6948889153054db69e150e91224b8378e248f32206573bd0455656cd307daf06cb61d1af9679c87de6b4c0f439241ad65c1167842977c445c13d81e9c7e698c0f439fadc11d148d78fdf327f713d1dd2e780fa48e67a642ad516affcbc8fed9d40ef3b0f6ca3ee363e81a00ab2452bf8811daba0630f04631c90acf58987f44b4936c6e66f49b518d723612db3aa2d49c2a2e42da9f6235bf44d6d32958280c09f2772f0b19823c21469c8f4440e6370e6ca5853a97dfc952312167fe5fee170f0d70e31b81e4cc3c099cbd20448cea29188e96f48f3a92bb6e8d77aa94bf5b24583b64f47d2ff286ce680382049370e08530e8803f2d7c4f4b9227f9971589482eb42ab01658e9b1165ae0c3784e9576ee37afcd513e4af78032c4b60e5ab669b7f5bbaad88876e2dc76486e32158c4c352ed5c92e62a521fac493cf4fab95e26372776cc5d0a8b8262b72fe2ae0d4ce22d174e0db2a51bdef7307eae98e37e761454591258f9d9d2377d079a6ef6e414ccbde082b7657b6afd3561db2de22d7f9fd83584a3a291fec945e72f7bb06ec3c685a1268987b2d69f739a4c3aa6d357d0851a3c4d395f44d3cd159bb1f1b612d6f45365832dbdf71d96aebd5abb131877b0a7b71f4fd7c375e27a73eaa387d79eb0fdfc4e7ab1119b2caef11577ee758df7af09fd796b70637c3addcf326cb6b834ef6a7ec42b25b8b1d77913e46afd69a391886bad41d85ae7cf68e4d39978b630c1dcd8a6c1523a0d3ed9d35d3531fde997986adc9cba2ed4607bf3fc0c343e3dea77f0e9271831eae4f9d77d67fa2e983befe0fa26304e0f6b5dcd8f1e524cc1d5bc4fac6c398e9f92fef8b024de929fe3a1b4d27a890760a8f8c13d700433c50c77a8c81343f46ee03fe9cff01d18db42fffcbe2d94bede164abfc9d8efddecc2145cda66edfefa136ca17fdbba9b486c2279031688f126e2067309d7effe25a5377d9f3e85fa6c53a9ef2d8ebfca9593a46798f9db9cc1fe3fc43ff5a19accf9b58f443cbf7e9f3dfc7d3ffdd5fdf77d39d8efbbd5c9f7a82df625b83a35b83a5d259d27ee9f1f653741bd1499268d9427a02eeed3e9fd1473dc03d6fb6cedf7e70475b7f43cb8427d63d477351eae505b90555d2b6fb0fdde676b8214d9fd0746d9f2148fac578a374d64be04b72652bbf439b204d6ef298db094abf9b3ef9489b9f76f09b772d2ab79636cfac4db10f6310f7860b10986de91f48402678113354073658610081dd9a0c6094c1dc0eeaec55f269375c0db228b0972b822a48411183b45049c27a8040c98c9829e7588211200000010007315002028180e89c52291481ae781de0f14800e82964a60521808a4418ea3308a628c22c40042103000206288323425b40100e1638228db63aa5828a5fe3b8083c17488ac9950ff66c505743773eddfd4c54076044ed45a5057ba48ebb6bae44a4abd1c2cbd98868bad9e06366fcb2ecd42000b3b665508106917a58a38018a3274ff234de6f4aa6834cfb95c8877dd18e4caf9722195b46e8d6d77ec0c72319851a760edc3b5569acb460304a1ec80d39332d70e9f6586b36a1d76c6467cde6b866070b06aa8c67cab3aa4ed1870a50117a01114c99cf59827a9c88e7b29cf9e0964ac0f0cab1a6dd4c594e8378fdce657fe1bfb15086387d2987febb7e834bb1e49001817e1412b7b38f5ef13da62a61eb1872dbff148c7f8afaa2c86ed446a6d3e83f82eacbfa0a6ebdb93f0492301a51471fe26ce80b1f0da184fe877570c752452a63e64f7caef4889ea5d7efa36c3339814bf9375c16500b21db6216d483cbc0f175306827a209d752c7f5dd7bbaa71550e787ac615e7e007b0de0f5206c2d68c21a4bbcb6b6384c42abc170c0099eb2b96b7433a132d329fc62981cc9fe776c8ea9c65234e4b4188e99963ffbdb05100e23f3e91a31ff2205fa292457af44d57f3c687f4274b8293ccab65f8513f7d8e0f68764269535b0450442d15f930df154996cef663e1e2c67e2d6a8884dc1f9bddb7cd22051890456442e85b20a22aea09a154f3f0fd2381ebc6f110f9591445c54b6e38a2a860cb157405f0d5d8dd928c90d1a1ee79cd8bc3ec09d92639071f15bfd3d30bf08f9642bc9127e6ca205a353759a3c0c638c10b52f6ccce1e565e3d29eb98becca16c7ab168161eba02da9d49dabc4a914deb2270d265074528ada6deb6063d54126d9126b57c7a145d7088ca875e2a919c567a2867c59c19f8806d625079b3564a7cd6d17a25e4863c350546167bf04edd2bbb5a3913d3872abc3105013e59b79697bc45496f836f6074e406bf4f29fb6b6d81642ea776d230af85d1227a76e3df7eb04c5ca69dec298b73e0e637d166a29716a91e08672843c0bc8f363919ce2d75b3c613fd1f4ae2586c7479bbb862cbfc8245a6cf1ccc32f879c35465b72a067be839c72c413e274da67a50e59724660c7a2ecd31150866d85cbdc16ab727687b06a779cd03167b7b9a95363a9bd1ecbd024bda5453391a46e83dc28410221298e40fe5ce13d2a2b9fac6a9d9798627713801104d4745e46f2b6c70b607b5ebb5e90a1c3dcc014cb3d3af1e35f70122a0ba83f4d1f49fa89491568f0483a5dc9794af09ee4e1d1694b652d03a550d7bd3704a1b46029da034842859fb2ef261cc6e7668ff4af77bfd7c75b9565cc2e55c3a39031987ba97cae077a4318795826e8f8b2f40ab21ccc1aa1f611fa5840765140933c0ce6ab49ab6d172009a5459a086520394b154b597aa0791d3064607e85020ab7c2521a0b6e1b3c46dcf52fca9d5cc4a188ea10442e646ec30171791c27a2db4fc686fbf4f22565680c068ade2926a0d22a0fe3f4e7ca46b4e7a8c45f7075b7e031199de3b6e30bccb55abfd32151d8059f618e9bfd65e4fee90f1688b3333e2efcba81b9ebb2ecc23203a36672165703a9f02e3d1803a382d1a650c22b92271baec7997eda50f5a1d68bba1827a5e12f468e53263f6bb50100b10ec1cd2efbe4d386884254a6001aca8b7fd2c6ec8c5121d1cf1e96fcd029e1c676bb01b016d4aea06f46c9de74235e5e369b1417c6487bc47041764f03b78328ca5cc4516e22370f22f2c1e1cbc9b8a1066009ef0a1ee05f5f898789ef94d02a28b304ef034885d377f249f3501c851c7900dd9c423f2b2070f076041ce926574829fcf3c803b1d1230327c19ef6d2a77e1526606768dd06d5d20dac19c6f88209288439365a07330cb8981dad962dd12cf680da65d9d8827ce7f6a80aa170e04c5114d7f19944c8070a9b88593c57e1cfa91e911b50a84d1f05f042f8245aad487be62189fbb19dee1c201749961ad54f51ebc635fec06fe1c98e8f0094c2da8c4256a348adc78844e3f5aa9e184e975f38825fcc1a382dd3a22aeb1088348715f33c90f95d6c4c1b1a7cfc703ca870067723c4e250bec4766dac3af560af9ff2bf617340ea706fc6e46a3bc19683c90025e032401301153625b1180d3ea64791817e8259490d933096fe7979af729a3654a9a4d7cf152eda7a10b11aaba6fdef46f24ef5867db48d827426eb3db00309888088dc2bcac27c521c674ead2ed26d974c2d6d5638aff435b3a28fc204c43f20a7b532c00d1139136145ed94197970cfd8569f67f1add390a252830f557a97f673d94cbdd4c141ae1dee36c9e6b6e9e91277f758e46372d912328a7110515634b7998b25469ee0621eec522bbc509ea86963dca7913a937e80a51643c5300d206352995b4bf96e7fc6286745cb0eff02a9112aefb47bf2db285c192f5c72445427fa074085e3184e464193328527ae89a9652d9847212b92a26a559094578b960bb51362d4a8e0a56fb3ced0d9325c2fe37262f9e509f36ef09ac0e5598fa3d9a4e7ee79db583c6dcb8eebe87c81ec72dbee01f339fc458f76a54ddc2bee4b44ed88c1eaebadb0edb28d06e3f4b2f315df2aa7fb3f7023c7cb5092f07c6de58a29cc752072515657bacc64eeab355eec7e50a1a59daf03f5e07b228f1e22100cf3a6237158671017307c0631a1985d76666ece99851712134b18cc40044594b970ba54e910bfeb63709cbd7cd92611b5ffeb533a68c017778e7a254ed62fa5dcde569501177c0cb1d61f01bf5c4f7b2d260ae63f5f1a6158b8cac78433a55cbb593abcc3bfb18838b38e6557588ca6a6d5dd8149ed2dd8e61d0aac5127db2b92989eecaa38a3a5d400556c48bb8ea8b39c2a0f592d0cb6bf22273c31f0820481442796ac101ed46f0833ba59df0c61271e9c3cb8193453602ceb8bb9a7286f180575565b2da3e58bb05f03f7419c12a11451ce16adaa7b2b9cae31f94c5376d760d604f03c8911a16e9d769652be485eb8a292459881206e5d7e0f57eea3531dea2b900d9f4937d5c5093e20943050a23adf78dc0be78ba47ec0b6d37a8e35b201505ed4c1e84a84568b6289b05a9b4b0cf364991f45b7409f080862ad38a138e01b61108f265a05bca5fb69355fc511b2a91719a781e5d2007902c361820cdfa50853533d15158653018781ef7bf6008c704f4c26f077590c5d7e92d262b84c846a0e12f49ac0f06287fffabb7f869663f0b46b818d38569f853c93e9d6a288addd3814d58019c9b2c069e9789dac069c3ae0324698cc2d0553ed85ca6161602f15d09acec00a9ae5e82b49b8029b10457ee394b142264ff5b668f31b48bf557187329ffdff89691865d86ba089aabd1434871c2b258f6dd8a5f45fe3e13abb898a2dd485da0ebcc14caf7eaf34b2632e0273ec0d5a2303578be84b1a05730fd48e73dc6c4a54cb2ebf2d0a7e1e254bbc413c5b364ed4b9625b0d82f9b965e9f8196d04b759e60d9d4645aa6adb9652ffb6a7a42e3a217a42f2c319bf86c0a9a8f9d543bdd31de01b7b2041bf908a5cf154b3cbb242c8a304b028f66096eb1783869f6ec045ee312c9fc99d5ac5c2ade67a37a5525b80764c5423d36509a5481084afe2ace02885146b69d07ca05320fda1ab78081b37632eb224502daeb52c402d6a471a03abdb2e1c95f620133e6358caace7d80b1ee7f9c87d8a6d8bed9aa56fd1356deaaddb9563c5aca71da80421d4d3ad6a852273b7dc4a4b35b2d22cfa9df794456542179dad1bfc12aa48fce1621b2e3a1761b2904ea0cfa5bcc22d36e4b0b3a52019add6c2f289b9ddca8091e1b9397ec14be8f876c9341bfda701d46ceefa333cf72a11867a72b44f41d79defc4a0f5014c700e2792b578efeb59ca5bcea3e359ac0faa0bfba121d3fadc18b9cbeb25fe04fac530960e3026ab8dc6d6e10df89eabfab6bd7ed5b0fed05c573bfaf0d228c62d4ecc080ce5cca8ccd36c7b1464cd8e32ce6944fe64e544a4f688d490ccb04aab170cb0ed19dde3d3d4aabd0170fa075c17ff48339971a1a66ac9ed6b3d25fa16c8f086d44d9e7afbb3f2863a64221261be4b9337ee39645546fceaa147a95d96475363ee62243670358363c40fa4dea74c95e403c1cdbf8c126ff324abe26928dd58a2f279dd2d0bdb2095b48daa17bed9f673d96d81a78291afc52dcf090cfdf76f62f9adf31b76e4d535f0ced8a8034053a50f271f28baa02e68d6d2bbb41e56d62a36f44e408382a0845a894b171bf5759c07c6491cb4e919083410e2b48045e403b1be8a82dd839793733cdefd3a1e60b8efcfb06c3b56c1ffb0da377c173a3b5a9c68c5f10bdd64cb1f8c8e59e4d315007825aee4405f0c0d710b804d20e2e8c489393a6292ac5b790881f35bf7707693a50c38281d4c17b7d8181c700dca38d1063e4f01ba682da01d1240bb3c9033cdc9f2227e34f491ee76f91b84de5e67094c5b9de11c3d941d80e81c9cc254650d3ac7df5f524dcea483795217881e7c520c19bb3e43eb9c0cf03266619b665959fa22df5c467df983949dbd427081f023801c682a1aaf7a0efda589b31ac2a9ba01a57e5537807379435a956f39037164f6310fefaa09e1b739468674f05001694919a3f53e3b1f230368d90af84d0917b90c2ce9ca0fbbfbc9e80d20a82237e22c23a07c90341de97a8fcac3e2654c8992b548fc620efc9e6e6989806d753fd0acc12595904a48b3dabd46cd988414b40da37b4deb6090bc744ef6bf525104b666464498fcc0ce454fed738d3e7cf1edd740da0cc1bcc333a8006ddd63d2cf8573a69ae2fdf4872b144a8c9d34aab2e9a6b2ee19bbf6983252ea3f7b5a19da05f41e9610646e109c05b73cc3903559769d986108816ebf5f754c72857dc9f892d8dd8ecca7f32a57d8c79f0322c9b4253f47f926bd9a577d64ed25e3cda635bfb2a9f57df26e1ca2539bec4097bbdf09d363408c089ec9f148a3db27200f0298108d189df8fdb246e714819a66a0835b561595c62c0754db94aa6cb86ddba76943c7a08bcac1fb88ae97a88853f8803857306a763df01c771389cec8c0f263923aef8e986041c5044b08034ac5568e61600f2d72611a6f72f98bd580d5a520024c3755cb546f45ea87a084a094074908130fd3ed227cb01435db8afc71c3c907d61998388e09e1a9e01b4cd325d878ff144c363d1d2e49165724a4c3c9269f7467e7aba1fdd8d8ad770b84c2afe37cc2afe1301c2edd4f87abb36a5e20f69b1f673c804cd9325604bdbe7aa53e278dee3d484bc46f44234c8a40a9b75bb44161e06bb66558c7d18614df6c934c23590ce3883fbfbd8240a0db49e639745bf67c76465b9e14c2ab270f8b5cd17582c02d19a706567a6560c6b04249f0242438d1d6c6ac7239afa08794d31824f72c4fc0308c6290d5a0bfa498bb87d130965b02784cab163654c4092c2c1910d7e8ec772b4d0482933d200d4429d6068a2c95394867b8c0c7203c21fc9e4dfd80e52f87f70697dcc610d74cde99758b6414583aaba469d07b45b689b5bc480f0fa5349a69133cde86f679ee438cdb12d91f61e78446a882d742288a04cba6271cc6bbf834cf4e28c472baae9593e8387ecf81cb9e0ecd143adb95c004e93548b9d9eca8b3b0b8c1ce00e2ef70f29b35bf492b6ea54f358a2d741a12e3c75ead8b27b81006384a68034a1f76e94c187ca0fbd2976c317cefa4ca84c25c61a5dc71a6f304add0071641db8b14107260660d9cdfb5ef61f3cefb46835d0fb34b1e23bac26748498a28148c8a6802cbf5d06144c548e8d6fd417ec464c7d027b74590662d9a003d23f7b18e5fbaa7b788b052a5d6581e4501bb410835a928e856009c43aa431d019ab7740a37f20b6c9ec67823736c1827c43368b780c3cc1138628941ec2b96681b052a51b07c915303b940bf59b7cd5fb649aecfd3189fa8a221de286932a80ae7da2ee7d23a5f11ff39648146cbbb9986c3d402447dc91c106283953bd680e83a4ba027c30b17e83e666c2fc77ef59b18e1f58711c015d7d61fafa5c0d9e51e874c4a7600d9e4bbd7739860605a649f7e84b21de5d3d9d743f9dcc47058207c8dc88f026adc4acccedba7025459600434fbc2ecd864dd00e5e07d37d1eafe20c61caf33934f762175e34432db98aaeac062ab0e68a08885c31d981447f184c4b868592782c9a09692cdfe04be9a34681fb5f5e728d0a764db2d3d51abb28e544b5c98bbf94df04647cb2743a05da533727eeef49bb90728c19e41e672f14d35f35b48a7601afc7780442842cb8381d06ce1063907184af8e5a78f180873f6381ab991d91ab94815b03836a491e7b357ce2fc6110cf28dfe9f20b491797bc78fc75596a031651fd32bb6223ddb431c024c08386e291e29efe015e94303ffbc1c87f8d423d59c00f8c5a12870610b1f3339334073fc7e63ba1a7ed825fcb99611ee454b1502208264ab39d84ca349b325ecbc25b3bd8f2c3f94366d3cf0c74b06a8acf1dc1c3e175cd892750a86d0ae3b8a0bde2ea3d18daff3b7d8d290b03bba6c719c1474c948f9bd0412f70ed0e6d3df2758b0017665d5946c8d5ad7e1c0abb79897d45b02392b8f47205bdb9809c695f4c3ff217007443fac325b9e903da8386758429ce904373c145788cfe0797e25a100111055fc12c68ed89f3b198cdb138f7fcd58dbf131d2c3ca50a5a5e9134a2a9f422a209e17087e7363d57da01cead59ff759fdc65a3cb9d999bf5cf81b35811b4f3e8d378e4fe676ef0d0e66ae1173423b63ce9c72d1a98af04636626191a3b67fd060e00ea6550f3f11271740147627187c430ad4daad6d1144aeea93facc13a13b87b0b7d44a974d4dfc7cd4ee159d7709d5bbfc9d3598e19a199ff140329bfc6cd226d1d4508d38dfbe05690a33d2383b8de6030caeaadd7565dab8dc81da483eaf09496c80a514f476c4dbe1a1defd5f5725186a9ab50d7d2be73e1eb6d091d874cbf956b4410019b4f5100dfaed5758c5fcd4573d26e1a5899f75f821f69fd6810b2cb5b83fc3f27fdcc574a4c60a0c5d40a4a7a610a060c434b3c59361dda287b1ad445d583c2b134eb77dc068d72666d562feae245089c067c815fff52a15734b2a0597e2162d4ff2446c2d688c28b64abf8bfb0148a4a4ab18413ddeac0162046a0dfbce9a2df8841e5f495d244096494ab6147e548d097396472b812236e80c328094bbd347e6d1db0da6de575306be64e644fed38ff48f1b5b718d9cb1de710008d6665ab245bc12687f0480f353511f441c1ca017b1c589e2b2921f6ac4dc15d94da226ba107bdf5468bb55691f866265728997167d6a40fbf86402ea17e768f81c8a3dda07a69d47c75fc4416ceb926931b2ff94f381026a1a697a49d3646c70ac5aeec220913429844890fb9661da555f4b3cd95b1b39e2fde8f5a62b0dbcd22cf09f12a4719fc2d96297dda37a34d9fd9d2c2bc7f2098c96e8707804d1178bdf9169ad73a127341dfb4dda09072e872bf85defaf3bcaa698c7851e8d05ee6831964472f15f578f7898a59ed78749281171855b0306459a464cf32fffaaf1ed3f8d73680f585e5580b28904589ba8d2357d51030a3ea184435d5166bff6a8e222a2d510a72a83981207f754c86a4046a3b3600ed81c5f1abddfe131e595e8842ca542332812395241e42e44da37a43d0604bfa537d5925394fd89dd0ccb91011da7c7b8a1c3da7714a235b8603093764ea1797bc2a122ea6f9119fbe9adced533648b84792fadf1ddb7f8d78e9f852653aa82d497fd25484dff99838fd51e16f5fa0ac0a79e6dcf3e8e5808e5baf7580e335107a95e013b42432838a31e3ba5933894a5c45892b6e9ec358bd704a7801a5319b92784fe9b19bdb4f66aca7f9b03d451839369b2f55354a61687fcc347819255128c332d4f9f43021c20fdf292a9ef5091a8295f791e87a394f0714049b8be5027b467e0891bb5785e97b7089cb5a3b056cdfa9f4ff4f59dbb7b91bb8f59a173d343068b4706a2db20a7972702c32949b498382150166d3e438a65508de1cdd4acc46a862593a8b204784bd00a3fcdbd72a6d29b9d88208498accae59cc46b3e36dc06289da541f6d19683e4049919abbb2a5147e707bab2a75b150869bdbc3fd3e48420b4fde4ff4bb671d31995ac4598e48d65cb00a9ad7c90d2c04d32a47701015c6ad2e88869885b1f2f7cb0aeaf0d3c3c72284d36194d9dfc9604d2dc3ab898b59a0ccd39e8140d3b285b7506bcc7c870cb859fd5a1463fa22d7d2ed17b0b6124dd7ad109b627497ff0167de732e7fdc1d96d9887bc71da640acdd9ee2f21c3b11c05778d0865b49169b4fe387088ba9e2e79e13d337380100e468f9ae0738727ae63562359f6765ec2f6d0b980dc70ec70067998dee61486a2203b602199bfee5f9a9c7a806626e940f6069cdbb5898c007587fb65b804168af9b127efebc40c7c3e0f74983e7f949c4cf27707ade4d228c7f16ba9843e17ad7e2b5e817214e5bbdb5615c0c81cee453cdcf5afbab9fae41b5c05be643b04b37b3c4ce968dca15d409ce4b79a9aa030095b4737ac25428f900e80e6029debc7fafc01a298742adff652bb1a9ec85ca3292b2bded4fda16d9c358cc0c7cac58181fe621fda726f4dad390c898f52f753c0178b1fc2550fa0018a539f808c1fdee250f1465a439f01fa1c91db79e40be1c4dbf92c48ec6027384cab9d0e238ae003478043f344e695738e0743db8b6e6e65e50fb40289b6321e6dfab901febd126e6c8fa1ea55a3ed57ce5dd024706756c5ec4789c08f0da5c40d413402ff37c62d7abd26bc1844683222422d169ae6a2360f520de175cba89c042f9bc840cef159a4f811b269fb89313a24ee3abf5499a526781c50aac94a5f7dbe651461af59b686c15978e58b8fecb3045ea121c2ccf75225fa09b46651c4efeb3ae018b3e6ea5f5ff7e40400892838f539d85c9547666a2e38c93d9cfac28eea961f6e99b33cdacee5cbe630ec8148c7a96013040f7a2aad180826531c1a7e696d7b4f4c6f717dfef958a62342a9642a9f221d5a34e5f94b22bfd4a3e16f51b6dba450dd438a505e7500e920d879540eb430a277a7ae6ccb9bf64975e319d5c777691e93239d936d28d721f70d1b29516112a7285f43630523954087da111a134a11ea7e28db32dd3c6237c061af28143dda6b53742c470a183d0c89df1f2406b50e8a3dc58a10176a07413e9ea150fcf5e6cc784da3d799ba63f93b01b2f68cd519a34c8fcb6abedcc4ae9b7fefd99790bc1cc57b904abcc12e3d377a9b75d34998da4b5002738883d23ebfbdae68cfd338ec14d032822f3e3f74efa6e0b2522ef3063a5e227b1c23e03d6ea589aa56ec8d488dd6d7a731b1331323ad0313536892a53da5ed4c2c3249cc9ad602dc74c3356b480c113cf796e2b9daebfdc6d9782053cbfbec8c739ac52fb821cc856a7777e93b0fa2c316ffd7620771cf7473e92885ce0886a3a3a57bd0e1cc3cc76446579cf4c6457b6b7ad39933740e2a94ff70b1ef67e1b8264d3bc7fd65092b60d0e3f04e7c36e0cb347a28fa4d86ed6d4680ba0b9c3e147f84336cb503ae19d3b71525c0eb9f59270df580e688097181516397032b70de8c9c42f4d0edbd8649fd21aab5d91b83303419f28e916fff8cb426b8ba0d77bc50a465174bc337cddf75dcb45cf9079b8ac79a3fffe1b34786d8b4a78309c82f82232ed996c1b44a5f132dd7587a3337989a6100677a93667abf6a2171e0a47f49a5a79717390db273b36a74ad6e74d93823b5e4cd593c909b98a916e692f73552c8f026efc124e45508b1b390564ccabbd87988069e8a2f41d1d392a3c7cd65876431c751f88c1e91e437ccf7f372020fa7a25dfa95d03c9e39374d1205fc11501ba4f02cefa6869ce0ddbbf98c66ca053a3f11a35b067f524584c0a5cd6195d135087ab978eead8cb328ba7d758c86117937bd01caca818fa283d9d2a83b8332f13ff6eee4318ffff0bf007f19a89cd06b9eb06782e738e29310f3443aaf190c3b9ade2ac293deb84f1e14cf4685ef00bfd36013528300ebabe5c3280f6dca945e1a2f6201666c11b498546db1df65a87fb88fa7153abbe7bc4998fc0b506d2fb972f8246bba9cf69b7fa3171cfb25411706071b1fccba36fc154e100e2ec9baea012d42de5eb2666dbecda8d9bcaf49dc7c5a983faf42d651aa95476895a2b480d714486c19d083e897a9c405993100607bceef9a69cc72d07502584cc51f46501f825bf2dd6fdb97239d30a69ff04bcb62087caf9b906616c01b14a1174a825cc7a518c1d4ef051151e6aa51de41ffe3ccc6f64628591d0164ef403a34503a020822bafa3be32852f9d1ced4603f276548d1e0f92c043e217fefb4e6443e10b082e3cf8bf0e2af49086a8a5c1559a3ec50a70b479b6a8917699710e5f39d2ee2122ecd99800ed1d12848d8d21a296d0698b8d8fa209e1d74201c8074af80386938000cc63012bfa338184392b126217210b1caf01c32c976fc722babb25e9e549179cd3673630f892ca01f992706ddd25406033681e6be2e9c1fef815c5f4c3a2fee0256a5ba8f7df9045460e124a7b0d4f6ad1d5d3efa4d24e1e046b8cc7493ab4dd42dd228d298760175aa7a462b1deed2bf43aaed9823993e74cb7114b064f2717f455d7add6db24ec7a017a51c0e2c62bdc404616ae7b0b121d5b6cffde4446f1d560d1825158a3c009eb82e0ac922a6f49034c7757f0ccefd1ecd10db02c364d0c5c4afa41f4139f59f40a45b3e9ef38f2d71139bd1a7bd1d9aff8cf8ea32ff551110b2c2858831f360a56747fb8cf1b53236ae922ec22980d89550bdb0cb83b5ec512719c0e30ce7a9294806642a411d7c17af39d7dd0b17eb0f3886c8efc873f2ea1c4bd0b9fd7d73dc838fe0fbc82e69745f248c003abc567207041cef4890d54553e6c49f440da33f4d50a0dc888235a67b687128cab029ac92704c8bc804cf0f29b25cdf304f470002fcf24a13c270a74031394c49c775f92538d798c28546481d5c1fa437d12b4ea7d07a0a90862071d51bccb67e596102ba38592d126c72f332fd7824d1d347a377e12d282dceae3ddab1054bca815d613ef615d68b48a8214209d8c394de385332d4cd3a4f9091ef5f5bb69a1f70d235ccfec7c6f56933a45affaf333cbdb0b70aa3e999d65e874d915913d3395ddcf8fbc964cdf0f31dac4927d64bf751b8e5055e8963dcecf78328ed2c21e0b44995a3e25a5baa93d13785e0438db3a192439dc46439c94aff141269a586b0e4ab974a0c3a0088429b192d5a9f5dd9ec3223fe264658991359e7c81cf505314e1c48a1eee06c70e433eba95256c061a9cb5bf860f7b996c9b1c44de70ef749720c18f6a3171871a64ce2c946a994a7ce97f0ed441e4633f93225c7af88fe0c4649d355ce9b87881a6c680c730f75139f04d2ac4c6bd2508bf3b4bee32da3363765ca8e78142f7ae87baf0c55c542241ed137dd7ddd60cd82d031723dea0c8ec43b909b5c02262307f3e92fde02a06732d5d0f47c2b89f6b08bf2b0905502aa495d13640588abe59c2fd5982901351b132dc416bea2a0e063afb1e0c346060e89510de2e1508bda7f58d5f2fd24d0967252b8af299807ae861720e7b5f726316fbdfc077ffc42ad44a518a70340222ccbeeb974143cd518a8aa67bf4582565434bd15f2e5c3a62ac5c93dca20dbcea0b4e191c51ef4c90c4005b35ca3c031779747b1c16a00dc46a0a2b6a595cac535c2cc15da53237cca7ad5a67ac1e72cc7f576421bdcc28435f71e5e98f69fb0f0cb5648e65630701bde8877b1dab4a613a8bd3b1c276aa7b6e802025109fff8d9a61ade05003a231d2ad169c500bff187ccaf90a9db3f14fce710fbaf04a50349fe6f2ada6d22a8c26ca959c23ca56b977abe856608069af6868f9296a7479608fd14baa30353d01769c4b816f9e291f32a0634ba7270d3fab976583d86422ba2a55eb0eb4375903b0ce894b6610333d80c8c8a0768161e860e89563c9ab6985cefd9a3c019786bcbfcfcb82fe9126f7458d30d2f4a631a9acb84bf9e440e9128716e2ab7cd4a7e1ac0c983285602ddbee392fd74b07042f70ef47947b8bbdab959704f325232745c2f3a1ef85a5daceff943a80840e16051271a3a01d0a6332b5877adfdec9be442b426afb3dfa20edd57ea4e8ef51e171cf0dcd2114a2704405d8fed9b22dc938291deff9404c9911a1dfd79de48c3453c3a3ded00dc2f21e67c0bb25bb7c24735fbee7d8403510a3e571d1ecfbdf7cc319abd115c1a835dfa98e9ca12614fc4c33fde3beed7c6e71a09d8b2734d062e366d6f9097e316ea0a863d9a8afa718f9c7f892a4ef1be5ea9cf1082032f384f02b826f045a1aba71289732e7c45948eb0f5dfb35577c888a32bb454566a300a4e77c473510e95f9eced25d8c1b4b9b823327ab17fb074cbe328a0ddbca4e3745140a04dd4f974c5e853113d6de40937ca918887d8232d86aab0f7cd472211c028eb64f519dd0f68c52bdbdcf6b2c16f553bb7a589916d66ca36937eaa98922adde23c7a5e13f760fbaa86f7cea5940e63784d8dcbaec597c031c8607fbb6ca904ef6f5555ee9ca8e6a498ae25c87c7da0e83c40d44e8316de0df0f1c45f60a3f1a6953e1c7b304414ccdb4d7f124b83b8c136c7089dce6c89805473f28ade0396bcf625c70277aaf8725a99cb9e370ccccf885a92f09f50fbf94d0a9f6f8c3cbd440efe7025fedbbdbcf2fb7ff0f3bbeb86c6e431f3c64ed872867c54aca682240bef7fe3df7c0bdb9fe5023b2e433aaaa760d6830da1cd8e2b8e7c3845d5fc44ef5db1c730c5954381994781ffda95b1d862d04daaf1c0734e608443207b06d34f648dd7f9c6327882b479ce2e8c1d71fb5d5e7852d46d4442e6aad072792eca3b83838318e8381b6ed2446cb2905eeca170fb3e43d37e180f674988b9b9b0e83d667880d7b754574d9fc385d22b64cb7a84852a4ef5b81a4f88de3a28e9875551634fa55a782be130c740758e0b47507eb5130fcbdebf82d64c724407a2b860bbe89b7f29fbd7e2495044b77974fc6b5b56991172ba3ed57c57d23e83863b345e4c4742d86e1c0ab5f33a397d407bd1059555e0b595bd9274dcd09be702470984bd537593d7d1d52fc8844acf7701cdad16f0f63adafdbc50c9d5470662026f71d4a92c41bdf50d670bdac4da2551b0518bd4ae06499d890901a112ffd31ffee6563b0585563be9c093736babcd85b2ab0c692bfa84381520c3d15c0e08cb06397ae7eded311c7b1c2a6e9629619edb8c1b50ee618230376fbfd61521df5b54bb03ec0daa06db5d8a307d78ef71c5e9af6f63ea3440daf36d986a1195955f3a65c79046002ea3b898957f17a25071b94325102134cf206ca5394ee068af00690bccd8f677973d9c239fde484be1de2d68902b5a70959d75116cccfcebba0cf232b767df82bdaf0109866ddd2c33b268e76f511d2cb1bd532ef091fbc6bde986228a37c2070f63ede118104e791cbcfd0b91e85b3ddb92575a6214e74ec2680a02bdd92f45c037a8f265ca3b5271facd73e134f21cb2fee1b7c0143188de346037fad014ce87ca850fd18b4948616c89c7e1d635a7f73f354bd1019ce57fe8ecf98372f056e720779897f08aa0469207c5689ea0b8e4ce10fa8f5671b6f3f593124b85a86c0de8337dc85b6f140da3947ece95d85cde87e33da8e235c3dcd53f4048fddaf4cb948189f0a630fb1e755524ee5f32b69b626a6ac15f6624e5d8bf9a88c775f2efcae00916919768dfe440f2515e7689877b14cb6306d1da47035ec1c570be13a2703500e6c117b3cb9f1539ffc0c6e020ec1d5d22bd05f6e9848b4a47f2f1a87903a9e7dee161fb0e42507e22e2adc6a482e9e0475df82b584cfd1558d935a913c41e8ec01dede4ba7d7f284f4e8729874c7f51710664c2a26c3274553a28fab4b1ee0c78495aa1538405cd61fc7163b476c50fb8de854c73605b695db56e5e88221b2d928c776b574d5c4d540f954315655986de3ac856fd0db65a567ee68d82208c41e8351d4d6ce3db53ba7a60acd06170cc0a13206caa9c2f406e8668b329f1806f5e07d90d92476bc5e14e7965ca0c9987af339fabae8b926062c3d009091f6806fa074cfd086790217a808ff7d27a0bce4b389ca0afede990295f76ed77a42a152e98c48e79ef75d098926c90e5b59b523342f83b71f2666ff0a862c320a13be9c57c0590250d418a03dced71fd932bb079d22a2c65eea1f399bd42f9208b432e061d63a58b704ac3914e56c33bd6a470bc75b502100b7c10ae854586a9e402d525118828d2a0a607c0ac34648a7deb16979eeb8ffc03123a7598b92464f588e8d0efa3d3fa5766bddd7bce850a2f99ac7f717a298f0b5409f1ce47b61c70e0e2b3c1c5ae31251ca3d53426caf40e4aa55e19d6b192458bc84aef9489846069183c34dd8d61fc220d6348fc4746b08c506a69f2c81728d7f028560cb7d8542fc3134dd030e6dcc79751c61392eb0a2f55e0c53ed046d72693b0db788411c10bd696ae49699bc1f9900fa52cd81280b2132fcd20fffc8ff6b60b63488dde3b24cf5e22955b1ec94c3bc1e60f496722f013c4d5432a72c45946390ebf4984a5c8351a1373c31317c8e447fcf9c1b6ef97a1956f90e0c31bbb213e935990d040724c4817b73218a6de44babe3288db27ac4d1e1df10a8bf68eadab6d8f7ab302a5687177f5108f268865939681f918e903a6be71f22531fa75fcec9049dcedcb25acca4358c22caece2997fa3c2eb6f1cd74ffc7fa64173c30e19048ec5cf0d1bb3bcc32ba4cd1436db01cb700a8cd58de66d35d329cfab733931caf2a9689f93546547246491916800f4070bad8c120d0868346c078d9ef09029b8c657bf95234fb6c13d0963e5ad6968b0c9226a068b26dd37038416c48c86fbd340f9b21b79773420923aaab5a53d15046c57d51f80b4bffa9c57b45acd041c6e8a1deee6d204f2b42de3934c10ac203f9b585517b8714528f35c03718c7f692432e109e36b05a1167ed7cdb639a6a1a369c71548c670e3a00ab86655fde9c64219bc66f350d6f67374e102f622474f8718f5d2e547a96cdabad727dddb3ca8faf4cacb6da4b0e41c79e3c73eb28810d60b2866a71d46fb56031257fd976acf0fe683ff4e3cd76760ed201e947280407697ef78f4495a2e13559afa439f31e0de8a91f3b80c68641d02c8ce4780477873bb2a138cf089c93b864653959ed555ccc07e0cf731d73bc29870a4fa4f90dcf228b97bdac6a067fa6633ddcf6eabeb1827f6f94de2beae8ad80479eac3c97e3090f97428e6ccdda8f70b4a952d21aca8247b01a4ff2fbc6306833a053737d44179d35627391ba4cbd0dc6b58e97ca460315bc0c9841f612dc18bba4a7786f65091c7b31e47162171e2c18ad44940f93432a1d7a231cb34d3d060d33031e55ae23820b636885e538753e424ea6d6c491dd425b60c92511c8fb77f2e6e33ffa375aa4ef373835f60610f1891b871e2bc1de48c64ef42371bfb8c6c0c2544f63af45f709f3b21eea49ba8bbac93899f340d94216333810737e596a871f2ce3196c9fef4439b0288e8d2f4059c97c0d62ca516bfe3a36e969dc574b0e613c56f1d0c9e59cb75200fbb46557846001e9185da6d52b2bd6501aa3a21c866bf5975553e41559c8c3220f71d6b88bd0485bc2cc0fdcfcaa469a9c7f5e99a2307998e676b02e584e9fdb64ec09c35393ed60bca4126e7dfaae072d2a45b34f143420b1c47787c803e477582df7edb747e592b600e9848e6266848f8e2a388bcdbe763a827f2e3b74dc39fa9dc8e6ee8d08b131eb1e53d541b285574777182df7edb147c19f26d55430c5934e1115d93e65a4a53d77779e2fdfebb26d0d32c69436d28e151415c34165ae6efb5e9fba37738c5fcfe37a6b3c77a0173c8e4f85a23500e9b3e2f2b02cfa9c9eb6bb50039317d7fd61290d3e6bba2a0e40b1f098f282b2ae86a448f718c5146028c9896d25ca31ec8186596953ed7b972a40017c9d02f47a2962e3ce208164ad1ccff9c41d883017198d9f642a06fc7a2967c78c411164cd19b7e9c51d8a416e28059ec926eccde6c138f7270b6bdac7b676556c523ea940872107789879ea5d9178fe4aff124f3522730de8c47354aca92c29b5cd01feb1e1ec5f734099845f00a9f2cbff64e8dfb96dba02110826e6fa1bbc574d490965fb952395732c004ddfc94c242dcd4f9022394c227d0c5260d15d8b6777c900257a8042aee43a9c105c7953304b85f0dc175e63087435a114c5763f1b98d2bb59450ac9230278f84399147e336429e482a8ea9d942b03c15ed82e53908fe30bb26da5af102707bfaaa93ba6c8fe2b20fe6e29b84a7e41ee111d82f1b720e012e9bba32b820fed759204ce4ea8a6027dce55acc2e384b9c2b9a7c12fda5bc125d9f8840bef0595fe3c35fa4f8fe40b6aed8519b9246c02c4a410c3d78d35389de34146a5e56a2987fd2f633082a8cb02ef92e7e469c2f06d1339daaf462fbf17f405aa1131467905f353ddd1b63f06a80d0d3e3aecdcc587fccab1f392fd76538e781e8970a6b29bf78b37238c0208dc2c665315b946a00262606d32b24c45b02a5f33a058de800d0736e1f796d0e568cf07ba74f007938570235b73f6145b95c93a99154c78ce9f9ccfc564562d7a8cd0744202a56e30699726925f4c82f7016e6efff40639068c69e17478c9ad668181ee6b0ea13af01db4a2e8c7b118faf0ea58146b00423f1924541298ef624ae1d1386635f34c5b0c4a1a95c49e830bb2a55661e863942179553b6e183cd944a0aef45a931a636b46b10829edd182b93dc5139d8f82896fc21520f639090df8d867ce9acef2d193c1988f3174d128c322fb45a1e5263e5c942644649b0e2f617a61df1726c47a9781d5c3e1fbce54b90d5a1842b2e9a893ecf4019b624136ba21db6f6b32f335d08615f7d8e9c84c821944a642461101e27077e6a6ef08afea3c6c929dafe6faceebdca50c0eb36f77fcffa73e3ba90c26b32e838ae0dfeed9a06d9283ceb73abd5e92c0e32c997703dc61a36d4f21ce12b0913059ca37c537f83e685128b861d0b44f7a7a28accf4d65eac7981448cfb39b647d68b62683d02990f356e6d3b9ac413f7ed44ca54fdb79ee2e3e344beda7d09d8d8c0c604a806467112695b97007d5e5241b79db194b3ebb0df0d2f726e360ae73c564f2d392e49933c969267409f5857bdf0536cae9377752227ca550f0324c886bad9a859c2cda9bdb48307508e8b795889b92a9548b3cf92366e75f97fe81dd995d2c6b1fd8d13dbfbcde16c67c5c08c3bdd53e554cfc4d4207cb13cc3f87eedfd9a0ebcc100fd88d0c17e7a95506c4a20edbaba14d7b5c049224713eda850efb9bab8083c44027a800e648563fe8e389672fb001d16501ec3078f939991e898c8f4257ae7d866e773b170f26761eb2cd396e230b6e9d9ac032c5cbb6b05e86aac3e3d67f368c670d8c617b6f517a318e209c75d4115592349b3d12ae0615ce975d7a6be8898884c75daf6614f2dfcfd878dd0bf8082c2b69615b9e027a16dab368974c667d79992e97fe9868b5ace4ea850ce0298a45e952c37a1b6d46ea929101acb9ecb6167c4e6c68f6649a5fb25dd66fcbf912b6181dbc2038e8e896a1756066964a39e63ac6750fc46720ec5bee0f4d58c35f07eb7ffd787230aba722e9a9911e1c5be4aa703ac4a09448c25838efc079133bf88cf4a89d8bc79b14e8fd663c2458c8507a90a88c5145b011e908db912a3932f25b084be6544237b65deff427d702a46953a3fdfa7498f53b26546f8ab1962c94938bde296cb9cafa9e3eca7a3d3bd23b8a19ed05add9a5ac2f7186eafba6c86c2da31b6246dbf06079571b4985a8aa8abf4d614d5c9133ecbd2a1c7aeffa4e7718cc50f4e5def9eef591a43b424453c3b321885264740623ffa680b83887505201b73d8e393fdae0656e1c8678ec499e9aa122b841189ece3e7eee2a95671abd48da11b0106f18aaa7cf7dccf667f10121ace4ce3912632cb9eeadf9989e44f4352aea8352485e2619994703ffd87985694e138dc6dd21a3900e62f3c7460dc66d2c57ec5fe88475bf8fe62c7d0a4beb9feec63428e119a16c0a16fd4cc4248877f3280d83e903596cb188e38614c45f90e6fa47627675d26438667be74a8130b316e6c524341c59695e14a29daf8ab9cd3af58a6725369f4a8cdd1ea7441d387e8b02b72bad4f86727f8dd88816660fe28d3da8d9b6bf8406f820bc9e6158c113a472e16edb7d799e5f2700518661593dac5b5e26d1198d335f3c263996b7aae3867aa9b20f236e25d91587a1e345ab3d238a5a78440829d8df37015cce330a0f2a5eb04882263642a3ca0a042e81fc09b1887c1b6301224cbe5e652c04aa4f88be5768c6e6c570e6be2ac6c9950692b641d8ca89711f15ea4eff2562a6fc16c126493300405ef3de1829698a595978be75ab60808bd78d00142d74fa56528df0a6ee4bab3f3e56e7ca9f9b0aceb0b5afc4911acaf8d7a967babf259f42a6f09038873efe89d72a69e4000b3d2745c109e3dc18b8a27548d4bb5db627ecb62ffad35dac33179847fbe44f147354f9c6f5efc85cd3df1a740acef782451ba136be927ce2e05b692c79308f4c896439c30310fdcea411256eabc7088c42ecf845a0070f3bc1329f68190ecbe66fb928fbf1237835682638749861bb84d786c44905840e6add2af61745e535dfd5b70f069c1785985633efc62a16e46f2d9796fa066abc40afc512ad1acf624559eaf3344d59035e375bf25112988a4cbd379f399caa157dce22dfc8addd7a9f101dd91bc5c324b9ef21d8e098ddca52f49ba588d96801c323a799e9dd79ab8774f31dd901a33769e6f3546785752b526b18940ae49fbfa597608b97d99bb67128f5b7f782fc6313d8f826f4bc344b40649a95d3d994f0949d03e6f302401008020cc88084ede545b036c0d54285ea03c6c664e1b2f80414e749c365a9e7c864817fc8ea2f96aaaebae4f79402622c99339392489c2681fbea9d944320344c29511f64453661a2789db5168d949bca67ee2a4d6b34089e692599e5148c15a205ef20285386e8a5b20d1753aa235875aa77abae9bac99684b4049a8eb15aca6a3e98b365355a78c5d1c1ac5b965dab0ab86b60a2a67ec1aa2a2a11edfa6e6c9691cde74ebdbfc62a90538376e6f3f93dff51821092434c214b0aab3f5fa2440671844c6104f0af92ab7dc42a4e97f3510fd246d4f211936607468ebe1d968e0ec1c13b0ba01ab780abbc14139db467f141931a5dc5eb440fbe312c865c38b945fba8c2856b5ecf4824ad815247ce62f3d262fc5e702984ee011ce0769569d949a740e26d8a3053204fb9d904a5088d6a013fa59093944ad47d308e55f142e341c578d3fc809eafacbea139e3c6bf6ed656feb8a1fc04be49f3a869ced72bd9a434e540633ed2810cff176e37b046caf6bf283e24c5ec9782a6c9e78677a0c7b32b24cc789b09b265fe9bf7f441ec7d9245cdb9220b917f0a46c8ec777b8ac8334a3f9e47c27c6579a992a8370eae0512f550a477b43074d7376eaea2441c204f161bd86e9318e0ee12537900ac227510212aa0f61a514732a5200287ae307096fa1d48c92fe7479677a7faf83e7c08bb86d2b74ede46316ea64098c5efac904594524d496a7d6d7857fa6f4bc62926914da4d8a567de0c6dcb12c2a72185df81f009323d4a8de1dd66537ad1be39ad4d90bfe759a5247da251e194d55914fa8a5dbfea78a4d6bf395d7a0fac822d87dd1434d483b71d9c1d552512225869b35f6c98af0364d3e8578fe03aab0fdcf0127e6da132913a506f8a4ca840dd7d02d9984d31de7756cce81930f01e5151a0e2fde78fcc298276d60044620d382e398dbd3e713e5427bbe8dae56326be7911e02640b712186a4d8ceef2fd4777b02abf076a5e40fc5bf76d6f23db9d5ad5037d7b3c852750d73ec6b80f5b81c21f158049e2cf1b7817178112cb6cfc53006ce5b7473efe8ef1780466f7d8e2337819152f8bceadcaddade54d03c21f88dde2a0752d603f0396a1b46b3cf1de5ccd64ec458f29e7a637066b841be1cfae0efe818d2fb01354d75e42bf9b82c3d261a53ce7335480dc036561fed3d3347a86883fbb15283acda11240dfb1cba8db9ae3bc49ff3bc5f4500e917b41a780694373599574b604a2ec55ddb300144e9ce5a472a1156a5984e19ea97e89ce4562da35c504e635c5db734da14bac79583d98f6239167330203ac5d7915bbb286bec9b493a9b613990a3b3f622e8b23f885ebfedb042de4f09758abf47861305243bc1996901c050f56bbeb48db01766a8e87e9b93017368383266b5520f8212a2199e4ae1d10f1d948e0f10b20f97e7848b8708bc04d4ef14e4bac33791581c37e2e2d1afb0570fb595b21905563a5cc1859755748e45ac08a10798ad6ac05b461978dc8c7d58dc8242ef8197bf82389516e8f8afc4acadc92dc6e3000c46a0152200e52aaa86583c01fe5dc6f66d3831f497b6245e545e7ad3bc06ae1c82ba48d7f0bd1ccbd2172b524e3274e0344c9274575b1448350d0917645c83ddf8a364cc4cba40b6d889ac32d177e138bf7cd45071b5606f841150f551c6f6894805f4340f52666c7e7b907d2df5522f0e288a52759b401c0eabfd0b4d0ea11917768313b039023322e9ea4f54afae987ac5606c4c4890e5e386a7915462663f79a2d67ca957e323ff60e38b126c2147df926900cff1be6f4f6ba0719e91119341d01dd311feeacf873be5b12d57de1b1a614bf168af20663a5fca68b2e909828ca1a28031df0c5a0ed12d7f5e9c040a6bfa041045cc83ecd4764d2b7b05b414af1764cb19d170a45396ad12c5385196984ed06bc894319b1a3ec804c9b90277f3c75f76443b75045e3d84a59009ec359c0f01d3e669ccd8a07d13d4e874d8500040e69c46b0e1e2b8da3abc59aa9335c7692e0d32327be74017c60b055b8b1849b3e5cf4cbcfbab99cc7c7cd33e66208673946a5a1af0ac503cd60ff2e47b56594fe5d03a38ea7cafc918b5dc7a2c5ec2c68b1ce87456bb3a7983e5d6c8d0e864970dc2653f85c9fa6b0a86d9ffe75bdc8fd795ee15d584c2f718bc1cad55467bfbbc75475bb9700db1e47a4358d44f22affb1b6e199bdd35e2e5d5b70c53f9131c3d2f334447caa4bfbceb3532aac3531e96d35470ac5b8604bcbbd39f51c570e6734c0647a5b28ee2368ef090873c139ebae4d96586ca3f0fc225a3c21fb71be6fcf275847dc87549053d17b19804b06ced245c731e8e670b244a6185017fc502836aeaa52a9a19beaa98153189e2a221be23a4fedebe55fd4272b5104248eaf8e952c4a2c8946d4afc51285eefaa528d586f7258954134bb40867415707bce40dcec21826571a0d1caf37d303be069debeffbf71d13ee8d1b179be2622c4013b9a74a94bb6a3204aff751ee900306a0444956d1b70526ca5db38bb181f7059f20fbf8c7c5cb38e76472fa34b389379151dc5bfc8f251e156498541b9faa57c9d8b04a8762505031d9f8757f735fd681ab87324b94d2f49993f5ba8eff197d9383d7d1cb00db1abc3def2a4dccc8404a26ac18265b38bd0f22cbb847767386f0f5f903bdb3482cc412c570316873f11e99b1c729bac45936cc135c9bbe7882b5c20611cb8b25de99432e1775e30b14cdf861e444973224c3c4053a77463d05e64d24fab88177e1761965299528dd27020f1fb171722715dd018e0fa5912a4feb6bf62ccf1728822a15f245fa8f09e06744863a75f48a644b0306fbc58041f096c7480d51d95ba672710912a784d6b964eea0d0cde5643b7b1319e3b72cf9d785a99759c626b3ea4189ab33dc23bf9a6111019c70e11ed863a99aee7bd88e41fe1d080f8da3710037c4ddfb51e31e194096e0b98b6429076e3a51a256076ba885487ad4b9f47e22f1422568f2f899bd492ce049f1364ce797c229cab135008ecb47e5d0983b4e19efcf51df27ab5415511a40e2c40a5113708addce811b658d07fab9f2f0f3e98f36e81df9e6e72d82869be261fd8e783c200db00cd2606f322580d6f57149e7203d7acb487a6ef914de20cfcc9859c45d28df8015d55e4224653343e920bafc58389961c27ace7871601241ce32d36890105b6b7aee88c534456611fc722714aed58586bbd658b71aee4203dd6ba0bbd448f45557a01afc547894cbf7ff0ca92880c09b48b698c6d5b6987a75f48bb7c3397581bd0f7043e2c89360a672f3c1469f3a002c3541c93ccc2f53899b7edf39a578976796da7d88b574fd683631251c943d1f02343cf1eded32af3c980b0ac850fbbbde2b0ff9820428b83ddde7956fb6e09148fab4fbb400e8ba27e7d92545e50db5ed29cb7babc73aaca4825eea7aaf5cce4e0e7ac598cba76ce3266bcd7faeebce3246728aef1d7cc5d058de5aeff48b5203fb12d52f94d35a83f704bc62d428cf1fa20310aa57651dc669fc3e6c97c626f831dc88fa42aac0bdca487e13cae84cf673a149d88a8e742ee103d8355ecc9fef2aa009d3233828de7f5222cc56549ee7fb499eb1240450c5d0c03c1ab6aec7d3056a73f01ee8b5cd1e9120f14ce29b7fb4069e6f049edf161ffe342444d1240cae377090270f344b88d55d1b71ae49f4fa414ccd43f29cdd932fa641224e72d498517fcdbd53dae03ae8ad5d026f8f8d7c74eb080b6fd7c52338e6d49419aeffb6410e6b5a37d33a6c73c28906fe2af7e2b320951199ab82c406561818671ebefceb378b11d9e5a8d11a5e13a3b24003798b0a34a12611f575c451e7c1f228a0bec869cb867416c0f3bbe62df9693a374037cd41ef1b890b3f8a2287797511decd37f826f9440a2b8ad8dec2cb9064dc6c730678b878ef5dcfafa86ab037f0e0e2e9bf518cd5fc2e96249f8881ce121e933370c29b904478af1f65d6cf664a5750343303973b940944d2d037f80fd957fe6fc7e8fb82f602d8ed7d47d49f25ff1da40653d194e375d3821d8120c2b8bac039259f910bf8a4cd934d4d04fef1b47a26fcb96bd06b39e8c4bf98f2f6a20f195f86bd9023a7f89f8b4805ea43ceb6b1a997239fd78fbbbe4f080afc2e80dfbe3944cb67c89e7a4de9892eeff3c2307cc9f0c78acfea99b5cdd7a09dbae69b8205fe17e1da26e8995540b68a5f211943b3b3bbdd564e4e0b32135eaed471995482446387169c279a37e34d124e861a7d026f6799f9a43c40edea5e3f110a7a130e2a1490b235c4e1df1b238ddf3f16f9a4418e0f6171b1a4c3a1f82b139e0d2a817ab02ccbd849e78611b93ca9282f77a3ce2644e79693b3a682ec22331075d2855ea13ac7c7c9994aa0e21ce7ce8020d2a1b50920509c92ba301d09409581f122ada8440261209f5f07bec8d93dfeba6bb9c043f80a9077ae9770ffe99af9182db597f61121635062ca83288619b974cf19f794aa1cc0dee4c28a6661c34687cb0903f297e12604541c8672f7825c9b7cceccc87bd35f84e1d80c95e1177f7d77632c192645611c6bfe31ddd40a50d301fb2d3402da9a7f96292f6b623239045017ce64a17180b234a14677369e3fa03b70254cd87fc3ef1501696d94434cdf7120e47cc4d67d4b330d51d63eab3f90d3184b948d8e0179909c903c31b67eacc3a5e0e1118006f50673cecd3577a25496c9c0bab54fabf859dbf4faa32e9470be3cee1d367593d0dc25bfa0bb22eae89611e0a67af08acb00b9dd3afc81c48d66bf4e8b5bd08a8aebecf6b25b3b30819f1c560bdabf943ac70006e816ae45497e967f5b2623e22012f889640ab472703b5e39aa6f6d291e6a71049a8c67773ed6faf5ce6fdc41e735b18fdc619f846d980fae0758910c9609af3541321d0658578b77a2d148751e00583adcfdc179efd72a7e328276211c71046adcbfbb54206015251d595ddd86dc3ade8f2e4246c3c6d82f2c8fcab464d319dec72fabac9c003a001038082714722c6003dbc0a1b7af610702d40f8ceacdf3ebb3bab6eec5f0207ed16fad35b2092184104236d97bcbbd03310b720be90bdab3086b285543b9a28f6cada27b696fc46a73e8d4371186818ea1a0581da31ef55c6633067080ee76cd661759ee8d339b43b7c2403663da332d7bcd18c0a06756c776e139168cdd3a67b31418bbdbcd5260ec2f86056b7fb907c620be6ed9dc0348ee818301ca87c7f272041414234427a494a1d78de5ddc92ccb6af61aba9cdc6c9691d38c524a19311923ca28572ea38c32bb7ca2e22856b24986e4e8caee9cc055d22c048ab1a2c9e884e6a2228f250b96524a96ab58aa5b4617c98be4b52b232b8c6f7feda27218f6758b0a7542ca4827247765773212258676aec7c27d64b30c1e3df42ae555157af59beb4157f62bcbba6a475c267a8a95dde5771de6d61bba15672b1b838decc3209b63a6ecaaaa64758c541dab9e62554f7975ed66188dbd6ad22bd0555edd8cf297eda82e5565afa2080263980b6577657498d0b38f6ef6505669afce65bf6c0e65a08f3250c67d56d7ac48f4baa3ba2ba357b72ba367afa195d14de2e195ed30d747dfae8f7e8d6e96d92c53853e814437069ba0671706f4d18521856c0c86ebab180c83aa7337c7609844ba0f0676adbaca95285726e2946b97d98eead8bd52aa7aee17672b577121aeaab894cb5555bd3274b356715a57d57d555daa835abd4f5d30d361e1ec9cb51eb2590687fe321cf5b24ba4d7d725b657b61e6447361f09d1cd3db0e8f55622d0e8a0d1b58feee8db47afb06c94729295dd8dfeb206f4c02ba2eaa22ae5660a8f5e6d5c6671219b63308acbb8bf8cbbae58ad66b30cd6e2b53b1ad5fa58513e3d94c27149902c91cd32b81e6433c429a16b0f5dbb75b3932c4c68f4ae6d4fb1a0572b02d9c6229b512ecdb8d0b3eb650f787865f4eda3675674cdd687383bb2f5a2ec9b85a9f775173a28641fb6720c064ae2e1957a18eddceb5d69415dd1a19dbb76b34dbb0f620bbb16064290657a4a39e5b522c628208629a126313b66730f8c3d64b313382492dc2f6961b437be2eed65abcd3d90d06e3d76491b7ab56fe360b2737fcd71dc4537f7c02cc4217b1989e133f8294b35013a589e5404f86a0422b81e1404cb93ce8083e547bf2c88e5b31358266298d16576f3ca5fe600d2e5a5e8302e6f56e9118cf6d0e576796d07f6ebdaca767560f7badae5af876cc6fede00d139cd3e40d39e7d92d76ce8a1d7bbc25dfe7af66a57b863af99c22bdcd5f1bac38eddeb6ad8a9a83e7457aaa8f5ca1da25f174674eec2805e2f6dd6ae8cc4a2f7295302be7e663a40f7ad5c998849cf5e6f8671a5ca958978f4ec32db214fbad7c86629b0d45eb1fc35b1da018c45fcb21d124b07e4ebdaeb95dd6599884737fb45615b86c3ba8c583a40bbf26237677f357b984cd48111ae5b67a0c13b2c8cbd5a8b235db6cb4117fd02d9dad98ed15f77a15790ad0fd9fcfa8c7bdd31fabb13dd689085f8d105e5315358f4d72ea2a358d92d22d18ed17ddd89eebb8cc4f5d7adeb372267b38cb430f597032a8cd9fa97d55a04f8ebd7ab46a82fc70c4df0f5cadddc23d7ec044e7141b916ba994ab1af5bb69b1dc5be6ec97e236a36834ecfd9281a6317a6fe3acc9480afbf46d7ace823ab4354ffae9db3d909f890dd9e59ae316733e8ca3c5f2f410f9c1de3ae59ecd566277460577bf6ed3762b5b9fa66b314dac56e96025fdd05b165c15736f3c84de0776ab313d39491584a79a38058c708c387227786b7f29460f969877abc873ce0fc6a053820c106c0cf0a6080037e594f7ba8999ac1b5f2fe30c49e05a4de659aa689d2699aa6699aa6699a6c50e7e5a7e9d3344dd3344dd3344dd3344dd3f4c732fde07a2cd77b97d49d948f071569e0f727bbd47f7089ee28aa5612e987148614947dad1bd4f8258e51f2ef481147d0a8d1c938cbd3d1116129695021ac993c78cf858a2d4b2826f84127f8f9c0ef3facd0304909c12f870c4520d6ef9a6703f60ca6ce019efa074460c1b96b722763562415aec2f4f0dd95ea50409c29caf24adae9bb27e3d17825ea14ab8e046711b5de35dd4d6f4c089e24102abd8faf9ecb0d9b3305a96c7013c1d3b34a3cc5688d6e997e23ba744d6c994e22d51a21a58fc2321c2f8b974677d39fe4e829f98992b4061a418367a306af847d7ae3e0b964b79e379cbbe67a4e090253cf41e0dc35787a66e96ba18ed9f83aead15e2998bab561ead4521fd63d023d0c19294b3dc6794c65fc9998172e33d4c6a20b65e9529e1cd61df53320be5aa385e62ff45906a664f46851e160c3471cf065f362dd4deffa5ef4c2f6dee338082104d57a8ee340a06de3b8ca711c070271dc768edbce71dceb86c26d95e3388eab5cad5ce5366cca72cc4beef11cc7711cc7711cb7556edbb66ddbb46fb556ae7295b3d6eb5639ee95e3aebd564e748ebba88a44b57295db386e74eed7c5711cd7190e4e62d372cfb21ab1eee6d52e9f59944fabfd65dc6ff468aba23aba99c22836439cfdea0c8768547fd56a1f16558ee3388ee3386e7bddea378e0b9de242a007845a2f0cf7fa8d7bbd4502e20b62dc763386e7c3f33eb9c511938b66499cdef4a6294e31650eb5e3a7f7bbe3a77d628bd401f2735eca186f8a171526e9eec1d73d3b08d7934888f008868f483064c12f670b1d8c43b3a4094f26a1c2c820d1d35d8a1b6aa62466615586a35fa34b5409868fefba444f2fec914b20c53826c19d89916e9f5a98791988e9657429d67836aa470b0138fe9aa653b72cfd345551c0eed751c237b595a66a7083db097e38098d1336c1fd83e1803fd1e5451a3f3f3f118201ea5c1266b00bcf05138981e2a93a5d42222f22c11c15c71959b0945027b63413ba044f3a743a9fd2ca0adc97b26d5e4b5fca203c17c8043a3102ee1fc95373dbd8ccb6c13d5fff60e8f3980ec7c39eee208e1726c9c1147c316a590adc37d417d57b2f038263495e519f69f599f66c0b2298623407d3c151eee0f81c63f4cc42612fc32171322ba1508bfc20a9e04ce270f1499c9b654db623bb3288fc9112870a230371e3749712870ab35d06e2ec9bed2bba8bcf6ce3741797d4dc61cd8afef8cc42bc9db4d9dbd4ed991423f254d9304639b5be993224268e7997b29b7be0d9842a6fae24d25d84db84f36cd46a219ea0506bad9af6aa69b10c357b96389208134cd53c1bd69a4da8d2a6bb7889734597b2c7cb9c67437bfca559882516cfc613f0277be680ecda2fed9b9d68643ab26b570b3d3bcc76640f5db30f5f9b0dd91addc5d066a76f998d02e2ed4aa05e29e465cfac84422671de8cf848862a7ace1641f0c3d92f0c8331c3914d4c64734b9b975d64473743ac5d0ae9d28f2e3dd13385af9bd8127ffd7dd3a5fa78ea0e354b1c891344102452ad35af259e7ebae952e8f193912e6d17dd4ce1d08de9f0f66bb3155f8d6df6e19095f8e230a985ecb90567f6b9489cd7121f2fd81d219c5afd6621e6c1d8ab6d23ddc5a7eca16eaf54a6a61b1c93e0ea970813892093972364176538b21ba3bb28c50fbc3db31932c159c671b56699cd5009de6c0c6482b3fb2c53e170f65ce12c7bc94e331c9994c2cb9ee51570e49e5daa26ba80ba25533840a8db85c9bebdde9cbd3e4b9cecdbafcc2201f17b33ea567f65f77597ddce6f366a38309ba9cb665983e32daa06c75712070a194f250e1413e7ae93f16cbc87e54bf5d33db48832841dfc8e80b1420a1a2f47b8a6ee67fbd99e10ce2903475903471c6fe70538c39dbe898d8363ebe01882e802b390d7470d38c62e70a4813bd60c6a3fc7609892f1d38bfa70efb4e17a2c170dae41ac94fc8d6e9a2ec1f793a9d7cd1ad41c71863491b6793362a481b76bbaeb199cdb0602f145de49135b220daab4c1114807e912bc0d1f2e7a6a7a37bdd38d500c35b5487d5608a283e96f599665599694d763d29d9493493be99f68869a23ace999e823d23c1bafc6b26aac4cca721c537a185886635e19057abc549315d5a9fd7561182c5d10569de1a0bfa85b96855116655996f58bb2288b5e96926e699ed87259d8855916765943ba05d4019106df680db8e10776830ffc1e838b25f85d66d103705e1040fc106305860a290565240a81b8ba691976591545a72963d7d09aebf5f52613fcaec5ce4d11f943d2f82839926323c449131e2d8cdcd4f48f253e3a4786d8c019142461a2881a50fa1cc1ef436ce48c93243c384680d4fc2c41a24364888f9ee94972054e10204c9cd8594246476559c4162fcf7b3330896169a8f0f2cec929b9debb59da48214e9af06861e426fe58e2a3736448dbf48c93243c3846201058f313f39024d1b1bc19efb28afaba83f0c1a8c15324503db86984fcc0950f8a60895534ce9984690355386c9fcbb4792dcf85cec41984e70295741b01f7a4600f54d27009ee2420a69743ea53f650abcbe7d893630e6ef83c6d70ff6574060a298150ab2b9fe94c9c89919aa133d333eca17936e8336d01a633d44797689e8da7447579ea158575679fa47aa81597f451ab175f7c11be1ff01be197e127433e1b564c548a1eaaa85b60fcfca77592b38624b8fa31f52b52918a14c401a1f5aaca7054f1f87a9356a7f16c34a62e810e14e0f9608a7a4972022a1698a2287aa3dbb274471dd2a0a8675131cb715c55cf94cc70c0aaaa604bcb425d98f733a3c0051b2d70010886c78ab854bc004fa3852ef8e8824d178260788a0458a4261667938b27c6e70dde8be7e269224219ab13183ec2489920368c4e8817462754f89ef034bd7f7581d2699a1ee5e3a47a305dce699a77f698ba3a830dcfe4e1892ed5b5d9135d7c4c019fbc1c813a15d4cbf54753c4cb752aa817eb79fa3c1bf2ef34ec204bf367ba70620b7cd1ec54c56be95f7636b13ea8f88b0f2aeacb7b05c8131507c8930f045c38d1e5c2c1135f3ad1e59ac39507dc97cedbb65ab38be76600474ce2d924c6438cd9886793e8d2afc58a02470a638d67932ef5ebe67b3a018e87d82f895116e2f9e4b95cef2a70ff9a9a3069d49382b2a2ba6569a817eb97f54145112fd67d91867aa92e15adb09a50e19bba4de64f972e1d3bbcd2d5056aa9d7327f7521ce26d1a5be96f9d26c0dbca72f1e196df013224e941e2a4e85f42066ad285837194fd127a59425f8ba78191b0a52cae9f3a494525a5078ef5d31c618e77c5516949473ce39e79c334a0ccbf1307c33e4afbff7de9bd59cf031ce39677d33debc10ca9e685e8cf1595398a689521eb419a69c92146193c6f266c6d8645ef8664c09218431b8095283fb17840f3ef8dec4e70e45e13c75289d1ee8e07eb457cd1729d5b331618c125a519dd042cc117933fa110f153e7352def766c8f89c34b97676baa445bb7045382243382254ec085bbe9419388d70e104bf2df0c3828b20b0f47bbc2800bf1f2b009ba266ea32427dd913f1c9130c047e7fc903c02f0381e91435d79c96c1102e92b095810b1e8c818b1a2ed230878bdaa263773745d198033965c734503a4d2922c540830f60e09793240668d041f4416a721d81be807487ab084d830a1d301cd4f747439ccd31984bb12eebb22c4c7d63cb6607d9dc61abdeed5866ddb22ccbb22cf7428724abfec594cb12618d0b45cbb2ac697d641de5b22c8b1addb252ae728bb26eaddfae6aeb2dab89ac8f9400818e4d585542bb7557eaad732bf5a16f189663f04abddccd31d87ae86eb78e816edd1c23b27e51ccb25ad5340cc3eaadd75b5f22e559ca379bb76d82d90320c67e51ac5a500ac84a397661529e5d18d1eb53ac6b229135b2ac97edb03e42b16addac0db3acfa0dc5c22cab6ed6a65dcaba552deb66cab2d57aa63066653138543109f40bc56e29903b8add9e627387b5ab695a0d7db38d492f8e3611e8dacd220cc2aa85b98e61d9af5a351b023d04dab65048035d170e3d74738f6a73cc75b76b8dabcddca5b0cb666cdbaab6c58832ac0595c239469463440f331183df8b286484ebb160bf6c8ec11748db9e650ffdca324ba9b69d9e5ed84399b41514d26ae835bb39fbcb6c8c08bbd38c599665716ecf8e9255190e2bcb5e1d80f22c233dbb3925dbaecd6ad78175b6633b776ab308534a7f5511ce2aa771b1da7accd6ad9e7e1aa1fc9a18b5d9397bfdda5eedcb76641c877db3da338b6d4884cee5181da1ec46ac3673a72ef35663b603f47a6140d72ecc284694b71b23c2a04b477f6f98e9185d95ecb5a6642837432c12d16da39cc67d136535c3616d9c767396659716c6be59eb229b63b0088745b54bcd9ed583eacd106f598c8c6547a8b31cdcb73fd1ab85b8d6ed127447a05f30db113a04bddeee4016566d22ba6d22cc5daba09a5d825ab218ccd910f6b7fdba9cb76ad92bf7300cf66b0777d061167a15dd1c13b239b66a615703d98afbcb72d4674f47cd2e0362e4457bd05eeb31d0a9e8f4a23677766af3ece12e774c4607d36712f8600a0a6987217a8e135d6a7be06af385a3d1544129a514e5b88fe8455474aa4247a7a1934e6f46a197e2b65f170569e7b86fa0ab23bbc72efdc6bd3620c471dba7d373b6d66bc7b06c853be8dab113d8e413d8607a2c63a738ad7e3a776142cf2ecce85c0c95971ba9702414d125da441bc765d8b773a2ed621947e9751068f2744974ed33c9e829bf46560b8decf61aba19744dc405c129230b718a5da9a26a334977dad5eec55dec372248d3344de36cde72e5ec768ed38e61dab14d0355ed669c053506d97cdd98d9a381bed913d8e8c8b63a7bea76b3c98373ccec91c1c12fe70436d417b367f483ebd7539ec8228d5040d805596e9aa649d3346dd66a7dab2a5741419944a28903598ebb0edf84fd35c9a6fc75e8d7c85a28b6be1998c66916071abd024db7eae8d5ab5145998e72f39b8edd9c7bc488f0f51b31c5e6c63d65631886b5c40e03fb686638aaa9b1fcc35f77308e5d4ac63876f34a632d3a7761dbebaf974384eb7491804f707645c75e43a15028c775141008847ddb4476fbcb96b03e6d77a58a9abd7e4e87b15d65fb2531914541c1eeca76ecd7caf6d0b92cbb2bdb4522f40b6bd3344d2b39524e7a8f09bbc8041887028ce5c8ee86e3cc928078bbc8a2fc6547b9d8e763b6c3fa7661ac4f1746e5dbb5edef98cdd98d1161ec55e56fbbcafde1ca440ce3d8b79b635cd9dd06e3668847a3297bb64d1b37d567df46d3312cc3517d9ab25bb99bb1699b260c9ba669b25e59577c309e43183bf78cc02001fa1876d9259563a7bab472ec5897528ebd3e1ba363280ffdbab08b62b56315d75fbd6d1b3655bed99427e0133c3a56b163157b08c56a8d351d11c5428c412330eeb9e2d031cb3d0284b0afd8d75d28741818ee877ea5d89185598eebc2b0faed31d35145dab1ab7c3b4aca497f184525f42ce5a4936e4a6874ec25e03ebaf5cd08bd07c642dc7561aa5bd5cd3d6e44cc56158eea9785a92e73801eba1489b2972013611b866d58c42e6bbdb24ffb8dc66cfd8d6859afb29b7b60368b3076b7ed18b6617974eaa1c74c476398ea9655dd42220663bfaeab3198e1b8369b03f4ed30d301dab6bba203f4ed394028df2c7c825f26bb033db3a2eb2847c9b6bba26303dd1c23cab62d7bd5d58ed9ec21cb7db375746344186238b06f365394cd213af698e910e1eca263cf21ea8163a603f4d0e5b6a204e821d0c5ee8a0eecdc4bc0ddfa6664c76e7d33b6f758d1518f3d47ddb49ba5c0aea807bed1387055553686092caa30ac12d0332bb21903583442097197c69dbeda8c61cf28a5f9f4dedc0307d76a45c7b827d1dd06fab66ddb4643f428f48e380c604dfbd3ec8d9859884536b3c4b0607890a6699af694107d8a76948b3ea25c3bbd1946cab5d1553494eda06fd744a7dc41df7664c7be5154ae3893707ecd2e76b7d36bf5229bb7bf3760f4807aedd3e93710a5c7ae1d941d54ebf64cc2f5b862b76ea7461904ba9f5e2fcce8d88509bdc6b0e07a6a33e8c6b0e051e83e1859e54a12ca35fa14945f281404a21bdd0e42a1d7ae530ada34aa51aa5d524a29f6eb6507d9ebdb8681eb57ca4918d62ee3352d74d0af10ca2864eb47363f1dac6d2c58bb26036b35b423b0764d437912dd814216e21a5de4e3492410a805a4724127819e7251625840d7b10bfb8dc8d91cc3b2d977a3417f1908c30eaa366300b31cd7632d06d662e0fad15f9748224bc201ef42217fa40aea75d2551e9fbb6f007097213edf3a5ddae992cae79ba7fb8a2e6548f0ccad45e71cc1f35bdff4cdb36164fe8718d4a528eaf699ee04718aa22e8f571288ac793a958f2eb17cbea2892e42bc263b5da22e5f53491f92268bf174e60f8f4f26f327bab48d0d3cddd18207cf4b18a3fa8d66b1b14977f342d8e8a45b46485a87b27da48db4cdbd14e579feacdcda24ba9bbf95813888bbc4ceca33ddd9d961d2a44b2b4e9848494949993ffc653862fc460361db48ff605762dce36b27ba305123cfcecaed1bd2559efb660b2d767876b4e0c16e59a4f914ba0385b450c869e6bec1423b505026a6242a552110b3a506d794a0738ffdf9ab1f6edc3f20ce853857343db410a7fb22d1a7a7dd87767a4f21ea4dbf24d6d3390ba2a24b175be6bbaef11482e882726df4ac8f1da53feaee4749290785b81a38431fda2a4021d1943b1de54ec7029e7e27907d989b5e8540efcdb0ac2e68ba89eb8234b0a2d61469af1cb68540222c542d9363c0d3b1e99b44039ece85ecc3a0e95ae888a6412aa9c4a6439a26683ff48ba2b0c9c22bba0c71485ae0d9618864ca200e910aafc55fb79e1b08ece2cdb74d13c1a91862cf42f00b10f479362acb1482670fe922785ed94692b012a8993e6f6e1a98043cb79b49d3b939fdb60af0c4a2e95b77f7b435e85148dc7f62c31d3a13ddcdd08d0262d0cdf0743a0cbc4d74372f06b2ecf16898cf922e59f0074fcda77f7469dafedef664df15d85702ddcd27eb5f8e1bae988f385a5ce193d3a5fc748e7429431aacd3a510401f12f7c989ba93df2c1410d374e9915e4b5ff3c16e266126a20bf5c983e7255025115da8db399814b047943fb8c1faf5de0531fa6e3c1b8dad7740065b96f59e8b65596f0b6cbdab257a60961c3638c16f097e396ca8c1900964f266509b5956e8b33cd2f2c89b01f3e3a9f166c05f7236944f4e9a6525e544dd4d7f396b7082330d6c7ddea26a74c9b27e2387fee5581587a14a1a6ac31bdd5917c107297aad4b615da20e67a63bbc4b0678b6cc67156ee6b5ccc329523e337d011138e5d3a65b48cf54cb6a8271089b00f183e7330a7e94867ba6fbdcefa27aa20b1127e25633d7c28761ff10248e6a7086e1bb1aeba17aa89e4c6d7ad0eb42c4ad6c28215d05d23f86a47c5173c06adacc21ddcd579776da40789180af5edd954f9b39a44b16870737785644a04daceecdb594adfc464bae769702e12b9bca8664df051f06edb4892e2d07e241fc4594b758f85ae03bfb5e0b11b6b2213dd7226c6abd20e9511890489004e1c302c30bf1b6f0b2883c2f090230fcf57828aa8b9e8aa7aa7a64115cfdfeb2280b0b750b320bb98554835c82050c5fa5cf110c935756bf370a58553d5dcad5ab9eee84e0fb955f97ac2a7c5d18699e7415115d2214220a218410f6a6a1e6ca07cf98032b9fee26842c782eaf743f493ca036a03058b13e5d5af97cb5a4a5ad90e4c82f30bc0a308c392dc030e6cc00c35b2b04c35f7b070c57be124432b9b2acb31c8d6358b08570e5d54e7713663bee576e1410db9be5abbf0c0918792b9eeeeeadb4e86e5e293e55bea05e1806a5bc1512216c865808593d52b552fde1da9dbc1592ee661551ee7f394a8a4c31a74bb94272a44b79dae8e420e952ae7a72a5048820324949978478f856d45dca4955cfb4999fc1f340dc4c057169dc6a87aaaa99482255f0a63c1ebee66a774cfa44ccae9544ea2ed3c0458af0433bbcb421bb2ead5c9ea54bf6f232e615e38793a011f7f3309e0df8f99557b29fd546f54c0add5efb1e01e42bdb5922acb476a58a158bcda03e0cef5421714c6130c3216ff48acd2bbf8743fc4b30cb8f2f8cce4c9b6933336de88c8d8dfcb4913612e7463e4f22f29046c88669135de01b60cf139c3cdce03989c035d0a646a80adebc4f0b7e66009be400e3644a8416c1535e12bba77cbad3209ca0920b7b600f168281b8fc55391b9b7b6deebdf7da0c6103843bc410bf4208c1b59472071097370a08459504726d2c723361d0da4b2b6df6e992921f188857b2676cf7ac42440bec892e2d37e690ba4ac47db1c5882a0712603e378eaf1b82be962186f8e419628821aa9c1cc72c7106e175f3d38a57e54023a60d24c0fc9c55e079029e3309784e20bc19f3b9b2029eeda34b40a01a9ecbb4892db3830c31df3f88747143073c27ce17433c4f1b000000040af54025b0b15d790262f82824aefec486abfb3fd319205e1da6ba30afbb7b1988ab21374549a58c744988cf5338d185888f5e7dd25436f6f7666a9e081b8b74cb3c006c34d2ddfc1036e2b0d87845951912d35394d44cf5c0503e3210c7233127ba54331cf6a88f792f848d4420118cf25c0476222d6c622193eee6af854a66ecdd43859729a419e3b75410560662202ea50486cd10c3f894d199eee625102a7c8c8bced09967030e897195bfa7a272697fb05089853eddcd4fd9ccfc24cdd4384931e6afffa22d80a7818fd48712daa18dc2faca8a7d18867d58c5420149160a9962a19028367f7e643326b299341fb25050905da976489982b0274eaa07cf6bcfaa5bed7489de5afdc41eb7238085121f210c41fb81d80f7eb20ba906b985cc02bfcb2d3ca973052d27ba68353535115eaae5d464c953135de416afa5af86205dd8d081c81737b8679e20b79074905d4835e07e4b27b8cf43c549cdd3a9062678fe414882235611c1fd69ca740c816350526573b5a1e6cac6471e211dd1840f2248e00f109c4e323344272ea99146284c5251ab693373306780df6cc154017e975f3c97ec35795a209148241249e2f7ac27ba6839b1a54f55524604ce5a0e1138673db82f79569e8b56135bfa941002201de04660ade60ab87f6939d145e5b5f4ad4ecb4e01787027a007f7cbd1de206d8e635aa92b7bde15dc5de31c9c2b1b4841ada64b40b42be625c1b87aaa56a3a44bb9b2f1e95226499e2ee59aa44b534fdb1ca97c7edc68613511927329a129b2833109f26c34ee67573c1b2db54072e40852102ab5aaa8d1cdf810a41f2a0f6e9ed7d267782e954d6ce99ef39072ba39c5f740b243f50277ac241bb04a015ecb15d91cc7b32909a7e8a1c24b53bca82b51484cffc486e99dfa53b6239ede28209e8789954d97863c1b48baf45e94adfdc080830d965a0dee0b79d04608a638c04d6b384af35a3ad2173c97e9b406b85da63bdbd21fdd35cd909ab11b4a13b11b0afa60373434f1664aa3f212258ccf2c388ae86b89af8b7f46bc4780f8f7952a2094326a355314ae4ef6a466a2cc1cd09f58674ab07417839a2452d76e70825bb611ae5b39d4aa3217ed342ed8398ea7b79ceeca74ea62dd5156126a144f6c18feba2c16691c9184049a8831818f1a21448ce420e1691c7f69d6c7112e0aa7f7cd175ba760670e60e94e74bb9a840a458e87a9e780ef11205b876f1082db01ad440ab0f5ae4bd7e3a54414106788a737fcc07dfa6c3c9c3960c387fd04c45357803723d22baf89c96703e28e1de1d4e4275b23a7004b2c2f8d372316a9f9ffa9b2c0020c448051b2a3a41f15e3843142a824063c86c97e078ae8fa9a8a50fe782e359e2148fbfc48f9dc3e524a09654bd9ed438be80e3e0435bf1d1e10c207a918e3d43f5305f3375d7a9fe8a789fe7d87a40373e4a34edd2c23e9e40423f0c609324897a40eae2c0dd9f076b12582a0f6e1779a88d4fbe11167b703df3cc479f306e22967a616e015f0078434d088e9d19b5fce842e900fd2c8862e80f438a8862411d45c4411b12fd62def50f4a00b62843f705b1217f0be38d34d68507a27fae94928a7a9d325486ded0eebee614c74d7f740cdb0a60742089d883f3f4df5e4f6c112c6996ed23ed4f4e04dcf26ff58ae4aa68148fc284ad17704756b18001a51ddfca8070d10813ccabeeea87d75c65bb10fd4fc7672785014bcf14ebd1a4f1a20dafa5385175c8169e097f3821f1853c30c16007e395c30c1dd83e7319a1ff21335654a5858f6679663a2917dec4797e43b42201f5233d421f25ee0214e8444601178f39a7aaf214dbbbc97f73229bbc709d7f441b453e7cde8db50024ee0b9d989f231f2ecf04c1e25d4f71b58042331c6d983eeab460d7587e749cc02bfc79da6993c780b18e69eb2d802942f57cd45a383fa9e2f1a0cdfb2ffa28c7707084838fcced47936faef7007c73b7578501f7cf783ddf08a26c0f8de7b3109f59d065bfc63912fba881af7ad4ca84f76092e210e4570fcf1b5a9e4388e548a1e6abc94110997ca15b569f0cb02b865eceeee31c109645ad67b778af1d429fae285efbdf7ea3eda8ee5985e96ee6037df750765c0cf372511ae773385bb77ef762c1493fa2019ae1f92280d9ce5c7f0cfe6febb3e4b775d77b0dfa576812ff0e5e4018b22acc08a15f8f7d2941ea2f0687040045f5ede5fd6b70af917485e8e12a6438c44aa249a2a68dceefeed50330df89259d78a8a4774d9b1ba9be8d20abc4683b3464359262c8feefaf5d0882dfd6bdaa17b7a68d45961d62d6b7ecab00a0775d237fcf05c36f0623c1bd6a921707781fbd7fdc10bb06e136fc617f54522d489a19058661ab9621ed83a8c3134f0743ab176a9dea72a5ba35b9ac294c2fd3ad9a01ee7d7a3872138bf1f1a35ba6b29600f2aad2d50d90fea217e5814810db8027406c7bf5030801d839a5f939c48ed204e4daa28a91dba34fd488a4e5352749a724e39e58dd862292a6a7e4da69ee822a30f8e7d3aa349e19a314218e3af098bb983c70def84917ec89e8c1d7928aa5612e91d0ffcfe304bfc13d3b3096e142a983ebfcf7714455114759fa02c3ea838ff654fbc7bdf7d627a86f2f3cfbe27e17d825e2be4a77b1a5811a2ee17270faecb724286c63bc3314596494acacb32d49c90cbe87d14744974d2fb492cd12518eff3783654de2958ab9a76372a056f8737a7e07c9cd9fa6963261f7a8f4e1dba8c5efda22cc782ab3fe061ea5615cc1351446c45b55eed09ac27a8e795cb830af8827214cbb2534c8102299e54818b39e4610f7c78f2629d052947b9565857b1a09b62a598e23d79a15179211dda27ac20dd16d4d78bf258501fca59504f3a0b526e3d0b4897145bde534eddc71650c716eef5758bc8caa042f7af419507aeac3f3ceb2fcb66af768aec28e8f1924d611d053d5eaccb82eb6510b853588716bb99c270064fecf96581fbd96561e08a4cc43be0738533bc565819be8cfa7b5c3d8cf7aebcb6facbd18510f8eae6e842c8bbc2143418423823737421a4bb874fa33bcc226346f6905495c92167fefce8093299c01a9b2145a693191f9c1e254b7ae6cfcf749233ede0b6be824d42ba94e78f8561d627223e66bad425149be9c6c87445bb3c3bec4c3f6aa24bbff4b1ca6aa2f333994c275d822bc0fd1f2518bb45319ae1988f72a4ee0e631d7bb68e1d9b939befaf54d6637449d4dd4a7591685cf9e81e4904e5f9c06ce6f0c4e5d1a127870e3eb85f2b6b4e335df2d125192372d4a753c7ae15d72d6b4e285dae3bcd4c4bba7bd7afd3639795ddd233c3b1d2826addba5d9974f7b008c39b313c5591a2e261f88c9866f0ebf1926bf53a88fb55577dbda2288abaf9aa0eabaaaa705033c351eda0a8ca4afc5e093e57bf7e55b63aad28765a55d5cdd8a5baa3bfe82d7ad92ef2853e66382a89219d56784c7f78aaa921e86e3abd4d7437bdeb50fbf9f554e182e25d86c239568977fac6113fcc0a0f0a69176a089e8741ba94cf44a4a85a49a4fff1901e0f85462e3a873429ba345184aa14a99ed49d4647c2b988f942913a1a45e039e76422b6ccae03cc41c344a789d24c5315b23012853c78b1a38497193ce1290665c013a93b48d50109b6b430230b64e09763872be0fa0422f0cba9431d306604521de81000fc72ec900339f000c656810e86789a931452277e394fb80043ddf0cb79b2733dc1e2c29e201161f91b5fe06079067469d2c11c7e394f6e9e0001e7d390d10baf082c4f63e48e56419f6041884e12f880e9cdf3524ba294a24498b680522ac2f4471430a5c1747ab20cd50e264dc842055ec4e00b2bbcbc27586241d383e565cc210d38c30f0b1a2e4c3c5ced82238ee8523e354980c6d3dbe3e5f55b79afd165e55d1ea497fe9484fa2358ded85a5cf0b20b353f1f9af6b18522e8bcc087408994d0a7a749853d21a0a920cc71b202fc425049380ff5611af842ef838a05d0c097957957261093785183272f34891740665ee8a7bb323ffdbd4c80a200054100b264e685de95797950a7b9c6740769624b4d6c996aaa133704ddbdc75033a48134d1e5b5a1093c48c0c20eb248c3ce0b7c8db2f60f9c2e9041d708a0a186daa731a875c993a21b5cf44774d731e045244a5235c68b3deb243961821ad45c2b3bd4fc7e96d4646fe0913d21f0e287090c2cf9e1e91142cd540d2a4cd2c31373b250a5cf9bd1cff1b0ece2cd6819a99798135b1ade1c7f7a7878787872de0e0f09c81f1fc89cbe699c213553110b153ef7cd0d0d8973c30b1a358ec4971824b688a1e60e7732644e4d133c4c789183fb1850f2721ada24f650f3e36182e73ebae0caa9a2cbbcf76874b34243cd9d1575a8449e8cd73d119633a7d079f103f765d480394f06844aa834deed280c6aae50b67cf75e272347563107382c993514a505ee63f254fc65dee0be0c193528852c36be3775f2a711d0bf2c30d42fd4dc3e3f5d82986a9f25d64fcdb3a626baf4674d0deed6892eb28bd7d2d7a93ad027ee5c486a7e393718306f60a50368936bc5c17d11b481438cc88b9a482589baa4d34420112245aec09d6303875c5fa819fea839dee07ebc8937427a08111a9c9b4817b17ed44c3511dca7b06973a38822e06083fb4724b1c44bf28ec8fc091ae32920104208effb7cb432d4d4fd684585771ed15dff7a37c760f80831d4fc74e27112308925ba14a38c33d4dc755746775da8b9e22afae99a19521313c0c107eea380c262401ff38fa5638c31c61823a513a52845297aa3e9c5a6354177338d803e5d43cdaf8909662c39d44c4d23978ff99946004d23a05f715173cf482fea650d0cd23472a66796e07efb90333e2490fe813b086e1fdd354d773df366b4f4892ef27d5883e195333de3d32da99927a7192b54fcb4c07d59452c988cb6921a01fd90192a753925948a9aeb8e4e9be0c1086184113e9b63e49b6fbef9de7b57a6bbbeb4e050f3fbc1f37d1938bd9fceb5767104f7454ec8401f3170a4f4c207217c10bec719a738c52946118e2f7e62ddf574339c71c20432b40d35c31ea804833f1515177c792c5617b56190dc418a50580c0844c80b6abf6770864156e0dfdb260c3c92e80208ee2ff1b4e853f11739610d1922d24b82472320be4a438dbf2b5560418419dc3df10a3628a914d5850fdcc748316488a63f16b9806bc524bd11d09f34a8f1ef05eb8cf40a6a3f5ee1e383db474849e50d4ef08b0877139c597cc09988869e970c7e489796dc6099c625901497c30d49babb727461132f88f5cd10fc700e3718e9d2cb821634f8612d4717365dba39bab0c18fa55bc826e7e79c3b60a69c179c3073d249270b33df3a7a4ed37c7f769c70a2d36c0b33f5a0139d60a6c6263a4df3493a052b84603dc65f13052f4b891434be7e41aab33210c35faf5f59566559958ce9329e8a11fb8bb471acaa595573aa5edd4c61965aca3e7ccbb2e02dcbcae8dbca49adebba60436aaa2aaaaa286a55546555d52d98caaaac8ab230555559535551f44edb0f2245a0875760780b093eee4f78b3f54c097ae121b417bc0e6f6681629281385ed2c63f49a34bbdc3e2fe8d485191ba24916addde375314f547dd47f8a246fd46bf2722a66420113175a937637e0ea1d2439c69603470cc0818f06c74d5a5faf91a457469feaa16ebaebb8cbbd1d56aa7ec8b82c24fc43ecce488b8e38d8e1656168a89379b2b9c5d6cbaef3a3c0cfa3e84395d9a4e1b5221d407a110aa05ad755f770f36bcf16c440c5b0548c0eabd1994b65d51e2e1eb9258052b295488214565ead5a985a1ded89b01ad23d4f71b0ce812fdec9b255ed1119f3c99de0fb7c51b8a4829a59452ca25dd3b3728c1f1328ae02207c7b35031a66088125eed8e0a351fbad4e8963ebc346240a307efe26287891d31d0330c717a78e74f7419e2be6e41f9bc280d2994a30861a51042087b0494cb4b77500e518ef297e140a13b5daa1edd61f9c4a4c90f45fd30a17ee0fc994cba84f2799ac5739135740b3ca91af0a45de059ddc9a48774b2663ac1f34024911fd266f281e7e56561d864d2dd0ff59b4fef21afc90c9ce80ec5b2b8cf465715c7dd67156c137d5026b4099e993ac1f308cd70b09c6638e82b8b62bbeeee2f6857aa8b04f5294be261485d96ee2ef5207521864279e80ed5a208d5faf4dc37d7a24cccdc3678f611968fec0acb7ddd517f98ea2175d6ee9bbee91bab24a9caba3b2b409c096844f5790b5ce095ae10a021401408f45b514308712d8aaa95b4b3b34365ebd5afaa8d587dd3377467c787fed026344997b2d3259409eda14a300f1e3fc498da48d5377dc3a30ae2f257a66d2c42b06841844e93f474492ad9b997565a1e419cfaa531f95503a7b6a8d44bd7aa233dac18d108020000b314002028100a8704c3e18040cd54c10f14000f93a24e6a4c18a94910a41443c61884081000018011101001860900f66e6a32d46b91df771e695ca8fd92caa2f7b9edf4f7362ad8bb9bfc37b69a8251164f06e1370eb5d91a807a4cecf55bb649271e90a13cd6147ae874f650569e84cc82166f0d68aa5969bf038981d08fcccec7040c97a401b10876a50a48a7769c77866ae91cc7c940a20a17389df9511c41477c1b8e608416868b2e8d945f53e5fc84ee28e0d67e8b01ecf9046290065ee280e803e8433e98915c2b312b9d651172e21d16c2ca3130b45eaa04f2214aa0c4df80299a8e41369703ad9efb52437c6cb51ce312964526ed9d47f0e80353a0dc0c05051223a6c24fe3c3d1f4d9200ebc8105df00e13481430dd7cd20ee176ba984f6d9018eae3d5478d24c5d8abaeb10af775771ea3f6d4ce1c5586b1d6465cf45fa5691182e820dcb6aa3ef122c633d3d53ed440694343a9cd13b1d38c1479c6623bf5782b9bd46c3605c068f95a10be025ac21623c6f76950860e8b4e776b9b65756bb1a5e8658f47c47dbd4945b5e2dbbed840a6e242e735dbff10a400285d43844418acce8fb91aae745346ef71a6f4e6eac6f2c44b4f4c06bfb1851ac4f258a578cde6b7615d3df90e3eaa21f2fdee3ee3cde93c8b76cd9e7dbef139c710b7f76f35e8f971d5dbf59b257854948124cce97e930f8500b22146e46ee73a7dd901b57b968d7e3c8c5fe22b418a7b299b01f103938150f0fbc1fb3e714ac7f2cb8f638ab5279d9a3a0252920415f2b7c35a27c85e09a39d39300b9d66d2041bae16de4d1e31f9ba88f2ebb0562c28e3240dff092cc52d56acec81ee23445698d0361925756f968b7012581bcf89a88b8cca38a27960dd8d248d5bb94075317f4f6383b47a46e5e2c01be305f4f2a06ea96a9fca938c52e2a9763599d7879643675d49415ac0016b7e703179a29b9ae055d50b00ae04d6e4242aff08fff044b04b3ce871a01dc80cc0e5741783bcbea2e7d213e594863849cf944b19e4d9309392a2e27b32e1c6d3291a7525519b1ce063dc21bf15866fa09b430b7f77895c19aec028c2bea9b09c9ca04d3a6e83ee1db93941d059f8ba63927189d1bb1baa390b091282620b4de9f4d0564251762e7601c5b8342bce4cd97304955062c5fbac93d2f19da45fa2b84f02a915184d129bbbbf82f3187b9f3d84d3e3bbc26205979842cdbea469751d100d57696f59bfd9d7aee2f339039f4b7367a7bbeca9e0fd59a17db6e65f16b7a89d50d0e2aac097199a6a544bf684d6ff1bec78d60d3f3c6c38fee1440871d92b095455f46114574a2882a6a2dee8d82285c1b74107bcf94ccd306651cb584c41ce20384b9f66101969a33edd7b8494bf4a8d1b90b75821677c9e2bc0bce2b792c58f9850849495ab902606cc32b808d2e2241c61a665012ee90309007b25cc35d0ef18b88f23a43e2f7ed14ad116892091d230c3add627c3cfa7e0b5514e590bb8ad54847bfcc9ece10c41bb1d7c55f3be737ffcbd3dc04328dba2c1481278fb6ee7a5b2f1e5425c75b7fa416effddedfc703a1dec1a8baab1b388f2b1ce5225f54081c66e6c30b8c80aab15d898366c5964cb7a02034fb68c0de3c85e09a89d3b3ff13b160fce8b83e4a18691389b1b2e71367e41385687ccc1fbd6e4ed4c1b96c6144099d9fe466cc417d2f1c4d0d09fd587b04df8578beee89d641351d2bd0c4bb5d7a8854ad3208de078981c894d5ea3f5084b72337bda0c6ca356518d2e97d54aa77f7cf33a457259d396bdc15b21f767883c05b1f65006d44f0063ecc8610bf64a7eeec17f192a664a14fc4b9aa499ae878a276632fd3556e218a4cad95e30e83b6cd26f4224e32bd6edb0a1e63e2146cea7fbf73936e2a84577d35d59847ced1008632b29131ccedc36b2b0d70db5d05ddf92128d8c59c97408f082c7e20214b6b0273d5a6e7ead29ab9248b2706bace51425bd313230bc1bcb5c06677ccc601a7dffb52d63c31fccbe1b1321087b340f55baa40b479d926cf9580431188e3fb3f5e8ee59a3bee4cac4f1eaf61858255e9b1edfdf62c7ff29629518f42e7dd82dda217c5cef35d425a7063d61566e1eb14d5d3f0a740916da19e9698169bd02a519747d9183c5169a0f347bfe879b89141bd717cbad10f6ca797f771ddb7f8c896d28fef91da110974e586417fee9d854d9a8ef5883891201357b7ddff001940483ab0f142bce4db06830b116533982a36398525ee299b33df7fe844c86124390873e0c9be44c94b1ce5b15a06b602ca4ebfcc1ff55399af94a9b856a4e1ddb49f252844a333c8e6abb0f5fc88c352f03d145fbefe4093cf8413f7a579ab0c68624a9d085b6941b49a2fbfeb68746c901c9333c4c5aade623505bc3cea220600739112e6a7064cb089af15c58b5185e87a98a7506546465489e02128cb490a01dcc1e3744fc6077607824715866646ce3b713c0c46da0280a555f159b800857013020a2a2e64c5a7b640d2e92246ae154660cbc14e283824e3e44a343025e34f320b803abb59fa609cd7710b4827e245044ac9e0f7bbcf891702a10d359be46d5a4397f784c3c5b9643fb975d22db01d8bf32fba933b53498716f05631c7f43bd6da73642e76bb94a5753ad30c9e17d6ce2f86c40b2282883d865e6c7c75bc38b2fd4d807b657a6dc1161c7ee0709d193303244ee29148f0d5559b3d023c8746b596099c54515cabc507b12195049137a2d119beaa2326925c6bebbb61813f1960b2b0b7ab6d768e1f638274d9742c5fc41b47d2fd6fc6105d7134a45da08d5720d2b8922fded4eb49f2fea35ae4e9366324265865931660b0fc1120c98323a64bd93d1e723658855e506201de9a9295b42ab05c285e32356865844c7244f4fc02226dfff217eb1df29d2f3c90e36a7721c6e8be1c311a8268717ada1649348af48b79f9628f451112b3204e88bde9817fb8efd15f98377b0bf5ad9aa8ca8b95fad4e6443730339a1790bc74e54bdb5e15387dd011af2ad4d86e65c4793960a61e369a369a2d9149e140f91a66a9d75abe4eace9265b65029016d17783b6ddafaa3ad89a4b7dbb08ce0c3125c890d045450d6bde7e378c0b43a18ab5162e3c236c6c014c4de95f87396d0a94409e208de0c2a675da527c9be9c18c023d54c90381124c3ac74e2af25c67cc1ab02c3a7f2ba661046394fe260320c2ce9b72d207bf95176ff25cb0bc62ae6443b8c27aaa0d5141f3df5f8ae6611345938572b23cff29ac53baf9817d234e977e040073592d9bc1661f9da88709170efe71a4d22728630e1b0ffe7256a5ffb9d3d571e770b946ca4f7b5a1e086912282528053f0ea392dc3824111c59b13f1711452e39e7b5aadd0c862e6bfe7904f0994084f0401d4833bdfad94dfe02f9ec9015c8d7eddcae1039b672eaf5745d769f991f5050485aa9fa41e91efa5a69d29bbe9b920c9adf394a52951808e91171137b8543c9cd2be169e8dea0616abb77a86db7166c1d43234b658564dc6c0003627d5415e2f684365834b9795ce168720bdee5de74346d56ddc97541045f32555ca381392324173b64e9a5c6e3d2f010f3c36872b8c238190056a18521d329c2db9bbf232eece7e82ef7c6ef58a3fc03c6cdfa705b673b7daec36d0643eb01995f512f12a45add3183de5bb5be0b67d3a79f32375093b42ea37e8ee6cf13542cf7e4fec751cc1884c3b0f1dc31f8e81c36f1ccbc95bba64b4da7036ba93f39e3dac09a5e2df3f0ebb3085b525a5aa074023b4957c19fbe5b327534707cec4fe241a831ca9f3a5a69f7e03e6ac174e322cf8eea1dbb135870412e3c5c02c9347ab392d372ad23ce724f461f0264f7caf93d64e62de4b939d90c6cb0e2d79f24929a06e7a2cf95f41fc7531db054493f075978712079ac9df3763b2bf908558a11142d579f6d432e598acfc209d64f5b90cda7d938623e194072ee76e3c3062a0ebd4a7ef04eed284937ab16aa97c789c9d5c051f2a59c4685cd8183a3a49e4361509873078dc57011dc9b2450f2bf5f3b938ac04859c5a592484d147c117fab3565909aa545e8fcb504a8c539723d46105961aadd5ecc05570b80cce37036430a14b4cf1892536ac2697d0ca53402db0463bf104f39edb9cd86b3acaf6dfcbf8ecc6403ada4451612b6e5680453c9e96078c5ec8218e33e19b59e52ce0d90afc8b06468163b310be408685638e52a41c815083c1cb9ed45c824e0b59cc3bef3c994073694e3d46dc6b81ef11e6b674b64225edf4678d888304f404f464dd5da7a1181dfa42280ffcd24b65ff1b5d4e4cf51461995662cb17c3b3a0bb974965bf35a7b8c5d8848594f58bb58d5574c95244ea485c13e6f81343a2a1cf51de9896c6a8c8c83f5e5dfd0a082215d0bb4a1983fd307dfe1d2e97cf11c2bc889086e915aa864b650bbf16a6184af2a452211d960e1fc15095b8a3b424406a6d0a1bd74c3dd342e2561492a0392f3451ddd74845973ad12c15a39b3f65ddcca529590344253bbc17fbdb17f202a336230da51c89907b209d254b6618128a7dca8597818d4d6de42d367fe0240e12512c1c48dd95ec8a6cead982c2369b7d0ac7ead567b343bfaf299437f259d5cbf4be0a34963792d77dd1bbf9d44e0d0f48b7a2368093e4aa5e2ea8e31a66a6aca19ca2b95bd49b3fcc04b385131457f9040585c740accaa3f0e228fdda5156cf9ada7d77fc97570126a218b9e567cdb69355b82a611ce9be0324eb9639a22fd41340ec7d0fe15c6e495b4a27377e07f0453feca938bd555d8078269c1b2fc6ad145a1da882d50d7dd51683b9295acf8b63f88ec6dd97c1d79d6edb0adf007f4dd0556d2ebbb749796ecbf29dda7b2922267c92380a6e4e7d48fa82b089d62ffc2ad38b26f70017c22f0741e228e72fb118c6cd731ed67ed52575488fa0561dc5fc3609620833c4e4a68a67adffdc39928455c852ca62826df68bb8511e83fd62ec300b88f39d2a0e0023e726142834021841280bd9a8ee8040220d1507115543f3b3e2078ad6399327bb3893d87b8372540ca02c0bfd51c96e4a38f62cad7b89f44de9fc04cdcea00ea502d5c7d4e1a20ae611fc60b0f116208673a6856528608a42515cc0904710fc057518fbe0ab829815555fc85d3d597189943c98d5c94e8884aeb2858b1835bcb6ec7f290ec2587e279d7aebcb7bc8873be07a241f08c6351340ca62801d096dc7556cbd90afe63af1c4843a1cfbe13118a2dbf5fe4dd2c7a6d2082afdb6ff649a85abec01c4f251ca374b4f8ecfd948300d075e3e6c67294d245ddcd7dcc6d9d85722e899f2694f893e746667591dff4db59ff501fd50108494a73ce45ce18513c6b8d73f1d30dfe03a143a5740afd3853aec364f45a0d40edb8acfad59869ab3896555412d7c9e6acdf6ee6e4bb6e4249facc35f4a7189bee782dc4d9a3ef904563656075b5e254ce257df1db7a352c1fd3f133a4fde19f83430d76a996c28e92aca4a7d6397a81af18235a80a83182aca32cfd67826d5127a343748fe9050e99205cd4ecb5432292e34fc4d7384a00ac6c8ae86d1788617205617c2ca027918acf16a907a45eb57c3aa1fdc259981a56d8ecd65e6e16a0675fa5446f1b807fb0fcd2439170e8313778bce2676929c395548795a9faf92fa0b532ecb13b15ae71f58037b27d3b223b176425f2a86f48cf4792c3842d9f28273232612d2592031a17bb5b0a8c0d69b58aa36e9474d9abb948a6b81937464f789828be247534ffef26a8163e5bc012037a781b9f0085897232cbc7eb498bc9f43936d7c70d789c9a99332ba74ce7c2a21018b43e2fd5d9408ab3a2782170fbf56fc58794e1940ccc350a450458d589eba9d8dfc8a6b30238951cc20cfcca0b334e968e9922d52466f9c5166b4e170e27349981bab6b6c4b514f515378e3de81c895496b1368d82be5a8198a1370f3954ad9eabd326b452e53c4dc4c8f9c9bc975b95bd102e3ac2140a3d069ce2b7cd87e88bc682c84c9e2ae70f4fb8214148d9f139f0a4719977fc6787ae2a420f5c0be8deb8549a8f5fd2ceda23f234cd9f7ff94da8ec1823c7a5a472952f30e3b87e168eae7ec17a34ada7b48c9a0a3305b7125048f31e9d6ac2eba59250abf51fd12b4932c92b4de3f8606889957b8f071659a91c3cfb603a57f4dbcfba51717b0bebf6f42b1599f2fea5dc4cbec054ec607849d2596673c7e226b2a05c00b28dc73f3c9d1a872c9fbe6671d3abd9d40d04076fcd41194458326352df07147c59c77ee7eb2dfc51b93e1fc6bc4b997d99d61f67b91b3b50690294f5ee5e0a2e0753c8aceb6679fce059762eb44d206884516b2cf39c20a260d3226977186ea22895a6c691e434fb39220bae91172874e5c22e7887fb88ece9259a509df7bca82621a30bca8ecb20c1bc90999840f399bb779d97b0560de369c41cbc53d48649840489a96b9bc774c93d7f588ff9dd4001b8140a2bd5f9a28b5a9dd3bb267ad22d918ba0d04e90ff6014f2a9e008821f0c44a00f3b008c146679cb27c274fc4564815038d53bbc9eea30660c9bf00dfe43e7ea5d2c0b5864ba1c96c28370a805059b0b52be5bea140c781ea8c847c1fd9c41c144e7af88d3b45ba92355486dec20d6a62d31567b6de73d3cc6d3e3a81930cd802de0abe26dc054bcdb2e990102e97c02d37ca64dfbd06375ab80dd07dc3465a8b9219c94fc02a49aaec29d20614c875443373572c41ce6bd03ddbe7655a324a5c40181fb371394d03dc98946f8fd8a00258baaef7d2ce2d1e1293c704de7e9ca9b2a224020f9bb4505006e7dbdc178649172c7bb2a2fa05529078bd0ac0c521eeb34218955d7b7726f3ef67fb8c8f2cfbf1dd31798d24ad29a7537a6793bc5ecfad1150b967a0547352363e88896b8805ae0d1ee046f3c4614684c001ac2317e34f21964d6d0fe56c9178fc2a80cf0ebb8cacf00ed6a8e9b296955baea4e8ed736146c805ffd3b11f0c3ac73adbe8619a85eb343e50f2f8c71490337d1a6c7b022e670f996b53b55df6fa9d751cb36f5729a224833a9494fa99d036a7cb15e077275f18cd745b01e5ba2ab68f6fc5e512b9b59896013a60382a9032480a855736f8be3329ca9f80c79c23fb197c4ab8d704f3b08905f379c78bd6d2c2dc02a20e8c8f1182af2a27566c9e318f7c067e51531b5da76105c545093596021b84d224461416e16404e3144f2cdbedef80092b4e79611b759ba3d9d3bcaa49fc514ac689230b97b5247ed75d6ecf4dd728b30689fd5b68ece365e9438b214f4d7b068b703c5df04b2cdb9c51824b5e35365e8e720a6ffd68979808582a031496c7a38b7f98a767e24235020354894939754aeb5e4b25e0f96ca4dd9c71bece173ca0abee2f6fcb0a6d1c5916acd766ef9ee067318c0b4ca166c069fefa748951983f1c65cdb4163339bacf370a7c8de7d6b242d7b9e8db9f514845f26b26250d4ac15d3c83b4ec30515b2faf5ccc6c57901f8a8a09a8d490a552cad227bfe610b3b94d334d0311725051f028e468c3e949e22a75aa0c6ad67267794e6ab36dd86c881e5be5733acde7572c8b9ed3a6a0291aa67c832e32adb9b1709454ad12e3cd5cbb4ac9c6fa4f5d495ad4b1b3416dc7cc91ea5f25dc07c9bb644accd06b633d678cabb5d6d300e8dea2b178553b1842059aff1794dce36a3f2553956ebc1e69e830e89307509ba3cab81021868f4467be4156182080bca66a79579fa9b25fffdb4ebdc2a17de6a3b25c0f630ff24e6e3de29801960d526605557e211ba4d6396e077bd0083ed11602d95780724b8ceb79f342fe8b3bc2296a653e3bdb1447fdd742435f69324980d91c05b7a57b4cd4fdba89650fe928117a27565da6880b17507f18636a29f1ab9cafcc6da4faa31139df554b561b5b1107303dde8497d7fc3ab399297a7d6438a6df0df39ee5019da39100e19b14a3eb75143d42e2d4f175c23d4e0050b593cf4d4cf8a9d57e57994c36558bd43d24526006fe3c016918e8baa8aba3c0698d7cfb4e0469580eda73d06bc534a9c7c41cc8f596f18da082fd17fffa5448000920bfb10e6f9e5e17bd358664fe895fd017082207dc819e4b8f5f48fd244901b26d12e5560f78db9610163c3586513f50ffba48e07a4a998ae2b1032b2efc25f86e63abd34525556691a4a45e22c96894278d45fb3988cf8fe5aef26c0f100b7d38213f0dc24fdc2135f70ddf5414e7f0fd40fb37cbc01c7cd596818e97c3d535346397db8b90a52a6a6884bd7d26b49e6f0bddcea9412cb43a5d6a3ea3631e217eee162ea29586c84abe88bb86ce478e8cbfa6c842784bec0bdf2763b87c5268b339c17d25914adc478aacefd6c1d36109cfba9cfb809cf0bed09b067449a714f430d3373abe11c6063a0254e65de66979c70e2a26191ed3bd2702235ba97491a2d43d1bf4151023fe184e733400d09dd5816a790ca877e8c14e8a020c04e7df30ce8522f690e982347dc54ae4063aa8ff46916fc4724ab3a79996985ec9eba376777f58eb85ae4c35dbe52c125ebbb741744cb340fa7550f751142d63e867b039a068b25c23df8c6bf3ed26b7439f5030f27f3fb034fd3dbdd4d8100d4dc8bb8d7609018d993a61c8c368f82cb419cf77ed37e64636e6eadc866b4c8dba96f3a68dd45fc2de76608a19651c470f5d1c77a9581c5ff68acdf96ec41a83e8f53ed58542aa11ae165b95927aa59085b432a8ed310306a2aaba1e003b303c2ecb2b8ca3d8462fa07e690b124e3118e63f3f188c6cbb12b95a23ee03f680a1b1034d704d36e0c72e98d083d4f5c46f22226a78034664c57fe1632d53362e62e2ded1b00e1f18176e08a0934d7a1fee38fcb6fc8e2500cdfd60a5e845e68bd91b73ec2fbd6e9372733e99779a6657e3468db4fd1000fc14c09b3bbe85828c5e34519231f5d9570d9c853825cfb9595806fbf8e8fea108033b9cbbcbb78fd1e8246723c34987d870e7f2ca6633bc42e07d43dfd07a2bd0e5d30abe5044a4d25fa05b473932c2edc0d5f96ffb2c925060fa95773caf600721407b21e445342bf07e5a8f2aa4f34df6eb0ccea83d8cd3c925b9cc4ce143d2a06ec266f77e9af83fe323fa21f8c90128ad57db0ce05f1482b176c506f52e0d3c67229609b40a350e1484c3025989343f8e1278d72056bad106bfc25682a9e26a0f4d6f48590bce6d9ae2f60c80f9b8a74d4c30e293de64f514ef3e92291fd35d3e60cef173d9807958b7af3264419ab6ec09e80d1ea8be13a11f6f69cd527c793e6d2ce8a2c25dbcd2b7d83c0210b08dfd7d6d0fc787c356c2a15c4822a9b52e63e2343551dd1d639400650a6e0a3e180a61113ed73513ebb307f972c5fbcc5cbd54853763d216d150f9142339223321830b05999c54e48e9c814c37a273331bf144d6398a4aaf56ae68bae89753374cd4c41e2efa41c111382f1bcaa86dfa0186d89ba38f620a467244890a9821b2133337adf038e08342400014d071762feac6dea073bd1346f9619b29d797d8088430b24e6be9b71bf3edf8c7cee888cd8826eb7859252ae338a6b84fae4dbeace5c1a36b19a92a638a85e64f57a02c2fa746a426427ad4e6f0e1e011a092184af3b6c30c24a106b4cd0c8b7738b81ec0a799c72b7202ee950c563b880fc8bac35229745683f9ed4014f184c70155e12c0bad6d2b1cb18830fa7f5e6eeaf461932a6c01a00a2a6dd52d9fa4ceb86689c7037cb566b060c1f8adc13f7f69a4197f0c0f384398b1dacc05afbf8192b99e2c2c11e2fa8613cd1380a81a727ef5a1f67af209d01f4c766c8d5e64e605bca85b5ed0164b009578d1c85d3afd46cd574e423cd84f4688da8642d8a3d89a69a69c61a365f30ae4a501eb7f07c70a27017f945baea07bac1c02d74eea5c4955d5c1e55b6bb1c63b2204938729ad61a12f278937de63846a15c58068bd09e616d689fa8acdc4a75cfdbf120324e0461784009b6ba78fb268e020042ce31fe179f31c71984065a7091f713eb5d69ab4eb8cfa0ddbaef1a1b7f0a10f3c837897d0c71d983f1b782c578ca16205aab3996abacef9b0412aee4d6359371045c3d66359ad310f8de7c40d9531aa633e18f78e2f1d8107fe3a0da6232f35f0c3a86e1f53c7d96a55207f1f6c24e39109ff818b0854ff5b15040c4bcbfdf417c78fd47987bac82e648c0cb935bc4e3eb47570bb1e1b7dfaff1471cde0496dfe0e9d09e537bdac3ebe115c1c9006ebaaa7d59cdb1110d822978aa59826eee128f50b54d8bf1fc6dc613fcfaaf7a1b0dd8e3e3f0b2ce6e7c33ed9a20eab25d029081d7f4f6b1df2d0a7c1fcf01965eb8bda852c10f4aa660f048393fe29dad8b879842fd5edf101dfce5a296938a5fcfdaef7a0a378f8d5d32bf11777e4572ec7afacd47cbafd3551c5b15bf78ef31fa8a4869835fbe0d7b74bb68fbb44b1fe37d1449ed93f0bddc4922c435a3917e149a145b2c40f73066da5d5cc20ad7c23aeac8cb2c3e0398f036794c1fd2f717b642fe52156d57529fcd59db5cca79d27111d5bc31806cbb86362717c4156bbbaf74e9338b8595e3f7adec1a4507014c49f6ef27036a80970206644db95da4168812dc45daaae9105c8fc3f316ecac04aef0f0b81a2c86ff231c52565094a5e15a7213cf23311c52810d71b624faf394318b7fd8aa9cd097dbe4a0d1168b8a5e152860a64fe8929c67cf9e46d3186959ee34e132ca617810be2b5e694f774d6f2d5ceaaea9066330f88d1bb80cc262863b926ebff3ec6884324f8974f55cf85beaf9e0fa590a1617c84750b08ceeb85afcced2fc72ecdd96abc51ac19275f264b43682d13e662f140cba6b183533abbb0ace03ff285fad913ea5603c7e7785478e2de4db2a1039e282644328930261f4d45c94827fedf20c5856029629f0d90117e67501184b56813b8c571936aaa5706a48505c9dd1cd905452702296db73b5c20b7feeb0ac30d5e15d11ab4e560ee006f4665f4d3fec1df51aca11370134e022be7596a388768acd554f103b9248fdb8aebe464bf21138b54d5b1630cc668246e04fde13266ae03e8535b283eef0cb178ee6442ae42f390d2e45bda23f95dc3563b6eb5d2d856a249bc9a653fec60e51335ff75465ae47985dc94b8a00ad24faf5b4fccb6adcf070f31ba5410aa54d14d7340c9ba2954b4e4a4ce28a2cf6b16387dc489026b44672380d52f6b93915baf19ad17b28346a98c815d854bde4a662810f4f6843a6e215e3452314533e456d3395d6a2847716e806f7c29bb9fb9e0830cd9b26bf0395cfb45d5abc964f02824543f3ac3ed198cebfcd66845f70b2ecdc5acef7160e90932a6897ba7f4e3311c55fc25939777580f27a074054f892cb7d70aa3699604cd17e797bac4dc6e40d839e83e74d443d7b154b81f9f15a8d9c50f2a9b595d4b954949b3ad8fc4fe908c58d8e49c83309b31ad4c8d2a09dbca23758a1fd8517a899b302b06ebb29af290b9f45418a94f39bc64d9aeb659c6f776d2e006507c20030761da1ec1056ca720c5e7095be0589cb3e91f449a16f64b82af2926ec3534fe7e02ca454447cfe6d07983097d53cb9135b998084be280cb16e11f14ee097cbddc544a5d0400352c2a7b9c99be875f825227c0289a74abccff8bc4c8dc0b2598ca1e954543e0c6b051d92249e9b1ce23da2dc933750c6a3bae50ea272c6c7a533f16aa7193b477a32c14ed4e3340b90dca06350f12642f9ef212e269af5dcbf876cfc7841651c28627a98307b222f109fea54c02621b413703820965990aa6dc30babd855d62f36104154d97df560d5869e54aea142beba8f407ab92a48a9f46fc2d3e431f0d93392fefd36a2168b7a8a1e7873d40c0139ae409f269ef7179ad50ba5a0066273ab9f494ae418dbd0dc7c71d70d07250d84184a4a053bf43e06b218106056996f617b4c7dbec1b06837515bd50a614e6a1186a15473ff849bcd9a52644af84ce68f690e8663022a4ac7083f7834f5e4824257d41b3505b93f5b5fedcfb7eb4ef71a8407022fd9d83e80e11eac1f36b74248401064a41e2dc4bf464c019eda3acd340199c20c21f100c64cb32c1024188f76a9d50c66dab0bd7596ba1b9bcdf88a24cd07aa06344008f419fcfee3836950a0e42cde4d281bfe21c6703dfd677c7129cc593515e7b6d47768e4c1fc17f772ef7fc01f7795ae2989cf343f512fb5cc68e6832c3b5e0146a1f47c16e1e1d026de28f3cfd0e55691d801a6d2fd9c3928cea56adda0014a2103a16b4506de81eff5cb99882828a715f0fb8a7cc6819f7476104e3f05d0891a1ecf5b30320cad0f89e0dc373cc7d840cf4e1748250f631c146f1ef9eb6af840fdb91124957549b2990058557a2579873df9557c384d957b4d04a225f1c414c9a0206c8283758a9befd5ef7897adc2fa52c32a11e05f38876060c13496df75fe4c1afa49d7890cd0baf0ea8746d9d8090d16faa65e1a922efe81a5375a22ee44bcc2ef20ab89dbaf6849b48c295602adf90bc0a8238ba1e62b9c72a22594c928542a601073e1e02193326495475e93c944e13c312fb64cbe43830fe4cc6156384ed1a9e43430c3ab9d78451489d3ab533dd7b7015cb35739b86ec3b235057285d03daf0e1948583745b863b2e78f51755f1bc59c5cfd59a9a89ccdb2595b29fd30de4b8bcab350b401cacec38ae0d1389401f02bd07711509b3227c7257aa8dc62d736fe25ddf6de34d61b30a9c698c0c80c31f2208dc7bc640021160a6cddf60ea9dae52a1139fdb5638ba22b4e0c7270928713abcc2efae97aae5fa1befb5ec0be1463d639c902b7c1d9de93816e6f6c90722185e965d8a2b1ea97c084f08e15cbf3b1ad88d8aba5a865dbce42201e0e0679528610c6462635ee153c34e1af8c277a75617fdc0d6c7b6139ace69dc6e082b2062363400f3d15b1dbc7b413dfe3b09102dee17e28d58841b9d84ff993ff35569ce518da5ea68c9973eb5afd7ab37baa8e94d77378a0f360bd0491f7fc3289a76ffd6e48aa109e80f5755dcabc42ab00a29f135894f3c98d832f99c6aba833f11610c5556b4b5dccc7b7ddda1e6ceabf86316175ba17a2657be27fc7384f7594202e084f055554544c7b183423bcc67abf0667888a6f4bfc16328e43094b52ee80709a461241b89c80a42f72c3dcd1a7d6332e42554574e610194b625efc18735ef900d2038554ef99b55d635f2675691cb1c33c37e3ad7065120631f28fc0c024823f5625a70fa507992dd93590df311c8c7302b388976e05650b589a77675ea569e5c6178731c508d0e3501dfa4a01b98c4e2474edb35677c22473a06f482b64258098ce1823985d55b6a1f673f58978a7df5b00a8e860139c46c4265d1d419f80541bc4c07ae44c8af3d36a0a6f4c66f57d06442c462de80a34a27289b8550dc9de93c15170087269a5d701124f53a593e07e1c109458d006c979ac1329e8a6b67a3c6482fee0558152f80318d39316396943fa04b39d05f5a4545c96ed7f6c24232956a3edb184d303b0233160c5124a9b7de0ba6197b88d7c559783b2d454ca15715492e5a03c5b70749a38ca04aa0f93178cd16db4a86ca40ecd70161d8ac62bb7a3b6b223e4e0dec3d6277a12501bd3934fb955b95144ac4e2a1216a8bf5429a678cd0030faa729aab96e925ac0723fd6280146ca994f02b396a484f0f7da6351092356c485081304d985285cfc947df06860da294b378c22ec7d434da231656f1d8e11517bd4faa7338e0b0ec295097525f8dc8033dd87ce125461cd6c3023aac3636038b2b5ce827e529bf6eb1299efdf851b49bc21f08dff7b8043a0d5c0710c6c2088a0354116200d64e820a0d97e7060ed04a46198f972617ba05325e38e655c1d085044dc439a570d1a6c290cbc8eb06901d4aa79e9b56b5198e4d4cc705c837d0ccb07d98c40bc1c56bdfe43a08e2c6b54a975d3c4cbf79c1c7863d9e2c9d9e42cfc8ab7386150128b129870d4a35a4b9c953d3383abb399b958044c6c6f5d5cc9e2f3c2e11fda034fab14757a38d545c253e1efbade8d4c2760c503c4c0c5f87f359109de20e1e4b5f1dfe3e6de9d069239f920d7b370c5c2460763ffb96b49c592b6be23a86084841ca81e34f68888b484c2dcf36b0c3df6e809e10a5bffbe4a5d74a6ff0cae6bdb9a1a2075d526da2ac3ad9097b31aa2e85ffce870331fbb3a1bd01e3a36923545c27ae1ead8fc73d3451278b2542f725e5a51ae4aa87b002c969f186084b9a50cc4f40cacb72b6ce122b5699957ad128fa91372734340ac72511aecb13f61da00ed925dfce41366ac2545d6130b33a0278fee2ea0a7c32af5d0b0c71c48400b34a91546235121217037c32eb020236346878306bd1e76d487ba485960649fb30fa33230bb656f12f7205a82e84ea29bc373295a35c54770bb7012666bce559981a7e0d555dee2a07c5a2f60b8d9ba2ebf83d14e867d89d6a9ac27e0c83f33fab70e7c6628e5d8d6a30881917f643e58903263e37283d30f984548366967fb911a82d41ba1868b61ce42da0b572206e89f35628802abc2e4bea45bda1fcca63a29b227a0f46e3f53211f98f3e1c1f441d0486d5f87b248984b71117b6c9eaaaf415cd1ce185def9b186f2685a4cdc71ab7c55129a9206edb5686033f9222893efd72e4ad683cca4ae5587e1ce97facfbcf8c8e4465f117b4f4a85b4e49aa8d8262d2cfeb9a8d50541dfcfd15c3f391c4461ed2904795788abef4b1acbce69183e4127bf8f2724eb34076007bc825ea9d85e6245e5fb173e5ac02696160bc5413e3753ddcd582be962137f2fc47873e01623a5337602857a1e3268897f2a6565eea0fd5668a7de19e6eac167c4f0c1ba9d8e46636d2aad92076fad602a24093a7e76c339036058142737214ac51d0d2aad986a66b7a5634e6a566eb94b5c918eb8bd61563569b883044e1474cbcb6d7fc7e6ce5524a9cbdc998b5e1b377ee616df9017c71b1aba349bca8a4661f9bed53952150018b0da1beab3832ac4d2016a34076e2e5bb7fdb6d9888a0f6d86a89442d604b5dee5492001e5a9a7772f3333e1591e180ac1fa9e7db83801c0e137469e8dc1ae3f3d904f97e387f68507eaf57a1e8a5865daa9515a7158861d7bf97cbe4767f14261f3dace443ec2890c8bd38669f33ee152a79d250ca87d14c2c647accacd88cc59d32188721f73485cf0a14f5e780cbdcbc6ea66c887fa8b98b37461968b4420dc8ac14cc768a01c4caa47bc874394331532226c52fd3b9a423f72c216802e5a55fb017138057182df99f812166bf17e4e35db549d4bd600b95fcbaca21446558837bd3cf191573e431e74b7839bc381ad7f2a48401f6a3fb2fa7861819740122732ca7231471ad205b62ad68089687bf42d9358db211b286589230285bda236244307de6b8429cd38e22fe2849b2eb157ae4e80aa34375ade64525a2efa2928a5e0890980a7d968d0e0f1c338058ffe312c4ed081140456ce3475c3389a02fb1852126f61415e01358c7331bbb067e4f643007df24a40a7d76aa4075e25d59ee8181fe79b5e9eca3b98d5330106ec63efafe35378c7cef87a5e64237a9586e9c649d820756bb388516de10749f2102dba6d3264fda34c68be53a83b802b6477b30c041d45e6fea1767b2867e081520bf2762125e394e232ca002af9c9ceb0683c61ae4a826df08b5379ae00c06d08992bd0b38ca495c47c223fd4a26bc00de39031c12128c24fac105c83ea3c93347e0af0efc05dce46f6cf092eb30c77697121f71ab7758488cdb3898a661ad3cb6e7d6357351b2952a9ef96e331422a5b73470c7f6e5fda1370d315fcce683ce80e68dcc87bca0028ea21f3fef946c2d61996deb1afe9c3a58894fe0c0e6f241bf468514f21727992f1e523c3cbaf2101018c8635e1f776204738e70c9c8abbfb3354c36c3c614db5eac0530dbfb7bd694579929597e44bc84acd617e892036c9e36d0609c1ad50bec83de004517a53ee2c0596dfaa0582d660f9f3f7f191167bda0328a10fcabd9fd5c5601c693f20587983fb570f5a68dd67f0ff7cc9e97c1c2767701a1c3191c88e24aeaab9bdc59498f0f65afabb46e10b39f5aa5e81cf70a4d09de984f2bf9d14119d4adb205fa3897d8c9e870159c49a33c6511ba633348e0d93e17c1a88a3f6259568f2b545b94c45add440e6839f0a29872192937c50f2c1267d965c932def2f99a42878f1d74922f2d8659f3dde4bd40f2949006d402f366924f187c1b82f1c64031e753fccfe1f58d01ad244b6c377eb116e14257cd88541e204edd5bff6f6b36f39bad969c8eb789267c13971c26562f20ae1e69021fe4accf5004e12ac9c89e033cbff3807bfaca5e62ead71c00449bdb322d94ec3becc02f887b4142c8bc5ff0d70911466ad3fd0918548c6ac6c021238c2227776b8ab3b6cfbc13e16103713a6799b0c665734c51ee2dd4c112b16bab7ef77181117d5c06208fcdf9a1c516ebbe080c8e99361054dd24a03c14029ba09d2f57a889e94deec7ede478715cf84e81128adb840c43f20a9c2881e7b44b89f388a727c861da3a161f2b53ae6c240861f2d4db5689d17fa41662e457210427de6e1d02a1c6d2dbefd9a368d4049a019df4daab732e47850efc85f6e971b35209d48f8c843d88bae15bb0c24ff791a383277ba6f386e42fa01b6680138c3eeb1b0c628b904bb4769612d7c7b100a9ff1fe663ba2a4fe9e42d342628c971d8cb2af63c7c20b70b0c970392cb3349e588276ce52d785de7124f3871c7c703444206fc83ed910611f4a6626a634093c22f11838f25e87e7267e0e905571598300682433696cbcf1446b453eb8d5fae391cc2e3a24b37be035a9609815d80b04ae00040e00fd4b5bcfe63c1e63094115474d80d02977a36de78565aa2b7b3c39f83bbbb4866960d276f40df8671a03f09dde196d425b7f5ec5b9fd6ea3bf9e5754e19a4c3ea666a392dfb799319a59a84be306b7e38d3eb5896f193fec15ab6b9902b8b7d6f9287de8aa612f0f062f2cf1e338ce677b14cc8f64088608b04df666151bb17ef395f183ea437635de922492c78c7b2e51e93b5a36c6b51964fbaafc8e948af426461bf44f2530cd5799236a901e958052017d4fe370acb2ecbf13f9e62a16b9d6fb08701e78f0d119ed2919f52fe41348ef25c392dbd5b59fb950850e95b269aaf98c9b50a14b7d22a88b0c2ad8fa3fbfb28084fb2d0c3a5333399be4682ceb82449f1b725f109a1f31187a6346395b228c6652699b113972129c5b9348b96a21bd065448d123a5a7ffe4024caed0c74878b164c10968141ffb978caa1ce1521e9558c8866d2c5563a0582c08438fa94f1d1925c44842ebd7cba95305362634356053f55433226e0967ab2a8683973b94cf4f8d570d86d4a80f08a29b6233bb91e8e9d17710774e1550bba0e30b0e86ff428de7f07a1dd6fee4ff741f20916eca046ac767cde59ceb17833d919f2bae14789202a4a72b17b6407a88f919cb9e4349db80da083bd0b91d3e5b0026feed3176e6606af71139dfc2fb6bef0e8c043062b2c43dfc12d55d95b4b997891e10730eacbad463d3b3a53de0cee56dafdf38c9e63acb6874afca66b2f9ce857e667b55ac0f6628a8cbeddf9607bf1821eaedf7b48ce3e1d8ae8b0fdab7fcbb5970f8e78ba516d5ddb574903f54a694e96858c507c3a20c26140bd826cc06145def7e9fbbbaf96b41506a5b4a5dcc9c95151ebac1018138f020212769030c78c950408bef7be4be79307d9ba30bad7983fc331cd824f3ba41fbff8a2c5aac279f7467d2646a865324e9c6b31b1f5559c8e7581f8aa173fd7a490c7b0930094ddf7243c68bbe81d878cf91858be49b9ddb07a4792ce0b29f78f121c5d9914ce8c0af652f7bdc917055e3483aeb1382d5aa456eeb4b9543f3136781a3c0527625c7ade4cf034acd49b52671025889b6e45df77cb08f5d87132f74ecedc6a5b89d3f9e48857fee5c20a4fe1ae05ebf22f28fa196725e376d82156a712652324283298f4b4db5a69c577f6380ad1152a20e69110e55f3c14083260b274f907aacc5337af4877910a67adc838d8b97b81309ac1c8c6a6cf0ebd1b55bdb4522089ed72d52042bd51494c94e90b48333f1c6516be07b2c58219c84de164fa40b5dde8482cf15216abce0995517e0c017d2adf0d8178d428456f773749b552c63b335332c23257532b4da802a3ce01c55ac720a737fe7fc52938a8e49603eaac8be17552108af1197ab89b983b40c8a821ea4d38f2859cf1b9b636f8e6dfd998c4bf16b6eb3eb985a318093b9859abc8b589b930e89749f0242f191a819c7f8a3009fa5d70342501d4726b8020ac8fe59afcedd50f1a905c8348d3ed6f64f5d9573792968732ae566d4a1376cd5fefb413307115c07701363de570b5dcf585f915a223754b7de15ca113c98c9a223c1f182ff1f3fea1b84a8e887efd6707e487078c89cdcb9a6f0db65ed8b7a6e1da3397f84383d7975ac4c2f6a842470d1669a3c52b6c311453c4a4f6127b608edb584a3fcfd832882a8af0f7436e282599cd74f70d2e8a8d9662cad1df16fe3ced1c2955320ec41484bf7da216c53b929f968db7a6c1535b58ce69561453cdc50fbc13934b2a704f1c22c4bfb4fa7346776fa2fa227335611f3d417991d0233eb3aabb68d4e86727f7b1c17edaf1038ec88060c0a18604730d0a2d897ed51d93176678377f77ac04d4938a3fd979da44b85bdbd7d1627d20d1c40dad0f97d3f149cb61cb0434a817bdd967087bb4de844fefefd8f505fdfaa8450001c82443c9794388c4b4a1fce7373123d7543c9bbd977c6e4c9f7893921c49dac76d89f4755a3180017f3f823c511cfd9c55cd29315f218ec4d4dc7c2240b23347029fc69722b5166e620b0a77e175c21a008955684310197f3aa40d4b9b255cd12dcd6b0efa41cdc044f63a8233a8a23f53bc81c89cc1ee4a2ea420fcfa023a7ff87cfdb3b4e202ac350d3d128b41f9a166914b95bc1f478c6cd1fa53921f8ed1fd1079cace20f4dc59fd7977b7a9ce85c5c10a3a29688c39eb0deeb43fe57affcc133a72538968d3b249a8c52828ec54891c64cd05335c1a8ca76112455957b0417655525ec39145615016b73f02bb29e08c4e2e4c4f7a2fae4002d9f03355afaa748033c911793975c2688c6f3d94038ed5db3150bebcbf058aff4222e03b21aad5bb1e8072ecbe715130761747316c3f96c96760a3da457e049bed5c1d402f84fff87e38335985053fac75dae624ffe4a8da6ef9a9a99006cf6be80503e7b1aa2b3483ee708490b1a1b9f75c514d1dc8c844f685f1cd2cb4f0f719c5df87d0adb78a025d2d5d16628ce01d0ec24a95c202468b09dd973389f90ebb88e19cebe8b1dec8a1c9ef5f39fc1eac4fb69bd0debcefd884ad736f748f8b087991b45f026586adb98e739c1a5a740f75f35e397d9529e81aff7989630c87cbb223484c7a69df08b41a815e03a16b00741a014d83a0d3486834068d86a0b1017434001d8da0ab01e86c007aa181cfc48d545c1dc9deeb47eeb64c48827c24eef19a2e4f252098a4c2150882843190a340901efeed1df07d46ece913812b96033e43accf12f319627c86b88f11e723f4c967c4f988583f1113f443207b48ec81533f781c18eccf0ddd60796c92d01ae795ff3f079cfe0ee253e2f8409c8f11f333e27c86389f11ef63f1e14788f900b13e4adc077ce2033ea6afc013fd4f65d6f81734601fc7083a0ace80677d87d9dd712e675e8c4a83900cd69a96703ff3c1a7d377d867b45943b81e8bc86f45818bff5c74946781d7efe38670a892cf7c469c1f88e1136291845f20d8fafc73e27c402c1fc187d29f037e86980f1e31f04ddf6206562c23f03afa1a98a82f380482096fb458c718e2d9fe2b9ac0009833a0a61dabd7f61373f27b52097a1c924136730ac6afbf41bd528a106848eb6dd3ee282004e2d4bc2033d32cf0528db1ca91c7034280a35a675866b4cd36c2c5d7662d6950e57d7f5717e74796c6f0100790563758814505e6f49da3dbec26e33313130e987d56eeb605f7ec5c229bde5da7712c15254902e7940636a86254b733ea72b9de483fa423de4f01356140d2747e24c8d62d01aec7937b61e3309d0d1281121043a7f0b70c8428556ca2cecf0b9a66221f554da7878afc0de7d6f463b968e414a6ee1e608b62797e8eca728997cebd14166b3a93834397a3c013d22a58c6b9240804da69692eab9771ed9762b0afdc5058d6a417a32f180810337a5f5577f8d3330a82487042e722ff0f9049fc44ebc0f8f8466d9fbfdd2228fc34a8ae36109f0690fb4e9e0d9d61612a291d814da96c3e73a4086595c87e9b9db02cfe50eec48a6e716f3a768a93c4089285a81dc1c8dfb6323a0e9c15e1ce102c2532e1f300d0203211e1fa5b818351f1232129b2b96662f90b55b73d388dc8fdf9ddbfcdd245c365150fffe71c80018eae1443aa9deb300a18a263bc141a8d44044d3ea4d432662b676cd3866f4505e647bee1c2b13e99e911084b61527f4b299eb47ae438ba48f124abfce86bc5bf05d2c2ae8c46c0eb19bd5860042e35ab006399d5de4afdc456ee0ea31e2afbef9366fbe40ccf18c99b86e97714a5915f1c7fad70258819652fa88002543777080320201f845b4af6e16598f2153442c0ddcfe3e5caf9d0d4fe46242b15736db4d93b3473d50256a674407bfebb095cca21153a90262e7cafcb31b485bfb905b4baf85ba56af68526434395ab95d5ca255e7490bf0794891df97ec16b65363f12157bf93327cc96e4faa67bd4dd0268f476bbc1f52537608751b604f05b27013dd9423c2846a7f18a87a5332503fbc5cee2f7d1aaa3eefbc33ff09f5c03c744b6ce3ca89d069b3256e34e90da009b4b80e6692ca453a347ae8ceac9f4109435d84bbaa638030b9f52672f827e7f8f354738c256c9857325548fa6010f001c4360f92bfc0944fd7ce3956430ca7f8fc6906476e0b03382d50df95d514788f9786b35c1ef91574fc05d5923f8717e53b4916d264e35353da739fcbd2c733173232a904c7f35a5ccfe9a3a47c25b10eeb4b70eb43a373a6a4f151f7bf541d3ed2d345192e3deb9ae71f549740999b331b5ef9dd5de3104274e09aa3da078a5737dd47971ca85a0e204b61674feb8a0fbef68439db5d8391a022c6b29a2228577e598dfe47c78e5b87be57116fd43cd678123de94f31a76eb5e2e90e7e0e4d7813048d2de33e3188b14d49b110abd413720460f58acb29068c1b97541819e2462491fb0bdac2b4b9c7ccd0510bfa4ca56ddf7e4eedc016b40a0e6bda47ef15ec3c6ba97f5b9d3325767331033cba04e4fd81976dda034dcc65b7b6559d3b84c87578241a377eaa30675be3a9e7fd4ac14bab71445b09ade4545ac6d09f2251853809420f2f213e3431c30e3c53ee07659483a82f100d9394ada5725f3aef2971b43b1c4db576075c1498448bba65f544d52345f2ce6de58e5831d483be3bdba4a9721589fac4b548618529972232afd9eb1a0890ca9e34adfd414858e380713426a55cda58e7bdb408daf74d84255ba8edb373a372687a322b3578c6005effcee9d788259859e4f5426e8763c0bca56a3beb0f2e45206ac3f87f3ccacc16048fc284319fbb2994eb1fef8e42e7bec0a21d47724579ac46186a14d8c0fc1ca7f5b0e813e41f01ff74add22300827792e55d6115658a1b732936b50684f020ca64a47475717738b7b4c64877eef2e780be6f04030e88edc68c61293d814dc19b5fd1ac882025151e01fc15c38ecc0ac4016958c39cab4772766089151308fc9231fb8fcd05f6b8f2d831cc502f19f0d32ed9de1a0cb884e0a021445529deb97add21487bc3d25fe5af01cc741215e16e5cb557d717e27e21acd38e717e7433a4502207af456f4a3f9cdface82d41de38c59708193d5e70fe8610800b442792255dc99dd2085b545b1decb92a0866a3846bd559586efde3b2ebf5104967b8ba42ee8a92aa2c09d0fcb8015d619c05991d4b2ac7091ad461865471277eac7863006b35848b30fc77af1563460e4e56e5f0820e0ae576258b469e36901b6b28b83fbc1a28683c5b628b19c70b83cad8956b9898a1155c6c4e4ec35588e395a515dca903eb781cf4fed6600376de15c047154e6c57e172f21b02bf0da4bd07e01a450721036457e7f8a3b1fafe66b9edc2ed256d260102adf67a040a18989f37cb54b20f2733902157440da253cf0395daa3e6f9427f860771284aee48960099a407d42e92bd9133201d743e429d53afd6b10041a62989a84143ddb84d47243031ad6bd287318fcc213783526cde764df88074a650c5eeb3227d4987e186370890b800a3b5b70bf64ff2b8d8a7fb28d28ace628c6269dc1c10a84022e1aab225caf4f42e2ee1a5951b74bae42bda0b6abcc553d87f9681d9ffb8310230c3408734fbf2278a07049a3ea8e9eee32a5c0f081de390a82b32971bd22687d131bd85da1626b62e25699c28386cb8f52a56e2ca20ce53682a00183c3a34037900cc228e3462c8e3eec577e4dc07b870d8c05e9c9c0dc6119df3491e75c82324fff385473ae6f9081df1042dd01297b5a062b461d29a30bbafbca0aea2c4c888b3330b93114c79a3db8ce7cc0d82a3176641b4abfeb41b566c90c7fd474120bb0bf95171971e8bfb16d546a4acc5953c2585821bce615c5598f386af77fbd49ed8c2b722f2f5fcec73c6618dfc9e37da961a735d71170b77d473f58dd63f9f615e3567dd9ad4b46394a21ddd6cb99738df200352430e933e215a87beb72202f52adf2bb8d0b6ccbe6ecd22c63d27472e556b3943250b396c55cdd3d5da5c06b24af50a946a3b0a8020a37d1013c21ab8f2100bcd945b8746ffbdf764afd35454492adbe0ddd039ae45865002afd400fe72d38d6852ae02e9a7308c914922ad896ca5831e28f2193bacb057f6c16ce8c14603aa51bb3a116fe9b0c9f2f519ed7c28645b8f104c39d0cadcca92c716dc87bc829e8ccf70fffab520b78b06f98276d9b65348c08f05243ac6e5ac34b75c3df8b91e85193172390759f67fa6868bdcbdd91cb10eb2ccb50506b2b06589b4e43726a31721a7d5211eaad1c3913e178a05910ed957b344e87db37b55a20154618216afdc0c37daf6ecc7ec049ce731dc0cebee4eaa87481b0c642e8169c843f175a46dde79fca51915d50d306cd0279c58cd57d737fb12e95330e782cccf2a64b770a005c90b8d60c421fc8693eb4a07c844b9fd4bfb90f0940ede823c849890e6d64b3f73ef5414f84ab7d91c9d21f2695a0392c973d99d061eccabb3876d70ec2b4d916693729ae349071917ff7dd84d22c659bf9e0d8c5e4ad74694330bc38fbfa8e825c13b02c999ad27b43484ceedaf43f57b94240023db5d2889cd19cf4208a9dd41b5ca82e0e82bc8db07d5e9b2b8c71b7b8799d84dcefe4f8bb617ecb355618d2ff081027ef768d7a002555274981cca571d6c90f15b89508cc77b13b28a7cfa80089c2f160d916343159473c3a1f87e862e222e820479b20e7349e8041aeb631cbc9801904389a4b1645579cf6ddcb3e57899f1d6ac2fcaabdcf84d2a978797180fc524c16c999bd2526464c40940948348662cd47dfbf971a5241fcf3d2bafb388560d6ff0a3b24af5c0e5baa22dcc659dfcdaf2010b713ded2783746fa15db3891b160de36ccf75d0bc82e8fbe77135bd3d02fb2fb0a97f1c56e44dcbbf9a1954b8c28a7bf71483c1d8ca8b013baf0c061f0a7269764afc7d56a58c2e76dec2f7aa25d5db91759b0bd95c736737953f298dc3f859cb380208d507bca2cc5d2f07e68f3347f7a9c95d934a72a2eb05cf9792ac58d8a4a4029f310a96a054f3e4f059c9ecceea0777d0b806c297914b1b89295f60aeff1341ff810b009e2a323a168809c7a3c9d42bc4c1f9728a07fe86f1a1346c14a5d5f1a58335ff14c07a600b8af82c6c1fdea2dd718324b254b8d6d630d91234c5012e4571ff0088a7594d7a4e1d6f4ff6023d817110d46ceffa008716cb789fff4d349a38480368299b537fb0be4566f44fe88e111570723a2ed349a8fe7202d2d65c89384e19ad119ee1545275cc81f6c022bdd373198d3778a4be169390abf188ddcece243e317bf13c56f3a34f3322becc1f388522683d11848c88371bd3757d1f3633e5a6d5d525ea4d29ece3ad2a993595e5252770e545477a3dd7a1950321bfd940deeb9f0434833e89360560a08985a0645c1dc4ae495ae077f3e33fd1749ff0e3c4ed2ed811017938ebd3f991a1b7d64a580dd127705bee704f28d301db44b0202758d6d2af900b81895a23e0cad87dbda92082cf5d1a0dde34b571fcd3692bf48329bf9f70743a9811415cea894b929928650e687efcd10d4496472aeb09b4f183d3809a3f29551e4427c4b3da0b7890406213ec2eec08bb3b0a533bd9e12e96047c88ede40896c34b681d66d0cb2e1e85693f492140461b498e185d012b20d0c154c5533ba41ff2e847f0fb03e6c95a5b967f92be976d000020219c386141461afea243976e0a11e0ac557eb5a7389fe5d8543b0b6f03d1201297d9066a44403ac96d76faadf67a723f3a56278e0393ed81e74f25a2790109309e3bba3fceca3cd92564424752384ab71b1686e233388dc0b5e41a70603a43e99e18914d383353bc4de6a3f0af68f4a4b932100c95806e84822ba8d2218db90f871130a4b63e730d51999a7061678c9f02bd454c7640555be1528bc827b4986614c7de3a2e3c3a530c5a658db63248852f0fdce470c9cd455c13385e2cc5d3ae0b81a6d715c2eae89cc0a71fcda65939ca0dc2aa0543fb3e01a9df2521ec6934f33e8816eb3b384f1a89f8061529a487de551434582cba24daac4bdf5f324a3116a0544fda44ce4736c704983642065ce2e5cd10002c8b6939ecdf45de110f3b9b96b7ad8fe24735291ed042b4fe94477a3b91edf68bd490f6e6660d29f2888879bb5298cdaa84719593347e24b50dc2456c86e4c7e153944e70d4b6c5c9da2c69bda99e6f85bea42ee64a8d148a11427ba65ac01d88a5da618e5363bcf8dcdf92dfea2a7ff43a379032b7d8acc470eebb5618adb1f484c0fc0c1e530433e0e2750140119c910b0f6ffb029bd896e64ca2f2248a2f940d555ae169541266f60d75b13f874d4f2e13c7c085d85310b8a99ac048d76a1cb23aba4eeb9ba0857ae142b192884a8448a0761c1716a893de1285171ef9437b510b36fb72e96500103a19ed82c4b6801f0ea6bf2620cc917b1945791cce1e759bb551ff0db09fc16b7ee5aaa6c8c2f142fa67d4b239aabe5b29a0802210178267d1af69f3bb554cb50f59e4613e2c0b1b07f0c1f98d3987ade2c949d20ac8dcf9bb12a2409101c19eb826f95308c6f67fb2f9df3bead24e1daf97db51047987cf03ea907130d7806af171c1f718befca9aac89c194d461f7c71f155221cdeeefb4dccca86b5b668c11b318488b055948d8b339ffdf2afadafe31970f5011461a21e945d8f5cc1fee2a3c24b8da053c61df51152c65823c6040ca4312f084a16f5594cccc952343427885b924f496efc8be916e1cd86de2adbca94c2a4c9cd097fe8b9b28aee4193be84391cacfc94b84f9889f89c8ed790b80a787c24596174920285ab5a8623f44ed5444cfa87fa1cd6a077c53f1442b527975dfce4f933af673c867282ea82fc4d189dce0ef377688925678ad623828296136b319e2ccb6bf3406f524d009eb3ec74a40bba5fa91ea4f69a7ae2ce41c01e368810925f0eb3a1d25c71cc08a2bb31a8286c9749d483a936a5c31bd6c9119b70203b70b38fa82194db9d6a1a6b781a954a2929f82fa92706536b323b0c7cabe3e8540af97d9ca372f43b7b9b4ae58498ac5c595bdfbb9c53c90898503120ae65cf45f78df8b9ec3a0bde777bfebd6cbff0ca60d729d778d053caee68a1fd5a0ababd7141171fe66e6bd9f7085c111ef2906b48cd6fdea07795d7e15d536ff878146bf1291acce8b97020e9d26a8a97830269a41b7c29e7fa1bccd4d3ef48855f4e054a5154bcc2dbc3c5539228dd1fc06261332c27916bfdac6ca4a2bbec0e6ad5be0f538e63083113dad98cbba7c3492ed05d237ada0d8d31b98eb808044e8af81a19cfb3d2e86000a0548fd8b466ebc22d2a562e4c80b394e646e03663b958668ac308f84ff78912204cace66807f341d49498b9679d80c26b4aadc9bb10087f75a053c07d02581c7e34598b4cd410a32fbfa1c31cfbae6f8a13eaddcffebc9c1c46b0e2d937044b4ebe48b3c8ee6ca66121f48d738b8d0a0e34fd89e2b49ef3a5bf5982db05db458860d42f303e95ce929791414b4a3af1a45ffa73a4a1173e810b4e44320007837486961f0549be2472025c3a4bcbdc17288ce9e7b2cc877a28f817f75f5e98d46ec4cc48f033776abc5e3f311239b175d391f033177d2d3585987b393531ed4a191bea21a87011b104746be3136e3bd09e8220f86b73e385ca400e4b4e1dfd8e131ea53ccc170ca8a664c47248ea922af36b07605fc93bb89c1ad23dd889a6d4acf0a8bc7250f3169ca12fab45d3acc082061d9bf16721ae85830b3278c84e5a7bf441823c6bdd71580745210b42c0c2346b1e5785245fcc911af11eb990bf52159827f2a62f251824875ca2d5b4a56806a0615e0f87ea3f713db830d38bebe541e91c8c5c8b7669bdc9eba423bebf4db0995179e67f91b41bbeb848518d64324c7e8fe1ea566c2d7b16fa679736eaca19ed184d4fbf1f9fdff38e74fab13df8151ee49f0f83d24f55ffa7714e58a093c4867d4825022eedfa5107199ce4adc197fcb67f2d2203e11e3f39113c527c920c775e02e25644a668afdebd0b5f233620b60b6946bada6b8343dea04509952ce88c986c2af469c3465009278ac27474041c480fa23030e3132f9646d92d2a70a2a13aa3769b16a63a74c0e234dbb0b286e1230cd3b6a4c3ecd520b2cdd26094dd4d22e96430833839523ebee10778d99e6335c639b648bae7d58bbcea148781419504cb64691393502b1c6d828ac0c5124fe7ecf92ec09a5c99739cc02870bd06cb49a29f09269322639437bf6fef548766c0bf2ed934f01ed4f12192e42441a886892bcb1d1130481ed1b456ae0170271756a926c731c2b7d726e47e7418dae758cf4a5ca9224d8ddb4f4c2c4ae249de5a2501ea8f393f82c7859d22d83ae656f371322dfe85cf6ddc957b9e3606fdb1747a2adbafa585eb2b7078590944ff1b2fc8fcc4386470d6087f7927402a8dc2f59e9966152231fddb0b73904d1685639ca649fcbb783635c68ecb99e05ed3a7b2c81451f5888064b33205f35171efa3bc4648f06a1e6fd687da362780d9efef411dcc21453bb70538f8e094cafa728e17fa371f7fc0d631cd9052eb09f15a3e6fc618285119843a5d5e9d57dec79395555d6a7e561cb6398e6294c91a7e650a63a8050d5103a9ef3b8b680026997b6e50ffcb48ab40a43d5e5ceb3a0b5101326d83956e4dead124d779b174ba436b8c1f6bd57de678fc39ce0696af7404074214d903ce0de2c1cb264fcdfa5061f13de981bc0279eaa6cc51fb52f1f28e8e0954b05e4182606363afed3545f1ecb396413c979849fc979901df3c4c067a1588a6c88d5ac067b910ce5a3ae0350d98af3496cdd0864e5bf60cfc3e8d6aecedce5b6c6d5617c0e5ffe11e609bdb46159be747b692192bf7eda536b844a5d1d96c48e53334570e9160f63b1b3f250702df289612259d83f79f2d6721d985ec39253aa4bc5222c490e2b0c2c1c527cfeb61ca79c0bc34141c90138407879aebbd2e84479ecb6d5ac80a3aa3c6983316c3e05108ef7055491967ce2c63fd37a662a4c04482d86d984b4c81703727c1e52fc2acd0611c0a81b08a0bbc880185e7353204889a61023bb7b4246749d6dc750844a63662c521c98d0d436c6a16df1c9d9d734b5b9173e9cf86042b1823e9d6920a111e41c11bdddf144de2f34583e47ec445539ffa8de1271568552027eaa0ed7537398ce2fd4c39c3241b02ffc1c418bca72b865460ab6bfa5443b989a7e3f90589cc623da4be990790448cbdec212303c565b7897718258c95bd6f4e7386d01ab9b8a4c91e68b06f4d2c75f1ff2bcd32d830fdf7a3056f69f9238802e7688535f3b67bd178f2e75cdeb876525893bc10f6c10e407d953cbc630ec3bacf7bebea77f63dfeb8a8f746281512c65d369897bc06c43917053db03e8225c65a81ec68cb593b564b8c84178b10118d82a077c57cd730715372ef0241133ba8a9b4fd70cb673647406e473da74db7c3cc614cdd326b19e9c0b0effc115d1868fe97910add1d306e797f967ad5453dd8a8d9c352fdcbeef8f15af6c4eeee10cf89558ea955321e2b30a722c7e982d0d54e0ec844a625283f5ccc70eb4a11cfdbdc6095a62b6626b281e5bec9bfa75289324b17418ec12fb592a800e966a8e7798cb415590ceba0cce53192e55d2c333f62914573ae18c7527573a4a66aa599bada7d9c215e6755a5f2211114fc1ca047c0e3bf55cd96c772c570d098a2b12854d24f529f9d9d5b6a4afe50a4542310b415dc7995aaa68af6884b9c978083c67330a42005630b11e8391d73048864abb9903aa4014a7da4802419af8c4becfb92a45086ec452ef0eec3ac9ce3c2755ef5ce1b1ec5881fb2d72b4ab76ba1f10af65933b1aacf2370df7bb79fc918945ea60116932105fc90cafb80554fc9b0a23473ead989259f2a0577881d38d74dc967120b71411eb73664a134388c8548680f6a97890c28f7b30803684bab7caf40a7fc408b00424f28f59dd2640348ce3b8502831ad25d15d69b0a6e03db3b6269db2f9a06e6399da6221d93859ad8a1394fc3f1027214930da640f99051d173c44ef3cad0c8b8f1bfe40c4c744850f724c68fd1509d73943a64eb1d1a1676be0586bbe0b7e1c9c24f114e746b6c33d42e823100438626726060c468f283840baf6428544b4c7c0414dae6a30eb5b4b658b28609b5d04770d884ba2512718e909ccd96ef982bcd8bfdcb6fc7bee984b12a5538b480a9ea5ba425eaeb065af0e074bd9773ef8552aa0c64930fd306bc8cf5bbdb1ba2ad731a4efe541a31c918dd41517e0af40b1e279185c721c54aadfb7a30ae262eb45e3d445f5c88e4590f4d052c20b623fa177597479d32f20415970b3a6c7c1038369bdf005acfb8ed0f3fecb350f61c19b068857b2e415a82697fc1f4eab229b53b8bb0819238ce64b7d0785aa9d0d8d1034c716193f8a212430006900a576a3c98a69ce54f029a180f4f81b64de2ff32bef251406ba1197b222d35174d26a80df67208bfc41d5c02f607e430a7d7388b21272b556275c38a89035fe3952d7405f9e6ea0ea8fe80cd451e157dca6fbf46c32b6687b7a852138c37e12272113b359a75fecb722b2fbfbe1841b289e5633b0b9a7bece06a4c8a499d762a0a569f3662f871efc1f2b1071d2d095a89b9fbde3c2511797092bee3205137c7078340e543012ba636f5c33889a9a7cb04fa83f3c8cb0a4643c04d612f53f80024910410672c752748b602d6049a033204890696213131d776ebf56145314523a44989e8eea13a0670c5a19dcc60b404bad42d5ff68dcd6d62a114123f9e305b7722052a72a7c170a97ff8e578b5e545b3714fb410394d0c72a230655e723e0731a4c232a6234193ec249a2a544b962ff0798902bd897ce9ee7b75fc6ac4e0f451082ae94e512249d54d51d20a683b3b1c3c0c167ec633c4aad7af2a3e9e9ff32707a944e533de682c67b7e78f5da61e8beef922c9993bd70b53ba3c6dc8f924cd3fc08a32e93abf1fcd487ef6fe738c7bd78412cbbcf3c80f08114197203fe20b3ee059560d00e8110cf2b2419c7235d76d3f6110dfbd22514b3283777587d29778c3cf20411a13ebc1de9fda3903ecf7727fa7f1d96c983f4e54708b5928c02cb3be65a6a73a7d83ceffb7fbaf87308184157afc36f6c5006ccd96409a2a6065dcde878329ee70671a71068c634a14107d0322b4d51e06b8dcaa069ea205c89dbea1785e44ab095f1156b289628daa8cb4a181141e82defd5dc64c46f2d66c1749391d886060acc74af2cf4bce9026c8ccb09ecfa3d8f1a57fa14e80e47e53708f2afc15db7852cd86a80129aa59267dae64671840ae40d7e6fc36cb0f98486d2b361984b127c18820b886493ce7bd6baac62049d022055c512b592fe2c98ac6612983852f83a94f09853b317f31491abb86e4c43a6b8d53f06a8badf38385f90fe05b7c5d9fc31906b5d069e7959e8858ed7873e80da4bf8fc8197d432391f0ebb64b352eabbbb9ee25f7fc0f510bdf93de7ce5303db2db159ff3f5fbff56a302831a07f09762f5a7a1c02426956c3a0441a0cd3ad218ba67d23d52fd3b0d9c6f73ef0758a5b41c5bb3181da05aad4ef0b01181eca235a646a643a77c70c8dd452166b5532f98423b8c69eac6ad3b11235736694fc31a4c10a4ebdc520648218ca5595b33a26e03f8752d97e4dc4747039301c0c03809642e896ba75cff87fcc74922e230c24335d57a9369d98de549ad9ae123c33ad1ad818203c1064fe680b15735d48aeff28aa9644844b244b7fca7664b2e16c0c8881087cbe23ff833851d936ed1674c2481330353168b9cad2d880c9eeb925297e464b60e17582073e4cdf99b0fdc67443313175e5c468f69a9950c495e0b7cc54bd2af68d1c62fa7ea71df343ecfc576856c83c00d1431d81b2d91427b97ec8e97385c50fa74917835f6e78f9b87e1113693e4bba36b131974c24dd15255f02a7abecf7ed15c2837e72708dcfe6afc87f44382e1ddca39b8e238d59ec49575f4720f730b0ed491c84d0bcca6777acb9165d66e77106c144bc078b5feaed8bb880a836df213a9d28a4418fe027bd3ca52c1a7d4018a8742b32540975a3a19f7c66bcfa55e86d23d603e1d4b1d1c2dc7797907a9f125445bd9dc93cd92af4998522f5f09b7789fa340d13e7446faab2205e38967ac6337c4edc922f985f73427aae0fb178494aab1e9e5083c4efcdd77c44f9998a8c9325eda19910f3077d983eab157fd3a09296330a47ca7f798bb3d06547a27d5f0c9ef67c7856325b646e9a3f8a612e9aa1c8308d768199628d946e595e784ac5cba21b3bc91c3bdfce2794812c29e9fa6554639d29a95a8f4059040e42e4fbabc87a896e2e53aec3fda00589e218c47bc1ca1fa91e55aae18b929a350d42c972d4031214261b124c782ed030ce1f48403dddb1bffca02fdce28a954b8585c150412418120e07ffe330f9c7a0979d4adec76a200ee211c047960176580cf3940d90c5ff2d0c28c25fd220228cafaa352e545e2fc67a9a7035214429a94e8c61688d0059d99b820f40b42b045b08c4bc7b32938e43d703adb3852310af574d40975b0a5588a0cd221ea3fa99810b4cd8e4a9246c8179b9e5ab467507b2f81d06a16ad16cf4e30fac907901620fe010d82cec9f0f68b0515cbb6250299991eabc3271884a1160d12a1a920416531cff00382356fa1a8c065c11e61b311bcfc81c44d904cee6ba308c7ae2fde30ef4291a2b22e49ac3ed0c0e2d547077b07ad7eb6829611d81bf603928173fba6f8471088777cd4719cadb9aa55c223710e08c54255f6b4ee3bc700e29e80fd0f753895815243b6c3fb24424e8849ce28b198b0acaf8f67244cccac40b0ac6018b0803c20adafad630fff481433b245ec8e20a058a70f1b0344c91c34ec5b369b9c9b2eb1e710011f05d8b818c8a9190b5d95bd0612e5a6af1801e751ed60e8824a0ed0c8a89c611963ad2590bf098b7a38c4df5dba9d74c654dd366c86a31498ca002096755f3ad1d0cbcead5d175cfd8688a1d2444a1f7358222db4d1317f0e2af3238bbcac55457b7e2b8a4dfa290dbc6a6465b3cd927fe2d324b388db8a416f8655062774f6f782e84dcc34c019fe4b563856d584659edfb84e6e309313f2ff7650c45134d8ed41b47787d3ee0df5eb9d967a6d6de02cd7b53ca82273f130c65d0e8e166eb294d132debe8b5a294696c81bc613c3805dde7939c80a06a2a7a17d9ccefe8bd292d9cb4bdd864afae53af90f460e713d690634e7d49a4c618f0b31060619e6234fd98fea10c84a9947bd70d389156e448636ad2720068f990313a08592c1c3517cee4c9a17352436f7c7a7f8d8109f8a056e5863565fd6525df59ece5a49a9fd4111ed8bb3da3b4b290f98f1c310c2b0ce23f822055c01a0680e23dd8e0834ee0bdf2916add6fdc6f77527d00ff14f9862cdde800797f48e544e4ff3f8b05a004910a4b659d6e8505caf92069728f2eefb38ac7d8366a92cbde816fafce9bd1da5fe5923513f955483034772e1740fccc9a1e6adc7df621030f0e7bafb802c0c61236f5923f9db2aacbaf0bdf88965d6fea1a4b5baa603eb3493a55296836b4d6c712c0494b28cda70a4450cc77e2ec39b4157634e420071254dd5d0f9d9fa3d88cca102c367612132c61d64ecdb19051bc5ddb926e3740152175df906e4ecd725649580ab23b12b58e35a179f72a16a019b6e920273b2af8e4a1acbfa4a0df96abcb17894492d929a2cf5c6a58041acda41612e198582c0168a0171b4c3c6c7a000d56597bcd6e9e64468a5091f1e31b548c31ad84e0dd26fe32bce86f4e182c450929d4398a38d6d0c19005a580b60942629e1562d6570f8bb6c18a28d2ea64c1740727ee76391bf110cd95a706a3e02858cabd8176c0f87c1b6d7cadfefacb6e2c1cc35ff555a35bd2b52e4fdead7c960c7c02b34aab88e3cd2d06a5fcf2186be7a131374062e55fd779436d2003cc8c90190697389e7bb6435f9c16f4681d02c0661176d209d81b7879f3a1a917494d9292f729b2b743881f95256dc4e22374acc9226cd5b494dd512a1197afb9938f4e89547cbae048303902836ba6d2ed551663829011e94ad627498413718f2e18122f311a2a6ee2b2485637f8f1c46b771c1b8162003bb9f355da292fc69ec3802ba4b7201f82c87c045d9213f8300f8cce694cb99255b1d5e1ba9c2812c7d52464f4c7cd4b089ab99363746780cef8b31aed7c81237dca5a87791cfd0fc0b49a23e0ad90d35de7ebe73b35f2cf7b9c90740a6644b324fa6847bde5160184dd10f9a40c8e307374174e6cb9385a89d7504075942a50fa95ad47761b2dff6e1652a5ea9063c874a399b3280df686281e661ccbc70306485d2ebd0b8e8dc576e7d37c1a5254705445a602ac2e2515b069bcc220d110751ac0b77eff861a4d5303ca4cd7474e84ff0f5d0ec89b3cf389b7c7deefa81f8ea5678f5742ceb8db195f3df0a32f27b54a9d943f6d52962e2ab60ee79722a4c06be8a83ea3e80328c71f5f7f6b1e2809cd12cb8e902af42c3511b29966f086a3f1a2e7efabda1444e05fbc86efc77438b9b23a15d37f14b9ea590452532c822b16baee63d24529579997bc2b8f5aa7789d1d2b27678c174fa06d786523b6c10a6b120954c437c7d700e84b526d7c3546192023f4a120d54f490137f85d9720d1bf4c1182895422aae88c211bf9859bf8dacef21ddb0e62470de371fba1f1fc0b8d56219a1d4591c177221e96c13349f44ccf6ecd67049785d245cbaafb577000015ff2f87cffc76c58535aba99fd9f4a41422709217fd419d8c0b9d3e5db179cf191b056f218c52d816a66f0c60194165e3ff445812c626db809ab3aa75a9ae29a0e54b303b12796326001637fb40da964644d1538ee60047d9c627ac8335a2aa08a45075b493dac687b78b44acabd603120281ec40dc120dea9a162849a50336d8c1cefc582b12bc0a5a0c6cae349cd616c82de9bc3f33063cd5a351811dbadab485bb9e1480904ae69c30f547087d14d94c89a7415d8438b622243343c0f465f94ad8121c830b362db42c5dc10e48268276070856b6639e8410eb8e612216e5a589a8661f1a73e006ad232f44bbc2b97994a6de183d32b924f3a0a8803c52e68613189427a28a5b627a0087120c65972285ec9572b1aa19c002314a4c66d8b5084df04c510a70c84e5fb810b87deabecbb248057daff1b62d4288206cafb94815f4155feb53a329a564f6603a43565405b7b40f18f84c9f25827794034bebf1295416e4ba3b048509010a4458ebfea2bed861303bbdae08a0aac05635491294d1ca1064ac49b686fc8bfac8d72019717a8cca059fa2b99bb5f36266c5e2857ac11227a09ef0ee46e8aaceb6da3d43234c8a44b515969646209889f4063b7f18e4424c05a55230fb4a6a14e31acaf8314ca837f24d200c40cbfaa3f7fecf33d5ad5f8312960655839890eb1c94b337cf9481547aba48fbd54d3878e8a28f5f2e51b8ce9024a4ea2745a3098594d07d5dbe39e702190c4c26c372b3656a4389643524da2c38549a898296d9388c65478c4ba5211bd06e2e2f4eed5b91f03ffc3037107bbd19235e123075de3c42c1b987d21978ec89806330b19f7debb9bdc5bca94640a22074e0753075b357dffd56481de2cc96acfef60efdf7fa31380d599efcfa933f0a785eff5d899e95fd336fdd809deebe5a11968db26e04543e3f02a0b491a75d9d5f6f2eaedcd91ad2a5ef67215f8323acd4c0361ef0cdb390d2b0d6c2311d39c6cd9c7dfb26f861fb3d1fe2011d1fb62e3b43112d17b03bb1c5bcbbb60618a854796575696d84d125117bf45417d5c3e4276cbb8ec6b2b3bc65a76182969139be27f8d9579d207c3772a5afaf830c84ff0a114d86a418aa1c5fc62f8489fcc77f783f430d422eca49f301ff969a0cdfc2e0626e6617e832c7006c6441862f818301e005f0c1fdb7cbf6d90ab1fb769fbfa2f7cd7887de1b32ec70bdc73dcef60cb55ee8dd81de0f5c0ca72cd73377ffb8cd8f91191b6a662b046fdfa38fc850f4797a3561abe0fdaedbff95f43e3cc8f6dd4a627c28f1f1aa01712c1c6bfb075272402eb3054b529fef7b1ed81fe73ada14ddfc7f6ffffc3a14d3f3f7e8a6c23f3322fa3def962653e46a5b1320fa3e2f0d38c1d4dbd74a64aaebb41c62d77dbd04d6ca1f9e09fbf7d40b837eeb7ed77b0ddaaf62fb87af97b80767b7f234e7ffbe8f38547accc6bdf652a56a6b63c0e879d8e9677791c5efa60bedbf67211f7f20d892d3e97ef0681475aaa8d8843f5d2d53bffc506047a7935616b7382c21f2fcda7f137e0a71cb87ae7a435fed68252da148bda141fb6da14e3674fa017b66051cb71b8f6dd20fcb2f3990628d427de89fce57cf6418bc156fda278d36a947b2d49302c462c664792881993249e4d51072bb3d2671c1dc62a6c1cccb0e7b81bd3e4a7373727a011fb7eb4a969a974830310dc32cdf87183b5494e2b63136e4a349bf1356da24318a9c10f5348c103f4840455900421a1c2def435ff181260b84110868858474041d881476c86c490cd901882cd300cc3e6c43099fd906519123f588111e213acbc008b617965e5872b5861efdf482bdb4c3fb7ffc57e885fc4c84e7cf971200af1bfa4d6628c8fc4c84eac48b2c74817073b6b161ffb842c895588f848a48cf1257c2411497c2415897f9bb088648b1da17c68e107bb4a1f6874eaf78685dfdf451bd76c734eca35b409e6a043150bbd028dc3ac23a8f0918591e7882b9663c12089bdd007e79a7e340efcc14209212cb29586362de97a73a34df0dfc22e04412019c429058bbcbfbffff78e0c02edbf39b649fda42fb20df6f37b611bece3c7b10df6f2835d0f6a2f912233b2f7c7b2ba24c69af753ac38e8d0a6fe8e3558b244fe1bd9991912233b9f919d1907643fff4713ec8b643fe2cb5a24d619af933e0e39f8c97f764cd0999ee9fa6ec35f05c37ae98dcb2863e8dab8660637d30fed85d61d8b212cac97c6f2e598c8ee29dc12cb66f6527047894519bf6628fb0911f0004fa6d1ca26cf66c20ba5a1f925d8e3e81928ac01218533fc671fa97e97c82d62e333660379d2a7f7467ae4c1d61d146640483b14503f95206d42ff8a120b6be063bb5380bd91f83f3cd6224db8fe888fd522d917c1de082a7215242a48d6211591442c10fe1da46758e3c6e6b29fdf853afaf963102e476c1bf81b641940f71154b971c3e9df1b1b76c6fc309ac9af77e287110134dda621b3cbbd95b0cd307c1e330ef0c73e02f1b3ea81ece363d5034d7e641fe37b007baf33f279c79f77324cd24f56a184ed4a8325ad6e9641876ecfe241f164e639e79c3ee714387ba409b61df98163cda724f17fdb11f638e3667dab40f8374d725dd2bdece4126d5ad18515d88ce7175f7ef3b1ef723ee4cf0a84df7dcc8f5f3fb4fe5d2216fbee8b0fff3ae363cef870af3c2be708c2d9adcecc99f9d19a80be5d618185cdeafd4b99a3a4480a6a4131b71461f99d8943f195b73c8aa79c89071d597945a1e26a457955694519f218a5c4b00c4a0ccbb23949319b9344d2b48da469dbc6719dc66d1cd7759ec702e1d7f9587eafc763a145d023cbdf62c5f2cbf20b4969ad845c3da5572dd42a98c4f2f74f7b9092eee923cd13858aeb55a5156508160199c020155402fec022200ab215cbdfaf76f8c52b965d965f763b8a58585828a5b4a5a5a5c585737171e972c01a258665d99cd92491346ddbb48de3bacef33a8f8585d29616dae2e2d2a2c5cb8b0b980803e30d615abcb870010313135392893232de50a624f3e2c5cc0c0d0d0c8e1163c4f08631a607bf5b03bf7bc3f2cbe4c8bc78c1c16f6686831f4db703468401c31bc298991eb7a9c50b7d79797971e1c205e6e206869f0404bd37336068608efc00638221f5501aaca75423230365a417d3f588f198ad34334c964ab0c42ebc6bc442f8500190c26e8ede2d62e1df96564bcb4f2d2bb7e1e79696e587dc0f28f36f41656f4bca32d79920680b90e56f719db2d8206c399906e4bfdc7a1cdf8e29a0b4892d0c1538340ebfdb1f8dc3bdb4da7f34ce10681d729d5db8c1acd18725110a8c55fe4a254a33ce6ac464e46c0a0b9f616741849e20a48b230750401f877310e9e2e101841fbd089b77f37bfd9a36998848cbcf900b53e827fe0b80e502e858ae2fc37efc98064759e33733d91da1292f2bbfd4c1950e687c4d93d2389a7ceee573722fe65e24db6c7dd538d76bbd785f5b87d2382d4f8a37a571b649af67b3a11cc84f70e536f24b254a1d684e4a4ba5bf26e78c4dfdde3704daaed11b4328ac6cc1cd36c967f9b8cf6b6141b9efed5b3156a0a4c7ea902845005cf49a80792dec3f3e792db6918f61d8433e3d6f02f0cea000a4877df7c9cfdcc79620940b7b830e571c5c6d28eeb5a15e56625e147c9a2fbd25d8ac724bec254db591f6204e362816b2941b2ca7a09757f779bcc04143e3485ae3df603341d0f8dee5c022941e25da15bdd3f612eee3df1b0e876f9f0e2054dbb0f54f07d04f9b628e40ef8c39e2779ccf42708f64e3b6ecb91782f44826694eea2721b44742d248a4e784e81e89d671af6dcf55214a3b48b626f2b9df9e7bed4910646fcd4de3382b042bfb6dda47fade6d620b8d1f738d95e327638c314af9f1659432bacbce9ad8feb68535354c037adce1b56387cdde14080abb0a23dbcd4326c13ad829b041ae67586e0fc2ca02218b773bba1d4512b6c7494177dcb0ac83a760b03dd8e9701aa3e7549041939acbe2bad891112c2254967d6099e563c56eb0db8159f86d841d222d76856924416f8de5016d37760553422590c7038bac048a1c507b3c1c90c7633b09ea47d8116347b69fc57585ac016568daba1b3a73c70023b1bfe810966008411842108cc0719c467ac869a4d913721c9771199771d9b665d996c119596bfbde7e83829a2c66c2ba216b2d966562aaa0fe2c047a4d167b939fb47ae7c4e6d692b3969a36913ebe7ced8675b6c77edb3e68990036f46fb247bfec7af4c671bf7d1c84dcd603b45b9dd12658b5af86d8a68e7951894923402641b8013e1d60c52c24575e4558f72fe23fd62193089b073802eb300797e238e40f85df03b3ddf990b6bf27086d89a4a16d608f756e53f45181cc684f9adc15288f3eda809fe6fbdfe974d6acb253135c4523edd8141b75016dcb2c21bc4d26054e0c19b1b2fe9b94524a2d48ae208410bea83c722be25230d5d3d3b93c150bca3f3bb2ee1f3f6c0259f797d9508ff58f3bd8cb65fd3fdfb99ecafa7fbdf36226087a5950d63fe3e207778cacff465a125f56b9a22bedca86659334492ea86a541bf92afec49747896533bb935d50d528235fc51f2cc3ee64175435cac8579864175435cac8a38cec82aa467974764155170585eceaa280cc939979c269444444444444444444444444444444444444444444444444444444444444444444444444447a11c2c6351b6c8f12cb2649dbb8ce63a12d2e2d5e5cc0c494645eccd0c0e0dcfd35e3ffc25fc6bf14e3fe17c6bdba80114346c645f107c6c54b0b9716198f19f5c0d4aa85b278dd7fb2c7a8e3b67eedabb15d8dd2485d5fe050ae82aae9aa176a1ac5aefeacd3a1d968148d2c7b0c50ae5e9c8891d42233dda1c9dddddd994686c5c6ccccd03a53ebee31467777accb114b259e714ac66413ccce835396cd40943b0b1c5060a9332cf384b0032a508f098b0b7a9234415f2cb37a7a54945a660571644d41bcbe26081e7bff268a7e68b969637648e484a014e068bb0ab67b497c1c7814f19831ab5d439be2e3508f206b58d0ad71b040b20f72437a92a663d61b78a115c231ce7fa3a216894581964a34108265cea23b2aa20338d282d60f56d87e599a9487ba20458c8d13a38a17eb8712d866826d2bb6dfbb1ced34a051d88f88244860628920ec800a4888408b31462570421a62fce18824434481042bae50eddc0989b09776515bd14ba15716087c7801927ee6e4ba1cfe184f1350405a416be3b922059347f33141d64332a208cc3571e02379b21ea0e69cb3c482208c08219873ceb902200c4b421073be9458b0721bb6d8c7e9b213b533e79c33c9ca6dd84f74cec9f2e165270976e6bfcc2c0990db3010472c208e60540537f6658c58866145f0c9b2eca574c411471c7184ea8f589972603747322a30c166d9bf645996654a7eda86816650db604a4a9867850a56e0821547ba2ab4c5f2ca8a1594ac102ab7612b65cf14984bd3a4c4b0c88e39e6987be42c63c6b0ecf92332ed84ffbaa2bdf611e9008d449a8722ba0d488142172665f3ec999979869d91996b6040f96113cadce5b0768f1c21dc81023333ed1f220f2758f0840f3cb892821ff8605db801cc13147241eb42d0159e8be0718175055be1030eacb022e2c709a3215eaea8acbbbb37a1fd6ca3a541c4a7f101094b04adace0841421c01c09a5e1362c9ae8596f4663dde92b18d1c12a8a0d7ed00407200012c2c55e1bd6ddbd63776173779702922a5af04312214a10839e16f6dab03bdd4d5f009db3b8220826a8600434c0420a18040b0eac732403eb0c5b1b3fe43951145020fedc969b18420f2c843e58aefcf5102d7ffc7cb018b9f1677a683656212c969f91606fece52cecfd688ab60cfcb013f296f23cc142d2b2cc42b2b22dcbac2355e4d828f20168c4941b18ed64ff01d80323ac8067a70990ede7f7db609dd614e07dff0bed2f1189e3cbd1c1f65b87e3d91c3fc11d560852d67b2ffa173f88c14bb9e5a31f16bb1cdefbc73dfc583e7e371ac7fbfe9c8de5632122ad2744011be43ef825c8a1fd0e5246850d7fe31238fc8d42b63f26611d97ef772df069871bfe461f1af0370a61c3df48821afec69f23d66961043ec11e1f7f9d090cf8eb51e8f1d7abe0f1d7ad48611d9a049f606ac75f77a2e3afb372fc7520d0f0d75b29d669f97e27824fb068017ffd043b7fbd05f6afc700c75f6762fbdd87753c2af8d43c0af8db5548c0dfd60202feba0f37fe7a8f11ebb034c1a70e9ae16f43a1f3b78d7080bfcd849cbf4d050aeb70dfdf2fe053b74e7f7b06387f3b0919feb6131b7f7bc53a1d17f8d44706f8db480af0b78920c0df3ec1cddf4e62fbe1159804f6aff1173a61007f211534663c2c629dcdf4174ab9108a0b9b10c05ff8c4f6c325f8e4436ee3357f610a86bf700631fc852fb85005b61f92804ffe721b255807c53aa417fe421fdb100973e1f215b6ff027c8a3f6ee3347fd9889d609b9360bb079fe22a08ebf0e0cb501020836d1af6e5efbb68b13130f0291ad5b04e7c2d2847e7f889b4721bff09817a277d407540c52167de50ef6c40bdd4865a43bd322ce9c747bd9301f5d21e3da41febcfa35e19f3b5a3dea9a35e9a23e54f43ca4f736801f5ce9d7aa9854543d61f072cb23e811450ef4c40bd14013c377882fc941dcd50efd4a9971ea0ded291f5cf096af9296b9dea9d38f552195a365afe06a87716a05e4a803eca82acff4d1f59cf6ad43b07502fa5e14033ea95e1ef437ec28afce502a87706a05e6a536fa9c8fa9b862600ea9d34867a290cb5c65f3f7ec27ae2ca4fd2f542bd324ab5de12fdeaa5feff3363d44b61c495cbfad3c495f58f467e922ad782fc2467ea95517a516f89cad44b55d6bf148d664cbd93c2d44b5dd42b51d6ffa55e1976735935a1a83514815a00e04bf4e5a55ebf62615b97afed46493f3a48059180e08b3402520f89e7c8c5956869bb794353b88a5a7002f540950aa25d85a5edc61dcd144cd4ec993c435dcb0a235791e7c1b69b16f4c44a956a9e2de3c98cc87e3214c77d6db5a22cbc8c5c2a1296c288800d612b4ca569b5edfcda6e18d6832dc180b01f0cf5ca5c591cbd8ce6ec2daa90d01a5a99eca705310dd766a00dd98015e404484bc2fa5ff963fd650f683ff68cc9afb5a01b41274e069e81b4823526842fa6881209dab15e68bbbb2176c37a495a0dacbfe401ed5f597f2dc859ca4a536929eb270b7dda609d4600a90928eab5617166cc15343a004e20074cec8089274f9e3c79f2e4c993274f9e3c618209269810121212121212121212121262820926988022c552dbdf1ccdade93a245ad701e536dde0d0ee3e9bc5f64b69c835a61931d8f8378b31ce50036bc4bf72623ffd947d0df611c36a4870c4620feb84106625a0f03d320e12f4427b67dcbe60bdde20b0c75621d7880e68af09fab4a947e680facbc71ec6e85df76f5997a3654b29b12b34e52d9c530262a028f385d2946aee0c95957ec3f286721b692a95e6f41ce84b1e25964d92b6719dc7425b5ce2ca6e2f2e60624a322f666860c490f15fe5b87fbbd57853acfc9a1aaf76395e1b4e73af97c3b0c1c0619fdb0d3527a591087ab9d78bb3d2a7ece56f00e0b02fda968f7edc0b68a2b8d70bae88a0577b69afc67179f99a953ecd972fd67117b882e2799e27f39ee779def79ee7795ecc7b9ee77934ef799ee771ef799ee7bd78cff33ccff33ccf2bbde7799e17dff33ccf7b58e4799ee7799ef7500917ef799ee7799ee7bdb4780f7bcff33ccff36a1a674847dff35abc7fe97278de4c0c99970143c68593ef65c4803123e3b57012332313e3b938a1999189f15a9c7032311e479dbc9089f1381627311eb7794e4a311eb7754ea2c76d917302e3715bdc9c406e8b5073e282db222439e1b6f8d282c46d71c356d3a82d665b6c1dcda6b4496eddc282692c1da5477a2cb7719be68f95dd47a03e79ab15d44ac15ce993bffc158f441f7fc5228e1a2746519f220ae54219a1a434cecbfbbb127d8ad185a243891ec5fa7baa7164dedf517d8ad17fa203450fb2feeed3382eefdf55fa14a715da57ec11eb31437d922854110a4a2be9939c4cfc1ba8715cbc7ff3f449ce1e7f78a571e09fe4a442ab586f71c2e60a9b49ac122638d8bbf6a59e13d6721bf9252a744a0d7dc25e411a67087d21300bcbf243485fa239a89b120d7db16eea5386aaf1bee594a5dc46fe1021300bff18ba1cbeb2fef213476f496aaf17ea857aa1527ed25aad542bd54ab5fca4a552ad542bd5b2f2e3cb4f1a2abee22bbe507e22bd5ea817ea854af989d46aa55aa956ca4a522ad54ab5522d2b4973ced70bf542bd50293fcd562bd54ab5522d3fcd54aa956aa55a56ce392945c9cf507ecae6cc50f60593a5fc94b5dcc64fa512a559cad278eb85b7609c2506f78231536fa954aa975618bc975709e6f5f2aae9645a2598d64beb05ce454bc1bc6829172da5a5acfcbac5682898170de5a2a1349495ff69dbebc5e5b5bdfc492e5a2f2eadade5322624a55c3652ca492918236b4142b96c249493509084b2f26160d8ebf5a2912de86ad596953fa3a2d15e5fcdd436533e53d84c5196192b57acb4d226d486f289c2268a145f78466d92912dcbd7367bcdacc593796993f49337a56de44b8f8a9559cb4fda6b52d75edbe74d11f2011edb154279b94a25aa057ee2b68904e0846cf60420005462e3a4a0b0089b31571eb48f07a6917dfc88648069642f846b64b0c4ddd00cf5d0cd50e9f2ab147788e97a78b580dc7272fec6bf832ed5eb9073802e79f6eeee2ed3dd20ddbd87485ab73a98ae07567b809654a75689b8626d70247fed73db829385454a297376689c206dca7020900e39aca33dfc1bf7e6e5afc9c52dc5fca5b6f4f00fc033b09dc1c22f7dae6a9b0ce6e3feb86dda26da17ba1b60cd91f286caeb7e73d58a85e5b70ad3f5e86a0fd07af57abfbdfc5277837c96da03b4f321d017a487da943df701f13b4d620fe5d8ec63a040fb67bdfd5b9595d380e6ec21daed85b05816ad0454d372a8b8f637fad4436e93cd37ddbca9c4edd26667af6956d3349677d1dda06937b4bf462cb709de1b0b79b04e37eb74fba458a79b6b5cba1cbefd4b778356ebedb7409b32ffabbdff85463bb429fb96ae8756afd7fdf69b0cdc8d9f5ce536d99bfe55b208f442206c08116a5dbee5b76f8341ac036df69e4b77c3f6edb5743eb6efdbe27970a7b4d4e2ab800526bb7c100a4c23fb960f3a816b64dce77295ab5ca552a9fa65dd81d61e5df759a00212079407a6015daca3695fdbfe6ef7d8eca11358876d9641239b3da4d238f1338971d3e85da522dae2c16db21ee2e60ba529d5bc29e60934fe7c5eb9ca855a147bd84018621dedb37f199996961edaea4c0fd16a2f8465ded926d734ae0768b77afb6157205db540059a5d3b54c002aba156914b0445b1ebe17dd71ea0d5de3b1dda93bebb2022d07afd791a0f1dd320fdf65d196e6897b56033226dcafee6581b2f9007a2b2dfb2cede851aeb7cfc0e20b44dd9cf6e475720a40a8940bb55a804c866da670f9504350ed42e7462332578f07a6dd8fe1b130a8d526229cfaffece3a35c3005cfc5c055d2d43f6f32302ad8fb7f2e36fdf8d9f3a4adb649f5929aa62b31b2a43536cd6526cf63156a017be5687c48e92695d0f58699bb2efcab5299be9f1397bcca48c1c80c20925a0405902a5e646948d8f3deca95284f59748d03bc3c6df48a4165297839fb356d226b690f909ea538a47e8152bf1a7601d17448167e824b0467ce9270f9a458ac4aab911eb604e858dcf7536c0c77ee3baae077ccc3187f0b7c7dcdd1dc3ea8c36396cb98b6de21b915e5706487f03d50e62eeae1107f47acb5b2ea54f5ce3dcb8b241a9c6e1db29a32a8da359619d0dbebabf7d85f5befb84b068dff56a57fdc706f876835ad7037ebf0379120ffa9c49db04a954b4f42677f915b7e25590a0aacdb5847b1cedae762f95fcdf5d44a0751f6f5def89eeea699ceb3d4ec5c768d252f41eeff17154e338b7aa5936b69091771b359596d2516cfc86e25578c01e300c89b6bf5ba84db1a7d8d82aa611e31214883f11180464a3b7dc267a50e330691c255827c8c6eb4b7cdb6b9f1016fb5abdd07fecd6d9d00f67bfffb4293efc240ee4037a3b15df7f3c49e3006d900aa7d2420d5b055b055be5541ac77dbee6ba8bdb22d74ae2fc211b6f54a271222c028abb3ea7c234223caa6293c0193c0a5c237e7c9f9f780475c5c6e856e2144c23fe6d17d8f85b8c15f47aab58c709ebc08f10629d0df0dbba575884923691ec90087f20cae2e8e8d33833de5d7e6a95e9bf54a2d45d367aab6cfc56b510ebb043efee861de4414da6116fec7ecf21d2167becd3424027102140ee0d0b1f35fd63c126675446312705bd54bea4f34bacd316bb6131ae3cbe1a78b4099355877c7f973a8c58199d47e370f6d8cbf089bff8c5ecbb351dd9e3c7cb4536a437942c312f3e333373e436612c2286ece5218b3df63c1a07fb1a4a3e27c5624663b0a0f13dc3b01e297c52d689efdff232a1851c7dd00417f38eb65ace4e938c87e5bf9b85f1a6736c6320c77205029ff41bdc28a46d6af96bb292859999b9e58bfcc11eac760fec813d0eeb6d1cddf3b9a3b9b406c9b1b07e2ddd6cb0a74d525009e8c06d37e7b6181b728c104ae3c81f6d92a6ff52a9762da0f07390cf90eb03c1f003c305778d19e010d7907f6958d8d359083f4eaede2039f68da06c6f090e4138d41f8cc21f1c827128caec895d0d462ce4879ef7dce5f0f893df10e955530e7eda5e62980cb6bfcb4536fa0ab7e2586cc2f6776b22c4b0df368eeb74b4c520d7260c8b0d31c7b0374519658c261d6db51a99467c18edfca295ffdc0bcb278abd2636c92fd619e6a72f8de3df722398b78041d012191859660de1b291377937e7bee8236499e5a384ed0e320dcd429717373af046c6f7ca433206f47ddaf83739e8c00ddd93b2462f62c562b1582c168bc562b1582c168bc562b1582c168bc562b1582c168bb5da2253e15cc549325deb87286f4099350413cbac2194d8adb9d214d24aca6ac550e29c61724134dfb271009e611e5db1f0492b9e7905090ad9431aca82b4c2345a9149eca1b2f224475252ed630405025de1221bda6a06add92cbf246d433646692d118be213928ddcc61aa1c416f02907027f630c1ef0373671409b95a8844c985545ee14cd48000000c315000030140c07442271509207cab87714000b7d90407262349346e31cc86190338828030000000002000a880808150700afd24dd1d380977fd1a75b35b4f484f2c049abd401bea2136876282e6c0e12acd4e64f31b159588282b123ed0c1ef2f62a9f9bfd8ebc47436a51bbb890eb8f6fa770c5840f1b73a1fe7ef02c5b4b96c476bde4c532c9e5498739a16a4b585ed3722c495c9dfe8bf56867610811396fc41b526da786feabcab2113cc3676893d4d9b8b2dfd880cec9b8f2f59204ba60831940ec89900888970269f447a43598c8f435bde0fa090106fdf19f786cc8a715cfc01609b322b4d326e82dbe71456b7180643dee1ecfaba8566aec88be2713633878a1f6fd2f40085c835d2d0a99dbb385e4d2ff1c1a8fac69a71ee41eae3a30a40a14dab64766f10dffdeb6050e76eca31ca38a154dbd344d333dff2e4706fb72d296daeaa99a2a0d7c31f203dc5446acec09259c36c3ae0ed7b2ce631daae1957e98b328afaf73f64bb04748324a174d4981d42eba80fe4530318327365475b39f34e514026f9ba78846d1bb85791f070f6c9d5f73575a7b43d8cfb9b9ee72e1d64d151cd1c7de948eab2039857d5578060e0ae8fe05a7093bdf7f201520e9ac2ba308d5026032cf39399289caeb011fadb0a980a3b16cd25a40d6efb4893eebb2312aed305f459fd2d96c598cf025cd2531d9e15341bfebd4bcbebdc8f89a8d1ddd8fa2bf1bfc88b2ceb6c56e098dcb581772e09cd8167971cc4040b811206366f05d3376d06a8c80221c2090e3fe1d40ab1edbe158d4815477a7e29be432998a1ebdc53b5f27dadf0728bce255f9e11f50c2729ab9a05fd8d0aba5df05b305672ba8b3fffe9b9ba61d40c3e4b2b8025bf3911e0db863e51a1fab60d1112be1fb169f7f12ea829caaec69007eb67ece2f622d2329aa7b4ffdd8c7e592b2cb62421be33b19e062e4df4a42c20ae19b366f2e7cfc347a2b03d9f5d3b815e2fcc65e9b20b7e49c435ce57c026f989d94496db0a7a1e66fd4d0851437266a035ba3c4272341d29a5469447c119f5e06f3ac415c3397588cadf601b5617977f578a1d2729a7f93762ffc0c086107c5f9de76419f48734b57ab73a7d3789057f429214ff4f4f60a3b0c9bd5e4d5d11494a8d6f22754da5610d8afcfd2520a8e6c8dafb81db870fccd5d58414b516f20cc673bfde97ed0089ebda3f7a621cb58b12da13c8aeb230844c95e0a4d5fcaaafa15191821ae0b5fd3936350d96e28638dfef6b51c0517d118fdb193d1b2dbdec4e9bb998a622b09982d7def7d5d971c6a919e6e973c288f3a6c84a5e47f31ce2c22aad883cf8a8c591bbb6782dc33b93c8720fba1dafb53593fa842c6b0f24930ee5d7dffd31bdd7665f905986caf18b067182fdcbf50aa4e751f8d39ea7d208b79e482a3a1b753088d37c76c48460a84d44104500f9ce749d7844418875dceebe59c7a8d403e29f4d963c4a1de7007933ad95dbef074d37907f7cc886724e6ad1f91054a095a4022a40120b8e552ff59e5886aa55c56915d89fab466caf5698d1f5d027f8b85364dd251becf395e7be5106afe9ad2f2df16eff905c119dc7b2cc1a3729091385cf05466dadca9a2d0665d2ff87ba8cc536304dbbdf38c80f5cc3a99500a09428ec0226c332e723436c3a16b6efeff85a3b4eac04d2c9b43fa2bf1cc9a88b3348b1db334f325f84ba4766082bbe6a2003a109593057209b6d09748d4b95b71c12bf2c24d21d3fa888afea56956dcc539a19b1fff04f739e67b09a797fa3876297070262a00fc98a4cdc6bc8f451f90fde438f95a26b0311f56dc3c6c9b7c8e1b136d6629727ad764cfdd0847485ba74b6a4fcb033d31d4d588dc956f16f83f99dfac7b4bc7e64a8c6a6832994900e4846e2932454b4a95e630576a08aef29d996abcbd47386f6b7c86bc40cbd80d2ba24bacf4d479ba347ca9e687a9ae45c23525b503d54c379de5431d62206189831a3b135233498095b22eaceb6726244086dd3a597504a0ee8d9d57993d76f63ef57e3e76d354edb1fc5cf04922f3e63ed11e40261fc7615fed52e2b0b59c2246a9f2e55fa2bab5dd20f909ac8df9e843d870798c94c2942aa1c7e6815c98c1b509233744df8e610b30f857e40a79fc552fa32c5dd64da0b30d317dff910c20bd07be9b060c857eb3fe6cf2670909de9b7e86dea59e6f911e6bf4e4de48b1cd27f5050ac87cf0fd719d359debc3233feb4206db2b1c2ceeff517fbe135f0cda2a79d1a32e02f6b6088b272d7e3db5803b4ac0a4fa75c1767a1334e9153ee3c75ef0dfc4582d894d0f272b98410f0e64a9faad40967e195311504964ed6c889882e178386316e37eea47a2b6beab746fa39cebd55a92782709a951c50644d742f494424d5851d2f989dca8ea76324bdd223e39c5af8cc97a19166e4a22d103c710a1118c50a153dd05d170d302883da6690e259a12baa5761e1b0264c80fe571f85536091b836cfc87048e0503f09b86805fcc33a27d17e362db15519133b55162e06c6d210319be69ed31862e62938c99da893becd210fd1dd903209e32437504fa1cee0a5aa7bedc40d069a1fb422d139d2cea35631180a5e1c2fbbe11c270081dc4b91bb9e258b1d7503aa7b5c8f91ba302b24165203c2f22159f18a9acdb3c60114c5653f8d5cb34ba44dd68b41921cf58cf00ac74c11503fc7c79bf5aba370ac409d32cca419d566308d4a906f710ad98567ee499ef8b8ca3e0bd2424bb284c65197421661e944369a5b3a4801f65c6b149d5ab84cd46855ed13d6a98ef1bacc9256e99e81e80bc8cc71449f6548015e8cccf609bf7e399c34307a80c1e9d3ecc921c299c82fe3f4bdf4aec1e3bb311a1c1fdb117c90bd8bac832d77fee30d2a8f4a22214b17ed5465bfa1b1c775d5e61b6b6aba1c9933e7910a3ddd4fab06dbf38ba3a8fdcd6cd7865c19e27a7e222f34d205e1921bf370c2a280e44b1c70cbdbc13d737c70c5544581cbfab6dab788cf9019c52a1b22097839d0a81eeac743bb47426b6df58fb3c97688041f582310efd70cb6a4424fa9cb7d2a50d04148597238f9222e3a428412f363272f062d558e2a65ac0c56a59e878f2e3b7c16a7226375da5c08bccda44ce6ad6396179132dbd0947e4281c1b0dfe28bf53427b7b1853f961135e9c0caa7b4337f176965fa4b92bc0b19c388886d86e4ae7f08804eb6474323b44a9de7f6176bad0b43dbc4a37da388c634b2976960996bd1497e1ce2811f6739404c3609a26bd7c828805fb1e320a0613d2621e6d45155ce5ad25faa8fdb4175f3e592ae11763ee49921bc3031f9065afb31ac5c8ee604047e56ea8fbf082ef7494a69477916b0d12e309c900be37e2cd11ad79f8c249e99d60d5f193402e8904493e0aa260561da668eee86fead0393010be0d7337056ed61ba112b462e85745eb6bbfe4a6a1a5ec8091cf5e3a192c0531920ba28089723d0f70370fba00953c0eb781feccfe8788ffa300cfb57d0bfaa423ca24b3f6e5a20ca155eee9323e46f54330dcf22792a7f4db8bd24cd081528c50793e3d29cf95a9f38fbf83a87ec03c3694a9036e0ff54019219127a52604e8b29b3353ebda624513f8511f87d6cdb89cc7058a4bfc57d2f9c744d3ef51897b99d96cd8b54b5a9a609693f85d2a0c15688324cfdc583b135e6e84a37cf3e3e320a3dcce987b80cea7806948719f807f3c2dc961f74e3d29df0c9aaf2e0b8132dcbea23008980bd2b9eb9ef0c20518d63fb93fa414fe41c0a287542c2259b3739079ede42c325c7a2c6f26f07af2073fed408126e2b43d31b5568874e73434c3c70f70f175e04aa2c2d97df7dbdc7a6c78591bf0060f73cd80f524a2325435f0e80175db8fd4e2ff724f02074b5aa24e6bf7ef693d6d49bf28efb4072a6845b10676cbd3260009e0f31e442b1a098d47d3bcdd2b10109270a64dd8f0a683bc205b8cfb476327bc4b42e72fc5792597029c3205bd22cbe5eaf1d3c56fae19e1686b32565a5a68c2a45f6833e87828a2a612831bf530537d6c0df1671aae374428a9a21973531a7f761e7e8bebda944e668f3e16c466448522899ef9d8fed2de74f4c76202b7f41441eb0490e3d326e323a161fea33efb6042c460c8c84d7bb988a4cd930773feb00f489d404426723097dc4ca913b21ca8706cb653542eec9644a85a2441002e4a08d07efaa58002e6c9497dd30425f8f7c2e952d8e29ad471f4cc9447e7fb562599cafabf2b75be4a083feb4c7d9df6fab7f8d0692a2c4c06f7a401e8c25eb20644a508f8a299e871a46fd95046ddbe03ff3022cedfd235567e639a5412507773b76e084c7f80fc0eb2ebfa686eef9b48ad0f0eabbcb7a9c42e421cc224c5af9feaba09973792a84db292b7cbc705a4af62325f6e5d9e186a624c40ea0acc1c4f791bd2a83b4e33a493be3b4c717d5ba317aabdea826e245f11732d3967b372f85e259cc23c9412c70f225c843e598f25f76ecac47daf1d73f6284a885e2bfb6510098816680a086d6a5198944df19f2c3b60f59f50a84381d95890cdca9a4f8d6f361af8c282d27bbf2aa9ec8760375bafba6406fec3186b64b8482b9ba03461ed29b4da7e16158b3c5c6654f1e438edd2dcfe072e9fc20213068aa64fd567a5602588f66dca3302817a57d1baa9686a6bf071a7384f88cbc2567446258b112e7d4936989c5a68c986decc7d44cda2d888b82fcfc8cfdd40672b4be49146122b4634f3624c080527299272f1ecbb6b48d34fec5236f2781d20b14f8055a7a299f02dde1412d9140617f3bd7d507b0e411fbf80d4bd7c54737832a73d9ddd7a9ea40230ba815dc71e86725d61a06852e17741e4ca6e73201642484129ce3e6a2bbd43b165340624d988395cd7f7f7daa58683641cfdbd1e290f102808827b44c7a4c62ed08b76a1405e93245b5e0ef357e3cbbfb6a7ca46e539c0956cfe8078a6222209e996614e1e104f46f44fd153cf2e8f32de224cab85f515d219fc580b0735e68c6bab9c97b3f1d919c281c682ae52f965b31fb6b175e150fba1f7f2ef37bf33df7dff7fa5a8ae498539b5f6f196d8a12650edf1fa163bec1fb9b4c0f8b49c0b5e0984b5dc9fbb3daf4db9f7761b72e592b34b15fd96cfe31607c6beed2f93aa78fe23166731240a91a221c2ef73351aecbb694019701bc241e9343b25130baec1292a762e4331f56d08f5bc05a788d6c4bdca169c88f7128ae4ed2ebe8f5b8af928bef5e8ef9df92a035942ffa0ea6d2b7a2b697c091a3341b5979f2c0ef2f14137131e9bdcee0be22e20f1a5620dad008bacff45552ca03f8fc9bb30d0f1c21945fdaae6f35d2c3603feeba706d7ae9e2211ee812ee92ea19f931fd8b245fd80f38f5553f347969472b221827b4109ea93cfadbd5258a88c3987376cc298296fd538eaec3ad5c2dd0703418f243d255bfd9f99e7849c759a3fe793fce6f677b7c41e0df8fd341576420fddee677906d8ee6f2b3931988052e9a2742f19643aaf974ebda209d4dfa58ad91dcdaee603391d7d6d37bf312da4c5c538ad79a2ef6dfcd4318b2ca8825fb1641b27bddcabaaecfea074acd6a5acaecb0f0f8c56426c3e7a7a1b0d1807d470ca49fd544d786d9557df9190d03df0067ed84962f61529b9b7fb045172d45a6f208ea78c147a69edf173f400b7b16e007431a580b178f4006f71ee0074f012403171f4006f31372297b8197dc0b7b81b20fa5809604c1c3ac05bac5b007a4c69c0583c3ac01b373708a0790e81a0d369c21b8004462793102dd25b6050ca5ef4cc31733d34d06c9e2ee1d9d6def18f2b527ce36cb11703d5e7f30d6a9caae5af3ade952401d1d29d18f42d6536e5e56deb83187491c73a231b0a88f04f0717d40b0d57078c023f6505d62bf10b00ca8aaa9305a08c37c87c91dfb20cc0eab60ae66caa98fb4a6122250a24c78fe23bfaa4833b85945341a7752a66ed2a468c0282172f55708c46e96b4c3a1a5b7c2220374c4cd5495085e84f496238094d9215ab54d524dde5dd82963b8de22a41eb438f450770f3db1d101d5e1260ec177a006e7077fddd70bbf71b95eed73b4545f344fdff7bc03ca65fc77329ee0fc62d1adbedd636139fe8eca41cb86093d1308c9f30bd6ea1591d28af5e546fd255d8f5298644378de4656a9a7636fba2e0fd0effa6d0a704be6adca9cefa54ed76ec88a6d78b0cda99a65099948bfeb39ea5f6459eec402ba75156755d417155da2de8efa38554afaa6f04f23e2bbee140ee939c42470abd901a74bf1213fe5089dda01f7f53b3ede0fa12d99630004f900e1e3e9a3fabbecb820fe2c341b13b638cc4a0499f07bf9431ff36301a15cab8e79af1352086183197f78c2dec95e97cfb8ec2f2b83ffd40f9af7623b858abc2258fe30369a4749cddace69213d67b8353663b2e52f9384d68f177535f23119d66e218df0dc728f197ee8d15a6f183373fc64c8e908132b71e284e32ab0e7e1bac281e4ace1796fe8ed31258d28ac16ae292d8339256bc7a8748a2a08eb948d8b1d83f1808c4efda9ee4fc85b53358ac67ec7ff907c1427663fcf11c237cb7d239549d73cd100653a09e7cf0910e1225e44bf05e9e6b3f7bbd7d543e7f958c325a379eaaac9b09084d64f47b730c98d65c29bf74d43d47f4566e8dd33f977722b9cfb7fc0ad07ab378806c3ad068508f1bcbcb6e76aefbdf8e4551689ef56fd54603a6a1535476f983f2ea50bdcacee9a79f183529747f71ca5a5ccc29750139d1bb67fb373f0f697bc4c52f9f8c41382f6b1b41cfcd8bff1cd9dd0e4e81f4bb8526933afdfe68d44e906cd77edcbc7cfb7b68bb4cb36f123add91a59fa6aaedbb1e9e03f25adb0a0b6c4a764cabcbeef76e753ea2b8ac3873c655669de9922c5f79dc97627c1817c75d44ab0f09e057d92a85a63d0787d87aa3ab4155691e0c72c22582e54eb1272e8b24df64a06f7fff93243745c130b2783aeeafe3e37794a5d5d4fd8477e8b9acfd610106048b10a4059f2c107c27ecf0c56ee839154399ada94709fe1c3c87062b0c51c3c3d69226a84c3bdcd3d97f5094d9beaffa56f2104d8bffea7ff4f37d2f95be35a168551ffd97b92578505c911c5d1863b7e4133558fdd78a42ea43dc2edad30053e6d148dabec483c53949a8d462febc3aa5839d12922c4d3fccf7760088ea0830b6bf858a936d9befc884685af855754abf6c483589b03963c0068401dc2276271c1760477a123558fbb59a42eb426f25dbb38039d9c870171d6a34c35bd9da21b42475e497658adca0d3ab9e5e3e092a284571f9d3643592014c909b1bbc4c698b45dfa391d55627655986baaa140ce0b4b10c3ab1d79e7a3712315de38f53484d84d14f81739317563829f764e482229335d9a588bca370a34b917ee7403518562d66e59033ae44212b89bc2396ea4d20bb6d7ffb7fb19822558a49059eb2f3ff636df5ace11fb8d48f56f4e8bfd7c210fb18f68c723b9b140cbdb9d4d38f71544709b1ce97018584513a693be455067762d2670c1f60d3e8a4b7f9ed606ca30125806c5e9213b3a7092837ee0542d2f2da5d4b6878f6b722b3788608853c4a5ad71dd2d9166482d282e8387a2830dc5a6cbb39829dc65222376af7d79804827b8259432d03d2cbfd47e5993f8ad6df817ddec170028dfe30f0e4c5bb03cf87399093ca044ec076f3e3ebf050d495f24c7d4dbd8f86acdf9592c2616b626c29f4a6f57c372edfa38e2fe86671e74b36f1fccaf58373b370f12576317e85ae1bacf45ada4b54bd546e3c81371617a7c416e3afd81f786e8b0b5fb289f12be8bc31a517237a5dcd5e8441e3e53854298bfb004065acf340cfdc265118fe8aee6deec72e568f17d30e29f35a4e0088e4dad3c2d6114913c23c08511ab05a2ea3bd8143e94ad2bf90ea072a0de58284725abaf4bffc84283d4c9a2697b5aa8fa167d7f3b86bf3af5a089cfc57c8d7c49e3a206bd97cf911071a25109738fa083da501cac246c16718c4afb94d61aac26e60d9eb126b5d874fad9d701d35571c093152fa21c1a31a2214f92c9e5d4b7b0ac802fcca4b1e3ec978edcd2d6d0c600519b91621d9a1e22467b8e155c4a32db470182b6daa695d7366b09e1ffd78be530f1150ebaa66811b51ebacb0026453b7f315a37b244d489bf9c86c35258f10188b1bba6a51e821e5012adc4465fd24fb4218516f20fe63c0b6c94dbc329b4f921c2afdd9a9be90d6d724ffe03067b505506d2a8886611851bdbe0fe775b6fb3c3ca02ecd98d588114962a997d4f1257127091e104d1af960f6c19cbf60fe0a61fc36798f23c19bdd7d4ca89917d1301b83cfc5dd797904562c49590136468656c7ec4c3f4d2f03a42579ca80a5d379697e9d36fcdcb430f7b2684bcc5c50bef08d8255d4d766baf016a89d9e432e5095acc923856749c340c6b82b0d323bc09384e8d00e2502e3df85287a9781cf206396e090f381f6cc0b62d2b86c7d1f88bfca4fc867005e72e81f7c5cd578120c69a0d1dd802b729ead4a6fc975752280fe8b2a5a5e2266408c5fce475c7d24ebd6a34eddfad4ad6b9d7aeaaebf7eaf578355a9c84f91a022ab13a86e75ea5baf6e9dead6b14e1d75661dc775f2c975910c88eb85581d9cdd5a80d1a4730efd36f8ba47c48ac41d8d32cae408e060f253b933934d3eacfb1274718ae64b42242ee084cec455794c04064ef81075116f8da9e316dac05e093bac34e857460bdf1c6585ea7eae23dacdab1b14e7580308ee78249e66ee6e6a3a29b308c7539c855558a03ead3f2275535e55fe6d15db3ca28a5cacf275027c98e6940fdae4fb04b42dd2e427df4c28eaddf95ec541a2a7074e0bcc4fb673b270ffbc2d078b978e6d3a59b07cdeaeb3a5cd536d1d2e5b396fdfe1a263b43d870ba74eda3bbf38e9d4aeb32d39a760219b1c6def64c1bdaa4eb3ac9c224f91072f0d587a85a2a7e3cb570c50684ef12c5e316a508c408a1460690636a691cc290b282011887955667289e4f37f9f5175ae810135cb6c26c1c4a2b7446100099a71a9a03884728669ec012dacf9c2c1b82a511c4061661aef452ac06a9264a24242f3eb19d3b4075948f389c3c05a463984c2cc34da8b2ca0f1c4cdb854501c4239c334f68016708095185464e3521a4beee4544991f5d93fd94ae17dc43de8d8eaf5bc17d2142e475f403b4c40a4d84fca7048cc604c3575f639f075d4ced6554c9dafe650464b0066f7e207773ec7fa4952e2a64b609697aa54d8f3e1a6487b7fbc3e9307f6890f973bb56efbe5a6188d45071dff9402161049a5a315f523b132461bb15f10432c13381aefa50bc7e683bec4bd1c82cab1fecc6816537df05c412e1ec0d6f06905a4e1f20746fff05c4d42156273e054ab0dbf852f4bfc6cd3c7edc481d1030bfedf3b0ac10f39ff34722ec638bc8925fa870436659bcdeec8c1ad25b73ecdd1baf7683ebedf86d3afea1e48bfa4ab2f04f60747f83168a4903f8038cbc858620151433338ae1ad39063d8e79f5433eaea0cb440e3993ad97779ee8334a514eb9b50433668201ba756255012bb169a9e160a791e0f6f096831f037889897a0f4f0dac9bb26b4cfa4169be8a4505386d290913e80bb1e328746e4e630ea7ec8e6aeb952236171d863403fc54637e425bb44ad6821d102a9b398b846470c7a441eed80f5f6c1c83f30cf4d788dab1aec4bc0aae6d83cbca8c720f0d9c4873e73da4d7027c57af03986931631c9105035b224433fa367d2aa341ebaa2db95490d0d40df14af0c10a6979f9872f01b06dcd635588f58deda59e007e597c71d9cf975c41dfbec38d52257f6b6cd04fe9fd74c32baed3ba589abcc4a5289f054c7c19d797133e84d4e09c651d92b949c493a004d806137ffc098cfaad6ffde4b73dc1faf3a89e4cee378172f56e2295736d8172e666d31b0e4b129cef1ad67e18b1ae120a89f1b52467095c37b43f2e40ce42895ac86b8889a00f130d7a416f4881b04b4829aeb5e44eebab7255eadb765b9db092eede317e523231de141b110495d6d4d76186effbfbac8954f12a19be698a49fc292edcaacc1a80324afd2f9767e9eab8e16b5832c7b096c3747974574ac9cd4d103f10de7b558a9998ae460c4e5532ac7893fa9aead723cae75b5de4d1677bde8342a2afe7eea210225bef6dd3ecce4907f9c446c68de277e117fd91756a56a71385dbd9fe0c1891ce2c1293eb21d5730031ec1b7c30c70f2e7cc5b7fe2b661bacf2e90ca23b3dce40705121b23d7760367866af2ec50dd1f6c762645af1561059f1a819fc916fd017cc496405143a5482b71a3293ba853eb001a641c52978bb6ac749b25eceebc1c3d9db9c7c5d95ca272c0446ce772a1dcc0c2ab4d226e4d88790d2d0bd660d2a3117043e5b10de12612a46db119f7c3050fe18dabe826f717899be62eb81a7c4073aced8224c4708a87b569e3840c556ba90a5f570231da08cd178123bcf1d074b70d9ba73921e4fc58653b783f12257d60d9bcd420a9be660ee8756ff64c453e87f38be166d597ebc231456935765ba43c2016e2ae850e66a14708adc492920fd41af44e6d2f34123dc21e9ef54339c3fe06335e99e77568cbf307a3b34e3f2faf624a36dbdbbcbe28b49414530b75439d78a04da50c69fa0ad4e7fbd4c75a5ab2561d2ecdd2304cfb88c24231f23618d52ce28e41722eda54a60cb24f28dcb17137a88c6ef741a40ef24edd25cb6f920f021b2f5c18277ad3742da0209f12d6a8d4d5679c0dc9231d128d975eeb12e48d06d1eb9c50362f771a5f56407c304fbdb708da29e3fa4057e59498a603d85397388170d5f2b53bea1c5adf6c7d92bff11c5c444305ad2b4e35e4c135ed22925d5c3016b33c184f709a96fab9bf01ec33ca3ca9b4b4058559fd10eb03214d5b1115fd2d0778156f426a3c1337140bc4eeb8396c8d5ef73f423126955ec78503bac107558f71005e09070cfe904ea051655657501c5ae8d6a92ccabf65f71816bfbb8e93419c8aaa5341fe22597048398697d37910c17526acd08d72373de8aa832cc4ee62463a5769f0d1035708576518cc470b7763cee9fc3c7b86f2ccd28169c69617452bc09543ae3d11e59005283e98b94a23ce7b0947808d0711de214f32ca84bb3822a08f231f458e5541de28faedf165602f637fad9a584680557a3d9d37b390f0307a7f4fa77bc898f0d6964443852e72403d5b212a7e344df875093ff7be90305f29b1d7f14809acfa7e634a360e864015c2bc1c96a5192410b004af5bb2f0731cb16463586ede8698bad8501f67cd595c05d55602e0d2a379a0919b8a3558d920500da7198d2caf58842c7cc5407bb9af76c964400ddf7c55a05588006c276973fc5574dfa0a5b79c51dab173b785196179347755c93fc45a4fea40c904d9e776e5f1e45bce5f504de802df34247c17c3bad901868eb0ee5ae4f658a6dbc8718234e70e6b91110307ac27478767678039b6a9ac31bfe28cf3765b3796ad6a12860b995508225c0c1519135e4d8cc87370aede508e01497a55eb78085dde6f11da0f10a3260d3ba354a8c198bb8f0851365a66dec6d7c4105d8779d3bebc18119881567631068f0fe3ef633aabdd4b6caecd7f8cf9165b7bae1718b0a6719eec83ecabb32a8051ef907ec87fc06e58c5498acdb295d1953c90ad5c1cd58ab066a6b823a4856433037cd6efe441102c4be90e58f60bf021fdf47821f086e4e6b06595ba196063ee01617a0c8cc9dea3a6210ba16c5f1850a138a2aa4717e5922591b2156a8de338870688f74e3542d6aed6996388dc0620d8961e83ec154628a4cb1bb5dc56f177131d724149722a23968f0e4a3ef6f8910db4d752dee38bc8494044c98517b15fba47cd0b23a9ca30ca01edcb3d5dc11953d286e499271ff36d8068d050dc79eb159eb50112e45ca6531b56dcdb023d8022baa212c294145c96675e3ae26b0fc78b9acc08db710db29dc2e712e32d8092886bc3361acc46772b89d71031385f4f8364ae0a181ed5e9c427c5ce35598382cb0f8ebadd41b4a91c1648571bbff47e035fd61e806c6ae7c39f834214c531e60d84564df355efdf5f950d1e870d7ac7e609b1375c60de8ddd84deacdef79c934d4c0ce7e91846c0f17a79a9ac29256b1f0c939155c0b25964099d5a5c36c7e409fa6f1a45b581ad391a5b50dddc5cac83718fda9346d9e87987c16b3143ecd4e7bbd95be493bef02b4a01ae897676d16e9fb798ee73bd9ec3d197f8462a9a914812468c36926426dd54d379c2ac6295e294c0190b0daed055ab43975df742e169b9294df280002ba05d665da8d170bbc1d0c35dd1b1c300155aee1ef252b6c16ed465cf46f8c7ec8442609a9fe5330f2bd34ced93c0f2982b4f86027aa1432f7d40e85612e9f335237df95a9a84467f729e3a4dfff39fea66b2dae3365d39830aeefee8acce7b6d4832145199cfd3cb93b21c14edf6841097fad29f941346db0cd1b69aa0556455e9fa694683ca7d1c58a54751b1c08eeebe2372898e151039be35794b0654ff722f44dac98d10dac2e89ac68d2fed728694b7caf9760142cb9c600d7a1a1fffcd00ae7dd1f61f926feda7a87720062aa148153ecc261b784bd9c7a2994006d24f6b95ce71bea8547a3f54ab8a4b6704157c5113c7aa6ff19a376625572b76c655add11e44c94a71868d091fe8749455a2315a6fcb6ba67beec6f8df0b3f28c6f2f9c60d9fd03c9cc6f053f01a4a7d3bbf2613a23622c16ac8600a36feaa55919154b321f1a5ef96e942339311104eb2da18edb3744c5fa30bd85cc9121effc305937a04ce4404f8de98fd24b27a5c65397340feec1f0882841366a47a33f69b026f0a8f9e703765a328499715c56ad4fdf83de3002b889abb9e27292794eef2d1d23fbfdb49759c1048b85f04af393090bd603695ec6fea9a694de97d0ec9ec32dc5f6b5f28176e58fc7fcedd676b2026b04087aded92b54dd91b4cf37868b57dabf93a38f3f524a34ebee8d782a47bf436fe1b4bb8806719544ed06fddf008523f3c7d930127ccaa8e9c95f860cbba197478529463fbcd9085c8319d5ef31fa0e4c5c2a0039d4590eff3bfa36a417a77bf5c4f2303832ef07c58243b9fb01389829220d23db39e7631e6a028d87c9afdf8282661820e6de040e7f3eb9c9f60b4706d12e044e20954b981c9a1a85ae771dc21f07be83345d2f2a9324998279d30e9354361d4bc077bbe95edbd63b008d8fe46911e85179c268fdb9d161e343b2103f1e5d141207ab0f27b1fb1f47f813a845c70682f6d25288bd6b1b70ecaccd06b33d4cd53bc4318847d7c0532a51810015c3acefc207f77e1b31619209328ac66bf1f1b42d0df90d96d52a79a7969292d92de2e8450b0b431436a576223d442f54be59309c84eb53c6c9423ccc32a7cfb68fe31a4d4089f00d3a844286d550aeb848cf9305f1d0528d0f208190bfbf34ee1fd5be51079f2e65dfdb7755e3a42f18f6c9b45589290d96ae9c55e5990c554ecc9ef4489f11a89c1c653ad84b8c0cbc9195dcf0d6b4ce30ddb50a371dff6e6873fd8c10e3edcc14cfa36ae310d377883618c2945748335cc25da50431a80256f70235d464a51a10f4ae57ef0627a00438baa1f975443a5e20ba69a1c8fd2ea120173fc4b49a9fac5a4a0fe65aa50bf324997fa2aee3c247b3d4746d8af083e94276ec193c7ecbc6a33efff9f39844cdbaabdba9ca98acf51c13c0abd7b912f86d8ae2a7753042595c7a65a9503ee1f1d52d9318717e958fbbb621d2760bc721c489bf4f624e8490a3de0303ee92af0a4a62e839035c140ad5ca24d434b1b4d2d16cbe4cc63c8bcb1d56d09c2a5770ad46fea50514e88d6d22805f09197daf5c350a6f902e71ca3ff5960f5a00fe53d22f6858e3d2af7fd311635acbf0e57dec58405f4e24dcd8574e67f10fa31990fafdecc37dac2c21c2fc7cb95265e824dd1601f7dcd8006eb71742e1957657fc09d9ff894c04dc318b4ac0b3b5330115e0c7324ffea20797c4fe324ab10d915ff8a974f5ea9286b8674ac08a8c7db0b812700317f6560cbfbf4566c6fa2110affa3411a62e9519f340e183a8e2d2dc83880ad0b12713889f83990cd64568a7832f2f2706517d7bb1c0923b863e558d8e785823014b2cd790a04a7db3713ebf96050705eab7c69d2593816736100326495dde6932d07712b87141a46bce5c00384380e479a6ac21702f181ffc876b7027933b7937d08f26e353c4a5a3ffb2983b477bc5be30f1dbd793554a1b87b6c9ed27b0dea6b6e3f0eb041c19143099b0c3600530105def17235267238bf682dcb2311ebad15fb333c0e48a79eae751ff186e5e50caa13fceb0c107258fd568ac4c6fd669efcee5e78d8000ee2896db8f667c3816d829b02e07c3c49d133b233a14c01cda87a05413492aeeebb3efbe935ebb890d9810b289a32db95f219067882501b9ac2f0289e2b155bb0834c0cc5e40d924e453a413ee3612f46d46180ba1f747e9d95e85de2ee2bdb1867478e573a3f47d7fb499968dd489cb4b8886cedc0893f9da1b4f7c01d6dfe6cfa147911cd395ed7adaf83b5228267a908d07fe48956d14b6b3fc3db9bb341a7aa8b06bd5f5ae0031e8a28cd0c80721a32ab10cdb5252606bb9747bc7af61b734112341b003453022c4b189ea6a8840cdc7394e90257b6c54635b5f57b5819d65413ce345ff51867368355b9f9245575495fd5021384403b02f8c302ef312f450fe481dc6b4ffc5b0df1984483481457111e65d85cefb36ce8b658ed2521c61124e09f17be4101f1f91c1419f21987e744cd9a2a0a0a3183b4af62a7536afd6ea9c4b309d84047fc3272f2f28edbf0f0e63839f4574d3a52721180368dfdfae695c12f237b5a24b1a06b850844b61945ae96b6458164f94d3ad2a35ee08246f8c58d2532b41a84a1a364915c59db8661663cfbf3cd13f7cde64db0f5d80256341b5a548bee6a5af244168be61178789cbca8c5436bcb1021dbbc3177d2b6427ca74bd156532733b33957a9fdbf209800e830325cb2400719b42752cb53b35da1086867ab5549d9d2db050d6af242a0c9eb267f2b8f3c5b2c23218023fdcdfc745ee2e7be88bf392baa54d6fe5327c7324897b81e8a97a6815c01c2dd994545ef3c8a4d22f90e4806d9f0c96c02de419a715fd11d10f0ae4bd8d7a1e462da907bb675c7f6f4e95fadf60d9544768a05d4a822e862ba86b5da2a64a653e7f1ab5ee46d56c3bb50e8871b2ff5198619f2a08a3d846b71d3fdcab49f6dbbb48022b04022609a90637d2ac25d2459385989fce1e28055d352b1bd1912056b4c0725d6d803de9b74d2cd257b0eefa34bae3654a0e329169c193b4c5b445f659d563bfaed57eb4c6f040ef7823deb1101605eb0653d24600c17ec6c8f0420832e992ecbef2fac71503a2acb1094765b873c1bf2fa861a77f20c41d2572408c050b9a812dbfb6890b0a2614e4b47ab2f1d646768669c1c15e5a5d1fe0e1dc107df3a096e3593d9d9a6217c9c570a59063cfb981bb20e166c768cc8887b8445d4caa9d7fec65c54a303349a9d143f3ee61eab9a4cca95880088b142df5b16713baa9699e4e385751f14dd85e0eaefe357beefb53bd23e070b0629aa6ea13ae41350956ec9dbfd17a47c25354fe0381a5735e8a3fcbada31af7ead171a578674795f5c946266b7943003f414f53802dd0385cb17507c2f9546311970cf7e24c509cba390531224bad40910de5239601bccbe72753469ef0878240cbc789121b0bd1bcbf974f171ecddb88640081ebb45e7e0b014068ee334cea88a8d4ac46f5d18f651a3766f9a95203dae42aaca6ec30cdf95764044224d1bd2d4712aaf10dfcbea472846ecc84be582226e1a7228ce234be75e02de3eeb56d5c1d8d4bf6186c6a9b485ec78845c5c0ad0131464f4b45a80f18bb762da57513923dced30f1824680a9df4c4a1401981039dd4ce426740882dcbd6c03b25d9424f7c70637d4db7ba312a27350c3b65328ff97daead1d1e069c08fa67459ad9e45ac3239c353aeb5782bf46da20342edc29bc2da0a25e1651e08e6ea88ac7d466713dcfe1c3c148319bc4b1c0ab1e4644661e0e5feb7784a8921448fb7f0532ee7d37d10c9230843e35de74e16feba8324e762d79762423851c3dd11609e67655e072ec535a685c9a90c8356f2c754229907e3600be052fd163c8b1a1c39b612bf8b0a0c6ea36928927dcacef57abf262598ed597a02e1cadae315df583616acb99274120f9236e2a4864048bd90e58133643d64713416ea0a83bd22da33b80faf6ac5963d637309ce7bddd71cba125667dbecc383ee1091d6c3f98cf76862e1b7e1adc931a5dfaf19b14151e9af53dcc2f516ecfb97d80d7561c7e355f7e3138af13ee08a1708b580e0794812807e7c3ad9e8ab73720c37438e649b2dc601e88fa2c999da6d5cace7dbfa28ae6bbdb55b940cd5ce4b84d619a396a20cbec8a45a5f7626ff6f683e72813ecf8796c347e2c484699f42e1804290727314e3596a64a66194b21bc62e18f020e4faab15b796968a532a21b770ef772a06a599d6d1023671ab284e7ca9f46e9ec1770f74061ccdd213232195524ace753e97bc88838d6fcc353b2c0a6b403243c3f8db18fce48362d6ba02dd127cfa9c4454a537e079a49ec122c0d8386ac11336dcb1b4eab24a4898acab2827f087c89684a2ec2bdaa4121cced3355451f0bb79d8a334b6983e7e69420d252ac0bd3572a6df6850ba73b8bfc06f5b9a66950da33e73213a89dbef350113a0da1230d4fd88c1117bee89e4c574d9eae846a39434f1c4c9d9480f3cddc26bec4a30aefd10b4c3f92890172a62781466f82cce4755798793b34cf8b44f49dea565cdea0add4d9e177b87d73156a71855d2f4600154fd8ab8020a760d32c54fa1c897fc0960fa138a370c3166aeec7cb7bc22d82f53e5024526f683cd00496becc91fbcc9382f9cf1cc4d9e00c13a508ad6b0c39875289dfda35f724ed28654337ce89ab8337b17fdf6aec48653ebb5f53dfbd17cb9c46e4260537976ed4c49612a327cfaa44c89e7104b68415b8cd54be4481f9bbc1195286ec37f810c1021d618ab7974616ab41050b6f303ff777d0cbc884b042fcbb3061a564a7dc46ea7cc5b32ad610dfcd594ad18899587ea98183466572f4408046f520054ab640adbb90243024e5185272e3fd70f66806dc1dda4d512d100540262b05cf042a39b09d5cf9e1550fa84a2e26f08e60522743ada2438600112d35068456168ec6c4ad8c9365f15c4d6a344c307cb54208eb66b6a577c450601431a37a7e5371d7858b2c94cd6fbe29088510e81d683e0524172e578b0ed5c10e361605b82a07257956e5609b741baf9f4409b1132f4d4749144d3bcb8bf17cbbbd5f3ea50652486f40e11f236c9bf414b0eb1f3f3155115da71fcd30506ae3993e39fc6fdc60d965622b7201bdd6425a1bf965bfb7ab6985a9e2e1d00b7e6fe6f82520cb62303a7c1d5f08356398e7fe3215303597d35509bbd143a6abc36dd3e95c14db420852a058ad9cf7b5c1672ba8767e45c68417d36001bf5b98dabbf01b7815d6d6610224cd32625cdeac0693141c9e8869e3256fa57717d7b191d8bf5688ebfc6ddba6fc9b9432096b387452628f0f851933175221a8c504f79131929f90b92be619740476aa1883c7dcbee3f4367868c84cd834f897a05d917f6d6a04109c3bcd90e6deee407e07128e4f9a52436597cce27dca055a1e322e15f12c8b33bbdd6871244bf83d62a6d1fe25630be8c8b6c8dde38d277ad42faadd4521dbf9e8888b1107c1bfe2cf1fd36890ad267a86705efafeb7048837bde1c3c60967f901a98124e9a95c7c7fb4f80c5090cdcbf757af485d737e9a4cc8bbdc4af38e056bbae863c9201bfa8f0ce0172a0428dbf9c873278d4175c34bdcb8361088b631d12c5540a05f3e169607ba6031f2de5a47755482518b44954db53d7d97be5749d9a92d51c9b8046cac80a10d66cb9cda2e051682a722e824ceceb3fca993a4a921d0410c100d5e0381e21f7bef1b211c9498fe18d559e66b435d84f5d17e746a6b82db787c96f3a14815c7c4f6157dc11d6383600eba27fc9d1add8bd352ddaa2746025c5128412318809d528da1ea790a89bcaa20bdd82e3e27720bdb8436b69ccb44921f30ea73a8b373820160b929b2678ce3a3e2b46eb3bde9e5c6410bb03a680a0cd844d7e375d9234169a26bdddbfe8ffe6073136c208a236f8233c0a2ff3e6d8cf51129cf94b66dc0a0cc70a3a13716c1f907527ce625c1d5c0fd791c949311d33e28744efb374a988e4f86db311f0416231d8d465f9fcb9c5b88e9210c904112d125c618cd8c9b34e211a9f0f30e52c65bbce0a83389e287222dedccebe24a3617af40f0f750eda98165da229511c149b683ac5a4e90aa666536040bddb1443b6a66e585449cdb843c9dfcfa79ed7a466ffe26f69ff8560b8e9a980217979c79f28215314418192448e091872c844fa67687c9e7653b257fa340780ebfa398abf8055bc3bd42cfc47142ade79b800c0cca22cbf044a4fbedd121cd89a7ade5dcf0a7b30d83a9fa2f651cc1bfdad47bc6d73b48fc2e12c201f97e3275914933368eadfad24f237a5364c72c153a35ffe6f661bca6c9575b0b60663077dcdecbed641c681570c768ed1ec46140420c49649e56c2575f806e042e6735e8667033efe6cfb6b25e1bd81df27459cc214b965e9bd08eb914160067ea9fde74ae2a1137fa105d252a466d9374415fb660c207915b78df83bfe30e4dcda4f2e94d9183af13ff24a22fd28aa857ff60f81f67b8be4621eb684142d65ca9dff4e792395510524502e68254b394b34d424dc7b5cab16f01f31fe2911a8c3005956fac52951c40490ebaa22b0a155ad8a1e9bea53ec9e0b0b84c872f7b66d3c0be474f497ab0cd980c42775f6ee2b1b739c0fdde5983c766cb3d682f17814565ed3a671b4dd2c96a3ad26a6411a442eef66cc1296b8d427bc2d32027d8a7e9c9e25aa4a5569d0635cec2083cf2e778c6f6dc161d6c5bca05d5073d0cabd5efcd1711802973575de24888b642c6b035668b6c4e4f1fe8858b23fd021f8bbcc8b857aa0c91ccff44efac9410e57c016262313d897161b5239244c4b7fcbd974512b35134e6e899ebb98e3e15eb6603f58d2175a29d37cfd167016d694dd98aa56132dbff1e5790045bc739e082711561ac4a0d652a3ad528708812a70127f7c68faeda22b7a511aa5dfcdb6dbb4bd754c0d9e5f27d375d04f7ca2e42118dff607888c6e41c63b848f0641220a6f79bcfc8cca1b2b8f0195555ffe198e22988b12434e826b516df291d3374b1701360ac89b31e0d7d58014b389a046bff7be104c788f35de366bca90007b444cc0691fa9861971549be995f2697ec85a25034cdfdd9d16e6658bed87b5cf66477553f11034342516fd06290a816a4b4bca3336608a525acb51dbaa98816a5b7d8b3df6042ff424d1a22b3542863e4fae0197c61065a9a4863b312dedb464d3c5cf3ae728d0a97dc2c9f07ecda9b1a5b0b85f095830e9c46142009517cb24f6842812e90a6b9d0499f1fb847ef08f9bf1ecf4bcf390acbc1baf6aed00448d1b55e17207eaad21cb4465c3b28abc3d8d9e557df452941c291468042413886cbc3fca25ece6f3794e6624335f30288190d46b02b8817bc2e59f83c9ba3d6fd76f34300e0b221785201436a4a4e33b757a4b9a9b46fc6811035f21d7cd642956cbee48378021ce69e92810e60f285155547247da8d1187d8cd9da57ca0d72c06d2a1c60b54fb8472b8c417cc43a1945c27134a3aac4884ff1eda4f767dcc3ecf7e02553d754833c6f0725419a9b8313fe93e080b1a17087fdf6f4134a131e0bf3f00a5e485952177b9545e72855bcaea89a6ac292399f549afafac4e4def05fa12d2a4bd49bbf3bcf38541fae98aff118bfc784cf45f1fb0381f401949c8c050b8723e3082b8e137d3b8a79b5420cf44fffa07221f6cb752484237e4774e21586e4215d741f25c33d0534109d956f67b4236100aebbd30580c6705fa460cb7696f05b8fd58c76d5072d4544d6fbc26ca3db92579a86ffcbcde1a035b0f6edcbb7227868ea16d7418fd00edbc86328d5cdefa9f47475a55659ca11d06e77f5b0290a4e6fd517d96e5b74a2a6fc18729e3cc7ea9727d8f321e99fb559de8cde630330d57759ea2569d720fd395ed9ab431f051fae5d7906f47402d9069c129d095bae824d2dd85aa13d895ae80463d86f23cd8a98e5a6f94320ad8fd50713f016e65daae8b09a3af4f34ecaae602fad34451bf0b4d19c19bdadd7bc981f43207c130ebbe573d050f49f46ec53220014bebb3118ac03c231bf258f99dea3a9a2a0671532858a93079acd2cec35346c8e0ab9cac24d145c64aba11255271bdd0b01d01fabf05a0727da30b48755203a4e8c796652a56050999b202b198254f5875738627b90e3b7c3a99dfb12e2b0e88fb86004dce838dc300ccf002ff7b13acae430e15d264785c1c5479e0179d8942ec1629b7bc943e8b4e6f67d7f47ef1eb890399e33e8c70ebd0805f61906c14f48d0219a7eb69574dc2ca49b46a82d6187095719e3b0612944ffdad3cbf8112c8ab289807edaac83da521d5f142bd32ea5fb2ab46821c6786e55b747e10d54e5347ff32c9c5788324cb44f6db2583ac02a081048a9865c2546be43cae4358b2e30c6179a674005b61e4d49450324d07d7418911e8da08de094af29b1c04d861c13be65e1c23760aae32c4933e4a70393427392393e500fecfef3e8db372be8fcefcf62aa916cf26fe01ec44d29e25cf9edb5304316509b611f98463943d996613095996aecc9e92a27a0ec232fd853da53b41678cd298f1d061e950034a50970b512920ee0cca69eae5d606e438bbcb9c11cb689f7c4c3415258633592715939c8ceb6e767a09d3c0eeaa8cda002e48850204a11066fc82bbc59de98e2af0f8db9a8e7bc4308f9aef811c7ab25a5bcd06c4cf5726a640d91afa9f1617b0a29ef70e5c0992c5131f2b681091faba2db2e1f01ca9679eaacfe4835225b3917bc1a888cf963352beba9b858f17d89347190c91c89966751ad0a87b5496c6fad30e809e4f93da79165fe02c3acebb23f2b539558898b082b99654d283fdd15e5588a1856a0b19db38e819b550652273ec208dc3bf02e3ce846367d0b0c104ea0da50bbaf208e3c19b148c51033ccabb9c1c1dea84b3a1d3ec17a4004a416d387080e1bc49a8961358810f0e7a68be80794e5f6e3b0be99d7b66c077abcb2a73993209b8f89442b7b175f025996465992572a538fd68e46c16be638f449c41ec6416606a29b1b60fe181413dcfc00468e5a9cc46842faf69c1023b0512f922dc185b41d4f39ac262e14eecc93336148af78a6dae2ce0a3dc24a13dcb70e6e806dffe732ec4645e294b5b592305b1afb64f4b1a28fae62d1234e7d35793b2d24c15e5dad7160c7fcb6ecb93d69bfe395d32e7d3b789bab67d7a9634841e23c9123a352206f60357183807389d7b6dec4c981cda1ea040847adba11c8387fe8ecd01cdf95e123b195bede1d12cc49a383b28b6c53b4c19f02efe58c09cfbd50366de8031a1b6439ec38277c5dca21c89d48c8ad0b93cdc8d83cf6e4a87046fb2d5fee4ec77d68f271ce523045258228ff5d8b9b608bbce2ea57abc3c54c0fce420e965365d010688a3c19915146ef7608ac0ee32bad1e7c206cf698d34d22c8d5438b014e1e5ce53177063dfc9a8d615b28db0d09e76269cde9383dc6e5f3662b1cc34639496464bd5f739850a12c6383436c9834298f17f9f0b31a9ab245ba00d3d458f794890927988e3af1a1a58cf6cac9e9c6d02665b75f825a8f7a5420746ea0fcb6a3194d28b56007c58e71dec8baa3b13011b2cd523af13eccace509d2cd9c1df2a8e8bc3109d5eb5433f7d8e95a321255d9d36a1e9088e28e3c51b9b57cc72ef657d096111723f5a87cfa1bcc3df1971e6dd369d2f5fcc4eedc169620aad55d67b1c632d853d83d9ba3dcaf0c64531a001c3ce6b1254e2ac190928b5628966b1c8a5fd1747c59adea91450df748cdb30c22450116209c35a63c32e27654668a12aa56b5f7460701a5e9cb98ee2f2db9239e289a6c34520ee7b8d18a35fcfedcd4d364edbd2bad634cbb14c5504efe40ebba82e2d830042d840b1997047dc70ec759be7b2e527bbb2ca5ec95d2b8e317c35a601d38b7b84c44ca9957cd0a965984a3e74e52fc0739df83fe2f7ce684cfc3a159c3705f9fe3df42874b67b46f19d09a2fa38fb837fe9e009af16cd70da25786bb3b1c6ed5d83889b844047b9bc38280cb4edf4910eb93ebafc240e36f84cc616b4477b16d07f6a7a320401d104e94aca1a2a29592018552869c5754e8486067c12b790451405dc768be23951a73811131acaff94dab0331f606bf535f3b9674324664567e5d0f17b6a8b9b3079269066696c9652356e43a951eaf6eee5d106611d86c1bd3bef8505961580d2cb1b7d9443021fddcac5404cc4490867ca4cc2c560bba08926365d559aa8af168220c879c10592d7777eca1f205f7a4dcf053cf617e27d02176e28500ed3ee282c88fc154a3cc09d37cb57f5b8d846149e854b9bebd18520b8110eeaff0a250cbc0fe9a60f9af451cb5c30aa52da976299cb1b54b3b34d7b4cac32f13c0673f0db59ee3f643a77fc626e8720e46b542e30ed80f1d77333019631a37ca09f74c3405aef9de9e9a8d604b59037f33a390df93a96e6d3e06357d264127c20eb0a0481334bde2e65bf7e730ba944e608fc30d31c4186eb23d228a7e88f626efc0af871e442fa0a8325020550c96a3c63d905f3e9efe6051706429e426a0615410cb3f0f705e3c4b1d511b6cb481f72cecb94822b05f3883e3a4cd25bad12e5321de6dda4be6ff30e09a251840421fea642f3438a36b2175a6844501853fb16238b22587afa34716aa7894d43591e5da27c3f7d52a8159925a35f410c20dc5489281f8e3ddd2b281f5e45a6cfb5813d6cc6a2a73284bbf932289de2d3345e6aa09ce002ca24a6695b509a15a503f90b969bc4ced4bd12d2626a2230266ff3bd13191fab13622a266ac47f0274e44d9c24bf2ba81bad2ba8771a430c501c400017a0f3ec7215d4befca987a82e8f9e69cf5e708e8ac8cd5ab2e5267bcb2de59652a624654206a1067906347a078852f1d619770251eacb98b3dc76c500b95f7e3cb1058333faa7a8c35f9e99f348cf36a525ec303b3cf8c1edeeee08379e172819737c24635bd15d83c44c86438e3082331c724410b210dd79a5430a11849792234f77fd0f02b2c4abbbfaf22bed68a44713074f1f3e2ec8338120a42c22cbe8a35578f8c8a69823b520a47c3ab1c941ab89ba931f67fcd86361017378d8e4f830d7ea61ec7b17c81c0cf4e2e3a8df75074120d0e6c508e260ac0a26816386b042981aa10d5ee46ec5c1d8af5fc5e8f7aba7d11e8a72a08f980fd0856e304f93c7ff31f98bb1c905da60ec7bda83beb117f9f2a507e78cf0bf14ab0113b3c84fe4f810257b932ff9ecafc95662b27935e458c23d8c7d0f9401efc13977ae07f612f40edaee7b35640f67d8c5995eb8f8df0dc6e29670ee087f7fcbbdc7c326579c6ff33d081ff5534e0702d84eca4bf797efa3fe1ede037b0f0e571c681e717e8844118514d4b23e63bd31c6ebbad77561d867b5d6eb5db8f0cbda5aed7facfdacbe63b5d6ead4a274ba8d350bb988c25a6badfd60570d7d5c605756af5ab18b5a944eb7b15e51d86a69a012936a624b2e9066d5fbb92ecc5a6badcdacb5d65a2cc3322cbbae0cbbea5fd56fdcddddaddf68a3750c756ffdfe18a845e9745bab8dc2da6aaf7597d6d62cc33ed7753d76718239f0bbaeebc2febaaeebbaaeebba2e17978bebba42d77b2efeba5c84acb5b6fe95c5adb3bb8cd1a54bdf5e60621679baac5ca499517a123e98b5d64661adb51fcc85777d7551affa59fdabc6cdf3b5d5da18ac85c1dabff1fa8d0ba949b0d6de6aefb5d53ab566bdb5c616f8805aaf7a71f586ebd2b736e8d29e6bc530bb0259607677771c5f71cb350779899b788b77f76f615202d22c77777bb975776cdfdfcbdeb1cf7b963df6d6fea762977feefbbd975f777777fbeeeeeeeeee8e5d7e3177bf28a594524a29a594524a69ad9f5b29cd1e074c532f0cab15abd927c36eaa0e46f21d867daeaf7faf7a23cada24d85aaf7a2f87e3aab5d66a6b96d5fa57bdb2ac7e3678d9acdaac7e2e7b69c06c5cd65a6bed752b36eb556badd5d6bbc12ce55b9b0d64adf43d7fea9307ddf1c495f063fc8ef54f72ec2845594e23587e0f3e8a2d35ce7017ad64297bf8614e2c8228fd5efc783cf28ca5196511aa9657edeeb65662726cb11f7fde007b31c7a71d73e82e461f79964aee18b76891bd599240a6113c3b005986c0b3145bec57b348aee613b2fa49a344a6b5c26cd49f46545f7d08a609600cb9e24a12486c1b595f7a9a66660c364bdd55df3fa969962a6b7fd6985c394f7a8433fa53727c29e77bf1ca6d56cbe2a457e7ac5ecaf48f29aa4a86c65f90ab77203ed32ab37dc681380d64813666c9ad69ca38edb9a440535578e553441591ec3df8bd10326c81d577efb03b444a294fbe6fef67d97f68e08309fb25f84bfaadcf5bf92dbfa5bbf514499e9fcdc774b2f5fed6676f9980f8b849e8d050c36537983fafbffe3e1825653ecf6f567c278b28d7388ccb388e7b8b835c7fd6e2224db69d00604bf1faebcf6fe40acea1b7429c8b36525be860138eb3deabd87776effdcf45fd4994737fa5efbdf73ebc7e73efbd97c36edcecdefbc1d9e77233603c7c62f6624dc66dd88dfb560688dde8afe1da2de6ea42204af5d77643c4810051aa771fc019d5a738095629295812d8bf71bcd844fc503c530654abcc1d9e18301bb2562caaaf44ac9957c61d5d73abfeaa11fdc8d50351c28f8033aaef106497c9d54b1f268fc9d57b8a9752fff5d79f67af8abe7a0f72fddaa4feea47d8e237714665fd10327f1e26cb9f0d66cc5afbb8e4641f36fcece7d02ad8db966cdf00d07629df89de5adbd95a5bf23360374aacb5b83fedf1dbf74adec6c07828e1868859e3bc98f157bddf44dcd97a7bd2dddddddddddd2ffdd946f06519190e1901907c65d41427b9901e55ef3b9ee346bc8813b1bafbdd63c06e34d7eda5ecb41122cd45b964ab58287693d637b9481323d6442530bfb18ce004967df3b8d7015c5b17e9ae3f24f84dad6af5b5cae13defc9d53b15ee93a24d7b6e8556e95cbde7484e3bb98a393a222355f5f5f3d67b3648ae60090703e3c1729f87580d1c29d7853b1d135129976f9e6263134385a1daf8c805b87cf36c73e34d13d9a6559c8b81dd90df361ee40606664372b2eb77fd26a222ca6f208b6cc27570b82ac450946f44b9f557d6aed5e2faefd7fa5d7115e117051c7cbcc9ecd80d8d9319560347ed6e729e79e659e6f1739a5a6323baf9cca6c3ac6f71e1ad73b6b54d108812bf1d7cdfd73a523c211a820ff2c4d13c1977b39f6da35daf9bc8b383c42f7ef18b9f8d8d8d4de7f4e9ba6de3a3d94f80a3f98d6d30156a16707b21c57be4f93ea8c88dea1dacc6fdd00e9e97c391fd8e98a455ee835a25e31ad59d6f3ab2f76e29ec7437e3cd0e9cb36f1b2041444ae499b100cef0a20a200a16812b20aae4e3457b842fe627e6bb758e44208bc5d5b0dc8e38a794380c68e2c213e94e699b8054dfd5b34859c5e4b8a37eb01b514a29ab2c73abacbc727797ee3cd4e43e797878f23482e71478624bffa441e80e24354dea23cf0f7683be7c0cb341e94b6a7f5a0149704e2ccc2ee439dd6bec563db67fbc940493a92560ffea250fadb62ce6a4146b53fb7099c9639a99d57bae825c7150a6551ab2401b55c533e7a411ce58407fd5557dfbb7da20b5accad68af660201c91e28814593449b97925b127ca33461ddc1b90769e092229f01319fa10723800246188f1e0f93611680508610c1583a4f99576f28c0c87c8fcc8f55a976559280873f4803a26d545c424f3fbbe2fa64a4c44fd635281430313894234251ac7ccfc803d397678737ea92883695251488e3ec05fbfea20c2acc981440d9c9fbf80e1613cf4b99327e957debb14afb527e70b52caf718a852611c0444e1d30e9044034600aa682f5f07a882bdec20697e7034ff618511ca3f812a2015eba594f286ac52ca1c53e214adf5939ef2fa77c69493e6aca999187b6b03a570700d0cce1389309e3579fe24125b669238633e913c5f7e4e8a224e7e9247a69c44639a9c94830727c5c9d7b6eab14d933533496c9144e28cf933499e9fa79cc7f47d9fc96459343ee340a4f5d806721e1f4d3963aac1ce834d5e5383fd9552033dfdab6da00ac3340ad2aaf9d52e4196eca70eff61010ac01cb1c57fc419f3659030c50f78298b18033b1801d8c9d7361d6027838023e9a41de0487e2c8952185694c87972ac0db49dc04e3e7d6972520747f267852322382ba614286ba0ac8144f2fc29359056611848ab60685f417f351a25349f81597b1aed40208bf6f33d09e833511082f21d9cdc06d3be665fd31e34af6c4300ccd9df14c6311593442f34903e418ba07b80de8b29a83de827b600d0a663623c68afbd85492c06612e477799168174373f4916b7005970d8d7deeb12e8b3b7313bb600d06bda8360b63c80bebf36d250685c1719426698d893edd3e89015b01753494a8ddd88200e26669010822891b50c851044266b2f65aec830f3c83246f249864390b8c9a0a734f071ab2bd3b61d10a50270347f444104a81e797a2107c4a20326c7e3bf1007ac3dd43889b50067af618f3510f6208c9d642c09ac7dd5b86baf6c8b59db21b37d07a2bdf6f22966437ba92d407bf9a04d528047ee0ff43ba2f478646f7e9ff26c8ac9076db5a672f8314545709dfd9ea3604e09a18043ba94ef448f45b84773a5bffee677918e29e7409c17801af7ba26151866afbffea6c7009222dcd1395aa5f324f2c207da0ef2b401cdec69538c910f35214e622ac29e1c90c562d5cf6f7ef39b9ffdd80db3db65b76b37cb6ed66ed56e95dda8dda6b55643fd111114d80908bf32c04aac71411708048ad3489e3cd4e03333333e93031675a7a0f6a0970ff26cf6e627b959a4bbf9f30b69017bb24632d12a9e7f9005ff7c595393a712f8e4fb91e7d73e5ae5f3f363922918d1e23c71c64c5defa4fe1cc47958249289c95d864390d0c9173684517ea611ecc5549e71d2fc7c34bf763845b482d344720f412b0259608681a3f9700bb588543c22157f7c45ba2b90ece818c1e93e9c9594dba2450b8c871a32f672be0e1065821eb4ede86efa0c13f9abd14857e48988a2903c6782d074ddcf9f73f67c3920cafc3b4f18d4d3dfd645a6c0defcf29c1fe7cf6f16b13fe7472e640149d3e4a32ec0546c91269a14cc20f1011008443fa56701f441a0eab3bfa0ad2a31c9a10271b0bb122e8321bf7af9da0643be263f1d13bb5143063d8db608e817480aa980e4c7903d88d302925afc0e88321fb4e5483929c455008ee6bb6861c2b2074b3227d939af5390e797bc077afb17e3c1580b70cc1eec01e9c05ff2d73793949c263d265c8eee4e72c9069323831e4b22c70e9016b3dc21337e1db4d7be6236e46b115b807ced21261dc7c7c649c61e7beba7c9247f9a4c756629f1d737c110e0f971ad1043fa5e00f27db9690fda225643ee648df34e8438c938e32dcb51028128f5b38cb74fea93f18619c132de2e9b2be3edba8f9bf166f158196f9688cd78ab4d5333deaa2455c61b3dd18cb71904a254bcc92883371702a2d40f55f572cb40f5f9cc9f5555555c0ffad3c2aabf16b576ca1ef2edf7a0d5f7b0f46be5d4a7cb8f1c903ce2a4495c784205eee101ad12b3bc0f3f2f7f4a036449808cd500479cc459e69043d9af5ff8fdf1dfb9d17f06c43cb9eb09919231870366560f33ade67b1917dfeb57fbf55ac27acc7aba793de4cbdecb1d517d8f5aabea6b05eff7b0d7fee4be3b8bb3b5568ce5fc94eee0fb0a444d1eeed5ce85271e69f495dd8d2500e6abbb282801e792dec98a8fe4c38e2eb11ab357208aacf102dc0fdd71918b5c15178023f940e04021a70aa183bb4884064e30005b4e2e3083452b90c5b3fc6ed61309802c25dd4112a80e19afee68a415a2d0b74164423b92aa03014477f41f9083bd1a65e28f56f1a71f67704c21c40460cb0f7106ad4f7f85be8721a52ba81f62cb911c3f95d3f32d21c7f7e01499564084b880e7cb481363d9f956f53d6845e95bf57b54b5b2d6fd1ef55a5fad65ddfa1ab7ba55d49bdbbcbbbbbdddbbe2217b0a21420e6a865053e36ff95b7fbd356972fcab03b3655996f5d7ba2cdfb27f9763c4ee1676c32daf51801cdfeb25e4685931c624f4ba3f1090255afe9fed6e741371571170f6579ca9134311e633115adc0f22c8595ccf0fdd599f715d8aa9ee6ad0cc70be29a03b7fee82d039699236f8cba64c72aa3ee53819330259b40ff49e94a2e441a0c736af04c4c91b0dca1b38258e90cf531e0151a295599cc96bc5d845fedf65d2741202eef95a857251f6e074178574276f4c7102812cd58ba64c8df2e6460a9138d61254a74994fb13bfc291589ceb90d1a58fee2c8b83d20451228eebe3cbaf55b222f58a24d6fb4b2f64d06b1b4c96b5879b07e234ce3febb5cdb3de3f1eac21e6f6b9da74c46c563eba8bef395ee473a6e308c802821865aa04720530e8719274028ee2479fa51cab98a61cff62f28328f1a70cb0cf5cd12a4ce4386dacb73818cfd5d72ad6c7f722ad9273adcdb7e855e4e83ad247acde92264b9a2c69b2744e32e64f44314c3ae392c454c55e3cfba8d3a7f86df2cee66b443433a622574420d75b31640576ae8a973f0da7d1117bc1e15ceb703b62be3ece4420b979b646d25dacfa14fbd45dfc527757498eff816ca57467fde5d6e5d73521f4bf2e6bd30102a2f8b7f52736c42cb27c6bfe8db83bcd86ee34ece3178bc49cd892439c115fb4c5e82ed2e8ab874d01ddc10e40ae3e572e42a2938fe2578f5b25fbf8269005fbe8a72a451de23cec82f34417e73d8c2d2ede22215f5fdf057c225fdf62bbde64bbfe8392afbf35e4ebfde4a4fb1f8dbb4637db9e874dbee675dd8dfe6733823e113c5ce84becc5daaeff7010f3a17fc817771f66cb88fb44f070b93f378fc7f577a394be5b9ba781127d883500bad0a7d1f2ed76f20ee89899fa356df097db49feee260d07228f796db0a2ca9a15d02ad6ff000411ad023f9a5a853e8425df035402fa7c09fe1e19ce40d99768df036b20fc2526df4333d1405f02babee4413964f829de0dd0a590a15791213c924b36d0a6659ff817f6d7c70dbbae18638cd7f5f1af18af2bde2b7bb8fdeaea8a233a478ee81c39724487094c90623e923c7dc8d81fc893271009fbe372031902d827e83dc853f21e44e54008b1cf7b1027c3ae41528a8fe65f7f18ebf5abc27cb5ad6d36cf575e975e524a77df74ecc02e372ccbde4b90e4f9f74e28f029e64cedbd0845fe2e76237bf9b58a960359a08d0cc7fd1d31e397ef4d7026c016cc93549c8a49ba2b75375f82b036f16d889a3fef037943a110be1abb11446e9d21e6a7ee3e51802c43e07e026cf1c720cb277edad5ae0b897d7645999c6d034a7152e7008ee69fa88022c7896f084e1348982e0c51dbb48dcde79361373eef1fccc6e7f3b95bbcb688cab60878be2eae9946f2a351eeae92fd77a2f793e3b6911f0e4294d38741da8bfa39e88ba73c2fe745548c429edfee1807630899fd61b2ec1c746e081b5c45182572944c04ba13e1ad77bac3a98d6a947ffef99f4439f8f3d3dde2107c90cada67255bec89468028f3a31370469116ad648dea2e95632a85b54d280ad8f3ef142bc6a20ff5d509c3756d318384e4f92ebfc66df2bcf124e08cf978eb1d170041695480a3f93764af512e82f7026e148c9fbce174fa24a22bb023693fe588f22af2a95532ee83ddc8de4fad02df751c0559a08dccbda26afdd820a95760514c77b798af2de66c8b40cc1a3601389a1f65e015729cf96c9eef0c97903c7f86197ae2007fd88b9f5c4709387ebfe755400d05593e7fbd17b969aaaf34d9cfec29973d88cb1e735ecb647f39af53f0c9fe9369dc27d409a42b3cb694b21b5a4bc0b40ad8833c197b815633e94e723427067591b017c971c80c6874137aa23a898b0ae160d795c527bb6e0fc648806f0fe8003138250a903bf45071bda0cafc86247f906f5769bb2d442f2a095dbec1b0a5985a3275d75d4d2975a5983672a5e09281a38edf7f99009276f82e16e47e938bca57241289442f2edf60d0f7e7e172af1fddf17099f3a22e99ee66baeb95ed07c78060ef92b964ae1f48b828c8a2e5f877a72f9226e1b8031cf5c7b701495749250931849321d68092d294534e09c25ea6eceeee06693062c420c30c3f38116e599585bd4c4e012fd1c27cb02e40f8a877709273d71502f6684d0da593d6d81ec812678c716e5e7da151464e39e56c81bdc8b99960587c0c7b99ef1b0f978b613e58175ae3a3beae98ee5a08b426778c2ef292c94e222c60f9de25937bbac8cdab2f710601f7ef401f4e4e8405966f82bdcc195404a104adab8fe0241872c6860a372efd840904342e30c327a63082e3d24804e902ca85b40426705c88582132e4c68503d204342eb071f04c483f4822e57243415e18c1e6a6e4d2790813a50a5986f02e9023b7c3b38428f165073a003316b3cf946105f4698cc6943ac5f4a702f0dca274ce96aa4b36a9ceee56e07f218410c61863f4ee767739318a04094592512408983ab8730b38e212f66a4be04a1999176a05f664cf0bb5023b4ef3344fbc8937915a8161bb9cb4aa1f7a73e18927434f762f345355a1991090d01732125242c3090509d1846e4238a122559dd56f4f0ecdcc7faf1dbe8673796ab555165f28e5592511d97e68e64d96bb3bdd5df9a2c7a1995883660dc70697c749b562ace1d49059c37902f62eea562d4334d97ffa35b98bda6995bb1383070629c0c093dd3f3413a26971440b53f6a754fb2e4ff6af349caacea97d3e4017ed3a66c373e5ee4e773e39ab05d8bba8d0ccc41ae02e960b6eb27b591339bbc9fe97c749f4fd6f8f93e4bb0b0c5564ff09032afb5733898abd4ce812a2a9a1991560efa21aa2c4776f03d546d89fe640eef75b75843e3954ab446e899d5669ee44cad03371086e4b89ca5cb892294c0fd04755097b29b16716d8e3be80a70c191933b172f77a77e48e949727d39f17c9cbdde98e6e3096b8de7af997ca284246077c94ea6184e3233afa7c7a017b239c4c3d1a4ea19a518a869393a28c9203e45bb30bd80bd5643f0247ae07ab91e5aec11e87d393e973384e8a4f9fa3cf39c1e538e9a2381c8cca1ec7e355d5d5fc5c94cbb590a593b48afd8bf211b5db65025c8365d0e48af364cc647af211851c9e5cc08dc1c4e8c91802e6c7dd704d7042be894cff463bc5e87192f5a394cd9e8c52c47cb02ed6fbd61a14b0d38c645068272d676e017b9729d3f7fbe17093290e36456879b32e523bddada765f9005daced32f9887e6f39997e0a7bda295304c0c42c32f6f38328fda1174015aea73b8a05d8d1f7424a64afc50bee7b2d683454a6afdd922b6281abbf1d44c9349a465f186304eca035e1183fc6da6dc264c464fabd83fb3d5ff700f646a9512ab6741199ca28651aa3c794e95f139318234c3a93ebeeee97105d0bf92d640becc5b9ebe27211b38b502886e6a77647fbc59b81baa39427a354613e5897ebb99e3ec261813eb547b07bc2b68b3224d3f7423599bea3e0788c9e4c3f5e38349c9c74bd2593e9df53a6df8076b1a829d3af3232fd4a0099a6648a724761d59b81caf4bd19272a3c194564faa354ab54f0bd2a7b349cb0a985d277957c44ff03d9934564fa5ee644a6de552a39493efdab7d995ea54c47291fd1a7d12f5c803d1a4ef9cb4eab2c656a47b0bb7647bf8328f4abad7e7592484469d0e98ece2c601a50349c46a94cbf93e0f81e0da796d2fc24b9ca5aab5ac9b8b9845d3f5c62ce58f21b5ce8024fbc904df69fc29a22c61863bc74e82e8a9b5681903201a990ec14e73a027bb4c648a65040123d011ce50092beca4e95a02738b2e244f694211e6d22fb4312f4e809b2c708037607b91dba732c3e5226baf3214017608fd6d8501b9b9a09a30bccde85f10b21183be96148618fd6d09a5661821681b4c65626b4a6f2e8976324be4775b27b74277bf42892ec5fb76b939b8e18b2b135b85e6980fd3dd91db7086bb7e0e21660f950bb2e2bd90fa3aa68777777777753d91e73e44e9698ff4e9a9c6c7fc18527f89a57c03fa474837c539586946026a68f7069869400052f1658e4db8385842e1b74b1d57b2bd6d65fc9c13f081dfca14324b5bac11fba3477d2dd12f2df6374e748e23b8fe83e840499fc1bdcddddfdbabbbbfb4f7fe872736f28dcf8714b00ccd72c02bb4793ec28bda1701f0827e570fbeed8bb5737402640f61a2a77ffeacee3ef671c0e982aabcd889b36a824ed76953d0e19cd080000d314000030180c860483d1702c902371f914000d808c4872561f8ae328ca519843c6180280010400000c000008cc8c0200ea0178ab8dd1d8d0ff6a2a4a0c20246905e98da5d6af0f49af9d1d600e474f0a6b683e6f38aa26ed06104b9c101fa922708987456e01c3e296ca9e7d690781554c6150c936b9b7aa343fec57748ea8b0e3e6c2d6b9322567c60c487976e60968ab8fe6fb81bf5e046b1100a2e770ea6b4ef7a071d0d5c8cbfa3134584fcb31cede45f784646e26e961b6efdb570c4a996b0bc81e340d0934098a2e600a03ea3ab7db93edf683bb8637a1b6a086cb9c2f1a5296a1788ccbc30e4dc91d2cf8728f522f38fc7cf4491f882c3a21bdce1e251f5e92f05d20fabe2951f6b153e9f1d0f35947def349e393e17e0288d4766cf7f1d0cf46bd946e49d1809368bd86db451c3d18351c29999fc4a7bbcb4187c30722469afa697bb3506c6ff8df21be74896bd3923c8d82c7d0dbcc98e1dc87c73a8b490b748df167393a44b622425f2f9f06c8af95ff16af0875684bc821b7910ab4f5e8272e5df8a52efceca2d629fb4027dd168672c08c0ad87839a603dd27379c970d5879e505985ceadf5728982b5b56cafa7e1b6927b91386e2cc2e7c41e720456f97f6d2fcdc8f911de33dd392a683124b47e09c0e2de68939ed8846cad52708854f5121ee3a51eeec1dd4cd4f0cf6bb2866658ba0b14ea71f3c57e252a6bebd5d18048e8f6989029119e5592314c6d7f544953493cce23470a55c7b3a8330403bb16ea262504f81a13878ae07d5d83780aecb1edf41bb7ecf3ef0b60a1944382b52f1462fa73de581ba14ff40d1fb5c984c827e0c6a59c65b29ef2c6231e483ffa79ca7b9434b5f62a381f2e3c8b7fb304d54ca0d98c405d5e0e208ff393e94f2d234b9e374a8ec0d8c7534e4dd60b8403f862e89d7b4908e3e8c5a10cd0f8dcb7ceed4c01f73a3958a5e12a7130a343bd42e41d60e3fab21a299cf8a019fc14143416359c20a665e30368de6a7eeb0280a41c26b6c38027c9fd5b93564fb67fd9e396025822da55951bfd886fdd1550a8661388ae839b887b18e22a23cfdf771cfdf8018fa0be486b0161ae5c5aa349605a9a86622954f7d60b5484c26c2bac1efe36caf2c0b5cda0fba33ae4676007ea20c343564ae9f784401dad715e682c3b071ec611c569b8037738c6d6333c3517f143e0fb38ae16b1906632ebe96d760b2b395133a6564ca6b8b55f4632e93b34ce1973155ee72e07bd8381f03dd3ec22aa776c91ef787861b8e93b66081b2c95ab38a541da4d1ff4a098c59a64aac6819babfa98b9656d710e7398952d35cc8dfd07dbd4a535f2b8fd86c4eb0d2a21ad6eb3f13fafb9152f4ac5c696a32ad52087bb00aaabd47e934f40cb98c5f5fa26036c3c519c738f6439b743c21cbf32cf742cce85d98bb21d663e3a8d57004609ad5fefda25d5f3876a9bd06657208ea39ca408321d0aeb1a19e3febf389a54894f4ba42ed14284612bb4f3dcd1c0b8d3339ab0ab13228bb500efe13e792b12c2c91590cce9722be220587ed96f3e0ad76be841f16441a712d571e31cee781df62cdaec5155519d166ad8ca69a32c9098bf630a32dc71c7c4a84a5950c0a053e377144f2bfbac829b04d9c8fdb1543643c6f47d5c1027d7242151c820edb079024d826c139638fbbadbb00b269d0ec74808b7d3b306d7bd221406622cd82c80088102512cee69117e35b5cf5d83c3eab04cb5e19688fd72ae48fa76086eb57d1bc492fcc43f1ad0817025788a265cba568fa79bd163ad055fcf1e4ef060e96817ed3fe106ceb16c5168256b0d0490d33c124d704ea496e7a74ebc46c148869176fe2d8978c36d1784a657a672b8c568332229175306734816c460e5d93407a96230e2af72617fc2c388458a2faf5d72d67418fcd53febc9c928d52809ec1d28b6142aae49fe0db9aecdd77b84a90bda3a30749568d2703d6014c3f34dad5ea8142ab2686e9019fe244c58a1ea048e8e1f002df5255f7f37994590eb0ec46b72770dc474729f749d34101cb0578b9d688b52234653d67989c22f641adcacc3d0c103a1562038f285d3c49ad304d91c691196094d87bc2610b9bfcab7bb184fdd36f985e622c7c05ab448de7deb530b5bc0d6f9497916b79043a4174a90b414fecb0baaa71e611f4438aafb7c0a5ec606ae38812a065d23d01b95c23f109c8bc51e8498f5b982c297df24883b35ad86c26be3e501f2ffbca632d7629acb4ab1fb48b69715121bc870424423442950247cf6179746c710d4fde2a9b34a7a286e2c45276b363f6d63a7140adcc5cf622cc65c6be3b7821d429ee040b83d9ddfd1128174eec14e04ae14630e39c99cadcdcce48366174648ed79a2a3977b51b7c3ad110d4776a9a9ee3eabc5cc40e982bf65f4c7997af2a9040ed3312fe2de9918bbedd00b4d97fe3c60aa83986b0c3895d7912293d473415ffd0f6783bdfd1a1c781b978d6796459f0d1d84b4f8fb9e384830f95aff169b6131b6e55223d687a1fb5c727270845437ced053eeee5b358d34d7b4fc5a30c43d01e919e7460acebabf2f0b1d3427d68a47f10d64e2b3654c64d1b184e59c2a3039fca652b8e88da95733032469adf353203249dc489e7a279e6616d2fe15c5e618d089e14f26a6f892c42e8bd0dd21e80da1f2f4d7ceb44346b54c9a6ae3618fef507314ce337457bc6077aaa93eb01efe61280ef584a89a58b4026c8fb766ed18665c25455bcd474c39e8a89992d6886702e257825752118dd216cd14b21e5295da0e105a39667d1049137ec3066f7519f46a07080dd43bafcd93a709a6eee84e3536623e355491e44e00558cbbe14322703b9dbc4e60d51bbdf5d39f8af82900de660ef9bf9bd99b3ede1e9f27f32f4145bcec46a5ac5b71fe889bac156fc73745936b11ebc40e4d5cb0881a2e500e5fa2f72c1c20231feb52018fb77114cf4540f9207bbe90361d7a4c488c380be1d32ebf2a3f3b0203ed7c30d35c2e5a3570c0bf3566865973fad9a9a582cb5aa0d9121c14b5001de33aebf8e4821ecf34ed22d4cfc44f5a7eded5886df1426e71b503f5515830c7540ecec201c2d673fd174c38b194266806600ecab3e89e203fe12fb4d271683583c2ee3fbbc23e84417cde508b3e30caaadad72ea090be4964957ff9bd3d8edeb737c9f4f1419189c7d5f2c00fce1d16b2399033f0be688ba67576617fdd5cc703cc5cb95f3974bf5f672d3349c82e6180faaa587e7d31c6abc54cd9c151d88299eec1cf9bae29ee218a0a608dec311322eeb1414e372a6d4c994e12e8027f5d3b2eb36f4b008f535d74c4f47b1bfbcb9a44ffda03edbd07baf99f58d5eb2d93210d7ecc1dd0bd4d1067acaae8284cfe3c25cc36417c42cb658fb80af592dd25dd8e586a3b1202c099a61dfdb1a48f97264c82d42262e720306c4c4da4e3c66a725ea337b710f3dddf1f485df98a764ecfe27fbe98c21f5e3dddec887febc90a13542b2b3701b82116d67640aae88c273718a4e01f3a4d811989c55fb664a5002012125bbefd1af85e14231a362efe8c7eb2a5f9d166d8f0a2378e7c70cd960fec198ec94f2af1a16e8a43fd9a34f5abd61fcb9ed3d9f4f7a65e2512364316c4aa045781101cd604cc490ef042d5a41e1c0f549224a1cf3dafc4d9a247351e9f4138fa2c6f0c7e7638acdeb38829ad53183c77483983d7324fceea6e827c3e1c9ce136ea8332576d6935ed7fd05c241c2137ec164ccd025b3471c18b26313fcd0016296668c91595bcaf6fb17e5dd60a6039e45ef3298cece65124de31fe97cfbbbea2cc96fa8e48b4a49951de9aa5298cc56b198315a169fb342b77279f557e99104a15c673fdaa8e1c365d8ad4b377b55ab721a399a7d3cc63920c89210029d9dffac442bbb3a71ce5fe3cd2cf5530b999cabf53786040604dc94037924c7898d84df69759be1630ac18b54afe2bfe22087fd1acb3bc8fd40eced12d9acf9de6e8a3210b50ca8f504683081d01099d63760bf5da65d32df1c07c5a1eaa69458201cf34d0070fabf3a6d16b446e8ce061bf28e6e8c8fa2d565066bc1da9fd85067f5453ecede99dd90a1a5859f5714b48af46c58f4ccbf6b055a3a29858a5a66925a92f219ddc9c5070641532c2362f0a2b1d76a9b905d193b6869a165a7edbcca5ff799331c58a8ec4bca82de6854e0c6bb5da393ae1ac4fecf6cc938c84abcd3c601f8839e763dd5ccef90820c03de0be8ca9bf578559e51c1770962b62c5fb2ac0069a3f0a5b54f62922ff679f5cb9784142eb527ff4294aedafa876dcbd28e768c03be6c50798fce10804ecc53a60707fc9b0ecb288e91add32792a33a193ffb48410e93f33090e129364259df4537d2cb97ec68ed634eaa92d6defb2e8bcf0fb9f299d166033837c9fc984195b40146291c0dbbb9f52bcab5338b4b2ed8d419d4888ea91a67063a2c4a63e00a0c402ec801661c72651d070018d0b6fdf77006048b9f669306f53fcb4cc0160d62580784d7a59c99af028d462322c53ee1926cb44b0df7b60b01dde8c278ef17792f6b56638301fb2b00497a54ac4257c66fc7200d5258910f21978e1d11416d52bb4b8e5bfd0aa9384377bbfc84d483b8100b22a95e22a198d258e275edc65018b735412b488b89b8e2e23a33c3b65cfbb410d2adcfdde978a41021367cb18f60a73e24c4f695a7e3d7f395738f2632ca62cffe43fbc698dbcd23834788cca064441a14750374d225abc276a9410b0e4e20ddb8c2740f5be55061d045e6709ee3152ece0d7b52f84355d4a18da0d00d1d00cdf1ac7e56724602b4e978b6864723473b0b7bbc44c44ad8024071a3913db59747f5695df4633dc679508fc464473a992acb66c30effdfe118aee6e17191a81fbf2258673f584f6e547428f8dc142831311b62a9fa17aee3270502eb09716881b26032270088cd85951fc8cac40a4ab5384bfb21484a2e82cdfcda02600d0bc642ebf79649f90a479aeeb805224b15cb01c8f57bb34da33119c58ecd5ec34c3967fc9df8de8683b2a99ccc55b9d7999d2c6e085c1136a2945a94538dcef0bbb0d686c4efa22d879d2560eb9d7876759fd24f29a20f6c95be0f0524253b0102ec4181c1c3d864dd248360abccf23bdca859109af95d27f831ac8a156b055bd975c517df29a2e8b3ed00985fcfee45707a746e7027e60f13cf5d18708f0d647ba99777370247601cc618d7881a828d8283c2997a15dfe46218c45dec94415bdcbba9bd59f4292d1be96ad2bb77d1536631c8078b48ab34e8de4d332120f828dea5e76a434929a9c3c111b238b08c3c099fabb1ae4d74af0cb152aff686149fabe10c69c581b9c345fa309193f76820e0971835d8b6b2e8d9398e36d1926227cacaf523f143b2f25f08a948eaac5de2b6d490a9c6845b26bbde3304573f7a1c2482ea47d37059c3db5116cad424cef08b9bcdf9efbeb78cf9f4ed0988837d9700cefadc737927a5429a86c1cff04dbb03b27f1e51a7ced613407f4d74eb025ba4810ea29c33608425c1db89ea2d260f1df95c2b22d1e4a89c237a8ed805e91b81ea2c3b33f9a60808f993fd9de3f091b4ffac06ae35049cc31a4a96dd0d764a451abf10c80704319d46b7996fefaecd67d16feee822422902043b5f67dbac3cb7682806cb22ed2a3a2121b551dd6fce0b6747ee5cea4982edbcbcf0a3568b7b8125b2e263e6660616f6aa92c5cfa468b01108e83fa86f59adc0c9f0d3d260a8392bb2f5e7f0a423d99d56bfa1533c6a4f9a33ee153106eb28c9e3e6db478e288f568183a4a0f9e087cc4186ec9c88eec76e62799adfd438c3a690296036f4295dd5918e18444ad94c99fb191fdbaec6b94d971d0dbcd6fddf9a43a84b0f72e0359bc482f5676d885b3a739cd23fb2c7d536c61264a245981c4766f8bbce9dbe4672baf1b8c9df7509ef4822d4a49904e568aa4c407866881519b934369f7e5564fafb22372ed005d0afa05820794ed19753c5b5537bc0cbe6464c82959ebfc64f1f36e1bb941eaa140ab5cf565b56d452a5eae92a89db6627184d4a15890ea5c664b55c3d95a2f4a0169665999e8b4ac9ca97821713954e29c89e4fbd12c69f5f2f493a77810b066c9d082b2c84e2c37ece1b0858fc138f5e2ec969be1d121548130c7b6db49e57892549f0b130b86e9d063991306c12455034bf8bf4036875fa6332995e4bdb337b3514fefb062350f3b4043275a0f34d1559041d01d7bb8a7d8d12a55b31286de1e69ce80a04342829e06bbb8ab1b26757b0db968938477345225042aeac6c067f8add376b9f3fd514ab51435a627913e021cde2ce31b05c99dfb2e1f4bc3c51a764c2bda8383ffcbc54666d876eb7830cc2e1434cba555ec9d7b050479cfa4a0d48730ce703426fd1abf1d42c73b1c7183e311b8cc6d24e84724826eeec587fff7ca750951b2483e74ba84a9de6f1884fc0a8da6148a45edb12a1bb0c2a7d6f0abaa520281885768a151ff97d46ea2b3a8a27fc2456c89581c6d5f9909318ec8467504750654275e1eb995b05b6323e05fce282dae22ae6b436a56019390d74ac73823b6a58bf9e9a99ada1ad1a38131510590bb0294166e7473dd4007b68e37fd54f67fc09b189d19cea702c2ccd81aa307435f15690ccebf9ae290541e910b945ef5610a14940b8c8511a27cd600707686c089c21bd17bc43fd20ad57c422bde4b0088aa7212b0a48c766a1dd060731ffebe5e55c990c1ce8805a80aacc6b4811b239d606af4dd15e24336e4fc1e7b801cd168abd24dcde8061c576b0ce8292bc36c4f5a43a85e9dbb8017da3fd99033bb71abd84be1481f32911ebd20622d83046f49ac06e6be4f28cbfbebf95090298ead252909602f2aa107342f73574a7b3a28e4036062377d55f26e8e5bda0b3ac2480eb709f0d1c6812dfdff1af18fffb7a71a6b7b1dea76dfcf780337ae80d1279f62d8ea9db64ae6c80ce68cd178ba8554acaa4f838b0c3bdf0dfa2ce368215c3a5ea2bdf32ddf8569e3391459b2fd6f621083e397d4592aabbfd8e89adefbe8dafe34c1930091dc5de20bb88def08cdaa93e422005da6d1d3e01769d4bc5a87f228bfdba315d03e52c291c134687aa51c71e31b12b4651f1c50638a75ceb2f08746c57e54d0ae3f47195978f688172c8f596a2ebf1626655e3c0234e460cf15de1706821342a03370369ef1ccc615832976be63854ba2bbaba02b46a39e58a5784a2c32e3215a2e701f673e53591eda4230113e8a8403f69f0053a46b9d2f5d0ad452ca9481ba0c3544bafaad6b28d16b2663941e0da2102c1a8aca2316e656465507ed7f7e86ec95737c9834f16ebd8965450f255fd130940ee366acf9213b9f7898eeff164088aa6dc5644f700f68fd8f5d0914f2e5930f0aae033f1ca49a65918314515043adeb1294edf4e17967a341f0917c4629833974efd240c11504759c30a1a8574a646a6e7133ada624620f03ce51918f49be147554ce812d63e104d6cc5aa50050d996814078446929c2dc8e867a0178321a58d03f834cb2463517965e6a08ae3694916c649520abff8d471204ee97351f2f7ad4a0cefa429f8819363418996a371486ad469f05b9308b573f58688cf4c1419d9dd060976ef648a3ef0ced078b1ed117121d911907da4811f66bd900f4d2acba509f05f305e3090f45f5186838d10e78d4c9160277901bdc3693cc1efe93e18e0d7255c635d40c634c876fadf7087c7bdc2b491f4bd81ee5398d48718765790a85fae74c757f7e7a6c04e2e57551205ca9f74c8ec67d8b2151aa9a9cf0434f36f3f2f9cc4d41e49834f20b1e9514b15aeef4fea61778c09c3ecfaf1769ad7526b3089a7e8d8440598f44c6d4c52dca83a59232c3055fb935dc5ae1f8d3c08394e9a110a854386834a2d650a3e71f4f06ccfc6414c44be3ba5308fb024a2fab52d7b9d3807a938aea55ccd2a2b80b2230b1a9eea3a6eab2af68e73f4a7cdeabcea474de1f85eed47a30ee106bc56433f8d957c9ff3d1264fedddb8a663d0e611e0ea383cc4d478960ffa2cd1ce8e8426946c10288ea8dd5f509778d8ead4eff3cf6f3686bf08e80d8341a3325042b86944aed99094f11fc8677af4711f393f59d27b0580bb4271f577362fd44c0879e924e76e79a6c42037dcbc452aea5c8213a7f16db48ef1ba88a2f64ac90aa53f8e51804e4a25b9eb8c35c7ff4caaac472752151e746283debbd92b9f438c670cbfa0300bd2e274fd08f7b653b429a77092a0a38e44897f79c50965713aeff9decdb0f555f9bf09074e5d49a0d06f2f5ccbbaa2898df4103347c2374cb646cb3c632d12e02aa8993bfa48fc0fed5366e1e053d9112c7aa32713b62ab8b9ec8a88e2a332552f163f0ca19b67bd21cc61f276ddf4a29c027bb8bb5a8e9e43ac84e4a6c3e5464e1237fae849b365eacf908ce74d2b3734f7f32490d4e74a6d0bd10fa0960c59a682d4f2086ba8256d8da0ac00716a687a229e1d43ac15275d25611d7a385375a3d59537468e242ddeea491a7a9e6ac2dd5cd5e1fa511eae32e5c100cfc594bb2f8a106d99b4f327dd24a31dfb2ca3054c30e7f7f9f31ad40960980e0e656fa1bebdfc065486019f1ba4fd111e3aec08b28aa5b544157809a971cfc7b1162d20f8a50c852f4c7ba4e5dade20e142647ff75db64994b40978fcba98501a91cd6ce16ca5aa118f352d20465a130bb68c32ba4b6c52089dc4ddd4742e5c57caee4169297ebc94d32fcd08642fb53e23277e3a2332b160f1708d89d2733a5d15a527b868b169684da8be10ee2dae274879274052364a4fe984786117a33e7e00d2800aa2f438b51fddfccb528424a235b54842e028b290c32583bbcdd2c1ba24fbba4ce532cdc3ec9176267481e621fe258ba2a2f3b96ee262dfd6d3274f0f8ffc23e2a044164a76ba9988fd1273a8a04c1c3bbb3141492bcbfeff74f1f5aa8628f95cc36f7cd34bef990e9019856e5d7cbf89ebbb1e045a558cdd2e4a881416e7400e9d45a6a9f08ba07a38c2efb0548754138b93bb993c22a92dc74e78f41521d3efb69c1d01dd3d3e6e6663e1122777f406c7d9ebdd12998316e7f8442815d59c743b36f7506d659b456071d1dc9fc4142ac2c3ffa25d640db93b8799efb8307384d37aa38b9e1268645d207657ff4da0119fd72eb62ea42b1168c4eb8b638334f4bff0a654a051acad0f0aa3a14b5efd059ab31915c73b6444561ed959798e82811a5a371c33a321e8720d801fbea8bb2471a4bb1a5baec5d54d198de057e7ed7ddd6c6eb805ce0a30f15e91389a51dede8d123750b1f0b22552c034221ad41995611d2ad8d18cb228929ef375e7b3d9aa47653f683c7514e61a2c27d090c644ef3b4753b8eef952f02395519e1eebab6b9a288746f6af26c8062cb2d1251a5f604e049dd93d0d152328962bfccbefe5f4f1f725defe1b0a3fbe54c0af9cf1d3257ee1e45d167b95dd2bf6df635be0ec71ac17f396c3d5161334450c06d64880c02424221e4468e051f538d8f0ea13c6b1a1106eb476d446a7633d46a46e5241e0aed5495de73950a178de977e9bd1ca95dadf784115b583f0a605e8ff2ba2bbaf30d939f8543e9ab37f3a7d0f5aa5fc6cea1c458fa8e9ccb7d8d9d7060721fa3208fe42be16609191e6bbaca0959f430262a6b64e9801613e8202ce1e33e66b46e4e52333375444daff08d959c325a9f1b607eb5193c540dc80372b557e0384dfdaef5310f1c439fddfbda33fad3e0422b626aee97df5405e8c68104902b57bba2313777eef95187e036fed9a7ecf2df5fa0a26aa72f7eb3e7a8d1b8aaa17195e827edbda62633a0d01332652df9d00ae7a2de12801da01ce67c8a63fc37081501492067426608565dd63f733ff76412baff65e33c281d95cbd47318b85dd8ac5a1d97c6154e3b473a3c7049cbfc8347b02e05a9064fb8f0195c6c7643fc935fd0588372a11d66d7c78b96aadc70b1cb82f67108494ab216b30b18075f53de53ebe4462a82d697b5a3e1df1fb505046a72d8ddecc285d7ef9269d05197c1915b80b9b4fa4d83d930d6e9e7d155c3862c37642759782197ac7d32f95184a6610453133c96e523121ee22491915babfa54925d9a0ac1d3efc3c2d8886ff8095898a390d60cee8bc262e87ccdb785f970bef451b6c5b632e49c250eb9698e40c05734b13061ce48a729e3ac073219e6d9a5765442c538328d3e4e92618051644e4946e3b2540da2b4d2cf8cd771e5b037133e37e39b04efb4edbd3decbc78bae81489244c1c1be8888b225a6978a9d94a7ae07ec64fab54f2571a8311edf7e35e6aea0cb460cc562560fe44c55fc664df17d8703f9489a45a2898235c61ce78b0f57061786e01a33bfd6dcbc464b96df3ddb30a7cdcb8e36bd993b8489ec8d5de8ca9c043504c280c28728c4e04e608c7f1339aac8c45d576449642e5745c2df88d80a8b55698b4b3c4cc498b6f0e13101eb62d847a65da4de4223141316a786cea3b876a2709844e53fca4e9970ddab74b64cbe489bc2d6284feb00ceb22c9502bf561cb4361bf293bbf6c8a702f017ce611c851be69d09268ac0a136f6143dcc394a5f25b7ccd3d4404643db2c16fa433508ff64302f1e5a3fbe8a7cdac61e153c4ea6e1a7bad577bcd82e0e0b33e9284e1a89731241f60a53835fd701bc516aa15228ddee1fe3596232fdc64b3f82fb7b9889a360a476a5809003c4716f06090ede093f814df093b6ddd74df3135374479cad9cee8e7290761d16569ff4b6fac725f0d403d6ae8bc79177b473f773e76176b0dee6ee3a5313d598d0814358ee7076377e25d86e3eb643f9ee81fba2a850169d765cf9181f6b74c89f48b1318d2502b4d14c51f4c3dcd8cdf5ffb115afb225195ace4e1d423ee652c0b3bb263a4a80f1d74c9ac730f394d21984bb19375178bbc13f5dfd9628990d8bad992711c79a94a425b809004c2407a035dab8e5db792d0f52cbb460a753056a73101beb1af493b7efa03fd0ef0322a27b90808012ac1abda6fcab2e1a42c4b3235f57a9710c4b5acbdd95eade41856d4f0c879edbbd3037905d58566bfa3896ae7c42051a3541f2d23843b8ebb4335b4b5884f3f6cf955ae91c74f4cacc861370bd8afba07f02665a31add95eb2f39178c9285f0dc820540aa4510302589665c26b608077d636683853bad51f803753b72a8d913abf6863c08afd383a303550a99ced7cf93c20e10bda65acff86cc67b237cc9439c8c89a6216a3e72eb49b06ed445cc0599c3e6ff54d8634c7bf9039cf8fe22753d34dac82eda80e543007c6a44a8528649a0aa8d78549ac0e497f3a4d36703deab180064475d83768c20d51d1f45f5e130b04cbf4d75ea113cc36b26703347f60c99435dbeb847cbb734c47cf59517b0d3e6402d9dfd163b899235e441a8f23bfc6dbb1788e849327d607491083c2cab7c1a5fbf818962c0b7141140e925bf6e3c74ea18ef3f0a47d079fa244161eb4be3ca03a1240e9289683dc2077d817ca1bb4959f1d8750279ee487f8dae19d64cdd44ff8cabe4689088758a2ea08cc7d29dee8ff5a846530d0c303c8664b3cb37c017c9efc1aaced00704675ccc38c01c2288b7e2ed4190fbcec5ce4974a07aceb2e352e2e3a7996d55e59b4d84a99980d2ccbb20f4627cfeb750a1932c901734297a1925b319b44e9cf653a10769f0e87aad15cd73b52e537d9d7cda8b4b4c65f0e85535fb36a76b1ec2114ce67337cfb2505b4c807f424091bd5c4f68551cf5bcbd1cb5a536a4d580e1b86b69b565a0c82a1a6392c25031961b4b5a30e0a1f53ab1d598c8671d99a1591ac44a523dba07565bda892ff2f0ed9eeb5ce05544da43fa51fcee8f3db05f4834a230a8d7267682ebd8d17d80bedbcaee190ea20a2e82c1411bf65a4446f0464308eb507273b60c64e9a5a3636a2853a455ca2ec937732dea90e2091901432a069dae61337203bd607c455bf23ac32e2a0d77cb58ae8387e775ba6741edf34f8a314776069d21e390c120941348933410e9347073d5623898f53b361f7866b5f80612e781bfb932118c4241a30128a4cc0908ef24e8429b248a9c75f1087d22b7bba7faba779218d7353d206e131d1756b754d2debf673ea6f9a81d0a3b30180794b2cfccdc3cf2daec04abb1f572f6fd7130977273b36d04e0f14e720d0058e10085026d7914ecd7799aeb71704106cccc4b162326c9f4393405d4a55934f69978d9da631d9417a968d05536d804ed55199cc986e0606ee69d9c96d3efafd6d8e0f442f183a6ab541d9f4c5487422915fdc8ea52ea19eed62521de42b83872c1974cadb30ea1a001206faedef6ae546c1380de9fd98331e2acfe5a503191d65e9316d9e64e101b0c31c08b16f253f1da982902af9076ff8c03e94011d863103c3c285c1bb2cc0fda41805c8402345373d5da030688151090212ad2f8efdb2aa26cf20462882355ff2957a3742a244dbac3a9fc6b0461f408fba860c50821d8782ae386c4b242b676debd512db04a914f04f203ba0097e0932fd26957422780b15908510d8ae1ff00d6efe5b6a6f70b284e1513a2355f892ee9880326dd57c4c9a9aabf9b15b4143083f1f093c8d550ba1fc7941cd1dd3202253944e0649b33160f769cf7b9133adfa1280364316f160ec99e90240add1a3eff1aae59ac15b6b96345b6c02fcb2700927c7b6c489a3d3c3767973452c2056874e6478614b5c10b44bb7a8e2d83854241f74ba9e4af8cb6f72c75efa9851c165dfac6326279a5e47fe4fa130b8e37f173f1be60ec11cafa53e0d2dc4903f5b8a35e65d51b64a4886986a76c4c7c8910add8b720ed90804fb7779965e4f5b045b94df9d339190166923726b56fbabdb9d56d4dab9bd873a6047f5b3eea6b43b45aab8d2fb79e6828226887daf566f97998b4ff3ce782fd32a39d87c59330148126331116b3cf218ec6cf9c1739ef40020c922c93120a106cc781634bd33e47172533120347fc6bb16ee9365f80f283f0c0bb7a9c94ff89e40f70baef77c6def2fc133c717860702381e172eefa3d700ba25e3b1234c063f9e4c0e3c0214dde724969858936516fad1ae20e7f4d82533798381c1a743b14d4cb0bd9877b4dfa404fa9443f83ec2844183fe8a68b58dcb671b38d265c7e4afc7403849c50e2c25997fa44c9ba85a46654e60d13213d09f162d82ff4b79549d53bc38f36c549717ee4f08f29def568289bb44062ac25d2f08fb83e09d306837f0684ecd83b05b12f807f8e30f3f99aeef1dc71b4a4b1e100bd55a7fdbea049086c81d94003c08b3a437fb44de782cb7c34a2ea4e4d385c939fc87a7ba462477e91dd602ca32b17fd5ef58c1aba6f3b42d76dfa955322841f7774bb903eb2f529d2913d2d45fd633981aec9a3477361692e25cd482c7d70e2ca39d4a8f64eb4d2577b6fcec347c460f4c757057796713f8cc529261108f764fbbb454acc1d02ed588aac0b18b44211a36d6228100513b35e0836be2ebfa589429519479353d72afdda4aed1c262c66a2399dfcc10966db473005495baa7ca6fd4bd88d08de27812d77f65f5be39ff1804be30096ec9e102cc62e21d87b8d0b99811bb1d783a1ea4b8041256d5d0c7e6aa5733c9a7601e60dd6117feb93c9ba002fc4666d284428bb7a70dabb898e81f89e54e13e322e041c892a203b104050408332830e8be1393bd7e3de2d408cb5d18b9f63bc81dabd26337a066e4e44f81ad1e0d40cd97bfdd49372084f5485c18dfad870b27bb48a4ad113ac7fef19cc3b3fb13ca925bd695e61978af6acedd0a3f0c576436a8c8f1febbd8db8750ba44ac5df93c5564389be4c43ba176d60175158570bb40808fdecc61dd57d20cda214a985b4f3f3ce2740ec7a8aa47357456921da7fc9d7027937f2d71ee92b375d4e075d21097667c62a72955a846402ea30094746de52d94b02a83a46c41bd91bd5cb66327cc0d9e446c753b131f57b80102277ed5d2638332f911a9d79473046c41c3526d0fcf676b14260fa886aba327d4aa0a3239767d90b25c06130515e2ab24e5058a81bb01111a507ab72deed79c675763a803c6fe0f0ffd449576902ddfa76df6a4a0a2780e9be54d6e4b86dc513535d6c411dfbca2f6042810d495e8127e54d8dc054e8424811269c4ad5c7496e2be82e54ce9e9e37d2ad2c0509d026642cac8e9fff469646d9cdd776eb8cfb1a716c40cf884c1289c112f23192b297bfb81dfabf68d58b73f48103d1534a6f10340c4d5ddb5fa752bcf8c1b99ad06a2aa4eac5b082dbda902eec764cafd321b4be815998cad774937839ef4ad535265e2bf57fa0ef5454d1bf383500d7e4087607a23619d2acb7686d38d00289fd5db1d8f6e4005e8156ec3be95918467a90b8c366c31495f30d3e36f1cddf864431d00fd4e1ece553cbb5914b435a411ec75ad1c938ee417b496c614a52896e534dae7c876a97644d5a8668d12472d6a3e10094f015c0e8a881c3022d2c9aa816e2c2c25c91c493bea628d5563ea6d68cd0e7b10a6c7697cac328c21ede1a40c408412bc859ef0cdeaf66076679523543c934c32cf8f7c8ab89d27e95f8f58cc64d3e9b84d162a85758561cf4161b60af984f94aa5c255d63df4f630355f7dd051ef12a37750047fb1be586caf52741a45845655b88deca1c3b540f73c0e98362eff038b133624256dce749dea02cba729c5c5b0de59e042937a2db9efd76a3ce751a229a2ae5fb07780508cd2820c4bddbba16e67382066c7a8cbc0b8afb4295b1c96b4952b3d6cc55ba64494967641d84d32f9333eab5e3993f676697c39e958b094df1136d142cdd8aa603825cbc25fc2b6664332fce335dee6fb0d545de30da8976011162381f47cf13a0509aae6bd009750cd0d4fba04219a1dcb6041bcb8eb58bd651d7dcd9a6e99850ee1cb96f0babdb46bb2feefd7b497ed97913d02c82ee06647951f09646ae663a8d4f187b7f6b86b16d782a12dd9e1d48eff0f7d5a037be4b1102bc4b3ba2f04d5152079060375b002c303dec212271e6ddd7b82b333c7bf31db4dfa2e2ccf75a1d0ec386859e32ddf01c67cbd558b0eb0a6c54846261c9985c1a0fa50504ffd7727ef35f1e656b57f11d035f1d4253a13d7ce297cd023bab2c523269e2fc2139516761967ef3fd1626d4d4b99f136a613753884961b8e8715cd2a22ecfda9310835054c7a2160c9f32d2c3c8c0cc9673926647954e3200dc36dcd5d8d39d9252a4aab4c4b7c1ce5e1f4d1158cfa414f2dd0e67c546114386ce743b0311b1edd55d66efe6da558284838c421f0396db5b063234cd7acad2cefaecb43cbf807e05c3014344ba164aa7c45128dde4486651448450b52e7338f98d2cd4dcecba899e429185b046f472b167564a782db134fe2dafe110f7c74e674dc37dd661713cf1a59e7da662c55ff29a2a22a9b6936a720c6bcbac90ba4acc21216010a3f46852557199b3d9e33a0aa8b6f695f44600c0fed83b6b43c0df54ae52916d28123f6b9fdaa2ce7ca890b16598b5784a65b09afb2071a7fed5d56b699a8ffe0b01bd0ad030926ab6e84641233c81d56d342c1611e7cce41e6bac0747715924ca4bfa6506885d12fd4af85f08226b2c9e74a200267c452f3f0e293ff9d5af974f1e45897e84983d4356c1f3f397ef4d2dc90ba7be1e21542c59c7e8c99c4283653ea8b136e74d1f0a84c60d54d4c1ef0f1f56b110fb5cb7e15ab800bb79e87084f2e24dfd1251419d55a5136a4e7cf6005a146554033670c662576d6bc203721f008a40fa21e4ba51d4c1c427080a2dba47e90a6617b0aa24c952c63a6dce4da440a31d8052036663e63d156115ab727d510d735e1323b256e8ba8300ce53054da5ded048880c35354f527ad0f04c49966b1486f1c3b2cef293c722748f32211eb92ec2cd7d589d4219e2e0020a51c2968ecd3c2b40783b692cd82a4f27751946e52adfb55e7b5f8788ad078b7d8ad0c49b40445f802e38bf97f29bb200801e19407848786055ea30c18b58a50ae0d80b115b83bd63ccf68011247b048d52e1d5d465b6f44838c1b7bf0063bda0d51f666b19c7610f2eca680d2c9e1c661ba01aaa26f9bcf53780351b97d031b6502c791e50b87fb53278e52d2330ea138470e7575aa1c7265c91c826d0a98ce21e4237404543a5e57d4f1817510407957ad45d202b61e66dbd132043fc8790d8ffb7e3786866a32f14fded19d6408d1b5fba91129a4b978e641cc1e67bafac508180f61f691c20ad6e33cb3c3e7e5819ae524747c8357040326069097d0bb455a87c20c12eac3a9d3e47c062747d0b7c41dc41c906d7c4323945f7c43e9ad7003115f3fd7b718ea8f8b275a7c1d472cf0bfabddab741ccf4ceea1c112a4193e8799873979844437e09b4bb9ec4a36923f1e27452c93d61103175e7a6e9663d3438503465c7e56794fc9f710b3894900566e6aaf691c638dc5ecd10a640e443bb1b5bb997276b243e03d717bb2d8b657b523420e1403d8cb02dbf26a6b472a6ebd69dbfc47ec2042de16a4a3cf4a619629895fd58e0028934d700e72660c4bf7f35e7ee700aa614704601c6fb8c49f50d7baacf9c2ed3cc01f8ba9aff8ffade8b7bd5d6e9945d94d5f2a192284f6bcb77f0001ac8af44b820aadf03a98ba50426d338405c8bc1dc36254e01266adcefec890e0134408ecdeb5e5c5930427755d53f034dd909610b97e19d5599906af37391f8960cea1279d9544a505562b688dc4a61d4406803a3d9701593a2243bbfae8551b34eb731aaa22f527d73c5d0f7397854107c522a2a87634d599e100f290beaab0448b477740464026a22a5349f7f037f723d7ba5879d7a0d236dd4b2881ee02ab19a63f7b7f2e28e638ec8b2b650ad620ab857588372a429f46ae9c60ce6b812c8cc5f75d41ea8f49eec4c6804cfaaecfa2dc27b0d07e6f9e5d21b8ae1358817061a66c41a10574ebc53469ba5efb35070eaf2454a42558ca934e6f32e67481549c660907520d40f00dc3c4aa290786248240d0908294ce7842ee820d0c135801267614be0e8ce424098f5c401bb433e4989708033c550c1692f0d50e5d264f8f5a3d4189af52940ed90863e8b6a9023299856b59786d36eff4fff28c74c686c464b3bbdd2a9440427aa129efe4e1439a54c03a117943ac9a31c7ad6b6ad033ed91d4e16404d9a434b8cd9b4dd914196b2f2d90b6467c3f2de8c4df8d0d126a4bd3507b0b140fda4e3716787122a7b36294f18bae2240a3cd5b955d8b04f322e0bc2a102caaf983cd272e102bdb329ee3ceecf99f4535602191e3989fc1841662842494b6c55f5375bad08e73ad88fe8e848e12eaf8da644329d4607d6c0c439d88479b53029a90413501142b74d9f6f03cec873d57ff2ee7abd6d583d847cfa3141d8eb395188b79e485ae18ded2d79c7c3ba18d92725e52ab9e83a357b25a63204daf438d985efe3ab1146b12b1d575aaba2b088651e7bd9ed99d75efead8f1987211b544332cfcbbd9dac31bc78dc62dc926faa3ff295c378e4f2421853d41085e8ed6bc43a82b9026c6fad198b1dbdbad5f8db0748cdebd865ca1c69566ce5862e7ac4ad638ca6edea19e98fef5760120ada7fca78373b9e84d55f11edfa209278000fe4be2c100967a502b66a3bd52edb49e84878fbcedd76744e473b10870bb3fca94619fa176dfb71f1c1d785b9009f9cf9a775145b15183351433cb762c53ad5847b5eadf1b897faff6da8cafe89d9fbeb1011253bceb37d507650dab95dd79980d47cb9a0f4ebfed8d8b73b29e905ad74d34128c662ff1675fff298edeeae17db0bb4b2f8c4711b89f1dbfdd4f090ceb71c36590b0cce247d859e1f2318958bff86f3b31039b9814b83f56e22c87cab71216348ee6e36b2248d9885802b8e94981a523baa1a589b99be37c8744c69218e4454c65c218a8f4110f0dcfd745c4b97b31be1f2818ba82848d1d43f31e7be5c16d23c0d5e2088aa6131d733b2bec8647a318860290a737a73cd1235d7425aa1ca1026a0111a55622e02291471051350c14623b2a787412267ce58cd5dc1c31f9bc1573d3a18daa8c445103619d8b3663468de5cc67c139a3fd9bc7b7664bd275e6707824d60a7ad9cb45f54c18a4c87841c21ed2c81a8e3867b9fe1813e7392045b472eceb7e3e1a53504451788dce36564ddf35544d2bab2cb4fd0ae5a4fff79e0434e96fe1b777b754b628c01cf1c4306cbd589936a0c70d4784eba5f97b2f547179a44bb6ac29ba5da319429625ae568447974e4e971f6f4d368e7819268ea0a676e0442a01402cba1721b439d3ed8a54554c59d4c422063af85c0d6f6f776211abd5b056dc9437c841b414e7dac4a2dac3efee14a826779f30bf3e9101234951944c2d8c3f86ee41e3df3c03901c580833173105b64ca10140ff01e4a00e1abc1ec68a582700be8605b62b357844772a38f3e0246c5de37f21460fa9520da080ee2ba84fe9917105ba5a39121af5f84010decc5d14105c7c007e5db664510e42aa1830d186abc17b70c7f91abc9d7be41f2c9c2eca09dc87225f8b5bab97c1046eaa68da092ecb60bc45603a628b8acc3d95e7d4657a1f0ce10fcad322bee945b95bb54adada2e8fa9dcd4ef7ba31d1e1824fb42098ae704c0aaa196604654cfab0b33b064a5ae831e0d0ff095bcf04cbef00c25878556a95d02d68a04c8e4b6969b9026664f79fa544fb2652d1d14d509ad056768091e28346e9a5c199d614ed182df1d5c5b623d47615a1bbcf19c2c54e2c18cd478ecd853eac5cb8e730623d4fbf7f2e7eef4452a553380363e9508edd216965eb688103763306cf0841000a1203f711549792a190c49d6b09600190ca5f3120ae05509b5dc35b302499a5db58249cc8841be44ddff339253c6c9ff4f97d9f8812cda668f13e242ad8638f43a66983967f2ad550af347ef9cc9b16a6cfdfa7633898a7c8bf2ceb757f8bf313d599198ec58816f6a893772ef9944bb420cc4fa2fd7d502675dbc80d817cb69cbdcf0395bd02b37bff0c9bd2d4b450d7c63b1d8b6c2a11952f79b53c41a0ced67551c63a4ce60a8d16479d757f0f2d4ab4458f78319563be7602b7bb307f9ba105168e60689b98373e6a6e47c51ca43e227607fcf4214cfc97cf98336a7f107dc64058679fdf96d12683f18a7f8812cd244470b401cc45fa360a14f50705685f9b13de42f063d7560f9b67279e96382a20a087473584313a84e5030fa406980a75fd72628889b5f8fcea476fb4f8a1e1a6f0115f0713c61919255669c7192259b49feea5dff043cdbf3e2d447f29dc605d5f9bc6a2d9caf7e5b3d403cf443a79b07916aebbfd2ccb2d2b821a717111499701d784431cbcdf7e1ed2914a41ac858ccb0184171e42f1f15044a454a8871dad02ea10c3a4c5391f6b193ecdbc87c1095271753b3fc0fa527bffcd2d7b88fd09509cf04ff818a79fcab93860bbcc59a04058e389ffdef3e99fc6a115ec2e87d8d7f87988e5f5ca7f48a2abc1cb5b02d54fa629344dd10fa4c310335b3df2b1118d992ebaa4386a5618ded82fe8f1dcbcbc59f1e738dd669a63761b16d9499f2dcbc7c4c3c6a3a89d84ab3901283d8b608882adccd0a9513c116099fcd5ca2758bb1e9611cf768a30b351381663e3048e44556b57273e19d59d87eacc8811fbb6d19f21ce08718ff63417167e4a0fdbe19fd3739641e8a50787b07fac58a7605095c5c3f7830061e989d13d4c46d9e99a3e20fa2ec55c7815f877817421c81870a8ffd88c7cd8391f82a99e3fd7c30bc122316ea83a3181d9c6a6c45a28389b4e64c57a8df374dafa278a090c7c66b7be4582c22182db90c1f589111f34f90f83ff3b796bfeac9707da25c60cd3a3d936c7216d92d2dddd19e951187c0a0af5dc496f4c3abf7f3922d6fbcdeddfef1d44366695817491519545097ad41ad0387185a10db0c3244e70f8c031f3037dc2ccf30111003e018ffbaa281d8fbbb15e7bd647417a115a56f2b4c1403fc99c2d3b34cced615e6f13864a69eecb34a0cc142835df359678a0b4c5368c208370cfa9ece700376fe6f443de1c087ed4568ef829039cb63538751083cdcf5547d03d187b3f846f8b86d20476a53e650f0b9ba6b64aabd8b4d9b39f6f949a48734c1f9b26be070d6c2960e8e23880dedc9b844e1b4cdb14d994cb4bb2476d713bc5dbdf8698ad87701552ecaa331c051a5f5867c0dc1ff7237b76d1759e73b80f8bd38ace4c27b18ccc5cce8e20b43c17902ba4130fcdbf80709e81e9ccdcca7e4c622b8f7018418e07a33e06dffde7d5370c0d0c984e8b3495adea5bd1b99c7589e4c8535bc2466e4c25f83ce02c2c0156b8753cdcdc98280cfea48e37dbb3142737b1eef040b7979e70f2b21f5ef30947709f2bf9ca886e81e98fb99d73441fb2822704fddb7de55cadc863ffe2f191a3d6c0b53bc1e4ddadf4e5ae712a49ec86f749c9c60c29d71c98c373179c9dd1e53245de6262e3b79f69b96f89c6d85f9549ebee9327893f79c6440ae9493b02f559af86afe1a8a2e168dfd59d944afb6200de0b03cdcd3a4cc78086c694465e6924c65a737181008fdddc926e630dda700a49adffb7b9659c2b2d113bef2d25d46148cd6a420ee57dd43e23cb761084bafe845d6f4b3154bb5aa8b90f3adc190fdf43fac694cef43b4683e784d6e82773ad3d2dc419b4851b9b61351bd053eb50554095e56df0c9bdb6775ea77fb58ea2b01d1328af49f86b022d0a5e722548b3a09d32134ae9ec43f274830a429f630311e53ca5fa1369adbce0c4f835eddc555688d9a1322699b8bc9e8af6b87b6f2ae157aa0538e483909362b1b11bb6dafa6d312ff3dde3748b0989dc5fd95bc30534b3149723831d5b38a74fa3c77e6f53c8e01d14b5fef9e857a8c0c3eba49907e3bc1ff284e1f0370225322f4278b278ec00e73e4bffd24ede3dfdb37ff2c96069004df3e2d8cf48fbd137fb88a2c348e19cdc2c17a43086c78bf714c8ecc4bb3c037e2f17e4920e4529fc97df78dcc9db9e089f7a3f202cb72dfdd450b899f25884d4c444ecdfcc220d679b568ba03cb5403d4d4a509c9dd95213abc43ad0ada934b68145e6b42dcbc55895bab5a8398103d5f0b616744674280d4632a15b1d91c26495821feca1d2f7e4b577fec7017225c655e37a7d4b70de735972f7cc8a97d1eea0e98baa2ddb6cede9bfd4e5ef2aa13cc28db67531d0163d7f4d5dd57420ce2464854605fd42fd42d3f0b5cd2f6cfa499f3f05b412d43533de4a5aeb22edbba0b342c6b76455380482f7dbe7e3fc792d37ad3bbbe3a609dd51d4a44dee8800878eef044d610074349a512be1dbc3c72f027c2ad30a866eb60489365112e9bfd651dc0e0403648f51b7f4b2c1666e688680c14c73058b39b1cabcdfa6237bce88c1062dab16fe5c2f36fa4480e8819096c8c1c104d55153dc8bee7d5b859a6a157afd5dcd6e8a9028119e0069f657f61db879937076dc04a574ab41c2d8129658f5daea72e7ab7ad7eb73b7cd8fc65e8b87a3cc5d251f110b486718385199f66e896246140f93b42e03b78d027b08ae696968132001bbf6620efaf11af913c0f242c0605a95822dae3908921fb64b9aee81e7e7be602f9cb3aa304187922d7886e2c2c96f59c897bccfbdb155a4db87036d5b429ed16277147a0c42303c67dcc4bc26a1599c86bb284703a267196ada461bed3d89fd515875418a8baf52aa2cf7f1f86f89da3cf9668edc3e05c2e787c0bf78a2ca13a5bef32ad304e1c00c575e8fc7efe1417638a1c013e4c9986f42e6277b291322795d175a978d2788de586d119cb086286eded099a09ff508eb8113826953ac646e21257f524093f6d029684af161b7b9172b9400ea4e10195c0fd09c9c386d6352d8512cf54034b5919693546460a4412225f0628052450a61caacaaa018a6e28be73f51df1290e4a04a587d0540cc2d3ea92101e95e1f23bf3d36bf8dbffa99dc319caf3660efaa63b824bc041d69db11488320539db32470c0e18661224a3d47a1bc716a0d1910cc7b7ec715747761bc053ce5180832cd619f764c031cae29232b754d9154aa2e9af1e8fdbb764677a1437def7cd1bbe65e6bbac930f95e5e2e4cf613a2c9a033a49b4782f1f48cd9d3ef22f93d4e228e592999bb868837f1d58d70323a202cdf4566c56ab390cb86910810cfc3c9680125f24a13a796842cb4e23af6d2b71548f18d63145bd1eeb7484b0e2807b9838a617239c646fd0a37c74e04903bb6a064b21714fee383fb9f9c47b7cd8163a30f792fe3a001909287bcd4fdb24a41089804804491d86062a92ac91e4ef3ac89b04d11db92b02b393a1e9017fe9a71ecf6c2f3f4190018b3c936dbd9046c2cc41df29691ef65e9ded92e783c1646cc34a4cdf0a715f0aa1d4d148ae72d01a31e2520d42cf43491175f492e665babc51ced1fe5e238943f0fda521ef900b12e80cbcb0a228324f7c853e0943425b1ff027bc00d43fea765f076f430f7909ade45866cd6abb1f1cfe2b3df4e2bfcab840697c1ec13a1655476b4b392e00a4141c9a8b72d8dc0224482aec6955326da94c59d23b490891fba077c8cba19410d9863b111678b64428c6840ec37c1405656d67d8c55ce6a6de254f7c3d22560a3be74aeb56a606a3559e2c864f4a57c1fdb37c1a25ee6fe4fb09f367d2fa6b609a1d9d5c2473e1fde5e8942370bf7e433d8a34630ffdff8920fe16d980d9efb937708cedcee742713605009480cecc2b90fa2e83674d34f409590182d2c544d584ef100098ca1268dbabfec80f07aaba8a7e7c0b94227289139ea2d7c60b085dc52e9dfb0e9bb299f84e579c3ef39aad15644f075dbf3b7b13c254861ae376647b8adcbd4c899be4fc4617ba07dd2fa09466931ab68ec5e78d5be6691e744e0f23b8d497c4253fd2e6c1dbeedf360922e313b5c45df7815ef4d1ae930f820260c65cdab58c63e6dc9841cffec6de66bc0e752cbc9ccf04edd212c120188d72686235a6096571a5819d15a610a8b380ba5a54a19683f369c2b1a3dba5eea1ffd16368bef39aa20c78eb9ff10a51878e7660e7d61b891214a20bf8cf4454e5103174c3db66832d455f9b46e4dd3efdd31d997760a18533952a94ed94f547d27f10fa368ab2bb12024f6999452aa2c0217d3a1a3371b8d643b46c3e949a5da9e90b502b5a035b1850b57206ce1468abff980219e0d89f1f72348f91dab75e649d15ff94b2589171582027532cdde23afd9f8507920555ae08ffe459c825002881d1a52526cb91084ebcb6af0980eaf93a210446bf65e9f514becaea3a143af75c55eb2fe21f91f32db198b9d475108541c5f3c4bf4daa0953c7d22c9a595e908dc4056daed2d5a31197b4c1e9235e595175904085a5a34f2284148c4e899027107145ab17d76fd79ba1f4b495f2ff47ae7be45f211e21203ad137e0dd330cceba0c71b1030a2365db29c98ec6b5313cdd93cb3d22f5ffca3a3178fbc5ac7c6e5046595fbe124dd99a918c6898bba85a1c33716f3384d0a2b2a38750e9c2208cc817151a1ff0a833bdf42e5454496e699b724167a5bb29fc522072e6f5243b4785e8069338311140a6d17586141e36022891bb64222bf431bc8cb85932e2e7b6638512554a5ca04cbbff6119e5fef4845acc40f9fdd3ad433fd37b02fc19520af4e954bd130426037ac0f690541058ca461de68f10527a872857541e14bc27ee3200fcbe7d902620b519d2192f23822a9584401324ea8774387f676767f20db4591340740479f443dc4b30f06d3d4f89c22f005effffbf90ad6b89dc5bca94a40cdb080f09af085b3eafcae6949ddc4287fee38ccd18141b3af31bd439e7f4a4aa9ea4aa8e367524c3fa32ac2f7bc873ce89faa1d22bb8ecc83c4736329c52356db0c93f08917bbcf7472d6a446d69836c9a45114a58ebd5eab9909019861c3a6cc28ed174cedca423f7fb0c6aea5437735e25f7d75a7b9f153b25444321ef19b2a529f33cb9b3d31502a25f442a327d10896e4d82a16422159919860e9bc70473ea18992233531a4a43c32377b2e9e5e08e0d736a45a31290b79d6bdd2ec85d32994c2693c97cd8d985ba1c8fbc7eda7198c75ce633a7794dfee755ea3c3c3c3c3c539e30ba31335fcccaeeec44fb5a68dbe99b4e2693c964321f78dc3d74cfe97641de72975769f3f0f0f0f0c0ccec3879ba5d100c0683c160b096eb058b4d990f2deb59d3bad6b6bec9af542693c964b2e983dceda8b9cdd98d76abdd6cb7db0d779bb9dccc4d994c2693d9dd9d41a7d6bbc2d74ec35c2693c96432190a3c423e286bb66030180c0683d97b4fa7ff30dc99b4599bb679cb52ce9c7c6f180c0683c1e6cce572b9e9e3c7efec42dde74eb85bdf60b086350c0683d9d6759d5e0f0b633b53366f0fb3f6de130a067318909197836bfbc1775c9652f6cd4577593b428df7f4a1a3c69d3cf62df7dbd7bda7d37f18eeb8bbdc654328bb4beaa8e12e19923afea4b9002a2d9bafeeeeeeeeeeee5e5bb0f6ded3e979320f0fcf100f83f438597bb233b4451b020e598585c5e8042e0513bafac571c4a590b6cef6f3bcda252e25e151879436e1445030f3723876b2d79737e42079705d76a723fbe7d5d5786ba89e47dfc839b4caa9b83cb691071505150475729b3bf3f2ee5c65cb2ecda7eda8ee1e0e45fe57bfcffbbc2a5d4a4f7e5286f34789e54f3b52357f8854cd0f5b19ca7086f3471f4619f58310b9a9cb014a29a5ff90fee267174e4861ebc13d65f923c5cc24e5eec8f2478a264990e28812d02d05a501049212a238a354e5ce64f9130517a659144f6c7069b2fc894209d7975a6ca7c5adc992c7fb3133f2f8978c180daeb83259fec4a058716f963fb125695c9be54f2c8989764959fe3ca9a388274488f2248e376e28cb9f27b927382874de10acbd77c7144305323b1a5b92181eec66b02801d303988c0f94dc560c932398b2d460987c51a284c3dd26482421fd406752d2c468e6e4035dc942590108242335d1e2299d3969e942c7e8da4089501b31b00c26ec4b8485898f86dc2dd4c60a3c1a7e50b3465041a5c107df4f932e301a80764968504306858b862cae2a9658b2061d5a90dc2c96b8724bab240d6c706995cca3bd272872ae92d9da6fe6d4e3e709387eb8f15d5a698742a1329eb899ac5c2059fe3c9105e9cbfd2c7f9e6862c9fd91e58f13397c704fc8f2c7490f4c3f3f4ecc4072bf2c7f9c84616ae2d664f9e3444a49774d59fe387192e4d22c7f9cc0b293974d18e4c7895a4ca659fe38f1c51ce009bbd018778479c1bc1ce695e9ad8275569992e983bb5ed1d1e27c7203ad3071922928d302da26932e28d82553f096e9d7d9a0ac57230825d359af469817cc0b0a38a55728e411dc82009ec15aa6606e62f0dbe2cedcefb6112c23d3a02b7f94d47253981885f59881a5c79da979b251739b73ce894bd9b52449873dad74d8decdb3b95cb619cfb68414a9129a8b74d25c2ed9cc45fb62cef1949bc74459d605c21d4f28d434c552ad67ed673f8f55b1942a4bddad00a5cde8a395dbfbc76a94e5934a54b6e7a4c33665b047d48fb83e0841cf1766ee0b45ee9fd50383462d704484143d588fd609297edfde14b93f54a5a375176669b7965be7c17a7047cfe54a8282d7242215050fe64d9687e2415fb2bc3fc2b247dcef9fd60e726d30a83e480718c4c6f53c82418e6f271f2d2a406933ca336ad9ce39576865b0ca6a0573754a87f67b643d336fd403f3bc91472222513d4e3ab43f1a490936e14a3945645c2afaf9463ff6e4b2edc902fed873255b181d34a498d2b9cabe55e3fe20a58323db0f05ddd18f7ed4b1a15c5265df5e500ab9b9d823eba1b9187afb5dc5951d4e942cbcb64796edf7c87aa6d82a451d9b54d9aff5c12d72a593f5c19d0e0ea4d803eb81ed20dba72422b5c6499e5fc3a40646c324cfa781f590b9e6d72029f680792ff7602e7ea00edc815d606e352745d3cfafbaba9b320e7a1e2a09cad5a145bd3ab4ef514eaeb24f7272512e5412500c19da1f55d092e7ab9045aec0daab8003cdc8f6c72059f2fc2038e903fe24a96079fab0e5e9a396e7fba04951c7e6e3e60397424cfa90f2fc14643285590a5b48d5cbc5d1cff7117b1fb2aff9c933a5932afba23cbfa6267d64e6f8e5d410e4989223db0f1571c71ed80aadac6373b187c6eaa1b9d803e62afbd6f6d0462232ee47c6f59a444407d353d20ecdf2fe842f7a6059547aafc72cdb87c14264aeb27f85ccb2fd1eb03bf68065fb15ebd8b2092524d6c59de1e522cc5faf25e4968384dc7677eca165530561f06409b9d5205cb69e905cb65f71ae428d3d78ae5c0950bff43fb8e38e5bb630ae6c5bf44d22227a1119d766593245418285e82789c87c1d2badb2df361fe9d83ab4efae63a543fb924424a543a1891e5baf685ff4aaf4f67becf754e995e8e7db4fe94a5884c15aaf7accc400cd20d972dbec220a4e6fba9068ea6a29a59c520a01ab782c8da8bb93403c9fd44373f79f34e9237afb601772d56316a265fb52ae7acc4086760c7561c9b8a289c1ef31eb55bd22c37efb3d66d06ffd4aee1f512edd6d24c2e0c74044b63fa25cd9fe8ca1956d4f2ddb1f53ba6c7b68ae4281071257f43a3617476f472d428d3ab76c7f0cb3d5b1b9cae2e08e3ab66c7b68d99e51eb0f48108cfcfe91479994411f7db89465afa8d3af14a4c3faa60cfaa87577943acf1b70b1fdeb07d12bff418aa154d5af790c37d0ab2fd79c2324d737d2a355d5a686d6863db8467af58957be75b1fffbeb46b0beff9ec7c510eb7bafca104c1138b678a80d745847a983d9512b8e00703e0f6aa7c3daa43f310fdee9b0513caa1dc9717950435cbcd4e9f4665357d35a6fe890ea402938bbe34ea674878787a743fa3b1dd239e5536a45dea6fd6967e87752c2ad21db29bb6fa836a9337f480d1dd20f157143ba11a5b77bd44398a767ea119ddedd2d8b78018233b2c7cca97bc85aadb5d65a6badf50b389496242e973c1f05023c2367133ab818d2a324ee2b8fdedf40e99567d24c07460e6c400319c0c0052c50010a4c40021180c0031cd000061859800212c003027638800e0628808c0070b8a1880d446aa0819587cc20430c30bce0d3c323ae5c6841480e0160616705157482e4e0a8c2d40d006c5240e1077207209bd16ab61b2ea7db050569b9f8819f1fc4e5e20f3fff06d6ab2fcfbf89499ffaf3a50f262ff8602257981ff3e00e4c80a7c1a53fe11d5f83691e057cfa1370cdf7c0283c0f7cc2ffc03dde07e6f103c03efee21f5f71297f0023b93e3c78f10f163847178f582416e8a38b4bac1f30921a2bedc2fc1fde075c63eb70d654e970be8905c40990180a084c087704024362840f20780f3c09f7f0239ef90e60d26f008fcf01dc81cf00dec06b0073e02f8033f018c01af80ae00bbc0530067e02b8024f016c818f009ec04b0053e01f8023f010c012f806e007bc033004de086ec033003be015808dfc0230039e07ac804f005ec0ef8079781326c0230027e075c03bfc013002be00588737003ec00b0017e065b001fe062c80c701cbbc0df8862f8271f81ab00d4f0417f90fe0d2b3700d4f0326f24330eb33a6e165c0437e069c1f062cc3c78067781f0cc3bf8063781eecf33df8855f619e1771cfb78057ef02163f07dcc20bc12e3c0b38870f0016f22b60167e0707e075f00aaf02def91cacf341b00aafc2398f83837c0aab3ec4380f009cfa1b1c7e0a18006f836f1ee3141e856d1e08c6ff18f503c040fee2ff8a47b9e3b82310580602031273f1fe7c20b25e799eaff300aee1d22eccef01d76ce99a1a102030204e6e6e3af0f36f662e8e3fff86e622077efe4dcdc50dfcfc1b9b8b1af8f937371733f0f36f702e62e0e7dfe45cbcc0cfbfd1b968819f7fb373b1023fff26c8450afcfc54cbc509fcfc94cb4509fcfcd4cbc508fcfc14cc4508fcfc54ccc507fcfc94cc4507fcfcd4ccc506fcfc14cd4506fcfc54cd45233f3f65737101af809f9fc2b998809f9fcab9c8c3cf4fe95c44c0cf4fed5cdce1e7a7825c3cc0cf0f5b2eeaf0f343978b06f8f9e1cbc502fcfc10e6a2cccf0f63a12c9c85612db485b71017e6425db80b552d95cbc5185430554c2553cd54aa9acaa6baa970aa9c8b39a090787f8465e5f0f3553ad5ce4516705a382e9c170e0c278623c399e1d0706a38369c1b0e0e2787a3c3d9e104e5b4725c39af1c584e2c479633cba1e5d4726c39b71c9c8b04f8f93939174b3fabe96bbd5f77e0519ff4475860f5723f1ee89350428049d8d558f4411286d9c104c5b4625c31af18584c2c4616338ba1c5d4626c31b7185c4c2e4617b38b099269c9b8645e323099988c4c66264393a9c9d8646e3238999c8c4e66e72a99a099d68c6be635039b89cdc8666633b499da8c6de636839bc9cde866763341342d1a17cd8b064613a391d1cc68685287a64663a3b9d1e06872343a9a1d4dd08ed60ed78ed70ed88ed80ed98ed90eda8eda0edb8edb0edc8edc0edd8edd8ea09a568dabe65503ab89d5c86a663539d8975248e869ba902e7838ca007e06df7400df8cf88603f86603f84603f82603f80603f8e602f82667017ca3ab00bed95100df044d00a75a12c029570470ea05019c7a004ec51c8053b206e0d48c013845338253b505e0944d0138754b004ef180533904e0946e079cda1d00a78274c061cb0038741500872f993016cac25918d6425b780b71612ed4853b554be552bd5430554c2553cd54aa9acaa6baa9702a9d6aa70ac269e1b8705e38309c188e0c678643c3a9e1d8706e38389c1c8e0e67871394d3ca71e5bc726039b11c59ce2c879653cbb1e5dc5c359f0038a78491c8bc1143b458a5cf01bb2c6b002855ce55f349a81bd8934bfaf95eccc5999f343f77fc7c8fe662cdcff76aeed95c3ce1270a3fdfc3b9c8e3e77b39177bfc7c4fe7a28f9fefed5cfcf1f3bd201781fcfcafe5e2fffccfe522fe89faf91fccc5147efe1773d1e6e77f321701f0f3e6e77ff3bf9a8be1cfff6c2eaa5cc4f9f91fcec59c9fffe55c0cf2f33f9d8b3a3fffdbb9a8c2cfff825c5ce1e7db968b3b3fdfba5c64e167007ebe85b928e4e7db988b2dfc7c2b73d1859f6f672eae7ebea5b928fe7c5b7391e7e75b9b8b3d3fdfde5cf4f9f916e7e20b3f61f8f956e7620c3fdfee5c94e1e7db201767f8f9a1968b437ee69f1f7ab9c8faf921988b34fcfc50ccc51a7e7e48e622919f1f9ab968c3cf0fd15c2cf237fcfc90cd451c7e7ee8e6a2007e7e08e7a20a2700acc201ab6ec0aa225865035611c1aa1ab08a06ac626155c6aa215835030e65c0610c388401872fe0d007873d38e4c1a188c3150e5dc0610b381482c300e0f0e5aaf92ce07007872be05490abe6ab80533b57cdd7c1295d2a978353381c9cbaa970ca16e2542d8553b41b9c9a0100a7643638154b01a760289c7a619c723d4eb580e09ba01ff866e703dfe87ae09b1c0f7c834301dfdc4ec037367c5373d5fc1a7c4373d5fc1df866762373d5fc197c13c32739eef7e30d8c86265998303aafe5aaf9177b2e3cd2d880e60637518c415e3014cb1d5138d4965e8d7e3e4ae722f8a82d12a5e57a1e51b8d183d686ee69f4de461b41b8a49c9dabdab8b2bb0475787595744d25d2cc5d3a9c15a583e142da41f414c58307faa427a184a07f8405f6106efdd18385e1a9cbf59cb3738a25eb06e62aca830ba373d1f4d3ea5c34a14ad692ee95aefac19579bc81d129bd54bf256279b01824eefcd46da66c79a66a79a66879a666614a96e7dfc0e6dfc46e64a73cdec06a79ded86e6eb89bdc8d2ecfdc986aa55c797eea95670a36a66237b0b7a8f1f4a13c144bb2bc2ceecf468df70626c64810987da44105d040c992b4081c56bef97dfdf5aca1e55518b53483565cff3a75d7bfca4b51edee3e697737955dd762d2f7f75a8bebb2f6942ee700a9df0eed9c338daf7e9ff779f5091bde676db5f5ab8166cf39b4ea6b97cdd3abb17167187a350e91e77b4fe994520ae43111850a681369ef69ce50690c22ff10449eb69546ac851d77777777b79f37de1ce604a388d4f117d9e08e1237810bf46a946514913eb2768c52598f5a612c51df9bddb37fceef7b4ed427f3aad44285ebac8b23a9ea9752d53da4aaab54f5d7531e81c873ce1eb594e07d9d9f67708e7a8e47269dd9a67ab659f9a3cfc2d0ab8ab259449979b9ae760bb24a9f518bf280d6507368156aa4304c7ac42c484877765a98400f304c99159e1b2effa7d3bd246f34fdf87e0ce5399264982eef6d8df4f9648c9875b0beb7fe6dca5457c79042fa43b8fe4817fa6370e58114d26fc2f58a74a11fec9ad451f5d71ab85c9495964afd9f4ef7ba6aadf53f2c5932d7e115493fde8c3c59de62186964f8660cae677db6468aa6970f44fa945ebe0ad267f4f257902f802f1f46ca107af96047213d88a3981e065735a4aaffe25a86659148df26d3f75b6c2baedaaa19f2053006d1d72c5286efdbd612ae385c6baeea1f992157e8ffa41422428d958ddc5fb34817fa43d875bf97a38a05c7b50cf9a3a6c6fcae5e680a482bb5d67ed466618300cd81e40b142db0020913962d9525b21d830a0bb2458d9f57691431a2d0ac98ad56ab1563cd25cc10421b79ca960300923d5785962c7faab0920390e54f15b07c7231480bd9a3c8b2fc8902cb41b2fca9824bb6fe3d76007e8f0e3d7c0f7bd5d9bfef2a48af648f402db7c749ea5cb96318eeb8e8b19c277b08d3e48ed1643647152315bc7b931638e13e671336cfea592dc62f269b48bed9160fa7a557f6bf776b9f087d5dee2873222ab8f4ca82535891674322cf8648366443d2a3e38e9fec937d242c94c0d2abd1d379b75e7dbdf24c5442f66fab61479fce3d34a259443217698f445946232a6594eb9e6e14c2c19ca823ae4fff66b5c71bb526cc39e79c13f7d1be2f7a45df7be26616e9e3bddb5dae2387c0435554cde18ac268bf5a6b015dd2506e4f082ea77d8f55e9cc77c20996b4837495574f27dfc1864221d20e9f676d91ce53a512917698b94945fcfb3f92251599b98b8448fd6e3f51ff90fec8f0e2f01ef470088a1429d2d3e9b42e62c103fd7e9410588c51fa15308b40e58c567fa76008ff7e8a1ac27553d4c17254ab51d287f059817e149aa02eba33db9035dc50438de7d4a3571e24efd89115b96133bf356aedaeddddeddded8d47d7f5405b07b509724e77f7a6fedf97db8f729fb9992505cd90521441cff3bccaa29e575151a87c57b51b598e7125c985e2d62cc7b8f2921d8654d024dc45291d35ce166900a64cdf310968a6a897d25d6299250b5744717aefbdf3b02f91b82cab7ee8297659d6103c748b553f848fe0615112d0fc3d0f1c662c7f28478d43f0b0281de8cbccc365d51fd2d6a27e30e50f15a5fe10f756ae185f17dcf933bb8a7e61ead93d3b07e009bb3b5f9a1c25ab80b78a3b5fc23c516bad158a4ac52ccb1f2aaa6416b2fc813245ce916ab2481151067352c7bfc6ee38a28da08b965d944197d4f13f81c91d6bcd55abb968933e9ecd6314fab2da50fdf33decd8492f35407c6ab7a152d89400a5cde8d3addf54e081d4dacf7e74092a128944a21015894234446bfde93f43d45a2ab47608457114efbfefede751a942e9676bb2c6339b8573f1fbe9bb3cdf6be867e88fb02cfba86f473f9d7f1606d265ed0bec922f4887bd6c59206190fb9bbe245522454b73c2cebae62fc8451a85ca207790c9e4bdd377d1d36c5ddeaedc31d9fb40a6378b3c5b9788041e09076a5d9ef7fe601db1408aa16f18849614e543de57aaa809a25051beb7f643f6f4a1ae93a2d8495f61dcf9b656e9a706101010101010101010101010101010101010101010101010101010101010101010101090f402b4b85ad09ce93a86f397c5d582e64cd7319cc5d582e64cd7318bab05cd99cee2eefc4a83e6cce26a411657b338f7daa99453b6a88b3b7f1ce5f9f473cfd32bc3658bb6b83b2e22b3e7ea9c73cef9d5b12da35639674f5295b2c4ccdd43b9d7c49b0ca7ee947a95fa74ce399bcc68129fdfdce66af77677cfe61f0e7c51979e29961c029a3de75b5ce73a77d444e1a3d1c2b8a5d9b9dbe5c8ce7147cff99616bf2f3e5a15977e34d92b09bea64d29886decf19afd87e112d42379dfe8036f23dc4cae87ce66170406520eeeb4212ca2db6d742514f2f95e026ba7d7c688da55a886a2ece121231e1e30943d0c929cb443b32a2a7455ff2c824884127d4814923693be5cc25ea6d8e699dcf9e3d74c29a57f975c9a298e52df4a1ac447c2c1668aa237c0b8e0fa8d89a1d4db724769936188e6d07c49ed770b624844a8cccc68e615dcf9437ae605773e4823a50efdd0e8f33046bdf24e61dda255fdfdf2d3c9f642cd598f393b9dfaa52dd5daa464328939dc30d361bfbc235b4eea80445c98965a5c10464a1d225c5b4cb74aecd768e6d5a857ee18dcf963bf644cd7243285ace9f3aa899abc27067af7a308a037618b0cb2c194ddab054d5d3665b97bf9198186280d516a237b62e6d4e304e505c5883cde8f6389d91233e9335ffebf12e035e5dd75dd2c42305ac19d2fb790d00832da418e8f4868e0ec407503ec85467377adfd3c1115151ccd2d28eccea772e7d39cd45bdc39bbf3b59879d2e0ced7e2ceb757aecc13888bf1c3d6608713773e8d048fbc3e372491d9eeeea0112211cd4a1932c2a47934bbf3bb24c6740965764fb97742911e4c115d1579a33adc30111c848b07902aff2c2d9c8374903afed2618ae8b6d8d91940b772b5615c1fd5ae7f1797b66859fea901965eda8c62ec8d7be3c48388f9989fb346f150b5561cf332085407be97192c61b984266fb4401f7da871e6cf0380a7823bdec07272d207e6a718329c5f829772958257e5a44fe84df03730174935b5be5223864ea73e0c0ed530db3f41bd4def4b8047cf29408d396ddcc0546d9c20967ba8b95c670ec2dfb8d53fc649abe65b0fe649b9df5e2c4fcf831de1bee7a47be5550fe6a14e61af6c52800597023cce9c022c8debd06665f6c85f8eea09fa4152e9de4250ca258ca434c483a552a9844bac8b91a0b6dc1f02a37226968f960fd7288742fc7c29c407f12878140e85f36a1ee93dcff49ef779a437c5b4eededc4ce9952de2f97b1f2ee9e3fd54e259042fe5ca87081e88e7e1923e3040bea606cef7c1043f0281c1ec606a7c28f1f122c18f3e98a8725207082c97c23c83f8d0c55209be7402130683f0a8353d2c7784b1c1d85c8491d92b9b0f5345fa68b929f8114696022c730c1e3f21f088a223cf47011eef09b009b00f78a4e9a2a68b3c3f081c2a0126011edd8c11e0d102814737238b008f3e92c8f34380c72b6f60d2c7632275e6535470e4f963dd192128cbba843c671d99a246af956d8714e88e36d98654a473458d3d545ca6289b865371efa9867b09dd4be85edc8bcd1c33d419ea68e45e76dccb8e7bbbbbfba44dbfb8bbf717f7767777f7eea65e66664e27f7efd55fc2fe12366aec9f030618dc0bcebde0be7ce92ffdc5bdb8979d1df712ba9710497f9948d498a1ce504fa7fe323333877b712fd4d5a5cb171e9e2a55be787b4ffbb9173b87cfe901f1bc4c9f5e735fdccb176f8ed148ee6c6ce6701e94a280a0d7c33c8ab8fba4737ee9ef35d3ab0944671cbe3aeaa8e34b7654940f35c474fdfc021e71a7bf24f5cfef80cf892b0388eaeed5bfd2ea40483ab22ed7616eea66aeb10f1dcea62332ee0e0b534d5ef78b1a6bfdecc9518757aeccfffd4e0bfee122ba2f4844bce9cd9e4386f3aba38e3a7277a114251f089be7f775cfeeeeee6e4a29a59436a5b205bbc3d3823d8532db5e755bef15fddbe3bb1f0a972b6e08010d2678e1c49589c20230a8dc501164703f2d5f5c181c989e5cd18f898b6ba36071434dc8a628c15bd385c3e13cdc8713e162b8184e14ab7db19a17abc59888bd8c18d1010e57c3c56ab1346239dd2ea8355d2f23467480c3d570b15a2c8d9848aa6a2501a5b84abdefc33a8c6e50f2e121b5569c87e2318212254a2a6e61a7c94eadb556146dc27bead55a8dbc94d45a714658a04409858112fae13cdc17ab79b15aec27760aeff78fd75689b9680417c3c570b1106ad68218b16008d79c3ddc5255ff13e51a9261b50fd2ef01ad6a2aae4df5bcf69495662a3ea08db8e084abe162b5581131ef5a12ce4811703b1ccec8035aecd887f3705face6c56a3119c428fd8165fedb8441ec67edc7c6a559fe4859c293cbe4867191b8a22c7fa41431830b2ee1881b2a8292fb692959b9303870c18d910112778e8b834bc31da73a00472cb8f33bc80d87926ad891f03433a79ccc29174383b93a9335a344f22f738bd78dc05910ef065e914904c57eded719dca65650f149e5e2baa9c8af4fe60caecd72e78f9766462606e69a4aa41d2577f72a3fe69cbda33aa1f13340108928b41b6171e7b72495ae4eca1c0d33e5721096b872061158f261d8b8738c7c44c604156b2801ca195c9a608db595cb53bef1c50e808c90460ca4708235bfba7a35ab649a3f10e60e7cdf3f56589e5f5fbdfa7eda97a2affffd10af3d84b956d787aaad0e47605c9adb62ca2257d48b172db9df64c584a557d24473b1b65aa6da686ab998c3146bd0aee8dc70c10b58fd2657afa618b9dff4723186d81b4db480ca132ee660f59b60bd9a61e47e53cc459d195071e5870948a0e580d56f92f56afee47ed3acdfb445afa817b9a26698414790f409c2a5457abb75c9fd259c8b3497cbe574b9bf14735192410329b6d04113ac90a3c5ea2fc97a559ab978840836722e183071c51522b0fa4bb45ecd1be4fe52cdc51f5e40b146197070d102c7ea2fd97a35c1c8fda55b7f094b89895c51269449c949ee2fc1a44f4eee2f256991c696643a6b955cb99f5473512e418a0b7448d088000614564f1be47e128e9473f1c89337bc8842441c51c85aac7e92ae5773d74fea42ea42ae3c8e38485fe47e124dfae0e47ed294167de7bb2d5ca42dda22c5684b96fb473917e510ac580389d7cd160513acfe91ae57b306b97fb47351ded4a06209601801664b83d53f0aead56cb928791054f3a20c19184fdc80d54f72f56a7691fb49af7ed2925ed1e4fe5116b9f22ebac8b37f84933e2a2b2d7aad4673d17d54f39117b9f22864383f0a3a723fa8a54597c9b6e4fe51cbc5500b5cb003b2620e3798c0ea1fb97a3561a3988b2658e3270d2dc40882d202564f1ae4fed1ac65ba7009b313e124f7834ce4ca891005b9e82e778132ac7ef0b6c44693f50a267d70ee0793804b7a25b97409923e3f721f69e97220fb424362055258fd2157af2617b915904497a02b9c34a94114567f08d6ab4925f787622e4a2db015d48008276eb4b102567f48d6abd0ac57a12d5a0459fd212b212c38e90324f78774bd92479280316b82134ada782388d51f0aead59c41ee17b55c3cc5f1b30336ae14a189285cbd9a5abc8ac8fda2252d8a58fd229188267d3ef78b6a2e1ec1a2084a2c418422b0409105ab5f64ebd5cce2884b8a969d132b3478e209ab5f84eb9528e7e21118cc600b1359240108510481d52fd2f56aca20f78b765292e47e5197d6e9723a5deeb749ec925ec1a4cf29b7fc29c30744ecb8c8c93105abdfca7a356390bb0449d0a0624a115fb0a185d56f69bd9a58e4de61cacf1a4ac8c031e10a56bfb5f56a4ec9fdf6d66fb1f4ed665b53be2d68d2a7c62677fd5f97a6d1be58bf6745e2a4cf0e9d17e4fa5efddf925ed1dc1d8bd52ebd92d28726f77b2d17354007161a152f8e304612d694925b6ad9428b561b4e56f00a02abe7151207b422d8584309599a90c26a2b72bf376b9c8bed6a572e49af5e2d5e98f499c9fd35e6a20c6a2245125328010d27b660f55759afeacc458986115c5ad0822f4660a18535a3e43e2181055bba88011b54ae607515b9bfde7ad55fb1f40a26f7dcb5284dfa80b99fd65c04a28ace0b32d6b0b2240bab2715b89c8b0220c28a2ba070d1e00a3158134aeea7bb7eda854e6971dab6c83d71381aa338e923cafdaef39d8b3b4138620a2753a03c0962f57b50afe614b99fbefae9925ec5e47eb7d2e28cb96a7e0c4bee779a8bb3367acb4511e8ce98f2841c4d5a6cb0faddd5ab2945eef7978b478818021522b87031060d9ab0561e73512a210c2f8890e40d396ee004ab672c77dbdcdf5ee46a263193c8b3bfb974975e49e9e3b9e50d0baebfb5f48a94fb9b895c31406671d2495a9c80abe6e7964c1f661772d5d393e70a6d88318435809240842bacf904a7dbf5cf2ebd9ad2a265354dfaccdc3017873040ae42329cdf6dea09858b12085196c87104952b47b082d54f481d113ce002466e087170c19a4e8e4ca1828b2e577ec0a58a0d58d71f046154010589131dd4aa38918d80841722b491851b4a58fda75ecd26475e008436befc8c81cbe207acfeb0573b5eee1f2971c7daa2b732ee48733496e78fd0b8239de5f9a4332e898c3bd2569e3fbaddd175704960dcd16f107ae386ba986fbdb81506776c1798eb17f9e6fa36619f7ad49752b210319af820f76417c309180020674a2b89ca1da7ed7a19779cad2baa63d6d64ca382b5d6fa3cf575186b6b35abab15e2c10d61b9a121dc297724e5ca5cb65122c50d1a2be8f08a305d01041a4546ca126811ae6822e4058d1434649a907e447068c9f2a99471efee3e3267b7dabdbbfb6773014a1b71b4d953019bcaa8040c988d82d22944230200000000002316000028140c880482a11c88f244cef614000d6eaa52684c1909444190c3300ac320820c018010420c3004008490b1a1150095461d1784c54f9113ec7a8875519490d647a725b5fe24854080e2c5bf32d989b1b55c8f5be6e7197e00c6a0dcdab23b78b8767d9db2d7452566d9baeb7f6e4af58e4b3f6e7affcc7b6e538b3289507ec83218329330584bedcb649141aaa3468deeb4d6eb2dd93b8a705767080ff9c8c09371bf6a68d4fad4153274eaac39e88acd798a620dc244a322d62d5a7e44a6476ab22275394abfbd559f13f9cc5c5d41e71baeb696dfe7473f02a4f102442d1617c54ed5ca357777d3b3fff8b71e0d5b103946df85a0e719c240f4a53f8eff3ec0ade0e334f4470e6fe03f5731732b1e37affa2a57848f5028752fe999aa2569918a68536bf1da77e5e47db9707c2dee6f9b02e896b2c82b8bb505102fef76b25a546fcfadb56c3d4d3202a12557ed9f1a95ac8febbcbfd10ddecaee82a579bc00ee2127b35b505cbbfb6d79e2d0ee78c5c3fd17d61cb8833456344bb9361d3c3850cd84abaa90fb932b7541f019f13b08103bb5322555b9e8636f8989c0ec5cf95d708a828640a3e374206f7f48b43a2ecf389ef262d028bea1fad92c2dd69172c70640a97acb42b403f86603701d81ae5c0ae395ccea77f51c8193c15ec9f3aa511000f4bb9644b7c26359da78169bff2f4955698f50f2ee983e0f64bbc6f308a0fe973e8b8da4f4f0901af254a7bb8fe38d6b90bbf387493f155adf2224c17f7b559be0ea3f7610dbff5ec66ab0b7d20e7f95569600750a9d7a88f0d3f98a329ecc5603b03677920d4638f36bc891225bbf8902406fabaefe69e8dbdff82bb67a01a236000242f02fd00520d55380a19bf808ecca1e3c4b0a99e66cac491f5b9b90cbbcf227a306d1152597ee9fe16326ec672f90a47318fe8802b02b5c8ea47602b9864c69eb762bde4391aed976a9b4bd8134ad5d48bc2a6160f0accac6665adbdf0eae1f559669adbf436a1752022f90b668a86292027e9ccf41f9d88a26de3451b220e7baafd5b36e116cfde3f53d6b57c5baeb185f2997c8d3905b2c0d830e9b302998a4d098b58b9e194b6b5e007a65448e2498fd19664203502fb8dfc1046558a80c90457a3929cd589afb0f80b7edc2f17f301d2957d6c8ba5f522a7b2c18d84408c30df9900b696380cc2458fd1774fb7bb28290bccd05da24bbb9c61ec8522591a3997a7e9b40c8c1aae8f4e9ed4f443559aacc4fc9087f4d744e8bb3450e4fb929f5ecc418553e75754d3c9ebced82611c5870607cc5c9ec0d04bd49e8534649cef38d2399987129a3de8659d76da8aaa93570c38c670054d5ef3fc3ab36c7fd720b6d67d6d42a3f2dd04c72b075f3d14ecba85db6431b4d71fc0b6e7119148c7ad088fdf08a19cf28b9408521ba764512cb3f3b1817480e38c981813cbd5853d3f215dc7995899ba479942532af8cb17e3fb808cc238edbfba1d4beef89c20237be5bf6fcdf1ef0249caa7014e1edf37f83f9b05ca840437c93e2eb728edfb4d4b2da6c19bb3285d696c1e7f92d9b39b541c74d74da763e720cc648b5600b62458781161ba442474dccb4440b0023f9087abadb3e980db86e2d4fcd04ffe9db7c4a01d2dc79f74d7f3f2ce26c8c12d134d0edad9a1241feea3663ca58e938e4455f3ada4b0d161d63036e4ea06c5c2f8c130ae6789e9b5223a64d47f7c62e127f4446c80df80af81dcb0a3b28214c52b165c137446a6ce39f626e837c90796560c2e531a1b64c9d27fccef6ba80235654682af6c77c229d43976835f4cf6fa22b922cd04061c92b1ad72c3ea6741b04e7b04e8fc862c932172a1664ccab6abc7913ae187b05abb1c6d3580476bd81045c4e57b0fd5dbb1c574433d9917681acc69eddeca77567dbabcdd64821d5cf4543178c520e9738304f6dd789df2ba42c19b97b52335dda23bee2dc0b65a38a7c45ca04deda49a881f117676aeb81f5c5d2575117b21ea0fa98d6765f8dab59b2862cd169f5eb67f3ab10ec224c13ee36c4da700a4b62b38a76088df15759240b69be747bfdffd8f1f4d1315bd8145791171d319b145453c23634001c67db91072c32657dea9845d34035504c6e05d4b072820aaeda6cd1f28644201d02b903d6672f7c449292d39c692c81d6f01cb0d67b8735a8f4bbad4b271dc5d32deb42934ec88520b3a863a6835471c899e106d0018bf7c483759bd4f3641dcaa0b6237567c1ca4ae6512acca97b6bfd7a37587ce922aac2f1d444caec917d9d4519a61d61e2c6b94db480c150a3fcb752aa6f269af84d6977e3f0ea301299b488d8302e91cbbbf3f739daa0735633cb9d0dc7e8c49d3d371338f2aa9f50d94247c6ff023239032d724155161d30b3a5bddb4c3bc1fb276006277bafb07274742f94bb5ec1fb5a9b25112a611862c0a7a0c9de94901a2ce704d28fa6827b9459ac1000a7e16b941c5ff1b764168d67b69b25489e6bdc9f4d95142c72851d04b5991006d0429d471c08f3941f73e18d61a528adfc25cf5037551b9f65c292228be2760f88190c8124f1220719768127e5c97742d3267b54bc027e2c70c1ccefca02d6ae36326fed84d86bafb1abda1ac962b6fdd8b83eea700255055c2ad117bdc38894ac84284587ae03303cd4730fb2813ba47ee26e0892689d8ec3c356266a57398603fc023b9115b90c4d1fd715aed67dffaa42b7ff4af3434e888bd7152d1f07f863effbd62fb9559985243c710f0dc3658fb83c81b69b754e19f92a4026c1256f63dc6a6714687b674ff7f7d77d6739ec0619996f7244ecad41de8a05448f65f0b6ad46c93080475c7868d11eb1357d3c10104e083bf51de25ab38a56dc769d29b8434a704d06db161699c5f34bcae894e5666a96d96156db4c6a166fadd0e0e034b9bacfe75956bea9cba533826cda777f7e6cf0fe5da1e25e2b08c2104a9de497a9557b5fd3b5d56b3ace9e4dd8641703da4388d2cf53852f9ec686b4afe790167a4c5784116268486ce3026d1872ca9af8f0a5b83a38db7e4bdf7133506e36765b7f5d01917780126ad9eb17e500d33d2c86252d8f551c3cb443582a5d855ab6f0414e41d8af2e5c87c195b87e144408e7dab1c40a537396cf550382744b8cd0bc1a9fda5c1f5d8b648d9b0334051823a1b79882565ce6923cb2a6bfaad804ae364f7d7124536c9d79c2dddaafbf0df0866326702dd3864e5863d249daebf6d885a723892b8e2216b3dda02b65e388f46896f4c6a9c57e3282492af664be07344342936740fc6c5f445508c78d4c11c6f8cbde619bbe3977c00c47ab3c4b89690eedb6fc50ce3d61c022a6933477af2c3a7db009e833a347efbe301c2f8ef3c0448bb260328917e4e82eb23278a649970d93a6ccf125e98fe4f03b31b4f2c1345733c2c215faa50a148fe08ec4219d16919b5f6089dea11c3c8a3d0403a572c5f971d34cf51edd8a9e4ce534fd0c0e50864cde2907d4f36f645a94ddee0fe0b8f58085f2b71db809ae614057bf99fa2a78c962c47126189a188ab939187b3bddd9132ec49038b1b4454ebf548bcb7ab1145c0e2a994038068d5433bcd706e026ece69e4f3cf0dfa9bff6efaf9514c51fecc978d7f40dea26e2178e13c1b707864c69d2a2c41e0f85faf50e03fd6987ff721db5440b0234d48f35cef0e1a87e6862100b7f48728c890d8b92d4bf38426f867394a7f2cc99f006cef5c34791fbbe3043e9f360b208a2ac51d9dbf99af3a36308138ec1268d354f744799c1fe251d785ed60b94b642e878138c95a10fd2c3e5baf071b50328be6c27540ed0d3e3a23d703e2d4da225530e4bc503ea5ece935a2e7e4150f264ec85225d40a3355cf0fef08e7e93511c0761eef5468d0da5047a5cd1a98fb4af68b4afaee6eca8b5dd65936542c852296fd3df4dd5961cfcc38f3bae25efb16d5a24fcaca29814a1d87a926923c187d80f7d2bfdf84169115f53d45787b0a8d558f165b4535d33ee5d06d905a087bc48426e8c70c0cdea6314c82454dea1ba35cde773e85ccae6db733b50cd580bfcd288a4c4a0e8e091294c8582c0d87ad24538b620f07f789fb581ec90a9fb6cc3c632264ec36d6b0320ae27cb93d3265ab0f65603d9b4984cabfc622acf838c87167186feb985c6a7e0ac60c2541694768c8514691afebb5f0a70f30923617a45237a8b3471bcac26564d3b452d776dad4a60ea50f7d4d20433c0c4fc024e6e308914609b15956120bbc5205d2e509de206be4f2209ccea27ab4238cea9ab61a53ee477c9e1f6e58401bb5062c442d02935f403dc34a144ef7bdb690fc59fbf4df50c0846c395f271f7789881d88e9226127977b0b18de00f792b0da71137026b01877c76121fe2c04f8dff9f4fe417c670b8163e06e48b88627becd31caead49fd5c36800e2735a053ef162607b839c1b87ca2d150ec8616a0fd03f509ee0c46ec5d3f74c89f88b185efa717aba9c4bddbbe19600e17c9a35067495195336ed37c9e925a6c3dd1140d51fc93cf2bd5aa509dccfb7d48a9494495e2b8062552864ada787ab21e78b986b21dc9118db1a66be4b18bf762ce19633b06b6243cf1e9926a7e08dd1db14b53e332ba99d53bec7a04829c4d382800c2c991599e3d84680971f5fc73812842ee4e91dafaaa7066c7d5db620a3674c68b34fa5638cea1b745e001d960d13515cc7704aa7a90a4c7b6e130fee53fd17c612f7e29322bf0edcde157765b902aa6678e40a1ff0e2ea198ff0310ca78cf58358a826d5f362350bb9fc7cedc8663b7e8b2a08d945260fccea9f61dc7d0e07931f89d0fbb2575d8759ed27c172d76863cb473af4fdfba96f64e35f6de19e97b6eddb6217290ef341b922aa706514ff5d3c97ceb087e093453cac31a97ed86dd5a8f230d1d4a9f4dbe2aa6d22973726433270b57537198ce9a8f28a09e2fb54bf25f4b8d0830a5835cd9541b74b84bf7386c5b6fca7ca27021f82ec4d07d7d514dad64f90b9e9a4a05587d8a94ce2d25e646527db7f0229216671463244eefae386adaae40f08f134226a58aef92cfc7538a47be58c067fd618066f45ddbca7397d783aa213d8a266f5b6f6b70ac07f3653335f6d9c968691311dd19b31d5acb985e415b63aadc101765dd4211b07de73729d426ee9cee4c5b28535ddfda154129532ddfeb1037873b389f86719f6f7ea614de9014211d405a4c9a808b050329747511c089cdd10ddb986706264611af73dd6cd7784d8e651aa8c88e701274b61b2a6ce24ae4ad2021ab7c51c48558cd7bb744250a8bbc40a3935d155585f43faa2032ab60966ea6c4a16cda4ac1dbaa8c681741d372c8f0aa000efc7b4d9a5f3447aaaf708876ff1b2c77c58fa41d820cd77f700e762470d0ecc32348710eb50c5a8249be1bd77659dc9bf3aac0ffd3ba3b00a37a2b0337557ce8704bf59c7aec81ef5892d7c41f7e641f8a2738c10e08af99a961303873b8476490ea9ab0ae15b9952eb58d3052b0ac353b3f6149a9392ae91c3a48c7ba00188f4b090f9f2d7d294a021c907424b129e7721cf38353805bf833f740eb4713e5f110b3687eb685c72de0fc892562a2222010a69b1f3753afbcace8a638d71c3137144857ab11b233ae22d63873f5535e4477be0b8ea21e5868f8f66e7604d96fede72708727fa3b3612929341e25230983f5c79dc27ba2fb4c182c6fe30bbea7b45a701115def3d2ac79c5886d6571c58d2f72d4ce38b1f12ba58aa36aae553b0ea82473a7dec7d8746322fb5efce1a00f575fd5c4a3900302204d406f45c2cc13ca1561a3cf268636ab3ee616f19f1167a2ed001bb5a784ecf8bf20a648996ca1a6b1ad60791f149b0eaa9e1c575b31a886a2a6c1fc5d63c7deaa534f7e8c23660d671efee1d51bb48b2cb61542cd5dc70f9bd901d0fed7e251531e2292f8e1698482c19fa0e25b8a41838ba3bb756800579898f906a24397f53040a89c020e5fd7b17dd943dc8a8691af7985d914718d3a9445013f8cabca372cd346a48fc9362f1b063e020c14188a7df9b4ace0bc3e3e4e9eb083fa68987289380df8e15dc337912029d97a41360ce71a88bcdcbd8706330df0b72b0d1cdcfdcc4627b2733cf96b9baee447e598c587dbf6378a52e455e7d9e1f0af4c0a99af5e88dd46205116f4d33e99f3ca902b5c84f01cb7a8dc4987d3f8a9cc9a2175726ed9f31f7474086582eb7bc3f18ac554b79f95b4b217ac17017cdacc91e4026ca5e2381c768f8225881ab4b290c8f3aea64da61145ee8ae2da9d3f19f484f39f2d3490adc3abed1a605ffda23cf0bd1a90e98bef75c9eb65b6f84c8a9a4596eca6ad079f4d336cd82f80b7fddecfd5665091112d9cc84c3ea5787c112aefd56ffeec27e972bd115d02df7a3e46111b26b0b635f0c5c17347f8f45ff8f46ca90af93f9da8aa9c155f6bbf15f53fb1975ea716028f617fdb728838fec4a3815825f24e5d7ebfabce8681664d4caa1f660380af237ddf3365dae223351eef8390b9b468816b2cb268fe5060dbccf1ac65f4a5aeaa8fb232a091e40219d7567bcb0ab1ddd547b42f768937e8426c47f12cff9d9a84d50766b607b553183f5c20b61b6c1f3db82d2014b573fcbd78c672ff4cbb66d4593e070294600a7e3c513a5b225481f7dad4f5a4c0a08975b4eb01b239d2b2730afd153564448a8237567768a037ac3c6addd42c8623059518cc56c41ce2b96dfe40db66d90ba28c1dd23d0de21ef775c6a4f34ff9f86fe39ab8969d68c92ba13619bb9e859d162034db29e95da4b11539a8de0a559a19bedda6a15d3d95641b0118afbf283dd8153d30c7ba50fd1db70dcc728f223afa5147c53ae5db25c81abb2cd68dc8bfd09da5ab8a70ecbc91dbb3b8463a5be2cd492ca8c98aaccd725ce17af3b0e752ad3228c5ebaddc179e5b8c37eb52696c5a4025a3359fc12a1e497019556b2dba6067395210660ce06782548e09f7ea27e55d5026a99cd3279533f47d243f552a77a3c67cbfbae025b389082a57ef93d4a99c65f15978e363fe03f6e1c7542dc8f47edd0c3566350a20bb64f74e96de1ca4b029a16374ee98d5af4e85736b7947cd5671c5203bd1e50119dcdad42d50e48c2917b652db485f0c3ea1215804a4be681d1d26f29589e9940a80181b5e2f28eaecf28f4214366b6fa84169810ccfe6dc9cb15956849d79cd7267d8cd0217849425d9ad4acead8b4a51a91ce7be87c798852ba1a4e51eea1deaba2273224c6231ad467e2b8bf8f7ccb937e448d5740ac1aa0ba2f59c3bf819d0d07fd1c090f0843c62c4141d904c311214f19dde3691c5c0ef3f684540cbdf20379917aaf4c2b8d5c3d67d5aa4ec1db96b078f69783dc7a1bd42928f24b128d1c319517e71719261f5173b60e982d11c5e8867c9bb7a1f2a59073a56aba5532406807946f48437d35cad7eb39687ccf46d88f3e9d63cde6fe3ff3ec7b9efff121ca9b04943bb51fdf4ba03a61e1304f2d8a720691c9f9b58d073dda5b53e5b8834b6da42e4f93f6b5cb69001cc14c1902235bf38d8fedd44a07c0fd9d6a9e7cd7b212d941f4f6d02a7f8ef8d821fe954b11049622175d5a7432c64156c0d65f453d50cf3dd176a95207ba3489c7574c3b7fb15587404310854b4ecff845d053392d184eeee32a17a1212bac361d0ca514532cb4ce8f40ea8bd631d3706f58530a2ae97bfc77bc1b47b456ddffdd94e6bbd907bb561c0c20a0301899f91873fbb5cc8e2c848c9b7be1d62a9515997a438893ebf7bda377018b353121791351c67fffb188182c52f179162718e72d2a46f2d079f0055434d174de1e8dbe09e4654ea689a5f9c17b097e39292d85b1e0e321c6c3ce9c936e33164181d5edb8520080985f9af5e4e8b5a2fdd3c57a6e11839dd46ae04ddd5303fef72a75832e96ce2cb94f9f59c578c6a122cc417f172fe42520c5a4f9d6f53fbedeaacfed60f0b060953bf1a9171f1043b155236d7528238349a90dfe21e8665b3d507823ddb4ed65a29d51156ad6ca1374311e3dc6659bac8797f4fcf870b818953298286b2b6aaac7e272db2ca95472b7270bb6e04a34680253bc47ba27c0b780ff1b9154b4d3de7a3e36e82a8b8138fdbd7b58d0fe16da20cdf0ef91a0e85ec7ed4b65451d850b8ae93e1075228ecb9c54aac1112fe8afb819f9b8da66e66604ae093536ce2bdcfca20c3e97bc6bef0e02959a507d117a43d737156c0b200f31401de20675a4234494586bb3a00717bd6f0b5cb7756e7a91a5ef381b261e48ccc8550abcb2e9242ff9f0abffcc3d4fb1e6da0c9ed59960b11bb5bdfc03b910c7921c1d9960f9ffdfbb25b15ade4ffc2a7a593825a29a7be80acc42ead343c785688f7878c4c7cb8f1b1d80352d8a936abc92537ae8219a4e6e38c6718b8d1db83dcb61e03ee8deae6d51f5f04ce539b15010aa599abefcff4d07784bb3f3491d466fd9b49c5f0e40c7c2180b593465ac02d3f7499a6fb75ff5dd91a5ba1852514eea1ad403ed69e6d4271ff046b16c067176ce9b40085fa9ecdead8fa9aaba89031ba96783704a78619f9a17ce0861d6183c41b6aba464823f4cf8d2d58aec1c3452b205402bc118a30ba0e8a106a2efafe1beb463ec813a2e84013a2eb440da083d107359d112a214a079a10ad28fa0ee913a275a1095089d11b818e318a208a107ad67b88ca754aeda546caa828c5f715a6d148ce0574599068f2c511f3bea864b84a02a984c8f70dde1c298cb9a9832aa984c8d01fbb6e930c34070fa71222be1f6c87e759b455954a880cfed851ca53bc4243981214966becaa8090e12373dab3325209915dca1e67a63c52cd5242e40c31834eb433ca620e251eda68c415c0ebc0d0047d08bdfaafe7a083ca1cfd0f9fb87fcb7e34fddaab0e31002831c3081d421740cdd0748839fc9e5241f83460c62c59c29f2e2b0bed61af54894dd212c974bcbf913e030b559b91cc512b28c2447873d04db3517a25d4d92936135bbe1d0f4d2ddb104a34b495d15d39c64f743030d4e8e682d59e89d32e2ba8fdf47d7d098da02c543cc280d204968545163a0fe78cd668471fe421ae03976d3048008b57341d9bc249ddf142c2009953c446bc50177bbb3ac330db211c347e8f09205a2264058fd36f5a3280b6d5ad1021ca937f576c168612e75100698a252c0cec5a30f60d01631adc9b39c2a57c24a63b0eaab0d00329be5bed3a45ae149fe8c35d01d5d2b7e559960c1d6030b2c3f0a82c82e72074fb704eaec13640ed36311c1f0b76d126a00dad733f64b9466a89dfa72de2da868241f3be676d283cc3758f794edd19ccdb565d092cf4930ded9f26c199481c0db9f39c6ac9b5aaae224448c6ac8ad89474d530a2ffc8d15752d1456fb25f32b08f47113ec1d5bb26e7ffc283c1e10c3d1ddf6f28249f8bd6c8e09926dc95aac5b99778e47744749335c1d411206516b30a852c22ab6d5d0374b4d5336b23bd3f936cef7a25608e804afccc01eeea802e195a8ad910e56ba17e6988f2b53348da8c10c16611c66c88474c95830460666cdba25f9c562887eaeac579ed2cf8ac2cd119a97eb4d7188ce931ba4f0597ef8848a7df42fd1e741e3ac9049c430d683bc66950b7c36919df997cb5882e910b7976d8ce15ef426a608acb4e7bfd196c37b2b7549eba8edf889aca8fc8a01730a23d8e82dad5a751b87d90992d493274829c167ed48623f34aff308150ba26949c6bdad7c1f544fbf71c7fb533110faa3f689b60e051f5dfe3700d2f9ca2e72238afb47019d3610b9692889cfec678fb946fbb4df68146ec504e12ffe156eab0fb576addc27d5ea10457d4ac04402dd37b3a2952a3daa8ace37a3a10f177c456b41fed7a9da908c09e724e36abf79dd6b687611da9d64d51517fd0e87d25b6ad9a390f9de1f1d0c0b55c4d7aa56e9845694433d0e2ce4b6977d8d75ada6516960c928bc3cc56097289d5fe99bb172b99b0d1b4de0dde64a1b681916045c2bc7ca84f952bc9f093a0e387d0400c58cc35b19da44e04fb974d4db4ef6f811f500f3c8f5d3d37f569a0e66cc7318c3763897aeb1e2581fe65bbbf38a687d0d50ccd0ca0c5f2a034d848a53ad17aba4e6291a92d593f02a2bcb2cb04d169d42c55128d50635fcaa1633da561e27ab10aa1154152594c29a8c62bc4e3a5dab2f4719099724e2b1163fd9e3b38165080eece699d1392f224f1a9bcfd38bda2796066ab163314a9ef809f1cd8244ea6d3d00da0ba0240e2f11a2d12edf7f942f32a8663060cf0d0d70ded7cb23146e9eac577c532352ab2f48166f3a550058b1dd2738905d2c721d931f66cce9d8e570122e429a0067015fe0abbb8f9c7ed7f56df830f22b2f80fb3b7c5420d36c63faef1e0f07728b32e1464a1002553e681c1c1b438057b260c757e5a99f44ffef40e0515d95fc60eac27411e8cd693110999504f663c038c14250c6479e63adbba2e08b543fbb89ff7147d5596d5983bb8da62f97e8053331456a319df67d71acd506f5bc7a0b9f74a7c0bc5dc42cc5175607340fdebdf14fcafdfff42e683df1a980e95e64ca9dcef419cb4957f2de7b0b0c1c8327aa0563aec09495221f6722ba23cd84ab57bc5938b121208733ba9453c8e2f620cb65c04ab421b39f7c2c3c07da9d556b2b2bc4d372ac044e786c082d9ad3913d27b195ba8a4b8e761056a9218efee2ddf2c9c7576fa4521e7b0ff330aa218edb516bb397370595255ece43fdd5444882ac20b086754c5df680284b763c1678d6d8364a99a452c89e8cb406b29bdbc1fdf48acd4ad902100baff283a3d79a2ded0adf2e3ade65c24e5da19b74a9909ce8a0775bf0851f79628b14055c1091a12e81e4ef662b48f51452201f42426b610f8340d95d2fb77de5e0324c6ad549b4dbe4e6d79b5b83fa12abfadf14e982f74c0d8290452719c4d39f9680b287bbd7364bc49a1250021eeed075a4a54b9650a659a7b2363b1e902ca06bc084fcf1bab1481adb8228167444fee6f5abfd882bdfa629b3ea964950e0c8b7d975cd876c77c7f8333b4063a0bca7de2f886dd5b6c20c63f62872eb0f0bc5c1d9bbdae8a07e8a50db2e2662e24493a1c77248ed02619124eeea74b21bd6122a8962c61cf912b140c335c3b352451a52221f5cdc9618bde07d9de7ffe069a7819f3bd15f4b9f4109bb3923a201e640a0e49bd25351dfc589b89c2e3beb05ab333f5831ee1f2d5f9a457ced22cf0d9011db583077f7425a22eb4b883a3890b6b1f8f72713e29d9aacf7211739d1ed4b7bae7e4da827aeb8eec70b86c86775104a73448735e86e0d89629c916c745fe305315a128f8c957fe3b29ef9455c25c72b14231aac9431893cd7b2d3ffea7feb57b74292da80f73162a7f0953e9c582bdc9fa6ff6baec58f222d7493f9cde1071614927b0a63c7aa44b8dfc37458e4a8761c297ac4c7b69b530f9d9854d1e8ead249dfa9934ced2240456076e541c7dca63690e10a5a1f9f361579180bccbce93d2517ca59fc310b65caad3bb03865e63435c97639fad94e113bc81fecdaf675ad5f23c7f406fd3ea123aafee694b608f6ac9c5e573052d66e8630e39318ccd2cb6954adaf92705e4580b874a7f0996f4241e139a8cd63f49ea5de725d5119063e096be8cff9de679f77906fbd9662c4abe07e71e8f15769a8691a1a335b0044fb551c1830c4aa5cf10a2187cbe8aae8092a1a2e5721216a6d9546a30e3e3fd54939ca297f4bad54cfe3cd19e4d18bd03904dd8785e27ebc3238f0ee0e16c65938c4480a4cceb12e09d86d5e7761536fe1408e4c6f0cc8776bea87e4a3764c26ddf6180ece14a6171c0e93637afa3ac2d8b8b9cb9aa2cf17b670318a1ee1c49b86a5052605f90394f9457b958110b6206178abb73e46b9f7a9ffbaf36c6b2bbe05022f1670cd1c9542f8597709f399b9a36a11e0651fb7c9309efb0a1b2dc3e1402196f4ed8767a0ece7559de22522670dfb67605ab103ac35375a54a8a3a5fc872e1ce8c87354f4c21da94705e1b552f641a741bd80ad03d71c523d424b6868a137f48187c1b8e2242a433f7c0361a556c7ce24e7fd387cfea0c2605eb24f42e96c0565a403effe1bccca4f140dad1cffe07ca93c9e588f603828466dcbd6489bad73f2660ebe48cf405fcd75637802f9befbd578fcad2948ae61f59747ee619185342ba543a5d3d4c6238e73f2406063724b2f54e6c47a117ff23e9acf0c0d9ee4a3e548af0ccab64285bef30575a0148a0974a5da996c9efa268ad6a6c28c266eaa4f7005da3cf0b0ac501068b89600cb147f0184fe89d14478293ef7fc956ba56fc6c9a4cc7744db037e045ffb56fd7a71897cdf34ddad61f97b420ec1cc682c410f33a075e508e420c552cb17988fc40ac1193dddf79cf0512dd4d0eed49d79cee5d0380ae4c652631b31b6ce8ff45b49b468d856dd3e0aae0136ebad091864d829fc42a6815d1c17b76f9aa07a877f5701ac7d23ced1309266f9533d79677b11bd4a07d2329108afb4969ca5ccbd575f219de5ccb0a4d5509e3b6d4bc6b80fdddbfd974d36bc4e2a6bc892d6835bc42cc7a97d1aa55d8a71f6a602318f852ca9ee7d690fb476b39c797a1ea3be60a21cd86d80e4dd4da4cc8626b58a78c1868fad2b7106f796e2d63051dd148319e8eee7c2372a8b9f78185d583fa110e0ed1b1727953b51a451908de53f93790d44eb08031681ae3a7cd3239cd9c20ed093ec7d8943adf3f21c02902bd69f681523e05f0631aa78f917c1eb490f1bd34e20cf1b3f197b4dfa4f14eeccf6cff65f323393ed2e9974efeecf227ad7f627f26f32b5b3ff9f54b4f8f74f026b7bcf6a6ac7f663d67fb235f3ee9e9cb2effec7aa7063efd480ec9ed4dd9dfcceea39dd790741a35d13150ed944f5288a12c9a8fcd14fccb9f95d5fe03a4ebad2b9aa8b8f0817d66879070bad369f8979e9442cc6311708e613431a31342e4d0d447952655110058d436849cdc8e02c3148c4fb9efc9772e69ccd2176ddc5938ea8088dbf5d940c59fa19bc2f144d484b1f29d9df34d7ffed39ebcee4c8de694d69b065af3edcee4b93193ea4b296de9d995ef6e4a5eefc98c42b17c1d995b434ae947efedf8a61b396fc6f47b51ee56f4d6894f7923920fc47a783a1a40c1c9d48a0441e0606a149433067a42c027f8cb0cfddab05711f2dce01e76d449c4fc5188afc0fb8cd09688755d90563ece1ca31c3a8c0920fc627c978dee59d88a846c0db8aa14aa5c2d982ef6013687ef74e21ac59d058a0b9be8a80aff6f0958d694389368f04f4d95fbf1d567733eab4a52d2a337de18dad6aaa800eb429999a996e6e6092264c9cc1b7b092adbeeca8fe9d35e9c0a944696df312ea8668f980e59d225d94addabfba398137d9170433ff619d2d1aa8ae0ca8599b5b11ee8a313e314a9dc31c7974be8939a7a501d493dabad9cbf71d88ed62b567b66cda28984afaed281b627fae05a2e6960d91dd5f77c679b58f26ef4d8f1717bfc701d3efefef52dcb49a7e0cb20b4acd80f83bf95ace9370a5976c3ba2319bb621a5cb1966ad808e6ae280d2a89177e1cefee841b025c784d8d97cd9ad0358766a314f5334017ec04698c0748fddf74e4cc0b7c00a13f0a02022743c07dffcb9cddbf2b1f93f417bb6a8ff9fb83aedb97e1c6b4f4bd816bc108865c5bfdcf95f915e024262981944db2002f897409842cc985853bb0684a3fb93419ab8ab00d828482c5c43c02d43efc0a4e3378093deb780f1deb79173aeb0b5dce73df7c6d508fd5c4f447584a4437728e8b1c67b312e4f147646f8150b0c4fa66ed45b85e564e2c890b19d1f84e35357a0a84a74d09321b437a7754045465e24456709f783a5ff9809302f5cb402c8fbdab23bb83fe8c0bc23a93090ad4f820dd9032dcdcf12c8b1af62803a5076f1f745873fa7b19ffea9a608811e8ced4f1d6be61c2de5a84cd22ed62b89292202bd6151d706ad0582963e10fea86b412316ac8a117ac7313e0ea000b2ea340e19a7faff5ca09a590d620b587949328db457a52896f488839d93e4ee13d5c8015b8a49079c895f4d93492bbbc95aa908fe0562cbcca22e84026ee9accd68c04f01235d97ff6f8881fa1883345a2c405369880254b7d6481a8ae864cea372987fbc68a078f6bafe7d347131af153508a56d2373079f1c926c7107abd5ea0f07db2afb5f1b28773d90ca153d1c123b53d012d1132c392dd1914677ae9257b97822e74448ad237f312a83159e6958657d2f5226fa5cac074b6d8120c736bf4d1dd9938b95ec6cadd1d5baeb704e3da821dc38e98ab15c1a578c64f701ff5dc2160e1e22259c1cc6f466291979a61222aec67cc65661a478c2c6c6be10e68f7932e7dd05a06395ee632808921ac42189265c22e3d1a6364816e7a571f9e8440766d28943894e9d9c078a27c416a4968cb8e882c673ced55125a34d950b4f8f41e0f5c0fa9fae295cdc7e3413867adfa3e5f3fab081abc01865537c75dbd9d4e1f1645a323f5b1696f70894dc1f0105c608728915c16327a279afb937eaaa441e8acc281228c20b591591cd3267b970eff719d4d844a828db1875d77650596f8af3df66e4a5ee01384170ed93d5247e372a503cadf82e29febb68142c558b344379cbfd0e3d5757788ddf0965d63d34a3fa9e17cd4f868117490d8a4f9bec1f20160f157b298526259360e023dac675f344f178f586b6c945da4681d84e221e5fd44b9c37e0e78ee61f129131dc2ca26829beb29bf5dee0586f32f81e2a30c6614b8cfe13490fd6a8b8f94e82a12a97e8634c045046bd06a27412d8912373ce0e2a30060a47e43ff5a97aab3a3a7d879f83266e0ab5943bd81a68f7aa6859ba87be1f9c99259de5d51f88e0b5c2c11bd9c4d095199a45cbe4d38b8bcb73a26642839997002ca798d6e3d02189aedc909617c41032896899870c6da146033946796763db9f6ac9ded401739f98fc441f918a3235b9465021f3f9a2e6a2943ef0a9b23543b9612d30118dc18fd8c9df3ef37d381e586af1d336c2e11a5856402b0c1c41293308df5448f58135adb0e792c0efba053042eb52f0342ba9a1ff1ca12e15e1e8d30904277f233e1baf94185b7868e4ed05096d8ad5b4f1d65aa18fcfdb03f266cfe203b929e1d378b3a1cd116f8aa3be4bff89e5a856dc638dec7c09f8b2d43a1fe9149190a31f20f65f000eed5c50712d01b7078e2bdcbb78816cc9f8392e0d9535e5ae0da29b72c6562de75ffce4b216c5f96430adf9968ffdab45701aa46e2f25c25532a8943a5a7630f56df342e00d1a68aac0f28a64e2e9246b279ccefe345b64c2e628a4af16ccf467ccbf9a30c33123870bfb726e19cb56cd5f03c9d40706eec540c282c0ab913041c183ea9ddebadcd4d04bffcdedca86ea03624af45e978e128008bccb9bd4004091a7c171cae5e47e5e62149b5f2d76b5e07d075bd244050c34bfe9ca2076848afe7f3882b0fd735f003539b1ac44a84febea0582ce019b25ec078c514f05b89846fedbc1b9a57371d0073a4b78eb870efcc5cf63860ccc43c1e49e839f83c164161cbeb0c7f33949b5cf78652c6f6f86d7f46b6c61100dc151ec728ee6f3d0495e32c5fe5de912c5e9350e5e5f10619e6a62c4bb1ab28905a0093ca38789c3c68f30519450f5f813d9d7a770490f5f4850276453e4080d06bec7a186bd8cf004470e83adcf46fcb2d4f56c251d738a276a2480def2d4f402248e89da4ec4fe029f60c39f0836d39eabfbacb15d79bdbcc61e795c614fcf3252bb659f4ea80175d0091e230536b7b31c034aa269cd7dadecae996069879d501358c8d40f5019d46ff37fd08f99206d7347688abb1a1364f574c356e94e2692d96aa9ffe11307ff40581517fa2d77bac1fd8eb589145c95e7d64b9bf31dd7ed481662037462d5733efaf2103870b83a53f83200b099afd2ff7035d48121ad539aa3e0834a036875ecacc6043f615edd4b549145288de2d60d01e2151b36a9966e181a3376404d277778cdf3a5577c21f043fbb9aa09cbc087fbf8d45319a3acfed9dda958a27f74ed85797e281a79816967335feda7b0067f2ccd6a6484eb184bf9373396d487f5ec4453da5f376a483f9c8c23f79f12b3e81af5faa465a07ee4807327780a0e9231a3f49db5bdc5963229edc5e0d6cd604307773c2f607941ea51c993b17515e347796abd12416e86cdfa47c06f0020a39021e0ae13079d9296778f3690ac972b798fc02a129d5e9d809838ff03f4bbe1b219652933b3a5faedbc37937201ca1dd44b81f9a3b28c9798884415234c9950fed7d1d893479caf0653bc04c9c8c46563ccf4fc18ff4d0215a80904f473fd6c96eb8619c1b1b96057646b6868e15b290c998edc5dc84e4629c3e46da33dfe38776d1c6430e0a9cba9b27903af83b291004cb9138a87d29401c40439dc0dee19d97efc9488726028884b515ca1e899bada646d8060a9a4a29ea4c418925e5d80a5e4dea91224c9136a8ef1445ecb88f0fbe3e547af2ff435b8065a77934dae1dba44a5c9e65edd98303887fb46b905dc9978639fbf382c711de7457488fbf91923ab0e7327a34e226d52136aa8ec566a93482900b3cd7455e486c8a98b8d88c6d9e46d1c111bbe643804bd34d50b1ef14e890c925510cd007002a86e0e803c4f3e652622c8210ffd3f3a08a8db75ace51d09309282abc9d3f917c1807b1b6e4cd5d6d90f1834ed6c66584259d25045bb2f17fa95fe4006586800e4cc03f2a206bcbc8df2ebe7226dbc4980aca62384ec2d54b97855de03276b728fb9b55be04df6d49144eda91e186ee37f08832dd8b60d47d359d40fca5d47d2fc85a617fb4fe320c6268c4ecacab5422fa4c97ecf8dbe644ee71c3f41ee78580afd203aba7a562cecef9d0db37e47b1b9d56af1a5b1b18a9b15a21950a6312de047f4249b0b4f7fefefdd2e72795ba7cee801f954031ef7eea3e0995e1c6c378151f5b45b4a16c47089a0c75821da2f4d3c8cbefae19c73e85d7e51b571693d8d5663daa2718af130f88d66347b4c28d8b19de60e7ed894bebff442d9fbddafbef85ba8223f62861b4d59e4569a5acde1e51a0d69d1b0a51ccdb5348deeaf971951ce9aa7c5dddbd3d18d3d0bd47bf418915beab706ac5cec4ecde985aaa02e5e89dab3ff83c082461cea3a336a4cdabb0b73956acf6dd923ede2dbef3d401c7a693d0c4d933472d06a526102d1e40f0a31f867aacae77bd9efb99ed536a545cdff554f96765d633f3a7a0c53c6f2eb0f279392bc0537705f887f36bb1992a8e59998d5081d1f07f9b5937225e7b532116873595378361a6da00c286e9537a4397eafec22fa7b6427d399a817cd947b081307dece7682dc7b387ab859e71d4da653285df5ece579832b5e92d434f25310ba272bffa36969fe3ae61e69ac2ef4b73a4f0e594f03aff133290c6635e7fc2567962c5e3b97fb118cf11954c3cedabdaf03c52484f307d1c70574666c31c1e7ea9b1b3c0cbab67d193d59083b2d888e62e952331105d0cba6b95846c0e0cb9dfcd62435914619234811e3fd0e54db6287e593566b41ba49ac3f3de006a9d1381031ec055f900f9535488113b65452e5251459602118c845881da6cd51e282cfa6b78a4401f53b2528f37f5453433860eec89266b12d71a93aeb5a45eead1ec161746a46db1eeacc4b388cde615b0db8cc17265be595873c9d168ef5a500c2e2d0f617ba6ec1ba29b858b47c9d0cca0101c521dcfa4beae978a4350154d1e8caddca2ed4d839c03672b358a908c602d9feba1541de58b8e7f2c527400c892827e27b52fc5ef9ceb2fad783ddbb27cdf7145b8edc93afd641eea86386f4a4209a0815fcb03dd82a43391f814f419c33315264032611c8e8ad209969c9d4cf4a2435a6aa5365a2069f00d069e6fc3adac1781b455367a0c3c02c91a90f96236ed23b4f986022b9b829366ca8caa41355de516bfceac6bcbc70019292ad764218b22031fc7d7c2802de4ec465079fd9de6309f36f93f1cefb746e5ccc4338ab76c82afc3a79aee91ca5bfd2ecc1b8356f5c1cabf1033c9a9ad45c23fbcecd0048c54dd656e1a83015fbb513a37025e61546490afc831f3d52f62ce2f1b92a85824a8d43f21cc4b7644f130a834d43deb8d87778804f204de4e8c42553a1ab8992093b3777dd88266d5a22455ee365a20f58495f2610b47a29e00ad52c73ffd21bf8956630805b96540fc9560c7be2394f645c02c70afb1bdc4461bcb9d4005f9d84f98899a155d9483d50b73fd1922cbbf83cbe5b1cf0325ce61064448b323feb00bfd9491c8b436bd5c559253b9d5e918619917372ba8a6f92c420b043f8443332cd9054d232d99d0947650774707b01288c53270e88ae27590ff46d54dade5dd42b59cdb14c76ec536cddc37ff87f38cfb8ef2ceb29f2ed62e9bcb7b20450d1d95c00b2b40ed40b0679aa5788980f47a40d72f4db4b69939112ce6fd92a54222f71421cd046fd01851b51ff628c651e353f9a30c9b59420010959e3a547b17c8b6300b49c8c34e2140d8b16aedc701eb1b1e49780dec8fc108c36a605e5780b51a2df5247f09ac93b640300b1d089366963c85f4238856682f2a0199a966d3dd69177b899af7171dcac58e965c5f071ddbd9578767156f30b87218399e87271d2d391e482135de296111062f605f3db38aa0fae00d6a0147f6a47ad2020d809046c2214caf8b887d91f07143d374ee9ac832df7a6caa340759d3b20b505faea5e4701461cd7f8786840e373823a132c059a195003af01894c0dcbd7e020d6186edf3da1c0e2b673b158466d1d770b2e90f9b9ac76d540002cee7b742732417820f524ccc25ba1bb23d4bf153f24ac702588e34e0191e506f6c421019212fa480da1ff6e397f3404b1372f522a4057fced0bbc9e36ce091477bcdd816577fda072a3e81b786b587751aca1d2fb0dc0058e27f5a7385d1f399a6617dbca536900764fe502dda6ca9ada40f56aec50c1a6c0cb9454a5bebb4cd855e33253eb519549c9b90f52ad59aabfa7051a4ce8b85d04009309aadb1cb507b4a5c8eb215f3e3883e11fbc8b406af7f0ec303ca3ad879075c2ec1d7630e8fcae7765d38be4176e8321a399de81283fece276a84556ac696c302f19e6bce7b4234e3efbab28d0377ed9af70cff65686466f66af780661956a6dbdbb242b2af32e860fe5a423f1022f399b20036102bf462a665e293ac32b5d09b2ad28e5ad0621c9641a582464de0ffce2a49dbcb919f366d744d904b9c8b3f66080805ced6a8ddf5e76b2c9fd078817eb964391d885e01ca882913f8c7687aaef1830141482d38187d678769220f9f8189fe7b6e1decac58917c02992fa7d922c091956ed4d0913bad35847efee394e55144598cdb71c133b8a08a64ab15dda293610b24d30be3c9d05a08b51819b60a63782165e4d42de80b5a11b89f6892dfdd19c149ff98149e0c924b893047f462e1ecc2ea32b53c7e68a5b496b44a2cc70eece57771ad4bae9bf5c563377e268835da6582d7c9b1aa5af7581b88394591394c9cea7dd4524b70c4ac55b53e6a6b4ec7c5ac4f351f6b613125f2fea477ed09b290ebceb17987eb054af1f194a6e3462ba72af118f8c4a170c3dccd30cee7d277f5600e4926e908a52aa5bca4b15d5f1f001762ec3152f379e56aa52d4f74296be95f0f96f33befb71cd2a13784861a77575daf58a9f8c1c99663410a5c7bbb43364a8563e49665b4e61cd5886cffd295c6ea48409d5ab84ef9dc3a69c71cdc2f5bdd8330ecfd9e0ac84236f7b6ff61d9365a1b0c52ca2ec4ce329a2fdeaaff6eb1afacad344154436f42698a35ba8511f36fbc9a808d2f569918c1b73a955124bd81122349f0b654207fa5fd46b2b213af66d866dbfb946c0c8381db881584d429924617f0c261c81ec769eeaacd73a257fbdeedaed4438a2452cf917a9a445e9c9454a7d9a3ac9d5781ff5b44fa3d7e605e3b0562d4cfb591a21f0118d5dae8b373514faeca7d34c4c6c07a49d8e5490dddbff3ac5c3223a8b33d06101010b2317fea8e7d5342085e064be6009c5c22caae253e532ea8335e5d1decb63f492d36a48f42572d38f8bc4cf63b551885612e05892696566873e4fe02a5e5f0c0ff54c8a9548ac771fcdaab787bd974bb6df42ed378eb6cc7bbb6a7b4b3d279fd847acee120f6f8cf3f23a6680de8ef8dd21bf37ccac07377cbd165e91176c839a900c10f44cf555a0495f48572942ea506be0eae3da7835307633019cb069c82521e5514dfea9a24556d5d2d31481089637bd388b3562624c7a8830fc9498de0908612fe4427b92c081b2de038f6854e92b92df2ae1f5e8462002c569e0878135b35561262a688654404a96ad5fee4739255c20e71705bc03459959eb7e06b477a883a45b7001ab3fa49f09084e7f421724896b7c0bf81b77eaa389f136fc1e9f729c24c19ea7cdd62ff83708e95bc099a81e8dfc66d1a3369798f620f7ba59a7de3952aab5a1fccb141d176e410b2f3fbb1f5629ca0e1ac4fdc00def13493c6a90e7673b39eaf1b976196284aecb72dc19d26da124a21b9fe0a3a4c7e04ec48070596180d42f23d2f557fca33896183b12ed3ff813a4080789dbdc7e6287c6dcab7211058c02cc4605a00d484da47c90a0bec4c9c6eb41c53226b5a22e94cc39668250b6ac337e18193d0b3c1158f02fa900a714d30a88d29950394a09f6199b72b6b4e8b71f26275a47a510cc6a8dfb47c62643595776df823db86e3a8cf00115e745967c6507cadc4c284cfed7d535d7fa3a23060c6c736fdfadac78a51eb90b599dc28d5bdff178c52d8d5a48fbf34861499f20691e409f0acd0c579918a6fedb646efe39fb3077c5e70bd4d6234c1c965d32789b191dbc568389edff84b48e0c932618e0b7d94180f41e20df47c4898ea98c6310ca8c7b9930c3588e43d1a0c419c3bcd48430cd8630890c4858a2e4ee0a8b06f74dc0f8a1666da0981811e6f861e020f52c8f8b078e0e7ed1e119a0ce4a3a148c82c4d83ef67aa2b57e79e6572cd129c2427522eff76f157549b7dc1d47a48783a9798098c2f7e15a42501092be102b2339c1397b7da18275498b6667583b78e3fb17dcaa2a37afe0102935373a9d255b0fcc5cb60d0cbc29b61cbbdda5a61eee64c07902f7e9bd99264e2e25aed9304442ecbc899379728b5d1c22655baffb051bd7c6721d786324d16db6862fea79820514831807afa8d67f97533f7ae69d48f6e8275ea10e7fe2e3fcf7b2cb6d7515a0c6194d4ae6391159b4ccdddb822219ab2d5d054db20ce71e2a6a1d6667a0a3e784af2b2cfb492143a6a53cc15f7629c87022d84bd8eb93948c9208418d7c8ffd650c6b86aa4980f2baaed4b4e49a489ef1d1cae1d8d0ea928a13e4ecc7a170851aaaf881cb52298929ebcdadf1575abdfd0c2d956a0b4c664216e164d86178fdff1950096903da672eea4a59ca362e28ad78d68ba8b08bd648c079cf5a33ea1f5e2bb00db1f4b29d0bbb3e3748a87d33891bfaa073bda5b0dd43d43ce36a153c1ebacebe279705d6d4a045366088832ff5db0bc49a1a286f0a4f8cae9ca4fcf6a37fa6e2a888be79f06502faf3bdf89aa5723fadbc7449c56b84c2998c170728f63ad075720da8cbb21d875259de95f28b75596cea5129c9b281f4b9a774c22d388d0c98f5c2c5b9f05a23a89d8828d6b429f8fbf64bfb6da2ead6770ccb0927e3d2e0e817ea8a7ea06863de58701d36ce12ea3511e807befe3fa72cfb7a8470d8597d9d90d54756b5dafe870344db9dcf8626cf0fab2b842ffce52313f7bceb721b0b8761c0fb374c454a9444daf12d64041fb3409a2a872649237d1c80e91d0a58078ce84b9586fb846f2a9f86a2eb6d811ad60f5ad87878a1d3f9d98de35c3cbb5ca5d423200bde0eb007fb7acd26df8612f24353e95566917c181ce0a401ccb3976697c0d4084551b6d9433246285e439bd16b3c3bb9abaf02bb1a14f48721998fee6244c7b065d1259e343095422bcca4e4159d98e57f0fc0d5e8c39fbd8e7bec1bccd77d29b7b361a6dcc1a7034f5d1930dcdd906ea2a46427fdb72ba9d8de23d935a9eb8681ee2b8cf3dd638db9cb97702109f135935c10765c76f92f502c8d92995c9fd8fa12e0e76bbbf8992e442cec55bd8f5420496de53dacf0624bab62e7c787c44bd3c3b4eed79bddeed131021b4f50123de9ae873901eb3c5fd3842898f8cd138658d0b1b5f96cd45dce5859e3c7329ece240fae25832e44becfc52ebb4bf8a02ec4d5d519d0b19c40405638212230d27dc3888bb84e7bd28d14aa440ef097091e46dca188a393c377f93fe4d43cba7ace6b5b5383137980e1476d0f1296f2df6d9f801e7db1057714a6a3c7fd16fe4ebd484146ec32cfbf4f0d88285dad2f336155520a260924b35296b4e73cefa789e410d81b7ad173e0f7426bbf51e9df2b501bdc9cf430a348b3f7c91605e7d0d42403da44663295fc3c13dc69632f8aeac1a5092bc355e322a7b9a8c8f292c685188169aa66ac3f66d5bf23111b051ccd0a3ff83084511503467477ba7dab8eda2742fcbf2f245b5abc183322758cefb64526e2dcb188de2c207ad30c3c6edfad06e1a1acaf905fc3b7c7e7e7d2339c63a21d95d9a8364991a73de448ee740d49f217931c4e06421f42aebacebcd32d6ac3f510d2aebb8127dfadacd0a18ae5160075758fc8087e549b7734f2275d09598da1d96b19aae30a859cc3768c7302095b2468132c968dfdffdf5d58a1d35daa5f01fd119ac8a503bb49ea2ef219280f3808e8a17a7027e3f3accc77871d8101776af4e8905960e8f859c3b5e226d5a578fb7c4ae5088317924767c5b717a2a0df3010c451e457be305544499cf875577043f1667d68c711aa67fc388752622493c5489b89d6e591db02f025c00316c4055206ca7310a82125028c404acd3e67f22d009b28d5fd419fc20392622115c3153e73b4cafb0f5d22aafe187c204c80b706762fd20a2079b53b65ec14c1943dd9e9d1073b1eba17f0015371058be758bffde93311e024cdcad09458b94159f5d2299b9e75801819b3658e70928874a29704aa0eba7ae670b2c3573af6560a6105000c8dfc685b5d2483ad039c365f4c0d8133b7007c74e49a108b944051589e73583d841e498e9d55737c6a5d81a3900b2d5ae230fca4380eaeb59ef08cd89fce03528a42005f8e8b96952af3203d763e9673d630284bda7259734442c918554a7afda84ff654eccca024add295f5b3096aa4d07522fb4e0efc1b3d0604c4602d2903bbd96b264b30cf7ff4390c4f194205941e6f0254914d6e19f7ff6c38360500003d3f18a0fb3f319c002e6ee4e4dfca157307483ea3e0ee3211634fb2da4edbdf7965b4a29534a012b069a05d7056e135941b833b56addb0955c9f5dc3b8eedc102c768655830637ec530ed6bfbb0d0d1bf26b474a8d93423a7bba1ceca75b8f48c4831ae4c06bb06554d8a6e190ad75312696d2cf035bebceaf033276c03de0d1a3944d3ef3d89cdd1be70966c241d22257385a742d1c36bc59562b92f556f6c1ec2090be53873d1d0e3677cb00e03841a525d7fa587d8e9d0cf48b23ace662405a6e168207842770d590ba43b6d6adaf8bd56a32ade0d6907d5fee2220c2dec1597b70e0eaaf9e5fecb99b417bfad9405ffb3c153c308ee6bf64d9059c342fe7306163da00cf9d323aba29a7b39810629a729850e2661593991fc449429c1400306ec84feee4d1b9a18e1bca4c403ef5e2eb781e4e025598a90e8a8ded0e96a5f042c9cd71590a2f6eae92c88266ba1cbd3acac1d57a1236c88ce8be8e7e91159b7d49e687a219406fbcf1861b34f2b34105f2e913c19e7e2f33501bb06fc9cbe5a6d7c5c034f2ab71d492e348e4626decc4c1a31c62fb928c93982fa6822fa09f87a7615f80c438a9336722f6c974cced56c149d86f1cf7f570b07ffb7838d835c8aa02e64dfa552d783829ca2ba78be8afac61084070e753ec37a6d81782e07ab3c37a38897ef2e5d7e347e46169cc8a989acadee6b6d5adce399b9b23ea19a047f4f0bdef328f0f7bdc1f7230c94f0cc6cffcb7e9a739fde424bfb1545b6e0427741c96c2054d5c75e3ca0549b8a13b7154e8486efc29392dc44270d9dc8d7a97a65388af7999c76d18076ea04bd9e745fc883512af08240b410c29d73ded6efecdb08ff85a1856ce4e4a29dfbdec5279b2444c350e167112e96f4d1cc5af63dc18c4abbd07f611bf2696dc871ec58f41c4807b147a0b6efcedf313fba08d6a22b8d439285e350ab5cad1544a9a515abbf99d5d1a869d3704af9f61c3daa1d8475411d13ab0fdbe72afd531fa6a268eba1feef54a87e78983f1757ae560abe572d2df887aa15e7a58750bab82bdb01e075d0d1bbf9f384dc27d0d1bf2aae6f543042fd84b5f48f633ee34bedfd87675cbc1b0ce700bdac7bf5cca2eafb4afa7c46e59356e7cf7bcf5021bfa09c3a8b4b41010979fdcb8f50b55b12d97a568c1eacef86c4d9e6aba8c028d01d062631eccdc7dc218d6c5499cf37521c5cfa7d46a040e86cd5c49638cb171628c31366993f4d9f445cce238628974b80607410b58088bc58a898989f16aa595524e289be434eeb4e7ca75f782fd06bd474cfcc0df346eabcf0c825efb689a07facdeb326ed33ad0a70369daa7036d3e3f5de6b17490bb2178a5e47c1ab16d432e75694de4a094a06f50661d94d2bf9a042b9fbb03d2dd0fef7e682e641ef79ad77d945cecfc33eff39ac7fd16fb725ca675356a33b10e719148f0212e373e2686cbe59d964b88d715ebc4b4b603db18f68d891eb4532cc5a60e94fe00a27504151a1c41630834f48b4bdfb2a08b4be75719073910b11c5dcec14191fbb99cb3a465e716a11964d1839e1cdcb0408b26011764b09193440ea9d7949a18ca43111b29365e806aa0b97868934d1436556c45389b2b6c78b0dd20088a831360380183249280e34242b76e30847cb99cb3a4ca12332a0fbcb86209124690a28a828d2e4e80aae132524220c1c31347ac80870b23b4604207339cdc600729493801106c64a1051157d06089152de5040ff0018b9e1d50d1a30437905033460f4e1a55b0b858a2091a4738c186c304382811c492225e66b4c103ce1648be18828c14122c6ef0c58e135c3869830b233990d1032d38138862783c468306165010418933aeb0d259e048813304ee079fc7db67014e936a5d44cfc3890c6ef831730a00cce9b95807e31326d2a09452ea42603836383c0c2963075d5c79030823a6682c9e5823e4e40719f344194fb48800a4073e3cf14209274ef810c5a586a15841174520e18410ac00c2083698010f6b14d1841e3ca14411fa0a31462c88428b292fe0618d359a858303415451440c96e4000952d0b8153da8c1b171f1a30aa1901bd21f22e44e66ecf962fc98b7c3c65d0c1ff6edf1d7f4b1a691c6e766815bad58759ab5e3e0ccc1da389968a0258780fd8057f02675a97e12cfc547d2fe60fbbb9b407ce38d6670a6e0f20a7db11fd89047ec5e9037f6537e215eda8f7d22885f43bd5262f17329636c29e51656bed5e1c28446fa113bb3154bf5b9efd4b9fd980358f2a39c5af0a8bf0bd70ceeb72ffdeeb70cd3344dd3de453b41a469dceca3458bb42a1058301586b56a8787094b098e8e942618b74ad5edcca2288aa55aadfd678eda116cc83d45b8828944313120c82ab144a2fe30ec5b2ef61bc6a4f8d8c59ee717ce3198d4fdc0542a11a77212e7a49f5cf717f10a7d45d8c7d59a75411a88df89611886613d422ed0dc528a62a997e0917f139d439350b561c58722ecb3267b8aa51ca353ab4e459c44552ad56c39c8c33e704058b0a41ad5a8d6e9288de448a7ba4943695533696666aed54aff19978925a6c1bef935ca49f47a3d92fa1ae560a7e248cade62db7943de11d2282796e308fb2cfbda646ad3106cd8d708cac123de29073b89b532333b5c132f7aa5518d6a1486b9a8b0a48a20453c64ad120d1b9fd96bad953a2159ca96cd5592713907073ab8cdee94d2226ccc1ca4638c52ca3929a518876118884aadb5fbd11f3b986599a669dbb6498ee36a746116b97f5d578dd8fe4f5b0b0239c825c6211d45cc6cad0d85422e2c2c2ca2161d3fd85d5cda657e5858585a5abca5fb21dd71b8e0c081a38505078e90c581030708c7a7c38103079d04685162ebee172865965f8c3d975e97108e230b860da39539e7a494d22ae3cb621856c1b0b5d6ca499308b3622b1836ac9aadd588e5e73a1740f6765dd7fde0aa032bbfff709815cb201710082472d101b2d6da5028146261a12c2da594f2636909d916d0a76b696969f9f4e78fb14e984255cef1704c381687c3e97052b8261c77add68a44fff454abb54d4fb15427edffcaf5609b5081dc145cce758e07d8d320b8b4bde2c8bfc2c01a713dc98b0cf5b3cfeae340b37dda17bff8c95eb63e4ab5d56a6dd3d3112f655ce6d1223eea6ee6962cdd6928ddb796533aa4e98c180b6bc3ce094a61b5fee8899e62899d52c1a96260a5d38f9ee2a80af76739b11471d2e4549c4a85613c2e2a3038d12a819b59b2ecaa5dcaf56043ef7d6cef8fbbbbebd7eb86f60d4c76b556d9d5cf3cbfb452daaa5d936c14a414bcc00654e8214586728360778cc7a5f80d82a01829a59436184b5dd26e7fcc3efc3326d85034df1d1041b0d61eba84dab572e5d6c21e2540818797bae7f79885fadc7920af04f56a9f04620cecce5b806e643acf860ef4f629adb5d66a1fd4d907bdbf77a0b7dfdd7f7e86ee6700bdfd64648f0df63f2c1f621d1b96c8107a96b7e1f3f66de897c1fee7656079e92f06fb9f9fc17e9f1958be50e70561c38632a5ee69401a7f114264890cdd839e88fd388eae0ca08fc8e7eb3ed6684b83b8599097bf7056b9fde1cb9caf797e378c6a5c3d7e30ee374eabdb56ebc681f5bddbe618b7dfb75906e7f10db26d9957bdd99a2e5a84def4dc1b280e9b7fb2902c65cbea4e1b8818511ce4fbc9af63a4b585a91162e904a9b1914102cc62d76c0ef231b0a8871cd8c70859277c8f47fefe22f6e1cfbd404595b04e1096136ee6dad2f1320c0554618a443176f7fba75f5cd02ec93b1d6c287acd2b22d11a2ef125c64db5ed6c3c1b934d896f38d7379deb9b94eb5b93ebdb93eb1fe5a692efbf6952d81717b0db9fddeeba7d39b8833d834922db63ab5cfff46c38b335bf60d0dfddbe3ecc8a94524a29ec567fb6e6173ec78856ec534fbefbb77bf5cf6184f5d5ebfadb572cd5f7ef2a7fc3bc9392caaf1da59b8a7db45c569c0ea19bc973f66c6676771a19d6e7e25629e5047382d0739303054f0e143c5e4e1076c851e244cee68e6bc286d549f3650ddd606c6cfcc25c655cbc0511d3ae4e0abbb5dbd91936940e88efddfdcdeddd8fcc03c277738e6bad5c6badec634e2a8d0141b9a7ca10bcf42fa5b5c563f1421ef63311f3aa9c5ebdd1e360a05f12fb5e85788963bcf4a5190dc4917f6dc3c6ccc4efbed07222ed93a95f7bee0d12878f539b8d7984b435794e27e2cdce2e1bee372dab58f771b56ed666ff154362eb2da388eff09c3cbf76cc9916022286146cab598c1613938160fd56abb0ba94f36b8828b9ee3a52be42d2466e0be0a122ca7739870a9c6b553855b4147039a70a2515055acd2fb4556af685f68a0dd95c07ad6ea80828444417f6882df241e948b91f1415292a1a8acf9f97a1b899c1dd30cf2af151373faba24a2c103178e6d7836ddb73fdd308e386f6358593cb393a57047039678a23d7aa62c952f12a2ae9760cf6112ac2861fd475a9aecbba1c7418d8d0aa9e792e14abd33dc065287cb0ba5b7356899798c6a33063270a339044a184158dc2140a3333531dfa843a71d0e9ea9e6891ee5837966d55fc0f2a96ba283ef29792e2f9a03a2a4e5d2c1bf683bafe4e9a4f3b28dd04d0b0392eafd4e8e1ce0e4a66850d3b218461e47e4eb1d49d3a55ec5e1de78a23ff9ecabdac69a75b75379dabeb7ab4276cd831b93e95c8b00639ed9d933b9cdba91cb4aed00989a5d2732564ea8e74291747857374a0dcd0ba9cc4393aa8eb6fa9b87dd92b50eefc424b3bc6f81efbf08899ac4b7a7cad2b46bafa2f4e798029ddd6a758cb63eb0e16b342c3ed771f22bafddef58b197345cbcdf4d39ceeeeeeeeee3e9bdd99ab157daa5331f137b62230a653319502638c959f9f780516824b98fc53af309f629ffb0974c34ef5e90a711d0442688823d72e35c35ab121bb68e015f87a4ca562c97be091bf102a228e48a53af5a383f82f71753ae5c2a838a53c2d375d1856d4e0d6faa469fda48d7491be5139d14e27f719d7344dd3b21e818e118077000cfa67ac4ab310a861922038f9e082fe8149413029f6e853942c3b7eeaa54005d8028c012ec508691624d02978cbdd3afbcf6f1f4eeb3a4debb4f7ae7bee66bc070522a6caed6810d4e67633943ef5a9ebb84dcb280e8d5d774c5a4c2f77266ee3a92b6e72942bf1931b79bd401004411004634c0c08fe8b449b066878b5a9afb749822db0055a9048376132b5686ee07ce8a15992033b493a1142448fc1e91d4aa92d02f64082ab504a9c14f68e9f5e6e8a25fafefa2afd5e94f0621f3d629a949a081ef98b44a1261aa0e18579e1f763dfddfc5ce88f98f722bfefbc2ff203690bc0d8c1f0227a898f86f4781cf192df6b94801408be3029afd7abc5741905c92203ee666630c6c44422bd43164b4c34376445ecd02c49f1f411940df9158179bb09ccdbcf31344b681dc2fe5d0349a3321ef99b6a6d948c73cbd981b2aaab561a50486a18c23da723d72b77ddb49958dabe5a79d5b5d65a2b00ae7fcfa461fb8623607f1afaeb007f8049b1566623c648a59c3c3d7eee1533ecc25ceca2c14959e561659ed9c08442e94441b23a926a0245c504859aaf5965f6984c2613f64ec30ebe0975e795d95365be1c44a1e6cbe403e5677e2119e8c1035c458969e1bf6bed6cec74f1a8fb9d9e64fcffd9e2c1d142b294ed1058ed50d1892223d3cf3a324d3f7e0f6a817d348047fe50a2f85862008f3c472785eb1fe327539b7d7856c5cac8c8ccd9d5e486e33073c61933cfd9594dcb34add33aadd3baaefb743341a2129447e5b26f2febeee69971b7d619842174ae022e07c148eb6eb355636240f0bf0b85be1b5c669665bf65d9fc7c97755da78475da1ea5fc30fbe8be69f7a3bdee33acfbf109c2913be9843e220028638a8986c80d2f3ed02c798940075abe9fef0ddd0510a260d184deb7f798aa5f1347feb5d6c00de1aecb73f7c3c50b63bc1edfdfcb0bfddc735f88c39d89da950b038fdbef857d3dc0ddfee6b7782f2ff467a6dbdfcb2761f96e88269ad0b7243e0e1787ebc2f6fde9be7624acdf38f8860d6bfdafe1beb056ed0bedcdfa7b6d5108fbf705c6a56dd8eeeaa04887e3780e34a4941285aab5d65a6b8da55aadfdc7bcc6d20c0856d156e3a01391f23a12ff99204b582d870847eef61b16312ce459ef35d9b9d03fb7efadd66cef1d90edb7af4d3c36e417aa8883de375f9f6a7dd7409fee9959a0cff56f5f530873f4c70bedd5bef3386ff3c21d57abb3868897a4e4c84df92178c00cd2dac1280f7743e3d0d216c1dd504a29a594524a29d71dac0d3531774f9783cef486de501b0b56ea4f55b4c8ecf1a889da3878a22aaa8ab1b99e14890dc3ba31ed62da1a7606e004145e04206a165c620e7063c4300cc330f70e4852740aa618615e3b543b98ecb0d991bafe305776ecd8b1634794cdc26261b1b05858cc94526fc8d6bad84b7fdf1717e8edcf46b92c376461b1b078056db4c448c9f591aa004b144015a2e0bafe286ce111052928f88e1da6eb3b6caeef385ddf5164c70d8b7dd01e6c08e382e999210cf6d4e31ec6054365767f493e2f3f302e98294cda4e0ed3e215b6df7e60d0ff65caf597d6f5a9c3c2ea3cd191fe4428110ae6d5dbc949302e077f60124c199625040bebe6856cd26e29a58c32561d44295958bc82b3b03a0a64f4615cf2fd61ca90eff243cb6261498f7a399675fd356f05c3927d302f110ae62542c552f62ee784695dff08c373fd61a688522214cc0b75fd5140418a9326cc0b06e6958289ca96269040a28a571651a4b841af2071058933584c94296ddacc9c84d7fa3f13845fd98b88a9c6db302561fd0ae960606d6ae2287a5324d6566ec3721ccd0469edecccd7ab06e3316d679871aa5d2b57eeee6ac44e3a699db46aef97462dd25a3bc0011ee29d83f3a87e2e2b8ab55315731013908042af5439f9b031016b0c638c6b5a84611886896229862dbe5580ec13f2835f28a851c2d5814bcc29601c98bb70be7ea8f3b34c638265f681d10f6362e091ff1831425e548a3b80627deb884aa452562b9a56dab1d68f91d8a969d5f3ab69599326d055ec186bac22512a954a3173cf0f239b0bacdf90a6308c7b5ae3b8da5aac35d68f59c462ac51fbac56eda3b41be33a272ce7b57a7711a9348e70438a4353760d1b5a9ae2157c73818d1a3575777757ece9a47245e0371469ef9f8357c87edb6615bad23c1445940dc4cd2c5976b79440d80102ab8a77f2630f6364b6ef8fc36a958108027a68b4dfbe1ff4dc50e672ce0f8af8278a23e7c17e1a3cb27f3598b821ebf4906ed810bcee52d62a23d3d2dee02dc794921811d238a1c53c774e46bc545b3c3ea5cc49e79c3d77bae79c734ed68ecf168fedaefe56638761d86f3e27864d6cee787ce74e37fb685139a1850d61e525462ed516d7b25b85db1d59764b2de4697dc3b984feb9848f19dd9cf4fc76ff6c4d9e8f670f10f446b2942df3a41904991b65252982374a9abb6347666e80cd8a26b437b633f3c7ee363a63ec269d9392f1e19d0b9c8c6c2003ad57177155b9fe4c5b4747474747876594524a29e5373fba7210932d256d971e28a238ddb9fe92f607c749b4fb2736b189cd39a54e2cc5c480e07f5cc9a6dd4dbb25c5300cc32a9512e5fa334f4104e77c8ef804216b9493ca0f7bb5fb118296ff11ccc979dd789d147e709838a95d2e2a850a79c5b647a7a03a5ad8fa74e5a07740b4dbdf0785c507553b57e792b53ee95c5cd8b073c1d0996c3a2a5d4fe772f08343a1509d0fce47071785ab1d72ac2a579cc41ceb08fb972573b0651c21e3ca4b63f385d978a905274a8e12ee45792675d11e7a25c3b82b4e6a31d8460776614a3972e4c84c3f4a29a534de9057de45407238b144756aed8fc3c1a12bee8a83de991c049d7b75a6576c8fae60e0d544c3a52da594769482b1c4341ac63df8a23aafeb3fa5a43a74478a4291939593e41327c59d9d0b60988bca87d50530712be8c1838b1f9f3b498f104a4d9eb367b34874bfdad89fe875452412a5a89052b2e4ee0f8788ae7c2abad405a4f463af768cd5c9d6842449922499c20d6c0715c48f3f7f7a610b77b2782e1fe6b8fd2d6fbb7ccbf7852ddcbe1f455fa9fd789dc739e8b93848b5ee5c989ffd4cd4bcf873480b618e4b317961e81624af3aa9e4bb91d625657ceb0aa142486c95d08ded099d4247ec4b46ebea0f2a8efc49d60656e5200d4d5b1b502f78e44fd560d09f7af14179516fb7ea9e38e811d17efb8f0e9426dde4a49c53ca18a393ebf3f3e4d30404aae2654db01d14073fa01e071de4caac5897bf1161d908e25a15a85691bdd2e3a0b750252261ff865ecf099757693cb9a1bd5e8f4be552599775c9c412f62e3704b9ea0b6c586f0872815c71e4a73802b942ab02b9402e99d93d2d8ecbaa1c54c275e1f55c7fafc74954e5568993709cf4c4aa9c14762b90ab7e287219b93982ba2124a92a8e049b02a8b4ad1716b57468060000003315000020100c0544429150481e8a8ae80314000d6e8a3e765236170964498e43210832c818838c01060043088081991aaa4100040a6d09274507de4cb5ffa00b0881605f11a96660b1b780bd9188ca25653be6bbcde3fb64672829fd2d6008ba4e1b9f653ca05ce82aecb1ac227ab7c42185b6cda4283801e3a918d3a8c2838210813acace0ceca3e63abe3aec0140a262b3ca73e9a7442ad454f3682c3f120f616b680a3f652ac4c5c483ad04b91e76e5a495d093d23d44644dc2ad66fba310793a76788cc6fc041e11eac0bc7228aa7ac8b9d919db8203a5ceee42884a0e549400b251d26e1765af5039da0de59c225b8a76ab51f933d3fce42c97cf3c877a349f95bf868e8ecd38e5cc9f38494f42716578b20d40f9ab737b2e264e5e369a89f932bdea73e62a707965400104a7cff570fd3339c8ec69c99fd69e4f233a38f764d09831e3d71d0153d759f526fde488a1f32998af4ef13ba8eed2720a51db6ad76956e4abd6b0260447140f24eda6c5af8c92bc0391bccb06496d452e3f45328e7dbc2618b2e36879c6a1514283e4fcfaaa523976561410aa84649a61ca66ead0fe88a0e4ff811031ae6d44f61899a6e059f3c98603bd19d8cdba9bfa48bfc48fe87c440263df7911fa2ae4b42b51ed59a5d5d2c97f625470d5003187835a1ca97f96b4ae40356421520a495c6fd6b91eb2235b6eb1b5fd17f2ddf0ea2101b462ce1d8ac86d14a6d5558685ee4790bdc4312e0db69c55d172c7642475c83f97e018e935823e58723974880668a97c6c085a3f68d8424bfc2f95b3ba510e2e527695c87f9f8336dcf8e1ef6022351557fa05c0bf49e0ddc4222dcf14565ceead95a5a8b7cdf38bb1be720715e976b757ccf592e68ebac19b5e363a89d11047c97051a9d2fbfcab13be9ec7fac5715e6fd2b19ade23e65e6c00a8a74f56d75e7ca57100deee9993ff3e1a74874f91b4ed187da004b1b163c841ead976a604bc54645ab9320ed0fb82d36bfe6bb163305b6ac7d8769d209d034b0528778c76f3d3b8c698bc3b9df0828c195e8b1aa475375b492d6efa0af3b57f352d5ca95432a2d9cdd1cc483e52b90fab5c1fe6f91533ea063c492c07cc3fade32587d6ba4d9307662a2ef88a30291e5d341404fa82575301a6119a17a7a5de79d5c25ea838dfd21880d8d757c3b1393425f9f901b3b28976cdfae877891450234b172576081285e3118bdef6207c66ffdd21bc850a66ec567054fd59e23bc649754de6589747992e36397fb334ab8c806f66dab052aa0b78744224c71470a86cd074eb9824762ba66011480138cb1a44603200edfac0dd28e2331eb6ac08aff9aa57b38805c5d601460392b6c1ef57775a6d1000a70ff3bbba95d800c5ed89461070ba15c62873fdd1819e81ba9f5bc515e3e7b1861ba914afae979a57ede020e03ddb4249fd6ad8f461fe52c9e726a232831a875e2a967fa27f76106eb5be6eedb9b4abbb405676f273868bd9f118fc20ef6f56ab7905ae7748dae8a220856284a31cc1f02f88086e3269bcd32292edb33c6b7de12bc8e29559fc1492801901383982b415d96b7d0ff9944a0d6484e3e30668ba7336dae58dc6b64546e09f8c69e2e5e24637fab0f0140a6aa2439878673e995c9012d86f3855ec782ef0f6f4e21e5125f0bfcf30c66fc2599cb032d766f56b57f146f6f6184900174f43c5a56b85917b44a60b63ac28ee6cbcc9c107e9670792e41052f704442749b6e6854179e7ed31186f43d9f85fd1699f0c3dba6052fef1bfade1416256571edf75990eb7ef4e7d59d112624848960abfa84aea16df8ff45d80c25397ee66469da9520abcdf3d8271a2a5521c06235aa4c4682f992782867bede71c0d8a91968fea11b9657ee09bf6f492e77d754c02be480a82056d027aa38508a704d2c9fd0e2284f6828d66c89f8200446e04287c683df5e4fe475fcdc0e565f54eb74a3307b13f4611e24925be8d446be1ce140b22c3ed6640a558bd46ad8af56314358c900c0d80922ea9b1d5ca991ad3d1a1425a89fc9b3680af8dc43bb0a12aa8f78b8410216ad6581685b7877a50c720b9e5240fbbe671e2ae8b8a163400525e245bc4bdee6adcfb5cc8b1cf7d2c9746d02c95f8a8442e543fef260fc420202e4c69bf1b4335482b464a1646e00f18a0a01389dcd1928baac3da98035cb11b477d0c6bbcde3274c996efae0855644acebcdfceb7d55249d6d078cc3689e6dcae3d272cd156a46b16b4c45f8ad5482ff7516f9bb35ea3ffb227e57f7bf176a03fc80a633ffa2c8f184ba536f35dee292c78ef8456ead10ee2f7b0b0ab94fbd217c516a4becc077f148a53ee0ba059eda21c671a199f19f181a90c944568dd77092f1655bbd841fd5df7daa157ae93b0cd8e8a11d02f7198101c6f6e88a3f3ed816284400624e99ee918e0f86f26fccdc6412fab18649d459e0e796850d038f8ff6a73bfb6e5583d6afb7fed264c5cbe2190dbf928a498cca290206bb3f1aa29335f252993ba9e6858cbc5a06622ae229246d679c24b1d0458dc7cdcc9466332dcdbcd26a2d5269ba0dab3fb41168f94ff5a55f3165f62561d3a3ee8f731e049bce3ea1e4ab9c14e57bd8147a528aaad94735aa1d4bd7a25f4c618c0d673767a6811b4a4bdfae144448477b1ce977dd407561df797411f9497d2c025a4242ab6fd48eab5404b2edd4dbd68b3d7bca9c02d9afded48a9e12744f309e810cfca0ca3776679cf8cd77ca4dcc57160b9e5d3d9321eb20f9d4cf4bb3aceee239ded35d9ccfa14ee2948f0ff2cff7a11fa1aff55a34b570690a8d5cad73f1f140229e2bd0b1c3dbc48d79bb463b8ba701bc3a43db1c9f95aeb9bf844b67ae7f0f219bfd388c6b41894c5a44da816d20373a0571742747c720535b06dbc81f93f742e970f5fba81290dfe977be04450c04b89435668c3f9c59f0a66e04bd595012273daf35579f7b4e5e15a4845e2d8b0d241e0de3e64414caa53c96ededbee365ba28dc984d299422fd78b4a79d10a5ac02fc4e473a7e0927a9e9653c3a482f87a926070a1a82a58c29815cf5abb733afdae18039b0aea0f4bd1f269a0ad2efdd32b8a282597451223069ba4b5a6634f379e3df5940d6ca8ab87388b53823b855284bbd44bba4e545635d555635fe8d157283f63915f17f51517d899d5deee10334144b1d9a8deca1b62f1b9f2de0cd03309d21987c2cba564f557bec268f4589b470f82090b3a795d6103640b8ac9fc1f791e39a4ad717f15416aa70c867070308c1e520e048b278993750289c9382fbf1098e30ef91d29bc9008cfbb8734adee548dd550505ef6dcec78cc7cba5c7318c81be3617a004f148a5fac70754dfed111e66ed7217ba20fd1aa3f7b9bfc11786b262b043b344e39e72ef3e28b0938ba3601a0155e5f281afaa2c1c30ea144efaac6bf6048f79ac6716694f8bb8c456599433f9d0ee3b8200aae75d0c6576e74e6e41cfcee51f44450acb9780b992122df1015ed3a5506c82b50cc225dc0e5a558a1cf5228d52aa2c3b1100bf452ed966e68ec5c96609445077290eba02374c246aada12077df6ba2931a213db11651f11b99d8cbac16043e281eec9e425b6bdbb003a934542f403700ba2615e24ce449e43254613df351964bbe2adf5648b8b740b44f2632caa7297abb19a1539c920f80fc27f115b1664d7e387e5d8cd05f9423780905bc85d235973d1a63f1558872eef30d2d903d1ece26bb36f517ab9510ab2503b0f10fbb68a14c88263b621c5060a68b2a8ddb0c6b51e456e5142a64bcd0beedd1163fe8e032c68ae46e41be2c2210a175e6c567a1a8f0f8165e3edf68360e37e677d96cde158474fcb2d92ca1afe82b48e8fea66155810b6f14e69c08c00ebb84a335163b681ffa5720c80bdabae5fd687d58306917ee0cd616731ad8379f7da2f4e86ed201f5ab0b76d7549ccf3d1f18dd2d84a86d5a098aeedc1dfe292f5fa1b908769d170de21e01ce1ac2fd31c4994238fc130bd41107a7cc9798607a2be880e7e31411ab0546b3861a608b9069728f7b843681dd2cd0797f59aa43fa53378b890d4c3c8dc2fa9ac0286ba2481c8aa34452e2c915c1fa79dd063e9490b906af5d9752234884c40c1d8cf6e82a8d626208107003b05a105c577a4ae6e57fe7a0c8cbb9fc0f3bb46b78479c85e7df1a99fe52ed85e7c64bf4755536352442a9db985db6e930b96d04f3ee99d76a6f65c2bba6a2f24591018979067b8fe5c8601da562b3c28d4b08ff168dce8cb4fb37f76ed9176d58ece61bcdea95d144a3b652a6876caf69447b375a764d7d54a0596537b2ca36b773e05c700dcdd41b7276bd4ffdd02ad84ede2ca82836620ca8f8de2bf0bc27cdeb8c7f656e2541b4c9f0baae9de3f32909517669c107fc50658c630665e37f893c186e52daf9c4221e08547c3f679aae1b82f4f0b57cdef7a484c2c0eb8bc8dcb724d620dd986da43a5ff424520a4afe7d0e1bc5361170a2c659d1909f6acc96ce68c7898da98d37c210fda61ca3157f189c6af9f65ce697666023369dcd6ff856081306f6b28917073b1e69cbe2b0e380cbe2997dd739d7ac1b8826a6a76712802cfb67cba2269c86f86a87ef4700a06e8b9cd087984d6251ca591e3fb5c3ee53550c5699c1f6e3fd4b46217d2c394d77a9a842b8286d6bb270373b33a3481986c11ccd0e8bdf83e1133467af9410f48bc76c85f09c6065a27492e5f61d5f951fdf04a070548f05f376b163f021139d696f6651b5924ef26b3c8e9cdb431107708f8edb0fa5c2d5cb88e9c4e282e0b5d24fa85e072085603350207adc75766633524c8c2d6e522bed0fb4159872b75388ca3099b61d5cf0ba5ba0aabf7c758e39acea1df301581baad2ea5038bf49c435f22e681f76f4124cdb7fd6e8a8dc9919c612090f3dd0515fa21ddf07c3976fa059e76aef85dffc9e63aa48d16dcf6d3f3fd0379bb1b77aabca086639a461ff16ae39afcaaa576f8bf72c715ce59c06e91fb14b398601be44495c5567020c758bd35f95f04bc47261bdfd0acb8fda0fee6ab44614913827dd6c46e051009bb13cf6b9fc30e3165bd1cb85dde4d5230d98d4830387ffaf23987db3995a72b2e47a80453baadb0b13e0aa8a438bfeb94db4302c0580249d3bc5c3591b6ee7cbef1bbe46ef84055b46369ee1ac5351fa2e2f2c6cc24401f2c2e954a9e7a6d6c1a7ac513bffbdcb6f778bd6ff4a391f73c629fb07062799bde6570ea445bea2c3c7884a87f9b790be24959fb0c66f5e9aa6dea1c5b9cfd79e65cb0736a8651c54215b66099e1e03892557599c024eff338d471f28faa43d98e973bbcd7ace63a0c4da5d0bd691244aef9662acd41898e19768ba076d6bd302347e46e9f7fab90d0cbd2d175dc45a18445de0c46076e1d9b43571a1e0c9f0b8e1c612aba716e3dbed50214567a102d926937b877b27d77ab54ae844408c779cb29699b24e26fe7da59beb57bd17c192e4250525b98de14b6dc8e5f66386b85ac5212302fa3db97cdd313616e14c7d3ed8006980dd06172883a492798e9785d4a1a66e90349dae6ea6c80abb606c3999be76043796c4fb6406e5a9d9304419b6c9d9731bb6580f0513fc0faef60fc72d11bb0eaee373095b8c7c2dfd8513fc00f81fead68ebc8e92755ec79f150a5ae8226d2ea30c16603c62e041447325cedf50437af02625e21978f84e8e3354a5f1c00bd8e0fd84e54ece289ac41135ff9d18f3b1f487556b255e74a0177a6a785a09587b81c50951a249450e39f7785994692020594aa2b26eb6c6f49764c47aac4f9cb9b2749b4315f0ce2843453b72a53b423b00cef9887951d7355233bb8381f742555371ea50d913a96e085ac350c35b4817ae440a19f23dab9d8e359e5de76de1182735d0d760a0a4fca780974fed3c11dde70c06053785afc5749d254489b997ed314e017bd633798a907352f5feee15bda375ccd104a2fa15372de698ea04e3372b9781965494550189aea5303caba51cc10ecbdc47d932b1a00c7953d7b31279585d37ad44a63836d6de636f38d801a686149a3de23c298a3e8fb9c9a384c452c7b396d1c143386b3c62ab93c1090bc8ec1d1df69204794190f1c300e14092b1dddc86c399a00f7835a3d1a7161679105950eb7255786389f83fbc084774a1995dbdd68f34349c3d900b58f057d0b34278b22505cbe3dce088b824fd8a13a6b2072294e8c4d251b6356feac47d92801a4d86579a25f0e55fd880b338badca19990b558cdab1b9a9c5a1642a2e25fe6002f10b2e9e9a2570d02a1da5065a34abb299cf5da279d11d17b877e4a7002158855eaa48680a2a23cbb13aa7e2d4b4ec6a6e18298e89de0848b93e85b22901ca789b0e20f491b6eaf7e52dce43d33125f2d8b38758c05dbfaf8aca7a3cf30640288feec3044a5cb2eef6a8d40cd36c3af40acf23e3782269a9b4581415c1da24fae30d4236701ce659f13e5247868a0b562a37abb6001e7a965535de7899f3a842cea8db44ce2cb7c5bc95c0f3d2dd0d0b02dea85bdfcb85a6358883f24160f5a2eb6cae9193787de1fb1c84173dd7159b7be6e0c0c274769217487d1193d1a56db3e38234e293b7c01a8e1a4479f05e1a7b93b1b725c17d44596e031f75508487f85952f1dbbfd849de2cb6ea945c8016d0c0c4757f5a1564bb4a7c88bcd9670fc2a51f7bd4a7207bb180da1fd889127ea02a7ef410e76bb1f92e45124cfd1f4829092c3cdee4d9b41f35be86c1d9c95d38cd08762f496613a4b16b9ff04223cacda6e2206f7b8a646598ac22c4602a9ad7db856b157c9eadfe1285923c857d4c71b7b1ed4d3208f4a635008e1ccddcb0a4ebfc1551f3c1c84c0363eb972db0710fc2f736f29020fc1d4f2057556acc92c92cd7fae6a0a8fab8791d6e425b425c04c15396c2d9d81e33217c024c9148c5472f56447b4cea88e03dddbdf58afad81958cfd74c36189a706951e6e3722ecbe639b9eec6b98865d33e4f353ed21a80b19b385f99662994f89931174ef7707c8f71300791035cd38a355e25af9757c1d238f36eb0bf65d48ec0634e1a2120b9da0ce92e0e9c5d646da231a1a8338bedf03599e3b661d2ee23dbe42a31bee5e8eeca78bbb6cbc811a99a3f60c3e55a83540cac430c1fd13cb184d812e4b998733f771c7d3ce768f50967d1b3fdd08c57473d7119e94b48d16fef2665c16477c8e45d81de9ecc6da2c7bc6c12a80c3347c33d1053a62c72fd4afd6d544efcd4841998adeced075ea0ce84a6cee2ed6d8000a6acd98360ad85f6f4139b71a02f299eaccce6312034bf074772f68fac3e690cd152cd8b70d0ae6a0d5e5525b3953ac0620db29d74605413f7e76630ab63c132b726eb1253f63ee75328e3fffcda69b85bfae6920ad24fdf01f5b5e7f66462004f54f735686e9d4bb0e7f21095dc93c82b91fd4ef75c8e31cc434f31cc1a1be80caf4a902a6953db6f08e7703d82bb12db4ae76c1af720e8f2a89d3be00e0ed14dc79398c022b258de8c215789dffddf8946ec99502c97e25450a77324836731619fa5f1a29ca3bf814d60c09115680c5b0f580a5bf7082551de37163be303f17e4fa4f71428c30098bec1c7b167d591cb40467c5c550522a0c2c590605220d7dc2ae8faa11cba621e44a6cb35f18b873f2e3fa035b3edf583f79313a6b284d3515be0dbcffd28b7cf2e70125a3d4dd379870efb14dc9402448bd45f7e09202686ee9b178a745e2a9af5d89b0958f0785c7a2ae1aca3cc78cf403b5aeecc1ec918039185bc67d26825493ad96408ef7f178de93eb57ac9004c904f6e59fb89099053de52e8bcdaec50a917395d0edd763b7b1c6d654113aa4d5d3fb2894588ed5e5cbcd9d667100a2dc7e2fd062515995c4080c745deaac229375d218c896b5616b5bb4c7356e62e00fe9b85396ff798bba649e06e807b341c1f0ee42ade861ce3718afbef5b9ea0640ca59eb416fe2b36ac092d5ea5057af36eae97f6f4cad3d2ea4d6de5930ee9b5c1844a713574883313a47b63a6bd290f439a805a334446999d3834e6ca637a23a86df2c4e1bbe97da2016570c85d1e0c5518e014ea5c7596e3a4dc0de966ed4fcb1ffd31ad3e38f4c67e45ac80b70a7860a0f2128aefa7a573a7d9ca7bdeced0af5bc552d7c54da3a31b5ac8dbe6784deddebee2ffa8db68f20229be03c6848f26a48cc9987455a20218c072a1523f933f052ae9e8084664676e361d4472889bbb7617769522a0a61fb3a51154e7cbf680bebceabe0d815d3e84ef7cf422bf890a83027ce32236b99b1a3dce56f04f1e2fad3ed879168839b4756c43a29d7fc0cce47a91888d05624f0ddec475b07201174b155007b61df3290a8df66692173b67751d5d59c58d141b582760ce275f8d22db413d228535646b1d86e5f3000a816171b73fc01f16c33e4c982e56b45a4c5fcf5ccc60d7f6e96702004bdd0e546817336e39e35d9ea6700ea90609ff29e70e65c2c7b6d700b27dcfaebf32e7e5cb5f0461aec80f332609bdc8d74bb3e288ffccdfcaab64548e481cecb6ff6663cb01470fcbe60168b64b1251b392cc9e6c13a3cad7b84800d027c4695e0d0008d5d7e12a8f0162d02a694e1569c8174a7649004b58b1ab27ed1119f0be02d46313247742b2265880c8eed5f96b3c8ac6074ea662fd1fba8235315fbbc3a142104f0fd51330ad1ad837855ec81fda4a463f0fac7f7721ae02c3f87ac8b7981c2a07e6774a0c2215455f7b807b30f01f207bbf6cd8e0dbe3bf380d2fb0ab5db2b9910c8750c0229cd1da120ab45118f40cd0093e6b26423b262ef663adc7f68f0ebeed8d01b0a7f3f3e1a4d39c3f86b897f9d7f13bacac0ec9303be1c85380cede888cc486c44a00452b198b30f0d02b94c82d51807d7d6197c520add863ac05630d169e069a4413a7a7a5a83b212cdb83901f64a4e9625af43f53c9f08619799e82977735429e4406e92b5b8df4aa77376544c7d764c1d242cc0ced80e18a5268acbf4aac6d0230a539cbdb03b99bd4907100359756fa0ec7853ad427244586b59aac0aac0de6ed00700de9af1fa07582e44c079cbae29ec0e9fc443fd02625eae60d438fadb002d763744b21bcbc98a2257ad0d0284622310445d1d6ecb9a7f401d4196c8c7dc735b927dd4b9baeb08d264538b42a281d638e70464e43f811e228f45ae7e0bb8197c802d91e08bde6e21c7c6f2a976ddba2cabe73877bdc96cb031b3ad858b3e938d74487f20546ed3f5e840541ea07225b66dc5931335c8e012b68317d82002b731b09da348ce774ec02dd00d083a80d228248855045c8eb96314e4eb6aa833aa3e00eaa541f3a9f54df611fb70330f1231a79e8269646f8ba24761e35d5c5379398335e7ba6bb33532aa837ba205077de115c5291565312c2f635546cbec8e1b4e26e17ca84de3c0691a2179bfd09f997a036c2d80526d22aa1a931c1685bb5691df3c117280a47985deb4f70e4f7ffe9f50bf9a3d551597d2c5068433e0297fd3d7d21775984d870c4cb0fd50e7ae0cafe5ae00a370da9262577f85579d27fbc5251ca6aa47d3980ee35abbc768a7055eef3b1a491b22c8ebd9d4d9e30c2c604eee30203831e99458122a5842fd215976d5d8a1534bf45a032cde4575fdaf451ce2be026e7b729cbcf155e315fcbf9bab7e3bf557f2dfb8aa73e687ecc0e87c9591e82e74d3af6438acfdae216e8f7c816988a0d86614f5c1318d83c06a65bd954ed94add8aea9f95a1c58aa09be277ed49236e8b6d6e30f812122ec423dc1d11f250d4609ec882d407a80274318298329e720708288483bb31eaaa459c9b2e9ffe0dda81a89b6bbbc9bf5dd9ac8bbc674874ce24281dd37fc9bd3e0509a151092a0fd3caf8dcc513a927aa3fae0991e9c430a0a139326db3f08c5f1fe8f9bd5b0fda983f4e57c93a3026993cacf1441b21d30f48181f27fafa0f6bf12f3f654a5bf68efdc02deedfefa27810b9d039b845471ce47106e08e710f09e788b1bf3770f56a5e0497f8511114cf26bf59bd27bb64af38ae71e27c2cda126cd7720fc9bd1f33dc9102110bf27e138e1b79f80573e954dfd8884cb62276658d23c34061e3a96edacff4a0e62e6ab2127becc955fd46333c673e6877bb19b31d9eaa7e67701aca300d18e769ce1fcdb514310a2437a7e76ea0363c1bf690f5aa59860b91c2c19de6865284c91afa24a522eaa37767932da78e76c29e99878eb9df511045ee7d3138b346fa65de0ca365d93e150950435961f0bdd257d00c52a0a8efc939f28f1a6534648a1494ec00df839a1d072af8618925c6c279e518dc798d78e36b0f13dbde431dbd8918b3382e4a1f2a30bc3219015f084fb215f2e917fdd9a5ff6f258cb213ef2da7d3b26a2bdf56d83822bc1d2de37c76fd874c4e76e0c865a7603375c1a4decb062f642501213896336500c138571d1fb6aaf96e26bd4254597bf68d258911861987a1ee5b50365abe2f0c44c7f47693b180c2b5e6c5b91c1b062714e37b36b1013a482e6b525fab71cf56048986a0a3fa2b4667836eff0ba729e607c8a170305d39d278c320dd4df132d10918105513af0ee158f84909cc662dfece96cb86f4c1b92383169a8da08c0e05a841ee449f1b921035fd76ab4a6f0dbf4816441f74d9c71b684773d5f894770617349254741d77076b830a8c785c4fcc5fa01def37184cf42c5ea0c897ad44b86967e3f573334c627ff5626a98825d2ca112fca307c479ae41e782278f29220fbce0858edf2b58a82b4e696f41eed4565d7a68b20d715af917816c1ddbc391a652f389dca7033b9bcd39231828ef7b1bb78f674274e364e903548a04625e4cce3c3737f258db0dc5a54c10769c594a0cb0883c79cc85354bf66642b6b0bc7aca15c75153b2bc82137f17e2cd81cfd50f69ebb15eb6f467e8a503c395c966f91d9df1b59f83f3aa3ec2055a8d1d18c1fd956f63267a73ba0455361b2f6dcdeedc8ff08a7c02c6daaa720e3c242ff22d3d1d15cf70e49c9263c58ee2cf7750d249c7e5a27857b881c25da6c41220422778f58d14690ea28581b0086bd9de93ecc147970281d317bdc27cf694c90c96e32381ed09540cb05ef46205b96e011ced18ade406737cadd6006750f521b9f35a0b11454f36b5ee56a0b8ade1213fbde1bfa815ba0508653106ebb3f4e2296d84f5eb94727b8418d1e6ca8139abcab8bd06bdfad1965d0602ba0693dc32c9d25c7bee1d9b29a329d03ebc4013d1d23822567a381800032fcdb02423eceeb45bf458b5451832a813a69350305fb4965274bf66796ea253e44c51c10bc098ed92c626f5029338bf78f6012756670356aef2d39e200c79d8d7e57dd76855030e0c0acfff59106659f598de805e4548c37c4af0a1267ae174893b00a72a7dcf385e6ae3e65412c05411019b539fe4974a219776642af9398977bd2d0fb1ffe8085c720bd65c0b441f552cf4984f320c37df05178ce368e6fded9898ddfab8202e69d83a21ecbe2b2b914e2d17f8b955a128fc20608d780c61abf2b8db667c8707e4112f86688c651deb6032aebb8b719fcf048673a1d03d5eecdecf01825e0525ca24769776d97089b2ca13719741af8c582f61029e7e6215d883a89acda434148394762b9c513e390b75e01996131dbb7d1708aa5bdca6ba36209e88600eabbb0480dbe3172163b02206af8bb28a1bc2c251d7be8f60a02968f7d93a817eda7c0007d9d33d09f33939330b2fb8a6016173b5b415f804eefabbd72f81b969072d4e6b5a90f606b7156f6e586e4b01450ccd62913cb07af475d0a97fa31ae6c7dde689c4ca0a14154456f3f55d4e5dc5e41a0222a511810f88fac22eeeb6648654ac4bea0918ec26ac85b33422f78464713c2f8550db53b4fbc03a284a59ea2cb55e52f7090a98de423ae7980f607bfc2ff33d15b64fb7d15bb6fba59b13d1f0ebb694ee7cf15b39e10fe63e45f1aa6597db314ba3cc108640ac28b3c88b606b671db0d50867b5d54b670ecc876db5fcbbe9f6a964a9497f6e63ad17fe0fdde50eacaa105ad467cd662e5d111468c1616fab31c575d6c93dbf8e5837931ad6dd74faecc71b992d2c40137162934d3d966b51da72352303bee453c5e6aed3eff32045381a2f6056b6438c14f211154a81180bf7744508114a3cd47af16c41ea45cb886ea0d5e4e316c2b27832e4c53db5f08e7f831d53dc95bc019998b69b4578378f638b180f7de887ec9fac2bd5e91a5c7fa3020232705eb1aa9c0abe425c80a9fd496be1431310823036e4d258de46a6399a317bd4aee8bd86ebf0988e1392cbc54497e10bfc98249be08e1b3115a33ad51c3dd7fde6eeaeb9844929a4da99937de064a532fd457ea369451b6aa0c5e5c1c48085619f2cee1c8087ef599fc0386be20ba3807f60f223f1a85719cf3016c813b48360b8dde83a536b1890c8149b6aec7f854707cd7c5e6a42683fd1ca22470a93c8f08a8a525844eda518b3e247f050684be9865bb269d31f3eb3e8c7c18c8f8d98a7eed6a91a10063065767d38716701a2f3fea808ed0ca4f61af7005d3828ba670da9ea2534916a4ce4cf04e8d3a40f82ebb2aff2a22e816eaa94cf2b7f56badfdd6c41fcd589cd8bf1f7fee3ca2fb3b5a963e1176caddcc25bbce20732683f60ccbeac911b1c686f660efd8c4534ac0eae592962ff94c4e44099cb7c49ab463d144e6c109cc1711cf34327574276d5910f60f047a90ef10c93828ed7f610c09c76a81734ede15406cab37a7eac71d43efa6408e70892ff67073a57018e4a65c70594f7665cadf331793c4a48c3c47c6beb7acbd509c2213f847b51f0bd8de98d888fcbd8f7258b3521365fd6bfd05275d4e10c74e58a427eccb4f382328ae1d14f07155c36b462d334aed52299ce013e9469020dfca3e9c1ce6751e6f6bbfb4f7aa45f122260af8030a09679805f031957edf18cbdb9d1317542b9a46ea0f8a660cf5eb32281a437f84024ea7429b0405a4434265293de0a11498cd4837af76056ebc03829a0a763b02cb5867803d7c0bde1584047598d2b33f3207d3089bbaffca3fee8916b8bbf18f33a78b48e347ce49e07d8c05c28ba89dfac9a8d224a23a1c83343c3b8867da97429b0141f8480959adc814e83ff31554872612348b6336964f740c87fe6b4b6ce11cb81577ca421e0877bd01c7242fc108c638a26cdddc7b2a81f91ceab4e62179e92de6bf666ce2f2877847527a6e55e211a38100add8059d8ced9de9790ce01c9c4e512b10160bff5a1868125448ca65a20e6c09b91e31758e3448ee57012edb1b9701e284fdc5d81f79aed4183f904e261c7b7dd8e95794e133575681a382a42a0af3fb9574515de9489584525b20366fd5eb6d8552ec92db91c3a3213794b9d2547cd6b3583f88bd191e52d8bfafed8c7aec97603686bff80260c8a1e18f1c7be9827811424d0baf03bb0a25ca40b87371fd432c4358a0e006f33f23f126e29a7a38403ac2b4a3a4d5baf164ee5e2dfec96ad4a0d61cbdebfceca9c50f621ec69d1348b48d1832af6df8aa5575cf904b0654389b11e302582226e1419b34c786a4737e883f6c4ca71e5c235a1732ae7a783d612d9b03bbe9b5d80c03980edad65d27c29e8c9172c8a10366e996382c660a2aa6236c14c06018759f38a69632b7e4cbf3e057d5c3551db964193a9b15450295aca9ecb2d6914150e6c451c0f83d83a23d2ac56023a2ed5ab2f9e15ff2083c167fa55b0a0dc964d39882cf585cd6739ae0c1251c07088b4152cc54b4e3943dad01677af4284cafefb44f311db2577be705f9d5f1227a87a4080f0e56f8dc536c0cea5376525546b9762699d937a5360c8387fb1b117d8c7475b88ccc5423ff9bf43a4b22167bb700978ef24152029050ab31a52791f947e85d85288cb3c3a6a63132d481da68dd473e297957c0491c73fb1dfb62e2a47299893be832f84ae60a1d3de2d5e76d737b72bcb31eb15059341bf82fc96f1def6a0ebde97fee893798723e9b0f714f237a51630d620b9c7b85d316c481bb7b5a13a58b804c9c883c1b1762417033e727bef7d3658d95b58a65e6e3bf21f7230cc3b17eb09c1909fc94b1b1e6e8dbbea661d25019ac797ce37d94d6711e236a7e86a9137e585a76a432f15d4e6419194d2d9f7232d28034204129172035f02adf8ce5872bc1d51c90b232534240862be1f1ce780f47df46f40755023caa66e5cad204e2580ae4d185174409d8f68dfb1a9654c62ce58c24f0d2e8796da26213df131ee16ed06e9cdd18bf3f57e13d78b502e45f736941e598e224bf50b3aa82662b35fafe9f291e065053db80ac225491db02ef9f0666ec36c444d8239477b8197b22cf461c27cd6a48d83b28f233e8d9f07bbdc2ba91361f574467f2a16eaf261573762cb3be17b475f10e9c25d5bda6ffb4fd29a1c1162f264d328ebfce359402599cbbe420d4182697714736f08f4f0c3b1471e51eaa69e42971261c573e2584c0042ea935f3ecfcb86fc0356743c39c2bf870264ab3df41430c9fe43820a87140aa8a05161372aa992e1904ee4a6225032c09987126f8b12427fdcbd0ca688d8e2cd1f4d9ed9b806d7f2c0c75fdc0bf248a93e76e356d75375652be80151a825d929026215fad7eee6791da7cd8127f31e35644a8274a2f7439968141f2faf2f1bf1b6f6eaaaee9abef7daf41f6625cf12bf8cc49b4955f7c7af72886d99c35c3e6aa3afa5ed17a1ecbd30b28f92c6b6ee402f85f7de280c51c08f9577b35caed0a660133be8f1da0adf05552ed86cae2dd4a21485d15fb36fd7a73c3e0599f49fbab096473b38632e1ef0b441b31813cc2c8990e21bd1454ce9768c205e17bd62cbab52ab11151b35abf9dd3ef5c2d5ec793ebe1a606b2569ee4694140c8ccec20ea6c3b4875f51e763ba3227465ddc91249b87229297d1cea737d8bd56a79cec18aa6e282d8d68fb0cef5b25903513ba820495bd436e402a621e209765e8c31100bad7261d9ab55eb1c92ab31f4fe147d5ab29c888ae9ba426269f2c6c9821f896befa9758228ff78254e8380a5aed4b717cf1b56f6760b87b38c675d37954675c51a3cd150050274956f98828ab68deb14d7acd12cba5b562e6a0eb0f7ed8f04938cd61fcc20fa8ea450e42f914632e188334c49c010668828a12d1146a6c85c76511ebb603cebad8d0051decb41da0110669386ea49c21df3ae07c79bac99e55b39b8a08a52e6062a2dbd61cdaafac3afdc7813ac97e3a4d256661886af110eea57f1c45bb4a3a1be108a095bdac34f958762813e8fcfb98868b915692db86f3af8ece375a0423e18b7212fc6fbac659292dfc2b81780f8da09542184839681ff0b86e90b6923c96a7b6a7baa14355ea9022c056e4c2282287882af9b084ab1dceab3e0bc11bb8178a9280587a316900a5e401fdc38bab5c231f46f896f7c4fc9dcfd19bff2d6f8edae43d8d377877de8ff6b5b1ff0877f7f671358a086bfd85d201f3e435f645cd5169204f3f1de64fbd2844767a12f59d0ffcbc50004bc6cc15985616a86d46ad39f5f6421d86807ec6232f17be8c674fa7a3c456dd4e1ecf8c7eacc6051e5ce3e2ef22e56b2b4aeaac0652e481d2cb14aa97de2aced8e60f6b4a90db9d768aae1c1792e8f70859fed9b1696eb3461fb83d70367b6517a4395acbfbdb31839aa9c7b7a5b65f4e05cb7a54fbe5bbfa03b027b05b0b53f2dfdf801487f3a4bf8850c09a635cfa30621fa9d8e8740bf15332c7a7e3de5ee66e265154cbda1b3309d218533a22238a6c3ebd461f03ac13b30d3bb4b0085766fe97981ce11527f3b023339fb15d5f3abde14eb7689f6eca5cc132571bbceb303eb9ec27007868dea4ff36638a75c8110fc97334e6824f2aa301ebdc16eddcfe304e595a01dfdb47d2949f88c77d98ae9ec97d8c2bf745361c65f4ae52cdfed1218f104c777d9bbcc04207f5b9f28bacba2e8ec4bb1609b1cb01b2edd0d4baab756c40da43472e9e4006f2e2cecce3fdb8b932c368c2e005c6c4b8477002a41f7b389d5ea6998e140afb1660717dcb832bf9c763649f16ab6371ff675624d753f600e90da3f0b8165c7e9814605b4b69711920600066fdf3c816ee79b266f38e21d21cd9dcf9deffc52c30a28b3350545a80ddf0da256b9d1fcb0d3e9eb688d8f096daec04bf5839445d5c661df3a7cb2fe716add9e8d62928c1954c4745d4f61d3144ba55a14f44be18bbe3ea35cf83aa8786b55a0ae2bac7618f08b1faa6aa72be1f409773405eb20fc4ed389d6016cdc5363217fba3be19c8d35939f93bb2c1ba80df0c17de6a6edb532c7d698f76fb5d08eef9758c1bf8972b5c6675511a9d95963d99953bdf1ba9c5bf20e817aa2abe6a43464ad9300cff0b93c361e06bf53628dc9b611886d2412dcd880a1e86780fb18a1222aaa8fb0cb245f36e6e1e17f350a2785f1cefbc13d3aefa4c808485fe5cb31d3f702080dee73fe10866d0bb560bc5b22c140006e3169fade5ab86d57a662c41edb39e521f0ba411955592aa7c6d68216cce9f29339694dbca41d35ffa8df8f56e3d26c9d6be45832c0abc36b7f9f1167284596f6897a94aa7d22bdaef9de685286586754478373f1bc0a0252474cf744622d13c9fd11820f6420f06c6397f0e88659008c5dc49b2e8f8b060c1253989c719e37f1845e96a51cb56005c631ed9d8957c0a19c9b4f19e3ee0eaad76c5cdb3150431ba413780f77e04366c1a4adcbb40d184a14b3e2952bd019ba6f5954903b884eadfaa7970c5aa45106ca04ca0a80d732494d260b637d2c5a833f447ea20f95dc103ffaaaefa30b27849ca5196a1f30aab34fce492deebe756c19942e822a96f39a25f49de32551bdefa823b7865f487a8233040ab8dc0a37c376a1787f07060e162d1ff8c8c921ed964ce0fa7d8c5f9a66203ce325b0c1d27178e635a23cdfa1a87408d8d88759e755381854d425f748c2b41c809b77a209a400004a6311dd70d16f24b5c5e4168a2e37a8c85424c1e80d04e106a5b32022df520a42f33730f5ecc324a484295a9cd2685ed7c1746531855fc748e87ad6400352ff47114f4ee50caac11ecf2e41b4ec3e6772b7565a0d32c5b138dc3ec20ce1a187796284e017317f823fe062838fa099259a97314f30f198e6df8a59dc668faa7ed8d9d0ba92b66024aad98f44dcdff5b00f6450f0a1ad8b49772299c02a9cee7647072609bebb5c11e20b94b40042ea5466464cf92d559c0cf40b79ea60c90a00b1ea68768c8a8de1b0d3280670ce3301be5be784bda35cc9119a037bfca042c3f2990c5227ce204f610501fdfbbe609f7cec74d8f8f541f64ce75ab178c6c2a2213f21b5f468aa2acc36aca433512ffce5d19a128ef23b42610438c0040ecadf2c2e8f07162ae110799e65f44c5f31a8c524784f5881d3a97ce18a6d2017686811f808728142e44e2addd412ce872da5192c48815a3838fad491de3aada01491c21537851d811492c5a130b2d3548764fe4e76d07f5a6c9bba18aa112930224324a2a2c2c5a94e6830d2e2d184a0153df7f50d1c0bf25aa4c37e1287b5a65eb77786169002d7c1e3b4de4ee1cdae40b1a0eb2785b09e3a2770924118d6c5bf78d37af685fe596f496ccc1f8f44371764695f6d02df33f834add7b6ebb14dc118830abcfe11fb7330b44daa4edaa3c220378b97d3a07afe1280f468d4229bfbf631b18bcc700cd88dc662224b601a6f457624c5099db02732e89f9dffcc21f9ca1948df33a1433aeeb453e2f0a01e46e60a0cd1a34c17a64f5c6c58f95e8c97837dbc72df0896e58c596246a9188204180dd6674448c51f67aa6588459bca22fcd52ba4aa38ceefdaff89cbfa51f58fde45ea054829941315002edde1fa9668ce86dd517e0fab6b3740c6acba76d6d220b6f7889ffa4d0288b7487feef5342bb21ba0613cfc4fd1ef85294b22f6ff002a77c31cbc5522dd708e5c2af3b45e893e1c566ef0494d2a47883889cc872f1619ed6dda643698cfe0fc0dd8046cee41fec9db5b2d752a8d45f8d370dbfcbaaa794f8b665297d7cbed8726b3623c108b4cf026f28618ea9bc5bdf8fb50ca912c8b06c58e4d1a12d679be9ef770adbdb492c2a2ec30756e7ad3a6c8e8ae66024f7a7418031405332a9a0b603510fdcd42fc550da3c4fede7c0e47a344a256c026c15ef58114e6ee4ef921f328ee40004e409ec0ba9eb538ac0e3c2f3d5c148de220ae88d5d71cf9821fffbb41fe8faf93f7a23771209b7f97ea5c715cb85c994fc1ccc6c8ae4c3926a16acec4ee0b7b326befcb4a2728e25d1b22e48bd431d335a0de910774854ecbb7c66a5eb34731f43ec84956721c4e385b98644a2097033ee9d8bc509a98ed8e04ad81c172462e5948ab012a098498a349d06629e6f04c25227cb60b9d2487edb54a42d3694c53a3a8334f1871654ac78d13e419ee83cf900b4a62078281209621b67295d9b519a9a1567169f6e3673d5d527daa8decd3a7f08e11298ab6f0335c0c7bf2dd9c5340971218a8fa191c6c470a4335aec1b8654491e71df799009e2581f9716d7d7b8c9e131f4205c9320046d9c7413f2b655fd857a041d4f5e538f948dbccb56ff2f7ea0daa6f4ce702d4849810bef133bda07a2b7d10889f860447d893bfa7cd963e711ab4031d4228060ae4d7bcd3934824a6484c6925d38bb30275d7a08218df52426cf014c4e5bb0e10ec6b5d8bea1de0abef00a2b48399319f54dc8f2010908c49ee50632776bfefef1579b7b13c1aabdcdaddf1304a5ebf5bd6cba74c346f2e16d6bd2df47e6c4726c1b2fb38d5db6ad1d8af3a445950dec8b73a91487b4aaf64b9fa85203bf3fb46377496648ca36e4db4e457995cc06f2d2a0bf3a5e28405c5344ccd375e3898a530fe230485db8bf384b11e37d9267d2205d8630313b156a522618d480587ffd9c639fc18a2bfbbbc310b7d41785bcb49e0b304090d94f0c4199ad19bf371a220125d74c0c5b0e281a798aa57f0cc5e3ef6c8d3b22cbb3959e317e9d471f2e9a15ef206d3387782799fb7a316154dbea52cf6e6fbe7ffb60b91478c06aa4c05a91948516c7a46929c23b6c7e7525b11dcad83ac3097294c9ba1f20d4987d83f8abf49023ee9c7f5e03f9bb455bb7cadc780659b13288a060d8d496f4bb20aad9abae943145cad15473b1ebaf8bea5fcef89e4d4afaa16784f857fecd9a4cc1435743078420f5b5233c7282856786b10616b9244c7b998dcd1c324f79c410c3a2fa913c08f24bb2a0b6125ebb789a141776d2a6f9a4d7d34f8c952a0456552a2b1c3cb6cbdf85ea41304f7107dd2dcdff0ba0cc1636a853a2054d6bd18dc68277ea0dcc6d1191f7691852671d74879e0bfc3dea5eabf9ea682a5a1afbba43dc5595f8cc5acefbd65d1f3b9937b98eca1043fe68f748f90c82640343b9f0e7b20dc12087be4561e2a9b834765ca55d5bb67347588a74a6f168bd543ac0d722dedba9d175fde065005e7776b569a887e8bc53e64d6acac6a621f3c151f72d0b58fba9fb6b5a778bf62b7ded6f198e1e0cc62b9e101ffb5e404fc69a2feb9e059a9cec03a15aa59ff6aeef86d2aa8318e4b4e18ad58e2cec0ab6b08d31f17e0550cd22dbcf09a81cd528efb65db408ee9f8e52b0987b4dcfea0b0e23646198160d53ec948ce89d157f18c9d6a24960db69a733f748ee6b5907a1e9169a53debd9f985b4e2e3911818709846b5d3499f353f9317fb8894caf397959c99c4312ac9f5758a200814128bc117be99d8dde2c06c25866d7177d50eb71274577367f2367fd5b7cb5f7d00abbb28ea6abe9ce7e3dcc0f491c97ffc8d7b06fb7fdc553e9a80f05e09bc0c8ad6e2077e77542b1b0b7c117b368200ec44f256f50fa5a9726394f929f71014c72b55c50e1aecc1caffdd4ef87d84162275938a511fd305e8b570750216ad3580a23b5b1ad1ec3592803418f8870cbb665382a757937d8f8cb71690090fa18855c49df989b49d72bb3c4b4fe4586c2297deaa312215793792351adcae7545cc18ff55bfaab0dbba7d967f981a181fd3a94bce880905cb87960d56e98e59d995e72cf55606bbf850677157882d0986c8f8d73aa9f76c49fec909df3c0978bc96319cad3561e59781da0861568e11e913912024174caf241a150722665bee997e544d0ab4d2275521eafcba21a9f56c340450ec6c3dfeec6f7bd89330a9bb40bb04ea31fdffd2e8cf4cd4e995b855a85a52f1060ef69f5821f7885f9b1e0a1e8f84d27ea2a5e5214d6faf64aec3aa9c06fe0ee0e017cbb2ef6d1d6ec82d66875dd0d73252efaa84859a2c2e6101c1d918e9c7ec4b16d2048e365a6b35387d336f16e7cd952589709c236a670ed77731e08304efabe9eeb3b646f308b9080924c87c18ebf590bd9edd985239b46c52f26e522f4ccec62c9e7a21aa8d54ce7ed0279495a3b144b0e07b2fd31c492cde7a89fea16e609c4dc0e2d7a544c895a55a661c67ab2929ddb3dbf45d2a96f1ae0208252c2eaa4d36aff5887c3a1ae861c36307cda1ebd83b5590cf770e2d36c5ca390f807fa09470305bc6f82e5a6d879142f32f2b5574f3848841c0f70ee1ebd6de3d13450cf259992dc81a4bf8b3b39603b1dc591c158c4171562587cfb0538e60c094dbcee6e1ce9d581322cfd2e5b1de41bad2186bad33c94de25ab84c9f538f6921ea2d72415e163f9f9765b975f7877ec12c1e00a9c310df351d6723106c098fbdf45af423ede257ddd2093e8ee53f3d93d56a5666b420e5e67749f7f42cffca2000b6b3f97f7dbda16f4874175616eca94cd762dc4309268fe2735a2cd7303f3dbc10078acf6452a0e90103956e3bb9c8ac78a01c02ab8ce8988eaec858c80346ae60b298c010973279ec7f6054408424ddc419a87542dd4694380864d16195028b43f6a007632f87b00c29cbd81dc97714d6b14126e18ac9ef2e133fbe2e5ce6aebabed2a2fd4f0f7710e500333741d0b8cff3d6d6b2ded0eaec71c2da47aa643a042d7e5a4aa6a94e11b46019f3a3cda0cf5601b2d4a8d050645035c5346de2b35a72507aef7bf147a0c7674049af434973718754c9056301a0e20d3078721d418f64e9234103a0133d20f4c44e1dfb29f9a1473ce5ea9391a1934640017a55992d93c1ce8b200d58ddee93c4e074efa0630c8cc632195437827215a2fad5f61bc56975672947eb671a38a62630b062325f3dd9e50433342cb776c448c099f31c873fd3f6a75948377212fd084ea11f1a266e6a8fe2147d0527b7e8743d133c74af33361137d6a741be658c278df8a6761d93156b8f2d4c31b14afeb230af24b2b69dff7fd298e631cb49b5eda5cd90992aa2b857f15e202c476b24d74c2738c3fb8edd6604b79b2a0f0e7fe8b98493a3a5a121e2fb17636b55dd23f6fff44889391892fb0f7286044d28615f8bce67426ef1e61efa24a5ee03753bb8d7b46b9d3ae3231322b56f36dc2df1f4aa724b0d547a20255bab31e6ba1dd6dfa458b80384f05a413273571f8893d16644bb9be158cabc1dc8882b5a48e71576ea1527808a23510c7197a807ff4353c19ea63af93d54714478a692c8ea099f6947ae59079a9388d1ba6478cc9840b3afe23d726b02142d3aba62ac10f91cf87c002536e66375525f5d466ce1684efbb59e3399fce3cf872954298d79a8a88095dfcef29623ac4de7ac5c93e7c879108ed2dc57bc82ee97bd9d188018e980010f546dd0acdd7f1663cfd66c33264e70c5a987ef62c2216dcec43ad34455bfb7aa2708bc4d0a5651bac35404d96d5a746ec300af237d891cd3cfcdd05a9816b1e8fc539b09913c16f1a05c56b6aafbda655dfa87851c764bf3a25ab1b555d1c61d72012b16a115b6104db8ce064e4f0eb92d842aa8d4b85983dc07ad7f8f60a0a803cc9b8175612b67816f97ece6af005e90e7105a77438ba8dceaf4faa38dc86c1a0cdd4154af6541b6687268c63d88255572898741eb8d56925200b4286ce5ff980b6184e343e6eee40b31220a3e1849dd3a2766cc668cbd9caf89ab50405bf57dce304c18c35af522a70d7e55028d6523aca825710ab63efc7376938b86b4aa3915e7b99fdb4b84e94f22d65466e38fd23c43489d56e5f31b4577e263f417975ae98a66389b946d4a50746d012be01a391bde59a2434e5a16cd7658ab4f78de9af0c91e9a21068b35c3332de3ce9b171cf605369d90561350efcd70a936e85187313a70e647363e618bb7c8f80f5aece7a775cb5a87648f24a1c51cffa7656c58daf0de1fe2873ea6b8ceef18c8ca4dc3d034a9759368040ed0896c5728609f75451857da6c16d0c4a03e6c1c901d3089d25333ef8415494e743a9df044ca9532d944dfe1f8d36bedec1de4de0214db9ab00296d76c701fae13b1154d01b15c851e4972ea25274f22f48cfb36a0d6e1c932bbef9dbd84ae6e3d1cb2c544f27a129e0407c42c770ee89bfa7a3aad3ffa1b04897a422c2cf4b9f1ad62cd49c25285b38fa7632e7930401a7ba7b377586887ac7d335f4f04669b7df4a30cb05e2ef377966ac34198c14505c7f50d35b2a3ab39cc7c677c1936893f51fb7ee8fba3414769fde1b836a7a01dd633b2a372cc80370031a39eed914ef9632d4d4da79ff06a0069e0516c431655adf7a03f3a386a39f9ceb1462d5cd553445d1ad711e09cd4c9b9c7c73814092dbdc3ed7241226e669e5bae7924ccde004555fc876f5a212d22b7a293f20e76a150e7ae8a3c4395ad0385049565402a273b1afc25c5974afcd5552109378733827cbd7594489aad213acffd5dc3ab85824200a9c4a7a8f9ad425e0e449e734d957255033cf614af55c8c48df2ee550f279835e7c76af8cb3e1614b50eda11edad982dfaddc2a202e889c960eb164ac40ff49ec57fc9d059d6977ea638ef12a07447cf2768001de7a5a70c8bded87e321537fa1a5559caf8dba3dea1aa25437d15e333d041e53d1a48b1fd0f0de4a3b8ab86389bb2b663bed382e13c8edc5b74aae2f1365fe581226cd540b88826eac993a9c1d5b06a1e0da034896f0896f535728c518a94fda1903171d846e9df268500c7fd286407acd9e06a6832fc86c601a85399df6aa812a7fc9687d03b79d0f48c9181850be3bd465642af26fb5a02a8520da775540da5f59a0345ab6ff1aa604aa464e1e64b306f770cc267bbc965a9b5630c2e2fe5074d2334322729d5f27a97b63f35dd34ba2d0615a44705ea240437af3d99a4122848806af3f8c42c4a476df6dde2c4fbf6447ad6fcb212054897e70052e0889ac0ed3067a10c6d05d07d27756341f1800fc59eca85ba6f32b49c399e535fc9da977cda8524bc6fddbf0f42ac23d706fc9112075584044ab1a2883e9a1eee5c1dd21a6e579f5f38a44ca582efd9dd0c90fb919a1e7a045a4dd0ec0dfce16374056a45701c8d1c8ffdce3e773c0f0c5a0b95ab2da264d0a16a9869eac5b6a9272855396367399305dd755453ae2c47c1520053d0fb62934e46fb9516525a3d6875340f0f5cf22b227052a8f2f0b36352ce761a451b702a805950b4600d9301ad8dba9e2c17d3d94675d4bd7d8ab9e43fd612afb8ad31f1e89c3a948bad3a5239728682062e812c29afc222d69425956dbb084c405c675d8c98a13aa8daf88f5a09cc9841c8fcc5ee9ba4371c48038c17046ade1a915a40d819a503ee76771350e060974964334b65b3fa431f61cdb052aca16a48b3e9b359fd59fdeb4551163f930201b3cdbc617b0ecca51b46255cb53e80ceb608cdea2d99212ff98bbd486f1b204dbe6d9290bdf7de724b2953922916099f080e0941a8ad5d3ac897ddea332cea774519021b0164a30ce10a620f08cad684fa5d44362d6af4a6b1b350b30a7b9cad349d538e109fa233ce73ce85ba70789eb523cf1e758a4cbf38242e17809497af00382286d45c3eb96f88f4ebea90b8bcb9785d9a11f658d3d18ab0f753bf39a4e4e7109fbe99a4792c203e9d9649154d9ebf0c0aa4618bf069d7a7e94f109f564bf8147d809aaa9a0e3596c292604a9eb3559467cf84be6c05e5c2b97eae27d98a4915491ec72f932a709ee3bcf3acb71b0ee3178a4f3338570e9f72a4f874a3890f9f6c383b75c2271cce4e7ff814438c2f973cffea0b588c2fd3cc17fcd2c9e13964985a0684dade83bd1a0e00970c88405f2300fec59f3d7dc603c0b960a0b64f0f80c7007407a063b464c8d19bcd8c0c32b80decd590a10b123f87c3ed26470ecfa03693a3db81bd1ad8131b6ec3b3d6b29fdaae83bd1afe613fcf35b057c3e16653c363e048ecd5f03954c3b986cbd470af86c3e038b8ca5e0dbf61c31bda7069c34657c36bd4e85c6ee07cb91eb8c647cb70cea365b88f7adc5761e0869e1d00dc771179f618eea342cf0ec30d11ff05eea34f9ebde3be2034eab0c754f8c448fcbe73375a5059adb438e2f76970375a68a1854a884f6cc4efbbc0dda8845443aaa129387c62d4efb7c0dd4cc199923325070a9fe017bfffc2dda8a00405a9a2f0097af1fb2edc8d2a8a4aa5524d29c2279885df9fc1dd4c29326567ca8eea099f6095df377137aa272a2015d014227c8258f8fd12773385888e4e153ec12bfcbe0cee4655650a6a0a4a45854fd00abf1f83bb515151ad542b2950f804877e1f067723058a94202941387c8255f87d1277a3c2c9c95119e113a4c2efb770372a232a1e158f109fe0147e9f85bb91223434d4844f50e8f757b81b294d7c7ca43ce11394c2ef57ee46ca13294052805444f804a3f0fb23ee464544a5a3d271c2270885df177137529cfcfc44e1135cfdfec6dd4889a25249a1c227d8c5ef6bdc8d142a52565256aa227c824ff8fd8cbb5115d9d9b99152854f908bdfc7b81b29372ad4ee80bfbfdb8d0ab538f679f014478e4815f364ebba288dcd832d69356775e7344ea3d8631a7952ec3da9b3fb600a95ad32a12c5bb197ad20afba7956cd4353dd3a34056b28aa69342afc35ea5993ac51619a6b09cbb05f947ed96af5346569c14d6491fa65abd4f3969229020eb1220f4664bb34ac7fedccdb0e21ccb15f74e68f0ef9a343f83f228c30c7fab538d6658c94616c0e64bc5b0038276dde27193357caa78dde907dd2eeedd8fbc21e8ca72d1dc9289f209cd1e1ded2c8b8b3a34b233bdaed72db5122dda5e9beaba3df1e638cf161203f29a58c9df6a4d7e1eefef87cb65b583da32dd32e23b5a2f7f4af5db2cb8e7b911d658f7deb11b2dd1d302f74cc9cd370a185179719a6928c18bbcca41696953a126d5a865dd49a323643981762442d486c8dcc3d5b76ec6ec84bc42fc61897243549e11036d0b3811e2759ac45888979987fe1bbc97d36b5e6fc69b8f0d9a535d7c2bf7c9669cdb97ca669cdcdf86cd39a3395647c56595a585a483062c8309966b8bcb8e082d61c7deb33d79a9bf2b34e7b2183c9608001408c2808bb5d7d39bd95599fb1b6a4524a9f523afb177d4e1863b0ab2076074b0aeaa9b6043df4ac3716160c5446b2f3702508211005791ec21ea804a6760581601054c1d5c3a187be0259761e96badbee6341fda4c104cbcd8af360ec05b5ba63ae65a06e0e7787c87bb4bbb5380f8658d4eaa3ee966359eedb940ecf8b315c4669143ddf920c2e0667a3913496968fc405995f77d4f176535d081a5f7cf1c55716afce8357b81dd883eb7b801fbc079f6af7c1afaef5a8579ad4a26e69181d647e7516cf960382c32106157ef56f7daaefc01e4fd3ec0201a3fb74446fe9be1917c595110f107c6adf6e7930953a047cf810e89860203e34cdcc0c8b43df01a7a65958bc679979161dcfc2d332d08605f53af8e42c3c4d23c4a723053f4382875ebbcf8319284187f09783cf52e18b381a8b03fa1e809b99813de83a8008428afaad0cad0cd15d19fa56861efac0271e331d13ecd075f0691d7a0dbe4e23a3e1b2ce4d17709d44fdf8d75978f8b4751ffc85bcbe1dcb11f6200bcf0ecbc72caafb1c32827a88fa76a0a19f958f2a15844ae5f800a5dcfb74709f535aeb66bedcf4dda2c60941e306d97d9dcb3a7cbfe1efd94d00fe15643eed5cb663e16139c232709aa03a0bcfc39d25d84cba4819049fe4430f52e5a1439e05421011498458782091202888c00f1e792863b3f0b01ce113a4f2307e84db7df577158428676b5a824445c04983033a3bf8b4b4b4b4b4e81ce9009277f2a45f263750f22d90c529b400aa6bda77ebd10eb9205c10ec512171dfcc935c089f583aa65ffd181e8383c12db7240fc225071d2ccccee23c18a256ba8faeb8d5a146dd57e2d9340baab68e47ebba92e532bca5a5a5c56b107535f8f898440ebde470eb21eae00a3e899bf9164e05a9b0c7aef2a13e5b8aa71687dfc1cca37bd35c1ee333e68cb5742e3ac4166fe97cb0e7d3342cfed1161f1f551d5151a9761f7d611a193eeabe5af227759f3f4bf7c1c0f096eebbf10c3b201d1019ddb60c4328bfc55b38fa30bb82c46fe9fca77eebe3639f655b5a4e2d0ea585a5abbbd2e3b01b20f9975f2637d0c02b606bae8e888bc54533a209ea7e74243ab538974ad4ca765e44f28cd4b9e0b04ff29b7d52e79201254ff2210fb9cfebb3780b89cb586be1ea8af3e021cfc205b1c205915f3b480398631f87cd3ffa55de44ddd6d5a75957ba3c5ed2b707b4631d1197778fabdb1aca45e81ad36c4d8d4e0fc18a600b46083014c0cc09da3382d8d3820fcc2864bb343de79c332dd3314ed91d504a29e5d618638c147e8ccb4df022ac9da46df0816f8fd966d3fd44145bc0f0edd866d37d0166e61a5a90b382194041429225bc05b33067e10acfccccaca448bbc6272549be9d548a02896f7f6926515cf1ed307c327d3bdd6cda9ba10346acb08344145b0839c18b2eac1a70e0d9815003283a9e7dd61e41a45164682002a634d39ee8eeee8662e8996de83144091f24806084091b80000353b021234d136bb2781a1a17562d91d88362890c0041b182246c7022078a1670e00914143418810d4dfc40e10322363c012445cf7fd5e7e5046ace396713487e4e27cd39a777d4e2b6c90b8cfc44fdc80795fe3269420652202139c1031b3cf9af7a1c8168039a17966f5a540819c05d0e13f99e3087518cc3453ef2e88094be1d12f9e83a1fbb2f88f5dd710ee3604028dab3b729640bf341201f8e2069826777a10e24025370890f285b0221a1121f7aeae752843d76813c1009e979b381a9291f954fe30346171d1e769fcb0d171e3f81f83fa00bb15f7810c2395df617c4cf1ff2069c017252c6196394d1677710406810d694fa51273f2b53b4c8e1c09845258f19d4e91b44044affe3eac11e07610fc3a696c2a8d5b9500a83f049087b417a34b52c8b2d2b76fd1e4d69fdb99cdd86ebbafcba86bc151a80f25be5d78a9ca077c9e18b40e97f4418bdce40b9d9690f37abd3d89ba154f9d4404a9e49d96953d4af32d1deddb20c7486981533ef1786755f7ba541857f03c332313af78ff6c8832b897a641acbad8dce8eb2a735912be3e8198965da77bbca5ed3ae8da8f1a3472e7ed38e09cbdbe2013a3b8469ee4196ad7f982fe6a366f2832a6ffa6502049dcfa4a6712e397c9b6b3ee4b36df3e85b1781f6ed663f9b1cf58c37d73ceb95f08b77b1346cbb815f7b083de3a8c31ba4a671dfcdfe0f20ed9a47d7ba0874746cb3d16e3ecc2d1f022e0ecb83c4dfc56179e47e601e5dc94fb183e417eb5c7468b7894e616f8b32cf5a9bf08b228c78cc335f2cebe035e14fc822786d8b8392184e2294b6c29c18744bd9393198c24c84dcbb2f2586a45ab3094fed2748a3b58c13466e40ee0eb9b17977e1a5830db8a027a76aecf1c820f4dddddd5d38816c7777979340b6bb0b77172e2fa481ceafefae14616e7721849099b9bb674e4fa14629a5e4292dcb9273ceb0b77bc3b22c6666ae1fe5755d18063129d2a864a7318b17b5a6bc2e783133b307e18f8d719165d92edc850b77b963c78edddddd0ad130425e926d94871af7ddf875bec1de42c83a1a6867f86af8f5de6f7f60af7d7d86cee58b0ff35147ce9ca72b8164908b115502bdc55c536a1a7e0ce861cba87ec2965c532bd056d2353ea5947618e782010c831083176d8b63ef20b13b0689fbc505f90ae73e89e2d67d16cc0be782812cab97474de3e0dbd0e93c9a07538edd02e23dcbf8b0290fc99000bbbb190a8139a6551a4413e83e8dd3b4fecb4723898d30c8f3a343d42d73cdda7618f25f37ed1a6273218a40fdc83511b6d96074db61c8c669392e4e769ff409371bfa244d8b1c40d883d50791df4e45124c6041124c60018fcfeccec0b26328848887dd37fd890a5f325f586b5c0775d86b4c35f3cd7db0880dd4619827956ef13c0e4db30c4b4b371f459e1d321b91529929b5a8640bf305e0e77977b53b3a4dcb7c9354f899c7d6185a0b9f3dae3c86613cf86b6102733000ad1f0ca746c0faf91f926a51bf6cb53b5e5c7672bbc9f190fad7c2e4f92686871d0d1e5641fd6ca09efda3c1f336502f9fad1607ffac280d9e7ac3ecb400fc4c29500fd9dbed66f0abb1cebaa597665efa0b7bd835c3271f43c09f01021ed3aec4160fcaa76f867d4a28404861f7cdf8581cecf248fda0d77d43775ac744e4140efc87f49e52878e9919cff3a791d2de62ec3e20405efaac703a8c3250f21f95011149d2418019193c19e74aa695ed66d7fb2af0f313f2f3a33d8fd8bcfa475b4aa5ca71f64587129301311919c79ff7924e924b3a3f6a7d259bab88c75c72920032c8bc7419bee222cf64985eaebcbc5ed2a62179a9fb68bce532ba8f06edbe52f518dd5761741f7d526775b476bb32a34ed46d5dd67da5b78428e2296f3696104ef1ce0afa401e298510f2d67f453c14c27a4abbb56e4096d6134530666146b268f9746a59965b945a256fdec97a5c74a68e68c972cb5b9f9dec80ccfc2ee426487d455d6f08bb8570927420a5945df77e419a25dd018c3142ff11a19c3b219c703903bb31eeee6e8c717777799013e40a4debae664b63b7a496ecee97393b1a2508bda2b647ed7757fb9d12a59452d26fc618a3f6d2c994f39b9ecd9edddd562f8d137643c4d8d1be80f409e4a32f27a5b62fe9639c73ce0e08e963f784e99d6596f715b5bdfb66ebc7dd8ddff4287badedbe281bb21532b9c5e810420823a76d3a6cea6147bbb0a6406b408b94b250a16b33a5af511e3e5791a40acc43ed99717e67a0067860e0cae258b83852ffbd30f141cf7fa59712d3dd5162cf79896c77bb5140b220ba03b9f2a9dc284843c63b809179999b00a5d4255012dc9d51c8c501bf2389d4d2bd4a389a4a17855b54e811e6b706be94fe441965942883a880224629a594b28b714a9451ce20ca29314a29a594314e81b0a7896c6120f77a8f965909996029e147ee0eeba24b841e2bc3be80a5e1b14678864d2a74ad69e8b5437a9768b03be28ec5302cebd28bc35c2bbb322420249a518deea0716a9aa6398499a7f58dd84519407b4027fc6c976832e6932d0c843eb18b66daf6c230de343e504aadcbba2e78a3690e21ccb26ccb36c7a4686c8948e380642fa81af5e1072139ef0308f6d87dc02a10e5c30f0e9852bffd9608cf7ec11d4b62affae88c421dfa2df771f9d6d5a53f609c6ffc1c22101faa920e46a5b32c55bf057a1f7cf8814f16631876ed4208af6b23add77242ecb5fb6486df1a7e53bfd8e5cdb85772af54628a49513b6083fa2d10ecc0b3f329f31d2b79a01aa76d3f04f940a4926afd218a0f4ea8dc0f391e900395806a0a2a0c0fc920e541132a2fbf4ca8187a1de8fc326952e4bf4dc9b3b7fc326952e59724d181d07fd98a890ea868280d870a13a8960a13043d0dac592a749070e0c8fb2f1544763ef5453a76c72c76ec8e2ea765591f091669d92393f01c79964dbce4618f57ee5ad0a0a11a72c18529392db410f4f2a252b9b84cd9993143056432e9944a53503264a85631624809820123874452f1b4b40cb1b0f8acac4801aa55a5331afd8844aa6d93b2d2b49dccf779f08d0af598ef2f5379f620f6b83bca934a1e699048eb0cdaa94ad58ffed0209aa24da80fd07cc21e4f209e79047b8201614fb09f0c95e1602b4c08ab82ad30a11fd42f5b6d93ff66b6fa32958a4a06250b8a0269c084b29edd91a2a95456a5755a867594344f122cc51e3bcc4ccbd24bd9614bb026cfd79565d3fbaa12240b3d20e8e2bfa993055151e488ffa691a167a7d6cc8658c9f3aa69da5d181089e8cea5016d81d8693c2db3d34360944fbaa08735944f2fbf34d33f19f4a2b3ab9651c9156aa2fae57bc5a7657a3534b3d510711af95594916a8fde824ed34c8f2de8ac0cb5a02323a5f3ed327687133e4117e91a7bd96ad5054916703fdf2f35a90c49c693ed745139460f17b5aa5e50ddf79253e10baa65da4d317abe63f4c4501263c90bea7bd179417def7c5f355cb7a0f42d83fc3252bf52500d2fcc2e7dbf75124b33b54fd6e2e80c28947c77ac0b8df29a3729d67f62a6652483b08327a492c378333ae4cf57191e6e2f6cdd270327da917f7dc9283a6175738c3cd37cb7edbb4abf6305d240bbdd1cdf0a847bbad6632d621913da29f3f54ffefe4743c6c8ca28851c5d1c2f73a62dcbc984d382dd677a083b8b2b790ff75a66ca4fea7743eb8125471707741333154100b8f05cf3c578f68534b47fb2fb2734596f69e8ee9fe0b339be87ad895d944c7a0b55214459410294b8424215d9930508e0e888234649d06a8514d0c08b238636a0841a6436e8eeeea6ce04084a8ee083e7233851da816384500aa8b8821450300db22a4ec0237cc0250ac108499e00bf4c8cf0f37599b00e708413d02d875ed496599edd47aa8c44f6899e5ef1434c1104600e2ec2cf6f15bf4c8a90f319dc604787503fa844160105b908d9f26902f37fd417b2b834be70d4ed972d4cc718e79cd1212744c7894219a9a81c88d384ba8a2ba7615d50ad1f227eeaf3dfd64abf30ef516a3efa310e01bf7db389bb637fdaf4ed824a7f88f89eb73b72581ceded324b830028d35e2ac2b7b787c3e2e8de95b6dd304a24d91041e85b7e6130878cd19252bac509d13ca7e5cb59bb5f098866b1538b07c3dc9899e19a41dd08841996cc1d23b528b5442ca8ad335a41164afc94cc3342acaeb0c4124574a2a802144fd8a2094b38b0a40b9c1a4ef2ec5e161c78f61b310b0cc8234643c8a290c2872a4421892328232fa06206221c906c20da8002bc78820a23046105475e20031b8c7c388a2a70a0facf610c1e568103a0e71a58c3c3d2b47c022941afe50497f3b545210b24bcd4345e9780be4091ba0f088ce7c597c9959ea7c002174928a1022c70208b1aee7906f2e259722fa522f5f34a5c1968e8674a4a2948defc5f091e916551c4224b6812638cd7bffc11404fa2f091520a4b2f573081820e57d26ae85b7e99ac9af05f75c9021ae2057e708bebf9599ed56797b67cfa0a40137be20c1a0c5d194b35f6bce75efac901ba0846ae06f6a044b544c59ff6dc4ba5cd04f513b9749e2356a9c676da94d21863ecb5bad336cd3311c76f756bfba29168f4d1ef1a45dfa21c460f7930e4ff2cc0ef9243ff57c493fa45dc47bbafc77f40665ec3344d8bf9666e1e967924b5bdb3dc5a3dad955e940703a51e9ae0970911509ff576133fce7c7b84bc318a5c72e86f6f917713ebc993ea22cd454f5ee4a37681e65692b75cf48221087dfd91672b239616d1b6b5888b2f638baa6ddd1b075fb4f1d0dd967f22dfdce234cbb32d0791d52ca38b3abf91affc1879dd1d2c428c44f045dac683c86b7591a83a0f66e1f64523dfbc723f462e726cd34173915f5b0ea32e8ba15de031ff4a7c84c708153efbc8254ae2c89c4cc4c19719d81d42c0d5631b0fed22cf62b8c549195b0eeda2ee853dd16ccd4525cbea6a48c2a74fe44af8f443e496b3c79e6bd301bea0c96b3c686e6d39885cf463390cdac25b1ef396ff34cdd6c41f5e1c968c2deac8a7c7d86ee007a289b8f872d361f423986f2c56bf5bfc32c102e7b3199a0c2eea07df459341a54e7fd16470517f2c7b41e0ff58f6bcd9821491d81335b3e8b3c088638b925a60b82d445164cf72fe8dd3b8af85ff9887a7f4c51eff0199f90a23f20a742734a073819963beb46566128c4921246166d931c6ebbafc66fff2219f90872eaf4939cb7783431e5aed53486f206b22a0137a6a32979e04ac71e2019a4bd73a9718563544c4b0aaa1ed019965cff27e990881c815557e995ca1f3387e990421e8ebcad2cc28d979c28a12407185504d3bdc74b062f4c93186617eb38f51ebe23ee89281ca2408409fb176f190672e4847879cc57d423eee4c2f314511233fa48cf6e4645996912ca7dacfcc91a1426834825e9667d61529a5945a975bd6454b108369f99cdd5a1d8dc889414e0c7e7a2cc481381b848401edc38c6c723d64507dc867933b62b9dcd06f393cc21e3b101cc4616fe63c6f0ae01a0bb5a47e1bf43d18c0a7e8f083934adf78437b4d6fb007b1a8b0fb6edc804dd01eb8c642594bea4791f8583d73073aabf9c551b6ba89f8eebecba9674a98bbdbf22c1d683c16bdd7288d3a5fafd955981f7e521d686450ae00e6c01dcf9b5999e20946547cda299e5064a7e8810f5779f67cf06e6ef46603c4a85ea2b8c327283ac2a7e840348d905ee930de898c843dd61109198239a53a82a29c2892395214b76e72a597dca7f111d6d1a87763865215d3b0aa65d829a55d7256b1c7f222415515611e3ec1a8c3479e91340d10dc84fa6945d8d3616f873dcee6c38e8d2021b14ed3d81059070868a454eb2a4a10acc234cb2b9d8df24dd2bae31d66c6aa134a563333423aac09831a954ccd87a6f90ed13838c41e6b9ce6cd7d7005b79e536bad8343b1f419a7fd075750e8494abb4a31ce0e0e355cfde48e70085679fee06a87e00a0ac9e4c02128f404ae646b2748611e4bd69cb3c26f9a45abf418295ed432c1f6cc39e79cdd55803fb4c70eac380899b3833810d57eb303dc81fec11e98042a814df824f2cdd9a1133e69ce4ec4d611c11e6a13b1b305511dc461cfa2122a69d267dc078bb02f10a5b5964aee9e4767745ccd3d3d5970d1a1fd47370389440a7b8a50b97d422008a67243e3815a390d44101668e661dc2eb09cad46398d45b4b36a505d72b87ce4a38bfa475fce3967e5d13ce44aa47df9ee55fabe6610f994d2ce45879f9d4b0ed32db73a1fecb50f79d955d3d200f1d1327c32df6e035796875c895dca628105976a53d46f817a448dce4088f41d22177bd91b42d8ddd6875cf5a169dc811cc88170d8a187463d528b5ace56c73ca036638c1d638cf1c5dddddd0323ba300d47969c51528168ff66a6e002c9145b34f1196b70bb6106f6d806c37cb9cc23c7391ce1e663d4a954250837ab1be6350ef59136157b66061d1ae9c5e27145a6ce30354ecce911799fb5c43840355d654fda50f68644f6606926c0e25446848c4abf5cd2605d6499863f2bd3401a6cbb3ca31d99b05c729165a477dfc02e6daa26235b912dcc47d79ba156b3bbe73164c8f0a765228434a7521974d9c08307162c257c12d23a0e334484904b4814b414b5a68cd49a32461965a421a81ffcc1611ad864a8dd09111ad804cb28699a4e22a4a5483e2f3f16d0df08fa9fd87df11ba29325c9b2dd8dbb4ba306297777c718a394526e36ddcd39a76559164bd9cdd775611886655acc322d6a9a46276977db36b96d36dd6d1b0ef047a3f6d166d39ded6ead7565656565ce492a31330b0b4b4b4b4b0b4bd9cd5f04b2a0e7ee07c908a604a604a604a604a604a604a604a604a604a604a604a6048604764476f9d41d2d959f618a258656dd532cb1f3dd8d051675bb0fe661e03a94c0741c24d6db5d0e4853cadcddfd52f3d1e9d327e6ec33ddd575f0c0a25fb134e7f48f4eb7669c73cef982e239e98c73ce39e79cd37956f6a64f2cd49d9f9eed340d75daed55c3fd40b68cb0c1f3bd560b40f58b59b2248bfac5ecc4ec70cc8e8c5912658d314649aac948fa17e324e627e6898c810223859705e78cce0f3b29a7f3420b5b38da4b45e533099986aa407a59ec9554472fb054daae9333422815fa5e3ca89f2995c55592dae2fc2dce834d4ef824bb162799f88733d3924a79a029ca34a55665a26299844c43a62a22d48b7066e06c5596db8636a16d654ab9388ccfce1b6d2c33945a3a742c67d147fed5917f259791efc3b0ae7861d7d5b1744d6090565a628c71976bb0341be7406a9a84a8dd91c5ec50611ae6e1814294c7b4a47edb8af9c72f23358a8af06934bb6d25cad93132121d11211135f1a224b4098e22a1477a1bda1dcbc38694d5ed80c38c29d4cf9492aaa85da92aa59453d39030d093275090f8d0e387ce39e7b68a305a4fd3684e37df2d4679a8053db4bcd082d8839aca946a9a6dd5db6a73918b3a22b41e8df3b1310144cb90a046e1a8642963ec8112b323e3e9b43bf88e37538e34b5b49c35f960cc2cddc7f4c404f4bc096d4296b42ccb47d6d57da69f51e44c4018775c4363a765da637c14ea6acb76003509f470d56c5d0f313114a0790f9a6fcbc48e09cd371b970c106142f3d8e948962ba5947086e8a3c0478f373112b889b989f9d8b3e1001fe5794973f64a9921aa779afbc2418d2cc4a62a540ba8e21fd40905638e7c7b7b308786f2501e6a858f7bbe4d29934f6faa9e9e2e82aaf0b338a8a83fbb233349538aa63d632fea10d194e2d3c6ec34cd55d3c36885a5059a5c8208b2ad442822d86b17b2432cdaca981df6a694fac5ec04a1ae16b3136384e9113eb9ec64d1b42abb036e124a6c22f8342fb42cec0ef85a16f81443464946a934b7cee4d332eda22d9f72506bac9a46e475e7a90d9defcf464e8da1b781fa8e5ec33fdd896c7a88a248663366ec7ae6328fa8f065225121f46b07b5def87c16d4820b313b2eb8e0428c118ee3b8cd068bdd5781211a3b4da35d236a05c58bfbfc4b1617c45d0c4fcc8e115f8c0b34b46db509717731a922a71c6384bbd33624558343d5d484655ad4699fe9c9b7ff0c29d45511c96868974fa5b1dd307a25ec752ff9f6a91f44691ca7719b0d77fc64775f78e185176262622e8fd96c2e00ecc200030c306c3623cf6276b8c6ae0d1b366c6c36d8288618628861b3c1663cdc78606a4d798db20cb3a1ed66376edcb8b1d9ac380e0d078e15c7b1d9ac7866fa31f9c816126381859c525ad29212e2401c54109f32d34fc6ed9b52304c2918dcbea9898fe989090806676ac2999cb0d7261fd313f6804ca92a2a768d68c4ae116571ec1a5112f7694a3e202c0a6059b1228a925f2b9850e405f06b4514249e4e81832a6fe3778a1bc870b90c328c5c86cd66e470bbc19cbb19abaa394919a1426fe9ed18e3a600aed13aa066a7d6e594fba1ffc92025dfe5d866432b30f4d2a3a4429d4e2367779852d8d6c3d648a06db55fe71ff6d804c563184126151321a6c1d950254082f663e4db4d3f4dc335edd6e419dfbe55e193cb777488e2d3cf8c256a94945ad76545fd62760420852af4cd4501578dc8b755d350bad251a2313b232d1b69903f764c54b12b6eab6d856d5528cf56856db5496169f867c3c2b777d6d2d2d222426d43241289144da9fa6dab989d24d491b3638145cc8971c6196384bdf61823db4a84d2e1932542b1d7bfade8b6096d43229cb9096d55b621f65a8423cad9563aa84066ce47e78e7f1888bdf69f6c00da5545d5769ac68501d5a5019b6bae7516706142e49b6bbef9d6d44e1ba2712611a7ad1ebaf4981d944bec7280463c77313c0f1f7352bf989d989d181e3e6d35f97cab54a614ef7c9b52190fead7f2f3fd7c5af9f56c86466505a9b007e1aa65da356ee81baab8a850b5ad20aa019a6f1d44c928ff999c7c6f2b930f7b6d8240a83106b5514ca9e7ce94ca41fdf887f270d9495b3ecde5938b02ae1a53ea637e7dfea0fa6434da420c2ccdd66dcb74c793e9d02efee127042000013027c06683c5c844a89ff6a42ea4c29407aada45de9bb7b5ad8c30a5dae2a27ea694a989c9874f995f9e355bf16d4aad898aa5a13ccbd3836fd7361bbe6e50bf981d53aa6920ac91402dc3ae7152d5422a6dc24e7e848c3ccb86d4e47c4384b09ea90f75022bdc16b147a151d8eb6813f69a874ffd4d9bd48ff2b8c59952a614cb1939322152c2c4e61f4dd18e2743e998d0509008c4691a51676ac29b56336e68c7d5a4dec43c77fcc35e3bb4a242ffb6158f6ef1699923f5a33c94871ea14df844797826e5e149f93879da40df506247fa4ca928a626ecb5296572624a49a1c2ffb615cd68d828cfe268bfa8a85fcc4eea6715b3b33bb267a0a6d1509cf350d3d956dbaa69ae9aab665ba6860218e0001aa9868d1b38643c180010438e191962022080190670827981a6d341801d34703514c0000798325e99b68960906298a6d4b7bbcc694af1497ebba949926f37ad4c55986600a97693133e2d932534f1bdada0fc7f269fa06fa881ff4caaef14132588e03f93d0b7e9e7dba78cdb6a13e21347f16d83a7875c128ae00558b094cccccc90afb8023af3ce8033baa02cbbafab8899f1bbaa9f5429699aada9aa2ce839333db3298be2d9339fe7a52295f053f62495b09e6a77ecb30e41e06b3fec7d4e520fa35fbe352e56e7f280e8ed0d907e7516708201d1db67f765430fbdcb564d6381d8b934207a7b67aa96819e4d6a31152088f2cc2e412814ca082a4490c27996aba641a804eef555390bd18c00000010008314000028100a87c442c168381c28c2a40714800c7e9a48805e9c09d4248661ce206300000000000000000008cd80010165b96583d86b58cdfc65ef9543026fa581cb1604eb13cae7eff8c37ec8a13d9dbb2340ee80f351a6d75f32580bf13c809cdb1177b8fa75e66ae12ffbb81d4abc7a3eb17e548e731885bea8d6e42ff3417af385cc83c8850870d01d274356624fec380f1439c2b58517cf1e25a09cd9cecbc81dbb6dae7974cf8cf241da1b2559154b8a961270c26c7aec651d0c556b9adaf8a889b284584e2e44d071ad85eaa983d6f3efa6a0d7cd42fe84a14e55c2d90fcfbe267cf6c886aec3a25e0f81ceb5841503e35107e46834add319541f197c66b1a60f57284c7026eac10753c06b350133ceea95cc856486d5a8da48f9290e343d19d02dda456edb05a11bebbe7580fff1e036efbda08b9822ab099e394ebac0a5083f9887935e54007a35038cc4044b8060340b2411dc96b87d22904b636f39228c50e0236f9c630d5b1d21aa8a129fcd574d1ae4097e41703efefc9670f2d444f2ef7052d52e723f9a638e3bbb95649ef275418a3dbc5a45f00b2b475b1f39e816446e42f62c4d9639abfac54136581fd1f13a7b07e8eb4019f93feff2390e421ee90dffcfe2d2708eafdd4d9226c20538544c728938e3c2e5328b6cd9afaea2f2783364c57a1cc84993da0e721a9e52a93eff1974ae971f38bea953d3f1c9e81e11fe4bb186ca1eefad99d8f85df26b6b13a20ea5922b700dc40928b339c11ce338d7162cdcbfa286cd1392a1218a009f51035beb87a5ab406b685546f75f1d839d0bb3a438385c0c5c5d5bf04458a240107264801ea2be421df06980ee8050a3f818a72dff82009ef72998da8c235afd26bb2450d5279f5a72437e03b43ac0a7aeb41be4c343643bf114762361d30d5b6bc8e59796fff7c8b92e9c8badcb8abd6766a6792670af455891650eeebad94c5e349db4865872d9a50612da6d84df988865b0c27c663223b7c2eedbf22a19053a9a196108dca634249216c5c54e9da4a867272bb3b24ecbc04d9f51d2cfda546db13b560a4cb703f87bc7fba412d6d76b49c2f5b78784f8f179ee140ae6dc1a95e43181b33582211f26ce2a125c1135d335e808242ab506a8767eab14a80481c1fc35753327fae7c90ecff8fa39e9e40afd88229cbce03eed0b40b799c00057b44d2f689a9128a5b400244edc026c4e05440e025094c7cb3598c5bb635a967d455361a576b144314d1081db07e1e3ee26f7d188e85cae85c1c046dd04ec632551a1c0e3cf436cad7cfba0a885b8332fa789d327c8de392cf48af5230abd693f3eb939e0ec6e05eb85fb354740efd0ff01f779ed55e087ca52c9472aefd311693a5579a488e4ec402fcf037a5fd8e4159c78e2d24e4842f94d3dd878c055ae62d2043db5d8a7632e73cdab59cacfa269f0c140eab56335f250b85269e2f4fbda6773d134f8656803be958d4b0b5fdd3e38e8abf7f1e4159460ef78c21f856e879136bdb21e83a1a7ba92370e161662554904463d231073c98bfac2b3e620912c6343c6f28b4eac1c47ee34678f17605760e44024f7f2d905a48660d3f2909525584deb0019d01cfa18f1ce64072a0b53116783795b61b5e359ed4ab17a76d8509903f192984d0a419ec03436d4f86d631cd115616e0299d5f7c4246ec62a7faa3e6e4375a9a751e1900595f17bd7a4d07e46ce634910cd456f0a24f038ffed555435d4219bd4e9320de96741ce17b93e7494b87050d2f66a885075693f98d3f7b93a1d4910199c01d418657cd26b018f33f09f7332c49c2418d00581c9ab551aceba41177bc9034e8693c2319f75312c55c626a1266a9bc6f495c0c12822dc320d13d88f9d0c40e0f614bef376252d932bff258d5898db1030879bcb2dce8e0c5423372ce291b55334fb81d12a8b8d29e6f973d720fa47b1dc6a8f5157c8cbc0071ab7c6b73bbd1012bd0a5473f3a34e128c6a0f0160c1f4c9bb00e3a9fe7483c46373460b421aafc3849b63a05485578a262e4a94c730dd4df202df84b0e78bb3e2cb2b04b97e448768d5bae9673ee6676ecf9a1a6bd6d7d866c2692df3f56c4410188bab74ed9d548cc79a11d61bf9bee9ef225590772641eb2e884a0e0260e6643715ffb742dee6a5ba1196aa46b627efe18bd9a37c638d4c891d1db651ece6d3dc2d9a0adedfe79626ba23c7dc334447916a951e4d59a10d1060aaca3f7c4e8ad5413ef3bdbe75c0b84932a9a643ffeef8b2e28b185debe03199d457cd2ff562d2b459b2a610a1f63349e6b3432bc131493c898113d46535ec0170032350585afdfcce0ce019f7d96bb83ec59542c8a6b28c59b6112a0f6a4fafccd00c131dda2b5acbc8824a17dc20bbbb23bc46e247024e02a256ace3cd883489beebc328844f8fe153fe10070ef009d14c80cc7370729dc4e2d12bf648ba321da65f3d1ccd02edb3d034fb478ca9af3114d11e5eda07b2ce220c4e352921af55c66c73e51ac307b835ead9eb8de2b75643c3009fb9be685271f7398b9aef0e0b3306675861874831b95b78ffbf90ecf0d062dac3c0afae3b245b1f58771b2cb4e5b4a072318af38d144073ec0feb9104ccee2d97af89df3c322401191e0f12cb7f3cf8b4c06ef1583e43d26092a00be27e1af4de2ab7df9f928c8059a9a3ceee862fc9ef235684ed8b368889b8e8ea71a0997c23c135aa23d5ad2a9436da88b798a608baaf5ae952f11e26f9717b78fdb7613f940af82042931b47561efbe0de422eec65c882de17517ceb18255578320f2672338511d808a5291f32a460664079f2d0a309b270cc050728427b50a29bf9a215f23fb6af2640ed70260d7f2df8b8617a056a0485f9372f3504232bd5026eb60fd8fc87d1e35455cdfd0f8cce7b8a6fbff469de21844d799e6490dd91b54a51f3759d2353af09b9ae5c51a371cbc40c55ecf511630619c0d2ba74ba85f810fe7c260c5addb367a94a4cc66ab81bb214d4b7d44ef1899949d8c5821e92e8ba7a4c45944c547c96ffa59e779075a49c6d1a84acbdea791e308cdb5430789ecf786305c4d6160c751a5da34d987711306b6d0873841bbd85f7bafedba248464341d11681f253d19356c4b039a8a543c3083de73ff36cd226488fd0ac31fe093904351760f99db6e64cb1acd34c076e289b89090791701f5e8d3f0478af10863825b9af1e546d92a9e18fbe95fa2585f41c98b75ef4535821c290eb4b1080da90b667922e4e78b6e98f1e81c50f244f20829e6e167f07fa5138c98a78ddd581b1e5443321117d3aae22254c212442ecdce2e24d340406f21683845ca152a2464044c7c2bab61150009ae03c2180cc572f2905bb14b74b7ea6c280b9cb46834c50aceccfc7941c6d5dde83915f14e29770c8bce6a1ddab2df1db73fa11d4ff78b54dfc98c66c3967c2a2aa4515c5416af4809cf9d25e11c705747150ed1ecd2390d8c127b4821a5d8e239085ebd901ff8eb547012aec1257f2cb356c6ca6ac2f1eacdc265efeef68457e89239882d2639494844e150f2fe06f515bce684871ac0550691107736993a79f852b0bb9e7970309985f2611161f10e80dd09c259ff95f38cd9cbf7d38525bdc1d8038b088921cbb72b1acc384eea191a7984be30989c61b416e79fb98224ebc097fc069385a1c906c5cc06488f7cf935dc7ca15731917c4625a47b13e4b59eb7e943c95ddfa99f7ad02e01424ed6e1f8400fd0a3adae8695aa66b8a876036d9d92dfadcf4cbff01ddf989e038bee81feaa09fef6222cef1f1b26ffbe77fc505e7d9ce7be17b685d74edfa52e80268deae75caf221c86b32bacd6ded337fe86c004787b3ef28a6a21a90d9a2bc33b2ad4707823ceedfca68266c8be69d705fb107118c65bea15584e7381a9129637c5703d2fcd608e7cbe965b7425ae02c6bf59f09477eb480b68605545962570acf348113c1896f10525801dca71c5ec93899ef3683008dc570fe230ac706d59fe54d3085c26315b5d783ff09eb65d7032637b045947292b2e96c1fd3932820f79f3f101bf8649f6f62c6837973f987332bbd7430e945c0ad4065458a8415bd50eb81cad0b137e70c6bc61c331e3ebe4986453deb2f1642c510986aad25ddf8118b9ebee77a3cb98860758f7d70ab0f7f5ce76c9199cd98ab5f386ec34ec596b378ca667f9ac0b8e048998594d24733a8402c24020c951cbaac2a6b69e216e57e6cc2de40ca698c14d08c5ea454a82f22863f0baf5c89637325eda290b26244102d43def2514ac005e0583ebb248292f836e7d8d2ca03b9ccc22471455be36967a1cebacd4424e017bdd5f66a5b496668ab2544838868704a8948fe3861bb39028baffe382a0d7644ed285fab29dc353bab2590069972c27c56c56a8a6441e85962409f49abe85d628821867cb9533316961713cce0bc6559e9e975172d3011fe9e07c48baa14d938b9aa82731fd23f86a611e70d7208656e6e262188e8eab65d1f43f561cd9a426cf0b34654edeb575b121ee8e653e05c487edccff22cbca879d7ec7dc808c4a7088a9a2a55d4074cad335655eafa3dc9b91d34497d25a6ba0704dfb11d4d61c83ca510752784d201fc1d4ff3fa9c4e87fd53b51b4b0c8df33136e1ea91069c1fe9cc0d3b2f0a876f29188c23b08cf1ef26282187bf07470a8dd8f4b99487e1441b9c16f4929905451e48b7c23d4718678e895e8a1ccf7403a79a079e3ea2286682880e675a6497bf023aa114be48c37f764408e85c0e823315ac1d213703b0ad518369c855998fd3e82f9574c48b9514f4e6929148f723618193835afa13853d36e672db5bfa30c793d38c4d96ea791f40ad7fe7c732290b5e86d47cf589bf530b1c4142646ae6318ec33d86046179c7e131f7be54b3b82ed8923e59c1672b532069644ff181cf973b0ae41c93b63f13b117412b3805b1396138e0873a9bacbe5cc44ad7a768912df58c7f8f338252525a2c223c8dd7ff1e338a9b1ce2938aef15dec861f0a041443598b36046c2bccc1f98d1a25a7309e8eb94cfd2c93063e03cb846bd3a18866717a8c0e4557366392c5e5e6bcd106897fdc243151a9111d8e4d0e4a53041fa55d2da20d004832082e7c63e75ac9a5e99ca8042e921480b24f64d8dd5f8fdd8aec33b991e1c1736b0a6a842e3cdc6216c2c87354a16e18deb05a0d7b86c69978d04d3dcca492338181104f131afbfcf5bee0b5546671b0c8842c8f83380e3a7aee499c3bb2d60b77fdf7400764146ba7b8052999febce2f50d487ba1f6b9b5d03c0091288acc4fd356e5e0be6c494dad90346a4449655c92766cf0de83279b7b3e4fc9436af7bb58b3a41b558169d7bee77e9da7d1d67441b07e61e7f07b740bfb3a16fd8e8fb2ba60227ed14585ba6c9d0df9d416bcfb511573388e6fb2e6beab08ae6008ae039b73c0c098fdc37dcbea7b579493a8f156f1982e168c4f2af706c09c03fa4347af606879c5649c81c0bd0ebe1b8a808dce34309f95f2c4d4fe9a2799fa9114ecd73a967a68159947eb89321f1ac15657094c4589e03a95a1836c8befa1b85e0910e2f0d2bcbbe7f0085247d15f8b4683753982aeb68045816fdb25bafca3c262b64eebf66fd16ddcb8310f5d7f07a19dd75dc2567d7d791fb8c51a3eefc1f25d2c0bb77a2db63b5944df7261c75c813e6b3ab25c45e213e1e575486f4dfedc24a9b625fd0bb38908fc12721865b00f8001590235e7a72f7b08bb015305df5acb72c576f12d0d794e3da63143a9469304c1457763601b99f9730cdb701faa62ac6984e23d8a1a97d571e4c4a3a0d27fdb710525a88adf4bdf535dab4b15091c58dbab4325a99c42f1ca690f4f286bce1fad008f236720438485730bbb57448caaf4f96a5761882898c385abce381c25ab4a77c593de5de1c866f63bdfa7c9e608ffea9da94eba465cabd425afc3f232b1cb8aacc9b4e67226ba24a9ac897fac13234aae782afb7048e48e07f8004b6e139bdb638c58435789eee140aa733e22053307ead2afba26d43762d3beab142d20d9bc986b36623508ea72ac29313ba9068e586ca730346c23b772a7437bcb9479eda54024420245ec8e3bda9033e6e97a873635683bd98e699f431e2a0d737f67e5de2239b114b3ff8b939a87a7d8e2e68d52668cbe1ca83cca66c6def4dcb4899716467f99336471a96d6583b4c93b11646b143f3917f155849041ce1587ed718598b8b26130f93145e66dfc44b4e047c945b5d322dde19cd5a5c40bd93c0be745853e51ca2165e476bd00fce14af72bf90fe375082e25da3fe3a54b103fcb814cc027dab0d34e723c9893359511536e2fd6256b15e301b0f90474e42f100ac1d75eb42b606c4a009ba61b180960d93104682bb3222e2de2f4d85b3cab837387e4026fa2317bfd080f4f0af267517b11f84b8d5ff6cd75770c3698e628625e2d5a2114669eb95a051be14464182eade061d840a9ec31d4193e6180cad5284b69bf4bba2eed1489617a5aeabaff51162933cee16be7007e08f856ab687b7bb3cb0ceb0c3bd8b196ac4a4b14c4f49a361c1c722e018eca0bfa691f82b70c7ac7695f62e689208dda68949a862044e3d19e822822308c6299b402e061350afc13dd5df78790fa243302535debce105ba5c096081513573ea58179c6f923372fdb4066c6ca0ca03bf8b818bf5bb8453b956aea51aebdd7b0c06de9817e4de052ca6ec1cafda7483354d655ba5e9785f99ecd55117e9a7a409378952dcc807db7d43822b2ea72003f53b6905ef4ec7d4d204484a42c6a1310d0634555c2806c57c0279f79e65cbedac9a3ab3b8ab80ce39e6cc785b2dd5b94e93cd331844b8402cdbd7bfcd47de6aeae19e208955f908948d2b267a051c5d95d7dae88642b3210833b466309d4d0185d751bc5902d11ed5030a90be5c5608d46225aa495169da2baf31de9ace0081eb9f3b65d31e27a00e6a40fa27c6f05cb008917c417f82e5f9957a3c15d3159abecadc097afab42bd29f137b7b34f2ffd81a4e7201d2fc252f3d81d7423df2a289f915e68e28de804b8f320b1b3da2c08908a2bc656e88202081332b547a5d159337d6371b9046d74e036fa48149373390ff8309fdf51cc0844eccde0fd633214d60909c3c5b462cb6483986b3583e1ca6052d763d19ee9796edae8eb4b00583aed0dd4f24c98c87a94dfeb4b6a5ce678437877a911d2fdf889e8bb0c24c7bd13b62d35b601040e0afbb7d7fe0fd52bec7a7b58bee21f187831bbddfcb5e2f734d57d8a40e047088b388f10b2d7c6421df3ade445194a9675d4e812b03d3d5395e5b1b134de0b10cbf3de3d816e16984acb281ae03fddc10a1b845068797cbf789ac2467a9822916bcc6c09c59f5f272dc2afa7efad59740339962cbb6f6bd38988f541df8f8a792201d17cc855033999678946f4703f9c0e08cc6e32a61dd51c5e7578a576078e7a01059e9175e94990528c59e585713ecea653ac3c339659faca788e3e70034c6009a3248c1bc69b1824ff0bec4e3f640181d841d375682149f6f9a6b45b56bdd2acbb491728da5444936bf2cb0ed867a5e7da3a5f6e06945c5412cb1ad10b3a157a6e6a6b8d5382cbe85cbe437b28c154dd7651c82a062876f5034070fd84b93eb019477da5f2ff81be658b6c405b9dfb4bdc18ff28a62fcc2f5a2bbf523840bd68945efc25114970c8195e9bcf91f51e48b30cdc070f4666e89ec22bc131a302e8c1f8555e522394794be264435990fdbe950918371f8e6c213e8461d804bd33c5df5fe2876b941c4930bed62633bed8bb490e4fe35641e35db6187be92cf174bd40aa4ec7e657054773063b86c4f56ed2a7578e439ffb624d36a5d5348c51b572bf175e2c50bac35d3e2a1ebaa4b9a320d52733c62f61d418a72958c4310db8280d8c8c1579a0c234e9cbe7431bc1e7cac23f73a4adad4b149583558e1e65fa295119f59d0e2e2a6bdbbb26203baf8cd10f35d24e02846e354aa4dd8938a0b6e378516e790cf79ce1cffee0aab270bf86cb20d920a84d43a902e0afe208d10cb6ded0eee3624333e63de62728a7cb39c41a0c28f309baaf4d359d4eed3e36e6ae99b6db3920658ff78c74bcee9a57f665d1d3198cd37547102863879f668bb8bcd8c85b6062f948e8afc73be488ec9324c04c35be4371cc7640bfcddf3d8347a8aaa7d1b359e6111b8a71f646bf41e35f0561c05fc3d5e7f4b345c36a62a44cccbaa433f86c6e7dda6c72bde822732c1d8416495aeff2ce1b5ba78f2199639f80656e7821ba61fa167ccc67d180cc02b9db21b22ccf337390e9cf8cf2e9cc4a5a9942cce7e661732adea1b82ce5b3e46a9a282ddb5d7c560b1d841d8c86d91aa2e1855d5142e2162cb4fc89c850b0dc7807b39f19c5c8fc3f36ae609e5bbdb0dff45a453826e0415db9586b54234d203d2a8534555c9d8948f54b0c0db7ba906ebe09681fa5255b33d421ee14b4f05fd280ff3cca901ffa92649edd5d1772813ed65653a7a1f446a288c9adb9841cb22792c5878a6207e7936ac0ecd2b2b730667ad44209c3123427094c99194e2ea700d07cf6d8ed75e913ea7b0d72bfd9d2f97de7259cf6fbb0c1d3077d065c51f60ada8752790d5264747cd631a027a290aae4f6901994adc4b61a46d10ba685ec5964d80ecd4b1637ac6ceff14bbbe64f75c3b956705a9d80bb47764e6a3f23a520473e17237a0ccc2dc0d15b3524ead7c9c033518bec435a11d1c9eb1305454009ec8fa5f483fcbd1573195e944134fd0454969ec2fe43c0c04cbfdac2550d74954711958c91a7c0941ad5911519053d2a2222a548e70c139aa5f30928c7470f71982abce13365171ac4661ed145f36d97810b1a65419a4858c8abab4fd72da46f8f356da183d755cdd5318a50965170b670debc28c3a2bdc196cc2a833777d863735958e54f481f7099982d5afd8707de55a043a8d2b823d24c39884f433976008de2035e2b4572065584ce1fbe37eb46cb13ab13308a603de7c4e38febd918cb61e4f94fd9411ea1335d096d4b1d2781edd72a24103f7848aec39e2ed00f2a592e86fc240265e848740f8d8e56252c3db555929b0ab3b436758d6b36b3cf3499d21a02b74545767cd5c7b584dc92dd388ad424176369388c521a827a2e006fea1c3ca95c28388a155e04e0ae36016985a151eed010bbaa889cc1e3c864eae74619595e788cc816b67db9c07d37ba185d9166b84f66ef5422fb5f416b84aa5ebb784e3a8414e55e25f439e494e8694b622571dc18c2c4d0ed55e42a7836c8412c2e4c0e172dbb268266aabc91f04e62c846ca389aff832531cd6e0fac212e300c635cfe3c9442afc317b8bb0a254ff21bfcaa1b298f1fbf8555fb4b43ebd6954a5bb632d0bcf0b0f152eb0ef4ece975c38f970ccc7b1a7848f7153d024bf005b066c43ab4520b5245f8b9a984f86321f611ab939145c8d70748462e60bc0a06acd9fed1cd8d8615438a2d74ada5588c5b766ebf88707535eb2394d7d9e3f27b4961375c62da2783d2ae0a0c3dd1c707dc4b21551ac9eb52229733dfa49213b583b1aa887e91c72a5eff2b5cbd2721f58e6d662ca60c0bbc6db5c6cacfe703ad936c211c3912971190ee979650e0f5804bdb20343672decca3d4902cc4f6ed4413c0650462c047d0cd25e328c947af34f4e30d443c0c716845c006908119d6e3dc5f2db2207f069239c1a852121d213b4d6d16f6bfe0394e6c9dc907767b277ddcb15d4508a7ab2a27cb476aa69eaba8e5ba98a412d8d863de55618b1a7227bf7835e0239ec86539a32a1435d0c44f6a91ef2e79ec0a1f7f165009bbf68cc09884f329146bad7466a4784526e0f51c29aa59a1af0354c13668b4d828566dab47e8957e01a7099ae3c4343807de8178277f114025cdbce793cfa277ba3d9fa15b5066cb8174a9b5cf0ba3156e0a35bac988c596fcbe186ed3de05d3d768421750d4232a73dc8670705353e7f8ac24507a4f83abc95cdb4883c5dc037cad478c452d131ea536d0a81ea0b5b84a94105ac8413f56f093a53d9309cf96bbe1ddec0b81aa368cc4cde6e0e082a9624b1ff44495a7edd9ee448c6819d38ff7e174b0bb51e482bc03f415818191a44c43c6016914aa1bb852206c480b1e8a79fbf2e596596814f91e41a8c730f1edaa0c3a2bf5badb2a6d0d6881e4b862000916ccef5d943f0d4158407e28ef85d1cc74a606e7e811337249b11a76b4e6b875445be0a99498b814b86b58684f4de42ffbec3299c232b6143c8168ec44b7f74f03ffecc8fc724e8fb3cc5b91c6ef1664078f1fd3e6e9a6dbe06ef78c9532760ac189343e8c14b199df0b6e82d23ad8a9f4c667ad804a1750b598cda912b67e9ab02f7229901d4c35a4c9b52e77bd413f6901fa125c2e00533680dfe66593cd91a9e95fa430a68f44d8846a01a923505fe680e18ddf3b785cc29468de495b8b99710d74f1e53858ef9caefc61c04088c63d989fcee096b4c840bce8e346e78d4441a627c988fdfa7bbdf89e8654ed7471e01d472a45629ab33a7c4013670d049858a6c8b1e7fd5fb3a2d14c755a9552ad83694bb5b0f5b4a2bcb5f7e6a7039c144b2cdecc482fd03b00563ec5825747efc0fc06a1c509d0f90df052a2377635107e6a3df3e3e01bc953fd3aece849c7a4dbd21bc9e87006b1585a22030083dc62f2b24796939f8c0c5cc26d4ff7d71b951a4cad985b784cc16f45f268dd7902796236ed7f7eb6d591685254062404118b0382814b987d08dbd793bad61b9fc6e8c6dc51a129a5b8368469d0da7ce788a0643727717d17fa3be2084a8b56de6894fd4b7b2b927d1ec987a819e0c4496afd24bc9ec49d0fa8f3d3dd833f772fd27527157ab7a3909661f81ea05f55e7db4aa5724803dd397ba2c3ecd2ac40ad8eb35e59c937812e9128a639e2ab4ce889b2e3424041dca8e34f18f434a207201f686b4c660745a3cf794bffcd9b31f274f0f3424c7cb3f4e96f86ed088a592b2dcdc5790723fd8b3198739076389b47c060bea2cdce8066880c95c73addcf5ca3eadbc65c9d678ab48449185fe89833b222f374f3b0e241e11150e4365a5f0791b5df2d7821279d6bad5d5c03ed6cd0c399f0195627e24ea15f597cc17e224e43564c0c19de52a4b0820f173be533b4609f184a88c37883e78dcac1d33bfee57dc7183c470d2a600dca2bc830ac9475e9f401cf480620194de71d0bd0986640521fac4785e76170f7fa3523589d79e1a28e33bb0cf1d3aa5bf0d6cf364f051759d7366f995925d7d63e4e9018998539e2fe2ec33808d6bb97f19d1368011fdd3de7ba0fcd0c8d5e569a9d6d37b291378f453ccd986594d1e5ec915754f95769a3c8c32adca40821538cad0eb9f713d9d55450669a009083f6aa454928bc01ef6269d6f6b2b3e3bc3102ea8fc8bde48be067a0c78307e32da311681da1aa34e9c9773e42fa11c4ee901b944876016823552c8d89aeb4d7d339f116a8c22f9a7419dbea813de20f07d517c75e68fb625fec77d1cabb1c9b42809335736e3c229d8e52eb03b64ed3ed0eabab8887b4c40783b8eead306296510c30c7a93543337857ff85c5fa920caf88faa30b621b4af919771952d834b1068290d2bdf58983d8edd1da79962bf3ffdd4a68f1a49cecc928e84d6050b083cbeda878ce48a6d1b775f427bc3ec0b14aa73782def8124f2f39bb1b468c304d212a5a28603548d1d922026981d5c14ce24e276a77112dad6384005d8ded87cfd88a245aedb5e89c1ad45c4088ce71b1360c60ab78f133541fc86d18eca8773350cdeebf62eaf39c5e83624e8786ffe667972230a048fb36c59218199b0260ca4eb934246e0b0f514d41b6c063ec4a780b8438c4e4a3df55403a6ac0c57dcf63fdd471c4b4d247cf451a874daec7ad30585200346031e9ef7cb3c914e631b4ab82818e254ac16723308e32298b33c2098fe994b9bcf95dfc34f76e52c9c09c8b7e38130477e8d275528e6c4d08763e312059f24762c47468ea46301b1280c21d6297444ed1173135fe47363ec90fae82c771f66ba11769cb3c88a49d5b7e5340d986c7a93ce0e8ae0f18a107a39d1fc45b109564ef403b3740e1bc4b511b2507be06961fc0d7cfe0c217c732d7e70929741209f0ccd517187ab400f7d7b2d4b890e06b3cd355e4cfc2a0911e31053ccc94269fe67df40bf7899fed238748d23a239b75ac2a8ffbeb6f6f6b4099b4ef2472840768ff4d9ecf2d917b86d9f98f8f8c720e0d3f0f11bbc3a4f237453a30bef5223cadbc42a9a5ffc01a580194f19c1bbd5fd32e2959d86681ca1e191770c639a07e1689fb2a56e661c033eb818d6bb822cea50c1bfaaa9adca1745a6e86fa2432601508beacd6367e555acd7c4cfd4fd419f1e8ec1d226b167a5a0d1a6626f14c1fdcf23a8f696ac77135e29ec1da5b8615a82ffefc973169bc6574b23b08c820b024fa168aead2ad6f1076a09fd26965fd36e463cc0fec15f8ddcaf57209d71038b6aac5c150bbab2263f25e07d44760d16182b15ce35183b8ea5e91eeba8b475c1e1687bc5a613695472b124510c0c732f4f9bf3045e888bf80f31348905d28f330b4ef89e5e8067c639588e1de83d39c908492a81d927d6dc42776f718b843b487e9b47450a84e2450dfeeaa90c2d4fe496e18bc784a72bc0b78cf39f0d2e37e0f3d2931716d07d7cbd7c71ca5f645a5c3f6099d385705cca448551136eddcedf0ded1c8629d24ad02b367fc030961192dc7d5c6c021f437e0e16f06bd8edc95178e3586dc90af874feaa42278397989f7e083892376e56340ca256539350c60330ef057aedc179004c015fb53e29230d86bf9ab29f3895f41791fd328524d0db0539821ee2b6ad13efd6138928ad4682ce37cda31fcdd81b9959436a61ac456c315df70d151d9810c32a1497677e0da6a1e02c7eab1c12921a205fc7b06ce0771f47338ab3c6d705079812a9e56e76692d2c57fc1d0e84137300d00643a67cdea5c460c830c94a4c26ca94db0e50afcb8c64b227401cfecadb877646575e878037dacac0273024a077a11706c816916282bb87d498a4c1296ae29d5c60ea2107f7e4eba26f92472ff60621b341eb351c78330e91b08f6c258acdc5957f92037238e4581cd4f310d2c2bf0bb4acf425ef13c0b2a8779903fca696399fc50b621135833642d3d5557a5ef1d8380bb165bee7917b7afbaf0158b46cde3c08cc18c2ab699d608856bafbb1c1ac8595158b4607e611d80786ff196092a13cecffae7d3883c6ae88a9af6100ed0d30a24c2693e6373c7c2ac36fb675addae62283afa360f6dc07a52656243b6c350d5477f6cbe3505d681ee58b9de43f9f23340a99b015a060315dfa47c4550d975e20813f3c29173e8f6afdb9406e14a8b157fabb6f2fff2592cfa0774e582ddc60f897c9a90bd716c60c34c08bb1e5ce0485a0b7a8f1828574c5a9b56d7dc13fb86a4b67dac378022df0fbfb67379a59714eb7b2bf0e3b4021e72dc641241fc4d09b3540cdac762c507faaa2548e4aebd173c1ab2f3ec64541d20970781f20ba0d81ec115e785882b14ed03b49512e9fd80433971cebc78e4d3cc495ca8b96017880964625260686cbdbdaef43f2541121138ade4ae2d528415f2e234e8d9a5dae52356e6d4ca826c5dff2f094f4e55740c5cae3a4c0f7b77e68608c97f8a8a52933aa43140ba5ba0c8350e3f6e9d73a7ebc559437b6e7c2ae66f574cb79bcd1ce515d19c36bd3894c44582807eb0d991cab6f65e9c8a9aa3420b3602a10c965c09ea086b42d23990e5d47c014aa0ad1d2dc70727f70e4e043511505c77182b6c4ada3750b719939622af75faa21ae0d582987c9dd86a4861e807fa77dc49a9e9100b414319d3e02452e0ed22bcd6ba087cd1d52fbba097a3617371ecd7d1baf839ee7f29cf5ef54a179087423523b9bc7e6a909f230b9dad3f9a3c2e4a55e7dfa17c0f39e94c0e966b6b2a40132c43018871431467ebc50195e0f3552569f1254882e127fb348b03a780783924258d7184133050a123cc03208cfc0c78f20b9f024679e7179ebcd3b87eabdbdc84e07fbf6cdf3d0e44ee75b567a793226bd7f1a93b804784186b0ff705ec79cecfee9a590da51fc003f48378f76d8acf0c2e59958a619b8dc1b08b0a419fb97f9c1fe8fa0a35f9fb9e68a19ecfbf5517fefce3727e5a532f307a1e94d5f6aaa02605789f2aa49f519fd6a0e3061449a5900e532dc563c98aa1821d0d3a1d9e99fb1adb21903ac05962a980e48444e02804a2920fbda0bcc72ac59b32f77eb08f43599fcea80fc2696fc3b0167812645396c75d69faa8a67e8d27a61518019025c882413d399945d954c580d9f4c172538251275542d569cc4c94ce995549680a3753329f6b96920ede4518ca46d1c5c0ccf6e5841d99a7ed2ec19d3cf6a5d31e6c31073c946c3d454896daf86edbc46fb15288de63a451721bd1823eaf47e1fab7970364afd76e984a92a4e432d3d0e355e4f63d9d50dad94245a3feb68c3daddb4a7216d932c9a45510bc150d10f604c4250933edb3d8979ffcf5bbe2090aa4ca740aaa70da97000d7e0abd22e4767d6c4d790d3a3fb1c49565e57dd9b19752a3f68add008532b216c2458712b1daef7174e257b44f83e84e0943644e870af8cbbe8c554722aeb95bc18ac9580ab491a02697c09e1e200091329986bb214768a9e088a7ebbac9e68434c6c4a9104cf4fdfe0d9c6fb009471494b1064c5482930b91f2e65b58cd94f5db34690c022933d970f5f269ffab895d1c5aeecc6c004983b4966e2788125b0e9f128b8c1356b6013515f3c8242f298a6fc3a929a32102056b1855952b33f1832b564423f2eeb4d28533f5b19a51c9679349f069e8e1c40dfd5109aa5f60dd6030d3d6847f301197b32de977943e0d2f49e4ddd3357c85070a5ba293989817b1c04ea90ced76801a33004dd048c4383dd29a21d0465640d8c1303cfd645cfd91393d418b6cff9e59926a01da81cf50cd63d6037e9f319fde505bb3e185de6163fb636ee3c04975aa058d89c41b6218102ff759865ab90c34025ee14b029fca5f5081c7bd957b1f439f1099dc9daf4a5b83859b109ed0c142af16099246ca1e99850b53c92154935ccdce7c48934d427aaa31c561475a603764b257a38d27761a57ecf42be82038202f71102f3220db9f7be7b79a9e19e9390ae0328ccacd7fb07a33687cd137b552c9f6c211d31f681fe5032916e525d1fa82d75c5b8589b9638935b53cc1ec6923e2291f94b096b6f82b36039018b7f4f1201a9c5a94462d334a7c6aadc4ef5887f5d42d6ac240382381406ee239f7332c121e5226e8af3a441f8784cca317acfe2f9f5e11bb859618d2036bae37db854a7cedd70c8db08bb55a8cef683a47a1d541f6862cda0331e5f6d100fea7a4d34bebc2a8f0a1b5d322ed5fe4e962043cc547402127ac4b18289d4277fa3e78c8868c3b576ac4df65366594a106ed28fa3e044037d6962823e291944118f9f88ce816c5298ca4d072d9bfe05ab48478f174d86ab4266a80d6e3946aa7dc99433e909e66f39284bfccb4a3eaabefac39656d944578515388155c96db40fb2cc0e8a326a362f1808a4386224b26cf6ff4ff339fd126c10624226183af50ea892cd5150e822733d355f5286b9a3c610354466a2bc5201ba40f557a4751b54c4b1949fcebd5ca559383cbd9b65d5f1b1a3971361a509a01eac072f43b78cfb056e24caeafef5ff3a996c22cab4a6697148fa61965ced77c21ac502a966f8b4a2148375b217a620be5c9af3d0cf3f3a9bdd3086a4bb6ee15e9f4bdd955ca52cc7851b55cf05c0ce984bc826f7d8e8ace2d0b8aa52a60b1be625d14a83861b038903912ceea420b439b2a2deb8fc47f3cb17466290a60307f33e76600eb9424b8f450fd993c3844678394c558ca3969385f7a6a79369799a130841a1fbb5d37a7da791c3be3cd009fd1bc0e017f6c1d2da922d6d98602398c00217ce2e898d3ed17887755670d754ffd045cc0364c0119f9b4d4755cbf5cce65055ad4c2e9b98ba82f2b465c0bdb814ef082fd072e927b33520b50253cdac9401299755c21612a7b06fcab86c72eb6cb1364bc055a98f8995208f400bb754ec8acef2dce45153367ca779f754929eba8805a95b3a50184ad888f5fb84eae981e5e159512fc35e626249f305d46c59e2062fb40ae16aa7793c025f24461bae346565fc937c2e8aa8877746ced3f141f3cdac6aaf416181eba55b1b1692d8bf5446814c82ad9d6bc896453eae800e38312355012ef4f2c9d559631bfc32cfd688c33f6ec59b004100bbe08ce9af6419ce26633fc45d2049fdbd9705788c5e2bfb8d1b33a7911d11cadd530d397eeab5a9d4fd8848c01e2d3b5a27b5c48da17084cab24f5ee418cf08cbbb2bddd1eb5bb718916aa08c1a5aed290e8dd016ffdbaeb3b528001e919a5392b6e27eb8afcbe07434d55f2d549b5e0fac7c765bd474ea3662855ea95a0265009e204d68289f2b8cec26b83a3d302a4433b39248df4a13343682fcab510f9192dcc379b2b834f751734e09c0490a99262f95313d40e02f525660ea07f4886fc598541f20ab45be349aaf00b8810b85c3dbee0cdcfc9b5de962ff026a4da3b73ef1bb66eeedb5b84ab1c0558b5c7e5825d3a36ca7841795c380230bfb5de40e07413656cac0b65783aa59d27b303691124f8a13e09212ce4ed44ecd4680def15f82fd6d64b246016d119e04aad505177731777ecea486b8eaf0c0c9a846629c3447766a2ba14b0b48764a43756d85e9b3b7498c46909e1b11caba2e23f2d9f7a6a32901758bffe3d842298e354e5820f78c199e6eaa4d8e170287f8424332b40c17124dde6d658ab82c918151bead66fb2066fbc3ceae4153eb33ab87db14fdc8cdf3dacdcf766ca25b5ad72bd1b890e490bc2a9cf645f107bf631185d4d5ae5c4c2693aa94877917ad610e648a926a5eb547e2fe211ad238bd0224365f45061b25a063be6e716ab644b659415060d093809f6d0204edf01ad78a653910a45a643561fb9f8c066c937004dea733bce8e95f6b5daafcb75e561c5b9ecc575ace498267b63d6b1b6d081bf0c3973bc410f38849b3a3ea4cb427b10ed6df6f43ff1bdcaf9202d1f006dcb5f023dc49c74f22a4702a95709a1246f88240aa5771c94261fa1f6cd121cfae5001cb45ff80bcc0744fd269c04772e89c1b6b0912978df86c94d99ae0c1163318ebf3a6f834c0dc53ad59e6a7ec0f8cef62393dfeea0294326671b60004597460cbe2c096590c0e7122896722a06fbc9c2c4839a7ea4e0e5d2995b58f821556392a2b08f3c739a5c2e905808b4c0a6222f92c7a220a5eb26cbb74a625f8fca6d72c7d68efa5a9801b5b6b29d1648bdeaf66a8b6586bb045fd8f867ef29ee810c31a6c233c0dc2c875ebdfbfc073ef62b02bfff2aea29285b8cfee3ad45afac12915687e8fb9b03bdfd1898bf8e255010680e7eef5224f408247863a65bc03e1e450d1a19d2c50c1659f5690fa4c2aa4d1cc08c1415783bfb54ecf47963ce7a5796f63b4e13134298fab6aa676e48aedcb0911920c911622b05a868f134dd7b4a64b7cbac0b7a4d5619c10d7df7a3aa0e02ae49cd0af52a0faa0b7c39936a39a80b4ce1987be755eb0125900d11d96002ccb15aec4defd2052aca8d4bb796766fc122325c687566606068117cf4bb989a527050af711beb0125376dc28f7fc0133bf47d59261ea11d6dc37682b1724703c7d54613d47c3603ccb0115685b31eb849dd625178f5c58dc0a9e0dc490cd38ced7c7841ceb6db2ffec514d3a4a0b223da294f5bf91531f0551149daae7550b36bc0238796ddc3b07559412db95418f9fc2bcab66d35803cc2a6d5eefbf10dc11589cc9bf0eb87e6b211f8fb38e4a45c36be6f40b207af4aebf503cb41119bbc12fdf6c9ebab791cd9510a46d6afc43636c88d0d421a640a56c38ce610eaacbec5088640429ab2c3c1fd62f80f8fab4aa8394e434300aad3222b449d446ee46c78fc2dc95a7305283de48227154003716cef9aedae223bd3b0cd9075598c2223167ec020d1c1af6367cae9610a1b8b10e43fdf0e69cab19864e6bc6ce4953f8b258c50203e1c32e842da239e5651b815bf2444e05e2bc1416c340bcb48c4cc09e5a86d57b276329d6dd505840839c4a640b326a39f1b796e4bbbfeadc25568f93021100229a0c2e4aea7e58ed025a08891bd3347b7d874cc9d448be33c7c2c257e215dfd6438e62509f1001639b8588b5f22ac6816942428a0722493ad4695e4e0e7a8755ff3a27d1cb85cb43c153a314c4161269922734875a88ebef516edde0d0f70e868c5a5598f23bf29fa80183c204bd184276a1e39ea92178049b94f188a18d2b29686eacdfb08f83bfa7a9c48d1e0402305dea31ccd34bc8f680694db68709ec85e032472ff3603ef076c54b5b029e9f62eac502d4aadb1548ba9a25026a414915c87b9231f671f1562ec67e0d76234348cf11d3f756e01abeafd303edf79fa5eb0d513ddc69b5685d61e4427e86f08633294ce250fbb88a843dd0ff3b2e4692ec6bcd971cbf55eaf1eea40614e1469be5c5b7f6ef3a0718952543b1a8c8f6cda78b11d719a95977078d8eba4e808090d97d8441d392c30b0c217a483e22751698109325abf8ca90597a61941b6de620f0e97a58dd14625590f767a61084a787fdce763d2abe6fedf8c179a66e808ab6def6ce20161b6b1443c0bac3904661945906103ad682befd0b59c9b692c0328c0aa3a7c68e1cb2783f7dd8cda89f9c91059661a25d611e0ca70ed9deb0a3e6e8b1d7a3e650736e7701db2a982f30b6c67a0e5f38863a176f6b310a6468a06f9d0bd776da134cdd68a6e59863a9cc0216fb38f05e823c3012b8314f88326fca9020df35903b7029433e44c16d4c6a7a3db6b294b77525d13e3e5d18abed547315774691708598265828eb93f93c4fa276a54609a566a2b7acc6081cea4052c53e92784a01c34717a09877f858e5c056311241bc5dd2a554286608c743a45f7967c04795bd49fd7df4cb9fe9ee5c6959c77c0e77148e8c5bfa127ce882c5824730a4937864ac2e762550572312819caad631e2d5ebc640fffd3f04961f12e2eec49bf7b425eabebb102d28f43b0f8693ba63ff07e00dc0938262afbeb439c70de6f540fe13308770993cd46c8d14ff64e60c2d05f42d7101c8890e791d4cf4922b25bb6823a5f2d0490066664dac9f2cc11b7e2585b331a343d923e9052870eb1b1ccbc358d3adbe06b27edad3f30c530973d8c6cfe39009dda84ebdcc978c8718e93d6950e9baa24ba012759c79ca8388495654e0972374053557af47e01875fd03899c0683c791a44b712ed1d31543dc4e488df0dac5c1855d51cc9006284436ef75f0452e810ad2130ddf455425f9abcfe526f673ddc51ffa1e6be786505e6d8df32cf9d03b56cf701b7542ed74838643569e85290c0072905c6a6f679ea2dde8e4cee37b6532263b78e81c750f0d7cb8c440bd7da55286a50134f82c04029905cde45e63b28cd721436276ea682bf00da6b277f9d2e570784424323ba8d47357979c18787168af5382d57aa7199f2609531398618818bbd6c23ebe235622a4392f3bbe0166b613550fdd360ed896453c158a866cc2c43150669abd97f29c4f701c0b8dde4ed38958be45cc48dd1f2a8cb65caf0b847334c30c8f6578f7cd7c763cb523ad5973397af93c5f1236b8a691135b4e887fd3e63f644926ca4e28d3287b60980644269f461f0ca34d15734f94cf101a7da1424e16d0fd58e5131f0bf0c856cb1768d6f97b15d337b2c59b72e2e9ea6e40f826d393f219bd594110d579a57c863aced7854d41e4d16c917d06c7a3a935ce90eab0aafd227d29728a2bd12e259998190ee2a1c205eb08d851ffb3b828ea1f676c684264283e2e1e1a1298a57130ac23f1cbfd7b285faa1d7d0dc72c1386e28a57782fe01f4501a34b8683921da5518b0eefa2e6857ac49dd9027cebd1a4ead7b71a75697e938d5d26b0eac52d71e652dcdf2e26640274f0cd2d5379b85527fb34748ed42402038ba4d42d94eed8fcacccd0cff7cd8cbc245f7801be090c382a47fe9742a9402f068bc947b195a29c13f78181c1f469ffe6811ae70c017476326572868ae9383799e060c9f7faffe2d97e6222da70dd5ffa5c9204f7655da0628b90e759878b95e8c3b79684045b9a9d03c45338a4536531c6fa2a42dddb1070825364c4985d3f76f294a13f178078511a7851911611bf66ea1e25b1f358432e9556125b73ec7eb0019b2b058f1f091c296a3480f0c0c262d1adbde0d5764f0cf9d3919fb501bfb5dbed10ef086e458134afd0ab081fb4cb08dc07a0b48cf1d5e2bd2587eb22cabeb43d6e0f462b5e20bdc897bfd3fe64b4b0f5000d6fe8cac6ea77f7c5004aa67ff0faeec93773bf6910bd57da954ff7fd9eb1796910f7225404d8902525d293291336e84f8089e61c41bf9778710fd5cca4574c9824f0cdd1fff2a0fb30097cd7cb9294ef51e020e27231cbb4ed624606605d53e34bba52943f3ad4b8ee7529c922bae9260c1d1924440177a82e16bd1102dd61bb322438277ff332044698124746c02305db38d197b3e60f6ae51f83cf4e4c84ed26323bae5edfa13fe18e6e4947aa4f0cf206034e369122cd776081fa99b8e225176268fe14af9fa0090627c227cb9f042f2006d7aef67b0cdab02028417cf3dff5e511cd416352d331b878e7d6f1188446b37dad4591401adca8d8c6e36122e8af3a4e37e204c4d33665fb50e4ea470177e9929c175d1368bf7ba14a18bcff2065a1aaed7fad81671498b161bf801113d6d7f9bd2d5cfbef5327f49d70b59011be8b289522f46c6401a98f3a3a2516e90a29629b8e3967257c21131f4b10026461a631f52a0955cf07e3672a33c44659b0d00d9046ebb00d0d49b1f76f97a904f8a9105cf0a23a9115dbfd44a31440aad003814d1cb2f06a3a1ccefa4608c1e5bf4fb30d6ac40e58c96bbb9acd1e3dc877b0e771f09016e11ad563ecc8392e14aaf44869592f45215edd69d418bcb966daa8dbbfb8e92d466ffa19b42bbcae9edbbfcb07ee1e021d4dbe0a67fb3aaae8f9b491008c41feca68ce6216e9cfed952511d4506f3d285a20458119f872009dd2ae9ec7e0714cbc80598f36c2fe6d38323331aea7b4f4a553e55870d4bd385ff5f316745d302d8445ac4f006e52b9b4ddbc7dcba035e6b75f34c40d62daa645f2b57a59acbfbe5f57c7ada4655a57b416dd502d52ff5d600214d90123116f9711a01ea246e2414739325db16720eca076fba314d0e40418065e63ba717bb43de59a9e31024b9ff8b716f0960805066e1e08e335aa4e4e930f1a914085e9da410083c2f7ea64cd3e40265e8bc84a0f7a7f1c6ab1b30d57fd14b7409c0a64d5eaf3e0d8209aa53d73e81e8359f9cd2296b3825e052f00de001f16285f789c93cf42bf0f217090fec1b19454f3f9ac0f7eb4e3bf0ee3cb4545a0304c01786c4df2f4ddfa48679b70816c5759d75648eb1859ecc8980709d73cf3df3dbfe0de5c7f7f955806e414a81b77dc1afd29781c7b1c36be65919437ea04081cf002ece314aafee2a43b1f744d5bcfe22e6b1af6327d55d662482035b7f407546251fc256f4861cfd042c3222c47f18ed89a2a702b1d78126a62733c9cdc3bf6a59980af55acf513fa2432c0a641110de75a6bbc02f04b8081514cb2b3ac29261fa52839c5ee420ff06f5680a9fef87521250fd3c7cdfcfdbb3d1d2a0d9473f7b5fddf4ff223f121ec0bbafbc61f20f3798f56c8e620d40c1dc6f65afeb05200f8a4ff4c06df443a335e7ac76d4f9342cead3fbc2703c56a219e995fe294bce768f29439ee77822122efb954cd18f132100b08dd26d8379cc850266c50196f60c3c4a5ffcdc0f0baadf2778d1a805025764b818210d81373aac89cf2fc298b51e293bcfb6e618a89b09e417eebf5b1d9fce253854da672f1d2caa4fb986b96303aefa431f59e2fbd2a3219204703078a3bbe06758121c3e4bb80a788648614b106688578cdd5d49485408c1d40f5101b386ff7e9aa179e0cf2f48e2936060792b637cda393d10705af888831cf870cdb39369120b16063ee583cbda9fa93545d53b8d7d3279f89753035d58b1a292e30dd57100822a07b2286e3a9b78c03e23c1c7fc3cfe092d0bd68d500cda973be8d8f2540f6143263389e012e9357f46e7d03708586072e53807f6c65ddc55c890f148aa2d68dfe19693be26f7d66d38a62406e3a1b9a7d02298e0d2dd25d6a1972bab9bbedce51e420c83ada9fd1ebbe612a9b6baa6990ffd5e5f5ca151046e1616e8e225633b849aae423de92a7ce2d27859ad0dc3abec5f54df247b84e47248462bdb34d86dd3c7709ac99f3655d4a41ec5ed523b0fb05e3fcc2ae67d92290a8d4930c566c2c7e3d7430566e6b1c20f185edef9f74b0318624b66cbb578644bc613099b94c17f03feca77a897e6e5a0497ff9d2fac71c94a77e6164a9caabb224fc364278e25e88b0ee0f0973d0e38623b82476b6edc7429f86284f7342973ea19b05a6a6682a374fb60b9e0fb2e2ca52b877517a7864243cbefbdb5f355703e6ab6dd95ca860f1bef2420186c0171d32967643cc3ab53a0e8eb8cca6e3dd7d822c3002b8a8f1e9bbb8d342e8de55fc90295242e8bc86cf0f4511fc94d531f4d6670389ff5a1597fe854c8d4b1fa6a92ca0a0a1c6a9aa8df3f110f52c90a0a70e902ba1554aa33002784346204d39b0c97377e16c6ca12c7ad4cd5ac98f5f8eb98c5456d9cb4ebe9d7ba988bff723ca44575a72dfe41ddee75487c47a2b86720aa1f04d9a2d428d86c65dfb78771f4ffbfcb0bdbd6d25e30b2ff9613228944f42bd25e1895fcfc00bad22aef9485357c4e0dac0771a4eddaebe49b064bb4c85520c42387d0e12e14a055a041845f8f07f6ce55f51cf1bb6633a99b1710a1a8338ef5bbac50ac4712cbe315b1e5537b4a13137e815fde034e8e606c81168d07929838e53604d11581401afd428c1533d7461e6b125db600bbdecc14aa48a28bf503907b92b0015a742ac19f9f18a346f2e3d223e2b743118ea8693ce599a9e610317fb22caa3e68919f738f5f7b9bdf396bcb73dfa8cf06a466d8637eb8e1fdb0455e977a14a70a1ec097b265fb0e72af921c80c1ce09ee22afef4fa19350a3c0c80c18120b6c1ab1e0a07b0f11498317d033a36d45dd5628e3b096e0ed2be14cd3e1ed231cc6eb02720e826de3848047819eb3b1ac53380d4af8a545752b259cf9602c83535111d72fee41909f529c9a64c50d2b33bcd7485989133842e085ee5998a45987d39ea32d46c94b0d704fd6a9c0f6bc54a272095cabace9fde1a4591d2bc24dafcd82cb42ac76e1328b069202cc50c5c3e41ff69cf80c527f138d5a82a0439d9a4df09a649e5f70cd98fcedfb4042db9acbf7c0841620679f1cc18672db1d0c60af0ed4d7f5486dfba047d03aa9087c6c6521437eed83e9547e8df197a908fcff54048f04cb0705c7357c4351ddfc065dca10cf5592286f0b3b9cc7ae4e2c72c96e3fd3f2c5e015ff27985664094f307ce1aba234250253cf4e492f885de314fce400f3500a703dc6ef39a8bf727fa660f09b67b7021cda699dbc742382107093dfb7a802e08ccf7709d0a4b3224d13f3ac7ca1b5cfe7ae60ddda49342c6e0f76887c23ac900d22d4f98b526e2169eac158f3fc6def2913ac783a96003cfe214ae91bb26f2dafa49806820395fd079c639eb963e19648ae21301ef53b4370b8a1d659609147063672e5de90c74b96cf4e7bc69b28cf87a70d45b90d588db3918f607a56c4467646a961d413189556a5ca6fd5a3f98ca3a2eac7cc5f461962faa59b9ec284820cabbfa1246708e7ce5f6a3fdd6e84bb4c3ddbe30e84255c07cbc96d7dede8593c6e218a898a3d8def450c832e128214d5c3f77c81f49385311fd93f9f0a9f48140f7477fa7a5b392a960622b60dd0d43ba44da0aa46ee65b7dc38d1fc57a0f2c40356201663e5506cd22b95c0d0f3b4838486fb56ffd5e8b98ed943ffb888970f82a8a11c986382ec6d9ef786f0e9405250790a80d7c6fb3c1c0080cc160b063aa3b0d6175078ffe9f1f6db110c398553742f021758c808b695b7252161df0f3423c42845c6082f38ca516d3cf9328c0f7ac87ad7e6e635fc14d1cc148709e3b9aeb4ef058bb6001839552e1d808ff4cfc6a4c9d277a2fe210766e0daae104cce176afe7b9356d9d1b25b8ee5835d4843b8e818b1766e5f4378264d30b68f9eb0edbf3c970f1a48cf3c49c1fbfd05ce3d21c1e78ae5b26fc554ac35648a536971925df9c904e884640bfa8a4ba22328e27c3ad88a0c7b84dba751921ea24802c82b0d680d0a7375ef879d38ef8143561f74a2d99a4a5a7a10ed1bd3263fa36113b53edbb656b44424ec612b882a96fe299ed3417a05a378268b45f61fb23c3438ec9e53c7a6a445946a0a9ee59f7b890d5705f2fbba96549683cd4b2d131d07ba68302613d78c4e25f46d5f3cab2173e793e3c84b2cbbb7cf8894a877247b53fdc0636dfaeb65ff45ccda409698ba053fa4168ec4aaa42a500e7da71fdce6fb72601ad1134408faaba2c2b350f898ce15477969f50375b626a1215d01d2a1b5c6aafb66eb3917d7a6e59fe659af54006b524662605568785c41f93ff8fa5cbc03180e8c565f301fd9f341710eb21d7a05c3707dd0c6f233c96d017a282942b2807c4d5cdd6de412697595210a975925c5fb0eeee25a4fead6e699b758418abf03a68926467c8849c608d4ea42834400fe49fdacdc00e8528aae35830f473dd407fb8ee027573323dcefe6b159150fbd4a40e8f389505df6e99e02758a2be77b5365313a16178150a9f827b94032f49f1609bd9d4e6c353f8dd404b4afd110fb342355054cbebfe4f2922b23bc33f09b951fd1fc44aecd71062ab84bcc17a2354c40cca7f352ed03d46a9657f6212ab10973e20eb84a29872ff7e1f85f2006df49fb067a62a24b552d2c100eb8d714d64363c4fe5f6239d4f89c6c5b2a501c4b1bbfc966f4e403000f4d8f1ff9b7985978e70d31705bd40486f894ab60feee7bd376a114f6aff62d890f69aacb8c2302065bea3fa83154f885ad0d3bc2ba19587d8b70a40673190213b8eb19e73847ab5b2bf14add0b690e7fe06819ee4e94a42cd39dfa2940eca9248bae59e80b4223daaf39fc50a264cc5321bff255a828f88adbc5114bb40801b4b8dc39a9845950f28b997bf425b945db97da046d592c1e798c3c96fa3c4c501d37dfb9ab6e83f591434170c7bdce3e82efce40eca4940558f2c321f91b32a5d1fb26ad05a78a53587d9f81ebcef73ede2e1e2c85add72648e202897b70df94bd756a2a694c684d16ac9804948bfba14e4ba069bc3984c25f62a6fe8b43fe0108a3ab2c602f03ee189fde625c9fc2775087f78ba12f941d2ede78ccab32c107b087fcda89f6e6b2ee439ca7f25a5870f2f6ac776964997287e36fd127209419a9eea964f3c904cb80ee1b4bd58cbcb9b460a23117762a7f063101349f7d53ee73f256d2334a67b172138b25b52484056c7eb8c304aa1890ebf4cec77c72e87fb41f95d387a8b4102053fd0263222fb11e3ffecaddc464069f06995e686f2875044002ce544b046c039d4225561f0deec9598761f878d214dcf6d683be664304b0a741a4bb1e73bddd6111b01f8011f81440185ba27c8586607c92095063e48d9a73e82d585c75c739826530c49e93725fac41142a2efe387f0e2faaa97ba21920357270475f438b4e4352949d6906490459e59d016e845c0a2f84f3318d706a85912785e10138e6447fa9ba466541e7bd00cdf484b31fd3cabe8b107624ac2bb5f768e86af36451e660de731a681d151c2ac44af69e244d30b58f5ff4b34620b852760b4a37a34a39bf06e87f2d2423ee945999618f33e2bcc9286705a855ade411ad8d32224508bba60279987f5fddc11d5a91dbea1dc686e7119f91d06ef8fc27b14b86a447f65f2d3fa447c3280b7595d5eae4c0a9ccf6f1c59a03ff3d31b88b3e09135ebfc91b834efea43c4b43b1fba52e96217d333bd750d09df6ff0cb7efc0538cbc6f3fc4301370030170291497790e984900db4f2c95a58f7e5ae861f3845cc93e2138ba8147537351696a904c6d961d6c7c9e94399d2e0e2e799ea92ae15d67c3dc87c0d60615dc3e3ef6178659247678e71908ac051690ec001e6ff6ebb0d426450137ec72ac6324f7f8c4efffd6084bfa1b64e131b0aab80ce9f5e3e48e80278be486065d507d3781098adf0892a3f5015818a41e8c71c6043000e033a598347ac6baf887dc9a5394539c18917d7fe0139a8571f8c9bb4a72221194cc5b1a1386ddc127ed8fbd4738534bcac0de30f873f69c980b329735080f884ef5b70a73a80c915659b9d3b4e933b5a36745cf0958f6791cc13aee4548b9c66234c823925f770621a70e098e19f41c969f05c541676e31df234b463829a204c3c41f52603d30bbcd3bcf4616306360bcd14e76083756453480201a6fe1fbacacff830a0ed3c0a9d3e1642d12702303667ef97511aace1044180e80f884c338fcf48175e20d7c914e8c5324264c1a339b52fac5afb42447ce3bf7d0bb3c159dd6cd2402e3bd869afab9800d5a3caa00301166cd4af78c01b0a3ca4d26df238b9837fff7f2de90008a626ba675c49beaed095760cb84ea3fa0450c857dc4a0e263e390c4b41fff2a06033a43b8fda224f0426e1d637b3eb7772daaf3ca1f90d4eb50da96a9c313ed05a8900b8f9d2a94d500e2aa4eb73747adbe8c652e5e056d7832ac973ab999b2e63fe81bd1721f4367d82a341dec5b6d0d472a731d93c6a15d91d0afd1ebe6a86cdad7678bf54460cc64cf25acaa16048805dacd192e36140ab9443c864757ef999fc7917325cbc91144cd966deea68d2e5792a3e5c0f10117626c3b7a4af6504e938a73a3c55d542e45c43e8481eadce295fcad58c9c4c1588e081e8951bb6f9e9d62136eb6365021e9708ad01d10ed28c129308802798a636ff093f6317d8c05193e0cb3c7cfcbf1f9590e1a2081b87629512701a5620d2d73e4a246aa132e74818898f088464987b116476b466f08dae5efe487e9844a930f0cf7329d97b0d364af1edbd8e4d3bf18e61250cca4d55c61a1148e62e5a4634b97201f5157b2352a2996bc0fcba3618c45deae6ae9bd2c08229e8eb25346af29b0009f5f1c79a561c7918fd2dc22f3551a6b09d656574647fc87677b2269acde8400b84e245e6d6d2877f73b318ce7041c66ddc78c177d6b9b69fac509b23ab24e28697fb2181aac345a77e490ead1d204e4bbbffa01e2f978916540ccda05cbe0bdddf32cdc4d29a07c48691f0f8b6237f4ac28f8d817605a3afedb8f9dd01b093e2b7ec50ffd2b3afa033c78ca40f0a29f8c5af960562629cd0f6ba885a2e7001429362d7d9ba73bb783f5055558c118205beb244f1594deec162391888b8a9a34b79686e5b2cb3bf5856265e00fadb866480dee7fb68b3ba3e5491055a9844e52f95fcd07e999ccce07a2e8716943eca3f9b08495c4f42053dfd4fef9717a3ca67b90418863b3f53f4604f61bd48b1425c158ea414628db2054b426a2f6624cdfb54fb346a28dbfd0e7e0b30a9572162471e77fe087836018ec7a696e1f5f94f67ac6434d83fdde7ed7102c046619535435b49c7832d7d905554685e5132746a894654a5c60191585a7e60b3e1af3662e5a9108e3f7023eb17f751e088edb2f1b114725f80ea50c7d7a16580d890c2c5a60d87b7609f5d7ad4a2cbe770a57f8c36cdaaaeb154a8ed4fabed5499647b5d5db5ada1dd7867cbfe920b9cf5f5e23399a8640e60cf88954d5ba3d0c693b5056ec563470ab41bace442e47d9d090af0f985751a9abd5d48e0bfb103a75adfcfe49f56451f2dc33d156d4fd6be560c67ce3e73d8d9f8bd35795417c84a887b14199a73b30ee4ac88aacff9621766493767a3010901227429e4876c904b657202afd05e7efa8d27c143b181844b59150a2775fb050571c674c19b2de57d1db63a39778be4749a6874ee765eb39c20a57aef78432da0c9240d35b5afd6764bd37a52e9acfe97308802705a9e076cb0345a19bd2014e022e3e92b50f682be8b77da1c8fe6fe2e0933c18fd83be74c3a0017908cb2b8be99b418dc6819fdaf86bd057918c648c33ff028d0f9f76e30bd3a2825a841a7fe6351a0f3f6de3ae405e0519e91907fdae8ad5afae22988918477ec58dbf8247c5cc3455016cc28c1f4067a85004fee0338749a93149a6bc0a3d25d466029202a6bcf8893301df79ceeb2684982330bf1d084b6b05877a8d6edd423020caf704f52b85d9c1ac1fab66306af04269efe8acf883f94b56501330a55392bb4895f7019bfcf2fd4d9bdb1701d09448328a946596ad5a927355c62e10a378b1d2b02f388c2d5a7d6a795b38077618067dfe5ab1e7e55b01b7ec720c0265c4b4c5fcac6800014ecb498d9bb0d5f72ff4a235edbc62503fd47ff83cf9697c5f2baea1f2326c74a93cca0e01a009b3c7c4378daec64bbf11e0b49c3e470927abfa282e88562950f871754ee55e4515ba3ab0e91c1faf461da49e621c292ae5c2e7d022daa75b03ebc9c6bfcea793dd719603697a9981c86eb954e79cf8c446efbda489f0d12724ea021803f9a9645a8bbbef2799cb0bb5bcab84789498a2b80480f340e9afd5012fc788b7064ea348cc686ceac4bddbb1bd1324caed8f6c394cc76331eba06049ec7e70ddc728259ace9f03971900ba83996a90766766f39974a0d05d39278457e67a0cd1f7b3cea1975c0ccabc0c92c974567225e8161626e4b9bbe3ae79169e857af200b6fbaa6b35db72904fda19615adf0722342e966b5640c1f177814e394aeb15ec9c72cf9d8ad7fd720c0293398a2abd837f08ef0ea0bb386821573c4be9c44f0de10049b8bf34f25c1f9f741aa8df89be3815ba7c85242502b00fae7805a9fae794785bce604580360c9ddd39219dd7888987c56f7022135c052e309241202bbcf2208a0c845daa13c8689e1c40757769012688fef5d43a85f52bdd5b4f3dc661fd0aece4304ed0b0f212403d837df32b89705aa6b13f7025d384362aa9f318b18319b56525d69354b426ce15cbac84af82ceff683ef28061dd42c01930103a726781edc2634d7f92c040bb91cf47086e8b8844f26af2e59cf4685c730fa10eb4abf553cc1013dcca1050ec93d0b254398c468ef4d9f6eea3b07502e499276e2d49f457e5e9bcde7f083496aef2872f97ebe4b9f529305d2402f23f64a53962e2923ee5d9b77c35b1d5dd8a1829b701636b626f28f9f37678bf460b14128b05f82bc45e4861120ef857dea628f6b6fd345c9b3a09b35b4b05949a07e3ead6e4e668beb065bfc3464f92134ec4c9ed0e7c274c6254a6969f77d0d776b2764b25dfc00dd642ef28b48189390107c97a1ca441be9a747c390cadc04ea9e2726d617698989b707f2cd3a3fe2dfd30640a93c9111bae6b88acffad6a9fd0657821b150c8fa18030b440a1fcf686020a6798b801bd08055d80e8aa3d56731b0792cb485521702bd2c0b9fd1d4f03bc6716c757bdc73f7ea029a3da08765921dfc14067ff2ae51b7f8537a807ba1cf06d3f6fd157f898ffe967ee84e89437f4a7d7407255e15a04387e4047d8c3668ea4b444deedc2e3e2f3599069f0ff44a1e74bff4a3fb253ebab7f4a1774a0efd53fad13ba51ffd2ff9e8ffd217dd9312f940348f859db05c559b9240b0af7e3839120cd75f117c372f16a04b666bc5f9a623f847094e912173dfbe73b47d887fa43897f083cffb6c52b37ade5c4eeabb2c408d7c54dd5a416a1ee7cdfe76dc43679693dce4eadc34a87bd979f64934a55ba4689125d6db722cb32c2c1763a6bf00517be70ff5dc7e92720d47fbfb5ffd40ff4c5a9af564a2d772fa0621ca70a50049d2e1fbf98db9b9e3a928e2486efb5761f0a810eb4b6391866a2aa4de3e533c9553fc39914783674cbc309ea805b0762d9b91539b7d004e291861047971b7629624506e40eca472ac3e290558b506b5da664fb69873bd4c44e25ba85bcc084d6ede6641842ad00178b6c1464dccb298f53ce910cbe7ad43978e39064093a0974a43fa717574eebf011a91324a6173ff214d12b336472a37515040f32ee2662e005ce59d28aed2204244fa8e02eca73f21a2a83a8bc7780e860ccd2d4ecb95a5688954f5a7415756a7e715b743e71d76b6a491f627758a1e9f59e7a3ab3850e40455aacb88c4fcf7950dfc71964f8144c22df0fd73e2ca9750124aa7e748d6c23b40f492fc3bb42c0f31d787e84e7bc1abe061a9bda9b47887b6745f7cc167a72af4b0962848231d97db5bc9902fd124e41b459589bc2018978c491ea84ac70938d4ba4382e5cfec133412ea5e78df5a985d83df252dc14bba2129db73e77e5e4671d31c6bf6ae66277e9ae162d5c1e4f33d4b6e5aeaa77a09c8abdb969a93e97d218e8ad41e578e8202a4b9ad69c32961ef7bf6bb31bf674bd0e297bbe8b5fd9e4d62d773f8fde334ce30262397c1efd93a38a5b7a15a5324537152c3e6c7cf66fcdb9e6645562f5cdcaf2969c1ede7a97f55e883a36b4612b67a4a0b4a9988482b1fa4d04ae583ec146bf575e03d2e7fb2521b3a0837ffa7e219ab3262b58e4a8f08ad18780f0eac3f1bab50f786a5a2f080645882d0153a90a1df9699d8d0f6c354ada0434298c96effb055364ff53526d573874973cc4a337e173ebac27d8f4205fe13a8e86b47a25cd805f053f8a3c394609a0bf8a78d26ceb67752abdbe388568f8f4152eb052618fcb7e8895afbcd59865e541146be6b2ac818ec58369c9ce23d3b776fc2305be5b2632e177ce08e005a6b70573fa0e0e182a0838e1e26fa05e1a75f7a000935fb08676ed5f323f3ce2023f022064f048137b77c756f0a73a5f7727064676564e3c9fa0ceee1ccca249fc16d8ffc152ae8fb945e14f07c855613fc05b948eb06222a869b6d8f86015d91a21b1bf1fa34b9ddedd31aa7461150147d452aded9237a0eb514c36144bc1b850e2024e30caea3c1f4e2772fa6aabe5f0b690050d715bdbfd915824dd2c9856b27732e665da53b40bd5703a4286db430913e3969e358a01d2a9db7c98d23f48e2371b37006d6bbf24c3ccbc0147d99e489109773e26ae85bdb40611959454d24c59e12b315e2b10734c2e0db5093d496d589b4aef92998d6f88e6e9c5d5050b139ae771452c475dfb396990adf6e508c9a1f5d9d90fd339879b88acc1a6b4158614713d80c9d3c1f80b3e5f79c2cdd20964c63c6ecf9648389dfd1fa9e23e0d96f9afe45404ab1daef8166e45b80091c01bfd79993fb0e0949d3e23e92eb9f61a4ded11085ea01b9fbe9a1614f8236ff5c495e941e1fb0e72370c4068ea6ca656b2cb22c76a6871f0ceb63faaa2ef1fbe282a5af19135ed5829eb0b2cc61ecc95ed4c153b9596167db5ff8c206acac59711ef4d6c1a4f33c9d8941105e57a748a21a060c5c14ec60bd1655eddcc60a1188b6b781c7b8619840676a3671e31a5292a9734ea6c91ae3d66183b56aba62ec4adbab4325676fb097cb2b5a79c46910c804c6d2c43befd798ba3daa6d1484118109f849a563e056cab539c6143530ef4eced43bb1ab024bcadb1f6b96879b6f2490149b112cbe28727b0870dc1ed76a18715880018d2c24f4ab6d28144a4bd18c842dfe0f9cf425123974b6982ae9667aa3f47efc73864ba4988eeee9afe1ea576cbb0fc13678b79d131fb740f7d813d1680f2b8446c4ab2c06308f7aa7c75102856e96e6ff79c4436bc2e803c110092cd6a608d8eb891e91eb7a95c5be0123f149a34d5b7718b64614458a16ab91a5393488ccf2bfe84c7f3edd6632d9c2c37538a6078017962f494417e3810b6d91c6c48ed86df4b13395b250e4f6b91423def7229c3fd02a4606655149bbeb49af87e42f2221c60d0dcd5e5cbb946d59f9650fcec0f5880b4936b2e37e8062d2078af8ed7ee26f5a8b73e2174fd4f50e07edebf799c3b516c51637015907c8b0c965e22d8ae6ddb35b53fd5462af7f0204c4e021998cec8063ee5e618b355b1a14cc87bdb8a5f00728ff00b78e3b80ed4483eb6cacf03c6859a61513512029fff25ede90b53e5a8a42148970da2bf422166cf565d97136541ee4254347d826bafdc31c932315dbfeb7bdd332e8656a95f1025f2772428e0c90f8b799b755e5a9fd5ed7cafe657595f6e6c7c0aeff87d01e03cbb433f7aa11a2d0cea902f947e7a657f7c6ebe3e5ef4e8e606064c0a48d9283985a774b191c2c07ab4b669401f16ea1e87fc27679ccd8622423c8e6f888bc6f1882a2c8fc5ec207c1a098e8e33283cebc12cf079dbd7747df5fd277f981096e1a284db19b19fc76aa936ad6436417d6adc3dc4f2b4f21e3d2cac492474e080c2f1111f21d2fb894c32b2832fe3e772c754fc70fed424cb1a70cdb3c8fefe74dd6f97e76daa82d2ebbfc0b56762c2733cff4decde11062b9baa84e64085040bd76928dc35654abe30a2b56f375dc5432e120908cd08ee2f4bb6e91a832f74735bb8e12111b0634d4dd51ca2f08b18681b6041adc059ce55e2530899ffa2078324b5709ce9dd5e17ef561a25cf9f75ef19a4e1530ace8003eeebe5d43809b4859c94d0fa8962d5e5cae33c899d86dc5bcd2479f106cae265fbbc05550b7dc2c2de0197a20e98f7d52a181b0ac0abb93528bdea830c28127ee037b45b15fdc9674154495b21216e7a24d0411a21ee34630bd7ec44443702eeb0bde218e1dbfe18c7fea471e94146b0edd53c621b43229cb749effa06ca2896452192c6b0f3811398001444345e83a93c9b061bacafaa75c9195316d26c2073bff87cf320897b6fbdad914dc5d56b784272adffc9b33402a1e33378b075f45e021f7f937517372e68c8ff96be095a0c85e0a3c87cad35d069b01d26bb9b48ede5fb418fdc6581930e6e66812dfb18680be0ba712489917f1cf975a857168480a1d5e5937c41399fb44152f2b7f196df516303b0bffdb16c821c4fe9ee9c45bcc4d8280cf0f4fb02d2a1a0f258d1c48b11b29963c422235016e4010fff4d926681f444308180bc924c712a4f21308e80d7227e84d898da850b90788cd776558d033444064150b0fd6eb4052e03cfe84ead646f2bc3a7866430ace2bca713c1bbd414c2120c7e8b724c026b0d4ba7c594181f67edcb300ac51ca5ce266faf3785bcd0211cc20a28025583d95b9f498e0d4c4d649dbefc557823381e3d9adba830ddd39096434264443666c0b78a9db55dffd2423fa1acaf49230d2a6fc4eec403ba010380e4fde5ffb258332982d86f53ecda4dd8d39b82e662c9c839b1b07712e3d1e267a10a8e633cd71ce988a82e4f2e191fec4f9a89880a406782d65d30c1420a132ee1281cc2e58a259c123374aba2452666cee7391a876047cd006fcab3faf7ab42e3bff497abc12c0960f849d65dced2598c9eb6f9b43aece606881b6bf1f462023f129ea5a320e81324ec19a964ebf337d406e3b134849872c33f49ec6e123fae9d89a0f12c06faed587c268242f5636bbdfb51f7448a84ac84902a9557217de4567308f80963e057e66556298fa2918307492744a2deffe522d071fb572dcba1bfc98c19e47088797930c8e5da661d12f7f707799300ff9d91013c969c14a474d5d067f68cc99e2dfb8acbacecdd7e30e29f823929b8f5fee1349b0a04229fb8dd4f184b4cf4b0d198d70f85e644201e9b73fc5dccf6d1f828f2c7565db6b3ceff98c797fcb1d3306e5a92914b8cc3a89ad81b359a7da9317193b30231e7b9b5eb1193481f3647e02d378a02486ad3f07873e687e8541a9c72dad93922e23d5e0bc3af11803267d7e14a53e7d4813d995efd124688e40bfe3f8b28479b6eea68d9391408404db3842e634d2d870fedfa91d5d8b0fcda8c06480043854193bb87ee6b03a8fc429cb42ebdf59200de3b9930b89d7168cd2de497d91bbe6dcdf7d46dab30790fef7da5ac4664966d88a25327a16f8a21dccfe936b653d3e0ecbf1d720f3bcccdf68275b7cb110b97f21077141af88f5d20b14b4d203faa0a2825730da55a50371802a85bc59d4092c9021ba78ebb2ea33118d989f2037b72d8a6a57fe2e887b4d8bad2352d3fd883318800077770884b4d8357b8d010c6466d6759fa6d26f7daf99a21e31caa28ad9bd37d9724b29534a295c080108e407fc7df3bcf233c46bef6868746cd16407b3bd33fdd052ea1498b35c666666668ef07164c88f999955f5e9eeea32abaa46156bca7bafdfee5e2b331ebfd7efbd27e6faeae0a97a4634b7bbc16630d7fbbdd7f10544fcc418e3f67003df8f889f273c26f66552f5da5d7531a9cd0ef9d06b16dfb93caf1393131daa62aeab0d5d36f29448a930f0a164de101f14c4584dd3ab900ab67034579e903826306047fd740c25e68c5721151c2109a9c087c80ec98111a754c023a515a1c31841e43ba78956c839a7343d93c969f6e62d8ae6043124339bc030d253c4b7f740e1db4bed6eca98999999a1a80d22b4f8f637839c71f4840c561021c4b70340cbd1f3a2e185166aa3c64261882c0ef02d34c4ceb79fb41c3d37318041e30a21866ef070f1ed33a6cb175ff0d0e07b8a6f3769395a3207b10a333333b33432fd91015eb41c0d8b480113629e9999b52fb4704109a5a0896f2f6939da34062f5a12f0ed328a0d424108e1db59b060820f941f9464e189b9535650848915a9c1368a82a226e46068c48e101ab023862b50b0323dfbf46b074510031249604216945084153054100309cf2eb35932d57018948cd1b3abe000c4041a2c61892835088308329aa8768514589e7d964c359c6000212050f0ec19e4999d040301423f20c922054e38011606c8c134550051810b21a2c085177889c948a552a5a0bbbbbb9b3486203c3b9972c4690a92273c5908a9aa600ae3c9b35f373f27cf602f8458a95284d1826fb74a586cbc9112856f8752a3ec2411a2081e700174847908cccc7e830373c6dc7c357c4b41c17712bea5acbedb958a6fd34d1834b8aae0c51114be60820ccf3ebfd8c2156044c1d3649adf3588e10a9d09c754230c613c6b218627cf46cf47533c6be1d91dc60b203c4fb9324b4c29496030b29c38bac020e58878832259440fbc0190858412af64c24150fc4112adf2c924ab865a8eab6da65c05f1de2b99901ce118fd0b899c61b062c81027823a466518b2c0821c49546c4a19e1d331fa52964c35848a504265884b2ac182e80a581c39918510318ac84206047865220529d6ebf72ccd62885111265f283283d512d8df5daee961b95acc5002ddfab8f1db62ed968e9df217a763fdf271e31fd7b507599d6fcb72981991d02178c50f8a5eaf780dfa41cf6f3628810741d3df94a9380f90f2ea3cf4c036a45504ba78d521250af24210df6991176ef09d76d13b3b268cd92dcfab922974e394d9c9f5f102a6705f14a82de43e800b4890cc052e9e9f3c73918467293c1b3971e351df3ecdaa11be6e6d1f60bbbbeb25f87d114228354657a818b436e8dd95bd17c498b25595673f86cb5f681940dd18dedd5dbf4e203161aa771b8334c5aaaa8d7e3d98e327028b71717466a3de88dc7bdd9d76747e279997ff9ce65131c60813638cb147cb78c24495e68baeaffb4fc613261f613e659bb588789b83f64ce1831807047ece632ab01e7cb041093c08721a869134dd5c37b5d0052c54a9dd5f92041155aaa5a9c97b2fc97c6f7fb8a0e495091790acea013b40e88d5412d621e57b9b3e7e5cc703bcb298909b99bbbbbd2de7e139e2b4dc7218c55ccefbc809e12108f3c483ddb153bf0d8cef2840de773442b2751a29a7c9e48e42d10841e227478d76873c0f8c9a8118903c63f1bcabec89b7c59c6d5a9a1d6c8b8a7e7efdcde5f9fb024c61bafe79b843c7ecc89e3984a8c8abc340d8ea2608d5af243449924868f3e9b25c2f16cc6e9f1461515e78e6f3c554ba385a0ce3ba6582f7915c6edacb10f371dd42d9274e968851d0a52062f51dab14440c3d747e02a33c6c6ea12c3767a329c8501efa0ed0574574031dfacd0d0d0d0ae5aaf681ce5eacbab7f0e46d786512e5c8330fa32054e72293f7a8bbbbbb1be58477d09976e2c4c96a353424bdbbe968a477286d968e4979e83bc0708dbe44498cebbc9d74cc1424164cdf4f8aa4c393942513dcf1e19a181d9ea2eb5f513564f54fcb1698c274eca38a85b883dad8f6cb7de0f00f9392eb7cd05898dc4d72da5eae5d8eb9073a064224e6f36e8b3a9999b8c672e8a8a28d723924b95c23d9fa85c3a8dd4bfa402e3cc625a421f14ef876898528df9e65018a6f2830d14212a83f80518a7d7967e53ce27a5ee8e0bdf77626cab3b3945d0ddd8d7f7ef3de7bef354cd1d06ce29232a802ca4731e284e8df2b132d30a1a578af08a699a543b2c5cc1677cb962dbbb9b7e8620b28dfd128932d9efc7a0d38db64a5808206aad4c362ae3fa279b96a17b8fc4561eeb876e6253965b9ae2639e9e397676be2390cc7acf360fa773463e0c1771a64edf48b52c034a5af0b25af4cbef0e4bbf98521fc12060313bf4045de66a7c062767cb44b8e8e34124655c1355db7cd8e65d445131ccfa91db9fa42141b961f266020621642139723619bcb492ebb9cbe366897b4f7a9bde3a3ee79e628ed63c4a8c9a8538cf19549174d707cb43a478c2ccb79fd50dc84469262943246cbba41c3584b290b6401b111d02ee11acb83760a2ee228dc85ce746176738316888f5ca17cb3145e62c60c5bcdd96aa9a5167783f1a47cf2bdee961810d09c966559b0832c6e6101a61a15a60a1d117404901653858e501d6952982ae4d3e4d9d7a1eb2bd7a8908f3294280ae549108c56e4ba52d474648c821e5d1995314a4732ea418f9de487f341d729cd3d29e587bb9d5f43ef079faa764fdb82a24bddefe7492b616ac73e6d846bbaee76f53d6cb3434376b57a2693fbaedeae9a449d5c13df8a31fa8b5376cb17dfcfd5232403265e85887084082e782857e8798ce21a28b5c728d819d7b0112638a669d46b88718aaea90be6bde527cb3545f2ce88e67d7a1cffa894ef4c6018c9560b92e582b7cdd217e60c2f62dae3c63f2c7811c9f4efd6481834f8c758ccd8f1bd27ad6d2a31336e669cf15b3e65ca9431fce39e949ff92a3464092b4479151ae2e44daf4243821ee3077b78eb49ae7b2f2597c332b306ee668b9a336dfbb38451fe1b047777b708aa709d9c1345c3401a04f617c9febcfc0a74d576bcecb5da2a7ba4a998cf8f702d85f9f22a44c4e7df8dafe1dfead538f21c9359035e17f03a233597d59e4a90c675374bb345288ceb9cc43557b482d969100422c0380f9052ea34e824c42cb1cde5d0a703a477325118057d8f483759a6c9151dd9224641ef2be6cfd3d93601b1104298492955f6aeb2d304610a037b3cae59f68fd1b26c009233045dcf5920eb277d41948f6e8af105aa8f0e4313bd06d7ac7c74264f18f231fac7ef4ad09f5f464c7d92478f9991f9e843f4eee6c7be8a8a0ab632b80c323c3b8ec7b8c3f913c9f57b5c2e1ddb4e248e87035e3a101dd55fdc6987035e3ae0d9759e784c4f278bd3f7fb415c908a50d70855b53b7a3de6f19d55f9e8af6a876b34ef1da2aa4a34798751568cbbd7e5962f84ccccccccd7bb300c7bd8c31ef61e3fbf72b08a519d4eb4c264225cd3a911fb9084784e0ea93b35daedf93652231e7284b091ab35242ee65555558ee4bce5c8970fc3e232f3427f6fdfbe7dcf79499b59da23754ad429d1723b6c1e605477e05232bb2552378a2e6841176f8057a11644e12f873fc7b91edce320a739fbecd0df426ecbf4f9cc938eeec7e5e860ffd1bccede5cc7cedcb217cb01cc065aae7dc3e31ecebc4a50d88ed370d3f71e5618d7d05ccfba914042010988eedddedddddddddddedeeeeeeeeed7fe36aeb60a84b0a89b084208638c511e7dfbb432cbaae16eda5d2965f654302894d7f01a30a6abd7b4d9ae02313bf9d96e9380e8deeddddddddddded6d1210ddbbbdbbbbbbbbbbbdddbbbdbbbbbbbbbbbdddbddbbbbbbbbbbbdbdbbddbbbbbbbbbbbdbdbbddbbbbbbbbbbbdbdb1ba7eec35ab10074700d314ad9df06c4f3a8ffde7bbe1b7ceff9b212a229a4206af22a059112a223af52101179f82a05d111afbd4a41b4f38f1bb2c339a14379c6ae92f6d95d6ec246b415d7d0ca0e102b28d3722c40d80c1d534b26211730f15d36c3c42887e918a0fae898aa1241f9f88451ab2a568c8a969326aa43de648a8f6e85154b943cd0c76d08a3e2e633e313658ca2b3cb184599a38f333b333c7e8451d191b4acda32808a0a3c6b6d3eab28340a2dab6eee86886b66886df36638fa1c96636ff3020d9e97319292a58c943232333348d826c647464a37d32373343333c3c4ccf646846b36235cd3d1e0d986700d1be16393efb6221b137ee4812cf9e83f1fdd022413388f6b0a9339923102637632324632473bd09f8c91cc51db40eee8bb16a3875d41b36b319231d28c7c177f70d8c5410746c500bcfa8c9f2d8c742d462d47af2ba96e8e8286188a8f5b88127f7865622c436d73ca01f6009feba4dec6e2a4635a76b485a765876be959cb598856b6169f96218c8ade42846bb48508a3e266f9cad62d3d5ca32d3b2d3c8c8a3d3ff3b316a38e89deb2e2ada568a885a859866e28f5441aa72b1475cb8a853eaeb8eaf5c5646b3122aca5a865d53637442d2b9652cb0d4ba9c432b4033012546a6131e29aee86e8a60baed1287cfc6276322d44d01fd742d431519339ea98a97552957745a523c1b718718dcec71ba2b6b959754cf49b159100fe6625345732462d2be93246ebc307786bc59b7dc55b565c2300b8626da9a70a473e7acb11d7643e25224cc0acaac0f3d14baab679a9e8dbd6e6139f997c749923ae89e1d165a4a8b806db99e1e11a158f3ed3137b656e39007931b68e65e8a3ab6c1d8b1320fb5a8b118b928f2c442d48d606276da33eddb2c33632159d652833c242a46d34d542e4634b0fc6752cab8f2c404746dfb518fd7c44b5a8b84683e4d0ea9db044f9c872a44443961666a743ec12071dba9b0991d10b11932b0cd1e8b6f887086815a424c6251fe5bbd8e4e346c72107aeb93cba0e0e81b6811e1dd5dada71d761de1695ec789625ddda7c6044244e87e724ce3bfe6c5109a3a2f316811815638b518b51748cb7cd8751d1658b51f4962ea2978c442f2189de2225ca947a3eb6187d2cf97c2c11f95862e26349f5d125cb18714db7f9c87411adcd876bbaf8636d3ff187013249cc7085c168b86a3e946432bf9cb7abc6c59d5d8e61986317e6190da31e8a5137ff9c6b4ed3db65f82315616244a00ecfdf90d1c7bc0aada0c963264c48abbf4035e9d052ce795dfe7239bce40b94ea2e1ec345d98a31468d00a3decbd649e9b2dd306d9d8f1bbfae33b616c23c4946bdd931cf63b88ac720248d875cdc4c6e6d11d06e30790c5f39c2cc5c8894dd5c199a9251d1575cc6767a7119eee2733bbdf8f47dec7271d2d6e950b6bd24660dcfa516d7e6dd61d47321afd4c22282897a1ed021033b74806b48965f9b591649734ce34e3b34c75c87c661aef3bc759b6d1a89b4256069785de92d08985e362039a6f390e12e2a3e37fdd2d6b2b16c56917f2ef563b0ab780432c031cfdf0dd674883998feaebec2ec7688468543ad60767a5464ade7be0225af55bc0a1d41f4d7268451efb15f9ab62373cc75648e695cc7982bb6659b32cab18bd9afebba9cb9d3e981514f95630493e442da66536f07e30ddb1688513a7f394effb0acb844b41c0e04ba90e5e19a8bcbd1c37b2b179601030cbad5ddddddfae4d4ed87aef4a72b04fa6d46014bc6f7dcff71caa9a5b5609ea437155534a1892a05dff572de33b422d78e6a9b1d1ac3ce196a8651432a4cfef54ab79c82294cf714d53148d446cca5360540996c7c7b02be3d07aa574650607e92ea59e68ab9a2b8e6045dffb9b28c4585d9b9b7cda65ecb9028697b33bcb8240ba60ba63f77c675341eb40f0c3fcf37980f3ec6196a3b9a9fb939dc204c6728b3c74366eb572d8744c9fd1883e9015912dd16e3563e30e125026cd394b5e5f02e6e7501d0da4e14c81c731d32ce75b8fc7482f9438fa46bd1eba47452ec18d7edcee573dbc15c3b867442c1fca1479fd31f5d53aa08b841e75b53d7a629d2f6ecc3b7f4a8f150c0152184edfd3183da73ebda9204e1d8b0bb9fa622b920dc6d3d68044c880991cfe99fa108a66d9324fa7e0e6f0171cb615529c9adc65c2290724bf2e07b49a006df7b71d38127c98f9bef91d36f39ea7552d231d5765cdeed8ef42092fcb8f99c4640eb939ba660c73e3cf36879e89895ad3681fe6f8f724b1284adee9650c5e282f0c318b9d84f064db0dd0821fb5adc2a0a42b65ef4a6817d596b7fd90bc2d1f2b8fc8464d599206ced782a954a85eab409f4f0a00960124bbe5de75bd3a1f3cfca3888cc5adbf19ab755a4b726011d6b9b88289214798d02c9cfbf468164898fe10ae38351ac6a5432f97b72fad02e94b7d71b76ef90430456b52d0f2f809de5c9ec7689942a52b5d2a452c91b4e8956a51a5b471843d779a24fed1dce43d779d8dd32b80c32e0782a2a2a333c862984b0b79df2cc2aa8e5606d9507b7fd1e7a6a8e8703989bf2d8cbe41518b4e445ca482c98eb9677127b2a4f6b2566788bfb3103b6da0dd3fab1446f2d7c980dfc831533cb728cbfc7f0cf5972cd0dfd5dfbe597c6037aab26010be3ba76ac7940b7709a084fda7a48d7bfb61e923bf1780ebfb7ae71bcc5fdc0f196f73eeea70202fe20f1ad3801e8cd03728fbbb87859980d501b94de30406b200d0f1d530368a780873b3c8e9b26d7addf6c38eb377072200e0e0ea3a06f356eda665345d3b9e6b4231661c4847577c628e8d625373da9dedc18d21a2a264f801ec29f274f20195551c4771ae5e1d6c0228c3c74996e4d02b108231320391675eba263be4e729c3e2d74795aff99fedd0dbcf9ab0553859078f21187e3b347958cf0adebc63ac3abf2087b857c559b7d8229d7e82fefeec218ccf5ddee6ee70a5ceb8487edaeee2ef7bb0be51630551bdcac92555a0efd79011e6e16f866d2c720cc3ddc34f0d0559bc0f59d0f0f39fea89e3d6f3d687a891897d81526dfdb66bce95f54804819eff2728b1a03551e02f9b6b652cb6356f63696c764f6b695c7aeec6df3311296a93c96656fd31ed3b2b7658fa9646fc31e8b81ad6472d3d4dbaec756b2b7c9c758b2b759f1b152e682c9c05e3253f64c2f3268d050813a039d04dfab0b804f9ede877def0dd154bcaefe3ae27d9e7d235c311c4a62aece44efce043072a7fa080cc8f31c0924473eeb4cfbebefd6da55bd99532398c27470a536a45462f74ac50839526a8bf01e14f0716f6580c912c7f072531a41003f805718de223e60f0bdfd5d2030551bfc3a70f7067031eee9adf990a592ac384337d5704d86e6b506168c05c3b07ec9bd8c073fe6986a15d081c1cf0f5f704114499ea0450adb5a38d2d66d5dce4b89d32b5b13d3e5e623fb26783c35b912c7f4f031627093513c845414e68a7539daf65848b6a9a230922b76bd692262aa74e401b6fe38c4189555e3964939e7fac88ecce883e65fc78edcf11fa31ea3a0e6d0995678046387afdacc6df9e39a2bc394afed726b93cefcb4c3e1f76d73cbddde8340879dd9dfabc63acd598be5ecf7e8211da7ffad240d99ecfda4a5fae7180d0899889cd35720176311a8695ed2bcb4e4db5d1c73d976d80e21740dbae6180032172fb9b8b80c297d9198e2958a5721224e1e1b806b37649e43804c5130dcb6b5b8a954728d089b41e3b19a2683b6a3e41a94f90264ab3622efcb8c9719dbfe0c07c894638c9bbb0c0000000d17ef605cfc72717171717617937643e6311c2b40c644dac6649a536374d2f64cda8b464441ddfbf102fa91c92876c135472f1a8f76d81264bf85ebf63b93b768b1886b7a6891886b5c341e7b03e8ab49c072e8a54dbfa4edd04a0e5d82da668b3a2967742dfa8a76037fe9e5c565f80c8f32dc456dd0f0e86f076aa3e4d1b152c967b88c8dc6b6bfa249a0e42e2e5e728c1587696cfaca9d7eec671ee4fd8bff88f12f5efa92bfbc383b76008c866c5fb43063fcf57e5eac30bb67e44b8ed1bc4031820412d676b4bf7041f686193154b41d325c4a25676d47c95d7cc6a6dfda8f7d195eda82eccbc8b8cee499ef971c6a3a7ac8f09c7d1737712f50a2bf235c238333c2352f5c278309ae71e17ac870178faee2e29ab643860b02b2551b37f4c83c67f505ca4b1666c73c0f886bde0fd7447f4ab826baa6f1b05c7aa6ed901cf72a06559451b2c49d54db01dd722b13c043003c74d5cee5a1f6307b082184d055482d9b7ee62bdbdc72b68b4a7c74150b8771383afb700d0ff7c420cb61e47889b615f9e731a86da2121af3dc07a624ff5c72ddacc1bf87719dfc8e973c25dafb61d47b3fff9c89bce71b856ba4ef839cec1b357ee7c56138d03c8c328351d752ede68524a6f8e8564d7f8739740c6e9d0f3ff4181e3a4baed178441e3d06d5773efcf070ca639cea4ce7c3675cb74ec29cbbf5cb5be3a13365cae33417f1a44de5d6c3e24e3c22f79a63e8eb909655a0da588976d9ab110460f289e218f68602734019b6c91c436913ee618ce3226a7148a07c87e2b70a7a2b0e6af2829a25b7ea1f91c590094c61300871d4f02e5dd1e9d87bfe84306a075a1098ac13aabac393fcb4d2ec717ac411b51cd39dd09a7668a3e2aa0e90bd6d141616967ec2ec1e62b8663a3aa707e83cfd30ae33b5b06c4056650302bfdb234076cb81dfb2d1d4c028292402fa19a3e00fb3db22295909f339f6322e1e16e96a613a8cf162c89fdeed171eae638ec3375c034d2a2c24aecb81374c04104218218410c618a3090296b5aab13bde1dad68452b46668859379665dd80ccefd15852aa919496bf075f37777777938630b19e82bbfbc9a92e97b31aae32cbb22ccb18c60caaa65d2998cc75998a4a8c1899a692614f3c4163730424b189d099e75db7104ac3ebc284be4898ed108c09b94e897cfecde6d199e7cf572d987c5d6f0a463dcbb2a2252d6949cb82dc1b9ac1ecde50f3fc7ba9e76f75d27fdd73f2ef0dfd7b71f5c5aaaaaaaa323364c890599939d5dda937736f2c2a4cbfa199c194614cb804ab58a5367e0912f5eb8ac9ccbc8e13338f996b3a44ee042377ca41f620b913e470907e651c8eceacab8280fa0811938f8ac74775e4276868a52a622315cfb3ca4d268740c7accbcdda3488e1db32b9cc7169063d1df8a21648748610118078604ea0a854480a24cf39a6679d5d20966481ce701fa134b063fcb4d0deaa63d852593f583e4f0b4b88e744c32112750c933e6b62766f4514571d1357912876a8ef2291bba97d22d133c7b54feca1f6199aed5d5c75a607637215fb339f93f7c33ccd57212770e1d997c6290573817c74865d303b8bc88adfaa05d3c8e358034c6b669428ecc33e6a83023ab3fe24c6760d466d6cee6eee488aa4488ab1e31639534683e9dff354538b71a71cb0cc31d74969ae93621d341cba8facb30f1361230cc4413cc42b76c244ecc347adfa751f22ec73719d0e459aec8aa933989de977c3a2cc0c302a07263e99dc999bead4c9af3ef9c5a2b30efd931073e52f57264d3c79d574acfca99d7d6918b5bc43a67f879222e5b31a4cccc7b37790f9d086e819d75c9747ceb4c97862d6e01c8824f98b0e372039310234740bbb4bdf2b126b52600a632ad5d829316466c8dc2d5bb6ece6e676e7476fcf11234d4069e209067b746c31240a273c79a59945861c597db7aa2343ba85196502c999df3dd099f5de81d2b04b56b52ab5d13bd0a670645dbf064db683302bb341d10bd980e8d75b4907751355defde773eae89310f33d1d3afadfd3a132b2309b831c4ef3ee70f36492cb6e30e58a8ea2eb8af95cd3c17cfebe989793f9fcc2623ec75e12a62a73324f16d04c70b9e61777cae1729293b81e4e7c71a71cb01e4e8c7127e670c03c734de9a474521aa71dc340cbcbe373e407286868b5aa22a3239e756fae3371b63550c7ac635bff6cfdd3315107d39f5bcf9c98f21a8230477e10d903ba6579ff6861aecfe59dba3857a397feb6d95c8e101e740a46a91345fa677d94a89013437e7787d44495ead92ddfb6070fe4609d8739f235c7836ee9a76df6078d5917e291f8691b4d0105ad090cb3d359cdc2547f5047f28c30acff76964355da50304ff6b741a8810a0a234a64a8100dba787cad62fce1a334c442d962763a14831515183fe6c7feb8f7e4e97b33c014667b0ec0a00f264c610af35cdf737dcf155e61ce47b3c214c6f24c531b15d0d48324b5219f442291bc06464112d7eddf74373434fbd92c3509424822912034a1b08c44c27cd6c048369a5a276598c786b213f2a49310135e4dbaa273749c9652a3a633104a418326424650f0e80c9cec4eb561628216df3d28b13bfad635d731d783e3ebeec1dedfa37f7737086b9592404c99201c44a32e78488d9ed24e16a5c86d9f7e2a00da2125482c2674f5a0e5c5ec9667884504c687e779931cc33678492bc24b5a11c60823246276cbd359416f148f540dd3569035f4e20a968cd07c3423b41ecd08cde7b520cd47f301025aa0df2220219e54109cda49a9524108f1bc0a01a55497155715979327f134c986c87a3e5b92f50cc1b3432c94e7210b65a1e01843e39082e3e835e807c7118e2a09ecc346708fb211ecf31cf43e4db04cb9fb6aeb6ac932ca8efdc1d2c00d9fc7fd618cb3f89e43c8fcfac1a7edbcbd47e4dfd0fe08213201eba10e1146bd7e726314cccb9df1f5d8cb5aea8cf6c3f8989545f898ccacf7d895495276611929cb3012a6d2faac40e98cda405883519a4197d0650ff97c6453da48ab6378c0e4bba74cfe31f9e7dbe33dde0dfa181402681870d303068129d7e8d0bf5632f37b167347d507e16b4d51caedcacc966c65d54b5a96163e108275730a8ddf70981ad4e2c2e242c71c0c06827c9024e149164d9c90e226b0bb5c36324485912394e684e167361224630e848cfcbc0ac5a0c927e155680931fc8ada1044feb311c4f4ef397b89c9ea9f90909110983ec7f43bc37799f6de5bce5cd7344e41cce67abc85bdcd1a02733ddafb7b70d7479e4c21b91fec6154c3c7dd566487de1b7bc3f66d1dfa4f877e734f9be81c30f45dcedb3a1fd0890a87141c4738c680a3c25101e172725571597139b99c00653d194f3644d693f500e1885f3bec7c642f7bef590cf45d0ec3854a9686d79bbe84f039f6c37a0821cbccb986f3f7836ede6666e6e614a80ee60d65323bd3eb493b1afec1b05556a883b1d69c433d468bb7684702960675c85b3cb1f790ef264fb334acc7ad5b5576dd12b0346cd4e9218cfa613d3fb6160ee92144d13502f101c309e82155989d3a81c1ea053540a1867fcc0d27f7bb5128a86a8d2704a4babb351c04132553da1fac0dfab627979854d01cd540c94461aad01244be332dc1f39d34d540c99c5895ba7cb60d747689c51dd11bca6d8747a6ae8b93fe36b8f1f0d1b2a202b678e59a3845ef41835223783e02f86a5eb3478f7e520e07e891ab80a6184ea1c4ce179162aa41345548892221258808294104039eef4c39b02a05b7f6b8ed80dedc536fc2e29e760c8c3d82f4abb7bc6a3c08f0ca35318257f39a7bca141ba0ab4e99927a396c011ca9c769c7b47b81d9bbee6655556606c29fc3af363cef9a79e89429fb39fc8ae3b587742301539824965f96a5298c03a3dda8340e56553d0a118d00000000a314000020100a0785428140281ad676457e14800c828c447c58198ad324c8711c858c41841042082106810198119281260062f724b45d1ebdf7eba0749be43c98665c9a8cd908f36729ad3985913d2d0f9147db4ddf44cbc8b28923a15b44d9c8d3bf4f561784c8f68489b4b38a4f87cf06dde3535f94f98ed5d20b31d5fc8dfcdab89a928fbcc8b684379e1d251110eb832a4f72cb14b07f9cfc3c1fd40f2a123d03f534d0b0b59427e98d611aefabd5e59397b84981859f5942ae3e2ce9ac160df315e1dc606820b93c7e98dc8931bf82e06deae20942b9b40f147186568950146a8f7e5021914433756219d67d022935dc534f05372a2ce5b054d035bb1a7a4d8ef575f5f5284ff257c5899eda7741d0e994a73df9c9a0dd2879e22606245f0b7fedeeb029fe0d807426bf36c49f0cb35cfafc9cdad1fdfd66ea3083ccd2f3a312eececa925f5e1ea97ea4fb3e9e230a6d9d1f9762ba0706eb4b76353297dafde4b389624406e3703e4f80204c71db0b27004db232a253b92a2cf83841c103ecea249d8b94d38eb53a89de0d93bdac4eb292341e10c085c2cb7fe076b1854ec6b180e000f2d579879e890cdf50d69b30722b5c1b42a30a1a0b590994a64707a0a00faa1b90453edef5f515bc18005230f88b32f502e420b795165427e9ce803d7abea74ea2f98d09fd8a8b53b6b638d07813b79f805471636b6ed1319498a9a0a7ef70f631cb463a6f4aa80cf15bcc20ebf62854e8544529c5dcd16e5e2c537f7eaa7e40a1db72299ee0062a81bc75aee38d408de6ae13e8ea408fab30d746e58bc1805c419acffd7a3527a05c0b79ab82e303b5e252db8cd2abd9f554749a028f88b0af42a87327bc193bde081b9b3e71a76be1dc314bb4eedc719b9c7166df759704144a05b3d6fbb407e4a5f136f641f1a95269db58923ea5326985a8a16b36094563d78f27ba30674ab2510f83e03864b38fa9a81fe1dd98c316c15001040c0e650a54a60d3922a1652dc6ee9dc44f5e7453d229e905f17f86089b599834a131914ec4b45588c4bf64448f548bfdd912016ce3bbd55f4b23a93531725bca8df9628937b1df2693a5b41c5bbb89c820ab1884ebbc55ce029037afd4f03098e793dc88a1abf8da9aae7ad702c1e5f302df96a29ddab521d15b729d77dd2ebcd48c12106c70e46e5cd110ae9454ba637d5bba249b3ba82c8f0b7286577d4b6d7104eddf647730f47cbb4e886e4796446dc091b383ee7e4bf156db768808dc94f854df72c9f4207b4314d7407a4306236792c00669d7c2a1518de007f7fa2e8e196f305cb55b09bc788cd77018608c616f70acb094e10488459a849eac699db66f49c71210c952e74f5bd62a13ba6e0eb4434a3b621bde1240db2eb39767774bc3e94bd17d42984ea1fbd2d80153e4e575c8267bfe46110c3d435b4b50db8b0635a7c5c5bc84274cc12b1ac756b8cd1fc50d7c0bc07f7596afff9e8d1a2bc664b5064c5943624240113c52a6b19a273b918ec2833fb045e58933d6b37b14df11f2e046043fa716f3b498f8406339d8b764ae1c54f3e01f528a53548a5679d927f365e69606253d91dbc271bc5544754ab0d6d85b2ea571fe40f30656f9047f74e596dcb8950a9b7134669f7699811213c2580c3ef21fd1b837ded817e38bfaed8fc8fb0b6cdc23fe18153c72ed0b18242ffba5b72ea9776ded687b50dbdeba6388f7ac1e27e0a3671872ed31f7297133a8631c1ed30962b0427171033019ebd19bc5487964d9975a9c559bf166be62ebdd3c80f8ac340f03b334e3513d2927dd58ee9563462041c564b80ca41e347f6d4582d9c46a045731a665a63457219e1b3f71a3ed9efb896e2c062cb5ec65a21446ba3d914311a3d1db3afdd26322b67066947fad2837b3c16d22143fdf4cdc2268d42f8338d15e28dcf35c722a42504528e272e42b84b830b165930a9b50d6a14e5be10eb313327fe87f189fcc0028dc47fb76f6f090b43e7d2c4701dab45fceacb582ed3b947c704745160113ec219fa24ebad16a51f351bf4884f4a0a3f87449f59a60bb938f6de675e8a41da58f50a2e07b3cb6ca8e88dd9bb0f490f77a2f7e245ae97d556ba4e0017ce14627107c45e7b6dbc17b30a3617d45571978b5bd6b62cd919d68d7e65918d0a627b0531b76fa7ff592a4b7a5d46307a48fb417f5c494d1782cc6a26fdd24b27dee3e8ef67c932fe570aa1350d9d60517334d817f7a9f613570a1528c7b392a56acbfe5bbddfdfc44fdd58696e9c19e67b4edd8322e3c7f1042bf4c16e3265696b30fa5d67bbc56106fad20dacbdc2782962c3d3f7a12914f3ba93fdcc333afdac4e098fc4f6bc63a7be2b2d61741e6acf2a56e7ac66c09a96934ee52502840eab814f1ceb485150699c463bd300cc34ae1d811d09f2ffdbd62ef394ff2ab97ac564f3774796554adbac7a5db3c3ecd20477e30db7a0652176ec0e697741931e5359368be9ca4e0a1b6cd0cdb8a2d1923f5c6cabd9ce48691dad91a6a0bc4629cd3edf7de6dfc20897c6806824254ec94d97eba0cec95d6f975438819e528820fcafc306e878d3ed7cbef823f185246fcef5ff49e85e343654c425a1f87bd2baaaab008641dee552b7138b416683d685b5bd26a037cd9c7deddd54bf8cd47354a3e23ed96b4627c1dd5297cb7d0b5bfe1d9c99999b686e371c3e05f577f0fedd780f5dd165aa9af28d64e03baf508d7f389b088b993fa2bdbeb78eaa7d44b9497dcfa270a61ae7f4ffe81117a5ff8c515f047d6c370ceb112804e04b7abf163aab108e05f6a139d26ba0ee7063ceedca10d465b8e04da32bdacc3390ec048112fbe06a6f4eef338832443a3fbfc1606a25f6f3cfecb2f197a310606ec455c8014f077cd946420de0d2d9409c421f53786bbc56553a95c221f884927038de676fe3e990519e82a71f633b7532b05f91e10b115364223e79cde9b638efbc1daf2bd5019621392fef0b6e8c2829c0540438bc1a0560b88cd0d3916dd0d78464338c64fa1f1b4517e645cd092255f4778bc3b4b14f36981368bcac2b33cd18e00fb7157bad91983d95d7fb06f352e333581d6726b110e3422113dd84b545bdcfc4d6baa2c6a6849532bb6ef0b64b19217106f36cc84d8bcc669dc5cc27155022c19dd5950ce6db15aff1b9f2fea116328c78bc709c767dc02cd30a4bdad7f5780c6309421e7260fcdc152c689b1cc1c108ee9ef4b2b2227f003652ca013f5978cfae64a0e9cc466e01caae8668ad42a2a9b63455ba7b8308c2f53df10b01ad2bea1bc3328caa584e2fa756cebb6e692ced329cf2f460907a69c26399db6af1727be856dd61df8c2ed83307a53135a222c540aa57a675834e6c669da502ceafe0bdf7a50ba1505ce92ad070aea699210ce0a298de7cf22ca1b48dc574361c8c5740ece12e8606f2d186fff5607cfb3f017eaf54a67e24c67699b0dcdf8d16400f8411c8a50b4e883c27f2a6ded361e0a8a537e1f04e46fa1551632dca855c26c34872dc52aa7d2d1af35c96c16739c93091204dce68cd7e7fadabd9bffcd49051d69880689599593f02d4ac67b128cbcf3b4102e2c00a5bd5db060cefe41d148f22844dfd078389b917d6eb636cf213077ef63e78d46cf6db03bbd8a5cb60cb67869a342e1788a4441287a2f70ed3bfe38b84886a5cc1ea877986ee749533e3c17f24cc4dca004d0088a78526b333dbba81b596fa69b941ecb63299092a7ab4da1e4433b298789550a811fc348003cc75f684126e26bb72b0c1acfd38cb3e506fe7a138e7befd0490123af28df5163b1e7f3056a9e295abc13a503c8eb0ebd50dae9659123747733ceb01be44c2e172e53c1173111526091d3186b8cb33c327944e3ef5fc9b542a3cba53c8b894b575cfc9ba77210b3c464bd0f6b4949ad1ab3fae6ba9190802baf8b61bc2cfd1b4b26d34ecbfa1f1ecd140727c5d88148aa78c8343347ed03d43e4d662152f2839da17036727e662611dd7379f6bdc474461801d938f8e719c835b5579c4ea48e08b0d3ea9f17eab38bd0ff8139bcb3820544a38160235521b9771272f84b1f716d40d68f377d63f81e49240320c64e664849948910164b672487376d6ac0cda70c4c01b1a67a5276b77403209296d4815a1b721b2d042af07b63166d789dfbd5090eefdd271eb809afbf3e124de0839783c898868e65f297a5eeec5b0e0720aa1337171f8467ee2fdbc20c8b8b9c7c84d794523fe55551b742f3637f01b65cf0601cf0c6b2ce9a2015497bb9dedcdd3e819a38a2e9e414496442ecc72d7a5f7d56ae5fe9fb848d39dd9d55db461d905b7f5fc02ebc9f5b71d2ecc6d556cde56e6f736d3e3b14878a773f96394f0c20602650f5c7a427f9d0d7d62c0e2eb08f2bf389a3e519a59795b584e06efd0427297d4922bb579eafa29e9a439e3763089d302d7071bc760e3894877754b5c1090f8f0c6d5c6392613ce006b525a9e064a0d4848769a82d1bca1afdc3d0df20e814166af321f38bc346a2807d26cb59d9b3f23460a8b9e9e57203e43a7d72310078b015a043db5d8fb592998794b6554cb089698ba3a89881cfe7d1383a8558b858491d8f8ab9d4e3ca7a155d9768e2dc8e68cd658d4ccdc798517b970b21819e91ba6e8a9f2d4435c5ff3f5e1e631a9f41447ea4eeb196fb2d0d79d3fe8e44f4f6d533de9958bf9c5da71e1fed44184a350157d68efbcb06ff343095714a79b7400321a3c870ff04241b7ab0b202a6c0fe92ad0441f30346470ecb996704b496dfa1ff78f21a64a07c8aafee21c0687d28a4e48722dde3bdbe10920c1c3b0aa4a1ff7bf2c590c734c786b0d9011fcc7ffd9a1c480d64012914f7e1f2b337f00bc9531fa8b28e5ff3d2ae38f1ec04a46020c3d6ea25777a8cffbe9aaaf646599ace16f23f445d376cf407d01291b7602d1fb79c45c90bc96a021856e2f34e941d97422017e638bb995724b563b7c45335bd81e002121437a30b9f2d3f470aa0abbb068cdce3403c4daafa0f9e9170e9d5740daf1f048ed3f759ad330d273bb48cf0d4ed4f6f78ca4d37967ad2376ddbe9e663702f04fba0eece665ef056ebb3d908d64d73808fe5d8d646e17b648313a31a10635e47f64063883a9d9b69b91546a6699c3a5cb52e0b969d119bc841ab17ee2e5542e94ad6d90d6252c466abaab40092ca7dd267b170133ac0383fc5ae7308bd31f99f9721a07db4de591eb8df954895e21e570df5be757ac9c06f657c132e3c65448a068710bad1d24406c589da9fc87a1b7fb84974a1eb4a8a522f6c79dee71004c672ea703339591d563b682dee4540a908dd3b0bf8538209557f12e5fdd0eb21f32cb6e17f7ef4c45737fdae22fd80869543dca7132f3c5509140e62a1fd2295c0c3cdd3b8af65b1531da6740a9d8767b3de16fabfc109acdd20aacabc220cf53d65d9c15d30f182932550341886b8b1361259dc6f1a7db89d686096955fc6bff9b8157bb1ab40ca1de4d1d2c8a8586fba2d1756c20730df47b1ab17a2b75ffff33204b48f7a7f98ef8adb62697b77a6062bb9ad742a79581cddd14d796413c667fd5be9b04db80b0df4c3b4a26eb59bfb22bd6c847ba2b2cf6a448a5ea27ba89ef57602ba16b1dbae2a090226f5f1225695626b0184db0a06bc5a1945d4bd434ad920c1675c5ab98fdffa75125945d7693a2e2dadece80ed8ed32468df2b3215acb45ab833953ebb6038f129a6d5b64ba45b606f470dad2d12f98345f628f70301e28c6f41d74135d43e32f61382cc557bed5a0390972db4c58981ed42e8b3cfc7af181ad541a16509ccb79fd2b7b47c6e39108bee818bf1bacaf2bd32754c1d484e591d0259aa1a74dce2d842bf554d85d7ef281a7c8a666cfa80fcb735aa8948a0320c9a7bb59791bbef42f244d28a6bacce07f9d30d107db97e61f06e8bb772aa17d4e87296763174f0aff06b2fc11049369bd8fe94f61ebafaaf6373c4dfff80de4a51691909f58465636d6c29d9a7d82df1e53bb9b0a4d2f2ed99b7a7cc00d026f17fc65b987fd1f172e99788d85cea1c09da1a43c3fb407ba7db1479c5e34adca952828bc13bfcca3502533d9bcbbc22352999f7097899f31a149e8f60e6c754426c8864e8524c79aa8916987eb816ace1c846d9495131abb1c3d33ec3442980d5e431b30e2a5438953415276bd65c03211f10acba1193440235a9b5c27f0cde0d35491ae8e1888d10991eaba49f9745cb9d93012070ad0bfd1c9751877d2daf5deb127b922667d80dcadd1b98093c2de3dab9dccd1de97fc9fd2f3a319af0cf07f2bc6b30f50967f2733c9ede7324364497560d579b22d449b60a2b288c78dc303aa3ac7189bfac920ff748a5d954fc9dee62e9b779521bb332ee88c52264fc7e950e9b927f72802770ffe2d45983784600581e4578d1bbc1a40af4b75db93394881b843c27ebe20ecd086467351cee321114598a7c47af292fc06c908658acb811b008bbd779939e718904f9c7953dee9be6352acfb8b8730968df314c5414c2b0bd1afa090c7136d7ec6e53c88ffc99230fa5935f967fe78b67ec40203ca117663879e0853fd56ef73b731fb03b55e1655f5211805a7a684f935ec0326fc04507e9e1114f6a003f629301b622016caaa33264891c062513937404510c0986ae38c8e8c67094220508224a17fedb0fd15de02a816ed199e00cadfa535290610fbc177f3e1d808e57ae7e39f265288fb0d69b7de91ded7dd267a1056c9ec5b343cd0cc0b3208848edf6b3e3b8ba5e870f10b6d95faa24107aadc01cf05fc7c030e8422ddb99e7c83acf4770a5370f28d567db1a456074ef124e2632da7fb625b67e6cf10bcd8825cbef5e78e67fb10b17198defba1084af939a2863a0e433af60ad63dedfeebde28b6e493b4d9731f951e63c9ba4d61f34f8f85741c02f6c67299a9c0a3a65103f79c6d0a153792992b37e657f74a6f59d6a8f46fe7e069db14854827cc50ef9b7878be7ec24af1b90c5dc0e15626b8db18cc4f0b41a58fca7b53efd440c73193ef3a65a170dd3dc1e1e89e80e6f9085960e0cdab30eaa7225f6f5648493bc93537c405fd2a73b948495bd20ee232c9d1da5e1e9074d630801c066c038b74d13a8890a72c69c09981d244050215f3c567fca6fa03b192a49d5689fe960e33393d4c5e953ad720e30fcbaf5faa502cacc578bcab654f53e023c040d90c98a06e11de440f979333a4f93a8d29d9a6d6c031339eb406078551a8c408cfc233af1927c83e4c0c8ba6add176fcf01c6773d1750546b9fc517f812aa8b9e854528ae6c818e539381ccf3a2a6d7aafb918118412ae7fa1c7e8adbda6dc0aeac528fda2801dc8bd1df2911a99e9be6a085194f70929eb2ab665ed3450f2838066195688e0f0c381f955876b74aff6884cfd15fb97e9b062e81bbcc727f24b4f06edc37802b68be84e4f0f10419496a914bdccc13a43df90c71393207a9b92b9d258898f0059cf62e151061959a5f52d152f13382bc60e0f222c3383b3830ea481bd83e7a862550b76cc5f40446a35c1a7d2dac9a63f01c8ef30de51f4d604b29722065326df2dff0d024271911c6ea09e4d024748ce4029b573aa5047c57aed5d18868e1d9ab1c2be643bb92f7a62a2a1a9e1e3e3b3a85a2e628e3abbb75a48f55cf52f188ca80c9881eaade02fcc184c986f1afb9acbf11a4c3e22a5fccfb5069d9e820d8155cd998554ecc5e1d0115de51cd4779eed882a9c9214cc0850957d24892aebebe4a7e8beabb1e0ae745cd0edeaaf5fd47ef34a0bdf1a90bf3611e533430b40e90b1ac7f37bea0c47bbd6bf89089dd557cfeea717d5506e8bce3047febe8f587af00fb8654d74ca8c1fb97cb492146771048d0e7e65ab28f05f444568485c6c4e75f29c70428d6b2310eb68400064453750ea2cfdc3bd2761a4eb434530900f220b00f30afafa8fa50ff1794584e5280cbc1111abbc9fd86e7b89e9b0395eb63710e33d09b288d633ba416bd4026a7384cc21ab61ac80daa7a3742f5db8ac356392d9fe4535eb78b9e46c309310e948319d290523ca256cedfac5e3dd6e8166c0382735c84d7c8be8b60bf156b1d52b4e6260a86e3a65bbf1d37b5d055ec2bb7b28642efc5f6fc1fd96d3458dc2c82180cdd18f2b10446fa97ce3cd75c5d3b26da1bdb5fc6caa3afa3f91946ff28c486a03d76c8415ee5b4e78e4e3ad874e44dacab0b72137360148d695a6ae6f44c1a942015820fc3770af341e03432801eb8d2a4f4fa7966b91f06d6f1ff4b22a60990f8625b704ed05a418e7767aa22dbd21622108f8dfe3b1d66d154a2b26137830788ce3b0c172ea0b4c25de369117cd86cf01cbf5469a45699e0f871c11cf4fe122613269e44f843ad3fcf17356016b71c939e9cb22be34777c636af47f20fd3a36ae887cf05ec8ae605ef8f936a28838c5bcc5541042990c2cecd2afbc158974b9cb6b05883e5064b13805f8c268b87aeb82c37438f713035c0c4a79cdbd6e1c242486b886c9ae77abd5120ac5cc4c4d20f43cb136e45d3e45130122f6625081214b8fc6049f39c79ce194f00e38058d979a6acabb4d958251dabaa401c979356041d2102db979a7e0d6a2a5a0fd80a56c20e59be081298c8b0251a7005506edc09f560c3201c225121eec2c9655cd1a1a242336b46949f8ebc309eea75e625115f3eed499ac9452c8213043c08288665ddd8ec4b21f3cf527b07a63a0d73cb8589448f4e3a3595c55db8a805c4a56d62009a0a41025534b4d1bb000dcae93f515e419f00cd20117282633624b8bf31bfa995ade42ed20ec1fb795e58af64f8615443b8b76e0d0d9fde6a2378ccdb3188d9041bdaf987dc212e9b65495004dab12b210190c90cb1192b8aeb95b560a2b857a902dc74f917798b2e3ae64ea40ccddbc628c0606b971918563d1ddc93a2b9d457ab6ca6c7e8f5a10653d83473d433e89087d5b2b9775064c91ea305411990f2fb3310b36c6481f7a9a8b9a56b5b2ae0ee24a229b468ec4ac058d1b608555c22344b8a667eeb41100219444caf20e666bfc4622e72feb1e22847cf72c2f3d17ac29162df687ff202993ae9e004fd37ea949b0c6f922f7ee9ff212e4c3581120d5499a6064c65cc41f679c86905f574cfde1bfaef4bf00362bba6906d09ba81460a7cb473c6485c32e0c46a1148950277d7b0a72528bdad2031e72c6a78263c90cba40b813b3808211004b7f7b71953236cbedfdc5fc0bb48efb0351e3bcd6fe6b1aed2d486fd97c4b02dc5afa3d7bfb0e706ec580d7d7fae210c811645c700b994ffc4c1d95f77c59646e501dc2aff7a70b0c6d1161c2da8499d957aa380327a68325b7aa0a20c2dc24c17897c0ced5a57192f472424a80b29e2ba4cf44c562184998f614a537c543a27edaf454aa7c8bd4e240172eaa8e29760960d7eaeb3f4f647e986f45fbfd5fff62444047b5879f71c926077187157679c5095686517c4a946c574a1d1860194de8003626e72eb5375c990deda09bbe188ffd5cc468cb6ed074b2d6d5fe4802bda2854125d2c19c478c9f718a80456db775b54a12c2af420cdbe88ff302f029b34943567d1c2efa73d4fc34d45c13cdea7ee354ea6a7bebc475365b0dd225716ca62f02f8804ee66845b5e58ae6898127405d18f4594eb5c2850bd33d7829c51b2a1762b07846422e174a8c00c5b2a2300949a9e662201af0cf2ce6b1a2fc4976788c9e445c19b39691aecf8d92a74cc2d5c4644f7f52f35a27fd9462c9c514543ca85b262a759434d742266647f1a153de1aad497da67c96a978dc735fbe7e64d2502a5b10d46733f96d469eee883ba3b2ba92a6db6d861c59cad59dd9212508006ea7073b596de714074b024d93d2193b968c8c7cb785f91c2eadb76f9759f91791998aac20131a96a9e0194d0bef4e68c87349cc5bd3e408e818d3d970acd97a9f88613ef6732b8d02ec78481874144b6a51ca327cfdf5dde48f83f5fcbfef2f05fe67cd9da0252e9bc1047acba68b6283184a78b38ebea8ba4ad741895a9545d1592a0e37225e5536f0e43a1c94799122a6757b31becdb58c3b2e5f01b109e7e68e9b3c7b190b694cfb96eb75c725a4049b302593fbb728d24748252e6baddcb3843defb844669dfd2de02d233828a302fd1db8e60c4a7a3ca2e11cff24cabdac00131e288252e66c0926fd466377017a9c8ad6dc8c6d36e837e440a27170b23feab03407163ecfd6203f34391d4d7772ef6bc99e425cd21b8486f0518f98a66b6bf265b5667627aa9a8eca781b16d6f7fa351075840ba41125dfe2c38acc082e46c1347e36fa4830fcdaa1e4277f3da12702fce972439cdbedfa728112c1112e85ed79af3209fc65450f47741a23cccbf666d2f8c1a9cd646177b073981a3e9dc2f2dd546e15cd70a3bb98bcfea65f08bd8d3d1b1c85092346da638382f66caa6a78bea6db3442d88d7cce2a9b0f8ef6bca689ee99c5acd5bc70c16bbcf5a3b38cc63a63bbbf3402ec3c3d2451ecf1637f34ae3c66828d515e1f77bc8b7f456bd34db5b57ca124cc395234d8402a465b7c1a97e0aacd08c0b2fe79d242036cfa973b59826e0d41042d614766ef672cd209e70c09934db9bcf2523aa5596b427ee733f935bb26dfe59054904e7569e60e2a3142bbac9a0375b3a0f40a6632a93ec320a97cb314631fec14709e9aeba359815e6a7b3f82c6b673988d50b7164eb1580385fb084c0516849ced59900e57f873ef03d32a5f6c217b6300bc228e73ff0db56bdb8649240bb076efae226720b28800d123cb6fe20e8a4022f7d38b70dcabe041bbab1490497de8e7b57d823b273516ac1eea64e8dd3d82e7b783f8012409d4dc93cb5bc6f54dc5ff743ab9206e2bbbc433bb3f4068adae8540550f4ad9f6bd3250c568741f30260b3e18e1db76aa9a3afb26c2402979cd2257e0bcec322d2c2f6170a0d2f67efe2dd42f58b7e41a4efa158788bf0b99c4838515edea020ce9b439a0899bc7aa06c3154a3b50a43068880a9ecc5080ed3417c89ceed4e8cbeb1cc77c263d0edc2c0893508e7319d72bebdd37d11f6f7117d17147c78d24185f5da02c29265ca1d2d1b6f9be275186257a6f5b4a2b22c91284238825cb661468c1ea8647266e7723f84c9fd537df45188ac8b28bd62d04ef94a554e64160e144a48f7151858bffdc96143752fd5b36b49b1a4dd4bf4f8240a3ce91f17811b5f05dcb79205bd53fa9b78de382de411785bb9599473dbdd60a891f3b1946c2fa420bfa7343f87ef5bebd8a7c85a0cd48e76383b5bcc147f04838331c2c32d17e76a26dff7bf55c64c0933eda523affe5198d85d90b55654cf544c287fcf6c8ebcd56f108b3f7f5a7c348f0c34c51c4f6f9cac2f9e4f8a5b5823b0dec04a8673bb202ed9a0320cc85c339140fda70996355824082495f0f745ed2764e570e395cb144f52a00d88f08855f3ea622033c02d8ae9491fecc2c0628ce64ea65a51a43794c4e19d4273a03ebe5efc7473da38537b82183e38e95b8b75b1e837db538f6552d190380c168dfc292db0e65f66b440a5ce5df6be883db7c87021e2b2a2d9feb09d26f4fb0c71247b409f626121f341229059448a5e08e612b4dbe1cb575acea3b92ff75de27f84b08a6af5234f5d5075135474c5b9ea5c8c086186bf51fe431942226192036497412845dcc738ae914b84d52e3858f31085cd872e3a52511ab72f8afd8a4830278316717cbc03a6ae2d0c375a12eb89ec9816321e36d4059c80fd92f1271161159b10a2f7aa12002db0f524bb846c4093232d77b82fb50143b6be04aa08d3987e02b6eb2acbb17e4ecc9c89baebcc191c068b7ba87a8938947df11861f3918bc75da83419489a4c2f58b29da3e73a950ac2dc7cbe0d47d226a75dc755155c27c266feeadc5b43348034a227529217e7b0bec9b9d8b6fd78fc21602bc86727c91f9b6b6496aaaf67a3235a15959daffdf6b82df1fadab43b0e3f0bd8a23ef729a5cbc86ada6047bbdc2e285d5e8af80902c9caa9aa59251d2016a702c3a3010777d8afc276652399b72902442333ebfa8c37ab9ab3b89c54f87f26c7bd51b12d8ba912f843c149eee4799f3ae9aae884da7ebad2e8b62a761cfdbb2b96f8ff1afb4b37c46333a9956b19dec6183f51790bdd28b1d89e0b1f2a2d6c73b04f3624499cc92b75c1d039f7af981438dd076f9f36dbd800942c9eac0a437b6706d0d92ae206e1c9eaa09c8d2646c7b47b2d70442d676051ece1a29f6d62848b523d1a4b77369e38f74710dd19ed89dddf84886a98e1740e99fa408e5870377722bc047e3a3bc86045bf0f91f3a4f7141e2ce83c1382391cec4b5f7b88f6e69fa3d800581896acaa1c66b352086db7f56cb4c8c448dba02c5523bddd40757254f52bb52bfc702059ffa50226dfa5e4969c06a9ab0037454c78d727ac2996c6a624443cbee585489e678ec42abdf91dbfbd2695b79a5241064ea06d95296a8e9a8b1a5a81a1155fa0a2ffc0b909740235e3338f0187fc9ce24a4f2202adddc23290e87e95bca7c9e8940a7de1d0bc9b1bc77737a5e0109fbba7c172367d855669b8b4ec4d0cc7843fe3f124a1ea73620de99f7b96d5e212cd57bbb5457fe258dd1a790740b35da444e05fcc6f2180d1b91284d5d6b756f8663b14a422bd19c037ae23a8dbb49b1e3bee8fc0bce85686e577d0604183408ce3f1491b5bf3fd07091b8097c564a5bd1e99759b05a6ac3dce6640423184a04e9f7678c26c10ed1e9eafa219310a64945ee441f63520caa05f2bc37071c2a224c63e44cc2f9be2ba532886b1c779c096e0596f154d4a8a6fd4a6d01945cad4b9eb08a13af6c455f43de3992ee87b6a7aea9a23207acd42f6245d08d1a0460d17542ecdd6cfd2a0ac6c618339dbeab03fc246286722c3105a88dd71cde64b995ca85be20470dc063a8a447af16ea7fe8895edffdf88b71914013761e9cddcc0aa611dfdd2f41474a78cfb5e7fbc9c6ab8783b97c34419106fca13f96b7602cbf33057875e70f08ee9395f2608d783b78492cb43fa2e31bf58983c5336473dd9dce782b38360f36f758b816cfe126e78cbb3a3843ad40b22013e83c69fefda1bd42a95e05790a46e4948fd52cc0675f223b0d03994466e02d487a79c54047d237e92a15df38ae330eb087894c6771a47ec9e3c173b4a61603b3d5d013d4242db9a8a624ed7814c5f9aad001977a0a98427128339c9896b09d4d8944196a5c6019dd838182c4da85223e67c36a9c9832d828b1da9e503f00345c143719d85d5b8c43c374d5e410b68fc3dcafb8b9de7a92a40ca557af86eaeb1c9d09730365cd0ea7507795d964d704b4d3a6eaf495e80f4a5d452996e86245dc838428d5abfd0aeb001483851c85d928bc3f42051298a1e23b3e0dcca439799b9c084d88ec5be5b63e05d3668c02937e2090b56c49662ba515ad161d46e83e268c94a8a30a0d682a8d20587c4f3b4fc48769eccffc942a0d48ab8ff5c114c9af3f3872ab61c22ee0b648d72b9aa100ad18bbd604daffaef6f77148264b45ffddef0a0e67f4094f929afaa3e6b37de0905bfd27ce8559f29aba7503d88ed4c6e00e55e4c53ab62b4078985dc710efce1a0805871094621c2c2f60d96a2f1ea201ee50b9ccade0d01174cc15686b8ae4b97a0aba8711072742820a306b0e37af509f0005d4f7c5e040f49ecc6f2dc0aa76dc81125fdde2cec1675539081739dc539162ef3b897cfbb074704fd5b13fbc97be07077af47f783b257066be8355d2d007b8b7e38e90e900b5eab297ef209150260184a5e7efdbd5b231d1e3eecc8e9f0234743f96126beaf0b6a261dda493322cef9d6cc1ec4e28e7aeb1fd8ac488b0ee428878e4b8f4eaf3ea3422fd04300b0026674b6045ea8cd64580f597cc2b91b50ea0c2bb1e6f791fcc3b9abd2b46b0d265c0f10aa0ec20a6cc014da36ca38ea1f351e52c93db0e90fd7ba8dbcef8b4881cd46224d0f29f69c4fd311efc8d315b66293aec8bfd11f6cba02a4a8e73c6b3f5d3e60216b5e0b8d21ef10264c969cc582d669e5d8d1a6a363a8b2342b78ad193d9117d8094dc05cfb96ecf792696dc2b928149bdc778330411ce434468b7f10941516df7616e6b45291f4a7c17a7c2f68e62bf9a8a8d33e60e189c00fb2a90267dda88f935c2d7508e0e5146ebc9aa5091ac23374114e8fbd0ed0f31a2a4e8fbd0cf2e3fae6e2d876f11562b88180d8d331f51b5c9ddf8b06f1f0db0ac7f5ad149390b618e13d7ac6424aab66ab82fc42c431d1db93ad6ca5fe686679989c9db164b909e6ccdcbe4ca52aa98e778398723828f935bc9fabf89a8a13cba84dad3eaa50e7e64492958ef0b864b66e3a7d4a2eb5b07524c1457b2f5296d079fec9128d2111d1345d072edc76f3213e6495c8fb5652032af1b8bcec28f83dda044e2507d60774f13c47afd69557c77c6efffe9d9030267e9829a7b493ce47db6fcfedf350e5713652693c113521cd28b3a5d60d2f3398a95891dca915fe2eeea4d32e60cce335e9756b71d1ae5e6fe8400fc6c0637f5e6b8b403f4ed49598bf8118c55e73042d4e47ddbd21d5271921993af12584caab82ec917314c7b1f7006b95d657f004861fb4430cf437f23981c3dbc59190b64222a9e0f8acec8d41b46cad0dfa29eb1b6a047ac088931a537fc6e27e901530634a71aa351801fc19ae07796a10cdc6400c2304cd05b876e17bd381b9647d711d507de7f087a9696c1f0627edf4fe9cdcb17f1ac23038d67cb365026b47b88ba3b5e4182cce56951dd8036804c2b44b68509f609af6f5cf43a5cd5014fc5868848a32c65bf11b8477c8df9643f6c6c04ec5736c11060df884710a6bb518fa01296d91768d2f331b381d988a8f955624d9a880b2e1e46e46ba5a3459cb5cd61eca562ee69ae5d0bd1c78026138b680c81923bbde069b8822f0378882e8154e48521c82be68a212509a73c65b3359507195df9feea29d1860869445812426629d094f433d874740121110f2b4372ba0c77da6c621bfba4fbc402d58eebfec3e6305d362391b217f5ac16a176298f6ef79e5c5a23d98e70d43550999b6543c22fc07e5133c639994443130fe0945d091b3abf8d12b62168ccacf2e01a4ca350a6e2fad2bf7af8b8e990bc67fba038990907077ef0ab9170ee960b41fecafd09370f80281af6c0b56a456d2aeccbf32ce8ac1944adbc54c2ce27b2421b66bfabfb6bce2508fe0970e45e2014daae7198790f3be5aedaab867e5d4c83065a0a0dc7e8c6990424095b7ab5d6498ba9b253fbdd87fcaa7d2747dc489a098a71596090fa4d5f82730f00206b8283b6cae659b7433c5ddacf3a02230c26a204aa70f528a8be3e4321793d2b475daf06007391aff7cc8c62b2e8267425cdf45966456100db536ae4a1f043faf83df266d58253a828941631a48d6a7002c4c1d8e0d831b27bedb46fde29148da691238169c1ba0d53286e6cff1a97397c4f40e138c418c21113042a545da15e2cf5bbb75a76c24e6e11f5b0ca59e2a91ff8fc07976841ab4f55f7b805082c221add42a1b77f01f10d5c75cb7a2cda659479d4520c163f31865b30046ab2414bbbfe4988200d5020b07e498a94a5c66074ccb2028cb00b89012129756b63c9720f977bd9ed7191959e8feda7f6de37cd8077989a1c6dcf5badf976c20e39e9a1c3cb3a95730f2d96e1300031823164d2d33803c62ff97de1738cf0b4998576dd96ac378f65e5a74ece2fdbbf4b0d98cb9d1d2a100f6951a14c1d1c08e5a85401ed60f812e8d7e9f38c2dbf9b69d7b818b834d7c0adcd5277f253feeba6c7bb3b85599a3c55b3da100d96a141e7092f873d2800c220bb0be0fbf6ffbed9a002a10123b43dfe27348abb7ab653a9bc590fbe299c404a0c3a2112a65c17a594689d2b4881215445107c59abc4598a2cd9aeeee11501f40184453fca6eb936fedeec031bd9a00c3a15c3ed58f9ccd54bfc41dd4e6775f3f2eb1c416dd6f0cf1d5dd92caec0cdf6d40b50502f0549e2bd53c472930da208226cc3c4bdc7a571bca6e7f1891b24a677f03b4bebcac6eb27e2e254b2605ddf407a782d60d3f2ed16939d974fab5e76d45713d7423c2ddf4b244020190e64c5724506c8febc79b5be0a1bbddb2dd232be05543811a0b7f9ca2bc020b5d0bfbcb51e160f6a83b258b84dea23793227c1b930fd8d816b92d110aef91b2e4e55c71158f1a018f70d7881c7ef623c491ee0b910fba5227095432508f96ad4bf636e11cde5c31366bbba3432cff414e32f715806069781b295a9d7191fae547c41283b5bcd96234141ff7f542d81f8a25d80e839df60dcbb1cb9546c535062d941ae0899e3d708747180ef4a96fa6b5c8a0dffc73ec91077d115008443430354243c41592b8cc58d7815ae0f7830303a099132c6cea9cd0683e7943af3b14267b7d37443fa623eb79f8442201c02493070c0b16d21e6bfdb4efbf9a0ac9679df10f374ac8e1d49ca2422283b13e903880b91cf231bc4604e08120cd2b3424858f6a24fc4a2940ef0a78b91fe2f74921b2f92c3f1102a9edafebdfa1780bc3eae1d62869816e947c106a9264808be0d42e89708590aeb0d8f2c264d507ab5e238dad876992afc73ec58b196451b754717267fb1c9b841f1e3daa3728f3e70998eeab553701d336d4806c8ac12fa2d3a38f6bdd0aa0f341a5ddf4f5deeea60df95a91f708705229ad296aec0bfa8689d9ec50db0f8306d9244b559b7d935e757751ea7b9b449e19307329942ac322cec47f45ec101edb2017f1c6b1cdeec5b223a3cda1c0c7a45b73247f64a2576273c5238958322f3d21c2822c2098d8e60bcf1f818aca902647f70be3bbc3c9b9ea07083408688d46e0f16043691fcd562c44a79c20adbcd95ad9cf1d774f0bb42382b2d554949aa85adda5fc9ca4c4d8762e15cc5e6c7c4b5d0786f094bbcdf3863aa4b1270c549e5b4e6a73b8f96488c8440cea52d490a5ca6141a0b03d776eb14f0572f5af537b4775f16dc8b2c8585e601bce28a9bbeffaa0977d65a9506b264260352efff9dd6684fbca1b434ab2b15ad2a704f9c8258deb13e146aa44360453f7d256c20a7ab95c4305c7b39ab9a4d7c8684a97c00de7893a0e5aaafe328845adf5ab7397f2cf52ccf3983904f617dfb7abbefcd1f4555138ba50106f6870a678ceac5752c288615a60517328ad4ea4d41c44c3863013814377322c7a4ead7a287dcfe8bffa9c1b393614afc6bd776ba5714dc0360cc40f5a576d1d406ae8f15b93a1284be9d56f2c7ec381e97be718adda762010b46da5b743979dfd3b7c48d152c0371254bea04b04643033f9a2c5c8d213a7ffb31cb60869120197905b3003df0ea06b4f39378e7242b5045d0c12442b569dbb41a5fbd27871f234fbe0daa2b8bbe9a7532e95cbd4dbb61a448d99bb1d7def0d6e737ba8f8918cda398c6b66c41cafe1a42573caa8ef39ed0b32b11ff4899031a657c8daf2f560947e93b01a631837d77e15b099513b581adadd6d23447e8bda27947f347ff6993e8a2a3b35347344d994528f8611be14365bf6d4790c759cc241e37e43f90d37f7e9076a903b5f73aacd51b5e709e2e63acf27e4b8be55933fc38400563eb30970b03f589d2183522b32c2ef22eb4f7c48e8678537bb9d5c00a955e5fa58c4b6b828190bc38525879ddd4083b79fe2d6edaa9b0478f4b1bd8e50aadf12a3dd2f423de98345a49c52862e5ed89d827f9f0ea9ad1986d4bec01a72ef9973f6a2bb171b00f99980588fe2d70a360bf1617c30ccdcf909ad3aef8c95a9445d2261bfc9d9054c6432c16901d1bde28707a41eda70f7d2576ea9aace6971f523e2c4a31b0e885a5a9e17da0516d80c5aed3a95898dc70f5732e0bdd9ca6e0578e897de20368b29ec2fbcb5840673299340d9beadb3521dd537a034f1884312a091cd3ea219cc4b5e4da3e7c665be54cd2b195e3c9be2ffb1bd36c9c702512eda02ce25cce1bc939ff2843e8f5946d17d892b927220a8471fe595250916006637c3d44962485558553fd15381d02906e8167a490c8705d5445abacc2531663de3990182a1de9b1afde044e94306ec427bbee6019441c87d4a2ea014ef1456cab915c87c8f1d3107cc5cd971aed0ea34865c81421eda14f94cdca1ad28c6b8b6e516c8a7b10a2645616b2aed3a32bc591ccceacfd20ffe111c46f9ea2856c879e95b66140ae3636154a101b87023222f8726ca07e64fa6246bc345bf2fbb530eb8830cb7c23a8c0014e52fadefe644e4f47ad9e439dcf591f033e5526c28631abd6aba5828ad26e0e7e7dba5e47d1197163168970cccf9797e43cdc345e4d1bf45359ca76fd2692e699fc3da72bdd9e416f8293a11157b4e439bb36dd8b72f3748c7604cafb5c5eed045a18e576b8f0cce353a047b571089b63865d1fc341bbe6eecf07a8531a935ca194eee0a0bd14cc88f6dba1372133c8ad9946113ff9eadc20c3f5cafa323934303cf1d6c121a7fb0cedfd91b35514ed39f7897f25bb8d4d101ba9e9009068a2459bfa0687d2ae2a549933d7d56cebc6c1f7b8e2edd8b05275660bf2399f8385e6a9edf312694f4d95f4f4b47c86dba619b3bc83f871c9eb24aa33621bfb1fbdf5008be194a8b8e02cc119ea43ba65c8423c324d92fbca73e64d0657448652117ea7525fd30222180cc46be7b9842342ace38733832f0aca0faa07d07699ac0ad5463d858710801a54152aa356ca24d8b9c78a8a8999454943a5e7c2773c7507b7849d868fa8e82effb955e22698eb2f5efd83492da1ce5f3caaaa3a95554c68acbb22168f5d01a58670f7971e54ad96ce284db7ea594cb29241b9c07160840aae799ad76537a3dcfeead92dd8d408ed7cc183ca8ad20cc5c426f520184377ea16fb4bcff27d4c52a82f92322200e1a5886ca6de49e7c9ddb81b1ecb761d456c973c015a476f5b49781836352692334198a3931da4b36a47f0ace1daed2375ba13f0a6e9b4ba49899eea6a9fd4dfb6f488b3134c34eb3f76173291c1fa84201f054b987b84ad331710f41da88e1068ddb0170c99b2bd3a92c5ca92f5b1eba3ac2ba59e2e9d254a2585d62b888ed8b4135d4c2bb4f50e891538991873abf481758d7dc47327e52a32c96135f91c301ff64e51ad8ef107a390966e1d81e6c7b774686d4fd48d8457482e4e23ba78924882de048d4b05ac3cf230cb48e5d74e202f906936c174854108fda954f8af361d0a4ea8469912b833e21fbc2d0196a2d84d59a7388fc7dba07f895ef6bebd6710325d82091745a7a8d1122cbace1a173e0ad937be339eef231c82695ebc75c08161e5753795a225f34b1110633051ce801c3628b4085c17af6c2c123f8bd1124858ff5806a315dbfbdcacac8790fc8a916cfa5eebe2902df7ca981ce97d4ba345e86a37b42469ab1f0c8198da13a39bd5270d2541f307f1da3afbb7916076082a1ea99faca5f149534c2474b561f5e44d4d88192d0506d3db03feab9ea7f20bee845db13cbd6df587db956d89aeb02260704b51d054e8d0b4e96bb0567e0a79c7b09268e73facd9ad703aff7e7e5d55a78e8a9c6e220be14119b7ac575a85a297cfbef3044e9735e7ae19c3300e2b57f67c160d921e510cde4f1092c98c72c013c6a1ae1a46d65b82ab5807f4337311c61b05c7150f10915272931e51adb084224a47a2b94c60051c9bcacdd76033fbb981671a1c51748d372e1fa28fd84bb7f5b2a031392d4aabdf25fdb5c1f90f3a0fd4ffb09a33fbd47bc600d9b3beaf428c55050e1b2b4fccb609e977a36997e807afb2e195cfde5366f16b7637fd40cd6104739dca059462722bcdef52a3b41ac60eae6fdb00e681860b20473e72b44a4198977aa2dd4bab341c5f1ca27046198eb6e16d0d8d935789703cb179457d2e87083529e970caa6dc33f90c7509e106ee076c2345fb1e547646d0412d1b313bbec3b5eb811cc5cccb30d9a2b778351d8816e14b8f2dccf9481e25758c3c7aac44350d8e1cd2f6a0d256db0c8de39fe5cba6b39a495b8685be4bd16e4fe3f4d8f3692d74f1f65966287141caefc80e4d444e71730e63a7a584d7128425a3c293ee9e440e1234bf8b42a6b4048e82406e399ed206993d08108ac46aec68391d89daddc533e176df54298abde425270a7890602c9fa0dc2712036d5a78742f5da6a4cfdaf4c91bf5aba484ffd6e33d373b634fe5370ee3c31886008b8d77d607ab428899ac79904f60f9df099c4f3284490409d20101c46d838b0acdb3fc8fa4a187608318644d870b5978689291c3f2515a6bfd8ce809c9aeabdb0e48f497dc268a8130297823776451eba6d8eb2a77597130147d04c93d01cafeb3be2156483e9a3e155cfd105d661c17fdff186bcd43dc0bb4e4991c029a66401f667caf50e53ed1f9fab1d4059cf13c685b91727e9e85e98e401c0fbce969cf28d8d0b75a3d5ce28680ef187093bf2204b4f03dce8b400599a896122b914664eb7fcc3b9f16c02543826ed1079d0e05a67ad5c82a01d565f8bc72f2017bd1385c780991baabcb7627bb4485461910423e6b0ad93016c1c1e3689c951942c934c1021aba6639cd6f9844532455bea1a5df69b7be5728cd0e4240c41f27fb03c21fc9cf99b859c729ba81309f39344d587a84fb7da5e99069874568ac4b8d196496415620be1d9e5b7aeb743dff7bfc63c915808d573c2faae228a50d7d98c68acca8d560c25ce65b57ae326e95f9a6a6accc6e297271e1047ba621045d050005dc0e96b1f24d0e6bda41d66568ae4a05ec909d703524b546a7b6a7f19cec34046ea9c1186d9883e1e368bae330a95907333bc49cf7cd82ba7bae17a1b94a84c71e9ea6ab70dfab57ee3bcaf641c933ac2d42537d26d03802512c2d5cf6a76baae267341e3db9ec765b74655624054f103c1468dd73d88258b18b7b3160539e071e404859d9ba23cd9f9e9409f7917c2118e6e65f7c82aab21f7a16f87759934858af17529a9a24d4736477811ecf388f3146e9358fe63f3fa8d743f5c8b53609ada12442e4f4221e2701e1a0c502df4584eeb94222f894f8ee306039935bc42eec0bbe94f01300afc6681439be0b081f6c75a02d3e05ef7a93228defca1a5f1706017930447d932e12dfadc17d82f1d74728d33712c84d9329accc2c9c39faa37556b9c4cbb95ffe45ef0c70df16417d670187f6b950e9097752ec8b1e43df26d8a980cacc531db0af6545e4c1d211349646a25755b3094cab73319c9d4e5e07779ebf9ab79c5feae3119c7883e69e6b02a5e1babee5c903f02e8f89a4b84bfaa0a2d9acc92cd122451a3fa321a44ba32364556ef672e6c3ca85bffe5dda6cfd7a73b6b5bca6c1a913fcd887c70bdd4b066bc527c1ecd148af3acf6e98dcba51023300bfe18a71bead7e996f568bc96a498c7bda9d967c6e1193ec498497a9c2542b7c4b0a30c9b411f32f4b9b43414cb8e7756e93043cef6f04a9d00b34222f31d34ddc0cbdbc05c76350f89ca247bc95f257bf907da81910cae6f5fa786c2a7329edef72a0cc3a7898979c63618bd94335770bc68bf88db9b40b7fd18f19422994e3f4b918142967a0265f59cb6f849ec2313c7c79eefe45909e932f9a61a41cebdcd4f233de106e56e2096b0030648b1510a78b63118b84a5774d25e1c5d824930b325a608c995569e27a5302a9283621129c2e64f13428b354d0c3e43f4e99182c8e9353872d6a30c7e046c23696b60bd33504cb56403870811d1bf631d9ab3be0e202d3c60d18d7d86e463b2e2bdf8c44869fd3101da6e14c4dbf646b7c4673f0bdb42aa5fcb82c06b1d31d6f7a0752dfb5b3d88b0e021f50c0b65a5bea217b426ff15911f0fe458d2ac25f3ff1b628e1888f502c5467708fdb86a5ca79037e0b116a17ffe1d625ee5fbd5e0bf84c51bcb04b56f38a0bb55b16e1950bee752b8507374f51b809bd18aaf8c3bf7c4b07f0b23c4011078a7838fceca7d0d06ea8d47321f0489051d3e4e7a1955f0d2e0999015674446318ebcad9f3dddb8ab06d95b0633a80359dd67826526bd66384f4fadb808f47095372539cedb01e5fc28e66f393dc038318b90a18bfaa99e08924a1da7c00c0a46bb2fff5964a80befe2542fab305fdce0275e5264d1f99ec48fa6625622318c59b850adb0d928145c36c80cd3ef5c9c7ab5307a2d7fb8bffa40316632d73da416f53987ecda087828435280405fdd22533012c45c70dd31a857f59163a01b6b6a34418fddd391e005b4826d90d04984bc5dde851c44a399446022291cb309eee8fd933d8db71c0fad333bd155a3d241606f338d29eb35b41ecebd6aa3555ba9d1d0b8baa745703cc0c2785532d21a998e41ee0f293c0e6b0779f0b1fe90fb4690d59fb3f5cc975ee513892055b66fd925fdff606baa74f1202421da509b85c8d87b1ea5c01e1d83d01404dee1f3e9d2c04bd6913ff3f79d73e190853189b25cc229473bcdb78cf984404e410b9c4b35e96891459f6211a1e7cf3ef0702123e427d92b97680ae9d2b32998014ee946b05315a9541cf21ee715ac48840a5941a96ad0989fac4d80e7cb72899afcca92800f461353ceda3818b6da107e7d618873fd44e05e252437c1cd81264739f24384ad6f23546af88a0c5adb7c951caeab99d4cbc5f15339c1a522eb9e33c3de84cdb23e729c8e1c0898e8e1350741cc3f87f23a39f462c5db4f4531d87d244948376108cffb31929633e747052640559cf54f01b9a8b4f66be7b96a9aec3034088896e01ab057be8c8502ac4d228a00df620785a3fc7bf0a423fd25815245516b467dc703f2bfed0d171586a7c95eb0b586c1119da8e2530f34f8ff2e02a19a4c95587a9cf2fa07ebe94899a32fd7e227cd9de74c9444452f64f8cc1900b8f95b3da306875b049320480d95ee47fc4aacaa0c41340c1a6bc53e775001d3749347bc2cc2f8b8853f02133606919ba517d860dcf0e435d6ea89b24b5b1e8f28b622071406d40d6a34e5369548ead4800a24005380cd66a54e4409c49c322899ef718bed8accb2ab23856a2ed61e2cfecc995f8dfe9dc56b560afef5a5d72e35e88c73ce033179dcac8e7c412263560e1115e769325b681ad7f323a0f9ba3d50270840cdbc01bf220a92e8ee0a004d98241698bb45c293811f6120a36d3600b16b4bf288a373fc7c04ac159f2771e728c22a733e9057d4ca28a7cc4582b802c714e9e42d57e3581e14f6b6c72026d663e5e305fb4eae3888320517099701bf5a3b02b45779ec6be1d2c01861ae2b9d91b3fdba1c395efa640916d9b5c53881eec63bd60fd86a4aeede7c88a9a2cd130311256de5309c918cc557570e64f18ac3c63147cadec300d8d1fb264ef683c5608b7975403073c35ba6ae67da9860631443818da22149af8e72b7f765fc6f900207a7c1123e28a7cf94319947e9469422902d4181ef9cbeb206d99f44c49284ba6bbbfd30b683fa45836ea99bb0296fc2b11c35c708dfacafeae675cfa7fc3db3caabe703376d8b2e7d3540c1e8e65bdb7672bef08dd240e78b6f08bc404886d0c16c1ad251bd387c73e84478a7785d09e68656c1ae556ec9a859a8d38b4bfac254624a42a691bbe5b54c825bf99ae531ac5966cf86d1155ba1ef404bf0f1c3735340318f46049a2115a245bc19475660e12997300061f51482951cbdab809c887ad39e4336a9b4ec60d704a9d8e1ec969c6d1c5eb6441bf50a3d15bd1e06be10af47b82fd2d5cff52853072c228bc867cecaa62178d1b6e61b4ea9b9804e9441722e20adbb162be68ef3ed35f403e55de35fd3f73f0b402428b3376950e155fa4265cfc61c2f79fde785e8a9522e3879fe944c4651efe68021afe71380ec658c3592dcd5ac87c7c732459978f1cc83433c4b03f05f7cf1a0b00ddc2b415aa4ed1b0afa09b66cad33200b4b0e70c7b35418dc377e2a3aa8bd882c6958c7a087086a1b1d3b9973907b7800f0b2e4bc29b0beb99da4eb6451b88189320f1032b50eba63751b496a4eb411b453f05b764ae5307140c9326a215783251c667822d11f675e28dc141dd949256416bda58f0da812ab3843f6e3f28826c2c84c7f1c6941c3b282dd280ba1a573b324b67350d3705a8126e6e525d8e45b56aa37a5fdf72e06cc25b16d0cb206a6c556f24f9159c8e2639294f0fdcb14f0e13d6b4c563fe5d4fd5f73b7af30d041c88e8b3b6e09224876e95740d940210758c169177de7c86afa255050c22090e99473315885e37483f6735dafbadf0f12b2c0beeccba2479283a099266bf65a8205e7a848b2ab17339648dda64c118aabc7125cbcd8d402723eaa36e969d7d5fafc64bc6f2bae17c6f20a2662246872d495ae3a53c9ca0b25585b2b46f4b09d971cafe465276da79ca0f9de5a64a048e30af56898b031294a964a1bf6a01b0a55af97bec822a5bbc21e115b3a60a70fb04a0833b8efc8611efd0ececb633b3a67113fcb0ddaacbaee8d3f64c04937f2e84dda793e2970ed8caab54c7418ce01d1eeabab5c063bb33ad9e53f9391f7bdcc1c65317cbaaf9f42b489bf60cd651e12bbdf2c7849c175bb66c0369fa67b0ddd52541bed6a647a46f258654c26507c153513ba0fdb599f107701a4acf912ffe114ed3495a684097d949b2e0f8c88bfadf4d5d0e72ad962d519346ee986bd8a77b29cd38b654fac3cd6d8a5e60064ca9e2b135e91841113bdc1ac269e348e86f419ba8e650497611263667c62e151234cdd514a95f094826cb9571e3a97cb0cfca135f87280ce499fba2307e2bc9bb1c218e04e45cfc92fc33e74db0cf790709bfb9a26425a98df08c459ae06bd53df2b4a0a24564924df213b126b50275f580c09f2eb74637975dacae50fc81c9b6afe637dbc4164119eedc5c63a3299028a65e9eebacac977d6e3c590e9937919ea5d11bc7b678353a15ef697e646036d38892336fc309711325fd917b5701c4414ed520acd7d09bfde2c10b90fc63ee7351eafe05f32aed700d7d68b9b45bf2f324f0be87152e720ae5fdade1e1c5d725834e568f28ef7b813eb420b0570e650af40e653cc75a9705f9abe6741f96973f7a942ad95aa26523f228498e9e1c5e38d867815d2270ee19c689b88d2d55ca757a7a00ed3f49642d83a1b3daf4df515191c842e95421d722a3bf51e104d3ac8fc8df6b8c2972a670b94ed19831f09aa7958f8d6447ec2ab821ab58c11fd8fb919a68c2880c5ccdeeef9cf5b385c014b5e3f78277bd063bebd4ccbaea1299a9a872d7c72c1b359605f8f46bb92977802e08d36590520821263cc4d046dc0e8a332a93d87fd8faa897e23f740a0bd618676e4490c17478231f1849302ff8c9c739cffc9d6d5d543c0b12eafe169d185574386a55952d18fc9444f59acd37e60743f1a80c78ef47cb92deed032b56c269442081d2e2cb7062646f2cf37eaedcf6396ddc1d871ad733a154c8718dee89807e8eafcea42ae26a1beed0096d779dd3cc00ba4453398fd4fd7ae226387c442590d506478d15038bda42af1a0d989b6c2a1c482a89cc3bbe8ea292a77b0fdc362397673b7ccc79e00a155184873b0ee8e279d381cc60663a19d282ff151f1541226c9d03ae24001c4c44d11345f47e4d61576f54c00f152c7caf41e5a1e20c3670fc4379080e24aa241e105c973c7f68dca8053878456760b2d33c6420ec210884153e881d19dc807cc94d02cbb1cadaea9348d428120f8fc689ebf58934f387c7dadda3af7e5c957e9c2cf2c4a0ff44fb33b7ab420f99e8428e7c2d7943958818ec36128e857d44684a0b93ae874964ec50dfc4d5e80561cc3de562257a79bae352dbcad5ba708aaff00e336b67427cd11858ae5b44fe74d5da52fbd05a3f5e2b9e17f0cf9a2eba1445e85cfabd221f3845854aab32d9cf15ab05593b7d48f448272ac88d691026303b54a26ecb53a82971f38b5e0d9813a483e7782a2c7d325a131187a3621cf74a735f11807335564d0ca800da11ee39efb1000c88eb7fe10ab213f8a193d4a52da13717a17478203bce4736ae33357a4699cb0753de96c280ba67eb9ec215aa77e06faa3589cac38b898ef2b7cd328e2850e24e7f67e085975c3f8c6dc5c0aac117897c05d2a50342a69f6838e07ec4ae108bc25863d097194b51de33defaab723cd0ba07426210254de7e16cd7d5ee5fe1ae4e3f039bd59c585650233b856870ebf268bf3ba942d9e3bd39fdce8937467524bb74d7f2ca72225e03898c48132b6e79a084ac7d06842a2202b5db124dd68fde3c1988d5edaeeb8298a27a84fcb3545332f0fb29a9ccc951a534ab1fd8904cd8ddcc14d497f3006906ae6e73a844b6c012600549b5a41f17c28c51f08f96042d9f47ebb1b676434a32bbc1035b37b4e5fa2a5b0d5ea18565e3f76681ce68a9c81a89b0fe37193d0cd0d0510b801e69d893a8c8dd73b5357ea70ddbc561c757a78efaa33a1db9b973a35545d8026a52479ae48061ddf9bffb04a80dd6be6e9b3defb123e289812aae63e6df8b3c1293e997f12c4ead9cca603c97560840fc43f5d17677bfb8d53ac07a719c7224fd33acf4b10af037261e61b378a2b86ad22640c5b96585fb65a642113431fac9c3fffefac60728e6e2f82fab26cbcd2283fd64287dd5951bf307b5acadc1007717c68e1cb0ff0e78a1e68ba060e3153224165e3da9916bc14ae0db4bf17db374e634e4035503464388134b53257f39a1f51cf2537a71e4a21814b26fc039342dba4c276ae33fb0de1cfb1571f37722be81f18c452472e46d1b8228e6eb269fc19e95ade188772456716754f9704ef384a9f2cb3a8fe9374002b4248e8639e5b4e20dfdecabfad60e3178f2bad4d986b9cc7d538757e68eeb12bd5dc2d25dc9f872104c5bf01969573f13232e220e033c0ea0a5732b8039b548964e53ae597935fa2c19f641a5a354a4c47b668b1d5d01c5c83eb28a9c30a74b737e3a0e8767088d13ed898c33fce1c74f330ef21b6d3ddc56f5039648e259ee2b685300ff5bc789f6ac454cac6c1615ce1390817f1b1dcfd320ceb8912b7253474a312aa1402f69804f4a358b8e9bb3ee3842734f9a22c6396e3e7c3f9aa8cda1c9b28efa35244c134789dc16ae9777bca37c601a0d7efa8a1bdbf7a9a6d0cfc9c0769f82a098f1a781ced5393421e4ddd11107cdc65e8855a2517442c4bd27b797ff9687052a50cc998e31d65115e6f308be998923434ea1993ed7b6864fdc41429d4c50b04a38c33793426e8cc8ca33653aa163cd8adfe6981883d55da97d54e1e7916377d9db3118d768955cc7f24ff3a6dec90bd7c7af8bb83da5dc65035ab126253d3aa042c2f56889e683b6fde58936c064f7cf49944e59092161b74ca68f26982e2daca278309481ae9d7274d31e8159613ad35cc4d0e6036cbf82b3995018c0ad74d1b09c4bf2c69e7b896856e9c490d6cfafefadb7adf7f87aee3751603b5274b835b60880aaad994f0b428024f69257a9c1ce2d00dac506bded97fd44cc372b4b1cc0a24d0ccfc8989655c960a993098229b514cd586ade54e370979c540afb55ac75c4aeff6d99e2bd8029b2aed07be7bfc5069c7330a49b333a1433aec64b9668b4c843dd53db1bdb64518251c29d6663e35449faf3ed468b602aa12cfd51136879cd305da02678a20b2ee533bab3abd058e1cd51d94e7b9556a50cef290be4efc9d0dcd678bea4ee186296e658b0a670a206822368b61c961f5e5f403710d985f93de7942343590bec28b13096b534ad4cff78df2cbbce861e17ea28c449b13b9bd9c5fbaa77ee5f4adb8479034ffb84fe47ba6fafd90d130b8e55313851ba895bac79b585235af3041dcf78ab10047492685aa78d9d76b62e1f9764d83efa67b16083e16bd06b468f93f2b393eeca412c2419deb3eb158ab6a7602393068b49c1ee2c8851a75115eb89328a52ccd604465b60d090b5e366b00461439b2281a66cac60087b641000cc10ff6016a8d054106b4e07ecc91a540c0db9780a96167b71c1e42af66d5cc8aa9afc23efe1599c7275b2514f4bbd94ad0ddc31f5820862de881745763ea59e093143396a35b120fba8f0a5a9774dc112094b06eeb9c828c8ecfa02f1408ceaff43dc0edb46cb596a4c1b1146ad71e6f870d027c683a1caeba253f9a3f20d354a867aed980e3d9d16f7d352e00d73b97142fc714541c604be2915193934800f75a684488749e32b4ee2541810645762fcf967e7953125001cabc0942f6cd1b9f0f7f4a8fa531dfd784db634ae8057ae9044c839e13c3d47fde170cc01f93b0ca6b24b6fe18b1f41b18c3fcec52017a4fd264432a17ba16aa4e4d715452310fe4a6e21924ee682b420723004e5a1dea8a2a64d783ec650d4ea3b540bbce36e17cd1596c32f3f762a886055370bb47276212cbb50f656d4f1130de3d12b98da30f4165b5eba3b6e192f639d8185da29e55ba34a88cd9ea6c98f82ad48525b138380718e84cde8b5aeffe6599f8b1b63add37bb10bb7423aa1e56399ae0ce2989b3d713b640abe96f78324db2e2bfe467a1f618a59b7527d5a29746b2b070baba6fe1e9c87cab186b570f799c032b0019c2f8dd3f9ca9912fb0f2b9d9730b782378598e237e8fd5c98ca345cc279df4e340c18bd84af190cd3ad86db511f58fd24cad45a64ef814af5406f4db2b2860a6acd3f5453f70fbdf02378fe84683992e09ca922958e8d30b6ff6b95c0d9974ce93dcae82c6367ac0a6cac0c0c6bdd6d4740915a52a52b4d64fb4a89e72fb16aad104909667b26631d157debd90c7e78837c2f1ed0023395fed2e51bcab7c17f82f1344adcd324d443d79a32e96647bbe0fadf8099cda3c3d50c8de51809336ccccdc8cbf6b08e75165fa3a9517a9f24f58299cbf0ccf89489550a1af9df145b6bf63dcf6b8d9388632b1650d05a394ec0800fe40365399bf3f97b73e7d73142f14c540c08e389d7e7eb120c44bac4bfe71683d0a8443d8a8e7561c3ef0bc00fe7cf756b3a2ef3d57926cb3b1b2013f0eb517101ba2e6e0698fbfcffed6cd8ff869df08ed3751ee54d61f9dea783e4eab08ab57ecfa5ce4151ae68875433f461557e91931bdb11fd3809b726f36f63b09894941d6e2bf50ebfb419dfe057eb1eb34869e20514603ac4d78d7eae1ee65be6640aa7837c2a820cdb0f8a4148fad2cd2c5d857b780e0b8893abd669e3c968c2787a84aaac86e72c43b8bfb7dab81f4a1430af47b5f03efd17969b13de190a158dedb4854f8a2d8d62c664100831a3cee3802a8f7bda333508a60de03a2ea4f844ed1d997d06909d143d597d090d356356962f9253728c9ecdfa653a21332b31d8b104a2a554a22cfefe08f0b11475d2458a85dcef8242d197e37d6b83ff2dfc5394fa0d109bbbdc316c33a8df2047ac75c77c02305cf8729f6bf9be2dd4a2f1e5defda5ce2d6fa75739c4b5cfb193fe6d519d64c61a460030b9efcc7fc43dd2e0038732b5a9cd1496347805e5072221456a1fff7c5d3d17837a2e87841d56a3788b57693cd16e1c5993d19a8665c558dacc751f441150bb58ba14c406044e690fc23fbbb26eefc3a5c721608ddc7b54d073eff7f95b9e53a21805dc925ffd2d084378d0208bdf1330dff69246d1d28703533a69022c4df1fac289a5120c48f0838667e0ebaec0a71627088dc065201e3b752fc27212886947aa6385e608a0070415e786e14ed832410ad846a9b220a0bbd63e9008e73520b55501c8c8f0e033f86e9dd0b9cdf1416e42543d1e35bbdd6f9a0c465091121383560b489b367e4d5a664e580017abb705ca6c4e8f96a2ac732f1138a67fbe636ee80d6b787bc3bfc1099608e188d4431c0acde36294eaa19418875f942ce30d142c7d26ba13a62a920231a4f6018b1ea76c9d497b3c21310edac7508321ab7c08331a5562305b1f49f0e9d67d743f467b54b4f345215694d4e11837ceadfe3681da2decb8e9cad70a2c6cb732c00d5d38d394a677f0c7f16d47687cf43713c464fea82672cee96ab7d786441291f26e8075a057ab133e5987301b4cf7b47d9e8472e7fcbbdaacab11a9ea0d8ec6e4272fa25da299eca9b5e32606a68a80943e51a62f4d9db0d42ae52fc7d56727a5b7ac8c0f06a55c2fa14b2faebe28dba8a9c84c9c5cb4a7e8ae2fb1b83e220538993a4552dde89bb814cb6c67d7cbb007431701b2be4e55a8ec7a95eb047c954a251e218a92ffa6f5b9601aa00c62c2dbb04d5b3e29913907d893f19fc4cb39144ec0193295ae369b98a03658d0c48bf2ded208bb720713fc8f1deaacdbe4180975cae3f4ce57a7c25d0f96087b809b6c11d9f5ab7dbb984bc67894ba13bdcc4793d5f9a3792474a27795e2db419860254c068ba8a3711edd90768db32903dd469465ef80cb05bbf463a6fa7a4035b7cf6bb4d494f5812130928ad6c1e7e1c1be37ab006d50c754eeee473ea8d77aa0f963c7dd046a14a92e786a6307d2ec1fb93fd39514375ecb1a31100f272eaeed1d246b672a2bc86dcb79c10d7031dbc2790d1a3afaf5d0d1fc26197515118b336e4bf379143177af76a233a7d6c59d41cfa2f3cf33d753408682f0aad239b43dcd91985eadb959c0d439fa937887a7056cb8ae11b146b5033e764eb7106f7bdc95e7bba5513e4e6da874cb338a9f57102ca9561d4261052b51714362d3d4a3dc45697a7c514fce47a0993ccd124fe3639db272797e9605dae3b0a1633f796a8d1b60c6e8df3673b93c1133b68a4c980bf22e96e96e53efe6b23986511aa4579f0d877cd348ee5fbd9827d5625b0d24ad52af100f06c1e01b1d3462cc320fb1cc62e1c3146d78ec005ee335481c3aacec1695d324112c3ff144feba30b198a8d1b154d28cb7e3e4b3caa95e48d2f25acb9b6b39b57ac6e997e084c09545bd4c64a20a45018afec6384431dea407675ed61ab712ea5e15ee12d68386a667fccb0ad5629a68ae9799aad16ae4d01645bd57574525d82b1110587e5909136da141ac331b3f61a623c62c0c8d66b8490de4e896aa1525f19e1c1a42820ba240ec74cf9c216b2d5d82a0b75f0b88909b6c7937054430d4e4091f147b7bd6f0ad6de8beb2e36ca897831bf04d861e015efa951cd0336ca71d441f7585279e7d908d77b28d38a8fba43038d8cc2f5b9612714357dcfe3548b051ccbd3e1ff8993dbf976cc84d3f9daf0ec3aa641770d1ba0e385ec670de0b896dc91e8337b2f1c6bfec7dd57d8c7ab49ca74b10e3d2a3f6201ffcc31489760762a97343613a718f2cb4f8d0d62b114a05966ff9c931c3020c0a0f1235cb7e76a6d40310056dd595796d5691e28c521ca63e5cb2aeb7b3433dcf3475514cdb4a306b86b5f6a7ba1a70c851705c5b84cbcebe4e43c28b8f828da008c0720fa8ca49e8b995f928d5df04692e39971481168189348c4a5a29b73be84153e8c91e6e5ceeb276151c5d630488c6b11cddedddcbfab94dbbabb3b854b4ccb6f97c2559c54bd474daf26d6d04fa31ca4e5a0824e0dd4309930e2de35031b9dd2b9feec12a80fea193ad4de272309212c5e96b19b7b0218d77dba96b4f494fe2225056b6a008eeb8fe536531ad50292633938bc57b1f772398a6afca7189ea39379b46c6c0042b647edb4c5dcc4772b428da5c5735db20c0cf8ca96d41a38bd908723bab33fff8876c415c8f6b4a233df0a887216c2c3587e0b2a453d90d54d37cb2a5c6f36b001a25cc016f7c70e19611272337fff7e1bac47cb8d0ba3d282a92443dd63ccc4c3b19f84ee49105d63c67814b1e55b3f542dc34aa0859d74977975518fe78b19050fcb5987e285377f3e83afa7eaa9c929dc7ba99da46d5e67adc0fd5c16ba88869f9ae059cf5d0c9d8a239029950152dcf586edd2bce920fa2064eb0c82551e0dddd2f77cdc27c3080d90307f1509610fc0e49f5a6290d225bfe443c6ede6e1d2d1770d0edc4742026c318b288ed7230a18dc5cac65a0aba662d3adda070c6c9d63b5ff926edaf6c2ebb64387457f0dd73a7921ab4e20abc4b8b4f8b4be118c20cf5019b935d4f80a210d4be6a6108070bea7efe3986af143f694ec93d4e2eacbbe5fdb0cdf439e14ca1fa5a119208af91906508591534b433a1cd82557e0843669e5e67bdde0b3ba8bac203aadf98388f0279b95f5b0013c80187fe8a3fc7646355876e1fe901d6025505de21c543419e0d2243480e9f9a37ee0d2807f0211b3e220965363bb03c4eb1c36403d5802f4718b3e6402c2dedcff41ff68f3617ed28a5740935f07215436f354b14566b9d387d952a5598a19fb7971b56e261036a758080bd72ff640437330661d4fceeb3e2acd2d307c43e13e8a24d69153a53d2a0cb5501df5126d27ace17b43945cb72d0b90900b67fbb29cde646af1abcb5fabf0d1e7a68e99b0c8f138624de454d01c4ce02eac6395a5a400e334a0745c3970628957f26ab396a7d9e1b1d3e4ec24de31ed24c8fb313b1363e6c1d3ce4c894d5851828781e22ce3c07f901f94fcc9cfbb1837e2b0fe41b2e2a3e791a0c8c8d6f1a824564aac617d2c8de644b29654a29a587055c057805a8ef9c12b4661f29266580719c17e9470f0add65de18afb98c78dc3c405954b24c7f20ee464b6032a87c229545a50a7692212a778e4ed7f477ebb4e6260f7ebab6da06acea1bff1f21700742b35bb97c0a0716c9bfbd6b99a10a918c514e374808e514246fec10441235cc4d7e91248ec0c4204b535953dab802032fa67c396921e154d614a42947525aee068c99f9dac8ddb0a7da3c77aedb1f8f9564d69899b9e56466fea19dea6e8e9ce3fc57eb76fe2d673363cc38ca905153e7dbddcdad6a2e72579990c4b8a9dc930c2154f89c1f81b0e111089f7329f0393fe21c3a97e2fc393f029ff323104a91028f40f89c4b81cff991fe02539a6d55b165667f5a635293e903394d1a59ddee0aadd0beb090196635e79c7bd0857a503001a9c73d0713afd62215da203e8015cba6809e1ea8e366498daa278804200e2e11125a1893d6d38b2568b9c89e20c83ca6aee4abaecbe44388d7e1c1c367ff152f75bfc1a9423dbb837f7332b55552549683554277f0333fcc329909b5e1d6968ac947ae7bb065ce880e34b193eec77d8973639f4675c343c74a9cfb3f420852c2be098c83fde060ebc3c21024a098f08a0f181f04a64302c9749f314ece745aa6531c064cff6e0009826647cd51f9c36a4621a2bb2f8f0d2860e30a274ea821e50ab7c2dd1d859247506180235a4e8e6852e34372e888e9fe435d4cffccbb89dc9c5e34a6fbc74ccaa0723c8d4ac74d0ca6ff068cb346a63f8f07e874f023042046a67f107f122c50c29439fd4d1082c26afaafc038a6e9cf02e328cb8996e93f847174f448319d00d3594fda68a017a6ffe2b041428ca32c284038ab882ad3b518ca61fa6f146719e1657ad1745fcdc838da061b519e2c028898a21443bc19228b1218740da1c50cef280e21460e4e080ec1c6119f02e819b1a13bc872e4826e60199414435da3d1c9073ad072c4226295a483295652906450396ea2b46eb8d9224f53593d3439fd20b3244e482400ae9c40c24c65f5804513129bcaea010c17c83895d5c3971905a96b74c2e77c878fb264faebb8f16105ac28395cc19ad9b90c3ba2ddf9d1aa44afc322b476f7f66470255d2a632249609cfe1fad3145393c2753ba508f7de6eeeeee99636e7277770f52093d530a2b5d11ca2c04d2047f21d9c5b3ec4bba4612e12f7da7884009c25fd288f43beed23fe79c7b25f83aec9c730ebe1277b683dc72142912ff7d910b48e94d40a16bbce7014915ab9ccf90ccec54e08fb9aefb78696abd01cd89fd473e4026117407fe05485033c630f2017ace7c909b1a91b07909feec70782fc2d27befbdf74aef7ad87befbdf79a8750975e11fcc8590fef6d331ab080c27beffd8c06dac36f14cde793cdd09ce5232ec46077390a3dd4cd181ba18949169ac3a36b5efff3d5b6448fc71b9aef3da645a28e71894a1a90ebc852b163ccb75ee6cb62be6d93e6fb564f1056bdd8e343c2cb7878cf5918a78871361cf3fd532e4b14c4bfdfe3b85134f5001db1748e36ff26692213093f4678bd0e735f113e97092dfb10435421422ac1c74a6948ded28c08eff947687eec47e8cf9e1f7bec3b95cd4460e74c0a93a74e1de6d4cc66a9991d8ce30524760d84f02384106a27688b9170de611313e9fefdbe3501818f6547640acaa823725df00d5aa1e91f3d87774d1ffd12b92992098c934348917e96dd39fcade682c5cf7e84f7ef338d0894209836b3f39ec8cc6a3340b2a780ee648f7da61119026584ee42060e3d3b18112841de67daccbe7b9e1d22f167560b923df6fb983604ca6a41b077da90ebb11f52faec7976744777e2eb0eb6261fac420a20ecf457c107cc0371afc31b53336b8a5c66bf1f21738f3d9199ec47c01e6a449c36b3da8c08f1df8f00dffd9b32909219aa147b07d39d94ee4017cf909187dba7c0babbd9f463d95d0633c846bc9a5ba624dd2eb3100c164d91eeb92765f6adfb5c462433f601e9eeae01f311594d57e2992ff57647249af175fa6a536c57549421e7a06afdbe5f5f2e3ae89c7b4fdf7bce71c09466bb9a0a28a66a444e53269aadbec50b684807c084f4a9014c69b6d5c7601ee6dde92455a6b6540aadac9da0cf8ac5d78464534baf25cdfbfdfbdd7c961c4b69dc4a5fd256e013e27b10b56d88a736592a615f7a9367299feea53d9d90c2107702bfcefd536ae349a1a7ff2bfdc6ccc1b87682be964a9b908dfffae8ae7f3ba1e9039efb4aefb2526ae3bf26a01f4dfe2f4bfff837c5ac6927e8eb9c00bf6a27f0e6bf2b4c16d2933fc0e60c08afcfbf422d2c8f37d644bf94c94777efba8fd97bd77b6f6b61c69ff81d17c9ee80fc7e7fe656993a8cf1c3ee143f16bcdd9f6e7fd5d5ae7d5994ccd89cf8ed80a90ba8b94abfc54cc57ebb60346df75dd31ad4a769ffa5fdd4e1f6d16ec76e04ab891f017c213321d25841680a76d40c5f008b38c8855da0a1dd21868c8820875dd9839437c8ad85d98e89aa8ec0397595cb5c05f3c06b359ebabbe6647737c9d0eeae0e4357ec9a000899451999b3c14d103747740a5a606a63dd398d4a87890c1ca2903c9555021b9c46a5c3513d95550225339bca2a010c3d74a3acfa7325ab4ad274da26636b9bd4bd612e484fac8eb6ca93d61341016082054e1039d1a5003a9888e2c48f134d126052620934aef410c3d42b3cac01e735533fd42cb1fa9472d0813f008daaca5c30e66a2bf03ca90dbf02dcddbf573b61bfa7f26c4da258c51368ed847d9f27eccbc8d550accab0c8ed74d21efbfcc5dd73ce699b73ce41771d53bf5dc6ddb97be69c735bb4a13e1db2df7289233b8eafaa17a1d36458d5ab9b0c06ece8c8152eee2e7f0059144628005ce8e28be9578860fa755d572c5d973b4186099aa051c40c408c80b960e9e205472efcc3e7b2256c40614bdcf0344ac860e298ca52028cb9fd4d5c82958690328bdcdd47cbad3d184a89daffa836a520a92a1921b3339728ccfe761c9bb78d8852653260aa95283e133bedae102b84cfefc8704dbf2442fecdec24d9ef1715e613f83b21e9eea518612946777db7fbf79e738dc3be5d98daeb7761eb5b2134b3b51460eadb4fd60184c6c4f1bbf3aeb9e917ba5b35f5395032328baebb5bdbe990f43193f1996c30b7155c63d7b5b530af8d47e72bddec0e55f89ec341754bafa92da2622669a66bd7ba1c9eecb210ec9b86e4efd24a32372781f9ee3a23266eb5dd6d9e5e6ec90f487e7546535f55db8b6616dc0c8d18c64b169337c82126048db0201beea38681125cb0f86001426699fd65dc39db80f12b8d3bebb0cb38358455eff9e8c67820c61a58cf6c20d8462724c9034c759867ba7bc733afc77020f170c4df16e320b57397c987839b997c2c972dc27090c05d98aa63d0e4a3023c3b188e23e46e1174562d91094bebb0db5516b9ba74c0d410cee1d11be5890e9a2dc7fe6c111e3c786c37372ad56b1b4a638155bd85ec22f795b583cfccca4855cf8aa13f54150392caa272148471e0c2a7611cf8905f75033750850fe16bc3e7571bf89811b9af7d450d495e7bf8d8d777334792bac63dc7eeeeee7649ed92923a679570ce319012a3269c94b4469db33e6be44fb609e3f8b82bbe4d5a85a9a76dd27a91d917999a3b63124e07abdc021c309d5328a6abd11a95da1895c0384e086780c8a1311d9020ee6d065fd5391718c38c07b512d4c6a57802b516b9ff6206e49c73ce391f8a235b5b9f2cb2af76aeb990fd3fde0f202390fd1e8480d3cf9f53407e302179a6fef811023485c0f380b94b0365b66bdeeffefe2c96da1fafa46d3c7c54aa7f148adf6ea889fa1d1df3a158548e66c6385a5402723b3119b41062c63648a7efa2bf0fb4c6bd73ef9c730fdd0f234d05539aa38f51caa1a1ced980d6f8bbbb0b44d3fd8706e93a14a394a8495dd3efee925c124abbbbb78465cbdc7668ccd5ededededed750e0c56a969777777af6ef76ef74b77ab71474ab36f9d8c2d5bde6ab6f20c732634fb79e4e41cb1b200cd4eea5ea1248ab258716e0dc661230924b16a8d9f3d3d3d9258d52ea907155705566167d2136a16f413e26982b969737f63c0025035f2529b7eccfdd2de18af990db507bf15f48269e734bffea53db4b1cd7848cfa002255a3b435ad02dda10da516e400b37d2381acab1acce467c70d2b3c3a99d1cc0dadfe67aef1acf8ea9088a20a0500206eec51600c4a11b848aaed4606589005c425b58444e840040afcc68c19e3370708203002ea01e27beba6283143f518b242f5a69d1124ad247ab2e4d9cbce0ca902ec55848d76a99c0b7500184454356ac50e30b8712c5666505ca132b50a2e802bf105d435f56405fb8c8bac45ca1814b035da1e18ca22b4966b0529afa3e05e85943a84876b4921284eafbc0a5c8872c2d1f56800dede0a3cfad23361e4b8a1b4c53ffd463c58a2edd63c5e86865a50aa16b09c9533f07d0d41cd48a8671940585cf060298db4fd515d0c480a200626e9aa579a50e60b61e60081eb095dc5458f3b3eae6013307ab3843e1c0c8908c63e20021b26abdb9e86d0e148464487ebc48a09514e7f0ba685499bbb292c9873f6692c00d08373332c087261f30c34c20dc4c7f78c5771f77c7a6fcdfbcdeb7b5ec5aff9992292102185a23c05c779719b2214faaaaaaeaaeda604c7de7dc7b2f665ae8430873fca3503231c6cc7b6854aaff1ca86e4dcdb4901b6aaabb91ec47ee67eb1ea7c8d53ed342aa9e4c1f9c4e46e46a9b8c072ba58c8989d91b3758753f023966b8788179c1bcbc571acc8bf7de7bcf7de95daaf42fb5d14ca8a9299b0fe6312fd2e7bb52a4674a5e6e18985f184dd5e67d54a15a1555d7a35017a6fd609508a4b2aaa4e9673320c8095f4ea8c5ced667f0c20b9d82416eb1c8c59f424f212e6d46b519f83b505458a7a9a86d34f3a7f644a0674090b33fb509a199fdfeae0486a21bd44b152ad4fec69e7f76477c4d6dadf19fbf77e6ec1ea14b39b71998d2e4f8afba4c655901d2a6b2562c98376df2c7dc7cb5e92f75b9b9e9978a5da3f920f7b716a84c6e93073ba34c7ecc79e6d0e4f71598351458c5399683ed17b028c4fb4268d7fc20e512de5921ea12a9526d92901b91dc0ad5587f4d70fd1cd7cfc1020b1218b51183e0c84d15ba4b0247345d92237244488cf4186976903f4662a4a7e9ea3050749093c0e51fc8491828aefb2a2587c494954aa592fb926902d82c65a552c9f4a6d49621c1909cde4ba65f9fe540be49fe95f9cc3e7b27b56da70984d2cc3468f2c0e4c32459d532a7144a7f93333298764d3a8d2dd0b54816c972f0e3aa6c91dc9ff733d7e47e7e0c4c692292359066134930dbb20913df9ffe4d009bef7241c2fcf6d7a682495daac31f605a4c99aed33f2aa7dfcd77e331a584f993b6ed84f9fd6702a13461f69dc9039887539cc9c77b1a534af5970bd2bf05a279cd5215938fb85196035d2a8cf31669c2946e914b654dab30fb78483a1531f23ba5c7be01fba554e406c4eddddd98621c8fb0678cef8111e38bef2118b05b4d3e7891743347305479e0736e35666693dae847ed78f11bc25a559537e5ceac82cffe1c19ee28ddaa1b3230a5d922e6ae6ae31737996e42e75cefcf3dda63dfb949d7f81fa92104d399486b23262a3272dc64735c5865c4aa2e529b7654baa67f3acd57afc88b20151be4e6abd9bbbe722c8c4314bd487f74809a2a1e2814e3b8bfe1dde8a8d9bafa0182ccb2238a9a09b6cb82507f0ff2a673f4695428a25537f6da39f71ee874cf53c26c979a3922bf9f67766a0537a3f676c77b4f35d4e85a6b4d06f65e035f229917f2b5597fd68f7df3ccd27ef6d79716c9ee80f1190ac7d677e81bff771a950e5d61eeeed111c18f18f63cd3a5b62f215b9e11159e600961e1840f3f38094389109622405caf7a9a772e04a1c4898b419b228c211a96f8e14a16602879a2e4081480187a414f14569eb476fc8b16ec7af15e8061a7016830570db0056033e1d85099b6dd38494cdb4a30b2f6599665597645f8ccd1b4ea8eb0f5273015b29ee9be7b487bac196ad38b02f97393b8b7b59a2988fe504b6dfae350a366b769842bbe9a48e899209849db953613a43424d396481b41bbe6c2fee2d9b9debd9b82607f6943ae5db54c143089b03b250dd535fe5de38ff9d066d94c101dd22b9e9d5dc5d79d19ef19d780ec0c95529bd42d0c0b956d6dab8790da8f66bbd1ec1eea557727cdf69ed91e34db85667b93d93ed443dfbcccdddd43eead13136f698f05a8cd663daa20fbda1bb7a6ab06dbb66ed4c6ff860c72db563771e8cf3df64cff6df96f52738f0b4d6f327d68babbbbb7bca59a1e3b85adea36cc07effb9fbb40be0cb6cc6ddda841f2aaa76772184546f4cc1e7a4eb0996c65066211217812e57a115e2fc28510531f197477a3bb2b6cc5fd61e110a49262e79c731109b9ad8f8ff6b85e84d78b10c2f5e99a4eb1a0ad35163a07a1bbfbe50ea1b6da628155eddc6e0ff2096cc238be9aedea315534cb9cdb8bc4f463ea6729de901a4cdd529655552d691b61988a55eaccec4838ee5342ee6b6f085aea7c7b9ff323ed3ebb481a665a645db093aec6a739e5e07707bf6f5c88c35c0ef688eee047c519de599b4cf6599fec47f64797da55a4b926a76fa369a4e9dafab4b6ad776c625dc6dbb7b84e7777df54d1926529581052c9905b0c15a1a97599fb5a17c6e926cc314f82901b6ab6e6022993638418672602cd733fa649c70011333437c66736614d865531546288e6c650d18eda0c4c05d933f51d0d09675053084d7702f81441d3dd4fee7e3485116e2a9ae20a87afbd98fe6cf2313320abcd88b033338223c2ef227f11c0d41bca53c148c5ab24632ef9da05981a4c4d63b22abee93dffa5302f7f230007aa3bb60018603e8e1ea636a67c1d3e9d4ea7d3ab294653993aa5dc51ca346dd4a674adcd2bcd1b1acc95eb5a407c200c628ccb499d7167ce3a4bda7bbf4d98fcbba8c93ae828913ba8867b44b45e84cf7984cf3974ee1c48511b725b9fd782310ac5089ff3089ff3cbb97320454ce4b63e985e057862847b6af985ec1df543ea1cadd91de1731ee173eece9df38837c8077b3256c360053544214d53592b2852862ca921c7f83203bfea69543ef4300c0d631c99623046d1184431c2e768543aa28bc2e8cb49872ac2382521dd54561547551821ddaccfc08518eeee8ebd705dd7a58486ebf2c1929891448dcbc5a004044a76c0aca862faf4477161050ab09ba7b1e2092b98f0ab099494a838f4e39a402caa2891282983622214a5b8a13df2d0721ab1e082075e62b4e4680992a3b090c16af4b803d10e452d51f001253d54e9010bd7e8dca21c4348cac88f985144c578a358306fa28ca25946971ba217a352195f6c8061005d2e24794754a27c253a38899232282fd6e01a2df2035143d0e05f5079538e341841ee4b134a68541d8071e4ff658a0dc3a8bf78c1e13b1023888da418e17351568131e304ad211760a2c8ec078625b1a0d38fc42102d30c8896e82162b540bea92ca2a22288b09099ca8a024805a823a8c420e1541615471851b10466257d2a8b8a201a950e140c1fd0a2e50b794d6569e9e2648416a453121980a92c2d675001e45cd9f0791a024c656d614d9fca2a2ac1dcfe6687d88b921dd0002451541cc1e71c054510d34d6551d104f422764c6559f1336f30a1514a19548e7ece655039584556160153595318a165288b9ec8420926002141e7a66b9807abf8bb983c21d0010b2460d001065e88d8612ff3861a26882205135b5469c10e1ff110a1c3134f58c0031a4933d8e12e7866687206174160c1da228d1de662f2eb744e4e122272482a810d2a086287b7c0401153ba80a2093272786187bb4c6ee9e0c5931788c43802861dd6a2250397a1153041811765ec700a26ef932d5bb6ac5b7e383d4db6c8cfadc6ce9ddb06acb4f83c21b72e0404e370166a02813d9b4fa8f40d5835b5533403000010143315000020100a0704027140241a9297457714000c7286407856349647633110c4288661188651c410630c02041883884235740300b6a5c4bcde1c82540cf75a53fadbede0604b6d44a7e44c9984f2b7c4d48d87542d4c4d8dca3c4c605237d92934073b22d3df8d1bfffdb53df3cbab1dbc5483f66094a1ead6a5f2b0931f64f0af8bcb973eacb8ae1097edc9cdc38fc34943641ba734167a79934a6ec12ac4180a230bc42508e81b764dfdf8f56528117e3d6b1051ba19caa9261dc78ccf2dce743933139b90391d5fe50bcd70f13219a3ba0870eb272af211e15e4f8afbe6df73f7d9e88f51ca467c21403b966562d1e6f50bb6d850d0c1c986a308ee0a8411304e82eb9904d1ca0532b236c7ca21a8ba1fdc1c502353d23fd332f5b8e15cd7a78e86ceac41cd106e3889261b58a6a332944f7c67ed52a5e4f08634c2a21fe456e069c271a920c12efc6cd418ea5d02117bb0411cebd1c969593a28daf84c8cb789ff68a36c766def755d3182e70c3da8e0f9f649e6a43b44b00fc99179aa9c510363ffb1775bec2f18b67e0088cc0e0087604237ecc27a93fa3f7f751d2c18baff8cc9a199134380d0ef6e53f83202f564cb81c5e9b1dc35e8ba9f0fbddc0a723e2c8e1b09b60c62436e7e5ae26f62539f51b5de4e40486ca22c3323d6fab9248a856fa2a00a8889977a29e7e119d96f566f15a39ca4e7f57293e16ff8a54943d2b92ef3e72189504516102a74d8793acc361b9d8fd8019bca902ee514a4ed3ca6312e09f60b70ffa07ae0ec7c71135d490aa3791b00e6d9830376e1f872155b80348d1d87ee4b84923526fbf8e03a16f2f596f828f3281a3000229aa40d38a735d169bf813f673611bf805352f3e5c776b3cbfcd1e90b69e17421946f8973435094b51c46923a2a9a759653db8b7765d3c5cb118f9c158108c4f74e570130511712e1c93fa26bdfbac155df237ef3be060f4867734ad6559e8766b89ae3b6e9b4ab38571bdd17d41a026cb17b433978495d1b07a19ecaf00df4092cd49abfcd4d7a00541fb9f19fdf5d56ce86cd56dd77fa5726fb55af9212b48aebea73e2c9ea609e16e801d9117b4f456b0c9ba5c7eafa08ed9667cb3748361b79380dae6cbaa1dec36afce4691fc154d9339df804743f7c35135cc4d5aca5e4ec142f9ec24c2417a8e6d715f22cc700c203b1633be805491205d917dcb18110b22ff0b8f9d4200b90529fdab6c27e7b203c3bdaa9f0f8385a36322d5b54394748a6b70b462fee8c432085a70ba4f0067ac14aed82442c0afb465fb70baed430291c105ecd8f3193ac146cd5fdb739a03a10ca0dd3eedf7f2a4de84ce663905b968eddcc862928bd8122ba93ba4adb53e0839881f7b522cefe35b4136dec1ee33b7b385cf3bf97e4f92d97acb99b5d3bd10b252304d8a2b400c63153b2314d09c27b2dc4d0ac29f7b18b3ada0d1ef52abcaf8a2b6a6584f77e36187b272830d5ee49dd70055bc4f514742d24099c53fd0fc5e1ff9484af11439034ffede4da549c10efa40b06d524118f9a6774ec31040f910cbd0870c923b0f9ad084a62422f766ca08d824b56ac69cee980d8bf293cdc231f5a455f718e5be9c903ba5258f1077324e964e99324e9076bba1e1af450157fc0389ba9cd681a8864e4d4ef15f414941012a19675ba2014b3c9df5e2d2f16f608cff411035e3ce187d49b4b35eab253d8017b1146c195448d18f7a317e30b59c6612a12b322d96c79325c53e00e4b91136d6abfc3e44eb798c9d2693bfc69710aab199e5e94664c77df4871d1a2a0c2ce939de0d1738fc6143bbae894fc57f9e1cedfa90890870d1c48369bad1ee1a5e37dc8b96c70befd175b885ad81732c581d241c691d0389ba457960072c314fa88e1d2e5d65be7522ce72e05e6e1e92bfaa448e3d2b2d9c24ee65f8a22a5e6229bf6728aecb83e7e9a0bdb5164c58f22994d7392c0f8d63a570fac99949305b2c70d0d28635546b4c1fb0ae9941a25abc54e87ae0f0d42ac3912baa0cf566b6aed7483f5367217670734e61020d3c0c17f2107842e0c39ba81de691ea6c0b6ac173c0b4f1c93fb927a0ba33cc5295705f793aa4de911700d8ef9d8dd991f5ea475d15fb98fd18aa4a78b93ccf9f5575ca77ff02d70017d68349477a08550f39cf95a8e3c8411ce6433c3dfd1e24498003e04ac84cdfb38a3160e963b32f545aea951e0a208262d3a4578181436ca9ba1ff855c38b4de3b04c2e364888be85b3688147e4216f1ef54369a69541532b05aef7f74d99c7af71f3265b4f731d784f3b5af913a0dc74fa92600f88f078129306321fe89b3ff1d96f920bcc189cddf3095c483ff5a3a381e026ef6f1a6a17581bde4ac208e0dec28e0aa495c5defcd6016c0cfef065f2c15d6f1ba189b2d80506a95489d0d1819ad355dc8f71c96f4f5c888b682dbc3b7f403d27f2b28d63c3547d8cd21fba0629ed22b58554a48deb2bc47474dfaf5d4bd8833f412461f7b9cf2f154f4c77919f5401f74ac2a488dda4089128dfc6176c08a58f6d061e579a7bacbf58eebcb8da11de160807026b1092840d4fa81b6bd99696da9be55ec7b3670a793b5507014014788c8c7368f4e8d452412bff3ad3ca540530f59371468cbe49d0fc9f4a996cd5220e4ba73b57addf99e0f029240215c5e710704b5e0180914235f3a095ca3f03e88a09f208a561344bf5975d1fba23174ed826cdf3f758ce9a8168dfa737061de1e32871f63bc4ca2557395cf0ad365a40fb24db3ae10a74ee413e8cfcf438309d5b09c29c1388c8f284631a07a5b8c982296660406272a82cf49d30944637e0f153a5140226de3bf20ec27f836b1774b26d07cedfdf14f17561427b50a10ce21891168acc81b37fc067a81bf600e36ea765861aa32dee1a319d3f30eca35d9a93d97112178e3336ed99751452ff37f1c4d5917f75d4c3909857ccd79a1f97bad4ccb7a0028ec0b196a860d764efd8718cc17f167bf0b0a1023b9bea41a712d0e8c1e8a6d2ed25abde3f1a0de7fb42a245d97b316a69b97688f73e4cd6e326a7d32c9ae7be76548277db616c0778d352c0bc6eda03fd8634e40df84a39bb05f24dbd418adc47b8fff8a5f132dae1b7c5329802868bb19a09f452cbc16f6bf075d0875985015ef30efc403f4fed7e7bc01bd1aeab1bee531dc8c3ef814f14701393182cce561e5a08286b913bab94f9dfe392cfb6c784d3250b79df3340407c9a241d7a388ab11f7c4f6958fb27c6056638fde97b2414f26b940cb5b0966cf60b51b5af8f5a96e86e4b0cdf15bae40cecbe03ad2060460cc62776dfcb0346b2a120c3552c1fa502393434a5582207111d07d3c4b68115f4001dc3a67f428c1c588de22a4a5d6b9ea78fad982ec31a7a8413cfec0d4f13444a04f796065a472a483bf3691f9698726ff3bc45a8b149e8daa5641e773b4ee97a52a119777d0550b53b5ed05c9c2edf434da04a867d999098cc03c96923436466fc965731b147d1d025e4cf71bb3cc76b8938158c55004fcf55112be6e9be9a7f1e291efa83263fbff31d1abcfd6d2dfd88020fe108dbee2d5f1d8f41ce157f66dfd0d77b64d806eac1c826e025a8a9162d51f1e001f618cb92040044e48ff1da39c7bb187277a053d0cad4412eafb582ad72925787a8edf44221b24cc8d2aa7b4484b8d3f0019395e89b94bd26a1d62b1f5647f6374fc3385f8e7d6c8938ac64f9053bf772dc6181c607763b6d16984f0224f66c592c4471081a9a5931c9a0eb98c2a81b013d36ac53a31652868e4e25e22443fbc40804428a7d874713284cd05c3277d9b972caf432fbf4b2fce88727411c9a85d44ae33250673ec4a3b0674cdb9b92ca89427f2eebcb24de3a630583ee35a494893a863f62e014d76277e8d45877a0f9d1373659e1a6fce0a86d8807c196b3e2d96da85d7a6131e629e2b606dbae52d60b1e7a0dfd1735a0f096a27807e00681e15439ebc5ace6d7ab16c5ff3a8fe94a898b20fe0f42ba9500d0af1502942ef1b6cf1efaa4ba05c4da07ffdfa21775f65a165f9297a52da01826ae0ab3b4c9e7c0ab3ac0fcbe63649b4802473981223dbac8dfeccad5b9febc0b5af0b54a47a9422557fb18e1497bf41c733cf32f09125087ab9bb2e6ba96473b1dcb7702519878ec493dee0ecd7864c3a0b67d58ce747772dfd4a89fe69cc5ca0dc0c1ba7f2e21e2529f5bb81d976fbbb95931817f1d989a99aa287c244bf2795c0d71e967199287ac6973ca89c1a4554d7418f2ef32c7f05b1ae31e618dc7e917510db0dbbb89501ac8aab5fe2a66e766750c3079717d3ec07cf2eaa84063123fb7f03028be9c1ede4096600169a1d81eb390062ba87b452ecb91ffe7100bdec6b2406ca11b7903d5a8253b09c7e20cc2ce6ec9f257162c1c9bd20661cf2c4b8b4effef42d4d588832dd8f4b7cb3d0f7b86c6fcbb7bafc1a88ef13255b3d2ad009fd76deef805bda8761bb7c0ea8df11cba31a373281ea1d73538f7ef3cd603d804f02548efb0402737119ec308d705b6d1addb33317dbf4b667922bf2bd132ff3236a07446f57c27cd902a86be01ad0d428615a96d9b2e758392e20b5835c1e698fbe0ca7b7fd59ca7a0118b739eedbf7e72d51fed5a764e0af649582119f1836730e1c33e1a8744f32a432e6dcddc1b8dddfb20f84ae7458942a16b80d441956a759dfb541246200f3944609e21b19f8f88386ac8137365e07c74e6290faa8541faa67e36db620eda234323732a9162b2cae280c535cbb3377e542a37fd6a254d7d37ac1cb43cd9948375b9d51cbdc017826da0dc7dcad88699f5da7d9a6abf91d6710fab29e42b41009abaa902014757b11c36c8f860290992e65417d1908fb62278b6800daf9dd0386acd1dfce6f22f3d0870ded2430ebffc40b10361882829d90c9c00f5b6679b9cd5dd04df5106b3c0976d387b1e391a0b53919231fbc6d868897d581b579ee55c4617f38b1c444306d3dbffe7aad4b92852c19bdfb9d72b0cab0bc5739225d08d1b4c6a967ee982e8c4fa3fccb48ec0e210c4f5e079044310c801e1d0859c550ed6e7c8086e1e34c7ef8603f0562a16e932c4fd4678d31e7a2cb5c42e8ce1982ca5dda4f2347c5f385cb410021994d76e283e8a27597e890689b8455225b1bad974c1c711e4ba21eb2780b0bf93d36b46f2aca5a161128908819f84e250735968403cf346f8780f683cf64a335eb2cdd758a30f67ab079283272102f28bb60789d618c7f81c8a2a46885313de6aaf9b5d1945376b31047c74a92b6a6d9e8726c678e376acb193be94f639a3692f400b4083c37ece128e83f4ed8f62c53c423e4ca627e8ba47f1c38639d34d312592a028f9ef30c16d039bdb624fd89d8fb9b9c7652d6ca259633349d9c57797fed0288404492367c42336e9d9015627d59e1e9da51638f546b222fe41753aaeab21a17193b6eac6d560df74f190087a089768bad17ed2e0cfc0632b540c115869eb9ee000ea620c5b69e4f188774a443cb78fc05295d6382d0cc91a73cf558aa9b347807436f4790beedd360d04eaa6200af7c9806c37d9b55a3ee6a5e64b92efa7109150014de209c2634fbde245497aaf21b04ff53de251a8e62a9d8b9a57ec1c9c13d74209eaf1bb5847dc7ab3cf63691bba8e2b352a4c5711003957601c51aebf598fe8edf984e2af70c6cd6237c32701fb9926a4de14d16a2ab781761542cc30d9b64a8b72df1d484c5def03ca699f045a1c5ca24ea9fd12e8faac9b3d611c2e984f3f08cae0b39577ee06134153ffb4fca02c2b1f7fe55331438520085a8160df79bbbdcc2d297a2ec3591128032ddc37fafa18475e35f694b211e28ac74647056f5b3f93566d8b91496c0baedb2d221c15ab168c20e81e76d85777f87a48ba5acb12b92cbd5792bd93d7f8093155d83ccc26c3fec3459b357cc81707132f6068c4692fdde0270bc5a7f1735214a7fd9571a64a71684e6a0a5d7f3feff18ce4a85b8e052db9f0a6788fcefc8ae9c3af07dbf5c3ccc14b6c6bc3b02bd3d93d2a98b32e3a74de3a067b9f456a26e552cf2d09961bcbe48710418edc8b4a4e8bee4d93f9cbba57c93fb957133dd0f2449da379d1319ec648cde51dfde6b71ce1ec6c86023b1c1a428a9af3e783b574cea419c1870f324a5f4fe8d633c9edba82771bfd0de7ea2f3583510856a6ce14b25c9ad75f51cf671c5e2c3737d6ef9f21ca6598d220b34327fde53fe2bd47983d3cd649f6b8a8ad0f60e5397cb2f4e1481d3f53a9c87605d68b841efbec36d03919c5626dd306cd044e5c753d99562c95adfd6b12fa374e830778406df65c1a89a0c394ffa167337b8b2fa06cfc28fcf97326b9e74064bd95a5aef114b6197767b9b2d0cab5f929b24b30d3f5adb299d9e87260e7f1d34399cbd81f170847e9022b4db30403192e1f653d54486687b88e57ff5b912391cc1b4150028e44a57feae9debb69e9af2dcca367de1fe1aaf682072d85fbc05ed9bfc79abf3aa35688c31402f241dac5a1e073104db08d22543100b546a8c8d3555372bb5581d597cd0793f655992ef60293d4dcbcefc96d0a836b7260125cd2f2ca0d38e3304873808a7f26ddca43e0be040f5d025dd301d042ecb551c495d85badd2220d87846fe45cf24183ce0cc11bf7f679b55513cbeed7a2cb0c1b2d22fa2df3affacf5117c645d9f0f2c6c45a92153d4047dc9875516ef2d30c0aa8cf2dee14a87600816543b71893c5477f6a75a3ee00f28d757fd5eb9ae67ffe11811545c85021ad0f7e9e02442b56e7a2a55e8696adf895e25e3e1861f3fbe0f1c28fbd8a96502bd49c1fee55cdcb00e74f19608af9e8d19b74d7f2cb2bc0f611a7191cd33fa7305caa0b389cef8afb260ee9fe1a64b138045d66849bc79fbb97f2993dcdd2e7ae64d1ea8db2f96e4123aa7f3263aea7c5039e94b8ab6af5f8cb8594de54c7dad6283ab80175eb3b81093b4e9ce9d7ac06f08accf724dac89332d7b6d29bc44ea1bf6789e32ad0de8681231ba0a2fde3081759ec45d36ff30b68f3841191d75e3c7850f153b0bb0abf8509f7339e94be00675febe13f4500436e7380076950fd36de18a3e84b0110686db04d5a157bf1a4c8bf15e0b8dc982860caa08a379147ecc0391c6a446d38ffc699e58f3c1a2a37039bd0d49dcfc19b5102f18fd724346eb41f135ac8536ce0dabcfd7d9843d72e99ef75317b942052c81cb87d50761fe1b780a11d4d581ea6530c0a52e48574ee6d9c5bcfd16a4634e3f173ee8068a363d845140317443308a5d5f48ea2d917df8c2bac2948f81a5b526b008a7c3c65e58f064d698bf69793513a52d46826b05a051a288efd1f244536e626696dbf0ec010bf2791bd3f01689e8809d3ac28e139f67d29a2ef4f99fdcd4d9dc8f9683034f7a1dd34bc54c920b01d2f6f502371b92dac1da9f719a85fb791fcca9ff93ef278e05b812fcc6504ffda05406fac9eaea7a6fbf32f09dee93abe358e42ff26c0b75d6bb73212ce6968b10ce1d8ca8515d1bfcf8308af7db6310085d0b469fd4dcb2cd66f2e330c1b4012ea541297b15b906d1db378c425bcc57787323d12a14082cf164db67f02c4b9a82f112b6096cb0d38a9395603de25f39bb185bf26991e0864595f0ef7686483ff1eb3adea39025497d4357466114857106b4d8cff02dfd8d06d037a55257a54b890661445f89af6e7382422bf003e6658bfcfe7f3d7a901f2dd95bddb1377d76978a3e305bf18dc282cdb95bd55fa3c6876e45a5c64e1b279947dfefc242efac483ae37a4599d429bbed46569837e5d2977d08bad02da63fe556553eece3eb92b6d9073f70287992028910187bf641d60cbf88df0fa46b44f20364b9e2414bbb8552f46390a07c02c718572b7dd00a8b24773589714d988cad11432cd8b5609d6368fe8869179c86cd0d556cfc969392954f5c949b19898c6cd1567a6110c1c7c17f4ad30028e926a0c1f13cea87229ad1627c0efbfb01e4ad6a47c96b125798d854e96609dd8e3d61149db2c830bdee05f25bb137303015f27816852a4d6078138007b82909318434789d892b806084e86c7669a9d8dd7ff0ca134f23bdd235b2a1f6d4d91e45de6a0b34251b3ea8ed5b6cb85cf402821dc5604085436ea3f44b0d2926039300759cc5e9f1de6ec815cb018977351c28cef03adc289e713538289cfd0adc519af56a38289ae94a3823a577c6ebe140a27bf5f9f10efa42f4d1d454edd5e04e69a62b704bf18cebe0a0e4cc57e04469b657c335287cb3af04478eeea93b1c782d679f22083ef6633282085ec20782c276438f16b10f4680577e59ab9d9a11a39012d7bc728a93a6960bf41c4c1bf07fad0513e6968589b633b609fe34f71d8de46d2261f717b648e8da1d674e1f5066992a94368777e799aa131aa7f7819625b7e337e1c97889aac0aae4119df58da5d2a3550e72e7aceb1b8c1d814a9c5d7be377f33ad290bbd4521fdfb8d7f6ec1b412b633353c140b141af45e4c128b1ab7a8e5bdbfc375acfa99b38d22dadffd1666020f406341968423e46499538c70e6b6d8bebcfb216ff0651301f14d4a24a96049fe0cac89b19f80c82f40ba26fc9f088789ae8e572357e44612b6dc20186a61291ee503c7367db741d44eb9e35c4113cfe519ddb4c7619032e6d6c25fd10094a8edd95189bb13c045868a2a747b6e350b50115e952b03986c76d7d84af2c6cf8c0de16c2486ef3f7a2d14f4256a2aa6b4c1181f1d4a45b4fa00ebac0d10b597cc785167651ea3e2c368c3002f8988581f5461d5b97c7cfbd6499d9967ce00b69f0d9f0acb32853e3a8cde9ecbbaa84c934b3c153b68d2ef64b4f8cf251318e2bceefe1382a7061db8d3ab357cedc2b8372953e62501e802daa0b4f1a5b9b90453b0b94d1d1e9076bfbbf53d1644e6ecb034d6a62b913103b91b0610a569882d2f7f037edae0ed862cd6dca66672428dd6a8aec7c1f3d51dfdd53fbd4836b403f06d7e9d7b13ad50119d2385615d27301ee92ac706b4876cf96e7aac50210c73a2403d4b9e06c5471df7dac8524e0ea14b5fbac0c4e8505ba4559da4d2086e10650b8280446677ac32a4babbd513806881788763b62f51af3f8b10a5f2d5a28769c27c04fa9d333cf66cce4a159f36b3d467b5a03b05697a7b8162f47061cccb531006077883cc93d07f33c925d810af200f0169f4c5774bcfe2eda1953fbe357bd69932eb928a9b7552dcdb6c01f6cc20d2512e25da0140422a74af9c3cbbc7f6df93d55d08476704412d33b86bc88092c9b368d91f4f5daafabdf7b6545a1b33bbaffc5756fd9cb8e37cd1dc4c599b1b51a2d6b7e1c0821213397eb8780ea4d9ac03b1de75b2d7b19b8cae39505d34acffcddb3a490defed76f3ee4a107b8ff6f81ed58aaca6a7c9847a24008afe26e2f3c8017d036c110277178f16e028c06354c29e2ab185119fc79f4346308edccd83bc1f0827f76bb5974a0231bc372e35bec0f99cea4a55aa60027dd151be4d48a8648b5dc001e379effad856782dbbf033db771ac34dede1f7c06df644bb548014adbe7e474fa729f637633b14e0c5f3f9f6de13e8370e3da92eb33db1c8313f4342ae018e724d1b71d8743c9b593c6215bd4327f229e03889617c0f7050e04ed163734f98d0958cc924ed58b09982aa79116c49a8132eb297c5c2d516a01fa90d1d61649a29b0518fb7968e16c70fd1e994b4f58805378d75a31ed276681b0c10d9d023cb3026ec3ace34567c112df982a28f16f01d60756caa912467576814907b8d26aefbac12f4f93225caba52cf4211c7812a0b9ede9120e9dfbd573080d2de0977afae545129a63d603d66042d249d2d7de3706f4a175f04e5bb477e3482e8eeb61d4cd3321936efbb924b653a8b298db0e09eb2c0eb008ec9100450272b90eafe6c2442d9500d7e72313ce423aedadf67433d7c1e16b4823e41bba6a9bb31a9dc5708139b9baf5c91ebd07d54c472e212e8df244e50648574eb52887429db8a0dbf8486992486edf0825e0794f1e9f51b2e35329d233a8041056cd28bd4867d8940cce307f8e9c95239c02a3238001a8b32dd273498e1604ac41d84d326252120038b75720f4bb93ab4db9c7a7905a3aa9dd6abbe6480bcbb5c735308cc8898d6e0330af0aa3f402042a9b84a90c925d662977aa068f9ce07979036d4f1c5ce4585d01cecb39628a6b20deabf4db9b63de50497d1e47c25058ac1df1f62517d3bd8335fa21cc18650f44a9dcae1b4bf12a8ad019f1d4384b1738c6c652b7d0aa75d8235974252d7b36e87008c35676317334e2dc5fa91f7d26f9295efdb8b2c13ad057bb138c2128a0316e7c155a9b392c80e31fd924b668579385fcc2f0b6aa80b4b23e3fd3ee093e809be88510edfc6a01f537d58293fcdb86a79150a0b1837bb22a598df5278b03f09dd20d54be0b7ac63df5c0ed544400764f9f87b21ba7a59c9f7a60993790f4e078aced766f763c68dc0d55125ef07a3b378f923ae37fb5de8547d88f25c2a6d958507bd773b80636ffb9dbba0f0cca5ec32d6d13565794cda02d3d551a8d8fa557fb50e826d73488ed8e8a81c0c5dd44278c6ed96eaa44f2a46bd93e2108c1710afea7c9135ae60d10971eb3820354aaa7b7803cb79bde54eee52cbd8308a3c57923129b3b2a4c635668e9ae526a2e53a2ae5aab7cd8eb52dad5d442005e248ccf9b88e9b2949c311bd3aee46b29a8a12f270c0f52c359e8ca99191a31b6780f0e521a8706b2d1319331e8c4ea69e0a989cd8ee571b50d3af657d63068e4707a019fbafdf530037cdd8052039f584dec3c908ae820fddc57e18f10628ee8c4ca908ae2df7ccb42041aa663b93191ed4b1046ddbe8b990a82373444e186a35b4b168624ecc0b876df665e858d13191615366ac34067580ae0c838b5abfc0552a3e3e34f07b528bdb13c1bdc686d3746d3997da0e990bab503b799a4724885030f8034cfb3d2703c4179249ac1fc11873a65d13c91b521bd5e2dfa61af04b2aac0394c3f3a82565d81a51783fc0bd2a8ac36447ec5cabe0e5c6b87aee4102e1fd122068afb4d82bb595b9b081d845d0319d385f4759f5b61ee6a8da6f9cb1588cd8c40a06b39931f5386f2194ce967d9e8b2d70528c53135b34d721cdc6a4e75c92d21295820baaf725a28d9979c19021ea8cadcca18a595d2b58daaea1e9db86433006391efda745aa48c2794c1fa8340205c9918594d18848cdc64c7b7f78ffa1b9d8f7b53b3a992466603d46f6daa539e43ad217be4cf36a27c8f191cac01e4575a41a141266a4c10cc7c8ef8d8e670bc8bc460b943b8507c942db57fdabdfd52e4c281c9aae15b080e6527e56d72ecd353f46c2a73a02e9a3c5aba5ffc30f3a1a99d5714cab11c22400be8329b7c278338058e94e2b3dbc6f1b2e188d91032a1cbfe6550e3c4e47a342c54dc35264995952faf6318ebc176355efa01f560cf53146ff12cf7b3e7bef2f9b991514f91ccb6c0712063c94977482ffd463f874a445fe1696c02be0e9b03e8534d92a740d069a5fb9ba601d64de9bea1c7836830b458a1ade315ba97854576817121c497a6eddc53eca580e8f202d0348f3aedd7c0e9574a96eab1bd7e591ffa03e971df27f805563f6b615a6ce0e47ebece85f13e6c97370054a6105984508a3694e6a2c5be0a38bbfd3027ea2b58f4a8b22931bdec74c4e30683954e9f9cdd6d8555ba37eef9795e9fefc351eaae559bc2fd4bc279d1cd981affc89a4751adc0a09c81eb510428237d800c3e0af71a03726c90f4381683e049893ef81fdd156c2b626e3fd5984433947e42afeb96a0e69c55088b182083e39964252d347c3797c6b2389ca6a84d18094f34ad6c7554018376a8ad5e67edc0bb247fd2b02e68cc938c9805eba238933c6da48f55ae4e3d91f69807dd298ffc811aa10f51103cf72a4b485d7a8f47ffb58d0f30473abae8c6bd30810fe6dc4e04852c229daa9b6ee1c07d57a58582916e918186d9fd1a1f8628d15aa5c566002d457214759affc07978e263d4029dae658fa5687caa5ee0b26a018e02419c6dfd4aa1cb5073536446ae0bfb4ca61b7e444420314d0f8db317a1e9b78dcc714ca2d055c32957f9cf082cdc848085aa94c1c19e53686fb1d6cda3fbd71713724587b0f0659d024876ad7ee1c98ebe8981a750f1a62c55c02235fba13b18864462ce221a90099862feac2321972ee5cf159025a63d0736400c8ab3c3ecced6074fc35b7ecac7c86a205dd684f8cf36ba41d99233c13c7b675b01a4476a7a6b525335fe6b03a153c24ac6b5156277312cf473258a83e44a1a7176dbbedd68c220bfc47e891c10bee02c377d6d86da7abc57bc5631d9fb5857e923f7bd1a7bef98816071791b53fe0f628e88bc4c2861dfa769aee13221bd89c3fb5bc9af0c1628d1f82beb69975d1f519506ae71971b6cce8479040b438d5a6141e3310fd2db2c7d707c68f68b538c46aa2289753e7465dc9dd84c20ab034cb59e63e682088dfc7235c9fedb04245a4659cf311f5a302315f27d3159dfad423198988e847e0141b60caac2e9d0bf9622da41ebfb09edfed055678c05760074ecab0ade492cb97213cbb8b15689872bf79909a8f34c2f5a2dc1ab144362f203388f934e507ef20f95910e08fb584ed65ae6fca6c2e765785c46cbb4bdfa05c02414e68c80d0d6f0905bcf3e16db2443f296abeed16b48e7d9b25513e2061cc3addb4e47373aacbf150c0528c370a3e31e208bbab2eb675dc45102e78380dc5bf5233fc6c7bcf8b6222de24b1d6fe8eb811510314a03f9b259ccfe756ea83f4a0fa6aec9a7696ccb8075581227c46f3fb6284dd2f27e50bae82b6b1836e2a0705cdd80813ea2079ab3c505f0f9e92802fb0477a2c0f0427349f2e849ccd2044ea5e3a849388feef7e678d979110db370e1716b8086eea4203d94f0813f1c842b84f364e18ac804a23ac4e1ebe2735bd782e54a2d983dc7e9f333ac7bd2f28c98255146234aaf1778510e1d4f76687142843e4df0c800abbf908ae9f7cb80ed843400b0265882ee20d1978c2533fc8c109a4d400e7bcf6a22c6efb7504cb63e7e48e7a4b71c79a731c04cbcf9de1f6452a2b1bceccb325020f83ae5820d396000e02afdae2f2ed7a6a661b0f61d76fc08c7b065cedc5af29441d95258c97a9d689494e314e024e4b48265fff7fd757cfcc2b408b7da31b4e265067e7e2ca9810b5e1affbd8fcb4ba143a8798150cd867484f3868fb423b9a1a6a2762cfd57e5c2ada47ebb908f900a072ed08bf8ecb52f2deb6e4d994db80534c4f09d379e747f407879095fda4154dd4107a3b936c506566d516201ff110f4ba1b3d5a50d4b2d1862b527a96db8b734b3af1b36bb9fa61ca60523a261f6d354f79d8e54dca9a89f3e90948be36a1e6e755d476ad191fc155509dd480404dd792532e431b9e4d3df2cad38163b1b40224aae331c24879043032bc370f7977303341beb8d9ce2b359748b25830ec96898545c3123212329d256d403b03b86284090df9872a5222a4817acad9e49a623fd671facacb8957dbd04c021a5b10c32d6eae9b05e26112587f620a317b1d2fec2a92f03261d1ce170fbfdab09bff3266051d5695dac95dbc8039f4a362f17ecc1bddf87eab875676e0b7ed81fe77cc16a5ae12e2a2c1894179c2c4d183610f88d90d3faa4825b4254aa6ac95046fe90948ce5f5220a94672d73a852a5e4efe248bf6951d61816b876cd50349efc4b57bf44d0100cda15561ebfa22726e6b523ba22b00971f462a3529cd82eba951ccdf41466a90d963d97541dee8c1abd61d4a8d962d81c644877e7193913d24289a39a9065af65751e2af01ab7fef8d667d0433e9bc5126a5738f35c7fd2aa70daa7b2379970bf8b7a2a5b45139869db041e093fa9c5a20c56bd02d44d697a902aeb40c5cd5a1c160565480ac3f269133725ca44d4692f3b1e8b21e1ee61e0bf13c433d1601498bcc7a7a3896e00364426c9713139b8924d746a4cc1e8316125f89c59c1cba53c0ebeeca048cbe20780837444566c172e581bf4e3ddc26731bbb0cdd4c4fa3a8fcd7ca85c5998d71bdd9658eb31240f5f27900ea8c856e526737e849931d1cc13b66af8caa18d36021bdfb255dd8e696ed33e8c9a1b04c55b6a5a0b002b6e653937179ffb1c554e41e2c98877394429bdaa3e954ed88f9d6f8fdfbc824343b804d902ce7195b45447a4bb84137b23ab22d16fa76f190a88cc0087b52f6bfcfd4710e8baa15d0f289e2e989d7c7adc35e3b5f51e05ec8ad1c677f61c81b98fc553ab7904f9208cea88eeeb71b7725dce96d067a7dc92c9d9a39532be2f93b0bc7378e0ecf5e4e9eba693d861dba42cbd2773dcc09847348395e51e235557fb3fcff74233195454f7ccbfb62cc8d349c8f467f1f6b2b67abe4b6eba226e36d6650a48492897c682dfa36573685d9269fe03b9c1d333cdaccf1ab39812ce7ddb700a174cd86638c70d579d4d1140a2163c04b0269679706907965d5346e76027c5feb230724d518299f466d20070a780abf5cd0ba58b7dcef9f8706145bdd924d666929da33ac6d081a77d7880029b8ee9a1d9eeda3066ec6e70dfd6a7aba520a0d45fc67bbe815e91434653ced825f27c68362d49f4579be1559b4def0c289c53b06eac99c7acc92a9bb32bc79a3ed4fb13ab0d7b70de6c1467428c82cbaf321835c2a1a0b44d8f73e58641c7a6951ea99e23fadd353f1aaf27dfdf818c13831aec05cd2c181d091f7952981c7e1e88cf8e60717aa7b7a25c78e88ed8cf726c2e9f1f20c639bac2996fa3315f1fd1cc0f5bcafb659772276fef2f0b6ef60c00b7a236c5e3e34f75e59b7296b3148f7a657119cbe2483740e280f72f6ba47c308235abcc88460f420923e6b965f8336deb60f3c9fab5e222186e0534cddf7d8a8c8a6d541a2fda6bb3e6dc321ffe6117b1d1e8214c709bc2a7f575c8e12800e0909c2c5633d253cebb77474d57bca08b00a3e911d8f4ab73003cc69360753054160eb0bcb359e3d3e9bf70d7c5b65986546e0e362aa197d6818f52d5f7f58b63685a838dcee7103fa7540345f891d18a66ef4c58c9e2c5e5cb4e68a3bfb5ff66df815068988e9469de1e314d3d9b21bcccf5790c30ca0e5efd580e40d435e328bbce21d6402c53528d53f5381ebde8ff29f35d1f8adca3f1cc28fa217d408b073009a2968e66720cde2c5811e5a2d3388f3f05c741182d79145cfcf10bebc937e9bead10a1fb944014e3ecf795c54cf3e06e6688048a339b1816d61c8a9cb2949bd0e8232eaea4e509941cb9b6a39ab71804df3898360e0e4d46bab5d3089abb6ca59179c056dee443392ccb644ec4703f3952fe1a71d858e011672d4026200017755e72bd3482e307670d83bfd67cadb8f50ec9535033d4370a00448318de5921db3209fab908a31a46a298705670172fabfd28b6c0880b2e8a4664b45c5805739993a9fcca6c879a88e1f082c1f2ca4d9299eea838a13bb71c0cf17bf94d82b98837a8863832aa05aa741b587eeb33547505d36a5b1b725590d92aa3df9538379bd5a6f7a750c4b50528511067fb2148f7b08a9d8372a24f96f49818cab9436e6b084dca0a3bea9bb7f4e457d801c2acefa5693b5b21c064f29fcc01fd9e06d424be982985a417192158f96ee8d26d5d7704e0afb1660e1ca918b5a2905de62b0b78bb48e4f90146549ce4412a58c81ea109d8882a05282abd29c0afc732208701da8e24b29c1ff65bf2a94ec899a719548bc0b217e5af929114813ce2215853671ddc30253615042e609479e5b7b9fecbfd5b2e829c65b19b38ed16ca8063344437b66cddf70ff19a65fc44e451244fec28bd834bec2bbd7134a085626162fc85d0297dd147481bf597fef5ee37cc4eabe4bbafce94c8fabd21ab7fe53681d99830840b4535567e3267cb7bf3b5857818d4d85a71229c3e14abbbe7542297396243980fccaa2c88f71f36b0ceb335394150dd80b4566dafb62c2c6535cb7a8b3d34f0472efe27c03126e5fea6b3c29a0a99100f336c238df4fe9bcd8cabaf1d1a903800e591835609e0e8678d448cc77720258057dc195f8f59853a20ef524149ddde8b9148cc2ddfc80c6424b405ee1b69302110736889c427add1c0fcc113e0c803894ffd57dbd5e01103ad6e5d3d18ec68f05555c93e9a9bb277fe86684aa435c2ed13a604236a1736581218b15587d6aae81a37b83da96bc504e566dee87fad29ba5f17cc7503579c18fb138d043e457f0f4c1202267209a842081b92a86a58c9ca23b1492190afc0334452d7c4f18ef0f322edcd615731ef476994562077db6da4dc816fca09ba9c47fca0014f4b4ce0bebb8a7f985967f52021ea898ccf13dab6b2bf2b541e3c289a61c809a29e1ec94fe085790396215257a0c73cb79a452e532452ea6ea32da0cea8e878358ec6ed3f1a084237d4fff0264aab712a1551afa117884a2412749309c4bd73a85e2a9b87d658762348551d1d747971887f3858138cad5c784dddd5670e2efd173792da25b5465ef600d408b2b10e168d795908825f808c7d636e2b1c8fd3b885fd0dfe6e422c0e04b10cc960b7b8a8c6c743be5da2c6b411d5c453488fad414a5641a0db5447a1fb8301773c75482490b48fa4c1fc3e42be737854e399a7d04c863d4cf8485ec5b485271dd2b87ea43102a9f3f6e4084bcb017ae2953419ff2fa1aa42330b2932f28a73865a9412dccd4abf4ceb203e2c11ca206ad13fe632400e0c2a1a6769f16f014a7bd2264760391499cbce118238dded3ab3ced0a1820fec33494feb7a6fa900ffdab45de59f23f15fcf33600e194a9b10f8dd33c8e1e623b742c53af378d90f8823dfb92d281235cfbd05a4485f1e5d04c28a47aaf1aa78da4554b8d47c691225f8a31c9ef0ff65c964fbf6484a19a074b84e6738227bd91e0a0b79d014ad09beb1b5621a467fef8b7cd6320c441fc56c1e41b578b52fed0223749ae6616d2414a516e6554a0019229dbae9283313c02625b8f767410b509f2e0306593efc311d06d7eab78a97d1a3aef85ea5889173f01fe57a2ee3abf8a4fa43c49bf83da3e2d23f24dfc75b86994f33e31803d3e59a334390b1e4315085d6f560615a92a22b3b33d3c0e4dc367c94d7e48c65c1ebe08529f0f232f11394261e6a94f681599ed7b28acfef9d17c752b6e3899426ff7d374c255bd7f6dd900c290433baa5f4fbc1c07ee9c426027f386c3b59191c25c1dee3f307785828febb3265f1cd6f359d4a97c013438e8c2e5cc47d9398af61dc359e3b03107299fc0d5171ee950812273674b8e59e7ef7d5f90f929682fe559c57ca029a02ee81cbd334b2147d5a343d80cc147184d74a99241a244bf7b9684198dbd53323acfd0058e76e43c68c65ac5da8d5713f07845985972734f75ee773f637bb07614c5e575c5bb3b7532f2917e6ad4485ae9d492025147bb9afe8b29f85e19bcf758fdfab56e01f15ca1da349666c16106d11b3606a277320578fbb30aeac7d618eb8ed9984e205c2c4bc23e43f212bbe6fe37df2a4b95fc0b3b6c8083bbee592f14e421ea12713098b984b8f07ec100c0bb0a7db3762d22d34be36b2e32776a582bc8e0f7b03a3c49be2940343a69fa0f89a5ea4f80482d5543623f732365b3d407c1393072bd89f57105d4d9b1bc0eca800a496405dc777c3c4a7f3e4843f5ee19ac26d746cf1b3a66c67fa52b6f93c6a103e08255ce7ac52c64de026cfccac1bf00f009c6f7081895233c638288cd37b5991df323714aad23e4b67d4fa56def8a424bc03f7caaaf15725b6cb7de0548abab2654a9e6ef32939876eda1740217d803aa18be2acf0cc8e5ff01f9f5373a9cce004de266f0c469001782d41c92257db2c47042db3d129dcbc406bc1872c1b819b395b2023054132c003d785e292a0acf61e82d759b3a10b1004283a150f44f2396aa3b75125a20897bba206c2b2bce85e3db6f576f5970a95d83c217467348b2f078508248c34875d434256f6087b572e531b86624e6dc135507e8aff0e39dd1443e95c06a29fd3ade0d7505ca353f93a6d5a68f88705301f46f7903f76929d63aaf3d641ac09ac05578f480c9c1dc0560690c7fc87b485861dd23d4f8e4d0a582fa9059564e64801a3eb38b6136f915e7db440de195c7f381cd6479a07675d3b34572b20c3e76698f02c3c500a3046bc6bdc6bbba66a7499307cb7a073047077ca98ffb1dd6170e82d1f99ba2095c5969ce0e2d7b0b934479d81a276dacf9f24e31aa3aa0904c3139a43ee3074242e85025ab87957dc907e43a5c1309b18d3c995aed8b77e744911901e530c03629f78dfad0bf7f272276a1944465456a47aa358468483ef6339b64341ecd730b586b5ab40247802198e44239afd05670bf16f7f1a396e10b8bf631bcc3cea05b29a64bfb165db99dbccc0a83913b01f1cc1948581ddbe76daa2ea336f393ab4dae9c2aef90881047826a7e262dc03c07c0cc0d00ab465a766eab432469931d7790a60c3ded4d9f78671b3e113aaa3b400a33be1615acf194f93251a55faa2f80454cb228dea72adb340fd5a3e6a06d0a5fbaed86c1a9547a7c3e99294ae362e45f39113a961d3407d373c1d18006c0655a432fd292282b81058bfbbc3c5bea744c086356d0716cb9261380e359ca1e7c99fc94a5c368a45b5c0787fbecca40c638d204ce7ff75867b1739b41177034d8b4468878a6af536564289461a1ce546a7aa3c3bcfd5c4e6a912ddeaefac97f66f50df9047b3d6347b4e218212e4ce7280b7d61696dfa3b1dcc15cac03b757f578578538def21b3ca073e9f3e07d49956170db6099aae195fe58bd27428ab52a579220ae79c92ebb9ebc4463d954cc0623b16e452a4aa476f26d16e5b9753fbe45742624b5c841dc067c82c2a6eaeeb3987b499fa6b9eb2154b03cf0284e2c5cdb25bfe3ec96053284f0e18558bd4e7be86cce00b8edfb8c3ee76f3c41405832d464f83e968ec52a055590630efd38862bef1c21dfa21cbe5991d5b9a888898aa0e4bd15a09737175fb111f356f1e527f225079e39b7d62c41b157c78d4d9ee89c187e0ad22295806940c4641cc4337e79dfcc8693a84b1aee12013fd2f9b1e0641b886f49d7b00214731f88afb8ddab263e628b1caad7896ee75efe3641759fa11fdd097584cada141d603661f9473935661c11f491975e1c8943d58f223a5f329472085a4d0166f873065fa8ee43162d4d9be74a60562145cb9e7701f0d219d2850fae5109703ae739be0820db70a348c57de42a779c609735b083f8c03a067921551d94e28094d2b25359cb20da509331642c49c6828d68fb78f6e96f45e486661aa04ff86b11533d2ce068ab01461d2d0a8c469c9f57136a466b1416259c5075368068e86405d5ee34122f74bf669d776f8b7f435786cdb4265dd808ea2ea8d93bf0179d7f41dd18a30283c354a61ab11744a22a7149d19529cecc95ae5d035aa49aaeb960bc00a1bd6bc0d43443a8c2312fd14f199962087d51df825de6021e1f9c5476bf2a8791fd1f0a622de8838d36c848678ee8b61f93733b23b16bc5a318b999ad295d169c3555f1c3ed00b3c53a152b8bf5232772f61016b123191c144d479a948e5ccc197c30812088ef1af120b559e8ddf769ee1a32fe387e4d39b192618d1ad562af97dcbe4460045c12a17c58624bf864209fc05a0ce21077f5d8690d4d3e0619766eed1a10d95eb40f39c1d71236c514192dc58e6c97d26cd13b9f04629908453641625f3907328d83539350cdb92eaf9b0708e7bb3bed0dd9dc416788f2b1ce5492aa8c6342109edc2568f02f81229183272594c99a516ff461bde96c357b3e7d47c6d0baaa2330a70204070be49e7679a458bd53aca376ed9f8f4d3cb7097b9e1cc1f0ce07eb68ddcff00e074b16aff71e269a0a4c5f1c8777dde5d8896143472d79d4036ca1ae1a6aaa241a8da45b053164f50f69025997df194ae76defdd00c0e30537b70d90c6ee1b0fe786ffccfd23ee90a8960e72d059b6c381f5506427c3b366c71dea2e7b2c29e7653722e0f31ca398dbba5c8a8d3f799d743df59bee3fb2e9f112f164e0f6cdff036e3481e1793c72fd19042ac717c3544e094142d48526704b920f5732583e1cc3b8ffdae4c4e333bd97859a49f452fe73b8161ba8a2137d65814955f77cd41283e5c87d99c2e465fd3fa3e03399d20ab797f4da97894dfa6765dc91d387fe7c5cc50d52a5848f50e0a4d26d71728a57c865f560e698b5ed32abe1eddfc9f108e94bdd7255acd464760e393d33b93e4612eea85efb9c548cdd7784e80211a489b095d56e284fddd5e0798d5f5ecb6e99f17ee62c56db873ad6fe7a2450e7c75388b6ccf31d602fc2462af8f3f942847a4effbdec54f345ac83bb5908b6531036e0ba4e2200f079fba830d7d2bac6fe15bb861f4ece29c8b28cedb9b809b311ea720e9a27c658d69389bb80907e0b0815701f5709080d9b62bc003b2784f8af4f1e93ed1ae164905b34f6332a7c3698210f35a10b915e92dad525aac7f176a35d84fb4bcc21c223d6a080904a49d82900b683992120b31b635644840fc9c69e5ef4de04ae804dc6da411062edc7d05d1dbb64f6e94120fdaf402d1bd13efc2c01198611d5678c801800f2ef1ac894ba258618dd6b6e0321389507005f2a61567b85098d8136bb12243f1c656cb0029379c6147f59c67d92f66590cea6ed36d72d05e880ae13d82fb28386f8163bc546275431c08746cbf3ce536e1c866f85a0424d5aab5b52f8cdbed064a8869220567f91b18766727cb464950388e6749b13a762c3c825459134b7f5b94239bebfada321e9f9fd8e3ed33aa3fe93ed8f3b3240ab0ea2a1e62ed7d90826f8b976975e606d101a143b8bb4cfc6a9746e22122a012e64aa1dc64aa0b9cb0e39c401771e8c00b1da0c9b0dd817ee90a2bbac4918b50f18d68a015ebb79259c6a5c8e73c02c9ce2625b07ea808610070c3bc4d88033c53f392b2bf7bed00838e25126bb88bc0eaf95e1115bc4c1c10a9e253f46fce00c881646bb2045324dd19f38e9a65b787f419ed59f9ab67d9f42edd9ec3ff0583c8da5790ad0fc2003a23b3400097b7f3c89374683d78f8b291dcc6f252dc259dcf543fea0d891e79a62f39ea9efc278de231ad8479647c2734a4a6b07c08709a3dd719dc5c04fdf653269325d076736be1ce8259463f72266dc8ac1bace34e6ba2d830921d02e848e588fa4e8b27fd4e2f448236cb15955eac4f35a871d043b17e3e6c0dd818ed4dd18bda61c0c41f020acf304c30e9aba3608c0419895e0ec386e991ccf63c2c0c738fea2629c7cd24835c7b109b3551532eaef8be161756d0eab22a0f901f2ab916dce7546a804eb3f57ea0e8a04e04bb7b633984ada7747f5cfc63f84d9e43b95075d5162eb926af67cf8cc66daf7247c703aba84981118a51baec475336bfcd5c77575233680ec2a33aa694cffc82a6d5d41042b5304721599d98ae6201c0fb722ba4559494c127aee763fff3aef69dcafcd83522abd24c27cbe1198a40f413a33d8dbd8695cd99eb6d30650b3b1a1eff2ebc378818e42d8902dac326800251b11160c218788d8515aeb00236c602e9df237837914edb48d7eb3fb5337f18233cdb5f5751c63edc268d864538d2b084d2e8e7033260e16c260ebe802e9bf2d08e156e7216c570ab33a0d31b6c83415c6c60aaa9dcd8b81644fa8f836d6d377adbcbb946f2d195429228465576d23ca9b53caac25452175409b8940eb2835e2bea513dada38903102580adc63402159a63dac82e02ce25aa3af49e424684c93372e29fa0cdb0c373769f68fd8806064eb36496c7788c686e4b40a5036342b9d6a672778eabb49ff07e82f481ecd8f60925d3022c80c603c6b2e32ce2b92bdeccd157f0388d3ee16412d381600e16375c8476bd25fa63f9b923e1ef96218a3f717c4017dac066eceed07f62e4ad22b256e2f33b4694896e5c7f0cd5fdf05c2d43b7b29e38c4a6b319928e105025e748556d2b0ef93d9501f758f858f2d76050e648afe03a0fac9a427301dabcb7e348aa789ec7b76aaca146f9bf6660f9196d7eeac5d10e818a87182b7dbc22ce10ade200696c1da0c5f29c4dcbc7e8db3267aaa10df2465371908443cb6c74d9871ee56d2939d0ae24b13d37278c2b81347c41e73d77b05a8fc24f9404272ba9b504a509647bad829e2876712e34a61461ee9167683d52cc0617cec808c18332afb54a6466f4d7258dc17158c257177efd75fea01dcaa52cfb1918f9360974c453e62eb46e148cfc448cf2399b3e52aeaeaeb9e9525008764e9a7083d887ef5aaaf7e17cff1819b2b42fa075da05ef38dc58375ceb02ca279909c62dbf6a83eed34215bbfd6ac6b8157111c3073554f4c220d5e1abfc37dbabf05fd4052ad73882d670e4d7473d433bc7873757061e423534342ebc2525d6d95c8522ebcaf95088668322fa7e6c1dae9d6919fd89a7ac4d4736360240de799626e04751a25bd134ce5cfa99bd4cad7e7a74081ca0c16b7b52f13ed4d8a30249631cddee8b0aa929b55af0b46c60d411dd269e662382fd1636110d771914eb8b140841296de61c6cd334799fc23e268226a7b2e6ecf6238c34633e21f33e85eee626d5f0bd3ec928dee7d5d05424162ac016aa76ab4fa476bbb12743e427c242179dd2d2405087d432c35fa96cb50782f7cda5ceb8ec936f9c7b46fa29c4d114f0861767618764750af751b85424ef6d1bb1b20c96d1a7d791d61d31a5e40786d5bed108d4b10744b4b6e169d3bf1fd086bd1c9476e7b6bb7ecb0d5488dedc30c3184ed7abc11f607c0c02cff6cacd74d8cf4a52cf57dadcbf32a56c6f85b14642bb6dd683e51f0d1817fe80fb02f5fea0990f3b71720adb67aef31722029ab938d93d8d0fda3ad33cc291dc04abf09f82e827d6840387579f575937cce28072ba8e58fecc6de0195621c32d64ecfd712b3bd80ab2a0af00c598a334e878e482bc4a7c58b486ed17b67f5616b10a40efd702b6281af5ee8a1f32d489e777f854c080a181986f22256c1b81859daa4fcb7dc5f9d54ea804e1189ae3e1a3daece9691f5919177c5871f1036efd93b0fb795438374527f298755e3d96ca82fad2b83caa42b55f00593af9cbcfad4856585d78fea9e762145c0a5a9714a128c42aeaa2c7a20909d2dd9def000c0586ddb08dd36f6b0a13fbd5d4c11e0224a845b9622387e35ad81fab0e092cd1d488495dc9dcf21511cb9f4dccd0679e17580834c26f73ac2f54c7c25a136fd2d496f28c3e960fae1de54625a551e6b67e3424b3d9ef2e436c5f02d36e6601d1ce7d790b862262760d7d7993d2f6616c195ac8ea52cb80d93593368f444133152d65828215801e13c134a5c3a75d23f41975fe0b5557099feee066a1760acb8de393cee09a2240ee70e5e77c92b883169bc3568f86ed40781444beecefe061f3e4a8f9b5c0ae6793848b64956b243d15ed68bee1059dd28a343d5541f38d5c58bdfae611b119ddca2bceb203ab3b8071835dc56da3f0823a8087d1536a3e00d2187488afd06efffeafbbe80383383da37352d990772b728d3a37d88a7690114b55af25c90c3f2bdd92c0611a36961e5363f484bcbbc48867234541cb6cdee0b29534e0aed20ad49d80f34bab329e88f0148da76146e8d29368a4474f44f53f74ab54a0d1b5ec9fd1ba1dee3b051fedf3885cf83722d800074ad5e2efbba76cc473b02f234a1f5a4b1c59f490a88ac6ea217a654650aa4144ea9b9256177f877e3deff060ccb99d46c24bc3f7fdea3babf6712a41e0d38e406bbf1be48582319077be5908fd00598c5057dd14722c5debb96b7d74487e286d465816a8f54ca0dbd067d784c2fc42a7977e6468c9f857e24971ef8ddc064eb1aa8b698dcb40655bf2429dd19fa7cbf0875da9afb9094872e20f58aaa8284e79b016acb98d005b7944f0cc5384b9049336a83591a0a9f4524f3aab3896db36d9883af018a03220e72a358649f217f6c355c87a4cd1274882d355ec683be6690c72be61b76bf1545d0219623b712d346fde8619ecf27057532716db253c70d2cf6fe3fd0d68ac09f756708b75fee12faeca77e91e15de2083b87f075a98847d86670d1071ed4ad1365b63b5d38253b964dfa11c22782ea8c36ac5f05a9f05e32c45a987ee5bde6335c49e5108498dbda3478faff27b5f8d9506610d4830d1305341f3018f39727b96c9d32ba1ee88f0e6a6b52cc3c9b1b001e01bc0f04bae83e925820da554d07ea83865c3f5606fa78abc0e385f86d38a78dd78ee1d6987be0593a3e5503586910ff837384128096211ec601073408a27491c7913a8857a8a7269490d29f2ac77679a7b6d8847513926477db7bcb2da54c49a60f092909cf08b7a68734294dc0982e4ae189ebcf492258b080a504212c9a90028b29376effb7931f25381943022e26dcfeb8c922573451e3d23985876c4914c0b8fe5489281cb94d4537414446016eedeeee6e3944c483eb1faf88d245114240820f43f0a0d556d8a08ac915f563b1200b1211b9f6fa3bedadbb6e283001509882fbc0c6918b4395c4137c2e2f112aba5f14baf06d5c5e624594957fbcbef28faa58f57986125596813e302ce33c372c03859c79a0cb8808e47122cd7297759e66c112d0e0c6a7e1a4453fdfe02ed07966e0e249b3f866456a3fd3864d3c28d4306e41b00b7c228eb8f0bd06370984429487714024f00b8c11eeb40c4ca2a88a8ee1255618a9b1b5be67dd68456f9e8ef9665c1df3c1d053049f25493ae6e3a00bff6326401df3f1162ff810f670e107772e51c77cd007fa300e226e04091e2598b8713fe761d2c48bfb7915171addcf9d347fcea3e2812ec8e332dc725d087b103ac27e42a0171b398f50083ae68623a31b200818076783baba81c969ad6e2614e217c8135f1128c298c427f65cf8cee32eef819ff3f8d1853cce037d7c60923b045fc660e33270480842283497d8984ca0a66a2b7a2a96ea320e2e89bafa9687a163562c1bbd8281e1b97148ba45b29192a84e9f7a01cae58d2116ceeedd0029ba6fe81868571408cbe860977ed5cf0c7435e9425f806554332c2adc762f04cdeae715dff00346a1d31fa40a246e777fffc2a6fcb8c9fdf8898e2e6e73c13604f4720618c70c7c4f9041927b2b1efef14d4758121b334348ea7657d5c0ad2ae59c1884c1fc31c7bec6a4ee104208614308e177634d18c29c0bef10840019da6666662caa5f7e8c6a34dd6151e5973e7ef7dcd3342c7d8bb33026b71a512112cf75ac018537dec86dc40c5d34f1d811326caf36abc3a89163921b8de2b3e5da3149d4f72a87bc686c542e15cfebb5f2823af4551ef62cde7c9595148f8ba6e1eb753a75afd7eb958ae1f52ad15377695e257aea4e29bbb3d32a9577d79dbadf21bf93749a94c41a2dd79f552b0cc8ef9e07fdd38d53473303c3cd82b9d941ad30d075a6c741ed28fdb438a72f7df7d32bbdf4e6530ffbd3cc227704b9498cab33a79f8f9d7e4a4c9a3089493775f234b91a67d06051a9aca46878f267d067f1b05ff1b457f1b24f9d9ea6553c6ea5a2a93b4d8ea341490d266f6787befc1decbb9f27da61df7d4e8b855adddd2ce5067cc02c3d4e9d5fb23b24dfec907ed33ea38fcd97bfe540215a4e476e0b1bb76c7dc3861e2e53e3dd9dc85db007009efc1a1ef62baf7b1a5ee95533381613e76e03511349ec2786bd0e138b4444372c18663b407400c7e606f8c5fd8499ead7c02d8ec5eeec742f7fa7f4589cd5ee903fb1d206652acc8d86a9bf3909063ea079b8d5cdfc1a7a9ba448293d95fef4392de9cdc7bcd3574fdee680e480d7f435704b367723bfd6b0e1552a959f41bb2a7ef1ff548d81f9f2799c1e7bd3aa25bd55db9b1b6b43b4f23b34dddcea66d552b24b5add25f5f4735ad58b97ba8cfc54ea5754b097bf03ab8f837accb257bb433eb53834655718903f9f87fc89bd0e28d34bec4f56076ad9aea2e7b7460f4c87c7d56303d18f6f393dab155e31340bd2ae03dd7fa6297c34e3fd450344e368e5883b2a1d998ee61176549ff3547ec5a35f324d6cd52b251aa097108d100e9db5a1594e24a1d4fe4d42a92c2d28336d90245c8f47401c88eaec2748bdea2107a47b95ad815b29bbea293fbe1294055203b7767654fef43bf4eb77f395c42fbd5df5af7c4e8bc5ced363bfe3f4d8b33cf639b57e4905a9f7c781882052ffc11d8eba30aabf6a580608c7c038fa9da7b8dec5f5db16077ef1d7a2d4d3d7677e41bde94b9ffa8f8f6a5637384a70e6ab50ec79b8932bb8a08cd64a54c26277d45fb138d4d6c751b1ab78fa94474fcf63fee97530d9eea75d451ef2abd5a1647915ed0d47d3341d73d0e171f5f824f9c1e1a4232496943835853e2e2380b7f9ffaa2a003d3d3dfc7a4fbe00bcf901f0561e005ee96b78a85f792aa6a7e9f7b8b5d9403b0363e2685083a8396019550d5886f441803db8ae3557036c65bfd9dc34ecb999f134dd148032d342249a7085c323f8c555f433d2c64653d2d86f627914a86694be658548b454905bc5f8188b8df171566c2c591bac8d8a97f250272fa324a11993c717c67b3a989bd233077b9ae53f0512e91868e4c665e6fbdbd870430e2eb3bd1f419d21a222a3239d92edbc483d6ee9803e98030d11c19e9bacb39ca6a728f86fa62a4c4eaebfc949c7c0d6ea615c55f533fcaae1cd5f79f26978f467785886f2b64779dcda60dc1dc667aedbb88b9b884c434239f277cc97cf2f4d38a79c96909c303033935a1d4a2f4b3a74dc724ff5ee5608f5342d2703a08cb43a268eebbcc46b4ace52f7e25e5372963ad3a9f4dcc714caee34df6816d5c2a8f136b760d7adda761dc55e7e4e6b7aa9971eea4f5ef726aff45e70168eb9ebac30303ff53be6a7ba9fdf76877c94c529bd6c993c6e6dd583905bdd945a25aff3b81b373c36eee251c854048560b374458cf16d5ca6c6ab62acaa67c1dc95ff66f805008f7e0d2ffb95579f8677fa19def62a4f7b168ffb158fc4e2716b25c618637ca53ac6481f7b1e59363fcbec8adf631a238c372b198559fc48ed8dcf30e63b80c7b87587cae834bdfc1410954701bc2b7667a73efd9dd3a33c6ea9d889fd0efa184e7dfa6d7764279abe5bf68dfa9cd6a6d22a2a2958c5cacf9223a4a8b008a3ff0c6111493e4362c4f82aa91e6a72633fcae2981ecb2c4ee9a70ea5a75f3d1d94ec28d9553ffdfaf47530953e7b1ed5d2cfbe647feb5a9d077776b8dfd9647c8c6646242ec648735a4897d3b1e3c146a1a8fe3a6c62a0a90efd21121d63e3320110c07f550580ff9ec67facd57f30aaff66667c2c004f7e00bcf900f056be86977a1adeca5b4595b78a33bc551480c7ad0078dc0280c7ad1a1eb7561eb76878dc9ae1714be5716b53b9b79d8181819cf75af1d3779915f3904f7f3e467fda15632fed8a9f5bf2b9253df945fdc82fd54399f94c03ca4c1b43bb401969edf56718132d1fe0bacb9c58fefbaaa298895f2c74695a8c0a9f03129fe356fd2c9e921ab8a5b23b3bf2577e673e8bc7ad8d860351e1c355db1bcc522ba5cae3ac58d4abd81d3385b2dcf294c43779ab56f556b1e4ad62b7b343fa1dedb7dfc97e077bc9797ca767ae43e32efe100aa1394d4941d836861b070988320419c4922831f025c0f08201b79fb64a7f61081883c50080a1841b7f7a0f524bc010220854e4400788c9912b86d0bd85e6babb6377f700b6d0c590d41680b080db94c6cb5e18a28447e0e8074eb45064005af0c1155b2072802c8471fbb9d301122f9280048991d9e36a1684ae9ef82ee31fe92aa142178450cef98fd939a7b4f0b5a8f1db53f10bc31f1b904145111fa8284205ec8681c160b0f830375a06070c2ff8e226b49fdc8efeea1858ff7e3040b032ee07af3fcf34ab87fac1c060944e51a7853e301d336937e523cdaa2eb30076e97ed66d1898c487a86e5b82f18521ee576160364412496cdb16050c32548a6e8c44514a8c441dc3373ae998f945373e1466969721d48926af07d39fe6349d4cef3edf6432991e7ad3e4c529474cea6e3199b439654b29a5849e3c52336e8333468d71f48d1d2d31c7d8a3e20318da2149040ada218ccb413b4021810b813bdb9b36121dd568a459374986a0d3326d266fc5c3f4a73f599c1c9fa1293a2d9375a2d62655585654585854dc003e3bec782b3ecb8a4a7c090d4526b149c7c02a3a4666ed5d7f9685fa45a24804a563eac78f45f14934ea181523cd8a2a36361122ad3c46e9980f46a2e8a46378491936b8f16351c7d0902275a0b8f1254fc71029c3c98d2f8db40c017e384887891b3f1af984421df34523377e8c404d3aa6b57063731588408c231681cb01b9b69149b35efc82455163918d44cd8a5f81fa02c206575e2e420e98dc8e3d2e93fad3bb9d298baaa13a45d3f6d5fb52a54814bf70a30b3551a430eae810cdfb41212e126144a89f1fc523ef7199d853d4ac48abcf4ccf8d918805f32cd55727b0c94ca2c2187b488861749522ce0180a40c24dcd233894a9bd564d4396d94241a6ddb6817f8249a6a02db7e15ca46a1a8fc3a6c6288a129b2d18e81cf27265498516ccaf6a237a55b46ca81b0b7801cedf759c6d4213be49e0b4d2e9eca0fe33511b9d099d9b4e3844f45eb43daba6245fd2a7f0f5a10bed79cb8eeee2b1a4ad7bf74fdcb09122f129d150fd8920f5bd2f2905ad152777177f7ee9cf606629b9ffaee49af3dd69033f24d69ad2ad5fffcac2c0d3bc3aa2c8b5db12a366551dc66a9fd62128752c4d8185154ccc7e097f9de0556c0fce66a4846aa7cecbbc00f38e95c9deb63300ebeada3a3a3dab4d67115b9cb65b29fef4c5a26a3f355f45a769749273667e6505c0abfcce8844f55349fd25a55aaa11f9a2b1e3a94a448d18187ecebe354bbe251fafad5ea50bff4dcc2c9be5a76ea2e50c98a4ee8e383c3d1110ded2aa7a28fcd27f1ebfde532954e157dacdeb922545d4ad4893cd16793a24901aa9fbbee74f9eb5dc86572725a3ee446137bc59f982385c01c97281dd6dd4a577c018b9fedc29f4ea4d02e7c29852228298a5320ed800a32e82efc864f217c61c1897f95ca7fb02ef604cce2c2aff055f05f08f7d435d96a7c8d1629638c3146b7d5bdb071cb177d7845a8b64ffb683a9793e12ceb55a60a45b5cee446eb4d3a5bc58db6e7c628ead757e097f89fbb17ac8068c42f463e1fa2b456952a7eac78f47abd5e2ff8822fe8822ee872411774f5286277c945303ea546df7f5c66f3b822548d8ea4b83e951bddf8fe93d38aef2f27621cf0a34fd6a42cb67899d425e0f257bacc2280cb2d32972570f95300bcf0a10dc4ac502155b91f4ba573f04b3f0dac001ded0281db4e603e08e48043b33a8ccaf7e3a31d6c27f8ccd31e421b97e9d606bf87cbacfae90d2eb33d8ff9dbc6aded75c09aa7ccec57b567aee6663e4df7775bce05353e6b96c453e3cff0b430eec29e036384c1c62d1b7492a740c02ff303ac80d9835f5ae0e2a422aa74899a657e39e9dcb6b109557e7cee067be6e62caa0d7fda074442df0daa4b7feae08bc4c4dc094a5f6bb22c2b79f06ef439161e54cac920ffe33e485f9abadbc6bde46ab8971ce578a39d4926b64acf7dc9cb9ea98d9c17b3e47d2419d3535aa24490abb6cacb795bc9e3db5cc9a32fb90b4c93b763fa1fbe65b24ae8d328d574a4aeeb4a1d4d07f960743968e7753fd69d0f733968c775b78c542ad9fad4f356f4e1a56ff256d4726b125149a4eac15b7ae66a4aa58e560f05f06e52d420f7ebfe07e9d2a730769de77da9cbd41e513f7a276b94ce87a46dce8dfb9c4be23c582a957e06be25bbaaefa3f4f44b96e6dccecbbc8f74b3afa5ac7bae23090115b07d469a3f67cb66a3461ffb2ca394e7d65c0b68631886d14cfbcdeb6193db6bbf6ddbf6ed91ec47e5f6f4b7a61a9661148bd09bb03451bf0a3fd3bc1eb4e748a4ed699ad348af699af699976d9bb679f3b52cba0cfadb637cf71835961f44969292b68da6495bf4b6e86d9a9669defc6cdac0eccb124bd1822d2e733494ee8cf1878dc444fdeafdea1ad8b8c571e3e7965a6be58ea91608840f60d83086214b7ed75f1f3e842b27d841edc29fa1d26866ef9300d09861d44ff2dce1c2e9c385405cf8f20b2f381ca5975fb8f065189f2ce34208a13ce25b7a9ae68e36abfbee99ab96f9a5e4759e2c22792f9947b179f9345dd80f4ea16391847d8cb1a9fc7e3cbbbbbb24dd40a5505d7e19a374f7e9ee4e630544757777a79c915c437edc87c04489ada0da60b1e49c383f437e86fcc8213f105a2831297406c30c6703397edf68567f7330516241541b2c969cb3486d794cd6506134a66acd1c32e4a9f0ba65664108210ff9c0cc4c6bdbcfbb50c6eeee66663885c329d88b6e9fc80f9f748c6674e4042edcfeded95c8687ba5db00802ad34210a61489105161c01a8d559b8fd70c80593ecb0a1c70d3818d1ddfe1c707a3219643471c412b05042143badc6c2ed873e5dc4d06be332aa1984e0840f32e002085c14f145159cd8a89380065c384254041451106935942aec70fb535d1871ddbb28c2f54ff9ab8a5c6f727d891116d76b5cff8cab71e6071ce06e4fb99aad5d59b8da6b8f7135daab5990c4040a1f23ae664da17eeda2f1b58b8b1bdcec633eff76754f1b69564fc778cfd73d2ed831bce4ce3f15d19b2ab0e01ee09e8cb4ebbada48cbb07474747878e978d465544fa6b8019750ff55ded7e81e77f94fd5aee76ef805fe947186dbe7d274a5a2d2991c4c99fda4238308b3d8273bb8fe2baa2f3401aa40e1fa2f7972c4f5255b2cb9fea9ca60a8900104514ed0220bd7fff4042cbae0a9421033f0b1c1154e53a698acd00206d7bffa0fa08f8e8e8e105240c2147a4080293ce1761554d00113a83086036e3fcc8d9922266e3f0c3459c4b0248b2b02a5273de93d8c647f3ce67da99e1d244dd1252198911098171031d45292735d0c2f8ca210054c4922a5c5dd808f14e9175086d0d38a1ec451f238425c4788bf0e42ac0e967577cf960d3b76777777777777777737eb36adf53600fec610d879b5fcc723e989cca30f5882ced06a87f3125c2db7392d39468d1d2b84220a20217480a400a28a8b80cb52fc002484abb91b3b45a090c265e4b214431cb93397a56081171772352c482adff89554c3b253e76f947a1951b3a8f651988e8199f6d96b9a8548643e2c38a87c673d72e1062317479783822862235c0e0aa227081e1e299783788ce0f71097837e20ba1b73352c41a83f2c42a81d638c248ef428277db3624b9526d2a3bc1e509f3a9d50cf9771725aa8cdbdd3a35028d4bb77b2288f19251fe5260e85c2ec9fecc7cffd68f1df2857fa4eed5742a1ac10d5c94f5e7a77f8305e8f66c1efbc1f39dc388253322854783be7d4a41b4418638c48d85882002184373fbd1ee66794e2e4b4e6cf393d6aa79cdf3f6547e8b0211236962758802a6bbdb3e3f1878ad53b4a6aff07a75c9dcbdfbd233d060e831a37d932f5f963aa107dce6b287dd4516ca02693b84c0ba9e46f9554495fb99a2ec3747035ee5a436921975b0b95a4e9353b6d1bf18bfc2f69ff43a57dd434cad9d0f7f1d977bfa5b62e7bd69c740c6c8752b26ca6591286939466c9cf843a26fb5a9d4ac86596a6593286ee7ca31ebc3d8d44fb344bd21a236d847b1a8f9ecb3f791f9cd271b4b38eb1a67cd3481907bf7a3b5649562ba590727c212703bcd0c55b77aa1a3a721e52ae063e8679def2ea6ce8181b9a158465c339aa23a7267b8c6294d42c9ac1dc904132ae660544cdbefe0fdfaa34cb32fa3f5493629452cc22a996db853e54428912b51f6462db7e68239b66c9a79e0e57e9a6597247b3e46b46a8f0834023a2ee39e32844415c17be4812be5ca68d5e0d45b6942b21932618b64246d5befbd2c31ecbb7d3384abaf03564da406b1b5dc9c4069781456017f99409350a57be9c3636cb5d9707b0f9ea071b0b14f5f44392188954ff18fd795a4895f95b300e7bac846118a9a4037b9b8e213df6ac964f615fa3e4751e0b106a3fa907a55a2d95baa7e94af24a24af6439afeb3a52b6518d66148b62e31652c774cff53beeffa32ddfacac947d66ca32ec4d3ab2b761b5dcecb357d53039516357f27a28fdc9642a3ddf52e799be542a95bef34cb6546ba97adb97ba96ccfae0a122c4abbdd3ee74b2379ae5d9534a297dadebbad76c62d8a1f6f3cdec0c4cb3b217549c8ef92a95e15643b9fef49be59bd3f4e6b1b8ce7e39b7abffd1afcd0252baf5551d435594dea0522f05f1664fe399c75c4d97a99ae5b59a6a35d92fe7522dd3bc4cfbeeb3a759f69da63957ed29ad9dc79d5722715e7ceac9d7bcf9417ad33c162683ca37b6a4a669b69d66aa4d0a952f8d0d022f666be6fbc7d0c3a35bd45293c010c227c61889a811c2d81281d8b845c6e99204a107d98396b78d3d2716484229a58e9ccbf283dad3d3d3237b7a5c060af540a1d842405000c1308ebe125af882403dafe832a316be809a151f28c31cbe80200c0baaff0785342c3e74726351361dcb5c28d6ae919984854a3976113fb9b40ab8424695cfa4f61f615d7ff0438f9411ca28a594524a29e54b29a59452fe46a4c69792155d1b7bcfc62d4d2241787d90f2030fcd9ec4d5506fa31d57b36df0352f480e38361c0e436af7ff503d89ab71da1d391bbe9a5df9fb808fbdd38729cacde21e7a37fc889a116a0fed699cfbf8244f09c4b00dab89d8933a12b7bde17ed8ff505d08316869586bcb30a83423d4ef558c83bbc4a7710bd231dc43b5d14137abe3e3a11b1f6b56fc21755a176908cfc2825abd4212508d2c4c54edfd5534b6af1db33d7d871be6718c31ca896d94522a3fa3128b33ca0805226e7958f9c57fe399a11fc29dad552d25eebf753710d85ecbc42a24e526448d4f234aaec6a393e4fba08744ac21c718b327d48e0fbdb8f1a76c58d3835fa2a5ee4340a5c66c6c68372566a00362f4199eeaac68a3592c3d60161711a1b5f4fc5b89bffad9d366d12f792dcdea2ccb5d2867b722f59bb9f4b19ee9984f0543c77c2dac948e8ef9de86d5f229558d2fb124a9b159b4b36761a26696bfa587a66dffe3e692b8efbc1eba37d5da3d9b3c9c9c56f79cb7655efdaeebbacfbc6a3baf54ea4adef61dbd9b8d6669ffcdd2345bbabd0aa27afb904c626337a2c666ad2484b03b669994592633299dbaa4ee8f49e9ee446436a9fb8b362b2641962833b3fd7ecc19896851c3b0672f3e36b99ac85e8c1212993c3dd6a88d3f2736d92516b68da30c3957868d9b44a4ee602b61fd3ee2cbd8ed4529175e788388f0dc5981e7d8ac1e6274f9ca501e3619bfad7fdbd58e8649302149ab3fa7455ffcd8634c05c7550ebb9aef91d87e30174e272aff86c17c9f35a1be2abf7bdc4fe56f2f735239462c6cd25bed9038bc9964e0a01e78563c608b37f7be580374999b81feb7b10f6a5734a85c7f95eb3954ae0fecfb6790be985de5b029e8ebc39f837ae0b92d3d6e454fe5bac7413c3071dbc341e5320f47b238e2b3c50cacde669cd31930148228ee53c6999816430c0fe37edc8407088f1f9b8bfafd6d8b035b5d93a8f3f200016ddda27e7f3b603bd02c7f52bf87413290721dca2d8e1cd998794ba2766f496c504a191fb9840c29654b099580b1bf4546b99f8d6bc338e2df687da83007d8c32ea5e46e6cdc4512a17e6c74256d97f852dec0ddddddfd7377779712368703185b894add057e6c164738893812e39c73da2b3d10fb4ae82ea594524a96ceecde438cee8246b8f041ebdc7e1374412358b5047fcc9406b79f865fb81bd0609f136b2ee8eaee8ed332477fcb91ddfdfb31310cf3c9d0c5d5c81d8f25370939960b1ac1aae0f22861ec3aebc20c4ce422865df8629b320677bb20e54a18ae94d20b4c5c295f35060f577e0df93f8613aefc16f92cf936964cb9c1954f608ea13a37be17418a0fa46c81d585235c7f1b1d63002e68e1faeb468e3ca042054367768004d5ddcd5588628816af258e70448906a351dddddddde2c20e37c618b9b0831b8f501355a96d0c2831c6183796dbdd51acb85d451839b8fd35c218e33691184e085ecb6b35108192253031c4c40964304184153f563c312f07bd400b318042a3d4291d82e6970d7aa3c2a062f24d58555c7632d32281c93de724a56ac4282464ca652570c763740cc354ef0ef3b5a654355295dbac784112555553b56e242b8e2c511ba6a6b2cb412f30ba5f7d69036ef966ea3ee109eef2a1c4d0ac48faf83948ec9d44f2e722fe54be72f9d46556debf237b2db37607c5e9a79652cb51728ad5acf8d472bbd08862c3fc994926d07cddd8fd6a0f9358fc0ec44823043c51b8089b12e8c66719e17e366e9445d13131be6b01184b40b14412a60fd9123d58c289ceb5849325ac30bdc8123758620753061501847bba1c54c49129dc551de17fa4eba369d89c73625b93342c8b51fba861d2690e143b220b29b7cddddddddd5d92f829a9a6543c4034eb558ca33b4dc31e661a764403bd98c5818bf223e3684971685651d633097420d0ad093c870966e0ebe3073f716b027e7273d430f1c4df3d13f803f132a6dc1ffce42261277e2e0735c1240808515cda311ce4840c6e3fc9074840ce91c067b63af1f9391eb08c16b43ad1ae78b4a2c5515203b77410c24f5a56879fb4381ef0063eb9515020a33d41dc58011e1f469bf18b4bbed04b41df8802786dc62cef082d10bfd1527ef195bcd149a619bab8f27ef47af4f83e61e3962fe6afd8ebf68f1647be3fcefcfe9c567c9aaeaa2f6a737787047b9a86b0b6dc7bd87d45fca2f66ff0f9f9fb255ce59893099dcb413f38ba08b81cd4029d5b05134cdc2d929c9312b300df48c22cc077c28f8c83af84dc0cf363bf27493247cb8a86befd2918c19773fb732e30391afa7dcc8f5bf4a494b6e5ba60db2209729c2f49927a2d97b90b10407e3442ef83f793c1f9b196684933a83e3c8a3ea23e89412e78127f6e6cecc4610d46421c0235220820364c8c19c601f92572c644ea07758ce6910e4f536c4a8a4de952eab88b3f4db7d71042487beac745416e840026f69783b9b031b6d6b6518ba37df6d18bbfd1e6f888da0f213757f3342e9fe34f6b6b6c88052a33294662950f5397213d0f35c9db7e73480aa26235c85841f81e5cc9580eaef46eef90c027d9c860af4db3ba052d8dbb48a9dc823f8db029654e29d3c8657a48574f0f1242551c995298e8017a35b952cbe8075d57be04c095d80e9159c438207cf93848d5ede131f1068c7b12aa9413eb3941bc58cf14f56a4ed8e40c885cede7d6da9ce4ee1886711a86056c86fb7e183ac6c784199781847213c3beedb39965daffd436fbd559b91ab845dda59f33a27ed8678f719078351ac30ab394d444b5a1a12d92a57072b1c9a794d2c9ccb363ae67a665c2d0ac9e747e46d32c8ae345853a9687dad2ac6edbb62de3948a36101c6ef63ca357efb41f0e37631f347b6cdbe006d3311f443dea63c7acfc86f2603c960b2aea3f960d0bb362519bc549828a38b449c5e18654cc7cd307514fdf0f7f5c86e5a18e4ad5cd7855a3502baff2284f05857a9a5641a1502b2baff29b8aca33578302c22b302a96c5d5cc40d920f1aa5ee32e7055cf5c05ea6b4fd333bc7855de2a87e6a3bef67c7970cf627be0c837a2aafc7f33352b968584fab1941c3a26f59f319b84aa8ae576995ffa94877a7e4affe499de44e355957d995757fe04f0594248a71529fd66750c7fb3fa5b9a156f667d501baff4b81754f7688f4185fb419eeece7121b0b3141128827a40e4ceb81cd4039f3bc4382011fc327ffe6a75a33ddf1a725a5fcb651a7fdb9e5a9c557cd6f638f41f47e59fd3fa1cd2675f03b7aabb5077994fb29faafe37235bdc22ade2dbe02e33da13d4c0ad1b8b93d37a6eade46b9f337fc55377502526406275b0df9e5b1ca0affd8eec4b2fbf445fb338b0453f16bae9ec8a87b6ca6195c477ee06b32b1edb8a076c610f5bd86fbf45b879d0c52ff34f40d4c813758eee34baf3062fbaf37b687e1375d19c3a3c5ccf9dee73e79c73cebfc1657aa875b21fe431d94fc55bfca2c6873dd0a7fe2cfab99308723dd4ac993d7dd31dbad9d9d9befb1ded4bf5735a248ff43bb66a714ad67970ba4df3364fca2a3ee9735aaeb38a768581edb5dfb1bdf6f1358ba324da1dda93ac5113b9cb7c9cc92dad87dc65421b691c9a39506e43a69f7e28b35a44552ce44454d2733f37bb72bb727f1c7eb73ddca5df5bf177432bfe8eb7dff1dbf69dc5b130ee221f476e2c3f42a5fc7282ca2fadbd73246dc543e317ff3badc1f41383ad752ef49750e6414e9352f9ae6e76fae19b2ebcabb99aa4d77eda1dd9e6efc15b73b3b3a33dc9ee64bf591a77c1324ff36056726625ed0a03da67bf43fbece567164789b43bb2df325bdd05d32c7b2bc3fc0895f9e5a3d27bf0ac0ffa34e7e65ccc3d08e3d978ccae68c03e8c522ca398fd41ba48e2bb4ecb2d77033e0f0ae916402f52aa1a3f0c816284280b3e37d8228b217a8a2c70441162c46009145d10c1822aa8f04388a8e002108a24a10c2244303f0840074d80020c1f984c49c2902953aa1af10a58537f3988081a5ced72101145dcaf3ee645aacf68ad29558dcc4b55221c052aeca08521f8d002231d2190a0e2464b8920028dcb413f42dc2fdcaf3e10a74c358572872edba3c738879873aaf888b2dd87fcc820a67ea4a4942a538a6560bc064c84524e96b3a31260be8565630b5188a8d9e520d7159054bf1ce432faa1cacb41ae2d8c08726531657435894db8ac2005b998b8a4cb41ae244760684663f3a0dd98655996edc063059122b2223940486109a39f22aa90040c3c882aa254f18508838a30c2d6c510567cc1054f784104125268459e1b838878ddf831d8c4bfb1218b7b8beae5a01740e1962e07bd40089b81165a68e14343e509978eca132a4fa868a1a28591a0223b347810540448e50995277e08d035f5c6ceee1dd63d474f72eb4511ddd373457a2e7cec87f05cae452a070d11da34127333f0eb96345795e9e12e93ee50e5c3473d7f8f137cd393381a506fb25ff728d44fbe9cf7c1cb7de7cdd0177bad48fdf8451343c774dce4ef574b5c4d476935d9af6a34da53ae46cb9a4483797db5adb90bc8487a6e867abb47799dc5bc0ffbd205ea4599ecf7e39a50dcd334c65de0c43d8d97bae737793e3afb2375b1af8ff18d5c0ef8980ca41fa9fb73ce39a7e7a39ffbed9dcb417aeeb78ec432459defa3dff45fffe97dce7932a13c78bbae7be76aba92f7b7fba71dd33d771e0b126af74e39af9fe6e8e71ee5f1e5bc550eee4d6f7aee3793574b8f7a93f7a1ac0ccc2fdd33bf94def427af76a66a573902a4e1a8f48db6543d2b15cd08000000008314002028100c0744029148281c12d561ee14000d829e4480541749d328c8619c52c8104308001010000010919981280840c9ee358049f51a9678b535a90846d0f620b1b7c2c2e1117be99c7f0d56570e2913e9cda1c866d0d5025495f99510d0af8f0d78942896c572244b096aca164a2c6780993f265ad0171c330a393afda9097fd5c20b3c21a32d04ff96d968d0f24ed8fcae811ae6fbdb43557c0f6cda5bea1336df2bfd0a029b4e94ace2f3feb735c00b3afd627c82804711b10489a3b3ac3341eb42b9fddc5487e98441dfac135669b7600e316c1f5c2eeb288c044ea0583dbac55f3f9c1b8f988eaf27e05451129e01c34e74ff45b3f7aa37051b352f5330af64ce5196e047c187bbb054d76c685169d936b3bdc57b2d017159b3724ca968cabbb36282308c013025742097e4645b647152ed800366c6e80474878d9e9a6ff6e421a8d5e9060381327d62c1a6ae74f32381ad6e7e2581868e2458c8bf8104f2add11fd77006e2ce296be7a57565174e13d4428ea3f608d09c7c508be3e53bc802327b241ef5c9746fc2d992d596d81137c1a289ab42b95383796ffb7c4d903d731a2a4cf3717166c74652f80b32afa6a31779587d0a2301183cb6f119072f30d946cee847df9523a11081162f88779f0587515fd9624722e7d0d680e5cbbe102d969736b602928c4a1f6b00b62a82d897b51ac8d85f96fe6ad7332c00523f567069d2bc0266f31e5f11274470e993c1d22d4166bdf6d51ab246ca686c9999c993a5974ef5e40d1d1efa103e2dff3ad88113ede0ebf1efc75b0952749d71d5788ac99ec8d3812a23eb90eb21001d2685009e5de325d1fba5dcf7b88a4eccb339089e85a11a2e8b2484d2a7797cd78f569c7bac40cbb11baaefc80b14e03cff629f2b18996d8999823a78b2b504977cb3983f8b80090ae2a6fc66c4692c08e42f73064a96e317b8b83a390801df20781fd592a30f0982123c91b7d74676f42d8425f82ba59c40ab72c9fb034f6ac6fe4bffcdae0c7dbfda99ffb123a4edbed9a1efdfbb8c290e930e2299c3a8906fbcab928b3a8ef83f772c1d4651387ea14f6dca4df7fce5ec05cd6bf4bb6b475f058982868462f6ef5412a5a454d6108b5e581cf13753f282718105f25f03167259fecdf1fbd922e4beb595d4787fec69e48511271d182e6558fcab5d9cf66bd6322a8b22bfd7fd8323f2a7cedf9a53830cbb0832e0ea2971707e2a3246badc7d0fff733fbaeeb4bd6d934a3c67cc21d3e4f851a2f5813c2c889cd0311b45ff476af0ebbf0b9ad7bf9728b33428013619dca48c44a44386ea78a7cccaa5a10282ea642dc77ebdb349a635097d1ea1e8f581046268b5ab0bc18239db8061cb38aaf71a11e5b3b069b6a72d27aad41de3f237480e665d94f1ec190ae35fcdd85481e5c33085ac48e409fca001f18b5cef95a03157d0bae5b948b1be365951596d25acb9064c2e193a209908d2a3184cff614597d69dc3be9e1f748d603697deff1eae977fceedb5a0bfbe2da0a5ccc8ef346bc2e4cf19f2265eba511dc87bde5deca277b77d6910bade03b8e92b3a1ab67a6169076e9374d70c79d2de4631f7f8696bb171e3daec48782f355f669386b4ca667455f9169d02e6cc7bff292894dffd163ea2210b52bd04722a66a67128045158ec994ffa6ff9da0f280423ad3d7017f730c51ea169f4e365fa1b7fe8b74fdc7a0960429cf9f9a0bc9168106789d6668804b85b4ce2a8a900c7e9e112351aecd1f2e3bce05011aad5d437838d14b68847e2adce4f82ee7d4ac0b5a09bee704dd41e214504ba5bb51d864347c795d4b10862f122454018bda7858e21dc0c013cd39dbf5b71419d16f52f3adcbbbc2aa5dcf614e73336c7230769133e5dcc0fc4fb35544aec0b84c0a0a7eb5af1eddb93426d0fc52c64268e8119ee4e65e446bdf6766e7b12985d96f627ffcfc3305435287d39ef10e4cc088a9e22462162ffe6ce93dd1c0442e5bea8f340cc791cacc6504fd900dcba499301ebd2c6674969a41305c89b51ce484d9e0722352b951ab0e741a30cb43a30de1a0f9aede8978b5fada682f2e540fa88ddb7c68d8bcec378387ea9f9a01594f39fbfa3feb060a382dea9da6eadcaf7c39fd055d22a5cefa28a6d61b310d5996d6487ad630f5646b80a430b6e6a636e89c87c07de33086fa3b1f25c3d55a357c9f0df02eb02b1545b1823f65565f302dc75e0421d58f226feb880175fd095ddb3356070116585cce8f4a9a7f7900fbb8e2fe1c78d5d43a733b8c8ae9e5096b647d123d83f72d2e01a422d627a6253fd4179b3ff512b387177e4dadcaee3d0624769d944028c06045e3ef5c50afed0eb1b9112f99a4101176fa3da1301bba9ae25a8da017094e951854c7b551bad143d857d80a96c8a1714c3372f3995683ceabb67656f08134843570409b541cdad36fbf13193932f9622b87d64b1772f87b5f9eda80f829710cb9e72d72c5b6938b3a11831f90e255797586d9ad0ae58feedafd934d6db3a483b7e52e007ada8faa84f1b03054568d4bffbba06071d151dcb664b5dc4ab69254b871d6c14011aa6a6e9aec585d31608857ee2bffd72331bd60a80d62bf20c239a04f8a1504ce62ae582aa804c63702d3130b0f0e1ab528c01f90613148bfe15786f127f24df2feb6aa16519dcfc67b447290eeb3071177570b0b147949326807892d0d51cf861b43686e60753b18c2df9460852bd3500fe0e126cdec0c6996fa24acfb25bf8b8133076bb668c1b0c19d74ee341955f83fd33f90e0e5b5dcfab214b325179ea17a2799d8e637adf3b123a01f0fc1ef9261640079c5def93f7fc65e8635b7ddbbdc073edaaadeb53e3fa2638ed23b4da1823474c10410e78271a0e0b895f91b64add6de23583e12c5e3d8f3d569057898086a6cd9636270e552d028618c54832d8f1320ade8f34a9d0bbef7457c784332a63e0ff832a65e4a4fa4be885f585d2b4cb35c54a216da31ba877c66e2fde3d4636ba4290addd9feeca0edabdb36da4b7932f9049b8ad32c0e61fe37322fe3335236fae6db062bc4272f48d563a4f604423cbaf7f8d87dc14cec2ec8466704bd778c77f0ab63f8aeab37cba936f24cebe547e6e422aa637baf1d3d684300e278ff8d292ac4a841f65c67c21b00146a56b5576545750fbd9652a56d9dd0b782be085af258b263fad8d512a71d342e94ed758962e7446b8a8e07d30fb8122bc34a01a4b7dddc29612241dbbca7731e9f1ceba55c2b64581fb78788406d400288a73816090ac56fce449cf4039a760e2ed9d444d4d275769b46c72d60d866533e892483a1bab3eca4eef95e3d1084b6cb4ec9cbedbd63e0a92cd729c1d46bcfdb673454c9927211221dfce8093e4beca29c41c01c199adec5da53775f4b83bd4c04484eca0fdd6e2d3000aa26df6cc551f73e28d01caf6ed004ec22544607e996471417168192cd67a69fb0614e4da81f19c857eb0c5847fb5f14ba5bf97f2feab89197ec127c49a35ea0a423eaa45003a4697b71df6980dfb68636b3f573d02f309fca814d6f8ed805619051f948a137838a3c4c345f31900494156f6f06221acdc7df6d728a6a78829329ccc98ff4b2c972b619ad5392b2f17ae29a1492581d77e376241352d9ab468d1dcd5684128496e44a006d2096d9c62e665c29c4ca3912f575de27bf7f8d89337028685244f329a79b98538918b58c2dff303a43838e8114843b1ab69338d26f8d7cf4bf400c2989ad6b10083cb66638b6cc66234e5053a27451def916b86249a4d9cf6682a1575f78f5ec71342bc6fb49ece56290f16453980c4f2de0137e44b12cc04ab972f03983bde150b6c02f8a81dcf73077e5040a069aec26cdb91c7665cd8bfea5ef209e66f19b314fb830cfca75887e3e248a2677eaaa536018e3228fa36c9b48da1d57363e820baae34c0bc0538a4627cbd192df06a10838e5cafdee0b2b4c37f12ade13d28a0ec3ad586da6188ff4ad71eb86a26e108993ac681bbee68484cad80a6c750986869e551913b3030666f4aaaf5a8f4db2bd7fc08bfb6602465b41c7a9b05b4586b501b94b2367d503f31e5f58406407370361c3aee0a7de1ece75a5e6edc105535bfc696526566bb23d354c9682ce93fd40674148afcf039e7581b1694a09122fd4d6c5498af250411f5421c9f91c46e67f7c60056521a35a914137ca7b01b1db596c932da332986a72529c176fccbc14a2390f8a28f05ee5e33b448b76028648bdc1e42560678429daaaf4835d904822a438f9446ce1fc2f0cebb2cb3595046368209ed1da2e8df5c1ee608add1e9ce8f07e269c8f5d5660049dc01d37fadcb54c7867ac578013b8d76a246f95ea8a02634242dc5322d1a3353c71bbac0841f0d05a6eedb626d75a1037f8255d17174d591b00eafa2b3ab65bb2efbc15645e40bec7edc69bfe144f9874fbec48b1b862be00db7a5b1e8e9eb14bb2882e5edebd72e7d483c5e2a41ea124947fdfb15af3355f3d2cf08bf58315babf9f8e8e4de72718b72df8440b973dfb4acf21ca1aa9eca8f75120f97d431fbdcdc98341d790ecd7d15ad1fc552221c1977861ce918b72422f7bb51719538c701464794d317ccad2bae5d1aab0746724da81afbd6a8ae741e37ae1a07c715f5a7b250efb6534088a25d07b1af62936fb4247160bce26cf0220bf35f28a345422a5ea706d16a4dc4d106072ce2d48131821cb36e08dd4eec41a25d679c8cc0f90a46aad8d105e3d6dfed513484ad3ec974dc143cb6c3a475608bca480a793676d0472ac4d92390344d7a0a2418d1cbff3434a00dc12e1c54192ae1c0d674b7a8f0c30c0eea2b6fd47b452983d038f217874a0b207b0490283a73030f046ce043b91a2d8a1b088d3fb7f8f208a9de83c6b752e737683c3b870e3259fd306bf6241b2844f543987654f507d3c68178283364579390b26298640d68253c98c0f83224d7d80bd53168f1d5120c09ffeed527a4d869b5fc6b60cbb19d9d792de75dc10d58aa07a127d1228ba0f1977623838d1f661d97ec146e1408be3657d5f7e5f238191ec7d3d4c7cc6298e1c20b9d549c20513a2197b30b8fbefdeec10e274ac9f226347bad245a5331ef027977a6e64ab38e1e0f607fd92e748468b872b8d1f38f3eec0c1f8d9b2a00fac092f413fae199a737f90f882fa935bfd7d7f16e245321ce0d98d43757580c5cd3d6b076f067c711c904500399d01108d945e2f5bc6572b201143af4267efc994d87dd5b04147ba240cc6e7ac4e62a6aa68605fc55f03cf49ed3869369717e09d6f990f127e130ead9baef47411db8e33d0bfc0d47fabc803b0b1871e780cb29349171503631184b3647e06172c95279543b97508794c5b8a3193f36e9ed16e40f72bfad7aa04c91dfe294057eb3cf26e606906fa5565b05c57f834af8f7e353c3e2a7bb1fbff65cf5153e150a0aed221fa393700e3a9dbeded3d982275430edb94b2c595570c14b20de50246f3a9175e09f0b57ce4bbdf9738e5fdf57e2428d7f7c339331dd0303b47f57e98999b2c3c4d5339595436a93bd40372e059d31c0df7162eedaf7148ea95ed236456fc05ac6b54cd81dad0e56847ea46c7f79f817832320f8da62853e708be71d0148d9a7f8609479862fd2c383d3d36a904062eb4bbbcb4e4e2d4b7c750bafb44ad225ebd2e3d08a175881cfa7f0e3ab37ffa96c24e7fdb21e7dae3c20db2e19cf1d0df43a5bf8a0b9364a2e9063b06399751473371bfa0c14fd81600f094d199c6d55f0f6db4ebcca2456d6c467f1867f9479bc817cc2939d03dee4370a36f61b8be4a6d2a2ffeb6507590e8ee373e4c8407ea4c74537177a9febe6d6d07c5cc650037d2511eaed3c80f033d477da102141d4bb62d4458545e499cda99807d1237a2f7d4605eaafe7224f9d5f45d29d0f01886c0e1ebbaa0e91540bf0eb9e1d2e9196f4d44aa43b052f276c0b5cbc1b75e5ee12e2810013391ceab69f88ecce601a33f105b07b2c598108c5cabd57d80fd3e5b1ef483b2a13ced51acc5ea111b88d348d470441f7412e60f40809b26d49fd345ff9a78265e6896608756c8df63f84d317120d1bb21f7ace1c596bb4e0094bc5ab0d2ed591131a677dba06c26cc7effe58c9f416bb57b7b9b07633cfc2e54c31f35c8421f419cf7ce34bfc748da956b5d8d43544a6dc69239ec6d535c02dfea042027231f8b00404d86ed0d2eb3fc398bfe24824569d8b8463a1e961a71141a0b2f52b9558a1f36d57e5a11703ed28f26e6eaee19889e707f75eb496dbbf086ab430934c431b4e6827be7119f74a98c632231f1389961ce2f7a079a384492c33d20189f6e3169839136681f54cca39629cc4d4953153445651c2cc491d60d4405ebd008838513ba128631b633b587df2f00ed393d586dee3af454422615f04ba148689302fa7d6b8b56037e1f07b18d88a38948599c09f02228afecb6132a66f7e67fd2108ad7af5780fec4bc1373ca2e348cde4cb0e0fe0f70cf8541a86fcca40a076e6ac437cefa50c0ca24c6d55af86e7156ec84cc8d70c303993ae1a8ef74b2426098dd5a1b40541a111bec62bc8693e2cc71a1c84d35be65e308bf779222da5e26a18532013830553d52ae06a2dee95b411c7402b0a4a5ac545dbfbb64df13baf43333077b120745c8b1b11b995e78af659034984482b016871f8c407596305d0176716bdb6fda1ed07810131ffbc61a33507926ac1b031fc3d79c53ad30ba1f313e16cc32d9778832fdae130b576efa24564fa581d262420b6e5cdf63856b933b578e69ec4510a0a58d569b1262a79b317cc0bd75f0be2b7553e5d4a0596e9ed79826aff75e4c7cda63b81c0d63d5e123d645a5fc01e14e73ef0b4298ec77621c9c398476da537a0d8699083a55d288b0ec1c2af5b0d86c9e00e4ef1f62f714ca2b67848d7116f905bf54435a37cfa394ac06b32252209b59a78433ba067b96505048fe76042ccb3ea6ca055054569402e44795e9356206aad67010f0d178331b7c8913d44addf67acce77ef2dc0b7ee2eb7f0ddfbb35b9a3189bb542980bec08a202301e7cfcf67d3c02d82c94b4f50bff0836c017eaef3ba574e1bd1932f32916625cb9bc00f6b4ff53379bfb317f3b0930765d62052eef9fcccbf68e0dd6cd5b5563be5d04b55c19bda9d4e419d1d6060da218083a2d409ce53abdbc7d93d39a086bd3cda6b57f49a4eaa5ef961f3854be4e46e0b60b08a3f17b7d970e413e5f3b684a7002994fc09a1bf7da8625b078cd3172f7c2f94f9f48cbdaee65be7cf708d1e7a695da0a6a301ac6ecbf75f8dc65cd57aecff494e5583fbff1de544d4e47ad2536ecb5a4f8a4a53df1ccc00673f549582a5dcd272f8ab77b523f87960b3542d1350a44da39638e0876085030b88a982b7b4eb13d81a0b884120e0bdc413386ca039eb2a63999b672245d92e126aaea1640a26a1d66b861865ec79c0aa88e940f4ba3895ec981abb5b1fbd52484e29126c62beb38f696ee9609fb29e31bb764d25d4422a9f02a9dbe0a3929ee29adccdc3da607b0ab49e1098d5017ceebfc73a960e5276449def5ed3f781a6e621add30537d7dd66dbff331d15f16eddb16900562fde5557a0b153d8426134d1c1a67e4ef578d4641f9f05d6dee2fee1bc3ad2c4e678cc7f3852a2b44494609ef2c9ee370e5475d4811160b20cb605535d852e413ac5e23c007da406b1c95e07bdbb04715e2708318bca5ead0ea646b19b099c248e65563dc1957d0dacbaf2bf00383cb1bea498e4d3987c49015f5b9a8892d5d427d2489b232fa49f0722591cfae7622700b407ac4dfa6500b452144829cd17fa22c8def53450af8e026cc03ffd946c33bdff3ae3b6801e71116bf845d7344c4f5280083747a168ba8cac840da1d5e90e59609123f612b526445f8a90d5d1a5871ba58b844a8320f22991412478895d4448de308c29799900b4e0a8fdfa7ccfbd471dd2c3afdcab3beae63fae2d0b3689aab633c5b905a8632daced779fe3480a6bc8a44d5657ab03d6a9c715a477ec7217a7602395e80173a0ca74684138ebb860700d22ad06cf74a21f88238af9e894948ba947509b6116624742f6a542316980fb91f9fe67e2764013fa2087d3d715ceaaf15d6c17180cdcfbee904c5a35e3e5355730d844975b3a80cc24f26cb16f8b0f1d60c1d4dd76e23f9aa64fbfa1b1a36711ad3e3344987704e670b9940588429d539c6ef279de36e6604d024c28f868918390f273ff0312a3a2f1081831082d164b2c9cc8dcd9836652309587113e288ac55a22d5e3814a827c21c44403f449ce5a5722a339fd9ee3c498d2f7c2e415bf7cb06f898f9a09431087fe21f901a180c3d81d3950cfccb32219a002c791c7060aa2d297452693aede78fdbd350c83be891c9bfcf269d8d5e610fdd1ddef2e6c0ae06f6d9099a7218b7c48eb33c0b65e7e1dc47fbb209c75739e40e3408675921a76d80c22f73cab1f0600230c29439199370669688f280b8490152ddb688a8703739a16b7d1edc3a1150e47008526171d7e51f571a3c24988a2f2c3ad736919611c9125025540d96fb099dfc35f1eb1e1c720ecd84f169a9a4cdb7b22f6980fd7f75a24596a6cd750317356b0d6726cfe479d7e6a7531b89f4c861883b1ec3e351dfb2e5dfbc6164bfcb8885c6a267779099e1ef53b3bb7bbc21b335657a4ea0802c0c6576081f1df7214b3451514a646c9695ab8d8ce1b7592dc0a2923cfbbc8c3af1f0a43e995748e6321775d4e37df381532441e6073ecfdda8ca5956dcd78340f4a231dbbdf371589f44a096a5819db3bf1283f1bd302259855c2560d3d800ca1976d81f31754bd1e951a2138937943f1f13ad9e6ed531fbbd333c94872997b1c74a9c4cf84022d6ad9e61c417795566d03fd270e15fc6d9a88c5112b9ea256bbf871e0217d0384e2e0f8231e90a8c743586c734ca10119d06f4a4c13f73e7f30c62a0f8bcfcecbd4f87401749e7375dac8df017869b8f1206359670d9c5593da60d180b8e8c8822dcd35162555e31effc5f758dd2417dc5390a7c036c76b563dd1c50dd2cf18c3a40a79ef7ddb3cc2ed71751bc8ac35e9e8c8e9b3c0ed6550e4a0b83a5527995ddfb24df8bd9e0a71e0308dcf1a594e0eb5e15d34d9de6fe95be402a5e55a0d69caee259ba9b8f4f367ee7658f13c854dc7395a47e42e4ad31b9e98500de6e73498b1791a10bab1b68c1bac38d7f7971422e4eeaff3603a841314d1125382f5a8aaccf8e5e60e911681c37d24ea149dc09a95d9ec66fcc2f26b26c0ad8b98d53ace4b145635dc2efb5ec2ff6959a92579cd14c3bd39e758d867ffdc1ef05dea447379543e4020d2c91d746652669d9b55b34440ff579444f440a5b395fb68965648f280dfb47b5408a1296becc83fa0979a339513a2a953954670419b594464e558f227aac214e337129685bebd7988ab36528a05785f079d5a8aaec9605e1e44e8515e45ebae0319f6562118042bdd1be05cb8206494f82ac83b0d78a68b1991f063a61880448bcdb3bbc3149c16a7f9ecf7191365a7cb3035093126653974db5e3fcf99bacb8781581bcc1560cf8a34a20e07489185308ff75e3235e4f6b9d5b23c8b92d09bf0013fe0d828c47a925228f9849a16885f0819bf8890b5b116f574d5e7fab2a1cb91c05bfad96d38bb73e0b885c495c06f0cce882c11957416a4694659d1c0b019fde342bf0efce1689768b14a01ddd51b4538f3113882381810840a5b48ddbdb646580d4a9d40955b920d0196150a2e25fd9cd8627b6d39525cb31367717bcfddb2ab6014ecb986ad47656796ec9fe769373d0f28e38068de5092320aae02357ade792ee685c4392e76f8bdbf72d39f4bf0225781cdf717912233a7fbd677f5def55cb104c3bcb40b7a1bd153dc9cbb54ddd50cdff486d7d2aa590e125a6cb84e5748acd114ddadd51883ebb1f1af5eb7b699b3601aa9175096fe1e6480a62b7d6ec4b6576023083708b23075d54a765d66606f21c4ece0be10c265332560531abae544a276aca2cc4290258de087422da0770c7fc4a158e05cc415fc2467248853dca3dd6df3769e8697fe5f4911eb59a2ec85576ac2ce223a9f7d107c52e92733928472e53eced99cb338ad444dcad050ead3b68934550cc57cc4ad5fab746596ecd54631c14817f99ca0de72a23b77e1cec988ab89a1fde2d80c1e0d12249d9b6991b260be447d743b2164008cfdca5898bb6258053b12838a1561d03754bdd4a549d700368bfd7d594e7687c5842a6a958c7e13ea55832fee22073ff782621d24170cf052da208d04256982d26145cbf2d29ab27628c6145b636e73e6cc0b923ec4b087b5cb0774640b93704dcf3d0249aa888c1d246c64bf8a10acc4770a895de345e1bf0e5b405599748edf63e3d5b8d3e190883813ea258262d1e02c9a00f443e92a119c5f0d7f83ea356fec3067aff0a67b4cbe9a27c9ea8143c33302bd819634a1aface816a787a8a4e5869f5a02545faa91b9de45854abe9e3745547a1029c6ee7aff2a73bb02f4e8aa074948a90045fab16eeb20fd3ff280284377d600d99a22b1628713a51a3399113f087f2d5ab314493d2ab5c3ebb4fbee083cf702c75838a6755a378a517c8fa2a15cdd9a36110105f98bdf408466de56394b17f06173e467e976a6f8ba63620581bde5febafec209a4aef4ef0fe94909a190b3872bc96edfed783bbc21e497d47b5cee6307b2a234dec4aa99b0adc7eb1e200c1494b67a6f6f93a38e0e867b35963ddc66cd927932e58eb92530a6361a8e491123a62935aece4153254dea75f3b546a8ba8b611adb0946485b3b0ef132188746fb5b8b2042f0cc8bb6ddda9d9b523ce729712d19cacb83e3e739582de5ff2cfd0bc42c1ef8035221c65efdf482fb47edb406cd168263ad848611fc5aaa26b359f1b3037fd7db2af5ecc614e84b0323d2b2ca234ecfbc86584e32085fd7eceb9ebba4199ec86e4293105161e023136778a282ee1ab8c7fffc988e11f6f1b9a996a4ea0d6698b13bc80a549cc34b89a36138a4f1ff27059f239d06140d06d7f549fe192335009d06665beb5c3b91d5a0be2fa910b72f8a2faded57354b35d5d47f398d2169c1f80abd427405ee4d58d850ed452c207ac3066fbe7a1bc621ec0f7992deec717dd397cc091b168069ab117ed3b80efd65c8f69f9cbb9468f96641e52872c5a79d53aea18210c70a288881f0ac7ce68899745aa44a6db9b42a47099b69f0cf16ac1bfa9e8561c63cb432f4a3330e392d48bf9922bbdd10bf3434f313159a7cdea4a2a67cfd24904184c5d1b8246903d35996dd5e301a2a8b88da5870f82cbe2403d4a491b9e20a0b9101266904059ca09d58b0117ca51a2fe022c008ac8ac751d038c25d80320a449e4b2da0a2879bb06d940053cabfa626c1b44bc91564250c970120256f4226beb4ee40d702dfc7bfabeb00a7e76587a065a06fb14e3482c81e838b2f987f9e7bb3ae95741e1e33ea6b04ae66f814cec63c12e64252ca338fe4fa9a4ecfb59402b8700dd9f4a3aa9cab95d240d350a1c840040986fbb499a7bacfd5e29777b8c489a4748881472405b1a7d087468e153f4452aa6c5455becee82bd20c66319eced23844d20ecdc5a4449710f2e22a7ed99d105fa0c2711dd6e2b9ab753071c42883fc5eccffb25e33264ba5c8321d34ba21e3421cef23deb58f92f01484d4116aec72c00d801cc2494176c27db2ec452c4b04c3916d7aa13ca49c308af3e10b9948914f7c2a7a3093bd01a182c65c388b44e56d4829e3ebeb16415142fb7e9488b73a71836af2b7ed7e688e8da96c1b8402629f495488c6f98849efbb13a220aafb405779e2e9ffa6ae4234700d1be275a9e766106876a30ba8632d434d8ea2c91f8404822c871a75d9287fa5296f2cc0f74a08ad258be410ab1bf721c6b2a1e066bcf5edc93d9b157624581c0f48cda390d006fc39e014fcb32ae63bc39190b0f331222c4d4dfcda05cfb4024d71f0a0aa8bc129921ac59f5a538117cad0e04b989df09af9f134a1309e53a5b99d0a0d7b938e1907a302a04ea09a057adac6f0d3bec07873213fc9b1d81977257a47b7b18022b125b4dae8125ea9aed6c2a6ed1cf0b300e17474430fd38db15e9f3c02c8c17d8bf77f0fb85b26a2be919d4bcee53f69e39a2801e2df9331a5fb7acfa5bedfdca294657a2877d07fe5bb0910f13305c3956c1847b82dacdfd8905fd111a2cb04c4771883b20bc2d435649c6a33530abdee46fa00316efea876ab07454eea8b904fbf9e6e44bee7982f6a0489a59ae50d966850d532f59575a98d30e3f0e1547db8d0ee6cf4fb7188be6a4ac1dfbb47ca2c08474d0cc2a3d7c68c1d8eb2e279408c550b7997405dfc4dd630f99e63302ed046bbae3bc007b2eafff529762bec25b080b5a41d4a02d770fcb2d83179387f3ab4adc0958320d36322c97055a661f1b8a0d70a5d2af19f91aa1c5c56774c729f64afccfd8f2ae2e6e054d9594ad492086e83f8632782ee23c32598ff0754ab938e273789ba3270027ad704d9d7a8f929c6da55e335afa122bb98cc632aca1f05a70648319a43667f7fa47c35d15bdb804cc38c4aab2e7fadf9048bd7edb2e458e525cbac0746cc6588ef5ce0236b02d8e665a36f5f916010b01364121c5d89f2ddc1a1b2f7388abce9ef5fd609a47dc1084ad6ae36a6264e493ac2b36fb80745515eda44a9733ef259d091de92ea476360d82e878486f203b807f6239fd61a5e7ef6177fcb45af59de62619aead386061c74388224e902737674b8d0e2711c21ebb8c22521b76d02ce284020292be5b674ad5522d2794b8b9934ad6525bbf2ea625bc9c921fec729bab4f06ae14af49bc2ab0990f3641007e34b66f36fba5cc5df5500b8160aaebb8b374c616206626f406f69fe9004088010b28c1e94e4b9b79e42f3ef115d7813fbbf8a10fb57cb4e9188a1d01e27417be221b46141a22c6c8546051aa1070e35f64302729567f32d68928105205f7fca18288fd01ec0eee45e8bd6e98720e30ddfe55df836b80bdecce070c727699f6c991550058b3d79af48b0443bd7292c74a225f996e8ddfad33e886406bf88cdf06cf156b9d814040f550b6e3e176b19367500f66de6fc99f7e7398633d503303edc48c8eb1bc8e562d00fb0bcb62817fedc449f9b750c5a37613581d4a902bc0e6521779c2c4bca5ef864b06a6d67527255655458c6d76db62ebc4e157d4d90a73a2bd71ee5286effd16a56b22e2c0963493d3b0444acbd1935861a98716f095bb501f44b91121f61a2283047cc3bbbcfba8c3386246f34ab4993989511320f7174ac1ae90e1acdcc0c18c0c22ba33399d9ef73ed3a40f65b8d8b13b18a2f33b3a06ce2d089cb0e88d30865d4091f3ee7c63989830e6d63653e333033b3f1490c5099a9fea7d631e2e9dd2035ab6979cb02ab3ac3d24e4f8df7a9d453136d99628b64d8ec2374b82f15da1cec8ca041afc96a354a32b4cd3a792244872578c4e93ef55726ac39ece474071107f2537fc6acaeb34782e00238bc3b95e05724af9559677a1bcce59b051b57c8bcefa40a689546ee2cbfff1872207b042346df7d7a852d115c3c866fe84f3690a749d029905e89eccb69093eb116560d6bd476111a33d3bb1f92287640dc5dbc3c8f0d2269cb7a0d6572c307960a231a8c7a00c899525de9b9df9df501c9285d040c18c5a0f3521ab3c32bfeedf1b7c98b0e24d44bd63ee54d6293d009b2a6b3f6a44a6401418f56b0765cfcd6a7b92ad5dcf277fb614c8482775177418a03432394593ff887dd43595fb0b41d1a914aab438f1fd229ed1165ade7664566eee5ad65558368a8b0fa591a5c07d167bea66337d329151a61b4f5802c2a2443676172fa45e81a95735ad80169870f97cb1fef04e93f79b943a4bcc2254dbe2f1f2a2d1ba415885c0a0083b1e7deff04b43f62be8f3a45e95a74727aca3782479ffc5fec1d2e040ed6d4a7ecd01edcff08c53f7dae21e2fddd53506cbb5a0ec901628400fa78d1ef367d56bce937c124a06690b75b59a04b81bc0d35704b6c67dc55f2734f3489afcc7f4805d8157e186efc48a87cfd39590d2f5e8690bd369a9ada766964730102478ca3e8d25f08ce3b40420176852d625819d6e1c431c48f88fa26941509c99f7aa4e0f40fdd8ad935fe799cb5385430f1d1488f1df64ac8cec7d96661fda3b8b0256ba9bd6078b6eed95d03cbe1f47e247e15290067632cddbf76f0f30783ee8b1b7cb89a2f0198b45d4dadc43f6a555b82554b78b321d73592b51d28257835d84b520cabfdb39cb563ac0332c21bc4e1ee8d9b7d6a0a1eb81cc88e7cf2f90fa3f0934258bcfb9edff87ac1b87379898409124b30336ccb818000215746aa442cd0382eb747adb78aa53eaf765197e2347b39750b4d0397f78eeec56a7ea069fd61a826c003b78e5265808a0efdecd0c9abadd636a1971106795e0d12006c62f26caf42c0fd0bac10d57bc89571498b44250ebecd17a32c520dbd9d5f34de40d1d528de42e21393e8216a0e872b3df116e293172bcb44ec9d0891ed06db4326f3e1b38475438655394e1bffd50a8e3854de57fd96760f6898cc3596047f594af542bfb9c58dfbe68e614345a0424db6698062252bf99eb1aa7ff03f69949823bdf5952cbded388d119bc2d62be59295926b68d8d90302261f1e5ed6608de496258915257bc8ccb826fc4829397b11096baf11fc30613c11e997e48f16ef233dedc498786aff0e40f4913e7be306d2bd9049a6e2b554659b4acf1a89b30afd5f49fb20ec792fe129c98833d48e99ace3c8b5d294b4518bd1ed9efc964d23a7fffc16e5b0ec739d56aad06c4d10026478cfe13d7e2a0d9a84a567780a16361a609d38dbd34849d2636244244d5c0e26d8708601a5c4d0a4860a2e18e3266c73d20a0146e1976ca6f32e0dcbc74601f12e2f12da1d0dc2efff00ae57326d43428d1a79381a8977469f7f44c968e87b717a23ffb14481a2e486994bc90da72da004385986aa204921628065ceaf155f30e0bb58522741588fd83abdabe717914d92385c40aa826718c61b29a715c658606b1b9fb24a9f39459fe7c7438d0b1bc98be21376269165d2a5ee1ded5149cef2262eb8ba409b4a2010c4fba406cec45361880fa7f3a1cf9689058f21c41202c3de94872357e76a36704a349267109ff5c0c891732faf903187d741632303dae4e310f4e03a1b684c80627e7385600ef141a0fbe430f3f744adf23fa083e14d9040177deb48ffcf9afc70f42f62eff06c2fc635d5c6d5d9d33012e898ba3749568733512384374c72cfa05477b1d98e6d2f4a6a9548731d9ef871ece8d894e40dccbe86cfa014f7131dd29882a421747296f3ebca50b77a353ab6151d8cb44ad846b9770d83faea21ac52de36d8464f7728e7a564b4a34d56b748c246624b31dac69fada89d08222c0408b140ecd2409598480784306e85da1fbffcddccf969360f439780703821ec1e4245e296243f78ec1dfef6c1a05af6e1c517e7a2c60bd3d44aaff0a76a850cdd3f785a462c3c054efd10cfdbf0c6a88db3ce5f9846adf2b00966bd40d9901c709a33ed466ba0cfbf456cae93ee1ff5e0b78e58f814a93f669ae026e49b56ca87aea7e79138dd59c96f9da4ca1ba05b7d5c21041589477d60bf8285f06046fabb579ee442df9ce3730d61db9c89beffae128e20f488710695f020908ced91cdb6073c8011664b65551856d33c45f2c77d1c8ead8c03130322905bcd21e8fddf70a9c21a1607c90ae06247f6c95fdeb8d357c72c87f3f4ac99187c6b351ef05f2d51733c57b0d706221f0265bbcffe3b7e203889ed1a0668702bf4f5d789c149b5f8ddcc73630355b24419e89e87a9a3956af19d4863d1472f0b22ab31f849471928201ceef665d35e35ce92342c7af37e9418d32c11f1fe557ed3efccbc6581cb3786ff5319e8f99cc88bab885a9a1449866454f89bd04941284d24b34cb821bce5df41ff3fea23260ffd263735ce8f46902b8cd17c7f93221f419fc5c8b009fd5ea2fdc730d40602b8fd411e53959e035d61923c7b284128cccc5e878a89a38a75be212fe593051a165c03135b8eab4e2452d49967e62b6e2baedaedbf0768dc183502f917c30b1131edc93ebc2173a5f58f127ffdcfea0f244c5f996a97c530cb645a2f6872e18ff77cb3bf6180601f4b8f4de201538c3cc789cff602388ceca2c283c1933f05d4897d183c812a2f6ae01746227e58cc88a1c36ebb683f8e6f17b2d2849f5e72a0429b9fa3e593dc6f92443bd08480fe750d063e31c330037a73a9bfe9d9acbfe9eeae9beea300e916617db7c299e90df3a2bc2c1b59c6e891693b7105e787666fc8ab11f22fb20a407a698f1ed8ea4550410ec23a0478f84f5360aa51879f51c5115194a23b1901166c1f716800890dc06fc4383fb5a0d6e244dea71d345818f7fb83e6f0f717dbaaf9fb5f258b9f8f90bf29955bc271900f146f06e34200eb4c19a031b1a66554f690cbae756708d974f1a047b90c14eb2b07da030580d1053f78a96b023c7f6fa6cdaa429630324c2689873d10209352aa95a239fac4dbb9df3f28953d1f19b85120492119d27f3fed23191e2a1f9be329125f6fa84693c161016bb779b3a013210328079406a76768b3c595c94f2bc82e9faef9263eb7d327d08cd5b4e7922cd5325119dfe8030b6e58b5ef4d53a2e574d36ee52ca2c44431a043c827347eed101d0cc65ff43ca196cf7c56788e4a74a3acc6c31f44a118f62ee6b047cd5f4782b5ac3cf67d1f2585448ea6bcf0c36f42a51d44d7d519ee8dd6b9fc0a1195ffd80374cbc6350c838ca710874eaab09801185e9a663681034d3ff7b8327a97b4122b797956699032ad950b73af04048de1081d73602fd8bfc1a9c330ad65010885eacc0ca62498de1af3c6e08291c762ebcc4c4b978925983a338ffdcc26e65970f29ad91be90a681a72502785a9d2b3a530797cbacb621c28045741b05c45875aca6816e6b1bc21cbe225564d259b7067dd2f1f13c964a2649a4dcc3163f957071a1a312620ca55a687c51320cda2cc8c1e8ec48d342e211e8334c61eebcb737ab7c9c7a60c53c93a9890d50e3b943852663eb833d9eb2b306716406198eb871f705d0542e824f4a52b5eefc16f16d05a31518f28cce04e8af3230148f1be27f5ec7b3e4a6f14a2f19afc27fadc877d823bc54b7b64d13e977077fc67a92a37bb4578c847ab63c48fa37eff739a50deabdd9551ff021d4cc87cfd13f57f10bfb0db167df8542ea660dfe8dcdf5deb66bf2eb2fba46ecf44a9ddb18a6f8ff7855f4a7ef0ce4f1404c9c1b720f4d9d91851c529ac8b87d1c5caa25445eea92ecc8922e3a07d27747fb2e9015cbbd761843c9a64d820456c3db00ee2b610d027849649989216e1d39320369ef62ddf15c8c0855de43f6158c5f9998c5f6f346c010ae307d9bd2ed987e957827bddaef49291c9f7936016ac3f575b5a1a97d1bf43bd44ee20a7ee900c67bfb7b733dc57da77d1d4b52ad630d3878a5ef554628ed4553c916a2c29f5ceac0c2d2e0e0d27a1de85e48cad912a3869ca148273cb844dc391087f5756cbe7214f8d89a31e216bcbdc2935a6c207e997d4abba9b2a737088ed589c575f2d0ee276152da2869e802a2481e03b4d712168780c1b81ebe45dab8cf0c4163521af11e5f39a76e44635653bb8d1ab4b041d441dc058eb6ff50006465cdca112ddd4029529cafb332d6f580d468a64b797348027e3f81b3a19492491d03620e1bb79d7216f49ee6e348fc4470d9526c266c348260f2b98045ef14abe6f923d3aba0e323a1c855847de0fb5db13a96f9a58cfa83735b94fe262cb3c7ac8cf4e92f96f0e8ba643811d2eb09c009083c15340c6375fe92459b950f0a4a72129919a0ed8e00a06f01f301022c88ee0329ce1d54c1024a5db96b5798baa7283a379cf688e3cd6fad74f6780765fbb729415afd8d99671a99887fbcf4ff9cb5b122fec09eea36beaa9ca834572a99b78d97832d546d108d6d617ec55b483ce8e00749dba1801aca493f43b142f1207d42e440c718f9892323840ec0da87437f19873e662d01207845e02f00063d8a30940718bd3549f72e80ab36d45445508e3fcdec0ac65de0d1443f313d1615af8557f0ab2177ad4ae3eef55677df648d7d5f647ab0086fc605490527d430b4c3c2f7876dbeab7bdfba3c6d10fd870e39b4b00b2deb574a571f95b0cc35a902d4a32517cc6846c2008c2f3278e3d2432ede019510261903adb9f7c1eb84584f120b34337f00114ea41ca16c9d9315412c2b919aaa5c263782c2d1c91c05e7be1667fca46e96d87f423bfbef7e8a9be57b97bcd91a59f10ab3ffb6c24dd8e745789079818261cfb039dcb5e12444873fad64469331c91f8ffa316bdfbeaa482d992cd4e6842c75ba9cc2a565d90e568f27d14b2eacf4dc4640ba8286fae4c7c59d6320156f3e1917ce3e68ec81da90ab45c572f21c3265da1ee15468ceec8811cf58fb42e4fa27ffcdb1fc3509a71546c90683a5dc6d89cfd98713ab4a4db6c36f59e041477cd8f1404d702e65e733b712f82ec620e1f59c6c2b28d57bb19ac70841b0158e8de6bca43ce258efbe674d006c6e701da964f5321846134a1dfc91d5b2be502faf2e04af482585ab2db1aed52c1eaaebba35ac4f590e50fcd4e8c06b6f0df93f580c4860e719b665db1c7cf776d1d4caf210c15845f4e953b36b469bb2e46b5717c85e59407b3c0d7a4778009fe1be3e1d5129b9221befdc9de7a2c467b28cb0f9a3efcf0c8917fa1989a2c8711450447f3c097750f995b0b49c1992f39da45b694c1136be167c7229e6a248209d9104b5ab81c8a9edd74833ed8d949efa929d3900338c05e6e87cdcfba61dd9e102a7a13330a4083c50ba3369b7a138c935e120a21ca54636131c1951b9c791364a8001c6af0bd58d81b8552c7717228b8128a9f8a14f70dee015b21869057cb83b9d4f3cb2dadde0c3238e7324a59c8ddc15c7bede2012fd398025bf5b0ee74de4db6a40a8887547f1cb40efd8f84c906ba0769fa3da1e2c8800ee5d257d78fad92231fce17374e1c5ec5b6b59abaa5d6d8da98be7fa86d30263674cfed8ea851244521cd5e272c79ef3ea1495f96cae847cb72ead2072480b9acb122489ee5bb1ba046bdcca40e1a9c8682a660860f51889fe989d598884b96d8dddb0e09afd2868c04255df44666cc6712a657c4496ed4b8e320ccbc480385d9be0306965a60941882025b8705a7e79281411382304470d5fb79894573c03a36407856e38d7e7117b98c4f7303e0d9288c860ae57806c1bcbdfb3951b0ad9cdf1797473cb9fab7d5e3c7aa61d4b97b929fc33b8161e9329a365752ad5721ce9081cdaca5939a5623ed7690c95ad016196aaa3d0188c3fb9d63d051300d7c0a94d9052e15d05cb9ea2c5d3bc3fc914b13ca13d8a3014582713eeb62f8a7d24ec3e364d15c4f514ca8bd9b65f67c09de4bfca22c35b824ef17c1fd5b9401e72960ac012c9d12da1a2d4c218c23bff2a7df995c6f7694d28ef8581b14da3079aa4baa3fa6c6b8ee8007b75472292b642f887397ad1936a05841325ab78cd4575dc8d4d3e8d343eb4cd62a22769a59ca073d6f5f1eeede4b52f773dc4724b7c90201e866cb4429ca0bda5aecc948146add35776c7bb0c5c32ce79d61824127b7599c08bcc1d791cb353e891e7cab867d803250a22b516bcf6709c8320b74063413e5acb04c37e5c41ca091f0dc08108a96656a93e2369a3a697c98570a4e80c89ebc14833b8b560c1e442eae3a8f0d22f0bebbfe71db30a6ad06ebede55f6d59aee8e63dfb3e4762c923138aa8f2acb7fa8c54048eaa523d1564e0573d9e300b6ebeec8b160d7148aa3aa6f8df5f074bff3401a9dd2a5eabc5c1eacfa029017520a59bdeaf4e23f9c4dbe20ec5648b71240dfb21d561b990465657eeb480c326abe922fb442fc0c61fd97c90dfd434d39f9988e2ff08ee4f783eed1660ea57bafe8ef3310a5bb2d1e9a54844ada7482c561ee7324c453519b6118f73ab38f77021bc2fb378a067236d937d910382c1efbd3888984b936c83b3a77800e9246f5e3bb4431d7de5413649ed31d8cb115b0cc1c5a4e72e269f76c0b1ef414a3888ba3858b786490c7b3a04881f48d0f3c80cdd722b4193745dbd35a70d66de1ff3405afac4daab6e0cf4153ee87b98ec15b6e221d10a2bb75ff906620976acbe22746230fac17391f3dba0bceedf2edbdc95b21624cbe84e77fc4fd31101d3f7320fa71b17109806feb30528bf27bef586b26eb6520788ce159deb4fcbed87d3541e40824e2ff69d928e4e8dcb149cd53a65e26e6e9ac6896bcf92ee34ba9b0fe30b4335843da7f4e0c6bd30470dcf2bec7aa33d2c39485f63b037bc6064b460745630832918502758dc4bb0c6082608c112adac0d6eaa85313f1d44a4ca7e414282fc210138ce038f1737f8a2fd15313c163e4361b5af1d4b10854b41fec157ffb3d42e0a470ca1ebab6b92fe2d2d9800a2989b2f9a8fd91552c85a887c35350fd887440b6f85c17595b99f38b86cf6f71dbef70bd03975cc2c9936a08f52991ee50dc169e713fd6461cc10b59e3fd78e2386f3f7f703bc88e1c2dcbfe7fd9f8069f51e860b76f7c418a92ba0ef98014059f9d31180bee8cf67003ad19f01f9bcff1a7f92511be1d7518316ffed7894ad3196ce66c8091b8b65b039d03f61b3b6c8479e849ae0e54fea6bffb03d813e71b77b3e46eb3e696664b906e272354ccc332ebe84d3f0752bfda95677ae4d21026777351e48ede8021b53203a68a36a5d76514aa4661ceb1c0f3e69afa04398e66f50ac81ff71227c178efc9afe3bd0e7e9ca9d64f4098007c67928caa42511935ff1796beac01715102ad83429cef018eefd3e6df56be06d68334d1621c3f3d7927b832b1f06a29c93351c272d26f6528c2ad8930dacfd285bf29c75b11d8c8c133a1d25fb612d422ffb9cac759c1cbb3863ccdb138d10ef00843edbe5174e9d90ca85eebe3bf031c9d32240adc984ce1dea0a903f206c50b376c49fe261dbad85c4c12027a39d6fbcaba71acce42ec2753d191965557c0f0d332f16044ceee7b4f96b561171a81aa6a602a20db87432226dc6688a10d7c52606da7c8b821b12ab4aaf4df76660c3179f10915abb0700ff389e615cce931b705d874186aa94c5ee63f375677512e8dc71aeabe85e3f0a457d0faf966eec2662d2c34601008771972a56adb2464e36f126e86fca3055c866f67dc93aae748ee9e7cfe19047b182dff8fcfcc62fe58b7c9c42871ed1d2f8c4b4f6d9d7d72e735fa86d385e85dccc35607d21b5a6a1c4d8f0206e3afc0f97f948b708517c409c0312341d3387811d8ea987e9b910732cbf62a56aff385eb9683d169f1deb4a91dcd64e196e4520d491b322b294afc0c730a22c82a2fda7a7123250a47ce997e71237b24c688695080427266ef0f79587742ff369f6f490197034397f459a3972347df2c29249869b89b6bef34464bfb2db626872a347393c38ed709d227314b75af58d4b2414c8f79ee03f23299afe72dbfc2b9404f94810c63a34283320d9b80e62de067b26d102b6287f1d10613e63d7e4f5605babe684ba9a2421884484c02e8bce9e9b0ec019d7d705491becee5816b9880a4dab60b5507a488a384243b01630c71a2eef02eafc547c7116e2038e051c3f6c575615f6219c3e5c93df6bf00c362b58f0b5576883d8784187a49fa2c8a7089cd9021398e4d985448778a9dc24014104c264e53189c8c1a4d92b37bb70e83e7594bc8ec1315d37c1ba7e5e79c9578619babe359557deb177a3b9bf493dd0c295fc4dd4e2912ef356e050733cbd4a4a02765253955612881d23ce7cb5acf0379fa9759a7ea03323b3e6ac65f1748518665649f6a6962deb6884e6598cc215c55980f0b4f929e100e829420612cbbc76a111be46afc6db2845015399e5f5cd29b969ea32af673d78c742fe6f68cd31f3f51c24cdf70a5a052b9c4ea07cdc64a4cc7e2c8fb54bbd74806f36e42cd34dada530c4e62857a80b7f0bcc59e68f022d07d7070d2e3dabd529af062dff7f529c39ff6e96e73cfd3cebf0452091f7bb8f26d12d40fdf66b4e5a5f6ea3b18ecda936d8254eb9834a5a6c21fde56574952575863730acfb0ed8a0084f2778836136b4d63bbd0937f00f5c8d425c6cf08dcef2b2e03f538e06343e5675f41ead2cdbbe6e2361e6865ee439beaa7906606bb68bcd18d7d6baffd65be91b613da1bfd002f72e94300866c1c639199beb9ec684a6d4f4bd75550ef5ffa4653d9838cf61244a157b26daefe2eda5f10492d09191c91a2e6aabe635a9d6952502c480f8c38edf59045a87f82017bc4e8827a89165a6812d8408b1eca3cb4291aab774347d1d99a1e490e5577f1f40d1c01580ba98c2acc02fb5a6871946c0aefae4ad06dfc2a9f0ad06cb57d823e07df3dfdd4b0f7f4c2e0d34ce3bf9afbd4789e60077e93502aa7d1ce32c3a3adae6314da0959ca3235e3c114f8fcb0d29563cbfe663300f12dbc404a69ea78a8470e8a5ddd8607d1cd31a62747168309655d2bf33a8677dfd4ca4f156f5123705e5dc1ffa0f2c419d3f9497ca064ed6c19279282390e49f99d47daa936324938fc1d550e4bf2f2e17217b684b076907b656809c7783066b29a662318856ca31afc8ca1a5c0c0e22d7990c195f331e6fe8bf81d8254dacc8da87014832c15bb27846b95ff472ab20cd706f73911a678c10500b1d18649409552a3a96c82d216de12a3e067c4eac382a9dc79413409f4385f48b414e15041b1042e7c18c3fccb71eb00689b261fed29f8235ca20f44204f60bd0eaa2b5798eaf2e41878fb17751b05e47a289fe5601aa2d89ee921d9d145057318024010b8b3bd68390696a09616d4168e9855755d2943662651415eb3673faa456ecec86d8a76f6f369f52d712104e1a2e2059f0c8f8a783fb99a7b6276c1c499fd33a29f15c10ea5d74bde1aecd2f9d6fd100f08c008401af70bd528dcdb692b3ba778ebe644da08cf2d2e1aab49b181c2c41ec071b1202f3c57cde66fb6217e8d171861993a7054de04b6bcc0abb7ed3c5e334990deb1fa7bc6896f590f7c30b480e08184b3db69e54ce93c1eb3b6f8c5f66416a3ae5a476fb3e3edbade30e6f32ddf169c0879b198ff6c637c741fe7ad63ffc513076f9be1c137d0ba0e5cb89a29b86665cd6b1606cfd1af384a0bae6f12722c0749204cd7ddd920778b00e94ca51747ae668ecd574cff8ede590c5c7ae28e45a6d6320a7310f137b1904decfe979dd3848e30e269a9e02a7f70dc2cbb73c0bf7f6810e19e721a8cd2b422b1dca31fac2ac7af0d23953ed6c4dbed7addc795300ae8b9acf7ec54bf08c44bdfc16ed86844648470ae53d03c0eaf25f4c6c229b7a0d5c642d4defecb6194fd5cec76986d854fc15246b082d6554ae5f5ca0fe41c478b1e537298b283291ebe70b815bcdae461e59bfdd2fafdac5499767eb2457921035f307840d4dde4f3d9d371ca08ed4c8534949a206be191429e94fe5775dc67989efab1c942b535f01e6324c620f15162dfe92b9a2d90f7e2e921bc6e879d5accc4f10c6ab0b9171e3530ca28c106921795e45836d946a04799e4e4eb1ccd06dba15c634fab6330a0796dffe2b1b2d6f4d4424a2779391f309cb9f8e28d5f8b5b417f3f5acd27c9166852b0aa7a33f6ab09674e28bdb0da68e99a5cedc35413c9afb99c092c71c3aebd89c0822f13862eb41ba5c41648fbc5cf425d136aadd2cc2b7998c51806ca0e047bb829ea8f1309302d2483b818ac8e1005826e25086a307de48193a2c18c76fac9cb5cd6f733778bae39a53af02297c97dd8e7f82523e3457dc07e052a3570f60892e8602f496711062b9408c4b56eff2f547e50e89640808b4e8924fc95d3c8a072ba52a4ba06c28af937070b7e4f8b2e9476c3620b956cdc643bbf2a9b6004e5dd0da86c9d49c686ba09eb437ca6adbd6794f0444f41c5b174f59d2ccd5ea9c2302dfdcc040de94cb89a782a61f27b7a87506239fcbe9bb35051950c5e51de4456bc965790abc9bc437a726d2576037079e5dcd50a0cb6dd857f70ff37ef3610954a7420f32a9cb866a97981e32857d6c68786e023409a93f0d56a87fd3e64fdfb9b65d9186c852eac0ca366c45a35e0a230eceeae08c6abfbb6dcf038206453270941d9b5ce69a42bc2176db180f33c76ce2bacb26f39a0815fcd5c7c128466dd6ecbba91de0d7c78c1dad7623d9bcdcf8185a1859607546e966406bcc7525d470f04188213fac349ce4f0a444b9c33e9b7929a37b410c3246c46223298150a72427331012d79a91cc5348d8c1b55b933fea0d53d3569aa7614a09e95d6b1b5cd438b216221194483cf39a406064e33236cbb449a3730d260a4d8917310ed808bda503f1606a43101c08d0b25066069b0fd9ad27987b233d048ca65e8adb6a58aa47ac24ad5c1e14531da097122bacda64993f90cac4ea5ffdc2678ba18c19b4569cd8c627be58cb58428206750792645e19e8761cc7991cebc4078c0be0087c569eb71174c4ffc17106be584379b5b9f08d582fa503a51407025ae727d0a5f7ae45a3c7ea948e81521d08f08fbfa876cd1ee71274b40f1056d87a82db244224994d04716db5dc28e03984d0a31ebc481100c07256a207e89ccf3f3a3cbb68c304a6855a36840e64394882305afde52335e91ca7c7e49be89ee9a998d133eb9f6379a04d52f3d15ba299932160785037c65217a2d61d36547d27b08d0aa75af906c4ce906b5f98967d92d68909b00731d13e0e058b49a9382bf2ce996c8aa6e9b580038bb358dc3f1e23455617e69ecd4541d623e8f57b912f271f8e62f432734fb23b3bf41223a5e9118fc9851ef6b9ed3b298fd919df41033656b7149e7aec2e4a3035b03c01f156192e48d98b3a131907ef7bd20796a7842274b24701dcf8a31b47bfd19905dd60e8b8a802dc6e699124016505f0ff810250a69bd82b7c0399c05889bd899adea56a8aad648ff54b0bce68e149181078344869128facb2ce5236e578622bce4f4e1b8aea059ee9a6f37274cc532db6d75ec78dc4384e54005ad8fe4ad9878db35986d256eff15b0711596512641af9965edfbbf51270e9878dca6f6cfd82f3f0354a0d0b3a2ef4c705cb2eb9c0c16a268d94452a8494553dba882ccd8842a5d77a43222dd863c6298634358d81a0099ec81cc0b14de31bf03d019859afafdbec17327f2b1012135c9250541559164ba655e777821c36cc8858ed0132ab27b3d5fb0af462181c42ed6e66a35e39b1ae490f49175aff22654715f8e21e328ee6febd49f3fd8073ef651af347dc4c2b9e77076a1cf9ec70c7301f1e037acf28036b5e512d9e4255bafc72e46b5486006b085bd1aa718326de8c36929690d96b59720a5590f53d8b1bbe0e9ed5949e40bf4af02049d54f438041f9df73255c34ee4020f142eba887c2701202d57d69d9421b911ccac3b04a61c69e3be00228fa25d3a070707c883f399f2749dbeb69d78183c60c63624628cdb57f7dcd50743efc81c892244b5de01fa376a22be365ece7961a2c26c161a0fc354c2e0e13db212bc0ed59649318e428416051a9bb0a36e2ee0d0ba46b6202d95b0a239735e0cf89e90c9fd0f99483de7a309b5b42317c69354c59d8c187c487fa28657dcec32cb59cd3bd29d65f6d7e87ae6f02636accbf468dc7ca9410d68044625366259e03b32c25bef36cf465a77108cdd1ebf045a7b096c3a568f0b7f94b0d2903f6c301e35d988832a39d85817865808242768915d34bee874fc365ee0c75f3449ee9dac9a7c5122f11aee7eb927af892f6682c45907b034d06d381ba4ea10ec140927925594b0060f95fa568cd0e03be3a6dcac152c9496327c99d625cea3c1fd19f3b56b0f6f5f8d53f66e650079e21af4720e703c02f07a89f9f39dbab9a2e4040e108a747f69e03b8db71441f5c63b3e2f441be04254a9af631145457b245a72e4df6f810ec8cb3eba7b041a562024c82fbb98c084d80dbacdd279c00172e4c95b0d2b11f8401b7fbdc6bc216ae86595c0c089d29d0f3b300314b1bb106520cda88aba71331a1b0357c739b4b54cde737297d08b4e40213b8c119942362beb33932849f8d8f2ce3832bb87dde07c11006b108054eaa68dc0c8052a274c821502ce25730700d44d2c99341efdac687f869a4a8015a5e5bc0c14d1093b445d8d75b50529dc18169e1681e2c935766df8986a59570019070dce3a3a85d58641acc1debd6bddb4a30f08e49d8a7587ec7b5bc9a85c04714631029fcb33a7616dcc854101cc0fc8b3b1706badf1b2a3c20dadd80717dbc5e24d25015d2b3f67b5d1e8f08cb795cfd012684997f36051ea57fd69aaf8543a2c445da46b2fd2dcc7875f26ba83cdffa5ebcafa414e1c39fffc9f326914ad23f0e23d31f49efe520c78437d67e930cc9682dc18bedbd11a27e9323608711daac633b0210bde4bc92d3fa1ddc040934a33d549628d7b219f05ac2b6c7131786cff32018dac973debfa222470ff67d21eac55394c9b4bcefcd676fe8016d1d589cc3ce8228ae7534a78d7b5d836604fe01dcdd659a51d69587569979709b1e1fe98eee9742f05f59ebcda65532777e822ec0e494b5e434c3150d4949ed6a6db8d870270d8f30211f92dc9e04aa3f2f1d0cbc6e218931efe4f80ebfcb379ff21f38991227af402ea76c01227184374759f32319260e1a7c5a886642064987cabfe3821732cf30535536b9889aee79a7ccc2e7b5cfb311524ed03793e2a883e1e02a30f59b6ce1aedbabd0851fdf27c200dcfaafdf4aa845ee11c74f488ea09856b337e1a50590a2c77652b31ca6d4ebbee154510a98b40e1538685234397490e8738f7c417e4b14a84523ff58474fc84020b9b1d3b99a8620acc4874314e880614791874c2c19b7bb68210ac6e4ea185c9f2a3843ffff52d1ff2d61310ca60f2eab81ee2f77cb23a611ff9ef42fde2e009e5bff451b67bd3f0781abf031b436d4aca8b3889b5ef3e2a09ba4f9bc8e6988543de8a8b7d29702f2625bc1647ecd9cdc471d5986b087acf857d0f0d45f0fac605f40b26d59047a66d70800eb81ba57b98c6762c35d31815a672c50521bd963cade9642e67bcd424aeb335672053402af0e31b2bb0d70bd4e03e517febf8b9d95ff31a3f4c3f00abbaf264b05d2da2c169ee797583361935b7fc599ceca893567ff49b956f40128eb32563f1843bdc58abacd6c43d7c6af624e86a59b9a6479851a39e40ac7ac67f86d68861497da8b448d2decd8a40987a73c31496d43d239fbc29926c66933ffd9f1e700eefafca276a7095f8652074cbe110409cd7a7d798f99f4de1153dadcaa22487520bfafe7f886b63fbccdf10ef7c94bfbbce638be4182b0854041b2d43d37be91d3d32ba4ea10c98e372263e42621eceecd029c3f44ad64afce8738c53566d024a609e1bc3a32b5ac16e54ce432ae8e8285466bae12b1902164b485a1a48ff30f6c7810af0cd57bf0cde8697f2dae7a691114d5e87e0b48ac5d288174f89924778cdbfb5eee9d30fe5bad769d4487256d9e0cbaa2b32c4a7493a06b5538964334875773b00b777e4460f86e7401df5b60899b3725121756c6972380eacfe181aff3cc0016c205028e78cbd704da324d19b03d9b47779a180546a26250973d16d1c3cc4a41d1b91f76b2163eaecdab59a0dbe2adeb0bb545f59c8964f0a6d31f275ea35969e0da21b85cf12cebb35d14ec557ab1e29c53ea5847283f64b9c2fc9f251bd1ae83fd6498f42e91a0e351238fea45ecc84a71d77420de007d0e7525756f24e1490486a3fea79094c55a8c1ce902010a2d3206ce2e8271ab13afd9843ca7f8b7cc563187c2a0c317cd821f4996c4f962f1bf4332e856081cfb976ed30354e15c2a782c6e4c852c22092fe6c08cadfe1f81d0288ab368e73c73a42be532de4d195df562175429131c8c881b2e89e4c5d6473d0e4ba0b61f4648097429c73a6ed58e6166b6b491d87c77ea0f4e4ff80f485fe32557e67483f3cc700a20eb90e6c10c5f17a9f3ce14446bb1114e155934b011ce2872ebea1d426d6f14a8a891423e3856b75b60da57c989adc91f2de5f3ff9111fb8aeec11cfc1e2a5c3fd5dc212a8e50808dd69fa15ad885afab7f0546944e85d81f3d128fc1dff7b4694717349f1d0951e92d8998f0f9f6fa6f0d603f9eefac26012688471eb836ac54e726d7bcf097449070bc2848ec111bca49acb339a6c244fa29aedd036d04d7d5eee252274a18a3ac6525732cdc52fca668e830bfa06002fd0f3d40481c976cd5edfda24115b065912583d67d48505c38f14c03d610c535a84da944670a326553e982f7459070011a1bbb5e4a1659254a88c8ec7d2997ecd6de8c4dd99c62fc2043af5a8ee16dcb71a1d0251da6c5a03edab99df584929e3455c38720cfa2fec0d029acd382d704d34b994b171e04f27964fadac349041d7c2cd6ef3505bb4e8b8a3aa443ca47fa9b3e438cd8565c10d23c01700961f0acd8eea64216531116bd921a2ec599d2f3615a8aed6d5dc7c6c7887c4c27abe3094525b9e0ae349f162ab37a7ef780516e0d82c00034a2a482eca75a2724c77c462b6ee25a0be4f868a76d5d7d1cd20e4dc67cee9c50237cf8553b666f0a7ae429c9c66b30efc730cc30245bfed3b09d466a9a76212ca458041142554466e943e3214f7e9ea8f0c003b36b92a63414dc7a74721d4d777e004e33f17791f12d8eeee6f202ccf49fb37ef76134be4ef68191007ce3ab2e3258e14e01f54dd8c027a8ece2e1c98e24aa1f54eb7c9e11130c97729e9e83d091f932724dda16ee23e37ee2668580aef0501b6acb65f3a3f12b0d66a712ac200ece052a28d3c85103c0189d82ef1494a3873de0dd51aae6dd8a3ff641b1fbcc9c0cb949bba18fea3a790581d4d2903bae2156772f21a899c2cb32665966e394a152cc986298a881071d21c75a88a9ffb681c76e7f243b216a035f4c99b98b6a1331b979d6c2f222df405f3af73f5d4d9da2d023cef6ff69c7d4d6e65df8fce805194b72aeb1d815c791ddc6234b3051db291c6452e20d771486196ee135410d7b0104620d0fb07846cecb0073c4fce0e32ba7c3656fb4a568e52302e62b3c945e34852fade76c592496bcc09925b4db628fe095fc41f6d5b76d3f1fb5720805af41ecf54bffc73b7e6750d6464337c5ea0b9969d44186b426f5ef5b58127ac3f751c762034743c482d6f0ede5baa3af2b00d589fd252795726ddfd0630ed70c20d08417d788ab24be2d7f56b46b0f5b621a6acd5d8331da4598305803ec0793dd7c736d1731f2b3f6f8351b4d6d234f473a0abf5fa30bbfd25b8c754e45ae752add0ec51ece5e1cfd38fbf529b67770dfe13dc01abb4dd840822158541b172d7d2e9673513218fe102c2020dc2de49c0f35862d29ca91a84584db59d79012eee1e4860d7c8f7b3b1d5bae6f059383fec0391fc29b072f770c067d0dac5966b6fe7e539c9b85afc02fba7757667b519ae0deda6acd0155c82b41bd7d9f78e5e2bd2c91c084b0bbb4d82a0dac9ead9c448205630250e1bba3a1d860a162ab87094d2e645b9234e186e1f67c02aa31d92be3404126ea0b011c692962c46beddf67a246679148a31433c36716b8a61b0052333110dac5013cf61ce64a9e19e51d9fde8676b67564b73bf835a645a820a93246b8aefbe70400f550f037613a2bcd26c0520382a3bc63d1cca8f37105287e98dd47db71629562ad16e494659a724805e094ae1bb4ac583e76f6d1f92377e0c5f2a7247f327121287057909021483ddd504c0c081f22c1a76f4475c245d395c21d95e12f4d69b7ca4edf0f4bc23c45cce36dba5c0968b9e0d370c78e4adeb565c5ec33d4806bda3763d1333daa4868ffcb0109b9103408c611610ad0ba26b7dea54e2dd4a094fff7c3905050c950aeff4b1c0da84090bc2a2af7f29bb20c92a61c2369b4bb49a28a998d2eee05dc35708b4b30f6a8f7f92581b26986c6ac41312d35ff62c44e36d34cb95e77c4c7cdb0b93ca2e31b68d9e42896867159af3c2ec754d4cb3a8d6e64c79cfe5a24ce8d30898352817709e130eef06cdba2fdb70422806bfa5265e526456e59d75e7cc0f52f62c6bc32d1397a180829cb3861e4f555f2d6a45ea15c378a464845a2bd3e9c0518394e76e6a43d5cd370a473fa83354addaa4bb027c990770eededc2951befaa3d9d83c2f34f3c20a5eef49faa90e805f6425b106901eabd12ddfe09e1f7782fb0f34f795eeea5a66b1d64a96718e9c718a7c7ff47c07e9ef8cefa99cfa0f6016b0c0c42b7e8288cf15a69abab0797fad20e74743fad6f895dbb7d3d0e519ab53649bce22dec3a28a3bad069ad51fd0d96b09dcd78a03316c518607cf36a56d0a7983fb4431a12fb6e826ad904a20ee8bb9d891d8a00b4e57a79538f0524243aca8a80c00b252460d248572c240e0debf83dbec860dbddfcbb1788dc0777110c3d03d3036f083979d90f758c646c1e5e9318fabf08a59118e4ed36f20e54d8a15a2de40fed0066eb5ae8466c087bc1791bd9bdaef680c222a27836f3931a724e9c42d4710297573fe3b4c35eb2321a6c63b173f50260bcc2555a1f7bf88f6e7570a446600892bfe898cae286e3a02f0688fca2426a344135649340e4179cc6debd28489129da451f953b93b5d3d03f0660b9de164cac401d232675936694a76d207fa020509372d06253224e080e7778dff71cad656e549279c0e17caf915f448b20c0c0ffceb4e3fa3e7d88d810408311a3f92a3e2c4a46e064a0d3a1f0e277d17e46a653217cbb97bdcace1be8ccc4ce64eaaa14cd3d22cc0e38933bf8c850af95d9f3029272b210d70ca536345a750e9d4965793507f026a2a8f1b74ca27f6782194b99bde9a70709c773839518fea94de3fe83f5f8ac7c2b898de7be6b7e145df0a8a15684aa93dedaab16a0f70a8537e3d4c497fad0c338642e48bb8306bed1626a5c866bcf71ece2dcf8c78caad87167a72f89ba8110dc787121986233171cf6a5c3e236932ef3a41976ef3582edf57568e4bdecd711d70090a2fdfeabedccf3ad4f45a92e94219508a9f539543050219201c414df19a53c2df1697020193984401a908470f1743adee8dd61475593c35b09ff1f4734ff230d86856b2d18a324eca318f129217591caec96c99ba62e44838bb8157441998b2fcfc26cf816077efce68200f48dc7cd9f2d99d13732a3d2cd9089d703e3c5a8bdf9b118b32cd72bc94f2e331a95e4cb4652f4f94567ecc9496ae1445d2e1b609878f8bda35cd4b502f5f008c09520f11469b372e0f5029af8f9a9b2a895f8315a98407afc605f392fba4932b08a64a3159525e98f4a9e8d4ceed6a4508f7a8ba9168eeb961c39455ec02701a1b1058619195a9adf37419d3622140324098ef5c345e886f149b704bce0c035a314c050c2b29c08d09f1fdface718c7f90fd0fc3babfb4b8cf1e08aa909f9c7b8d03c356627f92da4cbef6a226142616f643428704ced6ccf9c5e3c773b86bb3e47a7bf7d91420dc9ee30ec9e825b465edce39f945b4621a1444c54cc82e19759e9a47e69785c9c1ecee9a8469854720a941e1f1b923f4b8daeab88142c6487e0bb0325ad713ca6bf851dbf72f553345ec15ac4a59a363581e0e4e372091d1a9e0762117801c24822df476587d9e56f363d18a8a688b6fc11fe45bb5f236905683498aab95b51735184fe2df922d01c0fb9f6760b0c0c93c51210348176f12bbe5f767e52f0b4812b5e56e9271ece9c6d093c7d3883373396805a53421a5797fab0d61246c4b41bc15c0d2bdb19fa96af992dbc29fe02ed241962217eb807a14ae97f1542bfb8ee50edd9c6d3dede13f626c44d65c9192ec1d655b27afb2a1a8d07bd458894d7f690d756c82cb3b69abfc50af7828a864049b20fb0799f43f4b13e6599f469a8ab002df3551854d312d095efb9620348b0072077e6dbe62bdd2c37037a7d77085bb1f7c22c7adf4bea0913bd17c9fc24cf98413bd31f5520029f9edccbc08c57fde2f983dedaefde182d4b0840cbca1b0c7cfcfbdb2ab0e6260bd77891a7e42577b68ddfbb0b0920c1330a780985423fb50255896b386bf42c352a70243fa07e8bd19badcea30ec058147f1f75e885eaa3fb0210a3f61bea36d58013631efc42571abc8851ab0e6d45f90b4de101b8ba2510268997a9f943852c4b0709fb3f249fe1ec43c35d86e2dd456f7d2abf8f8d0c263cedf8098cc5f12dc8312394ff0f375d30a9d85ad64d1263abb16b94e64b7e7e02dbbcc63c4aef380a75b047db5c27266f61a8cde8b5656af24114ba6e80651a5b98bb5e03cf5512219ed0d7b5b2451b0b6abadf8cba193e48dd3d50b2145971aff602a762de567e2f19656892a1859a8c7bb1c980e2f7088172f928074c28820b87c6a368e7dc766c5c97634f323867d4f471456ef873218321a1649f114327fd73a5044698d33930bc2268c2fd81423b0a7749937fa0fc4665ccf0a429107849969531592cf322d6fe35fe3a78120d630d674925680ee0fdd3942282a3093745a2efd79486e01b0390fbdcd28b2be55ba5fb901ba43a04d0d085a067e47723d6deada179a977ab81bf6eb0c1e6d7a7252af6f7e693f621920ec71a20202ad01425f647ce2e912151814064e31304a7098cdea4a68cab85ee0fe84fd5cb1571883379018a814be8283749b40b82913dc3694715a15b86fd147a10f26c418983ae684a15e150b13fa250584de46889574c5c192b371765c7a30abd72b51ede260e89fad338803892522cf79f5354283abcc4eb0460710260ff0e86e0b6ef3b1b9cfc3bac4d48f26d29ce34bff7c6c0edd6ff4f9d9f931947d752298563f6dedbea581b619f03a03eeccd1ffc42315d5c81212c798e7b2c7605a00eb166c47ce80ff49eb4a98261530bc0b6980dc7b57c1b89f6a412cf07290c0fbe5854038cf65d292cf9196693f43c8886145503411d177c40164cf0db26ff7218986bf322ce9bd396308f2fd39f73c6dff98282da845bb62a2c13dac088d815e214fb0ecd419645e8baa12dc1a151697e78bd46c054799478a672c57817ae706a0c89c81829af126683a580a80322e8eb69ca06c75bfeb23280e6f731d9bfdd641d1f91ab597a3d100382535a3fff82050f5cbd042b1cd76390ed37fadce7124a8de96612a502a50d817042b5e1143e6272eb16165c3d2f84d284b10e3201c220864800d6336e620ddf47f1a83aa76b93c55aaa0d62d211d1b2ffed7fa84b65221d048e1b07b5eb5c3ee2e8e779259f925a1922da12715533f2bd3eef7df9bcf8feb97bc5a3921e4e06d12c54695cff4c458c3203cc97b12100fb41b6cdfe695a68044f1cdd50a3377695b43671141d5c91324df45768b493540eebd4c8fe3e9c439b614e9d68ada568bd82b1f1fcd1e87ce2533012741f26e8682adb7c4fd20719f880e8274eca4f2625d6661b5926cbf689d7318c86cb22de40203bb20a53d9cf0133e0817610e997d6fe70ac2d17cf87baa10e278da8217d2e8d999ce1623587af3c494be0117fb032dc9dc06e68233cff3f0dd27a2b32af962025937847f0ad85a2d2a2bd1022817bc11e8c37022a84e9515c30ced19a0e4affb1068894eed1e4245c78588df8401463b8e8c48d076de0cedc159f371ac808f0549301d1be82367ea9d2367dbf387ef4d96b1dedbd18da6483a8ab50c7d2c7ae4e24fb6b1317f1916d7bbdb5b6f796294929a59401c804fa0438057196f3349933f1f2819658b4fee681965880df7af04d8440f26f42a0251637ef0a81e45488051402c92839054681532d69d5d2f2dc90a702f3ebb3782b2e49d0a4c1d56ab39c37f14b2c74fefbeffb9c67f1dd5aa63764f1bd891068d626eef63d6dd6be79c3514a29a50f7bc1ba16a9ac1dccd1a8d6c759eb69f4c4cf292aa3ff404b2cbed779a025163bffd91a3033f13921d0128b9c3f11029df89cd709819658e87c4e0874229c5f083473a1909c9988baf938bb79daccdd843cf75bcc7a46cd5c21cf0d55088a5486d562d626a2e038fb9bd7f9251639ef7a5708b4c4c3c2f5373516aed70981a62ba4d4ca2acec0a74d2bdac492a115f2f4e33c7e30e4e97005fc388f43a0251e15c0b7409cadd0593b64f0e3c7ae569bd11b95d107b2cdd9cdcfe07a9c904785d6831f671f7d9e201c679442d5b8214b96ab5a6d166737a10c33b8c2daf71f7d1d359801415139f29069d4aae6a88c3e08ab4f54462945f3146bc628c5ba54c6a3420c693e391741a0f45d29525ca6362aa3162bd23eca349729a5162bd3de92695b65da61326dac6aa32a2cad4af5c8c26816955d9d2c1c67df4ce619b0acaf36f77a8b053d69b5b68d70ad754a29e9c5b5d65a6748cbb45c20ce0dce4d1326d783e0cb19e802fd5b2ef0fbbe2dd0fdfb3e07ffbedff77ddff77d34ffbede0d6dae8474a1b65c80e1ab51e96027a47d20e9f74412bcb737b04b65386ee07ae5a64b03adffc219785690b3efe5ec0b6dad59eb6d7516f4bdd09dc918c27d102667d3c5a2ce5aa1100c9cd9f77376e517c618c2bd337b9f8394dc2fa9e408c42808d09f1f1f9f58ccf3e494dcde8d7d0461c6cdd3fcd7522384ac086893e7a51de34591270cf22543c50c92932558aa82a044cd468aaccaf36b0bf18e90b0f5755164bb60b9918dbaeb20b40a67976ecc2aab1af1939bacaa5159bcba1e567460dce5865f27fcc0e11d1d9252a6e16f61c1a1c25f8eba1b4e2720e96ecc4047c39549e9bdf49b378cb2e5f2e9bd342767fa51adf7e59144924e12279f6494bc222f35c1478412a214a5524a07af3ba5b7d6da18c415d75a6fed7bef0b570421b8ffb5cbf5a0cbbaaccbbaacb51d91dcd0f08987b329cf2436df1061996f1eac2d90087bbe817ddfdfdf91b819d3d1795007a66379aca5bb9942064d15304a05359f46a728569eb44d9eb6d22c255a9b29687337b1e64f3453a723d368509ebb9922ea7890c953de0089b09d9452587da499e35d0f308d844c1f164988f4463f8c2aa62dcae6cf588c3eed7b52f8320d7757a67633b68b3b22dc2894805279542b45214a651b819c9747ae89053563138bde8bb1e6c4c2348a3ecd180dc2b44926cf9f646a0a39218fcec79c53448f6e3336e73b181c7360d5559f462dcefd7b5f1907b6a3e1dc8235bdd1bf30ef41d847c358ec852895efc766cc81e07edae7d9a111fffbd695c2976fd83355f87bccf3633ecd033fdd99746ef0aab719640b5b3b28034a09ab0600e3ac5ac1f2efc5527acf391b89d21d0a524a29a5947af41863a40e23019d3f547ea4b49f48e9ffc51e9640668a7f600fc3f0a79be40f5591239ffed090867f22099147558e3a1e577439ea78d8b29492f6c1127e5a405518d010cc0f5f60e18702ad4cc310ccef1d59b9bf7f4265ff299529109fbe10a57287254c190ffdda3c7ed848c91f9568456fda4c249ef274e1f3df13fbe006f1b6a88b4ae6078099524a639f132c3f9c1ff366bdac1c1b8a1b2c86acfa54cab2c330e04d57049ee6e59a822c9fc2b2c33c90ad6df73e4b2d1365b24c94c932512626cff33c0f36671f9317e308ad58e9b7f0dfe77d31efffbf0f9446a4620cf81140e8400027725cc1e7c5213410d580b1f3c2cef5b6f527f6b029a379d4d06bf2b6954ec576f8482584165185cc81de278de42979e49091e7c56f9573402b4bab08c2fc4f08964f2953ad75de7c6a9003949356ebe56ca1c1729c1e6487ad30e787821966877580d62fe8a3d5867da005ab175c08d330162a4cb19081152648e0ba2050c2cf8ffc606d67754667606d7db0398309bd664b2670c12067206cc6113aab00045b71a6b07ebbf581283e0b7adff781b0d8f7ffdf07b6e82bfe842083a168b5ba6bd7ae5dbbd6dafa5c37fe4414b0d88bca309531f1eeeedd7dbfb00364875d20d7264076180772f55adb739c295cdf4da4710121c420cbf151b4602f63885961cca2314bde8b711c48dc6ece49f3be3efd03e40abb40761a65f65be009b22b029700b9c238e0ba710f6704c1d26c6500c13cbd9b0f27deefbb33005a6a8c31d21e1a638c93524ae79c524a197fca39e78ce055d7eeee29a594b4a5cb01b536a534ea9620020b997e85491fb0fcfa424fa635c676b5d61e02e5d4c911dfaed7bab7dbea2bc8551be4d65a6b071b88d96c2d6d3392522a658c33f651f9b4adbc4f459893f63e398a3065d3a74611a610ef6121db0a942bfdb10497e6a60a164010ae335368e09d1c754b6c10b26468c9cf7cc57cc0a33982a83636ace9310325ac19e581e4459e59458c1acaf6ad0e890b703ee86c38028b2e490e3f4af42f984cbf85e9ef2889c9f437381225d3efa15467072ceb14900c9918c400eca1e9dce2c754d036a875c67ab60a1098b1ded5d9f7bb17a432d56677c114b308aa16a88a8b316d123420cb076b8cca19b92289cc596bbf0b3385fd2a650ebb10fefc5906dbe8fde29cb30517664cda6e42dbbbc2d8fdc279fb87727790dc51da2208ed42d8c28c4d33d87fc66c37be48022df2431ae55611b584a6daaa186090d279dc6dd246bf7c929a2a16a0536295fb234ddae44d42f5cce96c61a68855b498ac7d60a64091fb63982968914cee1d9916d16c6073650102466cb9bf355310205b6b6f3b30c0c45ed2e79f9c78d2d5f034e5644789b954e4459e4588045024c7a32bc28302f29c31223a7444a0a6bcfa4127e4890ddd90ab2058f29c2ee6deead5ea51e1568e3a5c0e8fcb72649378f21f42874322479b2c1b511b1c0d484a1ce98238d9f16da67edceb6d6feb850f928e3358885c2c8135c851474423b6e496eb7fe04384f3f12ae3a6492e0b0e880e5c0e2e494bec9054e66607ecca5197647542d2165b7b621f7c0f10313680d8fac4aa4f8dd120269528e49c737e61c03b53d02877973699caf5eb912939b97aa02b536597040a923133daa54699866badd5f5443e902538cf9f178c0f348c82941c315369517555365f6aadb5d66b0208928fc5726ffdd166c6114f4062c8406365bd30c13a39ea86cce8b0010be1a8830e6d668aa8830e51872a57720765fa5016cb6245122a93e7bafef72bd8ee0e59cc6f8540402d6050a10513a2ac6e1efc0e59b45cdfc2e2e90fc6d32b7cad5086d6cf0fe7cbc0d3f87d701e7c1c02e1bff18938ffadfff0fc38e3a900e8fad6cb70f3e0afd0727debe38ce6a301fc3c7d03e3e9b005d379109623656ea776220492375cee569338133a39b25593380d6e007814df62f6fa48aeedcc0e252b382526b7353830d181e54283193918013021b4941dbff00ec65786bb9117de8b2d3dc563faa284f41b4a5136483412e9099509c4e2949cdcddbdb4b15f8230075274319860f178aa429f74f885773096f1058ab6018d6c528c9e8424a1aada419d34251195c8eaee9a17bd20b272379d214496106cbed4154be0dcfd832c72ca5319277a228a22da114d116df19c6ea80c1540099242c38f2c235a9af8c8aa4f3ea6ac56f23eadf4eaee26c25b41c4abbbd5c8b29e724892147a90453e794a6a31a168180e11a00e209334eeee4cb25b186888647f1fe92c00910c416273b73bc4448349077ee11d8cc15c0c0d8c24922a4d92c0f9d01569a67a1e478d998f7dcf3f56e3e59be1ee6aa048a9a9f23e5689da46c083082b5da640e181c30d30b2e2aedddddd29bbbbbbfb008c32f0144594cb22430d0d4e5092ac104ee28edc6f9c8254e9c18d26f063cc8b1d992623849850e130c22fbc83717577f727d9bd3415ddb6dddd5d2bf26a489ed7dddd3d84cd91cbf194232ac72ec7140c500695c80b61e2c3008ec825273a7c680f382a8bf7f2a8b92348deddddb5585159bc4d63891a2b563236e81e82ab4153ad1c4f6a6892a3cb0d1b617ea04d4a329c745c434b4d3868b1f160864809ca5403132d73c5909585e14de50662726068898d241c0ba66ec841452bd9dd1da57a3ce5eedaddede32877172f7a9ee7fd8ee7799e07656acae216ab30582740c96a9f4bc3922e3d2451221acc3883e34c616e436a6cc2285124a6c84a49171758d442684cb96cc8a4f0306607aa1c948acc7ac966249b4c9cc4d81346649e7a20e26888531226604cb46ab40dcc9114a426204536a8c90e83068aa1265c80809172fa0189105eb8d4ff706e53168f2e6eca621aa5298bf6f364b87b12f8857730de71811954882405412244d59123fcd54757222044a46a4c58b2a829822ad9d012a5018c16b91a8c9098783182238a1126391f3e8034c21c6a909eb0aa9ae0a8c1a9c88858244911270af5d92832253b0effea1b1b788110a6a2226e4461428b3041e2020bbff00ec6ddc207550f573d8e7664a14144d783148cf1d32a397346b1577777778daa298b57970c46f3ca5e0ddac7a8503b15819ee3647777ef81fd8c79901be1e444caca1235e2eeca1531504f8a2013832137c8907aa1a4de3a408316207043cc90ba3173214d479a68d89abfb2cf1d7777776163ca220a2e8aa62c9a303f58cf8cd500e430616b311da9b5d65a3da1d695a617643111f57046a756e5c58c5aab8f6d42dd460709221544698b8e11bb1e63765e70bc204812cecb8b2808c9a2b2b8937ba8bbbbcc8f18418091d61bdd5c9258451db9614b1219cc1a97bb9996e47a67151749a8b5d6f851de08420ca7b2bc8175e0a87312e5c56b03634fd7e9ddbfdfef356dd98f3553570aded727e07dbddc46beceb35e987daf7af66d0669b7cdb1875622703e4a1f1d932fa0b5b1e2c4c88255182a1556c4a9127e3e90b487ca9ff1f5f35b744ad939cc18e62b6fa65f5f880d9b51b85a5bab7dfa82a541c01863c08936467f8c0a88df305afc184788dfb01847a001659a74a580f30c6930d418f769db3006fd18210d860e7372186308f1a7eb0132cf4f4102d59ff19ac5a06fc3197716e352995c9a4fc31863b443b4ee501b325557a022a681ebd3aeae2949be1604514a12c038754c575e3ebc8078a1c8897107304612c00ff4ae370e4511a38c6529c34053f93d8eb5d6c63a5f52f0260b18e527bd79a9d7b6b6abd31863fc3888cd39a7847df66e0e46f1a2947ea05727a53bd8b6b4e0ced266a905d77cb560fa39d18267964fb0945fafb7957587e21d13f47ba231c618638c3f395376ce1489724b9a2012972472c9d40923306755257784c4e1ee44f88577300ee21244e5ddddfdea68cae215ee5e29e1d71025f88577306e777797373e1c381f694565f17eec7bfeed1054eeeeee3edd02aa298b2b5a5075b18e1012b92154586a4d4d5c8045082a32dd309811641342847677efa0a62c4a5d396147e53d15f2a92d1a04993a1714e92871211b09ac0c1395293fd65a6b26eb4ced8cedccedccd119dc1925ff711d39c0ad8b1256bc50f9e2845e3903c4992b9c233f933b33054cac6224cb888332462c2db902b2e668d69487dc3fdcb6dddded4911a1c9ddf5a88b974565f1d2ff6cdbf9f677de5a6b9b9a9a9a9a9afcf3aabbbb0f312bc5e7f3763089dc94c5249e6e1251f85544ad64f71741d9bbbbbbdddddd835e4c05f99282d490365a4c115ab374e54387636a87a0a1dbb6bbbbbd1a57b99b76776ff95cad56ab41a34e0bd4962c845f7807e32309d861391521a6098c5d769d0f306cd8219fb7bbbb9b899ab2b8333385cd6c7999b1fa988c23b283eeee6e7777e222bbcf08aaa1e92a86509a255758a0c64989b8866a4dd59aab3564d6a059d3e6048e164932186aa2648c0d2261746013c4a6c7c7235afd806184c88822a66aa0558cb061ba113267c066c7e607ebc29d884c326531c9e92641e1d7d3113e492df4b1eff9bfb6145bac902802cd922748521ffb9e3f2297fcb8a1ca8335128a8ec88c6e1caebbbb3bcbf380b05154166f757777ca2ee4bb2a4308fbeeee2ea6ac8b2dd685958b30af777777a489425298da4d044a6eb9a3b75aadd6c3ddddbf5d8e5255c264f75d6edcb3e6a836148ac051439235350494aed6dd5d1486cae27dfcc23b189f08f2f486e6d626c84651cbb0522446eaaae870bb11f23e7289036445910242876e434743b821a5a11c28832c1fac5aad964346dd0e57d93d2bdf74c9dedddddde3072a2265ace0b8a409ca418a145814c46ddbdddd3d765316a7b67828f4b0f23c21a718d36b19ddad8425c8930d310c9a13c2b439414c505177f78b2a3139f3f376497584c33bd8dddd7dd61d58867b13ef41899101aa342db100092caa21546e7777370e57a62c52dd2a7cf53a0107329fa3a3fb6728e94b1a2a22426a661df58407a3784583cc010728ddddadeeee0622f7d2dce249b8bbbbbb0c77dcc7bee7bfbbbb5b880070ef79bbfddcdd3f1b99ecee59777777fcb3da6892f4b9bbbb7bcd3665f176845b51539ab28e5128d65a6bddddddbbdbddddb9dcb6ed5efc92c4fbe284bbbb5b8816625fec284266aac8eac89421703c14e0daf052c7e4c63c8d891ab3731c5edcbcc021b7bbbb7b12438680d035b8d882cc96b7239c57d44509902c4641080171932285258503899a23469a50d5afbbfbc78edc4de21151dd7d5d909b4e45531691882471f2154e50eed6ca18efbdda56cebccfb44878abe907f641b3dcddc7dd1d0cd49445a92bf60430546ff31664a7eaeeee767777cf4daaa84975a552edec6d676f782026082d516c74a0c14a141f4f3ea2dcb6dddddddddd9d642a462921acdc3d558f1a2ab70d1fbc447ddeabbbdbcad19445dc55c2b9d7d347c5765316575899fa31617e2d1e60789ef7de7b34dcddca165a546523cafbccee5134518811a6bb4fa4914a51966032c405162d0176ac2ea4ac5c8f2d00467e382228490f57372814a733496f6d54162f7ee11d1c31ce9105cd910198914aa6bca06fb86aeba82d554170e043dee004cace93b7578aef3d1d5698ec2e63061c14d5c0d45426aa88922c3afac449e7eeee3ba8ec8e2abbe36a0799d73b88845f7807e386e163888f23f867654246da20e5081fdaf35f9757a8042ddbe94987664a000008041000b3170000200c06860372244ad3388e393e14800b4e6c4c6c5c32938743d2301c12c41084611804610086611800621880c2784e3bcc04189cbbeded30096d7e807a50e262eaf74e7fcd97e46448e01cce17796c12d62dddc7bd4a265a908fd0e15de909197f9b9620550dd7cdee4bcc21cf2b7f52ae0ccc5468160019202f220abfd1a8dbc8e9136025c943791f88307f4b8404aa9d2c23b1bf60fc23cf5139dae73e7a2502288ba7beba76ad4ff25284564647d74514b0542d6026902d45941b3c559231527ec4b685b7e58bd223d3eefcfb00f6f782355578b5670ed313d80623899f90236882004f6fc28f03117204694e726b19dbdc5375bca2f774dc302a6edb29ad40efeb73687b0d86167ae4f19e7ccee4b7e880d37d1563d0ffe23bd3411bb28e46becec4215e12c6b9c2c5122d94f6c5a392de2f1c24d2936bb5165e494f7c955bb49e6fbae3eef244eebdc190ea630196d97c1389c5fd8118504628da7834da0abe4c52e57fdf10dd43031d81e1440ba7d489bca215491cb24f5c38e8535c78ba51f4b6e4357c875cb92504f685a7aa4eb8740ec916d89250e806344f8c3bdd314d424b98a573483d36d380a0e585ab1730eb50eba47543b94f775c83c7e62c5e16a337d51125361cc8fb8c0aac5e1ad8292f360242013f7ae8d0d9f0bafd33979fe1fb0c91399679e573c5903b1652b9eab0d62499cdfd13785cb1b8d972db0a101792a921f95fda5e6410ce97dd8e81b5c27825720e8e9e1f201c94a620d1eba8924d2384a17a4e8ef937516e9245b3ec7828ee8175e2e20ab1ac58baeae1064734d6b7cd783b14e3fcbe2c8fac3d93ce03c6efe42328ca3227fc20a913d5735141993c7601dfaf721d6ef48c4a096ad913d8a5bf0178c6b807b5258e4a3a9e2e40d7cb5ddb4151c86d39a4d89f582776ab4c17e716f46a72d9bf60e67e895e80dc56cdcce8ed8a5c20d3ee98c6610b8b2cb3c705b2c20b547207957c47c06320ca5b7b94b16a270c1986a662a26a63ea6db870256d450a388f5ab6cdcfd6b8922d931950009e6ade2186ec01219bfe7e8ad5547f09012e4fc902f2c9abac0f47266ec4e5ed04c28645794847400a3f5793ae9f008c583d877eff0c48bb0bc33ec2e86b726c0845dc8e5ecbf1dd82db0886645f26749af485c4d3b4f6f5a4528d3fc44f0299dfa57393dc59be6c4082c291295497735a452839edbafafdd18df7fd0753970a4da1719196e5ee82c8e2eba391f60d8b9f974cab333f0a0dddb606a10a3d43ca7661160dcbfb3b94de91730d9ca3676ac5d758c5b7db8790fe502e6a27309c4cb2cc686e2b384f45917d81ca6899769328551ac94f3b65fad7604eff4e30d9094c086979bd02cd907ccf65136e347a984acb9752bc1f76fea3591a15aa5024a58cf7b74e89e2b3a9a250e2200510f917afe871f4a3f5e8ee6219d817d15d0fa4957e2ba71f2844a3f9fdf800b5932ad1ea8102497ea0a89b0ee15998cb2ad8543dde82a6ad366f77bb6fc71edd0228d16a150cfd56c7c6e8b20b8afba9498c87e41aa05ba3cbb3d1ac82499da03578a36878940e6f36a0f44005dcfc78257befdae0a1fb7f66a80d6e070f478cda8400415704b755c1abf4b29e138100b1b35467d73154eb7a789735578912bab30023b12d490cc4ca48eee6801a8f585c76e7337411ed71721bddeb1b2864ebd18fa861245bfd672238757fc739ae079242aa2d7f7ca1c729c80a888eae6b17d54b94970ea76f86e6c127c038153925d7f50f9a3f7771e17705a23f5a3adc32682b2c265a9aea26ffe21aba70728c060b8d9c2c4d7e53437a13f7f0826c7433620ed2e478b998b1cf4bb48df00ba7d85c07d3440d4a7205d2b428ebcb80b742254d5b09414ef1ce48dee71ba523c2009d9944af6a4204a426b30f78da09c8a566404e065702735e98da980b5645e6a68854021eb71f59e397b4f43cf499e812349ac58990f997341162aaf10df805ede87471c90a4848d2e96aaf01eb95a5eb9a0c31c50287db95a850072fe09f4a4deefd5708e898e97e763cbd0c36c72e49c01fc06e4700290d434efbe1c787a1db0e549bf88015c7b7f8845ba393781ea24a1264230e1de7c5a55f85a17ab1f45bf1345abcb246687b7b5f50c2659159f685aa52a609422f84ebe885388196b73035c0da8c72ecfef551665d22de5151546a2c053cff27de1c51d2f95854e29912336afba52e296a4a6529c1daed34c6bf62faf1ecc4d9f7b337de4ab9b7283bc9aa5124f1d5eaca6711f4ab12870ad33c59b6f309e7e72cb4a97639ed99589d1a7e5b03fc96c3ac01b400ddde021178e720b213b87a20436380c27f5fe788380a449377b93d533d0d69f7c603b3d5bfb7e1429f3a81380ce3b1e1f28bd1b0146f4f9c66ff12f8b7747e65ddbb08da6788783ac957e5da9c816cefcb7b553a3cf716985be289d9b45ddf41a63d05b37aca76d2c3427badeaf012d6366811cb108629a93d57a13d2e33980a6c5c1f18159ccb5c8282b5fb8b2cb3cc7a2e62bc16b1f9e0552344627c5244fc08f0bf376e694c5da344a4403491aad52e2505ec839242427b1e6f19b00da8834f09b4ea52283a2f5bf5315708a247815acd5be42312399a002cea5cb007777b69e1b98409d71d3dbd2038a3b40ea8e632002e6420980564d651e164257ec87c94857fe00644fc4f1938a2e06695ad25579623718244c83785985f7b1fd7934ec438b7054af47d4aa0a29946a3a471d2a66492411ad5643d2db69e8f11be824fa2a4cba830d94e8f03bc0b7dafc7539c48cbc68a73507300596076470bb92ada311fd0acdfbe66819a741734e786e97eebf381b2a59e8d7bc8d7f9dd8002a05fccbe944cd19239f02b804ca4e1203375ab816afc1a330831fd9739a8ff45a316b293d6d85b3df9e60431cc27858dbcacbcb38bc0c9fbbe10c312e776bf8868d0b9dd3c7d161a03a329598b5ac8715db3b059e57eb6cee5dd5e745af42f74dfebc7d55cab2344eaf29d49f969eb982a7a8e189e2bc777eb9784cc6d84fd5933289168f9d6f4204b3f165312f397039e5050034880da9bb1fcb729830651aec0a6734bb526ffda87fdcadbf86f218997545727d5c17d29b2ee0d1415c5973b56132fb1f20161d857395f95a2f9367d1fddae45113ec1c3112a100a716f7a59f650bb0280d7b95bb5c8fb0a38e21fd3a0cae5e1f0714cf21b483a98677ea7750aac69085505c8d1a5c49cccfc8b80872bc7c8705be6e46864b14aec0d342c94a2ce40e65f67642ea7797ad60b1c20db49472a853880b8ee4036d0b60165410cc35942a79853415977f26b22afc618eab7d2c816337b9f67f06319508178a12ea950b3167676ef63439857ba6247b70adebd918a274d09564172e992da792cf1eadc392318d62dfc1ea762d53bff122942e792a6f1334385041d489c1f90f271e1ee399ff41ca16b356618cd7520d3efcebc8b93bc6c3f35790c58d897504ad7a19e26eba5369954282e5e10b6e96904e3332c9e0f39f779af3e711a281db2bb101e1a7a29528a8e641d32f62931ce63ca901ee85af63dda8593ac1e6577053530019f9b522958a14021ba444c46abb14a3581c8e7d82a132584aa448f415834a2edb06829606db422952719a1fd59c922cd25b3294b64bcb2d6337d69dd4475d3c40b8bbc3925b41310c1f4e28a2ee349f3fe0460c68eeebbdad909a4f54f54e35909ac452f0716d0fb449f15b0671bf8e3f3f488bd1b4aadd4f572badaad63c7a794423fdd575629757244432b710ca94a5a50e3562c78c1fa85bdf5d8871eb469fc119d04490f53c678f139f41f02c138c1ec724ed71e3874eb22ee90474d2feeac37d9e91ab9fc9066a10bddbee12a91f7e9c19db15a4a3c106a8851f6084fba3f21a18703d5e9fd4f76f5a2ee41d98e3537a20aa0f4666393910a72ca2f60bd26a829ba2f8864f3af656da045ab4937f1948a811e2983a81d22fae65f53e25c013aa872fde3efad204ebdb826369958731ed6ea2a69e9dc190278b3e048aa88bad8d7c43dcb745314247674ab97f914ebe3c7f6a07b4d7c47873492a3f492260b9d918412f793042da1181d1fa8014742920239230448c9737b2c7d00f97bacef20da4c9cdaba5248c622b96131b338f8b29980b363572f6f436d0b6aa64bc6c0abb68bc40f6e5d376e5981fe46bc84aacd863aef5b78ae8ec6dd606f18d6f769b2c712d0006eb1d00fd7bcc09a3680ec8089e67070b7553d4f0ad2336f0b169253b6c9f857d9643534554b4e23a67cf30d612e6e13782231607aaf7c233f112ff08dac95b95a0855a41b4d6b07d9d1285c4d3cc5fbd6603ba724f2d4cacc98da8a8cf61491a6c562888a3075ae3aaf206c3afc5b72454c8b494c02179e5ca8cd027da6091cd34d00e4b233e1ce9cd2327a5e335b0e8e0afaffa34aa6944f741b5f5b9cc62edb7f483ea68b1aa39a269f32363f599c3c468ff9a5e1e3afff265b9c66de8432966693e74e4485c00ac7e274ed358e3854220a15937868d1ae49337b80f7228f00fceefe107c097d4ee504528228074c7013c55567081c9c51fb55c1f9f266639822831b88f99b0f77b38c7753912c600d87e4ad7f24c5106a879a1d289bb7e868aac4bf2d228e22907d9c5e9c7134b1efed40d7a58ff193fcad6e81ace99fd65742126816d8b4d0dc471029fe137864494a5ca286746729cee8bca137543e4a8a0baba054889ab394e1521b362e58e03b1730f3342859cab82b399199d3a51d5bfc9dc61cd64f93ed90859aad1d640021385bd1ccbb6bd09e74d9aac83506f6c915b22feb5e41231dbc4174f4584bbe4feff36780415504023667be83baa01514f124206aaa2c7e9ad251458e4e92890678749d67f227f838a1e04f6d6b0b3241fdb21b7f3d4dbf6121b74c4dc4d1dea3208a54fa4686b7bd30ca5e45b2a5cc9fa4738740f86d12fac80d0335fe4326747ada5c8932fca68581e024b8bd510a1f2168308d03ed1a4d1265538f7c55b06c96bb86a8d3227eedd02b3a3168cb08184d4585f3650485ecff101252e2e1da1c8109be258056165b0fb66d57c39b8d9093376ee83f3bfd6ba5c047441ba5e2bb14c0b96d1f5ef1a3fae06d7c25357105f9a9b1f04aa8da9f48e0a75adcb1236bbb09ad59e0b90dea2117c8095a474cf14dfd0496680dfa8ed75e30448b2945816f4344fa24df67d140817bbbf2b744cbc34d3c6d6538862ab10dfac5c5b3d7ececa492d178efa97eea94b65701cc569223d26d68ffb2b41586cea90adc8b0ce7eae5df050ecc11c261610942ecc032cb1e59b73513f2550836d754e597b62efdfc541465b9dd2aecb9abceee8eedb2be0a39373e6ebd97439cd04832dce8baddab301b8cc168c88d6cb6412e52e09b2680ce46b48011d4ad2ca896ad1b227cd447c8ff1440cb4eeb1312716de03319697a57ab32ec82d41c446f64e514e19139cde2edc13435e9b50f33ac35fbe9fd3c6b76fc246d0731c6b8bb7a9f76e978e200dbb55da5d92df2895f8a10e1a8f1b6496316292c853aed9673ceb56a17414954d0efe86c900b54c15f21009b132449209815da846cd6b61eb83b834f1e1b6efdcd10c4a7fc49807e64c391cebc8159f004679db98abdcd88d2152b7c5c89722593e06ccc39bbcd94798a65a5070fb15368b7642f974139b2ef5830c207503010fd12d1b945af02f2a4ad4761f80f545866ab160b2853aca6db8ae3744b6a8f6d6ad4ca468249db37d764845af6e2a68632d496ac712663c61be95f592d6cdf51d1681dd2cb90ee0ffa851cd3092393bb85f72156b8947077b5c873e6252c32b090b8b8258b97c9c71148df4dd02fca4ad958af4683ab0d2308acb0548cdc0bfcbf0586950841cb6cd2ac370100c92caf862a989121d0847541ac490316b7f87f601c443beef75c399856bdace24e17578fc986f12509f4d0300fe7c541c41dc840770d54561dd83f9b0424a54fe7c139e021be3ebac1be033073a9f0cff8da733402615b4abc90ff4afb6448b4988ec2178b0d7c51a8f05944e4bf4d268620ffee505db97df5c8fa6ae61952d863a61b6e6627dedceae6d617de2082fd4cc22f05e90f5779079f6aa96974a51750831deb60ac3e955d7253a0b14249f05bba6f356cb9bd9e15e786c49219923c78440004c79cf48c5eab1276c1a59a064761fb118b3a09fa2fda8f3e7d33c3cec9309a45a7a93d2dd45881e382c50d519db36ec80857d0125663b7167addb3597cd74bcd16bb2f4a3d3d626bcae047ed7ccf090ac17ff68a3e6103fed68e832de9da67e13aecac99439afb749d97d08912b25a79c862d07b40eec4fb13cd7310ecb4c0a96fc7fc2c744c1bc40349b8001ae8388c90590a4b60ea8370c6d4b4a28561100bb4a48dccb410120031df1eda0b3e0490ecf1a93dcc000ad360f6cb515c9ed824cd1f4e7417943f6c4c51367d78543d059874cc4699ee821ffdcfbe145c8ef71b8f8ede9278f806dbe28e37445fae12b281184b9692f3da54148937c49598b94e2047bb8d7c038e480548963ac41f584d797ea9493130acabaaa85118be5d3cd126e19196b43978e5649791a9bcc12f80d7d425a32a2b71694a1cbfb8cf5b89bc45e4360f518d544eb9b88f481b824d55544d5ae18ef792166b40d2d6f16941a26bafcc0aae24f2f4826869a6cc4a9df173873059852d878910c0eb096b5f5847b65e08915a49d032b9f7cb7ff65a34117f40b15c53a5acfb20f42bd1c6a3b929be9442b9b1f79515908dfdc4d63474661dcc078213bcc89f875ed5f8bdcb867082221d5edd14ee2c330aab1186b1d1e3cd2db83fa282751acbc5338a65bd4c59060c289e8c190e2d86d1b170f3803e9bfa70751d0175bb62e528723fcc6c2a642694c53e9774a8e64f285638f6f8557024959e6894f366855fb92a35c25e0e52a57f5036a0967c2bbfa90fa8d86aec965a0fb56ac9128769554477cd30348c4cfbb0d5009ceac358a17ef441b5384ed59c51264bc63f23b01b40d0e66604942a34544d3bf6515e9e182ff1421c528dc6713a10e8a654ef65e4e717636d8723d64265641d0e40f80a1c94484a7cd43daea567b9a7c22d4326b9ba92e0d82fcd0bbad52490d4ba25e337fab545c0037cc1f12007856c4dd1806c14c35abc484c8b47b1d0af8b9890b5ccbec1b8535e7a933485ff37b4a20c41c6c80c4eb4d78c34aad839113c90ac5fe50882b3405a2e50583b76595887915f103066ba682a10cb3710c67843d58eb4b4ec00c98b8b928c4759fbe70ac9892c473921abf01bf4a0031e57962a321ec79c0832b8f5a68449b3ba0a517fb28f22cf27d119e09922d04c3a6ca4d845a2989b6b0c0aff4cb251a0cdca2c3a0d9a4c888202fd2a7f7118c82e41821e0d31f030537e35a480155c033ca5cdfef3bf7ece1d8ba221de6c28a8b5f59d9c34167cf00c3d6a295f2a7d069610dde9e3fea085b61a59d5e4bac0c5733998eb4c5d9e20e55a91340ba67d5262983e201d24838c3e7bd8b6f69d90cd171c60ca63a51a958df3677bf26f3ed7fceb7d2ef4b95e9928cee7bd9d005c62b9cfac114a241569d2172c631b2d99fafc329409f625046a2133c8a60f8575f934040e3b6692b959c95167ab80cc790342e19f60165c15264d7c443b637cef5f81f0f0ba2aacd0e685ee168c666eb499db7f02f93096cfc9f2a933eee32c3bb6e76bb109923c50249fe31797c265763c3e2dbc192e5c076d6062c247de7fa8db623da1e1867b6e519d16c85559035c83ed2c0bc6d8b7d11349fb9ba2c1772bb74b2f3f58c1c59e247dbc9e6bcde652199784b2cff68da7b2a5b386aa4861c21c51df5dc247881566cc5000113d1228ec728f370d3e3f1541f6b8821ff74c00ecc3c638c16f9cb8895b6a3099387ef9f3d483e060566387a180e2c82f30d33c85a139e5297dbb712c32ba7a485161e62fe404bb268c2ccd481ac5b24f058cd211b4e7138082a4a6a43e5c3ba87f2992553832115068844ca04eb0b7d1ce60c16d8349e4f6fa2001e82ac921b6864782bc1ad666fb95815f903a4e1ee8a7329a6003338d72f495de4d484ad1008c5c6d8af2f286960d66975b3eb93aae116bfc585ac43be912b9bfef34001ea86f687155728399fd59ff3f26483e2c0ce5f1933feda241a4ca2a5e914219b37aa3ef4fd99eeb455cfe45c139ef80ae67a5b8f4f3b790c18793fa2efbf35a95e0589963aa508e236859045706e19576ff712775c515e149a918d151f9c27c914cc20c759aaf30dd4540c7f5ca32c0d3492ee61a74178d0681ad457933ac2aae9b791abd59ad0763038226668ad5e02da0d60f380b6b878ef7a971bbb6165cf82c426c7e9ac54f05788734b35b9f4641594f04cfb8ce5dbdc81f0b1d9a63b1f6644002ac7d4449c7c8718d1ee6ad7d78db3f34475ae8d953fcee1cce333fb0aa5c02621a8e113744d18e5df3c4f2bedadbd638043a6399d57bedd6a173c6d2924fcfb3000fb10bfda2c452f46c43dcdb4a98718eb42a9c2eb62c61749105f2ad6256f3a4f0b5973070e9706cc716e528c209bd8c75b00a08d36970492c9967246eb5ed1a5da4c508e0b8510d6efde0c9b4401860eaef68f937747ef5856a7a2b2fc9e3ea533745900cad2c4fa05d437a4b35373252b823e0ef8130846b028913da8b2af7492df48c611ac1b270d4a4b3a410c147c79729652418b7f8370b52db8b224e3174601a7ca0dd3faa787e937c3252f5a6466edd2a35e2c35ffabc3eff4e6a2df96a4d5ad90b24f238829eea5e1f22584c543b06e03cb36a812b7288034390c217f5b8f2d977f875610768faf4bf4b8fa1a410ae5cdb45609c59cb12572f2bbd3cdb2cbb15d8e45b857ef4e78efe07b32d89721740d8ecc65f22251ad2ec421a14fd544223fdcc5e0eed64803072bad3da0a761fe1c5ff6519f7b10479b5241fbe3bdf28e0c4efa746ef19ac4a0614091c6dcd284118234012e618449f9122a8c46d31e6e087026bb664443b158d2a82fede724f51d79afddf283fdb07aabba4a648f85fe577efd9cc34ed7481ef781818224694fc341d81a3d518631c577430ad80a0c264b97adf3e2aadb634e5eb79ff1f57945ff2918f989dd31149ac0baa1dacc1e30faf311e20d246b8f00b53f628d0c5fac7e0c1264b6045191479b4bc8af40f62a900c156a35e2946df7735e34cca7e30306170b5b04ec52a8a58c7bb8cc0739b27872e0366ca4acec7afe6b96ca008e4688786a1998f6a934eac6325162bc87aed93cc5972f64e67ebb5a7416c73d671e6bf06b1729188910a7d30ba1540d5fa562753837a3436f3cf7d4d15fd32c930c19cbe1e1e78c00f5371c03454d491ba49f3c91414eb461595e404fa79e590b442ca6ea2ae3ec14fd97a731507a4f8acc80f70ee6bf5dd21d7ea58783b0c18482ad0aceac89fb2e085773239b3b476564c94bb536c4fffafede0c857d091c99a52b3c631d96f380ba51ce052e5484d038ad34f3ca44a58652f55b28da7aed4927084df7ef9d1fcac7a1b74801cb5957bba1ce17be31b91c66e587b7df5002de5a903816a7de2d0b17340e4ec84ce5ed6350d8a6a49d90eb7e1bccad4e5917ef8e5c7f919e0d88104e4f66c15b1556618b5a353d58bd3a54bf64ad8b38593ea61479b16a6b171c1a38a8a0acb66cfcd283c89e97c744d903888b0c8a4e892af6d3f9df6000566f41149c559bdb1417f3d4b4979469e99ac1254adb0d0a54509b2ac09a383cd7ffa98e0704d68944b5802b0916d565b33633b6e528eb91430208ac44295edb8e1edc33e030030fa4ff58844806cdb6090ba0edbaeeac66a9110829fa5144d1cc3dd808c4523cd13cf11449dd6035dae41e7b7b1584b1670ca74cacd13b9b97dc803b070e53f05f356544c36a6704f34a6b018ba795af13be0b5e62e732bd08e86819e190f8c8fa84772200021775655740b28ad8c9ab7187e7e95eb679a3ac7734d7fe93de257f366f883712ddda6ce6b689651414b0bcb7bd12777987ef8d50c246598fbc629ec3f2f4876fb7dce5e80ff901a98fff7bb39e4ad2902045bfd409639ba072f2779309d65615a123cc95549f24b3938f96587fe326250be72d4c862d62dab016596b52ea56578ed34632cc47092d7993369d26ee1d660b38c444b69f9bcc114b080dee9241a760056d49cd14f206684e4fb8466efd84f1102e3606653b49013ac672f714a2677c772f50dda8402a647018a8d0c31dc93887ecc2bf587c7443641bc43bd16e6cc5e02672a134af59debb424c44c022c36de7cf575b5ea6062a2ca6424b441d88ce7edf9c6172d58625e2e57815184f72317cc5c38513c861a4990891481b37737b59f77324e5abd436c2eb329a4baad3d2012e9183b79220e0643503098bbbd66b3b105aed82892eae685c0402f483d54d2c30a3f6a3c9a76d3549dab3545b8b143cf714d00fe7e33ca45cf651592f20272c8b9fbe74252d1e06f8c461bb240b0885bf0c979c6258e324ede6a0e588e8ece7a337b7576b068f74b283494c601e492ad026ceb3c6a9a20cda73d2c6f54ce60d990341a44f8a40d7c395b8a75aa4e61e3e1513ef35a2e7777eff00d3e7740ae716b00568315a2980ac1cfc3d2580eb74816e658999513970862bacc54264d004cd663466b04db126bf4f6b35b67718644258ce52f784d46b8f5f5177a4be2382bde1202412e4a3b74aa1f8459b340f3873a2549c0ad3fcf2313c71164c41797eab8697ac088f2862e8d87216ed54d4b8ad12397183af00a8aac5c468da084422a4920e60578eb5d5b6c3fed2ba934de2655b302e89c72a039fdf98b2f3952ed63de33bf50e4a92e52af49127eddc476c6749bb05e6a015056a3114e6c9ceff5e1f5d00021182b08b64fd8621a01c70b4fb7144c2a41ad7b864f62ae51c42916c70c4fa6a0c6974e9bf373591d3c679e428786e19d12e5dcb330bef8e0a7a4be1a5f61855648c1c09b18bf99f9faefcb1f855d55b736fa81f81678dc470bbf851f863fe1df2b364ce6ff5716cda495a41e701d136d9de08dcc37c069bcc783bc93e263c0b45eb0fab9e402271f5c60e9160c221372fed360ad83952b2057ee35c073bd93461c1c606be8f504a95aafdf21f60b314bd41969c3bdfdee90f5dbd8d9eabfe70a7cbdafc174c958a539916cdf7ff83272ee670708f176f44949ec9a89b69684c22ed2e5300e572aaa57951796775a82a8bb50e3774041ddc72791eaadaca513994f0b3feb6e3a09878719a625089956d351e113b382612dfece404728bf1fc3d032936138d95bbdc8caa3db95b4c04525657c90473e828ad9c82ae412b7087607f3b0e418b0466ec83ad9ccef083300cbbd230b6b23478a27fde60fcfd79919acae8139415d7b405e0d0d65532c6e3bc51822d25ae39e504b9de26aec2e06447f1fdd06152fd917f0bba98707ad72fece73420d852f0545fe2d633fa660c9e742cb3ab2968499078e729604866ef9f2d9883eec2968f3f6d27d0b27a84c50982d216c14b401799268ae169a8337086b91895bd183d2ad690991d6780ef32debaab402d20ae034631714fabb1540ac4b030e89846cb2143ac2ffcb00d046507835cc80f39c1512e6b1a787d728966c0394db1af49d279b2ac20f81619c41209a542ac17fd8a9b5c2d5df5c095d26e1c2ed71668009392b3d9bc172952ff067edcacb82b3e21ac99ced6bcfd5ad53be8531b8f6170a86f5a9e3949faa05e4790381bc1adec4037db1ea51187ecad9581739f8abe2771b027c35bb5bcf330b34f0036aca66cec75c2f343454463c5ac38d816fc13b85acef6810b63483740dbe651f47930afa9b9ea35853f9cb8c59422ef327004726dc4c30429829921a0a2834e30084e0d6f708e2fce12d42ba301299c0f026f5076d7dca6a3b93582c03cea653a1e8e664bf3c1c4c57c6183dc57f0888ae2c07ff14f49aada5636bbbc152cadfc6dd056150f9b00531efdf9f240f40526ec5c095193e0652c95f7c8872ebaa342381b30ea8afeb1d47f1573a489952559e187990f3d380c86bddba72401ba7f5e40f6954053f316ffb16ab63be3b7a70ec0754aaf4451065655189e20633feba1db037cad232325b5ced32ff6576b12050070af42e5f4995e103951018cafc20ff0cee8ea925322ed004737a4e1caea4fa1cc93327d249b06486be117d5436af09a4e591285748d9b2e28c2586cdedbc172c8f87e6de737e441a403a2cc524746156d85b1d488a5860ab3c0c2e1680a871d9edae1b38ac4f1781989bc71ddfb3e3613c930a3bf56366684246ee1449315d202bd60253f820b34b19a794cd2041a704f65661afaf8cea5516b732059ac1f3b7c003165df4cd599c4c47a4ba8ab8011b27cf2d02f864fb1179adeb8d50c4381112dc138739149f241d1895a7af1d801fc7ed3d5e58481c63760a4c81afd48f0929a6e71d703069e6bdd30ea703c86c15a8248b3a3b486377ec2530cfdc8d7b65b6665640c88e486f03ba7f2dc3d8863e8108c84c27b0ac02abe1141419529bcc22e4ccc5d5a25fd8b0592e562ebb2e243bd5a11d1224442a4f099ac7a88cae8aa1e2d6496ea25fa4bcda80dcfc6a19e71144a6ab5002e24179cd921bec9d2d84fb020afe4d02fb4bb7405985d6d0a1aff90d471a7e17a367ff0fe1d91a8b449a5bbb3412672ca84602ffb505cfc3b5c16442cf10d81dc3b2a9ba3952c0a7468e308902b6a2c5159c2ec6d0703c846283be519b0145c7ac5b14beb97e7685bc5ce658efc964e4448f89a6fc846fb5b892c685ae4bae52c38a0364d556dd9d67c7ebee0a29245ae294243cd55beff4521c5ac4142f2483d6a69bcce5d95a2b50d19a12b3111162c05eb8e818ae73292a5bcf624d6d01dc24db56d893150112ca3fb987433719b0f220b224806bc75588e1d5d91d20ccf958ab06e4143ca69b5743708e352929913beaa115c1e686f73217a5a8dd2b3e8e63634268811af1c2f8d4340720c9152c4b90b337e30add11035092ba8091c8729c405dfbda8a433802414fe4701569c0d92f6e9ca6dfe2bdeded5effd3a030de03457dde97c9e2dcb0e15896245d994a4d1bb224fb3564db88f9b40c6200ac45ccbff68c92000f2cfb49f5a7013fb9c956886d75fa825f57660659ece3e79f1b3646b04ce65565e5f75054a1627a35be36f441f88c1611ef7fd66f23c0f30c0d544cd5b78a7782241d7ba7e5c460152db18dec7ab3ce94fa5b2d2a52d782901556a5a8102470cc94a3b9cb37ade480a458e2f22932805c07e3b8abb0f8ce3c04af60c7ba1974522e3ccb2159ec690deee35dc2f6973ce1d5890e705df2ee2b7158dbe3de41a0b734a03233cbd14504c61d9357de5354efed5444d03f3462c2b7a5f75d7c647f4cf3adfdec8599b9e2221bbe96bfe64688c42f61389e7eb7316af7d44bfb42478adcc0641cac1f828a5086312d1b43ff56dd1481ae5f8ee661790080477d4b1f1a6219aa9fa6bc6fa389d3307cb57c5874c224d21767ef1d6df9a1622a1b4600d026466788de688f11f54749e3e7ace45acb5b826df6dca657dbd6f40e8fe748d95b0c4e543eecb006064c7c7f8660565ef6da962cc92b9bfb05f4340f3a538273dfa02f89a45abff09f3968253e45558a3c2fbc522df96856bddc9c5150d27be331258ec21dd7df96b943e57605817606303ba1b835d79309f907602fc6a8a29ddf69fff392899f2c18cc7b00e319aab830e575201a2c7a0fd6b355b8f53db34247a26097c45161bd655b6539c0700bf849e438f257d5b18215a0146822eedac3602d6b56b58a6c775f6af926bd21b60099939dff4ce574aabcfeacc4211c33b90cf74672896e836ba23a70c8bbda29533b6693938eb4a9b4ca4668282c8b240783883bdf00b4401b6a496d9958c7af817f3b802a4e6aded5f8b75a0194b71f86188aadabbcdb96517032b2c94fdb80a037146d4b5213c27dcf6a0cd729d8cce45bc22e15d3153d2254a9f89823f15ee01d9eb3a5eed55c0bc03e782c56f7fa5368f112c8397a61682d71b63c409b4acce2892581834dbe62401bf7339076cca5652a9adec5cb9c7222de6cee1ee670f03b85982a782cf59369a447d714a519c7c6d9b0097ed61796b4b106e90cfc1e973204fc77501c16886e0502564423b1d386a634f97944726f948258018c7efcf7f352b9a62865583505deee1ff91426920632b027f9a781ac88ed06be4b61d02ccfd811e08d432733b6e2f673c4da7fb500cf88c55ca87c06a78acd8a0feb6745c660a1b8878a3904a530d0e9e52f25ef1239a48b7ce4576ae45e8f7aec1c5d1bdc1219ad174da9cd8da347dc5228fc1a5e0e073716d8163522f0f26191b7c619609d7c42482b9eed93d5d142fafb8540dbae69c858a3d368b48ca51229f66d20885a2d07eda98776d63f4b6882c47c819198af59d810102ea504e5f8e9c03200d07c20459ea99729511a7556faf50274a3efb8ed237460bfe85a37211d000105be679d0ad0340e222dac91ad7a79b1e4c674132f482bc996c706931e21fd99d858eb2a05bc4cf08651278c89140aaadfb1c61762c09bab46a0f0d0d1014268c8935dce358630a42910867ecac5fc11ac557b309aef4c38359d4eff9733009bfc803e76c7c191dc38def4be1e8e1a38a080c2ab3e9c900f8cf5286236f47d3bc703b368649b08f808c7371a75706ee6ec8f46490e8ad67e390dd7400dd2e0c8dd78624ff8b5ed227a1e99c50ef4da1c103a75935fa0a66dfae8fcca998ec8e13d79fc47c4e6fffe68630050c0c3c4909169c643a6ce7181e89aef048d250f3903d2f5c59d8defc434edae0e109da50d3b22b915135702ed0c353506fff78404e00011253af30d5d43110cb09cf1f951df7180879df62e04a24f149f4979d0a353776151109c04da762a4477350337b5fe1cf00e58c2d327a83220ee54758c7a9159ad08a873b8331d79517d6397daca247b1895977940f745f8420165c3379c66d32da0532fd94bbefca52f1f51a29b111161b519ddaa768a6e6fb727d09135d64ef506ef7568175090c35ab0cb3b635abe56351eda18a99caa0d57b836931e3192efe842a779d3d5bcc951ef827935d18cde2fb425c127410ec97e5bd98b196d55e062264ac69c08ae50bc06dfc687549c1f85801dca8ca692cc6b24d2b67556f47f4382d7d8160f278586c531f056c31e829735a055c2fe1e241695d37093c91e07f88c75297d51c4e571a9acd79bf48ff1e69df519fea32c6d2e5eca4725eee1bb74a27259bb22fe84bc9928d73535afda00f0ea260f943bab5290be7db716d49e729f9c03f63b213aee1f267e467ad050a83ac3ca1c267d605d2ce6002f36bd079b2ce52094e1242b54f8d1b562fc7e0641259c76613b89bb8febaecd9db31e798dcbf9d1ae225db0675cdfbe88091ccf42057ac38a30ac0aec7c7e1619d749739b902ccb0f2a2ace456f3ce51f43104e2b6d9c53ba3a3afab8854a7c23cfeb4881137c4f03a2b3f11311bb5c1b8266ad90e9ab36f1540371072b4082634472c0c3fd76df28b2a9cd3baec9bf6494de48689c3d15f4008f7641a012ce8f51ce3ba142a93d11a89a4fb6c38047ed3f9d2fc8392c041482dce85931b1d6ed5c83989ca695ced4a186facffd26c350a5d0c57d6f5ea28a6561d4c4afcde4cf6b742c2b267cac57fc80cb4bc6e6e628be67017945cdeda41317e6ab1ed50060512624d035c4345f944da0931c5cb47e5c1399ccd44085a0ad8772fda6933847675b70c0d47e4a1dce764205276f7bc6480628cbac43824fea685bae4a59c52ff560c195d312ebb61c9bbc5fd8f3ed0e34c3d6f32ccb4c74fb7d339db9fd6f0d327f113d0b881a472c52973beff95dd37511a6cd41a9e82ae16781f0aa9f947026758678292e331fc93d3b2f072f8a33eb5ae45a96ed5c95954063bf81d2411d6fee31755863b5eb140c3a153dd86f2ce8d6a727a16a39e9b62c01436542354734fd390b2d4fd6e50ce1e9d3e6350d0a7e97af64a7bc10925ca8ffa86e68f7d0dbf407a3e10c5c3e2b89ad23acce88a4cc23b259df168cb852323de2a77610e4bbc9d26a4e1463c5a022c071f239bdc2d0cb2252a4a99c3fcb085c829c374b53dc040f2154d9b92364189c245391b6b8bf3cadf854dafe4f8f0360ef7411d026c462783065b1577cbde603a0a194feb4c3c82688ca95a46b6a2ff9687d5ca220ac385d40be8b7b00239764a5ca1d7dc54ab5886a5f08c43ca2b4191d4e9c0d02790f8de1682e80d225c84bc1a516a6998f06923d3d33b320e03ac7bb7b80d9ae505995a28144c8ae74aaacd5d2068dbd0689700459bb9eac474a00c124420a2d80d8486ee4496416173916122bb79119ac4922e07406877ef96283f17c69ce777956db292ee4ab3f380c8fec182961df7bc133cb5dd520b6360d3a91644073b5411f9bd43cbebe4b04f213c882eeef6e44dde151997b10d650eb43f7b02abc813121ce171427bd345345e187dad2baef8c646c9bbcbaf25757832aea9a926e05f8d59565844b4bb59198739a05e50b4aa7442f19164efce9667083969e885dd1931fc03d419ae61124fc64f68949a6eacae1d46a1ac2f5a28d250929890206d184bc992fdfc6a15a909f8113783639801e9f9ba2fe524bba4998f28dbd569e8881fa06e5b6c480a334b1aef61afeb84aae77baafe06c246827a00fb8efaf26ffb677ad27afe1359cbb7f4943550535811b784f094eceabbb418c5f3b74443658d0d9bbb0c002cc17f0029b6d082fdc21a7b855ee7ea06960a57c6a7898381391ce96a059bbafd39693b76d7047f5edacbe3210f303f063e402737103af3ed72b1fabac279c44a2347de40bf4e6ce41c4d0875fc710a807f71f232e87331e9029d9ef444edd56aa0ea93d84bf7dbc70f184445e60f6da830886ab8650e033022bc2d99385cff187a579b8ef5c936090ab521e966e94d85d3381a2795b5d6a6e5dad644407d380006a7227351f093aa02ac795c93157fb0264322fcade1f0b252e9f8389ced158a252363b54f3d55f680152e78c5088e78fd6476c9c527f423f76dfd0c221a93862c1f96c5880f6ec5f77760d044f06eb11074e2b003297fbbee1427c08ab0b03d645f541d7167bb0db210d34202772506c0555c2878bf9527337ca3e6e3af2090ec5a797de947def23c247eb07b570b0d8e44a91568990cb800d7353ed43ea66794e8e266e1fd5eff51c3eac1f26090ce07a4a3c7781141a58a2c573a110297404a30b54950ed6271cac6272a938934e738067acf0a48e8bc63b3009111304904c3a4003667564f5240b11a7bee5ad3832c8750d15c9106a141d5eacad98a3cc845f2363ded55988f706afd67b3edd31d0554d2a2521e8621de8b3743f23d3655ca3ab1d76693372a9abac9d7a025d505ea6ab1dcea8a40697eb17dbd6e10a2f261023c0fa3e73e2768f48eaf9a0c74dd1e7033a9e770aeccb4726f4fa30a88c6e7785b10af9454a1d72c1279b2c2daab099f84fdf4194e7bfcbbe83eacbb8724037cd6329c0014fab0e60b80f74051e061155211d41a2c2bc74cfa46eb528d0bcca3fad0a32905a42c0489d0ec8dfd2f51cce23b09e43a72f50cec7790c0bd0dd1c8061cba11b948b98e40a2efcdf3d17395439d9b9ca0a407b56d166549fd5376c1824d988b7757b7a4de109eeb90d128bc91b7c34de7bfe8f53b967584686cb2cca529c8a8fe45b22bad5c51c291caa8f96bcd81e32b0da141b5095792516e249b9b5cf7dd2978deeae8a079c5cd375746973e279d7a780f960c1d4ee1d328f5499174e0558e857adb8b400471102767fac00445b870ef56560250fc7483ee4b2b6347ebff84b516c18cb15c660577751d141f9d412b2f12203c38a6c2e84ea4312fe8efd9e8393a60f5e9ab09dec0a9770d53a02cc37df0929a6ca010dee2fca1cff42b693e85d8106cf85dd61fd7f19b0c7e84eb3122f3e4a4d18d8b484d788ccbf64bf3fcd702c0aeac250807623b99407f1923847af30d3562c43832654c73efaea4921dc465fe1d152fea0591926759b88da9f22b57d573099dbd013376f3bc286ada71917569b82168e3f79b5f4a256bd0f437fe93ce451572b6df469294915b1300865f64765cc7b4952b2100df38b2ea83b70db43130db9f48a3e1f96e0d76486894f35735c8498d8f3326ea022cbffa64a02f4ff02da434e755920105a4a076a112722e48449c1b2fec1816ee22f2e89068084380700d0c339a5758753c0118775e05a2dc2e1c9986a554a41a973904aae3ac44de152c19450f20e88ccc383faf55eda1b691da4892464efbdb794524a99640a7607d206e506dc279b3d30894264072d30a32340b0f8495ac0dae888c3ea2741002c3efd9220004664c5477ca21e72c6fcf844567cd4a720510589acf8a01f41220a760e907c9932450d78b8811858c3ba4806305a72c8614b1916bc8135ac3f7cf978990c16e391077d10e58cc275db085fbfe598ac13044145932758c3ab7d282483e082e17d158a8080ca15bc1f4a30240ad3245f4e480ee3cbfa2a1421e50b8203184d306c214b10741827b97e48823c2f08824e932bd8834a11219031623651c93eb9674d4af214a27e214548302b7b7106882252c864b03845d6c10b298092042400493f78910dadd069f2145908648c90053980d1520bb20ee304d61f9220d79e85d99287629e42a4d3c06c8344fd424090830e3418122593c128283392c9600d86de1e044220b3e50eacbf3d08ac6e7046055714b97307d63a5859824b9c9cd15e4a5ffea56f29add3a54775c82d77d1ea4e5cff95f1ebcbd7eadfda92523e4fdb701c724b5bf5f44c14d83a6f034987e2fafb67d2b611b308eea26162c82f8999e0fbb2c6f6dcd3f7b9df401ff6197746cb675ced79e0e450844c9fa7de97b967619c68abeee5e79e21e1947ddc75eff7d897d9738f05676168c83d0b73bf4eba2404d72a399852973804ae5570da65470f47bfbfd71ff5fdfbabdff5a387a3bebf7f05c3d861cfaebd90fbd89bfdedddc3617f7b7b82fded04edb7e8c55a302232449663a108ead8b99359e8262a5096e47e168a7283dbdfe8f1d89e935dad95523ae79452ba671a1be8520f47ccf6e5fdb61e8e06b7a71e8e2d7f0f0f6bc316c01ff763443994f9fe0deb47ffeaab2ef72ccc519e94c6991fb6e84b8434351c4649c814b3e810ae3b76c7d7588f6457f3a9d71fe186b8031e649f043b436229db9f4097c9360a1b450e59fdd931b1896c3db76c90edf75bfbdd8f954a3d6b6293c31bce9eb87e5f8509e5f303ad9759b2efe09a9504776098b27b09feb285a58e15d7a917dc8d3e65c8c937923077b13a0f8aecdf39a0080b992658aa90414d1a1760edad3a774da9b20287ae8c5d60aa647f1a2464c71e24d374b96760907294475c6324d8bf1300d7aa3bd8bf4bfdc0e59ec940ca0857d4c0e5055670485202d61dd71f0ec93c77ccabad8664de5ebe8f0759ce6ada6faf59cce2acbb3a347976bf75cf45ef2853162b7998cb273718d6970dc658094334fbe7b5b6fa71d79c6018732cf687bfbee15226dfd53664969406f92a3a2a2bf0531920b57c5474707c0fc2b58a0adc750cae5d892825c0c4e1ba2b92311acaa90abc31f19441c8463d5218c346c2640ffd198cf91fd7053a30832a14f6c40d9ca8fcf83d3ba66c668977b887a39964b2ec20905843c964798845a62f674c94d1d185b82514674c60c9f345f01a1316bef2ec31ac2c9d72d869ba4c51580148ac84ec0de43f5463fef1a009a47fa8f2b040cc831cbf1bc83f8238a26742110ca4169620d3273281098454210e86456dc1d0ff470543f9fda127c11e1ea109dcfd93cf454f87907f7ffcf892477cc923befb7810c54ef07c7a049838864b9e8f5930c8dd34420a86d14ea67f45d3e88f779a463f5d40110cb2cb9a86ccededf021396a36e05a05d52931557ccec42039cc7df20a5cabda04e123b2e4a72f4cd9a38afe8c2fe7d3afbe05a5bb0eb32f5732cd3df36226378ba53843c0a9d0c45914e30d2c0ac0c0929254dddb3f1a8c09c9fda17fd432f3b2947be6252983907b160338190f91325790de903d15c401def83fd3064e8506c70fe57bcf90ce21e3cb9fb4f6e0f821df853c6ee15ab506732a2f4db9ffaa54378809f9063b035cbb7c29a79c524ee9ddc7b5770ddd9830d65a6bd938f98c7e4a001b28dab2d65a8b5b9885f151bd8124e9c819eb84cb11d9d65a6badf588987fd5268f67a89c517ac375134b4c8c34e1458c84aab5623570ac88b31992a652190a44146aadb55a927c4653a10ad0a2d452c16bcaa1f2f3aa36effa9f7f2827994cc6a49952000545adb5d65689922d6e6116c6485a645b05c949924c2683e13617304c4e906e0862692639452fb5d6185e1a972e4e7ebedff53fff1c945a6badd5d6b1758cea1cd5815207a98e94eaa22fb0c3228a586bc53b2ca1a2e402399452aac58ccfe8346ce024404b54028a30b2af36d9565c6150c2065088d9d0d48026090d9527cd03a828c46c29098d767192e25089a314074b9ca5eaf241d3344dd36aad504b5308163c3d35c964b2177a36439a2717342aa98043134045149411863a6a3d13123b4c6ae09844a1e2882f6bda284109d1c4a408265fb629e20eb32c513ad983b5d66e6f98b2b51aedf04f1740605a922487d082df2793f7a2bc443545958972a2cfc24b383a36ae71f2a2f9d00224447ac8c193304f37c824a05e809aa19bcca148d8cce0821a1c81f4660837616ea8720310de26a2808820d69c60062de4c8e42491e3a4aef1b2a6694d99354e54a36fcabc717af3f406eacd9ceab2d66af488b5d682b6d65a6bb5d65a6badb5d65adbc685d22b5e6174c5d115507e1e49d29c77fdcfbfadb5424139ddc16badb52a61f2190d668c12332d2569def5b2d65a3a6320c1052b366052e3c40d685083a021c93b91c9644f6692dc4c443425c92cbcc5b9d3e4838ae6a5d17ae91cf98c868224a526804e955a374dd3344db3d65a6bc1aa176470393a377807c76f0270acce050970fdd3de8a089e42ded3013336b33549e46ae70f4ac19e1df4417f39c1b97a82fd238e56b8a516bd1433615aab5370c843c16532996cf6f0f020d289a5566be9f55aaa237b66967696dc0e096e878478244e8f8a32b717a5bc70ebffc74543b3af851af83feec24db43543d63cc91a185e508557d634edc37be9b8e2bd76d35cd0344d7b9c266bdf6a93353054e1f51674fd8f977bb6c6881cfeab0ece32c3c1d24c0a32584beade7b71d0848333f8051767c1acd91a1ef2967bb6262987f8a9bc5e9888d7abd5c243b45acec3eb85b14849a725a5a493bd951dece1e14fdfe5c4585e2f29c0c418638c317a03f9c790228b1442e427966652b02087b7df6bee991455722b680ea7a283bb960c64d9dfc7591ed4817e7735397cd94073cf9e686a02f49cff410ee3fb175521797ea89dd0687205a7d4ef8f7efd88f27c1b0ac9f55a10082818058972a8cdef83fc4176e94ca8e8d02073f4b110b3bf7b3a3a6be00ffa7d40866e8ef51d1cdd6f1b0786a005f6e6617dfadad7da731cd5a27b3ca6f6dd0b790eeeedc78f931e8ffbf763e8606ca0f8da472dc81d8a4535ba1aaebd46ca6bf99db1286f9ff7f72bca1cc769efd5e7ea7cca7145b1d3348d7b2e7232739d53dcffe8e131adcaf2b02a7b9fd3b6ff7a3610470d79fe3626097dadfbfbc5ac6d1bf8838522145828ca1d0af32d18d6ef40ed85a476694894bbaf5f38a51fe926408419a5fe483701ea479890ecbe1851ea6bd88c491e1af8e3db7bb983fb0a86de73600ff7f33db0870379dcbabdfd71afe571edd5c0b03ef71cf785ddf768f5ebd1401cf4b9e73a2f3127cfffb17d58bfeb3af77070dccbd7c058bc533fee8564ccf9f6a75e1d952596a4ca8d6a0a7f8ec3b1b5d5fc0f6aaddd119c529f9ac9f533ea251ae755ebe4fa19fd27737f6d27d7fa1caded33ce947d21a71fe30b494e662e52c1fd42de9c6c93b94969bd81a7e07e7a5da97ee5f879fe204b5fe2b00fd87fc83dabc151f6a4dc33326cb22bf78c0cd28ea12250392cca2293e50ec19318e4fe887dc05288dcb33349e4b8947b6683267900b9676688c89d6343c229c72e90fb736c5039e49890540e879e38d342410e87a8a0807b2287432e3c8102084d049143166810446e99acefe47e2fc2c562f62794f2e7e766522065cac53b6186a64c2eeae141b9640f83b8881e0acfa328880b33d983f9512e39fa1772fec3a9047b7270b327cae421cf547e96dfe7212e27899c0a74f95d18f8173d7e0efbdc6f1ff7da1772ffc39ff3ae7bef5ec8398b53f2ef97fa26935f9956cd634baad59eb1ce94bf90e4b8dbd5bf3fede4d861ef545f4405232a82a9ee07e49563310719f2ece014d5735ee73b4e7ecd6ea9c8059aa89ebb57d571ca94f766728ed4d4be3f477c4df374085d79bd1df7b5e7b8d4d7759bae2273b24b7d0d2773b44b7dddfb27b3ea336a166c0ee299034062ee9eb31ea682bb6f0f4fc1dd0bb999dc3dd754c6383d462b23d5fd2b73dcf6de81de4b99b7efbc97db0fd52cf3f6dd0744fe50e56181ec2fc36842d3d86efc8edaaa82609e426251e7db0e9ee7795ef7dc7b9ee779df3f5e14c7711cb7bdf61cc771dcf70f1747d3344dbb6f5fd3344dfbfed1da586badfd1818066badc5514dbbbbb73ba51f03c380e7cbd7dac60dffc64ab0fdcba9aa94554a696bb55fbfae81ec5fdab3a6a84c7f766da3be7defebe729e0fa87ebaf6fc82d77f509b24a7e37d0d73590ea63e027dd738f9fe0edf1135c5fa57ad57bf153397152b5a37eb5ef1fef4bfbf7860c1273bd7fc1a72faf94d25e29e58df58b11fd75ebd7af7f74e99f757ff9f55b79ef0af90259d2b82fe4f7fe0ff956ca0732d4e558094335c74a902b380a17c8379a504443e2a02fc1fb42f2ad9c608d560bd7beea54cf450f4733451fdc7d2e4ee9fd44315548f49cb51e8b319923187241705661ca86e4d03f66a386dcc941be50fb6adfbf2e748f5d23224332902035c70f62330b451ef6d7dfc0588cca43deb3232e7dfd3a8c5f5f7e413ccfa6e15f3dacdf34fcdbc3b18098fc8ed6bfd0dd9fa83fb25094fb633226f3fc217f66f9e08f7e07fb46c9c0151c66823dd5b3262c399cd963c48f01cca46b1bf13d776d03485cf2a4d7a5546d034894e5580939caa21c2ecefe961f16c5971fbba5fcdafbbbef1be8b5e37efdb1e5aeee8f1e0eede397b51576d7f6da7361d1d7da3e2e8c4162deb6e951ca544ce61e1c326baf81df40fd31df6f037fdcd73ec6ee7d0dec16c601c7dcde6b110c89f2941e2e20bef6423246e4afc91c935962242cdf81728d1fc15804215b319c3f9e01ffb0ff410e2d188fdcd5f948036d03c82b4fe92fea283e7e374841a3234030fa495ac0e24f56a8427e41d1c3883e141c3614793e513f96338a601d07c90528708192934c06a301d3bd03eedafe7b5efbf1efb5d6da0ed3dfb66b353c7b4a29e594d25e6b2b7e0b658eb4d6ae5c9a77f450b0ef6ee7bdd6ca56a8f6de5baf55ad7a544dabb5d6d9de8edb514a3ae79c744e5b2b8e1965958231a529b84b6bff2ea5ad18f1ada5757658d3a6142bfbc6c698526b69677d8831cee8d19f1f0b314f5f0db9bb0f799e9c4750d28974a27774ceea4372cae8d2630da491d5b4a9b1522b1fb94a697fdbacb576ca793bcea96945d7da2d524a39a7af4898d35a4ab959ef5449a5a473ce49e79c935249252533a7e7a8eaac2b21776177c9fbb27e8cb5fa7c29174aee191da61cb66674aecce818e59ed16193c3d68cce186b6bb5b5520aa74b0c5a4cc1f24e2ec24109111e88443162430fb0fe7601d51c0c32fdf035036a2af50cda82aee1577c067dea59885879a052a99ec35f70e84aee4aeeee9abb928cafef4a3ec6cb4ca5a934af4c250d88ae8127dd92a97fc9f45b03ede7323e3cb3cc312eca843ba5fb44eea2df420ab2c64c9233a203e14aaee44a1e7a9a31d9cbb8994cff850cef1c8b12a683b7e9643af1a070264d28f1e8858c175f8f8c99d4428c3d679babf83f1e047e7c1aae14e37f53c5f3c37379e2f33cf8c9768510ff7e3ff155f1678c710bee7c5fabf72acd2b9b9292d20c8a00e8a0470f77517777195734a52903bc718ebc944a864a46ca63eb3d8b07cd25a6d9034e1497524d29eea2df49d80de6be8307c0cb0fa792d254f2a0003cfd79c56bfc3cfde965367910009efe2c33b378900726d39f63325d9a4c019873ce39e7c492e9cfef47ceebf3a12bb9f839e70b0de44983c3e944c2f8e94137003fac1633e9c594329366925f712cac163254f6c69904821e7c61cc1e7cf55ca90a8eff1fba12ed516a1b718c07f53f8d9f420c9c63b4720cd6f711d1ea4af782217eb0850e3e7425007cd873fc05585cc9af38160fbece00f83af858eea2eff34d517dcf0724de87f1855d0e5d49c9e6b0cb0cc5ec0234c15df4595f4f8b145880e15d81b88c0a9c502c21b168a32e189066728eb01c9c83591ca4191c363338b2d91b36f8cb667fdbbe7f36f04794d5383988ca9d0338b9e9e4ae222a77a3c0b5fff4a8298b4f665e394a24b27f3823c7a8452789c44bd9e4e4b235a7ba528199f8b4aa82db5d617ceae2537c8a4fdea432424537d93fb2894ff129b289509dbb2c787b4e19180ebf99bda1436ca10a3ddab38ec3be7cee5e8cbb59981ff33630088c9f3fbde9fdf633d5f574decfe6f017f6c89cf691e7535fd823a7789e83f11b8c0f6fde607c88f3f6303e6ce50dc6879f37181fbaf2c6d3ba776e3f3f9937cf9b7b2fe704a78dfbdcbb83e1fd548fcc7de12b6f77bcaf1b08bf30fe98e599dfb2cfc28ffb3cffe362fbe1863730667f7bfcf7fd6edf0563139ca2a4e77bbeeb7a5a7cf722c6b362bc8b37c2e28db888a1e281c1f3b805863ca03fc6f8398cefd66d8ffd77dc7714ee0ebbb9ea2317ce1ff5bd9e2338f5f14e0ebddfbe7a37e67773f43614bc77304c811ef88259e15ec87b6e9e99f32ff2edfd37f08420316ba0bdb32a96b2994cbf6b2d654a2315426d651105d73f3a547072a54c8c4a6a1b4c6dc38f94c6c83913a9857040668eee02d35050b0443332295e9c8d932b6564541250516e8e72cfdaccc938f7ac8df61cf7d80a1c3654d6fa5718f2a0f9dab7a0bdd0eb188184d693a6690de64b7dd35df4ebecdbec3db77da9afc77ee7ab7d8794b5c7aced359fcf1a18d2c8da574d84ac3d77836a724e39e3d19c737a37e2ac72ca94fc19fd5deef239539dea9eb5ceecb099453df1c5975fc729e7b78c71ced9b90b0a4a0621f78c8a2417b33c4629639431ce1438a59bd3d331e774f1a18b3cbd3c7f3eed1a3d24cb468b9ff33bd3a0c1e263f1c316f204637d274fd9dee5413fe4f3f0bc6001fe60289cf1fbb71703593c8b6fcf847082b1f821cfb3e801c6e2cb67f1f3795e48ce8ee759bc90f7f016e88282214f0ebb89c573f16301863f5986cf00906b198b1fce0f22f314eebb48f7dc47822e163f7401ceaeebbaf92c9e078cbd00a7701f5d4ce9be45f754dbc02430601ca884e3388ee3388ee3b8dfde8806e237b2812db982acd1812f0d0a0a4afe2bca550e3555521fbe6e7823b8abeb5fef86fc178efa121cc15d5d3ff5f369048a60b603877c86bfea03425780f2c97e676eb0ccdc28cda6588a52882da232060d3dfede637bbb81eef7be6bafd5d7c0ee19a15f5bfd3a9d4ea283580a3c3ffe2cf3028cc68f306bce5f29c1a12acf67b5a49452d536e45b6aed4a86c3cfd6fedf1f4b29a5b4bedc48fc7805193ac15e82a54b77e950cce904b326a5f4887a6fac92b47c7e2afdfa1d6e1ba38df5ccad33d6faf62df8c33e48efbdf7de7befd534ed955b867fe1a80fc54670f89952faf520c1b1463753284d6134044ea6917bc6c64bee5f1935c11e70dd6c261038822bd0be2a42e99b80f35f21c1611fcb5d51c9bdf776592233a285594bb89cc0082e2d5680e483142c38e440c30ccec4a0844909196d75023545d4604e106e8810e30606253528594196950c1a9224552829924545c37c92044c9226b218d165490e4b94f4e0eefdf73a0d1148f8f139a5bb7fc7f2995afca75c85c1524ab98649110d513718e1b2030c51c4007332821517356f7290a4c9d21259908ce982c409a699952e4766e88244a9cb912e2b23565cdc294c1d16188961064b6693559695115028f0648311a4264eca3c994c646801c5ddbdbb1c39caf13b1f5eaf225eaf564b4babf542ee184431c39d2844983055693105a7f251f6f142112ea0a20667a0f8c022820839898851477371448bc2e94893135ebaf7a658b02a629483d10f2a3b1a9b0d8aa6699ac61559c2e8c813f07a9aa6bd89d3c588c8bdf75efa42d1102d46be7819a28d134f2e3c694ebc507365b0bad285c89c2e449cac5a8079f04102c90f75ec849101ca921404a1699aa6d1a30dc09901942e759e5c419a4aa0d8508f6845927429c264e5838bdc5d8c88b9c29b6551723fde7b6f172343e42bfbc13fe0ce737585d5c5880e5d8cc89043fc480e420c4e94194266055c98920cd16aad945aadd76b05c4eb3591e47e82624c92284d34412209587fbb585991d3ed54a72b2b5be4a983fde35be944c83248ba8c47e54e904c60e8e32eb9a55a5ea293dcba6f8aeab56f9b55fb59c150037bb4a7d28b06deed3daf294bd964e649c2911dd52045298b97314e733cc8c8833ccbee8f3cccca9ccf03ccdc6f31fb775d88c872f79ac74332b9ab0353180a07d221841c0fe761022f1ed4bdfc0e0c3d73a0640ab72e43a072eda277822a77036d31a9440349ef82b128bdb84b76a43425c77868823cf304655303c94f83e592646a27b9655c251e90563ce027fcf433af094d9e58a3242a8a2f47303f42848d92ca6869bc3d797605d42a05ab286d70d85aa5004a96568c150a301538d492564941ddc909d5b282e39a563be02f2b1d563ae02ff88b4c66b96564966be6aab94eb74c138de69a71979751010ea35399ec40e6aa0aee48a797928959edb0d2617685d32a29faac77eb5298858cf1c3517057074a1d24393eee628c2e5f5ca1dd9c73d2ca7384b5f66a2f6eb06d1bd7cdeac849759ee77929551d8231c62b162c90b0ba162d5ab460b948d9c0d3bd78f1e2054f6b4e89d1c1800103468c9e550f1d743ede8d187dde078697a3032f16efc8591d29990503b7b4dcb3a82c5d37e33a6fe126eaced31d991dd51a0def208225cc442d35f981c80f445ef065d63096cc2133fc30eb9e7477e87491199945b90a907b668598fc22f7cc8a2c5366a63447c7cc9237406001924c4a1053c8a9a27180832d3844a1810b9426da2972537e3d1687672a6331d3a75fcf0ecff13d63ead293206b498411c02c8d204297114104dc3524133220b384cc0a1a2460feadae5104a5e98c17188468c1106260fedf358a4e90039d2bc61c21b2821c60feaeaef180254954393ad3c41056c0a89072be9c9fe7234e2b90bc08ae7f26f5cc79b577fbc9fded92cf7d3edd6a56bbe4df0fb74bb64b3efd66bbe4c7af3bcaf76965568c1394524a39a31395ef7cc1c6188514b0c218e316d13bf71bb8491081764ceae2900692527a37bc2d0f6920ffa9e50634903fcd5d83002dd49ad10168036cdfff41dbd0561863ac43470fab8550ad3a14a0a345cfede9f124328805ae543a549fc323ca2c7e8411dce5ad0f4778bd60c578186da383974fdb86cfcbafd1003d24ccdc828b8f60d440fe3e5f34f2c5233104ff18fe30be78e42eff1eee72f1804d2fc0b0e59405da19582c2a70189b9aca38758d125cb043d30f5600b14508987f7cea1a0790bd59a28a951bb09081f90b29c0835abc906cf1a59e7e2c5ed808dcb97ef85345595353530f77b9c13149fa709787dd24038e322fee5a72d72b36c518b32889e20a1350c86ec041470191458cac07264d58dacd0c55846842254d1b1cda4b152564513d242133529f7e9e24d3eeeb6e17058263e61851aebf63e60a4ea15996428f8f4d714c4c139b628c35f692d0912ae4d8007582a82d718a6280410643d4c89a92947c3489c326871d9c886205029a907943c58626de6c913cf85630c4b1e96b4dd66898131ab2fff675aeb9fef6c50ea1bef6c906aa7fbfd940f5a34cd6a03da3fad0e022d7f77ccce9eeef9fa397b8c4220e0ebbc947063ca8a99d72f8ca22bf469193364d7c5801530bdaa081f9c7245da39b82083954d84047ca9bae5184c31338d8723445cb1548c0fc630d5d430339446122064e7a500206307ff9c5247f21241651e9c19d78255111cc82f6fd9360e836be8d34face9cfcd41da5a0d3696236784e0a3ac9a4cf28b8a61486d45ab9e7ed66cf981e00e5393f1a20ae78705157a63de40cfa1de86a7265da434e3a2918bac891863f3a7aca415603c5c7dd75bbda155979e2eee298d9c118d18ee9c347435918b8fe98bc13bb387dc6741ea494b24e2f7876141101491e56413a45fc29856be794a864545136680f70ed3e964e0db32aa59dbbacadd5d6792ba5b789b852a1a547265aadb952001a89eda7c0aa3566dbe16e3929955b744c6bf17d3359fb7a1683a7ac7d3deb1275d960fb73ba6b4e3c2bc61669ce39275e99e0ae3ca7f476d8bfb77a73564a29ad94525aeba495561de69cd3f65c65805e4935ad3690043d7635ce4a29a595524da3b4da3a69a553cd5c4dc05d1ba771dbd43a7c69adb74bad7275811c9d65fd7b6bad36e26b6b85c2be9de35a9b51a8e4ec376535ea18b2190000001000e315002020100a0644229150142541bcca3e14000c7c8c3e664c321147638118c7611483400c832184106210300618a32082c66c00fe42c7cc9bb80e75a3df5054df62271c071722d5263ec553e45da32f15fca1df00427ccdee556e9100958a6a2e58cf84e137f009d2bee5d99409f6eb959d824194486507e3d9831b95999b2f9c48847873d683023b292811404ae9f091a962595d988eac2d94265751a55aa272dac393eb148a2a7667e0962019d3691b3c38280def34540e676cc8b5be526731735345bfda653608d47fe764b5298e9bdfa13a370d31c074a4c83e3196e5d7def72eefe67c86f2f8fbf112af620e8df212ff18f8568b8321bb95541e2a3878868fb0ca6534be5a0a466e5367770f5f126d39573bf6555dc7f9aef7f852951113c56c856387bf75167f0b1a9c07b98f439c22d2e7306b298be7aea7f4c18bd4d302d44a974c30b44970c7c247bd6b96c639cb9491e19c9fac4ebe0759732ac125bbbe9c20fa94e1b6e1d49d1f702a84a32e02708c4b70c88d4a2926461256dbe5989a0843d418c69876e2968ceb2b52d19747e4dfbdda9c33092963c36c52b50dfe30397669cc068b621ab6bc6140710dd8c04ea8a11e1bef024ce138ef48df3f38ea612f93600935e8eac2f82a82136a203eb60c608445aa1a76daea43b525f7c0a86c4002f91276ff3e2a950d41af82e4ce94528be9b9cea73ee9a545b4540727011dfe9fa730c2db1c21209aeeeef9d6635eada8d41d1ba37131b99243ee6b8a49f1c934364d43b8163cb2af36b6fa1d6609d0048c77a80e6c82be944cc7b8aaa5ff526dd49413cbf1ab2de6ba44070a967d4505cb55450973f87709dcf9c458840dbeb0040ee4d5cef4685b2071c9090ac44bdb42d1baa720bb5ad3a7ba69cfb94856730b58ee02187e29930cc345cd934d689ed343d85f8fa8ee782f8c89805b51580282f4ad8e741429e6952cc13287efa115c2d440099a7e099d08133762089a8e0228e352bee48d2f05341b23c8c4782e050b48a673a2d447b789fb72461bea7a29bc52f94158d4279fa6012abc36677e11e18ba54f1a21fef35260da4f6b9385601476e883ea1c5a91b8f4d05e6fdd60f6901c9d6e663e83e09dbb0600da54a9cf8665943bc9d77e5d5124c977622f0a782a0b0637b546c45ec031b1e829da83b0fee5a7f119a722b1b8786e6f72def286d9e1358c3f5b7a4efbd685ead230a0399862db42ebc9ea273fe9d942d6c14a3f2d478f8ea6bc013cb9a96045b4b520fe000a856c7fad4f0d9e541cca4b723f95066224b7fc954bb1d1d971d5270057b58b05dfdc03e5bc1583eb2c90472595d4920a0d6dde02f34dbf8029d57ee60ab61a3a289c9a34a4f523a80063a000ab21464d1313df19cfa5bc610dfc1a05e34251c413508820e8b9d769d2eb82e61b3d57b509b90458e3459c3a37ce6b113d2593b7e484899e74a2458813155a49cc2f897003284edd24e5ee2dc7b895a029f0f9d5ad544e5e169c4596d7274ed7a9dbfa6c07a25c62d49b62e93b5c46b197f1eb7d40e18512f8074871b25e1ec0e47808880303d1421865c8d5a5a8884dea5c1eecbfbc1ff0417c2d0844fdf09742db2f683d311b8f95bba22960601fb8c58335710c007d4e5244b9deff159683a712812d8c18fd99dd5214ed708d537e44665bdd03350bbd9a9a81b5508f219912fac91d7f6ac40cec8a680bfc11a1c78f3c2d8b256c3a560cd998d33a5aa9987dbc0385da7d655690a1ee55cdf7640af016d4ef227699819ac65b7a57b6c80fc8674630ec09f6959702b42bbeab2d610956eebacceb31876257adfea469b259017396ce7036047004fd18d4a4301858c6ba9f72f5dd5b4cdcbbba6600882e8222439d0326cdad81730b317dbfd9108ec8656326bf105a0f93c06a22ba81380bd3766f186e33855938c65116a44c544d02f04c1e37d82216691ba725dafb51ba3075491dd6dc8702f8539b7adb2ba63f4a99a260daa34c0efa11f40138f5d285b655a68bbe8bde38dbe455dc08581e2ba2c30c4ce67595e93c472bd78d35026338efe562cc32a13ace85ab85f8b64d208e4d75666a580199eba62fb015a0d5e237dad19dfc5c6cb45b3abba5da2b851de9ec93da9ed4ed4bb58f34fba4c2f69ef4f648fbcdf415c7d9fc0a447fde62caeeffe952216b1e31517a4cc97b3dfa2e0f37342328d7a6ec486d9fd476a5b5438a7dd2d92dcdeed2ed4ac79e7476498fec7d69ed92a6ccf62b3cfa759eda38e486ddd009f17cdee7e57d7eeff2f0869ff7787bc7bbf7bcdec3e73d5ede477cdfbbbcbc29f0fdcde319ae1a5e1fec0e2b4122d33f47c5070e1d81485c3b303029e0663ba976a9be2b08ea41f257a0a4ab190a991351dcd557e1ce6f9ba6b39e208458ebbf7a7ad51e3ffbc32fc278baa6640327d931ac53821edd5d5a049d5692a8f7b0c3fc9ef7bb418451a132758bddee137079e8f35471fb5ef707c6ecb7017a54c2c61576b4318e3b0607373431b822585ad68efa743b42cef53a5584c63239acc31dba831ddc61ac435df46cc655f2bac29101ddf01df99f098f0db87bb5cf05bbdcd43c042d7bffb73434cddea799a57a7b1e9f0e1c555a663d213c1ed4dc177f7969a7ea00ca1088d48127e6ffb29fd5107f202012131fbceb55e71f486d7db5419abdb1efebe6a8ea68f510a8071da858cd1a717818f4d917e77ae98e6a1808f861e566b691c2b1c14fdef1f76b738cea1c0cd0e21ffcce912e2df05d1cb0c6cc0fe5d59e3283e5c0a84b62777b52e522b87bccb712f01f01cc4da1e8368735f37359061f16b5bdbaf90bcbe2114cd3d0ce7346dae4cddc67d7388ee4d1ee3855a50fbd1ccc456caab40371566c5fe739d55574f21b084d29d0e03f7235e9fd2887cf480337c8feff53ea8893bc27d4a01f7613a1d2d2e9f722a54fe8a109b9014c9b37686356dcab68c63fbac15e75ba550db5b277068fc588087251a4ccf58e2bdaabf1791c74ae62ad1aae166a2512896652e2a3729670b1989ff480eaaac7e60970b3f6f2c77faaf589260c6980832e4676d09bd9091241ce177d0e429270f1af95f0282d714b09cf30697e8f5e80b98fdd7d63dd96cef641dcd7ed1ab31899e5ea21f6b9ddd2a898eba0b930dcfac73798eb43da821c8c7369d3e54dbd35332255b4e723170b210428991cacfd8918a0b8f38afe9e071ecab167ff9528e194f71454b4c885db09e9a684604e0e72ff211e18424c92685a51eb4cecdcbd7d26892630db9020c3361627e0895cd3ccc6bf2c4780fa0d23c112adf3e24f6c1ddc280d866447d37bc2b8961e3a6b1691a96df9b75be8dddad6728623d367d862c9ea1da99b454d686b4b81ea136555969c51df1a509159e49e41ab230c0888a164523a69114cd8a4c70e779b0afdcab42a62604783079308e7e5f36b7be1a8763f1007de1567ffbac3f7be843054fa42a47b4d811be960c67933fca09df07d312ef852cdcf2fe75d2b715e9eae845dfddb5f74ef73b7d7888292ea159fd307b3ca02c10d104ae93828588cd662b01e4a1041289b39c1129b1c7b7fe91cbb34d5737374818d73e212a16b779295a2386c1d9a98ce4bd72c7b70732684e30925d8b91596ac311c113821af82b226b492595360a531faa72fc69250083913105a6062ca633a0bc13cf4c3a9031b7da57dc04934422dda53a9e1b962d48a7b0773b363bb09288b439782da459bff20cbfe1358b3d8e06134a3df62d7533e01f8329c1e1975bc12b56029e4e0750f069016489ed6c42745dd3cb55eed43ad55581894a1439c04d66653e020cac42569e8bf49eec603ebc7d91b1a73b807433cc02a36ebe6ba187616a5551ecbc12df8bc386d57101c846e63a733c8101af74b6056697ea8285381e18eaa4b34033b664e95239fd14265b973c048aee7414230773070ea3a45dde1b3fb6f4e9c37efe47c35278ecc4004f67b0b1ac27aae6e48c5c9b60444038215b804484845689d29c00e7e9b10de49c5778652a2d731e16d978e725e98f5ab2c1373a1a378ac56e0627d0950e7a13598e32c5dbbec37e7a10148740b112e2804686f7874ac441c1deac3e9d7749d1fc855da0c378bace8a160a95477a3ff8cf5e0df608e2a77a4e7c9238528bb393091ce8d06be87ba3e9836fec869525c08a7725a0dec17e6aaad8c6260635dadff3f8c62b01f404d70d0107368ae174a593320a1e8bc3428f38b20eb6db1cd543448c124253a76b19276f43e1977533c3bffe11bbf573a0bc0642b9c995c7846e4c3b55aca6f6d74ecdbc893fb65a95d69527a327372e2405770357282aec21409d9dfb2535fa8f9893a8a085aba08db369c00bddaea73668fae5e73af95af6fd36fa7520f443860857ed01fbca1c7f8f22aa5300681bb1618b6164fb33fc93b48b534c422d3195cf029f6fa6ece16eaa2789019504a3de80b94c47269281e6e439300ba0ee8c4a221c830dd89083948ec3cff7e90f762d9f7f5049111147795c1c59e793534d76eb69e30eea358fc0675e12c962077fac0dc9515822e77415315de62cf821422b979fad1a096ea46f5ef9caadff70b8fc2633bf2d66aa61f7db5aa60e5a3472504cb45c91cb6ed32084a744492abe049aba5fc179d8e7df64f5588ed61fd8e4be195f8803c11acd4ec36c2a044fb1abb11b6e1443827a487ce1a61eef17f912f2af812c81f90da8c6ea0e3a092a1802ce48e3d3a644b00dd417ebb81bbb0ccc7969e149c5b2a03c57b163e8773057113a7a8449dae06dce48af05a51158a19059c3d93697318f876b026372830442aca7f05820588298d48a0ba61b29174babe6ffffeea1eca18e7ec4c021c03bcad0a474139b7f3d48182a01ff98611a38e5f209f32a35e70514cc7e7c2fd2f2f8226eeae27e475b27cada2e82bd668419a0a98316c8e5c6b2b0018b21f9a84caba46d5c6fb6c4fed0b371356d5c9237912c46ac6734b978740fcf8547b9b73a4c80be2e18b88d828579dd3f88a166b647614221493d9555a2f382e289b9055321010396e73203dde758395ac6287c110cbc64a43c062547bc9d2b01865fd242ee4821a044561d9527c801d296374aa5bcdc6cf2f525e768dd15b726f7218ad7a61264abf9311597cfb5c98e525289f601577c9bedcb8997ec5f7bee01748bfa8eeed26ebb682ace5bd09c447227a872e9c0fa6832c02b5a7d6009ed8b6738a1599a88e5adcff86ce0ce18ee9543a423ca8bc7a0614fbc5a048e29695301f9230ab177db2332cac64b05492d2ec15a9a000c16e9c188f574659f2907c0086347b9d222d70e12c3881c298e0e37219f4e28086372c176e5df78c740461807093271f38f3135c3a182cb454aae1cfd8d3610604df55b0a7ab3024a3d7ec27f071175ddf14c3cf73d318efd9c686e780bece93cd7423c78d7888e9fdee2417c6208db48c847c45b979a3316bf1691c4d8c2c4e65d83bf1241fe2125a03cc515b1448c6cb9d0c778131b28474c76082fcd921d6218451814c519864a29bae347cd52251c6497b93b5350e7a6a78a1a8cf6ebdb15827605dfc098d465c5075f1626ee76e1996b2703e21e857899f4f8ff0944975e4850886dc7a58119123f3a1cfdac665f074a4e31a4268086357ee3afc76e1a443ea6320963d4575f68b6d8631ed44e88fa3e0b23aae2f7d564bc76d12f1bf18e3e0c5688d449381501e5b208be7d0653a1e6420daf9539833e9bb7eafbbe5c1cd1a3d567044f6a8588c3e5c25c6ba46c2fef19e77a4fd35a93878cee6e66925bc3ea33cfe3a4210305418b35440aa09f767cd521174eb723f891763da047ea9fbb3d2d2ad8c3a5b12cb5f52b3257f09657f7335450f44d6a73f4282ab4f1f3938fc6720fb7133bb5d22783196176ee494217a5c59dcc3902d2e3353212ba33708fbd40e93039d2e8e6027e92cb4a4560e681a8e81b67a447aeaa7d0fa8c54feb5f7661142ad8ea9e219a7ce0c5298512f0939d0504043e39e363a0d5c1a4ef6af8ac4c8752fc5cfa6f3b57c9bc49793c6abe1f81ff0fdfe3d7abdee78060918dae0a6a53e698ec8b4a8a18cd6b446c8debba1365d17d6f1a8091b3436f9414587e08b94ed1a5ec4385a63e48d63e8ad062f551ee2565642c7a5a0aeae5e7381c22c5d443d91cdcaba525c1e27c50f9d5bf7c66024e50bc2198246ee25c0725c545b2d531dfa22d68592ed1d6e1ed5d57ff704197a6a7694e0d2b8b86726c09348681a6a85cf3cf3018e06e79bf78d27a08ad9f0a0a570550ec16e784bdb51e0c808296384741d83659cd071690124514f4c6a56c1565f683a9663d46963d1c5433a32e3584507fdc625f9b9736c96ad87652b68bddabe45547ee245a66ddbe27fd74c6ae5e7cb95989220402e835ead8efe755754c49ad10536d67933486238f800c6c500b0d688ff76468695dd6a1556c7d0145a07d327ab10096cf94327b64bffbc49b1a775c09d0bd74a65c5be42c2400685b8f9056277a33d02e0774a3bf06632a5a0f34c55a13b9dff1876ed7d23c5fbc028af4863eae503ce64b9d98c5a6b7d013d93f1b18592200b62e4b61b342f2a3f81c374032768b694b15f4d0b4d046e54c624f14702b400c1f4bbac5e2a4e8669dae885ca3b475e8e36519dd2d09fd496532f1b9a717eea5bda47c28657e0e7a392a15408baa5aa1a5b698179bd557252741d602799833a6699b964c44bc1c584d78753142836408d08ae67b632bb67ae282c1056334ff12ccad41cd8d489d3f48446c543e552e46766c1661f2b654609a59b936628b981b00f602df1753be5f0e4c3424d476494509333fe7f510ebf7095f9a19d58ef62fe244482dff95b41455e450f08af170f245c5a6e3450f7c8dba958de12ad5b4758e288d3d828d381dcc3529d500e3255562370cc86e07f99064ac94c66b4596de88e9f73f392a3c49e300213d93b33996cbdeace17bf7c6197f3101f1f6aae7dcd0fff0974b96fe020e085966d75d14bae1499e185a4f51090e390bdaabf6fc2940269dd74e7e25a4a093dc44b0cb28374662133b23c4bf87a5b28829291be6e37d2550b94084c9479223deb190edf97ef97b909fd2eb0f95c756254979e00f97331198ecd9df665d6d60a75a2109bc8a7efcc461f9fcd1e0b4578aac7d6be6769d81b6e28363d23f0fa923006ec2ea9bda8979217f5fff41b6cae0e34b98af46a0abca0133b98a5d494c4da14c6138ffccffc1e5f6ba3ab34705118a98d56f4217cda078f26899e206d1eddcd75e8533c2c127b7afa1d6e461c1c491b8ed35d15c99b4dd954208123770521d66bdb1ecfb42d0327b150663881cd5c3b9478d2dc441f093ef4f53d34127d509c1daa416b15fb3c7d88725d3bd5994556de2e977afe7c989590ccf40cea5ad98579ebfacafb58e6fe304eedb90282047546960ba44ad755854c91326f30ee28782f783218942aabd8d68883be1280fd0aa5845fa0028b89c0467d86255b5438422a9d6643db43854ddf63a7784c3e63e7efdcd7fe63587a8fc9a6ccdcdf86fe6d5b612561ab2c8c279228457f06e05b771bc3c9aa251c1209c07504c500df567082d05fcaddff65080e1445ca4911156e7a1f38d8c510d355813203c596ac7ec80c20e6cecb4d371b7261db1068d4058cd733e8e5830f6d263e393177529a6deb3030ab5f174842fa4a24d8b29d515f76f76a8c356db9ec73533dd8e716f6ded3e43d25b70d1bd36ab1495fa2a169a4a4f85857ab88f844808e3054b5c3cdb2e4761564703c126184f78fc1ef9bd2a07da5358c31634d8b0472f226ff9d40c9204655ba310a47650414ec78012d653e2233aa7d82104107971811940fc70d50dff601296da58188d197f3acf5f9b6aa2017bb4155077a515b3b07b146d76178ff0a2158a9f9f46b3aa542e03fe4a14811a54f64f5946fd490227a59f693710ab0c0ab033ed8ddc442d3750c980ddd6500dc05e93de89c85c28fce54c0e6610b64ccddf229fa53536765c8103201196d89ae494348668464cbabe83993e6bbba1254e33d6c071c7220444e0745c13a28aa86bd7787da41590df606aa5a2781e07243047a4742b967a28cb73f0fb782718d3558acf7b91fb5b81f1531926a50e0c9b4f8ab2aaad11c5a5b1d56711b843c9f58cff8b5196fae446f55e430da1c78e027bb5907dc3c5be4309884985d192be3ca6c8cfba63089f5f24071f643c3f5c51530a043693f2a749af0a7abef2a23571374583411bbd54e94d1869682bd1e51891220cdee9ecaf40f6dd4dccd781b02a40ab041323707b594b0f7ae8a794ce0e48d8acd63826ce34b0e16025a314ccf6be072007a294943b5ef281a0b9c7e3bf71565c0a5ecbdbbc05323cce48e5789e56795fb41213cecf9165ce0eab871f618b46e3c87f9ebc3df7c049cdb18a89f744bae2835eb3ba4a381a9fd1ebe48586baf5a6082385afa130944dda0949edb84297abca59884e9ed260ca038002c7d464aafd60633f44641218387fa7d30a0d09e780d70a42af028b0fe18e2be708c1a2c998a42a54625d2f01f7853f04ae9286193f86a4091fe687e0942e2064869d58e9c83bf7692537341331bab922899eed6d60cc1c0bb30523b4c57fdf21f87f7058d645f55082ff74836d17a8a3a6eedc1bac50e61856e76c792c46972602edc3b1cc7b6db6d4809c068788b657b18f2e6f20abc43dfd33a8911b8683d3436bd35395c5e82761b5bf41cc1111c71b49e8b28c4d1c10d765e522a088c2ab539d7f480a9d92ca4f5e8ba434928bc8cb28b7d74d9a76c9c6681dafec746b22e28f364ab55cdaae040c6024ea95155d75d19af42635280b41e3ddd7c529e4c804778a1cb68a90c8152b832714e09805c320c3129a4f55c57bc4b27a873c0cf1ef3ca79055455ac3177a768cf2280b49ebedab3eb320c2fdc683de501541db39b55a9bb8f8ad6831ad899a2daf11914c74b1ed8ebbcbb63283ae218746a6d5321dfe4e9ae0f4035b9ea711b7dfd53a7533d8a9d0fb4f206b271cab01e3a8da7f6350a1334eeab066ac635cde4238f1390296dadbb265a4f4701b8c86e22f9401f28eeb6d12a3abbc884c01c9cb57465f78212ad07dd2a2a58f4e9cc9b0066859486d5f94011d6df15fa37f14eb41e40ca5d8207af814512893ae0895bf1d73cb5c79914bbfdd1e1ab198854cd20b5e73b93a0e24747c215459887bf047f927001f4b91950389a8d1e14637d1725953ead87535509e09100b41e5a248c4b140a287fee81c2dc9f41ac82ce59ef9effee294ba5e3cdc9f2f5b067c31b9070605ed483192cfad4815ec9f0f67d17af59600296b8c453ddaec40b3178615f15e1f16d468ab79b37c89ac538aac90956e8a7b4d6583a5884cf653f4af5c11d35cd9376966cac448e6cb5ff05c7bd156efd1c7a2da1b3b8ce8a8cbf47f46e1c4ba87c44222abc589e59fd9af296035484a0a216f5bc0061bc2898f7d07a9c4e5728441f2c4692f08674015d11aadc961b9cccc5854d20639c0fd9a124714531bc823b892d893241ea528f104556d50d153d635f12777e683d65a38bf5e1d600854825832821ae31ae55dbeb5bd8229e648a3eaee8ee3ce45dcb12a4bad7c3f60d57ef8fdd94470785fe9001b590805d60a01fab02aa35f8160c56c13945a0bfd9f5f20247a1a55c525f938eb998b4f81204aa31402a6ba1bb2a004816682139d3ff019ca8ac2319340761ea85bbf5b8c67bd33b37fd035f18b27c31ecc9e7c8445971441a204b6170894b5a5ee2b6816c2988ceca6d47aafe8aa4cebc406328b208f1e3a9642d4acb4e2d002f9883c773f347e28ca722a071db42058a029d6e6a8524fbb2c48c8e0c7b2554a49f49f4945bfdc4406f38218ec89c5e694c5d9d6ceb41813f36d53e2472ac8399dcb99d8f164644c4c7210f8190a05a40c918454f7cd67c51450e6629aca39e5503a41685debf1c956a8f07134459a3a62ef060a6f4eda3646956359ec7cf41e7e2fc5321a9596fd7992e8fb72c5c051770fb554525d572e9f3d27f67c9103490a4ffc7409ae720e5d46ccafe6b34867506e3c0eda076827a6b39a86c21f64fddab21e1ce6271cc5327eb67954c6f617e2a3172fcde18ada22721e45eb6afbb1a95bdf67ae6710b5a5be53df6d6531e7bdc92943f523655d156d155903db7218342042b608c16e325538e5b4534e22f57c82c66f880bcd37c2c5c334981f41be128ef696838146fd3ada2a8d5b08687ff51d17ac61f21a3ff4d991613dbf945080b5211ff0752c4c7019926e15c51ecfd0a219b91880aee10abb221e1c25201866716b0d0c9b1b7b3f114a990922e03046feb762aa8ba43d39555272e586cce660d1b255e40af60fb32371ff52937944da7f3d808af74be128c55102218c6463de71351f600ab87b53714278691a0189d948a684c32f16ccbbf8cf310487fad422a4b621bf0a8e11590f842d85aaf4afaa419f606ee33f17ff6e21c1d9c963f2ad32604a2fa9369cbac897a17b6a615c864f851b2340e114940aff28a1b48df5bf18bc8c570ac6710874832916e928d271b1b90928f4c441a9a6928d34c0bc50ef1c735795e43e4d9ffc09b78010cac8455bc17e163a8fb215f67f93452604d9c12b87103298af65612a23d5a1b6ac52f8a5e084621f1291785952fb1d95843fb877ab0b3cb5f9d08c6cb9dd64ac52ffb46946f0312ad6f80ed94c45fe40395462becbf1f32189179086c6eb1d014da46adf2606a551bb57c0d870e0752c0a995b8e3164556fc6c3a688740a3494448fc76a50435a6c7ee5caffa58d158b94d9d033e26ead00f02ecc5e6be17c2170c6bef54a226d35998b6f809162f33ff655c6e3b366b548bec8765ca790a1c3a4aa4f81612c09948ba2b63ecffc7d4b17a4a865f5e2b63c8d70dc757c9b9ee3702381608958a81cc0e4e4cc32c9eee876d12e8a8aa5cddd752744b1cbd96d657a222891a14c401c254a66f6a72bc88028ed9acce3f369ec171a6ba4ecd2eae11529bce192136dc228b6172ccd829711a526d0943c0a38b93149f67ba7acca6b1012d3bb04149bcbee31e719664ecc83ec634823bfc237336e409e80945c9596b4d6074f3ee48cc6b6e3f61f7d47dc35fce658faa4c6eba62861481eaacf08ed68c05e18798c36f6e4aed3930b834749d4331ce18b799a447f123eeba90f175b0d6e5384b60e8a118ecfd6b550f14d02658634e9e83d578a9b7304528f7ea8c9bdfa32e33a73c2c6d79a560da078d453a8e1c5490a4920fe537cfd0b339fd5b979a86521ca5fb4f772c97437699c554b00e114b543fde856991be64176619d46092db10ee8378c94e10d906a9442c2b8d3082408189eeba71033d9720ac44fdda38f2d9a62c4b15476f538fc2acc06c8875fff3a1857ba44b8475d849c12348c6d220ee2a0523dcaaaf42ddcc316fb41cb683d751dfee28825e142e2398c3181f12da05e87f8652d5b3d8a3d85dad4baa5e1cff61056c0efabf1bba0128f9ca79821fd0ae8f6b9f056c929079a05538e39fb8195691d4b6891726103cfa4e81690bceb0046c3ad8cded88f428f3e352b765093b62b3641e6ebbcbf006cf39a6b4cf8decd17850520fec177c8d562f60f80e95c629d3838ca299a141144440ec0aeda47229efb5928cf3f0f9ea9a99c2f0d7a5e7bbc348309a644d00df31f28c9b0a69734313f51e9f59d39978a5befafa17626c7bcf1fde6a27f45f82b8d9a6d40d5ae58f7293d1d42fefd3aac0cf8f37584d920936f88400be852ba25271a1f9db78c2405601766fda5f150fa254fae680f0ab29101b4f892ec3a4b5a4ea339fdf63514693f51a0d83cd60021dff87c3d09406fd864c8732e04f03551257e2127b42190b873a9f2f1f490c96c65b8846933d4b24768078b20724fc01f62d5d888e29d09f3c7a0a1447184c9e57420eb05311c305230d36125a4bc93e1e07e98286a9e97ab74671bac2fe3a8a4709021a6e1d82207ae65f13e7543884bc0446bc1602dc7243aa2d5a20a6a86cd77493de89c0b0d36164b5615f792cdc59eda4b766de325d0986400d67c2d43373f7d36e90f14b461ccd2414f5a0d5ff1ff81bb64e7122dfe0ad7b39813d66c27609e0837f5385ddafd346a804630ce953365463ef07624a309b2704e6a461be22f047d81f512d32440a7bcfd1f27c013e992dcb2f457fb746de412fbd32ca8a35ba971dcc4afc5f57da60fac931e889615285efb6efaded541d92ae7721ce0b71c043d20696442b82bb4b31515d53839510b54c7878179fdb071de211792ed3cb44176c5269acb145c8f6bde8b439781e242312b876b8900c73b0f569055208653a61ba7bbdeeccb5fe932e8fa629a26a4bebfd8665b1f4a748d1d3c484c29f227d40ffd7f4a067eaca1a3a2fd18aaafa1990b921026ef253a498e6625fe5f83b7f3b4a7612a0168d580571ed3217bca1fb5046931006c8fd14c45fab4e87840a9aa74ea1aa3b521109a3c5dd7d0069fa3b52b94c344d2a8a2b7ceca1e8212758ead0409b66974de892227621f348a5588b1b1ec7e83dd23a92ab370183c544b93e35f3cb10501c228e6d2a402357b682c6dccdbe220577e5650a8ddb6788f444e2ab72e6e7e066fca7f67ae4c5099ba76ea83bb286dbb7aea0a95016a59ad0d22816fe009973635321ea6a8e0e5e1b5703d65ab00fe8ed5e9499d3a92cbf9a4c66c0d70cdf251fcfbea6aac1d073cc70fd0ce704fc46f7c2b6eab6d432051030fbbfb1f7bf9e6984188c0177d864ae01ebb28789ab59c3a6747f1567e03bdb37fa37f3af62b42df3cb4d8c56fb2540cf94fcd56e7e28b8be0c0929b3c42cf2e7ccfd8a2da178841e82a1e0417a0ac76f9f573a7eb91bf119f0216fb8ddb0479d1631893b1f09aaa5533c477a751e7abded7862eb04e03d4f16c63aedf2952e3494f24fbd40e49781c278e04228ce723319fde3d7165eeba45354700e122c42d83c0534492f1fa7f506a1fc4de0404aec3ad87c6ccc1f8c8afc7a83cef15691b11e55728a687b1bc45dedb4c3f2c219d158662d822d30372a60b9954a2ea66169906ddd4393cd7791692aee9694adec486589fb41bb0316ccb8005e7e9ff1e818219d2a25332eb60c18106fb125a2490d248d64a3f416d59c72b1f93f0a257dc3d4852fcc74bdef6df2919c6a09639e1d6252c7feac3c5b1a969b950db2075c2c5377b0231642dea1ae57853f0c8a0863164632f01a729a1bfd6b7158709324a20c8392451d1828e635e9605ccd5e1478def5c803be80a782c04a06704a9419f018c0368e24d55ba47671ca84249f5b7c299290174a2c121f38a9f8d6f600789d60d54a54a7d88f381de9c9189e80bb0f0e8476b886bfb71fee32c7c8c9baf339b62a8210eed4f4aad4d2febe6f6a4014de3bfc4a33facc4cb22c23160800b024dab628165e9121c34c43849f289e06ba04e9b668bb266984de9caec4fd2d21b018322298e331572dff52fc2530065e3cec618b592018c497a6553f6f4720012b47c6767a825d5d7acd9dae612c933af0b6710ad3a75e6d094d0d9a4a79a6d66da1df253a12bac6d2eb2a016147951c515f93bbb189b202c12aa4a03236e48fb36619fd32e9b626394e1b5b854dbebb0123f0de2a5e23db7232ca134dee5f098035c8bd22d8face40617c13a1831aa3fc051c8c9325b47a9615e9d90c9a668f7843bb9705df379638ce5ee1c0b4ef7493f605f5e0e75baa4449261af9e28199c34ae08dd9095a4c2050520d7ab548674f622dc289d9c401105047f93c834e3daf2a9f138baf8a742ebfeb9bd6b0a6351dd30a32491f640372d2e94778ffd0e6f371024673aadc89bfda8a4d4af7eae95dc9bbcb978a72add8848cc4ce69d12216a9d6a278d766553e26205f30bbc279bfeb6db47dac701e63950c405c3414ed7d4ea99252fd76019a9382d592a6f395ebf34b7aac1e3f8b0637b8f8fb6408f98391381d5c63a7cd7b62b4ebb4a60590165399763d3c18406c37118d542c4f43dac84d914a836c9cfd54252e9446fe7a45e07e7be5a6d282283832a3e28ac17d4bf40b91957080cd29625c9f97285e092e4b12c45b7e03cb16855a5295e3f53118e0420aa70c3491e9dd84c502165b5cb74575889ae41ecd5e22a7ff7f64035bef81c6fbef65af2b225ad26cfff023b44734b8b555d305bb52928095a96fbfb36e773b88aba60a12ad97a033a354cfa539ca5f72789527a61855ef6ae16f39b3e115af5a4f42cac1ad92ab1c607da835ae92d59f7cd2738bac072b7dc6c623643ec22193aed36ac3e31adff28f03b8b2055a000fd6d52037f381eb6b598a7a10271c72f139c0fac8843f5991de400f93a05bb0d754e2d765768a7b275fbcc28da46f9fa21df7b5afc5d1da2a09e40b6280ab2947fb3369d49b7c6067c5ed2779dfd1fe8fea40d10564cf7a2f4a4a18dd762ad98e68b0850ddc2c9ff1bec1b535486c22e4f3993883e43e2408f6c014fe7fd08d5812827016b273c969845a815464f2435c040f2fb152ca907f8c73db55e6de6f14690957e4bc72a944b44e76a78e130884bcb3d4713217d518fbf8ead894544af1bc60523926cbae4dc6e5aeaff860717a3012cd343f145331b271ce9f9f6aee6860a2c00f2f13dcbd4b6fb61d740ff74f3ad68ad0b9989c699015755c3031d2cd865aabe812da9cf60a30d914095d566e8beee9f4ced8ca2b4d2518368919a4ce22be30c31087ed19e2ac6044b3fdf96d06a9a275807141b7d70a2c62bb29d257f9ebdd49a8430abd9b637721fcd20f7bcbbc5e234268a99b54331f2dcc6977950b7d66c62c92cf9e55e2ce052930448262bb16c5b3dfda15a198d3b19aaf19721e489acfc960d2ce58c693ddc8965e66ef7b801c5bf8222fbf3a29d325be866c937ca0f32d642ecf3fa4fa98c06248ce98a8695a55f89c8ac9537461ef74d60f722f446cde45f7909628af70d47e8a6a0c7b5e9b088573fc8fa7264934f75ccfffa6fd27094d0c2992d06ccbb7962304ca4204517c209b9e0121565e68c2336e51b318cf69a9225597457196c4a554b2320a774b24760697459e2c24febb0fa28c37ab0dd3ac65cdd7323151a3c6465dc35d130085651d8378fb1bf9868079ce2ab6f20da621a0a2d43ec8fbc54a0613358ab6e4e73383e120444288677c90c7cf655ddcab2dc29e36783cc32b8131e1a08fdc53a6c7f8cb323da8f8d2601b516bc218c681f3d3f5d5a3d195611d9943e8b6ca605d8b43d6dc387aadfd9cc2822d774d2c866bf3ea673dbacdc0d9b9d1f8bf8ea1ce69a70f4bf28ba110a5ef4aa7401a159c24b74349da136183236ce5becd515c9cd55095102c6bbb6af3b69f7e639d7eec9de0871a5b6c1b08d2e17e9be5a0c19e57978990cdd91ac12044a0fb7e75d912a48f2a309d8dd3ece5ea83ad9ed2cc5c56999c8f4dfec34d91292de3825cc65269b97f1066dd2a53902612e532f98a9d11bbd50a1b1ed13946cab887f7c54dfa5e8adf2eee5005336a9321c42709c68667d2c6c284929bf2f0dae7c2341cc058e488575d1c0d87a455ecdf1e7631c4efa47970ae9ba8136b31fe729b6bc3ca8460b6ca23120d5e7a9e4ca00eff1a5c3d50d93eb7c3975c194955a347bb84b70413c5cf852ea598966ade81e5febf3518cca27d5c33aaa4d243c4a19ea4eb3c0d957ba7367c04a6772695fa3f693fe489a3a752f7dae978857e4c64a1ef3eee96b586663246106a93048a209c61a76ae7cbdef2379a85bc730ccbfbf6a739ec4b204a41c66cb3b96dffb04cb2f3f735dfdad900da63f28ca20ee18771272e50f3a4f808c223a793b1d77a5ae2daa6485d7f0072af67327f42ae8044454bf70a4f3156e15eae82a0621df60d1184aefc46ea7c6d1334b94479b93833fdf0b7ae0fe82ac46685452a8028ca53910f1e98683068db164d4d8dc28bc46b9b4b6ed1ebf37b5400ef47059e04bc80e09691e7b44ac857e47709cca0cded996d920765c4fd735c17105d115e7a1d52fc8e820eb4b82e6bc23d7124c67868e805d5c002addb07675c03a39fb7d4e989f93d6789ed2322a22aea95c8ddfa1ced13e9e70c1652fa145d814b13b5c2b523b359f40aaf0f1520015df887f025d74c1b2de68e390d49276bb6c81657ad0862063730b05dd7041f794fc71ab70eb894fbe2fe77ea624bf5a8510a9b7d0c3dd6a3653a07490055a7ae2bc5a37127542fc314d837aaefb5de56cd2bf4cc4806d2e0095d2eb01e2f46d6d20e3cad42029316b6c16130f2c99899df1d282d63df7596bd04d44319c2bf62c23a793ebcaac6fdce7d481d83817984be2715f7930ba9b27f48636d062cc868ec1fb3c9864f06168c504b3b49da74f248b700138492bf01e579033bf8eb24eff6595a5f1c1f0961409013ef425792da8788f0c02112770352d70387eeab7f35fcc4926c40355faf952d2b25e050ea4a8a5d82f932e8292b4aed244a048a042c83831a5f408466d2420e7b00e848a125389061d447a0c4b86a7582414486ad819f83d51c84881f03990782891458020f29b257c44a03f55b66942baf74c631d80ddc3dfa10285ceda9b095b0e3ee533a84d30a8160c8a71b4e6ccb7a449cc05159051a0a71f207b587e029b58015f31816d29181b58e84d40cb649bcd28778fcffcd84a3ce9c8f6755aa69431d7a4eda6e11a962da696d6d44667afb4d177504f37d7cfe538875c1e37ec2d501b54d930f4ac3e9bcb53035a9d9395995df04eac513fada1849d7f673dbf9c0789937f7d335c965a121ea68a0fd4acbab9e4acdc8fd7ff5dedc4cab0c51a90ea15dea32d25037a4349a0c042ab72d7e4a26c7791ade5fdf27c86968c212d1ad2729376642a407205e613ec32eef409d15f50fc77c564d64b5426e65cc74f2d60d08eb9c892ddaf4181893c250e422dc7afd996c59312e87d92c1602df110b292d880b2f0be69ee9f2be5dd258ea25284f1ac32ca2c78b4a98a58de42ba6d7cecabbe3ff0fba1d7294558ce75b1fe4dcac98a13cb4fdbca461884378fbd03090c94b409c46aadeeb485dd2541528cb920292f0da43810c2987e82927b1595871592330846b54cf20f043ff9bf73a4d694f351a8945547bb68102db5df2be729b46628733cb6c242f9eac671d6f524cecdeaa7315f1e17e79383bcd1d4d6f036247729e3d7e175e1afd94779832119587f825ab621f574f24ead8ae8a5f248d348a5a5749d39cc3eddd502d6705796841983aba419cfeba9bbca991440464440259bbd413dc6cbff10db90e44f29c48ae6d64a8cd6c7ec9b0caf534cbf9d50088e4f61edc8ab18c013022b0429fb771d8775a49b3d0450e1c7ee7896e10eb9a9253fc5edb31eda2fe3d8d069a7e2b88abcf45f14e4198c3135e745a78b687370abb4bca94ed17ce0b37bce7116b7e0659f2de16d0f51d30c97911b8bbfe2bcce45fc78ef4c4cc48369773cbb9adb7a79b15acdc7c5adcdd28b416981f3cf6cc677d6e9f3c0692c1c7ceab86c8aaf443bbcb56f438aa0a7d9b2e6dd498a3be2b07f7cd050bb246b5acaa03c2ae85d55c1e1f37345b9e16b4afb20a696240670f3b81f3853a17886e4adcc86912a90cf3b17b6d9a71826b15502645851f488e6d56ba750308bb07002d9ec35554e9fbe5bef4ecd2c5442ff336aa782a329b5c962279322310f8dfc6dbe0b41a1ee16535af4347d6940def2f4f7ba9bb3cd5fe3080354ed3a9d368184b1b84ab86f8edd6f6a17ac39e20a84520154e33413ec460e5528106927a7e6f5f920d09039515601bf2df24bf98f5d95d9ba7911430750ff32b4f8b3dce6e63bd0cf9c66b9e3df251f0a6cc842a96f4550bd558349a505721b2a5bca54ce284c22dc5d4b81db1c88e3cdc44eab583ac25f32f58d929b94e47c1e8e225994878498a7a417d25f4bc6349627254a0892dde21e5533b016b4921add78fef4e7807562356b841d5b58b32c5362ea3e9dc0e8f47faadf8580d38a866a954bed45be233cad9bd845407f026974e9bfa7a5bb05f054d33800ed29762fe0966be6b13750b15b344e39a45c1945934da42033814e1adeb2ed19e1cce828d66c932ee034ac0265b8d34aca32f9332d9516dabe3a2b5d3e15eb1e9e4262791d99786922ab8680386bdaec0ef62ae376856b501baa52c4c70a4f51df056d4b6043eca77deabbcd0bae3ec1e557035a0ed55554611f7760bed0417f927071fced304173bb436b5d3ce61789ba951ac3150adb632e0493a39203d7d0d1c32a2ac9b395c0db5f5f36f8fafe0faf6daa1e4f969eda1bb610a83d304c9b8ad33cbc6094b8bd4d6ce1fd5f9bf0c6d3bd5be95b8419efc5c9d327f7f0d0b6f25ef41153c6e1ba8a5eb632cf84e7f8aae5341715654d3af81eae480c42682baff0180bb5511137b526061ed694fd4bf8c7f107510b7cb5d5d996f21e4614762dd0ee0321e62b6b7f6c7eccaa6744c02afaf6f7917a4019ede24252cbba8e057d641da9f1b0d5d2ea90ce826e4c98755154c137439140afe2b3b71ace8b18468fae401bb09381e4bfe69093c49565a523b98362a1af9c307e77d5061a8c73d8a3b3c67b8c4379672dd35a39b3f29369d826a2b3030c042d8a682c75c7d33635a242b164eaa49568cd813310cf47a57c2fa8b6875adf30174fa7212b9ea5ad9bb0d837266e2d0578a6f3b178caf4813700d9166b7a7b0e03ecdb2dd1b0f29189ce4121e2682a76f1caf9a56c496ff10bb3b4b45598a5adc66d6aaf962cf91f15e8e90410a6613718445f9c98c88fda311c38a34e8e24cd1dddddfa38b72155a8aa06f44c2444f3f715f42ce784b48ec288205be3913af4ece5bc9cd88a439ed470a245f48ecb191a31770b834ad6825d971d4ce7a3fbbc87a6130c91057e12c305864bac6777f8f5a210cb19ac66caa36d07e8f137090b814420fef8effa9d8a267505fa84c4df7f740152fcaee0f74156a97a70803624416e0601a71e3ff42f3933801f26bde4508b056394b8d145972625adcdfd32f925eb07812ee53d4836e44634f6321071e5a9600de3b5969f873b07f2dd0e36965f5e4ed195789534f2237f4129878d7b0a1e570329e20bf5659033d6fafe5cc4e43e7f296600cd220567633bb8104dafd2dc242100fd6cb277aa21fa7e1b17fdf0c21e7ce300e090ed7514a5bdda75d489a0b52f8b1c7b4b319f13f024ea6ab6b374a16994f54c0def0a2842ff02cdc3b12ae8cf24652465436730b68bf8f19ad86ab8df4b307a15423f15a39818af832ee0adeaf4aca35907ac1770440b36fd0cb59d460d3911473caf24c25e32a5f9e9786244a234d90e14ac45086b9c21d9fbc916918a2f0b0d60fa8a40e204fe3c9ae50ec1a29d1e040730e21460b21017a31b4c98ba7694bb51897d8e0d9e4440ff246328eea284991ef31d4b9c69efd157f0e28b1d02a49dd72d6027ebdc745fdc6147c66cc8a1acc585aca94a10f718c51185d4783f8aded52b58c2469993ed461fd3b6d2db80424a8d64f68395245b3c2d0a6ccf85c8163e747ea823e35fe9b3ab38a8b1bcb8a616386715bbdbc306606858dddddbd755b9ea47e0e99ba5a3f36c7355d2e165b572bece21292afe8c6f41aa670ec18d78cfca04ed2b203e1542ca02a09727fb51b900b7c5d03f41fc35e3a43007da0f2627a52f4d23035e10e055a843c716ad2ebe1b75eb156c836ec8629339150aad320c6940864be2c2db505a3513983104ab7b537c678fa9a8acfd795d2f44c1899c25b11479effc5eeb4ccbb5e92fb9b83769c6c3c7b7750195e825186d2f43a23f9b5db8b74b2b30d6387a9252696780ba3f7788050045b39fffa9e72336e70966bf5338ebb97290b908d7098e29b32e9958a1dbf40797ae94a1b7f1a705181de921f0e2e4f2fed6297466a66a9bc5ced1938a4ee25c9b0264fcb3542c1632c04fd0720f01ed73aaf8145143f6bb4e615d2dbfc8a08bc8cc7bb7964648a45358a797cc59049a612516a321a70fb61659585ee2d90fbf9dba93da824c37affed964806777e12a9d8fadb1cc6876628081b5f972b5559d831738f713f7ada51e86d7bf3916dc9033750f31d4c6ce5118f6a5908bbdc10c7fef4f67445b2c97756bf793c0078d89dc698554a70afd0510e2ab5019a3b4205253ae8322a35de0ddc32c60dde5c1663b3f14de4694627745bb5a4857736d881fa5db9c58dc7dd2cc29d05d17ffeebb968fde639df6ae9465041357843858a76ce6c26fce7e15259c65fcb86c4b90eeb06fdeeecc98f2e7700ccacadf47217030008bbe0e57e4dd3e8b7f7acd66bcaace611b82b3d78f1ec3d2975e2d7c149c93ed2705f6b2f231080edfe8755f06e148db790ebfe82e504555f4e90af55677fd7226fecfaaf96471996367958f722cb65508666d72ae9b41eac95b502f1815957d11e7e3d6023e5e02e28026ffbd7ec507bfb75692d96333f5fd5f4f6b1d6c65c0eabd324a682919acc669562e26d1fae8e9d58597b0e29b425df7180e85362737991fec6cea64ec1def2e97b9b667b8d152d2fe457581a8d798abf9beb9da2e5d628cef0886afa9ed9e943aeea395654cd5062424c4b336488ca9f6ebcca645b7fa1479aae523ee252156efe07ece2e6b4e636e804a42f1e2646973349caf2adca9299c93cf87e14a43c99f00650f38d6e325dda5d1a5dfc7b7d11cd1ceded78080e8d6d5ec0b36d59a7252c9ce7f2886988a603ff4071f46a4cf8f2bc0c4d3102f1fbc0a20d27f935a5690b403c574eb8d9e298f1043255a0294ac0fce71592ed0e5bfda3d09f0823b6c1b470ab2b25e499e539c4224c0224e653d10b230be1c465002c94f88a730310be0d06924e86af7d53035034be4d432c1a1ec8af4d4a9c4696ded98b263df0fcf140c334aa6b6db955613266cabac04608f18fe21893f92a5b2b587021841ded5df3342ed38939d55a310213d5ec9f0b7e3588a6b7327c0b12672d22817a7d929474f67e2d451eef2aab1429651be0a6c02d98169651d187ad3c8630f8e9233d962a6c225bd8bd191aa4726b5090e83115d3b4026ad361a9a22b33641fd487a2876a67c61f44a16eac81d578a661c53b23c02de806050c10347d579a61648dfe9a73b6f3588e8ba87fddc40ecc472de85e66b4e982f620b646131f16fb13a308d34d98e5c2a45c41c0f9ebdcb15ba099e73458532616f6f9f559891925e671ce011523ce0eb26ce5d5a0c76708f6df30a765c203a3bff683c3ead84a14333467309aca330a677f60e89e9466943a58ba6a8c55feb68ebd4ef4cf610e99332f1173135630685d09d01597e3d6723bc9f595e4de3a3b44494addf4d266242eb5b7242d010ba3c11a6394f731595f7221bb11a168446514b64a76b9acb7136d851cf760465ee6f97325145877db05820c6388939b1ec0b651a63617db06dc259e3cb430bafc2c91df21f3e7fecab29d78a8ee5acdd931575082784c217e2ea01481b063856de5dbac052ed20a9ef68589a0604c80a38161464ac5f9e2bb6650bb36e46f643375c6573286b538206edc1992a15e1a4bd59ca73852830cae947656426891ce8e2dfaea90cb3b84fe7615e2d03f925204d594607bba2931e54437484947027935850b1c6a92faabc4036a82c8afb038891013a40c337628ef12acd4795a4dffb55979eb49c405f5bd861b6768889d6fe033c935e017bdfc9b0b7c840d9a2ea451096f27497bab5c7e9d6fc2f37f2c68fb9919d9d6e9a92a17e4d45544c2c221195cbb2bf5d6665666aa90eb4761c4cbc608ea587a41049e7290f7db67873bc7c100e57cea881582491781727c57d44bbf23279a4d529b7ebbfa6aa902d6cef28e1c8c3007459eeece6944f3ab21b3b90d6998d8dab879cb513c0d58f324faba3fdbf94cb5443f58ba6ddfd5aecbab634210e6f0392304a4e16885541114fa86cce0d30a6850238977e496ce59fc4b05c3e3b5fe335ea5ad72b5ec57a59afb1c0b392d841332235ece51c69f824863d99836a013bc15d52b642eb987ef5273fae6ffe8d74ad1f81d83f76b8b2e5c3f20a69983a002d96ac97cdd9ac033079e0af59c5a2a40b05abe4048def77caee86a5157404a1233546e4890604a28328c85131d34f1f183f22e04c1e2ec3a907aa1af526d34a8eb41787627fb225224e0bd1d6a2e9d3bc0ff7143903362ca61774ae739a1fbc2f087accb9fd47eb6734e86909c1c7f483d4753620305e8e8486435f51e163a95921039ba0dcaea74b4ef1e74dc64b4fd2d4bb8fd8a5327737d86a4720c8c3d1afceadb02e58d408dbb33cd0629a0a876ae2ae4e2778c7bd27a555bfc48e7e5ad16b8c06e239340865c01e73443e4d5613e70da842762d7bb1b328fa6e75a76288b3ce955dede62e9e77e9e728fa1751d7a76ad41b87c14b8a796d3f5fabbea0367aeaa2e23a4003a02125e655cb26c01c68f15d391ccf84746cf155eb4d8054575465c07baacf011fb78d36f00dd34131489962f23fa560e5d1addf3fac0dc50e65ad0e9fb06ab4783401eb42224201e2e5b363e804ee0d78a76ba3a3922ee95e2a047390e48cc76ac28f383cb5e396071d3a8482d6ffb15cb76d8a796d6955e583272af68b75965d53f39e89cea3f8e36724cbf2e5934fa0015791df495ea721efa063f20d56dcb9d903e06a4c76e74d0b81f88fedbcf70d4c924263328f1371a0acfefdcb3f1b7b5bc7fecd6d886b7d6fa47c7cc3b35080178f52de466b29967a848523c9716ee22bb3687ec21992271d39a28b495ec4a75bdd17b9a2c1de3168b19a81664a3c7e6c893db373a6646dc32ee97194774d5dea248c8801a98e9700e5ad6423f8abcda636ce35afad8172bbad9941ed852df61afcd1a8d8d7055be00158d46963a8e3f3bccf43412528afc1d5836cdea7d9894943ae80aea65a098d8ea98666744972ebbb01ce04a0042660bd600910d271e2b427b6332fc8340c4408191ee6ecdee6ef29c6f72543b3a7b036926120804d331dc157427e66a4342fe3b90e0a12ece8262f1576194d4465cd10b58027c27325a97669a08b937a2112a65ac486a6628161c238b6d260c4218d68b2d127f8464ed401a90ffe7b672ff6d77bda74fd8c8570d69b4505c4a125240d71c8f48a8c865be67f4938a79e0c4b674933bcc48cfd4b4760d3a7de1bc074c5df4a16094dd54b26b5a362ab11345353014af4525d307ae041e853923612a8adeafc45de0c68658d685a8bebdd6da1ec28e471cb10dcbaf7a6c376faa12b1a5c47245cc040fb528cd8d64deef79096205bc25bd355dd41a3ea320370a714bf7b7065750e64d059135c0b0ff03179936a50b3744ba99134ee85e33eb5f76b8502d714b04094a2f3f6195dcb586a0a385eeae48bf91b4ef4286833812eff4a6b10d6556536f2cdd4c53d59a2d160d945c940cfe1903a80d0d63a12650fe7ea156534512af69958d24abc17d1144d514b72bc3a91f7c29beb5a86bc51619fda1abaf1f6cc9bac1eb604155cafb7af1001a81481fc2716803e1014a94ab4f050aae6da9bc7f0611836d6c6eb89955fff8baccc5d6e5192d56d824c9e9da523021766c3dae05cf6a87afcd3256cb6f6c5257873d1658494042d827989a74e23e88ed60953ee45e7f2ff96a41f50b485a3a3c446742139270ac03cba47bd1097e208d16b19ed4519f0518b9825fb7e9c2ac392e7a8bed38457aee80bb7886f88782e384a1d5732df4e8b72b5b017bee1c970fdc04e33bddaabaffb3bda73acc5dda3ccaa032ee68989b6f62332830e014df92e12b46e6876b9a7d12ae55f7a253d90a3d20dfcf4e438f291ef25d9d8952b4bd28049519ec23366aa293b4d0caf89973626cc9156e7ee48a2d52808484733bd5eda3d43544b3965420a2e1445748548b88ec11cab4277b4d711263da676b1e2b700c8d5dccaedb2b583f843433899985e781654b5d0f2c532f51c8801e616d72c82757c5089a12a9042372b5c64d3c1d981b07a8780359bbe0f96e51af28c814e54bc4c7345c1c97a89f6dd25e2909b0f7bf396580e69577015324108ce482901a4af86a7382d919008bf8d5c4886ed6ca88680336e00977889b2fd80fac7448863faf9bc431dfc7397bd5ccf0020af331645c4050424a074ebbcd882aad8fab7ff3271dfcfd1539572a6a5fc5e3fca760bd70628d3d2574cfb39579da6684bba974b6bb407f20d5cdd8fa235f84b614cc70d234f34a33e26eac4082bc3cea7d8d4ac8269290bd25d9726f296592294e063906f305a1932219cb1dea70460732b9a9c81dea90c4579ea33efd1a39d571da71688753af24e9b14cee69dbb918c2262e7820bbe93c6c1284bbaec615468e9408253b88710176bbbbba57cb40906599931c9ac8c1cb980cde9f546015c34c2083df2e4a91310cc3304e867bca1d3241ca7315320999f87084ad8ec33dc618a3c718330b05479425c9fe59fff6c9d5420e792ec933ab15bb688d33694440031a982162053b530726552293a27c73874c84bc5810cac4445830ea37e2e9e9e9776cc2f7f83e2ee8fbba8e2bd275452e2d99eb40c6b560331d22f0040c412c29aa82648b1c72b021871d9ef59fe5f0012a4c391cc121860cfe77e6bb5d4d9365666aad9b0c049821080b26bc00d102144b965cb164cc6949134ba8683d2b8c1e6e48e290844b82421c94704492860abc60065374581ac2923471f0215cf203d7028724bc818bf0062c382317c422644382942d1b4c52ee5ce215ae97bde3f4e7ffb08089c42c39bec4442e6fc597f32f6cf380b86343c2f5f4ddb18361638ef4dd3f88e315e8fbf4c730ecf46d82207d145f89d39f011448984f3f08fdde48b021813ef6185e613ec5403b5d510852714b1c08f4ffb0b0d39b109b0b076989f33371741c59987212d9f6efe388741d47249ce1288b44f5c70e2202bc745731856dc7e117ae18765d94ce29a53b8679ae97b2c7f5727a3b0e1e7f0cc3ae8bd23965ae3a2e4741d24f0128fb3cc1dfbbea6e56ddcdca5b5406ec767775ef54f202eea29ae9ee4a8916d38b35539454c31819d4a03e4325072331507879d2c50d4561283192d118728e9820091494f9a10a1ea60d45648a5ed045bd8a948ac47461a6f52c2d77a8c48a0cfe77b51047c0a5882cb02417fc768d29610206324818d972874a7ec8e0fd222520e880121a8a844a70c83377a8a428045e843580c943610892a4ce5cf041ecb3afef2d0ff36a996bfa0662fa68c2dee457966511833cd8c76dd23ad3745dca055df77da9167c5f2b8b304998308917210d49a612d91e3b245a0a3f400406b69d688c1019a22c82c8980940ee902809a22b5d27b9e0c22917d3d2b03902767773619b057e2ded314fc360ab55475addb09b4d7b0d6bee616c0acc056b6e8b798dc1564aa1e0d6075b09c32b8a44a316d66218d6dd8d611886610da6c160b83d70476e77f7d69793c2e29a1eec2d13042f56e808a541b2e9311931afe7cb2613bedc9807fbfae057b1f63ad42a839afdde91fd9518f8602b37d66a533a49d031ba4b3927655dd7c6a36550112d5d1557627095634eaa0457c62ba574fbe949f6e69ab4d344b3ee29bbbbe6ee56214ba941f95a5db06bcea03f33d565de4829535dde6fb012585f7a5879fe4581a782e6c9ba9152ca13f25481cfa41886cd97d50acd39db5ff82075c58d3b727767a911e6c69772b666ca0473ca9cd40a6efc54979c9aa5b7cc1c398e13735a0766521d5d6bfa59e7eabbf87990b611ffbaefd4b968da6a47eed7691d434e5a4bdeb0e815b39bdea4c505afbc9fe9bfc8fa2282b79272cc288913b70c6faaa35b6bb6da3e25e682ad568ecc1164545ffe0760e4eb807093a586c1cf3cac6bb867878d5f5b4bc48eecd3564773e4481cfa2a1fee45858eb8aaca55f170c15854e438a260a10faee2d00932c76d69c25de7a98e2e7d107c486acc055b890216701cf695889ce03f8e03ec69e5a882081683fd25538b7d668ef14adba0ef9e27005c653016a98e62d1dce29ee043a30419cd37f23b4f1c07d83377e4e8603dad25c79967dda1356258a696ea123f5e2ea9c7a496524a8a35a5f4468dd6a7ba5c4a23a5988c1785814edbe81d3925b0ace861657aa3c5ea12bf6f6c2a05373e8d31c63ae79c2da094ceeeef9058abb46314a3084e6374778f116c6511bc9e2f471dd789dff46d83c9f1ad96d8d36072bc45717c1def76015642be587d936bf4b072af22a635629a4f8b3d3b6eb79627d6654e966f3f02f17e6f0c6365ec4763b8592d20f2638c11ec72b7a6cf19bbbb654c93260dbe966a5f57f7cb66c1ed9262c18def616c950f76774f39fbe7a4294a29a5292fee840139098d54d2053f67e127eeccff313241014148593e0bd8981dfab553f16bd67196e300eb4d4b7a112956112684814938c4251cb221445214ca30942241cc734a4f0b73e54c188396308624e1109930862774956526214ac2e50e5ff8d27dd77b57ddcd9cb3ca8964ce3993e42954449e4246e439efc460b042517cb879210c1bbe200606173278ff4936c7efaf77e56ff2eef4eadbcfbc483dfb26efbee6f95bd4af36fbd64e9cfde5813703fd0c9138641e0f669272c8f5d9444993e9e9957a308531e89ea569cac79e6690e20bf43323b842a6af232768dfc779b62151287a107fe2066392cb2d883d51b65f9f4aece359fa78c630959867fef634daa7bf7df4c0a88d805dd806fbf8f6c7d3bfb00f45a1361fd7690342df4e14b671c6bfbe6d3ee6f35c18f5da0644cb736210c5235ad9f9cad806400ee4f934538f661b81d03c371c856207f4e9e6e39a7888fd788a5bbc36cc33dffe85a1de71506a31cfcce20891fa0f8b411f6983fdf6f469a41bb639727d648e0ffac86c9f7ef6264ff391f97afa3c169b7e6e40b41caf377d463433f603d36bc5f77169be4f4504ecb8044dd7a191138d04e393607caa22425c09828b2ae848082d61a88a5c2113c416cac106390824d6943f719493c9111b350cc619040eb9860fd95f933a1eb72b61ae80b9e9be39e79cfe4d77a4bff47955465c39e7a40f52ac010e32d2952d5fb2fc55084498c6545200ca1ae0803ac07b2f082785f8fe2a23aebfdb08a5301f9c58031ce4f8f188f577eb1e10d959399f6e3fe83be6f1b791f8877ca00c94e3c6c3e6c70365e901711ef91ecf7ce9ab193d8c1fb4001125836d254cf6ff204816d9bf70652ed84a1650caf18d2427c7827b71662ef82fe57f672982b33833d75fba771d175df77d63be4f8cc66dc19aacd842626d61657f96b6ea1cbd8417435698e922a98a17ecf8df748e57824186212e31bc10c44e0960500504559088f142ab224315214e06b7335823b2ba8e1fbf7aeb4ecd5bf2de2aaba46d32994c2693c964caead7eee84a3a9572a600d4a366d16432994c2693c964a28f45d3d78951c0324f05e9a70094e77716311b300cabd242c6c478d119b92083f76da8d9509655b112841523b6c84c56a664569c5c2b339cac0469475fa872874661b2325ef840a44a0d31e894b22ccb321b188e8a38a2021f15b1bca839418c2d55c60fcffafa956bb2c556c40710843e10715b78d132a29267e40e8d96d84aa085165a6821c1f824189f168e86e568582a70f08646eb9a530e72fd99335c3176d189e35c1557f396572e05373e585f064d01c11129676af822258ba22f538a9722b5d61b3c7728658a2ba84cb1c104b556ec48ad950a95249c972b36c81d4a0142ca940cdec781def096a41f98db7ecd9eb1cb62767777c7315d5782aefb3e2abe8f53818c28989e4317ae4b1747c372342ca75c97245a43286508096ae525ffba1e285bba8d10d3e488a76bd1038b6ddc137766509bb475d4a7d76332040ee42b4f9eeba9e7fa36d3e28cf24429fe71bdbcf09541d86e1c743e9538e694deed817f06e2773b0de84f119cc9f3a50567dc7ef44739b710e80ff9d325be787aab717b4f193d04ddddcd4171bbbb8b889a08863470c78e2951a2e4f01d9f53327823bbe6387caec0ea3de4d3cf93676a8e83cb8d0f85523a5bca298dcc288fe08ef023babb65532417edeeeeee239cc6d8b1bd35c7582e0b9785cbc265e1b26491b59522162e8b129705ab596da42fe2d1186f0246124d7a61156b0733e9855d8dd18bf6984967237d118fa6ec2ffcbd81880d5463d6420825698913e27608652984a2d443181e96b8216e875090c21e96422858be3062ba94abcb59e1ac7056382b9c15ee08ce0a3702ce0a678543e290e08ce08ce08e686f40b78c5205dc12b7df65cb88e294b8fdcd449428210f4547577e7f941d23b7c4f5ef233826ae96baeaee9649b4c6214d7a572ad971487061774b2e64558d3ba2cec072576e63d9a24546914f96c88c9173527a5d18bd2e0cab35bb34acd62c33996c663259ab69db699e4ed2e3c96adab69d4ea8ed244fa83b9422c34d8e931eb92d60be60f12b7ee444671c072772e60b3063b2d8a205293691930e45a11e23e40b9f062324ba4b3927eda24b3927a5d7a4d78561b566a66932498f26ac6699c964ad66b29ab66da7d37642a1ee4da5384eb55ac5882143468dc66952b5f988f95bad34192386263d199b8f986fbce5fe31eae97acb5f725876762818aa54830a05703ef0404307a6f030e4c214a3f0090c9e7041458a9c522217e59a72874f949e8091389d310c4aeb593b9460872932f89f50734fa2f4d8e1681bb258c21d98e078e881dba1ebbecfe90e3a704ce464e268588e8625c5c813233577e80407274a8a38a121c8c90c45b8269c9312ac4fe3311e1fa3dbc667a88f1b90da24633f9320caf6af8dc78c979e8c9fde0903625f6210687af1ede603fbd5576ff5298fc7da473de7d9cce8ed877c2a6d667cf79d4d34dee4d984609fc6dba7816d66e01e40997b9b10babfdffdfd3bff7a55adf53b8ffb1a8f7b191eb87dfd181e4feabbfe0a8356c5792c8fa7fe05bd1c9ecd2df68a6d5288325bcab26651294b59bf37077c1247d901404848da83e297dac9bb9240caf1fb1d5063bef478487c7d03892f6777eb53d2d4c36a95c3b595d0a5a122baf36fa42fc05f333d7dabbdd5b00d09fedb43c03fcb26983dc5b20693331f9975e4254dee35b0ad1ad6a53bbde69cf3b211fb53141840819f73cece87b25c9b7de3c5c436ed5899d618dddd298e1fa56702cb676067f764091de92fc67777afe7cb13bb09acdcb8f6bb57dd8b6d998308f8e90041f57c4f7e17afd83a5a65e5821ffdea43e9940bb8ae8cd7348195793455940b5e0d885b9dce49737cb2494ffc48e78546db3c30276f0f428c31c6b8e90c5dedb5133ee851734cd236b4d7913959f3b44e0a2273745d5297f401089562b0fba718fcb20a945228f33d67c7480275db505db92b6f752aa30b765dd775d98fb20b8b137cbce575870b462227bc7063c640a01c67965ecd71f544e694de8db77c53ed70c14f4a0a919a64faa0c7237fea78ebe3998ffd8fd611b71ef3b1f8818eb7da7174eb8839f2d05a478c2a2637e786d45a470a315f1fb1ea314bcff4d2f4597d1dcfae9c65207b9f2b9bea6758c5f2a7498b524a8d6edd36c053e6f1a36dd0296c2780956bb5947e8c96524a37950e17acd9f3663679529a7890e0c78ff437cfe6c8cd7e7b4b371e1b9632662dbb7ebe8e742981f85006ec006d512a51fd70ebc72b72204d9a3453648a63c29729e6f922188bfa81bd3d79d9df388e6cbb568ea322b194e5db8490bdfc0c4bd0aebedb864ae5ad3c307b0eccbe1265f9ab472292a976b3f1b83ef3b65f695f3f76db0057ef0fa4023969bef1b87ea532bdcab3b9fef49d7bf3917dea3ee7813621d0bf4fff629beb6d4238bde94f6f0a5224ee08751b00ccde1ff51e8f86afb7228f6925b1f80bfbd92a11dcae2b2b4b1ac39ddfd9a909ac1c31cf856fbce55ef194172b6317cb89e83a150c5d07ceac61e0a499eae581126cf4804a20ccf245066788840be1115f204194bb7624d6a2828a683c5454eebc62ee98c5115172874664c907c81d1af142b641456aad547ef350c9bfb08a27063d950b773ec555f5e4061132e41a5dcf1d1a2194ef511adb4effcafe8ad227065733c330393bcfd879fee55d1f35d7681b418cdc0bd47a6ff7ad310d7eb3b7e4e46897def556931660111282cb105fe42f7738441339cb981184c9436867f3870c21ca3caf4a1c9f7875d3f2c0ed0e23e59730447a2b242aa70b1e3d0a16cbd1e4389a1cd511295d4a29a5a391d2d1f874348e4675244a3064946048d964293bab75dcd602ded848c07c70476e659b4b2f7b9347ed066ff2e8471a7f79fe14780e0000d62b379018e8870532d83ca3793ca86721cbb618a6c5d73a8b75bc4cf460fbcb62702e0141bd8641075bc94a29e50cb9a16e5e3ed87363bbc13cd8a332785d3802fdb0a0029d6732e8efe33c35cf42be3c2733bda01805e7d2f5357229fb08358f44e95a31c5577322d873a3c76b9ecaeb6bde5568499cf9f5da3be33402eafad8619beee97bd2cd3f066b663c0718bc1900d806d7e0117ec8bffebac8440fae8f78a933c8c1fb8373e9629ef8358fba68fd1b0cd6d89f81c18b6d300a27c5777933e337b8feb4f990a7dfe4552b00f015ff06db601a333038b3cbf8273ccb681b0f8abb534daedc799db1ebcad1a339d6f824133bb8fe724fbe8e8c353a2d9923263d58e315559f0542cd9ff2403a1f755493e3188933ff8e99919332868c075b8e7b6eb8d71267e5b594382d712a8679e86b2f51b34cf4e04a61f0cbf422b92bf5dc007b6e681f4ad3343ccbc40ee2632a74dd7fab7534658e2a71664d0d9ef8a84ccf8dac3dd87323a3dea43d38afeb756d437b1e140623b601d1b2f62ac81c33e69c37f297af6d96891e68b3cc5cca19866115c3b34c752214e824319252d6f2f51389882858963216242c455892728f6fb6dca8d18fc1f695e2eedeeed976f4284602e58e51656655e54679faf921dc79c5eaa8458001cca8b5076a02b8fe2d716cae5fd170b1546259b7a70d4084e2f8eab0deed79d16b1582fb978a20a594524a293968e1eb58375d6774c1aebb79566b83fbc5306c8f980170737bdad09871838cc09a7d01b63d9c82a8c19b25cdb1714c6f4d6ce3b833cd437edd78ab6a542e78e5bb978198afa717861267fc6f693e0d6a4f809c94524ae7eae84a29a58684bd443a56ad35373a76ec3a19426cad31ee0dbbba3d554d23e66e9051e709902b293786b63de8d801f8df2b2b176c6559e3bb9123af79d11415db23e67b1bb33d62465d1903e70ea5a822f706b6bbb1940f315fbcd55224e5985747eed5e613394ab125c767ddc8f1477ace056b9e370e41e914a07c638c94ba8ced274a371f4057f41b6c0c938e72872438ca3772875280915138ddd6b1db0da588927d3ca380e5146a504ae94529a594528a6917a59d156b6f90d1a48f6d85224d57c61ddb2cc751bf1ff35621b8d9c53ec8885ecfaab55905c83c58ab2917ec3a6fe938763f176c8f982f9b700a22b9c3a4a55c80dc611212f6dec2b0aa92c1fd6e442f0331c79f506a875324c9314c42cabd39a63783361477dec27e662d0719c920a3e8ab1aee751c146904764abffd7dd978838cbcb37d838cfa8dfc09757318cd2d04f9d74b77da330adbac99aade5afdf456462e58796cbbaa4c14495c27378a2c32a8658fc24b8e7fb1bbcb1946e14528459093b1edd845a774298a32cb1dcb0c250c61a68b2b9e0c91e08a113e705992e3b6aabb6d727c5517511071e9810b240ad042e8090a8c4831855096289a6cd105170fe42b0d105d37a6eb5820468c982340342cf1220395263c48c152c4126a0b33a82db20da345105c8acc00850a1210318313135ea86419620c957cf5804318212866e842070f3881a4872758d2644b965cf10418275a10668827c674b922842206288e7cdf97eff3a2b2627e0792c8911ba94852929caaa4289cd0b2e50a298a38f1e589284227aec83377e84415377c1f97efeb3a28bace89ef73a2eb9c68620b91266808910c59010d5f783083948219a6105b88966822dc72255fb9c32d5598f83e2ddfa744d729f17d4a74dd08baeefbb27c1f5250ad352854ca59eeb0892155124b78b1c4900a498b90aa0855124c88800925b007bb2c7eff5dc17efdcc6befc7484866adbf7d1516727a897fe29f3efbed6d84ee15e7572cdf5a97f13164788c3f5f48fc393fbe0a0ba9a9c141627cc43f5f8c978163e01f1ddc12c7b15f61ecebe937cfe601f5ed0ba96ff18f91ea7fecd7576121a63f616fb2f8e7048983bd7f9661c73f910826f28ff8c7f1cf8f9190ec23fea1c0f6a8ef7eefb412f7bd13b1b711babd93fade89bdc3793f4642eca7f04fdcb1affdf556c856301292e1d86485ded9a28dd08d471207c3dea68809af402b5e817e635abe608228dbf6f61314fa3bfd24c41dfa16284e4ccaf7a988e83a3955445c214225c86470864a64112a112654e2cc1cb2ed95763b46f3b3bf40ae375b4dcb0e76f2e377dd4cf433af7a3d5dbedcfffa4a59d2241d0c59317b93510f022077b35a35be2ccaf8652d6b4f1e76e58d66d4f57e7b6bda786c98dcbc994fdecceedd78ebe4c9ebbdb74e9ee7cdbbc1b493179ddc93d779f3a84a877b33cd2e96ef76dabca6afb967b1e8337dd895c9dfbd996386639cb9e56d12c3a5356190954de077d9d295e3b84fdfcad55315063fa39e6b2455f6944b3df7947bfa395a0c0cd64c15c3feecf9b2b556e5cdcc79edad946751f64f9efdcd568b41d34bf05e8da6a2ec62db300c93529e50b637cd62b0f559974b76668a75ec9885956f69a83e70c1de02829459c51c6cd9761c9a167ddcebd251ef1b0fd4c6711cc7a5521ceeedf4d4070af572e381fa54ca87e6537c1de7381edca71e859806dfd6a3f0c9f3ed370cae9ad334afbd85c1d46f5cca036fe4d4c90381f25b94173f6e23c8e7b6bfafe339e5532676b0bd8e3c6d9a9723fdb1aa87a20e045cddbb4815c4056311a8fd38c5a9eaad2937e59a102a29b75e975feed82c3397326d32c9cca51d86d048bbddbbbdb5d08efe912847a421f105a248d4f40361115f4222b0844a70099350e2c62433cbcca5b9349f47fd0c850960a62098a4af238190c8344020268d459e3c8d5390d6dace9b5fe3496f4524636f13429bb5a8e46df754ad848a46000000006315000020100a0704e270482498a792b47c14000c7a863c865616c962a12406711005310c8410429031c000430840c821876a4700a6a92a2475697b91446d409535993146d72399a161732cac2c7d4199355abfaf3750e413bde1bd3ff5040b1781c2863f0bb9f404037e6a01f1bf9329ef4318b9057fdef5d6592fef472da03f1d9c0b58ae1773bdc5304a3203545646ee11bb15774d8634a13220201842872818ba1a58db3d5c95f6c7c17308f9c89b4c55a488b34ca58fadc178c8722c9f954c5cb95f8851b30ff3d5670bccff87793b9ae9a949c68910e44325aaa3003e1558f4c177d06bd8dd71f71237a86dfdb409a7d23e1e0e8787a982537d61fc4c67d9557cefdb88c470e3ebe35088b477ebd5a017f0a6db69524df7649269ff4ae70aa991ea49fe51bde9bf5404a0d75323765b2738fece35e0882d9b4e46b9023e1b97dd26dd81cd46f65ce256211ee280d05653501d01abcb1960ed04b0f4326552293860c9a448da33578d5e9f04a0812ba5e2ae1c42e1f084595b42c12916c9c085d9c1dc5505c0e03a2c4cd26cd7b147f269ae016cc0e1353c8518b10d20616f8c73733a7249228b40a5dcd5f05d360bea4633d0399023c73fdcff152021c65a54e446b7b8d23519f68a8c02526bda5bba6a29d5b4f6dcd4586430f37ca73cfe70c7898e1cb1a024b5522122a3f3632ce2c89299e4736067a5d3bdb69675acaaf78ad6ad0b7a746cb290eb76b9f6d3667481646a546c078fe11238dff7ca40badeb3a507ee2507a4d769a9756cd31897a0142322bd7eb1c897d06420e1d8f5ba363c24a79d8de39fdf0a56a7c9417d0c57328b704517cac658f330cfdeeb7cfaae5a46dd7a9716380741d6879bffcf957b1e51ccd9edc7032f908f9e0a838c46b838347dbfa8945a0cf11b31d9921151c98d3df48995c153da26e97b18a96d0b00e648de3e224f2a728919308709f3c62fe46882fe9e522af64a820f6927168606b41d6c4d8caa7203af23cb88f62c36d3f85c4025bb602b59492df4d5c9df9beb6980545a82b512382d0551da8479f64b0764eb164ead8c353cbed9b3bd4e0e1363a3ec1bb05841b96f4bfbb52f6ebecf6c31962807bd71b20564a0898bba9aeee899556916ac82c818e2e428ab3cfbe28ffff9c677a0ef43816d38262eebcf6af4ce88a4ff9bd5448e87c8e335bad22e9ca07d72c6e8c97039a50ed34f405f0e104a8e57462cec3c111e5f209018cd5c1648621f81bcd8e20695f077361a5c4bb04c5b5b576a709199505833de0d1ec920484daa7eb8f7d199d96e2c02e39f248286a3f60a639a1ea011163c64208d0d4563a6bbd01738483e138797477a849aced6142324fb30f9212eda7c07a62c269b99ef279f74674cd2f9099a5890a59fc3af87052043280281f2e6723b33fd346e4fe142ff4c90b8586fb1c848cf50b4e9969a19fa8e23316fd54d5160c5dff6caa5bb0534812b6836a8dc5476b36de1c024e9b519cf806211f5dcf57e739e0ce18ded4aff3545905dc59bda11ca9110ca94e4162e3f106d96904b5e78e9d88b0fa2c304690ee6f98aa8508e78af064bf1d944b46719df405b6b97f0de1f3528d2153ac94c0b612c4d2bd1787d7df335f3b856d408bbce727dbffb02084092493261e2742f76233b44df6cb881b1aa3714411e44e4760cbf0cf43525aa1356bb1e1d3a69981aef664110ef76990262930dd2d22bc218080c06f724287172064a6aedccdc144e12913c25147f9599ad16102ae505f9ca1544cefa74e8420abe411aefb73854dff1d22e4015949120be83302cc748cb05a63b28e28f80f688424232775edfdbac9c498899cc0d3d47051307fbbc52093e17d3712ea2ee77809a2063b723dee36a13842ca2c76e901358a619244780f08661d719290dbe927157d6f5cb56a7c6534119d7faa71f1539e58424f257b83474b91329ba15df62af72ba4e61764478cc4724ca438a22affe01d5f3d51523eb4a1a3d6b943684eb7f0dd04bc0d9299c97db9b9ead528b42789eca06259debadda1b656dda2217943ab864659674cd1a917cc219205b4e1f5762984ca5b096623fdea57ca0e89a159864b7bf4e3004ce891af6833ddaa7604f77a431d2dd61c4c82f5dd23828b66f1046f130b213a6a29641f7e6ff1ef687ba0196d3f829c61ac8b1f6ee714c079590e7dd17f8066e79f6532b0ce3fa4c6dced6facdb15ef14612c302e2dbfe931deb3493b793e851c9b7929f3c794e68657aca03e1495fe968032961f44f665c0fae2cf8c04d055db51ebfc711bd972c964966a420c2f4e3007a6efc17047f9c3e70ed6c413395aebe3ee45aa67b803d2d18a519eef317284c6e155e7a13c19b011d746d44f9abbc8c5ddd97b95fc7ccdc6034302fe9db3a31f5282c7851b68e347bf82de454798c9c89ed4a066e10c4112c3f1515acf810aae500e80a228200e2e54f1f17a25f92abfb081090c89a80eb50283136bfd08f490d92357c1ac32d434f69279a8c8a97eb8db1a2e94c910d0c6ef2031a4ca81a5ebe89dc6d7032b99b6202086855ace1402f3d605dc08112005a5357206080e5a067ae9abe4ad846e2597ef6ce4b970289827521cabf7585a147c0a25a06aec294be4d86f5f6ef35f96ec97eb90236a4d02356041af1593c72d24454e8622b9c1764248dc10d7f81a249aacf2bcdc920d2b2c5fb11e30d72f216c6067422dd2c547e6f82b32c2337ef3e022b25adf5ae50625721e1b6ed4c11b921b2f4018baf326297d3404ce35b06288dfc10c616c8c47d180fc844bab970a27a59dccf0f289bce6abc78c3c8744e83394db60179c8a345e803222587372fde09baf9ffec630407cdbe3f0985005a0ebfc2bb53c8c12cc1974431ad2f9cb5d5b6285ec533975e93a3c38d4aa2b43e91bde6aba3b0dbcbe249615eea46f5574f478efec2be655ad1d4e913542c859c26ea2a81327b0ef77ad82de0cc6a9fa590a5b971a22f287db21c470260b50bb8ac81da79b9c040e9e88d389a48aa490c8d8058e5096639c5c27c722229a95b34a00eb9348b97f2cfe24d0194b69950095abcb7869aa55439237eef703414322ec8ebed46b24543c12a0ab362414512944a93820b44df97795c434dcfdc4633420ec10807a8ddccf7cbbaa43cabc851b92d6b43f2f88ebeb8cb23a5fc0132cb897a3d0e68c0201f9a032f07a7141529351153fb88c30fec1005fa1ce8c46f3eaae6023fadecc1ada3a3bed3b7b9a5818280477de2ee80df69d559760a37ffa1a556a609d32b8a1be51d8e07be3f0a0beffd3386292041d6e0740c3050642dd049dcc2b808f4ba2da595f02e1f1a864d63179a5c0afca0fb47959a2e5ccaf9d5ff2595d7578f33d2d4505fb98fde68743f0b2f3e3b879d218013befa01b38fc350cfd57b7ae1c4a82d95a097b4bbd6eef7ed4caf2a31f132d32caf43b6c33a4b2827a1a5ba74fa37a0042d58fc2c96e95f773f2ab80ab99b792bbafc3cfabb393bb4f55788919977adde306d6ea15d8ddb5e5f9b2ec4c01b390d07ac64dc972ce9554e99c85b1039dd55e1349d24315f0d39a178e64ec874544cbb3550be6e9827b444c60411745efbc3e59efc850b74a47ffc601d1e4eb9871e262a98f11404e1c7cf3aa63ea3e523b78d4ddada02b27904eb0603efe611f3bbfb52201cc3e978c0d0296a96fcbc17cc9f6ab8e953e2de44a581c1b7de258327e479713af4e6a28c3d351ddf165fd496d2d433cf8e42acf6c768cdaac0974d9b7c6e773e120a52ad3b353069359034c9f13ad4ac5718049145f704ac2ff5d5a6a42fc876315951a78eb689247a468a165a8ca2e82b57a84979c2fa8be7883a48d5a9fbea280a901c579847014a579c8494069d728cd0e2c77876e489575b70c35e34f4e0542f8328092a11e1d95784293a836b0f2507f8d2a90323448f3ef4863d59f0fa2f8800bfba4428bd48378321c3e5dae7a3a570fa7a69d5928dd93cb5567b68c5e5d47ad876291ef6a106ff47c67b8642f79fc2393d2d76846a793d1697a3d84166130815f700a6c4a9240194d746ecc6c05e1ba6602c12267c21a9142d3aed06de7c125281e19a495f4382bcb7033ee914732335acd568d886c39380f360bd0cdfade01c37aee838cab8423bbcfe7115ba262e37c425a6b7e09424e58ecaf7ed32d84478e77ce93f634e15df3ba4f22c7d4ceee80ef2368011e2454c071d0f6355af124102d83e9cce5cb02441442bcfafec9b5b89cc59ce30b8d0c54d11f8416d0296d379e5f54ae8e5f12daa74eb7530abd54fb0f81e05b0dc74aec2de61b032da7cb4db05ebbae35b3158e2439b09c68bb155deb8c3494c96c1f22ae5e29d8a798369b02421c7281224cd56ea18ec5746d5234bafe2d7eeacb87c1459ab8a15f546d32a0bd8592e20eedc7a1c6951de0675f8c285c9d1aad07b64467bb951ad57b0aad251a4e2ba4e78ecb0350ed051467273ec17332c7e034a2b62116ffd2474c5a1d1eaa2982df59ca0540707dd4c38f50195f75c4502af16acb33a2025970e77333ef8260b1b6e1cc8bd67bc16e27e666c47d9141edc623760fa6b0978f3ed083cc990454c0b7e3a08005f2c099ef75956eea5b991b317202b44b06bf6d0c07436e449090866f71819994968403d769bb2388e15187a8eeda1d09e9d4af887a95e8a6d8f8f1cfe4946e924d554dd67faa4be1185abdc0a579efb2aa93515dab3dab13ee3a3bf25b9edd5856ac66d5389ea675e89000bd61e6396b4d860395d3626f893370a16f136c0d4db3f770c1f00d064839afc860e91e3c42e5cd275527954256790c3302447601a1e879a6935bdfd5fc106a718bf63f12f4d4c334264ebb70868199622b95d0a9095af986f495dd5f411730055f1a41f5949673d1f547676e2459aae282d75d499edb44e5d96678600ac118b11aa5d82fed118949b3af06ca9339f09dbfbada5442410cd056256152086f473a82c31a7dd49ba06584c3c7af8d1a564a67436df0347ed26f42d7372b12dd432c6034a6d06b5b4e15e741d6372be05d75a9787b82f32124ce81d8dc4b090dc836090b698a171b99e7d1145f6098ad72ee2194700eaba89cac1405cb83cb71d5e975349a077cb42790a16bdd6fca6e16852bdc03b17bec84a618f4436e8f9c8426efa4d6c75289f81fb81d27ae1949614f2844c8855314e5754a3460820e14c1325dbc47f19e8dd0c4103ff4c6d9d1f425557262c273b882aefc55935416533440564a7bf056548c46668ebffc24b537ddfc4ff564327b06c3d100b43d296a50801ca04dc9dad31dfd14d2955865902c530f17f3a33a40345429b979a4dfc2edb3913fdeb0b29311f9ede34f848b07249ef7ac210e58186625a0902a1688dc0a42cb700ef73299ff84cae0e567cbf58cb24a2d4a0b610c8a38889fac13e545db41077c55c4e322758f50751333196b18796e984cda305e16baca79e42211b2a7c40b429933aa2889c4e4417287229845aef8a5a44a68075f329be3322ce7ab4fd492d34c35ca1de005adec462d04eb20b306544f1bd532b4b4c5b6aa4b78ea1fa6dd226aa9625eab35777e9ea527fb46f3e4d142157c46db65df8e2ff87de8e94452546889d9cc659e021569e40b8daf3659e5b6070d49b72f36ad6a81751d9d83656a42044317c9970fcca54fe27f1149869c345105f0af492f63e5eb31ce545cb8a87ebd3466cb64909ddf0b2fc50e224dca3cde964c5de78006de3591b9deeacf484fe16458737297235d16d8e7cea2254590cd3291a8447c90ad10360c8daef55e052a4841818c8255d5b6679ae77d4d1511dc2d0a21d50bf29f0b10172377125f9e9c2dbdc6032c49bc3810b57842b1015650f529e480c9c17abd60afa169feb205720dd601c12b248c60fc2194201544a93c314d0841f212f54f42b37dff51e87dc387810c85fb0f62ce4d0dbf91df6d44cf9a92317ef375ee63f3960a5cdee3dc7c40e2a8bd14b0a825c41e227c34d6497767d9c943ee080f58fcba41ec2f6ee71220d86c7304ea8a5416a8fd50fc08241f01bf57cabb9cc313e093f9a426e04b8c5ebb21d7ca59c1e67700c61e3da989d9af2e7c1dfd61402e09ce8e4cc4b853c5fef7af5397192610a31bee76b1749d289d31b15802c3e1bc0074fa1c004459f1b274d899000f82031c200c8359788067fda4db9028490181fb519252341581a893e11b0efec22539c268302c6c0fe346aaa2658d66c60d27ccec55810719911e84ea4f13c998c2c4d786072b76b06e985d80d257045e9a090a901d3aba994b9846747e86cf48f351ac4da7cc503b3a91c6d2da776bfc151f5f95b8ecbc13a8c99296d6e3cbba92ce5dfae2af480a5bf703794e3e35546c1ac406c9396487870dc0de299d6af88f583cd86fa9fbf4e59bfc64f62645fa4444e08366669fbf2044876ea50d62ac4f841fbf8953ecadd3cf8e85938ac91182f91591829d8eeb76a64513926153a85764a49e04f7c0dafcbf351407d9f10e8bcd6dd36a625dab6800e83b45f79a4774cea6a81e47c645c60a89006dc46553cb567db46cd716b2d880994a3043dbbdc7467271cc909ca1a632eb1f56b53ed0c82c5711288053b2b86f993ce9dff961c50f64b4f1a870bb5524893691ca40cde022a7cdc47388f2786f26280e0875d77d6d2c9a4bca0729cbbed9744794c8094a5be5958071f600661c407cb9d59eb78c28a05f1bb4ab548a65e31ebaa38856c1ee0b690764788f6db929bf1bac76de3071375827d49136943ee968caa7433b02fb47075910dcc215ba8fb3f5e65ae9133e84cc9a537e9aea8f0e23c1a5c1c4276d405cda705a2ff7d3ea2d99167deedd2e84ec9bfe185eadc6fa9cdeddb514a9598f53f83cde95cd0d4ce4b3fa7701d11f3fccf214414a866df5c4a0f4ca3ede8a7829e1088788f2cc3567fd107e5c976b32b9b574c2003d5c027c65018678eb96146b6356dd8611992323891045ef1f139bb4eeb4a886694b8db94fd048059972fcaf938dbae4590c6a87e2269934164bb7aa044b3c9d65b40920de29948c0946253fbaae30ed814e1348925b964907b39d7096f4834ca91c36ce7be54f0f5dcd5011207cfb02d1b15952d5a1563a9df481e7f38f365999caa024af67ceefb4c814d94c946743269e864672dff47f0bb4466461fd83385639f6dfd42539308a6de6366cd2e06bf13f39d43b2afb07388b5055aa3428716b131fd1d38b194798ace8b2177e86913fa8a188d7b4d41c891801cd0d51e075b559ee4b18934fc7b495f61e5de9e48da2e249a9e1d9af515baa371fb64ceca02b8f05375c8828d5f036523c35b0e8970cdcb469eb14371931c42b1f2cc02594c59c58b91e283ea2362b76c1adf0cac6ffb01c8b791b10e1e45229bb88cf5ae01ba1e8d0dadf016752244df78a866eb714df06cb9b3c0ae88ff19846448f3726b9dd48b26c5fd6ceb571c8af1eb4cf95d1b7e20740edb8df49dd22a097567cb60d30b26b5ed54753c9248a07bb04e0ccd26e1753e21d1148b5bde664245869d2622fe015f0e3067c0f74b3819cf6902b36e0050d6a815246787a38094683c4f8bc2e5129b34e718f0533ca061d140caffa148186c53fdf05064169b7fecd55d5f6a15473304c96911a72094df089891104e908b50af387f9c2ee1f759e0df54cc6bd4a914c4407e55b9235095f544a992618d0591820693fc19b843dca52942924d1592a5e2a5013d3b8d4243a46b781718d2bc2effa92fdbf52088c31078a9bb98e808d9a5439383739ca3b353186f974e0ab773d994154626e24813446065c9c85e44aac81201594533d3eac8b3a2dd016864244b4a2330f97dd48d4d6a5ba267d11ecdaac715d79486ba2152e857fd297801f06ce0191e74eac3ab6e74018e938c26514207e5d9287a6bfffc68561cdd899e8259bc998802a98ee7109f5e58a78073d10611ae4d2ba89e89b816cf6b4f2c1cc4261ecd5f71cac108494490fe5b38556bfecfc9dbdee3950784ace1d7e5aed9c0e8712f6aacb721aef49929b9a1c48c6ffb0bd1d0c8f66ca57d88bb8fa6bcb2ac5b56cebfcaab3f42208dd353e4c8cbc6b60ddc6a6c23e31cbd76d26c6b25a5be368a5d1e645315354158b223ff6388bb248975deb77a5f4ca4716c277cb5ac67adb0a3e46738f41c65a44c548f2cc59d0c54a3e6b4eaa3ac4d9868f489e327b7a37cf797c19f221710baff037fcea9fff6f38ff70a380bbd294352ec6cdff27446780de966cb527be40b651aa9b028c190a63cf1e584b25018a1a721912e5f93799e39672b858a70e70102f76729c0036471a91e6e11c71eca7600374c324af4eb8e838c84c3a0d7f6ebed987052fab42df9868443813fcf0eac0dfa11fe0b5c8f95d8f4dd913f41f696f0077369d3e1f0be7ffabf838140865b7b41c588bed674baffea3c10d6657175f910784c6664d302ea36e36a9142978b9255b7b9a62965579a0e6a1a4a7f90c08de1a29ef539667f106d546091408392a233072d7c1669c7924cbc47d69bc2f2e1f71e137d8c11246ead5243f7f1c7f93e7d4f7762c23991855781bd3082af840ba9e41706927261c85ad1baf6b229f0f23ce467d3ef581432a8fb0ebd69cdb95f883ab3778db8f2da1c461488022c3ff9b84bc15e088ca58b428eb720568929dc60e5a098ab0b4d78aec8574bdfd78d098627e0e1e880121f4dd71ca012d82f06817b5e5d60b8c587e8638cc0a3d19d191bd4a99b6aa8d58a0082f38fcd2764ebb94e4c82b145fe828ec8f6514d841c5427705f756b9c8b4fca199da03a7b51d85d93b938dcbc438e190795306b335d1c864d15177a1d88dd6e5cbe6d8688262c6aab1c20404bd19df49614d37657e6745e7c3ca214dfb3a1f9ee5c0534fa9525cd0ec989c35c937363fc5503662db702a1c863ada65c9e7e10ad37733488d44de39b98837f484736261e4d3801b390707808f1d956df13715a5016a054a72d4295ba43e6f737272a321d3e64b9e8068aa43204aab69866737d842774d73a91581c3f7d953da90f2b51ece2eb907d4419b47e2d460e89e6a992bb786042dacd9ae7cbf99a025268adec398cf89445cfb746662ea2b53e17d70368cd2e7ec7e6ea5d0076253337496bf9c1e17f29c0e5c8d555728fa8f3d231796cab8f414092bc65aebba6dda76861bda60e93dd6c061e0d4a805b19ef9317f00d916b616a971334854fafdfab54e953f12a6cf4b7cf873d3b20e502081b4c076b049651f8a1754bb9b9a80e12333907343dba98022181d14d9ac4fda10bedb611ea2d05c28650864c44fc130d5b0f4ef1ecc83c0b9d87b2ed8444d6063656800a547f992853fa2380782d36660200d820886643462a2579fcbe46bea4c4c8ce6205f56ee15f87c8b645c5fa1bdd2b721d0c08bc25875c17c60961f3a7ddfa12e5d868c22cee516cd4cb26390d00d626228f5152b268e80864873317a7d001ae52d194d1a09a125c221534d3cbd81fe30a618ee0e5bca2e1fee789769de0047373853e91e0e461e0ce38433c2c562e8fee0e79ef6aa954f3644d0662361cb0b40a4a0c59b6b5cbf39023b5e7656d6f94f7d2b16009a19eb9400b602152ea4f8cc9514ae154dede0052abb7666861489cc401bb487fd5c63ae8c2da7317532bbf208da5a0f7d779b7721ecb0f22dae2880e812762a6a21a00ed40a42e8b906d120ae4302728cebec015ed081f1e2b412fa7e5f3c624ff8ba96703dde2f2b300e8caf58288bd4fb66a0ba2e75dd8f24fae01b82420641fa4d4bdf61e2f80fc51d85cbd872c6f2eb6cb932186cd8c634d48929738f381014e360b01984053d6c8700f9c40beaa46c0186049048391246150c4fc5776e0dd1685913f876a14124ea3280ce41ac885f205bf5754909dce69a9681cdf8fc43a59d071449c4708556955e61e0fafc727daaf464f0bf2ffbed71a56812208298bca0d32724cd363474239616c62764dc8a36082089477e6a584aa99ace487df55f658fa36df53b444f98821aa1fe7b3054f117f9a26fee2d678c2ac710f0ba9fa45362c71ba9d509cebdd7c0b31ffe50e407ffcecf9077502f8c1c4c8b27f89d356342db4443387a44bcb2ea34e94e9578ca9b84706be965f364d8124884787cc6592844d1e1d3da18781534c218a5c14dee97b44871c0bfbe9675637b84efbded2b0902dd93a797895979142e6ca082aac7bb2141183fa1f69e1498221fd16492d05a8c96bb29ae86fa0d79593aad728d6788ba3626e29114417d28d309382837945cb88cfcfed8e88a956be32d464da7e8178ce091812726982b7a308e86e4ebe1b12c5eca3f67bc857015f6dd9e79f6459b1e022ada595af42f490ef02459f433399777c34090e9da804c969a50c85ea45a1453ba07e7c360ba62078c9d04f5b7248ca6af097dfe9f8e14f10ea5922f0a9df9509d7e7f4bdfe2d091cfa22d121e55bc1efff97778705844ec00667665d65b2ebcf861770135a35cdb4099c0d256884c0953725e80e853f03962587311d03e11201947022ec945394c2b4be7540cc5791bffd0b65cded6844c00d2fd37b26c780a704569ab7c95bb8bf25dd504b849afc11d96135924bbb9e54ff02c9395bc90eaa8fb5cb13722cad7aab13bb92a2171e6ae880b9b327b69c87a10950f3165eb07cfe5b4f330a0a6b65daea65305034af63383d19221b50cae44afef6d26c23760142fb53dd1589413542a5eb4724abc2c3e7c4decb14d2fdd059974fd18d048ef80709df57acc5bace9fb184db664af0e350424215fa2550695b04bfef8a58cd4a3060161aca3ff763d84fc64ef27bdcfc43f975980b4e26f130fd1f8722e8133e641ddefe1dc7ae8677d6de31e20bdeb7f0418ebf6c6109384e18d997c4db27a3adcf611e78fd7e31ef61c7feceac6fa88b62ed893f7b1a26eaeb1e157985f669a283b051446312b6876265baca2faec358fb1220762dea2724a03bdc85b659b8f9620a7fdffa07e6c785be3fca2ce990ebe1aeeafa9edc5c92915ccc4ae5ced2f986abba9707a41a42381f1c80d025fcb38a901ce1ac2e93baf0897e8b4ff5a3311a847d1795169f785690ff28fb201cc20059d88f101ae0f6d32343c4c20bc3f8f82a7a30924051041f02c165e119b07cdb139dcf99c7ea5f1588c4bd93a9d830feb0e8e027ace639ce840edbfc0593258503632400f84f7774c691f0cb502667ef20d6830d797f1087d54c5b698e915e0fa2721e4a7c239b2201b917d91715bb8d0d42183b708d2d826d8011ed0930a8572487106d3ec9f687e418a86cab39b66ce9f474f03ce9edcaa4acc86843d163ac9363d8983490ae860392803adfb1fd8a90743930239eb8ae37dc1b25acb1b67eb0294f4a6ad94e8a9686bbeed08190c40242b77160f769f49dd0320d9eaeeda070851f71e737a0ca897ab832fd4495e77d1a74b0e21b7c9f8992e8eacbf1957f7fffb3053500ad8eda85ded6fb2a79d67dba4e19b64214c0148fbd0a444d185421f844ae04bd8a285ff4c24edb5692785215324b6ad9fc3f9677b772d86d6c844329f3e9ace8b3d1eeb7c8957c3abb6ce8d380178bb41b8c84f0d5d136d5daf00e17aee210514d994a0b8d9dc614a72204b2d4fc115217f2f950c79cce03843cfa0bb406ab26c2c08842990f106abf8208732a7be0818da973d12be0f971c61668a8c2eaf313f25a74a78b5dce6e086951987a241c0833d138f1ddafa0cf107fd05200b8b3e6d0705d2b136dc87c298a6247d93fa03b979a86b07ac838fac2857af8bb8a7c1aa5b492f77e01fd0ea00257d12eb6c8b1693b4187343fff30250352871c7ece3540b27e7e22f047c59ea90b51659f8dd65bcb44cc5b851eceacde741836755cd7a436e72fe7709727dfccf10ca0d0cf59db85d15fb8876b728a593060498c8321b5d965ed3c4572859254407e7e903eff01dcf4a9db9773c5576a6a84abf054866f66add9e5b889f00b4fd489336c7942bb501d8740cc32a9a7a303010219057dc0c1e2c12b4b0eb448c337d03abc0f21292e0549c193a37901218a8af6d5ea6dcf8175fa39bc26866b06b8201fe95dbd97ca2b3d503ef7199c1a7b8b88dc40517b7e305622e7d318a713998e1b92e8ebc2122a149488fa8ce613afd741b38416140da3d699c936d9b02b2c72ad8608142848547f63af45f86687e53d006533d2f60d5f6c740f7fa368cb83f8dd7d2b5b67c7577f3f343ec1226c0426ba482b48d2a878d77444898b7a2931590d4eeeb7d979890789e8c2aeaae13f0b19fb7184e0de2ef5195024a8712e4e714b2190cc5afb086c30916f756c6b2ae8a9b5dfc803f4bd5f25656e7de6829dd012c05de384d02a8c9f588ef0e519177a029aa1d9b03aa4410a1e5cce07d35852c777c73bbb79967523844b99872780f2226b6bcfbd3b453c4feeff251d76accfb08cc6c2485bb8fb87f3e6245496577cd01e86f5e1705e14439425065a0d2a6d013f4bd0500c49697d59820b27fe3a402193f7b928b87c593696ac0818d15dc7f4f397e6893837fc04471401532cf75b8af5213fa785b1040e2fcc0f51f76026ac24a083dfa2d7e4629ef3db936318cef606fa85929ab20722dd36e7431526636e549de69f9f4ae7345fb5c974f2aefe399e20a5ff6a6e594440e4efa04b0af0ea8931741d41c56d131fe529bdd89a122e535369b0f6b2c982e0f7e36d5daf99ab6ce55332b582c7ad46794de5adda8d286997268bf61331670c43ffb372cc67e7a0968ee3a87fca1026194fad16d5ec2a2b1eacc417008963b28405d8b74fdd025c7f1b66639b71f9357bf37fc82dbbcf5a92f20783c103e79461845863a228cc7ed2f581ac85c8ac071fb30c68241958abc58f7651d837926b9024dbe6bf46ee3888141c76eb21b19d23798e6df4429e15ec579dd55ae770a0fff6e0b11aa4dd465af8c59b3f712059e28c370112ce6868f3e7338aecf730addc1468aeadc9e1d5fd3eb56c94aa3ad133452e8d9551a602a9b71a4306cdcdec253e912a973454e909f5c84a52bf2138d4b037f2539be5024ca482341edfa144b3b806108fbeff6b3402fc8a538cf777608d133672d090726c29e1bbb65eca48698b9eb30b0ecc1e26be58980f0ae249476014db9e5eced996aa8cd847388f4553ab0ab5a2d515307c553cf37c893801ba43e20ce1f8cd6e7dc49a0108b12f2d55e9276a514023765c58a344a6cb580b73c7dc8f43decb904276c0dec0411ad8c13f816b17dbc6c899193478060bf1d8f7dfa3cc488a56bd4fdfed50583adb59ec55ce1be0bfd27841cef33d0481eee83a6f4fc3910750fc3718b1e19c88839e803ad843d17851768beb2c52cfa0ac2982ca6ce78c7dbd45055311c5d1ee7f17d3b11ad872ddf4a8badd36aeecb8237c45c876349ef05445980433da4aba0dc234395f01373fe61030bc257d156eef05ad7be288fe27c3321e9f4c2d28df81b69a0fb4510916c7b0d049f215750d7b25866a2fbd5ab72869de0241b1602ab26e582f57495f5fa031c887ea0005b137d585280bf5eab8a20e6a58ee40d9c6c1787fbebffb4e6d3272e8a79e6af82e7b8787839c476440c51848d131e5401b20fd9e8a146a98ffd99e9b7ff807aa37412ca8b2b8cd07903095d478f99e91698e0e62daa2055565cc2977b805d2155799b98a7b1c4a35c62596d425fba628e2647d16e99a3287e6fe24b38f7dea4f96c1e3d898cbd489b243ce45b27b6e696752bfd8ec24fc509c9d1ea9e80d9814922da123fec79d17f93eeabd97e894ccab0c9ad9de394466aef028ed8f05a8f730b87e5e0c63f400f55aac1d441fa5db965005dd7c780fd37dca8d233c97536369c91ae6f384c2d3015e49a9a56720126ae1f15b2fe19d29e1309c09f2b3f443227307133eb5d0411283836137138ec5a472f62e88ebf58991beba751f8f7f709bae30c4e6cbe0f6185ebc7bfbee4bde8d3f33183df6cb993d13b459a4eb9c0ff47c28fffae96b63f35d2cef3d1567a3afc3740fcbcc56aa6cbe6ac87a0b6823db82a61a14d21d13c8ff0a91a161adefd721e12b40949e81ba9b99e852212ddb81484b7160eb85e098427ff1e6edab2f6f9b31bc60da3b417a48e59ae81c821c3bc69dfe168726aec8ac69e4f686358135fa328459232dbbc080d7ee7bcebb4fb6da3ed48879043b4512ec538f3c4d47bdf452b731e4e2464cb1dfb0df14e189b11162e0e7801bd4960e168d12f07023b4eee17e8f875e7056209e24f06c4411f606c3a633e709dd90b6c0b4b9f065006ea17301e8215c7bc8e99f474dff647e568e4c454cd7e49dd0998abf7d1ef5c3b6acf04ab2a748028224a42816058d91c6734c63a051039de32bb8da42f86d508c63ea4d8475941f107bc904320bbf0e2df6df4646fff0787df0a700d54984c76665c0595ef314a6385f038c2b992fb08f1218c8458af6487082d5792d7d12676546928e6388f0221dd0f9870f6e0111ebc6c30ad3ba3d1110d2f4d5759cdf8d53f791358ce9c4581ad29d7e2c74fcc76fd5bb54270bb714725ace726348ee702f9f98028d83a878645e2e336a9c76b38034d1b009e02ea078b4dd03107e531e150600eeafb9f432bae8844c89d53c8bbc21a6101efee6739b1af29de357d901fd57270ea37b6e8d3a06d930f25eaab920960501b0196ba8a084389199ce8712be20e05c4ced43d2a46e57c51b94d41108d27e3815ecd36e71ad000b5eec40b23bc12fed2e06caf5640ba521d092919cff327554fa47dcc5f42bbaeda684c38b1709bad76a39a2556c7b9b044db073ed7d32a7e57bf46568899549b082efe4bc70235c747fb008e005c0703ad94303c02f8f5fce1d19c57d016be07d964c55f0390cd263fbabb378029e7896039c9d970bd5ebdf8884fb12a4b5270beabc6e53e2ae7b0ab3ea209124665452f9918aba24b6c6ed0ac43ee8057a203db115044460d0faf6e5b4d4c10dddb3032e10d879e3f179be29990eb4471de6c725e9fccad2bd6a2f521ef32feaa7185184535dc0ca16cb6717c4c0e108695cf0474a72600c75b13700e3433e030713a282176f8c4ccf6198d3ce3648c41c2f80685c745915f7caf5af8b94159083082bc52df976d4c0d3f16885afe1ce8c6de879a7a04101c58e0214abd09a739abee6c244e3b049b9eacca053f5197f4fb879ed328294bb494b7ed625e14dbe55678375bfda5a765512a49f8c7e85afdeb96b065c919434825b04ccac6fc54c79b11b6180ed42b5d384792154e64139b6cb1047187b7e1f900c384e9f7cf2e3b07c0fc9f612bbbba2117f38a395e644404f57d6308393746b0b05c1f7d9af90e8970a71b0046eaa6ee2e79447b0ae56c64a61a67612ce9a9f24308dbd469122926f87eb3b0c6e1ba046e24de3220788a4c49f30f80db9fc1aea7a3af969d45c04c013aec3473cb2a28afdeb9a11fc4f24192fbbf2f6232ae96d48da710042dc667b80e00db0084f8a3d6851ef87e31f50549be6025b77c81c559bd2fb1c8937972df66cfdc26557fc6cc59848d4f7c3f4bc5ff2973032540bca57a5bbb1474ca503549ed1ca28a5cc102d3ae0f23d5fc10287f7a8584390ef88b854fe049b2cd2f467283ddef88eb07700924656fba41dc62db25adcaca4fb7be3421bce46bc204ecb16c984c94f446db868cb43b08e95e4785554cfc560288e67158b54d0099599c976aa616f3861258eea492047f4edaeeffca570e6cb2d7dfb4b3e32ba842a04bc0259148466a1529733216a4f09129cf891909bec85762d2384bd1ad831e399231be6b938cfe5b5a9f767212fa6366d9de26fc79e284b99e64416227db5e724f5651f16e363468ac9a8bda0315657923479f14a345fb87cd2a520de7ed0f08c2190fd7f87d7249f1f0227dab55bf393807b62db56c3ca4af2a8a525036ff2afbb485dcf7683dfae4b2dd606c9b049ecafac150bd25c8b1d84cb4cb9148b967d0435786fd4e6e6f62820e0aad379ea8de4307a726eaaf69f4b67d6b388300463f96277946ee0330e61fb114ef805e70ce04251292ce5764e26487365fd0415b97df18a6264df849173f0293a4973e815d3d676332d01c08aa5867ff20741f02d137ba15decaed586f8d093cf2248df97a9a871510c6202ca69742ba91324688e60586abc9ba05e97dc2f65d659062e7a503275a9ccbb657b984034b80a0daf96625ebf306a80ec3f1ca7ee1e506dddac77597378aae12400e89ad954751bb2fc40b36d5cf90f160eac059f896781a4cae44334448b6bc180d50515b815b177bd31ed7a6b15d83ea767f6597b24b4c24d5e9d1cc8fcb69c1622460152c01b52ed1451a1e32607f7b0ef816618dec9395d3c5607869d68917e270e6da8c6de621894862e5d81ed20a71fb18f40c037c20851360953f9ba47995590a2a949b00ae139dc51ec03e78f6cc09374a935ae6fec37c0a61048fae488e0e002510a622dec4bac6590560043ef64442e81b9cdd3083dfe437e4db8bf521153817d0821a3962a330d738af7752e246ed9f6012a09119ee3d781b3900983d3ec820470410f58a4c9f5e89665e624ca0aa0b3a5f899bcffd56bbcc59b59dd53fbe04043532514b53af573f50f15a69c524530c871fa251a2cbd9b941d5150f59410bd633c7429fd11c411bd2c493fa87d904c3f36173ca854fb98fb7f0c179b93893911b40f6f950518f901091bf1130db7559db9ecf3edd517027a68f490dbe68a7f7a074a0cca3c12b63ebfd78a72848ab6029a25b079120b21c781cb229696e7db0d84639d277baa9310318f8fbe4d3b2e9bb6156f7e938980d191f8d72269426d208fa9394a232318eec44098b743de030bc8058840514b347c0a562cc259e0e33f4a24801dea399e49249891a05aef9300fefae5ec8a613deb2c390026df861be0ff854e12280178a24b4a70f7a6c8bcf38209a3cc050c4348fa47a555818c81e29c0c44004e6e89955302f7c37f77f59412c2947d0915ea2b065dea8b2fa63181f98d2a65ffdbc1bf5702580b000475ea72908d0818208fb707b5d30986394ac55ca850b00e823761c5f48759ac2a9e584829f964f0fb1d133eb3573978120ee180d0fe14c53062e7c8ec5e952c06a8228f41da6041b1c76b571563d3eee589756ab2ba22485040458671f9274aeeddf5cfe8c0596c0c7b835d961419c7a1616d79950dd64d6c370a0e0fd952366a2feddc168f467517dea65f78f909c7b36f3840d73366f1267304e5602679ca007ebee2f93fa91270464681cce31a009b39a00e3e5102b5bc6539dc287d2e7c8d75c52f092fd2df53ea672bf1960d53bf73571cb45be308fa7ec01be56d22b971c90e4a851a82b139cc3bc04f4f7e6ac40c32132d78f5774de37a88f542093593ca734da6e1df2962c80421a9abc0c8671c66e0685592f938016c7aae1619f5e34440a72536d327d6041d07156036c956dea52819b1b019e9cec9104dcd3114faa798ae494bb1373da2bc9ab65ffe0e8056bf1f39df4e115210f727ae92fd0c5281d21324f9c91dc0e5b4fe01e24906a1e533ceccddad3cbeb9666fb9e892026aab550b156ad3903b6f481f206d6f5b677334faf89cf3af4bcaa57aa2919893853033bd257dddc0d3590f5a63fc998013410936a194abccccc514ae3e1cc0e49d040213f35bb368d2e7732b0cd3280cfec592960b7d33c9ddf868d1b6b2a22204bad009c8e198c3cf8063867a61d1299fc1c5d6a857ba9928332bf1feb5a349d0c5a4b7e15d9fb24c1b902e5e32ab21ffff3e71a71a426c88a647fdba22bf7950b1e34085f2237d4fa10a44ba4ec025bdb381009024dde6f89f55fbe8d9d934b93687896845ec42d48ec8aee0e0ae2ddf26c35d2b56134af746cede1180293e570c99afd7db2d4faec5f44c2092620f00724f6bce34d9114ff00006124d6fc41d0d5277d2c6b24be0d447e100f7459a76c09e56d03f88aba57d5c84f5082c059d0fac4e4728f16df28362a57f466f0445c34d2f9383e8ea61f23bc1cccbd8aad35dd074ca7e788dc1fb9eac3c8f98d900a4a58b5d9228257382d62529b12fa7175ad2d9b57aec0392d849e0579107fb52276dabc79c84a989889477b1b8f92438211d5781605a5a7bbbddfd2ec19ab6054a8f2975114661e0619b0b6a74c9540c5d84ca66e5890431b778eacfdf4b204594cda0f76699a5957092dcf86413297929729b60e2fa5971af32da7f0f7ff867cafbd8c495352185b0d56b13c7f8052e53fa484c540ec5ef6d02af9f39792b621ede648514030b0636b0bf6936c22abe5b895e6c0c1a8666735606404176c6411241aba2643a1a4ae344bda33700fefca3674f40490c8aa856ac4876decc7d6866be0a10df353f8278f76269860eb2165160d5cc4f51d592b49573a766e267db98c98259c38e99be4d4e069bad0bdab0c5ed49ca446ed0880f60817c444c81b67cb59ebd4fd3cf3767c617013fdd66f16474dbc27a7bcef4640ac240ba94fc4d62b44a5b2338054d11aeff16de94167918f7ffb1bd2320a1be4deb9da54249c505182ab63784024e71b2b4533d9429d481449a64348bd4c40ab9d43b1754db524fd213ac025d5c60e86cac0414fa3518fb911b2e0e6ea0fc141dd48a40a99890b0c11f121d9b0510aeafdf6c442576ea9aa4fe75a74090ffda1409693117197eb603ba3268720d861e99de25445a24558639b3ad3f30f72bfa52af3a85d94cebc78283836e37d41e6f8b3151601e19bef54c19be0c790409f0aa4580349bdb7c4697c7c23fa02ba7c6e2fba2851644e3d14f4ce276e443f310833e053844e3f056d1f0cccbad37c326bd3948ff9a72c1c0f8860c094a209640c12b51dbc8e0801543828b0ae3ab100cc7f0688156c62812a6c6536e0a78208474d22cc4ed146c08bda09c18b5e24266580d63dd12c1fee0f92f80f0781d460586fc98e8662d42e1d52919a8595d120578e73b95f2558ea9758e2ab4d4066fa7175af6b01a08d444c98123de5e4202ec6540082d87c6d5555bb6d55c21120d48c0f58e5d22d32dcf3dc7a13c8ee729c0c97c955eab71ba525702a5b8faf0bfa5689bf4b7113756aa8bdafe09d012491b8efa1c5fcb53e69ba0b12fca935e679d306231c2444ec0006c7a264ed9349ced4a3117bec5315a8c1d1ba0b42b1216d33dd22da1ad4ddbc8f4ed20cfe4d5f30233dcb365267e9163de164851b93d9b6225e55339559687df96c154f215e4561ed6b01bfa19d40b8843e5299841a612868483939dad8fa41a794a4afd088a1f9be5de43f22fed829bd03a6d30e72a63a2384c4f5373e2b66ec08b26b659b962065805646ea8f8a62813c096cf2c0c70e1bd52a14cfa9613a58edf3b1e7267636230a378ec11bab88ca1cea42313175fda7b3e0c108bdc16776ad89659a8bce61edb2612cb75b44be96cae6996d1902438e7c12190be8797fa49af2b37bea082b7105079f748550961008ee978bb42ba386c31aa85e2bf7ae0ff6bf8e6e37ac4063dad3582656d1abcb34e44485910d99f88d7645bd9fed3468285806245a710aa0408d60211ea7f80cde31dbdc249aff727a0ef08dff5ffcb4194320f11fdb6e1ec7a40eee55fa8d8e83151311ddc708bfda4c5a76fc3006a3705d6da54ac22ad8a92ef82cbd082d4b264404ffef33acd587c8b1633a97c49345590ea856bb77c6b087bb641c77345b6dadea3dc55f07aeea10de192c4a379b294d616ac983586592064a13747d88784462bc23a7637ad56c244e82d9548aa31e770bffc82fcac357ea9dde4f094acdf6aa6bb58e548b2079a289d7845721208fbd9681955d77400d9eccc127bdef9d3cdfc9dbcc6e73c527445ac90e6d0df424b5c8adcb08e1caff7159e664e538232aea7efafa66a86a62454d194fc43ebecfaa783631ae03402c856bb054b755611f6d45c288927c355b17ec9674d60e7b32cd15e3b7fc26279b22ce9ba10d4eb6a06454c6d0bf46efe84d4328ccb2373c9c5b05996bf9085300ba92c07c6a8c05e2106c32ee7573bb0f1b52fa6f906aec32f59ef19c6cb4965366897e8bd2837ab834d5161143ea14f5dd225719ac346f26439242433c3b9c8d0ea34195cbaa9066ffb9b40a361a89ad2225b7251d928e06e7b90affc27a4fb3191f8f741decc4476dd3abc4a930d008fb2858a21a5c8f44f4289a0888d039e0037934e20f9b9514fa33324f250a3ab194beb2108113394eefd433c32d2c167c5911e07575f179a3bec163f5c72bffc1b38c388e43aa393379efd1bdafc104762f30588f334f4a14f32e80e8bb9f82871c75fc4b1d2f5e40594b87e22fed7296188409ce398f4091779d7b6beed3d4fc6ab615d856affbdf07055a8d924f72763b07439cb64bcdb8b49c6be3645adf840250ce8c6d200b21886f67bba292ee2b04341564452d810967bc3d702da153c15777092d71864fbc058f71038752288fe397c1d6d56af8a71f7a35398c3c1ec194cd3b5b36b88b0576eefb563cbe7ed7d73e2fd6ef455b6c17ff2047ce1c3faa3998ff1152d8c0bfe810eb9118fefbc1d751bc72530ef0122cad04f268546611f65d6f481053aa455647f2aea032efa3e9367c04a9ae1905843628ce30c8b8f30bb3121b9efd5cf3ee928c3d7b216846ede222bcdb718b4d16ebed30c180e69fa6e292c26a608573683cc006508550d6f29136b088ee18eed0603ce44d7156efc1058e83b70611bd8b4f65069607dc4191c8f64a750433552ce294348c5c81c0e137e8d522449a79a9dd48495b47493f8d3a928d52a358cb1522672e4e98b86f19e110fdb9e544cc6313a65bb9c004077a72923da965e01fd5c870494016525810fc3e2c32658570fa8cb8c03318b467dd5ac2b4a3b5599edc913f66962ff579f69c6636a5b6a9e70071730b5120ae2953e64bfa75bf88a567b3e2fa19862f57becce64192bcc9c62e61ad8332e856268e35d5a5169ed6953969b853280c6e2e81367828fdc4554bae24d8c5831e1e814bfe2d16577257ea9371c20ccaf726d6ca0493fb1986281412c3e8e38d71cca6b5318452803fa5563d1341fcb64a8c6b3f204970dd216c7252c3a53aaaff5280ca96888b56588d8a24d2a02e0f4252f779ebc396c6c236d964c8973ed65eb312f7314a97879d326768fe77e54a8e54b97e7f68cf5a81c7f629f851abe353b3dd4667627245183b7405af3f5b71e58697e3f843b2d2df3a895d5bf25a8c98007266a91f4a8ef38b1eb86de730c83b1000beddce103d2523c521a48b581bff16f059295c5b0a405cb32cfecb27879ccc0e1285ca7c1dd44140fffb035f16a118b9ccb46154fa0ecbd1fcab0031ad201f7c136c8350fb408a7cbad9333b9d411115add370a8004bd14e1d356bdc01b881733b2d6eb85409bf7b9fd7f7f235c4ad3bb664536f990bd9011b7ecc5ed6ce09a4e71f8ebf177ab63818e7b0c7f143a07bdc1035dc2595586ec883dff3ad016d33b9cbb318acae20d78b7b57cfe35f32cceef2acc8beb7da0421ad39c4413bb8f2a20898b23133d93ccd50118692cd6a7abaefb79a1d1b2813608fc18cc0931e880f84c03d198ebda048a0a5c4541251688d1cfa679d9bcddaaa26dca93ac27f88738c03f0c47e8c370803e2447483f7c1a344b122b831506ff13c31397871b92383f9721933befa29f396eb0569b91f0eae391f418be85147042ab0b2c4fe28ffbad8559bf336b57f9a64f53a0f925990ce5c8c5c650c37c0d1783c74bad9501b42e3c07075396aa7c2372618cf5de804cfd49b0a73ffcb7f13773d86f6be94dab6f629785f6a32d76f4874390dce5ef45c4e9fa95bda46cd8ae57ec6bb3813139ad20c2a75fa3bd4d2f9b82f321d3e18aca68c29ed88178d69d04645730a633b6bb2a91bbcef7200c62fcbae6eca4fb078201f42da70c646d5e6e54ff5c6c2136766c89355f18e0746fa0787b4a25d8fb70ff7dca8284914f8b3a534caaea29331b7f0c5df8df7fc5e37e182b731711ce82d5b06ec2c204aca85bf8f50f1bb7435ebd395ba8ede9c75ffd4d8234faae9252d275a9ccdbd72d6f0e9aa48c26d32a06a63845d584809903c2adc8d502c3b349b422a8fa54be93ef753c898dcadcc17dc71c348ca91ab960d30a6da24fd63e9a6b2593a0e3f1652f257e3230837210b531c1838c28cf7e57399cb75964a3cdeca54088a08dbed967d96974ae29e52158c4718dac10f69a75d711090f2052e88bbb7ad802eedd6605d86620debc654fdbad8808ac6907616899deffd2e0801f90b7715b95ba4ddee1ce079b974e719343a5ce32215229522b5b60f997e194fc7a2911e7fb4a356716ba8037d13518ab7bc7817770b1347a4c4db226f849c349a5abc1bd0e65ea106a4d23bca1964c3a1d516b763c01c2e036635cbbbd32de5e143d76ad048ef08ecbf5fbd84541bc0ed2295c3e31666efa9f757e6685c3d964606398df39b45c8fae01095c5a0a07f61a0a9bf64e3f71f4975da2279901156e126f1178abc4d9cc36e4b3a54b56ade424ad40bcc3e1871c9f631c5ba57768dac912b4103af5db6f9547b9a1c6840c330e4e9e25120bff8c05fefb1f2843700846cd067cafa52a2eb496d11f1d42549a911045690379ca27778778ca83d04d24b9cc5ee1554d529c283013d1fbfc419cc49945c4d7ebd194310d2008fc6fddd1a61c9b56d9189903c05b9f976eed20fc32adadb484b7fb16b8aeef298ba8b9f73d965a499337caa7064bf074e66fbf2e89aecd9d2904ee7ce3383194582fb222bf5555685e7e3bd17c85eeae7ad9289de6a04d55e33cdbc2597297fd7f67b613a5ff75b4e845cdc95c95898e0cb511f7fd853461f8b5b765e037e1601355caa5d105cc234dafa65ea041580e28b2567a681f211be917da5fe09186c83d7dd27dd98463028de925060ccdedcc2f0b0096d9de59daa015232accc4fe7943aa28727519180945ed061fb63f3ac7877e3017029126f6703c9b93974b0ac34a65a7606afc18e5450fdf848b956080f60f4a74c75a1d98185e3ef95ac176f14dd6fcb76cf579d7ca708b2f12d09f3ecff591770590f3c0f3bbb22e34fba89d6c97e9207c4e0302ddecc59f7254d7b8b774afdf42f41e425d130982c0025c1146ade39e3e7bc1bf26a04e467ef53392193eb69ab96d44f850c772ae9164fbcac93765364746774e4e7fab52595b949f71b39018259e58e7856d62b74e0177cca8a0284e185904b984075e7193e1500c5585d2254889d583a837ac6f8b5caf7babef89347a6158457fce44ebad0b0c0acb5d0a9d70abf8a43ed9cc69e8950616727160da1ba0f8b0c6262be9e59501a133f3b5059aef8718a36685a972794158ae0026262f80c83efb17b573151daea867994cec38dc38b24ef9e5a14bef16d554e2523920ee857a1145a459bc019166efa76b5bd4ec327d1b8649a755e62c97bdc9cdf6827ae34fa39deb5c730f5fcca92869c78c9bbc880607d76f5370ba51effb1cc2abc3c70c38ba01433891112bf2622e502cb9e5a4871b6aee394953f69597210e3021d58c7b837344f4bc738b1e114f2996669e9815a82522d1735e9d69e419ff8475c80f06caef8fdbb6a75d627c2e10c6b9fc4fef7e927c4f0becccf69fac490a7cb035364f8499b5347ed141b0ee03b0ac10a12cfc568e0a31fb6c83d72c31b1a0a361da9ed4af623e45d5e17d22739c1489092ac08e298f51529c809775eb11469e244a0d75a396049efeeb5c400b8b76d7e849342e28128f6783c535c617c92f65323f85b06deddf4c41bbc62b3984120ee4a11c4858a8e3cf0594b8665ce27232631b23b16e956c51f810ed30daf6dce2015baf80a3bb863e6f51482ec02691e26cbb28f9ddb8d564523d46eb4c7dc1225e9dec48a1de7e1db6d96a5658a1d2f1407399fb7ad19f0ed080b0155e4d5dc89c6314f5941d35ffe140e420570ec829df8d06a1bbcd355c25b692c135c978ed23bbaa7d0448a682c5b61d680c05d87a04aaf90bb2348cabf2fcadb6fd7379f50e0de15926e7c8ecc2181995b519768870bb44d8ba8ad7c43dd57478e92cd35e38591ed91943f1c944d867a89902ece344c60423d1f03021269b1988ce5cf3eeb03f9b359999fb4f2d5ef60cfc6b5ba9a716ec12d2747cd6386b39c70b900d1ca9f3fb750e9dbb54cd5388cbfa085faf7abc02381b20849949c89c367d207d6e59bcc16e739a96a9b30d85e488f6f87c7f412d67808f4f20e3aa5708acca6ec6ca76db3498a4262f93244097204cd7163dfa30309b75e37f54e174b9295b6806d574152d73af463c2cab737f08fd1efc94c2cd19349ad905309ce5905832861384eab3c3f3c7942dc4f7185024886228e8689aed41efc2061d0c3444a94f1ddb425c80b8421bd328e4610be3015597b4fef5192e6621f30dcdb42def37c5b636e7bca89fdfc2fd761e823c49dc0d85bd1b624246171bea83efa0ce37fc03c7eaa5960403c804550df02df5004e88e34a73e0a65e88cde61e5b794e85e911dd2a836cafeb23464f7d5b155dbbeed9ff910abbd6288fb2e6b3ce41710a4555c1ad416d7809d38b8d9e15ad6c49dd38b1629519e6e501bf80c5b8f3994e00c1cb659aa7e853307a56e94b006fb42544cd73a25e8fe7fd4f0c0a191e2eb3675e4d2f3cbbdb88cd072e26ecce2e882feebcf0a48e4524d43b7ef36e381435ccbf2aa95a2e14d55ea8c86aa40963a9dbaf80b15441114b9703d24a153c66e9d3ff96a03fc2e69123a8b48f8c9f51a97cc63a2616a76e8a0b7743faf462ce8bc22b711685dad5db33eeb21b6a36627d359a4ec58537c6bcdea90cc9b3566e1449414029752b42471b2916f374cc80a90b3b9d79adcc8bbd5dcaec9c21ac1f841718bd59d1168f7ec75605483bb2f07b3452e52a1ac67bb8f29deb52f5205499ab3a0c4cf41ca51ffb7ed047a552b9c31cc82f0234873c93650021305098b47da3311f6af4aa01ba27193f5f604ca5690eaa54b51611115114901400214d21805522011845ec08ee9567d9141bb5520c1d413b33b5b45684ee0db3c84c3e9b671e00efe01db8033cf0e9e8e80ff1459a6aea30a916ae9a99848134b110f611e4cdaa4591bdb2bbb79452ca2f0427047d03493e8ccc205d76a8c02102098d3668843e5e84595c08f98c3bdcebb416b5238211135ef932b2d022e698c204359a58120314a45ee26c60d4c31d5084f4120707c7de7ba1f0b81707c7e220e0849698982728563cd960e6c30969b0a5807bc02ea615aa0894129e26153db08b875fa73528a95ef237c7125cecc14319f8f63324e33a9acf66d4027be4cd788397fc33d50acb9fc6c06a8f875a9102ffeeb4d674422f1f277700d6a79c576ba5d509cb6eaab5dacb5373128331deb8a7d9d3cfd31048db3446dff781e093d4931396aeaf27aaa7312c4a592c16e7bb022819d48a4fcb538ff3d0d5a1732252e1f403d6033b198d4c483c4966e3928d46ce5e908c24dde021e7793b31831404a968f4e1b97858640987bbfe6d6e121012f127621126a215ef3aad35258d4c376fd354a4cf745a6bdad1cbc7d999d122a6f60445cd29a8cf3aad39c97ae939f1745a6baaaa357569929231259d889c7e80334e28851eba88a78405d554a18063131c9c11080e8ecb35bee0726d277acd4f19ab2d324aa860c248a4bf67ed07bc5a6ba741f8b53a406ac722dc0607406d498ea1da122136701216a54f875b06984308f4efef6045d07e431560ab676363f381df017cd9803920d9e1ba5c598f349edf7b8eccf1c8b0db38873db0966f6085e1e5cd929f5eda74ebaa29d5e8f61300a6db2a04ac3838d7ca75ad5c230faded98d08e097170ec36f2b0636cec189b166638839c10308f9cd4694795c1ac542a8d13a759c279c249a7983e674501cae97362e749140ea11c3f54cc80a4cf66cc154cfa548f6854154345c564d94085146498109d3da795b365429d0b306d3039bbd12c71cebe9c41675396939b4348ce21e60713670ba08aa47c7662e092e38b912e23841300a694a4d3c759e4ec31d567fc9cd9a45d68186e0006a031bf9c484ef2022e0f26cf59e4b480b974be7063562e53670204c00053068a4584933679392b9019154755814da193001cc771dc0c05d89846849c75cc962a3339a032c3f1a509d28d3129a852f419250244a39cb4c9d6755323b4e8bace4befba1f2014c8150d96d0a31a8f850996a49c5046888513a32e525d8694b383998291f3016698b376527166658147f580226d8912c3a9c1acbd902a4ab25435029d0a52591c396138659c2c33c7e9800d2698f33c29608230abd149b5502a42ce0958ab6a8596a9f2c25980106605cc8a2589480d5a44c9c45054176a1930a71475a2e454724ac0849dd5059d21098dc14b07a7f9c38462c204182897dae3c95515adc54987d0254af54985fae0743ab7ae47043bba04b950a35275830b12932e03c6190173a7325d39afce1567032e557a6af2aa31ea0d2727064c2b4aab51b40a08249c5dce8d63ea386b0038c846c07305a8a80c0c2118784be7923ce832b47050a78ca14c48638fd187a55c08bb00e0e50ed8bfcf6d0de874fc1d204149be7a0e7e6bb7bd81bd81fbf85e7b5d8428842880e09e5fd206df8facf42e98b76ddbb06d03e58d95ebe76de62eef58cd75cbb66ddbe06fdcb66db9566b37cc6ea05e5e0b5abb613584fd6d9bbd2451b9b73993e59605599323c1aeb3cf711c57c1e738eeb23668933b4672d86fff5ad98c95692bdbb6cd6e59ddb26efffb6c0da762ab1f7e6961ddda2a92e82c510d499623366d2dad8399f6bc6ddc6bdb9edb36787fc3bebfaf6dab3ff8b767d5d905818988669783898db45e76b976ccc22c77f54de66cf939ef36dc2dace7a7b6923b563f6cbf39fc861b447489b285f5aeeeefeae7ec5c7f830dea1893e87afe19b2b4b0cebd87b7578e70e1ceed580d5177acebba2deb5fce39ef186ed50d32a8aa503961fb83597c4d47909e78897aa163eb411a42ca41c54469adb4d6fa2a6bad24255d362c3b8461a7352a211dacbb024a6fadd6d29bfa95ded07b2bbdf4de4b4ed15ec94a3dbb5dfcb5d65aebd74a5e8aef11e72ebc618c319550c718638c311691f8664308d58c25aa0f442a2a169d43a7b529aa292b43b5e248e0d21d833fa01c61d35d21ce03a690faedb43685a4875735fe410f11861ce34c288c9a21b2f081089b928550a6548461c88c2e7ce18245479418c5a44992f0479128f5433cb242c88209238c1a285e00e140f4f1b2425ce1a5c4a80298508716914714f10427a15552102363d00b4e889aa0101eb1b104c6b8c20c8974f1098dba9c187fac441a973069ca98c34914414938c391a620a2181742155208bb048d3364885b3a3081cb06e1142e28645cb9f082a44a15a81b328827625861c88882470c3842204164128228e5277cb28505962d1a5c8d09a350892a5cd1012514b364dc71e4011111878f51041d218f12683081f0424b0bd1052d3c1a94194798c1842c2115a9f184264a4821ad884883d6c3425874c2d84204b109c8848b31852c3de2922c26ca548db02d220c296191a67049492462248e00247ca18590c9905875238518a19653ec000b6cc48165672a4cb84297108a8a68f424ce96c8c0002424a4da3192a0c2d8c108e1d1cc8a07a20c5762e20b57528c208809c74c2959197a1256611a374022f6d4c2323d72e41863082599f1e5837088159958c38a0ad10332a1972fa30f965146d40c265b92cc50144af921a2b0420b13469e1aa2d30b240ec6b3ca8a10489512323063095ac644116b3c3b1183784e1a43300aad0409a576b8c031d2101a57a0e1e44558e5421c42c5679451d9099dae421c53e2d315714a5408a6490843d28ed15010d1c60ef1078e318550c885468c17230b2e3898e2d3c2949d305725a6c4a22b2444854f4d58248d2d18894182883c3b422138c29a50161a545ea470a181141ff194b213bbfa32259a574225512351139128090423244144a81d610f1ce309a191031a629217221417638d283e230a5176ae5c4599f2724545545293a32471ca484b90d0c88e1f1ce21321211a2ebcd8e1226c41cae703a91d32572296a912ae8cb128234d442501e0d3be675864a9d3597abfae4fc2b2480c6e67e5641080bf9f02ab9f84ed5957ad599b2c7f6686d4e44ce9fd66ed19b2c47d3f59d697ee21c98ec7906ccb366c036dd83639e79c7bfcb9857197b620ffad4110244b0bb3433f346fbff52ac36e39597aade75ff3a2d603b7ad17f750862e12b4b01ddbb1218bf3912b1e57255ca10843d51bd0692d0ccddabff7d6925bcdddafdc7d4ae6609041a71783dba9f720d6c940f3d4a7fa27677bda6ba6b46a9e1ac23e264b2da325c87da961dbd69ef79a2522c79265cdd8626badb533df69fd5b6f4e962c72849ed9a0fd48125dcf64a9d33d32a77ef79c8bebeb8d996998e6d1300d9b551e5dda99ccfb4f96bbce23358b459658571ead2b8fd6ba47f7e81edd73673396ab0a331486c7aaaa1ba0d39ad59115ad3ba0d39a156ca877af127365254bdbb5e7911b86b92d03cb2dabc0d3de8699bf9de791e5fefbf5e73369bbefeb68becb5dce6419b6deeba8f5e091a5d64ff3a2d683fe486c6fa8c7410589cece9024bade7d99bf4d8e70f50e7c2da2d4c901c9b2e2ff3e13f9efefd876b1ceb6dd9096b5b4cfcae80d57ab8501d58db3a5ed1dfe9ebc17df8b3f32e7bb6a2fc7d5a32f4f7f16f69516c8767f828e69ec0b5b6dcd8f86ccd1e1735804414896b6772f92a5252df9e9eff09723481bc32f0e9f0517040e18620bab21f0e3ceed2da2f598d4ad978dd50f35acc1f6ede770adff2c206bc8125bb26c655affb8febd68d7d66e1dbfb66e1f5b6b5f16466363186fdf578c3dcec9ed9ac1187fdfe1e73ed2c6b8ed712e080b8a708a3efde0f7393a7f6fc1d53656437c36566bb5b2ee711cc7d918d4d5016fab89e6e3c94c17aae307c51c3dd405b97eb9e4d4c5b35296ca4ad9495eaaa921cbccb46ddbb641fb1b9795b25456ca4e32535eca4c15c86210475cd7715dbd40d7236aed0579a9d57d56ea3253cd4c5d66eaf573a6a1c94bf543bdb4f01e5d0ff8676a66e2f635b523cbbab7ee887bf17e485e5a57d3226934645eaab57e3c1db4d6dabc9495bacfddacd45e2dfa4a994adc99aba857fcfcc5c104a752bd243b52efa56ee3f6c7e369ed7db5f2f4aecb4bdd2eebcc7567f5ce3a067181a55254ead3faebc2bebfac54b8bdd97f8147a5ee515eca4b99292f5da80e363c4febd7de831ef7347b9ee75d3ed5534354ddf37d9f86f5dc755da77b86342cc9d175d7bf5589a912d2ab4c3058aa6a7470cb3ac8c2355c3c9cf5f4bdfbdbebe32f51fc2e2e976b36d31043430c4bc40454a7444c1c11314123079113a6d65aeb5d817735fe46e8d7f7919fceafeff559ff7e6d9d6e10705d777bbf76278898e0d2c1ab35cc78e9eae0adf6abc596e7a8ee0ab60634c0e038dd79f4bf04f823c1eec5518e7bce711a34e095d4bfe9ea3d95a095d8e9db082a95977c50ab753aa55debb7f8a5a3af4ba773de095edfe646670d26b257c62c03a31ee000eddbf6dd6f33d7da19efbf172553d431198e69f79c88cd563b73ff73d97bbb19486d04eecdf9efbd37e79b378e7b8fecb287c12d53a64c198d5180d452dd533f607b53413cd6d95dd7bd08dabbee71f73abddb1ab8bf3d05f7e27b9974cf77bffc55107c89a016607a33f7997b10bc01c97244bfd623f37b2ffcd2f1aaad6de506769f3fc77bfc99e4b08273d880f7f83d12ebf4ee55d6bce5a5fac1ea6cb546016526ec6a98866ddbbdbaf274bf791d086a1edd13d46f09ddd6b037f7ebcf1886dbaa7b342c8c0a90da94b7fe54bb65f6a672d6da0db49f12189375fbba590f7b083bde306b73f48d88bcfb3e9801b9edd878c5eddfd98501f503446acf0b4d48d1912325a6110cbf341aa7711aff42a309293a72a4c4b4cdcb7499ae930ddb312bce4357079bbf19bde42d5b711eba3ad8fccd175e3b0f5d1d6cfe66c7b649e3473c7c9a104edbb10de3b49f5e5a1abf4fa3595abfbf6320f891a585d15c1f995d7cc7a0868064c8f9947eb08295d47686eaed91aa128195e852ee82a496fd2dba45f74807d6e72aee99a4e0725b90ad43751bb211f0bee6ad0e8d53f5f356c72aee3d1da978eda5ab77789b7ba37b597ba965f686fb1a98a05e562230b37ebfc38d987e69faa5353028fafdcac382a81920caa4e6a91f5c44b0525ba20ba2065d0ff7ea7c6426832e884b06dd231dc2ac6603d5d46c353535bf61351ba8a666c3b6595353b3655f6361ef6ad1b46af8b7de6b61d78eedd88e615e3d1962a07a6dd169edea08de6eeb35e79af3aef5ca29b9d79cd65dcd5f3bae5ff9e9fe80b77da9b72659cf794b7f8bb75abca5bfc55bfa5bfc29b89cd3aeb97e95b86bfe2c9282ab75abf514581afd3333cf5bff258b6395acaec5c9b2761a92c5694aafd39025ef5afc69c812773e42b3c8efbb9f79799deea91fb63033be3ddf31cef96f18df409c6fd83639e75bf6dcc2dec5da4fed66f1fdacf758fb67481bd61ddbb11ddb3156ed9e8c01ea07e8b42686cbc7dd7bafde9bf59a85f77bbf81838a0fd4fee99efaa1e29d9fc5daacd73cf959bbd43f39019104a8942b9bd521b3d100900063170000280c0a0684b220c6520c89e6f00314800b66a4385e4022a24b42611805511002310cc3300883000803000080300cc620227a1c0056e5f46824e78329a196961a53076e15ae8324e60cd7e66ae2836fee094f4c81727c6f35ea17cc69f2c5cbcfd476b8e2ae7d32087b5680b2b6bdd741be07c2fe26ce0b00ce9fd3c76d3ac0fffc959d25961675f22eef73b502c5339992c40e85a7927c5a3f699bb5e64a87ba4445e7bad596691fa6e0d1214826127283b1853231a05fc0d9b291dd15cbefa152b0b5c2bc01343164e4acbd8b24356bb967d68db08d2803b720c73195589b3ee2b9e777497eb83c1bfc0e20e20962f4df09c136350e5f7f3b4c64e73d6952e955450686711e2d88cd5b0e31adf2f0cdeb89018aed7740d4510ca1ae1cbdb2f449a206ddd3558c76e31a4db88d78f9dc9f4f97746466eb36742fc888a097133e84e257d4453656164129bf1a1daca38eefeafd1241fab0e466940a52de39ba79669bda2a0bce92006fe327302b742edf2f7331afac04cb011b4c9571204840a99827cd4a13282a6e093d71d9167ff7c5b4dca0ea2abbde37d61333620318fa2e7576b03e0e92d52db65c2fb56c63cc6f20d1788f782c38105fae86ac8563c41edde1024f4af90a8390834210f8cc45358fb3bcdacffe56bb0302d372a9aa72ce15e17b0c5efdc7df70f4032c89b4f449b34056f906bbf8d37da7a8c2839454267bbcf75a0af4f42f53a62cb27acd6db46df30872b2f1fcd242d6df67f186c03200e70b158734d3bdc20d5de8b541abf58d96904e3267d59f874bdd78fecfe5c02373a922e08ce38836dc61886e61d6369470015c5283d0c1ae7042425d79394f602358d2e3aaaa1104f1f20c542ebdcf9a4341b9657a4059018c7df60c6d0a94fb2aff46cb392da8f01c3c0314ed7b314f9fcd7cb2b04a926bb15d4159c5fd2f4d3d9ceb8e17adf7d96563f7da4839157a74a64115e39d61f732680a8ccfc537c2c0cc627b83caedf6b0760bd14fede67bc5ace90c300a4248837493539358e52b89234a6ac78f8312ccee176a66322d40daedf7202151c9e43fb2835ea4827418071ab9de06356dcb2454cc29a6ba33a970ebb2038d7e99cd19b584c42ed45a1788139ff2c3bc15655c13b674942e81efcf960ad28d110ae7a6538cfd33671fdca29bb710366e6a6254d174d6070d08aa6524280e2565bba873ccfae08a34337d30fcbc9f2b73274c815fb86dac55b0321fcc87c0a44ea44003a42a951af6c00aa14b036b55aa263960486b69935a65ecb1b7586c2aa6581163d6985a500a3f442e61bd3ed12237bf8a0c32b94465f473b2c56bd22f7ad6d9f427ba84957f7bfcc32f11060d0ada7334020efcb638a7840962d8251c0529fd37439b1d37aa2b0aaf193a7dc1d44524cec10f9caf5206a69bdd2858e75600f8d23ac7bc89bb80939d974f396d96040f8eb3475c46f1f171a0518696bf0fedea227c51f043891c171b85092a20da144e2a06c7bb084efa983c3b416f8b7aefff85dbaee12f24514826fb7e19dd949b706d8de641320a32646c24d3f802fcb736c4f5a61f1988c610f2f029b384bf9e52389faa1cb2b7cebb2eb11d47d4c8fa35aba831bc8b1551f7af9f9ce1a4210f64fb19df96f86f4f7d6c4273905f78372424f538194baac31e2f4e2f4fec78d8837d9b8b5d048c5a7fc329267ed21d8eadd6942c5a719699a49f4c90591f87b2aefe4e22e4f574b9cf1ebfb2dfa37d2f30d3066525ea0450599edebedd2267817fd94f7cd4471cfb75ecb38f0f6e19935ad713453d4a878312782a904e0b5a892608f0000d47b7770d0d86b914c192053ad0cb9acbf0008d224dcc440929917c54b4843ca7332da285933cdd322754dc8200fbbedc6d822c6bb355bf87e8c8eafa217e8506c79bf49696308c9e0c89073992d064f4270e0cba1ab123e8d5aef31575ee6d205651d16f07d02ea4bf0dc8dd8f7f963148bc0b1c1884c0774fc42e2cb2688f0893809100ab092e5dc79435fd3722d469a1d728ff3103bb6c9a28135eba9861900d39030d861cd39016f1cc050fbec7527f3ac225709fb183ef9af7ab7b9dbbb0de33411db12d0e81f131626b955ba6cac40b62bbf420bee99a9bebc4c6e496929d525ca310e25d941c68b46d28f5cc31ec48e4c1ea9a6f8e7fba41e267283050921d806b0d2d9c26df0c8e77eed46a78829fe837d726d737d3bbc615b25f8b9983dc018fb27735637fa5b88bfb1ab3d80dc1d6260904727006dafbc5393574fa8dece46d14a0d82fee8026623779795331c520f565a0de1606385835d8a15b312e981170b90e49796b9a442f6e8a8287b358983559c263090159741e6afa74490da3c3c86c62905738a28ed5f2d834983f8c8b454b9237fd48ca8f6bf36ca28cf65864fb7d936b69649195b97c4ba208926ea580875e48007d9077ed22904132aed25b6b444e7fc1d60fb707a923cc12c18b72c4a8054dbe1c0e710b396ad145df23db18052b3c7e61b53443d6502b3ec9e9a58bb56ff0ce0d5d55d225c89e47367841953141defbcf6e5ca16c7a67909654159779760e531b2c8dd54f0a34ed55c4623ce420eb30c8c01af7b7b5ccd86094114350ab82ca7a841e0e346c21a20e3e4218196356462c798a3b8896460d9183fcf047c68ad852cf1d7dcba782e7078f431b49734d4e92e5f8c30593dee7021c5f660c33162c9fdaf90b449a53b1072d147e86d823a3126a88770b11ef7ca623c7261ff6edb2f6b14c8fe8c168b72d015344cd3deee718a97db5328e377b319a0cd5806e313dbb8ee2e55b4a91be2ea1a77d56e5a2a5f5a9c490e46bb422f4e7f4a9bbce1d9ba186f0f2405aec591e35fcaeba0c5a9398f67d846955c4acd10755c03ee7959648d77e7a65d476b1eb655f804b5300ce851a8bd5ed2297ba603e1226277447e2cba9351b00ca18eebb9caad17431243785bade75fe4fccd60429929944ecd187af55b907d834c9129c3ecaf41f12423220e805710f5ca8a6c02d78789e83107b70825368b27428d81207bb213b21d1a44432d80ad3bd586d0c797fa2dceb5c5bd775737cd06f93e1dea1c73c55396a30b055bc08c544bff35a3ed99d56fa7a342494b8030befcbb6343c0067e6151fb3cf5a5aca3b1d3f0fee67ddc56d0b839d0bc8a7dd82ead9fdbe230208806e0addb39350f0ff87e636b1f831294ec4aa43ef35adc82b6df00b1762acbe8026b6e0a9c4e0f5ae0ba1592566e48deac3e27176f4eee9372c288b67f0c84e449693720f46c11814bfb357205263626c7133ec97a3ef954063101ed6247724c23ff4c9aa9025fe4e2f865d359a3177eec10af201e103fcefac308f5ac78c21971e927b8998c0ca951c4af693b6496852590728563cda015d224ec6762cbdb945ee94848540347387b438cce74e63fa02c73f12ab8bc54533509d245e1001f9c4712a9faaa406d273a63834775e1b8bcf3a74ceb284dfdf5fa0edd4d5768933a19fbe3e8bb7dca91b94081cfb0f90ef62410f5bf96ae9d27077fc279304f05054351eaa57886ffa0710502b4a74570daf22cb8707e3e60c4d4773e6e836851c686a640ccc0f1f9efc7997178fe64349f14a064116327e3f4f54ac8041ecf7b0b95e7b1b43ed326967c73dea5bcdb1e231dbe25de076073891e697ddb4d4d3a3697e522bd2c09665ef9397a01ce47fceeedd76c6e457a647139b79f6c1f6f24192c96673864b3387f86cc275d777707bcfdea1ab03e8bb80d520c3b9a0dd2d8309446277addc39260e2ec21128458598e15de42bc37b3cc8bfaf78d8292c1c068747f22b33770f14f014cd46bbe569e00b66e4f5ed0fe07a011b9c6f2866e52ecc330503a53ee7773fa3c495e58407b0c38d0f22dc254b6b1b52ae34b9fc68ed2a192875e0ab7af67f7170d64252abf868dadfe259a3a02678c8a1ad25f4169e4c94260a44c8a89e006479103eff792d07ade865fd505d1883790a9c505d54207cc82355e668eec1b3ae3d7c502281894d0708001884badc40ad0386f55d78c4d48c378092c4d6e0e85aa8d533e41801969a96544f0f37895bf3054b429db7b0e62fd3239a616415a94cb663eee68c4d863f50b88ffefa868a41e9a9cfc5e5fdc3f3697a04939e86614910a63a2dee7cd946eb7ccb066bccdea0b6a64ad8ab7a155d97d5c13ac444f44f330d452c19d450361af759af577e202b07fca4686ac9f9f4fa8d76c0021cc5ebb225c49de58ecdb4a3f950a8aaf155df24523f5388760716a9151c82d8dfec1af5bb7be1a55751462cd1000726c24cf8300b5adc6bfd23471fc5f8d05976017684646597202e1442827471d23512f801c2ced98a66b38f972e10b2173ac2d1d266acf0eb4fa48ebc20cffb441ea3fdfada29373982aea9b21fa43775c941f44cb7b845caa490bb11da104e75535e0cf18005c668b801357e640ea3101551b5d8bc243a2f77b1df238e5c406f384c36c139820460a59728a6addd5fd4a8a642fabd89207398da3ff235d72936d23c7b714ec4f878c26735c3089c4691e1f5b9cf54f0303cf0c177491b8689a84cdfdcc87540710cf9861416d697569741ed9f6862327b65ee706831aa187e0cd96220ea7a2166526486e996b321577e8fabb243f4002740f48bcad2e0032ac436cedff7ecc6260b038c314f78082920c933c92bfbac2d4c647ad0cbcad8763e2b9ec96b704d076ce6e5578c7965ba51d254874821491442b487bf90aee5456361dc23693bb62bc3ca05c39180f3ef890f74da3a2dae6cd9b0486558cbac06652b5532823764567f1a599bbefa96208b3ea167739106821ef8612a56cd3b7022718df4b27a480ef57efa74cec1fcdd6768fe99cc281c2669b0e3d7a68e24e1050178967d584b86582a0d5c6d0327c06823398fca16e85491ceecdc2a0f4b45c9f05f1014d0d0a6266cf985aebe7edac9d82794ea6565a796728f847dace50fe0f522702226f369f80a713aa26a92d323f3862b58b6d25e636d82aed9a4f87c450009f194ccf7af74db987297621828c181e0fd65d1fc88b76f1bbc40fc6e8f145d73f6d69e224184215202be68fa6b76bb05a3b728c4fc8a9a454d1d0f5aa42f48951150bdfb0f591e0a3e8e850373d8315620991b56b998e71a1c6c36d6136c06e3b031c110a5e3351f7ab4fb4096280c8b795d4b0a0353864677ec5f79dc1f8262868b61c2e052c7023641b06ec2ec0e3e526f93087fbf587bef7d261742046789a9a9f530cd3a97b9b5f59023f6befbcb2f2ce6586645511381d93f0bd4567b95f12d9b876d90efc142f0a92c189b64ad7bf13a415bd4ab37a33927e4c0df59c4aad17c33017e1ffc50e68c32b9e38f7604fc5743cf3640ce78d220223781586627f0b67a000ef8331e149922ef0888eb3794f51658d14ac5989830de4643e4a394dcb0992cd8c3d39c1abf78169e678d0442a397836b4595c51ac4f28961332659b18766c4b594c794ea3e6aa65487b512b81e2b5ae02b8e48e10204cab69b84d851689e6fb5f1638339906b034df1177677808b1f4222ff183fab0807f50ecd55499966c1719e418d7a83532fc3f7ab9e9a1cf059d8c5654b7ae49b8a3c062f368972779aa396434c95f9384b93ec0c2e11ea92a7799418ecd1e8f27843157ab031368a80ea6c0a91c2f9d0e60cea2d93a7f96eecfb00214ec62cd8051900e2f2eab7bf088c5e2ae863c62ca782cc04f234ae09cad8d7d9e99d06052ab5f7b930f2617b8aef8a594a09a75e5f643190501da7bd9cf62a23aca72995f0761381da198090ffe2de8b99883884e67fd80a4a36472169c063788177dbdede6c96897c1b017f405ed4868283da2404d26ed321e83791a777bd6fdbc1a5afe4ef004d16d85921e4f08f0b8a082d9327ad00af07fd1d05c0351fb40d479469172487d833aa29bf2143aa2235e885f5c8c70adf4fbd03b51359845c70b7fb1ff21dcdc50b8bc224bd17054c29297cc9384490f4f7e86e6b05877d16f18f7aabb6db85e14ae9b0a4a62cb870c7d2dc05272e25d876e108c71705dd5c43a52b87abfb3e29f205e235fcab92a88c77b98007ef37db0b615d70cbb9557cf0e41b6340b71c1e5e63205b4a32748895d91b336c61d5e412000dbbbd23d88728824f863d8afd32c305723073b99826f4dc55c784bfb83c71a596929f8106b623f0a1c4d693b27ae63f909ba290607f11a1691784735dc9873de9bf2082d3ca5ccdd094f86c4bc9afd0e727277cf724477e6ec25adb6ee22ab1d34a24d6d0591688dbc8483422c6002c993322c666f8c01d46eef277726d41166a4276a45cfca53e1e0dca1ecae431d122929d49036a228a86d83dc7e3fc9008bf27ae164b592c27cab7e713cbee093ff3c28a43d774b0144312b4731d0a9d68b6a1d6939b0825e19b98c58a921373223c7b097cbba32a89db30c034de82fea9abea0dcbe5713012ed12dce96ba825da0644e1f0701136796e1934154596f4d4315f21c21e6543f0f7cc30d1d93b1304e3619ff725db74029507ad3157c59440ff9db1d03bcba842d5b0c2e5886a36d8c4c3520836e452afa951e7fcd91ff86b1acb7e4b82ea73e3848516268ad907fee4b1dabab3063213fabb3211ed333d452011d679f633e21145618084866f85a90555206cef2f6b469ef62a43f8a84c2bafe5cad3ce880fc24abc0b22f01240cea8ca8a9e390c2786a09bc74b57974ecc9d70e0354d36031489af18229732c3229f7b9244c2a6cdb5772139dc55a88b34f409d3e642ac07ccf1b00491079fa7e4026e106503f143a515073cac34704439a6e8773874a90d42913fa1020c6d85931a1c91c2eed0fc70e10c2f3f8978380385a07506574f4a71eed1c4892d9483ff55b851640c494eb8e0469bca1b3bd8304c70fbdf62b1ea6b70a54a08f3c47b5724ec908f9313beae6d54c0603dc9c5355938f8c72a906992d333a22353e2f0f883217274a6fbfd57f0a7c923983b8a2ad9a044ba9c4d74d74cfccd547372a5b3fd9883b2ed8273e7a91bc970b9e55be54b23b16633c230edec6074e31aa8e03040594dd958160a967d23dac098e6ae11d2400c572aa7ac54af82fb456871f511da924e1436c6e992330954bce5bd2ff6f6060dbe1a2bc70c6143e6a7a227db06db0287c192267f6fea46d4cd7dff50c42de810870ed0eb2724e757f470f3145bb85bbbb7f782fbc35c0f9537916817d3faeb98c3a36ece50fd0b253b9727ea52d76811e81807fc8adce91328b13a9091304399781983aea851bf43173c15f67084ca5ad400399d2f10b361ab772f60db5e49bcf4d36ba80a3250b08f0668a43ae5acbf725896537fd6a5a2ad6b3e17dd03f8baa01676a44017609253244f44e0231119699f2253e1c25354cdfac1f8ea571181d79076216330ec6d5f57db94d07ad019d31e58f6ba48e1ffae8791068bd3c1e451de0bf5719042119f1095a6537617eef375b23eb7711499173343212a25b829c80c0f9525bd6e1c8fe961f0c82b9984bc0726e453d2ed7edc9eddb815ff871a7db54752c6e5fac2c342526029d4f585da467644c0f31be3ff61c7b092a43fa68de609bfd3865cc11ba4305f61ba84fb735e79903c87815791c4a47ae75618164167177a7c17e66af0c9298ebf4501fc8250dbc43c94c80ceec773edb5a41346b20b78086a639ee291c6c3bd33c68f3dffc96f072cdc2b17c8614d90b41b8220fc139c8ad262e6bd6844eeeac30b385e8cb967f838a01f5e941c89c0572bf6a855a038d401b7fe5e5b93f6fe520c5176553adc757b22878cd6c4c4347d8b8201c7f7c356182f4dae972fba80b60d5018a8f8fe1c7f346fe4ad1d0f661790ff63df7077023810550cfade5b98f3ddaaf3e038cb684ceec507d983385dfa069cc8bdc81a09fc7233545d43e243befff92f341eee06443ddf70bfa70bf17bf8a8114933a0f05fb198276fb2d84e1222e1dff818487765399ba95e6f64e584f1bc8fe13fba37c68a613938b509871bcc32dc63d1cb5b86d1b41b3ce765dd019c7262e2ceb89b4b0d5804dcdbd35ac0230c81693ce106350a7f70dc418832b41fbe4782f0448e009b83b310fef40d86713cd43c1e28ff496b25066fe1de9d81a1dac02f1e6aa4bfc84b09925a0977d7b1e051d2301c7fa985ee5eec0456d3bf6b180d4eae7077b7e946f742e89b1f1290097adcd73919aa99816d1fdb08561799f70f312f4c03df11e7a46b4491e7b5e3d20073acfb6148f730a8de000e6dc32228de0e8e0bee53a56e19b2d028ba31d8408a470c480b6d25e4861def112397270dcb331cb7e19418a4b023e56511478f1a3f5548c00615636a12c5177c0c4ed3b64e315c67c8395311b9ce9efbe2902c7b41a4d6d5b6ad0503ffa0678c127419061d9f88626cde1458cf18662683c022cc574e0260a1ecc75399af32a6f1cc21c842b8bb33dc562f7d31db4bed0e4eb3f97d709f9033f2e286768c075851a51a4b597cd8e20e1ea53f54cd46c669826f3db24b467c9d9d287814b8f7643cf1482b51def95ab85e75cb49b6be9472482b93c24a5b9ccf2aabe718ee6e9a0d1c89f3390987ca823c3a4bcdfb8c541da240d782363932d7e58089c1fd526856dd657418e840048796a7ac8cc19d77dad1f472c7ee1f20f5921c7528a53ff6a6ecfa8b1f300bf7acfe0cb973e40d1917c4382a9139bee8f7acf3dc8c10970b095ccb8052f3dc6e49e9780782c70171a058dc205795d9df664ac03cd6d748c2d8943a0a2272e591cedc1d7bd6e96b989d0f838263f6ea1d05656b40d1376668de9e4ad8d745d03cdb78cdedcc1a1a0aed77f9cdfbc88ce8774489154b229ee1f7ce681f47206e198ce1eca5374cef1b5ee894bf76e04676370a0df7edbc1709360b4843f555a7f6bbcc79eb3e74ac6066d245b75cb4ffbb1830f0a947e4a4008e20397738a56b14ef5c2f7a56e7353ae8643904dd8d0e629d214f0737979bdbbe393ad886d6ebcafd06a9bf41a72a06c56a46d3d3fe6bdca61530615afd0651402b9655fc16d48ae836b13ba0024b3d0374836c118359460faac12c497e739d9ed4480128d1d5d9c1104a8a62739f7117297f58ac3440e90f1845c1cd7353a710b3a44bcbfb4ab101cf849723f4fd76437610c3c3b4d315dac6a44f9042d4bdfb6876878628d9b4cf664177555493b10d44a73c3fee2bd941d607f037b56be13ee1c54b9fc0e250df6fe3081a8c20094c229673cdadface0d93a351d9a7125ca9f84da8481be8bde7049398092b77f1046f71541c6efa009759559d60b18413ca9754c8c22754c956b76ef9c5165b71476f9ffa13ab1d6fde39027a233c55d7207937219eee3f9c182533cd7f70d5ddf45ef11f6edcf687261f9c7d7063df7d22aa56a0cd9933f8b1041b3d47e23d93d1944737c07cd81e1ab9d76938edd17d080bd50bc0941a7f4f49145982c58ccb5a9194d8e2e9dd1a08a0541d738d7412397088819edd4b664c2bf0ce980a32ca102858589a9406433185fdc7d121690a4fffa7aa855915da3b2a0fae79497ccaecfd481252b1fafe6e95e269bcf4f202c5645228d3dc0fa08a68e2624def0450a24409f7f219124d8763d94c02091a1c1048c712c5a0399ae1660ba35262d076585692a4daa3c3b9b9a4698cce0bab4e86794a6e0c32ea11267170be9664697a29cd40f1663a081d084243db374f19820a5618dc04385508242dbe1d9c295528657eec0ce761ace47cbda7360c0ad29cd951480c82171d3dd279c815b54f75a226b330169a7f8302d180070791d9c64b1d3042c49b9cc19c4133447a2f9c09a5e4384162de119690d04a775e95b8ab63d9c9bd81214a06e8e769f0b1c25250df516f31dd9be0994a105c5043454cd34bf41410042896bba319180e7f8f54a4671228c3eecc4d48b922c14adf35df2a294eb65c35ed29b8f4f54285415838741c1972cb213475901b99cc8b34fea69706833a4662b17f619e87cb7051e43b2deafc51188c9b2cd7abde771b72bf18f534c365dae1ffd996b632da89a0fb1623f1a56c6c02853b27f0a235cf010bd28bc9f7340b46fe699a1dbbd55bf705af13dd983d49cdd9e7d5cc4796f2391b672769b95d956e20931c9f89215fbdfc400e852dce407c0eb05f4e92c3a3eb6614d910c9b809c378d7b30070f164e866d047d985cade96a196d5781e99f9152abf9c9830de551198dc2ccee27ffe9b7fbb6272b1096d0e34d0638f22d0b5641ee8e3ed579a711231bdb7d390939416f99871a8fb017678bdd7dd3a5acf124261688c3f9e0bb366f2801f8cbf3c4bda70d481a733fe16454349a4f9bfbe83d435c1389609158a5cbecec8f46f2e94f76f69ddeb717b0e87e0f7881d949de10819a4d7c2172f10f21e61362580136f1194ebaf1ca328424805d5d3e7c9db6dda019d595b5fc14c1dda79c97f990c5a82134ba6784b04bf416648567a98241ecf62ac55c8591e252e3b5110184a07468e50f343b5faa292dd8ee75e1d95141ccdc272da58e449ff95c7bd5756a064b7910549832deda64cc1748297e8d57ae6dbe00cde9cf4558f7377f5f95704ee9f2afa48dd77b9e85ed619da71b401db7ea141ce3ea31e902c243f152547506605acc8852f3205b0bb8728e186ed2acf78ff8ea4602ad7636df37bc0d4ec9465c7fdfe6d1d02c07fd04310d97056467c4f2c5b5f79a0db67a64bea83e0604d80dcff3745518068ecc0126619d66dc258420d75ea255f1a06953982ae3802dc0410f3b538b39ed11779990f74c10e3a229a0b32b973cac5c10bb1336d7b4cc801ba2fe86b036ae2e621335bba87bc0b0f1f1d92c8cf672d45536757c2e740d7f900f88b8891a5e1e10aa2150a99fcf32ab4168ab946493dcd661c455e45d18cc2c0b48d5df8d87348d8d2a2561e0023222dc2125e73e1985e115de03f1bcf386097c8daa16bd18efcf508217604e45e31363a2152d45c9a13f9e902c1f6b1970c624bd35850077c340ba9bf09a81d167098c9cc7b82a7d399502807d5f611dff75b790a0564d56e3a59321d97f7c698d1746f034a59d0fcef3eb1e2fa38d0bfd90796b85e553fb42620a01fea9f020410e600b1bc515f4674d19b14fd702f7475b72260a78d25abf6936ee281a29218f7b453eca11e71a53369969a7fb113ffccf83658e6a61094123d1d49f043e35fb675c50fcb2c5f6ba514f6fbc3072b871b881cca39fa801213a611720f0543a7cd9aae9c866b016c1c014ae7a076507cc419d6a3c03e9422dcbfb2cf9a891a240917b424668f551d800a96da9bcfe4f07da370c79ef2405bd2884d2c060b21f15081f68aed8cb4d560402e806e9c4ce9bbc27aeb6f494cdb027bb42250ec0f64e827fc7de8fe431bb35577b4d090e4ab3deb900bb48917da5b9ee8e88e05151fd688eb9ada52054505a02246e75b4be21794377de1430fcfaa3e33496b3f6bbade0bc5007886a6bb180bfab84d2a699511188116e3847201f62b1dd092113004434909481805aaef164bd781312625d38316792ddad5bef2ffd83a1230a0a6f7ea44c4e7ee8083a2c91447dcdc144d82288dfaf82c33c0b7440476a1132b24a171c082a023d7791a28747d7de92fc3004bd52efd14f4efa6071ed3ec4a2066acbcc9429d96730c598a2e614321bcc768ca2611a468cc732fe749b4db65a716889e907a5e9425e0fb3c64b7ed73151ab89e46b548c5c783bc890e64a38d081b10cb5914004a1b8117b6509239d85c8677c8493f2de4790030c2faa094da71304d307a1b92d1c1280cb569cb800cf9c3697487f8dac2c44b097f02ba041109da89cc1258ac0a2c2fce7a2cc8503c9c86ae90f8eee822146bb35904e3ec1d7863c5d04b9d06e4d073f3ee84a521a19be146dd5814646464081410e9f47ee118c979c0d632d0a183f36211d13575c3ed841c7268bd12612464481426ac4aa026f4133427b813ed89d744fa2734a043dd70cea10c0dd3c13885daad6510439e74474584d0d932ba213e57ca895cb843ea96f212196c21671d52a790dd114d0cd7187d39a8e60cd80df55c29278ebe844ed532f2a10807d61306ec658371362a8e1c515fd773f5c270b1ad7e27e235a82f43194d21b1374ceaab833e61b0ce79cce6bc4087ead0328a43a50e3281a2764a20e4c9aeaa2564a56e8d796890e5fc318b800264521bf7ea946b7364418770dc6e5e0c4969d0248a126a81b20a688d285139890c65e86c9c46215413275744e8b1711261893997c82086fccb69b4879ce1e852863eab659c426e73ce2986a6dc4024601442df1ab726141d0b3d374e6282243264430e8e53624113486543cf1a3741208ddcae43a6b54e2c93d024c191502ca830faaba3d06511fa8cbbd41cba6b54144d9d0ef88c056f0e778ed4f7dae7eaa765d58a93c808096574a098d0d8bb7971313c9adbc4ac84128900f3d72175326c4d0df6f952f7eede323095d02458fdfbc41b11b6f5749eb70d75a293846c35e877377c9bb916218ad114d3744e66a94a6495fd3d82788e666c8d4ba776295d6bc87318865d3141c59328689077209f1377af08f7383cb7c0046d82f504aa849ec446a27b50df782dd6232fe4f995a38b1baa5ecb580ec53a304d40671b7a83964108f5c4698408e1d11ded84f29d951230dea16a1ade44ab845b096f22728247132ca687bc2db51565b0e19c8d5a0c659d796233d131b135f19ef027a2264624e8272c252c7522862ed436073a31f4120e0644e81e2d83133a7e37d73294696609d21465bcc1e9ee4514fad4d1352224a733da116abd96a10839d36038856c1b172934f221f26d8e44a727e8da87ee1b510237b1544293046782b32670e10ea9b7c64b64b0439d6d30ce42365a61627e6239a1558283047be23de12d015982c7135fd084be8e85448b26eaa285eaeb60b4091da3652c4315e334cc43768db0446e628984d6847526a093ecc070ce0abf1c194c22420ea71d228bb6e722dc510a659d798266827b824de2a5c4770232219aa04d584e70aa355a20b71b0a89eec4b612ef24fe0941ef91ae0c677105c41cbc8b2a0ec59b5f82ded83227f2eb630e04b1be3bfa3274cc96b108c53a9312b899d0e80a39c2756109bdec42970b193420d173e34b804ccc26081256255049f06ec28b4da80d5a864248eaa24b1493b826819ee0fd84c6265487ce6821346d1c2a91410cf997d3680f31da52a2df2432de17709d7ceee9057497287f66c1d4d43cd1a70b3bb457d40155036a8bffcd62d48e3711223137b140222c441abbc00577813613a853115d3c85171e3249105ff76d8429f8a5e8f96ec48cb2a7a6a2da129327296a8d7043db00e67d1a0989af58daad29460c82f14effa167a6ea2faf8cb7bd7910db1185d991a9a2742f3c5304d1daad60bddb6db971588c628443b4b8b9cd389f9dbd8a83595e48916d8c544c4c0dec2ce785f5affd4600991e516691dacbda52abcf8abdfb893cfbae0c03b79f578c48e0264b933ee96337b8035c1e9f6aa5a3a29f18797e6ee9f23ba2180fb32c9a82fd3fc1799a5f74ca58f4090ce3022ad66d5f5e5090e9a6039351906fc5e78bcb43cffef58880df9df920a00bdf5e603e0d3bb2100eb7e816986d93e05201d7bed9a15fec7c6e1c5beab91d7a10c7103ddbb9bd3bbcf9d2435c6a792f151bf602f65a8b501124a4ed2df796524a299394017805b90521066f2a759d49979e44caa10db7e5bc695fc518db2cf2514e34fdea2eadb5cfb95b4db3f6e65c2269369b64cd9752fa531c504bb4fdfb294c33e993af43fa0c71c3387368ca6c98e3beebbacf256d32994ca6c7a6c76f02a3a74c8fdf83df1ec7d65f283d30b1987197b897a42fb4d91ce5382ebe64edf89bb4586229ebe33b634f675f7077a7d153d6e2d834565a69ad9556f71dad77840961edea1587af5c80a60c6a89e65052747c9ff457eca88ae0bfa7e2474f4917526badd59d73772f7d2c71ee9ce49ce338e76adde13e7aaafe66adf62cfc5ebcb198d77ea5725474a442d18eb014bc6c9ac2966dab07ee83a1f6806c81e709590007435c5d0ed95e1d0c6ddcb8a3278be9b328474701cde9d351409ea7c3c05820aa2825b452ed632ca00154a31aa598528b574ced2de06dab699fadb56a12fc469f547f63a30cf9bed60bf0d4fd420f638ccbf2ddc2887bfb6c1d7fb6d3dab780b7ada6e16aef9bf05043522b50c4d8a993d71e3210899de080180b8845c04787912826370ee78cbd73c165cfbe7d96129c130522b109a240247682033ce5ce752f9f44da5e7ede3e4792a4b4b14b1b9e33c4526e1b0b8f0e626e487456fe6d4a67edb97d32cb5c448731261f050eace1427ee83cdb26bffbf30b6ddc4d01143ce54460f413f45da5512012731c769254902b1a1686b6f46147d88fd8ce8e6ae910a37c680518d1a1caf49bd49e855349272d997e2b917ea326138944d2dbf49d3ce7fc91e029ff384b6ffafaa18248a5da447bd217da780d9659018d9eb3baa8fed549bf99c0b0f4db36a3967eab1f2a063d9fc5046bb898efa27e68fa4e9ea7e687b73aa5940f236e09d678f039f425f87392485f2aedc89d6d7af985f5b769faf0d61fb74bdf69933ebdeb47378bd9447baaf29bebefa71d59c0a0433c31fdf9331af0f53b61beee2c514129d19ff19701b95131e8eee3971e77b4fb59515974fd7c92916e3721b771bf711d89447a187193c09ad2e790fe3e098c9e22fdfdd26b8fe32b91be10bfe89efbedb1c69d4c6cf85e7cab50fedc93e56719df8663303c6d8ddb5a8d1847ac8558ca9b3f0d4b89e3c7e5f849e92e6594516af193af61770dbb477719dd23183f7ea8223ad43b8caf4fda96657cce3b5d8d028126611bd984261ae0a9ca42019eaa2f31ae35681a8d4fae82c53c51c31578bb5f3930c7d0de3ec67801d2f670461a70c2967d424c21256a870ea31104728cc70f9e1a514f74fc17f4eddbf093fddcc6f7f8d918a38dd3da4f635badad3feaf5baef67b7addaca325757fbcf448e41ad301f85445baba14003b3b795e6e51a3942f9d031fcde7bfd56cbd92bb3fd6efc5877d09ef3784fdc810d66443a9c41a3c30d8674b8c16af5421d79b1e4d6e46aab6f719fd5f55411700c6d3eac2b81f604018d0e45e05c8434f5458d1071b4bf1d7520626855397f1c1c8fa7684c311fcb9e798207041de8b0b4b5f9a5bcfde5be725f9fabdf7dc7e5df9ec5bc188cdeea41bd304a8b0ef145e1376f2335cd5fc288bbf4dec5d8b62f819becea77f54bb5244960f71cf769b1c9ea5b508e33d2255021d0610a73faf4748bd9bf10878baeeb1e46dc1d5843fa9ceeeb77607d12d739f7196f72f2c8d13d07e23c63b27b943c5b62913c5ee8cbe700b576da69efedb145c884dfb80d82e3a93204dbf1b363c8ae0fdba163d7df749cc1b9727a624e7b6a6e60ce9683df7f7632b6afa02e117af83d12d3732f7a1147d7d086e76c403ac278b876fded613c58db43bc3b1855c656378e38f146f39b1abe71c4899182777b99e34ffd857d2ae5016ac83ddf849826cdd61e833532a4e63b7732b0ef8c5fd088af5df375d32d3b19726767e31748a309f475b175c9d1e98fbe1f77f836eeaee9d3bd8fa526a3cdad125f5a20c608c6393fa378345645548ff68fa03c31a9cf496da8efee608e47540b3ac618659ec7057f43fb5686f36d8e7c5b3b19f2ad9d1f61dcaf9b61df050a143aa52d630af911bb8eb6fd8a669751528465c71d9500edfc96a784c26f42ee73a7f3dbfc755faab5fb52f7d96cee0b6b95324ba943afdc6adc30b16d78bb7b1b746baf33e1e1eef147fafc6913e93169eb6674332c8867d8af234c1bd670cb35743060bff437e8b6f5a5e46437a3f47df54bd5dabfff61fdd2e75a6b247d8d3145f7f9d3bef4f5eb9ef4d9ec0e8cf96937c3729e92f9c3db17d611228862b3929394d9f23dc9d196eff5c3ff1e4594cf62e6f87334fc1b617f1294db41143e449e5fb4c11d61495c9ffc744a9bea48d9a6da674d56695fb3b56e196d76ec5ce8a0f09b982b8f24ac8db323ac75b4a4b58467479c1af2634c613fbc6f234e68afd760bea55ca4a0cd461153c4f72e488eb6bf2cda1506fdfbf7e327d36c8c6d9a1de3c390e0d43e82353728ed68f84bf0068d6aa9a560ced4a4ab606d1c29daf431f739d32d7f97a56f5476b9a6e338eee5471d3983a3d904fb1b8a19cbf35fd890fb3e6a8cc6ada27d3f23c1b229ddf273727329e98b1a0e0cf3cb4dd39991c11b72c7bf3f3b1af4317843ee0db4dd045fc4b7f25ace3e0a4fc918f5ece2fe1368bb36f10ddd8e5cc4712bf4a9539dd2f419f3e976fadd08caf368d7bfaba26d3f62d9f36910a0edaff9ecf9942797f0ffed60d4e8c8adfd0dbabd8e94edf83aa51dc1cf6f3b1af52f78436e0d0c270dfb112c85f66bc8d03b03feda5fed3dee6bdf86f61a98a3bd7f60cb2f7edafb67893b9dd2ce408c29e6a4e1afd11b723bc5608d09d3df86dc16cca92f74e4d6917b933428d3bf65f67d4b1fc38ed0771553e5aa988a55821224a1040a8d56a5ba686dcf69803d33d5f146d67869685ca871953a95e1e00db9b9ce059523e0ef64d8c73f3b1db96fb5216d0872f41b1d7a04c410f8e16808981f2490c20c4e10c38a081bc460884f0ca6cf98b903f503ccc816580c47a8a38ee4030212681fd0a0033fa63d9ae470a1053cb024518cb4d05c4032830b9c4f0f530d7c74c800b3e005664405606ca53fa4de06e693824dda11e653b4c3577138a410b1b7b0fdf503780822ab01264688100851a9c2a8549e9702cff33cd41795ea0beb88080f2ea069610854110c061e180c2dd80b3bb02132958a042a15aa04a1e680e43c2c614715b8457c94976aebc9535b15522b8e2a2398bdb0c39531ad282d6acfaeafa5ec0a2392655730a49ec6b017966c6e47d80b45c0c088fcc060b0e2a17a4079a9418874dce1a37ad0e1bf4d0a0590edaf59dbffe4c15ee8e141a5fc598b78d0f12945f9a0e9c79b88a33dfd2ee2dc1057dff9eac81dc2ae1f7e08bb7e08db3f01bb067fdc39f7eb6b5f0d064d40c0ae17ac9181805d1f01db3fa6993407d82cc29b457828984ae593eacfbfa06023a0c0747ffaf5dd0bfe219e883b2cc168a1289e88db82617d8bd241a376dc5e812ddd7188dad39fa04410b3de43894e9e037120cee340bce76d6cfc65435366a456c597d24f0ec43f1b13b17a447242388de519ef052d286ff41b7980fbcde8023d1b4fedc8b4cbd554c1d538230f6e056eb502c7c36119d21a42f43f34d0a1241a72110d0db980843c2d5751aeb8154e263c56a5052c4282f6cebef41d7f78dad631184a325b7e48e3f6ef7afc0668b77688370aa1207428634443b12989e69443128bfc18f5c9793a209fdaa933b81b0544d39f77479810d78fae8fb9eaee8aa9780343480d31355d8790d56c47580f991d6ad911d6f3b3730946dcfe67249659c491b31d1b90e70911479e1053b8a8b34e561c1271649c3164ff213d3a5630d132943fd8051dea3fa93c9b9a927206944bfea8265a6e5412bd839cf199cc53f28ca7e4952c309e927eb482c68f42a27b583af499dc1913fa6ccb77327e5346e637f3514cb4d43e94ac148a9dcd6dfb8296b2fe4431d1724b91910e492c72480a4dee8227fcf7f4799eb26ff157b7e5ac094fa15c90bf2286dfd4d8d38d2d6a012ecb696b8cd44b8c36d207ca35bbaa6d99a4e19c27a9a4b57e18716bb0c6f4391a079a19994c46cdc866b2996c269bc966b299ac7a3be8aec4993ef8e6675bcc172d429368afe2f99a5a481d97377a2d97377ca76d555a84cb1bbe5e355a84b2282d42599445599445592bca8a29fc294b66ca421e9c40f04a042000dcfc6e8b2c345b31554c2d5d74184fcc7620b64f99ddb250869285b2d0898a2fa9e3f2466fcb7279c377da3ab57079c3d7ab36b5cc178b4e2df3355ff3355ff3355f3185ff7cc9288bf376d05e0913213c383fdc62c58a152bb6d98aed8f81288b325994bda2cce572b95cae6d158bc9988c19c9188bc562b1581acb68c668e446710003d8a1afe20006904afd7b1e76adb295f93375f335da8a98e95969469a75336eb3b3ed1571f0fb6f5a5eafd7eb856d6b5b459cfbfe9b8f6d45da56ddb6e2b655de56db762b9151c4e9626464646464651a2be2d4f7d78a681c189a4c190c4968acf8692c8da5da1a6bfb7b8d451c4d4c2c168bc562db5f6ad815719c4415198aa6994c24487cdf89132ad70b6d53300bc514fe697498c908e14c86caae1165d9d97cd996f7c87c861082466ac1bdba0df0e8602821810f328c61a18c0fd11424567a932e3dc4f0d3a22cbc488283a3144009e263254526efbdf7de7bef7dbda0d9cdb52fa702149f7db5d63f53205bbec30af1606d931e9004893274adbdf7de7bedab342bd29b74e9418679afb5d65afb85850ee480e50439c05e51cadc106484a402d140455e41308009924581a2034c574c563e86a8c19734678ca0f0b8405116274508716608178486e01012640b111e5680831cc0204848141894d47befbdd75a6bad7dd55d59244b7e54e036072512892359418acf0e64395e40810e4f5a48302d00508f235d86944c596243500c4132388fd593590e38d8e1e5871d0075091a3243cb8fd01348d023082b2f701204430d415264901905f633832ca1065790168278a2450534b8103f8acc80e001882c4ee6953a83ac2671496b6debfda4b5684f9f74d45a6c93273dc86618a182ed5199046199f766f9d4dbfccf20698670820217662e31414b82c6ec9b635f6badb5d67bc4a0c10ed90e2113c4a0ca4f972015d0345287b5758627b296b5f755f8870af5cc0064c5440622b881eb0557ba2831d26221280725d4beaad6a0479220264270288181941d1e57b884c9d18212e50848970211477cf852454b0e244246680a4f6880d2b81cc9a2a41e113aca32efbd4784c264f9a12582131014f9410408172555862767643db2ed6b2f4fb20079f5f921f6f3a4ef35e202185c1f5a60422bf862e5a84c0a2d6e0cfbbed6027b42742485446982efbdf7da7aefa5f7de7befbdafda7c983d8a04d98004880d647096989624f1b33ac9b60811c1c48b989f4a0426a48584124a3fb933186a36f4ecf97267c71d65f3b10dad3d6fe8dc6d22a670cdc5e45ce8131172bfdf7833dabf32d4dcfc42fcb89b817fda93a7dc0695728b740be5ffed667433e4772e3050ac8bf55a90daecba9d86fcf927bf893fe5fd61ed6ff6372f5ee4cbc80b18ad855bce6682b4d67632b6b79b7de1db3b1326be187fc55fb7e06823ad39f5f167da9920cf7432eaadc1bf5d60df18bfc552b6d030ff8ea9e13ea6617eada1be3579603269eed4bd90af7d6fee177e39a6ce0489b1ae60cd0bdff7f3b39897f36c5425c43801050b7040478f8f14bf19e11db785d330085cf49bed3d0787f6b5ce0459c3993dffdedfb0d6b094619dca382be94512226b231a5e3bc29c8c7132d45150c7033b9c15d4800825586df92e27ac2d3f9439db77fcdacf50db57fbad81b3de98af5dd07e604bf7f81ec4b3e376f978cedff6f3c3326f539ba51de2a85fa86d3a411ac6d9852783416164436462856a6e697faef84af094e3971a96de8c0353050ab1d9fe2fa6a8fe28333a8c3116b384eda1aaa61c454c45800213031187848a2b45e12910d8689f2547fd07cd8eb01fd60c6536e73793c9ce12fb5f3917f56f88b033c0004ba98c11b6ec18f0c2df625c6d8ea06ad06467d37726b24dbf36e9a99cfce4ced467788dfa3774eee6620af933aa0d9dbb714c217f82dd8cfa14bcf1cdb0e28e067d97513f3a5170e5a366a0e747cfa92f84a68f9fbe8c389a7dfada06fec5f369a572c653b4e74f2c403e463b841d614b84f6672cad94f2e2d8452e6b8f3f5b9b3b17d696de6e8f6dfe4bfad20bf973d36ad0076b37e3ca2b431b2c168be572b95c2e970b03de72cdca62b1582c164bc65c2e97cbe5729161c55c427848bf3ca2374a8151c5582c168bc562c9988cc9988c49a199b36658ee68b9923d29d99a2b168bc562b15833952c45e6631e1823232323378a2f0f4f15136bed79ffa9944ae50d210034a57972bc7d53bf21c1a36bd0edd2562ca59c949a3c136f33b18d09cfba86a9c94422aa28ddc0d4041dd750e6ca981d6157bc5c21da117665087645e80a932d9fab46d4c6c6c3a785adbdcd6b3d34cd4705283f320b5a941f5b53827100b3534513e9341ab503cd44e350a403072325145b89194929ddada0b1221b028303cfbd17e39f920bdbb6e58c03161ca47438146dcf535a0a82603c01223c654640e547119a1658041bbf1623b1a74fa1fe3b7d68d035682b3e3d7ea8951976f82a189c706bb7ac9f0aa5ba831ca4d936e6cb2dbf6fc49e236cd9c128b127582be7e27a57fedf8803829d0f3c30e912a9e3f2a6e16b2b1d234208003051538284870261c4e9030f4cba44eab8bc69f8da4a613978e9c9314a29a58c5296e89151ca122da4a6e7f8baa5ed0e316cc82557e8d716d502fc26b4f168824509264cd80c81d92109241f31ba0f91a944acf86c6a0466e505b302c50647da172827c830fc986a5072d89aa6699aa6c44a0a7bdb1136341b42a5bd7f9b148a7baf97836ea2a914f9b8453412bd82e6d19c8f0efa8a5ee92a39d8e1abaa8e16740f6d830e7f879e12c48ed4db0c01848795fdf2a3b6c0074cca0c30c618e325dad3a2a59ef2f343ed8eb029ae7b05861f5b921f1a50107ca5856b021eb6871aa607d5623c66c4f7a96bef7a22d050f40926258b94293bd49f6139775c0d1b784faa7de83b92b0bac9ca6b71bf7e3a6a01f04faefb48cfedfbf1f05ad88033dd99524a65a49744ad578a2137ad9bfbbefb9a5c907e530511d2a44993264d8b6be231f670e474f0f8163112203c1520d458e32603e4b636664a223d0be7483f3f1298b77f463165f44949796f9ffc621c40f891f8fc06de14bcdddd38ee07268e392f971c3dcbf7e0893411790e7bca6b4a98edc5c9a19942468cfca604715b90bd309e619839092d01550fae586b82dbc30d13bb0106527af60d676037d02095b96ddbb6cc016dd956c9de971190600731f2c0a6e040a38285cad0a6e2da21f6e1a814cd3927ad3e9b0f97b3d6da7ba994a1c2024dd3b60cabc243054d952d7347589517e7e395e48898c27fa74a0b4e236055bcac98518e38c1a4f8ec08a3b2646b213ac2aa085571ed509f224e7cff1111a74a0cb0df7895214c51b2e7839498879abfd8166c0f26650694d25a6152d0c0a49cb9f75eacc1a60c814de1814df9f1ba247b3d682f47115e510e76a8df051decf07594286976a81f488e1dbeb6a2c20eb517c30e940eb4ef08bba107ec061f3a68ea6db41d6137b0b0c357dd1594f8017be282d9f02ac1837d689aa6454868e892849c6c8d8492123fa2b860773bc2a2a0807b09892ae22c32934c2713cab4e261a28a15f3843f1603c40a335ec4ccb162b563c480cc9dc81c8acc8a162ef12510d11f9bd1b2082fc7bb0f21ac1d4b85f92e1e971dd3125f4289ae54922fc29242a40f568e1dbf035185b3e6097f590a881713bd3051778998cb8e2b5204f813dea7499876543157d2b565fbcf1705c246dbbfe2220b8463180d9ead746032788cc6ba2ca1cd078b8f66a495d1c86833ed8c0646f3b271ab9fed4f6a957884b6bf06ead978b6d776b481d9c86c6336a26d4bce9ad3db6cc8f6f7a0c73f98aded7ff2a1796743e329cf42b928136530d94b1eca52bc1d74385f1fccd7880906844966fba3bcc812f2392996d6f6af3e9e726dc4082ae3d9fea8957bb3b2b63f893a84d2d9f62f41cfd4d09a9ae9b20693e883c081d0c540a1b8f9c916733555dc2ef31a896101d19ad78c6b17b1fd77aadc6b146d0bcbf62731344bd00e86dc5f685ddbdf4407436efb130140010080e9120098449f0409dbb245c413fe37c79c73ce66b665934415b688996dcd620aff99b783a635dcfc668b4a649340d1563c2ddbb958a35a892a97a8c226b12d6a93d8966dd9966d59db9a793b684c5b5059e552bb682f7c3cad2ce254335cdef0f5aa552e95a812b568e552892a5125aa4495a812c514fede0e5a13a1b6a0ac19359343fbf084fc2c92aa2d974c4659b4485441cdc864d58c4c2693c964b2569d99a8048f3635a564232400040063160000181008088442a128cad21c53cc0714800b707e426c663a920564510ce3308c61200c008000008821061060983386d2069e1f4c64de4f08c332977187ecf557a73df2adbe09637ec1406646243280c5338ea0f6151d4d458bee17e6a2bb1ec3e8162e66747f776ed62b5cf830d3ef983a6eb4b7e3b0e806367708b2536712d9ab7ef5eee1b793e5b89d988edb9d0e6d86bdfa41890d62c030d317a351aaade4567e2310e8b530db2f5461a58119b29c80e88406389da3010a3422724b483f3b7200fb84be47fe8aba61ce019f726eb9725b037e2c94250014bc0b228340a20d84039c3f98e5f0c118670fb7397890bb77b990461334808911f51244822ca9f23616b1a8b119efc50a397f0073cdf27a0a1b6a9f35ecb98a1cb678627ef2c4cef6715c9690b95a2f02cd6b496f056e88b9bf42cb410ab0acd66a495cba80e944f19cc4059de548c255232a5807404038618759808b1500d86e84cff09f2dfdce2a9176b1b481fcba9983a54ced102a522b99449e9199c52eda636114525868b44c80014dfa6886abf36b32cb5bac73f18689c8a3044e09130b2c0343c69853b3295accbc3f39e95f3661cd3b0ceafabc40d21e269f170643e673a727bc8cd26c154005cb9a9f349a42e0bda38e107227641d204dac0f03884922a8346d240559ea2069fe78f4d361ab49cfd6e6d17f91e899f12979a095345ec7463e0824ab651cb45bae81f070eef046ead5871b5c70c37048af73283d435f3c5340715f6fb1c8e6ab688bf109bf46b42276eba7574bc5c4d438fbe894b458fddd20f8e4f2ceedb2ad354a7fc83c575e9f3f21020523d191181a3a92d06131517924c8748ad531e5aa640267239928c5d34e66d2da3aabbf5ed61a5187865f7d00e753b4050af8d51aaad2dc431540f29c3a204b93e60362c7f3c3a3403c826408cd7a8ca5676ce135c226f11a29fe1d6ac1c86ead57b83df06af829a21d459274b98e0ddca29b1c193d8971e8b1cc10d346b5346348bb26efc3343cbf7a0c12f3a49d628f54e58b8c538e15076fb1e0d41adbbc94f7ae5c99d9f771b083ca2be67bb08697488fd747ed720855b2de955d1a70915d9a47f525b9574a790453ceb4bb806204cb3a78dcf5afb5eb37aa46d4d0003644517e2def6b2cb0f1b80648c61a2cf807ec1b2b0df4face8e460089c81adc9d08daa6001e1de2c5b5a725fee50c05676b839b0d1efef638d005f112242f76c1f8325cb2534811175fe8208776b1cfb4560a69d4d0a95a1319feb63ae10f3731176f164a5b8af98310879d6b6e20a2c27e6081085c4a50775ba0dc0034a59042348b7ed5f8b09a631357c40e93d15bd6b946abf1e61781b9c6e5a616fb2345a8f233140e4b28aa1cf61f347ecd5d7a7392f9a9ef64ccb31e25377d91af4fed9d88d29f89d3b2364f5eac9c0b60fff469fea3e022a4ab2a98b1ea650769f1ad4c8bc52e2ee9a4324ce8244905e9e86cb060f38c87e6a0619dbe8ade0bf4d357171d049d8751b7accee9dad445fe2efa3a99533e0d52acfab5c646e665d3ea993eb98fed9cfbe875f1c156209c2a1cd5aa330c239724a8940e8204b500603c0359994372b0f4619bb73c1eac9cd80ef3322b801ffaf23136699f62f5a19a29e3d815576b9fd16f26314438d1bdbd02d2ec9aff5f81c9c19c054311bcc3d7ee09f304f9e25a5befd63b46020733d49ab0549d51677809b5e5146dc392829bf794b81bcfda6ddfb1ee3913ee7c0ecc5efa0aa209ba40ca9941cf69eeba7b763bf0676c07cf8c36e0cdd4023eb69a65a05f10167b0f7ae783e527eb36f036d8728316fc64a71b7ece5288b71dcd0f52d7c9bfbcc14e523906a828a5eadfc4cffe19470d22be7638cdde0ee7192de19ba5052e335ac0334b1b3c66b48367861678cc6a03cf9c9c008bd9720353d8070dea704163d329dbbcab4e6efc49a5a13d4032053a44acb3e83c14062a0448e2947b81f31463cb738cda763f84628e4cc717742752326a80727de34cb63a3a6ea016e86144b7da94b193a16137476b06551b623476d8b1c2e0043520082317fd2e67cb269638e4fc78cbbe406893821247e9dee2702c52d0b17e0dbfcab3d4b50f41fd02aeea0a8dae048f41646a628d04288f6cf11fa5ac36bd1667d81456eef0d6eb9862096370e475f3f0965e0589de29e8b44e2df0d28cdd931de2a7bb92a3852bcd4230bd8fc5461cbfeb21b05cb46542a919b2aac8d2a3ce5d5cd0459af4e3c97e7527bcadf82ff1d233574819c0efc401893c344acc938966feae7d65539a13cc35620213720e858a3992a0010b462af84ef6e1a5c6bb62d7dce2290b483b2da385460d1033eae8e2ba1320a3d8971bbb7dda3929b8eef85c740ba852fbf32818024759e8b8561aef408466289108972b3084d892bf745b62df80bee1ed9bf86785f3d6e75f64f15b2dfa078715593dcb1121df0b780d9e2c5802099d8245a744a65a08ac532ce1e3277bd7002f17ff14cb0acdb501b9aaab43d7e49172290bd6561d16adf57d60d585ef9579abf365171c0a0bec7bb5cc7b086eca98101addf7a063a27342d4433b3f4f88e72140338c8991b3112b4cd014f1c85675be32382694bd0f66c0e38740d11b28a5a1ce3c37bba16dc15cf57550bd05980f5035e3bd5eaf5d733af116738b90d37f23c139c08da1dd6262de8867a6b4ec3c0da6c8d055821ee6927732838a471b668ff35975f98d7af1f7c135d3d00079bdf5fa9cb9dd30428d22cfe0872adaadcce3ad10e04681d3590633b73510ace6638df06fa0167c3e80d5e86e2ab77a48e2812da2633954261e044d405b2ce2efb76931e03087f433d58a52ccb769d94d4f03f422fc15503935f2557f4f04c2c9760d18a164191cfa04c64366c9ff11363704111f3ebfbc7d70378ae644fe5d6c1c11c4abd7715ede3ecbb6991b89511db0d59093b6d54045c097e9915162e66a226e3048dd9e3af6cbd996c0bb70b785104ec142e940431c57ddf8dcc071644cedc01ac95a1dedcc742c08c7fe9f39e9bbb726258f071fcb7589e293d8c578990dc1e68c23044afddc8ddbb6110e60e52fc627b6d8cd4473e5aaa25f5af06c8f88813c60ffc9f89b6d5226220ad877ead741bf7d3bd6b7dfe4fe199d3cde4d68ecb56f7757821f485a7520f46e9e05217ca90a39d05034afe3d908d11931810127e7bffbe8fdaf01f8c2e087652d47c0cd3504b32b3a5865da510ec5654a8f796770397974e49024e4948068767af51ecd2fdb470dbf7935580537113a547770a34c8467a4097e9c8182f373e07c1fa11e4fee9900e81e56d6501569c4d9ce627868fd576570dcf4b978ad38fdaff70f491c5954965621795c7586d1e527f4394ec1c0e85bf1b674dbbe4e94de86dc0beca021205b16c991905d6a7e4462e8a8b197e17739dad4f16f637ffeaae6b891bd626ce742f3b8cdad2ee36a46845521610144502097866ed9ae0cc31e0e670f86670f83b107c781f603dac368ae974de0787a078304fa4d3a0d5e61ec5976ca1a18f8bf98c56abb5a73de5f6f69e1e9a117fee257132a4b1b971e735ad9974cede204664a1bfd2862ed34ee053a2d7c5df4d35f9b18cc2adb3263409a4bf46853086ea20376598b278e6d990314ca3cd2327c156fd9655b205117092189e2217d7d2ab8e2b35d07afb2b9bf82cfaca22e36ecad9cad0e323f4fd28866963777f607e62589b5989cd1c90ebd50f20be71e32cf5ac494cc7ecd48cc79a618eaffe4dd376a8c4ba4f7ca37452a990cfb2e62c351b44bc1ad77bf7f914a972827091d5f4724637f14f50db4aa5bb62d4fafca45402a8f26d224088415b967855878745372c5722b0e40c4870c8205f65758973fe73c181fcb40b05b38f66795ec5e61ddc89cb5c97331908f879ccd0e4430f87c8a074cb2f883cb31b42d2002a74e67a1d755c714ec215f931323916f70bcadb7ba85867601f5ef2dd773c90102260b06f3644ee0be19b2abd7c08010cb9a7b68c6144dcd8e4b2bf7a608a626c79dbcd4225a797022b8e7c8dc2b9f8be2799c9985feb2eda80c264893e4d86bf0461cee331b044d2bc20e76a45b90fec767e09d90c95af8189362073c14303152a8e962e16158ab48c7eba3420648dba0027530ec800ce20858e0d8efc347385472df40a191aa1c437294daf02b5239f25a314d096b372f30dd56bda5abc9085022e384042c6f4bc7f5a6a6200016606679fba2e1d97394cd80b82d115ff9127443a0f90a705f08476fb1cf0dd735fe9f901811d30dd29efe418c2a92cce93262bbcbd9a0bf7e99611a53cdaacb642f424471b21d65286465adca4936d6937c3d0d4533eb3f7a27f1d0d74cfe43283766adaa048d4bf74faefc32b58deaa46b716a3edb43422f2b5e35f7e306e2ca96ca199415c84a1907d7502889574f18a3701b240efd8730e5d205efe8a93e6a6e3148626d5c3dfb1a8786d850fe076e97f4733421c510e197ff5d364a284d109c7bb67d7a7b7062dd32ef221405a6069cacc17f6c2e9aa7b5100fd09b56de17cc7da7fca342a831b8de7e079b5828296777c04bf01370bcb4075fa0fd80c1c7c952e253f1a52b3522624f2a12000c5b1e64d28cc518be37231e8e82aea1d4ad46a7600d635269f4bbe0333d32ea3b941c1311ee5f1056aa38e5d3dba41ab8647e0bc60f454bd27c8a3c968b75f47ec1022fbb80e66670ae8fde045dc817614b92639a65aa5cdbb07e822026395686ed1ee7566a8e10d2c55fe3a97d94b6201d1474b95956a30990bb5e54be8d743eab2b451ace799fd84eda449892198513bd92b4406e621da3c476b4bf6181233b2a6ef68afdbdb59af149d553aee48baee535e11b06af93a432baae01ec5b25707ff2612a250deecab183892c35c47dad5860d4e2112ccff53bd4f10c8fe2268a995433cbbc078594cf408c3161a43cedd286b5018749e34742c24730c57f0d741eb41e492babfc5a87d03224e20e131cc821f14e2a3429cf9940969a4c42cc6349f44594550df6f2e2702699e75b0b9da992cf76645122c0fa134dd3dcbe19ecb510d0e90890bce2e0a41711786e22dc0fe45a49834d57917dffe348f9042b0ec679ba061282d120c97f5d256ef8035c0ad3d6ab9550cc008169f86b4d2d46d36c2d9744fbb3ecfb961ccb72a67dfc08cb33fc00ff2d915db114959cc6a2e4734216fecc9d8745b39fa526a341b73dde08228e689ec5e6438a094f8408832c70d812349b76f42510e4d52cf9ca3575b42e29c1f1c61ca515272ec9614c788ad5df4edaa3b60940709bd7a417c8a8d3509001b924becf2ff08d8730819e730da639aa831afe50227e1db61680806c013ca65150a3b35c0b791712c76ba0b96603eb9807be6b68400f5b51c49db0799a62dc3036e3ce021c2d07118f702c0aad6bca69b2100b5320452ff89cc7ad7e2c49d1fbc865d8cdc0e5291679604a7886120ad08f808a644ff39f614e946e93f2f8b4f64c3d632220d92964ff1b73341cfead3ee746db136aa6c3848635e285a4657aeb27f912560bd06cbd1a179600cf25b2e8b76c7eb5cbe399cd5fd81c2fbef71c50926e3a402e90d4b2be9a4feba63ad61d195f07c21f62005ca0b986a1eb01a0ab7c88e7cd6d0b2da1245f87e60a730a0ce073e2ca53e510e8790347997143890048f00c8b7407c53adf6e049bec437d269f4f4e11145c8a27e11d92c2c24da6ca9c50fb24850711fa66194b2a69937af81bfdffdf786281bbbb0aca6850874b9eb6e2d774b08d5b69eff1ab91bc28bf2376f43d2635f139398a9e0856fc82a8c577a52ff8337ed9aafd6c4e9e89735a16919a06bb25af432cfee26fca0cc6fffa341348661c171909b81a3fce15b3b0c60f75b69f8735a489cf33796666af54a7a8a1a0054afc3b2dc0203115e80a67c5b8ce1f8006502b92d0445662d2202dc2d124c3ab0fcab79b72559a59b33254682402d0066f11ac4383207e4fca6b84528581a05211b4e081502ab4a63e094711e2599d4f5088c4ee75bcd9a695a277bc0344f66a8b6ca2443a52eb0758c3c0e2be28de29a50b5b3b4020126f42587452a444472872e2f94fe3c9d2272d35e76bca32beab3b17f10405a0a49b3fffa1016a0a0462c218b587d913088ebe3803e03c24f370fbaeb2a00dec2c95a270e59f27e10dbda2e0b03ee5979e2f2bd4555800cf0516f972d835d98f6cae427ddd7099e0b226bee3b372f652fcdb7ada35f67ed8625ada0482bb6269acd14aae260d00f88fa3098fece7c38f7e75bc6b9fe2a43fa92e3e4a63c6e66d719338e0568d46c0a306f5ecf8dcef552abb45d74c15acf5e574b6c2affacc698b627a736da32893de69d26cd6f5e7d7b73dd13b1bf507099b1a8d81318e9f245683d40af4298be05dd94430227e4e9234786433dd9f61f04f32bb17fbb1048e2acd0198490adc4932cb40edd0d9190ce67476d62708c7d4c93010c943c15769a9c4ed6192b24ef2d357413654f7900fc8f9da465d0c1302600081648af35504a7ba4c044fcf6f468f95cce91a6235639c7ca6ade25fe1ca0b33557d8bf187a3838a80be92cdc2f7ae14fe2b2cd097855d63f40ddda41758f270e3b345d43cf1b13eca1cea6acfc9f1475cbac0e8dfc78eae4a8be52936e18f05874d20a2c4136dd228b17e1ff9cfff8f831e487a33b003f214ad0f35515a325096817e79d0c09df2536404ebd1c18476df269c1cc2c35a4b1645f8134cdc281cf4a5c770df66f890fbffc457d5a0880a2da01e44594aa82aa26c0b9830e900016822645aca7268f440b4fb3c58c9eb828b1ee676e61c0386e2cc105c1e2cd7691d861805d8b1b38649281323266789fc336d485fe922af0096076cad65cd171971ac18026d901dc102134fb5ae71ac72312916b11aac6c707dd0d2871a6911861262f8b036ab29042d1762f133d8031aabb3568b1ae12b187a0c8d81760e6178cd6040b7efce5adc4dd662fbcc1b2d4350500d158f22a5fcf1609f9285e344c9ddec7f731de765e8772dbf1a29009821a5f2c6978be3699cdc94eb1db336025b0df0b67a9cadab22a3b544026fecd4253d5bd6bd1d4bbc534c8bc848da3f1d4ba5db507cb209bffdf9285635e98121e253069ad661292e3fdb9e9902f673dd41090b6fa846c31f70d84e752d0149e8135b785f1cec21744e89082a2522fa66f9b02fb80f017e58454b87b28122595fa27fb6c22d67330a573c3480a2f08faad66730baba431cca19833d862e23f1da84cdd9ddc9416dfe141f7132256551e9c2311740d10bd12c3391652a982585740dc6c3db38a18a3eb2092ae157b0ee8974ca28e40a0b5700d0f2592d5913308853ce6308d5a027b8d14e6dd0e7c5f104a8742fb01c94b086459e85155f22e80ac6d9b5acaca6647048805b2a1ae17b61dff124ed86f897b10d80a00de1ad710264b1a4d3217ecf9e6f1e2ad3ab62cef2c5de0aa07236b3612bd351c4850aaf4943cf420bd464a114eb9764b90ba6a590eb326859d68039191153c82aa572ea1e6737c5560ce222eac736149f64959c44bd80d3b6801be6848244d32a7801605370f64e718bbb90396213ea4ffac46811163e9365379fe2f4ca096a6e3f82bd1458e7aafb48b5235936e7e15f1a85641d15fb68844f4ed43194b963090a7e46e3058d53c337d4b83f35de6aebd5167db41a9b9ee7fd4768d2dc1951a842ee54680d4377fe3c7ab3d3334da818aa6a4a9285ce36ec87fb6eaca23ae86ec956e4f3e07908187009e715746e6a58c66ebd198aadc5320ae2be8e8292d0aaf0bf2736410222adcbccc518c71506f8096ef1e0160f558016f0e5d5401b88730412c8bebacada68e96a20d609b8b8506445ae397d9299def13b4c1735701f1537ba040620b310cd329359a68259d29b769365a527fcda20a7c1e5f60e59c42af66ae266d352dcbd8c37cabd74defbeace9e1cc3c1b6ae1c7eb5e2fb716776331a1d751907401b3e666d5aba5b80254d43051790bd24839ea526b0491884424209fda3bb475c5625143c14f77ce3cd96e31d0afa7fa3c291e8ca593a6af1814311a7e60e441289677fdcab1f505d90ceb328b41e47c5dcfae725e123e38d70eeeee28877ca313deff441d9345654cb941b260b963d516879b5d125dc3c02067b248582080c4b5002770155fdce47debe6765548d9132f2daa00378645482664415151a723092e6cd07542b5b18fca0e16b831613316dc0d5dbab7abc26604edcea05aaccf3725d0e2166ed7f5eba3c4efa512ee1075264fec06d91ae3629345170b31f6556c897924c69963b605a0bb92c839e65ad3013cc348fe2a3ec649eff2c98daa1d1b6f4db2c68890706c8b7d6d0850c2975d7dd8bb8683039f079118583631c483c77560d6b9fab83cacce7b76705e856527b71630670b2b485309211e81197b6e3a69960f47cb5cfb6c4be3b7ea1cc5a086864023c3a35a74e832ab4df20ef9ae243cfe79cc7f73135b59fc1bede16fceac778c9b693c1a72c369c3610ef4672b599a031a7c5772d66a396419d942f0f587b1ec84ecdc65152d3217fbc050cce1617be016a642a973d8be821825fe21f967adf8c1b280b55fc781db11593ec6257b6764b5f71e1e6425a9c387a2f194820d883f06d703bcdb8a719fff7a2925b5a52ad27d3f5fda549a41b84df6da763937c9e3ea8f3abd448473f2526fac2517d378ce68538d2853748f542df0d1903b3ca74915d732c770b11b715f48e8b9e349a4e394e6a1c8864de5ca036397e667e37d146723c90ff42bebbd11538dc1b72600a628f2d39d41cd5e7e003168599b8f47a0b840a781ce50923463da24b8a04076578ec37e63094a99274f491bdfa906eb4a7f7e07453ec6b8c88aa655e541ead6247185bc02113af995bf0150f1796b72fd1cc3e0b2f77a2b47fb343a254e0b69054ffad6dc0458b7a76d20421a90b26947ec0246444d7a8679ddefbdff540a9b9452f3a7064d04583f60cf1bf922d45ef66a4e58eb4e7cf9c513ea3efa682492a0548f38932862256e3b41c2c19661ad46321a1839cd6424b11a165c34a81bcea55c52958324c715a62a42c04aee9642430ad9fa3b29e4597b6074eeebabfa3b0e496f11909f9786ab26d6561a0b7c61a03a8890d7866366bcd28ad7c072646dcd90c979791f72084229a85aded92126f1f04e8efde134192152cd74e742c150da56b2ea362a08de78de2385f007adfd1240d98c7390e761035628fe3ce24e7b766db1437dae18baee26f735c4d58e65aaef5649d186d200cab02ca0d99c8868218095aed3dcc0f25e17b0aba76568464a37fbacb472b25eb0da94ea2636976fa0f715a57282aecc1399a4cfdd32e55072b8d3954a9fc2acfec8a7309b28625101fe2c153a58cae7891e4f984286b080b0dec2ae5a493151d4843c72acc34487a81664ba37f32b569ee55e1145d26b84e78513426f517609b2d292c22efdc8ea28b9e8c2e0314aaed1043d072fff49a45bab8e954b783c191a7f0302f33d681dcd13ff43cf604716541948ad43a6857b05a726fa54fe4d266bd439ca81f530d5d54a7e28cfbc4cfa797ae939a9f9eca414dbc50ebb38b2600a347ea61a6ffaa2d264b6bfd8cf2d9b93314a1b8d3ee16941bf1cc0a543abbb31bcd446abb98e03c6fde2cb256a795858300a198f0f1cb3ca19b868f206d6cfed85404431a55fc1eb99a97322b7c1e8013a51d7e80e87fd2c031eb66f43e4d9de16185a8663a604fe41020b260627514cbe1fb8cf77fe562e74334c8591e21f73a3183a4a2f9871c90ac1e43b16c331b00a4b1725c08426972f4a84d4a6c665da5fe2a1c300aae85b8f25dfd2973ecb48e476eb1bc25b3de7c0380332a376bafc37bf4a099176ec8d56293def9f9b43d944a8d9ee0e4de2c4a28af4e74dd39296772e8b9df0e293e26f64caf69079129dd32ba6a1279a451c5a5a223dc28547e60ea85b749b29540ae8b043a73710b5e2031723b4961fe1d9702be6228d539d495c983f0f3cfdfda24ea8830cccb65d0acd89ddf6ebc914a26c74941069cabd1009d5284066d9734bfd60332bc1e97d31003b0586ad23b19af576c29fa64725276b38140e4a5f3c0425485ef757a5277d9bcf0210ba5bc7414cde64613c6e5db3bc6e2c0af087dad9e8d26bbe275bafeb46f0061574c7aa49f2815cee656cfffb832dd3b482f0370b0d352574027986ba6d8ef910cd1d4e58a5cb7d2609d6207fb52000ee5f080aae46e212f6d35e94ce1f469aa4b3e976c8e5266f6c97ba63a2c1a0085deab15caf854f4b48113b9eaad9ea31504c7a15dae0a5c7524ea015b64b1320c185c28a42bde10211196c98da77de9b25643a36629a2562bc7f425becefed12d5d71b380496312cc13131d61ff6b1f1da88b89cad78af17e37d3fc4b3af07a5aff05c68bae833367fba4075e8ec5512c743c6a97cc68a15649edec01ac8d82af12d12b961f9c0ce2a6105023cfdaae9e1f24b6b5769c007e32b88891644049fab5f7e2165dd4bce7b7f62ab9df1d34e0175ffafffe56e62d38913c2aab56556883ada12acbad68b3aec44ae5cf70627c87e088676c2ef41b6a09aba9ff061f20b78bf90cef72152df4029df920ad11b0cacee4c5dc7a3aa98fe0789fa4261e7ac77e9387116a5e9e3cc9b4321f7f9649a6daf08b73929a31b3b35df43f6893d36b9b18dc704d67973af6c4a8e54d11e05e9098dadb778ad510539da99ea28407453a53458d46ac4f183a363b5a50d45aa7d9ecf6d8dfec6a09963b820358d7342c333d08cc7b1f4e2df66794e309a29e5b9e30033f8430a4eb0c89487e92fad0c0a3f7a893a67858b22c4c0b5bd43e0cb46937c810a6588b27631f6933ea1a1ee1f34b11b82281d444046f863c79ae2ef303cb18365a6122144ea96cfb412ee43a5b5a55108270a04718748dae4e7442311ae4da1b8583f74194810c571ed0e25ae0043fa12aac88ed410f07e7311054ce7dba2e712b0ba103930e465fbd88dd43e05e78721b6c95016c22b4e4d231c7cbb96c5214e82e9571a0329a49adb303abc3a463a9f798e6745e2e5dce6929bcd7ffa5b56a341150f645468df74f05c0c6daf601d6f5f3713ce32077398803d31168ede80a7092357c94ef0a64a15d712266811f300e6ca2d300dcd6817c8cd1d400089f36f3caa86e466a008a763ef82961059e81175d3a6673253b6b0e846fc9f08af3e07ad1a5836d2f0025cdbfa18e44512be2a9ede429e437623dcb633c929430eb0e9bc36be521d0389df0882ead561e46578763ddd95b995eb03ef50ec0aa75d17c0972323e66398564114c0c225b780733270ff4f0efccb09637a80f7166d8b98cdcd04ad0ec69dac42c1b1d8d9791dc6aff3aa9d5ef5c8cb9f918b5013da4d59025f5d2f6656c0386c4e9d68e8389b77e81ae53b351824104e963c68aa0d87a708111dc5d968bb2f889779b9633da9e9dc839819828777144eb2c1d0b7d60ebdc0f855e446bc82aca7ce1973d35a16629aa465814248db3fad62d4a5b3ffc1ba8d7f2a0ae7642638ad9a5b22c227dacfb1bc082e078f44e149a9840fa917c8486fdb68eeea06a5708a805a378bf0631ec1556b3c1430a7f210969061bccb2d943b138e639e6429e283a674ec98b47ace7522ded4af89def11aa7003111522d9badc3fbafdaf8ea9eea016230f42ec776a5276f252d91376eb174d4e1998ecae7c477c48adc75d2c002926d5f21a9d513fcaf35cb9c7d6f3abf376694cb064a69535a58e56e9d02816fa2cce1bd6f0223549680e22e7c2aed350a26d4a69b1050328205b73abc80d609c90216e2f03f734383f63b99eaa47392f4d55a52fa0dd6de61beb65949233ef71171f9bf387c51241e72a124fcb9b9ffe3dca0d97ce3dfd9eea29241744d701cd389462f5b73ae226064c400f00e44c04259d737827543398d4226da9be70ff30f8e15a598fbf7f676f1173b39d6b47ea082101de817e85388dbb25b1baf04154a52eb0b2c748ec2b05869c1381f552b2c762ac7a73838c933f5af68d20c04b1d091c043d029c57c6349c87b3b181e8333501abd3b4f9ddbe8b0296976d159b9c076aa521b6978744aec88d0fc72fac05094c809f377e9209e362ba86182ab25ea5648c6f4f79180a3b09310c4a4cf64267546d93b2b9a100b482239513d87fc614df7a527ab6b6d4ba3311cef7a6968aa75dae87c4b49eccf7990c2f8e445d4233425236f094d4dfca6e904c9281d04c0b5bd66934fb1fd7baac838621d62ff27841d307cc7aa1efbe03ec3f165ea5aa30812e4f12822b129fcd5518c8d983c5a9341f52077fad0caab2f42d07a837419e868fc11f83c59025b76c7c3119a4318e67be02ed141ed326d6db25223b7e4795fe913cd340b76df777caa4e95c952107c6d2dad88f28f486be23e94fdb50a42739cd5639c89ed80f7dcf9f709c4571272dddbee2d7fc0ad02fd58d80a871cd76687414def08cc049349a98dc2d20187b3d0d5184c9df89dffcf4a6556659e97572a74877e32741d080db130b780c6e7e0c5bf6eeb3322cfb9ffd67ef08990f2b80d24d22aa7c42b8d805eb5c4b5c5fdd5515d29b2b83b751aa567145ad7e24e5f0d8b9f91e4ac9e202cdd2c35466be9ce0da09048d3e0c3fd200f15c3590023d67c6fc73c60cc159d278138de92af0ba8071a8cc925a8382b6c2d0a5b4e918f46df41c22175f7e36ce61ddebe94cc1ffd9c9252ca0527f11d5dac1311bf8d56f8042a394c6774fdf1c4e40f13b04ad7853228bc9e7b9066a64ed90912ade7586242c8ce14ce19c028ba9e8755c90055d6bd93beb54aed5a9684f87a020d397e3f5a690206d283a17c4b26e895efddee6789b61c66f0f570583f71957d469236a2b1ec51081efa3073f46d1aadeb2411a0e99c209c48d453411d63c269f68d86c2806cdca410ca01f5221e2c333bca5c566288cdb5cd5c1f431364eae3718bc1d40279171b201172283f6b9281983eb45a305606d5b56cc1b8208d687ef9fd56627dabdd11b6cf5c456e4ee245028c7b58ed9221039a6ebf83a139743f1444d44b8d168b95bd011def9af51be144be3dfb3f8d4bf9beaaa97592ac37ba1800c5a5e24fae2be27a1d4fecebb78d38efe5f7ea15a913919ff43932cd6580eb54bc81103c1312d5f661276828601bce0ef47f272a5f70673f12a7f1cd0d866f43f0be2819c1e54c7d1fa9fa146e8c3965f1f52fddae0b383b0c623b739084ac871bdad0594d26dd3226ef4af0121c51645b9eccc97cd7fa3a5400ba2a9ff9c01e06741ec3378c24d70c752ef4ae7f56d5c1c69b07128f18f09ff2d2550156c378bba4dea111c550544e66ae2251f7300149a32d2d8c6720ded88a6034064c52284262b815776868e29882fd1ec42dc9215244a0c1b72243a38634bad4c5ad082f65d81c05a4032aaeeb20b28091b08d6a933cb837829c212af64e210c84763b405d189e01cf6f26a7d39083547802994cc76d01bd28ebfadf26bdb63bb35f6f8af6847681c636ac05a8f62272a36b6f27175c1f36c2e7bc31e8e6f185b36934503006bb960ae7f772ff9c03e8b3075e20456471aabb8174163de8f429e04aba8f8b28d175608b91016b7426649ca4109f3f69d672af37f790567f4ab26666aff67f2e7de4777fb670bec531f302d0c8c04b61499b8bc1522d20cdeaa6f9c51e85a851f043675973eaff8342b0685ff7f7e36e92ce7de24983a2e5367509cfa2754ba02001360e7ee430d320463f6c11d09ccd1ef419f50d38d2d3e8e4de9e71abd2dc9a63dc16551159bd9d3093be409b7bf7ad0bee632fd1e13efd27d7da4e243396f73dfabdfab49779efb2344e2d667f03f0a45f7c6366b667837a1e2791437d9ab86aa394326c4bbe9ec91dfbdb36824cad12b7701bf7f16a41009f7def545dc526c32fc6cd50cc2ff74da20af8888416cb8d5974ac64b7a1a82be0720162da3fd3f115812544c14090f68796186a176430034ee19187176e6f7ad377c0012c06133484150f40ad9efcbdbaac6fc2917ef7e023ad0009c6c380172a476dc241d2067dc2f90c1bec59e08238453e3bba4b0d621e1532d19a62506d006bf996c9b133bdbbb8f7d0ac16d63da69b9fd6c68b5d20e04f20b9776cc440fcb79fcdb7d5394c1dff9465da1f26dd6c67ea70f14d5cf6b13407576e34b326f117c5ae82d7c2894a6db0a398be0f172bccb4ed52e9d9b6eb83c69e01bedf6fe73816deeab21ff602eaaee512512f54be744f97d64654ab2b05dced1b2884ee19a8db1df65c9de5e6f522c1af36c8f64ac104272644bf15b0be68e549d53100d081f57708d0c7cc2be718c49fdc6df283cc5cddacfdcadc615497f5cd82a6ad42286de3c308a20e692a9d0c384654883da746b778dcb2a036a40e66a127bc65e3ceb4b9b43b04859bd9655453bda5d4cad952758f7ec48138168551707ddad4344e07c9b3ec66d158c7c0bd836f4c8da3f636c0c48c7e4af707442cc18fba901885c4ea0f1c91640bca41ab17ed655c01b24af953e3986758454c2c22685d65efb86c6a2e1d5645d560dbaaa73936dc565ef6a54130b2f63f40f8001423766fe12b349cf1ba72f15c819409bbcc488c32587c6d024455bb6781a232ac54b09501f603d2883bef591ccf72f3ebc64674a59ffd278cfe4c7eb142b03781463e950426cb0ea76aa6aad8015620a5f8d1eb6c71c2212b66524e0be6e4f86c825dd312d59126bc7123800a94f8f158b7ea5d3fb930f2f581e82db205bd62b9a4297ac59017ce87d64ead51177e5a2572d355241119566b12479e79b00bb455c55f17fa42bc83e284146661df197e78133ecc122dabe4dd88dd1659909a2e37ebf41b2bb427116246b2507a5cbd637a6e993fc09987f346d58bfb45039535cfd487b591c54bfdf8301beaec17bc8329b4227d3f03a4b49fb408530ae0ccee7a931a9ca07693970eeb8402d3861a56a48b9f85a15d40d0592a9dae84cdb93872c4ffa028a13f81b948844975410a4da85805f861f93656e025e5da1005b15087d667fb947754887c90dfe6aeef15aeef79544c155efcaa35323ed92cea4396b9841f7f2e6a626807517a69ea4be192715caf59af4711f5299051455fa6ad27f82254a8a8d6bf1af6662f747551ef34a5a8e4ad1138839967597868bded57a254fbcfa69d4178334c9188ef076990a4e12d0dbd5e7919006b8c200f8600179914fb204095abc09cd850fd52859ed3a0b01d99dd72aad90b3eb8f52b8cae5ade29438f2f964fd0702574ff546383df8fbd4b3b25fd4d3066fabd3f1dc1a1cb174fef2bcea5b91cbb366d28868edaf6254cdfbd5d6aa179f5ee69609378bf77f1ebd1b6b243ab40d00adc59be951c0105d0bf2bcf5a3262681f56c227063570124df775c8b92b48f54757be376799fdc3222912938808ca75c6f79fc898f8b1a5c2004abce27395f98ab802857ccfe6cdb23e8b4cdb72b3eb2d2127a65f624e8e9c412b416399d19d3aff98b985abe1d2cf4d912174a51e1665ffab097ca4444602cca6e8f321304d54ff7253994147d3cee608a7458e807ffd6e47a1d146222fa394e9e2326fd0214c5a866f9ada948ec269ecb7e7f29c93c9199f13b0e4994025ec86d174cca80cabdc335015d0560469e9069b61642501bc2d81b24f3313253f3da0dcc64a23cb2e2bb937a6a19605ad7d4cc0bdb5a2924a020099627709c172529b155fa6c6bbbb68468e9b081b97368667cfa519bdf9e9ba999548cbf799973ae52ec485bd982161937f0d3e02bdb57a9e7f2240a4bdc339057e29acf3e70574c212797b19cb1c663c796d7114b926f9c885bdc352c38b875f59c92872b302fcfe6ea19d56516d2a1c15fce6d307058dd79f525a60a2c6620e8925e3d144b74c4e12c9dc045e924760bae39d3e33fe1ba2dd72a973e58fc6e2148094381a85557d4a9ae7cdeb520ed4d34e2d189899ace2272b3bcf89b8e01febfb597c8432e643628af8ed39d84b30416f16372a6381970c5cd8b55fae5004ac7f68a1257c411ff10956691a96c3d786a2de5c446fbac1054458c4eb444745a144ff36cbe5980c9dac0014919457c59e6455b7c683cd3f9ac5b0ed740bbba923decf82425fdb76ae3644814878dc98c9e5f84b5750c78adb3ef82111c957230e2760c250cacef056183444b3045f0125fb6cc49223b8e9aa23c3683ca8e7eccbae955b6589048fff3965748dbb2abfa48d4a667979bfe4788d7cce7c9dfe0eeab8c7c88ac5ad61afff8f2f890c9f35a6781585c2b9c753c9fac92b7cf1d4623d736f356722af7ae1c2108e3e6d9c7afac9f02aa480d722f90e142f2e8b4976a699c775c1a76a6fa808f1450c9f0c9c49b4082ca586d33bdaa102cb96bea628c511c7542a2ff856bb74235dd4f51d833f00cca2d3c30c98983071bc8c30618c7be65dec3c0160e4748f6c72b2976f05182ca1ed9e771a3cd8c86faabbacff263cc4cebd523c6aab0146564cb905ca9d427b289c031a410adea0b37f4c5557afd4e0dd75e5bf6e8e7fffdbfbe2060062dbd8cb55cd72b6b21b4d6e2eb4e3ecc4b77df77e83836acb38a25d48d2a5afffc4e4d51daf6c478a2e4c49f7663d537b60e158818be8d45d54106dcb6ba76019739e1bdb34d6754f5884fac1e0abef69423c1bc8c5e875edf2a29127045e557ebc8e8680978e9c6e2d244b31b1ce5c56a7657737dc278a7f83943d4b5938d5c3766340c4d7ec3d947db261dd2ddcc5453e9614bd16ad087ddf99f37ee8f9f87fbe13a67ca4612f58030892134ad03c908f328268b66d9662b620e8161f384fff3ef1917201fde85c12205244cd846f4cb1b178552558e7f7b326a83a5dc9e2179ace334963a2310487b32132b27f55aaf074bcb094cecd6f168012427e2a086829245094b563cf650c88ff12a22e583efd281bac8855191659d148bb043c95548bbf9331ef4b4b9375c9c9f3252d62349d33d47850b704eb589be56977c3401abe69e812764470558169355c7f9bd83f842134d028feb1029519978789203a67f683871d31aaf90a104cefa5dc5ef469f81cb2dcc2095316ae4007f541f6bcdc506e389f2d587bac245045a92b3278882b3cefbc656e0bd27db99b47d9b1da08f43a33f3089423d14b7695e76c5b53dab24172a628a7e5a8b6e7222fccb3ab9ea1323ca9c2fcb82171d7825c2e0ac60fff308c5756f819c32abb1f9f3487d37e5e3539f5a2be99cf683c6d39fb3f5c818a0c312b0f80701b9a5537ae2461dc3e58a9641a2b301f8523f054f927fb94abc7c49ec3f020e5088e50eb45402a64eab295c6cac2f8bfaafd8d3d24131edb74f2906b3a2cb1933da99bcf10bda6d5f70f2138222c7c396915228a60b52227f3eaa84e3a97ea970278796b917a0179acd9ccd3570999472192409f1c102a03484c5872f0005a0e90cb1d7b0a6b6df14fdf938524c3be6a38f25b8b3159e87e8810ff31e9e57c62a8377893b3a785b76416fe7f41f7dbb2206ff22d73c31c003cc273f9e87ea74a67c38f49e75ce9a1149efcfa72749c231a5e3113a8b2caee97472040f6d08fe88a6e1a87d5414fce80297430dd7a2d894a3fcb1b1fed0a73918f20313f8c3339891a3995b938670fd312ebb6ac810662ae038136f0e5a4df6a7e781ab425d449a5f0797e4d36c216f39f817d0ea80a3395edfa2e4b6a40b282934ab0279f75a40ee282519a743dc2424a2cfb3164581b28e62671400504549af752b3dee58719ae0a701d4306039454bdbc0a00c20e682dced10a62d67327ab72c960982ff1f35421fb4c040618dc9cfa29228fce18a4495d4195efbb10cc278a2d80ee5793353a7b31dd33b30c84a4678d5bf36bc084351cdba1f6ddfd65521306904d35a7bd7cd363a6ed2270b00273f70a170baf7e894b39d4239d214d39d5c3c29ccf3385acb2bcde71fc000f524363b4742981eb36d3528fcf8947631e8ba26c476c92aa4c53e049e81524a11288b2a8bad9c14250e7ef1c238b1862cd3890df6593c3092dcdb0047a1431a7e6010bc4ca9648f1c5509667975918d9a95e8df66437ae995ea319da26a5820f4dd853d55f612a8f30df7ebd7d4de447686b46f4253a6f824350ba7709a03053656d8113f1d241918e1e146eaacd0b52a8c4aab62cf9ea6544995fbeaaac186f44cc501c6bb11f9d1480161340c36b6456a1697b2edf5e374e343680f24b60ef944de98e4bb5cfb1ddd7d761b416ff0a76ef05ee907e3e8da75d4b87c307ff0dee1c0491777d4a038599cb01da42efbcd9ccacb1104070ebef5e2a32f17850c28e095dfcdf54d1ab6c72e2792d787d3a267ec711437e4b83c99eb5afdcb0c4ae54db242f8f716e93ef6b40bf4a401fa860ad0ae5bfcd174bc00526b579f63cd38ce245d4f4b7736362e5b170b52a86ddbaede480826b3b188e6a352beb36e67b3176d9f6f82e3c3c4fd33146350ca00fd798962f3a12a332932ad30cb6d4062a4c211782e6e89377d29cf4f2d4fe81e5cdbc3c85a8a604b6a5e9e91998456a2ad44dcd3afe415f0b452de921151fd12d7f46e4d2552cd144a35429b226a40d84278dd5e312762efd6cbbd7fce5f4cc0c8c8b71c4f6aca6a64b9ae70db78d58d1c110ea390973fab6e29fbfc0ba9f949a34876a63ce40ae913e47dc4810366014d4112a7cea6fd2e3639834064ff94686c81f3ad81f004c0cf0dab9c62d29b876aef0572f67c1559f78b72f9cd416bbc6716405afaaf7ff92041def8e15bede77b98868f0a15ba5ef80378ada40a9d06135f653bf89c0bbb3737cfd6582fedac1e053b3daad0799b0f4460b451bda4c9f3e2c6e014a0516ceb1f16d7d53066b572926be5fd433185975c4c7e91b6ee9c3ff8bd2203facde85a561c7f65cc496c95b741121817b539373694f3e04555806464372f6d6633751e965b3c2221458b64c7e362822e4438b68e0dcca1fd0380f15d1f79213294daeb5d7876ae85acfd303be9fccaf3afc4f568b520422355e2a075c82191f6843ac1e32647855c978e5c368cc624c423cd13835df9f2d8034f9ccbcf6ac272b80897ce6239ae4bc0e0c4e5c3754fcdef2b44196e41d0a27b24943e7569a9511fa18a945bdf28f2f17605ba7019448bb7e8ef36b4228a488bd0546b7bb7d79bc5e30160b1fc8b13da2baa7dc002f03b01609df64ab8e4b921c779120e00568f52bb56b94384be60dfab1525b559404999bc64c6aadd4f7aee56cd9241f7d2868fd895964e292e63421a327bddc99ac7661373ba514f555b9eee6b5c59d21a83188bebc625b6a5acbd7c5ba468c00101ea7cbe255e3d201310b7c40dbda42bac2ac222d18d38e4d030eab247fd3c0e4a43ee37d709bf385ab366dee2d4013c4a73d99227ae151aa140574736bae551b2acec018425cf6470c4aeeae901ff6d7436258be60e41a22964160f040cd195947050ab3b177cdf724482f05f978be3a48cc68f0ce20ead0bbd178c14f98d3eaafe893d4b733cdbb43f1867468fd89e342fe7f0d850d4eed69ccfcaeec9d701489acd30826ffc824633f22e8d4c8c7590c81feb35f9e5681c663e88826fb7557ff4d4066f692f2e5ac8bce0db08c8d10cc492da0745a4e1c9595163f34d4e06f1d77c036b47c3fe3b908692037156616547339d8dd9c97d73e2ebe0037722a7921d37ac7472a90a08285f8e453a6c0e32d0ea4ba22558c16fbc0f49cb079ad4518c4efb6f785a79874b25bcded7b5fbfd772939856212de3570d652f15a1015af12c96bc53bcc512bdf4f65523249a5175a96fd503b0c6df7239fd94dfe03aff7e3ac0b9b1416e886b84ae7b00580a4ec9c9fc8764533d3253e97cc118acba101c75e2bf374f31cf333c3c51528ef39e9631f81eb429f9cc01bfb2ebe2189af14c6a6942340e083375afd44a7eb097ef75da62fd854ef837930cc951307ccd4b63d01d58aa5161c95b05cbcdbd5655f08fd99127435245f073ff87d239dc7bdda04ab0de7ee3a7987a87fcb29d4435807aa14044a101a68e5512fb800a70bf49e1883dadd2ce2eb3f4fa0c7907afa2dc4475ec4a614413e6184fd2bc08b7155065b24a8087c606a5fe026859780d48328ef5e2f38c04ae98bd31e9665ad4a835c37ab08f39f5ac7e679008fa7c7f308333ef0597b22d89fcceac909a35c5faebc522294dc40ead62f74243f4b7d9e17e04637dda8437998251528fbcf639370afc7be966c880cb505841e0890a32ed0aa25106e3c4586e68a4a142ecbf60edf07a96311b44966fcc5cb5785c66693490b583bfd2083d26db7845504dc2fd8a5089b8e454f04cf6730612bb5020e9f032d9b44e34f7e4d13c702d2f1ce54b83501c563099f9aa6d4a63ca04caae6950e802cf5d051944d6227ff092bc9724cdac6c29edde2822875178aa3cbfed2fa8571c781c0aa0e6a06f3002092086101190d67036efcd75033ce962be7684daf41218235da4c42f4ee70047c3a448e57fde68ada076407f97ad6d26eb25f93067370954e9bc78602519807ac6db5511a7726bf70cfc1291aadfecdc75d17378c90119445e98962b6299b2a8fe5b681f6dadac26bd21724ca1697dd4cf4538b675442898e7e3716d73c0d8eb631d941eceab6a6c0695856637fc339892afb9b60aeca5e98dc0b3119b4f258c753ce644806ae41cfb889878c2b1d0adae2aa00d66cc07d425bb9228a3ea9c02f8b79e311fb57207bd5151dc80ae2ac63666ce93cccc40657d346dfa5c325d94ca4afab6cf1c3ec1aa83dd067d252a9092502eb98d33c76bc7293c79ab95855d9f32a7462be5f83a461e8ab2074bac2f7bf87f77bf9e6df36a99f239107d3f8205289869235ca71fb7b170e719d77d9e2a53f993fac50d9af52c62de3f1f712c2a55775c145cd61218bfec17ffd2420650024dfba9f1e1938c76098668676c447ec0a944ed8d63070b606f895a7d20a513c145b70781b4d4e6edceab811ca77856da8e2562cfae24716d59759445ac152b831e66958a7ab6d370c4a4a5a3974479bfae52b4944d69f763287000fc5cab3c0ec0048e656df83b27c06c07561a59b9c55e71bfb90acc287d4ac58a86395a59438499b2a9aa6e007f7b40625c565b9b0f4fa271c4a96541c2de6d8ac9c045a96f6a14464b9a49c13753f20ff1e021160a5af8677336b97e7eeaca1c4735e1d50f8aa247d9fe13a876b447ac8562d4b356600d201061b54bce2a1a165487ca686193edef52cf1ce4923052ed7aed4d276aacf80e5e0c6456142cff0fe749cde801db49de92c8813a4bb975ed811271c4875ab215de34cbdad04d6c0c101152b37c9f3ff1e6bff40506afcf47fecbf9c0973843ccf03e073270c5b340c3c311224c5753c2eef8e8585c66b508bf418dc7d9728dee42d6f95f9832bfcf9252f3766b505a66e2ec0e08c8677c0d5876d984a4d023f89401b4784fbf558dd3da0902280ba9f8421bb300d027140ef614c7b6a8e23785883bfc8d2e7979e62bcd40c11200793cd69b0618e663654f487379d33e14553079f242917894c1971ac3461fc5bf6665df9c46d618e379a420e5a0fd65b1ae9bc6f98d5ef6e0545cad8f5917cd8d647fda3615cbb5300bc27e390d74801193b426b1180352d74600304ae28674a4903a3a859e22c8cea43984395617133fe589c7ecaa13d8c633f98d4920fe52341f492f2cd83e300e3f07ce2c49260de08d51868bc4e295cb1251563aea6b9e5c214b97bea9eb88e051e4b4a5be662743bdedefe5182b0670e43b9868eb96bd535d9a33b407741de08dea1cd9f1199fe2bd9825028b396288825058510075d4370427fe60c094bb6500e69ea41aeee3a64f9f8a5af73bdb8ee006b49dd38170235fe6b20e8635e000fee3e2a8e7873defe7341b589a5ea80504c8aa077ae01a8d5333582df4848819a51da1e5b920e4960b177e69fc27688654a7d38f9b98c73bfbacb3306eeef0962d69b01f8a00bd52220b9360a5e053f8926441b417340523d41c7e4ad598f72fb3eba3b52dd340baadc3d3d8bcf1417c9f7ca88421f6c283a2000e46f8545a45faa733a0ff913f407c77e9873258c9bb1da1bbb0a0f53ae0f5f7c7e0fd2396c730266bd58e6dc9a6d814c82b9b852bf2808481d5e20ac9b9a7bb1db58db4b62e7da462b9812fda66bad4cfee225507a9649983cacec1ae92aff1e78ebb80a22fd08fc71f33ecd22cafffb750ef5b826538e984561cec6a43624d6bcd8e6781a46d39bdae0564d68826c87cc4e956a01a41c35e99f7b95c230c96b29e663d82194305b1b35fffb4df159f8f0e6c9147ab7bf82061b8103fe32cc9a7e595fab856f36f93b624ab2a170208e10a87c7f5da43b846ac51225bd131d805e43f674e3721e083d3822dbed529bea37f2234eab19d11bb687f449b1bbd8361c3c90f302c1eb665a0d5058184284056c1a5c0023ae762922beeab4e8e9eb263ff9e65a63460904107ad0ba19de8489a45f4ea15f4917e6e440720c559034949b4d93a0bd2de7ab3e3c21dad0ae71536cb3ba4a26bedacc228aa4a4e164300d0c3dd9b66eea988c671febe5ae6a44c336c69765e0012e66588339ea01eb38da181f6d32c57017ce05c751f582622f7dc1bf2e193e5374fd927b0a651b9178cecf522d1f4eeaee3f206314493855cc30642beb093f8d9f47b221b0fe03ad0a8f7a62f5930c0c2d19c7de7faf49685e884dfcbd0976ff67718e95ab89b2d8d49987f07633546d182e3b35b1481e658a0355d42941567b787ef09e50f3fdf014ae84890af5e9d40462921565e812445b11658277a51b12eb2c8e21bed81103791695d94e8005cbbd44fce7b83d4361828f3ffacd12e8a1d45054de0aaa8e511be3e790cb44d92f9d4ae51294ad3d01fb8e3f52b080ed08f80cfb8cce78f6085922d51a283a03c53df19fe22565eb808440822acf308af578b7c40ecb5d03b7838ae6db6f20cad88a50bc2b0e496cbcb465bbeffe825f37c41e118bb9eaa86ec9156b6c1d2172a9cac4782a5861595b66376127f9afac98e1826f462bd3f4b0ac657f8f3c3590a5761897a27033eed626639473f2d61b75eee147d81b9ba54f3163f55ec9d4f686eb170ccb17ed487587102f193921254a1ca819dcc3f5c36c1aa69e0804389cdfcc1298514c8aa4c15ee6b2cfd3368399d477c444d885c42c1ebc08ac7a0fbd8f9b09fe3a9506844d9507fcb4a26759e9f551a5acfad74fe2f45082fda44324278a35b8dc39d311c68d8783e3a3d88ebd0a44a04611970002bcca9314325c65f32da9f7cff4022a1d7c860f48bc68b9a65dbfc32a9f1090461c2d7bb843573211d681e528b66a86fc7c7fa1c3089f573194aec448a3eaa699fef61423e6117f17e00185221b489913640944c47e8351ff62e0fa38df4d59af26fc1affdf31b30ea50d8b5eb555b96b96abf27363d231ab27fc2ec42b85229b66644006c9a565e55a4313794d9731da3f3032c9a573a7c1f8026187c04ff2c307da143ad52f03c07e1017ea2681763d8e4c40ced04a51409b5dd22ae8588c17a7c63063cc2fecf63ac0a4aacaab80067b4d2e9613027b6cad312669a8955d4ff01dca5521605a6def9d6adc25b66f08feeeda078a9613ec9132330441af3cfad7449424d3e798b8a2d2efeb97d183d21975104e27a1100cbd1bc97a0c1540e1468f37a3f62c87b0f803c54f54061cd31335b48b0f2cb5ff646c3045b1449fe170eac97de09f2a7278014f5f57e4ad31d58fe6d46ec2de7a5f318b65ce912f63d4e6e4245ed82501e35fc690a62fec1fa1c8d2b795805a31958ca5a3cbd0bd947bad5c4609bafc76a97317902617fcb1bf8477c3c68480571f0d221f818bf0d1f5193492636a1a14255350df48bffe435cd4c946098bbfdb8a9bdc7fb4e0953ad2fc4564725fc05f81f46025d2835605d27d9c2b60f3e7bcf7a6767da7e77f0d5890f8a5a14a9cfe7be36566f769ad25271ca63f7258340d7368e881a4affb98e0adb1a388c123615fa5ed5a512e27992a9787ba112bc2de0dac4811fad59ec70ca462dc791679d165994ff43d4a9aa46e23a3d1f4c15e360019d5b9657304929608aafb3c39f6164908ffd3284dc5a67a05a81055a9b338bbb0b388631061947d03c2a63a5a732333a49e6e44be0f508946edec63e2f41b21e4a666f08b5d7af031f31a2eee8ae27dc18575a2ca1210ccae4a658c532513238eb719422a56cd6b58e24e34f3c11982b0acbe41df6037008de8678fd2364a096c9465922a678732661582dc98048fd286f25e7c02d2e53d32babc75a787e7c7089067b0392cfca74361175b4eac624c80a2980ca53728fe9a815942be64d2b1ad4a43a2aaf39317fd4abfcee1cb5e1a972666d47d2972647ff419374157b298da8a6cb8cd7906ab927d847c4bc09ff58af9012e85694cdadbdfe888932f048c2950c3739a2bb839f9d57a4f565a45424f108793067244491f1f479d4ad968b27919f30144b6e4f481281d0b2a6a01a7136a5c3a9dbb725a289439be68504752c4ee1cab13987444c90ac3311739f567d36817238f833ed534efad1e52c06dd6f96bcec75ae6f2dfc21cc33118cd73cfadf51b1f45291dc346bf8a6037f64871f6584081da413ff084cd1be3a92ec59fff664eb81619eabe9ee7c4e8a35823072a1a79d176cfc10206472067173a09a56fbf1f400f1d6d24cf8c7b46ac209b4d07568f52330999ed33db3db6d2813a9b3cd281107c7f74a579d8638f0814cc024aeb29635d40d619a7030d3a6818c258481c978f052c63c4006764e129f2149d57fca3afd02c31cc4e1a512618709a933a82f3c1161a52eefd095b77604ee4bd4fb4d278002b6bc1e403342bba53c3bd9282fd6e61c54f92ecbde5de524a296592326006fb05ff05377b7ea5a134f3ef46bf4933df691c773f2858f192591d90c5282940df7e58bf6c9a3030e4d28499df828b8ad0610b986acf0fb30b166c4f5864e1eef92e5831c6fc21b4fcd0056b0552816ef63c79a91d57a0ce448e30c2bada2e1f471656be7bf91227b2c079cf5c4516b49772d2ea17833eeb2fa4f25d1a952fa44bd8f2670896bdca37fff3d927b6ffa7bdca47fff3d5faa1efb0fd435f7e156a3ff427db7f7b95cffee79bf7c32964fbd72df471aff2ddff7cfec3f60f7dddab7cf83f3dd8a00f29151867ddb702e20f04ea4fdb9fd776ea598514b5a310f60cdc874d4591900e51573baec0cdfb82ec910331028991d1820b305c80a7891358486f984629a57b13a318192db800034f138a0ae90d534a39403350ab0636c08a2b1931fa87f035c0810c6ce0066f31325a7099970228e07578f65c85f486b325bc100c7a5ebc90e6c30be2a7172f2bf1f284c479899f4e42e36cf9ca404de66a6490596c981863e3e4c7ed1bedf4d830607eb443830cece4c05482c8a21478cce3d8b2c3825a070ea65487768e107164c76422f498ea4a6b3d120508110e3878365a85875a7552bbd65a6bad55270650ec081d5959725211020f201d25ecd03a50c8e193c3a6e34108ce4710db0c7ec84a7ad07c1044660325ec806d20a587ab838de541446dc2074a64d75a6badd552d7aeb5d65a2badf506b05d6badb5d65a6bad3798b26bad94eee4b093838aaab0800e21a890028920181184ca64cae95286ece242dc1174f1b7213fbe832e365a82fb86943439e7f8a467cb8faf3d5d1326ca205bbedff8a43367cbda63cb89eaf1d7a495642fb617c74ccb99965b79e3481be784eb723ef4f3992e3f1b274d1ffdd12092ca0acf4a8885b2b04c972c4f56a46cf9a110cb3449174f1c88ceaae53c405c1543f18802cb69d2ca7939c9c19138640f8952491f12474bf2cc19547a74d259adbd38d3b296b7cc759feea33f209595100d85a6cb90ca4a6885a5858b162e3c8e3472c1cd10b5b8bca4f0e215f01f478a2005c1e912c4dd4b01b6fc14462fdc0c155e28e005eee2bb0162397a9c51ca9c4c4071628467271a39b283b80ddcebb16364c7c89102c4284065c4488e1029577ac78812466826ee8e3b4664a0e3899cd6ce91e226670839507262370cd18163c80f4986dc0c11328467888ea5b00c6fd8cefc117f3f342e8291e6def001afd8e576ed0cbab137bfb9f3dcecbe5f2a561d6ba53b73a41979d77799db77dcf8fba15dfbe188be8070a39ff6b97e746a1ffe3cb7cac4fc4ceb8b98e870342b9e5cc553ef29f3d45cd68b69ccba787784e135f8bd48e4e843803004e6ee3ee78acdb6f5e03255a7bde06385e8ae0828e74025e483959d1a5411b2816587169e8f40201048456548cadb9c1075425a3412974e0acf4744ac70d95a5a5a5a5c5c884c79d102f52c4d7c2a47e4e7f9e8fbbe0f045726961d77564f60acb000c0474700b22004d072c46893457060443bee144132006fc79d2204d8e457e40905d8a47e41f6a6ece4ece43031040840c01802c361051a0cb962471b20be1e3b5e65f3e20539a82044470a457878c9810e2e35f0018f961a540184c8861d5e24b2030f2e8ac02d36c618e31468bae30e11176cf94e8484f0b082e4a442440a1e4044a6d8a1895891c387c8149b0e2504d74410dbeac80f79b5831eb4d59320b215163b601d3ddc206cec1144d422383ed0224886984340ed1479c20e467030ed18e1b1eb8e3b467ad8a17e00a49418121a02083ddf5b78e8f94e4be033368a84889e30435aad7f8c31468f317a8c20cd020542a4ec50c720c7861d2151d82d76dcc9b9c186527bbe9472ae52b4da2bedc532bb9bc49996a596372e46b96d1ce7ad2dbffb42bf8951461973f7316df91a14e5fb6acb9b96ab62b0d6eab5c2915215638caf49276ccb1859b12e893274bafc86389af1e79228e3a588696aabea40b2a24cad2d2fb9cc65f9c86f88325749cb545b5e75e0f6fdab5f595589fcb9248ee4d7ee7595b84c27451ccd9ff9a402ad5db105af7e5d7b4eede54087f9e48aed70ba5a9d14736efa61e764cf5fed9a4f7b7e3ecd1918e284a94b7096695ace9b96b78de3baeec3759f8fd620908a06a9a8acac84422c9125b670e17d9eb74591688b2d2e2ff4e565ba7c71d9a44c417ea1275fbcd8e47ff4fba6cbaf65fb5e5e52889f0adf0b8ee4f23118ea2d55c09df40fa5265dc044a5543713673547f324e3ba5e3db0d82ca9542a954aa5fac0d4d39b2f2346864b05a351f90dce8ad542a1503337d01f1717f68d1db91aaf5bd2708e845f466bbf7e8efa3a068c2947aaa004658c5139120d5d9108e3d5ea56efca909171966518b0d8624cf218336e822ea4b81dbc61bffe8cc182ad96cc38127d7fca818094377d52defeee3f338e860441ff3dbd067a63ba56845c250cd6d3233992a4ce911c4f4da2db7efe647f63578e546d0686b5bee6fbe7b85f81f9fe31cc182e585f917e057f54577571b9d69034ee359cc6e7ac3169fc29a535288d7fadb5d6a834f5b3d65a5bc3d2dc7befbd352e8dc7688aa626cb610e73945cc955ecd9a0084787ba06a6f1cfdafce1609df5f347660146b002918626cb6d7235224d169b4eb0ba264cacae0923ffc6a49bcd6bfa8c4c5542652acb47f5ab3d883073a606a11547feae83edef4a98627b8b8b0e6d63feefb8c3595ff83db91a190d056bab7bfd785a2bc661924133411d982600db3fca04843016805b220b22d05b4a708610687f9728935fc49999e5ceb3ee180f63e8b6f6713031830c328bfde3477d3ab795a186d20d320b1adfd9de6e03b72c1e40fecd3874c8a0b1a009d35030aee80d396915337fe6679718f0dfaf81c11b3b52a741df9f44dfffc68edc8c3c23bbd4b7ef32ffc6b620082a48ca9b82a4bc1d8c92488e9e55c0b393dc6cf91ac798a449922731c618638c393bdc429ca48a6a05ada62c82419254121e4970ec90262921816d479284ea4262ca53a494eea82804299ffc40ca4e144a69b53c76441610960f960e2b07b39a6447c822202e5dee3ca1818e487890189943983aa4a0a3064e2461938309506832d7ce9c9822c332cbb22c0342127887750424448082b5d4344594f23c9f02c3a7c09053ce9818afa2d4c4c29aa674abedabeaa4ba746a4f7d52593ab1c64eb347ad1227c88d8eca472ae67befbd31d38d2e61881da37ca2c85ced40c5133f4d2172063f442af4d71bcbb3fdaf65e120d6b5fd33db440b9269ced365c69dbde16a2a576d0f8ed26987855928b647eaa87cdc94b5d67e7167bce19bba39db5fbb38f9eaf8f091df54c6d5d0ebba512e0c97ae94fbba4eae4be36af4eb1310158a074e10140f7caab5564d02d9fe9bcab9897170108a5198878f1c9f620cffccd5684e77a6ce4797ea6e3a20dda9dbd19972e4c1ac4dd24c29a53463167eb2fd39fcea306cfb7f30148c794014049a2e41b8c35c4dfc1c53590e8e8f8c95e964aacc4796e2b89af85a6bbd65311edbbf3b7d52db5ff72865527ce41947d2b66e69507a9ac4b4289aab898f5c6b714e7fd05fbc5aae5fac2c5bdcf68bf58bfb657e727bcd40b5cef65761057a11e6d6f67f1ef97d407ce459a5c287236595ab542a954aa5922aabb22aabb22aabe637abd69a30fe186bed79ff1f9a55593567b2f06956d127a08dc5625866b06a2dada5b5b496d6d25a5a4b6bb162b1582c168bb1582c168bc5b2cdafea2fee15b25416cb627306b358598c95c558598c95c558598c95c53e1d3e9d6cecb3080018b3b6ac71592a4bcd990e6f592a4b65a92c95a53a2ec662b1582c16ebe6972dc61476009c23693beb4c26ccc22c8cb5f6bcffd1288c91777371366b19be768b65296d6fec8dbdc134ae1884ad249fc8aa1db4566c0af910db4f6eb9e3f70940c327dce113969f2deb4c9d69ce5c5767e23ad3d6997267d23a93ec4cdb3fdc4adb1fdfd4766f86bbacdafeb767c2f863ac3730d49e67f1879ab537386bcdcbec8ddd62db2bc65a0343ed6560e8fd05c391cd5233b44d41aeae3aeee3d41e9c416e3943db1ae46ebc1fe87073dd6cffcd35673e608837700337d7763a6df9289cd22a0f9f70f069854f2ed7cbd5e382b962ae59724d53ca53aeba29bfb929c7b9295fdd94b36eeaf6481896316deaf1538fa36ee60c0e5e6996d77ad7e8467e099bf4c9437d6a14dbab2d65adabfde245b7eaa48e938ece69227523031838114489ae8156e99d9c9d1d243b2bffec1f8e2f8973f29eb9428514b6e3afc0d5c03419060d00b095ce2e244100b29661da85251000487f3aaee691e7a2054b68c576e10960fe850a29bcb8b4dc2eac61003023010400003056007117ded891009d7c4973a8f66e9f965d4be35f69fea3d53f7fd3fae7cff3275d3ddac631067ee19c7a56a88a459d7245e334b6638cf131cdfc4b6369643983a0f6c9fdf96e48faf4b7ed6590db6f3e8a0c88b2b70f999eeced5524f782cb6c2e1e4b20f4bfad7c8bdfc09005b4a13da77dda6f5f6e817ced9b6ced3d1ce8eced43dc6b5ff75a47ee85fc1be8a2fdf69fd71ea481333e20080c69886deeb9df7efb32e83730cc77dbbefc91a3c1bd06926416bb7b170dced07e7bedb7cfda83be2e022bdfadbc06bae0a0b9cf9b3601b9f5e7bb695ff72bcf7d076e1458790dda58f90eb4e1bdc68120f53ebe732f481a1e185d8021983bf4dc6bdf02a42c6008e65679ee436028b7ca97b5cfda27b3dd34fd96a3412510769f3f0cc1dc2a607882cdd19040063b30e43e6a1c47c35d66d3dbe3edd34ae8cd41ad5da07d902d6988659f5b565ee5f3cac739e839fd7d7ee5e3401f30d4faeb9ef34e7bae7c72ab7cfab96fe15e7334b6cf46f69a7c0d073eb6f6d9f671bf7ddc6f5bf789406efd024723a321b6410f02b38fcf7d610772af73177ec0ab85da83208bbd6d9f3f11147afb90e3b4af61060786356cede3f6f133e9811e10c06888d1f8eb5a2910452059b67c9b62d14ebc2298d851e8d8e41d77a288e24e262eb28822070ba2d881248a22a2400247144bf0dc80c7c54305cf1278623c53787ee0218267099e2678a0e0c9e15929b142c7548285c975f0d8c0a3e3061e9b1deabf158892d50d934625476c94f09c9438d1e1832a81a264081695e72a3ba2844789931cfef223fd9371243f47bf9d0972f493121c3e2841e91d7794dc04a197d04de82456dc90c4093bfc982740e0882b9844e108ae94115238c184243cb020a269d08391eb648a0e4850261fd4d3a11d242e8842a374de7107498a0947681fbaa477d03c34ab89cde861b08020b2c1474a9f2ea4fa6e257571617e7cd25e655225d197d2390a4c2bdf7eed6c7c4a278e4ca9e428e862ab9c53eaa89a94d3c7c4713a593ad607ad965eacd14cdb28cd5be468a7a16cf929759a7ea01df40b1d8a07711e96bbbc89e304f11b295b9e4a523ce53dbc2425a6e3644bd923595247aee40e7903449ee40e69923d3e63515c2de93e27a555fa9c94d66a69b5f65e8c2fce324dcb1bd77db8eef3d1fa3dfa852f9485b6a02e3892dce1afc4ac84581cf0d7563a659c1df6eb73ceb9517bef9c36b356e2c7a103d3dc693569dd4aaa59b73582149c3d3e92ef372a9d8192238c5004dbfef2420ec2a103d3dcc7f9e4a3aa24ca6420f2c965b42b031165f40fa49e22a3e68c568276b98c7ee91f68226c67218b2bf44bcbaa5f2d54fae5b5506dff16aa1640324ae3da08c83d50144569eed18a317cd0a17ec15ce6f5c4657aa0d4566421521f45116cc715e8b42bc8f46794b995048dc1dabaac7889d0b303d6049348c8d46fe48f9421c3dd3d14444f77c13787adf3dddf72242bda01453a3aa45874248b32d33a77a131df9f34c11b9b0a91f3c79a68fd1af935455068fb1eb78b8d8d67d23377cfe69c9e514aa967b5d65a3dfb6cb7fb596badb59ee5524c53edac1583e1d465c14444746851db3dfbace11f1948baa0ebc6961c7e10f8db9086d8ee80356dff29b186822c575e4351a81a568d882562d5cc98e13a728411c248c988b704eb2bacb0595d534a794d38589304436bda4a13c6371550a539e38558bc1512babe63179eaaeb9e050c75f72150b5da0e8a655a14274cf7393c5369c278e8499e0d7134f20f71353c1b228c3f088b0833774887b8cfeba7a1cf35e495620cff10088aadb8d4d7fe064df65580fecac7407fe5ebafacbc069a40fb953741b6a150db7fe503699f579a30ee7df649ae463444571856de456cceacbcbfd722bb26cc674b201828d602865a048678dbf7b71e18d69e1d6ea87084da2ec010cb67015df24320e8dab2a4f2859e697b69fb83c20d1582b0f06cd80edbe1860abdd3f6e7e67dbfbf81b6b4effdecbb3fafc8077ada1db94a278c14be9d1031d4673687f18fdba5b43e8d42a8adea9a33f8b18f3c6a99aa83386ae91e0ff635b6e3ae2f29a5d360b0ba64a0c39c9a2f554f4ec1722a9653b494513ed24194c9457cf94784f1ea9a33d989088372999c8aa322b212db89f46c28b67b4ed1177ed794ee49e91e0d65b64237945a14cc35679c66565112f44ccd99faa9d484a135a736a51a2689e4f939c62cf31862dc9d4a8ab1284f467b3e66e9d010778ee874c09a2ac54112fb934862625431319e275ac2f35a30812400c683274caf275348e9d18149912498d880491448307101931b26315842c592284c7af4f0822a700c8142891ea48a4ce1e910131f76a8ff4e237074a60215254b710113204b7c30042196e83862490e4b7c58825a72c4121f4b58f0a387b1040694521f8d60c098184f4a3d8fe474fad3779082330b8ca3749ff3894caffdeebd177780bafd9a8962da093afbbbfd06bad8686ddbb1efdffcf2e677bff9e79c57fb28a5f46a5fadb5d69badb5d6da9b6f6aa27a5ef6c65531d80aa79a64a9b5effdcb89a4dc9b7f64f0b4ef8deddaf7f1d540171adae3276960484dfb3e06770962bb03d4f4ca50b2a4d22b8296d29b21cff7d12826a6021363ad6be8cb4b9c01038e1c13a6c6c546fb36c5c546fb4797dc0caf48c8f4b1d473e2e0cc5289528a83536badb1d65aab52dd7befc5228678bf5eae72150c8683234bb21446d6463f2c6680352e367a6e710637e36cb2433aa79c2fcd894ce9fb6824418a63f03c8c9d7a8c21aa839e9e3fa57f3f87bd5807174541bf78f2058b74d0f4d3381a934e8a23ce3a7f6a6dc073e239b54ccbb44ccb3eb5665a663fdfee5e70e239f19c73deb761bfbe05af605dc1ba827505eb0ad615ac2b266aa25e2f57b90a06c3c1892cd61573c6399d7236e098651f29c2f85b19f1060bb05fdd234c37c673cbc85386cbc8fd8038e322c2f87f6c98651146bef7d9a18e31a28dd89a51e6d69aa941974a251b689898cfd5481bf57f67c9cdf85dc1b0c67efd6b8a316c72d061c70e3cf470f2e10720780421440a50431051440f238e50410a892494f0b1448ce13512bc2b8831fc6be4fd1163f85f93fb45ddd49e2b9fab09e3d7744d726b5d1d394e150e0af69aa597cb69b537da8bedcdb4ec62f5b0561cddb8eeb3c13e9fd28eaf230887e52a4f79c94fb01e960b47dec8934cc952cc357b368c2aa5fb9c9456e993565aadbd17e3ece24ccb5ade368eebbacf47c7e8104b0b17af63f48e14c718f10da0aae1878f249e04c19102238cc08224f012224c70c8ae0e792f37c3994d96418131c6b805db4d7cb1b25ed4434792e4b6afdce46ac070814a47315c0d1913667e0b96bb5c8d1819b6e2c9b9d8e81087b91a2d4c98f9dbb67d7efb8ffd80a4bcb7ed7ee548746f5c0d0c39f484a93be32e47b27bd3b2b66d1c89fbee378e345fbaf6c91a470a71a418477194fdc691b08fe2f61a48638cec3147c21957b371356226cc7c191326bec691e4963719bddb9e84b3edb316a5b6fdf692ab892223b48d1f336708d8f1078d19e3c8f2639ca93943dfc130abf6cc29f963b5e7efb47cecb893031c3bccb19d1cf0d833c81c8b2cf8cf2bdf3f50b43956840e555239d5b327fdec55523110923d9f7e29578a155970a1515b3186bbbce05fbf8232c89d6331c67cd1103ac4b13da3977648c1da8a31e6bbbc506dd0f0c7055d5ea06fbf7a2b19e4e8de141e153dde115e3ba4db5baedd626d6f156905a9aced19216631829c71af4d7fba2ca56088b5a7dfa337d40a1dc697ad20bd69023e61d39cb91f625366ba80cbd89f81d21b1f89a2e810dfb84c9b7e8c7d6a5f0343199b7eb4d6be04e3b5d6b42d9e34dcab561e727cc51873f69841e64d8c13d5f3e43529add5da5badbd17e3ec629c659a96334dcb79dbb8bc6d1cd7759ff85dacfbe8e8af1823be03d159b5785c15c577b82916c5513c625160394d5aab9c9793550e8ec4217b48140e598a4f3230a30b3146fce873525aaba5d5da7b31ce2ece324dcb79d3f2b6715cd77db8eef3d11a0452d120159595100b4b0b172e3c4f246a915f18da72d4c285fce4c791422db4a565ba6cc19d8e3122f65174c550b2f2ca4f4f13863eaa2705536d7ab36975899ce810bfb2cf8d86dd30e4cf740ba914da69cee89336a13c6df26e427acaa036c9406b3aaee5b8cc0c6203280fe401e8d3d76ee64c7efa1a4e64413ed59c6caa35d954e3d934d4846c7a2385761363d02774a84d9b7ea8dd6cea32ed27dfd4befa1467ce4c280ec303d98029847880faf471ecdaaa33caa6ab4d89b07d8794079bced784a1a65d4dbbced8565e77da528e303e9f95cfdb67a37bdf39476e86feed037dfec21b333a30bf0602c36dfb3e9fbfb86283db3eb957029d95a8b42e1606ad6434030000080317000028140c89039224c7d1408d63fb14800e627c406060389cc925710ce4288a0221638c21041002800100332445645800eb31c940fc3c299b96c2f4a2920e69c20fe14386e55c7b593dc3ce46d3b01cc68efe95b60e4306826abc1be1a6a13a158b335576f5f12823df0ab919f612d672a740ffab9c60a720f64e4e051ab55d7a1fe23b452cbfd59e65c08ffdd11e07dc6cb8fdc18ec5c3b1941a7cc2461febe441ac9b062f1db22bc79210f380911678973eb4818c305e2e0f8f9cc13e1500ee41194b925223e01d37abb0123ff8d77e808d33f9a987b07d291e89117197ca8735997158ecda04cd32993666027c3a0419a008e4c2f93b9c566f7f49bf88455cb56aaa5434257f8e9b468d153105452b0e79ac3204e30fd91f4262edd57645152fcc6c23028271c70ee013218d9916bc3dc42c14554722677a07d905a8e013d15113cfe48d396498f62cf87787875f72600ed65d8a99d244926fe6bdb3b107b0700270796feddce345be7b57ea5c0f8af4bbedadb3eb1b8fe8c413b6276f13a876545cdb92cd0d78929d26d391a6f505f873ab716308c5f2c0da6076d435950be6a981f50ccf0ad26246251b7f42cad864e892ec877af0744b48381bfbc53ffdcbdad7f20763c36d4c639cf8425a8ceab9d041940965baaa7fd0fbe0d53d6c1487afd078fb27d7e9312b0eacdf07da49642a0c96a974d6ce168f33bc65037e6f063ad53b2e4b8c57b812cab01b291235d11ef26f868969c25c013f9cc79fdc55c762ea8a62eebdd345ac2a469e2c11dd38acdedf7428470ce33cfa8d213da48bd5dd60fe3b692b8e4832fd574268472db683318f1b80f0d5df5e2ee9922c49f4461cab967caf9ef77803969e8792d3a2a779a8234b4ff64564447a5a4866fbc9000d2ea209a3a755090fc011bbbc41c8b50cd0d342f588536be3290c3f88758e9e66dcd205d011ccc9f91bf3a770aef6d3f3d514f6eeecbc1f784ae0a359484f4bcb303b32508d2b72145ef21d7ddb1ffea6d37d240be4baa90ae40846b127f43437e9673daa1f6529cdc713998591856a8cabf4287c155733f2044b4f13ff4300c7c4692c084c26905c307fee3869e2fac8ea34b1cf9d6dcf5ac5451be269377ebb34a3f66de4037cd3ee150dd7b1831190f833e569e2dea5713ea954140d8e4f12fc3b3605ce7000bda0c943ef1d0bfea5b2728de087ee57c84bb5bd7a9c5a6d5f70d09979e1042a7c87e5eaa4ec24e0e7e2eaa9fae28ea78180a6101a36ada79d45b426eaddda66a75fdf3337beaea929056efe3c732ed0103ae8ce65d58f27081b8c3294660d117f2564188f110f09fd595d6f3fe85b313ad44fdfb65538c8442429709f0447bb42d14cb59ca222005a798030e4c9f86512a3feb77c47a717a091aaf6027882d5674f3b1868b6e7e82e1b8ac7a6bfd48059cf205cc2231c9dd03837d6b8e62881b3945951c318bc02c2d13dab85550204252efb6ebef921dcbee309e976553729353c9c71cbd2db861a12c9230f4af188ac34190d78a9c64e4735937dc743abbd9819410f84063a614ae77e2a802350d444d15cba060a2373bc42071d5fa24750d6942ad7c44d40cd9e6c21b7db8d55ae8d533f54982226d2c4260626be9a50e7fadc1ccd84c3525721a409fdc8184e25cfe19d6111402cf9135b396f5d82ad20a47a5056756148b0808d8e99d6db91287c288beb4343fb6f37db84b39b3fd4e02d86bec28c88340d92528137f39bd463dd600e52b7c9bc685d482401b091bf803aeaaa304d1a214275116dd4b079c0ad0356f064cd55fbac4e81ef7794c4cead5cb5dcabc007d7911e0e8badcb71bcaa3bb8591055d2ec7a9ec2b0119a6edc860bd01bd80d025eb244baa94089b678f52dee797079abb7861dc27b28b17a525d561d64db8a04694eda4259a6f3eae16d36a4c64ac07f5da18e72099820a635bc5df4c0b8d57a521a95e4a8e3e78f489ac9fddd01344774372ef9478026fa7df8f3a4a447f44951211017eecc29c975374c96d9f3179a3a4a9de0c5b7c44f07162a23ef399de15781bc6fbc601f081240abd571e17da6048269157d86ced1371e4b2d8163b29c328ee1338d2edd12a581d5fc2afdc6f0c010afa0dc82b0532c7ccf9797fd4f238f27d06681426859d05ea2331f565f6ec73bd9b8374a7275423eccc484bd6033727b75e8bb5fea7e32de0089c6bdd194b9c8888d6bb09cbaf2c7cb89bed48955be0094b278ec1540ebe17d407701566590c5b3aa586e3c65f17c0dffd1e78757943de214e5e7857fa6963f4c59f2b47c02a7ac9e17fb7b6c2e8779f6d636ad122ae9137ef892d4d7093d476e7aa48145aee88a8e63c1e9d98bb361a9e72f3e31ab5ec0d3fad49ebd447c8fd340831104ea47269d8e27fdea917e2c8fd284942f0120e67e0ccfa897556616ec5fbd2cf13f406c569cffe4086b8dfb75664ee3c8995abcf878f679feb404735f8fe9ea34e2000cfb83d9f2e9e84b3e367bf7055f8d2242a0c6d3f63b277c87f24c2cdca38bd0d00d86d4b292aba9c33d82dcc7fdfec6b54705363b6b96cf133d77d41592c691a52a96c66d79568041dc66f638aa5073ab5bb568e332175e8e78850f41af3d7e2eac192f0c88c1c84429bf7131aea75aec570fb10d29688df8b1685cd258132b06bd4b3aab45c7a077816e35b1f8a82ed15f29f6e2a400991b35cf6a90d83f69178cd3b3d5f0f135ff338da8f83806f02fcb804c4e15d115e0466daa9d8c81ef423d58a9cd19065974147654b23bf5baf383086959b1bb8a6f9c767c9c5ca31b79898538c9a9ea15a990df2bc23fce39b9000b2f0003537089a25a40dd7722b7ff48ba564ace09fe8809276ed9fe26469f8f6631f1a84df61e2f31a9e409c1113d6842b0ee3bb3d7cec3e7b8f9913b14cd77c7cf17dea2706a3d46b292022819b6b15e8c20ab7f9ebcfa7ed59f1d63fdb843760f51ab718c43f83308b786e0e7cf7b7a35683edd1e8b098814691d3418170f7c85552f3b57d1d0f66137697b3e115357d0284043e24101ae8ffbf7183e01a6a70f511fb9f90fd27deadf7a095ee89dbe496abcf4255177040e19f1ce6e2e9abc45b8440d3b6ec31d2e7257ab5dbe8d5217ff074201cdd1547002a50f1606b2e7b448eaaf181a2ab60cf665018a990cf9fe1d96d9ada97c38275967129393287c353f547d1b1758e860b1350b417f68986eeaf2a6b89d97673a5d9baca21b7dc33cac1374e2358442432367c817475502a5fc1d2819c4aa1c7a12d38e4f5f502f025b398ef659fed6d3c61775678f0ea3355464b22d887d3b5788762e74f81ad3320d3f198ac736333b27906c76a5804afeedd6eed29a469f3257a3c0e370992b6f330cbedb45cc51fa106d6e33a23d7f4154131421d8e365f73f1fe9b64f384a27c274b8952f36ba63ebdd17742c95d9f0946305714f88f61edc6c26217865cfa4c398193ed4a797fc4123ceda5abfb23cfa348299627dfbafd735c6095310e91e1bc518e5a2bf3f000f0aa4e45ec7c4ce39e696e9e392c366d9e6a955d0c037cff4d519b59d526781299aaf18efa6cb652db2c88647ace0f2f231d8403477168d3615c50746c474c34480a60d88947189b1a95186f6372fd33b02fc0c4fe487f3c9cd953fe6a020933515f98d96a3c6887a9430c53fe08a50bf7c4003f0001c33b8de5083d7e2b834c8fc8a3e0e69b54b5ad6d1dc74053bf87a04914ee92ac096392f09243bfa2506d9a3b830f28dd1d101d5f84f56ba7be8a46f057710807c0032d0f26354bd3b6c01189bbbb7c7cd69403cc10f18753790747a933f91ed1e26202f972ad7bc069fd9b1885fb18928eb83abca2f41702f8fd1a7aad2c3fc61b3a6705a11f1e9df66dc7e99183f9630268043a0987d9e239d2aa64a6c895fff2bb988060048c68f985b02630edca727bd031162feffd0425f7976006f2bf7ed7423fe852c6a174cba6379b5e498dcced8e928371caa0c21be998f0aea7e58fefd7beca45f5fb07525871055368683ac5d2a0775272846a61d38582df1adb8306701462519f44b69254230425e4a57cba405b320688c83865c91d2dc49a37e2bc107a6d3a274976b5a2c1d8e0c641df63e10c6968345306d11193a3f62e0d027cd0bc684a70b69c34871228ace8f4bb78c06b0f8bd9f35900bd0ef864861b17500c013048778739126fe460323694f71513d440b0c81cfd3e61280dc75f183e744f3c441c800eee5c11307561edd62e5a27b7c6481a62f3016c546f96016549afd10f43585b10da08ec2c6be883c8a7313aa6a8e1d6e002a2b3889d24226f5bd722fa375e26bcb79205889e30b8c82d0ff6e85d8a354559dcc34f48e81ea4b66adca5fc638edc714ba4d3bedd3148a7c67ae7b64eb73b150094c509ce915bc935474f3106ef669f4ab53c52793c573682ab69715b0ce15545fc638cb4d7dbdaacd037ecbe55f602c36427508a4650c33adde290a8ae2400094d47ad12abe8c79453025be58f915158d5a66f139dcabce6f5d31f2a9fc6ed9d75667a7b555ad484cabdf1aa52502232de2017f6610780aa24dbb8a1aaf2569784a29bc1bafbcf5bcb3fa1987799e97fd4776f2eb789e95e4c2147fe74112b1c60c9817edb30b581690b879e5eb950bf0ce9cf46b342055ebbcb80393c4c77cfcfce1a3098a1cd065b10aeb723457a2a1ff20868b9332eb2f192ac24c67397ddca93a6729591e508ec3fdb2983e9677c7bf1366f986595b0743b0ff63156b464b1198249ffad559dc2134ee6623a488c30ecbbf370aa2cf03b7fabfc52224e2e41b15de3bb26c08c27d04b607a8a286ccb3097726e5e9a48195df70b2419d32b55723341dec8458e75b80d69ceddf406e46152b9cdf0dd2cee1a4d3281f10813db26bf3fdcbcb9a15586e7000ad22b77abca3c5f4e8f282bf074f1661d1ae937268919d254ad2f4e9d9debb498b4a408dcacc3c6554745dc51ef1f29e7a599f47c603832a172d89979b79f07a05daba0bcd89ddbd113fd8e2e9f683b84d41c3f60b526cca9b5b86b64d9d962567710f949e1df6353ee949362497092808d5668280f9b8aed57d0536ac1bc5aae5e1107aa5f79497fc19009141ebc462ffa67d62cb24ac8982c87c78624f4980001a886c0b7336481f20e14554e70bbc5716e956eba7bf039bc09a6d80053dda0d02dbd0bac83cfe61ac6c6fcf961a3ccd22962d5605b286aa405001879f3194e308441482a592b9d75f74d7688f520012553e3e0b91ce112d9c85e5918e41668d5eb8a3cacd491d9d71302623c09393da84a323f90b12f2d04eaf77479c99da993c0579015dcb51640c4614ec35ae3487fea8cec68cae1009bfd220f70fd1e4578961ff019c0cf895e6ecd6ee48821521753409884bd753553a22d35887212e7009e4a0b140d3e590768bd20cc9d55fe332bf014a04fee2263e409f896b3c44e149d1129872267e74d0b4221f8daf6c937af8f5cd319a009ea4d47dcf36150027098b253e7a28305b87482eb266cfde3eb77ddee3fd4aa6c200f40e73b9b972660f18974f96e9cb9a5b557cc0a323df5611129ed4f33289afa0e1bd4f68c30f5ddb9f1c6c19ab64f1c23efa893028fb462d15efc07f55a00e1a650e2ccdb9023092d9e0c207fbeeb5a0cbeb5027a2c5ed1aac2c228f14dc2a0b457e894b45aaf7aa8abf97e9a470b436f434cb57f571aa56f585afdb89fe70566d2e3c46fbadbaddfd1b2d635a3dcec889a7115453804a188893c7583f9f4989f417f4fd3a704051be5621a1372596f25fff55fe2cf0a9fe00d5bc8bc4659a68e39d21fdf0a30c845ee9927bdfca6d4e1a2414be17ba45ddbcfd3c6e1c6369f5ea936dfb35eafd0574f39a8766144079b506cebd418ca42e3f62a1a08f706b94f1d3f06cd09f51feff0280855c428026764574836c5412f2a871456cf7c39f3f58f4f3655f3d6f5742fdb3405c20aa5ac8f0865b3e39f716d6979a0bf26712a8a075e5b5da75b0b87ecf2438f1da8fff10d56de829c336275708492623f9b22b94081daa4b8c865e992e2dbdf41f65481b21e0a80c661753a22e798a7f5e192405b928be94cb0c24638bb0b32f3b243242da2a641af2b90ace916b6b9d443deefe30831a41df3cf225cd4f965cd5ee9e210bfa33df6cec60f3164a141b022c343a5ddd03712cffb2dc234dbc24701d5aa8211d9db7ffb6b5aaa06f25b1fe6d54d80b2b72a934f51302ef4445ed4cd8cdecec4a4a97368012beb165b236f71616e270f93361ebeb559f698b1e3e78fee7f8824ae6a82397ee7758c09a7ef09767d1d674931da7d89e9b4fac278af3f95178918f756ebe9fa7f7623329694d935d6ef20d646de0ca173cdf3d859b070a0b6cf14e5d5d2824b433f0239f631146ec8c7bd53720a0cb9d949be29e089d08274663a04cd908aad1ba9618f94ac7e0f9183d6bec206f6cb49fa43275233e7a29c9a2ce1d2a21a5f9c44655bf6b5949af186e07a234e1fb573fd1c71a66a92d9b2166ece11e486fcbd99f1d05dadc40add8e928251fe97ae89b3fb0dda3f4a023219936421dcb685377ce0bb43f1561bf7c7fd4addb3955378560a1f5840290a49df8d626fba22f3ddd5196e6835d0b81f814c3e0c9042a8f0667925b9475946b0d09f01ad138f6d3330b7a29afb8ded1cdef477d4516ab308f1b9c0fabe57287c029322b09c69862602b26171ad548695318e09ac151318ca9ac46ec0141dbd25356d3f6070915291f86b5ec95f9f8030ac06d8f94625f41c7afa7a258f8717a831c7ca1f283d8b34b081698a26223e38e5b2a60bdbb062e3fe36c82743aaa52df33a3be6d4347f0c8cdfb5c636a8798987ba42a78a01e6f7da007f8e3423b04a9e179112f1edf0358cbc399f0876a7ea7ca3f02860981de07183d9583101ee2dd7506c053c0d97231407eea92ffd055959ecf423b7a1a6e7d2c985e3ecdb40e276605f53a18241e398464e3755d32ff8f92102e3ea992f7cc8d84724e76f41d093e67edb3909960fae122d2a10b89f4ad49203105d910fa0b857485ccc4af10f87b9291680f10a542f7b2ece72a2e5ad0743d6fc350de698e45c087d7c012cdecf050c24359ee603b90fde0a11a07e27ef145287dce023a589cd4a60f3d12040aa93e739c1c1477e6967b899fae43ae482fe856b0de7eadc1281bcf5c78c635cec4c94daf8e0a2ffe71d0335851a5500347673c19f1c6141a06602db3cfc8339d691ab355d37c21c36597f402cc184360d70e3aeb49e06e8836e65d9021105402817347a8ad4593f932f4c60e03fa9ec1808f5a393094869687478400a3724738aa1c28ac91ef70adb1663aa0fe989f909a6a150f584c101ac5f155770f3929db71c174c04785d8fed860266e2f5de22c71acf412a1b7dbfb04848e20a24538df56100dd74c8f7ae9ea5e05a7f54886e5e2d5558443d6060657267033ce69eb7d0c89b4e75f7ef4493f521e8e86adb3d969da5e1bf5ea80604c2fa9c48340767c0031085025c66bfc57eac67d322c91bfaadab23343a39c8fa88f72aee817758967a6ab94c698ddc323d5ade03e15a6a89d81add8e3f7f248e82f2e2d2d0c18910f415496bc93b37e650780db03153247d179541e5a5cdd2694d4f3a5a2086bf03eeaa563ad379353befa4a15a1a5d22720c079f82604e6397eb72471d2871db4deec302e1ea9a84e604e1917c0d152dafa02db6b5ac86b33690098f17161b1ec8a3a0f1126f7e077c6af64e583c68e13fb3d70a201d4110798813844f2be7423a80dc9e6bbb3a18e27a68d895449e2af67881519bdec3ccbebc50b7cda7c7574622d10b6eaea00c40a2e4e7e666f1fa46449607143787e8ae2d532c0ba241d83ca39aa824c29da6db20541b7f239bc7467dae9fc55d5c0900ec3cc20d39e32dfe0e15c8797c34d290f6072fdf80ba1b61a213837bad1d1ac6326b60d1839626919f93d08ebf6337eaeed86435d79280a393f253a04178e873bbe8d64899cab21b86e109cb39db8cdbc51edd91000b17bbd0dfc757b7fbe1d7a784fdb11216579fa993f6e9061e6692f29a49217f82050f5c0efd53d47a0cdf6c35113e97826ad7e0856f17394d1d4716701f8b218157c0b308b0ee322ead47dd145c7d272e04af3879e9acf7042e5fa058ac76343577d5851233bee0849110728a0a3cc7a67b0003a043376c0a0d2936d574ed017763fd4794bce972e21096f6b66a7adc02c095f50605255a92f6a5df88a784f0aa6c23821a557c7522d0853698856d9ec769d0e837a46e7fce58acaa9f67de87f32a6f32ddfe332604ae2e2b50c6d26f00e4a7fcde3025b75ee67d863123db3248b94fb47a0237402783d1dfa400262f3665792542487f779f0f7b06d1a6e7051b857ca2a29901daf2800a7b1f2822313f74b5427dd15dcf50fb9067df5a66c833f8fc76d84d9a4fe11487f7d723a13d8ec91425cd1af8dc98afbe1c2700d3e8e23ee132928c313e3b8c891ec6d49343904cb14541e9e488a7a7724871947001a479764230688826e5908d784de8b3029230fdff90f18f74db90cf943320f9c71eb469a0c5536732b1382894bc4a5f14ef930e05b1c8f0ec02f6badd50807dbb45c82b8110f869c60a1df1d9f39aa5a4307c56080d1b7ea4914bac311cf443cfb3799306f56dd64e00aca050735b5e3e603662c8a8d2170ed0a436df407cfa91f413d8af629b6b043b73d4b5b2536584656bc6d7aad86178a99c7f9a6393cdbdc474b3d471d0ee2f3f6a3228f8fae9dce3847e1e6a58202a4dfb23f32cd4bd9d3b5ed282081cba9a4745916eb9fdd5b20778279993c1117ff4fc32cbfd28acef3db9a7ac361cb9446c5851af9de285852fab61c4fd16361b62528850c2e0cf6679c250d01919564ca7bc844cfdcd4145cf45e6c93f294025b0b12e207c0cca37c1cce7bb4453a84cbdd3c43ce1be4e9a00ab8704da1c3e4a9d94a9ec346b06ee3d57826329e2d72f5ec698acaf8800fdbcb87dd9dd0b6486a1d76182af26f0596c55a3acf680dbbf626f8c724cb1104aee280234d33c4c74f2279b990bd2df7498d5b637886609203b784a4623c3c9586f3a7367283c7589214efa91a8bac4727473001e14d2a25269468152891d53a9352695dd6e4681c7a8c3cc9dd6b8fae5aae001f039148e07b2ad971d9bba4b1e698c889d2bfc08cb62f93cfbc830d119255ee834307173c8b4337dce18af21154e324cfd68c83ab83d8e9eb5484e5e605c8c29e242363117cd1e662f6a1fca25c75399b650cade19a1f1068c975af7d3c79017b1cf10d7508f82c7925cf333bee0e97e3175a517bb585e69ee653e7de87e50675723b72e081bc75ef24b7a945545cb26006dff0d97d9c31ff99b314c69faa61485ac2ba9b97c9e8f76ad6fa93e0bc8c820a83a3f4fd1df69d445c2e73bea28627b3541d88e7e5cf36bb36a16a628a0fec8be0ba6c36b20e9333f34e73f6ae9aa774759b537ed19dfc7583f9113d4f6dd1b9c53261b6a87240400de285002aaa4de2aef2a4f202aecad22fa65d59de56f7a91c14881b08d975582dc08d17f6d8e6c5f6eb9afa910c09cde6150e3f8835beba263364d3f5d85e5ec2f877bad2b9d395e5db9ffc057e0cde178c065d3907a70851c19c15de3032ad340a633a60b662a85eadc9624cd8a20c35c651adad037de739cb01b8b14e9acfd5bce915a4c055fe281a711b0a685ffcad78e9c3f53fd69cb82377265abc5af3612c81348d8b318300416914c94f9fd1f66315d2884883aba170d2175b5eb5bce03da1b67e0e303c2080128942a06a847363a9e1ed434d6a08877c7981a40fd6beacc87f81544a4c2ba8301f207e1dcc477f3188551428ca9a25e78d062c8fceeebfca48a2458f4200b3e5ba297b1593ac84543437e0b7ee98e4d5a119bae99496acd7e1823cdf8c663e0c95c8de24d530ef58d9882ceda656c96f2fd41855f2ccd520f11a700637dbf3ecd32f8db61cadbbaa6e15d38daa75f8520611e9db637e63482cc555306568f315ee37e8234a3176473451133a5475d3c22006d46c72dda6b391fccb06b3b0c2a6eddbea60b2d02ad431fa982d09ae667207a7c48e9bfe8e1152fd598a9e3f0f340638f2a05d41fd42ec8a620d3d7dede28e19cf4cb0f801ca06d9e5244760ace36443f07e3a6a9fcac0f250d0b4273d14e1035d097217d03dfe445e662cc7e9834980b12ad492c4300211857272c193a48aa0aef31946888303b6351c5686fc83e3d095e7170a08ef8fb09286ef87e188567802233745a50d086344da80c6e988f7b18c1539140bca2891d38401fb8bafe7cf2a27a39cd8aadcc4c26493d4db6bd5bb53e4fcaca6e3dd9b1dc8aa90d9169ddd91760ed98692620a34ae3912b3d8db2a06940097150edb61e67590a695b7d94bcb4d6731300d8bf007aea307d807bd4b58b3ddefd949dd582c19333e75705605bacee9c2b31014d3bdb5a757ca5fdb6a67a21557edf54bfc4191c5582865ceb97f27dfa326474342643a24ff946f8d7c2911b1b7d6e27e5e037304b3a3bc3ec879db1c378db0aed224566f4b2b0344fb735a26c37ed04d78c692ec70617640da0adcfbea1b94986699d79bcdc952eeee62bce25da11a2e55b03a5b8c4c5360176596b8afb0685eda03883dbbdb01e416bf608a40c68df34ea88ca11f86df8c46806bb94acad38b336876f8a358c4c22c597ce3c69f3d804c0b43fba70a1c87d4b15526a4e59754788a342a2fa5cce82064821d32c4498e2cdcb0d78fdfe2fad1d99ce2c8fc4c12afa7865d51be4ca1adf0b33f46b9d07469efdcc61b6ff2225e9b64ccefa115e2a90729fd9ceabd7264b438a27f0c74078298b0dd7e05bf548e78623992320477be4930309ee6eebd2ef79dd972da6e05b906e4018d66835ffb5a07a741b365aaaf5394cee7459f30b510c7e651043436cb6c1e7f5f75c4f6d5ee4443b42ffc410ba7a232547886161cd8633759321f77aff0589bf961a0bb96689b521bd957207a1a5b4816e71ff0dbf5b6dc920ed5d580d56c661b0403dd4841c8c3201ebadbd195541080c49086cbbb86a8d8cc20d36f173c2bd7b673eda062caef211379a9bf759ad41b0a8f944150e89f0ffcfd8f6fe5e9559297648eb43b5fa0d365680efdde566488b71f7d078ec5987ecb063ffd1c4cfa6a06c424135b2a826f1d6d08c6c40db6718e4757c89954f3b0086740ee16d2375106eba295d0941f9d49a884d47d8bc7a5b7cbf08e8d38e83347b1ecba60530d692bef9edeae215a3dc02610c0b003b8cd8d3b0434e8b8f47a09e195d911a50c2db91acd9c68a9de3cfb956522c8a00b050d2ec9282de6112b40989e0ae92cdde1f5a152c6ba58a1e5a6532890a0eebab0fe5bb949c7cd60fa3ec84771c5b9405f03ccb5e208625e513ebdacb9b824b984efa2321cc6b0268f4dfbad391b2b76d04a036f61cc283951971296961e3c53d4855c234ac58a6f9096f423a281df1fe01d47c79343dbd314f757471705bbe1226c2c97af26c3ad7df5fa61b55eabecabfc8ec3e306e461364e39246f20f0dade0620d6cbd69d4c794e07c0b9f62493c95eef6af201afbe736e4bb2d105224805f7d91769776bd71822e46cae4e68dade87484c3b6321b7d31541a753973bbbe0710b94868beb496238033365ac8969b04a8f85323eb0e2eff8dae8d815373734d015653b8e94e085f5b8bef26de31320632da87a385b25c8587e9b0b7b9f50321d76955ebfc6728c75afc0960845cf0e1b3bdc1a276db77d1dad5ef39001fd023234c203be29f9506987499c0a4a4105bd9f57c643b65de2de66872ddab73b87b11c1ebf77f85ffa19f25dd81be39ef051f6392e04ad1afd4534b5ab3f4d4aabf78fc596b4fadb6a86b21c2094371f2415dfa5587ad3403e62c5481c927919821ce2c86585c54ac1b69016912121cc5e4c28c6c70746b1865858227f1b6251b16018cb637f991784a85ee946c7a2a29a8e546abfe1b2c2c1171cf4df753ae44b41f223feb4b1471d5c434cbc3597df5c3ce576ecf960dc242b032fd0e78e77efe873fedb6e70bd3bbdb50a0aeb96babb8b9b323f7485bdb9d53e8b3860903d1e51bb6df2832cc808fb51fc07adebd3ee51afe2c1608ff70b4ca38143e07a50a4b79ff885fd09bcde1040a9d73b88076df0a7f60f80cf8c498a36df1f1d5c2f8a5fef61b537fdad0d05c9c4f50945bdd28dcdfdb0ee7aeac941e6e4b9ce3854b76873e738cbed062afaac722533fd53fb0ee4e39562b3aa2e83e1a832ab65f4bb1a86cbb675599e9920facf277c97bc57df5d36967a2e75ac3583823a52802765cb75ab318f8da27aa86c5a1b41bf50c4a7b6a385883d40d53978d1748ec85b6c3e671a4b485dbf22c58ae4a114eac6e83e1f71db737231bee51ffa5d96314c7a66d67242596c77cb65f32e51f1ea6b61e4bc1ec395c5b24e1026bdb92c2807d95e523ec6087f1651d877a4b4397f604f5a364cc49d15e70e712a718e8c4b35b06027fcbb96ac8869089ac0236fc42a5042eaef949c1683293f0397497ec63623d206e9ecbe5170a96671c2cfaefaf2e88fc30ff47f6dc6bfc914b7780343cd1ed5b1a152b0b45abb6ee34f19d2665825a8434f19c3cca023a775d828c026532774ef7a8a187bdd0e5fb6f4e1ce3d71c01934b29e1da139c856fda8843b69c70e1d94d07c472847164a51988acabfb1199b8f9615d648c1795d9188733d17444d020ce71420e5f7b8294649ba2104fbf9a09b20a85f104b43224d9fb69faf5a61f41a90189e65e0376f24a1bb2c98f64a956dfa347260c22a96b01601962defd6e3faabab5289c403b390e93e3df8b123af1c68e4250bce5c162c35f127a12bba02269f3637e3cc91ee3bef10adddad105fd2b28be4d8b721c7dcc5b0923d98c9e5968d334ca50994a7d073bfa821cb0d94d644e49f6ed345bd041605e898d6f64d7289d447879ef7b82e0109f5dc2ab02614d4a1353097166f6f634e90ad0f9d53215644d22116d4f457440f8e3925c8007500cd5bfead5d88b0b8ab8d2c4d8f5c9bc1bb43bd70c372f40fdaa60b7d80c2497e13260dc37ba23190b140b66cfda3478b7f0e9d4d61b7e21f46492b8b4ba383f8a75a3b7703db9e6e21378968864093cea93edfee3c2d034e4bcde35fa751054b8e56a225e022da25c8495cc1650ae011325f3900bf415f2ef8b0d195ea7f1319a3b064f0fbe95d7cd4a92574ff33740c9a99b84baa265c88d7dec6aeeb06b0043a2e9b1cd503a1e10a70262a0f77d600445b254e2e0578cd8e7d1dfdf68bc97e8a77cac38152cafe84455907d123860a635a913c1d7dd29e95bc601ed52766a107ba73a2d81283400b7480cc6cae1b5926dfec5c934f136ae9f3e135a6ac2158fa525874f6a50a22afa939a848e217b4776e68683449b6cba1a86967e18c17ae528ef08a3a00621d2a8fd3ef883f840fac1310d8a33db0fe7fea792d0960c1fbdf6bc868927037ff3febe104c52acd04405fc8bf22400f71ac629399c69300144a057d1c48f9d65413d67f880022e3ef6284dd7784d4ff18566f880490719c714f13d8e2f86865620050e80abf345fa396c788ab1f470863d75bb04dec472709f1306adaa3f4f67ffed8e91a0128ba6d8c18bde7a71a0e9772e68d1061cc73cd51dd8e1bec9eacb28273509d6841c06bc9f4156da7985afe7482cdf7a1d512d4f94df0ea953550abe01386fe39f48a245fed72dab43cb9c41b6d7e9ec8df9764258a114f2e29f410c52b15385177409c0cbb038841fc96d1d5927d06f38f8d6e3240fee5deeba74405013820011851fa456c4827bdaa553ca468ad50d32c8a59362cb86cd56bae5d7ba343138f234030421809243da4a84b1c22f8caa3781eaf42e21386d242da27ecc1c0638f4f808aa52f6d12ddb2f229474727a4df58bd999aec7267fbbc509693c68bd83b6d23638e32af36e761f2553b7a8ebdf9eebedc60924efb2430c24cc4d7762e7f8b3f60f4f4442e60f0fc71b376363e2047e7841a7a0a71322ed00bf978a84eb78abbb2b61dbdfae0a033f577b3401c8ca73c1ed8fdbcccaf256c2f2f6d96b957c330d987f127de0926178df9c7bbcc6b50560aa2c987c88f184eace686cf92268b5ea64307f208859adb5dbc294c19d5606d28eb214c52287a6b1029073d520f8775dd997df4b905c6f854915f110e5b37dfec6d087dc9a47525ec665fa81e2df0e2eb73bb92dba7f8294812816efaa44d8622bac55bc340c9a4b28b4b84304a195dba557ec86c7c31177c868d3a2713d309f482dd9c80a98ad91e391b84bfbcd73be09aa512991027edfe15005fcf01f68eb70c0e5e6102084d1c7d4920b72084dddd640b34e42877ae51575b8061d65b8112679fc2be0eefd991e0393ac6636bc907e89a052429644247ca1f747fbd19b4b01a5cee957985b18b3ed961984b11fd17c160c7da93a57914e43ed52c8ff0af19acc47e769d17e81c042257203a48228ffd2a27ec594873d53f2635040877ebec82974cfe2683578ae42b639aae498da26de40a083b532338a21e55bcc3571a8e03c07cccfd7a002d1c47337547def11e6d1feedb4f26dfd3fc64b14492988609f328096bacb3d17a013a95c381f948659244a3556a52055295a62517fa802748efe6c0bd87cb2984bf478a3317f020adc9cd67e9a1788e9aea29009a2b31525eb27150384a1236dbcd5112ed90496d9518c5e0146f1b53283d3a731012c98b792d99bcc78c552a432bb42a347e67aca2da7b8e88a21a8cc5b196a59646047485ce361b56e487edb39f8f4976ab16a7fd6d42bad036261496927f6b5a37b421b7a59056776abdd7ceaa2fc0ac6039b1e22d2ca8f9b42c1c005be2c93198a82cfb52324f7d7a0519dbaa3c3b388d79c71f745bc1c2acdad28124e3a9b4f6371c62ffe24c4b9a39c0101fb1c3d8f26a413053bee589bdb1463f9148607ad4bdea117e392e3b9ad48ecd1ee1c6f6a841f708f7de7f12a3fec22653eca6136f8e50653c7b84f441aa171484408de7434ac3296fa74ac230929e37f7e6548a91cfa6c496ea86db6dfd331782460368621e53fd0fa7f879eb8e23fa237cd891f785e316c0fcb3a0b0419f60e406c3e3498af8dfc0a637ca0a0ff8aa0a8f2b718d9f8ccd219402902e207c8d2a9884844e310c99288a7680854f103520d944b9ea8771ea12087b637b7db32369f476125e9d869191f74e277917a823ef5693bc128fe4c1f9210f1571fe46def3192e85ccf7b7ad6d22e45585c57bcb93809fcb1727afab0d26f8fd4f0c0b82ab53b87596bf913b98c839db9e1cb01f447a53d98753d7a24b19a396bf7f3d29cd37757b5ecf90da455136f7f916703d39bb83ca40be66d7ebd78e2260f5580ab114605380ad10b04279cdf1f3d292964b36a120bb4a956cc739ca3cee5886a2958184c3de77a3d1b772ddc472720668ad76759c15e25204e3e6a8eb3dbc89703d24c791ea980721ac7d149d28418b52f6feecf68a98a025b2567220fc0f7c6333901ac94737093c0534bdb7689eb6bcac334a31c5abba000179a4c00dfd07c7b23300c96961e3fab3b2c14f254c1828c2a146d898fd497e403d426eaa1e850c2a653a58f00c81c0f131e0e65869079b2688d9c01adeeff5cfab1840bde832d6bf88847fd638650ab76fb17c208ba232bcfb0b941729ae992c78ad94488f647e1a718f075cfed2fea2af613204d577f05323f31c80cd3352856ac4a3187195e0545b28b64b5da0b4bffc047bf1496d4c312223dd379f6c08d75c0cd1f4daea1fefbc7a43be5162e6528e2e581a73f7cb306de4642db9bd2126c175e2db99464f545b096a00d23790ce02662aa800e44450c021c504f9244850524d04fac9281ac7c573b061469e5e4962df2637fb11a4c3dc15253d77d26d32faec54abcf6203f64ed2506e9bef3f6e6d278518d69bf4a74dc53e55f3c547f1df07ee3293431e3bfbdf29114aed51d2b39fcca47205818b7436940bb32eed1b86b5047c41afdc1be59235bc6fa9dd2c5ca4449f9682cb8aa102c8a2bcaa56b89b9ddbc1c2bdeddc482d5ce5e4a572e1b6bc9a020d84905402004870fa66e5fc657a63f660cd239927ba3db50deec0f4153133c3efdf79873c460c9852127f072f91633c44139e2e57904af81f0e7f82080d4ea857894309fe1389b09b3463d428c12531777d6c7b239ea8211b1b5112bc27763b637421b40a5909906d8bae894505d0be455e0cf9f95045aac962b26cc533c7828a36a6d44dd49f312aa48535d842817adbc180c1b7e9c62d71e1db048e03f6152a03e63d76aa1c6a8e3ce33b8e9ca18b8538502f730abd2df79df06b460fa8fb80a3d2011b753631c4f163e6fc94a12a769c15e19ea1b6b71a3cb37dc372f8311713dc8deda4e537a5d950b5193501b7f3b41150271ee82cd28ee9b06fa29c8ed7686139970704815bb1836b5a3e0c2cb24afc804a2612bd6760ef6a15ad7a9654f794d8a6b3e526ce016287774c072871bbd04d7ea840cd471dee76c09a8d06404141650da2bf142e0f4bc8fac0bb4c75e809ee680be7603616038844570eb3daebc76ec02d02a40e1c2b0c19d3962def590a85cb7f41399df733c444703811e4fd4b4e4a7dab6d9293f025fd35de5f2bed64fde525275b11398d0635b450a5c39a439e57dbad3fc946065b352923087b50d2cfe5909002e3696c3e74215d881852db2c58c30bea5db0808a760dcfa50ac441cc73902e887b9e4c6c62b31789cb272083c4b2f20eeeff79209b5a694bcb287abe212f8cc2b5c2e4baadace792c8106fd6e2092695c33ef087b48814d7305d5d05efee1374080431dc9a697292969b33bc1838c2127a74c5b6a2278e71ced592ef27bf5f21bbd1a08218af9f9eb1d3e423cb9e0c66829194c50555ec74a6b8a841626674417945ab2918a0050a8c212e14cff82d9f7ddc96b12b848600d7078799081ae7a7c0d41a2734c1dd09235ad09ac4f2089606ad33fa3c8ad3701f280c75322a4291da44abe70b013356c81915d2508ef7fc62e09d82623568f0775cfab54d3c3cd009163b57947c2add1078c59de798117c9d2c8630d5084d0519298ddaa4370b5616d680032fd98ac9933a8e4261f3f47e620d1c07cca8e0683812283e95c85c438529c6f76bc803a845ecfa432dd747834bbd87104a383726fb323acaaf539515f0f13b943ce1fd078a8fa094fc94c0d13db8edc37efae730671e5b8814a137ee942e2e1bcb894bbbf4d34e8fdac6cead22c7fdcdbafed71b195b0f08ff9e9b3e3bdeeb9b0182667836d9be8a2bfc4fed2efb1bd639f893bd472b7ddec0be3660794f2cd6839289f5b76c2824b7ee3d4e73a84d2bec44d7e14a202bb82adec8bd6aaccc5d63244746969cf43b82f7ed00aba0b0dad8feb133140afd282dca54b0a27fa8d337d07d82beb9126dc4e77e8e8a5b1c893862405f615256f1f5295fb3d9a8e36c5a8c9b2a8ffbde1f3e127f6bf77ccb1c3a004fab61212a87c409b0886f1e0318c4d6aef90818ed1b149df188aec29b4cc4b400947e151a00ed5e4dfce385cc506002a19358ae2755a6266e2c863ddd1e06b8d08a0b6f22d7245729caa67ffbc3083ccf68320ba059270f70bbfeede1504c575fda3624635967c008bfe7a53380d15c98cd4d5fba0dc55af29e45d29484362c435e60be0254f8af0afa628ee24d05efe9a21c58fde0a3ee088f18323dade28299934b8a64b3b0e51c6a8491b8ce814b305a52608c668d85b5b2c0a4cd07533fc9f3a3342050cf87b2b858f27a870518c8e20d35033d91dd5cf4ac55136f62fab9cb9e3a5ac1a84b58ccb6135f130446500ee6ee8d74690d7afd2fcfc6f53ba86050bbead1bc21dd96379ea26dd416b9f1dce62ee9e8ceddd0cbf8847a79ede441ae38c07d7f07a74a60533c9f85bc42956551b256e6d7ea0efa2af4ea14c10cf57db2587cdbf946551d23ba7b66481a2218b2ac08a6c4ce9335e39f48a6969b281c38428b104eb1f7299a90bc4fd106360870ed5dab309a15250a93b3abcc013638dcc538c62b2039763b1c5c7b11d81002744d4c87e13e3dfaff7f546ab850b7880572af273ad838303962b18d8170c6e66839dd84e982ed8aaa9e38081be3feef16098efb7f33e42e930121aa3185394bf72330c5d3f5ed567b59626eb00ce4bf6eb6883f64978f3357f462cfb3f0974f0eed77f2678ad43697e6416880b147db4473aaa8046239059ce88350ed172f8dedb6190ed36459b1a5a5963e2c259db5ba23835eeedde26babbe2b0c408b4841e84741b34d988454c1a54b060c6b75613ac7d41a597d557b23412f29893d999285a123232f59fa39417750f458ee0e13877881db3f8c86017683d83d668d545d2a426b25da043da04d111ed69becb26f64dabe4aba0a05c77fb6b8ed1a185f5d66120a68e683bf025581b3ea432a486ffe18cf418ed0547bede9262e2bfcdb7efa44241dc1786252a61d05142d99ffac09cc69665925a3c53cc737a0a10ebc6c6a0ab28e1005aed7ad9b224b5edce4b555a531000e4e73dd6c25418f69144036643e54ffa96276685ad4015978f438bb72929b1b79d2cf8924c53123093fdb11920c9d723fb57a12b83e0b48a642102e91f4098ed2a6ff7b3da52833145373882e62f65d9961ea4dc1e07b35ef53d9dfaadcc1823f0755b57fff7a35473a5c80dc258df0b9fb64c3aa57806bb235061791857627d039862bb1be12ea05dbece4c13ddb866bbf49516349a8ff5c3c91fc9a6eaf01e086b1c76693024e845d99affecc51fe9ba64f9062918c051e8eb922f2f4c53103a268a3f3d17d28a3c2a69daec03e06419e01b511d80135cc22b2eaf72f87dc5b5b3847cab0a2b42dd785e7f4115d86247aa73e2628ba7afc207d80f401ba003e561c7e4cfa8477b8e5beeb8834713a99d78502939ca872a874ea5a2565861fcddca64ff36da1385844865712be0a087bc80b60c0c8280315b4a584160b8629b33098bb234ab8f5d85566974d9beb27c53f42614606aae68faf4d4a05a5aa9bb31cde5ce4319840e89c81845765df7fdb4691a692f1d6c22cb7324ceee2d0b9890054c53dffb666851e542e60fbe2e1cd75b8417cb06d58b81315a4603964ceb81162593ffd02667041a6c15574bbeb2ac6bd9e18f54007b3850bb53a952d749ec93ae1ab37f169c0765e47b09e2ca2642e8f5604bea77df5786197cccdfb3f1ae5192e2c90b4e4267dc110a664bc44cac50ac0565d761af39d6590579f5e7e5f95b7f0b5496ba9828f7f08d2afdf9af445ca0401d6e874aabd5ac8b660797cf8e6d29078195c948e3b5609936befeca336590d0b0854015315b7b874b6e3750c00cfe865806ad3af55f8de10270688a79890207e7462feff65a0a45589f38caeb8378f8cd7d962c70b0004db4fc61d53ed1b492ace4da75757ab9b90e61bb3912e9490eb0d35b03bf50eba842069ef1c3acfce0ec6ae60e78851bc09ef2a2ab81053059482ec3d04aabecf1a28ac85fa4332f697525b1e05f0779ce36a1f2d990adbce0d58fe178153d34e28aec1a8b2ab128627a509d7cc99af48376618a7d870c2eb1d373c548239cca0333e20dd0028480107124c3df253782280b8521a0259abfe07fcc0ac0c6200c3f99d5528972bee4ef4e5ca15ee5c936738275ab4321dd7494fc3314f3b781c09e8dba7ddf8d14a81791547c72b2aa22ba69a807d7ce8130dceb12b2cb4cf824d91db24d379964238e758a97016a787536c72d81070d2bb97b065e0c62116472c74196ad17e1be0b8a3268a871400baafb6d77ecb3f43bab7a701b05682e7a3f68fc2c95e87abaa8a9b292eacdc671017d1d0f23b3e0c6b9e38c0d304ae01ab9b9a806e8689db34baf9000d724df28006e89b093565cdb983434441acb1340e6e8085d6470c0d2862aabf4be86ef262b467dac336ec4d05aa2f9b3f74ba3de5ae532e4a7e7cc89706533f3487da22ee53663f4dc8f3cde006a04fa2115f90d1280f089378a03823e9581ea039a59b7af43ba7542249f5e6cf3246152acf683a8c8d96e02ae9cda8e01eb0474aaf6ae45fe8802647f4471c78cc228d606c5209a77f7050487a0c0154ed2fedb2b693685cf5b5f80ac750034919624a2322e9a2ee40a30fe570a52d11062ccaf2972012e225cb26e2a0416438b79ddc7728f0f92c93767159e88dd8aba6e2c975f4f0fe82897e8e74a9244fb4a1956e7cf52b8b4d977b3de77d2179c6d7ce05b39ac29419baf68c05d179fc6e260797c867a2a9aa08911deaf9cc5436397fc966014b4f486601f68f43c3eb7dfe611c10291d8e7909adf1ce098c73f72e6e7778a087f5ba8b2129859af9534a8c8073854aed7648977becbd5b0d7f0cbcadcc98f849556c3960856c3d4805ea03001a6bb47db2811ab7f63e4314b5f30baf18a50d7092b660b0179cda4669a168c21fe782a75a3f8a12005c430eece7b0578f5e5fa03cf487389b1af757d7b0fa9535e83e76adae7352199bce022c9874ce34fdcb1a15216e2add176611dd73a42743bd4cb020c832d3cadf6a49586450ca785577dab9c5b28afc76aa4de9ba5d3dd632564324f0e171dbc37af1411a3907639af4fdb88c0a4d5740f3d510174532a24f7d03a0ab88e307a3e8a9757975f0fb7446e4a2868fcea7a08cdd4cc21dd6b719ab08b652f802624644f1bb78d058b8586e139c050af86bb80b69349fe69d3a9a6e5e630df68775ba6c16c69b4b14b2ca99d0404712d4c85d351b47c4b8392e1c34f72c96b8505ed43b1db6c3e67c68cca9ae778e63c76f37f7d9b43e71bb35c532f4310cb362eccd46ecf2b2f275249b369400dab73b6b92cc27dc99888bfa864e4d8c22bee9cde7b96004da58f4bc8113a7a3128b80da593b776edf6b0b1bb963fd9eba6cc550c6bf9a5e09c1627068b57ed155099fcd38c967190628292a7308c32ec6c6987533e20b21bc8910fc4f2cbf1d02e363173eadb14665c8f6c607f700df8a14d580e10803ab2a7aa0f261fa8f34a2e7590203c968c42f4c175fb928f182de4e115d02fa6f02d864fdab34cac14468222aefe69482dd3724de11638f28a41108018699207979cb80c44745510c385cc208ecc5ec23b80f9d923f043189570983011dc819c4f5f0bd842713c16de90d4bbc902621c6510440e091338678fc94fbd0ddb7dd88de5b1fa377c204b3f87172656207d0b834a7b5ddedc786f9decce2c2407d6729f3f94f00c6f382d916cf493a7dbf365315bdaacc2c0f4b17528c4914749ea0c25582decf3623758a8aa23d840c1138b81e0230eaea73bd70a0d8a01bc536d56d5c31df3e2dce73ac7709cca98410bcec6fd0f604565c4553edf6c8631f53b062f13913055fc5fdfadba0a8a8a9bed8eb30147b69cc4c1c3a37d00155badf471bca459aca37b716666921c7c805d1e7261a589528c11518b81efe318ab0b57550a7a1fc17db8a6196f96cadf4472366479d988ff011ce6673e082ae5a19b68773a71d147fa2d2b4a368ac65ad0a814bde8149ceb12842fa267b8bf6a36f7f51eb54200e1b2951ba5d29edc7af743c4771c44d3b23ab9ba0f68b572304333dc0235669e08d895b8678104ec9b3a3c0c6f9f4bafb2010fcf28d871f7224c92f98ddd8c8abd5939d58706acd603f889591c3089f52c6c343f80a25257b70112715a17461c5bbc91b5d253b0f67bb334acf3d89ecd125a7b2b48e537d4af50912db669cae32ced28f6de12fb45367abf10c341e2d139f034522ade1092255a267025cc061f16276b23bed137c4259979e60efb8d47583b0273799c422502e9d9648e676718383df86899b3334fb45e520a1db0ad905a58b1d5d9370e5915c0d5a80d3efd40c31f83b38e70b9ba5498c1a9ef8e215785acc86f7c0b8b44fadbd375bd5bcd1935ed7cb28d59fcd21b498fdec7e2b77583c33fbbc0921f2082811fbc773ae6896917b7a31df6779747c74ca268b59a8468da69db1c5fe4a46480e57600dff5008cd92fbfa569ef9be951b947434d06e7741ecc4f4c0cfff17599d594c7e4fc7a4522a956f5256d16e1b4e9be017b505843d348cfce719a7729e26fab000330b6f492379da4f9ddf84573f7ff6aa5fb837e6877136437e25539bd8c989ba74f3e5da54c29cd3417700d57f2f13ec5d21e2a76256d20a55cb344da1f8437fba90270d21cd7e7b519d2524398a3df57943a5f563caafb8fa48bbad18f07ba60bfbfd5b6ba56b8a5f98466545ad7f0da6235e045f0fad61260fe099e86717cee1e43e7364ade447a50dd504d2f3f10850ff0c66a56c80e2dba4b8afcc02b8f7d57b5b6c0b0dfbbfffcd4fef4783613aa487cb631d200b896cdf86f009cda4a23ead056585ebf1b8956ebb165386a0038dafb1ff3d3009a855158235ba8ce20f2facc54159ab82b1635e03f73b976caba30b7ce49bb5950da3fdb61c2013b16fb9e902b05136d982ae1e4054a1358acfa956cdca4e2c72b2695586d013870d4f3949cd80ede6ad87a09d9a7da908804529da26663b010f43ab3a3a093e70048513e967669847e121a35f92d8b11a3045dd6c6ed0513996cb1a6da0c97b843298d0c9da2e6f9e50d5710ce672a07a62bf66fa2442ca01af480e9bd55c70e7b29a74ae3e7cc9ce63d5bd488fac512a82ac21dc5de502ff5366a27563dc99eb3a441cd68a45399b4e1844d510d4f83d88fb7fab1284729aee5cc19f165a4db69ae6feebe89d2324445292797fb3a4f7849512919447b744687ffe188248c44f5b7530c2798513094982b049c81afd12d37f7977041009ebce7da9f3db4f381cdb01b4a57fb16678004ccf0989a013b22ebd208daa07fc05ae2157d73facc1f54fe7ca2ea4e9ae4bdd28e239039a525a9968418985948f15a027744602e7ef51782cc3ff30dd913f547de0281a940419809f2e3952b51c09d821e3344147d495e7a742b4e939bd8374036a4e2c0318c5eb675f8bb7c8a8014b641c2a6b23455f1f2e6ec1305b65bdd90e072169b685b80025dfcbea4a2f235c6a34e2bba1788af8ee1876a700320840cfc214d10b5d7e817bdaa3af418d1aae5ca396330f7c8fafff071191f7ecacb77deeb088cc361d22103c6808fd2251c81cf8a603db92e14b6526366cb1612109fd6e8ce7e098c68748841f4ebc75d26f8d3ac0af6364f64b66ee6653b305f34fe25e82881e3887dd7a931058c3026e7e15447c410827b3e047180c49790754fce91da6617b68ec11b91a90b4bf0dc8b1ea069ca882a71805b8ecb542c18ce35e1f1bb82ddd866dd42a7cadd58b57af88aba00cdd135f6b5ce3ab9d0d919099d93414ece8141bf26a9c607a0939f8a9a9483ce93512062b94ce04fe45c4300e5820945dad0e8f1f25ed34dd7a70ba3c973dfae6bbe6b5979e560cb4183c931c1ca5183429017fbe9b70186445746bbab1fc50216269f754491255b66757476ff5c5b20a2d9e0ffdf87e9b9572648e40abff446664bb9bc814740e9e0e710d308c1851a47a7aef5d4820986f21815e7c88225f245ffc78417a0cb9b75286477d48a62e48ae05b9b792c589852a25a23c852a313c852ea796173f6441a64394a747bd77fab195a7f7531031fc169753783afde924fee9744a39613c91a326b3856f6a69f92d05f32c3e8451a15e5c54e3c3a8502faa914c5d5c542d8f8aa922de9285cba3503d50ef820a4fa1680ac337a1fea442fde934fef72d2e220964fa303cd2a47cf91732553b2ba8262ea877115bd29104d3f035ea747a51959ac178badfe22755aa762da9198ca8b0b3529efe6432ad534adf508e34f172c0b2b3e2cd34183f80de7710f848ef721ab88d7f3fed9450fa27e13a6dfb1e1cd91d75479a266aaa54b7719c1167c475a2a6eabacef38cbc4fd454dff77d1f6864646464d4a58c4a4625a39251c9a86454322a19954cdae9b7efa19129e8488d3372c7f07a0b7372b49c6dcbe1382ea7ebbacef33ccfcb19cb9b939484054b8a06e17803262e2da5f14f2a32f5665e7229b9b838470515a0830ea2fe14a24c54a0a888a2445111862614ea5dfe8422534c1c991d99a14ce38ba40945ea5b868ff26084648a891125bee9736ca8f04d21ca84528561380b5133d30c85323dea439409850adff42813ea439483a0a79c308acf815450811ac799e8d2f22c5422ea5d5ac8f434a6e32c27c5c428fe894cbd5929cec6198a0a2a4ee36c361e99cd50214a2b51b31cd48be2a354292666e12c4c31319badb33d3b322bfd1d9c3151927a41138ca64842f566ac228e35e39e06da87dad5b46ddb388ee3baaeeb3acff33ccffb58dd872d4949a046a66000b2ba0f4bda2d53d06d9c7b0eed07476e9aea8ea1fcaa1086ae89b7f45fa8fddd464ee3c01e5bf97d0ff7e165c97b0ef5a7878008e307d49f9e07459e78c40ee943f0fbd20fd347765e22532d18bd0e24bfef589d12caed89e0927cdac684b04a67e57225cef41cc875396637bd20c77124c775473857683849385942274229aa108a51d9ad60d47e056377d449d12185556ee7054f49ea15951cab88e3384ebb57236fc769efb134d6925ba32929bbe78a4a71a6c974291578b34e05e34c8957a4d97020934e04e1f69d95ee1e992e1982d309751ac1987658c2a3f0a8c3121e75589c8447479a8df836489a8d979e152116cde6949474c589c4b942c449c2c982931442114e09ab4cd16cb8efaa6836dfdf5217564985a0d4954ad06199a26456e464bc1d17baa4ee0a25b3229c2c52948453ac68366938a5dc7e1642111e85527458c2239c2c385760c1c9027e2a139996c81479c5d9b7892308b7f766f7c68c23084230458231f59a784d361be2932dc6f620283e69921281f8447c222a21426142b3f19ad06ca46836e1942950802c25acd96da2c96cffa9442762e744b3d16ea9139d6836e905c1fd52574a9960fcde45118c17fcedbd243c279e0934cf7be241612db9354f20c10ab18966937a4d4423cd26159b944d9278a2d96c2f26213ed16cfc09244c29b7179548bd26e213d6129612d09b782952ac124611862edefb7d49becfa1e1245db13b1a47323d0df18ac049c2c992c36bf9eebd97f33cef7e9f8b9454084620f03bf05fb3b924e875583a2bac253a2e6827958ba80aef293c729aedc3534e4aca51b9755e11e57637bfd14a2f3cf21b1046116337054f3b2b25f8dd156497a4c96c0fbee9419acdc5a8a43a4a2a2f12f2eb521729a50b60138b52201881beef4aaf2b47cde63fc6f35e84712e9c69321e498437632da1a4f44a7763cdbeed5375cf9a7d5df77dd76daa52e97edf072a612d61157daaee722293924b31293def725c477a4c9cc6fb1c5a4a07a3f7a9c76423bbf717262526629884a0c9132750923081124d70b228d18431158bc42a289c249c2b709270b2e498a150384938597092bccc31c349c2c9e29b8dd3bda4f7625472555e8c7092bc57bd3cd962702faa5ea0949c58058a31f59a88559460a20927a038a249139c2c1d962e6956c4248649e90200b73004b3929be288f245e94f4881a50caf9802c10874bf2b4b23eebd293be89af09cd06c3e294aee53548ca96854724838220a2965d88948b8d38fad14939046b652cbd1c02f81647ab5d310f0964af7963ec706aac4d20d35d5a7ea3e53f761193e6bc65ac2ea7e84e5167e228b59c9a52c66da29c7f3fe92a1aaf3d3cc698030a6de6956722f4aa155517256941c174aeeaf2839139429c8bd07050909cabc9763238d6c0e32199b941cf7c21484d1058542a13e170f857271214b8fe2bcee847a94f89c8b8bcbb394b8a0bcbc2f2e2ef7a25e145146b6979677974fc134bda86ffccf45fc96f17226d6925b83622d612de19698e0092630195beeb7b4dc6f19719270ae18c116af6b19c796716c19c796716cf91c5b4b8b0bf7d2a2a4e5a5fb171630eff2f28d228c6f315e5c3e6e6cc1c9829305274b17c3b5689f164edbb66da8ae7b8db504a5695743b5681a0b253e6a0725d2c8a6a1b6d3492592ac25ac25e56c8a1448e50da53f210513a66ddbb61615101165a97b17c9ae3b757f123d9c2c49909c4e8f7345e94f2d62498593247ecbe9249e5aa474df827a70b3e1f2a896ed5d542d2d248adcba548b2fa66a77849305270b4e960e09a57d4ca07e9b69df1dc1ed3dcd73f00341ffcff3bc6ff38e70effb3ebffe1eb935edf33ceff49a0a4611e3296df9440ef5df8b2adf627ce4e944a65f0ba97647a8d3b7c530919bc3c91233963810054c982d01cb12a62cc1a89c51fa134b50026ea077fdbbdfe7dae6dae6dae6dae67fbda471da73475cd3fbbbc904a388d1f34013a97a3310fc62c6af88104a5992320337d02465a6a134328561447714fe063e4ad5439512f525904c4b6a77d4d242a6257223d3cda544b53b2a3f9389e37a98d2e164c1c9829325841272de0794c0052540a184264a1b4a7f42094828414949a3f4278eaa9461cbc7697714c1fbdaa6759a46a6b724fa47c230c29b7dbf79b33255bd99c944829a8c93aa372bbf2fbdf75b89dc584b584bb6d9175aee0e8c4293e8cd3c255e9177c4e7df8b2ad37712bd7bbfbff7433d1165275e259f912d8726a2fe3e6b49e93512887bd48752bd99773f0cb5244888e04acf52d27d89db01122550e829577aaf71af91db115e490c45d6928ba34bb5322c358f2bb7ae2351da879ae79130c2bfffb1662c251787efa028dd9aefa0483cf568192296dcb7dceffe2b99c8eebfcb3d6bb603a350d3b410f53d4bc9277a4c349b93aaeb3a2fe5389235d364baf71ec0437480f700208ee486dc97221c506ecf5a727178decc9b79dd18caf4a58872a399b196b096dc9aee4bcf9a6936be83a212f43ccf632d39dd910b49a558b2780fca1347529e382a7a2209474f2401ca1349b002b6402abb0f3b2b9d151d968e0baa7b7ad47df0f42daaed4715f7db188257849a0aa8f444941ec8814abe105542e164f9d2ad6dad7c7916e447a65aa9a15064ba39efe553eff3bc171248fbd27bff814990dc2bbebf2ee5889727a2fcb49b72e5e9bdfb9e9213f8723a69f7f4e066a3e5bfd7fed34820ef5ffefbf4347e0f55ca8f4c4fff7d2f9feaaa420d15a40492298cd2df3f7dff3d4e12ce1517c7f72039fbbe2fdd5aea5266e3a9078b976f390d51a5b7f4be3fb1f8b48729a74de5dbf72c4e8f3af5f01e0824d37bf2529708a827ef7b16f77192424de59fa675a5f7dfcbe971aeb835df7ba71ee09bfea4c2494ab9b2eb92341b9710064c28bbbfef028a60e9c10b8aa7074b648a93e5e2f85c3ccffb8fe33699f630a5fc9e04f2c2bf5edac3947bfa712271b27447304a2ffef72e1128bb4f4318941df8dfe6cafb240c2346f5f4df873f8adf2292e9e64af03f317589c009fcef71b25c1ce2878f93a4d97819be77ca0967a5a92b4ba0caf33cae2c81244e965bf391600b24d3065383f0fec6714c9a745d77559ee779aefabeeffb521dd8521dd83629db142b589adc9bba25c8a39be2349bffb66d3398a0d436284640a246a9fd8d52e39cbc405bc29f308211caedbad65d29352a78090aa9262f84f0020b8b67e17d485cca1595a64fd2e148ac4f372ba56f47321a1170d8a248b72a4e9ae4f8747382f3e9f6a4874f3728463b7cba49c171846405cb8d4fb5241b9f6e339b4fb7221dde6b7caa41319292c3a7da14199f6a556a3ed5569fde249a4fb5d9cca75a91c6e486cd05f0e99552c3a7774a15027c7a8f2ed2b56234233fbd45aa4f2f93fff43609c0a7d7090d7fa1cce0489ede283d95714947f7be7bd36f493a40165992e998030e6ea33de764cb41e2903d903c90e9ef40e2208f36f0864cc1b100643ac6dc20d3181b64fa36a40e64c7649b02d6205370a441a663cc0c328df91cc8f4656490a94c0dc9045c912938d290e9183343a6318f0399becc0d642a13839c6d36b4269ed16643932143a632646c2053992700997ecc00c834a60652002448922938aac8748c79328df90090e9d3400280f4b06c36b6a2efc96643c60c642a4346e65364fa312fc83426864cc7d205098e2e643ac68c641a437e4f58904f6420591c93cd8692255a1798d79e3b426bf11390e1b5e722e0386448e28afc667c925404a362f1a3112258bc7f8b0f4ba79c162c58b02055a3aaf316557c4fb81957c2a8acc0e2b2952c54ee342d48d7645c545e6a650b5515e57d1954ed1ecb9a747382c3a8be45e5c2824cc10bf97d287f27595c91b6a5dcbbe52d6f6c3638cec90d274e3827475b0ced41701c6362fe65647420388e3131ff4ca66c31b407c1718c89f99791f18cb618dacf9acc9acc9acc9acc9a80638c6624c3c3f2c9902123f31f13338e1e9622101cc798987f1999ef8906464f39272577546a973bfa3424ad63526a1a134f7b8265eba67458ae1137e38c3ca352eb3ca3adf429da49a54a69604cc727272f1d4b2594479afecbb185aacda4aa91822008aac0f2fbc2fd9072d8be184e18454fe4a61041f07ed774c30d051cc7711cc71521952928459c4204b96ddbb66d1b398ee33c4eca6643143991133991e3c42944502c8193f22546c6bd062983c80b268c373419edef3674c44979c9b4f45e0ac65b76338426f0075368a5dc54e98b93fbe9cb94710629399c9439361b9e14cf9322e55eee4a605cb9a212e8bec8bd2765b3f1f2048462546a2f52a4a49e958dc5912f4c18b74f5fa6949af602967e90063bb8fbc9fd03c2d5e1c2c7db7277197cbc4edc7da36a4c7fcfc63de365b97b0b1fef8cbbbff8788386f2b9a2afe22016965b1e74bb3a59588d998362bc46657ecdeaeff0d9f5a72a635ffa63b87bc95d3bdd3dc6c7cbe976b5613e9ebb5fd7bc65db36ff8affcc6b2bdbb9fd954537de6b86653506437fa3b8ddffb3a27b6358cbfa3cd63c779fc1471f7277171f1d09771762559f3607b14fb45113c7b0a9db9599835a42b8ad315b5d63b2aa8a6b885adcba50d5ddbb8d73770d47c9116420831e251752948841831f20d19902073c3e43a8410551cd915149e146151642e00a0c4dac10a246c004a51b640c11e45085c50b09acb698de51034e5a0481128046f8a48044e85266b3d2b78f0a4e84e066c3f41ee38997250e4184f61e39d3fe86e183e157e2bea429e13efcab1a02fcab81e22701554b7058db97544a8a2e6bfb2538ac1fb8179f67fbcb32995e14bf64faa2cb2a2909df04824ac2214a7f41728825784a7f7f08d36ba308e37b9c1268fb141069ef894a4c5ff4acd27b2a25e22f7956e9af48ceb6bf7f49254310b17d899c6d4a963c4bfced848071369bcd58db8baa2188d8de44ceb6d7ee89677b4d2ca98a2eabf4a0aae8b2c037a9942cc16195bee8b2ae0ac60fdc8b8f80edc3f7544ac02092b0c4fb43945e3584c9e4a99c55527d7f593020301af131941f09940222ed5340c43df71b8c1fb8d79e87d3d1de53e96cff5d92ebc920843143828408edb727e23ef7489640a2a4e8591d4fe9bb27027cd33b9225cfea482444786f228b9eb57d892402248b9e7589f0ee77e4f6fe4478770d5362488de12e59c208ce124e49d1fd2531e38521811d4270b3a13debaa8a963082536e459c92228e5cc208ce92258ce094409b9f8cb894720b224c650981bae734324d529249b66b80d08d6c9d0a2a71401125102fb5d7b82b038ee3b8bff702dde77e53a5e07f1f8648d2fb250f4990b6b747d26d1d92d20492e9f71bc8a9803a12a8fb2465aa3d08049246b6927b8edbb68ddb388ee3b6ad454b351484aefda6a98c845e7ae7de2383b4f73c4e6524a604d2482f4da4112465e96f894cb94f41d20892f22381b607f2f79652d39ad8a094f7431074f7387f4e5bc22adabc238ef09a2431a61e94fb29279b0d6fa6c5f0d734d6acdc349577840845025250524552d93d4e12ce15d7ddf3489c2ca0e324e16429258157949bd755a1c978b30f8cda5fef8eb6ce63c2813d4cd96cdc77cd4918468cdd7755745294ce31e1062a6a68325accac0c3b31c7b6dd80c2784bb147b9d15085e3388e4baa526a7b074d46fb985a04109c024e01a7805364ca1b3bf81561382b73686028b51aaa800104bbefbad2a735985431492fae30a63554f15e862633138698a4307c610c6128b5f6965f4c5252a9fd7df942a97d5743952a1b594315b0862aa5f620088220784315f1891300b895a4986653224da6fb7ac2e856d4fbd704d7c94de296be590e2d69e8c68d1b2fb53b721bdf83ef6ec30341174e4a910a2a289b30be48711a2da6c9a9c9685718d398265c4f1305dc920aa5f649394a45a41729dfcc696edcb871e3c68d1b00c0c26d250a3029bfedfeabd438f051641a2bb5231728b5234c7c54316e9fde243499214de626d1030910cda9184516a712adfc75726f8812916832420881124293c15296a22af52baca47c00b92521b618f74bdf95ba2f956a50de2f9534ada4912900dfbf5053c1f8a1bd97df6bda89c756d6a0c95c5667054b795f88a2f2fe6956defb31a941fa31493561bc1f13a7b9afc9682726261254610595842a5aee15abbc3c5832c9e00293a3c52da96450b9802dc616f7b680690103a585f8bdcc664c5e9c5c3125c578a1a0c413a168321b2956d1645abc6f366478df6cb878292823cc6f1f8a50361b97e59ee74c68329b51777d436955349b3405cb162f832a1dcb16efa24a63ca160faad22f5b90a967546e0fa36ab9d213a168369aa6bd68b4dd2946ed7ea24c0b1898d22907fcdbc28369a14a4718923b959c264c753499ed15a004b21bc29863eb896936daf6e79d6d2d3e85295ba882745be8d7fb928b4fc2ba31efe27962de858b8fb91ac9f3e2633e864412f32f5c7c8c8b2a5c1c693652b840d26c60fce655f1327b51a2d914410961a8bc6fd1e261546912af458b169f638351f9d064b61650ca4d7b51f252d482040243fbd4bf78172a181a09e307171ff3455e7cea63de454af54215a3e2dec5d16623e65b5a5a7e8b5179465b8ced5da81ce9a5e865e682d1f427138b17cfc573f1de05a6c5c3b4e8baee28d582f1bef77d2d3ec7d602e63f98f061542209a65fbafc8b2a8d295dc8b43b1a5b5e66e9cb11a2aa4df1adfb7bfaa1951e10cd86b3622e115c12982d260baaeb292674e4104b987e88ee51ef9143787fe2f1fe74f52d4dcf69362e4c6a096352da6da6cd6422b9effb3813c77d2a24275670dcdb786d3138948fa70f37adf426d2b7181bc903f3a624ac30a4ce85cfc53c07c5cb7b471ed2a9288c51897f3f87e61d852f9f9e8abc234f0a0f2924d5a3938e92d29b3e4c3161047594c0bc0b18728825784c0ff343944818db97bec5b770410e6122616c248c1f5a7ce98bb4208bc0bce94d0ff330a4086564c1e2bd231a8c9630ba3c0d46459b473a997648e511d761291243195469925286df4e39327c286a9ee71da55a306e20286eaa500c1f0c5f930969404a81766150ffb148bda3a3544415b5708f525181646b321ce759f192349b23cd46b3517a8f5152f188624c69e84e391e0d489d88d4a5341645f774d35b549e8a58149d8a6808ff87567e1fb228d24e22928824562162998948482252c95d6d2c39a7d14e25a7d154de91278566b33d926705969726c3a99a0cf7dc555232da627025524c69614cef518a8a313d158164e71df9a66ddaa66d3420793420899f764928d318a4f68a504aee43902cc2989e8a4e454db83f3d014f4994dca9a8e47e53899b7669400a953096dec4fd1152e97d1ad364b847a2c9702f42d96278474418531a908e4aee69a0a10a1ab068361af70198690f6e342095dc832aef4e41bc4f359204cbcf35e7501f784f391bc7a522f7aa66c3595c0d485a92ee9b01059705c38811697b2b563cf566be6de0066ee0066a1e4a75528561e8daa987f72edfa9bcef8a5c0655088230ef9b0d1620887482f143cb9fbe480b29c31741892d546992b2c57fa79c161f8657d3b4ef384e0cc5e754690f462d4a2f030c8b170e49a73bda621025159dbc990bc6fbde7f62681a552ddd9fba2351657af035cf4d5b890a610fc6747c2dd584b14c1961fc11867b19ddef6022614a998f4c6f94369ba7e25c0ba9a3310dab74553aa4d0289472c9b4439a727ffb242cef6ecfc3fd462259e23e92ed39d2379ef0b9e74824dc876412d6970ac3b8bdcb1317265c94708112568942b3f1c2ae2b5d93c974946ac1e8dddb8311184a1146a1c984475d87d4b910a125b95cd781d06bf0299be653c454138478f9f00184db524818533195842b8c9d4ad49294a92b8cdc839f84652a912089e44b592155658c99a1ca98be102f1f407c709b6fdc7bf9dc6bff5c0def019af657b82b57b82b57ae7057ae5cb9c25db972e5ca15eeca952b57ae5ce1ae5cb972e5ca952bdc872da92be015f00a7805bc025e01af805740eec3d2e5aede48ffae90b292b292b292b292b292b292b292b292b292b292b292b26285155650410a55484de1ba6b4545b31452672585c49ab1966cded66da0f6dba66ddab66ddba6793208356d4bbaa2a67d9af67d5e769b1776dbb66ddbb6e598699aa66ddabf6006a631385970b274370627cb2954a5dce8f29d4b0b87e2ae0bc9715cc7054d66fb2ec97b9369f332ed210727cbadd1baaebb9f38c59863166ede154fbba29451fa134780a27c0217ba2334710a622aefbda721ac255d8ef6b19688ac196a23d35b9e7c30952792b5e4d4a3f39868329b878426b3bdd724c7166acf9d72584bb67bef6f292946569126a3fd3ddddfc4db59b171a8f70f97f8c81c33134a75527d25b8fd5752dd8f7426e056444b0fad245945dafbb3604c0300450c80dfb856a69618ef73a5d7a0dd2aa494dde9e34aa95418460e8a66d344c8c47f264d14bba3540bc6702c4befbd8693a5a44a7b30f2a0283f55a7a990bc1b4a295d00dc164574924a5d31a6deec08cdc6bfe8081109502c536fc66489d1b950a6de11de6c2b72279a91a3400343e8a618f7042327a35d6d7b2da61cbf0e2c5933d692920abcefa9b607841efadd92941f48aa522d18bbd7726cdb739a8c15655745e99ba60496727b27a5aaf427907045195ef1c31299c36b494a29f17dcb461ab9630855a9168ca6d778941e34a9ba2f7d2a8ff4108b5605cfbbda0c4f8a8e60d2594142065f7002254cc13082359b12fa66a3a491308ce84c1c67e5c9a7796298f284a2279c90f404921439664270e124ace26274aba4619530742b6195b04a67650a2e469d15172317a3208c61151723228cdc7761950e8b8bd1e55217230f89f27e8a0863da6109ab38e16840850a152a54a850a142850a152a54a850a142850a152a54a850a142850a152a54a850a142850a152ab317e75277e6a228ce9ce6def672cf7a99fdf610f860fc00fef63ca095cdcabd1ad743d33670e3c61e5cc971dc87ea3a2e0863fa32e331e3542fb322f1e5ce949895daa78a30a62928a2b1b67b7f8b998120744d0c2d5e83cf5229183d272628b5279b125d4ec7a9b8188c5b0a0ca3a6bdf6da6b0f05091e5c71764f3a4a2000810ec9c4c35d86fb4e099ae907f718dc7726ce643291dd514aca785ffbeda65e11a4372b4d45789177c4f6a95704a895a0894c4ba407925ed19663d35efb254e38e18413dbd6a409c7714e745dd739e19d524e38e184134ea49c70424b39e184134e38e144a94bba3548e5207583940d52384835f179a06f31b66f23491f448912254a942851a2448912254a942851a2448912254a942851a2448912254a942851a2448912258ad7f0a16a1c4f2aa016973fa95e5e24c50f5fc8b4b30203238a24e9df79dd763a9d36d3e965383d0bcde67462f1a798074f4164f83f3de7e24f64da821cc34785a12afc934a0cc37f717a195a1ef5a1aae55d54e991269e0fe29f4818e19f7e8411fe4b48f218c917323dd2a444b97c8ecde5135ffe44a6a2da596979182e248f3b86b2850c4f61cb8bcf9d80c07ccb731f92308cf042558bcb879f180ab92d64f844cf159badbde6bda2ad1c84b39acd951562e6d80eaaf39a7b0cf10099f9a5236b9a96b569c2fdc746c7b70ce63a37f7eb9c4d8cfba94392aab2666bd4410a77d72a8a773e5b39fb987cd4618a3bebb3ea00c5ddb3b9b26b0b23a1c376774d948174d66d5ef38f02bccd402d2bd4f2f16175c89137d641c6b50ea2ebd78a8e35b8e03ed690c289d4580cd780026f7ce29d6135983882aa49b87bae7184afacbb873ed61872d7f258a306d6644db6a571c95e34ae1a3dd610ddfd6c6d56d5230d2fd09062cb622bfa27665dee86473862040425d347194c68fcb0325716d56bde43f959214e04948b842b3cf071460e275af15e739b7d7cd0f3f1e720fc3a735e67885a169661cfee15c5ac9a5953a3af2dfbec5ab1e9ee43b83b1277e78818a03dc081f6c68f373e5f13612115ff9935fad2f8f5f96457dc93652a2e727393cf1fb301ee7e84c8dd7170f71be4c8818bc98058567523dc9dc7c7158f935dd1765fa05146005cb72ba276afb92686613552ce160bad42ec8e33ebb566e544b73447ce4d809300a724c69d9b3927c4a123cd0c942937c845c0998d3c8e7365cddcc6fe645f3bc32e07b4ed26619cd9c1fd64cd1616ae20c51426709d196d778d192b1934ae9326a3edce81bb17f111871ddc756bb3924173aeb8150af2330bb16dcbba4e14ebe4cc6365d195d631d4c43118ea83aa3118ce42bf65ae9fe9e959d93fb3c6ae8fc164aec7bff34f66cf1976e757df134337bb9fd52a7e9d513516c3df6634bfb4ecb3d067734575deac90efe9e9791c3170781c6d4683e4cce36f781c3e679fd6caa25ae679f818ba5b3bab3862c4d02d63aad86cf1f0bfb3dab23cbea6e673cbae3fe8ccd6b2b6a7a7e7579f5ffb67454f996aaee813b97e26460cdd8f0ac9fc1154d310a92d5651d82a6b69f6cab63e2a86b57ac519f6fbc7af5499a96213abb29fbde6958da1fbf1796215bff076b1cfeeefc9d9a7a5e215f5e95163e85665b0d7e86bf4c5f33d3ddfa29a655dcfee2763e87e15cb768b06919fb72c0663cd67f7eb1e15af6c4f9b5135b6515506fb9c7dbe7cdd6634ef681e221f74b3424602686145e4faecfa159b7bc5688b83f48ae2bdb23ace9d3567af7967af79e76cb1500c888d612dcbe3c4ac13b35414f3d03a9ba66cc7dd5f3e120000a552c974e6bd5158fe047c7ef1944a2593bbc37c1c00084aa59269b5b3aa964a25538c1a1abdd71c946bb4c99e342b8a61352cba5a69933d6b6a6e746cef97cc0274021ec66e2dd3512a954c3c2b1d4487102bfa3f2b7ae6d7e8ef6ff36be75710bfcab9f551511c847f0fe515bfceaa4bc69ecf6ab5fc554fcfd9b2666ebfcd41f9f5fbb0dfd3f3180dfa339f4fa4aa58b66218517eed57d1bdca3e86612bfa4478ebd7f9e794b9fe8730eccf7ce66c0ee5dff8b5b589c2f04a746a956df7ca9a2f33f55afe6acd41e8cfe35fd1bd66fdd9f52c8a85f06bf4738b7e96b5fb35fa2acbba5ea3affdeb0faa9f68a32d8bb6319ad70ffa7afce79a755eb3c6e6afe89fafd1c79157f637fe15fdc9aed7ebb7ac4f9665d90fbfd2b0bcd1d781860e3ad8fc8a5ffbdbbc716e7f9e5d5f886d7f5e67984c6bfc39885ff5f47c0fd0901efcd9f55bcb609f5b9d3736d15ff36bff8c8a6546f2e716bfce267aaee8b342fbc7afd8df1aab5bb7a8cfb9e29ff95951186b66d7fbb0dfe6f3695676fdfce2f91590c9aaae9a6f73ecf3ca7e763d91bab2bf62f377def827b73f3ff87fa5cd47ff5cf30ecb03338538316b289feb8f13b3360a4381b439684810e343e1fe33b167f7f3d044b029806df39127727d7e76ff8ec1f2de98c746639bf1376e82e810824627880e21fe6745b5ce26dbea68390070dfaa04a009f7d5cf8ac6626c7b3e91bad7aca2fa57bfa2fff908fb373d84fc1042a40c62cb86c460a610eb0fad7f7869eca3cdabd010a2ecc310bde6a01cadeafcd218c86481681f3f3451f6610f6521accffa43e316ebe08d77e8336f8d81e8fc8375fc0ad794e45be8dc1df59106d49a814cec5ad19789613479e3add915b77b2827b7b28dd71f744755b10ead737b9e656ec96e6e4cb4cd6bfe41f58ab67b28e7cc2b100050dd6ff8088099369f6810dbc6f24be84f9ccf273a716e55fc2d58de9a488f330cb9b71926d32b6bfecf8a9ab233b7bfa27fe6150813fd61816c60061ee30c337ff312f023dc03308e7c28d3e056866134e1ee27aae2a0f5874ab575fe41cf5771d68f89c8ca15f5d1e1ee2deefe2385e3ee44ae3fb3eef9417d565647cf7fd6326c66d77f7eedf7617fb738f61a7d9a9abd6618118eb570ea74f76f599f67d78740124fd0edca85aa3431983954a36bf4ceaa2acb3223b19696a93298cca479110577a7c1c7174f8c2f96bc48e2ded3f39f7f70cb6efdfb75d0f7f43ccd3ae45731f2dfb05f3f28ec867f99e84b48e675fb83c2746eb16ae6f3df02465870e28127b439886d65130d6af3caa2e79a635aa6636c7bc2643ab7a7cabe746e5f9b089bf96c6d136db7ce6d4bc5b2130d42cf156d0d61582b0633875a79e3332673f7216d46db2c33222488aa62736b16d6dab8d5a23223b97577202b0ad46a0d61583e82ea16ac95d1186ae6b6cde79a75cba2442d464dd427afd9a562183a84617bc5423908fd697d30c04661a8dad239fbb8fb8f9d814c74ebd61086b9bb8f136d11b55966c4dd7bf0c8617d56b4d5b6ec8ab65614d6b22986b5f2c6ad9961786dc1dc7d4786e1d5dd75b83babcc81b3a25bf700d4b2423baf6d96edfcd3e2dde655886d3390c9aaee0e63f3d0da68eaee3ba470dc14c0dd3546d5d24463ac89c6589e9d77f2c906d139f3d87927ab2e9c372b445d8100325920f9076b9d83ec9655b16e7310ba02a1651bef1d55c53adbccb1a19cb595b168b82c510177592205eeb24413c6e02e4bd0b8cb123ceeb2c4eaee2fdc5532a4dcfdc7e0eeee8e00773f808f2f45ee2e845b1aab1939ac64d0ac426ceb7a69fc7a39f7bf34f6e12e0047f9f8e82285ce26aaead6cf8ab6355966249f2d3307b9bb8dbbeb50c3dd69f4b8fb0c77cfc1dd653871f71a775fb93b8dbbcfb83b0e2e0a185d7462b8bb0c6883bb13c0bd0617d1dd4917775789eefebec39f284caf4014c92eb435b54bb61383a158d531849e6b8ec5ca9d77daac9e2bf69177b44c8db5b29d7395f964222b8be610c9583f2b7aaaac8fbc31bad1f5c7cf8a5fe6500e9173cda6ce3b86b438ab3a7e5654b3aa8e33ef5836cd526799914c64cdb1216a5e57fcd2219485c460ad8f6d629eac5736a63311a236cb7ec86ae6c9416836339133efc8416dd699c8ce5a9741c8daadd71faa8a75cc088299428c3eb6807fb3753e7fcb585415eeeeee9e8f281ea06c30a292d83ff33730b6f5f999bf69b38f5e71bb5ba8edee42b83d5f1b1542216044f1605f423fb3b3d6b8954fddaedabc63f8c42d2273cd6d2b334f17382171f7bfd9f825f43368abb56253003e9e7cb8c902c16891f507cd6d0e62553e9e60dc757e565475a13ba48fa2137c068df38759eee87655a36f6e5a76cbcc3c945b3073a825beb8d9bfd9d5d4ed8a06660ed5c0cca11a1a39e01932ceb3c665c3746197ab460e26a691033e0b40d3e616cc1c5a5954b7396865cdd6cd0dcc1c7277d0c7d07477183e863efc6fce1505fa99bf59d11fb4653f07a11b0bd1e58ae2970e2d7473b35b361693e95686e1d6476bacea78991bc734baa3cd3af964cd7cba7b0cdc7dfb689a80bb0fc047530b77ff7c342971ddae6a6acc56d7e81c846e1486b6b11a9b1b3568641b3ad4d0c0363564c8c03eae9c69cca851c387266fdc6eed92e91abd652c90995f66cd467f4cd8dd6bf0d1c483afcebccd1cd3b9d5e87f8eb1a6fe15fd96dd660efac7efc3fe6bffac59dde1317350ab0584b3d0cd8debe6469bb9c899cf55c6b280700e42336940dcbde563c908dff8070dc259a6e2a0efc92b2ba4e7f1afecf7c49089e1566895dddcac2cba63437afeccaa8acdeb0285f2da0131c4aa8099191e7ae0e1630c9da8ccf6c9406e6ef6eb0725d20ed93e19c8ffffaa8006fcd089b6f2cf995b40266ef7bfcc8d615be0c10eba1828e1a1079fac760fe06187dfab8c351f875fb3dadaaf1fb46d9d2abb29b101e78a7845d8fa7c1e78f818326b565766ab83c8f81e02e0ce1581858fdee8f964d727da315876f71b7ceca470ff9b7cb2e667d5855f26ceed2afb33b779aff96f54bcb679afe8048cc8e1697cd4ac9f071e7ebf7ed0fc5ae59f331f71f704f8d8bdeb76954f76adc9b9ddae1abdb2e836b3aab2664d01700e3635b00d1b1b9bb300336ce8a0838d1b3a601a260d1a2e97cb358306c63af8cc982123fbd8a831b796b53507e0f9a1a3d42975fc0e3f74a27f736615c53e6dd61abbbb8a612d2b249f6c909f15ddb8cdb12126aa312b6f15fbc83074afb2b61573b56cdbe6645576b241f2cf89b6ac33af42437c58b6d5d127ce3e6e6e72cb9aadcd9e18486563ad98ebec61073317e9e1866c073317994143b64dc1faac3f708b75346e71ac1573e913671f3368c85616dda86663adbc5b3606435b7b28af2f8d7d9c386f9d59adadb3eba5b10fdc629d97c6afd689f34b0890c9aa2c22ac66226d76c1646b0bc86455578b08abb96d6d1ff88ae297d96af389f3ab1573edbc73fed879e7445b3bebad512044a5cee8465bf6b565d8dd61dcfd06ee1e848f9abb6e57a78aca5c325633563368589f15a5c92a36750d8d1937cc5c00d7598002ccb031cd9c030d8c35572ed96ba33f44edd6e84f6e6bd89f169b352a3659b5a6d5b29a33af3fa8d6f837bbfe6b639d1ece1c8ba1edd637308d8502270652d91813c624b6bb17b941dc97f1033cf0356b9d5beace993746db8d6399c80a44ded12c90ad653bce2cc49e6b8e07bae00199bbbb0700f063080759877090150cdd3afff99b15f2abadd1a19c63ecc772067a76cb58c958cd087af6cc302ce457e7fa33837756f65f8ddde4f00c11f22b2275657b7ab4ac87dd4fd4e213d53f2babe29e9ed733dac787cd276e338c3565ac5ff5f4c8d8030cf1df430f3be08d77fc8ccecff4b0c3cd8d8f9a777680a1af9d180ef23cfcff37e0bf657dfeb38ac660acc9ae3d3d3d392f805fbd0ffb2dbbb51a83e1a1fc7aa3abec35d1464f5466045d7fe420542d6339fbf0532407e71d47142852c200247adc1dff174162e6a5f1ce0017dc574ec880d1ca454506869490811b3b8880012872103a628089fbead49a0d1818805f00e9025114d1c40bbcdcfd7e17b8271326849818c0bd880f311c24e79687f51688a208208ea01a26cb3a0855712b963390aa4ae0010ca88013cdb6f9485671569fddaf378a9aa88f8f4cff996f101480420576c8a0c18019442b7e9d612d7e1dddaecc1c56335634686828f014c871576e87509fa7f94cf340267efdcd901d43788836aaa25b46013ff3fdc1fdeab8df21ee57c8ed81011c5481e2f671b07b65df0593b5e6e3d7f9a565b116bb57b475060571f7e137ad53455fe6cdc9fdf4710245ba5db532daee71023c196db709c3b9ddfa33da6ef70bc4876b004fdd1f3dc02924f072f7197fc3c7b0d93a5536864d1fb7876e573482223013c1f3c0c3b73273eb892103c4b634ba8aa11f498cd6cf68190220f91919a20cd42272b55a2bad756e7f563407a68809cbb663118fbb13d9c42201bccc8d613a3a678b4d8dee287778180180252458ad2cbaa29f5b167673d39380cf79e7573d3defc36a6c7ef9abbcb2333bfef36bb3eb8afee4cf42e78a7dd8c7dfb23e1bbfb44c28f6aa8a6efdb9c56dc642af1384888f5fc584fc4a9bafcf1c84f59f2bc6ac7abad82cf4a7c6421ab76a6e61ac7ee9185ef5bb30aae2ad316c2636d36a54a3b1cf3ef877d6e8ebb70dabceecf91b7d99689b61f915a3313fbf76dba2b087e58da267de0be8e949404f86b13b3de88c3e5b5668fdf12b55d3e8bc718cfdb5fc55fe67f7caa26d0ee257196d77cb6ead6dd8686caf16d0a351dd669d9e04ecacd967d79789f3fbb0d94543a4aeacb9b189eeacffe626a6ffe65799355fa3276b2e2067c7101d37372d55cd420958404f3e59f3a6270159e86768d81fd9eb1c84feac2cab5f97bf3ad58cfacce8c73f5e0f21629d79a32ef4c5b63a3f288b6579cacd0a39b32a3bf3cbdcd9ac1056c8b9027124094798f09e9e473970e40167d63737f8e6c608222b687a8c4440b8ab707a744bcdebce3f3dffb3a2aa7a73330e61c073087a76b7b2f3738c68bf5e7dda1c939d3f5aab9eadf3c66a1156a7cd41193d55964585f467a1bdd1b6c52bfa2f139b416ddec9419015aaac731bfbecd25ac62053b7ce4c84d53607b13d9b65db560ffef3b34b668495cefa7f6567702b2b573fabfcab35ebc71a056a59a12752d5d732bde6356bf6657e2b630ff02bdde2580cffc9b6b11b6ed85828b37ea5630fc3d9f9e74477fc2ac3d01d83cd395e809ee29e35deacba83011d5bfdcdab56f6cc2cddf337da869ed151a7901180bb60808fed11eeba55b1c9baab158840cbe5b0ca8146c57bcdb0956dd1a035974016826c8409ff1b22438810c979954a351a618daca3111e2340fcd59aa9f99b33b735baa5e2cdee9f698d46649e071e788869c1377ae6d890d80eb11c62628c96ccffcd1eca337ff3af5fd1af408b1582bd59bfb06ac17dd583b22a116e65b895e11587712db9b9cadafd335ba32adbe623ac4683da7cae2886e5364b7a92b5d853a65b673e829a78cd195033e09eaea8b64183460ea826d243b9855b196edb1ca0213944328cddc92b2b84e772c00451a0b89fd9080ed585b70c06435f63a1df28ea839e2cec739b855ec568bbdf077d198b06e536f63eecc7d8fd1bff99373a94856e055ce7adba50add9d813a9ab86b5f87310ebc29f5ffb473e5921ac1d196d79fe5cf1cd8d0b266b75dc22c63409ddae5a76e3366f34fdd959b7328c3ae1ee51f030a07f7bb83b912a2386b786a15bb72b53a3b01a53a3b0211f7fa2d059a6355af332f326da688b6335bf71fb59e3acc61ec799dbfcf383b63b07a17acd3ef97cfc6334fe18b9ff4d57e567a6cd1bf5a9726bc2198c3e3b2d6bde15cdccc5e1769703b08b84665f16585914c97842e125dbe36756d4e75799cfb342aed089f30b3ffbf3b20bbbddc65b88f347b30ac778e31deef706f71bc3fdcab85f1bdc2f01dcef00dc6f0dee5700ee9774bf2af7fbee3700ee9706f70b00f73b83fb85e17e53eef785fb8d71bf2edc6f0bf72b83fb8571bf2cdcef8bfb7571bfa3fb6d71bf28f77b72bfa2fb0d6fc9fd82eef773bf9ebb737e3b0feac11b9fada0d6cebac7dd870b0a5fe556f6b3a2ac0e1d2e27fed2788713e71ba3ab0677af71c0ed460c8315c53ff835508b612f73cdae5f65edcb58a2c7c1f33dfc10868d18070ce3ee3b13fd76f71bf4f4f4e8e899e989c2d8c384bbea7c225595f9f448c0ff89dafd2cdba335b6acf8dfe836ef0cc32d8efdcf8a0ab1aacafa9cbfe6ecd221c2d89ab96b15a37bcd322359ff9ab3d0ceaeefe164d136ebc06430744583ecf05be3acfe6ecd151d420b66648014e6ca9e2ea21d83e5955d5f0761808cfbe3f06c9b8facecaadbd5cebad5e6cd06d9e4b09241f34343b4512093058237ded166d4e78776b5f0c63bc605c8c0172084bbaf8832100edf73fec831221ea2ec43cfe717510622ca40b8a7b5b23d2dceaaf97ae79f97d9e2ac9fa8c52bead2e80bb1faf5ef58063273d0f93eec8f5f691bf28ac2dabc5dafaaa850d67add43992803e93f33facaaeff59d1df6b3ed1375118bbb266fe9f152552797ef5aba04c947d38d5986c8726ca3eb042842000bb6fb6bd69b5840cf9b14324a7482ba747496408cf4eab7573bd50640777185e1fbfc6af37f113b53928c7061f0f204547cd4119488ce5d1f907c76238bf6e786007f0e1ee30b6e571a238b60221c4aa6a59c401604621a8e0ee3aeb769585f05e6b1c434fddae749bd1b6c5ae207482600531630ce285d99af99b9ff91b8dfecc46cfdceac84098e7a5b10fa28daa78b76c90221b20ca40adfcd3ca62dec2cae19b15f694ed1021124490ed83be7676c04c2174c04c21748672702b2b712b2b7774943a9af5597ff0ec5071772140e1ee41707720b8c62dd61977b0f81d77a470bf23c0c10a23224fdc200aee7e5d94c65d0320602082bb6b2e9407360b50c8c01577bf4f48a0021d16369862c3dd2f0e06123084490440d084bbdf0954210031da6e0716b8bb062407270a13b061025e70f74d060b54b902079a34510477f73ca081a41bc54d961cee7eabe8c205905cc10128d87077ee033d5f075e4148a970f71b856b460f1440012353b87bcb257bb53451f661678bf10377bf3ef8180c6f73ab814814ee3ec58765dbd8e7d7deacaaff716e9f10c2dd7de01e43c6dd5f14e023111eddaed41a1a413566ab77840cf9c1a3430810066809510641db33b77276e87655f3d2f8b5ca6e6e7ed0f933ef98ac6d591d278a7574ce3cda1508bc775414f388c570cba33310e6c9bacd27d19951a213ef72084c88152dd9dadaaa0cfbe4d36c591fadf7b9661d4407ee3caa8a7500915b998a621ee79a77cecc23ab2ebc02915517fa63b3427610ed182ca7655fe68a3391cdae2bd6a14fcc62859c422c90cd0ad93948e671ae3296c766856820d61fe79af5cd0dcc1c025201204533343f5af848f241031f50b4157b28b0c1a3f18258fdac6c2bb4338cddf99576c9f4cb5c71cbf298a0319969b2ef83feceba75662da3617be9ceee035c132e6c21864c4ba32ba20cd4fa5951d5360620b81c1b1c70f70d801f902d635f1b15dae8b3fa67f2c9ae796b9901dc5d03ee8e4406dc1d034c94187392dc5973cd9f5bd627bb84b21011e434b1a2ea8a7e76158932e69cab96f529af02dc8fb0a30aa118323b805415ebc8e68a55cc627dd61f412c2c1339cf52885016ca42744040478985e49faf94d1068bbb5f8089bb8af5467f5aacbe2ad3b9fd6dc38acd673f8661ad366f15f5590199d8d53e919a3746cf0c83a13d3dafd7fc0a027f86b1e6b31b8679fc6a9bb96d5973013d4338c8cd4d027ed559fdecca27faabad31905ffdc738f3cf106d7495c160ac2973e621d4e767502d9479b02ccf2ac4ae402b1028accd3a9b15a2571c846579caf3c7c6a8c96a5db242cedc66229b15d266a23607b1a6467704819942641fbcd38511a70aa7f1f1e3a5b18f18fad22d58de3f88dabde660209a28fb70ee984103881d2f8d7dec35b758276bfcd239b3eac21add21c40af9e19389643586b63c2ccf49947d3871f6611a096204b6c35cb14ceb1f2ade2c8f5016c2eaece493d58137de1183b53e7446f30fd6d94359c8f9a3cdabd09a8168552d35de78c789b30f1d5bb33e34508b616dde388818acf5f1d2d887caaa3bfad471c660a6107b078813ddc163ebccda1a0339f311f45c59968e7ca26d0cb6e35c65ac8e96d5d1661d7395b1eb5ae693d5b1b268919f153dd7aca3b36aae40ecc81bef9d9f155d715056331115b328917ce2f62706c488430338547701fcc6ff2f9457fd42797d58d61a4760c44173662338842883dcdcd4b0917d7eedf555d5862cf45af6da387486a1fb89320cebbcd9a0cf421f635935bf368e9d77dabcd922e69ab7468772de2b6eb1ece7354af3ecfa1afd0702a4f4611f264b6f6e4cd407dd5a66001c3b57dcf630734cb786f60fcfcd8dbeb9a1a1a9d135bacd3f78c5b0ed83be746bbc8103ff8d9f28033d51066aeda1bcded0c08ded7ff31b7f8c19a20c24b375c0dd5d3eda1843ab6583055975e136afaa2c68d5799fb945753e5b3beb73655d2d2273679d77a6c9a8144e71a6a120644aa1991148000000f3130020381c148a05c3e1984c51d3a898071400098d9e5e7c481a6759960429650c82841802000000200020321824000f5fdb4d80ed1d498335ec2374a082a494675331829f5cf9bf2adcdc521d9c7f0d6deceaf4f6da23385b634579c1d1552115c5896d3108e7d34c6a204edcd54d9ff3e9b5139c2fe01fd733b7a7f4666f17ea3dcc9f00fc62e05e4c987f8a200b5f6bcb91ea0ce3fe2a3e33d323b7d5b22fe8e36076d0d7a95c0993202e045e5c3d018223d3d49de801bf44cb66e384930b606587676c99ef900992ce2ccf255680262dec8e395bcaa0070588dca909f8d17685bca8dbf1cf21300f72256b73b521e0ea2ade6661f6bc9fc317b25eca3a866c0104c77d721fda1a811efeead0a74797d8117efbe0f08b0f7a2c45e318e41f2ec188a6aa568927504a6ff812f4cc4893aa567f210d8d38d9575ce4084df9cf19ba59ea14f1a49933b1e04c70ee0479a5f5b02f7cf3e05c2a0a6fe27035776a2b54a83f11a49af8dba9349b97a00d3ccd904f02e41fb46a42af4a3d710c8990ca6611003dcba3495da20e88cb763c60bcf2a08380eb9dda712256f3d8f9fb934bbf5cfcbfcb06fab7e34d59a75c65781767f15dd73e6579af65985487ed724a3f55055cbaff39708c18e76a1c0d0620727267c6ec5535961266560ab723d5f949505868aa89a1511c3dddfa0e68ba4d20f60faff1b5c77b87d3b30afd636037f9350cbcc97c72eb75ee6c5c2207a91d9148ee6ec82063c4a3aa3784ea7240ea1770aa8d6606eedaecd77ec4f92dbc26852a680c0facdfc03658f82ffe6f126de0f9198539f03cd91f1c501ec81b059b85f17a4b56af75f4e3f5186e5c95b404f4fd50c41195a779cf9f48d12b85f4b89cedf55ae3fe4d7c940c10831ba783d445b699f98b75c7944f8126ecbb297d22eba7b2fe727d0818a3d77c0878abae1ffb1470699d8713bf3f1cb5de8255b5ddf4ec493aeb0e98e780683fcc84af2cf7fc2b74dec3c239c91cd996326f28796d12dc3f0926f9e1b75f0de14aeeeb697867a0152704e37e47162a9778a6ba5eb45dacefddcd504d6df83e03ef2fac9d5cda1c701314c906f73e53467ab509e645af3cfe40046e09d75309d8a607e86802f201c69e80ee210e9bead3e053b9fe0d873eb8aaf9f4e3f6bc306a86f0faaefaf17e78956869b28e5974d71fb55db98cef36a60f64b59e552ca9d012b75a3cd80875b6db17dcef45fd20beaaaed757c5a50d7f9fdf00c17fc0d187e5819576e7fd8e9065da10c6da06364143768d4a512f01b69ba9ca0417ece56b4b8180163c003f05fe837bf273912e31f1fa9e9cff81ccf764c1baba1193470782fa2f23bf5fe00b98f0d3a36a95633c4befa9ab15e0f133f7a3e15cff2bce22564067d1920ec8be97c51765e38d5dfbdaf4344a09f411dff37cfe5521d8cf137d21d488c49785964b9edcf0d37ed82b82c5b080675a8b0036cf6d2b93f4be8fa3471cfe970a3f453687bf2ef90ab9ba32660fe95f45d62adceeaae1d0a4c6fa1cdf6805c34ccb4eb27c81367dc91a5e0d99c9e2fbbd328a272d979803095703fa4f4002c509e977c4a241e798d276f303f274c0f8b10c0ea999761b0b9a86830107f96e54fd727a3ee65cdc058527473de67b8c66223ccfd18aafff5ed1d29e9f89c1881822184661583a7164ba2158c609c7e95a3853b6993e89bdc95b21f82b80d2d435d5a2397b4d2fc0dcfbf25217106d8953d8e8aa0cbcc0351abad6f78e91e16b35610e01713b3086057a814c90d7067f917b84c7540e846ba918152af9f565f13dd2e80ec5a4207c78992cb9036b5a9dbb41205a363f24ef76e9428f5ae174f46e6acbff2b5083022e728830d0bd1b8c5444f9c8221b231a3ed024b08a46e26a658b48a39c6171891ee84849b435c618d3af3e73cc7e3471a030916224e81a73098f477127d7f6569cf86e0f046520f4f338796ff955dbdc57749a076a8ade6480911030e8575f337c40d711f40507502456aa9d95e2f008650ad4b591dcd5e82f89bd80e0b1473ba3efc4ca7580cc26070bdd810951e6e872e8f8182911d51d3960691fa0b0ff797c637305ae0020a50cd78e6a7a68399b2da11d42fa0d2d7971f5ca0da5c94c210eb4c6bf2de95a178a5e7ff5c563fbeb6c3614e921775dcb2183c8fde8ecdd4d2dc5017571afdd5446d3f7fb853d2f42f18560fb6d5dd7b19ef4f78036f8ef5ddb9ceb0f41ac9ac27fda7e83850f1ef135e78923daf37add2acaa4900bb36e725e614e648dde9a29ba27587d2bfdeb222482bbab3358f7608c0bf9c9f82a9a2ff05113a005b9c43103494be74daea3f76ebc2fa2bfa84e99e39e8543cbb5541d3e637ddc62864441cafb1365e27968108f8f200c4415c99ca1582c59ca4cf2b793366736f63506f1723636e7a3000f20d2e489b9265fbdd6c5f4323b0e985bee96213ffec04dfb552cf8fff9bb5df9a7fd3b46805dc67f096f62818d8b963f1a9ce5ae81851b9ad70faab9d3c4e457ac35df32e32932f21f4043c9639daa35e390e9d0a80ff4d1b0e1cfb09df991c145ce06f79e4d0deac1ea9ca71c35ea994d9bd06317d659818c81f3f5b49d14e260a4c345aabae914ef9c4c6310311830da02382bf2431ad089cc2a8cec813b81278fde3067ce1edf1fe9e9e6f62deae55450b58fa761374ec204733021a3b12d3f149b9528b6db579beb26829c3c165846b3f59621fa25679b6e08c68cbf15a66013b84c14b99cc852193f89fd30d9405c7eb8515f6566c9bd806dd75275979360694abdb1a772f4fa3ccb4eebab690617dcf168d568bd99e9afb4105a6d2eb9592577c674aed04ef5db500d4c0715048901e98f9a8b291f73c5dbd3a52363cb07e3fb498aca0c45721a2b6d4144fc12e4352392c5cc538d3599c9edd1b4a2d0a2909423138e18ff734999bbd72a4d562cd7d30d37a5317992f65cdb3f5e45b41a3f2ce1cebd0bd6e0b154aab8822efdd6e769a841dad7c7610382e33af931a17934fe5b5bad365e228b6e4b79fc7889a8683a14277776d7a9adaffe5977de29a15e058bc08a5b853a8ed30951a3e34ae74ce8be7204382c4b569acd80e66a57ddae7a1df995ebbc08e9e81193255db7a31782a61e2ad33102960843a5232186a9ead77ddf59e48bf19596b7d1bbafb5e35287f0a23dcca253e21c56381c902df279b794d93a2a1606336f875464714b712588956ff21ed46ca05506c234ca941f78720ffdc2fdf02f3e0ce6f7340d531db20437b968e2802137a376325c67c6405bc643227d50f5679747b45465a77f675932bd9e6ac99c1470df0617d013533e97ade559ebd1892baa5f9f89af02453e485c990e6870d9978683f809e53e252a48ebb97c28420926f9df03472f02bcc6919e06f554ca2d139923337043564dca7aa53e6bd3807b82703e190ca8ab4def80f0eaef429401672cf2bc152a468b6e37aea53e241c698d6476b5bbe000b62da1fa72cb10704bd4733cd4a4c961f856df5e420d183b9ddd5ae527e32b71f97b60c6933731f6ba74776151cf80d70d7f08ed4cb6a962365fdc6fe34bf96ccc9bacbdf5e238f61dbe0fdb0cd6da3eb985c0f13a823e8a0fde770d10e0cfccfb466ce4966bd45145d44127146fc60e1a48dee00508f140e88719247c5c83cecb9d9d80a30cea65746eb9a9d424d1a3fdd2827860d7701ebf561830a4bf69d706df7999d202f796bca3b87bf7cd6be7bf1d0fb97cc05dd0f447b62cec30eb9bb162e97ecdcdd7c51c1ec6e77e493dbe22859b26b5864b6c9b56b131582acf9623833ecb3aefb01419d5e25d7291238ba3ca7cd1f57071501ebcafc7cec8fca65002ef683c7d8b78b80d7c7d58fac92ccd42870fa2a7c3051700534f913842bce840dad88dd6805c7ce4fb15ad0e495febd31f5d3998ae1620358fb1c2a31bf801074f89a14ac5a0c165d9067e15c87873670b1dd6d4e13dfd645508751b2710a1fb0cc141c8641e7a6fed8f47471a4d3143c930d881c80828f88b31bf9b93ac63fa8e7eb7da2c1bfe181b8a3ab0b86986cd2a1e005a4288313c9316fa4ce4c3664cd97eff698c2aa096ffb0211bb5585902ea7af50d2d77effb1f83f5068e0c4302aaa2d91d721b5e92b29d6777fdff2dbbb1cc193e83bc7a331bab3e51acffd3e7c74c66a6ceb8a300b6eeec69c27dcfb0177e23d0086a08161057b0f13cc4cc40298724fc619eee32c4d5b8e740cb17228bf32d41bf78d0a8532ddcbf577f428ac518a7af7797c56a5d1463311a95480f0e0454599c22704215b7a8702b60e256d9815e30e3411f64699ae0df12c98abd454f22ff1743d61c029463a6eae4d5ac1721bea2e43470643b3bd3e87501a8e1ab7205d97ebaa377ed7b986eda1fd4335427d0e3fe7742ae1c173a135b8d5855f2c75c55b8f6c55b0e153173b868af31e078f638ee4724bd3382b49e5817e63e6e7f720d814447d0690c908ccad05cc1565fad8d43d23ca33141bc847415539d1a9b6ac6464a3055e37f3c94ff581c590ecebf6e9e7c6527a54c4f4d2d4dcffd2988dc47c1ba1d8e3b8c1933a1499ae0204fb88c73f90b57baa53f84d570955e17080b6e0243e4fc3420a84cec9b2578bf505d9f57652fddfdd0638d2ea7fdaabb364fe7aa81b94bbb11eaa932652ae216c98b45811b5c32d3d3314c051ca38c9d8ae833c301e5b785f96739e5fd4f58c494535c0ccdde43e2f5bb744ea4bf2d75fa678022913d88a7e702f673708954ece3a30449689cffc787a9743a4c6fe960ea2426e5fed2d6f5b59246e545b9b7c02835119fead4764837f8a0b1b2f3ce37da7983807e1e229ca3925a66e65b6581bcd3fd9d68431c1add3326029506a15dabf1e7cc1d6094a0a7d342543cfe80ed60000dbd06a67a0bfcd419b82834dbe9cc1da501cdb5451e2dbfad9b62bb52bba9c3d18488ae387cd3af870c4f98dd0d408699db6e98f5de88e33640cc771d678d7b49f9006ede6c6e4cbb38fec79b3827ea11c4e8bce64a32ba80a34baf1d9936acefa10aed5b757a4859bb3fd21462a06dfb709dc1e61e29ef4d20ff738c366b791b52994866142608a6dd898ba6052df408010520fc025d60fbebe6e7e53b3cf6a5edf756a9d5dfd9736f1c55f9998d5f4a8ae2a29cc1bf493fcce8ac607bed53d8b515ddd255bf01105e71102d282f76430d9da8377ae605316dc61e8b5cdb38800a7f86370975603025176e4c37b1d46c5d39a50f092a27eadafb1d924882950d05a08f4d0aad96f9988e0f7000304f2bfc66498ae84c5236e000e0756b948b97a81c4a8d36e9ef2c02993a156e278be9ec5bf6c0456380963ef488af447f2e11b913859b1bb55b5420f08a20b39db753cfc04efd9d447a4acdb04a2453633f3f834cb7512226f87d30c1bdc0036285e06b7f80a1064c3f490fdf7652e9974fc7daeb9480ea50f80f0fa0997b16ca6066cc5ffa2f69f235d8cc7584552869bf03840bae7f3ad6bdab2439449ac5f51e37ddabbab3b68300ecd2e603c6d535acbb1c7c38c0c8e32f29b476801852fbe27b33bfb8bd8574dcce0792277ff16557e5eedbf636a6cd4e279377edbfcffd53a8d0aa6860df816579772e0e5ff9865f0f2b0148463960514c57790b91e9d2ab8872b2570fcd26ee7f19ec18a1f38077c7526ab826f819b251be005f4bad7cff9e17b0e90d60468e97b7fcec6a76bffb614916970abbbf6296558ff73e2ba42488f6d2382f02881c7cf1d9961621a24f4a16cb3a464e61a2759dcefcdf58d08634215314a3528141db7272486975b81b1f9c0cf80d4140cbd00ca67a46953c4bf89e9801897cbad4d3f37548498e74f2f77a3cced37be6fc449b44fdbf0d70c0cfecf357c43e6383b951891f7e1591acaf00ad6bc4eebde549007c1661880d4fd59dbbfff499caf2016f2e40b39e5f3f091ae833a29b53e189bdc7a1788f70dfae4edb93558a92a6e2ac5fad5c13d89d7ff282d63cebfde45a37cf369c548026a5516971d3889c76bbc6f660fc7737b63962e51fb280094ce3de514b681f94e5393d6e5268a8d8d17621cb187548a31c29f0c60df9a8564be095d60c4e19253c8eddbbd9b75192f73edccabb0bb4b60d53ca464102b34e6baf556377ca8e50c00d6f9a85c49e0e6054b59b2a1f50c6da6432223af5f08bf5e7fa76947f5234dfd6ff508a9d9588fb5f313064bba13b7ebaeacf3136a5c3f6962932606ab9d1213f6b72a999fcc8df8d6679ea7d4baef57fb41571559a953f8dc0c7649a311fc23286bbf7ed04f340ff5b9210b1660f841d317c951a35f2feaae5a1b3ee5ac778a92b7e52dba05e13f628fec2db24015d69f4ef9122ea0414ee3ceadab094bfc328611bc148863b711429b69e96bc6e8268b03e1e9ccbdc6db60e7174b6fbfd6461f6043fadbc852cdeed336fd0874428149ad7fad96aea8320edd30b58ba3f017cc83bbf20ac184b8bf4d7ec0d9c21709ad7241c5ea4f7c38681b75d381264fdff3a9680fa4ff43143989fa4712a9efde521d1466de75fe3c93a9dcbf4bf66ee44c696dc356441fe56a781bca7d177c5fb167a95bed6484c6ed1e140fb67ce3f3e0c4e463b5f81f0ccc3ac40ab241fbd2cb3dd75b2e1f0f3d0f76ae1673b2300d403def4a0f78735047bd456532380305725fa950af82dbe6d6cef7c8e9c35ba9b6e05f4f43dc2dda67c449a619a4449260929d1d03efa8a93e17606576da2cfa9f5a6ebb7883f33760f30918549ae0b7167d5acb2d71f78ae7c0d57e62e11e8986c6c149c21aebc609e2d6db0ec25584d1811cf3522bdd85b97dba88f062e71d6f45d807c2182dca898fa1369ede5d1bbf7ff80aa73a8ffa475c29d2992fa2b93dea7511dd40310bd8481ed38c8b9980dfb69a5c6182bf6aad0bdd308b2fe3abfa2649eef3a8d5366a254383234c0f46ff6e3c745a049da803b6e96a070e595118206489429debf85164c8667583c354ad09684c664dee26615d1b47cbcad1e69d48882eb69d411a97f0fe0b67853c97b65ad631dd669e8b5bce0b92e6f73601a76269be5ed8bd12a9fe67282af6ccb4ecdf5d3f2d45cf45d5eac7b7845464aa2a96b3bfda99f564b20427b3d95ba77ceb6ca62f4ab2129b9f3e536d11704ba270291fd152b5195bf5f9ab94855f1d03162e13cdce9cc54d2358aa797d039a6156716bb23d82b3959beeeb85bcffd1702804c7ab4ffda90e44f8f7c0c1ab4658ae21f31dd48ab2e13d8fdc37d4416e5e985b1d57ef20ded2aeaab1e152f9d1f2e86810bd7260dcde40516c012c66bbeb2b541be7cf4e46f415f587a75a1d14186f6d05761f0eca5791452de590e4b736f4be58e2305d5d64f3f9d44c7b5cafee62c1e6d78bc7fc4d4b70be38633094409332a0c543e10a408086477467be7c9eb185cc1aecac1d249cb1df4cc0a62cde505ca221e0eb5941e3124a6239766b0b0ef0d58d59b56b659b9cc359c83bab785d8ef9af4e556e42744a78ee9cd0c462fd7c058c24c15c6b78c46c708814350626589739e0ee217c8804ecb8bbfc268f54bc38d446dacd266445b0e7b8cc495437e98dcb1ae656c6ebffb1e5c60d4fa690867e089333c20b7e4428e8d62d0f5ba24442c2518f00376775dfb07fc2751d45ce14c3156857519b8d384751b5a415c79826446e4c3c9400da685b19a11dbd3d07ad7560843ae5ff694de590c9e338a28d5008c09d0af99f391bca6149fc8c9b7f3c935e0fc45a33709481c5760d66c1f2c5da587f7236b151b3171b513ed782f807efe4d86c074cc7faed4b93a6af3570da017ba5d84abf57f85f49ba5668ee4dddd338dcbe35363b581185afef719f606d2bfc638b0fd5d1e3eb449773f314f717ac3897848aba9ff932fa99c18ce02ae95870629e4ba47c0fcdb99f086381318bd43273ae775daa92a66b4df4529c3e39e12dc7f4a9a01aaac9a8e5b998c807993d208d6446c54e709562a8f8cfdfe6ac716c682f4bb420f01399ff6fe65b281707ac775f1f85acc741f1566fe3041708ba027dfe1cb64f293a91b0cbeb72dd7ffd7eab8125dcd70553b5f29b1d4ecfa8c3d61cdcf17848c02115c4cfa960ffb757848124165f4c8ff84afd4cad3190f7d820fc20cf8db71208cc7a6c756c4a166eb86b543e526d8a3ab2703d1637b42cde1739c02b300262298cf4125d33093496bfbdd73f8331d69bfaa246ce33d22c8ebe56ffbea0d6a9198cb8b9062866f8db9df00c18e4354ef82a16dc40fc1b919f1440e26dc890a9a0935b4c7fc87cfb66f8646dc4d915f84b6cb24e53973b6cfd91ea15ff40f806bfa57443114a46329abfedcebdcf99bb750198cabe313ed3785c2f44de81df26cbe675767f8a65161d09ca7f34e53e39c0458a08f478cc05a1e52ce2679204607027a350de2463dfb8436089a93d48f7d3e150e343c3fb2878289977337152cb1b593dfe06839d423258cd99c9ba6f59d1e63b84e633aceaa579f5a264fd4ec1a08c4e7449cb2a669fd9ac9f33ebf979264f7d887f4a76be24ae31fabafc587d94cbc80b50ebe71f484b16e3390318cf5ef82d2ac55f55e9a46f5af149593c84a0921b545ea0e9c0ea56ed1f46847818975779fb79c4dc45836c0074775bf6bc41d12572fd3c2fe089af00f0730f17474799851fce547d9caa84a0465f02bf1c04e9ca4cb9ab15bfaa5495f3f2843e7cf862ef7249af66bfe2f75cbeeab9d9d2ece0dadf63aa5aaa29bf61dd01a790c70852cbc1d8ad59c5cb8fd9f8630b20f94f686042438f56da927659e8cffae12a981c153c18ae53f664bc80a5f3abb64aff878240bc22861744f8877887168eb156558923959c672dba43c01e8f26757a89c0f46301ec99515906add0a22a356c1d733a731be8a49c1911997bbcd9c471d56997e694bf6958512b0066b4e5e9f96026de44edaad85cec3ffbceb17f255471ee314606786856a02ee923898925d413efdfae652250495c706b2e76e328927d0fb035837cc48aea3220fee5c23210abe0b7d436fb6320604e9a2fa6bbd3ea458481f76e8d43dbc4e7fba8b284ddb7865788f12af90d3c64a86d97c50e338abce0291b9c42e11fc4caef43f0068731cb07f8a94012ec00b1218070ce829c5a8926f588c5cb5fbc044948115e81a93c489bec04db3153e8bef928f89dcb727185679073ee205a880ce9481074109dc08cf7bffe7930f3d5f205a04adf06b9b5cb8e17d42db23861d698132c12760dceb1d4d17b1919a9c1db1df65f9c2d4d57ca08d44d7d78c68a0baa95e3b6daeca5a200b9111fa97b186efcf306e35a34359808c6e56da46a8126de8d3c00d80e4d9bd4613e938441ca079f014122dde3ce75dcf11c47b0ff3392013e0eb84fe3af231e71af85868a972f35c7f1d814780cf5b92601239cbca0d4fb7cb5132474c12008f38edb948789f408bd80c91c29903848d3293b4bbae4d555a51b35f913a65614cf4e1f8f08fb1e8c06d9c8757e12a6d072ea961652e0516c4859282fb53b58dee4f11f9523774848f261288ce707a447d786504efebf899e6e3251da0ccc456e7f6546fc23b5e90e5097aa78bde5cb12ae06a3724340ca3598d3022d64980f7ba5a1cfc59ed1e7e8b72bfbf9f0657c687c4693fbfd0d8e7ee3c50fc4b8136cf76351b357876ee719cdcabcfd3cc307d21441c0b104779817202d43601177497831e29e571a3a6091aa93f55772776a31d83cce3dfc39eded9787a66add058d171e6dd37ad34d46edad2a2103de057a908f1ab0e61ad4afa3681a3c4fce47d541f944d0ecb3b9382a53fe7ddfab6cf281124dc12377ff94ba36b588ba1adfd6a2c8154967d9cfb81025bef6b4f831609204ddf94eaa1d005322ac2f59ee3028752cb601b538adeb0618fa57d7fe2f75e60255596f4d2d97fcb0feddd48e5b2fbff7fbbe5ee005f9122faabc051cdd790d785501e34edf57fded32422d4ad6bbb1f0c4f9940aa22e5729786cc0f1faabc09126f6f390c7eb5bc9fc815cff3f121a3bf249c38fe635de69b8a80ce509fdbec126d8c1b4f0e9064e66b801e16526659f776597affc03df4f1e9e29ee7ab8ea7ba2b74daa96e253d9dccb8779a216369eb83215cdf365bb2f964c54166db7fc2b966c2a7c0549a3a9ec895adadc11245fbc881681e40dba5a4e606c541efbf0e26d9f8f3fef661e4873bd77385f1d9ac141681eab582c72c70c53b1c831cc5771ba535c3fbc18fdafd18e15f2cd45bad2e1ccad897c107570f6c37ded853df9961cf07da319b9e93ed3eacb9842e58785ea1d884c19d5ec7a5f1e835e4389576b4d30a3561f9f058e01c7b19bcbd1ac4b73fe9716bb3866161a47694b1682144454ab92ad74c70287d60fb24f700dd80cd4eb999c1a5189a0a8c9a1155f2bfd80acdd7c78519c2d713ea0d6107e7b2438038a47ebcbfc6ec9427303bd3ec8ce99b30245895d2e95aea6ee26817becea9892f5d7f5bc501abb35c0013d997563d83aa450c9c626af21d54594b067175061c73be5732393eae3a93eb63411d11b286350747e356852a6390a58e66b3d7e428bc83d0aa2e3354d5d7896549b489a0cacf6731bfb9ee1ff593636019ffd4c5e16143bb7a67aa40f2b855a65db0bf5428607f4176aa947aff299518207a6d48c6c78974cc532a215f8dc011b85cfaa4f730fa478741ff785c2f003c7bb38915fda1334cf77a7e50ba2111644ed4605969eff4d62cb1df77a5f7803ce37689cc9f83cd2131bcf5ef6db7be3311292bb17fe4e1fea47a73fc37c57db88fd2db0dbd7f96b415f9b94154f1ffdc67c79ae714ca7ef5083c431b52ddb5eec87f0d1310071f6666c993c8a00ff0f8c79386e961a4b8bf71d7fbf201800b181d9eddd7174b24daec7eaee78f27b9b1ef797d52279df8d61c6010287dfa88e6b83112addf80e0bfaf5042a55c28cf7f3f62d943af402b295165431aa4e8acf37f039367d1d04e22cc59a77da1f7aef61e4d4302f47fdade652c02c849ae5a79c9912b5ef5d6ba7ed277fee0b2e7468003644a4f0f3a1f8ee2bd4950dcea0aad5c8badb398f287d31c183ffcc6123c4f4facfb5f5aff47695e8cb7aded69e4abed75f30a5a5e4bf5a62bd7f73a82ae70947dd7fa0f4666243fb9ffb5f0492ff8b1f7d6e9c968d55458c8e05c03ca5ba493a4ef33e6e2f86832d58bb33ea23ba0eb0bc314e4963391b33d91406253dc5002e832fd15c22d8117c15cd992ab075b5c008f3a4dc69d0c5328772d1e9297f04c1162077ecfe80aff7631758276c3f5ad7e8fd74a314f13644f20d109b0e660eab10db993d776c747ceb6dc439a5a472af2dd9ed9ffd903d22b4acc067a9c826fe9bd253233403b97a5b76ca1dd2f499c32403e80dc48564bbc21f8fb4b5c85d756ed417a1648784c2a8068288c9c8ac82d828b5913885aa5e4465a06e79c18b0468b665f2dec35c901c455dc388bc220b0188936a6c5f900cc29e950af6e3d55e700189727798f8a5ce28720c4002648c346405deebcdb1b4f5e6625ac8144f6de83524a93b7b754bb5da8b52f1249920c17a0b7b02032f789dedb42c39442ae69ed3099aaaff4f658d5908ec31bc80a08715650e856e8ee8596ed81d116ccc76ff3c827a053688c910d4d354fbb258b2c32266ff7d99e7ae288969f19a53ffc3e0afa243c3c566aacaae82f05ca071b7d373eda531a21b8000641e0a506e7def633f8215250c96f449fbef89c5ae48c6df8621844302bd03cd925bb4d21a1e47dcee625189e2ae0ee7b77033433304134c73eeb43d23a5665cfc860b13c480ec6e9f81f055b1dfe0596c2376a885e7055d58d48a658ede82db333d7d8fa950b9fbcaff7ddb7c8248937fd68a6caab06e45dac4d7f2336a853f5ba5a7eaeded880c92f7aa30fd6a38faf63fecf0d22fc2aae954cb4b2146a6a68ed1c4d3b8939474be3cd0360336226363867b98294783e544570de18705aad3dc03f04f860010616059bbe5cc83cec0d4136bcdadea0b5190b8ffe5ba1a3407a8e2e73fc566e8688c1fb441925598ec5bdc224f9b01afc25379e5a69229001059c78a0e01f0b2c1441ae8e15621015fd6a38806e43631f5fe10a936a7e0e375f9c1050eb47d6a2ae0c639eec9eb77b5c90b77232c252dd65a7f144a81e8759e4803be7d186520d54f93a8bccce39859ba22cbac0dbd8b2ca2092ea9069a5dbcc479e1ff5d19efc3832690e58927ec470a7301a23cbc2d814213b83d0c913a4ddb1cd2416de5f89fb64d6005fe487ebbe0ea7fb29f49bf0def951dd70232b18fd8bfd829cfdd516d3fe2a9498a3b1d8ce80f54b3f434e5d55d215e9d6a0190cb4ed0e1aa063703a73c84144f892d716986afb6d2255be8316957ec9985bd640bfc35807df3d75ecf0bf29fdf2779a15713955a5487ed966350d763383a6ce22e7ac7f5c31e99f6a8418239aada6b77778334cf497e6e6897c4a54861955eba137f58b25931002ec6c92002c8ef38a5ff3a4230c32294b77fea36e14b4af1d6bc7ce35c8f86fe2eae41d9d5f66c0b130ede186115fe31b9bd3f1f678553a8bee1afe0d59d7a9faebc1cb524047d0d558ef60f7e2a5e73d41c162b0956bc95339de8957191746e20e623db56b1a02b213c320297cb8075c74a81b128b40f85cea43bb98844cbf3ba1d3e59bec936d43eaaafacf89983357ff69d792a34ad531133dc9cfd83e567a8e17eda687fd1ae98aca665ac4ab394063b7a759d7aff550e0522427df25c4e113363a94e03cec79db053db22719c7e6320c224c397d00cf53886749c4b1ded3b8a7795643cc56bd4e0d369d6fdc5f0d7f53c5d1342c2594d259c488eb150d484739e0391eb02239a7b241cff440931fcf6d2373e0d62b8639efd916b561e19e6aebeb53a8fffe046abaf7ba597887be1df42384ab88a1b9c04b8104c96cfc5424d4e8da379752412e3b5d81db51dee0a29a54e3d0cb2408410bd2818e24a0d3c9b8f615478732208de4170d650c75cff8d1ec92a8638936aeaeb9c1860ee415bc6647f0899c202d1049590259aabb38eedb895451860b24888a025124f833c71f5485701d6710c3251cc46461a54d28ff2ee177919049d9117eff43e8333954a0b1fc192e79727768805a839e0b78d695ff182d443a40fa9d15355e0788629e62992117c876975d39a273865607edcaecd34d69f9a685c2e1233a1d50cae233b4bab9080dcb04edf74df7bc4e620ac2b6ee77c6ca7eea9097d21cdcad42083c8642ea23060c21bbbdc24aa1f3f7452f220ba5b3dc8a07b0f1262ef6e12a0b382a705d1d8f4d12d981ba4212d2e36ae9d7754492098d8e9cd0535ed4b9e757a81b1af449904ea37da9a7479ce44b96e690d4656ed9c3f88df3ba620bbbbb87c90da8597ca1e8c55a38513232063250814838b82b406d4d995abed7e1f7282c42d2a2d0b24361dad46039f8f70142808043dcf71502bdc30cd16f566ab9384a8ee55c52155f7f77c3a36ce3e9851280f3e490681734cc37c05341853c8caafe6693e099c212c961a1954a4172b1b213aa3ddb4847ff8b64b7593c34fc9b11454841bf841379d7bb34e78641d0702be6dd81c87aa3942a08b423de3883320528480d935763128bc1e41433ea4e2e8e08d93ccbe551ce9d8c2a3255a2f4e9990f58abe2b854875755575517907045448cbcb18f267b0a1c3439ecf7f3adc80ed46c322005d4fd13ec915fbc3ec4b4cdc401873b63ce95c43d533e3cdcfb01c58d1c5161d9605f182f0a296ef6c0f74fe6e3dc1f82f6d514c2e4c5a8382b5ac8ad5d59e2d66f5bf91c262f6f6d50568e69f1d0be8e9f96677916325e11a447e20189322733e6bd416700258288c2162ac921e64182644ca85ce9c96e447e23123de12bc5efa6c15333304e73f4d4881363352651eb701db2d1d5a43b5db34aae4380c5b9f3b39eb49754ff1713a89832800c1fa6b19dfcd267c3714fe715bf496e4fe4b205be43ecbd2755991152e0b7756b0b2bc0415bc3529c7651f5e86c2a4f14d61248e148c11610b50bcdbdf80c6369ae3913ddbae816ad765632295a292d39149597c4d28a2bb5f0f8ca72c8eb07f92590e90c08c4ccf78eea5186a3ad41347f42899db9cded067108ea34f982850b06d4146071b6a01a8c1aef0323b1aba6d174ba27ac571344ba506d6e1b4fa16c024e5ff47f58dacb02c1462fd44521ffa40eff55d5a13a50a72a3fbf73896edab85f491c95e5207028423c46b0136e2a047b10832d3c0e609a9e7b711941c7ec2786992ab05b8e2cb0f4c87fd08ed57e2d6afde43116d603530bb9db05155e879c2e471ba3fb996800f44a3caa5b3a8315463ea7d650f2408440ad1de69036e12e12c7d9eea93a4fbf94f720bffc584076f77efdcc717c95fd5f1ee7518365e5a00876bbb8c4f044a5ae414e7b0c984ee22a9b8807c3e064979f157a1047ce2e0a4650accee980086aad0596050b33780d084f1bc4544577daaa1f9a5b075004421562ab649b6d0eba6346d805cfebee00a548d6fa223c04aabf4f810af1e805dbe2d7b0a90ad99917b2edfcbb215e1d48198259c8c4d6cfa90e955f9265df416b41d660d769a4ac3d98320f4c372ffdf77349a900a16d063d03cf10bd8bc85634d18ad53955aa8e190981b245c78116c667a2e95a2ef348dc83a3435f5a1751951839e35e786ca92d4e1a8d40580a17ae2834c73b098adc5f7b71e00a31fba3e41fdeec63d96e498ad1f4ec421946420a96e1cc87cf1fc413e9aac0623f9c1d558364f4836b3ae8c5038ed498f3d515b93c9464bae1fe7066d7a8b097d8dbee21c8cbdc100a5fd32f42323ab2155000f01313deef9274f8c0c542b72029f29ca167fb31cd10bbba646047665639c0a1bada37f7067ac619d7632459817cfcc75bacf41196970b1ba2ffb5fdc924a367fadbc897831608ce742f7a1818c4b6ce056b3e95fb900c20b0d3b9a1298e536bd29926f9c43354af292542d203f5fc6bea3fbf4225ed41807b102a6e059eb3a70d4a037ab1a0b58672beafacda03e186ef40ca9fedf8fb7fc67ec077eb8b895fa8d377dbb8e4557fcaba007985494871cddcbe2204ba15174846003c952e0ac0e9a92c9bb875c2ada6d309f1663c86eb47428809c2648e980f4a2b34d0ef20ba52a49cf8d4148791a3c31b40f82f744c3881a81718ee35e8cc700b83e0d0761643021d87d1eb40cbfc390dd497e4db6690b0d904075ae8840a7b585ac408eb30b8a03240697a345559b90905ac96da67dd7709107d3259cfd546e9405306d931cbbc3468dba5040c1b19ac91a1958b5438f0339f0622ee9c709636e77880a0a4cdb3d9dabebaf02db1882d7c9081a0f23ab2017857d5cbbb576a549d27bc084e13792fd2a0a0688090b389495f93f6cafe8f1cdad9132c3e7be6f3c8d5fa90c9eca9b0eb80ebb2ea8c063c592443cad155ee2bff7e1a1c5101429cfb866c7e0ec810c694b82b1aee4c8b1b9a49eb94b747a994fb43b07015e05070f32af5e64b2b92997d2d73779d11a549dcf995c738956f0ea266240e32101b74d722bba93b835b44ebe5ec89eb3b56c0c72a67c19409c3e4f75c0230d3a140c0720b92ea24fcb891ea2e9e74366c27e6d173cbccd55d892b2b39229a784077d985207b794b84f306b369eaf4e33e21c81a906afa0bce296959e07f738932c0dff865af2a34514ae9eec67c7cf7d47f340e876275c60f00246953f763d593dd40a8e926389bb923db16475e660a60402ce993affa0c4dcc5677cc95b184a7620e1f8cd9b8d914567df71f9097b89902819c5cc5195754d2731047acc2cb71e1735ce107c0794006aa322edfcb8cb9543269e1ae6068e2140377cde5ed41544283ced6d8a8ca8ee5b67717d116e9d67ac5238049c5a17bc3760f3a731e96604f4703a7303eb25674664a2085eb7c03ee616046295f077e736b7f8e6f81054ead2850f149b0983759619a1d184c67003519e0babb4b232b489fe90bc3f17591fe329751f5e2ce9c53612d02eac9f788bf0b8b97b5585154efc79ef3b9b53ba4a2bbfcac9e16fe616cdb77e00ea28a048461d8b2a03a79f8d6875eff454ce3f623442c5c0e09303bbe648bc831b6887fcbca6487ebd86e09873b3817b302b6c011948a72a7d9e8836faf50316144060e01cd7d30d7e4d8580d51b9274e32a284b45b33267178488e6df27461f3277ec71920ef96770b743b79e784131e5ee012507d57d9f876cbc12b48e69e50d3b2a8ade9b757ba7810749c87319d666a09c30a113d5098ae54a419af09b4687c29ed5b923dd036f86702f9b41dd290cc7ca8875cdc5957ae2559100d2fe2cc75d61cc50b53f4d75a8ef1a4c8eaa830c7b58478c21b66fef48131493ace203c3cefa52240ac7f7cc904928d6597b7dc57745d4cd57f28d27ab6b15caf0e2700293c2a6f28bfc291f1df4fdfef1c9118c30b1c4d4c4b27e8d5c27833cbb6e32dc6670e99e2895ab90d68608fd4a68929601cdc89ad19a861e8b9aeec7d9dfc6d041d6e01167f9671fa531fa84c938c599cc7330b88528ed3a151cebf4408b6038ea92670a37c97638801f4bfa2b88d3549c95aeded6c7a5bad839bef1e7bec00e63d4803b54e382966b743db3e3162ff24706bda27d4c68d50c5bffbbd21cb54300f4a574ef9e8988b9e287351c8fb6521ab7cc45bb2208b77021e8f4fc60bb327cea44280f9db217d2ce0be614fd13f8c459480e122df0e985d66f344bef116939168f2c5875422092663f58085d9b7d29ee1d6a3c950e339da9d10df9614231559106720854a7a861d092193999b105181d52e0dbcd0a43eb1626a9fd6d000bd0089708c4095d1caf1906c8366aebd48bb0ceb9a61c82f66155e68591c3b310eaffbaac20522efe065e51d398bd2a32839623a2bbc9b68d5149008012c54e044f00190129d2d827d123ddbc047ef943ed70b1c67eef436d7dda00e705f6336fca1e09a286842c8ff837ff196ff1d813bdce0c725a55e0eaf66cd2d0b85a9e6475bb3b124a8c95b0f7a65d32c46833bea4c7a46bf9d497a74022c5943be34ba16e057370b16c96bacd6124df6ac60c404440f5b5c90ea30ff05e767d318c8045098a092ce3387e91801f23462efa5bbebf4ffc6f481d289775b5dafc1540c9030bfa09353425c02ee8d041ab3c005acd9ded1230ae1c76f71345ab57162352b384562498a9c10cfe48820104edb3c23f71f6b0b334ed8e90f22108b87d089cb5528d1a7df71d355cc7b02f83e996302fdca0da4e227762308a2de7f3e1d8fb12d09ee696bc52a1be617cf8950f0b25cfd73726867f76ed1fa85c00551c9a8289508827694d8c29336b6146618d30e8b68782aca7296f62317e81d1b2f39edc4632ee2381e315f2eb708774fbf9db40727696908cbd4fc6ef6bde50c28f5356b2dbff9a9275fb8186ebea4b00f5da227a126cb389b655f280fc34e9c6f0cb14726d0c5679be4f54cad8a603d6d740f8bfd21347c993cc70bc683a65339943b1def9da6a5393db8b9b709f282f35ba0cb60856a78c470d1d3361b8c294fa5802a69575083f303f30847fc2466c0c3bb09eb93cd03878ba15e408dc1a79ec3e9c55e871112116ee7f2193860c30d981597e0a0899b38f9431f7607d2ba161959348cdb7311adc4d32329fd52f1769c446a0c24bb74a47401ee3f9bb81c4a3abfc9801e0b62b852169898cd06a59a6a60c006cb98174dd8dc46fe9e41dbb4e355e88cb23c9050df1abec53f4f3a6e9e44ec42b1a58c5c5e40bf3311b49abc252dfe7c6156d81d00bf33b617b2e6d897df03f7b2a840a76b004db096a50d0994d78898565167c4cc1f1120b5007346049f6f6adb3f597fde3a6322be5c8d09ea21bf45799d94f973bcb4251476cc167950a141585bf9519ad3f00e8a81abdb1c7973e9b7a47cc721583d0cf139f905390ff1c4b0e53a605ac5fc18233ea53cee34e0445e117c69ffd0b2b80be2792807503b31f92201cd5d5c9aaca8117155412e81b9d1545d71d010fa2313862a7e444cfd06baa6a39a9060466b9c95ee65b291e7526379990e92b168c8230e2d3fc491b6d875faca3db0745e9e0d7b47a5a7f08d19e49c88d28476a738da81ebc65ebac6120ee8963d4ad7ec55d942a98cb8106d8af77f4aa6b0bb27fbf872cc9b9e1ef5d0d03eb879f1aa9586f2e1cd0bae5e71681f6f7c72c48343fff8c6270ff08fe0915101e7b1d085c26ea7c4121b7a0f05172464f2582e6df7910c8667a2d1547c41d379ee69391946d57be6c0c44c5f686ff98357d64176b1fe7271e9b2b61521b46b44980ea29146bb61ee3852d9bccdec1b914d1d0cfa571b9f96e3c85ae95aa9da53b9ade82471cf74ed4b2f8c9c20e551e91fb79c1b42f5fadc1099a50ae6f3ff6c1c7b21181bff61ef8124352e181cbbbb3348f829f63a60af052b5304e805ac57a82b27b117f4aeaf90f2ca52f520fdf5dfd02205b1bdbad6b3512fa379d75062c3ca9bf5f2e0a285b0de8872a0742386ce28b254f9b14fad4fd7b973ec5184c6184d49c4ef9ff2c0263a1ac81b785c066596e5fb4be17239cfb9d8c473857cb67f01cfe2e6e90cc1232d71b4ed0fb7d752beb3b96fe8fb3fbff92149c96bc0d03d3347487595dcc9ccc3e3bf33d9f78e2ed482f12ed0d71303fa6c5b5c3fde6967b6611bee81587a556968a716cd9f6d7bd65dfd4847e15e45a2c30346d6dfbb7612af180e29acb69a0ad0af32c19c3e73d8e666eddec9b3346d2c8ee295ef4dc6d8b84924ffcc6a90c16c5b35105e36e63167caac703aa19d85f0f1023dc9ace016b7e29d7aa3b765a82876a1d54f7c64946707b1434be4b1213639126325ae37e7878b6154d626684a89bd6c04caf4b743f1a1f032562126f896c4cd5113ed50c2f8205746c93ca1831fb29fb65daba77d6e60c578999e67773e2f36bb7520becd9682de8c77fd0c50e3709ccedb91ac5437aa4a4c66647a1060a637278198493205aea87b5cd25320158cb327f59be519d16dcdb87b90000851b023cef47a32c04d8cfb84445605755d4370b2dbf0ef6becce8e8fcb32b76e7116c9579c4209706d28453424b265fd784048a053b57f66af1288b7dae7b02f7effa53b469b3360202e1953ac9ba24178c007c86920366c854f3fdfa1e2dfb4c03a23a8debc51bf91a17421a2b5b03bb935649ac5c87ddc5a58219745a408df8ac2c506a089856e0de3401f3f88bd9aaca8bf1e832e88717cb178f6ea33a0a32a5605bf52be30b345f5f240b4f7e57d20913c73861302087f50ff5ed660266446d5e41b44174ad19370bd0b0ec0ed1c2678afe045143869b901625a00c5a18f08b937ca6884dc3fc21e9f3112472c459b8d221b09f3c16644410460de291a00d59f8703e8eadf53cf9edc30634144829eb1a1c89c052e210d640a10099313dbff57c6ac71d38e5e78414b4d6d1bdc8352479193e58d0c31e878ab6cda6f780924811fcc623b34368127d3f29045a092ad26030be953457f5f9757a347f945295321c385f88211ab68bbe6c0943b578135e4cbee458b94f7ea4588455f1cdc3773fa25697ec7ce0f4fb14b2bba4b2df3727a181b560d2d7a03851767144a1dc160ca7fd7707d0bd05e83898a662fd24420ca334f9d41bff9f0b4148cf32ba2c34fb877e7f5c705da0a177816b08d53bef3c2dd10a2c422b6c2ae33c50456b6317548cd380cd650bb76400ade26fed398f34d3c980a295cb1f94483eb41cbacc5d17d65a6e30455253833b6ad6107cd4197acdc1a31c26a0eca85b69abd6fa17296a5d584abc044ddb3de8fae493bc7a7ee348e895acf594106e2f5a391eb548a703e4f7eb48c57571148b4a5e5eb45758ca5c5bdf452bea7280c615a923ce8d50b359108d95ea456ca9979d7016f28cb997224172f02cefda1b2904f20a894d05baf1f1da53428685bb2e032cc52928d686236053fc37d15a9fbeee9103e4f788242207ed0b431fa191c2ec143a8ef221c582461209bf0c75b3258e17501d9be94a1d065247ba5a3c513e976aac0ce34115c40ffc32a73a8374dc9d7f721db9027a0d3ef916df56e7c9a89441177d40affb09cfc2fefb78a6a4036cb7d052a92b2b321c203aeb11a187dca472edf3db96ca181906086a5714cd8be657e34d160295d1af67c1446e42f53cb74bec6a5ba48521032fb7b301cbfa9dbf923786e25cc6285f0aa42b0414c3eb7ab7840a2b7428814f15ebc9f7fb90c89f187ddb24676a1f39e696dd5ab0402c42e8c556d751476f418eea741456d95d00973804784b5ff66f35a8644b23191887a49a0d007c608fc7286753a394335034243e9a382f398ccb3cd1b4b234538a910fa40b60ff203b882a2ef09cab9e735a00f20f2865183348b0c63927ed8b868992900a6f0b2c915fb7cc36983370cc9c438234b3f2c5a8fc6e5c6f7e480600fcea35e88a3a5f7905c20740bfa9ce2324ba976f157566998e1999a28a248b570d689e15bff377009025af129fb2738b3691386ac763e43e15b619aba87f7e0c0d2ad20a7683fa4ee2a797f53ce2e677900f99cc2de81fe2c7512e0c1105230be119d2014ad96789bc1f4c34e909d1367d61db62cd694f7a632255d5469a42e48dbe382e979334a92cc6b5cf381e7254304ddd01a339970239260225913e1cc9e070254234e6ed3001964af4d6312d3323cc541830ff484028853936458215d9d0495ace0cd8c839eff24813778a9d2c9d44bf614c0362acc083bded3d197ac8b43deaf14976bfd29d1cd8b8ccfcc199e35199324f5bbbe8f7c8533e812770ab1796fef65ce2538c5f3d48d7ef87af96485844afb80f3ca1a62da3063204fcc51d482201cac808857aa01d8719aeb1d62a7ae9a02c836316534c1a693704ff4942246b41057fac0f6a2a5decffd0485017b38f9478c913f6a178bcaeb0439050b7fc6209e99bcd77f247220e02152337c363d4e2e8ccc99e6d2ea87c6fcc8e3b88852a9491bb2680d4c90a741a5a6d538353b7259c4d88a649f70472c5524856e78febb783fa253a3e8ca0c516a563417e513accb99038f8b979074993c367442082c92478b3d3b73bfacd88d3369ee358b7b292c1f088df26fba781b6c30518a59d750fd6c61594f4e25ee315f4d989b009c315933c85c92fd1a43c59b4a611fdfd81011d56f672546b0c6e19e144b318290eab06edf32f04a152e175941d2f7fc0a14a9befbed3d683740f13c578b33755b9075c96afcda994df1d640c105da4bfd6c240e0f6d2c0e8f6d7422fd363a69000fbaafb2503db4c189fadae4b4813cb2c909f4b7e994813cb0d169090ce8a77c5cd12f0b5c37161c35722c5d6693e8068125b72e3e7e5206d72a800105248c306cac5c4d12b33605c19ca156dbb9712897e5d031eccda80883248dbe789bd172cad5ec455991e920df6dd36d1072459af11825bc3556a4d8c3ce3b233994037b2001af6326baf10931110b7fd0cde79895449734c30d286bfc9293711d98d395bd2565941c12295320c548088200a63240cd8a448ee292516620acba4203e1322d9bfb906a3aab6f500bd968dc857e621dce7b4a84572438386814ba3f82f09d001cc01ed1bca6d6ce0a261e54f2379d70fe58e249994cbbd407d237567a2547516a90ea129c957d1bf92968455b6281e4c90630e50046460c0a50785422bc92eb82209e692948e53f7ecdf3251e92649e9078482d42dc07d3a197e43770f2e83ab298b9197fa5471a807aa3091d8c16f79c14114cfdb039eb41b55444546d9ec8d3cdc81eb20a100697b85f9132f09df3201bb0ff67149f67ef0489dcdb1fd9049588bd55622e3af66aedaa33dabe0a2e95cbf51a8d4669c3a1207af939471efff3964606cde3f774828cdc7b5b7e1d05b96dc824760a6f890a7a3260832ea1336eea0b8aa94590131a0e60f620b38d8f91db3726e0cbb48bf9cf9c6cd440661ba377789c4bfb7b194cc4ea6c2cdf0b484cc8ec509010e9a5e7e6ed1100f1f847d3b39e900a24441fe38aecf63be9d25b1522b0a95f575ce62dc13e08502834d64fb5d2f752a1d1ed2707f1b27cb74ed486bd38b378850d55cd13ed40740ae452d5f16f40b0a0a8bbf1a13421e51877ebdff4dcbbc38eea3e6bc3aba695d8df025ce6adf0e4f6c79828c4f129d1a9003101b167b8a80aad7a0e909e0e127a2dfc4ed12d276d763aae7e933f938a2a6ef374f8915fb42fe41c8b57323f1d5e418839d60b2f74c968d3f49d0e25cb7b07020e58ac32c6e2a9a85ba7234e0391be1b55d5ddb84120678b5d2a2a63e09f504859c07ac07766823ec793c01c5aae38c812c88c098842f67133d8f9dcbd07e16622be3963e9b520f2d77afd95f089de07513a35f64c358f7c2ade8d140b0d39d8f3d5eb56f552d82a1a9e90d47beb7beb5ba3b939a85e766e945664d225e74f20f46aed96732d0dda8923978afb3266a9471fd5663d50cacb152f822347690b8977d47856917fd466beb30abea5b4c69ff5db576531fdb31e7998f29c34deaf050dad5c776824efdb1916b44a1fa5b589185569ebdcca6989422e4eabae12a9570327a15381bbc3ed530aba7c6a6cba40f2afb1a9023bd86e0a41edb8e626ccb786a605eca8e626caafa6260adcb1a6a6323004c34b0dca1e5927ff1c64bfeb23bc3442e90a8411fd184890631bc3561203eb4d82a249d8bcb000bb4ebc3bcf082451e344302dc3436c3bed7ffeb868a9cbc4ed8c1da0d2129315153c51d535fe23f69aab77fda06320eddcc6a8222c0a14c1c33110d0954484f554d8f7aaf1869b49d96b308b3422b94049619c0548e17bae0335d88015214618f8ee020149a32802ba376e781cc5a3d3ecc68e3ca0f901c275d62c4cbeb6a5b94e3696b86de410043ba7255e370110a1913a0602e22e9222a73c7459aa0a5d2c58ce0d0e429c6e8083c6a8f09fce1c8b8c0b4470e46d760cd4ce4cabe674422db84b9f547025302ce77e86515afd1e57c371a9aacc1d9c8b454808e70b2f0a84500ae323275ae22d044d94ce7c1c3f93c9b398a7a91bc65ea00c4a08aa602fb89c03c74051df1d7d8dab9797632031c45f8835c7616c1a299f86b80e5f5ed32ce64607ebffb14ecd00021d34da300439284042db2dd5a30c82ca4d070bf2d33b53802c9a305c24fa27007af031106cac81aaae575f7780b7335cbf37c6d202b33119dbbd892e5ba493f2943e5b09dc7ba64fa17efcd8e7846a46eb960f20e0416fa7fe0b62027834b225a7813a561046faf29916ac2f5e9a6337ac083e40f460edce31505a3594ed20a8e1190ef1a530e8ee37de55805b849bdf69c360fe05011fc955d43b51962af15cb036406acef1f41a1e038d9652c38a26bec66a414dec49dd9ce4ce80c8300a6cbf50ddf02773fade748f40b44282058edbab96b34676096e961e57c0c30ba807d36f0b08041c038dfb85cc0ee34d205560442950f611fad096c9b385e8d50db509e0780ca4f45f6af1016fc54293d17c1e2f16de09a72da2b0e652f2baf323b7c7c40c90964f7bac7b67eeafbd0e533f4cd41a3e5f9cb2cd642d604a430bc75e7ec8598c60a6d3e98d3b7b0549e7cdf2c2c4f6308d09d4546e60cf749eaa4633eb26867e439668d13fc089a1bab028d8d0773a2408790ec244eb86f7ce25868a3a27c8bb0b72ca50186183082cd115be5d3d8381937f808f6e7aff3c315434bc893b9486e6914d9a92230a9c182aaece7357b1e596dbd47914baa8b0d32db1d4a6419c870aba2288c3b41d0fd8063c552c5fd7112182fb17b4b37744a13e4717ac999f8af9cd301318b73cce78d9bcba467ae837a334f6bc299ba88c24139c35981e8c8306361c18c6e2dbc09cdba894984f39d0d6ccc21fa1cd3476b029242e19a9bf15869183186ea1a8a522c29fef3c6e9059b3f4e8dee981c1cb6c5083ec52266425fa0577b6a0810898848890b8360aa132c7753484f0ad43c290256be509737f849c5b76982d470e088fd246156c2522383c6c4d7e8239483007624c22a270ae82eb8218a90263219db5dc3194717daf02c3efee24e87263473230ea289d29e567c0c66560087531450fab14d0ce81b1e96f672bf8832a0c3d191058c40d12bd57a252632df97253ed1108e62c784d4f4d1f87c0020e0e0493387ef05a6213aea3f1a00f40307af7fd3b9ddb0a4044046a82dd4830270c42402498f2612d982b0cc1da36e0850dbef145e10b10579f035a99ea7de32d99add64a874c61918b6bdfd8de94e857e49432ef67aacf9ae5f1da9b620aa6ad0129fc0277834983154cd7fd6bed8e28880c835d6a057324840eb77bd81dd1b97e94711ff43268d74726f9e62449bb9c1c32184bf9c759ed6f5dc686249b05959582660da6ec55d35f211048bc151720d66084555bd9f71ccc392f1ae6604ab34f1087eb08a00e894b7330ad5642ac67eb79007a303e248b722d2449ab8692be222608238159f55e68995a54f89c344dd765174afd096eeb4ffe47bd7ff5de3f169bf04f78cfca52b160837f12147e6c7906b3aa43edeab03ef863766e7f52f9c37673e1611f4e555747214872a9b98324661e6edd66173843a2b5374a3905eae23a3ee2b4c610734e19c8977572441816069879da801eda14028d78cca8a423554baa56aa565a6829d07d258a8678e87911ad016d2484769746b6a66c8d64d7862021c3a0222af6436898f709aef107937542c9b8adaf44e0eaa0308c5654f426896d5d6d429421b7bb45d82a898dac4f0bd9ba0281807a925ce1221e73afcd2a82f82c83a025db80d9062e35db5641699e4515343351db7e90364d913fb3dbf106584dc43606b89860306e6505f14889c5f63e5461eeb82748a99dbd80ea265a3b6516e2339bb5202cd5a22f3aba00575cfcab9f0b49683dcd33856c9539455fff934d36ec6bb4ae8352c48d7d7e9d684222ac0a264ea6d2a11531060f4b469f97232fba96658115609cf00013ac2e37091ef09d8a52a725b935328982a21449b0727f7e2c0ac6b9337e1f233f8f78a7142223c499eee2640239074487f613c4922b8aaccb72662435dc032f3b10ea020df71389572e7b75deeb8a3d0b02b6f224af124746e4ded03165212969ab3e4e4f86293f18e95c2bdaa9078a6da7e918a6ec219fe213251a106bf60bafb7ba2a4d37387b3f7daa2086d52e45d5cd90e7bbda870cec0eba8c2a22793be7104cda7b559f94f55d362fa9c6f275303a8fa44b1fb65429c59379246e5108a57bd8a033f1b538e029c33a58a0e4f5e04e8b9fe0565c809d6fcd725945cecdcd148e61fc3dee7840cdb9b1bafd9e4fc7704845c824138b09c5d838a9a5b343574b88ebd5925254ecac2b92ac074ab4574cf7a1a2f9e5354a1bba22e81f504e56abeaa8e7cede36688d92e58de39f69d8885b206b9419d26dcd850eed079b88bbc788e8eaab70676984f1b0a9fbcb7e843309ff98e3cc09fc92b460e2bd019b311466d07c0c52139cffae2668cc496d2e53af08870c131a4cabb99ffbf681d8f9bb09c3df34de9521a609f553d3dc0253effb9709172904219ad894299f9f70cf5401c27109fc255b72f5ffe08bdda04d367e7e5523ad2e12f04233c48ca52d290fe870cab62cc15d29366174f7bcdd3c3ecb359f44f6661ae6c24776e18915270d0ac4991bc712e31c64a05ca44739d2427e01f33027860def737fac94e02cd455c90f5b8073e25a888658a38cb185967dc8cb19bf513bc796c1cb6ea70f0b663f963ed00dcfbfa67c329488f75d506ad62decce209a96ab711029474f43a006fea4c44846f87968fa78759df056382fbf0343e8ae463074a769b6e0158daf4f5001e199dd12c82f69b91eee05f8c712b37fa328ffa3c268969bc6d6b522735e3ed3893cadcc228e256f94aa4c759afa90a06595fa9afd1039ed9a4c99ca2d150c863a1a9e50e944f5fb030719e264e0375bb45dc7f94fc356881b50c371a98ec0f12e7c0d11fb94824a10515c1b655405eee52448275b56797de082e488331b241004b91148d6404f208a1eb11b39a34fc4b8bcf5bb70b2ff8237b4c4d44f686cdfe512aea5fe740439a8b41a5687aa51c22250793af7b4c87e1434bc0757ca492d1ccf470221e7fe2f50674e7a9bba5f6f09ca131845efeb9789e2647823c86f5294e9be4e842401db80fab85d9a96884cde889ebb4d451ca284cb1dd63c27732075d0aab60ebc90e513cf2824481b1ba78d5ac44ac673327980b04da3c49df1bc80a368bd8a6ca10187db91933b3a88087030fb668bc43ae181bf7981557eca39e5b9f12da4568ac727fc916957fd1460c1901dab821616c77c941e4e6a92a3278bae2d846865d0b63d386c743034f7847bd52cc913a9d75388b4162852511e2589eca4f050bf29631ac8126042fd372c867edca0c614acb63c9e3ad494017084610397aedf4a4c8322a79506866b706c56879cae07500b7fc05812c07b6444f3d90e5a58277b6e6bd657641802c2d57f231aa3103c3001a88ba4f9c33eaf3277ce66110e77352b1acd9d5f90b2075c765e9cd0e4d752cbbd0ef505900c6be3de5b72f2455ac4efbc8cb68d6639086dd5f76c806249551dd3a541681eea978231174988735f6a0265e82d02054d1ddd71c9b32e82509d0d5299d4605e100f24451132ed56764e03a5dbc382ff7eb5150c302f3a974b8f2f686ee64a3cb187f7f2e8fab6efd9afedf310594eea869566e0b55634461b96c8ce56764bf17c93c52c2614329588ce7ebe6b0a921c604fc7b8aae4e2f82641aaa1400d96a8d67b4822783a0f0d2880ce233b9614888360e6b7318b587b1b0cb035c6ceca30ee82e1691f28a97100fd567f2ffff1a99d88c1a73c56751b1fbc674110dfc47e13a443c02cbcdddeb0271174bbe4454918d2fc5f51840806c79ba24c2775614f98710ef25061e171de02be5feb318d8ed4fd528aaadd90c5806c0d37e83565a41ebeb51f134251c9e97f953cf9db0d69fce5251c53d7aca79b9b4de74fccb12c21677b8706db190316654af4c2a49c5825987bbd3a4474ecb7f794683f2dea42317274761e17cef41b4e2d97b01b6feb63b8a19f7b81c64eb629dc644665d8c4247319e8e41eb8766c59990ecf025de014e4f669642fd16b76bc62434ffd39583b7b1f251ab8054d8b519aeb08130dcd18527b1146e72bcefc9584db087eb4a0d7a602a149a3063adcd96b4dd2316be558e6b1c388b8b0ec51bbdb2b7b19846356e317a781318ca8d21c947c9f29f13ab913813d86a5e586d7024d2431ab38c02e79c3dea0bd35c03451dcb867b940b5d3057f7bb0d88c07fdc740c47d1be77eaef61c04014a39eeaa8d4995b157e03cb70e9f67d72380bfec883ddb2390a75cbf4a201034c1208af35b520b6268658f5620a5530de58ad9223b2ca9b4c21e4a5511ac5ee57e4ff080d11097a1417c6128172135cb548985e56141ec6a501dd929961f2c0532377a0dec6c0be39460619056c2035a30befa371fb7fe2f92faba9563e99779fea6602ffd4712df2c26f25c6a6691376e2a45b48a10b15d3364455efa15fb91be5e91f8192d8b579544cff94d2ad183852e9a8c9730b97cb73b425f9b0977dfcf0adeecb7bc7fc4bfbf3755e744e5fe00cee9905e30bae8a880f5877e20ad0d92f1a4d210e33ff0fa0110bc0606de641f795c91e2458a12c7c10c2b206844cbe0b0fae23118e4f50df0396afa822bb0cc4cf5e5632b6a07634d4387989d8df2ce47da66864b10f8a680c44d318a54dbb027a77d064d48353508e6063ca10e63a4792883571d77e6e451f2d762261c05c0cc11bbcffd50f591917440d4f42c8e459e6dc5087a86a3f0b980f1fae9f3cd8403b3203dd0c913d709a76433018ad4cb6e0d469a0c2e0f1acc95b2a4a828074c13ff94a6505250dc2d26dd6b51963dbeb48f90143d08d0a64a4e4d4e1b0cffdeb9b3cb9a448faeae376d72703c1e61f11411f7a2546f821ea00bfa4e9f168724b2965b92d2c656d2961099d4b800d25b405c2b7443c908b1358131b994b86b787291f5607df0bf9bb60ae592f0f2a3c37df479016c1067e8b53db9d7ca8f2ca0134929eee6b27c68ab0a7b636858bb83e2ef3e7c676702116b5c26ce4a7b30c4b048fb76d2641b8a17b193abc57abf4a6a2347749946a1d1842ddf53cc64b97b068eea540ce71e6d1b4d66b175f11811d35e8835c5a4f624fdfcd992996e2bfe8fecc52febfd82e3c961de7904d69a4d5f385d68846b7141f4ead948420a02ad7ef38850059d8f0d0b59d705e40c3ed97d69e8d5cdfb49f0acb58d480b3729e1052bdd60c060702fcd2a1894095a4b6e8554ae41a85bdfbcd71749987c8192f4bc48924e86bc2b6660c6a2f8480e220d23bf02d36bd874ab751d4139fba0fcdacd89581d847dbc57ce9bea9f0d73bafadefa0469c75b5a321741db0933f95b477cbd9cc7df3a329b3fafd88b770bb880b92ba411272521aade407195b4f7c522017c1e4620b38328eb48a107c5e2668347d201577b07e2dd84c549737b228b958875c4608e73d5741e5b15ac23fbcab7fb9e31962cae8ef477dc26f7b0b2f8df3b23417185659aefc99fe02e4560bc289be058ffc87b180bc298ea48fd07e0f21a09dad4e7fe21e7a4c36bc483c496d4417eeac88cf524a1eb112b8fe2dc4d1fc1588b8485289b98428745e802f1ba62ccb7ae648ce1c659d3495a1d135168437a02434e18b30d17eeacd708635d446032c6489c8a08605c90a4c8032b52acf2cc7d31c58ae014fc108003848864e0302ffeac5dbea058ef6f992f962ff6cdbc4eaee9e5aeb13a52f20ccd9ba0682bb243afdbbb4351de22194ddf5b0fd37579d5228966f0c48d4862cac7c840a9d674339e7ca06f6a6171d099cf9389256b754d18ca81d09a2ac2ec38585a6323a3cbc6eb74765bf806b2498e08740566ee9b966b0f4984d36cc8a5719d9cbbce6d8972fd8500c17dce1e689d11a681bd25db4976c9c85c2e79885485a1134de0cc2de1a3fe96f962b962dd18cbd42372660c58aedf3f2dcd1286473b7edb1dc6384c735d8e443d621c3e1e919c8ddc730af197a74776bc886cd66bb0154cfd3a3e2752679fe9119a31434863673c4d314346a057313f442ca7951e39b05d8ee9415d4325242b374817f4344a7a040f343abe33362de5407a644492f4d699801eff98a77bc0011f4f2befbc0632540a74b9536964b8bee8918731c1336b4812d90203550edae4c5900e6c6e91e2a30209bc169a8edaaeeeb5acc6449ec71414fa1d3d5a2b60db5ea73e8f7491e7c7e614274e3484ba25d6f7c8fb8515f2e0fa0fbb5cf369e263031fb51de34730164af6dcb9bc2f31349732c02f71db0a5ada6807796c6e7c04be7deb578822fc10dafacdbfe5c01d171e7f447d8eaa0ab653a55690464677a6112dbfa355cf6c931ab242ac3dc745e3fbb73bf9d5e382718503c490a51e291685dae3394322a554683da9febe5a413474e4726559a4b348ec03ac0b0c5e78ae70c8acba418e07109879c0fa978d75487e93298201b95ab222fb84c7bace0b4838701eb793eaff51214f1eb72a29e9e57d351da303a60474367e640ef41ce266e5d8de39a44496a521da243f2f9b44e42698192c4bd2d590fc4765a50124a2a95b193c864c228084fbb1eba1722e36b3b75ba39626c876e3f4e563386cba11b8a0050092f0770728fd982695d13bfe3ff288890ffa9cf275bfaf17ebf0f5b84e5ffdeae4353ee6adff482be1a63906557732c5f5fa1d817e16b70c69910cf11f6139f30469cb8e0c4e52caafb4dd99c8a6a53f64b345331619f866e29ed3cbc59d3845773bb9e9b216893ea32cbeb81ce2c2429c2312c961d3aa98e9a6fb18e9e8186b1a871f74b332834dafb51b5de7ba290d4a68c81d8e7a01ea792a07297572aa942b9ec7fb81146132b3cae5969810c2a2288e895a30c8a5a52d5ec8c05ed8d3d548be281be7f21e505f080778d59a32318c978f70d362d19aa751543e5aedc648b50983805ebe2872ae2e85ac6cccc7c1e8196751d97d1b23db1f9e8d3b11befe53fa053d5ec009713a985c58e9d580064ea7f46422394b482923a32a1525a4f61f4dbb97fdb732ae8684c90459164dee9f0425370fe77b45526512485d917c7fb25064b32adb35eaf3b1445cb7b0af1898d1707650e5178d766acef24e7fb322318c27e320ca9a4ec68a6424edc2552446362ae72de1441fd1414c00d0a5327a7b6c14de67cb77edf4d6a9c28b4b9128f67d728f0dbedf03d8ceda9a1a2541cc0fa2a320be3919bdb34f954e12f45449528561c9b78578232384d7da37f6961968ec10a60caef73fd6b43030a0ec34d5f0a3d667ab3fa7284b1e2185b8db4acf4ce69677b1da104e4ffe868fb18f258ee3ad91bf26705bd6da2ab205228359af28c8a4e001b48a86dac4fe77169210a713aae090e8b67901c583d83fda57e6ff1a1bec6eaea96eea9ad6f39597f0e964e2540c57d3c993c5f17fa11b8a873e708fd93ff32796604079cb23eae52a7efcbf3ec4cef7c2b432729aeb5c3513e63f1a000bb58bc6e0d7fe79da5df84bc6988675ed621aa1b3afcf7f75edd64355ab2566dd2c4e7c8714b715d18be74e18b789d654f23ceeacd6b9668a84889c88b697f464d440c1f0d5d203ea31f3ba9cd518468a1024aa4b4a7861c8af7ed772e829172271400aa940aebe0b28d190024b9519e4bc20849d33e0606a0c410f7b8a7ba9fe9e3b32eb44970ebd727eea38fea6c76a6eecbd7e918936feea9dfa54a1c1d8cad725d5c5dfe19332d54fc34515202729ac116825d4adcb4d4f22e5afec0fbb7299c10ef02635d585ddf568c105187f717bfe19c9a572c81ebcd2a64d91c347605cbb8bbfc7f6709f82222684a6f1b7621793e8e15af7d922cad4790873296ee18dc8f4db6112aa80c43e377ec7b1e06840819c20f35ebbd550c8d13c5d448c3a3c2f074c0b19d224814e53516559314c9345e05a2a7b81083549655ddd0e2b110c460d782dc70c5ba10a0579397f7f03e92f34a6a3d94b76d0342188803ffa530e75e4451569003fdbcfa59a28d359c745dea3f6095d36b14595c1a9a7ea7d648ee2748e0f8aae3fa5b66204689bb30bb69ca164b748ced786d6dfa5a50d4ae7c1adc62a5007131bd97a32dede7bd1013667ac004189927b2520b17e6ab7da839915b01b527464565b70f86b011b64fda27d522413235c18732fa67c3a36187cb707766b9326cfe87d7e69df9b4f927f1b7b6ee0dd9599f5e6fb1c074deaf43acac52dfb0d271fb6625d2f4ab26e083aefd5a5efff755b22a69509707162726ae00a14ac5b3a9b69c6253a6fe2b4037b1b72d52732bf1b235dc60ff10825221463ea692752672a9e0e38248eaef49c952fae979bf4400dade605290f34b5683b24a027ac3680b83defcb81721e1bfb55ed795ff28836ff01857d2f3d047ae313a0cee0f5f1890845f686195e5b41a70e0cf927d60959a5b0f19237aedaa870ba7a6513567f067c5f77d21210e821dbeff172f7ab94d203ee5cf50202cbc561481b04baa4724437e79e56fecb36882aaed701426cb05e5b343fef082e58e8ec742e21824d893272d8604c1fe60600329aec88ae417d7b40f87545e8c2f056a7741f7cc849c714fb03e89a68637c9d3517656aed8f3ac0ba326b1a6af28626632261006ca9739c4e05382b5b244eaef039cd926db3295a843be1a4c6e06f3a2abf435bc73793f53c48f6ab054195e49370b1bef360634efb47187b24875da534028767d230a9b8240c8bc91dd9bc21e84a68472cc91b5e02099fc17ea71abc1df686ee4af38beed96b71237cba80c1b72c1df8f369762b3cc59057da00544b7699286f19fc2296b0c9ec13526a6055faa46334b09c38c4dc4dba07b26c9ddee9f164d90da8d6d7f13dd9136ce0db577623cc638721dd115ca8c2a020531caae18285899d0bcc1b8794d8c464eed82fb274016d460a32186a138331be5b9801c1b3f0dc772c7288bd6de2310593b9b12c7badaa8afaa330c110ece2f5e319b1e0b8a32dde0c4372edcdfa7cf667c6b8188e949d9b4bbc3a0d06a89aac99054bdef65568ac11018c2df0db2e5feb5b05901f422f146f912bd9c9b4d472bb0152433216b657a49dbcf53f086eb99816d9075318dd95502fd3779657ed8d1f458b01fbc6a4678b39b5e3e1e1452e4a90d1db4b0e8cc1a5aa092f729e5d515ceb97bbd285c661655c0829cfce9ffca3cfec671451fbfe33c44534b995ab03dca30f13665598e5d7ce105cd8f3dc9b93d9fe03fd1837973f627222398d0c3e14f3fed8d75b70fbe492bbbed8aa1ffed11f9fcf9167c110743741adfcb8c583a4d731c76282feb8ad91f0248e1ead0932710f374fb3cba6d03d35f9bebe2e7312cd1b43f1ea31c340b48578eb77b65c25c6797601f0788a9f68f974817b9a2131553b36b805bb931f7d2da9c6c8f56e817540c945ca109e8a2992671da006bcc090b249e43ed9538e694ae9210eccbbae3143dcb3442f1599aa8ea7ad6d735a3f9b4e0fcc81c62cb1b23794fcbfba44ccfc7c269e57525a99b61b079b91c5426e10716919bfab4c0076659d9c3fb5c2e917f0910643e383a22383fceb81b9cec6c5e4de6a31c8a8e05123c28f01aef3f2c097256e265b418c98cdc575c396165e806305fee058a29b46236ab66ad78c3f82c444104b302a04886560f8172e9261428704662cff2eb4246662dab335bdf6735c2608b3b2f6649172c00d1ce448cc68027e8e0311adaccd12d1caead81c4a4331fec1e3ea9aa98abbcd1e886a8201a1aeac0d087565edad817711a6a01501d5913eef3d60c1f4978f531bb2ac9707d39d8256e2ef0e9512bbec987e211fe822f5730a7a657d4cd7d3c4dde98cd485b83b47a5474eae72fdb7ce445724a2ca30057cf038bdecf62335060ab4c7cb2a9cf1882a359cbb5c7ed1d430e244df68487ab837f83a5072b31ce55c31b85ff1174ca45c92249fe3e13c078184fba4cf0e8364482bb55680e3f066bf356492b55103596f5b337a0f05254dd67f1353f96abc4d2a1de2a4bfb08c02814f32d283f478b01de623ee8d6747279e3fe606a6d81b497fb19a633f91cf16008e963878631f3d60dac06790e801040d3883831e5c093000387a083260f024df79c0bac013281ff9f7a7f2244fadae3422a7ffe2874afd80f780fd935b37981801a89130449484a01fea07906a3e11cadfcae0dca2e7e6f968b48e34e2a74fa7d90e3091f7e18ce1babcf5c3179de1b2eaca6a406addb69a1ec1040456a54fb540a8b874e385f976c42d5d3b91364323f2b37d3b0aacf8b63f7ee885373eac73c27115ab45cfe92fae47dd0fc039bf6ee87802ad1a7f9ce97ff01e33b3bb3bfe28e459eefa7ff48e5fda22f1b3c1bd4e83790a09d2e5076cd9ff3c5860ba6cc549231b0720410e969b9fd1f8f0be037435c81316bd93da17ce1eb9a8ef2951a9252711529a0f993ab2ed8e24194947d46787e7a582919b944da8670f62ce8f9f92a3432d1900ffe3aaab1aea8c09ea7413dc0e4fb7609bfa44f25e935530d8b7f928ac24254b39faf903c2206980cecc1b1c13bd679549a3d06174d27657cdb020d229503b822da2a799a698393b26a3d7a2690bf06ad3aa3e26ee21f84341049598f894cffeac4e38ba7422d72f17e3b030f4208e331481c9743b48ce4dd70f82aedf2c525048850c6a0e81ac4819a9200d9fd276214f2853e55045834d1781328f8bea346b477e31f97471b2b8cc3f41e409f440ff602c8923f337a2938cd806944c448182ae8f7ddbecf5ceb77740c8f9ece4ed2a3ddab711891014a09a6b2410088a7e79317be2c78b836797e864e397cb4e2eee8f3ed11ab3d971ec27552b78555053288db27596da06d39dee5ab497078c0f5a827c043e478fa97bf22700cba544d01b0a2315724207c3252259fb3546a585903336799764847a0000b26b805317a276ca2ceb891289a396f209de9f57070ea2071a935583c55a0fb47c9b0d95104ae6cad326c3f40866ea36d73d3fd651bf7f2ba189d9409572a9451d11b26be8d573b967d3ed1565105d7e008187388d448821583fca8bacb5f15ce7a0ed46f0ab0354f46790995c2939c9a48abfdf9211e50f324b8bb4186f893415e036e834412a18d12800d6e8812f930da3e8957ef13a9368ec1c0db6e7c8fbee851da23caa1fe560174a3d016549c8ac8b94e760ec8e978e0172a60a0b9d0e208b605edd2defc029fdd85d50ea53240827ece61d8613435524692056ee4398e070a21cd502601d26eb638a0cebd6c5b5d0e303e821aa405c21924ab93c1d9badb33d0bc0d1066893ef34bca250de8a3c45b1d50c55f4e39f83bbdf3eddcc3a4a9eab3751a93bab4661927c11daa4e7765d993e489785e1281a6c09dbeef30c6e5a00e9fe50858e577ebae07f7dba17263f3cb6c70bfbdb81fa26d817149973ae4781d47b78bbad680729fb411dda398445b1b83c0222b628d1a8ed24d4a70954fd147312c5cad981d4e94c6c38f9dda86e904164c36268f6f4fb267237609e53f36abd15a101b23e09576db946ec461c52a634c90f305aa31888094e43b42b803a603af4f15fb303882d43ed464fea417e457833b37a2365e652e96abbfc2a66b776387e3dd7f783107e40c45897677402cbadedfae68fc677905b4ff9d694e14d939af7b41df850e8d787f2d29695f4d22bbc8dbafa9bad9f57b0beda0d9ac6e95e58064afd8fc5bbf08d121cd487b5281154613eb04d84f4e83860904d73d2a67a28e41313729a145aa8dc3ff4b4d62cec0c46628054c1d524ef048dbc070fad7f3929f0e095e2ebfa744c87fff189aa970b0e48dfe0553acfc99931259dbcf4ed47e83c7981340b54864353f53ef366f1ca36414d57d5c9b2d506c77389dcc35ad87079c6d751fcbcb6f96e0248feb967b73cb6dedab11421edd478dfafc9d5ed6c02c6aebb5b998d1b01aa0084e0913058ada67de1506c351bef04890a4430cf2b33c09cfd3cc6801071391dca0c81d09ff78059fcd0301943785f0da9ac8ad1accccb28d912e6d097e204fffa7ba4ad04aeef046a10521050abb80dc4ebf0d7a801ae803be0163831eec07bf02bbcc21eac32558849cea8f1d57cd9be6fb4fd7f1b3333336b69a0e910cf3dd6a13806a10f9f2889322993ec0bc4a0dd3f34e76036738c07fed168a74b115312560f6216eaac54b23629a6846ab2cc3945318754934d35a3f861d129bfc94f45430789a60b538d16a241d58865469375164d36a7a986f4fe1399634e1211091061b26a95a52923b5900f9120a258ee4087911dba98a0de21f50161c972483b64938749d2818e4c8711d8f87185448706cc39a7988962d9857c088ae2f6bc1fefc77f12f2610e75e490811c8072980107367008639a5019f9d5a068b2d2280af9b005aec471b5d2590d0af599465f82fa8b3754e08624483704818421570cf948436cc8d0c84a40ca3290a1b21e6cf001c9862c6c186d30d55004520d2398d3a4229124c9c43013c9f0039f6436a11622834651283ba17ac8027549b29244e91e0435ea24e4c353287a5ef82423339b2c33b136ea146a7e42653659e64531b1b2a110d43626ef938d7e259640cdbff21b79c9da2a16d0a86d4ca38842958444908869f47e5c820a49352c51439569fa79f2248a8ec9f39d69120d2ba0a1a4010911844440888e901966a863061dcca0801934197420c30164483271c83253498e1f4964f4244bfb31abf1acb1a4c932b1acf124a83f0da9533f03417d0ab50745d1c6c764e2a5cf4a2572f4ad50ef951f57a5ee7509fe6426bf4b3528cf1a4b54167a2ca3f7270f6e9d8d5f19facca7ca1892a0450c108821c93481baebccc6b42a65a4ce5a60c9f59092207390828880140403417e4c3880aa326532659ea90cb550c6125b3a749d29c8eb2b2814113e2419c445279cfc54de8f3ff44788e33f79fffbf781a7130e939b2111094edf29cd94b292e7f590258e3837dc4f3991be14b1c010635a7d29f285385a3ee80528e6349524112c1fd8f24d5ea8e2474d64925ea09926b1c705d285ec820aa416c420b52005a9051c1682c08214241650b344fa552bfcb29a92cc6ac451932c9a2c4bb2937d7efbac80c60a1920adb0c40a3ba4157020c141028344428274492328cc39bd4e6527c96c50a8ac84fa4f4a288fecda47d949321b530d0dd9bf53cb0735f1bc1f5496939950355527b33171ffbf2f3d6942912d1f84f2f901f5288ea34f913a25fe7027451a3159693fe69046d99c73864f4673aae0c69c5a2887a4421773ce99a382933927d0c8ca5268c20f298530a689933ad53ffeb17c4829ac262905164840dc98739a50a3388a3a28259e0404042420dd84d3f2417763e1b47c931beea780e0b7470fa5e59bf82e72d20f05fce0804cd28f22241f3820f95800c947696a8d1e4b8dc7f29161467e19caf3c4efb422513419aac687e6092af3c4f2146a8ecafe10ca07e593f190917c60332020cd8c2cceb1882a3f8ebe87ac8c6cf9a0cd2245314a8b4a56a44891222610d43593347333e72c3f9527c9c8419279424609999124230449c6448aa123338120ebfbb1a4f2d5ccc65483aad1a17982aa990d4a0795f9b1460725f64cc89362ae9873a57d2a238964ad9ceda9f890628a803227aaa6d23c416536261fa4794831347396de871403825f1318114c538e27750a8a27756aa584e424392af1bc1f9cbef373eaa0487ac163ce498495cc14248a39a4972a7cc8227b68757fc5cae8c5ce2289b092f9d40fe96535857c3824220141cd717e6efe13277d27255403928b1a2e43d3d4f2416267d5a0847c88a2c9fa4e4e49725181d47282d9c2915a7ccc399b4c92880cd3e8c3efdb985a24ab6b5f922149c449224b12cd30e70c3249221109cb83841d81848d41c2c639716e321c4c94d212ad190fa0e6282cdfe83b95ccc6544393214992f9f0496663ca6c4c3595e64966837393e1603a9d32bf51a5284629439f2a5939bd64ad52a46682f333e47b7885da2b56cb7ac82c22481714f389e7a978950d5b263899459a08204d3ae7244bffe598ca504ca5f628630f720f2157ac64253de0d0c38c5219fa1c3d1e30d22375c54a89082ba6951ecb2cd43e7a1489c213a2908139e7152b19ff4aad124b960ea3108028c4e401826942d564a5be93955024169d62f51e5020a8755099ce230079e840c10e285431e7152ba5505c6db11c351428d9431f648e9ea084279831b2c44c2c33d5139278c2ce136609b26a7c388a9b262bbf2f966198337242144ea8e2849826709d954a90d5024bf26b420b4c808309633021354b6278f241ba4465a9f2d34cc06109692c61689a4a9698759f2a47312bf54c03859fd64b38a2043aae58c94a52e45989082b26926465e44ad417500294690241cd4725fce841129e98f38a95d290ef2552a7422d44127e126ee6152b254b0b21c18d11126680042490407304378e708023d05922c28ac96fa02d8eab8cfc428f65f465a8c3d1631181329eccd47d8b6489a3070a752f59624b2c575dfb91c6d43dd91a7b0b0cb3adc4087918218c79c54a49af3222ac986a502a5ea587de07e5cb2fd2f82546608011accc79c54ad6bf8c082ba61e6a23d080871e787471c54a564ab293652522ac88a416d2a99c111e554cdd977e5c811b8f203f77e031a76914ff1d66984677ace69c367ebc23235924f9d99104fd324776e8c08e25e69c2d2d94896189d442dd8f7658b1e387a9a68fab21ffd114618d220ccdc9f52a886b30d3e44aa7460f2533953540e157b2c491d44245c8e61c3dc92282195e358a65fff4ea87085098825499289644404d133922428c2914b314f90965ad1a10d4345928248eea08a3e5533f750c4d2275d039e7e8b17014ea67348426783e840ccc39fdce7270704050eb781e0ed9f2c9489dfddc588d85738383534373a3e255ca4ff539a172332422c1217dc7b9191291d081003a78a0e385209011041f04228210030436e69ca6225e2592453cd802492de48b94625884fc40d627c91c93ef5a258641baf4a992fc7446961f0ad5636a81a01e450fae541b0544aa505f98632a652550671e08f8076cfc80033fb032f528fe394030879569d220a84bdd93de7f1f00c1075598c896d659109933e714628e7a80821e88a007bc073a2339dc90630c39aec80113c716710c11471438f280630b38a0c0f143a7f8c85af1e85469853ab3f1a5a8f2a3ff59d6d22b952f6d4c2dcd9399bcff2d5e7a1b93d5429d657e0f65399987426536269a5a0555536332d164251a5b05056afead5a6506821f95cc9479a892f41d95d998429d7d71a5b92988fcc6acd421f9a1502dbdcac472c5ca4a28952fbf93f71f95a148dfb52a233fef979854220f20c003cd83283c98d34466a8ccc6d47d273f1e9d2ad91753402c714c95202896434a767081d10ea6ec60c9e88d244c9317ea590b14cb0c95d998481e9d2a592bd4a306f26346b2460f148a6026863aca1b0378a3bfd1476f6c3a88639a3cd46e69152ab33195a2188527f350a8cc669a6a582b9fe932f45c546d1e9d2a345f933e735e13c8020b98560ed898b35cad50e468224fa35ff1aff42a293ef22b353972adca7a400d69550ed94d5633a4553918318206fc3ccf8c6446a00a8023e4c38bce3957f099231c3c31e775454c67aa36dc630141ad228374984c1d095c56547141c18d3426399a52e5376a23e44aaca179c024322746851b39cce94b0f8ae47e9222359392873b5d7b261f8f8fd27d2865a70d2b6d4469c36b4348769389e50d8a708313dcc00266d0cc39331f7032bfb35267a3f8bbf6291028fc4813ca06557e244a95851f948eaaa11982d2a35ea1b21a3ca1066f645e0d5c500330e69c9958669d88f628d2db649ef8c32f33912a9f2a4b518c52922c9d1a52a24668ca863c100cce68d2a427ca122346697431e71cd2c572fc49c346066bcc394d28213144a16ac6032af3a38df69d24c914d0c744147fbaef2c907b9087523af8a47b9f09f95024c9d2a7bbb82250c54c959f90175ab1b21efaac2432af2e747afc08250641260cf280811b33cb4aa7166b7b1e29650366a8604e266d94e1634e151f3a838c9979641f4725a82c8ce281469dd2a1d66a423a4adfd166c494f18359861265a45e9082c943d6a9f46403e69c45ccd10bb28ad4425c7f9b9538863e48973594247001d102151796d429658e5c40e79cd4c45513b52945719c28c05060020f30e1472a5e60e042c09c264f8a20cf864ad2b380322a323326321bb3522f91993191e5a752895c0b6540e117fe0cd41acb15801d26cfd5a570b1308436462b18f207003ffd95d09103155c418e9701848694b8b2cc79158000735e03b89098f33a62ce6ba835e735e43b1f59a20ffac08c0c124527645c31c61373f664e49791410032463292cc999158b2cc4b913a9571bf22cbcca7c82042868c8d4ffd8c61658c2a63e499797ed442be271b928965968d81c318590a12b0b3f2be89f8f38d65e8b350a756a398896519ea920cc592a5798ab35419a95be04af5913619e9cb2ffcbeef3623d0b81abd07b5494814431e3e29bf5168c52a81a0fe7c92cc94a1c85004750a95b540200dfe4cc5ea7ef4bc16d7231755be67266ffa684823259895dc8f5fcfc470fccaaf04754a67265067395092f4e44401e2cb4ce1d7c727fe7fd6ce723213e9f74a8f25598ae216c7158b0c7d2696a1e87b467ee42786590945a232b2fc52190a4592e0939ee2a556911f4f298a514a9907b246282c88800e13b912c34f8f493a600211586024029a39a7cc4c288a640f75284e6965610e1ebd5cc00c241230918c2ee0801bc44f140f825f1321dfd37d177540d085c7028224e69cd3074ac63e3e3a2b83b42a134b2f6e9d098961502892da47c94cdcafc85639fa9538aeb21b3226c91bb806577ecc32f6f1c9ba277d37110122db494c3e48c4127ee4d6a5ee5f4c919a49498e1f6b5c6525e93b28d2a8321e27415c044d24e9913b649929b5ca7c0ae5c7520cb3ff653534992741bd2433a1acc642a142bf5ae99457e91055922298f91d66a4c8bff07fa89a92e7fdd0a052a41f7de9fdffa28afc429de29ef4583cf881dca64c017d60460a8920a8f957921f08e6dcf82fea9a14a99978de8f1f6932feadc432d33c99a9243f3043e1fcdca0545dfb3114bd4a248bf8a24dc9123311282bc570484492996cc22fd3534428e427148a650f59a91f32339556295233c94c28944f66da99a94697ba8b2549845c69154d9699529aa704249659c95a81ba86f4656b67e1479365e24f664ab2c33f0d82acafd23ddcaf7c76c41fb27bdfc3c51f95a89324147b4050ebb8a02e90438ad429cfea624f03be687d51c417a92f7ebe00e28b23a32fb82f6cbe28cd3957224d6624abd1212be990792b312be94093655e76858839a7688ebe08e2851e33f36a68b4a7248af8c3d18ffe88930a5b9060cee9433109992376964fcb37e95fefa2085db460ce2976960fa94f6267899d45768144ec2c1285123bcb270a399a30003367e6b546b1d43dd4aaac6b24a753f9a9368b3c959e3c8d7e8850ec09e23e35e2028a2f7211665cfcb8c011e69ca64c546540a18fd859e4162b258440f0fba71549962cd177b1f4e4d0f7fdb83a81e017740a1ab5ca8f39243094304919686192322025ca9c36ccb9c384a1237d519aa42f7426e90b2a26e98b3526e98b3b26098c1826098c1f26098c004c1218ac4902e38a4902c3063f5a38028f393a228f394202c81c2111c31c21b1cd111250e60889297384441173ce1758f83167e61c1d39cdd111d61c1d79628e8e4c608e8e7c608e8e8c314747d498a3233898a32371ccd1113c56f831a4658e86b03047437498a3213cccd1106ecef98305216b2099a3357ae6688d3f476bb4e6688d26e6680d09ccd11a62cc397fac20838c1850e6480c2b7324863847622431476234608ec4f0628ec410c11c8931c61c8991c61c8921470b3f4668b4e6088d24e6080d2ae6080d2de6080d0dcc111a259823346030e7540186155c98028d399a420773340510e6688a28cc11152f7344459039a2828739a2e2c80b0c435831474358608e86f0628e8610c11c0d218339e70f16640083ce11183773044693390223007304061273040615734e971f241f73024961cee9b2820f16cc911333e6c8091b73e4840773e4c40873e4240a73c40333473c2ccc114f0e73c463e788c78739e201628e787ee68847cf118f38473ce51cf13c31473c59cc110f1773c4d381980be829f381253401e4152144e79c33a6093a641ca185384afd331f8a3f3b5020480d4e988004e64aab261525d86192451d53489b62a7a8f89076d2832c6e2e511e734e2c5ae0b158821cb180d20180450b73f630b1583caf1403a408bc117ee2ea14811144e0a78a39aa380327456a263740644b83be8742620745919fc6d449c50a87b4aa8b56634180068ac5e27933901e70e45be99312365663510186cf1641245d898a57f1a5e6a1a8d3773ccfeb54608a2b301da6c8530c9962ce39331f6eb2cc43a1b292d42b2c5d931c304492a209734e92142398132b32b11a9a69a78439758a90ef21454184190501a228490f04c422fdaa55923ad5434ff464a7d0133dd529a4273598c3c98fc7fb252f198a24734eff331419b24e588250e0d6a1ff469b960f9a5c355d13b501124122980e930485901ca00032e7147fd80257ab9c1fa0f01b4b9f92eb95d55838a7d1af86b4ea6648ab3ca9378ed55837435a05ea1f0614997392a406c905b47c3da04ea37842a5c82ffc7e86841c4d64f9a9b06cce796136fc7087a7a5a1901e00624fd77e245b1a8af7ff44b6f48a2435c89d7002e7c412e468ea3d38d142781a7df99d54e2f88d708f45015050c00e399a40ed25c0679a4c2622a50749d063f1408fc583de9c58101826a9532952a79a8062ce72145364268aa5e7c9d163f123f6820bde8f4bc8d00789a507484ca860f6d0b3c24f95e1643df4293d969b459298e048071863cef0d32926383837a02e43f1886f14533827cff3df589e56e4c903912a5deacec46aac1e0350993349f7414facc6d2e05659a028ee53cd92ac94c410529623e6f43835393556637123542723547d16200a1201f2201160015dfb281df4f1477c3ee1ce4a3532d151b2528d4ce6c45cb09693e79d7c8a700116deb0c10f1510c20fcee802073744b8f052809810cc9042a0461499098ec042248840c8017c30021d54388093080c810288a5020b1080c0e608411e10240d1b7891840034f851800f3c7811c24802c8818444063ce988302e3de6bca2703de1ca63ce0b0a4f20b59620b5a09086e6200d91d184392f262ce10ed2500e24a10f4c2125744f122a616163ce39844c021496600ae27a8b63e6b7d8b3030b09a6d293a0d6a54e72eaa1f7401a4a49c7758410e803b53fe2234b96d8d24e383cb2a0268c0a66b6f8704d98e942ba60a60a97cb0b570a578c6de1a6688a961a60b098189716d1754d1830d3c4e62544cb0fd784e9c16562f3ba2e11c6e4ba602eecba442f47601776c1602f44b9ae970b6662171093087b79a199c9ae160c6b81e26ab92e6ce6520276c94c97ab0513f9eb2a02bb2e2c460a360376612f2dd85507765d2fd825c405c37589b01816b8886052b85eb8aeebc23acc8518d14581eb05bb2e4c74bab09619998bfdb870622e971c443e5c3018d65245331876d5a8b05d48b4541f11c066cbc42e70b588685a5cb04b345da8b87a704d6c8e46a379cdabe5c22e3bae18acc875612e1826ba2eed3272c55c3f300cc35e742e2218d6825d5464f21143cc0e17901564c02070b960315c2d97e89a18142c9bb95a26f6bdcc0c752d403487b0e09a475e8089ae97ab05e6027289aac0b0184f7465d185612d19f64386e672b92e18da526da01736f312835dd775c15c977761980e442f970cd622c272dd80b1ae4b6473c9f8c0ae18980b3b82d580d580d1209aa279a970bd60d805a465246ac162c030ec7ab95afe2593c3e5c305bb5cae14b819b0205e7cc860d78ef901835d425c31d725c2ae968bc94503d66142301f180ce672892eecc22eefa201f381c1b45c2217d1757197e8a2b964ae1fd775f570d580cd8860ae98960bbb2eefba5e5ec05ab85ab0cba5e5ea301f9708abe1128930ac056bb93a0cbb64c0662e992ba605bbaecbbb68c0666044574b4ccb755d5c760529127323e2c1e5125d32574b904b46c6f341c40c355c38b45c18f682c1f86be402f5c1e4bab00bbbaecb66c2ccd5a4b2c7850795706653e4a0638d295af4600352b41cd4478e386694213998426831032f84566342d1e204584019223e7922471178381952031280904308edb40862a4d32980c1c90124076ec8ccb5c971456143ed91c596e9314d2828d46083ea022bc5c18558ca68111e2850bac30654f6c3860c8615260558b832512e51ccacc500f33202ce9c338a112e2f4bcce810832c323031312f33332f2e3e4434c8c0885c442efcf849a2e786189a979998dcf24385abe5070c007e942220a2918b1b06b022cb4b0a188c4c4b09080d79035966a6fce82da51492c0ee0c2e525aae979b1564f1c149129201b096991627354cc812c712978b47b2e14812588b0c221b1b64e1418e2c364059c20515901c400444124c64894186ca8f16acc8b2c485550c39b3842c37a50c10282946303222202e412d09782289981692c8464b2208159029728325629a008114301f2e4748305d4b134f44184ab0e04233b3646687172b2d45603a2548a005104acca001469600c29d28a005597cb0be040c00c4aa70899125862d891998ee6526260511b70384888699253049ec00f189613283922533324c7ef8f41b126481a1a5b4cdbc782c007999f1a1e486007091652686490ddb892c2ea291cbf522c4cb8c4b0f2f45727ef0c1922112c3e4650626062049647268e99181a105213bc8f04291189a9e283b2431136382d18068dc0e14981f2d3032b878a24de6470b91ec088c0ccc4c8b1197262df9c8e532836888cb0b5426868b3a253d0489997941864b42a7c7c78da169c9a12587972fb21449a125890b0f2e35c8e4d02244852aa241892c262397e88b6c5a442e48dc10000788210736b006196170e08b2d145080010c9141df735400ac0c51658a141f284e8260e2658d66071d70c85a5ee0823dee2042107ec0d1e106196280a1800cd0c000174228f9c1a3c1851f3e5e5a70821268a00b0c6c61012d288085141a22464c12b0628f3c92b087b8c38e314ea2140df83e4e0f33305d44010527830337bce8c2eb36185af0311303236406314460c5f7273d41ec2c5192050312c0c40a7160147060035f1480ca146e0bd242cb0cd640c2141c1f361998970e20a0041ea8c0031ce0041213c8228b063ce9c1e981c88f0b0ad813b026b4e091d981d1d112044c0e9738ae1c606d603790b1c10f36b019606a60271071000b6a29e2ca21860617214062c060c05cb85a20b1e0b20246ba462d29603e5c66309998180c06c6056bb944d7f53281a8209a4194822c2d0bc8d2e2e4a54876c12821891117595c5c7ec0ec9083245ab4a82d30fcd8028c170564697122f3c28c902006c822038096214435f8285f7ec42c89c92f33a21e82ccb8c4204bcc08b2b4602509194e84c5117ec091c5658f2c2e3f64b841961fe50d29c8221324050ca60717232e5334e1828f1820a044cb13595418a2c210910dc9484c10d21e4ab8f01093616e442a886e62b6c822e2a9465e665e329045e4e3458696928f2aba5a7eb4946696b8f000038017195e665e8af8d1c28f166482c8bc0003005114947891411486122e6664d1a188165576d2818616ef870a2fa4964b6423d2698102e485170c860acc0c303d2e2ea470bdf810b9b4fc1089449808461423929911d222829102b3820a506648302f309708a6e5b61869b9626210cd90848c0d2e606449210349905e5a668849a2440b1c4adc70cae61e7a0401076ecc608d33cc6001f944018512243ff8c089e11d761821325221068688219ef4ec91061b34b0a101b40a081cb1028e36d428630c3138800123a61d1ec02a1b408e4a30021e6fd8800d1784a0000408809521aa3ce90162890d35c0f0c2094c50000e6c80000338d120440c6d48010e1e18a1cab2871e4c58821522607881d446192f50c1186288a0030d6040024e01000095273d3b6aa4f10219a0a00a263ac8811a6994e1823152c0c5164e1cc00046c0a30d1b8c9182137860892cbeef282101c1e30773c071860b4c5002107811c5027c3f05c00a11547694f4e0d59080a891c6195848208a257c0f40103b4b94f4e0a500a465fe600e35d280c10b4c50021074c00b2918b0c49520767ce8c1e36ead5981d432f7f8c11c3c80c1192f7081094ad0012fa48882010b58a207a72b567e3062ca030a4e9aa890c20e8440f1841355a8d00a2cc089d6900664f610e98145c1050ad81362c068f902f302c3c065b12aa297c9878d880751f6820d98906b06181944315c415a60b85e906161851544a3161580a4300222c38f187c089921c9c0c4bcc0602f2f2e2ea24b86a03ec36f5c19f15de45fc82ac0cb2405ed105443114d406534590fad51eb14cff400c422b828a202454c3127ea03c99acc879b8cc6879b6c06154153844f114ce69c656b5f31994c44ec30992f327c6e84a32f4bbd3232961f155f961a2b5f94ef6666a52e7546663bdf8f6cc8777f536f4c72341949612045c59cf30347cf02424c0c0e2928f3c263ceeb8e392f3b5a5ce6bc7e0094c39cb3ab194e368e2c5ea4eb72c7755b87bbdb699ded6a47bb2ee7cce52de37cb3966dae99e68ecb1cc76d1ce62ea77196ab1ce5ba2d6fdcb66d78bb9bb6d9ad6e74eb70c61cde30c6176bd8e28a29ee6ebedcdd2ebef76ad7de7ae9edb4ac71daa661ed6a9a66b5aa51adb3d97276b3d85eab596baba5b6abb97275abb8deaa555b6ba5b5a3997274a3985eaa514b2ba5748892a349c50a8d94aa7f85889912cd0ccd00b2016448645969a634834496655936e75584392f22cc79d521841f47e69d00f580af803ff44f2854e6cd9450a8cc2bbf07b36c06559365e5f7200dca071f7c1842fa142fb56ace6b084220fd1a9aac448448d09f737a7e071e0533233f0f7a244bcc583ca3388e4a90907c15739a4c244f002fe49dcc9aece626ab34d9171d8fde82a0f01b53e437c7ae2f1ecc79ed40a40451107813735e6fe8605e3998f3c2c1d5c665836ac3c69c570de6bc6830e7143bcb274a1415af52ba19c525339ee77927d2f753e87bff52a76fa54f2b92c4b1387567863c799ee7e1f00fdc2c28ab55ce8fe7e590913ad3232be7468f2c5d865aa8eff404815353b21a8b06d42a1d8e46949e247d10ffca9d1e7e4dfcaa559e48efbfc7f201a9f81039a4553837dc4fb1586e54bc8ae76958ba3742d54f55825071c34465684e4d258b2595adb3296e90a6343125cf3981c20ffc524253629062c6fc84c82fdb22b8572429ac51ca9139a7d82365ce696a815f26f6fc98314d3f41734eb22457be8bdca70d9f06f81041f2e9a2088114e50c9357899c55ea39a569f59551a888f2830925a684505fc9fb9f9550de7f544e0fc53da45542ac214496690277cda6e91e8b4ed5681a2198d4d06450e080d20414254ff098d3844265628922c38f07959125ca148a99c7e29fb460ce9d71d2139d39e769927ae6e8a1c09ca6f1d3a9520b8d40dd67a5ce31f991059402fa52ac91fcfe8ea9862c693232fbe2a987e2eea19942fe67a2f8d313c343078f1973923c61cc39799ae0a932270f4d929d8c67e49dd031b35093529c28c0499ea8955892591334e63481a526438258238856105b10d74e1c1395d9802a5e65a555280f85ca6c7874aa90a49d91b423c36404734e13aa665fccc94ca1f88dbe24f51fbd90086639a614d7e2b85aad48263f4c704840b831274a2cb30e8a2510241099b4840d944a256a55a975f97156f7236b49124bec929639754a9668c4c4a353858c4252220549c9968408739a502db0fcf807ae867cd6552290a915ea244c2429220914924e1d73a24cbad449505e66631a7daa1c597f258efd8ba3388ebe7b9255ea53197a1227456a262952a77452734e1d283a344162c6a35305091e2632d3610e9235900c21a982249bd3c4cac21c24319347a70a29878e39a7a96b243964e40ccd692aa1e4e8393512cfe31e8be7f5923cadc82357cc39a77804892339cc394da41ff2f8a18b69020a7dea872c3fe09021f48393659a80329c2a262070684c64141f9a30a7292bd1e0f41dd207213767dcb06eb49b96d6f61b85ca7ceaeb91b74470e57b56f2fcb341a1505f14572bd60f96cc873e40e1d777beee49d6a907d49c26520b917a1801859fb786e7e58e0211e87eba9aee86ac83ac453622f34c53f824f350240b9579285466e3912dadf228211f7adee8572cb2fcc20f0767272bfddc8cfe66484472e2565c906d05dbb8d1845ff6774c3d144b531f45ce75a6a7600fe02b18e60e8174abb8248d0e5348ea154b6c859afc74a955a36f815c8fabb1ccb449480c83bed293df1e75d60a75df3c6c9199586a249a57b14a5033d1a86848e66ced96e631a15099e7d9384c3fb68a394d366522d96cce69eae127f453c99833eba09891750a53bd348e9ade47dd435d9236a11892da6b9a6514bf4f46a247e687ca3c75619a3a1671751ad22a5dea24da87a72bd9c813261e9d2a24239f913c49a6264c13cbb406c954c59cb30c3d273f53ca04c5449a68e6678a99739a48367178decf0df753847c68a3058e4ae4f1bc1f21312cb1a0f4811211251572c0030c783882072545f02015d18109557af294d5f8205d7a1e2afb224d5655357ea34aef133e41699a0cb58342a15a5bdc3ac5bf50afb21d1e8a3eeb3f38387d272bf96425262952f7f8e0f464252637abbee3fdf8c552258e9f54848654c3849a2ee69c269214c1ac6405b66adfdadfe5aae910f0cd7134637bffbed9f70e04bb777eebbd75b65f7bb59bf23cb13cfd707da06e9a6e94ebdeadd67fd61ea85fcbb8ee4d53ad51253757072aad56f7ccd9ce6d9e622de5799ea744e70a836eaee395cbdb725cedc639a06dbbedeedb5db98fb9d61bb8fb6f1adbebbbce5eeb39313c690073db5afeb9cc59ce71fd3340ed563bf65ba6fe579d3f189a6edce7acee79eb74e6fe179aabd55eacbdbf1bddb4ef05ce9877cef5aae19eabf65d6c98bb9dd332bddf6a3b770c68f963fbb56eeb74c5b8762e2add38ef3fd5eaf75f6f7e018c37e5b8bf69d47e8c37bec5a6f5aef7cad58df37f31b7c0d65d6e739b2dcff76b7e57a0eb2eebde759b7a4efb7a6b517be7b5ed9a739fe3aa3705b65b7fd657cbfb6a8ff39e40d5b96e7a6bdd2eed3eb7b3d0b4ab765b6ca9e5baee6b2c346f2fd7f7e6aff0f9d79d3fd6b8be344be06bbbe66f6bd5fedfb65b91b7e51ed75af3a774e311f81add34bdb5e53cd78dabe27fad6d5e536d77f6ff0d029d6bade6db6d1df65ad5f40376d5eeffdad7f562cf3d15b47fce67df75ade31ae36e8afa3de73a5df3ee38ef9c03feadbac31aee997b9e372934dedd72dffd4a399d6d8ea277f57675f3dae5b6963b14da7e8eb9cd7178fb5b3780fb8cabc6edd6696cb58d01dcce9be2bb396e73bf29b7000ed3cde6cbb5dd32d5fc139ad5b76bddf5fab5ca39b1efa59cfd1977faf38d5380e678dbeef75b7735ec13a055bf754d6f1b676dc75b139e6b4afbcffe7a8db5dd98e8d7775dad795b5f39ef08d836ad55cbb9b61ac6b9e203d0bbb75e3b8dd6ff6be7ef12b5ebbefb1ae6b6a65fc3dc0074735dc3f9666e2fc6fd2ad1fddfefdd2857b1a79527716da759bd374e57def14dbbb250ca6beefc77effb963b5b007bad862bb7f9fe33dd6c2500ce146f9db59bffffda0f00d3df713fffaff99c711780ff96eb32fdb86b4d7f45c2d39eabb6bed28efb8ed294e7851e3c718fe5e479dc3b9d728ea01feb9cb97cef667dc6ba7535ca519ef7d55c77b156539ee779382ad50e16c650deace572ed3aeab9ee6f5ea8bb35f7fee94675eae457ad9f1260e95af75d77be7dacb5e53ae579588246c0fa9dcf95e399df5b69e771caf3b0049dbef6e00907674a13ec8b52e7ba69556bcdb77c31dde4d537578d53ce51bed9afc70e77b6fbd4ea4ed3bae734236ebfde675e39c7b6a394e2803a1c9f5c6fac6cf6db63dbe9b6bb7bbaa53ccf0375df458f6b87bbcffd6eb7cfbaa73c0f28fc8238d0e855a890d3fb6afdf7ef1d776b69caf38850e96ca9f7bdc3dfd68f3b9af24ea0f625790a0259dbf370a6949adc0c01d2cd76bd7ecbf7de78fb41f56f7ef966354733a7b7ad887b35aeedd516d38ed2ff5d7e2bfd9c5fcf3f976baaf7ef7dbd5de53b779562a0acf95a37bde9cdbbd7abfddbe1cabbef946b1bbf9ce7dae631e6f86e8a39bd36e57940e1478e2ceec793e7e178a0ae04e76befa434a5d4a454b2783c6ff441a3567d1fc577b18789e7f92e869ee779d8127d5bdfb79a2ddfb4ea79e63ddcf3bf6dbcd24efb387bad0bdafffabfabb6755f6bf4627ee34463bc55ab79772bf7b5eda988da54abdba7bff24ddb2df35387b76fbfed2aed9ede6d03f031f5fc6abfdbb6aabbaaaff0bb596ee3b9e75c7ba5db8afe5b775bffca5ddb33af4e10d17bd6976a9aeaebf1e5b0f64c0e3004ddf0d6372d67ac379cf9650000789c35ef3bedbcfe7bffaedf44a9c25daf594fedb62fee9f6e2a5fe3baad72ba75dd615af514ad5d5bb9ed9de338eb8ba56878c379e3ee71de7be7fa83b5cb6fd7b0dfeeedfae53e18e38e5ace697d3fe6328d627757b7e69dd51de76c7785e8344a3b4d77ab658bbbbc41e932fe155bee638bfbd71cd013aeeb68ae95578eafef547b1e29f6f09d35df388777d5f6deb694e7b57cd049ab2e8f66adb595ebb6b33eefee9dd08dee0dffeee79b35c7d526fefe4eb7bad99c35cbe51f04e6ece63b677ff59db3bdeff0bf6dcf58db5fc3db6acce4ea6b75fd5cfb94eb6b3110bae2ed625e6fa54bbaddafe6d8d7bd71b8c35d0d92520ecd14259a7fbfe15ab5ddbc63fa4f6292aed5ebedf5ddeb8abbbd35d1d1b86cbbb637dfba8b79f5291c20d6f6493cefc60749d75ebb5bbeb5c3bcbbfbe75c5abfa79f73d9db7aad4e791e0e0e8e58965fb9a312c725dcaf7c3cefe65ae248d6f76e9ad5fcd36c79e6a955abf43c1c1cf18719b8527d379e479ef80f747738736ecb5a47ebb51e07731dd7efa6b5ad55fb2de743adb46a9dbb5a5bee668d1eb9f1ffef9f3df7b196f5cdd9d483c6e1ccafafbf7376d3ae7ee08accf1e8e795d39972ad615dab4e79dec9f33c165185d361babb7d35deb7dfbf559cf2bc535689e209683c9d62b8ad79bbd15ebddfbadcd59bf2bcd3ddbcce9c6b5be35857bf3b9fd2f0c69cdf56e3baaa39a69c4b799e90380299eec5b4dabc3bce739de5e84d79de58929aa671fd6b356b1cfefc679ff22cee3a4ef39fb54dd37ad639e579a71eea93d83dafa77eaf654e679ffbae9cdb5acaf352246b751ac57ff23c9c9c92908ee2794768dee8c6d16da9df72c59a4ef193e7e1785ea9fa54b292a5dae1a6072395d3bad7b0cf7ffbdcce34e5793899b5c38de71a5c9dc2cff3c672454d17e30ed76eebbcf17c3f4f795e397a9ee7f960e3ebd6b69d77b7f9c65d8da7c493e779f8245ce2799e47aec4aec437cef5ce6d5a77a5d77329cfeb3ee481ebbace77dbd37d37aa2f4f791ed9bd274fa507f23c9c8cd43e513c4f25f6508f297d43163991acf17492a1e6069a5fb288d4934f9d84ec209e366c8be830ea92480e31e020c30d415c6f10148b0c9121abc186186a1042c3a7528934426e9861061984c470371a820c8942650d306cd8662f645b4fd60769709f80c22f45ea54e60291162c0e2cd4930e2b5895c84f7e3c69b2737faa411bb62bee498e94e2a5de279ad1e984830a435a65430af5248227a0f0030212c1188068f407111fb614c599aefd4823538a01220253c9222f96bad4b4a86a108d62a98560d9554fa3f6203f9dca217fc39cc10916681082cbab74d8c2020d5230af242a2c21bf95fe7161e1639621842b7266a802838af852409f6f8d3a05e08a1522860040152a53a4fcf8441102ca931e1e274d769800b104538225d1e96192a0a0992428a6b890206ac2c7112c60843be69c971346d8624ea0b0d4e409d4e46822f110c5cc89dd6184205ae0c1c49c981d5811ea3811a18e39efa89203c98e32ea8052879021a0e64908091002969979393a9dec3bf6e6ad6e1144a261c17e302736c61ca420b860ce89cd4102821b62f8a0fb569863474fb246bf62f9a87895337a007e544030a793604e4c8eee9f8060ce9c581c181c21ebcf89f1a07f3b30f0a51f79ce9cd80e3c98821c94e0be31b30eb6dad2e3087230a7d55a7a1c73c839b1366e30e7b4980dc6c0d8a8c19c131bc10c60303113ac317de94b0f0a89e00fa580206bc9d6624f074526fe886f48f3904a92cc89a9b112c7508aead3a0ce90e621bd5f528a3d4a44f167a5c71fafd23de1ce901250fb24624f10179d8452bcd823461a18096430e724b529736268cc39b111c460c89798181d8381f8c31d1064f5cc899d3127660656c60be6c45ce0d3fd8ff85b809180054384c0f0c68f3762486f88e6b45adff9eea682960f9a132383da966f828d312796023dfa2620101dfc523a2d307c3227868272d4ab39b113f46f648150e6c44c00ea2d824078fe89533c4f282e2949ed9d9439e24a1ca1843af5e34791a7ef7c25187dd0178a3c736224984a40cdbf30c5fd0f487e40de09a83ffe75561045b490cc898d60ce3931314410348a4b5a48b44a93ac263df425e9a9885ce42215fea9584afc28a648ad137e222f3f24a41f3d935ac556d1aadc2ab8ca5685ab92ab781e69352c9e67b17471b7340ff75370c41feedc703f651471fa8e4a0c7d57892149921dfca8e0dc703fe5030a594b42d6ef57b69acb51436975114939ea55df697511090e8ed56eac26a5244351272b65d64db69595592d1f743756ffacc6c2b9c10c76404280268014e0062df32ac265c79583eb04170a6c806a9941163692f0c6036cf88109364a73cac42421d5208c0b73c28d420d4629a001cda42285096471c5154f30c1440296c839004e0964ce8fe787f0cb7ab0990e59e8419af930240b3d88d52042cebc722e52127c580207347043164822000594293f084923e5c1bdbf1f215167456616f753c86f95437e2b0d14822bd52703d50402070d374e8006188327cc3949dfc5300641cc3961608249fe94de8704831f676061460ea6150fb898000a7d134b18e1472ca432ee858005ac7c490691a5cffa8f5f3d117b2e3f8abf2423232333136a24ad72fc58590ec9055764416ac11b2416cca1050bb80e488431e7cc0147b1821fa72c0298130915f4a94bf20932a0cc795d91c3185bcc3934469539af4c520a323086cc1cea2139924cf004c904554825b862ce0d5b51e8c70769108912507b9f118a1ebd1e95608c31c6f8de7befbdf75e4dd3344dd3344db3d65a6badb5b6d65a6badb5566aa9a5965a6aa9a5965a6aa9edbaaeebbaaeebba9c73ce39e79c398ee3388ee3386edbb66ddbb66ddb30c618638c31bef7de7befbd57d3344dd3344dd3acb5d65a6badadb5d65a6ba594524a29a5b4765dd7755dd7755dce39e79c73ce1cc7711cc7711cb76ddbb66ddbb66d18638c31c618df7befbdf7deab699aa6699aa669d65a6badb5d6d65a6badb5565abbcc6df86ab6f2a8760010c615a337c23f29e2e8876821117bfc97a4832c910a8f90087a243afe08715c22654e2c89d1a7ca275d2399134b4014b1876c62ce893131278680282dede400d81273622d4d76ae4449a6438e624f9833b23817c39c0fcb972464657ed4feeb5b93a13f625cd27d28a45764066a1f8a3d7362496059e6c40a3027468001cce9cb2c64d5b4f4d6e18e1f5925ebfb9155ee2c558edfce74169a429d812c52fbadc11c13e94b31247b00fac82f72981313c09c1812242ff2c05a437396627845c9103b3ea7244a923cd1e951728af2244a142527254ea42479c273aa92c39313fa20f127b37054bedc0112c1bee3c30f2854a2d345076a68a61260f75717182063ce7d2249bdf2582f431d7eaa6fe4e4d733152bf4a48ad44299c80428fcca51dc3999c96380098624674e724eec08f603ce9c3349f7a3388e4a405dea255c70ab95f6bc9358ae5a23b79a263bf77e6495dd7b9e1faf5cbcdd2df41041b214419fbbb1b6004fab95f6bcd38aec2ac676242dc0c59c33db2a25068b0aa031a705b639a7e79d56e47c42aa800f723495dfceb69ec8d1af409fb21a2bdbcad2028b273a7362181c47255c04c11c0a1401879102b445c810972a5c9e98c004a64f8b34819cc5d0fc59a031a75e196995988655324a06e4c92c55e678de9c98ad59c060c1225de103098056ec60c396891527985c64e9c114655654b1a2ca9c43140101bfdbdf7b8d3790ad46b74eab5df5bf6e5703b8d64dbdd5babd1df7352d035ce695dfadeb2ae578efc0a01de5b68cf5d77867ca7d51b3e6bf6e9953eb8556b9aef24efbddef4e3d4d795e073f9e9beb05a22ebae66fa679d39dd3cfdd8e01dbebf6fffbcedecb2b87bd5f727921e2e2ef6ef7c79b663b8dd7bc892ed037e71dd5b5fb5cee7cbb116db1b5efb3a639eddde6f4f62de0bbff6de557efded58f4515a0be664af7add85fcd7d2f51289712222d6ef7bfd738976be6dd739d88023a6f59cb55e35cb7dc392d806802b9abdc77d577347b7a2fbd80280bdf6d9c7ebfed5d79e7e94e791e0e4b840547b596abdfd86ffcfa0cf60fc7837d670bd11538dbdc716cb55b35ad579af265a881a08824c0b75fb966b9ceebbb33bd271e911599d77cb9e7aeddf75ebc599f892802fe6ab6ebba73b679c39d4e795e19b244204455ecfe29e73bdfb7e6399c77caf390785e8e0802757b8d7f4f7fdf18578d539e17c47deac6835b4f40f400baddeaf1e5bd57bde1ec539e4785d6e1cb51ddfd6a77b7fd3e85c8019dbbbb7b4de3d4675c3d7785480a8b2fc5556bad29b5fef794e779ffc327529e943cefe6c280280aae72dd96e95677a7b97bca43299e2785080a6cb1e6f0b69ed3cdfbe50de81b5bcdfb9637de79de750ca85b67fc75de3ce37a35ba805c2fb75f638aa9866db7454fd00ddbbcedacd1aa77de9d1334ef9dafd72ed679d3db8a14b0f9ac55cafd6e77dddfb62660dbdee78eebbe1aae1eff262cde384ba9ef796b5c7b67226fcaeba6b5adb9dbdda611b0697c6bd8761e6f78d34d1fc0674e7fe57ceb99d3cbfd129b725cfe9ad67ce3faef1b014406a09af39e7fe7bca55d772e5282728db5ec73ffdabd9faba224fcd6b68bbfde1ad6196b5a9485bb9af65ced3ce72fbf1cdd83765973fd6a19ff5c73fe7a7076dbb8ebb7deb7a556dba2d0b7af1dfe1dc73bce62baf3d878de74d5dbffbfb75d3524302878cfabeeb5bb9c6e77dbf7093973fbf34dd36cffb6d909b76b4af5cf9dbf9de51b3701ebab37acd55b2dbf14774ce098f2cd5e7b31b5be6b39e57909c096b03be56a9739bc374dd32e5582ee97569df9e5b876f5e549d8f95fab35c7390e09946ed86bad6dbc76fcdf7b846d37ecb54f79f69af57d3382a651ad6dced67295b3565bc1f0b874d3ad75ba6addaed6da144b756a8d65cb37292981dd613347b58de3be6e95725fb3c3f6bc599bf7d62cbd5eeb45a09bbb9fab1aae5efbfb5622e4acbb9f3b8d77f5f64d731dfb721dc7a9a59c767bdbfa10f096ede538ae657cb7ed382150ddbfe6b7d7aeafb7eb980eceb5e5ecc7db6adb77cc040b82ee68a7755cf5beab5adb0b847d3fd6d4774fbb9cb79b7fb0d19ab3b79c76a915d81c9dbe1cf61a6f186f7e6f36e5793e489755603ef0bcefad76bfbb946f18d31e58cf790e77de59aeefee6f3938bb7d9dadc659cb37bb3b16c7ffbb6fd67afe37ced6ee869f1ebbe7e190439aa7d42a572a25e193520fbf26a592b5b5e59b942a93d20d053038369b3fb6b8dbb2d670772f0f3aaba9fed66bfb7715777a5a613be0566faa35dd77f397db68caf370809852b24d4ab5c9cd4af3ec95568d62a9a9c0dee8bca5f95e6cf5a6b9d73fe5790318801f59a4087224908002d341cd38775bed2abf54578d4be1e0dcf89dd2cf4d1017412406c0727035ac75ea39d7aea69d0e180efcb79a7395de7b3bede486077383fbf85fdee1bfd94bf3e7529e772249bd6aed53f7a114cf93026b83765bb6d6679ea9e778c7a53c0fe789e79141a298ba98c06e4029bef96ededd7bbfa5db06b5f31ddeb66d676bbbd55acaf37c3036f0f5b9e3dfeba5b6dfee754a55b01a686f7def36ea350db0a669d7ee6d63fab14667b0755cee34e76d6b9f7b4e636b6c9cc75dd7374fb57a35cf5df921e20753c3ef6e57dbbbbaafa637de4ea5072a3d103f373703c0d2e8b6fd1abe577bfb2ba7bb94e7e194fc37fed474b18747f13c3e32f1bc12f7537a3c8fe6a60459666032f8b7fe6a79d69b4f799ee739b969f9264892ecbefb4d11181a9da71f5b6fb56dd36eb63a06b6ffcaf1d5bae3f7f6ae9e301868bcde2de36ef3bdc39de233b0b79ddfbaf5e6eeeda84de1f85004753ccf8722c8936066687b535dbfde7ac6d6d37c042be377dcc595db9bbede73cebd60e36dff2df36df3b7b5e39f8bb9c0677d379cf3e632ee36bda53c0f87076b81ff96fbdcfffaef4d2fad4c3016e44ee37c71ef96ab5bf32bc05fa3bf52ce76ccf1b55f059b77dd754ab5af755ab69e8cbd3b6bb9bcb9dde59e6d3786ed7adf36e57ad71aa7bd4d01bf5eeb75efbeb5fb35e550e0b96f37ce751de6bf5ebb9da073fe79dd9ad33cfd5b9b605f8babb673d52aed1a530d2b01d6eed629f69ca75ccb5a2501ee9a770d57de3147f39647c06d9cb675eefe4df1ee3ec4c4f097abbbfb8dcbfdeeffbd08acdf9dd62cd6eeded766eec342a0efe678632e73bbfd5c3508eae6b5faadaffe033467ee725df59afdf75a0f74eab1cfbbebadd3cbd5ed631da09cb7fa7676736a3bd77f185be61d972fed7f6b6eb31d10c601aa6fde5acf9aab45601bf05dde99abdd72b6b3b9761aa0bb6edad596e76e6d573d96018d7676ab396fbd718cef0606de368f7db69dd2cc558c352bd81759c39ffedfd96dbb7cab3b857971efd6aebd7dfb5bdef6df5ddc5e35b7dd46bb8d67ac61cd080c03fc6b1f6f3ffffbb7ae518b71c13daf9cb35ddf3ab697ff0b605b6016b8daed6cae9f7bcd73dca529ef46258e4b44ac02bc7a2d771aa7654bb5bd3b4dc3b4d874dbb5c3d5d72e775cfb1da3c0a798db388d3bbeedcf7f37816c2ba7b7cdd4f39bb77dc447faf1e479383937a30fc31d1d2c8b6fbfd63c7358d368b75d4df9f1088645b6bd6e956ff7f78e56cda63c8ff4e0d63f427af4bc1fb02bf4b63fd7fd4ddb55e31673563009fceaa986f7e77cf794f69e3a919acca98259b171bd37cf986bbcfb9bde11b09c6f98d3eed568eeaacafb60557054777be3f0eeb45f37cda520d0b3e65957efb9e56cee78caf3a2600fb8b6b3ddeffcb5d7eaaa75258b878aada3f4ef7c358cb9dfb74e79de0f3605efb65bbbada3bc7297d31ea8c4e4e6fb2848923029214972f3047340ad75539ef1a631a7719d4f799e141cd57b57eede4d3b7bff8f8256ca599cb7be73bf5ae7a1a85c7badfaafe58dc33dd70670ac3be5f8ede8c633ffb4620cd0b6c63fe7dbfd596bddf205f0dc6ddf94eb4d6f7d7509face03f604b5997376c35aae1cc5177322f7ecbfd739ebaf3de7bf0278ad97d7bcfb46eba6dd8625c072d477cfbbcaddbc39ce86dfa9354ab911cb70a735fe78373e604d684ff3ee5daf9ae7b6bb0d89483caffb2171634ce05d31e69bfa0dffad6fee9eeb954fcb3711398121c0fe8e53ada33ffbee390e8e4f71ade3bff107d45c74e279a51b2548fc37fe785ef8a5386603ec00dd6a8eabfb725ac555d39dcaac966f922235931292243c9eb7fbe9f2025b8252ee763cf7ed77ca6fd506f81cafbf737d3bd62ab55785b30427e7a664f1844b52981238fb8eb3ee32e7aea51b4e79de4a1cfbc9ab58383f37523caffcc22f649d3c4fe55558129ab61b87e9edbbc3b2786edbce739ef76e57ecefcf0e37f4dae3776b33ddb42e576ab98dd343d7ccf9d6afa6dc674de328703963ae7faaadb6d5fa9b47a6b467fee9c58102e53ef6d676cd73de6b1c4f791ece13fc763f57b76e779cf51c4e94cb099bbf57e78a29f7b7fdf94de15c4db0386b8d76dbd66eef7e47c38b095dd5b56a165f7fefbfd92e81db7f3b7cbbafb6e3f0d68db894f0abdff2e796f6ab59af9390a9a7b8f7de65ed7a6d39122eb771b7decd6e8eea4bf31138fd9ee3a8a659df29cdd808de733affcd657af9a698065d7854dd31ed36eaebdeb8ebfd0ebc31ad5a57f9e7f8c6a91dbf6a1a774af7ef9ca55d2e82ed30ce7bf35873adf31d035d44e03a0d6b1ac5b4ebb5e28efbae3a2cddba4b379cbbbd31d7f121741bb59cdb7f6ba59c56b710ba5e7de5e8c69dffbd6e747cbd6fd7a9c637bd659c73396a28a92b085def5bbb4eebdef34eff0f84ae6b8dffdb37deb6bffff603aaabe62cee70b79bf67d9fe3775cc76d9affbaeb1bafd45f3eb0f8eacdb9cdaffdbdf2ffaf1e50dc7dd5386f9ab76ed3bc1c5af55affba5fce5a4e638e5f71ecee7fbe37ef3cf75977160edd759db51967bea9af1db572f1c0e6bb2ddf98dbecc6354e717a6e804427d712d70e28b7dddddca79bc3dacd3ce579254b64c1f5c6e6bf724bb3d7349ef5fd29cf0bbfd30a2e1dd0ba2fc7b7ae568ed334de73c0d94db7bdb94abb7bfdff38f05af7f862df617b39cdbb1bddd6a9aeb8d33adb8edbdd46b75bb7fdafb5edf96a7f6f40bbcd5a6ee3bacf6d8deb6d836e39dd618d6aef77de572bc0c506a5f5f66df5b6d6dedc710d8d52a242d0802bd577f2e08a559e0470d500dbbdb9fd9addf6b7dbe634c0fbf34b6ba7dd7cfdbe3d04f2e29a41c75b6f4eb56e35e67cb6a51bcf0be35a83f22e634d71df72f5fbea94e7e1e03cb9f13c5e73355b4b54890757ac1f9a9bab02971a1dd538f61f63aaed9ded8fe24a63b3b6dbdcb6ddce9af31b7b0ab864c0fd8ed3cb39ecadd6f776539e6784f73c1c239480fa2b572aee79374e2e342ab7e1bbefa75ffb9caf3be579dec82243259ef7e48a01a76d9cfef5ebcad57baf96f23c065c30e0bf57ce6b9cd732b6d93bb9ced09de53edd78ebca378dea94e7b5401c0f6eadc77248490904b54ee9c6f32e272e3330d670c7bdd61cddb0fd39e579655c65749cf377dbfc72babb9da6a53c0f47a75496469ed24d4e8b7bdecd65c6f5829fefbfd66afc374a3ddf29cff3bc29970bb6b675efb2b63c739edadf826d538edbb673dbe27a716e0470b1c06aeb2be7bde77ab7fd9e7d4a3f375705ae1558aa359f3b0de7edd7dedd6b894b059ebb1c77f3d5fa7675e59c8ccd595db5b758db9dadf88eb1354cb98ab9f59ab6d1bd53402dcd9fbbd8e79febfdf94241a6fbfe9a6f673bd7f73e41fff877f66bd67adf3777fb6502fa2fc76dddafdbface7bed4a4fd6c30f11374dae1274da79ae6b4d5bda3b4a3b0938abd18fbb6e6b7e3b9bf108e8e73ffffe351583e37b63afbb8db3fd733819a9b32a37415c22b0dbc605a097a854f1a8b6a19ac41432860040b023092000a311002038241c0c060432b1744a8f1b001400025daa6aae4c174aa46194a228061943083106000008208600620c4355666cb89d37f738b6ea1d3775e3de9acaf18e9dbc616bf623a24513d70f142b740dcec28d9d6187c8b3d5c996985062981a0642278a6e6e23d27fef71632a1b959d2a80ae1abad2ad54d8dbc10778bfa67028ccbb86809ff11f9797a154eb2d18af94c0f7499a2a4474ef48a8d406ef0ce3c6b4b985e0441f40dd2a259078d08211c6b6031dabeada20110e5393eb53497995b28157f24828d7d5dd94d2374607b8fbd4cef455a818bb8b224817a0191fc645c4e45318c98e863ef83367ae8f7f7985e6e18e8de832d8c7f7b836b419c26047408887a0d62ec49fb9214c9e2c0cb72caa78007af0af89a09cc6c5aff4a64d556bfb9f6b86139c20b26c16cd573873507a74d1bf906192d9763d42b133e4bca128f7a495b64534f1e73b51b87d7a4d046d9e88e90776060024eaf21d628ab33402fa1019ffbd14367466732d79ad7f9795755a76c98ea3584a51b03382513289c6db31384a2ef37d95cfd58eb0ac918ae82d2b7145b79e9b805ae1a4974ca3df3e2ec113e822e8c4c01241b78da0db41d07a128273ed4636b4b2ed95bd634ab4dd0dd588cccd23d0b450972a74451ffa2f4842b91be8b906e5ae27c205329d19b50709b98a22079b11a81594c8063d57cc50ae7d172221162b6f2c0cfdaf88a1dc6d3668bb8ca34409377487459468a27f72995ee13ec6aea9f086bb0f0763a38c7f86fc7e2e9d33e62235ed0bad41bf71896ffbd2f9142d7115a63f7fd3e47215a1d63b14ec6c53fda168a6ce73e47f1b52a83ed765a9a0583df4e18ecb14b910f256a7f3ff8ddeffe326265936641cbd4ad375973e8d8691df3f3c46a5985c22a183dc545dfa52e19e34a3a45ffdd7aa6a0a05f1b27cfb62fdd57e418131f5580f939b5f440f1334c1379d3c92fe294c811c8ef3f2f5fd1a0f16d869c67ed5c15173230aa4ad6b5dc3113c9e1827f84185f0913e5d092ea02bcfa29d35323544b31272534fca08b7b9f14067aa110f11595fc1cde0e8032beeb05cd1bf3c3e27bcbbcd615c2caf0093ac570366a29039455c0db34420ec444613c6fb0ef4e088656bffccdbea9728a506d7c583b07861c9deeff0362ff5adede0f846328ea98140977364210fb6dc45c86d6fd8e375397097b8faf8af8669e54d2995252bd7385a0fc4ae6715d24a3a3d74c80e26353169829d7057f4ed68abe44dba6e37331a6c04d4097a9ace414113e57255d5888a6fe4b19ff579e212337a7b03864a238ff92202a2a1c2552b6aaf95afd6118bc624a14774b5a384d459438908404c160ff37ba2f58d9bc52cb187af1fdfbaf125357b81ae032a59208fd0d344221ce8d88026772485ec300f19233418d0945992a1cb1b0ef9288cee255a07860c33f2e0649965c071d7f2d19044b5a2c4b147a2be488358713bff86f547c08a4d239b619866009fdcbacce3cc31665df5f6e4d1c1bff94f83c666f173b7cb80325b7cc8584c7e1cc7d52ee7ebc2b5ea72da5f77b99d9fa369fda076fb249810c264c313c969c69cc94355d899a6698a2d38a36b8aa8c064c041bdf78d0607de228eedb1812e66df22ab54353d959a553fe3e2d658e2567dbf4d9ed1d0f8ad9d22040c78f1718c8dae127889ec20868582c1d667a31a0b456b16d119254b66adce6e26044a1143e58a9a09a79eadba33e5e87ef466c2863f8c18206c54a2d50b624417ac4485aa254ad98743a59c7927ca3f580a4c6918d0deafd1505aa69b1d0c6b3b387fe34e327d83d349f4a6f100bf4b36f1543faa5accb8826e2eac63562f79f8dde97539462e9ac1ac66bfc55726441a580c231ec44bf28705482cc0e819a71568b7f81df2a6bf9f22632ae9d78fd8f382830cb5e24807dd752ff8ecd05c2cc544d2037b5c3856178025063e86c718eb4cfcc5610016195dc0ef2ae3c2e1d2f2bfe2b266ac0a136f30c315698c7d3da0ac2065256d5b69aa05334306109c548879ecfff8ba6113edb7d4c46d96c75b723cebd6f1f2e40574d8f041a928e7dddb7617b4f445ac24078d6011a3f22b6c19f86e206b04e0985c4ec6f41058f17ed725bd9e84d934e55740bf3ceba5e196340910e8521a25df1fe2432391388b5f01782dca4e545cb930527b2c831bfb6dab1fd5ce18e920bb3572df4864e2164d4d65a3c70a7187b8569ed27a34106afa4686e9674ad0e514642811bb3d7668dce1b299590cefc6740a9c4ccc2291f52c5ac45ccbfb5c367a57a968b1e6d4fbc4254d4499db7bf852712dabd3f3e3c9194c070041fdb035cdb094273d7f48df63a30dacf7d6879d9870551664b4eafb4a76d3b9a4ed6a0125a2d56413882142b16ebbcb7275bdf22d27d2cbfdcbf38e825d5864bc17a8df1e0d7ad2604e0435516107a4542140edba54ef813b3d66d1224b0c6e7536507ea4365c05ccb678966b2d054b95da6259dd7cdeb44c80c7d686d1f0250366093d96736e2eb1f9e9643fa13d10402f3bad554e0800403999b8a50d5a34235669ef77bcd7c592a0bada1c4fc83867927c88cb2a9f8fc0a4e0f6443ae7ba320844be789764a1edaa92c38734f169655e47c8c3badd909b385cf4144af20a3e2177024b1558a5a4b83811cbdc569a953d0f9a844946af28cbaa1dffb023ca8d2eec5ee7f0674ed3f111bc155d82bb4973fbb34237cfe1f76a95943e259359aab4b74a0e5cd75385bd7d613f95fbbe12084c150cbaf75de1ddd89207553fb5211062fd73b4240d8ffc9831902c0bdcf87f550d0cd479eca3a0250a508f60e21df38ac88fd1ba7a4e87dfa06e94b91f544694b072829a9b61d59b4443472011a30bd141466332a0621697317a5524da29a03da3bedea2ae72a9c09169eaef0d875b128438f162a8d0b2fef3eec889f0a871c7b2cb1f2246717ed885a8cd480268f763098c8873441af72db0007c8474913dfa265c91142fbe463bfd5da5953b79111cc0a04ae0ef28644d0c87bdfdcbdc9d911fcebb8c29a1bd77428a545af52bb277e8b0643e864ea2b7540d562689c0c6a14b0b803fc739aa41f08aaead517ababb053c0441757016366e1d3066c786de60cd8bf79ce749c377341c81159d42f79b237953d18404e4ce0f2ba7e8420495fe4a523a71802ed5ca8555097becc2ca25003a620b140aa8bd90a3f2c50f9e3a79427f2919f6aacb0603f5d5748016c8a24bc5853f384c6568b6493dd61fd1220255994bfd2f5e80b6e5d06c7180b644123f344beac498688a435b191c5a6596352d44f548c071687ea1601eebf7f103742048536c719e203a8c72c3a555a3fbbd2603bd4564ea9b691f1676e97124ebe43b228ae2757bab45f88125b990a6b8283e0f12b83f04dc152b191239de961290f22be1bd8dfa4a68c0c6f74ad4b7210125b2b7615f890ad8c85e09fb361a5042f736ee2b51012be6e6c6eefee094e076f2fe48f99c2470c47b6eea1fd00037df1fa8cf8904ce64cf81fd03a9c04ed99fb1cf410307a2fb9dee1f48033bc9fe4c7c0e1c38537b2ee29f70808bee0f289f9b83030f4f894c3964205b4640890794cf0d189c7e1cd00d64891f729a7e1535e806f4c84b3e6c3c2fe25505db14b8e12818cdcea3d8fe16ac278bd9427129a08aa30a732e6082b0cad4e1aea05cfcaae67eee1ba9d2fd5339d43a3c624c472a0d3e45648501eaf1cdb085357a798166e6434f112fab672fd54db9144b3d6f371b208bffb19d7f23e96d75da9b4e79bc6f06475b15a4bf9740aa4d2c516e432a03311a3a61fe64f1335e90f48aed9401be80860f0b7273561153b0e431e799337fdfa7fb36994bcfa3476f4af7cd9f576f320f3d1fd215f6be89d5d2baca97f42dda14fa7e8b8d2e6bd40215b0da799be5ad8d6b1fe657b2621c167aa19f3118e103b94f697c4f6621311f84c89e9e1f8efd7a794d94de0195d4e2470d7c26d5d2d06c96c27af23cba4f1f4ced3a7e37951f29ce39b77748438b451b57908895e3eb2e34a371069ab60754728532317db5fe563c7d4d0438caee02fd2fa7c2ada0d3eed189814ee98cb95de272944ee680abd53907138f3d05ba013d8f9586a6c6d5354f0d28ebc00f1fb84dbabfa005540d03bfee369e113365e19f0a4473e7d5583939441cb844e3a429d1f9d35e1ce774bce70b9e6c0bf4446fb6e0bc40e2694114b65f8119d49e47222c109ec1c9f0bcda1a1931e26717df4b612c8ad9e990235607dfa0c1ca5488fbc371c57369d27f55171b627e8a9ddcc8d800dc596085ca67bdf98ae1b1e4d02af619d5dbeea3f4bf99aa6988c13c9afe309c68aabb12917e4053d11376db3e584d3545c5403018804f81313e4cd7e14d842328828f537c79edb87d51f790ed96f388030295e26d7f406610a0514df8a175ec3965d9104b91b0e70395cbff63a9de69986e34484c5c007a54f7913213ffc26015ab8893819f7805d95697912ffa4c060cd19d5e77c9fb16ca357e29fbbb03835a9a6f4da078cf30cd76d858d27b13fc0102cfbd5d958b5c81c35e27d61eaa42e815d2924c88cfa5e43844ba0d07cc47991b459dd6347dfcac20f18108f263d295a04225db3346643069fcc0478c2d523142c7a5baa0cb06afd79b8511004150db9d4e0548bd2ca39f016007162e494dd25064712fbe7c28b47880580b0055a0071206789d09a78f02647bd8e606c3e509a9d833e599116608952400f1c0e382e9e51a176137b96cdba00d8e26152395878d0a4e5abf71cbe357636226d46e68ccab1d4bf8e1325fc3574a0232c1a6c48315f54c375b51808a507071e455b3266556c5402cca1a09c6cae9180c49915d013193a9dbf4e811cd765a3a737a84ac53a740f30faeff85809e4bcdb14b62fc472dd6183e9a1ff96a170a0ac002cebbb08c7909602478bd0c3ac2d24656c7ff7e4007b2346a8adda7c04b3663ec6c3fc60eb9b642a3df0db5898b01c09ff6acc7ad71272240922590dda15960d644a1581d8d04eff34bb0a4ff3c1195b9717083fe79450b015fc9fea74d20638462b1989a0f0a481589d37ad37728a0d62545a5cddca08c94acf4d5f6f20639d27252685ccb0bb478196dc923d57f72a6159d983e728fd21096cfd6541dd972b2604ee4c7ff50344f21dc0e6ede909fc5c17d667e7f083420be310ef3b004a458327b918660f8a1e04061df8c085cf652dfb49d27eefb96024d8e8a1ae2a6e9a81ff6e1175f1f62474c3f92c176134c0a7bc0e374156d69cf4727c73d0998768f2a4e9f8ba00f5ff3d34bed592b1c4549e8a8e5964de52062dd0a01e625e56524ef0d32547e6870e09b4ac22759c94c9fa0d5c6abb61e2cc7edf1946e2edbf708eaec2f1ca8df553f09db8d5e6db956a660dcdd0190e8649a4fc6b0a6eff8adc1dc3b729b4206df56f926de846b393d93a6f76a6d96d223ec80ad3043fe465b14c51f4012b9d91b892bde33dfb7dbbc36ccd1daaecee687ac7c78f775161768a87f56d8f711705c860e90b42bdabf925f80d21b08256dbfc19fd319167339c87aa0d10b5a5d64490ec4d7f79e1bcd36ca0af4fc4bde0654c6df39aebbca0b56d055b6ee614abb249858ebbc53566362d96b6e832da4f082ec5610364e550b1d48db5d6c8e457e818dbbf658d92de4f37c89bfe3b4e3e456a3cdf57f8de759d703da6a9a210b02163e9763908363f30327c29e6396521979f871d8c39f82d27b3024fd41825e053880f15f375e402dc549be79358befdd6ea49f1a8431f79da3558073d2c77e27f6d0d5bdf4e278d863171dfcbd9539787279f62e0f774ebe084062c3914fa21424a705602e6623daf57222763aa593c74c8ba604abb547c73a2e80e918dc44364a6fb6a140f67f9eefc1455f6a8d6fe6816bbdc61bca65ddb678785d821d24ad9a78f6b1f08d92715d2c464f4f44accb5f57c5b8fb4d668758e7a67d6fd5f7bc21b90ba22407dce4ca9500baf19749605702410a0d4d885bcdad87581afa9688dccf80a8e24f89e8082848902f620f73c1dfef2262000b1dc4b8ae819bbaa85108ea84980e42612272841f0d54fd0352c7caa1f38d12a7fe952d71ee6482e0aab2643f46d284a048924b830245d97062bb9b3d3c346ee560363d23c7e1eaea8e74184cd2f19d6c23249ae88d93894003d347eee077e99e5ed4c1b5eeb89c9b9433ae4b712271c6d63f3023986ffc8e8e60e25cec683b74fc43452022cf4d347b6e2e6f2c0c04654580cbe578f0a7e6cbfdf0ae9bef236b449f2c49be569de17fae2d11220cd2cb2e8ae9c5d2e1f714199ba1194606f1e63f3cbe15f14b9ff3537bb4d54693fab8b611efbcdf2b523bb9c67e038596383f6e9313ef5a6a92a330c86c3db3f6979b7447c2e77f254dffbcf0b0e36c1537ef21d61d40886deb4e1ed250a41e0723e5a46a9c9089a7c6c63bb9d475d641126aa073e42077021b36d46074ebb180b4139b46602fb1bad13fa4ebde43eb108af684f8aa83ad5ce5bb34449e8e707a2533400e55d33bf407e1ac61e5a15ac16e6fb223be8aec27b48e6c561d57dddb3fb644286f7f6907b241377905734d8985f59bbcc50afcc9c3ff6d079c5af26e403e95aad7e5c3528ad0d0f002d7d3287f3624de515eb77b5eb43966678326fe289ea4850d0d3f15c825dc7c4381b3d2860c2b1c3a783fb5df30e987f9f8a8032a59fe4acdaaedb4e6167b7d14e15f3d0c74311094cc103f7fa7bca76563249c3527cc7d7505430016e5b968af1e74777c30d4a6cfdd4131a410d4edcacd6331c87fcad291c5919971992341e4df9a5d3bad2ae3e429f7387436f3085b04762a447416a79dd1a277625609cf33fa2a80393d23dd5d757becf3cedb53e73f5a73056746004f37be512ec924e3658742bf135f1e686ad82963768c721d5d379d6b0cfe410f71fb83363b79549a79d362fb6702321832446aa3c879435776e01aa9ec0ec7113a63d19e26a065c7b6da3fd62906476c6dc7a63719b5ccd2dcbe6d08c849646715d0fc1a296c2ef28adf299f4d76eb94804577d44365521ceee39fa12e3ff1bafc1feabf14739d291c4114b9742951fdfa3a1f8ba2ce1d0ca4efb730158e956a24b8e1f822b2fd4bae701f43f1917183151e2d68895767de8c631c53155873e8e4ac391e1a0de2d1ad40578617e6f42c7afaf0e2f842a00da801543e2cd0662d71fc73f51d79fec4b9dde7fbbc43918daca5babd2c2df50f99ad16e36425cf1dd2302b7bbbb2a86bb1c78c30e70d080d183e47c72b272b5b32f1358413d1b95e568947bdda521656dcc43f1ab5570dd23e132d6c34ca6f4fcd62773fad268dd6ef8f007e00da5faf71896493301d5aa8b12fc33ea15d7f1185dcafb31c51ca3ca6386f0623ecbb9828f7a90d1c05bf522a3e31c5c318329d671fa3642317bea6ab8644c69c595fa29adf014c1eef18658d19aaf26a3f05b30aa760374c8ed13bcb2192639884e8eb63c14735664b1a0f614a0c7d0e61acb866200130f792cf2e1576e823a84cd2945590d2004cc2e720f327dbb8f1a7ab0efad3d342a7415fd38023aa12513ce0e2e385acd5035e4176e71b7e726436d76faab28b606a8dce1d4773833fbd641eac0d8377a7ed4ea475d8df7732ae68a8d450b13c9d84cec8b48377bf1b1d99a63d48d727978c6f604602b12a98e99289957ec5542b625723596be2c2775976a6b95dc84fa803933eefbb2d1252956d0675fbbcc1720ae2300445c4b248a4bab6fd696a91f32063a11bc05f1c5f801328b6e2d9047808150c87f3f20acd394a17a1bdab0c903fc65b9f6e1df63aaf63076b71e9b0f8687e2452462de76f1dd62a26945c22293291f0e04e3eddc6f022b49b53c6ec82ba31328d728b1a748a8f0b51ddac8bb2689091fdff4fdf5009ee304b84f9a6b6f1e221ff7e396d757a8c863a06ff2b3b31aec6deedc51aa3108d1bcdfce28fa7729a7178f115bf9b24f2e833e61f3ace77dcec376f03e60b611b0c20ca728723d07a6c7ec41f06a7dd7ee5922d5a13a9dec53687e8fbeafaed897d39a707d50de1d60dd69a55708c91a3b1b5fee1e9638e5a748a39c7743547a689c419a5ffec9cbda1371d77d69734b142a1f820f9cd4ed73f77ceff8bc4f8741399dc734adda5b4cc21d4993330233b651b39882af625033b67fda65374af77466fca3438f26d60d2a5a89e986570dbd55f2d1cc6339f4091b0e9e304ef4c1c4ce9024ae2fc9e7cca6b9126887409c21a5f11616f524881acb5d0e882f3e4338c7f3e873573bb8bab3a53556a704b1d994caaaafd5f1020904ba9b9fe1b4a3cea9137d57849084b2d8adef6971160a94c5ed3023572bf7cc9e6b15d41815c5456fedd454612c7ea125a0a283ad22a6c724f1d49466a95bdde1020ca4fada1d62b7a1c151134d57a4d0ea7329afef6970860554c54d7e22a3f3a09896a5b7fc040212d577be54f98a828c76fabe2971222a5182d9bdfa340a59bbe42831b947424b844d6122f6e68b7e1a90dd76c08d850bf86ca1a7ed5905103f0515d653eed132701435545eabc471b8f8a881ba9f12a1918aaa3d5abfda544e0a89e749d16577341216932355a7e0a0bbdb4b9b4a9fc691c1414c7d2a2da7f2820562a56eb4d6fa1815eef341534be850252795254ddd72be4486549a5f14ed39730441e80c87a46e220daf896a1f93260403c36d042f94866c5d4572ddcbc3242e5552fa79fbee7c2e883905dda5d85b3ffccfe26421269900a2cac94160b254e5134495fdd77a88bbd0dfbed634856ba036bb8c1f8b4a6bc76bdf566b02f0239e2970b9c8a02fab2ca31caff9cab6217687f1911933883714ac9bbd113a40bbe4bda505bbfcdd785302af3ee1fa08b20048eb9130bd67bab8e377c53a1e4b1cc3bfa1a1ba0d60cb7599b80b25cd7272ea9cba90e71bcd86197c9366493eec83e64598cd5352605b10651d0f48080c1ca7571374586659abd5c632377e2d430562df83596c72105c51e9d515c4eae3bd32dc26162fa364456a96dd09b442c6dbef9b3c01c005b905c15ef39e01d7d876ecec94b1b1946381009d068ba4e4a4972b590156e0248091f0d30d7b2060ef0d4e005a072bef5bf7285c48170bcf0d11ae2c1eed6608451c3d237efb1f402bf925b0cfb5634e2f95da94b74c95980c848f99e584d211674ee411a02f68e9fd2283e21154f792e466119bafd58438c60d1005be889adf1719c63180604c80ac17b2ac059fb9c61bd3434a8f4e1e661c908632bc5673a83fab3d73a23083b8934af8c78794aea081d712852220a42b7984797c7332af5fcdffd34707db78b928fc4dd0bd24e2cf7412240f654551a194c816b3c2883ea0ae153a0283c49490660baf1c055799de7ca159a22097c088805d9c2dc3d8224ec6f05be24a58bbf7ab5609f39d5ae4d2ba591fb885829be5e5029bcd40157e3c75c275bc8a022e5b1f372399fc39653f9a6a4c47768ae86e7659bcece9134238c31979cb8bfe7be4a1fe86d14423fd814d26139e48dbfaa8ac5110b85d74da014b3c2bbe974ec1d925c0cf337e7e583eed150423c2dde9b141cf3320b0468ff4c515d451a7970b5886dd9247c7efc3f5c1166ffedd7d49e7af74bcf12a85277d5e8bcbd3e8f520ca218a1d66524178f7b2430a9df80597866d1c3831d34bb9c10ce849b0d84910ab3c44f5d09503ddfa3080d23dbfe16e006e8f245db97cb40dfbaabf56cf1444e9ca3a96b0b94ad6a76c7986d92dd07ae4983d06b6893e1656114d7e003279628429f610a5c18cccfd91f264de837114027dad7a9e4fe99125ad0bd3da9a992e13cc6ca235994952bb47864a221b8de6db44e2f1f037ada9ec62c9632f0df40961584af05adb685bd3d3e57d81b639739a7036ada4f7903ed014f3784184901fdddb8f193fe6a4aa289ed7128b5792b428c6a8bc80e0eae7e8512bc77a90701f48101fd389e3fc2e8307485bd0f00b403eb2fcdd17b92da473c44b539a597582f5ed26024ba674ee6d38c946657b13fc44c1f513803f7241962e7d1d3a85fcfacd5474e2f29f94b62da78f947655379ef28be266b2e6bbfff2302f43286baf219e691ba6b4bb939545f6b2217d832fd38e99f3242fff694b1e0dedc476acddfc8acf6f22871bdfa6fe532d3b49d9b8a88291502a211a32b8dc4b969f09cf53d9d7283f21a5523df59f8ada98df9a67b40e3e7e13c8dd9ceb42a56f2bcd298ca148257eec774edf0fade827d7ae02eefe26873d1e6dce62a5209f73c441799a5ecf57ba724a5d196224b3055fc444c4a66d0ea7c1d3aa1875197294781c928951db0e233d2111b36af585663ae60bc12514e779491e72deb2f7d1bc0a237eceffd839fd691fdc7e1f57cd7b51b4b03678424c334346acfd370a0e6e2caf8dd3fe791e4e19b41f64b297eb2e96f790c7721be4962954536684c01cbc14fe4106e95d0a9908ee9ee3e6c513ea3398c1d2a4e485208c3fb4e401fdc5246f3e9a7ca6e98ad2e101c7b9162c276d0d0cab14e31f32e0eb03fc1ca9d8514fda127e6529d3cff89b80ea67e1928b32239ecc733557172b3d361a2b86c9be03e8451d0fabb65e9f7599cf0ed1d81be65c4ce5a66b3f98083aee944515811dad4403a2e90f4daba871db9470d33d4960219e247ef690ec2d5786cc71e9b13c73644f6d909b82677519c933779a0c9c1fb1920ae9ba630c1f679caa120022546d5aaeec45695b35f6b99342b8bfff7929033ca6dcbcb2dbaabe447d4f84d8ba8d1de496d1e2717ff736c8f271028d9ad2d49630849c350cfd87e6b41dfddd668ed8d3b046de2c8d27c8b520b7cf1c424711f5d8bc4664f01888100443ea9e33aea6c60387d8e652f5ed912ddfdb75d5abcb0235e25c8711aad855a6446aea66edd90b398b869150db0b5ec162f4480d6af25fbd5cd2adf981c64aa5e9f59fbfbd51ec679e3150a8d3bf7b89de247313b71834b95b6cc1357b64de867e22112dc04e5eaa5986dd2787ac9a7705a4198a6b8eebbb877d173f7323faba5b3f22278fe0a1d3f83ab0d611b8932fe8223fdc416c42c7c82644a9595281ee13aaca339e61f2276bcda02994614317a0a1ca6a3214c90381f85144729d6734a81f77b34e1885f2aceb3fbeb718c3b435243876571571b2ddb3f31562c6ee7afa6e8700649eaaa9738d7de2fa25484deacf6c38d7af0e3786323c2a3e8a4a2c1be129de32872dfccf3b0e1384c53755f6cc21df08ed0653055b7b809699021a03f842124474836b3f546ab4bf109d733c3775bc04c000331abef32cfe742340478bac35ee678c7ca42162ec646b943ec0b7577fb96bd74a49f60f1d1f40ff6723e0dc3030f972d81f718ef2ac59de14cb13a41a1d2d8709a1363707fac7de34a83bc15f7f465b1b596c2cd576297c2ff1ffaade83ea73f09083f4b9dd13112b0fbfcf036a280b49ad973ea8b3ea932c43394f2a0b3deca4241283904d0b26ea10781d7865d4044405b2888a4821319a2106e5b0e7de741d41196fa09ef36be36717e0c3f0f00ad8af097f1b5924472f1b69d365986db4263afaad60a1ae004981610ae6d9885c9d8113e211b0600d589297f52132f37c592a3aa3107ec33cfd72f7f0c2a80f22c1fb1cbfb783a9417fa99641e8d07961a709679fb4fe2bc482a00fe7002cc2d6a195b14545c8a7a6b7224d54b5eb063ccd543683687e0db18fd96d44236973ab3469ad60d136a2902e9d90d1202b1c5f67cd7ffe62067ea03f8ce282665066ceb410b11707dc8c06a2265d2b62b43e6f6ac697fee9a046c7fd2aca71bb73b2654b350521788ebd082b401fb1c44db144a084a7710bd001dd0a26ee34b18a6a6ec51b13b1360c08f696155e0cfc9534b3261e71febfdb86d59eddbc2db415d041fba15516e953a5c9ae7fc40e546c496eb277d6c92b5241dc370aa64cc5214ea258424839b5bf3a33b659045bb411cef180d68c1ec319d5f6224e6e869fbaad073a8e9af6debf87b57dddf929c381f856dead51b0eef61e307cc05a9c4f9f78e7e299794b78e3bbf6585784fc70ffcf6924f90196b3361e560b76799ef11bcd504b4a761467c057c6f554fc47edea045ecb06daa872ac984d626153bd61cd25df605e5423a8874493a263af03c99347261491ffeeac6ed97d97eb84cf2c24022c029aed11c93fdbc97e7d441a65659b4a5fa8d875ac5d258cf8117b76d5f0ec5bc09abe750e809cd059c0b3d2f32a6ca4184872e0fcf68d8df4628815c2ad1f245d4ba70e77fc3f8398457ecdca9d1f600d79bdf5f7e495f1f0c5051fa9dba5791c271533fd3cfd7b119bbfbda263d5182fa0d5bf7fa2d3b35abac21352d60d03343227fc12fb46e1a38a9a137906d3329829d09d13b5bdb35b915053bb33d81bd6cdeeda811dbe6d8b4bc3dcaf69b5c6e862cb335a756aeb63181e55fd8c6be4aeb2decbe2ae1f4972af7014d6ea4178efc2e438ffabf0fa18f5e28290b103b954e0419f042181a97d79e8b76912b583393b67a90d67b5f50a3e9fd4d7867154bebdd8a2e7bed7e232b4bfc146909d4ef3a2b1e70db94870061af92511761afe482697e38bc74c7cdf6088d72e6f3b7c95e435e015c93bc6a9154939787887dfbc7115057bef9e10d96d36d5e20cd3fff55621b97f8cddd9054a3c2b67f99c48fd320736aa107a4978119fe2f8690e9f939da36b47229c507e9a7f6ddd471aba7e158415103308f04ab3b18836b0fb8ac8c77358cc4965c2430a7a6f1f8df261db979509f2103b67d201c2122dc593f147d4c035a61b86a49e27169d8e2917d876ed0d7e8c0eb28129863978e9d2e9295686e37198f276ad7f52f1c6ab7cf1896b3a336e4a0f6e33e6a2b698a2336cdebb029de2c2c3a5515ab6fbf685d8f9c07c75fc8095eadafc648772779a048efe5f8111a5d187c76c2ecf77f82678007cb74a931afa9b4f5feec75086cf7147f3fbc59c1e16c5a6282eeb52f9868b08171242b6250a8b9602f45a8633b6675daf77e403de732902692a29edfa278a8b4ad9285c62a1aaa250c2283679e56acda66a1746d5425db1ea21bbf0271d9c8cbe6820888f17faaabaf425f2d844a4c725b21259bbb70af303cef5e8371dd14df4e9e98fa8581763a73e7029bd5b3f0866afe1396a6fb164183bd440c082baf2e7a4651bf518e9bb91d2c9b64875e809d02de8d5c68f1c41cf8a95c1731fa68857d88d7828e0202889cd1120a8d25e4a05a1328236027bad1088bb2bc76dc07ba3b3e5c67d33dc97434c10fc515eec1e127e7a5629650531cd28b5810cdc4f65ce38c84020397960a8eb60ca0458a3f24b223ced9a42fb0453e186d52eb85ff59926e29cf6f589e4ed234590f943d8aa32d5714a6da12dca5d9f45e248ff8f0f5567c2a103ed72e6285984e51649aa194fc185b1b84a9096d640891a8046d01aa1ff649015cc42b81036c8820c37a118408772d4801b654e025ad426a1b110110b2792d695831c3c5f4e4189aa07cc1205847877b1094ed1076aaaf64c190b7082ee4244a22da52eaa42ce4ca6f41b62bee83860c4aa687568cb0c7be3a209dd432f316f5b856ce70173f1ee840fd1c7010a81d08c21b6f71beaf4cb606f943e5f94a363251b603d115985633892a8014a770cd03fec941c14c70beaa97d5f566290bdd836a9b2ad1c35fd3c69d369f5509f801784111df8cdcf29cc0665ec27491ab6abc1f06ba9f14c7ccc13d0637abaea040f5811ba5246f943f9c38420468e5e1923acbc1a1a9123b80b8728b2f7a6bd29714c4031937c58ec74ad3b8582dc6340c1c9b8765a5fdad0a5c831addd2a403a06d736205c6a07c050ccc126cf84bf7844e37edaa2e14a93ca272bb2fa4fcabb365e0f3b3107e282046a26c01a4f6aa551d65132a8101511922e84f9b4b29332c99e5b0226ef48bd938ba132114cdc44cb86892534eb94324e03a5c8f4b0fc1b76176b8323cd8d13818671a28052e2a0d554c9ec23197ae83171a3cdf3571e97a2adf8879c7c1623d14a5fdcdb4dfa978861ab6800127a3725251ad162e9656b292ec6d38e91fa4ab2976a102e2d8d93ed4392915ed5c43cf8d77622258f621cfd4e76d81ff6db7a4b940aa447cfc3eb91053726e2a92847c0f5619c503a40f428235ac2dc2f3a4b707a64dfd0197a8156cf7a759f02cbafca8101208117a3b58e75beef76d547a76ad4791acd89f449177c7d4802652c933bc3b0f8a60c860f1a48a9351d0a7776cfecd41378f663fa897a92f3376ffcc1728894d085f7a36a313d7f522868b3cd48b925f7c4ad7222be7f82b75541f7942104ada65f25f1c07eb84380e332ecade44be5cf64c3ffbc368d4cd95dbda00a89583dc86eb683198918123076387fc819ca49248933f28fff37006f3df2c57df389819403edad153f7617064bd63ff588a9bffd605675ec6b721109584b4bc8cf08242448b07752dcdee033bafb472256209257137913f29263f6454f98109ebf2a0b8033739b71e68bb21b24fc0a51a47fd70c375c826b7e16af3ca2e1b7b8dce602bd50bf713e251e67b32eee6c68962f00a7d1507f83b9329dd40692f59a30944fa0ae3012d8ba1e144ffcb58a24fc7b0b575e46c81d3544f186e01540ef00644e31de978f92cd75539217bd4558e8f6d01c11f59c0cc774e9f17fdefa95eb81e57b3d485ce0c257b4f52536441303687a474791bdcfe8253f747ae3276b33fd744836b273d73d6a47ea3350c1a27c7e04b258760bdbce164ee8849220662877d420c247877503cd60e3311d30c63f46a9f196af8cd586237fe3b7db5e4e8517e22a0c161688f5e76402dd249f459d03ba057aca795bddc8c23b407fd25b4d7c65c2bae86855f0de2fd258bf49ecec45c46637088f50af9c8c54783f574c8f3114173447ce960ef461644c8f138f47881e28c32e16fc6d01a97e5af5ca00d47b95a7a84054512b8c65d302e3f728ff846a2336d91735e6c29a75a8174153c32af18eb5fe149c72233ba66b097416903889fc108b4f2824b2b9e3d2a8ed90c837232fef255098d33c0a6083c2e38dd8540827553a85900f2706bbbfd9cc87c907b42b142c3f9d699d98e08c9b7e4dc9b9be07fedc818dd77831dbc2a4f3674f3e72b2ff983647a0fc845923b84ed1ee6a82a1b4a09b5bc64855fa25f133cd83a57e3d7493c960d72bc2bf84d974e2079fe01118d6006577aa719350354fb04e04b543e8d3f77b5a757351e1aa0ea5809182ac9b2fbb1bbec1ad03c84fd0ddefae823b8b1ad73ca5f6cb0593398b04fd2edd4807aee968e3b8e1293daefd742d34c23fc56c0e350ad9e41088a4678f3f61f96cce31674feaad01b9949d835c3850a67763f0e2dcb61890e26574ea187c0153122593226665a00300f379c1e2480304b2f0ddcd501006b1ae47014a434bbcc22e868dd6df876e06f71ca3c0874fc31572408be74ee1239cd63c0a469ef4a0777fcb3618294cc040975cbaa7ec0d33f7f5a68bea66d2a6dd1ef23289399ff1eca76676d532df64671f6303fe0bdad0a911702377233f811dd31c73936b434c1f9b09e473350f2a7c9fe825a62eb3103a7cd81d77ccef31475b9a0b3bfe6d86802f07cb7bba6e2615995b8931b5ad1b764906843bbcfe720a185fee4e7f2006f0fd1b4253d296a7b74cf3b418f686768456dbd5002fd350c455a7c04ff9145b73e57787120b09d80d893e0ccb06ab66ed8f43cc5c5a13037ee3dfb643411c3cc180d615a5548f9217d7b922e520f3524c89329e29c9b8f8c1e0f95bb6d18b787d88d7d831df7b50fff47dcb516fffa6dd0f18e2bdde9e570d6e84ee06f80a7aeb8873ec8cfaa4e56a0006c7d5c6442503318b186380ee77580ffbffa4dc0b4aea215d0202ba8926b26ffbcbf6e5e62c52bddbd02e50f8aa3839c0cbd18bdb8d3a3919e3cb88bfe35fea395a7e158e48d6ebfcfe968946af4dc9314fc86b8de4645b1647750081b09715ba1dc9d52b53e9a5285ca0d42d0b750d6aa6c45f17c35752ba74c973fb5b30675c6f4825a38fb56217fece623427cf0ae2b1c01540f4f8c3060950f2a68644080352e1e376222d8204000807b7643f386024de96a2e2b75e0d1a60e3ad6c110cde58cdc92f86d34af841c29474babb484b7900180ff19d37e1285f985ff3bdee37a7fb464366e2f93d2f8f3460c6a50568f5ca7716ea0a5880d9be1daaa1ffa814245a5b88a32e419fc6368b3fcf68e3ee6dfe9fbbc75e6f7d7f05e4fec636c0c7ead5f1c31b668c71aed171838f7c97a744f5f86c1d856f177ac63302b9e8b28d10454540a90e446c28dc75cbb3085df648c47e4eb7c94a20265b68ed8d2d34e93081705a2d918367365089e7da3eda20d5b9527b1ef81eb17abde44c1fe0e32e04ed96f2169587d5a770f63e2f9f994925a67155718d575904547db46b77cb10ed6ea6ed0de77063ea5a21931a5b5234b4594c501951d742a31a78fe6836acfa40af3f0390dacdb0dc42dc5976896c37f184d0f957eb7a6e793d827a478c8e2ff44748f97305fe9608cda184b68e7a2d07133ea0bc14c9bb85781df54a0180aa3a7dc44d17aa54a53caa16789858887a621ca9a611ee3a6cc3ba1098025d2ef2156341102789d892465bb3b78d5060ee44abae2d776e9d0c727027307d686060632431a6c4642b70f7666113da345324b1dbe8faf0d943296a9e4e4e0f76306c343669e52c2b382d90a14c5041de259ebf8c16fd6e4b8d764811aa8d931fcc637f50edfb047665bebaa1a1c662e2305f93a22ecb3ade77705bf9bad02ae45e9646c198eafbbe9e1f250900cc3663369286021bd3d58d2bd730b8da2261b0ff9b09ee26bc4e4aa529dfc19c5611a24cdf2c9f1b1bb60e24ec4d0fe85b429087a05c3e9d464831f68af628d40ec355a4d7fb2599eea2fe9b3f521490f8fe33598d333d0535d8dc1fcd50145f67a2644c54a890bdba5e77a9814d2e2dc9272b24872e6880972a9be1be2fe291e44de29bd0ac7d7f00fe6913d9f4f65763ccccd0eda8a3a0940ebf4da996b595f6600612f777927cdd7d6d8ec8a4087447cc32206a6e65d4a250748360d5b85bbc3868ee73bcc4b155d509fb5b0dabd43e19355a6de31f4cc7f2bee26223a949f64ce454ec0297f53c6a9ed276d2f9ef9e31f4d589392c2ea4377020ba002712dab6ee5e91da1ac9791159a44d6b8e72e00216feca7ced7c295eb26ebe94f5e4356031a9e6fc128e71c85eb512afc3f5c2dac203b77981933cf5b2691ed86f1e4888d9e58dc66bb9f3e3f46bf70fb7bb8bea1bce0e7cd44fa4614fbac579051b654b3264294bb8304167d41dee78a841f5a6f76555f9bc2b326902fc1851a30c28668c360218e5fdee8821af8089f99d4759087fec14c8d2c25c325b072cb551bff86f60f82306150c9691ba8d68ee1296ae88cc744190e649aa1346c4a5301d4af00f7b4453f3f2841e8aef0cd71eda0cb437bdaac66354abc5d6f930183f5b17bf1fe85d6124e29a80dd938a16b63df859ac2e4a0b34897f3fc36fac1f57f5d92fa17f0ea7ec656f513af15b6b25defec6bebb784d74a277fe82df6f21f20f85b805a129a0878e344738ec3698d698cc800f3d5561a7e64781fd1d03a1ae9f0ce6723ecc04e4bb3f0494d6dd47bf96c90b0ef872d1f10424d777c6b17e4aeefb08698d5702f04c460c56648dfbfa9b0b320ebfe050e69cd914733e7042ca3418d4b1727fca8610c9b020bdeffc215c21dc9eb42ce16329b81d4e959822c777539eeda88b487aca88611584684942cd05c79eaadf73957d4fce986ff01713e58dfc40771f0f23af666edd6231a6d854f521bfd6645ffe6462c84178091b115f37232f7a6429b86c16c7da4d10087ce4371701e8ee1f9f523787c1e7bbabea20e5ca0177649819a92b783186ce76bab07ca1432538c11ebb0418133da116a22d70260e71a6115833b7906da203c5614d4d92e600d9cc367093ee51f16c32da24e9cff84b6fd4b686539c8d35bf3e66b07abe93fe799ab10a32e9b950bf23e7ae190bb5f4d5148098914dd842817a855627c22a67cc9c55af01f062cab43813d4acd13075a526a669feb3437948409f200feea6c9d1372c07b097254e4c2297438d5a34f6be87808a15bce1260557b66352bf212f3f21e6f95accac866a97348223ee885318b1ad3650b8c2ab9eb503c3bd84a16d75bcaf19c1128024eb43673a724a909172c6317e70c29c4a20a12251d0cfe93041cf2dc1b86ac7f04fc9739c02a068df6d4a969e918e8b1ddcebb139967cb4ecc42c11f3ecd6a9ef93cc7c7118edfe0bde42fc0b20087bc11306aa1ec3e7c756229ceeaa7f0fa4cab68bd022fb60411599a1f5bf3890d221d9726a8cf2ad7da81601d946e3bf2df83964bc3c68caedba96abf75906b7066485d20db074242ab4943c4ec69adcf212597c07d0c2243b0b0eec39ef85f910b5557bdc2e0f1f710e24cd119564b5924723748c95496fccf0278c46ff2bfb0530e00b882b1bb3511bdec14e426d5901031d24b201aa21b4c6db23142eef3455b3225c925c883445b4bbd7b9b5cd59e7a33cf765d3265fd9a324a8f30330ff88c880cff77a994b1ebd205aafad240909a6dcdd5668ac90635d3d4072b5c80323f5ea42a52a76973a365c2891a4a3c59f72e224e30cbbf0cd00fddb25e39a6edf82ab0983d36840f5da00e625a7c6271880c58ce0217274b0dc46495721100fd45823f28f1163923cc1f6bd9df1d74de0a27cb30e4e7af78d7de49dbc43dc9aeb3d71eebc59ca97dd64596821f44d15cbc7971cc1a01da3e9195226c54cf022583c2ac34ab120100f89495a55d1768a7b2ef387f2f3f01143c36e7a562a4d524a1134235cc22700d9481706b8d6f081bf6d1b28b57123ac35e08ee4b778e5b0d3a085d4c0323c2570fcec3873180a33292b95183261fbb6de904c9a34cefac13c00fdf51321c5b9b9a0323a6ee468445331aba11aa55e27b5e412aa41d277ccc465889ff6506f40c602ca979256365f0a8373f68fdc03c196c7783832b618f119b705d8b75ac7cbe5fea86369a6c96c2cfe871ab7a5a8e8dbe881652ece2455863a1221c4dad90232129e0a4b21ee3040f0978377af156914d8973fcb6afccd0450456dd15c4be8496b0602f59a752810c8a819221e3cc0949bc37eae98b361e16ba432edc106bbcb431dfbdf4cfd51ad8ad489b01e104881bc33869c0c0345277e01df58f214fd4eced7ed5ebf3c962bdfbd93f0ad087b01865819649a19173e93cc5069a283ff064c93f2ec0ea0c12da462b4642e61708f93ff489f60eab3fdb3566f61ecfa442b0783238fe56ef2b5662638ec6c215b13d0daf0a873b3878b64031eb78d6c8d7d107611b74b080e625c491e5e79c23d5c2e58031902fde3afbf1e6100323b2ed269fc0b9a45292797f1eaac0ca9672d5caf660068c567a7497cf6463493c0910431a3891e8daa17280fb972fbee82804efb2e3eb6dc63f0ae9926e36cf85665e467845f3788c0d5118128f38116c6d60a2613857229f727ec3b758de36e8800058891221e98e1a437f31daaa95a65fbac71d2f6a68016da3d8746787cb2066ce4f460423d638b8e01ac6a353504042265d0957d7a40de01f48dfa5afa4d9f9014aacd2418e46790057d68b72ce50ebd4752e3ea7632c1b1cc29b1dbe6f0021abf6d8751fa7e89cddc59b92043cc80bc2e6402e2584bd9b5db868cc3b575205a24ce229bce1d5c7b05cf274af05b666f8c2677b7b425207af20a0697249fad3f27547dbb8a05ad350041e91faa19dec092f4389393d432f1373a4c163b2aa8dd6f11a12006e3c50a89c6b5555e978c1ce80be34cd6d8fb8fb5abfbfee4e89e4142906e0a32b9d20e921afc03de8b4813c480656f52ae659917f55621fbd97a077044f900b9ea6224fa4ed46b60e4b48599cfd8635a580a0bb55ce9121bf9434d75f2c4e8c416cec0ea702bb3d084e93682c31971a3a1e2f691bfba4f06f8867ddee3979f315ffc4bc565f75390729d0b029f144db8157e6769782795416d83f4221023799cd30288cc139fb776409ba36dabbac49de4805e9ffb551e463501fb7ce79f8730c89175f1bdbe4fde317cbe454ff151238209d600905d78f3c4a59226f0a6efc018976acce2ecfe8a4699c22c280db2eaa003c1773d917eaddd6f56eac18f5e378105d43b6a38c69862e61ced0ca975db46e439fe69b03b7f76057ea6983d4bd240c4d5b13c4f6cfb06c8ac8cf5bf78670f3033b4b60182e303d92eaf4d5eddd1a8c3de0bb7139cdde96c4f32707a3affaa199d4e3681c6a66792ff229c32eecac31a5cd927823c2c7d13b1c27ac2c0c13bbeab156b21f1d6b4c37c959e3e53f0a8dc5febd39e7ecd15a0809321b04e22b6a4435246e48c6f5ca5bdc308c32495631db893ab3131a01c2df0f1a4f048b2baebc13c05e03239bf86ef3b1b467d190359b099439d17d7b928d86967cfbdee1122030c1e93b2127f8bde7c1bb1b8751961be858258927cefe8142344bf96beaaf89529c49bd20d40faf4bc144006f382f583554412f3a980d4f991b5661f979ac0703cf35f300421f8ac82bed1791ff60c69b35afcb9619da7bf198efe66b6dc21b6c9e3fbb4e2af644f389cdb19e9c0b51da8795f77238d88796c4194216de2128d63396ea7ecd1976a7998f9acfc8241aa69bb30fc7dd477716d623a0c5c9db0bfc870b47dbeee7d8695addcef0050769eb384243d2a731f61031e4f4baef747916f02beb96ee2dd01d8bc65de470225170206575dcab617f231b3fbad037ae1cdec44ad4a2bfbb56015042964661a292de762e2f29525c3d82246dfe446c1f29e5a4f5d05c80f119b5bff6b4b8491657edaac76021fa1415fd9f03681970e4a65371b87b5f28204b69a50eb779393c8a2e86fb44c8b3332a227fc4f396898d1f63ceb51f2f2542d3805b8ed173fd44e128fed360d390624d662b3713188d5a5957b7725f35b3ed805775dce51be748b1d79c9aff4da45cdea2a732cf7e5c16227333de624b1b99192f66e1781488bfe6fc0f047384c897cb1eebde30e6bac3bdd4462a628b722c1b8d63694ba1124bba43a86464c9e6f9d599c214426827ab3a0a5f9e0545a02f75166e7b17b5fa6f34f09e5ccf5a7b522ce6749a7beb8bb4e57db31738d129f1ae51cbf780f288a01dcb49e1e09cfe9f1888bae528879a88d05465db8211346cc9b9943d10ed86ee5097cd85f66f1ceb04e07cdbda814fca631a3d761c00a36aa1798c401e7bf133d613e0edaeef4fb2a7974f960979970acc4faae74d8915b037182d53aa9ac891139fe563eeb6e1970a6ccdbb4f377f8f79113f2c0fb22011f815ec99a25e1400a254aa9c70295197a6e4faf085f8118652e5233554296f90b19496c78882822ca4ee933f117e93de3cf6060237227c70f0c3b0b7e2da25e38714ae8098604d86a0ab686735cbab94fc9552208b6231f9afc86d3a3f49f4d88a889be5639a04bf97a2091a9dc647570d01b9b3148e05b5717a11adbc745675f8858bf19f3f536b9f3b5004ae4cfc8e1e2ef3a0fcaa6880ce46db6d3b1425b07e292f5ed03be4a5313e958c740561656ecf1fffd8b19f3e278d61f6dfff508c25267701f528bac53b915e80fb1cfd95572a2fac4b1ba440fac5d3775de32bda9c3acf8eac03739d8ca2484e393ce9a840fb6dc2fb2fd7986d1f2683e725f0bcfe8d5bf3f30a86dad41cf5c9bd6211a978364ef1202ef698f06c8dc75313f4e344d4aa8268d5c134c37f030b69b4123b3da7625168e7df902a231e8f6c7fb4bafbc739f0f274f34e639b4a7b76eabf2772e3e23b9bddd2187f4f785ffec9efe2d7a10d3690a22d82c236a6bafe2399a944bcd25bfc7a8fb10c3841b9607148b30f797cddcd7158283a0e938506192760a142b44f69a93edf26e3e0086739ff76cb4b6ea84802fe5433357cdf53625452b3eedf0b86bcab2c98ddaf006e891128d1621b7bd9cfd762db17651d9dfdcf0eb4882b3c7bb790b299339e366fec9219ef82c8e93ff70e58d884c5c83d8eaefa56d9f53817d378bf7b3afd6424e48cff85a4d4500568433ea10ae8f02fdadd1ac1c0dfc18e803984275f513713c2507242a0bb3cacd2630867fcb5e7518b5d2038c34472b26e8975dcc8f63df9155aafa01da61369ba8bb835cc19d23f6c7d0ed8b5fce09f7e2e9817645505832104d90a68743f32ee156904f0c3f24ac80f428f18af9df6dabc8470fb9edc0b835cc331f02dadc9286fdb00ee869ae1dc6c4e6ebd2ce8efd5b5566f51fb158c92b61d14fb1b6a0aad9586acefe1fe5aebdfe3c5c3d20175c0253fbafe424b83cf3d9606c73739cc4a10f6ed061ffac530ab11584c89dc602227920a25d5284be6f188fae58ccc5bf901ffcdd46641cd7727ff3c359afd71b3a2a445b84f2e111ce6ef248878b2702c55b1d61d5e1fd88a55ecd853a03ca20c1b23ab4206bce9df250cd624bf95e0c3ac23d14c1b1f955acc506c425f6ad2bf30f175841f6ddb76168c43007f2afbd1818ae872ca9196ba92dee276847deaaeb50759320c11efea784440dc1bdd4089a6811555fdc58840dc6f8bd8ec4978b4ed2cdfe24124434ee72db62bd6deff7e3ba37edde17b3bc3bfc6ce940c50b4ad26de1fcd8e894869a759a9f1465c96ee7fbaa03a132a9ee7aef3f7c9ddc1890d1618a83afa2256e0612c0fc650e313cc7b96cca477a03d8801e645eb1d3980f320be57e057743eba31911963e793aa54d2c286e5cfa3cc4555180da78b911e42255d2b41a16ca697c6afbfb373cc9dbdfc8c2f0a480ce6e7098193f9fc0d77333f7cc659927151ee9b59c0e0086d46ec686a08112024380279bafaee4ae4b465d79d0672433a949376b4cc5dbe22e8a24c1302e219b6bd6221db21194bb79211ba51378b50728a8d58cd208ae5e0b9c554a46c3c7f5da07aafeb74c5d1f813209a337e3fabc14977047018fe82d5805b7ddcf70bcc66887c3540c10e6e077e15918531122ad0351f1c675c349778fbfa0b29e833d4ffdd83c00a46814a8c4bb43b21dc9cecc56477232466e636b88e43bda5a641d5ebf72c5888981997351bce669ee7a8d655fc9bb3d5cb41b95de8265809d7f033a120ee013eea6e50e400b57f27679d42bf0d2c437d708eb74020c96d604f833044c4ec53bd85ac482394265c2a4ac2bae5895122d5f125afbda4101188c9fae6dbfc51ba9c065ff7540153df04807e23d9c12a1a6904af425ad4353c9894ab0230dc51d4ea813b0b46d179ba783b3cf27165f099ab3d21c7c1af6fa8982609598ba735366115349725c65dc93e0fdc2d54ae259f063556c2d19c5317b0e7dabbbf6dc724879fc784e68fb5e37d5f3214276c76cbf18c53c072160a56b683f27ffb0f0894b64353d01de0cd99d329f89a1fe81a129c9da8017030883aed96cfeceae71405950327c09719424514d5a698e585ec296b38e9604ff933d6c44cf1734a8a623c634d850128a5ff5deaaf6243aae0d414948a8039256db9759bcfc85ddb503638c2c2c1c3267b271f0894245a150e56be4647fa2e725c88e8de18396c519829940e5160112d980afe2032040d78a90205fd060546a7290010352041d6300fcf8d33b71dafe7fc157a89cfd16d19d9b23a88dc7e62c457b40af92a7cd7461e0e503ea82a60de35f1bd84fc98f0b55dd9b0d380cbbc21a66c15dab5c574feb16e62e897253cc022cb38ac896c8daa28c18aa39c1eb1d0f7254890efff31629507ff17d58b856dd21c832194d28320b81f1ac1dfc413f40d852ad948924e9892c9cd119bc18deff9159a3a5bf73151f101301e7aa84ef31c5e5d010965f54af0f218b210dc3a5dfd2ae5be6df022f0b46fcbd3930624d7bc0a9da5005ba88ac24168c435c49079ad1a77728036d45639244f78092e3037b46d0b2cbe1690f01dc18ea7ae2d58c64230e504f6272fcd3600779b379bec4899febec139f534c7d42fdc091e15256b92445d7701cb9b1fece634fef73b1cfb8acab74016f54c2e96ff205808e9e7f64e163f070bf635c14d51ecbdff4253c39f24f1aee95566c753f290ded7fb2e25323197d4625cafaae91f7a8caafb00bda6b7eb742a5a175ad4947f516eed15d4396d04cc12d2e9c7891d08611f69f48f631d86ef1f59743c813db36c35bac31c302df5f7842d0423d98818ba49c72b94db52f915294f60ce59fab9f939613aace89487f49542e375a7d21bf887d22273867304e3232fccd098e49a56617e951d21de10af4de369b7f61593f44bccb1daefd9579eab62da33c36ea8f0d965be9f68eaa88c73fa5e60c26c1696dee3e16a77d85eb6cae76ce862820c44d32db94b8577810afd39c18d81122f638a3c5125ab9361bfa8ca72231a74e60573f9112c21eb42b3409975d5804341d115243196d770fe4f02d304e4cb0b21fba77942f13124298f9981f55bf19303c70aa65f3667230d63febfcc74b91f98401c8a6a4ca3d91e915465f6c29eaf5031ad56e26698d2424f12a69e33f30c2c4f03ba6627ab079fb97f7ad55e26f23309eea7d0d87fd4ecfaa7461ba44a46900cdabdc3bf0b6afff7dada0ff89ffad9d41cea450b31caeaadf83aa9a5dfee4a3d2150486eb418a3dfb12f28de97f2d05809f7bff19e97e43d200c7f4f666f9c7c4960f29274fe1218ae01844fa1159d29e4069f1e09d75ec8e10194aebaa95d2e15d2aee639acd2c24add86def6bbf588aaf52915011c66709564bc500b40e6a42768e3b114c7be7070259104600470afa2c0901b67c7b270d721ce06262a4981e40f31bb77fe6931ff3f1ae20d05baa9eeb6a4f3296cd436cd69e55883dff60101c4f8e40d1f0c30465d9bc97b81f6bb89565e52b33b5b4e01e694dc00823940f29a1034edae734df65e14ed01b70a02a2e490a1869cc9dc2ac2271d3d13090ce85c466b98be5a19339229450bcb3da730ff56e02be7371ccc8f41da095efcca83a0f29ead3c517a80dab8f2d1cee442cb5a7e9d6fb7b40d2bebabf6e818a25b7ecd791b23043738b31f741a67bb86cfbde222f79e1c08b7d0dfd265398bc44021404eb8b4b0438bf0c4722cb88cf01e1f214bebd4bfba387710ba01d106c5000f1b88ce2ee41c861ec9e89c79be79a1d7703347f395fc84dcadd6ec20fbe0ba1c49d061113f585cb885e55091243779b226c01b0afa54e48807fb7728e80eb466c814a883a8a95ac3cc7a93053d6600553cda96802d981a55c273e266da4ed86d0af2fc616247559b020aeb192a71a8687b9fc8ef285b8fa43c37577f7e693d87e39b4d19090178638d4c42c875804edf20b4cc6a4ed3d5bf5e37a1abe3581810a74251f021dcbc46b97a634830978b7f7c0dff35b73470099bfa00691f96c78e89a505ec349e560b618b399de6c4c500edd216c6fe7b976078a5d7593bf5016370b8c85cee5a396066686545088ffb10655efdaa2f05348095f56825a3296dc2686c184bf4c830eaaede69b5e790a2a9608e5567ecf97fdc7370b8539f507fd9a0c907ef84570a5ef17b232bbdd4681735185ff4340e1b9ec92b45e7a34dac11eff45e0d63bbfd4f962f8d3b466d32708f640a0d9b88a5b364d497813d18e775ad7ed1dcc4de619177ad6b113cadc198e74692414e19590ff2d79156d4dc6d0d9e8ab427088d3dec7dca570b66d954972180de0acb77ff28047873c10c70018368e335161f68a7e13941a57f33d5ce3b2548057d752fa3e59f5ed86cb24aa580077eec28bc993c70cc9b20e8dc6f0593c3942e2104c3ad8b9a89030fb8644cab284180e8bbaaae620e787c496a217aa6d94756e5afe3ff654e4ea53b465d88adc4c8b9f1e532677b8f6bace49378a1268e4508677c82b9efcd41d340f3c01d3d43ec89de518a3ce23d7efe3e39db9c5f5371cb73d02d92e22c7b3834996989518c09b9f05bc65f885c1383cc40dda319e19ac0c95d3dda2236e710481a2a4b7ecd332d0bc667878aa25709018a8650c3d0a58404473cd313ca832f65a7f530e6b6f902e6b55b87a234c9634963a2bf0cbc8941315d0440c575d68760f6042e0ee3733775db90d51a0a957952573db2f0548ad119944a37693f4c81124fa5ec93119a5bd1a7aa4a8f022b9db5b3191d2b25d4509d4d113d87cdf1ebfb00dbd7d5d5fa56b13fe61f6155efc96e0b2e339c8507ec7a4184ec04dbeeaa42381f3e41efdd297407c01e743070a9320090a1cd00cb8d52e8d0a5a1c1ae693957f4d69ec43b233a2b864a12efd58dd8b815b7cd9b1643dd4d79ea578fd3cf412c8971080fee69bcd253ab3304b74262df68c545a0c94e5560ffb925f08c2706ff065d850ac16f6ad48858b13f81dbabed407f93189744775695fef3417b2f7b2c0132c629a2b4443760465dcde711ae2fbc820bbdf6be730eaf74c96c763644ebc72712cbfbd89ba698dd65ddad5c1941db117c4fd1d7f317f035616712ab6054c6065ea8dfab6efcba848143ce4e36601abb5cb86c48a9c1ea97a00f9c17205091d6bc0e95b05db42b6d211cca4c7e645002d670e39bf3042554a0f84b76c263349a00566886c9935af571e7a7b00888b71cff9705d38728eb3cde1ae0a776515a142c2815b9f65fb0b0a34ab9087f02295277c857e5b6572bc890aa258724126845a6c349863e6550e479e8c30e8c3eb3ab9950e64cef55ae7bc0e1d292ff944e53add96f31d5375adb0df1a529a9ac450f93a8e27b617dae1700aa7ec5f0d85a68ad81bc0064f9acfdc0185353d9cb8c0452a4b5f6b998385a3c80947711240048836fc6a670ef92780c3f01431d080134ced417602a2acaf94a9b9659a23d89f63216e2254dd7f10c9d6a76607a9de1f0cce4af22fde5bde728e575147e1e7ee2429fc77552277670c07e52c4c26f120180c20c5bb52ddb5b6facc40ddc94c1d72bbe8d38f90833534c57b0f30222de65ee030b4f8685325187fa6aa3d32a56e7ffec791151568e01a6c75d2ab2c93d8104f8d72b1f238808bcdf51d465669141c1e08870c458a4405842ba6486288e0296f8500726d7067f930195e14d53d7ff4e4b7fa563403c9733fdc7f335f2b17dfd6ba3690fb662a0683f30020869e2246219701d4219de388d7783260c982cbdc9491accda9ca0bfe0d15619a67bc743ff10e8ed61490317d5c53b2f5d49219d33641f5a0776abf7981a44f466d5b3874b76c2119afd5b051ed43fac008047e63770feaa1660bfcc04f2ed076e85c2ccd3fb29a2ff0b2bbfe7027c53236e83f36d17e9173f2587daee3c2dedf4a8aee48b565602b1582c8f30ba6427603a97100d40cc2ef0a1485b68a3bbd078605262d6fdfd4796ed2f3fb242349976973e5ebd9d60c69b7aca999935b36697e0f1eba8967d3c243df69200830b1b5f04b144001352ab840793070bda171b5cd426880922bb208d28f150425ba57a02a363fbf86c20509b885a6c8e7044095288c4010f3dae4f07976c657ce783ae500d07cba1ea73dd67962126cc3222c822fdcf805b5539c45bf17e9919b666e262b4029d0cc3671179bd30db439c205125755499e822a25bdee43c784812e71de143fb442256d906e639823f28b63b68f933ef9ef90f5b07239fcd20207f78d20a0f027b2117d9776b35592b8a7c9bb508f9b28ded4264a5a40f5c37ab64665bc72f0bcb29db06cc49935691c1a0c47f5d8d5c015c3426f04cb2c6ba957b9b4eb778b9518630a1094690852b9117f1166119c24de4fa32a7859fc04426467a674c29bd56539dbcc9c06cfe18a1162f0872b016426233786e3ead20750ae7fae173b83cca3ce7a9d07fe2ad29d76568ed1fd2c2abe7b574c5727ce39e5826c64950521c9cf7003330ea00e53526a0356cc55fdc3bed329e0db188bf3ff76f53966374959b13e5821c039e2e09bbb951ad6c5be9a3c42128b87b3d4eb932ad82b5f62c272274be133cb7108867dc0ba57b630cd65caeca1fd04ea4118671859ef4400a66a3757a1c7c2660f57ea0371cd8b24ea558d992149888860d402e3a98db08cf4a8125e09f4aec8f7decc2b2898f3026c5a257ff6423063204cc209847f8b263b30e760443c3f2c8db4ab3ef5b58eacd40751200cdc3d23670409be3adba86054b5adf1f378782bd94b29a8b9a18f6a27c2a465b0c7aa76dad443d42150eeb0ffb44bf403d8e04b255731bd7e8405a65d89908c76fa29e59935c902def06d181e8e5faa63f36e3c9847065d306a547b8d7fa5d9196f2305a7fe97de608a707d666960313f9db3e9e7e691c0b77cc930541e5b7fdd3152c91d0b7f0d4b10a13179812993ffb71eb46df48da247da832a10cfee0b2cf01fe2070ecbe1c18da2bd227ed4fb1fc4fe21fb923092b7201decd911311aea492341a0985d2e102a20681a8b78641f16417e6f6342e04b5a6f86ce11e0fd07e72533cc56576d771980b283c7ba19d241a474d4bea847fc33296fcf8f71c73db894676f8b79c9869f897a6912663d5799dbbf7c4db93fad8b0954d0da10f60c66a6db7190e77d582ad4ddc2d576abe50fee5040d1b3b825715980d9f4d85cb32468fe8d43b7c1e671a263d14a065944c603729d2a5fc923a6760731e074385e918a7024c43f4c89b408d1c4cce0fe9252b8c4eee0008d5d1739c474eb33e8a67e95e8f5aebb38b80907dcf827dae646d5d2ccb761799b992154ecd50869c65aa788c5cd685f71d7d0fc71b0b50a3442765d3d888a2fdb863668f6effef42294ca80d7a94a893ef887941a22515cd713e3a265f1ba3ca820a9081585bbe34afdbdced88712378368c2c97ded2cd9aa815487783418886ec424f0ea5c9c3a0b549588a77db4659505c50c3ae0a2d015fef6b97ebccba9a3583dfd033dcb6a7073c9d0e9dca176c583338001aaf8044c3d70b0fab9f401e0a9f50cbcdbf8a738fb5f9c584d36b1a0b3189464cec513049dec5ffb4a426dfa4495bb4a274400c69257fdd8e749de73719a02a8366bf937552cb95f70e16de02596e3a09bcaf54cbd08c95d58e6e70747a432640087694e41ceeed2380a54c4030b993a91a10eda3e6421b4021c1536cc1f48d707945113c97e85983ee40ef8733d69f595d8c415e822bce2a96941916a817e64af4ebbd9c696b88dd2fb76fdc7c345f1bf20ead848e3d06bc398a283e32a0e575848094df944a1d69d9a390c9bc2e906a5f3c82de9a16d00e61999bdeab2a4d9d710a25a874c27803630e184eb6c41d2f82dd7ff2621b18a32013f026efe2b95c2179076d9463ebb2a30a4fca7e274fdb6e4d362cc429153860530b493584374aa9e580b9892ccb80738dd8f7d617a22fe9e98367457c6b801e5b6e59cf553c470b607f7f9421b3bb70472bf9cae3ab94a307f66461ba75793816ce2606aaba6e0d6ee06ef477fbca6d9ebe05c9f734cea9edf91b2c966901736d0b911ebb2a91992d4de4478e63847828024be86368876d59ff49cb1e30c98ba21563c817b67f44c2a62b31f604753f1779595ad7b552af2727032a7cc0108ca7fee57f04bb369e20b93bb15a41a24d0bc6730b41e7209025b1bc8292ca5748b4e2f72cb6dd8a97c284da20b02bd6ca1076994360b331676e6bac163c82c707352d61d199d70d2a7a9e44311205dd576cde7ab6a65071c07b7ed9fb5110514d8468016ee773820dd1b477b68bde160bc2cda7fb5f40fa5e4cf05be53e94a8d59363c197688e28f9b4fd2b2920d0e186d693a28acf3b18ca715fb46f5f18256c90127a203185630c1e28f3dee29aad8d0ca0f6465218ad7409fd4a31c2f6f2007c7581956f190393bb6155375ac3bbe31f9371ffced3ed363af8e4314e4d2adac115abaaf7ad6550710cdc554440a8fe2301c79bb2fb468a370ae930bbf3096a3f91346add1c791bf02e3ce45708d9679fcaee56e41967e0701da823f9e5cfa881f88af11fc5f004e4f8f7957895d179e815d40aa726716a99dadc64f5ba11a41b539ec1f6c6e589f68c17be1a873237e479e4eadda1eb1a6742c9734e513a449834a841cdcd196daf6897429b4fec787ee331dfc147d7ef3fce156f6b731c55af0d57fbcbb95069f62860350c605c56f65c18453af5699fe9db21ea85b40f510d6f015a11bdc417cae5fd6ad958338f0b8736ff10c7f38c26d545434f7b55e7d0d87e1af96d6acaea43eeca6111cd2fc66ddccf7c9871a10cc97a4fe4a4f43ffd5965c1ffb38bf67ff4ef4474c91552325916b9a8e1c4aa31f1d187eb0feda6ea06d44c916db3168f0bed60d5e39fcce0718fed93bf5dba83932b84b99360a7f95e1f00cfb56d3e16a3babdf13ce8d1d3cd7cda0e5e2824efb301727de6bd4ae3e8965bd4fd651448cef5c80e94ff02be4a85dd0d3530b026c30100000000000000005c2bbe35fb66df45bc4c29651d529ecf869d644a494a99d92a826b59e120e00206883475773778101b0e7b0db00ddace7b98550bd79a8aa2e89f64caecf9c54654124619408268400c1ba1d0bb4cdd4c691f1d5020f5af7212fbd698f51308a1495f1217be7afe124fa082aba99acbf1a0e910d8e8c467b96d51cd66e4063981e687cecf58e641bf9b78664577f43acbcbb4643239034890214244f00e84049a5854052d1acac2a47f76e02365e2a4b3567125ad82ed9830f828b33e25456a124b26131b97308893d9ce6645688e62f018f229501b96b035e68b9fd029849055e2343ae58baf0ebac94489d7d4472f13eda084a6643201d2c3c624cabfdc68f966acef954479a253a7b01ce5d7974c264180fcf0d163033622718e39089de91bf4e590e0ae838d27f9122d9e920bd878c4ef655a94f6f6f8b933021b8ee0347de65cb67cbcd788ad472ed47752cae262011b8cb8feef644c0b32863a2da25febf8232f46e652604311056997fe44eba7dd9888545d9dce18e6faa62b4144ea3c9cff88fc8b493d04fa43a74c425ace2023437c498929910b715bca42a832ba4e7d7997fb87023608b19d18ebca0e97944e07e15c34b19a492e5947108e6712ea537cd67652033602a1e92cff1e569df386259349056c00e233135ab3dbfb439ff344a36bda79b67ef05485551ee1a64d08f521a54909cd2baf7db1e58359acf39f6ffa55caeec1f28bdf6f2a7610557ec0861efe0c4fbd9fd9540a9520407a8461230f89151944a356756b28021b78c872ded01cb33d821d62a0c0c61dcc6933438ee6946765c964e2010f00000f36ec90108d9f74129e84cee6086cd4216dd0c1c61c4660430ed8a51799a4ff8bb68e0736e250eacd6df9d4e794e95367d880c3a7ebd133ab766a34df60c71c943e7da71bd6d253d1f24bf95655d22b021b6d5884a51c3275c5da8eb1c18672a6050b6a292c96cac61a8c1a2f2795e3e7e8a153831d773ac3661331b234dc3b1ab6537d8871d90336d0d08912179784b0e8ed0d31630436cee029fda0f33ab3618694e7f6b42f794aa9fd0007f0281b65f84ae676bad42a171f19ce7eb731a711fd1e351b6330deb865bd974a26131b62b84c9be63c4b362a898360230c6e2a25cacbdcb269130c99c5b4de29fd4aaca664321122c4c617d2514ba25d93ccafbf176c788175f39482998e666f5db0d18574bc747837a59525d5820d2e24749a96cba7f378dcb760129dddae936c257b9c1164880a6c68c11017523bdf3726155332999cc04616b0d0cd79a3a6fc3987a260030b5ff434eab5c6debb62e30a8f1c39ff217a77dd52c18615ce9bb941c5ded355621b55e8fe7f2c85262f19990d2a5cd24c25151b64061b53d0ec3b469716f94d25296c239b1e73360b6e7f14927717ced32995af64281494dba9a06bed9bd22730dae2cee5159976a513fc4dc12e54d00abac126702f66ae9dc45b748709c991c1ce52294b33992558a927f23cbcd8c6943bd85002ea64c7103a26a22e598c196498f1a3033692b0699e1c4f26d3694c2ad181186c204151d56976cfceee63251db07184c35dbc970899f142c908f7079de3a77449c9cca7c14611dc6bf7ca297800f0051b44c8d24d3db6addd8cb844c87a00d91842a971d4d6750a19abe3011e0050820d21985e51530ab7291ffb60a4f79fb3de566dce513172500318a57c7d492919a4c8b88c1abf50df4bed35a6aca9927ce1ca89ff06dd77f7782f94ad2453837de8db382f4eb272c736bf09b5f52e56fb94ee4a09adbab12ef43ad1b64974b8cf6f2ebc3b0d769f99a2e2e2e2e039fdaa9ef4169d950ea9d14295166db107f5412bc7d462ccd5e2ee4abf33ca35735368511a8b2226747898d22cea90b129937caf4dcb82f7783a794691aba2587437feb57d41f44c58b4365661de7a456ed1c3870817f555b9225199d4c77862566ad48af3f205a5d49dca17ab8415e894c4b7d8e5dbf279155a6ab21251263e8aac8a4fd79dcaf6b8164d536156b99e35b9fa994d54146d9452d9846aab4b9e22f398f1d9b0779b82a660c4798bbeee9b6eb70b354ac1867513f6b1b64a644891cec96a742bcb5dbc8c22d11d57dda24bb46da2387b7bdef1bf79ef94196584c2725369cc2ccbd5e5a0486efacaaffbe8a9a29f48b52e369adcb431351bd4f04499d28c6d94d169b4dc89c52a9af418ee32a91507353871a7ea7411eb309e64a850631359fad6ba6c55bb2254020ff040c16432c40c327850431387c8e9a78c4e1dac93821a997836fcc9644a96fc30964c264084fc0092010e84608707cea8818984f56c08b51765f6bd849e4b2fd96cf8d75cfe28c10e317650c312c8f80afe1f4a1ed4a804e62273738ed671295432996c8007106a50c29474909e6f4283e8f8c30b3526a18cce660c2d1d9593430d49b82eaf27b4a714766464a811897488ed334fd9bdfb86b0a10624faee7c41b48d10116d478990f5c00e1e50a8f1884385da73d7fcccf905927e7c200d351c5186bbcd7162f2ba74de8c31968c1a8d705410234f6eae887e08901f3b1ee0a3062358a0801a8be8e1a38c0b9c31440135140124fdf840063200811a8968400d44f4f051c6054ca0801a87f051c605be7f0041400d4324a04621849cf13758400d42f828e3022c28c1026a0ca206355831205043104202214401350061801a7fe871801a7e10408d3e14a0061f0650630f2435f440801a79e8e1a38c02d4c00352f5b4c45d94ce55d7b8c37f4a281f53f79b93dba1112a089d4de42ca9741d74cf9c828ac94b7416d1c1f82a4da631524dc63934565af4e5e4a2b4de838c1a7248664a3194b0a4921a713857784bf59be2266595f48f0dc270387b1e711957b3bece37e0aa6996adb2745d23846bb861db1436df6633ee37259309aad106fe4d99bad920b732a6061bbcfea42235c5d83215096aac218ba3473de81cdc94e77b811a6ad04f69ea7c49e62adc6928e720dcf494c7ab3842831afeb26f88e878aacf708e481379496d0e76965c0d3394b335a2645db05297ffa103203d800c994c2850a30ce726d1cdfc8f1da38c1e1ee051830ceece5cf6a89b827dec023bc4d0408d31b823cae5cf52c6ac5c0c6831f9bbd82909cb701810d5aeab999f452d080cc8f3743e961d53bcee0ba79c343fbd7c7e4c1f2f349ba74e7cca74a1a42f76867726216734e4c7f6ae408c1a5ce8deb2adbee591a763c9a44710202bd81182c9040535b6a0ae076195414b8a553850430ba9264b96f219e492521652956d6169345e368f05bcca6694a6dabfb8af50b9bf7becac952aaf15ca1c39f2f4994e49b8554044efb9c60f150a5de9ed4f3d2651d1293c42c6b2ab1cabc2475240f654b20a2a768c470139f6f6290711d5183350030a975a2cbd1e96947ffa0968f71c1a632b6ef1e181359cc05dcab165c3724613964c2667ec1893898f1f5b46d66802b2b2693a79612f090f13d64bd1d2b6a73832c6ced82041b6033596504309986acf6142fb873dab643289418d24e0df31f88715a15e4d25934918324053c0046000841a48f07416e197f635fd948e90664e0beff9c3520e1a637f9461468f304eb043043c5c50c308e5995e8a49d693deb808e83a93613afc7cca1c193e4a844420073de881076a108133d96917748a88abc6102e93733fd5d4aab9ae2184763f93ea28e91ab60dc6a1298ecaec7a6a5f01c32446ec3ae6fedb181252041abfc882b2face99ff995d5fb81bf2a2e664abd1a241a0d18b8276cba8b399d7728608347861cc54f94d65a599cd2981c62eced55de243c88afea58b83888c49a798f36591736112a5d4e67c5f71455b3299f880062e30d5992df3bb192e060047a0710b7d2fc7d3fef76174f603882d9693dff09e364756aa16cf0875ad496ed0f03b2dfc78ca747c3e7b576508346671881ad39dd59451e42fd090452364ee3fc388bd34c742cdcadbcee91d2c4c58a04e26bd71ddbd7ec35798315b8893599a4eed0a37e9f0b1cdc4e69472ad40c8efead9f7f6d11f5624639ee98dcad952d4ac22edac285f3ae7bae1a7a10a4575cb560a2a769ea6e214cf7d6f4d848a4aee665c12b731a72883de3bb551d1f275a640f96f1e1daa3c27e929453ae5706195c2bfa81852a02d33d4e5cec2651b05fa83fce4722af54783001102011aa24078ea0b9fd45a6e463fc063c8776032e94146efa00c1aa1b0f572f03acd3db6393e8288e1021aa0309a3a9341e6744c5b29994c6ef02fa4c78f2f19d2c304343ea15bef5ae5ad4d9a691dfc8fc98403343c715e7a2e717fb1d3bf6432e91104480f1d6c2710e95e2e36d7a9e596f4904f93491822e031a1c1095489cb090fb9972e6913e78c7cd01c16d56a0432a447891033ca10010d4d9847a36f10a7737ace967c804626eeece33b42dfb32aa56432994cc2c8008f1bf40e4941100d9c80062638ad1ecbb0315d66bbc790ee61011a97c83daca8d3e097f40032c4041ba06109de76b4cd474d5d972c19638ced5132a48c773178348d4a94a2d9c78bc13694528901031e1ea0418947265d9e71a31ad098040d49d08804e2368e1475fd6b3e974c262ea0018962c588dfbd92cf9cb4150881c62336312b76aed672a743c3119e785df932254aad1e091a8df82f336c9d5f291102030d4660e115cd5b3d261934051a8b40759bde1539163b1a21437e2c0c6828e2e47e4108d13df130a38046227237ff98d3f45fecb764080d4470dee9ce37c6d8e4641206101a87d0845cb39223d6bb4c5ea0610844a8dc0f4a372965e92ed028442ad64833b13da2b288108ad506b16c5254cc9b16680ca2a46ec3b2d9668b151cf2e373200853a6b60e5b39a960e2096804222b9dbd349a96ea6845810620509ab616d5f47c640b051a7ff0d566c7eef46897af6432f14325e4623eb605bf5f4b26931efee3078d3e6423bfb19944c326081c68f00133bf6d8dd92f994c7a84a1802040921000a881c61e5075b519261aa7e5d4431bcc54f43e7d6d0c9282461e10aac763afefa914f15079a716e5a363c54a77b074bd3bb958e9ec6f8752cc2f6742ab7ca8ba0e9aae1032e5df3fca4c87f39ee8a49e7b34fc73d8efb2a4798c0d76914331a8f5d38d137e1bc6c134f5a5c301a6121ee5400e2c7429a9ac289a62d28257c86105e6aff5c4a6cd38ffaa907c8dec5cb37183faa8a09ac51f99d2e73abd29b07f3294909b9302e24307e19664e5954e14ca769554cadb50613e50306a2c2d32fdeabc779e8010379672d238a1e49f5144a713ddcb6942499deda99363717326d8f1b76ba356acb897705e7e0eadb78ed155029a174363a9a464f09270c88ea1a57c3e56639090e308fae893f14d8c58e78d8056eaa77817c1a4e7c44b8668512a11caca504b2d87e06d4c39c4e94a0e2194527a4fa3e3efab2ac138988a0a1a2c9d384b4b180730d49439b641fd8f4c2a0f0fe0f84529a3ddc6ca6019937c51a6d5ae9c84fcad642f4c594dada4eba8ed182fec74e9556e6310aa63bb40066f192164edf9ba705cd4cba63c33e2cf45193b657a5dcbf11e17a8e4b1446dcf47374e021cb730785ef2bad9d49b6d51becda516344558b0167fea0a6af22c93e5d0029d59414c75d0a42bf7400b70cce298746fdfc7aa64321963458043168c2acbf0f9b2b1286889513ff2f6c72f3860b188f78a991577df64893b008a80e3154517b91d665db329ff200e571cc7e546881f19bb4e2561a4603249028e56f4e9d3bc8389967aac6432e9d1041cac3845dbf6cca4e5c00e15ecf8008f32760443760c0e4c263f7ec8901d2305c1b10a2ede865c12cbf64c9380431579eb5dd8ffcbd176c4910a6cd547f792dad0a6848aa2253db5fbac16709ce2f33a5121cf47e61299c2f05b0d152d5796e9944c263d26930de02885eb257aafe29abea4ea020e52f0baa7e7e5e4e8a6b41a708c82d9945b31c7bc29fb248a34dbeac61d2164185328faf410a682dad49e6d0040010728d0a9d495d8c8ff5fd227d098d4fbc266a9baef0964c36f890721dfc2de0943463747b9892795e6c4c9cac654e3998bbf89abc733b576890f91264ccdb3176794dd7599583dfa69d8139efe614c6c42ac5b1215a388e8127f65cca4df2aa94cae254ab9bae7342fd427b5129d8e9729c720cff4a7c477dde26954d218dd4920379af0d1db314f2a09cff34c66d530ef208ac479ae53c638234b9f798306a2031c904874126725977cc4213d0895fdff7da3ff1c04f925e070841b45835ad2515fd146a0d62d665a7c5af50c23d64bdb92a37ad386cb22ce547a42e5ea53de28e2106eff2f97e3be89309494cf1b5fc9cd3244a0eaa4a6ac995752d521f679f354592b456736446a5ebfefa73f57670b71b765df4adab3e58c1a0721ae2b99cc73fe6aee9841fcda15938dbda8780c8720ee982f3353ba9c4cd71fc011081c80e8e4c89eacee775bddd7033c823c0c70fc41937e7946d4355a106160c705784c263cc6c0e18777573b5dea7b90dbf701fb146176a36c54753e64ba19cadb7410baf21e7ef5ce51e3895c19e9e17f99edebcce6fa6d1e5e37e95fd253fe9ca278c07a836c0d133a584cdee12ebd319a654634867668cec34a6933359eaec39d493dc988efe841744828ff24e574f6aee7cc0195ecdba35825cf580e977ca8d24dba4a832c0ee89c4795cafad1435e24c0018784a79b964b4988fae90d6db2a0bfe3e9c90f931bd05b35e9b91beeafb78155b9aefadc291f72367c6adc7aefeada837c0d09cf6e2b69dbb377abe1d3ab3ae319ced2e734243e3e2c874c96b4b8683068116b5ba1f65fdc33d8f1313a2731722cb766286ea964d23cb669ce96e1ec790b95e9fded2b19b29ce9f2c96c4997ac6340ef5e8fba9bfe282331a8a937a9106275848cc280ca22c22f98c0d095fc8ff25fcba2fd822932ae8557f3b42abd80b693ea1542e59f521734ad0a1af7a2589e8f0b2591fbc16478b5b7906dc8e029c45f67570b9f960c9df24cbb439305467afd99d2ffd1b482852a85f20e291a2ee75740c89cc60d32d8c8a015529593f68ef765a7ab82eb396ce69afbb01ec34185520a259e2ba97c39f41fb213c03105c4c7cabfb1bfbca089000e293c1637c58690052cd0411436ff60165f636eccbc6432994c26131f656c8f203ae880030a9c5f08156b6c539ab36432f1058e279cd7a2e6f9fdfaf5e10a7a0401927a8cd1ef650801060e2760223e980cedbf5f630f7034414bd22a3d6b76cbfc8e808309e5e857a242da7adc3ec0b1842fcbe98bfccd127028c15c49af2e76e828ab020147122a4f13b30c0d637a2d4141057020a11cc4427aac52413e029bcb4a77ac55e6de8d50e5f5c8aaba08c9be545f394c846305bdf6d3327a221e424abc66b97d38294a1c42b03bd52b08bf68f605239d6263c73931221b054696572be7519583b6fca268315f6b955b43857c910c3298f7bf8af4a0ecc559ea4b4fe78df9c4bcd8767b5359cca73fc55da052f6b8125eb96b92b13f26933080dcd0852745e6ced6a719e7940bc45b5a7927f5ff976660093770510e9accbe3e684d16e7a1834581226edc222bf77c4afbed7efb31c63823888f0d22860e6ed8229115b7375a44841bb5f0d233a8b0d1a4cbdff631069b51460f1d6c901e2bb8410bf5b2e5a9d9d80a9f30c278c00e07ecd811c6031cb0e38c201f060f1ec598c183c70dfa4b446e7163165aaa9453fa4c217f47e5862cfadbbe0c6731e6bdaddc88c517c252e51c2cea87088024dc80c51ee492f08ad3633307f931c6581e32b8f18a2ce7182af43e5b922957b442ab20ce3f5f8c5810b3e2c62ad6acb26da6f6dc3606c8901e434a6ae0e39f0340c410c10d5598b2deeb1aef7a6572e0462a5a8f1d47a80c423fcf1ba8389ffc10f2738d368508f90e32643211726e9c624b3a2653c818212eb8618a1ba5306da7f74de5a72f850c39dc20c597ff9355cad49fe591d12d08726314ffa70a55e966459ef2862894d310ab316e36a1aa506032061d35b5c624561d62c608f20628fe54aba0b5620899926e7ca2f8593c57c7f6f4182b373c8196974aaaa793e8fb9d28d7afe8d21bb4babd9ce8c3d6cc48117d3f75139f8a99ed1a93d8c954137a6899a98ae2299a4c26fe4ab594ad4ba6d40c264cb1b5e2684db94416ac369bfe9217942cf177f04c9aa63c5db412df568b76785ece1ca344d9c3a5b7bcba61443589f2c5112d6f4a8e852e89525c4ad9d27c278dcb98483c3279a718fb14321bb8010993f0f21c43e3e51c46c9c18d47243d47cb7f319756fa8e50de94b4d0b14ee9e46984e94d778e6bb83c1a3302e1d9e2a70cff418933083716f1c8a5c6ca193668c714e17ef5a80a6245ee7349b89188741e55152bffe50622f88e2316ad479765b1dc3844d9c3575c8f3a274677c310852eb724a62fcdc8aedc28c4ad6149be866c7e26dd208469473e28997ac4fa350833cc7c104b1a3a1f3bc20d41ac1efa4408d1eea1e7c08d405c42ebfda891eb93610f6e00a2b8772f32cf0815a3e80f257516cb84d0b5f51c3f349b1f272f5acce49a3ea4934aed54ea67612962dce0037a6d7a4956f472508f31c66600004ab8b18742c685521943d66d7c430f8997ff4b9e5a837eac643219c38d3c78f9d399e7af7966ae70030f87ecf83ea5c1e454a737ee9087d9ef7b7d93e1d9c1d229e59d13bfc9a477a30ea5da389a448476f5890e484bfd3f4d356aa3690e65292f4bf39df6679243e6f13cb69b5750e3c7c1edb92e69abdd353a1c0c22d4830c95ff3bc7df50921b36c58d79e11a77c3297e54b5cccef3a9db60cf9f4ebbfd7c9ad46c48e8cdf7651fdf54e335ac1f3f45de3bf89e560d6aea33b9bc1dba739b86caaafbd63aab36ad6878f46b334cb67f7ef50c5b6c79ac0cf24e76d40c79e6ea1cfdc95473548662f6eb9ce494d8ed4906e38b6cb9bd6d9231c780988fd621ef94744b8a01dfabeed0a2bb328361e0b27b4e61a363a78260d84b6f86a5c725d1ef0b576b8a794a85729fcf0b8ec9a5c5d6f872f3752171b6a6f45470cdfe00c00837b86034a12e9cd0a2edfa16ce2647a7d469f92f6ba15c3f3e2a8cf96f5016aab91493529684d09c8305b6cfd337bd55ecff0a957bccf0efa3e6e762853bc7fa0b732647a5ab90ed6651f2b64b35a5c295ff825ef24b169d4231bbe88f9bb2769014b8fd511ef2ffefbb1305946f5dfd5957e7a4bf018553f8a91c3ff384e2c8d4d8d6e557ebc1c10d275479834cc2572fe34613cca3c1fadc2e5ec7bdc1044bac5b8afbea41965c32998ce0c612ec0beb9db759cf746e2841d314f27ade19749e66c8e04612aa386b76d7a7833eed061292a327af5fd92e7846f23104c88f1e378e5065864d41998ec260c10d23202a87fd9ef23609378a90af785059c74496bc43840441c20d221cb5468c52a23b3c3c2564f8d831da083786500e5e25ad5faf3794811b42d82f4c5c6326b5de9f92c9248c07ec3868c60e64c60e64860340602318368061885eeae59229994cd8c62fd231bdff596fdb32daf0c5a7a7f6bb2f6ab4936520888d5e9ce33cd794a9cde9cb800d14d8d845afbd3db66bda29a92593c9ffd085717ea3baaaac43ec2593491827b0918b64e56cb59c6c12d8c0853159d8aab05f29bcbbc5f2e139f5dc438e1296fc00628bfae7b4f3e694738ead16a996b7d19f357607959230842c116cd0220ba163b42aef344db3d07a4e2c5e59bc6c5f6cc8023b0dd14f1a46074df6c8808d58a4bcf48c0c97a3ba9f766003168b45d373ad7de93144365ec107396b396af7811a3c09c8f03199d4c0862bf89255d1f3c9a9eba092c9e46cb4424f174ce952f2661d0b6cb0c2a03f4eede60d3b9d5a45eda69ff71b7b2ea954f199d66c29324c818d547c313e96aa3796b4043650918c5d3569a52b7a454fe1e8d7583966e61e9331c5d7b71d7f1b83fec64ba186e8895bcede0ad5fb08220629bc944146cbbb4133532561a8c0c62802c5503df1e39d31c4071b7444e2139e625d76d26515eba10312a9b338275553653b3c44c723bebc0cb31d2d25fdb6101d8e404c56b3e6f246943284bced8c97a961461c837ab0d0d39ecb428bd8aad3347445c820733bd0a188362835569a2bbd9809083a12b1282b159334cf127b29820e4418dafd52ee097572ffd7e0c7a200888e4394b94ef9a6d210a9fc9dd5fb6d649d58884d5ef6d392bfbb672144f57257972e733871fdd031887366d6d9a8743953238620ca57ab92596467515f101d81c0675ffea2568a73018499c2fd82ce18e254d01f0c4f9a4ce58a161ff37e40664f2a7486c625f9e9e8c39e3d9a7e12d917bb7cc00a7684c0013c820019b22bd811021e43d0c18774654e9818ed198450101d7b709414a13bc8e8617ad3c3d9ae7273535f06f5d3a300073af29018ad5d1e2ded4913f150b07613622a9bacd7a06263e8b803ebf99fcefcd56187ce7445936fd1a9beae83d6962b6b16f72064ea201d502327ace25ad036cf21f58e71a7f27b8a222487539e4e29a2b11be888430074c0a1ac762ae6543f5515d6f186f3f8771ef3dc3b1012b00e377825aa72fd36282f7d490c74b4a1e8efa29b31e68c1b6703a77fce72ba1cdeaf5e0363b5336729ac867dc35a2a4b4fbea64dc325d663ea7e8f8f264283d5761bdd72d01c399ee18debe14697cece68318359bbe721a39bd8208b818e3220ea2e9bf34685a6c9500cb1a5d2dc064f338dc1b9a4b347effded5e0ca6cb26d7f9da3fe20f434aeb96d57e25b7dbc1e06796fbe09dba79f717728f2163bc38e59ba35e5893ac1c66c4e918157521357fa5d64a69764b72a158cf66c2734df63f5bf8c2e89ad3144285d5b8408716b0ed8a551773efa8340b755dfa66677c53f129994c32a0030b494da3a6b67ea3cfabe30ac6a534bfa1df0dfe85e8b0c2a131ac499166f943e5e8a88241e7c8ed54e9a0c21666548c5ff9fdc4a86432411d5328e78c7d7629a59825639f063aa4607b908d1936dabb1e3f7cd4e079320923053aa2a0030a3a9e905e3b1397eb9f83ee4e40a676a5282adfe1978e26ac3a52cff45330fd951938780d6c400713162545f63aede6b8211d4bb8dbafae4525437e4c2661e850c217df73f5d35732999cc11bd09184553ce5878f415e2dea3a90e0021d47b83a96fcedeb0d0b72180f70000c3aa0c30824d051041d44e0bbdd4e85d0292d3b256508b1808e21e810022af987c5aa57e93c188756d34c594d96bb0918760e9beded4bc8f259e417be303cddfd88eff63c71033bc4e880482f4478b1b857cab0af27acd33c3620b28b76df4c6e929bedfd974c262df80d88e8422417ee6b9de96873c96422820b4b73d0a2e498858f5e3299f49082c82d30bbca1c841495d4a44c10b1051653a98efb617c3c82482d4a137eea2e4767cfc192c9c40822b44099c9664b6f2ac6264b26132288cc02cf162b66d25154c5a7061159a0b79e2a9b5269108945fba9a49c48d54d32078b7a75b55bdb57934e9941e41576accdd451d51a2fe98aaefbdcd244075915c40291561c26e229e7288ab042d377da9d37135945faebe242c7f33e8531e43bf0061155583a7b5f873c4b7a0019d2e34120920a6eddf594d9e5f598c020820a839c4d1eb7539a785702915378da6de27542353d539c727c8dae14cb66538afa4a2751b7f0931693c210a9ba6bdbd19487a3707d544d769253e929a2287f3389a876fdab168a73fa6d1aa1dd62299508284a0b158494fd4d6a53229f40df345e56d097f5b06432c1838827d0f3fd96648fa6cb8a482790be31c8538db1db2e3a8870c2cd4a3165a2bb4aa8b0416413888cc94dd3338786b1643229838826ccf154ea8aab8f4a9f92fee16383780f914c6832b8b7fdc51ca39a844c262b10c104fe156c3bd9d566be885ce2acd671f64b9318d37a40c412a78f71c4fd8fecb8711f8854a2f2bf749639fc477a6410a1441bec53939bce351ac782c8249ef7ececf095d4764812253f6d72b1ca52562f12e9cf3a2a34d87b9e205197d612239fb289bdcef8104c2663ec8721f208742d8bdc4aef0a768420056598e1af01ff21418008e9518619fe0e80258838e2cab672939ec465762d8834c298e4728bb80a17452d106184c822441411864822d0272c5d6ff36ea8ad0c1144e0b3a3dbb63cffda2a994cc8e816f88f4fc164d2029143f823f4a5fce69c53a30ff876c08e30840c79214284ec080315336347193b02418818a2ea14e386cd79d494ac6432710a8814e2efe47219d583903f204288373767cb89cade53b3194206d169b7cb522ffa22bf92c94403228260e75fee424b5cdcb76432c1804820de689e297c9bfe83bc0f204300514e3b232b878fcba309c223081032ba051610f983c9b57b838a06952d974c263d10f14315bee5b49d2c9d491fc48220d2074f6655afcf39e533f109227c6863063b8b9f3f7d8e6640640fbf870adda093de5c62ffe1234d2661f80f1f1c984c7e00f191830d62c68ae8c14ef152744bfb523d1941240ffb08553e6efbe61af251d2e379e043080f44f080684bf2ee3fe71c174e266688dca12c716d172b8d9e731bd8c105113b2ce3a16cfd4d2566844104913aec796cdf44a793f7f9870a5620428735fb986daebcf177d9c00e316c20328774e8ed5ba91d59edca01cb15da262f55e99cc6a1505ff2457334abd881435f9d4e8fbee811577943baf623fff3fddd76c3d5697ac182fbe98c9588b4c1cfa5df3dab272d436c282bbed774fe2791de1a8e2a26436d12dd292f2c105143ad2953c4aea9f84cae40240d8d8d7effe4abe97ff30ff000801344d090c7351d1d43646e48c560400a44ce2062865544789bec4cb9ef922152867456d3b7ef5c173ea5c40c1132584ad5dbf46589ada964321922320611319c4be7bd8c8fec36a53054197d45d6a64ee9496040c830f522b3851fddbf806b9271476b0ab3197bc1dc21d2efb45d282e765cd2f143c449b9d0a5b0afbabbe1b1cb2da4a5a25c9e1cb560ca23abb1d4a7345316ca23c22a2e7a32113a58b037fc9dfa53f964cab90266f3417f6494d0dd0a59d6ef1c63f941d6a50a7bcec15b53863b530b151253b110a629542a9f9452b71e4624853b2be62b69d6c755251f68912894b3dcc7ced430425f3ec02250c07eb5f3e5cedc614d4f3873f509dde97b359e64fc900ff8107182497ace7cf254c5cd7b137451318b95ce36af2d133cbd1dc4f8a7762d511862882cc1caf8926aba77e39394c0cb855e139e4b7dcc93d025dd4f5d393354d5484057f2143f93f07d9b8f7057b07c79cf59cb424670e42f3b25bb8a3996481116f74bd91e7b22a43abe1ec4758c3df910dcb9955791bfb4d9161102326705954c188cb467aa393395e232070ccc2b4e544688a765bfa8845fb6e019470953f1c55e9d9626534ba954f1c0408e5e94831e9dc434c69cb2ce8bb2fb8f65eef82e32752aad546c73aba20b4f5aca986ed5844a5b181f48428e5c9c73f39250bebe41e5940cf92093890472e0a2a0e3667ea997d3fc1cb7287988db185247a954db8251f93388968b1d460ee4a88531fdf6aa06a995a5428bc3e71af7b81dd3a2b36094c7cb993d96811cb2388cfaab8bb14dc9dbb138a7d3dda675d2ae6c58a47eaa1b76324ea9f91557d4cf9dc66457dc3f6649462b5d27a3565c6e417a12f55873650e56fca172bf9ee6d81ec71cabe8e5b47f780cfaa9833954a1e8d254b3931a45a6409023158fd2aed06763d263ce818a1ca7486658ff74a663c5744a1ee00015e430458f1ca5f0c2c9a7f5dcf1417c39728c821323739299a76a352b0a7b73a7349fe692ec68280cc24789facceb9a41a038a6cf1de3e3f6d3743e71eb556758cab151533c51e8cca5f568fa3e5b8e4e1c3ec6bb6022637acbc1895484dabfa88c856f3ac8b18952fbbd8a86c9073934e167b3e49e2dcce3ef4ca437899a114268bf71cfc881897dfc4b9c0cba4c4f778974d9c68698b398efb343560342725802ab0f572bfdb7a5e2259ca312b5de7534b9693233c9851c94c862c8d0dd243f8327719687d8061173636b0e497417ccb25dce4a55758e48fc9e94d6de8dfb8a411e3920b1c51c32e7f09fb539e91149cb7a4a734e2dae7947a074c88ec13b8d308cb8f5f435fe59933918b1c5b10f93a93223c7220eed1daa3a844c1d838ab0ee4ec9aaf809428e44f4614d537c798c5e13229c75d1fa0f67e9af3e84363ae5fc4d77940719c2af2443e756aab32a7d90a310690995537611134a5e3baa043b8a043b6a043b4a043b2a0453c841881c837053adebd7b45be6680e411c72214ce8fd99b4d63fc06332b9408e4070362a6ab65fee51ef167200c22c2adbb9a75fc37639fe90e6d2a5a5b488d6ef73f801d1286acc2a53d2a51d21471f4cc9b4c673df6bd0b1070772f0c14a4bffeb9b99b762cec8b1074e0679f66f7283bc981de4d0437a1e4ee78cf7010ed8408e3c5c3ac39c8a0be5c0433979c8e47ae992dfe90ecac5d0bef1f73f45931d0c6e77ad7d3188ca7f1d0e1319a31f9f940a3d1dac52ef2da75fb4be9f032294c94ff51a843ac901f9d69dc53b3e35f538a45d77dae1b3568d1c0e5fbaa834a63cde65fe0674acec1fa782920d6a37a8ee9f534aa552da4db7c1313b1d93b8be0d5a66c3e17a1e45676d318fd79092af5416da1553c76a703e745f5e56b3e8a7415f538bedccf974b46860b53e835e13d22e65cfb055c9a0f16b3955b666487fae7ee4ef828a6b198ef29dad4b769d9ec8808fec91952c7d4af9183c53e97b21e7ea321243e6f1736f6d76cc0c0a03734acf6fb08ad115020342bc4365b6cecafe0b65fdcdd3bf17bcb3e423527b63b0bb60292183c8ccf441c75cb0b35b78d3e695179339b69050fa94f4d0e1736499430b25cdfa669d4f4cc498230bc9d1767e05437688901e6508e12103925fa45fa9b7ee44c92b55c964a283fff13b20f145fe3f428ff82c5bd1954c266690f4229dd32a465356af42487861659cbc683bd74e9b92c96417e56c11966eb7f5c62b994c74612657eb10cb92cb7a10925c3032263368d8b8286385dc5cb7f31e642593c9197f831e40486e81ac8ec83fcfb639555bf0a3d46bcae823b4a79041520b2f4c56c694537b5071c728630c125a1c4e89d04a99bd4eb36894ec327b51f9f44db26073cebe0fe71966203f7a1c92581cc6c63c2bbc3a4c47031258d4299ad21acbf4d3e7bca294e25a92c973bb5bee0624ae3866337efc926725632bce9bcffb4a93dce6838415d689d5c69cae17a75ec51fee549ccb1e3c66be0624aae863ceb6e96f94fc4f0b490576263f7a19a617747c8cf1437c94e8a0c790af012a90175ea3c3de5b74ff14a9911bfe7b722f35a6b893a9fc2dad9c43f5521cf649e5c6c8bfe74948f149f918645d4ab1d5d324a3e0ce554bbb9890a5531251383293bb9c0ea7f145a1d0f2e8dc32795b6e01194388183c3ac8052693fe7186102301c5273c41d209ac62a8a6d1963e5f6c40c209f3db890d5bca455320d9042ae97569ff1864b2701533333363668a128fc6114834d19ab6beec3947afee4c18c235c7390b3a27a11f437a9c116488084830719ad3ebf8bb1533e8128bedf79dc5bc6a2924b184dd9b17643cb541c37905924a5cef3185d85252e2f14f49a387559a7d54209944b6a562d2f461f126b7804412e65827938e25da2edd9168ece7531cd94c199912904002d9cb27f48cac0f267fc4166c6ef63f668b314b261347a41b3c94a874b249c48d2888f8cfb13c2ba90009230e95ab74636ff6a82f220df7db77d984089d5452468f1e2f24c896c1031245184a9fd06842e6dcfc44b4394686b1b03fca8337204144353245860ca3b7f13f44aaacd2855c9021d0ee212e8af2789014a2137b1bc467528d3e21b4cc26a7fbbe437f3b08d772564fcb9daee22711c423b4f8765797d6a8974c26814873d8d097f4a68f8f79200184b596e4eadbe898ae2b21f9c3493feb6258de58d5fc60d4525d72442a08923ea81fd463f5bfdefbf303091f4cbf5223e25a64db7e40e21dd307923d182ae8f896f7d6838d4a269333008005123d28d2532ea552d401923c2c63d164a7d84a261b20c1c35749a3f85a90cf620ab23f465f80e40e7a8a356ee9921c5db56432994cc24824763889509f2f16d73cab00491d5096a673f8e911212d2474702c2ba987bfb85c0ec91c4e17dad2c69295f97808247228c7346316db2a87d9e3901213966bb3458f3d81400207cc92c68d5a1fde6b44049237389b4506d778ca723e4740e206b4696e0a7fca64f5d700491b4c9ea14744ae6d2c6dc96472021236946b7de39d9554aa60c964226449d640a20663e998139694cea22b253ec628a38c1e2469d8bff35d5e8e3997a54b46408286afd2f383bad431a32fe4839c81adaa0f7a4fcfb9652593890a48cc50ea5c9923f43b969f246548dfe6f568563a950e954c2629202183593cb6598af8fc218f410d57a24b4b97305bc580a9d058f1bc29c77761488e902af69d03865d4f75f6f4de6542f3857bf448d176f2a976f142a7c38bda5dc8769275a15bf9e45b1ac66b54245c289c90e7a66b41e77ced286347e0dfc33f089021425c41b205e4e85eea1297a9c38f245a3846cf5ca7c37509adedd8000f922c686b49a58db733e2e41120c10232eea67c90b794f2e50524572836a9b5e9b8f8395d2593c90a48ac70cce9f62a46f38b1de3214286f820a9c2a2aa6df1e3e5de67a9c0b668788c6572d74d0a24537857f3e64be69d47d42452b882d2d59ebf331e7f219044814d5a6dbe5b992be441208102231e1af46763b8479f40e2844b7da9bb0cdb39772369c26b723d68a7f41f362461826b9d2fda99c7269d6740b2843c9aae96cd24a4280448194320510227e4e8144d53a9b09533820c190204080a4892b096272d3a3b28d57f3a020912ec70c9ee3bede9391523901ce114db5a29a66869ca43628454aed374b1542445f077dea366d1b4d2370911f2b49027f4544886a076ce569531e22ffd3b7800493f488490a5d27cc1e36a90bf1923c1a8bd5456bb7c615793c070adb783c5c818464e43467e915f095569e16916eb2023beb0638c9673c7f04a0f2a61a417a8290d2abd92281dba115e64ff7deacf3ac87c590f23bbb82c3654a79ce3a1426118d145aa3ce7ccd233727bf4000fece8c00e0eecd8c00e0decc8c00e0cecb88003782417c9fa8959cd2978698f0b3d7b94cb7876e104466e91ba3d4fca2a9ccb6f0b7367f9f4e5c9635c3b08901e41fe870e3830528b2a5b527d23c5e28cb4408aaf56ee101eae2b646c902f038806466661d021ea1d833e657ef6604416e557d61deb5032e7782cfcf5f3b8974c4f285d8f238cc0e24ef71cebf2a86432711f3dcee01e6108791f41ca10e2011e4376b0819157b4759ba2b58d97b0f8013b187185418c079974bde898a5156ec56e7eb611964f25638415b96ec9db1efd195f16a30723abc87ac54bc5bc8fd7255574ddd14e6777dae753a1a7f4e12b5a0c9b3e0b8311545c4957e86d0b3d19738ad22c8bef88bd541a9ba220c5525bffc89d61ec60a414a41819c52153cac9ee83d094543dce0832648c115198a1a9832515c4c51187c298b139d6ea6a5f0e0245999eca643a3d9ef33180a492914ff01e7f7f723725991bf4970019f1c4ad4988507b163f68071bd821ab011e658c74a20f624145ace7442bdb964e9a76b6106dc28c37552a9a674f21d484dfa5fc54bcd8a6b29e0984ca68aad358e81d31f187b473cbd195fdf0125acaaf1565a39fd62c516d8d30213ca9a8d755c28b576a3c090b25387dfea3c64b291b27c1265319fbfd5c0f329228e913111715eb31120963e9d8b134a77bfc483dc810b23ae8d103c8100a8c4002a5740eabe13ae792f7115fd9e80c19b9d16b1c71049aa49fa8f035ee9b32d28836f454d79c87ab6f8411c8daa0ba63a81925db45147adfef57d4d24448112779f173a60bba1d8489b075c36d5fd6b64b0b042388c8d2eaccc29d38307208f69272598f1196a98f18a28fa2a1528bdb8fde8530c91873328397dc5b4708f1767b5f902d7b536d498f2043ca00126432f9f631869032320854fefc6a5a172d9f8d08a2c748203e25c4d346536e513a648605460031f287113f142e3d8fd231c6e0f519e9032a9b8a29f699cbf93dc207f4a8d22ad9541f5333b287b3838de56ab1f7d0f550f08c257ea14dc7d8d860240f57da0aa71dc3964c1b0f272d25fb3d736c71f30e851ef5a3eed5a48b8c1d8e26336992f696aeabc1481db4d04d666ee21f2374b8fa343f656f14d5cc0d46e66052296d6d9cb6d63ec718914325555493a74b1c125ea355f474fa8b958f1138d4c14d78ec718fdda737ac61a3e7d3b2fbeda00c46dc60ea905a0e46da605a86eb9884d0fe16f38611369c3ed5f4ee67d3c35b43665f3288b27d0c236a68aed62c973c133a08d3d0b8a85c2d3a25640c2368e074522a7e051d9634e761183943b2ef2733303ae5301977ebc2481950ea2ea8a6ccef65bd1746c8b09de896d410a12a8b5b181903aaa12afae8bb8d2627c10e0ff088c28818b85c3a4be7858dfba32a8c8421b5ba9e9f395c722085113054a22f262ee67a886587183cc8001284037c18f9423aa5e77dcd315e1af142e1f38bac18eb2eb863f595644b2e94b2c35a4e7c5bf04b25ab3c95a285745b34b19fb3ccc5580646b2b08b7a6cdf2acbd4da081646ae60cae39f937656ae8a46acb0689cdacf31e5d23ca7305205e39ebe119ff3a624f3c20815acf5ee0b6795bb93bc44062353487d33e77e56af16e487f8c8c18814bc1e2ff5174da7bcf551a2831e42cef81bf40032c4c74814d05bf5b1c3b22bc7a1f06919957b6dcdebf2849355d0933b9f3c68b764c40995785f67f8352dea197f831f658c34010fb355592e7d0af708132a29e627f4ba7c776c09cf79ec10db61efd2864794309284f3cbe9936fbd2ba352428311242c3f2a8cfaf7e8b9c42347505574d4654ba8aac78411231493b20ebf3e8d2bfa4811ce9aa5b16a3e4430880d39f7494b5959468680ae24445cc53b85ed478480c90dc2e4db9d8abd3ea02318a7cf6646cda3926de6073a80c169ca5be1e1d3f10b84cb7d144fa6935a42cc701dbee05dc4a59c1a751eeb92c944472fb8149ea2f75690cdb36432c1840e5ebc36fe1b72f363f0fe18bc96d0b18b4cceab75909949d6583299e8a2ad187784a81cecd25e32990442472e30751ebae3e7d8418ed0810b83fd767e87f1f0fbf9c00e1db7c863bc7865b51d948cd0618bd72a467bce3f95692aa9052a73d4246a9a2e3d5932998cc11f984c522274d042043a66616cf69777e8f862ba92c9840a3a6461484b966144cd873a8805e7e9cb45e8a01227e8804541dde87ca63673e8d274bc22f1a6f38dc79b0c177685962a7bd2e95a2ac55a32993c41472bceaf7cc2bf4e631e53c96402051dac488be5307a4c556e8a4a26930c940cf1c10221638c71811d3b767c0e7c3441c72a3cd576b249f84fd0a18ac4ae5355d83e3d0a32b0c3033b52a0041da930bc29a5415cfe4a874dd0818afe4b9a08711fbda49a041da7f0d32fb77812e273ca01a0083a4cc185133bfa442fc5d6525cda4a78e6d266959102cb98b36579df4c0641c728ec1483c96ad031429580a0431466d518663bdeb55a0cc59bff37c510fb1916fc810e502c6b39938ab3993e4e0ff048018f0df040c727ac9c5b55d53f33cab0800e4f946385876f6749d0d109a3a7258d9af45a3ed4c109636f5c934178caf96926e8d8c4c9322815b5582f77d1446a2bac7fbff42f9632e1792c2fdf24d23fac984875349529c9ae8f115de2d5fc4186ceb6e898b104a72fa771755dcd1dae046f4ae8902b3552944d894f9ff876caf96fa46612ad7c4c39269ea3d2b983ffd1a3063f7c9ce124d021093f6c09038e4da824f1823161180a0542815010000083a42e0d00c31208203054288c848201b16c9bf6001400003c3422524e282a2c1c1a181289c3c16038180c8602c2501810088643e25048244c444d991e01ed3860bbdd06ef3dcb1caa0c37ef8f250aa03ed448c1e2bdf10355e3d778d1345066d889f28f16b370526743d09735b57bf51bd1a012020a30a8bad8701e3e1c7856c639ec88abcd1bd9d72c73715de090fe9151297cbe8b72dd3ef1a807453bec9191807a70c5f96c3d32fed108aac9345bb0e3b339b683f30cffb9f43d0e3f2a918f41c6d9f8c4ab4393bbd495d079a95d3ca07746bd6672e65711b3d64ebb267755084f85ae96e6b093505bca3a252c8fc57edc202bc8179bf9c9babc4a33dfbe3275f76ae71c79f5461e6a6bdd14f2bc75c0a42a27e5b208600cd232410d718dc413f2a0216b8c3317a3dd94e0f8128c44e1828c803337b5ca4deae7e99076f966a1b204e7eea0a414f3efc09b236a880b3f437144294989609487f0f0065c3d017719a282b9569d975a31c2b7d347027ab53b16d4972dd82aabb96966ea2ecc99be442a0b57ba62b2565c8656e0651203dc6f66a3fd465c2f35ab1ef556dd53a3cc3b276e52d5ad9820d96e93dc0eb39c7b0c985954eb7d07e61e7b6fa6b2554c016aba076f4e1a771297b826282b44e6f5740c79e6820c9252d0a28f3cee2f1e7a1e668fb19aecb999fb6b1c38bafab486bcb0b3ecc1ec96d7a75392cb86846b6c5046f7bf2b2c04613865238b8e51e8d7bc6cb982324e01fcd7e7b4c60bfdf9a4da8afd24abc67310827cc3c8226ae6bc14c1043d577aded3071c0b9beb29eec4dc028b1fe0c1ab4e2e837197d44e31f77b1800bc1d8ddc4ae4f8da3d3926946852d0fb861bc0c4e97053336fb488a9711cb3136c12966835738ecfbbcb897b0cdad162cdf0fb5b643b37c2e750083f0ffc1bc5d95d8b68b2c44d7e209215074497801f09bcedef74e3c683d6546bd38ae9327fe74bbf9676313b0b0eb6185af5e54a50ba633c8e1e974fa9cd107a1601585c591487789cf07389b836b81f65464e5a97eb303a6d8ad16215cc08004922ba0de57bd0bbfdda41c633e3b955edfc91d96e7469e0da73516b6e04f91688778fddd7fe117ad761e3b0670632eebc7e7823d8ab6958bbcc96d816f02bd3a02524510c646dd7782ca334dd3907ececaa3a33c20b1ef00c41d000440b09549db74a688cc15626a588e243820571e340a37f3a64a9fe4eaed54d6d3eb582759f266268034a8e300000e716cf83bb09e0b0dcd2cb1d3109775a6ca6fe7bc8a5beb97037775efd73814603ae09330f3b7622034a33942ce78e178ac50c118e1860913c3df9cb7a9f2c14043bcb2d38aad2106192fad710e6dd6eb8116cd5f9dcfe71fc18e3fdc420b8606d7b9c543070261fe6afa63f6b6703e61f57d0b1e4e3d289845e8ac24063ebd90b8d18bcd040909e65eadab6a44019928ce04d47e8804bbb5b2c4e792f4a4a9939ee12eca478ca58bc49da3174e1be06181a775ac78f0e4b0166864594f503cbc8b7a09b59518ba3d207477871f258020eddcb064ddd5baba3a44a9031f34b3253ed5b6479b2151b0974a00ddc031af55e38530f7891555b1352c22c231b35a8e60f7ff6d87ec00fdd1ab6032aedc1a80e88c216218a9a972ad6bd466a6c027923b823465d2a50ac5984b414e92dc32fe6613b82fbb2bb89cf1805d64d5e1be5e6417ddc1cf3ddedb5e637a296ce7b780380765f42679c61bf01df0ef2ff03ae875fc4ccfc65e0f5dddcda39fb2225fbbc1134a056de2d0ee826308c8af7c79db407c60604b51a3fb5edb130d3f3fea6ae3e625cd0b01dfd24d05ad6be709abde01a4c344a6015a1605e91efa600096cdbaf60a4b3528a97fa241288a6c4b5eecac5fc3ceec5908055f0afccaa61549b26087e44d2b32bc2a4d94c1961e6d1db7bac60611826215af6c22248585205331d0aa9619db503032c7ec84bfb6c174cf5dcccd4d65a7e8e208419f22fa94c403ba2e23b890a7c635dfd366e4edabf4aae19c22b8f30c81e8ae2f40e4667903ca902ffeebc83e2b3438d511653355567eeab81736d343d4459197f07fabf380900b51471f3a8bf50331ed8e517b424e2793634bf63de36c497a01e6a79e53c143edf2be43e5e71e6f3c655f19f4e8fa20eb9cac0538acb9da2e54b45a148d463efc9bd8c8c5ccebff328adb95ad9b532747f156d4c80cbd42cf3a55e546a2bc3a1eb6ba4bf8b1c06b122e657acfd97b79001754b832011ee4814f5423cb8d9b9b7469d29928a016ce5ae117e04b23ac51975b05f1f575b241bc61cbbd017d3df6f42125fe1049a8f72cb77046b92898becd9334ae597a7667167323a0abf120564fe81471a7aac4b24ad80adf33d92da14b608df61b4626fab4f1cec618a81cd10c6b82853fe52b37efb8ae24195bdd6c5970d3d99acbf6488e3cb529e7caaee9c424249132ad0183d163595e19c07b7a4a6dc913bfbeb67fe34881a28467e0ecdcfc17069364e072bee095d044a83191849244b862eb81b4548f91f7b21c4887efd81d47574475e0b52731020f4005220bf2d9dd4e85de1ee688a0fc753b47fa1fa850f1fa62c14053ca86949c8269c2d067b9ce650d848dcacb65160edb227eea3529fab78cf81ae1e8ad9926229f7065eb6c5ed4be9c26d919b479642429381eadb3eeb80e504465eeac306948aeff40020957f0ccd852aa6d73778d83cc6512976565ce9db2aa07ccc795ca8f056c9286c8a875b9e26ca46565f243515462d306b73c2b8944660835ba7484c697b61923537d922aa1dc041d7a96994ce7a0595d334112ca8b833e5cd09602e9edac8ac500291ca6a2986076f228292f214c2873a613155c2bead641fedd6f428fb41588b16a621f4e8f7eec060fb1f1cbe19f5b846cfd48d921a452d6cb05b88a022be56f2ab68f95e4290e34ac2e7a0538eccfd3ac73bbd20a0cafda8ca7d3172dd723f8da3f3e40bcce2abadf6fde9d3d299bd3e0a9deda691486eb22e5cdaf43276f91cb734ea0149f72c409d7049efd88e029090a0f4197a865e2cb1ab6d46734c196c07cd468100c1882ec21903620c47ae3560a3553541ca5245dc95f5f014ffedeb680e4141c53f8c191664d23bbb2fba152501982999788a4a474109a3b011ab06a764780722e356322fb4019d4406b0ea1546434ea8bc0f3730049f971caa2f812b050f1863cd4a3a3fdd8959d5c27e999e8f1886556f4210e29f9b52571321a5930dbc533b44e5816c46243113824894d2bd05edb368df39172a310e24826e7c6c851b5c72d6a5ea935c3321661952078d550ee92a31ffd00a8761039ab8940b54f4cae7af362fe85d100af44bcae49d6bc1eff472fa1af0041b649d99ba64440a700930009d0a29f7a73103a41ee78000037117202682555eb20118df36b4d1004c624d1baabc4550c05d1f2b79313b9c8485b2b3297e3d0c9da09f763e8a410356ad40521f28b1c287b6cf5a08884ee65e1e4374f0aeee5d4b42513c5fd3939fa755240901f85a9bfbab27f72a8c0148b7ab98a68348ead0327967594ab57d442d558e30d088711aa2352e8dc1b3d53033ae62a68d7f9d52b8edcd04f29757a654ed7385844db2fe35f525887c2fc6c691a3ecf4202423f6af81962a9a42374271f866d9320c342fd2781159f7f869c3980e84d636fc1ca0e33a501327636557e76221d0cd47e44416e26aeb3c9e4e9430f329d8413a59af77d0929d26ffe05ed79925c083b967b787c1feb0ddbfe1b9218ead32e00de0be25236088f71b910631191ada584745fa445dfb9c9697b080e99e6a83b1a77d6d92bb8a8922344fc1ee80a558ec29448357e4ab9099d79386a53afc3a3205608dc41ad05ef84dbd6067e2047ef21ea43224945645a4caadb449c2e592c4689d2489808a08f59af975c01b505dcc67aa63c7c0d251059a53635b91fba514cbc6e925a6229d71fe8b5307fc72f690f0d1be2e16fbcd3d9dbd4ea770b58d41f03b6edbb894d4734e28a5519cd56f35ff747e3c837c496b12f6a2969da21656ef41e6e2a56e340bebbc301301e9b644848b06720fb85705a00b595d3710d42e0734d63040510f500ac700aab6031d006c2475a23e3da79efdff83d7798c24f095f32d629d2078c34664395e3920e06eb73a01efce2403315610a18ea27a467b860e937bc6751c334fa261a8a781b0292a7e2d48ec08059f72708d0a14b786e0aa55ed30ae7fa23099d12a94df8475a0d2dda7eedbed2e6359d972d6cb366f7371a938fcf1e28f2c96e455cb3f72731e805831a711223573f4756afb6ac7f6190085324a11e1f6d5a6ffee605e34b353a1f234ac4d86466e01f51049081f85c3aeef073916f82e40a411834f6418532caf91d5dfe79f47d91eff57426656adc98a38412e2e9d6e7d31b9aa4077ed704e768027c913ce9b24bc6b6f6616fb0d12bbc7cb59f5049c50bde58f42b304b271bc054a53613571f22f22bf19df66dd74d7074ebda9025f241eb72763b7b8eab89efcf9612447c2ff04ca42fa849896e4ccf58e11616546f7ad39d9f40e8d64aef3a94d9b26542b1630aa6aac0e13e810c4dee21d892a796ae88a65131c95318f7a87cc37ab87b2bb720490af143cb173d8954db48b4adabb199186be6f20e4f95d62c835ea478a4a34e7f502827dd111a957ab46e86be0f83d69d5b8be5b4b09a7899d699794f1897af3148bffccb8a190077747f222ff7268c93cecd7205e86390314c8ecd72d63fcc59066c9746bdfb70097aa765a0574f4924e9be6965a05e42d3daa5d39bbf52eded864c37bac8be0fdfc1dc915a3cd49030abe728a03279eccc612473bb13e3198987004d5f871433623f12cde1cdae6a975e65afda244ba99a222fc579eb2a0c5e48c9d64ccdf43a1f2fc77eca4c218aac56a249c59cd05abac4fa09dfaac39cdaddf48804efa3adb3ec67e9b577a52f63e9a8907378b8f4fe191da17715a758a554f2f11eed6a148c6a6325dbad70b44cdb6c62d500a27718793c49c03685d90a5ca6928bef6c0968e1d9905bf8aa259e1581a2814d22e29bd42016ab52f295305e9d079f4f65b6e8299c7432b37b35a244cd52467eddc653f8675fafa7ad98e32729e570640d247e796877f011b7ed3a0fe0d8a38b7195b78b026f6a47a759aa7c87315f5a20f6c58a555b271de67a89258680831690e5c242cf24edf0a1f2fc82385b09176c28be7a2623cdf3fe45e4baee4f23b242881c719ed2fab73c249154f114407c0e9073423aae4ce0d15d61594a005ff129d2936dd35195fe53e85f2452120d2370144813ca36adcbc60065edba5d26e66095de1b5a386c914d4501fa8b52d1a90529958a1350c5646cdfbeee01e1823285d4008fbf7fad20902ff92fc8f4d9403e2ef2a2dab23b98628b8654c642a6c0199b864df10d743fe54c07257d61da42124c946f7c270d21cfe9dca292b755a117b2332a3ca6c47ef4021b2d2bb6871577e9bd4f96bf2d357841a817bab2edf093188d07ce8a71ed60bd346243ca6e320cf702833747bc6f3c8f6c90cefb6b610786200241297fa419a3969d26598332817100dd0612444a8b9ef1ae22a77519436e5e63cbf9c4ae18233152fdf8ba0a6c46f0e47430de6e423959a336d687e7e9e503d9283b3d9a6fd2ec872b336019b0d1afe3c3fcf2fe742f99072b3fbb6674abaff083586b246a7ff16b0151124c866886730c4eb9cd956605621f37b65b1b942ac626836f454c18779cd80fe087a646101278764b714f72741b8357bf2b7133a380f3dec9cd309441b8a048e3cac19ce3983a1146a3845aa894c5b0f8ce01ca7418b25c36fbc865fbd5ea3ef5d3a3eb55ed5c87511207f56a43672e9e85aa9700def4c97cec9a4f9c28f7f94097d316f5f0b604b6445142d8d6a220371c318bffe25020622c223cf66645ab3d4eda710a31eedc4672bb8e81298360179b127bcced23287a32dac765c4bd381e893ec3836426fbf11b2f3ddef13523f21481b71e3a2bbdec768769da696c6f89c2eded81453fa47f8d7825feac90ca30a5d64b63bd9f29ab5a27d0197c14a7bd50ef3d357aad8304470be45e6fb32048fb230ddd1b56a72d1769b08fc40631ec39ab8c3cc4556944b3a152b03f0f799471918934fd1c38a8138b1f6c9e13d8cefac8373cbaad214d6dd9bb3770ff23350527b281bb99d85e6499014c4ed9b0d5944a1e1bb77cf72e904702132841b23b35a063146c328d0e865d9d5799530dda5a092942855f42de3314f6bb83a0edf1164e829ba1f098ec899e8b742d4dd0c9c53d393aee48553f5976241f13f5e4c467dc1cdf6a129c99fe9896f1d14b83d37d930c8bdb3b1392529005bed3ac876f8e5c1285ce097038edb0902a6779f5f28c23d8a6450c899dbcf63b3d713f19315e27c24fdf4a7c669cc9e2fb149ebe08e26b4ed3d5ce44613b12042979688af75dbb18b600727b875f0aa96fcbdb2254a25b2ded00af64275a045338606d9fd8829a7b9e3a4652b8b2c4761b05c2a5feba7eaabd9e526bac58b1fbfee24c06ef3de7dba2dabd6f50338bbef06d7ca49319d914e37210bc2173dc4e3972323adb30de07e4dd00fc7f7fcba67623167a191019f40010b787351c351a91a2a2d1a70f134e12bd3e72d77874d5a02a7dd619aa18114ba592b00995c9aa631733a01a04ab942871608c189d3476e85d6b1e3486a71ab1c59ae21a9c1f2524ed65b70953b2acdfc818b2a4c443a614b6a04aa2743b90de4697ca21d5a30d16dd23fb9c063b2b30d050686f305a166ba4f3452f36c5917de0241132d3dbeec2a3b0840d4a47f28d8de93d6d1de2325e123eaa7b7e657f3d47cd7dfe1da597f531ce101806fe44d329309a74f0a4eebdf5ec61beb41666390ed3ddfb6ab2df1c980e89b71474d121a7963f975e4f339a8a9dac6cd8c176e3f4dbc0933d2870cdee40d6cb7098ecd0b776780ed6f1d59dedfb689c45df79c81ae74b153996dbcb7972c17cd1197ecd3969cefb839ff1e0ca3d304a6339f900e9d3d0f3490328055800773eb160cc96f939568295a91ed48dc4b60f419c14731a9a148c7b95b27bb28952be432de8fb1b171e072958b9dac690be2622cad893f6b3e3c374796f69f22763f8c6f175cbf931feb5da11f3421b30e0ca0a8f12e5a6b46e5bf9f98fd3c6ed370d79ef52efa9b8f21450802168e44cabd964dedd4610af4d4aed1ad9b204f4d779e4531738ef879eeb9842756496a7222cc972eba9c456064a3d5de335fef30a75ae90d8069ca6c89f4cf16ecc00a5b93a977e163001503dc33da124eebf12a5b08f805bbcfaf010e564a8f557cdac2dd644db69051330b216bc13a165652bea5c9c4bac4049244fe5c0af541ead77dfd3c631003ef86a69e957c4ba3e3ae3043ca4eb621ab236c99c5f337f26bdf1764bf045f5cfd4c2a62b92f26a1823825e1e90b2df364238f1719bc087f4773f2fcc1293d0d133c66758abaee5a8dba4751f10070fb886110d46fc16899cddd31b7b291e71d7d5bda7ee944f95cb6d00c759a99ebd4d6e0bb85c8d520aaed17de94b8d98f72681bcf9e6cadb941d6232f742889d839a1b0dffe41f3a3364083f7a9b9ac9a5fbe3200d0ef8435d9030581d2e1ef973cc2921802cbb3c19850cff7ccb381a48b75333a10576d939f35be902e5a07e632bea3dfda77966d9447dcda77fe6d6c47c2b5f2cc6d7c47b7692be1c1bff6d2978e8b769f521984358245e32e71daabd96b55fbe9284dd0573d4c97106f3fa614a2f4b98e9f941b2eaffb81fc0bdc9249db961106f6022d9f52d79c2b58dda5b8fbfb7226865e1f8e30db23654969db1073cd535022811dcdc148f2cd07ce85530390e825e80b9a2fb2d6b9e268a6244faaa62025a306bfc8e65c2b6cc9d0d0aeb8bc94e7763af7fe4b9b5c956b3825833039862127a094e26c99d9fca100dc5dbd034689e7e710e31313b5c147cdc6dfdc1efb1e7cd61487fb0d458a1e6e530bd0262dadc04e17ce7482f9e93a8c5b6b895a09308dd396c8d894f51944ee968409e9fbcd177c62923eeeeaab2dbd34420f488f2be87ab94d11db624e7fc85405439c4afbfbec0ebaf34c7028bb544f1e9c340371508bfd0a628524676e214db7126c8e951ff650a5afe08a421b4b67b43a09c3ea9c18f1d9a903fc8532290821541296becce589cb4d6624e08ab9d5840a3319047d6ae3aa884cf16f6840608c047ad44032e18917c66e15c25054dfbeb7e17e3a4f6ec67ab43c56495103bcfd9e5823e15c7105e1ab8040cd848a27fb5cb188071af2a82ef70bd3f27b72df3cc66c395fc878e7c5b23ab9c5bc318739696b659c0a0652903d331b68e89ca18e105d2841b00152ea422f26b7d24941c910902bd05adec09ce17a86408ea74db23db75c8a079d92838087a7d074fd92b646d9a07b7174ba84c78603054a8687ecd75a9c955101f56f026cb9d0eb44d9688571199578188bf5f5e9095c4fa7c23f312acc76ceafc8667da9388e38af04dc80365e4a0d0e6cc734e68d78a26f081c9476edbe065b24b5eb2d677bb365366b0a2677dfcfd157fa98a57aafa0d3640529cb5c3fd40fe46fe107f7973fb8bffe83ff0b3fb8bffcc1fdf55ff07cd2ab20f50607df0e36fc8b181af187e9cdf8c6bfec0fc15b709b30ba8d7c66ba1337292900dc458d664e6f6951ab718bebca656b48c9fb12fa2eec02ddf4394541781cfaea000aaa472d263f8535d174e6607472e341700c4329be704ac017886266e0225a56c14432a31ff7f055fb0104d61601fa0ff4042a5ba5a0cbe38da188860d3f1139b85959080a2348edd1c00db598ed484a9d896b00d3a2a880ea040385f67e7f5a209d07c4432ce55f49d37c541fa1517e1811f0571140ab05ea37dcd5948e68b841263d4f50f5bc18272e577594160570ae88984c65f5aeee0514e08b687a1d9149efc25b16d4e4f2ba876a0dcb946721b28bb83e3b1cfadef59fcd0ff9eff475f8ef3f2eaf2336163766095a95b65d54847b42c7ed7d637bb67af886a002aaecb902ed8950bbb538d9fa0fc6fc8245a9098fd68cab92781a391c587a15388b3b92efb63c06a8b13e0a4ab02daf3ac1b8d325a1b76f589a4ff1faf620d5c9e18dce331107d4ee1c39d7c19b98c1199fa004231984721078ec9a0946cd26f00f434ae50c288bb7b86d1cf218f082d2f9a70153bac858448d6a521c3a7346c3e70bb8761fa5d3d0556c766fc3407b42f584b829b9bcfd54fa5aabc4135ee0892977fda2b377a1fb3e446fa35dc01fafa74c86cd598cd15ed6ac0049e3ec5005d2e84f7d6dd741cf9cd0b730b1287120236019db66226d4d6126a26115a069383e251c86384fc13ec0fb6e15efde32d324abfb8af3b1100324f531b6428d9038c9b5f2049363dfde74e7c2cd0804857121079492a7ad96c7aad0b7d98e60b662601e5cc60cd27352aa2f9952ca4ea46f6161ea4ac7b36b08341a43bc9f88bbcfca2a9cb706be19826d5ffe5c40151424e7e7b9327f8ee0b2336983f160bc21296a49903a065fe39a044f2b4d9e188bb3840d341d2de86d398381fe90ebcbf4a84082f058064251174e31d1c4f5e675743be09d63a763a6129065ae9324551f41bec81c6165cb644d36dab263f70acf1e6d78680f426a6cd1c866b3aca86d87914612316f30de58aac82866453cf7621632e71103f4840765188ad90e0f3037703d9300aacc7e1f3bb4bd0672b765aa2daa80a85ca76688b84d2b48c6fd5cae37c3fc004f7397723822a139ba06daf6acc6c79b1e3fb842fc9d318b22b700f9ef65ff2da8f6c47668a7ee13b58d6ff01783d570be02e9e33af14315e47ef4441b5758d3824ea7dd73963fb62bb8297da841e7ae54e962d54b00eef65a755ecf3408b6b6d6986c2fa6980bb8c8b9284a2424137cde447540406ec611080095885e9b1cdc6bc9b74fddf7b40b7eff5c38e8bb15dff4fc497348ae4f1dce078c4de911bbdbf04b5517ff2fc791ea2dae97e1afa17aaf190c5a8ace845ff5195253df17cc76726d0296551cd72cbd326c3d350518da7ad56167d938ed366955b2ea1fb58d6d9358d175f1258a96815e93bfa1ca40159df99b6c736bd4f3ef015a4d9e82332f72340346550cee10536d75a08849fe7e02a44603a8f0f49bcfd0ba7fdc133f2fb778615ef884bda137cc0d73614ef8087ba17f980ff3c33cc2b750d8197ac3bc3013e6866fd81bfa14664f0fe39bfb56f45e711b1be62685a0530fa4e985ea012618c89d72f81039df87c5f91777de902779a736f357af45939c6cd0cc0a5fe04b9ad69bd55df3e971f6b214b930a26d064b230d44e321ae4c392990def03a1619cc11ab96462e0e1af82543a5c38447b2f8b85ad03a9d3c8d9102da0ceda6abe20e61c69b0a0e589648ae85ee14d5d516303d30ab394517d035319a9ebf892ca5be765952c52ad053b3f6e645fdd86927bc5391fb35a6c2ca1caaac9b06869d454d41679a25334d2c94d0499d940c2598e1b0230b85982821c81a5d55dc798d041004a8dcb95d57d378444ad5afd5e3b1dd77a8136c4f353001daa46374077d6b1f9759513e60384d2291cf905455ede6508abe86116266727bc094f65d606a5e7990eb01de072a4a0c2351d46482e3998e9448fbab2f6c993f88ca931e90719e9b2883a7a594c86f3ec588c1fdff024f411059c4cc0ad17efdd091c8aff2938ad758be05837e1bfa1faac649b3a0c32b0c2678088a90d3ac609f5e2cb1c4689a101e4662cc1436dd3beeb279cafa724fcc2c65efd221529e58e3eec00d5c6951edeb781c6a3cae2483171d07c1be9920e142f9d74ce5466ffa2f5bf531cfc6fc2f809e1ec3a6e8ac26bcadbd5c2ff971dcbaf7198c7a419e8d063619601c0b688e1945c2d58b8e57bb9217dccd388d43a1c8f5dddf223550aa18c3c16225f5d4d16ca1ddb99e431e933bd5911dd4b69c80181ffa94e8b1bff73350134603ce13840103ede52db660a32bcc4e7185d5d33989ecc218a8c0947e167c0e20419227d1c378ba51f7fe88784ffe117745dd7dceba0af2e8a418a111c4ee19d24280287102956e4854ad99e9c60550c08cad8839e4928bb3649b72fe9f0204e9cc209e9966b15a862af27b2388288aaa084dd8ba78f3e35c32995afa11b7daab27e87ec47a09e7ca3104104b9660b6f6d35f554821bbb1adfd4fe2aa459255a0bf92ddf8ad64bd9b68f45e180b8ed8c0858e02d38c714e2f67031972516b174616fe8d22878436599e915119960d07d014edd60ef5df677b3622a174e090a059847669ec374c1e0f1b4e1979a086585395a00bfb982251bbb05aa1b40e5a3ef23a40b406e350feee18a67a9036d58285246e95be21b720cb748d38a318f08f2c31d8c6f04ec0cec2e6b25a892a6f114985d0b375553685465e3d59b34764bea92aa2a271092096c732f168b5a113bcd334c5ba7880154dfd422c2182d3803d39e3749d079420a4ce3d2fa33fd9b717bb5355fb8e24cbc5726deb2c0f085a96c5ae93f1fae0dddf7910427377433409f07157b113d81958ba3fb53fc0709497aa9a8ab8b34b87fa698a7059040e3cc0f1e67381c32c508d48c0f0248378d558dacd5b71d9d411af79f372d7c5f6c6af3104a0e03018051e519f8b22b9b3b3004b902b80a6c80191bd2164dd3b9586e465dc77d05fc343efdb05f25b323870d1cd2d9f463118380fff790c3ffcb1580626442769827ac9839ea416e78a01a2bf2521b0f7e17d4419fc32967fc27d81f9465458423d4a08a24ac53a011aa5d233504fbc19496ff3a8bf08a31508c06883211ba9f407f711d586fbafc94c6894227fde1f17128587618c542d9d480be3b1cae6b3741bcac73d6b57f421c5d7493a66a47bebd3dae05dee8f5eaaefd2f55dfafed27b2cad7fe9f796d6778917fa34b770430dcb11cb096dfe66be3bef09dad2471b4dde70c832c01d12176b6cd084d1e59c68b49e14c08d00a0ebec9c2afd04f355efd247c08c40f2a087557e3b7e82e17a71b591cdf78c89d134b9aae5128649cc583f95afe20843858710889bebc5ecb797b2c7f631d02e9b872123a512910b3d6caa507b69e46a749afb99a806b766842d3485e70b8f567d6b08345e6b09fc09e2f8b3f4b7ff8437e294bc14c8ca72dcd5e1d4189f5fceabe79bf3ab7cac3c8ac083a3178b5a8ad9dad0ccddf139fa10fd4e4f977bce118336cd2a7cc6561bc0fb59229e83119a433d2dc7e314f2bc6df2c4313798f0b682f50313f2be00e9e4857976514bad65acc7e57267063fdb9846ddfd39d167264c385e6b1b10564d0cff07e1dc26b3f074a48ea38e0e8826aa5a704d8bec4145843730ccbe9288f01e61e08f7337d6abaeac37b840a20804d112a8feca2c83e1889b321085f31caefc3f64c41ef9581d9dfd5a302caf0dc4867791b22589be16cfcd3ea0f8c737a76cfb87e20b1f00ba9c590a61954c081eecfca0112680b78632d96380083e4c989e579149f46e58f2490710acf7010a559a9f14ac1ed9a64b98929b11fdb092cc750d7c096e79572acaeb9466b89adf283d65544a390f1055cce912ae98875519c518044944219f851a7a51d3be054b5dbd30ce35f7f594e1d627c4245f7e58b5ae7cb787d43c681a110f4cd7c487eba9356f81b183d0f53e6acaea1a88af857340c50c8b0ac9369bea20b61bee565de6ede1602a5aff6aa40beb03cf36c92ce0849f18c811a93d388c5bbfaeb49a1bd8e8dc91cec8919a3430f1c0ae0722860725e6e315a33544fa69b4785145bcdec343e6a7f5b5d12bb8224af7220d68a5183fcdd234a47740da13ec234ed706ed6ed25b67ce6e54e70d75659f44421c4d93a1cb72b8f34444a0125b9f9624e0db00903e59de499f6a70fa34b8c38990cf53718d4c498c8c6d1b22a23d96f01d97074fa058411b5515efd40e2c6889944f86b6371e7f82903ea3b39437abca57d874f4fc01834a7cb40612fe85c58f6373b2a46da0cbeed3ae2efc645e7f0386cde534b46c23579ff3e6792e363ddf2a4edcd6cad61eb9dd6f27505c2e9313139ad60e338869b8210e6ba04821bfa12ad0159b879f9f67f813d960ad3369ec44cda8dd0c4fbb64e800f30dfb4cf5c5c16840e05d552498fed2c9aeca5dcac65e3090bfedb475c8b16b3c777505229b1ec24835752faaa0bfa94ff8517fbe18836adec487ceb443fb47b1120e26434f7557bc0082ceeaf7bc679de70fc98eed29bf461b46a33c891f8d10566076f14847c22e3f6a0f0b8130917549038e932905b300ec4728fc0c21c02d8bcc116b47c561f5a0ec58dad09f621faedfaca55d55d966329641d86ceeb8955273324fee8d29b71c188b8ec1090daa5d79eafbbf469edb52c2a6eaa6625998631ea9ecedfece0955f2d7b8fcb2cbf76d9f91a10072658716424fd51425a80adc8e535dad4e653f8a56cc8bb2c42f0fbdaffa89ba39c8f6e2302431a14a4781c92a181a5dfa8dfbb7ef77877790d9c7cdc1576486fe18ebb35a65c33307be6732181f2d3d857088b29620cd93b1a8e3221c1a0f5d5738c2a704924c2038a5a250417e12a90cf6fc21b6fea56b787fabdebbcaff6e5c8356ea4217091c4556bd3416dffcf1d57a1a8fdae7dbd82f3fb8f4f94a9e41b8b23d5007941694458999261c47e4dd54f9e8b8c2234eecd570625cbea1399ae91c123a57b119ad8a6053e9c48a44a865b343f81efd6748a2f150b29a1147ff9513e17612b7e38cea887f3e0d115aa2c6a9d0640f2d7e614f5d24c34b6bd5803e9509dc944f47d20b95aeff17682b2af7ce58bbfe3ddfc59b7c98249e859847bfe0411f18ea607c03e230dc8c92db4528ebd9470f14af110616564d3c5c9f4dcbe08e9629e1da9b572eb81fb9c12089e124690d5c580dbc1919e21e7d81d5a0d80450a719bc7ed0e155d178588c0cfccbaf892b89e3f25c7806e208627d030e89cbcc652890225b8a50f920162596b8e1d7d8bf8c989fd0a6dced064609c270cfdb725df278fc75d7466e6472461e9c068a7a053c4cc0956b8b6a1f609c5ddf965edf58bdc98a42bbff511dfbe2f10d4362a9490d75f9822725bb9e2e5811e7db4e196aba039edbb3ea901ba81514ede0f130c1b106e667f026e51db30e7069571a6165f5e85b45e6eea69931ac84e30e35b5e82ec61a080ab465f7f1d63ea6e4a96a596585041843fee6b2368de54549ba11216e1fd67b23bda622b583da0049d160b16309102b1fb1e8ef630ffcb79d2a170a0db8ba2f9cdee0d39cfcb343bf847dea42e35edfdd0825f4e8073d592c12f3cbc751070a4ec132c49135de457ce46c090c39c8418f170d81ec64d2cb6a51e11acb499d6f13aa390626f0fd4f10a3a08db4b819f287d954c402563b7e941c2f4616a954f26b0a3618a0ef7a284ffd9f7eddce415f411c944e8276d2f9e172db36d8f02084438e19d4b31a4c996918ea80caad61b8e03d1ba731c49b0a9f381b1a7880978f58255fea205d8f19887600f25cc5214e39d34acd94a51310c309e0f808577f1ec9843313d9254bd377bf7e6f37208b120ef6467f5bf56bf36e5bfbb2a5bf5b6ebca1dfd9f406bc09746303beb3c60e113530aea690033cc3333cc3333cc333460f9a7fc8227d5a5352443c43bd206c992477ee0c1b12ee1f000018b68e663f32b9fb83fb0704f0104811ad10ee3fd250230579830a657dd09b0e275dd2ec66bc88c8105623c89032446e4c61b7fae83095262e05a375ae245f8ab7a538222272811b51e03279e6a84e7cce950b74102fe30614dc8d61529a68512ea62170e309462b89ea70696a9dbd00191ab8e104d7c3c5f8e60baf5572a309d5a74dd9d1a41cbde4e406139693742bd95856b0dc12fafc1c245ee3a526e40677430996874d2a615a52427823097b65d85b8e65dc1c1db9818483bd96774e623e8f01378ee074499896e027ba9d18e137d1643268e618933f430d344870a3086cbce6cef22dd1394c84b3a685cd26f557597a6308d96ee85cd906e4d1f8009b81869a800c14dc1082bff9634c4a62df98f003c20886aaa151674f1c026100431f5fb14af26c658f79208c5f9c9d3b772d96449d1211911f3f9cc3f0c5929a5eaadd35d0b88088c81a686809a31777ee2d29d8c7c86cb31941748080307811c62e1657939345c564267976d01f1011110282307471e52ea1f3872dd9c70fcd81fb40e3c2c845c28922a13a96b13b91910111917633d200c2090012062e8e4930efcb25470361dce2cf7679a5219673f4b648c8b8a1d3bc4d7a5e0b4af6a43177adf5e316088316e710a9b615bdf3d29bc5dfb972684ed12f7e4910204182042961c8025795970ee5890526be873a71d2a379e8030dffe1a3d1701fce66fc1082813060915dbe3c1a6c0ae315d954a79bfa983181305cc1873f297aee106fe10259354018ad3065d23a4997c7ce222b0e95e52eba8a27117b44446415c60bfbd8d1244c8dae8a73d8922de6d7f856d1c248c537ae41c42429f6de405061dd998919b55937c5bb819c62d98cf7db91173f6d270c531ce5637e464364ace88888c88f34ce30c3477b101d31619442b5323dd1849793d39884418a447b999336339ca775444885310a3a598ade9e0372069020695c18a24822aff3c6cfee9e93230e082314e71333884929e95df8233e800c814018a0c84dfed3d2923851d1079b31448708111111e243d6d80784f189a4aaba4c93931686273af5246f97fc9b9befc449d3c79fc99f13f87e060de97027f76f8230eb8f95a348ddfc9ac0afbdf56f73ae347f26fef4292779c5d5a88f09aba2e2cd77c949c8fe12764449a1523faea5bc25d0b1d0f5ea2ba1650a1ddaba6c639a12a7147ef64a8a5142c924daf512538c5529f2e39250b3a6ce555bb277c7234126e17e5aa23a68c72141c625d13e7d4e4ecc1fc1e9b459c6247ec9993be24d27e626b3928369bc11ebc4560aa27f9a2ece08b5939d9c448913165f843154c99bdd4abce02b620df95b91f143e49e884493c42479a7b64ee28838fe9e2062e793b93e44f39e843a6bbb4bb321124332cab97cd2740b9167d64fee5c6bb64d88a4ab1eddada479d21e441f4359dcb42591c91604172da5b98919f76007c2248c67ac33fd914f03a28c278ab97dfa0f85ad0fb3d1dff22aed87c2da4b92c3b57fa3ee036aa28e97f852c9c4f94089f76249fa94e1f31e885293d7d3fabcbb7a285b8a95746d79379b87bfbf63489570628c7828ad4aaca8de1dea249b7267f195d6ec606cde7f175de9a4d5c1289eae3a3e89d3313a987be365eacc25f7660ec6b1cdb1dab01c123f31c6e4f3c9ca701c28b75a8b1293946f311c8add4a829c8949ce167e03fe3935ae2dec86f34ebacda85e680bb721a12d564939ecc74fcc062e4996ab96f3f24cd6b049f207932f295a2e5103e272964b9c92bfcea601f1f8a91be4aaa4b36838c55fcaf9db9e4113ea46538e8b36a9354392c70caf559e9b962d4397423c494d4a6cca9201b7309f90cb7d311dc3494c969265f2e725c540c67c5d395f1a065532d4c4ae1493245130d4394ab62445d686a75ff84366aa6c57ecbca91770792939c66c9324d32e14566d3da7ff7757940b7fa9584d4fba0575cacaf2564d7a522d58b1631acbe9bb71d22c98f1275f366a2aec584853962d8d9a4f3ae12b2498b82513cfbe165b810f7be9a5b9631156c18c57e69da4adcd245448dd2bb46e7df02a9982d395c72bebbaf32605aa43424fcc9455b428547a396325f5fc6aa0c0499ea9b3d984de4c3ea1cf3174a76b75d8543aa1d5a8eaa5a92634f6f2398d49e15f2668f727c76ad49895b28424326477ce9fbc502a21b1f2fdd6e275fe49b8c4afde8a323d9747026b21365df523589ee4958e1be1cc8eb5d12efd4b16a1d439bb9f9c10e1ccdf29fb4f0b6308e66072497daf692e06c31042b973926389d351e382c1d083f496dcae29630a02c32896e4daad4ba7fb2f348faa266682c6c7be484a72dab0f1cfab6e2fd0b7d0af31ecb4b9092f94934374ef59eeb7c92e34b53fa9bb2f3eb5892e0a9d622e0aff51e99439860ce262ab101e4d4a624a94dc62bfd63a5963deec39892d18118d2927d385ad925a1c1197eed67f33a384164698dd678fa633791685383ff9a3e7e68f659145cfb3a32299e21b0b835de7acf4c2a2fe247293362585f7159f7f5412e98a2cd515ab7a5a561c5ba1b60955712a65c48715495e25557792cc5f5bc5ef513da63ca94d8e51c53569e25ac65c2af03c394a9749db561d2a7cabe05912dd2934bf942bee847df5a6286aef322fc713ab4b9120aa25b9fbe8984b8a2ce70a17b2379a47477146d56a13364f5e2a8ade3d3f977f0ed6c1509877e7b32a94a70b415178c9526bd9f221fc841fa2a67f323c9d9c278a38e1544c96ebeb7422f91393dcf165d3b7c3097dc35a652f9fd0ed6cc21cb36dca5af5d399a309dc2479d763ea648228a1840a4f73162c07137f07ed9ab61c2ad3b9c49ece93796c3e137d4b586ab2689acabdcf5722e165662cb5e8cd4e89e225296f747c1267c6f97e0a719a95c46d55fde227e5a870243a79a3ffa6ba5f86441bb34e579930633ee2fc5937fef89f55e908fcbfe236fc04bd116a8cfcdc1c3f7e868ce83775eced5757731154a577b5fcd5fc144149beaae3d595b22411e5124d58b9e98ce241045b527dc6ce90c93c44f94bf0ae134faacf1046936225c4bf726685d842a664399774ee1442144a67553c83e0425dcca92113c46361f2a6cb07c2cbaa25e61e31d16e038842bf592a5f3fcbdafca1befe3ff714d3926bfce0a6e8605f62318568fad078f44ae7211ffe2be95292523733dcc3d12f5ccc4acff6440f65f394233b2ba3bc3cd4a9b249a143a5b91c3cb06216b3fdbdd7d91d2c1d13c6932669246387fed26ef0cd5cb397a983f1ec4cccf11d835e0c1d1264c367f3eb18c3c5cc414b428d77cca3f315238724292d8ec726b79a4c1c8e2161e6b939c9f9c3c10cab55251d99e37f032715f2a25887b7ef863ace72f8caf0fd7c1b4cae1a6e92cd863a9be45c360f1fdd359c357d7493e28410570db6e612f73b974ea669d0362ebca5cf285a161a12d3d5f8c7d01938a9f56357493b2589193c93c7737de8f7942b43d9936b7ec4df87870c65924dee1cade7b1338642c5f8f973e88c392b31b0399b9a6bdea4d454188e1317a9b5d9d94c81010fb9f1fe1335574c7d21d1d3c7a2f2bfe4555e603343538afefd665517cc5bed174ae8d24a15178a4ea2745c0b1eea6fc1281db570712d34129fc25af697e859d8ce2eec73ca1419c742b13b95b82bf9e1f7155615cfcaa1648974ad90e439a65cd5b75db255e0734aad269f54db2d154a772da184750a7425e9145be6c454a57087f99c268ce889c928d896bb297709f61f84c21e67395ee21392aa2fd92574db3d4ef843c3a4b6a7096a727bb7caa19d1b13b21cabc45c527c70cd12b8bf18b54f104ba6514252ea2ed3de55d32b09f576c88d17fcaf520509d55bca12ed97ef4b21c011306d4b37d267ed9542002314f455d385264f5e2904288226ff6b9f5872b0a8140210c1b328eaea7ec2c7a81060085a3cd13e5892dcf00920049334954b93243d9b0f4639d67b845becf238303acb9e5286923a56fe8bae9345ddddbe58bbbf8478994baf7b8167949c9a84af4b352f4c499394f24dbce8e15d74a733e27ae286adba2846fd8d138f1f93642ed05799cd59e2e2b43e9f0d5adafedd82cd9824d12bc6d7e0d942fbe85193259582570bbdb75a5df432a78716ff9f76fe3e99459227fb13f760b2cac292c41ad7c9bfeb582cf098a77212636b8d061679fa4e9d5e4e53ff0a63d3bdc5ad4eb25d61a7c6bd9892529b1c8888085981884862a315ed89394253ecc3062ba8bdd42dcbede57d5985e12f9ac4a839c63e33f2e3873e1a6628c0862a98ffd825fb5a8a57290536525186bc8b5ead91f3282aead73da172544b3ddb076c9c8228dd54b1b710cb9b110dd830c593ddae3f97e33a65364ad109dba1e2a3b4fac549d1c9f9a959d4c6c6ea51fc1b5ab6ed37b4f52c0a93a78af9e4ec6679a2a1a8f3e94926c6f425d706149d475eade55446af3e71d6736b1d13e3b8c6ceb0e1094d3dbb58ead6ef6d860849808d4e946351a56db6fe849c076608f9800d4ed8d8449663888f11b226ae2431b261b29189c5badf24139324d8fd98c8db72637cf905b1d3181ed8b8442327c8b8c5d0bedc7f60c31274dc2ce14a8ccc1f1e1fd8a8847d6b779773f2a8498e1265f41424fcedfddcb2868d4950173f8d6f566d48a228a19b2926d92fc64c176c44224d133ded1afdc28790283cc6e8981a2fe3765ab0f1884ae3989614eec3241347e869b2b9a24bc5b49a4610f1a16233fc277d0d0c6c30e2ddfcfa37b397a470116d7fee8d933f1a3c654444240d3558604311e989b904f1e06ab1f28920757d437f8a92eb521b88404dd0b27c825c92ca3c04173dc222cc3befdd51b0610832c3c34fae7e2cf50bb1a546f5b025d5c15e28d82004517f9e7ed169833e8833efa3fce43539ff6e4310e53cd72606cbccf1f62dd8084431de6654b096730d204ad1d3dc4d674d67cb136cfc815b4f521263c60936fc6034ddaea9ca71730788880419810356e080321c70c610216ad8a02fe0ce817643d8e843baf759b305ebd6d4dbe043624e69d59d39ad6866630f8b9cf465e6de5bb26b430fc598242976cf7c2ab1f3d0c5d64da66fd237910d3c50b9c7539d1cb671072d69499a7819ad7d63c30e054d726bd95340b05187259338679ee2d11304481a6798e1a3d1500ad8a0c37e19d53b95699fe532d89803fbe51faff2f9d47a35d890831be65f6c4cd2461cf80ebe2944775a74520d1b70f8324ed62bddfd59a70936dea0c6f5fc90de62b92b0020820d379ce4380f1166193237426cb4a10aef3c92e226f7f436d860630d09b9b333ff73b9d12029708003da0164a8c1861a8a12b387d09c3931657660230d66ecf05c8d9fb4933c820d34e8799f771dbed4537a062daea7d3e0d919ec23820d33fc1fdb5e4af7329426694979f225cf368d88880c41c339889b4044440336c8b0941c65bdc9c247194786d4000d07818d3164e1a3d1258fd810836f9774ef5d1e51011a251011a9818d30949774ec3dc686e7c9b1010653ae749d36c927cac92322224d813328b086060972810b90414693c1be821f3ff48c324444c8d0818d2f149fc4d4120a64800c327a60c30b869e3cb27bc1d26bba0bb6a714d9339777f7a40a36b890989389d1f3bcfc940107a4808c326c6c41edf8e144afa9aa106d68812ac133aaa7339494648d2043cab09105f3d64ffa49dd89b58c98c006161c350f276172528ad238a3352022c2041b57c83ac96c3031a78889ffc086154a1dcbb7569298443644b051855a4ec54d5556de3743b0418544b1f829cedb4cc88160630af6bfa5be983cabb52d85fa4ca5ed2a18053a6f9ea4aa9a4d8e162878297c58debe8ad89f60ca79dd54314ef098ac60c309863e31693ab4e44ccb0a369ad087c9dcba9a2e6476d1608309740eb326b856c4c673095a99c5b08d8bd949aa0c3694e0c796d8cc9edc828d24e859f35758cea555e580321c400220d840029e35e588f89d24e7368ea055a7e6f075f28c894ac08611cca89e53c6a62bb1e2368a70f8e88ec7864ac9930d22d441b37a893977e5a69105d818c22669e5fb08550d6949c38610bc92afffc26609d92920030806fe6d7993f28f8888f81800308a29f30a7392247a9286193a08bde8e70fe9c84272ac86adc9c9fc9dfc0104c8aa400716b09cb25ae67e043aaeb0a5d4b5cbfcdaa0d6888808193aac604eabd099553a4927958e2a1824fde2eca42343d40422222343340d11913586e8a0c229bc25190bdb25f73d05fb735abd92231d5240d4a2c5b40a8f1b71e988c2b9dfe367aebc614ed4018522c3fa8f58b6de543f81d0cf134f3e93b431ea04743c7ab55cd8ec9b60bc3c6a524d8925fec50445efeb2f4d9cbaaf976088ed454d1dda75420be850c2d1d3d585ca9d24962410d09104ce7277e78d8f84e439ed8a3049fcf8a18e239c21ebea157323fc52d931fc97e053253a8af05aba64ba29d2fd3c3a8880c919e3c674e4dde7750ca1e49d4e0e9b246fefd72184fc72688a4c791db709c6e2aaa1d32e6f49d080d17f5a8a15e7f3c5ffc5bb2a1763abc59c5bf751831cbe783ab5b325c9eed4c43790a317e74d6b59b2c950f66620072fec50f94257d677e01e08e23a002202936317a5a7922eaad499cbae0b4662f39b1826be92a00f111117e4c80527adc99a2b09339ed50f90d1ed6800f981831cb8c03c6974b494f2447b72dcc2ac392a9f64d2b6d824217affbc3442db5a9472b2591e9346bd345a648264e449a13c76a57ca47146909115e498057e96991a77ca42cfeb4d259b9cc24f2c16fd6c78eb6469392f990316095bdefddded1172bc02350b4be1c4ce15d70409395c51c941634e2619eaafcdd18adbe4e8349ae44e72126705e35f6fe2e4268d76ae62ed3039259ca8be76a20ac345eb933def547c72781af38ef54d0f15458d35cfebb16de2788a2e6cd38ac5540e53f03e96a93ad75d2c739442d5f5ff549d49e16d4ccfd173398af3e8f4dd6aca191a444dd0851ca260754d9c2ccb512147280c8f493293db9097ac22e400c5a95e4eeeca8ff31f2e21c727b2d02458f81c799a334fa4d9ad5463bc850f7e8e4e68a9836cc7fe0c95737002f32a8bd1b3630961e5d8c4e95fdff2c534b1a9ac652b49fcd470e5c884af1999337a3613338c89e494f22795d2f806eb12a518a2f5b527c9a14c2cc1798999193e5f6fad4a949644f66ee39964614a746729780eeeb937f493c03d7547b7eb89a177491076b9dc677242b527418e48ac214bbf62d35e53d0043920516b709d9335cce5a8e578046d7fbad162c70a262687230c0b626dc2c87f06a91158d409de9b4d663ac7063918b1ecc690daece9dd06722c620dfa57f1444f96d41ef1e14344840243d448038d0f04012224881a1910e243d6c0800f72282247225e132b6d633ce9f385ccc88188e389a1761acfc7c31ec971087a35676dd64b377bc96188fc24d9a4fe945275788290a31047cd317fe2c929f3a49a908310c54d29e7c84cf251cc1c83302d4d6b5f45337a9b4310e6ce59232bf55e620d44f2e85d9bf8e7a07de70044927974b43bca8bfd39fe60ae56b26c97ff922d63fce06e7e4ca761da555d8e3e50a92e8365ceed5be27c30e76ca193ea5a8a8872ece1b7ca41dfbdf35eb61111911c7af03cd2cfe752b8fa2f471eacfcad299f878cd3090faada989c92ecf92ebd911c77403665a6069773d8e111799dcd52f920471d504f277c928f95a48708f1212a822039e89014a3e37b593cc71c0aede92db366dc4b1339dc75498cf04ec5c1164d1db7f50d21071caa50296ec9ae77d2856fe0c4bdeca639eb4beadd905ccc457b7b50eb9836b421b5739b2cb3e14be279b76bc2b5de39c8b106aebbc495d570e27a6a3049252621538e6dd2280d7fee127dbd5734d0b1dc842d6dbb19f50ccec58f94ecd1efac99612959c5a4ed7463dd6548d2bd9b0c546ffc5bbf110b568d210d162e878ea218ec123a65d32671e9c250fa6a6f2aa9cd0186664a1245322d091a3714d820c71768ed91937c4cf812af111f66680ecc0892860f1f0cc8e1054c3ae21f7f39bab089eedc5c0693f270c472700111fd35cb1cfab15a0320223880103d00e6d8022678ca50b9f2245a6ea406350022823480904044a406407e4820871658e992c743bc09b2d3488e2ca061d376333b436a252407166efba01564a4db332644474cb039ae9018fb4c3afa244f95cc6185aacfab526d4b899b56e1d9edafdedb4eeb961c54f8ce664eaebc64f3438d337ea8310211911f3fccd81c53e06e2c894132a7e8319c430a656ed5baff647965e688022661529be0259b506f0e2890d16c3e93c65ee6648e276c5ee1be3ec6a7cb5c0e27941ea67245a9edd49f3572342111b32cf627a524eaea831c4c30e5cf942b69f426b314e45882a299375da95a668d7790430977127e83070f63f9620ce4488255f1cd2a321f1111a9400e2424a734f52953c29920a201c40c2165e43842b9c395fbdee68b2999c308c6984fad8c5d33f9c95184cab66b5473dc2aff1c44404af82426d1f3d307cb0239866058d3d914eee510826b7a6f5b261e8c3d252943a5b5d6f41d609427d4424efecbfbfa85318ac67854aa993cfa02fd0ee2e14163ae8e42c0d10bcc4bc60413a23a53f7031cbc207ad3e7d0b7d162e542f4470670ecc2d413435bb264163cb5869a00872e38e92e95a027892bd1e3c8059fb5a6d3fc65ba5471416a87bb5b8e6e517a3d293d8e8fc316c56dc6df94e736f942386a510e1b976731bfd297c800072d38694fb08d3bff33d5153570cca28bee8d49cea27235e290459a3aa568b98275fe742cdee0394926e6c2622b2948ef47e64c42f50af56fa3a7a50ff5e1b982d1f063d924ab1596149d399b2559b186694c5319993e7331c0b18a2d2b4f6ca6fd875055f14b27b1df76532a87a9d842efe50d693d7526a8c84ec3832671b2b25f384e61a62c5e2646a94a2177347098e2cab1e1a36efb6f48d5c0510a7673664c8256c6a8a79f91c61a2958630829d0682967516dfd3c1b29e0184527f9a798ead39a5e0c0026e01085294242f4aaed3c774270848292da2cde7878a8d2f0016488ff581d120488097080425d0bcfa41a2291192b2ba8008e4f24fdbb899a5289c313ab5cb8c6132f3b895e14707422f7da0d0d25985882070727124d223c867936c93e035672806313a713e7634b30c95bd216e0d00421a572bfff2989de67a22ca97b5dc238309124175368f8dba0fdf50f378303429080e312ef9c9c369efc9fc5336b0c0100137058c2398deab1de761bbb127e8969bb3143623d85831209d5e16925de080c704ca2bc24ab87cc382491774f87cfd9704422cd5cf5fdb866fafec00109cc4fdecab85baf1d5901c7230c5b412f98fc6669612de0700479ab9dc42e2bb50b03400c381a816be830d970f372393818916887cf21dae2262f8b286649553aebaa960b87223609b5d0a99325315d1c8948be6872c86c3b6519c3810836a6beb568322611e338449f7a4f4cd74bf2d605872190b86c525869d4a660215cf34d91b72684a8b76acec4b83493d341e4b91e2c749aecdf2682e8f44636adde63764f204e3956fb97a4ef911240909ba40b97d6ad289f3f587a524ab152542539ec875b329eb8f99e6a34d60732859e49172cd72e8f041c7ce8a491f3ae68feebf21370ecc1941ecfd7f65a27893b01871e4a37cbbfcfb124cc8d230fbb4942fdb6e745c08107d36eba93eec4b034e51dee34262549276a07aee4d412ab321c7568d22c652d2979a438e8c0855bd9abfea095218e39b011be3963aae8be1612e090834994ff174d39c6cbc781932429544ab182030e49e12dd1de49d0f208c71bf814996366f350929c23222289c30d9f2466cb26a50de3156e031b8a72c998c924b76c08c71a8ed7e94b7c2c553c1c6ae883fa689a0f571a1d1c696054c358673371a0c18cd0acecd1a63967719c619b93f65035254bd938cca0c79208999a12bbaf321cca6aa395546fb226c1418636345487ae65cedb708c81faf5ca7bbd235f3e0e3150d2ef47578ee7b6151c61e84ee8eecd6937369361031c60c04ab772864c72081c5f28cda7edf092279e5641c0e185d7dba48fc1452dd489fa88fa0052031fe0e84229f6b44874db760efe1082830b5b2e4bd7612cbca31f1111f1d12a041c5bd05453a8342edbd5ef8888c80f7068c178974c4cd217e3c8c21fb35bb91b22adc7026525c6141b1ff113a700c715cac152471372435c92471260021c5670b424693266fbda164715ce9456a3677b92cb250de0a002361e4d3b2f2ede53b0423744e6b44a4139b9df62d6340a75beb9de5b9f27c984c249a3d7a73fec7ef809548f772e0f9ddeb34ec02dc8743ac1342b884de863a7c95bf131c1cbccf41aeeb3043d66cb945cdb9284470946b52cd982c4252b394948caa13e27ee61ee1912744f929c2dc5daa76647f0733e39e5cd1b239423933d552789f3a9229853c999c4ae8a137f229c2d599656c9cad91d82a2c13cf57c70082139cefcfcdf969c5582b17974d4b7ce1e29d10d60acb21bcbf74a7ed15f4ec25f4ae48e5cbe30e716b3ee0c927157bed10b2bc5a5bb24ef29c37c83176d90bdf85e62821bbb70452ed74409f917f33774b1aee55fc7790ae6ebc88d5c98f5b379a1926a77081768c65425f974c34335624690143820007eb8718bc4345f29153acee26f0bed5278933e49dea8857362885dd5db033768b14613557ed6d2c5fedc98c5bf7933cbf3b75f7b6ec822ed4b662757c8b5bbb128f233e7eca7a167c04229133febab7849b1dd7845f9e3427d5398dec66fb8c2332ff1c450d28aa2a42dcb33e90d569c842ef1cf3f9d5831bfb18a5353792731d368e6f6862aeaf392d65d5e5267be918afb53fac91733e5a242c51a315fc1a48eb379b9710a2a2f343d9c374c91799233656c0d12bdb9518afbfa728f27a9a762e60629ac943b9e8de54aa2ea28cc713669896215b33537b8218a22367ada93f72adeaf831ba1d8beeda3724af21982a293648fafe3f52776fa09c73f57ce55eb1a55f2c4b93e4a591e753b7ffc30e3063db8d189c233e6f1754fa2fe1972831389baf33e5227d63f0bd98449ca7baab3c99527ac09d23a4563e72b137fea9cea55ac6c6317720313a5d62445afcb3a8fe11b97d0a33a7c92e6bb3b4f416e5822d9dadcaa7312b355674444c48c1b956036a65a3cd4a5d29612bc76a54be9c6336e4c22a9eb3633b12309bf4ac85829d2111191bf1189731473c92c79dca753831b90385dbfc74ca9926534a9c123b695cf3293daf40d473c352fadeb11a396644444440890111111bfd108ce324b78cbb851eb4744447c888888e002182881083a202282821b8c30b7b2ce2649ca8d45f0257b1c4b529f5de08622522b29f545e60f174d129118aca28e6468d9498830c9951bcea40fc106f78ebe53e1af3943983bc6f0cb715943ab42ac194c74555642dc97c49caac4ea5d1f2570631097b9a7b7d6d439981c411843365f2af55236b91b81a0f76dac345c47b713841b80d0c34c0c99b12a4d8b37fe60051355337bf4132be5861f0cd62979a30fb977dda9598977b26ef0a1a09737932495a9ee59e0c61e7297f7144a92daa7bda1874e2d54e8709a3c34a53a3ddebf9d27fd811b78304e48bd38b5ffa4e91d30efcd697ff6ccf26687d56f23a3d74d4a19a3c08d3a5cbe6252c48c454b2e1dd09872f213bc4d8a4bc111dc984327c9a1e3493f8e8888d8c92139a5849eab2a3199e53c3043880a44444870230e7daae499354e0a1665dd8003965dd3d87d9f74de82f80807512f23c890324444d048e30d8d788a1a522674b4714444e40c1f68f88f3480b01b2e31f54d8a1e5d2efe23222213b8d186cd652f2e58fadf15b8c1862449ea655f3d26c95e47988cbab186a36dbf68728b55de8ef810628688881ace496cb5ce711355e9110d520311911b69c8d6ff4fe674d3b765881032ca0d34145ebf336f759d27cb3983270699dbb592536c123383494e492871b6c385923565e8e6427ede7349a95d91c1d7b13e212ea6dd932c18b83186ecf3679fb4e630961422864376aa70673567a11d1111313e7cd4408df50e435de29ee267799f1d70030ca7acf36ad279b9bb9b811b5f2846c787f31ca5f33337bc7050539334c91deaf3d9054ef3494f7f8c046e70c1b06b1f3a5dcc6b6e6f6c814a7aebb6b7221de388160a62daa97b7a046e648113b4ff92381525e583d4c0350d1191338200e1c0b98185a3a59c3449d89c0e5651c3c79033be0137aea0b4d5797a8d9bbcb753c30ac927c6da98734e315670a40a7b099b6a362a7f3e01ff27d881082b681253c5f9dcfda6043b0e51e88a7269c13e9ce88f327618a2db3c6ae3be2b62f911da5108265ccddd24e1525f4d883b5c8aee9de4259e8338d48798daf350f2753b04518ce253d91e0d44dd9ae3e54d5226bd0920d012c552c578d993e5ecf88327dce6fd916e871fecf4f94b446673febf0f56bc1d89554f25fccf07b3d5f34eaf7c9df4ef21694a2eab884b0f89a9673f3b595b2c3f0f27d984ade8498eb1c9c78392739b67919549f377783e7fdafdc5e45ede0ebeb69bf5e6a94ec275a03ec77b55db648fd2e16cf249aa7b4c56e61caa888de1934fec8972d8345344e3a73b11e35074ebfef7f18e29091cb6f0276bc7bc9eb97b032b96eb3b93609ee4b8e1dce97c71f3477bd606b604efacf3e621326783614f1efb5a0b361b5f4376326f935631a68cab8134d1ec644ff13418d6e34e8c9f33d9e468d0f273a77df7c53f833e9feea6375dbe6f06c6539b6ca7d191be0cb966ccddb993e124b758ba27d964ea31945bb118cc95ce3e49a25979360c7c95785134f8c53bc1600a267bce14d25ec42f6c3b9d5137a7248d1748dd5469b31fbc4aba50d25aa9a909ddec7181d1d8bef47b8d790b5bc57831598e1612634ab2e96f9d85e753bab069692c785df75d2f265c4b7d85d379de24fdabe510b5158e49923ba6fdb16abb0a7cd0cb35b950fe692a785765d2b74cbbc5532886de314deb2697ac14e8b1d6581dcb4e0e4621e12e6c664fcfca83c2295610ddcaadb98e9fd0dc6a74a818cc530e3bc1a4270731dbb8099cbb6b92847e5bd79809553c29c5e4f9931dc34b30ba3786be27c9c40a2bc15b8dd32496247b8c380966c8595dbd3b217f2428b2fa79953388fd473855bcc41c1b411d97be57cd9cb92fc2929a1d1795af4e9a08a5b05e13bede4d1f4239ca97ddd49b70ef10c22786ec7d8783610ae229bef92df60223b17ffe426cecd67e714b8692afcef35fbed8c2ea925ba70e53f6e23c17bb37ffe64ff202edaaece33d3627bb704ea8d3121fcfe874f147079d4ae7e230268afb6b7051ea9c72fe5a7b0b5ab3539e4bdc05696d61e5dc4963fdb5f8849feb08cfe0e3d382cccfe2d7fa2cd8928394944cd254715998ac4df43049febc3716790adfaf8cc242bdf0f770e9dbd97c451fa6844a226fd749572453e25b8c9d52b4b115ae67ee1c3c64051329b69aea41ee641579729d552bbffe5461996eee58a26d45960ad2dc37ff532ca651c1e5e46219253f87cd2968893869f2320593214c9c4b29b864da27b9caab5b9414a47cd8ca61d251a85a5144343b67f3a2f8f250240613e6777c6b64507452e8e4abb273b29fa8faf5eac3f786cc3d615d0a9a254fc54ede09d3c51852f5c9d7643981fa59d491751349a51a544e90be3435f1c61bcf7ac94cb02596f44957bc7a4c9c2439bd2fdafec92597e8dd673ae62496b8aa24793a9d28b7b94aec49453a5b0e25e8101e756df59a633989663ebe438949491c25d1bed9f725fc48709b62c8543b4282ceacbb78b2c78f1c1fd1addf45cf15cf90d111e6b5dc20935dae2e36a2d0c9a36c9eacd82932629352d8492fdb12525c84a922730e27acef9ca888b2fc87cf7b1ba3879888223675a6fe10115f92fd32ddc894393cc46671c53dc7f0da0f1ae2987bdae25f83c4070bb1d526df9cbff2e59f1087f68b2dc12a79ec83484afa1cd3c6106b5d1085ff145fa399f01a0f84b1e1c3b9c9e7967140bc96fdc25a389fb1ffd0c589d8a8bbcc20fba1f3b5b14de920baf661cbb127f9a6d142e5031e2fe67d13937cbf3d24ff68d8fe4c0f87b5dbcb8f5af29e07b3da892f711f2d67f090985573e2a58917ac3be496f2b9a7c323b74362a2c6988325a903baf369c344091dcc1f36a273cda7e81cfa93e266a530afa51cb4942c2513af4b361e07272bfc9c6c9d4eb6e1a06d36936fdf6027b1c4c8ac1bced1e71fa9b6a1f658f2b2e3217db2c17cb27ff2728bfbc935e09eb1b963a206db4e4c92fc792fbe240d476b6ace17cbf649d0f08d79fd7592b37c770656aa9377940bb239339c52ce16dd634e9d396538790a699f3a64b862f49cad3709f9da18e84ddedb2e26496bc4d076ba99be6138e87cce7c0586538812ecd3745fc063ec1c9d72ce0b66ea66e554a7b7772131c9c5b9f95cc8e492f33edc42a2d9b46eccfb1d6a2db81b37e8c75825bb59b0deffe23aff525c2c14dbeec249ea15b4bc612cbfb702e39d692c5aa5f0c92a986babf5764285c4f817532521a64f92292425b229573149fe914262a45b8685d5b72d0ae62b394ed6e4933b83c2f5d229845e650f3fc1abfdf78f9fe973c6099ad8594a3e26d13ca6095e4c6db934c304b358b6ac193ca7642da16f17cb31b9625b394a40ebf6d4b37292609a9871bbd6c48c3a12d0a4a1261d3f827d9249a58c2f795e23fc6e92278b3326ce45c032625c2f2529bc4a84b38879772cad0043a052caa9fd74e993a015400806b124d26296a6420b8626a5f6e7fd28164203c6a639473dedbff524f60b4ccc716eb423263ef3c57562c624a6d4294e65bd70df344a7079c1ba6587cb9a68ea2ece4ff163e518d3a5baa84b43cc76ba4966c945319f9b86b0b3b28ae1024b5791a133774c21bbc562792d7d496c796f8b2431c4b767febcefb578364edc940d930fd342b7b8fb98c4dfe466a1fc6e8ada494aaa595918dc53c692b7355f3616caa78d7ec989ca24b0206db4a444556fcd79c51ee3ff65e6b8628ff79d2f9338fb9b5694a3a3ef6f6358a179a456b08ade99625691bc743a9d5d15a57b6b864f7f5b792ade15136345e65f6d541c2e94bc9b4992a2d9a7b865f26226ff0a9335459bc4306f952ffb6c4a6168a53561a4d29f982285769eb396e5248b5e350acc92b49636748c9f52a2e892fa79aeec963ea542516d9e381792d75583e2ef4ddf1f3f55d0f0278a2b71c3fceb89c3b27f4e679dfc643b81a72c2f57a2b11ee5c45a312116532594b98963c678b59e68e23fdd8c5199d5c9cbc4fa9eab6636986863f82c93ab5cc2a8dadee999f4de2a96d04e34395ff68aa256a9447fa268f84c51a24d51369e245f7ca54ce272eb1c4e0e7af22f093f787cb1126d7e8f841ad72f6ffafc39a10289f604effff89d3b43e51106cf5117e592688a8a23bca898bf6752f8f91b41ff49be2e651e52678439cfa4df9038f37a11874b2594bcb2224c31174573f050262742910de939bb344b8a0873f05ce3413c84f7ef71fd3d64fca021f46472febe39da868528b483bbc554a2614208376b08f124fe27758338aab44e3aab98334710bd4986d63fd953cc0944de31e58b099fa93a80d033739cbe6693f33f6039887c4e597396fd60ce0e8debab9d34d6072d67aa4a6597878c0f5dcec9234bd436647be8b2c925486425a9337ab037e7e7d3649e43260fad9c341ed84f7973aef6e37e8793c792e358f4bc69b143b1daf5929670624d24461d0cd3413aea058d93480c3aa47d276eb6a68c963d0cc498c371f27e9de6d870398a21874ebedccd6f611b88118772b6f06da9a3e498bc1870489a8e5142f6cf4fd4dfe0f45e55a5b4adf0dd709afd94c4f57781186d40e376f4fb13546a13830d584ed2dfca5944df34b286f43679874b82aa873f440d35984bb8bea9ebc8a4c1ecd6de94c4764444040d7da68a56527af9ab943350eb59e9c4b40831c3d1b782bf5c4c79cdcbe0af497267064f5d715286186430f44fa5ae92cb85950131c6f0c97beeb270d37f9a18b8c82bf14ba368441e0634f36fcca6100cad8af44cf8fea507bf9027952cc993e861d50b81185e7063def679f11cf3b206c4e8429e849bb7f8b7185c20f3dbb45aa675a41b8980185b10430b5a0c3f9a6db17e4d3a0bbb546ce7bb8b9bc4f4471a677c600d0c0811a226488188c81a2b0616cc74d19318a23ea48d66fcafe0031ce000903384a80e7ce8408c2becddb79e7f63f2133e31ace06eab9c68b3f761f31c10a30a955f0a796252b2e980185448d7abcc240b8b0e6f4444e44e4de1f9f3e49d63ae4c948c8888882185ea3f3696661eaba39068d9d473e518282426334f8da811f5f488880809c47802faa6329943addd3410c309d7c797374f5f52b83b2222d284da240bbb1b96651381184c380308100188b18446c30c1da0a1868f2142d40882430c251cd3a7b8f1cf4aa32ae487fb0802448c24b0f1a4e96c4d9b75d6213fd410e23f7e98110211112066d8400c2460b12124f5bd1111110f8871847bb7e41ce34ec66a1903621801c99471662ecea7ae5d204611b293dcef18b171e329440c2224172d6b3cb743b0336b4ed4244f20861092d5e32c674ca9966215e808c6e9e4b34faff3836e26d0018ca4b3c9efa169d3ef2c43c72f3eb1638e94db2ed0e18b240dcf9d8b0d053a7ae186a70553b30feb8c0ee8e08511275d7cf41236edfa1bd0b18b3b2f23e72f96acd57d40872e7e910a3a25a7c85fc8a311048819b978b32cdb32fd3a706177df59dc6c62cc8debb885739ed184fd75dd7175d8c29230796b57530be62cc7247bab9898355a1ca2ac2f83aac59beb023a6661eeadac3757fff0ff888808058e0e5998e1f2463b3974ccf263414dc963bbfd76594f74c0c2d0b1194e427f855292106d67154e664b230ded810e57bcb1be1ae3e48e76d20a36ce3e5494d5fc9c658527ba2693d3774db75c859664bbbb8b1fcc6da38adeb64f08e96e1d7f5361bafd09ed0e1585aa4c173f29f5e5764f03080bc1808e53f479254ece57fdd0610a3b7dc8aad09dece32ec5258cc514c6626acd4b99fe00902169ac1184c900810e52f09a6286556d2da9b4818e511453eaffccc977554e14866ccbbf6852a120f3ad75c4745e8c221a3a409194b539cb9d6bbdfe37d0f189cf2ba7a3895727a5ee89f3987c6296ef982a7b9d304f5069cd948f8888e8e00469a92ca79c3b9da6a88e4d6c259cfef589d18429a364edfb64c238fe71ec7b4cf4f91aca6b4e5b4dea12476c97e7181225776c894e4c2efdb43811a556827fcdb152f7319fe628919af4d134454b9a6312d03189aad246d5909b924453126ace55256bd98493a521407e9401e4879033744422bf34b9d4ff556b4e20514d6f4ec254ca23b28ff2ff733aebd63a42cf2475ca24c6281735a2b4f924379c3023ccb623956eab3e797411897be8d4a9c45453393a1441c997cb72068d7d972411e64a9fb06e16222e937b2e79da0f91777f5855c8e57cad21f6ac21a71f55bda25d0863e7cbeba371421cbe73cc99b404490d07c15ce7599244abcfc408829693a4b00caba49d0500117404c2e496e2e5b0e1d1f406820e40b0ab162c54aa2322223930a343a0e30f6c49d56929f3524eb61f0cab127b9a6dc3d4a50f46dd4abffc2e37f7f0e1b06a317b707c5e63902be95297e8e194b5a32e588bf8f6192003043af2c05d8eafff77425d2e011fd08187a35a8e231f6ad2a4cd471a7a860674dce128b7d934c9c9d51963031d76289467ee6459a2d9363f74d421f936a8bb98594a93a58362bb96deb4e4380b75ccc1f610265f888833880741810574c8c1246f7a52bb0fe9e5eb8883e2b91b3eaf09ee81c325e62485ed5fc71baecb5b5992be232222427e9831440dd4e186d3bf9598859282c62e08101f413c033ada502529b75412adb5f2c8867a2b5fc99dfba47f53c71a0a974bd2c57e71cb87d7200890171171810e3598e3edea4b1849f5ca888888331aae060ebc8c770dd440471af849b3f6d098921ce3bb063c8d3552a03ad060dc8c31dc25693fcf6730e5dff4e73cb1d2351d6620b3c7cbd096b12f2b26206388193fd22823c89019e82883569a61bf3e26c3b75993181d3578d456c718ce2dbf9da3d679b7a5430ceee534b144cbedc8d41aca02203a4881047484a1ef64296df767c7d0c1e0ad49497252ccdc717d612dc1bb6673a2a4cc18b0002fe416839ba7544ad7f12e1c2fe56029ecc685a473aa7c9eb7a5e2da022776a7ca56eaec29a3430b7ea8ce27189ab0a39d5ec92dc2a047263e4bab3979e55abec4053d3061cc92d2ea4bf54bcf31e8710944ff355dc8fc3bd944063d2c51de6fa864694aa9251a6684a07a5482b63c6269c2ab18f4a0448f49b449c81e134bbe073d24a146946add4659ded5231294c5a0a72f24b80aa64912f6526e973c024d62ce5d922f7e25e78863b01822b252ee4bb74723941a8fe26326ef9697831e8ca0caa35fe8d3d03f961df4588422d62eafbf4b2b655f2722e1eef3e5ab1c172f051122e87108ed2d7ad49eb81e9949410f431c7ef139beddb37363418f421473c58f2ac943ec920c28a3860316d0841e8430091716cb5532c9fe73e0083d06915b6868d48c3e811e8230ccf2524f651e11cd0a7a042259cd95c964c369d0fb043d0071db58264943b48c883dfe702ab14cbe3c9a64eb68444484020030420f3f68d9c40a4d962fa674e94321be63213f57499be483965f525c99cc6a7029d0630fd6e651afffcbb4b0871e4a26eb9960a2aba798cc4392dc932fe2153456aa84f4c0c391f193d059610f7adcc12485750edf99f4b0fbf8a1660ca140072981097ad8c1cce4e13977f7a843a2e61727b535acc54ed0830efa260f969b265d5d3e073c5a7b5add57a6a81cb8ec7f726b7eab198e839ec2d9a5b471ebee8483b7a5c1244dca7b71f2864db33294c99750296e821e6ef0a29fece1de825f5c6dc82aa55e935c4dfc90d8605ebdaa19cfc9c3e21e6ba84eb613e62f27497f570317a96ec96298d608d3e07c8e095792d7ed5fd05065bcf736e1f2a63491a0c719bafc1f5bb74983a7d60c8b75d77fb89d184f2d03dafa669ae332a53de94106ed2b8cb497c67bfd7a8cc14a6da279d2eb97971e62a82baadb9e38b6d12a3dc2c0ca676ab890f7e1ca203dc070491bc42ad4c7b612b51e5f684ab8cc7f3d9d9218eee1055dad53c7cf3143da6b1f11a2461a19e8d1855d3f58ca4c93bb4fba064174880f332ad0830befe55d6bf47831b264444444023db6f0c5d4b952c80ae1d96424013db4b0091f56937bfaf3d887a04716588b6ee92ed9e1b1c1423147bcd626297b7ddce30a9705abbf577b5881cc71fa42b377cce955d8e63cd55ada958f692a189e2c945637054b336ef2bda0199e49a12973cf26a78fa1a61e51c0b2b59ec87fac1c61550f281caea652affffee4b9c7132a315347a9b1fcbd5c0f277cea19a22a2549a9cef76802529ef97b9218ea840b0c7a30c1ae64a323135b13795d8f25e81fa3c4649bbbcb2aa1686207135f630c9ee49370fe5dedf20d09a51337ad97981bf4c21e4730c592d63c09aed1626f0483b8c5b974f0e41e17610be295a35f8aca8c8df4208266612c991c5efd4e8c0d7a0c818d9fa6d1358344da55407a08e14c399f18e521ab91a96000830e3d7a77fd1dbe2b1a18e317fec63c9344ceab4ece17f9fe27b9fa2f499e62bd38cc53cc163e965d5e2363f0a2cf6d96c92e4ea6e663eca299cfb235977d8ca10bb63dae591c3d0463e4623d2d395456ca857c845cc00463e0a2ca3cd138ad6f719277b57e7f330264081a1f40c118b6b0d2fa52864c4b31f932c6a8856321c3a6333976773c32823168b18bc8a5cb363fdee905c698051adba33ed32d847863c8a2b6d4f9d762b2ed0dc1608c5898aa3379c9eb39bae30a8c010bbaff2dc99d293418e315c6c9703b761a1dc315c6eb44c9e6a2513a2a83315a91ecd1571219ff79362b8ca6b94eb0165791c9b126da3d9fd8aa5105dd725596632615d44e8f759267e74234062a123a8625b92f3ba3ca639ca234c1c61153245bcc26393534c7fc4b6169f4b1b426947d4e2105979ba67e33bedf854681497159df3b7566dc65308628be9cc1de84c88bf1eb506c19375b6b0c9afe44c70005276f2ec964cfce18e3135be578f299ace3cc4b630c4f7472b935f53a6b5749e1608c4edcf1a5c3fe6bca593c6370e2944f316ccebaa174b309ccaf84ecbcbd8db13561aa5d1342aad26c1202c118993849b2aa5ac9276cfc1513c7903e97e4205136f307c6b844665296b09b56e65b6389f2a4063b304625d4542d9a19b2312871a8c91b35cab649e1e3c11893c03366dcdb6d2aee07c2189248ca936e376fc2c5cad6182312e5cb9b425afae8a6fa1890284b8ec12f2d589eff117f8e9539f3625279473c1e5a4b3fd8559cbf11580c66ebe2f3979567c47a82c737f1b2f6a65f84317c7687f8a7f2d6156166ad285bcf492acf134144c4a4fd5aaf6e8e089394638249b92fa6ef4324f8f4bea63d51ce6e4350769656dbd3e55bbb10d964f2f4a1d7a37942747ee6c935b6b2ce4138979f42bfc882305d3532ca5db2f87120bace7721fab7536f0c886aee73e52cfe031b9b295686589273fdd0c8892605354bd370fb50d88744a8da446b960fb6ac6b28c1b27bc0a7b76c4f12aa2429ab073aebabaf3ddc9f9e07f344d4df05cbf8613ca097b2b99571714bf40e7985cd477759c8246a074b2a39c8c50922b1691d9e753531d69c9c45523a781a9ec494dc3fb83e872dac8f8713e6c2ee94c3a3c9457a3264fdcd38e47d9e333ac7ed94241c4e6255deb426266f946f703f054f16ac24f7de0da7ebcf7173c54fdeb72131e325d124660356d31d3bd6b377b8063da488745ac9ac4f0d653c8b4f62be5062d260494f88864d6c7734f5331c5132973ac6ec122766c8d3493195d67e25d9a40c5aba60d1764912a34bc870e7133a567c0ce6798e0f5d766ec227063d63cb76fecc8bebc2504c973956670f188ccd29a6d464b29d79be90bbfb7ac70bc789d7259de0f13673ba8079aaddcb1734749c0b677dcf2477e8a8b26ea1245d06af282569fab4a056f659d1fff1edb260a51ecd7163c1750fb9bf1a2e4cbb426b53625638865cddb831358916ab8296b269a97b4385b7e4b8cd72b192c4cd1496eb0fdf38492117a9be68a28d495b14fcb8d8fb8f5262590c140c374fc172c2f3869f909c65ef8d962b5cd409c5bd6fb2144b8a39d904c4f493899614133ca9435a9242b5843d2e84e57025bea6450925b1c39325714aa3a42481bed18ba651a22a8f84cefd3b4ccc116a4d6d319ee4d0941be114958c6e3a164f2e42c9eb77ff04a9382e118c39324d6cb31c323a84a4149e9ee6c153c73184d09a94df346c82917021912749b95d53808147ad8d7db6454bc4e3179925a9a49c2429a45e43de8c11213e78f8e272cdd1cdeb96f1f368e80f13141ebd70b72ed57c6d23c983172635213fe6b978ece2934cb4309d4fb4d2cd431759308f8cd6ecdd4cc981a3404424071ee479e4629feee89e9faa1d3767140f5c78f27db0347f7ded3203282083330e5800091270051eb7c0c2ebc9a19379268bb638d9c660a2786c945cb5a02c464649258e494994164a6f6a3439cd7a65c40132d0e0c0194082704044048df721c439881a4084f09805f91ae735bfa24468467e68101eb238dd684a937a737e5b4644447cc4e298bbbdb19a27060bb580072cdab7b7941ea3274909d11f3ed20012a463c0e315a7186ff7c2a7d624b20a78b8e218bfadb37b48bbbe69888850807d0568ac80472bd05025c48cd5edca344281324810a40442d0f8c18315d97ddcc8089393ab5a060928008027f05845319fef848c3f22441587d8a7f69897e42679018f5464e2a7187aec2c8634e0810acf4bb0e095d35b127418f03885df596632c7254d160772c6194058081accc31468582c51ffae7fe4322222e2a30c120841c37994e212d43b756be80c518f888850a00c12f8e0410a4ac7cbec2fa90320191011d1019020419ec7289c9dd64aa12d654d8ae27709994728d2a9ff8fa1975c36ba021ea0d042eebe47dd8b96f74f9c2a49ee26baff46e99e4810bf88f0146c7c2c9d28b6fb5778797f4b17275ccbb265e269f8bed026b4bc3975ead568139326bcfe11d9d0e998bb92094cdeb2fce0e1d5828d892f5890b1ec308f4b54d23f17ca24b3ac938725d69cf34c6c8e173b2c3c2a51c5e66efe9c82aea77950226bb1fc415388c72490b390fa75dfb2aefd808724d6f0a8b99f19f6fa241257feb8b9cc4ca33ac6073c20715b5a7c0aab24dedb232222affe03063c1e41598a56597684d93ba678deaa11495797aa7b2b8cd0cc24b977ed7783942cc2133a76bce335e3731461ea4d4a8925e45a341351485e38b1527b07f910f167b1dbdb7f0fc15ae69eb06d2d6fa67fa07f983192010af03084ba69cdbf3cbe935816a21436afb8e64f884f744f5e92b40dc2dd98b9713324085676de84aa2df36492021e8130d8abb7fc8cfa5e0b88deccbf438fe769a778fca18d148fd694f656563fd492f934f95a65657d24880f41c3fbe0ca578e279778e247950f5f583a8df3d5d7b266c0630fbec85db6cacf3cb5ea21f9f7b793899d24ac1f031e79e8fa6a93209aaed633233cf040adc60ae366df215318f0b84339a92629c778b5b6a81d724d29ae6c7e3aa9322ae05107ad32bbdc93cc021e74f0efc2a764528979cd27e03187e246ea52a8932a653ce4604c9ac724ba92bc9f9080471c92e4987fbcc23a5d7b3ce060dc2dd378f3144f533cde90d47de222a269366a3cdcb025c9ed62f0df183a4683471bb8eb247d0e29ad6e260f36943bd542498579d0fc1abc5029a7d0687e4249101fd1818f20ea3f98871a924f6234cfaa72fe9606cd4fcac173667da5063cd050184fd227fa7966133c835eb2b776e64bf5ab98011d93afdbc4d08888081a3ad034944719feeeaecf9c27145801192ee041862d3eb947efcd18ec1befa97c5fea278918f254bd34a7a21bc5e31186cd2c68ccb82d7d82c53cc0704cb9ee841ad58edba5c1e30b45bb87763df77457f18217962c5aff8dc68646ba80e6a87a123ba63cf672c1305776966ae9fc4db6708a64d89cc653a69403021e5a704ae4bfdb2ccebb9a85a7734b8d761093e7c4c259a9debe711bc2e30aa5f3287965b61b256c052c6e09dea2d1a6e239448d1104f1211fc0018f2aa86bc26dc5477950618d88fbed9c08784c219d7a93db36e7957746ccf0b1061a1a04880f15f090c2d5ff7143ee1a1111a1008f28ece13a9cffabae668e88080f28501ff31fd77f29a65f011e4f38e5f3184caa9f8713ce592e67fceb2412274d78a3dfbcac6bf37bcd04473fa5628e39a658138f25bcf16a763c09f9b5110f25587ae9b21fd4e4bf78242113a5c4fb2a5645077920e1918e276f3ef3f114e77184f3a74bd918133af6f1435730071e46e8fa3ac5d49898370a1e45705f4353bdc37664368d34d420425963578e25775aa6b0a7b1460aa8c06308264fb79356631e42d0d25e8c21f6234f5283a18e669cb9247d97653b8051562b7fb1e8a794a72f61624d3b7cb1cd96c5e6dd0b3fe5b826e5acb361072fce1be2630e99e46ee45d68b2ba5b4f94f40e5d741e4ddeceecf839b45c94fdf7557ec258540d0d76e0c294349d10255e7aadfd16e5f850e28907831db6e0534fbd3556ce227b2daa94b564b9c717eca045a52597a85333b5a2cfe2122d8ae9b454c79295459b7d233cbe5daae0c582a812cf8f269c94f3b603167bd9a7948e5ef3b7ec784571624b555acf73ddef70855e314931b49dea698a0686b0a315cba5d92f49d5c081ff503570b0811bec60455966a24d6f8c821f3b56c1bee624e86e09ae25ef63872a70add2f7e8add17cdd808888067115ec484531e7f08c16a152198eec4005258dd44a675b933ede710a4fcaed4c826bfaf0cfef300573529e1d0b1fcd9f77604729caa01d5a627693e6e40fbdc10e52245dee381373a8778c22f172078d1b9ff1624a0d768882db8b3e2968deee706b4728cc72e17ee77f92580a0ad2df82eb5bf213eb5ff68fff1fa6e2c4135baa4d29da2529dda8067674a20bab1cf2e7dbfda43871564e9159cff176ddb10993b84fc9f1a652256687264a99ebe432694c8ab265c2db9c4b2569282979da8189535d92d2efd4878ab7e3128c76dadcb4f6a5656508d11d96206e4c37a664bbb5cb48258a5d67413cc6acf8eb0e4a281156e7b9aa1d9370ffeb35b3c7ec90c4fdf1ef3bb77589e1b223124db50d095364e6f920635b6d3ee28ddd71849dadec5f3c654723f0ba8a9e4e12991d69072374b377c722084d9a3b994c47c38c3d03088b8894b1431167b69db75cff99c96447224a96de9aec4b9a328e98a8942d63c1816191482010880381204094dd6d00d31308004850200d062391348e75710f148001582c1c3e36201c2212102010100e8743e15020140c04028130281012868261f9340d73690dc7e268ecc132f3ab7d08f69452903d13b378a85e883ec2f838dc758fa538096129cb4c33b99130036d228a701aff7e3b5d9689839ce908156e8876a96dacd63238a23ce81642259a5cf88c489fbc12573add7200e1019a7ed01bee7423020455f781a8b7cc0d6847d600a6c4705e5f2c8b78547e7340787a67180d25522367d229c3932dfa50e7e91cf38aae701f368ed0b804d6db7f001da6ff706179da85ce187f75f620017e6823aab5d87a13afda80f56b7a7db383f7770d6eb43024a1ac5105a687d458c79a614f2291751048bc07d81158221db1df52e851d2365d249c1aacae71662010ccea19ee3ce33dc8eb9329ba276c6b02e4f5ee949b4ecc74862dfe2714855681643c08b820edffe8a187b308bd74cfe8d6acde7af658757aa927618eb6ffbc5fd44aeb62181d5650eb1b084c4ac89b7ff9671251e315491945a05b43a247002e416c6315cf4126967456b8c5f57b96ae042d85a83cbf74c75dc0c1aaa0d27c15e3da4dbe4e979e6afcd9a1ed0e76f79ee74e90b765097f38e0871874b53800012c19784fb6323090530079b2a3d8510fb64e3d25529497bd80edaaacc7796a665aec52aa1575c5d556bfc61809c2bef74c2ce8adf722aa24da578cf08602d71ab8e1024eab0014924a2b5b756a9d513fc9c9d64e26f2ec22108e177d7454fa59959bcab05eadf1b40f7029ee0265b2979a7aec564347abd3c4c215b72f5a772df5022f4a9c1dc24d7e43e35c87fb18a909616f227f1ce9407812b974224e6d45b3293454674e6c616ea8ea3cdb52b4df969e571711431df1e71f50acc95e85d84c28f5afb66f78a3b7f8e7a6a5a254bf41d082fb031c92a3f5d69c91072a90b2c081af32e660dae5b2d27fc07804a7fbd48b0749691cf42ac9a9b1aa3773864734e31b509d79980376459fe0fb0ad375f269f36ebbc58dcc25b083363feb323b838591210d99046f0da9f93acc6adaec55355e7a5cc4e2c88c1962b3253a64f29dc432d68ecf0b6585bac46c33bd55f6184ee8e53747189a55e9b84c2e5fbe274c56c3a84909e86c23a6a824cf083e9788ad7d66bf107f45d5748d27db6ee87e71b61da306051d21aa0b3c4b44983695bf884c9fc6ed6e9480e05023abaac13f7ddd31a7f7376b2bf47310ea45eacc788696eaedd5e3128052e2d25abab83780425951710469c3ce1f76570d1fd93ead37b00e3d1ea9c5df25de31e9cd6d6e15a5feb34165b585a81c5c90eb84a4a2296cfc4620d1169c4f099457f765b3fbaf8d6330239817b449e2d601641fe524a61b8ca6e3cb0e270316330a803327775d41e721ee24d097c8738348064230b5d53aa8d88e3585b78a7c10a889d6b4f7414edae07c077ee0b6222bb03fd035b670722a3915367380747b7dabd01513dcaee369715254f5ca17503d4658da4b846e8c5706faea66b16be2a8ad0ec466ffdae459d4465da9ae219c6bed6becda642f146a1f35728bb2a774c947116b656fbdda92b2170ef82173dc6a4a400280c9e250ec4a24f62522dddc96fd835b18f7aaaa3100183d8a06edca3d9bb13dd280edb94e9fd3b4c3130c12078f221aaf99a0f0a081ab14e87dcb35e0961997e05135fb82d6f2c2fe140348da95e252639f7e1d8efa439510176f44324ccf59d64a39c78f0b21005402ee3ac1b0502912665538e4cdabdf9302ba6162133fb79df65c422437d0064f00796e189404d09d687946eb4b175bb87ea45bd952bf352372e91d50079ec6acb342758dd5aad4496ab11b8b492936506f5b9ef5d856b1497a8bdec396d6134b1fd349a2377d304cfc1120dd1eb560853496ee75feb4ebcb05cc54fa331d1e0c00b12d0d3a8a5a33c9e79ed2727ad59358c779e75f6c8fbdddb1a2107397fe7d4d68d3c9c99d117dd262a1deb6fd7e09a9a228de768be1800abbd3455b22ee856cd9392565bec08a7fa65d67a763d912bd7b38c8b0e9b23eb6b2a4d2beed3305fe029dd3f81ef0c9975846264cbfefddfd9399eee3ffb78d98f41896c5fb8036943969b017789aa33cc5ec0dcf7098a693468b4d572403eab50601c55540f2c834d57dacdc41813e538d3e8d8d1259d2ee98fd605b782740aab1d4f1c44c8adc260d09bf870ae04d50e9ed4cd433527d2c07bdbd3a04a3cae1a492acb767e178b0ac026ae88fb37275d9964e429a9bfa0e86ea9c33cc27632c10a288245f2cda988c35475c04f16cd7ad6968396109120f286eb6e3e8329c1ba03254782d679f1a255a74bb2199bc4ba480ec94f723e338a5aa06427332ebe6092c40220b17546d0d041f1a2c71efa8cee5b412af78e8fc484948a6513c037fa2827ee66c3bbf70aa869c02172828638acf52deb1c9dc432f06f32a75781e8f38a22b9c0eecb925a4b26f5eeeddb4d21d8ae6be7684b7e4e8487914c797fb639f879d578cb91a2860a8565e705209b6841da23a1932e3c9df221fb1441e10fc70c852d7da8c24a22238570c43c08c66e4c6e6f752b501888c76094ab83c82cf164a1c9930423b5ea3ab8fea16c2c1abf648c3f2e922cba67dfeb394096c4f96de262ca294c2b0baa1c180de6630c7e887268836a3eb7245dc58c029dbb80284fb2a82462b4e39e8b16a7ba9d370e91b5758034ab818da4acf783503f9dc01cbf61d7326601e25cf2e68a9a8d98bd4a1af400fcf3d70869f630a312fc5a0f10f1e83c49bf2bf14a72ba8142b3420857017b07ce3243e219ac64256acc6bc989840608db4b7baae85a93c07845ed7e8c6cd484a57da27cb2f421f42f9f04e70299ef3865947c3345ba4ec3dda370374659d6919dc6a0ccc7ec08daf58b6b2f200dc01cd21a9f46ba6118a14dff40f5188182ae2ce902defa845fc20e06d06e28e9869188caaa07e2c86b5f87a83bc3a824448005b07e358c9cfc361a86709be5a13e25cd2bd4992711c363b574bbed0939f980de5a584b135b0e2ba36a33de483982a2501642034b1de6d76bf5ece0f99c9931b37656866fa058ac8e38c351ebca5ae68ade244d5580f160f9fcc00fdd2468a854857378cf3c51a307f03053da813f732c2c5a7b0161b95c7d4a4e92b465e7e87d9c64facf724068fa161594e51abb945b480f498ead4fe08847f8de607d80691886bc8c357c773c17f0ccae9bc34f9f267312b4de020b843f4fcb0e81cec429d623f12914215b39c27218b006a47fa6346003bc6359011b923251c8ca478cfaa70d89f864e22526710d457c17b704556be2a4f599e13dc2caa571ffa4006f1b56875d5a40b028d3e7570cd174a163a15b36eb2ad57ca718714787ca2eb9477b4151bf23fa3adfbcefec9d1df47efe6e1286b1d7594d09fe7d3367420ac838e4883547a29da12edd7af1c2e6786450c2a9786439f4436a2a7482e7fae0c2d659f11b8c092abe78bd6d6148f57bdb63f6c164ccb3d9350883fa88b84108108220525b0304d9f24310b7635e59be0221683215097f940b0b6689a808a0d2355378fe534c26ac9fd7acf9807dc66ae05fd66d60330bfd8a0ca2ec3abbb63301bc89ea5e55cef0fbed7b61f5cdedf578b2b35375405532f4cc132e0927511bce9baf519e527fcdf4f6018e8dbfb0b8777de88817f29b0a1800534573981c844de1706bfbd02265ad88656f6412579894da351251c1266152a1c6874aade6fda516c036d53df166cb7821a6e09aced76520d7c3aacc309e665c94ba16780caaa4a78915e8f74d67bfa82379a7fcfe64787d7f553b623c3c5dcfa7e0a656c76eda0dfe3ded62f19b830c3dfd88f1fa0893711c1db92f3345ec6c1582375543a94be26ddec6ee9a895ea789a85026044fecf6ed0756d32296d290355aec97fb772c8e05b927f4c9b9c4e1371c00fa6cbcb92d0e0611ef7bdb2e5d895af9de6e9664d035c1259eb2af33ba656ddce58d5baca57c4c49e0bb57d63b904a7d25777d6e43a9337dc09200e058f76e32e6d8aeb095805fd9befd8a8b4b640887282c09a550285559982cd7ddb24733c956f13ee9ee0597ab2ff4123ba5e2b40c0a02d50fd9aa900162969950bf28756e489718d908d47af00f660afd19712df237d6788db0e78bb29cc0f3f2044bc25781a8536e076c4012beada05ec124e9af42a312a1b106b1e12fa7f817daf99d9cb0cd782124b8c4cfce1c0a4597016985b9470aedc2bbeb2b5ba92ca33d4081e42533efd7904e3fa4225d6483b4b3168b95c87b390b7652415119b12b54d735218c0071834965d8d0aa14132ce344411640c1694aa5f35a250d06dca606f1e38a26579996d2d2459d047dcd4d2912c94be5325007431be517a92d46b47bb573c23b3ce081eb5aefb8faee23a4b609d7b50f5afc64403f1e54892fc66d40a9c92c76099513521fb60c8b8bd434dc56fc6057dda0b384842d904ab9039d9b82b72edc1922cdde0f49b15f33e7a0c6f614ea064d6143c0e16070031b9db9568f146d55be675f291801083d52990d45ab7e49e65121414aaf3148edfdf193250d121c7a36c5221984b7042a338db8d1937eb73a6d79bb7a406becf34f2f9133e71531de7d8d4158c0def9bb41c4f32c83598e83740535a2de6bfa613942559dab3052dc303d6b01f1dd56996f946c917f18313e936cb167b4ad8c15f6a479c713d6726c318ac1fb86835b373de711d3e052a43c6dc48fc4047576164be55965bdc5ca07e07e8dd9ba4f7c09d55e6d803e97c7a2c297cab9b4ddb3993faea11b35e66c2dfd2a67d9e6c3aecb42825a374d9ec925c971c5931299c0f8324d519f2a7db30a2ec8294b4ab684f14cd399c63a5b7b333e7dec2c0f4fbda5d4b8e1cebf535a1841f27b05f68ed1afa97274ab85859b431841ef88172a787b3f3695494348d3e4d9aa9c8d95e3e62214977ef99fb114472fef40e8b825206d7553cb90b04ea6b044bd1ce42ba7f42f96d740e50ab664ec39fb8405c87142ffd401558d17b63252eff15ab68b3707b579f971c31b98e4c73f16cc99e21b8ca3da1abc8ca9b9b2858a2def295a8974fa598baf7e3ffa111af5f39f9d46888db008bb46cf9ec7b85fa1d1c058777dca7aa5be535bbf6e0e7c8b715a9e5894dae7ce40ae9aca2ebd8003681faf4de01bc652f9a50313376fbd02bbc81315a7e82304c960d39c275841882f5bc724b9cb084c786a705a9772c4dccae8aa19a7c5aa6154c1cb7a82b955841e5dbb0549e9aa16161b33d2be9587267ade345aa7539e8ba23d53ab6c4263413b52161edd6476a5e1f8e86100de2bc090e9a0088284f23f2d6d10a73ccf90d4c723c5e694af9cccd73d844f141bdb8b29c347f82b44eca642d18354ef760be430efc65c2313fd75ea656ed429d66198ee85a5048956dc6c9e08c807ada15ac7b114c0ddf24a859b50b91b5abb01a149201d3669d2e1428a260f99c7e9712f9c9ee4b0e1eb4ae29facfb967bd21e76b0618f813633ef44c0404b57953851444d6a729084c98518fe0f9b8a4bd8ff083648ac792ce9abde5030c4b27d3a1f22ba0186177c843157553d6369da316fb20592320f400a4abb5828037f0b858d14d70ec87fd10e9acf2b4619468c94eb7cae75352cc4bcc7a60f649e908fec31ca64ff4e16b5897ebca61209fe4e3b39b8cb56a576cd60c9365764dadcd4c9f87c5ace17f7a3e493d9f72fd9498785e959c2f0c179b83f29cc9b5f69aae832d840a6c036a0d9b5ab3523b6f6c26e08dad66a93a675b85eb5940e76a53fa9aae3245825fd2ca391fc4dac4ee6ec249ed77e7a8ecb073d0070ccd378d6b7531992582e8f66ffebf2e30d543b16a24b3e8ed2d9c86d4e8302cbba1d7ac79c10d81d3ce748efdd9fc68de951a00cad7a9eccbe125002bd3ff9c64c3369722745d3c96146d0c6efd37d97e34deeeadd6d68ee0fb6c2a810d58ee3f83a20094664d166349ccd24565c56d3666288c9131d6b9e1bed72b378ff3979ec83b57f6309851cb29e0c7acf1420e231ac387e7c20c2fea925d9e45345fe7c00d38f8a2c08b5ce24d14a1beaca2523d44d0d17293a6c8ec93348bce1cfba834bee3ef1d0b9d1549e7fab5a05aa6d59fa14a68259b53faf6c82d9bab4a5648a26a6a53be095b0eb3c6414f24c0bf0a62ba8880bf67fc37dc81c29f91abd24438de0559178563ea13ee0d69992ae7fedeec5590280d2c04f236b3ee40047417d63d1b436850bcec390915d87dd07008e22548aa353fbeec4730bae0969effec2d77a988c523e809336820e891f24d13dcd09a59750fc2ce3eb4898d1e59090272dc265ebbe6817615178f2b3ec69594e9c4de25f0bbe749a09961aeed4e049d3cabc6a1d3058a172d033811597fb1c0d8f9a67602b6a5838f0e604b60b1e36a5d37fe8f56c96c1d47b42f2afc3ac56c5c8efc5eb234da9a097b6429e30ac44c160b6c8ec9dc8888cd57a92c464d822e50c9818b2cf5e18afac24c66101ccd1e585f208d59c91ce44ed80c5c3b7ec5dc6a945f401bcf0fa2245d5cf477a2161c0dc56e3ffabaefe5a5c8deef03fdbeb36cbf2e2d5d30ee1483e82636eec94d2bf8ba06c0604a4e20c88990d96891df4f80720d8e381cfca79de8e34aead3fc39344f6d9c865d2ecd3a104ac0f906415c6cdcf588f81dec29e60c5ecae2508eba603733dacf4c3ba008eec25e0494844a5a6455ba99aa3e02e32224d62e8ea7c96f9da05f7c54e24263cdebba508dd163cc739bd86d0f2122f8f14903866e127c3f7b9839be7bd90a23b28615de6987f1a5c9926e10582c9ce1f0eb5a967750ccd8b0f264dc0e3805545d420d24c927ca3f7f00abae8e6f1571eea75eb09cb288debfc3b12d59eaa65262990ca4a98492b9cf7bf659cd9d892ee1ec314a0dac1d669e70e13ce8339824e9f95bcf6954aa41f620c8f59fad235f23c40b4c6efb499f25a10dd60a84cabe00e6f6517b72c66206983abc642ce3135845fe84ecb5f930fad10c91a5b82bad14e1205d80d514241431f468373b6516af35b69012e2faf84f31bc0a6d37940c2bcdb03b63dcd681e42148bb6448087c4ddb408b826ebd534c8b25b0110d1091b923bbbf811bdebf921305e4620da205057b32682a1354a22d49acd421ec37b5345edc689cc4ab35cf11c1e359cb4119dac631d07b8bae92f75446bcbb08fdc6ef779c356ffedbf2bab52f10011c717224d1391011d217a2f7cd2f0896be8020c47471ff4dc9a7486597eccc7aa4650838fb03911a0f3e0d151846ba5e2cd82a676fb20dbe5f03d7fa825d01263cd9970a102934885e2f73b8501d613939b89d515a8ad21a3f7693abac1a1f095d57e501872955f4408f4dc24145459ac08b714c4413d27d4982eaec3508475837b723645161ba786ba9697697d65212d0c7c64083e0fab52772cf582d005c4e56f8737edeae0963b8c0a2688a7f3d97a5d675c8587618fe9e2aa336d2a8df27dd4dde9be8544f0c1a699cbb7fece84af8f564ed60516412075457fbbeeb6f840a47b6ca5dca91af241f871fef4c57eff6b8d6dd88f59b583585655270ac26011aee76482def00f49272ab0d8b0c4430c1bcd5a27d9f4a34bd5636a152945fbff8e2d7cd8157e349c404d77dc30ca3b6a974c629723883e1712f2150e86acaaf790b2080ce14de9af3276c61990d0311abdf1153243770b8ea9592c69ae719514e43519b4dbe362463e9b01af21e04d8f5a6e897dad776a4babe510dfe66d275e1ed3d456e702b8add06aa55efcfdd1c4200896f195f6f6e768f192dbfa934bd76c31d629a2c8ef3a70998041014da29b2dd018ac2a512e3745cb3bcb4a75c535139caa28add76763a16878d937c13ecf397e76f27151cf6a5442e61da72ce3793b5e484e3b0d7729cbba9422c8f7d25b53330ee28df39c94a40173d1ab35cc35e35676b3a4322ce41ce64d71fcac4f57c42e991eab288294abb159640c4087427f26e9cc9539d12b1886a731d8430515ff910f93e9a1ddb74cade1a484f42e6225a6aed443f027f06a37237c1e1f07d51f5f567e56ab52fb8a9cea3ea5ceb61f4813edb06f0e3645c393e86c1d211a2356ec10f2e5e97c3224c37659379dd4416ca4f22ce6dd991047679695791fe9359c8aa240c6aff3ee2d3b2b81b88e10c21eade7416d0ea4fcbde42eb07e467a723f1d8269711873996ff812601d9f53820d55a0e51ca80d311d9a0a0936df2e65bb581721d7af3c804a066331d10add05538a4375ef7354b01fb8de7f3b98bed82f7c58fd3994739181e22c1e4613912c6136f56dfabc53957baaaa5cd308519b7e0a48a68cd2636076dd31be58a4b7f69f2b1c15d863f77b3269a36393adfdeb38ac00ef78465f954dc7b77b94966ecff8f639df435eecb800707bfb01c41d609fcbf3894540de39093e1a5467dc15465db1643d245cb4d199cf2940e771f4929c1c7b41691fd0895340168399de58f1545aca2b974cdec287786860ea3d28909e912b78a44a411ed0a5c735113097037a65b05f500203da2328fc63b016bf4ae75cbf36b01c37ae2a01711ae56a8704121d30557c173a24bdd46b5263d18c81f3e89e5723bbb0840e5ebe22630ddd741a83883fc2665f2be005a935a3091317e02f44291c0c71ac384ac4077780d847a666bf311c533bace117e96442aa34554169c9da05840f9a76b87718c788d296ffad4a1103041ac90c69d3ba86bad06fe90d9c72bc5c5175f4fa3e80d3b4019a24425eb3036ef4cf38f3225dc32e842ef1f13b0cbb4b0aa1e9b1369815272db7459cec8a3561fd012eccb01017205f9fb41a7de0be8c8343e7cd7dd5e44daa88005192d84451d2d15f0469f7d4c2846f56dd1048dba5b2c6c6ad744501bc9f5748136d241454bd162c908c597c242979ebee80dc92944e3701caa966f2f490ba240b1a227cb8d0cd2be1b3868c1763acf1b1e078f8e3c570e3005851db89738311df1367871d34b17ab25b029d4d492e10325bd5a21a7f44fbb11407036169ecc6898a9ada69c41213708b86ffdc0c36b4c87b83dcebaf5e52e2c7f91b270962403e7cfb17253123ae779ee092f9c8f3ec4c3aa1b6ff381c8399115c7a261629db79673412abc04b09db09ab32742cd2c3c7715b1930098098329eb6a96ed32102a9e8df40c1ccc6a29ac1abd38806cd630ac40d4b6fcc4a5d9845c87ed77d433ccb11cdf01f75cdeb0a68771235560e963b14bd19d8067f3169bbc7ce958aab154a13a8658540b827b6402e40fbfbd04b023895a59b887122a71836802a18a2296dec88815d0f014ed3b8a8ee7bf3fc5c30c08fff6fbd50922e255407e2bcdf4aa0022533aeea150a49dbd24899c1823620a274b35745e3a3475b44cd2967f3d7ad96c0e5eb41076b823de0fda4397933bc4809a394f7ff2b5d67b8d2ea1ccdd7c37c3ebf78e0e92f96e6c556362c9745eaa31cd14d155c82853f52570ebf8dc80f29145434f46cefc54494f099c49c4d0f689ceb781e6672e882ea61904eed345236a2d9599d43c5d0b5896b42c16d1ccb2148fdea8e07c025bde498db5e4c7cba50d242593b4e31436e0ddac99043477e38d68d32c1c9c7338b4cfbb5268de88036c70cd09ffa545cc08d018c77901f8b329f7a22d60fed3b27d5a139919a8b46a74ba0052d61e1b338b3676a22284bc90cb2f4c716064d3fd2a3ca6f1b769210e5daa55c333b70b8a966e8cc9077727d88c32b5e5880f879f341a93bd7d24f00e5ed0a14e9d611f7ed2179caa2fdeb69571674e03d861bd4239619b9344bd181c304383776af09a59219d16423a37c5c5db4b954ff0814042d37e1194506ce4d24b7b01a11018f825b80de24ae9e59c7789d1bf74d5fd395a947672518389258954f02c12d0ff4c1ae6c6e17f9590ae9847068ed25693fb074bf69bad935d7a3f75b5d363288189fd90efdf29201903d08bdde3e3825b499a975f3588250cd486ef4755d1c6b385ca143d9f2495eb71330dac2da28d845c88494e495898a058044c059b90a0dfa806185ba20d39ade68111f119a976fa0849f675eb44b747e7fd66cc13b68325a7c8ef77d2c908bba748506b3e3b20c84784f1a2870589f1167c902e90b8741fcbcd87ac47aa0afb0358c262a780940c4bf50b276932e73e70f5974b93d90aeaa04326d071d0ca30e6fb7135f8469163429744829cde6c52dda2b8bbd62d9b515f2332d28f029828bd1147e9eee5c22cfc57a62006d70fa47b3e04a68114e74ec7889bade0779a19ce4e791022b4ae32f419345adc89b54163502f6c1229af576507cbf2099b78d2c2e0881360e2a1b4f7a72ca9410d4c0d0287a16d7155ebf167389da3b57e8712db89763537f32826f4ddb6d032e70da4e51ea75ff300598008fdec47e8b81d3d840bb180457a8d5576b269899d79db89f638664ae1c233458d9322a3630200e9d263825ee75d51d5e2975955278de9da08d3537d426f4732a1c94cb5b16c9b0c3567fbea61dc78f31952125e012acd322ab4ebe9fa14964dd9e64febeb547092f0a2eba650d0c685bf04e5c62fea63377131a270cb4d2cb37d6b483cb80b37930f04cabff0128428474091865989487cb62fa043297bf6b6d0e11c86dedb559321b2b11c3230dd75d2b07acee41559e73983289939a7fe14037fa9a3453e95fd9e670f6a35ad61f0ff7e8444b42b6c8da30adf3f62818ba1b9fc2c2744ccd14b44a8db1da2749624480c7c07d66b7f605c587b134ec08947c2cbf2eb9e42b58525d18e662941b7e2e343b0d4622ea31b59a35be70605abf5365028bf6cc4ad7d8cc95522a023a7fb15c339c8bf7e080a46b812abb24462adb069d0e7a7f20fff522dac70693115c33e4bcc47b359fa281b310f3b55f1b283e3ffa17e921ab477f13c1add4665601224c7ff0dfa92f3eb154320c84cf6dd8abf351427c95807458ad3b4bd7d82da5e9e90b4edc8223467b79cca28b83fb7dccf827a9c90eb0cbc0aeaa4a1a96d7aedb20af4611c13278aeb651a8e4858c7e56e9d1775fe034a84001a58c2cddd63a34a7b855733cf1b08197fae2bfc22a201b4017df6d83275eb99c1b0b02c2e34d848b661f8a8f35715c38659b5aba58bd86d055f9ff9c37d97eaaa1bf8e1532abc912bac8adabdf34f05456e61f8571ae9366757cc544ab5441dac95b530d20b178f42355b1552a1358e30e7d3eadea9b06a26ce4b704325dd22c7f0875fa538d33927bb4d2094badfa7a9d78c3a49e4a8772aaaf7b83f1ad3bb09915df6b4db2e324d5f24124f8bf814cbfd148a1114d889d02cdfea89ff28dab85a7f0919d59c896241be4fb1e73c81a3649014123f174fdbf0687b4e386783ab39757265dbaf38aea7de1508a730414766d1b54929e3f82b029961d51600e9ee30631df86f96ab2261908ce2bef1dc51ceec5f09e20104dba31b1fb0c7af2476fc24a249e459cc51823513027db973ac953bf905ddd7b0a04a99140f52796f4124dbb101672819f7b0485d631e51ba0b7fe4f41cf91ce65b82636ebb39d814eb754893efd55b840196d47212a9e202f6eecd7bbc00f2447e863490647bcadccb16d70dc0bdaa570879d3b8452262cfc7a7455cd1f20c96eed62e65618e30ad051ac973c34e23e17ab20702aba59dab0b19b263f95a828c430e6be16d4efb84692171f1efbb695d3a4bebb42181c283ce0410e44bd0fc6967b0d53e4cb0558340d27c26efcad2ac95541d3f817ce9a78b62e9fdf129000ed331c37e358df7ce85bdd7de32ec86e5722de7fd3f13e32249acc17d79afd760cde4fe121082f2f12b1f5b64c7c50e3ef8e3122e22ea5d0169e886e6023e47c780fa2b3816e949ceebba5ba51617e92f1aad221b02c3888cb243d8adeab19edeb07226bff5a15fa642499bf85c05fd9b5e8a0f441063706545e3f87642b6efeea696736ef3367ea054774429bb24f2237dccd86c4709695532ca97dee7a45f8c3d7b54b8c2a89d6e45cb98d2ac8399510cff3498dd6ae72d66f0c74014a6daf7809e552b4fda7da727c77372682b01210da4c8e278e83ee545b5756c481d3aab87e625ad5543dfa13395777976327fc7d462cf20a94908d0497c4ec3afcd940eb24c615da9f1c288a220f20f784cd21029e9fe35618535a1e30c170541b1f62c34b67555e10a352b01a6ef7ae1420c0a979e0506b6ac9e2587e2304f77d1bff386d8d7c16a743291154767cb44a1585dc378a1e18b395501e2eb2c12c7049363eec2408bf317fd5f1590bc1355c714bfe66562db820d89d709c0bc0d7b965bdab4d2cafd4adad771869e1344032079bdcec1ccdce9a72adafc6f5ef32e5c0b7ad0ee28585ce3ec9da7596a27e95e4069fa669a385ea67a68f035884c9331c477059bcd143f068a32acbe63597447134cd5a95d97cf2d31cbb62b47f35ebd465a66f3d2cb04f0fe4260eec6a9c662ecb460dc86b5c32b568bbe2e05d489b97513e6ed4b611f7d1a9b45eefe80fdd8e3e10c15c18854d5119b9ad06c8ddb3695a1d9d2d83dd873c980894e90b57ea45292ad5501f5454d158f6c19fdc9a8c316ede23af0d1d5eba28aea43c605462a4392d7923bb7d5d35525acff07967ac5b661aeaf7d19d60fac545362dd7225524c33ef810e4be8e49beb8625b48eafa3dad14ce0c2c1b1f3d6afc6138fa07de67169a055ab94e1dfc3ad9e43bb3450369e981a144a1e86c9af330a67e328d0e45ca7555932a068cb6722ca1127d5f11728e19fe1c4006c19976c8a68e9067ea0c33cfa3bc568ca39db8dac9e7bcc648ddee9e80e9680503ca55e4cbce0d2e105aa43f0d5904903a8e4b98d9d9efef8783cfd63d613c44e61c2c10d2eb3bdadac898ef75901811c7ee8e2815c4b567eba5efa285b176698c49e50edb357c8dbdde05712773b9707385fdec9440030f9a42bcd556b9bf3fbde367ffd4f6ffb1f905db4e56a53e1ed0bd14c0321874f00a4524072004de38f1d2f62952f6c3e4a2820711a52af68302c26297d23faf7df7022c83bdf569ca8e6c94efa7ffe9da9dc8b616564041aaea42815b40f6a303b42a85da92eae710de03e25e60037fdf8119fa167eddc3c8990fa479cf58997a01e707fcb8acd5a31a2470afd44c8c4413099ebea74034b8f4524a6116bc93403cb323ae14e6041785493656196c1f25bd08d67a74592227de5f1eadbaff438fb38f0546a58e69e3901de2e3f13b2c12e53be8f8858113a2cae40cb50c8eef3df98aabc429130136a3cf2f1bdaa1851957bd199898fd09741151e00c2820f9928b7112b8c1dc395d76b78a91d8f42a0ad9ffc291b10a8fb1e28450922056cb10eb4c5e27fe0de5879883c7816ca59c232feedcd0501fcb349e3b23a47756fb4153d3c3daa2d882a8c45a32b0c4f6bcf0e1661af8d9fcdca1c84ebc232a3f97cb4a4a0055b6d2df0d181c39026aaf511619759e6c803e7aed6b8eeb593ab796b1fd0a8ed78ee8521a5c8d498fa9e038a5cfae23226eb690ed8ff2d2281541b4523361f04607e1ace934f7cd425c5f0ed559276390d7933810716493f6553c5a34c179d9c294201fe5d22055dac0bb4d4eaa7e475319aa13b12a581b860b7d2bab77985892729da53200cd5fff03006cea3a3af7db8f1dd26ad604f44905400f84721fa3438c4e377e0adc78132761f471ff83f4dcde072933357a6032ef2305f43db18ca40ea34618f51738a382d38e5c88b4dffa8379f75cb1fac4b644fb3b996a1f345150f911d182a20c6a514b988ada207bb69e93867e1652216baab23f5219525cff85197d33cece76f55f45a9aad407ab5589ee01c654aa3e00c21f6f2559ad32c2ce53e8575ed9da92876bbfd4f4e1b8f7fa31f17571a45b3bf187b1f106da34262fa06d7900f27031637557d7e8d4a9ffa0b07b8b012bef6023bc457ec29e7bffb03a94d0c3ea035089f22a6f19107354fbd0d484f5042cd462e865e3a09ab19b62e0016e2c1221c83c675652311b1d9038ddf29ff28cde51de2f8d2e416a96ca9f8861536ec5456f83854b2f15ce02b622f95bccb0c3021ded31529010be97a93d0ca23dcfe7dc2c25537b76f3ccefcc86cfe8c1a80dcfe695aa10d867010273ab3e8675bfa7d437899165c6f627be19b8beb69ae74f21adae163d853488507c4426f44a6521060d10c2f69aee0b6c16f67258d3eb3f1c2ef3088596d3bb3d5833f0c7a4e09cb3553e5fe4f09de40b05e1636d72b67230a6b3bdc1c320552c7a1f4b165e2639e0d4fcbbe6bffb5d58ab49cf0ae5006318ffe0a01a3262ee5caadf50ae62e3a42d41332f3a7729096f9459da8466fc1d7b8157c31bd089b1e84a20491483473af4197173e46056f1da8d60f11fbc9ff1bf8fcc31a610fae15f8973d03d2442a9f81d5ad64d75457b7833d4243f026a3aacd4fce548808c0658f54a68b56f8e9a29141d0405a6e7d8166bf0e1231bfe26b0f5808672484b5e8d2111efe00f89fb801e347bcd2099d20d46b4836535c081d5e00faa5fc1b3770b62c0730c30c33cc30c30c33a29919f7be1721246bee4da44c4969c7b596edb4d3ef764a524a999240693d1954ce89468ee4d8a18806a0019f01a30168375d4b6b195388de4e69ca53777afcd92f1341e0628abaff6bc97621a5aa9202975254dfe76e42d9f6a195490a5bbb0fd5efa35aaa758ec2a92bf1a974a5a2a0d695f059a263a7cd194a5a972c95613284c192050b4c8dcccb0b284cb9978f8c2be5274c29b63019534c4f96abc584ad29353be15fa93e9fbf26279bcb9da9324c8ded5a8ccb26287dbe53aa5b7b885aa626aad131946e25f7cc709de0928923bff597acb4a5afa52e982c86ff5855e2bd049b173e46e930bdf6a3c0c512c53cad2be4dc964a29b5381a2d2f2f15b85442892b62aeba73e8fed19bc4b96dabdbac9c2ae55e12e46648edab6d91786a469b50396aca932151bf8c9357225695703db21ef1faf6ccfa1263e54837dab4b870d1f342aa91b72ef755634bae9f26465ca27a4a29f412f63f5d04a5f7927d9d430eada78abcda96ea5a2f64970d1351eb5272083977db768e886b4fee8adba5e794b6439adb9696dda5fee5685d0c69ce5e6d3f628bf97b2ac4356eaf9e6ddbe7d5d08510f75a33db7e8c2e8330c594317c9bc92db7ba08b22b35d56b978e25bf0fc125905cdc542ab692556bd802faf09f9e285e4ab9decf1e3e64fda0604c2ead2ee2a5ac9944b872d308394fa0c5a86cadbfd4d8a172423569e6ba479e1e4b29e645066692d3c463eba1a6fa6f6a55352616b6f4ed2cd15a3bb59e6a262b623e4a34f2eee708bde4905a7492788a6b3b3da2f6d59e0709f56891f13fd7ec1bf11cf16f392564e59e7afb7c8ce0ed5ed99bfa35f49ea788d7d6f7a3fe7c69dd5a2292b79f3e2e2b6384ea0cf1305f39aeacc122d33632cd81022f2f6d1c21823840a0eebb84dcbecbcddb0fbcb29be93f571f4e0febd6a3f3c69c0756a74ef51bdab3435a8ea1a67ead362d6a1d9e6b6eda4e71be707260e9acb9d65edfc141e92ac7fa11b9754277835ac7122ee5cd8c9bd686d41efa956835e73e2d756a489816e1f2732bb56f7e68588fdbd2f6127247ddfccce0accd99a12a27034a64cb2f975373678be19d2274cc9a5aeafd0b437737853879526cfc795e7075c4d9a8a999c70584ab519fa96ecc35f6cc2da705dc964a977b4b21f6ce84c342b35dc4dee5a6b7a8a915547fb9a74a4ebd4b5d128e0a6f3dbd742c9be13b849934278544fbad0dbd9669f9699670500888a42bb23f7a6eab90fb9055ad794beaeccbbdcc8f210857aeb57cb93a57ca0bd1a569dfe32e3c843063a59463f6f8dd433b487a9ed2fb7f65b7e854104eacb63587abbbcf5920cf9e6e7b8e8800596e5fa9d55efaa7bbffc7db84bf7a5d55d3c42de6f8a11cffc36feafe82d3c726b35ab7de3d5d73e6e0f0c19c164be547dc6cea299c3d1e5f37132db5f4689b182ff54636270f4cd6e494eb6288f8a9110e1e6f2de62869729bde6276eeb0a38ee6c91a312355f4bf4ec71cdc1edf39ea46de7cc859a1711a9470e478cfa7fa9531fae6d44e1c2b39565dce95fb630d0e6cd9da69b364d74bbd377c21775fe833b5f7946ef06aeaa36ca58b5caed3061b6bec42ec595d73cc9b4cd5c0d4303194de63efa479d248d79c1af3c6d934d1a1a13261bee688dfed0d9d334038669471c838638811861e30bef0a23d57bd4af868a1c4ee621f1763daefa59a080e17c898af4d9adfd05276b6d0e264b190a94f5f2a25858c0f168ba915afb49aa91c6a57b4550b93b97c6ec1b1c23d7fac1691da53853b53c5aadf43878a44f6dc987b48995bc6678a67c9937233d4478aa6cf6bbb17030da4acc2b04a26a6020c28b0e58a4d8c15192c55d081b41c400bb84dcd0d0d01016c6955de2500804dcd4d070304e0acf48d152c7de500016a689cdf7c161a2d087c00003685ee9d2a778e7dad984d74f3f232e36860489079980ca8c8a0652606858139800a0c5bb4f80b36ae66c6cd1440c50595b1b91253d32da88ccd67996141656c4890f91b181804a8aca0323634313557606010a0a282015452786f396efc902e46ad65860a0a895f5b7deb1875a77f35a7f4f36689fd191536d6794c61cd1eb3de978ab9ce16f3f2b2458bcfb81a2c323657646e1a0c8dbb22a365268ba353ca3edec490e1727cc67f4871d5985d23b68c72193aa32026a76ca93551d0a1ee44b9eddf555ba1f05a9ee83dc51e35720914c76dcead39aee568a54f3a5d51ff4becfaac1e83e3095b9bedae30f9f5bb9b49a713e4c5b05573cf9dbd95312f2feda6e681c38932550df17adaad0cc1d90493f17bf4b4cfb5df044793865c3dccc7d50ed51d9f4cf43df4c997bdf6edf1635e5ef660d24bfb29e512bd941853129c4b1ecbc6fa9e127137af2595503289249140f28823ce0ba9cee6bada9ef21827427b701a79b8ae5fa3cbf5369b3b8c783fdda51a6acd385d3b8b30538b9962ff1cbb6fbbc15184a52a3fe794ef6e313c8928c7effcfd63557cbe4e819aa85121228410420811110992064907b1848410861032ca3154c4071240e02886c430084328086210846128062186106408318e10438891e68c1b0568dbe63fec3b345591660751ffd0cb56f3c31031ada541c1e54e424b92b4af1c60776732c20296489808140bf3eac5b2d039b03a957d29282ae0dc89889ea26101ab60e5db59c42fe97b1fe32e6e459a7d7ab7e967da2da762f68d3b5bb9fd61b05effd0be64d33abc26baa4be23a10350e5797228e080315f143a1f9fb1b0e7a5a5e51615ec277c97c52500e0a1e216be80167d685881f03a01fde39ce4e6f8c83628d0cbcd85d6b45bbe8898a2515dafe720a168b753c31c64d4beab923ae69554e722febabe8f7f0f7f93c6b030ac568ce830d678fa75132072e20fb98478e99ec8cfa74a30d2ad42c692eafb64981a293ebdfdd6efe10e46321b377a78f248810e2a5074b8608da9368441af6374bc3a8de7dce0326d86160bf4287268037ebfecd4826e5479faa0d633dc530619b89488bb62b85ef7b430249b39d3838d884c3b3cb3b3228371193cd43e1cfa06db38320e2998c15f503b3125547e38fae4884ce8db9f69fb24b9f3b19a8464f0f8458b30449e659f5ce544d44a151a154762026d4bd1527525c18349f805b105afa35ff993cb736a066e7f9e9d89839bdb89d992d7ab46ea656a6c7ede706b87e59cc97873844b3fbd56093364826d1faaa3825b64cffd5836776138e0ba9ff22151b7037162fa753feb99a8bcdf9471739b0f56db1e63cb5e5c285d86c75497ff5d491e1cce377c87bbf2b8b6f481ac9eecb4d12174618f906b741eec42f7757227ef08cd00817889529aa96290bc6bddc9cb1462f6731682e40565afbf6cfbff8529ed0a4e5675987132ed74a25d81869ac94e90219a6568574073e6385209e64b91f6018d5b39ff245abe917f336e944d620b8a260aa030231e89d1edc2e72d0ee0f60affcea8c5447460d9705609b93067c86129534a58c250421e09d84c5f107a9fd344f66b0ee5b6e95866e0e60704fab7e41eeb5734650492cd8a678203b77ce8e8face5807efb6a6489da3cc87804ba600163100933ffe9dbde4b02301838a349322cb300a079b646143f1a618f186f170200db9df5a5049f08b99928a978f0bf71e02a3eb8891a496517cd3b6878be5060c1830432a1138e287991248ac1ab7034a7ccbf6b5d7523001f5d1649b9f75e107984204a6f05a50b4a4c5650c817cc88817133f1eacc11a49de61c09084c18673631a0a92a15deaf2352455d930c9fb9c132a1acfb061608632a69cd9c350fe8ab32a6be956ea4ed87618ba47844116e02424e325b23f412e43355c56e6e87fcfd745f36752408f3a1cea16e8fb433f8bd6bc22ba08c8baeb85ec7699b080d882999521fad304c2eece0eba6744159300f9928315e23872885190c80e9653107a17bb1e1efc8d0ddf63f178cc3b34a967b3a61f864890159ab75e7124bc358e72603fc35b233b4d94188a4eb3b554334bffc638bdb4e34a75596f1c2103ef48bf7ec1680ccc2bd3420a9479e189017d9a4e27a5d6b2eac599d35a4ed93a0e93dfea054ebeb81d847f29c4868bbecfec752cbed85b2f0f7b6ceff12b1c6335739cf76a7834f5c148a0df3594dc7894ad0b68fbade8899c78fc86d52ddeaca43d815d85593ba13689342f448973bed68bb84c860a7f2ebac906fe85de8d0d", + "0x3a636f6465": "0x52bc537646db8e0528b52ffd00584402045e8e84760e521040ed361d686da611213512500714bb39cad5cf5641bd06bd0abd9638f539f67d2082d63fc6761fdaff8a1fa0ce30dde74873a8d1951f13478143baf1c7fc501ed2ffc5ee26424bdade7b6f29a59432c914d40f5b0d260deeca2b0c7fdbbaa337c730fdf8653b3fc62f4b4cffc7b7f86506e0f85b77f5661a70fdf8e55ec2f1e797db5d343c81010803387e6e77651670fc3c57c0f17fe2fc1ad70f4a8c13270e1c3c3f7e39052c7f835cfce4cfaf7177482a8ef48352f1bc19e278a1c03871e2c4c13986e3cbafdd05af7417fc176e116214ca295bf7a04ee0f5920d5fb075582fd9e004eee7dc070ee96f39ac978ce0e0fe1ddcdf3cfdb3b5013cf8fd9d6d5b3bac2186dfbf9dda612d1de6dfbfc14d72edb0c6d9e26ed40a6eb47157e3283fbe7fec88f86d885ac10d37ee6a0c3f7e2d2fec885877c5ef6e85c6b023e2ffe3d08cadb1fb43f7fbe331081db0c1871faa04de92d11a9c25f6b7f0adede8d3de933d0c3f7b5842d84284d161fc1f8731c618e1c708dddf62bf3a47fce53b3402df6ff68f0e219aad79f263ffad4ff0b636c1eb8fdf1d917ed8a940839191910db86fbb2b5becbf39d70e83efb07697ff8fc74fbacb1f8827bfe5b0c69ec3da5dfdfe2b87c5f76f390c62ffee8844f9cd10029612b318bec5f0ea1c812f1fbebc4410c7db1d117833c4f07ffcc79bc7afd0e3162116213c636bdc3cd9c3fe9b7558ced931e3c1cf1cf6cff1db5d3f3cf8f1fbbb43025f285eebae2c71ff8f6ff08b7806ba06cfc09936c8c14f62ae518802dd602815cf90ea1a8528d0084351009e81c3f0fd4d8d42146884a12800cf40317cff53a310051a61280ac0f0fdbb33228d8c30fc1fdf9ceb46017e8edfeeeab70ecb10c7efdfe267ddd55f002f7e7f7745a4bbfae3b5eeeafff1cd39f839e61a85287009cf40317cffae5188028d30140560f8feb043e21961f89cc31afb7767041a61f83ffee33ffee3fdb5bbfa9bc7af90875b8498439f636bdfe0b7c3723fbcedaefefe1884acad978cb8e0ae5bb3c2f0e0f7b7b6a3043c020773683b5576c4ec74d9c9610769e76847079d3874ded07942878d1c38726a908384ce520e169d2d397ae4c0d19922e74d0e0d726c90a32527881c1ce878d1b99223454e1c395ce4bc91932647889c31395ee41491a32687889c2372aac8a122078a9c2c3957e4a420874b4e1b3a5fe4dc20c78d9c2472b6e848a143458e113943e428a1d325870d1d2e74d4c84943670b1d2f7484d049810e179d2c72b4d0494207899c3572b2c809a303448e1a3968e434a1e3440e133a6374c4e860d1c9a2a3854e173a6de8ac9173860f2e7c64e123091f5a7c6cf131840f217c28f948f2b143eb8ed619ad33ad315a575a43b4886815d112c283382d1e5a46b4bab47a6821b572681db5acb4aab47c6829b56e68d9d09ad2326a2dd1e2d2628255048b4b6b8716102d245640b0ceb0cc6095c1228365863506ab042c3158655869b0d2b0ba608d6171c1da82a5054b0c8b0d6b05ac395872b0d6b0e260a9800507eb0d961bac14b0da60a96165c10ac3c28205867505cb0a161a161aac33582660b1c15a83a5068b0e9617d6142c28584eb0a2601dc1328285042b0b0b0b4b0b2b08d696d51eab1bace658ddb18a63058355192b33565aac985829b1c261556565a4aa4345878a8d6a052d2aaa34546da8d4a8d2a8e050a140a502951915192a32aa12a8965460a8c25029a97e50f1a0caa2baa20242d5838a08d5169515d50daa2a2b285438a46e908a418a8e1497541da932a92e526c52665263a44c90222355460a4e8a885411a92da9a55517a91e523ba4aaa47040d581a203c506b502d41ca814a0c64085818a831203f5065506c5060a0dea0c94095060a0be407981ea227584fa821a83e202b5054a0b5416282c50605057a0ac40516153d091e9baa0289042684dd0353a2f5039ac86a03fa47e48f950bf785ca4122b703c3820a2916dc1ed50814d0cd561ee21e730d9918dc9bcc8bae0c4c8b8f08ee40a3c2b340be5c1c8c94a648185c246e1ad31b36c65e60f560913189c0b4e649cde7049a73cb830ab32528e0e88037020fb20c2c7122a36542950c9a19a830271aa82334ad9b07a0206931ea637a624930ea61898dc98f030bd60278b1c2f2617703a7467a85ac0b540fb82b2e1a9c1e261cbc1760396145d182b30a6195d178a0487c56a4c0a063e7ee0ca9c84e07ce07ae0624071a049b49a50b5e1baa850d03d4c30e8c298eed870903385244382915b482da419e9854423d19067c832e419690269861c23976416b24c848104436221c39063c82ea418928be826de116310ed9062e417328c24234b10c988654433dc0df4065c95d416292d52625259a4c2a4b04881495d91b2225545ea4b8a8ad41429295251a4a04879493d917222d525d5448a89d412d40a45a247ad24e80e9b1b5b0ab63128961614ad28343ab4309a1cda15da1c9a149a155a15da190d0b6d4c7cc1c441eb08af8aea0eee87d40d3eb8acc2b06db82c03180165c6c68122c3aa20270ad50c58535253ac98ac053488eecad60595c36e5911416db09a816a881a05eb07153a2db638740eaac6b687d462dbe8beb06f745cd036ba2d681a140e548dc604aa8a75a31bb319d1899143d013d034e81b54087a443481c988728966c4335447aa1ce810f14c95629b82cb8252d9a8f0a66c52c835f4064e0b0f87f9c644639e99269867a4ee9868660db41fa21afbc589052735d68b531da736bc1a5829362eb30a8e8e4dcd1cc2844337c4e90a560e53ccd4428554a9a044d429e8163a03539b2e0b1947a605a724dde89672bad00943c708f946ca60e2a02ae35169c1a065470bcd0a07120ae9c55485e9cb76c336451a21a790544829e4171985b44256616ac2d4c5c484c909d312262fa627e411b4074e0c35826aa145705e5857621b561756132a213a21ac18272d34e944c7ca8b131b3487139b79c5a6c4f6e544c4690d5b666323a6a0dbc285c1ba617b43a5474b89235d0f2b1b7870565e72b0d88e905954656c557838886c7060e45c3155a15b54303965d831b50a8ecd14827b01ad62b2721a22e78ca6a55e31d558346c173a6298a6542c5462e488d15193842a715bd02bdc52ce163309eac3fc62a9c8c9e364850e13d58a1a4662c901c274838e1b394b262adc175b18394f14a1535ee8404e9b9a05e7c55687ce11dc981c27748858d58005848e12261b221aca854e191d2d1c9906705ca0d4acdef84022031aa0c04a8c150d567016b0d24388498aca8b84120880428312b141dc904b22f3182445c907923c5112044392241b8c2d34094a9244434d210129241005910c489c50e08582e2052552a404c5d04489035a682283930f3431128393282a9480f27281273e20c30a412fb4c024ca901e9413177854976e81484ad007923079824340a48293254e963891c0a39a5892c4890b4d8c0c0d395900d192a0a121270b18008a899632c4440951500b4982825c10006a8976e24213264942102548062849983cc1444d8262088112161ea544072d69e2a449122549983cc1414b9a3881005113199c2c59823f4025d1829325412d848042a2a58420482889142921083ad2c4091329528226f0a1b83408983cc1518284a238b900920f75442709922148860850a085a0175e50428101a08c6829424b82605852444b216a1214250886264c1eb5a5091317745044f41226529a30894244435092100c3d79a2244994a0168698c83044688993a1213ba8213a86244a8894b4a08428e801289410226032a484091193264c5828199604d110944205d1519c2c8043656929414c889844710203910c4a54282cddc40913222551646821099193212949983cc14aa2c880599c96165ae264e80349828894b4c06448c90ca68641431f5042d444061792102d098ac2a485264c9224f1018235aa6eccc06107eed8d64b464618c6a20f4ab7d02df009ac52e775a6ce9c3a73ce391f3620cae8ee31d2e88336a554c618a38fd8b4db2775eab46937a5317a8cde9446a7d4bda98cddd1146974d92e3dca0e423a21b410c23651e9d1caa6749e28b5d646774925758fdd1dad8d32524a29ede8dd4edb3bc6e9557ad562a4d57aa4399a4c596beaa47b4bf76a8a31336516b6a44d9d76b791e8923645511aa36d1929a5d4463982a49c779c515277b7564ab7ed544ada945211a2b74f2b9bdaa61d2195725249e18c905219a38c56f300d476f796b77b6c2aadec4c566a295b6b8dd1bad328b5f6485b4ed931ca6ea7b63dba5c817e965a08671115887c1f03e689523a29e559c090384f725a4abb1829ed6052dad46b94526a324a199b6e51f3683b464a1d77f4e15ab7f49316393a42bb7bd4dbfb27c7a9d3e850e5dd0e21ed1c053c37bb63c7d63414ca5a77872dbbe794d2d2ee283308a3f57992d1ba161b00b43d84107a7ab6cd7d76d65acd4ad9d15ae724c72500019ca7c0c52929a5ddddb6b5c8797bc71829a5d23d5a6a6d8c1da537d70ea5f40821f5d853ce9eddb337dad1bd63bb5c11721db5d8b1a91480bb006208d14485a01065cbd8b1a39c514a2935adbbdd5a6b6db4527ad7c9ae93d235cddd5a2badb4314a6b350f8075b74ebdbb4ed3bc45698c07a0b2a9bb76d2da6ab6a7b5d6cae8ae494d4a193d7a8c515208a1bb078992cae63877cedbbafb50d8cdba1bd2da5edbeb3640ccbadb3bc208238c1dd0e89436ede8edde1ddbdba97b778cd129a5d4673bed760a29841f50e800701f1edd23ddf16edfe1a2945abb6b10da181b15a375eb313a75db326aa713baa7d3e8de2d216ceb6dad49762d29adcb38dd6d942977f729a395537ae081071ea452ed31524a6137f47677299da7dd9b8bd1bddda3bbb70928947487314619a5bbc728829452caa64ddbbd3bbaecee3c46975d53f70622658cd15d528feed5e5a4d16943e83b4aec301155ed2188e22486a01868092d0445096a218a932430030009621285680401a87c749023b444c90786869c8cf0ca010104107cac2ad11225529a288942948488490c313871b1aea7a36a12f4e4891210ba84189810359121caf55c2092e18320189210356112e54992255298c4b07302ec031a9c80800804980180acba8420404a4802244848c9d00792bcd084090c5294c0f0260c31117afd1040011a089113272e12ba04254151584142498868086a4200d60b02b85d821422294c68081a81000d8406262e044579f25205bda0248a132229495a186202c39224444332287121a8859e4a14450991140100116100444aa20435097a218a14006c0da48520295282a23c89c1c9922451826670b224284a1225525a581294a404219801803c591224c52783610991920f2481410a93264c5c707d1002179844099a21c9014a30800180b41094c409d193104459224236143443d050121a94c410c2ce0b4e8692bcd084c90b2f3819f2016300a18382a2b890248a1322254b9c0c0d29d951110549f9199a0445af4b48d2c409132951826648124549d0d092243404b5d0844992264c94c0b0c405179c2421a221e8ced024288ed081e13158543ba0188cc158762a92b1c8813a5654818a8a6214a808c6602c56648162b0a8a828a635501715c58a62d1818a60aca8a8481645a0182c8ab92c2a8ac536a02e2aea585151512c36818a605151516c027551cc81baa8289601754c032a82310854048b8a1c28068b2050171559a02e2a8240311883b10ad44545b198042aea18058ac18e4da06ea0188cc52450c762b108d4b19803c5600c02750c021d9171c0ee86314851d201dd3e26d376a9e9cb161a451fb8147be49f3e07c9afecfa56f6b2cf16c325f941b89b5fdacd2e9ce15277f36f173a5cc2d98574fb6cfad3676f83eb764d9768629335f59f6ef63698be1b994c6f7a3732dddc5da2995bf2299d66a85ff85d507e05b2c1dbce3b2c7b8ffab0e8d775f4f14ebf456bad8d9f50d5ac8dd16a9a971df5dacd8eeba36e76ff3264f3f22f435cfd7afcfa6e791c66caa63fa50ffd297483f3f4a6344b7c14fdcbb00dce93422ffb496f866cf07c1a9f3e7c4a23fd1c25a6921ef1b7f0a3f1157d7a7bed01f542783dbebdd48b3ecffdc9fddde9c32328c49f5a36900dce4c5d11d4fb15f2b7dfa132ed39a7afa29fe228ea37fa92aa6eca1fa5754432bf82b7f835768f2e71fcdcadcfcffbb25f7dacc6dd473b9fab97d78b3eabfbdd73f7244dd07ef6b0bdb65e21ffeeed678b2de471987f47e7b0df3cc46dfe7215b1bd42fef4b3b7bf41fa995b9fd5dbee427ffb65a2881d5e9dfad9cfffe8f382485088fd99450c3f1345dcfd963dd57ebeec73573eebebeec94411dbdfee43fbda67d9cb8e4806d960ea5f63fb4d5ced4771fd1abbbbfddc25aef44224f863faeaebeef15ef25ca526c6325bbc62e4799577320f8696002f87d5e71c7efef1f3dfb22ccbe06f9fd0c966d9b6e93cccec15dafe64a18fb845cd6a5bbc029453d0ba3fcb13fe96fd26b9131cc6f270ac8619bf5f3372c1997efd84b208e1c7f88242e2d7a80546988fcff10a10fbf914af60fafedd6d6d7b2d78a3859f4fad335d26857c028559fc32f40bc469f6db6139f0b79fdf06dff428558c2f986559275ec980c01fd56a9fe2de9b11fe073a7787eb8ac417823abf7548381c1f083e77b3d52ebc1fd8bb1375b48e488d65f0b69db6ed94bde2ccf9baf7f1d5ff8911fac71ef9a66fdd6c511f7d7e628ffc8c75557f518fbd6bbad96676d37ecb367bfa4dbbf637e7809ce0b0f9f1e7cd27e099029e5e193c5fb6cecc394fd836cfb6ed6167e47481c01fd9cfdfbe6c6f9638bb3af627fa00c51ef96b96d62cad59c2f2814021d9c4db67a2894f6ffa7c02aedbf64227ed678784c3db039dde5a0f9f7e4d0b9f6e269a387bd3cd2760fa6bb0fceecb8040215bf6da05027fc88e488d5730ccb22c0362845b84580ef4e00a0887b3589a81b6bdfe56ff0bb78fef0f92c20f031ca6bdd7bf6dfefef0a38ffa6a071eb452315603c7bdbcc2c934f1d4715cfcece5954cf591eb4edd47d469d59d7e25cde0985d78e6c733b7f09c34cc79b92f437ccaf1f3898bb7fefcb287278dd5101f4e1a1fca3442dc17e1383378730c4a2579e50cce1466dca3eae9b33f84374b3398bef60f4f97c871063ffb25723cb30b43086dcec323f1b9f9c12f6b97c871a461eb56acef8679a7a8ba5cbc3aa7af4f55dfc1cdb683f7e066cf836fb95a9fbaf9e7c79b8d3cec90cc8f2f951c561fbebc0285c487dcffc41f8f5f63ee8b3925cde0faeef0e3158acfcd796516f8a3b2be75f3afde839b5bdd7770b757ddf8f42bac39755533f51d9c2fbf8573c399dfdd199ffe165f05e3a7383878cb263e7d8d293ec518638c27eeca2c515ec1f0695764be3ffde2ffc49ccf07fce8fb973dea3bbdf7997efb3c5ea9e4ca767ef6eae7567e1b7d7c48ff0a0eb312f0213ede157d58e01ebedffcad7a736bdeece1ecc2d18525b45f61e5be7e96b8fee69ccc42adf5bc562b3fb6f1b30bdbf876e885b3cf43af4c1f3ed70de332987fe5aecc52bf2cb59c9e3e0bd18786d823ffbfd5f2bc4683e5cb2ca76fc78c37f4c2dce9e96b9fc39dbefb2cb560ed7d7c1d7b20f74227ee69883e36f6c077fdb75a32de3cf4824658bee9eb7675374b2d98becc028570328b5492666416c885e539344883399cfa31406cc106c0bdf4031bbc350a055e2ffd0006bb702ffd9084b746d1c0eba51fcae00dc3dfda044f622e094b2e58fe068d3cdc6ffb570ef3fef4fdf08ac3baef875a1ca67d46bfb72dfbbaefbe7e343e0dd1c78d624ffcd46f11b56d99bb7bf609d9dedcb3acfa767fbc3b24dcc3ff710e090af1af799a9ebbd9b17dd3cd8eb3fff1f9652e09d739e7cdef5f86d8fae739ef9c7e85b2b73f7b8b5c774522cef36a0fbfec6148633514e1f9f5c29a498031fc9f08bfcc69c199bef609f917e1f9e588e17b6724e2f85b0cd7875f8659e2a9c1d76ebb6b664e096722ef88d40b256221ef9064c7be51cd1dc3aff5662e09d3b7f5a5c34c5fbf7eaeb542a8c4d56f87993ec3244ceb7cd7be0c6b4e8cee4343ec890fb0f5b39fd9fbcd12d718df8da24f3c5a7dca8d707c00fc79b311f8f3e7c30e09fcf9ed30edeb734750c8fceaff13b17f3b663c22c7331339ae4f3f734958fbeecb1077ef7545523fafd07cff53eacb10432dc6f851f5ab38592b53bda9ef6aacf5b2a296fab1de95eac6f9296e0cdeb2aff19c59f6cddf20373f893324134f6ff8237347b83eed8ac0ef9e7ef37fa2e93b3df779bf7dda43faddd7cddbeea2f1934f43f48947b0e1b41ce6b81f6e71d82ce2f8243f1e459fa825f6c897f01e61f9f0511fb4fed98fb0dfaeb3dd670e6fce7148504886181ae1f89366f3e16b0fc5f086389c5da1f9f5b5cf7e83f001f5b59b21fcb2cd3bfff9ddc3ce08fcf9dd97258657c7bef634bb12961fb5449f8e3d52462df52951c5f03351c5dd9b3ec323ac0d71187e1ee270f7dc6789b5b75d11f842ddcd10d7dfbe7648380c1b0ece16779fa31358bef7b5bbba3f7dd25dddcd4415d737dd0c8f30fdee5e76370f71b83e77b3c4f239242804de2c2586eb6faf7d6b4f3b221a9784b36f6e90ab1fc5f66b3cbf1c02e86eca23c0b37ef3bb3332f1fc1faf9fc4f683b83b231bae9743823fe8955c708b104b410fa8b830e59c3226441336c84116187ce14517a91ddc7c3cf840e50331268eac30429aa202373940edf0c60e2ea8f28115eea51eaaf080871e7304c92865c442ca6f2b65b6584af90696522a91a93222183c70b1c399243982bc18bf79c002c797ee038da2121ea0c01de05ee2010b99d7ab056b5f3c3c4ba4e47901f100bd8078fa045f90b1c38b249c80c10d1e0f49a634ca9cac58b166e8f04687a40dc829e594f2bd242a584a292fe0d91ddc4b4954608a7b29e9090f236988d70b03d6be3c661270dc6fa38f1425e850420b1bb8a0c2e814010bbc2c20010ea98c0e521c539034194725b0f2e50b0ad0e061426ac204c6e25ed2814a0e6b8ee048c0091c20cc88630b2b2fc8a343c29221197184a4288d3bc8a0e00d366cacc1e5e006090725241dee25241a20b1e0dfc5c2f3b8dec5c2bb3ccff53c2c3ccfbbdee5792d345e0420000108cc53f3e45ec255b6fb78b1a77e5b2c3fcfe95fe7afdc55e7ebd53df5e7cdf43d7755fad93b72c242b4e5307aeb77fb74f7648bbb7dbe7beac39be9b7dc551f56577d04781eaebfc1eec809cf5f790e830fe9d7d83b07c4f08c3e7048addfddbcf9b15febaefa3f2e3f7e7e6133ee6a95175e7ebd1c96ff84faf583386c7efd141c96e9cfafdf11b5821b62ea374fbd42bd14f348a38339e79c55bc60557982e5b599585e2def795c047877c58fa70a96957cd7f3fcac62c5245f6e31684591f42f3f97fcfc3cf27ffce5c7f75eaa91f3ffcfe3c10970fe17dd02057133c18c22e968290f7ad4c4105176f4f1e6d10eeeb562c32b8f6c48c29e4603530ba64b38681399f385f3bcde2afa0c79c38836b81421851d446c5d661449f10ebdf07c6f7ead55fdf81d7deaf5620f7cf9d96288a588347888820b135ec46878be37049ebf7258c513093cab0c9973ca2bc46b9c3d6c87c0ef4bb5383c363dbe81656e7e26c0520a5d06e27e09bfdf7582fbd821fd5ba3caf07a890a1c8cc2bd44a507dc9dff8f33c0fbf8f0771c9679708e778807c3975c2f5929018e2f8518127fe53e7248fc6f14208e31c6f8e34d32f0bde82387c087ef9fe72ef8f35b790ffcd51a6fc3bd64250c66e15eb282046e1e580463d25d0e2b3fbc5e09b0f6f5fd5bce8e18f16bc28385e273916b87651b7bfc3bd7b7eff4e5e7211e6c3ffbcd7ed65dfed9a700af7e63fff93f9e7d1277f49143fcadfd1ac30ec90c10c35b6f131e4caf90fcc961f8dda9206fbb2b5e0b1d696bf781464646464643f20b77903683a34f4c0b2b92f8d2069c3cf2886248bfe7b00c77bfebc77de490fecec0fcad3b220ff03cdc4b555a80fde1f75b1ff9396ef1b3dfdd918a85e24b77cdff71ebb078b3c4f3bb61f1b375d7fc76d7fc15164fe2ee76453864ce6f9e79853cd845ea8d191ca0c00b1a274e60c560e1b2c9cf96de0c71bb8b3ebdd962e83d8d42e3fcb0fa99cb10cfcf1ea6dfeed343720bd3a7f6e9af1c2631bd798807d3dfe867dd053f058f3efcee8ad06bed573fe9ae580f539091c70f698800c28621f4adc32aa6efe17084e9efb84f2f191919fd30843e8fc31ad377459f1e426f1e7ae179adbbe4b7bbe40bf1a28f1d22e5378fbc42de848e044f8c3146f83142777f19e14081473a36b223060f363c28cc97432f6ce74714b111961735a0c0f0e537f49218be9410ba0163fc2d7eb03bf83f3ee7c78f9b73f1abfeb083ef5d740867b0f54471ee6a77e51d0cff847b69ca1adc7217fcde998206678bdddfdd5d0c767777ffe6f127feef3097c3b26c392c3ecf94231c9f739f382412c071dec1f1e34b87f1c42bf48ae1856ac3eba51bb6e05eb2e10dce2eec6f02ee251b96707744bc1ff73cce61fd3e1ff76ff36b1c3f88b736c18398466e7ef4614724d2efce01314c37d819994f6f9e3f7ff3ce8887e77727dd257f935cfc20de2247bff9b0ebff1321073e0cb82b7efd8030c001208755e830eee313d9a7f3e61807821c448d0c1e0c9282c39e012b38c061dbc767c161d6088733c499c21fd6edc0f07d30830187e5200ecbad26c8128e9fc20a0e8bcf0187a9707c181cd64b3e2c81e30fb90f1cd241f00ae76e43e10f7a279289a1c438715ad12748cbd4ea5a78c26b7f0bf25a902c08aed7ba0b06c15109b708b10e6e74d4c0aab5d6974a9a34335f43623ffa52495e914a328b34c37aed63492579258b34e3306aa954aa55667987754f9fc761dcd377390c3e7d02386c7b6a29bd7966a9850599e564aa52693e0bd107ae549b177d42f82f38c2cfce37a49a98c6d1400cef194ce195664cd943d4c3cfa6bf0fff666ffbecafddfbcdcff66f7b081e7c6e7de0e3f38370db7b74be7f27a7d501f71d7fe7eadceccab93e6efed6cd2d0c3bb8d607c2b86ed7bcdbc36741de6c3dafa5baf9fb66577cae9f0ebd3097bdf59b539dbd465f62ee0a791c7debbd455193a705db7985bce73e7bef8e70d9f6de15a2d44289bb1f77c52e8363e672170182e0f9b076b08310baa028800e05c15485f9dad3af49102c34b5df36c881f085f0f6044c5f96d26bbbce7357d7754f3f280af0becb07bf4dce469f0f24e04342b836f6d0fff10fbe8e3d947eccb22c9b9c8d3e1ff890bb82bbbe2e213c343a456af4eea2df7d2d77d1e7be1d77d1df3e1e77d1ef2e02be85f081f0d9d8639f7e2b382cbe772a686fef0a12884340f81f0fe1fbe0e99739c05df141f8bcd813ff83cfc69ef83f31fb629bc1320bfc426339dea2c206890d92890d365d93e908898d0989cd11121ba40e29226994523894bea1d40da57650da86d20bd9983e335def3bcd27f579fb93473def7aba9b25ee6ee670173d1ae5f7b763c6d32ed77ffa22d6b297f54f57fb367dffe93351c4a63fdd0c93ba2b647a4dab37c74c57c87690fbee886cf6e9cf8feebc0bcf393f0fadb07d6add45b9bcc2747e34666fbf262b2c443ffb2d72da977daddeb65def4b7df7d5efe2fd8fcb4e02aa47d9d4c7794f4d7d5ef614f5b5f7d8f7aaf6f5b3eed25658e3b096bda679dfd2723bee82de0357dec3860d1b36486c903af7c2d8627372915ba216bf02313c921726b96bd2979ffdfa98dede6c3ffb97fcd8f56b6ffada7bb4cb7dfd1c87b52bedddb2faf26d36f1f4ee88bd102976856a9d56be8431c62ab9d9c273c6fa4939bfcc6159ffc7a1c4da84c342f2cecbb92bdad8acf4abfcccf4bd624fa417c92bfafc98a431f59b471529c961f5e71c03916a4dea3a1bcb8749f09b5f4bdc52fa4cee157d4c12ca3bef676278f1636cd580e7cbeffe44f92521c11ff2e5cf9f38e5e62636e8d7b806faf4c620a4104ed4dbf99e87ba48a018d8c67e504cec88d0d3a3be130c632d14032d180ac5388cc236d852086118995fd8e61360180ac36c5b63cf61b47e997dd5bfe67c3bcf4833b18c2f416cef184cc1d80bc578eebebecdfef6662826f5306f8fead7defbe893fa08dfde1cc447f5f0e617b6a7b75ee6bdfd8e3eaadbde937dbcd9950f87388c92d7f45aee2e916bf025465da1eda27e7bd3cd2fee127976ed663931457d7def8ea0ea29fbed0a41786198ccdad969763e7cfb410bad9d75da0967855c772ad0d77e8b5cea533dfd4cbf6d4fbfed539feaeb977549fd8f6f6f9f7e76d5a2147eeeaaeea21f1128104a7739fa21a9fff1d815b16ffad811c9ae74177dabfaacf7d0dfbeec619a7d9ebbe2abbeef8a684fffc74d5fbbcb7bfec737fb6d9f7dd25ddbd32f7b78fbed61a782f6dbf6f0b35ff6a0404ce947c46ba97d5ceba3cf8ee7307ba19830274f7d71a536f8712ff5b0056fbd39d79d4a057da854aaff71d81151a1fe0585b01ec5faf940a010d645b235273b22ac9b6398f5a8cfda47a1ac45bdfdd811b13f3176447c3cebf371730cab50a8871d11d4d60e53b5fd06f2f18ac32c9189fa545f8658f571ab9c4dd914b03d614a200f3a68a988cc8f3e3f1e74d09a1f6d837de05e0ae20cde22379d904b442ebe05e279b5603aff0585349e375e7117d53c78f8f33bf8f959fb393b1520fee07774726e46bdeadb61accfde77ff137d3eb83b3af7b63a039ab775739f69f54d01cf6bedb3be76576645cba57e655ff5ed98f15097686255eae613ba6fc78ce75d22c7aacf1ed82242f625565d21d45565a8cf3ab8dd67ed7693bb42a8573d7cef8ea820eb1239f7a82b647fe2d82199d87240e08f39bfba9dbe3b22a6ff71f8b02b5221ee4cdbbe7eed2efa75cf15e95e65b9875fdd320fa60f147da4f7d0e7ec0ad31def54e88a38e63e9e3ed3273fce6b790ffc17862e0ce3d780e9cfeffec49ae31520f0c7fcf9f427d28f612a6f9651c63c705516230c7dc0bd84c50eb9d5dc38fb7eafebbaae7a9f50a66fbf88769de7656fbbd597d1c97511f52bcf5b79deea7fe2aa7bd427645a9d3cef572bd4ea511dcaf3fec74fa837997ee5a1565d664955eac4aa1eea4d2bd47b52255312e5dd93d4bebea9e33eae3bb261ed66fad9ebf8db4be10f3ae1d73182188347c0bd148412de4e6efa84727df945b3a2503f6737bf43559b259e0f8094c21fda6bd1f4289486d2501a4a43fd4f44696ffa843614874299506fd24cddff38f7e5222c64faedbb43b23dea7fbc7b22c726a811cd6e0861caf41beaba77b3c57ebad9c36ebada7337fef651f843cbeccd1805eb94b8c5d8555921ee6a841271ed4e8518b6bfc9fa650fc76a80dfd50e7e57ff27d6f8da2734bf31acddcd31dc41a8d5d7dc8eee4289183e4076dfdf1d962b1808eea52b6bb0bcd16139836589aed3b8d73e772fbb2242f566b8755d966559d775dd675dd7ddccd1ace5d92c3697fdc5dfe0b7755704f6973de76c840d31fd84a0d480ed37eece48847842fa9d25fe0c6fc75f834dc0bd04441457d05c71026fb1ab7f450b1600ee2520dce0cd39da11a90f3bce5dfdf55bb9ab6bb6a6cf2d5c6fe63efbf67bbaa6dbb1a77ef431bd9fbed69b1f57efb78a7afba9afbbc7deec61fbdda3beee1eefebee397db6a7bbd9e2daeecabe1d331e778926ae377bd915c93121ed7e16af10fcfab45e0a7f1cd95e5e0a7fc4db33c63915c304c74098b4ede4faed94d2f9442ee41ca59372574876fd244cb2493246da5466f4b32dbcb254d636c56a707f78e45a8438b841682d54b26e9f3bf926adf45e9e3cd3cdd6badbe73ecb23dc854856722f4dca9792bac3a45a4dfe9ce9244f9be9b57882485996c13178b39cb596f30b91e4f665ff1337c941a41d99c98afa187d50f73b2985e4dbaf553e8448f047f71277dc8ddc6f96abdc476e8b34494a29a5ec25937b79442d9ab7d9b8ef7f5908219c270d426b2794fedc2731fd1a3b8542e84f2badf53c28fff4fec18ffb38dc1e3d7a845a849ff6fe69dfde495e2e56837c6b5fcab7fe86936eca124bee2514e2cfdd9f28bfec7670d67485b8afaa77fb9093ab97aad5cdee6dace79d6eb7bcdc5779e9fd710981a2cf4f6acba47c29e54329edcfd7baae087c7985e49f9e8314fea059447912f5de6bd0bd9b89a685f2bdac2342b5e04d72527e12739f94107b6764c32769bb22fedacf4f3efce69b1cfe2639fa82f4a1b556fb1ac3ee6dd4628cdfbddcb49b3d7bb395b25e4be18feea54e7dfa337e6bbbb99569375b4c35ee37ed85b84ce33e7291fbc8dd4c346d7c0a8568d9a5f007ed88506f83a194524a49e18f29a1d12a0bd7123b7e74bd4e089282c3b6d73ed3779f7dff4c0fbfba52a1221116583256830646934bc84f88d36e8e61eda1a42fe1cb2d52cbc240215a279f4b653725dd2f97297577d7ae907cee35e95ff670ac06ea95ca2570ae4fe442da1761eaefd225ae607e22fdb2dc827395da77557afd970f6f8d13c609e384127e866f1d963d8410d2d7b4f74f5aeb79dddc8561fc93afd1abc3bdfdba42dddcf2a7371bf1a74f1f7648be38cc3e7c08060aa10f35fa35d6e86769dff4795d11d4d32b445ffb13caaaeeeaa62efdfad6614e596bea8a78e773355533753314e3f42dd711d9363929e6bec692d24f62f951ca691482f137fdc4eee3deeb9b3eedb3eff4f16399e8855fdc55e7d3cfd673ab615e0f61991eb65a4e27ec60f6d05a6bbb2b44df64c34021193a4cbef71d7de27b57e28d7ed5beb6b7b75f21faf6a17d8d6e374331d65a4b4d500c8657277beee7b75af6eb739f94d8f4f5b7ee331483b7677da7cf12496cbfbe355dee2b50b4b6bb198ac1f5c370bf3dfcb23ded886c520bd668967d8d29a5946661e08f4aa191e73ebec009679a712ead77bd3ef81c64e7732fe97c6e33dbf773def6f1dbbe3f04104c1fecbc4ee6eede69ee59063dfbce2914721f7eb18b9a77af4d3370c9dbb81b87739af76e9ffde7fd79b3ef7e7e16e1b2e0b01c29a59c69aecb79c6effd3e4f33140af138cde0094d6d6ad3c39bebc4596c45230f8ebb8137d7a7f0faad7049e3fef4ddffd976089fbd06a1bbd4efcd1d4ac5eac08356101fd27d9a996670fd6dbb14fea8ddca5eb7087fe2785e9b4fe30300de1c2fd1c4fe9c6b3f5eea01b07a378af23bf2dcd38d37fb14e1423ae1e02debbaaf719665ddbd546e1f7cfbe5df3e04106e6efd0737bbf0cecd2f9d9b8360f93fd1071a512824cbf1717390eee620ad9b5ff6e6970737bb5c1ddcfcac9b5b4baa9bbaa89b699fedfbe7ee3d33994c266d7bd3677b290bdec6dd389c68947d363dcc2ed1c410c2cd5a9b3d6cf2cf2d6cfa954e4a29a5b4034c29a5d487971ae4c0e535a9a559ed3b6ee03d4d25e9ca968a63b044add9eb0439b881892a698ca0038c375878f060adb5d622808b1a3dbc80d3822890c8c4d1c8b8c921084de332441859b880066c30a1c3901a8328c8c4608b2462d0438040d364b600c3e5861af490858a0222183fe0e1c31ad2891e7a4a40e72b3ecf0968b821e68c13fc70050509f8a2bec14229a519d5ae54e967e228cbac0cf132adf1668d1bb041858da4f93972dcb0871352bc71811804241db5154ddb810a6e06aa2972072b1ee89004a5948201e33d3da64cf603e6cc534a2911714ace0c5455ec10565c76361a2323231c68adb5b21185f7349831652866e30ca5aee74169395da932e79cda9c9c1a3d4c9a161303e6cc164188d9c386255250d26176c1a2696dc8a1d3c60d7cf857c541a5c3136ac82c9b42060730e894aa06d73a258fc65f3891c54887232fd258c11c6608f9e28b46e68b2d785803255f3e240b1710c018820b0906103b24552cd424d912e07c3105ba400ccf0bd4682f98230710d2c820a90c2058000334be40218b35ee48434d1a6bd26843421a38b22519fc7065eeb186524a7b50e31b8f399096b43053850c3d0e30459824b8181919191929b911ee2526b41809a169796c44685a1a49043563ba000e8a8c1d3e5ca9b29179810d3c326d4ed08351abb502ba83d7f2569ea7431a9e0956def53cff409ec0527778d55a2b162aded3491363b9d28a4604a005061456ec0e5d6489811e53bc50c38a206f661b4869a295278e40010f44989182295990f1a9c28c172871f4721fb223dc4b561c1de10078d1c2488a3b54f0e608d811774587773dcf3f5784694a00e210038b25ae00d3c50b0c40a908f1aee7f9cf6323e25dcff31a921bba54f1018d227ab0e204306a88f857754197c7a6e466ac31a38d196e2a1044fcf0464a1f3b44ce39271aef69356bdab89143b4c091d2ab9862584cd9d28551162c4a5698b0451c7b58c9010ece70a20d901d58256841144234b1460a3a4a78c20c0d0b28a67c41d891c41674f84046122fa57f5122bc373232baa1d65ac15862821185d7840c5cd0050923f4b0e28a1f612c6185a70ea5a3b8078551888666709329e20c9c2328618238766061b4e6a800532c4d244dcbb952c5948419f7a1b71a234d1c2c50230c376c9004095ed8c1439691f1c20654bc8f4bb3d25a6badb5d65a6b9a3770ad72881df08064d070d380339618038e334865d610204d13199934544cc121b1c7111b61dac44187990178c1e20397d6cbc8c8680aa594aa31f29e3ecab01aa52c6b41a1b2061c3adc28c388140ca12360ba83894c1b53b09851323232ca41f6120ec698c1b407d31c2861fa84153746a0f4bd36984ef1022a5e2a9d34c394524ac56893650ec6107270c1c20734643084ba302553c69a24cc9833e032adec68d0464586067b1c35a00c30ce88e9f2c51e360800ce948c0c9ca42a31ce7681317030838e2ccc40410b02200796d7ec3546464655e69c6f5773ce39a938e33d9dc6bee1b169d9e13d950a17aef525b930726c61ec4002001664ec5218347e30c349102c28038e2fb808420b1e7aba0308315baad840658b0857a49713000146075382e88114270c1a7508a1c7f582b481f144172449040f1e8cbcc04153c798af0e248e8e785b6bad6f784fb3b176783f6fbcf19ec6b9f553c8518498247a48620c23fe8852b9591902b543ec072b6cac00872aa840410f21d02094f4b0628e90bcc1e20436bc80871a80010219206440660a31901acd192536be443144970fba8854689054e9a4b0f5d1470e518a3a4b584ca1e5c00453b63459d491650c1c1510b143c5b261e268027770e1522689277c8043c78b1dc24a1e8d066f27c8c5c8c80889524a698606f7d21d4798d24683b57861250f2bb2d481c699213998be970653374b573c7f63d65aebac3f3f9e338eae08b325a90d1940f800c3892e910d0cacd450868c25c250e30641f4d0029346aee0cd0eef7a9e2cbbaf471a2d6364a0061e6178b0042607948d0d3afcffeb4ddf600dca4d1694cd952a40c6c8228b29b05843055c7480038e0c0e7a30b2b8d65a6badbfe2813a78f52b66b3147b1acf69f18f679ffd56ee6a373150c1094ce0461a3e28c172c2b6f9e283a4523ed5ae47a9ac2b5cc9b031836b65411a5c6bad370fbd3c77c9cf84f01a7b2d6dd8fab34387fed0ef02e4cb2c7f932da55c05e1b57b77f37818dbe9d309a149e88fe1c6dd81f0c18e4e8e8f96071db056aa14ca3b993a6ed3325be994d19b84feee6093d025fe8f5bdbf176f714c198bbb3b935f87eefeeee7edc5d3ffef11883900431c6fe7e7873fffcd8b37ff60bcd18638cd25dfd664a895ae10d35ee6a2c5b4a29a5942fe99c7442d966c96f5e618932c6e81f1fd64133a594da34fafe36e1cff717729392e79f39188cdfb0f9197ebb4bc2772c045fba4bb60ab42f22703395b0fc8d76f140c0dd2464f9652a61f78caa02cb975fa612119e7c69e340c92f384f2e21e07933eecde422abc07972511a83055394b0d4be94406a1b673d544ab5629d4c5d16c4cbda95ea85f2ec13ef64eab84d6b363f99dbe7b1945e0955b02601ddc8b623f71b3cfb06787e0bfd46a7e933b9cba44ec09300789aba27904a10af2de562ddfce26e0e82bd27dc77803a996e77eb85548a6e80eb0342c0a8875f5837077961faab9b5ff4555ffabbd9a06e8e6df1be6fd7fcd919e925cc3d548a3ef0cbe99a6e7779f0e46ee6c0972f5f80707d592195bae6063d402a2b98c1218509d0582dab9c4b0bf7c280c5f55fafd76b02fdb42e3499cb34e89c70287e99d4271e148afaa5a20064848e9b33dfedf307a3f6734421fbf9b0853800fee8323bbca2cf130740213009fe88ef799e6d712fec38f719ac3dfd64bbe63f893e16264121dd3fecbf1b376e50e826213bce30095a0b95a28f92126e30625e3cd64cdbfd9a84f957bbd903583efcf04c82db3502dc1999b0dd94dd6e12a6a55de664250c396497c13266821605734e5a6bad75428974cee974cd6c69ddc19873c239e79c904e7a051cd3b3cc68835ab39f6f1b05faf3b346c1723c50a9f21a62db84f954b2aff24334ad88b609d31639b677360993e209e7ec36c0ac41d25969ad53721ef5a88775d3d25f27bcfcd5b109c0836526b797109b46dfe5fd5ce1c1cfbdd44bd2bedc4c392c9d73cee9940d8d8c680329d0fc363f59b7a9cd3929a5744a0e64d9b7c36a9854d2496b9d934e77c92b7d684a27849452af66997d3a9f7eadbf550e89fcf9c5da199197bb4df3901d11ffe9ad5a63a07c7fe8922b1ae3483c89b472a330bfaeb6bc8cf0b8cf2febae88a79423c053ca4a219c52d4392dcd68d71eb4cc023d9c614641e9500b533a29a555d096562cb58401cf97de500bcf8fb6f38ac392b68070cf0546e2ec6a8da0861735640bcb5803d928d0b8c411c7f4ca26814e4c63f5eb26226b9044314e9c38716688785034e484b235c4cdcce109a77f5022843ed66b496965a535674a39a5a453ba7c971c1d99a5da4b88ed73754f66e9bb68b0d5e594083f3ebcbd058c1d116bab0f5e2bf6c4df7aab9cfffc7c3e9e9948d28e08bc2defcab4020ffa6ff3017242a8855c22411b0c9fc2e165a8c5b56029653709b2634fee36585e1f722d30449ffaf2c225d792049d702796221087d41b95624f7c0d8b3b91dd09d712d3f080c6b5c82579c59cd3d239e72442bf9896f3ac98d4a7153aa5cfe145eee7dbe8b3bd10dce6c25a665f4fbc17cb8b1a5e5e585a8f2e786f35e184d07dfaf53cafd592d67d32197d88ad6fceb7f3eb7c3b6b4744ced9199997f3aec3c38354e61af09cdeaa2589249eefdd1105c4891347622971f344eaf3b583e94aba0ead56ad40ae4a6b903c3b62f058014f5443dd58eee6f8252661199570354231948ae1ed2621c7a32e9efcee5460613458deec4f7f287ef9f22a0d96ef68da0d8e9f87e21725dec0f3f30a473438c725371d607aa9929bb8e469708e4bd1a900f3838d5fa86403ddd04fb61b4ca1bbfbd06a367cfff94189eeb93569bb1982f3ba0dae9f5bd0880a180fbee5bc7693fdb00419b2ddb8a159a637efd3eff9591ebc69db45394cbb874a8b290db6d639ef1087a91a5ed4f08269a54d38cf0d47ddf0629c710f7a048c74465a65d233b65b9db98210d1ad7e5d84ea5dae17f75a76f3b3107d543775339bd9ecedb51f113dc5891307ce40b1bc338ead79b2e6e6ad73f29371d6cfd38989049653082ca70f58fecc611ec9973f78526636044cbfce239ce7128e9f83543caf66468d073fcf258d8cf9057a41c60b3bbf60e92ecd0d171b94327a97f6e0514a29a5b7a5c33210a63fb9d919794caf7744fceb4b7a25d13c12429c387130a5ef4fef0c13e20cc637fed23fcedb5bcece179e7f6329dffd8df8c71b8b106ae13e072901cb9ecf47fb78ad46817e90149a0812420e01670f6f5a86a63aa617862621befde2c1ef16a4803a2a93844c014b0dca2b3837214043bf78a15fe8172e948a1d3c2f5d9253e8125da25f2017344ed02f9c1b19dd153120b387ed37e4c2d2b39ee7aee8eff9b4a4f5ba789ce7ae18c45df15bd127f39a82bbe2d3a52096f31c4ad71af902ca5292216b4d7872485bf28c1ad29694bec0f4330affc0f36a689a84f8b40c0f7ed6d0686870fc293787dad29196238c2a565b62491881983e459e9191910d43a4f06ff95448d2dbf2e3d36fe70b4f7ec30e89bcf1db04af9b3fbb151acbdb1d8147e2851225846f54585b58fbf91d875cb8562d5eadffe3daf7dd33dffbcca32e3c69b0587e4b202cffe96cbc794784aed0b8de21179e0fbb233eced471059e167737e7fbcfdbc485e595afea51cdd62778f48bf8cbbbd36a08615ee1a6d9c2ee964edf7b7e919bdb6ce8519aba49f0bcc2ee9193d4d60c85f659c52d9efc22f25afa1eacdb954f987ebdf98463a7c2c4f3ba9a84f85d1c1efcecc2310ac4d985e35b0993d3bb151ad33bb4c2f2bd3b62821067eac0b23b79758cb8bc4d5658c2b9471ee260124c82470d91c40871050f00f7521070f0766a87e5a1f905c79f2d2d0d98befcac102dee92b508bb102d38cf9fdf9d4721f136fc41e997855cc1f25d8892bbe44f2159e00f7965ac42cfc23fe8380eb6bf75a702c4f6eaacd0d87e91c6f6ee94c07b9c6d0cd72f3faef55dfefcf11ac3f3239afe3f3136215c636b1efb0dbd69f036eeaadec65dd5dbc0eeb3cafde93375dfcbed3bfab4fae6c7f266a289a4e10f7ff94148e10f79fd9af97157bc96049e7ffcfc3886730df727f6a4f087944598e6189424f0fc9fc76153fe45835b84a218d39c9e606b9e0c9570b4edc25bab1b05487fdb9c839dbd2bcf23e25ed33ece3b0bbf19bc20d10786d813dfb6e95a064f88fed4607058f6f1879e1039ac068719394c931f1f1ec123982435fbf688b401a219a8646a598a4335ca9843333390000034005316003038180e080524b2aa6af20114001179b2525e4c948aa328c95114848c3184104208010400600818438463039b800a8633fff4469cb02112bba7b18c6889720983c7847f223b9fa0c8e05b82de07071d25ccf677f412f1965aa818d4491dbc287da19482b109b0a6ee1f9984e00db19ce74b3d9a168fa6ea485682252495d29dd0dbd223fcfe7e96f2d297f6c51639505a73da552aa6b995b76f5751949906b5ac454006d2f0a35901f92005c4a22a21b958db58153d758a38548447420459157a234b34bc3c86970a262a4a44a8fe835c3accc41504cde1f8769e5be53f14a268e726ba2fc37129f22cca15cc173a186afef1908ff09ee0262b6e6ef8cd027f270ab50cefffec365b712098ff0a89dd72516d13da3d44e0b19ce4d71b5f069b80a06ca562909fc8ecd3306976f35444ccc32ff084054f5e04ee5135c25d3580150d04de271e15270695536ed00df8a051bd03821b5ef8ec63b2d0f17496427d9799f8feecaf0ce2ab300f10df58e96203ebda71c902d61a2b61cc9433be973f0bf658f3bec0366c79fbaf34b140ed1e42b29eeb28353c537326a83b6db7216d17ca438291e60f1bdaa41a71a43a807ad708a4a96813cb910c0234324ea62702d044e8412fd27edb3c1347431fa23b71940b02128c79cd05968172ec84427229f2409574359c3468a7273a58fe88953bbbb53aecac0d75bb1d8fcd8ab60f730cbba170612e09b1c71d89571ebc64a7884c59c8161d34c41d20d99ff5523722e068722461bc8a4610988c2103c7708faa59b4d821e14efab164ea248a5c58ea3a92e110bd89c68df5bbdca69ea2a0480a84ea4eb3e2c28de6bb457e82818697e0618727696e59f781831dba51f572b63488b5930a725acb7086a47029f909463814b038a913756edefe8c955d3c857c7d755bac9ea6272be3080bf970bfa5efc42130ac8f8e4be28819617ed4686eeda6b67d4d505d014ba3775e383f0a5324e08df6d96c6d454c863d31a1901982c2ad2c3f02512bf94ffb81b02bc62084f6a007fce6a3a79e51f83639ede732bffbc70fbb95ec2d409e7ec36d5fb607c227353f873ca7660fd652b2c04b6975a806f4f526e28aafdcf92f91ebf23a3baa2ebbd54900b0f170c399abf58d814679d71d7b5d1ef9fbabef86fa4c56951a4df4f3d8feb9933c2341225c61e865bdc2f20d0dcbb3facb10ed0e5850ba175d0e061839214e01bd04a5383df04a775b2c3f3ff7749e35b84919db2c0baff729d72d0e1c36164865d75081d2eae90bbd05bd4636ea8dbdb72e01f635ff9e59dca97445abcdce6842103cb0b32083aa9866e33e50ba4d9921273a2d17613ddcb2f78bfaa9682e888510aa701967a509f30749b2ecf16608a4d9db59d1592c54c2d25f7e6d076a76953a1ef347e1b7dbf3f73b8ad8352842ddda9041f9b5e94a8802d553bc62568fc68ae7a3276f1279565b4f2f8767e8f4f37d00ca8e27c7735db6c6082024e73e7027903b65457d791f697ab6600c366f74511a4b85b0d8b5a7d21bd41c151057d11f9205702982159e2e513d19a076f054d53792d5d0b8b139315bc4afd1dac03c9731971a22abc93ab5a4d314b65c10530e27ca1ceefcf793be863d480d617b506b9e292debf6ec1be6d87f9ed8999ae77e0a8a821a10f88d22a4346324376942b484b10ba9de14d1e3d7468235b74e089e4be7cd4cac038ff60190558201e13cee9d765b18569dde3d5c9a68c70f2bfc3802ea584546d5ca781c99926a12e5a3bb8293726f0b7c365ae1ba511515ea2656d04c043529cbbff9ed72183fb7abdf6a4c43dfcec55cd4bdb0692b5cb5853cecb78fa384c78d61e4c461320fd53047e7997a5681b8d5ba9c49d2093a6a656a13a0f2c2acab52b0ed1c2678373231ec0369eb9af014883062dfbbfbbffa0627860309337088060b465e25593980f1f63245db4d2dbafab0b26ab0200e7f4b7d813185acb8688fc62782b90036bf6bfa83598f76ea6a812a039a5917496731e5105a0c8a4e27298743c02757f31b929baf6d5961ae7222251b62ccb4e20ad5f62a90a351e2cf877f34029e42ab0aabe7918178a8c8364f4292bfcef84111020a1b2ae040594dea6ef73d83d618cbe23adf4424accfcf206468cd7faee4000884af6a4f82cb7199d4b8732e937846a4ce0b7cf3890fa26e7df5ef61aa2ddd943c1eefd0bc64728d0d612855f115a773ac4f46e2db3ca201eed4a6ed6758e9686a54c665b24bf7d3d0aaba98c5a3a14df6885735abb64506ff81f657d34ac1897f31f658d20f643fcfa1f70f68a8ea511df4bf8ca7dcf89347ca81561694bbccf53dad64847253a68b1660bed6a141754bb6fea99c75e95665721c3502fdcd72ee05eb3b0d88aab0e1092e2b2e9e27481492e39ffb7d5c882c88b6bf3d4241f417ad9b7cf46e09c8e4383100c4f16e3e64b6d25b1096b4e6d19bc6c8c660e19feb36595cd08cfdea2b7ce1868bedccc720d954ed6189a3e13f8c9bf644c20f96e706e5ff68423cad062a3af527cb0c8bea85ecfa0c6b4d0e864c6b3246fbf537ac68802d6e6f7d120035a1851c61cc7bdbf4cd88edef952799ebd24a4d05c7cf82beae23c9933acc09d67fd851ae6553309d46a160576c1174ae9351b8798854f2712983b80f75b1fc971dd8d50701eb2ced7f31262dabc006d6284e0a640d4cdf9d2f2f14b81ef7dc2e430838da441be003525f17d0b326bb844877e457fa0ac7479d52822ba84d1eed93181468e5a5a671e8620459c36dc4e7f26df061f55e800282e43970be4772046fa0963c0c7a6d80ae040a02bb891e765dac1ed7641a5b45202cfca6039317c099b6e542b823b1d1711ba8796f724995e1ce3521f0584ba3b5b194234af17cd4561df5c52b29b12f8a0374b356e71d7dcb974b051146697a438f2f531f3b80907ebf298f3312a04f6633dae17bccd8b4902da9d624a70888230f2947c48c7ae139c4d9295770b05a6b3efb6366f7d20af279d9fb84df379b5e4b3dde917fd6514bfcb570f4b47e56af5ba3e9d27896cf3a23e9d89774f1b7e89b61de876a106fdf3c936ed9b1c520c696cba71f98934360420ea32bd33e7e217590471a5d65975891a0443b83e2f310b0edf9d0aabacda842bb82b7756460b4d7ef8f035a2b21868d0e3bd4a66cfc6cc7e553b3b616b5d7fb48d7b80cad7a6767bfb40676fc7ce62545203b42225e2f93d7bf41414b7f0bf051549a92f946ef1d2b184d650289cd0673093ebef15cc088830f99553b9481f649bccbdd06953ff311f0159b7c845fb4e9a4c97a0e5de19a62f62e43b81636032ef22e7adbebb64798d1a5e5a921aca0ed9189350a01e8dec10a70630197bdeb8288f256ee9394093cbc2fe5604240434e82c85adf0d31e882d64be51770e82708a48d465dc4f792f63aa385280e53571b5e0ec57f1f9d8bf153337e1652dd7a11e4ce9412206650cb185058fb6efb53dd1c4c3b7e33940b0cb00782d36eaa25a7d4ee3035640b2d4ee07be3f88e4b126d2f8c8eb17c486851da2c9bc045906dade52a85614a2bfebdb0e7a181d2d374622f65d5cd396f85e6bb252500234dc5d5f50e93ee12da74a9c849e88e15bb81de159a60d20bba68cb8c30d1806955505d1557c08e162ba002707263cce400a1a879b6237f33339c05b565251ae712ba49c4cd0a1fa82a582e3919722c04a402236b9fad914b846beda7475325f9a6f14a6e218b709db346fd56e35766bd5876829dfe06ca938d3fd9d48313906ed0b4d1d36fe5e6e10d0e550323b98504f8763bb4c7799671c626272895b9e6611d4657b36acb88549ee1b5e9ce29420f82638528ffe4c378158fbb67bcffee197f1e4d3cce5b2d57470fb2856ef3e683d5bc446df6ffb905041af320e8a9acdc06b0b045ffab110bd9140bc8e356508e606d7f52bfab673edf130631e2a1b2c84b0eabb8970b66b533bfff4b44483e442e90147f3778361bc6704c9f6c3e8ba2932c466de7da05c6ad8b62b354d575d1f28e2e3817d2ad8284a6a87386c0aa37f5aec395a7a0c6a2a4a4b085a9800c204efe084aad04ba95773c587915f8b3e1c8d855c8dec7e16a75df04d3e42424417fb1390892892f1a3adff7ae09e0c7bb8938c98ff077f74ec63c9dd409050e58033e34019be50a9a035c2ff8447ea304d318aa23a7deda022e6283dfcbed6431b7849bb96d7aa62293043ebddb1df1116720dd3361b17c419247cb652f1e915443fc6be8932d642ee94881899139e128f6ebc1614311ad4e10cb524c224ce0ac23dd81a3b476e9f840bfdcd8c30a2993701b2548f50b9c504a7a629a322bb22b6c469e807fae416d7a6ca486a78b89f6067eb45a01e235da8191b53a72325e63b6c06c58ada2cd3d1cd5a1f03feced9f56cc4f6a9dfc540dcb031bad8f42d94b1817f2b99e74dfb2e0ab98d1cb5aa92bbaf35fac643979475f33a2a44303edd108e9070d643291f9e487472034f87dfd605f841afe6152c33f63c92aacc9592ba1682ed3a3fb3a0a80892e1d618271e60411910201456847e935165f320ee402423ce913091b8e2f8f9e4f77f498759973a426d671df83cfcff8d078e3f38a67e2bc691c57f56599802af9dbc6e70a1365e2be85cc61045a346239f050fd3cf15c010e5a0de10655c9fff039f56a6c8b881b49c489e2e08f24fd80823301b3c4b76d362b2520e66b838f19cd360ac6334919e456ba4d2de24dedb140f3cf367eb4b4941f9a0770c2a10f4d408e7475210de2d4e8d9a80748afe78b3b5f5163622eb45a0cb3d31531b4a0fbcab933b278eac661d5046a6ee06296a3f77fadb5fdfd647b1be9cae0eff5fcfc6876cde50ade9655edb34e626e01f8dbec4a4300148f7139738314c9f78c61ddef08a415c4beec71487b279808d8d04f118d824f5844e03988cceaac091528395ab7e88fa5cf2414e2e95affb4dc6b21bd8ecd3682da314b11ff9d62fcb0cf97f309d9b84cb0dd36bfabd24fb3ac5eff10fefda1f833b9a25564747d0deb94aa7282ad0296efbb52ca5bea7bf23e1fb1e8170c4a3e1fd4f60719c6b5b44cb0c1bf6374d159e3528014170874cb5884864d80bdd7616354afa9f6a3b7d9bb4c82b83af079e780aa45a4e56bd23248c08531e629f86c64b94b0a1b1c7176e1eb689fc2073805b1f8c45a8b7ec3f562c3d5ddf422f939fafa85c70c6152de4663692d7e4e3d0a8802a695ed9926470982615ad63b43913611b51ced522895669479bda82a1314a6c66290d4e887764cae247782b2fa9a04c86216ac5aab45b5b95e5798210e1128772d2f5065ef6780302b4a1828ef2fe8fa14197bf8e57c784790fc2db41509497feff9fcd949f459a09560380f7e90eb1e3b57cc6dbad2890b08fc4a40c1f23ffcff494361854d66e5648b8a69f6af222987df781981205e5c289a70c549df74f76efdf82b22549c06e7bc6c1904a79dcf4f411caac79e5c93c451a82226aed8440fb8272abfe9190a7cc48ed99f738a67319ce9e3da0cab966372ac6c02826f1cc67acbbda5d76278f5f1571638f6ee172d493ac996b2bcc11b6591cb6948228d2a989bf606ee7e6ffe49209fb752b8603f4c2a64128adffb6608b5f09b94722420fbb1a98116d4f254c93784ad71ccf398ba669851ba948430af0456da252358419d5674ee9b61487042ccd6e58adb58beee8368ba55d5efefe7fac675233878d337cabe0986c78a84e2e71e10760090af95b5fb8160aced8f345a47d7b94ee300bf62e3bfe92769c0d8adb2a48d18e2b51c7c164127dffffe3df979d8a292ff60f095fdc157c420797c857ba7ab3c5ed766528cf354a3bb10684bd08566ebb00f2212da607d8a704a68d820c9083f9c0c3cb1bf4065ac91cfb19ed9c74da4eb90c7ca395d5f7dc5f811b2d15e0d177a40f86b8f7eb621c0fe2002ec000736d80e542759b32dc1ada6687a60f1a50164435fd9c7a30d94696e36859ef54aff538e22fee6de883213b92605a128c614ae533f831687b5e75bec927f63dfcbb7e95c3570920e5c3e47e2515a4cae43181248570f0f9d3da5220254e8ea3f457579485cbb7ae5ede7a09c44596ad4456ae04002e7688e2bd6b6514046476982ec80728c5a5de7b6159d9175a1309c77d618f7be11b8728a019501c571909a496a0df1e268b2d8788821f5b80a93febd912a71530d6832193e4e0a09dd65b1e521748efd83c5128ded6d21d5a1ff3bfea6ca3bf4685cee29ee0f5fc183a39f0df78f71c401bb039b38a21ffcbbd506ea311c118156488d5e00a9f711805f11415999b88dc3e731f7aa3cb6dde35171b95e93bbae1e1160080ded4e0b76f174b6ee6826447a440ba31b79774bc2700ac930068c76d5f3d0090e19ece53c76023cbb8c976e63b0e1407044ecd76a0f666132c6b2cae15867e8392bb633df67c805d72acfb530d21af5668fa70e0ceb025e3743cedd844339c2f7d649aa37edae9c0cae71cd35938f2cc468a9ef09338afdfaa377ce7f64ac7d2bb310031446f7d9497f13a3596844fcee309adb4ff0c8a6527769797ef52b769d4aadce3b1da8ec676be77e41149e19f173ca9fecd8d306fe797d5b6aa93d8daec63f077861e13427d039cc3aa1b1f3c82307292c1b1930b719fe35535b316f4194606a877d95a78862596f8ac5e06c62b0a7ba2ce8c42c37b203422c8d90eaf98bd7ed0a2e8ea1108c4f05b0c4bdf9cb40a1ec40aa26273c40550da1671e825224ff4897b27f7919e92ed11299eda8710837162751c1f96ee03908d8e1509a8cb53b6fd5fd56c2b77fcf7e00a546867c0338945ccbf3d5508648fa7cd18446820d283ec768f03f57dbcb7c26a0e0d7ab2881022c6263fdbdea9e021b152ae6ba4aeb757ef1b60caa95ff9af37b9661ccd370d20485ae3a61a94e0ceb3ee04e8592374b8c90e221b4fd83417e0d87d0b90dbacf99224324e19a5b637a61d89f83808300119069116ad2e19c78d26c83205c2e06c17795ad453ee6d47955c9fb5253f3b46a5b07ddf8d1641ed5da6912c1f85c3bb23436c16645637b6b79841d85247ef80cc317b0f40550ee25950868d38efc17df52a01913f59587c84fa089c2b63447067806c3da0b9090c5bc33d0d8d1421aad66ecd9c706a2b809ee4193c6b71a3a384fbaaaf5f47c2c4f0a41b148dfbd3805c86b1a73425da8524a81a7b9a1dc322142d7cc6b7da25fa106b6de887170d85cf5b04b915b78dce04d02dd1950dd68e61055bbf5db1b5af1fabca8c8e00cc6dfcc120cdb970c50e12c1d73701f00f4d0dcf2a8cd91bd2889af3d4879d8caf497e485b2a6c44d8454d2cb02bc97880bbac71b099124999a23444658ad58eb6248c97e9c598faa5f10ea52b9300abc4ca06f5d21a1a586a45c516a0c585256c3a410a4db066666c444cfbb89bb26ff5123555490a7e47cebaf49a117f5e81f1a9b8c57209be03a788f451f52536c446f2aba792417bd1d8b74a3ea6d009b602bac73c2f01458f0da65440628acd0af53bc29f0e721dd04abe2426d0ce470ed2a42312b78cc486a8a0992ad3f59aa4fa165f40fc1a7151967dfe3144795b05514447f024e5127f11afd990ca4b1ad1429174a91d685889042acddd1020edd07dab346643594ca8d717cc27bc82fa8d018a6d04061067294def003ddd5350b25e93130c91b35a482bffc792195706d13676f8082f134b34eeeceb78db8bf31f77b68580b667f2c8d86d2fa62c59dfe9f6136667a11caebdea53b84a21f88494a26103b393ed394c520ef25712ddf82aa57b470976fabe3e629df2d53a259b8b4630edb37a22160095197f51655ca14d5e6d64d48539391d2be9fdfbccfd01104bf37ba949288d8254377cb43172c2f6389dc8768f231a37e8f4165cafa67cd0a2f3f5c08a3b370e73f302d0c10ca063ea46cc3970582bd5d2ea781120c1d20014c20c332a321e4d5151e3db8903a14a7233c62635cf6c0e02c0434164ab8ee26b4e627d369eef0bc4b3eb3f2ba91767e6be0a84bb70fcef720f5366f63df229b6331db067753126473e078ac9b1cc848786b24b0c21bd8d1ed2e8cb1bcf8795cd58be6c19441c8a41161d730aa2cb65d15ab1f5c9d57a8dcceebbc0f22560c8d87055c2166385d3e33f88a18022cfced79da7a60d939ca36a3870f1ee9d4a9cbb5212ae29feeab8cc0197404440b38c6eac85519de69016b5f1ad0ef1f4dad51522d3486d983812765a42aade8f859c953152da41df9141bbc4f7ae59281b4db14f24acff0b39235f5e8b8a55e8f3ac90b0c237b7b97bf1a2c419918da08f69c91a5e6973339d434b3b4f3ece1d9dabd8931644a9277ced48658b04e1eb11b984e7b6d5f734932208561c9069e90ca0e615ef82d9dc12f7aa0c16fc7e9512d1a86a772a08454bee0066c7eca3e7c90ce619cce1be8038faacaf434b752532bb0ca45141ebe9fcdc72a48ca6485680467a4213ddb7f756d47953230291d678bb71f0c18ef01b71130d0a71e8161ab80f247d529371db418f3c1727f557d4e1269741a0e6a1f7317102eb5c9270bc20fc0cec4550006b1c13892b203e62557a361e198717f02541b35e532cb364c7d8940d20abbce6d03fce80e1c28bf0ade4dbc5763834a927bb6a424e002b2f9e014b401dec941a07afc43bf9cd25c4933f1572787eab436485c8a8da581f513262666a6354f5c2f3f044e419dd3ad031df63b8aeb154ee008b69d8f101bac28ef4181b7ff60a404d2bdc9be14446daf489d197eb6b341c2723c77c668f0864ed0fdc337b77761d8a7a5da6c5b3cf4af779379e704c08373f66b7188b381a28b4db9e96ea4d5dcecf0f8d4a64fa60eaf33445051d183f7d203326cb1eb01920dd68b1fc45a59c574d87b9f08b8c59a54467d9beee232027cb1b2dc34fb6648d47eb05552d26f7326e338c8d9a6ebd6eca51c9e1ea1c9c15c6e02a91d89dc7425975d20b5c592753efe1b9efb31cb6e840723e2f8ecad092353292bd57e107c3147da1e47810204ee1b9138b04112de9711a7310548000629dc0c58e68417c193359a0cf0f06fa0d17a317a52a4a5a4ec7b5945cecb8e337f8056140a5993928eca2efa063520be19888e7eea349218e33c05e71346894bb087d0595e2618120d63bc2002cc5954818e5d9632b9dc13345d15f336ba6ddb9d0b2de6f51a482b5c365d80223f957e7aa6420458f504ec77ca3a223223bea6d96420c374fe4d1312c636fb3e638b084661171c45b01d566c99579922ca319b9494aaacba9017160cb878d868b7a3f99d38ca5c1869c6c64347aa1b5a0a06e7c3bb4e932bdd0df529d61144f61f80667853c62cbadc6c4da073c7ebdd9b29733a2b6cd838d3ba4080be80126170253c5050e336ac9a8ad10d74d9d7f86321538c9f6a30bf5ae9500964c54612919f04e9979ae6520f961a4f6d8fde0e677f4998e9dee44f94441915c56d942c5037110a5678456b575b26aefd36975b5277a958cc2f425acafd650a663093e1077623d9ad3a4664f73c26191cb5def7edbbd4eb6701721a51f299088774e70a91301e70066de0f80983cb6ea865284e6bec2e65d68b271ef09fc7fa317962ba68266a9def9876b85865fbd4bdbcc75960b41fb091ccb8e98750b545de3786ea2bdd46dee9847df67272817a25760bad9ac75d08c816cc7e2110102a04edc67b2aa5d695cb616bfd31f9818775173b2f4ce3717dff84c486ba32d4ac8d7b370a86ee7d1207a661c5cc097897e7f71f2428d7bdfd5cf26ca57b4fb1ed52362c44c7cfaf57d643154f8cccb8124a4d974208dfafe70f0ec6cd1b3c003e361019a651de2180636b74c31004b700990841c3e72492c8d83e9786d763cf455ce6092272119ab98d7c7881253e68d77a1e6a3ce8aefa4652f5fb6de6e530b1a9b69e6f320805444e7c6885158828d2919e2fb14a5e99bf54501c548579129e5746eaf9f9f52e8c29fcc912f6e3482c004280e77eb5c4c04d99b4829fa8b3080772150f91556d815daf58068b78b4af1d78e6b9a8f19f55cb022eeef016d0c7df2038fda1b7c059ecc6b7d6514fa967e546218731481ebedd5c58d76a33018f16b8193fe6aabfc2634e3401fd85a5e3bbc2ac7b2c38bee2100749a13babb11ba999f4095c361caf4a0d7e9af829059e0c418e8865db30f957c674af04875664319f5479d5f2164cd73434dc44b6c4902c54a88d962b187c0a4026756307f6d644e2882b6afe25729ae3bbb23fd2d2e1830f22f34540409a20b4a11f344f9adc7bbb22fc2475536f5be2f640eca3d01132bf8930c856abeb40f323ddc8ad0081230908fae7718a3750c3f05be05be56aa8b05a4e05d8432c2d454dde0e82d267801371ab42b2d38f56c801cf580c60b2646cce9123e5d2cc558533aac8eeb3ba4a2d9a338a4c92314b52ff92e56a8e9459f004b20bc49aeffa612e1653b8366609167994c866644300f094718a4e92ecda647a34f1f274a0084b33f6a67eea0fba32bc6c6a1d0da56c85d45b0075bb6d9959b57df62a667f0e915909a086168baa9e8e7b221e70f2f70812461531d203929e2ef06a751357099ac460482ab07e052506ae67d7537c75433197c19a0cb4f685eeb1c67d9774fad1d532502610aee0aebdbbbaa5e6e5dcbc9ce0f65329d7ba51075e82804cc03b57aba04d7107a67373bef46e974f0e86eb771473238b2422997a9eda3184ac4e7d7772910e9cbb5c9661cdf20fe478786b492d86b8d18097dc236a70beb3cb0120c9b5b7425be8dd958861ff9083824a4ab0fd2ba1fd8bf464c7a61f4c254c9a6cbd9225f6b59c0362ebfee9878b2c1b89324004a57dd68ca0ea6ae6a33c8e5602c1182f5d602e203395ee60c186e38391c3be5459ac0322a083ab8f5f265021b6a0265060164aec1a484f8bf2d55f794aa0796386027b6433fe0b1c6b7b960be8ba4504ab0fe57e7a240c05f0e2fdc98f3038e7943e59484de333887290ed6daad0ff062dc261bb90a9dcd16450ae902715ac3f3379de529735bc20d903b7b85ad15eaeb74ff9c15b1f026a07b53d95f34238204a73493c9de1130e47b80a9ab1314fd13f701f1094cfee038eb9f9630f441aa0c9d55028a12bd8e2253e0634b510ac2031faa190e6aaa4af991ba40f942cc90bb938801d64f71e20a33e1f4a2f7fe899ccec1803e83f4489ae3bf3464ee1bd7ec37ef735eac1d07fce9bea11020e03643aaf8a4e13f7f7a307d577a44a5257ec1d12b337047285e7ce803b45d32840f8a571304bd31733ae74642af71e3bce1c7efcb42acad1c22b20727b8541a99b90f6e2b5a25485483e3261a08c1da1cf4913a4b5fbede2cc0ddbcdd2edbcecd667625cd6f14a539313defe3165ca6a088a7098cdb2ac8b0974489a0948b76973663ef85359ed65c8cc1015281fd7186c370b919a2be6351b19bc00766ddcdc87ec6db23aca7a3344640bf4ce44a2ee3c0478b30d3c6e0a59c71bc313598b31fda2d39e303f669ae57d9380367ea2b0d8664573b45c2455194598427d8c144d8dff15b40bb3d684401a8036867e0bc4a2a5e89a29736d1edc791f0083de1cfa03bf0a0e2cbfd84497cd40df1a64cabc840f00bd7e3f5f5a01a5d1d64a012544062f12f89f1a130325029a17e8dfb8c5a6451e97190f803d735765b1ab7072fce7cbfbc62c37a4dafb5a6e3e3844b2c2c186039b4c66a8e18858eb2a38f0a9a2f6f383a950e749a6da415a1eb10e83e0cc1af9efd5001daa3f859e05c32781def749982e18631858c1e845cf8de6c43f3f7128ea510d149e92d3088122cf9e897da8c46b6b1856278930558297b5e213d0510b731d25294457208ee3b98eaf224ff6fa242b3a338f9beb5031eb150d191d0c16fb7064e133c4125cbfa5fa9e10ed42018b4d49810579aad5a7d678613943b5c64dd54da1a9cc54016077159bb9cbcb81a2802aa1ef0eb5a05551c7360daf51f5bfc40919e2b7f2501b89a3a92f9b219ef53fc35b75040ab4ff6eb88a6b83baef6c11730438658d05318a81811a96ac324608bd23c07d92578281fe0938268f9f7c219423f4336883d528c511965e0e162f75045730d1507f0c4ed0624a4818e9943d53792dea0fdeae805957b4de45d511c0b630dad14d39e319389ae8fa807a450a2cfb0594a868236dff5c57d50394874ced3523327074fd105ba4d3d41fa1ca28989dfa73e4c42893503c37283aa7345fd3be29f35b5ddd2d5411a998684184650d2e976a4db40099ea53bad423fa5bfbd96e0459a57367cef5a3c26a70e6e00f2114e9944787f6e688ce46d5fac0b51b35c28eafa722a08fe618a7828663d430391c386e66ac92830418d8542e54e89d62cb5b9d38a33fd07225e233e4038266a4452559980a702a241e674e433770818940a77836eb2aa35df7447dee2e2a456ac82674065d7207eb50dffa23c549940da1b2bef74c196e5cd3d696a2fe6951d824e69386c0904b9755803ad3c0a9d71688b27edd12236e92d9f58291431600b5a96ec58a064811e76fc81c8bbcd71ad9b26fea607abd7cd44accb4cc9689a84216354df9ec9a255f3aef8f2047aa3d37df42bee02e043675056ccf5854f09697da6fba3d581cf4a10346fb4611a7a92b18a761ccd51bccc338b544de02dee76cd7a443c34f7d9bd7774172ad8e5271755cbe88d0fa6cf196de06d42a9f18fc086924856ae8c5072763001e4e21f6245bb9196fa23200e20ba106d544842a26c6c3ea9af7bd895cb611d8c92d3975d10c39bebd093089728bb0e48de6334132c37a83227e2e0cba7b2f6d2e2a5bdbe652942a11d181cea5c21b06d0208ab3e7aa356f625b498851588093e252b8884ace894e048be21870c9d24af21cea1155f26b27d60be8e5c12db8b6a59a2d8d4390691af42c2bec341d2afd081299b6d9fb73b2d3a976906124339ea950f4e9e41dff8e469263b27990ad9c247a6a70c720b07fc037a0a62ad642f562c48914ffbdb94ca6ad409cf795eba607a51156082d79267922c11881b9552d15ee30503d4c48673e1e8b98e1ad06adf6e28f1a4a70dbd44ab6f02d8e115fd1b442d725b8db08163f75b21eb4f103bcb96050eb5cedf7977bba3d35bdb3095f870c64985a6cd0f63df567610914b7b19ef625a6a7d919632092e23e2c64f6e4e2c9e24ef25b72124a47a6675b353762d34ebbc0c9381bf75628690250d3a4189b258a4efcb1750bb6f7a5c74626ea8b035e87a57fc757c4a90aee841606c2235972dfafec93f348a1f462bc8311f525b85639739f97d455739051a8769df433022016484cdc0445a2451017eebfdb2a8c197f485e72270755320b71cf10fa2277f7629e5f1b72db735168699890e210c558fb07a342fbae09b65f820d340a6472729679678d1e13982d3e603fa97bea183fe1339728fb7e6d9f21ee6a01a5c8b296dd8e6fe6d2bec3419a21df90196a8a9a5052ae594cfe156a06ae52bf0060569cb3380d4c71eb7c65bf83648d054d6c6cbbc6b50ca3e178aab633a4653746a868b1b9178008dcfb0344f18ab1d7e63205a03d525aef820c700641dbb3009e100eb386ade95834962d938901295785a4af3c95a2bf16840cca84f54642719b4b23463407922e3b05a302c3b5373e11e8d14a6c762a6a8d90c5d7a52d495f7fda1d6dcabafeef0f88cefc6fc51e8d96dc14d09549ca53728013ed058d88ba56e2dc9790cb4a256982b9a52d2a04dac22376e9422365a2bcf6b77f91244e92aecf58386ac4e823642fb31c91adbb79561fd6cee60a5c6a1130ceb62e6d5ad611ee6a169386da93458d00b64e22d4637308527af4ec82d7f1b4115c82ed32315e99ae409e39a0a16e8f3d9ee608f8bc7136eea94e86067e8f7cc49244948db8381e36b2c68c5898c18e6a5c3f529541c3963071deb19dc3aee04e213749e7a7145d770b66eabe54133302b502bf64f411c42c9c9f848bbb93b0f0849ade1b5c1d2cad3643d54328acc77bd0addf4c133d566ef829e58be06115e0efc18edd21bfcb0b12eb6390648ab7e4c1e08297a21fcb352c99405c89bd18d3b2f857237808c1128115af6ab7ec5e32af85e8162aa70a572f09a01003c121b6e96d4893dc1d5745dd3eff84b23a50a872ab51fcd48713676361c227ad4a31e41105e8579605a016526ed4f12f265d3653e5eb2027aa85124072cd8a0acd01401a48112aecb305c62c5be6fa97c535a78b95e46d44011b723b08fda34292b75286416a918c7163cc5c3bda8d4bf7370b8067f266e5569ca4b065b2c99851060e80b91f18762897748a17075816eff00de761d1c26f8e2d32fb0b294885a47164b2986c8f2a919de41d110987468f543e7052606b3280090d0736ae857facab96674eda3f13d2382ee279a792cc868253c0c90c84245ff8c4d468b72b6656066e5e2f2fd2f49ab32a0fba95b2197fa05a0070b686201fadbef8fb3ab8d4684498a32a5284cd752cef61c0f011969c451d36175fa420cb8e52dbce24d143fa6865e992970b63c0174b05d1460571105a22272898f0c089c52510c3891dfc40f68b8d881189f2b7613054444f3db1aa537b8b3a7b220eb7c750ebb19c19e08344fb8648c3a57accc9af689c9fa3878e1386119b81872c3ce9dd1e1dd22e618ab479c8768774d2c44bdd2eca43c7ea0046e6d3415632f51742a974c093f34a35d2046ccb170a6214c0c95e2328c120da8134488eca20c67f1ab28f5fde74852da2c2b8d0a247bee1356917bba66acedd6cf5a852045c87c96592044c86063f923c9cee41ae43696d8365f833bc17a0049803c2ac6d1e1533083377a21a2ffb81da5b356a4dcc8333297e3386fca65c90b365f503d484566d3e1d809be15d5e96cb0c38df8a863e5fcb541663090343561c4b3c977edf5a9fc579e6042ee75c2c60e0d495dd656d4c415c155fd6c4d868c448fca9dd3c9db4d64d4470ab9c54b1e53f8ceb9096ea04a8706fe94675911cb5e698a0b2df0f08cae791b53f7b6bdc78882dfdb28bc563aa9d690e245a54fe7cd6a7021b0795d22d67e3a7599d9d945950591318eaaed440cdac3a7e3850359358d6582d46fa4582c8ac6938e4ec0f6f21cb2e43be1179ad729163267c633203fdfd1e4200128cc8403ebc0b03a756f270548e88ea893dcdc23b25817b646a3ed0312ef497c37511b56dfc314fa8de95e90685a1e8ac2b2609694187d4664e711c2de6719c15b8ee64e53ee1fd366151dc6d4f728d12a9ca38325e5296dc828db4759610cf5d74abd0b24209abbc5be88f90ce450719ae2fe92c30e35e28d38c1f001405025cbfac6dfa44ccc7a1a8bd1c9714d8a748ec65e3ba60679c59051c7cc9e15e2e044aa5250a040faddcec45105a6ed0509c211fde6f746eafaafbf1f2db046b98160132d10af77fceeeea1282d32bd49ac3878e6023956ad4a9354e0104291a34a014ab919d708dcf24f18f715600a53294b2a271cf4d4f2c3969f30cc80562c859113d83638300bbc21b686e582501c158b482981bb148045729badc8179838f43b0dedc3e89e3839faf1d895215d240d0c4fb0371ec17d220ff7cf21f0e463042723e929a5b785987f51cf559445622eb59c40521f4b8d7d9fe0441f9cbb3228adfa6d5994be466d53e50a041283e204c1f900b782526b0edeb83c07f932659e4ff011b189c401d3e16a9de9809cbfc80f46cc60b08a22425976f003fcb2bf27581fb91f00e827d472104230e99630e1353220abeb3344b7659ca3882931a8cd715fc3b827d201af53596b8b0316e1243ee4e661067b5922706338ae231674880f7852bca167f473dc4df0bb7cffacb32cab4631cca535ab5c33fddf7d0eacd25690628074a63d8b13de6f95c378d00104304c27c95447c1ba68e51e1218be0125ca33b841cc0472cfd8c33118fe26e295814e65d3cf8fe284c7088ffda0568fe8507510f426e34439729eb5783827813229420104051bff2411e9973e3e383f92d6efc726ff16b3e2f859450994d361252439e3416c4856c8ef6d15b0cb652a02c05edd9993aa39fb7220f6c8534967e3f4abd3f803bc371b7cdaf4c9ed22ca5b67753bb6855a321e251b55e136afb5e8837453a738e25a5ae437bf19a25281c2b1dba55768e5d20ce96f6b9e4234d20a87969588156f61868e24e143b9326c199a73fb75629b29f93c0b9798f8a1160790a322778723aa24b83273ca8988141e2b26dad560ed59db88424366d9e8424fc04f8fca0caa44ce5d5e1841f30f1ce3840c454524fa48eab769da6d9e125aa7a5663e6a5cc7256e1f88ab09b321c202c52fd3cd6abf64e0151b3d2ce5095683c8867feedbe21c8462c419cf04982eb24ec379df46dfc6542067ab208e3c0d7926ef283d6437017d860cae421bb5841b114526f368dbbbfcdcd094189c670a105e56d52dc9c47e76ab697612ec2ad9082b1263d7dd0b56b206c00dee7aefc0a7610d091c06a34a93f323374467aac15526e0c6c007f47add3ca1c38c46b2eab23deeca15171e4fc2f73d2fa21f98ddd3659b5ea2a60453a48dcb4c497268bfd012dabd00330657bea37ac5e10770d26201e26c788146108c8ed6bc3b90f94266929f6f775f8c0fa6ab82d901332421a4274b81150a8b8728ad7c75ef421a1886bc8c5cfddf6440696b4a0442286fe6e1c7e84dcb107c5206b8614fa264e011bb6e572e4cdff6bd8544e7121d2484bb806124fc9543e5c8fd77f00084decf21a9d3bd13fa5a42455e74e6fe5c2bdc7d9ab61b4acea6dd885e31c51a6f9d0ad363d74c88d9f056570cd6f30ea70d7787551029ce1dde8fca9c1a8b1cc3c4e3a84d23f23103fc4201bedd68775b40ea522b4730b119ca786a571808ce60b69ccf5d89332247660ac9650d7dee483c1d13e5c40052c1d5a30c86b8b7939ca12c6d887627c6062e83640dcb4fd0593343502d26104691474b9caf8e00dd5e4544803ebc6d6bd01fdca27389483cf97b5b2ec2ea552657903a578144177687b3761a4a21e9fe89268160d0466a168058aca24d49e16997e5ff6edddd72851c8cc698004b739a130a42ddc0985c6513a1d4303f8e488f534086d223d2443b9c2861ecfe2543c3a1a059bf52180d53a5ab5bdacb9fcf8464f0bc38e6cdccf9c1f25a80de669ceffc260e2ffc22e225f4389a5de085ddbb888f7a1a17244b62c32474d04e545b05702f028d254f44817d0f47de0596baa6db4f995af179aa90d601dd571a1111f6afac42fca8bd93a303085f73d9335f90cd988796702e9c10f3ba4916eb80114041b45a0eaf6957c0efe2a2373c9bcb87d1f9935827299f85fc6843782c4999941ccaa08b70bd3bc939d49fa306d406acf753911ab3e1ae3057a984b19e807f8e261e0afd9f6e7b8527fe8d30c19d0dd85a4fb0f8537620805e2f3ee4c61dea3f6cd58f52f99b2e28e3d8cea610c90a019adb847bfad4f6e933bdc3769cfec627d8e60c8e415b22d1500daacbbc8b007c41600e6f5b2e648cbaad9073589185c42b76dd739cd420198380c856d00305700e30cb10db99c17afe5e452c03dfe27e7cab2fe0f0ae5cedf11a663c69b486325503d631f21a720f4f25ed7c00b0f28179cb8361fcaebd12680b32a29cc191b0b4274b5815a711deeba0e0697ec1074684d87edce403871ae5a0e9df1013711a1bbce6910917901cf2b3d674e8427f477a9f446028878d026da5d812d78ce851951455aea96bbc581e18b69524ea4ae391da385c22b74d8d4eaf16c569365482907fa9388e5b2550e46ab17e8b6794f9b0978c56d51d77f27885294ccdb7da0f0aae27463e38552c9226a3344d5a0d4dde860aadfd38e4a54423c8a467e7c90953e96ce7de969cb32fdba4b251aeb0fc39f5012d50076ac527db2a9a5a804308e9f94071a06652573d0930d08d5fba0fc597ab6f4133a0e92461c0601186b27e300d4bf4897deda915e85edae1035ff8127e210540e2d11d5d6e1a66d1cef6a95b17281ac71bbc3d45b227bd1ea5515dfdcf43c61d36cb42ec686177ebb61129230c9a49814d35e04530808850dc5be1f39501d7c31551c0c98f2db5affe2ce07b84634a99c5f00f33d4f6f47db05802276fefa8d80625523fce3be2ddc6eb2c207dc1e0580c54bc475c952dc7d4f45fd6255ec878e3825055464c5d5f542bb1bd46db9a99aecba7f4bfa289375f74e4e41e6167c4ee04b4ba6c9d3b0b50bb6abae4311044fe88a5514a7334e8f1b94bf18ce0abcf1eda78ba7669bdb9b1ea1690629c164cd0b3f6b1c9faf4726df5c9a7f8ab7e2b46ce1e264e4bee05a0d6c76c4979d328c97262d7df052e06eddc373f48eb3c94e3ea7d5f55cb4c6794ec5762acdfb6d3c981893335b147b18783f1fa2d0728a6cbac0a55b15eb189d50eab223e0ba3ffec60f0ea296483cdf8f03dad3eb1ef28d69bd14d939d02ce9d36e324f8d396be870b13b349644f1c05e9affebd40aa656a18d17b8359697dc4c79c432b2b862261b743701551ed88c5a37f91bb82e5494bc2d5aae14fb6186085dd206a0d1ed3ef6a0c7956dabd7006d293b5f2097b72611458d956022a163170c34196cb253985586332c91cb10933289db00269a9606b71d4f5cb2be3de048779b83a6d32c0c0b11d202d25066a285215d59d92c90e40d0f19448fa50a143a3f006a28c89931aeaa637bb43c6524b2b854e92d0fd51f3915c5dc41ff5c84b777615ceacec8a8d6bfd9416698714dc4824a3dce93db181637f34faccf3464e91275e045fdfc23f17d8ca845d3a605f33684afe2bdfe62cd0a8f98e84a3fe3e1b067695f6a042e19753af1a89c1b8e2866b87be775fb87c5b5a1191c3b04de32f4f23ec33730058e259eca350c3e45c5186f8a23641bf08d65c2106f3a2c2d926f99f7326cef97aa29d43dc70036a28d44ec7e74fb2c41c498f041acf52546ccb4b67d7403a430c9b07b399ff314c106ff4a90629fa4bc59612f1e2e837e0fe465f2ef61cb75751df5dbf812640c945f953e862f5c44a167f6c287e0b4f5e87c0b2482a57ab141ac9dc925bb96e1032a4559b96ea217a904c6f71c57c5867b77c65dc898db2d4980a3fb10bfc515aca7728bbde27900f819985fe34476c141da5e28781e5ca07da77e0fe40521fbb658b2dc0be222c59bac7cc6ab669845c01e3818979f1d5d58871f6bd9af6c0338bb6f256131549a7bdf0888047624ae948159f78604526cefe093b98a4db4de044f580511bebdb89579dbe49f88a55e1137a1ae680adfea2b7a5941743df7159f89f64c926bfc38912bd00494c4f9e9fbbb64af38dc260efa0529199dd0748774944f9a4cde52a61325e4a76565f11293aaeb0fcd78dd0a85fcc5bc649914183bb77d2d7812cc33c0444fd598a49cc7393d55b211934c1b775301d360a6177770932e2cce529a33490cb0874d83743830f899493a1add68edc654de0ca0326cdb0e92457f84471588b6ac07af09128aa300ddd02793007e0e1b2cfc5b041403a104e38ea327809811deb89a087a15ec8f28303ad6a837d7684df5ce9a0c99d88e5bcb0a4b076a92eaff44453e9821e2479c0d1ea44d1a4e6aca78991ce1db06276a86958c7d74875e082de81ec64212dca90df24794a2086b17353ef40f442ad1779d117a6c97c01e3542e32f0edc2864c6874f886afed1c19953e167b90a5f99bba7b5ef9817b641ea2f17e5726a6c4a588704fb48eafebe6590f2da24c5659bc9789df17bd99402316ed35b389cb64f26fb4b99c9ee6612aaa09d7d2c9a17795411076e35910707e0578dbe47ac086a803c123523ab0d61cb48a5c6f9219bf2496abf4f259662341ba72ce84eb955d670d55a6c583eae5ef947578112e23cf125e12b5e4d9ff62454faa27d8efdc93ac33bfbe8dc9a7a0da102e2fec79fdc53ee918dae56a9706506b5605f6e2a695bf6ef7989d3de94cd9fcb54c9597dfdbcbd652596bd9383146994f0dc685e1943ff89fbf2e84b36caaa42a56337ce3366aef26969e9c4d57859dc6f776af984feb503055532479a3c5965ea5f98a871a14f8b434155de025815fce907af4990ebf8a7947f012b6b5bf9f26ab8522bc4512e940a93ee405a21b3fe426f0ecddea96dabd2a24e5724c29eb5d8a6778f3e28008f08f9f30c995e2df14121599faec216b2ea8186a6e590046ee929519ff715a044dada1dcaa11e0d64cfdbde1ee7043f9ef0e31155e5f9943da319ece37ec4ea8f5a6aa257df035ae571ccb846ab7c3da5cc83db384df0db2c52c42872ad01cf8a341cde0a85fe84df1e2447940012b0aaaba045fba7f7b43f1ba6f991c47f2dbad4efc4aad980086871db16527d3527f999c40c03a4201c7501121917b19ce419f84e4464083eca618007626e07938ada6a54121cb5ce8e6a7c4495db2fa13636ae84ea0ffc66cd674c56a921a8157e9880e8a112b8e2463931b0b6fd8bcb7c4ce5c41630ea60661346a1a4f5b77ba7c5a78ba216f2658f366917a29938e3f912f6f19d52f83cac79d5c08c6e11781635ec2e10966d34bc091c6a28eb98a30aa1aeec99e7da40a98fc00d7dd23193c5e0d1b38437291563442ac43a4797b1be5e06493da831f59ff81cb643b88ae5c2a543f1f38a1e16be90130d67e81668456b5011f41070fc476c31cd02e1259c6617576e06cd9a4ca27a3331b885dbc6a60106694646583a1a872ab51b2d4b1c1e34b4e712c5420692651e1f523d34643867e4be56059b238bdcd20e21c0871d67af2554bb1eb50fa9805bb4e8c27abc2c48c6a4ba12204c45d394d0616abfd291c94682dcea560daa8347c1802b2c47e884b5fc6c1638f68b76a3e1dfb2b2ec5ba4ab319289d1ad92bbd2f48baf8ef609b8398659857c31128a4819c5aa66f448064e75ced79084f72f0e6cf1fedfc0d9b230e2130e6af7ea6e7f20aaa61091ea7bdc4d13ff9d07a94f0fa86e8b2c12af7b772dd182b25bb26f409d6d8ffcc34d3533e035c0ddcd649fe2e96d56c1db1a028de26e1aedc384833ddb7ce6e8e6acacf8b081d436a68cf6be761d31b11550e10d57cd9346299eba58c7393f82179d40619d9017c32749bc6169a6be2c96679c9df0a3dfe7b0b860ce749f80ba094a72824a84309e99fae29b11562114b13235ed32fb9f4ef189ec5406e26ae08a8be363eb464d3aee5b25428a887ce36f014230213cd8799dadc63b81dfc214e6edd830c425d427d393662c09976746b2981c265d0a16a84ddc28c4ba3b7b308ed440e39ccd29b92db94833fe4bacb3cf8f124fcb56defa4e8ec0689b255abe8685b424b11c57b52887ef8a75ad312bd399d640fbcf18a6b2b734cb30137a0497beada06c0620cda06729060ab15c90d600fa0243a33e418f5c77f1e9b73cbd73798463cdac7399b27bfeb1ea9347b979dde900e3a1d0bde76e6ff58bd0ee093b41bee6b9f3cbe478693d8568db14699622d9a52d395435b6da18d95bc520dd154d4f6cbc3c2934bbff7efa5152c41009f45962b88a9d6a93cb4271f94826ca03bc7fb5e11d6653cb20b3fed44de84606657356f0f89a8b2befbf4e20b34f2581006c07ce0a75bb794e07ca99638a390c897b33a82678feeb88af5eb7409ad1accc594d4f91b315456e217e9d9c54c8a385c6baa22df614da9de1c7c1535fc48a70c47263d5bc388fec29e4b51b3805dc2a09c031c51884be2afba06386ef4130fd9fcfa88bd8d86e5008bfb8ea2d4a26a2559041f988465dfe14d020e04a123223ba232f85760d2308eebf04346b411119b6e64124a64ddc0645cd8acd6b99d70c09587b2d445873256ac92ca3eb5434d8eb8fd0bfefff72919f9aea6b869b9ef9f16b8a991c5b60af64667df7976eeedf0fb57d1abf4cbed64cfbfe3c2f78c1549c909349fd83b9b06d19b46429040d2ffcfd260c559716adcdf7b9c4c2fa777e4e0b18484c417e611e482eca2b32349e7a4092e037a84ba1b2557f5d29515ecfe8036386aa4c0c5048c5c7b9d36d79598b98441a8cbcab320a3286cfbe7707f80fd6783a25294a43151054d387df22aa89a4a9c075b90fea908750e3503450dd123fc69a4fc1a5c4a880e9bface41267c88cff61a0e3dbace4126728e31f3b04747978b4f01f0cdd978b6bf489f1ec58fce26f497c97c9436b25c2108a2c4da12cfe4634c7d60a8ca1165b244de5f1aba902e8af5249a9086fcf4d1eca2f27d5300e12c9083ad7b62029d237090d0994e8dc1098726b80cb72a8fb114b8cfbac298380d63a41ef9e40356262f3df97cd0337bed56a2e71868cf7ca98f0bed4fe4aea065bf2734dfea85a9eba435c20465c638f1114bdd00bac93d543f929db09a24bb954a86fab7260451b69659f04025009f8b022d220ea07b09e24a37d14f616a9a4f12a1c5049c2fb5cfccf4f212cb6491c41c4859b010127c0380a165b0308df13421ac4f200230c7c04d7050ea830a1287597eee14ee8bd59d95a2f0f97efad560c6cd2a26661efcdf9a1e28f8a5894a07e92608821c7e7496f05ca14e06c7c843261b23c732314e20e79f98842ed3bded9b419ed381bf8c4b976f3842c48fe75b6142ba8c5c0fce33d3b428b18660b1ff828af4cc288605d7986e68d15134fe85eeabc4c3d09be579e0eed24beef8f8602168fe35f53d104f13558758cc17782425ccec7f8e7e9dec73210274ff4b7a888c348fdc5e107e4bf415b70f1e54d3e6d4e1c0dc8bb6f6dc6923ef3c40e2efd694e46bd75899a7e42873cb04b4c7cffffa9d090f3c9d7b118fdf3f61df569dd93ec4cd00238bed1855278ea154acfbd96f8776803399e4d1b1088c6a7d023500e58fceab1261fc34665effa240046bca4e77d9116445e161b2b8e007a9ed1455d5e51e7fc0c35496ccf1575d2587e837b3622f3a7c7f0ff674aa3e5c54d969bb3253a03f1c33a9b0b3b95f6a8c02559810ac394c4b0dc5f1dcdc5c9a7b2b5f3e57b432258e5b50d4f4c465676b3e7a151b8119fbfdf76b3068830d027b1b530bcb247b2f8f4a206e14bfb54a9c3e28b992a3c50c419f245723975c39f5011bf460965317bdacfa28966a3c4ace9e1f3820782114a7b5e9ea202e56fc55685cbb7acce3960081ac7c35414989a18b148bdadfca98f8b1f986a3c95675c549a17d5cc2252c0324444629334aa05e80f5c4ee641ebf95b814eb1460c500c34682d4ad279c1cec1f80fc5f94e91a45824729557519049ec21cf9c014fcfada74a843b637b38bfbd05a16b221af969f10724eea4844c6c246d33c089c3570905c99b9e7e93299ccaae4ccb9e53a94e238c813e20903acac47a6a097e36d67b18a5be85212c446d2edfe2662d80490c63c0f070cdaa752ea0b3285015fb245e04be38146f2720f506e3b0419f37a213490fd325b9b1fe81d2c7edb9e076ded7f6243415ecb10bd9d5fe5dfd3c956feb6b74b9e1f5e8802b0a08ac012a1ed241a72bec9b8ae2f6298b592adcc002c11436f2c56bc102985a07988859706b90ca5b6342511f8fcb296ec54e1907c8ff3b6b8c1981e39902ea520d9b9aa07edc428f13a3380ca7afa609fdae2b4a14dc760a49a68c2bfc965d156dd65727dad6826d72de8eeb486884296af63ec0925150fbe0cdc95e2162e5218c057a8fa5408c12313f66e292593f7cfff7866e7ead98bd2909eb2364de462be74b6c830bff4021c3f80aea93eb00438d033f969d5aba51432b13023e188a98fb2a519911c15f0e192ecad3882f8bc06217e3957d191b0e1b4eb578b9ae4453dbc7001d95c4244dc249e4ee5dde30ab2e1a0abf4492bf0868f1eaa8426aedc52902fdd9252d576a88a0fbed37efe23d3f23735ff61fcb9dbf3956efa443af262d8abafd5032780fed2fbfbd6c9eea8dd470d9ddf1e7b3bf6bbe0a646dbb7342d264a5332e05e56d61db969b12fed1431cb50afc68cad171485cec25c4cd84116c3cfc3ba7f9752e3f9850fb86d0333a16c54ce8a8ae2d4d08daac026787fedf473f431c2f3f476b83759b5c54141970416cb9728a9283387db216e390b3427c26e25351c13304f41adee1bf7d7f9a5b8d99ab27dacd113acc19d411000b88c50c61fd94283b80ed67cdf2df249366451d8afc7987c4b611c75a1c43ceca1dd49f3c6f37904a582d3c8cf03be63843df5297ba4c79cc38d0136da4bc427cec54f300838b841b8ead5fc07403276b34d1a31cc481558fd2a0a201d30572eac84c2916070bd8a85478fa86d4d08e8e2ec94b16d7c8023c013d2e576f3c3b1a685151528bb8d11080da02c0372d15cfbbc65459bcc20f59a093c96d823cfa93ec5d3320f61ddcd9fe8b9f3bb1ef2239caca74dc3c1bdd4e7655108e9b9a5ec6398ca81e80abeb354644068ea99a43004c73857236621f27e7000e4e545f0815103ffbf9654c7d90a69e1f437bfa4c6682493dde7a5ebf793c94a5a1ef896dc647e3d842c75f482f717abbd71fcb13b90f32569534d8f282f3a12d32d70bf6828c9b4f836e2158524e52c49ec3b6324b61a4db12c2dae8cab14fa73ce37997d07fbc28f55e23a675fc436882b92dd8e7226ed512d42fe8ad912e19f1004ff484d1209d3dc013dfc6b0466c35ef7df98e84f4d760292cf40525155e75eb6d72610afd30e618169c2671ec698d036d6a49d38aa3293854f638fb0af6f08aa2df5350fe14cf9396aa4917fce0207f38a840592d3ee8899527032ba1d147452f5879cfb0652601eccf8fa212f57bd8572e9cc04548e3e78f9feba71b259259302842a0fe28343cf927afb82fbaa1db7fad65d3e09b16c1ce8f62443dabb7b8fe8e5fbddf53e673a6e09eaa9460c97586bd1b4630fb42d3172624e3c0fe47019c8bc26cd03fdbb867c688b60e87b820b4eac6427a70ae8551cf2115451f886002a6877844cc101cb5867cfeb25787c1be75ef0da87f1109851de3d7749448f9d233b70b079a9d956693ba1539dd884907a52bbf6c18af0855d8cfa555a7df1eda1251932a31ef42595f6148db63000e5900c7cfc504dbdd95bbc280a286993b156ee66197b0aa2ddbb665188d3877b0be2b6c7dc391ec3962518da3c404b68e4e993a421288862564aaedb335a4e0d4de238b0d0d021d66a9fa516328d41618a4f41aa406148f59735dd7a42890f61ef5fbab91c1ae217dda01ad441510989cfc7adc6b4915453a74357123bc7c35b5d182253840192ced6c711c8c15e7b56a840e5d40c468ab4114473b38c1d1aadc9a4802b1f81837e3440e5672decabc19a17df22135317f9f8c09e3f6a4ff0a921028a643697cddba4d748211e8c96592c38916062379bd7b5322e495dc762400a8a8d818d7a2bbfb52aead4e3535a7760a55998aae4421cfd5497c62032ff545c5aa8d8aba612985f406ebdd8a31543e05ce8a8e630e4057a03186e5b2a89017f4eabd4f5e848aa83c312eae838c962a1e7e0434d9bc43e1708930f2a5686505a8ea0ff56ec3d87309782bf507cc57025a0bf66d5e30d0399b36fbc90598330a2813eda17b2cd61aedeb3f85387031d39af7b59f5d80732cd449536f24fbfb11f2389c7870904af58123461707880200203410e4604827b3e230b3d865be46f17ac0d028f2d4b977ab43ac9125462bbbbbbb779e07e1066906403748b202e6b886ba349473ce46d52673ce19a8d5ca25b888720a504495199884f0b090e080e5a6438f0eba70214aa18a0abb1f2596d0d50084d5c383188f196c489204103a254a14d1c1698ba089aa2b2a5d50a071c4eac895160e20a003054c7634e520c921861b1841e281e24ac81e81f4fadacffcdfe8995c88a21821830b3e5926247154431116d208227a00b19b1284d00e48b8b01b7a0f78eca9671d3f1246961bef041884aec0b024e4065725e9f8869dc739e71c0413b5c927a9a90b23881eb86327c82804211e947e24211464c7952147824c189a3a0c501ce71bf5eae64a81c40ec6187ff8c3b320164259b48f910f928f92cf924f13a8d47375c601cbe9e6c4e324e484a3a1b5d65ad3d859425103151d3e544574d06075c4902074c4f4d01fa0911232be58e3aa4dea9d1cbd63a726e79c791c519bbce191a479932a792469017ded67be7e8d1da236495474f43b46a6121a39b8f028a9728a322247dd41d11bb29aa0216946124ba87c48e2724281cf788c735b0b3ea67e100114c5c34d2ee2ee762acca81c989002d0f7e5686a01551f8acc9b1684f60f5e8cee47161d49b87a75a8728d9c1dac33cedfcd39e71894aa4d6aecc3673cc67905ba596dd9b163484806266e30114373891b4f2bc7508dc6f5c118635eb5496d430e893d9210494108a8188690210909fc44eeea972784b0d1a27aba375aad6d948b08b55a32871f285d74f8c03aa2a367a3e9efbdf7eec0519bcc69237eb30369f6540248b1f3b59ff91f40902e3abe0f4915a21c2d3da0f840e2260be1020ad9051bf4b08801a8060da52251928eac283159f5a04a461688c20061e9a173c2b2854a95254335f54ad3d557aebc2411ffdacf7c8fccd945e188ba5a5243851a5c7835828435900049d119e79b73ce50d526f5073e3defb45a39e7dcf338becf06c38ec49a56b7dbedc6944b2c2849ac09001c7f33ce39e79cab9e7ace97e39c735e8107861250b86c71f2c4d2122a4bfc008d94d0035df293fb98a7a5a7a627a83b93cdf8e1331ee31c07f5fcbcbdc105251e8355695dd1d9d139e79c7394a228475192a22489c2846b3ff33264e35e6b5bf2daa08131c6187fbd213476ec7c4dc642b728618cf18d0e7812b013f47d3f5f63b58810602203103f82340d614310447646176b8c31c6988810111c911c11232248449472cdccf5d15a6baded10143c84c4bdf7de9c546e2ad7cb55e5ae72597ae8c9074bcd71257b29c88255c291a826865801081b9830a247c617638c7196aa2fcbd5f7618cb18d5eadce10a23689cbcd309acd40fa5a8e50ee76bbe5d0425d2e19e5845268b5b0803c781c717908f1c0f1c8f130e28144021a563c1c783edc2069ea080e2f477ec86102e8410e273c61850769e787293a6868031ebe1a3b3b33fcd372408d1b4d9e7041a28cf8210987440e638cb1134cd4269fa4a63e184ef4be8fcb50c77807064518cd70840db8d4b0e506fbd003cbc118e30fd7087d1f0282e88a9f7cb176d292e46786291c4b64d91dc1a2e3fb14c063a733cedfcd3967aa6a931a5cc2e9169104908d5b0a999cda9cf7de7bbb0c519b242a3ab2bd4b92b54ce8fb32c5680ad214a5294b539ab48e7befbd3748ee04fdf0af28aa28ab282ca9dbfd1d3ee331cea54839a1621e0f5a28826aa714839193a01e9298d08465e2566d52efa31c1b2bc823a0824ce1312a62091e358878e8d85ae15600e2003b4534210429080a90149223aa72195f3c75f4a84deaa8d23aaef86c2be560a92f40559bd4435a723044647d5aad0a54936f909e9df37d2ff09cefcbc9dffdbeeffbbeeffbbecfc80e1be1612354d888153682855fb8615cc3232643079523965878c2470a5751476adc80ca2e280421c4d4a39b4149141884243185c87dd045880d4f3b90f0d8a107a585a7b9e4a7b5424ad44e091f4ea258411d55ad40afaca84df62b2ccdb172a8903c36885804fc1080c8f50100c6f8678510942055c448cf951c946e3094a31e4477fac0436d52e8c2f00177efa75320b18331c6180705d55c104aef42a9820185cf7428328431c6180785bd1e3a2e5e5f7cad6eb7dbcebdf7de7befbd513c514251b8a85c9451145294524dcfd9050e3a564ec0eaa18a0eba275156e10d514f04e1331ee39c85272d310861ca3144e5498a889754165c1056bcdbed5682b4eaf2869e9d55b7edb45a992a0917ae70e1e1049f1cb8e858219dab73ce5988276a9352533d184254fd68ad754bb7b44f2be71ca4e4de58bad17403eac6ee066fbb901383815c09619d0d82080af2ca2e00d1f9dacffccb84f47e58803288a4a8b440044815131d27dd901f2119638c31c63a45d4268f92927c373a4cdf877b723c4ca8831f3dec19070f1240010b152522a92641418cec0842e473f30307e486d60141010768435eb9f5d07060460e271e5c8448c20307255c9aa099052d4047434f922b2780e434f94c2c4520b54043951b98ecdc60271deb00a5d89139e8fb58cc9005dedd3c91c659ce39efba5c1a82eb39ef3ad852ea81e14410ba9da919a0704d8e5465242b20d5e49c3392aa8be48aafe0a187881454d8098a729399e8604a52365af86b3ff3af8444815eb05f83a5c87e244e09c9d6b1ca4076165433b758fdccd77ee6ff01365c209e562be79c6fe4661e2043cf52104c6c6c61bac14c2edce0a95b76ffba24204287179c78a2c7101a6ef212a03ce387cf788c7313acd0077456d728a35b97ebf5aa4fc54aab38ce3aa5e2b8ebc3a054e49be71d4e69e37e7981c1606062b1100794f8da4a63668c768d32fa8be3bc40beeb14075f0cd0e79c94ce0cb394d2b4a4998fb3fe5a20df3c74d9cae9e42e3d03c0032867460ad5e983548738f86d1883f6fa77cfc47aadda287e2975e812c4f3d6676869a6d66271eec429456bf46bd5664e511b7dba83fc803e9d559dfee8a2d3d9d35192867ea54d284953a3e8bce70d7e9682bf7439a1a48d7eeef26794b4c9c0fc7162995de14274fa52ce299dfef7b1d669cf80a4a10f83d6287c3e98779de27aeb6aa35fbec45dfeec13b9ae36fae19516650864566d577cd6b0b57a4bf94277f9e0b7adbda3ed35873855c58a262eb3fa0a6c1f271414082fddd22614add1bf527cfe38a1baa52d6ab5745a0af5f1a04c1461441320a434d93581fa195e1fbf26072813c0679f7d2ec941a5cbb70ee8b6a9db97da3ea72d00d0edd7d01672091226baedf65d4c2003f28594bb136ab7b485d8e94b143aa547af65c517e3185ef243c77726adb0f86c77bbd43189a14acd5c48268a2f9637909a3009b2e3315dea980cd11798087d8d098fa42d2653927c83906ebbd43129fde851823e38317d89d19a8c50af32970682931b59ddfe8cb6a09dfe8a24b9d30f6103d40605d2ed769b714375508e14d0e9e842c7e674d841c72574bbdd6e3743a40eca923ecaa74e6fae1802103d31841faec4107ec0a235cea53a9d353aed210041158a2076203c7f83692ba88d4edacc4adecc88b7c87ab3de8a33d369fee6c5a87ab59ae9f3c78bd5675ae3afb56a53ab30aa4d5daa9826ab8dde7c52f8dd5d5ca73f5f76e02e388c24bdfe0432c0d461960cf5fa16943214b0ddbeede30cbaa54949336f94eea958973c0663679d23d439a7a58dd6ced9edb6e27729a5fd499bdfeb4f1673f6fa521ce7ecb58212ffc839e3aff6ebbd3bb0e49c33fe6abf5e8c69f256ca02285f5c1b3342acdb4b6bad2fd2463f266d6aaa8d3e1d57f41a5aa33f7ba8b6734cd1e5377ea0d77fbca57c71b1ad745e6a55a8a999616e4998e95a3ef1faf847f82705065ebbd4e9a4faf842fc76a9d341a92c70da6b9208dd0d06e815003aa55eebcf6ac5b32e75ba5cdf5dea74434f6c4b70a9a16308914ecbcdd491bc49803ea7b479190069c33be813095b8c69e2cc0c13a8d01e8d4bc398f6347e106286b6fa7a50632a480c5a80e80194a344c4a774831b9fc2eee317f5b1d6278d3eba70d2421fe5d20cdd933eca9d9cea3574e2a3d78fcd6408f96124078a0d1d4d96b8a91f42b5915568d8e0e1874f4e0c4a6e6acd4e4ce8af2e754e74728f71f6c2f6399f4f3de78e1a28113d6188ab217afaac832fd9a2fee32217881c3234cb932732708b838eb7bad43db1413e3962adac5b6badad369e08a938f8ab4bdd131e98f6a484a018c5f1840655c2b7962bce89a0e232283a3a2a4f45a615bebbd43df998591cec52f784a34c4f3f7dea9e6edd5a6b676c466b5a382c89a90487994284a7d055225ccf40a1f0d88f1f294c273900f5c05174a973d24283f0982e754e55a8140fbbd43949f531d69d94f45a61d09cbbd439c5d0c12e754e387c308055ca179af9596bed6b1ab3d65a7b6fe8746b97605680ac0c5921b252f4f33ae39c73ce5aaaa84d5e5d185ab22e0c26747e1fc939add6117e216708c6187360020da0bab40588b733e531c6f887476df2874aff58f11facd9833d3c5e97371451c5a382badd6e4152ea76a0a8c09e1b357893a2a59aab6fe30c638cb18f2a1f573eb28a748a808a868a888a8aa82de7a50e837e3c6cadb5d61e10b981f669b586805008499d0a581ed0110109c9d1094961deac3ab6818e2f89e7938166f7de7b6900519ba431a46910f1d94eee927649764c1ff4960c1d11eb81a887a21e8e643f3d72c9d012a22118293d295552ae3e6729f1d8134c96cf788cf37bc3b6b3c294ed7a0cd1e307158e1fbabc80e4cab68050e4b09443530e503a35a210dd2b07638c3ffce12430e7bb1f0ca0929d03e46425cd63e23df1a47853bc1e04ae74eccc1c51603934df0d3f08ed5ca901882c2e4844386d2e069081e484548e23237c74663c8184d3015365e9581e9e1adcc4d324d48483d1949b7dce79c7c8e79cf3932b6a934fb2b40e3a5c07a0d9fb04a5a0ebe9aa787ec8f8628c31bea1065ec7584609af2a11a2875e21559bd44db81e03ec5478d25962d4a387129c50a2b365033c9484b050020d3a94d882c3b6b003e27373aa4e0ea8079b6412b404ee96038f9eae89197a605d2c4b3270408121ca094582f458016b07971582a414205c624713346e44546cc0040d296ce14036f57a8e0839f11375440b96203db5c850bd29426221a348871d26e8c822b4044b1622443b72f4421c4330c6d869ea73ea7d4e55338cf18e11b3044590b8da2129ca67882b1b94248829e829482a686abb90132364b1300e57eaee680b5811add167d16554a09a59758ad5e9a68a8935f78ab6f39c431c9dbf8f52985dc5db67e28692a5f826215a1b0af7c6e1c79aabb8d7110fdfc57ad4467f86382ddae5650db674d28b4629dd81348da479aecfd71fcccbae22d85d62ac2a6675859525045b3282e1aa4d092957043bd2b1adc5182fd68b55c5ac6257314338180e86043382e160447d84e17697312a98d9070260b7e616677f89b3871314658fed2a861ae9d6d4d46affb3d9a58d3aa906df30cfe55d2aab194fd2ecbd779524539dd6546059f17a555662ccbad6bd7cc156cfd22a8d06ba5aef0a71f6bbc218d5ca04352c065018490846eaad97214e6b05109807e98ed91f13e2749738fb8a5dc5579f899b576dbb5c91d1fd6abf1dc882058bcd79a7d1429c16c62fe5e31ffc3efe76155ffa1581a87850dc39153bcb85debbd6c76ff77d9aeadb7d3bcdd346fa4823e924ad7479ba8a4e72b1aed5e5adc0f6f1f254883290fe8738df7f18c37615cc2e1dbfb8ab15be228c31d33d9d9b6539dcd230da0b6da4bd9e8b16be6895b65bb46dc4a3d3c77b74739266fe67748b6e8feba3cfcfb451c642a72f23d4e7ab1065b581b61d8030068aae691a09576d3413b5dd1f5fffc8c1b7b40f87792e16be5d2af1f23eaaefe3a936b78a6aab6af35955db77a32d3e1fbe1cd9e29b92b57bd5e9d78fa7d36f67c5c5d9c30178c045d96d1dec0e429cfbb9c7001e6c2dee5cd111add1dfe246a2359a446df4a5eaad8ab3ce5485de2aa6257e3ba86a73a14088e959450efcdf4ec50e775b5abb429c664a9af957040243bb52d466566d1eec2aca74f0835d4515ad0b455bc468ad66573145bf221098b7345d6df3c103ec2aca3a98336eb5421c1811add1ef7b8c1f0be1f8fe2792809160b92fc4c14360d1165f4bbe909dfe072a605731d641172fc4b9bdd79669c2e5c3a0611843f78ff6ed240d7d992618aedabcbc60e91be799c184aa68d555a6559d3e7ebbaa03020a277c84e1f08f301c52c7b4512b55db7c185156db7897fa7cd006f6128ba9c2ab15598869b2dcfd6f275fec145757b21c8a1084988e6929ac2eeff6a80d0cfe70d5463f3765d443aa52ba5afa9a7a1f94ae92e56ed587ab36fb68277db99de4c36da60f693f7d38a9a54ea79a76ef83fa7ab96f476b29b691cedf948a1027d3be5dd6faeed6072260bbc4d9ad287badc265424c83e1b410b9f5f4bfdd7f53d406c6681f511b598eca72497cdc49669b69d3c6fdd4b4a52e6ddc53b2ddee7dbd4e1f94e5aa4dac8adae8c7ae8c62595c470948961b9235c51041f598a24e7f94ed98fa082b8201200441d6312d462573057fe0535987116144d4469f8310e7a3c1703bf0eff1e3bf1da4714933bfac19b0abe8ea570422f356e6af14b541f1f46544594e85a84151e7aa8d2ea236fa4b572e6a2e4f5fde2ca9d3239debe3d34f215eab6aa30f235eac6aa33f13b74cdc54d5e6f228d5869234f4b5b871b446df256e225aa39fc59da335faa1b88b9cf876148bb6d83bc87aab575f35eb63bdd57abb19f571d6e9a310bf5db5d18f113f5eb5d18f891f55b5d167118230ebbb2502e1f91b98d79baab7f6d3df5757be1ddd08d9800f373fa02f5f7c4f5f56446d609ebe2c07237eb410e7d2be1dee6d2ada423ffd5d455b804f7f5bd116f7e96b24cd932fead3d749d4661f511bfda9a95eafaa8a5e5dd12c9ad5e973108210d36d4e6ce5d7a2cdf5f9a0f85cd2cccfe2165b1fee05e6c36d028438fa0910c6b0fd7b018431623a48db46b4465f450e3e7ebb4e2f8eaad30709108a40fbf758ecdffd962825cdd8c2f579eb230cd7e97fe215b528ca72e3d7e753b04f59aed3071bb0abf839605751830fd855cc1d8b56efbea57cb182bf5cb07083ad2f85cef85a1a0f2250e97cf9a0e6000848800256b85a5dc527e385b2200782f415eef8fcf972f1f0f9f4a7fc1a23ec52d2485a6b13d789cf9fa135f9b546f1f955be94d2865b461e6e994d2b9db8758d327aad342969e4c3a0b40e593b27c5593bec90b5ce92b3d08b679d2f37ab56cd829c7376bdadcd7b089ec2a9fe5a1c9c2e586a2dfd682d8d31801ab1104e49c04896d6eee5b11027f770bead706a91248dd419efcfda7bf1bd17e3efcb3bcf40adb30ea7b45136f53975cee195f64d4941ed423b40daeda22e853166b792663ef8ed6fca39c73df7ae1bb4a0c64fdf8ad4524b69b4525a69a5b4660d6a3dc6ec1e49905616dc3b0c37155da074bd5e9c8a2ff28e327a18baf63ee2a394eaf3c11068ed8952dd026b3597122a64c24707f4a9f7ebb579af7ff951f74f04dc796881dca54b97acdea2018179575883d9cd10fd148714d0ff7af0e5f5126f7f79e997d1c110873fd5ff22ce1084dbf5bf6e07ad7e4b6d60afb97e7912669d87382fb4eff9e6a27ec1fbe52582a09f3fc8451026eac0dec6dfc06843f4abe0ef7a300c715cdbe5e2b413f4913f09b3ee7a189487a22e755159355deaa29e3a58371765bfe1188a805faf459728a37f76fd305146d3f60d8ec2377f71d7a74516cc1e3ef13eba9e84dd350eff57e812ad4bbabaa481e3df9c8638faf5c6af1f631d8fb3d80adae2f5bfc55146df210ee734991064e71f43764e9379f911682b9a701911f86bfefa5fbef532fa27bac4b105ddf5af17918ba3ee2f317c19ba5ea28cee1249d83da46d9ff901f89f38da0e6e51d2dae3972148b3adcfff7dfad3b48c6923d46cfa3aa2524c75cef90329bd2048c54d0bc57bf7f7fdfec0dcca9fcef5e9df5aeb1050ceccb0f091f7dfcf6b0dc59cf05d7b87ae300c7f8b2d5ab877185a9d979d3240903653c24a10c817b286d6ee7d70042035a8b919f25bccd91f3e9882115e503b1019ec971d7c1834146720e366ff278e32fa97814ddb6fc5164d04c59106b70eb65e861a90371b14f36f3147c66d2927c4a34363c6eda6868c9b9638ea9e3f033cad5d1cda0c715634e1af6e69b2a986135d8622bcfa47ab1115093e72f99c7efd0e08c004a82b2a5540f8f06143065c4b06167ae019b8880a555a6675390269939452b9a435e7b3d97fad56835dc8a58e23d047c0339d4629a5985e6c71c518637c29add4526a7390b4b106595deaf4faf5083ebf923075baaca26d91bf56ad6fcef8bbb4b471d6abb4c9af592fe330d2466a993e33d20a2e834f28030a332c9ff118e7980b2843cf0cce79eceb52e7e4471ff95f1f506ac9a5dd3222e0b7faaf2e0a5597ba28bbde4197ba28475def24fdbbd4451175d0ee1cde9d3716ed0c2f20bbbd17901dd3b00232f45bf942f63bc318f82da64fefeb797fa6cb88403bfd17986094d169c64f47c00f5a1104d0af38d3e54cb52580ba28a0fed2a52e0a56072568f70bfda37d2cedec327f8fa59ce95794561440cfb4da12c7ab1fb4e27d1b82407f62e0beb5bb4bf956c49fc5f15260a6e399d72f2d8d083ec28f9acf3029fd39e79cd58a24d4fa9c3ac4a1d64e7b5dd20a986df0491b6393d25aadbd17e39cb5feb49ca2fc96fc560b04f70e2595526e9c977cf0c55fe69c72665f603018f9b1584ccc8cb92e0ec2c4260e288bc9643218984cf6c26532d94be60a65b20db63e9d65f8da017c59548af6bafccaa3523ba949ebb517676db5ae746a89b3fcf4d702b7ddbbd2b95be00e5dd6e5aa74baacdc3294af5bb35daea42eff8554d4a514476ad4e5bfd0229885c12a9d309aa335f958575487deaeb0aeac9a98a4a0989a989666516e0e4d217ad5e557abdaa3eaf26f0f040aa0e2289914e0a286d6e44f4a6bb5f6628b71a5134f5aabb5f7e26c73ae74667b31ce797e2ddb6a553a5b597f5fab05823bb46158e90ce79ee19c2f6e39af74f2d0f57a71fe12c69813066361602a9d30b02d652c06058a2d65333b9b553a672f7b8aa385911f0b63ccce696d8a31618cd9e7833128a4ccca64954e9976c950a0f6f9b38f33e89396718c76c95060f7f9ba8f2ce893e6410b20285fd50a970fbea8b55fe79c73ce491bedd3f954565a5de3089d42d1da9df406ca99518504a7fe1ea4b8c7ad7df0f21be3a0c4398c61bb0e71c007b5c6693df87d21ced76ab540704e2e69429cfde0b67d8738af07c310277cd0e56a2d615788c31f7cb91ee4bb15ea075f2495d54ad885813374bdf84b0a97383b98c5d9412dce0e82e2ec604b9c1db4e2ece01667075fe2ec6028ce0e82776f7176d9c1bb5fe2ec7c5721f87cf02554016803af4b6f81fcd96548815997b4a21a223ca4b1c6fa66517dd5e5df2a6aa3b151b551f1f22f956caa4db52275f9d5a8da502aa5ba546d605ece5e7ecd551bd94b27648dcaaf38640b8a456bf425d0aceaf02cf9b48adad4db5721f9e27bf97447a7288ff628156db15f56239d21f76dfccd10fbb2dac46c2e54b54df9e2f5b2f52e51a21063aa08a3bf1c59fb6ef4c38d1a877b598ea7262925596a6dd706f96e89b283303b667fbb8ab090dfded1e5c17ed42f3f7297ae197553eb566ddff751812a24b8ab28556cfced5dc5f9cd8ec35d450a2382e13e193dfb156577ed2a5618110d971aa894cb9aec61d221743302000000c31700002810088724511ea969c87c14800c5078505e4a34508c486271300e83208642100461180641108481208002294792bb0150f8ea20842f70778afa1dfc6306e1226b6871b0b545448aff8f8bee024bba1249cc327f3c2d28f6cb4c368208f26d5bc7a3ac3e1669607f6ce913a3291fce8ebe4e087a9db5405a665132ae1c885cc826c5175bbd2249647682bf0e2b9998ce0c8a1dc63aad6d1bb4dcd39c28e8706b9f99812f6b2029738240d74bfeb815d68fd3b58db14ca91b193215951df2dc38e8aeccf88ae32eb50871736591fe1dc3f3c919be4aadcfafcd03b3b0bb6c0225fd9434481b2e345baa98405bedc09ebdcfe63cad8378cdbd0b6370698f1b698accd0bc50971c0f80346aa5a683b3ff0cebf0d2478d6db2cdfa9fa8b4ed68c5c8f78dfe3453e32773ed0b6318540a114f01e5d4349d8032f9cbe92f747b5465b454debc7ff49a65b90ef2f2fda2cb24b7f1768fd3c7a4396ec890d4afa0f444f877ec86c5e5ad172afbcc0fa3c4846931bbee2da38903e985726384ede914c08026166c4caf63980123373106155e3b71f38f5e6ca61a7e38347336a49eca9c000982702ee352659bd95beb3020938b5014070e9e291f5d51f1d8ab74ac5a0486d70388f2924e03f843c985e69502eafa99ec8fd7bd12a312b83a73024ff7cf77dca966ba179989c44e1f562784de86d60bc6cb78d6ba3afbc879bbd805f4953b6da158415c200545237ea84065b224141a2332cb40092d37220f20ee004f77b2610ab86649cc74628fa65304d6ed67179d39b6f74a54b37de36b5eec2cdde1704dfd4a604409b655eadf0625fb39f83edcee00a25ea5cb9fa3802c831bb478dc8561828fecc0f95acd69543c71be9dcb2be096e3559c1be195204ddce4be6e8780b36424d36ebfdd180e1a9739cc0f3d6d9f6b09173c69b9d2ccf4ce6c4fc41aa44c0480881149e3a2ef523f835d88b3ce54948c822329ac147ef49eed35e04478be88641b20f18a7bf9c4a4034a8a7985a8f3c0ec5a734ea423839ed389e79c4e2a40bebd62638da5737a53620bbcb2ecb22a750ecda138303e0fda3552b558879551dc65d3a25723459a678e30d7b22b754e510b378bc87b1a4715c0ce14f5c7587303669b6792335001e58bb5fb3f111a97c341c521b4699dc8102e28a650423f6d0d8d8fb0265e7509219192bd903019008d01c5ced7f468ab3c32e21d527d51109d0966d04cc31352ae097e93f6986ac09349b14fa474f4d458f581d099428a3aee32c51e43643e086df5dea5ff23cea41acd5cf92cc4917619e2e59ca25c76804b0de95ea127ca518be8d5c9d8b9d8317815d894a2397c827147ca5aa0f7d7f0a5001c2dc5e870ba4401b58095cb05106794aaa1b37dc0512a2326896930f6cc7d95725bb815cd25b727b0d2036ae5c135e2ef4389082bb77e481682ffc384613d34c123ea9a9e32aa26091b56829573b8cc68f1e7c8e042593befe42dc5a28b243c5236390b6d514936cae1f8a4255afb9f07d9e65927d7e516e57f0247666dfcea6dc703989e498b29e0509f7ec2e53e3eb5a043a919f5550a81b5793f785e2c7c93871dfe89f673d97d16f20293e60d00a1920a278e7dc9add61869234c3f38b555741d494229555d50fe73c9591fa4feb398f7066f349a5c686e3415b01d6de006e6621208f36de4a46cc3c0f780b980393efb792f5d9da957ffab0cba9001c35b8998344c74906298849311800559daedebb8e2f5be996779db89dd3b17a32f8d400ad7e27c43a3b79d4c100ac834f27e39d0a907bb891f618ac8f30856d95b3386e06de7ae442cacfe0b1dcc31b3d884d49f1bca167955c019a025c09b432fac04f6c77a0e0ba90c32590375fba06140359a68bcf33d79fdfac02de0445a5f4c491d7c001e3292334eaad03311eb4dc514023284437f32a1b85e11c7cee8fefa2ad85b6e09519ac719371b9c81def078e1ae4a26eed5ab0cdf24c2d2652c3672d2e7a2d789081ff9d386ad20ccb4737768c6049a2198eb913d7a6fce4ee583f06faa609f239de1746a5e7b6eaf38f231de498d5b12ff4f2a6066af4a7cc5dc7a1222ba221b7b90d1be79ec658ec616840ae164c3941865fbf41e54515b7cd289db3618ccdd3f9d4c987ced1c929f630ebb743e05011da61801ad561663c19d9fc9a9866b2f1d7f2acfa046dda5ddcefd1aaded7c83481620581be9decf72e79ba6e15516237ca9ece7075595f32dc6ec4c170dfeb6671f212d6d760cbb61f3448db11b99c232bb26df7ba2f6e9904ab8d5e4eb89874db3b246e51f89f76c9cf99f2c700ecd24fb183016c639d2287ed2b146244f6c47e5c601604be0b4e3210ccc90d7338f08eda9aa9baead251e8c598a65dc3a582d025b5451bc326316da97fda3b284db028f8bbec37a78e67ae947341fc963ec3f2228e43d2bf5a4dd77ceae29e0cc664a3dfb8acf04c2982622200eb07aa3c0affb1fd5584fd45a7206124be295b3fa10be883a87a96ac085497a5559c388ab6c4a9841213407d91a62bc5a8147b69a77b96c778b654f6f826c8c8c8548e683c16e991daf40df615cade8582ac73ec6393530a1932a5bac6a50d78a636ad9707f201d678d4815eb3f62c6c4107505328cb23bbbff5f4b0ba6c805f093780dd10b62b2449a45d99cc222eb0ab7245bd3561ed0ea56f1ef099897aa69c0f86dad6f639ed0d7a56240be5e1f60eb8d73ab1c8e2ce4a06c30dba9c3dfae37b1026364c51833addb02ee160acdcd05cb122ce7611f051624470694d91f1958e2c2e80fff37059da683888d5a21e29c058473ced5138b40fa8a3c20e1574b288e44adf685ea801c50f66fc9ecab049f84484015fd08614365551235793e600b61bf44a88d70aeed83d9948341a02fab3cbfec6bfb7780bb2a7941985e101929309f19008a1146a97bea0b90954a3c8d7234daec45712ed7ff1dc72e3f5eff65d8d2260994b5f6a7f48bf7d0dc4522604e7d623ce71f4b316a932e19291371967b7e01e4cff36126ea8a9f3e1999c5f24d5433f35c69b51be2dea26e68650950eb2b8d583ecf22e017659f287b8e7b53b6f4785aa3d56c03d80809c506c2684ff3d80a324f4ac3edfb7f14e47511aacc878be3a82962cd027b8e4058713204a0a1989ab3c4250fe91e645ef2819c9322a0087ad02d0b87db631f3c94ae6805102c495ea90298527b121688d1c58d50bd45791de8b4631ecc314ff162a8e6ee59bf1f4deb899f2f78a4c843bf8598ac002160ecf1b7ccb584159564268b8ee327dfa941c40b670e71c46e4e955a415508550dcec4da093aac9e2318c5d0f6a70809e383a51c8bf34ee8b9961b50c7325813f9e72c7cbd6c804f0e5c8e2ae9369216966492f0e2c793557848293f573a2fd1c008a2d8d7fc1193fbc6f49b74447586eb3c893b48f589caf9447e9382ac7ce7be0ee7e95f7c0d1f04c18e60662bf71db1cf9d39bf2c21c41987b207029ac93959c387fad74588efdd19ec577825004502768f487c88359a2cc2e19a46ae3c05de5da1e21ed9f4861406fdf0b5e9ee51b35dac7f3a5f9abe4d2f384365228df5b02e9b076fceacfe9ee1ef4729f9aa58b90f41bab64a60fdd16a17db699e3c3a7f88c2319738994a7ba73861af5423a48d197925a3c4b6709d9607f3fb2b1c853151b5defaf00b446588af5a1b15abb9c403ddc67efff8da71b872e47491afca0811e2812649536138db7390cd0a6e57ec79127ab7c0f5b4bae986d78153027b42eb4ba55260583a1ec6e7f02cfa548d488700b3a9598b72fa23215d3a95ecdc4dd712e30d9d4b65042b8eca4695853e82ba5336567df99e972e5cb6cebfb31010631d83c816b4d65f595baa2dc4a585db5ac8ce9d7ae0b668e79b50c8b50467f7df09ad3a72a63a1443a3549f3afd69f25794829b496a77a6611fa7a664e89f6812dcf17bb95e66b0546e454c07debe05a0495502e16e7e60b71dff36ab4ff7b5fd8ef1b1962fd5b7f0e6c42371e73a7693c7851c3c8301035b3c80856011813f434f9619f7f013658cfdcaddaa7070ef0b34654cdb63510eb2401437d23e49bd1766b533a8ac02d330419c847eb3a6d9610d1e77d84cbd16167663a3508e3fed8990f55f0a9873da59884d55c8ae5081ed411f93e4dbb000c257603389bce453907d5eb0631acd4fb0d297f538352e13396de6a4fcf8d1e393dccae2c32f27fc5df243bf386bf70cd8471efe8e7fd20aa5dbce51185ab8fe20924e548492275ece4c9e70265432f42dbd776a534a42fabded539ad629d079b43876fe5af177109609d50c78ea637217a90795bf6e03845ccf71325c154ac06032863130a50d7dc30262e25bc595eeb22bb5680a5308a891eb7bf06cbfc9289c130607be5ce967ff76830636f2ac3ca1352b7be7e177c311d009cda32cce9f51d4fb8ec33ed5ef2f0f7ce1d323074363eb35cf53f69973d4abaf00eca4d31e14134d86c0e97c8558e3bbe0b87699b7a68d87f20eb499abc9216ed4cea1170d267bc2112e84ace5465c6c47ad1f41025c6cd58200bba03b92902708a1101b116b6265567002bcb7bd7773435445545a7a0b1cf2b7fe1f332c78c08f876ba8a3f5e60499d21d43ab947a389c4bf44a04cd7c7f0bafe6e1edab371a3378a488030e4f4eec72b0aa40b9dfa192a0b0cfc88ec061ded11b0dc6b354f075875e9a79a99c501175f2a79ab2e15b4cdf0713c1e24d2149dea9e2f169cbefebce83b94e0f15fc4da34597f9f618ce038b9bd49273bbc43e861b91274c11139650465a427c5c6045d0dc85a46637880badda3e333b32affed1a9f276f7c4faba6d863b5a3de055e3b0a09a3023033d72e5e482886bba82a2b1484bd854a67a3fcf914e210f10b9824c820f6b27ed2911fc06f12e9449f6d6ccde4763f6e7c8da67db9e68fc2c6dd141edfcbc94a1e276ae68c830dac5d80d113965019b0a9a234cdd785ed6991c02931369d2adcc217f5190e57afe2c515a76db4b09f9906f4af046fcedde2cbaa338c3159239da2657962ee5ee3ef171a65dc62dc032b8b6cbc85475745f771e7a93e5a9bb00d48ccaa7c3b85fd6ec408f3a392634aac844d2c96eae6a1dc1adcab1314beba2f756462b26e794e8ba8d7638b1e636a781e6a006687c59c6b03f994ce68e6c64f5e9d1fd8bf5681601142e64215d0f684b253c1b350499ac6c63a38bdabb9635644d134151a609fb0dce3900a339ebc0c448d15ee3ad989369e630a7ebed34d4d1f5663ecf12a86f9cc74bbc44fb48050a848e09e0108cce411cdac9c04aee1f2442e68ae771a145a568126061ac968928ab014e77138017b4ce9a9c866bbd0328f0625e0923fc4cbd0e5d56cd90842c539610fdd286ecc4a01b073aaa39ee673ceea178fb19dcae2c6e29058b568891875418b8e2a8cce45ada98785022f9e1c994b83bc0a41893555b7eeb72e6b2d448700f1aa8e1ac3e1d87217087903bc4fb8b3f62f3855113eb6f21938c987e0330f603e53cae8311335af9712221e1fbf63b7362eda61b31927dc6e31913bd712ef5e27fdf18c57515c5aa153450a6cf0535da87f81951cf6c85d094065ae8bcb2f53e3f61983b6192abf07a80854ffd20c177dea386c64b04e637743a0ff31268d597fea63d051b579cdaed4ad1b1399d5d046d418041bdef92a99fe92becef8ae68e9aff7926d5c9d6fa9b4b8dada5613193dd71468645482b1ca9affae07a24af17f7f1bc69f6c2c189daa184686c34a5861c3b127063e00aef58a8f531388ea217465483b6c64c13f4fa2aa913ed3d0683b416f8c1274cb3620fab86ed3c771afc1ebc5b0e5472fed6b0b331101a4282fc904d6199b32e8461f084c8c619ecbdf0c00125c9a83e7e1de0de84e04ea1ed12dd56f84e95acc0c6dc56a80970de8131e40e840086e008c8a30479a0a5531428d240eb306619385984d2da7ce16e9b362dfb811efc1e800650c4f239339ace6685e4b99864c6ed6d1f0b9e439168c46ddaeadbc6f880721aefd806b5abfa06fc0e2a3fdac99cef0911b7fbba110059dda3d330d5338d0bdb52ede4ec85efbaec72976215526dafd420cc79c2f44302e33ac3a2e2bd1a13ea9c3e598a87d92b9d4245b6ec0bd6d74fba29292ca5bce83fd8556ffce1c48c324f88460e2a663de5938105c7470a27db0a5326acb13f2393893e6adc344169a7c7ba212d8eac1c9ad866bb0dbbbb1912791cb7512611770ddab0e7375c1834c0fab95e1a866fdaca018d65ebada3303663e3307e6da60f1b5f3aa79107e5f28ef3aaffb461c045e4edc4652ee546a122611b79921e5127986a5b0f1939c0d2569346de1a0637d7c2963271d739aa7c4806cb48739127dbae2dc04d885dbefddab5c4a156c16a76020e6825db92e6b3f8c6125fe8088b82b3049995d3ed5cd665a8b8d71aa42f2c096dd9319e006d129185fad6ef8a1b84f80e6d497a371013303a04bf19dbc705cdeed16953f876c5a6709416446ae42c5aa0063687135436930511c0c266bf363e74b9ac31090b54e83ab8a851f4488c19f0cf626fe767d5a8f71479b2304666f7f2afc1339d5f0f8a50c9d1bb55f991dc6510bae5760a670eb126c51e69e1b065a44631f1631288c1dbb6155e6c286f7fc4960929e4ad78a462ee8c63536a811ad8559c8ae3d223843dc009f7ea160ca9d944dc54685c580c20afbd9d7e950eb4310b69b99b5e05b92a847070138fc860d4266c9380079acb7f836e7475491992b6937ed1ba8944aaa2c3195fc75f4a3b05edc022c75ed41953b026045385c7e264920b657fadf37ae19395b20663921e5a01345f3ca00410fb8d2b8dc85974458a99160067cb0584863702aafd0bb8840101faf5e7ec430430cbae8ba2b0450db665db8e366930b577dfd7dd50dd96ef6317a18155bc2c9b9865bb14756fb3155ace4b26731d35cbd2c1d0c574751255645970c83e8b0f3fc55c53c37a39e32950130ab5b7236151f9cee457e9e49655d398b16070691b864215e87516d682cafbaf547dba0e7b4d4cc7caa053eb4b11ddc5b209059c85eb2640decabfc48f1c6a6529469597afbac30476815bb7c2f39d6acf8cc9df3a2c21f2cac0a4c68b00a66b42207edfcc050928374820a8b81b3683b811bf96cb84e7c98a13aeb548c8e19a0fb4097065c2ab613cf7b2e701397115b2c893d4770325ee0a32d43bc62a8481200fb18cbebc21d0f00f5cc5888e0cf5f8bd5e96ccaf3f376da62102435650e01653b6b380dc0fa042730acd00f2264cbe367cae190b35637aca81db547dd3478674a615de81c52e423347a158a21d0ad652ad4ff9a465ff42255989d2898e80d9dc6fb35526e0833f25b0642017c3d8e1c1c7cb1c9e07d7a9736e4adc83b612fd5de2d92d9c78c8ce4c4e8f0f4392561b2299920f313af1011d655906fba9f27e767668c69e1f0acd1d9936524f5c9a269a466b797b8701cfc63e9fe7e9c4bf4b74f7ebe544ec6f6defe1e04a10f68b87550d4b95e299f7a11e592e17262e448fbe48aa0aba3099e33c9996e1d973f594f368c267b8a524d12b221758076977c5815b9d5f780abd6e664f8c8c85b6201c42ac4e664fe5a92e49b85c3309deb6b04f1e6576598f2cfeee4e0cf1ce45127af4fe7df3f23cc92a1eaef047a994e3fc96bc5655e239779fb54a9f72e680174fb2824e90857d1a23e20f320b414986f30b1ca0d3db1b2dc47569fe6f321578ee91027c037c68d08f76d291c36bb01c3041029c72d51435a7504524a773d3bda7c2fda1990107416d08aec14f6189c700f882e77c44e7c3d55dd91167d4430e0ef05edccefa4e79aff988da656335d17c58adb9dbfa288a8a8f1a0e914a7a890c714057cfe0cd8289ea87dcbee9176ac40b974cff6695f06c7927a0d604b8b3db307ac26f1f24cb750f4dec0283d3ce15827eda7e8781bf7a4a248e7773ae025898d2083aa8919acf0481f3782411c482a7502c3a3d9aa102d8cef5ff3f7342329f944a43f8bb0790bf28c813f12fc4a8d7277e1a043b8082292c90673fb478558ec8c07f9df84ff72131b69306af87651c2ce08b1dd127f20cedc0f8fffacc45b57464ed6870cc230848be3e848ac05e7e66fe238a236dc6316fcc99096494956b09617a2d6f7a3486b5387b67a3155aa1a77612355e172d05fb19b7b3233a3eef7c6dba8308e2f2d190e9c6e50b7051d43478d94d89eccbc9324226988ce5023cddc1e98cc0a144196f5f414ea09256425aaf84726a0997e08cce1c3731435237b6f7328558714fdb6c34cae11594053797647e1071dfe409ac722e17748b67f6e4101d187f88202a023cf71ace27002dc19510d819926452db70f703a154a5ae322a493d1fbed43cc599c57126b88d9a7b89957739bbb63d788698b120e8583151688644b308f957f33740b31320a630b87495400753ef0f778c9e0a8913ce4107f73d49dece1c1ed8e28122b950f946871cb943afdc9cfcf9bb8022702c366553995a69bfbe5ed5a33fce6ce43df6351f64848d38994b668a965065835d19c45890ab7f15d62ef22f8bbc34b6ddff184b49bfe73fb829f8650a890935a4e8c85d4843022cc54017df00794839f1ce8d5b91a38262f02fccddd130b9290001f0426f756c6698ba5588dded127d9ba5296acce409eeae2e8402abe458f5404bef9f565dad4dfa77039cb6492bf6ea0936ee7d3360449c6a2dd5702ad3e9d69ed58b4d07f10aa8a3229a9b10722ea5347412f633ca46199965248d1e7a30ae053c255ea7f8b21c82962b3ca060b2bd28c412ad3b2eff727ee282d2332d7061bdbfa1ba36e18e3fc883b42158999bd1353783e03c337ed2638a0772d05e0d1f4c4a78704039145139097a22f9c046c0098ef80d16372c7f98ac5586096934bd3855a792a5ca97be72fc4ef4a966c8aaa5dcd62c6a06e17ed472431419ed8bae4a0c75e93978d32a7789e3cc42d8e0ac55bf09ff61ae685fec59bdbbb796e29a3db1a3f07bdd4d8dd3dbcc0de2657a97e5eb465b6e1f603405a0698e1045166bddc58ce1645196670c641099ac1806dd4ddaf28c31e14eef0a73e05c30672e2c45daa7c3b0f0d57da74b5ccecb96c3bb6c7b65be37e11417c108292599482429ee29f093762f700f3a1a9965b32a89a2cfa873d2a208d78437eded875d809d0708c2cab6f6bb018e085175b3ddfd000e5591228fbcbea9d3b5813448603ca9d5c66a1327cc3502ea10432cf09d8c06f3428e55decd4de6be2d0b17ae022abcde2858dc18d7ae6eb6c5d4a6c5659ddac128f891209eeceac101ed5b4fbfd563f03cfc67d49186a9dc36414a71f9d190acc21bf2faceb027dca918e987071aa485f2fa393d0de23b44980a32ad6d6cce2e34e92ec419a89485eafbb3b23bad433d2f0889d561397fb1e562d46fed8d095ee65295a79ac3319fd70f0a11ccfc3b7c2a5fb905c93df2e094acc6c61fb3097d17311e9ebce6c653748d8d64c0e3bf7fceec4124b09b59056e33febf27dc52883e72519df1d5fd817fa8d883a7d7c570e2d21368471082aa86fad72d847a61f0e639099e4136c92af29974a5674e561e040330ac32b8eeafa736476cd6f1be888dba909b804b920131f8bd34a2a4bd57d67c6f4deb09a60ef1c65bae80ba44943f8d98b00c2d842bfbea2d7cd1325200e61028164f4e4a8ca4866f712563caefc76cc705c042f4810d61a9e5a17e94813ef904859f42dc75ef017bcff1a79fe82715ed66ca00d56f4581014a5d8ebcd54cc9cb043cc8d80416cc3257d2aa6283d52e5f39c4a81b283b75eccf943d272c39b07e402c8f2d5281b1e7d73791cf545543edc9085485e9ad37775e2bfb10fc22f587442de1e664059f2a8610dad9517e31c730fdfac5d22ed77f309d8707b68f2050de9626c541fa48e58f3c928cdedc3ec67e9f947cd58fa170ca9809c97251265a4059592ff4c50ae07d58a7e8ccf0b75e428ae55a016d262c1948c7029643a25ace5604d09d174f0caf047dbdadb8a9de9c305e6c7efddb7518489c32243c0b092a743fc81e65f9ab1891b7e2752e4c1200b36a8fe8b9e7ce6673d69c4c95967d67deaf04a2b73e1997c16fc7d0cea2aa72ff2a5b3212ea1acfd1dabdfd2bc7af0e978c6481fa35fb2d0d4a46d6d65d77a8129de8f65403c3f6e347f04e5bdf46979828322eab7068642fe78deb72db93c4b306bf7b127d8d475aaadd84653f7416908424104781781470528474483f82ae05234511b58757dd5e409d6f8a6a02282b4c41cd5671531d3bec6bf09bb6539c6104b1138cc1e24e21b3e93d0b384d24db191448fe2d278e2ca9807d90a7a20636b982dd50f3e8de414e0a9074599551b680529441e6c8e2ca6d60e8f7251689f0744f3d038d9f504961243f4112602017bbbe62c72192538262f3afb0d893b43426a27f53ffd910388072e603ab51082d5f76552e886a5e36709273c60256cd1c63ce1001e0002dcf2c3693fba193aad6b6e179a4100c902c7c86632b0117c44deef804a02e367d2aabb57f108b7fa45772670e2432b9ba5cb9cbc79db8e8239a81b069c6c8d9a3995b720995f6012bbc410abb17e8be30ead0e9b2117d88ce95434022047f67c11e0275a3e68f8ff41f7dabbd163a2bb4121d2d376f5217d0a61c168bc60136fbe6ebc1b93ee023b668702b8910c11d6ba36f521e2ad3de591273b7e588e4b4cad9d3a4042d95fda0af74b2f3af5fe964a3805670cd8ab7e295378617eae22de52195f54481d7637e0cb3abea34cf0e3142d0da865972892f50d726cdf1690420b8e96426d8ecea1c335516f99ae09ed413389258615d74b40753c640c35048387820959f28a0635b7caf396af4c31fe6441eff48954b8629270754cfe91446f5b55c44509f6ab9746254813fee5fcfe869b1b119c7a6e1965147db07217c42103afc0625509fa3d8cc38516be3882884394d8d311cc3c6944407e55129d9bc959ddf67341d3f18b5651529ffd68649535b059a5d4e925c21140f0800ee628bd45cffc352239d9119c24127faa0d6383022de75924ee4f51428416d59877265c7f0e7497c487df269c7300b7ed74b5cb22f6f9200c7497e270a9814d10939b20fed85b41d02ec246af47eec8463a5d7002a8f2201d695221480ce59e0951054f07dff1566274a587aad0ac6e0aaa682c7b18f131c35019e269026dda3e1570dc502a6c79109981d27d28092512cfc91e9a07de5f0f3a9aed1bde9d7697dcf4252097d40b84d8b58630008d583df1c803b2fd222d5a8ab5d4014517415489791fb953048fc78bf69df412e9df1db46e8949092a443b132fb8df02499c1c963ba3be4c3591e14e31140dbfa00a59ba5703ee82f9750caedf0a9a96ddf68c3494b219d916ae2661f39017e3ad9985dacf958e6c1b3abc43c6a41aada35c4450606701d23c53a3a416f07309440063a7647a148b78e74967a5c44fade62ebe8c0f87a83bd69348fb330b01b11f78689025b0ad1b944085172121405c47e929a7a618a695439a38fb2ba040f30cbfa3e38a42334cf1a595327b4d7d2bf9c3482989e1f02aa44c0eeb05e54eb456001936528cec2a1d62580e967523ac02aaa015679e971b3e815ea7733ca2399236fbfb96c7d1e386487be13da649c7a9b6636b674858791ecdcd89b112ed064d7f27ac7ddc6b011ba68fec654a84145dbe369b187cd4481ce8c73cbfbf1dfe98f5803b3a6c8bcaf35fd6e0370dc0030b9b80d96aef8f2363dc14141aa3d7458dae32a617ce1a005fe0a9d7f5862f4e1b9a060a5e10a520b9a4cc2fd0c245c4d83c06f453a51d6d5518887e7a847c9b1b9702ffc4717990a708c6a0736b9bfa3a76398b3a5d5c720d03e89e96692466cb26596014fa0c55402276ae7c77f175d0f715efd033938acaa450570337af4a585031cb2b361ba7bce226f97445c6424887767d5dba11e90b46c47a09c49db2b6dc6df83f52df32b1afaedd94384a28f5d8552ea45c3f8d2c6aab3f9027b4843a111f5b7b2cb9d5287a32d2e64be9e1a84a9bc538ca6c186bc41512dc55213778a728cdb875d291e788412a128c3e361d2ca33457a3a376b0b0d43d90c1efe8ac2718ad45691bbe6a45a0b11384582878aba01f1a77f6d654face0c73a8c2c1a5e8bb25465fa2baa691869e52024981504cbf621a0397a527ef8d77c01c93eef2cc68d639a476fe462047958261ab7f516f1932996cd6c4b9aaa86ca71dffd162842e2486ca097f08a6f8ae9143595af047552ee44cda3fab8da37423aa88cca77332a86fe5b570cb3c9c0b214444a0738987801c6d006aa1a019c611726d6560c4a6c3225cbf89394197a28c10953ea08c789b772c6f24829644efadfe79f176ae550a6d788a60b391c12de55f1a874cc768649ed59d6348c026ab484e2a76b84e3ffa2b22df23deb15805e2887a56478546eafe4097512680471063bffa84a93fa84117fc3c771da2687d24a5c5c418c931a9ccc4584d1394f24107c3556f1cf6a6200c91435a8b7c64003795df4be374c3434746d2e83666eba3a4279182d761f6e2578326dc384cd50686ca05482988e22150f9c70696a982877974d4733eb2e6afeb381814f46a6e3362fb7d7f58758c755ccd3c449af1cfeeb491e626140d260786f2e409a9c25dc6549352d0afe5a03548aaad1750c3d4863f66affee4ba769ec7e5600e38d3df4d2a215c72e6fb542ffca1a09c6954b888cb7186da89fee09d4757413e4da3f16e5fdc172083c2df2408462033fd30f512f9860f594110eca2eef250318501d55a8ce3d2ed90df1d819d1a5f4276398ddb4ce42ea380d26dcf9cafb8010f428c974a619f815612a42379a846941ecc110cda2f2a6aafcb8d1207661717f836811402071cbf724297e05205117976f0aecdf873b716c1a929a02645a2a973a313613c4b5c9e18f8c06ed56e10a353a275400b2b99fad201c13ac189f0848c5132565103ae51a1a86485035a483dbfe7807d1d6a479a64e68cdfdf989486c56532e549840a17a9420a809dd98d31a9c0aafdc3445ed93ede8cd6a4a2bb7c0775d037ffadb4e6537943e4867dc0373a97c0f486284df999255405c792210a9c6165c760bbd8060f7d94881e436e4f4ff024e507b46916df220a764914bd235fc458d4fe28faa840d615df844a79c8612e95b5c78b31a00852afba02135361a7c50c49db998cc601091400aba870cb432af43f58a5f2ee46e65d1c6f19007909b8f321357c9dafe12b8d64d3785a8c3b5c0e129542450dd125d44ecfc1a3b177ac196141a262e9564a19d23bb1eb501b97174c99f7cc2acf8a273e6727da0421707ffcb05f7dfc2dd70769715921803f885a6023517b85bf368de4a2e2b521dd86b10203e4fa42715372870abecb6e2b09c342bde00ad006eb701ac2697422f44c51a0880ae5e0ba2401d15882050453438718dba559e8233144f5176f0d0d88c1e032afe2769930d82bdaa6185532fcfa8326f9f2afdab4b81b0cb5e44983a9e078f6c4b4a25e74751d51c0abe4d6068a55ea4205f776ced60884251b04c5e8e3cb8fd878c58998a3ea7f58bc62e5e33895d1045640edcd1c05fc7fc116355f9f964910f21041b19f6e0361722c54b3e775325a6a7f57ab7731f81cedefc961eacb560151ef75347a3730e0c7ce603888ec7b5797025850d96162706a32d9c6b7c3acc02f7f542d966ac5c0400bad07c349fcd7a5ce10074b6b7805dcc9001f80c3cbf5b1a140fb5fb24abd2e60beaca17d435c17b5add0264ff215cc4943ae244f0bfcff6e0d29837ad2fa216af18c8f0a138f235b512ee3083de0a0a4be0922e801c10395a401ebccddf687e072680e934bd1cebb8f3b9122cc918758848ce319d20a1581325c39d507ba053c9849173b8496944c8c63ab772df63cd6f3051d506de9dcdb33ee0dac02a8ea7ad1c7213093046608e1e1275d5c3315afd1e98385e94f5182d1374ca5fe4f7a8ac83c615c84468cc5c3717723a26263eb5d9979a2ab930c43fb60146787ab872220f9eb9c4a50198d3b63efa0d2c51903534b365bb950ad2c610afbf8e33f1d6c1f227845471a0e2eb50bb035ed6e85efe4f672381de324dba2492d674facf93c43ad47351fb143ea5efb2b9cdecf592652e048297f38387350401517e198d8114716468a37d08f794944e4b4724a59be76376eeab653295e89644279736e8b5137ceba261e2668230a18253583625b49dadcbfc3977057b0d9ca40f828fcf284fa8ecfe80d1c0378433ce0efe9bcc181b3e7ec503ea94cf42e83269804ddfe104b0c12ce3d989ddd356e8b0109dcde3cd182cb3dc0164bfcd3b423e2bdd0c8aeca54b68f95f592f56fac84d0179fd073524bc1dc0c84ae738c92c6a8f021cdb872d052dc9ef0910a7f60bc262c2e05596f3a2509b16da81063d81a6c03ec7954dafa63283b40d374b52dfe96dbb4d70bbd6b3b514d8b3d6c0fb5a2b4ae86c62103b81676bb3a22be92654e7c2e2ea0a7f60a1097c3c1d66ad1295250d200e2f2e991d01488c9561191d9a5be9b547e3179d8049f3b044f0220e1acf54fe4f22cc2ee7c9a9ef3c94008617210c502cde9d014722b530c6fadec11082530807a05f191cc2ec51475ce084192033acafdab90ad20ef5aa0dfdf82c1cea2d850aca8bc46bbb69e1d00608e6b15b03406a9a75c4eeb66d2213a23de790a5ac57e9778d22fc8f6d82d334dab5954fd1d88a4db7cf5a9273a6606b942dae9ea0609b28ad442e7c82ba088cbe7cc5813dbfa4d85246ef4b2f8184254b9644d04ce000fd0c4e00f92aa806c70279482402fc7494cfc50ac3c2524b6545a2065d5a5a59abdddbcf90cc842105c9ada87716db1d5fc80e6eac533bec6c2aafa058776fa92744de5f2673d8fc2bdde0f05893df16097afb80b9d045f37e18fe02d0708079102f9a4e9f09b0fa4f17c2350b1333b5bd17c77533e117faad4907be1f11a301d0b4502fc545e748584e7acfe9c5b1a5ecb06457d16da0b02878d8f446be04d880dcfe466eea4d7479d384fa0252480f0f2bda6a6543ac410dbd44bbf3fbda02e1a83ce69a3538cfc331f3f90fab0e18438a650e84eeca4bb20cf9652f9fcc92c995436826d03fac9899d119f9afb2cdc92b6db97534b8699c80375296b2a54c79f81b03508a8f4ddf5a354b0e2905ba05131a82fa0f447696b223b6d0fbb5316837e2ef3fe33001994a1ffc05d0b2317d9739118f443be334adfc7061ad83c4e4a253c98387957dd6b76c88bf7a35e43e804aa2a3794bb587a8248f3b8571e78386817b4d5520ab7f2815cece3c8a1b2d3214b9255c768646446f8ae595d006d739d853b840817d9bfb429f193d7f2d41d3161c4e9448889941fc043ed77ed48a3413be6eb516cc1d5da9b8a0f7d09f2dc41952337918895925257405498812d653cd60a17aa274702591fe9f9424499c16ed1b8489feeec063008791d18175aa1ff20cba01e8002860dd4fe23b4ebd31514f485142bf339c4206b16628c7d02e02fc9185c308669460970c96bae8f051617e2da07a2e4e00ece8092c042110ab343624aab027be788cd0cd694e3008a4797cf16026b3acc55a7fc9f209833dfdba97726d9bb0053f5e77390936321695334cbadac6889eca00fdf1015c6996bf4d0eef600359ee53dc8202b236623d787d426109f09970a828b1e926f843a3154cd1c36e61205f8c55b38caba4a78fee34071511ae360972867b7d003d82ce3831c180288e85641c6b8ec64447bbc6e2961399abfa073354de98761e205faca20ac91af5ebfc6aa6b51c07c5133b7bb4735075b7512222ea05c7910e4753db69c92f34f6a49a7950f462bd40cd2947221ac1d4620163a61688ab149f123bc9b0695756880c668356ee1059550807549948ee54e8c453c24419f35fd01cd2d803fd69be278dd5ba251665a493ef9bf10e0f928754987774e03b7f350eb33a91e4a9d08951cca4e04f9e90361942fc3912807b650857f1d78f9abfac1ac3b7f37d1d75fdc30a15554b45a9021b3d1e014177516adc022872f4c45cffdae9b4f3e582238202cf8255b421772ff3f60256113a0ab459587b3f901dd456737bc4d67b27c2e9db0ad6b0bebc925fd21d0d79195828cfb4ee0f84e17ec038ecd74d4651e285ef4b5ec2197f0387134470610c2cc373d9320a67778cfc65abc7b845fb1f6b00694c3063646adc52082b5b6303d24fc2e7739c7fd634b50fc648ba63d1cb2ee2bcb0841d6b8609001810dfd6ed85dfaf5aeb6e1164e70d306d1b23e78546432efa540341d0e9ca8bbbcd13381b355442a8174969afc2bd6aa086c32dffe2af9c6382484c76cfc124a17564499c5042745c508da35d222183eda7cda26fc0b803cfc140055f218a9654d75ba33c767e87d5070039ca2f8578fd13657c193c961bcfb1d9206fbdf3f5513ff4902b7d4f40437ca8bb6e8c28263bf5f8902db69a5b28b1c7150cb53d3cf51ba9bdacda6bdb19097e5f646b68b860ade8453da30daeff7300a8cfa179a76605fd5375ab8c90ea5bf2e1e6aabbdc86d2e28ea3649f81cce2ba7dc0d11b5a4a4bc800293decd4d28a1bcc27a85e8cfe43b5936b706452f2868a71831f416f02cc5e4aeb7804285d1ef9a846b79f4fe93783927021c7beef6e0fd30921d9288b47033a54d3dc425c8e0668285b5ba9cb8210fe77436133656dc5539d9660cc30725fffa7b571ea644de8145387e4ef064ad32e7dcc7d5de838fc90fc92a6c81f21c0809efecabd38d74b79ceabfe54b74e5d8097058e224ff750197f924a8b1164f1b334c512bd32549ddbe6773b2f0e0375dfe2748abd848b3b184667d90b834b7e6c99b460d040c77c8ad00c81bbc29c8f3b123ed4c2a1e780b8b1b3dd83575bcadc1117fbcb1182c7f70b01cf7d8083285902c9c54c301eaebeec38efb54b99f9aa38442ef4221ec5d44aa0ba33cb1489a8dd22c9d1ea5e9fafca3f3f33426211b22f289986ab9a7813ab430cf10f49d338365740c4f883068e84d3fe73833e134433dacd3b14b2935514200bd980bbcb10bcfdec9af3e5d3b4d8c0900252fff8268936711183a0499c0a882776626567bc04946c8e0d9823c5c5f86bb185a0fc5288118bb001e92cf51301accb036a766ba857309c11a539cd9a8c737e6e97cac1f8bbd715015cbc48c1b99c23cba7d67173dc720c0dc1d590dace5df8d8d5cf180680400804f2c45da40a0b404b778d8ddc9c173b8bdbf4eb1b9a0542d65adbad7523dd88f59295bd03f50a200ae609d43e6cdd1b293b5d0bbcb4956b816a0812040e7190810cc62023481038c461075ba7ced57090c5bab32c8b5e52beeacf66d41f75dd7d63b4c9ca2ed8a2977c8f980d1f57e3dda26e43d126ebd6e91ba35e6d92af7e12c9353ce8ddc6712dd62d322af5971ba7928f1e64917899f599d661e38c81ad4f9a459bac22d8bab44eefe3699375100d1e8fd5b5b61586f5c6e9534e9fac37147d7246aab083adb7f5d6e95375eb1da485f4093467add0b2ee56ac4bd33b202ec840066390b1923bc755d8750c526765d1c3ab2da9a053ed208b69cf6c3c9c1d64afbbecbe2ca82b06a9f3b47a8f8b17bb3388417f435c8d1ef429bb8f8bd30787aa5d76631b1183eef3712df3561c6a76d0e7f3f3d1ee6c5366ab3f8bc4c32c1231da2178feb2f96581e769f582b4693e5aa1e6c6c1f3f03e216d9aafeed369d33c48489d7f3e13d60a9f4f5b61d4fc7cf0fcfcf3795cf4c919e9f9c1f30f499ffaf3af8b3ecd3f2fdecfcabc349d0352820a5d28230c508c80085e9060e70b9d261c0e8ed00114479430822a70a290814fb8802eceb03332852ab2a0ceb1380fc0172a5e9e09d01580806ce3dc02dc02f28f7eefd4614054dcd732311f41f24cc42ba9792b647967c239269c63c93d7076ef83ee26af7308706fecc69391d6c93c0c8807c4c4fd43fe09920c13f1d47d62ca309184f3194206135a74818538f8408609797dd08fd739d7370969d32dc3e0ee208455555537cb43096704452ac6282384a00761105fc7186d8db5535d4be30084fbf1041f6b2822f8815c8d3550d7d4f57bb03b42186177144b8e106109d65c8f8c0fec2fed12f2d4d481738deb9177583e4679e59f8517b284e006f725bc8ec68178478a808377c0ee481074801f2604fcbbee7d9f756de201c4447dd712a3ba4318ecb815e75c942fa78160d838188e81e15be2c5cf922e9068e183240b1e2a76201188b409baf7e99302c321183af8749c83328eae2863bcbca368b41796f1be9c94abc2531f14b83ba70ab424457d1775d8450a81544ed41c8dc28ea709425ccbc3e9e1d48225dc04c1810ec0288243a23857494949ed3801195730210963a8c20c32d2092cb5208337217646a040049ceb6b00a1ab01a5947f125e31420863c326a08412c2e08b9e028c1da3c94463655e2a57cf27a68b8d457c500c312226c6732850a2448912254ade53222191c8c37371f10106a6e10afc699862ad2a6d224529dfd5a62fa4a328098b403de99c73ce41e81c26c66c0921a42ee79c032581f00b28a18430f802070f52a336814465a8ef0e3b233d44c08d3309374c7681f1810752cb74ec136882a0901294402767d6c04108a1a4c14b69d82357022205c337ce18c0110b9a9d730e7bc31bb273ced5ec9c7330a2384751f20444616f787087424275f8c12290250df53d8ab037480a140251d49049adeda2579442aab4d861cc1042986b865508218c43b221d8513a62502feabbc420bc2808af5c2b9dd939e7429568424c0c509c97fb8bd81b9e6ba1503e40c527282227c567082194422e512eaa86fa0ee35d4b8c3146d21549e09c73d2e66aa22933ba94f21c55b97ef44022189ea601129971772a4e25db81064e2861010b5e4a69872d898f167ab41fedc727894f8f4f4f95c3058f114a92f8e8a1e487f2a161d8626707469c1520300e315fec0481e30b1e3944470706efe5bab9d181a20b2d4419656c839349c299e49d847f91a2823cd722df7b2f084adc0e752bb9dee0fe1454efec54393b3b14a887b49016d2459a481b9ac88cd6e99cce494929a9901a8ac6699c1c4e1a48df00c04472d1cf89fac6784bde922a873a8288c8fba1ee4bc244122d47c9cece0d81dec3c3434a82c4e7a59476d854562a7557e5482e86b02189cf4ee5822aa75a817be91ec852c560a7e7dd1ee91345129f1e9f9e9c79750f1b768eec28d9d17292ececf8b0c1878a224a8af8eccc21497c0670817eed80ec13a263f2880c69c3109f2ae70b36d8a91e909b1b28701ececb79cffef0409bfa4b709f1640828ae0e16a8c5092c4470f253fa4d7837b07c812bbbbf7e29e7bbf17e9f32417decbb5f32c9ca54fe32b0bd732862174f4000131bccfb5c84b123613ab1c1f2df4482e70bfafe05e7aa777b6c0fd3a7af7da0141a1e5f44e1fd1c0d07eb49febc8b513c4b58383c700d8cc1494f673c54e15ee45fb71da4f147ce610ca06ed4703a35fa8db5a744f955345017f88d9a1407207644d6fc377e08bf4d9f1d989402a8c8b8b0b025a8544424002dea8d61aceb9972a0a57e55c550eee5f3b7d3ad22f920be91304e9a372137b071e9cc0398803c3509d06a85b91a7de32b3e4645a0603a3ba6e99fa96a0a51c113bec05c27b13fcf51b01a597ab970a7e5ae8e09ce9e1f70e23d6a14913bef0d03ea460785da40c4cee750a5639c197a85033093749a55f68b42905d5b93ee92061bfb8031b6a44709f4a9c364121713004030443a540b3e7d25c8d12894a490259a6784890e05ef233e34ab9267c9133b0bbb4cfe7f5a4a494544825941c4e720080491c01002ee76a8cb08b4ad43c7be41144441e0f646979df4ebc793b444e7e4833ae1b7a4320cb13f284e45c37a4339c1481533c228f484a69874d2587ab62e7248ed401435d995a983d1a769f57702f7d33b7c07dddf48d0e7d33802870963a59df0cc9c1997d134447e70cf6c29cfb7b51b429e7e5e490ce2080eba6c685a10e653dd0c3fd983c1c38c5bb1980e9824e3c4c6f6870e3d660ba9cab6155440ce153040f243502c08c484280025c3bdaa4e3023deb034cb43fc4ec04c182e170342ef81c1b212c75da2406171309b311312ca3be531a170c436db9a0e89e3d4170bf97702f12c7e19480d40de390f5481c8863006c666ad1a69e3d0770b99cb86eae25dccbec81ee2a01c6c149dad4a75fcc1ef8227574660f6e9d8b05f4001724a3ba48ea8e74dd4096aef3ba812f52ea489d8a53301b3a6009c7d0399c82e482804640025e025414206930a58e8c817bb96eae9b1be09b8b0537b84f1570a9e4504a99414239311989425bd5324c452302fbb2e0e1d87d899850f37167d28a09f6bc0a5f53e2f3fb6c538587a779b75eafb54d599e3a95b6ba1f183ef6e9f5e16542bedd354d1718ca3a29a5a4e2bcd90785df1d45494aca8b44c43e280c9740c518e19b079677363f9328e74eddfc0eba2ebe471d40178988e5f5219740679f5c84b54d3d1bd3b4cd4be2f5b342852cd53384cf411607af9450562c638c1144cdee17af6b5327f16c402d85502cbba8aa5956d5c2a0bec87e5d7f325776818e6517e894750abb40a08ab26e84096f5a8781717131997e12c97aae79e2ec9c802bd0a9492beb52d47556b01a1406daa7ef7a587c6333f1d008f8d23235e08aab61dddd08f872ddbd077cc1eeee01f842effe6e1e0e7cd1eef2cbc1ee3900d44503770fce1d80ba483c9c8f3bb353606ea84f0523b5a94fc22a809dde244a147832f44f865e9c4b063b4e940142dfc494790e01f3977d328ded8832d86d62094d86be655c9f3055391987cde8a0d73904cccfeaa5a8f054e7045c7d52540ee784d77de18030302e2e26d34f22d50a9d80a74d133095235ad7b996f998ae4c6dfacba91ac405284881a11004f7af3ea969bc8b3eb0275467f3083ff8787d3c3c135f8a77d7dd5b781b0944344ae9bb99d4f0ee1d3e683530dc92f6023fc70592a7858ec7e37a7c80a1e10283dfcadbf46cf49db3f9eece479dbbdad47d0a2ffca27d87afaf1c03921e8ba28c608104bb31b033724511f0cc46e05c5199c390a8d71ed3206ca63aa8aae66d13490c55fb9cd76cc8671fad693eb076b3f61a129e9fd88cf689cdcc536d3e6dfeea9728336fde4e37abdd7ce1aa3a5c9d5af45d9b757aebf4dbeb29365381a0db2b4c085019659441069eaf303b45ad4838a2d06758d72cb91ef4cd76e0e1d07569340d8b2ebab96684b53beddb65730dd66e7669af872e9bebad195d9af64bcb2e6dae51b1a24ea9fdd22e152beac4b966946b46d8b2fec31035d87d56478621bc600a3523dc81478621bc400af0a44338a0239b7be05128865eebcd3d70bc597bf61848e96b7de86f53bd66473458ac35c6cb1beb43b54d6e641dce309b6d5a3e9eba972138feda4207c79f9cce580fc74f987b601f10d39ce41910b81e511eb7931e6f36b91322de139a1ac6bbb6050ed68e72ea362daa33c28525d45179392c077dc484d02e6f3ea2e20e94f0066c3076f96a73c5b59646a7091dfbe8620fc58a9dda1c0a62fbe810bbf5d5462044ef81437f1804eae32d8d5e1a1dbbeb5b1a5d1c8e7e443f3abd23ecd86dee1173e8201c0f6ff2a21b85d88e5d9aeda34b8372cd47c4daf511f176518078d7feb01c28d7cdb83e20be2eafddec6e34b93e203eb9bc6e7ef8e4c4e62470d434fbe4558bc02188230432f6ec03e2939b4540e7756133d6a36dec3021208eda8d6daaa19be57d405c6ff601b1c79b7d4029922471392c8cbd5aeb1ac466acd1430f7df64bca43a7a120b687b25b452ddabec9af5c7b66afd0b3a7d807d8e052769af8d0b31b1f7a0cddd1c3d8a5b14e0fb5fa770a81aade61750be39d9eba4d8cea8c70c107d3d7d0cd363874cd4a9b7b949eddf54bcae5e825eb9a6574f3c49af6141b0343b6030f9f5cda6c72107669ea698e7afa98c681625ddad0b7938bb4d0e54397b72e76144b13bab14d237779b36b56f413bbd9c6229b4deeb4f4997e033638748c9646cf3e3a664597567bc8e68743af36f7d0e445d8334ba35d1ba16f21eb2a7df5cde624302d69a791afd7e4abf68add9b93c0f4ee3e22beee1eb6e8b5fe987560f769338fdc033b1948592ae27777440fece399a0b4a8060ebe9476c4cbcbfba3072908614726de4666254f8c659c9005c2977877576373351c8c6b71438d6de20210dca701238235a8220840b0865d1540d8021545ae788317b800d85d2146cf16b228d2180fb9061901cf123dc08e47092af061b0e3514209fcb0195292ea70742199a13ed2914a5dc50378deb2d43a0284e7a84d8f349f835975e9d3acfeaccaaaacfb8254f6e9d827042399a13a2c1da6aa14f4452eb6d003ec8c6c21887b0276467cb4c03e477c88809d111f29defb04d819c94214983a6c8614466d9218b5678cb15655552f0ad50aab5393f522bb84e82308457723d16b155dd1a10d5d91752e2412d9d03641950ae9aa37bbcf9a150bbeb2f015a9aaaaaab2f498cdd4081a0add0b428bddd91a92116d7ad5d2b02e968736bd63964711353d7c78e0479b6edaf4569e509f0d860ff7ed77691e91505b465e8c31c624505218fdde83955da2ba35677587ab4b59559f96ba950581a80abea2a07cf1c17e4d84929690dc509dd5401a08ae7f63d806d2267752186a3fbf31e4730275d306b30635d246822576815e14fd78a8590042df31db54f44e9be42ddb476c17e9534be04bebe4b9a4a990ad93d10b9bd94eb199581d04c2b0776c591476a3f1bc5dc4b5c81f83148220688222764bd3fab4fe6ebd7afc0b5d988dbfec461d75d784a61ed4ea9af23c90bdda7405a9daf3bcaac7d33ab2be563aaf07f3d6fa5c6793cecabe24cf8b36c936cd99d783f9987e5df4c9bd2eb0cd5ef911c1f2d522117116483cdc05ad288f1fe81fe8a66b6a93c440f7e5c4a0fe8736c9c750e8eece9d82b09909df14a1190a9b99f3ba669977d87ff4e9bd9b79f30332a93c18959d56a54d3ffac4a34f0e7449f499847950539b30d07d90fa7ee8d37b45da244df25d041403a1e3c1f254def4038f1f34fae5e55c389be27d39f0a58b4016f99c1c2a64f360f972f0cbe9d3c3f22f8a3ee9bc207d6a2cff2c88d484aaf3a26893fc65a7652fec30907d3951a8f15da48bc017539e5517298265147d023da64df0e5a5c0b1c8bf00cb1a60f97480e5a704dd97935951fb8d04eb511828e90da12e62cca2c2c7088f03d2b26b57c52ed03550051d848140200df301040a02479fb483fe114905cb2c098cdad7aa3927bd75bdda25ea374dab775bfd75d55baf59ec569b658f819ac56a66ad57eb9a745ad5044951fdaadfee3e72995d6953f5cdbe4dd5277c095d1364a95e5d2ad40c83ab78d24a4c9fb2e9873ee55110d571f4697b75d3ca4724953e51afaed96a496dd8427689d045db16128542a16fdb43db160aad5497c6f50cd9021731c668851a218410768443fc8b943cb54bd05fced13b4c2f25bdf4cebedbfd685beaf45979a3cf2bf28ac81e299f045d242595a74f0ed34a47474747eae8c097c7a3a3a3f37876a828425d5c8dc6f13442ecddb7f38ae8c017f730ea6fa74f11ca59ddb753a44d31bf2213042bf5768ac0972713ff5ca650e1f3e3b1fe78b2e8f141f292f4a97abc65a3b314bb7a5210d473cecaf378200b36d35b14d5e2c743a2b563a5ee13d2a6189d50e78d8d2bea4e9b9d0fce35ae8bf8c7d3a6785213ea9f4e9b3adebe3c5ccb0c20b51fdf8f86052c6821258c1d636c28a59452ca28e5a594524a299f6551e365942687c404d44d1ad4154a0faafeae20038c2be0b004bf0059422ba38c32ca90b9dec465aec7c3f4e91d24abea4e711db12c6c86dec266bab2d84c05aa287dd88c35b119ebd38216a4af6c1146d4b4293e623cda149f45a1becfbbec9060d61b736d82af2a58c10cc3e1b0754bf034efa0bf212c116d8ad7b0997977b4295636c2e8e893d301ab09133f274cc3e0186164506132e835245c1d6233a0431008545dd75537f5e9651a38b6293ef38089d057dbead4ce3fecdd790531f5c9d1227e30a98a6afde1781f6262daf4ee439be24136c66751c0d49577b635054a9242adf092a19f7d824a6acc7587c4866a1d9e94325fbd3e4a658c71526acd496d45e92deb3190da8afa659dcd93c657708c43c04214b7375c6377ab6b81cf786aff86c34068bff7deb37d698f78f13d2b50d72fb1022161f9a4bc9a05cba010ffa87cf1a7c643d32b23be273b6647628c110c38de0c4ac498e1e15ae2edd1020c086f063987ddacf14d47273521f1708699f085018e25de09150b1cab00311b8ddf85712df1194f35457869b4c9445a83c9217962feea5375eddbddf334b569be5a1a6d9a2e90653ebb34bba256a7ef550fc298958080b81ee2fc03d7eb5c4b95f554f78419f4e0ea315d8407fa94cfc3881e7da24e03cf673786cb8d0a9ecf2bf36ece39a763a99e64e70757f30e2ec1d5bc0b0d93ca572aab59d219ead5a679f8ae27b1a1d63ba9655957f6cd2eb13d5455db5d687b966d777b65b5bb55aff531b0b2da56edf52d4f7c65d663fa6db2e8cd2bd8a2956d152b2a6cf8724028a06ecea0c63695b4bbe7e2ee44d07a365610ae9858dc8834c305e658a73735bbe0e8925ddac567514aadca7577162a252a7c9c95ab61639b64846da06f7959575c97a0cb5799febad52f7a5d17bdb14d204a3e315ce65319d57fc5c462c532aceb029be8573f74f17233ae4df0107e240fe3a1bbef29d47375eb5a6f2c47f5b65e75633ff6dd0a6c03cf13526078eb27f7a2bbc4665e8cfd306b05f4f1a33ecd7035feaeab0e7ab64eef3c7d6339ac43d8d80c0df9616c52939a14858166409b0559cbce472c740a8878044d065c300df5eab23a6633b665ec1676299b5db00e1cfad5a7bbbb3e3d0a4481a8570abe80443763b7feb099d0053d06d6ebf2c4d975b14d57bc9cd7d150b7c605cbcb08b707ca29829b5ef0b683153ad78f81135ed4ddcd2ed8e16cc2f9b89d9b591ece79c1f88701d18f44e0e9eb1ee7836930b2c51652088694d6c7ed3183e027d3e2cd526eb24a79af2bbb306c54afd33ac2e2265f85c8aeede6c862b5f4ee10ca7a5ddae52b65d296b2ad94dd1c20f801c10f8edfe841772ea6b5abc253eba509d5c81c5c43378eeae8c444848944d7eb69155db566d7abe8aa597573ba19a5b496c491505c4dfb93d9cd42da103c7a66b757ed6699832b8e84a24da15b2d1d5c6fae9139979452ca2c7bb5d96b0eae525e3657f65a23732e795d19043fd925737070ae91391cf0e1001210fc40144c0147e640144cc10969f2aa097e6239cb7a24dd42b965726b64cda86698988c2ecd3cb1ac0ac5a4aa500e8f626272b3cd89cdb2939050286c66a2e4405229a188be519195a74de9a539b68b6e88463883b0a45d1ba2d367b556cbaa337ea3bac975c16bc6cd51dde495098511de61418cae6d303b842420aa6f971804b2d35b82a737f9664bf0d6b750a804af0d93876eaec1a1ebba6ab5b00480df48f90c912842938b007073a47cc6534c3ec3da40dc0e7fe01b2637d7606add5c83b7db600cc7ab1eb76a240f31214687976674edd29c1c3e64473007d2e3a14a09e56194c7340ab5145248512086cdcc4398d16955425894f42cc226df5c0fd73b40dfc4e4b14f2837b9c9af3e9ddce4d5d518dde4d735b1d8cd35183b6d4a6f89be9463bbf61bdb351b7aed37e835cdba90ddb094a31cda2ce5f496721cf1cac0a3c39be3e961bc35231b7cd9c6d7eb21be4d0e4d22d2cd8df385db24dacd1d40746845232cbae814c5c636897e622f5b5d8be831c6d3df889866c77e23bbd8b5c1d0529be1e15142779816d32856742d74739c1cfe4474288ab7946374f80dc0152cfae8d67af81b15de519b442f470ca4f37a9810f3669bea4054d8bab32ccd0debf51033c136939bc37abd55314c9bd65dd96360557dd2ede69a451d7478a6838d81a053685d9af9aa9aaf6c9894b0977288befd8668bbceb5d4db60df7e03fbb66155d49875ccbe32b0c342a1cdba39b0c76374c36e29473cf60d60b7ba96ede61a8a519b785db3f426767bb419b57974a932f0292d69bf11baceb5848e5de75a44b7c1da2de5d08efd8676ec36385a6a31ab59910dd91c31c4ea2956aa3707f678a97637a0ddea5ae8e3adae05bb0d7698766f64d8757312f8d68c6c66acee9cf6d50c1161cda16d16a8c8661f58b4d507b11d5e58bbd94396b59dd426ed99155d7fc7b457cd3a4df6ec6adfdc85b57b89acc3219b6bae4b7643780a6f52c186118300fdc921bc79d4b0b5d368e5c9254476cde64b2b894eb3fdbae85edf2ea83d06467bddb279135d57bbd42cf40101edf09644992d897e3ddbac5b125d1c0e97b0d358171dbb2a56886eddf9ebf1db2f9b7dc0edd415e38597bf2e4dbde8d29c1cda1c6fcd85eb8d2727903ec251a42626105e1bbc4ce0637508af1ba1161b42086f2545856fd1639f4c2efaecd3551289be91eacd57858f305eec249b3d74d578131b3a0dfcf610fc76b85d384765b9aab2ac0b678f36fbc071625966f268f3e8d45bb34eaa6a43d9f6f8edf1d5bd7e626936914dc49ed9d04d6cadb671c8e6d10559d6f66b131dbbe8970d3ddaec9bcd17d9eb98a5a972cd556d4e22d37eb90b4f9b89a06cf681dd61dc0116769005786baeb7032cec000bf8bd515a44443b25e54a829151461965943832f94cfbcb44230b65869cb24a92bc34dd4d4a29350d463b9172c6890909453bbd26e54de44fb24b4c080aa35d461965945146a93d4a6ab376a965f4d2661c5866b7147af6ebd49642df4ee7ac9544fab3094bedd905cb5b0addebdbb75fc781e9af18282595269730583e83c1f546931c263348379e8c4652cbe42533f928a59645994529a5945264330e2c2a997084c1dacee4a93d241f930b8e37946d39c4d01d24a5a8a8a4904eb2d056b32c7bbc19066737d7c0886cae81891607ceee6096f21928d648a472127cc9e1a519249c652a37bbe0eca6904e6efe4d2e2e3893e273b00eb2441c301126cb70e078cdaa585163966559ae818938d7c0c0c8247c727060f75957b050410e6056b050010e527ac60c88c0f4541b8d6aad2bd9cd2b389bb3de4c048e376738a28c32ca28459a9477a566a566c5c484141a1de5c4047b3df6788a65d94352bbc484d84e8f3ddabc826316b398c52c66dba5f7f231bbc8666c747a983d9eaa44c4cb5bd2aa2d611876ecf938c3b69b89c0f5f46622b0c4b29168f4ecd28c4e6b567076b98247a12c44ca504e4c4c4ceab3d3cca46619cd4c6a463322544651ae886e83c6e401471f703c6d3836ab3dabd1e4e61a6f5e11adb4a966a512b1729c1d4b152c557036ba9a3431f9c8ba36896eae99a6854426cfece82636c748448428746b5688a859a959b189c2a60811304ac4c0b302a3440c5798e8f4c0548b999499b434d67aadde614264988f88b938ddbc446ed65a7f851eb5fa4c087aec992c693707bdd843365351c542af3db057faaa95b69b835eec66b4da5c1faaa2639746f4edd25cd4fa88b8fa885854af8a723232198db25146b35146b7edc2a1c77e3964810fc51c5abc35bbd963e0ed7679d10c1b9b25c6add6dc036777998c8199cdd6e899157d647360d9b5cde61e18bbb4db4356d36ce3cd662bb3d866736037d324c6a3c749572b5cb732eab05a8385c8ba6e97f6b23287379b6b46d6ab910884cd542294110dfd44b45ddb2c755aadc49e5d6e87981036d67a1ae333f9cde6118e37b26fd9dde2319bb72042cffee2eb633c959766b3572b81b09e5db3d9a3cd3510d872dd2cf60cdb6ce6301bd7b7c774b6ddbc1d141f311c0f876e09117abc34a16797863e7eb335a3d08d11259e8846373f2c1289b66c1365d84cb565d5451f9663b35525aadae36dd49b6da48d527667a1574b5fe10ba94a291202bbc36eb455752b4765b9cb6ab334d7ad5bd6b76b368fb00cd94a2275bd39bb7be824abdd3d066ab66684b763b6030f5f879675cb6ad76633ed8de5d81eaa56bbb4a29b6b9cbda1bd3eb3f9e5d0eaad57d7cd6e6d8da5f5b939ec46ddaebbbcb4a2d6ad2afb7ea831e9d1109ad150dce208474dbbb906c79be5af07d0ec4f74074377d33e437bbc798435f79a51362f93511d913e4d1e6fc5504e3eeb28bbf6ecf234c34cb6c72bbb4e25bd6ef6f8ecf1f49bcd57fd76a8d99c9d5e29e5f678bafd0a85b64b7ffd3a7da69dda5c83e345aba6dd9a11a6f5f4d2d46f9706bb6ccd28bb35235c318c6482723232198db26ba7da28d3b48b3ebb36cae8a5cd5aaf592f212c7c5db3d64336d7e0d0e1c4a2bb7e119d8a6c1e69526553b1a26e36f4cc62332e7a76d1b73bcaaa4874f308cb9b6b4617d52eed0ed3b45c333a894e55645d9b4237579c6597331ed6f2712defd3d6206d1252a5681319d5c55b757890b527f0bb86e36af45fb67cf01b6181dfb34910f07bca88c7b5bc5bf5e406bf50121bd4ac25d1c1cfb9162d499ff2d3bce853b6c116cef7e96a48fcaec3d5b8f0fb0ed27c414d2142abe8cb1cf852bd2f756e758e051ef1c3c6c4477800471019478e8856b0c2c37bd2b21c6c18d35ddaa4c38ac6d95981e16dbc1efab027bb0cc1b3c385089e223c38a09125398097455871e101c6c5c58507d31ae68448be080327320192de26d8a436ddddabf880e17da06c6dc39c334e41e79a389348f0a59f4918f2b89623520b0872ef6a13fc73ceb9e7e9fe2e854c2785b83aaf5961051f943e9247eec81ed98321cc320b2dfa942b177dca328b2ca490474222933c9f48466d1aa0bf4f9065814034468bc648ab49279d745aa7a716ad267dd47ad57c0f142184b18706234904ab401aa636c18751b309d3e897918c8be9674f8243854f1249124537d5ddb161773f1f2d26cece478b1e2d30fc8e1d238aa8f129e23344083b234b94a8579d3f69d079407ac3c145ce14f43ac862a71b6acb0841efbe1bfb70406fd069fad4df21f5dedf71dc73f0efe6f90ac73e206d82b09aefa64f0f3704dd2170c4e8685317d103e44161048f1f15cc0478f3838304489f72863c3e7a55e9539f4a431a702a139ee003e4094ee0f9e1e1bdf7de7bfdf76aadafbe275f7cefc1b8146144cd73edfa21e1d127238ae8534dcc869d91307e2ef845cd13ff18a14fed4c71081842bcd0011dc1d10113c8a879de00006e1db0c3812ff333167135faf21247aa20224182e1092cdf5cf40b2807e2b81a3e7da24045201022630c012383847801cae92e3227a80008386329000396e35ae4295b648c213a00c07db31dd7226f8100804139f0e5eaa259e4bdd00143c835c6100c08112c31288a60994117121b54f96ce56cae2584df6506705f76565dcbcb1ac055c62ec5f89e08cbc3c8ffd02ff5477ed405964de4f202cb9bb0a444ae50a20c9c7dc0d25da1041538beb638b8b064064717b41c10163a2aaec6c36e08ee6e25745c0f4543c618a939e78c261bb344ed43087fe81384f03cd429cc2851d09287f8c3abca3a8aeabeeb6975dfb2acda17046fdd5cd909840eb86f3d768e135317c77589d9d10349c55de4dd21ae8e3689e250454ca87dca0e49764930cc5007ac14098ed0c0072700bb233360014f0c64e93705765789d88c6c4903c73b0804ca6e078370f409741b34a763a12c8e981f8af0809f8130e6071cf29a98d03038fa9479f821883ee58833cc47d855d1c40e7026e1fea93d23a0d28a3a038508a17272346945cd91087ecf9108122418e6c09cdee99d9f9f27044b2beabb931468660e3bccc63bbc25204c72c67d2311d7d28f92a20a21e26a0000bb3b2427e84cfd2c81e077a9843b3979e36ac4bf0af77b24e26ac0bf7b02217c4ccb246d9a8116091b33114288d9a03997479bfacdbc6761d45c75d250f39c8dddb8110f3118638c0e8b11da0a678a939dcf7b9ff0aebe1b34c7b5f4dfe5e15afad4bd1b54bc535097e2c012059e8cfcc339090cef14f20e0159fa5bd42c7fe48fabe17a09ee4337066e4b0ceca880b3a5831e827b09ee678a236524176abf678a83fb55888505752b940445cdaee7e9c09dbe8333c98bfe6edee545f53fe04ed499e4330d9c5d4fe5518411357db22edfa34ff4f23e3ce06a80aa6907d1cd34707e37373979bdb9a25c0759b27b72dd5cabc97590851a5d07595a5a966559d082cec54f37e785e78cddefbaa2e16572353abeb1bb8ed743bcef4602aec6bb6f04ae455edebc1e5a092c0fed93e2e950fb84bc202f8a97f37a80e5a50edc1be25ae4b7a0ee4df5c2fb21cb7b44b4ae98a054d39c6bedf201b5457d315eefbdf762bde8033d51ed45467dcf454949498148da73cf39e7faf57330d80c7487ddceb9ca39372d6ce6bdfbfabdd7efc57e8ca018f328b6fbc1efd0a2f4c29c735896695a7597e65c16afdff411e3acb0db5137a9d3dcb1acdf7bb1e17b365ea8819ca8efe6ab6e214de4466fe4b2ec974c42cd3442319baec55da3da8545a8f5ab19e8ed5003ea56f2dcc1f1f4bd39ab7b4284bc5bb750b3dbf18138f0257b3ccc812f35c31807f408999514e36a6038be7de04b0fc8123f67ad2452fbb81f1ff8f2864096f83a9f10c812c3a8b97d7ce41635bb1dec625c8d77d2166a15707458e087a710f882535e7c0ff8f2bee058e2cb30d750c3c0f1e79591d34e7cc6bee2192a7d3df4783dc0db3e48b81af336175c4beca10313cfe70a3bf7e96a4c1b97155547cbcc5b8354f87e5f702f31af0c38be35e0f8c27865e0e88410c8127f980359e235a8f3d23ed7cda66fb76e26cd7943b56e2452fd6f6e9f08b44c76210e6489af3eb97d70fc133267ad24d2cdc753481582233677aad68c80830714903c8b043c385e7fd1314660e5051f182b30a26c425250ec9c2205b272409723d588cf0ae8a19f1180ddff6849ae057e88d743df19ee0090bab3c4052c2f8fccacb8a0130912243c201213121e104e242624538c9a9764c78321fc10f0451e1e0784a0ed083850388104cfc0ae0a277eaa788310ac825d1562508121114c352ee9b86c64dc557ac5e46a3c5229fef43fb0cf4bc0c990200bf5797f60f7363277812cc7d4e7eb25e064eacd557b7691324e263bf55e295e0764a190908093c1717fc44016ea978dcc690fa6ae82457532d82dedb83e7fdd1becd7e7f54106644a7d4b1598a7df51d5fbc3077d77ccd3fbe3c9ccec7630f592fcc3980eec9674805eda711df4eba03f19180127739d464b9f730f664740fc44486aefdcdce01e430f106ef0659f0e64a12eb750f3c38143cd6e49e40159a86796b24e51b780abd181cae3f5d0778a7aea17702f409a30c960c721c9d453efc60138243460eacef9800323833dbb3560ead4b24fe6b22f073e9c3651d5e737dccf0f47071020d7b303a1d75e8aafb791c12c56ef0fedbe9dffc82e0fa67edd4686527b5922a578ec3632454af19622709d3ec6bb831ebb425e0e64a1de104cfd7a3a98ba1e0e64a1acf7cdb5c45717b2c4832e2482dd0e495836afab26281247bc32b0bb8f99b51edb7fb603131f012a230e37b8735075c09769723552b0bc0b7cb15eaa4b88e4ef3b6550af63d52d4148ddec92c067f7070fc8d27f8d29b525f7aca7e49e39fa1dd6e9dde9fde1037777d067f7870b6491ff21a909b2c8f71b0e2341722dfdcdb558a787d86529b5aeb4e33afd75ef2cedb8d695c09371d0edc0691b83ae0b64715acfb4e9963ea0dd327d83ab7b71ae5fdd05beccdf1d06be4cfb642c5bd9ac4875b8a40348ff7dc30f97e47f5cb73e3fef0ffaea3632109bd1010488f5eb02a94e6f8c655d4af2f436323025794b11b05e7d8755dd1f3e907747757a7f38c852dd0a5940ffe1208b751d64b191b1917132d5842ca0c7b4cc8c42adae457e732dd24196d9f31a96e1260bf575a9217ef7a14fce48178a9c4ef0bb113efaa5ba7946c0a023939f038622383b24f8c51e306c01f74faf87c68fd4271ef0fb3ba9bcf3f07a805fc12f0738d3984dd4e30c63829d912ea4c03c38c36057450a7e80dfcd0cae3ec9964656600228e6745f0421b998825d154d780193b033e28523b5ab60822dbac84113c468a20c49641e0ef0042e1d1e4a46f9b6a08626ec400d443035e420e6a0861d50178646d410030ce1492e8e67c3ce881a489086318e604fa75f5f0f0f3b200a2e96c8d077b575000970702353dad18787f7470380ecc8b48d0c75ddfdde83d05559b6c1ee005a3072239344bfac05d636f070e8b34ff3d6318c52ecb4af8d3af80923176070d105de013b235b14b1c113bb01a25ea854cb988a8b1a650cd188000000011315002030140e8904a3e18020a87aef011480108dac4c62449889b32087510a19630c31841002400446006664061b00c0497ae8d130665179b0dbc158114d3f880b3119da4a60013541de03a980b8448eeb53b3221a5cecd288ace50bc4426564df2241db17894c82231cae20f6ce9b98499589cc461726626d13613c960a7161fb1314c8a5984640648b3c3cd45cfcbbbc60ca865fc6f521360718b53b3a072495148d4f1ec6349ee1a9e557944c254bcd103e3b12d8004dd42d2ba37a614c0168e45a06a7bfcb3fee76e9bd98c51323fd3c296e675dc04d4c9f378f33010d4a4bb87346463c3ecc6673274632181df1dcd3d1445d1dc8fa7e894550b4d08cbcd6d98a579567906320914371a27ba96756c1da306e581e2fd8f7fd0636b673163f43bb5bee9b82b1c1eb893f474d4e5de7a2712d55a1d9f6f2476ef9dddac23d4d61a29403931e7f2e104d14f0c866254198ed9ce2b2ee0d86074a288b8b42eabe42fb7b07b11ea6de256a3f46d38793be08f241ee45ac353e0c733cecec1d7b70635de0ca7157f3dd63f007647bb51b243e87126ee61debc93b7cf64630b68894359601f076878f869772b3522f9b46bc23a0fb1c44d523ad8ac56b771aca25fce7b5d34096e53f03c1aba7bffd205201232c6ba193e0763a885cecb17f0553640a00b88a3492953af654fc2193fb940cf9d07f7ec64b4ef02909485b4020d56ec57862369a9fe4f4145c6f80da2df8daf49392435e674e16c33d11fe5a978cdc727dd4285df4d4689cd8352e1e8b70cd424eb6bda7bafac3e2b8cc651f6c9890e59a1eb792b4cb4b000283819793392f54932300bd241249f9b125045e53a1cc46852a71cb73c944a32fbbc0c5474ff16ea5bc34e75cc8a8dcddaff36591cfd2161bb7c017f4a9378c4aaf8fc707f2dc887df517cf8ea7e0d5971d5245f501e324b4808b5f070f4f650bfbb0c0b5690ba19486ec5057dc4dbfafc8f61661e536c18a68009d901622e640f31a15aaafcb9a0159d4385e4970c52885409f9ccd5119cf2bd6870289b2726ebb2ef334374caaa2c8dbc8bc0040ec50a27e098c83891d522de11123a45a3725f0fbf73ed89bfbe51e75acb9b0a5e5dd6ef65f0f557fc87da5bbbebf1404e9455d446e0ed7cd468fb575925f16d11e683e36265e2b98792d091348bba1f1069f68695d4a5282b2d0a9a0b80da8258ae8683e003d9834ae693570c10475b6b839cf9556b4ab6a1bff06bbc1505e40d397e06cabc9b5fabdffd7f970c1df37c64694be2335ee2308e13308b4d2eee6f2362fbce8fb078a2afe217d3b68b83a4724476696db93e0660804675ad03cee5ebc2b44278347b043ccf7b0e1cf3a6cc156759c00b8f143981d0c9867b266be8d44216ed33dabc147482711b1e81308efca5e3a35af2ec284fc348cf6f3df5df0d0df0b2573717f7b3d5463c37f27240d0d05a5b4cad07fb75f14139d5b207df7e56d2bc92acc0595bee1178672eb6ab5cc2f8c61edd5b02aba7525bcbd6c9b8c955e57d3e0f5ea0e2ddccef88dad8b23d0770fdb5d896c6aa731267855bdae10e5d6e03d8b6a38e8ccc5d87809cfef0eb63bc3261fa2fc9edcbbd65f97933bf55792d1bb9c1ecd6179b6bac937a620f7262337f35d7e079b813cae3e84bb6fcce2d56065efff64ba2df3fcc48f09ff91c7ab0b52a018fedecab16b6b0782c3cf6b8d42bd8111affcf9f2e482c9c6b814855a674b0e173d5b62806b33b8043522286fe0b2afb84032d890843fdeb5602d42f9d361e74514abab73124fe0eb8fb5ff65339c9ab94df8a370795016696dd2783e9254addaae7d3c2a79a47a4b934e4706e4e64cc6450004a65845383795017cfb1173dcde89710f3c7cd66935720954726fd02cfe991823e903e44b7a2bb7e08d383b17a5e2355c4d5726a584ad5457c730cbd893068844a45f89514b2597fc12a5940be4bb6188a1fda31ab3f3dc340e91b8c062865179817726893b2829ece032b45be6a8f39d3e4b7854abdac4ad533414a7c0c1126b7951135e94e5ceb0c34ee482b3635ceb2dbf1a697044650159965018b7ac603f8f61a0a392940b2328c876001f28a0f4bb0ef1fa4ba2cd69a9d313348e9b8dbbc29868c9b7a4f29d61a808e5caf27d80d128238f83d1cd48b7812c0f454988d61dd1f0e42508432fe2811112dd78e0c89eec1d3b1695d3ca2a4b2f026acef294a1aa5c728805a0432e1f7ee4575de49aa0df078b1cc52b10a510a26bb0eed096f396e0c32bf17e00f6800f17420effc6e2802a00e444e872c9b18c97b05883ed67b48f081075408c293b72a2129b11d0321c08a574043532e58985929c25ee04e5ab2e189225913112841e48c4cd172a3871d8721891dee9d4c7b97ab4517f57419d126f2818c5fa6105f4c3e006e912e2b0632dbbddbda20f6d0b5199f4736dc29232be202b3de04248df8526c78c5032dc2abc3dfc58da17c5b6dd03ad19890ad6fa3ecf5d4f6c7a16956dd004213ac62c185b8e1579d9ddf8b26021517f6883665125c117d347124c88f1d48abe935eb1244f9666951e3e7430b03a0068a2c3c2e14851029e48377310b310f3c0e54d68a8851ede94571c1e1dbd0c73f3d3f0e6c40f007db86d04872b0b10d892c5bcd3cd3fffaf26d7438e2a85fe63b25d70474fe56b6126f91025e07f844f76e5b8cb1c8c2afb88b677ee1286e1a03089cca87f766a05bf7a0bd43a062e33abd0cf5f3a57a55a5d9559dfb391ec7fba02d99cd80ee32371dc019113e7295ab130547024030280b9397a5e3505f91b7f82674b362094c59803adf15133f5ec38fbddca832a00dd148129b8f37d3e31a078a7a80ff63e52d68c9f67e048152330650031c3ce028ce9ee4663c4040f66b1e1ac25a6b867fe2f1b00bfb90ddb5ee18aba0b3e6b0c12cd0d66b30c7a35dcd0163c3a90ca4b26ea7c946179b17bc938e7194bfe9e4462c3ced0f871c4c1558f2a6d31f37378d144e068ce16e5b3ac15022b1aec41553bfb24cd78e7a21b4eea7006d0fbff2982d0a1f53ceb8577433cd807e03001fb20a3930329034947f77070ee6482356fab16894b4a1fe1c2049785b044419e6552083395d1aecb4f03d13ac6345f94abe08558081db56d3668cf05cf8262f4ae0fb9a03d357eeea31d74ba8bc949e1a57f5120201c797c80b6ef1b8e19faa70b48adf2fe1a24d673d490451eb0f5c76ced64d86558a81b345fe920e575440fcc9ce83ae26cc7cbb86fcb54939aa0b03f4aeeabe4fbcf29710119c5bf612458855a131017fed814f138047b9f962381e4153f783f6894210f4c47ad279888904f447708036d3d03046104d9bfefdf3d71c0fb9140e6bb8c2ff6f79ee91085d8240189cb0ee8e23a02cd18cbadc3ebf715f523e609317bd01fd20385450ab4ef8a4590314004b35fba937dbf5fda2219601520cca5ddc8e409a52b4cc58b37c6a2fb786b127a09b36f45189e6f93a802224127415a7feb0c5d13301bef9a6b9705a79391da7adc07964aa7babe043c54ed8b90d0f59735981564aa1400cd79bd6a0240888e45c83d1383fe1aef9a613bee70c973e191341575f8ff438e58310797a787c778e05a543e8d8f98f201b1ab7a8419f9b58d8c78a167237afcf7ef20faba0c7849a6818d4aff4a42395a061450804a444561d8fe8ecf924fa1ae14f4ecc632e4fc03658b0a7a4bf228ad5a3e67c2adb3bd1f90bdcefee815a05039ac7662ee338ec9c4454415ab78121617f63cf8f40c49cb675628c534a062e1da7875928fed1135fde6bb25d41a38105a6750ab6c12521a06c593d0e185d0bb4a9ed42ce69802661d2ab34fa633d2a7ac0336aba04de75333ac6c34d4629e034f3435e8221ba549ba5dafbee73dfb8a9e1367754d727da5d21816c5c0854c0b6a37d24021f5c251ad66bf65db493e06d6bc69436a2ea50efc6c3cd5d487dd06ca10aa2e995f56b7779f74fa89156c8603c20dd50d9e1545746f796b07a356c4b1bcf00d2962c9b22dedb0ca54b94ea744e3f9ff916dbc33e1ab3411adbf710afe1a599ae1606fcc59586934584d7fc1bcf56a8a0f5885540e32076c2f8ee237880571905c890274467523fa38d06413aa2f7ea077ae37f49e80b89bad7ec78d9d4f458621b6a2d1480f2d97ca2536155b2a25fee08aba43e3ccfb2a194e1206058472dd48f2cbfa3c8c14491a1a9a91df516b49e3aab01e50a7eb9517adac7e36cf6dae9461b22d8e42176a8da1605c25ac686b40fd382b8b08ed149a3a2d8860f0c1d447a6c7c6df7bdda7726a3c85c3b80ec7da1f7eb20d09cef796d2a611ff2fdd9f690967cd49759e02c69c18b50f930737c6060e71c7bfdca3be0514452271a81970003d85bacf4221267af2aa1f3694267769f20c201c4b062a1d428feaa2392dd65300c123fe47e776bd8c387a66c79725d5a5d0d99a06380a2e670574bf2c70ff0227169fba0242b843a180e237d4320d8e1030f8641858b84e082217581050c137c4dd1e14df9bee0431f1e7b68b48047e8d8eb56382680f49be5974a6ee293d06149ce1162fb5e7797df80115e0934b05f4154fd7e19f31d6d5530554488b2d32ec8b6835c4ea5e688151a04210d444c30d9067f5549761f961cfdb085478ff6278c6f3089c52d1661bc7ab30810a604cb676e2984826d6decadafc3050e17cee1ada3eeb82d362c0b1f2faf08718cfcf43dd3f31a94a6895cb4c91b268a6887a1b1b26cd309159312a184b2b59e9d8992a69dff37fff1317359bfbf66cb24453a8f067d677483da615c1f1911432df916d2165355100660d9fb96139b0504bd0eab6c6c06a4d38e34fdd31bb50bffa4a8f7c36b619edf594a7b56b95beb480aa994470eb6006f8499c36791e1f07a363fa903192457836a423fbd91d43bfc0e31388ce0ca9f864812d501a414aedf028300a0630c42d8f28453c43d443ec4be332cf3b722642d851b57753a2f5753ffe717420a4b4359bd87fb90533f5421075ecb38c584106b2f5e555410a766814d5d9794a55970ca8414196881795dd4099ce2c6d994a2247fd33b521eb1fbd7446ddc2bf8b7fbc2f6853918ff14ee21c99a2875f3c36e43e9b06e68c89180b705ec113c9c7390509c693aca7006a9d1384a5853f74ff90322c49a39053dd3f04d367392f31c7fda3132e1cc2a440ee62887f248c36adf8554dada05f4161318b42773a54da85b6649d296ecba8a29652444903dcba55870070e2c160d3ede1292d1ab00e96f55787f2ed346394a571435427e744454afc5ad52326f760ab7ad7b66d7ef05e355c6d7360e62c0cb0dd1e2137968da6bbdcee02d030216f4c0634d1890a5cfbfbf7df5c629b449c62e2897d7c8f1bac103d76ffda860dd724fa854ef27eea95610b9493aaacd0353b1b58eff09e6d1cb9815ed638d9d43dd5de846f9832426432bb45229cffb958e8b074ecb1d8da2ce4861c097c0b2e1b2c3b0f29f6e5ca9545c5e8d72ec4ed882f1226ce6b1053701ba13eaa9f015b77f8868d03bd8f5644873b896ed68a0ed8b79075b03f7c0b7f7170f890deb5957f3cdc8b58dd4787d86b74b50c3484bf87507dfacea4aa971fd05bd54a931afad858459c0a13aa83bea32de28283fb2fd2f0494b575764525d8c862da3f4cc0ec5289daddf86c40c5b0eed84924ca3019d5474e8dc436cbe73d3537bfe59dc59ae8d9256611864a41daf02b84bc85ccda5d1e17fbd58187cc50df0a7d1d89119969076f98f71d7a93b4e4fe9f6aebcb536567897c200f913a54bcae2ee1e853ef984547f303c6b9e52b116a75c4d49bf657ae04784bffeea3f62626085159ba3bf0a222503334f86ba4b8abbb7e422ac575cf16847637c30b248230ed84c44a15b0305cbdf97046ccd8252b88f21d56de3d69e725c6091aed8a99957d7b030d14385d123e17a9112be6b4c52a784f9e2d5923f6ebfd56fe84140e8cb2c17bfd32c88decf39f16ee519cbc3afe7daf4f2ecfcf5449022e1eeee8635abbbcf7e1e832eeefebb81ebba71ee46f5ca1add181be547fd4066d3320d21d6f10159c963c76699e009e8442c8a06a057884715a3aa1047aba357cec0280fde3ef03e7ac5629528da36ffa7c25698233b4d3336b3e49dc93e7c0da3cca12cb8f29f641a8c36123eb7047a0796ad44a7d1686d6b778b591ce30908d42a21dc05452b86bd4d9305c54cb18ebc5fd281dff02e30ce1b34f57333f257f121930cd95a9db867694690c8001d7f23b5703c93e89a1b402e932234458eb50a00e27bcc298a7c01f6007a59ef226e22faef83df382313f76330b636e68697522fa2d8f23ed1b63713631f917d17a2cf9cf728e514c450030bb3fc57251ad3ffb3cbb0a57c6b59d4230d3b7297d9dd473b157c1a5bfef957469b9e796a4a427e0f3f84f4bbec90dfa89186b787411e0cddf55b1455051b09366029157c198ac0ee36eb8085bd50fd43e18afe50cd42ee6cbf381d330f85e043b2eaf6409503fe85157bad870ccfa3008a7bb0d4a6a4581d40b1f7de3eede566252ac5c003bb466191215395d175d0ceffb6c9abd695963081e9028532a084414a707712f9b271f1c02c805c97374ff632a55a9d4a98463687cc6613b2bbbcb85197ce7f3b83ee067ea3d3fd38a9a7b7d074a55a89ef419a6af27e5027810b6b2f2563a2593b49c86758f3118dba894048feaf461b25a9f1de895dce1e40cb6527efbd2544ef0aa15bd04e3e686b96cccf6169724febb49842d8398ce15f0f4077cc03a5d6b5a9ac443d68bf7a40391edd2f225a70b35243da3d4e95b3940c3fa07390c341a5080d7edf153fe99a85aa400a94bf412e540eea40fe7bda3e472d698409bb5620dc4da54eddffffa95a2f46523755af9678e9fd6521f30c7d490a0293ac7a9799135da628ba09360ecf531796c1236c71b2694398251924720f00f2e07a358d6a8bdcc8beeed18c323cc4c5b19a1a3c325b8ed2680f780fbb0abcf0d33131d39e6d9635b2ad02d69106e0ba6605e8904f0fc0a57a990d80d4d1b902d60030529eaa42c25ae2567f8df1c7d284fb806afc01c816ef12a1b880af78a98a0b5894076c6daca15e00a33071010d8df93b0a74634f241d7a572224821b50135c9850fc2f5d20b8cd0fa798eb3c211b67fe46cb8609f5c90f10c060592bccfb0f89e0ff0f0907b49f3feaee7b1cac39e4d5d8cc6350e43988a447fb1d68c47d1c1186c3a2ba6e39e6b1248da2266a5f8bca81dcc1f43e6393fc8f6d6c6d2ced1f417321f57210190282b0a2ccf8d017f228cf15d52d60dc22224e040fb85580b7a80d34115ff4f2d9b984c20ae5b912f5aaf73a102cb14934703581d6d9721ac61d47b8473efc557ec877830a50f21c700bffef71c39d01d4dd452933cd4464f7fe2b5350009cf0781710ffd648121d5ba11dfe3f376fa0487878fd7b5f63df9865c0557be934303bfa39b2d4f40985470e9e0c2202e588cca83fe35d6114403cf4d1e5c9a71783c02fac03b37da7e44229b4d7b7c0bb07687dcd54d6213173dd205e2844a14491bee714a64f4d1d521f760deaf41782db6440ea5494167484f179aeb95a130d090d4f286522b7be7daa84b6f7c2426b6867c426051e38862047b0564ef2fcafd5dd49496315f2bb7765b1662b923b437e375a0a091f1e77f8c3fbab05e2df0f9efce7c523f70942e6955c939428d86ec838d80d2fc10b67b2bdb679689ff20e74a276c88ad01a8d3d480fbd377b7b97dfa0fc3f9bf752380e73a768525359a3e620ee8c85e7daf74b35bb2dec80dfb469dd939c7e9e385a1b89477fd3a064e9f8898b53c366bd5052f5de2025893ab770c4208ad99479953aa1cfc9374932d3840c1c589284304c3829359e31695b19fa8bfaed2d035de4dd86533a397dd995637351e4982d9066bb1222bce73f950e08c68bb974ad632ef75cbb106c9fae00372517e49521e6c367bcb9d4e0061606f39c11e117a226222891e3030b51139f55537bb2adb236b9acb58a690d8a13b3fe73b0e61a3eef9ad5fdc89e9d60bc529d68d08aa48f246e480e836e1b494a37969b57af45072bd044c6ad0e1d3f13bc532a47937a69a55635dafb31edcd371c6a29aca592b27dbfc83e059df98512667a9df9bb1da300da3589c25c12b6fef11ae31fb8a07c049154be6cc54368c041c2c3651c06206285a1bf672295d1501f7fa216aac29aad726a6c94a90f482b59bcf3559d22431378b374cad93c26247e2831024efd6e892d8610cecc48df3274766ef73696360cc046b3941c2d7ae6dd78b733c241d1b8526e5b3779824f3475820d37d12b50f50b1b49111dc14fa2afd6bd07618aba4657f85f6ec65b8eb6a723c75f246a813e8e6e526039c575ad31c41e5a39674d60864ddbe348628135360b7fed190e88190d6c94f8916e881115daa86f7eff89b3086bdbfb98e1930216351d0d02f1184d87f7514099c21bf66113fbf198b4cf18ab0ca8a93da2344619c73edee1d7b058182904086965e89a81761d67f8b7d4a88572872b018159055fb9be7e2b7783fa5014897afedef9ab9552bac0b89246f20f69523c0a54c291d304e67f1ff93ce519440b77f20af2990335e16ac9b59964bea1b693a4d6702946e1639ed933720acc9768579705ce628edb375e157608621b92a82527eac3852abcc92d162f40c7b1de7f7b09c48e336177004960fd3c8752a15e30ec21866178126f6e447b20665a792759ea097a8c153210a26468c2688469098b55660fb81ade35c258be700310055df1009a663d89feb1f222edb1b711adc4fefc9ff439980872c7a3731d06752ba6b8569feb47bc03c49a705b6dfc499ccc3da11210c49ffc6ad4c1fef5e156c3d4239359fbe9155710575084a6763f9cbd926498f508fb7f6e726b4b65a305ef20779e076b6bbc398c0c947cedef089919d6bc695f8f226a55cc88d3737a4a97631378fa15a570572b04a00268f1a5f40a99577cf45ad381671605d3e9d4832dbee7c30c5eb4d0230458f6f40aeb32300042c101b90d03a9f7867936d32c2a8e41dc9f759961ce0c3dbbf96f7760c9678f14a6af5667715f70d3ba998f011fc5ee7a66b24ccc0fe8a07d336235a97d0512c8c8880d1d0102d205d131cace11c0e1d80e14520bef477b4a2b0d14f8bc6763f9461c6945958971bd4ac3d1814a300e2896169d58d7fc90a1904e74230e18e138ebec78a4191d1a738f92fcb20be8a10a3e062f13f9a996a0c3fb8f71f128c9f00ea6d2c0ea243e73673baa12f78314b4cf6f1480929c52c0f53be4b20f76288f69e3dafcf796d844aeb699383e4f0896a9a34b3925a83025e52b599da432b5674eb8dd68d0ecb312eec47a5fba4cf841b47208ce2dc44aed4512e39eecd2ad2198c985501341e1a3f3f0b49342bcd2468e9222a297191a56e427858bbdbb388331990fd236101c52e3c8ee94ccfd7235ba83b08b0f0156243b8de1fd63950ffa8db714cf69128558b693d18cd754fcd46e539609e186c640d412c789b528f24310d1068c419ccf92838ddad3a776a351e6b8ef36a41e2dbf36611dd1b5414b2b2ab0408041c3d22aa87324c51594631f3ae99b8e9d31427c653f4606867539328c8aca2518255f332701f1ca07037636d765c8e20468c5f90b63fad6ae5c4026987f84eacb532a33c67d0fb5ec473e6c93ea07cf1e0923cee427ef3bb0c893c61c26179696921730920e691a34ad451da011944b88a0929bb2d356dcfc0600912bd9f2978a7028ddeca7359d3c03e6f51641f02013efb89906509618b1156f9202b725dad37b189e6d0929a422f68351967bf15c3eed8d628c4e42c33f90aa9436eb90a337f86807b52ddceb117f9792bbd1909cba949140ec476c356d1e9ec412b6712535d86841b33261ea270548e3f5488275357f290e7b73dce2a39d616986ce2ebcd2d1a3805ee1b58fd3cb7d096edca7a7b0f585a0896fa481b4e566d662d006cd0c33bb9a2e2533294d006609962e769211a70debbaa80d63518e331590dd100941a08726711019406479274b1f1e03a7300de04a4020d8355a55aa4b33cc9b76356b9e9c88728d83eea7a026c93a1474804bdf3af2b78786373209424418fd2d1129f8031f84727cb14d4d718383ae76b2bb39c9b4a43c23fb312957bf424622f5c39bc2bb2cc0e58bc2ddd4179ec1dd6a6e33a52fb69915d40c9f399ce134378040d172299aa0d1e69e1709ac6e273f94bba512907ef1a24f443bd7cb916f06ccaf0e845b32ca10350cb06a1dcf31a0c56125d33183334bc931700ae46a3813eab8d3f322d702b9216a135abfd1b86d1a3e2bc01241c6bfd732014940a0ad16b214c2bd39584be9cac0e0945c2661238bcbcf92b30c51ad565c394aac5fd474b0a2cf6dea34292578933bdd37d592227559e5252029b28b05d179faf187b315a1b5a403973910952be4071be5469989f1d84d8c3255adf09c46339cb415d69d38a059e2b997a097eff749ca34811129c4913074701cdc1e5adc047b3c9108cf911b503ecea378cc34217de5faea035ce00da395517a2aa92ba745a9558ef9269f46b0ee7cd12182f8c306ccd29e0c7385bb5c90bbef53213371391a969b0bd3c4d7955223809820d063bb1bde3e2a1b453cb816d550794776a4f7ed929bf8ab453d67431db54862791a728c9b598dbf14460cb215b20a3f3ba85a879a9d35591fea6a9532aa0e9949cb95ebc3ea55aaca54c8657578d1402b6e013dc3b5d46de350f9295506d9ddfae5af68abbf50a667dd3548195a070fc86047157637156bf0ea3edb9bd1effd3b6b61bb4d77d224e238553949bc0019ec26a049c87bbfb4906cc55d305e83815f08d4f1b13de82cb06fe7146313d799e607cd7dc61f3508b876aa5681d96f44557cb89038a00f6c12d0e4ca5902af993aa251aaec16046a1c34447c4f45c87fa473dc961485e0e2c53e42065b31d65a2047fa0dcbce23abfd1276becbc88402d0882e6d1eca20edbb00c6c517b536f7f0566f0d50542191b054abd3179010bbcdef0997154ea15d63aafe9917a3d64a04a166df94effa807e752c1bb84f4fe6b20802b2b6f982553a82b04e8d4c2907b42a608804d661b813c650aa6208330b7e7a6321e77cc7418aa5f518a6034fe930368290e2c613af4a054fb425b13fb81708ed969311b835bf84e36b8a11f8312e14d72df746206dc9508c2e9ada95a091b087d09decd2889f06bc052ba6a46f3d3494ee9686426b660d1c6f7879e331f8e4da5bfa826a4246f23241550c99df7e3b40f6e182d7a3fc759a0bac5af61ecc21b3266efe30d2d45a6725d1cf0694c35928ecb27a6a3e874c4b9fc5ab6056e85467edc49ee96aeec720b11f25ca0695ad11c880832e50601635558b527c0230496b704ded2fb225ba3125bf46da3127c1f0e2188737933cbde7f8b7986187fc3c969121253fe49be7b6c4b6e74a2de78789ae7eb32f9c91e9ff51c0dae1703a5e2660ce9564c90f816c63c84d35090c7b5384607859426990a6b8d5d523b6f828ebfabac25a09a37d25a0a379944bbf6783e31cebd410054adf287be71d4e4b31d67e07443644d43c7353104b1498b73f9981296cc1ea92433000755d1e0e060c9dcb9eb3e860761c557cabf613cc1530c02af32a832dfd81d2b04a909952bdbab4eedb9e7d20d3d94c0faa9a9165a587fd1fe3adfa4b6c9d231c92cf7d42b3a08a8297ef497563076c1d679aed511cf4f663793c53d88409df3744a9295b86a9008ec87371a4b64574a4393d293e17e7a23473c324a10c50ca9880e779e4d5c10b21a62b0fbd799c398762245c4542b736a3f2650f7c91032bc6e852afc097e79e479bd046d3a2ee1afe0781a33c61eb8820989458bcc5c0f30a6b5c3b13d5fd8e14ae40868b6a1867c3437ae75eb98d183a5815f71596722a41f9acc28c7f924abc0d74ba76a8a8cb8a81d270cd1bcb653a94bbc2d27846a61500c0ebb712831d4367370c5f601e26072a9ef33ac93c42d9beabaae38a11424acc19866eb056c19cee297a5628d7948592bcad59accfaabca67055da5879edcb659d317b246b69e071c066385254435ea0e996a14606e85c29b4e15d6f609c077134621f8dfe7e4616d6fc30e21ed79f482e28206394202fabe82a70abff9fea2f93ffa26d4f416336958f1f36ccc594e0cc16d4867f3d2bed57cc7f360931558bf9afe6a260ddcb341a7e34a872e9fbc0e378b86d0514ebca02bf1c760909825a07f888bf5828b4d1a8ef61085989907fc81d69067b284284ad2ba84fe3449a8275c36a006bddd14cafec9a6693909be0eac1e1e9307fe232895fcd4f11dda651e52f04606c42f10041e23e78b90f16ba9359697e898c178466bac2dc631bc1a42b61ab6720f77b76bacfb124135374e3ef9e711b4d023a5f4e351f2c574792a47bd74d8561d66f3bff15236bd15e07d28e769f46c21be3f2aaf9d8ddb0195732a80939266dd69c1d56eaef8137ad87f473ef6e4c9970334be96a6665ac3a11e050265588b09df816edb734abf11c83c19181a1091090f7a9ca628ecf3bfe304a1dd83841fac1e02b1165aeec6fd8a237e41bcd782ebe7201aae08f67d12b4f363e0204bc6b028ebfa6dfd3a34688be9168f03574a5cefc11f17eda18ac7c9795d5ac4a152947cb407bc02c54b97729b11634b56fc7d126accc8c2b7e405d2e3acb11d85d3605c62ad07e85790ba6e6b3611f8b204008871871858c598c0a9d8f96404c45d0971aa433507613102dc55f01c13775da35cb20c82b5dfb207c54922ceda2f266213949d3885272577a3428705698fc5311de87664a4350c82ddfc08621574c5a39f3b6ce8b8a3592d4da206d0698d7b6b653a845331a2bdc44843be90f5d301489db89166fe815c1e7068b81f3f57bc81e802323d787df5f2df906ccf7530e13496886b3f3100f132ee27af3cac7d262b03610731287d7e434e5658351229501d72821c41997e2ebf075552451e8ba04bfa6b020f927530ce630f46d3023ba087c7e5642a028d47f70e48284a33a8513c3dd6fb1696df05b67282ca925ce3ce4436a0c616af995d6e295d4e5b337208778fac05e70856821b8cac38ebde6ffb4c02029b83a9200d176a3b85571568484b8c0ad7dfe2324ab318f48640112d73dbb31d6a5c1a0b88d0314a51503f954dd0a2b113583aa060ca85196163388aaa57615763f5d82d33b6b51c9848cfabdb397c382a7369262e25fd5481b096c5c67ca6e3f8bf1aa097ce10df2b4d596673c09c2a07242acb91f769e40f1d5e8b0c3b70c2f5152d358f4ea61fd6a22379c9aec507f35d49c88a2f89aaf36d18f105ec8a18118bf6106a33fb09ff511e829c3867c91afa4775d9ad6e9a12139159449365bd94d42422a32853c4c5262af636a266378e1fa46ab8906889d6173b3cc184e8288514593038bd9e90e5841162e014ee85d247a6f0280886480688dc2908ae9307b006f6fe9d7feddb73d8e19c6c7cbcffb59e0bd0a6ed74c0c3fa0b1e60317fc42c5aa532b910e34331ec6b1450a2ab9f2a8694d69175765de2eec9303b20ba9305dd0d665ef4d773e1a2958b2971a45c1c762059685be6a91a3966c68f2fb4f1890842c3fc956a1d274d48edb3d8e4712996aa000ed2865ced4cc7d0d4f5979b1dd219ea74fd927b8e5c49b81fa9b13f53d075151e8b865b017810c51512c20a6ce53d15fbdffc411b6502051a880ac4c3701bd8f2e47858984ebcd519c900950d25e52e20dcd3a0207c3f1fdb30adfe70231f14cfe6daa05877cb2992959f4864cb0bc4eac32a4d5ddc1421f7b9c42f9da7e96a98cef79be54f39fd9513dfaf1432c16ee1e6e79acc5a3ec17545ee1445ca247c73462bb13c89bc6dbe1dee037c014a3f79c011646cce02e74a4a3612501c986a98f8a865418809dd5a461382b9e1832f637d0192931411b33001bc36ab9da3b156b05def7bbc04fb6297685b1b86b4b2db98aa962eaebc0045851c077d882ddcdc79e55d8887cb941d29436bfb458830d0e40d7fca3acd7eef4d3c82b9fdc2f7707c3a0272f0119f5f4afb94ae46903769ead6a06a238ea9495f2bcea20e5ed3f353a37c64b1192463f1d163a95e5bc53cb12b5327d9c602d2533baee979b38491886e626e4c58665129720f2f5cbf7b7feddb3deeff6dfb30092220bb632cb475dfe1e9f4250612fef277f0d5a36a99dece4879092f845f97775058c8af580528ab5cd8bf0a120c8275d70c9861f0a6fe02784cec84c1203745fc87f5e57e34650d0d1acbc4186c3e9f1dbf1ea1a171174d3cceac37d6023bbf5b75806709ce9ca4aa89f3074468b6e6b55c8a587ef9394395853cb003f4867fb7c1d107a0189209079b2e3465bf5ccf75c5cba13840cab4f8eeff9f6b646327e7e227bbfbc165519df4058eef162a78abd15ff874e0d392b7c7d35e078b83c04c878529584392e54c8bc9902bc312162d704c5bb2809174ebfab77cbd00a7f6ae59e9f00a7b794eee43defefb7a67b04a5b165e8c4eb406f651843b6e2c8cb97c18a2449a1a734c6613fc2104705111a98f9bfcc1d5ef52e2286dfe72563dce9ba0cccaf6ab4e35bd9c1a2031160b262fd609619766e5e00a462fcf67b77511504468699f80c3502e6e8540bbabea241b30293301f10f533f0dda50f347db910f5d66920457a057203fd84c7f583da1fd75205c3d24042417a17734c104518f4dfb3a2758c61e830b9c53c987a9cd782d40f64d53b128dc49772d559dd91ae6ee54fb43f4122b8c12118cc02062b8a880ccec7802dfa045fbe850ec04186809b86b07e928198ca3f84934c7db938cee12e79dd1bd65ac3efd4c0fff9ea69738f54cc915cb69f5dd222d70671df26cb59059b5bdceed523b38d366ff64d59ea01245c824f6a9517164967fde2ee433a2e2a9a864442c7a9cdffbc42f6b9e0fcbd267a39fa7e8bb991541aadbc3f5b8d85e90c7d453ad107e4d525dcd66c7cbf88eab323c9891e78eebf291d0016419a7e4e7449ffbca3c4e6e6e1bdd7c96226b3ec3726c4d46424c2ce72225e1d2d4aebcde48f327eab4a0df7ce7353d4a4b9903abc196dfe033c5b133b150c5f2174b005d2b60a304cba07e8762097309181b9ae3413882b85c4e7988beb3f9f4b9f7e947260fdc1e2b894d206d18b7d89926518b989f115268402f1da14ecd71c70d8748a5cce375a97c8be5b10adb784e460baf14f064f33153ac85982ab2abb14698083bef339abee8f3e251f4c53f0c23ee9a0a0c771756940802a0a367f4ea83ef000e924af6f241178adaced8d405071b01d5053da16db08382589375373e9a21d17086300cb118e3d359215c9d3a92ecee0280b24854d964288fae1e9894b6e7752ed9ba085e89a1934091b8d11b2f8121acbddb9e480a03e8abe62e8fd1c3ccecc3b3f40631e3e942351de038cbde8780036a5509ace3efa7b91dc8a4592e98ec9ac39386393f1d83facab83923516dfa605de82bef59a28dfa0fbb1a7c6cb1e36c017948720f6f655de99a14a81e47b0281936ea41428220056046abf0390c13bf8153bd963eaf750e4b874ae87173300c854e79f94d23370a60a527e8695458e0e714ce71bf1678470093af4d6b67f4955484474627e24c3f6cd1c3ac3876c28365684e5acdeae005a87d237835ee886cde76f8face187bf190de7eed02b396093560add81a5c9bd15d3efa1032b20731d296ce64d3e814fdc11051af6512792799d46fb605164b886ccdb3e242c652005fa74f05e5c879c00da0076ce6b39ae79a358e2dd30e890f10232f7af6878409069b3d1d19ea7c3b8ac59345df2c69120e67b3f650ebcfd49cf1da70283e51519a910578df54206e1a09d5a682b806404bacbeea0aea27abd1a3da9b46d799d59dc0c1b7a40eef932c141dc9c25e95abd8eba6cae46329a2dbc68eca6b088b08e3a4445b7f1c74ccf27c8bb0633a3bcef32211b1c0ec1bfbeb3c5a710071f37f334d5d1db3c6383318e1aea39acac88469ac18639bec385e61f1268262b969b0692e0c418011f0fe3f3e334d6da211b9079c207b08a71a8011a339e59f1ec6d704da11a1930d995b6a3be1e9fb79022fb67fcf274dcb4fbf1bd0b34d252b02aea7a5ea5e7eb3c134241635459e9ee26ef2fa4dde8043000bac960c2798d44b9b07415103ca903f11eb7283bbe17e71e2e270b9109ceebbc45a54f3a30714b57cd0c42ed089a5633860f8eca630488d8d5571e2708d8e6118240f5a96764e4ce5c8422a80d5e0cf43210b85bc5fc39d10fb077ba9d472679911220152ad9b6c20a94c5d6bd657318eee3fc886eb0c07e0cf066cfd8782700416dd03ccddb90a8fc186754e8056cc523bf758dcc3cbad14ab068a0ee5406cbce7598d55501a414826b879565865f481fd6a477c839b6185c92cf07b0941efe70e72fe07ea27f1f8992ec499d958a4e1b4c92d0d9a8da1c4e1815fdb38b72f8c99e9c7e0c8ae67301b0d7351afb68ffc6e0529f96adf20f8a6787dfd407f63644e8617cfa210ba55ea80c5c9fdd43b344af800af92a3a680e1a185c1da4328448e2b9d0bb39f251038e46220850a4b37e7b034bf11517818a3185893a6f6d509707baf57c4bc3b9ece6f2e57157a062cf8d1f7c23df248a4b14f9f94e33d4b0c631a0978f014e08ebfd9528bcefc4a2184c0eb716c1ebbea3ba6a0e1a0f2b3edeb34cdc4d79f33096168796c34be75e1d718a302212157ceb5f3754fe0022f8c58d32806018572242fa7efebd52fb3fb73bbcde4063108c3e1269a3274d842ea880f04e56192128e669c688deb307380179ec25a9802ea78ce53088706c40dce1228e98c8e6faa2ca2d679cba48f3807b22242aba8937f165511d6929d5858705961796e97c185032213f3957db4e0337ea72f9f9228e4f507ee667d6b7d6631f38d188535e19d925ccb8eb20ed2ec45b410c6f6d0399bab323bbf9588a968d6eb8e0bc03e135c63d564372f5217dad2aabb1230e5ad6c93fe90a9ecd1714afcc580da5ea29100ced67d1c55e0aeb33efb1599869ad86560abca3c0a9228612a4dc8dad2ae31dd079ac34bc1cc0d4a5986981b2e87ea3ad27edd6196ac72de8a022db0ac64c4b871c9c4123376f188fdf32ac0951a6b6407b59100ea6255d4020e8162fb5122fc2bd0fb4d5a95ed5e0482c6600c52376ca0680fadcf70358a332f643556dac3b31d048e934184407f3202a9ec7d5adb0a55e2d044de040b5575628f898361746c5368c1f18c3aaa901af6e3a2fa33ce87b3e980e699cc0991f1c12d1b0b0f204fad75ec2de54e4d32f209984aa068f25a0c3df95a004cdc7297cfd1fea1d6d02f985273ad2e8133ad8ad37bd1c817790259fff3e4ba8d7bed6f35adc18d4eefb96f2b4f716d869e1c01602f32276da30e680b1216290aece63a77d682680414967206437dca760d0a01d5144fcf5270fb95b20568d4ab034380540b9f8d160f8857b599125c2686246004fe25fd2617dbad3be5960752c9ff2d5dc280a00fc139fe23077cc6d6023f34d266406dd52f3f7a8718da2b6e424d4ffeee954249d4e56d414cd2ea334490031fd3b5f192eb13a78f149526ea7b04f31df046e062118a0838e73fbadfa15de560e350c1edd891a49cbef7e25b6430107c06c1780988981dffe0b9ab92d961c5683f560eaccdf819f80ea53c27d6afd6e82f78a06ed24b220f25c51123dcbafd27534732fe6ac87f01e2868049f873f38e4d4715510211ca2a96eda54fe446b97c07adad887a3c9c95b6a2288afca599f672dcf9ea03ad4f0bb40280bba768a0f694b806b20df86bd59058eb9ae62af5d4657de09a41918d8900c20e37d35eaa70867394635dc318cdace713a71651382ef128c0000369e74677e95c01cf60c0c3adcaf2bcc74eb100c4db0760e81ba00a0522e725f8079fc777ff280e6f70f3ace6c474477f593f3eab0502987e0e64c4dd2a5920c126ebf4338354c7c09275511d947127702492daadfbaaa71f5e9f0b55c297e8452a126fde37d501aef5d8c97125ebc8f7436c619501ab2ad50b4d188de6a83f611ac6faa37cb97a7c94883639d8c59ea58568c5f4db5252dc514d92066c9fa0783ba82f85d094d546cd999d92e737c963d6a8dec5c1d5a4b590afb7aaaf815aac8060ab346b827c1e48023e7518ed1328c9fedf6ba216cdce652b445e705f92f662a0cdca3dfcf69639ba0b37c7ee6ec6856b8791f111fba6a2523920d6e7f21c86d4b5287362c4e55775e43ffe77244f7a54d9214e3d7b7c225989e536c81e8b2edde3d08b2f8159b7e404468f73b5ffc634981026974b92c1a92d749b86b0edae1cca881c95c8542a786193f014b3bc870982efbf8dde1e10b7107f3bf53c8b070048e6492c566b9f78d38c0ae0e122346c5b264fef61da237c11dbfff89ae988da0245267d3ff2fbf80702f81126a1ed7439810c26c292000b7c68b81245e08fe0edcc597199e05dfbabe2e603488ac613d2ee93a1535d5e562ccc14da4b7c8452427c2f814311c59dd627b43d88821e429be44988b45cda01e6a6ba14288f01b6d657c7e6569d86404eb5c5f2bf479c91bf4b6c70cb11ed1d4940cde3a21923b955c647903a778620f50d1e1c6c753a8877a29406b6a0a76dfbdea6b192fa87dc314d50afd14d2488147a75b30374fd0bc41b100ada13cd01f645d0ec1564a8231527e9539e696ab2450d817586fcc12007d1d6277ef10ad5aa51621f6236e4a0e10206d5b934e3a26c26a7d7713215a369c355eb032f178617d98cb9c25cd011dc840e149650ace3704c377679f895580c376128a4d26b9a80bfaa69c3d49610a6bb05c3894160f550e5a6d33dcc12808052b521fee1d5cc774b5f0ca1e93d4bd0bb841dc317f77297c92e82b53c321aec9e0463ca35409e71deb668dd776e139246aa0de2ea46027593cc52276b4c0cc6abe70ad7f2c3bf0d6dd0dbda036a1e2cb777e16dff3651bd4076936912c2c1e54b5e4db0b88c025f6b0dd771ae9f25536f9d3b8e2c10fc34feb9b78740ee101c4117af9513f8b8e5f130d3f3de6944de645191d94af4a0caea3d88b972fb6318556dd2ebac5511564e00eb8911f06f14efbf4bf631683399bb51a6cde836c451e6314aa880546a55953ff66ac19c27c3a176df6b064cbb140348c97869be3c2893c04176644348dcc3b0e2d19e9bc667646dbe77aa84e27c300c915544baaa8108ec252c1189f1a5d0148d0b1847068d39650bc6351d131cbee20e0924521dfb92b710efc940b831e2b4dc459bd83dc011cee13f7577d016bc8eb7e3e445d91ce55c9dd586a7e129dae6f6449754bc3a1709249710aa13f21f1420e266761f3513e0bf99309654a569382f24f41ea64ee8a50fd7d8f30e79ef5d5514c9d13ec114e6eab688d2194c2de98bb885dead08a53067f94832dc6d82eaa36f9a0582c1c3ee18760da99a914c7d765286eb67f1ed3060b8a97fd3c890e31f60b51554db0385c7c10202e7d0a55326d917d34c119abf390cb7351c37f918be8b2addc15ce4c7c9aeeefd439e1677ea17d51d6a7bf42fbd3433123e3f760704f4e61b587e2315f58ad245b252c6b441bf441d1e337bfb1715aecebdf2868d6a5693176766c16a876ea4b388c7d2ec4677ebc3d99d1470ddcc3af141a2b695daadaf71ebc3de7b9730bb7eb79d49934160ac6350fcc70a466c2aa44e408e2cfb8d217380aca995548f62be452c4bef58284eb10d7183006cb52d514ceb9b2d93595e47e5d69cf750d61263251138d3be04a0d37359320fa1b6d93b5ca4dc68dc379df8cb28044c7cc3cde42f0f03678917e33b61b3981521e83c5721795e4bd1c8d87c2d9a87eb3335a4c7e434fb2d29f64dd6a90b349a4bcbedebd17b986944c936da8649fa640580d88dd08e99a3c95571120978a64354bcaa0e152330f3a40af99b54ed07d04e1835e6c13bdd26cbb7f18717a97573509a37b26f9fe0570050fa5c7703458960bc67cacf81deabbfcdf4f7c2dc2324f4e11422ced34d727d5e1bf5c60d43c86dce606f3c6970f2bf61ebd48fb8bd2d7ffc41b6ffbf3c0d1e6f32ba2436601e93df9ec02fc800bf54aa8b620a0dd0a40ab90e8cde2c1e7a333503590c79c9f1e5280390180908ee0a301739c044cb3ffe4412cd0103b605663fe29bc78a9c24f9409ef65eb7c188d28c288e6c5d0f17818b70fb73bd7fe57170b3923c85d4c022567435e697140c525a3133a28a6ef0775101765f4ce70b58e7716fa3477ba543f924475e8f2ab08d053754fbd78dec0eacc6739e64f9d5b10ab1653fb3cece3c4c420047ae4b8dba48ba42b50035baad037531e89de359b75e4637d743329437906d56b42f6331166dd6510a1818b51552b6b6b770089f301c2640e2cc937889b5683a9e01c5e53aa88a73a6cba95c44f9fefbf97da93355fa39bfc30064b8d5276b3cd5452cd7b6baa4ae258778261e786644ac31964edc1322195b38a9f74b74785a54759bf591618c3799abdd7d78d0754666fc4b2192d19bbf7ebd6321597269d8127efa4604800fcd3130a3ef69b8aa71dcf52d08587500c2fbc52142b047d24edd98dc68da2de097b15417c0dfde2bd27ee3542fcdaf48bf44e482f8bbf7f5cb10945f8a463f50ea84e18ce538adfbf4f6e1d372d61ee735b39b4b18fe83cb93a99af594068d142079c26ee03f95610aea51e854b5369b8ee22efb52de3993a85769d21f59e28bea9e14b0701fc7b9b0944bb1513dcce3bd5cbc561efd30406096a5ccf59372790b5decf085a16680dac16b6ec0192299b31eb5118a071724d0c9db93180feb0c3b3da0c6e3461cec5610aaad138887ef5d06ace81048ba0be9a5cf76002b86ceebc6d33be3a58b0cb01d7012eb5a561047c168464b4811c424d58861d047aff97baa7baf20109e3eafeab330c0d567e6586005105bbe4449dd45e3f7b207172403dfae469388945454fb897c1f76a7786733cc600b3f9052c5a73d8bdef08158e5466af39413df3145c776a9e3586b50105b960174c2cef114b1a762130cd0a1a15be621e8049c8ff448212b6a70ab6b3d6dd3d1da30c1a9e5a5a1e0bf166036be53846831434e4d7460177d5667536fbc8a536675a1040d101cf7e93a931fa182460f2da3811c99d1d24efe4e212aadf44d74c480b13634f2188c498d2dac0d2aea0e1180d279b21f35e15d1368986deff6ccf089608cebe704d78910a5ac478f442bac79a2bd6851c3ecdc5ca364b02281e44047b10ebb59878b08821427a65c1cf9b8e001fffe98b040f19ee9310d53328a50359bb0f7d497a9d548dda248bd047fcab1be8fce58a936c90e7e88fbb4f434459e47bba1e3e6347560407f5ed9cfc36915e003428040b249bec7f5ae5b5ab345a2a0e1d50e798e730a7872eee6e488c42e9241b526a3cce7a59d84fa6dac636bebc61a3ba425e4e68d8a8c23a6e0dca86b118945e37c49905802cb0aa4f1ca5c38a1610ef9c199ff94604c793e7fe4889e8b83f13b700d08f687433e48f2fd3609a858b7c8b096dfd3d9bac653e50f5da61013f3018303d62a4e470366e71c245b2951a42339238e141abc1c3b5f0be73cefc5f67501308623dc1308d83aa2dda762dac0cb50054344eeb36061fb3664bead35e8a193e86ecf62fdd6e9134eafc700da10edf7d8f1e2ae468d357a0a507a73d890efc81b93963ea8db641f680f8dfe63e25013c7b3a38ea171740a086a8d0ff392aca79175c20b826b331d484b72fc90ae9df044fe714177df48467aefb3be47baf99d6df926ea4629887d52cc27ad31a0146645471aab2da813168bac09a1d2aab234d1ea6dfedbee0aa6fa3ca15677d9b678ec559baeabbe38e37ff24b89f34d883f4b7b6fd8a87e39b89e6212caf331e70953f780f762c399583437c95ca571310ee7138615bbec210f0d7710304244b3cd920a74e537eca85d4d34640e1da77c0b57614de382d18249f4c445f1181fe9c2122310deb0f0b2d0391976e16aca4c1d3e3137d6ae88442d01e5422f2a164dec9be2a7bbc763b250740d98487c4613e5f16da09b9d8bedded209bde1de9d43c6edcc2e9f85be3e1683cc4c64188e2a4642f248139353685c26d1eb0b291779962e2d6d3d53a1adf1b745743835e1af588495586733a916d48cb880daa017266aad39045c31e27691f79b5bb6d6bda723d9fd60e04f095bcc83668cab06c0755ab3066e684c443720939d8e8522a025ede6ba613d6b05d5379d6a78759e9803edb29b32435947919d68b1c724149216e4a10e38571c4a825e590135a19f316fee463c6d361f554fb0cb352b83b8744dc6539fdfc73e31c65458b00fd47310d08c41f861748baed1afb3f1b1770aa6ef1d69b29bd854c7c6ca5f00309de64022d07d82a6751646ced3b744f4e2fe340db245cc736275bb8f6401655852c5861e5216a5ecdbcc3d306e66f8a54266c3742de60a984093e7855584b20bca97202e7ac7885c0e60a86b247ea3e78acac0041ad604e374ba0d718e3a861ae9679d271742826ad820a5c39de7e65f80acdb7100eb4a53a26575e30fe3d53aa5469cdfd3961972db4ca151ef905b4a55398888f0ccf094041fe3d5bb33121958f5efc3b6464a088cc01ee1ec70b3f3406dc72f3537c9d2fa174029815660af4159d17eb9702bf94f22f18d933f320b29afffca3d940317e6aed25bd99801feb588a7b244e2575644f7e29267476723facb3cf2d558145ec050ae81bb885b63fcbdadcc6d98fceea04e3bfee46b434065e0e8faf48d9d2e8ce322a718f8c9f18a9f19dcaebdf16857ec63a4f069d1ed0a251e9c2eb80a8b59c286d5469a42ec4cb9bd6864a42514ca2cbdecc0669e13ba23ff607e780725793b4fd165087dd4853e61c22237f42ae7801b95d082efd4967a16f247eb107926c56725e4e6a48b1c334208f8551d93020f4e13e43d257132a1523299ba810819872a2354a8b92358b3516291f7402c53ab74c2606c01676bb17a7671e30e23d55d58920f2e52308f6668014f18ffd6688441cea2a8f5e8b6f3910d5ef85968163b86b19a36523777cec55ad10750b5f42dea88b901f7faa18f0cb92470f379b0dd010f09c234121b2344f0b305822850d0ed96328c2fffae843755d432f287a44fc07409765910900039a1b673cf1944658ccbf3381f08181c209e9ef004dce543cf0db09810f317a374bfc20ab5d2c20f118095ce1678ce6f382dddc4898f711c7094e8a75c9cc8d41617eefe288053f57f4a07b70e882694e791c319cf705cca51b371a2cc370f935d33bce6da6a1ab2ffe807acdae95a424f06445012b52ea7a7523aba04a7d35e7900ed4b087a1af6b2c68746bef8cf21cd1136277b85cebe87329a83a8561980e7c1a3e2a7c380c811e7c6246cff78f5269f55a2ded67265680fb5e5400835929b49a651d5c9731be456bb5feda4929b57ce72e4d7ce956fff1eb47b80b1207b18130b93c1f74ce4cf5020e70e3fba597f80432a2741cd223ddf81b0219b6ec0f4c76744d52ada2a3798ad469f3c59f1a9a1b1861fa6762e2eb993fdbdc6c82c7525f89c6ddcc7ea5447b14eb762073dd44dfb1a8262c9724dcb26593e95ec742d1aa5e0904f05526cc8797c06b5a5d325b062483d77f4b208e7cbfc669efa20c66abd025c29617693061db0920f28c4d0448a69e649718f7515c9077a6c18b115794e7a931737e214b5a6a106f70021162f425c9810ff1f759109abf450639224e84c642ea38df3595a45cc5086e1576c91d5a022f90f22ed1e3c3074a648d7985147f13981e5bcd06fcbbcdf15b8ce574e0ffd5f0618466c672be487da6b6ff08cdd3135d17f50fd78547657e3e5e8bd819a465020d750018174efe37583d2276f0f21140fa1188619239e51e74c36e6851cc35cfb5d73f7d30b3cc80df11e8cccaf361751e991bf03b9e64f0c1d5e6a409bc7310d6f2612b0993e0964b59bbc1eb0dfa1bad2db7329a6966b54a4bc40da31cc78f5c4c763de0493231957aee3e314f69eb9120391558dac7df33878e98f6c65fbef0eb711bc4a64b8ec57279fba51aeed2386e741fd4973af8c57f5a3fa8a3c56c23003db6a30456283d7e15c8ddb74995d8fb6f5f3fda6b3859c54bdc11a38e79859f9bf23a8e12102633970550c31f0d2b1ffa4601bd4591dc198393fdedcfbfc4ab86737d47dac411270a6877b7f43fcced4059788a98419034efa0935545f0d3710bce0052618d8a3247a974df6205bf6a62219e5e1304296e6f78f61c981b33d1204cc765db3394f9b120300312fda7f7256822a5f22b2737333500371c4f0d398ccedd0a37e73779fd6168c179a375b75af6f3e87bd1a0810be5146754aa34770bcfb9a69d77fe729ee67c0bca84d4482954b199c286909ec0db935660a74005afe1cb339aea226d524bc2e3601ae86a4b8fc62da7bdb7fa8865bbab955832764d7957acfae9390b8a8efde92be5067850fbdd4803ecb81ac3eb0c02f0749ef0bf9a76073f8f4f74cb0a0081eea7e745064869052ad5ef162045f76fa57d807da839ee551cc90d1a31c5bc1fefc26a6f68efacb445d8c53e83656d0395192f2fae66ea3f7428569a43757e5965f0d0db0d5f3a464035bd68575aeb5e86ef2b5dfedaac28bcef4bee48a7c0e968136c50d1bee09c0cd00e3873e682816f62ff009cf0b4d5e775b2d3ca145000b6be6072379980340d21a57574fdb275dc02be0d68e2765c74a2933719fc3bb7acd6481f894c44694b15087c7cbfb38edb248c1430b1bc6c31bc9e0149387fbeab14fe04f58ffb08980cea3e8963ddfbfaa12c39a4531274c53519990f85c3c6ddb9bdded671fc8a2b3226d8e9b631e670660090ee8c1b7ee778808046735a3172bc4f84a909e8f99f8b20a638dddfe715cbecd63526ecab86b0be8e2315ffa04795967457ecb31f3cc79bc853db6f57b1b893e348097d048e6fcea3212f7939be3fd2bf729b6b9b4d03291d5440f6aa4b9f3800e3ae71a912c936e21045fabb3ac012daee51c287d862fe6e9bea068a8282e4298c5dd5d700f1d186d66d265083802e3833d22bc4c8bffaa7e5a13fdf0d4d8cb62f3bcc2d082d0d80686bad0b22bfecb6d372d9a23b3558cb21204bbb71b0391bff29eaefd7d3f9ab6c246ce90670f946ae04cc7818404670e479602162168a82f3b5633df361d60e08631efd97914eb0c6c4a3dab269be6cc1c254e706033ef55d074318aeeace4d0d05098914136e8b39db318688642d515a41f5a5e9926316b8e883301c49013692aa450bb725e5607385a4d296cd540ad7cfe8537d5bc407471e6247efa5571c79aa403ff88fd0c4871f579ada4a7fcf2b97c8902a93cc914e7b9a9278d53980effaf8dc8d352675b24059f16e887d9695f4b6dde500bf4e4c9ad42d9ab94e464f5f0233008d74dd500077669486db05246f0940938c7c7300384baff0b080131542f040481be06d70fea2cca91558cbd6d2b3518848d016bc9090b79a5db829c61b555c4f82828b3ee22345b7ba32a5aacb5d34e7b6d19d0ac09155cec81296498843fd988e3249721570b9d01d051f8e1eaed0822b9dd9e333fdd79e650abab159cc4c5ec9a5d2be567feffe728747480d616babc7a47f3a8ca3a532fe30e9864bfd7282e87644854999c1dbaf169f7e5db37b4495c2ee1e5ba3df90a170a1a7944d34c8e473c3e4903adb0da13327991b63b2a599b9a8d0ccf685c8ec10f77f694e7cad8255e91c389056ad3d459caf7669937cd6144bb273f004b95b6cf01a4da2dec2190248c272995bb7bb2b44c096be66a1da0facba20b676116d610d5dc44ea690cb29723b039b293cdda211f844ba9d350aff9db605abde98cee9e43c83f33e8b057559e813f1e5cb0b0b02a4c600c7b72e0bf3ccc687e702b2839fbac06127e74a0bea944ea69ec244856fb7757fb2ae4435f130144ba96ffa46e1439250c6641076c7a8ba27b4043bad1a8ae37a99499ed0ddeb7cc5574fbd85ef411ba2a3b461428439fd05f9d39308fc2e76f12d65dc01530912002f8094a9f5bae6af3cff7f485ad64221e6a514432ff02c95b9298fd89eb552d08ad34aec8e150a0b5b4804a92e95f7bce0724bca9c18c493baf9f176bbdd6b82e02d06cb14bc622ca5f1e298106d19966d7bd6854b12d3fc9f10ad91944754a3ebd6c1a3c8d4f2cc8d398257d09b866060b787abca334d11a78058df4392e8ceacade9673b403120314c2afe32628e4f3234c0157b8969fb4170f63c608c79a3256b8421a395911137d285022be812c00a571713333f53d52d45401d2d0e78d828d05c93733868ba43528643bf39021b8a1126de0646be2405c7118c5819e378c1ce121ea3fc8b07674d6d586596ebb3c350d1fad2ac466212c0d834798cf8e2487e3bc1d83dba2a090afc820e7c6c88f9bae35a06ddb85267349c797d672e321bd77797a3116a173d27741514bbb06dbeadc32f2b4599b0da5f07a4510e617c6324550a308aedfaa2d0c49631a8a6c54f7aec30c284815c40ca9886267c106ec449c1cf6ff622441023309e6b7b66a259a33a6afabe1c9de07eee3f5872be760d4b91c7dea23d92b283d54338393910ae5b2556aaf96ae80aafb1aa86a7feadba2470b29fb3217cf40b100350a4dc7a085fb712e4891206c59d3321bdd2e9e041f2313935fca051610473d816839878111be37988d9cfdde1ee4b93247a919ab3139c5861a675bd9ca3702ee14d9b762ed233890c116c26c8df3706eaa2fadc2502172ccc8df5b94851864426558dc859970852676132bd8abe2a695b35a8434a656dae4043c42a17eb307acba4f1d761d2362dda7f4d1ff21fba4b9c3dde04862225182dfba3046a687d49d423fb1f0941b22f0895fc07d26f9855e1345447082f3f5c9140fad1077e6c5be0359879bd0d972cbbcda537fe765f6f8dfb620b4cb99624dbefc1c302f68633c3b5c0351fd13ed3e3e84f910789dcf9c049efd5e9192e090fa57f54a8bb6a661a96372997625ed9d2093fba2de43eb694d4729c200e41035384371ace39df221a8cad89ad94fd145b0a01ec760a0cf6088b3f70d1ceaa1a5085c1ad6ae91a4a69a2c4a94983a9b6b33169b909ac54a4fb2626062bac4abac27d786db7016188e82dfd582d5bb68a9bd93c5eeefae1b8a9755193023e43a143e74dc938372dd6999a49a9fd08322d84f7a26f8dab819c4661520f3e4b08148e4a293ae20a78ed4a02c59936d5494f61ce825218a72428ae8302bf988a603f0b670c9ca58faa2705ccd5f07ce2464b9b1716ec8547ca0516bec2208fda681dde93505e8741665b9090a92f1ad52891a73a12608548f3cd9272a22a058b5f173eb8f803c3bac30f8fd7d31b119a3b4ca60a00027d1b5d977dc77f64508789b2ec3bd40594da4d219b06948960ca8ff3bbdbb178412c6048e50bce58f40e64109f72510d24d87b130cd2f3749959a10a116caa1491bea492e48ad8354d13446368650b3cf81bfb2a8a637d1437bf4f16cdf202abb081b112e04087a2e6824d75e9507092a3023c7416e12a3d01c298904370375f3b2b0b75c4b5b76abd75bdf1309447cd0e5ffcc3f399111eeda97089a9722be42bd461df015a7872975506291d15e483471605c24538fb3fff3bc63fd325089264d7b968f72f19daacd4b8256f9fb808da3c4a7e26b7685699afbd9c480a0b87afdf0f9085a54824dfb7dfe768c0112f20deb9dd803f98d37cc8c8754ff9016a5782c1e906088feef34d4488ac25a25886be8c03c60bf8147ea628ffdb4a0169e509df98f2e03312630205c82234c7520d8d6799c368936d47ff3814a5b84fc86b950b4dd007cce908dfb8034b3a81f8c44c063d75f34f604f39deaec7dd9d40de58df1d78a258afcf87b884225fa6b5ba5e12efe77cbbd005513c4453fde385067548b32c2cb635e2e2b68d96c19c54a4c33fbaf33ccb7f4eecbb0b770ab64b89e9d6239952d0928ff93de10c5151b9849886f97851e97bc10c5d941c693c478fcb95d61192142d408b58f0f4b4935d8d96ed108372c077e70b350ac0b1e931c251378224d80a4d81e1b4130fc151e4bb55b80bf35aa1916aa716b488fe0f0b046ed39d90abe74f30cc7aa816d38c1ac554f823c7e2a97c211ec7cd47251407534ded7d5a9befc03ec1593a66b3de56f9fc9276c4797df7b2a39484ac08a17e21e1a3ad27f9fb7ffa6c2c3185647fb5b0c8a21614e9be4f94e94555bc22d8682b82a9caac376e6dfec5ffe6f05c583651760252ddab743c9cb8debb0b44c8afab794936f7b32b400e30ae7e8f1bdf090751b533d9386ffd092c45ed003e42f1545c913f28773e4d9a768fa5fd253a38688cac7c5c5e9eb70261ae22266266a025ad990c7d338380ac780e2a9b4a61bfb8a8dd4ba046cd0a0cb8325a028e4f8e6655886fb66cf817fb04c8636df220e59096bcc965ba24c2971840385ae74acb2f7da5377fa19b69e8c8c13ed42d8985c7077988815b32438700eb792f698e9599a8813ebeb301cb1e57b82f7066044a8198e97fb9c8b17ba2db9d2050ed5ab3376b3f1cfe58775de5abaaa7e106e0fb005edd5b926ff0dfb80890a20a858822baa4c1983a04086be6afe55b40e6a349b2f885f48af3b1880ffaa3a55ae2bc796b41ec4b042e1684680d5920923252073580551cb8131241d84541088f2e0a3a53521923719a9bc3b8c6b07e59e32995be763e8fa5396bb145323e07838922c2469c35d984740ee89cdf158b4b899dd27fbf37bc831f32ce8a9446f6de7befbdb74c49ca570dd20cf70c9f459cdb9c19d451c2a54a38f1b4b1511ca791511c7f72cbcdcd589f6f8d071cc29f6c34ef20cdc946731acd538ee2f88be6cc8ce2f8098cf153299146669e7a954a9d431aa7c657cd5451648454b65b167f95169514152d258e34b21ccd0c0c9a2734517c467572e4c0a71cd521b1ff6892f8d46526770283ea34d5e9f3851346fd3760449dbdf2b3e461c0a790f491efa72f5bf234e05348eaeffcce498391af4f5f411c34294662d2170b267dcd981904c1cc208d19430dc1cc26eb94c3fe3ccc187a87994d1c66b649e4f0cdac7a02fbabccbcc30aecffc198315411cc6cf630b34d02c7cc3702664f7d2de0bf574e1a31e9eb48d881eec327fdf7a00c8dcb27bd2d87745ffa67950a0b92f7d9c162c4f4b1d22bb7c282ea4b112bddca5fba159e66955eb9100b2a1b12d55111d88e50b7f294236feb569e62e4a85b79ca0aff9d66cd84752b4fe1f9f034856644de5f1565c8fbab8084bcbf4a0a02de5f4533df5f45e5df5f5553bdbfaa8acdfbab6eddca017813de5f95a55b1900af43d4ad5cc29b372dddca3fdecca2c21de0fd555b5eefaf2ac2efafe212e4fd55395517f0fd55462a2f61ac5bb9f5ae9f6e651f4f826c842704981920ca0940059002843643055513abd8f7ff5bb772ce8bf0fe9fa55b7900dfe3fd9f08e7fd5f0b8ff77f5cb7f27dffdfd2ad6c7aff2fea568679ffe7d2ad8ce3fd3fd7ad9ce375bcff77e95696f99af77fa36ee5984fbdff7ba1797f33d6ad2c800fe1fdcd9f6ee5d5b3dedf94752bdf3c08ef6f3ee9563e7f478f7d39e0fdcd99ca345bf8cf1c27f0a23913814f993a4c25df03fecd47f2ff038909a7125045841c72e60fc9cc9f79c8ab9098f0a953c945f22335430111c9dbb4f02c9c3e4159c7eb9c4ac03cf3259420e4ccaa7f04d894702ab9978512de274875669d6fc89911f0259c4a4013ce5c0288c4049d572151a9ce6cbe0d1295cdfb0421e0cc5390bc092c9480e47fb050c28ff709d239b3ea859867b67904fc5380851990859438bec9c2f77f2a2159307f0a0b9fa2c00c0b2f52c0049df7091acf6c3e91efccff431e34c11d02e4d4bb4e25e0ceccfb041d60e77566f0bf9df1d3b173809fd921c06be6470980671effdbf970026091d32728bb9e8453099875fc08237c0f060943127e8453c92df23e41df995d1f9e4abe2fc43fc2a9043cc09947007782b8fedbf9be338b1fee7ce1fb04e1334fd9f9204546d879029c4aee0804f81e871fe4410988249cd9f5af223ac02224a4668a804546f07d825c679e52e44990808e229f92808b84f7099a39f3f80710cf0cfeeb5126cac8e9139451c83c5e8453096844c7fb0419e08433a36644118cf038730aafc3088f1374fc0c0450679e79d1c8db08a08e9c3e41d900b907284201ce2c3e0a88b5229c5984c7e9f13e41e299817c283ccea9044ce1cc38a091020079d188289e39f5d6c869834f90e81384c299a718f9021cc131f23c8ee0f0789f20206716bf00f6813c0a0206f8138e88001e39e1c8cc99538f3a32837a9f20039c79ca913f0102221cf91e1010a1c7fb04e938f3cc1be0cca83fe16b92f0ec389580200a21d4a47874a41210c2a9e4f2a880828e53c987cface3533c4f632af9f09407d4ac70fa04e51432eb413895802180508383862604d6994378104e25770556eacc305f021e07cf8e57e15402f2e080f9144f2a45c393a2799fa0049c790acf83b0c20e9ec7b102083bde2708e6cca987a1791c5f732af9f09424a614403c658510c01550b86f5a41c7994d5f732a21f169834f908e9af7094ae1cc53567814928470e629b17fadf0ac53c9c536f80485c07a9fa07b661d7f4d67ae21324408020ef0c2410c70420180986006e0754c007c09e6cdff30cf77992d124c1ff5e5801fc1bc790298e78b60e6f43007d02f073c8e79f33cccf3433005c032573dfe72c0e9131480d3b4f5ca01709a587ae50f8279734e49f23bccf39c12fb5792d3bc39cbdf3473fc9b32af3263dec63cbd0e33c7d798329f32639ec63cfd8c99e351a6cc8b66cc5bf3f4a399e334817ae50f9a32a729a557fe9f19739ab4d36952e9953f0b66914762eefc0a6691e73177fe8859e4346b3ba729e42cff9229621ca687051329bc6018f39b22968512d8643a1426b732f0353d1d9e30e201a6650ad4864ba739eb95ff204d18ec8f441a8bff9da1991f7f9fc9f07bfc7daef93efc20fec13003ee3e2fc11d06df474eb7421b9ae0d3f203bbfbceeecd59b74c2077edbcbf49735791f7376bee32f2fea690bb8ebcbf69cbe60dfbc330c5474c1a9a1193a6884933db3169645cdc4c235311f5cabf01a66ae62c7f06982a2067f92fc054d19ce5af0053252445b7e55af3abc1cdaa197612a64342349c5534ecaad9006296a4d04968a3753388ebdf24d8bedd99ddddbd3731edee52773748bd3a8e38dc553f08d626797ad5e009762008762058bbdad5ae56b05a13f47610fcceb305d0a38d2d3808daf7b301e07f592e4882eb7567925e9d3b9f15974559fd2294d04d7174b88f332d6a3368985e3163868730bd528616bcea16e87d1a975e29030bb6dd42afba855ef55be97eb8a00b9616edb4e0aefaea6e411738ffc8ab0690d83b4133bfe0df5bc11e160cd67f8162c11d78839b4118090523bdc98fe910135670bf87e9101334dc67beb88a980e3961c26404eab4b859a489455017118dead4dc13454e884974594383fb38868ab348c3eedfa51191c859fe37ecfd41da9a1e1ea9c98a4b87ba88e12cceba95c52da36c76d4ad3cc244a36e65f1688cd22d58b7e8101755b0ffe8d3e217b1a85b74880820ec2f7269577d8d30714bb7baf71974c0dd0942b91d6e22256edc67071412e1d92c0ad06d0626858c2e22ad8a902d0b4da48935918a586504f28c18e94dee8cb8b8ddd756e784aefc73d7a47b82fdadd013f69a680fa738fe79c494c430d437725c8ae3368ae35fcd4e46714a1dada37554ba5a879375559e74b30e4b77eb88ba2d9d503703ea562e75513a2954a7df28053730ecdd8b823646365eb8601cd85f05a4a2d22e5491cd1731b4cc08a97e54320b13630d9c5533a03182c059254505c3feb6f68c50b7f20cced6ad0cce5899c152aa70aa6d6163e42efa1ae2ed449cc96e3f4e76e6992223dd6b06c7459ec11561df6144715c9c9d6aa72a27a153d1a94bbb646c272b5da294804e594e4427dc89cbc956022ad1ba95c559494a89ca0e20aa53dfc519d5e9f7103bca0b5411c5f1534de6aeef297d2b24ceb0dbe49ce55fcb22ddcbb34ee03c83b391511cdc0cce596e636473067585ef6f6344752af859d5e4fbac6282bdcf366c60ff19217785a78d91b3fc4114eab3319a11f26c8c6684b238cb01ce3b8690612f01617f7aee00a2385bae794282e2f8cfe0b07faa9bc175abc3fe335da860ff99a26ec9607f9a58b79eb4ab03f29fe1d22d7a258d1b769b192f47d8bda8cb159c6960d8cb2e4538d3fc609fc961ffce561ba3da51b7f209091b2fddaa2cc06e04ce365f1660310a5b9c6ad8ff8404d5b9817993de4803b7461a4ef12dc57c03ce7a653b0ab6f5fb473429ae74ac4be0d1f481c70e4d7a458d30302d09803bd62b1047ad20683299683099faa79f604b021e3f3234c758af6c772199f566d62c95c859f649b36ee995adb82737f7d197549f35cb9534bae01bc5b1dfe4b651e77ab5806bb13f89a90d871bd4182247754a6fbf7feed730a8ab028122593ee969ee9957b79aa338350c4a3af3cb00db66229eb9b20154cf006770fb69e7b56bad5d9d7ae79d77de75d5ebbe69fffcdcdcb95be7faedcd82200aceb20f82f7f6bf5afd98627a232fbdb28f82bbea91b3ecd77e340d5b7acb3577cbb9e56e8fb3dd59bb8c2db835d72d297d42ec5fa6b36691d22b1c6ea59d368dfba9517320786fcdb92f291b47199ee7d3abba02d1466bf56a9dcf0fa61dac57d607975628c6f2884b3f773022b09771e9d7bca46b858da2ecf6f7090e81614f88e278252fbdf228156d25a35e79a5a35e791fa479f8708944cef2be8a44eea2215a7d77e6475591c8d5d528cb7b94941be509d6a1cb8186087b1f0373178d2dc7c4c092c9cd34361a2ced2a19659a2cd87bef881ff07dfa04f0bdbfb06ec5c06aa6ec77d85ad1864727b24daef717467546ec3d4d91eb8558322ebdc28508978cba65dffbd251b72af6bee4a55bd5fbfc037b9f738ec0de671c5dcc59de5b77816fdf444de8a8d41ededb07a94ecc0db6a20dd7ba98bbba9ab3a85413686cb587f728d4bdb9bdae86bda7b15513bad853aaf3d1d8868c40344531ec3d68ca7eb0f7344533ecbdf75d2ca609f6be868aa1dd0e284607ecbded6231343bb86ebd52eb02612ffad5fdad696b14c733484406c7c0288e949b4522ec7dbe30ec61ef4522aa1383bdb74960fbd926a192519dfa9e149c496c698ab0f73445345cda05bef75f7db05612e9f0c3ca303ccfeb9ede649a1aaeeff85b81686b211ff86e1b8f4e76a6c51fbe3fb171766354405ba5a28e018a914275bab403baaa3140b896ba509cfaa51dd016a5395c2aea56898cce750b2c19957245dc7c67b959b7c06e7d343cb83149784310e1fa370515e80e4175c4af7fa3d09678055d41a12d91a857f5633a356e4657722e2852021bd175a6876b3964c430d43788c46018ea1b43643aec47385fa23b8479ef8ce2d49b82caba82b66e1491a85b170aa5f5a6c6152317e49428c2b5b2f23582268c9b455cbf11be88a33add33c1f54f372462a8f8cea84efddc31c9de1048e0f08ca1e2ec0d81eb77015d0dd7c666e8e9cedc7dfd7e1af07385755f777ad503cb75865eb5574da825f882f8d54a85f54a347bf00c675cef51e855e7f1bd91c4a23985fc9e7691df3f43b76cfd69565bb9dfe3192aacc670f73b3b7062a0869b97d41ef5eb9357d8b8c286143c365993f4aa056ccf16407ce617c8cf6c210909eef799128940af5a063cd6af0fbe40e2ef6cc18bab42f8de995f00323ad9959e0bf52d083e117ff004f281ba04775f8574ffa457668dd22b201910b7cce1e61f3ffa4b2617656f4d6c1215782c5910db47c0009ecc60044070a922c3e2659308810a377239a04931dbe2556b46264ca9097604d86bffb0461a389c83a94d46c34d6838044c876251b07d7a93abf7bde77d0f82f7a250be72f0b39d9db9761688daa3bea556466bf5afefa590ea990a400103ee229d07eeef6e70bff79daeb35a0337d31ca6380c64b0669a4b42bf69adeea09dd1d6ed15bd6137813e2723bdc96e841dd411dfa6b2dca6d61f603faa2cd0ac63d3d3e2b14917cdc6a327d42bb7829d8a0903d3d676d32a4deb551bb5b64680fd69ba2937bbcd76732277e17857ccfb7b51b7b878ce853c4abbe8cb7f3409b144c01c1ff52443774f0f9a3018e045d3c3e069cae94d667dc59897b2c0c7618294053e4cad270c33e05a4f1fa429752c26759653f11aad5725a6b61bd8c06e7aecc79256343b3cc68004e64d4fc600be0cb22a045df9c7d096cfee89ea8c5f69a7c77ae56f327b70bce949f301d574d90fb7fe8fac57ee58288ebf0b517a7f9ff5fbe330837860def4b41422d623cff5a81e75eb4bb7c41864b8b87ef8f58b63f19b67e95514b4877f35c7ae2b85d42fbd72583dead5eb9abebf1e99dedf6bdd3a39269d3174a5997d8a98ff74ab7e9b20f698bbc097bfcbec8fb66d3b4ee155e7141c6f4d1083a591a07e1c676a07c7e3387bea8b06d3c338a6258fe961fe7e53e926d455893a46754aef60c9ec1891bbba0965f9bb19d887c0de50b0b713ec26c5e50c6ec55d575b6bb5b576b72debd9547eeeca1d7410bcbf02cfbccac939385ad64f7ae53f83bb9ae8c72de7c72ae76347df3fbd72b7d91c0b8eb36a6996bf1355c782c3f59bdb88f0a706ce6eaba1ac5aab8f0be576cc5d57063aabbec93c750b48afaa1b91da4668a437200882407a453f7fdff799a3936eedf7f6ab3dc1cf49b2ef86878f1a2fea3c6b3bdb759d0d8188b6f3c20acef07de0f9bdfd4eaf938286b5d652ef8430dda9b5d6026181d947f7e985fb35d4755d342d6581240665c0deafc01ffb52153efc819f9ff6878f5e754e941c106c7ab1b760d7c8e9ac6799107fe86823cbf83933e881417443d7755d37a44689a5061eb0f480038b0a9c98702ed7e82200985e594308ebc0f4ca1a42f8000a212bdda25008cdb0bfc7a53da36e51289af8c19e974f865345be0f3f253ef862ca9e48be25f6c5fae3d9d3b364865b120c90148742ee96a3386b7af1edc9d3533ac29eb2a199b267aa48f8e223a93f9e348c5fff4c591ab277f432fd0f52fc9e209ef24d670fca59ce437e79f6acc81f28b0b40257d843547d6a91883f9e3404f1947e7c1a4c2f3e92f1cb2f4f1a4a3f3e7de51f0f20bf2c99296b3253f6a4f74593f430bfe4d5550847ec74f654da4c881683b9a4eecf7516439379d42f79e578fa227dae42305f8f9ce5ff3d413ce39bbe27487ce7293dccd953794c5f3a7baae93c7b723df55671e24dc48db51e617f92d966aafb2f55e4fbfe547722f9be5348c0222da878510308067b911ee68378ee871f864f7a9ef0493187397b3a76366d167eee58d8339ac77247b15c1fa1cc1ca68c196352677962ae8fc6a7aff1b3c74edfdf13c413fefd9e201e1c1f7e8f7fcc002f98279d3d413ca43f9d3da727f1dc279d3da7b32faea785ce1bea0ba66fa6a72fd3e716329da9ee97bcbca35be9e9ab74a6ba73c7873acb2b7296ff9297971b87fd63985cfa4a21a9aff0eb2b0ce2217de94b674f50aa48e94dcf537ad3d91364bf873aab644f9ed2dfb3c767ee424e2b72d1ccd197f8d967e2494310cff8e2d357761a0de399e2f9f2c5338584fcf2c9b32728b523be11fa124f24de11106398c0043718ece53db17e912f82f1cb33c513be985d08fbd760b9f4857396bb5bd161892aaae00283bde88b3c6910c178c24aa3201cb8e0053218ec157ef8d971d8fff4c42d808f0dd32970f61caef9e086294c89b08d617fd0f423d38f2e7d89af8ab31f7d329825b64f68dc8ebabcb6edd62b65854a407cea16f5eeebda76e419ee7965032cee4edbb786fb75358c3a3315e53226882975755d77f2707c30a029bc62692491654992c69278431308f3e1f04e5d8c95f11c3d43d219140d09ec157d1fb9b8d759efeef6bc73ebdecdc5d82d7274b3d28c4eba89c239bc320570fc3eb3a3acd203b84c01044937718ce2c52339924878248d25d24d128f7834916e8a78c4417e267db919e211c7781a63c61c638e1135ce8c3533291a92870037a542bc07f7d7efdab178274c44182b64b53d6eb8dfdddd99a0b9bbbbdb19dc48e0fe8a806eed509d8a7fd0d91141324023056b0c61c4cb91c0de822e3cecee5e832b0d777b3780e1f77d679d41ccb3e5402a9e7432c87d5efcd827709e11193f5eeb8cd8c0dff79d50354bef0df8ab4fc8fc0c20f0477b856db38bf2724f68187d3da2e1755e1a5a8881d1c86d51051a339c3f478c22dc34c7c78f9cee078a2507b7cdf951695e37f9fd0796fe9874c575f0ddacf85ad0adbb5b775b12c51f45b1646d78c3ff36408db00be17ff75e4a73a02e98d3a3b49f544a603a69b3b24be98174c5754cff065ebcb3b5fb4692ac2591efc31fa3f4aa814eb7eed6ad7b674b77bcd7cca4b5b5bb4f30d4eeac513a2925917fcaab74ebd42b777721775bbfdfba95a5dfdd5f2debd9ad63fdd34ffa3b4a7fd9673411d5a958a47ddfb2ccd0dca439ab49575c0fc76071f619f6a7659d59fb6ee6d19b866929a42b7d9d8d7fadb8b96f7deb565e12962a94ce54c92a71af694b2625edbb1837835fb67fbf4893e3a6f59e64d230483651be586c21fea1e9f9d995441ae7509cfeaece6a942eb83fdc5f6f554bbb4c597f95d22d8a05072db8ab08e1f61a96181a38571b6e124b1312e05cb3e0eeac3b50b7728ed3ba959b08cfba45b1c4b8e07e8fd2ad6ac31138bb14dc41383b15dcdfe5f8f826ea16bd628610ee6f2dddaa3bac56f6cdd50ed745ece7eabbc1cd9fbaf804c57d5c650ace9586bba657fdde2fc6cd282fb2211ae94d1e800dbb91234fff05fc7da60b2f9cb048b2715d101ff5a3090affec67cf9c5f78e1c75ac3174e2e7c8f7a5a0e417daf9cc877a23e34f309873f36d98343d1ac988e1fea5cf5ea3f3672b8fb2e851189c9f32c982be4f0f8a81b1e1fd5060e534cf18f1fa8cfe9d50f14dc91888f21e0aff0b534529fe76b6984be7c76bec87747dc3a1a317b8c7c913f62f61cf99d6f1d9f92c1b18e332fc1366f73d652888eb3ff83b48e244f55339b368ef0f8363f7a576570c557cd5440bd127ff4a48c1e59cbb1b4829b5533aaa303e340c05d0f78aac2d2ad940aa857ae9272ea02f670b36a769e724e5c08bc2af5e3dbd8bcff29e7ae9efa82c099129fbe20f02204ccfb38ab664ab88171cc9adbb579b114629a4a052cc136a659b12d5518dfe64ca930becddb9c2edcd393f2148f95b4b1b1b1b9cbc765412d7ae5e8a8ce607f15c5b921644305fbd7530e8477103e9704c93fe06b894401ff8013d3cc7ae545cc144f91d92f79ed98299e9d7fc03fe0eca101fa2534326775485c1a769e85a7a1c823a9e590c66211d75f343da05ef98e495f45524878789ebe583053485658e1e96bec91969e945e8de3a9aa4d6e4daf50417c6cd203ea16bdb2031fecef49e9161d7fecba1c7fc7a4afb1b6946e8da7bf07d42d1def3fda3c2d85a8a2e83053e3573c7e1233359e34b217cd9366f97fb8abb23955b35e753e3775dacfaa99a53829a1268b98f4b5933a73cd8be73f2a09189ec1144b12328ca4bf8701bfc2d9c3c2ef7c2d91d4ef69c0f39c3d481e1681a701c9d7df316111781a5828425f11f8209d4259a49f85e731ebaf603e9255af3c50b1f8a449f13561dee633a82325a69e762b358ea9d409f66afc5aa6be5ba9332fc137b41b5ab752ef7f43a54ff0a757b0bfea7710fdab66eeda09c3302c8b742fd50c85ea685e0a11532991c4a9077b957a5a8a9ffad44eca9c528486c747fd581b856a638647f264a375341a9989864ab7f28c91c94423eb564733a3a9d108d9b0f8d364a1894203649a31ea5656cd668eba95bbdb8c97992f5eb900ab9aedb85db3c79ba6084fa9e99a3025dcad99aef9a34716316f68ce727aa542c1fe1d0d04efed68453a279868e6b348e4a76a36a392d9d8ca12ee948bf1e956beb553eed4e56414138b91c5cc62a2c448f1723a12c75bbb42ddca25dcad72ad4481fd71bc8d8dea88bfe3696454674ce9b0116b25dcad459145afe6ae93cd59fe62cd1477549f6f6d0711c5f99dd15401519cf0dde6d471a6ce0cd2c8529f73601e17c43c9e52d70c8fa791d138a13ae3fbcf18a5ce9cc3e6cc57c78c512a07ce25dcc9e6ae120e756d70ec0859c023c23662a67c4cc4264622ac131d4c3d301123cba27402060e9915af872eac932ba2cc0c981ea6383989f2f5c0c3c98a5076a5e4440b0e1918600f44e0a8d5dd6bedee76977db9b741cf2875b2eeeef6fab3fa3b24eba7419a09196ecff3aa15c25d8c07166ec1fbff51a81b005a93013d5101c0b9abddae06329cbb9ae9e1b149ea1685bad71a79de574a47c7fe6d3f9b70776f8d1c04fb658ddcd5c99cd50f82f71ae17e2fd6addc71e96cddca5ecc83752b77b5eea85bd983793f5dadbfabd29e4f7f97ebfaaae1ee72b8e5b6f313847231ad955ad2141089bfc0b34704af2f64612772d704d7ef64f54dd40cae3213ae9e6c06365c29143bd0308542872d459842f173c35646756aae67387070b395b50facc86030188cc9eb7e57eb84dc45a2810da490d9dcd536d8cd5df5e55668b10116d8ab7e87f3169c85b345ee12bdf8608d2c3977552318ec5541d17adeb9e668ee598d72991a155518ae63ed80aa0cd73377b43ac3355b19b64245f5bb9ccc5d3044417081bdeadb9991bb6acc13b2409d91b5b98bbe4ca04209dbcb3b7317ebcedcc950385b5937f33377b03eb33df26c9d96d1a365cd76d6764ec086c6d8f205183b58f1b256b0fddb05db2b2c304a62688717e011d3a11d6a385fdc658cf4867e4daf6c7033bda190a45b8d1d0505ec243933dd42967e6e8ababa9306c7096efe01c4ffc7d00e40980efd64c104c074e8678651a03a8efd53a03afdb4cb5640b7285eb92b4768760414422c61032f2f7f1e0ff8203da5eb1980d14b0403307a51ca5361dd3900a3572ba0578e42af604e701f531a7c21e556db5619e94df66a5aaca400e883c11111407c08a2efe8e86886198efe6835831fdd4e7402f404c8a3c56a2e4bad4e9034234a9104eeb2edcdf05892a41d404cba40d5843e4db4877ff652d061149eac5bd9f3682592dca27eada79fe052f76aa5da486ff2076483eeae95ec55f7f7703d9c1f531cdcdcb1ff665f141c700e9d78efdf32d0339d7a557bd530a0cec2eb6dd7a2b697dc926c576ed1795fc8a323100aef23e98aacb1dddc7810f9a0404a461fa84524222a6d017a7d0f95e0faeed525b8e6bf0131fd9b1b9c1fd7dc1348a250ffa31471fcba25c66e793d5bd17203353886669db90b8572103c2b10ea51a87bbb4ce13db2ecc62d4a7bf3b27b227129cab688e290645002dd153ea57d4da62df8298e094c303f6abe59ae9eb9c4cf2f82e09d959ab8f573c390f447138ca9bc2512399a4453089a3eaf33599377b500c96539aee86c40da6bc5bde28a2b4c2d6c706da45d31b5433b68712eb8dc70cee051ea2284db00ce1040a90bd6475e37cc91a60243b263c28604436624472e6296c8a8d112d488d213c92682651bd79d2c51506dc9895b9f9a485dc922aa0d7bfd6e2d6cf96ec9914c286c11eb15dd69efa626905981b0ff0cee2a4259fe58607f0f03fb9b3853222f7728ce96a35ee51cec0dc3dd095a9344a386e9df2eaed3efa4b8fed405bedc56db778391960ba00da027dd1961c879090ecf9c2db7737ad54f63b788c1e256fe3003c1392720393e8672f0d1416e7cf1ff7b5bbf2008c0e1be90d36fcb06e425b83ef8b65c40f8f53be53dc51fb880f74bf0dfac787ce1f8b414c7f0ad18be0d4fef2bf63e13c430b870da25d8cf4ecbd83f9686d41e72b27909267b5557b9c37488ed55cdc9b1b576b5ae9c557fe5ac223f7e74de29ff283fb29645bc7bff20ddbdbf100f1019714764c47e82432ac5a95f777c6cb83680cea23f9672fdca211587e59051a4b5497909b6562cfdc792903a9646a8b34a2215e391a4b549fe20c5a1751449f68774dd69cf940aa30c4b6c056932b467fd65682f38044c879adc30cd2fbc9073bae03de89d2ed8eea44464e86cb770b8fe2fe4600be29afbfb5fcb5013211d7cd9814f26bd14e2b68b31e2062cb067d70f5c20a542adbf04e3ba45774339ec1eb551d7dadd3ef34426e9c0cd555654655556651d7014d84217750ee5b07b041a758eba2ef56acdd342c4c52f09ba77d5bd562e9ff935a0b3ec0d076ea6b6ae77d5f66dfda1aedbac9a8313cdbc7ef4a4f6f44eafb35ee73ef3747ff4aac660dba9b5a8d20a82d4eb346aa55dfdc11eb5d9aeabb6eb6a0ef6bf9a5e9da85e81191c2b79921eb8f9c70f77d1936c16fd5a2991c5a2fb917e18ca6067f466dd82e099bdca04f85ead526b95093b4340909cdea9e7ca598e020c442db853234a77ba15d1dfaa10adb5ae9b61eb5d0b46f073aa0961f6279662c9404e9d3fa5ef11e0fb1caf0470f06b201bb89912d976baadcb36e903db4aa90f02b8796b56fce94dee225cbbefbb1f4cbf6ec1f565f8b0fd6c71ae44f4f44a15446ce9772511efbfb6247df2a714423f9346a940bdaa4ff0572937d71bae5f6f55c82b5869954aad75eb64ab8bbe9c967ede36512d84187766a43ddab794077c52d396b53e357171290d685c9a62a13a5487521ab8800418e5525877073df0039d65ddfef7cadddd3bb7d6bd3d140b6e7deaeef76b438b5b9f82a4940a00c0fe156707603f6d8357f4e311ac9fabf3ba6dd7edd6adc5eddddd6d564cbbc16ef0037b90c4182a09413054f2815e57b2b6ad974ad75d2165d9a7260ee0fe6a4b153eec5609eef70f45f0f32c15af32e37261301dbae1864ac62dad01bba62f6cb821a643366cd1565c11d3211baec0e15a4c876ca841c5723f4c876cb81dc006db90cf15387f8e1a15bc0fe6d823421821522551229f1a1a1c9570a3f4b24f9ab61fd401ee1a6242fa9e51ab90c1b906fb0eb0bff507af14ec8f1a410fb0bff8678c0b2f72a012458a279ca812349638ec00f4aab57e107751296c5dc860affa3b5e3f49b76a85c15e7985999c00f7dbeec1556385fd41700914990e6954c1031b3479436805a70f0adcef75773716475ccc60073f08caa186573781bb6170777777570ae94c9e720d169c6f70e681fdc5c0fed6aff8e0891ffc80fd076360bff2c613d887603660cfc1d65d089801151ea8a0600747bc9c09ecfffeab6eb17cc0d17d3ddd13e35613c4117c9182fb6d7fffd336dcd0a10a0f9610a20624961082c2e42efbaad88d3740a0453027f8dcb0f1c086901b35e8810d9c1b5edcd0c6cc0d307e8e8cdc40e3a70d58f8a4dde062cbecd6607a850b1f5c8182f3b709baa8f5d005140d45175b84ee09d32b5d78711714c5dc766130bd5264026c627ac50ba11c467a9363567099d056925e7910a02beff3e783bdf778405d15463d1f7869602f7b6460ef3d21aaa3c3bd9bbb4aefbd47e42ed27befe1dc55bef79ef79e171402d9132249b7bc195d79ef7942fe79ee5df39ea369ab09924822ec9de8cb9aa433e787b3c08a49a1539325d39e39267d914cfa02cdd20dc0ab347deaeb06f0e573c393a808de937504efc126c17b4fc85da76679efddb0e7adc2b8f4653fe6cddc95c48bb90b8518f684cc199ce5bd584531b40274c2666044ece2ec095daf46359cbd22710646c8400c2bd08dd6ad4c8fa274abb2a00ace24cede0c7b2dc0d9a39282ed6a542707f6bec2a88e8f07a33a157b3f83f749bc072f4871d87b1414d0aef1bd1f290ecaf2be345394e53d68eea0389e67cd1dafd1f4b93018ac88d70dab97cf0df7255211bc07eb085ff56996577d7ae57ddfbb99bda0ebffb937a338de07698fcaf56e1e96f1bf3f6114b27933aa439de5e1f7d913c2dea7c2107c4fc8b3756b067befe5bcb48b5251e4bd87a55bf44a1b32ec554f0bf69e66af087bef59c161ef69f6ba60cfbb61ef43f0f366ddf2b2577dba5595588097b0eb9fc198a2ae49438cb6ead5be26ea567173895e12dc130dd4f3ecb57ea24a54a7bead5b30d21b6b76415c5b8231bdf8a129a26a447334fd282434b3587a93f8a54e344be6354f585cd4771f665a149abff2b9a4173f3f26c727387c981f2b69329d590687f9b1144ab9a5a7bd2a9da4e9cc38500d38e2522cfec5a73f7d97297ca7ef5ba314323ec8e4bb2586953a291ce2383c53e1539c1a5271f88d2b0ed2a4399aa119c441109b4ca6bf54876293e94793898a724d5df7a6f02dd5298584e358325d08dff463085382299d29154a2f3e91d2892387ff818a4b7f5f2c7d787bd53daa57ddf863486b755269de1d93a7572fc5fdb65c7ad3dd0a6a310356c60efd5e4e67cae1bad07dfdfc02d99dd905fffaf574e1fbd0a4639319ec64f84cd299c117c8217d86b9d6da237bb8fb1828c5c921ee406fa4340ced0dca2f954ae43f0afdab20eea2affe1d9efe08b4abc678186120fd25929442db6a4205414c92987cf249d2a44e2e09924ffa205d9a53ee7b66aa7cef29ae66aa3c8754ea79a4b32cbfe97dc1b5b5548174bf7cd14c91be62d28fa4f3be4ecdeaff707775e65b7ffcfcd813df3c9928ead549b70ce2521cd3ab3dba2e9343ec9ba3b537b8987a4fcbcec3b6b3b5fef97961547a19bbd5185370caf7a76ee6acdac9a85cfb2ef09d55bfc3b55222da34debd25dbf5bdbf6776318ae30f0a259144267157fdc9486ff2ad614a032f68604a032f64e054f721f670c595ba50aecf264c4b2bb83f50178a5659b778e0fefa84fcac23467574e066f5997382ea594d80fb81f4aaed0aacb5160bee1fad8024bd8a40af4837ee0f25f1039ca48508f4aad69f5e59b901ab7ea31be66a60614e05a322f41371963c984ed29973c860c26aca18796912648ec05326b7c3cd30b81814e8e4a58d27799642a6c4fcb9634729c47f0779f10d79530a21ffc654530363eb158c09870117684b460b181a6944a24157fde451392b9b286b4457986eb79a1a58afbc2240717c98219bc94cc99ec8e46068a41f41203ddacd35b01e6ec2e8d0f129d327f5af7a9b30e7625763a68c523992992aa23829548502e71414dc9f4ddcff995cc0c09b32665011fa55a60c1974847e1b53260c4a423fcacc117316cc593232b90090a953264745f80973e4298ea78ed386268cdbe5303d9ab3eac904408daaf4a6dcb31ccff276438e6300483769400fcd29a713f4bcce8a625892652904664b075384233d15782df2ab270c1f1e71ccccfcc9f439bdcdebf81a199c8c163530130e095a392b85c8396eb531cb1a5011fa759825157484fe1ab38c82925012a5cc12872b8bbcc8f73a9de58d8ad04ff3862bcc39ab494663509d7e9930a809a65bed4175c6cf397e70e7f0c12df305e7284ebf8c165d6470dd4a854157fdb71a10c5e9af81b92b150565a5924082865343d06ea5a0d0951051b01535302c8a707f5803b3e19213b319532667ca18f5cacb91b372c064722a6ccfd42c05bbb9bc81f5c7fa612e0aaeee04d72a228c0aaeefa74b711f707dd3994d52e07e1ad351361286f4117c904082b71cebf4b0c1dfe9e69498a70923247354041820120dda8221823c2a676513261c79e426f2a864ba996ee447049009673203e60a98263e195ccbf5e347ea8dd41ba937489174205c7f1c8124a7a0500f9a31d4277d07eb5669ab81b6ca21130e86e6ac0e0aea828102c606d4ab2b680b8608baeac7f13947939f4bdf853877c9e040940923530a194f185c6cfc405bb76088ba1ca22d982be8ca84735798a3b98b3c7228a8cb54036a7397e9e62ca06e95b55ec110415b650b72dc1b9a70259cc9a312ce30b412164f181a0c0ee7ae726692c199a9544e0610c720c385b9820b0c51b7525e843977a59ea0acfe28707fce0a5cf846ca4671fa4b668a4671fa4b3335a338328ad34f1eb92b550465d500c87473570a0a65b520f5a4ac752be584aefa3b2584432a09dc7f04c339874c660c9930688f7e193347cc94e9d2abfe1853a69cb94be60856ce707f09e5108beb2993d3e1da68e16243d4adf2a3f8cf13f7f19f2fee836bae8175ad81d55c03cb365b6a70ad81e1fe1a588d4f4dac5b5d8c59e3d3ac7e9d528877d6c04ca831a11452e3635403238dac672dd5a11497273802e966e8c3b42484a08749b3529cfa3126ca59f547d386e694d3499b659a7f3dd4dfca1b004a216516104a2135b0f25603ab81c560700260e1e0f0c82171a510982dbdeaef510a296f14a73fa5c4cd3238dc2f73d4ad5cde70e7bac818c978e9567ec16bb8bc511d4a758c62689e3b6e0a1042575aa392954b605a0910e92975f9dc26c1fb1bfe459b84af59dff8f57d742bfc4a96e514d28b6696c192ced0cc3280d676d676967e24e9f3beef3b53e053ec6729043c7f58f0cce0e360357eef991feab10743077098c2c5200c178367ee000e4f17be074f7a7ed7f3ce947d8add330f4c36180327ba34510316144dc10419621c7155d1a1081c505053220c2332e8a34a0508444127a00d7d11a392bb38e0c2061a54a9a24311180638820a0f6ce002329e48f18591112481866d8d1c9400c8091e2c89a1a00539f4e0498e56c405ec958fe096cee8e18bd9961c7834d105b55c71c728ea1b563a7749980ecd8e50c18c88fac5ac043a98ed70c32dfda01bc3871d59dc530e90b8e414adc51d6d5ec312b1208a9012c409da4a103fe0206ae82145415bd940630808336a184340f0408bae610808222843403cd10d015105db13b6d65a5bd70082160410425429eec57408081d78b839bc94e09660f4172dbdc3d00f371c0cfd00834a34f4436de807150cfde0c3d00fb0a127604cf1a40b9c6f9dc193296231f8c1d093246030f444881c869edc50dd18f2810d3be48318433eecc08732ca26d2d04287378c9c206272dbc1117ae50532f8320217b0c007247470c4eb41133ce0404a1858047961642cf213c6951738f101076bf4507484ae808720218ecaf0c10db52e8eb4e0031054dc8084131bd0e0c2c816479230d2046602273e7c19ba4194d8161540e1018a0d3a88c16c8a16475865119d1bb040cb5015143421250b2325a85de1c61644ec10430116472a00c40329b92cac2041159c112b4a14d1440d293da80087235e340747c822a41116cbfbfa5d8cea78d4057ead1d0ed7ff5c45b87a8cea7caeae09653571e2040a9421bad84963a39f5d8ce2d433bfd0d53058afb8f906df78494485bd18b627798322c6b89f1af547dadf8ff5a496526a2975fbeeeeeed46d3df56a54e2661024ad3943af6a4f0b6eae76f9ce9e66d5197a5512e37e69e04c8f8c9418e94d077a55bdd833d390c3f6bd2d6eae7dd20fd26d76a057b58f6cd36e4a9bb6f5ac673d6bdbb6694f4b7d80393f7c802b10f5f9bbcbd251b40f43fcf4404415214c707180c183f6e28e4d54336e78442771bf28dee49a74a07245264f5c100c16dcd00a252a97510d2c40604e9843cba1e584b41a48abd1b6a079c92d89085794cbc9312327a79643abd1b8a0e5d47268351a0d68e489085794cbc9312327a79643abd1b8a08994d53d6dcaeaacf801ecd6e27272c01c5c43a250ffe314710c73c09c90560369355a151a4da8e6029f56a1b92b27879653e3515be5589b63736cce89bbaf13c0feb552ec0ac1cfecb063d06ccaeab288bbeeab5fecfde8d66cbaeadecb20c01efa68567f17746fbaf2dff30e37ee5c3e8eb83528544e2d8756a3c58006769f9373821c989c9c1c1fed6a5a9803e684b41a48abd184684039be18ad5befa10b67c11445f4c084eb704f980ef5c083ef7063dae82a2e98a576b9e21507e28e4d78096e78041b4ea2bce14487867261cca05d10d3211918ee23bb42c4f5301d9259b921c5c0f9736c94d1bbb8f51b878705a31840a90702b05fcc237e2c42d535614365bac72e6240a291f4845802ddfa4d29a54cdcfa4d29a55d22a55490c1fd1fce19c02d0e317e38df94bb7f980ef5b083ca850908d72f74b0420a325ef47118716b0d3018860b8c88e20656d8808817edc1184ca0418215106184e555dfc375ab56d185ea58bc811576c1bf3f7b395c3d9fbc5ff97b66eece1756b87bf7707e7a449e962dae57eb56956ee5c03970f5a23041f1a2304531fd98b26946744464829962b8ffdafa69be37dcf9e6ab03124cc1650926b498418257be54e07c73b8f335ba5e3c28d7090f8a15dc57c803ba543ca05ba5e6acda05aad1707fa9288ace25a31fe05c3ada52650b2953b8608a26af7cb3e07c63b83f5fd97d1246098b304a5b78e05cc21995b21895b478b09290072bd93cd80df79760fd349762b873299766626c3104ce0560cc9af8bc72690a9c4b34dc9f4bb5521527d870e20b6ed291ad8bcd0b110947544494c3fd24a01ee04ca29152509443174048e0c00667bc32490a9c4937dc9f4944242d5d131294ae0929ca007026cd3a19e947467ad2011d0191601dc9d6e50d772e73898b99a249edcb153088d0e195cb28702e73b83f9746279c4b2f58944e60515a1100cea5505149a5a8ac6254ce8c4aa09286fbc9a2353299c936bc98628d14bc21a50b28af5cde702e63b83f97b2189ccb27b608120b5b04b985853389b335328bad69b13652c8666d3736702663240762a0032e455f82c061f6ca2414389334dc9fc91a6e9cc92ad6c9c88675327e19bb8c5e56388f4776366eb1332e34a11beecf23118e571eb59c314239638c32fe8c4f4c9cc7d991787434c28e445b3fcde20d771689b2881bc1113e4c51c596d918af2cd200673187fbb368247ac14274020bd18a4845acf2388b42456214514a0e770e8d70875960c1124b630530c8f9e09545d80c701663b83f8b32f195c5274f84583c116e09b3845a70fbad59f5736805778805f787301de430873e9818c30a932f98784119af1c62c139a4e1fe1cd65ce32b8755866063882f5dbce0068f68208e5644cbe17e1048076790863b836ae00c2e11420a327ae8a283152cd1c52b83433883379008d4e24c4028ce048c02fe804f7083338f7d4720cc63b11b2e87fbf367f479f1e273c28bcfca47a50aee4fa8bffefaa3e58ebe1811fdf97bf28487c513de162f0b6e2db83ddccd13ba79b67e0fd6efc570674f0c9cbd067c1102075f48e141114bbcb27705678f86fbb3d743743d44d775e98e681d8e560464847347c39dbb1a1e03e74ee80b2a5284f1451559d0b8bc72677b02e7ee86fb734784bbd3d24c3a28cd244ad7ddac63471dccdafa69b637dc8b0a2ec0b0e9200a32a2bcb22db2e16c73b83f5b2f565827acb082fbad10ce52c1d92a393bcb5920dbd9a5c0d98f6c0cf7672b83c1d93e19c2b118620b6ec7e1a679169a162117725b158265d1d97b94a1040db254e1021918bdb203c9a03f7b95da848ddae44b77f182fb08b7ac28476b222d5974ee2d0e6828148b28b87bd682b37e9e74cca855025dd5cfd5095c8215dc5f6dfd34d71bae3f572fb85fb952c1b50a6e5a1067d577006d857455bfbd737d82fb6380738dd584213b238b1f8c4883c98bb6d0af8024dd7fbb062434c1043786ac8b202e5e1506b83fa7df876371b377e5660fe8899bbd5a6794bbd98375595c70e882b65b2ae3665b84eb97c2b8d91ae1faa52d252d6eb6365cffb671b31fe1fa9d12f5cb1f2e89874b7e7173637173d370edba2b68c470d7fdcdaa7b544d570208389fce58038fd003237a4069ab13a23e58457d908a4b82c12559b91e91ad1908d8fded98e802779d7d75bf434471f68870e582ddc3758b8824a33ed9e59642dcd212b754e5e6c7b54d40d833ca31c2abd65a6bedca9e9dac817e466bc5275442c1cdb6b4c31232886bb948d9598d0ca1575311cb01a874a9c08116dbab79a8e246d1cba50515dbabe7079627845e22b03e00bd68936069e78a81098e5e63e9a8541382bdda44750ae063d3696a4b5f6e578b25a359a8f44a95b7a922868ca1190104000000031500303018108ac56291481669c2e8071400108da44a704e9449b32887410a2163880100002100000003338341020091ba00e1af897c8868da0161a2a3721fbc1e43d59be171e5c48b796715a9e083234ef6ed7007d2021a96b30c75e21e15cafef8b6bd4868c7e1fd4c1be1f7793b083ca846b9ed8d54305cfb96c8710d494950d85a3a467ed944225eed0ff357bf14ff5fad032f0abddfa2eac0f8e76c2258c8c533435250b30a9999f246ae42edc1f44235b8a03d4e80524e910c520cad50c87fbf8ad44c52f72543b295d32fe0655c1de103732047f5c784fbcd3ba8df6894133316d8ccf34253b2b13cd9e57eb3505884cd63f9ce31d88c17d05e81e151c60645d39383fed87f341c131e9ff83fac3b84f1e05714636400101705d81a047fa79b8b893e644fe8c246ce288612504fa4ec0491cd82e179831f94c3fab115795e01a06501b7cc7cb5959c05bbdfeeb64c6ade83ed157e0fad3fb9fec16cad04ff531956bf7ec3a67e03f4f718adeaa322ef14d0f5eabd322c0628d200a0a02e5041f28425acf2f5420235f894c2beb18093a6f5b8f2ccd3448e0e93b593d08f4b0e8e0d2aac571db0c7ea549e56f741067096e38199c6d108e8ecef3c1df417898191121d659c53a977886ddb0c09980da71f30bb9fa6cc4699ca770fb3107353254a7e46d83a4da29d751e1c734744b12d5af3de8005e6125d0787e0aa3166f6be49f4f8ed11cd66caf6cfa0b04dad88f9ebeb01148087518c46090e2b8c60801112327ddb529f9f08978a9137681545d33c1c741851144ade48a6059f9fe057123769e46efc3b240e74241e9ecb3cbb99373c043ee2fbd6e416646bf54d34c4986ddb4b700e7aff514bfb6e28a5ac3a6d97b25dadd3fa09e27e05da86b0e5d7882c24b38d26a49781c7f08b7b870192f58bc3397c120bcb1b8585745b2490d79980ab129ecd9b0222facb5ae54b54f56366699ea61cd4d12bc9a99baae20444a884b14b8c2fba50a202fc9efc5ce6744714cb9459466cb74c0afbdcaa6436631dad13af992ec9ea894b7eb4d974725bd876d519e877c749e445dc40fbc2343caaad7de59cb1e7eb1d1ff2c10ff3f85d60df1d98f1602891330902f238ac32d5333a99f4d5bc39e9b27f28c0002a1b655cbd9db8ab1e6f685be0da4457490af6f1f065922fa1636d1723eb2790eb89c5672698bc35392042b640b3a12624a7d0482fb91712fbda1aabca374af90292f4631734ccc793d97928b6023786bc649f98e57ecdde0eb8d1c699480c3537e9d1e545737a5390f83ec4c9c32f8121b6c0c6ff0fc996554ca4676fb3acbb4753574788a7b8fd5cbafacbc9936fe8f41a786ced52f34d5daf8260d42c61d5de6b91ee7de9f5408444b6c6839e01193876960d935173c4ce4fc21190d1292076ff974081ede61f6184c803f963783eac4ac0927ee888c0f57fb53f256b59e2713cc0849fab62769cac7ce2e68561e69b7eda0527d5dac3c3d61c18dc27294714a9a40fa3ae9f403b5c287bdca2d1f84e218b87d6f5dc1c2361cb7ae48d333a1d47ac10254e419fa52ba6855f96ba3f83da77f215e6acbe87b542a679e3956d1afa59e53b0f81311b8fd8e34e7a47add97643ebdf9cab0749c34c608eb8c910d5df0b16a7ef0deb84f350e2adc9743c77962cd073e7047732c3a4766df1c1f464be895082215e228c58ae4d73cabbd0edf455cb1b2fadf5a0828f9b0e8ee889cdc9fc3626c4dc54b20ad76dcabf17a159e10946c47d6ab130973c80837576d758a6160f05476c3472eb9a24d5548650734c0a6deb5672c8a0a490ca97699cd790c88f8276529e1916fc9893398a01723e8b9ee707dea852f39c37273f07ac88909072d180c210ea2104f28642963725cddb7fdb98f88a18e5cb9f490fd69a38ea515d1eefa9e27f039356466dc19e0b79ca5cad7e8175406f76d14a571482197807037e0df4cdfe96809989ef8bee482e99c2ad7bf28b18ca2a8e485c2dda76ad538a9fdc744a8cd486e0c3760f3ee4ae1494aba143d30c2827006d5fbd8687c3eabcab468dc8e53f664658169900b66e70fb291825c8da7d1b71424044b898f0a552cfeac6fbaa645b3964c047fe1d0a5ab4ac7a5b8196aec6eb93ddc8416e5cfffd2ca85f0fcc38c4627f90f385c5c54133b74214419210342da896d5ff1568c39ba8006305c745c6ea385582bee6a6c1635db72d60e1eb71306a6d68c0370a50927aee4700bf48de2b1997d158b27d77329bcb5e43d0733ef644b11f2f2ce33e920702e41fb2d377f785a0e916abe58b00ff033bf63c734a433d61817e3057e409136a37eb2f2dfcf1048ec6a56ad2d738fd4355fa751b38b88e95f256cb7a0cc799cea6b62aac94c46b2c0755d88cddbd10e1b2751740045e10b3375e1c0186388babdf605780d207eab21620cbef960e01c925dc7d274b7aa1391d2c8936e605a27c55364540d47ed1f16d8cb3ddd7bc1c115efecc2c4bacc7367efccdf96cd3002b5539e6bc48fade49f994aeadde8a83b173b2131773b0a36d65f7ef5085f68c990b3f76aa09d24e5e4e078bd0782a0d4bede01b3131f4e38f8fd1877cc95dd86bc3ba30c7d3f177762be25f69aac00defcbba2aec2f0471cbf4ef446c56d46c8580fb28ffd6d9389e280ec8bc328e01c42c4510d64ab7cefc1e0649aef6793d98811c4074f13182b0389afee6c1167baa480707ac2435bac3e7753101ec2cb8fad64f132ce8d3b85a168a25415bd6462ed0f69bab46507d9b67298e92f6b237826d797e2aa5ac59b4510c8eae4335811f6f371dafe32c3396fd2e24665dcb4b73f7b0db0afa6ea36ac3222905c41e2672ac188ce68d8b6649af3cfab762abd971e894fbdb01ef31348f5ce251602834fae6e8615be6ee81ec47f7c0c85708c62605f004a55c9e2b0ee03d0aa966ff4f5d50ee1ebc369c693e1e07f970a82d84aef09aa68222fc784dda0f4a149e4a83148ba2298bf445c164eb6f9f12723c94290b3c2e1d0a43b9663fa80a2b2c7e9ad6ab4550229b9a5b41d10201701e72f7f15cc97911919a2b7ccd67104812231fa72dc8262f5fa3877e48afe26ac73958b6c2ed789aff8d1032fe60b52a641cb9489e68480ad7d421951845c743710b8fe78edbaef8534b825ed84cd096416ce1bb52f9d9860ea0e36eb648cc56fd3d92cca604255168f640905d183670703f02b7c74291a8f97230fe0b0e6fe4db9841352bdd399fe28bc4db8a759753f2ddf4f0856588d12e54f42314455f6ad8e7f14133890b3e8fd73f2b495057d66a655f458b767720be3fa8218c719d13322e094b1c9b2333a37cd9573109c23bd2bc2c94afe029062c779d3f95ab1a6e585ea3d6e3c96a514f8cfed4089ba25bb31d55b0f16f08303d5a671124b7855dca37312391e51c20c0c60e3edded1cb627a14f2aa5753d6e8b6838bf194978cb0942f6b7afb92f49d9fa975ecf8860a8974462053110641c1a9a5ef7f63f791a8011dc05fdcb21ff02a3e4ce2a55e64b3bb3e8ac21ff078bf067220c03068c5794a5e6921d33b5d2d554264ddd5e1c1b3dd1fec895abf418c7841ec4f88fe2c0710ccb275ae2c4bc0604cd1550a80dc0d0bfb012a9c3829aa8ee011c6214202a027941ec0292f56618a3610774da08ebe163504eec39288f1e46bef980db61e6e3fe4a60e8521cb78cb5122e1b53c578fa32de9d6e9e9f68c8da6e5d7f8f450537c7a3d478d20ebc0c20b30780e109ed30e703e85839e041337f00ac9deb0a549d08cf884aded21442129c341183ba9d441c0f23a720a0f08e38687a625b0c76bf72f83ac6943e3e7fe5a78293624d87dd9a51d574610085118816a53b280c09810026e3e011bdf1e5b0bf0996125ee137617688f8306cd250d1faa2cc87a06fd2d3b15a85cc9700b13f8d1caac6251eca9c4497be1643a9187ae0d3997defb69f60a873dc3af66c4f21e3982784398469102ec159140a8c859489834ea0dc497acb45214de60afa0e13852492e241257d89f3319a737535874f6a1a30c434434baf8b6a0343ce34d0fe1211d49b3910c85005ccffcea7a21878fcd27e33d199bb22be18933fbbf9d9ecafcd878ddfd2b92f046dc24dda1ccced6c293e125046dffe17823f89306e0085b87287d910bffaac58ef0b796b6c67f786418d6a3a163a35a507c985be103381c0cff0d6169e2fc7650674faa039c056cc0dd977c37a4e97c12f0076ec7335a4f3a154fb0e545a01ac3c8fa807156ed11434970c8181e070cefe5851030382ba9c2cef5b4632694010506f2c56aaf8873465ab2191e9d61f10686af9a8367bad0499c180a8a519072adff284134f348b8afb4fdef88316e0496c3af6ada93488a3def40336af431c0e7dd2029dedae6000424001d529b0c8f48646cbd926e902012520a220e249cc1f44f55a9388bfc0889c60210428b40acec04e086371c62968c31df6af70c06d6a519978ddf73c86ab986cc7e70368410e18e01687d4f74738d0f70a287b5cb060c3ea57bcd6164969185df07128874a1a506fa49657bbfc27ea3db184ca4f6cc9108e9575688f97334e382ca28169e3630708135d6544a0bfcd6e41fc6166207eb8c5886df1bb0ae9098db8cc738b113f143400396365820997a6c46fa715c1f12bfa306ebef2200bb76a37b63c9c3f83cfd1c0452c039362cf8131cfc54ef876357bc747bfbf1b5a5cd955ad770d3c5caac2f74af0cb1374b2ae5fd25c39a206efecfb06844c1d026a0784870c2d428e96f38af6527d6516970829fae12561c61ab33b3786d847534544cca4303d4aa7e00e2981eaabaa5e6047491716d3c8b60ef0ce83b6fc1f246c0fbc6234c220993db362c484ee75b5c7b18abc979a10ce1c0962cc16488b2fa0c2e38ff57e6f690cccf96fc27db208426de2de77989cd569e970bddd07cf81807ad7ab09af8f4493946999a68d69e19b3737fb8e7f5c042de40070cc5ad426409cb256b9a6385fbeef912ad049c9b4f1f27a04823095810708987503c4fb98b164062169c2321bf2a63bff16fa66a6c98386127989eb2250ad05319260ceae1758cf0bd3de6cd68f04987d1582ba630cb60ac994ea63dadd5d50c1202fe381262a8627ec7a34a9b2ea2ec7685db50d67f087436056122bb0a189a73719d04566be2dac12bcf27b64b94d13263583c7ef9a85d8b05cc8cd8e6a407d0f2865912f191c8abe4b8cee6d8c9579c212fb62a57cca81b90bd3aa7b93e4c4b00a6817bb43c7590431968e0a04e3e6629904364b663b10e1dc5fb6472a71f17b45239574687b431554ca84085b5984447d18bfa1be4a605c2a6057003e4642da633f1bef9b1aef9288440ff18be960b985ae507e5439a1505ffe7bcaa17678a64bac0699bff04bcc77fb63d1e051aae4d1962b98ffbdfc765cf362787f0ab18c9e602b233567475f21913df11112f511d0c777ad7d85218268b0180d15f516def5a3dc79d6a7d36bed62a2226dc006385ba6b15b23cc3d7dd8b4b34461d1cd93829a7610b60b8216f072b9f7dd39c8af3d65bd1acc3b991d5187f3667beebe0093222a0093bbd071638da7b661e40a88977ea9901dafe3bf32ba90439ebd36718c22a91bcd28704b952437da8461fa2a3117f1d5e5c6cbd784fa1cd1dd662be1c0dc3116fcf9f0bd23fd3847306a310c1530bc64017f84d3b19592ed5f83c13bb30c1d5ff3f3f66f1924a21fd3bfe152084e567ff537e449475abe5fe549f4fc5a17c9eb7bebdbdd5c8dd558e0d51f2eb0c0a009512b01aab87c9a34031c98b8b5eed81d3b4e316f7d19e0ecf5f2dc230f8db5d8f1caa4a9e9df1dccd1f9bb44685e3ee1f273fd0933a052a21aa20edc3e0b55dc3e5547c1d597d3355ed6246b7be89fe6721a73c716dec18a44452b6188800a1c3dfba0f3d57cdd32bd2b00168a726e6b60763c0a6d7d2e9da1be5bb5e79a31966a191f66ff7d7a132297c0e6f78b1181db901a6d98b797b1bea8a7980154ef45d7676525da317c95f25fdc6ce0634a02ad2a3d9f604816d2946b9418e3f6146ba3cb1285707c67288998e04b6e494540648e21d928b668f2f210534a1cb36092237ca661a46f70cd003457548cb9ea384421ce192da4e8e67d8c900893b48075d7ff1f126c74a7dac9b75046f3bee7d639583e72c8a39a4ca327dba3cffcc885463cb6bd6a66b07af472a1e6d98666fc490165cf630e94c52c96a64cdf58ba5a7e98557739aa12302e0395613824ddb81598f200e94214b52819c6b28c8a102225113ab23a9f4a5c9e248804044374f4cca3b23f6f754a3855da68cc6cddc42b9f13af09319c427293f56e35c90905f78ba2045833cf21be0216bddb83149b6e4a6fc7f656cda630bca98593312668c10e6318632c00e5f21a7082760c079108e1d24567f6cb1c4d2a4b8923347789b99a89b7e8d4894b83d1a7e029ce17a8659695c4dfd0f697cb06f3beba25177c71cc5897dc431691c36b78ab65bfd203f8cb0ad7392e367981dfc6134f5dabc8d87364643848eb87db282e323cb5b001b6f89cf1bbc26d8ec304491e3ecb854d929db5054797e3097091014afd66e7fad8ddb1321021c9d7aa32c3ccb5550bbc5d94e989f1830ebf4608121005c287dde0be90a18d55094b9a5b764f200f624494dbb406f0c601d2be25c023fdd53f457aff057008d90ad901a0e25e1989b8a51a3aab89a9d00c7151c79dcafd0267e34a2f0a6f49b3ff7444b369690146604c74dd2f5ec52f3aca132b24e5d174bee1e7263869f11de6b1da933c1621f06c99a15ff3d86973dcbba64c2f9f99076086485850f585603498693331548e9dcbf338ff2b115e3d98b57189deafd000aaa810ce1480ed464e0bfe6a5980c0fb83c0d5546f4a82d608ecaa24a822bac712f74e21dc122ab25f283320ff400801a4efb806c41dfdf83616227e69f5d939cb8f13364fcc319825ac19040f5c207d99225a3dca9c255544389550f10dbfc82e6450c7e0f7a9dd84e0c67e9f5b64f79307aada47de985a33c85ab12be9df433197df22618e6283717164283e8d9623c94c43478326628349a3d0319179e058d8486c646a1eb20ac3c99a85796e9e68e1ea1b8e4d51eb06bf877ba14c64a11d8fd6d69ea7a0de7b0dc5444451f2debd5e5a8b708d4f55e28ff1a6b6fde01fdf9619625b0eb06584b4c7f7596b8de0d0deef73d461c33b7b401f95f835f3cdba4514b27664160f49d874bd7aca20456ada553ce46cd24317fc03eaf4c668db312bad06ae0df229460d27efedc3384cb41fc88e9570d16f8cbafa8e745552888c94e3ce4aea0e8c1a0119310dd11644fa03d337f019640a43ba5e22442617ce4325e2e63834102a65cdb6370e14e3dbdf2ee0130c187f223f41182a71336e4484f6f488729bb63c38bad8bacf8e5da313e46de77e964eab814c29a44a519f9d269bb2b7b1c84a49bd367bddbe614d17d08c855b0ff9194bd6fc6f6cfd57bf00508505b61d7fdd8b05303c04df26f13980a5403031f8b43e128d4ff6c36fb34a5c2ccff8ab583b4a99a9a5589dd36b3a7940702756d493db4552937a1af6da0cc8586eda11843ca2455203c13ade753c21add945741097036629801535007d8e0f921b1648bd43c61378ef07c8030ef7ffe3833c653b23a48a0fc2d5765b7b589c673c080bdf7715db61f5e383482123c726116972653bce560943c1c5e921b0652d958ce22000786810d70951f102d6b397ed25ec7764a640b1ef5c8ad32178ee27c973219fff7f763cde0653894a85850bdd8e6ee1740c66788f63ef3d43b2957aa0a29ae1ed36e380ccdcc0f098b5bf43173611b58500ab7a5d5e4ffdba9a56ce6b6c73bd9114609e3d6f8180ff0379461001f06b918e724d66414fc01a2f0c072b33dab2f4ce669cb5ba786a2160f4f839048dd9f8fb7043fbbd4cbbc4fae30f9596ce18fc8579a6fb34a3ba1466a73f848171eb1e48213da25407a7aa42fb4bd310244d9a8c6769381e825c7399e8d9a8b16b9879db2ed09b03e523359cb5cb21f8f0c97e3e718c2039f8a950ff4f726dd15c8a2c24e0a6a9833bc9e403add9f709f00d3c3d6c7502e9336a358acd57c6967b8e6233f65dcf436357e9f10a604f56e4a813a332b8c5a85893942cbb657db7375117d8a621f75174ac278c38a77c2e72a8f324d53d19ca6f213a8103c5bba6220e38d2362367fee22b0c3be96b3f3bd4c52566d7877e45bb0144b0228b5d6c2a198c0379a1527c553582b31296901d2d85fe4ccec347049c263e276815d305df5ffce69051d4b31f0b2d36b2df68ac4cd841febea129c015d7f963be8cdf226ccfef3b75d29d0e1d7b15c067916032b858862e0db949de242381cf8c1929f79fdbe06231b0a8b02b5414b459ea21b31f831a5f3527d12f3903a84dfa3f79a4c4903ac7426af8f9da13fd9dc338b1f988f39f91d83efd6dfa33c78870e4c4669b4dc96c81a2032ab5e4390f53723d634eb05676729a06a61e0da70168e28eb3a560c11ca7afc383c287ecbcd2492864091e3cacc4558606fec8647099d22ffd26cf5a750f5d4adf7ca717c73e0cb44e8f277fbb50c0dea50960f614c17fb003c305f4bfbe43d1337c7b65ddb003d52309a177f0307ea8cc0163f122892fcc516f9a0426cea1735f49b51ba02539a6276ed27f320fb5821f070ccf78679f2bb05f97efd89b459e50d44f9881a7d2037f9014f8998deb70d9b814fa095e0e821a9cf2762820035169692a9c02ac036ea94ef06c11d555d87ebd9b77218115469ac3dc1b311bb42310110febad50c49493c120aaa1dded3b7dd46d820392b5f70ccc5a6da26e903b406837d9f54bdeafc2df3d2a331ba5571a85165541c37083ff0717a849201944c8b54acc0655fd177e2683ff0fda135d14e3fdb8b86710f7a5522f2bff24856ff3210b1bb76176181887cbc90fe29ef9d127f5865c55cedd49cebe501a3befda9ac6be77a2083cea79ff7b3767ae1911ea1d35428d7fa36902481896e728eab4deb2294a10dba28add573a0842432f2c49f258b67fe52eb666630200721f283fb08f4ca5eaa43e694ffc37be0c3404fcb5ddbb817dfe2c2519d4791c3c1d01e31b7f8c677c1c097f3e902949dcaa2b5f052d691aabdb57ba5fcda9dd27a0ff73ec7916c2a6aee42c55c9653219754ac3749e0d08e3967363e6d25bfa7d2bfb9ddc7316328b54179ec7e2d9c42286f4b6d9533630e75a60f699adacc9c74c321382eff0cbe22fa65b5b3363c9cb55390de574abdf7cc2e8f878268a86818d3c0009374088de2462b3d5a5f27c780dd5e68e78d28f218e93d4d44e860c5d7dc8ff7fa0efbde108395a7dbe8833f75d69e5a15641e5755cbc15c7aa8a856017bd05e79729a891466e2e0d7ba97b530895dc430282396bb3dad8a500c465dda7a3b20009e8ba2a259901cf31c6c58f758fb3807800a33fa2e3d30b88a4acb5793fa423f964252bf9a9b9e2f413bf86a5c3fdc65ea12cb4cb2448afe6d83f3a0fdb73b7caaf7c7b1c3145417391ff898699b25658f0b2c33ea0fd4f650a513087446794049dd2a68d8b8ee8cd5adc6dda0716eaa9f5e4af6a06f3319ce70574a33aefb3d3d0b86b942a9024ede561891e54af8237a93890fa87f920f675c23560b4130d5194be82c8a2710784a39137209a4e1765d7811b223399c07301fa687b16784af5be038bc8af47289925b83cc392796893ca31086c366fb02d624d9d4110a3b6bb608791ee05d31c1cf59e09835e0982bee0e71f5221bc392eb4e93940309637a9f7ca61ebe5edfcfd7409abd7febf4fadeb07b7dc84bf7a138e3118609bfc8f8118126b8687b0e37d2cefe2d6dc67c3b78c98145eafd42d88ad39f6aaa13ddddf9b28c3ec8ff56ac33624481065cf10a2c7964f31c86329e50aa793b59e783bb884e5f611eb1607181c2a6242309cc7db4accdd860ab6416cb83ac0e01cb160b711232ca1bbde897b9db60662e9fc54bd905e4cccf003374b0c15ac7a1f090c9f19f66112a09930596897e0ee6773842938a01cdac0834b5e244a6740ca7bd98bb5d6ff175b2bd8a357f8240a653064599f93655d82c3b99b55121fea523362a43ccc5d92c842419114d63dc31d441e0554c698ba74d475a8243fefa75b6b33fdd638b14a8cb09fafbc6c192d6c5441a4865eaca44127f1fa061ee8ed7a42cac0ce29e4221c5c7f9528f105c1d0b6f58daa11d3d996c7e7784df60642870157d9e3f5dda68157b8a7b7f233dcb1d10958bf49af0fc2f4043ec18e57b3a67cde79f75ee8cadb44230a8d9608edd8763abfcfdb617ca3f6919c4ffa3d65dd10fccdc793f61bd468a1e9d32d36d4ef185886fa16ed9cf27ba8e96d1b3761bff477515d75691c3e7a3f659a225391c6aff71ea93b38978986a4aa8866a4f1d7bc841e5cc3e6b815ff726e44d9316a3821850f31d385b6152306e6b4f631b804c97db65d04f2bca2a268f2b6f30f49ed08151cd59ed28c1e3b5658bc982326d3b58f2f94c1da6d1281a00075214ef824288c97837fc0e543605b1206df29c4eb88dd842504f9cc14acc12bf5c42878ca7df623bcb09dada2eb428d821cb4233bf371b3286c170d42471d465b01e1d38f24fd078356460298ac15a019082febcd9b06f2f9f7fa67526d39e1427d3576fcd7c3a91e3f1e229eefa62f5487edcfdb33f40f7a986bbe047b3155a9ad2fab240ccf32cb47c68161934a28bd2bf5a1ee1f241acb2032116c6f40546458db16937e01aa15e2114dc79f9e86f4fdc33915d13dd57354faeb44d4b5c51a482ab4e9385a174cc7c528a4ee862b77e1f08ca8fe2e71582198506f2206ef9873f4a3efb6c28ad198f4cfdaefbb397e83198ca03acaa418d5ac15b5c62e78d9fff60b2b6ec2fda258e71c1c20a9249a77176250fa6c78ee97a1ce7693ac42bb4b12cd9cea5bdb4e6a2e41b77ffd903a2ad87c52e4eee4f5b232079037b1c8809928ce8d84a4ea58c2b88c0fd8702173241bd9cfedd3f1276fcc67ae8264272ba8cec27b58f549349bbd3b2a3e8ad5dd3adf004dfd6a7b1b4137eaccaf59757f34fcba67dcc8ab68a24f3be135e6c9d0f825c68e219c79d4827f6ce529acddf6234f780d44eb3a4bf702e12c25581767878a58e3855f9975bd890189b03f3241cc010aed0b64411bd25563dbebef3687c092865ff7ce2368e2ec450b26fb341699557487a69e1fe17b29b3b3468fa463286b5c0566e0accf43b065fe86fb3b0ab5042931d2573b864ce5cb35942496380601897a1b37d7a6520cdf51739ba91b2e79240fab3fe8548f15b13708939b82a39ecf412aebcceadc0d90347bfb2758dbd0980e8ca0cfa2d898424f9b827db0abd09af8b2f004e3125d411e5a283454aade5d0ea59a90697140c1e22f82b0cbf2d0bce6eebe515ac967a39a56832806d09aa14101eac20324f79fa784cea82b7c1e08411c8ca469310742b18120ae5cb52882449ce9b7de7cb149f522c8eea1b35577e55744f8e4558de9fb4ec0848d22e5e23ee78dfb141b9a0c1aee6d5f141f8dc1ee38cf0d97da2a51d28fe045cdf7a6cec2530d317a600223c5fae21e4bd0cc89951f25011e3377c4638c5d073a5eb790cf1e1011f86244314064541b6f4cea38c324837b59d5900e32c57ff1af8a9a27edaa22b4c7c60e26ca83f063d4460f9c45e7861efe858aa8cceed35c0b8ba48866dc62ae64fab4c39f8f5e84aa31f9184aafe2a54fb752b9ef8023ef2162ea39993cccd2a06292b7929c97da8291b0bff31598989a8345266348b37c145d1ee2919f767485ab1423ce079d6daa0e536c34bb4d56d1fa002885190fc541f86a2415c4b330f9ead968a5f07ee9bf8c99795a9515a608f5cdb75609118ebac0deb910ab6f994c86c2d03ae271c13efb4deadbce6bc2a00b6af4029c2879201436338d842ffbcae7334a5b06a3d2bb4f2d21b70c3f1d2b0a178a49ff3c3878841a88500f17c5c9bc8e25a0471b2a38514b1f094b994fa096a7ca7c03322992e0d9334af11657baa55a6d68c6c0448015d4ece5707d50fde23105df68f7e285440020c6f3b7f12955b087874545dac456cf2187bc6e68ef9f5cb5e4caac49d379e9576463b4dc2c7c1f5d889dc0d3f70f61151d444986226362622989b3da664a70875cf06484afa6acf383fd38780f1ac3b0d0081f2b84a318cff72d3195448c0cc8cf93fa8444941ef8d7a6823eb8adfdb6e2feffe4ac956e82319704c0dda2347907583208bbe2cac85422a616a4c79a29901d971a7358950b80d8744b7dca42bf655ce4f159e5e9f45cd1af70d2f2c8fe62c071dcedf6a1a1e14a16ae92f7b8a09760aca1554abb6d6e6db5ea24d6d3e2b80baabc09a405fe2d125aefbc47aa92204d528f69cd6d3cf18d9590c21d0230d28e2976785605a4f83fa33682bd19ed4926a498b574b93bbfbeb359e22d451c65aa8aff4945775c1a2d28b4372bc038a827e464906565e02ccfa4e5adb2f578e26869e59137ccbcc6f88bf256e28c954da9b56172ab6154e43ec79b4631cf91911f557e691e28eedeb9bf2d9fd6dfa110dd0f239a6997a0bf9904defb7dbf6c62d518b5b8d6d9a60d73c96a3743e981ca8287d5d32f3f5c96faa476cd1b92bcf38a85f2d6c0084268e8b328d6ed75216d73e4ddd7cef1e4389b845419f11aa1520137978657bd6435e2d5a6f47af4dc874ce648c9d656ea4334b8469a13f9d8076c52e23139c627babfe4f68f7821cf1e4859f240a8de8c2b7e2793cbd45f526dc41c15628db3ee3500e719240f96fdbd69d4c43fdffc7c53709cea9698b8a130c8fd76f89f767c3424f7373d7ade3c2da15efd31780c9944ce0390b033c5a59b12eb43190e548ccc5fc5e18ff45aec5cf77fd3d5503d4d04c451ca82b464e1528eca51758daa79ee749a449b38485a14892e69d44930c6d08008533f5aba29ba15b9822787e15473093ddea58df33ca576047a9ccc201be09e286c251d47c6730482072c483e3f0f9f5671d40275881242082977d6ea32e8d812747c42a0a52bcb5efa236dcfba3c33f01f6f7dbc6d8962712b75ac12f21c6e55aa66d157150df19920d6db52350b049bff4c30d502a7ab3ebd466be3439d40ba64c282147cc56fced1d3cd5c4d66687630270223370f8b24379208a5ea89f7574e239f0243ece8f34c451c59e20e4bf46fde1a28d9bd0e36a9bcb86c4187b5633f5c468aab6cdf76b5f71218a90a80e66083bd5c3df3fc05e8d1324c2cb6b8695af2e531183e6b9058b6d33748c7910906aa54f72108cdc9b8c19dd5d411d42b28c974028be120cc76601bfdf9cef13ce648fdfdce3ee5bcff2634886d68481104d2d645157a98d4db9a067cd2110d04a641b1af5bd3c63c61a89fe71853aaf16c47f7ecee9a4f7a3bfcae974c73a0d00931c02533ec8aae8a8bd0553375a1aa747bdc9054238259df4ab86eee91c4dd89af28b184acdfec52d6dfe07fafffcbfcb700d8cfb31d983097c0f9441938d169ed9bc3f14622bb84dec143aac0bfa085526988eb961d53d1d54efd414fb71cd316334f518e8d08c94fe39b88f4cc26bc88a4b844579eae8b3e89efa58b5e06de47078265f49eaa3a5bbee28a889865d46c053470fdb0786d258270fbcb13edea2685080ad61c789663954581486270152c1a6776cc4157ad12d8584c4fe7268ef133fd994d900409be37118bd40ad46777f972f49ac4389aff5c13145cc251e5793b416e4e3edc0f8dff0af919a9cecda15a54626d301faa0ecd49d1d6494c311819ad535bea3c086fbacef24e976bb7631c2115b9bcc5ed8cb798e1aee24b1f47369cad9763ec5ba14192b6faa9be782a1bb8b1169325adba47d081de48ac40e8bf92888cfbbdccad3da9cc284434def24ac318c629f070cb7fb9e3d529981b9916f1067f527add497e76f978e773734b64c6249b6969a1a48ca3aa6d54c79864d5aaab05a1c61be466cc0a1063af4db274b6810a56e69a19c6d61aea9e37a9634dc4d3d1951d719d6df6a5e253323f13bd5edd7d267d22ba563c2ecfc0d72f50f359f951ef9018eea573a91492f59425d6934ce899bf10be318c39fe3bb7bd2a95b1a828b5f81e0fcab72c3be60acf7b23e81c1ddf436950b445e591bd0b8151c7c65ed59593eb38d499c007250fe89043154b9ecd2d006e6f71b46644e61c479a87d196dc3ab3dcadeecd4b3521f827e40e2a12d5d2bc2c7de02d155023b451eea9ba7bb0632486b59408f24c3a509c071b753b6ea9e177f260ddf0824dec6a2b0d79c396eff603e0ad6b6d30d023cda254434b7784b74d89584cb78985bab8fd5d1389e31c046e7d214bbb06cc79c439772c5a21aaada9a3fa621857260fa201220dd63ffa4c9e1693c534e16b869ac9b17a749735ec74a5f4e956f6d1875f86cf74d6c8d90748ae6fa87ebb724e5685e3c937b37e1f11d920faed22876ccbe050afbd64a5afb6fc0ed26b9cecd78d7ee36cbf40f410e7fb65a3cfa64fbdd2cd4bbc55afe07b70425aff1ecd5b8f1fb3329fbe57bec7a26386c622f4830ac747f9d3f922e57e67cdc0ce2fc905a60f35e6cbbfc5d542bf26bd8c5aaccf2004c47ae9a5b10e1f01e75b7648fee500682abfbcf6aa60d1f81743dbce42b2de0035dd50bdd5f79e5e8d6559b504e2e3f0514d3c5af255a9e9759c3a78fcaa648259f0d93a7374294bbddbf4f972fd5edcddf0a0e97d9d260835ac5406d7c48d0564164c165586e6234c3315476e45f27f8af3c7865cff7e4bda05276fa3524ae7cf8624093629eca8979d5f22485cb12216ea7528bd6953549c70659ad721d6a7ab8e6bfba8662d4b9340c8cbc753f19feb05acc50c4f0c8d641f4aa767724f1dbe188d5ddb04665d8e24b69a8ccba927c5354f377fc3cac94c28a19b383aa354f68d1033cbb292e08a17c97f4d781a3b84001a9e5c912028506dc7ab12dcbf1969672ff666e7545d94c982c5ca495cb816d4971dff6cd4091fe30fa31fcff367549d517ae31a3f8888836dfed5b493c87879a1d4f3fa765567451bc87fb7925de607a8462c3935dd17812ac4e9639e6bec4919f3e60f17a246dea1d8e72d2c7c4a3039fd0343cdcb77d8efa9744455411e8e35e1aca7adf9eb280f370d82db602fa5b2c24d7d3ffe1cdf17125adb8b6b3d2e4cadbc37c2bae0716ed1574b680b07c5927794e48f9945682a11876a504c10941e5144e5525d89868c65cc3a0b16350e4148337b63da12b1a2508e6128d6368c08b4f4923cc0799db0a5032b1aa7d28e34da0ff1ed6d4ed785a2abe6f7b3bacc86577bde57bd5a5d86b77488a0ff3b7ff1543f8cab1b40971df651dfcca50c354370d358f11da1b9573478902c072f596c373e0d66e10cd9178a1d3926bad22a77e16d2c034f83b330fa2b05469ed0ac8bafa6b9a8e6e4a10a7f7ea7a409ebd9f772a615475347f27a4a9f8f0d37ffd23e734a38684ad2ac353fcf7956b0d8d0f86ce1866320eec8bc6b0b7a52cefab7f3eb136ef723b48d320d6b159dd9d8bdb25a20b46c9da5368d5d0786212de1e9d50b9be0c41bf86518300474aa657c8cc45a9e66c7eb47f6acb28d967fec5fad0f190fbae7a46a124028ec49f3abc6a6733127661f1b73da5bfd74fe1161baf0cfff7b4a5092e963a3036d24499de29d5e7d862c1a9552bfdc8523bc0a3c7278d8a1397fbdb272f318659e1316e9f14dbd28fdc9f21a848985408c7e21195ebca01d9a93593eaa837054f45820d1683e8a82239f4aded32f27d08e67a8f318a00f8168a68f9d259affa9d9d043428174975b85934ab73a1062d27e94cbf414d32849ce775d9faa4a9ee4f18ce83830411dc28a89dff94079cd631fc265ad7db8fc8a21d39676b85fbb9ebf68c7ac0aef50afcc9c27628641f9c9cac710c2cae4cf09be2f69b1c336744d81f321f38c2e8f2294087b294877e0f74b88accd59e8fee5cae6fa85ab27d31824cbc83e4f7f2b0c35bcde5a85c14a7e578e1450ffbc4ed45f95cb4577fc3619339d29ed893d5519a87470d490242530d27112db17add0b511a6500a199c336ffacc67a64226844169946c0fffdeaad7d65e8b0cdd5baba0b5ba8ea8bb40b7f1a79575b5968d60d96c2daee2af6749513df9338e641b3a5c5150a1f10d7281742d8ba0894726daf355619bfb06eb1654ab8e9da9fba534b9c8d25218bc8021d18bb60c9761e0037b7cfd57f5cb2cf09489c1142c7315d63b961936ddc7a886743363c2e3ce14d83de2ea66d487983e1c760956153664efc66044d16e1478bb00fab4df9a3595ae76de5a08235a39dced20cad43256adddfab97ad7ab91bb0d0e3a4f4cb60667bee643e8616cd08047e929c085f69e66bcf6b00576d8caf942a919c3441c6318cb05c36e6cecf52bec474f04690eed5ce486463d8a12101ba2ed604df67d3dac48b94e3da759038fe09d4b2b5a82a5f28d8ecc09e82ba47c0e9b8bf21f7d77b4c2a495ae26929d836a34bcde31669dc10d4c7467f6db6dc2a837fbf982104fdd6226dee6c2db56dc0e5099a49135add5a6643a075d573f2ee155f238f5b2ec9041ecde098d1f2af148ffd8e2fdb5cfc22db34b823f172418082f6fbeb2b783512121a3747c10c2fc8236d5696de903632cfca4a0801a0b130d959185f4402e749c9dd74123c46790c86e3424f6b38d5635ffaf0cbfbcbf15219a71a600543329b9d993cb533347862ed13ebf73cbc3a2db9df604173c850168bda7c20fcc4a683ca7d1a4ab00d3e797d10e0785a206a1bd151fb68b76cc94753dd630ea9584a630a7908a04eb3a3602f4e3b78e1a75e5f99c233dfa6086d89636f8bd213031c0c02cf0074ec1ac5b984da85fd97aca5a6241066d299b9aaa77f7bf58248007ac0d095cf21ececea89f8c590c3d2b6774798e59f5f9a28665b9ba18628745b45179a0798a174303667169e8e24922d8a45ade8a71320a434620b72d5bc9dae2e70a692e4ea0cf7469dc680077395f608e2f12323eda107a427e303baea6c80c895ea436c1622eb4636e310ade5d2b123a8fd58e43fb2841b591049cc44743f1390eb148ac983830453952f67d9bdbda95bbdb8f48b58195552bd018acb3ae71514bfd5daa56848e6d9e4c68ecf6dac6e6a18a871403c08442fb788b22c2189771e5683aeeb325163ae11351112e27b4787be6b2674e347d9aaa70afe4f742df4491f2f9bd7c4c98d86338ec4da7e079c1e01b7bcb70f89f9aba08bcad03b16d92f474bc1b2361a8552aa671d0e14b62d9b35c6618c8c0e8b6900b9700be980094f18c9e53fbd5a99a4c534f692e665faebb05024edef1cd1413263715143b2a21d4e12b592530bd31ebf460a41262c113b4cd90999e290f355b412550aea887f18c109e53798b5778f26e3b88f8a3de89b4c628e2ecdd0f4f485af3a8c84c02a9370a4c92412855ab3e9753914bc3a459d1a5def39a941c1008fd4418d6eb396c17b6e268dda9cd6a3c52b51f2eb5f612269076c6b10062b227599b7419ca9a04fc4724e47424f70a00942fce4675881c18ab5a91333e498cc691780292e5686115694f72779019d74b0de2351ac90e75c97f5d9533dc8ec89411582d24c1b60804860b6eab643fb216f82bb9eb799f1b57d241e8f1b51e1d81b5c5d02660d19469c0a6a373f80e28880dddf07ef043cc5bec0602546c3ff2b153c58e038e265ba0a57a93cdab83e29ad7e733dcb4eca548788123d25d64cfd5a9ad9760cb1156da08a7377266ecb667fb05a782ac3163b154419d0bdb6dd12add44a20d2e19fd842b00650cf0c361063f72e113757097722eedb6926fca9983dbf882634635b76d8541520e5c38b25f7b61abe573f429ca164d82b00daacb4500d1849a4f696bead7f2c017685d75b3f0f5dbba4d7480d57ce09cbfa9f60acbe1ef3462e2cac905f06553b959bfd01d82e0aed05163932d3a27f73d2d68cab73f83b09a16b4ca2fe0ef462db4328b2d009004103de589b30731a8579106359231f670fdbe06d9c487a3813d2f33f6022a747ae9c25ae8c3295dc43a13c43637c4261ee8499071ac91818387acc5a624d98b8e5af5f7da00812371b553b5643bf340672b0006ef78a190557dd7316ad1c6354774883bc815c87915484071a6f2e474dedcb1cd680030d9809ddd0c58a96bd08dd80ef472c80b38f68885e856071940ddbee19a934c439a384b74838b70e6a1e9d672463ca9648aa9a8169f21337ce4e455fa0b550d6e57cca734619c1d73d0e92fff2e60b05b073d2860ae7f92c17b529a136fc6b31875554c02baa5c0a06eb28b7491ffd5c324c780ec3a93b407c27172ff40b365844e5a0ac5b9e48c139f72f8e0d2bad067453ae14174b3cc303c52dfe16573a8eff4e055073941ba863046767f27024cb2c2db0c3506d824f86d3cb0fbe1dfc94ac302093b5c201250f33d97233867c086be8a42a1476be19a1ca9c36ab9b2d05a5eeb1b6f26a04c62a5299730167f5d1f2246474125442b9777c8420934a8441e68aac1d167b0d2cedc46af49eee5e5a7deb8b95f8abd13879ba647c65d0e70428fb25388995ccfe497c3c18473164dbc3a64f35ff1f7a958c06eca3b4b71aa8c7b13c3d5ab21d12f47e554a3a2a8c5a39aee1f745a73ea18a452699bb09b5957352dd11a461947e39c1fef3ecdb1f555ca8f770e3b07639b7b49594ac16e59cd8f51963aa19fee57af39e2ef69791757ebe0dfc98fadd7f24e06abfae6c6ba745c9bdf5a0fa16d8566ac0b04502b3e60d83a01de5c8d5545a8c1fccacbf54c3cf449ce1a965687e09b8b6eca09ead113687b27894c9d845450e8c2d94998d9ee9b4e8cfbee3d5640eff915dcb1a951b3a19a9d209a2df5f86a069a9927f072cdad707443fb111bc6c84b848d85159d135d2708ebe9675012b79eb0ecce1f33be2f9016d3a1908f828939c365c6b3de39d1f4f2a9d9fc13dec475471067a4ddc57b4b8290a74ab4dcb7f911b1087c67274928e7ac4fb70925159da048208021426add74f3e3ee236af5be62a95d9cae2b8e618d4864ca7c8871086bc78464d5fa18e561fe4db8e8e4954b4949af877ff9ca62a4b44740179d7424ad674f89efbf2b843a25e36a691c76c82b1f64a717cda6bfe3c795cc2a3ae968a5343e901910e2d4275a748274ee876aee7bb00a585ac20371006da720474a34b90e080f0253c8dd40e0531455922c2bd509534ed870c31262ed07e0027b225091b2d01dfdfd5ebb8e33c7eb68adb783114250a26e0f7a47358503f06d3ac9c54d82a328195e038024f4e9f96ee4d86fbd497ea9fe175f9fc804cf02eb2b674f692daad64f8717a70c750b83bce77949893fa70bdf9688055ff558b2feb12e4c83cdc3199e3bcd4aca219ab509b14ebd5ebaa9c9c1f3fadc67f949619217e795b14b7e238aa17da3c026d511c7aa013c07b50ca9570cafafc8ddaa1c0950f428dfbf4e6b3fbb6c72cd3c4f97c9e0a315bbea275c2e426ba04aee08351ba8ce9843599039c665c686367670daebe6672351bb5fa609e940a2733cb745621643558c47a01dfb2fbca5c1d16836c0a1c0433fe978ba2488edb18ece5fabbfb808df610868cca18d73a94a524ea7d9fa2cbf0f949b72aa2c5b3f214134faed526249fb9e8ecca9a9d33056d368fb1d549dce0a3bc596094a66eb3a92d3d4bec1113e08be8c0ea30312a8b817d0d5727e3fb9e0a9146975ddb0d4f1602e2aa5393351a73d312714ec386b8a4d6af69fa35e2f7e451abfcbf63927f36e0ea18cf65f5d2f56f5ca637e3892882a3da11e8cc4ce867b9aaa6571de806038245a13981fa759b4a6d0b51481fca45eff2674983eb4b96c5d476e02fcce12cb57a221317048a651628c0f2c260b22706dc7cca6bc6b83d4996eb2505ec7142ecf2cf185594c636c15a8491a5bbe7d19b09b516831150198eaad5a83ee257756409d331fa8000b091da8dc4b11e0741c4cb8becf4e115dae06378413c2d7569b4a2b2bd14f47f5e2f829f1549b1343dcb79c22535d7aa7406b9bd5584b1fd8d8ef0c98bb31690b35639b1352691c8c941932cc797e36793367c34095c6d1b6c03e8ba0b589058a85ea55290a8c96ff31c3ca8f7c55c5222ed1081efb4477b4dae99c188af7752d982d758ef8baa0b9823239dccd86736e306aaa659984fa97d6ce045004d4622fa52b5448f19776fd3a1e292a7ef620c4a186b650252cd4c80719d08486bd1c50b06cb46fdcf9790599eb6014504c4dfea7f8d6b445bb3e0a633a45682e29aac6ecf2ed2d169ce3a1931fa77bfda7346df8e14b07a0847e701a4b37928b2d09a07f608dc24dc225a59e94d0f47ba3cbe4a407a03b90712906525b375b9f5b6a112b33d015a2985c63ea80ea07fc0687ca6307faef69da2e2e669cf9b0860552f2e678a2d89909d85178ffe78e82bd95db64dc19437d18dce5262227547d2700d1cdcb89967a0deb43f8619b7b5db9518c80716ea533c080e067ab512540464d67db2246752f8b5ec0469144baab9795d19d9f11922555d59fe595b68cfc188eef632bc4aa135b043083248c12aee51f776bf504481a817d80d36ffe6d9a25039071260e6666beb93d93ccb783ddfedfe58e2e3a7a8d68bd3dcd5ad329fd73ea94f7d27838f149d882b7a4e8cc41f54e89925737305372c483b42b10b604f4411cb5b318801e4bf72ae4e0bd3560b4b207ef1089d02d3ddf9f4a73b1e2443e5cc2d96ae33b7b18e182fa325916ddafd231242b7c3eceb311fc59000f01e5b63d92ed2024a4831e9f63e599ab5b9321a4a38ad650be39f4c804ff2abe90e123a68d6409c8b4bc082b7573f0efd4414925b6b4fea116cb0a2470ab0b023a07050eacaa643b2b55ccc954095555cce7086807f26a45379692936abe7b735ff12f0c5c805dcb05e849672a027b4eeffe5170abd1577be274567d152bdaa808f6d899697e63cc3f3892d0f23e5cb8c82277679f270abed587541a04811ffe8321c8688f2f46fcc2e02f463d31e00f43bf18e88d815f187cc5403f067c61e42b06bd31f209039f18e8c5885f187c7c84cede2874cfe48ce4a41c56321e4ae6602fe895cf6d4dce372313ac334eb524bd5c0e577d5fdb2659a7b468e82b769dde9fdfddf49cd42c7999f17f333a3c6041a499e77c0bc2421a42e2cd67ebd60e07774fb780477f9735100e8f9b0356dc8af756363759ded51c89d1f710719ac57cd40f94d27e1edddef41752cf55616b2d381dcca0d88d92f92c2bd9e1d56c9fc646cb10864b9c98393a0dc395155cdfbca3cfec44d7b66443b9facb35be8ff1336ffff35d30d097c4f04229ced104229f115f75c68ddfcc036a5589430c3e41f7dbff47df70f7b4356882f4dd776c706d7dd5ee2995848ef5a41c806fc08fbc9813385d8f7987888400591fc9bc9650d5479dd1fed10e613f5f6f876fff759f69c2e1159f8924050b40c6f224d951f421065871b9b117382916fda600195f607295bb36277d118670848827877f563bf2fe9fc2cc8680aad045ca40a8c8fc587a4746110d18136e7cb5541044302a0e299a43fe22b76c9288f8774f077bd9d56dd42a8af85ca731b3c1ed678741faa22002605f009bb417bb6aa1217ac604220df4652c438f6ba7895fd4196f0106e373e13383d5f8a84be589adf4d22b7a37bdf791ad046ab63cdacd664214b35c27daef2b9a985aa9cf41a23ec1bea61cff264de2792a6e4aed3ddd824433a0d009ac53725934dfd210b724a2220aa7c9707dd6ce35dcfc6cf198c2f2db2a4d426e8d87ea99e40145751f73cf86525b330f8c8db803499bcbd3c2f67fe21038ce970ad3a78982928813f7c48745e863a35cbc49dc189958989f484f6070156462f1accb3d85709a7b62b23d54ce01b7381f48236b0813cdcd7f66da5f28da8fa32120d0ded830f8b5db23948415c2edb40b8d401e1207b6cdb360b9004434b8d80b553bec0b58e2632261781601545d48688cc1b430d27a99e955180c289517fa2bc961640081f3a22eb85cde4adc8dc5af55da6e457eef5245426d8a73073e1a740cc00981c717cd085ce5a72398f25eb2c5cdc2376d1bf428ed2523a2e5f83320a35b48627d7d98fbaa618d3793b98cfb0b75a883ad5a8d37d766e5e9aaa13c5c444075b4b1fbe02eaa7349f7add57e9a0ba8569416f06801d8b45dae29867a2ef2a626365bf70502931b41b9b46b04de5b9cf3c50c22af971cc196799b6d0158a7a8e90954c7e1e0a861adea3a288239edcbbf081e55ba8b6838bf19c989cebf9624aa0a08146d699d6b5ec43737229e0d97836b37561d14766b33f55f65c4007997a9b427eac8da8600cba2282ffbbf6d998123923732449e398494aafcdb2d39507eff9323a4ec6492568a8ad15da68fc51352eacdbf23286b27a20f6c490c4c0a69f4103f953af7f080db1fa43004d31362cb751c5ea1d132fc97735eebfb17c29826cf0132ddc200fe7039489774f53bbc829ab88cd460bbc2f080586530921b6fee46b2ecf409be32d7ecf71e83e261d48540c33369f8144048e23a7c3a840931b15f5d4c6a7c81b5ee21a4299d1a67872ba8c683573ce8eef53199245eedd5dfffac6db0aa3e33044e77d235bea52329a429381c56d8d19db41948ce80e330c14fc43286a4ca041c5b501aa6e4a9e8c5546352baa5ba7d5110576e628cd119a82a37a720550ad1b4615713264470c4a18f2e83e51a4e32aad3f65584d9e584188cbd29b751514eb53a95e9ceeb312cff12a7419fe3d54799905c690037d2cb6b6f300ef0f8935ddc9c574ec46247cabda17582b9b750e92ff084da56a001b05b085b878da0ae3c0d0a6161077c73936bff7e5f36f829026a210aa771678713d8f3d679e5353b48d34b4962fd1c05721ba3f2302b752f3af24d63fc3b91803c5ccc67d6fd16372627ceb52ecbd52deae5cf0b9506b9fca4c3cca2a5f3ee8e1fc67e5f537d8835b517e6b78c1c272c2c40a6eb12870960b2e1a44e618142617ce9b338d9d3539b53aa48e517c2641997497d44b5b46e8ea4c116e023a923c5c6d274eb5d6201e90a5c00f96321e0a4a076edc88dde9de193c50de00dbd0e15a4b6e23e9a7f70312e75a744cec7e17ee414f0b52c37c5af1333e4a1858d3d9b99f9b440a3b5ed7f39424a8ee940fb320dd97accbed5ea28f997f5addcfc81cee1d1a45256df9ce0729d1239925f3c9e1e37a9651203457b76c2dcfd0db3de19d43f422a1cc6fa4ac64d9ca6665c4d41b3252b04f40771345fc5aaae65871b66b04cd7e3888e47aa261511b406b1d61afa5abb446802d45421538654cbbad77792436ef972b324dfc31c184c4093dafaaffbf00db71e5e39735e37452571c6a682f939af10272349abe6bac14e37509a1c18222ad78702161534753b292a7ab947c817ea8e668a960fbb484d64b0110f4817f6385a58a5ae29d3489a982c4c556d34629879268e85bbd64a9535feaa4dfee7a6401d7bfb82d1b6154a25194af8e1a010b0435bf322a515a68cf75a7cbb02f5d3264b47bf05118469ddb540a7c2c754e067ec410c01634ab02eb30634860a6fda269488f0d826d49d74da29c95b34c23eba455e27046546228af960bf1fb275865f9ff5661911f1592d8676f83d129f4ad8574efff5cf40e08274cc14afaf6ae8991e026960b942db0177804a9250f010d912db1aac8dde61b3c3191c07a4557fdc4e42be5ebb9127f1de733fc8c8aa27bfe0754dffac6bc4029d5c3c9bac743220b192dd338493528439843361ad91f355e54a5e7a0fd9f06759d88fd955b9aaa6647f408ea67e930a6bed848092c2b896c24bb5171fd8c80aeb340377a132df212b43f1b93dbb6f3e1404aee3581c6fde992806abf441ee5d6f43c32d000a20c1fd384f4f7dbc4bb49ce205e97588075fa0a35dc055b650d09f24e52bd74b5c439f23baeb01241070c58f5ca295d34653b79ec871145f8ca6981ad0f1c81440b80f62ed8c010eb7f1a77c5244809db8e47ab8090c50cdc1e7101e33dd937a253d269ce9d60cc9ca1fca6ce221d4d3d57f0f8f70cc453c7bcdf407f57d877e9d67be21847504b0a2e36d8cfc4e01d842df1c96815211b71ed102a2660110f1a14fe61820b1b6bd6975c7663ecd0e54f2bc69c830ff4a938ce89090ed987ae5d1157170cc074f8fbd5605f8775c4242adfb57a197ab637a427a0f3d8a6df7e0fb5f0e67df18643832a13cf197a6f587b8324b9855a424ca4e2db5d16e6e61aeae9e19d38500403b9e36690df0a9a59e33a4850c7ec5cc9f0143dcb945fd637518076b586cbc5dabc53aaa4ae147c731cf45411ef9bab8d825a6b9e632ab65f999199bb4ca49154ceab73a6761c6718fd89f2e7a1a8ae1211d7bc3ede6957d385934a7192968c44b3066c48cc7a1a357c6213aa064c6f02b90155b711e477736208886c9ecb4e8b0ef5abe21c98471b2058f256f0a3521bd31c5426959922ea5e2380a71ccfa54d2ed5f2111f0fe4f30dd4c582a5242e2309d6a3a9cdad816366dccab7a4379053e5fc1395892657a29478b2d501dc2961108f9dbba494d48bce0df7f14e0e9ff3036c5e31901b53dff87212a0187e43568d7fae7577c963050703628e33b2ab58378731d15e92f48f2e24440fbe22b518bb5f59ca743a0c8f856247c803e199eb1f184881aadc985afd8449f8431fbefd92b979b82a686c963becd15f6f6c1f07435d5ab60f5c3f195af6eb3e90dacf9fd791577a7faa9c753530e2f7bbdfcf0239a3f62dd4f91cd5377befe6cf949f56b9560b79005af45fd4ba2ad4cdac86370801d99b120d4e932d37d185bf31127255f3a8db31c4589e763c4093cdc6a1abb11690e52fc8f4715df831660f5f7a1cf21a4022881d7cd8fd8a93fcb61f483f9279a00b71ac0a3143dbbfd111afc1f967ea6b2227cd625121b03566bd90f0738909e9af5c1a3453c72101d38ad51198bf3381a5506263b89e9268ae8e1cd54f01acda2c2ae0e8a31055cadf3e41a8ee42012d08618ce2ebe329976f7170ac118ae1dae29d4c6a2ece6c99793b2ecc11c6322190fd220090c7771784e43a1e5c59e5199a8b3d2aa7e15937279cb5d931061c986f1cf43181b7bfb599c956afb7139a39745af32174a13b1f3621405042b9f8c764699b6833f8016a886141a136bdbdae32427df52d4acc8cd14d068c0dc11195581b06f9f2b0bd167e5d42df622045b8656971fc12402655c7faced0d9ac8d55f25e6cfbb8d9c91b13a83e91fe3ea06b9ef6111b5148797ddc41015158373a6325bf6fca08f39637c61fa5e092f4625d40a257ce6eb11c05f3dbdd15ddda1806e0a5ed2869e0105f4597b86eff75fa4db07ca60e1f63894cdb2816089223884af53b4337f60f9f5ba544674b8b9037acad6847d5b8c7214b0dcc1979b5d042c90d915dbee0cc9a0b0fc39aa6a9934a27990599a697dfe456d0d95797c8d22fa57d1783ca6cc2990b11d082bd564b8b09ce54024687569c2ae12711b2dfbe0c9aed8a0ae4870b02421db8a3a1eda489e34cfb9b07ed117860e52429bbc0eb0ad088dd9d39314a003c7fa02fef521db6eb1970d18b8d465e8cc4fde7a23db9a8a5d515b5ef922f9b9b01d540636645a7e35cc3402db6d7dd44d54b6212bdd96458585aa8eb468508871c272960391602dd2e4588531ece35cc56dfa4bd207db5664d06e65390824480ec56c18ab5e9a3c6d0c5dd37f605f1a2e915bf6766777f3fe17a8d2889ce0880c3afaab9a281543091fa338c366de34c51bdb9657ec1a9ae341ad83b316fabf427edbd316c7dc6085663c87a101976a37e718094087a6d8aaf3c1613b4c338a821c8427ba82f28a9668f8588cca463d56ced75649477e100ec5005149dcb7e4d5c9b805f0771d985274b7411fb1954b596321c4eee682b92863704af48b1acdba0591994c3198437127c05044290d2f4f3ffbaef0d0fb8692773584e1f1b59553e4270af83b0169a8d26e684033aaa832284785b54b744458e5a496c63bda1a5e986478a803c846122a629dd86ca5390cb37c58310a0410a611998c8df877fca1b5ee9d9630331928fba5071479852ba094e82c1b5cd756688463094a90ea685169c6a606d8530be195d01298375fcbf3ffd35f42fcfa96cd80fc894d59a815490039fbbc882d37336864c7ab2a658029f1a3821fc890d2d90ae8672cd118d67cd269ef20e5e4511b160e554dcf61b1b0fec58b0d08739eaf5cf9e46f0404db0373d0c2467835a12bc42691d69b9e63a708ddde6c45db9387d3cd472550a1a8685b699f1fca7037f57419b16aa6f5f1911da3e880d51026be9a0c4ae2c91f4846697a1e630b64c9f923a6f893f6b8d2d5814409032f9fc3f9cd2edf02ce6731fef91f1f9d1c74af0481e45327918f5126025f38a88abd316993bd16111c02576ca976f4ed0e9eaacc35ad338f9497910812e9c56054ed5f7523e059294d16174f919741a632a089359e7fb1fb6a4f545c3ac5e4d345a44579899f88d91a64344a2df8cdd9750d7940c4a9c6289510160a597a37304e4ae264e7c42280ae3c434439a593badb31c04c6164889e7e69e89bd301ef39919c8a48a49a7c2efb10b7041c4a8099ec915aaee5c3bad2f54bea98cc31b3e079663c11a016ce6629f1cb47a9f90f33da5b291891acb7876109bd95c970fcd850c8487cafe4197978a2fdd5d8070b22b190586c42c1ed4a6b205dc97b6d40fc1a1e3501e5b07a5ab45c4a907d74bb8a8ed415d3f32205ae5c90db1b2f8e8b924e940797fab1560ac7f4a1341565a6657d4976902abdff1f48cfe4cb3382f2d4733a1fb33f4b54c255488b5085eb1e101bc699ea18944e50f9ceb2830d0ec7c20f3629776213a7cbe76fe49c772f42f0442788e4c55efe2098ece66c10da7d69b999cf33c4fb6a9b001da108e1359052ff227b85236708ed4b2d71d48cc4a28161158cc8bcb8e3131b30b87f2fe360e4053a804b3982ce2034352d2902fd41811cf32578003b6121b327d0e491a5c851a59353e65a5a3d357b8e13c0228bfc669566cbe68860b915f27fde7dad6f73ac39f17ad0ceb0acf109a008526561fe4a9e3d3d84d98be8bf2c08ac670d8b9b7011f06bf5aecd6a5ca0fa7b342d9aca18982878b45f67b851b937125f1403a50b41956ed3e411b9e384a283508a0017b06ba933ad39b3786764bc6cd37e5ed088ce9b7fe1e57fbe1669675fead7fba5e6c94b75f485d3374f68a6632a1e1e3d034c6309b69294c063e7bc531b55532c0ae0ff6d0c12fda5071ca06559e0e1ea1fb1a3722c5e264447e0db418816582a720acc93ce8c3e70c9f4d911e46b9dc9054fa0548689f74d866ee4103a2754764ccc1695aece592f47a80c0b4dcae5c284197fdbb63716424f729a13169fe272c1ec70c3eeb5ec554d0d34c897c4fc501792282280d2edd9460363410e74716ad2991d6664e5c497eec7f3067e83e30f0fd3e9eef1546e521247fa0d23490021d06dcbcde5e0e6c6edc868eeaf5632a327717f643a1a534506b3948698a79ac0ad6fb5359031c3f42284ad106a01970730e0efed716296af748da3a0d50dde46831fef496af2f942598daecc14b8b905a37ac183ace0c5ae1085aceea4bb119042ce8a5e46bb996476b8ea7629e1ce07dbcc31f330bf669a95599bfb4fa9f357b93f01fe017e394f50cd7059d9745e9fca8fc35bde26948788baa2f18e1289ff1e12e27e37781ef4ec503d2c6c910ac514d83ec860c403eba82e8e6a8bb0e43f23d77e6fc1815c41b6b3c4e8b2427ebe678fe2b4243b1ea2fcbdf8904c7c72a5cde9802e1c927db06681dcd1ba8173d172963581fb78ba4c69cc9589110aa80eedd6ecf7e2baac1eb8277038963759042fc7cc64bde6967ff1143263c926660dd41568f7c805f993bf16b0c4332141c9d12db6af88a118e447e3b860f7420af1a5d10ab008b862b878f9b5adb5d9757e4accc09c384a860d52e4a431603bdc76e259f22a01b417ba5ffc126c544dbf89a3e56fdcc8e85f56d82cd162b7e761c08133f3094c8cbeb4fbb0014a24cb444f29360f78972a8c95cebe9ea1b2b4a2b15a4824c1f3aaf28d27922a3966d89ed1ddcb74235185d14b5e6c9749bcba747f329bf26fd6d8c0bde1b7148a8d83a821072eab17b9650dbc3a65fc3c8b96ffd278eab6c0428cc738193272fc00145b4eaface5922abcc326d71629a056913df85b454f2613332af046c68d6cb297e521c310c74719e1534b989accf3714a6815c1de53a61097f2166ef43da424844d9600d992cee97f1ecd48874c29bc7fee64d0c30b9c4138b5c8f40fb3fd0ad94ce20240a715124f744b3a915c64136ccf1c613ee53b842e8f2527f8b60d0941cc08deceff1ba411829fb20400e14f740f11765f0343d3c904980da5a2e935d48493c189f0c16f09d54f31789733e169746e3705d130fa7891a4730779e132539d57e151a6a678b47d49549ad6c6e5a0b1c3076b3355e3faf906cb4711c9dc052c62bb62f557ddbdc527b87819f5e0f7c7a34c5307cf9fa766e6b71280f0ce9f14a3197d66349812deaeaae8d7c55c3263fee22cf8bf0dc44837e69c33e4ee67f2c62e5199b1f5d1b291e2319a154619201683c0dce007b3366574ed3074b82a4c967a214c7873076167293b0be438635aa08c4e28a2cdfd6724a15b6e3158afbfdb7110b9542d1490229f39dbd70de0cc7115c0578d54fc28898d388828ed264234be07001aabfdd49b1109efcf6d145af1f13fb0c06d170842ceac02cfa58f874a289ab3a34616175f3997d1984e1de7a0dddd0cfa91951269cadd1e0fa1ec10aaf54bce51cba3cda5c495138c98337236433936e535f8015c9ebad54929dafc918843d811690d0353159ac37a9be7f6351af277e242a24bd7f364c25866e91541a2a6ec81f58550399d4848857914e7b16d35604461fd938bab0d6d5d58ca37f9df3cf85bafa0372e272afeda182d1edb546ebfdf074ee3fce1ee0c7d7f19158f025b5b0accdf9ec5f135561b5abcba40d6c264f852a9774084db29967e3ad0be81d9997fbeab384393f4a6ccf392052482f9b2eb02e5ff1dac09fa46c6d7304cd9cdba7dd237c8975d2bc678c35408353ba04dd73f229ee41afbbbc569a7283d0c46f2861177fe54a80fc47da0b5a5d522ed852c638eaa4f5b13fdb1141a16aa1370a1e81b3208ddc45b6d4114aaa8640f5d38cbc46c5c0e0843cca32739dcc36dde7f1f10d73f175b1207df9eb8b901c15a4ae5f67a41412200efae7039d74e1c6823a4076de35375e68eac81377309f9c72b32d5c2f8bdcb38115b187e605aca98c7059d2032bd3a2d7575177b9b7922894b50851988f78cd853384143e95dbdd2de15c6f8ef251c3485162ce8a38be706abab55d799b2ad7e41a139326fca26fdb7fcdd15bbbe26d71f912378ccd4706135d5f188411a9b29fda558a493f6fd0b12d54496f34280a9cfdaff7924d743af6a4199749b9cdc7114c532881e82db40031fb8d2e58ec1f2b38e05af7d81f0b4bc150024513eb74373f3f3d8c8412e7267b1921dd0aef55e2def1717336d360091864138a3264d2e23d9e3e755fc140e03316093c82590a6f5bb891985e50fd51f7082cc217760349650e87a2a7ba352fb0d808c0d469970b92b496a3d22ab7476c6c71498be97a21eff5c491625377b0798beaac618aa597344f298f177a5b1a60a4d958cc46b2587a8b0389f79f789746d53b6701aa087911beab2ce9751e17a8d67c02032434722f58346c6456d7226c0c7f59cf31d1c15cfd27cda0e2214b24cca28cfc68ab0cdf3fd2025fec065070c41b81382190a1c3e4a33f2c530e39e9fe550ce9c98301d613200f5d1a94563b2426f09092adfdc717d4169c92e9dd01f10ff42bde0cbf8ba9133711bec28ca869e4a623a7dcaa7f300329bb38a8658185c168567c0222001ca053881d640c2f40745b929163858ec03106243ca041ef1b6c19698c8fa0aa1c83f6f802561b098d007a93cec2c1dc11765f88dfaa9a4837063dbfc880164e48b5045d82b28fd52f5d787035ee3c833b0e33c14ba3bfd993b8b759ba3097617b97afb7efb85c307121086cf558418e94dd11a9fab2717727d130dcb6455c656f6a74ca296e70c973a7a86869373a17cf0a406d049c3ff364a04dacfac15c546d2dae56567e92846c8992fbff7cea21c6b3efbfb3f5f2ff3b1306797d4062f4a4acf2a84844b0b2ef46380bd866e1855e7f566693d2f201c9987f2e617b54acb34f12133585014babcd3f64b5b674396c0e996ab3e021ce801ba50ad456040d05d6d0e451dc31c668c7f22da9fc5fdc4e01246c8b93e5da52e1b5fcac9d8a6e0a0879e306b80a72718ae488b7d06f847dae7ebed0d704f0e1abbd5b5031ff0a1e18076de053a36e3cd1f812c8ffbd5d66ccdccd6eccaee955b4a29a54c013004340494042914089e3e10044d60c9034190869e0fd0d0f301ad35ad35ad5b584e26546b0586ae522764097ed5b3e8a65d7446dbc09ef0b1d8b3fa1bae4a2c1d8221086aadc75514f4454196d62bbdaa1cca345f21583994895572ad34188261088a3dabc7624fabb46a551157462c1d30ec4416267edd559369d50a5da813d895aa096b568bd62ca9ef964c166d793614eab46ab94c2d21b89550a754c7d248bd42ad5aae53edc41e17178ec5457409d196473ba1562dd7e9c5e5e55da21097f8ff302e5cc59aa5e56442890fb36ab94a465ec4c246bcb884a0d61a049704b1240813d69dd8c3f258ec69790e14858030fff22eaf126350a105e6c525748126ac2b872ab1585a5ef43571758592d8a42b876aad5a4e21c8994a26164bc963f1b892a769134bcb29043953c9c46229792c1e57f234bd89b3f5daaa657a9538c3ef58751caaa575c7a142d0642a9958ab92b7f24c254feb4eeb4eec0141a32e035fb9323aed95d169a764958665545f9495f2d9f4cbf5a5127d470906149d800f5d67fae6a4b6d6a9c14db4bdeef45c8bb3d51a56f2955fce3a2b09b112edd977993ec6e4f570edef645139854a9dbe542a954ab55241e09e3929add5da7b31d6b49cb76de3b8aed3daf34aa592c9f4dd9c3c1454a0299014954aa954e04ab55aad52a8d5eaf4ad562bd3aae4ad562bee89ce634a7f52b4e977164668d3a9af881f46c86d5c9b1be9d77523f7437c73731c2734356f222b6fe034916dfa37705c1b4608468813e70671d8dbcfd64175d09245904dbf0471d042be0860d906a27dc0b2cf6d5c9b763e5eaf476ccffc258b20f7f690d9738cfd382cc95772b19f4d3f8751598efd6cfadaa8ad3f29c242741e53c2e86caacde96eadbbbb8fe5fc6ee6397bbecf79eecaf96ccf2d4e527f8e079f0929c9f1704a2a8debfdb91db729bd8b431bc8a682edbf41f1991f9414721bedfdb720b739b94fe8964a0cb7069fb9384f38cdcd39dabe93b48fe8dc9d4ac37affcb53695adeef1751b47d8921a7b94a9ef88b7e6983b6bf6de234f7478a93d4df1ae133578892466ef3bdbf15729b1fb7f1defd12d56c4eaedeff2bcd46a47337f5ca68db5660e7f27adc9d39ed7937f6ab68f726f6b0587f63b8384da00c11f98440e7e61851722b95bc9b1e960712b90c15d017a5559c7e9a290b6a8da53ffd892fbb8a75c3944c8222aa345f7ea39548865d29ff1453f0bc1bdc852eefa627fc95aee2d5275074e5d01af43199334b6c29aa2fa2fa129a3224cbc1e141a7067f09d9419ca41fc46ab2e9e9448464d32f8d382b23239317d4227e412e63be742ad5dd5165187d738a5819d5974511e1b0c4ba535fb37b8d7b3e51f46ef28b2c9bc8c24469243f80bc4ececa88ca090706c76154ea8b65b7b16ce9616b6d2c593bb5e3ae17f411e59d6ea5dd9b93fbd93de8dd74dfa950a142663ecda5fca8df1e14852c993fc3ef003d994704ea0705e4eda078503e6c51d057376e4694a4cf551dddf8a3be649069fb9495a0a93982dad1d1450e1bda1971504c86cd8dd30c106ee0f868681ade8df63570cc99393367b6d74534a5c948a0396f799c5728a90d314393432684593497984c38cd349a4e68b299dafe27bf31939832f48d2acd9ccde66c26514ea2211cfa717837f771783decae6f53bfcd6f68b22943f197dda44236a538f38a6cde98493d184b6d88591efa72847044efece0a01000a0e70c670a1cae08003ab6039420d400148870780ed8323b08b043a3111371d4d48800004fc74f006444e868ce9ccdd9b432697451d19c45219230afe761783cecaee30edffc6e4700740f7dc32ee74c936d238a9b683a56b413da8892453737decde561abfb8c7dc74a1c66e5bb4271287953c72cc50d0fad8d349592dc353c6036562245cf9cbf6fce601e0fbce7ac045f07a3870fea638439828c00e61473e62ffab3074ea38bbc88c6a08b2829459100646232289d8dda2852a50a121a0387ab681f8bd9c36f1be2a1b20971e0ab95e3f9c193271e3ee93c263eff720c7798fb9c738e2fa67d77f1c2a4e6ee5e7d9268e193b3bb7bf54982854fad96dbb43d5560c52e6badb5facf29484ca762c4b5569755fdf698fadd2cf2ec606b756ab5b6de9f02f3b9bb7b459d5c52efae548bbb8a056cadbe6d4316cbbd15ae58906881b5b2a8dc5bb46765de1f9ffaf2d7417312a98cab7d7139c1bc7c0eb3e27b37fe5e6bad96a7c4b3fdd6e75c208ebabc3a5a4597dd576c2cb497f4cd16572375cd2efcc9cb5e179af3e19b5fafb5d6da17da63c2b3f2f3af7fe7eeb78cd56c180c1a03e6ddf84f9918323160305eb878912d58ac8851318a0ff3e2e292a136a05c6b91f868a574da91ce2122907a8e8a20c9a0e1cd660de3edbaa873567ddcdf4de4462721d72d8f9692d7bb41b2f8f3a89180ffbec519e318fb4835e8b2fd187c5573133525adbdc9aa6dcbf966fbaed1ce39371b7374648fc47c12b561fd6c4da23f0c411a8254eb0c4878de9b825a6bb5010933d14d9be98cf95953697cd05aaae3a4d5fa0e9f0adbefc755535fd4477d1d8032b179b4d8752c014a508b4d2fc6d35b72be1a1c9486003e80f87276ace152110ec2afcc9014c407215058a08928a4d8e490020d98408826acb060c78502882736e0b66e07caa1e86b41477374d4bd5a6a2979290af804e4f69fb5d6ea4df9e65f0a08e5246cadb5628d0a77af7cf3a324cd3e5f993f3a53e66f6e0efbac29cd47eba63cf6be5b6bbf7ea97dfe4e13b3e803a8fb3e16fdb5ca82cedf5a6b5557fffd7b6f7681b5d5da5a3fffac8f7f6ab77a23d4d15aa7b8e26bedb576d4f68822b9d36cdcbb0b74e6cd330c3286691562e28bc73bef9c2f6b3df0f79bf3f3d7f9f6eb7d6dcef71b6d4e6d4eaa7d25e1deb73fc21d7511b118131b84b036c686aed3327cda0ff1694f82864720770442e70d5d67f20e4c5aed5da22da1396f5ca7a9e7b0187cf34b1e8fd3d4e133d14f15baa0420baae9ba49a554aaa942a554a00a04c1a6efc70c1f37116bd239edbe39bde75e8c31c6d8628c31dec9796b8c31c63fb5fdbed3c9a232c67f350dbffd7dad8fa9259dcf25554c81560c8324419ebb4f1e987071f2e008cf11d997a1e8b0122c3ddf09079f07fd702508081db607fd7065973908881bb60729b9b24b1ba4e4689fb6072929daf3e64ca04a0fdf93567bb19637aed35e698a23c0c10e4fdf29c94e0ef5c99b739e504c26c784d529cf5a9b521da10255e090291d147005ae708e5782156bc5ba172809c10a5921cea1f3216c852d223c0ca192b7212d9616cb11447c367d4d440911241f8bd65a6b96162246d9f483b903142e6d32994c2e97994a0997179717530a627450f01d1395e5c93a37474779817981e921490f4c6cf703a353a9540ae67b30ea44f01a0441f0c5ac8160b1582c716cb55aad51c5dc21869815312b523c39277f482bb48b8b8bcb0a16d9ea80450b162d8cc044581bb4d073033020b5288a22f952a142858a970b27b2a626d363f202c60b18437278b8e23364665190b512183018304cc4ac8725b24704160316c34ef962c8c4e86434a24f2626132b2a72831ac8e0063119b1989dc2f34d913143868c2247e4191a0683c166cc20924d20232323030208448ec89e6c763fd498a95163cbd145925039ccd0cccc6c073292b353db81348043534343a391e86f3b90111cb28e910ab46e5063a3c6eb6ce829fabb771d66c409234d582341467c8c2cd9f435cd32b06163c30611a2ba1d88c8143be555516c6ed8d830d9a576a02239c044d813dcd0738649b72341021a76386fdcf06eaec31c362489b61de8c8066ab8ed40476e8819b962a40acb8989cf818ae8ec523b50919c9d6b8843dbb061c3060e1c2b22376edcb89123470eefe6861d523287b782dd393687d1cfe1f5f0fc670822cb29069f03f5302bede636bedd662c0015ee8789cf1604d97b8f8ea2dc28f6e85e1b746d1421295248b1831dece05e6be55a29808064506228c8de2656ae1db2b789956b87ec95e2da217b9b58b976c85e7bad5c3b14c4817b748f8ee63cba47f7e88806323070e26466af946b9d24d92be5da6b93ec9502058945b24848b52259248b14459369e7db39e79c4854ecb5d75e7bed104ed29c73ce59e79c4944494449444944b53a4112c30907cab516096905760517c9da0bc5de15b4606836ab52a58ab537c9ded91147e010498172ad93247ba55ceb24c95e29d7ceec9572ad93247ba55c7b6d92bd52a020b1481609a956248b6491907e0220234237675d0dec759264efccda9b64ef6ca88a09aacc66b39a295c50a10595f5efc694281d157c37d6de7b31c69aa67579e33aed954cdf099552812b56d8d22dda3f27849941a7f9535ff4ad0c2d2e1880e5b8f8772f302fb6eda7edd813b58861dec59ee38b2f712447152d54c414c14296737db6af6256acd06432ede743f2637bce70569a4c9369324d362b3559b7e74cc80aed6e23c312b3dabae89b82057964dcd979f18fd93e084a4e254a26134aca349f51324a0e6709a60f25e9bf5e2e7e8785a3cc5cbcd0e16cb6c14c4143f725c5403716915b1b51d2137a0103032f3939309e14183017580c578c16994e4f9d984c4ed79a3812f0990c1933c2caa2b1a251036c52c33bfdfe45a4642a045fdea24ddf84059ce0425fb1db5aec61c0833e2c2d4ef47a50c0613140cc2898f0752ff415370c435f3177307d3325f173a28ea13dd135aef002ce76bd16b950ec31e15bf7bbef48efa6fb191c371d86df04311465f415ef0ec5909274fcea8b62293eba00d15d01a2fbe5794a7a620f0a7f82a8b3f1b388ad133e147b4e7896d1c6bf0adaf841ebb3f1df9c2764158a12feb3f11bb1f1df9c9e1b64e39ca49ca31c5bf46489cf0e6dfc338c82f8d517fe135462ca00a2d7170244afaf04885e4db0bb8be993d88380ffc4ef1020a2c403884e5f25d94207ed6464cd726bf87c87d341a8a5d22843e3a4af5481a055356ae092377e94c46f0031cfd057a4579c34b4d863c277e227e2d907abc1f110b3fd5ca6c0fd50ef66be7b2dd43d82cf0088866d1fdf9402602881ce1c4610850ee6cf03bed6dbe125dfdcd6ded80f870571828549e3017446eb348cf18c41c05ff48324f951ec870ef29553c78d6aad5f4e9daed378c0f3f3e4cc3e24b0a70b7b8ee5fd07e29bdbfaa8343f7250fd3ace7dc5d8ae623975102196631f4333a5f1c1b37d549a1f46dff6a01b2899ebf843874d3f8830490f373f7c2e9f4c69a6f947e300ceee4c140625e948e35fabb96eb5abba7ab5545bc0fb7e1853f2cd58692c6b6231fd5ba65a8ccecc9c29e4b230378de9f45d1973b1cd95353a7076a63335f88bfe6c5293c73216824de9634ae9dbb1fc2a0e738ee5cf1c87d13fd1f0955348484888471df5d8637ad33869f951d99ee4fa6e6b6a623192fcb7b97aee309bc2be4fc237379e40ecd7b70f9483d02e730e4984a86a6cc464902f71cfd69cf33d1ba0e06006db9d74096c6ccb9c7f50ab7b9e34e0af9ef5e51752a7250cbbb43f637ca67b2e35b9544c49de683ffb52099a3c57e875548c5fd8620903b96fc61987372f6ceeafe841dec4868c7dbf7cedfbe4a7def4de77a9d6be5f9ef6fde93461f8f55d3596305bfbd458c2809fc732448de5771ac39135babfbe7135babf4ade088ee1c6da0a2fecbc892bcce9b59457a83b8faec128e706840ba128474309db727f630ec4b96dbae7b1bcaf5d7c471df5c62811dd8f6fe3fc3e96f8b5affe6dfcb36a594b21dc3e7dd08d25a93d9e9af637bfa6b9fb5f4a5622f35ad7c074f759cb9bb7cf394b4b6bb8f7de7bdddd4671f78944be9aad40ecdf7bef6de19674be5a6b75bb2d92bb5b77776badbbbb23d1d9aad55a6bad5673fcfedad75a35ed8e628dd2794c49f2b4b02c88a909d941bf1544862f02dc8f91afc43f6e23c3fdb860109cb81fd79e234be6b0a2af7429926d97a24d9ffe70f88725237990ac07b0cc06d5eb61bd5afa332db6bf3700ffd973cc30d4395110c5ecc0572ba5d8035f7dcc029ac792528c29a5948ee5fdfadb8a29543a038ec10bbeafe82b3f2d830ff7d88befc55a38e99c934e15b6d75e7badb5b86e9dfba40193e7a8046c5ed8d839190100083317000018100887854241160449a8ba0f14000f5a76465c5232170824b1401c464110c53010c44010c640084341ca40e50c53a802007c613884add2807b38610ff173cba2eeaab0beb1edf2e2ccb9c512f343ff8cf7561241b144afd16e1765e115ddeb9b439cee38284ce03f8c5b712632bdb616f1fa1ffefb53101126afc9a753d7684ab8e9d45ad26bfc0dee3463ff1592a4948eec27f6e4baa2b826d5952dc24cd541ca9eef6508be27c835dfb3b47fd220704fa64d1ae63410cc30809b7f33a1448b3a65d9336f569813d7112d7b272ee81c6579efad80e40d75246410bfe4402de463827973307d79fa3345054f0f81bd8263ccdb9deb33ee731f911db2a54a71fd6a86d1b7908848a4b260cd3c8c26a04014eafb03215d59d69046824b6bc696327a69d8c2842449495ac443966e7bdcd9c88cc77bfabfd26b205718b869e9ed12c4d595004dc3f2ec02b45d5c895ff1d1a8515e4522ffee05f32cbc93710bee3cf653ac4978b8883a8bff1193c202848e088e05257a1b46483193c0dd0457d15c5d737c1e9181424db2f64b039e52017862ee852cb97f271ccf0b29e0d91f0d11f973d284f92629dd479b963364b9a3a42a35a79eb86043387c02c143ebc9562804a8c33652bb800d0d7ed939eeaf6622cbf5afb058631c91c712151ce1f69b195e232ea2d925fbfc57f2c6a7106cbebe1fb20a1aa69e2dd457612531a21ff5e2cb50b150c6e87ab2be26e27d6d9547c486b3e0601f47ce6b35f193d59c03b72f4fc32688f1aab3f7f42efe1a0de0f581f003089e20bf01295a4e52882f8ba857ceffa5e8e0c531125515c704c7252415796162b3cc89fd86c4a79913c3fc65349a391dc7a66148ca96f98d0e07faa7269b4782b807e0a6634fe7699f2a825b4c3477c8680755c323c6eebbf4900066a90d4584a334c0e4b260eb708def9f0a4f81957bce9d71b5fa8ec64960c03adc21daf466ae11f895328d8e4f1410a4e6de3d1a83796e502314aa42c2957493246ee0cb9d5b2a93df6e4e22ec8be3a2668c89b69ed83393785c85f4ec233d82c74b945b5479acef8ad5afc850e3e6a386f8b42bc1e5c92b3f5e919a08b615227e79ecdd9b8ee9313d489203ed9a7630490e6b9814b50410068d5390b826af5d07e317ad4ff6f1129b0201956e9817c829a7e259140ad4679d02462706345696dd8df065d5632e7dd843fe8c05848a86bac6c34368d1f5f1c593b0286aec1b90d563660a827cb00c72713d736249f473fc58d91823f83b643973ac9f97811598e2a7dd8c231e672ca80c6b4cb2a5945f1eda3c8d957e1f5621a062f6cc4c058b8b354d4a0c63281c6aac216e928fe808d315e5ba73bd737b6ec56edcae19cd813c8fff5c097d12136626432a78d9e6b9d5d256140e7b29685208bd5ced0aab760b25ff7dba3e67fbfb25041e305456817383f97ccf236456e04d8303943945727e231aade5943b87f0569cc506e3c790fef6abe4670d350b12741f8872c1696941b7d9c5b8136b107d7fa77243bda1061a3153abd6f00c927ca7d5aa7501733a1c04de310e80b55e274feec0dd0cd1af642de5a17398df5e16e50766d7cff94c70379a8a1e358c91326c180d6ffcc676279a2ae7d1dd347fd110fa8119c25be313b7f749cd56da7d42115215c287fb68f2a11664e4ed69ed13ff2268fc5ffed8d451cde55f59049d6c379040d17ecd2e164c87cfdd08d8533b6755a250babfc194defa0a65eaaa23c468d332a0d82fe57494b4764b771ce0021187ff25ff280d161567591ece86deea96d291bdce95aa3e8b482c974b5b2930f6d5a060ffa28f511a8eac5aaadcdc5cc78483b2e86ca88fc162870f477d120033e20349d74fa11eefc1f0644b177ad2bb1f8c0b682b20b7c3933717e22bf7a165585af93c5da5180c983f4667e1ba1663447440ea09fc4480ba715358a4c8580b2a3c8305ba43392a2075ee0482f9108615c33bcf070249bcd606a04811a594f710affd78439070c72c023e5ca6d417a7b85dc64fd299344d355bebd542a240cbc848d4f5eb53adf72a0207041289bc72762c34bac91004a063ea544217e542b5afa80f625d558784a8caae1215eb526a89da7cc8301e55ff03ab4d9f5357c8f7953ce17bad5e7f387300fbbd7af3d891172e992a68165334c855988b9c43055177ccc153c4ca17aeb949b9498a033d57951f26d222412f533c59624bf70877720cd66f6b3a2e1cf162ce9333a3eebbb488c6fdc5089a2ce411c706c557153ce2b1d9f4f10742690b9a83983f84eea2a0631d88fec071831e80554ae7689657dd29c07e31046d164fd5af8446f5d72aa67e87b3cc21f999a54d10372bc649d84a67c0f316de509b990142d1c308df1ef2ab93852649c1ad309541346482a49c07fe34c4a8c089661911f950476fe4681aed4d7447ef5865f11aa8e859e42a951e652ed375ba36d12572ead9402205b8e10676e1e4485c93874197e0d1b408b87fbb854037c3c6fd5e45480b41bca44b5a080a9f2a087f7283873ea6f1615a68eb1df4ba33bb1a7427f306535fc3448f487cb33496e368028b65c8accb2012a6bafd85ca92a43f59a23f921aca2905c79dabbecbb3a410e1dc6a4567d2e353d3448f76ea1e67b9794ad8f3ef0de559747536b54452f2dbfe70403151e6a58531958160d138ace928117fe3644d9fe58a6eef8b0cfd57f4c20861bd62d1985349070ef2308d382e423e00b6811e8771262149a0ea87084acd42095c7539f26fa07b6efacda734cdce650563c3a6e352fc32d748b35a182d7347ebb241d4df5a469847839494f3efcd0faa1dd2d8853ab07f907d3d8ea30f260ffae2d42c1af8d7e89bbbfa294be274d40e801a24eb658e309adc6cde649c710a07d778796be1829e78def075ec665bcea621f8ba8918830dcb565dba8929c3fe91610a26f1a0a33b3f4d6cc6ea549d699a257751d0ba56f37abc38f59bcc56c85447ce104032350ae0d75cbffc2a2adfecc3073a9918134d489888ae2d991737a0a7adc2b9e767070169f4b4a0c96914955297298f70ded567d3c56c2c76d2bd60c147a0c7897dcc5b590cdf6241ff8446a8f625c56f6d75e34dd756506155d8be889c85de0639a5b07c2bbaa7ed132317ea8947731b1274e29ba516a2530b971d061165bcd93cb12c55f78263ec5b1f44e704b232d8ecf688cf2bd603e4eedaf1478594fca9b21899c8c321fdfb291c96ffe184f3d9f4b0bd7ee3e9309a2545bcd490636102c6a0412316e7c8638c7ef3b8d4926996ca312641e9fb14fcd84a3e17ec44d9a303b3bc5473e40b129307ff1e4015f26d662974654c85b6e062a209d3969488e3a4f7bd935847e7242d0d04bd662d65bfc7fcaac08ed1d94a158762cdd48a7a668b06f36394853da44eb83cb2983860808742582dec2d4f55645966916b92c231525b443ce254f608ec1ad71bdbc6abd571bd4ab4cc4f41c1c3b7f2b2577579ed67a3972c383afa4c1df281d2b9cfdafb965785d439cfd16bed7da1e82fbadfed116ae9f75d7ba72b532349c0898587e51773fe3d1c2862edfd6ed5a01453b0a7a8da393c4ce27fb5b757680ec8a47faabdd6e85abcf1bc53057ee10eaf0ee7b134818c1607f193d87e934ebf6306c5dcc6a0dea91beb2441f765b574dbb4d4db2d7fe489699cdd9ff864acb9503dad6ff3dabbb4f861535bff87045f8fde5fffe5e344ba9a384aa3d3b2c18591c46a9c2479266d5c1f01d7a40a0f118550e294b240ffe789ae9b5eafdb3bcaee80c9eb429aa21d53ae1a230f1f846abf55267e5aa92a05de4297db929719bdcfa46c989f66e107f13908c34a61c5f63d95b18aa41771aace37b22b5231d137c3fae76e7a4fa154dbe9379075cb993f646568362bc418b63045d047e2c5a76b1edeaa6395fe87fcc7f3c51f8864444a263483862274473345e05e0ffedfe3d76d85d89dd7d999be108da30c223b84eecccdd11a5183f2a82513a2b15ec00ac819d0db73e1bedf2a88e1fab20cc74e7dfe6e67107a235a10896c5c49b71750d9bd70b8d8d35b885cbc8a04c042c987b039b1b85a02c1dd924e2c8af9f4fab756510a1078f936cf16ada4f585f597804efa67a4f9f1c17c85fd1531a1f3216f7890407d6140c8d40ba7f882aae427979f4ea89d33938a0a874339526943f31483656685f0ef034806843cc054209171426748449c06e5960a7e00c063885367c2f7382067e20e933ed3b531b8393f8095f84e7fa5570447f0a071a2e93c81cde10d2a697086489c3474b9e5d00ca3fa839d3385c595bc66b54b018bbea8b3c39b9d0e47db1e1e5f5d94986bfa3a31b9a595908e69652957b86748ee03643add9e1b52c3dfe71db5a0d3f99e8e16849ace8eb018453c59a00e4d6020b12aa5e712403da615459f62baacd83c17b9148e1a19239b64694f5ec4cd9d18d0168c8a240b95be51d4bff68c32fd0019a62b5c82620e1bb386493ac9787d98191067b99391f65635081097b33ba7f122a92f6058fd2530dd67ba5effd8173889124672895ab1f8a1a66873892e0ab0e04e8a5e86860c42818c5fc9d7933e5de89f02356d356beeb75523a0bb1ee1ad9d4f70daee08dd4b2771903ff409bbc5954c7c672a22a55c520004dd3df2560a2ad59042fe563a1ff163229e8add3a81a939e0606465e0bf6cc3493609526581213810de48df73343f1f80daa037f6e94f9d6522372c34fd06a30be5efc0669072c28ab6c8d79d2cbfd0a325d3fc97931cfe2107bc4098515be68b3b6e8ea7746c8d796f4a7df63c09656ef2b17f339a21fa15b5d2551c9a7b3a68eed74d774f37fa7d4770de8e1f4d54ca614b3a9b7ebf7f5bfe074ba7f91dc8bf7076322c64c0f26d5eb4f37322fba75cc034a17fab1f26c14cb094667df32f00c67abbaf59f39080d7c8339819a1556902df448c0788ca0847c236658c77f9c4060b316cdd85958e1a0e988b11b129e5c074b913c4ad2a762e0b692bbdb35785966f89bed2bb5b75d40f9f6e9136a57aef5247e40deac29e43e0bcb042e92d054c752f8ec5bbf4133be08e400c5f443f9ffa0f58175f61071bf1b8b11f56c5b4f88746e680b9d2e88c611f6b7873f247e8e68d9180f1879fdb559b95d584583f054d07abf3c22c2d71da0a2a18f5dc044f52e326d4347d25810a19fab600877edf2f0d52a3490607dba25e143d325f762a1f1cc692afd47bb2615db940f999b913eeea4546a2bf6023ae0cd2d427ab7a7365f705eab99fd3255845338df5547ee69e275abf7a1ec8a3559ada64587042ffdc40961e44cad35e78df8b44402707dcc8aaeabac6680b8bee1c537fddd784345ec8e9323c252c3cfd8b7939797ab637e22a9867db495ab702c90659610f0252f181402edc85b164bb6a2365fb00eb3b17e99a704f3795f0a96e0bf9294bc37e73acb04c4303ea39b48a5d4480312d5f46a3defedf546bbb855113521df7eac27b9cfd88762de1d3fb17e9905346c45e9c9843ca0dcde4c4c65437a1a54898a142e169497ba1f88f88be11daef61764908540d1aaaf14f3058d4ad50085a67aa7f99eb9f61c00c14f55ee0ae0da5d7c9841003f9c93dfb7f65029faa7b39fa9776d8cf2edfc193d86b5fa62850163470898e3df29efb5c6a32ca6bde2367d6dc0999f4ec91913bc533641d0051b659f16e868b8dec11c3e06c827091512a7ac907f9579e1043b9efe70d059637e9d53767221f64d7f97b54175bb47a0c2345ee36fcd7830666e6e07158a7bbc00216343db8d713da29d616687a63fa8e2b67e7c6dab15e96064eb59b115526c8919d723720d66125837a6cf273e61e5a66a47180a48f2d025f736415c20d4581ca834281f0ccf822f619c9ceb8fd6cdce8a93b8c0d15937a27a11c6d290f3a4883004d7752a3a59ebb9ab360496c047618365c5f2178a3c85140c0b3271c10ef5711fa456e3329d146243981b38e62224e46c2375b8f96674ba6195f7653ec3d35e720ecc13243fc956f89a628aa7c808e0ea1c0b02cc34fca863e013c32b8dd8cf5182008aef9fb24941c50f8a929cbaef69e17771140607d74a64f84c6746e16ba6567cab56402ae0165f609c3276557b25ad499ef20d05eec2212838698a59285a7c2eb8eec7b5474ae606c3deae0a5e32472e599cf53a64f95c8751c9856c2f6573edf7e1d04d7e050bdccd0f0d012cad98565a1b27a03aca3f2c86d7c1810e89671ce6e2c1bf5f63b585e25cf01ee660c3b6f280d4e123f6c863d7f280d6e12372c82b9796a462756909f734d0c7675346ce6c739a6647b695399c8c73926d00a94742420e05ef0b3669892948f12f190181c9dd3810323c64b1c345ef183b79cb4548eeea641f50ff3013ab8cdbf7eb1f8c9669c795339b859fc62fdcdf583e640a8f865031c6022020b2e3afda2f69d27d17d9610d69795b7aa1df81bf5dd484ddd835d13ef60d05f68035dd76da254f276ba0d2df7c742b55e3e5590b29d681b585baa3c6ee35d8e4541421720f47818024e9f7d23a857204bda300b3b3df6e819a85cc83567e1b84e028199ce0da63e0098e2514330781ec15d0a7a901a6a3bf0e72dd0fa16607d0b05f07453aeb6859bdf51b56eebeba0b88e8b7a999db607963127172e2bd5ca1db00ee591dbf8dc9041b18439bb7059a99acf60794a9f03dc37671cf943293849dc6231aebca11c9c44dc62132ee78f86271dcaa025c13e1c636230aba3b1197fce3521b0494f27e25e8e310356a0f4313061f7823b356248aa7fb8b4c23330b73d46a22581c01231f0d38210b4f2e8a393d8f126080459df8ad34840d603233f28778988e54a28ed28660e9f6196b9728c9bf7dfb9a0ac7cccb24b0278e66cae40dc1bd57dfecee298e19052cf1b3a0145191fe4f7f2fee4d8e05d76a9aec225135b7ad20f6df866ab9ffa873768afba0ac851512604b91aefc1571951661018967041434b846bba9dd0ecf5541f0f1df7524833ef8fdc5ec2f2497df75a2f6ef3fac2bb8eb6fa08f9114252aff5fce602b3acb60ad4fae9da4bb522922303a7e85ee7c5cf2f4583bbbe40a2aa5db8ed3a5e243d1369cbe47e90a27f22784593516ca4fce7c8722a1337a1e0c5b1f059b41091ca6ef7fa4aeefe23551d36f825e900e6017b39b2ee2447aace2bdf21f43f1bc775a9d2d71d1411e4a4e126a8e17d86195fecce3113d192a4b32b3262052fb68c79eaed77617ae5afd2e4499609154cbf13eb97fb37388ec7999d97aabce60352091bf877d5059f1207f4501df2b49cc44ebf11b63505189a5c9c75ccdcbfc52f5ef1458ec5d05a0e4ca9f4d41754f1999c69e597a6b13a39cfd33a39c59337362e16a5218554aaada5bcbafc3c667d6123360aa0d2fc028bc1cee5f29ebe792a787afd6b8db2d44de721c6396847e85faa8d887897a58d16aae96e40873bb9836c921654478aae956f38b680288a0d14a4c331646c4dfc7e029986a243a7e73014169fe4b0504595aa8a1bce85a9ff9dac76e52ed1a1df7112a2dc48dc8957d12a28013d184f225868b3eaf92f32ea6b38cd2583c0abdc7ee4c2dd10580226ed32112d6fcb86bd10c32001088bbdb138e0ef03f98a18282e6f1130e8c19b672c1eb0c16f3094ca04a6a0cec14b6062d91884787d61eec43b088d2436e2c2a74da5842f5c9752c3d2ab90b553a876f776766552ddc8e67c121a07055f70871a0764aa160ae80d78c0ce5a2ad079a0754bf98c33c5045a924d1a410f843ae7e086dadaa1049c9c2be03962f701de91a6e0cb1062c9419b8fdae641ae7f52261cecf652fe32e327cc4198ede52750c9c47289c7879526e91e9fe0d8256bd708492df9ddb6de8a2109adb1cf83847c950121b49d84d10df32b87af8b5468ec0b81fbb9aa1202a6573a5a6870ecec520fa740602dedb6d157c02fe929104a5f5667225c4f4862424d1961582d8d2689850b6815d7c2909b1c40d633ee5ce4420db336e55db8376c46fd3862ba581e01a5201d91be33ddfde6ed9d3750f3be4bf90b99c0ea44ffc41fafcd454b925c6ebbeeb3d1c3b7866717486e6db56104f4a68bfadb5db07b4f4e82ad580e9957abcb5769f744f12b11cc5e12eb09dafa2e07f4b9f30f7db0fc7735076b2a704058d95ddb9aeb61f10482d101e5823188c78019b071c6679085099f5c2602b2f491c52dc31c71f4839b2d10b9efca791cbb8fbbab629b3382fd29b908c72dc407bf17b72a9570cde5cd2e76b644caa40181f800b8a3a00c9fa5d420ecea36325d6728cf9ac72d0479c22721c1e99e05a2b969ae59fdc1fa663c8a8191cdd08fb92e1cc4dadb0651ed0e19f375f7071e660379d313500b871ff50e934b8a94df1e8b2bd4384864b38141b741b2228c89aac85b484a074abd91d25f2bc4dc4766824f42d4252dd48e28be011d42916fa255f41b276049d734e002aa8deeb35e176c1c69568f5f0146e37a08824476ae1abce4c720bd5f19caa4733b9755a2b83a9077a8066b1e89bdac9e0ee20d7452710dbecb073d8270459d2a2eb4a7a693e045f57f50be79278fa2d1f073325264ba2989c233df229471ba57b13501488b55e3504b5b9443c41022fc3283582ce5d03278b9fab6f9e148e8400e16d88be4fb370eb6b88a51237233194ef63c91fe15c1710300f3247e48855711761104d7cd9dc1736c20faf150cf0a8e49030c1de082cae9b9120c75cf5476440ced2df3c22b965505f33708710d0870c348568b7bd8008f06a534b88c2b46dc28997d640d1111680b5a3362de099ba548496a66ed5bb780b1acea2bf00d2061e6a47f89ba0ecd588f20e64b8146a98ec799ddff6dc647a6afa8f1d066dc006d2b4865d80270404a39322bc8236943272d5e451dde4258edc942a2cb01f5fa237d8b55f77c894190fa96aaba5ec1de8f5a9451461eb1a8fd484afba1821718501914aba2e5f2d955f2233c489477da2601eba8d4899c85902278f8081aca926836cae881b023119fce2008dd9d5af5a4dc10029ca0d38df700a329e0259389be7d4da18780f779ad24ab3ac80c7aa54f30aa1a9330616daf41d0383730b3402cf4b2e8faeb9bd35f4d7aa0b1b29bed5aa64bbdbad93dbd1fb99ef353d3f6f7063be03193d6300fb1e7e425b25717c55386a507dcb3477b7cd858f022ecbcdc4330c528c84fb8ef512b9d8b58f430f572c50c150805d775f7df091a7b2860bbb5737ac4f20b5c0495c06a4a2d03849a51640f6b68f2b03d07361fc21233add22ba2e5c0ff9133b890367455b84d53d0650274efca89df068442a33010121140f90a524119e596b17e1a56a2e8f2d5454f2a49ecaa469936badace88de4b77a6bc98f0711ce411e4d144627a2b34e476fa4584b9b10c632f664df1cf45707617e5e16173c011266f2b0361d486914dccd58dae157ed311fed4582a4f7fe8c94442c8d0baa8e3ce3e49eb0619d2fd4b6bc4b0d6d59130be6d8e42932d59db8cb9c2d2ec8e6d7373b7f1d7e482440dcd0264649a42af2cf2b89d42a520b54d887ba6519bb04310c4b845bd27f040c69b50000df0d409ed821904a877800d117bec6a388f9f5bb86f60fc5a21360f0d5cd72c7661b108100b23b43d986786b0da81475a79252c977e343b0827b02a62823a6487432cb58e0ec4a1fae1c8726df41f9f80c2015cd0060b2a03fa3be04e1fa4e3634c96a1fca267645eb8d541831dde5199173ca1769928119d9ae703d8188cb1d23d1257e5b0dddc286c35fe85a10b836d39eba544a63b8c6bbc9c4a04a749ec9c6a564cc06bdc1e04d269a4b21251208b6a02d3fc1458319a1ac8933dd05e8980cea4910f4cdaf853a709b68ff95a22508df64f908ac0698652953aa5f0174d9fabe8ec79fcf81e8405f43ee27d78da1639720f33b09373baa84d42334494d6100830d924cc2b64144258699e2fc219ba79f42e0e427c9d5cf08d612cd16953f0538534fb8a39936f1dd2f0004592d9be638c485c08a26afa54f81af670114a517ef1743da55ebaed0a20e463451d38c660ef97d67d15f4bf296718fea62d966f6d07ca4b0cbcabfde44b496d4a9c5dc15ca8e13db4674a30707b0e462604d38a54ec20d8df670f21740f2b4bd0c070b9143d220800b82b6036be403e1c56cbf9e6491492fb0c8bd43f56412f803cc6c284ab0b075a3acf083143a41e40538702a0a30aa578e9360e496168a803b9fb08a48cd198666204aa9c0ad5d8e7f33958dca227ea6673f4c2703f52951aa4e350fd16b78f04d017e3b3277eed4856f5e201badc3b1d29c64fd86302e551c1da105486a13f57ee8a044840983a37e52b81a4faa9e17f9e40dcc3b992bf1f370960d984a0345f295f65521dd052755129390223f9b912950b93806724d47c109c3767fe5ddd9079cdbd52f20a664062590e46e0cd68b365ae3f04e204826dfae3833623517ec91b27182d26f35ab6532fa7962bc18ab10de18aadddf77d1ac64bdc3301ecd8ab0160ba5b3608f1e287c002a1928d1eaae1a74cebf91986f9788981bc8bcb72cb145888120f863494c7b460af2863f0eea072af6dc8ff2d3c5005b2c09772793113270b7b2d8abcc1bee3c743a6b8bc71b8ba512abb402a02c142ac0ef53455531c7d2046c6b67e581dea0d558c9554e0446c276178a1d6f07ed32419fb3cfbb39a8d10538bd69611808572c98d7f98580323b416b326539efc1a650341a1db25715c8ac7dd6b1a4b5d03b380d7f76f4a880d0f4753de9de4881aa0c2d0947239e0143847872d793c815ef9176c3508950e9b241649f7aabe641000e09a568158792ac27b5ff6aabcf597c0387ce79510a300af08e72ab985d3b240feee14fa182f57de5a97325b0cbde035da716784e6710b41e59bf039cffe161b8b507f2af1195873bb057954c0ea213f6e41e4cffff1736643dc934d78e05a22587d419b2fb08357290bac83be9f4cd313f9611f4988ab50ff45ef3103ebfd43b02bba0ee4282fef49ed54a78b8f0a11d238ca579dd00c5e8976d85760301fde070e82e46ce3480ee4edce85a8f1f057d20d432365bae69f8399eea1facb541daf99138be8af7f8d26f602bc617f73ec8e736540357b820bad69effe052c2df3110cec6b92f3418f4d2197e748b605022c5a5dc5739b0a0af05436f100ed1310a8d56a41c401a39e6b29a99e3def8b7e6b47bab423719e14b44bed6870c17a1553ca52b17dd32704a8cd1c9474abb6fd1b9c9e17d933f825d75628bba8086a00988819365d064176732d8a29fd7334d28f182cf030ba0347187bcc6ae52c688c7da074ff5f51b81bf78dbde34fdc02ad3964ef5084bd52d69e3f70cf25da7b741fcea2506284f84963691bcc46888aa6350e213219f00bba9878f159eddf05cb63e1d333bc340728c409b9116247f4e02d6b5fdc917086cc58f656b942eff4267f6e9b02046db02d7911e2c538d26dc7ce0cb33fabbebeb7044db3e7d0a8678a7d62004d8babe04d92685890bcb84ef8a9f49fe8f884daf3d3974104e54e5f478a67f29f1ff0b6dcdf66c4ce54224790da0382fb84e01e51ed068f647407aec2304125c348650e6810eac5c6cf606fa8843e4669875510b6d8bfe4e1563fa20475ce7a2daf54a1cb50b4d2421220220dbd6ef3556efc120a1a880d884d990e02e1e808f30873e16c1a2fb327af0e818d8d18938f7ec7cde01c9f171ee07cbdb2f83ac197c7f78e91397a2b1159f759df5bf8d7a79a6d3e513fe39f7c380d4abea036b4f277b8f9d6761f582150d5899d25df210326a573aeb7a33180b37276882240eb302c3ebe2af03277a3527a2f4e86f9dc1c925fe3bd4909566353938d32961dc738725dab6c1e8c2bee084e870eab0ae6bb9a351d9d27861f732f9caf8fba59da33e789bf290c4201b21e877fb46749b368e7273bc364aa2573cb47249fa9f41182846f2006644a5fd6f4fec95fbf8644c6c7eb161f59ab8e8fdbc4dbf596dc425e82f16acf3bfa8afca2477ee5780425abaaa04c7a594ecf7a9561c1a091faccc0dccb1e26cbf553a47312c194064018619df033e9745812843300a43569a59b0199be32d845a4f65c28d3a0ed3a8a62433a3b248ab9264070b7131b41a1e044d103dec353ecaaf17472e799beaca62ff812d99f43d80fcafa909ccba2e47824ea55217f4a777a0b131d2f7ecceb024118b5d00a71abc225de6faddf10af730fae80823ba6f7b5a5dd3436b8da645210d95cd8c1c5ced4ab321dca0e56775ba9a1e6eeecc2c9a05ac8292aab50d7ee6f4aaa54a441d233e04a3a386fdc907b6596427636b3a4481ef779f9addff72d08caa7e9202dc11d46cc81a8e8270617885cd7aabf027d21fe1b7e96a8ddc41b04f6e3d701d9c5399937ece21fc21f3655e0f4e9132aa3e2a33bfc8632a010dc097b947a8c8f7f0880b18c8c12ed87651246d11116e09a5e529750a93a546cb152d76bc593213ee80da0a484220cc229f1a99bdf0fa82556d4b7c95c1b63bd4df2964332e1432c5640a05afb6505178b84cb5ccf4a7aa0b8cdf34b6fc73cf0024581c6f1771250a5252444853ee86a027bae57c20dad1474013d2733945cd7dbb02be22b4f57b6d92cb8c2646765451650d4ec9990699e29a88070699a88e9920f80a273803a5cd220d98bb07122b5d8ad07a9211bdd9f8e0d08f84faedd2439b938ba793dcf4f7c33a37f9f28fc85fa13e56ea467b26ab6d08736937a365ac16da7f3d35964e0adf1146b172208de6170f07838be79f6f95d1efe2d12a888ef504cdea3e4b377e1ed2892487929547aac32d1b063338728d300637afd8e3e6143c90a0c6a864a38a7a1861815079fab1288f29869ababf8b43fdcb6062a60ec89a602007fd48b4b454e8ba701e8613a7178de66634f11ba4e3b3b8555ce272d926b6fc80777104f425cc0ceba725b1056a92b9bfde33192ef341098810f195829a1e762d47044e7c6112cb843bee698d3f186968ad80292874ac756038e4c28917e1036532d614337f3083ba4a7004deb98f241e7897a415857d42c837387ca2be22a4faf1b05381f5934c7d3b5edc4a7a28268b91c84b8a28df977e0534443796686c458ff713094323cb7467084567d2f5d94133d77e6f10242f38819ac3c13adbd197c1cedcedf40381e72db32093814691c12488e56aeab4abc03d407504df67d2c7ba8daaa376a9a7aad28ee525199579e18fc36853ed4c6956839598800bbdd29ebaf5d726a62ed20ca2265912d956a9a96b1e163bd4686c7bdae16619861fc57e7861aac2ed78a3353aa6d11c9f010ab848657037261c984b8301a10c52db4c8ad9106e00820f6c51112528ac19bb15c5c32216ecc8689f2165aa4d658037014a8d820b3d3b864ee9d23f698506f6d058f543f7c37cda4c1fbd6111960ac572b04bde41081beb9fbef8d75008f8ab477ae062be6d50951e5bafcf1ac10bf9cef43d8a8ca648093eb6dfdc06a710a887339f537d23c377bc16622f4ccc5bf066f3ab7446438f9c7f2bdf600a1fb4038fdc4a093c21c911149cd8800d627ed4e82523abf2d8d2d1f9ecacb0f0783c5eeeb276b67b885ca13dc0c5073d943b0f3024c520a7c8970e0f333f13dedc905a5351e7ff3f29bdf082ae1f75ddcb3a66b345287271896f3274309bf5aaec1277797b3d4656255cb04da0d6105003b66c98f9ab5dde2592f96f083a761a734c124f3c4541936de3e2d48702d436e8d5c11d3ee096c48932687c157123c885630e4680a50c723d20d7377092c55350bcb8ad35e1ca4806652d01f3cf613d8c3dfcd14998be3ea9bc6c6dc58724054289f6021e0c7686e0a8621847fb0fb7b0fde6d244a73e75b24dd5a88145d5ede993dc8c84c6006072b40619dd2306cb985207c9d28ba448d3743e67b7248c5d012469cb3a458a3ebd5cee0af66101e236b0fd6f0cbf167f390297ff0def7644e8ec0385f4978f80dcd58fd0caecff58636348cc1ad315556b349a08207d61b506b02a204ebfda62e621401ecf2abac3bc388e74d7c00b0c05b0a7672c34eaf0504763a75d20982adab81ca09fa044d5778512515a968f96405185a450f91fe25cd777e5de3758503ac50a2349568e9190fe5a95960e305bf8c9d5113f204d1a1fce77ceebda52a148116b4b2f3f0d2c5d265b926d3be283ae6b2530eac4e0fe29fd246b7ad81ba6baba0f731186dd346b3ff20fa364008b06c37ff51c99d7da61912707dcdfccdc96c4f3d73a86a429102c0ff129632b0e098b1ae5953334ee5b0115c797f5ba7eae38dfc7e136cebe29adb0603665f5f52abdacf9de2f4bde5d1c3df41819061931e2d5dba4fcba5b50ec1055e2eca5ff6ccbe985b1208a952c4813a6d35288016fbeb9251060a160da18b85cfe8a12213955f4658dd6a452297c11daf6110db57048da85e7f32ce7f821a0aa09d5b8fe84cfb40d40f7baaeb1f8ab02afb9e71b4597851498ceb7faaa61f94112151370d8e5550f84649004e60b80de46f00291c92bb97ed9a005a63d1ce1e410b2e50e15636be6c2970a0aaf2054646b1b9bbc2218a0045aa44f4ba42b10932e070bc46bdd72c4f3b7e7895c97c15470bf9cffb3a02f01f9226eb5f72865158070249479854a050dcf48b9fdcf8ae9dc28822f009f7315024d9a03966ee192d2c8d032f3e3bc141899570af0310b493fc628d92ad90371a590c27954ec47a19c84a27b2f30b81291ddc19fd397f8c145c5f6e59ae283605719105b490d4c8d99ccbbfeed4747bda4977824d6740b876e523fbf78f0ea8efafdaa3a3ce35f225580ab9cb09aebc76646c4f1370a87401faf4622d1a316c2397a682a02c37f5afc817831f6fb0a10869ab08d70e348a97c7abed699ca20d290633a53b3f184138f79a33ccbe2b084526168639c8d38047b97fcfb7d09c9f8f86ab702fe86a7916593508ddd6950f2b32f0f04b142dd4bc2421f27231ad103001a831721656fa609f96cbbf2b1b1c0baf62c34092c73d223e8719b73367e28df23b914d7ee443065723a9d40669aea9c8ca1ce167375c3632d999440a771c9aafd8571eedb48c337dd8b60c8c7b3a1b4d5104f346ccb6981bdeeea7bbb6b2976413919560358129e3a29f369487543c3279e5b5de44a859b8af3ffccdd62fbe13ce908a2d760a7049b5f5473d4940062a401a6bfe1a6a8e4112149a7deff7d94e067c0e37a99dcf39fb96dd18ac14e4dfac2afbb25b90c695fc19e53fd6dd5fa9830810bd91122b337b8e92fc0ce5eca00d54d64f432b6bc65ff80d19c13f28c54e87bea14fd56b20bc7b77147a1620879f20d1d07a262194e134fa08a53c8a02219d868e2ab8495b3efcc52ba18e8db138b987c78362fb17d07de995f9eb223c7f9c09116bdb376b87ede49848858b69fbbd61d09eb189f23baa391659f36cd58c8e190b6f96fb6ac559b7594594980886c85beab2f1a824368055560ff43fe357cdfd778358ec7bd11a96263f04c372757a6e91eeea93a6671e5a3ad2fad38312ed68e0839430522fae9b430aa49aa42db0a3abbcaa31f91d7cd751c50e1a7a9157d261bbde52d8900b1b5dad7e50890b33b22b122fca018ad3cd5b8ad95e03352e8b9f7e8b5859f95058534cc2e41de815477ad32ad06bacb04dcb339075b35a9e0d94c8c2717b12e2dddc85ff3aa527b41bf99692187201115b2306a42d94e348ddbbdd7c8d551d2958cdae5303de3e095fa615644bf2cdcf68bfc970955edeea75f8a408cca2039aaf1e8852106878940e6848968671ac85bb11849b17e89e4fb3b910a6c3007cea63eca3e4630e7013e1c79989369bad0d8224ed80cb20ad905004c700b4d4c70d5f16be2c961694d0e1206964257bef4de49652ca9452ee0822086f0836d5cf57d3b82fc636d5cfd71679f7bd6956ad6e8cd32f51ceb573fdadf55584119d04fbf7105c1062654e44c4ca9e182d5606351c4da0fc7cf9a80469dcee2324c8dceeae9d451925561635af8f082e40ac4c9e01852fac47860c235636ab90c114ceb032d30a945819a6797d430c21c4ca32cd2b961b33384290d65058828c11101ac4a0c5ca4e9ad72e23093e28c3ca6a8441626554f3ca3a98c2119258998d1b9ad767840cceb0321c9a57d782128a58198de6150e29a1c5ca72685ea10b90708695e9d0bcca15e4000644ac6c078f21850f8f21854f5791048533a89804c5119668b1b05c2df05912e2716405134c21c4c2d2592043894c8dd75411789971523bffc438434b8be5160b7f7106168268bcf6146600218895d5685e1f154818b13200685edf08b420c4ca5ed0bc624ee082102b83c1bf10210421af58fa082964a939904082112bb39a572c0f055b00f28a85bf50420a8a58ec09e8b400d8d8fa3636363630d8bc6063636313e1f3b6e961c92dd9eb7ea2c8b9aee185b9b5a64f3a5ad3eeefbd07618c170d2fdcfe6c729c8fdb92f9ba989939cade6c9cc3b086f099999999db8868a81adfb6dee2893ad759062156350d6ada73ad35d671d23422db840f3a84b02164ecd459b9cc96c7b332a0dc7ece50b5fb99a570934afb5cfeac35f7f15800d79f0e548aed27208c53e30cedcfe0468390db9f4516a3fdf4b622ef3eabc338fd409c6b0a4fec5cdb549178b99f89f82c72337333379452c668e110e7323e85acbb1d42d80ddbad88ff3a8ec97ddf1d9df52bbf1be9ff3ac2097ba0fb15af1f7b4769583b2117daf8ee0037bebbc1f15da5f98ec7edde5abb942608bd613dd68cd6beff56f4e953dbc19fdfa1226b3536e421450894d0c64f18271e845e98b63769b7bfc3be6db73d5366da889ce6a9dd9451a9a1619f61a6dab786965dec6959dd7e52d14e9fbde9b14a8587def9d86733db6ae8b9db33ce0f53ed3bebedee3dbc93a65b8625c434410acda9e514ddaf6273be8f8f610f6b4737225843bede55fd1f0f6c1ed8636a4f02f46cc895463a3ab75fa3ba89550a637cf83cb2613d425bad167c2c05181de6c8df5b068e6beeb3413d0a2f9b1abf69d3bfcf3293c984d2fe3df84cd779886182178e87182654717df2e69a3f263c9c9e63fa391f42211c1168fb9a2d68fcb993c3c1390560ce7f433dce8f2800e764b9fd44a78db3f93ddf5b11f8d7fb59e3439750c2d642073980efb13c73b8c4471c40d721d20edf1f8378d5c3f747215ef1f0fd9188573f7c7f2ce2950fffe4fba3d15b61e07b28fedc168a43fc747751d44540b8fdd00b5ec5a2260216bc8a493bbe1f2271101ddf0fbf70901cdfb1359f28361efdcd4152dfefbd00087cd3e0f87eeead947cbfeaad74f825dfb693f25eca0c47bdf14aeace3fa92d83a7a935be473dbd8e8ac3a3aa8de751e92fa9cde573d424bf43ed27be87da34781e6a13fd0fb587de87da33f858dfd5943c519969d0443d54996750996580434dc2a5eabc9b1d6474a8aa53a534474b88b9f0210f9621bbb41ef5868f1506279cf82c877a436d27361c15c92ba9cdc4eb501b064f536bf81d3502bfa4f60b3e47b5e177a81d83efa176138f19a08584e2520c9a88800d6398944e464215f56ee4d780840918bc40875552b52a2b0ec5b4587ddc2cd3622d002601ad0e49c032042af38fceafec2015669c29585a97e6334d8b35869b6d5b5cf0b1deb4608b0b5af043ed25de87da41af440e94bb4b04295183052bf85809a085052bd0f2436d15bc0fb553f0b10e2007149cb95d44ca4105294041ac026082aa433007d40d4dab43126092839d434c2caa0ee5606d6c6e5c4a2273630dc0cd68b4582dcda16d157b54e5f1b1d66be31c8f8fdfe3b31b1b911e3c783c6f4478d444ed331b1b118dc75683ed513b663ab61c72f86ba3c70e0d472df24a6a9be075a89de5692a0dafa31ef91dd5c82fa95d82d7ea5003fd56873a8947d5a13ec16340df2389e7d12738111599944c639c888a8a8a8e18391161455898c660a3531727a213d18908eb02a8ce24fa043414691374962e410efbc2cd505aac0088b526d6d4cd525aac3dae568798603d688d162b0f003cd48d75c7cd60d062d58146ac39d08895e66656fba13612ef43ed233e561cf7498d5fded093c7400f1f9ec70f8f6d75c88747d5a11f7e870e9f1d7e9efce0037d42481cf153990fd0452564e8e20a12d04514e020593b58e6033ca94dc463a0b6cfc77ae36a7508038faa434f1ec3c0f378f23c541f1edea788c9224177811505912fe25951c422aca849507db05dc4ff50db081eea6b8d87ea2c1e6ab3883471252473bb88644417d14418f1a0f9509b04cf4385acf843e412854c4a263430255a2dac08034f687028d161898d86a33ae095d49ed7a162799a3ae477d407fc0eb547f03dd41ee263b571e30c5803aa0ecb7a2b7589bdf20ca839ec7d25b55b330c71400f962b3d821e628607b06eb78638a007cb9506fc926ae57354060840c35157afa456791dea079ea6eafc8eba80dfa18ae07ba821784c08bd31a7eab041de03758915e21350735805bc920a02118440c802822800043aab2a1f1042042110b280200a0081ceaaca0784c0727e49f5c0e7a809780c8b03d070d41fafa44e791d6a104f5355bfa3caf03b549eefa176e063ad014407ec3b5a7558043c107589e5c0e3d41cf600afa452e1e90010191070002aaa1f5382e0004f0780c88080035051fd981204070cf04b2a109fa3e21040c35109f04a6af73a540d3c4de57e472dc0ef5037f03d54291febe96a75488747d521259f55ac08f3b2f9906850ec3bac082edd2e22f150a3d0bb1940d56163f80cd42516ca0ba0e6b037afa446e108d069004a0f9a0d48f161e3ca2f400c37513802741a8032805f5233f039aa007ad0d0ca69382a0cafa456e075a814789afabfa306e077a817f858b7bbd521258faa434ebe47e551b54a253ecfb4c15aefc631a177e3d8100c4a58ab158b5a4550e97611a907f93cd41f2a3ff93e3cf972e563407ef642d561ed37a94bec041e003587adafa45a002b6abd555c127aabf8830df12a26c137fe30c0804251baa385b5ba13d1f5cf2e6053e507c056ac6829891fec0758111a4b4b1670f2305480021380a7f8435c477de197d4269fa30200ab4aaa76a3c5aabd1e3cea56877a900146904066bb6e3bdad6af51125f7c41e4cf36ddd5bacdccb6d32ec720e77abe9bce7ab3d11f9be055447a371db1e038c56d241adc7ed9c539f9457e9160fa19f5cb30b248f64b23296584458d3427a548b7d9caa1eba5d05b5d567627816e9391593ce76f7bf972bb78e6762473fb8a41718be7c026dceee6f2773717f5eceff5c64f59cd937204ba5a46c4fb5e1b83baef75563a65f848088cfb6cee6b21a1346ea78a494d14713b21f74528f472bc081bbceb76fd3d887fd71f852251e412893ca7899cf348e41cf7a2d08b449ec3f7688a2ef39a01f5db1707f17ff0898cdfc674ce8773fdef25fc94b445ba48228d1b5f4748d1333aea42745b0506bdcd4a3eb065a49539eab22423ac468c70a69d885bdfe5cf98718af47dedec43d77f4dd13f0fcaa0b104b594b9f223120a92aee92f08a1e96166d29ebd2ef9d9dc8848db3733d52941dc877f6d36644eb623f1a5455a8113b753fa0ad1f7bdcf2e47728e0715b4f3251fe3395d94f9bee44ae020f2a7d4908ffdac59ed3b9150b0744d2fe59bf8ce1f21fa4ef93cb6dff7f07657eeb30e8673fe3cb4a0ec5c47732f264d38c823c08ddf1120c29dc7542295783b567a166e69d07e2f830145d125639b6bfa79bdf76c67aac17bd7bb30ec9bbb1ac36c62534e9e40973e6b92364d2e2430dccb46275e3522a3959ad190211d07e11c10a81bc5f4552acf46c30cd9358b5471eec278925cd77c29a87bfd109f0f67bde28c97859ca4e662b56333940e99c1c8bb9853471a08d48d027f2302e103b95e859ef3ee83f0c1f71ed4e85b6596d2eb4d292603d89a5a1533c99d2f471aa95243ec9782eaee8635b75989c9fb4951ff26d36b26386b0f91b5b7d8f0a5a06e438824deb635a82ae8ff5f46bc32a8bac309937bd94e3e977adf739f93d732ac696e648de437fe3438e0b7e97ab64913a26783beeb5f1256badd48627cd048a767e3d1d3e9f4ff5639fe44f4d9e99dde1342d4481bd25be9f88df3f8cc5293ed503a7fa37dded6065a1e96df0dca68eeb0ed3ef0410e1ee8031cae6d91345fa51dcf85efafbe4d83f6ce5141ffcea95dd76bd7958455d4bb792ad2780fb91b38381f2acfd9be6f547e37d049dbf8d6b991bb82c6e864da2548bfc633d56ab0d64040a70d687b57bdbe8ac4de460dbaf05ff7663be8543b1d9335994caff21c21e28d72d1b7aa32dfeac69bbe4df57d6ff0df0d2e5ea800c90d2e5ea4a0ca5b99beb6cd7bdbdbb66dfb57a738e09d3b0df18d485bf8dbbff7de7b5beda638b935feb4a91eb41d9cf277b39d935bc3764e6e14f8eaa97372b7bf3a743385a1efe435aef77a7d0359935f55c28fdc0d3b44c80fa87fa773230b3060c080815af610be776998f69e76cd0b4e613e8649d3cb961d3722b3e714a017b38939e7bce075cd26aa206517ba99d3b092a4c71b7982a6e2f7dcd411dbf596982af22e7c26ce5971ae63d1e5ad48c74add6fc473def7d710dddda69040b729ff9eebb6eb9b3ad2f55c6e2150b79d8d4dbf16060d570f41fb916492ac0103a19ffdaca8ceca46a1e868119aad6ad73f4debebb3eb3d0c5e4f427a339ab711c1a0e75ccfd85f6fc2ace986be4c52661e4b30df1ff267a64ae337ef256f3ca916f05e90c378220c28aefc773dff1412fff8d146e9f7e71fc2e4eb27aadb0ada17c221f9e0b5047b9364481b107f46ca36db880c691608f6d8fb33795b0ed89bb0cf78cb21b3d836a459207ee30fad342b9bc104d408cd82769c94e4adb2f767d203865a825993e58d324effd2e5570aced00204209bfe1a48ecb499e69f34ed7bd332db413617ab405a800014e7a4f43fdad47c99c212cea0ddbc50a840bb0622d3405ac8a0ee19cfe240415a8000b4f564814a0505dab1914f4b078aca890a0814321333454cca6f938cb67fa60f1bc1a7e3d75f17075d40bb6eb596076f16cf0192d15ef7d9fe699f1e74cb814c7de9e019ad386e2ba3f29ce97e3d574453f07b2e13e7381f4e726438de6dbcb7aa12f43a6c8eea4bd5c570ce2f0eeb555067dab5df361b7f82366e34511084df44d1c60d18df86edbc0cf6439e445d5a8982b6dab918d75feb1a890aac5460ed34ef742eeb3c9dbe6e3bfa3d670c613c3104540c613c11c63bb795a1433b2e1568fe045fe9c89adb3919277a2b27f17dc93997d6bf38e7605c0c5fedb05e83a2e9fccbab02ed9ccc9730d7cfb48f77ecd142dbd1380a45a973b134b7f3202fe34f48a2feec6e7432f29794648b1097eca7b3b44f7b61f2ef49dbf9279982b618e75e526c24319e23c53871eea5da274baa835a888b737dbdd25cde88f4fb1e6fc7638c1e5d7a8bb9afff9deba2944d4ae70a016f14d7714eb677b784efd25d46f1ae6edfcb08a58c32466755ff90f764082be8047d2fe6f6d32b409e3049504bd314547ebfb4a923f165b4edd3ad2a8c54430761df45edc573be349836ea308dd462ac68d64ab7b4cd34bb65dbc7b969bafa0bcdc52a2bbdd59c94a250ff1ca75219a9c18b641184fdee6d53fd3df7c8fbfe17055a20aa2cb38b7e2893264fa0480982c7870f950d596e3f74d2040cca4a3e7d7a925606a526945f523edb8e5641fbf68641be18754d35ca7bd835a5c9f9e9abec8d6e566dfcc6b74b055473973242e79e1532064287bcb975a819c59f6c87da344986ccdfab52e94be79bd364a9f4fd9697943685e44a9b92df73a5edfa42711bd1521ebc92e237dd01eef3f7dd0d9bb9efbb7a1fc53678880df802edbb7ed3179a9adf73a7109090d0cd5c9b357564fef69bedb60f027e7c20e0c78f1b919b75ddafbbe76fefb7cda68af0e529dd051af86e0fb7227e37fb68e01b450afc090491386978579b9fc9daf5cbd72a151e183fcceddb778b28bc5cbe7c6dea08f48d88e639effa67a7ebb1ae27c3aa5fcd733a2c43782b0b5d4f85350a7cffebf263f5bd8ee74ad1aee965757f4f1df1bfde2ddb14922b0a3b27f99a329bba9e59367ed3a9ebe1bdec0b81762ad585628d26615eb41041cb5f219e937adff7fb0c3a6b6c8082dbb6f34153432f3716322ad0df4411d18e938c763c47a3516adff11c2a4dc5b093273c220ca0db46612eb33333d75c7fa66e83f2f76850171568879aa85963d211d2e900b1d9885c45501b8ef361f3342877a7f19a9a47d1568da4db4f5b6f55a34c8b8ea11d2d738516b7abb15423a98611053275e982887f8811317ecca01dbd5f946ea77a6a86164237bd9304215c82323ef169870e638cd76503286ef7ee656c0163c718db2f29ed55c66d3bada0dddd0d217cb19b4a43f86243f8623784f1bdf9a65c010cc8d45c9a3242aeff0bc142a9e6b29d6528ff99ef0249427fc8b61372f96bf480762a0cc3302137fe157fc21c137c3a9ed36cbbd386695886613a2e1f5591058699e625ab469da341d538c7a79650384306aeaf7febb80a883f19de04136498bb3b0465b42bd058a544a01c978fb060c2edbe8c11848aa230c6fb8e99d9ea38d74f428176aafb638c408ba277e2f6ba2edbf947ab724ec7a547772ad06ec2f9942002c707c2c59faa195fc773a27bfc6955cee900b96c072333ac538bc17fc3d9855b5bbafbaf93fbd751284aa30e101d9e785955759cfb212ec4368b2f16a01032d325ab2199d5dff1c518e16316f38db79453e4400a51c8ba0af415b3f84c15cfb142e4c5878c9656905097db7f4109c5733a951351e5ada09039a349413b269301c6a901edf88c5213aa58c980e442494a4905101946170cb85cc1841959b22863832cca70c20c201b5d9819c2bbfcbae425fd3207bb1ec8330ccdf9a6e0c196197411f4450b1962a4fce70b40480aa416cb6d92eb4d0f5b2d9e9f9f2c5fc2686971f969a2223039edc36a96a9d26e2bf457b122050a1332cce2ed896a67bb7c540512b763231f2aaee36e1214c2739df8fddceb7eeef58b3e1ec7236e628a269334c95875b4f95650c8bc17827f402befcef74e453ecfbc4e9e3cebe4df7603bc52087fceef647a939369bb9e77d9662e3e22f83c08bff90784f2231509dfdf252f699971607c9b433b84d91d3b6443e9784ebf10cff9ebc58b3ba3e0f0a34befe747507fccc567124170e1f52e0c8ec0186ffcc73f640862d4dc5fbaa5e2cf33ef639cf8504acdddad334efc7725f2e5952f938b2593af79c253941d21bcbc618174cb2831460bd48aa6c0833180ccb905bea3960d8410ca187b4888dff8730c68e4fa9deb77ce47bf5bce47cd44d1c08804284518601c99e13338e74ddcdddddddd7fc7279d1d2319b197e580bccd125aab7ed96110ddddddddddddfd2f09a48646830df9a69f3b2bf73de7b904a42e35a6f4d3e0b7dfdf2f3333333333f30e338eff0ccecfcccccca8cb47566481b9b985e0115947d19b0dad45eb69bd1d81afd58230ca8f5398b9dd6b74966183ac684e1613e79a08635cf30dc9759b386782262eb44324a042e88c2bc8094b17019777b00432ee9c94a250ff1c333fbf8a935cdf598cc4f496d9a58bbe84991a9d28fa0dd40d74d906da58405be9fa76db3e970da265458ae7fc8de276ac6466e95222cc164961b2c8a2c4e4ddf4a42c7e5f03137756725672567a76062aaa20830b2131170197b9100acaaeac9934f9d1f22489cd3839d3adce92d3713b3def942ad7a758b93e8373fd2a959190397d7829733b149c56a042de62e67b5d97bc24ac7c55401e64e63a5a9ba169e95603db89ae24cef94e124e4ac2c47378ce0692d20e6141bc15f63a1c44bebf0278d543744be7aa789cf3e939a9fed3cfed4e3ff75509a25bfeb355836a416be39cf727813049da392945a1fee575556e097a35a1afb4dd3a6141e3cd78836f92b54b72e510bf2c274575a355a94c5f7cfc9dec929fba6c11bf1714291dd45c78da2788fe69a0de72fb2fe9c4733ad593a1e98469a5f84d43d142e3c72a84dbf0e33d9326feef4221fc4e795d48e388b7ee94d78ce358ae7f2379b6997bcfc4df3bc929ef46cba4490f13cf7976687e7f13cf715b8391b79a56e7dd20a98014c92fce304b218926d26b05d2bdbee3a5eb310ab49b971d9a3f2dc7653a1c64bbfe4288f44fb76c403b5e72b2c4a71bd0f9a66febf1afbf7c23e2c37574fcb7ab530695b7d34972a3ed925c55cb24578a4ac56d413b4e12656c8303fa3eb7fb2d7eb65c045cde6288a2cb9b4b817633045f74f141d1303472b4ccfc3624f1e5f551dad705ed26172a9e8dd3f7de7befbdf7de7bef9f8e11aa8576dc7d76e8faf73e3ce7b2f3d138f75a08d9cfe5a31f98b9978f7e50847b048599cb47508cb93cb87c0445d05b51ae79c20d89c6ecdb0dd70d9ed3c36d5d3c2e1ed1eb7628ab938347a5a0ef5c435f4d4ba5e577933d213acecdd805dd505d885bc07c4c64a4c0854d9664e25065080be1beefb0333d840bb4e333dd44f21cf8cfc7179d77d3ff52d6eb39153d33df8db3301280414649262d2129193da317c45bc97f3fe4aa50decd5382e7743ce6090769c6799784b2bacc90469009b4e33352e83e225ed26a506343023fca2845628494bc147d495a1af3043046446784b874112a2af2525404a648b66299eb11fb5416c1006159867d44e2d2d77518c5a4084614c3575c85a5d885cbcb4c9816cf728f6650e6668fcda26903353f0ae636faa74c359ee3f1b9b7f2f1a5212fd79f755cff5ff9683338014fa1cc9099eb4f4d582cc1f587507b5b0de3c89f991dd21ec31cd36c14925d86fc6abf793e82398d5f9ce34b733b96b9d208c297271a14dea60ff56ee81528bf1b28efa6dfc6b97678ea7a6edfae41960ad38307526c4599143dc4c42d70af525581049417786995410618ad312ea04d507ebefc88db3f7500a60b582832230a455bb2383203c5135820018cffe490441233609bedbed47b39494d88b95dd35f2218cf71236e7fdf9e1ee431c8df74dd460cf294733fecdbab1590f684bf0dc49b3073bb9ad79b0d7e03a688c1ec69bef549e31a0dabe07fcc143e0821843e02c833f25086e2208ee2202e73e6330d8a79b00268b8ef5bdb2c8a7122b49d81fb68988ba8f73452eed36810f74d9ec851c68ef28f0a4900c3c4edfec9008c09ae00d33a83839fdbfd4d41963d3127a5d0761042f810c27eefcdd41267f9fbedaedc21b7fc6e1c4b3becc1cfc852bc07e0a0c54737c812032d6e60c41347a0503e5b391981beb7c2834412462cff1f44d8418b9559a18c110b5e5d40a8ba10fe0e9fc1c58c11cb59b232cba808f01aa16174f403337ab0050aaed042fa0fadb9fe5690e5cf431cf532b882098c934ac085153a38f3458923a8a8e2f4848cc92891c6991f2f9060a208405408e160b9bb9dee41d99c8106505041128ca43084c47a4938e1c1282fd4bcece3aeabaeeb5c67a3242def67d8a6d0f27ccacbbab4dda490cd3033263eb11cdf4f5e50f89d14d4956fc673dec7f7249f14e54456221914cf71a307453a81a202fae4592862e8db3849a65ca47fc2ac8d17ff8909e3ab664537923aa03c7ce8fbce8d6ebcbef32425c6893f69fc530f2864a5db5f6e7c4fca7cc9c76c1fdfcb780ea655d3cbcad7a1731de6c625a87d41e7a414d5bd93b782acf850b2405f119605184561f84646c55b5755f9ced637eaade43ff6995f5aa03ea6f93eefafa0de4ada14fff56f056d8ae7ef986c8a5fbedd79368583fcf929b63bf2676a27fef5f1af1e16f6b44b0bf46d3934ebfae7fd4e297e9ebe29ed77de9b7efbbeb3a6f8af4491fd15d3bfbf12c56677e0cfbff2def4574db14d691607d3bfdf31b132bb331ffe1568536c51ce61ffacf9272b688a95caae5f62facb5e8962e7fdfc2bf04dbf64fef597bdf27e3eb3b0eb87388651d5eb22e3e7e2065d6eb7562b1b4aa051f8bb753b7e0773fbf6103d97ebcf64c98f75c75b2c2c9d10f0c64f1d69a11bedb31d96bb4359b28b9f3a82dd68a7d47826c255ddaea7d5ad2b6fb4687fdfdea6fc14ba9435ecf61e42ed10328ebccc38f2af4bc616e3c8d79cebb1f48410376b7ee65a347a2bd3cfef6dc764e7ec1866ed626841188c60307a395a8c737d8ed6bd1a3b75425ccc88712ecc68b65a465c9e39a973712e3d41e6f6cbea574c8fed981eb36c837a37f1676a4e239e136ffc1ac0dcf8493c67bbf1ab788e8fafe28ee7f011972d373e47462273a32771f1e2765ce6c6169724dc8ed3b891754955fc187322156ef413dc0ec88d26b8cd85dbd5bca93e1ad45bbd8f6f03b49901c3e77d056228be141e7387780849978fc01873972e1f8581843b842caedfa31f6cf941d1ed8d49f7e02d16db167cfeee022bda16b4a9252c68ad449103b3ae60e1312cdbe231acfe4ec263e81586004633f75ebb37433b8373fc52ba1d7379b3628c276c614543af20d0ee55dacf9641472157dab38e535f716dc5b99e1d34e7162430b878b9dd42fd53c6d63e73148ad2a09ff9cd5c9ca67fda9c0ff90734cd392d15ecfa6e4d13966576cabbd70ff16e75cb3993d5aae637fdf087be9ff115e0fadff00f1f3b977ccf33ef0de3c4b0e4ba61ce1fe7ba92ebbdf72cc7387e7377b8f099f36e3189f59bd8c0874241b7431ad4b6d7d1d188c8e6559955a39b691555b8f3a2c1c5996b9a7eabc9b20ccb30efad52e6e06b958639f85945710d73f0bd3e73f04d55e3c99039083b29f73d2b379e4e3694620b9e8e773b11bd3ac608af772a578c505e3142795dd76563845c85e7eed3dd83e69c33cea899349ecb84097199b60e5c73ce0f5c87b30a34fd7b1f2698447777777777774fe90b3308fdc8d9773c4763cedf9f6f7c8891f8cc9c3f73cc79174411f6242c89e37c30790204e9bea77a4f8cfb30314f890b2d9735413bcc0833c2c23c882569f1de0d433fcfba9ea72f10ef5e5588dfbc18df7b526809a5bc2e1510210efbbafd917fcc2721a4029f67dec838fd9cbf84363e09218416324e3f17217cf3dffb98308e8151c6cab761901fbcf7de7befbdf7de7bcfddfd6d98732f6273a758b9dd704e34fcbdf7de7b1df4de7b1921e783bb7cd50eaa0da4e5a7b340e85bf0f7aeb762bee9369dba2a774a156f8d0b86114209a310c7227bff8391f4e5f95cf085991e978fac38413b8124c6154920a39de8d17197d3b840bbd9aceb278764ea5809ab906ff826feac347c13ffaa1adfa06ec773e78517da29407c7044ac7c74b804ab98c5af06e7bcc9fbb37c13854869872e192d10d55b5dd688d339ef9213fcd1b99b17f042ffe85e405f46d113d04703cd02be7ff437ca99991b8c9ef73bef3db456acf42835d0922bbce40a3712638c9134274571567e7eb27c09a385cb0f1051111825a09f9f9f9fffc2fd645ad0ae5b3f2d1f9e20deeab24e749a30928e7ce208fa378deb4eb843f0c29b5b1962641a51149dd10afc6859aaf7fe2db450fb3322c529b6291ce2cb5ff21eda2b57a2d881ff6c8a9f59f2afbc8f36c576e87a66c567967b4555b6ccf079119860c2e48e240673c7904cef1786a4c29886ce2fb14cfcf19cbef05d401f920d5a97bb8ce4e5889b3deda2027fcfe5eb9988f14b1118ae890ce3f46346f18b0dbc841b55f2bd767d7b70f15af722d2038976aabbd1105bf7fab81579c144c30bd764e112e3f41779e14e0bb17f75087becb18a89f19baef34e0ce98a02ede217a488d4ff1c756b2415656c337dbabbbbbbbbbbfb7cdeee5af48783127051c5ef67f256d7f712ede6ed614d49440fabf6b05238c8bf7e47fe65ad44f1de4aca9f593bf0a3b562536e290bf52e2cd04e75051aaa25da71ae84c7dd4d1a54f3dc28fc52b40bff8a5de4624639f7fc06194b4229e39bf39a9a13325c3ad4f4fc5b2c1d61f1a5afebbab84b3281fef571bdcd50763dd7eb626677beaeefa4bcc33a3fab3eec9f3984d7e5261b85df947d766d4498afc953da47627a7ed904fad78a1ad03819f406d307a168a8c4cdf644390a159a110000000000b314000020100a05c462b16844a289c1ba0314000d80984a76589a88b324c861148590320410000030004004648666241b05e8867416985005c033236dabf73ca0a07a7bb54425016e1689120c3026fe0fa0437bd3d85637ac4d6e83b15ab798c154050517a230f2e98bc988aad370b0e202b1df70fadc7cc48e5bbdc465473b614cdfb3b5da0023bbabd424b42931e3e4aa4258aec408c1831b12606db331755ec562af72c7532e4da4b93a786b0994f627aa17cc493107d25c826ac31683d65b24aff119e4c8adf9c1a73afa2d0fcdf007d5435bf4a3a8dc78c26b6f614e9929fa574557476ab9c7c6325063a02898c8ad506fdc4d2f619671e7c4e179de0f38491a773ee09fcdae1de8fbf8d3e3cc070a4de3bf8269b4a7e17b7f45d5eb95c9c5c6a17c1004645feba845c95094daff16fda8127f8e5f715436144ae9072448f1f4680cb568dc785e49ac30ebe87300784959583331cff52b593e06c812bb85633e3e4ea8c576252bde7a335d38e6cb9b5c21f91ac8c8fb5ef4a22b1fb65dd131541f9a5daf0cd7c350c1f0d28569cd0408952ad08fd913fcf67ea82cb367d847e251e99b1181084cf3e3cfaf685ad56ab3afa97a43c87ffc8540f25c315194578491c18b9606e4e61c67034d89fd338096e5930a6b87cc95a38d319704904d122fb268e3c79b0487c8180e602e9d5c2dc9533943643eff54bab0e4de1bc563cf7df25f1c88c9aea7e33dc101bc42eca22e6b7537b80786ab08d127046979db23ec707a6954a5143f9460db75e8c4c37cc45aac805e99c7068e7317d689bd454fe80b36738a58b94fa11985dd86a3b4bfc36e894907d259ad1e88b5d1dc9e49c8425b21ac10ce2d9777ae12d86bebde8703cd0f612d1bf6acdca32414d423afe6a602390fc8ee2fd0284db09a70406eb32e62acc8c761ccf1542484822df0512987aa1118222dd6f40c878dd68b3626f2642f4324f1d0814c914d6d6e66a83911a396dbd5dc0275005b042e843d32589c7224134d904c54137f606fec202f5c41dca22403ba3a18aae405efdd1090aa4e6f50662e61460b2b5a4a18f6f8830d80dd53c44539cfd0d8b0b4dfa39eb06ffe3f0d1592b5ce2c3ca5039684a890a224077ab584f01ce1b70a4939adff9b0420d1adb17643c2488fd9e93022c12a9248a9e3b171ea2835a70eb28d046de133efd5881875799896111225cbb2c2c4118feb9bfd7551059d8cf7789a104e519fe259c7a1d422519516e1d0f2b87a64f1ecf523cb5dbb8c0f4eea1f85a1cc93a6db89663983df937852ff26746030f55a7f3d07d77c7fc6ddd8b1edb6d495f29b0351571fd71a580a1b95ede6d07aa4c109d2bf43669b6a7b8046158e840281cfd95ba959b77c06b1337421f975687d8ec30247d59d4e9e9024387d74d81f07ba8ec31542fc0c173fdefbe87d944ccc0de723e10d90febcb0e6b8100baad93d3cb8471d9d5c1f0274aa316b35a7ef12accd4b3027bdf38fce4ff5d7aa962e2032c5d0efcfd14f78b55e0b51cefba0071c35fcb0d41f04f2bfe801b8baa43d12da85af3cd08f4d117eeb5d03a77dd27a0cf742d95fcc6051db958b175a151bae3729421c0448a125a005e593a80c61f47c8eba74b46864ba06eedf384003cf188da071165a590c54499b1543ed72cd1d687c85086604b662bef71044307403e9f06db4499a063275d1928cc50b4c6f5b841c4b24a3a01285879a37bf4b367babd3bef680133778d5c77b8131bd020975ef8909e1d35381814db475a89703d725c936cc73523fbac45c62b1bc9508f8a005957021d0ba86861747f11e287b7d70b00c85e6442c2c717a46668f227ae6f95e643087e1f6e938d60ef1ef855ce4c09e01da5016f7819ecb57f347c8bc2cc1ce19a401c281d3b686d4d039905cace59d5fbe54284a6fd6a1de33bd455690e06056146cc2965a7d70b9ed25d43c191528a04032907d74313c031cc8834d2c9d27909ff1de55582395683cbfd315ddb02b0d9f1b716b2e62bf7ed479b30f5e8501f306c802108ddc042993d0d2e69913e2d35f636851c1dbabb2dcc466a0435df2855ab28674d1368661a728762c7ae79ffa2bf72f5784af456fff3d0310bfaf3ca19fc82645f8ffbe5201bf7b07cfad46b5143e0ea1b6e25a426d2800345a7ceb6fa60c77fcf46863665a120b8075c44eef53a2ccbf7b0040ae8390e365edc94d20cf2d7e5148ba834efc0685bca90f7262771bec30b22e63d066be2bf1b13b6d4e142896b453bc457140e70660366176dd0a0033ac160ad5018cb33483c79d39725c4758cce6f4b0a167bee77b97a7be7fb17d93c4344c0aafae3b01b54998b36b04e385e97a72054905a2112f0d880aeecc02a6e5b0ebf0b4ee2444c051d6174def3c9aca53b3e021e064535d363dc99b011d9086a19f36b2bba42ddaddd4215a1e0b0f3f3dfa6127719c3cac0e7d6f0b114b1f09682184ae9a9c9fe935fb984287fb6b00a59946ca56e0e0438ffa9ecc091601377113916b9c7e1926e011bbce5a9c23c0730a286b1001112f139dbe6f8f0c09624abd12202553b7e5fd79f4a126ca84b827e733d730a8adcc29e032b0ff00492f59764bcea85bc00b9006ecd9d6c8c47752b454f0b11b450fa47cff4d0084f3a87c8a94f55e8cb1d97e1713f56b879bd3aafe5d5e9cf02eed16e81aa439342a750c8dec957cca3798ed5dfc642fdf80289ecd41ab078b13dfd68132d0534e8b661ebfc7e8a9bd72b4899cdfeba45441e544b98cfff1884e04fef696b1eed24aa19118fd2ba2f715b1161f10a039dc8021c98327fe51501011fbb6e4de45d7a66fbad0127e8c6a5f412221628bc3da4947814b5946a427f17d00053bfb47f37d8efa1e8a8bfffc3bb0e6cb1e93fe02dd4f1b71869c9181efd3840f8469ec0e6ec367f19b487803beac13cd58ce9d9bcd7dec3e84bbd6a934771c632d740dbb80514e6a5e736c7f5cef532678c10a23548df4c6579780d548f049a11d6682b0cec442bf57ccc255f7502444e793135de809d4b8489e3a8b927fc0f52f6d18f05d9c7cab9102a8944c10ef44d32e21b8a9a1f90b93bb27ccb672e9ec46ef359750c0f43b72c9b630b902ac401357ae528b1830d91dc08a83c875e51c3352f274c8397718809022c8c862dba1978d0a84d3cffcf6740596a2bd63248fae800dcf316302b928dcd5eae7a25a7e945bbbc76cb90e0caee490219033be32c623f791b52dc8bf61f520100a3062bb9c49f8e31f1054ad8c5d11a7131a15973ca14a61de261e11cb21e59c23b236842741ab54eab5d283aa6a17da3e022710724ef5d8666ba2d38715ec135aa475299d70b3c5aa59fff4c2b4b9d6cbe7f5ffffe8f4320d83fbbdffaf47a18a452828b01f5e30551f08a72746c445a94e0a9dea689ec8bb7a1ff7be0829adf32784ae170b64e97951a58c00dd46227de0b52fc02128dd36978e959964c4072cc239c409d0b88a449c63d8eef0196a9440debf0a4b7278411783c0151941a1b6336eb055913b30ad11c615692cb65a00d504a93f0174d3a6081e1d1011d68f562c50f2ed784679ab27821b9f3e95bbee3bda84c2ff0cd9cf85e783b3964c688aa6f9c88731ff489489d078fd3051ab4d9f74bb005a3630929e69082892de9f280b15cd1352d9038e280372dc8bd0d4cf13588fc45c0f351cb4e1a0f7579b9e9beff91b507de83efebdc33238592cfe2996bc95134c20da309154e47d06eeee4706283835990e4dceaa434110a3995e213b22026092b240515fe1c1344b960600c0e92aa2deac1ea15172019a4e39c481bb6f98d2ba1d584bed44e914c7f2e8dc80bc50fe2dad03c33063ae6eee4545a90f1cc090cc49eeb129d7a15dd1703de8c704485abf139afc9cf7022974f476f1e80e22c519a182d343ec2f2becb97db6e8066c8a067a1d96e446931461c216717a81a17a256715375b2017ef998bba1b44e7ac7b7587b074e0751edc2b734d9b5f27a9744e0644f843cf34e20c2e92dccb637cd1d513dd43f8563814995e9efd824cda59bdf4b3a3860aa8d0dd212f9d3883b1c3291ac98fcfbb1ab4139cae062a0f5273c34d457e7f539f221226143c358c89984cd34443ba23aef2ea176db71174a0d7cf641e2692132d017415c310884dcd850cf248e042f6659cf964cac2cbcb3ddee85dbeeb6b4fd418a8feb6088a5382b90df466a181df2bf46a4040e685c793611da392e803f5fae7cf32e3b4b5a82d29ebe6c45ae57234d3f455bfc177e8e343421e68fdc06fcafb296683710936d932b47d2b77253890f06005504860874394104bd9448ec8e824adc51f92c20ee05721025ebc2e81e93bd7fb4702d94c5440a7fdb6830ca6d1634b6c2540a2a7c14ca548eb16910039508cab07d0752bb6edecd613565747b40bd5abdceae3e5d6d2377fbc016136109b39c6a5e78edd7c53dc2f242425d784c78206cf2ffedd0ed36f08f02381e15eef6fded6339e5fbd680d6d6a4864469edfcb16920e7ef81c3aa430d2aac8e35fd3d0fd6356cad56ba7e0032117ae536ac2b2eb010db3a100913615ff089cecb103a399116584396361e15360a5c836c177c837bdbe7dfa9e272fd627c0790c8708f6e90f82669a6c136d4186ac319aef60118a70522a45f4ed46d3c81208e521a5ad94ce5c451cf0a0b22a79edb63259470ec01a62ee41c9288953efa4438722cb39db32485e88f78b4f2467e5b843d2d0edf506d2c49209ce365f0ad27e62735e0fa09147f991f6570a8453d993f74b16285a17838a38baa58b26c076dfa121e2c0206dd99d755f80b63c775c0de2bfb0b873504fa9a6766ee773258d5ce5a0070b037511c4d713dda0696858eb6e6d0f2d88791040fa284f12bbfb0d213212ed1093bb96f294552e03de0bc73da9b5ebabca496db889f47c700891c18eba3468d9637782a9ac20944e4456055a745518bc1f71fa2283b12c950619c42d9bbe1c20304c08312c8f6269d3d9ece930d232e7c4a3811881a99ffc42bdcd6f1dd547c85bb7662cfc9c8e1ab907461a71c15e9ee5547c8bc52f917f9b60bd1d2976c71f1084c70fcef7509e2f8d35640380f08939e5d32c281f74ece1aa68ed3c293e4bf86bc0624195bb405ed14bed8cec3f9a1a04695f31ba86387343adc4494283868f54ae9aeffa9fd0225cc711cd19900e622cac7a80827ca8d60e7f45392eddd6f2040dcc39871830109f07c0c3a281f4840b8c4fc5e1a4f9392a208932f87f73b7dc2a863d96f32fe01450c0641ad6f4c991d0e4756e9da75c50cc1cbb784783766ca31d1fcc7463f407d9ccb4bdbfa4e5ebd8fa67fb45736bcfd799d33995ca9eac62a0701f2805edfa9e127da21ad30d59a530858f74e02661ecbb2cb4f1f41a50c4ba2dae80bb8990dff363938bead282acc07bcc5c5009908caa989fb32c8ecdc11d00dfbfb6e5fd49fcf5d251242af0ded9c69c99a6fabcfcee884f10b2dfcf9babbe82df42ec50256c6c7b44bbac19e909cea38c14cc7275f8d3270c3ad6f09b1721232e4a2a46664b0afb195f9cae4557914d842a620aeb282feda0cf2636ff60a89c9fada8b87f2aa7f2a1f840024ab4467977ec258f70416a4456a04a58d1e0b4492b8b36c6f9e4b9ba4942e0513e2b598cb41732bdb4224f88488987dd9dc1c8d8899319dd1ec358847631b3cf115d5604c80cb551393e19e4712b5d635774f946532f96625c114d504d55e42db373ed4a6f4a7a0fc220fd67888c91b064eef26b65b7cb8a4e0c0d0f8d642439f7f456171e568ee943765baf16b094bd8ae5142be3f1daa5c48ea75cad5daa585040160427151097baa84eec994859aa652c3d15a7e53fd74b4d243db6cdff35e58f53de8c1faa09ce4d750e78b46c6bb7b7076dc6b6819e17345bb5046ed052eacee65aef4b95b84110ab84d9bf92f4da05a4ba71bd7fa3c2424f3d486cf265ad8c4d124839abe5afe0a260ba6022f6653ff57489893dc8a64db22d6f190df8a910fd35087fb6ba6e36302e0ac1b616b3737f3e7b487fe16b8f8d2d97962dc741367f698530fdb7d427312b2052e80dfea9d69c00b8fbfb238b233e772019ed0790318ffe085956204c56153a341d80b61bdddbfb557ad9d0504927f90c591d9a5539041f3f65722d58b334c9c0925c848491d203b2b85136431733612d5260108319f0c2bdaa3af054398cf79c6b05bec515d5a67b145b865337cef85e7441bc59abe9bd000f4d8b3471eff0f44e9a5f9baaa9a3c8e3e233fef7c0ea621833f97100e9d33668df9754ec993d455f44e4f4aa2593db9dc27409ce7a606c082cba9904d5c2efe0471d436f2a9ca51b3c9cde55254bec0b9b02ddcf4431c12f7aa8280c0e0417fc6230d1d1c338efce28e11371c6c61f531bc8bd267b6addcbdab4126c2e6de06fddf2b43fb503676d0b55f9c2184b40a69b673f2b4cd3e974530c4f8d0078920a8c441cca7f474cd3dc6f81c29dffa776625331c30725861b64d7d21bfaf5fc480e54ca8da4eb12fdeefd0b2fd577d74c06876bc237a80e10359b264b5d11e420dee79871da6eba25d37d34154d8e1ba9d2536bfe5acd968a4a980e22cd6af1e545907c65c5b7ad5bcd2e7bd7cfb98b9d1cff5d6a8741f80f122ab0f960a959db23148b15906fc6e1a465c4fdc4bf3626e8e0448367721ab45e8fcbbebc0a8a248ce0906565688fd758a91b5fa984cc9f9f692a5b4a8afd5c4c25af7962583cfe795dea7e915de4d407505aae8a75280cc6ca64bfe7acc2d6eec3f55ed2e9314a06470d6716335332913d4af82772f2ebb5d1ca9c713573ae7bb9afc376ef7d638e5473843d34dbadbfb5be016cdbaa4eaaacb51ac0b04aa2cd16c0880710ec7a1922fde87dcffa284ec6c1c579f6ba629f8d36401eed63cda37c73809e9c8de47330a2f58bca93b91708c21090dbc3acbada6aed5d36a3d2190cda54ea93a2b91fe5d3c1c21187204f753e3c5604470362da365732fd86d8d99e629f72e081d27c7ba1c0d030dfb296b3cc69c3dbd3fcfdd3b5e69863eecfa878a5d01822b239c6920cfd512c278990ec20b944cfae0edb968b6608992ecfc062c5697b1aa97996b751c118fc9f2e9beda28f877d7e1030e3ff3f111c332328262abc4575e0063c6b18c6889ea456c916146eb5ed534b029b8234a5ddf4d2d60d8a9c7470bdc32d735182c250879d4389410be40fe2f5f06881d64c74dbcf57de9572f5765f4d3f0f02781588a6878f3f7efc110fee43cc88269afd8ccfb252d305dc80cdd45f08b435c77e4d5cb5ef07bf7f3a3a0a14d3d79801b16915e3da2d1f5da420c379accb963455744206e86a5aa3ea7899a10b76e4438b7d9da0f860237d57beeff03e48dfda004b86acbf75c6ce8899be6351dd2a2de9ba6b28160c7726c6c94a6e8920e63d22ae7c27c452d7e0f6c667dcc6681ebfbdbdfb8232cd17dd1b7c45a69027db0277184eaa1b979b9d86e755fc94edaa46d2106bb0cb0c918e83755fb7fd524e7813e56bebc2a890008519d0d2c08832a4c30d22e1a4f449dce30538102f991221aa0020d857ec1bea481f78db163c912697fe0948d2ea30bf413264269c1cadff14a06d5c42eb51d4e1c4c7f90c69a020ae9c26e7e0b5c4f172b206fa9af09164fa2d15746bcbdcceb358797a5893cda83ddfee2e05ff79732ae5308b92adedcc595f4704860e50a6cdeb51a23230499d633765f1cf8af87c5d941e7f2b4188f2c6203d15658e34fbd4d0f4d1809a76bbeb3647596246c27d43b47f36524ff079e51abc13e6eca46fd1a5bb7228e1812b97467ec1df479bfd9717707323b49ce7b30049f301e03a6a96a28cda54195fa369af3080553f3ad86020e642c8ca6ee360be0650da6c5d638e44979fc292b149b9319e9917b8ee0488998e4d29b5088832ae0e5111452d0b47174852ea76d50d9a7618ab411e8eb3b06f32f91bca72eaaf318335c5d4082bbbb3be3eb2d67c502d7e6693f6e53e6b58581f5145337080a529cb75c8a7d9cdbbb6813de505442c0d4c1ed6940c5cfbcabed7a172f825a6e9886cb6279709c60f4ee93ac49f8a65414afddc21498621ed0e0f904879528bad87cd0534aed430819ee74a4e42e4f22a889137b4d17546d14205881c6d4cf89e535f8e1969d5457a61ef49b1b1934e49e9a1405451207dec7169fcd672b183182027cd4e53d2022db34141bc8aac109ede0a3012f371984be9d6041f028a9712b834c8a6fab3628e8764b576774bd8cdbbe86c0d58f18f242c8afa2ae0c44076b4680202102d879006ffc88d7269271f28fc6d40cc4b1e6a55de0a5456d32b0a430d7451f8c4c2ab445c41330764b63675d4b1954ba7fee9347c2c8de8fad8fbdff87c9ca843615e1809a9a9067d0e51836b7b5e2935a3f68942aeb4f6ace2b9a63ce346424cca3a357dd7dc98b28a35453ee2abc0534cf878aae0daf491fe2a072fa2d5cb8ecc73c24e07bf48623e1523b292b8f8125a246bb42ed29d693802dd8268f63b9b7384735841521f00c81596a582aea9d2c3949710f948ac62b13323342e8b2049110e83c51a71d2adde8de951988b5e46da21a0051ce681da32a614344c6a8214585e872185dd9ce8aad29c156fdf18421c8295ecdc3143e426f8a9578f9da5804cfe38fa8c3fca86409dcefb45d0418bcd146582546aa91c30b0194902204eb87245a0028ec60da189c9ccbc2817b58d0267b60a523b66e6c8ac12ed445f3878537289903d355e49718bae48de76c3fe6d0797de22da34d0347f6a14f7b67a0deccb3bd225472292b8310990d09ceeb6aeb62640619a23a00146b8690df2bd1d081bb4eab32311bcec9d8811a668c32de122e9cdddd08e3c409ab0588d52bfc7f33190c15a70521b4e983c674a9f6d0dae66341022cde477f8712fc0a74ff0120384fa0ca5ae9cb0ef947f423e7e8cd0f0ac5590224d3794dc6705990e8e0c9336d7f8de1981b86c39918bd0da1222cbcc84f576b3511cf01c6a048f21349024c91966d94bc2cae99187a0f55611e7d0398ccffc6b23925a0b401860561fdbc3125fa854c0b6ba6bc830d86ae7dc593e5cabda3893e5d974efac2d3c73f44414a4a097f56d84ac1141eaa10825e97679242e90a8cdd347cb943efce8e57edfbb73c6903a11fab8843912e272707dfbd103628e15a0f8af8108202e430bcaab31618097ba80b11b5a7e33fd66c353f984af64d78d590d9dd3d57141ac5b499c2d68f6354aaad41b5f401218ab39a42911d035e388769c94d18b037945227c69845ed1b0e3650212d3718bbfc615625805afaf98694573b075521e70ac64bb77b8c45370c3d4d6e28b9f54f5d9ee94bc618ec795c0f54ca2af64e6a1acd745b8b0b3b426354fcbe72c755ec48d015f70e2c8ca1d4250369dab1845f8523f5cd1562569c7be8027d33ec87b83a88a586773079dc66a884dd210039cbe2c3adbbd814b20e175a5b40019c053f9bbf7d87ed084f4a962b630e2df43a64c3f2fc24234c22cf647b9381c3d5468c9b7da45601cd82da9b2308fbaf4817238691783286134c018652eebce203c501acb8c6d66b6e82a56a1ab163ff905f5e9c9e5baf3cea986064ee9eb94eba2e5922ac90f8f6f512cbd8577e3732dd2622da44767d31086150ed75ddbb5a9696f4b997c7ba2f22daf45d962032415918b9e353cf1b87f60ecd290c7a30c0416dd13931cccb485095059024b431778a4e4d1be0f6cc93e2d3b4c8350a57b85f799aeb70ef54acb30678655b1494a534b712064952a5eb10c738920f33f2c1b1b10218133c54167461dcb5d47d33e86352ee2c41c016c4e186a7855d9df102b52c3875fc0417c7865277b26474f48a9434494b84220e1b4c9d5de6989fd9c81f185e35517c451601aebc0380677fc1f0f2a600a9bffa39ef1e332136ae2f036ec45381cf3fcd4074f106b930be5e5e4ba8505494b30eddf8837b70fecdf8c9d9101862a8ce7c261054194c6dbe0d8a4430257fb1e94441f68dc03f1955b543b70eb8c106a78d60911488c662f2973c00512d271a0b4f7dfdc13f501efe586ae53da4ae841e9f25228dc46b5cca15a85157cc7a905b741ad11aa3153a06743006e42da52009fa707915b6834ca15522f289737ab984e0c20ecb6bf63688d4c9866f65f8fd75d53b7ed23e3be5b8a0bd66c2dec204f4735a25eee4a2a56245bd1d7659298e4d1611a9347824af786fc10fec74373a8278ba5d69c13ced920c82bd6fc8a61663261ef5725ab860d63638b6fd420fcf0a45632bf78b88c10d434eabbfc12d8b82eabda832ee8819165573e2c286652b4a1504cb1672de03b50ccfd09629ef88df08ce0d8fc83506dcb113be11c0f61d38a6955b55165f978e098ac145776332fb2f9fbe4155656c02465171a55bb09287226bce5beb2f731ffbd430c18dcf2327e30b4ccdba8730b012249aacbe01a10973febb2f68bc78845bc006e031cfffa0595f137fa5a515f3a1d57b5f71076ba0d3b02d6ba65fb0b3e9764a7a43984770245fcd74f086e70c0a0b97380f72b4cd05e4f7f6a43a080324b4f2720b61cd927b7c2bc98607f4a377453e5d13fdd9daa23bfd8cacd15ddbc0018abf5b539a5e1d325f3ee5f4e1adab3a684e88ca1a0f179be11d28ce2284b119425b12f93a84dc65e1b265ce3f96d644fd56ab466373724869813f6d4cf326242d5886842c07d1a4ba8291ea1bb5fd77cb72db7dc158dcec7e20c9724ac0118883eaaed96a42906ddebdf790a48a1ba0120c4199d6dd829c244cd57335cbbb3fc85c901a797c69bac7c220232d56fa77e02504686ebdae49a580340bfc27febbf125e19c4ea0a51a9fab1232ebd86c23e55d370de42244dc78c8af53624adc708724183a66041c1fd278b99726ba81e21ae403bb4957018246e5641b1435229e4fac5df9661f4c93aa435d28e9c7ab609de4ff856700e81b56adbd358a70214b2baa0d9b24f0a8588824afa992356fdaefe5c473dd347604d32cbb96a90acb717bc51059cb99b94578d89100826b4d130861eecf72f0beb5a6d4bb970dba4072d2240468854f266f11dce48c19485f8841f62a86575eed6cd92d4fad74d2fe024f501175ddc43a0071bd78dfe8d1b38734ae61b069d8da69335a7317037f7f403127054d7e9bd2758dc17fd5942149aa5c0692f01231dc1dc240aded34f1f8b39432fd3fa55effd1426eb9c4d8584973ccedf648fe4645a64890b9a2de285ce11e428d00321c9774765c059bc41e7ecad8522f4e34e8d91e9e06a857f7e10265ddb2003ced4f98b430e0565784623bcc719331d4a245b62260ca8045b0ec6601791ce9efa4f46a0fdf74024264ba809c8db343b8b5909911c2b68480102bbd88aadf80772b434bf5f81e963b7f7b555e8da08efaccd0f64861d544722edfc7e9172731372840c5915fc28bc9bcbe59a521d909a77549114aecce3f6bb7d57d27b1d508689929dd343bb1fb359f1aadf193d4cc11c376b650c61ebcd7833ed5865238381224d18068c655d0ea6a27077a3acb4ae030e5cbe3724131a9308bdc036c99dd4183375cafd29548db74f9f5075096255d381ad88879950f8c79a616612d216660d0b4f0d623081f6fa742bd543873d2282c80c6cc38875ac4989cad2b07ae09f7a9f139b67a565f69ba218ecc44a03c5fa2915e38a203a1152283fb2a6f19b31b3227922a363e2c2643434a6db43a9b86ef3aed04cdfa543ffd63862d9725cde6e80eab5a901fed002ce104bcb9ca6b3ec9394eec45f34790821c213e58d97017ea840b2a688046a3e35971f05d3171892990266189141eb0338ee5ac658815f8640382f51875c51b540a5299a181d0df71b7a524496f87ea0e2b9c08e5a0cba89894d7783c49f3dc2aee478f0f3d40944b8e670ce4e5d98579ebece0de47020de3d11fb7bf13ce8089cf378355378b9091d24a19547edc83c87b421fc00b6828a40f7ba22914e2d10fc12840bfa340a504a258aa57a48ee62d38f35b367234e0e60a6e83f76ecfd1a128cceeabccfc0bc6773c0a1e80b306142669eb1704d0bca753da2755ae737a1c7a41475396df63dc15848d965e74a48e362490b1e283f20560c949a4f0133cc9f48ba9ef6f1c827863911d616a3cb22a8dc11c5ab64ee6ac818cae732e43b12a75e8d206c23c73cdf0317b1d6ceede8c208af574fed8150b7ff0ecc1da48d88475aaaa3b188915f4e0ea5f3e6f1cabf8e11b175ab1560106649e5658472f90ef756acff5eb0bbf6c08994db3b15db6db28f460254c4d8801e01329a8641328cdbb8871e81319d4a3c106f9b5903bb3ab3bfaa2bbb980d06b78c25b21e4374d38cee6f49c3dc2999770d8b9c4499e9ba77010bbf2ec204c9622c9c7386299b3a73dca6056ed636b20aec4a5a9db13ce464c90e7099e3d11136710d78e6176153d416e465376fc4d7ab715a81bd43d910d9941c1fd6d4bba63023f699fe0ab5c42b6d0d61bbab61e9651d7dc3f7d17395891a7f25207ac4b7fda0684490195f110d2a82004166fb5106ad7e87b0835cdaf4a42d80ee68fcae3dc25a72e0d71e0a277a7937528edd29a2c1a4a6d7a3d088d36d1aea99e98f5f0e20e864d7b49b1681ad22d3ebb114e0808b00c2fdf8447c0980c1983023bcf18415d79c6ddf5e2cfa9238e5ecdbd92e976008be7d399e9aa65e0136a80052e0b3a77c9cba16b810c7a8fc0762d18a1c21382c18cfd83a1510aa04ee21a3887288425702d210a664b090a25d5fa3b4019e1d18c18ee6a62ab752a3c0c3b2ff9f6c0e096b44c7254e6525661d30192087973299a4936413a71e07c157082f154977bfab9dad1994121d6a01c769ef2f2dc94619619cded3746815222e168e8cd48ed8bb024a8d66e036a38399b03140536b4fffab11a1135ffc6fb9437b185b6472cdde623317638de28abcd96365301d3e35a27b54704f32b26d42e1e417ffe9207e7d6cab12f1d7de26d2d662fd51b2badcb10c32619381b18131fa3a4b6befa3615086b7a799d94a6c8b19c330cd073b1adcb7f3181cfca98adf5d6f47b83fd3815f9b0acd08772c97e169299c879c9b1ebfc4547c5c49e217716073af074cac9c9ac7ad8ee5dd9d8dcd99c5b4930b6d97b086095e89d4240bcc2dea70eb064fd83da0a65c1bcbe8debedea59ac3f23800d3c26323c1118d403e4d182da2e5760d9942bd881315383d8b3ccc8214be757882886149f0c3760f330c4868106fc796e55f6d21583bec1008792b3027648e9fd1e6c5d9450f2dce39adbea572c0a4af86bf2575432b808d629101a32e48829dce32550778295449c4823e4a6c522473320534c044aa00d455651187e068ab17eb088ac14288c6060827aa382ff1c98e1d1011914734bbc2b6feb62b0224d1717f8340973cbe1f50ebca49815ee2804ef71552a9e48c940f65519f8f79214c3877e96d5639f7619deb6d3c53c15db6dfe1fe5f8c282f3fe79ae93b16d9ce9f190e7c1ec36608d99a5457f0e8b4844ba96d9ac8152fa484c11e70107a9598ec1daf0c7e88bdc6d83f721a34aabdb92a9565089a03332ba19c46ee48a9ab7849685f3a086d6aa70ed01930f7ab759773884c20bac95ab2d6127807325a513d26d282b9d5530629f1e2d105a06f085df0d17a84521bd6c02280d9b68002e9e6a8f79bd72cd08f1cd1d441203461cbfd7a358e4defe8482da8d9b124a6ecb1b34947437286459a9252d881b3c6a3f36cbb997555407e505991e249cf1d77d1d88ef4a6dfa2baec72b3ce5bc28a0b9e0f8b6bc2da725f685545159404b7d73ca89ada28deb495b36de3ff13c34bd3747b99c24f9e39661c1d6514969df1220beec5bc5b74a1a1a010a8d685b6d30d0822721cb0adfe802adc15b96557360a57a5e4b58e60e6fb96e3a3c7624c89d569ca3c19ae808cce68753e7ad6e46a8f0b2bfb7a87368c7f86b6aba62d170c213af80a2792290942681431e8de6118b2b560a6ba5b667b46293dfcf6b28bc1ad5d09053a32944cde41918ffcc1653c3074cd33143d62def39ed03bf5917c7474d146e332d1e4f2a2b6b088cd0913251d26449e86785795bab348d469164f92768da6f4a7633c19eac2409e63f8e7f18131f9954bf6ce0cd87ba89f8abf8a675adc292e18287fa86000d5af219e4c2eb7909ce762d786e02f3ef2ee46937ac9ffc839f9c686c448cc310d438a4381d8442f2985df348e00cff531b9510a5d3d1856456c3a54d4e2124bd95b4dd89c554d377e61bd4eb688c5596d19983df035f03af821cda534fbe9eb8f950598e2b7df20192c358459ac4cc9e4d043e87b7806c8034712604b2261fbdc8b6ab8a028ab07f5d2e07fda59589e13cf6c00eeceb66c5858de1eed0a54605cada8cf745b814a30985c5d1fee7878c4fd9a03548f1bbef52a136a84ff5753693af590f5771dc582d1635ca5b44d4990ee2556e20ebd9e40b56301c62bb4c93f13fc41746897e3cd8c7df039742256274ba3bfd3a3b4c5510c906840a0596643b31bf9eea2fddeb9a3dc3d497638cb51855217fc430e20866d08036ca321ba044891aa4899031d327d063218bef21862e63135bc5c4ed421a060ad221db0905ba2f473cbd200fac2a40bd57e4950113275d48f734f923b10d1b281f2293802a5cb6100b9830b01b6a8bcda46910d4b7d7d068f29a9311e628e8a264161312e8d01cd2017646e4a4c77747baaf13f5209acba0d01f10526bb5f5b179f1713353082a5accf8c3cea7b1e2a4fc95b4990cea6fb20b1b58648dfad71bab72e8c8f1046110abfac7e520137dcc881f6d4d117862eca8c88eb43388266620a0732ac3f0e71c47f444f4d1edf579c9b1146807aee577362dcb36e00b6bf5d4de9c600efd13646e5bce707a63a2aaac507acc5baf6373ca2a0bc2a7d14cab67a3ac4df004d3ff565c7f81b69a60e9d132469a6440b4e2195ebc46d18a0d36f5f3e87204a349c44b0fa099c9045e9ba940b2adb09b8b3e4f9e2ac96e1b8b94f0e46739aad49f144af71ef365ad426db0e98aeeb507720ea2a06c9ea43e50a2a7d33ade6da15a04235a8452c2ce8f869beb9cf541e4958a9df854462aaf3049633025c69bd1c9e751faaa590008ba476dfb516f24602780130c11240e701377dfc0fcdb41a15aea75cd7e88720c844640365bab4b585f08a45bb2f9e99c862918a90cb0a2f2c9bcb16469f55c89f6c6b13410708f0ed175e2b0d22527bba5983e2b63f03c923a581ba75d08ba1a45a08d9242e1b39a6aefee23987edfa435b6bb915ad7906ff2fbbb1447c4b55031950442cc26b3842030687f1691a0fcf441b3a4e844c28d1d181907dd4f20de091ac5e45e2b2acdaaf07ce36ef71b1b0d01fdbc8b50df75a606a4724be20d669a696909a83da617f09e2140e605a4a8f750698043fad26eac354acb0e89afd6baf1ab5938fa3ab1b291c062d30691b183eb7633a1a1862e904668cf97b291672516cb6a2ff5a9d4f9e7d7547971573ae70b4676dc83eaa16f0eeb9368a2661461087fa9de8c35daae769bc0c9932c4e430f843d18a9e861b044c3e6eb6224be78de803da9558d6d86a45320a65fd64e9a96cfb0e44ca50b8ad729e35561bec4adc44054f605fed468587f97d403cf312de9095219797e2ef86b5e9de74bbf3d926ee6624b19fda24423a0b5182182b9bcb0a85b8ef7ae9d040b8518dceaf63ab02dc796bb0fa74093fdb61aa97282a9f55cd354cd18f51d17077585987c9148d3a7deb79342f3de5ea13aa55ecfb62052711ee0cd2fd0c5212ed52a5feec62c0c634805616dc0bfea15d91e87696ceb526e7fc89949f17fed3dcda95a1c22caa92a2d9156d6228abbade1b84bb063ae0269b8b9f6ec7b9c6d040074ed812a6b5c021f16d7564e8ad218431cf7f3e02e4aad384bed13bbb83d65eed12cd84139dfe9e19c487407460508d62fb343f596681818f227b3492620ba90adebbfb7e055a340e80e5b0ceee717db03c00a4bdb1551d0b53392f06e119d34771f867bd475c9e26d782ce3eac17a8fb999051e4591b956294f0fc5c02e9ec0830cce0736afa2e3806274ff0080f1a594e21dc03769c5df4ba51773232f76eb4fbdcb1d62cb23f4bae1c9ae56fc3b85e0cd416d07607daeb7f72301a4431c8bbe607d2eedbe0233052953b6d57f4c29b535b92f52ab8ff4e8fafd29349793f756780121e6f8f8f0ab9f4c969e23e0c5b37448abfc3a512345a5256ff041f2214f398b25ae08a8a5b4058b2803470054b204525e61b12b7f8586bffdb4326c75198784daa293db213527578086ad82c590be697305fca1fa44dfb65ce0af219714dd332517fe4f17ba759b5f0d770893151da5fe63fd7262d1d2bc15f2da873699ddf8262a06b580a214709adc9725f142a7ac358ec1deb590808495d3a22697e95eadd4a9b9b1a64ce269783af9c0139eaf6209c9c321c175d0de312a2c6ee9457c649890b5b5da8db8b2292ee90263f667058bb5544b5747d080605a973a3a095b5899c6ab96c503af4ffae60eba8dc37afa4d1b48f4e2ab7760b5facfc71dbfdf1fa83142de407f6dbf17ed02f761768fcbc9c5d643a1f1c47a0776698de76be5a59512ea32b631291dc4dd9e8047d1fd1701ef33010f9b79ebe05f5dbf750f0fbd89471c3a1c9de9a18238ae33aab2b1da7a01a1c7e41c798fc673a7f612a5a79add5da8d24d625f573cb08958c946cf38743b69a63281573fca6e119844c54ba3263e2ee6bdb6eedbe7f2897dcbda3e8af72d4d7750decd20bf2a6cc470e50e5bf1e6d14d6a7f0268313458dee8d9e2ad7d1c64bdf2cb0e2f816454877139c68d2bfce08a8e38de782aa432deae6f0166914edd1db2cf4bfa150cf2eec02cf4c3af254c1c28cdd7464b98aa7498f7cbed71682b7b8029b1755c6eb035495577d3d65184cb503592ecf9f91c053f909c49145b1aef95b1ca01f322562df8d68e793c0e7c19adc7a8c76398d953e9960bc2efcad21760619ba44315b3c3a61e16d01bd64919ed0ee110a66d2753b7b29b2177b9db16515592b444cd70205f6f858fe4db5ca238b4ab3776759a74638e60875288fbda843ed5d52ed43a7c8de94b4f20e0ad9dcd047ac9ac379c911df8a67d4566dc23e90182890dbbab6dc27d0e4eb0214c6e920165d2425d13ca86b901461d2b725add3e67f0e880ea55b231c808d9795d06c3a9052da02e2c82bc21ef2a4117b04c6ca98ffe07f53308a9b325f14fc821d0ec09da4d561a382711bb8c7dfa026e15e47693f930cd71b37fb1d6f3f838febdfa6dbc1e0657b38238806fd04b70dcc590c20ddf6ae76b0441d0c46c362db9cbca227c0067215605e6cc64a56bf11d3a9de9a2b801cb79b72aed9638405a1404b48ed0c4c682af7841c9ea3a421baf8fb0f3d955ef5e704d1a20429a4ffe019d345d932e7b04ea287cb38ba80100004a04338f395acc8b5d4c5e6f6e8f9ad8cac6c47b25568f6305ebfa3d36bfc75a28edb5cb4ebc577094fc96c9b46358061acd4323776dd0f40133e3ca1ad1d34fda716a4dcc0b518c8a9951ed1c9804766749d5c90ab818a507436468c3a6fd13e967811baa13758923944436ade3c27681483c6c1c8ddc9cd9c4da1a36599d43e60ac63a5ad5b3a9e57579951aad6f28ebf559f8770f8758ef5d1a7cb17e52e77b570287fe6580467ab19fb70e8d75a81621b876800b8e031fe81c84892f45c268c7e2ebb3155778dfceb2450ef7c41a4c302060bb38edb8c6472388fd2187493244c2536945bcd1d84b351f4afd8ea08a5def1d049b0b603a098b266bb9cc8f8d5b51c92d7cb98ab51c0f593d62d13a54d031eb2067886f8a0468bcc7a094236ccd917de6cee1508512d85045c0d03861ffe46aae66ea7c240b1ff2166d9f57b406b18026cc4916362cc5d3ccee61f6e8929e0d3810d6e23bb992051d3516197d13ab529aa2017e9a2eb80c7a30a0f0ee8db65608cf94a07df01755c0736a81866c8d40ee88dcbb63d30c72c26a498ae3e7628c118b3294b63b44ea6222d725639f76037fbd9ebf9481c9d26287c618249a1d0a8fda8e68b258d256250a9d15029d17ec2ea346043c49642b68d8732156c5f4124fe97599cb9482e043ac172d568d1a0009a675a32795a0ac171c2374ae066405d24a1cc27b5d431350ca8a3625c416c5972525f46112e4ba08da698640768ef4da4fb6bb3e13169a22162642f3040d8dc027a0f0f019e8a3696ef3a885ac20243be1d4ec12285dd746d677d712264d338c690226fec0dd57bf8e86af1546e7837a90dcffe7ea81c24e1b9810e1d9f880f4c48e3a9eb3b760833dc08be0d8f20074feeebab2af678ccdab4fa0c82b57dce32f9d5a40bce5e968600a57e7c2378a8f0b4d40974fd4124d27b24f449102a0d2728347d33ad3ba41db7d9b2a066fa00e99aa0b84cd42e73ffaa32a12029e05da7fe04cf286a2129fcd7c461a6bbd28f9a28c7a11ba1316a3d8a0246ccb1333426d0c6c71318fdd3d12f1c5fda933cf50dc81c0ca7df0c239a2e0b8664969e615a3c7b60b7ef45dfa0d9f1570f3b9f01be833552e770dce8261c357d63d49eebc1e6a9b6ada83761688c04b83e2e6d791dc4cd6341d3a4c4d24399783b1271570932097a4b891204319af8a383eb4ac4a435b7da783f87e83ebd838802806129587aa7e0caa00872c9752739f79ce74e0224c0d3353ac38d0101c852871a588bd7893a570ae137447658fc59ad059ed3a18e71bd364a2222ac9e90bb2285fa5a2463e66cc39f651519c47a2253c6ea772afcad7266dfa88a9c9eb33cf4e9597f0a9ee72ab9a8eef1644cf818023add36e95004026a9d74fffba9a0c23929ee531c7779da58a3fe0954828d55672504cf56551ac42b7e1cd4759aa29ca1bd9d9113b162000bd09949b3a1920f148d40d553a4483239b14d13f97f973cb195752a8fe556d4f55f22d864e8d163ad1688ab932b8890d27ea23489c69c32972f83077653c1b768963636c04dfc6691904894fa8c2993e665ac2e664faf9e9cdc930c3ae4c06f7391eb42d09dbbd9f7cea6fadc4e354d6197be8b92f6e5c96ebc5506ac223965e9ac2d3b11e7b63dcf762f6e952206eaab2bc51f856e28f6bc36e109c18b4fc531ba0e2b4a999c2d61ccfdf4df42f7abc8be4df96e27cd86ad8013cc596e3d23bfcb9458c942a3b399507f3cf8cbf75f2d532b52bc3328d07fcd405b2ce2f67205be3871da2c6cc38d009ea0ee626a4d0f65b8be84df5079a451c05925a9b8b23230a195ea0031bf776cd075642469d3ea7985826f4a8edb68979927b9388081cb9d8106b0d003998638d4099704d46543239130dd818a407e771dc0171158140882d7741681f063d3d1910e5803d6716e41a3465ccd317915fd8046babe80afd2b5bf041cd2602cdeaae17636472d855e103e0b0b4da807bcdc7dcf1f8f0a4acae13410a41c02a1cf81365c9bf19ed20e2cec7fc88f965cb4a137309c716a55266e835c534e2416d05e3d15f3a74874b5758a3f78fd351e8c0286b23e6859821c3ce52dc393cf26bdf2797176857866de93dfa565b693a8b9b22df70da3938afa05ca07a977769609b8e96ba8228dca8a85881593506edb1d23e5b2f8ee06686a9b3041367b26b76868c76e748a9049fd6ffe7fee8a17293d83a94cb903c1b08990d2f59c01a7834203d5c230ec8830aaada4cc0628735cfd649af645f3d7858d046b91b54685b23a7361e78e575709f0094f9e78f9073de018b04c35fb1bd7c4288e2135746ff1b1d10ee74ba531bb14989880883020cfd9c45634c1648992f412ba8fcd2486382c7fb4f1b3f4aafb1e83b00462bdad391b8fa112884ff4063593d486141d3d89e894ed2706afaa2dce31093e0e8a3011cfce346cc54a10963d9dc8924bcdd19cf16205c48b28f13ec394e2dd7db534af3c2cf03d18e1a753be5a3b581c4f1ed364f82752d3d6909297313add7542793a8ac743480c85b4e889d723912fa3b722919685d929e1c2a6f897c9db6391d4534c30b346e64f4c6cbc6590405eed8055fb7fffd5edd418c459cb52e01e7d4f2a14d9a2a6bc008421cdceea9c0836b4e0918aefd2a1542135c847d060fd5acdca3460aaadfb890288b079cab49c5194e4f95843a07337733536e60507858c8b33167884f0e2aa3ccc5b6433a9f61a1633f24f6b0b96a4899710a20992d6d1482e3a742523d650e5b7c5e355321890cf71e0338524695dfdc172cf30240cd2b452353325500d076c164319196f86e763eba9c57c4087b473a811f3b5e099856f24344e5547cc126a66d3a45a555b385a8be9de87afaa42ed76ff97653c1d45272ca9cfc44b28952648801a06e853c3f849347182084614e213a0100d18d24fe8c6805e877c23c1b7208b2f840260da6b52472ed2c0d46e8d42b3168437049dd95040beb401ed2c9858353a980523fea835418c9129ef87b40381ddc7c7a2ce4ac4068077b9121aa7967cc39d0e93b1dea247e166c20de15a2dc5fc0703c28c3a89d90e0364de47f4b5ddcfa3713d54555c79fc87f27da0ec3f4d55834aef2e0abc892334f61b4995df1cc5308b9321546af77f2a3d431a4359912a1e58bdf1b22e0974f57ef5d207ce10ddb57faaa16f46f5d09aca0609b41dd548123fa430a1f64bb8d683c8a9f5704d81f7542f40fa79f1531f5ffcfc9bd1c781f5ba1500b61b286baf56b8c16084e4d13e5324ac127379321cb987836416ce2a56405b4c9be98739c2aa4aec5af69c91be498c89e0584aad4f6f9b9570b8aa8df1710035648c6c7b4ae86ab6c9fb60bb2e7a3854a6547f636ba1c6d8f4fdd03f26833b9dfdcc9f6460198ee335d9b88c49958f1614db6befd361878f80224733e396634fb1f2d18e02905bbb37c8758a4ad6d8bade58c6e818130a2a73d96a696ee4859a9f1d3962bfa29f06774338e12d3df7d338e73c1994ce1a6e237e725e8989a09e46d0220161496d2b55f4048b44165786ef0bf101dd9c9ce43df4aa34644be25faba32c5dd8f710aefb000f811a03f910a82110e14b17ab35a2ed781d5d00569180b4222ef7c918e80918f610316a0d521252aeb6449bb134ceb2658ca28f6255de98e6b55f83e2f9e7490a17308c6608cbc70ffd9ca08cbf346f277a143e430412248cdbeb83f4a08ea020402a0f59e6229d08490be73127232b636a30f54d89254d8336b2191b0a49d177062be61f605d764f2e0ba2ecb5af2b57d48d7d06fadc838004e17b71532cbb382675b75095a2bf5a2ea8443678da86a279af6c32365613a8fbb54e17b6b2d9567a763fbab2d03cc240ec04292d7524edb4078e41e2aed91caaca614eb7758578a7e46f11fed13642160f2d682e081c59ada62b679666e14941c745c5d4fd0facea75bb724efc3914219b23a3ccc06a9a2712759ef9263c5d8b7868a0d8562e7cf04068ff579840964c12036b6594396d293c0cc01655f59585355c6010e023d5eb76c31f72d86cb6c140ad401689a86fc225ebfa18898025f29502a9bd41be3d0abd37a757075c090fe5908ca36b1839d8918fbb3d1e1af3eeba9522d84f0f02b31e07719f62514af6584272b63664cda6a4dd6e97ce7012ebb191a8a9bba122ab495f5992ef2175de6c5ebcd5ba10311d2890a96f40208ae0abbdbcdc6307c426d675bf830aea8454cf531f38be148032d897ab1621bf12f174d96d9748615eb805d43abfb1aa459f0f3a0a0e191fab80d76a2802f56eb4ba02b717330bbf9a03b4376cca4b6acf213619542199c692e427ac4b202171217aa15c777c27735210c68963ca9e8f625187c9d60b773df5b0c5cd8dc73dae3f660701c6ac3fc9a38c4aac8ea37f10ed7a6b73219e3da427a5ea2ad762bf51d202595f08ed00523789fb14305ee7fe1ec7904cb89c1ed07fa230988072d5ffb91e80982c0fe82da9dbf635212a3527a437b7fb70cc9b6dfaff624e61eca726506c5a8b7e0f73f9326eb56055ff71253c1fc6922ebb93986c0a6280f2e38929cb6d871d5d9428f187e32d3652ed807adb266baa2cff5d03d03e84715c790361cf50c8e14da2f33716efb9d9036b0b5883d981a2f313989620a9f915a5143a162b2d02009432c9b92392747b96c3d5dd9f01cb9c0d35f7ece90fe5fa975b6ba8abef42258da2782fb19cbd6c65d593c862b5acf5ca934f012192bd978f3e396ed7114792105fe5e9d07c10a1b66c8a2e2a6a14f31528067c5268f86b5a0aead28e4b8fe7b758a18b668c00fd078705e897900f0971ea1d7c96f9cc720feb7f1d23b71c2ecf3d6161ec91377697aeaf061951e80f51789883599d4e314781c0e94a73b39855f419984eacc569204742557bb4c7c9f82c6a59afca9903a76ddb6950c2cc96971011b73ba67c239bcbdc5eeaa37be0a418816415547486e4882e2440e50fa1844fec8de6cdb04a1e086363a4450e22d4735523a2681c4b2458c164918d6fe0cf3a3394e5f92f609a07423855f005cf401dcb393b71c8ee8561af9398b32066fcc852f075879488b008dd1ccae3b07a92a7c871776caf4f8601c2ba5f2505002ba9e5274c2701509c3b5ce5ab777cd389d735c2a1d977eaf4d3cdfaed65b8e0595bb040e4bc3af48e138ae2dac26ee5c3afee165b705c5e2ae3f82ea5c24dd9bb712c57bc0a9cbec7a905cc2be949f76bfb9d24ac80c7d23381f46f6c9b17419ce15231ced5341ba041d31405c46811c4e92edfa285a342e28b9f21c3d8e9ce11c90b5ecaf7e9b9066e0c89b0d451bb724f4690fe67322d19f1b0e9cda82b19267b719ab5d3def7e9419d16026bba65f7115da32057e6ea1023995169974ea617f15ea4261c6ed105e01caedf60d05c408d5bb7284b987f7331bfb43bdd12f84bc2561c778d5b3f0a3fa0611bf004eae58c349f39a19c007d8084a140cfdd395a24293b7512fb12122ae8fa2196345532aefc8f6e3161efd6d4b7b8034e2ffcda3a37ffda6c09c7a15dc5f4aeb9497918e5347ca29a3ecd04faf2056b6d6f737540f690bd051bb0e3aa29c192578aefeceaa58284d3a1904a300552c9702d8338d2640f15ed6900a3aa2e356bf14cd752044b5be41c06f88801fad3aa603e509e2a0dd35fcc4f45e782f09dd67c73bf904f2c20800e9433ec04607ef83318da50ec85be74a561d535bb0a6f08c319d798313e0087ae1e660bcf7b8fd74146c16e200022216350692e7725e7cd0783e5e8db4ac8ace04da5180f335d74d398aa7c21ab33be38e012113e71ab0bcc61c959cf1c7a9e6bce82606e2e33a816173e1badaef571148c2d1a30e4d9c36d1acfd5150996464eadd450e593d9bccccf31ddc2bf524bb596323620dfe7f4fbe903ce8fe5d9d1decb4dc1bf59e728add4ce2f85823ee1969de4c83abce0affbac4a4823e1a8e1933753a98a2d9c30fa8d95979148110ca4616dd8ef68fd56c6a0ea3a478e2f74a3df592cee7087d14a00c9e071d4710677cae044f5ec92b13046a4b0a2249fba300428e87ec1ae6abe8f0ab7aa459343cc8404031478859788db5377bdf652c7bff0d554606a09bd5fdf6fb60ba52f49c1de0eab3311c031f03f547cd5dafd6bf8815ed648ee68cf77aefa4ac9ddfd13dc8c569f8f3bbaaa656e4420c5a702df66da1cfe3f4bcfeab9c9a9a418fa636ce7058d5b70fa745d0b5f4c503960ca10431a75339d01ca23c0452e9fb2f37df32d5d16c56da9dcca1ea5af775099c42dd970ce4cbe3cc9bc4e818be2cee97ca3e2dcd0901dfc84091023f3b02d3ada64e7e099a6a3bef77d4656ab59974be56086c210147c65fc3d9a44200b69bb0da90c53499f5aaa1641e6a4ec38e78424b988ef134c57a627b8f537298a4068f9458c83657ef6952760790f6bdf7386387026b6fcd25474131baa84147a17fc2cb73394e6a462d9021a88b83685c91f9191b4bee341692acc5e827ee1130f31fdf90dd4ec293f953bcbe936415330b7c32ddf358bfbce14484870797bde1c99f24029240c18f18f929dc3b9160f984da0c713e1bad7e5cac5edc4a5d3735358729b85278b785befdbdfc5a208fc838c5519c01f6870594bea17dd59cf6947f1e026df9b2d4537efecc3edc1824a980133f30136ad3b1b6fef61f7050f395a13e1d112cadfeebfcedf2917f4f78747772a51333a305d1b78e0461c752b3ef1b2034f554840a67c39a34a7810bcfed22cb14520301e6d3364bb14e202710adec22826a61d2cc58785d70dfe80e2c87ed0f26b22dc74c57828358c2aa61f86ea85a0d11cbeb38fc8557ebe584089d3b5a9e68692f7c7ab9c89958dbf6a92adb18e9a5b443157ab3ee577ae5e9660490d1ae56af341146510e54f5a9efc646610ceef22bf161759bf11805906b460351b15e720dd9247b167ccea9ff44c52745867f2d1dec9e0cac36706d19d5ba07140aafc9fc99760cb5714e57eda3e71f910f0c5a814fdeb5d4e833abfac7e82dad32713f7941820bbd86e6b5b9de115dd44e78fbe126225ff0ee6fc37d6185ed304dd3f0e2e482db513de060f264f6ec9a23a6df63a0f51c8bda8cbf9ef4ed3c64d5afe1f6d40d6ee06753cbe09d892cdf66e29f76a47188d652ef6fd481c14540ee7c3f8137c2054d23e3994006127efc7f3a95614df42608322b0163cbb5a06e28bfd32aa302a2f451f84347960b96ce828d20671e759439a4e8b9dddc826672e8e86c96a3cfb97449b63396839606e61f116226f02fe4debd51a14891115dc8e9ae15dff5d13e54d7ed97aa46f911e5b58c3b358877aec5f2e7fa64ef05efc10e3568a9932d9f8b24783f55ee6fff46658c549efd4dfceb0cd0961e530a81f52c7d2a4cced13145a0956a4702245a05b1f9b7348c7cff48ff5e2699f3bfcf9878808cdeab75e2c95cd5ae793c690398a2d1b78245f6b4f14b62ab43637aa26fbf5deffb1779d8755f524c912607df6179ef9314f8f5cccc1623917213e6e2c4780c8f8882605802932716006c1e159d25bfbbc872c23918e08f2c8d98e8840431964e77d2b0ce23b465b4f939ab5a24807958ad2c8459fe04784577de5dabdf85ee05ae869f5f52ff67822a3a29dda914a4547b9e3bfdef366218628298c49d41cece9fefdfd87299b89510e4af2d6db94d610570bf7ac9d67dbf6f8cae4f3ab1468aa74448b7ab71be08c53b0607914d3c3e1faa6608e1a61d435700805d3f4b91a837e4a8f14b95fcfee6b0c1ceda9c256eb0d9ecb17ee83e4279c080f9b5b241c652fae0d98d8fab03e4816d7b8da1feb0f27b8702c595319b448b4dcb17e2814d84fe739efc6131d7764e01239dd9ca4bc4d1d787af64eef8ef9e86b486e1f8f1cdcfc28083143c6d74773edc8153760d18376873a3d897fee276261fdfe4d703673d93123d3f2dc6b3717bdcf9edd67e24b5234731b1850b4e980c7d4b13142669efecd3da8112e0cb9ae951f4026e0c21bbc76a6cec2383ca236614596db1d1f42f3f9109f631afe001575e6e1ed99054c8a145a3fe2c186469d9aaa9ed53cba04ac9f15920db2cc381f4fcdc6754ee5022c4149f9499a7828a0872823987d4979ae5fb09f8a1565f9ce6965c791fc926285b995638414e532df416918b73207312427f11c90f6b0c4ab689aff7887d7045579bf4b64add0d1d44f7b836276005dafd6d97bdc5b23a54e89ad3f9fd9f3a3d1645d317a546f595ceff6bd2a6933b9555ce08d8627cfa904d10f4c7ac00e6a329a66cd10aab4a0e3b7999811773da3b38f4f84a82c77af8856d5dac77d7dec0abf161f21d3258ee4fa2d05d16141274664794babedcb391752531bcad77f88274e34cc07d8bd0a8e01ff196bc8cb361f0088150d51e2e336491e9b3529bdd9176f013fdf104c1ce801d6fbd19031c9f94a28b81bce041d99eac1fed4481ccd6e900ab7d157e9e921c4f4f99c0b9cc9e547276f4879f91f073599cdbc2b431d8f1b7124f949ca888cd0c54c5fb7b42af5af74df5e2de0917d7d18e26d0f5af51dea6eb2e2464f295be92ae820c9a0e53586109be5337d0f3767c86f6cbc220c256b802aaaacc757ff4aa3314ed57ce73425707c1c0e3723d27884c0fb4a023478d9fff0bf0840c838aadd09d115a35da765575402b9d039a8abe0ba75931c775f659291bb893b562f670dc48bc8921727dbd4767ca55a262046765503694cac94c8c62bd432ad1d7d6aaea0d9e8b32d3931250968e782e7728bca045b6a81dbbd18a39ac4f2a747384f817c9a9ba975ae5817a76a028b79d9fe7453d74b883233906dd01fb67dfcea4c0215fa28fb14cfbfd49f365b144779c74aed037146f45289eff7211a6af05d0feb024c4ab31bc84bd347f9b05a65b5cc37c7a31421077e9997c97c7f7f3e5c4575d6b4a05c6a5cf54bd1f55012e91d1e7242e2d430a54670473547dad97ac5a1aba67806b1c42f869d605220730ad8518e2ac3809bee5bb80c104b3e6dc2ab8c780ea59f9cec4bd3169944e9ddbed0994de700c24198793ea21287faa5b097d28c563853f53163307119453b99e5ea8460679942f7b0ceafa6f2986ba1abc87ac1c94ef961ee8d883e47fce6408393ebe67ea4d57462b9e6de2688f23c0c113f5c1e610ad7bde17b4b694bdc12b3c4270013b2ac60d260ac1a32d50675d5d4c1ba3813b95c26ed8ad3cf0201882109901448eb1e7e6b20ad2783cbe53ab0f91783b146fb0bc5f7995ea5a51b7a2ee205ac027cad6246ef5be805075a759e039e45e31fef16266edcb9cc270edc7f941f8c5e3fb480129464ad7ad63ec97ae83d11177d7f77f1bc360ade296dce432e5dcc15be68244fcdd664e2d3fdab7f95569a1050fa11e218bd17171e25cfcb4635a66b6ebe8281f311f621271ecabed36f5515886a8bdcb63dd3c7a3d2490fab301bbe8c5ad886ebd88c1fa99cbd474cb02ff86583d4cdd355e712311370b3fa29b80869239b043e98e00e53c27de4969f9794b56250fa456575b2b3d73109f1ad6da02803aeead351c8a009c3d26aefd0d8ab9f72de95fb8afa2c329ce3b49e29e9b3116f1675055b60ac525a1988069e12a00644456755f39bc672d99bf8c860bc3a65dc94bd7a16fac1db1e25b6a0b480a6d0b535df9fd27da8054c23dfa66e1b6a14f9a0c3989db42c759554bc30d63d65172f02e67dd9cf1e73ebe6e8024a85cfa317489d0844a619339a95d4e964ac191c4664890bd85be143a879f471b93ef93b24dba954bafd4420e62c9d432227d8100805611bc2094faf1e866c8aa88a8bdf73f2fc90171a252c2115a015b2f5df1929ca217f4bab3ba5c5e6e3b580749c5211240a8b5a3e079e8247f5e51bb59d76ab7ce9600a00aea0e1525ae8f11293908a174a656d34eddf40740043a2394b1c7b60a9834dbec832238036ae826d174cfe523021304e6590651faa3540b4b8b5dda1016aab4ab8774cff498cb0399e3abd132f86b0028dcead61d7340b9071366d0623b12152cdefe1f50d1cf2a8bfd0ad81b24195c382f7025f8a73fc46b227b29887e4b81625bfc3f1907b7656ef662f8763e579da99438cc8c2edab6e47954072428370dafd190523fd74d444c3d85f87d7a1ccfd1934c618ab9f78bab29afc4312c8c490a3364ab055f846a17d28b68961adc68b32272d964e04ce89fc2ae57b0425fd9e07e21818fb78ce70c2023f2d1e8e2c6c92934cb4472d6ae775935dbded7804fd6a6045b3642745208c459dded34f1de1305dd50c4aea9b1d2bf52a9ed1a1580b8b01e1e2b2a945b935693157d0e2f31207f36ac19a17f2ae391149af16874a990b6d492d7a40832642c3a12992b520e61b8a071029d18d1523621eef447bfbba33a86b47f27742c0d6d1b0b588dd9e8832ad7d94cae37de1a6b4a6662fc9aa65e1482b2e6ac75c76c05c8083e0021f17e44f88a77021c60cd8dfae30a699f9d4b950b53461502186e5dfa340b4160dc4396770844436d396043e5bad90fa8f86cd44c8296094f55b0281d3331a8544f341062a81802eb5aea56e37720a38de6b29ce7f89517099b9291012d0067c8f4f8936e71469cfd282796d203649e3f4207000261aab48b6df1a4356e8545e3098824b39cf97460acf0adecdcbda82ddad0a5f1373fd118770c7a64f21f3232a8407ebb48abe7f728f4117340a1ac15b8db92b84441a2bac138a30a881c0a84d78675fe9e4408c16b8d8c3a3d177726fa06d409d2d2e92a170fb578d16d20361b0e010009b8616377570b674bb3fe7af98c38bfb0e9012960a89fd3c3cd4f031554d6b6521a4b99907db4ecc0f0801b89fcc6e22dd3a36c5e4de1717bd4009d9ff515f49617b7a9e14f80a7980c3860c5909186eafa90e2afef4aa035917eae81e10a6b35bd2e8eaed22d0a3ae328fc97b90977f6beadb030554f25a2be64d9e62d3fdd8ad9ccadea33f3d5a7878ff2385fcd60c41cfd100d091e3e05c34a2d8619c5614f49ccc91e94191b2082af6cf5c0afbbf7d73831e6f8a77b450b0b92e36c56c5650b09ecba289e566a432fd332cf60e2e6c8405b60e10ff8e69f878b0c21a66056b99a10e18c4282dea0dec6a3e0138f91d8138c9b1d8c76c094ac4f9051b1f48e132428087f94589dd8c3b058b49085d65b22e70aeae8ba92e54374cc68e25dc229b243fb4502feefd2788b833f8120fd5cc34ae85c6142941057f8f7a3307a5e0c29245733049f8d5495e62db3c16de3ef3a02e650bc8304a7a465cfff1c87da88fb035c755a1233c48763b62dbbb8f246dea673acd3009f0feaa39d1cdc630136319cb508370ae23a5cce045e011301f81b7712d7594c320994670804a6b51ea9a10c2291efabda6994bf96ca551efc84db8685f0990eb1a25c6f9c9b426cdfec465a8c3ef2cb32184268f62d8cbe11dd952df7c6a8b40c3219f4b4f7265b491425ebc2eeac0f3064b788adbdc9a50479acf0bcf2a034fb9bed3747133b6375f42cd50d065168bc00d0e8fd59e2d58937301ea52dbf8fd0f94c075b065dd5297d25b5a890d20970781b010267043210bccc05e22c51b1f4ff1c293a4d7adefa3325787253a4aaa68e13aafcfb7571ec426ad70f3231d0f728d07ea52ef6048fbdddc63afc2b1e67307fdb0c4c8ca8f23e25a764804940af9960fc9d70cd78f6d6716ade4660ff0eeba41cf14a3d29ff8ccb6ce62b2a14669af9395be229ec418132eaf93613f30853fa73831ca67f1d0bc3985e83a36032bfcb5d789c1ce334de9c4ac9f19269029d9134770e05683b9f46be21bc43601891d138fced33c9f18c58f4b9c58eab378505e66927e6ce86ed6078799b7f0c8a39ce284363afec0a9cd199f5336e8c0f125112bf1cad2a92937ebae467286fd3548effd4de1c928a801ed72eb2ed05d41afed2c386d051405bbf8d14331c861a7e550f0f787aca824a2816dfb02cd5ed129c022ee3b5827e1f3024351534cc4f9e248add0ef6b5a0a240b12d842aceedf19511b007747faafe556443f89504578090e3acfed6f68456bcb9940300e89859636477d7f341b929db802fbfb9a7611e3a3ccfef223d1efcef67bb0028ef4e39ab62a375cdc469a36c19120257798c30b28970049004d863700cd47422677268a649410d05d5aca290952a322474c158ab484d78d694be0369204cf6b46b9668d35405eaf5a1ab620afb4c64e603297b1c86c94f41a4ff501d0ccdfe2611a7e4f9df251d0695e965a62fbccbcf26a047fff86e87ca90bdb4eefd36140a1064bbc5ddffc9d169589bd353d3dcdced3c9ee881a5badb08709b468033a8afe7e5a301856a503753c781c664c75ff01cb20d1ba45e6af75bab148d8a3ec4a8a2ff2a615efcbb8b4ff264e82868e87b06ee4dc8fc8e9e1c6fc42ee780cdc7bd95c2c681f8e06235bd88b937568c44608919becbdc9965b4a99524a0109089107df0741fed324073eebbbbbee04dd3702db6ce24dc5b8bb72f74201a276f717a46e1b87b1d999bda9248850a0f8cab27118fe8975dfa43465d9546e1b7eacd404092a6b4054fe2f42d16cb86ce78b9c9b128f0c40b56c7c95456918fea19b29389599392b63a732c9c6e2f12e0b9201ddff4836bc0e77b3b1866c0ea177a73541b7c256353b11d4aa948c0fe195f154525ff4941eaafb1da53b284ac3f40fcd29f48b14958f65c8217288daffc9f81030784be52e06b9c815ec4cbf2c6273f0b6c011beef8c6ca916e7431aa6dd46e2d4fecf8d149139b2838ee918a1bf06140a45b0ef5202daffc1769e4ad319701b6c4d64d015829228f5faaf17890421694a7bcd9034a58b9a280cfab5511bc59db9f1d5caf0f5ba1ed37440926601213519ccfec216cb5645dac51efed474c0d674cc275d1756bab6ebbaaeeb9a521a5139320243c861fa49c3185139320223e338c7614e6ea30d994f9605cae38ba9cccc3a0ce1692c8e95f63a65ca822ed01353ea97d1d899ada99de127846a8aa980b9b8c0490c867079814f7797efefd79437a420f7ff362605b9e3e0cc4c3dd0b307a8cd309be71403b2acd76c31a2159558f9fe70ab41bcd919ebf9230e2fa02660bd860c90249f9d4aaa8244026a8f93442c4cc3f4f82af6380c50abe44d7b55ac4aa260929ab02429d4fed873fdb79182e6cd356f7cd58b79266f5ad591885d91bea50b6a3f495201b364c9eaae1f2c5931525b9101f2260055dedcc89be632b00151bb65b7d9908216c20d9a40d8dd857199c2ed392ddf87de64f03a934179b1d9aadd5df668b338f663985712c680d5b531683f8410328410465e8710420821c39501927a98996d9c3bc8c37501630c234689614481ddbabba3200c27d86c06383601630779b868d34d369e14069999a704ba7ae73576746260a196841647ff2402edb8393ab4918a3a9557a424a41dd81e6967a3736f3c5644e80e53eeeeee717677775f777737135254ac78a2abfbb3f3829dcb8a256aaa76107fca06d68022bcf0ed1301d46fa3ec1417eab754aad721d5fdf7e322d585fc3f3ee284a7fa11aaff042a41f5a74aa4808aa8fe0f34a5fa7b532851fd63a6a052fd65689a54ff1d45a8fe434517d5bf070e25a8fe39c0a0fa7ba055fb538516d5bf079b9b22d59f715ab53f4f8a5ab53f4388a2fafe58e1829dda3d063d7510e815f420dc100189138ad8e00761b420074b28b90c4ac8c1902934410c2b889a053908a37fae1bc19ac24fa27df9224e7c1925375caffd7e8f3da971eafcaf118bf919e7225f480d8973915de430f385d4d8402e94d8d46c8d12fef930f395dcd0419404a145314a6eb882f45f9d920fc9f515e028e8800a9bebfaabfb21fe28c1448d0f3fd874fff5f3270b681f39ccfc88c58c383009d8becc27143ec639388c4b632f3f08e9af17c2e3e2482fa426884bcbbfe1924fea82bca766153476a69aad99dddcdf997d9a43a402859db7ff60ccdcdb7d1ba046d8a8fbce334cc8ebedefc22b305a8411941f6a5530824b50fe2821f4ceb6e3e9ae09517e676fdeb8c54ab9eeee2bd9a173f0594a68c918533990b8e9816bbab4208e434beec0a5058fcc39a57487ded5b2a49412f27bb424ecc1bac718638c11faeadad12b6c955a18ebf7d7bb619b140e8b713435e5f35fd3ea61d2c000819e92338413e54911336bed7cc9c13dc03953524310e8550f3230c4548fc4d1703c64782023990757437b37344cf752c9f9223565479cc1ecb9d282ec3e60cd2b6766500da0dd19990e0396ecb86b7468f50644cc8f9324ba064e88f081139b1ff0643197a862db42e6a745a85caafce2c5c8484144addf36a9f05ba105faf649dda222a042205c3b00b99d7a80ba2a49c614415cd426aa8e44155180428a57ea522a423841006a7ffc620a016a2f81aefc3489c2293540ff0bdddd34b5d21f7d52bf959f292a344085f06918152ee9c126bbd795fabd20a07e376a4bed9f4a002106b53f0546ed9f20fcd4fe8101104a50bb2d2ae8a470ce981839a965594aa059dd1f288e78fa4b20d1688aaea488708872360ac59315645bf094a0d0e2084c09411a1447489074761a662b6dc11157163e180e8ccc241891aa286249b9a2d0e208c905d18ce2e80591c8322206904848e200091fc2894f8750c47b2f9d67035f4a114e82ff8fcf148afc4be1992c5ec4804159a8fbb30414474b6451bf37c21261d0808814473f5228b9111380ba3f48caa8b0ee8f144c3421dac1309e227e416dae00156b20d4e6b8ba5234c1456d2e01d505fefa0c21415d9f20a4509b73819d565115db9525171aea2704874be14491da5c4be54c0bba92fa56c50e08571dfddeefd21dc79106b4dd69903be9a448c1420a0d9a90228512b172ee34f8cbf10069d820ec7e6c75bfad523f776e6b2f6008d8be7c33702afcdddd088fb41724c60b9660138b76e66567c05895fb2a7e92f63806390c7c5413287c62881a6351905192a32540adea22157eec69d5fe3019aa2baf81df4add2f52a97005425ebf8f41be8a5d4988f214c187b06849abbc8b44d20942f108414f08090911050d4d8912d4da0efda24e4f9238d4e3ab6854e453a1b481d167e117390cbca191a83df8de45a1f620f501ed8f3cadf2aedf6b4161e558d4aa58e1c72fe047a356a52a7c99d3aa23bdda284636363640d4c08f54a0b8a97088bca990880995fa499c0a23cf0c76ea278d54f8dc9ea4fa0855f8d0880a7fbfd8a442a1566d93214451e1c7a156b96043dcd16498deb9b875846e85f0639256e9c0af3103471b82c9f8db7d701742f88b835fab025d2db0d9aaddf7e8ec871d842d847fe86e1460bb2be8bfdd36e4b8a903bacf8c045e5ed63a730c92e225a7654dd901bf32a0a4df658ed4dcdc40b043df19afd7b4e0bce637fff23199bbe6357dcc79d1581fe7b4ba7b798d4e29801042082184d0bd1b322fd3dd0714847143bf5fbd1093f104030929a594314218a594524a29a50c425a2fa594524a29a594525a5206017e319577b79bbb6c65677a4e4a5329b8db90959d99b12b0a2c0c7c08340c7c08030c0b58000e1c0318000000f065372c0ef834edc12f41a16c03a1c0f6e58b41bd1aa2c4e7ae8ceaf2d17e7d53f7378b290215ef7e79b1fe732337da197630b6555ba58f512504a24a284495df0d8b7b79f96fcd122222c121553ef64ed41d51953646befe2ed6c3f7ae8e8a9751e593b2ce8dda93bf033fe1da55dfbc6c9d2abbaf736427fde8b28134fc1560b9fcbef1951b398cfc54ca8d6eaa57fd8233f3979a0ed774ccabbbfe73a318f8d28d38c7f5f27d0a3be32fdfc358176357db6bff6d5f0b0b75fec782fc8db316663e89f3f5660bf34b2d130746ca2657e3afcbfdf29eef1ec3d7cb3d066938d8be14614aaa4d597bf3bfd40e1a2918162e5ce8f35d422697b507e1d2581373d6dec74f416805141d1c2bb02fc6e8233e8451beec7cc468452bc6ef193a426eeeee9d39abb9d8b23fb698fb699c3ff956bfe497dc26448552c686d6126c4f26d3b69d7edbb66ddbde74dab69369dbb66d336d26d366da28b7f21bc7d5643299b66ddbb66ddb4e9bc9743a6da793c9643a9d4ca69369336da68d9b6f42bd693b9db6a7ef6fc346e9e944b73f3d464f277a426da7cdb46dade9d860b6c27d5da9fb6ebfc2b13caae5b46262316d6599f1acb1cc303d663a6ddb76da36d3b6a1b8b6ac709d33638669db9ea64ddbb6994edb763299b66ddbb6b7341ddb763235caf4a895d3cac9d4f9d8def4a8eedbfea4cdb0bde9b16d333d4d9fb868dab687db531d915be9962a7894202857ae94503a0d2768f7b78fe92f1f7236b8bb5b7066346e4059a8632cf51262943e3ea9f1b5a1fa55fe4c6bbf34943e6a557bcce27898f587f7a8d737b74303614c7e2f7fcaf7313baecaee7d33d5f8a4f8754a29255cf9d52bd78d6b1dbcba6dd897b0cffe2bfd72d897b0f7512a71259c5267596f75d2fae52e19504dd3ba6d6612097273426f20489492a878a9fb53049d8a53b76e4502931e17a0c104f57730284dab5cfaabd7f8d6fc786192fb845409dbc3b2e69e6b0fe5ea5a0c5cd74d08baff2fa4aefb60e581bd4a15ccdad9a54f488519d78f715fb7293e480f84fb4a3e4a3f7fb3e29c5fe2e2fc1f5c4941fc91748432eaf73c9e4af57fe9989f19e8c48c26eb8f9c0ff940b8c6eba18febe3dfd899f9fe3876c67a7fcce27ec0bfb86983f531be65bd84b3bda9e988eb3d79630942a215237494ba55a855ec46acb8c8216f8ec6e116fafd978738d6747051e3700ede16fa9b368eb393daeeeece3e0bd3efce3fbb825915f587ca28bb8c138242ad82dd76f65f572c488dbf9b83a7700bfe58f785a007f679ea6f65cb39b66e93d666f0f1d5364f21bdb74f8d7d329e523db60b3ad25e6d4de32ccccecc1a57b05fc3a0fa631d17fdd0c6718edb25b9035f5cc4596d0f762cc418e4752f7d3f706f7c45b1189f9e545450457d4e54513c5451a8d3f3101ba138eee88f3da5bc276210994ea747991e55049b3ab399f97f2bf2a6ba8a65158d3c52c1b0d5744c2850d314a0d37b8a0635edbc50dd179f4800d5c5285d04a2c1437d00e14a8387fa383d10aea8a781033f722a190d1c645293213efc14ea5145280a25053742a1bac8a43d9f5a0c16aafb7460a38a7a2a646a3a505dca098d455d246acfffb388e214ab68674e278eabe585bf0d26d3e96411b5caf4fe25cc743a994ea7ae862850d47f91a8ba51dd191437494ee8ca9f229556a1de3f1eed8ce9fd3113c7358eb1333fd808f533dedf06d30ceea4cd807a5317350d9b61e26eb4e7a6199c7cd3c7a1f6fc511c0f5c4d5d0ded9db8ad510885e222188bc36871408112c1a86e453d9665344c15755514ea79575b837ad674a07ee3beaedba93d54d74f378ee521477f35d4d374145a79ffe813af8b248de8178dba3a0b2c548b76ce5a44ad6a797f6b4aab4eef2c276e6b0b072bcbb3d6c2d2e9e0d5d363278e56cc94c156a17ebf8d50a847f9a31e73e6768fa595953f715c53d8cfe03aa73d7fd650d148daf86a6ba6a9fb527fea3ecfaa2b9d4f69cf9f0614f4f42bef46ad9af14ee430fea41ada67535a6555b7886674d1a83d7f8ba8f46e3d819e3a1fa83ffd4af7a1def4a78f812bc619ea043ba845a36fa57ede3946353ea9feb373a2d10e71ff656768ca326a7182f9631e5d69b781697869cfc68ca954730f511bc231b027adb04fb55149b465f82abf6797755e7b730af47b1e7e6c951fc3b8cc6360d871030e3af43004c7c8ce129f2742256e8892b6587e775f57493f6d78696fc5a353a01e7f0719b6ce97812bec9a919e7f9090a4b60dfdd8b746830f95d4dd4b7b3c482418c1e0dab03dff60cf85c5e19ae53bc375dbdb3996e04bee85f67ca81076318b63bba1dfd717a30b09e6d2e282b07eee5abfd57a082deb97eb8ee37e9ab6e23707bb5f77a12e03ea3edb30511103b670018bba308bd303ce6120ec143828b130fed0d7a8ba3f7756d783eace1deb6c36b4ffd321abd810ba55479e20c8680a2836b4eecf155390a88040ef80dac38770a59630928665dc56d2757dff0bf74121db9ee5223bdade4ab148cc59f13901b62fcbbbd09b37923821b2c4870816f5db285f4c6d2743982c64cc21dcc99ce362ef12af65daf65c62d0ea041a02aa0b34a453a755039ab330fc3ddc4b2cd38ea1bf428d1dca0945d575328449a5699ac6894522518c82d99082c69f32c4a71d6c18a7bde21ad2ecd63f3557da6324d7c7ab830d136756630cfdf4b3a183ee60cd42500827fc8e12c299419733ebd93f5c9451b9ba3f5ca0a07631441540dd9f2d8a6abf106c865406d415828d4e17424149845af5357701d4358d0473f145f5a028ed44adda1f2e802a4fa9fc30a8f21605fdfceba020bef92890fffa354fd1624ed08ff2a8b643229f1f0b6a8fa3b4470225fbf25d68e6c0f4e2a83aacac14f1333333ffc73a3753e040a9cc58e854d6591c53a03b13b9e722bd92cff0a3340ce77490070539d13af4710faa7b543d08064d1d6c5f3c1e75dfff47fcaf87ba3cd078c896655996d5fd901fbb6fef0282fee0aedbee47fc8fe7c3f8fced4eee7ec80ec9fcd8c118dabbf308b49e856be1ad126b4d876ce1826061618131fe1504faad516dbaa261b2f1fd2bdfcf636750df5f4bc57e17c3fe7d95ea66f7d196da52bf67e15256e65afcd67420a161faf95cea22b075763fe67fa5544b8a1341cbcfe8be59677ce9a1a6834709b8a6c6d76869e18208f243959a962722a4a6a523e2f235ba5d98afd4fd98cf7f7a1a9ff11c929647b59c3ae690d87817ee8720b64acd0d8d1a1b9d10a9af91aab14452359e48ea6d3c1197aee589d8e8520eb3eff2342e5d8d69e26e6453a3dd6f89bb8f476df9ee4ba51e6be158525c102c1f44480d0b1744cb071152d3f22d2dcf1ca9fba624fd673e0ba5f6b3c897175763d9446b2c9bb2f6b883b0bd09db9b93a6776441347f7f8286d4a963da54e66e471644ed6d7db9d11e5b42563abb42e51cd2a6ee0ba9f995d4ae8af4dffc0b9b5c363f9b19336733fb2bcbb2ec4951d39149807faaf8bfebb18c8315bbb8ebadeb693a55b3cccabaefea30eb5ac11aebb2b419e6b32579f0a893089a4387437bf2e912b4ff5ba2b8026c07f6a161dffaae5e315efab3467a35e3734eabbe87afe6a43495fa174a59bca4bda8d35e3cd23011872493ce14fd94f731de0ac5a46a4cd5eac5d4e853d3c361e21341b9a7639ef6e2d32528ffc73cf108296880ba3f5374b045090aa0829a5501c21b757fa66851a9138e6871c1670a51dccc2929a84e570650f7a78ba24aaafb4344543ffa31c000dd5fe9cc5064a68ce71c23c5dddd9da116eeeeeedc5cfb99c8ddddddb9a5a49cd7076af1123f2ac1dddd9bc8dddd5b0b7777776e227777e777e796e2eeeececdae04cf8301061b2f5318b1840e3686803810cfc21b074b56e032329247278809c9230548b74d67017b485289c5ca0a0e3b69c29088731971840a23293133623c6f8887584465e0f09015c2a0f8204748ca101b41191421041b4949c5d450e4999167a55540e0fac02c483e913f0a9058231659ccb2d8267033c10f17dba47366141540a9fc52da9b1d461d5e869c3bdb8526601853fb617785bb10e60d60fd5873bd9a3710776682da40a061fa9f0c6d55680e329d8c8c8ccc94faedcd8eeee721fa65fa61e01cfa0757a948e077ebeeee5e3c827c8ce510f202bd926918f92b9550bf05aa511a46befc6f9bf8ca7af92048a24a7641954c44956c822ab70a69ce8bd2994a753bbc73b3434ae92c7d81a2c897e11ee9822a7f87164faafc1a606815aa55fba345922a9f93f48a6b24b6cd5dfa465099887b2a01e3b94750ac3959331aef6b90b2a4f2bfd02bae41adb8268bd5093374d820161696de75c185468d16ee13c22384e7f49f0e1b248447080f0b0b0bcbb3bcf5980d1b7e351e00789a66e158de46044057c3ba807a966e1d263e9401f5158b0d1bcfc26223f52c2e3452445a9e488d1a2d1f9fc657b8ef37e8c47d3a6c904b0d2e4524d52284878565069c33380ab3dda02a3ffe42ba7209a16d12c2f6653ae0e66b300760bf9f0d5132fd598bcf31a331b31105bd9eabd4cf7ad2f78eabfb313fab4382fda004be92869ccbacb04a2921f709a91db02a10aff085d41f5ca54a952a6f8445ec870fed43f28392f94a305ecdd274740cd63b841042e85f2291fe47d6f140a3525246ca1e3e89bacc68a9917a2234fa59fc377bd28ef52f7d3c6aa99b97f694fb7854aa3d3627a5a9d437aaebc7b84a353d46ea3e5849f033ce2bec0adb7fce2ecb485ffafeacfb484f7990381e75b518b84a25659d411cb0fbfab1c6fe87f55f0f55eb78a0513309ccfac3fa0ff820bdf63e48cfeddef8bafb61fd077fc6633c60f7c37af8d8d70f9b9b7577613ed263a4ee47d62111c2e55d66b87442a46a88b4fc8c1a349522c2727a1aa80f0839692f03dbd45d1ca6eed3ba7e0cfb1a1c0d2ee530fe298e484b17849484123a3882055154a153b31de53e1e15fb391ffbc5d65741866c9528b49082186458e10a556a667c105c7363d51011520331d27f6b3a48dd0cf37b3ae46cb5f21a97717485d4ad70930414be0b0da4ef2775fdb34e1b78e07a752e33cc6abd0e56e5c1ebec2ca12fa2d04d8d31b604a4ec7e6bda04e3875c10f023fcadf09921e7dd2e84990fe6f29304b457496a502a954aa552a964838dbe20131f3371597bd99f38da5e06dbcbb40fd6ad2b69d90909eb999f44a2c187ca2f3b48205c9df3c7be1fc219d34fb3ede6b39bcf6e3e3095a2b4bbf935f2fc1b7371c4273141ade79ac5e4c0819d6161ceebe3c797d13e6b4f7b1347dbd3607b5ae9276c595beec0759b57d5bacccaaa9401eb207ffcddcdeca39fdffa56b5c541c8cc1bfeb3c5dd3983ded16835e382ce198dec1439c4680111ff06a59a14f48bda44299a4afd7b5e4c8c8c4505fde24e44224229258e058316c4a5344553838d956df23720a510e2a0461c741bb1c6dda4d2d429a531f20821c6b8a9973963b828b91f1c353965ceee93ccbbcccc5146beaea0f1af2ba8114afaae59994aa5aeeb88e58322ae2b9ef48429764e8c9da30314e5888f919e273bbcbb393939399cc3394073523a314f88faef720ed7ccd8e518d9192576657deba898b0baced92ae41cb6591c40e020a942d47e1c1c86d36a3c02d1b6123a4c34c1c3399073f688856967258038a773765a1593b40ae6e4e44869d28eeceedcb98f957c2023d4aa9473ced91e94cf75e508a197646a5aa9eb8a385c2c92138de830e19155c418659447f108c8f3de2dcbb22c8f4632c628b985070a431d65a9a755328925301ef9eabf7864e47107059beab555a59e129356b58e515d35114a3d13ca21918c6b8ccd515ac22d1c5957a04b38c76684afd21208af9f2830a98ce1f4b4aa757680f5db74b69d281ca9fd5b92de76e490aa63d3d98e4c3944dac43238239c2346321647bf6f466a3315276c2a56467b2d6de211152780a8dc6155d031dc697686b198420b82b24fdaeb2ca8f65b41d01d60853433718cc5d1f20444a8fd2b494045ed5f5ec52a304cffb74188475c50d247a33ed22a77da4644bf524f8d47a51e2ff9f84ad37a4a3d380ed33f27a55438ad270afdb49eda40edf5507b5c6222042df9947a30a1da4f7be8970101f92a96118f32a07814817c85613c47d887f160516aff50abb6623c56603c4d24c1fd48a042e330f323f758f7e7094be4ce925ecdd89f2f96d4ce80726a7bd013aaa89fd4a9adf384a28e9c013d6955270161943262ebeede41663935f92f533ac2fab44603c4f1553c6a98fe39319c8e98131446a32a81f5997db4c234038a4737d81047ddc4a1316a41a5910bf5b0ac1c232f40603a73521908ac6430a45ee6b4a040617b1e638c91351d14093aa5d429da58407d244bc9ccccccccccd2a7539f9ec7c4380634e77f0f32822ae949f2594a29e5f498181fac87f67cd5355be3e3b2335b630e1b6915e9fbab979c3fcadfb8127759eccc92f417d7437bcda4c8364594e912da437998bea5a9e463bf9bc39f44395e18fec82d002abfa451e234ee9bd5fa52d546b5fee2be00d4097b10b2333c501f3687ec7808b233cef93367d52d3d4dfbe3b0335d8fd08f6f64e49b1bcb2a7151c7617cb22e8d61e2d9ee69d24586e89d66d239bd3b76cc09f49fd3cc3a3b47a4fab1ce0e1ba020dc848d0d51cd0fd9098c6aac4f7511db383be3be9adf4cd46e258e703ef2e123afa117d08f5d17f3a53137e7eccc7603ede7ed0c0d33c831c3b6d05f636749417b7a98b4eae3a37ad329aff39e35d4db85bb31e88bf5df35a080f2d8d14be8c742dcb338fab9070bd39f4a994c423c6b0fd15ef73c6bb28b813f461c17d0af6f6c70f11bed7996931dd4af735e883131323fa0dcd1f40c0bd3efd3b345edc7d1df37564eab8cb4cafba66f622c62e239a9406ed1e6e001203af04df72fcc01c25ead420e83907ffa8a03b3b9eb826e515d987e9f09a5359ac5d14f93a0df16c1325ae5454539c4286d24cfc927c6e8d11fbbaea01e638c5106ad57d7efe6e3abab9bddd6454cab42b12ad4e5aa59fde6ce68b50095bbcda73d18d0a8c4aa624f7bf107476a7f14b51f06ed0c7ca902257646bef544ed7f1293f67eb02a5214ebf517a3c08c50c19701e109eb25c74f62d2aa08c47a51ec10ff0f33c282f2535d89daff6146b8cee2d881ebf740f8c511e405fdb62a556e3eb57ff3e920cfbacde2f825ff7ff2211535f4627a6ce0851b40433290d7f376777727105f95806b94c8d095bb7d2a03fa79b18789e77945601576373ef15e88a994fb6e2aa0bb38fc8be4448662d06bafab47ab7a001758debd3020ecb32f719be37a1e0450f7b1151e66c5ba0bb80dd53499d6eb5f21affd1ad855e9b56f1a3d768686b76378515b6747c6f990957efb96d1baaecbbb4ad9f6a5acf45bf6da57207badab40e9b76ec7b4f1617bcdf4f4fb314b7657ac338b0c1f94dc40dff43e68bf7d7f0f5ff940dff42efc376caffd0da6a7cf9a0b7713d85efb0a6c5d054c1ded64b2a31e3f90c94adf8f65dcd6c8d094b8adc9385f6d9a0b8f204a6ec8bef441b6aede50ea8268dda698fb05c0c2aae27ff1c8242d89bd64fd0ad5a2565daf9abb3d5a45baa66544a73d263e36a4d64c36b5dfb2588889faa655bb9a445378888f7a082eb2fedf573d1ca67f4e4affa5a0f2b3848ac1ec7dd531319e979adc0959d66b9551d57a2c1ea9fcd65babf2b772ecb610bb2f1e515725a5b4acc9fc9ce3f28f3db866655147ec2663b1ab1fc0036abf500a0cea8500761e802aa0768edafbb33328a0bf03d78f79aceea3951641bf3deaa18707305e762fd99002670bcf8bda60d416a35a292fe897e216a61f867118b0f33409fab1902c925fb44ab2100bcd197162919cc56144c764b369f1844f504c8c0c0e3ac0f042904f8c89e1d72a33c7743b81c8094238adc56efbe7739759736a37044180518df5d38ba3fac5785184a69b609d7298b6a8e0757f7640e5e57a69da31b4c30323348c5dd0cfabdd95394d078d01fd627c9ac4c4c4c4a04bc9e6135331eea77ec70d2f3664e2c65475a9fdbf335dfb27b58ce818638caf73c35aecb6bf357a847e29984aa5a4c42992b3f9c82b567c36e6b4aa23503c127d624f7c1277e2128901b5949de9b4b7757fa3019d316767b48d468ad48e455a25634e7b3a54a014094ada212569558c3931c7b2224e9f5ca8142d7775450d11cd00000000c314002028100c07842291503c9ee8baf20314800c879846724e9949e324c9711432c618428c21c010024004406088860800b17ccc0f9967cba0b8be26a7e1c0c86b0fd9938b95c77bf289c01d1f623c61996d1ef4fea49764f0c2b049a02c78c15122aa34838ea6fb615429d635e75ed5753030ad8a59a4522b24a1fc86b48a399eaa3259231cf1db1c037c5c098829761b2745f8b61669be1b81f9ce0d544cc9e14f5d5b951e3b710f0377732bf956fd6d112bab45646c179e17ce52a5430a3a7de3841c2611ee1522d009c0392dc4a1455c9182403be85fdadb3feeb9984c549b26d5936a64a49d66fb547d5bac0cde18864044ab5dc0f2258cf8904ba549b2c13aaa2144bde86bb6ad78e66be379cea874e7a047bd9d8a43f0c8c9e519ac44135c0027dfea9142627dba5acf79b5e18b799febb6c8b00605e2a14a11e3f405dd2af6b6609de284bec7c70a72c4ee1dd0f7d860e59527b06900eb8ecd3f20f861088d0403c989203be1d83c85002544600e2282e2cf2bf4599e08e52dd7ace1e439b111b626ce1aa66ec44d4cb26b08aabc113b09709cf6fd771ba079477abca084b366c60e30d088cbd37519797e135874099aca06606bc1981535ce5f51fa48cc1e3f54012338cdb386b26b40f39258bf1144a2858f610aaf72ccc9bfa6554bca2085132280ea71dc89270da279cf75e80449a3df6acc2490a3aea297685ca49e02591bd55858a925ad400c494066d90aeace08eb5ee1340bed2466250de91e0de74e31e16b1f9467b96f546403bf022885fcf45489a23d9064264666db57839b06a8a8454567f251bcb9350915f729dda1ba650e64326fa9a3b300a53f98cba9c7fbf4b66d5bf35fc8ce6e19897e1f56fe21a04ff62cce5ae1e479c8a7fa9b414eec877caa37b39e8b9eff3b94babeea8bd5cf31d3777be35f059ba0dc24bf162ee526580046374471fe4598d4d27ab380795a483a2b8ba28ed21f96c2cc1a55e31a2566e9ff039519c85fd74e42cff227758992ea2666a51085dce7f1ca77eac16eb6380fde72f0d2f9e691420ddc45d044370c9ca8d340169e243fedd3b3acee9ebfbeffa4cefd9e3cb5990f222d0c39b5bbcf7f9ca87fe6f46c322097562792396e65acbdbc8e2242dcca334d8f898be7a46d20e507ef68c2929764d131420910f6c39a49b7863815f751eca0a4d0ac89112d0b17af00a13209abe222b52081e0a2ef6188a4dbd248ea4e709dadd62fa4c94409bb40ca18bcc4c4f0249566d533082ec88d350de8a0b911a28198cf0f20ba2e92251d43dcaee39509ee1564d3d3d7bd8bad806308b75caecab81ca765c31302d0c1a46b5120f834356bc20f39c566da9d60c952f5b46855fefce142063de20b3dcdc0e11809807e580c08a6089f413f49e37b1c1f7bcd2a13285bcc66899605e9387fb122c7da28d00daaf81a12397c3e8548f19a99bb688d2639b7bc4f041523529b67f51193dfd3cca7aa3cf4f691efa6035273a27eee200755a4f836e293297925f15a3bcce3c587bca7bb0d650d30f01a7b42285181ca1add2dca15a84613b725820b38e55b9deda862d133f3f21f8b4a9556d9d2d044b231a11c0975c950cb282205223eb9e5dff27ccf4092effd8939f3efc8d8fca201c0fbe737fea62f1b7508f9fb24ba5a35c10325d3a33e94ce1047055881d124e5346a9ec4f6401dd7d1d403dc263cf4216bd062ec6c884eccf37edc5dfc86146067c428c238abf8a95773a9a1eee70feb69c9ee0204efa4c530ea1b75f082c02d35de47378dc6d749eadfb2525b8924bff3f26b21b892a1914fd58a6a0b9ea50a176b99d4a8fe98b34659819fcf0717104a09b9471b79da8ff47cf62071f22fb9e0d48ee3ed3384c6706f3ab80a9ed9743bdeecb79c35dc3fcc3eb68cf30ebdf6b0f77fdf412ec373328faceab7c57372f979471a2b547f4d8dbd354285f19a107f861ac3f95cf34f5adf871dc007d7a0997d7003a16d089b1a13da9542da07e4bb60ee95949310c912408a61a6c7a04a86f83f8ba5fdc0e82cae08bc3aacb7003db90565b3f73dc7d0e4d3eb58c030434dbf779a495f76f11054e1a36ddfce5e47eb422530f6f42bf0220717305c463cf0996255f804c0ca8347c01ffde4ad09c390d9808388a013147b6d3602b010b9fa36fde7178d53d402ce629b5917fca351d5b31e7e781a4c93948bdd9e16c0967a760cbec54b493e592e9238d0516f36085f1c7a88c254034a9cf7690f392b806ad9d6fc2d1e128ebd841a6f561514aadafe45ef2bc2400b9b660deecbc9aa3d0878c35cb7fda47af1a45e1a0e2ec9c58445bb11caa2c3d4851ed6538ea0bc7cf7238ac087ea1b9a40cbac12f236e974a4dc206ce913d6e2cf9901a79342a923c6ae6d2e062a313f911005055d6a8ba66c445c2e481cc2375219a27af6204a57018ec54b261e8aa7f1fa2ae02a62c0fe02fac7cf4d52205e3dc2f07be122cd970129ee21d2e8077152355343494965df6f41d8036403afbb1967458427cbe18a1746508958886ef51f0ae89de02580e9099a0fb1e9fe407a8c843dcc9369f904a9fb6472e8ca3656094997161db81acc5e66b6927ba19151ed81248416bd0b5fd2fb599a8f14ca338205ee1f740e37605128ee92de9086b683d9fd58873188a7de7fdd3e383f45fb1a0aa8a33be72bad2dee9d35207d2ecd3e106efb5a5acf19e2f4297ca435e90c80eff09e5b7586b81ec1e522e6b99094ed5e1502d25cef9e7ba82cef57119b695f1f08fd5abb7146cd4b109d1aba77484478228af9f14675066c806adb3056c483b2453b1bf237b85abf5f2c3110acee2862bd587ca3af1b9a59c6e3cfec2b8d084c2bf2817b4e660ec9e1cbd3966352e6102bb307ca019bd57c41d772026a4ef90698eae68626419788ca2d2844858415ba6f299bd41128f3245b12b336607b8bd33c76bca2e42528096515df5d19508ad0fa08ba9e4b016539e8fb9c43a1905f3f7e12aac3c76b11814120181f910405a5c42830050aa69830854da9a78da4e017684ee3867a671d65a64b12d435ce64bae0438291fa2979396aa20e25403eaed0db19296cea109943a216623ad490120a1c15dfc5654d06236968dd61c249d5e5b4e0a873ab7e82f9f870cab78d1bb9d53efc1b5619209ad7b93067864c9364b40cf39befbf90cbbc00824d840a56d6a4aefcc0ada183a8e0ca05646f5c8eaf080c0f0da7f3c8a6af7124f7994b8556cb0850c10275aa985987c5ced5a8b74f96e6bfdf2450602de0666496885da3e680d5750a10f74bbee1693c7f716044c205f199a72443299f066114d7f62c830204b398f4c03da6046a822eca590588c31db72722364c570421bd24105b3ed499f9c0d8d69292c9452c1704479d4baa2876197baff7d21b64e4ad00487da538965519a52ca802e88176146d263a5438643ccff0a80a2f0cf615db68e4e21933189bcf55e11c83ed288bc251790471db734fe1f731de1c8a72fcf3c924a62b0b9fb2a23a53dfcabedaf75a3526ad81ab0ea922e5452462053a3450a61ac0823b838a1c4365edca803ec5f8b6dd649b8ef01fbb6d5656461ddcd5e2125686921a7659aef58650f775854bdb160c2dee40ba213cf9cffdf14df2291b6f0c2a975e5ad5cfd82f5d73a4cbc24f5f41c74a107a189e47d27a7d642e80f4c5ac95e132e18c4e1250468d62870538e4ac45407177c5a41623a5fa286e47406f39496e758557ecd947195d65df20e38e6733d3a3245dd2edda78c21189fb29011ce98a341be0cf96353c578f163e7cecb88ff410cd1ee203dd1501a878c8220ebcaa0f878ccf2d4cd3f4a9163a6aa1924bba8c159106e7551ea60b4f5e140ccfb0e3c9edd651fecc938b958e56593e75d5c51c0dc1a518e4260c8209d06d36cfec897aaba997c73e7591892d47f5e860ef2610d8db9e17a51203949a53665eaf41d0ddf7e9061ff6056826c2d0368164ce49e5d67f72740ee19f5a466ca92cccab9372b8c226d129d4e25dcc8dbec0d4f4d5880345858de2850c3a730ca38062e0a61c1bb460c9cbaaf911ee77c4cec6f2c53d5d8707ae8d91f187150bd53156f4050f83096f9c059edc76d00866a5c744224b325eda14414a984c93bc9d2c1613f42ff221b9e6b3860135afe8090bf0729b4ab13b098ba7158ce125d115fa3218a6052b032ef019dbec1453fc2b9dd043e068e3ede390c4d73bff797e7dcdecedd68a4729e0fe0e177348b058622f01a98a8cc83881c34e3d869185cb30fa2e92d35c94a2426209fe9a0a5b1a445094a47e9ec316dfe7cfdb79e72a29ac9263eb30b9d1d77bdf2b5cbd422a33149eca1a7c0a8202022ebd9b643916632e02eee5b1aa6327b9655f556742a7857ee0d7af5943fa182751e1302ac968a9e8724e1ce8a647fd6c15b343b3a1611705e608f98757bd3789e28a375a31aeb0b3b0ed8ad77e3962274928d6f024f53b8c5df2b86613f9fdbad45297b11dfdf446f74a9715bc0e4825162cad1a10460978ad8f10d967fa0bc252ce823b27159c6558495ca83b5a77de993d5ecc65dd4dbd9c148ce59928c77fadcddf20245e928dc18ad601f3acc2d09f3d09f44970784fa57fd384016c4c724cc2c11f5f17fc09fa9f5e3dc7ab5a7bff27c2133d0403f9a61285273a479b17cdaf777da764cc13d3449827292410a662b1e1d9b414021c9376ef9482b44ab7638c31f8570b1e7cabf5ffc333046030452bef14ee37b64208ecc74ba4caf225d1fad206b527622943aaf9a04f2def4ad4846ccc812cf63ca12a9f0270b25bd7d17d853004cf3a1081c3523432845071e391290a0394c4987c74c9111084f6bbdaf3726a21d6df44b08dda93c1fca8be79677284148a84b8a00999c6d0e90ec244f71e795f6d48458feb83b10fd4630cd5e35d24c9199868602ece493eee1d543e91f3d8c9d2eb9e758f744e644fce3c1a4de0d8a2109ff037460d1deb4145a519f2c208bd8098a5030626c2d81126e5fc2b723fcc7d3a76673992e67b59a13e987a48ee0c06180416b74110f4e6e5278315f105c5f333ddc77ca79b365903ca7cf37292da84ae82d293838b21f88a8292ee968142714f408d9aa322c5ad56a8132ecb70e0ddbe642a402e927934dc5f0613482e30b9d305ba8b7d8bab7800504d03a119469977ea098f7e7816592d1849ed7266be4f2b6142348bdef6769252f4d9019e7f506a6527d0ca013597b690b4002626169fed8b821207efe7b0a0c5103406ca89e0c278e8a6b82f349ee544bfe4cb63b261cf1d62ae02dab1c320d2eda3536a1491bb7ab824a2eb03ca83ae7272fb48c11240f47401febf019d9ebf072a2705ee336146bba59037c42e6949d36a513c826cf17e24379dc914696353b83066dcdd73e773e3b7b80aea870124ebf24282802d57aa72d621a7b5726510ceb4e5da00eecd41071e673fe67b97acf2441c3cd76da925565886864737221a835187150f9345ceda9ccab5b07b72527d3697866253556f5399f94eda646362c8aa73df4cf37db1ba5007eda2627ac7bc5b8befdf1009c4245094b58883f9f7536d2d2d3f705ec6eafb764aa685324a401be1e601ba42a419d5501fb08604eea6126aa8c163d794a783866362e4d11da94a10dd8927cf403443c64b44809fef3ca0bdd9405aa60bb30a24249d07ccfa472d7ba1f7b494cab8ae534e2c1a3b53feed70415243811b08898dcffb6874f4333f6e6a6a0b641221a130ad8df904496b1e428614041d33771d3781113125dc88ad01c2559c8c16d294364187dbed67776fcdcaed54053425e00bfffb081b209453bb53ed843034755fd54deccc71d548d6b95d751fbe1fa62de5a38b6701314a51c92aea30a687e972fb39fc33296235db329018e4b6042b98e4cedc574d6bd22da47cbdb765d694238eef90e8ad219afa0aa7054994b49bd7fb1986b22e781c6381c3faf094c99e657a807b097f9a49798b9f59613f9698cdfbe9202f960827971fc20c799e4ca5961ce9b4314f8014cfb1cb27bd7b9affcb9a63cf62dcf432599856ee02ccd17e3701b684f9bcbb586557598a98cc7e0893ac4020ea799850dc92ccf60fe477b1b0c1cc8be9e3ac04f49bc227e3dc89a39d2350911bbe41bf44c6fd814299f480ac425e24553db356e3eccaaf0fa10aa159d1e24abf090a443753ec2122dd3cd9d616ac70c719e961f821ebcd711c50eec740c721aa3f8d87d87d6db366e177ab35b510f1c5b306ebaa5dce93dd33cf95c084dac60463198a434bec8f4a3c4a23dea6737aed603b34a6e2bca036166fde038d7a5d3231dc7774fb81827c53a5dbdfda11487717797e05898614a36bb80eef56f659bbf879d56cae529b384f132170dcf1a0534cb4585364b3f1ef795690eaa1e911792b258a13990463e95d2b22a420723cfd29bc748d605cecff4457d67c522cf1d7f059a88148b61358624413a20008ab739aaf9572eb91d495497d26a1bfa583bbd278baf98cac2ac7535022998847274160131c6557817e96fa5e52fe2f35952fd9de1269625e67f9009e6f86950a76e4ed9d1a3b7d603d0496d05824d2527cbe0b69fb2077bd58d4021875711eb0a0a33dda7cfd53ec294ab282054cd8a1251a96298b2e0fc1b643c48d64022fe03c263f0c16b8094fd17ad6ae4abbbf4ff2693f21fe77df2b72bd8f2d7f30b5281574ef7b12e64fd7b6da2a258cc1c362075507407c5f21c1573b6a5c0a82503ac792b808f1b2d3adb0dde3fcc0b267de96a8d047ec87cbc9b7ed657c404706c0f6de1b57d8c33c69bdd663a4dc09d443bd98d0c7904f28301cfde4d6995c21815840aae1525afa77016501a952b0eb2f941012a1db281e70a63316237e10e97423da274395607e5cfa520c0ff04ae212fab54ffbd760b22896d32966a77b6d474732e9be3eef0189e590e4a78473ce594a8b4bcfaed672b86d32e7f8ed6fc963f388d6e6bdabc203af47651b93ccd50cc1c5802bea15948bd406eab1f8e94fac32c62befa6bad9bb012c8efd2f0f9f10fcc1960e59a275c28309c14a39cfe44a3a79f997b84269d773c5a0674ae5b51213e1a42dd877ecef1bed3bdd946001ead852c199b29da16928ba7a960576a5552d2099a141c65075ed3445ded0cb270b5c0feac302121b89d47b110077654fe3f2a2fd3cb92cc9f8629914d6af4b0c0251504097ac601fdc97aa1e4ac02f0788c70bf5f8af32c0c55e89383ad3e6e7f9d942c43275e0fb92e8ca2c4e180d981c66b02e15df431ea1f93b1113d7701e5463641e5a79281d1e22873107159539d52209020c1f7b24151685440513d60a9e1983a2e408c391832f6a44830471bc5927c1c55c587407235c0f0da71f269b96a7c6ba682b52383f510c8fe062f67caa8d5c7bd769785a829fc12baf50a4f4a9f821e5acbb6a1da07001d58af82a05961419f630f9b3c1a2dd5885f15253a4ec7d39187fc78d5f303a76aa266cd406295b07ea469ebab34ea2d0f07427f05250e6d1d1ec9d7b56756b4dc3228fb5f481e4cb6c4efe526c32f4b724ef5693211340aceda38b162b36fcaefe05ed459d9a167940ac846d7ae16f2a5460c6c774853361f15d8732295ce78f4e41fbd8a6f5847c06f2ed53de8d5c7251f341505dd29f5c029664eb3c166316573d3703f49dadaeac2d3a532e7f194b804201361c82f305760b532f1f9ef26811c9af5331fed375f3e4c8cfad9199841fff44c283185a92435bc605b8960f114c8351059d1bc83956b9e3c324d60b524d790a24ce6eb7f8a36bc6d9906038d0cbb418fa83edef296971b4c4dd46912e93c6517af20ee257b08fda16747cc7449a9a8bea6174172e97fcb7b4053551e4ac22a59d865b6e112f99f9caa9deb4332b6360a354b0ca5aa9985b567050bfc718bd3fe166308919ae5004d89cdc73e102c4635dc0681dfb8ff0c5c816a2eafb1a923fc127d837b681d81a4fc793dc6baeb940451111025b17b926acba316e62991ee8a12aafd8d225658243f30c33bb84ff9ff64c3805fe0da3dff9e3b79ecfe40b70ca0c2ae209ab21bdf7214c686c899f8e8cefa37a9c0235f2aa411c0048bec4562807a8916fa2be1b1196dc95fc74e0b261ad9b38f509e909694e5652b509daff710f6d6349961955a983eae79cec951042e36c555dfa4d73f6433d0df31c5ed09a2a9a141a9c5347abbf374be0936fb1f169873ef3ee6637052002a4938bf33a11fe0d91b7c8c7daf5c883c82c814e569d4aeb78a73975f62a74a7c1249b3d3daa282bee803177756a53cec1fcf9182a29fd34969e6331e0cf485771f22ec7139815c4194355f574da0e354e8bd9d6541e17831bb1e11dff88b3ff6ef8eab99633d17d24dde4865368947a558188875beb9988ff8a6af5dd05d6f520ffcfedd91424cc58d961f344342e8a48a20744938898a4072481af1ba524dbd5d679a3adc242d69177f766f5f02108ef1c3dac87e0dab76f329ad90b87e71c9933d716da22d6a339fce8eb6c1f16262160695ed2496f40ff7000e2f6ceb25596c86c3fee678e25e6daf2b7de055653490313172c3c0717f557046b018975bc7486c912a10c61dd304d69d53dc48da231e4b88e977d0e79fccff97faebb251ed103c6a92224ddf6899d6a2dc0e05295633981abced6825aed486f3097809c670582a161413d5866eb683472e5c5da90e75c91b9667b760285228588ca7abd24df50a6821039454a0ef2eab1af8747892a120363653a97c02819f65b5950b76beb962313d632ea365b7c0e7f65f25d9861584b5a291c1fe671a8d4d0d76ba0e2032dfeb3c0b38b7278e41d1478160529385f0176050d5de23b852a9cc1a3fbfb8d5380c6fc75f3c148ae38c472795124369527263c1c3e6df7e6dd8bb8527974a4fb312b1440c800a4eea9dd3d0c3672a987d8a3bd54dd82bbc5c74cffd2971a12892f69ee9cbe4698503fdca3e26733e6458422af15996d04462afde56f0f0af1f9841aec59e0d1f9d9b9e30cda1d5bc014b0deb27ade65ccdfd30269fbb4e721e19db4fc33c0a7b5469b7c7e339feba4d3f3c51a78e8323cf8d997243164df14d5625c75fc43645c59ca1127511930b880a4f293c2f832c6d177d62a926441bf1dbef7242bc0b34814acc12798da92b9aee9ce1da3505942683177e188deb43d89e65e9f587489b4dcac1b1b0c9185f4599a1b05a5f70fdfb335e58c4dd2a5765fae76c32851aefd9710d9e824e382341a8a9c430f8cc6a388ca9fb75ee9567388af2032d28600e104ff2665311ce50ebe244d59a3573c54c11dbd1c9de04883c4ae297eae32fd18ea73c9c59d4af1a0305eeb0fcc160d97162a76e4d4dc8092669a68b107858bfba5d36f90953b0d6b10ce8946c7ea1be7b3c2398b2c0b8bfb8f49bd51eb8afa5c75d97d1a8087da1c78d2dc6e30b421ecedab340b106a836d7d3c1067da3e2dc7b750388e5434c5132d56ee84c0b94a95d4375ce2e3a13381acfb6f8825e3cd23dc0d82759cfd178f4d843dae67856927a83fd0e2c407baef24c182d4647f02ed006911aa2bcdd6a321412a96407ee4a86336bae35f0373dc370a180b808f8c99cc02e5a496d1c2cd4365e39afcfd531837eb02951e990eeeaabda4c3c52a5e6e0c6c370c302ee0cd33a5955b43fcf77147066350a24d914c49f9fc9f872162986074d1d86ace84754bc0fc24840f91bf9d41df303024be5f4d32d6a9e02581f78ef44663ef1a6355d952ab85b6e1465c7b63f260859ae64cab8180b7915560870a0867ae8b96a2110c68ac81fa2439788b028361d29e66dfb419eda4131ac43fd593acb446141ebbbd47115f1c7f89e6784f4ac0de895ef1065a027730eae6345499df691883ded22f2734ec5c959bfd3b82071f63840b350e5a0462dc80a2bd0276ae6c068a11e89d8336e241424975b2768162f0eb780a5f94570efdf0cdc5a0988959fffcd91ac55156880790e49a39c842593116fd78d791a7c3af75bf8c01ad218afa532d4201a1a201cbdeb0befd71f694091ae2bf805179c3d8b22e36b683b17567037de6b7ad2add1164bd8298ae215a464c13ee298cf394f294583e6439b01eecdff897e2439662a52032ed17cc6af3877e1eb94aa2483c5e47723a1d318213c18cabe76bcbb0007042385105b7e3f10d9322710c1c39861c8a02a854ba734de937edb258dfd0034c6aadbe738b343b6a091b56a482bb2c74fd088bc80e0b724f4e1330d3936886d8c07d9c43660ece87e3c868c7dcbfbff0a454308e62aed81e584a65267d7440d1d1f739b10445612541c4ab5eefed763e7af49b7c2962caafe9154af66ff9315af944b24ba9ad515717c18497aef37774cd7c64bead74b71207990f0ac2c9d926e3946609f4a15fd9cfba2f379e015a07f353e0496976c74b2827bc30fcdbe2dade037ec4f62545e00e097a6da6c701bd929d348c479b89125e042a162b63e8b2c05adbcc4f1302a5675efcd096ded89541937dab2b2b9bdf231033ce128a546e53a7d6998fecd39aad764ed7657cd84e49286db8afbc200c2bb90c924ecbc9709a029b9ef49ae9c67f766db5f5541035640d9cdc35d859df802e9fce09d06bcd0d959ef13f85892d64b5de631515f5383c8f9269599eea585bc285eaf60a573ec8d4b46ea4dfdb05a2a90db3a29d3ec5352c9bb83853ccc3a4ad0b1ac07799ad28f44ed09b80007bdd12a5a2510c6f43a770db4c09a1d39bbcc454090e9aea45b380d0396592ac302ffa896fcd2b02e7dac5d1438bc6e3db333911f56ea470593cd544ade9f4ac498c05f794daf6ffa608d355a3e67bd99e14fddd306a4d137de15518e310d2fefa5e8539c1d855c2732e5263f5ea9ad3909e708513ce371c63942831b1d2f4dd7c7e16ac624b63cace52e80950a1019c35b546252d0a634fc0abd8e6d41a8a2a69f2ca64f0bc24528c06ae5fee21dea282210eb304a22f6de292dc02b877e76e4d3b34661dbd3473778cedc47c52433ba6de899fdeeb9a25a88b911cec169d6d39bad65438d18f758c614d1f088f42b841095a525dae3f3528ff183bbd03aacf7437621b5e10f2a926b7aa464c3295a6b2843094ab02055efd3a71659989efae1f7650dbf3503d1dcf6590eb764b3d5f02ea2e98acc180deb4410c697734f202e21740ef69d70b370bc016746039b81bdb92c6e06a5c81e52493b32aa58e56aa779a54134ef3eb17a3c899778006660d1737d43cb3d34764120fbe60104f04ec1195f8362902bea9c6151230cc8886a4ee46a2c62487d79e80b653a3115b465471d3b4f8440a1180907d13f4bd3886f8afa57612f94eab263e672580066a1c4fabdcb6aebe1ebd4524e8d970020c5339eac7f7efd15bfbf73ddfe450f8abf0a58aeceada566466bd33b818a8f53a5fd8752a431022a82b9072bd95271abecf484b92a915ce743df17932aa4e5fcdbff88d9f0638737fb1611be62094b449057c733c881d8a07da35c73a934eefbb253670a50965889266a63493c140880ad04a80b5c9302ee713a9d1b111ef08868d1d91ccf74170b9c8ce67fdd6f3f979850da128bc82702e207ea58dd95f868eff3739bb89cf042770184f0523d5eabd62569e79891e1f5bdf48ecb41abc8aa7448b79fa9cdb0905b0a9c3b11a75d0a53b90006939c0951d977d369e1f24b9fe167a32e3b3c995378dba8295597660b918eb7d2211ca144b52bbe707a377f642c865541f9087275604c984c5e51a9ec1588245a444f5cef04ea9783b4eae87f94b997618b798e66e1f4274cfdf001f92d67bb3e657597658794c8146604b5a344c66b8c9083510c407d9f176ddbe118e393b5aa030b7188e86dd670cd03fa281f46894aa7bf18e58d7764f66be224e620f805dfe60829d1aa40337961d71639e30a42c14dcb38e0ae0a8525b111d893a42cafa7635804f8f58ac18fec6c27805b57064c814d6049f131edcc53218f2d9c849021eba9937de3310a559d1f699d422f73f0c307a9e456eca1e6cc6b178689464973244980032746dfd3be597676a23e87959b9ca4bc63f1862bcad00cfedccd0303f905aeddff1a852867abd56a87bfd0098d24ffdc53f429576502579cfa64c02dfb462a3b4cac8d09e846d2247eb420868273d1db010789f2cfb8d5a60b45dc375e8263a743c01dd4ce07a89e2a932f62167e598caae4e7cc2a4af7fdf5a046cd862c370e0e45a34150eddf866e952242154049a157a236a81117e96ff1bb934fd844f476aea63fd489ac6368aac61386a32bf1b93b677747297de39d8a8a0064c37bd1d20a0e5b12e636b907200054f770f79c893928d87e8d8f5b0b5e638e3daeb4d32e154724c6bb6c0f51b011490304cb3605d36f09d99c687c8314d89425a9b49e0d4aa9bf9598608dc46836741d14288f3578635573dec86578e5b9c53cd51b6f6863efda32ffaa8e502cb50e53533fa52ecb7680ac8cf05197c5058011b1070ddfab89f6050b9d347fbb0d2ef3b8ffa5b15df77bbd28838fd58cc06a8eb48568c8a1ce4d36ae4a18a2b8903cf2b0539da51af7fcafe43ae24e51380eb23671c7f3da2c20bdff8c5cab43037670127b2276b9f5a40e20a436d2cf689fcbe101a446ea0758b5d34bc6c171d48c3461b93079dacf1d1b3239c31ac3a8c498e509ebe8292c46fcbe65f854173aefcf9f80bb396d003fab070f84ed8f77d961899d253bec0bf5b2417ad18b53650504f65caa22bcc8689d684e91776fca21e2c75bcae304163b122d7e1db28be63d3a1926f1adc9c33c4f9f2c6b0704ab392260597962eb4f7ed55cece468128eb7cf3986173cd3e26f2d73e90e86b54e2dba47f745d90e81cab13d9e618a3f6b971245624a65c5ff76a85a48863ae08f5ae1786625363820b64ee965f401f6283fdfe79171d4022c0ec79c9fc361b36d74203ac3eb1e8f29d06cfb1bcf0f7becdb1207948aa345d440813038a78aa542fcf2a732b2ff15f52ae0bdf86f9d076112f034295f6c8569e5aec8351d7a3241897486e2f8b508788f456acf97f11599b1b0c8d0447e62a511b9beecbb0257c3141d5c09ec03de390f91288d76facf4f070b717c410b80f2307bfa0da23bdf93bf0a9b130851f8821ad9f7441adb16201e091538ffd4bb513e588dd0161562e3b7db52973152632ef09589b583a0893cbd31e800376cea51e4af14304033b8ddfb7230f9444b97d783b36bfdcc0c4750098fe214081d7242617c1dad4c9acf92a5f1f3c36ebf8439dabd8aa20d58f17881f47c2be7e65c4fb47942315c41539a2a4006a9f8a3692f0c5db9a7a242f2e9afbfb45e8f608f910e4bbb8740182d439a9998f6d29bf88061a8d1aa7c943c327723b81a92c845eb99c44e839b59a42dc996bad9aaef3d57de0f99a34f456626cd2ba11a1113763cfcd83ffce717b3fee6b8f5a09e4c8351b7a0e4714065ea79599a4f061ee8ecd921e8efb10b26fc0d45c7cd6aba18986416cd1b2c2c2961a0ede91c36bc596da3641ceba8a29a45012632011fe0978934c42dbe978f7392ec0c6c3d919fc405fe3352e52d7622445a5b452c76d18e5c5e8b6906f103c6feb89b2045d3cdaf754654e3f70bda90cc998c5f0d9c752046603ab815a6f26348aae07f33b206ee133a036c62d7b58157b3ece579c90347c4098d47f8a47ae16f06a3c0048dd9a628e05bfb9fb8887478cf59f80b980c518011460de2f8d002a82014bbf0a039e1ffc290c2b40ea03f5ec70ae72109662b587c9061776b794ad294eca26e36231811aa3c2ff4b9f9f38aad3f085cce1fe6084987c969422c45f8b62aa9795de5868c0bb7387848b49ca687b47270880932d92eb5b4ab3b3b0dc885313f8888ecb84270b2281ef972582274c607223dbd36ded39077e8825a87964f1c03d748fe86568b20e6b541f279a4aaf120ffd63f4a6062ace4ecb2d53566d307e02d29544252a2260a59ed904a075019a3cb4f2dbb9d278bf0925625e02bbb5d6dfb9d2a6070893c1fe147285a12c577233215852e261124fcbecbee148e068cf72d119a928c199ec5cad3267b6e6cb7d75f383099f7134c32bb144618b0ca42d45232297a00670cad44a28eb54a2b03a8bcc545f7c606ffb867fcbbb88025cc40ea4194510e51490107aa370b880008e9e10379634e5383afd0f95aa6b39a669edbd9ced6de2ffd8028503a7bf655416f1dfd5a1991179a86c9ed0819769d9c8461407ff30cde9e8d355a72edd56472a99211a3b3e7fe5da6c47541c59fbe45061115b8b852a344cb67013ca01c77d3930c48ecd7f7d07d843407e3d848ba0bfa6fb90c024f5a174a3a997cd100c825116d48ea261c9fb5060c3869659939df3c38805dde198881907fc93ac04350a561f85f67899b6e678d7b9351e2939d0d583410bb137d4cc6cd8676aef4b571b610c3d5e694c2d3840911e4f4579952905af2d7343e35a971be2629f7b624fd7106431b1f435cad4ab243cb286f9f11fdd15c0a484c86782d8e7cc3bf124a9d091cb9d6ffcbdc469edf47adc628f9e5045ba98bd36eea77f37e57248afca48cf29944fb507f05ab4619ed015ee08a099ab673f295dd2c20784d2873318a400d94a01b90e8b3df2efb5b976e762099a7dc718508a6886f8dd0cdb11d877bed7e3bba9e5f6ee30f7b8834502165b77c7aabe3a1d816e507ebd5d1e0e6a59044d6d3f5448d1bbae19f589c7629382b557bb69a1ea3e33f61f90c0a3f4ed3009c29aca9c0f6f2fe9e2b4cb3cc49354b4731127b876015671861ffa7aa75c5c6a8a95822381f7595c44284c67d5f33008b56740ac2e9495440edde5a131ea943d11f0579bc7cade23134201731673a67e5c74bcf7b8bddeedcaf17a6d54b2c5ce689db3f6b422158c7bd1389fcc429c8296b7e50b11860b7fbaea0c62d963c46b2b1899868d6d1090cf2ac6fe695556283362ff6e089452f51aaea286dc5890d6182ab339263a137ef7e3e32db2c57ef1aea83b5d28207137245f8046b4871f79cf8df7c8e9be15567021d1db792513d6353fb23c57229b410254e4fe38a2dc6b701c77069530f4e1191c9788e69fb0a1df9e6817dc799db85e260c82468ae9da8489e46e98bc09a24063bc5c8b37c67ec1f335c4895b8ae8d7212952294a5ff48b0cf1ba51d8200dfb8896c1f64fbd27339a5f0f7a8660fc387f6cc05d5d762b7a2f7233d98593daf08e727dc6a4f516286d77b50010e166b6b0801cbe6e5b7457b0bdac1dc561f74b843aed6c5cde3e8dc94076a875e99466437205d7352ead681b214894f908709fed3c0e3ffb22f00c846e7420a7bda94ab89f1ea044df8e05ae36a1fb44513534660d4c031fbeedbbcff06c8126b641fa229d0d0271f000b3880515c33f60b6092f007127ac5d256fa630a40b0161a147b111ebcfba9b5bc3afc972b7d0cd9a5e6cabdb8a3357285a57ec1ab7a84de505bcc43f30a552ce5810231b7aded1705292434267eebe9e85b30da8ab801e97859a7261a0e472465e71212fddbeeba1c041e085827d508eb9cfb9fa429cbfcd84cb433bbe7810e4215836d8318447ecc7afe27ebc24fe8ee276358049c023db93dc601d1cba57069c64c067c0a28a42d776235254224e42b2a53128b2a8bf7313deb96bd69e739d1841d4968451e9f3dddae05f53dd3f9a0aa1deb7f87e8571e971c1e8e98e0410de1f8c9132d972d17cc0cf6ae08d682ce89a5832e9e9036191a94f1754c3c12d59b3c68439305849aba37ee9420bd97ccc11c23b1f18b83b6aab05697b160e4e21b847f59378e49bb1b7c1e4be265bb922f967d1288548a907754d8c89cf1064ad2338049fd001e19285a33a879a8d745e5497555fa4d1622af4aed22e688dff218903371b7810a0ba8bfa1f2d2717a400a6b757ca728a2851773a13797944aaf0031f8997ec42033bc4b780dd0c0581b27b52a659326ad720cdebe04242b27ce08334a43c94f7f4ff067205c8222fb3799ae27faba382ae9fe289d5775a4486ab00921ceb5a53e39b19d9631f5a7de5ac7f045f84c100b85ee3d5b0a7635d7941b26bcaa85509af3fa3886aa3dc69bb1eb88c970c1c94a28c07934aaa7557740907876f57151e48cab9c9b64b6b7512c38edc205e453940d33d25b3f62906007bee8cb36aa510de30be8783a8127e324b6f5e66d56cd63f73806cb374e7bb877c89739d0e5360a1e3ee8bb171d607c769d61b467c9f5536448927b41eccf2018dbd9c8ef86bb7b2333d94d47b7d6f8f9951128cc872d68d8c7e5c1a6a4b676b915991fedb9081d7485ac1126314ccf62f823e13b3a4c900d10a4dc201a39c946ff0e7132d70ea2736cecdf74b03591e7aa008520df9baf5d4120e74b1104955b269aeb4130c8343e4bed22ec0382fb131b6a37031ead37750743aaadd01c6d79dd7299b461f3e1a6b923e08ee07d64f719cc9b1b192a2167427041a5aa52c211ae43f4b45048b851bbfe18f03d9c4544803e32a7dd2769da07d8b0d90555be6c911632fd1ea6c6873a65099e964593fe6b00a371c6aeab5c94c9cdf609bf2289f03667aee9aabead8ff6e1fe8a66dfa840f01b8cb964e5676dfd95369edd327b2e805da6aceb5812772a029e25d4c7a290d32c6b5e8d8ef5651d826497ab3acc6cbde8cb78738d10821902007a505d98de595f0b55fe4cf485aa8ea2486b2e51c828a479a569ed4b12a20705f2962d3d099892cd114033c1fb915a1740f3361190a34ee0be065e64d3a784a3b15b9b59ed2383bdf8394bcc9d6eab2130d7a76d3152ca65dd90530b6c04afbbe558a88e923ee5fbb4f51e288021bb9cfe5c53af138e2bb27240e122c40405612212a070b2314ff742b76a8e0f647425230d3ac6cc51d8d82c52a323361df1053a3e80c068aeb00acea9a671123967fd0110a560dd2c5de5aed0d667096bd5faa37587f242704b174e7b662fda19cd56dbe88127c2e04c6a221223cca8942773245ae8f342d784625c9069c89e925a5829cfba4a4b557d2d756ef141f005a1da1e98be23fb1e391c6d960c43c5464ad33504dae59e79496a9a4039ac0b23dc9113e1d0b5ca089f5f06563da99a2f9e6d3bef8d75e08770ded20c286de10b2a1950dba9ec0e0ad41bd653da208cd2aa3b5f501a064d6a02c07183b31fd264886601f5a536bc82ee844ee548f926a139dbd7adac6860213b4b7bbbd6ba908f5b6bc6c4542d8eadafe05b54078a7079c289b9987ba89501b4fd5cfe96bbc2d5dec73672c95ac13ea47593695e6139451658817ecf44cdac817e7ce3a31889d3d1cc2b125f8b51c68c8125be1a691ac72ce2ab5d5699f99887e877af6a57f7a2fd5adc3f881a630d889d5b30fb89223fc23234f3477f7ccee0e3adcc4f773b433a607a8e8c5d003b3f72d6d1dc54681c19e2aa5c5081b4b804d75a9a1eac1e7db0b4567df0f167e5e31539f318ec8de689d5dc691621eb4ae5f9564713b12c0e24de8d70a40de0380e26295240d9fe22ae2d06a41df61a07e9e4367fe0ac55e41b79dbe9e8a63189fc4547b7426277ed2cfc75a46c07a299e110bdeb427301e204890a1b0897865e111c6c7e2ba1409044ebf7fee8a71fe45b77739e8cab61ad21090f9c15778d296bd45e98f56b923e3e675ac2570e2a2722a163232041105f4a78ccea93408b6a25cdb6aef06a9f88a4694ae8b64212e94bb94099268d4b8ad9e1e153b8946f766b899808f5bbc1f59abf7c5072707418262985e20f31cefb9ee014814c488344fec60b0c30fd2c4566939c329b5b6eeb8a82b1f4d5f114976967a332a0385cccac9991d3aa7fd6952e397696151f1a750cb6f57ef216f663873c8a2db156b9486638dd635600cd28d94afce246df662bd21fb5c25a01ed3e71bb1171e6b7114a4852c5fc25090b0c006ea9c085187937ca5f7abf6d3cee441db4407815ed05f7a916a2cdbb485e4d054919c5bc9a0988c629676d82ccfc4ee6722607f2270019f652fec3fa24e54c8f90869199473f0c1d5a2eb518ea85fdcefdb168da1a0849701907d0e0719b3de1144f93eb05973d307fec839f1027d8d84fb9bbfbe919231042a4407ab3025e3c61049398d3ec5a52b5bf2f01f2de47aadbf81291d3f4f4cde23d25036aeb7f83f501d4a8dee04ed3405c0da50ad6ddfafd2f89c9e601b860eeeb5f967eb135db03ac72385c65700141c15a35daadf7230c2a6afa1f95ca2d1b4c5b4a541d14f3913453c6440a268c0fc90b1a4fe8bbce02c511be5e6c7dbfbd131611d0c54b1527d22eca9a7055f1f2bcea27b0f95c2f48ebbb8a7796b4c40fafaab464e7859159d9f677860cff17fea32bfe0685357cebfb1dc841bd3b26797394b9ed3b6f0512a00c00f7b4f8352259cb1ada34d1727ce3d84db4f9b459d7cb601ed363929703d4dc3b7c5c78162e02db6523003e514d8d4784054a8bc78a5e0efe68f76bd48df97d20e93d44f05ff450355fa77093fd6c6f477e82df5817e16958317a57a62db32299042f1a97c77b455e5a0086f8888c0eb67713ce7e5db41dedc7eb57087bbb16539db5da0099d64ad426b0ff426b441f63bb896472b92a439d033da7a5122f73f8cb839d772c200bbdee6bd17540d1ac3dbc9e04c9e95e2747ce7eedea4011aa14b7bcf419d8a61895f519485ab5da3abc5019656df58a329e0bf12fbf7ee52fbf1b21c16c9dbd5e073ecb797986b7b8d7369b60a1b78e0216097995fcc487e9db10323f614a0b9333248af3bdf04e11a28a3ba536dd0c92f681947c5a1be2a8886d8905eab3f0fdf0763798e4da943e8b21e57ba9fb0f8100d5280dc0b5b060a9bf0a7c2565266729821b2b98f0c0be1b2ffb8d915ba0dce7534760d9160ddd741fc60dcb4a2db7da283a1b3847feabe321d81bd21d8696bb64f4d9d46b88ba9bc35a0f02e2fdaa36e5a4be00e82a46e14af8df92057a54d64b02244ab5cdd279847026c5a51fdaf879ba46124b8c88ae7b1cd61b098a0c1d60dae71dbf099a17baed38258d30f4ec699fcbfe2c884f274cbf7954a62629cf47f158b468d3b1c8e5a1741e580ab47977490141d184127354622e06874647256c3bc163c6ea784f26097c16c4137410872c04b864bb0e8a16d976f56b087a56261bbf71f077aace11fd99186ff899326eb55b627d856994986f1f9bcc6b0ecbdd8918bbe05a141725684d7325a14e00fdb711f5fb76898eff4944b100dc24c6a0e8146a16b7ce193a16da5ed9fb3a860ce019a754668364aff6c1d3ebe5b80a4f268e8020e560f991e7c127d9263da67335b42df3d30cf704f8993245efccfb5984f1487d62a6ca1ea7f7849ef021a598dcf0c332781377707dcb4c93613f01cad4dead88541302cea278624dee64ec7f9e62c7681ee94d944daf84a40e9ac4c101c22990507e91fd8c55cc213863bbdc194ec3d9faeddfd86c747d0e4c69dba52c916642031d2557ba2762c74c75d501c119fb6b6cdca601df9442f68057f3dff54bc7027f4bd8bdbfb8830af6f1a33777e172df7f2f3ef584e83b63052e48522d2f88f47139acbd861456f3dc35ff2116f9b68af3e2eafc42e7b40c6257f64fe88e8c0e2a58f88a1785d8809757eee2809f20fec9bd1b03772f1f83beaa41d6b71c21e858c309edcb0ae6090998ac54ba3201c223b08c3e8b486fe53fa048805ae0c3081dce8e54f14405c09af9e37987956c7ee213ca79f8119b7e40be65daf9eeb082780fbb8b2f88195d615d5cea189bfb185e7512a67d41994602ba82b26d7c68e977e58901b244fe328a0ce3c6819aad34ecff9b79e9fa4674a4a65889d0300f192b83cbf0c56f11a5519717210f3156b3c02848131f95eab089305393272a1f241b68fe2388b9157bd764c8ed02d850c196cd346f4136e5a0f48bed71f571a9a6be42b7cbed7f12f3b3321ef341d36cdd967e7f154afc492b9a28b5d5c62c92e7f62b08786153dae17e89f168828d2439b497ad429335b90b7e4a511aa097b48c4e04223e4d13f46945bf1ec999baade5819031d8bb5668462431a78ca0d984a3e2fbf8845246e660d0abd257ce781e41e607970e2a494e268c74bfd54ca2b624418886947c475682ef6bec919d888f736fa00d801c96cd02ed5ca958d641c6c65973563723b59499446e529109517a10b9e5fd8314e65a2655042c561e99bb7d648747c71bec9a203c2e4c6b2cf23a7874952009cf1c5bb2f2e28ee533ad538c93b3802b44e893999a04907fee0f92d2434e129ba3ade199471583584475016e16ddabff99e8fb060fee9d4d170b23dec43ba6dbeec87b9320d345d452ac2819da708172474ae3a155c680827e5b0790f9216deb0879ba987815b2fa71382a2a4dbf7beaeb75dba7c47c2a21c3a38e0362473e9f329a633bfcd323a137658c4f15fe0f12ca7522b508d723ef5ce2269b5a8befc4259560e598be33823c0892dac76de57ce98643c704d972b2db600297c01a977ce19282add97f5e1bc17a7342feddefdda36d77a20d04d04b03bfcee96929d42b608e559f84ce62c5e9331089765ae20807fe4b3d62ab0bf51afab9597e6a06c249a1ce0e41e50221ab1d4536400259ec2b4829f6568640d84726892ce659ba4c49ebd0f49be29fc35b278a58e1ac38081266e7359968704a46cbeae109f0d650b4ab84f45492bdf5ca2bb2e65e5f9b43095e27d660e5be88eb440710cd57ef3eb8197b9772b86e7cb784a1a5a68ebb4a51cc2093893321895c01105f045e3764a69b4696d60dac6882025d361e0a5e5ea30889dc4e0af4e5772b35a8790678e2e0794fea5664a944d3fa1286ca410d10b52cfa2e8db93e4882312194a9c163974addf1d6ed2e48b0f1efffe84e8bc4030f3c4e5e4c585c12ac4399b6d07234f1c5606fe7490160f92ca8b855ffa30764cd6998673507dde658db6413813592bdc8099b5ec770c16ad7ef74f4b00cce4f86b0c4b099bd572dda07a33b295e75f9f02de820d193112952cd1f4c62598b35473cdc78baaec8cc711a32676ec3221e6af9201d604268772173c0c625b0ee0a25f5e2632102400e0245057eabd4324c2f3971a273ce1843db922c2c85901c1de2fa58ebf946e52070278df1b3e42f02d44fa5fafef9aaabab05478cae829cd9fb7f54634a7d0ec770a813ee4a1bc28ad5e62532de7c4b83be82de8ce4ba46b26a54e47ad09afe9f1a06aa7c1f10a4d1cff0a6418e4eb13678ecc70c709b89f90bb2c6e27cd2dcf64c8e12ef45c4b04984740d2e80870af83e5f4376d77f571a440256c58ba6aecabac39b952736f6c85282376625e363de9cea4a533c0eec5947339876408ac4d8f690e431f9b5e8a580b5753b89e18d0d6b4ee8e3495d7a9e96a7f1ef1e3a6e9dac08ed0bd6705d819ef06a5dae0fee289e803c81e5703f4ca08cc924c22e5594a3a8bcd75abe18987a37b27fd97e27884342411e70ce484a616de42130060c6009fb76eb36d7b6d0a59d02d806bd92bd7a67372418aa3a917805a9317b92f2ded97af972cfc6a589b5f93d7c6571dbff36a2de5beba357730880c0ad6c6832e3573f092cdd88a4f6e2a226783fcd62b7332c6c9d2b0e8211b5671d9f62b96b11a997abba67d927ebf623f75b1042196e11db611a54a52b880d4b481ee4ee518a8c800b3b44412521bf1e49f3282495ecd07a46a078d9ac8b4a21bb362db64431ab05c08db50c958117d25e8437c0155fd5d8d3a14f32ac0499cdf6c33702c6474c992f423f9a087ba651cf6f98314a96ae9425fa09478215e8c5e24afbc90cbe243028e3604c07891561a3fd6c27e75098e7e35ec4804ff7b063e91b97a2392f627a35c5693142975b25049dffdc7e80a27a231376992c688e95863c78c3d01adafe1c69195b67c025b7e3c9abd5dbdc117f183be1aa34f6568e1552c9301871155105f91042c6fc2f0aa5e38862b2ab5fad8cc732d383cfae3829a7adb0ab24975502e41987777d446e911f7041747ec0a453a1040f23314fb688010cdebf511e9903630421121f62615829a3ec29a69c5d66bcd8d86aa6a834d6c3808912c2743004e72a0610148bcc376647178693a6c14470e9b25654d81601a8fcaef384720bb4cbfc704f009e3eab7560823d4f9506b003fd413cc154c71d645326e95f0402d1676ad3a9c645b8476e1c4e4b5d85c239cc31de41022f6ae103d46a6ad46506f997d7f0ff48f56cbb6bfec8cfe16ce8be2a60a5ee37b3b6e35df95f9129aeec1865241c13f82416d7b39c3339ffab11c94d9a242c6039dd518e426b5d913a190d07018e82618e44a6131fd8d63ad05e0adb51475e0bac8e5cbe291e20282a9f6e94b331868f938153017144fdef0e4195148f1543fb52c4a660a9a963b35200fa6c65fd4e8ffebc53ed7da8c5288bc300d1fe53c7a4405e1821272c4917ddefbc3162e982d0ebe2fd682cf3ad29be479e82a604e88d76c8f4ffd74706ac7a4a32700b6ede0d20e0e7e22f66c1ed362dbdb61b9aa9193faa8038c0c47318447f2ec51ca11fdf48b2fc9390b4c66b7eb3d06cdf0ae6bdbb820e14e6b84fa66626be7e0af283aa213aab32deef1805d3b2371a1f821364739714a31a5a32d9603818d3d48cf69da03fc466dd950a2cf1497cd733039c1e57315d4d699fde4012b56fbda27f7f9ea7dd23b7149f12df1d42ea0eaf4f877463809e9db0a066308f73a72e953321c076522569ad40e1d2f1af8be49b3dfc144d0dac1c51ae1c5bd8dc44311134f4953a95b63c013bc80658e98e254e460dec36b00f33c08cde55a43558939fce7eef859b65b21ea37a89c0e269ec5fbf2e87dbfb5b6c6ded68dca787761ace2fb26070c457d4bf6c9aa8963adbeb8c7b243c812cf4a7404b8b8d72457f664059ff84038f5e4dade6dfd267d9b405581098643f06e5dfb510f30f04a6309bf0f1891219b4b0ede52204e837fbc60c3ffc2abdeb5b38606a480630e598d0765363a26c6558948c1b38e9b8906b4e3d2cc248a596be25c8fd2f018eb38b2b27dc0cca1f700a62b2456d65520c6dafbebca29e0d14476f1f5b18149abd96b6635ac8bfca5e432078dffe29654822e5ffa96319bfb63b614a499e38c3178cbe1bccc2a4b18c3711addae90e25aba8f78117af000d92c0626c6b90aabe201e84c3ab74e91d2ead099c18b298264011c66942ad1a4ae3b3be93cbde472042d359c176d50ccae3d50c0b5f0bec2382496cb92de3d7865c75447d80e95b216456b6c3cc0422310c50370927b007d61a2b9e9b6aeee2dd15a87ba89970f2418e27fd5b311c94cc01b686c729206ed3c8a505df915ebc75d48a296a03a811ba80e3147d6bb4e9267da5deccbf3aa5f51d03bc594863361e8f9ce657f74bc614ee6b407cdd9cb5b2bc7b17353c95a8d678e47ec357005ac8e0bc69a9940de226d2ad3292059459558caeaee0ca6367574b594725a39c3f865317076b874c7e409d66a174b65f501b31a6cc76579ba4546b329710c0bda932e98f69709eb447ea8fe81bab1b3e426fff088c09a2df7f520255bd865852a6cc295f316bbd5c06493381bf74896b4456870b82edb706fe6e2c0b1aae7135d4e9caef83be5521020709138905dcded85e626acae32a55192bca69394a002bede0167784687afcc4368a4ba4a2d92b2ba80aff1b90ccd494a521a1c33fdc183a1ed5b5abf4195e5b080dcd01800995caba400751a70aac271121ab3b8f46b8efc8336b933c26fbc790c5431954295c44f3c8ef5d123929667f94aeb81150c7f5eb8c9a1245e37d951de5c5c5a4616009dcbb33c2da3b97268447f5a383b870b42db94b59e0212d5852c22a96bbced81d01e28e97ae4876d279c8cb0daf4a72c4c41ba0cdfe7a9f18483a4d156ffbd72e7436b3e0bd51c4fbc0962ee9c967ebd1349b581d5193bbd7e577847404ff62cceb2c2c97f88a7fa9b899c988778ea6f669e8bdeff3b94babeea8c559f63d357bbe22f2f2013f2b110f1d5687f4c72ad6a0e9018238ca5e1df69ffc1274a220cce87b56636485a0f04cb1183ff0063670b95a1329ad781670a8b79438fb2cf97f33ff87165f6e1b231caaa758c2a71f4896236ea40d46bb605eaa86d6a428641191a4364fdde0cdd92b220885e1ec60632f4d65f3e24a37cee3440e68443b5825b4ed775fc5d55a84cb489ed4f19ecb6bd38d5cde25c9cca6b70de027cf9a9807d6d7c3d3a2ebd81fe86c8ad2760a545b379d65c36a2b76407883bb96adca93217ea40ab7f14da70eed36b46aa8c5b093205f9eb2b4d4ace365cae28eb1f24962773cd9e930360d0dd7aece338a452b52073a24a77b5bd2db94531951201a66c7d8c77b565671461b36d9b1ae96d0a3c8ba46d7a8da49089718d0d3d7c874b371e2e0eefc301ed7ca444486cce42e56e23b81584a0907bc8a46c76462fe4ad96fc3ccd6a4b39304138894638e01e78c81800843b053aadb22ddd3c19570b409201a822fd2c100a8d350d41f51491a3b194d0c316f1b848d03a5c5e8198731092f1c82f186846506b4c39904083647ab5ccd6ea94b99ddc0b11ee987d9db126d16ee371146554ceefae8466a882b1f1de7fdda19c126e668ef2ad1336435b48197614dfc14eff163436b37c49af453befbeb44e9a7fd9468dd5aaf11536111d1b46e1926aae942884e1b35faa425d942ec92869b8222da44f91b3bb73381a49b4da501f7dec344e8d1eb597bffe1e72f8dde246a93783a41ede408ef08ab951bf13f05a52a990492f1efe883d19c89ef08aeafbd6d335fed1f5fc7edcc6a3a1fc2796f726ae40da0039d62898056cef920b93c74efc95d38863626da853a4cea66b08df7f8ed8b66e6b77053781ee03e256a5c29391e33343b8cfa7cc07b41dc3b8df5ceb7c82efb68e75b9c67c8f96d5093b0702f353b08559bd51be695ec67a071c77c6cf9235de9c39e5405a7edb97916b66860d9e0f9fbbb2ab2fc411f1b05bc2adf7593630eba25b5a2af414f164723902f501d2bea332c537f91680c6caeb795751d2f2d28163f45985b831133bcc7a6f2f92227d8853242e3985780977c527e70ef4df4030d5d268705e97d47a6eb0828990df05c829916939c0bd6ca354593396801ca419f4a83e53123d7c547143910b7fc80294a407345c58178bc0e91425dd0d44442712b7923e19b5f8b0aad4385b39e8175bcec0855b6ab26b99f46adf21007ae6ad8792385ff1c0b4cd72c2d04e243a40e61a2d38bc4ce3f924d42ed7c4cd108adbd8e3b8fb881a9de57ca7040e756c24d91a658c9503da92e93f63827810dad8121ab8ce0af46a9022d4ed4f14bb849e0ee85d260f444ab580777e3d7f444a5279f021bf4888dbdb160dea3541cbb721a18dc2466933dc614ed41a96c72fdca3ad1d143b3892bc034713c938b1e3061f9d75f7b646a32fe8eadc150000012bc6bf7897f25bc2c89c13ca12880214854807b7b0d12a8938ec5ab5d675c57a2ade33ea25c49646a48fa82ba6500c93d21b11675a1176d83a5574060fe89ecd4625340945d1187f3b09fc0ae827fcaa79bee429cc647858675cce4324cecb731a144a8ec411ee604cc8838620a94f2f9008bece558658b542064e0cdd5fd723f0ba45395fedcab11e6db556172de3c4e30242b4f8e0f7ffecdfcaf09fda1d6ec309a83680ef985c4a5800a1d2fc3f64ef662da80aa5127343c9512309e23d77add5018c4ae34bcce408d36d84b2e53e511f037abc46d2c0cafe651514cd097776912d6528e81a0dd982c004fdabec5bf00f1b2506309e46bf086e06275eaf77edbafe9569a1c38d7d78ab841a1eca1adae80d7f2f4f13e2d75af367a0b42fc389c3f3039654441e581942a10768a2a734fcbd21a3fdc7c3df0e6cc8dd9892329d3f474138576485dae14cad49f4b85b16e1cfaf95e13488a69673b1f822bb8d5d6132b184a6e105381d635c347661455f8dcb631869e7f65a06f1bd52327bd40942e15309f68d84befa937bf564278fc37572d32b326de17cdead88392d2764aeb44fdb3ace15129b23c0a74a7708dcf644cf55e6bd505f863b034008b8952a89b19be79e875b76e8b9f838fb9ba81e7a48904d321433bc7b1e689b9e0fa6f688d01808bb3f2d10dc450515e4ef4586c0813f5589ca22acebb42509953f05e6123c512446a59ac5a619065e277862a86d89f2fadf4bca34608271e1d45b16406207f9bd67556b4a682af0710df9bdfa98e7f872be15490a956ca465a142570073ee82c20def557b4c7b3e4838d2e41f36e30c382600e83f0c534da690dc4976150f620e8b615527c47b7776cd41183eb6c8310f55944cdf6ece7eaee5004dae560d948ef452b0608c93233b47e76b946716ca8896612d957e077a68d5d789e6ef9dc07cd5aaedb1696a6a7cfac6060f0d3ba1cd6db17772e5b5c25618c4c9639f668d50aa60f674d720b45b6b9e20eeb84d349c1c5f261e47d5c268f884f86d474d6f35a9a3b0f6207c368dc6252fbb65b3642bfae10789da99e4e853b2f65ff373ba0ed0ab872e5c5dc5486f41c4cdf85aba02ed744ff238ae360b57d09fdf02f4d4ee47866372644127f6e9c7c276764fd238ac164b57d4971f0a5a3b5f8d5a75496ce5d136d0561fb3441f08ba63c459756626a319c2ac769eb0e8e7d39eda085053d0436166c4cf278689704bf0218f244c03cc2443c563c6bed6638a890fc28b5787de0bd3ac2e437c367e2e70a82382cf7c67c5db7072f917183a5b8e38b6e71c29817fb30447022890973aa2a15f26d2570c0d424bf4cdbf3c4c1ce880a4dfe6ffd536de8499d4a0a8fc13676d321028099914ca51c2d03b05b712337aa885258361c01bd868b58c9394a8b9b7c0358ecc8bcea2c73870f38cd4a9ff37f04eb25a9d1505b7fc85fec78c3aef616521501804bf77813960f4db413ce2e391c4cb17632870017450ef6ad5b46e0d0213b1f3992d73a8a52e6d244334f434a38d6ea5b828a761c31b04591c5eb6f5b44dbc27a6e23fbb594d24ba515f1e3d68cabf72231e8c95a1f2458c5eb1d70f518576a3bab31538833b11288cb51e00d244cb4da4da151f684552759c36398da9fecd5933caad53e46fd0b2a33b406dbbbb39228a9d48ffb03852c2f964a69f65b816bf2cc29e2e04e7b26dae414f447849c21c2a5b5a18ef24e3958f4013e3b049af077ddd807e3a6a6599370258608b507b9467904c9665e291caf202cb491c68df26f44754a0d91d72350b6e3d7df039b7106f83bc82f026a022c8856700e0a3dd409171a47d472be6f85d521d0d27815b3d42201352dd23c60dea15b095dfd01392883ee388d6c6ad6f22e7d413d344131d41da6f599945be9103119d3342538c2da5480f53e00f2c46edaff94fac624b97b864d7c61280a0f6d5f3759cb763d28d2d9a4b64fc4fca0fc6d50097d308cfcfeaa4a5c9d8bc3ffb40686425f029174f0c5d3a433e5574a14393f6f1939d605352f4a93909821d67c69a78b129b1e6fbcdbfa355b65524a3971b51e08f4cf0dd259bd8a889401cefdc0aa7dee89a74cab788df677f36b50eb1f9d514998e42b5c2a652561f2bc023ecc0aae7be052d2a4ea550ebdbcf307691123ab9ac64399049a8de5c3d21839dccd6bc0a792e11b71174370236493f0df41bab0acc13991286e3c69c6284792c799d9b3258bf7189204c6b702fad6ce96d7543648f3870a3897cd586c52e4761731e2bdb8683019db49c0ec7af625414be599cf34425663a068b7fa7e227168c051a995502d3de8c70e4a3f944e01c369070e7e3e5d6b936d3c4dbfbfd411ec1e72b178c30b9e6c1829cf6c7a03ff316c4e12481e2d3d55a65d1c7b4ef5fd5da45c9840eab368db9a1d62c10e2d13f1cb9ee006a77889d925e80ca411953b0b2eb5da479fe9ea031af428ad7980fdc641e88a31b111d7f8788f76cf8045b623bf69bbf68da0ee4240bd771d9b8cc958e12bb5d37805b86d005a88661b500ac61402d00eb485ceb0901343a032a5bca765b30dc11a703190b57b53a0ce6fa53e5e5bee68c02ab3e7b2512719a52cfe280be93b59273545a72986599c080d4eb39c380705f9c739e77f8dea5117ec10c42bcba8a5375bd33fed81ba80295d603fa51486fb2f7965b4a29534a0197076e0729071703ecc30f7c0136c9af95b7c0b14b35f30149fd22c64823ad957e371741f4c7c67eaf426c582b9d5936461528179be816658388784431d18fa511c14471f46c6ff4fa76cf80417f2817dc794684fee4419e11a2b73e4aba1f07bd5b72bd833938411de8e70481402050cff5d929b9dec11c24b212a9ac576160bb1b91ca419137ea88d878ea6ca20b2aca1f09c1fe0d5d502e2897171da14f211feeb7afc2280af609edc384eb9f298927d0a75056f4134f22102c95ba20d813256a30b9fe3598b82974c1d9c2951f8eee0f045d685c6a4458b0ef829a33d0920bca4d21c8a7566b47a37f102c9544a9984f012e2789dd5c918b65e8f51719e166c1c8002e2789c52e8f8b7a54229428d8b063857c321f2de473fd41a21d4f9484543c8994d030fea231ae4fe1fa17d7455ab8f70e91cbc1276c18f209f98494380242d18ec8e5a0d3dcb0636551b022253847cce6bb9c040b4c4429142536ec5856e08cf1ea58abdbb1e249e472b1b0a0040b53c4e326d0bc9c13861697037922210e039a9f28656b903136e86089a2b02c33bafea2d70db27043114c8789859acb49c668b95c4e3286cf15b9e249948a302e7265de312452827380ae08250c2b72f9a3806143518a95640c55922bf4e45441c905c0e51c2aa42ee72091103ff14c153c3757e46272fd453b2e82b989ab68821151cff56f2109c28f06a23b5487b6a88b22b9feb426487777a31cf47a3baf67e20cc8880d413cae9debdd55ca087239c8afeba09c9b72d0855c0dd43da163c51388491021ddab3661bbf93c4c1f2e96074020b3c2be6af0841b8a6013a03d2c09c9c0b698f0badd4d3c8178bce7e6fa7736f1d4d9dc1c075de77a0ba4aa367685ba01e1a4ae3f4be4ba410b442e207c70d0d9c7b6c08622973f8d895c2257f7cf6d171b185c3029a594523e318d1e4f2486f1af60e7841d5161259d5956595180711557a41bd28bd4433a42725d7f9211528bc443c221e55cff2857562b2b5f59adac5c6e375c59cd8e42c618a38c1a16563eb7b262195fa51871d033c0da58516cac94554a13cbd2df97e5067afb5b317251eaca8a653a1b3fb0c1b291f38971fdbf1fef07d7df63d5f8410d1db0a9b26a20b94e038ceb4fc38765fa493ab8fe241c92147c9a29d28a89eb13057c4a594598c9829516d894550d29d8241ae235562cc3895e50c3c8154141e3c532dab7ac5866fb1327ae93562c33df3717682c96e9dfa4703d6595628465a610ce31059b660a18f44f59cdd44cb914a455465a8986b829c5c6c117b029058a94151429b12adc9595135efd141bf9fe2950c8552c5ce99a58ed862babeb95c61256b7458a9458cb6a65455a51eda3f1ca7ea65866ceaf5e44710e29515c709db990c296e21c42b61fcce0a62e758d2595d890b4bafe35706a2071d37cd28ab422adb24c3504dc5ac68a8b514697f2c54728bf516b2ca3ddcd5c2a954aa5582a81e068646d977c4aa20f47a5d2c997e14418cf5811c6a7beb8e05306639817cb7c2cd33783c2f5cfbae053e6ca5c61f88fcb4d61d6bafe61a6e3e3a630c3b96e0ab3d5f5f7277e6ee44ccaf5276e587b52ae1337ac4b629cb4e947e3679e100900a3f70ececaa3e3a0534a698b725dc332eef429bd71507429cd329a514a29a59f640648ee002e27a1a2757b037547a58ecf061d7abc28102db73f7339f8ca5cd777b2d72a7ea1ad2baede7c2c0e10fdc99f7c1d6089cf3306e0c2cf8ce879c68e5ef4e4e92bc2f8d39583f52605967e5857b71aa9373be8673a3e9cc38f60c35a5b3a2e4780cdbafaeaba162283e74f91cd554510255056154196b88983b003d5f5efeadc71e2b1cd5adc4b2fece112316fd7237eaa33fe15c9ca4db42b0b74f29c57e4e4b9a65346592b11079d7e585f4b622aa6a182c010e4fa579f28e9ca4d6125723dac2d236eaa47dce4426aab48ddb1617dd55a635f8797dcc43e88b8897ef2c18e013e633fae89a7af267e15e6a0eb8835d72b0f1636cc60d7bb27fb71535337c5506e7aedb8c9e5266f205aeecb154b10b9add37213cf1013d2c26637ec1a1f505ad8ec9565f55579a830e4fa57989b46d75fabd184f8e905567bc44865b0baa4c660543872430d75438523dc500be273fdb3b0ae6ecfaab403cb0ed2fa8a27caaaad97ebf5aa3118caa7e6facff5cf70e249f4fe192b5bc593e80bd9312c75d4ed584d3c098061fceb0f6aaea829d557e7d32971d3cc62f665446c585fd507477d5d7fb087eef8691e711887e9968e9b5ca6f9b3a7878767674747c77372261224376c1b9bfaaaaf5a6f1cec5f1919a1401e031659206149165974c1c510701046165984114626856c959436ad1d22ac21383aab66958a59c7037a60198fa592118d8412a640cd1ca1b1e03543db0966b972a4034b03296e019c7976cc212facdf9061bdf2664117d79b6019fcbd55397687d9363fe249f34f5569c44d51a552cdc92de12e7671bb5ea8203c1ddbf9a18129b9fe5c5dae964da90482239fb131220efa4ae56096c92fbb19fdfaedd5aeddcd953b7be6715d90ec766833ebdaddb53bab2f29a5557ee609116b880b2af241bc0ddb5ac9679bb061bb6e6ebcda051bbf6fa0df2e07bdba833b9ee32d6739f81feb4d91edb9d809d13565dbd552d832222b371971537c97abdb611ad4e2ae1512742be0eecc0afe5e90e0c6d85d7b576ba5747e911569c26ecffd48d3a66ae5a0919b2399103e32cad404f26eb19092a6ac40e5112bc448a594727e943be04020909be229fb92d7284deea7d20ee6e1010e96ec0d2cc8e0c4c183fc521c8dc007dcd83f224cc60000b76958226597b021019d526c91c4064f743183264accd0d51232a03f588206597c1f616a40109df0c50a6e1cfd4b0d2637c5e7865faa71d5e544dd3f343bcb961f597090a879e5e50e8746a12e0be42621587fce6e61fd434ec2fa2381c65533aeba1e9b9834a3d964c9214626199d4be6fca61858b4c32c73cc5541a4a185b40cfeb1e3113b06f88cf69cf68a33f3597290dfdfdf0ff23aacd5f5eb56abaaca8eda7a4cd3d0392ec4f319ae827c88a738c3ce6ab95e7746ef5c7739a8d3ca691afa0aee2154213d54647bee1cd033db67c41133dae737f10a2d0500b919185d22b8c98d1f0502a25908c11fd22f22de22b58d0032937df5369085f3f32dcbb24702c84cf621a1fdf67d42f0fbf471a3967d49648f44ad59f61589ac3e12ccfdf5b31186f91e2181cc1790cbe8201d80ccef5bf26f1362a38c35e8cc8f273953e486af84286e18c3841b324b83c10dd9e5451737e4d79ce10516377eca287e299e803ca14410929005124ca184d44cfc1ef1548117094c5084248a58c1c44cccc0cb49bc2872512e27f122053ce06e6e4a29a52198cd8360f7d05ea32cf3f372afbd3f6704ea87831c43fd883025b08dfa118da841ac3581a5f34796d950e7aae0146d4a9972ffa8d5fd6390717e1dc5130396f0b018eae76387f6db871cdbc08f6e41838039c0a6387fae4870354fc65903cb9c837ecf0086f1f711c411a03dbd309cc38608ad3860c518b3988d4623e67a90849060bdb9d8597767dd31fb58b9bb6528c6af66a010889981a6ea6604f25a332cc028860ded88657c87edc6166474341ad51abb1e4c9eb36fd49792d64e734c0cf32cd58059e11c0cfa976274d810b3e26e4ec13233f7f4c0572ac912186cba925cc103b33b1cfc763b643eb76374dc242915c12a12aab5a3d13f08964a42aa1dbd7d50f4335b73f6f0f18386071dd867728f4adb677ab0cc0b778e92e0f6c08bd1a41b728b5bdec7b7e1bfcf97ad1ea21f284437b3565114f68461ae7b61ed1aed8d5f77d73ea4d62aa25518d2df846429fba5d603d0b066f82d4b3f5fbfcfd29fcff40cff540da9b5dac898165a7c5a68a1058f1625e879d9c20848d8620b1e7c91451649086389192c66e6549c0dd7e2763822dc100e87d3e156dc91ebcfb44529a5b4a77866e69ae0d364a99ea3c1754e8aeb9c1377f3d97c6c16d7371fee33627b2033da17dafa85f5b71f6ee618da7c1cf499101876fbed9b2c9fadc55b2b6e2dd636597ddade00575c7f518b65e8778a53cd1c8719754dec3627eb091b72aac9a93895ec50568aa1712acba92ea7e2545936831552c1139ef0841ee0c00ad11150df5f8bed5ddddddddddddcd6ddab55f76fdcf7f700084c20b485dd425bd8d00d7b35819605f58ab342ab5557c70a384b64d9b66ddbe6f5cd7860fd7b05ead58a655e9a066cda9a154fdd87be7bd406daba1dd973bf7d7bdbc6711bb77942446edbb6eae0f6dc757f680b2bba61afb62af08038fc4b55d0f1462264f3012446cf9d3ef800e12627725d0c9eeba06e0709c65a432250680bcbddb0b34a433f9c6041df3dd7a5dcb47d3a38e80d60e9ef3e1f11c61b30453f4798ee3d161f3dd301d18778107d2cc4f6f5b90f7d35c28440946ef6dc4ce609513f0472c3fe84f861730129d2fd04dc549ffbd083be05b04c117607a6a1df5f0796d93ef4fde83e1f9dd01656bb61afb855afdac88d9b46af5301504796a4c0cee956bb9a676b9e62fa70d31044d4db2f4437b91bf7dcc70ef68f5511f56e1f4b731e4b7f2dc595b1e21c26b061a756ecf2c1839bb2e7981bb4148661fc3987ff8ae250246ed27ad520d09711b18d03fab05717f4855dad40209b9410d5901c075b3a2e075d9c8f4ec0d2ef8d0aab815fb2392686b963bbc4930118c6ff07ad1b5871436ec98e5e0d9ce3000ce3af738350854f7fb5c6b48ccf39a0b0f1fbc398989839fbc5edc3d6cc3c79e278f911e872944ac984ab5d2b57eeee089ab4d2ba39ad5ba51288005fce4277d997801f6f6aab6ae6484423a094d266e65a9dc1582ac556c7c4989898181da1efbefb8c38000d6b26f44066421b13a30ad1b801b8221c19b6aa6d3c9794524ae7f5d1aa19d82132edebe4531011c65f030c7a1ad8de3f88789a0ce3df6363a5347b970ed626d821e4f5ef1ee2a0eb52d81a140de86b220ee3350b3847132c838a7354256cb53ab00f88f1a1a273c8e69d169ae05a31f6e05139c8f7eb5a2ba54d388e638efb39778ee3388e7b8eb5b90486712ee60ddde5bef9eafb381f00be0731b049b624cb8e21fa7de70e9268f54d1937713819379c38ad71dbff8340de4c4d2153c53986d838888473b072600eb6f6c3444cb682b9b2d9e804a2149d1cf31b1566846a36ff60b62927283254be94910c10fcb736bbfc732915d913149452e90404ed176331fe30e285d5dbd08461a47d3614d79ff6efd811e7f8800d4121c74e34d5cdac183ffdb839d19b08e3fdb4890df96098f925fe31df6704bbd3b9c5ac1a93b1fca68c855dc3de9d080536e41a198bfda0acce455dc0a0d6ca959f65b8a585c420773cdcd9b57da11c580efe1b15b66fc86e186fe211d7de27111528129944a611ed9b11e7c369138f38e8d626c6f9f4c378b30aa714577b99ad588673d05eca901d94d912b8ad06dc7663fd39f7e852327fdc31c6edc67677b711bd018e5fc2108c356f7c99e32e9dd70e4cf2dca8f95c8f1f8b6bd56aede871f868546bfc19e7ffdd428f0a5817871b57be1cd49868b119a02746232dc6c3baadc586b0b69e1a232e6e890a09cb38cd4aa7fb09096119df7460c311d70f554b83c3afc55a11c6bfd56ab55a7de54b867078e412a944f6f0f8978dd2c68f654e07f9b26e38aaade9b0cc12ad3503f4021b6aad988cc91ee9e3a6506b5d3785201e9d0b6b8d6ae88166a5d33f42709c0745a41595a458e608cb7869869f5aad1d8d5c63e2a0bbb490001b4a004c83960596c19fdfb558cbca979b785886c64d7ce52b9eb45fd9585595af987cc99716932fd9e23e838f3efbc2e36facffa41ed8f9451047a2c2b214216ff642886ef6d10fb5d8f5cfbe22fab2e4d0777edff9fd107fca2fc684889b18247fdce4517af30c85602b3720b0b90c84cb391fe0b9dc7663fb55e58f9b96f8a94a2692090d4d9020b7854c21f7e7cec0ded41bbf6ae32015761be2a6a60faaae5584d1b961bfbcbf4824acaebae3a7ed26c2b84982e2cd76134f31debce2291e4172fde5176f6a18b63f6635f145cff671fbb841c1f667d996bd6759c671918bed5e4310c7f6d9735bf6dcb6719fc5df9ee3366edb70e8104936b0344302852eb877ee9d7b7fee0b5babb5dedac925ddd8f9f4c178aa957ee1e8725b37d7931707b58d7e8965fae9b3002efdd9ed3041b4c3c19ffb401cd8e1e0debccd7145f4e53e1cd2fd4936d8e24533c510cddefc32d4a55fd857b2e460af7c7b04ea1251a5c737eca13441dbcafeec76943847f65d1336cdf528959329a5a1f0882b6538c36d196ed7e4f0b901a3a172db4777fb5956d01702e1694ed8fec99190581e0d844aaf6b3a96fe6e6820b7dbab7193f4e1ea6f6f8e3ec4bc1e3fe61297b8e4d525378bb9d94b2f2c952aa8697cc4185def923a393b244b3653a734f4241ad86ce51271c4ed4ffe38987d5f1dce91a8b0fd38dc84c341f90efebba22b7a6169730e3a7378b9f3eb9e48c244e76eff57e37edb364dfbf0448b8930da77c7c48e73d0d6cfc5c4b08c5fed21e08301f85435ed354dd342eeb99af6ed951cd44007b5b086e0d53ec65fd3be46ed396a1a173b1e7fb7adbfb7edeb0f11acd0c11633b440b1cccae5f8912347fe81fdf4ed176e444d0f5ca6fc09d14d424983c8c5ed8f30ced1345aee8c7e9619373ee9c6d740ee8dee8c8ef3b95a433c51da104e4e9a55ad6a363fce8a2998bc91936635ab363fce8aa90893936634b3f971564c4d3aa9cd8fb3e4946df323a3bb8dbbb3cb03b6dddd99b942619bacc0b0695185666b8f72d2ac6a1b07ea42227b8222234565e5851169064b0b0a668392372c3348a3175668d470a1115336d3055b51499181325dd7ffc4e55b9c651353275614eade6349d19f0ec46dde3723f5b3d5cf06a99f7f46ff5753365e40b01b6a37b01b56ae767382d26e644bcbf28d15186a6c289524387e899379d28f1c63d4989f3a617677f7373f7bae524a50944110f1c38846f96923c2613c08e9447c4118696b848341fc20c2060d9b52e86134b2967d0797fbc324627a08e67c9fee737e3f383361fba3d349635f4166313a3333c8ccfca204cb1fd282e5ff19ff59267ba60ad48dcfc9e77eb96a1ae447552d430c58a63fba02ced115e56aa16c0caa54ab457df5e20dca531126fe7fea7aaf6ebcc2869e6ac59b78aa2f57371187cad58def1d037ca66aa9b9d252551561e26ba9f85a6a0879c3dabadacb5255d3a029c132ac80653414708ef85a8a7330c1c6afaaf885eca3baf1e3fb122ce01308056c9a1fdf9960934c81427bd9e7a6a12f4f4f32106ef872639c41693be0b2e797d0bb2eace8b92e0ad663e1a7575e1e5e9e6b502765d7850d4b5e77b78343f9139f9ddbb1cb49beb0b9dcecbab0a3efa2c05282a5c4eb721928f3fcce8e43273d78c0974bfa170f868f63d8f0afbcf1b2bf77dac527f05f3eac57e5994f31a8a8905e3cd27bc7c3cbc3f0f275e5253f4b7d50bd2facbcf02bcf3528b4f785e77e017c18bec6733cc5f0345a1efc183c183c66987f8e6b90e7b5ee7bff792190fb7dd6edf838ee6ee8cb71cf832ff7b5ca0bbffee6715ed801d6e5b7f1dc01b9d523625e1b1f6d19bc672ff4d6f55e47fc3c1d0e7a9f0defd577791da47cf771d0050b1e7838c2868d85a31cf532e51b0b96e12b9fe5bfc717887579fe7470f03dbe2e2eefe2f970b055f1241986f4a04bfa1a5f58532e89f42d9f0cd96bbdbeec20295eeeba21d8b05ddaab3c6b39f00ffcf539f6bce3d14d2eb77d2c3df86ecf83eff6b55461ff6a1fb92fe42fa2eff6321ec88ddf3497f41ae93b791df1c5e3991b1ebfe0857e57bcd055bc146f0829a3692e3b78e260089ae12398e2271d35ca94cb5fc83f42b0edeaaeb061e35cd9aa28e50ff9b57ef2b91a587e1db13edbd048fcc5e399ea79ffaecceb4bdbf52f107603d9871dbbf45132583c31674fe313e427a36f3b1d805cfb2899cd44f124c61e50beb06fb01f0e07b3ffe1ebb20f81687d4a43317e30a761ee3e7afd41c041fa7d45ef5da5a1c681350e0f123785fcc3ca490981c5531023213a62ca107d3be5261e314c7c0aa236f6401f022ebfe9bc86158de21a646d1d6ddaf76019bfdc03f1c34df1b9c7813302b884cb3df389e3985cae724bb8a0e4b292cb49b880ba5c77f5fb657b61894a6fcab4d64ffe70a07a92e2207d4ec244c90d632ef7f363dc14da1a38933ff73d38e6513c4950e54431ee9d7b3bfacad9f3f73d3a7bee53dcf4c2d11dd468159ac5d3ab8c5e5216109b543e74940fe9e5d06594cb2b33b8d8b17882e19fc6733cdd78d2734c79186e781a871435bb1d241594969f2e3f293f7fc6288ad2701ecbb28e3513076716737066a4277dc79a899b687ccb13ebe0bfbc783453c80fc1cfbc3c47c353798e515ad8e531a60666710ebbe20c25cd888ffcc06d6b9f1c54cdcd5ee5391068ce96ecc9aa8e74c91df9923c12267b6a2b87d57532cb5454a4fcc0ec7b699ff6c93ed92476551ee56916bb1d19334a8b8b8c14f9328548afc88d7ff16a7ee6868eee4c9a547efef4824d28dff2e104c3e5c329853b4d291fce2bdc49a26122fd7c1b545050b7bfc5a526c54bddfe1b9e90ecb3a4a7e1c52ee95b725bfe843c04e2a54545652e40e551be22fab67c381c0455be170727084492819d1b102c2769e273394913d8e53a504bd93b644739994ad91ff6759119b7949f757056503db1f19c83524d6b4384b58f0f3791a2cb4d2e2e1e5f1b5e8a83ff32f8dadcf934beb092be58caf7127561cd60f6289e64b0bfc5ab0cf6bb782006fb657896c1fe142f85c1fe176fc430fd5c37d31862ce91fd9d272007ffe33bcf13eba090222f8f9a2e51d68e46ff20d8ddcf51af6e42394d5884992f5b5e3ea8bb1d35fe3f96e99f6f834d2e3f57acccca33afbc8b0cf233ef86f7e2c916afa565a70acd6ef88ec51c376557b67861cbd36f1bff3cd7fbbf5c68c23a36ba615d2ef91d932e99aa91f23b4a103038a508244abec0539209af91277094fc80d3d4e4824f121561e6d7809112e34e55162eadb9930691a86c3472d0fec00e3e0da988fd2facb70b415064b85c94ba28a594ba6cb88e406dd878fa2fc3fb859f4a96502fdaf022cbc1e949eafdf03f445f172e30b9f233e9dd80e1c379f9252a9e60f822ec1bddf8502f1f8ca76560f0fb088ba78c613e6619be9fb1effb220f8fbf5eec2d3038bf09f3340d1d6b19e64f266227b873fadca905ce313f8c5adc2989b8e9467a805149d42871a7a79ab8473eef4369c4fbef698a8a444994a49135b1a5b40e4ecfbe7fc8beffd8411befb2432a6233216efa9f9fa9e2e9e5479fb95cdec617d6f92f5e44e230f33f2fb61c9cef795107457637d02715b1a1e5f1c3c8c1f944f44df981f638088ba7f69121effcd9333dca664affbd88e3e0247dc82c0fe2359ed338385de66b5fcc71704a548471949497f111a6f2f3994e1acf92cd7f1a1cf7e1844d189d347e3e0d7232c3ce98f1cc336a7892c693350e4edac1e2e9eb84d1f824cac1f9e18485120546bfcf7b7f6cd8ece9b07e4b8bc797c5e33bc31ba2495e389a3ff242d00b5e385af186c8ee1032c50b536e3861370545a2eeb45f5bd9d914f664bd3263b9388f2347ba9febd515657555573cf18ccb555dae39e4ca8eac8833e4469c78ea72aa0ba7ba62abbaa2eb0a914aaaab63551715366c588dbd03ebf153c3e284c176dc945ddfe1895f75b9ba411d23df4cc6f81cbbe55f19bb9fda85cca93a3b353033ca86ccbadf63c6c713d7ec1bc91d2d5558d9378b4fa9bc33b27e742468858d2f4b978709620ec3d74f80aa1eb091b3ef306657826cd61c4355d2b734bec724ba4702d481ea779fc4f648689ba67df749708fc4c66ddb779f44e891e042dd73a00e34c57c695d7e7086cb49b600bb610d9cb7797d625f7adc9f9473ce29a79492a9d72b38e79717909bb4831d17dbf902f1ee23703ec4d3fc7e57f1cf0de517a5f6b2471c8c3837de708e185511a65b80bcf2930ebaea86b2c5803833bf7913615260ddc64d44dcb472138559a16a24295b7637fc8ddd4f27e97eb5ddf165741bf43bb716985c7f8903cc75e5ffe5442d290138e1681922670c720c83e35d45068e97a41e58f9fdfe5d72a941a385650669f4c28a4a8a0c94132b0a75206ed332156474faa0da70043759071fac38c2128841a79a81b8b099672cc38e85f6b200c7ecd231b2923592a607c642128744556cc98eebc68807f11a9f9e1c56dbf41026b19077ee0ca5fe6efee6ef16c5980d4b3cfa3adfe026684a1965e49a72f46cf29ff8702c6de41026b1d7ce919b988a4194f8b47288b88da398f0bc708eb4904efd00c13a3db6176edb8195cd1d43fd99141cc72f0e52806b90e3b8eca16651941d0f6666e61a98bb9999a315dc88b95300902b9f3b098e5f628cd96539f252b86bc92507834814571cf1aca98d83f3a65faf3af9c125504afc5cede512a9ab916e9fdcd62e4b7c79e76d9b08139d87a8444d56563d0b991a110000202000b314000030140a874302a150302215d5cd0714800c848c4a74541ec9d320885114648c31c818431000c41000818119c11407043406eded9961672cdbc2f55f085e63545d2677c4b4e5a3e9a743afef616495b0219b15ab2d20c50063a02b6b4d831545b1a2a6fc6a6e3dbb26d570b39b8d022faed469e2c2f949a9dad15042f8dd539423b18a12f23833acf499fc3fac2878e82a5088823e37c95bcac7c5b012887a512660a03cef89558e98f16a03cf1f8d37302f174133088b509f30bef2e038610b28efcb369affb1716e4974ccb50ea980a8e740567dcde06691be5757ffa41f27af3117684896fcdee53295a2222e7fe593cd3dc06442f45ebe1ea1728c37b7a10716c65e0501984e4ab0769aa4b81840be9b07ab4aa8bafd426bde0e743c1121254e2992f4f510c2a856a04002dc936955081dec5dca0f5f50383ce438edb031749f3314662df49bd8250481334aebda532401861044b66149fb3c3357d802357e14bb057f1168cb8c748fd41331f46e4bf182034fee7b78c6fdecae67bcac568385551da1cfdbfa8f439b3c0c50b4e0ab9737144ccbf6a8d124ab50305f3a008dd99d36f06b75c9d93e96dc44306058a5a15411069095ea599836fa31f26625885ce3ae62add7e85a3987504c3269c60ec2915373a84003af09ba4e7082b097ed7677669fc43354af58408a18096ca91f35adfe8b77e0001004d20d9a4753a58a6409b0c5b8a62334a551347081c74ce7e8fec120157ee755046e2bbe916ab91df40bdf47a7c2b4726b82d8283d8dc238301ac2d2b2e507aae19cc283134484aba03415a0441ff010ec56fe89e07e955e26abdec507acd1d31e88c80d90d686ad62caff3f0bd49d5e5d19c18076bee4d98e7ee29201ffbdacf5c6e99d938267961d5c56739c064ce5babc8c10663f5502b2691cb9586001aab51d913df7cf0dc74646a7edf5d412deda11e65c05d6d806185ef54fc32ae358adeeccc6fff642f5056e1774c58e0251ba52a8c8d1c874daeda0135534d199817fa6422252884cc489e863b25e38c9a8d343abf613d462deaffdaad07ca4a2aea204c5009ef94fb6324a916bcf469d1318c88788b653088a9c030c581ea2397ca2fb67ef2cc6d2ece8fcea8a5a700332a14ef4311232b8c0f19c1ac111f157fb9f7b053880f52cbbdb7426929a8fbebd3f80a4550559db31343d6b1b89ccb95868aeea10df8360f02b69492c608a866ef491940c9d8bb5a1205893758d26059a2b54f46e43ec62277cf72d0e5ac143588424c76413d63ef2e2b37bd60052f1346b083ac067fe294d8877c42e4e589780febfa4385268d2d3ae0d6e5d53ef1ed009815a3f0687a282135c0216d9a0435840118683e38b9d46b058f6bdde4c746e4c433b9a1c4a78dc952648f24939773c8bb8afa0ba2e9346d987d2738fc8e6a5c5663e1a004916e338c52a27b0e075ede1d6c3d4361b6643b6c2326180a2810f845681381e08dd7f67d99ab92b8b2100739f3afd40c13185350fe2fcd73e2521b152ab805a98076e3ec99d096d86dfe698dbf306612af032778b98a48137c1ac85b87326033d6d659ed3f5a046515dbb40c92e8565989c88402a77e10c8005a5f9d47560dfb821b34620175089fbb9ba1f09db1161cd0b935571160badb421a67728050a1448b5c563aac4b95f9ad29622989724cc27f71388a07bc2c3ee717232298810fb88ab36dcca94d580b62ddbe2464af22938933c4ba49ffb7867fd4b82358a32b4237b47002389f72d50561e0edcb2aafae1e9bd0e835aa9fcdb513eef959b42f84646d882122ec944d1f75e2aadf38b32dfbe453ca48023902d901eec5f39ed6f5fea7d9578855597b68bd109f2112b011aac5a32556e9938eee1b4a9dc7e47b0fbafcad0fe077a9da08a0ef56f3c73aa5f0cb2bd8efafdfdd49846ebd9620e9cd02ab1248d033e8e7eb95632c45384f584633bfd56ff83be35aba0b2e5d40391881e4a64b5c67e98a07bd6a25f92e69c52584aa72c11f86b3fd698232c5269256f91c4c99990893bca5bbd6a698238027a8319e68817cca9b2bfdc974143118800206fc91055c4033f08398b4a8e0793d6476d8bc163ade125cd74ec40f4d4e1856eb47e1054373883e3055aeb714ef0226a904620fd2832148b01f33fe047065c42e96ff14214ee85036acba0aa2f1490d2f94e3da8d20b0b505236d7042dd64dbb3890c6ca832af6395d54aec14331cfa84ba5168151bdf40b42c94a38282b41ed2786868211e8ccd244be4fc65ac440ab8aaf2c983ad2f511fd23e0814f1a46d2c9021d5e450fa982547bf84f2e3417c46766ae816e8eb6f100a2e761670a50e976d8b81df84f6adf87bc817e8cfcbcb58cf514cec07b27f98dace801bb694c6ac53b3503a0150c21ada1091ee98b7f10325a241dd20942a070fa5a85133281f7a0dec5ba3fd1aa7fac323b9b1624783d42862ccfbdc8ee3c18e7506c4521f313ba1a1a154242d363a42d321724dc62dc661f284385ae7aa85b70660bba0449cb7f2cd2e0a0d21a27a598a95053e9a1bdf9cec38214503bc347026d5c515391f95d27ef4460da500df7858fe9db6a5c13f50ae618cd655d40fa6068ee21c6b71b5cb9505e3d384dfc1c9b333e5770a62ac6f43a0d7d31e6193b1bb316ab1d509dfa3493fbc92b389c38344a8670e0fb9d6140ee021a052fa4185925835b8fbfc9d9cd6b9b4488e6b3a648032e74a7ca34896d61bce8ebbbb21ccc322df7b659fe21867c0c796c46e4e2c91f08b7a85c017f139dd20515a5de51b27351e08eb1eaeb4d7d670c62afbc3c95ee24531e57fadc6871a01b77febf1444d66050ae3203a2471b296a91df344d37759e4140d0e526d1c9546943d54c79b248390e0de13d361c221e60e2b8acf4399caea85f820c5522f6809ed4e98c105e89a1f582809e16a600a2a3aa179ac9d51cc31daea19a50563a08fbd14b56cc9e1702ba813a01e3dd5cd596cd529a5b386220049b640894109695744359e8871b42dddd21d75d0159f56e2469d7294c00993b7ce212d944877f3d987077580e5788fdce91e82afe0e48a998f3b9e648efeb4f20b0a1e4a071a38791ae4ea620983c8627cfb11f40d87cf2ac421d04afca866fbed1dd302748c453af3f92ec062d2a7fe7496921285af26d931df04aa586704a49d43817a857e44ea091e9c20445e46b4c2c4608564082df79ea00e49b1acca3d5d792893c379db0d8a8cddefbf57a7ff4b8c915ec45be261c1859f02ed960ce815a24ca0e32cba3db626420c7e9edd4f9819241be94ce6385f8658c62874e2619bbf7efd8b782ef1e94ce3c2ef84f429ce524489d15b9efce82fb8772e08acef51098a4fe4917baba3fddece788146e6f4f6eceed19dd6fae615f33a5558667a51c426d5bf358e754b1dd690ab52ea43c10b497ca9c3dc2f22e66978070aaecc9e21c310710b200d152936565f4b3e383716957b391e76a27c0eeee8587b9a64c7b23a73c678aa51367e43082bcca6d444b2b66ef33d040477d1f517b1664818e1de3e3673e6c5ba63b947271f4ed8399ba43fa78cb6beb2b150afbcf5629afa4232cc09045ab236a21a89fc1ecc5a95ac94c28f0ce8d5158bb7b964b62a9ebc93947f47cb6a862c4718db8d827c432fe9b0f7e46c70fd96e5090aabb5fef6949c599c7f320a10719b4613cc4ac33872e1bade10145f235ab4c90dec183fe9c4e0246ea8b60d4fb0f82cfc0028071a6c95c4b619203dc8c2b7907ce34f3421c0131ce7ab81699a7eb20d1e5862da882df14442772a4318c375d5012f5a11e7b68b5a06081b359ebb0917789ba3146cddcc608edc183cfd10bd5804fdb1394205b0e7b2e0add3a9f089e6a2829a6ea736becfb4717e5bab3c3a27b93f713ac72356ea25aac3fda5e3a43d4e8da4755ab17d250f1abae9764c3e01a6b115eb14437a6153c2a2b6130d8ddd40f982a89d59eee7b171c885dbad877cd4c8766b98c0b1d17de41c51296869f2119520ac80c4f4a19613a8683f017bd578773f7c24064704a935cd2633e7edc2820890f14f087002f03c091e1dd925d8de8622823974b2af2694f8e2b4772220e76c2bb2ccfb651f9e7d0345e4466162cdf60072fd04e5567c8fd40f91faf4b1bd00f08b85507f7597aea321b66c93bd895bdc8e22edce8dc473eeefc049a7734037a5c2f532a81997a1db7a00e5c6cdc5d03139c72f6936bcdcf3030e4c88ac2f348a2f080268f2dda1d0c6b9e8e223acd64a4bd8c737f8ddc3c391932d2099999f59a6091ee76f5d0a2a807cd1b70e47970e8d018b4f135da877aa6490b86a70ebd0ed012e19beaea67898401367369222c918b528b24d004ea0898a5a8e210dc9ab76a6cafa3dd7481f484412fcedad294cfae11b972c8fcb6e5743311d4c3031e2486e9b99756ab432efa4c0aea99d5eaf88bc3b204f7c7aa43988f4d2903b85369091e0969d2c9c6b6d9566c83bc02b7c0257c5ffd857b50c411a38a09f90209c668ec6c129ec4eb09294f5986e158ef05897089e8210b55574744fc6d7988076862707431a2ba274f67332673f312d03e838a23063abfdb3c122c7a392eaae6cf4c52c6779b023b8aa1735807581c00c3771b1d1ba49390d465d4c2b74fcfcb3b30567aa8b7d47a42562181f329c1b04ad123cee07ee7bc54376ce94b5463279960f829f0c05656b38f5319bce7ea440c1494b1ddb674986eccff0350b759794cd86d2a1db87f50ef5c1128bd09d6ab804e8d471393f3afdfce61321685a130bc0d903e2137abb01e2216d34ecce53a5b5647b24b1282df09cfa1d914504f82cc744f4c5c0d26db347d038ad610eeaf0332a1114e3d36c98922eeda6f7672a31a7eb8f8edb04b5c1544f555ee55631b199fe049c76fe40266a24b2c9b7bab33bf1b34944ec51d1543ea49e0ee038c824580526dcc4b08ac500dfcce75d85431b26c4e36539bfe5d64865d704cc99cf1c3631dbd7993f59e104354846143d0326ba2ac0ce993f3b409438ac9017e588676bba9f982717a0e77e842c53f691255a2ac12a9671afcf38282b397d6a9e6aeed4454936b98bb9ee68c896a2d89cb1b6ccb29dea1d4870810eaefbed44f5e739ed6d69cb96761e15ce23264143c11a6d5a2ea702eff80c54545b02475069cd6016c9b49635748b700acdc44b4a2704b354c96af1902f7b3da2c4748e1d498a14a8c24e432cb02422c2c0c698819d24792c171ccd8baca2b70e3f452ab8abac2d900ec83fc7b2f056aa993bddbb408dd0c34cd765d0aae89c2eb51721d145c87a2eba1f45a285c0f45d74b01dbf9d6b3b82e289daaca912564f681a1bba91f722752770a853da66c20876af994349becc6e97a166fd11bf3144049d98870d1c8a45ea44e6dea4ab9807909a84d87a43512287220030c0d4393d255791c144f7b318aec65f466c545188dbf41d5c633715439209692a9ec4969682e0b95508b04215e51595ba664e04cd8e35fb2f7443542bfe7bac503150699c7a7637a61b6092f8cfca165beba7496d202a995151d563da8e0aad65525cb96b1425dacd7672084ffc2a72cb4a120474d601d9f6255f7c5247cc00a3eb0faf68509fa80153e602d1fb22afbc3647cc22a9fb12af6a7497c14580d3e19e7a3dbf585c8dc9a89d0951f0a0891e101b0a7c0e773c7a756187c2b99c7ed083368e01e727af7a3d341c43fa5f5f94cd651b3a069b87de1e98799eafbc88a3229bd4a938cf98c588e3aadb8cf20f4cd09fc4f42ead66544c1ea7c8419a66f266c4af26648212e47cb440c07465c07218e43e1139689780e82980e8218960f9f3948621c0031c8a1fce4a398654d2c5f66cf276db915d95c43085dc60142b07c67d75822c2038de3217fb9750c2d41dce4eea1549e641511433d33bc231fb72617758a82ec6b35053deb5abfac69b710261513e97f61820bb5a7517f4863180125e67bf32e135541e60739d5daf3e2693dca0ef4ab694328703845550a0b667d82c49fa056a00644bd9396edf3e313b3963bc391a22a1815ddbacb28e01e86af3b47291373e555a0209bb396d843bae22b37a1e90a7789f7729a513c2c9b4d3110b4f7a1bf92519e32cb44619d58dbb2f806938009ae3af9bed6e38402fae0035f63c467f1f36b5ce2b2b8d37aeac54335acbf1f954614c957fcaafa59077c58f7efd50a94cff75343188e490e49b151c15d9f9531b7f391982ac45d4190026230d6ced54c44afe2b7f276512bf969990be5f4495cc17ba465d50749a08266a24d69d6e3bb7ab5b9b9bdf4ac6c4947c7eac6b7a8b194a1a11ef92ee65c05906824f765e00a91d0ff46f6d0e56ef31d290dfc2405e8378fcdd901a20723060fe0b7dccbd93e4d982e42ea724b385aa83537f409b95a379ea4c8a6b9361e0882ddd6dd61aa844ee6049e8f2f1a3c78aab632890830671d8b628cc0f1bd532a395885f44ed570f8956fbdc90c391e2e4598281fd47c6b533770de563968ef12010f001d6bfea28fddc41f48781888cd17f94655b94dc1ad7191449275928350c82fd7f81988f9e9c9f689fe6125bdfe858b8ca264f743d9947ea93a5403cfb461625c07a74186463734c33990c17fc9606d3400c39874187cb67232a4bcfe33d177c3951d803fbecb6062b2381f7d50261691e4bf30973da2299ab8c8a113cf80f85c0e5d1a32687d5445b40891154b37ecab18d1a5df67a76e9b21d14e746140245b23274cc0d4916cfe76a49b296902f238101acaa8120c96e02fc8e9c7c61e3a37f103830c40082e6ac58c3406de8477c42394c10db95c731454cb8b834cfa27b6d9258c95bb5ad14cbafee3246fb7fa33e923ecfd31aaaacff9b6b57f3e33f94f77b2a251d17805d19eb52d3470a36097aaaf482650ed88888b1b580f77edbedbc177affe0e6264e2f918983e46c758adf55348f6e342eaacc0bf9f0110aafe80f1e4b752df267578d157f8aa06c820c381634c53966a08ac33f1c43a38827c88cc347c29aed4e140efc21d2834e8d53d49c97b34efaff39e0c7a0ff4bc833c5ea267af50ea6b638168e47fb6844c302bddca395cf43f9fa460f8f969be427c68889ca63d7f085cf0e49d8cc00a52c01ed7cb4a5d2c00462a0ca8bc616c5af01a60c9e20a2240cc01cb60b6ae1a2a59199f302298431de04076a49a687834f304cd4ba520eec844318fdb22a91aef4900a3b5e68ca9c9518d3a3d3581b2068a563c7b6f1cbe525f7c4f55a909dc1b865db0020dd1bfa0020dadb57fee08c215a1880a63b328344f643d3e7cee862af51a501a8ab64a14e292ef24f68670fdb790fc3ae020e63965c1dcd42cdbc7e9be2b687dfdd5b3d2abf8b8c0118d7262aa1dce064cde835dee2c4cfc28343df22af75c6cc27655cbe7c6e46ade0a5c6a567aaa60a72f68563676e63ad9ea47b8f10983ab55fe34468f48022ff2f1cee306320424e3e9db16402d218ad97eb55797dc880b74b67b1c3c7715c9309e4bd4b13a1dc3ce3f8b58056c45f7efddcd00090681b57a14f70c446eba84a8d6876e202bcbfe152b6caee48f2922977ad616a29e176cb0df4209af4b3492be23eaa258f72d7d4d9f300063e419622272565e24b7c71ca9718a92fdf419dfa554fb643da6ac730c24b327e671ea400f6ce1c04f6d25d4914cc559e476ca0285f368d7b4e9bcda0690ef67a0baba5e0931e8fa70ca6dc2535f3c23e6b373587bc5e6683e9e835218a7efac062a99ebe123d698d542d0865b031a3bce8d1cbb8beb1c6b67768a86183adb1429e610b16ae631b55dfb2752eb5edd3147accd3c1cf363fd9c0e2ef2c149f7a92c60a79b9a042dc85386837a2e132c33b76742652ef2a0806a597b6dd72878d025b0a8685482b01910d5ddf83d1c41dda9a5a224c585da56adf36f78827000400bd3b89051a2169f7904f8cc9657a9547a640270682550c14642c2f16ab0308a4b8ff002a0b0fdf47ac4b2fadde43f584935567878b845028885db97f5e687a57329972a989cc1f8003a31dd5f590aa283558e2ccd9c52e8a18379443044285a65d86befed087131d82a328c49b7e835f2b9b99c73249f0ff3012bf91c7cb84b269f775059be46c528edbf9a4abd99227e82deb63cca54b112b2280eb95dc3ccfca1a05cea561132713b707704dc48811e62d6d0445e21d86c2d1b0244e0f47ad330d8da19498ecf4666879808456cc6a9839be2f5879117041df885da9835d2e8a06a36b2b7349866e6ef28a5bc398083dbd441ca76fcf6488a34200969bd5a73ba0d5d5cfa1b61023388f31ae9993283ee39b99f764a3b807cb41eb4222d2e17a72173038ae4508bccde1674d2f3e1d8efce301c723375fe1d2de96005cb470c08713aa8b419fb6b4417ea0f117b317f90bd3b26a5ccec22f4e2c1c48a734124e345b8590a756262f8c8bd2c00b24abbf626869a6691948288295e5e068b4e22a310fa163fbc96101b5370c040a4d89ab44e69ffcad926463c62d9c386f4deef6c71ceb016e779faaad7c4b31c50fd02f10d2f2c2e36a164b6f7d169758ac02116b3ee22e75ad409afe745b5d769a19e64aefeab0ec60bc1742a55245e33ae056dea7ca7f723935c8c080b50ca11ef8ce9a63cd9e04932749125a19f7094efbd7807ee76d4c4a176da52f10ffb881e832983e2b8613ae4c330adf3928e10881f8fc3d024806d863f45251622a948b9a0369fe445d075c50dcfc73fb4cbc85255dead047b645fd0b7422d08a93f145ae6fea5ff0238792fd4f5f0af189788c46477f8532531929528b89ceb0d95c57a08dcf915d0566913ac61ba30a3193d8206e119ea838e496efb63908c1e714254117fc7a1295c6c22e58b3a7718db101d8bae13287acca5dc130b1fabe5ee198c144693320f108cc67c73031352037ee836c9e402f8a0b887d7d83ec0261050691c350998b2ba8eca2185f4efc784b105225c5f0294aaae322b5beadf26d2f62130c9f48db8aa4d007c1a9eeca79fda43d2228e33d9aa4cd351be9681d90053534fb14797e4bf6e83de58373c829ae0e25b77f100d44ca14aea6ec0cb30224ca9622def1244629f609e4f8693cf0129d6fa79e85d9b199121cf44452c87cb26c099ed107bcc79d3875b873dddc309e145480e36a88f2eb6ff256bf823b28cfcc7616679652d5bc5d8df627485618774b9c5787963c5282472ae2270185477af513e20056e390a0fd9087ade41a36268ecfacb8ebb0a42c0eca9e486483e66275f42c12cbf4ef64617c3be5c57b4ff2c34c4bbf767809c79b850b2906e28ac5c69b0ddef10ec974c6ce693f03c3455df8289b53119f5e25a25e1c60aa40c24e2fe8c3b2f258b8b5fc02b077b031053c015697ec239fe71b5e0c53527d6f49b30224f21b0cd30fc480f9d1eb332847c1eee69156ade0e71ccbe1845447012538b5114b01b890511f65b8b54ada51825620cd166c8d0d6a953764ffe2c3fc6f063b01feb700aab4ad629af7dd1da503580f403254b44d18cd13193fe446138666d2f3f2d56da2275983914e42a815b32d65cd339ecc7e991301ecc24edf821e2b34ce6d9059c6384380ec54ef388225507a82de2dca0f7b76872fdf144d08722f70cc40e3dce24a4baef1ec55bda29b054e68d7dec5888f2ecb69ed7c76c85244a2e812b8b5b577c09308ef84f6ddeaeae3de210054db6b9933d99961d2b0f8573292a92e771abba36209bc6623acf224062b8997ca75c7fd75d9232047ce2ef56cdcf30742bea482c98912324f3e11d84a653fa88514a7aa2c18ab29f4f5faa7b07336d273518f02601ace2fb2c4672b0d5db343d55ee31eed006b78ce746e72ff76b82fd4f2ede743af6cacef6fc0fe5b12f68e946567e8e069b015c34537eea9f83209527742da4318ad97dd067598c0ba72f0dbfef1cf8cec90a7c6768f9f314f1ee82d7705d3a0f11b41ec90b454029cc52b327023347f1e124007908ed1ae852395e0e86dbbc6f8752e1f6ebd087ac9f0b9ab65852a5907ed890bc62b928f308b53c424ecf1994427f4f149ae83195f0c034f0b86c4fdc9cc6491ed27bea761b7a128272b4f2e0d34913a24074becc6276ef5cde841488f682a2433a483012854a43a296453ef2cb9d1f0249eb88931228ee367a1af1854df937dc44d85a790f884da557fb247d0c7118d839ca250469ee66de5730abbac480e2b3d4f60d6ab3b450d050c702bd39df38c5b494e71db9f15e86e653cce548a79241d8a9d69eda6e02e4d83e8330cd208ee7dc20fd1618ae6ec76fa56c6d15e0c0314aae362ac1617ce6c890dbd5b0227ace4e5f0103875124b5ab002ee25823e9eeb307474403ebd42b60e01989a4565a33c0333b3c8ff62f96041caac0c567549099b5068854d251fc44d342e9da41c5047f742c3d5e315fc5e95ec502afbb811ea3318561d2074a6de5605089da17781460170afbe8a390dffe56855813f1587e8e7266b6d0269ce6f8ed4d490e143328d0a8f220a8861a415d2280622921946500316cdf8cbf35872dd37c1999dcf8ab634cfb396c3cae9783f79a6d1bcd013b700e980f785c3bb0eec51ce0ba0d48c21b5243d991005ca6bf4a48db60f1db299d03f780f0c0393b90b87f886117808d149a5b8d2f2788c890d328b503340706fbc04a53973c661bf9fe8df6f43d341f769b2efb01dc6c7442db6876c0241fa0442284c73693efefa31a1733f9199a094d7dfce5a5d14f9aff56d89922fc2d3fea72d6b9bf35ac8313f408885a80361ecffd20c7ef554efb796613d8265058e4f108b570e926f96dbf7d6d690e0d7460d037a2d88ca4dc4625a8e06c8eef49902b102d09fe1b3eb4a441811bb25b2094059719017c4c4027caa03e02ae3b6abb0205c611e72acc19b990d967b0a04d31109084bb8cc9ca9fb9d1992d04713bf0df81e7fad12f3f26091a642da78c86eb29d07f053811e07be51f139b3f88f0124d0a4ca846c449d59cff323aea622f2ef354743ad8a38f7305458e7f7181072c34f6720f33230a650d2d4beb29dc589208bef31322f3b07ebe12d22ea0107ea2bd78ccd0ecf4a9e5f8b8071dc3a3cd00527ef520491baffdbc0c868628d9692f59266ad189c40a488bb871801c5ac8a0ea341f4f0279df3bf03ffaa26e2ea516965d9827af798e1493340c86cc424d39893b970bc4fe822a6a1a5892e32018b1b6180c00bf7efe062361fcdde47a854ada3c9a90be19cbf605db185b8d33bfd0db47fbef971b936a8b2fd64fb526a2742381347e996a300bb8789fa6780d993297618937fa73a86a5d248f4a913357fbdeb2540aaa48e8958a8fe5473f01c90a99cedad3cb152117311d9f430b41d09d4df9854cb93b790eed3adeff3367c2d4cdfed6ec78ccb8027a09ff287ebeddfee34c6e2a5020ad58f4c317c95d2077d34b834defb3addd0c240ba2396001d126f51ada88b0c8d763cb2f3c3c2b4dbec4d5066ba8c788a028c772a48cc30fd76a8f6acea4fe684a8e38a05060cc1431e51fc9652972d6d6a0f0aca281a7d784ec81e95d3743a6f0bdbd068c11a9fad21205b4094bd541df29d3d0bdb75e1c51100cdc3180c5bcd0a6b5f42754f70298d3d7abe5e3e927e556a812ddab86941add5444ef3e98ec046349d391b690714f0c8b9888ad7095293236ab07f264be3845a68bc84c0cf52815ebdad7a74d4a6c3d000a6f8623f677bd724c1ed828a8994a106670bc8353bb45c71d053dfc8e521f554df76a1602d5065ec5401f19708afbd838015c6b037da48c8cc31139b5c689b26722210d29db79651bb3418d3ad4d0c5e23c8e8042313628ae15d2523b54740950424150db2722962f19f9cc8447d304fa9813a3a410024744f8e4a389c5d7548bd2af5b3ad045efaa59c1875bb13aef87d6e87a5c126c850e411d6e89e1521afc184df93c533998c5ddce1337c802a8824ef9dc2ff20b83c2f21bf350e0d6dd8b4124eb5c9ebb1c62cac843d04a21408cf97dcfb12d004bf63031e8c89363fa28a4981bba9fc3db2557d611df56085cebe6762a32364ec81ee1b1e9db234076b5c556e4f2666e2681f7848d0778bc8b90f9b9fab49354373ced78241a44ca3fe6247f88c3b19587434370d985bb91ecbc73f4eb0aa10052c945ae471e3a47d9a71cd43a58f250f781f0554a09f501779cc264d17a8c19bd829fd3e324bdaa23419a29c648f0c71185d0e5cdfa319e652922826269a18053feb514d061f60e8a43ffeee0923e79a080a2664f20b12e4341afabca931b1e90c0ca6a743c8b282a6afef2dda4429e9de3a833772a68398dbf26e1102ac611e5516084526c6292ab2aa7ede942bfeeaf336b1a0c6369d29d7fa274eeb15bf848c1993ab481e7c0ce3e7f814401286644dae84a65170a425de57713301b42374dedb63abadd3609c9f5b0004a58303175acd150a8f5a1e522e7646de2b8736cadee0ee86cb192ee558e6873ad0b8f30c05db6b05800caf9b126c914a74177350de2eaaa295362d9a6691f28c4438bff691c49df3c0d89ce0b321f23a280f7e9c6941e9903ad1c4649c362bbc9943bcacdb440f2a81393c6b1318196df5a20dcfe69d069165afa652074348352a2118b7a84ebc083fe5ca7113730036879cba6a92283f01b94c9af1831df12269e637a5391121f39d4ebd3de91acd1bc0fc6a870281c4fd4fe2174890b4c8cb4e6d4ab365f5512eeda3873e9005f95f959812d6c1c1a75dd5aa88c4144e3b0923b9ff29cd48e5895ea886d027f2fc3a11bc23f67fe6d32f61caa40530680c4e2e37f5256b58222595dca0e9be778dcead2feb1ca04dad5356fec78bd28b86c17c969d362858ead55920967542ebb1ddcf240ab43d26180b0e4efcd9bbf772a83df0cfb8499161f2128e978de819c1d91371605a6506a4e181e069e441397c699eecc93da51dd1ab6c9177d3961cd4107f07f651138e87e043a27240c87531976edb63452b81dd938a8acaecefe243e3f36e4085a554d53e57e173a56a22c9bcb7c0b9bb567b587a48cf2fe226a01e7e0e5ce891c5ad5a1ff523d993eb9cd3c95d6b830a91df04c68e9f4bcf52cf81f5b311199cecd79518eea2d06de4d8fe29e93830209e28dcaa98adaabcb190e226cf65b09e05de57be350ed4455df41773e479e01189bd19b378aef19ddf0eec47827b7a9795b853b905c83ba662840f2a16ac62e95b617ea6590cd3624da14858016d67527fb63bb4fa535aa6e075d1a41843fdf71528657a24ae98a917cea2659f1785067f933c2514856591298cc3ba3f48ff27509fc0ace38940fa7317e90c89eed9c3448f95813d45e6b4fab87d45ce92fa7772964ec411aa5282d24350d52db51054c9fdd5386262c646e09228db9c30f62229a6032f5f8d8d97bc0c787ba845fc8fe6157a47c1b3e205b969b3e57f517e4dcadba64d36e3f6be507eb70bd800055dcbab57b10f1386b41b43db52024961ad9cbdb684403db851872386a252078cd2e24573e360fd3baab65731eee0e581ae7351d9c5116ad5d7665cead4a93348c23efde181ece76827b0a39ea620f1481e7cf8d1dc0f6ac0597d20bc23b168e4b391413bb146a34cc49315cec649598f09533b257dfac60e5ecc236210b103471c6e4815baeb93f8bf2b8a0fb80cbc5eb1a362d215ff48baa1c9487c589bb501cd511929fbe1c9c0c6e2b73fe79956233ad3da3ca0627c56a9d92305644017f0d8fc1b31382aae58a57c016ba01a1e38cf2c4503a6b2e942705a8ea397e4991b400f45065bccda6485b1c9c43f15436fbfa12fde40c80fcae03369b935e3392a6a6eb99068f46b60cef8a253ac213f9c081ea8e1ed120710fc2a065471ad58a53c5cf63c80de0ce66e39af5ba3e3b8f41742e083ab5109e9032021cfc60a1d784f0f4960e02c4a0395dba75a4d45509cc30e93fb78e522340b8675de7cdf9e3283a79081696b9a9661219f0c6fa4d291084c60889bb0ce224fd59dbc2abf9ae926b5c2472284652116233ffa0b2a0d0f80caae9b957db145a1be45a4be9fd1b6d4421d4143a1bd262c809e3a6a484fce97d63cd3c968e9edfa3c49df2813e7a52002be118d2c53a08f2018522085d158d7f9c585631d5a6fd60d0249664b058e0e393a980adbd06a625e05188473ab32498423ba8b07777b61dae512be9465f6b635d863b968d115eebf45b2447399f401598ec555d2960b16feff0aab494805c980064e6f7c22eb3346c8d641738ba0f5f6b280ef38b1be910c59421d167584d952ebe5d4d4827663668386d6f2022ca7a6311e9028e3dfa0877380036e0ee76b29e265aca2c8197a0d2980c9fd2735f0a84cab17f320775fa35e06aeb066ac9534a7e75ec5ecbd0fec1b6ef22f0b39b851b7bd92bdc61259e67b39598e5b5a0edcefc4a90ae2d1391b574e5ffd26ccb6cd86926ad84c82e6cbf3a620467dd993149f06056ea2595c0e37bda73dfa9d7066f9164bd06f41bc1cdcf917d9b72bc4945a707d216204e70bcb75ebaa2064edee54c36a28bd61aec02fbe6444e11061da6062b9a94ceb24f6fc5adbb7748116ad86c732dbfe046a507394d80c82aefd037b92dc01706ede09e1f8524c217335798c2e11d6072aa8e21d1d5e8668c52c6cfc920f38cd98da6c3a8f1293e762170d6829daa05c30b624f0ddd3cbbfc08bbcaa54f9bcd88c51fded6e38f48460c9d311c7854a24cfbc96b718b5d92461f658a7cdf0e8d6da11a0e3bd9462a9b2a375736cbfb3aa55476428727c154d88705e3e07f3750e0a9a84d97ea61388ce6808c2179305c72657a285b206c77b15abe6174c9e52860c42efa94db13c6517e63bda2f245957d98aae8f55f75c0842ada47a9ac35005de343134472f1d9cf03cbe496db3ab23e9852e2b62ce13ebeeacd830428457cb6151eb58920a7e2f5b762d82fb17f842953ba26180015b005158f18dd4e5668da762f37a7c319a32dab24efbf1a4132a40edc5ec221548d0908332253d22410df5bb66f2fc5dc7638c18227e787f05e34063e90d1b6f5b652a522de042a98b2cfe776f9df05c8a6100ee4be76273df0d288c1375375960ddd9e74da6ddddea9acdffaa2dca88ac475b483f1df152510b9e5e8d282423d4c96a1fab17a75df0d80f45b616683a547f315d159867e4e956c2e54ed1f4213bfb135a6eccd6303947f12582a5e04ffe1552137d642e0cbc4026457aadb9796bae02153ac11c7d0894ce3a73bb3521c2b2e8e576e6418eb7b231656a2ae3f39b869e4ee1d625d3594607f875cc498e52c9997b712bcc5a24d08dc6b16472a3b04ee38c4225fcc9604d7824fca462c248d0cd4d8402c14c582c3604035f33c18ad40594ae98e08046ba0b5aff2c062854c3be0ea7cc7d922c49baecc4ff903dceeb0d244dd031b8bbd6784037fa4d4d031f636eaa07492389506cb916c419e4036704d792fbfa415774c6078cdaa71ba51eaa8b55060eeca590fe7895b333eebd1ce1362c643e0f1818b1f0b2517068f150cbf19829b9f703444c5483f1aec9d5ce108baca591510be3ab3f79b1f05eed842121eb6bd06d0b6051661b299e6a7837cd22788904c87d409e893dac5de06d23cc6b013a3740505a70d558925a11c2f59a39ee615637984b7c13e1f7ec31b098767391fd1158a1945206f7ca76da0132e31e5338bb318e8be548325a19aa17960d32ffc02e3575ce87a22a8d98f0a548e2ca9139925f945ca01029e16d7fa5e6bee4787400ea4059925a2920c428c18025cf879a93824c69409a425929619584d2fa0c65e44859fb6552b2caf8ff864f20ac9175a4bf06c6f3cc9efb2d4679f3a479de955990d726104c648605833e3048a203ada80ce96b144fc63b6890c833e077822b5f4b9da4f29f25a27b07745d9c4464390fb0a7f3e24a678e089bc20ff12005079879e8a412645b55a5b0e06581a0a1b194b4f9599960e4ac642f15ad43cd2f2ddf4edecbffbae0b0392f14e1eb4e4be5a1cf73ef88480413e15be62fabb22aad8ab5de45858d9b78b5dcda1bae2b9e6e02bc3d944b1ce4cbf800a083e24dbfdeb6893c6db7b0f43ead8e016506b0ff938420d3f025b3404631753ef8f7785aca97182d9e3c8120372c4e022b8b10388e59f8eed5bc4eb1ae660abd61bdba4c97e53a640f8d8468b60856105c9b12a75ab26244a24503a1b47c41d56d901428bede51dc3559eacc01a3a069bf905c233a15f4867308092c5b253d32b68f62b7da215fa0fb0cff44409bb002b79702da9a7940b79cdf817c73170a959bc286238a23850e0d53774e4945484b3abdad9c9f1390a8d548e393bff4d1e12fbae63bcec088b96888144a38f219540b03b30153d5ac9709ce08db48cb0528f1de879a9dcd0977acb6dffada844f8aedea104526957daafd0b4a15ab4e13b5a2dd51fd06cd87fb0c22709ec3f44993304bab543f364dc571cef177f2ecebd709bc1c725133e584c8f85514f70b2356eaab89782bfd81b8fd8285d0da63b197495c45ec637ada7aedb28eb1d98de6eebd5afdedac4eebb0cdc6c8bfedd34e95832d6c3d27a235a65411a7fec3b175e0e045d835559880968b3e19ce485b0163b9686129ed4ab1072beef84ed332eace744942112831dc214dd565e027312196921050b17f872a703101672b981680e6e96e865841c05b5c9e3f267da68c8b3a5e72f93423fc35d81807755c7ed76b4c18705b78e7bd36bef93e58132eca1ce0c99d3bfaf7790c170db63c077871e39f2a7a0afa20331927ca31f24511b3364682a5aa126e3498ba9049dfaf166ecc30447b5310e09023fcbf9efb3722e4e8b413117ef530513eaae00a1ba96d80fb5540b0e2515873ec7a51c2eb81b3d6c22eb32247082036ac905315bc2a12b81410e6ec4e8d1c7406d0b905fdc24f741f512029bc8dde3061575c395c0c4fcd739c47e0ca7aa8e604d422c5af2963145e46337075590cfda9b6c00a2b98abbf7a7faa504183fea80a0ba43da26c1b66bfe3bd96a889817f472421bd7db85ff8f0d3f44d01cd005178a73823a5163cc6c4e8e39d504859d493c9f418125671b25302c76635112a8a5fa9a46e80b63124374b2d30a0a0335a9927e12bdbd098815b7aee0dfd04d56f4ca9f85ae1e4b9643b02ebd5546377698e5974c4478d8bd900fb766a8247dbc859f304ab978942a4f36524924d243b2fa9493eb5ef7818a477a0a7646df242f7b6158056e8d100cebd42e4cc9622005dda609cc29dc94aca96867859bedfdb76514bacd5625738d5f9cf13f5301fe66229dc2ffa22a02b800590eb1c7671749615c96f2c28514f24b78081a11657c791345a160e2b87f0f32af7104a70279763b1dc5ad8390380c2a0f5b0cb5351ca4cf577c73611bc32ba0e1d7d16f8b1cd457964c5c02badbde8400423dd4464473519da425bf27154bf4054ba7331b7c4a790e2ea90e85540b9b24cc2c2d1a3873433170573d46ce4373de099550e5851d7610fbb2cd16be886f7c258529e005f48cca9c497cfde7a788a99fd19c612154ab9943e4a6c9a6a2cffc4a25528b9bd6c78add660c70c043bff1851bb6d96fe21784b5bcb536939cf2ed3936df74d896a5a932fb1983e6626851545720c4a947e6c4efec3904eeb871a7dd82e2e97e979a29ff0ca793c067f6fa3d1a2e9e4fb92082ce694cb02c429e463301b64584e631fa17012be34f991564acefb19abfb6e88ae5377a572e4cb8ff7db4b2d0d84c90173e183f846bf8a427e539d56c1753e184cdad5da72e75e30d37617bc80fad890322a534d6a57b5d97ebfb0d517f0a56a9be18ee32e1cf21329cb513040bd5f7ec04e554ef26106ac2fe0dcfffd8150212d55becc089d28b6fa7e5ed3575568aa4be8656fc91bf3d74d0c907011970d5a1b5bb7b0d98236fbcced3ae38edadce2c833e1edfa04a6b1a5486d49380e6fc1a86e44935a971e3f51ac63a86fffd1f90666a69dcf7eb5f630bb78513f32c8937c74d58f593f20b0cda9bd538eeb70c2f6ba14a8a805352c493c300eb0ad4d7acccac382fa0652fc77820f294e1d1bebd97fb79d45f34fb8ec9b0d4bfdddc36a08f7146fdf8425b9b543093718db31ee7635fc4220ac939cdf781621bbee2554ab7eb2a802679cfe22cdfd3ffd85a2a427422e42930ba9ddb1b588fb989efb182a3876df1661a408a84f848fd561efd0129aeddd17856e674a5a2642206e27658653b43b27000814484869ad7acc3d6bc33bcb42ca53d2cd9a528c52f5a10aea254d90aa4eaaa8ab2004322b3a9250365e6101d64855e9d736d887a3dbe9f4ddde0195f5d63557cfc78a2330b4cb4231bb56ce44e236a47f693dd054c2bf36c4631e192120c24ef37948938a64863fd4324699a7553fd765a5fd31e0212106ddf3f5475230bd21b842762fbdb96d0159477e42cf4a6cfd759a374ca7e93fdf74698bbe266f46f47974d3404380044355064b4e859564e634b5300d8b70cfb3b8150b41fed501a6d5773910edb59fdf19cbd0229682cdb9a71ebaffdee7cfdc3255d4b374c348bec202afdc0239e77af85726ea76475c2b1745a50289010f459af825b0e739df0ba241345c2827166226abf9ef2b6dad2f27c47e2a46839a55b9b73791538609949fe7c14166c93d416a126c1026e5249a5ddbb9089362e7f6d965a349f655a264bde3f1c79ef6946ca36cbd7ce3cd739b39c84374beb3d3c005cc491571d2289c93603437600b2e18a6a7cce67db8a24b7560a05244dfe3f44f450f2f897f776081e493bed5c188994944a800ae436f725450cc099e294924a489a0c61071759b3a3b2fe422e2cd74b45d3581dc7cd7fdeb5e229eb74579a234b896449593fb2c8dcd9160e498368d0b810183272d2980a610c6c8b758f983a2c01a3e5508010b39f02b4bbc5f14a8b70035cffd7978377f4145b20b85f262742d047fdf6adba13e62d56135d652aa764c595809f9c54411ff6f5355802893d5f0592a21497687a2720dd69468a81f2413d4de71547017226f99c403961b60c4b6054547fda8982e9027ff49a2d0312e875c2dcfa53d41fca604cea230c17308559e81cc8e99c294200b56789c4dd08130fb8489a391e047f66692231b1237aaae16101278f8f87ec4058bd5df7c8dd4c9556cb56f9185ed3a7492d138cc5b4b1a7d4477be304a8ac7c3045e9ab8c09d478a1108fcb953aa209f8212b6d33968422ea6ff03408a68e0520f0e59b8f86016c94df4c97e0d067213c2b90e8b15ffec62f6b0d49f53a588478aa82e3a670825f1614426d80f72b4507b2e5410a7a309787c39460b195f9a91a0c8bb9445d48f0c827ba0624681df8bc14dda459937bd4a2e373ff27840f09d84ad7b08a78af5ca66345ffc268e01fc951e29bd3aa9c21a9bb7fef23c8f0affaa467475ceaf2831082a267157137cffbb1f6806a391b05489fe4825eef04e1fbad24f213b83f3c531158745a714eae6c20e3369aad620aaa0a88ec96e88a50046b969d09277b923988fa4458a4d326dd628113ac818fc02ef581fb07440b57f898aae5ea8e7c04240e8b83ac3808fa38fafdc9107822d579167604e3e83154ac2ef9f1ab741dcd91e2d026ec74d4932b69f7daf4d9b84762cfcb5274769cc42785d829c4425834af50fa241eb3c530ccdbe13337e0001ce46ebbcc3e1fab620e1692bd917bf102ac553b9bee40a67db008ff8ab32a87beec0738a176d8012a90a3ca6ca019452c6f84aba30efad4c38eeef5b5a2dcc6e8020e371b8c4461575645d9d0088138b88e7636e6bded09d395ea6f01c67b766b9303607e76668fba11288edbb64464bba9f1b976e701cfcd42bdd648057f0dcaea07c5e5b7058c90556467d6f154302cc3debc190387eaf02f1c9d198f582d5bf7272232423c55b5844387c0ce44e6a14438204092bc8143f7ed477fe16349b54dec254c2337113177f3aff5a0a9330adaf4b1df4df7e063b1efcc293fdeab8d267f6e08afbe94a32dc5983592cc7263ecdc39ff9a772d5daf802bc3f6e6108cb244c5d7048eafa81944a84bf53ca5d80aa3c868dc5280914ed5d7cee13fe9b218aaac06deca61437516d16aafbc5960524e4306e3c63ebb2da76da381b1045ef48aa0f8ce85c9ecdf8dc5c762f53d03fce05bd4cd57c6a07fd76ab8a3c0cb222f4a37c41a43978f72c3e520404325c90e37d49d0010e0dafab3a2203a94ee56013d5d3e26e9c0bf10a72ed3e662b78e52d2b26e16f83316e3d4e3b7a8a246c0d65c1063a6e5455feaa9ed558c787fb877f9f8028875255f1b61faf1aa9a2368a1a30b265438c1fe4f94486173e8bdee9203f3a1145a6e8918b2ca75f73bf94db4fb7044adf74b354ca0cffc2dd0400f71b2ca687e4777245304ab80ee2eeb073b8ee100a69fcd0c0e14b107c1180bba3fdb6972365c058fd9d4d6ccd69623368407d4278b6102866bce8ab1dc91dcb885629349c1c3c69ddb1742ba15d77ad2b51a1de5f564c8b84a1a4d12b1f5725f05e010aa222d792258c92a4cf9c4fa077804b61ee7cb82824d2de91f8cd7fa35f3f3073a0f165967002fec21fe72370a271f6854634b220610fb17c4808258ae24c438216aa6dc3a9a87cdb07f29edc443cfac95d220749363b8c7d3cae7e86a1b622fc7ece8f546d1e9fa30095b48de9b9ffd09bd8913e76d7233def6a859de0982ef28b487a68e3d62b2ad05ec336962d90847b612f94cfb938495b3ca8d89b575d5b4a35a7349bd6d1ab26a162281f1480cdfbdefe07d636b7c6ac6f6f4fa8cc3f5ce111680637ed5f3d16338012d183cab5ef85437f40d1ff5eb7be8433587e1a710940b86dd19a5808840870460bed53266adaf48d112967eefb72c0640bf74f61baca8e0ff5c70539d52ef29e40e15ecf9793314103080bf18b7a6f6fbdc22689ed58d52b4226fdfcdc222f9efad3ceb381c24cce8b292b5e7811974dafc2c2beb05c81202d2252b7b70bd6ad061d3149dc9d7327e318aa2a8cbae8a63613293d16d5ab62965975a500683174ff33115739f0b135797906d8384b71c07a041eee92894dc21f2b8230412112d263411bf5f1aaaa1f47bb875df650c6bea08af4fba42ec97945d2dc118b896cd2d3ff6cd881c392d31adb4e873e6d988b2772d7f446bc9ad3f7dcb79d5eb21f4a034e11566dd1ae39838890bf786f32d8c518182a8d353e1d014f1149def4fafff4733850df5dcd87020beae4f8b5c92504259f6ce552516dd10fe60b137308ecc8d44680a04ec9b01848129302322d9cd23e7e656cb31eaa3742dcca25c9d71250636e589ff9b278ea22792da2ec92c449a035f389c909e21beb2c4c835ad656b3bbd339eff6601485fceba7a44e1ab3acb29029cf9d5e901db656265af9c83240a1d4b03bc18a43d565bf1b672c807929a599e26759ed806ecd28310afcf01633d35668cf7f300fcf6494d220e9200fd95fc0a8487405e59b80fcad19ec37fc98997cefcfe9a295b0f8453d46cbb7dfb4e725e663ade9927838179a9e517f46f5ff52cc45108063430c8742da13175ae10dcc48309433441d6724ce66392e98640a7d4334ebc0a34df7232679c5458be06904c59d4f4c455194000ae36f428d18f2fe740f0ae65117312190b7ec7de98feff874e5aeb741baf59fd4e03749137234ad993d5abb39030ce4289bedb5f0e575a1b185efd56a170e54d88d3d4528aabfdee628995e2c98d6e890d1303c4f70245cdbd4058bd341675aeddbd376424e3b562b62af62750945a0a51a74ec7a39e5c4af0e126f304a93c166c4803f04bfa858091589f817453dc496d84294346bee7b8f20f0fe6052dd401b157db1cfb8031d3e6053197fa98e77a0c7a83a7282e8da149fa002e43d6f735e62b70f925ffe56894bc27e813915ca1e9f5fffa7a6f001874c0e2731d50868b42dbc544e0834015cb4db968a428b047de33947bd7456978a5d0eb5ee72c35c31c1e7d834098b4a0d299351332a09a3abb5e9a332726772e01b2a157f4c990e664e13168a235df7957215d278d18d4aae9626d032e065d9b69945279e89583b3fc4a7aeee4c515c16c6f33bcfc27e541890e60e7e4a07864ee4459c153e5e0ee521b8922429b6ee318659a6822d6c15ab93647ef6528b2d39426e69192809ed4990596f4543d683d060ff6bafc5943566b58e588b49e9f997180b4f34d98fab8157cc56b6b5d602466d03a1d20174df8652c18dde6e23f8b5f85eaa6232e1456770d822f48a154b9245e46af4c135877f0a308806b92ce8318d43d846fb112dace76a4fce1af98082478ee2f37fa7e8fd5d2979323307fab7f73e362d6bbc38079313f61aa29f95b136d290619b2f838264d1a9c7f2a37252466031a36f9564dc353ebd6ad0e02cf2904bf649ba4458e0b8c7665e318a40e37f6bfaa4c2ba4e97a3dec6c6a63f9e2dea3e2fe6ffef885899fa48d5ded5880316b5c680011a19f0905e848d3c1a2f7cb580e53e0ef76ca998c392474f9b884cfe06a575a5b0b82b41fb23e05aa6bf7ecfa90ddfb1e386da4516a2c492558a1e500e7d3273c2f89cd9c580bae340168fc3d2bb0db0421cdf430fd747b7af7a28a896519e96f83009d664191987122205e226f4f01e8fb4791d02826ebbaf8d7d52034e5c06f2b44636bcfa2ce1ebca0ecb0958b1f873a7ab4fb16b03315e5409303e02c0159f09b9992e2e9eb49f92cfb9bd69624f4d7242ca9a55445236704507d2488056d0baffb48e02251460f8f3fe9310eb1ee449a0fb88f0acbea44957e20984f0db5b3196a14753188972884932dbd0d9ca818d0d5cd97c49589a3866bebb1922cc3432b2e0ef38700a55ca270c6a7faa568a62cb3a86fbc24c435368cc7fe03ce09f246c6c3404fdff5b63ff1e684ff8fa25da28ea6492dd0f40361aef671df2b1cbfab9a5c88ee81d864b9da0536cb54796d57cfa19c3a2c97f87bc1548874137fe17f02f4ed82eec2aa30be330157ca52494ae9f842fcc0b8bdac94899071593ed2ef63790805465c285a80a371439a4d1cf5ae5f7b6ffd3f195dc7c1320f0838a501f9d24f0d3b6eeed28b28b49b50597290f6f9becff35d0815882a98bcfedb688ec02029a60bc56f1fbedc0e53df529a85f85b96a8a511f1f0fd58fa9b5ca771056b7608dec0b73866169cb07ae1961b325fbda6ec6c53a0a611f7adf3fc77f0d6664b1c346919437f59c59186b6be84ec9880c66e4425f8edead9e10a2cc61f10fe042b2ecae62de33e2d9169cd74700e3664a8c09e3e73a841de9cc40853f8b4a63c5c76035708c508ab1a472188f1fe9b0eca79763290e081af4d9183fd10825f80a3bb64f264fbef105ad356edc13107ca3338302e33de192e514420102bea1f98a23014783e6cca524409065a104cf1a7c1b4c356322aea69263fe751967273968758815821c9f00da61abd3afd100a44a147cd976a4f75eeb16b0b18ffe230424d92c00151babca5807f1b5db812e049bcbeb99b9f9e6d1ac44655b2aab55b26b059e68aefa4854e75e74e6c2144199aca45243824a4e79e570c1d96ae6385c5bcf99e17d8b8767b1dbb47d100651d282cd8433e6f4c318496ceebbd43da27ad4df30ecb619b08341a579637a003a4f8a375eec4fa00dba6356365306356c2bc2bcf3187be9c2fa9bfb3d1971d4ee0f06d234e9855150623a868268cd25f1ce12321aba050388818891faacccc47d338222e87c34180adf0ae3f870b54fff0f07d3159540d800b119ea2d35ddfbf22c1f4cad28cf3a361cfa035e108248cfaea9bbe387be5ce7f5f6d371b47491300a1adabdb3666e613e8a9938238d61fbfd3f18b13b8e9904c50cde4f2db8e2a811a214026acb34b28596377a811b79f48f610b77410f341e654dae3da2865d0bb46a56967e309a30573f9bc0ce3fa727fabf04f407cf4cb11baf5d6fb1d230ab40e1536d3583257d9c0234db6d75d0fbd1576d6d3e0ef8e190346b0d8951a91fd444a60cd5c02ee090c98a9632d08120cf443a62ef7147d4023cababfc69c28d9cbda0d4925eadffb28c2a066c1832919c1189fd72ee8192387a855e5b72eca4e1b190403b1c87893204a7d302d54bc81e66cf7171dd116ecb181d540c2eec0a0fc6c2c222dd804c0a70c4b0656db3d5d114c499081158d28aa52c9eae7cc560c22796dcff491afaa0b80a501e928b6cdb19e0886db36f682bcc2e6135710732a9ad8138c9975fa70d84e743a6be3fe2bbd46f9cd550c11257444db2e2aeb8910e0a9be913a909681cb6c54e7e9fe67eb12143678c98ea158ff10d81f0e639e2a93a03449392e0d466df8729f82eba816f72b1759723c633f6cac141b963a88b0683623326f85dfc7baacccf70e179f5465f8625c68d85b1cba54621a39d1af33908e2435553d5b1254f82a7bd6db3061b74c59e8a10d705e5a4dffbd125a837e3f9b5fc488c683f0800a3120f8f219ebc1097b524baf58523bf33c7813dae5186df7c25211b35440e238cf4ef69227a36995251d83cb955eb5370393ea05716dcf41f2f0b6824c240a6f7ed9deade66142276e352ce12cab40cf289ea50e0b40cab5db7aaf289da799b5e9c8ef0f852709e5c73cf1369dfe6ee7d96b4a0f97ed85c6fae88d5f5976ea1f936415818abeab1f1e2ba84370e2af95d29d97c52934a488d0c9c27b58773afa4bac7a9238ae52303a37f68d6ed3286b34700cf6a49610cd6a176ecf1fcfd2a9379dcad741fc898b3f30b8a33ec9db6f1654ab4aac8b40b75c1714b98c64d0ea7189f965d654be73c97e0bab99bf8e95a6664e92c9f93c07fb602d4ada04cc1d4c768a597b3c3d3e0262fb514b6b69ec3fd78f1fe44a98d26c6c21d531da2681e79f0c7d8b2b397f4af3ecd74d7e8b1b809c819aef7d47ffe37033c5be6fb4f767e8f6b1ce4672af0969982eaa3902cf3c2387ba236a2bb9cbaca95371be3c2aac1d527060dcb00427f644b66e55425dcb023f7051f49205c3cbacafd422b1e28fc483586a6ad10a7540149d63aea3baa3dd13996ec707b0c176f0f18a01efee82ab51d5a92691ad4e88412c30af400cdd2a8771b58d91f18423d518e4828ba11d2f133b60bb97a9515ef285516554e140cb250108f7c8410d36106fa6f054f3808cc0c2936ebfd4a54d232573a2090b8f7c1f24c0f6d129fb0f856c628878aaeb1f428395fc14e3afe67f2f52edcfbdcef4d11f261a1f63c9c1070edea5d4c19aaa3fce6824172aedc428b0e4f37d7d4ed8c289b75da8bf6e36e6a3e8a25a9f1cf0f1c363e7851f463d88810ae819b0252632c229f8d3698c1873bdb5d8ab4469e5c724e8057c4696d5ca46b1236ec87013959e2c25255a8ae32995187e4cb9a855048d036fcca157ec3aa4228b244f06ad68a109268dbad6cf590e6631d77f313de72d51dea2877f8ef7e6e389fed2839223a69c332bbe548be0b39668bf1d08dcdd891532ba185f5570b5b97376354e61a5f57468a8ba1e27e5aab5f567e77677b8c370a4218f2d22b364af7e1c74a1597aff31088a51f6b11b65562db8d4e6a9a307680aab503541499c8ff3a3df41352c126709675fcf6705cc9d69351b738602b64ae957235182c637b02036a63cb2b68fe702a3a45d0f922819983cdb657b6d93bd76f97160bf5fcc7171895ed07b47e05a352729d8104f282e85cb980682eeaef1a45205361646a78c4f638adc230939417f4b88998187ea3bd6331fbe2694a606c63c4cb46d748c51d372b81b81a7272e39e41f223b4f29c993875967711d96ac91be8eb57a83e6f3e9af28e13f5c94387a2ac79244cb54123d9293dc695f4c1729cb811932512c8de440103f6ee575b4ceda7c3934b9452c2a827585627452f4e951351e7c393c55345dc7c3b329af5e393b273dce71ca37703d8a3d9919534520935a4210f989ed89703ee5e2cf13f95c33752270f55904d0b586ab18e28f910006c1cbbbe9f71cd9e62355f86076257225822925d1265247d894814f0d6c6fdd4df4976a36b4d71414b2192327a63e6e8121d98eb3886ab85ce948c629f803e3df9fa0085346c623289121ad29704a8c1c63b9bc207100aa25df6825282a95993c9da0d6a8b6c6d1fde90409b79c5d4e71f47d4803d60315aa6ec6addccea5ab6d26c87a66cf012096ca51419d09258aee663907248f206acdb80bbcfe6052e41ffc580f7a8bb8b4be3319886a79c98061d128974cac21321e7e8e96822fbdc262823bc044aec29d60420c1a68f865a6bb02f81414f7bd145ca8ae5c13f0a799b690af9bd36470a45217f8b2234e967c5a3d1f1b3144f41668774717fc915dd18e107c4fec67a066483719a806fa3ee04c67c8d578294d70a50d76a02fa283d3e28d83141ec2354ec5ff8805cb5235290a10e6dbd6637729aa52d234ce33536fed6f873eb07b54ed55e191b8833088c20ad0c1d7d1867f082bdb486b9ab9c2be406b777addd93ce208143ee872aad4d539299c0ed7a259888ba4229997987308228010381f37262b8f71938a7134d865ff5e16cd9e218922e461d8b16bb6d1da2df672a2a237fab6de253f31d34f2edc2c7b4f5c2ed86e9d66466c13a056af2db12888029f7c61ea0c012c1b6d25aa9ee4ce02ba485a715b501c55a503583340223e78272d28e7f34ef624a4202331006e0be003dd3281ae505e08028536f0ef4e41701cb54087d203290b94497fa6a045e4f16b3613f58dec2951717effa1ba97533d3bb5a3932f09915bee2db794322529030c074807d10628ee73c441c51f2820d8aebcfd888217e34566f81c5580edc71e0f4686b71f65fa145371261551a95853a44d2b94783111e786cf0e86a107a342489f628780cf3ea4132282edbb4f9b52bc7d0ff262224f8ccfae046cdf7b1eccbf7dcfe9535ce1cfaee32aab6283171385607cf61ba41e0c8ab7ef3cfa245107f8ec3d7ca070b07d47b5e9e4edfb075e8ca431c0670701b6ef340f2665d527992ac0e71e9249f98c509b50a8f06224ce8bcf6d85a0076382a74fb28bf9dc3d9d10fbad6a93c9db6f245e8ce481f9dc41c0f67be7c194de7edff449da6073ec89b7df2b783152e8e573b700db6f9b07437afbbda34f1375fadc34a8e661bf65da340ac38b9934a6cf7089220fa604a84f3355c36718945aa58684b48964092f66e2b8b0f3edec08f019ee743c5d0fb67ec48b993c03f80c7180edc31f0f66e4edc39a3ecd95003e439b1b5bdf3e0cc18b99422e9fe108b07d683f883e59a8169f5f1114dc61ff87365d6fdf022fc6a2812de407e0f3cb02b6efc383b96f7f873e592d9f9f0f108bcf8f08d87e8d1763e1b0e8930580cf2e5ed8d7a14dd8dba7f160e2dbe7da247a1a3ecf80ed3b0bcbe78bedf6d9b1fd152fc6e2597930fdd67e6c137dfbf0c55842b0857c6b7f8595617570a45a4cc590c2813195e7069cce4f3585802ed5a53a1e18537162f060592b8e35d61885604cad30a21096d8ea00a8156a85a2813198900168b0ec0a90ea525d0a07c6603c2f70b04cc574a92ed5f1c0180c0766cb8d6136484c4a211883d1bc48217cad4ea8156a85a2813197908926e5a7abab21d5a5ba140e8cb9785ce0747eba5204e8525daae3813117ce0078b0fcb9f2d38512c05ca1e64afe148231178dcb1442f989ae5aa056a8158a06c650a100d0a4fc44bb9654aa4be1c018cac302a7f3134d01a04b7529f9160f8ca13890061e2cdf5af989a29c453ecb96e9b556b54708c6501ad802e545364b08af44d416515c44cdb079112cbf6518ad80aacab312c3b66dda06557545852845ad2a6a8a5a71b0fc18deaa5454a551b1a1526ca84a83e56f28b29395107682590cd3522a0a86a1c04e30dbf160f96f023349e17c2e5d0c07cb57295d273014466302439960288ba1301a2c5f05898e5642a4955d8d5642587e8a128ba4e32175f6ea3a1e2c3f654e4fe1a452291c2c1f6544e21c652ed44553572579a12e1a2c1fc5112e5d09ad562b212cbf5e282eb43d58f634273b9ecebb8e07cbafd8499456d59ca4299c52eada70b0fcab35aea2525b20144569600cb6822de4974a2814a634d802a93d3ed80ac6d423bc16f2e5aad4d59c9a84967f02eb6ef49345e1d7a66c5371a6b5c61102d95995a4da64fd49c6cbb740ac8edd71771da7a938bfd6ae39f923adf6549f949faa9066912f8465aa0ec13255bbd722b1eede8aca155579d41aa87a2aec6daa0de0ae533b67915f2add6b43e9bb0e965f479a48b31ed7dda2bf765d9b80657794663bb8d4a6da643723663e868c977f72c4cd46c878ebad871b10d1fe68d3932c999c3615db6dca59a41465dbd71fd8d2a04a8606799aa5a735e8038540ab93f213544115b429df32e79bf3e041ed7afae43ace52759c0754f9c9fe6816f9120896726ed68e50301a76804877ade8eaa6f42b9ab2226d9b5c6dda0c48ffb648229148d7f5b1360ef659ae563c6afa86d2be691cfa51eb1bfaf4baae97df385a78a71f9ca4f50f7eead48d24c27d7f7a828f85f4f2913ecb22a4cf254dfa3c69e4fc975ad05eb64d9bfaa954e21281a5f6797a00fb98353db0e4f11cc4017e9c017f5a1d86c017d4e8b6d21c095aa4cf484372258914e1623240d092b627a18bfebffde5ef70319148442f6c45daa1010e78a66b3389c1849a98fc4563d84818bdb7b46a27d2260baf2c2d5b71471667a24c94993313356b64914933794c99b903d3a06155aea631ba23daa6dd153541e3eafaa7711ba7395296c2f20633e2a634275fe551cc3fc5f0f2638d4d9b505e7ebca16993ed81e5e7e8034b14963ce64854a1f461ac28a51b8d1baeede51009847db4497bf9d68d9fb697bfa24486fafa1834af2395a743e386c6e91b3f750d8c4486529a53d1340f6a4ea8594a9fdf0a484eff684ef68dfcef1a227d43e525b14ba66819e214b95279b9e251d3e3e4e4e4df3bf9a8a534a7b2959a93f2e95da1f82c57284a58224322d1aa4d96c890287b0b85969dc8d56a6561abd50e7204c36a1a9557e66097a2b92fd4b7923b85e7573af2a38f0f816aef6893e7e079ef0673ee43adadfb36c3fffedddc7ad7624e9b2c20100af34b54782d4025f0fc67c5ccb5872deb47da1bd1e27c4aefadf5fd905513ff12124b83401e6984f4b96f4a7f4ba41117356e4d6bcec7465c300cc77af71e6aad10e57fbf5a1626e310154fec8942868c543b3a2211bc378328540ff7e14fa7352369104873f35de67fa97427cc696e3e46846bbda49f7ba6712e0cf2f9243fbf79748ff6f1a387406dba3f1f4e149eef736eb16ff07c126d072067995fb71a6c9b235b07198d92cfcfe7afbff160e693c027adc0f047eee8b1c0177d49c3cceb936879062c7a9296bf84457fb57cb1080828c36ea514cd8717fcb677e1e64fece6663893e3f2aa737bd79499f1b60c61387d7ff17b7f0fe6fcc032e3a8ee0fb6fbfc0177b4e901cd49eebf5492c2cdfef067dae4d0ebfff39cd7427e6eb9e12cf2a2b4f4299f3304cc915a8ec31967914f6974aa510d0849370eceb4ec99e922c3a5089eef13f2c898d3ce683ce79cd3e7a4f4de52a9e36b89315ec98e067ee0cce1c7dd90d747be0dcadc9236b9ce852427c8000637c618638c52c61a39e79c724a29a57c2a295f4a29a59c539ea0c36f870815aea3b7f2fa716c538c1af081df8e067ae0b6f0bd53c630246188c2c559ae18bfb7a7c26270637631651adfe56347af59c2cfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcf129c3a848f05aeb650df6887081ef8ed1041836b6725213a3aa74bc75976e20bfce8166282e8ca06fc60e2a5e3274a5730e6aad1b974746ae88ad698dc135c44563cb8a464cbf106c71d788844dab20361d514807c3a20f8de2d7b0d268245a22d77876f82a09393c2c1d90cf6f1c22a086808c630d8035b404b0cad8228cd5142a7d299b2c605bb74ca094ae455035bb89faea01a68e1f8b2e2f85750bcb8807de268751802cf8f53c67f38c71dec510705eff4e280cfd1050df81c67c080cff1881d9af4fdaec43be1b0f13912b180cfd1030af81c43a043930479273893c3676f42023ebb146a7cf62bd0f8ec447a1eccb5c13bc1140e9f3d07333ebb0f647c7621dcf023dfef1f7827e84382189f1d052e80d1222abc53cbb4160cf0b99570225ef4088977ea9b22c07cee204d78c9be3b18983eb70d3a072eda86e19d7aa88b18c0e7fe4093c0a5b1770885007c86546861f195fb0c7b601168f80c91803bc0fdf088777295b3f8ca8330c070053004efe42bfa7dc5008b7861c0fdb880fb2df03ce5f30b4ad15de39da28e0f0f66bef5289fdf4e0170d7d0823bffc9c88915ef14571ecc3b5d41fde2d4015ba60dd818f072e9d8d8325dc096af0274d8b20abe6a72d8324dc0966f8d99ab06fbd3d8b28ad3150e5ba633b67c65a4fc6fd8b20aa62a046c99c6d8f2c5d007c696559cda1c60cbd4005bbe0590f17fb165156b2866cb1466cbd7869b21ecffd2f9c9ea4e5ba6a62ddf1abaf377b16515ff1ef2937543802dd3016cf90aa0876eb0bfcb9655dc6ab1651a802ddf161b7f165b56993e00d832a561cb9745e583fd3957d119b64caf0c5bbe2b56b6ac126bfc34679c45c7fd63d8b24a69db72e96a5bbe5f4355b64c6f8a2d97fc53b6ace232c7af203f49942dab94506cb9744fb67c73b07f29d2135ba6d7c496afc9e65fb265954a9ae14b37f6883d482e9deb0997cfd55d2a1ff22112ad31e95e35170eaed47573d9788ff78cac2e105c3397cc502af55a6063d514807c3aa7719ad702a94d0f7252374141af053616698d6b36444b40517486caececbc962e0b41403e6dd336af0536aed84d10747252458a544bc652815563a1ac191f1fad31a635aed4c70bab20a01f3fae9411543a3910f55ab49e73661a316d66cd4409f9f05a2ca025865641345e0b6c3c7394d0a974f20a5e792d50bb825e8b53019ba90146c48d12d85f0bd8319fdbdf38be9dd7f392a5748ddd6d6dc4ad1b2ae1f65f412f380b00c37f1d26e33a2bec04c0f0f3f50397d742b3924ff7f2e2fac1c9afa094190c1573e9c016feb906fcde4590b3f8e9c51ce0b1e8c01837c11e040c33681330c4c32739410c803886d0938424924822892492482289249248a2a7a7a7a727489020418204091224489020417a7a7a7a7a80e0e08bdbb3e69cd2f67facbbaf39dadc0edeee5b7479fd4218c1860dbbdb4b34e08e1c6c11fd71d6e3e0472933d7011fd83348298425b810461610421c5c941912eee0a08e3c04ee8de2cc02d2c01923c487abb88a6e5b4efff6f6e601dba39c16bdb09a59d1e8461d5c49242526264e944e50a0a4a450a1f275cb2c901a2bb06d13fc95ecc591e8df977c6e47ef2db98db3bc15e4811676b5a7552a55112258de2a04f3c1cd35956ad38f3ecda8dd9c73c5cf883369f839e764f9c933ffe79c53e5a7d0dc7ece39b59f9266a6fc9c73a2fcc4992a7ece3953fce499a59f73ce133f85268a9f73ce939f93669af839e72cf989334d7e4e28846792fc9c735e3f1f1e311ffe9873f64f8b2688392d30477efe0f3e3c18ece79cf36bcc39a90b3c75a0f160eecfe7da447fce49ef149af6e714fda434b3fe7c122b63662ac3e7cba57c15c3f3744242945ed4aae2a056a815aad2e0a4ba549712e2e9525daae311c270502bd40a85d1e0a4ba549712e2e9525daae311ba702c1ad40ab5425d3438a92ed5a584788456509c88533bd47f090d4860597133c8b0625bd99af42af5ab4aad239bd56a7d959b8204941494918a9aa213ea523a944e543ac1934aa52c8a139cd289cc04124ae66ab5aa2656ec844ab60e2399578aa7ae521744c285bafa1a01025aad562b12c530ac07eb84ae6e76b1eb919175533ca9d44c453a2d90e624ce08355151d6fe6181fcc899217d703b3f79778a013ab8dfbfcac0c5dc40b9817203c9b8f3c49bf7c9ae42f90c8b9512eff16cdb36baadac68a08a23db566c8d195862ac5f6d9b78cb872249be26b61e62b2358bddb24d867ea658b5f65bf4d8c5304c84fd08135d1856c230ec5f300cc330158661d80a6b03cb300c1b619f6536db36ec57ac0dcc6e99bec665aec5af9a74b9dc82171011286b99190f68dfc2229b9aef34673da964bda7721c05af85f552731f6ed35c8fd762fda5794d0fe92015604b88070d1120f7eec138befc725739be9ca54df5af872fc655b085f5d7257a1d238ceb76d917f8651bb0b523fbd78e46cfa7b30fa3edbf88c1f66d78f65f1e4cf69ccb73a5b7d65a3bfacddab0236bed6da03659a3cff747234dc5d6b87894c17bf568eba11b37eeeeeeeeee14220d4773d68e14a601bf9d1d3b706dc9a0d6d5dd5bd27e6bc3bb5b3e20da323dc42573c9a00c974f371c2ab606dc7a78411d0528642e734b5700596c53b7d64380825c1577747dabd4e3f28834d87a0f03f6d67b910793fd09764bb4a98788c0959065ddcfded36f9f5a0f7bee96290e15fd76460e020011212482adef7f449ab3dedb0880690f765d775df52fecaa29fe22824d19d49cd2928a7b15150b88e276dfdeabb036e2b7a7b0394420efba96b9b7e42d033bd85d5b7dc97c4a805282b0f5796585adcf2c86b0f5d9854be7f1eaebba2836e3c2e8735c749159efc2d65881614f733ab82eb2a9c19986321a8ed7627d9440047131055e8bf513782daca7253ff9d0400d94527ab841d4095bc33758037974479f188ce1a14df11ba8814a1d84ad066a16eb9d62b481eaf67a4890095b636e60083e65d87c92f662b8e638d22cbb5e1ecc8b7938db707277ef5cd5f7453ee9b30b49c9e73739f1fe36bc98873d06fb9fd05cd52cd69bd05eb39868af4bb4d724da6b967b9d581bd70bd5e87aff2ccaaeb23eb3ef9b095b43b40901f168cba2cffefa92b571bddd8480d8ca0dd49dbc806ed796657fd9172262fa3a4698feccb62dfbe0da5582eb3fe48558369ccb73a5bf256abdebb8676f626dbc601d3a2fb4151a3ea07ef8c022363b1d4493eaf1a1d8836465cc2fb136e8666b6ca08d7f9eef323b50cd593f626b4cd18b8b5bcf592aac0a373710ac5fa1f530c0246b837edb401ff06644e4f65e204e833c604de62a958a7ef6b93fdb20ca6e225ba33efd5a3558a3411e1a8684f5385e8b8cd53b66b217d65b169411f2febd7f3971c1cd1015a3f3bc16eb5d7315156e4301e8c1d0b7fe4f4e464630eda71ad66d656aa36e596e686b60dfcfe2806d39aab5411e0d51f4ad9a4a9b52d883c34f2faacee781e80e1d6c6bd8ef4d083883bee8dbf6534da40501b1dd2a606d9ab35cbc96feaa615aae01f70b6b63b50c61e043cb2f3630959203adac657a47cfc86073a0df3f73bddea2b7f40de45a0f1a862171dd7a5751eba9c521c7cd9ab338f496a36e3d34677d1010635b059ab39ee26813f6d657a08736c1ae39eb5d50b8046cc11e68e3271774e7f2942ceb61e7a7d856a79456defa6aca30f9aec527693388df9a105407c4f45dfcd4439ac57acb22a263fd73eb69b065f5f710cbfaab866f77cb9ab3626a0e513b889c278481094c880113567244e1f8f01f7eff0fec8fe1e0c6187d6896f8cfce90985ef49ff4d15c3f0ce8f63b90ee1a21a59ecbe3f11d070f26057ec48be91eb045a43f7aa8843390814747fa730f79ce83f18f5f3dbeb50ab0a2e7a9ccd6803fa7c43e37c8a239df62ebf0dec9538f257ece155d8563f7e0e6e87ffc706e9bad6f5e6f140c77c86ddae4a34fa236cd7fa6d6e91cec6345e2c1643a4676cb360cd70cbf836c0df8edbe6aa11eea22cdd243d60a366c070252e2e61ec2407f12bd03f99066f1d47391dfeb25ffe71efdc2cdd140cd4521cdb54fdcd1a6dc3e38c69907f3e2eb08a988c0e0e6f669216d7a40fdfa6d9d133ba7c473771c0806d2dc0adc5f61cc71078edf714773d1815e4b7c2ddc1cfe41c0e8369d83e3bb4c9b72f4c38f45dab4e3c1581fdf8b7830d8d7c7a696eb9661b5727f07d91add58136eee148e36387e074520ed3ad973d09bff682efa909eff2ba594cc7f747417f22ba594d83ee2b74d740b95dab13488baa2ff782dd18174f780317ec46be142bea7c48363dcc131e2e0b5c4cf9d021c1d08b729fbcf8fef400f060a0fa63f7a50ff7c9f5601fd107e77f398f08a150211718d1de18b52045142521e3f72ffa5d2bd310ee98e55d4dd16ca4420627cb738484c9b8b31fa77e0765140bfbf9874cc8781c4b572e8c8e111f54bd42d36d78197977effbfd868683a6834248adc66c8194360199d469bdec97b97054f0144abc6af1076d6d15dca2d12c131ca2c6e2e566c683debbdb8b381d4fcf93466493ea5f7cea759d765bc975da0816a25c6faa86b367b15c7b8b9d4803ebc60bfc00bce01ff6db9befd5a3d4f250299cbe630f271e4e343ab80918f4ffa112d3617738ed10701314a0e2bd8be7decddc64fd481c41b2cff3d18205bf66105dfa76f227a97f994de2bdfcc33e0fa511b799296e3d3217864cb2b303a5e6d8461ecf15ae88ff0d54443227c35db63f1d5b25486af569da6e2ab614118bedab573e1abd1b6a1f86a56110b5f6dfa5c4dfe90f86a115fcd7d782df461340dc7f7da565ecbd5e0c32f36873b476c03eebf2c2cee9725ee8e706b10c67af2664c7d4b2028094ae86eef97ed36600b51227d8ba0768a90c10f085fd0c119ce7c21855dd2173443697c23f4af87330f0666cdd1871e78384a14297df8a1b9eeee255cf93cf0e00ca421b826701a0ce10cf4ad873892d29c8c0f67e2d4749823a5e6766008652e9f1e2285e77baf037ec6009eff1e4cb6e9784b6023489f678eecb9e69eb5d65abbc9b7810bf17b59a6e9c8b44dea3004ceb6f758e4b965f266737ea6e5f9d9c3f079b76cd3613dbc4071cb14da06bc18a2a79848241a899c625f479a68341a8946196d2b63e44764ec70b70ddccfc01031eed76967e8804be022e0ec7ff2e0ece3f5a2c72ed18f445a965834dabeb92c3fceb22d870ef8d9fe0e77bb5fc41031ee5604c98f6c9b0ef8d94bcc88fb24a46de624c6dd3c70fffe94f56a1b804bc49881ffbd5a0fced21fb7f75aee86436b02ee8f2cb8f901fdc8bd3f52c41df9228ab85fc41db9a216fd58c10a58b8f75a924ea5c37114e32eaf9ec55a3fab6f5fe49efd8884c54c52f955be95f233ecdd62b5d60a6fc4a8578ee3388eb302a594529ad9ab6299bd2a7661d876c3d68851af1cc77196cb382b9ca01849898909ec04356192955812ecaf2cb3b5d65a6badb5565aebbf5affa56badb5d65aabadb6d66a6d96bddd6eac581b75e361cbd8e3c0208e304257752ec4711cc7715784115ae932be4b7f9b437cf71cf529a594d2cf1c0f385c467bd5af97afb0f52e0417b224f7428c7ae53a8ee3382b504a29a5f6298655d90bfcb20d38cb46d993b2d1c827fbd7d9c3e8d3eb176dba3e8bc1d9dbf032fb5996655996655946cab22c23dd8c748374335a29a515c70dacc364948e452cfa38e632fcfbabc6e9a83ab9dda011070f8e09d15a2f4b29b502bd28a5d55652f61846cab0ea9a73f4a2f47a8cce8bbae698e3388ee3aeab52ad717c3856214a297d95d28dc64a7dac06653080693778c011010854eefaa9029d4a87e37caecf9db950342de3ffdcbed8f0425a79f9e7fad3d64d08783df6f5945dfead39e656a8b5d282274c9894904029258e3641d8e379787930d8c37f41024ba00934014fc0d24308bf74c284490909941794524a91fcfa39fb6aebf632517d297bc01a200e7c8c194497998dca2d623d281522e2fa3a46b8bedb796113c3300c7bc1300cc330cc5ed8b418865deeeeeeeeeeeeeeee5e67b00dd264a81b3e3e3e3e3eb36233d87cc938ee390eda642251ada24afabe312289ea065e0b11afc58c8db3dc3467717f697d29373833cabe7ecd348882343e375e8bd5320f06eb1b6c7d13f1609e8fd672cfbc1b3e4132744608c3b08afda3d7b545ac521ab30da220cd4c9b301a148661377c7c7c7c28966518866938b28d87cc5e5aa5194633eca29816ff9a3687c79246ecda78d84e8cb8d6dfe00107ad18ad188d5a7c9296e91aaca75a1010e7f0586a9c9f7a48c7e9bc3cb63d68a1d4d0b0fcac312004e612fcf693154b383e8e57b8f1dfbf9f76802dfca95e8c3b3ce0c13ceaa5d79221ce436029b7f75662741d6c7da534e59fca53aa421fa32a3df409ae9cc57ae86f35832d08bf519c944e9830297912d2c377b90fe1bf8071030d1d7668d394f1f40edbda8094d217cdc1959f6280f0073fc5b041d4b65167a11b44a97814158f12bf033c7009bf1d0e14c13566293495778dfe6bda47cd887fa9c59f9aacdaf559f622cdfe7d9236f2241ae94b349237d14ade8466f22734135fd24efc89567a14dac9b786e251b4fe941913abf809a6527c2e614d83449ac5527998f27febe1509bfc6fca962f45d1541e62ec0a17a6f8dc40b0f5b97780533e370ac5035b5f536810abd074fc80f2291fad0d158ff2aee251be76962207158ff2303a058a2604c4291e12696b07b68268501e46aba00011e3f760f5a7682f36d75c0a4de2144d067d140d12b11e85068b682dd39cf525eb4f683da3354d73d69b68ad3589d635dd43ebabb58fbe691cad53cd599fe53490e6ac9f5aebf44e73aad79aa7bb6783abf79ceb83adf8348b56d4717d86356a8f62d785c58b4a29bf876e7c307d4bb27073fb5c202860eb5b88f4f611d2a69783842b8743de33ae73d22545ea5bf94ac37a15e0a14fa47f1d8b95b62c210e805fc078373c937429c08be9c78d53638555bfd639bbc3d85bec33ec4594da1fdd7e87f645337ec7ef48bf075f6faf17ed60faee9ffd0cc18a7ea3029b3da804ec980a831eaa9911000000001316000030100e868442b144cdc248ef3914001078884a7058194964711044214c19631421840003000000406040a638009f001e7779b6919068760b89c4dd60303ee562dbc6093aa7dd54965ee0ec2dad11e4b572a4634f12e481fd32b06c42d0110aa56923aa49ce425b1a360f1f748f03cdc1d5635171ed21831fab1b219117c1a23348af743e5fdb67096c026b9244030d3b0c428b4afb3a95f6dace7268a7907af10fdbd3616386b0cb44c45606012d58f356fffee4ca1b5ebd9e7fb18a8a4fc19f1fb135fb22ed424955f4b009078d1c5b226978322162abe3978c96d69cc9aaa896d399c9e13134f293b7f82684ac46cf1c7155ebf051f6501a8de34f1eeba8696beb81bc929f69d106813a62af72800a4cf45ee4fd195126196d68f97f6674e74f98bfe787877a5798b8bb5761188b591e99c90571b43fb9572569f770c74bc9080747bbd4e1bdac42d8fd2939e217f5446651faf5d6fbbd617543dd70562cdb99b88a1b3fe9b1c791c0f80779ccd8f47a3c900b2424c2dfc3a929704e082419e80a595ad4015d494c486b64fa090cb57ee66d2179e5da0e45ad412c7016ac1ad7ed12d0d71e828d46f2cc87c66a4e6a523fc93fdcb1b5183a2f58581ae28d20eaae7e0c129848c6479c099c3e6b79a13f34862bef1866bc7a6d996bf2ecbeee32d7eeb15109fc49e4a13223503e2f9657921d00fc57d0dc7e2af0dd14c816fee2bceb7e708a26802ca8405707ce54698bb5e20285621835091d20465b005f3983e076da3b22a09943940069ecf39a3bc8ed5bbe0158768c6650e10e625a30e52626fe1494fd0eab6c12ad0a6f25ba6d86c63dbfc80a01d7c229a62d140f9404b0fa183fc22ae30410ea0d3c94f33a6d6b315c8982ea06eba377b1b6aa18a549c08bdc77cba1398551364287dd691af1e217412d65dce38425a41f6296f8e6473dfb4d313b5198adec8dc4a126a24251dc6a86b7b3784cc019614d3b1104dffa5ecf23e18c1a3c12b2d16b9c6e82ee04bf0b804da0b58653423dd304bdd3641ee027e7a4a11d387eb1f2cabd87de910c06bb0398f984837924432e2251b99fd739339f4acd7b27c9a53e66bc7724d38ea74481f03e3be3e6432a7bc6d003310baca8b7be7c500d22a19b0ecd6830db88c76d00aea0f3991a32201af16893a4571f1cd54b324e1175322263a230b471263a119432ae3018776b4b6673f7324286a65caba10d895a42a27a85b6422c0dab7cb67bbdcb055425095aaebc495d80a4975f6a615da9cbb150691230619884d57040385766fc0043952559e6855b18d465f369a669062885ce38ec0cbb131dc9d8ec02fc6dc0be11315e5427aecea4788925b3eb5615144914577912d9272a25ad64af6421620fda91e0b0f3efb6d58c7fb62e90df3814cc7db95d0850e977a90128910d372b71733d0d4895957053ab06eefb2df8017a21ce2186dcd22c02539caaf6bc4c03290e4a509aaef7a615b1b8b1015b1d4a3d2a4c6a47ada6d2c5eacc612a4124f3f40862c22731e84a2275603566249be8108d3e2fac9eb77a7daceb6d213c94bf25addf8b333555e234ce73b3244bfdc9e3ef421233d6757f11465c1dcbd64c920726618c4f4465b88ea7464eb8e8a110c9fa524ae510bde5c5cd24dc6d3f5e9b9924abbafbc1dff74ce29ec3879de1ee0060c8efca8eae33492859697d33d34134502a0c52289ed1312a70c4b38655acb24566a3e1a96297bab78862de26116bfb15988115671b9b8e7631964c4216c911d316075d17a5b050700350338511022304d0665a02ab45ce8c024d4a89de896dbdc41f6f8b2f7aa2c24c1213024df6473852afdf8fa4e3f0ac6ceb4e144711e7a828f10e43ff3dcdb6ca6b93ac526308d4265ca04c0495b8f7a316b9f2b9cc4b28b08c4334297a47e334ddb498c0b443884c0ee8af02259ed9a32cdb241f21a99a0eef999a62cca044e9c02659dec0c5d6a1c7be1b902b8507592e4938a4e9697385aecb09a434f9cd3aee8a09a028992df8b1df882523ea3a93748f841274c6be916e2654b48f5192442fd5f040e602814901ff8b2687629c7248b32311497697b76306e6916a047b77ad766b28fa7a1904e18043c0b118f0dec409da122c90b9847142900f15aa5ccfd9c421740a6625c8608820f638cc42c788d4f57acb729ebc40e27852e9b0447a2811a0fd05428f9075073f4221203ce8c46e1520c31413019d1ad1d3ada807298c7f47fecc0e6878dff6a48da3205c9d665eb106b1fe753474e00b7f18e9a2527790e4265a94bc74e081c32814388905dd81a28eb8cafe9c8b5becf05d4e44a26dbe21254fa99d4549e9b7ec2846dafc9b2e106761d45b8851ce0582858ae5f087e7a8681809008a8aeca15540814e981aeca796a1d8b575cf31028ba9a1069e5cb71311694de42d268ac06107be198781e013cb10c4918a9573e211c07d31f5faefed6c534a8547c375a4220a990029194829e7daf7e00e49d4903ccb1763312396ea09d939f5ae5612cf17e806a507acdbb129d324d63d363cab063290556aa175b3b669a9f0af493519885b9873d7836a5e9bb0e3a68f3821e9cdeaf3848f773edc4748fba495f222ff7f5ad39e7686e710141f48efa8a2ffa2ef21340dabfffa8dc69a8203dc7c7b141ecbbe6b6d20840826285488568f8986d263e5e7ab9ad9d53e18d22a03d1dd389b339078ac071ba2845e12421d6c58d33eab8669a2e2d24cf366516f69ab78f074268593ee09aedb984a83710b00f1f8b0cc9573e09abae54493c69482b6e37d64f737c89d3b83e695788a60d757c176c4a7daf01477697350b24bc9133af198ab23ea4e50366f0de01a58d140ea78174d87bf49c02195d2325265251b9b0375072e1640ed4f3ebab1d3a7d1c16f1fd0a1e977aaca32e20443ede465d50278c7f43611c57eb29d67ae03e3104e26a06a7220353790fc6c88ee00a2b484ac8e07df0f08c36d8e5427619933815217287b5f31ce5be36a430e9837babb540f0e46b8380a8d0f24cd82d229237d91e94d95a9977411a937abce30da41dfab9d4da3c9bc1b0b86a06db1dafcdf9bfeaf4b071fc7f0b892dda0d4f8e98f44ad8d0f8a50e10ed0d2aeac6c536cd32743b0aab1b617482d03567b4a24730dbe0d787cdf030137c786418b3714f85952806e11dd581328afce996b84de489f4be3765aee380aca3cef0f08653537715bb439a5ce67cfe7b3409ab3fab374d6f5a2b20b5c883a7478ea6424f37beff476a53684bc7276ae11915060c2d4529ff5552cc23ca3e9663d4a212958f0ae277b28ec6b755e42b532f57e19b949c5fbd671e9e9a959637187cc14455310c93eba01276f54b6162d76ca14899701cf67168af327bc80cdee2a1bc8d1bc271ecd6592021db940845289748ba8f589ec958ad22adfc6220c31d8ca1a410d4a16251915966b15b5c9b92c9169d1c0471bd7bc28bc4f229eb647d72091979e16d8959884f9611161a5ca7aa45337952359410617e6d86b58da14e6d9c8388d1c87d3d89ffc8a3475b52a6f5598cc93576983c6325004d9042c43147782daedc5456d2a9ef211e234827afd69fa21847d3b6b7e075977cb4039eddb5ae62036decbc025e5e631f964478e3cead867f232a2e0802b7f8b28d7b2f2e3e811767397c3e183973732506bd3cf5f80c478c839bcbb27cf94b75f44a9c5663107a752133c1cfcc786b3d6cdc37bd36f914433e1471952e9085b1a59c10242be80aa69ee47f7388e1fd6ec62382855d91ccc6dd70a937dc76261c1219efdf46b19f17963978eb57351cb248b70b0d8464513e43e8ebaa22f12889c9140e0cf45886ca82ccd07c356c426309a36f3d86f101708f7ef3c0a61d82bcc4983caf65ef982cb8ea58c49e3b63295eadd753fde333c609ed2b2769602b15fad3c8dbed15b088a09d1af10d5a14ad5ec52e007fc79fde4bef2cf3ca5669d92ba6db267a7aade21ab7e8114bd1408b03770e34cc1cea04988b579928b86f927f1802243a3fe4a634f2d1fe9947c33367cac2dd7e879d490e7fdb5f37d4df58bbf45cfcd11fb674ae31ceea07276f29178cc50aed8445c89227a839c173d6287c2b58d9d08cb7b4c6992305679bb4af83fd82a2eaf2a908ee010403d72fb7e44f31f6ad651f45a420db449f5dea8460392164d4e09020a24aa3ffc0841a745b905e8d723838849378c4b9842fc225950731fd090941007fc42a1bc84ac0527c5a1fea24ec8b786d004ea687335c7aaefe590a78df6dca954616e699c0b2277d2eab72aa65b4163bd5c420034f32fde5c1e3691ecc48e78e5d9134aa1230266d6c026174d8811918ea1deb82c7b128ebb8e85339238ecbb05d45963ac2cce94f964746c1f2361cf9dab184d5576fce728a34b74406540b9fd272e49536f0f7e35d119830afd00b618a2f2a8b1fb6b5c78d616c0bee5b6cb3c10c81b68c83aad9f6293cc2a57f479c24d5ce41e6d99ff08a558ebe344877238cb106353ff5cf5f88f8f0afee216edfe296170fdc7614bb1abcc28c9fd9e24334ab3ec159a203d1651bd695f76749e2b2a9e8039e21b6e2d87680b02dc6ea545c17400fd6f08a68dd3e1edbd283197d9aa12d264499cf4e9447f5018529a41a7d0b49dc1aeed5830ba5c4a9a701351aebac2436657ebea5e0c43bf58dd43a1072455df10fb43162b1d3084891c521fb02862536aa0b6901370ffdee1cf0c870b29837dd99bc9ae555b960a42c8817e280f8a3c7a0509cb3cffca51761f152fbc95c97a1218551620a2f346c0c8ed7d213220da75c935ec3e2e767ba8611ee685878ebfcdffdb37731c5ec3e991c7f9d5f84e2222926433e53c3d6ccd7148fd389999651c1764c58d66a82fd48928626ac1c28b22a58a22ef15e08495cefd6f84aebf4b18eecb81bcf92c05954ed4208d8e4ab950995d82daf41876d864db25a8959386ae1a21ba7e9c55bd27f6126c874bbba0e95516c261b70cc0a9d990c5265dce956972d396531a39934669fbf25ab2f99c3948330b100f9e87daa33b4dfa373982211083c8f98df4e59ff9513af110fc8098c7d76ed7b9f8e09da7be93b64258b7e3f4b3e757e2117853b17c10f06b14d73fa0acbfd8f5a0d31dd8b523b1410f1c3b16f9273f80ef071dc0fe9024bb0124a606b656d9e78fc87d22a20a3e5236fd951f591cb2b45c9935557b94ff7ad5e004a8c3dc88906ad5b6b8a9db48515a330e114cd8b8ccf5123d81956f0b2e6d65f93c20d24fbcad5a07dafeae9fab102ce62a8d91ab60132acfb52a00bca64b4d1637cb6d82ad962eaa5fe2b52055f04aa95dbf381f219886d4f484db23f35876d07fb4d39f8d54dbc0ce18986f6a3e87baac3b4a6aa4f55b81407f613a9876e791d0aae5d1b5ffda3b011d48d25e26744ef7d1ee2cba70f77b1d1241e407371de76c18a844661a66119658b46fdc65c1af15f5fcc93a80d1da5a210601353e9b77d67871082b094a971dfdbebf6a533cca4c184bf4f2a8613dfb56ed8bf2523c43af3f0e327615e0f085dd01134d2203967699919511621203302979f39458e8842459a95f85fe784a9fdc124d9573ebe38132d3d1784c90d9f4624cf7b95136661fb96befbbae33f8957e8cdd3798cb6e426101d50f5a41d95a7716d1e8238a00a7d85aad9de8a861b109973a09939e27025c13e5b68a8914a52691e56c404a3d895921aa18a1c7ca69826bb03d84547735513c05c2a33eaa0ada3fa117042a103d14cb5324fa2cb724ba442057305c81e823df9940dcd764bdd056b90a569c487074c7415ca9c2b946fb06b70302fc1c0452c8f2d3257e06dd0c1ca0e19413c3e828d341d02c9e641fc61cd0f096fb5431847ed286e5b42188349b53c3f1f191def77a0fa2fafec2dbdebed89303558466d92ec0a9c810f07186b507e0c7659c1d3ddbe95363c4db0237db98357483bd955f26b3b212083cfca87d59692f8663ecabba17e392ad0175af8fcf17e3113b5f1e06d0698fe0030b6eb360d58bcfe669a9d91f966825d3b9415801cfb5034a4dcb0e67409f08e0e039d22cbfe400a1bf912c8a84f2ca03590d7f45ff9371e1a0b564839936891a5bc3d7c8ae810e5123eb556e2c9e4f2745b801363901982cb412952ff2abd0b8e0efa52e7251a2d0a438efe0ce798cc37d267d63f1ca541e456a3cec4bea4524e234e36155615c2393cd80ed8500f29750c5e5ca1c716eff16e2db4f300512c013ec3b67d494133c02edf0f16467313d9671a7467c7380ce87ceb0c0cf0a27c8b2108fb0322a08328b6f90e2f33b5afb50fbce78f76f1a130065eb70b598988a3cf0bd25594ed8938411c9741eda477ef5b4df0d2f9c6338d58c070775a65f6ea4f6d686e3a0df4d83915e437e9f9f1cd3710e8d00f81d55880f6d2d0ed703dff28b601dc48e5b47c94a3085794666cb41dd1f4ef036516d188af4df0614c91edd2014e290a0624b1849660520631b2c0d42b38c33a9497bc473b56bcac1efc98d2cc5bd198513e9bf75b6df4fec5955376801b7f1b6da1eceb2492ab0f593ada5eafa58c753d807dde5ab6b23aa0892eb0f46e5a6a04ace2b9a37088a123dfc0f287a2fd26f48574c64dc020d24aa72cdc35d3bb84ed270527b6e3355d5408df9b99d195a30e985000e786814fab750005cc3e6437ea45b8f4602191db702374a201d02b3b13036a0a6e274c0b1ef0ac8224ec644649cd36ace598cd72c3710909105cbc0622c5be05be57c023376ebac087dd4f7647ec70b7b9e0580590baf160366263c35a7ea638bf6479f9729da9c2f485a00aa7add3d3d18372469a27116f1c0cd3d17c8b5a3d9387dafc47b30f4d88db68a9e238b6981182cc5f87c7bebfae586d93e529476ad76fe0a297ab080659bc2c04acbdbf1c5a7594f0703645e472082c8f95380701c8211c08006c0989ec32f5e00cf022b7fb55c5b7bf399161760986843a161dc70bf51d50f65bee384c652bdf5a16c9a168c16746aeb5fc952913f827bbd5d441d044e466219a82be6508426472753128d961b0f8fccbce9421d1bea7cfa5f9c0dd496c06905fc7027e040354a7143df8340a658c151a7704ef97559aff683c3d4a30c5ec395722c119a7a66b594ee1f696aa97436b1c4e16e614e1e197a0aad08564b740b2d36887a82d0fc353dcccd0504fad718c8e4bc8581d66d48cb5bcd2cd09a0544b1028a8cc9ec4e59f5091897301651b10d95d6cc164f9f45d683507217baae842ece7d82e1a1a00d1b16187340937e81ae4a008b992ee9a7b1b84a2b9a6bf7431e367c0464094ee91b2cec116ba91bbd6cf00030e9055f5e70a70b4ef882dfcefae3de15d1ee63ff2371822643284b1944832080196437f03aa35b466b14bc6b51a80784d3c2c4cfc0b92a03539e12ba8566278d0bc20ee786328fa90544c7056e19114087114056ce1e7b0aa87c6056cf3736944ad9f545d5932e199443d42bc3aee1480ab438f00a936a4c3e90a1fdd42e0e182162c44b69e423c0facc86906c81234183503ca541ab3d7b7a6238ae29f1ac0116e80967ee51ba79b89c516c5638ec307f3cf110e004ef4d2f6913e8045e78e9bb23154a80cedf74f8382f1fab8496c227af1d3f4b788eb056e4385cc9a9c326df106420d5daefc1d28468439fc1cb5329f38a49691726f0b4e45f582116444ca580a154b3f921fe91fa06f062b5b947a3b8656e9715f8e6583207fa98e3b2f52684b36e78fc55610619b7b5ca008ebc2dc3169141c26b50d369117077c667d46e421c21554515e2a3ce7ce34435d847dcad15c01682eb4010d2ac13a43e9436f8ce18a8a53910e705decd952caffa74a6fa91c367f45a7c7512df4494fcaa87c9e2a66dd496962b19447d4b32ead53393a2a499cba3e1dcc3ec8d31cf4758e82df5107447ab7670fa284b62335150a9146608344daaeb9ac8c41c0817e823059c87cd43356cdf515da544db467506a39f951ce9dd44842183c4c143e99c1df01cbc67f1731e207b661fbb0f46facc8559b68836ff4125f338174b2846fcd99fd55b09248d57a968ddb8a6f6058fd4da0f0547075521ef0798649d78c7ac400ec341f48fbd8db9b13ff6d73dc712f23f0754b148d91bb18122776e7da549e0b82c92fee4c3e67bdae0310029d9d539a3f2204d35519c13171cd5359eca0ce8e00b15a3e650816914b1003bd0d07d228bbe3c26eb2694000259872734306b707ef4128cb6355bf339b43e630ca3f7bb5da5b6b9e296ddc59961744b521b90f4bc3aea6c91d004182955fe8de4928e81d51572649f4754866592837429bc7173034f24329861723c15704e0a6ad47f69762fdcc1d214889b29e540dd0b1d457448e56c16d2781feb88c78e60528a803b7fb34dc13cc158cd51afcead52f254fa4dd32965d7dc25fef135234a908e91d03fa17ed28e6c56ed2da44d3ef247ac7cf4a38369e18a39c4f23c5fcaadc780d6ba81d8739c13556a910288bf991f6abfe51c0616a7880fc4701f00c40efe5193e5412e10dd474a0635d7f0f3b4a7b207b77e1258d1592af8a28c25588325ff88ad082091a7dc1555afe2035046c956781aa00157fcfe5bfbe3c5f01e88f7ee923877b23ae2215d8c51f1960a13553f2508705d9680f73a52c975b35284ddb1732efc4e9e211737fce91b9a5e010cba0685390292d5979f7f0618e62da8a0b280b00cbb611f1a167c2f18afe28aa8de8c48872fd6e5ce4ef426587bdf54445742075627728864142c796904b27940dd11f8380732ee52a1c9a91bc4c38ab6b94c748d6d661571f657b25e7c854b73a43f2407f376ab492e9966a066ed30f3c54e0fddcd3ee0b5f17f07c972f9111dcdbb063e6b14c2b474473622042bd9b1499d75b6fe4d65ab4a89c2c404dc824731e44afa32b22e8ec3aee9fc2339140e4b5657979920aaa98b0c4b04b37d0d80dc415d282fc4339471e24c5414038d840db3d23f70a5f60047af83689ed369b00cb2c5ce05e28f6adaf1c353af53ef338e3e2b5a68acef55803106df961aa649dd7b3b41d8a4dd058da12f6a38c43df69117ac06ee54bdf23afba8fe2e357f25b16f8c4d5791fd895db7b1ff98d9092aa1575d68b5be753c24a10174befecde8189b786c776467373a9712a73149b4f4eecb9a35e7cad75c5ddc7a495c930406780d83cada9015c659c58659bf0590440d614fe15aa91008ad9111bce8757c050060e1b289ff6a4f95774987cb2f82266da31d1bead31d1fc3e800346aaf6b35adbac33d07c535dc9cce6c061e5d63a9ed8ff6a08bce73c79ef4a99a41c87cce1fe2ef48ee3dd5b35d583e7c559c625af5bea820ceea5bc27dd98bb33a4e351f727521f23a23f2e2112a5406743c5ddd147f397aa06054a932baf618eb9e772aa22d461f4a4b6c6ccfe0d37bb19e8ebbaef6228029e79ca45c7d5ba43dd95ef69c1e443d09e9117aa3b7a3af9e8c108e5464be19cf04a7a582149a035a55b5b078a1ba9fbacde9e495491432bf22d8cbe4f444a86f3a3d51b954ae9223c853ca55f403d241b98a6e204444a27d042774b2ff8c65018df64ba2d1c4988b87cb09ce13e8de3e85dc97a807c6aa955e8a69b124f860a5cbb9611e49676ab5a4d2cbd04dc9df5cbb8e8ee9a9287802d78e374d644ac49330f7891248c342708e7f557b30cead8147844349c83d18d2b417d313e23f83aee135787120349003987a19d6309bd6400afb4c11710d305c3c00341cf9eaac371018a911412a39cc113b221b3f8735fb3e72526748a2eb41b10def097c3ca6f86b4d20d2b4518983020284dfa781da6eac4405a664a638455dc2186127be19e726192b7d111180f10b8a34507b81789b934a302c3394dbd1638c362c4540a238cd86289583ad48febda3ab87c2cd4c004110cd4bc5baeb6d418dbdfa4958507a1f90dbf0aac7dd30c415fa6ac52823abe3a6c5fdbc2c91dba2f9a8b18799cfb12e11262c86b2bace48ded5c6ba43348ebca23aa0ebf07c56c6a8652a366bcee4831ab154b8bde9a0a7575c193dfa8748c8f07b22c73479c57eae956b148f5b8d73a23f6dd5c86e713e3eb5e4771b50fb3083c5053a939d5fe2e19f095bdbe17b476e91ac0145870b2b56d1ae256fc9115c8bda8a900822438452ab5a17f8d508a4804655651fcb653dd8f76ad0284541ef95c7cb4809b976870834bce643683c0a60936ed975a53a53389b1dfb51ff1c22909ffccd7b4684416d550c5311f1b2c32fce0daf36dab0c058b4e4b08418df82704bd3862a5fb1af5b102883e8310b5e4d34749bdf939fd5db1e29a2d3d1d5a2e370e6654da3d2f64f272c96429299c0879415b2d4093de1849cfdd4511db0401d7e26856fec38125dabf21327393fd00570611b454324a6e8316a29297b889079cb313122e4acc2578a94d33f906c93afc367a3eac94ea8bf6a87f61ea4a7d345fc3ddde6cd9fd619b60c3ca303919c2a261474c6e1b5b20833a94c2b97bec2a684e6c7cd4420eb773ab5e666368981086d3f62c82bdc8b0092c0001860d7d7adca50f2d985e95e8ddfcc1b657c810160dd8df55f7171b26778fe5b77e712898e065834d6901708246cbedfa9ea0c89cc201b42ffc14e0f18604039da3a557f6384fd7532299902d399625a691e862d51da3d5c1be6487dfc5811fd83da53b80047e0dedc5d9f5dcb8f34205b56f01d4262ad49cc2eb5e318cdad8b36cb2e9bfe196f677c1df75cd224d2453086e47b09ebe736430bc8387d11552865fbc1bf16c2f02d7d3b175890b64bbf09421f3f842ea0dd0f1138671b60d6254dfb35e16d763fb4a44f6fd13437b4dec0b32ec0f340b847f10585091c87a63ef2a5f3bf083cc6fdf97bbe60773838c136060c1e2ba81507775c2d95436433c630aeee3b8936c3219ff2163db6ad9499969357ef54300c1bbce780a40790cf890cdc6508ee046cee004f810c7db82487f59b3d3fa99b4ea7a036bea1f453fa80f9fff1a5b59910e8fb72d0182b6fe4b69e36ab4c43f681a3af5a3017a881aa70550d01532c9f1febbe87febd0969891772667cec26ff8ae2bb7b68fa28c5a23033e434ee808b8410a1ed8f9b9f81e3b4c712395e69f33afc2691e5d13511513715dbfbf85aaf52182fc57ee6f37a05b08baf4c17ca56de90d566468275d048d8e5bd7d417b70316598f09be841bdca4876e17ec00485191def3230cf13b76896af4b863105559fd262db46034f3db35746906319324ba91f4930de76c570559fe73d28f6c371358466bb248bd8da057140acb8e1811e8b2f8b7f82c805f3171cca692fedb330a4ca7a9167b1613f08028d09f04d1086492a1a99a0aaa2b2ec13112a645cb9096b823e208b81dc3ecf2251e4ed15800a66c7477951c4e3043607a3e90aca6f57f225cdd93cf626867a47a812ef4873133cf340d39b2534f93459bca290df3a4687b2677acc07667b16691ef25ede4d7aabd137dab36fe99d02b675aea3440691d68e4251ee1b152c5bc4c63aac182a6b5e1b59faf834c009b50df64ddf58563c8c5e18642cd1cf154d5e6b2c732b15ca4504b65dfb34dfd8a836aa6c3c04cc1a7141bbd62068de00287b9b6569e5a42a03fcb7a0a7823dad3f712fbe6e721363f3d97f384605fef15158585bafe8412b085f9e0e26c282a696e25ad211825c9395c46c4434176ea264698a03fb1adc32836834a7eba82acda8a28d25c61536d85056b3e0c8626ed2eb3f5f2bf5ae1f7bec4076c7ce1bb326f0b23f3d129976a8adff28ef2e94b477373dd25946a8fc78014fb045e45e71830f372502121cd27c487c7e26c4124d45329a832a059847d58661e79e273a4c504ebbc8a51aa481da5d26eb79be0fdba71457a049a63671faced14adf3edd05bfadbde67792cb6cd609e5d2f6e9e6294c6dcbabb11f21852abce48e665baa8618fd3a3b1a051dbe04373ae72a27012285aa6efaef9f98c8231b20de203ecaa0c114a34a2703e0bff59f2d9d35b55a1cc2f3cd077acf269b17917431c26f4cc2edf8700e5ab8950f9d812f161a1cd7dd1bd5b81cff03ffd46bc298b68993f5e8e3e9142fdffd081e844f65847b0b0ee4f714b52e9b6578b13629e243c53f047e4fa583ad13637e1bb76d0e6cbfa72e3648d30ee2fe932c9ded005fd401d26c2a0e7c09b5654830356e73ec674c376d70b3c5c2a7c895d7e3aa2f3a5f404c02c5a1688a94d9948956c1b32fd23d9e374c8b3f5a1761341785ad90aae0528d666724e70f000670da5389c647d2208957fd4c4e712e9f633f921d6ac04bce603ef27b2a3ae3908a7d2aaaee696cd0a3700f738262170f9a84aad426cea2f8d27c6f53776393bdfc4115d2013601d0d34614a51c67f9c49f8ad48872b6b029da3f4d83ed97094684bee123a13ae20b92c589302692378c137308986734a7ae1ca23275aa48ab8297fe99cc6ef4e8482dc05886df39efc7f5cdd24d1bc2f6a96b1985e6ec1c6275a06baa237da6d9ad2ab63009fd8e792c6c480ff3b8bd4e4198a8107a53a33816c37e2a4a3f2bdef42ff7342890a3f88a85f266a87ce429e4f50c3327559db4a6256d59fc598572980a46545fbf0abafe820abf1ff2a36e4468c33ef94386bfcca5eba1b1a0093fa5e0e919f6a0f184c4763a8d2da8679b79e8be05a7701f3a24cb1b5a69a0134eb03cd1b3762e8fffce8681ea5d30e308862245219def0b077c23d53a8acaee56a5f69bdb3bd74fb82aa7702731e591198b6d2232c13ff929e48d5d88343d9c2f1142c5e526de647a9119d3ce63eaf9b107cd7a6ee2b8b3d913620efe2aa409569c683f5d6ee85a43573af4d5d8949a5e89351112a88678df99118f25dd9ed4c417d952bc0a7d0316efd2fdc05e734e1e1e1de02de606203d5e0228068e0ee036ce1d900e571a3006430fe806ee268a93bfd17c6837b41fed0ded477fa1fdd06ff43fb41bfd436f43f3d16ae8bea7de758663a35fc4e17ccb55b440d76d04e9424e7114ecf621b753ba172136ee5cb0df94c99928442e6844fbc8225416a24e6bdd8001b7db59ef544cfed2c2c75f03d6ef3a3c17f47d84bed0d27f4ec0db563e3a770c6bd29aa4c61d9b7b1bb08fe611196ad49d8e2fe2e9398057eefa6db2fc5d0b3c21a8b8800032c561536b43b7b42b3fa809260e66d22287878352ce0f37891a4f395b5c786b9ddaea5a81c5d9dc66abd2744578aa79b7d5d3b3951214a12d33d7d60181ebb0526c5bd134b6db2e1257abc8d7ef4aad6a116dee078ad6482dc48dfd7880442d9c0443ca00034e2b620edf89303f6bff50b2a655b8f9328034a3b4961bfe57c992561a9ab301438fd604893d7e9b7cdafe53baa2b5e450185885d82b8c8b2bb4b073f99e84b959ff49a9052d149a0d0891f428f7f8ace2c6fe3e54c02e52b3e924798f0700b54cb9cd3a3c733f4fdbde3f2604532feeb4c19cb8602466e1f063a29e9d7565a5960981b6c95d7b8ca8d828a25a03aaf1dac558834239446db87f6049b86f6375b7941b5ebcbe860da63a338a629f07b9aa8a02c407429804b5dd52eb19dcc9392e5b426a95b4e762aca6f1ba260987228485b4af606a455f2b17a8c74f2cbefda54651c0df1e6e4669864d895ac87113cda3eb2c2ccedf1448c2c5162357f89b3d386d8898ca842b672db23920c83b74dc8b6265b7e690e0585cd33e0b2db534bff9366d3f0ba0b0c52b01ec6d93fc458ced37d3f7a754ff0929edd71a4a17dc8464850e027d63d1614fa474517fbc809885b7598b6cbfafac2e747d708e172ce98bb4c95fd317094def3b0cbd1f8ce330b05f60685f23bd5a9e3c064ab624cb8cfb1f220b8c4b0bc3d97ca92434c49570dd6a74dc136961d4f989011c8f1a31a11a668ed96884b1217af3abed13d0d9712e8e31061d907a201dc97ee5d7927084bef1d4b532bcb2b9dce691508e9469daef28e6b12bacdbf296243c311876e1621b5185b0c378822b4c12f6e3fa21257e892dd00122fc92509b87c1724e69e46aba997276ca540bf45401b875bbd0e7e1af87127edca9cc00e443e6f79dce13039e842be49e06b36e61c4dca41e4247e62df3fb0d0d4cb6840b86296fcd104389224881862ec123093186e3d80de0bcc6a92fd670be10b5ef9d41978576406b7a7c34ebd052a8f0158b69258a7afadc41f520d629d8e73e9ee19af1e0a5abe499bcef2f9a03506e8ebafca8e626b30aab9b5e31ccc486e88aec9bed75f8f64dd2d9d1153814617588677eedb1721e2ef5d8a1ef5fba49a11bcf58dbcaeb078db2e723ba8bcb06c9a5f7b75c2b6da09e5d202117f4431a23b0f1c41730ec49a6b745178cd7347c47bab7179dbb3847468da9e375cb789e3de86cb0e9e19eaf4443fb38a433bf21b889346e127585acf594c0310eaf90614fd2be3d7ed75a9bbc5420613146057141f7d72de41ca0d456f44acf55e73f15d387666a58d763434e4f61edab621d3ba79b6d8334ea13417dbff1ee86116b2d90b66cce9f90658272cbe596348b494d8ea95310879c97883654cb44b264ea2ad50d8e5a873c9589a4a7c43d0248f130165e1ae71a9893d307c7dcea74cbc6b60459c4a607d232e3b80dee182338ecdae8698dd4029cac73a7d9ff327ef4d88faaf730a88d647083dea37a8cdcc4ec54f73f864cde87574192ea8dfbc924c1f27fc87ab843d9d08274f36770958fd4bb3c366fd326fd5892af52a676e46d3b64401b3acac5f8435be72d15619eedb9ec1f377b522ce93d1762e2a0c6a032af1bc834f73817763699999fb7957d67d53066c1e54dc3d0711e57f8792497fbe70078f0d8cbd7703c4f9fcc0f1bec06ac203a4e37da4491c3c7561fcba6f757509e12657a9f0fe353d0172db7f221cc192afe11aa2baea6fff3101ee73771e7e87c8c1054cf27bddb1a68f4e4f214287bc60506bb6f25796171d7c40b6afc5f8167910ffc362faab91c68bd729227ce7ec4be3d536c4aa7ebe0bb14c531b2893a80ca59de840770abe97562da09fd0b60cc9d419b2dd9cb137afcec605da9c7dc0e32cc6ab595ca18c543f8cdb11c5647f83c5264a738232ab26d70251f856d2747585e156aa2986ffba9a30177c2460eeac447bd493f4251a042752d76e160944dd7886498e773787dc760dd53e6bcce37cf5fd11c14bddd4935ff4d4e7931fbd0e07b2a30cfe452452af6f28faf2f1373ef729c149978445414ce3270f9bb27daa7804debadf19af0c822cfc6f94bda5906894de42ea9bf81259769a17a2812b688b913197709b1338dcb3c104a2ced452b4904540c4db5282582f49f6f1f943981c220c38c4367e1c4ef4280272abb89ced7502de2f2f441979846db045ad9255143d2606b10fb04bd2649abbe2000559e961ae726e9bef62b1b85d8463768d67fdebea7f0b23c7530480dc2bd7749098244e75a988f042e258c7e3ee0e64834afcf625610fb28bdb9a6ff69899fe9a6906ea4d8853723e80ac58ebc51883e32f6c40d0af56edb9bb35ee0f6b2d48be59377f3e6e7fe92ee2bbd14fb4777e9f6777f41f72bb98c95dfcdbfbd12ea1559af400e7d5a3151ce41f5c709d5f71531a3a79e2b416d9e325708fa22096725df84c244c527f9be2a466cdd2e43980afcaf944d46c02e819d21ac3ff0c89c08047f46658cce3c2ec578411ba32a74352029fa3bbdb894c4cc6ddb852e16f1d2803eaf239d781fa13a979bdaf38ea165d3fcc419489252cf4e0712db4ca744071f912b1ea7ae1654f112364f1721ca007fd377d605a3b6b58cf0ceb1dccc245a19452c7efe0ae5d85837d9fb0e18d41458cb4ca004e110e7c82a83546a743b01a9e1daf1f1a8a9131dc7b9f88a97ff00797807cdc238120f9376460a53cabc56d3b284f66d6459c09662ed0361de5d5a4c66efcbbc44a19f4f86d512549df93e61ca36606bab9e9fd6f2eef15366e6b4e26a7fea145264cf7e0166ef3832c151f32bfae30c49c11f5dff0a92e818842cec4b64b90b3b892554962c9beedeb39d55fa5a7953b482ad9f2406ccc7a26f81cc873f5eaa5964d66f86eddc3514ada30f1bbfc84618b996ce048b3cf049e048d5bbd755978531fc2f097fa2309f2865526e52248266f0f1d2c53140c0684445af8fa9ab39516d0f9efe43329840e9e9b92ac470b2b0fa20f1d967df3ef3e9cb77be7df2d997cfe6a3c6fdd925b21cc6d8f8f6d557dfbef2e9c3573e7df3917d98f521079f99171920a9180ed195918e3b6ce60399442b899bd10a058d9006d06f091b149ee184599bbd8f866ccce77d630123de2855b6bcf6420609bc3e4ea0815a94532fcdc0f25e1d74ce7cc63e533a0c2910347c63a728c4c36e6c60fb6bebe5d14334202c3aa49e06a80ffb95089c060ce06ec006fb37fccc52fbad88669c196244023c1d2aba4d5ae20b0d45e80208c1776eec78e8b6250ab4c282263b1462c991dbc5aab1ebcc32c82ab1df232b4d58bc4c16797580b074c33cf44afffde9b698b8faaa5b847ed67f16d59015b7807ee17f92e8b50a9017137d4e323f2630c321a5794ec63feadaf828d19c21c6ce86f6fbca72dfc651ce6ef99f39397f4933765c6f4eda3f5e942593f23527891fcbc8de2c9af1e1d0ac517fa849a6251ac99cac3f1e1fc3d76866c30ab346fda100991634ee72b2fc786e0c5fd0100843cb1af587ca3119d1d0cac9fae3ca1db1973562cc6acaacf9f7604ca23cf9649afc44276699d1382886c986fefb42713bc111c96ef59f7d38cf8b2287896630a3e287069cb9f160d88a5c88bf3f88673ff64176f6fff8c27d85babe998fa952673a33590ca3451debe76a33aee3c2daae98cc44a121881b6b861b9862e61cfb9bc56edad38c4145ab486d86f52ae98ac65a0de0864c522970792e73d9fbb708ea6c64e790014417839bc5572ee70c3b69b083018d330e6b8987eef3b4ddf15c39f113043aecde811d651aeb29698e4ffb504c0fe3d13d8c7da6b044d5818d73bdc45fe3a94547f4faae7b7ee13641dd7c85720dbd2fa98fd11a015b5b8e3a2dafc8d9000f05f1423fc4cb7f2915050178bc23b788d72a446dbd502c1fdbc6b2410b55f96fcdc36c88ddd3e9b716b3d54ee221e37cba3972cc2fdc4f923f4dc3262ee6bfef93bbcc71780f1f80b72645b1ce733eeedc677ec1fd3809184d52c5d148572c9eb16434cdb2778ffb82b76ace90b298c8f3be368faf590eee114038aae12e9619ced791bbcd2fdc8fdf05a3c9c2b8ce9f3e37c737c7e11e0e1f7e4d2e636b7c675ca6712eb3dd8f268c5287340e6c6bf13ddbe71c737b1096b9b5b08de12d2262cf5ee727db4fde4aa486711c8d39c762dfb3dcb2a4268522dd01a2b88e6bec7e83f94174c72f922d29f2f6fae9c6cee71934b9640347c04672b3272d741c8b3cd08b8ad913e5b5637656ef16428444d39238778b8f3f40aca36a8f6f1ad999bdb5305c7e12610be62704cdcfbd5db2ac992ef6401aac1ffeaef0818fde1fbf2f34201edbd3bd45ab10b20f5fe5184abf2f43e9d085d2c69b24fc9a9e593d6120fe0178eced4984508c6830257cf47c27c8637470902cc77e468e09c6fb816ff53afcfe300178ec4ff7d2c8f8c3974506bbb847837285e8a87a5c0ac981c320c3361e2fd9e9a0b56be214a2540355718fb4cff6a95ebc4e8ca8387e32090193b4a31a56b7f107b040c6ab9c05c03443afff1a2f5bdc59b731c4de379653cb79e3fe4e3cfded7866c5c45fccb7e45659c5f3f4121883a7c8b03759a45e396087bd58cce182c693006ea9a8481a7bf498ed12f1f58c1ad95b3fb16363aa662bd799ade697d669edaedee348a1c8fd60dd83392be97789fb9e3fc2200cbbdbb7f22487fe98a2b30a1e4cddba769153a515dd45f190c3db9d4ab30be6e436ad5c78e287f616e3df225081aeedd1bd38fcd9bd78d501caa2550f1fce6b6ddc50ca612d7302ad1714f003b189c20a4cddc3d702b910a8d361f56a9891a2dcf8c1086aa41795857be95a90c1b83f27216a7826fd0078107f52893e9a1cb334f0c8ed2228c4a86aac9698d1bfe203353daaf487fe6aa02d4498973ecf8acbc4c6351f8ce5716362c0a2f0f4937668ffa672a5af257f80d99194e7c1f578bfb398c995a906cceebbc1c707b838d994995b66503616193d0d560f6d8d45169e9398b8dde22d50c98ce897243ae4f4b99c6586242f76cb484a29b54f4cc797d703035fe1801bcc40a676f1fb21996edc1dfd2946f122ed9dacf6b88a7778e2551237fd378fbbe848a4dc3b414cd09d7df2f2ef83067d32617f66683b4d1c002ffae112a1eb4d5ada90cea364bed8a2f4a0d0312476c07b0e7fa5fb4b37acd6fee77398c8ccb10273f41d6b53b489f785c046a3ef33106bcb0ebdc8419feb4aec7d9fb96102a614284667011854bc971f98ffc1bd42809afec8f7d738ca562c9be6f7c702b9850d178c3d972cc080e6ee02d4f5dff71ba3505d3720f43c521953d4742a38ee3fdea7180572d441d27127074c5cfa479731b0a75b56e31518a68172991ff30e5da09b92ff4e0e5010227f241bc1f24637b15abb3fee6135125ee133dfd1ac2e58ef667d296482bd76608807b21b0c8c9e8b2ca28c92be0f9fe5f129385ed21fee2986da8b1e2260da6ae5269c13208423ec3507472842464766c844c521bd95e9f7c1f5e77db5f395a0f1b289bde39a785e5e967deef61f35bddcabc0df4a28a10fd43a153590bf88e12fef76a882aba99b76091478d6b2c2028e1fa00ffa389f9a85577d9da592d41c956812d729ef482496a6bd24f5bd1b8826e2ee109dd1ffd367beb2e7a4cfdbe9647b57defb5dceea91d2ed9b7c7fd0786c24486c29450794e9e75d5f4490d3d6d52052e81034d509e2c1bd0cc95ad88c879918af0bc632966449a16c33b2113fb426ba09bb5f393cd406052041bb68476943cec2b0498aad8ef4e36518b59d59b2e63d716dc6cfb0a6c52479d8dcba39fad75be86196c3f987fc2082ca3e96a4ad63f65a0950fe6cc03dc6d0bd5fb12946ccb9e3d1a432df3999acc9cbac6b04ff421ed920af19db8e1aa940701d8740a69303af968f16a23cffe6680cf48b5e3ce2038fc1a25a8e748b4b542a87ce41ba932a247b2ad87aa343a05820fec59f00fdd1dbdd525108045aae03630f5120442d2fc8621bbe20162ba8da4a0836d644bab88b2bc0f213e57e21d1afb45752fe601a89ed744e04060c9945a3be2c53d66679f46f3cba8a9963ee234da598f3ac8e0294639545efa629330f9dba68a6fae01ac373fee03c83975e6530c2a7baa42375a34716583da9ddf556b89050eb3284e6d033136bedc51d809027d059c6c057ae89598d608a36d7c5653e8b14fc078cc4c23caae4ff6378b392613024d50056d77c817a7d8c5a080f8d7002ba60634c4573028012b6f70af1459051d1f34d698f3650285863e38801776cb3890e8d34013792265fa1ccb14f7f42d1f2ef1825563d2348bb85c682aba6539f59c3fe50350eb8edd5eacca5d2ebf2cc8e09ddd6b4742b329fde4f1b2a1fdd0ace22d5c6f3de88c335cad0bb726b75af123870576eee0b354331229e2fcdc41ee9833545a3c959df5a20ae48c947b576b3bf95b6563e00c834103cdc535ed89060e38409976f34a28271329a9f3ecb9c836a8c542bd71b578064777fab8fb49d81ec33066ff5edcad1a76ddcbbe1311bc591b28cdce4c5a7b32c85f3f9a3ed8b080557faa28798eb60cb88a72b8c6a96a7f226e52479852d22288cbfcf5c6825bfc30ed3865dc82bbd990a20d57ab689039c243dae8cd834450560ac703593e41c8711f810ca9f3d2a92853878386b3bba7c32bdc72ef3bd01312dcb794b388fa0a8afcab83c49cd9079fa3ca15bd9661bb3977841cf97b0d2680591a1f6faa6c0776c36a23a2a36a40385c2535751c9d2801d469432fbe565956ec044519152d6b29e7e42e319c335cf0a5c04d120ebcc99f3f465e4e5b5fe885195759c3a626307482b7da5c9fb40c3593f60e20da1c750dc1265c63c4507b72195e8eb45d498067644eb1f015520ddbb9fa46ce8e8cfa08eac0657aba4ecf244d535f2ac748c838652e95b5b91c2facbb172e22a365dbc4e2889b2a8b1cb550eb7c9253d6c2b6aa36f750a3ab8ba2861f1cd28b58edb7c0b0f5d8ba3e9de887537c7f09b6be83ea03cc3775a7634957076b43c1aea6cc62bc7961ba2c1c032559976f401f365492a3dfcec1011cfbc73aaa77a935dc334d89386a22ff3d15967c6325c68711469cc061c798a4064a78d7cd7cea28231d499ee6934a86615826d07a391d3d51f1a508e7823e411bbd9d420d87d09e79bb15f830980600152cadc78d509eae715454e32c045b1984171b51c5db95acd27d44613cc4412fa57ff5c140414296b29e449a20134885c382e9289cccbd3ee92e247dcc45747d80c733cefcf5d4a7615e86ce7566bcde6b6e794b35fe00f350c1c32a3174474e9072239374b077864a8cf879f436b5a282748af15e83b2263535a9d20adb349c878507f299510673ad3dc43e0c201cc66e4203171d0383b32ada5f98e7c88759aa0bd8d5bf503d5265d421bfbff41615f435134da99c4b2a254af0cb1df276e98f7c494dbcfcacaf888508ecf18cd5e717da850b2695ed679b6ee56fbf2ac55d5039c4c3297797a31a32373d2562e77c0d7e616179ddc2bf378d84b152ff5bb3310c877ec2310b4b2328364df4fed3788c443071acd527601159926d79329f9e37166446537719b9f4ab7363bda3f1a57afb09e6204586aa0dc8f581dc0457bba53df2f11a13735d7b198603b95ec0cb9d578ca0350ac209e2b551fc06f8415fa87d65a77145ccdd63864bd8697f22a4c89e096ebc43e092b2af51dfadf8d08783d7b5cda812a6ac7f7cce0aa6af3993d7520e2275ab52b4c201a1146425c64b349da2ed54462f0234f2d8482be59fa9448e82d9294d444518f0397dadf3a34a3261deb93deb6645f758e709b6333cbdfbf318c45120fdce8802132fbcfa65dda8390f09e93ed4c49927c950a1e0cb16be16657c1cbed9e0ed74cc144126d5f81189556edd95d4a21476dd9da9f6ee944b0f548d5ce1d6cfb25e31821738ee8e08344e7d3b3eb7780d28e39685665d144666b586a915733789fb305b2895c870cc8633004a08fd319a663800e0d4e177608f35f989e246395b3cdacba121883f1d81102a82b0657ffea965f2c0aa80a9354d6a2f162b05d31217d6ecd6796e06d123eebca7a1858672f126c2931c16331edf12f6b49c12d17c1dea1d884ff2ade1d48991735f82f85898021b8f157c26c0f03820e5cc06362030404bccf752d189b1192b21eb8ed730377e01a18eae0a129812126febcd8c60de9ac4c2d03d6d8185e332583701aaa7182415d091d53201670e42ef011ad3872bb9c329a2bb296f0925ccf771b0e2273c5d11ce79e9752354b949a17e53bdde5f2b21e8a4709c0b7cc8f4dbe39e6a6a815a7bf8d04b7142c1866f10fa7deed2b9aeb24f5280060adde25a481016cc395ece64a0b7ccd342dbf1d83ee7425e4058146d0fdbbe7449abc745b7eb3e4b3084d3f9ed4573e574a5745a04fbfaa9268ad3d7ae34988b96ab9ce81e2d27a1262f4089391f99a320ba669068facc48d120da6498d25459c742e745afa6eb6ced413f47c84b4c045ca98e523f5295d4b933d40a463970f03f7e8cd3c60df57c55d17c2cec4c1d65de4e224d68478945a77f42e2ac3ee16877ca3cce6061a1ddc4cc8a033a43e2f0546990d9ff1c1106b618fdf8b717f5d2b5f1b0073b3643bfa2e63e4857e59ce9b096f976594b898eb3b677c14d6572942dcf7d56849870f95666af433a23798f29d2612b17d730ee86006ad0c70f41b7ec5443a1449c444233322a13dfbc6396012438f036ada636f9b3df36bb66a05bf9904f0baa2483e1677ba07ca6e405f618d45baed874a512fad775199596329140b82814fb31595622bee5841667362727217c2e243414fbe5e5166b524558131d973b52257551dbd9b496c7db00bdceee61063874289fb052bec43450f629d2657d707352a0c1b1c932682d7151035caf50c4bb0ce679b411fbe46393cf5b495351f176f1a71e2c3034e8981acaac8eff9e9d1882dcc7db16c490b584d1984c90c8ba30b3f959fcbc774366382993185caf5f39ffb0ecd00daf98564a6f3108ebbc8ee90d63dcc40d40783245d3053157f6dc63ade921c5a74ba03e40d034a47a2735c4016d6a8033927a77fd91b8f09160a88508166d7b40a61891e23a042050f0b2bfa7959312c91d5218f9137770b1582d6266df1869b3b7427d01d198b89e07df400497cf8ce815dbfd55b72d3bf9ebaef5b229becbd9bc8bda54c49ca030704073307b90b7b897c91714ea8ef57c80fc86312c6f32ab957a3366509fc0ae67c610ef85d983376083c2028019ac1df01663094ff619ae987c327a45e03f8387cb060e8a03b08b691b9b2acdb12fd0b06b6c4ff70671077cb887ca9b0860206eb9bb662072224b9be0c7c7099df841e6b53d726f06bb71cf3f8bc835d4b916b1b993018ac63d74f8e852cd5efdbba5a58265c2f727d97428f20c4fc46bed4af1f8eef5588dcfa85dfccf3c9cc093dd6a2f452f78e7980efdf81becfb3c8f513fa4d8b75ba82b819fc6e5523ad164d905cdfa712f2458794c5aa7c1203926b4afb9429d5d379d648182ec4d109839879b37866a72f4fd7e09ddd392f82b5906e4de72ad7110d675999a122921123258402fa5cd0f3791dd7b13f7bf66823678bbb8389fc73b8f31de658f0ca3b66791ad8bd0f87965beccc4cd374a9b67cf779d4bac53b80d8def3efdca9bb7b34700bc78454c58276ab1a999906de0144f7e140e3e973349e6a349e6ab50ec9b548ae2d4d69da64c79634ac67b67bb6d76aa6b8330d3c8bb4c882e70a9eae7964be4478d6c888816792693395e0196bf1a645d9c573094e93399b4e66ce7cd228dcd6f3164e9693eb6f3b95d34e74b4d8434e8bdd5edbcdda4deb6c5bc76e5a9d4026ab552dab4d2d09236db0b653d57ef7dbb69bb59bd6d9b68edd3447ad740c1174b145175b8073d6b0dfb483fe485df3a1fe8ce2f677f34610647f85a80a59a728803cd9f86af0dcf511ea614a24d71f6d4898ede57788b8c92e64594304106b7b889016eb8d18505e865ece78b9f292f492e5a569e44d8ac2cdc44c51144551140a7fda54e34f2edd6a23edea23fdea9a09a4456e76671eefad6d02615e0dfd3895ccd8ca8ff386c4328fe4f0557eb42af8f361ab6cf8b3e5ff2e9b97c7f871dea4fc38b190f1a21759215d047f6a7c7d0aa44f2e346dd284d0fbb589fb176eb409f42a6f1562b1b468834296a79485be88b2e4a028fed408559e3ecb93e8b3d0a6fbf46dbc404dded347409b509e3e0d6d0a3dfd1bda3463e5e9e7d026d2d3d7a14d349efe0eada34dff9482445f3f1c2888522a4a3dcbbb608af21209b3fc0a26fd0cbcf2213ce35170e83d8cf21de630f81fe6fedeef60cf6fdbab60ed43acf23570f81f5cc362ec82698f2ed56f954c06aa99a2462d5f3fa6434cc4e55530111efc34532959a761cfc1da77f0f635b0ca7b70e72ff6fc87ef73f87b1073df61d062d0a360ef43180563fb2b78c693f0cabb609f7909ff289aa91adf82ff3f58e547b8e569e0d163fc79164ce34998e56b6077c1eea44b15d443d708c31087a3883e084baca345fa2d988716e98ff00e2dd2a7c182736891feca0c7c438bf4439806148c8098185aa40fe21b2fd86041243dcb07abdc2ed1a79f7602f397d77c28885290d540208d826c8f56895ec6579894af3f919821984948d367b260129924c8f5e787822805590d04d228c86e22bc027aed412f03af801ee4b05cdf7a1290bfec7b8d84017d7d47224f14d526fb9504b9aa1c0bb722fb0e727dcf833c0f02bd8d816790b9f35daab608fa4843d14e9cc0aa52f00e20b2fdfa298047fd6a00695f0da0f74c219e07fdfc70b0d6d30977cc6c3daa5642c2d8af2f413ff66a07983ddfc1d73e0864ed8364ee19ee2f40613b9100cd3be62c7754d2a4f54a521ca60d1ff49488265966213211493eaf7d9efa58655296353e5ab9b35e0c2c33c808099c509d20d71741ae1286c70e30777e071099fb11f4f7bb6b412f6500812ae8fb6bbfb7f72365d7e2825efb18786610963905074173e7bd06f4db670308047ada898010b967f683e88cf569e8281ab693ed7b8100b94f33c8f57b562b01e469a60c2091803e9baa7e0b49f90c670a147e56e1c3d17d1e37bb328f3deb5945224f53ce9a49935aa282c5b422d7a7b5ce219f9756f1a01dcff6a047516ead5354a7833d1dfc75b0d7c15de63ab8d3c1b683b70ed662b58369eefcd0b14f67ee402fdcbc15a21d482459be9406c872dbb68d0052f6e7c3d1a17828289d94a9e6ecd1e2c75ed6bb215967eea40301018db4081aa254a4b8a3cfc6de620b0973ede9d65c92915cbf87b09a89aa6a1328c4fb5a6bcf66b32b12a17c3cc96296353e94e44eb694937a38265c8a42a92ecce605ab3fb616b9829e4a275daa3f9fd81e17c8a4e9d24cc47999e80ab1436e11d1ec48ae3162bc0ef9f2f90f9e34b024335b9de4fa9a4de692793395bc44f15fd4b3fc09678a8bfbf97a86db4913eeac3d3c5f9f162161fcebf7122408225513063fe2ae285a2cc8d559443e36520ad0fea37db6cf67eba06ca10f316c614caf7c7e5bf93c7dd077dd07e3d4e751330e2e19f4910f902715be3e0f3a24cce76b1d69e4943c4ad99747299b34f2a53e08cf940ae1fc802cd507400aab57e34c4591ab8441f911f49eefa80bae87843bf30cfe3ceea37ce738f409c729048443ffc1e34cf1a8799ca95038532d92320ade014406bd6b713fbffd3853342f9f993f5866d7a1e4064b48220c19fb0ed8be135ec0891cec6fe18a7480b58009689aa0646cb822c31c9db71d3c67bc94c2d6031788b08716ef09ee6823cf39e78c2ccd9ffebd62438b0f72a586cffe9f3dd4028ad80a9004a982083632a30c7568717e8739720e39da244dda118e200213b29c5a6f51e8a4ed3c8fd25e71fd7da43ddd93a195ee4210a84075dd89a2286b8c0092e7dbd041479fb656cd8f26b6a604799436ddaac6082379943795fa040000a6470ab1885198ba3c3d0c3f15e1bd8fceeb3aaff33e6cc7769ed779dce73dd779dd73f45ff07087398c377777af2f9d6216d8752280b427f556d2cd4f80cc5e8b93a3b9eedfa1c275f01310fe0290a7eefddf4fa06fef4ebd002e1e777fea2e04f7dcec3c58039eb78010329eef30053c6327a4de7b1e0ff6bc0f21643c2111cf6f9e9f5a2502fe07864574efc3f3baeebdce47f7e0fbf83cef03fc3cfeb9e772be6d0d4827a9b8397610803c7d4ffffdf479fa220b36e858001759a24fe9d319ba898536cdf7bed364f805ccac693f7a2f01cf44be0f53e07aef9ef75c0cbe27d47efe46e47ee7e6c85bf879f01316f1bde7f3f8f8a4e77d7cff791fe07d1f1ff0df4f3794526a6fa345f99a27e924c9e7ecee04482bbce89ace1ebc44a764326589be0e3350651e3453a924d3af39686e43bed09653877ca13c38e181f093fcc107fa109030b04c17da431b12c633fdaf92854e4a4775a6340e3421f1a039d4a29673e70b2d5ef9a23deda6276a52bbb93a786072c79bb54fa97cd0fe0708481876982e4818fb9a48d3aea66978c6c87698305c91e7e7c47a6a43c8f3474984ac6d73aac8e0d6a7dacfd5105ca716b1bd0f6dd3b4e7ecfbd8ecd679cbbded74fc393ba5f5523b95f343dbddfde5d7dedd7313827685816c0a456e8a14d9de9f7bedb9cdb76eabdb7b5b751d6d9b64cdabd770fbeec3c1c36df3cd6de46813d5c35cb5d84a24172829cef9a760142c25578439b3de95732665920b7310896438de2be773610ef6436a91fb1a762c6c9b16fda96c868e6fe8a4e7c89727d755bd366d38f4d5d548980f8984f180efe9f0087a42cafa64a62ce9d15611fcd9f32f66e6b83087cbb9a2173d27cb3cc74fd4e5e288705d6d82f92914ce09f3e9036db5388bb448599e4359394f664eba499eaf82824b59b4086d110925e953287c922ff11b9f4a92b4e8dfb93f19a9903cbf3d47be4c1fb476b5e9087db5c9f3817b6f93a44d281324cce7f90f8f9ef00b3f057023f7eee270e7cded09f6d791cf9547e7d173be903033cf7730e689cabc849301fa832291274ae6272a852ccd9f62cef2ac82e650970aaebba86c7eedd1a6d15d5946970859e599c97d4c73ae361dd15c89c75a9c5e459e9ee4499fe67b4e9bb67f92882527274fd90e73f224114ba83d25f3dbc6c75eb991398f4c2078be44d9c764ab1e92a44dd2c96faf719cf63bc0ac85d539ee67e6421ffcb9700299b3e944e2cdab6f3e7da25aa49c02b8d79cd35cf3ea79e118830ef2c5bfb9ef6601d316e70c2353c7f239dc5916e6ece76c3a993913861738cffb98e6f0f61fce816f6851aa90b7cdeb7efefce99953040a477b45cf85e3cb09c3fd8eec8108bbc812f757ee20732f03731fc30099fb140270df2b3fc5f8094e2e06f6f0b7c87d0ae62a97c32b49be70e128442bf432bb06426f012164429f2314869ec35a18c28ec70aa0b27f8c7358c3282f3f064819143cce16e4f99bd56490f1a7f8a5ac295007000c4a679e3479d3e44f940572e06ec8a384c99a18b27c1b5206f48056bc9ecffbb89f0b7a1e9cef09455c5c26927e5f484339e99cd46b71865a1c6ff6df6ebaf0a28b2ebce8a28b2ebc78c213b678e5fa5d7b64f7205f2a8e99e9cba6b23df2635a8654cef68c17c8f274a5489a402f7f07794279617c71a191e2317cb2769e84f9f8cbef3e3c2d9ef983bb255fea7fd833a5d4dd41f01d047d89df4c58cba64dfdfc38abc8dc8703f8143693b4c9e66947bb4e426b2161b8dea34f28f7c38dc256520f08e441c9913092a6dae4f9fe781feeec5802367b5e067236f51441c248e03a161206bf12c673c1effed37dc0e762703dded8b22fb8f0781110227b9e8855d1adb15b63b768cafc667098cb6cbcf879e91d9e4fc4aac0c11452cc3a514ab772b73cbbc7037e3878de6df0783c9e3d3c7b00b10eaab907d67df73fa18af6200db4390ce6de851fd80dc799845cbfdd434d821eb6fcd48397eabb5216cbeec334c7f0dc1db6a229d7b744acbbcab7895dd26119baf7a75307f20507d20528c0a76df250f0c441220359fe3c48f3d4010ea40b497a8908890cfa480d2e1de7bae384f9963373726eb754527063577498c3e44bfd199ee7d48574a9fe9022b91a7125a40bf53dd855f2c57b094e90cc4496ea7bca87f8a23d1f8e9abfb06572c23cd6269a6b8cf6f8272227dd6ad29c121aa7823f36930c86a00f07f05d4697481bc0903e68631eb67a74234f478b1602b3882172fdfa75aae44bfd98f660cfddd6b63d2306021c1883b93fbfaae409e5ab95309f9f29a8dc6978ca74a523b5fec0bd4aafcdbedbbf4bdcb1e4caf64bae2396a341e9863b52ba22cf522ccf9ad24c9ea30a57e4a9422c4f2aae689d46385e620d41d2e4199b71c40cd78c579edff292305f6e81e5efc79624dc8fa4d88cd40cd58c559e2945e4f9dc4bc270797e0a4ac2a0c07c7e4c99c9738b128e2cd99f2f4b4b902792aae4846c4baa15507984e3a7124e29d62692aa3404692205a9a3128e9f546024559b54624d0a224d2a4390a22de1681fd32b449454f97bfb28b8468e97ac0acc4fa4192fc5da54830ba9320458ae1b187df0687e38b67cc31a33f9627f45033704bb6f2b2b8f251c92ca85991848d17e0927975c2d70916d0b39d956554752f969e5ea224a31b0ac15a8aa72bd619d912f355cb1a1fef7328f3fd456aeff7d8f0b621e3b661036134119c1296cfe40335606f41d08f77002fc464dba34c3ce1be17c328c9ab468b358694129a74d384dda04befdd2acf4fd7cfb2a5950eb1d691a70355d1279f4a8c8f63bcf73779ffe9d7bb8dacd2ea41cf6beb63af6b927a4e9aab8d64b69ba41648fda1a7d08db9230dcf398b9fbabeab77f83a846952cba1694c8f6c7102c5b169864fb2349c5028e7cb125952cb2fd894b38f2c57e7b610b33985cee79cc91f542daea274ad9da4e5843255fec8f239c6c4b38d97e01525cb768abecbf49a90179ea843f74c94da366c49c2f65e74922f71d2f84802cf93bca51201877bc71439fac68e6a445af39dd71f679d8418716fd453f75e10dcd85379474421a5af428dc3a9b692b0acebcfc79b257279574f38059855c33704709cb34f8a9c1262e0d120694ebdf90c34f2ca8ee78e3460c7dbad2faa95ffbeb1a90d1de869ffcb591b6e51b369c838616eb2c896eda169d0129a84cbf9bb3bb9b36716f00c9f4eb17f25491c82b648d499818992ac91449a694522aca022625a53366c670cc8d16db33c3151a13dee81bc5155d17d1eedf6a8d698ddeb4486fb448e9e7823b6f8cb386312dd2bfd1226d19d94d28eacd8a0b6e7d4b6fccd0c1b5b11afbc4a6ec8ab25aceedaf610acaeba2d6efeeeeeedaed92a5d6ae9b477aabfb64086202372412b9bc5adc2ef59e066ad41a721c2ae4da55cf5abdd32bfd62f3a89673479a679d52f318e1fdf07e783fbc1fde0fef87f7c3fbe1fdf07e783fbc1fe04bd2f882cd8e1946a7baba2e75471b1bcd1d6db8ec493ddb9c5c4d8b352d72b0914bc2d5b4a871af16b56e6eb6d32571c71112b7ded7dee5e50273b1f19394d11ec452e6835d5ad503f10a11f0efd3ef34528e97b477418184e9522061b8073129a709a03d7541650d0c49b30a853bbab046d22c6b2db1d0ac6271471796cbcf01f46fe8c2f292f66368766deefc512c34fbe4512c8a3b8e5823d6a8357a62e482656dc4ca1a67f31ad9b47deee525ed391796bfc5e090ebe2f2534bcc85e5d2b2b92eb096d811b7f3c01d555679c4c468c8871bcc428ae012f0882f0b8b41997ce96fae25e6e127932f5aea8e9c0c4cc917ed8330802fb2f603c87101f6c152a673d1dea5489bfabdc585459f703d8f2ef7754717960b6bc2d043b4b236cadec24cc28cb21d6529dfdddd2790fd66fb12ff2066f73e19bcabbdf7c9e0b5a8d99769ef682bbcac07aefc5165355a6591b51f695491b5a761f344d67e6461226bcfb29230dbabacc026ee0a2e3fcdd79ec7cc2e2c09735b8a90b5ef3a5e104631daa2a11988e3c2fa3a0fdc11c4c95a45c99796a13fc4449a48da8770248c35919c2045edc750933c8a2991b50fa9248c7f68c6246b0fc29130df8fa07dd2aca52039e148878995c243c346c2f86b3f5aa9ac326d693e7af1eeb43796aec8da8fa358d6441464edc7d04c64c99751658563943d2cc5e48bf633985c54fe7e6c8965cddf5a97e7c3d1923dc4c95a87b5e7b0c6c9b2f6dccb4ffeda1ec7cc5b28ca17ed7178b62111f0c15024d2dca5488bdac7b4edc07569b9b05ad4feb6eefcd185f5820d8d994ecaf8ffc0fbc1f43c2b244e9b3a779336d17799a5d316c126aebf56843c552b24135971244c977dc9a81d91ddfda5ac5cc4e44934562b72a845779cb9e26d37b835cc41438b1ec51d2b0e6cde10a7498bfec13813dcb17a0deb0c67acf22b4e9d856ef01522447ea2c14b6e330b0cee58712a4e9b9ab449dbdee7a6bdaf6c2f33b542da5457da10ad880c3c6a68436d9229a930a66cce9e5886db9c586e9bd4b46fd165f345c39c2e6d704729e3a16532dab425dfbb36a148515ad36d91caaf4da21651a46985245fe8af882e52a44fe9f44f0bbf453abb767f1096569f2ffd696def9794c8cccb6c2f3e0ba4189b1aeb7e90baee47ad5da26fbbb81e9a654a4999be683a49ea6274a022363bb298b981f186b6bdd3771e383dc2a437602aedae6a377032d3bc582ca6830eb1584c87582cc60d318bf1936f5608b693736fdce8dc1c9f1889e2838efb3a4ab85e6b273b76d32ae7497587a4d89edc51e2cc740f1ca40026337fa6be0709d34b7e45c3914171c73923eb61866a37b846886425dc1ee6f7cc94b1df8388037ce7a5cccc945991e18a03fc26f47e9615f42d5fa67ca19acbdddd3dd47949ba778c6f8daadc53b24b81942e6305d5ece4d1dd1d0cfb1dd3eed4f6a605e1cef75a434a4ff673c2b796525e17dcf9deb7823bdf4271e753771eac6aadb55677afdae649cef3649b64a59d5aabd72bea63036fa5b5d65e51296a5ab51f90dd90704354a300dd42295e5112a6bef760f65d5dcdc6409712a3d65a434a81a8fd5c0a3a104a8a8a772710cda8b47f26e1cedae944934e20e68c09c44a051609b753595dd60c9b884e92bafb3891bd16ec783ceffdd411751da5b2012d6343adfbcc3ab9fbb1d41de4a17859ab00953394563a1d85a8a81ed42e34bf1e548e098a9bb8e3d3f9543a45c10724c3516484bcee0437a5b3d33aad1677524abf8be1f547679d32bc09a8cc8f4ec04fb54865cdaf48bf7be8ff7d32782766e44e9127bb0ed71a25f156909815d7dab8ae28cb9a202e87e2c6c8b2264809dc06413ad0515c4f963541504e8325bce0aaf8604ae186b2ac596206dec412447a043725cb9a25906062091f34f7cb99b12b230b27eead028c0ba28113b92845da07f70a69275c9b658d12345307f7cbb246890da0545e9440c5854b713b59d6a87054b210cc4a30e7ab4649eea7362f584d8bfd72af74455333cc4c693ab4d3f19e1d9ad234995dd2997d33b48ce35a9e919adcffbd3cae1966831e88faf483785094508f149a92e23d531cd57259eae5c51d698a0ca7d6d3d2ab6675ab5dfd9abd60363199b7506d8342a150a88ec9706639b97f6bb55aad560b8542a1502895cebba0506896d3a8994ebd5eafd7ebf54aa552a954ca65ad5eb55aad568bd52dd7ab2b756fb5bce52d6f79eb138a2142794dd8b49931d94cbd5eafd72b954aa5522997cd721ad5339df2d7ebf57abd52a9542a957299cb62c800e59455cd158b07fa7abd5ea9f94aa552a9d4844d9b1993517fbd5eafd72b954aa552a9192b343cfc59e1c899bd715f22588be2bf4874ef54b166abe7ebf57af54b1a23efdab037562b1e44d1e2dc99e851d746a3b33aa374e674d6e2755e5c30b3c06eafbce4a72a6eb7bb91fb6dfc546daa4d157d15b387a3f8e1f872b579c2a764e9b1f65e91487edd13eefcb669d397e577b623bba900267492348656b9692ea7f21227a44fd239559baa11ae0837c44f154897fa698468b98675463b92a59fea8c28f7ad40faf4b19971e115f2b1a9342df913eb71678f72f52c3b30dd27a81a3d38c8fd1e9884a9332572bfd7a44f9f954b186f13fdc0685e405c5f8fd693dc1f96e7e463d34e31ac898b3547f9b3f253dfb096b49ab872bf723ff9d0b06d60eccb6f0943e753594e9f3375ebd36f8691caf3d9e03c82b0b9e58bf6da942f1b063ab3db6367ad4f5d3da8dbd7d91312c6bf7e6c72addf4afa04ae60b95670e5c4d5a4952bc8ca355475521461936736981317cbb5812b3f6d375dea5fc26ad272927b7bd223779dc9dd5ab8c5ea4cfb055ad29e1e5a81b4b0d7de1b435a11892a7ca0b8542948000108480a57080923fcf844f14241a24912178444184040197c9230e287272472cdaefd00906f06319089233c18cc785ad081eea5fa5e46c4a0151303d7d5c18fce9199ce0a7ba4c5198103db14389d2d3a20ab49420a3e5a1950c5cc606609246260f02a321312d160c64bb21a31624a3b5f7e33d029864c51e466800646d8c45011eb92bca210e0d42c6b40b0c58f66e61140106b1bdc962c6b40208513f7b3ac01811260dc5114905c1523f4e07eaa0072b71ccc255c192e68215c30cb9a0f0c89c1a559d67c6085800fa86a6878f024509635469420d32c6b687290c7b731826ea5062da84c7f82ab16291872dec8fcd43d7d10076c02ce4027601337c4449ab81f3284c3814c297744a6b41fbc429e40952cc5244c4aa62408224f200c66821cbbd172ee08c2626012f0d5227d70e525fa1c068b7430683168047481475aa43504592dd25781425aa4609216a9bb833620ac45fa200c8481b04cb5154d03e9833735997e99fe9db9347d9a4cd8804bda246b903092a92ad31f412739994e0e1c91c78bcaf402c9740471327dad9a469026d3559b4610060369daf47476912914f2080e510077c4a46fc1587f378305c9fd817b65ee955b765a70678c6011327d59c381551e350d5b501d1494beb02ca3592bcd636934575dfbcb39e71452552d82b9736f7695f5dfd6f51f6bac43b9bfde743d525db09964ce9c14940be924695491a55ef2a5672d3271edfce5ae769db2d2ba792aabc57e3077d4bbaf5b3b9079741e39d7846105b97f56ce4531f7029bb82e0983929b7ed7245deaef0a6bb17b065dc9a1bd165a60cad4b0be10329c1451c834dc3277847b71aed9f53c722e1559f5412749630152d93ef89d7b5deaf6fb0ca3dc79f7eebbd16b95ef7d4c733305955740e5cebd0ecb4c3b5f18d772ac164b455ab4cf6a955425212dda9752cad2ab45cb795f1877fbc2b85a09a7c4449e463fae3093ed0a4364fb297191ed8fb6b050b8a330b20d5b3690ed7b985cedb58fb964c9bebd313f955825562c5b708a6cbf059584e9bcbd2a0d8986af65d92fb1da54c47e4b966bad25ae8683e5fac9b55443a150280402815754963ca9c0bc1e215d5e5f1c179bb8a8c4cd1476410c95a21900000000b314000028140c8844a391581c8f035d991f14800d7ea23a745a9d8ac31cca611443c8106308000000006000464646a2008e230e90ce6020521e1d441c93c903feaad4344104290cef06c190a646f319881fd14c2dc2e9caa4b93db06fae30edab91a898486329efdec880476fe1125c0087021f9dc874e9e6f8ab7ae148bcdd9c8c6b18dc06c4b898744767b21ab7def8dc6e6bf3618889e137e17dcf78fd55e8179e05ab6fdb249a4a5cfa3eadb4e130697f00e3c4473339f8fc7f9312a3da34539a40a112c4375360dff72369a28b69db7e4bf814423b5479409f066dfb9752506029770c63270d95336882337a18bff87cca26838d4a841a4fadb7cf76b1f748aaf220e060ceea770735d64cc904850d1481f4f93765786a993125ca9d5b1776a6a31f4a1f31a6370ee79a50269377a6a5d8fbc89d2e08a3e5f238accca0d4cf074741c58a433f05082c90f2f2e50b32b9b511799f650549acc4f710de5c247c904d6ef30e6f47575b20476ba5240ff82c1445a83ba451a47931a94978a757107bb3a56addcdf61d7e0182010dd010d2b3f4f463850cd24a5c4a66284e3843d17808109cac53a9efb6333528f499fde171936494727be68cb41539f76b5b1b70108daa7b8d9d93a3b9c4a04a12393b4ededdd73a898aaf5b264067a8710a36f33e33806a5f385fc4464c97d02440b60ce6091dd05bbaed214cc364665351e4ceeda7efa411e5aeab06661826c1fa1c205e428518580b60527d451ec8427692f330eaaf03fce2099824d559caadb8f3e03895f27a30441f869b970880dc6ef3afa37a7fce7417a1132f866602b06327079b91f84fd9a7bdd6e26f382acbe7ca588917a986047d7ae81494d358e39c4ab84eabbbd27f8cfd516dc7aeba0322c409d1192dd59d54b1d2a70ea22ca2fbfc40eb853d38c91d12604a66d5ab0e527efdf9462ab44a1f74c9fa3f4ebee12ee8be985e4dc1beaec038c93a8098e8855d64b76d194a81189d4ab90a4ecf97ec1584b9765c677b4f940cdcd79416801f509680f1ad0112c050331567f6d49128b7c1295f8bca227946a0916b9453d0f14071a323fca47b936deb7db13418b902e804f2aadc9c63b4a02ca3107d738daab30586d21e7238d929c8f1cf2db205d2d4d30ffd60658262223c35046679887f49a0ed565b708a100c1936d75ef27940e097981fe5a51a037b8cbb2c4a89713f0685f583ac79fa824d70a00eb604e448cf8cffdda095c4c65f237648162809504cc43212068617975a88fc4a449be0f44898acce7f9580fa472fb7152b7b3318005965c87c4098009c2c57f08dfd02dd6ed7aec128a425f040b00a0615f5f9328989017de7675b99e07977fc12c4240971f90af7c54ae228cb0f0f7eb516442ad8fed156d5fda77164b5e7cfb55d1058602f4e111f365a72d7abc78b19c1489f1992e8bd2d87aa739cc5b0a573f9aaa3849fdaf465ee455e7c6247c1270dff67c57295958f4ec227b822de5ffa531c8a8d336cd93f6e745a23274bc6e00f20823fd192e334c4930da59ca17d3ab3b93a772d59c1a36a700f568854d83378c3d4b24336ac446eb3fe43a7575ae5a8e5e53b8abf3181548245d5240949c9559f36afd0c8c0b6e9e706e23fd99833453b6222a98b1d40d3a79a02d52830d804d11963cc148f20b46419506ff4b80c963c2d2fcb490734b2eb0b5a8d632245d02d11a033855593b4c2112c3302b5b53ef9d610ebd5b4d1792f096cb3e29c4ec5b9d447a0d225b793e88e6f54533843dff004cc0a2e7a0f280bffd0e4fd1688c419e632c64a87f636bddef489aac92439d3abf3a72d60c5d384817ea119341441f6ab11915af458a693a8a5cd0ff08302e786f7700283de518bee06c819faadf450d93839f2b3bbd45202263414d795757195afe0d82d66497f2f3ae801d90922722fa17534b6789424a31278e89e6a566c44cc3ebf8a7a1b70b9117606449f416b1dbcc6a1231a347ec8667b7630352160f5657aec34028a22b54eb20d3aed0efa515c24003b6f47509d123c42423f6a28cff1c3672ec83429fdc6b00bf6355558f1bc48c482456f69f22a29c88e26b9f3f11aa722b4283b5c06f605b076ff80b937f6c7190c460a6c4403101f552eac0e310f251b1e49bb0b70f1492093c739e0bc7163112440422f70eed93aa429e7b32b0b334c244cf3dc791d3df4c170bcbc029144b9222f38db9a1edaa9508713de03b4ad9a72701125722656a290a57260cbc19a4caf3e7321d8fa078061ff5cbc32ddae6cbbf69a01f224d6daf02e504ed9c5622572a547a39791a72142805e68ad2af350c2a8288681af8e7e5e74276ba6f344db09b99dd0eb8b046f938d3ddf9da64b3770cdabeaa8d4c3c6434047eefe5323711ca184a14e29e26a82a8957d6dfe0292fe0c0cb65a9d815e1f729c1aee13a6e7d019641073b3a62531f923cd31d456fe33fd8a8ae7a1829222a624006af15e837873abc8fb4186f85de4d48533ae0fa508012814448d0ec819fcc734d6240d03739a43df2082e81c22226a462f007c89092127c0c4163084e654330d4a500830042a0bf827d1edf30c3a95e32d0d8ee9e46a84a143472f509cbcd8028a41ee44951c8a61a40e82a5f746eb43c529388f71b869a4fa55825228814adb09b87d82388a87b06944ea1abdc91e75c441bc9041ffa6c43f6898401a595a64f662b67960f8e12b29a606ecd695875f19124eb691aff3cd7a1a98925116813185a5341944e1054af1302607731fbff6dab30c4a491a5b377557d09ce368d2c04d3a2451eb4c55158630f42d241b11e7b06112905b3a98c6067da399630fbcc7f779019dc5d027f111456e53c17660ece88e8d48a8cd7e42f4839141638f88812ad00a94099467ed7bb5683898886b8b1dd06f68abfd2ee7e4ae6b7204a08ab116703c7792737d6805050081c5f64ac125624d63c1695eb98a9c0c524dad006349830998bc3592151c852555528b26b53e02cc5fc48248c875dc9687317f9347104521d02100fc24c524fbdfe889354ef01aa51f4aa544a5289546f143ed7433c05da5e885715ba953ac10f18693a71877993c26ce9d7000ce4e99e87c8dbed825d694286269c62194c373db7e88460af1555d6071cbb9a90813f76131a622097ac9800c070b6f0808424ec0c5bc8864477ce162c4c8d5389d9597f8739542d00e7ab30777813c96bbb3f093bc4dc3f1745d1b81a421c7a5c55d407872f8ca9d4f8d902e6c2ddc25269973e38dd40fb5d3cc829b8da41fa5511225aa9cdbcbed1983e9c4b6bfd9dfcb2086178ab038a5432877a8c9211942a112f99d41b85b066cc39f7cf9344b4fe83c6c0d8fbd7d000b9b34fc78c27c4e4674dee4ef4d2bd20460a9707dc7f92963880024b1022f1fe965ee6bc7b816156a45b769e1bcfa182f3e80c66db3b92463da73eeb1252e882da7813f715d19a9eeb6a8cc4eafb0a38ab40313c3f62330275f8e4e1f945229982045769f417a12a5661bc4814c00d655f88a5aa5fa9c5778fbf7ce0e863b98f53500bb925238af8be885f809e71d7c0c1d9872d02d3c7072394dedc4adac939f957e8327f2b9531ef230c1c261e43f45926390c8fe5818321ee30bfc1827006744eafcd46f929f6e6385709a4956934737030036e51988884c0ffe1e0d0a8a502bc109d01444c0967da791c5649aa9baaec639c2994d0e0dbda74ea8c64264415057458b0cc9c5a8b51ee5c134172f17a1fe56b24e1895f12122c0bf6661c864f7c0d84d985a6c280f38b7efb6d3b87acb4e513449ed4434344c67b236d184480bdd122481a2c81915389d6314282eb2a59a0e24d0b11d52a05e1ac45eb5d83b551467a51774137a430a46e4bfd4266832f9961218e02a2d120a11b040928126e82826bfe69eaa02b61fef94270e8524aa66a39738e5108c92c327ae188250fe1f1cad5a49c60b0ba61d465c100cf44eacc187a740ce7ed9481753b1a1a653e6cc6de1bdcb5502e48db947479b7dcc85c17f02bbbca4c69b59bf4e3b9341e0f0752880951c1827cd66195e9fb72c978eb5f2e24b05eb9c6efb313551f23d5b4d382c396e33514e023973380fe00ddfa0c91510038a1e0321e0c3efbe9a59b72099d0890397b27f1dce72416586e1171f697b6a8b9aaaa0f0c3cfe0a7acd95b75feb4a81b74fde2fef17b0acd7c92a5d7ac6fd4626bd70d01677cee8c2258e408dd038225ea477c163d2dbf5abbd9264b7e0a51b1570de4e376d2b26c2167cf2321494b9b4da83a65b9facf032283fefe8085df5a19f686ef8af4ac04d08d667515f10cee084bd7a9ae9fa44a581318f18de98bf123eebecef3935bac1bf57eb3b3407ebae625b392f798bfbe361511f671c735c1355b0b690e65744e98d7455954f7c976bc2a998b25c426b4c7f6751bff1fadd42545452e60d5af31d961e6ec9195c429cf7557cceaec3baf028ccf36778d97f06ae447649bfa3087631e4ead15ae67b4e322619135caa87b84fe37014852952f98818c2e0779091dea0dc8646dc249fdc36adc66931b05ed02254743e8015c7972f30fbf2ed4ab972561b67938f8b2d8a170497169d3baf8fa18bbae56b105522c207a7720f0dd38bf3f05e6e8bde71b40bbb66a0e704a7e81925dec0b2f3f84a34e1110b3cdd0199c63788e229315651bb419ce7b47b95612cb3b2e3b4507873aefeeb4e880a16111c12e69b4d0b45aeb415fdba13c30bfc937550ac00ba59ded28d8ddbda3e66dc60667706ef7af59c3987d9d28ce174d7bf694baa8a85306c69f908694d6a17ccb25ba56065a46f161658dbacaa43cc40b2888cb840b1c1a9a4c8177b6ac57ba6e1a58dd589828e9e99a344ca7037586abb98e4e3259fe612d8ed4beae3846c589fee0414b6461b7d332f193dc8860bf9962ec2d7f7431ab7a031d6dd649602a516f5da061c71ea15a89e23aa1b8465b10d575863a8ab22cd23564e07c5c41b846a3e1bc898272e134528fa491053fe1d538bc7c1c250f79c45a11e30a42b8b6ad88754d438e88b59451ddf227c62454ca9addf1fb27c6e7054351729423626572267d49285d2f6b115ce38839aad3b35255be4b197c99fe30908bdab84815ec980fa8cabd786fe9c00f9dcc5dbcacb49195aad6fecdeaa2ea16bfda62f20e6a0a9aea463adee5f3ac49d874c3d9effdfe5c44b56f0f3bfb1586730f99ec661baeb7d8d330bca494e396ad5bb982357d0ffd2e38bae872d13844230dad4057eecbe9dead92a6538ef391ad8359a3d457852521e8f71bf1b8a8f1e311ae5b75a871db94f3c9731e08d18a37323e8208e0cc5558e0c31cb11256a021666f728daf56e30a4e6b8404ff6656806f39a1f0cfb175d031d61f9945490e3e6305b2a649a551789e2602251e11935bb6f2079ce1afbc8bd36dc58982d4a6a403bd5c6fe37f89ce19a220c98e83c0795c6a2fa1abb386250935c0c9e9f7059e274f14dc88426ed0ceee526fd2c001cf225b341b5470e4add53c10b64710dd36154293034693d582a26a5bef609ab43c73249126e98253e9d68d3c0259946dca76d06722582a8950bb2b45daddacf44862482c05ce45050eed91172038488aa9089a3a0d7f72c3e8e02ecab4ad6a50ec9a48a8269ffa90e187f2cfd600e8baf272d8a49a27b211f8ca31168901aedf9af40f7defc54a5f4aeab6e9508071e3624050d016ad6528c1e36d38ab2bd3dc4a0c81d9bb16411c62625fb78bc25d0398c7aa9bd83582fd2846adea736077a31844d283c25ba703d94c95d9ad08a8b5873540dbd1597aae301c846cc4f48087e3d6923a60e8f2a68e129a3dd4e23318fc58104f260176b1c970d8d62a4932d8de76c7dbf09f146ef382a8572e0df2d586869a29d39ef8538a0d529b4b6856c59e80f64c0f8d6ffe981e950927313fa2f799052d8ee7c8485f4683d00e8dac7cc93a0b020ac1cf3cfef3b04ace0ae434bbaa5023d8c2718dc7b54c25060325dc2664d07b9f4ac23e06d234a4e99db4be8b420dc1cadf969a7b9910befd36e957a1be84fc30858500fd9d933584a82a0f91a2284b78001cb8c0f7364fe1775766029ed08b6e5c49389b5663e79a96479f3b2ccb53f3242850202e3ff0bbd7b7e02dbf672167bbbf62ffbc3f9b6acdc75abd7084167cb2ee581fc9553b5cadd91e0ffed827e9ad0b8755d1caa0e11b7f1d069b6c9a96af168cd7d2051c03e4df00444f96151b1eae20450d3e9cae26add55672ce0ba12e6d2714787d951cab24422210148daa88e491bb6abb1bc04d728404e26c790f24a9e137193c975d4328f81abe3718a2f17da21b108195c3e3906c28cba8e4bb3674639074746f0bcd7d65342eaa0db1bce5c6ed6e24a75b4ef4971b7d857e02f30b6cef97468b86887489fc21290a5f4a998435a6ade4c7a19c77d058c3d4cbbb9d0edcff524f783617291acd6d516fb5def11f5c62d62508d3614dbcd3d7fe4eba3b78041b0142198a1c1231f67139477a23feae85468a60255ee1d618dcc5c6999094b5d44c3521d86bd9ac5070e09ee24be58c7ade5fa667902382dc19dc401fdbcb2b05f3aade384838f5b962226af4c4524a886cf6b4ba04475e5de13685ca35cf8287f98145d5acc4ff290ab64d7295fabef27843a91e2868abe8c1d02d9053f14c42442319fcc7395a082b8b5f58b950b1483b2e221e1a39956fe1d87757ae5bdd65cd4ca5963b36770e78ce001f2f00842767f562d158d00fc7e240a649c4e134c389d609884c97a7b74493cdb272ac9d6c2abf464d479ab8caa1f487d27f686d1de0d55de6751b0ec59cf47df6e07ab0d8fb6bc958360b80e885e523c6a4a4c11993b8456c67e41e2180f6e7cb3bda9bfe1aa2d3baed49edd1e00c146ec8e43eb266cd0148f65d01fa833d8c40b5dc3ee751e722aea563fa9a7b852708b4a0e6be0ecdef6d82dc71dfee344a19b892d2cda8038d3861b29259ed6d7aa03b3c05902c3f10de60439c2142c80615fc5593b0134a06a0d3cdcdf2fed9b2885579aa12760ce58f24243c1825062600685a262e48b5c19156ec82378cd2eba9c8bca64397b5fac263ae24ca60cddf5c7b4521c353ac0d69efc9835b4b47296fb592a0b94449ac6c3f495a560a1b830c4b9fad6690b856d859e613b22a708359d58b5703ea2a2cf58997e86bdf703ef434c3ea71ad111a41600b339f8a4751f1a16e7d5085e80a448dddeb3b62fbcd9d00bf1671c7a408c7a3aadbb1bf169b54bc0f6008b088c92ce58f346992c00827d9436fae1f61906f2c8e54cb076a1ce1766f5382a8127a621a6acb888972d316ecc3dde466f64fe93fe1aa9fafe35eab6d8ecf47720124627f37788e31e858b3f173224e720a6daee8f98a70bac8ba30f734bc7b1a444af0ab5f513ee14395535cb5215338e8999d68068cfac4fbd55496048c8eeffdfe19d687eddf53aa1ebab027971ee86b5c018ac5058449d90178c0a05722497321b2cc23d5bd238fb5dd0187813218ebf5cc5671d5f90f6318a61996f456246741a9f9096995c562835bd24a8da832bce38b7807ca0fbcce10cc481114714b28a228152587dc77011abf1289f4513e92428a4c74ef8c33b0ffd86f42016f9ec21eeb56c0ce6d6682c16ab57422fb92e9f08e5b687160d95002b790983c59ce506b9109505c7addb8e66c6f98f8b3cd3021bb12a6a33a03236d18dee2294c79497ee576dc1d1809b3c0229590309c33c59c2157511744a015918e9bac307fcd09f66e298fdca410f6987a3813fed3ee3b0e12886e86f647e06530a737a4dd40ef3014cd80ed6024a55bd54ebf4682ace04d87c3bbc1c86495744431bfe85da491b40f7aa63f6923e5c398f085a2661d533b520549639c35a9744428972e57b822c3c7b05c734b7dbf078d6971121a91b4fbfabf5d99ebcc7ba455526b61874c4a58e0a750dbe57a05366d0a0dd0980c9727bb475b020bb39224e66f58560b7fb2b79bfce3afcd9a358c93e31f01dbbc8844c8a0c32ae97a879ecef5b8d62b3bff266b2d2fe2c44384e0edbc1fc57a3aded5d1de12ed7363157af34b332b531e9c22a9bedc3ebc52f870af45e999f588e207db08f3bb12fdabb210fc6162658649845d8b7173dab6b41c4d050514c8443d1a239284ca53c8be57a1f23e6ae66c20a2c262a0fa38e5edece3c8b4d1918920b40f39d02615752c8051a378437c12be4257018fc9c1a6edc63fc6cc73f00ebdc416f8c2b9d4eb552412b658923286f37c422153fc121de577d20789225b6a1689ef2b677d6284213ae1d0ecff991552a68c484e6c9ba7cb80193f8ce8ae6b9c7dd5cc8195287a311e2931c17f535ff247cdf3a012ff9919003148454af80186e5aa791917053d363c83f83db6d73ed23047daff3b01d6532c38c26919aa7df85c40bc6bf03c0e9c769e6ba9c4c5d4196e2697181653861a204f076b5e2a465b52c41d131c8d5d48183bc61dd0ade2d51d272f8536146d1a381b5ca665622a4be173793d3d716162489b507bd5a200bbb8182e2937bf1517d4d3c5e8056db3e40585e71d19a6ee0d5b5d40649c0f0ac107559d5f0a75ffff2fa410738e86a8f24118f0a81cd73e95f84989854bd8c1bbf090ad77946885423352ce235c6254ef03a6865d6587f45c6ca746da201a7a5394fdc35c13bedc33bad1c1adc20af3b2dc49f24d848ca47599f332ee470fc87ca261f3c065a669b77c9db2a6147ece1ea2c7fd45d6eecea5a238711bf907b1d4c59ad480fda2080b91f85950a88b6c8cc3fd78e4a4aadf1e8047c40f4c60e12ee71ef040299e7c1238c185a23cb4d33d36dbf769da539225bff3406d52f63c28efec221f2a24f68950aa046f88b27795c52f68a5b7617d4c922f9ba467be435caad5dac023742d8090003e5b34dc13949d31f1224e95978a412757cd8c62d2acb78181cdad58a69a8395f18a30d80337d259bbbd3cbca6b35233360b7af55acbba99afbb1e422a8ab277d78f99cd1238804506b27811eab3f54dabc40d120bc934278c76ea5dbb9f857ea473c897413d8d06031c1792c10f80965898198d42019b8ba9363c1709e6e46c2c971f99bf37f890381c2dda2a2a3084bc1548dfdb10f19434896fb4687e7823eb78e73a7b0dbd064bd86ed6001f7fc4b592026460dc54ec27a262d581668e1e6bc4f61ea0d633fa7d5b18388645ef179078fcf5fc626058295c7389754d0330cde02af825d604420f5ac8ca70cc9ee37d129f72cac7a6de9928e281aefd075a3b05fb24d34d4daaae49d49b5b0907f4c989f1f9f35d547e7fb80108bd272c84e1cc4ba05a4f36bd5c8e761457cfc9919df30f07fc33e3e97a67080f17c3ed7791f72ad96dc27dbbab85d8645794e5cbcedaf66fe57045d2c11ae768939f1db9c93f6c762a69e9fa445a86da198a1504bd555bf9e929b88b33079dd4d1f78189eca1cf9895f84d4384af44b088ae6b30bc1229227918846ea86f6faf06aa55d5c1c0bf4972f9152b256f039c64046a9802f4fa6791453cade72c5f04fb960ccff958955c9a2b212b41027462998efb9cd58c1db31aa2b35c801d89a871f43a5ca64a182b168a19870586f650c97d947e62024d9692df0b0d08278e87b6a2e56608f7e5f241b27ef0ff7f29b3e620fe8b7432abfef3ab7d39e90eacfb8340a483da27a772daff7e802cf7eb8e836b49b7cef93fa4ad19d7d6a80cd2a955d5059926c4b6ca7c9a8a40d0dfa3f3f6f96cfefd9445941ad75184aa15f0c83b441a916bbf10725d5ff8f7d7a1793787bea983c4bdd87f6975b0d29453e71c581a3fbe367f47cd3ef1dadea01117b5cffd0409abf01bdb50b320eaab51e812367fa7ad4c9d36eb3d081c613f115cdaf9161b63b6ad745b23365db5d7db4f351ee948991ab88fe7b5be007f014d3887d24f26360cd2efb526adc5f93e39f2b38430e71badf8d03ba4bbf455e780300fe59b48b300124ddcda6af5607baa5c41d305c5d10a8311ed83790a21d77a04422fa4506408c9c39dd5df6cd20fbeed885b995c05e37d720c013a0c045bdab2367d18749d876575218a56bd4f25a0344f367a9ab3a2f6bfce068486be507da59448be9ad1d96c84656062f0c10865ab5c8963d67d485251142448003bdb1ad945b0522b4a94325087e26e2ac2fc9b040b5fdf088938d7cb7a10e8cb218a66a1a60a2ceb0229f26147e97fd5260ddbc14a408665f19f9204a1e2cf1391726ecd19a9d201e623a371dad33a8a8cf4708b4965a6858dd115c37d9742caf80df68dab0a696024967a0f87e6f0450f54cdaa43327453b0404ac6ec0f37e35fb9ed195e3f56da36aab131b1c5208325816f8a80056f33b4be158030f897b2d58915f152fd651eac9683cd8f009724df13237ef13b8e82691bb7adfd30107cbc610a9413e59f985020cd6e5da27768d17a9d2e1b43a9a8fae41cfee410541a6d05caa5c2974e246978a6d686019906c1827cb404bc5605ef1c6b5cf3a231424d1c0154e1f51eed506608dc4e2d889b912a06abd97c07e0c1de5185b26b389e18af081d137dc2387f933e52c23ed933e51dcbd0cc0e4ebe0ab1b47a051f5838fab25d122077f2697f77991b0d60eaa0cd6cd3ae0b04d9989ee89a1bb1d98b6c803224fc21a01e9e582ebc509fccd1299b131938623311965f3daaa159b188bc1537497b1468bd9ce74eddc7c94d286473781a43deca05ec5c7b0e7453aec7d114d2e01746870a3616c56d3968b676b5ebb01aac684e30c7815d726aa0e5cbbafc3de7e40ee6d27c6493e0bf5b7936ab606cb6869fc307b74aa950eb5664cfbad2f66f7fcbc79844b85f360ef66a10bd21526cdef17e2d172cf4d1d1afb3e984b95b63db7e9b44f52f8822a15ee3a693d4e5ef304855f04e1731bf11bd1a30b7c106e11af65524e8b246c9a0e7cc680b97b8fd2618ea63e218bc597d114c041d244c1d753ed8f35204f5668a35f7b48da16ceb5423ec72138c6d421a1cd2837d62a08a0950017198111e7b5d29d1d4cca4f325b719453c9e0d545c5d7eb1f87a0ce303b883922722bb75125f714570b0377bd11c2db27df21182cd04151fbf7597b9f373c17b2b745fe3b0013d1c73c2ebc139ff6d8f1c09a8798e1c57687395454fffa46cbc3509e5d874b7b2cae3dbe5f7426510c60c1cb09ecf1fcb438baa535d0c29521d80d2265ece2bbc61b1bba0d46a95ce6a16bd0ecc17626d7416f531ecbae816311257b18c83877da3a774d29fca11eaad8733b9925145b0fcf44f489d2c4d7e18347972884fe1ad6bdca69205490dc9474d77fb287a4feafd1c5562e3679c2a8f8df537557188a13b55ef2739558aaf66a4921206a355bb5a7a484c1cccce531426db314bfd0f6f76df1328d46af63af546cca640554d0cd888b2c686e15333df5b9b34ff69f0bd8377ab64deb26e39ba62b4ccba07b29c295adb4ff1c09f534886f994541ea0564b48aa4abdf142ea5cf2301f231cc55bcab621bb3454bd9bb54f7fadb5049ba678de3a17f5dd7bf575c6cd2f1b83bba569f289d678a20fbd3963b7bf9ec7f8840cffcedc136c9272075027d0aeffcdce4ad52707680e31d594aedd1dd8f215075879289ba2a5c5b11a07062a65531ac05f9598f3248bc81b9214380cb2c1a9fde6f8e1454dd73e06c2fb4fdc6f6b9c3db26192bb794d3bc9446f26ae01702ce8fd518b2bce03c0fa2ace70a659cd12f8c76e7eeb8bf13c14cb39cad95e0053b06dd06e6aaf44185a307294f5f29e6de5dd3dfcd6f8020a4ec637f1f5661102bb9d58925e01e3b819b6fcff59ec242c35b28d777f21643a2be1d0f6a7e9b6a3253fd2d12d86c4b1c8c672d981629bf3f994aaaa9e8162014ad1fa486efcc599c199a4c32385f37cd008abaf06e08548141f84fd34d9fc746b36da67e5fe7cfb6748eccd60413fe72cffcd313252cafbe9e152f5e06fe15edff3150a09ebfd41f7d6ed53091a5b8daed35a477b4b9fdaf9f8e33810849e9840a7356a434c8714b3e5e9eaaa5c862e16b8a3020a01dcb005531e6545042e55347fa7c6ba89e650c4540057c76b44b52ceaa5e9b8f9c01110ac5143a967936e43bc31dbea1992147d47bc14ead730422778c327667408d88dfe716270b297a65fa0d20289d851657bf20f0e950293e26e63592d65040ed5d25c4a6419c1c1e047c192e470d0f5374dc7a466c53ce2108e35cc2d4a76dfa0f58934be86e6ac1cadac65428d7e00820bec1580f747955cd4128a461f63c101636a5fb90ffcd2e84b7fa8b6db2ed847ab07f38b69c79042db78a9fb748a1f468c421f3767dc6dc2e24761eb54c465d6f61b03153584adda01a4410b4271d195ba95ca308490747a8af3f0b65a140ee4c680b55313c4da4614142c3972774e3e9a2ee7a7454346283c99a9bdc462830f089d24a06fb0ec2bb682673b790503ad55c1f8567af4644a90ab8c7c01c2331886dc8e855f447846dc2a42dc0af90b4ce32e34dc58b46548738143196c9ea1989bd41eb871a6a3f6f0acf5c20e5429d4a21d78148e4ed797b4087b5ae5aa95cf984a441849db6a202c5c44b4d867d4a34ae786a04cceb87eedf57fecf008ae9b7e2a6cf0711a21dcb7dceca51f825b41d357d53e77a9e82cf00b1f8f09adc5a7a1fcda6ab059a36e5016f2f2c0838f7d5feaeafa0070e1370edc97dacf6d37388e5db6352d6a674c6f986c2a2776c921ef7f0cf0e361c467c99343491d2987289ae3aac6592d357ba3960863b2b8e4214dc83d4a90708a5b522e4bf62c32ea51f58c0779151059281a736c223c82bf14034c96b06b186cbb7c0774d74aab4e701fe5ed3738a7566013ad07f2ee2b71a80c7ce611629120b52534137817c816f8c862e11dcbf2a636732b972db591eed4d48e65f9cb80627cdbce9a74996db5c0d1fca86cb514f04392a2d06f239a131fcb7289e894e0f63e9f2a35935f66356711a4af9a62b7cd6dae2341e2906454c944ea6c94eadd16c1b38e8c0b81278661163896908792b4b8e4732a7c375c67c92769c2704f9c3106f4a31b16f3a49045057bf4f9c6d7cbc313c0449f13df05d0ffc33a62d7544df4b085859570cdc488803bd6cbe4ab705025fb15a3cfabd1d3317c18ecd7bb30819b4949bd498a688ce29fa0c554ece8772f1f0fcc62fa4e50df6b698f141b64ddfe42f3c700a50a7689ded93111f299456ddf00c0e09f8df1a038c9172821b9c8acc3bad85e609e1e1049205592fc4f349e3440925a005be4ae1383d870ab09dc360973908d895f1c17ea394088a2c8631ac73493a93d891792f6bf8dec7396ff8a2acfac5090249d1641208212f2790a4d7637d9f410d0246c2cfa798181a32d145883ba888737e70dd12000c2eec936ddc4d2162742e2f3fe0ed32bab9a4a2201c631f39c116845d366344496db8f3486ddfb61d5264e9cb15142460b52db613ba538d0c642eef8f6677438dcf6d20f5d72eb7d203ad406160fa5c1eb899a9a02c135f7c58caf8a13516dab27e3217c15a334acea9672e986fd04c1170283e03f9c4b0ecd4e574cafc02b1514b2cd979988b71e9e7d19e11f226ce9ca5d0f982feb57d980948f37feb05be71f800d87a6fc69d950a18de7caef42455ad4bd0d8de0117bf5b1d0a8db06b942324a845236b478577b47d9ddd5a6c11e7e43f1ab825d9287d36e0de1a99d8f1f5a5346067a942e9b961fcee3dded8277ac71a80dfa58216f80cfd5d04a50cf878c6cbafc1af8f954a1d304c6d5d06ecda07de0bb7c8b58d8d1a65f162a043eaa0a61ff595a225420fa04dc328375151ab45d54eae6f7ababb56723e4e272c2eae3332c4cfbe86e11ee02adce34aa903a418aa2e23c78366f283ee6c85579efb9e170c6fcbcdb81fe8fb86cfaa6900cd2145fbb77523e8f32d47a23809c701ac1eb32354c98ed48e53b2c80109cda3a35a9f0a6d249d84ff9e8a6d2aac6a77c57f2538e529da9971e895da8b6fb268065ff3b3e0fc92cafc2b886f7d9ed0f05179910fcb603aa0274df2cce2b492c1f4a1c0aa41d44ffb74c100197a9af77073f0d59cbd3d4f0805f65c5749302273a7940036d50b023debcc316f8d2d800df7402763610fc1381b618c4c8de65d8583d4ea9ea590e9d0153d44b4404a3f0ae96c14389d0d48cc428bc921a0223763e858537baabfade6a1b38c5e351043aef2cb7f9f751423b6e224d77ffd78bdd20c2b458a4f5c849fe59e4515cb530ac96b461f327ca247d5deee7987f9077d9bf9bcc0ecdafb15e7c073506e13c721e69c4b3207f4545e67e608a867acc31bc2b9c938a69bde9c9c158a343c5e30222f8def07a8d33a4e0be742c5486d315f702f2347ec200af763949c6089f87b4baaa9ea83332e44d7dc6b599625d3a652fa62d4d180e64652948be4205f4a281ff487936a8246a11699b2ae68e6ed1d253f85933240f8a7053a5d4051bbe2138d68d0eabb7338a2e0abfde78c64d75e67470825bb27551c655d87d722c342b332de28f00a37094c78b17bb1a8f0fdc7f78d9b5e7d582419a242245a974e26153cff53fde3424ff6e1056343abcde689f3b53b34536a525a2da5a76477fd9b919cfe330e561c6d92d66e66dac5231c959921d460f96dab3ffdf0a5145af066124976f5628a91fb049c7bfdadb85eb5b6422f5f146470127f1991c56b10391ff69f29edc5a861110bae08f635205ce835dbe1ce711ee01332dafe9e63f9f493b93cc910412b7b611365a00000788ce2ec126ec4d9bb7232fc00252ef25f0b9a3686fb22171c00c38f2aa7c907abc8a9a77794fa0d756499923a25ab38f90148401369f4935a8b17d96578b310db4294674d12f02facaeb053a3dcbf4eed7d0478bff6ad9dff0fe545cbbfe8e88d622a1f9ddab81f76e39bcc449d58f3aa6951e49a98b289319eb19131de88dac9b3cd1ed9ce744129e071a32b2642cce090226a3d9650fd4b815511687f7511b3156d9ca20d118f534248aa64eb7c645032685f1339deef5d7fe7463247b576caf4e4d8f6021849545b643308ba10262cc78e5b393fada3ab3fd0e5a4986f79b362c28b9e8374150ef87ff05c3a6932da5660f06670bd79aa219880a181b02b29628a59d1037d5d206036f3573efe682407ff66dac20f7e1b4c9f2981b1ded0564ca58f78bdcd8b1ea42c189b281e98dcb35517829bd034493c2d06ffa59c38ea5d7364f04bc398d31ae6ff21c3da31d47ab643168b814209e6e9f94c9c14cc5136d1c621777e4c21acf51c78c205e80d34f53d22f63790c8ce9b952cf9ae617a01502bd31a275fcfcd35dc65692b0fcc67b450b6190e5f7bf05d7551c5a2ecef26e32e73a401a2f8673cb33084d379f450f554ec58ed5415fd699ab73425095234684d46e0646280089194a896e5c3a70923c231cca511f3b58316c98f490674af4029eb82f07f57cbc85e889a363b8794e3941c172a4e1e37b494ece6c6854364fc89ab96cf38d6481ef0cea14dcf115800e48dbb755ce3af2a128f87c1b7cb9435191ee6b8809604b88b026747c362f1552e6b0f070f3184e2e7ae34326f07ca72826b2ac22a719da5058c88692b8522069c5c51e3a4e9fe54c0b0c3c18edd6b7f39b204936dd9358bf36e5d74bffd27bdcb487732695cedcc340a2edb57c17c9af639b217e9857afc2e0abf7d49efd1665c4c5f0ca7b24d730d521e81f490edd71d8731d100b4bb1b8b7b3ba25703cfd1e31fb7992fc9e792cebe7292b9731273812ab35746f65b9080fe46426b744d648700568a5df7cfb056ef323e042f3cbf60b9718cf7f1895ecac3bcf902eeadce5674c1ae72c991a1874af63d7a781a2b11c9f452b1eb2868c8bae02a54a66fc439ab74888adf1e1c6fbdbed2498b84530cd020be646c0f5c61807f1305ce3d3c143340148cb992062720f14a6882e9cb9823ca44b673818069dbf2d318cab1860c71c853a374b723fce54413cb4fcb6ed07a292550c5eba930631453bbe87c499cbd2ef9419e75a92b1b71b31eb1ba742ca7a03934e187feb064b40657b839aa5a24a38e34d82c9012a9578a5c91d4fcb3821d2582ad63e00b2cd7fa5c1ae00971984d7e5db2491df72532fd7686950010fa219ee429b9451ad89d14524751873585697753d552d3673476305584ca4e3f9124b42db1fdc561926d24b21f8b42f06be15d338e73bcde73743ee7dae52e5e8342bd710dfcfc4112937c4abb9868330836b20a8118eb10542a7c67e8f1cbe7cdf9ead8619d110bb12a1482bc82afc28bd5622c3cc7b2b9a8a0410d7b0b7d6cd9870b9e0e834e69d4d68871c1d0bd203cef68d60402f9d74db945889da956a8e774cab9fc077bb93a2f3a35f01d4694c862145f401bb6fe3d67da2531cb1b92ae7009a69873c13d8bb48b4394539ab35976f0e75c00adc2e926174640c8de713760de911442f55ec88019152e55fefd49ef26a3acb099906c945a7a6dadd85e95be029a886c8c20138f663bb1eccb458022e9866e87bc1e20b0cc4a420e8fda6f45049a77ca5be0794c635d89c191da40ca1bbab6cfb1e2e81a8dbb235def862f5bdb86d2da249f9f687668fdf7066a6c59b4f2279cbebb58579e199324f92cb5bef3eb69b33008b6d00d776431a924551451e34bbb3d5a61c64937d7b9342d2cf612f2ac39b207e3728a655a05eec22880d9f7c135ed214cc7813d763435764e0d53549f9a831122088b64fc240c94988e61911dd6c84866fd94bde36e0ab6b4811ef5b432a7e28e9ab018ae9b51564ba0451403ce1a5bccccd9d27c84ae3f539f2577a9b5d376a2ca1a5528d9a76fba0edfc2b52462161c825788ffe4a5cb0e9993acfeae5abb43319be9ac2cc04ae0d2b409512503b1011e8c59eefb775937ba3176e09dc48441160e49a0f125a303cccbf237f0b3a6715a72cad3e207d9092ea15256146fc8bf05203b1c2ec27a39731a549069c4570eee188cebc60e9ac62caa52ab1040698d6e96ae1595c3ae6c8262d7540ad7cbe180a3e71cbf655a0c6dc924bf6ecf70dea7bb9c1f03a6de2ba53a34f0cd1bfbbfba19fa5985cd09db1d5eddddfcc8900919f419086944e99485cbd97223c4553c1159c560509002e273b7506c6dccc8369a313e5cdbf97917d317acb913915db2c5707406d44c9e8f0dee39e07bc544a9524d2e853bc8daf51272b59de194079a92d9bac510626c5d6090e42478c32c11f84c26cfaa74ad2db88649ff76c4f7dcb3debc544442694a59c54247c3c231aa1254a5a213ea50b32349de4c79810e84e86cecdccccb215f2962363a4f056cdb5cc42cbbded5fb24468c6b5fddedb89eb7fb489855976283420a16b5d48a6d5999d56e593970b8c44a7b1e10348967116297a1d1f13e5a42f13056d4a1c769cf882262ae872f8beab98f9f3d90ba5e8ab46fa4af5aa32f4838bf96041df86e873efe4c3a1976ca2574cea67a38c7f7224c079018ff2c68494587abf71a64909fc52824fe630874b699a9db4590431cc70a3e6537a3a9853059e46cc48712998b65f56a01b28b58c944668403952bf1d0183792820f96e08745a95d0455544514e74e17742ae0afabe767d690d3f878f55f436857dbd24f62d85b14ffaec047d779e427de71e1f5109a17fa9c26a4d17bcbc6ad08f786e9c46673293032feccd282ed1c4df4004e57983d495e8c870426185c368483810b4483901852089f3f49c6efba0de5716c99c6b52bbac87c449f100c0a6ad99fcca34b5c517c67f8be356cc92698b53b0f21729c4c0f56e9eef7c762315b83a940b6ffa0404729b4d5eea3c1632b4e44b945b6682ee733d60a676879770384905d409efad35958ad5ff4471d1b0a8707d57975b3a405ad6d3ffcfc96f6264b018d9857413de0594ea87f4ebd66daf22365184fe0912fd81f7073638c143a9fcb4797c8eb55cdd471ddda561725135bbde313415d93f692501427b178755eba6dda8580c9838f51b61c95a92d08f28275f4b5777890af53ad99330c43094f7348695391be43c32bf5a931231a5f59ccee8aa9c3a71758bda41996f119e968d2188b077ede61a8426ca0ba182ddee62d1c1e46317550598e626e2d3b6dea31bb9a4ed744101ec2b876ed12190b126762d8097668e9d0fda490d00a0120b417966d1a4f33bfc33a87ebf7b47ac1509614aa8221101ff50b41db0c5d27b9f5360fee143f97b0317f9912083512e919b5f186581a2a49c2ab06cb40b45d9f3f5c15a18b8545be7b6722aa2b3d06a2fd0d3264389f86e178dbe99af929b8a2bd7e9bb130220e5dc557439b42435b0cf3c7adb635581a154db92050ceb981239d5669da3cb211de265a5ae41fac769f7fb337328e17b6bff0b2fb7b69131c97fce76ab745b3da7581464800c8b696b036865fb90325c77acca8f1424bbf58f3c444ba9e5095d222be25e634b5ad81025fd061d9d5280308f2b112cf68fd160fa8dc1fd25d619967ec8c608902703ceb52c72266f4072c4715389409e63ce9652dcffd0acb625c290edf817e2debd0c9280d9b28297501656b2a149a5485c17320a85c077b9e1102c0730bb4587b0599b252ea9851ce5e22dcdb83e33241062110c8db5e5aa902b4a883f56ef316d67fee4d43e17562ba806843b18a4db8d00f1b6576b3e42988e5c6946b8e237b433a7a1676b9882dec4682459bba5e651283bbf5126e0d3b7cab5f3660efbe9e9e04e7f875f54849c4386976e51e481370ffdd1c65489ae1f91caac187b728ae638e29d792b807825d618ae06dc3115c05e479a1c56242186597635f634e20afc3a52965bba2eb1ef713c4a710ce29eedbd1112ba14ff3a5761673a6ec5ac3830126dfe3330a0e55b41171f38fdc7f7b676eb3446b77ad6cd5bd318c184ca40eba1c912a3a5a0ba7dc3053c54773f3d12bbe771f955ff039211fa089621c928e97d4255dffd8d41b067f72cc8131e1e29510cff514c5a804904cff5d38fc97054726ebb3d4db9cbf786a4af3508a1293348cd88dc07e60b8ca4a6b84bf797e749d765271535b0314a9fcb24c675afebfe0b780a7d21c289e780614cc5b27a6ee2cbfc458dd2656a89710f239bebff8640a803c5ed8d1d4cd593cbc42560d307762f29fa8b73e8b9d9399cf225dd89b8b12002c8ca74b91f854893b5895310a64db4a329c42ee7dd5a8511d270f53bd6cf609def4417a458854c4ef4be94015f1d3a6833fae48486d2199cffa30dac850700cdaa8562094c105205bb12682509e4032f86bbfb28517e4189f592f69b1159c8b7626236e18204de631b5f972003a26935ea96d5c1846ae26ac7be0ffc1e1ae5da913fc5ed21c087dd1ec4627ea060a014f37e945dd14758007e7c80574c2709cfa6a680b05eeb483737fb9bd22e205da26d981b0315204eab4ac6d778065ab6b6b51c9db9ae18edfce56ac9a49e75f87dd68b6aa5662142df3fb40b5a0f694ec09063fe33f0bad444f839af9b8bf40c64d77fc8eed11f3fdb1dc4d5a62bdd4a739da9da60267485baee1f7955721d3f22e04c75c6da9fd5057150cb3c302c382d0d5c3225c723fd06f1c6a0520fe65af175cc105b7d3a04a926c001e4c514d7a645c08e17ba81271cd11293501470870cdac8b99c61248fb4bab51a3aec6bf146a8ad9446b4bf8133afb55ec4c94636cf003abbbe068c0b48d7f978be878574d980db794d81b63c4b9b8f51aca96aa083c681fc3310e88556d0c9cd92707df9919a952ad664703f519b355065c289b567efa774443674da995ec9227525ce341f8eb2ab450c0d573298372ce82ffdade06650db26bb6c19850f2c4a3d92eef09a24b2a2a35225082890613582b2efb6508e69018ffec0451468d88e0ec62833369477f6046bb7ff7d961f746264a7896559412165122fea73ba2a004adba7da7a15ee622c0062fee00a2cd3964146ecd9a936a5f4c1a7d0d417958ff3c1e718e8d89fbd585a725121315dfa451fc6c47ad322851684c38282d1e3411c01acc95aa6373c8e9625480049952cecaea5614d1c9fb5ee125d1801f39f1c56b0336b0ce33d124811eff14b4b8446b58729bcab57b4c04bc5d87b7b5d4ddd150886f498f88348ec235c91c6f0a54860cee2fbdcb7c9563bae00ebe4c817560423ba57ed5a9885ef349a4870f98cbf609788f55d342c6db085422d9d6e1ad0a3e40834e1d9d1b108d20108eadfa93367d0c656510fb6c45b063d43622de85acefac3871f08cb1b78d230c307689bc0f8de5141082312ae82492f59d1dd346085b3cead51c62aadf4f8fe6173be0e997f4e7558b5ae6ed17bff30eed57362c4452916bda7075251ca29f682abd8d6ea471063776ca24817abb741f4a5f08b3e1aaaa851a1dcbd52cae896c3d772fd7d92815ce28b54d9c424e0a4ac731db9fed2f9cb46301cdc84bdb624827b8ac59142428f3807e9c1d3460c2a58bc6114f1cd066c451068386e2c1eaac181877e28dbc167a761a1c622f1e41a7d99df94651c518e19c53eb47960dbf292eafcba9914701b3f93e2b0a2493ff81a2091c75b2bf04f7273c94b701287495f71f1c4f00265345309fb9dc4b05023da5ca5828760c44b6e30e850f0ccbf0db252d68a950258acb18c01605ed79cb43ab05214a484c4d39de770c7fbf3f1fff186ffbf9bfe7f2ef39c32e539a3833c1258aa9186686fd00050a829b2c18cc2518e2db6b6ea8808054f00aa83d6ab882d614c8e38f1c4087c6e89881b7935ff49e45aa63bae9759eb2c2e64dccfb6f272627e025f21d10a2e014d4dcafe8657bcca45b0f9cb1145968c8b574f0a38cf1b8f5310c0a134ea499281d04ac849a8b02fa727e710439c84518b7b3294748c248717169cb5d30f81a02857e6a6c959f1deb18a72d8d5500d766cca7c2e19a149ead1a15a82e0066490df6209aad8418f09212d614e855c8565c545b61d10d3b5f47f8cc000432a7bc07208460619c7060407540c4fdcbf7f7d39770b1e07da21e64730e4a30cf91d98974f059990250dabf90bd86f39fc8813ebc9ca774c089a74ff8513aed3f790a347c0c6da6d5c5048d30aef05dcf9fa47ccc8ee88564c7ae8d3f9e96b6f26a4377f9794f2fdc922de9b9ea1af8f551a89351550dfee11cef17de002a1a574d76b49bba1f4940ae5063a552bf47935f9ca8086347045a783a63c6e6e8d2261faf6c22545e269de44ca522456692c4f9875c3de7cc607764871e141238ce5ed82c4b0c5419893e4ab99488c59a8a80ee644a994cd767d3c6499e0b7b58acc244cf59b5b1d7ee4e0730b35609e725a8902619731c0d14b03a75a899dedbd0a99eed306a40237b7398444abf976a568f3df8acb2bec31fe2ea52d90f5b2f23c45329f80e35c21a0340ce340b8c2a9faa798161753a826c6285083399d5aef2a097e1c20e7c80d6fd0f987eb734040f99995e00bcc94e234fdbceaf2e179eb37c144c51006374535a5de9ff15b6daf162b345242b972029d38de8dc5c4ea3f4ca50671db2cbad34337e9b4debe9836d1f37b3a617c336f17151270ec9f1a5d754c009f0f7a59041eeaa17352d8bc123316ac448c34157f0634a5b21a3a5ee8a4da4541cca2b0860fb14f630d792b26fe64c0375a5a7844b0ebc6685fe9e6c8026a9e67e990cbe980cddc73c0312edf4997b610c02f872c02fcb92e01dac7accc78a5242e09144db70fc8811d83eb9aa416d6ff63b323969c6c136c64a63576aeb557169d76478c72536393fabd7436811bcf8011b25bfc74924c2acd324625a4248319b8fafc10deea41e0d08c1c5ea3afd13c23816587c04995d3f12827637712f6db675ef613fc90c2fc9e48fa94841b3ddc00e83159c104b1fa7b587609fd92ada2ea8711bcd71037ac81caefd7bce0ea8300a0602023117c669e16c1af67196cb0d66037bdfeef1d67f077f4a0235d3d79f89f64e530fbc9d90bb49571df5fce58535819405bc91ad8147310c053fb92f1dbcec6e409afb14c2d0140f5e6ef0094f9ad5f9ae4a96fd6d4d103033706715c5d7791992ee78020a6f99d6bc0e483e8ea7b8b6822ee59e9b995c838de362b43fd3070e560266ec9e588eee1c58627647b1fd1c3031ab8700adb70cfcddf693c84ab22b35acb0071f80cd60455dd500b8d5226ec822ba3fe2a4ae08193eb250bfd91ca9b88a3832a8b73c653547ae5c09d0989ab23f57c00562f83ebd1055d7aec008733ad2dddc342d5ec4c345f6b45b9277852777b98e7a8088854f6d4c6e78012d9b77042b2884dd25c6cdb2e8ba36f3352ce9cdec6dbd307c18c7b3cad973f086a883e6b2d498b4d08cff18615d70cea070f42d56a33a15db2029348dcd558f1c790262bdd65e1536c07e07731a99c4ccb9af0655c2421964b7ba7963d6ef4305d4a2240161128d9ae9a1bee44284534f95bedfd16c6daeec4e14156b25a338a345a4026458379c46e8f1443ffb61ea579d3c3bc33e03a69218ba91eca20bcc0058f79f92569697ab89121ece12bd5cbf4cebd25dd019765773a73c5fdcca0a83c402c2a20833843efc9371b2ca11be868e1120a86f848816fa5ccaf1291a4afa5de8fe98ec79a07e090382eac1fce011c518def1bd2b84b719966105db38c36e5159174b0ad2b183f133df8de4174c764915ca409ede38f73a3329fc301cb60dc3dead68224bfa3136ee13cb8a700c9c9bb558f7f1fa92a19ec019a2d9d363f4d684954a4a1b862953b4220304cd0b81db9e5d4458ee6cbe914b9730b54164a0caa0c20d55c9f5b67a999b27b711ed60610fadd74bd0e2bd20fd32d5e30a0708cf3861e96eb3e2e415aec7eaaa7fe973066cd59f32da6769b2ad9e7905f9236d74bc8686aa37906a19385ec7087ffbac65ff70976ba2fc28b425c3b22c864b8a97b1c715fc92fb22cb6b24dde4654e750d373e05f71b4ad4071bd09f9f4eea704ba5c4cb8a8e907838b004bf44145248383041678794d129c70243cce5310f9df0e3dc8672b6f46e7b9c0669d9a6ef1f352d79c965a681fc18c55c44689d5e70879e065c9e19888b57404c7621847a016a0b1fe112b5386b1cd5ab807d1d129c5bb2d21b5e83ecff03f648dd00ed2063b47b53a29380c096d983c0c46e4ca8c2ef4799fdf3b2f451255712c35f255eaee43dbd2668e3ec860e05983490f881d33b001506656614946b18f5a2e21800e309b5ae3654bc521951209c64a7be81d8c38b97dfd9a96f7ab3b77a429a5ed873b0bf768ed9f9df8385ec0ccc8b3315e3057cc5659350fb3f991640c6f3b932299bcf553005c69d40fbd89080176414beca79521231e51c61221d4c9d0de5797e1fccdc34403cd308d4451716d8bb500ac626bd4e51723212cc53ca34832935d2a84c38fc808803127f29b298c10621160c25324b0f71dc01b4f40da0c390ef80ae8b86be10b9d3d9185b5d35cc39b0d6ffcf75d540490de466c25841a149a27c3eb351e3615cb87f6d145c62fbc9d4963282bedc73c6d99179b7c19299c558ac12f86749fffb38725500a35990492a138ee781763ff0294775af3ca3d6db4486110e6c03e85ee266475861b5505a215506a11176fa955d5bcbaac060e6451cfc0d97c6850ec3869ebf473b116410726670f95227ce8d199f421a4f8fca0e675dffbc3d570a804de4781042c49205877004134e81a8378b25108213128300fdb661d5918e2002566777708ca30780bd07412a1d67c14bb3d95687d183586e4ed9df979d0035d9530d21172b87933f4d95120319fb0feb19060d48009c79b2ff9ad6a091ce4a8672099ac445b0d704cd94ec9aa1c87b90b14b9233615641f450d25831647a9bcc63adb3bd9c2ea21b895ba59b4ce466b25097b4aff3616bd741aef7179b463610db8ce36fb13aa44e1a784ee845ca957c1aa22ff1432d9d26ea80e324a15416e1fe340fe46513938e70e4aa588db3366dfa99fba3fbe0160b1b1ab4d629dd284852434cc1ffd012f6686f359f996fe62c80b4ce903d359f6ec2e2350e3d068591fe3f1247c293497321a5158b98f24588c6ae35c9f59491c0f0e1f671d6ff5482f1d1fca1e1cfe8821827f5c06a67f64f1ae88c210b1bee1230bf6c8338b32431d3b5fe2a2677db0002281ebbdde8817323fa0a5e121ce6c0776f15b0e9fa52106b0a117aa8a3bb97ef5110ed63b612ba2b341700aaca8ccda5cf5825e12f4e33a2fd7405bf858eb36f9259e6eafcb5920a0e296fb5a453be8ad4ac32bd12c86a0478bf62926802b86bc6de03d3c2291abdebec840c6be41b211ab4cc33e520489a6056e1f74103d17dcaa077a321b5670e2f03f3e9423803c61b2cd4614c1f17da4100c3cbf047b573eda641f395e09ff3d3d76d880d628e95dde4d8f6a83059beed8205435ac8200700ab3e9389b841567bae15ad88c41ee5e3c815a3d49498d57a44a5d0d7ef8eceeb94ef4db5264ec5a6a86978331739a935b20b036cf35768f03ed32e216e90191eb409c92340eeaff77b4d2518dbe447621d5f4efdff99171be145a5c22968583868f6b69f96461eeef29dae47c62be7f6f4e7093eeffb8e4306831bbc35b4d05628cca458d041fad7d7499cb4de37632e10556d55f397342e07a7b1f3dfd06c323eca9d134be5cce55ddeb2530c2456eff3885de0d4161ca9422f0545a6247dbc55b01e59c44195fd1ecf6fce7ef120365dacd70a5b2b876594b9d14e028294d947c729614e469d8fc20c9ca74afe5332487e556a268898dfdd30260d2bcdbbcd9c8c3a92e7ddc58c26c0ff6fa676417035cd9b6a5d2d031a67bbe14ab599e44909b83b40a464b728fb7f93d2a1bf6bd26c887317cc77b27ff4ba5ed563c4fcf152a649a3b3261da9cfb464fd1507fff897d2acb5f097be5d10a66decd554e9436fcb43065cac339fe35a378cdc2e1a99b07642703da721280562058524d07c9096756d83c8a4239e266992a28648caf17263f8179061c09819eb659003201a9eab328f0a358e3664f94e6feffffd05836cb54fb8a3ad390bfb1892ed0e2564c7b4c70088a5ddfeeddd6f6de52a62453080831089607402e8edf1fd65cccc0f787422e16f1fde1908b18f8feb0c8c50b7cffccca450b7cff4c8f8b15f8fe19968b14f8fe191f1727f0fd332d1725f0fd332e1789f8fe99978b11f8fe19988b1070f101df3f33737188ef9fa1b9e880ef9f01725188ef9fa9cd08cd0ccd149d56a79ed3c9e7d43ab94e2f177bf8fe13ec143bcd4e27a053ed24741a3a15d1ac687a68687c685a6178e47b273a55f8fd342f1a184d8c664643a301a2a9d108d10cd114b9287e7fcdaaa6a78655e353d3aa71d5bc6a6035b19a590dad06c8451b762afbfd3535175df8fe1a211701f0fd35432eda7c7f4d918ba8efffb172f1c7f7ffe871b1e6fb7fb05ca4f9fe1f3e2e9ebeff47cbc599efffe172d1c7b7f778779977cfe1c5c2f8f7f822aa169f97d90f2fdf2364c2736328a67a794fbef7592d90f80dd8d015be4258180b67212d040a6ba150381416cdac667a6658333e33ad19d7cc6b0636139b99cdd08498a9cd08cd0ccd14ad7a583e2dd70b169bd1806a424345342b9a1e1a168d0f4dcb6d685e34309a18cd8c8646034453a311a219a229aa59d5f4d4b06a7c6a5a35ae9a570dac265633aba1d5d46a846a866a8a7eac7ef4fc60fdf0f9d1b2dd33c730fa1a24f00cfd3eec1121be0911ac543e3eb43d9d0a8990c6550344699446815c7ce1fbe61bc87790c7f986e103f0fd1bcbc518be65f8feade5e20cdfbfb95c4c7d8b42be69f8cef9aee1db86efdf6a2e0ae0fb37211707f04d80efdf8a5c1cf2fddccac5fbfd5c8f8baaef027cdff0fd5ccb459d6f1cbe73f89d988b3a7c3f3773d1002eeee0220f35170fe02202be7b7091888b09f8f6e1fb3b968b0af8fecec7c51fbebf6bb9b880efef5c2e02f1fdddcb45067c7f07733188efef622e36e0fbbb998b34343e342c9a1e9ad5a9e83474123ad54e4027da69768a9d60a7d7c9756a9d7c4eac53cf6935533433342334539b01729b19dacc6c263633f39a71cdb4667c6658333d33ab301c0a85c25a0814d2c259180bc357c8a30c21acc98761d8806d79db72846dd1806de1b12d46d896d1b664c0b614615b30605b2e605b2c605b2a605b28605d26605d24605d88b02e11b02e10b02eae075897d710d605e600eb1213c2bacc1a605d68415817200658971a10d6456801d665e807eb52a400bb63e583ddd19300bb8345c4eef0e9c1ee6821c0ee701dc0ee78f16077c076b03b6206b03b663ad81d3b76470e76070e76878edd7183dd51006b5259d3b5a6cf9a7caca945006b720dc09a5e02b026980dd614abc19a6639d644833509b126d19a52d6348335c9605f62b02f01b02f30d8171cfb12c4be00b12f37f6e505fbe2827d01807db1b12f35b7e947d9971ff6a5c6be14b94d3f8de571b23c662c0f1e21e99963f0de5405cfd06fa7181cc338c1b82f3ee019faf965c637fd3d2c95b134369aae184d588c138c3b86ae2946e8fac1d9a08e322e99260d93692203ab42f53bcab8a895b2c5f5728af917fb626b08c217b111c7c6c6c6684aedf1e3e53674b25179b61257a3fc5fe6c7ce7bbc4cd7d52aaf9296969f4609c1e35f42981f5ebec7f778f9222a6fc651fde2505a8d870faae3dcb63f5efe6259d5e00f24154c0d10df7035a8638b4fe8e3a2ccfbf8b1ebd1e2d3e273fb2bcb459930f4d1753dc2d0f512b26eff6ca3f21d431749b2abf8c7d8ce590563d96d66e85a9154210c09750c5dfe625965b2ace28c0003048ab8f4025c1ebbc8958b428505536c4c361a9080524a2901a0b821d4441b48e830a880a950e1248aed091544d890e0846ca5d14ac2cd7159d69a715d4c93cb8e63ef9e7bd975bfb1cbae3399b8706ee1f4e98151e5773e83fe4ca48d1b6d9c74d2a8e3b62fa4d7a47694d9398e63661e72d1dbb66fd9f10de98734736fe29cee2bc1d17b9649a3705ce2724b803a74970ba7128dea264036261274e83250ec324cbee4aaa60450092fabe0f169e41abb4837aeebf89f77dca687044144b62c32cd4646803c232c43d8ce3052baee58c06ebd2cc3c2558239e79c2c7cf320163a2c74328ce9c9f7da7a1cc49f411d5cfaebe83057ab11a35c823a7ce7ebd096aed2df5347de194e26b1ec04c5ee103c3e3569d4f63120c11ab5915e8d22411d49afbb35e9457f9bdbf3889955df7cf3cd49b12be813e419b54733173d2745d1295aeb54d7559f2f4408ca4455a321d451084d0aa174e8c382575550f99973c38d933fab3a17e7835ab0548144b50273ce90e39ffdcc1d27654d883929b770c6b2eb36d9b68da00e11a1390e0e0e8eb731e79c4fc453322653691222c2a269c62c84bbe6c66111a776dc0513a1d1da87bcbbfbb773ad2c5733f8ce8ea5dbbdd7a514b4e9e734c9a0763ca52b555840352122cd5dd7ddddc5702478858dce0d0b1dce95729374523adb0ad52fcb7e842085da97653f4f380d2030144aa612136a196e0201e0c8000a12444105294d4ca0dac0a86d5070826edbb66d67acbc4848e85318d4b0821320149c7850b001926ef25334bf1f2b6c2a4fb4c182328ef0a095c69f602ecb7e8e70b7cbb29f33eef8381d1c422e4b2356947c904145151c15452598d554b7bab9cf1377fc2db270b9c7f5976161c6757f93976457105df0b26c8b2ad0adbbe56f3fae1eb49f9629081546877b92e30b56823a1c16eaf8d7e9f53fa971fd519da2b92eca262b2452aebba05cdfa88cb94b3ca653d251a87fc3b4e9c063cea9a13975cf84f6657cf48881e1f162dae1c2ecded25dd29183a5aea8809f471a75dc46a7b76466fea1a2230eafb96817e78fdddddd75774b7627b44bcb16efd274968e4a272b470583aafc4999a9cf486ab9b36c9503c1f0b85001dd3d0aeaf07fde07d280446177da11e7f20375bcebd40c3d77d2080ca2ddcae4aa07ddd18dfe142f9430545568b2d8922d96a4a0d6cbb22d7ee846e956a48a234150ecc0c80ae268828adac0d110420773254780367c9ca8261ae8a0967a5c96d1e00597745946039f3bd67f524d2c973b1f624c01aae66f9506c40586cc5582db4f4d473e58182c7ce72c75fd758bdb5a20a96e4ce1daebdf5d1117bc208b214c110512de70a20661cc489e1975acdd18320d1c9881c5097290c6cfaa61347881b28adb3f3b35737b3445d77d4ae050c12c8b2856d0ca420915435988918496051090a8a688657104964f165180428a57166c94408582a22c8a5852c5f2711bee62d023854a8a21b7e1eeeb4ad6ea7759f633c50957f4b471c515fdbaa28b367245196e03329a00440613b6f0fa4b8489886b442869c1955f44a748578a5e8c47ae624a5c77943d2f254fee28930c4d69a28ed275a50965227ac5c182d665245c76628513497bc00318ac5426598374a34e79e5cb30bbf285746a74e513c1d3e268c53218c4aea4bdceb83020ba234be1919f5cf9d3dbe43623a89d13b554ab6361073938799f952a8e8ceafe09f1bd27610417c2e0b03df7498c927b1088efd993e09efe68f4feaad19cbfcd0d0402040286f4de832153064921077446a14e172661049d8ed3b56fb1832de48036b803f723520fa41f718ecaf3c69151d96366e6da4f7afe2e1cbfff3a9544f7db7ba3cfd6466d25591dbbeb3dd7b9c88454756e437ad2875aa82ea4533f412038b652c100317fe557c21054153b57d77b151fe633215530e0affce681210774be50c70b93308214761d17abf299366a9a8295952b6a4fe59773520a521ac2788883ca1a52324bc9524ac9cc2cc3dec16496cccccccccccc2c99437e29a5dc014ab2e4154c6f834928aa3f75771ab694393eddd91d8c52fdc1245407c1e8c24eb2f39d51e0b9fe6c47fed2ed920c6a7fbe02451dbffa7d3521228d239f70594a139a0997bb5bce12ccda0fda8f1a9ad3ccf5b6cf6d68edf5bacfed5f99eb913cef73dbe37adfe736e67ae0e716e67a2a9f5b1ed75ba99f5bd3f55872945a4a2d2e3b4c2f3030313d7cfc7fe1e776a3d73b7d6ee7f5686a6c6a503fd0283d4ef7382a21cb20643a89e3e7be5c76b2855608723ccbb3242047b87215134586747d724b97652f805d09ea106179ae05553edfd029be2a2afde104c3ce298bd5f11e0f8a83ca37c709d5a9fa2ddf2fc4459767d9dec5a2de6d4a61732f70dd98cbb2174091e3658e90891ccf6e23c311fc117bfc8dacca8f9e64c7f955abf20cfacacacae7c0772584dd952f72a51d5fc58e72943fb2de7f2ab6c8052d12572a41af17ca306490d10ff1d137f703cbeb0899c8f12c447284ef365d67fd59feca7716cb2ada4a7351be98ddd18ffca54c064297653200bab4cb321990e006e1e2c73d0e4309c245264420b108fa4248c5647b11481d14c18a958a8911dc6fdf4fc40fa3ffe8e58f4ad0911582c3a4fa8f7ec451826af1fdb45a804380b6e7551fa52b3fe78a65b6b1016de78d5ecad0a911e9fd19943e7adf41268e38e2b8231c221ad541a471b95088dddca8230315411b19caed97ce8333a451fd3f744aa5e776d795ea5877e4275dd2e97600433217f8dcc62162c9ae20925d41f45786e4042d9f4e4fc198e46d31fa05d91a89e1f1698481769b4ba386b5532bdfef499dfa2070ef9e70574223bce7ebf5b00c87154f58c9ebcd1f56d43174fd7071c1a87ef0995332aefa35409cf3b5f8d070ef96f432ae3622c34573d6473474f5f7adae7f0edad1ef168ed4081d8d2018ba3c9e35c25f5ed3f7b03309758c7175ef992ced2e835d486921ec52200aa551ed44fd1edf1ed4237ce1fbd18fdeef94ba72afac52cc7dc8bc8ae7c31ef1b1e33d1f3e7cecb0324b7c5815958c3d22f3929171f9b02b2a9cdb03c7e5dba5e565663ee6b749b9128395db593a823a863d7c8463748a9483dff93c7c38e7e30103bfdc33a778b456fe65c639cee30a261fced9ded552fa91c7153ca685070c9c470bd4f1238f2d74b024cb8f3cb6b8397eb3342ebe89711c19d7adaccfea60f14d97627c430222d52c49c892d46854d348ab95211a6d686885b6a206e7c8db3fab50473faed05cec2833bbfd2d76acb76447d701561607640e96d0357a8b0f3572c7169fb193711997cc129537aa4c93462961ffa51553c218d22b31a863485add91d2c2259df2bb813f42033c468c1928020317b0400528300109101101083c60080708d180201800c4027e50800f0920d203020ec0c30e06d06127071c746e2880ea864308300001d850430e0d42c4d40c32c4100018708200b979c1ca20e102006c7c5aae17ec47ccc50e7cff8f998b1ef8fed0c539fe3df2b8c26d1e57e0203476e64fd6c7ffb0345f634f6f637f3ccad6bc0bd6e60160518f8405c0cb5817deadcaed803d22e3c9581faa4fe5f52763415595b12a2a0fd823d5a767e8f74065ada8bc982531af185716ea18e37a7b4489e7c06fc026f147d80d3c8f3de23560ff47cbf346580d7c1176fc0c5823fe02b688c780cdc057c05ee02d6031f013b015780a580b3c1176022f014b81878025e2236025f0435808fc036c045e083bc43bc03ee083b0427c03ac031e081bc433c036e07bd899ffc102f10bb00c781fec0faf00bb8027627df80458053c022c91efc126e079b008f803d81ede009687dfc11ee077ac01be03d6c7eb6077781ceccee76075f81b2c0eaf63737895bde10b60753eb4aabfb6004f001bfe107b5f0096003f003be46bb002781bec009e065bc3e7581b5eb434bc109bf33358f15356c8c7606778196cea61b0317c00ac0c1fc4c2f03836007f63833c108bf3d6defc0b16c82361edcbd8172616ea18e38a71b9d8e3fb635e31b04e7985f50cfd49d8fa729b7ec5b8625c314b42978b1bf8fef015c25c3ce2fbc3988b1af8fe70e622cff78734178df8fe305d12db21e98ef474ac46f9c05ea655b72a2da9a3f77a3501572f3402a2f0f8348235afb277b22b49b4018c24289c74e60673a02fe5b6f546aaa6da712412c916517992eb48a4f7b6cf1651c9e724d77deddb4ea37e43fdb66d6347db98773a2587d016e7702f651552f64cbecb2331744b69e1a5b4e87995f793c4513a7b4257b4412dbcf7b6cfb345540c6eafeae813ea2b4a39aefbb4207d678ba83cf0235998f9ac2aa2f22c8cf49ef8c02aefa525e23dc9c2f893be884a5a9c9ea1dfed0fde9384f830ad83f273bddb3f5a21697154fb49721b045263e8b61677ec8a6ed79cfcd8e08ea3233e2508d4a91184020675aa9bb83d41289d8a92ea02b0943b8409574a2923cdae2cd5ae64669e49714bac2bc71c4cb83247ed4ac915805355703eaed71f68e40c8fd6812be99b9dcdf7e86c56464837ba6dde795e371a798ecfb3303f78fffd1732e1859ddbc82fa2f2be6f7377777003c139e7749fd3dde7f469bfcfedecda664a3048766556bf19a8439f346ad6d166970625a8679cc31d5047f38ee6eef66b99ded2d2d2d2925b4824b016840973ce39e21b9eb3e31b0e856e14ad2640dda97ba468c30c2fd8f0f83d8e0195edde6d4c28c731e9ba6e34a294524a3f8f524a2908aaa8544a29a5254a698c49878e5249967474b2a5fb799a14bb4674112a4116e8d08315b03194852b8a15391823084734a9c2118af022128202073945e0020a0f9a60218692243c09a316049bd10381278c14cc645f14ad618331808a68d083042f07befd2225900b01d49152d62e354af685901bc199516f727f7f3703e76b9b86f9c261526743a37ae84d51fd3f97350ccf9399fef5c64db7f249f249a3f97da4cffbc0397e3a2b2aa0bbfbec6a8e39e79c639d3a3eb72550c7ed686ae17671912e54a5e6d0e1f29540b9a3861423c5482cf05cca9eddcdcfdddf3d6479cba4db06eafcb80fc7aa946e73ebe6d7baf239ae54aaa65aedbaeedb5c3131a6171a71f75e7f0dea74f2495ef749e9ee2e59524a77777777e9d2634929dddddddda54b29dddddddda5cb5a59587248777777f79fd265c83fc4db8e3cdbebbecc1bde1452cab652498b34c49448228f5c675d7f9e1f2a89e8d40daebc3b51845cecde9f871d3847bebf0e5cc3f6fe3b9cd3ee2fc4c52e44e20659c3166680834c23f826092e827360ae3f129c339f301216c137b22a7fbbcdd31689327c87b9f1a544c21d2ec245efbdbbb00b67655d9ee1a4c031dd46cda210125d65fa2acb11700f0bdf34c7f16c2bdfa871c7189917417734c5f0c7d255cee16b622b3c1e3880e1415d19e22c67244a299d2fda4265e8c3c7ab7559e6c513182ecbba605d0e48da1992a4a41a6969714ebf735fa2c9df6c8338b8dc91646485c637de8fd4c8ca8a6f483c6a95b3251aad45a2ad9c71bdb6ae7fd7d2ba2e8d5c971746871e617668b9f3bb3b8e45ee6a93e138bdb8f2473b449d9852d41354f9b363e6be0dccc21d9999b9fb3999d9755c237cbafb646666eefb974952e5b304afc0ee6117854927654d8a444a23bc3129a593521ace39c5a493ba31e91493524a279d534c15d2a80a8f9959b664fb9cf3a373a2208a2adf03bbae0b87786c8817a173b17b1f4da3c9393af449dec7dd574b3ca5cee9bcc1ee6c96878ffec7a8af1b7d226e68d4a4027490692330d6a8928f784bcb8a73fc9be6ab2273d48d3a20f0ca0ac76d1cb76d5bd7bd3143d9978a60d4f99c6934e1f169e45ab7719ce5e2f632dc66446014bfb75cdc62fe04a736da1a0cc7eddf622e6e406ed39b50a33622706a9b02506c0dd6c16d87b1df8045ae391f1edc666d4260dd0672d1cd38c26d2087b914ea6ae5d7e1af9c5d67bd530af526d43db73558a41b1ca016266fb4712a1eb6ecac16dcd2613d0ee35adc14888888888888888888888888888888888888888888888888888888888888888888888888e65bc1eb46dd88c335c98a0d758be6c18133724db26243ddf2e0c019b92659b1210f0e0f0e0f0e9cd8a4936b92359d6b707477370e1cddddddc1ecf0ddfeee8e1db8fde3eea12369d426c37777ca73d6ba32fd89cba23fc1365dc7f9138cea2c702af49986ee3f7dba74779f2418b5dec337fc9e53ca734e76fa1c52ca048d0a5532abb0ba1dd7b9a909ac68c3dddddde51a2e4bb677777777a75b28a7505dae739cbb4bd1c61ad2c5c5e9aaa5f4586b1d2b0dbbe6c2722b464deb540fe740912dbfd5b8520a91e18e3b435a4bb221c1d98ea0a6bc1ab7db9334aa4767b55c5a0375e8ebbef9745e1a55b274b9bee386b68131a3599ea4c5ed67f3f162f319b58086d029a750ead8ac6675924fae58ae3f77ab09189b4bb5705dfa25195c4affd429bea6196861064377acbfc5136c7c9f154f58d1460ec9f24477446c649228ea281c54bf5e2c7963cadf12ff9874a3d1649e22aaa85fd7c6ab6bb08cdac508eac842b28b2290e8827366a9f2340a091f2128212fc42424844e7665481e5a44a5461b86746af4fc37eccc8df4425adc897941b63c94cca312f3763f1a3dd73b1a3ddb24f85bd21f171e8a8daec1910a6a49b6641975942d2c8cd4918b24163c9258700ec734ca746254f73b2e2e81ba5d105e833bf8e57e091e7de1c845435cda51269678a216fd27472990cccc93528962979452bee1a6e0e54c917bff8ec5ed3dacee9b6df7144b672a2553db4d77774abdf09a3223e195e526a1a85fa3022698a529d7f4a7ed8d3b3e8e0ebe2b793700fc7c6635d552ad4b682648e14eb66061e1fc0d5abc1107157c88a0021e389164065c12304a6085ada8045310630953e288020b1f38093a49eb04a5ebeee4064e5870bdcb322741f848204e55c90955fe28c3e5cee5103a56741105106600451a2b3fd24fc0dc90022382a9319348648f8442229982e71b95e0be9e0f8a0f4918a3087479680f0d2c1a90d0d043c3096860d1c0f24625dabb0e7647298334b9ed61c8204d65f7352d226587138ceef3a2aa14d539594e5b6a147f5ed4d1c4301c5e5761993ae5612c0aefa52d7259f2e479e41ba6df935c975d96419afd79037598d26e4a7f486f019041d8caf7248d92b48827613ae5b3bc736ac193d496e03975008c5047067241ad0a3e313333bb337d670e479e4f864619a18e1dca24637e4aa742c2aeb73269a951fd813ac6dcbe25194608bbd3ce225414d4f1a53cf594cd85c792467d660961a4f724cf76dd245aab7537f539e766e553ba49e948c625da0430403021a5f052794f7a27a48a8910df935eaa421820432a26800ca93aee8dface3ac363961f880113c375592686cfbdb92c0b43e85616ee5a8e46a32f7297b89d1d658c932a0ba376b922b7ad127eb9504a3b4a24e3127732d7ae02291b24a34ac9b5a7c6ccc548efc713ea8e56a42907fa75a9c97576ee807367ccac8e9ef4dc8f401d8e0379287247ef791f773beffb1adc01673e377a1eddd3397f8775e19bf9d2966490f99be5f992c32f87914e174ea7e1178ee444e2832a654f6629e7c6ffb951e9cae4e74aa55aedbaaf73c5c4f09bdce7f2e74695eff5d7dff3af7c6eac803534b951c76feeb827aa0c2b4b8ed0db9b25cb8fa1d26536f4b9e06bb5bed6f67c1a597a9f0beab66ddb36b76fcb43a3dcf37e5aef6bdfc9440d2719c47f86e30736961cffb52a372a071bb55b6ff75eb76c57c9f13247c87cc3729235dc80eaee4ad881271007e7c14db5763c5b6514c14d826ffc49b33a7fe4d9c7b38de704b343bddcd7cbf9f644a9d4f249eb02eab07c199248d0e291ed9d78d51632a1cf743aa5d4bbdb89253fd785cbb2357ed29ecb4ddb1541d2a8d95df7fc908c4a7093fef8f55c6f9f3d7c50c820fef47300e783de811cf8ce3007be1e72d26fc0bd13afe242269bcb22ee489fc7d41bddbe9f2a7f88d7d5386ff395ee9cbb8be04e6959f7199c52064172e54f97cf600f7d024b879eb42855fc2de145588d924970b79f9fe7141e9f2cc783735d722fbf7bb747e05c2e1c712ef76cf983f93eef512ffc0e912086d8624e9020b9e1f324f8de370ad529ee7bf4a0ad6dc3bd643be2426a3ba652f4e067cae51efcb0725cee010ba67051147359c6c508ee929fcb322e5a3a2ecb96f45cd992594cc6647547efc5b22545d78b7932273050e3b2131848b9a274d282315230e5fe65274986aeb77284097d275e05e4f93482abdbff43a37a76f5070e9ade9c3b9e0d406cd7d333f4975c75f5ea1f29a594524aea79d39bdef4e6ec1f6f25b0466d636caeadc9e66a5407cdcca021250cc7b0f6c9bf9256a7646496f8f011d4a3c74f4c0c141818190f1e519abcbc2831999eecd821c5c5658c961624a5d28f0e1d5072e448c2c2b2a4d6262b2b414077274876411df087f4b7a48e1c1127c4a9714ba69f9d1273769513626ee8965151efb91ad54b622419e9456ad24d6ac2a48ede6bc4f9d4b8ede352c324a50675e741b93d7a4f6e8f9eeb76d79eecb6e7ead4e8f92c19492c25a426a4216f8c298dea55a3fe84d37d3ca6cbdd67e2d114493a3285e6688c3982098d6052eaf7601a75bee7f17c4a5c96df6528b7a350697eda71e4536b547b526ebf47f3a000b14d00f920409e8300010204083310205ed06d8fe641a1b16317bbfd2a4bea2c09532e2949a7466e8cdb2e543872413e9d621913daed5fdd71ebf15e36c81a522ccee11e56758a2fabf8927a3887893eda48c99dd2e2541d19fd78e1d83d91dd5192d17b3189dd9bcb3226acebe0f750eee947efd51de914ede91445d229173af5c237b7e7d13cb7486275349009f5e689348abfe625a873136e5ba83fe4c72da16013eacd0be15c3a37e10b2e58120b00e168b209598efacfdad8489b90396442823e340f28dbffc38e1cabb163f7e4f6d3d8b16b72fb4f763596ac4fa3ba05d4a92ec2e53b6377d0000a0a42a3d6a98672f98676078d1a1a52d09002eb5413e1f2b576270a6c8c31669dea215cbe6f77a2cc9e3c6975aacfb87c65ec4e94961225ae4e35edf2f56177a2b8962c01ea549b71f9f6b03b518082827c3ad542b87c63ec4e149f9f1f5aa73a08972f8cdd8942830225d6a97e72f9f2b03b516232599457a71a0897ef8bdd89f28ad2244a9356a7fa0797afc9ee04b594289975aa7d70f9eeb03b41b3274f6a9dead9e5eb6277826a52a4c03ad53db87c5bec4e106c8c317a3ad54e2edf92dd09ea4182c4a7535dc6e5abc3ee04f9fcfcd03ad5b2cb3787dd09a24181c2ea54f3e0f265b13b41ac24495c9d6a322edf6a77825c4b96bc3ad53bb87c57ec4ed0ab4913a04e75ecf255b13b414041413b41b14eb50e2e5fd0ee04c582649c03ee04c9f886ef902ec3044deec8f56c435b11b7bad4e54d661ecf7b69c7ef7adecfc96a5423a95197924e0a958663cf7a5c5fd4958b5e54d46aefe58523cff57e5acf8349c92f7be0b0eba1b65bcd1f3b0f66794ab26b424340d264a2af693dd8fc26b51fd6ad3a357a0fecfbe62ff4602f93c7ee12d6299e3d2e7e4b9a67cfedafe85bd26237a5bf968b9d3c5274254bb286bce78eddea08a9352aa22bc9069c40a884ac37763dc914a2111900080008007315000020100a0704628140208e5435d73e14000b789a4a7048198b634110e43008a218c620040c008018600c02c8304353260022f6bc1cbff12752177aa43623b73d4270595ecae83cd1726bf84757828de109fae3cd6b64b4f95bdac172afeef25a32829c0c5660bdad01bb2a82753351f29433d2196e0acc97285067e04d56d79d358452ca357e921c1bf52701c33d250b7ebbee6ba84760dd9ccefdd799c053be78f8108124fd1246e8d9ebac0463dd9914edf63a4cfb6d0868f5a99c8ffe6fe20b153b23b1c72c772edafc68af040c644fe9ab5682d6d71dda2bea4f1b378e9786910c6957070fa51a896abf0edef4bb4a6b5d5b8d236631177dbdb255d394fbce2d424420d5d50849aa5afbccd911219b6aa5fcc899790834e96f1099ac025558cfe7c12a50b54e1f0dc37ae6437783549f0925231ba7105b8ddf061f07e68f3ca88e1617bbd78af13f8afae3d21f5420727cf96d302d8dbd8af00ca2928bd82816dfa18d1b306e3a1ac421622ef673c8a2e20b5e80181d6ade18633d58c8be2edcd01583b1aea96d4bc317e933cddab1ecf03b0404227488ed2f7711980e43c3505cf212c86e17f95c3ba12c285f4fe4ea56bfe44eb8e00567cba913d7a83f2309de4b948f0e0bfdf30775f7665131d9c4c0d521a4512c7c4010d11532d9185325c7a4ae6aabb3c20f7b62614a833014a9c29e93b6b20ad27e8370e85441e74082f7fb48a2d0f044cebf86497b22121702f4cd10157d87452eb4cad97f7d3405643c0a36ea7250e23582f41dee93a82272a0dabe473cec2378c2a143f9ad40afdeba9fcb91ec29c81923acc3c701b06059e4f107e333e61a42fd45fc33ef9d16bcffe5fdff2ecc0b78d3e31f6d5a4e47e286cbcde953fde50099a1d2ccfa0b2be6c8fd5124fe5c43c8e70596c68dc9fdc9b284654e8a30eb7694863881246342d20c1ef8e4f1ee8c5d202f6d4c8f009e936f6eb1e2c73491225e68d4b091710448103109c6a1f12d0b9fafdd84e51f5040052134f4b1e9ab0836cb58e83a14fe193f963a55169b646f94ec20cf7f004cbe874ee8dfc3cf7894da5c1b69995c8234f98899b907a357266c510341570ab910f07441672fe20a6247bc7f93ec3d5581fde8d14a8d005c419962fbd0c42a6b6f3e7c46746dc4cd55c2553fd5b2d472e596981d58be2120f82320e3087d1cf6fe5cdcb4b13d7adce06784cc29aebfceb933945a304ed5f3fd2a16f727e90763bcfe9b6e002acc370743892c5929a8f7a4b07a26326d09a7cdde92dd66deeb099259d98c6fd2aa3974fe43d1263ddf080c0240442acf8a8c68b77fbed294769f09219dd1291f1be5a2f9c56d6ad2ad5650cae1631fdde452949c79faa5235abde114381d71bc85be059a4420ed1e92957045f284ec1f8dbc61aeb26125b8326756ed2f5619a9551144db7bd5d19ba3103af16e6658ab42153963de07d1ec18ff3300d465fe025c013465f04ff084379f07a16bc04ca1d3881d3590954b0a3b4dea638fb185dbae30c8f0f0d839e71a263f989154c8a162754675a670c650c485d9cb0a3de376b77e2639b07be46cd4976d9530bed94b7818362522a9bc8d4425eba791214a121e83e24c82874bdc1a6c4904806225434ca8dae69046e2d617c94ef4515511cc9af2b8d4fe6fe06364c1eb7c38a04f7a2432354cb99433842d4bfbbb333d8788bd5bfbf3779154529b99171dd3dc0d9b3c72ea00d1925dae2d857b5e826451fb18a83a458c60f3b5205717d0a013d40bb996257dad2ab0ea1ee5b8c8ca35f0452d390ea7fff8af1ee2cdb7a5fbd7a370babe98189b36bc5df8bb5a73a42ec0c58ce1cb0c07cceb3a7d6208315558a569731e076ea261e333d5f3799ab3311337f1539abdec72f47d0a748030c78359a00c8da1536d1ea099a9ab54017faaa8292820c97306ec63bdd6c470c9b632639d039d2c9e02900fc476e02e854046d466b4f36f62ded599afbe74161a64c261467364f93ea8582ce45b9383a82b8670c0bcbd1c4b91d3efb22ef7f46a088a4a4b77101f51a1a01917306bbaafb8060002e50323a041964e97e415cbbbfb7312405ed281ceb15678d23e8f964f0a92be1caf2428dcf9421e526fa7b07ee8d1bbcb4141e7d16cfed5c79975a737dd16306929af90a8043f49d4f3f21858964eb0b1696b36ee96019b11ac7900c956533c2a3aed143c8886e2f44512b640f70bdc27715c827e939d9d10d3c1c15e7c290c14f8301e2c3d50641dfc5312f9ac0e7d3d4f713c1f0695cd67c11f00eb07c6ad06702fc34d78199daff51c5f13716f6e715d640bd2d460c0f7f46a7b8ca6393428e0aa6abd1c6293ec5fadd9fcdf32a9a0d0fed8c3c927671ab0b51bdfed4dccd7ce263a01c02a6b3ab167771eec099081fdb2b133f40e3bf33f7f8bb7efbd788e5e59ac7f61894a54edca4afb0aee95aa0031c73139534bc132e151c0e47a862f661d6297accf4eba38f82bf554e5839d9e84fc9b9f3235d5ca1e7c6291e8c2ee2bf1c88122aef5471b6df94672d865ec7a609c514b9ad4520653aabdea8e929e689a6f5d0a31f2f92ea0e69bf17e03da7c1cef5782ca17e3fc36287c6bdc2f839a8fc6f84bd0f0cdd8bf01353ee4b1e0ef05b508a32c5a40bfbea82844e4358aa03022d72b12e5885e5b112923c27545a70851e91aaf2814235e88da2fd6817df3974c9f6ab74499ba40d4ef4b7b3d7a32451d8dcacd34d6c7075c184a77c5eebacad4d4a5e461a13d1a06e0ec079e3db4be8ac235fccc5871baf8051cbae41d3d1df388dc8239c3d83233c7841b1111b1f5f65734c3110e66b0c85f9d557f1b761def57347488f227d8cccdbe4d26b256e711ecd3b4a89008ffa7244cd49c9e9213c056fb46d4d156c49bca491e793660d626856c76ec98bb3b28a96fc5cd8b8ca4dbc2a75ca03f1189bee5c221da6331cf05b6cfdefcfc1a69063a081192f0d2a837f442a5490ffd944ad65b453a8925435259be9b19903e8a6b9f8873672389a02508fa16f62c57a4354ec22c64d7dcc072e5aaebff9623c9bf2b60ff2790f21ecd2eb08f92c1937678052d0e9803bcef244a893696aadeb0b10105db9b47e63e9cee2a1fec2d81461009f77703d99ce3166dc4cb92d5b0fed068bc873ec12cf7e428aa7760f6536e154244b2ea2d1376aaf65fcefcdf51f897453dbff65fe868a675288a19e1ccbc7181821036a49ad0b1e573cb04a75bde6dc11a2e15eb5df3c191c0a16c544d2081b81e3d19d4264b9bfeff91691e1ce5a85d9da9d514e7a0892b75d3942a74c8605c763f4d5e62b006d851b3d33d3ad55608ab97f013adbec43ae1a3f44df0a2afabf2f735ef7f15a484d79d67b7e118ce1100077fcab3be464bed6bfd2f04216c3f0099a3e5bed805aae64937f4f08e47d05037d134cfb1c33d7cc50659f698a7bef4efa61b5ad33a8b89996b215216ee666deeae40db14f61b439bfddb9db4100f12685a18d8848ad09f76f87eecb55727bd800dd9b6c9cf5b402e8744843ddcd3a68e96ed6c66844803a50e0193c526afceabcc14c040d5934e5cb3553b878aab114c0416be450fa3b7f17c72a98f226909c06af8eb1393b34385be6568ebce082cf34ddeb5158c5e1a56c2128bf04d870da76e7d0280816f08b9d2427ec6174b6bc3d2b24fe391a548c67fea7964b521757f15a3005e0ab2ff7aacf477b91ae9205300468d061bc1f137970855faae974a9f301edcfd12ec0d093cc046715a3af5df6fb37f215d52477170d954e801f1ec63ee612c80749ab45b55c11e60186b04b9810402417db14481e552111992be1b8716f6adbd170dfe53c5f18fe91714b78a680c742d5fb517cb7247a71f9282cefcf75cc8fb5a29de817b0f5d0efe2cf95061a980c29f4ebdcb3d024e4df0b6b08d5b28019e289634a99bf85d87fbe6a252371d320a6d7d6889a529b4c9d2240c6daed9876a02c1d4e1b19b15afd0c680677950889d4d074c001536188229a5ffb868b2bc5f3836521270db06a507dbc9c31e78bdf1daeb3a6f847b9dfe81106bd632315ad30e0ce089f22e088d2c91ab3fdb0def6941fa1f87f87b87f77b57a9c41d873b0f03af7c7d3039d56c64a037bc91241e5b5422dd4279e2b19e6ccb7dccbe7a60ab361a871b97879fcde4dcf0c6b4131613c5b5930c5e982d47a8e2320168cad7cec052964b270bf756f90ef6785febebe584d4e0fb840c6d1e58fb6cd879128213332c2ce5b532acf08ab7d6204155089ec740bb4b6bdda4b614e24ecd6a4ff05e320d3adbd650d6e25d1a1276282e8923207d928528c89b8c611cbcf0b2fd9bbe8a1660e4968b4479cffbf594e7cf44c4bc29a06513f62a3d0396057586ea011ccd4294277536139528aca53f18ad770b5553697000a4cfe2f944c15fbc16dffd181ee3f21f5f50176be663c045385a13c07a7e8e06806e9d0b011de229286072eb477b01b9fc191b60323caf07f00e8e2302ddc68925003a3d4f0ae06ececb02adcd1930603a3fef0c606fce73038dc1893a30b2f78c826a5352f5e203cac93cfb19c442f75c667bc6c49aa004e381338c7eb01ec08590b0483aa2c5361a9de330e685412a3f31391d0ae843d44680cb1ff5033649ae07f3e92972fc00bd1d6de4e0ad4eaccc530b4b3249e7bf142af67e1dc361ecddc54886ca196e514e00219b11e6822ab5044e4f538883330147fa5d061796ed66338df7093e62755ea2b400a04897b14868e0b3e250d63845e4a58e9ae7ee6e7cf3e955630932209295f0f1c75f18c20beeafd307fa623a397d90c457e47302164d32cbbceff81ddb93111095ba7a93da9b2874ff725ddfaf44350fdf4c962c88eca40919aef636cc17fc756e291ccdc067005a0140d4148715345d56fb62fe3f6d1f5f26d4396999bc5301e90f38cdd4c983b8c58dcf95fc0e54930325f69a2d5f3201010d370c5226cbd8f1ffa8fd862d1765f2ac53ae6160452a8579b4173732106572efd027f0ac6d86c5c04874a3b92d8549a35285e2399192b424440f2db7564f9d4d8eebc6ce7880cd22a4fd81d22aaf5bfcdfc6bb2a806d76ea8803ef1407d125ee082c81faa1d32dd9f5935b03e55cfe5e1eb136b7c0bf40f45aa96e1ceacd6b658ccc1e9b3ba7d70f3fe28ef168f722d84835b75f4edb38039d1f9fa00bbc4e8b4885b2b26f9c3a65110f5243a7b5add2cb1edc2769f7e170605ee9d0599d50966030121da76c77e31d81e5ba18c41a8b8d74ab85a5b41195018efab65a43af449e0e4398e3988a50894128d5f07781fc1297b84cae009631236cb3dbf95fcf5379028671ba7f283ea3df8cb2745b8d0929865a9e91c5e0ce2816e40a468637ecca686cb91b0005e60cdb58c5be6040ebe497b90a9a8b56b506140f3ab8bc7d0b3ada7d508eb40b6853c365db80dc2d4e40d3a847e48e811ec2533d20d264f0bc4aa40e4515d22b6bc57041cb9a5b089ba9789f7887792f825653dd97b9a6413273314927421230cf0c8cfa56c7fa138e618d3b25fa34778d44247133f30db95f4bf933620d67869d322306f88c78c39920e7ccc4003f33d2f0cc7fa8ea07eeee4ca916334c60cf881d3b73d6c28c33c033e2c7ce94b4317306f4ccc8b13327adc44ce5f4210669324298556e44486dbf7e411439c0f38db806da858a4ef0d6f060b35e66674ee4dd99edfa403842b3b4a134cce22b5cf4531165024beaed6806a6ce850d83e010c476d04ebf0b530f00adafef4d7f7fec591da811ff1807c4953590233882da7a73cdc386dd34e2fc5b9092fdabd860ace2800c169a73130a9457b184b9f905f98f06a20112822f745900032d959a1d14bed30d6608712a3cf87be6542b2e8a5684da70800ac2c18a09c21159e80b7a5c5579b2985c2f6f0d759b436cada8856d6ee76299b3215b7f9e3c11988e06192a62364c46e4ba314e81afd7372167748f95a2905a8f0bb100209e0725935dcc9f9ebd7e532c32f9048b00dd6c63a906352ba1b77c9c42c1852c1304fb7513e83e2d4c7348a019f3854b80f4b8df30c58aa7f4a0bce59ceebbbc5f423b240973edaeca54781f6c9620a58cd3042fb8acc5c2e2dddfe5a009b0e8a5a6218302e37e841a5cd036621d6f1128e024e0123aab3ccbe0ced9c74a0ff3db9258f34c77e4b8885a206294321c3de8b70f646a95c15204df203f3263f2ecddd7a0ecd29ed746e503f67fadff19a7c0d5746259f4c83070bb0bc85fad63b9c09b95d3acbea17f01d7ac416f0936a1d5a18f9e8a7d6e4047780ef047b708b3a472087a1dfda407e8237f0a9de28a6bcfa18e9b28c1dc29b27108defc47beb024b84ba02cffcf7ed0c4091abe657d30edeb378d42369dd1c40ea48ce45250d06cd93eb5ffd64467f0843cc76573137f6ceff412d4ce8116dae0e926b02e42c58caf3ee6b9f0c9dca32ed1d768ffe5a450f3668f4f55fd24995294d887af7a21a8b153c8bc66d59040105c3a78cf1db2c15cb87a1635ae865c8d4efc85ab3cd7c7c402bf3fef49e76a5cdeaaff1e019dc739d4b4571d26bb2712437622cc9442fbc74341c12d48f6eaf5be831ab3c810ac13ba5bef1b3a2eaf6017f456906857f747cac2683752c493bf0ebad19116a0c736fd475c039b513f043865369c6d38d42229341cc19356a0fd8638825ee6eba4ebf183127f1be82bf8e757f1af293f3595330940401f79c7880819b6d819f61ec23df8d5a1a5af5a784b1c388ec9f8e1a98e5e2268b33fe4f7f6f7eddb155c246bd5bb0bcd82705014ecc762570624861a742d1f5c06dd0b47457fa69da826804bad756ebe54459c453c965c365fea70d75366920b912b191263c5b0014dfa22d6ea03e8ec26f8a522af4f9832825a3124c9435a4d57ff4a41d168ae0492fff52a3ba497c0cf861fad8c2df24e93a4bd0d46038c0ac9ce595eccaa8367f1f8270a7d2a2fe410b427859e2a33d544f7f0b6a2f7f5cf94beefb8aa93d5f386792ab2ee71965c27df5f9a7b6e5a1580ee4c146611fa9f37127c7e610cada7b5139435e787d7d4cfbcd0f1d65f15babf5f7fde82e4b6e78a0ba37eb501941942efba8261fd075a67d74fd8617704d34699ae88b28070743634c7d50194f838e3c691ef51644b16d61c7a27fcfc279436e409e9894f7fee65192cad5e6109d1f1ab34b686ba826758171b5403273f5a6d358885c056ee418b65c8f82a3d1d3293d061c05a6f386b51bc2c0c2f32f4b976e26c316e6f4a3d6f03a9f03da7633a93deaa906259ed45bc5541bfcec4e77e9f41cb2333386403e8f0a08690e77072d5b33f52212db7239c7a98e0cfbec652f20f76ba1a2b0caf5b6568f85edb7c1a18a4ed6597b15e2308e32012654eda6006b280521d581869695f4185376e3af3fc19af75d36bd28ffef27a716f6c7a3e69842821ea4bb8c94ae1f5761186536339cecddb87d55ebbd7eede410cf3276ad19fb319ee24c664438ec53ac5ff7ea535d2ec042eafe783a4a0327a6524de5abfa5b21d42791a7de65d7c7d853b61e935771bf3903f679f295b98aafc598ecde32bb330877c308e2215176d9d6fca5f9a8b6c0041f540cc7ec925ff761854511cebda42178e07c8477fcccadd3234aa72397bf468ba78c3991c93e248dfff503e018297e5ab9a81e3722a4afa6c5897484364c87e85d29b00189bf4e9980b80bcc7e866060e5debb5f4d414da75c75470d9784d272431ed008d0d19441087ac4de0408360f546df71c59b7c14ebae2b47bea32d87099a9a9c3d84d876eb8a4bcf330a593b966008df0305f91af33918812c5b47d09f8b0c4b2cf5ac897c7723f394642d108e48e4641c3c7cdc12fb2762d8b361fc0ccc5c8690a77b52c0ca804820c9fee06305d8dbb7aa34f55a3423ef02a27f3c5a8021ce3c29e26c6bd0bf7930e861a93d3504f5f828661e3b20b51ae5be06853bab2f1f77d4b668e88fd6dac7f17f5492b309fac76b6de658ebd394dd862fc7384371c606d9747e655cd4db1775a6626c0d27d9dea0fa68e46edcbdde1474f381ee77aedd899bd657430ce2af23146dddcc3fdca4b0c11fda79bea81543cf219fa635bd305fe9f3d9038e3b96ad39f07b0883ba7e0a632c34bd846ae9ff8c576944956d76dc8af4e948d3f20379a3950fc8e16a1aa9e686c12035bd765167b29a07d2b9b03bd45799140425f5fcba401d34ab7078a8363db655aa4d12a455dd2691307bb2f5b8492746554dbe73eb2feb6ece1b1bb06439992beaf2b6bca8c2dd3b58b0921e847d2cf3f953c7874588c0c16fd97b061dc83ce40c93064bb64fbe3346fdd1f73838d2367ba0e928c4d4a44ee09959bf200d86f98c61cd85326011bf4fabf69409d568eb59acf05c7c9294f9588b18568813624e9c98f19ba15af6f8fa9c7be3ef9cbb6e57d14745c68a4d731e772b161cd8163fa97dc2dc0aec15ced96b06670a701d0ef7e129807ac0058e792def87b23582a58bdbc77749d97d2c3eb9714f957be0904dbab6d632146c26e1a1b519d5861956e5b4d1f08d5eba89967bcf72623855f1c9be1c29ac01176804899359d5b608000518c5556ab74e6f8ce8a1c93e9ee781f05632914fd0be2ed4797176f7f66b41b0eac32cd5550d34609544b36255ee015353daca301e4d70e859aaaed7c0a4c6da3c1ca8f290baa17a15730f33705098727e834d27ccb40bd7b8a0e1ac7ee9596672fb7321fe16b4cc0fe71db39ad7b27aa3a049eba7e0974bca1f3476e5950a155624c4049b788310333b4b52ecaa00043af655f7c4af1c08eea3fd785c04a95acba2c9975dd4d0a56eb29a6cbb18d88117232cdab75f7e6dabc7d2140bb352e51cde421dc9200228af321555748ddd9b03f0facd297e3abeff79c7a5f0c651ee99c2d762a3a40b219a8c77518c5070879c17444736eb824d55b8a3b06bacde020d062f2a6021fa2909c566806beda3492394723b258151a907aad0b5f1990f7313918a7867b299ddeca9d21d9558788cf9e5ca9e390661fea57813417067ce44b18d9cca36274ed859e27596fa43f89cc72ea0de0b2c13d8d4c41e9c355476519b556d27c27d1e4b772b3b03c06083b3202efa8eca58c81e53a3060631a6a21a132de43c8a5c044d44fd6aa47e7cab2a4892e2ab7ae66dd12962d1e70976763ac6af0063cd6a5508c39395d2f21907be538e5bb5af2a1a4d20a0ddf0a6376b189f0f51e81f590d52f553017cebbe1ea379a225fb912380753e63d4f3bec1f82226bd90ad6a02a1def25617f40dc07062eb0b0b567228374ac5078c3587a92d83f247497a83e437b77b28087756cce2ceadeda5e9b8bf52c5c93d8f524be5ab71c0ffba40cd19d9925acc8d6d45ccf5ac34b0ac19487eb14c465bf8bca7b1b501d47b445a2360e786eb465da47775b19bbe9009135e03f7bae64ee31ab215435dc2546c52a2885e480a998a3553425441778d20f11d8a223dfb40c72730a14f6f3760c5dce231e08a0fd4139730763ff16e91fd24f72e5361a31f4d43487c0f27086124288bbe2fd00a0323002062acf4db0036a5d7c0974c5f75d340abc0dbeab28cf4547551c5a0f12fcda0243977eb49b540ea4b3558775844bb52c973a3f98aa790c58880c6dfd46e1831e7679280aee66f79205826851bbcef33c049bd1df762415eb60b00b290615a6a0be09eb78f1d46817fc316ad261ed8029dd4241750ddf2b18aca20d6802e4470cd30a26a4845db23dbaed97531174ea124db070c71aa9bb86d9545353e79041c19bc699b14d2e6e3abaf534e7ed2fd75efd0687791b8f118dd730d88219fc46cd278472df487ac3dd09a39615f23ea996fc0175963f33637f8a491480be639653c86322106da8dc79a62847425d2978cfd6b73bda90395a067adfa28cb039d34a6ba1b6dcdac30046a9d57582e5c2ac238e42bee69237b09666c64f980e69106f582163beefdeb481fa604209a032d24add941f08113872975e507a1c2afe7765662b036da193261527ec65f228955c23ea2ab0a59c5b579d063c8c3e96499267d04c27e06874ef8b9cdd17e6a48766ea1f9d98a3dbeee6fbea4d71f54747763016a6980d0b544b0adf660435646e502a5ca13e886c20a3eceaf540b29a2bd592150f5100ffcc93d0c0f209c2d7fa703e28db03940148318124327b56a7d1a1afeb9f9243badf66e94ce035acfc7f0cd5acecac50baea030ab869cf0f11860a972ff0635731b6f5489f82467f9ec114acdad506b4cab2d3a2955f305b5c94d6a15ecaffeb4911f5ad18e811018931146972480c008648e233c69a779e2be12950bdfe1444de150475fa8f1a7805c791fdd9f61ed605fe2e99fe07fa3a176fc2ce8333f1f2002f274f560d948199085a738903b753846c86856e23fa9981f3302246d0a0598ad82b589b9e45d08cd5284cceb3028eb290aceb8077a019262f1d99611cb553fd93e358e6f5f06fbe8630245694a8515478349e0223a27ae6d096148dc86d98ce9a4ba8b3fa34188124fdac55858e98fa186a07c23c9301317cbdaa261249e631e2ba572e6de1ae5a700dc21b6b7e90422ac37e04318ac9b616d9dd42548482f6e770f940faa2145126236b8a2ad690299ed6257f5f894fce70389432f15b1a60c68676772d496b7238e16d875d505a3685805ca2db5c4471613b25361dcc74426bdc73f5279fa8f23a7fa94c04724f0d95653991e04de23ee3ffd217e0f1b86f60b06aa1420a67349647834ab3fa6fafe676d50aabd13fa7e3904e82855d8894e235c850f95987c89643e297e7136145d4cd741d3e936073e77dc80abfe54b059ff994479cbbf07aabf3aa6f147a62a61f2191a63242d40734c7000ccc8cbb952c46c5c3343dd0a17f579673bd669aebd77ddf1293617a4bd743f884f6c3578d9fa805c745a7555c4a38988229689d140705946c5788854d9b70166cd0bca1403ce17000b5adb70ef97c486711dca4ae77e868c9ba7eb30b847c34d08e4f938cb2f50f2619a5b78fd1042e0351353616b1c71213a035e3dafe768057399ce7705e1f6ac57f1e1a8d90084283a2c23c1b977c199b07787e458871b79a589fd8d450547dbe17f4b6dc6a1259b6aa9bfea798042211c6793a77571f1432037c4bc061a07a8e42b7fcd8dc517bef25ce2ac005c6f0960fb9653b2ae3edc92846975f9d3c01b6788067411647925e92fbdff7a9a845e3037ff85e34e1f0e483601813ee91aeeef8ebc1dc1a33282e12ee1503107b74966720d18a9adb7573f4aaa1c11534bd8e375b243ec8b6aebafd89f76bc1258c576a2f747bd31dd9ee0863a4839d55b045412915584a330c4c6ac0a2808b48308c8ce88c181d98ced1a863af89b88f38a18d1be86af29e72b29d012794d66ba27c83304d7d5c30739d636d77fadeea3fb2471d39267885dc90f996e9e67885db8ca4013bb2ceb2b6227c4e709789191b08061b344a95be2a13004870b97fced707686b86159003884beeaea168da566c3cf5411eab9326ac56e4f7571b5f1e7dd688a51bf07ebe26e2c4873e2bebaf8927a2491aa3a745a039a57b18686ae78ef47addc24eeb8e9847bca685dabd89aa6328b8175cf9d25daba005fd415d7a045ac5dbf548bc94f3b46c8b2a8e473755f4b0a942e005c02e313b69fd132468ed59d11ba8812f3eec0eb4015fcf22f72a1e9f6bb5cfbc9d205b110caed0d6635eb55eef15dbf636e982503be9851fa965b81a12ef037ddabf2d4ef1e0207bdce909c9d6ffd36e2a5420a318a412ccc02f4038992cc3689c53e5fc3d28270ba99f75ccc7c052f704d74c3136a87f017bad377767851f7b2c7f0b4161b839aba7ae4d689bc1a9f8e5c505275c41af6d045ff7bcd2b3bcfdd1914b3eec3aa4f14e1d90e9aeaf244f1c0511ba8d7456616fc1db84753758343d02c1d124287b7283ff82ae8cc2ba0dc4fa5a06fa712b06e842def1c0d94ee6c05a4db5ccac50ddfe4574d37ad02e53e95667d43a9f9337420e9e7d52adcf8d653f5cd548aeac69a7aefab405dd762a9a41be4c6d8b3cc46fa6afe25b46fad8b886729266875a869c68053afb2d107e6044709d6e74b866e0fb98930a55b223eeb5041ad9f38f22898aa2bdda2309822b97c042ee0072ce99c2ddd42082a0c89c49c5fdea096e4c5cc6e906f41bd0f6bf5e253d82598d8cc26b437cab08d4c1601f4dcf3e8b28f28b73014ebc95c98694d5e8e8a603a09a72a7d6c662b722911c9084a82580243fa0188827f9e6569baa54731c7fe370d2eca0808a2d04690b29694438d86ef8497624897fa28101ea6be960d05712a23bc489f275ddcd9a46e3b417656b6c1f44a9a9dbdf243ae82426069038b23e65192ffd9d3b5cb1d4629a4cba912060898db5b1c02bd7bd20c28b8c37c368621c0327095df2dd67b459a564c84bd83a0f898406d4770c9182b4d4aa17e4396c14682b1b552f449fa474515a50b4b164030caff0a4d67324fe8528cf802b0101c526e8596641d1389e409c002dbd3153eb905c9198a05e1a5fceedbaaa27375a93f78b58c0983217ae2533208e865badecb3f3f6290ac5ad11ea9676084ae1cdc6e7be26c6b43f0d2df602ec880218c769f5954f4093f215dab94d16627a0e20e682e9c9a572090eb8e09330bc2e01651e521c06f4910602dcf04070593dc45812d14581e0e0394c819efe01096ab6d80fbf2eb05e44731e60763b07b6dbea07c0e026fef353879c1696adc8e251779c87501747d17c9f228f19ffbe791c42956025ddb8d83491439f633debf1084ac0515510008e8d9fecb00c0ea6981286b890d43bc9b020c585641ca58542c210a3267e04149e6bc13e3e0046a79f679613823d6220d6ba69ca3855ca78998098943318444c25f53801dbed9f8a343bd59c5b55924f2a6fdf75e9855f4d40763e50a2b29669417734a52a03d1fe5bd80bb721d79bc290fa2b52d227e4aac9c0576e905fc9c428cccf6965f6a15ac2ff4810e274f30a6423241e4e31b5f6d88f06d135a909189fd3a45ea22b3a614a15345d6b552842e26c2e3f2d14d649f2a83e5f8b729c33c9580b05bff20340b84dc4dfea311db926089a4a15e279435889f117f112d21022be8053095c95e3380b72119fdb5571c2216e9d8142a7c212b78fe35adeb724959de4e2821a9dd4baea9015784837f68828a1e65635f2305d042fc3fb0014071a501b91f497e232a9035888a4b1c7fab4113a8fdb57855af676255caa0f0e220c26ec84bbbdea5495399d4ebbfcb1bf9b0f846eb3d255bd29310a3c63c31222b70f15514f1f1f7bfc08259d5542270017a0dce17496658d9245d5fa19227f0c04872467e5e1d6c4ae4203f134a1044036569222772640175318369c12ad0a5c8dc5700b42cc16a45fe0c6b9c2d4fa00a5de2451cd88a1f881451951f85f4820abfa21d252a8661a234038891dc3401a454efe40c3a475c8824a7ba93f19d9c2bcd36b1ee27142024f71dfd107fa42c9ffedcb869bcf4126a3fc9ae02019ec288a07e43b23904fb19e481fab5dcc818daae2566d31b28a876958c411111a58fdd256695631223a77fea95f45f216a431e3915603fccf8fd7700e1a233dcfd10dbfa60b0b65d20a08c075fc9f5eb8f90a3e3fc4990a92cb7633fb15f8c1a11b683f7b346ef877cbdf0172c31571650af0d6cf204b2bf142ce0fc3518c3f172e364209d6ff49d5b4b699540114480040d631f9d2e8930f89b26ae1b83223d9adf8930fc06136d1434e358d0c0f239ff77012ce3d035b46c0c7ffcc06dd08e130a61e72240150c6fc8bc9dd28b1b1fa9837b3072af7f3304fa4f0ad3bdfd622efdb5b505f583ef48bb8fdac04febf59539e43ac2398d7f615109e57581b877b5a4ea802ba87d5bd58412a4a8b7c0c7256b57079412ffeb240d2859a795f70681d6dc56f610203be5eba6faa867262e1a9b61df613a5b4bb031d3b7a4758fca17375a8e7918a4a9f127a00801ed78df98f82492ac9cfe80509517fa1c72cfda252b18264f1a63084ed062deaccb83ef5a6faf2be4bcb75b6695dbf86fb3ead978c035cf1db25dab44cd2b58a8780e501235897f00292b65f7d49f3c9ac7c63a9d4ee5c006daf9d9f97e080f25f011612f9f03637edd50e7e1b26e441f6ac4fd3360d94caae4eb540029f1561bd88ace2187a00e862ab50461cba9c8a11d3d8ea4d09883ddcc708c7720da403ed8f38826a515a4a7dfaf2bd3c413f63e7a56d9aa4e2543b57cba29761329054c9aefe0890178ab19d284df2dfc7b021aa4ace363cbfb0024d0144c160875e8d2492464f49b419558d144d51be529f3d6a3146dc1ae9ece67d914e5f3998d8f447d0462d56a7c7a75b858397fdc1124fb675ae48d910aa453692d0ae15d686dc53cd842dfb8a1858011e23039a9d2a6913bcfc562f69d877d63cd432af40d1a0eef1b5cd9929cfe94d1c12d2f019d6c3d20bb6e40de4c28ba40e8bdce77964036a38c5b4f2f50de82b59484b30577d9865904b884d640af5f40163c05d65f071c4334cc13ccd925f00322f9c268eca046760f3c32c2ccd7a64679b8ffb028675690ab0ed05a72d907a1db81e752ee76a4082611761aa6a3a490700659588d96ecb9e6bd7d5a6838c01afc8e42c19635707df8080bb36c9c3eab64225bd954e5eba9f97d6deeec58810dcc3db3b364bdec25df1e0837bcdcf3445a31961b676783e82706797375cb12b4477fad0be564cca013da8b9f5d0edc3f9a65658070af85e23b82e0efe03ffc04414f53ddaec5caad7484024cb1d620a2bce3132ec44d0dca77e39e81f727e6b7ab3943bc6bb1a6b51b1ecd9359348934b9d67c16b428f0e126fb24bd6854d7da093ca62b1d0636309710a93ad5376046a8c16c1dd1417e10b8e9d0d5fc72171985fe7af915eede448582a97e8368d574907b022c1eccc582e54496d669b3324556ed2935181bd5cc52b2a243724a533d04293f7da46fe367d1fe03b2a0750e5b01a7d4700eecc3df0f1f05d0afda7c8b1cc29a16adc87c94653d6aad7f084bf1145bf1324cf937b2abd997793ea891fc41a588d6d219f2261d3b0991f6954709a6c5dae97ca4c3a28cdeb8ef2d18b4c553b8249bcc1a08fde0f2b0ec5e0e30967b6656ba549c0bd37db0ddd6defaf3c539d6eb838b788732eefb00e9eee9275095619a4de9786f3ef80e9718aa0bde583b5aff4e2b537e5533360365a895403907116049bc6e8d76ef146d728073f232ae402e905f616300d8c80eb60593f7513cd478a062619f6296da3f03623b5907e2624841f3d6f0b8ea5f9efed2f04ef480fcc3a1e306de81b08c3d8d5c07bf195020011c75d00354edb02d24062b823c3a9bc7fe689625b65ca456c3c7b69854d0e585bcd6c25a2d2774dd0a82d0ec0bd5bcac4cbefcb5cd67d96a8081d4f4c07f35abb61b83b08483aba54551578f7a077da027151608c24429063f82054c512314ba2bfa210074c1693cb5e114f1e35ba9a593b78a2377ac4f01f9070e1fe5b5e69c122bfc95208756e415b7579ec04cf82919f2abe8b6f02931476897224415abc8d0b98da5f53bcf6f2f834a27099fd03780185a84c7bfab8611706f75bf69af1144f35b8d86d7d4e248fe9431fdad51ea65e35e57c7b3949f323ebabe8e80c2b3125b20c74e42956ef7d856cb089a64013a019bb41bdada94bcb77cdb702a7158a91d81ed090fb0e0b23ace38778a8b856500598c23918ef83b98ef5c15e0e08e8e687e1c1c30bcbfe8867caff21fc25701e1287d3faef7a13d0071b014ff5a2472b02b2c022ecc9386c09b87a5a1640ac261906cf93e36ed13bab56398a913287553c62d375f97083cde3b87394e2d915e7d99c7e5239b9974075da2905689595965bb04cc8561074d506cfeaa2d90ad112497bccc332488bf9a07f42c3709a0daa838a61002ed92a56014b2bee5b0c6c1a64c9cab73ad661927a77c87bcd6733960377a7995d68c4152657b75029c912dd57971c74a1b5a66d41ddb9a034076fd840878aa113d06eaad84cc0782afe477ea2b0b64a3c07a72e7acbd89b1ac3a3272c013b318a7f72bc1385f4c000b1c419a8e4300493ddaba8c55b372338d1423368227f6145b516bbd7cdfac218977c620197b98c47a0f39e116056678ac5741cb13d342be114c34cdeab99d4ee74fcc7b7c3de6765b93e93c2ad99688eedfa4abe232d954fe340cfdd8c116b8a9470ca327e53f1a77a2dd2dc74da7ecf47654752d2a2a77018c4759606ebbdf574cc8db3f98ae6bb7c45b88337408c91ca6246cfacd3aecd625a98886f39f1a06c7851c6ee6af881ba7b7fae98c220cfae2934c45de8a5d216675d3ca17a88eb81eae19585cd5e46c8fc358060d322bf028b40c8baf6bdca14e4ac2f6cca0afe52ec262083705e28d2ff4844ab5ff5e8f0d8249ad9ec58f22f1f55cb44fd938e424c31e470e0096265aeede9da89677c4d30267e0c3a7da45e0a6b5dc5d927a1acb462ab576e9bc2ec6e5c1eca7c73c70861b279ba0f648f25ac65358de0cb4baa139e68e93a4e2998a056041a7a2d3c572b4dbcd0e4b900734f2046c6de7be9a912ebfccd5591f77cb46022d10908c3308c4b810e8c0e11abc0c8dcc93637d657e3ce21dc75e3bf1e89b8fc187686d001a2519f571f67c958065411ad4c713043fabd26eda780803d2764c1c4e629573f95aeea8ac8dcf67d9c9c3ad0fd7af529c6bb9b09f7f9aa5962bfd8768b97f91baa5754d4b1a6d3310ad6070a75d14492a2dc5f599578eeda03eac1f76b437f4168e508cebcdd7a7e295bb7c634e1372b21e976bcc77c468226b65e2731ca96de4e50128ed7233439093886ba5c02ad2a53dc96b9752ad5e8f191cb63d28c644e5809e008db48360b93bb687d79502ff9278b36943a1b713de376419a9a7132099f420587b4653c984c22a4388a66f7578fd070836c235360c8a04da04d2b3f3402707a5681de38f7362580e03c1635f4de17a7ff4ade1bec9af650672110b0fc6507787ab5a3614499fbd80b4b8021dca110f649c01e792a6f2f18cfcfffa8b2b343fc70ac4ad1320e51adbd8feb442f061ae11e2ec7caa5b9da296cbfc91fba1c306b280e0c7826975aeecc4d6eb401564dfe5ee7bf2967f530bc597736421603019ecd29616d711bb78b6463dd9c5439bfcc1a6c65362b9aedd623c65a3e45b5e3c25acd7ff76a9584b53ff99be1fd7d9cfc3978aaa761f475ae969bda21fe811fc9c5d308da4625ba6b5b711ab980abb665fbca0dc665210bccfafe3664bf97408779c2fd399cbe4ebe5d7f9154958380b417dd18c1bb659b1f1e4cc2f6df9527a6ddb60f9fbe49b8cb30896647a66c1edf731bfe822f445b0f2bea267b39df70a466fa8378dcc30a69209bbe481e24e83d0a6b30178e20bb9a4785024480635d0237a393075b7cf890b60526e20d9fb29e9b56c5ba7c89ccb1506ac5ab072dec806c1325a700c0c3d1db1c70ecd1eb7786a8c3ecaf92107a48e148b01e0c68fd1dee68a839ae73846363db00fe058f68d73412e4a996e6b03d91efe58e156ae75df195cb752c5e8005e66ae0de5c0a5eb955eba1555f0086e81604dc2516c6be45541df938c44b0f9075ca35899edd226a10edf2ba08c2f32ccfc67884c447bd77b9caa9cf6f37997328401138ad0f990e887ba8d485deda71a88e1bd378ed82bba32758b593de82c36d5931363cb6162b03c950ae76da9facbd236146c3664d54f00dbf80373b6e17a36a9956dc79f8d4b8366395948e00f21c70450b77e5a5ea2bc22b1d45583eca444eeccc78934c8f582604047fe7666aa9b836a47e11812e0b19e874afd900dfd6011665f7271033a03fb58ec03f73c80433f508dc2b9d2d9834314d5a40891f1a0cf063b2fc6d4ac82ef7527335de033e61b516341775279850e7214c6084ff70e9268140a19e9f71a13dd98f804726149e716592078ab6256e2a8eb1e1cf4dfca9116983e0c708f16e83f5053be8a9c847789ec16c66ffdd01a918930b8cd59f5a0a2f822e194d017dbd6d8235933e4854716a28e9e7a1c16d9074a801017748c90fe289ebf9fa31c70327074a4a569909e4e8ea9c706f39d789efb2bdcbcb2dde2c0e774e64113fe4d4e8e8d69207ce790a3556ba90d4e1afb71a4cc105c35d38412453f34de3df659284fe497d3e4dfadb6c52517e01fbb3d99f685470495f85e904d6bf3aa11c86b2cdb7b4da9f26a00ad64d1f31ee3e7e64372d53ce7fe5610b2880a3b93f1ff52862baea3faedac5810f7d567a0602c56fc3d0c21ad5ef673a711ec6339bee4af4da2cfa5b817a090e24c3076318f48fe6f13207f7b5a9639944aba054e14afa44117cf390c54ce367154748c0cd94eae6c809b34f86b6c49645fda49a3b0f4e96c020693b21f21f907159cb0d505e4bb5a2cd607a967269c61458959b1a39e7979805af402c47cd7e441b03fcac68d22e19dfb15d9c379710df255204d33b173d3c5ce40b303510920976f57431d826ec21efcdcb71d1075dbff8b1ee5f0215dae3fe7484a47e221a62b73a5e40fc4c98cf793bca4bbe615f5230379e018df8d6b4815fc3928126d6372a86c62518caf39c7dd40fd656a5a91f359da96a4bc72d7c506879ea3b008618ac08fdf7f700165727a2350e69644e8da265413e698e4d81ec1db4cb8a3feeb4d823b0bcc51a4b372b5899308596220a659b0d5c484671d7f525ddadf94e3504ae042bf207ad8e1f099f46a7f222b65a67b81c35cca4207738e1bb3b23dcc14c253d47f723e7b176f86400663fd096ca41e57f531e77671e1e0f42b34d308b1d24e5775b821712a28f16eadfc711f300635fc088da54ae24d3c31c6873a5589507a7876d159ed6eb356e8d41f0f624c3fdd12515d0409ecd00f2f2187a64a8f63d1becc232ab15df552c8a4870e26e8fcc7a8594a7ea71cfc8b147f5164973436f3e03153ffb6266b32a98e45833403550e0bd181dc18bae03326d876c36a899e329621ba5d293b26795764d9e92a200c64388c1647aea48d452de1f37b7c9809c2eda58244e7d73a2123d8971461be1ee220b6869082c6f1889b44dcb07fc1bf9d4605b68dec33fbc4f48f1389058e00a7219e67ce1efa51bf61922cd050f10bdb7f1cda062ac405b3f0d877fd39d1b57884c5078ddb0ef998e60e3ef91a1fad65d1936fb4e4ebecc96802707f68da8251c678cf7d50dcafff368163e1b3e5ae0d23b1606077d639a5464878a873aa5b30bd32b9b5ecff6e8bcf2908e7c68a57dd9bf9878b8b9466cfe8373e20ddff15353cdb1e593dbcbf5a74f546e67a415404b36affbfc74e3949016e2ba12adbb47c09f83ab0a7131572f2956e29a5a4c24a427d062059aa74fb5852a41dbef34682bf2020525f2eac5118d4201305f83e7c86ef186c7608659d3c93c560b2a16bee6aceb98e1b4a2f868a77384b3a32bc734c498dd72fc05464717b47885e9e15fe20997860b9ee589d055c8ff5172c219f79ce2342a54f6a0f702eb632e1fbf2845144c3526e980708a62771eb8eee376defd2a0941744de8b09b8567e7a53e5af77a426f791f100e419e5d482d1364cd7bcaf72880904d2bb01be311de5dfcd238630daf633efa0931ae6f8bce5153888656b7cc5967e8af45e5aa00bc61d4ae26e1c90c149456f01e18e20a0a5223e1abffac9b54ad0d25da47baab132075df84e09bae5a6a09eae8f810cad515564692a774539d2f30b6a4ad792b794fc978eb2a9c809a1e16fe827493d2eb5fb16e524d78d4faa7d4833d12bc64fbd7e574b8656bca2d447eb245789c12439860aef7ae42ca89cebf9a41c9155c3bb627e538bc4a570a6924fb342e428834755fc5a11c2d5c04d095f5d81340570bffa2a95cf4375586af19b5825110c58b469e838dff17f0498126456ee2ee682ab58151aadb818c5a2a9f866b97d2144d7d0f997f9180c56f1a6b528ffc0da309797647a1f60ca355d29d7412057a174909da9a0d43281112d315f7e41f382c5edb367117f54e38d21d93170234225d57d32de69441fb927aaf23fde2b79e519f197a4a2ff0e8b20bf9ce760c6a678df30dbc9ed7347d44c8e553940888663268726783a8485fde2809c7d466f0d5e0ba72b5491ef8ae9a5ebb1aec6758df9ffc582d4bb2c74d2f61111683114ac172dbcd5a7487ce7cfeec49dbd4e6c9360d93871fddcc21656534ef4f7058026663d71f47bcab0492a8fdc0f15b95fe281aefc83f649ab7ac53e3ed54470a6360fd6cc371ac1674e61356ab34a62998a05552ca6395de3484180036fc9981a70bd506478c3afd6e766bed4fc94bd747b9f1e34c28178b72a5a0a462cfaffed7c4951760c31fb7be72e9fa2061b871ff356e809a786367fb8bb1fcf9f90cc9914c58ced2f39b58c8453637f753e557c943db6eea7a7fa18d484d24351f375e1799c669368efecce0c1af8d8dd02f0b353c46768db5e09b11400c690d9c3fd702df34fec4841d407230744d7326d0bc25bf52654e63306aee07cb7412b04b939f01180619afee5ec1d41a7037faa3fb8c76491c9c2cf5f244b66df68f7e934ea7d505117e8bfcd03c80229abd2e982136bf54d72d3619276c368981ee8561d3347be836b059cdab9f89ac48155f9123b4f9def89e713345442ca802271cd86417c36d3a9dbe765b6fc2f2f233a329df0c0289385419394fa589b8bd01b41216161267d802f92618a30173dff0aea501af8d3e891106b1538282a3c27cf4f5bfe4bdf3691e5c39fc5443825cadf90616d169adcfa93d7cd9b291d9519598741b7e8a311f1fae38251a74c06363c97d8d2aadd13ac59cf407cb5bb6f551e43fffe82fdf82c19e3ffb765558f1a636aac8ff6e66521c6dc19dd7773527a5e1fd453a2693a0dd4b49123760b118489293ad1c76a5805c8ac9698fad26ba315d4e418fa122205e2e80cda748541a6c1959eeee559b4edc41abbb061821a207d2205be825b516fba9925c6e8af9eb21e8be21452f7d00d425acd07eddeb569c6574a51a08cf8198855cc48cb86a0120fb4ac5009cbd0883ca897e8e5adc2dca01a4bd0d83a8d3f828a0118b5fb266ebaa5462189dd9e965a54001bfc63be31a110813cd436c19676d8738bf0a6a19d449471fa94e01ca4632283e136961928443e37ab3c2369ca77b9c9287d017af21b10bc6c1f2b3779aec62434ef47423ffd4b98496a1138032968ccb54cbc384179b35a29ed1ebce6c9cf17c84f6bd8f77d1c3f7e291bf42e91535508e1b922403e827ed403f775ae45d3907c54f22a879dc9f5818318cd610c6643d594a46f4d5b6fc800a2a97d543198b298cedc97286c2453e74b574f6b338f745ac1c678ded403f176a9eff35213d430531b1fb24bc23b5bb4d50d5e88f12767f973cb067931d5726c5c282db75f2579802e5d8c0abbeb8d6d2def38efe3d62af2133f8fdf570275f60e4bba187b29864f89cd20267dcceca8c68a28f84672133ffda9c0c37382c194c64829a66e0edb4882377bee1beb28d56406acdfe001f70b1d9f33c6cb61b496d2685f9a60a053b6fbeeb1f4140d64d0e17a8c74c3b33b05541274f4638fc556b427f09a7eaab85fed5690e17c001155a9b70498226fd2462346d825f34794239b50718268de66427d7f2121a18843af46dbe0def3992c3d6af00052f7bb5e1ee662eff90bff25fe02ad5f14e883e8344bd6ef399718f61d434c53a1a7d9a3c8838347b5d3c1530c4cc29534a66a20cb018ae186622219bef3ad6c48af250f7dc2eab581089623680681328ff2f1146522e4d597946bcfcca8f045ec678d43d0fb66434f64989efd49fb0a79dc207f7f1d39ca6b100b03b5e633210a8059588bc8964840bb3b4a060f4faa198f12b2d18db4433f07d0c5145c6009eef34d0bbff963ca1c5a79fe995188f778f19ddb0230247a73234cba5e4d282bf9291cfe7a5004c42d8d0762ffb42160c286e49f0b19575fafdfff825e7d2c48a57e391e8803d8ce1292dd69c5450820a61170950553e04dacbe037f27d7b877ee4c066e6fae5e0991f4c3d4e010a60a2c1594d8544337c910d3452d441b0d691f86585fc17d78e92d3d07147a1e7a2ef4d4e9b52252fb5e5be1e222be71edca01067b95888047cc96a3e9bb35500735d8faf3509d5ed14c0c041c3edb1578c063c68af88e368822fa24380304a79ecdab978c7c637c54921927ebf5e6a8338dc26ddbf5352ef46620b72a0c2710c62b072d76fa6e518f5ff1d3b12cd53b20a298984197050f8b55e88026de10c0c3603454d2c265809f1144ae101115901126958179fcf16eaa41769831071eb39d3b36c8cbca5015a30ec1486e0ee40898e6b6254aec556dd1099930a7c0d85b5e12bf976fa176151acb1ca0d328ac659b685012702f29c690eee9c9ac0d92150b1176a613910999e2ea2dc7cc1d5d8f127c47a012a033e83f0da5e8a925a63b53ed5e49692dee7bf2d2307581f7b5c2922a5b69cb1726f5cb5204f491f2fc4bbb8990acb6b4430419f967adaad2f4ffaea7030c1123c6a817002dd11321452c1d66c5e4c4b0803901fbb7924cfb00fea1cabbe7454b98527a9d98b0f5bd8a8ebf3f36e52685a7431b8120887b7b671aa243a5920caa146304294a9ace20ed806b6da78b4e6d3fc2225cce2d134d3e3d436f2b52dbd1d305954e9064f0e886ecb7c9a88e6d8797ce2efb83f9516ee3c0ca207bcb1e0c0bd887f39aa4c44ede104c22c132ffa3a3607ebd1f40156f08e83f0054e9c46ec5c807ab812c5a7708de2ae115a1a2c20ce018a9442a045d3205a94ad1d9039d2014024c468fbb63873dea36329966a0f2f4e082b5d8e7e6b80ab9fee0a76c935af1fc7ee3d68d00863d81842ee247aad77105b6cc27f7cfc03c7169dcc6354bb8e33cce5279054d03ab2f83c6d94cd351224a00b1d9f3663234f46a78a7407eb11ec5521124be3d8513ce4214ada4a85b8017c72d4f16d9b94d541e277e1afbde4e93c3e7246bf324d1b7525037c8e70c7bc26f759ddc97449f15b5936c408c62e6c801048e232f7c4ce7277019c199c3e8c69b2c99d3fd0871782307cddb19664912e702e650d7d9414b56e70ef539fa665b654178d84c59bc80dfa4fb4f9828333bf3328707c1656b3c32c09c5b8bc9e88498efb448dc1b38d0b3f903be35dbe00cdc761061bbd13861bf5e70e9dbc2c3d243f80fdc204527cbbb0b257890b3dcc268e1675082a7c0da8b18245fe617887debf0628d309caec5dd753001a0ee5446af74f595692768afd13f59fc74d44ca2a0c48baf9ba3733ff2e4b4bd016abc24fda0835242cc69c884a0a4c91442540e53050e8a8357388a994383fb42a84a052e8e781eb2f88f07c4822dac9f08f2d5aafee072f93803255475f23c35f55149c141ee032cadced7065bc41260e500ac8a743c006d5c2fc83f1296e05866ca9cbff4c1cf06d8822cbf3f7056101f278f0241b5d088f487d8fb598a348376eca00b1d90bc688382ac2916548587c477009d9a59fc322d92c48e96d0d346679d7fc819233323fbc0e56b4a0a7a229c9f931d48e2c0cc131aa8212bb9823913914f8a5b4a0b719128404a99ec156ba6bcb5ae878ea100761b889c2541edafa0412b81f92cae542b1931cab071a4b1614a6a8e70358cc8b06bc03178cbb7d9009204a939cc8ebdb11c1e0cfa3ad463fef1483899f69f2d67de2cf7716b14ab837cade5a129720dc950b9c2af668b44677229d615209d0c5cd4b1c839574406b0c6597db63890a3cc553d547c6cee8fe1a5ad1f8093eb6aa92c78e85edd37bbf1e12a3d96c852e2db06b0c3fdcdc9c01bd372570059f61c61dde7779b3d70fff0585b20d299d3b1f87eab2bd55636dc28dbe45daee80cfa18f6308d966c81301f3991987cd84ff4012131e138b7d3f6fb8fe87008365b54d66980da82a8deb08a9934c4ad3e916358c188da5ecf1fc0544b91c75e20f89839464048ad29fb438830fadd7cdccffbf6ddbf5c6e7549fd4f3e5eacb2f2ee93300fb8e979b6e8801e9dc16165c9389bef693169746e1531865af949a4a1b5db889fc789cd40f28224c54f5c2e8a15cc2e7633df1e2b5cb32cc2bd26efb45c5008b2fc280a6a31fabbd4a427f67ef9931a819e3e4bde048ee049254a7af6356a0158388826d3a3348ba83f12ac49e83b1595ec3ad5e3da4257b43129fd0c9731c64fe9fccd9e82bf52e078dc171eec8d2ef13b3ee5f3f9cae7fb94cef72bbdef29bbbe87728d0c0042ee912f4156b5fa616ef4c4eff82abdcf513ebfaf7cfea7e47c5ff9fb9f92eb7be5c77fcadfe79eb23f107f8852dec169f3110b53d7fee06c7b6a270d27a0bdabdebf6f98ad8108319fb95da3483a9532572f6b231a3bdaa4a86012dfaeb41326ccb74152a657f1197828dbee470df8e69db07cd1206dc5ddd65a4f9611f0ba50704c7caef9510d01810c95a4226a0f86c06970170a0afa3533b1ca475df3257260d995edf12053c0aa3bab12155d09dd98283330517100ddbc4097ff18a05bbd03bf8376ad359b1135d1edd40a55272cf7d94988e050b2e03fe66dec32a5d66b02de2420e9566ed23447b5333891e2f4c668f96de1059a12411377244fa94125011bd7ea2c33d43f555fe6ae7c6d351d87a9d7b871724649314d14dae506681a6061c9aedaf7fe02280a4e2cc5bf210d3d0b06246895e3d1363f097c336a1c4b22807599858637985700e8e7c117224cf235d5d478fe217b9e295b42ef090b0e2b77f07b2c31dec11c84de0d796612c4c07c42f69cb1c90bf2e8b02c545ae8ee128fd0d62b2c6b5f6ceba1459131dfd03c00bc1a8215e4d69e658d360e7f0941f81d6d761fab6224cc3865d2d8ada475bfe19df20c454d57ea796da622b87ec9433b5a29eb1a873130e486033a6286795a30ceba4a079a17ed98da12247a787b85b70dd10724d0a4d1a462a380992b0d76e048b60cab0f81caaae3e8222b8859a9c0700800846110447d77fe3806502ddd51b013b8a67aa8aaf62d031daee07af9c9de7b6fb9b79429c914a7078a072d075ff4764e977e7dd2078b920916824c9104526b4afbf4f66e98ca98941155ba28a1c34bd932cc195bfcb4d80449dfd20a0f443f2dc9ad23b7e0c2e537894bd24f04812533777377ea4bfd9054672984594a7971e8cfd9179ef048d82fccc313fefa7a64589be5d7231ff34ea1bf137a8aa83645d8a5b34dfbb2f7fbc4124cdf180c6ab8503d3a29a8c0ac900b69ef9c10bfb0d6d5ca1d047574168ae7439d1472e881a250ab13866ab850504bf8c04950f1fc45ad292a052931f9e8b2aa5e497cf6c81f56458d4418a0d74c93cac320bb8edf7c5c20d3b7d863e2a6a9514708e105355c262d0ce147080b84c83e46010627c0a59ae050d490dbe3189c0066c61022dc51da7fda0f53d41294d21392d30f12901211922b814b3a45daa374b57221dd4f354f090797f75c7f7dabaa828a55d1bd09cc9a579ab60aa72b5f9e7d29c0acead129e98a98a2a42a4df8c9b3e49865b35f58b10ff78414d073f5a7abe2b486f1310b308a26bc482124851829b8f2bd5300a816f3764322cb4085acc86c98e08a2cc69c28849e37345a4a29a56cc92c1e4a62e450473101d4ea4b071b0c3dd1a260d8a0cc1605c39c73cea1213a0445c2e00409b678511d1e29e3755d2e7bd64dcef951c902338f11e79c733a0771190c50d21ac605df2ec7782205dfc1d39f1758041048001484124d2ba062a59d2c410433f315467255d47cc8230b1adf2e97207262e83bc909167c273d914483208f3123e8620c2b9868ead26aa0259ae8ee6e9e0f3924329e5dd22c569efdf2d9418834d6c8810c2c579ab091a5933e230e15cf4e9192b0c4e0d9650d41784e092f5c77b764d60658989e9ddd4340020dad77642b9e8083678e08ec4a1a4ede06202c2e3479c2e244861732c292830e58a6047d0d47aba3f99ba44568486785f135af6bf696aafd262d81c60f4baca105e8e84a12950f5d87d7a853d4e824f446a99002fbb38338bb338207cfbefbc1731733490736877d84def1400d5d3c892d3ca7b0392c3fba3b91fbcab63c7c4b1d51e7af4fa00a512f8fad662e43923ee49f9fcc85c18e518d4343bdbef8991077e24e64f1883bd917c970fc82be7de30efdda5347d4fe25f24dd89c7612afd4e96c346777f7f48c7ea8181db50319191d31cb84cde91b28e24f7f4be21b6d4e7b1429450c56236a187fe20fb3da2310b3fcdb3906ae54ea9025f208a512a3da673ba5a15db68731eadbe32e0d2f693080f94012d513305e7ca04b4440a9d0ad243cdf60b2a8e13e41a950676c923321d188e968b548d18899993d53a3ae886211e581c6220612d27dcd581445d064546e89d26aa4f3ab138d7aa752da46341353c368840df14e0c621ca01e82f27d91e19d1ec6515a62727afa714549242e0bad2ba4d58840f0d1430dca7316c5b3674ab387b9c312adea864ddfedd38a3a06999f666572c8cfaa2db21283d0ac0f96455ad13fbf94d3fd67fc342524a4242422dce5b3da98448dc32eebf48b7e21ad983356975e7af1a98caf3188085c54b03ef645e0d2c5e6ec9452f4f2faa427b525ca05cf92f8fd404444e1464528cc8acf1e7fd8e38ddf2782288aad097077110ce2a565344c84e3a28633ee4e193521a128b424c6e8d1e78724c6c8f243cd885d33625d2c70d2e505ff9c2caa21129a403c81e60c319c428420e610a41d88253f11b8342db841fa13818bd3fad8ff1c7dd1ac04ea9a6b9f0a9a53f769f9b09712a386b4d5be32e0010c3006895332187d84c5898e41f144899fdf56f931eb08e3c54fabb94720e148ca473f12697e629215281f7d63b742282b563e5ca8135cf1e19259c06c30e9a3c785e2a347ef115d8418a4fdc783f04d94849cf8992844b36cf952c4d7fc6ef922f4dacfffd2badd2ad21856ef1bfbb722a2ca6f089b1266ca942953a6353d5c792bb430fd721530bf3e9b085c4e9d7e2b605fed29362dee8ecc214716db02767397d9e4b2a22fb540b44019cf7e81ddd152f0e1aaf1ec210d071687fdf27a36879958fa85e2378909a0df6217bf139caa12176631f1d33650393aaa72148f3e3ec5a30f823e00fa40e83f40fa00e803210e6221167a4662a11e820c05110a12244810a20f12442808910f11e8490413c13c0f4530114c0f5d5027d4cdd0097542df2175429d500fd9970c8b8c29637a0d2863ea410e49212525998392d2cb2829a4a414a9442349248924d14ba5684424897cd82465268b397b927b63c730ce322cc3bad9f38ed14ac7d9dd53ca9976e9492ee8392bcfcc0ee8eee6ab876d7950211a8814c16ca9868b0422459ecde9ef83f5d1de1fd838ed21839b93755143708703c362d0e8956c22c982c998b22f19161953c634b4719daf6237a49394eb76c1cd3b8df3329a6d5ab669d9365dcb2826b5d8666cf2fac2242a55ca57c66e48cad97b30283bfec0a21a7aaa4bf9163b22b163ee88c4e794a779e76dde0b686eebcc85708ccf69fc65dce52551c9263629a5d44dc52c6e15995bb1c2d84df8c6206154ec9748a415efc89f9f9f9fa29604235b2b7949fc96dd10b940b699bae63b8c4a7f8904c105526473dadb7176870111a75769b505cd76f9067f84cde985ea8592bddd51a6429f966330df9cf531255d6c772986794a9a52cc993233e674550c3a0f06416e4b5d5fef30ab2384903214032665c448a2356170c11617309cdf242b2cf8d46fd2095ef8b0fa0f4569dcd058fa0947058fa8b4baa2c50940e0dceaa7af1a3f5df24efc99ae0a4954cf39c5f145813a39fcf272f80da21a6e540a3333333774a8cb6075956783c3557ec33bcf0687772ea7951a6ed496c26683a3e99d6d4de76abc65b0c67778a1cfeff0d80dd9e15b1bd1e19a13d1e19a0a4f7df342ffcdbb6d7c3bda3dfc363535be79427a87c7c3e0741d2030888109306be5d32950011d3a7ce5853ebfbae1d443425d876fcd514f87afb4229a6f4e44fb3e9fcf6a7c3b22493ea48e25f9f67ae6d77c06883e74bc0e17d22b6f0406757c2430387378a16ac990ce8da8fcc627db861715aeba196f89fc14f54b8cd2ef97dfa43068fcc65c479d04c6baa8f24ba59f0e88d5006634a346fbbc4fe9b79a937a9619d11c73221a0a8eb9cfbbe6b1d38edd10ea0cce8d4a4105a0181485f6336f1fbb965421d85ac3302965c4a254b218b62d8d0c7593c280f9f07564632354252c85ded13cf3f607ccefaea1d8d71836b7c6506d967e7e5a36d8776473cddb8a2f5d927e5ae12ad51148385282a7c3832dd570957a46882ae5eabb9579dbc2a2a861e5992fb6502ec5c9766700e00ac8b70136a77d6969a19ed832df3cbcb1a1beff92e55f36d7f6e04d67a3a23a0f76a6eacf831df3d25bf4172e928e1cc1b63c5261d4f91b73f4ad549fb1893a3fae87d08863cc14b53d7ef9334665b0dd1863ec346ad49921d82db7bb7957763783717773b031a494524ab9524a2925ed28a5e48c6a6a4c8dbe4c63438d21cf4c15354a29a59c317072c6755d9793339c384a94dc0b7a5543b3768ee2581c5eed4e54f2e7e2431ccfcea4e4459db339ca149595c4218c961fc24cd1210c943020e8c1417f1e6014a0c61932b98c163c790b96885c0826075ba0e43cc660fad075e69815ad2bb992ab95ebf0e8441e1941a9a3e3c40c33335319f3bc7a66660e554bdf4f3c7c3b0056dd125b68f62b801822f8768f9a6005358e45185cbe9d834af35c6586e71dcfccccbc41013ee4c20801185bbe5d83ba049377d30d91208011039824bedda61e31e110c382307e07e52553c58894181410a066c5eceb459923199c10030325a316df7034c4cccccc021b707454e9d2e500bf68c1b7e3e886f44a091d54dd1019c400df5f1ce1851adf7ec306062ee8020594266364b02109a1ca1d51bb19bc20a28b8d216a74e71139d58d1a0978765a9d9878f69517373c3b8db3b0454a14348a7a3003a906249868aa1702ac145d28f1ec3360f4208513615c2082c916424a34916353365e7543002dba00e2d9ebca419d15283d7bc783435217647878010620a6a0e1680633ddddcd71e16509f0610f0f9acc5081142bf0e1d9b7e9cab36f3a64b4b00230a06c21850c5a3d43953456dddd1a93cd0d17ab0f7fa8400b325b7cd9c20c2e52f0ec3a4d4268f1c3b3673ccccc5fd4303333dd2d4cdf8e3133b3ea9999992f1e26caec6384166518910652143d74d1e296e199999919480a3ee4be553fb25062ba218ba1efd5561c1324753154df408bed0a143cbb64a7363c6361f4ec9e859367671067893138a0c26c962e48cf5d78f9e1d957343cbb835fd22840944aa9a604f6c35386a5860b0827ba1434a1302b9a7c90ad22a4cb8a313c744b0446b3cb0d4d58b502424a0b0823c6d90f69edb2c3502b88a52e37b47e301a434a556714c160c2e90926a27a4081132b2800c28a151044a86a64196c1a75220eecb2a5c438582ea0821a2ac98aa1242b86428921c13c760d900c7ed1483be6d243d2547e3645a24b27125dd22f6c47c1e7314f7a7bdb0c720e5289c82d77f51bea5c47c02360af560b729291267712a00fb1586c4104831b96420003171d4485c9169b3e54dd80f0210f2a90602ab078ae2289e7a56d8a122f5cf5ca40981dc4703203142f5ad0ba6248e2b26549cac28a5361f43d74aa8ac76f12154153a4f1617549c6360514ab07a5d4318a61d431ea48302aa9378d74a32f8314c324b738dc5ce4dc952ba594f3baae29a1dcb071c718a58cbb4b7b803cfc866319f905b556963eac7c70f0c1cb0c45148da004472d95184b528c2fd7755d57d21446244d01c307e03789cb174d644b4d66bb6951ce6e488c9e7d51521a23a591b2804d4abd18a5f4cb9b31d218e3754d193fc6de29e594bdc5469915a30e8f8398d3dd997526d6ebab110c2beecae5dd9d5f7b4eb7676eec8dbb1b7dc60ffc9c92a76cbbbbbbbb71f7851a77bb9b36c61877e76ff801ef393f2e4b32e8fbd9fcfc267637eeee6edcdd7da269127597ceec16661f29834eeceec4664fd991e01366925de0aba38c335e916758866d65e571f4d520666420cc1cd78ca55f987df4db39a992377271d6657bfb4ed9e204296a1da38ca1b237c7d8fd3127d5f59622f1e3c39ef9589669ee9365cec2afd3eb9a8e7d1b7d649701a28fcbb199c2efcc7c7a88c1a904cbbca77e797345b9da3c5e7ef3862d82bee265e61f0c367fab0383242a25956a83ac87d2a3947e451f41e4e49f46e2239932fad8287b7ad9e62cdd1c2f7a38321cd74e3d62f2e9271dfbb6a737ca97524ac961a40c933c974695517292ce9944f558c47aa4055199bd9d32fbfc912b25c73278c5cbdc2ddb65f7b52c76297b0269414c6666665ece82030e98c5c5189d97be05398ee36c9047947730a10662266649c78ec87c216644312962e81762458c71133333b3cf23325288524822109ab2c510a151d40961489c261499851921ac8a2fecfd4230b69471621c608fa0ba4969fc7cb882fa90ae6ec01e36472edf2f817f5a99d7dec1fc72ea3169c2d3ba1c88b6b2eca37ef5090710e3f71bc246ba0a8bd4eac0e51dc0244b36bb4b9736fb1dc15c7edb38b30a4789f9283034d44d1ab39434e6e7d9f7c728edc4960c969efda604fe69613d04f65d2ea42f6fee22e9e1971f0afdeb377e3b231dfc7e437440b264735fbcbb65cab4ae214268b5cba6cd977779097f97291376778f912d53e6777bf8f7578b11d1f7f40ae09ab2fb8b29d4376e8bdce64dd9b8dc376934c0516fc9e2ccd91e8bd097ad8b9b7239f59614913fdb8571d4b37940e79c9bd07d9c9bb0375254251b95f93ebd4fcba7c5be794c36af4cb49b2b55c9467d4ffff46ded7a707958e3027cbe5b9bb72dcd0b57cf535e9d91036c584bbfb8d949a79b370505be7a76b7ec52d987023f4629365510059b8e794ba6cb12b22f2e8e2c42bd2952ca28a5c43c1380a628e179243dfdd4c1f76965be4d26993799a284e77bda83e94cc4e759b2f0d2c88dbf7cc3b8d839a063d7535060acbbe94cd10f056621fa602c49c02b9f48f9e94df9a28cb98fc831d89d03549073859746a62b3d21fa769fef8e88cfcbae018c51cc378a518f31c6103dc65864cef82db97c4c2ed2df2e250a7cc54e613e5d62599fdbc64cafbee4d579d7cdcb27cbb6ac2553e7cffcea86b06f9b4bce85f0b6b1b07da1cf6bceac6531f3d96210839237cf3ccdf7af2bc964e6c974a98b9c185d98bb58e08a2b78f264e6e6e666962cb999254b6e66c9929b59b264ee3002d1299ec8752f73ce39e79c73ce39e79c73ce39e79c73ce39e79c73ce39a95c27622afd8c9a4abb249bb68d73ce39efee6eda4dd9d37cbda65a6354d3b6204debac334a31acbbb56e8db6532fe33009a77a29bf30022d985de262d7c4fc854956425c768a08c4befcf14e31cb78f172c5153116c156ac7e9993acc4fc7a64301460ffc234c2fed0d9c00534fd650e7bb06fe7fd98ad28e2a76f22f974f1133f79b4a23d4975f40b9370f189b6bdf6755a374413c25a8a26f5e4c9f684e3aa4b4d4b79044aa568b7d14ca7dd179f601d912ee5f1490cb29fc2ba21a92d729d57196c1583ab19aff3946f5737242584535efc2e05cedbdfb2f904e84990952f6a0c0a0aea99786d4197c8054d18b755ef24d1bb30629d03b68f5facb246d568ea863aa17c214cb9ed8a741ff57cc6354fd2788c34a9ea9cdbb0517da3f9c2787546b4af5facd211a98ef9b6b1caec86c42d729587075e7ead51a5b3a0f49a0be92ed409e5cf68e52856893e58b2a8c420474f8f79d18b1a8fbed02776462b59dbc91973c6e8e40c39844ee496bb37abe5a8aa554a19394a65635c3b0f97bb7465f3eec64c6691b4b1c476ca66d6819999a5d08298524a2977836e0e9e3b76939e7377775bd8a44f1923effc760a29e5eeeeb619bcccccdcccccacc52665738c5d3672749ab3a5f00805c7d1059b7415e3b06ffcea1645070c66896b851432456810891162fe61244060c5cfe64811430424bfcc887b85262030426230323ff3c39638e0f6e69fcb8526116c3dbde3ee789391598811708cd31db10b8c1aaec0e9ac80892d9021744e219897bba45c975e92cb282e2bed98572a3be7ec5ced18246d3647f2dd40516d8a68deeef3d10bb99ea79b47334e722b198735662cb244c752c3aa39e6600f9e1b902b3fcd3929a5d4fb72e9f3d2eb997f7d57f4d172bbdec8916333b1c5dfd5e6f21732d3f317b2d3f374a276ebf40ee7f2552becd2bc9b5d90bd71a4cb0df354abe9f3ec71e6f9fce5f5cca79f90c6245d90a5945386cd03d3a8e3ee76fb6e77f7eea7c445e90c7b9acedd5dba12cc6297a45e1881e293e863fae552fa3c989dbb2be76c597dd8284fb7941e992b067db4727dd7855dd775b1cf39e575496f4e223a2738187897b4e212b708f1257eac9d030d548dbe34bb1dbbc3e8ded9ed2f94dedef63aaf0759ecec48b9debed95cee3101a22dea99631e106d61bed433af094f0b9b36d77704f3fd126a8b5ed7fc86a02ea4298d13ce6479e5178a26803efa364333469dbf503411b4a018ca60099c233302bc44a40d8a8cfc4d3a52e32362d39068c688e1a36f6ad38298712ba3976988dbdcd404068c98ee5e6a2762af1131c6488233628c118933aa8811887562d0d9f9c13a35e87c69a2ae7299e1a48b326078322e32c63cf79b440619ec658b930d8aeeee8dded77657237c90e10518621ce28c187138e32746252ac7bdaa2a190c59f8175620449b8a9a8c9a58908d2180674fc92691b1e587802184964d0430fad5d6b6c4c5d26ef8e300cfde269131f4db2da0fefcfcfceec536d7b5ba69baf1d347288177544f68c1e527e5f979196961c6878b24f4c416229891270e3f873e5415e95d2729c5b63c361bcf9c7ad98d534a5d082f7779c6cd289bcea5ab7ce238a27265aabf128e11c860f238bf4962983e859ede47bf111d47dc1c137a876fdfc091e3068e1c33335e5353934acdccf826a46b3c1a9ff1aaebf036ea8833333a6e7821cd513d1078a206de8d57a7541beadb383b3efa1d31a171a653a071a6cd1706344fdf1e7a45443a78fe36252b35ba57a4460d699e689e9825c5d040312b8a5992868c8d1f1b406b238859546888595098456d14d98862a30866d9c8cf337a8f0a83ed15e145f1a478450cb6cf7444b017c00d951a5f4a4813d01dcd130f35cc01a514d23c7d6f1a54e328318bb5a88f4adfdbe615056049011b7c0c41c8e7e89153001f3a0418000ed88107063880007e2080b5c313001e1f000048101012f0292004217f4d9935c7d536ae4be9c8c19e03ea3b07540e34c87c7b8e2866adbe5d47918e2278872594830cb3564b1334be3d1d4142df397ca883e81b49cb0b22f85047946f1d3fdf7e4de915795198d53e7c0b60e9064723acf0322773dddd577477fdc21b249899c36e2634a7e70e76dc24b149f9e81a5293605937160462593bae67d78a34a3dec15ce9c68ea3e72a38c7b3efe889bf2131185af9e8461f1d939f8d0430bf3c0214f3cbe9a73d7df44f73fae81a53efac807d3625f8e5d8a731354ef4b4a5c69973cea21965fbddb9f901e28b36ad4ca4a934bf4cd91a13b3426de99b98d550b425a1deb1290153c12fdf16e63eade8db99807d9bd0b73929b12d8f9b5f6c1a2d101cbf3e8fae9f05c2c4c9eed059c3efdc0188d7f9ecd3903647cbb2382da503203e9c473b5cb3869f4ce6d1918d13955545aaa879340375798d940d4915757d610d09c6f870ab91b239ed335115a926a98a16c82a7bda9c76a3233903c5ac700b67e6110e2066853a3368cca369e5872a4a45867762eba87d43daf133c3d89647d801d4f7f56944dc75d8c530cbf2e10c94eff6f0464acbb74b4f47d4e614f14be487379466a0dc5062b0ddc689ca1ede50baf9ed13da9c769b2c35fc9ebe574c1d102d90586334bf27a6ef0e88767851d9430dbe270d9e36305a20dcb76f40e5138a3546cc0a575fbe2866853546cfc4acf07bd2008a59e18aa986caf7f489593569e0a40198ef4903a76f5f7d997f436983ef00aa8c6d79d834a9458dd318510d69ec0102b322576f1cbca28b40c2d29b22176a38b9e78b2f7addfc50c35954847483430d67d154e272396bbaa4328f988594c42c5e2213160887e3dbeb0456bf432562563883269110b3426a44b91bdf3e8ba2349d41f39790e0ba0502816d4da41aeabce6f435d7d134cd7f30a8f1e8302879b8aa569ba649d7b4ef4797aaf399c772d35c6e13cca49bcf6b5e485f0b356c4ea72dd9bb27b430014a42280929fb8100d4a45b2e60ad291bf80172a165841617a044bf9cfe5eecb1bbfaf2ab277f48f892d9513f92fe1863e434e42f33333737c3103d885f97b515f04208bc30e124071310d1ea1e90c0b2310d400b3e3cabf1ec71a505045f3c6f99917288ab3bfa45549b6e3b43d517110bea616035b03a0b44821e6c109e1eae0c966210fe3c581fde1e5e5d7c73341a7e9870941b28aa212e6881c4cd895d46cb1828818c139592acf050d129e28ff44c8114137ae7f276cc318fa7c12b0b9d621753ec9a58bd3e6f1c3235dca7f05aba7e2ea08be85aba98c00e7d9c26b350b81e495d01ec8e23f9c418427d1b77e3c6dd2bce3955577bf494741929d74fe09dda727f7e886214ba225064c260139b65b37c96678fb2bb63645ea46f10d4d1e1e18948989979126dcdedc68db55d994d6666c97c5dd8dca572b7bb05979b99b9b5dd652fa4bb5c489fdbb4ac4b652ea0204a149542892ee0018718aa8d5f2d3cb840c69830671c51b55f2d3b88e1d405af51e9af961db2602715fbd5b2c3d20e4a444cd28a186a7cf1a2060bb8a861c61435f59bb4460b57eaf69bb48650d31a3d3ca843cb5f2d39cc400cd531a2e12886238aa4d0a1c80b273f2db0e19390a22588cc0daa959153e32ca5018747c2498b0c5166f82396861069d1e1474b0e6574ca88d1a20313967dc08601817dc0b5c27101948c521abaa2b06447605a506901a2cc4a0b1062fc8d5f2d407cf1a24cd36fd21a509f82dfa43582f82a8da05efa6a4102e939b0fa30859f9f9fef597dc8fd76bfedb193987cccb16fbfab3f24edfc5b847f1ef187a4bd1f8994acc568e937505f09071f3d74293eaf9e2d9b07120c615c0fd62981c64e1aada2a89b65e60b4177f979b03bd2a93d10188c4e5de7d439751eae36a773212c8d7a87417766bf564a4cba9c73767727659cccbe507ed77599fb7429df244d492a697f8ace7cb6c5ce48ea3b2f32f39d8a3f5c81bf487fcafbe36ffdd1ebba6f199c538300ac3c13621cf62cfb4c665e16829a39fd56d34267faae738e776e78e7aad90de9a07ecba86cb323225f765fe8f39d77d7f9d50de9d488f2d187dfa419a0f1b3ebbe95cb51a6420f7c9675528e8eaacb94cba3974e33ef6490f28ce83397633297693c7d7de99b065cb7e9e06a8c2a8e1b3974dca0a99587f47aacb81a23ea3ee65b6a46c94cca3b1adf979e793399a7ba22326b0e2911b39050afcece420bc4860391ae64867a36bcf8335d03a4d7ea524aaf2e846d78fb1f8d573d24d4488a7ef2db7670639ef350c5503c1e621c46a34666079339bb1ca36bc00ee95d369f53d715e93cf34d7a2bef26bbbc03210ab34229a5300b89f4ccb9eb8cc41048f95403529e79273bcfa451e3a01a74e6bdb32d5a57bc399911553ae6947ae7586764e6af1980f90df39bc4d518d5303e929ef8483aef1cf3cde36a8ea8988735aef67d33a562b0e3e654b8c2d34f7a1736550c76be711c9793a9ee5b0633a690858e7c48bf49c35e85ce18a7bf09a14ba824ce562539090dcd0800009000e314000018100a09840281382c1ed465c17c14800c778c3e7a583498c7b2188761100541c8186388318618600891191a9a220400ec21f0b2476929381f6babed1c109f0adbffc4101366501c27f194756892c6691ff69d606ca20d2dc2389aca3542b0338b61420e859fdd42a4f08609ff460341b4c6c38e324d3f0d428912ecef54fff7c852aaef7fe841a7031d31296af7feabcd23411ccad62e334128e3dfeee18cba02318469c8875e75e57371cf7bc16eb83fbd1be0f8633306758c087667b1101546fa0c4a87e50de3670f71f06a8f6fc9e13f9185b5c8625732e4afd823af0355135f79868588fd7c5671b3f79b4a28e932b42ab1de71e15582addd077f845f6317a023bbbe22d3bccf95387e48d52fdb10fb133275011ea85e684486267c3d319649e48b8880285e27e788b271255c58b362f0252e55059fd429217963abf9ab01287b338634668d3eec5e613eb3795f99b7b859629cd99dc66057b1dd8512af01c68e41dd2e4692ee23e88a109e7bdc04f208574a5efa0c0b9bf80094145c2108f6c1eedac45d8546f18c31b0c13c0f3effb364c7753eb007e60e63d9a6b87557500949537050cbe3e3140087f7d5e03143ea899278d76529b1f01a4f26564a101f82518e83d0f233826ef96bc61488c4ad93e81d25f7f7432866d337366b6e38f3aac4bd1ab7c3d2702e946ee4765e56169f6596baae0dec988025a1f7ffea9313002df29ac6c6896f53922722bb686c9c0b7e67091e07593d9a06280b7d411999e4892c6a4d00b2058801cf2d6f3117cd80453052c8b264711940ec088e5dc562cbebd00ba68e57139db50f309b6f08fc2507395e093e382c298aa41a0eb6930b1e3b55fff87486b92877088834334d3e26229e85d0018da9dafbe17872c44881ad970d302204f433c29757ecf4d8813229910018ef4223c573e62ebc28b4d01d2cea843df87ae07c1f82f34e73c2fbf0b4bea72d1c30784f041c6e06cf5ed61a8bbf6e81a79dea865be74b717be95fd9833e23245da1ed4c8b02d1087381d4a510d8f79799aeae9e51f2405d8db61ea70faaac4dcf287c7677e95146442590ee9f9acfa8432a0f71042ff0bbbdb3382f8a8e99e726d6023c4e6c398d7381d78f1a25e82672517e49eef4a95979d372de5cf9572554f56c1b2fdebd6bc28e4698022db3258a120035eabc728041a396406070031192c063404fb03c88b32dc8a14a672f63cca8bb0f1ed4c68570b6e587bd3c0c08ca081166336c85163c09826d1faac111b587ec5d184cf81c364775a915a7a5da96559750fd0bd71770c28184faa26b4789b84d0082cc16be2a0e81173b8702b5e0f739eb2f35cd26ae838438dbc3c4f55a1293aab378cb590c5bb7b6402ddd3dd096039d0fd47cbfd6bfd7b12936b5835d9f6606c3752f0c0e9e47d71cc8f15cc60588957b709e8981944f27368471ae56a796ce07173c008eb50788ec43e9c3dcc8da83bdb025c7adb2b714276069a2468be34f01b49988adbaefde025f392c8136338e2490bb8dd2300a5c1899357285ed4243b0bd6381086e4e1241cd833a74fdd36eedbbf056c1ef75c38e0cbd3ac0a3501e1e64f82f316d1ca8bf1f145c6fce7b44c14f7f100ff5338edcc0c6bb765cf35e75e34202b0f15e6aa943efa000ada30b4e45576f22e5dba58fbda7f9e94491fd8b71a36c8e76cd23e8a15c8b3f716c8a3aba6d1af6c8bc08250389e07ba6aac10162e888f1b994b57fb87f97a250fcce0d8f2dc303fbda70c54f509450f767aaed0d44703355aaba20d94cdbf06ba347528578a37b26d5a4b4a4d8e6844c19491ffd9837662198963746fd68407a467aab76b36cfe1fb1a88dbe9d85cd07c90df5004d12ac6d3b354430e62d2a575f8409235b60b857d7ab25ef653057aa5bc3a9b9a642978993da99d37109d44e114a20800f0b9d8ff2f9374f32a1e0f85391cce6d03bc46fb822c30758c33dae4b95f007d99c6baccbc7a4668c9d3b20d838489e6056682ac01c23db3c585b0ad4fe8c1af76bcb8edb74684a25f16a4298a96a9b8a2f4ca37f31a17ba5c322a9c992cc802d85bc719d526c45638c22b98c08260f9dbd31c4623d0688216795a34b4351db03c7b8754ec0683454251d830fa70ac23ed0adf17979b37ca85a13069ea3f6495d11cd20f2a72d47a2c4a6aaa7926aa31db48cc862a1c7403d77f5f16332aafad1a1e6a37f1cf456c3fd26f23b29755681b7a7d4e24f1124a1445284da7f88047ce46852fc06e8b4da8d1201c194c80e23d5d0a7add95a626840059343a8b156f035c44858c1f0ec1b5ca5915b9165e9ec0add8e456579627e2fa4807369807e8dc4b00a46c522e222e40a6d6d01e3e324d598e2d0068a5ca38e0435ba33a7fd767719d41b623a516e17ffcf558b7f051513d6370c498fc70b2bd22017af2b50ee27f42d16b0e3f1a8db201522804365f49643233af101903f573b927ed207cd4295fcb7f9f85a2972c6fa9cb97f870880efcfb9b9e02fd40e3a000b03d914d9ab77f9118a26e8db18aabe98f3e5f2a3d72241892e76f49f110c1627e71a637c401150848fc832f6216b0fa84a3f96291029829a03e95f95a4090f23a8fa3154acd8ef0b074521fe2cdf72a317e9411be09d358f662da682d0e16a6e58919d560b8471ff27061528ff74a47dc3f9000890d5f46e928d2afeb59587a3280cf52be721ab5e0aafd3e27b7abfb9034878c07c166d06f049a1c1e0498c0be8681675e0502b25f4619af9887e32805ac721d950228eff9507beda6a4b8c783bb96471ff251aaf74992a4d8f9bd2bc048206a542cde9b1d502c3f7cb0713b7086e6fa4337a4dbe25dcc79b5e8e77d683f75ecc1e893aa91e091818a9f714ecf5d1a3dc168395fad560cc8822edb39f420363b644b027e94d90f771cb96c520cba45c8f71453038260f6260637b0809ce69674cb45cd9632f7ccb3b9b0128907b41d375c7cf74114859984668caf12e2eb67563de1c8ac28b201966c29d171e96167407bbc2175705aafacac2e05f5cb87c5b0aa0ea99928d9918c99862a45a01575bba30731b7c9fb2d1b1de650623225f722d5ae543794c47310c20f77164864b3dad82192ef3fccbb5f59e955c78cc3016d2c5e242a172a91909995938b70e79021410cab93497833aaabfed25e03f91c91fb3c8c12daf8877c464d1679ca5478bcd5315d481b05ea31fdab32f58d19c711b6cb17e7a1e2922f5010d2a43c4844a463c1839a6deead13a8826877ec4f0507a2003c53ea0194eb2a4a9d8ac46c459e4756ce1d3b23f064ca2871263540310136d641dd8ca4520891c1713104f896a111b85ded1539c2b65a98d93cd8fd119f44a8b71946cd8944d4a1052e2404ebf6fc4009148a371fbdd007453f0503ad6daf6cade826cdf8f3b2dbb75f8068614818dc949f97c85a3ae3ffb0c2ca3197350805cb552ce6ff6e81db1602a5d056c81bd215881fd5a6bcd7c59b978ee8133929d27438d3f5c38f5494ab6106a71276d5f3c5262b015e7e796c65877f2a3a18646e9bf8908f774b03af5711c27f9272e9cfa14cb282c0ce8682b7bea3e027e464c25700270d5b7561efbd594b9eba7a4bc6b3004fd99ed94dc56d0d36f9c56cba8f458f4d63f54268a793d9e82a900e083a4f95effdc6b78a8255c384f54c35d92286b2bd3ecbd8ae2c2e8956a4627a00c0c2060049837a3e8816805e68d2aedc01fac0154e5373c9a83225b21be5452a69efd06889614a427f1c27f5ecb70db79aabdc2efc885214005fdfb413a6db8a90c72ae1c126c4ca8f91ee00e9430acb97b400709c21e0f1636ba032bf09db720fdabf4ca4d3195c54a301e961cb3975d8cd4708cfca570a97d30f2858a59160e23bf8c255fee8b3a4b29b70a16f29a2c01fe0b97b0804b061cd3a655211f9de06f328699d72796e5bf940f813339ae2c7eaea859c47e920c7d7f5e5f4ce644d56f2a5c86a7d3e73772921b422f91c9c64ce8543f6e85d09c9c98ddf8297ed892ccd5c9847ac26398f0e0317b6c39b94f37ea2a347f987226d29fdc3ea0f76f87f095994773fae0261e55af56207d346dd8c3ab3a0dfe90459558cbbff8f86b5ceb076dd6a8ff69b31948aa8122e2d8daeab2fbf8f4ff3ecdb1e70e8b2243dde82d1a5210a3809d799eba23d5637bdb793b4550f53c905221e8d442e68e34281419b5dbfe246d21487cfd971b3fd0b33936d575d5ed3d7b1787dacabe7d9bd91801009e96542687a5a3a31f95f664f795a9126bd42b1630d1487608433f7aeedb26e84418929bad2a75bb6a0d0af4204ffb5f671d0dfb48a1c959a71371f433bb2c79977b65557a70f17450fc82e0e9cc21e5d99c35b1c53ed200208c7770468e52c1a6aeb4ca3063e270a8dd2d161a404a873f201e46d4900c60d37a9cff1aca9ec91e98708ea5fd6d950f611d7e486c9df10cd20176459511efb90fabadcb3cf481920e704589ab4887df46d2811afb0bce78c24200b8ae02009a21ada81dc3400b27655ae3cffab904b45d78f93bbabd14a9f13ff741a0aa26ed4549d6fce55319b5bece9fcac7a91e37fc722262a1c065086f053d207af47b87cb79e068b000e8d9515ed13c32af4ee46485686a48bfdc1f5405b131c42d70628bb46259aa8faec3629fe3df969cc16801d635e82fd15c6f9a764765449755144a16e974013cda765f2592326dffa2387e19765a41f9d3e52233c9886c5b2f3bca933b2ab3af151450a3946a5524d586850ce838b743f8c3a245e78d37e44f71f901dcc79e300ec3d07c34b0f5c9930d7105d5d49282f03333ae5da1efc77ffd118a56cb1ded80b2555a2f3b08b67cd5abd7e83dda1c6c026c7bff21ee2c0f9807e7ede8ff3f910a18e920cca0c0c651f7142f904e260d3b321d41d74c0a781a5dd0400da40350c3c04bdb8b32305ddc23eab5a46b42432c2f025bd1739e1f2aa9cc9e2dcd700ec3a72ec469b6ce4d58e8a46cf60a4dad66576e288d3f9c38a0fc7a31c1143c64664a8efc98e22dcb2c2640e89defbd3c3c3b9e08b88828d9aa5fa6529bfa72a1b30fcca40294c96f0b75a4651a043a67ef429b302658c9f35739a4b817896a22dc50a9247f25525ba0651eaff47dbcc53ed8d4c22654e79d2879739d544a9de42f7a2243922b26b25cefe15038bc630619901925140461209655da6cf7cbe30e5926be3ea9af7ade7398abb00e613b61792547dba9a1999030f25601f1a51517fd3bba0e71ef1d90fc0380a428ec71f7a35305ff014992f8a1506427553bc1c8a4b567465bf22f9f6f7aa3228ef0f634ef9a4df963863390cd840ee38f9c078dbdaf7e3be5b83d0b793e527ed2e5dbc5b01a1f6a81cb1b8709cc17bd7cf9014100437894fdabb1e1d5aa297f50ddf10325548f9e1f5b55d0359bce92ffd2bd43e77089f82ce49c8b6603c713338b73fb7de0e65c58b0e2dc17b7ca40e8ff217cc00b9fa3997e282f954d431566a24246cc5e1b50bddd0661d27f129de6987c37701ba3aecf0c3845b95fbda99c11a499c117114b18a0efc899fb6e3b733cea608d39a9254499988f65964819af13fc9483778f9c78f365924d26d8f28597cd25bb36cb802a581aa54299e82e96c449a9f7984bfa25131db290d13a2b4cad8377c14d2aadb5915d37e49f8d3630d3242676deee09326b8828170c6641206840eb045e142e5fccdfd016498c19307b1452a4b9f6d01a4905b97e780dab9bf62af452da64187caa7dfe498c7088d37f4a40fbbf617e16125136f0c8bc4cd897b1c5470d5addfc6aa04a21883f0f62d66c71220614d08f463fd7e4e5329e628aad2e420e9fc0021a20b1721b14d4ae714f825a17d4d16250e5fe052dd4bd57156b23569b0977cb5d243936a6e6bb6678d7af8a42a8641704c8529c457a700135b2f31794e29753c3e5059ae5586d6c9744c9948a28bd92d51cdc3634131e41ff0392d62b1e55cb91f2ad7b56f0ce2e697060e92a2b9041a6bb983b37ef12c725baabc5a1579d1939f7d9be97a723f12b5713faf77cd05d480fa7d1312ff9ee14ec8d9dbf0a6291dfb33dd6b2ecd44dfc740597c1348e351e9533f2c4116a2a8390c384b83c9eaf22ab3de56e5dc6df606f570458d8698acd12418e4c03d4e5825d58cf0f73380b19873c011e62e9ace8b26155ac1e30d95d546232f1a6f3f2d5c6497003390f7701f220d2c031940f6565df8c65004b99c5fad399acd974e2fe58723a014121ea5106d2c49eea1ceca7df369b1fcd7951cb8fb2c509462d8a6f092e20dc6b767809c2b384972acbd866c020fe598380722a5c261210ccdd6611651726afe65ad7d45519da1251d0dc5fd3cf3b84e02e8b7ea5a81be3782c8244530a14bc4e884bf8a83f64251d036f6729fd3d04508a0d2bafc9002d724dc8e7d01b9992edf295db999e1f54bf08dc443228319032ace733db909db508a001db8670bd3062d4ed133250a373f6df40193f3ce1d95a1177b384dd6e34a610654a82fe549b428eb28ca1d3f9e2a4e039cd1c3e011347f4248cfaa4a19e60b9809c6a462f027c8c86aa00c64796cef8e8d105c7b7d228ad8bb97792c8d9d98902f6b4b3530218585cfbc4b0b618b7fe6857b29bb3706af40ba7b773c411374f00e92d33635c9eada1c54c08b1b8f683add6ea7214dacf51865bbf6f749263fb8afa7630631a0f853a864710accb27401411d0993a16a77c2d8a96577446656fd1b81c4131dbf62092b080471b9d999df5fb16e8add1c79044400adc1f37245d84811acd3c3e01803e388d81c3d5f05b532596d6fdb63c2683161ae320cb768a69d896ce499b460caa694e38b3e7c0950f8c3d748ffc2004aa581195641c987a082b47815b2411d3dc8865d4adc4f20d10716e2087124afdf963b62428294adabcf1e42a6c5617c2bc614785060f6dd14e7c430d105085830c4ab71a129fe022b85c39a222e3a023037b422e2621ff19a018f8288c72f4c29b93f1b464f5e62bb2ce2ad68ecdd7159a9150474324c5da871e99c3502f03456052ff7fe643e56997f45b8aa82e74c1b8b997dbbdf8348ccff40a57b658c2e970709b1c3202786b58f9fdb8d4fd12c4396941740c21c0d55e59d5be047ebb4db8a75ba843a77ef7bcc686ca000fe9eda4fbd30e20aefb4b4640b2e723d43e717059883dc575704d2506fb2aef71b8434783254176881b9ba837ca1ccef25a4623e1eae716fb7fb408d8fab4a172352281298d659818c804fd523cec3a2460149cae9e827883dec6ca0bc85d419f6281a0261b3fa3c9562871c719543ec6ade090a0f00e51526f871508dfcc7cc9b2ff61fcd934c77a811b936914a51b4b51be90b33007c9efbe9b137899428180cfde8543a60f395b80797bbca7bfcc341ed48023a8881aa5ef2474a3c0257b7eb18a544318bbb2563530c466301fe6797251d8ec893b4eb8adb7ed3eb2a27d5ff2344567035e0998204bc03747c5c131ce0428822fb16e85fd311ce8b223522449e57cc2762def0af2a01f24ad24b1f551212e2a3ea79efb1dbf06fda61d3438ed9a854b00dd1e568395452aecc8e50bbde08bc93c720e634a69e1782470196fb6560517873675f17fcb9a0e31e028005bfa5404e9ce20c79dae709ab138d62443e03f2a5f9c4bd4c4858b4ecee1ab953789ca671ba4fc263823ba1a237a6356f8bf5d9f13a71840061f7b0c34fac39bff2ac380666551297e03461b812615ac03c911bc8ca24ccac0f0acb3b50e62b7c242092b7fbf1ec29c9694e40517ea7fce63a1d70009b3fff2c303192e570010c1031baf2b0184118554a45e944b1db01bd9c78671b0a1c4e0135d4025d2a92b036689eecc268ffa2b64946499937f85250da23af8291e39a3ba152a117147f8930657d263667d4f5b8db82a1fc5245f259ed35ecfd5b31225c37e16ffa1decac10e9419cdf16f6636f167609510f78af48cb2416cda2a9f01778139fba4b4745b677650030bb473d9da5e74e8d31f849a07ccad9f92fc31831035ad931bea88198c827d046c67adbe0f9d3b8c39806c57e4b093e015008e54d072e13b9a7acd8da340b2fdb0b56445010b49e68a40fd77b338ab03999e8e354b3545225a19ed358277b8b79f04ea1cfd66fee64a0509897abc2a74875f2013b5c652e44cf9eed9d0e2bc3082392ddb6b77b49b67dd4c03ad226b27eb95742478479245b6b3057ef5371ce1a00e7aa720a8a09b41cf6d372782737092d2c9f9e26aa1c49979d46acbbe1c04f5f238d9874622664ef717fe335ce385c650c69d83d3f281bfa37ee070db5ac0530f9cb6b1703b3535d4b670880ac93ba34513c836eca40a8624cf214117497f6f6df02db7fcb0892e0fe4d83181f87dcc2e28e5b4a9334209683fbce6b0b2d30cce0bbc8ba5e2283f090c4f29de7caf8a7f764bb0ad6c9025d44023066449f31418ee532d28733377b49cf3409700cfe77c196283b1d346dc6c5ba641d3a0b23985d051c987094176b5b9c5b77e1cc064ca4ebae0e45ff1ba8d7487a35f0949de9bbbcca33a19a962f67c208c10b93ce7c980db7a9956826e3763c8e24140d19e90d6775158065bb3ead3d6f7cd4e37eca9b3d86544bc9149a711fef6af791162d338afa1f6ac8dc9de2d7931b976cb777c5c7f388a5acb242fd14e59fb446dec2bcfc1bdc4940d92b572d5fd626c77047a67796a6f3a38c56676efff87691f9eb269c9dc2f4e4a7ebf6eb76d2e5ec5bfd76955cc997b20930f59ea5911b9832a60e97b2760d1fb462c39382b9dd5d6a1394f525bcb5a90722599b279c92b8d4a672da581c033f54094c913210ed2a316606e549bc732a4f20afda991ae7b3b47b62d34d76cceb1db42fe5274752de9be8984a0292971af54fa64ef93db0b3892336d947b5a0e2b1f42208e32f238bd18a5c7996f91265680f4d32546eb6937477ad5090a0e000353c1326935feb0972c3b9fe077218bd77269ab35a90fee35a2be321674c3b3b9e05e28794980349716120f36b3712b05047b427b80edede5a6b52029e43fb8286cb51afe579b58b6d198a5861a3e92d6040455e96eec49ab796b3a6836db28eb08097b7600a503cae61bf7adc948b1f10df2088826ca3531a812265c740d69afc7ed3348bd748b4a0905ce7d324d0d54fe4e28670f220a011b3a879875398a34fe5ed1507c903b96902d963da06937573ba913717ac0ea1a208ed42c0cefe9bf21bc9b6845ed6e9b33498190fbedd482ac4c54cd969c29b83e02234b322916fcaaa9455b6dab5bfe66717385940015afa7946c81e017672ea0c5278fba6acce38eadd6c148f92cf1b88597d15cc6e8fff57ffebb3367e260392982487b0b33c28e94cf43d19bea46d17767f1df6534dfb7cbd538fc60eee00f72dc5559e0b794378a913f232861252cb2e0d8f709a378cb4fe68cf49e8e33cac290594e94980f7dd88f617966080bba0e512d6efe51529fa5d3478622378a7dce5ee191cdc98248bd5c3c8677f01b1184c60d6068f6a55e100e5905390b7f60d7da798703702485d73d35136050831378b139c4dca2907532cab4799f0bd28b0e574619783fd4d79b81491c82b30d11cfd1eb7003a5d74a2679683fd3f7e34b3eaf0461d0029fe78a2f1e7e68a291fd46cc3959cae55cfe6d5e20008deeadef5f62e0e0611fb0dd92a5e56db19e6d23a52c43c78313e7705add1301e6e7ec9120e1280fc55eaea3d06b30c8feb8f309554aa7d879035592d7a0b4e0337bec76fc103e562bb376cab3b0abf677107e4edbbf3e021be21f739ead44800169a9d27bc5303be1fe104d9f6672db502c830ef329b13fa193990efdb339142a2f0038ff487a98c4f21f8bcb83a42b60c616d58751006d2e8a82a2813f3a7c25555d5005fb0310267b2506b34476a37a50748f8663ddfb981f92af6561b132932f1113daf35e117b5855985936af2bf54d5b05a57b27d9c26791fce8d3aa7a5270822dbeedadf028aa5d8842058b32f95e44ad2dc24500684c756f6e48575a7e1890d8914c34ee4e61bd540cedc60257b4d41967cfe0084ad09aba546b33a206f4502a06a40d0e949bcef9a8bc809ad026ba18d73f729ef6adb16685b35822d5f60e29760f067285bae908aac48ddc811c2050106d866f57890d03dd5e68c69860a99579e8b7393f1a14bc6a192892ea2c2e176e8faa12f47fa9c91931eb9ab19555f7b480b75a6579150a3e5425dd2e7974d3b2b6a0020a2ddeba36e31789c5ae88cc2821816e4b4679d33fa21b24f7634efc7fab4b21728af7b595df86a53082b06d5cf2ab89317bf68a289f9f225aec93eb55eea38c3311642bae68957b59b572218205a12ee61aeecdaebcf41b3d5bf3ee4a9307d68e19f6c9c2fc92dce38a1ada4b98e7fd1a77aee9000f3360820660cfeb08cb2d581facd0ccc10285e47d0968ffd8cd6847abfcf11e9cede882b47dc1aa759bf2d4222aa5b5967a5e2a27f5401d963f722a09f887e41ca51d21525bbda2b7cb8e522b6499b462c3af1cdb8a4d96e005a6750b071e8e5436cc74db2d30606d40dff8a6192e029bebcbe0ff90eeb86e72a6396515f986d60556c9bb94692f1dd24a3d3cfb4524ecaa5b84c9ddbb4cd289bc88add8359ec81850736a21014d0820349b1d858d71038adc9ceaeaefa5806f912336323ee023407cc4d89568c7893ce36afe89a2f29feccaa7c73bd97d036450aa27ea3784d225b765ad957ca917af0ad4fa533ddc08e0a4821ff1a7740d5af256b58fe8c051b01d8d24bdae5eab315d3d8670201d161f3c4fe66443b70e9a76be198b8982b23000a84206306721675ac2f60915aaf51658c5faa209a5ae847931b274a420bc9443b00c84e816cd89cc50ba6e67a338bc49812820b4dc05ca762e76653429421b2c127f37c2909d43c0a6eaf6b593ea4d29015ce3e1ebe4fceac4933253df4d1f7c3954bce87c17750f72fbd8613fc8aa6778b3450aa4164a508478cce1caeb27c51a6db74d15ad11db0052379297524d860cffa3d040404e29efb49c6d2f404b9447dd4d39ced8e3d870b7c3db072d00ee29a4bca3b44bd2fa27b524a3ae9324af75bc630c7f5f7210cf751dee1070084850eec80c9410d72c7b2bf8a50c7cb360dfd05bcad3c704a5882c3338f401c3c800e96f7a2feea74df1d50e497ecb9e6c66734e81b4146674e8f87239711f1b2d7076a65a745ccd92420a30cfb5f3bb7d3d9a63cc10041167b7fb9c5fff72924ba576671f77ccb1815b74d9562c38d7142e02534aaad67a0f4b7eb61dea84b3c23bd0028d89dea2a58065fd847a9b306cb6c00b161de2ad5d71daaf625192e8f80ee696a6d115b23b2c092e6bb602b7119a137434035e3b61cb09403227fb180f22f650f3f8211c96cd148053d36787b484f0b21c570725d149efae5756f8572c2d7069631c2d3d49d77c1f3526f0f47f032f73abffbe335548cb058c1db281f630258cdd2adf61eedaee4ca0ca6a119abd832200da0740e27a2c44c368a04f7051afa8ea2f0cb1e868e0a3cecf892b849af378d01b0d202696070ee967aa2527184920a5568214fa47806ac02dc1d943da1c754044cf105b3d9d193836554a957e9765a66f553e49b435e12042ce81a7839966e330b30bdbad2a4836e3356ca827b0c06b727a6fdb9e1395dd8bfb667f737237e45b9d7480a49c147e2396572ea5686219a7388b6e9871c5c955c1c22d1e56659007132e839cc4b3ed7aa196e921e0a176af5de89612dd4a36f62d9e474112d85bce87383a1ea91eaf0ff82bbc85faa982e859855b296ca6038add18456ac6d3a624fc7fcfab9a6bd62395876ef115142ed7a9161b86e739d1eef112d3e5fe4ad3f04bf5f563f6d52063e15517460635442e45ee84bb24afcfab64622c369f1839a907c9fa406af080394a783e915cb620eb21cb7b2492f61dce168ba2d57d2df5a131b92d3d24fb99748dc522a4714ff52725594f867e37b01ba3cef04ac390b0e0a4b39b96efa9d5c59843fb1fff0f3c6234800d017342bfb24d08b121214d8666a15306e5d4842ae6b3bbe25b4459858f275e2cf8ce6deea34414a389f2323a34f19be20f66dbc59f16d4d6b68ea18080b210967d5acdca485c49d0822aef1117de0b82896f763f9b31a86a1a9d0bf4d1eddc3081d58bb16f2dd57451e94e3eb8985b348d384c664fc7930b258553643cb8ce370dae9f758c7cf58161e36226f3b1eadf33f91897a2182eb0dfd61fb7a436ea55ef6e3900fe31e8f58bafd055bcf08358d6e54e57bde926856479dd388ab01d16881529cd3abd9e09499b7db1e2ad361110d67f6e96083c5605927b993295d93cc9ead0462e51a4ee4ec0bb0c7edd1da7aca1e7afb285a7652c9b9c56604c1927d7cbf85f84f2ea499d8bc5d772be54b7a6ac871469efa12a44663ecc0ac0b00e006e29dec4b09964f387443a9f11c768ed70c50689a4ebc08c89f0f9d2077d0f387df7308c0826ba340c1691993118d9aa5a48c328158ccc860ab9464006eb1a0471692744bf88defef05dc0a56dcb68f4ed368a8d099726a41827fa8e2568377aeba5137e4b7705615f0555fee39d4738ad7157d291f1374f23649725f2de60543814b71daf479f9c9c0d83b4fb0ba6f63efcb578d740a936e5f18f95d7b1b9d9a131d2c8278e1d3a90fe1cbc153b810a62c1b7f4b96e94eccafe79fd341d1e95c3b7a7f2f0ad050ba5d6d7eb97f9dfbd3c30cf4b3b8aa8ab1ef22ddcfcb13342db098dd2e1ccf1981f2a20a79c843bcc3cefe586079e7a85e7f0f7033f8fb74f82e847cfbb23421457ed9bdd0526d96051ac6ea3162530d4e9f37055af6cc87837e7ba8a68ca8dd051f4ccdc5fc186c749c02a80f04e8bec6a3d2d82699f1650e4e15544addc7abec12ce72aa5746225f2ce3f82db138ad5001040d05ada8fd3dec07e1aa77ee539a8d483a13d96f7ba41699bf45bec83cface1c8cb9c725025f2b7482ec695e2dedcb2b2db6a45822e3062c4b4d14aaec70d411f6ccb385894f8dab248997bf8fbbdba2561272c463e7a1d41999ba3cc2f6b1977812187d918cc2efd03850016e87d2ccf372bcc9027b3fc29383103c907f028c4f5423e714579f27384e6f1c02feb0051f8314c868a59165452f6f2b502b93dccb095adcbdff38cc847e82adbb220c51b0c23aff2e0e55211d60181efbfcc65a92549df27a4acf6dfe37f907de452cdbdfbaaca4a64beed3885f96b9c89f7d0a443629b8d6f29857ab0c8cc897d26df277b05f92970f09bdc4078900856585d3727fab35d086cb74fc433cf9a4a895a40880cf012f09d93b7cf43ffb1cba11894efec58c2746a972af997bd00c45722c88fc88d411721e2b514667a923dd1c93617c4220117b43485ac8564302f4fb91e5ebbc1c7769b22944e9777afaff645603f192c6e886de19a94816ca03dea7d407bcccd66acb94e8ef36c5210ecd6ca0add0d9993a1a06775f3a775b2f2f1c329b30c5c13e1472759888059a5c84a6e0f8870ad42d1038abfa21de3ade6c678d84c9a32f9c314e5d7e1854fc2bbb2bd49888326060c163933cbf581bbba580bd2efc302d11df317ece32a1325b08c3b34ab9aca95ff1d01b8f0c6a2a4685c666c78238de7ee145660f3d49cb27b5f7283396c149d05f499cc9cb39df6ff7cc57f0659a99ad430e6e9e17a01b10cd64b8369abda67f0420363a0919140ad652b296b9359c597861cf14f5d0155f9d1467ce882de98ad6937e93d9da2526ddb2f53d8ea05ceaeb4b0564e7441d1e87ef783fe4a72e15d5d72e6a04dcee4fab6e53fa3fcc760c21661864d9f23ca5104456b58867ec8dee4cf3937a9db72da6d9eab045f81f2fd60b8f8589cfeb1be2da881236ab5d591f884c17956fab558a7c4ae83fb7b4bc65d68699a0dd25e2e7d3f21e81bef388ba46fe3130a37b1ab9d77f2c06c3f7599b56cef387ce60afac1ca7f0e14dcca2c886d0db01ba4339b0de6ba4a857706a0563a097d85c4780857903c9f7d16e79dbdd09254c467071de5c3af89ccfa69a2ba2a2c51fd5801f3ca673934dc6961f8f4ec0ee37b00e4a044f20483ff8767aee4e999cb9541949c224bb212f126699624007457206652d12e65bf8e56ec3f7a72fcd69a6112b6df9d0532263c03c3c4ef78ec02572031c4b1032799f13563d9d1bd819c3bfc7385d4da84301ac468dad06ebe32aa1d98a6ec63c5bf9dd03393cc4f621e6bad62f2081a27bdcd8a074f8b279c1a5b634e1b24695e7a902c2afc55bce9bf000baea7f37d2830f54d97d736cd6d8356fda8cc0f1981e983220e9c42f189de72033a01e901a1923d4528ae2570055ef1dc0f091d32377c9180445e001dc92f16ed611a3ed83f7de04b8fa3518bcd343deb26060cf5ce08f3bea502283a0b192fa336696a6fe259c993a3d1c88a4c3f02743ccaed1e2ec28b67c9350728ee0196c49d4540e230ffe10365e7bf45c8dbe19d936f1ef286be16cce0d17b9f66c4ab627a1cafa82f32f0d70d57e11926704e6825d88cf7f361dacaf230a71891cbdfdeab77e6d4b7a15d266de110bf034259f573af4385e4066f4255927d8b8045323ec706fc59019b843cb1eb174a968d4449f4819e46e15ef1af8a4142eebab895f94909aeffecb037c0b77f983138e4ecfe27992010603aa10e3b41de5c536b848021ac3565dd163d2baf70c1754dcecb90c8a2359b49db8267ec6b843da597cf499a3f66e5dcfa39e62d75efb0b977659be75495200d830220cd6c44a9218c49730c9b296942f83ed001346c8d314544eeea351d108cf889685a5918d3714c0de2eed519927d2a72067069191ba934e83cc928393a40f0460e958868f5bf4c296e85b8f2e7d6454ac4db9508da2856228fa7d265866711e0a543498546baa78ed122542b07b621d7a35f5634908f48b35d211b2599b08f20de7139d90a8d18aea23ded5c2b25b197ff1b4428bc0321eec11e55b59254ea13a5b91ba276748c93c2b6849e88eba9dbddadf301b3b5bb21f0d36d60ce3eac059a334168753bd7b801588958efc7bc9034dff0f650010e76f75dc9ee6d1f41b253ad7226166fb99dac8fa1ab996d9098007013c0d9c577a1903d98afa593311d2189c279246336882807f2c6d4c1dcd8382de0063c7cad2b5c094aff7a1eb6a8f725aee48a075a060e50069e2a29190da7a4bda6cb2a0d005f860437a044cc284951768faec00df84dc6f1433d758434d80ae4a45a108c2b4eb8be9d528297ff40a2e388ec01493a941d5b08f4a9c0707271585914bc3b500ab05c307cc65756f4ff1492a1c90960ef068814bea8bb1ac8eb9e95091b76cc9757258ca4465c430a31bb65fe6d8ee2ed5f613fee0d08a20930f88430b3c82011f59c2fdb9bb255f6f77ba4e6ece883733301826beb4d438116201f1bac1485f18333f303f3488f819af8ed5630b007729346ef0bde247076872c46983ab22a95256affaa458f40949728ecf7429185ff0cb3f90fcbf019cdfbb78285e9a4cbfd796dfe258c6df20602aa17b16f5e7edb4122a263b96b09265915fb809f5fb505f4d506130435255b73f34e6ba05c84b9aba9b7c0e43bd6bfe5b63fb80d710815b979da999035dcc738adaa964a8b809e715bde6246c18b376c1347f84eaa5079d93d62f023d3776c8a44415c4940aefa5a28f30971691554bf90bc89870862c7fe492b02f6fd6fa32ddc00e95d1d3c968c0f295cb8102656f172dc364a9cd395e8885fac88da1a88109bca204bed92850b51107580e4eaa4018f50bb695d5d09062ca320619d2a5dbd082557520013e69af6751da65299fa41b47061ec07fef14b0b1534e4835367d9c809f28d8455567f543da829f8cd1aaff6eb91e30c1445bf209ee99d865783df3bba7feb7ea634974ab14abc0c85861b7dbf90a283a3d00143292094f2f1bf612f94c4932b735e80e8b1cb2105f829c7a98a0c7dbadd83bf4fdd23f045df27c3cbf9e7e97671f06a172043c0aadc1ac846a56ba8e8c89ba6de71417a30de0c8d7aa1d6fa13fb878b8dd3f5ec1672749d88dc563586bb6275d253b54fab30b810b68698d1cdbbe8fb54789261091f4484ee9e3663972c00c8b24a6b6949071f8c45a2ba7b7e29120545c60529ce31a4156ca1b50ba654f0e1801af43ec1295f0817087a5588945a609e0d0552d29aa521718fc5ae77b163c64016e8ab4d2760a6dfa22316b4844a9b3e6e93fa8c3d8d8c2fcf1cd3c11c5bcecf820429b42c1ded516756ab4fbc8b2ba200cfdd873cdce1f9d8eebe1cd53e1995446ab413b96e1bdbcd828110c15105e0e7c3970aa76f93a8b78c2ebb00c0eb05227508e109f0ff47ae50d6c5f6815c1084786a3c18f4bb359bb54d9b8556a7cb5acc8a7b5af70f77c885e6d97a160229db4b505aac19fc65d5218766720881c54a23067027472fbc4d75f187d9d996533acabd3caada6e52c3701bf1ca76c0ab3989f826214a4dd861592910986a84ddbc3fd85465fa924bbe4cecd2948b4277861b0e111bc9c5ebb1c0efc925a01d7a4d8cc00c4fb3ffb871552e52a158556a63900261d842e960c595073876066026cca1aad024a060f43a10141457e295a834d02e058fc303548545b4c4620836f00ad50fc9fba6c4d170fb15f890108db2249e08d2338a9fb15099584909eddb11626b9b5bb97bf741cf3311c65a97630d2e1a7fc8dcff0be32893dcbc56ca71db575ff69923ba6a6a8140ed513e1b73475e2480b02b110e33cbfa483d9a62c835103c0f60cb12c1872bcb06d04a1501a232360600c2ded961faae7e4ed30a5e24b5c3d6e228150ed4f8d38c4c1b498ff27def4f88be0452c5d1fe1ce51c1288f063a46df73f06b323684534fba3d1901e74666a1dd863bb6335dfc0237c3aaca7544029c9436bf141da3c27d59dc57ade87973bfc9885658b9d96fe2ff169877f74068089e52bb6180f81835ca7b170fab64a8517a5a02090064a294f15bd5c085ff016ae74cff7395705e9d5f67b36f108df3fd1f6bd3d7bc57e78edff12adfac36a19bcfa33e7aa794711fa5038c6f43104ec78135714bed4d834c4990ec4b453a0f6082a2cbca26adcdd5df684cd4859cbba7ef986c0eef4d2898213f2b70fb01bfae00a7d0c0051b4dfe25766702e9d2e82c7f02b1f4f3b4a857572536654988a9b481881512216c8d029337834d1109e0e61e0dc2382dc067865f9761b48251c268de02d0f0a70441fc9af0d2f197275b2b2147d172143d210fb3abfe0e961c0023854c0df0b005bd17a949f9e8ff8b5b64ab9bc9f84c7856cdb39d1aab681e5f729c30bcd19257308ac0e4bbdac212fa8a7677f6465e44538d43c19b63e046fdd9952cb75fd5c6f46462d19791f4074b0d0cf16ce1a59ba9b6ae56916b8130bee1f17b4a2b3d7558c4cd4c369f5359da9ff42a6f64d9d41cf85b4be90bdd76363f9ac0a9c4e9a363236dbf697444fff02e5cc04b46cbdf3b351914601e0ca5e6d2895bc812311cc3b7a973b153a358e1c91ab03dcf5ef3a7a8c0ef1e9515ffd7b9dfe19cbaa671442599aa944df55dc06220d3d2b86b8f45e83968092a4053d1b5af07b7a81db3db7556f27f8fb4989348803a06759294098d820550a47e2ed41028ed1a493e08cd6e09fd6428a5a82857ef2acc9c13d50c1ae05504e48f8420df5f86c1d246c59a07f86d3e3054c85e257e329e73a6524568be55c8d5cec0f4a78c60a90a969939ba79adb916337c309e9bce857c9a7f39c2425ce0ee57ca91e1bc9c69862660190e2c908c4090d30d03a0c685626d269526b52f5c7a6b1db1f738e78f1fd5f0383a70bf0e5ff1fff1ff2b33c00ba93766059394aee6e71af43ec11ee3273d43d080396ef8746d1ba0a1d212a5268078bd9cd5a6fdee0a98e52a6d62593edaec6dddb6e6d721396db07ccd52c55a418790aae673d6c072fb6eb2fe6358a5150f70c69ec0e9c51741e9dc275e47453f8c8e045d7a0ac072285bb59fcb17ba39a6d83e0772958e41ad52c6a990c21057e56ed2e9e2a0fd8286b01d5e87e6271f92002136b592f4e9b163e74c805096bd35add227729a9b8237c27c35f8099af1325936433235c23546b2943cf1acc2ae9ea155cead3abfddcb18dd3db1e19418ba7ed437a4a2315707bf3f1f1e6a040cd5990cf14a8091dcf4c9fe43474b3c5f4c9f316ac76d8aa013881648106fd67f4a2999fe9d92cfad5f394d4043da6a4fd58ff88ac52f82a750eefd6345989db7f368eaecc53caea4c416e19f873c8417dd4d4919dfd118d6895e8cc95b291336d902bfd59a54f72126e3478fe5374e43991a8d2f5fa27ed3082732d4bd9a751252cffe04df2776092052dbff51a49c7d6176495deb1787e1c4157022f6d1ca03c3f3d83b643039c3373b88fa207bae798e1a07e0422c9d8877207c6606178f13606eeb9485c28741d54b1f5b9a6ff41f7502345fb04856d9004865c2f6b7408c96375a08519b729bbb9083696e924fb382ce5015f8812c473cfc3319af42979ba55403479e32ceefa02269fa199979a6bfd083ba07fcf3e2c6cc5ee022e6cb91d81fe1a9e88097bf23c79e180330f0b4c0ad1d0ae329de5df12824e87a5f04475b337a649b8c95c949faf38e6f0bad45f4511ccbd27e6762b6a8b73cfef3d11b6f4b2b9a82e9d24fdc91afd000afc93f4fc95b8f8453186c289736283274cebfbc26de7923952d9ffe805b4214404b5df2cb64006f78f5d6a7bd0af8bd70094c08e7a133d00fe1e9365e08093d50ab91079edcdb23b55000240cbd3101791a75d69c00e7387807c7d4e5f05e0ccff15a59fd4dca69959a28071f6d45c4d688f47eb8f9a10d983475ced98134f055701f2276852e6731ae7a2fa7e5168b0169ea6c9e70c98401ffd62146c845fed7ac49582fcfb2a0ea0dea159c543baa0f7cf39b73e734baa3ad4d79b9f50f73952dd16555e2f6d87d60f5b1ac28be7bcbe8694e80c4995e1d0042c70faa97d51a64566cddc77e03c655042dd91dbb33039cedae4f85087c11516732e0253439d14cdcd63e88ac681533a3f62ca8cb82afd623735e9eadc6a2c168fee647f40691c860619b2be566e8f1b2a61d80dfcb86904ff5161912e7823019826c7d1b81cb2359ccb679cea5ecb426310058ea61653e6e9bb251951c0faf7d11364c37252849e52f6ca13ad6d3941ff90facb7303923feaf662dd6c57e2dd15b676966e15ea83af431fc6ddf0c54e735ed66ac50ae01d14ab611515c842bbf8c8b853050356d81cea4aaaac94a8eac16025b14cfb29e7ccdfab645429e84459d24ddd68a668d96fc9ac5dfb83738c4a0700e0691386c9e943bd2964f38738cd0e86823d4de8436ce3ba055dca1d8b42e6e52120a2686fe327c6aa93ae0e01cdef43db18c329691da937ebfcefb0fa263c56354b29f36c5f54641434908cf58a94d63c08c662a079fbaf0a12ac844475246de067b9d10c663dbb86950a004f7052647a7ad67a8bb5c5773f139e6bd3e14067ac167ab6f932788d9a59af64c41499db18bd5db69738da839e02805597ddd8770e26f9fb7688125d41a16800887092bbb9e0786b1e181943df4bfd77692d34c368f383c2da9f91529eebfc94c5473ab72a51b2ac95b0bb7f7167492a2f0b255e7b3258f0517c96036998ea64c581340c55470e27d3ae4f0c866aafbc39af5443f695532effb53a2ed7d0ac78009635f5e87f9047ebfd042e6fc219352a23676b4833f0a686f2f2cce8ce7758cfaf9161e48aac5ada33d1216332b1bd82ab44ccc5790155393c547de04eb803becc29a326c75940f3b0aa4bff71a11357fa7fc0fb0be8e3b73564eb9584ae6b7ef798c01028180312b47a75d31825262937530d9c4b4acce3280aafd52a7e98dd9c0752f9461b63220ecbaa37bca367fad478ffb88d6d15457e6dce2abca04bf35bac2aa771d797f82c412701f5b129fd799ad9d865e834ef42de4de31632b76e74379106f016b94115324edee0fa165a9fd0cc097d5fd424b5c87d5b7ad364d514e8a7acfcaa49cc9587374fbc121d61c04da311aa772db5bc19e94612e23446a4dc2203604ebbf23082cc7d0543772324231ba83c5e50f0bf2030c13a5172b15cfebcf6eef8ae4024b8089abc9a04e91aba2e1b4e34799d03f17cd0e7cf90ed866e7963ad743171ac0c2471c300d7561fc8b3270b5adf36e2377c04258ad8881bfa6d5d5791dccdf076e687879ea9f04c2fa963207e3fd14a607439b5e7af463269f8fabad5e0f28b61054a3ac63e7c9f181193fb3d38ef8f0d8482ed3378312e72115b9e7c145fa5b2d11fbf7d141d8c2c4fbd78cdbdfb8e82f8c5f8d76845c5a83a534bcc91e10a4e545d4486e0bab549a9a27d5058ba8e8553e1dfcdd7c81a65a50c111162a61203318340eede410d2f5aa933722b4672c10b04350ff29ac4be74b87a58b385a87cd04f7b70b50d1635f159832f17d0724788d59f1a77c32ddacef4d699c9e36194fb2112a0cb2cd61afeb48649647cbe32d8fb69741e123f36df36fba2dcc509d13ece972e8eb16b9b8f821db753c70a45e65b21336a41237a78d1da819cb8ad6028765c6f796604f96ee1067582ffc0cea82ddb9531d317550f19fe717a573056d69ca17166122af24b8a6e1e74923b0437a1c75cc62970356bd8c707a8a71c6711d4e6f9e6ef90106da7ce49542d7aac0f29f52ad26a3eba0f24514eacea5b23f566acbe8a2fd6b3e22b23e9bf474a93c25be3bca995a74980210210397338c78a186d473de7813f246ab4848747c2bf3dcb393a9e87bb68fae9e13b8caf974464aaf0b2f337691cca9742824508ef82dba73af45ab4ba1c1c5e8e9c489f661978d2e99fcf7386dcc7afcf638bda3c44fcfb5e29193e73fe841548e326261e7289e593edd014764edbc254e76e01c056f1453b93fdf5d980d7d1cad2033ee08c4639d923eca4adee51855b433317e2e5d3a883b9811079d58005b67727a4dfdbdbe7315dc6f65a64b5f2150d868240edb1ba9062f40e950fa9437664593816c093b69bf3039ef944aca30433730ccd50e2f10f90c2078a3ec29e2c5b1d205709ad158f9b33a407f711790878d9a1102e1eadc52812522254e1593c20e893463132b7205d19f70596b0b9482fa8b67278ad57f08b89999bf5f5c96ee5090999bd40919242b06cb4cb82128387926f971e96b7ddc85ed031ec6eafc9ba2b27ce21e4f5d052500f3a6ea743e4eb6ecefa92335938a46bcb970fd1a96a400c99083b8c86c1c6939cf03284e37ecdcaeb753a11d021f80c1a10a5a80660b3b20522a5807fcccf3a9ca8cdb0493ebc2b5360f5254a23b92daa32142506c8aaa575e901528f8e06f05ae60facdce33f30aaa676debdbe667530bebcdaa2f2a594bc90042071bc9317549d782fa5685de18ddc013462dae8054704724fef2233c417c61f2b1f174cf93678f4992d4cc8f508688d25280a5b320770006dae896237b4d700a1e691bb2a90527f8918c812205f32e93363a2d5c7b9a17b4fd8df6f37b5c2d6e2ba10812300b9809b52d5d3b26adbbf69ea366b0fc116c421dc49a79bacfd0c462f0d460e1fd96e3103d6c6fb263fd57611d85cef32bbe213775b87fe2d8006e111a767cb46fb19f483bbf4cf5c6cccfaa862f342e86ba206d5ce9deea7ed0c3a9bcadca03018b7a1e776cfa676e65cb19f9ba70320ee3615aff2f1f6bfd07456536af90cec5303b9be891ed4659edf57ee4b29a5de47edc00591038e16e074f50d789e1b29970a821d8544cf0c12b0772267ca9e36ae897845ffc9623eaff516ed3df96b98ed6d6968dfc14e6856a9089c5c04168962ad171ea74ca7028f6ce851286e1cd582e35c9720a5ecf76ba3f75c8a4b07a500bee84930959e5016fce40e45ad1e97672bf7966685fe233138c749c0b2559f1de9a6842252e65dbb1bf44780f80c6fd02bc3546f2215cbde21364a8b6f7a2d65102269a8c11727f06f1200b8fe099bb2e7c4936fa3199251f71a4f087fdf75da25de1d5627428182066f4a626e511d689fdcce891fb7609edaa83191085d56a7242bf9418dce6b8005f15f96e80a767b92d2801d16df6a5bbc9c2d89d3527817176d82877efa4888236b7e16c8a3c24d98b9aeed84c036b79c0717511cc4b1ca476d2d28a67bdfb2dce9a651bcd6240b7deedc752bf5fa60c8c08ae8af9802fb1d8445f5b98ae0e09223dc8bfa9802c0544fc269647707a42d48158aa6a6e749ad94b47a83da2b63f2ed7e986452d2fbefa97b7b3a73f7fe600816fa1044f170362e901b06a16bf784c38d967b4402c48358a7d2a3c666fee936c7183e12b15eb100fd669e7c003e1fb77d3280060019caa7753b5dbd2d5d60ef3e5e1e93c24bebf4d4fba41424483a714bc0c6d9a22aff504a5cdc93050e8d830e482d167aaa6a6345e8d22aae921444f602cfbf4831eea2d2d81a762337ad64b95ad8c82c1cc37802f51f68c4bb4e112ebad9fa7b0b103b432ad10bbf5f0a7128f22457888287892d557fd984817fa2aa95d7280066aaa22a9ae18e68b7bfe1a6b3bb7e3e5b42bc46fa1b58a9eb54cbfce52357bf89544346d01636afdc6c4ec22386b106ba4d0c85daa710bcdfe57f25fd36f616d6dab806a2175b1b4485d6b073d54171d5189aa6c09678fa251a520399af07996cf2236f2ede5e166ab24b7a0893c4bef807f6c8f14cfceb860b51e8bd8a5b7c9890854b2c54e0319c7cce3971c9465d649bc674b14ce39c8ebbbaf73da6b2e78ffb9fc86ae5a8a1305bb345668d4fc94a699a66c0665035b74b3f00eceaa22ee90c7cd44a820a04ecad16bc8eeb9c486015a00035a0ce2055333d00665c4d85f97efcaa7cf5fa5e62295d3483c36abba2f9a9cb0c819877ba5b297af86c16657c8f7b97986a3808b1a2e28ec9fc63620aeedafb03c234a8c72963e77c380e69db5364e489e682895bfd0b9d8fcf45affc2a114bd54f67b96ea192c2577ffa4adfb1055229cce8072ea1cbb970c5740438a28b753ce2d288fad14edd4cee31d30a1b15588cbc202af50081f7d1ebeb0e575647d4326f6975f97eb3205dd2ca3c5da14ff0ac759a5e625697edfa0c908384bdf029c85df1d901f6dd38516d6947b71cf569b75c7a3805462db900928b2e1dc98c524261758017e4ed9ee006f2edd755de270165a937e01a9ecbc3b9c1d89a8cbff945bdbee95503748b409949d11ada2f0816901ae547abf1f17916f7f8603400993d3e30fc9fc8cb25687e6a4b83a46b4bf0a79b1a38996a3c0e40578ea0a96da134cc7c592c11786fa4da4f00e48f6f4a3d4ca57e34f464e49265c45d445d1fb5aec1b6e6f78e1619c3ebfaf0601c34d884b044ea7e9435056ccf4142a5f37cdd057234d3e2cf73e38865785dfc364945b64045630724e158208e51bbc72caf8978a6c7f04da387a1f1bd551ef83e54ed61bbb93424ed8e444ea8846ccefc41721fda74cabb9415460b181a101659fdefee4a773345a4fd13365d4dd63c0930adc38b103fa604981b350ef40e002027b719581abee3304a9502e7aab62287c5911d89acd0386ffa9735f86748eb23ebcb86df39b58055ba004d8be543de11c8ce3f34bd33552db7de0aa59aa6d3b694c4e66701ef9991bfade5038d5045209ac6f1c8f32f8cec4a78a064b931ec5162e4b6c258981bf4e46d4f8c6b980f7bc38102f02e51a514daf8224124cdf57cb487298510c7c5af7ba65669a6f6ed73362d74943289d028638a0f59f06c9cd99066ea2f8373c8211d72eb18910cc9882ce5b164a73fa4a23d802245a1302b04219a4905c1942f247ed384e38f805969220c579a80c095d7bdf4a829f8226ca7769d3b764c2d326c90cd0cf4d1b3192be0169727acbd1428f0639a60492f3ff9ae22a1a4f973971a3f5624495efebffb4a96e037b7774fddd76d5f177235ebdd561d3df5dc6c21b23053cb02a61ec17a09f7d6b13bcd1a2c0b168fbba6b8dfa71178f2538ff93f7a6ee503213f6777c795e36e3dbdaa238a7a8795a6415966e408d3bae7edbff9055fccf5b8eb9eb8e3fd8273d5b67d5ee60788b9767e387798d3bc6c3bd08af64a9e9ddad38a3a978f19d5cd927882624c9555fbd166c012ee76fd92832256a6ea6db69b0da2d1c4716022a07773703b2148ba67abb6497ef3cc46aec542e45d86bb296cbb3039072fdc31baaa9fe25c91f66d15b3932f5c6e8b8c1cc8e84f07e89e06771f7b42df4efb1c096e267604fb0718cdccca62069eb6d5adef69a6962d3661a77d578eb7cc3c6dd1193f711b40abbf58a9b397cadb335720ec1dae7697a575a8ef7f469ade21b8ac433e77164e1686f9eb91a217aedbcfa4f21cb8859af6f7f5068df5469062b0043fabc635c387ba4ac609086ef75b7dac1e9e5c4be4c71f6e1b850ebcfb9a11b446ceb9287d101131dd8efd05e5dd8950831aff93dad92d7b07f2ac2f76bbb72375c1316877200623c3c1964b468ddc63e66a4fa39c1510e5330b73bb5037e0553bf10f1d9858fa8861533e2b34101e08f9adca771f489ed371ef0af122d21d258276b1b71e7fc97a891035aa8bd778c3e0cd0e22d18830487a82a89c42f576c50d21443319a9f6c2c17f81aa6964f0976e3b0be1cc0afe11767a24ba89fcee71ce698196140e405431536734dd7df0cc209ba294bb0735b200563db69d82541aadaaea28a8350f117007d5f268430d323d6400155583175f95223311f5e8db6817e2e6cbacfb7783a92a02013688a75e97a1895226140fee3ef8ed4cd795261f4be37a0127d41cbf1881e5cda05c27e3d1b89bb158df7a3a149275d7f78566dd7daddd11073f43f05ba8d1345e6b642e3ff16752efa2c747d057dcdc4041f2c1c7711c05a45be4beb95ac56d55c57a1105531308a29f9a1c2b2f47571d70f41d4ab8192e70070961874576ec020a6b25e66cc1ea05bb8ae95f1a81abd1433815c7894b67a56ede139083ce94fe71924c7bf321a3397140eabbe72e5d369f94dd0c697df96d9a7ef60175f321cc9d4bedf692d5eb83a7f54f04a8d53248842f54e1c7a18926eb1deab56ed106e55ad7c529f0bdf845669a9508f7d755aa0ffc821449626036de353b2d6e2b046dc05678e82bca6c1e33b5d4e1a45edb9a339fc2ce3042d3ebe13a05ebc334c11355447058b13bb04a454064e096b448ac24aa7a1d532fc4e303fceac31e556a037d686d977d2769ce6409542b0508575c4da93e14caabcc6f4aa94b552e9bd0c2a05218ea6021d098bc10c68a85888652b48fad5bdb88acb3f4b225a4524ac0d9c529f51c62e53cbecaeeecfed85693d53a5e6dc207e6cc1b4af74635db04c8f99e456dfb4d55b9466ef6bc54731f9e6c9ed2970ea33dbac294c9d599842f0d0384f8475466b249dcce37abeda6e7bdde9dd3074802de22541f4ae9c349418f63c8ce6c86d2585b698f8ffb7020711b2e354028a5144d58e20f155e7ce0c2e78ba13d109e208087fcee19c9259c81df01c6ed802751b9c484bb7344b629d714a489f5b68dbb6a4f35008fc53448c4a98657b21bb5cb579e1918d1f2a34dcc0b4d158be1e946cbc16079e6cfcb949e710fd604e7fc2264563940723a7e95aa356a2c107cb6612784e25e039f4dcd07155cdc55be07b519c3087c368e275d591eafcdbea7e6404a636f4200e612ee651e0bb20d78d6fe30f2fa6f08b048eb67708dfb778e4f41ecbb3a89a45f94c4ec6d812c50bec87652e5420a74c35142ebba381d440b2d3efbf72b4ca1c8d3699a0018efb19aca8c7859537c755cfb8c9fb8c2c9cd6392e8fee601c0a90ef5a9b56bb4f07af83413e1ebb917662b9313fbf86822ac301b47184bae68db7b51df352853231e73960071bddab7330a4926b9eb1ae95dbe46924d60a2b6cd95f8ce413123b2ad7488890f1a07e1b31f368ca8f1428eb87640f45caa70042fba128a88ade7abec1613603b99c8f1055a18554e4b942aed94cbf141079595fed78a40fd577e55d87203b9cd12900601d17abea6de10370d73bf62e8e48a23f28a3dc5641013964b1fdeda941131f93eff0044642401f02413a79104a6598cc1289a786d1ebdb908aa19f1af25830b769da773cfe952177119dedbaacb211c27ab3df9e803d1215602c5506bdd5b34f97c05bd8b72ea8c6f3ba1788a401d37f1aef544a743592f28a497342225ce5af1ab117e2e3acddf7aad690c1d63fdb5b62dd1ce30607e15e3a1a1d44f14524363f21918de3d151afa8027c5daca7e002dfc6c843e60c2816249033824dd9e086138477544cfc6225cb63ff56c2a18656e73db57bf6703567c25780166ffda172e25d39a49f01dbc275db192b75685eb3f6ba540f9c2ba1dc198cdc3e670583aea161120ef11c9321df689b721047bd61e6fd32adf4424b231343c76715f392849ddfbcc913b1e19b2c828afe00cb15de46e3d4265b29ae4e5550976b2b4be07111acbc684ba7a065ce847941f29f69aeb42ebaa1aa0552ec06102b57695b3e3258c415cc9276c5fc1ae8b7061cf4e211b29f19ace5d9073e1c517258e8f64542117a24af92ae66b2359b525851f19c9cb272493dd9076664d250b52815dae419e7e0ff4cf3a9e36523f809115cdaa7ef4f5d92c4bbb0dca5c99d5bc84d2240e6cf94cca12c51cbf10a554b10732708a4e569e1ae3141af89292d2ce455e70c5c6012de25ea58dba6937ba5e2516140664bce3c20991863bec90165c14d3280d345ccc5b433dcc374898b1df85688d5283f69487a78e3bcc6fa1f203d3adadd882838974fc524b25e3b308b63d4c67822795064d867798580d44e2d9c37b0ac3f1c215fabe6a303946716131a5513556b4c23d4cedc99ed5ea153b09c269a5c4d0964d598ad614b752b8aafd3459a90d35f214d725d27a15335800e28381a78f1a1e6bd79cc1af6253ab9acca0358fc8c61b675ea6a38b1fbbddb4f7a8bfd5c684d8bcdbd61f155c41bffd355b7ed90085f61e9d8fd0d9197cc1ed667cd5315cc967b91d5b08f6e53ed2e69dc391f782d20c870ec957e9c87ee3ace30211fa762aa3d67529b78fd0d29d0760f861ceb4971e011f6dfd41320aa005cfb6f6b6d74c89a01ac0b8ef76e95142cb55049ee0be3cc9476a45824fecc478d1e6ba584d167483dd4d35e858953362e2b7cb85b99fcd57ddc2aa005ed28bc729eba6911517ed913551aa96eae75174f1d97480ece9a217e1b9ef342bb5d533385cc7833157628fbf35b12f72ca0c76fd0523bbc3233b9b613fa8b88577583d1d2e9686611fb9fcc4bf2064be4ea8dedee40c8143585e825093d7f7cac56b1e303e254d639b1e7bb0f2a12462a4a74d0bfcd4eb44e22e70700f4a63cc948e059f529cb868b473f37c63f2c45015407d2c370e6a57ad6a97f2c5657e8d72202f2a4fb3c2f8f57f42104414331300a6139c8884bcacc66b349c02ba35d4642e9d77235815e732d04e479491ad0b9a0b929a3f225785dc9ee81102b3929de4beea2f20deee81848e49de69c76e25d8a558d794448ac5f2724fb59f5d9013ddf7964115a8571760c8d0a9349a6d4a20f901c64639e82ba0469938d7a637db42e8b5daeaebe149be176c44bab9044699c729c44727c86571c1668cba05b7b5d6550be05af5f162ff20c982384a3b20dd34ee7de3761413fa26642cf41d778f395e8fedc70c5464243619c87589cb8a3a216877b5b24245796aff18707522b0273451238b9af08a891e02ad610fcd9c071f534097171c3775c2aab62fc6b28fcbde31e3e94462a71aa106951e12cd447783dea45fee7ef38f99179121258ecb04d1b2aaceec1692ba5ba87f214832fbbbe2a8267a583129d7c488950ef4d1f0a911e6d73cb5025082e32e22a9965bf5865b7c7b7b40a20029c70c79ff18b07422b79ca3b4fcf5285800c890eee9900c2284c87c05c8e5ee8ddba754d419dde2ee769016c51e73ec90f68f7f4f4fe439b16f3af05159f62c420631212c5f097239f7c6f5290ff5463770573bb814a571ef1bef964e24d64d730d709a182bd18de725e829f24adebb6b466622d631dda25beee1a26cc57dfadeb81cea89db742052beec56a42c6251fff947b331fe6b880ca17fee41a6e9449eb621f78e57af8ea86102df9d682258c85aa297c437ac530e512342f1212fdd5bd2036a98780d4064d380f45bc7db8e7a4f37ebd883eb5411fed55a4d53eb6df5ed9020463bb86f6c8b2eb1dbe35b5eabc316728ca7acc65519860dc627dc4a0a6ff966b8fd188e0215c20673b71450c2fb9506f3a2f2b4e6e1ef92fbb52e66938863f5f5b6a429569f590ff3400bb3682ec021edf3571818e5929200ef607c5fd91126504eab632cf04e7463afb87412183dbac82a642b83e5181043b739bedcc390781319d201b0bc9597e8e3b2f0a1964ffdd0e50cfdf73899ac4dda1568a959ab320661a219b550b36a2b85311696fc10eaf526e8519e95c2b1141e51274c0770598904506520ab70a73649188097b153a063745cf5066db5023ac39924b84af06473cf04181f2ef16f29daa66a24c2591665e5ea1943690a007cbae20a6c35c5690835845ef29c91834f1179de2bdc882513794e5011d9888267bffdfe50d283c8f42eea1ab393a1c99ff712a0307d9caef453162d91e4b74f53121f689f7e8257948283bcede76e3607f700b0c8451e73d410eb04040f8ea9a677a9c984d753e038464fc59b2e837aef60addff7a0bc9cd93fa84ad19e6b20a87ed73a26776c37a6e5845f5fde00c0f817cd3edce63cd4117cd6c0be7ec4a64547affe743ec13e7510026c366b944da0d260302e49f03b2157c8381bb218931bfaf55de3a21844f750ce39282c5e41e854c3bc8b0332e33cc69f01bb0a8cb32086b8a54250e78f386a98f108ce9331d1ccc57a062cb83041f966efb179f4b41b37ff73c7b35438622aadc311c695109520e1441225ff8b8995798deaad30ffa4a59a8f4ddc1c99458dd9a7bc92646d37f43a9535b0c0a6d1e7b1de3c0273554e0ccea95b84450d4228dce3854e2835ab7c06857b8a329d61a230d4f7699b23474340202a35e3b99d6f478856dc3dbff0819b77479c53d1bb0707ba0208c80d4f59334e8f041e2e3d84b8a14e3e680d350a3d3955a50f4611ecba079290635957ec7b52656071c6cfbaf209975cac1965ee47c89c0e0c0043f2e4a2ddec3a2c0ed831868d488977b15623a55716d61bd6d93c937d1d975b43595c3d08dd0d80372ef2cffa1f93e9a171c1264a4f973c6b90f3c76f11ecfd67747cacb20552258f0ac40768f83059b968bfe0c4e5e8bb122099beef125060c7c844880747d8fbb4c3c946f9052781984ce1ceb62a9a47c6c64e4d33a1e028e08b693195203f95a68001de2cfa5c8a0a79e0d0b90489d1fae7b95faf3f63b31af1e0de80d10c218af5e6ad0ab8198786a8125296a88fa00a358a96de25e84256268d20c7c51dfd66b7d713699ee15e429862354e014042256d369f527aeed79122cabf8b0529d7ce8a389bdf4b7d9a9f66cd62957b256c88a23fe46f5bee3350fdd094232a3910481e00a55c43b22cc1c0747ed73920d78f3659fd8a071fb82ed39a7f8f2ed0d86b174b0a775a89aed990627114e629b2ab95fb170cd5a97aa6c8f53e22867fe78dca147319adb960773bcaa5b7236f5180c1de1882ebaf43fc963aafc3dc71fb00576f27ea934db83382fe3bf9a62e9bac917020fd7786cdf5bb32b93c6289d947544ea6924da1638c8e9960f5ecb48c282e44a9f14c4333893b888860e099f49c9d838edc359ed252f73edaa2c84ee518bbe6cc4289c801c40704dae916935a309ad9a8036d017d95b81b7534825bf2d2eb59e7172b99af1215d352945a7b55d4f985a604e02d693b8187001706eb32008b85bfee0d409597f7ea7b5b741153d7f101e64cc00f7bc9101e6b3e07a234b27709915b4a995292015309ff0801094ee9f6a4c1e9ee350dce50c7aca9b973de50b145972cbed0e5ba68f1c3165d4202c4e8eece3e41f1f3f93e2d3fd5188f9452de78dd4012f27844375a104ff4862be4051bb6ecb4ecb45469943ffd192b4f7d9efe0cd68c239dda9efe8c9946cd9869903efdcd9b71a44ff43fde8c959fe86bde0cd70c240dd28e8df219ab06a9ec6f06ab41dab2d352a541fae534f84d6990ba7f6a3ed997d32095f275682fe7a5d2066925cb4ff23fd2468be2e052fa9bb672050f0e403597b63cf522463facf4378da4852b5c5a82f222563f2469e1d28883b6ec406931f9c04a01d4441cf44d3db01febc6723c5ff87205ebd2f07b89451c547e78c31577b05cda4fdf468e8d2902a8893b4a97bec7ab2db0393237608d0abf9c8fd5a8f086cbc6ab51e1c7faa6dc70d1bf81a4bf23365af46dcc748a55375c97da685dfa9f8ca77a46f06071477ddab27369a57109f002c140869beb4fc5865ca5ab820d1987715ab264ae9dec24576e4e93a0eb41ea0b69946b9e902035b0f25bce8f46360d31ebac5ba89ba1d9cde9df6d32efd5d5ae76ef5f3d914e87be50540589be5ecd132fe641d13c9836bd9a395f44454465a6b5c07a349a269279ca7b45ca491af72e06f4f9783697f78a32bc62b8893b463de2275a7953cc3959bc9877e345f1a4740a064db451c2149e6a559050b49a8005e268265a4d11cc26c1e2783827d41ee340b1fc954a20884bc73821d0a7a9482681205e0a7d58ea55cb34abf3787ad532b5e31a4923e12a8cf38cf395ab5c5f49c9612a8d9af14e5dc9f49146d5994642776815aa43673211cc26c1e2c040709b6d3adbce36db749c3bc21dd9622f5bec65a3b2cdb69b0d6793b2e56cb28dca36db74b62adc8a93e18e805eee4e677447ca1ef2324ec4e14f659bcbe394914b3627a9459a21b91a4591344a6a24181352934699a4dca2d496974846a9b74571ba49b9fe2d48d8502413edacb0568eb45666a69cf2b6b741f17fd228f9d2ab2db030d968c61380b5d8c0868c335f9203b1c0f27475ecc913282e1d309432b7e7bd3c184a861bbf6e1ab7f176b3dd3488339a79ca7b39e94b5ffa8a6c241f0d3a10979d32e7dca66cc661a971e167c7596efcb094ed29f194489efcfd91244362792f98a7a4478a71e2c877921053323a01f49434e8a30b850dbd9707bbfedb36e292b03d4cb388c37faee20f3d68dcfe2a67a31be530fa078c3efaa987fc968cbe8b69a9a2ca92d1cb6fe5b3679c734e6e01851cb8f2670db28d1f7ad8b83d6c5cd985381b6bd04211cd2026ba81b5d7dfe248712344e14a9a52db927d988ec540841808091bf35ef28be14605d6f472350a49a7b8d1e81be9f4cd75ea042b5a89563dead117db98e5e7bd746c68493252e53fda71ef158b512072117281651c2a9d6a4d08eb83bce168d694e50a89567ac40e94c48e6671070ba926eee04ab50536243d11c558b6416914b39417295689a40c11b751e9dfc216066e8870ab0215e20edea81077c446fd0814031b8e66235289542ac55045f7198e69670556ca10bb1ef3530d13ff50062664a0b93238d9e8145ad61f4b242379238f93d1c5d09f1ecfa0c1d1b85c77413774a5a723920d61c3d18c04f314d71a1b2bdde06481e53d8ffabc16ba42ac4184c303883bfa5db087955e7cec62603c4c8b565584c3358894f000fc59beb02f0c6f058ab762713cb55263bfb0c64432d08773fd3fde0a92065da4239a354865e529914809d7208a421c80ffea3fd16a757d0a11878b946cb73f916c93c286229ceb0f13e14811f916639cb8a3df29e3344af3d4e612c136570906b3b11d1b338964225983fd8d5c53b436b5176bb6980b2c6d2ef0869b89337531fc5bdc5ab619dcb602cbdfdded99e40c1658fed1935e7e4cc24a7ed2681571cc9f3438ad468d2ec6f2c77d5dcce76b373d49c416c413442a3b1e9ac70b6b5cfa9b17962efd90c7c970831199390de2b80a2cbfe51adecb53d2366e7fdecb654b5f473adda73e354be914e91bedf4943b9ad92876f4e107e77a30cffb6378de2b06db3c2eb634b191716a136c389ad53bf2fa412f1b461c5783fe5f17b32169d01f0000e8623c1ff6cb1b430c5d8ce765d86cd8e862badf22e304c023802ee6c68d2ec6d3830b79ec1a605282edaa46a77755a3b36a7486b8de6edc9841f4337431768256b2c8826a539b9352a1543c9e4ffd7c3c559bb47360e716e23a39b710d7897e0b71dd0ac80b8988dea5c8c670aadc183302cecd71630cc78aeba9116915cac1e5e862badf401c773db6efafa506769b82260404cb32c06a5c598a5b8c877643df64c53c72fdb55fe3bcb04658d2bcd06eb1500626b4ff8c669f7ed32cee78c51da3ebffe97ad0c9da620d76251df1dea275595a2497a764fc4609259d92e9565bf840b4da62e5a956f9d7f698348a75e49d4f658d0a45b22da6042b9f6adcd3e7ba184e24932024ac01646c25b0fc24130f1aa39fd1fd0bade9157198a48e52c5698fbbd153a38f343ff23c98d744c45149bd9757e3bd3c1aefe529f1601e8dd7c473e229f19878308fc66be2c47b6dbf7da88b916fbb6679ea8bd476b3e1d8d8622024768bc51d9cf79ab23315b1b918c7236343c6097d7f1659cc494325c9590aa9b5cd48ab485ac934aa7b7f12abf3463aa32adf68a741d2aa411912ab1bcda4b0fd9366737ddb4bc230c9bfedf22dc5818eb01bcb53261d621431ed50fad18fbe204c4b485ffad1973eaa628cbcd18e9f666d00df7236d9e59fefb56792cf2b93fc7060216e7f1eac4107b16ce8bdbc977bb0464591482492bd48068a64e188c48160609b9aa46c3997590ac913cdda00fe23ef01ad12c9b4b0a2558b4ca3e4164b89607ef2274d92ccf588b3c3e84b5bb061c4e16e2892b5ac5a44304f89642d3f14c9463b394c15e91bcd4c60e70799800cce029df25e1da9e8dc70341be9f4c9fb1bcdb8b02195b1a965d7db74d0d828130f1ad7f48a38fce5b571e36f3b5c5f26f9f1b8db01a81544b26d07570317638af4f1871053a44f3413ed18e1fa6f36d8d0c57c98b7e70e0a16f4dd136c48657183d222996927ee58798af441444d44d045d375c3d18c45f2465e741809406243efe5bde28e4e046b11ec3a7dc28a609fdfde2362d2232e5cffdcd16c948598da5cf70bd73fd4c53468c6868ce33f9a798aca46333a922f3bc8ab09583fec76e495e53e5c69b91a15eb87e69ca1919556832b332fb87ea666b2c2a4c18f87df95232259a3bc3f5b031b8a641d725eb87264a5759d88f9bae168d6b2e5b7a4249790e86cdab3fb5b32fa78ab5ca73a944a835efabc17e91b792fd9f5efac273d2f1b402fb0a148d681c186015e1d46d2f0223f8c3957018d8a3ec042e6bacaa35cbfed7d0c19db1ffe9319119689f5889db92123f952c9da7f89373f23d881d650f0e94f7b5210599312ca34c9d7666cc84eb6296a368e714a57d8edb9e3747638ee0b59093f61d7e7dbf3946236e4e49c56844176ad08434ec31a359bc99df2e327ad96483a15553031a7268c393535b55a5b2afd83edea9bd9852bc428d46a6da9f40fca2f04db75270c9cdc1a6eb4412273650d4e2a77be676571e4738ea7402738383737311d3a5e5e9c9dbc90b8c313127138f55c9ed877719981aed8a24bd8ad3be387910995d9ddb54dd378db1ec699dba8b36ab5f55539bbfdfc96df3ede8f8cca6c87ab747bec6a1009bf2effa6c1748b892d6ed3679db3d6dfeaacb36a9a36a5bb00c7c605311b17d4508d0adbaf39c17a3e7611f0bc9663611aa6e76fdea685594d086c5871b0ddf5983caa56bf70ce7e2258ade713ddddedddcdb5bbbbdbbbdb6fc441e59cddddeddd4d5b879fa2edfe423adddd3f7777777bbbbb7d748cdd35babd1b65bb7be56ecf7cfee89457760dee6eef5293183f1b9ad2bddd654b1963d4d17dc1b3b0fc119c54892aabac52a3b26a746251659573ca8a4595556a747241568d4e2ce8d4aaa453e3c2a4746a58c84a27165556c9052ec88a4595556a747241568d4e2cf845086eb74beaf98f17e3297a78b81e1cecf5ac5c4a39239d9519042c25cd9c936af2b5e2417601ee67d7a32f7dea40224eb5212211c0eac6cb29d6c048c0b883fecac503f082744b80c8f942f27a315db9388afc5c5a77b186e6619d0772eff12c790f46878e5abb987afd3d1fdee15afd8f1abf3cd6c02b1e407f5cd26da186a768909f34afb806c6411c403fc36ee7e00bfbfae784fa7bd75ed1a9925b770bc58f345694b835c6a05c7e215136bb619c451ded4396615695cb2b70e26a54549aba21771170e7d72adca9021a445401ae7c37c04c154fb99429ccd8d63d42cdb52d2eaa94f2a5ac5283538529c44640052c54d25f5742f1842be50a40b041fe6f901fc64c143566a2dc7eca85cdcc912bad605df88a1b722950c195d22605545c69bd4ab63366125576e08426b2e842042a47a28a24aa689d1136aaf823c3b8d2e5060f71431a6c884faebc618c22e5ca304e8954ae8c3a376ec832574a29955cc94caee4263712e74a1f5cf9d5961e7cd9e2cad701e3038809a470e50711e2822bff023c28b9f23b50021aac5a336039e14a1b1340e1ca67d7ab0505f6baf2045fb8d206053657d6748bc60db8d0d5c88d92c1881a7421d840dd875cd5fef3fcd9366d033dc7759ced4adc57c488aafa4982752e11126402f26384dbab7810fddbbb1784133af46f5f10fe3faf94dbb64d5ae78ca7094ecfc3f4ac73ce5a1fa6ab67f6e64e581999fadacb7c806c2d0dca2042fc24dfe3f1e027b973df2a61a3aaa3f71c424a66081daafaacaa8a37b0aaaa228005aaaa0e55fd307443cb76e7be251e4adb536bd55eb4af881115e8271817987571f194f7cb9904e536ad7aa8901e224a3ea594522a29a5dc531ae44337fa7998e6bc8d7ea807a37a3e34fa2126f4c5a54f57ae104a29fd172b7187e4964c5a8d9a29ff25203f01bf9425263a09aea97ea2dc77ff7ea2dc51aeb3496ce8ed87961041e41622a2e6db974f631b71e987fd624aabf538e4d16fa6a1f47900e229966db3faf9c26a59e6b57ac83296691f10d012f60a71876b5267197d9fedb1414b422c3fd1d057c4888a577ea22e3fd19f5e9106e9771ebffc449ff34232b021cb2efda6cf541a45c31aacd3a8305661599128dfbd46dc319fbea845db53821bfa4318d626b1feb5b6770e1e4f143e7eeaaa3965a25b1671e4258382f541b245970d1485edce94037dff1de69bbe3d530efe9bff66daa17ffbf638130f36f570fbc66b52b96762ce33a936af1a8c9fb4f7782f7ed29e3d20d54f5a3581ad9f900635ab84e5d7be2171d6dd336895db2496bef61a882918f1c42fa48788627d0562caf3120c573eb7623cc51ca2aa56e085785ec6b841e28e18a518058fb7c58e0d5ae2d981559eaf881155c94ff245be7b21256c28614c00888c7fbfbfb42b8f6db5d35e7bd01784133a749f89bffba8d23e08137f2620a07f405481be7bd01744112776e8be20b4073de833f17bde882a08530ea0ef5e07d077cfdf7d459ce04f87ee3d5f91edbb2fc23de88da8a22aaaeac7f8d0c303a2caf33ec4d007e279fed04755f54caae955072f882907d06bbf03e8b5ee833031e877e83ef405a17da61c3e5fdfc4df0e9fafa61d3cafbde7b5ef7e5ac0dbbc7ff160dc559d175551e5d1fc02426a8ec7886c990396b69dbda659e2e048ab26ca68d537312436353128363956b0ac5c75ecba7b943ef9bfd010b98f399e23b7ef1b4f715fec93e7fdfb7cd64f427bc3bee16a9d2db484041b7a8ecb5b316f4cd0739d3ecdb2b09286f9f272bd6fa4dbf03a9a603d54d8b06f62cdf462dff40d6bc53da188d09c52b26466240dfa0ce8091b7d90a19422054aa77cc58a72bdfde67aada5d23ff8e26c274f66fa52d2c91486a3602e59462352f264f9e486f2b603348f1155b040e6c62a9898a982099924b31b6d92b86e8d1b6d62b073aba79886c64649ce0d998606a7869f58c1862d73cf91b4c8df040526a3cd94ac12c039323509eca65668da460924ed0e6b54e82d67d2283f62497d249daadfef2d3f49fa7dec849bb4ac651f107c63a8f4ac5155588d32f9d7526ee7340e15d96ba36495801bb27497d069a42c715c8311a641fad50b417d8f7bfd78651123aa0aa47e3588d59d757a37c01995063b641a19f7cda16954934639695477aca534d83d651ac186356c9c96795381718b90a9b2abe422101d91a01cf52227a9276bec5a9d9d466463b384b564d5b2d16689d8ad29818d364b1c991fef1232b75f5a227488982273a34d13b01bde68d3c40a6e113a5ba847bcf2e534523bfa85305a2e578a462a1702d1577acb4b82991b6d9ab8b9d1a60927973b1c5ec2a06f0488beb2148d4491c624dab0a4f477ec28fb5f7e4858bdbc8c52ca180861dde50d5b74f95861e5546dd185ffa594374958fee8ff430f1a3d685cff9c75002501250125012501250125012501250125012501250125e9ee6e8e99bb298d6c69b3604159b00fc2428d2e402f2b92b939b618638c6f84f987e9eeeeee36783c221d771e209ea68e886356ff524ba73eef729aeff1ac7b37e80cccc55ab9247cdbb60e14e1d5c7c371bd6a14cf667d17d3df8a79c52deeb7f7e799bb5166cfe779ed79560f73778c92888c32ca2861b0b42dc3eaae570dba911dd8b05b9f1f1d0082b906dd79c52c9eed3428032ac2869107203ad028fed7a6841b38b1fdfc71868d99993bd0df2897521af1f81cc5f6b4a7ff90f69c8e8273f96794db3e39768ee5a41ad5aae7233f1f6ffe54cf6763fe6c5c0792209037839a378e39ee744234cc176eeb40ec85dde4f2876838f62c33efe8b44caf74767466b127516e9ec49ed4b09217cfb08b5bad13a7cc29426868a641c7e56e6ee6666eee66eee6e6666ee6e66ee66e6e6ee6666eee66eee6e6666ee6e66ee66e6e6ee6666eee66eee6e6666ee6e66ee66e66666666666e66eee6e6666ee6e66e668e5f9ce109e865638cee39e79c2e7da367b468c5b460dd188bb2736f8c45a172632c8a943b6f8c45d1e27637c6a2c42ed712076c6620538a013b07c1a68999e812e6985dfa5b9cb173abdc193a0dcaa8c86633a4349833434a8374c6944b3f8b2ca23408e5de5c4ae34983f4f4a27de9cf8835a89d5a336a4e33979e5c973e89a29d28a29d258668c24c76dcbf192cf9335a3746fb4d7a9b11a670c525bba7d528f6820b4b3fde6833c4eace1bd2a809575c37a755a35a8990249372e9d378d229a9a23094dcf074e4b49a51737d8872e98cd8a53bbc0862c68b8f2a9bc569e5a996999f58395476c315178805365c719d565ceb86530624480c377e8c1a292b2ea90a576039643b74e5499a6802c594690f4487209b9d79caa44391f9d9d91b51494fb4f2938846d3887c6a444c1aa42c62b52772229aa2a641faf5db13356954143111d1889a50cbadb838d7c74e8976fc44bf6b6f65b6c2b2331e36ae6867734d86e9054a3c06dcca4a4bb4e3a91c37a21d5ea29be52ee59c946a5aad4e786650123303cc33c3ebf3d9368eeb64ba2ee6c492397272c556c4013a12736a753de43db1421ce865b70f753c44f7d40a4000661aa4ad2ee6b384d51ec64fae285a59717f4dd334ed3443049c71219969909e5a0dbe780103064b177362354845be22ca91e32b2bce6ab10c342cd68ac873e4342acc7193634aa398090b894482f182445a119148244b0a8148a48e2403e8b45a61cd6fc5884afefc8a9c587ea2dd813eac31368b40a107ee691571d0d78ab0462efd8d94e3c6533972e4b8b9f44b1d0f1b37474d0b6ccda5e16925f2fc69f5e1c10df96a4fc3090d270dd21fc0751a348dd2be7e4aa349a3f883d160d22827362eada29526b58e60c63959a981e29233829b4b7f05e629a9a22fe24aa3e6e38a38e2f524588db2475a8dfacc9ce89f90506f0f880e0dd29968b5f2414ab9344883f4b46245894e5cc4a4535545dfce7c6bd91a5193cf17da9d4bdfe6c8726e3865b04b7a790a74118cbee849a362855d0ad2dc90e4443473a9e87569dc9cd417d81761cc0112c4dd6d64aac820cd6c5e20e3f6bad6b29cd9cd4e139772130ac50d671577cf577b5d6ef7435ea60f4487203e3c359f3ee8d1a3fb8b2fecd734afb60cd7f34dd9a4d220fdeacd5983f42375d9fe15178d9a06e98aebd215d70a12ba5dfa2bb195289df2e4ac2899b9f4b46a72e98a934b576a2e5d8172e90aecd2af5a9f568d0a69d49c64a846a3a651e194695346a72cc7aceb02e0606b6fe9395b74a9344ccc64366e692f1b18eb32cbcc9cd795a12d98106e4883b708ec8d4c099c9d1bb85826b381cb062c3660dd1feaed21140358143951e8d0908d8a727eff2ef85845009c24f1240a58144f6ed0763935273649c854a1647563154a88b8f1230fcfd5aed7835780e375f38d7a69c2e4b22c1471f40c52ca77fe86f0fb8d77fa674bfefc0de19777083f8c52830d850d43a12e821452803efe06f2a2aa763fe70cfdfca9ea2d7641fc39ad87faa1ec1eb07dbfec80f4578455fd204faaa206f25c15bb086c527e219197cbef7998ee8ec7479326edb77e0ee4b52af499b4e73eaa603a7611f0ef4cda0724fa69ce49697b131ae88006511544f7ced476dc05f17c7d1db82f227f2bc2aafa9d9dc2522a83f097ff89dd0e9ecfc4f533f107a4bee75915bb1de4734c876095ffe659f7d0efad1456fbc27ff110d0800734a0e209273752f104ec2ae1462a9cb0e26a1f6d2cbe49d5b1ed15b633e097e3176195f65e3db14bc9ad9fa20724b22cf8560aeb5f8c600f4e36af199750e45c6fc2ba2e00f9dcb94b77e92ebf21d2dda5bb7c7f59efd6dadd5ccebb4d7937ea77d39aafe7030a8d444fb16c020d11074bc9d2dddd67d54c931ffc960659c62867c85dcbc5386b832597e52fa1c07694097c032b29ff1248e0400183e2090c9a19176984af6e6e7fdc116f8ca1f4d65dfad43a6e7de957ac5cf41457e9936c0dc9ed76a9f26a257df27ccda45f35f2d592a6514d66c9a7afae9c4f6cf2a39473ca66e11003100dba03b9f3397604a2087f7e2b5d6196b260b7f744d7a234aabb188d6a5b0c3f69d0b9662bcdb4adca1a37da5cc90a36e41a4d3648a7e0528c9f0693d830ce580431f5e2a0cd6304a25342fc2413773c0f2f9e6211c49387be90995c23745fc84a5cbfd9be9061703d765905d7bf7e6c84b34cc4e13cd42d58f95ab06c4dfdf109176e4cfd552ec2901b18b6e812c618638c316ea01b1b6d8e98a243a3a2a6bd0e4d7b4da3da0d4dc8478f9af69aa669df1f71680faac26a9a276109263eee8861c629ed9476beb06a7387b5e329a6413ccdc7c18a7b7045c431614bec84cde79d5aad2d9558b3d209634dfad1d73cde8938e67fbc606bbdc1165d6e3438e701eebce2345fbe900b10b100af80e8700356d053158802780550fb847057b7b0fcd463cddddcdddd9371c2433e72e99ceeeeeeaef12a89e5cebfda85f587791f14c6470cac2ba041ea6386edf9807162e7c71bc61a145f1f0ad0ba18ad6380012ec77079064b4cc13c8cb8a39f1986be0cf785918bcbab9813f22ae25c1af2aa7ecc3311077d5e8188d0d2f629a5db94cb1e7d496d8eb8b9b173aed199ddddddccfdb93438a90cfadd1030a2fafcc61c7fdfa49f0b909a09e8e0baf37998308d62fa35efa739690ef134ff0897093f5ffb3e2037bccac2165dbe566b4bb19b48839286693c858178922fa594af84122b630e8da79899b9c52fab16f878c132bd428ae4e9ca7892b546c1faf7bfd8d0a9a8923647481c294056d81c5173e5ffdd420db2c23233337f34784471761f106c01850b1f3517f061736060c86ed9924a2aa99c31eb5384a7b360eae4fa47f762f7e3f978fc0836e416cc5bad96a7e5f13ead176e79cab65aadcf57fde4ade28f5aad5e28659c756075a178b4b021b72a939a16d31b13d830586e005f1662db3e443017ba65cffe823b1329e968d093d0b28b39051bc6588eec6ed4d3041bd3364f10b8d618f5413dcd07e8c353b1d8c7a208ac29095b7377f3e76eceb5e7f2e88f38e24f7a378da3f36e95d33c5cdd3e9c67e33ebe753c3650bd120794524a76699152527727d59b8a1657b46c77a38d1653f8115ad4c052aac5924b2975b9d4857ea04c8d97c0866eb4d1c2b585b537da68c1baa0162b18364e9420a9f0c251b103ae268a2642d051f18318382042088e0a27bcc0010b449003264da298c12c0433a0a2092a6cc0fa5a503f27075d8e14a597249774a34dce111c22148104144588000c76866c89103a6661dc58051152341756c68d5510f1e241844b070c5924e16fb4d1810deebcd1260a8e0c162fb07881f42209e945921749727832ed546b8a99c81f694f7ffeba1be58b0d9d42dd7039d0a969799086c9a811743f5a3a9dfe52da9eae6e8fbbcf1a7fd63ed6d2572f81da175dfcc38299d3b96e0dd788835ffe6c35caf919e4ca91fb413973a412482f92a0c00639d8810c162fb078812d8cd9255d7f1806b4e4dc788d200727f09e0cf3777fe27a77614ca4a4a16910f409a301931bbe8d711d069b3739d74fc04537a8146de9a594923bfe92a79cabb5a5ab2367eb8f2f62ae88df90a00a1355910fc42db6e8812ab6013eb0d2050994b43077943d44124dac54edf1f5400ead9adf3e039c1b6d688e7c37dacc40765ffc5d4620f3b9ebe1a014d8eaa1cd0166f9c30f7431128991ebbafd41c8aefcba856be058d356d8a24b1867cc3c97443f3161a31424ad925f48a45dd7bfea3962e3b5118711ec6c76cf37c20337f9fb0073e71b69ff78874c771a4fe3f6d3488004dd8e35ee16b738675ff7b80b217ac591a3834c1ebf5f7a26ef587363177793dc04552cb6f650ad7a3e5c4888b04a90ec1c06f5eba1c6ad3ceced8167b7f6c039b7720d3ce5268446a5fb4b5929d7f1f01bfa7fded3fd98efb16b40a55fe8bf753cb6afdd8ff9b57570376f88e7e3fd78433c9f8976026efd7a48c0ad2cdb25e887be1ba0b7be46bf1ff3f31441cb4203620a8c3e7088a839803b5f0031a523cc71e77747ac0f4f959a60c3d2ed7ee2403fe736c979c77d9670b6c1eee676cd96950eac3bc129cc34364228f1d982cd0d6395c854878ef9371ad535801af1ea1cd03c280dff7e9e3faf3d77311ed0f32d61bd8afb6ef4697efd6c83933e5f2c6c186377469c3baf56021b6d9e904d7afb9932bd629ba297347cd00725fa100d77e5431d77c5fbe4bfd858abdcec7ef8dd3ed935a06f8ddd038cbc8046882caca00a27b458822ac6d3c6d5ffbcf6c2e30be3d58b44b81bfa4a0d7e3eab9100b46d9d2723b8fd16c1edf371abcfeff14a0dc2b02b3ca5d0313eb3a6c5d88de2da4ba9ea5fe839c2768f4ed3e6ac1faff649b355fba2155bff94314a7f193dcf5187d5de1b55bfb9f7bce6d5777f29995f6808e472d7d39efa7288fcfec2974be4e56a2febfdc1c8d5be5f56d93f67ec1a07ff7e30d23580bee40b04df21b141ff213e41febde7bfc62b3904022bbfa50a5b5b5b7409690ee378aa5955b4326928f42ca167193d0b0caea1499cfff49ff5e7fc5017e6877ea3c1919e8544fa18a3d1932a3f817323116eb47162cadd6c7cc703f440805c045d469fe7bdf8182c2c1faad96a743f3814a2d1e1c0f2a18f9d0bcbfc9100388e3bba07be21d297483f92517a920c8f6f8dd1e8597e9b818b30edfdd3983143869df33f73cee9c528753c406f7f0b00d72c4f954a94d22fc9968e654a9ff4a16c1d61fe4da39a1de03f6f68148a43ea7ef8cf7ff11be0fb22c6bf78fa840e09fd7cdafde0da80fa1746874368f42ca497a718a7be3c1555d55251c7a3ef286cb92111ee967e54fa70be8c1f329f1477989ebe3c41dcc1f2f43716193f59dec4f224d2b3fcd6d3636179d2c3b4697ea5ef21744b1fd6cb7259be547a8e3b98633c6143f7f3316a6043d98a01b3a15c5d96df5e622c593de1e4fa97bef4dcf1b01fbb1e432669caf8c218a46f488c273dfd10e9411d0e31481f0bcb778703cb8f5e86172fe959bc0df02581be30c6832ecbcfaec710d203c177f431e4ea52c96a14e95b35aaf485a4df9e8ebe21a41f3d7dd0e8bb0e07d2a8001cc71d3c86801e08befe34c658e2e9787017319cb021ef5cfad2f5b2b53d1dc2ef79fa1dd7e1c0b1c1ad9de33641b1c1d1e7cdee6384f17d18c38c5b596eed76843ee46ead47d8fa9a0cdaf2f361c0f80b2f5e9017ef8ac701c173d5a81dae425fb3a325fac142dedc8938e4d397a76617e249fe09ae7cd0179688b8920a415f57b2bdf25b3c39d327b97d12896c35285bbe6449f92c05677e52caade5b2fd7ca10572eb4f4a298d5e3dee32cd29b55725e43f3f2044e861b8f3bbce1ff487876175c30f0871671777fbc0e560baaf773f8c78175d5c186f23eec78b43b48fb77a43b4cff483fe7cfac9e7eb61dff9d50643fce13681e869a743848706e77bccb6ddae76beec62427f8088f27ff1a08f3105b217f431de603f2c5dd0f3eb27f7aa331e1ac5f7028d72224034c8c3ca5331c655fc42fa9fd760bc39900e1fd803625ef9f140ef02f746770b909f19cb37cc750b2b7f93948dc059c49f367e373bcf4ba3a6d53eb0c11713d81b2fd5635874d2a0565b6bb0f9c6ad7f37f3c268ba98d95285255dffd8e140bacca4c1c94adc5f2cb9eeda8525dd68e304922b7ffbd0862b0d70258e2b63b872c6e5970c0970ba5114bd4e3e219ee64f8ed2a7f973fb42b6c200f346a33d25e298ef2022d850878a51d1c609d69d1f04f419fb86950f5deeb4eff37578ea455df992e8bbb19f8e1b2a767eb7b666d982e5e4f88b7ce376ff0e62cfaff34e193f6a709f929b53d6e0430fdcc56d8f13db570e911bbf04827de037c5db6d84fd177efa376868540f1f7817f08423eef07cf7c0b7bec7f3f96064752b0f98ec5cfaa546511b26472e7d977e41bdb8347629c8826e3f6873eee3993e1fef10cfd7df3cd3e7fb71c3adef03ccf57c261c6eb8fd375c8f9fd61d6cdd1c638cf1e327d22eeed831b2c6cc517e6b1f4fa5f2e3f10d4e83fc7944845cc0029c93334586840f059474c45b024b0fdeb85ea76cf10183fa11a4e8a8b44d1566450c198d00000008008314002028100c888462c160301ea99ae23b14000e859e4678589b49c32087710a1963002100100300006000044606db00717a79d06bc7bd4a04f2404a6cf04f531b43d1829a4cac527b03fe8c9c34712b7ece8f9860f8a20ea117f96dab79d61aff747f86413a8c25862cc5283099a6ef92b67ae92b03c81bf22b1d795ac2c3439188306b89df4c6258eb0edf37373e8c667a4598735f0c606c5c390dabc33bfa0fe9d63ed948b52b313e54be141298b682660d57f0f0644bd58de6ecf5175ec57badc26c8a18519b086069215125de3959be84388f37b5397050dca6094b692d9ba4b4d076fcd7004f5799132de5e17eb29d9e8ac60c7910a1148be09e4579b84cca06e421390ea83ad44c82c91441e6acfa0b17ab47ee74d65f498ba1840702ecc065e20281224e7bda082791bf3e2a724acacd089ff6854667330ff69655fb5c6472f402839c321e2b21ce18687a7131e8af0b891c09795c1857b862c351d436af2c1d1750ea565745c778311099ccd1007cec36837462f9ef6f69712719fa5926546157c7bcf1c3010fc4c0e0c37891b6e706a9700b7f6c00c8fba607daba9181708e39a093396eb5b397f16a2f9ad1bac475cf509aea3f1a54bc4828bff147becb7be31626d636e398a359a569c3737fd8169dfc4479dbb9c732ec4aa37bb78f17ed6454af29005ac19fb6bf819186c3ec0f76d6e47c0abc36b33472f13a792fd1ee0a258d5f7a48ed4a6fa8d7cfcd2d149157bffdd218a0af1ad94fa4195974542ac58177f8b2ad76d7a9861acd6a802e1814819a30dbac8dac55ae5429ad71c5970ce930e2a817e10fc99155dd9cd8c5a6890315ae7743eacadd217ad7460cf785d536749ab8af2e2284ae6e88699d6e756c456d26a7f916012972a9c01a7701a5a0e9d552475bc8c345c2af969061a995d71a3df8aeadb5e45c3e4bcaf4790d30dc49488d144093fbdea23bff2d3e0aaf01b34b5ac109c4086d543983e93d4780ab57afdd84b5d39daf006fbeb07f69aff4aa2eb887276fdd10f66f25b68a72941da9ce263879d0fd2ef899462e310b4e3807d211672125520a2efb60117448778ec4be842253991abeb21b50804a1dfbca446ce2f094da3a7b2ff70b7e6f98cf35b8da3c5a07b803d97d092686205bee286f8326d9e96f3cd701f929eaa897a4fc084a0bac77ac9647cffe567162884b3d9a1c87879b0af455f1a66446eda17bbeaaa4e95b80aa688ef5acd04e53c3d9266c310543fce054b8a3387a67fab079467614b9c63ffcdc293b8a607740b2b889cf3a066c098b9bbfd093c29d2bf6ba1fc700d00308099e24ae45a6d509c8ca3591a11d3fd88ceb3af0deec0687bd1ab8131dbfebeaccca7a480544afa01c4c29fdc8ee4e171c3899b95084d377c19e3c626eda5bedb6ed327d0230b58904e15f4d0d7485dce74cc094d595659eedec314f4019f20eb891e09617ac897502cbb4de6be35457e9b348d73e61fcba93e5c931eeac4b2d6b498a989c9a2b4daef092e2e552b77a8a0508882fc67f2f0a2ca15b90ee488cb199e8dff828eb787797590fc6b50cc041eb8b78466210000c366b458ab9c45554b4ffa73e0515bd4f1b99a423e7ea8ed144feea3a8256fdef43b34c2f7a7b743181897822a2dee10643aa55076bd52724e481b9a16c900786492d9603b80bfc86be41e068c458d18d209ef4411e38907ed03f041fec746b28ced82119863037624fde21fbdd440f8ff7a5a5c779d7f18d9ec5f12021d473a3babc307c67d20008c67d868f39229f14acf52617d0d06dde2d0b84404192269db5c509c3d7c1e869dc2d97148b073c2bd6a928276225d174f1b0ef6840062f708ae964e8599f80e19e15ee86540348e06158291920eb6f6a2d3e019f72141c0f0db84b6a2978fa2f99d9bdb922481a551a7e1a4aefd3f4fb3cf14274f9dc71cd2e140a3f30c52661915d6fed62e5c2d8999d308687054817feb9cf8521ee55c01cfb5c5d51178ab4fb76c5bf40d2cfd699e09a49d90a059d0c7172cd98ad18f4f727c0752b144c43b016617f5e7b34e42797c432da50750f79171b1ab6d9561b6863fd884c942746ec8758954d70323b5315d01aa1b65920964884f63f38ee7122b26ec295fc5a51d3fbcad708c714556899c8de9e69a14533a73bf4e21cc952f48a72054273ca0c330b949f7ef444a882f2187e4d391af6fd74dd7073219ee71381807d227ccb46287c9feda297abe5c50e04a6ed546ac7a7f37feabd384920a749866f56d6fc2147f94dd0b20f63be10a6ea697497b07563683f07f75adbf01d8a5c42ead8589ea9d7fb9869249d05e5a3b20e70d3281068065a1b00cc0c909c97df169ccbc8dc1d75828c4f680ec4502a3232e45236754aaccb7fae09300b73e598dd699570c3b555b206cc25f35514dbdd8c45e0f5ca71a687f78508a1fe81c15f4ec0744e231d0fba12f834803fa97a5eb86bdeb6eaae85ec41169c1499f2b43b15956d3ff40e30969aad407e280c46d1f4863fca7ffbce5e50fd187c8c7435f66dd428ecbaf9bb4254c63fba47596e0821929ec9d29f47776c9274572d50508fa94192a50c1be6f452d986f29b30d664f597394809a55b6ae2bd39f79f97b5a192fecd0b185a43f778fb9be3a98028db575061905170600891161782784ea975fd48fbfc69d2140f2573371f7d348f7086b567e6409ab2356f45d5b9c396ed848362a32846d286b19344ce5fec4743cbe08219d3448e89ef1ac651058225b0ad4e00b8e8ca81592da51a415fd825e47dd16636fdbd35e651f8220c9fc17d965ac744d932e70b733ca8c12022cb56d4b1940f69d728a24b50ef14f335a91803e63c7a4d3a73f02d17c11f63dcef84d37526b9c12fbab01019255bca36d791e94c64ba1209e01e34a8e3153cce519e20487f0359388ce572626ca9971aace5b38559c718a400e8a5e88115a8f8583ae32fa8266377de5f8f8f70fbbf81d8100bc14fb1b33c4f64eaa46d5b97ec05eec50011d74b740d9bfedce02271e9f594d2a82de0631cd89b4cb6c2d44f1a39fbcaffa91311ecf11b6d5268bce86660410668ba7cc1ab419106ce8ac2874e390716f981754888b1d3890a14ef9957bda34f80ecc563272d98adb900337e5e7fadfbc11093b1817252c87d9866dc0cc86f390422f185debe15447c6427626216ea940347058b15ab17ab9290580362d754148da5fa8c47f5abb5aa5451f4394cec4e502781b4d0436144d9876b426b4de3fb0df1802bc9e357526911d1415af911d29a7b83fb09ca4b7c157d4c0b17229ebf3ef02944c45708102d3004032b8750676b12539f164289314782d4fa8d44185b3f892065af8d68bb87e01549b7d6784d8bdb444d2280e02bf193678f2aa598779c8793fb996a64f16cb57f45b6c50dd08eb97f0a70e90a38fab2fc6957ef43b7d41bebc0a51962fe2be6450fc7586e08b267c5e9e843fc7d4f36125b5d3dda6b9f178780817a02b6f798e6f3c5b6c91608fc275589eeafb1e5e75e8e051c43f966406ee6542452993dd28ef93edadb95a013fb4f46e33949a2efa563db2267aa89859b2b764da60148e04de5c5b302f3a2fa4e743075f597f762ca12b8e67fab321c9eca9db282359a700bbde14593feb5507c5c2e6a245cbefd2168e3571a19af75ad3c33c1d7eecbb68f477e93d4b83875acff96d37226d505108f5429510ad9835faaf7e9e824cf49c218a317e3900e0a110b34b4e7b5759165fb1be5dcb7fcfe33ed7386283f81fbfc53a92cb2a5c5e13bdbadd2392abae3f2fd142c0a5c8fa199b201dfa1cac58bdb8383ddd1958fd9708ad486719d19ab3d662c62e98af1d1410e40c88fd655819fd43c5f0dfdf2d12835b6d0475d9948684d28f17446508a91c03640ed1d5a19c68f373cf4142efd7df082fb95f6511c307c56ebd2a1c9301c8fb8401bb5bc62acf5c27513a035ae37224a6d497ae27bab044c6590985e474d4b31d72cb5b8ac0c725d3e0af5a3dc834c582f7ca27e5c9c73b68702effb3d9ea96c6ba794815c7391a62d722c990f1d7407c0bc5ab7684252713ab24c15ee81e3fe26aed8942826286a50347e351f3fb09f18addd0a914d7c5872ca1270c9bc1b67f52b18dd571f785de22ae1a81e90b8c8b60f4866754109df4c5ab8ae44da64bc50624446a3532e4fe6708386d000c2e55295e8ec93c6e67a38c35e4d042028d89e3911fa70576adc797a1cc50a2eaabae78a1a643f26c44585cbec67b2109fcb8eb4b0037fcf40a891924e3bb1d7a08687ce635dba31e1354b503a3704ef98026b464370b54b7ac5e2181fb13e5fc626c4b5493caa960e3eb2c58a6d1aee0e4f1476a9bcbfbd048264d80c370e5788985606632d29e235f84de69315a2f5666832b9492730c542ba548c5c2c6a11a82f73f724d93e49b2fe14eaf984889f36e8a550dc8d623e1483aef260023f5df3214e2a2572ac59e8c4976ec6e1c5ed10579d9931fa1e18e1e63af31bc4f4a2cfbe67d4cb8b5d268f6d55e81ff8cd4e36da9908810aab7647cc001237fbe47de0ecedc7818f74d44c45d55f1c54b71f27febfc2196c04d3b168d58ccdfe9721af1197ce46c6aaa74b401741c2904c2e8a4057b297656b9137caecefcc590b8356f8225f24f1fc726e96e2ee985d5af83eb4f1ae73c13261c0323888a1856cf3192e9bb47979eb49b9a11cee7881a31854d4bbb4611f071ba65978427e12b1d0a94920002b960c0992a18d2a43f25b240510bf459b4b00f9487615860bfcb0eb79cab63631213c1559d943f8a8d4b0ecdddbfef462543283054205b1a3ab4b8c623e4688a71b65d9b3747cac40fa7381f2fa6c090a016e329eb7ed961fa21ebc2d2259f2b277bbe62613faad7d270b10491da7723082f62799dee038aa08c929b0681578d43b1f897b8786bdf58f3b01cd63c264fff68f6c9dd4882c10259f938ecea0537df56a36e0297d2af854564c3ac7350d9c77d6d1998959142b5f42a919ae21ea65f84430a02acd71936bc260332af1acca57f5740a147976f0ba9f674e0d92b8c480cd912835976aa667d394c575c8f6013a6eff3bfe9fca59fcb102661df7f23a547744dbf0e018b896e827f5d765e6554c1a4273c149fa7922dbd60f0cd24adb18d2cff70dd58e2365acb85cbfa4a33fdab14029b167edbbb1576bccbee83d10183549811ec3d30ce497d910745914166a34c37fcf604450d46c9b3815101c2477016afe80c2e838b0c6463f25a281ea3f0e5ef66bbe0206ecce6caa8a7e83a6bcf2d034f0720630359c99bec9f516da37cb886741f3642f9df2751637887a534bedfcd8d8ff59c2683f0eda017b6f202deafb1b9fff3c3e2bbfb3be3858cc9958fa75da5533f4de294be216a677d1e76459f869a2e639712655f36528c96ca291c01999f3615af0f9b069b151a530a02323d99da449ca21fced214387995060c011d5f8db6b2860476f1c278195f803f7fe4f67d9d3a89ba333197550341c1bad8e03671a5019524ca8223616af2e9c009846edb61ce9b74aa811db0d614c8815e2befae489db987a98b300e3d20446487e1996368ec5c32adb5e8b452ac827fa2434c3a49c440b5f42ca349a745a8ed3cc2c17bd06d5085e016b997d91ef411d24b8f95d606601d9809a60c08471f13b7a9d8ad70ba8094888e44eabf5556235421acf7840e6cbf5d437ca7a717d1d5ebf8b9c15b9cfd6f6512649df4be711545b2d1c738dc3e2481478b2fa8a69d1dcd9976dacf87ac29b65ff1edd26b7ab0362d5f24c05d1be6db7a7bb32ad84082b8dfabf86d952cfe7824ae25d4607b4f688b5c5fb1e3e8cfd7d2ea8226c17a041bc2ac866e1604be24c9e94fffdef592e87dc5a4963ba0ef3dac178ff03e1db447f5b0b85affcf9896fa9b87644bf2f600e4dd8d63e9cd4131d0f79f07bcc2e6de6c9cd63c22cb8c35d11e623c48da295bce9eeedaa32b45b3f8300bfc66c737e4b428d0df5340af21e68a82c27319d6c09cd737da886aa991b68f6c2f9cf26cb4b51eb12b27abb498ac35fd0e4ada4cadf87587bbc4ecdd44f8a86f85f576834fa9de67219940bb581bb03fc9a221597bf44aedbe01927a5012debf71c0eda99bf09593d6ce6ad131a7dc3d36b6a2f4ba286ef33416fd7eb2c9d51ed63d353c3db281055d66a6f6aac030bac403d90e1b7de1715f41d74da738849dfaad29b0d487ce74a452bd8130f493367219bdceff00d4862013b8646d2474a3510204fa5ffb3f90d2017a1610070ce968f5e320d189beef8f4d1426e9f6e93a5d863ede0daf5cc27c6470b81cb310f2dce6172c6d7d75daeeb8b34d6743db72e1f73b65cf2bdd8608f2d7eb7d8b8b3151dffe484562527b283c8ed49f9acc8c0fa681628be06fae93c7921bfdb9fee462438fca5ead64142e636104ee679e3733d5adad2d268b04e4afa302328ef518c4ec68194824651070b814f76bc9e8b322f2fcf6a59333725f35804f4cbcbd89564566a6bacd8ab680cb9339a79350c5984ac088a1a67d41931cec7fa84bec8bb5a95ec66e083862271c62299a9da4fb7cc9abe6faa38f66577799f9615eeb608c1d218f569e4b205d355cb338afd3e8ef2f731c6f9287b8efa3e3320ff1c449c040c5570f67e0444cdc3a0c11430c744f105c3d1ae38d2f045117c19ee3928eeadb3c7ab8445a0bd084cc5e4c03200284fd2f2c9ea6c598b4e6cf7368424a4a1e7ef3f856ca267da9a0e7d1ed02d0d4c3fafc43db1449f7d67bd11dccc5f2c76ab12bc4e95bdf1e4fde6e95c4a8f40ebf777904100ded8140c9e6e1271df2cb85c77dedb8e3bdfc98aec7501b0bcf52d807500f49c2a6bd0b1b503eac085d88ce6c47801070f4d442ec821c80dd42f141bd6b4d358c47b9b31c659297591af1832fe8596cda850e533fdc09d2906001f79cd1a362269be1b1c36ce32d0c0282e8984cdfa6a7982f11c6e4d20f7f0c9e3dfffa864324ef544f5b59cf8c2a6b30ed216bf6807a03bfb79e38a2c2081cfdbcb9225c7bf29109a992b7e5f4d8ea4c6c45cb1c7abe186c42e6b158d80c91c9b542ba9d1f78a93c8f3588379be208dcc16d9c82341b476bec0c2e8ca4819622f9480b70cccd5e26c60e8d3b53eca8ccee42c7e903bbe1c05bacf3df0d6d939e08cca405f66a4ea399fb5dcac265b06298fcdfc20273976ef74714de4ce8e9a1ee7feef484866367c7b09f22a49da19d55c602190cf5491b6acc570dd30c3bc037a7eb9e938101b2413f25e5162a7bce5b3dbb3d22d10a19a2274f0f1e763f3587089a58b044002bb818b5912c8c00be049d960ff0d6235c955592284a333fb5b85550b9d3b126fdd64b5ad6a35f63b4e65d15702cbaef129d1e6e7845235c589d6bd79165b51060c5c2425ee881f1335832154c3282b113a148283fb6c90193d3480a2343d8455a64d343d58a54ef069c8b751487a10582ecd1abeb91426223dfca4bce26fb2c03de9e10750432d0038a2d1bdb74e8881931ef625afdb58f5f49339f688325e3198f4070e484b8786009a59a9386d8df28210520856c52a4213aa4c9c1ce09554a8e373961af925ce750d30951c9f253ef3fb6d838581849a87531ac3ecad81677993539c4019d17e810618765191ef73566e08fb6560916f77fb1176db7eea0ad19815d1a293ae6469a8c2445f1821e97ad0c61bed248c802fc06e854069746d4b61597e5ebe520af9f4acd6aaa0ae0cb8085344d082762fb9b696165cba19162de218e9a6c2a008a21b9a456081a5c1e1baffd36f08e4891db57d4fff53a645e05a6a9be91d993a489a478a8f321df4252f09d59475fe09863bd15be5138f01083e2f6c1851c044f69a447a624bc20b37c7998b13e30fc94fe1e45ee7d6f5fe8320279954a0ab24a20319ebe4a5efa7ca97c6c07539a0beafa7cb426ba08ef806a4ceb438b4e5d0245b6e655cc982d073db9c4b3ff1c948107744bb4996073211f530e59d93b167241e3d8e9b3311745c2b1b63432dee642699b56eb77be8af5c24fba21063b21db520b94a0635d2e49d4ec701d5f2ac2c5f71e8520d99ff5619220f6ac3f75abda189a8854a99f6d80caee362beb6fba3da2c33d1ba9af915ca4d1ca62a152d76e3b1026f6d6566baa98cf4e1b8e1d724c9ed23768a3cac25e7d408ad8af2ac6e7382908f405fecf3aff245689d09605570e221428cffaa8553b00d709a2b91db9e7aa382b7483d886a880da7609cc40f89b65d623649e322ec76b358872a57a122676c503d414ded9bc345ab9dcbb7429d685a3bf47575acc5f2e84a285024eba222ce0d41515022a3b1ac9e18f79838c9c846be214165bd991289c923668604d5d73746d7a0bee0005886973a60b059c75945b2c9f2e541d4827c48a58a7980429dae03eb45f23b906af977e9e37d5daf25cc9a06a80115529c4222421c46d3450ec549bb4fa725deb166efef6b60c580faa16a0a1fd712dbff27853429a4d340da477ee71122705eb8e2a98ed717aef8f8d53bdf04e77160767bfe4d87403f59ab6d8e24a486df9a4a0eecf6cd7762abf95a300a70ffba88c5ea22e23f6c7fd0fffae73f2eb30c424a13202a386b01268fb8a7d148a2f32719e3174510a7361b53af4f29d5902e186e643cd31898f5c52f9a9e7532b2a452b3c7b390f7ffcc335bfdaa6e4d1e2362289c55dd69444aaf31e733fa42b108761b04ae29700a29202ef487bf2a9bce12b6a55a27fdc6fedb5b5cb869c19fcadd4be8dcdb5188e97c18eb2eadf45aa943d898fd06573db382bf6a3e74a054fc7d517a497a6e1d785b30769d2b64475410f488f32a220618b40f09b60488e092e1bc6bb544e8d46b7f977f69f71afad68db8f042723bed1e39dddcaac54e4c4625fb4d7cee7ef13512d7687db0509fb3c0bbf5d1ce7857211eda327123af1a0d5f742b41f6fb2984069af717c361366b902ae3549f6a42804a7cf552d5551b9514f9e1f800dbf5c1fe8564eeac924c4bdc485b5da7a10d210b1c09d8cad079434b43a4342960e0a3478de1f6a700bbe44a82544b12ed1b5a5491cacc59f49b1a43f6e634a4ab66135dfa3e4aefd050c842e3d9014c1a5018bf12633a5af55c56d17ff2583776f9da4f542e4946e665b5f07946a854e49a96a21a0c889118432dc4b28ba4612c79714d6b0bcc0783ec6f4981a686ddd9fe657f5671e5ea1f6a7016777dcc64350f2d6714094470a50bc906caa4bda2be4a482c34fdb2aacc55214a7493c489ff5a41497a171f83c76e9598809b0a369412ff00453def12e50b1d6ec113fca37aa6c3c02aad4950de4a2e68dace5f3e9e9a17debae1d5884ba32e83e0be41be828e4cc04d1b373034eb7dd115fd9935a03817c862fa2db93c3927136f015665d5ad448861e1eaa86564f222ee00ccb5cef8e530bb64ecd74033b91b20d20c2dedefa466f2c9acbcc3b2adc703db39ca90a9da25b868486c3f086dcccbdad120ea583551c2c51b287e40b083915bf9003a401e38cad3e41ae037f2d00ab0b4dcb2ea3a57ddb508ca463eaa7326ccc668775c19a9de3c2a080138d06282b16ee380388703d01dfb1a9ac97cbf73a8445ba45beca401cc9a0aab4e2674f7cdc5faca1399c48b444156821bc1dbeeff41caca868a9307667b3efebfbe5e3627eb6c5b0c2ab0f56b8f41c30abc1049e0b02536e15b38b64245132bc38a23470b6c742e4b1a12a9e6846960915c56461c61ee006610297d98808efb85b281ac141e6d8ebffc655b7eed2cb172e02418958c59015657b42a08ff14740402196ae2183fbc2ced132d9aa832319c81fc55694ff408400b7ce01d7f1666ef8820c67da8d53bab0de1370930d66dc4579700fc1754a5c771f8162f28eb9430dd0d2c6f99c906019fde483955f3c012599ed034a3edf2c28becc77a78742c924986d73fc6fab1f6e1d406b1d37d51116091a688406a684b06fa178e3ef9209c4c6a0918dbdff5c33f0f0bc1cd0173b73f274be41c30ae3dd8bf3d36d004b9f820e6e84f2f97d746143201b5446f86468f6b4dc2aa383bf0b1fe6c5c1f05c0d8ff90ae923e8dbc73603efc18701bfaec888ca13bb15f591e92e5875432c2221833b69d0ebff2e6c2413873b0fd343c774d3e2bc06ab6c784c530274caa792543771135318e622af41141d5c74dd989a40331ef7545d1b3f9e2355dcdad7c40d6ffefa414bdd05ef32f4fa9c7d9ec6264f45469495c77340ba77b628b58edc3b82e84a9191f1545abf99d5e58a4c17ffca2799c6f9130bd5d09a1a5030581fea6a42fd1f72db33ed5a1ab01620d8e53e776fc03e767bf430282ccc103ddb2f362e2dd9f394cc3a958f6d0a6f2754ec421b009b6c12458e41a53599942896ef2b8901741ce62347be4369b8485d3173c13812b354d7ca1bcf23faa629fe6bea7be97d8c71404685d3cf671526a2fe88da00f3c50248bd6269ac4f4a59088506d4506383173281efbe0a1aa862e815a4e1a8f2f21f6cd40d7f060f11fa0eda8e60691e17aa108f1991e43e97aa3f7b1a431ffca8f91bd6eb7bb46770357a9e10b5a97fe3ea5462df32fc643a8c11887aa117118ea32938b1949316383225ed464c0c23f40737c804f5c2a380361a10fd8673a68314c8a06240e29b3135dd6c3e198a99af8188410e9c67ddadb2351beeb991ae405ce3bb2e698daabac24c20557b68b8244eccd4c39ddaa13d8ed2d1c3293ba338b06fb8e68ebfd12f42c2b11f0d9494488195c539bb4d58a4381f4d9e752ad56f2508a034e63c381beae8a7816970902f8ecb4a15745064169f18c8b40353f2da2e970d6aeb2d57da45ef7fee6ca048ae96185604603a437f5977c4efd726d7ce73d62da63a58d650f3045d7f879c95f40221115837e4ac14740fba1de54b3f89a22b5fa32fb39fad7d5f20a15d78fa0afcc2acc7417e6b8fbfa9586324c4e402d135bb05ac3e263405b48fb766dc88461fa3e7201548b6040856336eb53a1e757798dabcf3f544ac6bb2c7b1cf42e7dd30ab70c12e90646a332885eb6f313b23d14db54166a3c6167a0a091fd9569ed2370de6f51ba336def382e4679377fe95517d0f0327a189696dbc1614f30405fcce56d3491b30e5d740e7a07816abdb2794204943e54069b49b987cba8c429475409fabfa64da29c2ca1f3badc67f7ce6e90a701b06f2338b4372c1dcc5582123580ee56941963c34f7663d3f3b64ce940fd4a9d5dd04583bf144617d3db87fd227c0703e160f89514a985d3ecc14983b16320ed64f068d6c9ec19b77f9bac901748ee955d2c73e86feefb976af81d11079325b7913ee7da5ce8dc7324d45d52aa400f00e13590206ed80fbd0348d01ed745f8c261ff578537003a6c54a130c9068cb134c6326e772da34c66f7c2de869ef516824d46f2081a1750d0c6dedbffe6b8bce53c74031fac560dcaea95cf467dac9559d17710cbaabfc5a45be60d374577605821141eae0080e97f880d100647039f9d63727fbca2385a15f478792e255ef083079fe3d0129b30647129fcecf92cc9eb429e297c146f94cd60a439a6308b20756305dc93f0f24fabbb0fa2c1c407b2165422e8b0ab3ff6b16e1d2a023fdcbb7ece9547d0e0dfea2b98004e7492983957ae289f27ec6907068a1b9172d421f5a40de6c8f9593d431e7bb50f4a03780dbedb0bf1a51cdce0dafe107e9fc2d8f75499164db19bcf3b2be1e9949b0e2b5c60709a3075449bad36f2d728b8a5d7a3654b9c6203cd868c7f5fa69ab0adbcb239377ed71d5449f8fa5cfdedf98c034ad45204b878df44c5912ce360758bac6921dda1c06e217095f9049eb9580e2cc3960c461f37484d782863002dab7fb4584006a962b81b17e9da712d56cb958e50f7a71164a544e46479750f6b5be73687d22ec2d8866d97ae91e242b7aba75abb2ff0ea15e404325296d8714e2c018227a2153e26ae85ea83e46d0528151de8b550f5f4dcbf0b4ffb0e489a1d81c3883985753fa3f465e56c9ad0309987ce6b905e689ce85963a33ec0712f9ec0821c8f747065f8f84979373e51bf363343d07d658a6e4f38c45d3647fd3d21f4d77f6fd41e4236b46c77c27d44d8861f02bfe9d9e92de1853a98761b901d2fa2e13f86938c61734ca9a5c1189b6588e9dcb501fd38746e55de2d3423e48301deaf845fb835a79abb69b9c2f6e1272a44671e9e929d8a364c409d41e7691c894619e0da27372f0092843df9610641876c67f394f1017c4534280012a65a14d8113b1ed9fe5eee98427b8afe7cd97bbda5fff77034729b26d9e320019101c8f6c9a0e0ce9335aa503d3d31b4c8f66b83022269cbaf18d75e9121bdd4bc9b485bec9c71b62b135cdebeab595608b49f24866bab91c61b99c0014dd63ed13d0320061e0416fd59da0ac954d6165c10918ad53c969abb68570d85183fe5b1704d81a36a8fc55e1d638144383c5034892e1f1fd7fbfa952c72745b1b8c52636433defd9d87767de94a5684109ebf25cfebbd56bb1a3a03e030dcd43a5960600ffee23ce9437ecc9b755d34c5f492402509898dedf98d6ea5b4ff03d94f38f4401fa0ce38b6c715b9f7ac118212c57ac5a0eb6f5b64ab2645045def01e3897eb3c8d066f3eec3e0b81e38c8a0f5c1152915fd6ae329c2001035d627e886085216a26a406f71ca6d640f7a45bfed7f44f21e8d585eafe57177c8bd0cc440b867c80c25c83b87d53bc25a75bdb8be8e44d8035b5e3130f180e08a737545a108eda75c1ba49b9131f4951b9bbd2529bba4c866d432a5425540470c263004f18272dd65fcd0fedc6e54c3f4963e9e89eeda09762c532f571d71080e969e1db75c79b685aa65f52defa56a12fe9b680ec5457dab5e9b0f8a4e384215554ec0534426ad733313fa91a7b3d9c1398eca62fb712559105f81e8a13354f4e402fa4c595fa55d07c45cf555487603bd41b38e95a8da7e54f2a9d91d1c6f941e377f0b6da04ec1d85221600b065b22073224e6825cc56f3ebac4c9f403c2dc24556879692d2e3602446110ca9c1d421a3adc17db2c38a43aa67cb920d84c5bba83b317628fd70a7df25435c48740015bd2ffd89678b9e318b11aa0e3089f4820212019905cd91f4bd3e88d48111545b52d95cc4e22baf2e2aad111ca2a6cfc9961f63d85f514cf6e7e1ddb7d31e86232f12af51757c1b80cf0495222c3fee069413b052b61ad4a90c92ef102dd7603a22f7de0602751b20893bd112fa87ec2ccda9f1e7167460d4ebbadba4689d29d2da2b45be8eb118bc7a44d7f9bf47eb4e6ab7d9a41cf68ec3843516ecb821b7260a9ce122e031ded2dcf3b95eba7e150223d8ac562b1c2dc98e7e099a1940abbd2e0188b84d9c719e38b5b9bdda173fa8f735c8036045b1b0a29ce138cc81fe35b195c831f24354f651b9128b0703042f89a04bd718ad1ff865622ec443814ebfd4c62a1c30732696bcbf7dff77ff563380ab09178fa1eef205a92f3e890eb44d7b2124fb40c0442dfd0a5751589dddbef2e2925e4fe44a6f9d84beb9262166cc744a43b410c8acf9041c0c925cb61aebf980edda2c8a908b3ead2850fb9fec8df187af33bf08ea22578fa8e6ff2ff5f7d23cbc6e643a19c6b540555066b54466471f1241a507241db97084a96850277210a0028b0137d09536765352dfb2db79222ccfa6e855b8a62975749bd008e0d6400d164519519fa281584ccda302d08165d498564df46a4daa7d87ea33b78386a90d666b20f67134a8de9959a700c023908a0bf62175aee16c308941fb542170eca38e82b97c7865f51dd1e1861b36dde92601a4723bbe1c5ff5a64221710f709304968eb4e060ab1650f6926fe26104445f3d4199227bd35bd4f007984cdc2f00c8959c8ba8c9b5e39e08a12c1c9c7c39f92cc388f22e49b6606ac2a44ca0c8f7a0dde3b92abaf666660cf85396e2636286a16ec7d37991bd360f6c2d1edb77d64754f34ac00747076ecce7d4ce3a1ef2d9f9e316602f21c9a4a6b949628dcb58787f463c8b209c33041a807cc0f28d3b9e9a3b6ffe1d7b41463773f28690a01079a0bb878a9b115ffd280820d016842792acf475400cbe098711ecec890b5a7529b39221d29418095fc466d19f9391d38ab9f9f0921cff4f0cc34397b948c8d18534fc845028b6c22a82ec24d12193280b4b87fb31826a60fe2cfee57bdcf6888e33e11aab03a0514ca83da230820b077201a0e6a6874578980c5a68a282816388606cc384f149042fd198941cc4e0d3ae7faff2cc3eceb237a925c6aac709a1a7294ee8dac80e2464aa6c99df2e2ecb066383e1a96ef80d596bdb00646505a6b24fe9a4b9db0743e48a0de3e5766a26c631cb2b0f2b576e61dc6490e6e33f65153174bdf00439fec8676997a04ecefd392e1de382930f6b7043ad12a9966c5463e16ac9754c320506cd98c963b7192d0ea5f969066d88b92691a7593a24c8e769ecc6cade9d105b538f88801ad9b5852a788b39ad11f812135db34256bae283071a1e0cb12f555344016f93c682cbab9879b020e92449561cdfae3a76b220022905f15f46c5611d47dadb75a7ebdb69e4e81c27e437d499ad45103952f93aea4ad9e2394ee79ec10753cafe11a4242b303029e0846402074e1cbca2d4432b3eb23b05464ba0be3b0951b2848d556184082c9f81200eb318252934a57a231ca8278710fcb8f5ab7ecbbd28a60074a86f7cb4420673bfb15c812a9545513e5aa317d3c8550da6e30d8a7ab6258fc44da820a83e8fff7cba1d412d250ab700842b29c2f6d85c047ec05b560099ed2122c877179389d528109ec5d558b40564c0c36f6b668dce120b0ce762ec1816df091e749cb89c078a1945bac9af5601725d931fff75bd8b23223bb540151b41362b8a876571c0aace4b03291bcedbfb4699ffeece424b6c319844351151a29b43caa9274ec4f291f9cc2155fa489b7da82ce245fe47b6978dcea995902ca5ad85dc2cf733503a61b1401f6de6ceaf63104e1f7b44306903757e6daa0a1339a5d533fe83154505e8587f0fc38470cd2d2a2d0971e3b61c296e260d67b58eac32c7751a8e535fa6b4e8de8329256859da5daf6063a5805b8299cd48101b1b143c4cec36ab2911dae5468f5e07f6a4969568fc2ea57b8c9920c3bb828a01372f75b2d7443128831f89d46cb49367a019b1e260f548f39d14f5190b7095391a35e2f98e532cfe4b1ed787860008f5f1b92e9e980292cfbf228473469ee5510be367badadb335c24eba59b1f5c804e93a9f7dabd62a03f7c0c40bedd64c5eadf4e5a6f777b132aae8969ef9f4bc3c6ad13843beaf74384938ad5ebba29f60ca14596a56e53d32652660ffab63b5137b77c469ae1048267a4c49bafba6e65c86f4d807ea54a7fff61bb1c25dfee462faeedd39b7ace01ed7a6ca361d974ed9002d7ecaee5f6b65e4b3c89714eec845ac33fa58e4c8175ec9b54a014ab7a960fd2e372e32f4bf2014ee78db32d2e5e7541f9612ead550986ca7aa398594f3f1b136b13d614c73ab8a9b010cc39b9120028b8c197ac92fac7834668770c4ac2b6fe22d01d4ffe6756628a8cb7e6d50cef41cd32e2075e3b2e320d9fdcdb3024643248c01eff8907bff0357eafd00664faa6ccb3043c382c61e896ed1c4a034a3cf9936ec98adce8288749f6f06d82768b5d3b365774f74645b991a91ec4b7f16546ab837f326dd786fa8b9dd67378833d8b6fd8e44cc45ab274e3f805e6da2a1c4ba88708944ebf390acf7588fd1847451b2f6afe7542ba0ac4f650bfa34812029c5998b3ebca56337f2c30e19d4c07481db2248c8d4f88009d2ceeeca02537d43a3c6dccd8476b72dec091480eec7fb4c52d9aaa8fd71974c8581e4a10ea19210955f7e21b1d55827c83a6a6f51cc01780d0bf9f30b5ad2a7307f17b408f1791606b389c6f04187258ae331c47834f1bd5cb862f7d4ffa57472961fbc1dc8f53312201dad4220d0a0696b7397e5d525b616ba838a49cee8950250d42016d182318fe3b135e14461e23fcf4c687ec46049c6dc9401c8c9335aafeae0abc609bdf7666ce1a41e781e464d627bcfd8c83f7440135c33b11d799bd2c10711a30758b7ccd8931eb4f8c59bc04c2dba16960eff9571849d76cbdde44e3fffbe2217f7f48e21391e25fc3bd10874e731b65d3c0e95d430f2c4257175579547fa338e75499e76aac42194c801a05948044d43f713b807d8837451453c900b28d4329a076a9d635b73f43cf71f661b99626c2acc5465f7ebc5eee506c05d19904371fd9f8874937935b3e7e86f75bdf62a692ba644fc50dbfd0bef6309b2d83adf85030850bd55b45d773b9b0103fc3bec055c424b16bb9a5339fa0be8c3de1bcee6d18915420bae3ebc1686d9a7376a35f90609bf9238ed244a91885e337086c0efb7c4de0c4badfbc94c1cac74c3aedce64103e2e6da744a18dbd51627d9a4d676fda110cd94d7e1e3d3b67b36eb44cde41ce0bc42f793737b042ec78264bdf06a0227eb58391dad42903a4506471072b7a719ba802b3dede46a8a2f90c7a10bcd2921bafb34796d4c4c115f7402ab00abe324e13c0beec57e8ce7cdf9093404b78a0b6c0681ab328933c5c1ba8cf20ef3e122f1f9f83722de9fa5de6b1d9d64bbb11cd7188490e5166811ab86ab7ec353cc8b4db255dce84cef5482334305d3626c767e60a3a3c73035486249cb723474e313fa73572a0425cd909a40813832ad589b2a1b49c4623717368c532944ad042929327e013e68ad7c3d4ae82b0c032a34968fcdab1ddc6d6dd50999254ee15427b2f7ef4df2dcd90e48b7bfc314c251992169f7cc43646c4c6878d9f2d93cbfe45a12a75be4444f778b1ef6c90c98047871426982779a1521c98add64f050cbe3890958ed2d53f4136c8accbc3d7e9fddb206df917b423bee71c2c239fd5eca4c09e9f3157527792b97f321034c9c30c52781850b88887d72d7667c061eeb7db07979b7aaee72019a4487e504eed229fe677792fc4783bc977a405d98912dba119682947af4e6fcdfd85a546438bfbc27fec0dee7b4a5a385ebf07547cd73552161b6a0dfea60d9d8983a88ef1a22d5ed08f64f96024c410bf32295fbf1a59f073a71655e033801acb2251f6db25cd85c9d2fd796a249907bc7270eda4454c31316fb3975339ee5d91f34375177e071725272f2425cb11560785be6b54f400ffd2af96804a06a468f70f596e39d89c7abeb7c0f2e8b2c99085c91238e163681acc1b9dc71b0cf1c93f9312ccb333099a917ef2fdbb6be6d877da3884cebee924317b042e30b785f2d218dc8934dc26fc88aea25eb650162f67b0ff56168d9c9350b4293719618ca229b07c03144e5e29756ce168f65d00348697773a44c2f503c72070ea5d96546b1cb8079c47b59a00c12297dcc3758e4981cc2d9135f4d83308caedc48a1713deb08beadc0e86c829cf32ba773630eec70c89690726099ca380d6c572fbb372a14c3c82f7e169e4dcb1523badf97492996b834acb9fd234166185c1a16e1a12fbb4eb4a1c75b28174c35f384970a5b593c4919564a533fea0d2539298e1ec74c771c19aecadd7301d9aecaec5ceef0ae6b3ecdfacd2886bd7fd2c1ff66f06b65b137c31c0c16a4f1f4b0c85cdfc20696a5bf1cd9149e5294bb72e0cc2539ff15f8f55b6c8a4d8e8e30a63bf791d6a41f68632d6b63a0db8385519e0b2e26f46a52513bdabcc78f5d6feb538ae4e592c1878c5e09dbff69ccbfe202a6bbd3dc918c266395c3f4d6b2dc09f89d4c67a6abcc0bfefec666c7d6dd502d0bb7b81702db0ec0f4159c286859e0a24889cf089c13f5d63c2661ae51ad23e31ea4a19c69162c3186de6e0c7dcc9e2cba4d57cece27176662bc320340a2dbe9d0761b7f19b652990cf44adf64ff2ac3d32ef7802d2159f1cfc021bf3e0aa46dd9823403f0f65249563627fd9f6ef5760a10a916506802d5c2c6ecf1731ec8fd16c18a7585b0d6f41c061ccc54977968b7ea923de7f5641eb5a330d33356911156a586c736d08b0329b4928dbd1877f530b33e1e7097c05f66c6e7592e12c6450e9d03cf56e272826c5d3d5f9bc1153e5bfa392f708a2bccf45e469c48c49c57671f1338f02f0c9804046d076df9c256656112f9bd101603d5fc3fa3a1c0022f2be34e4f22df6d79c13fac30fa167e4de7b7eda2b59b12adfccc99dad401b807a8dbd1b550f1c4911027d0780d7b40e71cbf48296fe4008e9038b4ea1887e0bdd7a27c463dbedda5690ace959dd839299f9c56f7cf41781372e24425aab3ddbd9d06ed4a4af96fdc948eb7d0be1e6d7afd124d82bfdaf61445195253c67fe22cd3e8edd5ef33434ca30ffc61d3dd13ae8ffb2a8e97c6a41ccbc0f253191e420bf3982f267d1f70f347bf4c2cf50462a7d303f071f34f07aeb3ab01968466af8c1697539569a413fca4652dbb5da2fcec288f6cfec7a6c8e8d95632c25d73c074d861749ef3e727d3062680108e22efcc2890631f92c40032f9375c9a66b749be65131d45eb5c1cfd1a21aebdf3d8b10605288ec4e04c1e7ecbd52671f0d1006be3195d3fbf02e8c0854dca80b716a6c1f665c26ec848fa535ee174dc386cdef40f18ab1910e4ae03184836fef8f65111e4d15ce0aa04a3ba05f24013ad4067b4ced54732f49ce7008b325aaf5b13280a9818dfc8366cc9620edecd1bfe957e41fb8c2a2e76b5d086a41818f0ded9e265274806da0fd51a46b1ad0e40c0ff88524c9e6c1f935c82f01e02a466065680b5d14b148ca2fc18edf4c5b5a40517ea2dab2bd77e2d8360e1ea8003f864aed89127c046906f6edd6a24a46a3611f101edfc2992ab3309eb2e851c715e7d1acaa3f7cc79dd1ff558bb67e2eb37bdc75af16d3e0156734aee0f1d85866f5508d42bab6d0f124544691f1047cce69501014d4fe388e944cc72e8f6897b88bd8f46af81747ead7506c3c02576a18973d53a66c03997750fd69e1cdbe0083b33ca7c11b87ea40f5b60ee895dc41b4bc68022048d9939a93564f1f196b4f63b324f7b3e5835b1fdf875208d4786e23f4cfc051edc4ee4c9e951d697105726083f1bb89b78916723bf046f50a5786198003215c79abb8f895425b4bca8b4bb63121614d108bf89788d9df7fdc2202cb4a82228edad0cfeb837611dd91936c663b4e5218689803a925fdd5d1df0929e088bd90edd0bc0ac492cc58fef28639a5e17083db853a593ddb4befc03d6184b19822fd0afeeb7c9251862760dfd84fbd8c7dfb072718898278ca5c64a3ecd72643640971eec6ba5a11b11b481bc9de74e1065621dc6d34f102f3c64af0221aa7371ad5a9e574e8557a2439f8710d2ccfa048ceb2b19f5c234f992e0029ab13d75764f30ed0ed7b509333857a0c08ffe301db350ccb28c1a3b5b39fa1203684b412cea27dec5ea81901f3493c100be0a0ea39e97dc737f27de91aefa42daa3062126b766c0a75416a9b8f18c4bd9ea726f2c8c4d70f97eeceffeda01a3a7599c25b322e810a12d585a20a402518c0542567d568e27ed6f4c95106e7bbcd7566947fb82051a1d00830230bd7866c3b6a3b39b0b6640a420b56cf3dfbfa7637c337ef8953ff783a0eb1bbab3179dfd4fdc12231e76c98145f8ab7638e6ccd68be8eb598dea1a6d15f4f1963bc3415140a7e1845da564068daa1435cdfade09b72ae532ef7d425019aba0816a5ed7c503bcda72f806663d8d0a84de4fb3e9afa61a45beb99e67546e41f90fb4551a0a663f61622d4fa986f2972026928589b73f3f806986db5084efa750c0c30932735675f4a083cb37f2fe4bb43155c9cfb49591a93b5978e4c9b75dc3722ebf0de462a54785b9b65843b5b3ea77f75563a5f08291b2a5e447461cef7c0bd8097785becbaea515c1aed86427c92dd8686a2595bbe77155b4b194773c01e9a6475d17f262b35b5c0520e140b2d60a25eb0c46146e8279da817431a0ecc6aa4963b62c8fae9d494ce4a20584278b98757b1230ac0d45a52cd90d4a1045e7817042dd7fc6cd1dcc922c91011e7cdb638bb0cf3432ee1a5dca7a6e5338017af3ee386e26f8b5c3f5df35af5a9ce400df75065c736880035cccd8740e3b34dfda2f153e70ea80c9a12827f6f92802726439ee23ca32212960a30cac17d31b50a765cc30c1e9a3ae95c41115a6dad9d164b7ef7b7fc61e2838c0bff50bbef4ca88263b814cbae1757f425fec41f16cfe92e6f8f2e12bae6841e7f52095b2b4b60d22d91734b686d95dc2f216339bf5381957be3c06abc2f1bdc25fabc4553c4a0b5df00860477cd7066d82adce46b64d268c9804174d812286a1ad94534f08f449a1cb7a039097e3764093e022af0d61bc7819379364fb94227201a2f2576efaab731650b8f3ea593f72fd4cec7195d3584f5c9922585dae0ff0fd6fc480cf45889251848c95901f71d1bf2e701f3eaeda90aee77d475c247fdaa49f24528b2a222e4a80bbdbe960436a1f4420b0774917755d0e8c6d199da27b760d2e19cee6c47b157ffb934c76b52762347cd0173aa748d7242cc2eff80778f990d343dd3a2bd28f9e8cedfff80e51751bb68b194b66c5e4d5700cdc54781af06bf14f25a6a7e47cf1c42337f397aecfecc586201c8f9f01ee529e06ee3909ae9b040fe41557b24840f51d19adca84e95931c8b20aa537ada1980116562ea7f4b40d72398c93e540ee5d187018214444764e820c9c5c0ce5673e5071b263c963b0480a2d2652559e429dc810f272d8982e745f1c54aaa311b28fa3f676381e6c11db00871a3f498dcffc8b829b0bd06f15a03f047ed90103ef30edb90e851eca672fe338ac2b513a88586ba4b708a12da1132836e916a85370299d62397717e4f84882e5324ab33c52abb999ff31281733d0063feb38fbd31b6991d50897c4349b723b9494243e01c69aca7f323a4efacad0fd5407fa682b17ddeaee73f2f010c63c582571a2ca33111cb249548ad6cab3b2a21dab69f9f860f69dcd954b9224d94c980199f706b6c09a2da476843b79830c2dbd3ffedd05ac323695daa8e35af652e698903cd5f15468beec7867150f63c44e83a6595ebb45029bee364b00eb38a926e402b602a89d200cb93bd11b5992b90fdd2cb30a8f73fe0c839a8f0f4eb0a7edb075440bf79c6ea579d96cd491ddb5a31fb707cec1fee7b4e59adf8650ee615eb7602188cb189ca2605faeee594af7c069e99419b96c7909eacdbe6917b8b64b6e38d71b9cb6c43d153b5cd900a4744496ca6a156029a345794db44fd2ee7d7ab084d7de17f002a71eabb2d297ba9c179f4178831fd0b6a4fb4c1d448608974c389bac7d5dff69a8089fc68b3bb9daee14738ceee55425795e758d6bbd461f15315ee05278fe22509773f8739d6cfab32b0bcf3a6422973ec2b5fcd61e2213469c4fdcabbaf4c60ae92718368df39b4db2c65b692fbd891207a69ea33a99ef2a69a04e1dffac2f04fbec66aa5f2c100358b1a1604d90589ca973b3233bdb19cf492dd150236f6162eac056b056ae628b965d8db91aa343396ffae39fdc8cc72f2ab5a588e99798b8e16260b1e0095790174123ac79759bdd035abb1e28cbb235de17fd3101393bab876aeb436d79e41c29a4fda47289ffa24c60fdb6da8b1727bc7b1ed941eca38f6d1b23a548b62a35baa693a528f8074a7c903c890c458ef3a1c1a58c25a0cf8a76c7b5e78b613fbd9cac24bb7f6e32513f611c7fa60e5e042cd4537fe45ea18f741127883ce9c0f805d86655e8d354f0a9f79561e0f80e261403558d371dfaafbd7ac5e6774b9274abac68f0ce4fde1ddab9406511679141b5eea020093aa7023d65aa8588b2ffc035ca5dc38039f75026de894a0106d5744f48d265720b9065e0a758f2fe7b528f6b350af1043b41c9b6a824d84a7b5e6809d093fc8093e920ce6381e901973d24d658cdf0b2d30616e5ba0a6892a0a11919ed6be8244c76c65501ab5d4ce3a8a79b0de3a991bb7fbf5f2bb444bae84a493b902931879b182e12dbf7c0da04287877aa4f878ee43c8125f15954292b8eccf3dc7abd66d0ea6dbc684442b9551c0642c090b89192f7045b329f91fb1b347d0f8d93337ad4339d67093e652441ab92589f81062bf7e0a5aeea70895b7f299628427518f80c78b9879f3fc866e79e37ed5a297d5f115a293d7aadd1fd4f057f52ac102456aad3a86b3ec294ce6b1f54e7ce7c059fe7f0d16a005ec66157936a1db00323616eb2a1767fa2bd9d378639d0ad45ec5a0cebc9835bb6aae412912da2e4a997f2b689785af7bea73aa217b62dfe664042302a1fbcc9023527cead1b78614f419b1392f7371b9cdea014a67dc6541bca4f379ba850c38d9c1d89a67b865f998c29362a052bf4928f917e4c01ac7695c02f2b38fd39e741f6656477760bb4bc61b3e803a231011c426206680fdf28ef6fa1f6ccfd260de1ebdf515c64d82081e8242f3d1b7135fa8d3230e8153d9cb7a0799f0005a6fbec28f503b4d087b8794f5c32d39b378736e6720e8a4cef4ba16940fe80e75326d43b345e8f13265ab1e6530e0530a726cbe30f1b721e7537e72296d594caf5271bcc98f084111f3b49680a0bc80074726ff8cea36ca15278d8157e072174422a06cb78710fa72f9139d75b2c39e71b3fb0dec835f91d30357f98c201eaf34f2f9e623a0bbacd195d5b7138d909de3250e11d945bab45e37c396e4c4877cce81a1470cb21ab200d7ae655144582c2051cd180032bd8d69bedddd60eb7b6703874a3e4ce8675645820153041c57811d4b49024eca3f84fc7e63d11ddd9e10a5023b0d99139981daeabfcdb3a6da028c14357cc60e69699e3cc4546b7d274cfcf412366b6cc4721c6ed91805fdbe53442ef887ee4808d7601349df42a618221995df9ce55880cffaf50d47634c631902221cf9773f61080d480415c9e5421154f439b262688b9a8694d856fe26530c446bc5896e4e3c7a21e6690a141b0171f898d1ca1a82db84f028cd343c2d3a2dac6aef92ef9de93e4949aa4264b6196765370af0e2c288c07df84bc3729c0137a5d7f544c2271522585c88a094ab8fbe32947d4f7c3244bc2c5731049934f3223fa51d6d6c5b27a422292c833efd9cc68bb5d3fdaca317e9eb158665d9c8369d8732652e530a28f19e748567788292d19ead0c2e220e50203400413463b230a61bdb264224dfdde28f62beecc96e3a8d4a47e63a16862ad80d3f6f02ab62260027bb61f083e69293cb048c5cc9a2eaba72fab560e290842a73ecdc333fa28c1d997bcab6266a47df08997ed8de17f1bd80ca59573db63bfc05dd84202c7949340e13cd23c2ea19a2f7cd291d5c06f633ab9925e45a46b758a36e1d2fa68527967499a2b7c91f9832c6ed0085610e8baa7413f744d7c400c086286f479e745daff03f738226424fd60d61d260ef57ce9af90d9d379d5ad499bfd7572a9aca104df3a19193fe0549e476847dcbc7da7d9ea4075648a69649d4086bfe2779b38f44d9d5ba7ad5b1ae95e69a62d79144ea8d9be07a5e2b374096b9159988707222e63902d9b58d42c1805073c3aa7ce551e2bd7dc50d559b85886494decad104c56b2bc5550b02a92bc7c057f6f8ff0192d2d2dbb386758bab22f2863260d78a641ddfbbc62cb04898bbc62e244d1f3028cff34b7d52f5824e69d857702cc09e7e97b1b9f15a2a395b86374b5c8c92a2c6250b8f9bc394d98a310459fe898a8175b8bb9575b7983aa63f37b2b7d55822e191fbc495a743bab411a46a735b91ce163d3abf3eacf65132baf5905b8d7825e38e6dd112dcb3458608e3faefd4479566d3916617a62cb7ce7e5f8ef522f4264d44e91a333acb574f06e6c7d4e3e2be4241f28f28a9659374dd041c661d77217c4349bf9dfb6c5068a17c51db16f5b1407ac6e406d36d09b7e33d1a7134ac75105c9c404346006da32cf21441789462dd69dd390b5c4931489cfa0f77b439c72a00a685f83b5f08ace2bcd82b426fc659735f67f4709e74eda7ade558bcbc1adc3f252f7dc0e6437b93f813995350c8f2c93b169756825c8cdf416e6cd341bd4c711bc8186ddc4e55630f04c3f365e4708607e6b615600e96b2fe0e61c1c51df2121dfd5ccdbec359e344de90d9d51fe5e13b8273ac5c2c4ab6a1a34d587141154ebe4e54cfb20e64d7f29229a8b2f21d7c48f3bf55aeae7f96a3984df20d5e25cbd7a045e18c7608920b10c1237ed147a7be297ce6ed869c050709d1065050713a68bca86883f71275f66079a78c6863aedef9deb0bfd17b43235e14437a255aba60288ab63b3805a44e812181890734b3c606844075238b5afdecd20101281a8a4b42317cb782901e7b26bcad1d7bb0bf93fd2de52c18555ace66053175bd19aacf81c422092302b837b084698f4387c213b0f2cdadbeaa9b91733b917561cdc750c8cd18c548fba55078eac53e12ba426bf0b7c173ac379472082c3df4f9a695ac683c943487c951936da05c30bc92dd0f4e5062d40cabd3778da5d26394236347d5ea4e3b6d74138eca0ca160a1b8378eb9fbda3a698c391107d4a8024c756bc15c821bd8ab0a895f0d18019db60f96c4310d62daef2c40ad47b62727e25d4149717a3d077605d5c1aa09395418e7ca512efbd8d37518b81c5e17f77a607b487aa0233370d16402cfe47f1f617d0ca71608b836c15544c992d5eee3841877242d1005069046d7055980fd4d949f58b200e0be8ab0ee8750c61f7a1a6707b12f49ecb233678ad562fdfeea7376b870e88094be2daf3ed2bd1db9bd6d08bfd8c55e587d303e62e68d8bf5702000e88bedee846b0dada7a66c5a037766a9ab5b5224da740a951c5c522ea82a8c9c64786b9c07c0f342e228942fdbfd4176c6598f2da1e20ad73f11f8af7016593243098a14f5bdc0276ec65f306286b7fd8078f960bfeb33db6f3ed95eec922bcd8f65caefd7ed509ab84e27b70714dc0a837672a35e79f110ccd2e22aaa6f0d18c506875134c92502ea0e3e4298d0b37c2307613b56f7579f1e19168fd25a637d288510f351c3ceaa05171f404b1279619b2b626e051172f303b6f123395d67f9dc6fe66314ab1892fe5c80437a8b04dac440ed247a8415d2aab4381bc458a482877f73b41bfdd1270d6f5605641a49ac40936269bf8d8a580d4de6230456ff1e67df66404bbb440470c787385c4ee3ab6bda230299160104017944c5adcd73fad0701ca01fb97a0c2d4b82ace9920ce1fc63c159856255e02f98f1b28589359053056381f8591ca04f011107ba25b863f65b92d34da6e5a1b4d8eb207b513efd461558b19d7c88a0a77066cdbb3e050d9bd6ee4d2b9d52ba55549fb3d2fdee92108596033413a7e648e986c3d9b5a289d3740cbe875090a1ba765ca3b024f3be5ac70cf9375c2210a908cac4b0b24dc3fa1fb76b0438bd070608b459403eaf02de375213c57e89b375ebca39e35b59b828e65a2680ad704f968836c54d2ea60f00f9fec1f097700a45f1b4532de0a8645a093edbd57f50251811f42176a7f1fb5457b52b01bc21ae3f4ea39a1ede9c7155d577b7a3f9bf2f7aa3f3e96c378fed78224f82ef98db36752c4939e524aa3eddb7cffe2bee1adcfd5db144adc06ab3f2b054ce4f87862274074a2d8149ea8602e90007f1ee56ec359629a3f7faa49adf5181df050256c8e0ccb93cd9a6cde18edde0d83de96012e9fdd651e0f699d7a159f450cb4a6ef00cd1500110847ab9c3a0bad55f30bb212e28866186e88a71f9e79ec8612de750a3033d99e3993a177a678680c385c79109a9ca98d5cc4c7206d234b4214a4f193c7385b887da3f66e105c62f429c9194f9be75e48b2e8e17d3acf847b0aa4bee22def200a3c558c18659cb60bcdb29b190a89a34254183e52e77b865117c0860deb9103841d43bc5c00e9a53ae8186cb337744752f5e019a45b446f8e8e6c7ab00ef05948f676f1d2846766534892e7473c059d3f18b243dcdb3a5d280135f3831fad9cd69b090443fe7a39e7018f03d1da1f0bf57a02c8631d72ea1d5e8439f1de1a562903d40976a51bb4a63bf4a68dec2f3ac53a155c99aa4638ec692787003129e7bc7c10d867897b3b37c928f3d722330c5e51e7485ba73e1849ee2e48a853d248121be6848a4784208a48a27466a531226165aca4505f94f70f1ad3fbbf5714890451ca2998a2e40c8e2dc7a8798af2bf787a109175da9a02271a119128ec70ecc9ad0ea790e4de4be84ff64e419183bb1b66e31ea20307115acad93473920a22ee95038d83f55148a42adb2f498d4c66bf54be8ea8cc4a5330737806a57ddef2914a24176f01321e988470d8d34cdeafb3c7376045f7aeb66e3484ddc342113cc866a8966d4f339af2a8b25358f05625790fbe130ec19526a262de232aec68155b446dfb4238e723a6f0cc1fff3810bf68aa4b033b068d902387479b49f1a27276e8e866ebbb08d9a5fca2ae7fd94fdd66d1d443f5f98986153d2dbd3b78e1c8d797daa84e3d70192c585ec3069f59ec638322f94ae302621e4243be50fb965289b1bd052b140c0319304a1826fe2d4c533c0697507af35bcfa8772d939d10f4e35899c8b61846265ba23174185c6ae81828e95c3bfbb219497225a1ab25fdd62c530cd10b0cebdabd5f5a8c00063243d401b17a00224c347f404bde756a83e6e0ce1810777ce40bd0469663ffe0ff31e3e5d807bd4fd77c279fedd9b89d1e593ded161891254b8c5e4c3149708b711f51723c0c7c0e79bae8cc6b6341b55797c404c0797cfa8f1f05ea504783ea0972abe39f45f45f8df0acc73001154f2b65917d4adce9bf021b3f4930c787c57355ce0de628043fa0bf171ff02c7a0a823db91cb38dc8bdae6fe2fc12057f3196a90c13437fedf2c1a08b5fe1477c176b6e9520f72d0fe3e4d4fbe4cf2b0e316ed9c6cbd345d8f007af27617839f843e5d4b5360d3e8b4258b15cb4ad12acbf3f644d60438081d914e171bfa334014f71e2a50d82f7f71035e6719741c13b5287033adf63ce262c826a345e0472a686cc841c43e40ad09eb522faa686c9c24cdb6da1a73f7a7e8e355cf0c31c33477131c0cc8c60222a0397ef401ca4fbe5b285aacfb7ff516657b655377d9bf4b2d8b694c11a0284e8ffc6b66a379fbe4269619a50c36a62133676a66fdef4cdb30886d06fcd3322c5ac8a15f8d85a4461f1b3bee4e2eb2b864d9674f6a1f93264bcf7452dff26f2232cede3d012ab43396bbeec4d51c6747497d647ec99349ae14fd282dc9b67b2fe57baea2c50156bff1ab11712911e99a1a8790374d4f2791e0152356c52fa8f68aaad2179b806ae771b1a495469a95a74b8b7a723cdef03971cbd0e947de8aea1554e17e6700d03e81ad0780d3cb945cc63d4cbfdab145ce062b762dbe15f08191a804b4f85f4d55183d48b007dd6dd8b536cbceb3b73cec55136d0598ecefa19c1e1af65841b4a171107e8e11aa10df5a23a59622874d8d0008796a03c9722df3ca5b8acb421468031453239854bc2066bd4400232d47d3f3970bcdf81734f9234416e030459f3cd31f4bdd9766687c0ba727307d8e22ff3af6a504c440dd0018e83180a4c7a39aa03d55b104ebf4ed94cfb310cf8c3cbe90f89b7b171a20eb4837fe52e74c0d53f703939d0bbde39d58d12a48da49b77b7f86096cc6f122f1ca284c45f537c4877491ca6a0f9565033a8ef067264e59136bea4327205dcbb82e6f1d27344849ecbd92fdd9635424086d7a5c7ff868b2e0682e393f7d6ccc136cc008fc6b93fd7132c0f39ff30e78be3c4e1166075cbc1a91d00b69183728d0f00ea35ec4e7f8ae3daf352eef300b0fcb8ec432fd3c250401ce0b14ec36aec4347599bc657cae1f4f75b3075c84513f2bfbfe7b55f6739d8d625980d339631ea3fc015e12466f675913184d3c1ccf5b4d43ff88aaf73ab6240d3ff6f67423e7e5274eda312df50cd59c3f34b2ce1dfbcdd13554afc84b8a92970d2560875c8a2c7d815e055745f958db82be0ad95bc64787ff987bd0c4157907e5ea1fe039b834f4281730b8626ab2bbca3f00140ca7fce2d82d0dfe26a88b9451c6a4cd1a687c1cf6c775cc78aec1316e8489b0e6db4071b92a45e13a99b6cfa2c9f772a6823765f44a989f287eb1b9cad1f4183c00b1385f7ddd906e6b59267e819c0217e1d962354a70c519f7c2f49a35329fda1e6300024f31695a87918c69dcec413b8eae0ebdcccfaecf2deddd4f933a0b5c6f13dc6883fef215438f5b151b0e82bb250dd5854a20cdb6f73c13bc8064b46fe4e9c32de992dfdf7e9daa49ba5da4cda95135832b75d0e02a4b73de07bcc86e011aa407e11f2dbb82e7b0a017e98a83ddd45c8d00bd4f44d0ae22a5ec6f8db2d33749b487b6710fe14e095b005a9ff39957b69d8ce99c2b2a49cdd76b437e280160a2ded7e6080c143eb84ef6a95787363ae6e37400d57d2dc5aa8faec9aac1713b242db62c1037a0d525902ed873a6038a0f6381fb305bbcb5be6af9364507e115a2428d119720a13e4fa5df98526e7aff00de3dc1a92c6dd90b6c4a211410f7197b0a29b2b815433912ba33a37b206102831fd1a6d7893ee97e084e61193bedace83a4ed34d608ba8c0141d3a2526f0686a3871b13d3402138e2add203aacc89f3d520d9393e224d142cd45c1d983937409190ed5dafdb22934455a1f22c16ba57c4690a9885faedf39de1d140225f43baecf5752a05fabedafb937f11b2eb544949925ca33142ec7792f5b09c1570196392eea0c449c1099059e7fd3979feeced783b4bc1e8ad745767b0f9b312733e0fe239f7de2b78d060ce6ca5db3d1027b5d64dda2e070ab2a47b9a9fd73e0e7a049846d80d3fb1e772f57e88034771d38524906937a88dae47b68ef9f01f388a4e2c09a2c6bc4c99d6e737e74b382d5ef588e167f94b58c45596ba0438f98a807d8d39be17b86380f1d062380faedd5a624731f91e87a3f51f2aa17435a3bf49c8d904ce79e8f2017ebcf21d1de479fd83631cbeec57c9c3586097fada92cbb0beddcacc13cc33d42d6a451053cdcc6bb70280b56130bc522d7f9f3137de266c91708641da84287780aec62ec15221635428e4690bb36de8c40629c0d9888bcfc3b0d022365692f958322775fb7bd444c6d656e2ff4479910f246b3ae0302416e8c2b72479172a0013bf734858014a12cf4834a340b80450f2f441763310a53edcb0664633c4dffe78716332cc3aad05e0023a088c32ea6191908b9b0d228471cbca33d8057b3048d5737fd1f56cfde77ac713e70c38200dda81cebd02d29b0524360f401bc0099ddea7e31c0630f449b9f476cb8e9312042a6295a1605da6539865e551d60f73a1936029c42b4b2fb4fcd2e0aae636fbf84554482814e072d5fb06c9ad041aada3e51e52249b38761c22e8e39e642b8e5430e19d9ac4868a3f65e34eb9d2b21c55c045a7244c40c5dd75bb1db459654ab3db910e5f764efebb577e4f2f37bd66b567b3fe601fbf3b25ca6a7e759be5346e0a218b4c6b5c31e31eb4efacbb5a0810bdd41860fa00803439a830e8956820af80ba71e346c78ec03db03a9c69146013444c64d76142b72de49f328bc30357c829796b15d33f1251e14a10a00f416dcce6fb171286ecb093ad6421d548b71c4a2ca5abce2f51a1ed74ed0d4a888750736c28c6aa21adcff9b2398711383a225bf88c960522afa45f2e0581dfee7642670f12d2250fd80291c3899aa052c0294af82a742a82b2bc60a9e2eed6cf2c998b41487e36f6be74476510d33a2e875055b20c86cf10c48729054da3c1236a70c65b1556269fe1431ea9a7d390f596c71044f4decf1ad7647a74bbc5e78784461684a02be1bcc10b55c9d47f396b3fbd4cf644c24d62ca61337591b7811db209810349819b68dffe605a90c81b999c0c7337022d70a88138e0ac549b1c3178499452c29d2bd8662eed0f4afc497cf5dda3a7c4f30e076fad0dfb42381fc7fd4a59aeafb3414bbe23e05ef35689b3f73da7e63d0274e7c362ddee81373cd2c73cb147cbb25aa54b1572a3603ac0691c4dbe7735124906782690add6321f7837fe321461b6b9cd7475d60ec3a8b1d710dd39db0f94533112659813a3b41a84b9829b9c21255878eaa29602652e0a4e11f650d03abc08da98379f093bb3830c4ab06df69bd00ae8ee76c72bf28146848d96508008576b10a29694166f26580aaeb39867cff1f5e6eec2efc714f05793aebfff40ebf8b0672c7b7aed28792dabe4581799ee1b9a271990962f052e37baa16df0a2abc37759282957ba4e3d012922541db8c3048fae818e7c41096da27b32d6ab5e939d5548ac791671af1ca5a1cc1a1016243f91d39544e2c59ea97c62ec3fb3c0f064e6204d6e394c9e563fa05411e2d6e739b94a470051dd22acc3ad054398eb9da2675b2358fb80fe5815a6f755b284628d935306d0b81a9e17b49b06189c0a96ed6e609c67590d83a55bd4d4da88fe6a835b811284e2494225aa36a329869189e060b2521753fd744cdbd792e18a9ab9858e17980876ba75baf94af8c53266821c00a7522bbcf0bf20ddb9d852d131a14d3d52a906f839b23f92f875ad298261ea18de2067db76bf3841c1021242141039811d4e33403133a2df5eb52c5dcbd608703f52107fae5140fdf658c05932347dd2ce1efe4b74af4d018e5f66ee4cf5fe4fc0874e5be2f5aa273e4457ecb547458df356d8d77b9b864b054f3323c2937789a758b4775940c87b4c146ecccd79393f0a616b7f63c8492f6f9ecbe4c66d43842a31666b10f74734e71e8b28d5ade03c02dc2f575bd562002199c2e3485d780ecf0d458abeb24d88d004b4c46b0dd77ed5a34747041d9c045b2260a7f710fe17e7124b8b30cb67e0921c36289474cce15a633883aecc6b770f9a7133caf08025211fa655d52a5b384e846e27f4434d52481d0a89b0656d456e681a018ae40ee5fd0ef50c520a68c9db2d37ba5b9aec66c55c080bf58c633863cf3f8714c82d5c25078f1eb4c12adcba7fbc50e9d3f4a2aff2cb53a820ab050e9d63b67783447b24a69330bd7dac487a330cbf617efe64c178ff84f388292078c8331fb3ded30d43042686bce03fbb5c99d73ef9feec66fda3a81239b04b64243021e7d091a6db1741f0d77202282573b7ccc726fd6c5b4683a31b889d15a6e428b119134fba8afb0701bfed124a90b340ee7ba7a3e640ef90c81e0994131afc627cf591de8ebf483ae060b0690264df494793054c0b17ff8ba59a46c1ea21cc62440ddd1d505c00146743ca0e527abaf3060b9c441bf0d0bd16980a5bbae4b17db0910ed52f2999fa59d5598022f3b9bfc9944fff932fcb2cec4cd9460ee6e6748ec422d08635b1662b83a27725faed47c7b04b74ea67b455075a14dad2d5719c7dddbec8941b88aa846f32d382df4f578dd4e9fb41cdefe220e787a1ec19f238bfc7702470c56d63e30247016930035cc4984a501c94481e60cfb273a83ab9534033843203d063fca7904530d72c29148755912a39a683478bcd0151060963f5c2428ab201a03df98db8a55483faa4dc97ec89107aeb53037e53f2763195c1ea31f9e3f211730ebb1709f322f6c7142c5c0924608340dbfd45a977bb8239844a889f246ee269107d79fe40bc734ad0f2a39cf103ac5d979b511f6a1b850afbbe2318e6b667fa3d39a1b5332a1556f2cad3aaefa3039736cd883ca36988d1de17894410411acb44219a1cf63b1c357a1187162ef53102740d6d6843eea24069691b6a3ae091e105c1fd82602f4b2b19dd3bf4f7aefbe5b6fcd4c50756f76aa94868690c8751cd8d313107f79e3aeb45db2c49b5f279c2332c43a743c64c45065d2231921ab47c5d8d07c74bace70667fb9653a8b180f9d9da2d0b76fe49e60225a74aa07b368ff9e254c1c9d461c51564dc44818e192e4e06b015e21cbaca9267f6603d1622b8c1fdd941677873461bb1667250a9b666165e87596e00877b4eb82f64966a0a2dc09227b8d712ed993dd817fa67381c7048ffef4e6dec964650470a09af0c693104936f66b9c6c6c57edf46c0b71a82b516209c23af5ce5ff191b0da94383783de2c50b315ebf058adf6e42c169826ad72788ed73be709ac99fff5632e6906389ca219272f10c9ef4c8c034a35b82d0bffbecd5867c0a283ce992ced5f0c217ac4b7070bec2c09c45caa6782a485109cac09a3556f3968838941fb23ec318d78be8ed4e643f7f068b67dcb558e2da3a57a36e96bd0406545c576288c167c5eb9a359ebe094a380a5de7b4850874d051ab371ac85e7cd0c3b46698595b783402c87d176e1fc7b8c6d8b908150f95ffe976250e92bd05857c06a0b3b4b6a48d872450e0a1c8b5d8d71378b463d7294bce0a836b298ac410e496888486733a29ed06c57d483137ad96c992164ec0b835e9f348dba61e4ebaaa8d44cf0e6b1ae093e0ec5695b94003468144546133bc356c591c7fe6eef48d5c0b61f83892a4127d851f60b5a9f6618be4c4be4c4c882fa4e9e225e2411dea6d38007bfaaa6aad23e2d20beef1434d528e8b32eb3f61d3983a5a18a2492448a7bce4e01ed88f4615bb2c7c23089fa8ec806b252b16a7ba80ff0038a320be16444e1697fa0c3f187ccffb4ee7e3c10d7c6868b35642ec5b12108923a03432bb8d1a39bd65ad99402341219c1782f595b018990e5ac46d87e29c7bca286f002b19b0f0845e7f2d41a7eab83f233ed6ed98a74ddba55e700cc53ab46f18f17bb93425041a2ed8b6bfaa467fe2ccfacd93d9ff634ab9c1620b81cfc9bc04dae204170b16d128b0fe778f15249b37f94814846be2176435e37d03d2b97423ef20e56b7783dd02dc31f84dec2752e0339e7a392b34e2a2636d5a92f8b10fe3f53e0ae8f8e8235ce73a9b52fe0703c6bea30a064830511fc2658dfe45e295d2f6087d1aa01a1c26031b84157042de8248b66b8988506e6eaa3b26471848b561d60a43bf87a0048263f31470813677831c09751e8b5a89d0ca679baea44171a3e0a21123904d998fa46cfd145848f44683e4885fc7672fb042c42c9b649eaa8bd2df34dc5ed231d16abe11936fd3b29d0c61cc51c8c2cc56a1fccba2ccdcb44c9f405215b9b1121e02fef2108b0833b5a463f3ab2b0616ff934312e51206d8e9b205265b11514ea36b2d57c9137eb539c7134bd27319cecf9727662ada18af056bcf1f6653e465e53bce7ce328468323404e36e71bea81c9c42dacb795c63d41c3f69233672909f2ca1c95f1b59288951b91874a12b63e2aabf95de87893c8a56561f2775c78034aa05a875a20d2324e07a91b1a16d63ed926ac4fc13b87f3d8b1ac2b8e88f58ca60bde7b8019ddcd84ed8a65dbc54c1ec0e4ba333b3c00882acc3d80f381e6ce3cb9d47f7f9557b3959acba248ec433fe8626fd8480891dd6dcbbda54c29c935086d0852082fef16d616bf1a64067356802bee2590b7ea16d286bf67645c7e2d40de92afd9655a35aa4696d262ac5187fc6d5cac6fa490134a2ebfd7d337a7cb51bac54d8ebc237d23856cf043f3bd604c137202883b7a40b22034eeb8e335dacbb5113a331f4f7ef40d730d2e3f3331721466e3f2f338ad5cfe9122f1e99b91cb72f9b90a71f9f9ab1012a79f6920af48a22c72cb8e3b4aa2d8112547cceec84d4a45a56a54892ad0e5e7b28c5c8dab5d7e8a643bda90288dfe4c2b7d33d990387507bbaa5d7e22afa75b24b90dd3ee1b76641a1312a79b5b6d58ee06aa5a6c51550389434920c1b824f0255474b8e25e86c0815ff34b489c8d04d38df8eb159e00f71bec7a4882edfd55e02e778624d8c2950a7c2596bb3df75b480289e572cfbd875824964b4189e54e3087d8a2b862ed088add9ef5cdca6582bd52a8892ceee8babcca39a1c3d156a309f8f58a62b878524ad9fdccccccddcfb265bb4fba9d4e6ad8b17e9d34d58825130f29a52783902d594ac9cfdd31049743c949296fb03c4a2f375837a92089acdced9b66c8a2aea462031877d5fd659aea9aa06a0516fceaac1eb9123ce79c9973da1759aeab024b3f037a76453b704553e08ac680a79c5681f26553f375e6ecc161ce9c73ce39a7cf39e79c734ba19e522ae37da7266aa27ea652a9ef4f79def3c87c69bac012b8eaf09b5b71dc36b7c4eee4ee9c386823e81bf9041a5cee7cd9375c66357c3f5b82e0b6964fa4c1e5cedf497de977525f0a799cf4a7c215ed70a7be17f2a0be56273af665c0214e86c478ef3d0c380415ae5648bdf7ab0e7752e18aa2bef4ab1dd4977e45c318d88210905f40f6f1e69b4e75d28ebaeb5a05026a260553f24000635e057e3f03ca3c0ac4f12998926751dcf4058a7537ec9cd47d73c959ea643ee66338fe2bcd3c9117ea63defb9d182fa6d6eb5faa5592414fe0215b18f00285972736e6bf975207f7a587f98ae353cfd3bd0c8e97a6958ee7cdfc6cb1799c6ce1cef733210f8e2f1c02f3a9e7910963c252ed4a30a91015c662a7f90308c358b6f54b0d4c6821490808443ab410927024a99266928078161402587a10c0ef55a0e97180303ff3727a19fb31a81040f9c231a3e32da61119e91809c971b9529094a48214084992f14c5ffa18cc7f329e1793fa30e4711213f2c0fc57fa4a257e8162a743b19433ad74bcaf807c79a1573f2e954c5ffa60aa29880a7ce957938ec779a0101f5ca853d47d4ea760ba3a5287cba2d27bcfc2f7312f4d3a32323120ffb450df86ae7456fd9ecc7bbf23f35e873b38de7b2fe4b16ec33d8e27f2aade92796e48cc7f5ff2bed4ad569899a9cfe3fdcc8ecce30879625e26946ec385de977ec72b79315f58823181a93016437decf431d37b1fb36ffa7e409eb9cd090c1292c4f5498b276b46147399b19e36eaa2a67550cf6cd85b185aeb79263046be4a2dfbc96926f2561bf53891b7e4aba87b66d8304e72c38edd1333fdcbe774f778eb855fba6e494b9aab48f7744f1be92e32c3eee99e067ca37d37b9b0eacf0b42df051aaedafbf926bcbf4ba7851174ab04f3fe42b6dc1c6f79ff305781a78f953ea6c7bd439d2f5cf57f4fe4e57dbfd3fd17f238e9e739853121fb84dcd33de16b03ad14dba63f05768fb762a44db7cb974ef739ed41dee3edce4546d74b44201fb94d0c6424b7298289eed64f2d283b1f6f79a6f7e7206f9ddef98767fc6299cf0f5053fba77f79d3f75ee93b6f59fff4cc6dfc4f60d3dcc6df8244d7768ff8ddf3a8d99523c0f011d93a45449fd036cddf32386050a9179fed65c0d3c75414587a3bb91477733e836c1fab7f9adc73f6f4727a1bf2d43f3d4ff7db665f38ce9eb002f7dbef70bf853c4e68b8b3fd29e47909b7d78999fef3bed47de57edb6ae9e3927a7c7c187931f038caf5e728fcc2317301a964e4918c7d7eb8c701762f03963e06b40f9342995e6875798b81663417edc791f81e383afa80077e1c49af56e5a8dacf7dcc7e877a79bdc25414f7f5fb6bf730218ffdee794acf3d9117f7d6f35ad5c3fd8e2e1797d03dd761de82a97f8162fb8eaed50adc97de47a7631bfc5191581f3a3ab1faa8efc021a8ef9e038738d97e488581a996fb9d8a0a4370354be0d782901e9f9aece787ad474cfbc4d8a9520322c45b323f8218c63d2c631f1ce0cbcb80dcc7ace609dcbeafa0e9a56927a771c4c06c3fbcc54134a41f48407ce8d43848ade642102035766145bf02f205f35d107e79fb31ee51dfe18a765db8ea4f39d139bd3f6b7bff9cfa5c77bff3f2dd77873bdca7421e5428431e1b4ab7912fee14c662b5fb975088dbb88d3310c3d834db80d4b8c8bb06e06706566ab1e3c98876995cfe0030e99be63fcdc08eaa95bc4994716578a2f5cdaabf2fff89e8f4c43d9d80aefcbab063bd7c320ac08f8db518843d72fded111f3fbc8563e41f2bb27e74f10c581f07c87d0c587a19b07b18f0fb14e8a140d34bd30c285f3840f98a01e54b06942f18f9922f2fc7fd81ae7f87b6c7c260aa13dae6058a9dd78509b3fc1ee7861d5f472571fa312075f8980aa17447697433c036fe63c9e5c280d4e1c31a61f943b05558aed55a954a14592c972bc86a0b32bfe7f7f74d900ac8572c56fa3ac3d516eac0d8f7a759cd473d91979fc2d50c572bd4b7bf536dc8e364863bdca3429eeeb91f45d795790e8c7918d0fe0944fd2908eb01d992f287c4e1ae3ff7780bf5fe5e5d92617f64d96a85fa32bf535f26e471b23d8ffdfa5bb8c37d4cc8837aee731ac6047c69a803f35e0ae49769a5635fde1d144c188bbd7cecfb58e9ebc74c1ff33ed6fd6b4ef0071036812c731b97492fece3a34a4711996d4ae2ad58ca977fa54c82008d4e0d960e026a11775a5822ab0052eaa8c18eb20889d66812ac481609f1e136feb6da9a0e163bb2ae8b5d98d79fe686a9ac4559ade2014f7dfa869f3269f714e45df9cff79f482d3291953bafcc232c3ca69116769c46957b266df8895c66182079cf3c1c1b080805f636cb126af71df6cf4c5293b0815a6417ea078101764bbf712ebc60bd69e2d1d42401a9838491af538fb9fd4367578e008347a4d40f4883df7b9a163becaebbf3baeebabbaf95ab5ce56a0df9082b89f2848b242e6aa3aabb9fee02080aa1a4db85236b54cd80bac07247eee9e2cae9093b72cf898a75e1057bbd882124095c8395425b44b9a30335b946461e85db82cb1d1d480a6db1c5833c8a941e73b74da1f39e66c87aef4d530a7e3defa5d451df0b5544d8fe12f5265f09a4e91b2e9cd9917b6487ea71e534c5726fbdb07f2b7f274d29f0ed7cc80db52341fb5c1174bfbdd775e13804e976df2fd781a40efe93146b42ea59c15fa6b005efc03a728f3f07bec0a17ae440d6a2bc32bb2a1f16ba0274995fecfc5ef7d2e4d5fa1e33b9df7fe1d8cf976f7d14bafb1ccf690e247247eaa16c51ba112afe24bf0406ccf29903b5c8a3039d68f6eb983646186104228608823109599edddd2d6bb195c05aac5272b5c2bc043b0439ae613aec58d8b006f9568eab5eadd5e33cfa32f41892c401254e5fb79bfdb69eb7a0fb00683f680224857ea0e54aa11f3cb9fe6e44dfacb877c065c0f5ef9a2301598fd1bca503e404ec05f344de8a797f0f06641a0cb86201e6633e26e421f253c405f6820999466d0a55645329eb0590fd00e62fff14ca1619f51ccdac3496bee99f09ccdbdcb5bf70df5921ece8488e747a6e4b52dfd8f7afa0c75ab4a15ff9d167462d228d3ee3b877cfe2b5d93365de8a4d5813494c26dd8ad1284caeff6cd22df1c7b7f48d147a23a9b9592ca86f467726aebf47e91bafc1041c48e2b8155287bf876ec58ba40ecf5283f59a23b568013b2ff7f44a9a912f2ebd928a2cc6e1e8c21e0e6b18c311ceb691d2ddf42770a4f990c669c338829a2bdf144adaf77c2471e8cbf742b66427511dbb3b4dd21c49ea7032a48dbf1220a07c8e7847ece8b3a3eb336fc999d7ea3f1b5dd78158d71d49bcfed7c53b3ad25b47f22c27d422b64798333ad2f5b749d0fcc85d1ec1c86cafb478b2a6d63ba5eb27f71bf882c4a852499c0580205b3856b0a4b09932b9cd153491d865424e0b2f746b8676820e9054568c8e6416a2d1c6d0d4d8184a9b4fccf764ac7bd1f02f27fcb2632e33cc656e6e70e6b2ea7237a72eafe88a7eccdd4a974bdd274d2f3b52c7fc0b481df31b345da6463433cbf29b1c6a448fe603e8158ac4eaf10079a382146958757a741115e40d77953b0c2be78665b9e6cbad6f25ce0a52c7fcf928d9aa00dbcc9fe217777271e78f20481df33f2e6c075219b3c4814dbef377646047ba05c2cf0664324b8c36dbbe99df9d3477f606ebded8a84b97527af3c3c24d3cb0e0b86eebb69d16e9735f2af3fea7c9a5b496c6af7ed7c6a838a4956532598b4cf0a531d786ebf107830b230370c52fa19822e84a28a690e16ee08a4312ee0d686f30a3831bc42ff754a8713dfa6db4bb0de17074b7f085be41b86b8279b9f73629e087c5f5eab781a9dba084e2063bb85ef74171032d2bff986f034b7705d7f33690666629906d89bc61d8467ffbd9826e780629d21f3726976e404028f734cf206fb80629d2a745b886ede9f6260ec31a4a4484644d2f6d7a0972ba497c24145520b9cc32a9e37e6f625d9d158a7e44dd5116c9a21c2c89c349a40efaf40f205b7c03dbd0a7a14cc5e1c83fa0d8660b47dee152d665d9c785156554b66d7493511a7db95596b1122a73e933adb5fae11dd0ca6d634d8e4b9f6ea12f478985379a958157b6ee94db24fdb60d8aeaca9f261ef2a5d339a97be5da29c8f33ab37bf7e550448bb34b2fa28589161fb414d152440b17d1c21c4ee9ce0497aba79d3c3d76b4d8df26568bfdac49b926368728d26dfbb135bf719446c5d51963bfafba90da9de33934e94d152e4f9e29677348524a19b6bbbb7bbbfb74d9dd3d96dfa59ca3ca8b62f95bd61f74ffdc29b1d49a93b0a0e28e0dbbddb1eee923b795dc367a1214012ab234310415405c644bdc9189c4b82317b1952206ea6e33babbbb5b47321c11030c189151d01dd9c78a26b849d7884445484104962656a421f46a25eee82ab2a159ee400746909421850624aabc3a892ab4b8dd3fd65861c6bc42873baf08e3ceff23ae942514e0969e33f1285d95b7fa48c80aa1dbf1e86826d44c1cd851f5002455da465ab922626165716d691bf900245cda465e5558e4c6ad5fbf6fc6f9aaa7f9517555453ef3615bd39a06c62d00cc0eecd845475384c160324cb07aab8faa00823d1bb0c466795ffd73bbf6c2436559e49a5c7b0a19952e3410a1e50c27af867284142f1284b08a18aebfaaa690a83e3b93eac10d4c3cdca6941842064786b1a042e8fac7a8a40a056c48f1c61049a630a2c393275174779b8a54f18731f170ebeeced6741d0232c8628d1e18d9e10d365e8ec47524f7cfba0b80c68a3bd62ce08e397698d02f475985c889db3ffb6b142cb8fd56158511b7ffa580dd7e510a2b6e3fcb65e4b6d01439b82d546576fbad2c4aa28c12df9c2691ecfabf5422a0d8e10030c41af80cd5705b68e88adbff220d42505871fb4d261eedbad636142aaa1aec285e9eaf16c284ebef0d215403cded2fd927426cfcd96eca135194e103cec4a36d1a54ac0ce0f66f314ba9c4aeffac3198f24568bf20335eec677bca935bb36365fe1c527c8073bb8640a4aa342b476045b4232431d4683dc021061f2932f010a39221a96d24ac6de49da1a76de41dadca871d603d17261392b204929c2eadb252342bfd60a3db467b0d8bba524889a03bbe50140e64bf2b8594b0c2444889232b4af0804a900c0e847db95228c808cd6e570a05c510d483152015165adcf15946684f5c29447bc29fe34a215a8f57742b90e45e25da97ae6a6465ab8158b2558d74648bd22460e44e7bb4c857a7452ebab51e85d5a8083b76a0cbe55e86cba1faa507ebd1854f0eb603d2395fe2373256e221317df064cda358c63ac89618aa66d0fd528a3edcce913aa45896fee3c3b24de6326f19fb33c7f99c1eb2333b3bb3f4979be424273929597a385b9cbff90ee2ac5d36cd2ab2ac4525dc93f26c581786cbc59ab4cf8c2f2ed76a2666d6e284659865ecb3858c98cb958db8dc31075deebaaee676396eb7e3765aa6dcaeebdea2b182dbbd0a8d2e6ef79fc611b77b718b91db3d6bcb95db7d35f1e86a65d5deb8dc6f9b0d3f94d25c71c79a1d97527ac610979ec1c6a53d20baf445faacda0e669821e54d857d5e33e6b8358b10d75fe559c6b8fea2194cf0408cebdf2c1aed765cbb483206908a5519a672ddddff3ccb5883073f77fea712e1499037e79cde0c921464f44046ad0c2224246a104248b2cec4a32dd2183a40c0edafdf932e4c3c7c0c99a37ad3d00f3fbcc4fc69973b7fced96fdbd463afdc39c29d4dec5869c428a23f2849be39e79c7e65042eee98a30077dcd1002c4061c4c210420c30cab8fdd2563b4455b1aac771dc15f6bb52e80bdab5555a505ffc1a5108a8f405173a705218756fbfd072439da105340a240e8c243bcc17bf46248a111d2152b2a9ec1751c4adcac6a88892e8bf05e3099217bf461c9a4dfb6050c181a598b9101859288922488dda290c16d752d8d4954251f80cdb5d2914a50bd6220a961f2b73a550943344b852a889254f60c1e2f93daf97ce3927ade6c513dd37e3c73f2ce626fe31f3fff8f8fc70e5c6b063c324037a9e05d2841ed60bd50054d8535f56084b9dc8e73f5d61ffc5fbaf63dd73a467903839dc1860dd44f5b862f4776cd985dc754aaec73e7e1a6b38aac251e5c3a5bb5e6d58adb08ef1cce5ef969cf273e40ba04e62d24fba6552df3497bef19ed8f523b52d7d534d9c17c27cfa0883cd5cb8848e41923083d4c177642e9763b5d6dad5ae76b5d61a76acf9b7d00076b7d09bc29fa510a89ffc28d0c10a4a95d421654e9280a20a14dc4c14322d88263916376fff8e1907c05a62e19cd63723ffdc27faa67ffcc7bdde205c97bb75f7db94d2d5e214cbbab37f6319ecdfeead5555ece56e8f9bb4d80be828585247f75b29ac874fbab8a36515753224c14cd422bf0db263edc2af6df839946dd361fdd911b2f8a7c879984e29a5343539e44fcaccfc514a671593b3a51a9a98b975a96bd3ee40497ba33133f04c38b0fc638cfdea3c92a9d1103c2444eeab39553432d75ba9a31a2ed7dd7dcaf98d31d7251629c1a751c5d8af52777729e7fca68d51c3fe690db08bf99e289f8fe6cfe72f240e0d5b0622eb3f7960295dc37a38ca233be7d1fc5aad85a966296c8a74e79cb4a483ed772b1b6fa017b538bf9b6f8bb0db53fa35a421a5f4ce4be5579c17f58d17b4e77393869369379d3d7b4af9f45b5ae91ab25ed76d552d8eb69ba34d7ba34dbba9bc65b1b62eb64be2c93f4db1abca3cf97d86ef56d26897dff20a866a6022014f1a61815d39a4c488dbe10b7d4fa0ba2fa88830bb6044edf273615867d236fcb98facb94f4f59e3a73858e93edfa1ac510a036b535d60bf9a95ff69b1f23d2ff22a5ec4ef42bce5d55a15ffd4faab1daf7bcf7dee38593b9dcb6d587ea8150a72a806262b16bc6ec50237a4ca17fafabb498599dbdffeaa1504666effcced17c1f6fdf4b77005810e51d04335cc5c07e59011da6d10c84ce9391686745f7dac76ba1e352cfd58ed54e64286b9cd1676943549f35add81276b4698a3dbefcdd9ec99796b1691366d83db33c9ed375d86756ccefac6c39ea1dbd3605fa6b749b76bb727114ccc28b6b2a617b75f8e539630812409fd63c70de87e9c5fdc7ec90c9862947e82b2825f0bd1535fa537813df6557a6f3699b3c96d3f67f349ff0cea9bd39d59bac54ecc28b1db0feb89e5f67c6ea30deb7136e99b55cc65b0fef5fb4b478279191287c39641eae8ef9e267d333faefb0de4a7a057f04b415e17a4d4b19aefc99d52e989bc3a30d6fdb7c128d0ed39bb5e80fe052ba0bf0369f857b7e3852bfeef4da57ff9d2bf84d26d60b7571caed0afea35b7e92237e2048c2e468d4bf0ef9f44781ab2d535481b2fb1580630c00823ac5630307774249c177e22248e87466e7f73d83d4c427f05606e43867558d11d59740d3be97c3283c2396bb1ad1376a5ba342d7acb9174bc555556bc638bd791faab0dfd55c3fe6926768431f299893ac058269edc619d98fd34f176f7769f5d2abd572a954ade61ff7c45a8ec888c1ee99b9e7907796b1a55b7aa69f4fd17f27ce16ae77bd313799d9ec82bf5445e2fbfda81f1c2d56ae7e589bc504fe40513127975388a3deb20b7a9df4a85fef9335c4dc91ef6ec5542b67f54cd2bd388bbc2362d06f52fe16ae7e54f7f0a794eff62026152f66dc863df14ae764c2ff332210f036105d0cbf4445e68d85174b1a8d8fe9195d3e30cabe34389fd01b68086ed3f828cdb4f5ae4f7598bdca2db52e8eedf1b3f2d5279efd934f270ae66974aa54e8a12d3142947889822a5895237857604e40532ec285b088f18769449a8d40d49dc0084373b12820b33163b32ec64dfab45497c2043e4e5812bff9727f22acd52a92b152df911aa41a99bbcf3bde9c56743fcc0c18618ea39e2854d8128906545ac09e431fdf72f20cfcb7bb3c87f70c27f30fa698264f94f2003ade005641ac84158ecc841b4565b0287395b82064455d0b0231371b335685064a50776e4221d34277e92a0c1e84a187664a31a8e74331eb2d0278eb4e08922b5232c67d8918f8250b499ec065ed1941c8c31250731a42c5aecc84840231c51f9e1e76787ef880a50142a30d852db32861db9b6061355d11a4abad625cb8c8d2235c8e024e67264474eeadad112536cb425bce0c27482fd38ec89202bb427a0f8acfce5134aa4a104d622bfcced1818f603db03fbdbc28eddb3be794274f93ba86f7064e9968a1644bb6c54a5a8c7366a2cdd6ed437fcf38acbfa86b7cb3e2d3255628fec539924b2f2e2f6ef7c2f47df4c8e565a7b7047c8b02ce39e16e723b1f673bcc5729b69439657697d814e06b9a760108902e78980fbad7ad273b95a34f66938be306feae97b29d0c54a62533fb26e0aa9d0d5e214826605f2f06bb5a2114d9e2d09cbb016a76d815d3d4f9a9f00f819809fef2d117e3ef7489cf9f2bdfcf7acbe898199f3653ef5144cd114a55446e653a9d45370b43725937a1c60102913ba5221abc519d3435e1abe302fcc57930a0ee8d2a5cb044e31e0bc30e04a854e04a7ef589879115e9a5850fd8740dc66823c217c0f1e105ec76d668eb5c1a60210ae587010de5fbe0010ae58e817e145088784c082f1b123a3a484d1c1a668c2da368fe2e47da92327b12bf05bf4583b5c93025c972e5db4dcbab1fa468a2dcecf0182005614cc5b431170e176ed11578116e4b6b044767c3bbb7204184a44601ad583e782855df95c172bbf666e7212a156b384c98ed83c42a26868c916cc9f7771fdfb7e5b05ea90fadc0fd99ebe735fc3badaa135ac00bf36509ada4a1df25d081cccb834e3211fd9656905367447793469433e62dc5166b9e112f8f2d3eca8c9e1a292a44a57b90cf31e0670e5d0122472e8c18778f099245c39c4c3929bba7228872377b4b7979cbebe41c1bc2278206ec39ff4034947d2e60fc9f2c60f49fc404312b1d70c596ec34637a71b1c595cdc51b5c51d45a33bbab4b8a324cae28ed2080756ee28b1f841c914337a3022c41a2fc6e2721b6d0039d183315c60c49317bbe00a173c448143111fbc5a90f2c30f48a038e30ba1979c92c50e8ab8e244075704f1e2d7f11691266cf83083284ddc40450c5efc3fbc45c44796450e50a8449182cb8b1f88b788e0008410d2164b72b0850d5e0cbb39ad86959f225d9c809245183d5c214692082e48425ffc9affd1064810f9708413468cf1daac08e204e5053478d1145f6367777cd65644c5dbb66d49361a92148e8644546449b5ddd00b8ebc2b875e40bba82b875e40c65125626405555c5144066d34c1c40e3a9c9a4605df1123ebcfa1dc416697b0760887d9954338fc70b972e88626b787b76892dbf07c164b14ff6baa19d29adbb04b9a7bce09fc3d80f0742b351bba81b925f52997b17cb769cd5bb4459f456b5f592c4e14c37053d94993bc259dd6c64d6655cc4f93faa693688d26f5d32de3bc340956eb9bfeefc52fc1a9841d290c269f7360475aa3491b6c239238dfe5dfaa9480248eeaf29792240ecd2237a39bc37f425fa140fc5c7e9ac567df54180ce69582a08136a573968e92d4e86c5312596527fc155d2fc0ad675c0f80cff5179598a08e8b6407d7c37b641b92c9e0fa7ba58f36a593bb16d0bae9ec511eb9ee524a261edccbfc920905ee4eca8239dad93075c8062ed72dad094167bf4bb9cede2d31a3200c1c0cac1c9214bd9cacf8895c0ed72003090612b80025bd642b60fca47f1494666471c2e7c57d74fbca85306cbf852a9f208f93ed7960a03f9fdb421886c010c2e6cf906ea100fe120362b32734643e01ad440b625629fb2547b9bf7ef79ef85d48636338fc8ab0fdd47be98131866ddfb3932f252d9e8056a20551a2519f163d0545e346849d9fe3b5fe04a912caa4452a6bd19ffe18b12395cd77daa4451f6f51a09f168db0e3936e79fff44ea62d8ff64496684184935dca29bf512f2911b62765a44b417a3dfed2b85eb3cb9e2f361394435bb85c0a76974d3c4c3e7664b176b898094bc35aa52cc293356337bbfedeecfc259b7667fb489c25b2d5b486c1ec2380e60c3b0abb74d2dc559cfbb8c568cf75ff4bc35983b9c08eb376dd9f7bd6e696a4bee16e7685e78d9e4481c84581aae4cbd0b7f7cd3b5a294729dd28a5b46edbe6a19c379c88fc884ef739db6e74cae935de74fa07a055edeededededeed277477cb539239a7cf39a7cf397dceed4729a5f449ff45564d70da83e51f41cdf50e3f362cbf3b0d5628f00dc25ddcf97da3dca4936b92c6e46260adadd9009d6d78ecb2c5103e65adb5d6b7e756b52aa9f2af7247dbf0ecdfe1f21bb4b400460d5c67296ea18ca2bb67f069afbb9b065b4a40e352840f96a001547bd9206ec75ceee548a3008e33e1722f81b81cc70dcd7074b9981297fb6fc1e5b81550b9dc100d4bb8ed270ea89052d61892618aa118a05c2a69deb25f4c8d50171aaebf55d9707d08e6c56529969f2f3f570508aa317f4063a03180b4bbbd372b839fabc5ed27cfd90da809b981349bede7f2dee536db6f2edadef6837d1e48a92b751d2704bb62d471251f3f3626b60964ab3374c0edf7b834259523212e466cf54b933f6a03d86fd3c79b33fda95f7afb6e20a090167dde86633dbd842fb4e8817c55de6218fc10773be26ecf425adcbe43202dce366c15bb97a6526982d20401de4283186e0d4720462c90be61d187071f893382c4f1bbf1d718b0138672b7f75a47b2497b60cc1f3393ed9917f0c3432ce0226becc355b697371099e56ebf6dd2e885ed9989cd0677e418cda60402b42930e5087ca8685cbed16d73b931c2ddfeeb1ba6cd20a90104834986e5608165892df664c38ef247f1d61c6dd3bfd336fd34aa16ab2c6a1daf462cf730cc12e10351c5ac246454d20d1c6a68f0400b1facf8bcda89294931a46212c77d50c49d3fe9106d68b162f08bb04ac01d6b92d4b8fdb3dba0e1761b40dc6ec3095114592c9628b29ab348f2a1bb356acdc18f0f4d830b9a30b791958d299068f961830b1cd060c30c26b5a4a42239a071b4e50daf884f62c65e740b6cd7850c16c795424dccf049cc78a085165a489c1472c08c2185d8885dd79148eeca72fd4df86e13b28813c38eef2fb248b8fe3a64093344c1f87116965e1f2d7a16b65ef9dc3454d1c44861c79f3b3a1cee44617908082ed7df65440b2d6416b6fb250beb2e136e66ee0bdd2aa1e884357eac64b9d46713e0d2fef128049038f3ef0c5fc0bdcb07fd8d7e9538399d6c5d69439f521d2e7479447614615048769351f395532c3fcbdcc443be9361f9e7f4a05839e70c6873ce195eca0196304c18dc279592caa794524aa9a42e039ed4a8e48c16863d0d15a2190100802000d314003028100c0704428148281c9466c97614000c7c984c80549609e324887118062162100100180300010032223434a3006a49d24a09e5875e96b9a9420a4d1bf61d2b220c8854e6651b430124cb0e3a7c5dcf6cae9cbb239d0e1af20c1f0efe7080cb55eba54558738288d4f77de865b66aa3b4d77ab1ca41a0d1ec71bdb329c8c4355016a1e71d6309418398721f5d80b6b7adec8ed27a8aeac522a901636c59ca4374ecb5d9065b595221cd40925be9b8492d53c70e390ce3546b85d8bf77baf66900eab53b46105a26db4c0ee06d4c1c0293cb83a3a5a355d50e5933b6c66ac8ac7cc663e869d11a863b7caff5f2e4820237642dbb5e3edc8cba2a9faf37325cfe00d95753b48aabf3049c19b6a0714b21c4f6922bec122213c1186a785789856df21294cebc86faddd84d1fcbc4a2ccb22929bc0b413145a4f03c111b0fc4694b41e91cb96262628ffb59469fe56d0109003eb5809ba141816fc66992a6082b425ed2dadd5f2e5a39e5425945d49e49dcef3addcca2ee8fcd7786f66841ab889b90c855909637aab729796b84f20ff35ac88689b4f4951320c5fb25d506c94a0c91b4be59b62f5148db256944edbbb02c025d405956082389f51a0f412d3b817dc51df1b868795e91b80703afb80ea2ce1c6927d77cc5d91087fbc2e485a6ba8b19a1e351458749b71c8201ba8e1b0edce7d99f7a2a791be5c11464374df03a7b1aa53d5588315b3047b033a72175737a0c308be2fab2111ddd48d8a91879166c8dfb13291c0032161657004d5e9b8883f4db015a2319a117cb68d6595abe54ac2799515c19b2f5aad084e5824b5c1190e1c0e2774186ee93b418436875abc8f6aaeba2315087a0a7fff1f058abdac2234eb1862d70c8255d431c27665936c734f10b50791a86c330032e2530a1f0c2b4cbf09aef303b514928ab314ee0fa6c331f78f7b5d3ced8169808646d4f592c9b2ddd441f6eba692c9dc7eea9e0f148a95c2b498afb8aee713d5e4c7dcbf4d79793913e850695f2e30f6ace7a4e296d5bb26c1e5eb974c9a95ff66adb885bf5ada242d37c6168df53628647dc1e5948dc7010df002bcecffc16110b9fb52fdbcc35f0538f315080a61b75e87160bfee3183d00e22c18e49adc0b6e83b57f4c680059572dce6c6cfd1ab42635233421bdffe9237c3eec9b5e112f433fff60d44b2407c3febf2e53aedf6dda709409abba0db222a337d10784de393b629bc1ef0043f852f20845b771e0219c67f9041053008e55ba7b98f3bf27be6513f61c50753c32f020fc89f877a3c0929e4931e89f23890430e6fafc71a7d76d39e4b4b6fc9bbea84bc55be361be112a5158c84b5c3b18f0865d9911152049e557d13966e76a4153ba33dc2bc58ea8a68f48824a16107120ceba6072b6939a104996529f7f42d424499fad1f90b1fa7cbb0b5f1458987c3a655fdbd97b00d8931c54e4c9d2aef409d82aee804be4d789b65b37005e52140a1d8aafca1075c31101ad7eb5bab909eb8750a7a2d497935dccea7c74f4ac70defa75ebebc1cc70c1f928e98c009687831ab41ee611a394b8cbc45c04290ad17b5863e1502ffa6e35c8ff6e9078d643986d1d1075e5fc650a5f14b1f7c32bea6b240e50a7b661a9a1ac8322c24d08310016c26d8fdeed0984bf7b2d2bc8849344b10d9bd54f9d5a6109d93ce7cafcd0e8b160900cc65da47d2f1458ca7e83b344057d5c89276aec1d150dce459c3d389e1fb8721ea327e6ac02b32506bfadc4b7cf72e600c8325d6ff338456b8f3408b301c1408e8fb8b26a463c709f00c73ecc1f2f3b17d1b2f631868d8d78c2fb4a4e6b056df8f601a0919e9bd0f462069405693c7a34127d8ee3425440cf2a9190aba13a1bb82048201ab41775e16e88234541caa70d5409dd6baf619b416706d10108fd94cefdfda0d598f25d50f1c2800acc96e1295ec54a374407d36e9771524c25de09fdaa7b9ba7848789f317d15b706fd2abc5d847983a90aab741cf23e7dbca5e41019411848a737105a9ec31748ca7dd01dcd26e0ec2d200fd6892f12fbbbfb6231a1bd9d380feed28014ff05856003b8adc18d6c341e8ced8ca2ba98bb08395a4991f4625e7244282649f19ebf9c7cca4d8f645e9f065f683ce83d4feaf19f01e8e439167e1afadc497179fcba9782051059aad07c1ea0eb8e1436e931dc92b5988c49f9c39362f276d2fda8decc1a7ee97e1b5fb8526120ebbaca18bd8f22a00c7a950a4025788af509ec792478204be7d4a10417e99928923f8ddc17c78dcdc9a5de85d367af293273693a08364037d907bbcfb93dd7502b061082a5c1a67a4294c0c18995052da8613e056c9fc10afe5c359cdd8358b1f8aaaf59cce79d50e4306a88ee6ce235f59c0bc44ce8be234dc318c6e98f7f37bb21f5718c2f0c0ae805b4afcb77f0b97efe6799bb3258435ee48a0cc2bd955bcefd70803d79e6dc51c3d4074f45d8d8bbb83a9973b7f6e2342ce50e83427aafac33fe90ba60eb949abd1a0f39301d6ab9d3070792ff9c1da3c01f6e11879aa95315d241f4998264b11c41a03b5f16150eb93c90f3755aaae826786d4132c605d3820c88474aa79b8cb245a4bce1cea202e177c5024556af3b780b79452ab904345ad36f1f3534b04772d53d8368df7b782045ff72ede5178848e78b3764028a087936c22e6b813602b59128b75cb7b16277e76e7ef6da3ee0ca628d52ea7c3ca6e561103151b599c504dbbd05110319645c08f441c1a8b3b2354bdc99231983c908cd3c9d74896f05d77bd58b85126eb6fb470c6d42776f3b8f35ef9867b15664d880689bca3e329b479a6bb3e55637100b72dfc6b0dfeb6d941ec9c411e0476a1a8df0ce29f419023603d2751cf6c1bb3e37d3fcd4c0b80ca319a7d34b00a8255f67672c04dcd1ad06140faa76ef130c8b16f6a1349a746b94864510ee2dec79c6b47a2eee2d3ac7c780a24dc107c006f07fb35c63f9b099f9fb18ea5751a041b2c079df904538060ee0138e2bc487f8a44916df6fb140da3985f458429c9dde5f9978c3b39f6095d5a98ee918479882bd130285ea6f893b59f79e08b420226a12fef81970bda7eb464b104bc45b40a9768f6288b592073f25b4473362a9c22e219f43bfa6a191fc91eb0a4951ac75d5cb1328928ae74314c7d2ea4b895e6f195ccc41ec6838f11c4b2e517da1857f4587901e190ddd8d333703f2578a32a62923b9064503b6e062d38da5b948464a01d1e471ea34206e3881d633883414241f8758c5a0c10b6b5b8a67bcd0d8a164d1883e72369d01a6906446f69b378a02b46ed701fbcf20ba6511b29a49e845d01c49a51269ecf0ca202ace3a32b70802194834b06e3b7205d508165e33bec099a19dc927c11c1f00b1f4877f5547a0074380aa659f584e3e97246b3d5a489de0764d4b5d5b384954c978835366d7991acea2b7bcc32396dcb2ae8e270b9a80408909fee81e72732fab0e42897e68e5402013adb132add98e58a49a8cf5d6a294c28a9610575d7dc7b3a180d878c4f5567cdb8d6e24dc4fe19445d5fd94072c48917a1b888850135fac234585babc3366a159854b8061759a7a26e3b8eff3597e5e256af521c12c56f9ad879ffaa4772aa46f8e22ec30b1a17978791aeacb409b3873776e8f13fa281482f3a4c483f597b6edaf2ff2375fc871ca7f42445b15f739e3b31bd07846a59f1f8d35526ffac8c6721d5519248d284f1fbceff992cd8e7a9c4b8d0bb121b38835af8c6b5f182acc3be40754e5334f1fb0a2d5912eeb9bf1b4e28d90ab008aee414b4d84e7cab695b8f3d03d1ec878ed0945145611d5496682dfe9365410ca865c0262addaffd808d0db3e011532b7a747a7afda5ed03751d621c2ec5666f10a6cf06d2fb8cbf0b24d58b01777db7db1c898a9f88537d9f1c7ec572fa7bf9b0fe34489912a64cf17dda0c728609c1b2cecd85308741e90806fd0cab439bd0ca53963d269fc080579072d7a123faaa53e7c34f79bc66219b06bef21aac319452a1ef113306a379f89d020d0a0ec1900f6d02bbf15057cc05f047b5ec10428ad84e97f8dc1f777ce6495a71fad09d1bb029eae16e16e861cdbbf0c37c5729e48244c529bdd8ed66111fc71076ab73e843587b204e747befff8f6be0f9ce720c07abc9f221a1760a5676a49459ac15c11ad63a30448318920bc8ff58cb1769635613329389e0815327eebc7ff1e6f40aec012d12b49ec67734328f6c1dfa36d7dc86e6247d01d88a4c2ce021243a7b868e4ef6dfabab5b722018beeadd463cb26303ff0a9cacf72954a7983894f0b13ba58cecb8ffc4671ef83b3ebec87d6cd80959a9564cd9b91d5d775a3384717e6b2d3f8271702435b7a1b189bb3244d412d345516fb121f730b38a71f0ef0be470b6db57e1b1a13d748dc55de9cff683c001b98012c7cd2314be96cf48774ee2ca133d7bb56026301e978f3b68fd704148cd6cfda16b5d490311d6a913bf9e394f64428a8e282a18c0f59bcea263408613f68c28561664e0913499cc0254a1a33bba5c485bdd5a40e55b599f2c7b0c81a1b5fcb6bb81db82cc71882bb74df427cb5934332b9667f6c12adc1287c47fbfbdcb373ae020789a5224d8d820bee93b1497c2fc1cd2b1ef875515a392795ebaacdd42413e966e7616d32a2d9f9db9609c8273d188388dce1f7765051b05f32be0b77451669422580d672421c8cf17ee42f27823edeff874918f02cd0dc6773bc15198a88480cf2c2615c4f884b4375f0053917f3d266fea95be76881d35e4c67b3027e8c98d6548bf6871ad9c25c68b915ed508f6955ee97d39aef24c403a4db1a75ccb57effa21564da594c850922f5a7488a37342b58c162de4e2bf305c42e93d1ce449f6e96c354a2c936fcae0de9f253333331a0682f98a9a3f4fcdd7d9ad441f16c8a83785bde342c431d6e982c131f87ca7802ce1b0039036e5157fa26aec699f7655819c57d59a931910c5818900d9b5d34f55a61184dd55034d33366e8e9da35815919985bf86b9d97c9fc25d5cb125fd1d017eb5d4e65f956c618d4913134da136220d279564e0686f5d21705b128b86f898dc6b84f9f1b1fa167b8f51eb87d74c0e7498391f007f1521eca3c6f4018a70596842ece684c0d3ea98c0dd6ad920217020bed64cb4ea5b85cd8a8322588785dfcb1c45d1276fab4afb5ce7441a521b5731631121e43d9e3925e3d12ad355e14a073ec2b572012cada0a4e9c76e1190ab593c09e5b8a437cdc030e234b1148dc260db7ef44d16f278d3233cff7377738b6b93ffd601a98d61d798ee39489a7667e0d1de58084e040bd61e06e46d2c66469caf330a07f3f487196fc6e13577e60d8dbfbcce95b25ec25f6a828bdd9e88a2b253fb909440fa37b78c50aff8d69ad45eb20d2f42c4d5d961d43d482a82fb58248caad9f90e5054d95b9041c59b6b52ab819cbfe628bf34fe7308f1c60ed282fd7012f2c256906d23ac0c6ecc1ca7d057b267715c0a9c7145bdf954ffbd54159af9ca081c81c4b100f17d98512b6e5b857918793bc32ec58b4a52e1eb97c6808343808dfc5140fa7701c8ba64d1e6e62568932212aa1a9b4ecbb9bee148e4882d4a5a8d1c610a0d595d42a2dd3dde5dd4af1401dd506ece7a3157ac2c10a59461e1b506afeecb4877415d7a3a15df7bf39c51dd92ef0b4b248837c2d8a9bf7d611b5752e7bb740473d5a628d64afac6fdb2518b51c374a6700c4349bc22b3b53f2a84d187ca646c47908f6ef81d04da82acabb82557b1e35d830b8a1c37e8a61c90357d3979cb02882ee1c3bd4850851a22c42895ae912fd09bee1bd632174c256d9355f37b9fd94ac44757eff351ea521e48343222af386578c4c521cfd4ce716b6ff11b932a66d84602a594b619f7e3755097faa2a808b3797490a135c7740856ce3f2fbe8b9fda3bf8e4453e263e3a21cecbd674afaed246d22e723290493633a78a6f0d5890c5d3a7de7103da6c51f358c17d00f53da9b619030153b68dd4e1a75454497597c553c56b8c774f783d6686c6e78ef32a4b52b9cecf76275831dfb3e31e6ccf498e114e04a7f73c6e3ba369a3ce948d9f28e92deaee53081a7c9cc77623286dc7402a218f5bd4cd39a5f7eb31165505de3a5e7f8bdc9b65980e6c80f514007487353812fca0bcf7643c725b1aeaff121f7f6586504e0f89133625d99027817e811db7af59600b85003520d8134994fa80f415355923e47a420ec75a14eb98fc09c7904b41d3294d0583765659f36000bd1e4aa4c9b43461ad8d74a4d8392f92a42712a2519382f91a06c2a71f18f8cb3fb497c1f30f90a95d2155250c55f22428a6da50ead3e8328d7a7c106c7b997d68335cf559e0e316615890416742f20574f68972915772681eff28a185c574b5a1499e11ebfc911c9e5e44608cb0ee89d80f84881164cf3ac55b721de9e59e6a6f793cfdd6ff91515a1453f607d9b58d56053fb115a894047b8052b29daab9a2de5ddebe9ef31ed2a7fb62cf68c7ea9f93bdd154be0e832d81c7c845e74068a51412f0cf81357365cb18952c4558a713fe16a718c9ae7f2a9fb08e41fa26132bafdafd77e43410340fae188fe8bafa95b02104f8f3a859b31d4aaed17f926bef966da9f7be067efa70174d3576848bf463f9b847c3279bc2bc79828464333843b417642a3bdd8e50a8894418fc2bb84d505d2c771a4038104b0b35f3f219e8934a00f13b27e07941c45c4f4b72b5b3a8b37c7fdcee0da29c93dbe976e7509075cf90d28d3d9be8191520ae7c49b56419b34014a37f8ccbaffe010a823a1c44085f181b27fc5c36ce6d8fbcf84291cbd3f5753abfdfac1605ad11418836a5db74cb1f64dc774645aa2e80110bd591336cd5d3fa9088af76a8beea86c7beecf1bbef853128d92f29f67b28d690ee0f18c1c4bc931b6744b8c53e244914f5acc95d35165148fc4489488c710ce7db58511b2262ccd38846dac657fde39b3a5ca310c7fd57044f4551b42d52d4d090930360bc61a337053e7afae74974013f7a3fd3500a027f52246a45052b25e479f44cdc42e89eb06e23875012dcca4d517f0d7dd2023c8b9666e47c643cf569aa475ebd115a19e990171e93ef85782f906486c402251a2f7aaead80faf3f59e510026ca64403ee6017701260dcd173cc6fe05114cbb39ee85218a23486ae24a3b71ccc22fffb14a7d8b73ceaf8a7742d122472143405685379c52ba949aff9e4a3276183a600b4fbe36ee3b32b8d9bfdd636b894fc3a40d98f09ae14be288a302532910bde5a6556c70ffacd8e9d08cce3174f20655676cccd4c595207a6228c751635507087ec0020fd312e1298a257974636dca914e1697aaee7201aca8cdb605e793b1431ea302fb038c24dfdf980c33b794416fc57789517cb20b642dd1b1fdd4de3cc581fe093102d4dfddaf3cd08eb710e08814aaa078bf0e490a8c315d6768419a3ffb73ca0dd0c8d795c70940e77adc725180cdb351293bde6f91d7f7e328e9fec608e54c2c4694f151624040abe13317b89429f27553a2293d1a10eff20093302e965ac749ff0d6a08c1ef4c94eaab378d77087cbe0bbd67197add8142f937b1937b7d0e61a784129276854590734ba1d8ee139f767d7fd038a5a9b7bb98b733fdb4adff519f6a843fa1c8439cc1d66e2a558b9a0dbf1104bb5dcf195db1806d66a109c085fa6b0e79627401d25458db0f7ae9179ebf6e05abe00a51242069411c33c19f129b6172a0d0184726e7297cfb8ece9e7912f0188a763830ebe3e9860e4a567a39ea118b10c89ebe282efbad49c58538b01e8a339c18e3e3db4829ee5a0c3f955846052e82927490140f0312894168c7213bac4cee6dc8d0b6478ccdca4086aba53bc0d579ed6b998d62f6df0c730c30fd8fc13aba0d606b48ad1d55e5046e6ba09252379b14a96bab3a28699e2892fdd12d8b890b7289ea8200d31f18d2f898181ef1b86a0cb2876bfea134f616663bf5325a213b69b3c7f7e71d9f8650b9e1ab2e351004821567fd878bd3bd184aaa4c15293f72683649e8866496488b8531be7c6f0810563a7285a744404352a94bbaf1044796c8faf53dcba1bc007751cde97f81838fef67905aa302b5e79a8961175595616d0292f3a451775314977f6c986cfdb160f4da559066c2746fe5b3b88feddceeba21fe0b0b3cae484746410006bbd485120c08b2b636f2595c909a50745d860cd2edfba7dddfcd6048baf459feafda4365780df1b4682378aee995385f1dcac79c5277b691dbb3d76058e1a5b237b1f8f5ca0876a061b6f7c86a8ebf32bead4a74eacb9de75a9ae11ea5dbfcbf3db725550916e2c845ea43d490dcc9baeb23ec9d0db0656dc6701f4b71739c1fd88d70000221b09023a8f00f6a5e975e4f3547ad132056c7d8a1d5672a8afbde412ef3a510755d04c7df16ef8be4989eca9271837af6fae164751efe70c50de7d43e270304500e3152af6db807abf7b2c8150e9d5e0baca3a55688dfe658ac8533e09d0d58d85ef5c5b850c3e6d60d28ab27243bc1e15520d25a9da6760f13a3ef1544a866fce43aaf32c4cadc1923c11a765d091804265ea12463d30da55956b11f0db2318e8fe05e2952a540637c04352888f55aec8f893ebc9dd306efff2c7dec4464503983886beb8a572f51e8fc66ab9b586b25db53baf71408fe99332e0deec31e3644b3758ec90808f0861d8034a4584ba8c5e67f76ff686b8c203c55c242c2369e9671741d76d663fa61af2be2ed046f40db2e4aa9dd958d8e8cb2f7afd2c9cebd241330815a7268e4254d676d3c5db5575fd0fbfdd2744736fc3436df7e1d1e9eaa399a4891632824fc957bfd9c10669c3bfd676182f519d919fe507bfc428d8fe60dde7466a0f587560c8dc4140cc61ab9c9937555c8cb301863018c3978d17055268b29da1eb20b4dc1450ae972cec300bdbaebddc3278d650855e1d8dd0b6d8610310434c0419ec46358b06d4d5cc08328d63d6ac84db707a5bd86ea269e6f94d132da82b58f859e8225783834561209a8709b6aa5a27018a0a3d34dbcb1bdce30d6977e2789d971ff2542a73c6c760362db0c81d1ea95e8ee7315598d542f6971c3cf416cf5474a1e27c8425ed0f62de301d4e9ede8028f5258fae8d0117e8e4668a9e8721f70a28d648a898adb442cbb72e054745a1b3e556f8f7e81ddab1d5a5183b4da94678eff234a2880164ee9d431ac2bde463483bc8e4f23cb49daef111300b2fc5ec3034d9e14c86adcd107e1bc0de7fcc5d4b93c820ebd15b475df795c89bd226db911a7a8af47c39c3fc74a19cd90afecfb3599b7c467081c35be855faabf57ab325176859cc549d994ca015826df03b65fcfe329fb345fcc546564430dd26690104db4e18f5af0f27c2c12ec9e00e84743a4c6035c5eb92294ebc1f6c23acedcf0d5ebc63d60c2b1fcb06652ed0174d1d04b690f06e54307451a2bcebfd380c7d7c067971ce82f6fb6dcf83bd8b75b56b28cf75d9d366a7e3180498fb3d8e0a19549915fdacb49b2d9668080c66a39938ec03b216287e6257264b5cbcd3b06d239922715ef4f27854d50267c6917941595475356390aea8a599d12335eb0748786cc185fe1517e8b3b2ed7d3c21f1c29b04fe6ea517529f24ad21640778f7a9eab3f0fea616cf42840a4afed4cf206e282bd20669b34bbb3ffb734b8cd15b99e7f2156514c10cdd68393bda353656aeccd4cc25ecbfa7846d93544c706100d99e608c893446a539f0e6f67af09ab86516877add7c2c6c30c71bac4b350e15aa223706179a00512a03894d0971d5e9aeeca87cb2ff352c69b419f4b6089fe2d52e03b9bcca29155acac9ecd608b874bb5c9e1cdf0929859b093aa4973a13a854f42d13591de83466cb14b8ce78610481e36a127350f229aa1dcc6c51c69cae2608403839db39dca81beaf2ef539b04fe4fdaa6d51b032cfed26d54d1ad41a0a90a96d8953a8a6d2db73eca24d58a6ad0d3a2c8031a5417b948fce5e9a62e7744ebfd21406fb1312c9372a2ca91d37f708862fd165d74bd07c7c9cae4cb0426ef0beb4192ed1d6219a463dc892a0071dfe6c288ad01954148857972529d3c90579e656c796a34d11e8c3c0e14849060a1006cb591d63f5f892dea79795462dc7b9dd2b6f1932e075cf067ea587e2a258e2a783bd35962578c676dca10c898ca9845e25ca59379dc51947e52d33782edfa0a965b88259afa3371b84e6850b3ba2a0dd91c09dd8c008c147d16786a3ccc5560831aaa5a0a323ea6edd9c7cc7948365665802daa0c4b5c3c32d49c78f7ff21a873d23fd540cb76c6c39f03c0fb053321a59b06f068d5e36031612f562e1864ac1147f81ec9d17182c63f59808b9880bbedd0726289f5242c6cae2831364e7d06dc0670a83cfb3b169104b5a895647c24b5ae35f568c05c70e862080135834daf55a70b0cce98f9e86b1ece88aa107cd6cbbe19454c328013bae1907a917138253abab253a960f325375aa1b7ba0148f25b2f028ebeacdac7346d41794dde2bb9681fcd158519714818a40516a0c25bff78133f49409e647df1a0299d635c222608a51e01e63307d57ac446adbd19ea799c8433f87f643db3656d9b8c341500242c2151c5824d86002bfb1e27c81c3d59b4d5007516ff7efe404a4b0e372055c1c4963c5025e2920a790cd75cfc11032d8a8390c03f995e6c0616086c7d609dd5b3cf82082958984f113567d2ebe441a3683d5b6dc051b8659c355ed81dcaca1ca33bb0519e079ebeb3f148e7798f7452d299916aabb973dcd10ff05bcaf0406a06c941460110dbf1ff20bc2a5f11014cf3a7a542b11297cbb5a24d7065ffae2776e7c4f46f30e3cce31a4288a1bdb9542eec807dcd457e7384d24684d88a321270a8dd86c2e824eba0f3ba65c0901322821f0a86b3c7ac7acfaebbbcd22d415cf114bab55b0e63649710b9d79e72ce40e3d2d9a523111fd1bbab04ce0287c6fb0990ffdbef20909bf613b55cf50055bd88c506219b85ee33ddcac408376fa9a06ab1419e35481131adcab03fd74b9781ba6b2deba5fd8e256de8db69bc3cdd6ba759e6777a421790a7cf20e3b6756acb47dc18a15606801428840f21d9add54fdf5f11dde5bde80836ffedf4bb3e07d4690394d8ef25cb2c845270b0362e8eef27c11c045ef48a343452163f202ba66c8b17047ad2d5b0a5e0d639fc2a591251057719180aa7b2301f79e4078ca93f4bef6d643e5c2335ce6ea3e073f7b81f6e9edbe5f60c97b1bd9030d6e618387bc9edf2768ac8dfa92878ccfd3e91b9d3342dbb80862d194187daae658d75967baf56f1692373eb058d73116c0204524c46832a3a51d300c01fec3b6fa87be570a0e7ed31126e24d65b2dd247170ffedd7f482389a4600b9ef59ba68f26a277df03ef4d91e0954d22e5a8f7d6a97d34c029ab2eb64504f52d577d9c5032e0a08199f513fe14ee108a5abdae2e70f2ef43d658b98df723bf46a8e98fbbd7f31eefda40c634f914a3134204c0016128da1faf0581edbd66b3580ca6e9d8b517b5b778614edc8db84636cc4a975601aafd69dc5ba6621750c9d6a9cdbb00a3198c77a527737d32c63a36160ce53aaf19e05f40b41274f3934f0d1a6740419fc2f118570e8138e31238052d9fc21a7ebc3e5a37ee8598c813af7a8bb7f349360886c1fe53640235381b5ad1342044242fdc103f1ca505ce1f9c5c2062a183c8c40b1debf95736cd05165a5ca58a44fd8a5bfcca4b920c5d71cb8de5eb0070527f0ba69bb5c7724e9cb84aa7a8dde15e671f459e8e4472442120a1644889407d1005b4a302cce5821895423c6aa782ee7b96dfcb6cf568ba0b5956ce70850b7276d7340a83be54a29d7b6c60e016009fdbe3d9ad14c3eb9cb0230e2f0c18b74bfd91f3791aa65f88fddf492ec835e001a4a68f7882351c4058f76e9bd14e67ed0bb64da3ab2f91053840413b322e42b604af8435dc59d4e413547ec7ad091184e7e0c45672bc6d7ee08bc665575101d5cd92cbbaee30cab7a6e34598089d2cee829adccb1ff1fbe4a8929d23c237e61ed34c90398bc9ff8f42a5588962b9e61198d1a288df7901e2e6f37aa6979bf5de7efd239a21c58a1c7d8c6653f245738643107799d10095c8e6ff2f47d6e2db1bc87b498c52de49aac05868a6f87d7136cfd499e2df012997f3955464197219c39748f21068df950ff58756cdf3eb3aaf8ec612adbc2ac1c9f418ab27555c5a7663212cf9dea09caf728f71ab430cc2c556ac81d3dc4e649af93429e5c092aaed48315e6320c2874c250b5317835e51ff94d169dddc86728902f3deaa4822753556b4e1c9515052765f29317b0d659c82017fac9c8c3d6da0efb78187641b8b0e111e5112781822d1c2254d02d347c9d5754f7a53c00498806a998cc819381226a18b383932c778186fa22b8335d25e24ab8a862b93ac340ea42cfe8fa2a23628037e83ac484594c36841fe7aca72bdea8430efcecb8a983a3a2d99ba348f764a57c6ef2c24c876ec25e39c552f217a93aaadfd869512a9c47c06b8c371ab88f8d2ba72841cc6afb77d68f97138ac87cb4b19bf6b6a5edc10bbd1a99e9b866e11256d6fd4311ded2646946452d24fb14e48ce65a56286053a6e66f0d34ce6429a30b4066926bbcd8c75a7a02b9740f517eb7c0d11a1bb4a2595c0fcc598e96deebd1ca340f52902f5d9f02d2965b9a8691932e238a158b885d8244631421cd6e5d942924c646f15bf1c91d7b4d132336d717657b37a4f4969bbcf4101a7ce734f99ce25e3ceecbdae66964fead001abdfd241045e45c6c5b7859ae4701029f329014300b69f5a35a814a9fae448df2cda6aa4345e22167f012f017ef2b4840118a19b24dded57ab0db3a3561f9b62995ddef211a4da568ef532de2a79e666369c446d67081ef22df077ce14a123ccb37149a1569cbe9a756ac9dec917122681d79aa467a42f5cf273c74208c9c45a8119fd425ced7669ff1e3b1c633013d7775984fe7392a2dbb85c32e018f601cc97ebfcb29c98e07f3a257ad4dee3f80e993a2dcc8ebef62ebc6ccd8d9aaea892ced84ff5c97ecaa6595710a7b34fdf8a072322ccc4cba74187f302a0313ac4eeb6e1022269d67fdef04c441161e33bfb32cc20fd03eadf1f4f336e71438546446ec05b48d53990e7e535c4600206234ba9bb96d5610d05ab0018c68fef7bf22fcc62470a38b8d223f251ff3d9445cbc79507ccae53e84ba125d22474a9782c298f5ed73e42da99c5d33998e3d42146a443f185a0a83c64d459005d2d1a702fbe5df4550ad701b409c53d7f1382435c78a658f5d9a3ccb2170aa13da506d9af4b4c1c72ce5f7c6d02a5c043aa6746b2ac8658b81a12dfe6b1a49eae6772ebcf2a22064ee83882151f6a948d76c5836a27c64ab76a27d42b863213b6eb51bc6ab38bf6ca370bd4ecc82bc9edfe1b0ef70cc7cb778f8d3676f60fcc0ff60346fe4e541dbc5756f814b691215ac49204cbc3869b728887ba54754dc38371bddb90cba9c5e632cc2620103753ed7ac1788cac51a1c5cc55483001fb026ad868b6f3104ccb61bc95095453584b85c061abc97402451c16464df3d663af556b335b5f77d453431a236278eccbea0a49c0b9f64608001d4b501157ab43033bb90945484fa18d78abaeba36200b398da070fc1406a78709c30275e84d1deb33a3a104adc73200cbd694385a6497108a35641bc4b2d8055e497ca2dfd1e3778c9b70e8ae246507e8c62298042b7b03ce86940f618231763a589d149c1dff05fd96ce21da2116256b8f33e2de0dd6a49fc265d6a9d207499016ca6bd07683331685405d3077fc2e6c3420015f94fe3260f8b1b67ece083fd4de11b07b882f4d2946b7d994a53b8ecd2ba91333e106808c2f4fe5c88c59db8c223e6876c6d1878221e49ca6e2ba04d8b67549811b7620f4fa2fff5e5c852c1992feb88a3abb891af35988042bfdd86c6c17b3d18082252ccd8b0a2624aa1888081c5a09fea17f896e8caf2e13dd11300ac23b0312e845fd20b292811d2458e9e56b528b1c2f4917f41d86253d7158c6aca3a2bc4b5de3d5c9c80386a507195761ff0c636a01d7ffb38ab3ca69b5a05710fa20007843f11b054cc6c2ff9ff956812ab02f76945326cdfd470f978360998b221ac5e2736a7cf45e6dba774f20543e131dd1317df1d49de047b952122f12ba567dece9b705d1de1c6746ea92d18dc303c42c92358b2abe44429424a997a2e80f1e719de2f033da8ffc6aac96d2fa81a8ec5f78b2bf34d746cb3d727a656dd124c28bac8b1ba7124889efd562cf00d9a608a8d0ab04ec46fe83196d0ace26f554d86ef45726da394f099c0c8563bfc0c37519e6c79d25dfe578c4e35369ac71031e6e881b0c8eac4a8a2ff2b279e75700642f6e7c0963731495a002f540f469688d36e96ba58b70ef0ef4be43a28bb2de62c6669e59bd2acf02177d8cd9bf48b2e7ff33a92d6dc57a935a356d652281a26efc372e517b742d2deb07d55ff5d65a6c14124d092dd063b4bbc934bba4727d205232a1793d5aa1686ed48dcb45da0d31b28ed5d6266167f71f18de2bad2f443f4cdc9431ca4c30802e8b29afd80c6061d99ef2855b26b23ade968ff2c1538476273cd34adf5a98922f78b8bcb6f74cc9139903b13206fe566a3bc25420df18b0653ddee6a124b2e3bda30c2e210d522c14fd6d07f996345358edaef4e3b50845d57109151e118d99e275fa0fafb1f04d7ca243606ba1373a9912756d3edb205a236d426565a2ac2c1bea64c9e412685a1090b503f58b1dd946d54daaac76ec74c9c9b871e58e2044a04f75e97663d64aeb714d5365b563a74b4ec68d2b77043694ea21f25d8ec8320daa2d75aa403319ea12752356dc10968db531df33372847fe6d2a5cb12995abab2f699aac3d246e20a8fab1908547827e9c2e992204a607c6c307c375599ba18323bffa26ecf067b5267a414eda7c7fd7ce51e1e9e3a6959ecf112a47612776712f866309115a90600cd3cfeb581bcefd08372870c69939d1e49dfcf5f35647023247b226fa292d7c95be19903c72c23e90ee3cbd5917fdf0c6e03249cbfba3b09330252965ec034698ccf88e906665bf64480d8b0953069d16cb8581431579cf898db6ff1568c5da5e3cdb31636db757d1322f4627dd309135a88ed1d2eacc7a5047c51ca8578b9b50c3836e25448b8166af80506270c4217690c2cfbcecc025bd8f5d93d3862af0b43d0f3602e41b4905e6a187a6ebcfc3984943ac788a65d4a9605ba8e765a8693f2865499ad009616774607ffe31a86152cb77b4393c221754111fda029d097252bccf982c99533964a7c939c1e3c4d30afcf96efbb1c477fe4017c5c77fe7018f391bc5bebb484f29bc1873d869bfa1f143ea0a5a93783f1a63582781de66ae557b1a242687505cd582c7cf4696f0850782a44954f142e863747d0b5139c26cec8a342e6e63ac49cb52550515eba54ae65a228d89afec983ef4805d280b75a9be0a2a9aa47fef8558defb907e6700b799a2c7a5d9c3e044ea35df5464f0759160b2a79f47be0486647cf4fa6739c50894a998b2de6cc0c740a92c0d6fb63190cfb37b1642bfa38a82bb5eecc8c12a8e71809734b09bd173f6ae05e5dd66eee0e7914317769c095e1913c78f4d94b2e0372da27220088d92d6f12b2fcf561dda22d366b608a9168ba32d16f0370a494c3156a29b0ea0fa2dd6966ede432164b78b65a51412859caf0165c2f2f474fac3baec774033d67f5b8b103d05906934507930f8080b7ace89258e47118ad684a5406ab0315154f38b8f634f609fafcce7a4b6d304415b02539d51c25001b59bdacd439a116cf8a4ff3e28e036d6ed25f1f5f4ccf3b6f3976d670df5b9ab3b99a7a837e1d950a0a79701d871fcdf4960849bed9363ff9d009726703d18d22ad705086ee5503b85cf5ed469ae6ca5be275ea1a381bf7a193e853631732abfa0f049bae3b1d06303427cc7ee0599b2070ad5ed66c8a5b0c46c7d560f7e6b2291a4f39f832be131ea599ed8c34195f129658be1e5ab6fe75d201f522d91a73fd590dfc4718bb503fe50e2e322c219bfdc72b4a21223e9a233241d582a126968ee1125bb938868866f1af422f80214e882c48d3cb019cbe515264a4848c8bd2a5d5e46e207d72e1ff083d4ece33f6ac887e28d8be79fbdb06035c95b826c200034c191ae248e94f36b1f5253e9ae9433040d570818df4602d461a3186813fc6058ba4c8f3401b394dd1eb84e3bcee1b090f790e5c913c92e8f9b45d18a3224e6f6989330466c9a6bc58aa739cdc19e57b09c4a4a09a360e8e34f1c77671b71a382a1ff726e5303cc2865952d8851724a0da20bdef5c874e26d32740533775951fe42bb36efa0872e52d659548871d67f834af8451c6aa5fb1c6b791c702d036490dffb12a85b91fb5e14f5c237cbdeb0ef62a8c0ef9f469bc45450e2ab5a70f328b1b3afd694a824ea87ee9bfc3066747ca5a228536b8c3245c90c167d12815dd2d1a8b2bfb9d8d5e1f3ed606693d2affeebc37f770900227b8cb32ebcffbc9145db05200592c194ba40171b220f335b8765708e5e2c93389de47f836ad23642273bd405bf715fa37ba27d3a5708423ebce103c53d3fa2a1ccb89c8695b701a1ad096e6abcfeadf989fd964a2bbf4657aeafcf32084474057af9cb18a2f769e07cde2aca037985d9a202024128921917d9f7278b861699cad0a8e3d24df620c35c0885ac11b9b4900f745ab2057f592b24389869288bdd4ed72219579c51ac8f792fd97a9ee7f4ac3d3e06cc67eafa14744704b975236583673b43406fc39fd77b47e2cb5bda79b0fa3056de9c5c448b7fdc79f3d1f3cca7eca9e7e9c62f045a02f68550d3d861b703878bad0cacb528d9d82fb111dbd1960dfbc45b0dcf6401460aa55301356963d95270b2c8fc25b4c5be8162ed08c003f44a6434801b421cc4060e8d7276656dc670cef7347e6f3c5ac498cda2735a850275c8cec97a5a854cc94a1b765ef3bcf55f0cfa9d5bd4a921f86cf56210bde967de01362df7d7f4472e0635e243069e4ed5f447186ebc9ef98df37c081875f262d00ecb1389904988fee9810521195d25434f5cbf9e43df8eb04abe3a2869517eadae40141ff391e0af0826b04499cc6549c5f30ec45862baddc4559a9408b45b349567b503fddb48a1e8fe40f88e721b6718538ec0b4393084bcb895eff59fc8387e1c4baebc0e70204db0b2fc3b6280b5612f0626d554ec39cf036a10059583bd82768db71a8c4a93861e6c89e4a897af24790ee0434bcccd9c566b45dcb53eed95e6bea707d19b10655a7419440c368a8f96dbb0e702cd39a0ad63b70f01de51caf1a8ff11b324eaee9a9f4412b85c2bf24ba430f501ac93a0b923e4c2c2e73a141d23aa0e346ae4e4d040c35a0faadaa9b0ae79cf914e1a6e6abc5ab7fcb54a8fe55276a555ede50232d0b12d942d926e8acaa82990f5f961718772430878723cb6e5028e607d5fbace6684b5da4aa386520f56f4a54d05e035c988742e4dda8928490bbf78bfa50078b277e02d78ce01dff0cb6ba860b09471d776a7f18fa6f80469a76e30f336a3eff424354bfe04a3d5406ae952298d69885a0a224f4a9c0d80c289c748aab83259c23dd1e8df7de10c168011691d60fb16e2f16da096a2c2899f064b70fa732bce340589c53ce83e23fc7c109988b28e34f44346b6879d44ae196504ba27e41ff6440ce1939f4860e8aa9fd79c65f2665e22cbf4100c2bd3932fc1d8a2093a4019312027535fafbdeabb4c3749ac60a596389dcd9d960175fe130289ad89389c845bff40b85031177087da19bc01081b1a370543167a2b86776ea11772d16b6dcd51ec240588507eaee5db18ff1b72b88b232a764498dbbeffa33466002b49fe1448d564d0fa16023e5c43da3fe8f6adb57cd3d74c682922a886ce749ff59d1c2725a3dcaea658f140ab7aaf124ba52b2b5dac93af12a3312a5e4055b2d9ea6a5c2b3617ea5111bd7521624d66c3ddc56896a20092666dce2baeb0a3511e1e19013b275dea7bdf38f7f380a5808776e16a258146977fc880145dbc3f25e830bf292d80d4636b80a97a98d5efee894061e83053dbfc68c23556f56c7a28988af566a7abee9ce0ce5f59c3e05666fa9fa0856f5b0d90f9a005d295b4b307e840b05aff764481f2562a8056408944aae1ced81a2a608245188f100690ae231d9594481c4c1fd69c5d3631af392017af1b717dadc6997910b8516dac8813f9c1adbc68d8ebdf33de8f235091732f8e5bc857d87c37977d55c0e06888e4d2db52fa06960405b1790251a43f31339cddb084b1d3a8fd6f9db384132694a1d595386a756f0032c4666b9ad32715709215aaa390f927e6d2feee77d3c3a5c599dde4aa6ed4984fa0b6fce7ac74c01766717ba15ff2da874f4e9211e8520ab3cb758d0214b3039eb7f27d9f00439086e14878882c2049cc67106e04006e136208020165329c1378a5be4b53a0494ea254c5920407af25811d6ca08c852eba0979dd81a89c49fbdf508bef806df9bb01697a3a54147b530e6495b18f4c420c06d11658c740c931c71be018fe81731b446c30af6a86b2ebab25cc5b1b720ce910eefc6bb97585c60b80215c4ecf1b42237161563ff5f3e085e53c9b158a87e69d47a867193aea926a8cc41f35443412c74aaaadff00aceb09231e591676c8d8f2538d340b3b43d547832ed0e94a46bfbeaa125bea63ba3fc4a52c13def9abc50cc3f930f612c2737292633a9a11421dc04f23ce5cb721a4f1917c0ae80214ebc054c4d82c2868ae3d85b7b2884c238431ab7735d00f2aa0718cfcf83ae94b2555ef0d61eab89eabb8bac91b11ad349430101bf088f854810291ae27897b7067823e39fc439efa9a29d64ae9122e1d8d5db5a8a373903de01ec59f5a41cb2fc7e42037dac1ce3a14edd29cbad51e14c3f3fac2d98af37968dbec0dd89e267843920234a192a6f34350a271db09f54b6eceed6a46a6f6af24c02e29dff0740666a948ca8a81255308efb487e346a890506cb7cc342a2747e28178efc6147079b84cb763ca9636591e82197ac34b84f41b9f22446b4cad725a61f8141979696da6726cc7d4b5586ff68b75bd00500a3a076779c13e04f204d3991e4a38a3170a07cf3aad80cb276f7c1c4ab1c075c353c60fd803559324e8d7cf6d4dc05a574a71191b2437950813303c11315559a36eccd431518f3b8990540109047e4234fa08aa78c2339b2b430f05b16af818b1b7d290b92b6a6bbd984c8fa3fccdef4272e2476c12bcf2b75d7c6712ff6ea12b143f5f7342272107a827da768c5e0e56d13b1dd0d0b28b049421018683203fe07991d1f3a7f6f1d727fc17a34aeeee4bc02a276c6be9c243381a59d3472552e8e4a37794825f63ef2895341e1b21c8ca382750fc4e66f1ca2b40649a99d1b85a4c3c8a82dace15f0a2d3882b5258f99cf6fcc4260f19775c935d02d6e5cc5cea4021029b11b63cbc2bb605d3e17f9d13d5497efcdc449d368bdcf007dcfb164537f5a9c99f66a9f2d09266eac6c54e0f82b64c424896add2d32ef98fdf26b7a2bb934b69fd3001b43d0e435aa736dcba86d765719d56cbac09c5759a3d1337c11c9429e461426b218aeeda954bd21097b41e7edb17dc136b901ff5a54a0134dda4f6715b7b52b8cbc2a7fac4f4c433c6681b8535838ccdc92606b609acd3154e45c949d7e3d8ba592a59a12038939409a6a3b2dd868281f50fa28c6c353864d1592398f6375412dcada4c21cde38c0addb6fe7b7019d2189400640c8ff9677e319e99e643bbe7e77aeaa169982ddf41456d62bcca7003697f4cec0dde8a785e44805d4610ca42ae3814314a5965cd553fb21424d5ed06cdfb3a36ff278349d1740ab2d49c7a762d4899cb09af04a5da9afa15d2a1bed42cd22162aae34b1e08a2f124f9ca9fbd4134b7e4c9260e9597f781df921e3904d870c0c1488d77e9f313a7bcba54b19938d1dd275661a1f55a03e65706217aaab838868340cbde41a268da5cfe20172b9c1bcb06453d357b220632f6d44bd3a54e16f3b2036a824feddf80ceaffcf62f710b50f368d9fceb643b2004fc0b7422d7e8d6564ab303ee2d2af0902df0523f39818f5a8b345f1e0d7cc7f42be003c4afc57299cec29d591d38e5c0ccd57fc9b79916da9cf13814df1e8264d3b0a35edd6011a7a90f147894768dbaaba2e1da94a0e0bf7e3f59ae36d0686d3d7f599f1de590b6ec96c3b8ff9d34550e388fa7088757ff684df7a8758f7db2bb1c4654f9c4a7686a2ae7aeb4ab89aaadbcf7ed5763ab784b41028dadbe573e9d57e4917d713a43218ad96cbeecd41e8d424811eeb147bd42001fb7a55203ea1577011535701ff87d2d5dcda9e78c1c9354337b3307c3d74f68f4238e4cb809200166c3db6ba803de3fe64b0a5333c94b79d311d53bf4e5dcf877d12d3668ce81c15cae7fdd01d8708c91329d3497758d35b0f5ce578651f3d19824c8d67bd11a54e6ca31502f7194f0186dd18b38e78856cfbf86efbcfeef847bcd31301241c37124f97cf90435a0c05af30f3145239bfcfe4c7cecf6eb39faa7d303e716cfc5e80452dba5f4824ff2b63272029c5c2d9ec134a67c1220051296273b378d998a8371a5d72c9b858a31725011adae86459d1c11c5e827f1efbd352dfd77fbb6d34cf7993cc59d4ee40090c278ff5b3e987226d838283729496acfba464fe5d28aba4bdec4febe86c4672d86ac9efacd87fbc1654e6fdbaeff61e0a5d74f33ec8d173b06c75dec5ee5ce09c97f8192b659446089a8946d1b57d5a5fac464630576629915e8ed3dcb1152532da4f31d2f6cb2fd644ed10debb6fd809ea210a506f06f7d659f7ec03c248d5d9473bc140b9c4f767e8031268b6246d175469ca96b8c67a1be249abd846c1e3d7d1609231ba1012055de18669e88db1311b359e3f74f7a3cf3bc13bf11edbf6d1400c33283bb42c1c128434b3a2270c4d26ab20fcf0befe7b9686184b4a2b9e9c704af32f1742afd9ec8ca9b6624f8988a64af78c3bb46cf6fa6cd41ba2caff6fb05383d9d264400275ce99c79cd93e641c9846b4b2e670e48dc6baf225c3412784f0339bd940dac66a4db841abb38dcf635dba30f72de6b61a807962a03dab5c4425a897cdc651908248e75bfa5d11508b04590911cb27132ae9cb538e06a9c3979fcaf6fa96ec108ad98a0001412926080581bdc69ebf2d0b530e2484e3bf9616a8a98abfc3348b8147cbfd8f889200fb4d5132993e2f9df7acbb43700130df30b4a0f1e2dd9b6a1811bb22b085f0f7e173ba0cd9b30d513cd23e17d65df6dce706199ebf8411ac7be201b4f2944fceda7177c736dd7e21608236ffa7b1d74b229d1010d2e2f655ee120437e0c2ca4c0142a8209869e8fd8aa21ddcad63023766575d702c94e5609d843688dd07bffc884e263bfe74fa9a14c84c35bf45b24374e85a0168aa37ccf85dae61a3e64496c903d140cfedd1a0672de0c1d85a11f85bdc7c6bc7321030774c47ed708baadb0dcbf90b8d5925a1c480f2b7e4bbd8d2907b6167911b66985d6d7b38cefe77656072ab2556775853937527038453ec07470daf80e338b6857cd326c77f69e447f85b8aa3d5cc637f64965b615ae605f4c2843e162e9cbf73ef337f6c639a5ce1b542e2aae465f14e92e635b8d8d2b344fbc076cfdbb142f489d8da562c1ef7d6ebb03ceeaed07dff826e4a861cf29642fef5f105e48e0bf0f8a19b624c1052b8983a13f315f9e581a679f10fa1dfe952eb3a874d367f2ddd716f000e85913673d23175e24da7710b1ac4e94d1b1f72b426d84061d74b4cd70adf252c54b5c27c2856bed277b931322b439bb0296abc5e80af31017e3dfa8c1175e73fec872bee8de3fa513dbe2e4565b1635ecb232bd5c38804fe0d74f72e9e85c42a50bd54e3ee2f460d12a438b4e12c303abfda8f9ff29f2c207342fa2bb49721d652d71f9fd7e52c0fc25bdf2b9fb50d99a46e68c99845dc404d27cf0e6830d93a7acd9f11eb0689622b8adbb27fcb084f4156c7f4adc55570fb04214550c1b480118f5ec9aaed049f95f8e722a70e1fee0c6a5c8dd21024499539a11a32a870ce35f8e81f7681b035d6647bbb6565818de5943fabfa42e1652738a84e33001d4ed90efe0112d9ec2cd6f02db21c938eac60f81ecc840a974e21305c2b070646c220a439558af0f9217c59c3fe738c45459e694155ed8917b3ed2d7ad578798c39736c7481ccbdf3f8f910782394605e14f4bdd65e0398a462ec5c4587469f088f1683106f32c331635f91946c730d181d752d1f96621a2e83ae1803abb7d3393b2c7a0cc81d65bafaea84ff00540355cfe30c7bbe7cab3d3fa00b65f221b9a6adfaaff49406c679947d323e68649ae2847426953a7501357ffeeeb41b3277c56dda3fff92f6e7875e14d206fe52bab583435d5205b3fcd50c0afc16e0d5b7153ca5ecf6ad4857c54bc8f7b9cfc596ddcefe3df6dd3cc52e5bd4b9a6173fc245e9d23ac685752a18c87928204cfb6d4123aef3ff68c8487526948faf1b3978b67f10714e56c1888fa646991f4256932d9c9ad7daf41e94984266531f5092505c0474d1dc887c4582c39b27fef06674bd3d3abc59f46978f3dc256f86379702b856ebe6f8767bba25bd8762f92b4fe1a9c9950833b2042ac60003ada96917b40b5aed7c985d1bb4eaf77801bc7265ff5fd49b68cd4f07ffce3d231dc57ec1faa340d87d722a00db55bc6428e9fa5b5cbb5a9db391ac4aa5de941ccc994f1b0952f75eb16214d5e888c43a1dd7d6aec764605a1b0163f99ffa11f49be5bfd9fe38887f19ef75079eade0b65a7d447de5d63769d26cb4e24c440f4e9db4b1481c8bc71e75d55e46bfc8fcd1e48819cc01c4de18fc01fbb0bfc19636faf480c9063bfaad5d8cfcdc79bf423ecb41f4c37e6ba0e2941209166ddb1db304eb810a70714d66524fe49bb4ffcd048c3813cb2fc07034ebbe600e464902a1fc1351e3de7a2864b01f8b9c76f8f485267b7bc9ad842a05eb74e6757842c2f71b043936994d6827cdb5830e848995bf11180d28f4000e93bcbd2995d66748003fe8b1a084e85f20e482bc36cebcc24f8877e50fc4c68a2d039aa79c61c6912db31b506bb22142e1e0207add6d202dc91268d6c4532da305959c3d14e2eb6c423f36b468ca85c1e2d92d1079c10564b563da922a511bf0ec0a454d554134ef33091d115436e42856999fb9c28e43c68db38f5f0410c2d912cbd89309a5108915c4ed54304ddacaab05add9835f0535368d1927f452fb6772286945b2458ff1896fddcbeaa5d5c9ae220078cf4436cd69cfb545d236675fe25188d82e44552766c046997ef5f05a22d7d15fd3279fb10c635743a06789205ddef8b2b8a37a257959a4bb17bf2caf9baa3ccfc47926376909b809de9814b5eae3d13565f3cedc05358d648a5198a35ac2c081e53c44d1a7142d4dc154969dea4de9fecbcc67bac245b5753b6ca22dcea18d2568ad8075eec211c4511d8374f21ec121f5d9292a747f127ee456cbffdea020c168fd3bcfca563bfb8f0d409f098982503f3f3074db2c9f41249f8c4159d8f935af1a14f25d6d3f800c13d00f74b3be48e080610fe982067ca5ce914465ab670bdc779517db48320b9b09017f6960536091a8000b94026e4e9155f3e4d588025e2af9dfaa6a2254c560806a356988272fe449e67b7cc2616bd5d10324fe496560589b4d38856c9887d531b8ac14c48e0a53f54e586f1906a10aa764bf694ff585ca5687a2341c9690c2c516f64f84a7db1cbca590ddd7a0694bc634d2ecaa6756a716da873150c591cd33aa8d45780aff78b50e5000dceaa2ace8e9113c7a9b5d72fec63c3c7c2b4605010eb076428a209d612645de3529309210cde6d142c4949252930b216dd83bbc16b7e4640fd079f9858314ab96fbbd3588cdcb8b76799a2466010a85c3d0a4bf6c45e72d8bace70ee9d1a610cb88a1a1b88ae474faa1f70117f1039ad2fbc3420a7b50d0eaa4cd840976e92761f6bfba7f2e60008474492ae124fc359aadee1c31ce27759d55201f83822f87b8c323747e245b498ac47477dfb769a607bc0e46be0ef22c267da154e65465dbc07a37838c0c63a0f024995c633a399edc221b930835c67db20058a24d1679806ceffed8c83898e11dcfef8571c2bd60d3c27a6925056f56b06a805fd933e10ccc71341ba911878d03fe782475483c3cfb926c8fa8cf236a48711cac4a4c873103d7a0c9967a36c469ca44c12f6d0990b7241430256cb1b1b377845653ed8579ee64a00672df88c63220a04181b357795085c391dcef0a4ba75f1c3cf9bb22800459022e12744f4b61e42367e52ce0207ec5d6ca87b35279ef963effa3ed8e436dc2a0f90a690aeeaaa30ee44ff90d230709553ac4b31606cf1198ae21516ba84c460a68e370b892d6abe3d21f27d61454dd232bdc8bf14166e4b558bd1fe76dba97b3afe8cab119e43dfc601b8c03abe9f93d4269483a51e9e206ed0b76fa2a1e118c0b5225475cf05f5c246e5c8e44a1c7a0444f71c536ec1f5deb09fa0bdcbb6f7069a8ffae25a0a98c29972a1935fde518baebe353a670694b03ff6520c8b9103bdf1a781c0d3d1bcbcc79b9820bec64d02adf2ebb559a74229d95e3b995ce1f922086f0025dcb7e92ea9033176246537de8947e8a185340c355f98c10b0a363f076476e471675fd4d3d38e037fe4088101d7a387b0dea526f2979d45d8e0e67d467f52f2ab19cf0792daaa5bb7726de5d63821e6d041841566354b7142301f0869a084f406c8e622b51d6f3499185e87ed61f18eae44506a09d1ec35dd514393c6e163b857acb7119bd0519bd02eb4023ab319c47011e2ebedcb8176538ee48816b13928d65e6de3db676e6ea4359690df6b335b0ee7ac08232b98d3e5aa3cf576fc1b8feda8ad8c831d716b210683459e8049e1a1567cf31baa5aa0cd8e5be4e3c8e4df3dfc0425cf16496dba0b1046237cf7dba49102c4bbaa7cebc295bdd60f5f2784f1897e34dd4e333cf674b1cd55b9d3e313f19ed551a4bcc090d12e28dde0ed9030d7f250863c4fdd49fe3585fd417a7074622f8f24e0b84eb9ec89ea33bff012ee1b640c2fda3dda317b22f09b5e166c4ee2a6109642178bbace073867dd7946eaa6c2fb2730bd8f1e5b0919a17e4f6cb3a7c88d2a2715e0e3854a248378f6d0d01ac3f192936bdacbbfaf86ab91c03874bfdfcda69becc714cc4e8e02d790495b5f6f53a1ebee9e9f81126f4747c616d701f8df17d2a61c95e309f17b100204713d52484e262c2a1512ff8d7fb3a51381ea68695f4a5d1ac34047d2f5348425e6cd18a023fab3d9a9bf429c702674c508bdf1f797933abae37898cdb5f02107054f32d17679fd360c57ae4789432e05ade29db1a63bd6af8086df0f25254d4d2b3260f32db1ea26247a97e5f1a73042c00c2af929791bc980454a6631ad5241e0aec882896aad9f924cbfd1ef63621f8e07cd03726b0c1af08253266d844002d0d71263d6dce5b88cc098fd3c61eb16647d0663334f0ccd925482d63492f22794c2a23d23da0dfdf14676bd98274d538ee87b05ffbf1191bd8b37f1b502ca349d8a2aeab8dbf6c588b7df9ba6ee5147acddc68330c222b50626cf03b73a1936e93f0182ef57cf91ebc7270f84f9b8e4d2781e8c4d880c112ca6b02e51a11ca138a7bd702d268d8e42bd2549cc80965c6aa8e5987c649f91b546ece294cbdb5466b03a9aaa572bb189474a306190c431df962285e0e98d8da1263871c830786d30d66014cb17e5f9c410b9d50e19abc922126df64b44fbfafa9a55fda455f1558b6db06d5cc0a4f3c63f97f6198f30902b9ee72564f79c006c5de941ace858000b00f788212eecf59589e7a9a622717af157b291f9cc7a3efc23840214659403a9d428c69a7ce455fdc277f245ea391e368b51a98cf0d0e268c67d382da81837d3f4799b9cf5be02e1afcae6518983452a28ca34858c0bca67933a5f2fc127d756b0dd2e865c40395ec85c35cba80aee102edb1830a735b6f41ab1662e4dd5b03d6b8e1ecf1b655227a6ffa2b44b1c63ed8383ef149f1f4ef58b58a205761ab6a25763345e45274fee93f33951b28011b1f234af12892c921af9bb93f40fb38b535537df30daea8b3b7954e091fa41fc6cceb2c52501f303eed5c29f5bfa0609e69d24cb859086b662275c825085d7810c5f6624c9ba06c16a7c0a17ff1c0d5059628c43218b098bac392807c4fcb76ab20daa08a9335af518570ca2fdf00876034a2a81648d9b79f9371227345780f3d0ba465bb48f8a50bba1c7a63db8e156f90b37edb0e2c3085fe4825c6925f9a962b085960ee242bd7acfde1751334dc8a877e9f115d2a1ba2483c20c48087e4d20902375ba623b983a1c19b774cf6fd5ffd280624dc37c83f360bcc37d7a4ca1892d348f54a3322088ff67cec29f5f950c00ba7c794fd43d12f73d00ac38f306ba750d32577f74b960a41383d45f89675a684fbefe9a7ecad387410d76ae9e6f17bc4427c0bf862e94b5f91ec76412e74d280ff3c022b0da797868c00d08e8f069339b8351314570100670445b979a0dfbd92657e76c618b1f3baa8dcfed9608c74d903333c3b0bc8bbee996db50e9f2a22c389eb0dcfa0814b3f3babf40ec3e7a088a3d043c5ee3f5ff721386730445ebe4747c9e88286f5ff9a5e941ef2bb000a455501bba19fe735d3373e5cb970688021b84ca746ebcce4267e727542e6492ea764a4284e7c21c7a1154bb547c86dd58ea873f891a34e51b56f0a992a3ccbfa5acb3b5ad7f2df9ff6b9f2e5fee62e01b39f4d47e4ad73d2bdba1223def75bb9155e5cf0354a0e7d0cb3d242a37ba1c640b73ffdb620ec95696ee7cfc99928d8994fa2f8017175fa09e8c57334a7b631e1ea3f026eb457d25d922780279d07da0be597f891c9c7a9175c0a0c4965f314f0f5586f0199b1afb8606006afbd5e5af973bd7eb4b6492691599fb3a0ebe090c2c84f1195cab5964af7df13339fc558fe03f42ac0b7c2ac65c6b3775df838b68fadf41d757b71c82a0d0afb85792ed6e5e8d4e352a1b849f8a0579686af14fda476fb246501bd15c21bbed9b561c9d847ead42f18136b21f02b11fa4e01c9bbd4238ba9c88747804dde265a458929511fad9bdaa870a2006ee737240d5ecf08dcb4f53952922bd888417130643c03a3fc8aba17ff6eb7397cf9e4763a1de7139032a602f4f6deefd5a8a1c9ef1cab93d05a634589094389c930d341508a2df02a1db0aff298abf2348e53a2f7376598d45d3b52e6ce5085eef21429813533eb3a68f6c2800ad4b638d7f2fb4ed259149c038486a2616e1b905bb83347e266bc0a2bcdaeb465c793c7eb0174c8fa4db3cba4b203be3b88005504852ebb484e6de72339344af3d0e1341968711619ede4a55965a6b63ed59727b89ee34f36c8127a354f989465a4ef33e87ca5b7f849f6f95b746714cae28c5909610f1fe50df887899b06f45ae15278bce8ebb53d75ae4d567037313e69a18c5ef0c6ac5d5ebecb1a6ca9d644e00cd3d205f4071975c5a75ae7a7d49c82e4330432755165003f6940f1dc4769e652e3cc763619b8a9f9d0af08971ac4c584d1732ac535147f6de3b01b967380e92a4affb5f407d83ea32c3d9b9e66d1280f8142e24d857bc43fe8e4b9cc1bc237a6007fbc1f0ff48ee95fbddf748922bc4b4a0f392aa5d4dfe2f8893bd217c53412e6bd24cc8c1ace79a917de9024e0031abf428a5a3092b24a0774aa617e1b91213d28cad17924336a9311c1c8f70c4cd22b47045e65a70271c2fc5018567e3cf33e0a1359721d8e18c40e21ee26ff9bec0143b92dc90e8421a6848edc3e61396109a31149c8de9b6cb9a59429c9148c08a008af084c3f67337589ae281491c4a113605c3bf6a82ef59092be016a4d6fa2eea2b9ade91d95dc9882b25e4bcd39e7a494d22b9ed8620aad92447be89342af100ca5d3439756a644b1215ee194d4e82cba28827ae8bcdd2e855e374a81b01ef3a8972551e14b2f7a607c7bdfea04479ebdcf5f286a0a0d87e3d0d6b52dc6b8716fdf1e3711cb444cc4444c54d459ac4d34b22ca1be3979ad84d18ca68b384ab75ab5d54ae823968acd168227605e2b350ebb637aed851785aa0fb59dd25a572bf72eb10b121212121212121c1a1a1a824370e8058ac95dd241b34a57984fa983d695fbea88d2125397e88a42bba3c8a86f805ad1fba86f622b7a2345ef24f85b0f9822d215ad9d72da6058270fb2e009ede9ec8e01b62b5567ab7876cc6bb95dd41bd47d8c190d2d88fbd05f1fd237dcd2b1424dcf4cf711c1a8d117872a65b42a7d7a78027b3901d81dd073c0e6985eb39b764aa5ba55dfa0bc9c9e99ce51bad5aaad56d46379437a26077b8210fa98736beb0f756ed947c761cccb9a583507816d515f6e85cce722d5302a3caadf22e5b00d4324509604dacaac572bd579b08db3faa676ad7089e8312d9df64d0532e753e6ececfdb0671ea5dec2afa3e199e845f8d40a2ba0e4f21d63b72ec0c036fc51032fc00053bf158e24eac74222959676c7fce8d1736227a516801f3eb2784a164df49a15fc57c24724d80db01f54e85a503bfb76a99042af672043131831e39c724a0ca379950119c25a94a3c500bbfb06b6a0d1aaeb315dc33af82922ea0b30ec8e7e66eec17390455a496196115245c25e68d0a194d57ab54a67b9cc8470357d6b75e9ccf2a0333b4b764da8c354f697ebf9951a3d5a6550d78bd056662194e242d1d22fa7600f50760a7432e5173af4ef080b255ec085949bee197a43b18d3dc3321fad4cf1b5e92397cbc5a4b52ef3317e4e021bba7ba4d71e6afb07a4e6d7b1f5d9c3b311e83bdeb00e97611434ce3aa2aed333aad05626c69652ae1797392889da0d8181895dddec4526aa13d589fac9a1117e374775a27e957d865416c3c84008218410d2fcc0f13d274f0c5599c5109ea8d413d14909b3b8a8ae0f3df453d1d2facad9a7878359bca2592d51bf55d7309329455a8ef881094ac83798a87901480ec56116e7308b1d859cf839363407a762511286ebb5e40c2e3980f1c40e929ef8c28b126aad15935336520b38be554a3f072e925c192209d31e59b9a24675b435be37bbbd11ee921cea56a9189d4589d1103a94a194444329a535ac231d2924e950410eb0c856b522e4f40cd4302c070a27451f24012df5686372a2f43828e434a5b57609355cd41e9428c038e2c86b890d922061586c39651c8228d265aa5108b6608d186de286450b0b96388210c26ed8b1bb1bb61af5b56489ffbc9508829bb449bfbfb467588859ccd083aee9b1cf393d28c42c57d763467ec18520b6f9ea11261a4589553958909cc0c113b85f4f1151774708bb237e4bf15d05cce2834e40230db00de63ab816443ae6da9c3306b9942089b0c796fae6c237f00625fb0a22e9af1c01614592464a22a5b919326a52eff3d7c00b4098d59967ddaa67da574a56b0e0218986524a4b1822e8bf158d34903146116d8509841076c38eddddb08647b6654b0e1a7466b11015732618382d7b6173a4c888dbbccd14faf00d8cb3c3a0f217d92d0ccc82f17c501eb7791f0c3a791f3cc233eb7d9087c7a1625ea54c9106bd2dd5f614729885024e5ff1a012a6ef9ced733aac153684b23d1dcce21520dc97106b3c166dbbbbe1228959770f45c87ea134fa287b23db8baa905238f17032d3dde66c67d6bb2211881e2e8c59b01f54762d08faaeb3b74460548c1b514da4f743a4d5481b9822c491286ce8a0c3962d4cf0f36791b92589dc32c40c33892d425b74e0867e055a284184b444121bb43a8b1a900749d404c114cde092c39c73ce2b341d084929a534a32933830b217964860d6c88dee74068d1b890dbef0b082b804092e96e1684524a092504646bd2cd81a4e3285a901f1be407ffc2ddeef677b71b6e4435891fbd1f22adcdb102285c826c32df48600dea1791963d22f54d44ea5eadbe2378d41ecb30ea4721c7c2a92e12520b3cb3a27e1387a8f7d160de2723bd2b98237b38bced994f8693b61ab4202c4f090bf14acfb01bf50c47a266610594e3f0c6c54820e51c508bf37e384f79949340ca37ef67f394b73d138d863e02b0a5f97210802d9487d3659a9fbadd4ec9278fe252cf78675d1ca2b4d638d44b431d87881e63a3c2a3e83046473a74e94524a4e7d8dd64c298dba2ad0c16f33717e558587d7029fae79f45fd5652469de93cbd18fa40bfaa328c2ae3510ba1adccd6a1aae599238b46cb94a62c96e7d81007d7f0002619f06ebe2d84f04a951e236c69da7215a08e793f9d9400e659cf347b43c8ab427d22c070a890c28e5d05199ba119bb5b639d42075ac0947124d65585a9358629ca88c10896ca20628cd6c4a20335280d44a4aae042a36c43b767ba7d841be64f87dccef4393dc3a1f244afd3a52784031fd0cb989d08c33718d6c1f08c0f5374ffe86ffeee583fc6ecc0077ab9fa9f908be8f9f4b7f3c48e75071fb962d6c666231a659b15c518323364ee41a33793621b158a49323be73226312c4297589c5146e8b1dcbf1a1a14d8e6a391bfaf2563fc5753dd945276ea7d98d3cca8faefebb8f0d16747a17fbcc144fd366905d6f151e8fc5a1205550243e9a76bad9d2dd97559da1c7369ddd604d6c87c0db3a6438fd533d0bf392464844509299d4ca69377c9e943faa6f74ac7999302df680e63c4a4c4628c724e27a61fed8efd9945ad624af1733ea91273780406314b7a10df4c293c337d1a316b722ca8df1c9a43f387e99c6b9318e529d7686bcc8bc39aca07755f4c907ce69b6b736b52b96df3894fe752cdf17074f0c83cd37ca3b4c7467dcb9cba77facd53dd4fca69cf44c845605b5587caa28e79f4181618e50c5de52a4f869443df56749cf6a8c7a731d4dbd60e15f3a47f7ba4c2f4b6134b1c147d14c197bac4a4a48e51aa9215499c642e05201f2e3a4e6f2aa8df1ed9601293580ce6c529b72d6a2fd34f34a0abbc88355ee5a8203d30df5cd350beb17ff89722c62802cb6fce1971e46f3867db723ff6e8531eaa5339aac985fee80e75fd867f40601ee5eb2ef48bb7035de51aa773c355de87f21b5eca672a3a876aa36ade8ea3a13af828f8d3ceff867f34854239e57aa0b0d5633818d703dbc738dbbc1f9bd747a57cf86ff804f2aa1b9c639c0a2f1ccad7551d0fca0302f398d7d40e74cc6f7428c7e9da018189c13c1f9b7373ce39274cc7c3ce79733b9b73ae719b083bd4e93cec37fc637ff1f6f5e9f4a55b1df61baace3f3553a99437d72385eafc51aef2e9afa15428ca36289408421585e23a76981d76ce511d8dd1e15ce59cab5c53752fde07f42fa8eeeb71c355dd77c3f3b19b23e5bb3950aed2fca5abaeeaf653dd324b55bd189d946f8ef20de5619b77ea3e208e751fcc32a5e0a6820d4a7737d35537abd9611af3a8d6a575ebf6511e6cb350eab76a1c4ed374ca2da600f4537a5b022e5cb468b9e28a2953a0405161f1c597be2f1630794a1bae8cec403bd4a40acbe9acb3c7b33ebfe9995c20cb799ffbf60ef7dbbcc8b3be0f6117a3037d73e8db0ff8a76c6ad1e3598f1fabb0db06e4633f65de0764fb9353c7e9930e3fa45eedb8cdfb36f6930aeb1bcab554ac5eddb66e761f109f5307647b76ca33d4e35957817d3d9eeeeb3e94c3ad8b9d4d744edb3c7a86ea806c1d8fe6a76da3bf798ae3bc75aea3b183fe387dea787a7e3eba07c4e7f903ea6920fd9cf631d1d3615f5f6757a24d51a5d1d123cfb42f26e56504959fd37b19e104f2320a07e519f6866c682bf34188d337d25b01097895b6eaac1bc6183d86defeceb4677b845391a8dd903992400b809300b460d78db48c623395e2ee6ed6cc047e678486ec3025a594187876d811024101e109664c1b8b53ca19b1ee1333330b76fb535aeb6ab5cc98195ab61bb91ba646b9cd28753e8c52e5c328353e8c527be3d2a87046a9fcf0895f9f5166a4ddd8ee94bb72eb9e3015e6868e2865948d6d5aefeeeeeec6651d6cb3bb3bc2aa68a8d0a1ffd03ea6b1313bdab7c34fc90032cd53d215252424e8332fbb09fca9e3ec6308b87bd961e09bd3f1294fb1e3635b7a0bb6a5b7605b7acb9678dab2269c1a42084f9a7642631d6e292456274206030486ca19b19326671ca2719133f622edc629e1102aa7f78da0a4952841ead5aad69dde1e4fd33739bd330e8561534eef9336b5cfbab7300540076d653e86dc5102203275795e9a734ecc0df0944f425ec77bda5de03bc6e8410a02cc4dd093e945684b6b180db8a0f44277fd116be14aec50c0492187870e1d0dbb22abd6131f1898b48ed704e9530498469999e132c6365162372808d94bb6b134f793b6c1ddb1be9ab6a5321d7bc339c6751b06e429cc538c528a514a378ed6ac0e9508d9d6ad9ad49ef1018661189665aea15c877abc39a46319e51a36d531a4593dc419ebc26ddd42cc211c629f1e1f0d351c3aea1bb844eb6a846f043e499f06f8ee5878adeb9d91be71da83e075e863de777781e7381d87cba84c85ec3f229f79fcf54bc828b6a1190a84d4465320681995d13316c10ced1413816d01b54ede4f4d1023106afd10010105d3dbbb1d73acbd186f72f288b4ab7572a00722025f2045124aad939fbc2751fa6c5dd3df9e7e355dcf0fc3e6a4d8571c5615864184a46da82f6df01635c5032cb5febe9a70c1d4a4063c54eef7d5e4a8891111486d68abd85287c45831c6183fd6a7432b82e346d96677777777b7fb84a451b6a1bd4420106488146ba8500d6e11c5a9391be615223cbb74df5ae8411714fc8046941ca48cb1640c09a3d4cf1fa29054978950b003ac48d9d2860a5c261012030a5356bfa8d091787ea6b436f9b0c51736e80088334ed0ea3090c0a1bb9bbe20a91b01941c81e46a4167f54d74b95a4c943cd33d0dd1496de4c43a7619814b23dd81127d31317243dd811011af1520fd573d7221b79da82fbf2f14f0f09ffb58c1d16be8f5f1f7c5840c160c71611e6481195d7469615e4c04b685438abaad05249921a9b52dcc3b729432c68ea74f8e9159465e8c7da02d9118205a4f6276244f8c0ef4e97ae8c5fc80de936a6ac23ce9fbd1fb814994e0a0a84564a3c20852eb09bbfbfcece86f5f4d47e33ad2a503fd5ce35590f91ef81fd85e7a9fcf03d97e42a008590dc802d886d041156933f32085183df801102e2e39a061e386088a31c6486536332c8b3176370c1c6ed472066aa5a466a9e663091638609283273518b18325519c8ad4954dc418230a445dc808b2d274448c565cfa88c56da93290186162ec668a54aa00be00e30545d460a305556cd0410fe4532206e8f0248729a21c9d40098c2a3d6c8143105d6cb0450d72b0044bed618c16dc50832c3cc021888a1863b442250908299608b1c40f5a518b0da2c7d50ab6be15b3b8ae927e68a992c49325429441462b7ef1d15d004b50c9c33653c48551bf5a5732090b005b2ac30831daa872021c96064b5440250f9c095054a1a2c90a2a6db0e1c5921c52d3638c3156017cec5254d44690133182a0a18b25357ca41f638cb18ccaec2b3e025612769e9c208b264aa8a00a1fb4a2d2c72dd9d2487730f30eed7a28a5acd2576c03f372065cda28030823485f44d1925cbc94524a295df22e0e37925869a66559b6b03765f43055f4d0a51add9b72f270775344cc82f1068d62bdbb1bb320f30cb4edfe7c626c19658cdc2ea8f1e1f3f6e030ebb1bb796477c75fff81a7b1eaeef689dfc415c3026c49872de9096d2de931edf0637452df9efa2622e86623c2ea28df343f39855107ce94d6ba5a19618d04000fc6bbe1bd781f5579d5fb683c94d7488dc494791f126ca49ee2b1ea436d26368227b0d74c1c83dd01bd3683099ef0e27a5e622116da1c049fde4eac4d4cc1b2a64f10bc665af9900894ae6a0d61b532dabc78b444ebca63580801040fc1b7154227030d21f8ca7f565e0c0b20f8ca579e0c2b07c1b7f51382afbccaad10021008dd9339271324824444908808120de9d2a54b972e5d90b627b6483da4f5d0a987568ecd0fa1ebd1d03efafad67cc67372b83e75a853084f1ae47c6429f8dac933d7bec6e1ac8b272199c34e08fc2c8bdd28e8f5f4c7f8e8870e3ddf1cebf06997793cb4876e56dbbd0c6ab4fba8c3d34f6c864b03fd73005013bf4b037fbed93315baa7bf7550dbb93dc743eca737c5e5fa03fc34426b551795473a09ffc9fd231bd81d416abec93ad082e1d077bde90515c9d19a2e74a305817c4d0e56089a2a2eea078fe4e0a3ffacd79ef1c10f30080206454ad0355734620443d0b064895650911c2de842375aeb4145b885b910038418d0fa792201e83f2bf8cf139c9e8912980ebd1ff76a66f68c1ba46842861645747183d6b616a8072a4b2449e2c495176c69d5b8cbd58247d886048844086d77c7579372787a192311f9c5474f7e138b8e791f98de371d48bb80e9a5f781a50289f7fd7cf4ee960a12fcf49a508dc87fd05b3a10a01f7e7e600141471f582a44f0717a1f582d2fbd2422261060e589e84961f2387e5f528a7c939f7522a9a6a6a61f5a591317085bb8a0861db85c2dd8d34d4ddfc425001d56c08392203e30ac966fe2428095275c4216107434c4b5542069ad7f2278f9ed2b064cbc0ffc2634ac9656b6decf3a910f2411228b19d670b95ad473057dbc1ebb76e658408015279a585075b105116dd4d0d4d45ac7e16626b48e11428c4a59a59c3ca50ebf629684dc945976747b8db7b3a6b767d3e96cef5508bb838d704c4ccc019bb19374732c93a1adaf4c94bb037e09cc92def435fd519516f5e452328bba741dbbc3370775e95a577986ced010be6f563c435dfa6a9542d81dd4fbf715650dd8f4fb8a42e571308bc6603198b7af18d4f0fb8ac10cbfea899fc9cdb1bedc941ecf1e2121bbe89b23fa2f71c5b777e47a8604d13083eb7bbe10fc808da69f1e03d04f2f06e934888619be07367d43be77a8f70df9f6e83ea6e3740f6c7ac8e3f5fc3c141ae61ae47a68beb974da71bebbc3c7e9bfcd355fce07751efad1df57942e2f391f1d3bd7513f75f0352dcb9ceba0f765ecdab6fd38bd6bdd8683bab9e6271d9a2db7c39eb9dc60a7207a8d8c46a224bd741c7cc32dc983e50cda2e2fc30d683b551bb3605c283ac6184f6c457330cc7960519c3ce616e09bd8c2f60a8603760467f018ff4a8ac73019ac8bf62565e93ffabda8a0f20d24c2000bd778201a3132b2695fd51c6d01e044dd881e3660512030e44f6b677a761a1e46de3728f45c962071e58c2476288118ad0541d5aa57fd58cf3a584643cf4ec3a3b20ddf00bdedf76524f4d15bb9b4555951792b80b62a1a54f60de583f6fb8201122130183222c60d58ca38d2da4d83de4c99dba9f2e93f611a9665de97f9c933ac2eb320f72316f993633efd996346bcb53cc3c2f1d0fba637840df0911bb753153977f83edd322f8368574551a1ea553ffa301052c2e8d971ac3a551415fa7e0cdab2a6ed06d401d897124457bc92284ac5448750464f077a7b4fbc92500205e9f7c5098c1e0860ec61d42864c0c90f4db7e559af87884371f2fb8272e40ff0fb4242e95b9b33667a3ee04f9f1e5c9998024d6554dbbff628f6c7f65aaa699493bddd210f0e3ba1adeaa84291f2ebebd0a35a4aa5446d67ef21f25103da7277ecd81d7b3b24da2b1244df0ed38dc67f25380f7be465d6c61bf47dbdc08917997d80db74e1bba220ba94f1d7ab3dac9cb5757167a0f730e6a0053d816e027bf5a2fa9682faf9c4df15a633909af71acc8f68427d769f3f4b6f639650ddb7404ed1d1b3a740bdeb55fadd8db0a6678ba8305bb7db65ed42f71468968ae9fa582c9a12a447169b50c45b352cf69a086553ad82d6d414113d7b094199535f19a8847645bc0589d80476ae8d4ad40d95b00d1326cc824ca093d75e503fa8843d9554bf1a244d34cc7938cc8a465a370e01a27e955b52bf652a42b2316d4a2449211798569080962526289552a9700ca6a54ea24195439b23074b24423da3833b499ee192429b245dcc4a5aa4a567cf4941090f91899138894d60a74a4c51a1d720d5442566b1d7c4259c236a4a98d2ad2e3ab16822d30c2e5c69e188a367b700dd7068d923081de63ca9df5e6961db6582a3d905613e2b8cfbc4bab588b7566c023b8c41fdea3b0a490daa954de0f582c8f789dfb07a9f4ffcac04ab0a441cae71d61144cf4e53c2dc66dced086bc2751c718445c32308eb6ad80436817d865159417a481cd0e9f41ca408a30b1c39599a60ae35d763195b0c426f023359abf9adb085fcc1158e0a23d178b5600c1db22847070f2a911d070e29b3bb553231ab4b0a881f2a0bc2d40cea278b92aa2cd291c434059cf43113ec2ba934ea57c3f4ec3525c8246ac4aaa80546cf364026506098a8ae60188989d80812b11154c2529e8f20938e8b986806f563a2e7222276c22c864f8a8a889c1071511b1509f30f4c98c8c90fea47e936a920ad742879761e39292c96e9b1db6716e621b942920a3d8af342c7cf35cc4a6185169a2687fab158d3a361b842ad51bffa304c720bd7ab0541cffe13e42dea4866c3ee863de59c73cee96d11baa8a81f4d45513f9a861654c45bf19b496fc4261cb109ec5a52fd5849ca89da37d1a1111321b1cdc74a58c918cfce4ca2919448859d3c1b3d339192cdc1117ad1281a454f2a39f550bf6af4ec156626c519d4ad3032dbaafa50bf45cab951bf4a030db67808b338fac41f66b1c3e033e427002ba55a9f4c797618187c60108e67df18638c314a28e184134e082374c9d2fba0eb8759437ca0d40f2a2969e11b67a71b354283ad95493551e1100f7d50e808dd70401e506c547676fd0787bc5f49a4fc3a5b81a343c7131e9e9dc74e89be322c19d826b0a3aa54766e2d3a287d12f4ecdb0a070dcc93f622de92f11444f462dab7b5714c3b76838f257950d99738597a76af4e7e7836628de7163719ffc9b467516a71d097d8a608e97df86388df039bdec52c2d278b96d6e0c65c987e3f478b2e6cb33388e28adfcfc9a2856dda7b0662ba6cad250ed6c719a7cc50d841470db31a43f2c3365ff588f930d6b1bc65e6ec7a523f7db5dabc97d4d8421a6314d8867a7bce0a19cd4e5e4c4665c7ca82a8fbb125c336b1318c7760535353d357f7619f3fbbc6ed1f909a9f1e338fe0a5898d9362f41eae547b77d460139bd8cb17f5f3c7b04a23837985594d39d5f081032a2cb9c22c960ab38a31c62cde4594527a1370ce1e2f58d46ff57385c365586c237dcec520c4e04ca2588c27c7783067ff302f46078b1e0cb328a594524a29cdb21a25b428a5d71bd4cf5d86153b1c48754ef9c5164cde005dac2c6591f270b5f0a28e10970b16b4baba808ccc92f14afdea4b293bcaa0bd5851bfd41603d07eecc31ffcf0258b0a7d13d01bbda2b24399a680b632cbbd64a9317a94892a6471f232bf2f2c4753623a9f8cccda972a2a92166b542318a3d949731b5f969481eec2d6ad706840eb60366c57839d84b6329f4402a930513948f476168d37bb4ae09b9337b71337f827efc3d1dc4a8b351aa34d527e4a79ec7a88b81eeb242abd91fc4f1e9d4f5eccc9a5b73177f228758eeb388f5d2761596a2eae23434c8aa4205da13406dbb00c621bee829e6c5ec45b4f62e476a647eff35e730a9f71f0c3f796713b3dc5fb60bc4662d6675c9552bf3e624e8783fcd83f39aae3e7b41dde91e1b5cd7bf322b733bd9ee27d42e07f90761007479e1de31e001ba98acdd18d24172e941215fb0a66c910bd17283ac906896df691d0245e827809e285e9b5059417265e98883ea4008508ca10528e1841e4c3f6cbd0b2e6943d30b833a317d9891d7c384a1fce52b3038fc8232e8f57094d59f290a86655695c464ba2464182e8618d3da12c56c41c3616a374ea75e84f5696bc942ec3f23e2035ee2ea3050eff794d0ab495d96ee6c7e3776f686bfd8505172c8ebc1c8931768c5e833e3acccb8a093ebee0305f5998f1a7df57164b49af2b5dae1c791941dd5716575e5908bdb2487a5de1a1ca1afc92c38b119fee7e318205dbd699b933aa30fdd8871042086143f6cdc155945e55b4782aa83c57c1e2d779febeaa24fd0aa631c60beb3c82d7e1bfed049a2a8a7d5181e5c515b39e03807e5d0627ebdb7a3282162309ff4920f9c52fdb2f5e6adb51975dec64bb80a9453dd7528124fa9791f01a5cae02243c5bd1a9a2a0d17ae0331443bfb974ee628039fb99c7033d0be243bf679b5e73e67c6c7ecad9d73c10acbc3d7ddda2117d8342e837cf5cd36496ed449dcc37cd61a779d66ddedc0e75083ff632d7ba8f1ffa38d0845c06dce807f8e8f3f544d24fc75668e1688d3662a4e6a77419960b2f3c71441ec941cb182fa7b376977ae76c758593c2cb09a397122688cfc6a900c363ceeb3e53866d7a605310769fb98381c79c62ce8b79bb34ebf9c8e6697e1073eea887751f3bd04f9e0ff3f56fea605ecc0e3be6eccbdcb56b90dbd957385e4757fcece4cef01ff57d2961543598e9d43f199a9e6778eadab28b7a5887d3738687dc948bc6e326e59cafc3acd69dfa02fd0948d3b418ea99734d70f9e85c1a411f9d07a9694030ebf9e9d75c8bd9c15c460c85ed60288cba969d661773f27c4cf9e82787f291477ae66d59733e4ea7d3c9e391fe458f0148059ee9d1bfe99b97394e673fbff9ecbed85c643701b01594f222b085d35bd7b1a777328f87f3e81ce73d39d7bc18cd31ce8bd1bc1deab33b39c64329b64331bade375ddb3c269b5d4ce6f9907bc4e4a36b3c27d7a66f9bc69c0f4d47d3bc3df35621fa740d3e7c88aad171e2ad8726a4f4186a18756162e80c8e1a77d297797631d2e163be53159d846fc77c3be9bc73e74e0cfa4eb8581e76ec586746853e436f326ed2650f550f2a744d723e16cbc4582858f31cbdca2c1220d0847d036d556cd4a86aa3f211bfaf170a9e83f87d41d1e559bf2f2888747c6a60da0f042578fd3a54b551fb89dfd7145e3c9cf2fb9222c9d3fcbe905e90fb71aa81d2f7b8f04c49be8708ea3f1f2fb65805f19f0f51109a17fff9e4e085fc2f862c9238400c92fe8bc128e997ba5cdc36fd3af7a0f5c097d2cac307bc6cb0e41076313bd2ca479756fe8b01e8a3cb9e6d6a6a924b0fab1c7df7dd6bbc7137c2cb4e5a79c8fd100200e8f9f0b377c7c31e5d934d1fa303bdfd07f4f67cb4a742cbfbf04bef631702bf1de8bbefae7bf6d1be0d260fbd6f874dc4a9da52f7b5040f4f7f609e39edbecc794e4e5dfae6ed1aaa8b41a162744e3fbd9ed40b61ae8b4139fc1475d6892ed76b1ebf5f73ee9899fb8172ae4bf9d6a17c76bd715bb73e78b96dddfee0b98e5fc3e244f5c0a64f391bcfad841c51313b0b45ff072465b1fcecb89b9eea38b93cecb409eacbfd804e29655f1f103ab30e3b750dd569dd9661c66bbda1bae5a239aa8b017b21fc1c08025f7339c36bae4d4e6546d57c39550faae63e168b6c082343380780d27c8a8b86477535af6527678df3dd1c19e7dbf4fe936fa72d3bb9d605d9386efa76f2ec4473f2325fa7ddc2a992deb7b7621673109301c76d1bc7711cc7ad0cc786a69d4e9aa6699a764696519a650e3b55962ccb32a7bb23ab9007da3233c6cc9159d8e6c01c76aa2caa2c357ab63b7ab0633b96b45555a91fc552b37b7677cf6d73604ea90a4bdd97942e2f1d9b8e3111da8f7d196dbb2df22b66ddf0bb3952dbe64839772a2c15e59c6faeb90a4b3d79a6c292f26d77b0a79cdb1dfb29e754175a4a07730cebcc9da977e6d467379d9910f8edededf9538f873a96c1ac697753c7e99e1fa79c3165ca0ed9b78b69878ef3bb341fe610c37a603488cf06830b4f9dee4e71b9a8d3eef382f42c93cbf53e2f7ff26e9fe55480e1b3203d0d78de1ced3e93667307039ff9083d0d78b839a8b74bd38ec3d2fb0372c80e7da38751d1a46bb2a7ca31d7e00fb6bbeb58cfcfc39e7ee8a9a514767b8cd19e7eea09a90f24ba66f88fbd075b6386a61742bf8c277ad7ed63f67a8208c13e06a0875e8f9dceb307aef1311e752784277fd3bdafe7e723c47e9e3a77cccc3d3f0e6300fa756ef8ededf5fcbc0998a58136349516955910ee4b0a1408a167a06ab1dbee80bfed8e20f0886b6efbd4ee08025ddfd300577505a338a8240e1655392a15cd88000000005314002020140a878402a158341e52855dfb14800b8b9e44745698c9b32489611442c618430801040000008088cc8cc64100e84a32aaa98103b553366244dfb51e7ef1e67970766edebd30313ed43fa9fe8b7e96998759ea9a1e1db82bfcce61e5e2f3ace0e05753781e71a774045d99db22068412b4d29664c394c0b2b5e7517e6518d747f3754dc2a584585e9504056abeb17a5df4da963e364691bdfaeab9198d3b3e0c1c0d3b93b5adb5d1b3205b73a3cdd9cec23e62f940ae9676ab424b2007e4b172428cca0a1937516ba765fab19fe9592d29f4cadf8d92d3b537ab6a7d5af520e9e00b6f2a2c55eb9f997ed72573008295eb7ef22a41472e39421883223f6ba4ac792438706c494433e46882b6ef28e128d483246639f88921febab60ce586f97d7b2e8bf880937511bca7de728ef913928f4a958319395600dddb9e0cc92b0b43785671a0d550ffbdc6cfcdce48dbf4c0550c227f2914ea6d74fb29fe3f0890e43b67bafc0a416e8d71ae27b6a6faa2ca77e4f9b1efd60842daa0e51c3bb10a159ddd24e8221d3041473d7d6f3aa604b771d5e9c6bc35b6e56113da961989c2ccfcd98d3ac3091896b10f1d7b05575cef3728520511ea01f4662a266ee47457251b3b434aa426396315f22046cc17645081e3afc761d088a9be63109f9008cb9168923d536797e71882310d942149f0b9a6f421645effd689dc968086652f4160a984207d4de502dc81abdd3042edaa2c1b2902f1e45717c950e445e3d006834fb8933e597b0a6d75697308f104a9e1a2658900943cf16f5897476dbb6fcd5c630e413e6181fb0996559f6a7bc6d402c81d2c623862d8e85d45a470f155b6acacee4dd0925aa0dd2d3db9a0be34cd12a73826bf2e1bbd8ad166a4fe5bdbc00cd33089db341a6cda6c939946525186064e94497473be053353b8dc4294a19e417a765191bcc689ba2be9d7f95f8b7292e5fd7fe5023348e988a49998389c33a98a184408b6a3c98409a45496f62f8c0bdb5072e177ef84d896c97dc44784e852b38ec4578cca56b3a2c7527b269dbcdc1883120858ae2254d6a3abad43ee3bf707502da508ac18db6e9b00f1b4e63c10a6912c41123d4f67b586ef4d5cda1807a0117c5c7feca5c3de4bcc93569d1386908bb34d96b855003ae82000943f708ea7c177ba371df99787c28af8760b0cc9f06e15a49ed9471fb965e4e3298c0bb2b43a75e1d1d280f96314bbd31b1c2c6b90a47372aba592d01f65795768a02f6b45bd2feced2d24bc10c38b8e02a1cb2c6010d5aa2149c31a3bd2f4f9843cc0a15580d2d7669638fd059a2d051c6347a146200865f69a0ba0c0035128216380cd8447868b31e0874cd2189343fd53eb8d32b2d008f01ba632693c7f04d912343f884771d12ba76b0556ddb77ac2a2868d9fa4d5fb03788625a1ba18c7a078684635dee4163f2bd737d5dda06990c6017b48edb139481b738f7cec05322a39681c8a6316c9407517e42cd8650227a96fa286c9ff7421708ff9c2974fefda63121ad12052bb1db4e0be07b76907c6846ccb2c729b984c68e6c4e1a53d0a77a66252c923710c571d80941ae39f57c7367846385b7b878122b280ddf7939899b38ee848fd4d9154206ca6b684a593736a512c5aee8ccbf7a44e7212a82b1071dcf2c6ddfff100b572ea6bc21b241b31c49b2742b582badde494a8b09495c5fff73ef2308ea8a15224167f060d84a07dcf4e8f67c73a61c4d83395665d4df8c4ee7f6f71a6a267c9537a2625850de4ff5e827c6a525f7609d04a053dd60c3211165b13e9f7193beae530a0f0ea6f3a5c8550072ac7a822406740bc95db8b9548c2cfdbff6718d6b67913ecb364217363653e3e89d33dd450152b601f3c6d74e8a29f2bc88f6f786e7da9c6aa9b5ffc0f84d6957fd814bb1008cb2504c2c7b5696a43e1877d4b0097c9811b7c1b0bfe968808ac401105ba40ddd6cd183de2dcae957a62887cf71b391a4506c82a47b8ecc0a76b92d413697f6dfaf1cfb1ee4044442a202d58ec0b2f8b257d9cec387dec74c4791c405832f009346b6edd7a74a2b66eb3cda418f90b1fd55925213691922316fe489253262cf0093122dcf5fb916a68c8cfbdaec98a5dfe74ab9a2277b4e38cb55ad255c2894554aa8e2ecdafb67cb93b6f206862fa03fa4f6d9c9ccdf3ed999377d750a863b022532807c7b65afc82065efbee77e037a13fecb1170dfe989a1151a9d38292ec03d67656ccee3686785a920826930c98d4e642f4cadea3371fdd1b538213f89fbdbcc94d56dddf615d6cef72b651697a50bac3c52b9e2af585c776c849f54a77106bd244b8039bc42f2a49eb0faf7b4199637c4209005e86040010134217616e2e29a6dd5cbda4545cb0284df183656028552e9450f7502a80fc421eb870a1e96a2ec1fe9d449b369a2358a56069042e7736d5c9da03151d18125a33602bfe148fef1f5d593deb9e81655f2229431844b07b358889ad7b14e63d63e1c846767a3e529cdf264830a0bb623a993196fe3098053a2bee5d59c13646080bebac285ac8b0e329ea3613beeba6d7152be31a8ec8404fd4cd64cbb4437c496e7afc6a95f5e963295dadf1bf510e65a8e59e8a7a00b748613a93a71210a7d4c3066925f86945e20b74e5191f5f281b531c7a1504526a7446fe501fa1efd17aab89c02d6fc62152b7a84eba91f61b8ee9447e20fa06382c6f41d08b8df9776c2283a13348171f01283db2cbf1856779dc118ac34b3d190c735010aded84e96475ac4c420e0d6571ab0abec9ce733c37f48d9e1e95e25521043dc00aa18d838c23234645b18f2c28740bc92016ca47476be466a62a4328d70baa00866469ffbb60761108724944e4859091479d938088c8f8ab8dcbe9d4aa7797553be93a367c51c1f6ddf8ca2a6c9a8cfb98f1e813329b7b405c95b1aa45d2ba1e9b4e97c041582c48c73b16eadce47d422758a830095082ae124b410866d4fa9f66ccc747dbfc3e78682afb561c59845faf18c9cdf2f7d94961557a26e8afbb5b5eaef7681943d2483afa19cc60ddb74992c9076612dd3ad686a8f68ef1619df56f8adcde239ff9df512c993df6ae86ac3cdabdeb07cfeb5c23d773d198255ff24a219883cdb7b422d6fc94e866d61bbf3f5fe2ea1fb38e71ac8b058266b42f998c6dca58a8a74ccdcfc84030cca99e961a6b9d72d0ec30746c9ed96acde0cd121a318892e33cb914475b533b045578808a53b3c4d0f5590d8f324d08a6184a8b5601cce30515e66a6690fd7d6d3c3c5de4a10d35fd1e3ca4d0809426c120e65468315b31a29dc148e56535049e5b29862d8b0cff1fe6f16f459ac145e684bef3c1210b2c786f7592e0a5a753b8294a8d2748683973131f527a63514f205cf3e254c169d76f7351e1b6b8c03238317c817c6935296bee7dce0881d8a86a0cd478c5554dab5d83c1bc78c56ede16b2b737f4b99195193e8e5717f85cb54327423c811db07da6d84304d65442f323136f1a8e1d2d448c369c6d9a3fd0c40d655e9c15699e1728bb96d48378dc112749627744a18a0992f9bcabf14ad6891adbe148f201e97fb7ba1fc3e25218ae64632d71e2307de24c66638216afe765b8a3118e9391884241500a565544b2b8d8454c60244c825ecf052bca8b85b4541ec6e27b2382a3c1b15989f7e10d0dab9ccd881085099ee6f1f548f8f3c87de6a8e7496d88666195fb920692679add3760d9cad5c64817bb1e5d61e9a953e750d8336b5d3980357bf8e4f02f257568111528402515fc0dcb46cacf9f26509d3de374b97a28e1e6db1c6c69664ecf7572c68e95ea292d0824be983c48c7971ce4be7414f5a3a589df8f2f19eccb2a8e2bbd4e52a1f1d43905ef0280a060ee56e8037c3417d375a08c1a6491203ce78e0e1172a003167cc97de4ce5f13069a7f70b33be724dc6c4c10c70e08c26d2050e8e6a696f6d28714b47e921bc0df085cd807f233274e9913b7f221d7f3150b934d37597024832a1bb160b2fd6e1f3e26220e3986aa0596f1cb9d849e27d954d999e8631d61fed118620256191378c9d58e538ab6331ffac70c27d2df1f0d2cb5e9f3bc36a5b04479052ccd7f71bf159959efc3bc805edf14ac8a3545d86cc5ce5443e8cf3d68203fce9885ccc1cef0043d771d9be3233b14332bc88198d0c43d3678eee2aa5b1ffb0ca02087d2d3b8fdd831e6265b19dac1ebe28f540ffb22309ed91187b8abb95613aee45e30aa91bea11ea6174ac0e4c307dc020f5fa14e110eec0b1e915f2e10714e172451cf3d24887db21fcb3af9669099f23bb9cb2ea589fc391006ef0c0cb6f5648230f3728144da018d02c7de52da8c0b7872c7629a1427cd4f25cbd33377bc6f3bc6caafe91072e2d584293390cbc15685aa5e8476414589dbead67dfd34807ff43d7ca2d71a9cf3eba3188672bf1e31abf5490ec231dfae22c3749d0f023620f490c90d33a0818f2f96253797ee62c2e33c0efc9c8862030c608b4ad9f3934a792b150b4f79caa09257f88613fb9b85eb78149a33e016316975fd6dba1f3254df7853568bbad1c016ac599db8ed7648ed7558bdbcccec1d7171deaba578c201a5c28e10dcbe09c8bea096c63bffee0d7c491c9ee0e3aa32144632ceae91e7cc099bacb5813f11df6ab97032afea1b9ec030d04c60e4c69155c781e1c957c68d510740ec9131f1fc4346ef8ac4bef0799bfec202e5b3abed3b0bf96557334268e658a0e06a69864d6143a378477217a80a3d54b0d0dc952c773563670ad87d41bed7cc74cbec9d6a015693818433844eb0f900d4abe7b0ce38573c3a3eee5270ada3365600aa2515de6b8123efb3c49fdd0f91af6bcd87a40f4b03002d59e937d93fe0bb84cc1faadb137ade0adb1832046a29b57f4ecf50c3308e2520786d3e45c987dd3f961077051dc23e0cf179bd1ad604a811c30fc01ebc6f1a77f0e05423971c1fccf82fe74eb7c6359a3c14e046cb87ab394b354bca8db2de64d382ea0acd64078633d98e3a0d78358100626711a554750423f42c63f0f2c8192ee1d4522f8428eb95ec4b2456f3ec7c501747cfa4fd8ecc61266763ba3854721475f47e9817cc19a9fdafa4fe7298a03d484b25ac17f0b558bada259f23d8e2ca8bf34057bf5d7d332a8dd33059475950ea0b7e0760e8ff4febd9dee487ea8cbb0171c26fe1c3b37668984bba9a890508feb2828cabe370c2bc6d0c5bbe4578c62fade498118cac7a91c8367bf7cdc65fd714267e19c6c62d3e10062c8c254f8cf42bb729519e943fc894e3d499ff7a373f134dd4f88f314d10a1cb3d371aca34ec894ee984b1d7705f7a61b57b480b7ca0bb4bda2bc8035b62fccd755fcb9eafbcaafef262ce4b504ae7c43c119ee1e24f4c1b84c2a0853624134d503f4b4cefd4f0a0e716401aab50f4c9e05381c848718b1fcccc516ee684216cbfc293653d9eb17ce053e633f55e81f94d79ec4f6608607cdfd6f46d2a3b28a7325f2e4255a5cdf11c77eddc6e94abc9988aa45e89fae0de9325f013398e6eb74fd60207ae14ac96d2264b40f2269100e52a13f87cf91ebd19ca0fb5dbf7123f09b004292dbf182a563e2bf6d9eb11087008f787db8fd35cb9b60d63cc98d5c31982a7e4b4c1cded99eba0fde05a05094ba99320f3e9ffb7a03950882dd82820f7438bbbb079bdb1d149e0bc284f8374df8f56fed35c02cfeccde9925447cf1c5a88744f708531e4a90a72bce7f1c0f7c06e4eeece09008b7049a4daab46e25f50aa84fe007aafa75019ef2e4a289ac5a42ff6120ce96dc365c2987ae85447a2118da7ce7dcaa2e00f6e244c7b0ab7b12f17649d674dc4244b45ef930e0381dde337d2932e02a21a90dfe03a890be684bdd50dd8f435f48320c0b990a5861364b3ff218e16443bfb523fb6120d3278dc4ad9175815103efcf4839b0b323972b0b7f82a2a0304d3a7ffad6c37f04aaa7241c112fd0fa9da03e95b050aa266ba9512854dc015c0cc53f876ba7dbd005ad5ab04350c20ce57a673516ec8107ee3d86ec1fe58d812f933007e6d37a52cc0ad225ecb0d88d3cc1bdef4e16b5a70dffc9da5626b9aee728ec35f6dc5c3d684ae5a685207757e23f11f9a643847fd6cc9851293bfb5b0ea4d2472caf7e1ba65fdd301f67faa9487f99cfc18ec00e5a6ca6bcd7a6749166c5d086f848c32572644338ee0b2f8aec371b7c2ee2f538f04364c02e5b8e16b35e4b9bc77a8dbd68a0c02b35822c41734cf6c8c93c53dac2aad54b760c7bf63c6a83f5a7fd4b6ba04cb1567b88571d19ba4de08615286cd4402685f06234248bc8ad0d48001b3a7e19af70a6c5e3cd33b1b97d1062c1e1299a0033253959f2dea34aabec1eeb10c706afc05913e79ccbdf62d5215f5bd564ec010d97cf989c24993e2a97ddc66a3a50158c88d58f76c1359050244e91b7d08ba77487a40ea2fcc5e0f0cfcb29543bb95924352a059ad5b02a407d4be8b204d1273798d057b09f753df10951e2199678d2d659e36dc733c6084dcf8158bf1e71fc2b18fe86138021ada15a0a65ad37e2c57570b9f08a80b1bcbe58e373553bee94fff44a8af9f7ad860e2a825c847f7530fb7b2e7ec2bdf642b0596263ff0e52da967070759583ace26e6e5a7c36a182ee356fe7e25f0ba2ca90731224f3f5762118cad2fad8a56f8a459fd3a71dffb419082a188ef0821594294025493fc44c9088d77f8be9e0e931f69ddea1e87a5e3c2435e4304e754338f37ba87fb6af4bc4c346f5c5ce26e798fcf3c1b023e8e52be11c8aaa18e74d1b79aa215feb33559251fadcb910559fd10875f7151f8875dea4ed6a3fd723b07f0e9abea6411e61482f8317000b0ae5fbb78e2fc8cff9c0dd56c00ec3b087d70537cd6d502ec003531bdb17c7f28e5b71584edc2be6183bc3aa08d936999412aa2f755114d2e7fc6a698c6e59e9f150bedf24bdc4126f80078491cda1727e399331cf5a2412fdb5fd970e8ccc3a2c309c959f1115613cfd0d0a5aff00ffff33d5d18e720e98a68b597602d24b2feb4a71c78869795a524a3f65def70b6c2708fa571a0220c2177ac564564543267f37af202e7a10a98283686e44e05264a12648f937afb3e60794c8dcec37e299d6008af65465aac504ee68ada743b91da955c6c00a42b03e88ceeafd548a754d95b5977f0d47c1bc14cca4940936594767b75325eb285b48e8b2062d028ea2f1ceb70595a6896264f9c29a6bd758534ee631e6fbd658e4f3f2aaa0b7a99dc546cfbca58e952d1a46f1385d8807ea04a4ef8690aaec5a2e2dc3b42395f193106c0b03c7760e352a9ba85cd0ff902f963859d41814b44718e70d52c15233c492cda5f19ca8eab51f309625d31c07c27479692f05b46f897bfa5bc2275e3ca464ee2b704f979e97066c08f495ca7205f7d7245f0594098c67fec512d20a2e12f08ea4e5766871e8bb9015399fdc7292ea4910daf9aa5b7fed99441bcdee9365c52cd395a0801a8cefae81c67603cb7dbf25b08723d2309c6f1ff50803f169208324d4c2dc9fab1df4481df9882d979368e71a02e7d591322bc905b7288b2b1fca2e70b42cc1351fcbe131b7c6e566759af42309c74051cbd2097d3cb9de5a25ad580114791a01ae480e7c75365fde43b7ffb5a7d230a0fe6f30b966bc327bee9a0d7a55c94ee7e54d6c5928385ee9787f910ddfbc1cf08e8ad54b6dac3a58408ed923b542a338015b46f4e54808a82ea127ccc1a883a8d209d8ddc544d19d366277ae249d20eac6499fffa589168e6edf7e2310f482676ef9d5e41e4702a25b7c0086f60b5e627bd5a9149f57ddc9f475254ad408f67db0cbe8634fd080a57fc23ef055bba50db972792546e638b971a04246a3315c3774e2d8880b0e5a3e32a66786f64b567ee2e7fd3b1d008c5b89844460c468153b8fc56ab53048191a6aab8e30c162b28123274d2ed89e9a853a9164403e6265330db701a3a6e92f0060babee7aee8a4ec0683ccdeb0bdba9c4749a042d435de84ef4d71d3194c647c64c72cc9fef2949165c087721fc5367e89f07fefe38deec6b7194c3abacf793619b344ee92238c4a1bafebd188fb3acdc9df1d9642f24791fc10cbbb7246ff4fd28c46b586a1203e07ae9ad747967ec455baa0fd4e2dcd561ea76a1128f3ff575380a9aa1e6f757feb58b8e0fcd387d00f0b3c3b56beb1473f6737414c27c2cf6b8fb326119a5db381d0b730ec570bf2a514fd2fa3588236391166452236da5fa54a244773ef56a040ecb32431831b29e8327fb274126c72a97e9616f216305a507e26557f42fe1b8457961495b8100f45d40775779015ead4bc36512eef7b42e81528405378f30906bdd85a6c311569e6f32a96f6384c9ef297f1881cec15fe27f66d348bec919a0302944fd78af6295fe58f12129bae11891230a4afe01e7d5b75be621a2a00915394877b65b8f8934b2f49b8d7d05c1f4a693f8a53a1e8f6cfea45c81c796dd1ad6acfc4a8179a8505a31dafbdd8d4d9690f4494610af3e69bb3ef765cb9542cd290ae0229d0bdce7f703dc546c2661a3c9218d7040c6a159b6523eefb0a80b8fc16075b4325f84bfd8ce41219c5602849c700b722a865d3ee4b87f2d784ba43819d856dd1b3e41309ab0adb574453ec8fbfc7afe471b75b1324f600c1f54929fe4248d3ff25eb7894aa8542f09fdbf6d56a46bfa905bac5c3102f6a7575a31dd98d9f027a1c12f02e26ce76a6e0ae9341d4572231e3444e5b84e74b95b43af4657ffbdb6da43134980b309044220257c18f61b13497f5c107a07b56f275c46d618226259bb5d50d03154b44cea8d14f8979b0d33e512d4390d4414169bfdcdc75751006e6bec5ff3dd7818b3e47c8593117df83cbb73d9deb970280f4bd19940a417550cb631d17e3b8298a401165e64dfdc418300b8861e4c504712ef6ef24fa7ebb3b3513771f226eb4bf82bd371addf1be8e8094f94e1e93532242c9badca9b9b497cdba93a0581a3268a22318fc912731e765a64171de6b2be6acba8f9b0dc431089f6e18f9f0421098ba64febd2400a40add11728dcf003f3a7cc0013b942cb58ba10521294324762d2659b8247a33d680c261db398ba2fb8caa3828c2c59eedff94a7059679ff8ae28906cce642c5754c028bea041166f202d36090d59c42a16be6ba72439434a78c8384c4a5fa94da144843baefe4f9993830d737084a373463c4513285c92cde0039ce26389443a77fdb59755893bd6266bf2e0b928c0f697703bd1f47a2969469725612169313fdc081025418720201eb72a5ed4291bd9586ced4f950844b8087646019f6774c5d97862069e65243cf37f5476550fbd6cd263fdebc4e268d9b0673a42c5d97f96ac910e19871c44d725049e465bdb78b0092aa4a010c4bd99a1731f00f0108c4c5168c4a7913809828bda95c5c62956a368782e4166fe75a0cfbf18ae63268cb1f9012ef3f62b345af47a064df6545f917202b597b1b53f5c9a7ef8b330dc6fe6aadc8c4937566ea6b7fc7aaa86b106bc175f7ece8764fc0032193943f8ad9c840b0c9419526a80261688c1dd7a1b93f39b7b250f9c3810c27a964c9cd081e3bed16089359bec1a5583c5f2f61c9b026cd599ed8021cd2730320da8e97db4a75f61641bd3fce7bfad13fdc0a1e7175a6761397660440a6dfda4dee21fdd67593585f6e86918a9dd555ddc98069b6758549eae24311493375931892d07e8ab05f429cbd7b2c5ea4051af20b32fcd6642805b5cc9b318431b9d447c7af32b2c131f442a3b69affe3d3a651a7449af8ede6b0e60616d7eb2c31913d9cee7f22192149360ac77c0bbc8c6abb0ee96ef8cbaaac06befd9c57daf5061ee04111a78ab1d36d525046a3805100e51ab1429528a6b95ade04929b4219cd1d5d51e5d5c6a6e025444730c55c6664ca24ea08139eb667942b506c4e6cdb6003c2bd9779443a69bf1d6a5dde0b3e16cf6d8594859ec1930ee1aea5df535f38d4c6fca0029008e5212bb2d5f0e893c3a6511cd91172b2ea1faa07858e96a0f3bcb167e7fc3bc7b2f4b89944b2818c674a3e8eac176039cea1d0239ea4af7b9578844149df8c66898539e46bb12d98aefce63c39301e31a3ccc985e346d4baaa5d06553801266156bf45f2c5bbc970286a0c7df453611a359eaeac1926abd1f8c416ec08c63a4402b06dd5150ef5006d3634c912b7841ea92e0d6eeea03a0f4da5cc7c2a504ab8bb43ad942b7f6d147dba47165418a2353859073d6a2de9ca997f34edfae92358b644abe1d3775a0cdf47495c64aa113be9f6872ebc9211da3170282123441228a17519e6cd878a6f082dc0216fe42183287d6291e1f95ca6ec34a694f5693d5ab2d8b5549c6106c153f5736407ff5c64c39eb6f74ec5558f67b692b75893ccd6c5d25376d55e34095a7e166a75428f85338668acaf6fd73d85850d3a696e832f6da94436470efee2c3cb7ac14b97dc0abca12eb6e2182a889120ebcb03e1a24645125af601a4f7f50621a2a5fae08b02c4a008f1f82a4d8ec95fc23afc0e0b16c2f789094f0bd34ff960c0c722007dcfcc3ec7204e47ab2a689d620e3a06801ec993a8e5a8ab80d66e9abaf50dbc119d760915310ff7261c6112b3723e02d4b8e19fef229f03543ef057841f13cc46e50dc4ccf0bfdeb6bc9066593e59da9b0e532d7381d32946df200945c96686ad113cb92a4c5a3ba0e2a67792599e2e9e54e83e4d57210a8508dbdd007f874b2a4064310b5d38b3e625c93fdf20b56f7d6b514338439f6a59396f499af4c0384b41a19858812b9cdae03b744afc485b33d690998d14e6767a299c8619be31ee8139413d716a17887c5b5a217888b201aa4fdb9148eaed5f1f9949c2fb45185eca89e54efaff1035f038134390ed5e7a1f50ad2aa4beb21f8882b25f8be735c29fe73c3196e223ea684138c86b724f685827c3f3dd1b6e836a7481bee9b8038ce3d80099042f8a2254743678d2cb1d604728c372dd8beb5e44a4282b98cf44475d83dd04a9652036ac0da6656c8ab069eb74f9c9ba24ffd4773df6efd00bee7a29307700f0a4e8e257c75b095a5d823e3a33cad64c64fdf2fc6b4988cd6746f5c7d31b24ffdca4aa1d303e33a24236c345d50cda72aee7764fbf0ef8582f662e3e301c64269051b7388f8c5332637062a2bce4e06a1bb3bc911458bbb30961c279b4710264d7cdbc8a5deecc58f0311d6d67a3c7857253ebf245eacb75223ed3bd90bfef05bcb15f468afec1181476093f084b9f1cf057bdbd704bf5904807a89df121de9b14f6d568dfb822caff3df1b041f92f58c78b9c10242181a1c72eba9a2f3900c36ff8c078af84b81fcc331cc1238b4ed6253c6fc0b591ddb3d5f70b0ce02450521d1ddb0a5695a8bd33074a19e5b7585a16622fd600016514e12c200705d720149d30eb73f06a0fa9cfbf9433bfcc784bda04574a3e1af65006ccf82236141f70841e7df2d56a7ffc3bf410e986c5ca01bd31dce9cd823d6bbb5e12001b7938c7b9d6debd2bc4d11a1949e68c13e1ae37a71680529f13f642eeec084b0b4149b58a661aba9b9843a97715fd17575ad744f522b6bacb2d726437f0e2785fd8b2db11e7f2ed3383e61a0f2de68c6c56eea5d66151f14f8481972ce2c0b4f65550e49160251726075516d8916ac51016d1995009db07b00243f35daccbfc9a37dc38b4cc0bb970f5fadb10a09d4ef1200fabb60eb5bed97ef1c8a3b994941e36bdbc8e51799385935fa804efb7c35474ec7292f76268fc314f7e997e63c612ad9575d16fd02050228fd2598ee48c4cc46bcf85d867da29f84b9fde48b4e71329addf9292e76ea87a6cef0ef4cf9fa31cbfb80f06b838caaa84e959fa215b8261d71b5ee516b34fd38cc183d1af50501d66397afd1c353895ae89f92b64886d539658616e5f2516f00a3352dadc23244c3b36f5eb4197a98c2a5d4ff9b1adfc89b3f1cd7c64ae2747f81b3c5efc19d275af7b04da3d9176254d16fdfde1643349b91062134b56fff24b88fd9d21a1837edb9380d767f02481414e4245ae317f96cd5ffd08fbdd5bebc3442eada0057c6e6c5389fd20cf1fd11a029737e7778f18fe82671831eaed2fb65791ce69215dcbd788c72bb5cb78d9b8cc2530eba8bd7e2f49da57c000de463e61a0368a8cbe87f5718e23b0bb010d89884e400cef8232b751c2b90d4532d77a1c744fba931393ff73d04eca99cb6b217a8bdc3c7bd5a9733ae2762ef9237ddf8caba16a22ed23c9f207b8158511865a9b38b6b7400f1b87cd05fc7066c120a0c9ef247a10e107fe70ff830ff6539a8c007f7a4d4fc5c31e12395f3583db79cac676f2674791efb38559ae5233b1cf3cfe99ddd78306de9b312a02087636fd079543120c342429099eb1f734318f04bbbd449dcfbb2501021e95375f9214c33d59ba9f62b4a545b9d2d2392872a4c17d3322e27d8054c743cbb1e835742e1fd6f5536ce1304a184e449032e1419717d60764fb921572e62391c94530d722e5d9c4e763677b91cac152455b7b9ff24356befaee274fe5fc077d848f8ce1a98ee9e001805cf15dfbe92ea7118d4d33fa1a830d4595970d4214505e3eaab723ec2ce7a9c9947219465aa8065df2322ca0df2c8055a2dd536accd589b56a80fd61e7ff17dd46017656e61ee49fe3b5a5b9f9492205084a8f3f9b9897768dbdbf91a5a7a0c53f5fd5b29407bf0d3b9da7af0deeaee6d35a8eee6227871513f59610529d2c798965d90e334c7dac8edfab2381ec37ca0f490901c0a3e4eb8cc42803e4ddc3e7c6e5d211a73011fbb0068a9c6eb4ccafd3a8888bfa2c797c2a51cd776d2e7f464a8da628b054b92021782f451dfd20b4e0b3b6db05e5c46dde74cb30abd80c94bb430a4a2b3fd9343314c95291af9da77fe8b58db83b5acb169929ea414623902009286e15a7375e6db9824de69c3015e181e6a8759c0c448475727431cd8300f7f07ec730d7691284e94fda3524e6049fe91c0b8ba72bffd389ce0f48d4945b189cbd37fe656c75b9aaba25bce3bbdd7c3d3a84cf7ad036bc2b303f1b3bc1b38bd63ce892124a38bb06c72d971548c077262ccf36f500e39edfe21c348ed669f9dda4095d26a22f8dd36ba2f9f17dca958c52dcfb3bf6cb50caec643bcaab7b6ba5768c811179a2fea9f8bba1a982ed97b816e711ead67d728741b35b8bcce3b97713321167a93befb8b78f2c7be5c466337ed17b5d2a5c43d3fe5e451e3caad5f608a4f5c39d428cbd6ec4774dbc33f04391ec40002453eeb360ccbbd5127a89f215576606e363545e0051b1424f0672542837745edc4fcfdeaaac37f9ee275f255ba838a5aeedf377f153563fe7a4c79f14bead33b3b3239819cf0484896d47a5e1706d9eb6e707922ad38f59fc655ab506e5652b347e5db224a1f98b43ff7b150ded829616135862a9fdcf705665cc5f9105a090963d9a107700c807c42fb4980927b8386838e4465f10ad6cef943c4ad1b90066212d002aa952d23f6a4a49b8a34ac2562f09bc01cd59a666828cc801e68864dc42b92b093127568b6d64fba285bbc8dc413267b78aa83654daa8cf3507380886a0ed290b365d11bb9490505dc8127b0dabc26e2e283056c5f65bc733b3e02328387f9e8e8ee8b90d33c5394ffa9dd90c6687a29dc55d950ee8cebd95165d5132f3e5a9d948d3e58e046b99df408c0e999efc4f261f2848bedd4be391b96a0b791a9843a3b99ca228abba3c1d163b2c4384b519c8251a12e4ba85c7cb62918e44e64d6fa3b17489f67a2eee2bb241e60426adea5fb8b3bcd44d9558cf169d53d190b3f7b5e22b00582551260a304cfaab732999bac52b5452df427d01785087aac6ebcbb9c7fb87e4c9464bb0829893e6fc8f8b32c05b72a72031ac72d5ade803620a0773629dfad9f6bd1d5272161dbbe8231e27f20327fdeb2c038459e803fe0babc141bb8738737a4ca717a8ab5be8f28ce5022d8e7f8cc6f119d61a75dbf0cb0d701107fa35e16bd3aaa5328241a250c977f610cc3a33c719998c8423ba59803e653c1a80700b0859248090107b1de1008e698d37737df1b0bd6e2d5fa228c245c6128d38e7613d388c7b40478e93aa497fe57dad02a144d6568cf7600193700badb8d3985edb0aea22ca40adb109e89c03caa2be8c3b56642f4471bb3d6b444fb407ffb82817bd9cc37e859613234acdcd71cee8a12f1d8f85e72780c27e64b2bf017bb908b12711518a869e3a58cbfd77bcc30c38700ece387d647fc2ad6c44ab88712ef4ba34afcd9a79e2e4e8e1e7b461d9865ca2a40ddda4b9ad340d6075ca82c1d5b58ef7610a1c644349d0f78941e3b5549ea400e5ca43bc587dcfd108d4d97f2ae4da386d0247134f8573c8ae631ac3fb1c1e0e444ace36a202838fa7afdef3d8a90bee85017364fc1d4c64e3a5a83e73333cd7b348e02059b4f57770fd4609c08db70ed7f27ef6e377470e7b5825c05a94befd8a6cf8bc14b340b42937cb7b431c13ab590a16dce31e16f4b1282509bd2eebb5514a0c4f8af00ac2e65f8709323b9ecc889bfb6a4014108c8a5244054f9a40a032b409f0e1edb4a54d18e5c16a74f4073e576421a05029a9523ba41cc90c6f92b486efc29735fde8a50056763a66e8a9e994b03992e4d3e824aa368a538d1cf58934a184253122a60cdd1afc101a8a432a642770b0efd5c60133c19a88f0a8570fb0cd8516a90516b091e4837d5c45896ece703a0cdb3f7b51e420ed7fa15abcb887e192c957dcc87ebd0d1281689df7d9438930e851aa2a2472c6e03f388082df1e0c15364235ac699470822c1d99c4dbc11ed74a25c950eca2a916c32e860f5d602ac668867831ecf35bd97050f980c9f422e908a2dd84cc1779fc834be665094a1f182ce1468837b02913334d7b09aa8efa44a3943c72260680bd4646533f436628262b800bc1a0b6060bd63a7424921bedfdaba3b0b3da12e094e15d75744df07827de2d84ee50065f925162c03218fae078bae378b5d2f8309f5fff6b52205621aca6ea69766045b19b6496d5b2aa91c7cd2ff5405e7da8b84cd0e59ed97a572409c7b06b4fc6b3c242607988c79354a178fd620f5a68b5846c2e4d50eaca5ab963b6d9c637f761909e492a5bfdd3ae1922e0578adb55ec1c6292930aeb6300c28dd70e84b29d7c5fc977fcc26cc287c1aa7b1f3791ef026fa10fc656fbd39d36e4af27e036f1db9312cb779086bc3fd7d19a348e8e76f97f4143aad87f40554e94b0187012ab6ed78499e3cf09544927f00454e823067649166d586c69983cb7fde266e8f8f558920c8455c4bcb699283bde5009271069137df2fa69f8c11b681ac9458bc56d4771548297d510a75990e69bde250ec8ee25f158d6e1a23122e9458ab4e5e3a973fffd678829dfd01c5cf0dac94bcae27859b4790bf492993d164ea484676a2be121f011c44ca465233625ecf06e25cdcad5de049c9974079524e148fe52b350b4a7c47da50814c2ed32199aeb5314c230e94b5715dda226880b28d7dc5675fc106289720067dda862354c72b87c624de468f5cc873607b04c3ef62aa922d20ecc545b892b38adb1d2c8dd507d87dd8680c28fbc978314d8c2d0b07abd6a28c4a64c63055d240180686cbf4bda93bd5b326c038bf76c4807fce3c00e4fe49560ca7bcb4f1553f12f08f4d6c0bf90fee389cd47fe01b3183462811e2e330a3af1032b349f34161a9d4c6daceeca0ba391cf4d703385f44d6c3cea235fc5d64fecc17b0c1ca8647206f3631cfc3ef2940b60f81ff8efd8a464832186d91928d9821c966a3b1e553ae794bc6c3a0c5cbedf763ebc18f2037f2c221918d7176b17088a30c3f02f70945f09df9b1a6379b89d5219c21292216904c5b43bd28ac8e0c4a0814f885fc4151bc08bf1f1533c61e7f6f56b6e8a2ee4472c712e8af374e71820b1c21d0e3f915d75baef9f1d8c02227471160ac828b5561a5386ee69c3159f8ad93da1810d44b9c9b88f75ec1f83cca7ecd40a2714fa1d9a9e71f76d89c77eef6ae9224c2f256b9a2d1213088a0806423eda9eda3f7deb07ca5d4cbe13bb9065ff7c85703a7e2168cfb062d2c117f7fd9af8edbf8f92ec5af25ddc76f83b45b29a67bdd0118910b5289231b31bdd7010b42f982d17c4182b66d07f4de3685a83277ac851fb92fba8aadcc9294a49914f1219b3ceca24c38943c8b39c210d31b5d3822048f70ce3dc1b37bdab692053b94cea2ca27d0df783f5b10e17e01791a2f580a8c60b621d07e2bc8338c8db6a07c86a1ed569cc78cdcbae00d751eee846e13d948fcde9fe314ea84e92231f5417e2c959cc704eb23c42a0ca13e0c2e942aa266744762dcf3cbe1478ac54382ee85598e9a17e71366e2799611043d7d61442b2118d5b8b3b6f76db13e70730c51e2f86c0d08f59d77439f3fa9d85298632a4de8b6361c9bf0aaf9181e38c3639d86d113a3fb1b9a08e35d39776befcacaed55b18e549e3e03fba66e706ea6bd53b203d767380efd758a486e3f04050112ac8de3afe4cff2efcb0b936790a680dafedfc9724b9e1ccd22e50b2d76670085822807e238fd0a4ed5697fa48b5cef2274eb32e800684068ca49a2f114550868ebf387d25613675da6e192092aff13ea281af0f76a7272f8a19904249739a09c190891555fc51345a8104a161ee67090ccd214eee8e22af2db1ab00e24e2590a41b743e359cd6106b75cc1a86f3625048285c2093d2d1383e29309d7c4f734544d7318f840c5a0001ac5248e5290c19363b59c66b31790280bfe28df302113db93ac40ff08f92f93e0b3232911133bccfde853f653875374a4455161786642a242f322a9e3cac72fd0ca637ba947a9ec380af5d7cd5abfd8440133b88fd505a33ec15d11d60a0a81dae1622a7d18b766e89acc951940bbe808d2989bb28d0414c30c38a7515e764b833655a52b62251ee6eb695ab2b263f7bfc7edea80f07160a09464b8a43095a1312080467353c14f06847de07e8ac071699fafdb1737cf7c886c0afa365a69e124dfef95a76d7342421bf5dbd36f7673d25626dcd77c04e0756ac749d3c2b4aaad9070c4d16305b898cf6312872183f21f57b08013a99ba637d3e7ee9d8503957726d6c019b8170852503d8f716dbbdf341fa6478b44c44c58895de006de04820c0674b6d44beeda9fc4f971cf196cd0a45a3ced5cfa49908a2e968714025a3210bf91ac9505358e9234465842420115203ef140f6dfa97ab5eb72d7ee7f4dd8043108d27ff18f33a332cb5f0f81deb657deaaf7245ccff48fc31fffe439969503706f6b4b2fca8597dcd012995bc0ab7191cd46cddfd8ef6324576b6c1a7ec523d4029ff0329c53dadee0b9f080bf369b764e47d2fa5416ccc0ffc66ecdc5681c412008b341aa9903c891122464c3d928fb7e0c3dd9a8d8ce59f039fd6d47cb09bddff778c5e578f0c1f8520f4e4654e6fd8270cd9ffa7b8f5e6631501effb5e4a9520a5e1d4b2df26395bf155353f37bb7a13ff50632cc0712d1ac81620170540faeafa8506300998e895f912518ca08636a2083ebf4d6aa9a5b5aa30b7ee9e092c4801331aca1a08611c68040e351de08bd918b042a9de3d3a989780a2464b0b25506dd497524173efc1aca65a65fdf87712d99f4e5b9fdb1ff375a2d3ba2bfa8133517a14c866f00444b2a173c9db11821ed08194d24a80c0b8981f1d54d54e1b100ca8d6cdf1884850a13063f4f2465b2082d953cc01f979d4c0d1316484c263c646c68430aae0f784282db8043e7591b685f6d5ae11b28fa3c06bef2602513b4c85acf09bbd622be510b85d147b572e33e0ae1da2c760dd7c9630fdd0a0093b27675e98f9c9ab70db23dfa4f322f41cba9d26550ad0b4a3487a961feca722fade93344a34aec1552d7e13e997ba8eb041c93807522559a4385a676e08c338322572952d2c1168878f53044ee770a75878e6f648f3e458c34174f708da94baf3bf6c6b4fd6700e113b5119aeb64429afd1be91172fd80ef2486815107fc9dbeeb6bedaa3f02d155d544882602efff75b11590409153b493eaae71fe31765972bd9f890313a938656876ffc0334e17016c2808cc5444344fffd8cc973ee41ccce001af018f4d80393be931167b32c62452057ce0f9e3aa4b8f0b56019e272315656d81398e905bc5b7fea5dc5791a013a5b55e4aef098a6575de89634dc38e4c8833303cc3240f103baca85609b64073c34459c00256fc74cd3bfeb11a2e6a0915907381fbdadc9e1dd26867636997abae62e4292d25615d0f7553af77f6f0e148aef5eae9b95ce7ebddbae3501d9b8b1846fe427026e82c47de45cf81c93582720115c6ea46305253fd4ba0eed15d57d59d67c93745083bc116d9befaa4e4f7674b4256b21fa65741f14d506d04a4eaf12379a411d8c3cf6428ca712d4c19962cf27496400d94848febc4ae6661fb403a6d9071c95c35b0ff9b8ab3256e0d606df935905ecf00de1f401451eecea27e60c68cbf8d456cc7fe4691c642c09a483444ab843334a78882d86d01851284ec3d9c2b19355a19ade34c6c9a2cf064b21f752730cb31aa19a3db96c626be18ed05d68c437bcd3c12cf013295af1d86df778af51b66659d6da9e51855f442dc5549926da0dceb0f1794e3c668d4b14ffc622cc53c9db3be53a7937a750c56eb61826354e553753988f6f10cf0d12f5304c9e0a55b2d0a704b01c8fcb558aab3cfdf742fb07c739ae1ea91efd9e9a345388f94d70e61d75f902c851bd9e56e725cd3161d98203e0f02d27b2b45fd489870ea3ac4a4d06bf462fa6eed403f30231854c0177790c148653ae8c051bd6fb5d31c6270cb220344cecb897e6aeca8d16374223225bc3224ddc938abdd9222164c7d53eb1f52ff04ba9d74f5f5afb3b4735d497674624602651b9f7a218d015cf919d814ac8e2a55dffedf82cb76cc5563f4b51eac39442de03e28f9c4b50034d60ce19a87a4aebd43fe65b6822ec8caaf8bebd404c7a3df90572ab4f8c6e5028473c6545eeaf2856a1e5478188254a4553d9eafa4cffed7042368d83f9a974748f3443462102d920c041b1f589c0140da4449c371bdb3e83c42e9cf0cae98e6b6601d021090bd174cad9d230cf71b1b626068a293be94ddd21786fa53d6c41325976537188e50d83beea70056a49645a213a89b117e91a8b947805236d41fe8398c2dd775e70fa0c185826443b942bad5a6be3c9e203f4c79843556820c3a759b0bda4923dd7aa60bce3d94e59f35cbcd5b3bdff8a1adc898b5c7920a5cf4bee91f891dc321fdf5a001708d9468684dd8251ed1e3b29ad11fa5ab15aa73b144e8c4cbb69966b35ab27bba5b9dd2f7ac7d7629f7282c3f8e7f453068945d47d25516a21432d6a522482eb65db818cf57dee158b788fd7fa3de6b1fefa39be87fb3a3f71013963a093c5cf474ef0f936412b2438efbd08aecc9bfb2e4e9632b5ac79f750e6a843927489b82d4949c02fc8e7ed749a5cad6158540f98abb4107589bfd6798d626e2fb69f1bea0fe3d4cb61d89444ea0a5a93bafc184ba616bafd9917a5fc0bdb427988dd73c66f62d7b784af2b62fba4076ddec14f472ed8d563540a4753fed5adf53905d79eb00bb53b30fcccf29027825288244334f1ecfaccfbe9bc8137f53725d794d0a51fbab8f37edda8b9298a689491847a09a08658d4defa3fc6d1c95a79f19dbfa81e980b8b7610873bb0c8a98f6765248cafe36b419703a78a1496380c5100d79149125b5c5651454194ece08586fa010526c57b7149d361d20c0877af6ba4ae0864016ba85c037d453408fd9e8052ded94271645af574365570d3d0cf2a6e08a494b74919232c94a40eabdfb87e662b3e52d1419f7a02c6f7010e23c2795eceffffa8e211b883ddccf9e2d2ac59db7a6f55925349f724bd989dfdf5c385dfbe85afb7b93228a2277b90431177e94484f575764516ae385767adb0ec02e931de478e2fab29156486281f5ff54e23721376294d8ba5a17409d9c5fbe341ab6e47aa511505d6e9621a1c3ba50e47b05e311b21cdcec16348d08b8583cc5fcd0e5122f240328918803515888daa9275b0c90dbb3d51311cbd272cfc3381c3c9324cd6f85b6c436fc120814dab78269d9c6534137a129393d72e4b971d26d57dd39a4645134810668c1365704b5f6d0e46b5d639d61f27b01ad551cdea81f1663ff6603ae2be0372dd33b71cc2c43a314341d44595ee639d1b7ab8b7b7eec8d5d87639a4c086bf70106e9ad1d34c7928d49715fdc02dd7575c5f070491becb88898cab08e19c1d87b12e2f4b4223223a67b67d2374741b87f20f078ec16a4be1d5c0587816b6477a81163a51980bcd516f169369c370ef8ca100f60b5e75599af2f73a154447a23ceb14f5fecfaff4e5f5e1e5e1deb6b45c3332f9f3ce6a34e9f57a44712cc4612e9d3022453bf5fd4b1f46d4eb03792afbf766184ff78a4e35cd5e5cd862dbb29e5150f8f93563df92c499bb013cd5c4a4da2f2a263694bd471cc2b94a2deb31fbd491c1f60bda5e8c9471361c401a2d2ce15bd6615d381d6e732b0b7341015bf93ca70f46ea09c3c2e20c08ea7e7cef8a730684d23a26aa44db034a51b4582f81c246d37a4655b3f32047e76925053f8b2520847dadd7cef1937a0b89f8e24945b1612034688c4d1496a1d9249567fe98ba71c87a8cce1886ce557b462f327e45865815b5981c630a8563a83e069bf176aea990f66091934b142b2811030bab6b174cf7e4ab90bb8763742c9e49e16491f518f93681dbb5240e860ad69c6431303ff3725ca857a345e871c4753bbc547090a41a42e30a0e2241c76491bb32fca113ef92bfd8704ea7b6b509f9f2e9a5e29bbd95af57f560289e9b92839ac07b8983ca6551d0847667789c5ceaa48483817cc49afef9a2897135583f08ee8347068f1977e6c5bf1d8cfa6e7743bb865686af90040280aa6bb29488a222be216438d1a78bcd1b7d17012d3da2549968695a9b34e1a4cde39abb43965ef6f589a29676ca4a3434d051280e5e47b38c34fdf3a521ea46265210d5d301c11f8df3579e6d056690b211fd813d34c6e377758f281f681bf59fa60a0358936de579b711ac6085b826608abe087d46bfbfb6d6f8a75fe41447304dfa460ace157cdc4df706e6f4f39cd1c38a464c0ad4a9db69448ef1ac9c064ebd4773aa40c343f042923627e5a19ce39051282cebb844337013b742e30f6a133372449f4b6ead9435210cb0d8796c194d8832329eac26fe3d32b72f422ccc5c268ffc1ef4519a30e7cff88ef3d8fa6c173ef8dd93b9a7066441fe014e211a0d8f13dd39b01e3012d99961722132438c487a5f888246f1551687d68be18160f8f5cfd95b8f86b298048511060c6e66cd5f2b29641dc0b76afb0a88abc2d02691d955e24706293323b53034ccaa768422d27c26167fc21080125644e21c7857f0a4be2f76e314c5c58da5d416b0089d05952625ddbba528a704d4b827d2f7665446afede1fb6080cf8cec7df70e01a4617c0c5039405b8978a74fe087756b9da0430fbdfb1a99916312972203a8b5311a04f3ed7d9bd8c0310d4f38176625f2d3fdc250690c689c291f67f44ff6b794aa319a75a4d1f7d8cf589d70da014bfd789120edd1180ce8ef1eab87c7a0930c4de5feb08fdd81d2d57a91557a2cd979ef94ac8b0b88cb08e7345785605de9aab8d37c9e84bda0fe5b3bf82b2858f4d10feed4341b53405c045ca53247c6c64f67201001e29ede270246e2ead7f9aa593e2eceb5eef68cd77cadb0b773ab430def44395edd4350b9b80ac2e0f998045ca25fb6bdd53f8d752a9511bb1b76184d70786b0270868a9558426c8b35b0a883b789082bc9cd5f4a2c4557a75643bf37a0261df110d85a9ac6db82d1e48f9aacfd09cbc38847e35d67ad18d657ba9122ffd134bd1a4f437c1fb2dae8a07992b85e03a6bdb39f07b6971922663f560f4d96cdfbfd66730e409815e0c09765a3a1a6bf4445ba46a0556ea99c9c82e7b87308dec95e8983c837647e3e17bfb6c7452224cd4b7d5d4e7addfd9b6dcd1f62c2836394d4b204141c986867671281cfa0eab24c28ef5043f91b1995a363267dc7b5eb5c35cd32599378beca4fe125b08a1c6f8d09dfcc8a14d41f60a9513089d3af11a4384c8eb4460994f86dccd9c9ab1ea7b7f6e6bf032d3bf5933e2a8ecc3117f5c5af91b9212d76088846b18ecbb153dc909336d3cc915065f73b7170667637b243ec8bc028d0767c4d85617ca0127ad802c560f7057770ac39185a13dc33186bdd000befa42c52be2e39dd9aacbcadc002de88ff6c8293374098906a2e3e8205263eda433753f3cae97411bd4b4ebe88e7847662d2f6008f7225d6b82dcb69659b8f7747e4cf2efcabf403ec9d03329c1a3f2e4c06c1c1179057e24da1f95713515d2c6d4e521af280ee9e424bac00e8c83c735a6d73337c081f2d54f1a898d76a5a37851b738e08894179b3a5fbb62b5d834893a1b71b0803762fd88f042e5dd3416bc0efe71d5dba614ca90526828770eeb8f8d8801b2ac2891ee61c94cc6cdede7cc3389bd32d9ac078ef71f892909a50d1a24b290fb9a122acb981f0a7e654c50e52bbc0d9c214e67667b91688f97a595fa2b4025b1bbf485e08e222bef4c5a2b5a72e2f722226a7c0a4064261ee2b6be08ca9d377ad760869225b789bb10bbc60ced413cd442ec15146c58cb0fd80fc97a5e2c06e69c6a7f5b4b0d4c93a04c377545552886dee783c534bd71f97eee1d561f10565fabdc75960bca4cf8a390c25908d5ae9e3b2a90457b0449f3bc6c23fab2ca5b6b6aabec0ebde1e0db076095893f6fa6cb889a1b3d35fc449e749fb700d138418cff0de2632aa636cb21df0f49ccef5039a01f79cf3433154edf38a136258274c704b7cc6d747092dd779185898b6815c1b1434697ba220427efb2cd0b0c4308beeff11107d14c916c0427bb8d903cc5a7bba21bf28ec44865a8b915c88e284624788626452f7a382f93fd359b677cd7ae2292970889ec7882cf5c50dccfd0a78438e0ef498b657319de5bfb8807813121144d6519aba464621a9df54803b74c56de342d4df2318d0771962b349952d237a17dab7f01c6b6c0f528fec15a80d232c10514f84bb76ab94e321313ab54bebccfa56f0b6a2de5267b7962d45787cb43301c540644f263cf8cc52dfe0268682b88f02a0bbae9613331c68c68b02ebca5db5a0deadb9488c13dbf7b0ce82173ebaf83c16e3160c2506a5bb2b25b0da453043e065c1c9807daea04d1853970c1b02b335c6c8cf875e71068529d3b09c8bcd62a147d6ab7f8e19b09662f67f2a0cccdf56b2e22ab643753279d2d42575baf09c7a7216bde428303e0339fc2995a7689901f13f1c0dd6c20b920c8815c718ffeac314906e464b33ff91dd706304eef29dcb385afabe328e22177ffa8bb5732b0593c0c0ed1050349fa1ade4824b47800a9eb63863d631704cec78b0907e52b88e67a8e04f2f1896bef14a72133a7cbf21e4c91874b6cf6108c5dec291daf826ba9ea4e8cc99abbdb36b1e423bda3e9a2b827396248cd00016797f168850b48ccba87a8345e72c85ac3951fe9216236039c259c51d81c9aa0a534c4fad90d238799a396216ee8fd54c35c441956f3d93e108a339855eb8e7662b2a604beb236ba2eb4a44b9173fb4f613c440e2847afd1a5a1ee3ed8f817f31ab18f5e6554593d12bf93eda059cb31aab6852eebbd6c22ff67f615a04d30f7db6d64e087d52216fbc51559c7acf0e58b06e7faf09a822a9e7809f25871622908b7b9e3279792c3845f83701a4109bf5f04be27fa1fd98803346332127d7cba2137e4fa932cc29acaf9658e0825f27486c34496f45e0619958e0d1a67388090993dd2925b57ac0c8080e90cf9fe1205df624011938e9a1a6ba50e390c3a3a6c3793c7019678ab5bbf4ef8d24322264fa5e34a1888d32a3b56e68025beb7be8025920e9a0bdf7224d5132169e6fb63a02fa66ee10c703d0a259c8da12c4bc60020f9420fd347e0422d0b48501106fcc03d1968c8478c35e45a0b2c30441776032830a1a0dd00316539222cc303dd1f9171ce498a302c6f02c4f58950584650eb7e4831a099a24af1e4a9ac78487c538d8602f22bd11583d6bde90217efda965b13af417096844a6e16b6aea9465a092fb29f8a6355e869f48c27fb570388330b0579b1ab8cb9d397b5d258db3105ec3747072a4f7af7655812bd268d89964d423c599bc3e03228339bc0952cfa0772bdeda70ad8c42096bd6dbb226de27252a46d0692557893bfa032be8d17e8809bcc4a39f2265ac5d5acaf810ff047533e13f7b80c1abb3217dabebafd3b2241a6cf745cdc6f9dd73e1aeebd2f726cf74163d9e34c2dfbf37d2392b25b8e0740d21788efaabc0bde9ac980b76f980217a7b10539a79de1255b88848e64a20c05f500a9df3a31a9e9864210705bc036a41b40199068b51a206831326810567a3d88bc0c5325e31929e27063a602c48545fe790a6b837236b67ccb34976a58425eff5aa0e4d723f42f3ac56b8d23c45e68aebe8d489df7df6ee140934dcfe66bde34426694474acb9219786b110f74bf6d33cd19dab38794872b5a24dcf332deb8dac523a72f7b21d66063b508fbfaaefda9ff1a816fe21b2dac93549252dd0635eb1afdd885659880e0dcee17ed39bbae8be15d4f016a0315c8f45fdce6af8ad0b378eed3507da0cceb1e184c360440e16338cfa83dc3429ef57821ad5fdccce7d4237694b149fc4cf62a79845f8f286e4370ff2300f97746a93f0fb8f44d8ddc0022796394b19999856754dcf2c1f3470c6fa94ec45f592ff03b7fe48e93b54e1f39b15de38d4310c1dc81eb495e47aea0270f4f71c484a7e54279b256a42afac3f71dfd11ab73e456e6b18385bdadd0613bc0caae448b523f0db90d094a854dff3cf52874bab2ebde982e542abfb1896a7a95f4708f905257c21fcfd41ed1684dd8dc9a98692ac7a9c83622bb93c7773d743ab87db694c7158242ca37d2c1ca3701c2fdc51148e067b38fa2233818028335cd673adc23a0c2140e1a087d3a294d9bd1d7b87a251a10b4d845c5ed3489549a26af58a320dabb55bf8acd4c063c28a19a11a0d825985b2dfcaa49367434a66c07e0b678ce25ec4a9a44b103d074a38943dc5916ac14665fd69e7ff784809385d1ca0ca280ef78f2d71f00381007950dbe4914168af9b150ffdc795a64188764144249e0aba7ac4b1e459fc083cfb50c7cbd397054cc52e08513c4504aee3b82ab482bb6caf69463b3b0733396d873e8b92311e160e80e4ae8c9ae30c1fb3bd161831027e9ebe374844c34b58935cda24ba85b7a30f58432722d01e8852527f3c5552b7c620f47cff4e4fca9c58359f28d40670bdf059afe5f8c52158452211c792ae245208b0fc04328ea2eef4e24588ba020dbb9c627ea32606241758727f7fdd38cbb4ce085b4ebe9b8d98fc7a8f2ee2d622e07f423535e2e61dc85f2d810f67c698838949a6064ee4cdd64435b85488092d9a49c22ae33b122b789812f318d259aa41fdb47372e847211069cfaa962716497f6f7ff6cd290410e1b73f9594255919adf4e8dcc21d985752065f4b1543a55bc111c594667e1022ca447d62fae40851e1a24b10949c3b88f7dcc5706797c0befe29519945b39dfaad2e81c47ba4d662f8840dafeb3c22566968982ef8c53b86f18d31a19f988e8e34385fd89c96462aa11ab3d1440349aa47845953a02c708059a9cd905eba874ca48456df7643fc176387234a40ed1990405320bb7d35e3458cae9db30b41aa2953489b0e160a580b124824bcd8faa940e236ef4c224290eb1b9e309fb5c27162380e2545d372e58bba7e0e650d9534a71507ba59a986f54dc2d57b1bdc7e42c679a4cdd120eac6eccb81a8a741c991bb70a2322fa8731f3ac2f2a6586141c07d4e60bce44b075127826606b681edca2b4908639c29ff56535523be09a1aa2096134ad5e402d0a9443c572a3d31b9993dee02db7e015269f6ffc49426f7a28dcb9a0cbac83f957a0d9e1fe624626954185c1b66460a5cd73c62d61fb4bb7aa4607c912524c19d6797d2965a165ce652b6f56f5724111b2ec4a6126ddb993143dcd0eb14aae94f19907994899588e59637588ea5d24931ca5e18e092f65ee45e7a80c5eaf9fb8710fcf3bdbb9408755d45dc713c4cb7432d5b1eb9636c223492118ae5fece426f59bc08e50ed53a649043497d56c0fea470f6ff5f4872e27f9a8256488462620f49f6ce2515fd32820171461212e15e438f216f6a8f9e8788b8b058c53748e4fa861562be935768515c3601d9faaefc145066152ae5de70624782a9aed3a763dbb81ebba776230fbfb3e547f96fe020ea913c0e1080848335cf9d5972c89fa4d6c7109185e49757aa32b51028a28b71e21e50f0b2114d0afa4605af1f6f04f61664143b6e06be64717eadef223f326413ac86c94d2690255dfaf0a760beb3d4ebfbd82f5d3945f2cdf6f265f32fc3e7c3402964bb2d0030384d2d34a46e7e48a398c5ed783aa91fa254b291fdc8a879240710d03c5d7eb5466cc629b1e4e55a147cecd3e4c5b4af15d73aa34b412f43b59980db70538f5ba6246107b893957c41054411532d117c05bdaf0138f0f37d37aa0e6cdf02b4277da9f2e976b67acb82ed19b9021edc0c57944717c030179c63732e6e08bb5eb01a45c8e6fcac47cee7fa7c3fadfe971ff84d182d2066ac183d02cc0c7442f97892e4a1b1c987ddf779a2b39140e51a608c30b70fff4f9cc48d594ff037a1337cb249560081b103fadd503352b88cdf19e4388726b9f7668b5fed8f8d6fc4000dec8e5409e86119c0d616f6fda196ca16b66267e6c39ac17b6aaebc0355a8c28c54928ec919437bf5450860cdd96751aa6d9e8ccddda9ee9a3d793ea2ace9b14d0049d735442b892c8fd150c6d992c45d2fce892a9aca397decfc89da90dfddb99a824571246100fdee499ef0a9a2d60e8c675aa833c9c5bb9f586d1b1a6e0ade6d0efeaa9a33fad55cc270eba5bdc8db75dc3f1cce50457f63e573a70bc1ade44f2a369717404d8091f7626b4808a91f18469c51ade5fa2120ee89226ddd5728a289f1b6c3a47117a288bbc22268416128e305997a799d99b320ff242a0250203c9a15726d64160156734eaf37e0b1b8535404b6659bf17741b96cc002b2c4466aaa427ba6715db562b51564a0a007cc878eea1b02b9cc8fe26ca26d1a30c2304aeacac89658a3064882e2cea9f8487a861b1ada8a6d0bed196ae35401ddfacc5ac9b141bb9fc99a0aa0e6849413ec3c60c87f7561c43010d2823515c935cd0a15ea1b55834b1b8e1043b8f4b9d533d6c0d1775f6ae39b0b89c77404d20680608c64a4c8f96d5af1997ecb9f85bd00c533c4bea611b71c7ba946aa4724746bec269d6be6b2825a6d49b3bbca4ff16ab12e543d593dbb021a46a3fcb086483eede6a1d4384122f5fd94ce4de2c883d4513ce8212ecdd0db88e166b59946a1d4198959e872d6d1f029d8e4685477146a957a7c4e3c84413774dec4b58246163b227a1844c8f9ef0372c995eef42806ce21c89640cce7d5a48c3c76e608dcedc327646af69ffa61409b77cef7bb5552deef4325a20e89da192ae0d9a5005c90ca4ef79852086de7f1f2ea0f481b3887783d32985b60c59e105d11889da50619490a6689322308a52286ec9b2009452f63b281616537c2dfc6da774884212126639304088a50623b6c8cf337b41b8518aa844b2831fbfe2e7ec313e0d509f93475b3f10f51bf8e871d6deb63794c86e95803c9f2d3694f65b76d6abf8fb45003138dccba5554168763e635176ad8f999725bd3463a416d432d210accd6bd625b8d81fbbec52fb9a7ecd6563d71db5ee2db769c07fb9ff769463f528f464abaa25f3f8c9b6ab5bc4923dabd9d916017796978014027a7f57db9da70dc39fa633b0647a3a4ceaf86348f519fd5cb1609ea55b63e6fb6cecce131b371ea822a4debbfbd13024305ae0602aaaeb2a091914652c1d1b97fac7174fe2b16b3fc4ce4ccb7216ff499db59e04c90a0f4103935c2dacce139f0592074aaf632a829810640178e15073fad3c6827959665f68f8e4d323b5a23e316921419425aafe13fbd790e362c1635d6aa0dc75b103702a59d2916fc00f4a7b2796f6c2e63de1b2bec17d7d23b2f27a60c70efcb0070c24b9ef0f9ae2ac07c5c1e71d83fc62ad0af8dde74897b71ddec5125f3f72168b9c441d2a4a3de432f321a34f4d3c63e917f078df41d5bbc3f56719538555be69b3fc2c25404aee4856a0ab3c936a108ae71dd89861109f9dcba57926aa05e446a320905469fed05122a78ff9a15e44497f4b4e84827eeb7a2e4aee2468fd6711b1cfa66d6cd1670e37a4ddd94ccb2857681c3bc4ac28e6d94418eccea172777e552455df607f4359d03bc341577b7775dd92d0c4e5a88b005c23b9061e0987ae995d2afbf22b8a2cc5c8a13df0c142d6cc8ba8e01773d2f5d020f022d7fa85e33b8542f039fdec2898276b2db25d4493d17d390dc1115791ce2fb21fde3f9c1140b9a5b59185d6a63a6fb72a7f1c711bb28236c0af1da14167ae6085c243686b8277e207d677c6fa836c254c6cd27ce0b28f83ee090b76e76ead0ef984f6756286f8884cfdbeb4c2e5279f64eec8cb6fcc15119c29bbf37b40e7d368cde535bf59f1f0b377d501a9d72870a75c4d944deb01e2ffb71f8448a686f2c86fd707e754ebaaeba6235581f67a42165ef5dc5e87e26b1939df33bd9e3aa4a5fc8226141500bd2f35f3866e8413430158c0a89931924279458652aa389f7b18cb01ce4d1428afea4f261162b8a724180f7c72935e51294c5d53485a33e378656ec08b596eba1d568e74418ebd6b7bc6aae9b993a0849ff156cd172a6816b010c2dfe2167467c52446d81c58ab28ac74610a5b16cb82576370b8a30fdcc90be837cba17217163c44e55a8aaac26a625fac3064625f2a881e964b720f278d31c58851b2fe21123a09022905f470b6d328d3ffbd8178fd73d120bde3213c2b6a62644e602a5181569b8e9cbb1e468a72e924ec811d23410819ac497e12d0cb7a958831cfc373a82c0acd5375cd0cc92962c4114b188c59c04357ea31838a1cfbf9a3725237423c9b30f64e45a29a670a90f33294c64a35780f137f0fb52fd4a8b56d65b4a2cc1da8373470aedc20e160610beed19cc5e9b1bf604929966016050d5440704b7b24373eba894cce81223c360799e8e87aae12474d7b5623a7a19b45565acb1d671c15fd148ef4038fdf699d2b3b9ea9c08e3981e416e851f9468ced9c96564046c94a74921f0dfc2aa66890fcbe7919f32542b303b6215d865b45ce01c76ad4e0c4e1719c40318e0ebbd39d0aa8100f59230b7141805590878cd793616b8dfb35023a51d1d8f40c8edd3469c9caebc78c70521c75c21c8279cc83eba2f7ae443146dbcfb1c1cba900b532258430c1e84683717d00f6c3d1828e4da4184f657a5ce9a9c95230e86917e445794890e49e659d213144502b9aeb64749c4c1df1161e8eac281a9afe739e7203d25b4a032fee3a7fbee0104a602cc41191e89c55230747a6d099b2e9a96c62765c600c520f8a84b52c09d11e4dcf670ad31f560aa5d85a66ce70a4e2b99ee960ba941a50edb5247412e2a2c9bad5d61442a5e147ac53042be9a27a7630c188948368c455b91dafa0ccf0f315cc4ecad8e0b0d0701c7c5cb7c85e224c6505fac991c24087817da3eb748e4d1792b1c1a764517600c8e9c9f7876aecfa1cb2f06925e4f9f9dec433b8c006944ee3bbe0de67a640587dd6724fa1de1bd91b468468048b692b91ce081978ab0277f922fe3cc61fad7679d5533183efe330547c6690d5488e085c08f9451d987b4b3f6623271e350e0d4bf7dd2d1e60c3f1cf976233aa34f6665f44a89cd09c7b73d4e881a2a300644d663eee16aec47b5ff1e5b062cc692a8baeb661b15c1a3cd1440df9f4277cb6c5f9413ed911d9e31bfdc017e2388c27a223dd8323f51cc14d8fd1732684241eb654f6192d1789fdcf5f130b311e3c6ff1081184b1a472f158bfd7713386224ed3d964624ea7697c1d783b8abd2c048e741268711ff1198f31f93aaf2784bc79d348038b0fec2e442b7a54b2316b49adcbf41db8cdd0f76c0b2ba4268747137cea38ddf4c237ccf88ef11a3ec5a6e39e6fea59b13cb30b2681a18cab596541365ac8469237cab06c97f811d4290cb0705c1730fa66e21c5c57884a687026454d65c6205fcfb0d1dc934b59023fdf7453e0ffb5b094e8344e1a89781fa3c82850cbc312610b8d83d3a18d57941608ce9d01749332545f3761dab11418fa2b784dd99c6ede23bc306e6147cebca4fc15317c45e29cb8abc3f04d9981694f916406a2b9330986831a625cbeba1e092f619a11a872336395d23fa35b819953e21ddfc54f092de004851b78118a8e3c9628c7e00a94acbb34830455b87aaab222dba61cbea9e4d44a08762c6aba767aab1b86d54ac03cd52d52205e9a700769b7ad01b3f2c84eb79278e047a83a9d5d00ce2a1c253fedea7c7e901c504aa0ca97c4bd094efa09e6a16299652841c9484bfc120b037273e9a54a468af374c89150cd8c5784d2306f38c0dbf91160a597582d8f761e423ca7222033113f57c192c4d38d5fa690e8244f5132ed89dfa1519f39b4a6631690005417a054074cb0801881ebf4bd18a2f6e31ed3bb40e39612f5ae02d3d0900a875ec0b6f4b5ff3b1cc17a2947425c8b40a16517f6f8d59c9a382eaf64569f4cf1ef06ca2889a4484e59a1b33eb43f7a87d6f66cd04b2cb11031635af7f86656ac839c752a33f497563c5595ad8b6c2e95f93c38d58ca46d60386854ff8f8753bc8bb206a08d694ecd7acc6cb4dfd525d406bd1c88050a8aa25afc0f501cd71c66b5730fa527f1ce1243035ce18127f8e63cf0cf1ee095cae27605f20c75853c9ec9a800ee67917544ecf025115f3fbb4082c0a8dd0bfa10fea3ec7b52ec132f87d93e3a60af7ed747694f8c3314d2d26e1325567a2fee29196c21f3ac72ffb44b7ecf8bc445af11469c762a027a76c362b650e02e473833e2ec2bc3568112142b0fcf92bbef2cf73fe3b405f4d28a13a21acb0667e07b094a3133f18cba5b68ce6cc297e98d5d79595f365b354662d5eea1f109bade71fcac712106f8f8d7c7409b57b72d57346c4629f971664eb13fa3aff941afdb1cf67fe93b029b44d863c8d89e55bba5802673d1ad811b53323864780474396e5e8a2012eea5496390e8f8228e210f2e9bc4099c926cabfdc64a561147aa0a80ae4ae450c66fef64d086a7525dc975d95010ca0fa9d99559a7c663a54cdf1c057b43ecfbd399fc40e28337dfbb02c963a1641aaa9d4dc0b05570848b41dfe0ab77a333584bc8890105a3812c14b5647f7a0596064a10ad89e6026c534858d4938aa7f55cfca97da885ecc8469cefe55d0c4ed8df093e6a91610b0545346009c637dae105af59f68363b617fb013eca5968d7377632284fb560e813f8c38ec2eaca970f98d6db67c05d9d6c0c5b89a2012969fe76f91e57e07e0d608f7c6c087e5ef7ac38d5e9e67eb0b574955c8365223b232b7de64ab424708f5a42ea0f9d7dd07057cdec11c442396a10bb89312a16429718424619f3bcd2a7886bf580e22a0b7480a9d291fd618b33a99e1e427e050b36787cee51c111f5bee5e5a2b039ef4b0fbbcce7cf303b53cb87811d1537c7d679fed7b8619cf60f482b377b8de12b56f30c7fcc762b97538003cff7e1898242a65a453e56a3fc0d89fd4d449ed92b4848893aee45ed0b71eb90481fd33c2ad8932356cdd10a4e30d1ebf8c5d8927a6522f1edf48345485b8724fbad25aa9bc16a566a59a138d4c61cb2868c027fba9043c23fc844ef31913c945318ceb38154e6a0441559443ce2af8d107f7ac430870024f4f322f512e4e641999471fa1ca0986516a43008fae7e7b828b343a1f1a85c8cf97354ae6eb253cfc45cf501a9d033fd2bfd988100fe5c05019a5aa8f175a6ec902b988544289c327198382137962016e555b7e7a3804d7b56edc8047a33e7b0f2c65441fad1c80c4b412d4140b1bbea1245004523a966882eb1912dc0a9ef41741405dec67a09d0ad0dda336e1b4df2c8bb3956036602d2ab025f8eb442f1cee7b7d6031429f6a85e3ddf75aac855d701f484025aac51595d400cd24a07d7d6aa264511b9b597ae4057e1176d0482e3006f62c4ec1fa591612001b54aa8c992a4da0e58e6eef6e61e596527f60878c99f6ca2d9b9dd37558aeb4f7ab9a8d380992351fb7af9398ffede841291bd37217b6f996492293205e7056b0551c0ef6e8c4f061583769d9325cea5e50c5264159665943a15afdbef764dd6b80192edc1124bb5ade0717b27d88ab8f1d2a5658c018f1b86a5ebbe2430ee3db5d4eeb9adaeba6dabb5b719dcddb9544e05ad1ea6a94e4fa26917dab5c86d75e38e6dc7b6c27aab8165b46cf59eb7da9ccbc1c5bb6eeb4497d7ba6d75eb28ac7dd6e91b95e6d2b5b65c2dab9fbb3b753a3798ed60b0d94cb9eb105494525ae9b67175b5ac3a57aed4290ab8d0ca71f40575297dd1f79342bb749974636206266544e706e6a5cdd3c52d6b597753da4d9b9c4d7677336566e6d97101367657052a07bef8ea9cb35a3ae7b6ad3ae7aa37b91e5c60f0ad1e1d574d5d21bcb8711b6bb4dbc64c79c5d4683e0aacb5f673ba74b4312ff71cd364c072bdc97523d85d71efbc63db7157e794503ae52adadd29a5eeb452a74ac89c7271efe25ddc4bdfb69655af75dbb676f759c51b5de75d57dbab7be1c2c5a8762ee709d67a6bfda424b94cebf2744b5897a52e4697695dccdc1246eb22352fad5d7caac7024ff2240ad44a6e8502c9508a8af2a4284fe269a530458a14efbd9ed8b295528c53c2560ac3306ca5d05ba93bcadd7d157efd868181ec5d7d38ba9496b992d3adcf25d9247f3d71d50840937dc2a5b095c22ffba995b60fc5370cb0af3dd53ffd5d0df6caad0ffbb27bb875555b0a451886b0307c17ed713cb970866da595ba4a092ceb8b42a35b513c0ab21da98ed074c69aad9fce9a3f7ffed763c1cb980005f88795e2cbeebfb4f7f5cc401cfbcc7fcdf95d8a3bff75eefc14a3f79d331c7b9cbaf1236279b56df030ba9f2b292ca5a4a4566aa786eaa85eeaa7c6726bab36dd1ac3f36648d9923c894b8a41046cdd70295a3dc3ef25a6a6567a8f7eacefc6ae65acdfc61928dec6bcb3831410ddd64f9296717524a496d16cac65f5757ee74f3cfda424a0673a0155a569e74b7fb2b274e3499ee4525c49c759b02f5ba97b2739303788d1911db4aca2203937f2222e547cf7221464fd1f5d88fad4f7c0988e9323c5e8c0932051a06e427deab7ba4a5b89e9d01435ecc66e6aa6962db5ac95a84f45c122cbaebe0bca3396f3f2fcb646053bbfe7cb56ca799d6f256e81a5a3faa7e7ebc4b1170786f30cbed079176dc1786353a2405d542b45f59023b4acfece97ad74e24baeaa0102add64fa52a6da5f5fd6ab533c2c628295ad6329cd1c6bc652b85a3920deeca888a8a4a413a596badb5d6a74eb400748042530191d7d3cb074b4b01141c685366685345b422ccbc9ea880e81ab1e6729322882ebff3379dba5da9925326aad46de00bee5f6a2c14156c57cea83ba32859c26814b3bbbe96d59f533305b77f4acda82935a79c4e6d2b782fa715ad082e3f20390015114338894193664a903389c0eb8abf9ab093c5b744d41dc0659a981faee79f6fe197e0652010a88ffbd7d6d7a05e73bef46fbdac7f66d47fbd5eafd78b74d1af31f67acf35e62539406f140f509f560717944eee7cab7eeb7d2c3bf71d72088750d0eb2908a18ece2d790afcbfa065feb1eb0f989792fe4ebfacdbef8c433b3b0f1b3db0e3727d155fdcd9d9791d388276481d382f56b2c6bfc828b0777d949dbf69913863095388a9c951119109398286b84d500e59b413042379c05e8728c2602fc2c4d701fb9cd781b3f33a72704817d4a71fa7eb44b29dc2a8ff39f802ac4fbf1fe9f774f49f171ce7f52ff9fbd99de0ea2748bf7b91b1dd9796affb53ba7dadb40677fd9b45f92b988d7a5b46eb37c963e775c0766062ceebc0c9c1c1f91d1e3aaf2347270796f33b275e87ce899dd781edc0eaef3cec775e07ea96b1cbb42d4e3b309d1c1c9169491855776ff26a9c219e7ebe013426b4e480030bb433416e98817bd8e189922321128c5922021a1349b4333acc508858068520539cd6c08025c969088d7c0620677ed45892e58b101a4e00b304ed8c8e5bdaa7f28303274547cce8c0b464a98a0b2c413891ca42d5040b324420f411fa986939b183abae11e02ced7076959162cb2ced4862ddbd0c90d6501b35ae323e866e4eb03a818a1566a8d65a7b9031c2231366a8cd8b8c0940325b88dcddfd8909a9214e38386142490c649a7e90b96195b303d72524f2922c9961284b932e41602093838c9010d45a6b65336a5e402a8a8aa1c913a5315475cc19a336750c976dcc971d6d702ed3cc5059025ea69951e1bb3a790c162a30e19053773348d7cd18b91ebb2bf09dcbb4326cee77995666cc2dedf3f0026781d2c3d29212506569018530472c160ab9c7700801591285c70f2f3df8a02281199b181b2936b1865571814625f54a037ba1b1250c067ba5a96ad9ab8cedaaaa9719209eb7f462e2f5e475e675e6f5e4f524c9652246445c9c3211b9650745a32e986bc3299776daf3a5260acbecad82542fbc98789da1513dbdccd0c034d194488a227e8b1d8e07b2c096d7f2a87d10b4ad96ed3a16ab6f97f363b1baf7be9fd53df82ea8d7faae63b1480eec74ad9665756f7f46d712bf25b6ba6f91acc11245511445513c61bf652dd9df02c756abd56ab544b1e52d0f0767249a3263fbafffdb9ee5b11efcefc5f1fb09fa37f8bd7f64e9912c328af80c96300b9ad2df2d763f8e5ba3694a54da972ab77a01d373ebd6e1b8e9b0481255b42f4597695ed6d0bc807901d1de6cdf3ad6d7b230984e5718fb219104892077beede69c4d7396eae7ee5e379d20388eeb563949b0582cef635da621d1a6f5812008b66e361eaecf5a6b5daf5710f10bc33014715a2ae87c393939393a307fe1c4b7b3b3b37382c72583055f0ff862f67c4f0f08635a007a2818641a1252685e9a3ac537c7ef07efebb089304b60b0f090c38d932335c50e42f0603384871d4749628c6c1227ee354a982476c498d0e30818e6e90b1325e448069769470c71732ed38e68da76a830f86ee41c400c4984a0cc10060d19fef2e58a9227477620420491e7dc82fe772bcf063d6f1d37e84beb9cd503cb911506ade4023af0601643c60acb40348b0f5a84109aa2821f82fa610c848308d416a72134c860431041fdcf40385258926605587698d0c292a07e190349a00710528cb61061839aa04981e876b1809675cd9e6f640370cf0f8058cfb52a32b81a74ec0160065f78e173c081abc705070be0c0d5b36a56ade99f9e15e5fb85a3087d815e452cdf9bcff5ab1720cbc117b641b1b50a377091a55fd7bf7ee74b169712e0529f4bc74ba94f5703f47cb95dfa5d142d9c5b884fc992860697feac652c8c580a83768055f4f78c1e9818f4f38c374614e38ce1bf43962960ad43e69038e4346215fde2380212e2c4228a566c0244e004ea0535305ca1e100426584903205052b5a82c277fd178e3f6eff6b5cf1c2f50caedee53f57feb3ae2e576115ed4137eb1fbf3eab4c2badd56a07334627604209282d637eca0710a3a71f3d66e8a9e4c57a8c96cde6d16c89c71155b2741142e10baf816129082117804059e13146a4d01024898b1825ec24c504426b5c0042488707e0ea999d65d4c6067e6bdcb8ab6780df150ad2a9a96593a9653262fc30cda9a9e46207193448a8525873e50c0e100cf121071242504d943c087206cc1227453c518380205aca242121a2cc95596532fd943a4c55553c99b4dc9e555ac6dc651451507cc78d9331a0bf8d9455b00a5a7bb4e05c4a5f0845f3b432954411d892a13cb0808cb20b61e64c0c84e3484c102a2d34c920460541fdd38981be3037a05902491a286582fa271403e15062c4972b46517058c10f41fd338a812eb0e44996da910b26d021a89ff6744f198f63eeb376735f2b4752fa2e68cfe9d3371fe2087d10188829c9f3a5c3086cc953970ab90d9bb78f3b51f74ebfe9d88d1f13a03e37eee882c202809c41fd170ab6847110f7baa16723bb9fdd67c415301501b0e0320e03f1ff70ab1fd64f364073a37763e7dfa3b38fff8a142a6001b8ee5f52760a24fbaa576b8a5b3f356ad8884004be860d193c1fb0fee66f4812bc67f5db90c1f3c1cd73cf912480af6fd2f5dc7bae675006f8dc6b9cb9469e7e1c41dd7b7ef381f7ac6f5006f837ef323e1996bc2179e8b7a0beb1f3c8182b32aa71cc2fc1e2a8dd09e1717f351e4eb5a22d2111a1096a3eb408f14310351f4ff3eb6d1312fee05ef3513444864f966f903b89853ecc62a10f33fe19c0162e253124357505892a20e86104643a354421205146aa882f42476282ea95a9dae58715c7521b1a22b4f25dae150561c3666162510f3cdac244765a94838f4a5ad9b115b94044910f138a7e0cd58a885cbf5c2b3a9299a9ed0043543b018ae740db9bb536d4543361081c6228aa1151210a450080cb35221fb5a11514416b3992d4724829e371536f62c0eff52cc009588b96da0ea7fe698e5b701bb7d82ab7a8945bd0e6163db90503388eeb2639637bba6d3326b76db5ba53da3debd6fdd65d83bb3336fa1bc0719bf41484926a398cda30b5b9a10eb6fc8f59b1bbc1b29e21c0e29ed5bd4f06703fc97206c78d33b655d95de69c52c56236b8c76a30412ca4614643cc3d66e6f9c5e358b8c2569b9a6ee882edbee4291068b3940ccb3f714db76a00b4a27dea735d672d0cf63f6b1911528cb27a17dd4c7426b3b7254ad907bb16f83d73ef711cd9b58c2bbb6ec1e318c7711cc7711c0d2e031cc551fc17cc56700bef634039fcfefc9fc5797e7efe679163149c0f459bf3dd6f61af70cc19bf7db8678db29671235fd73f05c279d7f733c8728d769cb58cfb9b71c5025ac63d38723076d032eebd3106abe09eb9e77e72ddecb02b0bde56c97276c1ff98c912fc4963763330bb33be677d6439bbdf73e2f33f6b20924d72cbeceda73e3731f02d057ad9ef7f95dfdb0bda1216e3cee0bedb6c7b0a737653ea5ebf58f8c4321942b14cf2164ab1fcb505f6473d67d5d5f6d1afb556b61d7339c2a57c66e6715b7fbe536f500f99f89c33644229ede12a1053c17f9ba19318a55d0bc62af835105271f1eb388ef3cd2332bfee614b0e2ebf8f258beb1f26d9f9cc9d0e21930b5ab03d4ea9cb9642166870d3f5f0053b3f64c262049eb7c205b7905def65d4a77fbe6d9cb7bb9f210cb6b49676ac011ec812822c4797c60c4b072d6b59ea3357f382a32865e9bbb8e9680cb694b13c269ad2fd8e0fba79fa0be89fd2453feb7b5894068b06dce8610814a54b83a3ee8ab5226fb86932d42682ae3e47c9600dc482fa8863acbf48c68a5f6c39912652734d38320a4f93b66eb552cb54a294adcf818c50060b82054210d240dfe89feec892b1802f3652049f555845fd1ee9c89d01922865cb8954c32a550322f44f12fdfa134aff94ae010b17240d6eba1f5821082f5397064fddb98d7ebd0baaf65e70eb331317c4cb2f64f25de58eaca5083ea198525a39aeb5ce64b19e90491db965b57254bcdcad6015bccd7a8699991f267e4ce81017ac823560516b18c5ce771ab15b57c8624c389c32bff236c6f3d901d4a4738a403ba0316ba251773a4dd289054ff1944f5a43d7aeabbc824511eab68d3512206bf7c9b5d52cc86e562d1754e0ce1873cfa511bbcc2b58f4ccdadccd4df48777d3f929d370061b320967b0f38bf0bc56aecf33b8bed71e175b3cab60104227b57ad8c46912112c343547684d34d19a18425bc1118d0d5177d358a804ea072966686b96686b8c684d70a1ad495267e07f3822d0ee590ebe586dddddddb3c7c4a092cdecd36f3b8c8a81ce10444d0ed877ec3714b911112b5a8ba649141f9d7b5f1a26d26cb9afcbb4344f4dee55553cac98e6061e422106f0210c0a599658e9b2c586aaa5b00a88162a00a0943182882c637e18c26ba83aaa52210577a741d07c71a169a2885b41d37483468823b43446d2e4e0c19966d775676164d975ddb33fb1c771e0a7342ce2d5eb1133982d420931684eb0e2c353b01540fc2b20d6f373958bb27a8f670a6eab95e5ab516c1f510571ca8b99629425432728017344494d09162451892bd9c17d0cf5151e6a4b6a4b7a882c085163427482b804c5e59a92274a52b8a57d1a9decc432597e77bafbb3cf16a1383333f3a424152336119950f1301ef7d6dddd546cd3e9b59bce2db39d928e141393cc6662928aa6960298bad1dfb6c771bd0a3a60b6b993fc2606fd0e8a466cdff2bb1e573fd188f5ca2dbaefedb90634e00377bb3e637bdab70319e0b79bd4276dd54abae8a7cf130629035792fdc799db00cd6c635ee656536b2948dd1bafde6489e3ba830da80cf819eddc829dce362212e91fee9a8e3c9fc19e3fc17e15f8a42df6e8c9cccc350833736566a6cc3dba364ff7e279ddb86ed5d1233ae54a62915824165135133d9af2ba719bd7ad566f42e5d48bec7c168b264e793b07cfebc675ab8ea7e61113553781f2ba711dc744d54dbc6edcc644e575ab4c5e7d1e399d953cd51db61162151dd40c89d22d61174acc61b98604aa8684a986b474044a34c1720d49931a12540d0952ed48140fb1870dd9846c4236219b908dd82364239e10b209d98846a20f9187c843ecc15fce49760af01cc4207d2302b1fc747425561183876b2ccf2906b1fd5db774a4a866a44a14221e31333333d31ffc6da2917b157d783fb338c4ce6777177b74ab15108f63e2907d21352505c68b94d49aaa27a827a9a827a827a7d94da97b9ddd94bad710a87baddbc6d56de35694725db75ab158de8ac5f240ea7d1f08b65a37dfd76a2d59ab74bbd7ac69a32e5ab3a60aea492aea09eac9694a99493349923b917a4d1583011342a941971adc762616b39b52f73a9b52f75a37afdbc6715db7e2bad58ac5f2bc8fe57d1f08b65a3760ebe6c65a97eb157a18d29ee12b14451c9c1c10468e0e1dbd57d863c369d35b6ee05214d7ad88449c739beb1106341ca0893468d018f152a48d9a226c4ca82a628647d589cbb5224314090204454398c0a6fd32662ceb72ad88d32dff65308a30511fbe34e7728d480e446ab8e5cf946e607cb76d1b63b7ed68058308d28a4893bb11996244d55cc82c115b22b6c46cc8512c06c63df625f6251646c8901d428c7674976b4288b621424290c208c931a4c4111b22258ed40c29513b52e272976b475c8eb05cce72b916644d77224fa08edb87bfecb1edc3d5b64ffbb40f85a23dc578dc97dde3fe8e9d9220042b19905092c6cc8a9cf5da838fcfc71110589b73b906c4885bdaa755de24823aa58e05888551fb395d4a5fec0fa16ceb720d880d3520546ebd5c03d202125e19c52959c214fe293368d0e06f3203bf81d7f54f7773cece7f1e31837128f56595a3beece963ff45789d7775dbde46dd8dc7966dabfe934f90c312164260049b294882a81390a40b0322e4dacb3520396ef93331b35909ba6e068494171e6c90ca618448131e7a6c59f5404285a72c2b243b7c60fd38936406b3523b74406c5801798a726669051886f0c18685a61e4cc01e52803cf5f0d1ea81e4e96347d07e5809a5c8232f39865648226929c87728436b3c9828123ed57e0471bdcbb51fb5304bd21405dabc4caa6d42511f1236a1a8ad6aaa681e4da411928a661488fb201f9ffa6cef23cf074e42f71eb47dfd1b256cefcf91256ceffe374a20854ad89eabdf0fcb800a55755212cf07503c1f8ca443714a3ce616f366de7ceb6dc860fdeafbf59f8c1bdddb088024bc1e7c2fc8f5947ce1afcee5653e474571de73de5ddcebc1ee3df03dd02379fa57e484a23ef35f2338f274551fd5d1467dd07bfd8b24c105be3efc902481fbd77390f74dbe68f143d78b1ffefc46e2d91e4790f85e1d6df4cbb8f9d5d3b72183f5ada75fe3831bde7f7ff31fdc00df82325c32b87f7d0ca2ee5fdf77ff2279fc73924610723206770638a438ce0f479e8d9c56aab5b181790398a8a8346e3755a9636668000020080a4316000018100a8843e280388e24a22af90e14800b6b80426c503298064391480ec3200a8218884218844114640c4206198520a2ab00384cd46e0c59d7bafd9125bbc32a7a6caed40e18c6ad04599bda52527f09ca900bc53fe979a97f7671da1580f0a693c1d93b59201e84846789568bd6f38226ea3012680842569287cda5e2e0aacd80135f3fc88d9aa0d8bbb57d2da69cab0cdc362c14477331654405195e657767bfd46681f11d8da18c5811e6804ccfc5791710270de845c8e80e47777d841963158d4a05aebb8dd2e797b93b6419e49a94965e55941e62a3bd63da2c81183766590e2ceb1063b690323e666849a6f0b639d259b9a78dcfbe355e4612694d1612aa6090cb56589656ed1c5d16f196e7d71386296e126f5e7bf93d406aec85eda9ae53cc689a6cb1a9b2803fb50c0927331f2d9770c2bb04855879da0d3df7cecde6e7e24866f3951d7f377b17c7a6792d4d8fd247f2a328382c12d0698ea4cbea978357e2a799c9cbe56dbf481c47622979d1e27f319fce37ccd1a5d15ce6ff70e50074ed95b0b67a166829d1effd4c1156775bdf55e399ad99a5e1fc0aa9218f5893269fe6494c655425724ee15e6bbd2721d8e616af1da33227aa892dce973859e22c9f2cdfd55ed1f11019e9bdc8be1da05f74e8758386b6673bfbb34ee1c0f6602b59f789014b5b66f2fc6673d166920c0eb3c4ff83c877794e2f4f17c89e162fdde31ae53d74a8fe9e40e03bbab4367c0d6d89091aaea3d59fa06ea5408292f7a2066cea393a38846953026035d10fd087c8b2e1acfa512f58a65081b2a020179a394407bbfe366a88fd84f683f28942e51cae0a04544e96a2f142c5c91165d5e5c157a938bf4fc8d6c8193abe1c36cc2c678a49bf87e5a9d870fa2a88e88d8728c7e728f30182df2b3ac7175f3eaad8936e95c00a2e283b37fd1802b91c7d6ffa1d7623df858d3f48dd5e93fa00e143ac5c92f8db7f9f07773719e86778005eff4085aebdfc2d06e20de52e27348f72cf46f434348b250db126c94feaf35451755d61d9882bdc38801058f67aaba3400308622628a6ab0d08941e405029b2576719534f349166353c2e2aa575fd879c9e8f2328b211620ce8811243e4be959d899e4aef82cc0f9bc4ccb7874403e10f340729a8f1c0c2fc32f80865d1b39736cfbc4b5e84ac7aa039a3944564c4f3dae647697e59d7ed8674d67863fdee6e13547ab62f6d79a81e83fb0c80f454020023f02d5ab6233ded7d17805073f014775ae2232954c03ab625018c81b24e9c2e75438bb992a60afab28947d817a97c58bc98d45e4d7f1f4d0ba76b4f9a4eaebcf8204f2ff7a04cd0b79e721bf8bdd83693cf95b75c557e4a1c5e1783b45450206cbabf09327dc08c1c268995474c855cf3a91e51b213a7974a42e5eda45770b0647298d30f997390c4181f957d49b3ceb55984720a2277186f34f8f9b8713bdb1644338c919784c10d83d716ebc41987dd03a963d18297e20048c4cd1e4ec49532e83e2019d71dcf77a41e04ebeef7372c7068cee399433a721204a71f9d40589325840b856e445e2921cdab084a8e68031d154d4a7a36f8a76dae7c3f59776aa39f9caa431bd481d7b0303a998978c6cf3d0352da421be8c3be62234588eb31a5466803ba56eb7b044b09a3296803f5d76c07db9a2f425c01135f406b15fee5fd178027f0222901262775ff6ca0cd72473ec302184dfe154023f5b0aec169317f619e2cdb6d44cc5f76af5476649bcda171459ab5ecc74857b2d9260d1b18185767228ec6e76b6460ad5db18a48fa824906d8b321e9e867675c1ba6b492bedaacb94b4326f7f52cf60c707587344cfac2fcecd593601b57a84b3be9cb473101a373d8da6c8b924789eae87423c4370df9d3452bea7352faea303ad632813142f9651a5ae90bbd7a1ca4fa83cfb06a2153137460365c0df62a6a625c0cad79b4ffc35d51308b9806e0cc571c44b89a347b5c6b233e25b89f830bf802a9f6535093c6c6788fb407ba49e01b7ae2cbaecee6dc436ab543d41c7ec2ae9acae3f0c33b3dd59b3b6872dacb74a0fc8f43e2bf7a4b632e1df69ae91b0b3e7d7fd1650aecaa174553393dd54b6c46da1402076c9997196a244ec03128f6611f5111fea90ce9821155aed6a830cd8dcd8527db212ee19b8fddd63c51302452e0ebb1fab3ddbc5881fca00baa9db225885014d441d0736f98aa9002da76cf17d9815a56d482600ad6f298ed780d70d593f276033b4487683f1574e74b4d7e3d0624240baf86553391c1989d35c53ea5597a4624bd8e76f1850d9480423c377dba64a9ba63d62df2d86b8e0b0175721c630ad853c498cd261a694fa02e697074681b238e2900c4cc5ff66adec02ddac12af4b448fe04151f22239e5afec43f7c7647fa35914726b838ebf501edd254e93d17363e3bd45e5d7bca10981aa2ee5eaaecd3fb1401cd5d739d6236b3a1c2d25f8d08d3f4e31ad262f957851f4752bea7b3819cc0f392a95064d29e5bcb2ac8bad001aaaf3338aefe0d220f3cb322a553dec8207e327fe5520b071a2818b940eba6fbac12645d7f3373743a6305e31530de558cc906733e10b290373c9873c9759a5ccb93a8ae72d7692cb72bef58dae9fece3660d8e4bf62c2ba322ec9c43c1ca1e1ab616de9fec8823aadfad67a2ea2691e1d9b54c6fa1a12c0e632f903ec7b0252a7b3a0cb98bf6602d0ca2ec0dc28b396b641ffb2492a66eda1fbf80b60e7dd64c49e8a94497d81ecc81502300d55a9550e6c707bc92fef1d709e899b5eef00d09934b3ca299cf8eed004b8f99325dc9692c05eade0b207454ab69fa1605937eb66b919c617067da7bd25b03c6c206de15292a2e4492652d602817da3589079dd6d3fea01b51882b91c8cbc583b91fd8618519fd17d8e28742743356c9c82979db1409ae2d403466feecab779855de7769a07aeae260b7a007bcd38eb286346c552666b019666e09294889403bf65929a42736c83237b9c48a56415e23f47d5bf47a33a0f632fd3a76062fc68930159bc8d3de4ac2c4d6327bec50d19a56b7a4719771d3f677c984220e45518e2e38fb9275d0dd27a666d8fbd1d90df982096cd8bd7b122c751339d3cc09fbb28af31964e0f6029ed5af9be8007220ab40ede6854a2177f4c9c0ad5249e3758aa240a206f44b5b37ab413ca9094dd7285d2f1dd2c79c31c3b8245f6b30029f6e50e1981600001dc6e0214b5f9b7a43702620480c87e161045be572447704c18845ecec0a240127cac0888850da1cc310588ed4de3184c4c925efa9b861209fa25d7c7c474ab192e348afe4756cda4e72f79e9fcf47ab19a2907d1563b83c1d4d4fd8b428eb674019d2ef8284bcc72447606c7ce8ffa81fcd67975a4d944104c9f0e5a175093be2f9b1935298ab01927389fdaf42df140ecfba2915937a62ecc55bf5faea518c756c9544c2142500f7b2d3ec86cbe8eb3e7e5ee167c315266b49d4c87ebd35c8e7f3f81c9f1ba5e15b251cdb0a653b6d73ede674ea123bcc92b351615591cc0e131ae5fb518177f19b2f658925b084c2ba7ed31752c4d1ee7e79465da1e077ad691c917f919d9c683ab1d59797250fc19cfdb9d820f20beb597ee75b682fb9f37907d8c14e382697189998e903a605cc084103fa35f6dfee18336541bc49b7f283daabcb41f0ce56d6ac6bfabb9dc8564ccb66f5398d50a95e79c26d73779147969c66a61f38f54ee07724f8af5ced7b9c6fc9afe444f9b60e5e12b204340c149448115a7ed28fd2f7e622484074db35d8de113506560b95176f782df248d0e1f9f321adabb20b68d5180185635794352b8cc81a77220591b5a0ec82f7aa9748729dae54100ea9583517b5d28039d1f295e1153fce85c54ce198459791015977a0c5af36e34c207e2605b1393c4808de961f723e3237e980713e2960b089295ce8f4ee9b289fb37ac8dbb4eafc4cb06c87103246f354f3cc1468fa899ae6e16799fdb178467840a50bce51a618b76e312db602b6e096611160681641f76393bbf0644075c6507c55b8f8d890f07911d789d9b3c1c23148fb1c55e219b37f83ce3d1ee250d18ceb561741162c8890168515e77b5e768b70c03d0f383367852b292d4718c72f930ac3756564325e5f31d5582998815bd4a531f31aab136bace80b8a54e9272a953ae394fec1e36bf8e57f78a50a7477c139dee685cb012f11b5b6af744e2236f9905337e0c53f16e8224d66332359b1b330bde6ec67081ca7565125314e2a090dc90c44070b3ad736ea23931b4a3578c14e7f06ec9f615e26dd64eea1addd1c8bc3541cbafe1e8bedf0e1482f87e45579fe9a8041e356dfc35ce88f0572aa271c6dec498afdf52ba0febd25cf44505abbe1b2573cf08be97262914624a76b7bca7c4b43ecad43381b53911fce40285a8a95c707453a509e67b70ae1ca41e559ea6402fce09ee1705c6b4f932c9b43aaf3c04138c54e22418cf8c42f1101454b0396b556ac254c5554c56abe82b089b30631ce375bb0082c1bf96d65af40a02050f5c18176d19dbc858a59f89def2ea4abb3233901b0e7c2543e4005112d8b6f4ac8e9bf7faae29b0b46a2e138f1350b281d94bb52af7bb93fbd3f3935e51563c8c7f32141e7b27314b5e96a4b3294b30e9b7d56c81df2ecbc4fe9726194b73a0d583086c7f446f58a07e04aa256c27a044a3e9e79d2b2209664995250524dcf664a600e2b6daa74accf1f812cc773e65454cf8ae9537d07f1bed501da43519c1445f99d88b42dc9a9859fd91b86c92282b5e9e98ac44a221814a2c657742fd26329b005dbbbc2774bcb4b8445b9a71a8a586653d151a3995d1427f5cf3541463a87d267154a87d8d52032478692eded41d09ae9a0594a19afdcbc8432920034512630e67cf0817f6664e48e4743052abd3d31488f36fbaa31130cb78d488f3b26ffda4e950e79f5bd685c48421b3b00bccd11b49896401eef7bae27c7aec4e35aae5c144681f2e1a9efaa10ca291eb7ee617b481e10277bafe2621be681f5ce1e63e544750f9dd7ba5bdfbbc4422a6935019ee29b158d1af9a62134a9d01e8712c635127e4c5e18e4834d4ca6534eb614b7ef85d6bc097c27bf1582b73d44b2a9e4418b85dd9522eae4b04f744654be477835b10b873b25dcfe81ce23f469668992eda444a64d3b969c5e6b5ac601c1d2fb72e1e733673fd0d5e9242de9fe2c1bb84fb7abb04a07fd2e16e79d204166c453b84f2447c6a5942e3972b120f74f1a2c8460954541b1e29d8ee993925f31a0c6b995ea37ced7844147181ebe8833cad846ae8203a2254a8cafdfa375ae2f215d4b6ab0c6c5d533e8d1a7ede3025422849c3a329a48bbb2ae95be3c16de4466b45a95d6c674e6fd7cd40c3d274e517d73695feca5062b461e11cc0e75c45f52cf9514f36dfe2c15d6deb6182a2ea28c9e8d1ace29dfe6570c4e997a756532a01b93a8ede801219aa64562a4c7290bde2dfb0f0ba1fa811a14c1feb850a1c6ab0fe0a290f388a5b8599ad59012269658c1f3fedec4d8142d87106aafe849ccdbaba701c0746de0aac3e3278bdabc96c3449dbf66eef739d1636e0278cad77503a8db302a0fafa30456952889135b93079ea40f80281e1de2f9d493e7a0550af511a1504e5e6e6946c219d04f5f68fedbd3b399bc06119d38fc444dad1cd6f3030fcecedc30ee50f9d4d2d2756ec6b50727b535a009e4820834de501d20379c0bd3f3c11fc1e69320fe15ffb9e26c85e8bfa856d54fb6a1b7f5b19515f2e19ea6a7a069dbd61717b688ea2fd1b672460af140153ded6b670187edc021d99045c635358829012cdb9c999584d4f5798e74d625a26bc7f13a9c8adcc4621e94305a3f210a47f26cf4a64208e9208a133046ca412cce925c2f549fe26e716650d90f42c09dd056de0027e9c98e96fca30338c5209cb917a1801eb41df1c4911524702d027e7a545ebe45eae212ea5a74f64faae64b98167cae94113c5117bfaf2802bb32665bfda563ddf96fe98b2387fb14c592ba7024f3bde72b39d15e6efcd907b471765e2c237c035b8f162c7f465023fda43448423e06b9e4d7831b8a6ef4fda44e75418a13ee7ceff2c296493f0a888204b31da1020e8b32128ec0b1fa496c77702daa13801715a84cec2dde30941fd822ca37d87217ea4bd037ff8d5dfebc2e5ecfaca07e0d583158947f2f89419a4f2a953c51baae37cee8c99331d543fd7914da9e98fe244d86c2620a6656de56ada7443556221049e6f4771496fade1b2c7795176d088c8c4926212bf302a4f49baa8ca0562db91ea16fea6bcc6ff4e49080123fb034cbbc486b7d70dfa3201b6da3f8836c5af0856a5ff64615da1193d2897776ae7b8932778e26b7e5e8dea00ac31e659aaf2d94eb08ced06bfcb40f4f782dd06e0023742cd8ebcdf8b447ebdfd18122427b336feaaadd5f96af41f9e0fa876fb67aa15b397c196a2031a1ef36fc44e3da119c21a4fa5799361de3a4616b3ce1d0184e4209ab323c2680a87507fd54407897bbf3ee30171b5304da2d85afb61190561a8091535fab645d378a822a564146b96b04743ba1bc0b33a7ba04cafe044bf8f8cfe741468456bec8bb22542f26ddebe4d64abde541c8219c696385c6c64bc953864897f1017f4a994a71550c5e78ff98edf6a36da50751b1e97d8de941680372666f879fb303c26785aa5fedeed8cd202faad7231aa01b9a220096836551a27df3ac72dfab935132bec72f16912fa8c887c4a20890931085da64a17e2ff1d3ff55d53fa90d10018c648a29d963ff0ca107cfd69b93fc303783c1fe8c634468a27bde048bbfdfc50167211adf356d2909c0924adbc27ea4b8c36a54247ac9a1ee9b2ec1d7bc3794aa2d751f3f1155c633f37fc14c42cfbc5b2f3a9737f8deade13775bb6ac7ee2f5fc6fc2a40b8726b0f3e2977ff9180dddda0f25c1355ba33ef80fadd86663caf6c4f407074cfae27a048f85fb8e8d52c8e6e88e49cda5255c62f8450e2250b43244f27c4ab8e7441b7e4caecc801481e03e8de27519a1d24209f2c29188c273138ca6b3e49a040da8ba49c2f8bfc1540e3a6072c6224bb5ee5cb5ecfe30d52ff2a538103166f6e4252aef6a534da051489911ce182ace808366a53795503f94aa7d764433a92da219548f823f82f4473b8138fdd162293d6c7409bbbaef94bcdf25bdc398fb3f4b20313106ee0885f6f0f072a7f4aaeb2ae5cfafc72866f345e4643d7b27d007f624ac74d816f528d3235c7b5fc8a033fddc85872812474312e9d2bff1dc1a5aaf27ecbeb3e4caf6da736ab75ff46911f574bcdb5909c1560e135e922e52d16b35085fbe4813c11a8c3d07511c0e0fc86786d3fcb0262e8864c1e5ca0f91dab1d03faa8cb59fd7f1508c12804ea27f78d4f483687639aa4c6306ace295b9a4d3cf843af515dbcd8b77d18dddc58538ea811f7c12804944ca2914fccf8ff8de265104c341c8669b02d6137f63cc0441659a3ba08f0f970aa8341247aad80d88a99342be185f6c7924c62180bc01909b82b8050df128217832ba6d1b43774ef749c626594269c85950f35cfa7afb0586e5a12ea57f71018d2d2bfc9867938b6a39ca61f889f40838877d99b2e7f26ca3c564e250d083393446ed37517d1c6e204633900bb93a03cd69ec5a60b9984f425b35f95a24212eb963ffda87c81ffb51ae17578fd398639cfe85809d13bb7293edb6ee4b3804b0e015f443d0c0a490a9c99e2c9d5c33103ed070a616c3b88208b6e652289345a0c0b50fe48d95b0d3e0918feb7c7ebd1d8172fda8914631593a82702419a7a592ceeb26bdce9a1dbc21e63954845a617b3502cebd833d26989c230378a0c4e6ad29600f8228e05d53fbc414ec66636a36b3649fc131920ee5ad930cd0866e93a829c8d3ac7476a3dcab44f40e9cf1df38f252fb32f9bfbe78b789f2104d99190f28de5bd9129f322996f823becf90cf9e0f023cfc352198e44862b05ca9d8f7238c4234dcd8dc414c4d21a404c83d60204fb5601235e9d584c87fa4c68cbfd2041b172365a42989337402be68215064f85b0bd0071eda2ab6dac3753d8ea06a72ea7ad8a54fe982e413bd0201e87fefabe461fbedbeb6432ab755338f3cfd37d28750055ea640725fda3bae980a834604ed145d6b4e5b63d99c1c6b34de0880034c227ce464b05a1c44151ea97617e3bb6f1153b210c96a598fff8fba1fe2aaab0c97163c266b86e2afd14ced11e9d1afc6716e490a80cdec085bda03394acec554626e189c08761903561b021455a813083bd75ec18c368f0942548a8eaed97948841ee123e20d130c44196be749880219597f843d31682b6f89414405acb4abd385dffa251c413035e713051ee62174844273cac31aac4981a3b76fed0c99c661c7f33fb89d1daea387288bb75f4125c243ca03f5a7a0a9e70e912072f0448e97e5f4daac63e984a690f79f4245a16bf0a7a5fbe16236e3568ee6afb3637679451083d3da4cd5dc690d272ac9a0501e1df2aa787eb9643d052d81c658c9a3196bebd7177f549d5b9dc2213f0d2f6748119f945b49ca7f97c5c9a4fbbb5e818c5738e34430ef68cc1bb47436448d9519e1aed3523ce99e1a2120fde61a0063ecb1681c6ef2f6a816d4dc28f54a85a408cb87ce3b93b0989b078a00d5aff7c4845375545377399f22f95b3ea5511300e3a44deaa17aad751267c7f5b96ec661869d4cb0011d71133cb47074bde6803bfbf85f379d6634b4a4cea318ae11d19af9e918df5d7d3cbc0d899c4f1f905d1f00fe697d26ee71d9934d0c482f488634c12bb5834432d4bdf17d3526360244eca1e75c4681dbf405c8669a209424712b2af4f059344f717ada4f9582fa8c3b935c0ad4aeb70956d3d9480f35f6ffc2425f5ef0fe8c2dc324a658761883d74664dff514410450de6a06b34dfb8abb4844e86ddda99cc4854c35b0780774f9ef17451d1103754355c1ebb3966187b509b71767e8ebb33ea006cb4d3514487ba89b9c22633503a4db57df9895209d3db25f4208f50ca724911658a21d4568d4fdfff0223e0b5504272b9e6bfe2f23343ee8af2a74a43c0b6b93d862b8023106a3e2e523cc5979d5c0aba3b40d0605439a7c5a4d3ed39c3e54568fb3d31430e51a430ef41c20c1bf4555bb4090dfd1aa62561155fde2fe927eb72450ece3c61375912c3af25d11f751347df2125d5565ef036cb1ef9a744169b74cde2e4359ad3ab47434954a7c30b0cf25e6dfc866baaa4cb455f8e231dcf484d44ed821e3b372d33e9f864396a52c3601301d7d8f149235cb8e824702186b27a1e532a950637d6e01e583fc0b17637e4e4b38987c5dba2be4e75dc9aea5ac39781c244ee63de800414559450df592f209eddf32cf1dde85c503704bbc071b084bd214c7baf5826441fa254bd3619d6b4e6bb56c384a5fb70d4921038614fd33191640731728a74b6c40de020eac0e7d0fbf4501216b27b5fcdef6ec6a0a663d79799011bf59e82ff982a10e1675e48fc26228037c6ee469dde8dc2b32523047678c7bb0e1bb61470ca488c64048814b951460bff50463678bf929fe9770614c50ecd4f8a92d9fec968026ec24d8edd2e5bf74b416c67f249e6aa00f843b5ed008068cf14e5d1c8bf54cded3fbb805767e900eadbeda1da23611c81b5b7d90fa7bffd329e8165221778bc33297f6033ea41d3adbbd9fef942b1aae4702b0a1a0f76061a6ced92d8f25df1c15c89f67ac1601e72a38857fb20c37646956ac6677013c4136e5b128a354fb8a8d1fe4868d3f5d6062a9dc5df9d40fa39c00bc8c0930e1010e29e116d61b9d5a723ba0563660e00584de65095f18eaa394ad829c63fa240e1fea4798c336502772b8b99895bf0b097b11f88751ec5985224c576a27506ec6a96e49ba61ee21c625dc7ce33deecf177c0d67a726ca4188492a7e98ad1978e0e0f5ad9f7d6c08863955c40c3e93618f0654334de8109ae7ba23498c87e8829df2bbd59a2481895c0f5831b4d298ad4d2829831386c38d89579d408ce71aa8b838676c4875815d3b78a42e4c65ef915e72e8e291cbd5ef65d1b814ad8f574e3546dc9ab90a4030b585aeb8c436876b966e82c9ba0d73e1ccb45c8d875235cbeeedf4a6c7153404419ed83130fd9dc50946fc90f07418be0aee1b182ed57cd0eca3b4f473f0338df5c35e0be80bc49a9856ab206a38c6e221e329d98329a6cb0f4e64a23b0f904a72b24c9410bbdac7ae58c7956ef0951ed66600842c93d3648b880168f6a392ad33f2e830934bfa00e94172ad1f58621ece710937b4a83a131d4625cdd1976ca8f784664d0ec0b27f79fdb7000086b3227b14744fa449445a5c4f39942331311b74785e621c859f8109e31b518ef5260ea265d38422f0645126365a66f47f167c497c4b515b51e212bd44111ba2c332b0a01ce9e7ad18a72a3c73a1c3edd30c481a9dc330e9876e30b0ed419f6dd8214c600ef4522b6019cfb2f7a7d0a2ad673d3034f2114691140cf8cdb36c1037d454d2eaf562efef18fb25904ab91f7ba24ac3c9dcf115a410f62a4f17e834a0d3475bb098c642f49faba930f846bdcae3281528ea3f9c7d6a38edc7833835a0c3584276099bc811706ae6f26eb00b65f296294395723feee7e3d7ef5555cd4e2192e537da8f438d9e1b8d73904c904331da1871f04b995ed57746c0227e564a9233707b3650bbc232b831ff14acb515f4a8344cd48abd87ed2a740633dd474cf1aa26777c587ec813e490f4142fee26b2368186334e331de3b82c35085b24d31e5c51f970623b9ae91ca8541a928d62fcde38903bf5bfea6e7bf8fa4a8ec2f184f699a4b413081a4f8ec214fe69133fe265e643d6d07f1b684805d1d49210d742788848d96b4e3da0a8a5b0d55f96017180f96617af54df2a9c0e30bea63fe0bef953483175993d578ef6f0c54687742b249e25631ad67a307e1cd425a22cb5408ea72d286a705b2b9fa08b36aa409a51555e031791acbc6134f3c08e06ea74e70a5cb5d47f64a07073407771e182792a17eeac25d7de4ba0916b536d41fc9d888a596d617320b2368318a3d69037864d4d1c3867b8da5c9b435da1dda1b0aac539618b8d81105784a11cc69bb4cc34e0e553cb381313ee0189e97411cf182999ed041e0fdcf35a9606c77a4345c72fd500706bc45467c9c10911ab0beb063fb020ea632d2039c638bf180f7a3788c4011299d7f6bb12f02fb850045b20ad8e60a9f0d5ca5a48d95b358598b506a49125bd07d969414a5a0aa52f675994d48688588ac498e7f04534c96032aaa541503aa5d47a980844e269433987dfd031804b9ef5c223cefab1ff4017b82b64b42164e9928a6af8e1aa2535c51b876cbb6840dba2d6f8c22e01e821ad3b8c4e3d5b2982482e485d131b26df2ee67e2004c06134e6687acdfd4041bd181301333f5dfec4f55ccfcdc3cbf4af05d60ab564805130e5efba0c5c8451971b31d39f66030c1c7962a8f9b07494e8476b61e9054b73484057e1a6ea535830bf54429a5055b46ed8b5745285aa4fc400bc17d626a46e7d33f1035104b8865b9c5cdedd5f3c2642b476a0eb491a946e2035650492a9ff02fc871bceb620886daa1222aa948e7a8de66e28563feae31863166e0c33e6d19d5e8747f2080d6482b61a3cb0377c8ef1ba1b0692dbb6d9c9102ccb3017647f126fead8755d6afd00183fa6e55b392a819a8f6a9c831651ec12988a043be46707a84b78f92072a82240f9dfc8eda434910c035109e05cc27a093e80c48108af236617e48e0c9ee2125874d43d36651cdbcb97c1a4666f04cd8469cbc9fc7a0d31c8bcaf03e2c8f38c603ae58beb18d8a7003834c5fed31e76c70de8141158ece895968e0b5aa3f8055c412fca42087b0eac8d17182bd8e714beb71632385e8050f4d18b31df9f1339d962f3c51653fb82e81cffb29561da3ea1a848724b75ccbb52ea03eeca5e3c166bd04aeef155d8f663722fb079586c75836b9df7dc79757f82dcd557caeb9ef8d1cbd904ca58c6248cedcef3bbbbf29e1d4dfa5146d4c9bdf95b6e546d3043b66507839cb50b07ae66df97027740ab070fcda3015ee91beab8464a85564f2addeba0d5a6cdfb44a1eec46eff3c1b562c44b6ee30183925fce5aa5f0bf69fc372f63e5584d895d7fef3e9af14b2882d940d90fe35f193a201f3c601d8ff73d533bb155948cbbde86554e389d857f345c9d56ba1827a8af9a560a977640fff78fc8d4681d222302d0a40a3406911983605a255a0b4084c9b02d12a505a04a6cd1418a06dd58c48d5103b60fb07eae5ad069fdabfd6839eeabfd5c14ef7b7d5e053fbd77ad053fd9756ef60a7f603debdfe541a92cbe7a4bc9c76196e524710b292b63a9c830efe4cf60453ddf3c6146019a7d1ffab8537481316d7c13c4dd854644e6747d7d4ed39afe0ba353f1148c234b86e051bb17bf2f09bb770f5eb97e05485483a3b61f3374be87c507759461f0fd3dd9ea24eac4f18295862bad948ccb6aa7b5203bac9ec5775c35a73e3d65d9a5249f46aafa3756c115904a296f924c084a56ee8f78728c6ff5af71d55ab56ebcf277c0505d41cf670519a40426ebc5c0219c158a2ed8aba533c976371e534efc417b55d646532acb50e1e45a6beea232b5c8d467cb229c082839f41cbcca5ae872c052d9cd170f793f5bcacf97d49222dd8e5d0eeb3a5714091bf94d866af2f1766dc40de803c66aa198d6161893d691345ba9b8e06b86eab80ffe6198005a720b35416416a46c3c125cea8f87641ff0c4df8f085b3a9e3bec34d3834cc82bb66347cb765a8184f8439c010cec3f80b19425e66eac25cc1fa5e239c9fa1d992a7a1fa8fe65ee1611a00de10dab228839c746db558d6984066349a0f013e5e0a076843f7f0ee5f4899d12888869a3cd8a352337fc51e8956339aa5d24e412fbe1c0961cdbbc6b69cae8a2c2ec134e7d255c33abd01df53c83ee18d0c3a26d38c4302522dde4afd3db825464633c25449b864e2aea0d117e528cfc1ebfe7c8aeaab57f6d0a0762a773e8ba5aa255c4dd44343b95f4424144573354fcc4904b7ace4310b385ad7f229cfd9943282916dfae8c848784680cec5f085409dac8285c12027061cb289833890831c9ec31cec409303ee5594ac1850d500308a9554586e6485a7b6196afd4365e223abc83236156f892f1855cc2ead6282379543791f6793d291036028830cc1abdf851b1a7285791b4714a8b4f18eb6a51661dcf4130115ed8a3b7ca884ac02dfcf0d18d6721d42a0ad8d3fb689b2ae02e0b6bd3bdbce727e51b0dee61eda75998e2060d5ee6fd3be8497db6f55bf3a939122c619fe16cc94f2a14767d61e6bc2cdfb79dd5aba1581ea62c352b4bae8de90c36be90a16e33198caa0d0024bf482ac5252d39d3aa33bcdf7ea2e7a58042c6dd8acac3c1671914440a33db5309f053114f06bcc8692aea802397be7cf79043e346ff36917fcc5d1676dc1440b320ab2823a7fdb8af7ad993c7316d2149c7900b4377f09e8734e2ca04d30a5518eb9f6f219a56b421e3202a683d41be48510ad104797341683b420745cd4dfb1d59c8f3a995a872f6418a6a742cf1f0ee7897de661a19394c4c5836c8e07e4cc8af387bb14818389129f9aa63c2078456f219c732e34f23ec458d939f39f5fcf641d28dc5d25a9a78b42ccf1277ddda99f04d574c352d5ddd0391e6320293ba963bfc8f39aa0098e2011bca75542e34e733b98e282116c88a031aa58d480ea2a4a797e1869b6ec59c37d82ba33a0928bce2ce5d616b8c85fc11ea3cb18c45a6906b42f66c39b3a4de65564e312d990f7ddea484ea29ffdc1a727ac94f4b613f60773725a0a301372cf44678ef79c990a4b892a773d0f91ae33f9848f756fbc786ca48ea409b9b39370fccacf02392baf108171810ea6f29af1593c577470bd06eac13959efbcd58a746385a2b9a4a028894aff89107d36c84e473b15f0499068456debc4e84c3addb7a20e5cfdc31947a37d9981c3d2542560eb26f31144f6a28909cec6908e1fde488900fd5444585ab7461c235a015c864c4a033f274992202cca4292bb0e90929b73b824175dd007ea611104b74b7aaebeb93848c6c3f82a1ebf8cbe1abd040789b43570f88b34a5cc66697601a088931fcbcc38718c043b6dc99f9dc5c436776f5a59c480710fcc18e38f1ec39a539adcdd4526f4a4bd7baf52b02fa7cc1f49411e92621226271d19e6c1403126f2d80778f211b0fdb25cd6b8106153b2cf6d385ee51b66f7ceb13656c77acadcd343ba1b251c0724a4d052d2d200cb0f02365511eaf4c26fab21f0366fa939d7a70484b2782d77a3e6182630be3c82dd3f3404a2bce723f7456332b7dd77d89313d5b7939f0756a279a26394d1e0c8882cc482f8ba84cd48641af32deeb1325c3c93693558807075b9155819df173136c8c6db57a867a0130969da7bafde5bf0bab3493588ebda927f7b3d0b3fe490bdecc5f7b30a45408d0cb678c86e0a7daf04bd1c0cec0629b98ae1f5205f5a1f4c84e9078eec32bf8d95e083d03a4c1c93c2acb61425cb887fe6a14d952fb0de2d0fa1f14cb250d188da26a5fc668d8a72f4de9d236152f253dfeb6882b4b1618d628522b22b83efc35edbe7460a917117e4d4579384e24b0d4e93721429cecc27f3379b56f43485d8966f8abd9e04bacfe060543b6da6f4bab1cbc922e02f203bf7ec4ae43f0965ed1813a840832210bae96f686e6421fa83c16b218f2189da145266b36d84b58dea1769a7146bf7b33e84cf0278feb53743e0d6e9a688f27f0b8ab5ffe96269d8635affe2e4a2cbae9336b0fc515e7fbf1919a09fbc0ed0ddc9a0347c78d0a315da8ec8ebd089cdd6192f2b2ed8f54eb9dd0823fb0aab64891e0762a0cf48f59001e3fa75a3d250485bab5cbcb645aef14de32bb287123e87287834c5dd787bb1ab4111c2b1257df53bbdbc41798a0591f93a3caddf544d4f82dd92e7e4eccf468a0d7a245c26f180286d9663af7d30e4300aaaff050114628e5466a045da3cc2af3b836775507e35588371e788d296fcf2a1086996408e2c88bc4dfe2d37fbfb69ee2231ebaae55638eb6ddcedc3d24d869f2e6929dc6c85f56a225143c60d1a85185324cec5073c9690bc8f7bb7b46424a202a798207278a3d7b05ba8d18961d213689d7836e837a1dba42208abb70530bbb795f22765a3f43c2a480f5e7457b9002592b0595270abd73e059a288852f0a5203483306d13ddc2c1827381e9559f1d28a806228ff8f0773403fa6d521460d7aa9d85e5b0dbd1e3246377bb763a874fb455fa2ff1ba9894974f0276b162a5dee1c926f6e0866919fd7ee89bc318dd61b24584f0d1119cd3d6ae6d37e468a50d791b5717e5a229b7643b9c2b9dc98cef6321aaa5043158f52909376ca689f040d8ab81d8f3a0d6fe3849bd3c28bb86c3dcee38316fe6fbe9cead90afc74c1ae9a130fc636d5970e30a4d4b660e1d735132b18b7e8a7c95bde3aec210ab0c6312c4515499b2fa089c9f0ca2d85c2d77e95ef7d9e7c16a25846f02809977641b0fcf12f6d86d2ec28a7c018b4bbe03afcee1e2a80e3de1791a01c3080ee90760482654e23d9ffed0033d6a220d7880b5005c90faeb62c973469f1f7ef461f747acf3cae992b754921bf6e546effeadca0aafb22d1cdefeb01eeead0740e474bee26939326a408935719f9063690c03c63f3a12853487b86c419c9031f358217e13e0af01859eac93f882b71fbc893fdbe147269a9699b2f655c1a541475aea68cd6828771ca141f8bcea92893638f5c650d30c2b06e5379dde510839a715a558783509b2c029f90bd077198adc33080ef03a577ea6ee426736d0b08c09819b5c043e40d2305ce5c9c6368a90b08bbb660d5f25081695c31ea4883cff5bfd892a68225b0dd583b95d92fb8a54c2e0d6c35a57cb0f9338b8667e9014e9d069ee1c58a5b5b2b8471c583348ff985d69ab19b8d374eec02c5c08e64e651a3e819adbab98f004fedadaabdc3a06b8726e6f339d281fdc1dc68e4248b9aaf278320c3e6d6b9893cc1273a1af87485723c627624338e4052269d29432702f98e1c11ef19681158bf296ae6b5d163d2919ce6590961063b36d4b04ca238181154b767c91943f2a3893511a2244d5f3b7083928c9ac995ce22c21d6fd89059525d5a98771541d9d5825d389c38ec759f611f46940417d77af4c97cb5a7555d9ca9069e4a29e8899d31911838cfef603c6a42e6655188c404025343ed17e097dcf63b0ec9eb068cb2bebebafe9abc14efcdc385e0e540a13539ac4637041b044a035f26c12c093060f143eea032c52b3921e0937893810feb7522d757475007fe05baae2fcceb29cd436af3159ac1e8d579848680b2c2b6384a207083bfef6d1f4eab510f98b6b204d93c66e224ffb7328b48ba19dd94d93f7403ac7e16145e2181851b0532ae901f3d7c5cec19c27d804c35140713968dd4a31f7086e0a4f626bf7346bd01b608e7a942b7303f4d22006e456e37eb24720525cee70fbc3251e9eeb62dcdc15f16c65b683f03cdfa1f78c274cca73069808578724e34e063dc1e6a29bbd0b38a7658e46e9cb74e01ec836aef121a624c36fdef4eafda4c6444ac2d327b57481f38d5feff5066edf69e3891e85a6e57a8879e50db8ba88e80a6f43b62c08da564696ba30e4045683bd78642b29129b7e01bf24143f13d9728848f6947af7220c1d59c7665d8f5c754bdf2c687702da3a4893e8aa43fca3a490a6a85bb124599df75e992b636a57a67eda11e9fb4def6c1a293be3a9b048d6367c0c80fc264404ccb7fd8484870fb823305a5d4ecca0ac5b7d4c227aeb5d74765dff6a93c9775e43064d623958ca9b9443e77787e06284e2b3f699ff71aac7ef9875ac06e7a2552c0cbda2a54d415948824b04afc23cd267951a69925a6e372cad3f714e25d9b3e7025991ef24cdc8c1af9941ebf9ea2e626414607c796141498c3a41ba764fc99d23435557044c03c014606cfba55abd2c3080ef95ff5d05d12fa505b015a287f7b0b5ef04677ccad90532137a533b880a313fe058ea93036b0211224a0feb23d73d35244b72da9b212a219139b9a6bff776b9107be96b1f914906273e7b07f07abb7185028dd1329358d781acef92994ba220878bad9a58e00acb404a5d7edd12b3c6088a305dba42c9b00f6f2b27bfced10184c1268a9b8a00231cc2058ab3b74cef76ee63112f9b48a8f00e9a09a9744fe3866a2de774bb80afd90b23a9d2b216fe7e73880fd915a1b36fa7af6417794995c3068fc8deec741c617a5038e240634461e40d3ba410af7eda16750373b7c38e16d7255ed383467d0fd5fc6dcd0c3a15c869a5a5d96c5e9df405c19b6bcdc963f94c5d7132f6f70aa923e1463cf9c65610cedce8040c57f329a46eb5e91b1b1818ec241afdd6f9945619ec764e583e3998420ca05b74425d4ee639c1ce696e9b1f6f3a85fbdf92a2bcd6911bfc6d3951e13c31a67946331e42811ae4d0d3c4100f819782864d1e07444be5cb1652aa4c7a50ec56d19723cf96eeecb13ecaeae3e30fafe98c9f221b71cb56d48bd62fc67421090cafcb8176980bc8812f8bad887a66ddfe0fb33316090cc2353bb0f9382e5c225107606462ff54d213d7633b222f8e1a8eda2d9bceac899a2cde00c09f515cc83e7f0d9d9b3373a693e845a0eef5eb9cace0037b59a05d92623b30c7d0ad05bcd04e96f15c667b3b2601d6a7524810bf6f5ddb7ae5a786c38954ee6321cc745ee5d0129e62419beaa82b3ea429770688ba071950d765c1cbca0a433682c0b9f0eb68ea6bf4dc66e6711fe84c4d55aae4c1afcdf3a87f9440e915e93f5f11248b0b65a96859fa6dcc338e40e56ddbce3f2afa8c9a405500f62c225d0896b33b5f65a11693e72110821d55542fd85db6601ec26ec188e1ef34e66edf0b62c948ea6cd62177fcf3d324e414d4f04a13ce2caf1b1ab98d82ca565ff5bf81403ea01565c214cb7ac78a902544dde490066e8d358b4f19d8929c1c29b6aae46e4167bac2f0df3d94c9569eac52ec1a037c6fa21744e8eb524652a8606ed2fc6f3f412c518a51437e3c99cd23e2cdab2cbb712ff6b4b7edb32b56d7186fdc512ce991f1e62052063eafc7c357b61efc4b1eeb66e399f1c6050568b7818a2976b4ed3b72cb028134c64b72d727ba658bc2a06234ef0ec467c75ce860aaa403a4a6c3dec5459b9fdb98bc7285e0adbc9ca1a78ee4353504f489ca8e8e10d64cbda324313976c0a722c68875621c6c6bc564ff641148afa5124697bd79e4903181717605f64c7494c0b3b0b09425c5ad7429a2d619f13e126e6871075f38e4d9e1a304c1f2e85615f1d95e45595dc92ae21151f277acd633ab6b7e26d45b62d565f1db29851b9ba125453ade0355112b404dae7592c8e06a3481fb120c898031942eea6806273941425048a6998391eacc7f855273ebcd7c7004e1bc1ba5f62600e424ca477223fc5615f1145753f87efe3e79f1c44e492692e90b865c1fdf895ae26bf9da36bcb58a6d55114f7295fa70ba18455839f6c74ea96ac8d1eb76c2ffa2d952fffdc1e9268d4189f39b1470becc349853302fdda49728a22a622bb94a37be3b362cc3dd0d3f6e9c454b30a5a112849768567460609734a62fc4f00756ea905d76d1c00a80e5b1bb7a85f492381b4d6d3fc7158e250a09432d2c74e60cbd0c597ada5eb783c35bec1ea47d4606133d51833281ff542dd555d6bedbdbc43c4a307c1f0b1fc43dfd3926a2ae2d0bfba00c2edf857f1289836679268c7d17fc80ee8cc8a1173502c823788255e956a1c440bd0b8fa38abd3bc3841fa940ddc67b3936a348f16153207b1617a1cf2b768a1a276002d1bb960690c4aed914a7de260c9101201aa2cf32a8c702d8863f7f9797e469b3e70bcb925d72a10e6aa66102a064bf873011a57e35ecef96641131bbbd72f7de0ca80481338b2351780426aa1e80935147d85c4e347649e7b6aecc1d4d21aa62adb4eaeaac8879b432445109656b4d846212235d6bb8a39881bada511ae2b223228570207bb6cd7105c16dcb3bc5dbf3952ddc73501f67756abcb320fdbad11112c543d81adc36903ae4f6c9220ed53c78e4d149c265069cf68854a2314a60c06bd9f6846fa0277d77587120525e885f2979393f315e9a4cae402904fc93816016f14c276509a9494b43c6862d9083c2b45c5d8b1629464280527e0e2777ae09af368cc7320bb44847e07c6063eb612fc32226ec87d511c950a0adbd3e53bac7088e561f1a8f0ccabea2a261115b70afea027c3bbaeb9e23ad143b1a02b343c69d4d7217dee1bc35bca6edd3ca19cc828ff895ba68aee5c60e8d4b46cd8873f0a941c8671ecef72e70244df6cab5d534a8f2b0e3f2a85ad0326c552b2f69d27833c07ac05eeb7d18fc1ff20098522b45b21327e45f1081aa28eba6b062a9984252dd38e024e3528ba65088c6bf63c8fcd4b9ce9d7331a5cf881cb36722b189792196b16bc18f0905de4fcadfa356d8965b587385b9a95ef18e9584139261ed8246505e249e275e83964d6bb47b559e83566157cc261b71baf1626d4592c44c023eceb504287604428a3b4f2ed0b43abf27bf95ba909d28210772a263dabeaf135ba1d888af4cd70160853ec0fed236e9bd3d1aa0f5e9439a957a6c2326a2cc045afd40dde0bb275f3e72c11f04436ebf0a3d3c52e02018772243239b4dcec346ee3c21b685f7e7627130190bbee0883f111786b18f9457aeb5a3726ffe5c5108fcacea81e6f7862f7371bc07da83960222075c470a13d8a2a7674824ac8e80c8f541e266cdc691a32fc90519123ff3c49d5c66f97313331d47a5328fddfa09602509863444ccc897cfb9ce3b4e90aea7545475eff3d2b11e9665fbbac6a1dd68b94b9dc5fc59cfbe8210d4cc9f7d8040bc29b69c21963927e01c0756217e5a1a4eb313a612e34d7d5a69f281eb1047babea5d13a6e092c082791cdd39565e875c37cf95751e91b879b6be7b036a9291595b4d34726075f8f60edb7ba00eeb9c12da8620bbd3134dcec23b75c4407b28674258e3e0c9d4122eb62f38f692566d1698cfe26045c4c1f203ff008d47eb7618a155a04684e428e091b346be101120de4a7415b3478d32e6509da11a7b4865e741ae2bb17a68923d48658558be86c3069659f19dd89ca9f07e8b10ff38611d84a21d896056d4f7b803ebefe8e1acf8ab1c265bf95ffda69b2cdb2bc0f79570e3568cb8e689f10331dfc65f80b7698dd4b53534b65bf90beb56a61c2db6b949b7a8d15f5596cd859029507372af86a2e1e47891eca92040f0b09b66e392ba40d0225120139de519c9c3a67d20cdf37578848da37ff11217fd09235d3924898febb9b19088d6a92ec9dd91193624a229e84d0deea31f785d11328216674950eb20d3abb85a93d03a3fe31378984a93542ea0e6193f2ce61c9f47ffbc7e920642b644aa8e895120133082b6001420894e31fbb7e7b4df4e2ef716d5a1c6cc7d76cabd7bdc41528105553d5cd0030cce23b8ec48413d137fe4194f0456d173477dc52c04b67cbbe883077a941f45481dc3e8bfd1b46096a374810e662c1f653a82ce7307ff6dd403bad546e9f8dcbb395a83cc730293c47e9344b57cd0d883903859b084189bd9730c30c2ea099f92088f1d970e6e76029cff2b90a29c72e7a62fdb637a53d56928797a026d4d457b47a56d1ca5f10b2781d312f4ae423c3c049d5045202f94c869b8bcb856aa552a27300e7620270ed7b4a33e986f5c197e2972c3a99805ae8dd2b8c4ee8f0be833504f5bfb4e1713c93c68b33c198090da93d26868b1d8ab0ec2bd06b6551ded6625a441f735492ddd30142a58aaee55f8f750ffd150795afc7626fdc7a2006a28321be4dde644ac3c650ca6867debed146d6618c65872ddbbd49edfa1aa3439920451f28e26da147013dbcf4018a5ef1465c404cca4d1bbaba2fc55db04ecd56cdd64ea2a31d81b4352d2b4bd52dd5dbf6a572d5079168fdec08eb9742bfe2d331e4a31c03f61d99279a41e3c2fb3ae2b558780fe0b5b45e6e364b1d24b906cc8d280234b196483689eceedeb40335041e045f04f962fdc272bef09bb677e95dd65ed05f2f68fad52ffca69f745ea7f3ab574f04e91efda0f8923fc29e9d47fffa5f6752ad7ecdb9f1f0a217397e2ffb5ab5e74515eba0b8b10e8a3bd3b556afd73d9f9ac7f4f7a90effe93ffd88df985ff5f4527f7af99d299d6f9ddad681e7f8fa306d39328ce936d56ece29edd39cdfe8a3baeac3dd74ed5fbf26fdb2dcce3ab81d493d512f28badc58bfe675aa03bb172479ec6f4fdf91f88dfbd48349ec4df780c94172fb7b4172e3a79f7d4e90dcf74d14fb289a73ce39e7083e748ab986a4ad6d7f7f23178eec0c368cb5a0b4632d18c5a6bc89b57065e3adb5d9ce82365291d3c0743275f1e3fbe079b1ca12632dcbb2ec658237cb34cdc5fbcbfdd2de74dd0b8a3caed65e94ee4134584a29a35743fb679e7ca965a6e57b41516e4f6a4beb90d0081bd8d43d11e884b3bdfa41f1a5f3236cd97972d38f4b7e83be9d6eb65781481ed9534fea6c9d36bb9241938df02419663b040382cbab8e7def87f5d7f99fe8d92410248dcf18b5e9ce73a1336d7afbd507cf0bede5d58ed6c158077dc9632ee94c4aefc557fb3a8fda7858171aa537bda3bd2c57ba48aefcbd171a45ff94e9ed37a7f96afa2e34d5f2573b52502e7fd3b61cd668638d564dca9752c79b4b1d50997aaeb34f4fef5dfde8ed482f823b7bcec982366eea6b481a20b4a9b1611b6fc7a568b448eae99e6b67996759bfe86b9fbd37b5c9c66f78be6d8ef65c352818179a192f7a74a83ae817d5ec67da677ab2c93a2f286ed73cf7edd707418aa4317772ce59ceeceb8f195b7befc70ccfb56d50dcda96ea565086ffdd3ad0fe95edc643dbda7b92a4e102c2f09f4e06469e3f66ecec51dabbd9d79f6cfc86a6bd206db689d67d3bb829a5d7eac9c60f50e9ca9f0a866e3401970bccbdae1d5c3bb8a4308942254c11528e8ec4a0d41045cac4e0823da318b9a2f67cc2d99188f8d837286ea06859754e444a296fd500063be2a8f46bfc2a9fca0c437e10fff82a12729452ce0f3265340245caf6320b59b6e398ff5cf69c176a2a41863625920f1822ca2a12f29d1007d28b7e10aa3580c17614d0ab4da6fbd794dd87f0830c41fd9c8c34fe94523e9c4186e25f7f68d2f79afe9afe9afe5ed35fd35f9389be35cdf970cb26fd204399571cfe52879c0c724a29a564e1db525ec9d930c6829088c3d011269ad0c046891a2bcae0a0638ea6dc232c4a2c3d2a81e9c884eca8487ef2e6f464496bb161ec488aac0817c39855124a29a54faaf03c0993e48912ad8615c40e1715141d26392b18d956201a82e5ca7a64c376b161ec688ca6c6370e4b1186d127e2d8f1902b2cbbd7ccf3e9269fba7ec55f49ad43fed096b1e3d1f1435b763c22962ff972c697dcf4988513374e927032705b9c187132847257e6e6a4034670b819b2f7315a714a6de6eb38e411a3a56df481941dc7705eb207d15c40fefce460c8d501b15543f6fee584c0105783af58ac2c73b25cae17ee7559b162c58a9519d7deb7f7decf4cf8b3b75d662d0b6734cb3217ac2e6a4df3efd32e03acfdb25f7138ddf535ae4c86b1457545b25a11b1cab12a6eb6dd30868548ec8a1b2c4a62d11527b6b7da7792d9327665cc9672d28f41727251646f4584e44c62578e368c59d1b213b061cc4a914de94522fbdbab5f7b41920691e74f7db9359942a50f7af8f01bbea915a23dffca101846d45fa880ed3d0b9ed58ae19ae1e54ded3d4d144a4ad9c258eb6cdd19dd9b8b2debbfb0197eb85d1cb39bcd6da5d6c5ad75fe84a6652e4d96cf4191e79bea8d2fa58d319b3d06f69ddbaf76fd7bba1d47b3dbfd58ed1aadd541b34e96c431f280e57346640f82f140d2c8d2d68772bea3a01d451f29d1077fccb19d30fc542a954aa572ce5ace39eb1b5ba9d752a9eef4a8d49f724efdc94fa7134cfd4ff4a71bd0dddeaa1deb53dd67edf564ed71fea8cee16b2fa772e7cdd8f971d4b40ec696f65be70dededd4f9a734d4ff440837aa0b8afba4fd8fc45a8da21d7f29c3f71c85ec81b63a726672148a30e27b8e7f63cb2f94a14d91a1c4493085b12a6fbf6218370f7e36d4417c4b20f3ed7710820e530840fccca6ff23e396efb966bdec177ba8049b3e103cb3a9b69e7f90a17dffdad71eef74f4753afad837d9b560b2edf3ec906fdfc5db1caba71d7c6e668f7adf78dc2cdbc6908e72ac5e76aaa7dde931dc80646c3c2c7ea9bd21bc7560bfc5dfaec5a7ba17fed4a33a0f3fea733a9ed5c3adf333ffec7856bff3709b5cbca9e3d981dfc5e377a179561a6868e73ccf8e9dcfbff3f9f3c3ed83218ee3c8d77fb175707fa7cb799d2ee7559db7fd7dae7ba51e6edf78d816dac37faf64bdee678deabcfbf5d4bd8636d6f6afe6019a943ece3a99b5c9ee07d59d7d08190f47b3a506c2c186f701435eb6d410c6b86fb708ccb77303e21b686e10caf04e335c7072dc3056a5cdf687ff801814beeca4969e100f10fcfd1323842d38a529e6d8c7bdd2faf051743b56cee66c713e647fcfe54a02fa7024b2e3e0156af66f189342654a99edda303625caaea2b40bb0610c2987ed464b2ea594527bdcd49e6bb713ee36b783c0dd31a08cf84116933110a31a1f92c68c30e04f78d1b4cabc528c35a6ec321218b2a287b5c7daf863441fd45356f449ddcff47334ae88ec41a50fa28f174437f73adcd3ef91b3f354fb9ccf79fa1ee8742bae7b21b2f6706f3cb457e9e81b5bd447a5b5d8e280c8706bdce77430b6763aed539d86d2fed469b7f3f0cbbf9af6f0d76cb58626274e22d9a3c961f6192074ff9126d36337c90e08c4dad465c0b5bd0ff6cbf42ffbd7a43dd746bdd3c83452ad54f71d97444675598338fe35bdf5561b636df22dce2a2b0210d142112bdf805c4a29a5942ecffca8917314a6841f357e08a23388ce68e68718638c510649332f448890208a419c38716af61e3e753d1b4aaeb318bbaed3ffd6f7e17f4d3fc2f6ce93bbe2bfd77dbeac2f03954e19bd8cf9f9f129b40141d0e533934215375c4260c21327d67cd9115dc4d8d0040b538448d3040d669e4822a5892e492c79b2fab22b9723d50a2ed50aae2627a48941cc902f0a11402c41e6c4c2ce6088d920021b94d850a4490fdf1a2744c22c817284e60c172cf286312e52708162ce3955297c8410f6009b13b8012651a0c9192523592cc645cb1625551325b88051156dd1b20569c992d816a2580d2aa83e30b73019a24a92b3616c8b0a5b4ed041091224b0d290a48508d511a844864d155ebaa4817d6668505aa22a12a3c1cdae1bc66860b3841115111511974b25c4758118222d463bdb30a6454c0c5cb28c5111c5a034244e143374c0fe5de1f75a5b9a30447dec84907a6818c7c79fe91ea7975ac83fea9c250cfbf67b62f34d5a3e8e3bafa3fa9d57bd10520fcefde7f7f09ff3fd33dd63b5d21f6821a41e3aef5ac8b5a375ecbb8612864a0b69a19730ec73dab4759a1642c2580b216599ee81fa9316f22209c33eeafd5d6b5a080409c37e7c8c75d4422e44c2b08f75517cd742510b091142eaa1bd6ba10b4818f6b517fafc108743342d1ee2b0390f71f8ffc8169d10520ffc395ac871e0477daa13417bd3e74e04a41e9a16c1a8274caedaaf4818f67b62598b2084240188839ab408f4b5f82e43920c4a9bbe04208efa2cab5f8e1a92d1b690451659b2d40d6359a8dc0d63598ef286b12c39449c6304ea3869a65b89688021bd81bbc9b2dceae3ad13d6885d907f941d100e6274089bf8f022eca316514a67d19c45d48be89c718e669b4d8db814e4d986c26254b2c45c4061c65c9a6c625998c4603832dbcc361c175beedbd33a4e2967ef695b732fb823b2163788cfada8e65bdb5e02f1373dd479beb3a7f6efd03675b0ccce3a48e514d6faf8fceea7fe95577f08c9044d787dc60db2ff2cebae84c13931df1d4acb159183eecc5e7e0cfff952fe4a88ec0fe97b575fe9fa66a0648528b5519337e0096df9208cc9be835cdbb5e3628bbe101a39ce29bb9e959aecbd522c83e18cac79e0eeeefe3344d6fe47ca1a5b834af7c61f0760edad56ff2daa3dd7a6d33ffea8b123e728cc375d3b132532e76db2b75ac5d56a75a592b15fe5cf8e7ead376eefecc3ad5aafbe5f535fad45232fbcd9f36b7dfb3aa00ddf5edfca7007fc9ca00d8736dcb32feb337fb61c403cceeca15f1fb7e8eeb3fe7670f7449fbab4e9f3ecb85f9f7b9af3b5f3ee7bf54ff676f755efddaf2f28d9f42d16381bffcdf9dbe5ef893e55bbbcf42fde34d6b6010dedd4f7449ff973fbd9f1e47c8b87dbc4bdaaf3ea4beef1739a27e77976b4d87ebb2f5f27f5391dd0d04e699dee762f94d69aaef5356823c8bdf75e7b41d8d7de0bec7bedfcfa38ab2f37a0baa3fefc0965d01c3aa00c7fb847f0212ede21775ecd94fd9c530b9a306972803dea70a297a3739aa63e9d5efbc9423ddc4e287aa2f444a10c7f8d4b833b3837dd80ee5bf939ea9cfa076b478f961334214f2ea178414d7e947d6f6e586acff680c9a8cf7ecb911f6ea8ec01dc281d2477c0add33a53bed6522967bc4328974adc7bef0dac1d2a3858c5129f0b26c40b4cac143b20718411951fe20c218109489c70302a5f0a1f54d2600e878701c06012ae0526f91229e8e15bc167016463c2978295f95a58a1c18a6fcbb0244ec4c467441361a019d81043d67c18c0aaa8014c083e2f015601ab32a508163d54f9818e2049122762be0ef6c577f8a00f6e94d8f00eac19c19700d8149f9aaf85cf00302fea81b9142b3e00c0e07c10cc093ea46f07a6037331f08a34377c26f8bc042c180c2207147c2a1cf151d887e48bf3050096826fcb9723c3b6602898c7de48017130c11c862c20141f38810f4a3817a883d11468c27c4b1f0d1730193e173e1f05080076f4f1a8606dca7c453cf15df1043a41840314cc7c59602cb80d513e349f9ff0a5f96e6c006301b033df99158c518106d5ca77e44c60f14125ba7c1a6602ffa10858950f0a11e753e34478f94e3011c0215c8a183e203e2f4200d80c2a3e27c10ff794430a2028c104244c8688c06889d10b58ecab71009891af003027de04de50e1130ee6c81b32396c71a288112adcf0396183c3b7822d7d12f686872f068c8a249660f9943e103ccc8f7cf1270a282e9ce952c607129ae43044911112300706c07c06272ecc498046f894232011eea6890f896fcc77418561984301e553c1e7818f0c105348141306b1f87a78ebd4566badb5d65a8b421a2373ce5c6d0adbda31dbda67d93ce743fd5f629d28e090c6c8131d3e04c050f091f9b2c800e62b04f1c12f9e9415418608082078d832460c3da1c8e7c4a3806daeec5891ed54f1a9d6a8d6a4a88871c7083d6a67a2c9d2f65c316012f4a909b12647a8090ebd5cf5eaa6b0e415fda73ae6d314b2bf4bf9f429109a1fa6abaea84c90a34f36c2dd1d42c881ed7bd61dbab8cae16974b56ae5f83c555fbd92aa20f21a3c800c2716405e45f7a803907d019895e1acb36a426400640a01ad4da826776f3090efc720c3798105b8271321b9063825464a29654b0545cfb35a3cd9e557e0172e62ec8b980867cfd5a2c8b552f7250c59348860cc406ad0697d8f524a554b59ca28bffaab962aadd47f683bca1a56ac9d18e16aa5d303abf20c5a9f4a9a6717828c71cbabb94c8494d9f936b344481e6dd53c32b6c8444893bec6306e40c3a7cec9d21b80216bfe7d95103995ed132151d6cd90b5da94b2628b7e4f6ca9a0c82bf72e04dff3e7947246942b12c140be4f3c966a2a2bd9bb9bcad885e09b3ed573ca2d1321650f95a04ffd4ccb4c1ddd1f81c09aea4b8e1f7fc85dd31bc6924ad8d0020ce14d7980c49bd882494ab66f55d2abbc64ef6bcc37db7dc6f62452469fecdd3da7841b160ca38fd4f1612c8968c33dbb21e915a8c2daa73ff4d5dda9b096d2b74ebf83b887aac71430ce709a60968990a8563df1454ae907f0fe0b970d635fa81860c3d80d7062c496fc566ca9b064af1583c2a8a3c4be28edd8c1d80d70a2dc242818c8b7cdd06220df354b55ec86a50d63494b7bc686b1a41836109ae10f9e990849c6bc38b1636019337c58b16722a4287f85612642827f82a92ae6c7a18d51a86ec37777d73e776c40f1e30e0becf9b2f3b48e2e6e3ab61c33c25039fa54181f46a923524a5f53678f61c8f21e7e972cefbfba60b157b1e55d6a297b31f2b264fb67a8638c72c6bc5c8979498a52a8a6c8974ba60d9168a8144db7753d0a15d10800400100c314000020100806c5228150281ccf15c63d14000a868c3c744c954723711cc6400cc3300c822000800000022008820008006114638a1f539420858f7061137e724068bd07c5b86e9365c4a555ba9ab9ae67da5e431047e159ec6631e7a173f8106088720f89e7256462a23adef307266d16531e5c7317b69eb49d4f6e8ff8cde30f2a983d6e6b6688d478306bc743a786e701ec87721e1440a815e2629a4746a7a2a967b6e1f6605910c77af778d32cb87b16eea420f650b60588f810a980288b8647d56efbc1e2cfd3e91c3f243dc4142f1e648712420f51c307712024e3593c3a870d42441066d1787cda2cd03def9d09c2164473212eb278f234b14330a8c422583ef5c7fe5adc70808c932287676eb3f0e01170c51f708eb888eb81a04181e042643f78f9c10781911052883d1ed02c883c4bed2e163d62ef6663f62cb65321e7a9bea5d0e5e1360e10ae3da860e151b6792584593a8f67370b78cfeaba8b1d8fcb964e88b2d27a44ea7ad63d6adb2c9e3c98dd7ce63c7668ba1e84426006b102b147a4ce67bdc7dd26e70f2a683debb41a88f043ac88787c9d4441f844f44ca259a879dcb834435cdcf048e554d4f62cdb2cbe7a047317461ea772762a7ade36f383cd87e63e5864f3946b2ab479786d20208810ad1fe536a53ca8d8f7c4dae6426cf1f006c1830805218648db834b0fd221f410b6219a037191dd53def5d4f6f06c0079b050ecc9c45da4f190cd41d1dc23d3701f92f560f1edc1968be2db33a641f460e1c4e3eed20462c5c653dddd16c2f1bf9e8f737c00bbe21e4c21e21f2c43a816420adc1e9dbb1e02130af180bbe33ffc0962459ea7136ef1681e6b9b6fe34e57e1530750569ec916f581fb60ad0f16df9e6039e4b87b543df050e25010b820b621be3f84efc3a81e2c19f1037df04b57c6fb7a9c7090b1990fb122c833de8611844fe61eb1370b674fb693e3211b4437047e20a680787cd72e02a1e702cfa9a3f90171105254f608a659c8f7ec9d5e0f413ea41cc48a82a7272e8a741ef79ac9436a47205a808c46611074b5784a6a49f980d1e09c3cf8c69cadc59ce1a0be6b0f4365360b8e475d8ef14f0fe006e2070b674fee9d4f268f60350b79cf8ed3f310fc87f2071442ddc0965a8a07e83d5a23cc94c1203f4c0ac34c971c04b286792ade2cc43d4a753e153d75df2cc034ba071f305d8043fcd3ea5972b358e961744c20b410c9057181e5599ef3d9f6f47617795051ec31b5497d105f4b00d956c251a3cfb119a3751f0521d941f9a86ca9f9a0c2dfd35d734224cd83142d1ea5358a0ff57fb081d0ed03bb077b85b8f8f784db6d438747830e0a318f6abb82070b394f129a0a688f159b37102bf8793cce9f432aa26295dd53d33cd6ee26c4ff2120502a8b39a3cf0a37c2830a791e481ba086272d84a6f3b00f0012cb5906987ec4c45e6a26d78b131436e0a6afbf1882b17a0dd48ad7eee1ca33e41942f11823dd507112a7aad26320ea708013946265e0edeef8ba9f1e30b5cf5344d6e17bbc66a2b5a598f985c7104015e4c9d58240beae743e8e227dc856fc09d6e4548e4ef7855f00aa1248b8ab1212d50a182bf8cb48b688b1963edc7c935dea6c8d39fec6be83a024e7538a8754cd82866759a727178f62ce27718faa73e1830a388f841b0a22b943d1dbc06323ff83d30f4e592f9fce4131c9b46c1c9ddcdf767c0b004ab7e93f84fbb9d0bde379721830a59da7bbce184efd17749e4f297fed4e0a2198ecdd1084faab4292ee33f123a1873ec23be2625b130d41c638d866019091ef7bcce487dcf7f901ced961cee82fef0351de98e27a9599258c42b3a8777c854acd267899fc039709107046bd8aab7262be74f4d1d42be38cd3d59ba8f61540914744c91c58030f407ae15875646df56291ff3c57ce6cbd9dfb01977403cb099291438736c61d3543a66a88a3507b9634be8fffe0ef701aae3b942ba6ccffbb0bdcdd71228d753e71188446c673e64299b8c624a9cad405e7c731ae49bdbca6ca058750eaad3317949432d51a41c98506d1f91a3baff9fcce47bcf010b1eb5e1cbce219b3f3da170e8639ed4e5b48c2bb0ec0e35b0035c6b0cb58d75e9281d246ce746d9504dcf028cbbe0383b24c0acdd6469b4ded7b3dac8d30f56f6f73503d56041958e210ca43defaf16366fb54bee507fae841ce1bdc3092a1b56bacebd2eff9740f4dc732a055f094ef1197f2c23a66d12ae89c2c00651d2a388b4bd183500704f0d11a19e284ae7905f558b1fe0fe5eae5441a8c4c557139c6e03014b74e2b252c92ad5d73a03b18f38f73a92517b6aa87742efd76699646703153fbdf844cac2022101bbacad63bbcfa0bc5a46c6b0850a53ac6b6aa4b9180c46f7d0951317c8fa21667c16a808c9b0e746b9c779bef8db548579c3892a44c60a0ac1de7f321dc6bf24c27d77cc775d5873812a0ccb101312a41bdd3c3d02e1f5acb29a10f3469edeb7ac332d6935c2a676e96ef2091360c8252245755ed54204b5b6775d149bb56f9f45cff5bdc1395cf8271682a79f60129014b994aa3a1e536c206f90075b4ea239414912069476fbe8e7a2eaea89940f44b600e5c14468225991ff7021c0d3762977ff37c11e506b09dc4b3e8cf31d324934b9c2f9ca8beb00d6f4b8b44f25f99e29bb7e5f580a3ee70e8a31663d4a110ff389e079d64f9f0b5862a14c2e6e497c42bebdfe8ff2c7330c3af61fd240910808ea07bf3023bd604b84ecd9d87f8902555c64c5f8af971a42b72d3503cb1e399a24cdb34cb2a1f5872986c2c936cd62d92efce2e082e111df371ed5199b076dbf9d02032f1fe4e86720b0ae31bc6866bfa8a571c21455c13731b8b4740c432cf55a2b17391986f09cebc5a5cb08298e6b9a4ed3eada4e562331b69dd05922c1aad0ccea1d50be17431409277b4de47f56468acd7b0ef0e17da6ec827ce9c2c14b6dfa5430a89391d9a050683cb64abea2d2ed898563c1cb72478c258f575768577fc4f868c6db18237b7c13baca926f5a5694c4aa272367eb5ee11ec99ca7a6ed0de312ba4fc21ddc941b07e90de2cfaf8566bd55a1e3715a2f921cebc2efc229a730850f858e79b424bc12ad968fe4d08d2601104cfd6f9828ca65fadd374af163aefe6aa8762751f60672e32296c2e149410fb258969c5a0798b71bc36915418c496fec044c188e7486924d9a0312a2bd66a331d48d8306ae8ee72c215364be625185089393a434dcdd23fd3ca613cbdc645dd32d7409aabce0d011c7191aa8b3c4803717dbd8d681085e4e481cb61025487957eb4361ffee98bce05a381f1adb022906764318c49099a78870a72c536be07677ddc6bd0a09d5f2fc0c38db46b16a0e221de584f3d71264d037310a7ea09425ac918299fc3a7b22cc0091d70539b62afa8b5e9bcd5e8fc50b6bc1821b443d1946b69c85d542aeb6dcb0fc22593ea5019c4ec0d098f9215967ddc6992e17f680bae536ec2d4d633a3f4c9a00d13a971b965533b74209172f4c8e8d5f53f167d5ec222500818f9a82792a9feb51de26a7a05200a842653151f4179349d9b59aa618b6aa50805114dfbf3efcc362e3ed95f785dd6bc2125ed51bd9f8275e5a7219de9df88733326fee47c1fac8e6b314d13e968da6182e27daf0d8ea0295c837a1b035ba3c92fd9d0b1c8a95f330ff5e760b8280daf5e2b83a6cf9eb6cc7e99b53e1c9ebffc0361e1035a0310f6f5d67e14f401e50ae971cfd48233260b5bddf56b00e86ac34151e0cf3be457bdb07a3d04fd5200437fb360f70c225ff7d3de43945d925cdb60dd8ed70cb266c3416c175c9c4267cde735ec11dc648e279b664cb8672cd7abdf6f5070a39a7994f37655fda583c660b8a1fde2928c7b054ff9335852721d63df21794318e86d02561bd091185438c0f832abc4534d07376dabb4f301031c9f2dd52ec92c79bf47e30158a8f17c516ef4235290e611d3abce3447eb73780d8a8c11c427e8c2c5bda466d6b65361cd2ea22c348f4037b84614becf76f42c1bf278c623bd765de486f0604adef822a13d5471ee900ea62c7b27d515a68af281612d8534941da195b65fe86641a8c7924cd3f657b87684183deb8ef364f7659d02d746d78605b5df358fe8ef68279e17fe50acb549fa273682d7af54d062bcf563ccc8484f4a49e912280c3c7fb47f43931541cc1acaa8fb92269a11f9958c22a119da71e3fb1d78d2564765fd124848e70bb06a5174c1939a23a349545bb9a0a0581e70d9227b173d14eec742fd9324442d431815d266a45b51c0ec39d1d48c38139abda9825ed6cf807c2ef789cbb04175163a91c951aefc86232048b3f9e4de45dd2b39b3ebd92b15be3598ff2ee7fa3ccf6cb75174ed171cc62139940318cfc49ba6fef319deee2e433b6b925c5f496bf638a418f3476490a9017161156156ca563ec83213ebf94e922908791241d6e84d42934a0cae598429304394c0307e731763f67e9321766b74dcffd573d599de541860772e65b42c16e84e2bd87682badf8ed18700a91876911dd0c88918815b689a16572e1d16c4034bc029f91985bd7f4dd0a3a3f6a14c3d9742b9116e14259e084064d83644af22d084e51a27d816f6a3dc32877ca655946a9edd4b14bdb876fafb93191f0abbba2f9e71a3ed86fb6000d9f17e95860929bdd3ded459307aedddb02619debcf1fbe8f3168b059b8d98b6020f1921fa31f5386380e5f6b0686b29c57c187e32adcded4cf5ee33f7e71290c1a6e7fe656f877977c3655560435a6d9f144853628dcef3edb1865d44fdb7f6fe430371bb225062d63f3adce005a6f407383f20661ca29f56b89838d56aa33b976c204539c6b9df205935e3a0b71aaec490da19bf855e796bdd4000857e0eec9b31e142e7f44eb0e91510e0dbb4925ad22dd9cafcf29c1253fc13ce36d38635b6a74b06684dbb6b70c3c971410c05fbb0467afa68ac797eef47e4d646fe447583cc7e1ae9a44505caa59300517146ad5461c874f6540e8882077e7d83c7f45d8f4da7b7c06f778b969233e62c96d7bb71e315e7963c0db710c30ebd515840f91419778c69528731cf65dc78f81d44ec4b2e38fcee968833123a1265f6684037b1524ea3247c68ed581d2a9ac165f8ea1bf31356c1c05356be8428acc948ddeef82a2f64f2fc17db7e16c34db0456aea01ac322313a22cf9807dd2d9655ce310691718e533e6a303b8eb9b60e3eac0c41bfaa8f24832a51f7da20a14b79f0b98b7a1a8a77765228031b8e71549e7ca6c17a9243707cfb43368ca9ec344d554b47010ed0b8aed3be1c66e09f0cd218207500265f9315c2bae8a62e2a0d08b5fc373df484e8b1ad6c5b8ac261040525dd750aa22be3e02934328beb4bb1cd8cb1e18efc24c139279b5ea942248e145d999e121afff6186e4aea4e1a67ce60ea18b879430a19401b2ccf21bf312a28b77f1fc17c5b040338f165205b786862411ecbb855f466ae5269d95ae2d998d4ea95371f30179f820b9f8d9e84a67979ae4837b140b2f5dda920b6727d2f31004eb662d5026fd265ee34d28b34062f11e4fcc87775815358ca26b28b5c8df561f0a9cfff89019bbf0d4d255e7612710e4c690b634f22d08328f6aaeaa56ae42f47dc674a8696cf01b747ecbc0ec672e0ae2d414607abb77967ee4904d7f6b9c8b3a7ed16cd8ff37f916c99a9a9d08994157d5548f9e9b261f93f499d316f661ec908989c05348499ebcf06184288430a0c3761dc6950cf3a2e1cb8f0db2fdab246699a597e837d5587acf6dc84a85ef6612157bcc12c3573a6d2253511314400f88856ddce7aa217104a0fbc5ef83333d1b46060bc53f5fe28120ff3b51f9efa400ad0253c67604671c52929212a088efb7f0359eb9de0e01ce3402244335c9d87886d964032529de57e50268c4f406aa39ec6ebf7f1ea165408ad4b6c33fc608d345221ad36655b723ec3581d4110fbb45312bcefbfcdeceb97515d2e3218f32630e8fd975d0f7d8650310405bef0da332cb93cc1b3cd9de99b491479662e54746c1bfede5a7d88ae809bca21397d6c825222d1e44c21c8ec0aec650c634bc2f0d8c1429c861e1230b9d704835f203584702c122611776ad0390f40b2d1458276c621242e785528930a508a3c2b91d2545d582d474f9be5ae060b7f855855935587f94390e1222968d05174a411178a25b5b2ab20ec01a922781a823f2bd0638bb8175edde472dbad2c076d94f2344c728d1afdf721c503cd45be07c7d4ee66992ce77b68cf19d11c0dfe8066a30571a51f054b31916f5bea6b0c5116105efc6e860371f55c3248d6c8293221e9394663d2b18afe0538767d9658f040bcdaec862e876a4da830d0e604f062952c53f8a05a751853c87f1a62f4415c10d2aab08a48cebd883abd6c543fa5b94298c38e19d059a261d7a1e47fa69b9d1760a5742e8799fe9f13e106eace4debb59424310066864eaaf495b97ef51003ca05c43f725b87522efe9626650e1816e773f0fbc43cab1c4916f6986322929203388069699af807dfe16a1dfda9babd7481a48477e9bb8699811d3a884455900bf18617625f39745b08870c0a5173a6d88099174f58c44f4a3d18b0a6b0722ed2f45ec1267d16ee7711da9e4af04ca66221c729ffd4ed3d7abcaba25c7a6bf4cd277789bb02a0c74ea83e1cef614ecce4a7820904512f10816ac2c27a178457b4b3f10b3e36406747039cb07765fd020ae495bf5504b0371588c5c7470805e107df25cecfac05f9cea71ebb8b454ccfa5b960ac8de3868b3a4b28b23f3476771c4c74b08f86c8d404dae01325470a13e4589d3875cc0d4b863076de4c88081b7a9c1ef812eb108cddf55056df1cdf43f99c93d52bd2497752a1af49ba939895dfff8f8f4267b69e26c51191bc4d66c651e53517610529ae257c4136feb531ee19d3db0daad5fb8b80d91905f177ce2025cc9c497cf36de36e7603e6ac8a882eda0bee283f802f971acdb0580f3bcb955b41c63945c7d7d099445acf1f02b3994c72a9dfac106d845c9ff1516878e7b761e92e73bac2975cecf9b93500d0e08db8c88f2089dea11cf131529e5aaab05ba5199551ecdbd407134f7a5315066ea26c9731d2e5b98493e400eb5218ecbaa453d29cfef8c730810fdb85600949779da0d7b812866bfc8110e1941c8163dbe54da7b70e5c144e4b61b1c3df0f5af35a42a776a7e5c56489b23d2656cd0b1b87bb61c6f057994d2fcc047aaa61afc9702d7213bc57a013cb3cc9476d026456425a17cbc7ba031b0d16463ae162842496949503b9c1aaee0df95af4f57566f22a8f4568b910b11b516fa042c88a26ed94437b3ea09750f6334c821380ae0368cd743113a0f7311e6278d7828f201b8a5fb772e565ba2aad6681bc1fc026d47562a6f1002cd3099e957c4356ccbc49a014859974b6de0c61a75522a6df747d9a96ec96d27b0f2c1028ed56e9a332ab513a447de48773c849906e145686558fa22958a6669c7b8831b574d486a1da2bbe7d40c484d3fd4052fdf33bd100f07b130530c3485c74f2a9c0c45e6bff2bab44affc795866d34090db58475464bd446afc5daa873c1220e56be438d30e8466ef5ea8e62a01505d847a22f7a386d1306348b6cc8c6ac5594107795ec80a2ff22f961a0db016b6cd6cc7a7ec2ec56172a16383eb78e52ba96a2956e4438d888fdccae1dede98280b0fbc8504dc11c05db38751195904f53ec4ab5748d5cc7a53be06ec1f6352011270a568d28d8708f03b2f913ebb1804407160ce6efb0ff1ed250c55f4dc64aa76dab4433c7de9c4f62df2ee6d8c7edea01129a4246666a2433250b60ec78ace3cfe4d7ce6b7e2fc7e4c6ab82f4bfe0da1369d4458b39ee3bfe47e655b86e9501c64e5219ee4bf037a0c6058c2c016dda66a41a4f16bdcc3fac0d02b77a43b373acd9b6c4989209ffe5ca38e49a8b521c7b6e67de6126c2a85e3d2fd2cc347e322e339460e8e26f7ffe6d0b960a4bee7748fac33a2f07f2afc72d2ead2bdb7a52866a09639fdd4d6faeb52fa7edb0d705f5a5a6135d367a3dab441f25062912629c4c6fd6ff076da99ca1962e086f264da024a5fa8f6db05712b314bc3546367fbcee3372b9671f3fba6791a71fbc9bb5ed33db147ab99157129753381814dde845eddc70e3349830c43dba8af5969a3520e644d3a002ae6a3ad4d2c46bcc18794d6710014216c498f0904856053197840970c3eae32dd9abbf4570c5589084599caf737b353938746d39534bcc3ae0ecd5abd868237e7e6bcc598c99f4606ce5141b5ce9b0a1326609142613ed424dfb6577b53ac84f0cdd04391b809574d118a1af532c02dc676797783f627297fb945d1e1dba56172aac196f8eab1533de350856060de9024b0dc14847c33c388b6034b8c11673c3e83058e3ff24f88b33ecdf759648d804576bdbcbe798c51d3f328efe21f982e08df1a1841e528b30de2c6c1f03d76615d7803ca3773420ef24f8af4432a4682567c14f33ec95076163be662b2d1cdac93e6f921ad250031e1164257d9bc3581b3661ff1a3cdd80c59ab9de9995876ed0e56d749cc8c2f4aa1b35db1579c6e66cd8b865fcfd1103f06e7a768fa0bc5f3688974a4cc0583f0ac7d655ccb6c5dbc0a7f5500c0e24ea2257378ae0164d243c9e95957b49652e25d0a91a576c89789ef2849788e0eb8671d34b3ee5e0daf0ea368dfee1f1ebe259553b19c2b5aca8de1a79d97212cf9c9c727071e0bdc551a2a28695a7b020f99e60d63407ea20e6a4d4d2c6b3e6e0603ffe630403bf680ac7709342a6e7bd887314214afd0d49476e6ee77ea5ff432d73392379fc0b9899b3e4f87ae54d41870e4638c5f0405da89964074f54a18d53417e9fcd77e29488f02ef21af4c97a3a498eb49b374ce70debbc16025353b4ab847f133fc3caec293daf0b8ccf270648303927de43e8fb522f6137d06db75b1a65061e14ebaebe9228b58ccac3efeee03e67f87f20560479e12b9de2938b2f0c2f11915559439057815ebabcfc7461b78c7dd01738512b0a3fb91ed6963cc32aa0e72c57642f9233b12ea760c843bd8c1450c47c2db0a6673d02ac0dd60ee08aadf510145287db4564f8c31bab73822a4e9613adfe17a0c6f9c585a1444367066771f2bfb06508bbc6fb039b2bbc5aae55782f466d70e2b574feddd91f43b0f811330b2062b9db98fa17398018ec721cd5ac009ad9eee5421fabdcfa9ac2d42f0a9f3cac14e41f8360fc84b51e97c126b239e22ef3b0e9bca7292e78a7f56513f5b79827c4978e338d53113b02c2486b953e2041fd781bc2f1a6956237076f45056248c868ce2df2bfc6fdfe4513e940bcd62ff73047f334570325088c4521d642adc5ede8a7239a51d2a30ba379b11fc858afb81a7889eb46b2bf4a918e264c639a9841269025d3efb80424ba764b41e1405b8588c64d1824fbff30a3b2ffad50800f8c2f69c8cb5823bbc6a5de65ff8133fd7c428b91e46bbe5f24bc1dd2b9ae1791aaf487f346fd41390ec323fd59222b65107d0674f0165f015df9c03fa2e57c85d7b8d55f90e5379b8c7010529ffae747fbfa082eb761baf97a2e2268aaf90c3424017c224837106b10e49110f2a5d2f99048994b64336bb2846af60e5c877f52a8d6e5ec95dc107ee2ba1a9296fefe95e8acf0b152d5a0b75c65d5564d0416b50741f0ec8051e508189983042c192bb4f6b50a7290787c09946cd91d538c3c0aa72b8dc37bf39e61cd2d002d4a831ac10b1d450016a7e61ecba16ad0d49afa71661cb9c726a85443eebb5026d951aa056193fe8ca0c27ad14c7dee50b55c14fe08984872157680875b318f2d33030290e3429d5950658242170651337d16e94b11fd348332ac2bc1d5a156dcdf9c091f86c1f483a34a47596f676f92b7a330df8f262380185e661e15bf6c255d46327ece19b54888b127ab71a837804a5b3deb3c6c7683790b565e5f7e33c5703b06904bcfef336dd49a6d8f663e9dc3bd6ebb689816d7187d6ad0df2a76df86bdc4e08dea2fa22607c16d1ad42ff9c1a8e54cb43e925c287c4b75c0c857fbf619f33318158ae0ec8c72680afc043eac86769304d1ce2227e9a7e093f3bc0b1fd12e7c3ab6c90d6473b37d2f87d62a10480754915bef7ebc1466f1031a2024a54f2dacabb34fb17e25ea5be741ec515afb1f1680b257aac75665765c58e04d9ebaedec83fc73c0c37f94899d3863b99d2e0d17a931d5e30d9117be7a943d711795208625b871013a9b3bfd098825dc08270626b03863e62f8afbd07715aa5e3661336d8afcb5a5d76df7746abe7564667885bcd9d406cd16b5a9d4959ada4d66a58bb03cf72acbdaa6be6ba6ebd3a67ec6758e5071d9e82c243a52a01eadc27ef616e0fb14b5a2ca9967e888c12a9649b4fbad378e1c34a6b2f224621beaa4e22dd28596ab2951dc69fac1fc7b1bf78117cce218265e202471e5eac66bcc9a4e179e9468d14eafc9476889298a6567e7ca9c5d5fc25113316a39f1e8bfcb8d7a7759a34e1d76294b12df3a2b53e567e0c6074756a595475ea5a0aa0370498096f8cdc1fa62c178990bfceea4f34b0f77a5c9d0b66ecbc551251d26980c23107d743ca11a1d0335dd1837e9aef858ff4fbf7b6cb11f3ed7ae502d264361639320cf24377002cb90de0d24235777572bae9682f5e554b8501234dd49308bad5dc8b844cb964dc016c9abee43bb41e9391d5a6982d44bbc18514f91d0fbbeb5a7fea52673e67611e3def88c4bda27752b27aee9c033dadc6404b2d3914bae4cf442ddb8c0ae35bb3206f49d48228b12f237505cb8119d3ee9f35f4d6005c636d953f7c203cdf2d033c36643db01e87c573a7c8e81a10e7a8766e738453907bf5f826c3f355a49884ce68102b3e6bab6a36c6bd47310e45300cf1689385cb4a46025ca25bff20c93c54cf36103935ac20335fbb5937cbc3073344078d38e5ac743a7e23cc44b36a9d5e3858935e088ddd52a3c7d7459f6d4562fb541bb3b7fe1d9c4be2a07e26cf505b011921c5cca74519ff033e9decf3df85d2ec518a654549776251c528db56f204125c76cb12c30f1e6046652b58b37f826495e7bed21d57504cccb43ef90fb2fe6c2c1823ae2ab06641107122fc3c60e11f8c90e43210c748bb2c1d5aae455e48607a2d72f10bf8be0bc1efad2c9ce63362ff1c948fa37d1d0212cb25bb1d14616317ecc4bc93a04bac6fb4c72b71a67949fda8804ca17363cc9f6bfbd033c4e86ff5a2526867e22d17a55a26090ee1e1352fd8cadb3fc8fcbada8b016e02813fc665c30dcc3f1a8d588ff49e0c50a1a07706a5bd92110531ddc00ec0438433498188a77e40352cb8a70961178bc404de808eb6b33cc6739190cf61eec6c9e92e168b6ef65e769e57193056d607f88d7f93375a718cf73943af6aaccfd88159c128dd3cac2ba6039c8e407156bf9f0a9a19e16500e20f5c6e47b16c0b0979d4b850c4d3920d8a7d3cb39083bef23f2d76ce871b28106174eed3b693906a66c016eec3d3e6f4019d334ca160a2842f65a03d0d9bdcab94d1bfe003bc4686d3c7e054756c1f56d56db8bd95e90a2798736e1ef3d7ff9b275e45b686a1d81e0b8067b55dc3315513a7a9bf18e514f99136b4ccd45c6b9cc16f4032b1f5a2621e937dbc8757e6b2c4e6d057acd9363808ee792d210939434c333332c1307df9f3e7f3cb187957565a214021618b7f5926ee1a21227cc3772bd6b1f5c612d7b62c9c0d3c0fe8963a3a18a3423c629fe338d1d5632e0576769955e5e02997782a11f703b1860f9f9a3f1ea5d81cde3ea1e0b8807ed7469acc4e59498884bdd52056751949810342c656e9cf113b76a966ccd79f9193f0c0d5db3e1d186fff07dc2b64dd8ed464b41157ed25dc6bb0250cec30a6be76c9cfc769e2eaa524a53efa7f5702cadc9067a67d26f00cc70cff120554d50b34327d58493f6a276cbf498ae496ee862e4adf4e874058a65dc94a3ad8116833fa0f85086752d80344f564f8d0f0ee64f129a692a4e0b3f145a4841d374ff386e79245a5b06e629035900e979ef46c5f5a926d7832bf3fae2d2982603639633d248f57e74a1fa8987c1ba5b3c50754f204ede12bcad6674898638a94221b98682a86372c56d623d4b58137f9afc3892b0445117d36be10ac8a25762fd4dc0b4834f2a06535336ba6a9378bca7a70401fe83b769bc77a77ef57c7abf3e2f8ffe23aea0ec044d304a41061e285f9314e915518699cfad3918dc1d92b844bcbda992a7837c6e7da881c99658926fe490da7c421f3b013dfe9654d682d3f3e12a8d5270d1939d40b17c62a650ceb02ae5245111683a30269a8eb8c11de965134242af48a728a72c41963f4e4d661255b8b64a2aea124a57e9ed8d4a31ec5170ab138d1e0cec2c035a7ad48b98c475f5cc6091d49fdc8687a80a8f36312b8e6e241738bbc36ab902cf2b9f0c1067523b83bbc81f60aecceb0a7612f16cea55a79deb0fba8079660981de1e32ac2af310b21de663f6165137a7670403a40354886a20a0c3fd32678019274017ca2143adf3aefaa53071ca1ece46d7ad649207f4a3c98b6c60e0180bd31200edcd427a462aee8f51bed337058c70329bfdf55c4b037fbe1088dd9091c8dc27644fd6720148d57a5bd4a4dd60de045717b7a36dd14ca46326a4667392e5626b2dad4f05a352a3cb6b1ec987def50987484b03259bad1acdc8f997bd8dcdce7bbafde0863bdcceb9c02b5758d4ebf71eb19a764de3e30eff37112e8e22d512a23735fa0f647715a43a7d2fec08bf0a954c84ef776648ae2aa8083a2fb885cfd1c8ccba8edf181cc006e8deee4a25f0862f35cde9bdbbc7f988390bdb07b3d71f94af5194f892d8e65396205493cbb55fe729258f633453fbb1b231d368c9e94285636a06f3ed1fbbd4c96af2d4b69880f50487c5d44850396c7e5185bb6bcf7a36ea838b64788055c59f83a9545122e1af4ad8bb1bdcde92ec16e71d16089ea580b3f26701e2d61875b746f4346e554410dd13f410a075b1600a0fb3e16a952074c549002997f0f814fb6b85e32816b4a3cb0779ded4f57af2795bebe0ed081e7d87936fe2e91d68451652f326c43d9a7654f14ccd100af2aa7280e46a0cffd56ad0f3c1a9b5999dcc3bef8835c068c6dca9ecaf73228dc216a6d05a68a9fa6afca01cc0bc9b31d05204a49f3daac94391a0cf11914c8c6a958d859337e1a9abb03f2fec580ae65d3fbe15a957addb42d336b8075950cd0818416b51f3ff0418683699623964ece94c81a083d77ab91664d77525afcd65e28f594d0797f1bbb4c3555b172cd11cdb6d7c72ad2de5d07a353ab2cc1a12a0c8d9170bed44862bfbe59d287d83f1719076b4a3f7f631479f21eed053f85dc27ba559464973319ce831713ef34eb5fcca60430c7d2bc2909eee50f5526edc5917355c030eb6de5dbe23b399eadc629a5a859e1e29db9ee1b6cefbebc859aa15191900ad89b696b5ffcfee17f9bce79f80255461966b50b36c98607f1af1ecc45bbeb51a863fc1410398c3edfd7d174865126afed7ea04a8038b64c59bb8112a6e9a82dc20163e4e89998ed7b807af4b8cf5a3120a5b65c22815d77846ce9ffd4005b3510108979399b58d5354168647646d341360174d0d93cabea8ea2775d46add3ee46a380269b4ce6951b8369f3f7733486c35ea19a115f024621d63e2cfe59c5e95ae8937f43a3e8e9dd1af50a3bdd5578412c8d98525e96813b7f95a3199f1a892939c3699c644c8938147eaa0ec96069f7fb84a18d01ef49a3961ec85a4cbf9e24075eb99027b7ab13c4abc728d99020120070651e6238a357231472367aedba6206323c5f55d81fc7bc57e48e3420541644d1feda6e6c1116748415e9d5ad7c04a1e60e1f765afce0d6523501dbbb86ffdcf2d39be6efb2c7539783f1922ef30f84b6491e0863fdc5f1c04117f18c4ce9b80236fa9b53db32c0554d9f17491b0594ef7f34c2a92f414209122450c94667e2a28b2efc03e59ed92b9083bf570eba65368b239c123ee01530662006d4f0c5ce44b907a7da50df630223f498b10b122204e510e5117e0b4013c83fd7f5b79ddb343c3df216f58f7488d205e249e033cd6016a518a831cd8b00ae4c866125d23d30cf2e0f29feb918381c65c582a5e87046690f4762a5aec1d996d0b189e9f420233cc6ccbad94c3ba8178979295b7769092ed905223e1fb6b7fdab611165a78050e7a7c702abbb3f1d115e09dfa1095b9bea1713d7595f1c1a113e0f6c8f8603946aa4e16418d2d9a98b1d8a81a45a562a2853f80ed586eee46f5aedf7f83036e13636ccb5bd97165ad9376361d629aaecaa05f8569355de509bfb52c4bc9c10a88020e8efe2de7da90c6c1e6aa632c4849150e69942e6d5bce299700f2ab9715a6a06972807aca14f93768b5920bea2067f008d1b2e19407aef6162e6a60d16867a649b6267e5041e48b5b6cfd12eac03883f4bb3be8a5c40a15a2d57640d351177b18a96c91e573f048fa5af325a21c830688fdd00e0c7423852c56c48e81c46557fd6707e409cf9ebbdbfbc54fe8fbfc10e9b2a517a7e2aa738006ef5c0cfa516901fc56a76bf31f00328047f6c2815da0dbb329267c069727191812f4bf3d00947c01d16833e860589a3046b98bb900486a02da0aa2f6d02db7dac08e2b177ea337a176a3e7abcb90792a4474f4cab9534bc9637b505b9d64d1de08856fc21f7d11d6ba263cbe48f0d50cf5e4eed6f66ed758551594ec95ec7edd9bcfeb5eb00bf29d0dde73591a419861ee039c4f649c245aefe8b39c269fdaa1dedb04dedd1bbb94fac7d0a972ebc5647eb8786b3c605a46aadb082ecbb2fe7a6f4ce63345853250b71171304ca8be2129829773cd76fa31997ae782f8fb9e70fe8d804e42fe169dfff0be823574d96d1c907b92e1da04349609848e014c6aa3c5fd8fcd4c37949dba21963d4446d19806720c3ce44ad7280390cc6fe20441310af1181bf2744793cb185545a52d80c768375559274b2769e731a8ecd78e97dfa699a3c9a5c03e7e639ef205c1581555a917a4a9f7dd07f0abf9a1e7e0be0e159f6d26c193c10512e19662360543834e911d3a1c98c6620089817a5d72959cf5d5989c5f178ef694179517f98b09fe0bde6caeb1b2c0345c1e52617f1158fc179476efd61817393e573642da67836cbd3e4b426322d0a4e9cb2925cb65317964dc8983e62862334491d523b236293a8278919022b3cb32e6b3ae6a3021d2754f842e33c4cf223eb336937ba8a331986d258476a79ddc89349f489a9ad54409ab9292222009de2093998cec36fc83400364484a3f462e2e839d45bf6537850ef604853e50386075679128423c39ade3111a10bef4256cd4cfd5a8fef6249e98c2fdfab7a21a2474a0736690ea3908c18661f3c3da72e1204f398d61bcbabbd35cc703d2e0c3e80d70902d076d259d15c405cfe0571b7ddae4072fb64c2af7ee75bdfad32a6d0f85319434c409cf6d14fc3474031b039009c9ea367d975b79fa621c1642cbfa2ab18b4a590b425204e47a2e0c1b4941e48e0e7194a85c0b2f29a0a75871369ca2cd98eea318e2834807744173eb96521d0b2bca7635238d9da0a0fe8c414839889f874a1a336a38611ce60242ab2c0ca4df69336adbca262b9edfe16a5767b6e03b65efadc7831c1cedcb9073ef36a86ec2e6686e3c67b442f2b46bf604bad5961bc67978ff3319a88a62e748509a89815201869a7163062c1d0a328b51c7d6e5244107e9410cbf71141ddfe7a02481ebed1c4851484346a13e1d133a0780e1ce4d3748cbceba4915a28620a3c70730d4e0c8985cc860fc3c8b61ae2fe37e34855d52d082b8f25a43c6902971637820727540710c9a9cbf0518e47030451d56ece01f1c9a9e0090eb8d2c73c019030e77916b25166beb3b929484a80c156fd99b06d000e62f87f3823168630e7561b03ea7a53c95dd3e48955f69ae9603d9cae1359735967cf810946d8d594fdd8643632903e536a2cbcfe77ca36b1b1974faa77891a2d89277e20b5af00b4f6d960fb24a1208c39aa3bb6ca242150cdb7e0ef1c0025e58d6dc45d5b0ff4f05835ef6c54751c74df577ee6fef71c3b4812aef1ea3025cb1032a6ce67d5c400ec29236ffcf6a2bb2b3ca515edfdde2defca42bb84bf1bb1ffb33979f2636cffc05bd19de778f62de66c67b770707079fd03c069a0ee4315072ecb09dbd8e54bea81158e09b1d1e2d2045f41091b445f677b7944422bbbb37b70c640848075f07302bfd5ccfb08c046f36ca473e508c236d08a1eb3c5f9783de82abe1e2cc30786f3e82f96857564d368207a831c66ff1375ca76fde66b66c560ee36b568beb1ca6a367a35a6b7eda5a9665d9e5b211f4ec2809ee4c5f8232e93a2cc272d6dbddb66ce4b00ea8babb3bd499de2e978d6a6ca949ebebb05db7added88afecb371b410d6036aab5e6a39ddd7b6f367a6a0171a0561036d1719ffc75bff75ed28f3ffc8e28e67ee483b0fbd3c222a58fcadbe9d8769defada49490c893eceb07434aed6316a41a849ac575bef16bae73aeb3d63efe4e6249b91cfcf733082124a2ee37e71aab971c5e5d167dd6ea33fa91b5ba99c6d52cc6afb7adb51816590d16e9588cf1ad346f69b8cf56ab5c078be0ed814537ebc1977aa981b80986f51881cd0691dc27677f75f6cb9fbfbe28ec5143a369d7ddfdbe7eecd3dcebdf5bfd625c0435f0aff7ed732bae730cdb75ceeafedd9cdc9ccefdf8e4b2f915cfaf386977d45f843d3dd63a67f698086a003fc347b346ee67bec3ece96b5f2bd761d8a66f6bfd5ae151a5b57a8db13e9caf77ef116144229bafe1e29c3930914e4c635887613bd3b9460dab268b31b6789b7963b3aa9189eb802a248a5f399b165cacc19946a6ddd7db735eb9ef6b4e9f9868d72c23ae747e5d4904a5122b262559683998b3cddfe15d047b00d53debbedf01d5ec21bc3a6ece5cae1c2ecaafeca9941f932ea875e75f653a70ae4fccee2213eb259d27731d50ddf557dcc3b8086a007151966518b66fadb5e222b9e41a62b2f166bd3f91d90bd82f4a0aa82d0552145ca438b2b59dbd1f6d8d85ff3d8f8510c21f0be184b673fc927b35e94f09b3f816eeb9b3df3efcfec2ff58af69d9bb94d2df61d23bcf5e3ff545d8d257eed93b0c6ae09f3952f670c5fd645f69de5f864359f3345ce7bb7e0dd7b91f698e7b59957ea3c20d3988307ab4ebeef01d06aded72aec17ac96116fd1e2d1dbed6720dff1fc87530cb39f0f6e720ea4d3acb9fb696d52ccb6c68dbfae39f2fb97ad197111d763a3668df76fa86eb892ee27770679fff4518361cf697dbfe919f7d956ff3e7d49ed8b633fcda7772d3ec7fb0fd9b3d0778dbd2086a905dce8d280dbaa194524ae946f000b3c64a8c514b145a96087a01490ba6d041aacc992733e492275cb67cd2450b55144d42082184104208250771a439daead75aeef1c0872fd7a836fb456981da5a76738d1839ea6563d2125a4ca541ee599f513e8ccf3ec8cd282d49568831c6317a9ee0868c96455a23685921055a84eca82c5fb248c1c2db52e8c9202aa0019bee514016293bee179505ca76a90044182752984891a4354511570a56700591e5e804597e18314305a645854b4bc815149270b910c54542ac8a1ecedc906486960f47c8b4e01ac28422b09470d3022c3da2ae20a2828a2c2d26aaa430258a10537c7061716a99510112277009b9f00452abc815202d16ae8ce06a010b97942e5d9eb8b962891618d102828816ec898b053ab8bcd070e3850906185a4229b458e04410d790134327187363451451b4ca54451de1322ae2e9879b2c9abcb0a4850214b87c20e2329274534513427e68c2474baa4cebca9711b620391143c28584944b4b940a3c606143eb49494b8b1157092cb8aca81ad25aaa22ba99e24c12615a424ce192010b154a8891c22408139e5c2ac0c18914fc88018bd2095c9828baf9c284095a348871f56801112d3a58719a7a3284d20a9a72704dd1d4820249abea85255000620958154b08b97ec862852a8680c24503132d22545a2fb0c00754931d5c46d4e04a9281842525159658316af1608584961464a6bcb88890c2a5431348542d134069c58020727263868992243114a15ac115a6ca51902a3f7630d30498960f2e2e2757ae2421260a0a623754f470c30d2e2666801d6931b5e0cac184224a94d0eaa1440f970d57fca062862cae23fa56696dd1774acb6808d794272e303b34b1a49544925614232e282db89e38214812405a4392103a618cab8a97a12d445c3952e28829ad150cd10ae2890b861d5c5c96b47a48d2cac1080f2db89e9ce00412405a312021e4921a93c44beb862dae1eae984089d60a535c4086b8b1e2890a767065591245122b46925a681971c21254405c3b50092931c6062fad315baeb872034689569329ad3043e0f0e4068b1dbe2c693149e25a62c465420b474ea8e108202d274708b5aa8c711d7981624b0bcc951494702d4d996108284f56b0830b4b8448d20262c455d582abe9042f5480e04045a895c598960c5e5c4c5b905c6959a1440ba62831c4094facece0a2b2444892169411d7112d6c39a10420ae226df78b3242a808322880194245abe809170b5537679068219992b2bb9e2c43ecee5d354c41edaec77b7caa498c3dce15a04c18a32943536025b0f68b9a828214262a65898f2925079894f2032c4a8902b34294e547b65f94142ed60995514aa9208060a9a9224d6a8912b29b2ebae5a45d5db4d311e5a2c4e6942192dba0f49b51c894208504989421246421b3d2eeb446f4a8cdb420340c5f5253524bc418a39c525fa2a49ea094d26a8fa2a20c65f8de7b332d0a5394251dc5294a2cca14168bc5d23a0a972858a290d91b097b731e1b9bb7b1b9919c94242943b42a52477e4825816521a570d85498503e26d4152353cffbe4cc841a1a8173616171c062c2a82750512ba8b2a1205b2d68282d25b1d694aefb4541f1e177a0e0004586dd7d8f0cab305386a2a6c03426ba24871f70142956a464814e689b529414128ccceeec97fda2a0608982c20494a9bbadd49128a9241b10207a7acaf46c1b196bb79d1d971929776209969040c3932c59b2289af192c468a3c2139a2efa7138401049196113da8b71f5f957ff76b68edf1a0fbe8d2eb2aab3b3f3baaff99d0eec1aa3b91f1aa3b8f6dcf5053c3bdb37b8b3c1233d68d084006c407bdbfd63c411f543d2fe8823a4e1c7f86cd09eff0f4c9aa27e807247b2d3e3ba4207de9133fae96b8ef5d21eafb6addd4ab54d4f4c42736c29844118844969d8eab88f5f2a5168ee5c13a5d25f4a4923a5d48daa04c569a17741ee4735bae46895dfa94b850386dda0c1f7de7befbdf79e8f94d1e39933f03df8de63bd0de41b6ef85e4613d3648c1230fa458951daf53331b0fa569b33d3d06f6b97bdcd352af793fdd8ec6bb759fa9a7bc3793ef76bdc7cbbf136fbb8d6d7a0ff646f69c6e81785c513164d5aec2ccc7edb1723eef8f7c96ddd0704dfd7be18ef3dfa7f41668302f4bc410daeef986fbc996b646fb91c1bf0fad6b20deb5bb31bc09dbdf6824ffbbe9659986dc01e3db67d6bc3bee56452e6447b8ee5e39eb4711b42773d3d11c6ff0645fbcfc73d1731c61dcb68cfc110b15fd4972aadfda2be406df71fd7e1798ba2fd9fd0b66ddbc604133bda7bcd8f6fbe362bbab31be2d7db167b8c0efd09ed39fd2ae5fde9a56abf282f52db00fb457939b22dfec2e40bd2d6229eaf559ca7dc31cf1bd4561cb48cfd472dae34e80b49c6898067cce04ff306fef486cfbf786e2d62faf2af367cfbf3f04017b465f8bb36b71390868d789311b3fbba34eee5eae3fb38fb9773d09d3dd8d90eeffbaa56e6fbe65f86bc74d99ae3ad7bb1ddc7408185d4d622ce68de60aba2b1ac785b0b82dcdd96a3bee867cfd20d29670384861c7ef82c162b9eac10da1aebdda94be589a66d32aeb36fdfbbf9a02d0cec9fbddeeef45b986d689cf549ebb6021d9fbeb42f7d5fdab10f3ada9755205501eb40021dbc3ddf4e6dc2e7ab19dd57b54b6c7736aacbd3267580d0d6be561dfa2ac25dbd873aa1c5183f564a6b377f2617a7d09e575c040fb0dff77b9739f77f2ed0ad419bbea4c55b72b8e8452370d1a5b2f7befc2b83f6dc3568d7970ebc2977a9cc200a21161e2433de2b6df5aaf1f6eaf1bf3c01297fc5c95fadbec6db2b2e27fb9fd5d3cf381d20748e8dd5d35f711fb4b5bf32a20c0fdaf7a563dbd6fdc95790061d8b26d811122c4e0c9b3842a227717c4dc344b888c98f384f7c98a483c64518a6431375414b82f261cd47ce536b7c92f169cf3aa30be9537b6598cb88d239981477500c81331261d3bc83f668e513ad4ab2cf61bbe4092a4684d414d586837ed24ffa49dbf0041523426a8aca7db44815a9621527ad6d369ddfe1d996dce9a4b5cda6f33b3c3668b8b5cda6f33b3cd928233d69296df34f50fa291b65987e4ada9d3f697fc29af6b4659bce465539c020e4ee6b9a63982167a3750df835f8b8420e3bcf0d717b1b13fd737f6aefd4043e41b90f25024ab98f563f7e8cc55824a26a725f195142254c4d90c97d94c0a6ad863bb71cb4fdb7bb8d4973bc6d7bc77de484dfc1dde1227800c81d4545c1b43be80463eed341a6bdb4e3ebf090d9b1c58e51510c6ddbe97233c7dc9c60bb73a7089d76748a30205374daf1072dcb38e8742f17b9ce56a628855138aac1443535f868d6d4d43c26aac1493535353535180689740d24d29846d71a1a8c8b70112e9a62a8a8da70c87e515310f1996b915fe7d735bf56fa11616748e5ce35dfcff5817c5b4d6f86b36ed142b768a15f46d4f981fc67a5695ab478ad59fa59b9458b97e134abd72d58ac4c59b6c5731da4a161cd1afdcf3557535bacf4d7d4e8973472cbcfe2b2afbf7a652378807c43db19d7d1bf39fe5667695d58d2f37536d25a7f8669ad334c6b9d8f1ca67f63659c595ac7b2f2968db25136ca4615891628ed02ec174545974b7fbafaf245d87066ae9bacc7ac6cff029d58fd9bc5cc6265162bbf8c98b37ffd6879c5627dce383fce58dc8eb8599d08fb07bef62fe7d03ebf0c67e1cf8f3916877fb5e230b75a752208eaf48e3032c759fce2a4e5ba7d549934655886655846da369a2687b9a6c1ab8c31c6eeee34347fe3345b8d679c8d367d9dc418e3b591c6f7f8b3230419e15f7c71d2cffcb761e62efd658438e4a55cbe6c04ec17c5e5698a26770a25530cdd0b54ef773a30ebfa63fd2caff1e35c0733ec5dceb8c5e71a9a5fe9c03b7bd62a638d03aa3be3ae6f9ac66d97a67c9461f9281f35e95553d2d3553725b5f8fb3636fbda6affe6f5dcc5dd746c5b5fd86adb1cb66dd9281b65a3a61a9776a5f4bd3af9e253281d5b691fffec0861db7f7bfea630faaf931bc83bc8bdf85b9e7602f68bda02e3d2c485686b31db2d623601f68bda22b535c714dbb03effed1161c0a3c71cde175ce0abf75af3d57247f6f50b6ffdcf691ebf38177ac57136e334ad0bfad1e46719771f46cbfdc0af95bba1edc975f0658c4bda691619d1a58a23709f9cfd4645814dc77bef8df8de186f5c5d2a50838ba3cdca66d639b93a679df15e2cd7efd4ad52dd9fca7c1cf3af5faa4d9592605574e1f088deac669702999476a95cf71baeb32db81aee4edd7ba92ed5bd54e001327f0ec3697cd66f55afef55ad1bcee1a592655946b4b51863bc542a7caf0e6ff72a05a57a34f9d6e4fa052197ed97cac3a2e361b9f32f15df36891d0605bc4aea78581e169b9dbff32eda79f253c03e2c5e75a95c2a59965daa7d6badf5528939593c3fe5a3a7a5caa4599fcdfcf9e111d10e5376efbd56bb376b166723cf3ccb471bce39393b27179b97fbc9ae43e8015f6700f6ac0f816dffed9ffaf3ef2bc772371eb0677d67f6cb34098ed103620a272c50f922454b940e4ab218d10d43623f5ed878bfd80f1362444ab11f4c361ca399c39e73e29410dfc67e0cd95bec4709677080c47c50c57c74511203a28453e44efec3260ace8f2a38100a0e09d1fda573b15e9e8bb73b16072460792edea65c379fe2f8d0aefd3eb0e13f695d1bbe1d4f8233c2c643005b0441fb08f99b165de9847e75ad6fadbd3077007bdcb5fa0e7b9dc4365bcee946724e871dbdfada716e47fad38ee4994177ef69fb1e37f2d78e4ed55401efa84067c0036d849e8476a71eac4d949bf946b6e1fb3b05e5a572a9dc07fe8baf10e0739e1e34e80e3231b9cbf8da96cc3cbb99d1cffd3d7f21007a31e0c7877a71818701fcf7daf1ed07dd41a919efb5b73d0efb732a300c5681445218e3224c848b300c37515a428afd911438312969721f1b0c8920112ed29b5729c54898da06d82f3614b5638f5d7f3ef94fdb747853ae83506cf86e64fe6436ca47cf977667ab765393eea0d30cf83006fdc1874eb0097c729f771eeb366fc79723a7b3137b486d1c1368191b7ada4f8b9eff76f7305e171b9ff950ba3f3d9ee7c3c1d093c7f37617f7ab6769e868bfd8508fedc37e31a126fe5f6013d824e6c58062f6d39bbcc08b012ff030c0ce13a38c32461921d28b0191f6fb23cd5f899052062fd3c06f9ba2a196f1ed2d2340692c7ff81acc1b14fafd56a521ccc5c02e5f9c07569844e53c559cc7feb6843ea2d25088712eee33b7283484493c3dce03313542cf39a1d21685863106f8246c029d6093769e5a391bf9f4ed6bee899d87621de7d9a664f0a2a70132e8dbcf79e0d7973af07e39159fbea947c47e2f85c57c6d5268fad5c638e79c336ad9b5f6da2bb4b86dd1f2e76bda66739ded3ad97b764ae9b362cc791573ad974a8cf7dae870b3a2c78bf676b6277a74ec91f5b595b1d9ac9c58ffb675bad7711f8c3fe74c639fc6eab7411b685f9ad75bb1f05ee1af38bfb48935fcaaf65a58f3f5714dac9952dcdd8336dc9452c9e5c87f3bfe94d2a394324af727adf78b0dc13048570c6d4f68dcfd66857edfd9383f83d7e33c0fce785cc7b3dfffcf2864ce39a73b76f7e73c21e8c73a07ffdb8e1d3b7eec1863c7733ee9cf7ebedece4f5f86af56abd5b3de026d8c85b5ca38bf8236d678368549af52f83af605cb97a83058f68b02c3654b305536b521d4e7baf7c1fe299f74dc53b4e7ba8a588f2aca92af49295b163db55ab55a6bb5afbdeed72a69bd19b8cfbbf75eac15107fd21c2a89b459e68bb67975e03cf0be071cf800e65be3edecb50b2187eb6b32d3b8f1fabdd65ecb592c45cfcb6d52740733831739fef0bf2059e95100941276e03cce011327254c5a6909e98806128530021b020e7870010c74c0818c0824a1a3ac91abf9ab10f60acbd5ea31c678e54faf8ac69964507952c08a35575246c9751f6c89a406cf1a29a364c98852466471da5de06d1557f2e7ab931b471925123abe8c08028df834e66b71da7b67a6a141c8e5d4781b7239acff81acaf407b3ec6acd78bf95f9ef3a503efcce1f8c22f191149cdcb57cc346ab808b80bb732a83cd5ac409bff76623da4363462bf2833488e99d09d8d31c6bbe9701effc079a69c8788186ef2c4a4493bc2a49454617008f6f8eca98e0f746c1c5042776f4e7b33d63169cc9ff74ece613bc629a584704228b357126ca2e11dd0c031fb0d8bb63f907b21c95bc706e4dbfe1052cc316a1e44176ead8dd1460fb4bb80d9431b71c0b81da1a10734e0d3b0167640757fe0c1c60307f0572bcedd05d769ffeac1948e312772d366cb3828a0c9d7f6ba4015cd992d8bfeee35fb1201dc1b12babec97a5b5fb2d6f9f9b591d1f3b527ed7d12efe93e2f5bfdfc2dcb5ef31cfd3facafcfe2eaebbf1fb4f3ab03da59bfde5b9de9c4acb53d18ca30176e57f47bf0bd07ef85f741682d7cf642c8c9f7af7b1fc26ae73f776b39eb6eb56addedbbf0df6b3b4203edcebeed6ca63d6d371bcb6d5386f34c05609f6fedfdee69700082dc1b0fbaebe95e6ccf9ee862becdbea001b66619f4b88f85776b558eb0de7645d3c775ef753c8aa7c59b101aeee7f43577cd39e8a613530ae907bb83af63db6e660866c8d1d72a86afb775a4931e6d4db8bb95ad898d422c5b4c771069c7df96d094a3f452d5163a1d9b1ff95135017cfe684028b56deb80efaffaef059fbf18e78c73c639e39cd1edcd345eadb656bbe1fbf9b262587df4c597f78aa6f94b8b31461aa3c797479aa10cf75adfdd29b5b87a8df3b9d328ed7b5af584985894163251503845414134866aabf294045f48b4a72fa29fa17dd2cc322e1f4d0138b9127711ff1e711e2598d4447702d8f1bbc72e00cb83e69e222c8a2afbb55f54144e5b63e5fcd220f69c390ccb34ecc320be5582412227ba63ed783756bdf7e69cbfc6db39ffcc3f3fcf5f3d05dada2a630dbf82b6f6aa9693be244fd8cee80b36335a62b1d9741e3ba7fdeab5f94856cf7a516b6d8db7319793ff077f7dcc056d7f75783b507ebd9776fd2523aea4bbf0cf3c12d917e0f4d09d8531c37c615f86437cef6759a65f7f7e751bdcac872ffdaaf1b67ecf1c44cd45adf5d7785b7339abffd14f7f15408a441a5518f5408118292d4daa44452188a211248c6070257a985042589ea02105467a28828930491811813f8e6431c488fc9e09068671424288981052424d481439633483c081c8842b42e420868a8c0f505431542525c80b08f1638a1cb2c8a082148e289244042795215b902233f828421b80c214145b86a678d1c18813285181cc952d45aca061ca07158f420b112b544a2c711465069df203121148b444996c05669082e587092838a1420a113372042216746062e28910172824518888f1016f684249280c112846a69841af9820849011d884c88f291ca6ca10c18354112641600850411c7962041915846c31001952e1d2924f20481246b420440e4e9ea01089a11e49618811b445132aa4a04cac8a99a122a8966250126204d67a04b66b25d2b51ee9b16b159a1a5ad29bb6d19ac8074a9d362d82029e3046300153855094120ba6822cc91b22504230f454c943f43cbff34f247fe7f53b2087129e96f8c147165033ea83dab512fd7fcfe5a1035a8aa8614c192233ec30833e01e2b48890135164e9c7120c2c0c51451555676af831c491268c88f8d31412c2889348a48890113b340822381005e9c0c4981f66ca14c10405028a2009e23232830f18a2701283911dac0c298acd6004c916285ea8724491a8084491213fa6868c48292314c0080bce70b1a14715d10f44a68696a6449d81fd500609144e50456251080f106668f0c134441527cca84f762562c3aebbfaf02352c9000a494cd9618528534a984157602406a2a50ba8d0013395d4c40a23aa78e28a15f902e4022e3068529340307c8101cb140c50fb7d45daf0dfd2d086dfc1fdbacb6347dd3a0f83f7e0c3d14177bfdf7dba4de7f5769df2a55f5dfd17c551ffe9c7d94420b9e5cfbbe54f8d661c9c949c8df338dc4fcbf970ee9c73c08feef37aa0322cb2bf57647fab138452706abfbfa0c3f6da3a6fb47139997139e73edec0b7761ee8dbf30d1bb65a2c2c86156ad7ba2ba713d3f5e194f5ad8dcad99c4aa3fa8e4c362989098bd8ae6fe3cff9b405bdf4dab4b82d6a3aa96f3024c27de27edfca1bc0a75dad843534f6c7fe7c1a9d6d58cedab7336e03df75be7c630d6f3b3d0de0e0021ee818c1d0911213d7cc98095fbb61ffb15f7fe27c03be4065b30dabb76d83729273095f9c2bf8c44ab24c5c38b25f4c29a6a4f4feb2d4b67f3d73e6cc97ed1cbc6d0397eddf056da80423b3fdb597fdc67bf83beaf607da51370ef8ddddf5e3fc0d9fbe3bf7fc3d845974d96fbf167a6896661e5ff6a7ded9f195fd7cddf852defd7ee24b1022d7d19f74bee2b49193f645163caa17303290af1b21a41f33b00fef6b67539c8781bfad701b4184e141fcd7c36032d1f133d8de00397b9cc7795ebcddf578c0810f62087adce7c5787bca298f788056da980a55fbc55220134b414a62d9bf5f2c096a6b12d7495f527e9dc08bc91783e2095d5ab8b33588e90f7df903d8d96f274a236863940f83fb7467cfece5abe20ddc56f9f4abb4f4ebdbd1417b70b330b402962ddf5798daf2e70a5f26862fb835c7e1f20502d08e1a9fc78ebaf1c360661cfe72661cf35f11ee69730ef96e63becb87ffa43e65c1973829d0f17fe6d7a0a57df950a3afecef6b076b7fc67ddd128a69daf1e3d2d1864f7becb78398ec00ec174b42da4070c78790420861b5f5c20b2437a654cb3428a5ff30c85f5f19ddd9557bfab236dbbc01fcf8eae6e521b909a1fd8e7e7d9971d0a7b4fec06e661b90521abfbe80dc8bfd76b0367c18ecb72c486dedbba07d9ff6db5a55d3d27efb1da1d065f5ddd534b6d1b217feac6918bf9dbd7a75da67d9ed286bf3901b3f90dc73c33adb9c2cccddd5e797b51947f61a0737cb520ac28e10ecfc3ce453fb2b08e9afa8cc38b2f65973ed5734344d7b0f03bc7ad99d9dbddeaf72ce5e661cf7b5d7b297d5b847279ddc7be13c2f83496df8484ffb6deaaf6e6a2cedc50bda90cb360640df8fff7b6963003fb01341d0f6d74dceb9175bb4f831a4a6cded1743826df8dac1f281174e68ffc2a72ffbd7eeec17ee43a7267f42187d9c4066c7d82f7642920a5d628000bfab5fe97b318068c5b9213e95f83df9e26110a5467d095ff0cca6be03da780fbfc6e37278c8ec211783b6cc39e63f8e478ee56cd41ffa137d044aafbbcc39ab2c5b79cc37e8e31a2fb818660320b8ebe320d127342d6df9f2dec8a0dc20273f0f69fffd0ed67edc0e8823e37840cf39e43f8e078c9cb5f3c68ea5dc0be7e19ef08f0ee18efbb97d31e612f951be9cf467471d4774f9e231215481169f36fd3ab5b4e34b3047dbff0ee9b163eca88bc6b2cfd9afb9c6cd3e6707dcef79c80d7f072be7a05fb92ee6980f391ef1fdbbf920d8d05b04febbe6d8887f3f723fdccf1561bffb1dece0b679076b8be03d0cba1b738ec8c92b7940b8736cd48f0f0437e5e48fef80db665026d1e45729f9367654b5ed531c634fcfecd903d89053c005383de89e1d2a0044d141cc1de364c08e2f76dcd1be385f0e081d4fe9369c939dde4f27a6e100ecfbaba32fdfbefd982bd7390f488bd09efbecb0fe9207dc91b31b98616a1bada70c52480d9bb55f4c489330306831214331a316708cc0900959808aa525a822320cb1d28310241b13a22484f3c2cd7e31231f9bee1733eaf11c6993c0051754e001850e8cb0450a0b445780798287205171bf5810283331d891189026382a047121a3608439e70c52d402043d3c9962ea6949114b33a693333288d00f2ff8093c85068b907c294410263706731204078558101df66abf5810251a0bb4e7d6f16cb28206a008e99224aa8916f4b0434f0f4e0a3d4838483edd7f1c532074887e2f718468f96fe21763ee1061fbdbfb62dc979f5f8c1aa4b1a804ce564631643424010009a3150000280c0a868342c158308c9274b61d14800c7fa044744e974da420874118639031c618420831c400030440886caa0058956e60dc687ccef5e20280ab452f25787b747e97d7e6a747c374e0b440ffed039c2161819fad1bdc34bf5008b524040c6b078a1b826bb73164c6b104e25a5de1a1182a8559e0dddfaf4635b0da881b6c1aa897558ed8be3d22a7067dd110bdd451755cae55d36d890827c00c6ea2242af0b7b735c123c28e02c7d4b5ddf56edd702fd1ece0093063a91724ee3759ce6ba3e0a0fe1bfcc67812f4550eafad8d9bd4061dee4f034047be4a4c161e736547869f287bbefd7f5337a706a10fa0b529665954a8554ad07bd63e5a75afe02a32bfb65ea32983894160d59e4ba7af69906bcdb40462adae7041c9815502d7cd7c191f5499dda49a9537ef246d5f94925b52a0748a59429e1c0d6ad5ce4db1aa403c8299c12f217e631ae28412748b44bc896817d15c087369e9980aed45fd57479cb76ab99d79bb1dd0029bf4be2040c315221117c01b97c5fa765bb4b870f7ba2cd2b75bcd227e37a11612a08b80d0699fbef3bb66035afba84113f58dd2105a62c5c1ee03faf456cbada54b28b4d72c790204ca154c7ffa4d800d3e1b71ebbe443a42b2de73236b557cec969f099bf4cf711c82d9a630d899e46d9d518457539145ab1d1c839246a7e2dc15e4957eadd0c38132a1b0c0fb2b7e85d39a3bfd410ab937f819b6d9803e3829dfc53289ab67789f9adb51a5db31361d14f87a6fbfcf1fcb5c70d8058441027fb67163ec88ee6916c3af972b27178205f7edc3339a6e98adb6f8329b92c30bd6593c6cb3ed79a21a60e48af20d89bbf8a0bf7486ad6e600179953c55e67d0d3e603aadcdf20112e93400de01234c903aadc1d99e8640c07a5e338b6823137ce4aad0e156acca640e50582ac699740041226b0031f4de8144047e31258fd58b840c81b8ac964c58d2eb3e7be3424b9c585187424d5704ee2a0589355af1637630af50a872db3fda6e0efcce5e2df7167dcc62da5cd15aeb46c30d9a9e5c3e82e78b24d286eabdcb7494cef81ce62a14a2d549750cd5aa61b7e06713ae430fa77418d89f26336f01c1529f007a26ac139e8cce3e50237bb00c6fc14c9cece42d644bf62924d25072eeb11699a5eb237f3d5503b1ad955ceab1354a1bd5304ab46ab57463775509fadd77ef49bf9db1edb3230eb39f7353a73803ad855838951ecf4da26fd83941779ef3126c7d3a0005fc945979da91d92874c346110887285b428eb1a124372085b14657560318281452908c9b7a7735ba6393df2365916504ec6b5c49e86fb9aa06828e36cf1647aba68b9cba398cac3c63cec3ecb950292f2b163402b709550f882c3caf490b7674fd093e135cf596ca8c9d5cbd8c2607d49e73dc94e8eeaa8133a20319cb0b71f36c78cfa407a9ec64e4e4444a2eacc009b1f6a07f93e5396a07485c38c36447752b852aee692c9ebbe35e9c751e48fd2be5718b5d07999a04060d02cf0dc624b42f40b18527e653844de88cc60bef14d43d532a08fe05503afbb664a716ce0863e46cf888c01a8684b4915c3956e408bbd1d36baa98ba1ba0c04344f259b944b27aab6493d2b767044911b9e857669ade255632bb7d23d0db64bc2db77e3dc7a6f2e657008fbbc16da395a6cf2150e332a1608a6ce1d8848ada39b3d1289611c90a690e57de879b1e7e379899991a857afbdba1b80790bbeaa3198114d0ecaed30d2890bc705f096a23deeede2e709fd0925f858a9dc395295b54b15378c14e05c1b4145d53d47725d07c61ae8b028d00eb97edf118dd020a24d9135799dd4a99d418c9c28f0d19b3c52aa7672d671a7d23133a9c27a187e66550ad73850656b60fdbb3d1d82c766ee14ac1f2bbfbe371695202f49dfe72bf79be1143e5f6e3fe30b4c997029872021e79dd61e3c002667125ed1f4c23e74a18322d4eca52499a4e144d81f8a0a59f79345d58a5833ef06bc5e4755e6ec68c7e38b328a2c27c934d46c0257b473225995c6318df4883aeb062e82ab2e1950238f25ee93fa0958d65c17ddb51d2949034919fd191bf0a2f9a973225fc231b4b4292fe396098672fbb9a28ade439c007ad6387ba682663e63bc802e6384b8774d8b6243a885be794abd5908993efd73b6fa6f0804459df118d6023b4e40015b185a1a7a840ee26aebfa0825bffcb807db2eb76d83bf66f6868dc229602b9b9d3f3f33eddd365653b5fb3db27c4b34934d31f8a048c2d4d36c4da5cbb464e9928ad4f902718f298f2a8e1b15d6fbea358fdbc46e51c7bca6629ef44eb35fd58c634d34a89df296d723988bbdda5a1ab24827f02aa5788dd994b249b4e12dfe4852814ebfb3e859406f594cd0d89c98c4a06b911a76a82ac155133bdd97d29ab73f27433263811e660d9b4b457af8f54214b12692896ac49158c520d560f13d41a696224398430ba0a92b34234a80a5190cb88037d5d60a7b44ead44325c3e1125c8dce2902cc1780ef560f05c179095d1896db3cd0c88ce25dca3a0f70b2dae5ba6df399c38ecd43955dd6213a75cab25f152cc5ae8f8a71249196b068e7e4af7290690020faa38774130905b3449a066ebbdd03d5495b0679d8b8c94b1a364e9cc789988101c4469cbf84a114c38f7208acc2b4a41c9ea76e14dd47a862c7331f4d12350a70b188e4a1733e48266b1462323bc3cf1e601135a46d29dab548d9036bfeb8a51d2b89654a31c14d150237c7556b9c18906e4be54a13675c47239bd6c3ca7e6e52d808c5ec53648ee6d3a210174fa4518d09ca4f9872b2b7d222d0544809fb41cbd1f4fee37a27990d41df48fb91b0f735a09a465091571a04c0a2ec530ec93338e851898dad3510c4d12d0500c0f4a40a5999d3f2ccd03080e08667d220421d5600363b0aeb85482313ceaad93a35df8e0c2ce4245728307c08d370433207858a1c502394631545fcb8ba83e55c58b20459b6e61a543461a9b16022d32c3351103b282b11c889896ab23d7b5f3ded6f7b897d3c1d0fe53f100bace5ae87c6fc18c6b79fb0d0fa0d6d490178fafeafe0fa08428ce4478205ec50aa7cebb8f53476f2f01af821d4a12b5a3e794cd772792cefa3aa6ba88ebbf030a8d452a53db41e3bc5b9a6ccbd2454243f7889fd82625be31a904a101080c449f14d13a6fa3b1bb05263cd3d5378035a598598ed0d525e25424c8090a53154f2869d01967aada06d19c3441c31f4e42c16c2cccfbd1232ab50b04cca1767712ff67de96be262a9e85c7c8fcc26e536082c8c7e6c6331aa261b7595767105e4d0554edbfb196f3b75133b73c0058a3da77ab52ff994cb0013f9cb4dc343c06d6fc84ee0cf7aa88c836c6f1d65164080a8b5259ed7f9900901212e588b0b50efb852c852ad98678149a901d0ed498f46743ffb3813d9c7eb62084b91bb18c218bb0d7566f8e5fe82a7672f0c277be1549adedd74d4c0fd90683643872c55bc7caa8b063d2db8d0384581897c153de1de9908093e8b9a52a589fc2da8569c22e2b0fbd422705e181e4f182e03a2c347fbc5544a4566e4fc5bde6af7b6dffa661d5f196e9127394e9bce629837f26d32b18e244ceacdbced37fca6dd043eff31fa9c6c43150b9dc3397a927588c0acbc410d15a4ab6673dc583f9eefa14ccb7e06085c47cd2388920b44c84e118d936f22c998aa02a2eda228a01beaee6c045834465d3abdaf60a586aacb2ec8ea2522068cc8f254abedda11ae283d2a3265cf07292eb3433e508d040199686bc7bfaa85f6ce7829a0f5cac53f8e42a3ea0d49436a170d6683dc325318b78590be8a6e8f334b715b05ced5c838a9379287ad38d485a27babe17052a5c5d632b913c44471ee7a0f842cfc4c3c1267c1bc65978a02857056237cf739fc956900bfcd81d257c48cf5b4577157a0d284854680c0b4980b89d117088fccc477b765d422d386c3ba87960d9e4b73849e131b358dc537ba6a9ebefb91be58e67a6f43110a7808e07996c19067ced2d5c552667f687c33438fe37c5b7a4741d535ace6609f0ea466711929d32fb2c9351733ece0210b0402bac10d554cac085e57094f44b7d70a9561f52f21658d394570ecd56d5ba6bcde8aa6741ef2f28a88a40178ff17f9f79ce3ca963804a3714b4a16ce751e8d8fffd975ed01dfef1eeff0121384c7a3df04935774828b7517123c75fd573337aeaf1b7fb70a6e282dd06a33cf058a0d9afac0998dba55084fc5488920cb0730fba12dc8512952e9117ab0c0948c97e550c46e48c3ebd69450c4944c296eff9cb3a6b310d2c2538ac7c02a087b59799b2ced59719ed903777a8600342cda457c4a8f5a937b14299d0d8ca0308db154a6290a4bbb30db8d1ce56fcfb24701602a93756c8f11ec744475d92d4b4632991033e80841907cb0d2dff3e25d34ed62a40abdbc1048183f78e7dfbf9ae64e463984570e6036a488b071bb0e64c762c010877eb820afe0f3d9b46405fd15c99acb400a4c55be9734e46dd4963459bae8ed79a1289741555b8d9533a13a919072b150421dae7259aa2ca5fa4e83375b121299ec39e25059cfc938310b7cb282debf72b65f723761850f9f6157e494ff1a35b6bdf1c8760587172cd5f44b597eccf610688741eb1c61b5b06f50c29c55040fda745275418d5db0141bf461f3c544633a401f568ccc38d671efe99cda168a629a076069a8a72522da245554862c01750304f15d8b909fc1900570a8d11405cea55e149b30bd8b00fcf1fbab76d4bcf63ee02af1b00cf2aa0710b07c8ee0503f121a9ff9abb3aa662b1a3f0c00cd13da776338960077998dc33237cfde6b999930d418b6960ec5eab18f638546014e5c8ed3e69e3f47467002e6c20f3c81b322619f0e6d7091a125a588c384822e127a6f992cd0d77ad8dc04ab49889b3c4df6004ea0f7bc5295b0d0875968822c791c6f107d25a8e2ef98a0ffc4b91793d07921f7c4be8f8d7e5577f6dae34ec76f39a48dd458c77d6de70185093eed081f63bd3c59f065e2c2526d154b23e2f2456d8304a70e6bf6f716b1a3e32602c9026f9df2f956f073d3f1e5ea070c126af09e25af1387b84a861747475b4a6c3f8624c8033f92570d8b0b2c81393ec3d3df809a19daa8c12be6271c220c60a1d568a73a7ec074082f1109e3738d268607e3e12f070efb72f408032e446944cdbc1cbae6dc6d974bc25c25f717eb261c410bf4e160fc0219d3ec911c4741b719710a90bd70bf894cb3ba8c9c27d1710ba7be864bed4a6904d80fc3792e6170a8a1f8a44064a7ab223834f05c70e6827f82f150c11247613e4b2bc651d4c608053e2451d18008e3114dd45ca0b81110b36cf7d0fff1d4fd60cef8110bde7d8b11cd4e9426b62c431e18d09f3012abc615e7583c18186bba0c16c0065a642d2d85130bea98ee28b0f23b8785805f670195ec0d8a374303fc15a10666d90acc4b035bb8dc3b26dd79dab4224afa8f81366d020fe379eea71a9977946538072c1241ba36d7e310157d93735fab7be7c4307b9591e1ef400ed496e75d82c231a411ffcf1bd4a5159b76d2551eb768199324d18c0d52c428ac7534e0a6877140f86566b5f0c377d7cabc2ed4f3777e9b1387be23230535dd1d1024042bd71b73a08feaaa1295b17300f9ee0ca9eb1658c04235cd2f27189802ab64044571eadd00bdd03b00f0fb3b51449b3c0790a73932c6f450926ceddff3293fb5df662fec7bc90623438b9871ba550c709b53cda4c55560e12d27e85347c04fc497d3ac2b0e914590fbe41fd85b09d4bf30a7b2800c13cb0d1ff503b6b884514221baed6283889351d3b802cd7c929fa4b2d9fd619f24286d4039335a65b384a93ed31a92d4cf53c5a8bf5387c795d33ebc151b93b957e7609006e40a299430dadb01442b59f6c0e41afa9150b63980da41761f5dc426128d7f7f83a4daa8d1947fe194e292e906de95cecea4a369b3f580f5ab3721b157a65aa679193319612ee44f4f977263acab35ce30c16016ed491b62e185e55190ba992a8290e51ee1b99cd5361d8465762d9b62abb2ca7e45e5d24b29eb1ca0137a5f31bee7befe642828f7727d6391c8410b1b9e97c6b5c2294ff7f878bcb7120232ff54a0b6d60bfd857c5e9f7cffecec29b209647d87b5650a6a5454dda3254f724aa044bdd7d790031bbe95272130c21373b84334569a273916a0f30ec3bdcae228519214fba8120b421dbaf4d19f3e1d5cf29308dab0899454c85919fef530e400ec95bd5836137d3b3cd7071b66fafac11ddc850b1a5e3ec737f74fad0529125f00c7cf2f3c9ecc7e7b0418b08d1906670be587d59a84b7e589b4856ba4a32f29d2a6ece3832be0fc4a80adc0f02f3e397f05454d020a098051fa8992959865839f3662e3c00c5aee55dc3c10f777c2b1dbc63a5c57d151bbfb3fef89eb80df24f9f56f10d0009ca73eafe4a359f1954802040e321df43c0a9e50fc77d7b0b2be025b814bf3086292748ca53f3b190dee6c74332af3e832583c72b685bf6a3a54f1c7c73da534ac18c7abe2c35d66c708c71ebca3049754bb06c9a4124f60d42a8718606a21d45d5e18b115aaac492d6ac4724676feab6d69bfa07ac1af847536e52361c7586e7a7b843f5dd1de15ce588c137aee4dc34bc023653d98f1d581df8aae5d40907260a8daa6ef79c27cd1fc8cb291d6b436d4d15a60e0d1e6890fcb3449297724d154cbb035235ed451111340afdd8d1e5afbc942d4bd922009fe29cd85c789b5859eeadf3afade76d90ebf4328749890a393802adc0d4dd2790fecf01dffa7588a60edbb78e8336572aa0f5933c85cdbe154b220105a34c1679485c04ad4a53935eb0151f2154f4c30b0fd6939d48ba816c146295ae851509e6d5b02328687fbbf339791ababfa5c8e22f0d70d9942e12ef481400493f7493eb39a41b819c2734414b06eef342a6cfa5134ba14747e41a0e6c3d4989d7bf7a4787f916fcad84b45e8fc1c4b62ec8ad9f014cf3b867308f0e32233871e1eb06d7aa1be5c5aacc6e2bfda515c7de6216d1e66e53ac84ad05dcc18a4ddd1e841462718b6dd4700853f54d1bfc4c12b84ccfcbdb0ae36eeb85c26c5f867b53afdbaec5575b4c4b76b71d1c480f6969f4a642ee03b41542f4c65e93bfd53851d2f062577d50eea821cbdced9598903e2e4db4618e74e2376d11780f131781939199e09bfee11c4dea1a50fccd383fc1ae95df5a89bc84508686348c1ccf26e046118c925a6a98d813c4ec7c58695908fb1c1e61b943af5806543698ff2b0cdd5dd8d8e0f41db4e9d8de50d55361079d7c2a0789b8d6b559b78dab5a975baf406cd404435a76b15169428e44aaec9c10b27c3183a0c2f99fb3d416b183b066fad0c7fbbff4c6258fb6e9656ddff6d96c8e89c2e74ff29b8b55e8166e2146ccea18be68ab9404508074a56e9f0bbef7c3ff6692a9c5a686be3f4ec7c3aed88c8ce9f5c7b956699ea2d47e3197e4a09e0bfae95839960ef7fbcdff10de65d9181113db54139e02417e5f476669bac8b776197db6a13862db9ceeed26114d37133ae6f5ccb74cb71fbf9ea777355f5f1c28a059084895018519f6b85e66902d02ddd2fdbb0c4c8f99ec186735c17a0f83da4d9a6e03081a2202c5a7a4b47ec8b0690e5d3d1eeb8d5d56ec8c7f533fa576ef04b0ae9bd5ca308fcba943f915599bc753679dd907559abdc570c59431df14941e7e3233771df532850b85a33b4838434c1546881d54f0b8fc65ecc513c00157e7f225d207d0c0582cc8a8c460fc0daa3e911bcebbfcdfa3143c3a76f53b5c57449d123ac55a4e42bc87964143de8f5204c7803c1f6b848ac8f10e104b8f1765b4f857281dc2e9c887360695663f179403657dc5e7514a8192a4bfdbacef125e9850911424410c89595d492ecda3ba3486fcd1359ec47b4c486dff23f3593b68e916ff00c9b6a26fddb1eb4697966eb4e0328a1242dd9803adbc69cbae1be3f701bb6e8533ce7e85b2fe4bcb2202d6c87af1e40a2905cf86f53e2c7f8a19c5983a6517598341c9631bb0833175a47124a3fdcfa9a3e13a0514ea0d661238e817a2b1efc297ddf40e1acf7c8d9640f2188401f8ad13273fd27b158ab55cfa083203d4099036d41b9b8b55a88ac6b2147f972d689590e72a600e9e1390b06348af2a2578509f4cdb6ac081af4c30b32cbca56d2c277256fa784d339fbca5007385db6445af62b53e38733aa62185ef48c56802ac3a5eb7512406a47a26269c05756c23347c656671d19d685f13161a20470003bb3dc64d05e0b96a79da1a3cfbba0b6a4b7da518ad992a20d7c7d3808f9a4a973d89249d4c7f8f4f0b56008c7031d9d66dd03a20e147d39c7d81f8cc33daf7b0d8a99f07cadd35e58fa735f64b7655500978216b8aafbeb4043591ed24c2b835dd69b36027e1f170bdfd53670284d1c1ac8e563969017292e58aab825ee0d5d1d8c6ddae5e7b4e632c70e11e0a234a67f555fac13ba99e021bb7a204c9c64a7b72f01389273967b46e6a00b8be9879947ebd918b434ace168e8abf4413c7c05bf0974265f148499dfb0df3b5d344c443f10f66770cbe7cd649bd458cfad983be20a8e06db665548c355bcd572cf136ed371c5d64e5584aaf75e138a233658f62f3f9973b722e267021d9b114ef53ae264ff53df21131499d07a5353fc6b4e08a3a8432083008341db94c40349a844035d26883e8c05c45b5355ec84590d88e2c80160857d8539dc2aca3579e306b71c74a74b0dd50b9659f0a0669cd9d7b91ea5139070a3f71d185090b2951329799d61fbe93d1f717ef4b97134df6b8340315e9e53efef62379eeaa68e5e7b5efa463c65caf5c5e65a0c381406c8a2096c90d95eabc61ad8fbdaa3ef52c331c2c3c5a6367f51ffd7f82d3dfbae828d5ec9997f2baebf8a384ace8cc08b209402edc913e546b7dea618196d6aafc11b94cde2ad877a9723f90215e01bf6b3523e103741baeb6443e1f5887c5ef96b4928f81410a83064b85fad32338ef29504a6ce5081614f19d03c6aac212f6a4dca2a3a24bafe7c9110b2d1d302b30fb2a52a6971acaefaa07536fb3e314afb5dcba0c160f51866dba919515a3b782684c9bb266c5665604f578d40314701d1a4233cc98853e2a18f5f15eceb167e61210b9fe83a176ac2f0022a39b31f370ace4543a3718ad10c0b2deb6ee09cfe0c22268adf21f11a995639cc2031f4014467a597686f6d2526d0200fab7868a7d1cfbc4425cb4bd0511380d13136fe24248603205a707ee9ce6de3b54a6c01c671fe9485d0168cdb111d9a00ddc0bcbc0daab4c2300afe7430b4b305024a2132c7b6da32d1fbba2e07818e1d87897ab9e0a829da0c03d1a1d8f4dfb5a4cac983ed038d4652d151c950db4bd1fe7f8b1212685f96fce4ba27ca077938e95a69b669f830c04767ecc810b9a2ea15f74eb2f2afa8de932225833349915a4df85b8383f009d56dc5200fff05b64d19c15593a4c2dc91c0cd898254dbe2b3b178862d7564daf513f0fde0568f12e05a2dc5ad65a607523abfbd131bb583f246d9eb1a5613e919bcf092f3e588396c9cc9ffee38a44e882f1d97d61191a9b68c768ade5645bd03f1044a789dca43909643a2a0ac6f775021de277feafa94440b1301fd29986b91d4a91c0e8e79154d2071e8213218054a950f7522fa2b0542b755b94b7746b7a2ae1574b306a79603f2b0bec8dbde3d611b4ed27e415ca335c11d4c2885c7f3b9d721c83e8bf7c53f9b75615b958a3df93682c5b1c1ad8e918f42539b92172389742f008faf2b9a8244a099a17642ba7b3e5f6b42d8bed1ce88d878a4c7937d98be22ee1e2cc6d7990bd40ae33cce6f3270450ab68bbf020546562433100ecc17692e674e900ec57e8978ea2b1d8dd89efeb8f1b1319780ea35ed7223ec6b5d0b53e1396428517cd115599997800da3d46e83e197a54bd8eea404ee41fd617d89fc868f025747b6cba84f5a7a41ac50f2ead04fd3449e05c92d1daf0fdaa97274b54c5eb01ed4a5747c2e8f989bc3f81d4c7a3682829b4a633d8a0357d4452cdcdea2028986f391df27587aacaea9760e536fbcbac239da7d92e9b133bb68344c658d92bef58e56072cdd4ec2c1631df5a3113753e101756e9202ec98fbf06f0faf5249a038093ac8310c80230050a32528dd2fea21dd1f43426c6643f1f5cb1b7d8de3a032e078462ecec15bf84392dd290b07c45b52c44f9c0b86174e8cefb2819c9f8233fe8324a3045e0c3b8535684107db229bfd3a0130fe227eb48aa6c4b2eb17d52188f5e1140c7b222578ab779d4ad0695086f5db9341beccf93ce4a25cc9882adc19550c958a60920e24b5ab2c3921bbda5ee01afc9562f35eab8a8e87f59e9f9052b4ab098bc770ea1dd4ce91bfa376169d50cb4862fdbb00a7285ad2fc6ba025669a9a3e26862c17b3b775a4bd7899e3a673fc0722f34d43f7d83ae3bf460b7f469f61a4b764974dde522382d90b1d41d5823cf04ed62fe640c91e354b132525c9e8e8977d1a8a875bada7a02ca03b8872260e64b2ed45cccef0d43df3f314647c21d2ae0f0b5b732b95ac79b263fd6962f65074280db9a60ed92fc12b1bb1ba6f4a3f8e24ef6304f43ef4170225bb7d4712d7202162aeecbd6566a7a214ec734375ce36023de13644a25c96abe53ad804beaade4522acadaf67ee40437e4595c5de10760f865c07652774679946fa78a8292753f5801fa3e0e05caf140f2346d72b4cd078af9f828bcf58c7ec76abb90ecb566136b6aafa20e98d08355879e0b27d88726e82998ae223ae28828c5e887e83ebf666a792bccfafa246b97c93deca6229d0f3ffe55ebdc2c9166c5a5208e4fb415229d40f59be519ee28dd3f30afd1f32b4e2257249b804f05f0c07c79e2db8ee17a07dc5224e4d866728e696770804b958f577ec75ff9158649e4ae3d89044a23fae4050f06a912fe37146cc7f2aa3124ef4ac13d9fd17037bcf96312e625a71bfb054c4669b0c37a68b748565c73ad02395d8de420c67a4340dea1a399057844ea36437dbd8c6e1cb3303054a3c23b3043e35bd11b02f06c9b7187605e7b4f82fd408359053461557ff71dbe34c7ab05fe836e32c28b5055eb47180399dc06cb0c12154d27d6a4c9b47c6a8205743e613a36b8d4e3807a1ee4aef9d4b9e8229efab9e914ec37715165299ea48bd29accfaa585ccb8b2564c1167ed833f9667b1a72fd1d48f0443b62f53f039c27bd6f54be3e25e2140280b3d7dfb025cb7ba608f934184365f84688a78e90c59515059de9c65f8ef7be8c87515c3961e8a7b734d1d69502c78c3347fbab1a34131050cbf5f047fb5b202357d9a8d13d8871ca60b6f9358c164030c804bf3bdb8e11746706723c9cf705960e7f2fdefae1f00eacf95c0963f70d6f427c5c77817d234f9610e0be0eca2316293ba85ad9b16daaa7cccebdfe63a9aee710abd835dfc23795b1b5d31b5499a0686414c5ba122b9f2590a88ae0cacb9bf46d4eb6cc125c00235191b579781b51086d62f1bda4e1305ae53d0cf0245dc8899dac4c880c24626b211648675dee3cfe1bd3c844630594113df491bd510c4bc63c230abd718980265d5a2abdab1f624954d6709d3054c95d35d8278bace5a070a9b0d09108ae2a01dcc3a15d635be66dab78f29738eb42f4f8a26d9fe739d8802b41204ec1f9a6be72cf85683f4fa0e6ebad003a764ac5dd70583e458eba2146d0554a01e0527d01422186590539389198f327a08f6143b36323fbabb4d7d8d2792e4161679cc2779bdfeb6b06417fd5173687bfa02ff802b72ae049605579ad7987fce8b2b1bfbb5e14636b8367e2b51d65976e99218405058c74ce7f299a07cf12c9e213ef333c94a5de311f72b73af4c285d3ab383f76ae3d1b2cc7f43b5c5679e77abfe8f9a1f3eddbcd858af4766995b3db9809782fac44b5da880c0c2e2d00056b81f61172d2338ae4a33bcc1c305f2b59f6312ec237c48733eb72135360c8725216be5e6468fd65dd18cecebb12690f000cb6b6dd6d33f273b01ae434d808d2cb22fa922ebe16b026ea119b8d758100773a8897b95a701c6bd28599003f59676d775734ed5c04f67a40d418a30124c8484fdd7614dd2612f2c946ce3e22233987554bb746d4249e4e926ee64380db68ac38fef31972530ae0970e7e2604d83d036be6b597e3767839aecf42ed007fbd2c91201f3cb3233aa4133f253488856c54c21542fee5611e353d26c261cb9456da002a0c5b0e7b593050e6427cca3c3a0359f70307bbbf481151aeb981fa983ca364dc5a7e58401c55251b2debb464931e5af9b05256f6741fba55fb2472230ac177aa1157d7a9b40b60da8d06b80af78ce324142344122acc4a8ad8d5948cf32b1a062c97d14bda015fa7caeea08021663180d025c702eacb37cd5a42943460e2d3449daaf9e512a1019e86dda4e3da95e9dcf5ad0c5f0d0d331e2d39dcac57a10f284ea0da4d1e4b987bc9b6a609c287f4f738422df9f188f5a9022395496f2a220a79f3406a8b2382871475dbcb63df5e39e162430a5f0a452b37632732b21a8ccbe2a972ebdc3d0e84c7aea5600d98307dcb76536750dbd2431a1398251da4966f1a73469e9cb1907d2d52152b669344f3b65e0eb3091f0b219f4fc861545cbbee44dc32d839e1e330f7017880adf4bf2958756b048c05ea0fa5a841a94531cb37df5eb2388ce5f084cfbb76c0d49d62b6fd0b49384f468e7c7be1849ce1f4d67ec02e6ea3f9e7e22c8255e839c276ea84b2b1a55716e261c41570d2215b61524c078486d5bde254d88fab74a87d533ee2c711952c317543c8438e4d5feb3b8dd331728c84f7dc43a9449aeb55737b40946a591d143be4a0267f6a0cbdab5db8288cec4d20f88842cfca51bb10c6b6b154914710ae3d864e860414e297e18193f73eec2508a89fa149ec48b6af1b3c8e27a58c794efb82911f460971b58ec97119ca0aef28c4e392f6c96ec9cd7384d1943d3166228ceab996ca18512a1c859fd7d4cdc81cf8f5dc0604971ea42d250a374a068dbe06d106a871b78e426c4140e3424c8b6505d4a54cff694ef9f9a90cf8b94787cb661b760fa1262967e06e8b1a01e2b0c8994fcdaa45097d505f16551a8b52560c2a642108a575c854140121a747d5ffcaad61c11aa3d85a54ddcfaacd0f45cdc32aee64f3c1410e8761458f75bfb7e6346ce5e6c73bf360481ffa7f243300373e75dec154698921d6d4e69d172eca2457c027a1c7754b68b27dbf6590df20af3961a419cbb5c56d6911c1969246cd726a5dd8a6d4e2d782c94ac5e0e522ac27db67af68bc6ba0acc3f3370067282d4ca635e2af39965035600f98f2f85bc932d628e4d31fd5fd9252bd008b3590d56706010992218dbb84e430001ca35481ae92b6a653899fe8ce03356a8eb4581c8e3fef1c5223df7e8b294517d40bb92dc09c69449c033b1fff064a2bdd2c1ee943b7ece5ca748b7750ea0b3d507e24944cc6785c83c50290aca49e399261e3bf4eddbbe9feed74ec9a88690b4e093eef5d276061ea022fcf07a0508a9ba53d6a19cbc9512690a082d2d765430f70c8f3984c782db718f199636d0a0fb4492c4632eb6792f20fa529694cd0b2ce9c3f91467922962151f19e165142dc076fe41ed42c84201735734b6c4a05e5a8c1a735a25328cb5d50a47bfdd82e5c3be89921e4568a6decf75e615af3c47977acc8ce543bbecea56dcc50cefc296a1628d01221b624d766f0b8f9b50fabf42a05748757a7d75bafa0a2c4bafaeabf9d2c4e2531c9b6c6d7a7bea89d5a1b7140d91051e4eb5db9f22cbee01bca8f77252c4419cc7a1bdd1f784d7ac77bf14a9d44b73cd14c3f71acc93ea0339baa94a897dae5d174ed07b415d36bda80f5a4027e20022db71d3c119ac666f484dda0d77ccadf4c1f620fe5361df7eafae2cc45408fb690da5ba6a86b016fb1249f7d1b3c6c585bd7382d0b9487270054fbed0f046e3e5cc438e91fc1aca4217164207b8fe207e0bdf0b59ae38928db967bcb6e4c2a445ab65e598a64cf47c42d8c3c69511028518916ba753210194cf6a9ca89bdb64247e77a3c6d0ba8283c002b000c0d8bbd6df76542a20cf62d78612c0141d977eaa7c5cb9bc723c3fa52b42fd99537dae2ded6fe4b40e455064ae8d12d1168f90e62d4434e9606213a2e193bf488fe8e65a53ebd23bb0cb4f2c7ead2075979361b26061801dc6b97bb2a011604af9856705f072b05b664a4f75043c9c931cb0284c4b7d1792aac7389615d129aed11e479655f1207667cb6f88032bd5ce14604819e51e4d2815c837aa85c8bb45f177a6835ee2d9660eb35e82e6d29da22673e0d943b10f1c4a5fb6e24762077c03d4fe09ea60d56b538aa9f0a4fb601200278ac8ebd9bdcd46a9c88fcd7189a2fd38df6d8b1ef4947ecff7e1dc6a1f1f8ffb3c6d6683ac40005c473b8b265e4a84e98615b1cb9c9c612adcee42a0f36c7bc7af54d8aef7ad59a65b41fea9a30576b9ded1e037be58be0a64126251c3ed87af261214be9a4261cdda81fb5805734f49fec7b78c4abd0eb2682e02b7de03ddfc9bebadd1bd75a79f39398c52e4c1d6357ccb02fc0974310dd952773ddfbb474eff14d9197946f7c7a631aed6e6812052f74b5dda2c674ec9f82fb60ca0be357904b7e4cede59c3fc375b200782dd782c9b93cd373fa563b3446194914007998f0bac245022ce422613265c63dc07ba289f867c1d0225c86d804493a26abbf2e9658094eae697b24a3073ca28487f9bd7e0713cd142319dfaa581ee237a0cea5b1ba548791e06b1f6728d31de1a0cb6a3511970f5480ee3e90f12bf20b12554e2168d1d7421cc11a5d01f88f46f6c278d28bfe99b28968254247c7212de088bc15d2e5c0d1c1f3fef7b40a4c225a042a19f9f7be014efc227b38c1f90e8da99ca393b1a421212edec3990adfc863602cf7bdae2414c3a7f244cfae60888232d08ea08d4a8b9696aac14da4edfbb468f852d4ec46ec27ff9a9b27ccb085908eeb961dae9cba3ea75b23d3dbfdb5c1cdd9c38c78e6bae5e2dde31a12762f809ffdd0dd4b2932575d66cda7d68b41f8c6764c4863b617eaa324834ca8599a34eb0e8abe7f55f5c787fb74ef42f57c0681dbb39aca260bdd23bb156d0dbd2d16a28bd3facecba2eea2465445113762b4ebdb786775c6c872eb013a45f609b23468210eac6d8a92d58db6bb9d8127acc7245f09c4c288de4476dca56109175aa6a9c06d2a8a56c2f877c7651f033fe6f38ecccc3e51f2c979752ca7a905c9d3bde1f3bcc92642f4561bc958c3ecf38152aa3231f9ce7d3efbcb5524a3edcf75da4e0b7efa0a5c5842956357967460f77976a5d4c651868369b44827d6b98b8f048afdab7ff18c07753a49defd422d9d14a80dc86a14d10779b1d91ad33e8b31d6fc20ceb85e6ac70f38a007729850e47e6658fa31893e296a8b85148e3241b4b92b1d4a169e3536d9ea1741d6b8e7769c281be26d0f1c49e011c9db7f7dadfd8cca85f3492034ce458bcd3ac18d4019b3b7101ca9765c79f14b901868c2020b26ceb95bebd30b922711072b6b7bcb3c508fa0dfb7e91c85ed6343fc1371cad72263cf2ea9c43b0e07b3cc32b7947f696d5a973de5d964db3702c2549ded7e84b715031bc4293c5ba4013891c75f5413256ebbd1621840ebae00ce1e197c269e7df3c941dd27b33cfcaaf10316ca27e7c9f33f73cb1a94c02846583cdbd06c17b1a3553c6e47ba2b2936761c3d9587059358907f738ba5d14d71345784189b2b178340eaeee4e5b20c25c23eb75e42547c599835ba8fddd54358ba73c12658da85936fc052f4c9f86c6e0139f896ac5e46538443d32904dec99093512f7650f1149205af00bc05abb6b17d23e076d10cb013799c0ad3a6eec6746b50137c3a01f44985be1158fb573daaa5eac9d58436ff3d48341ea3c585137923d81bc4d9ae770c46570776703997a772de630a7009112b4c8d7159b3b0e0b9fed47a7e937a219c5119b879509a7a7980c71c1a21b3dcaf4a8289a2546bfb64417ae25e4b8d23c9d45d6117a4602d54403570d4a1f5ce524f05da730182490c4a4f8eaa55414b25b8f8ddfd9074b6ba23598793d8f67c70040412e3bd295a3a98b500b8a682a3565e188bcc6fe20a1003ed3635c083c528877904f3dcc72b448b61deaac5de552d57368b56862cb39cd0a0192995b2b599a0d36a7065c16af8f072a5f8fb429e0081ad12dfd1a19b4002dbe70688c2e2fd7b6b3d145464ae94f729a491783bc1c158c9562d2d09548b1b34a6dd7b71ffa035841ee85dc5ae2ab1a94ef22d14136b0a4e1567481cf1cce39ef128838906d25a2801d6b74016353fb25194b9dc002be8f2c05f8574cd59161f468fd92cfd17e3a6675bfebb03ea8ec4f8d727ddf0658c2a096d0899789bccac1c2d13d93078947bf2aef40b1af1480242bfb98cd7219de66b30e3124140c467ea989cdad6a1517db37f208801842d98f9ff6c6faf2110da7b9c8db5e3c62ee3f205a5b4ed1b3db07bbc79fd669d85e65e85bd45231735dcde3aca8bfa99815bec953e06fa6448f27c2ad29e9ad43039a0ea6fb21ecddb0ac6a9384345fef28998b7b2c34e2c2dcf07580086fa91751893b825e1bb7df32c4fa83a962d69fd374486561ceb9192c5fa61c1a9b6e759248720cf0d3af2e295a1d58ba465531b5831d8a1b914a8c1ed0e25cda2d4bc2066b8f350ecacd463329f3e8c0714b7a2f330e82a0dbc9a7b4f2bc87dba1ccc0a73f92599135af1d76750479508ceb74be7fec7fe9ba4dd8e1772b4893afbef0b71829bc39cb4c5bb8a76c4ec6346f026b80ab4791d9c56a95d607982ee5837c481218ab86b0b6238c34eff0522f4487b6d1bc403bd1c37c0acc42cbf98fa361464c7207c45858a0f74064a580ed9aa8613ae488a8bc1f401130a531c85be80acdfa0e190d068cd7202eaa148ef1c9dbfec336cb8ae3575c1a063e1b56c3a1ef1fc0d29cfe267bc7a54eb642188676766c2568087cf429d3be54c459a49c9e20a2fc5e1c0dee52635715b696e65a7a3b62a93c1cda05b73cbdbd7a0918fc3cdd21334656818ca1fb5f3341b80bcedc3a16f77f66f0ee4aa30c4aff149fa3197a43d4b5c30e3d1d11bde10956feae925bb5283a5ecaf7b0506830bcdcd1b5eeee50f61d28542cd4614abc20a7962dfb42cb6bfaf11b26e4ee5103eb7080e9eccaa4244239436c6824498f4b99702b160e5fff77ada3d68f4fb41a7470e312131f72ce901070563ba1fe9f282e5303601e5623155f2481da5a1a17d91c70f02571eb659d1713abd45923b041ceed76ad0fc6285888f99c6e34a90666230684863bb18634a72d2d6d2d2bcf4109774dcc4977fa0c2bc66ca51acb16abce184b2c666a0c4a492d6eb5d350b574541c0644970d75772d077ccda11bbfe6dcb9878b4f8757a6ca13d69d1901c8d9329490134e9d40fb23d749ab3538411196292730fcc110655cbeeaa3ded9851a3d10d3d9b1ffe0d2e092110b4c4d548a9c19fdb245b41ead35b46b41a9edefb40191482830c92e94e348a07f218d14a5f1d5824bf42875114bd9265756003375a3ef953b7631e80652c7a9b266950996bb8e4c8ac66991ad40752e3ee247b54f7edcf565e12d221fdaafe5e152a041060b93c9d4c384936c0ed39d078afc4b5e268cbcc34d18c0de0c59afa8bffb9fe1c4fb962bcd9fa2ce80f68604f5f4da2cd7d1bba56c55a53fbbd7420084362e957a5d70f697ecc88d114f9d04c86d26c65ba121fb33b1abe5807eca71965093c04d9e576080029cfec09e47f95a3df29d30725fce274a9c5493250e2832a2b77d9b620671e5f7e5e90dea87544f888d689d620202accee9324a1c8f68088570d0dcf743a5f8dffaabe6d18bfb07da6ad4a6a60a9e1db60319257fa198c8a1875d41b169ee8af9c0140d1c98712991ba356a92046ead585b87dab195fa681490227ce9c028ee3b4b840514c038b0bfb4e078c98288afd862095ceac265cc2f4a8ffe7437d50dd6b80d24adc286ce7a84a2960106e56ec64dfb4bda0ebacf46f2f4bcf7bd925607599d2003f86fc1001ef862ff1d1df947682c35a0af309395d8c61799ed4708ae99ed6866745ee3e0d598646b311819385145cef1361585ae41fe08faa5cca47ca9f3ed03cbd04dabae0ffdb5ce00e938c5cccaadc4dec13a52756c9a0ffa657c60dc5c5bc14626bea2ade2f981b0fdda1aaf1442586911b7248340575fdb25b0ab32047b003f6841c704fb7229d7c24082ede377dc021e8e5d81c497c6401fa423bea20eb6be231819e4b374466f80caa3af5bfe55613dc1d83aec5fcb6bbeb50e6ba41f03c0c60485d5b749611e0d2e4ff64ff10e0863a19da84be9a70e5e72bdadb5ac931f62b1b5f02c40cd0eefa39794d656b851aaac32c233a1ed84ac31eec33b97a8f3b17374df3ca94b52af0604008fd05708b9292027965cdb7dfd711a14ed4df15834d940a44f27a69302013ea10a47babbb0162c2493e6a21092bc41bc4725823dc16c69da7f6d93077ff6b1337a36969e6386dddba2d0f5b335ab6ef2e9107db995260411e0bf329c544f0ceaf304eede12892337c8607ed5773c9df97dae307698c8fe9976edb6899e799d1491866e4a9132a9c1b8cae9325b64a65355a2aa9c4a01f8494402c7af4b6f3327f58e23ca78d712b3c06b92c5f8e29ad57da4498137360ac6e2ee6b20bc81f400c45b3ba2e816b4b51f420d3a043876b88299cbc54b5ecd2003dcbf4e95d7932c89a0dbdf445a59c9de5ddf571901c566997fcb6f98f3f8ed05b7024bf944d566d9d2e54d78ee53dcd9e0c6ede698d9073d4411c7c262b5a57ba8e06f7d638dd7ad07a532a1958640a715c480c24b481a10f7b0347b7c33b6d0128b05303a41dde49b7da8aa25e6faaec261d9fa77826790d1a532570c085ac04142a14410c9a57e500622a1cde80686100ddcd48c581f769a1b1f4fc29f123cc2d37ab48cd00c0a7fa16521a6bce57610852438a5a41b5c74c0bb46c31134ca53143dbc8560ccf814b402b2c03298b8b4eb0cf97769878e13aea57f64f33f5ae4be83c9343df6309c642fdd18b0ec5f64fb3ba9f8cd71113fa415b917b9d3521b85372a60eb5c443a9498400d83780e8a21e0160aab1b603e3e8167f6d52648e48398843804df3ffbff7399d81cfb2fdfc0edb81d6b536670affb83b38bec67e088076e6dcaf5551e7a4489ba4ef976b95f39bd45dae8eb2007fd6bf1d67573add61ed93626755342d4c1a44055056d021d6e239e9f3dc54f83be10403a70b6b8753b45bfabc71cdb5389e525f68cefe0e014f250b6b93c271fa4918b45721a491f3e1c6f3c5c0c3f4a951a6a22734475fd3486326d73d9571339a117349d433c49af19157d1247751e471e5595a24d91bc437bb8cd34c00ad737fc1dbf3fa5fbc7e0740dce264654be5013a265e4b49081b832a9de77643fcfe3af5358a3a99d7924b6233111d490632b71125828360bcc49c140ccc5dfa4ca4a0e6b90018092b58631af0c1c08a4e302e36661ad5c933555572c6efbf23237177d69af8cbcf445682f05bf6ee248523a99d785ea4044c8c85eb04c3c7da68a0751005fd40b46a16d4fe82f5d97aa57b7052b6ce60519242984d7aee7bf5f86c04fd7e409d65fb9d001d7d99aa036d31c239d15053a83d9b4614800e1d7456811c54530e0fa7c31a549b89694fe5c93818bb4e3e0014b73f0bbb23ec711a941f2187fc2c38e6fa032699d1a03e30ed35a3f90d89be464947c2229c39480334b229f6470007ef50f88e3e954337406c4cc9710642dd079f699fa89e03fce75349c27d87a77592d8c8046b8701349d9d102c3d79cd9bc7201051b042228089e7c58ce5e8fe1e90deedf6ea5c94299b4c4c71bf938c785c6bfe2514fc366b7fe082c230d6fcc66cb18617d552c8ce9257a415cb42211fad918c7e526312efdce8c3b7e9dda8fa9c6d6883d139a9064c857b6f72005f6a5c8f78ce1ac0a83d749628175aab1c50260fa232db9538e695a47c2b15268de29bcd233587214c1c5e96ef1b1d809812ca4bcaf02cb05d0d0cc5838db9f3ec3b558be6264b68e92787f1d762ac150db1d0f7791cac942664084673a77a427aa94333302eac0ed11b248fd6874e988f9f5f5691c1337de4358fad0dbead55607a6c33e332d51e9e23595180047ed7d8ae52bab5aa12992ecfeea93d90ad07d17dc3839117f79c498e1f44c99e3e225f4b341170aac0c633a45ebd07259ed25382a070ca47c744da457988fc1153643aedd081af0a34df731e1df4e92f075f661d0a49a12eb77bc8ffb2100f4a9f531ee130e23567f85ca52983901840b1957c405b2ef82dce2e208d76e35743418be58416ed084ce21e68b2aeffae948c5a71713c0a688e1c0d4a9d34e25488248a24868cdeaaa8333d0c048434eb6565cfab7369faa0d92de2a83684ac3023b39b18053742e479410284088ccf83ad016cc2e49b7207dc90331850cf754915f4343061884a585c9b0182f5cd390b16ac098714d3afd90378b7f59d1451c252130f6f80eb51ae5b8c7eea1174a34c5323d1a6a7d056a75c73a3954f9c4ca3db5505385f6bbfbcf040b6260a3f135ee62fabb62f1bc0b05041aa536e9d7123d5f24d8530e538b0e8f925a02c54000d66b34ef7f94b7b8983682ad135fc33076adcb3e7cd612272b14618b7ea3b5550418de4ec9e3cada5ebbf9b23ab0c32363754814ba99b8e5eb8b57d1ff423a382a4d3c76f27fed77f7b2a84ccad3de1bb5922044e1a207433040f5102ae247e1a7d6b52b0b261da426df64a2b4b75e6f1ccc35c74983a54280c87b3b1914920103c404e32a06a2ca79315b1e22ccde9ea64bfb64f01f7243894e5d5194186d3ccb091a8ce0128a584332c28d5ad1bff2952d10a974e134c8c758f491f00d979768e6fcaf01cfc04a8feace6325f1e2ca716e165d10426676a2af3cc6ed67e83fe6a88628378b7ffdbab527db6ff5b88fa0cdf6a82e62e44677cd6068f14f9e7551103e02da444b84f172efa15f18b4871cf06ec0c4c9a1c9db18307ab0d2b0a56c71cb9475c1eb308235699308ee7e0abeb017db96663a55b602bc1635d80afcdb9de3d8f568a3a037e44ed9466c6d331ae3469a842bdf36819f80f4ffbad664592898b5dd1fb0cbfea8017080410c9575e0c25fb3c4c06029e526fbb6bbeb989d9d2ee761d6011e6936241f82246ada35ba4e8c1cfee829632a947be7d1a06188f16db35f45266480f2f6efe6eaaf90e2c50e40ef607d447031f1c4823c96fb01e7a52585afdbd6db16e756609bf3a8d33e08f497b5a780087aa6338d09f43ef37a6284d3bf28c5360a5c9b72280a039fc95d75aa2cc6ad31ae8ec4156e7a8e45a1b9c2c8bd006cf9b144a6dc873af687c5fd9d9c3d2665b2cb2a01e1da87379d538231c62048a95c0253ea30884b939b0802a815998b973fec783120b4fc6d359e08511ff5bfc8ea456b0068e0e4b7388924c8b59a247a6f5ab60baba0568f3c4cfd029b434af48ab3d9822836da8600e3c3856d1a4df27a7e84f4d49ffaae15567c30287adef92d518f3a8d72d231c51a607c812d79526fbea80236443e81295bf3eb2ae737c99898ffac4e9e90228b4b64351695c73682816f368b16d36c74761688f19f05e4f374c71d517eede16555b2a33dcaed7096d81c5f6f05c1985908ac4271588b6bb091478e843240ab77890b8ef887a3462c4b4f63941141536408f1ae0aa749d732173c9edee1b17665bf51b606a5171dce2d1115784998c1e430ddb86bd4f33a29d550ff3daf7e04cc4261e13fe676951fef2f2725eba5feb9e4e7d2e17f32bca7d436f47f4589faf0ead5cf16071e384c3f4757733308794e749d668d01283967416866edf1bcc501fd2a89ed6a8866ee43461393f0a5a579232185594749b415c6718557c7ada3d4f39d7e803e212e3efa1306581d12411bf633b092c1d8a902304b6b1c6c555f8e48219e68cd39b69c6d7232714938e7297288afd38c588024dc37feba27ae070b566fa58e2436b4cecc01bd3e05d6b91012d8c64901b4e433e852b68e77e06633dae4d981c59150001cdbeb91b94c403887a30939b769ec7cbc266ce1bac41a86f353ef4d964625eed3d15682c72050fea57d0bfb1e7226510a18d4ebfacdf90395e5b78dc05809766df5793b157405fc066397969ee40fcf8b4d08fdb8b8f89087b7b5f81c07d6778430df7e63c8c1d9b1a27cd9d1315c141b017c2dfc800c0c4ed194f9f46e10bea5188d25415ab2c2ce02a29662579a30e53dd908ae8dae45938f1cf1a257809dfb8426f5fd6b4e83c6da0d70a60943492424120ce6baa86fb511de84e978a43044267703ffdba5f99889b9ab254358342a305c656e574740d93833a49b79ded793b720d623104e9fcb3f2696d6dbe2d981e2f3dbb1df784f1cb98e4e3100b490d2b413c7c69cf6851ffca3f28134a9babbc509fef9738f8bfed3e535dbedc607ea0294aa8c207c12b0f3d1e72bea6d385f10be94cc8a8b96bc0c8cebc69cd16ee24314daf4079678944bbb12867e1b03f921d4b88ffc28c5e3e7b631fd23289e050ae6b50279b90790a5b830142388a72659e8953882020ff3dc6d46e65c16f0b397c55c5a035da1d08115f63a55466a1493e317e88be2f4c2189db688c37895aed10bc75045596299e65c92c4a8bbd22b5d74cdbd17e1ebb345a2f91ec29428f115ef9d244c45610a067477117b60e4410d7907bbfd8fdf145c3ae83429c00df2e62c9aca7d282c21cb424cbc86431804a29457b63b0d223e8c3884ee05861ac5ce23d84cb102a48914696a115999ac9b3de3dbf8b56a72c881ddb8f790a3cfbc0d7afce47d7d38b6dbfece5dd70ed12cd81845bfb3a7329001ac4584ab5788c22c833a0f5d7ae3a31eeb0cc246fa76911cd2fc8990584f6635cfad8c4ef831c1b04d89c181939fd5128fd627be407ef1cc8fc7d4f97b845b8042e0a7f8b4cef2c87373c8689bf8f74570b94509e60c1dfea64d69aaff3aa90fbb72aa6ab203283f72626d6c9114718d4e297c96767591611128d12c9281047522e50f498c209303b19640da673aea6082c6df8d9a8fdd5246a00d2a15c8dafdeb1b067ef31bb3f084beb02286b75b70e1131f7d570142241ae28c7cfa1ba7977c4208d02b8cf97b8eed1ae452635d92253f81e37f8097f698141a0645ebf7a526bf452ee84c07b575c4c51c7e748579ed3d478a9fefdabef598b6222dfc7debd8589e30681cd5ac9930687e7199ecedbe72d8ffb2b41d1405fb5b61262567ee0edb5a1b2bb69246d2d22e89f4051d1cf29d6a237c456749e40307e78a8ca9f65732d77168f59133436ecccf1d8350b55beb1c97d09b4c9b80c0ead0d384b0506526f8c40ff3456364dd1ee10bd59bddc51a89676002b3e90730dc6c54e6b1648869bd569676a15c03754439e7cfec75c1a7674c1ee014c766cf8ecc3368024e17a56a03b56923edbb8f8afe18be705d8321b0830915466cd0df8d7cdc288a98437f51294896746d4f1e08a27b831155e5345143d4df5b5e03f37ed514d7ea5080461790429a587811494217c26729c1d53d4a22b7c3693c439b6efbcb42171a77a80b639438da0800545c4b518b5cd1c7ee1ab77e08be63b61cac396cc6c3088d2f164148a8a78f91bf2de3dae84d4985fc7b4ddd706115d77fa188adda61827e9e0974cc0a24529b2a032b1c72b60580b6ae7cfa709a91a644cdc057d437061532402c601028e9faa5c44da5f2878983691fc02937130826d4569cdf5b405dce0470846f52323c9a3bea78dad4b0a1b5ae1e05371f81250f963afbfb08eb41e16e4981f609406cd8bd578e0fe128351c51956db83f4466a085a88a99f596d5eb0d8d4c57cde1aa3f489c22572d9261b024ea7be5df6e42fd595c0ff146c865dbf888c72ac9bfe8503e6c4f47c08a87db980ed1554c703602464ac8a303e198b6f95e3c23d17a718237e78db4347b918eefcd4b2a55c4da365064695120951020d7a6b0b5c5449202c2ee3d91ac035774e57096e2b56e2831fb77b390a16dafd51daec6c40c0dd210f14309edd1a10cd83f15cb5fa4e213baadf4848967ff6a9ce97d503883d4a303b591f2b149a2d539ee5cc69c783549757be7be9a962f4864caa6d8295c311800931c53e85c222e3279055e0a22ef732cce0f44dfd0e40eeabd1d859e3b351f0075e92f269f3425d1beabc9adfa57910b687eea16ad20e3b32d4eaa368d3c01a5fb408108322d21e053e80f25acf3c4cf301f20868503eaad73805ee9b607a6d709e6759f984a83226bb0c15720832e3eb36ad088c1f8840a0d1083181bc0130a906f32714bed0167d53e452f870f716bb8213cabec020803a2cbc32a690616c5428202a78c8985843dcdd97214745fcf8f0226c42b8d5129bb68ae0095d2e2f70e7c025d67948f9f84438e0f8bfb76b057b83fac774468c804d811e1016e65dc2ee9a3faedaa362f20b42344ae1f581f4724517d19a53c0b1f109534473ab9e79f3f24925b5895034bda6f8e7477326a3afdc974e80ebce78f87f70e475f037e3b7b8cc3864a48254923553b65d64c051582058171ca5a7d35f412b6be872c0b8a4acae8386b740711412ea86cc2e7d399606d20fdf0544cbeae6d8dfeceb7a0b87f0335f7925aaeec9f5813393c9b138c666b0cdf54b9b7f4349c0dd23bd46b02132b6ee7acbac61fd44f0fd669e3890f7b811178db9286b769dc4de16bba63c21fbd6e0a4e8790133fe42c22fbf449a2c30f5ee348a62665f5595a3c8fe5d9408f73503dbcd22da63a801b5b8b6f8b798bad81bd290fe1665fa1fa386f0f41d83ed7e453c1df6e4b0724fa163ee5d1a2c2b0041930bed8a89a997300445520f38e9bca0d2dc9a1e4f77d871fc3f57d67374a62cda7c4d0b75477452118a9b240085dcc3e0f0ca2a241989b3b8dd9957423d160badf9d162c7a6def77bcbd0242d15e2392f8532335534d9fbebf7fd4dd7c6a24d8d9f2c35a96f9258e1be8f8e8b2741206ee1562144610e39534f58893bea1af153b4987494efc9afe39ebf73ff5a6902636f43ed9b5c6e03f46aa1875fc43f1d17e43ecc0d4389a9722750623b6c90dd834509f7fd1a58e42c47fc37c848863d1581ba815593fbeac3244c73242c06e810b55e9a733d47a1a7e6a07f467a14341e2e9008950cc14e22d1a73cff5e597e8bc5925384a841cbee470a0fc773836167ac99ac876371122eb23909ba4caed2519fcc762e2f227e39e90c2169477ce435f3bc453947981b61e87f69cf80f3cfd074b46033571b8e7484512d4f6fe33599c8206c6ea49396572689d81833d0cd5f8d85ea1fd334485c3301c7fadc09f44776cafe8d66083ed6d54afb0c77f261831e04858769632ada1678c271228ed89f036859c2af0c6efef13a01201f2b5d2dc12763e47a9e8d2c7b990caf5f9356a05a1002958042b7a5cdb7b7107fecb483337b1d84681a4a12838c4f68702cbf7a5249cb13885271c926c3c41945260af0e4908dfa2e387e0d8e23e03a767a2bbe6160ec1fa6c9cda0529457e921c4289b21d451acfa7af0a8599510f652e377291c94a7464d5340d982b0e85ddcd5ebcecf33ecdc12f24731af75a795a6862843215f58d37c6c80e02c99a24ce0d023524aaa0f07b8ab211e9a198408392f6740aad7e07253de860e2082a3628252d1e7f4cf9d5de6149095d0bcf9d829b53895749fc3eaf5f7f0f5a162841f43e520a5af114b458533dc52e04d647f9861a45482eff6a577f9f7376d1a98d64e572be1091ce145928ee548cf2ab4d752c726460ff0daade17b97c2e91ef9d718986d02677b45b50ee6bf95ac6123d97383395c1fbf20bfad7887f25a80fbefa7d289da6c9c056cdeca6e24b763cce894cee0bda816f6df989f9b173bfff706694346b830ebe15c0d1a910c553041929e9819d82e01f271b4ca8c481eb3305c70e10d6a02188be4bc0b99e6d18b2d0f70c0f8930dd4d43fff0365d0400b4b280e5e938abff4661efba48725c3e571a514edb92a74b3a1b0b4b44a808fe802870d0d5fcf84e27f6fc8c7e6bc1d742100ac893a17fd17dbde95e2c82152842709fade47c47da89f8be8e2c6a4b5a47c6b10c07b86d14362fa7128c33be0bc9b339f45c22baec05d1e925ef37c428c54369bb797c718ba0de36a0657ae56edf92dca98d04e16b24f9aac0caca438308ae23bd569ced72c424a0316d0f8b1509eff2334cf899b31aea64d0200e2066333a9b8a80edf8eeab22fd0bdbf30cbe98f84024047e96a8fe3105ac4a640b8ceb648ad7398050f348975c5fe951d0bc2db834df01a00b458f3de0ba4940a993d8f52b54bb76cd9169babeb959d9f26e8e197a0297d98b35f5bb04d40632ff213e271f6069212b97f8618ce5f215c112127688a5abb34d4aa09d452f805b0d4bffe4d598a187b526588698969670f7d43fc60a79bdf2cbd8276edbcb528018bfb4c89bd19426015ae0ab45d1b52b585a2a06cdb6368b652b60e938c2ce79344ff2cef1c6f646cea1b223ab4be40c3b3e99171d55ff3f1e11a4ecbf04a40099302b853f3505ce79f63c9e4d659ba2c9712147e995c908db5899f44a6ac6b3968ec1d1e66ac8113737f1fab807b79a95db646cddb4ff908bcd90e155f53fbbe32c98ffa6003c6bb6342dcd4604c3dff9e195d40f041e51643b7eef36f443e6e4c867da88e450762bdfbd98b3fb4c86db8c73b7a386c7ba7c6171d1bdc3b12e3aa39f82922b434802ba951037b195695b4d49e1b31c479f1679b44da3d8476122a8729dfdbc771b7720ff95ff2bc2d3255c7ed0b6124b935212868b1c80d43f4898102de3ec12227ad36b039a0bc73886dbff0b4e1dd5fbd2a4850b2b5401b20d0845ad7d4602cd4b0fb8b033834d2695a2fcda1d848fe40285a2085218ca92c2d9d09a0ba3b7c0a47b0bc3019e25d37bd865e35fbdb4ae7f1dcb3bf5958e6ea3490ca1d5ea21e0a0e579bb2462a606187bc6bd6b0f9d8c833308c945f8fe37000fd898c567e9ce82520d8b670e312b04d52f0126fa782684964128bf8e25d3539ab92e824e762c8ea014d0c5f50284278bbabf685d831b4a25836bdaa19aa28f42bf4312fad31b3fe9a5432454bfad8464d79a220eb7201c71704e1dbbf37ddc90cc4467f6de2743c02f68dbcdedc80780f090fce404bf70cc4f3075c4da5779f112b650348df88054a881c6ac61d4c58879a81b63b930a92888943f8f09ebc7ac41e82275916dbff987e4cfcf9e0a207ea4adf433b9747924c7acd8818b4db2ce8adeedf91f745333be9c9022ffbafd81c22a271d0884c5c8406a44b1ca801c721d0c37b2a040d2698aa0b11f51435291f3ee8355ca0a3ce17951f14f97ee1420fc0987f10f80baf1279c1913f08049cddb3dfc0946778a5ee55f4532c35b4e59810e5c15bc92919f302689923af3a530aefc0495e0d8414cf0c3027051323c8b693ef861b9171a21c60ad7219b0de2b777031d2d1909e779aee709b62bfd4f930f0ebde7e269cb10f15293861a517c7634eaf095e8e4a010d0a559866bb9a115aeb5d1b23df5b80dadc07ff78bbe16a6de4a8bb9c96dd45222f9691dad32089a3baa5fd2e3cd02f708f5dfbf16ad704485893e0401cfe41d9f5b8fde1708b764b9dde8937c10218bfe7a7ce26d55a197fec0b8cc122056011fce12f494a0e580f12bba8bc4b383621566f277472378cb703f261682568b769a30d127a5647f4424abe1fd6e95bd1b0c8152b66e050a87c23d36d3a757087a93fc4dd14b58b8c3f5a3bb8ee8f6eed93ec8355068f2d8edd51c429a055041de5d1e9ccaee8e9e69e51911d3cacb21f1c8978045e6e91147f8b1680e00123efa6d43833ebf308307a3ea80a157228c885ff219535ef270b9585c21018603c31e061582cf235a159213e8a74c27fbba6d1b0d15be0097b01c727acc3590e96a125aef25614cffa125211b2c953fe4ed2e8948544084fa2d82083f05d15cbd9875538eb5c21abb7123ff3e6b1fd7bc6ff468cc3fbafb29a8d9000ca8021571bed6ae66ac35813dc1b4ebf78fbed67b573ed5d66d141ae4d50a5177467a224345a5687e31a41e2b179a7bbf372d6eb5e83ebee9263bf75fa2bbd35aa22156713578bebcab2632057a949bcee59d23d9e469d13180b3f28fc2700d99a5300aeb7226fa041963e1cf61eb2d6a20921fe53b3e8065b45f5d2c16d52036b5413c54b9615716aebf07ee577565a3064a65019d69d7cce451cfbebe8317d403f1c3349f09234ab4cdaff2b9c3205c82f0a4240c715e313c89f93bc1670838915b2f97b73e744029521bfd9a318adb9576d25d290a3ffb804c8b4eec73a1a32dc140adc3b8352f5323200fa604496f6815cf19ddc60315e95cf131416445e09e71838d105e5e6467bf374ca3151acc10bdf28c1f4aaa996794bdce4094ef6666a65f62649e80b3408bceb856092f1e7724915e8137af0ab4116b0d55b044f28939a3be786b660470cdc43037455eff53e1810a1e20cb47476660b1b87b0649545573ada15512ddb6d4e9922b1533a05dc5e87c9384fd9434806a5b1912c1d3314b5279d9e55e506253f3795e9bf31b0694418acc33e9ea731071b9045c1c404568afdb9e9634b22ed85a2160e9321eee1a8d6cabef6cd2dd4f154030c97c9637f79e0792271f68e972d02b239ad549e1a0d8695b058088788793e61ae15ae4b67861c8f1bb9ec398a22008d241143042375e896993773f3c0b60dc2398f9ed09ada86a401f495a5df9ceb5ffd26fea8dab0b79d57be335e951c64c749f5d59b9c8d7be17bf418dc90dde6f6d8637aaae3167f696d026f538219c18a0afb0e7f666e9afd46d7db0bfd8dee2114f98a49fb14dff5afabd7fed441b63ce64eb8e8befc1d7e68df20288bac52db24efb5b38d62f9255d7bd94ecbfa9f1cd0ce67c3d412b46b0d3d2148df98185207529ad7b6f6a2c332faf786f7a90af049f768e320ee7dac4b61b3595d1ace1fb1ade0653879251e12997646bb215f5d6731419d48b264ec5afa4fbb6f46d5aa7d7516eca527419983ee55acd27ab79da35e97acbb86596587dded598bd4a57ac1a278dae8cd7f5f7d246b3f657cef3fe6a3463a6d9db79e66e257e1454aeb3c51e35a929d050742819159e3255ed2ffb93ed6495523f8dbdd24dadf71c458edb262fd5bb795dbc6d8f9717cb6b30e6096a84dc4ce57a91b14f6810d8ae5ae7b549dae4b297f9041acd38d572cbdf6c1e3293b5efc9d354465db3acd3cd64b195fb465a1e839fb020a85ca6f61b953c424381e6ea70daed25bea16637ce351e9fc1633479283114de261e23a1c0586824585d1ce7db5ed05cc6b24657eef3f669fa1cb1a5f9e2e5a8e2bbd647f01af78a4720f3d313d82b79f5c1d1ea26684769d8521992d041e4e8f245e371dcc0fa6fc08a50807677f977c3cd8e1905ededdbe28e7454b9b4387529ecc312b9ed96b2293bdd9c6bf48bde18ed74532c34b74f6ca0c225dc09ec6895d342d8eaaa0c056a7403945cd5f61fac42f84036cf2b6617da5fb01255be1961a2612a55c7799b428a77e2766366e1bbd6da563dff9a0d2fd08d7b722fe07e97080828f60c75a337879aabc444a0bec4271e93304c16b0eb894f201a4bb22af72d36c480fbd21c152456ed966069c8b91a236c54ea7a8f5db18038484286390fe962b3e27138c86e28809fc72ff320fbec63849bcdc3a3d87307968e1d31f60c590649618849588bd6a24330ac3fabc6e83f435ccfe4c92fb79764d5e17f7114f46fae82a17e5e782a8e26c8f96b390b9f75f68b63f5d2daeeb497362dc3f16a79485bdd99e3fadf7e20ee1d6f9ca2d51f80a4214d9e7aad6aeffe0649144bad7fe9dab6c09be4ec97ed29c6eabd724925fdb75a63400142b238f67c558210cbb77e6958a07053270c3d90ab57179d3229a64d9e48e7fa3c6121de0e45c6b8ed1dbc84f67b6dc5725d5254daaf5d4df0058a9b40adef361d4ca70be3d068a6f7afeb2bdfb0b1d632e563b5865a90d3efc739dc31bb5ccbd91c0f26a7ca3b0e4b316ebcba9acc4c001cb44b6a050e7f972f9a46288b6571f3c76fa41b4171b864e22c2ff39d10f8d8aeb03d21ce4ba8c487d43f32340000fb26a42075abfddf009d002a510a30619948ee431122394f68fea79475f7f0f103dc9f7dc760b902e61298c6734e9885150a17faf0eed7fc1cb81caba74e43fe29c9e9aed9af569ff3fe3fd40ef48eb5626938b34b36b56bf79cb53058e101fdc65789c0a3d374127e9677dd6bca82a249f8e00020b9684e6ce94e417a74183b9c6a3f07238a6a24e74835ba28f07c3ede57e05c19735b9e7ffc6997745544f197d1bc8a9c03777d5d629fa91c23c15b42a49084c47380243e227d1985a35a7fc510968ca31dae5904915c720f292f7019b34d5f060e7f22a3ae1794f12de2eed64e4fe4b47fbddc9e11528c539298fc97819560b2c4f68f39cfc3d66dcd9457afa15d9343205eca96559e99c8b3671abb2e4c6c1bdec6ccec4bbf014b4eab71cac3d90c569a0a66ff8870d054d741a3126620ffc4e187db762fc92a8469cbe81853e5a69acf44c2ccc56c84caaaea426b544b1417e2986395cec5accfcb0e3410211a34e482c84faad8d29a341e9e28d4eedf57cd569d50116b499eb0e8f59696f0b9a706c9b2208b9fb14be8d33d844ee5f2204c8532e0c1cb754b50dc18ec8a5365507e30d4da5361164fc3ba61f8be71ac122b0713354dae71070bf56116346dc6303a1dfdabf38cbabcc7f8381bbe7a5b34fedeb2a6cd0e1be4d350605fc4d620815f570a0b35e06359bea2385fcd67221e2321a2f80147c44b6d8c435bd94b469c1d6995a14c51a31622854b97d0648cb745baa1b7914d7c29fa07a7097d9f06115a1521a545ac934416cc6c7a5df178de0dcae655ff83e63b4c4ce4118de308cb2efd5f554e60399d22dbc7201a837eb48956b0f8e85d5bf299e4bc01848cc213046755421b31255b82278821c03a2329611f6726421f42ef5b8d6f8c1c440103dc08360cc6869a1664f93e19532291fa6d0eb0c3b83645694c3b878477ecc97833a2fb17fd45a5352f6e387be7ae27b25bf767d132d0ab86ebbaf2803553fd88960578f9f31cee0131ba8a877911079884819423f52e5350abf12332ae67f1fd266293064c2a93eb29ba26d226a8fa6a5009f4b3026aceb95e6050901d710e47d6ba854efec14995ae1ee7fa3cbca4c31e5b99f60143190bfeb2838bec5efa5f08f384db0760118f3f55af72551c6da0a39edb430ce0181a2508a5b100b43f7c44678d5ca2f8e93021a0c07fabdffe777f6f91bd456e29a5943206060c06b105f7de8b6ac1877b5235be1e353549dc1e363e3a7e66703aaaccd864d4d022b09179c4a001c144605c8b7b88616a1bdb01b3a9d6a29a7c34be1bea33c6e3865e43e923ff9471a15a6e5f52bf608efd1acc61735ab19f83b3e69064800c6a9f7fdf4b73fb77efbd74850e750de6d89f418dbfbe8157675aaf9777e74012c8f1ad63d169daecaea7698fb5f05ba1c1dd2de8750ed47cc5b4b7daeb690f7e1c073390419a16de51d755838966a0219d3aad9d39f41eae1e45f4b4d649acf3ae87927eeab1828c839de281521e3bf020f28310fd74efc598474fce596f3ca2703d6a708e9d7a69ba47941e34d87cd810f381d37dd87451f798a2b9988f1ed6da7b392154d3b49c99701fb66ddb38cefa88e2c389f723c7ba623ea6807efce0ede88fce0953dfc67cbcf831feb0f9b1a49fea0a04027d1f0f273f860809d9a91ff54747950b92814522d0341e19c80e223dc476c0b69db4346148136647d9434ebe19db31db3104a9b48389ae32e26aee9812e341a3c84ef588e9f0d1e1a4cf980e26bdcf980eb2cf988e57176faf3a7474dcd0a94e9f3fbf62400044e410841830c6181391430e740e3db8b8073244d13a339e157ca89eefe7bbc1477e3a3d8f42f719d369d2c58fb1319d034a134a69ad35b11c52eebd17631cb37c9373d65aa7474c87049ae3b8bd7560311d241da7604ca7a79fea6ab3c1d6aa237460e04086941b3e9aacb84ffacd41418d8fe48362c1e7832f470e8c24aec783c60caf2ff535ada3a1aeab596564e80cc65496a8d231609f76fa52ea16009d76ac54b77fc18e05cf13a63ea5dc82df4c89128061cfba593347ac2b12843d653bf8b42eea188e1702c80963c22b8b090820b1b0a0e8338663d6c5125e9870696151a550a71553893412853e90d7f1cd6d59c3b90473565a5f3012857cb0e9cf580e1a28a5b5c67094b11c26c8e9a7baa2f596a0a200e4757c739bfeae0c3120720440bac0477286f0d84c7272c0c018e328b11c323ad7672c472bc7d6543c52316a80aa810d88f004558423504bdca08bffcdc84117bfd4a8039492aff619d361073ca8f97f145b9f311d7c5083030d574e9f5d7d13aaaac725801020288211362f1834588189c8e146e7102307193ac6a9285a8725bdeb33a683912e7e7f6dec9e3a339df844f999d2e7575995c9d13dbe9d13920a8b61d2e757d74f9d61814ee9b3d6cc2a8d7a339f96145667e838522623ed19a993d18746e9f329496fea0c256d20690e497b903b24923e7594efe734a5a6d360a1ca68efe47b4e4c26acd7013c8a13cb47aa757f95d16ac78b79766024cf4ece7a07d6eb6f24b737e71c142d141fce3bcf96200bc527744318d31c9778716ebe2f7473737a7dd1758d70ee0dc65b0c96a2e113ef68442a9962bdfe0a8ceaaa2fbe18db4af1de255eda268a6b7afd15dc3a61d4fd49dd54ca569a32993a167bc5eb55569bd459ad52e8a9eef4faa8575dd98e86aa7258300b8bad9405e3afb22a53ea1604754a29a5b4ba3e0d6f3a6bd75e958a5757759525f8f5b2d47659fe9465599658d32d2d5a6bad35a93fcb699b2ecb9fb22ccb922461244992a4de7667c204b7a0a86fdbb66d9b4faf63eeb82d7fcab2e4ca72234918499224e61d283402416ec1512f498ee3388e6341735bfe94bcb465599230929396244970cf663eb3d96c360b8948a653eac4098e5d314e7ac5b2fed32b9eb2e9b22ccb92849124496ae3388eb399cf6c369bcd5eaf9dd7ebf57aadf0140ddf29c5e2f2e1083c262626465cb150ced9388ee3488e244992242d29a525a56549694943176e17edacd4fafb4ea7175775b5fa3f9dbeef643fdbfbc23921ecfab00b845d1e7675dcacac32db4f599625deec485e18e892de25bb4bda4b72ba9277bc37dd1ded1db93b6e77d477ec5dcf2196d5d5acca683eddccceb8d936d31a7f51d7abcaec1dd1beb6d7ebb5432cb32d5b536730e7241616142806505b345dc721960da1b94c5aedc55a76b95d044a4ab7189a4d95c183244200c30b6e68cc6e78b08b19abb1048610ecc7dc26d506326010e16245071e354a4ad8c4a00811b32a2f70113b3268229c2cb12ab0b12e88f001c35ce08fc9336ce0f810810d1e6e7650a9818d9f19aeaabd3041ea090c4be8f8f84182270139047991738ec1072d2da0268c25a2f333a464ed70ea985283222061a768f47b32e407633ca4ec18e36b87f4401ba28322500c6a223c448480413ac5880c41c4e706112110598207064f0d3c3af004e1f18127089e2178a0f014a1488d2237140952842cd282224d4650e40734187161c406233b469418618191205041f0122d60d8800532b8e0494f1044d8991a1ff9c5af055aa573fb6b07b9c7a13e670579df20bd3eaa167ea4a9001bbd82fb9cadb5964e9bbd1c5c8877e615dcfa0635308339b850770cd2cd72da9c34c4b96b608abacaa0ed7bdbdbd49c277ea06b9ddabef7e4d5133f50c1cada3686db2c0e3b6f63a0df70776d4e8ce7540159d547b2eb5780fefc1a86b3b7a07445165374550f1c6cf81d7471d5ad1024ba8d09fb8c09890931d2c5ef99a07020244a17a2040d8450196233e406a5c490130061c88f2843900c5932a4c91019082a90aa990109c7d7fc53f5824e167cb58a17e339356dcea97a7d73e2a01855c40e0c5dfcca1d183b35ddd4674cc858c39e2844d1cc4fad8d4c75316ba60cb5166b13dfc0239ecd6aa96b8f6f8c15cff00ccff00cdbd4d594c178c433175326cf982bfb36dbd495cda37db1aff5f79d4e9f33b96daa0c7e61e21978d66d4da7f685b9a25a7f5f1ef398b5a764ec1d4eb70ff2d98ceea6db54998e86b9faf928279f8fcb2377438f9c30f6533af842211008040281c6f0beb4c5333b13f2a92ff69d689f2fcae9a7acba8867226824bbfd1a9ad9f844d0788178e45491df5a1b9b8e069f3a336b374e15f8ed736bfb7db1bbe976caff9ca27c3e95cba1064e98da23da177a4ac9475fab945a4f6cd1eb8bc64f466a496a496a496a496a496a496a496a496a496a496a496a490a084a9fc51d474c92a014102c52407c62ad6549922920b4d640b065a4ea728d442419a2e3f8cd3903797d7eea878f565995e9eaeda19546b3a60dbbfeae20cdc7397eeb004b22b1b07cf7de171ac4b2214fc13e91443b9ad9efd49b77bfa3092f147a2d6799b4da8bc33a532bf8445d37a73b1696d2a9e26a166abf99395faefef4d5b4b576bb978584c3cf9e7e45c3bb3b1698b334f4eafcc39e73ce396b4f6dd223ab785eac652deb6dea8ddbdce61d9d74f2d97920eecdf9a009cef9a1595d7535ff47ca4f098bc17a64b0188ca43774ac3fe5f481c24209d5ce12fac43193d66aedbd98566befc558bb58d372d67acb7adb386e6fce6dcebbcef3409482e8474322d12495a669858e48255389d2b0ca08a0c3c6523e2104d101940f08b9cf5890257a6c4a35cd66cfda537dba7d3bb3d73ebe55a7e654d7c59a5642b7dabd9aee589adf6dfad0cbe1b036bfbcf3401704b29582f80581be8f5f3004da5fa86389ec8f46fcda2789f80537a96391f8b55fcae9f6f7ca89a32a0a752d4549f9a935b5f5f353c6603d32188ca43834c7456fe8f8534e1f1f2dc5a5d1ae58028b6a1d8b4e0de68de336c739d7f1dd791ee8e34021916824e2246e543299564cfc84e250285b290ac55750a9942ac5595ab896165b694b0b57b5b8b89870e10f7220682b05358a11462081047e4100acee6a652b5d85fc3b9a0b9ee858b4ff0551ec137c848e453b2561934082ad94044d47d0b4a729752dacb27e4391fe7c6a01fb8a3962cf5a73aab55a3b22b9f7826e8727a7bada41081fd70a693244de38da3790483a4aa32426d9cacbc9e9351a8d4624d20eafd48b8aca93c1f2e1b4783a2e7c08901f5a380b0b0b4b4b0b102620a7600c089410089513a21b201d50f013274e9c40810248931d12ba2000c029c8430c569cf2708397529f311e920800772100548d019c74360138fd4f899d4af213fbe113fb71c4ac23d8a1e606093b8400c60842ec700379e3c40b66089fc80077d8018ddf4108314cb464b8c888a1659c81e586192a1d37a9243750321aa7979319565e485c53bff7de3bc477fb8cbd88f0a2429231c3086714e9cc101a42e303f2830c101026313c205040d001a15203bf6163eb5083dbd978084206cd430c6ae41bc488f190440d415cd0b8416a88c006d1c1a9414819689015d8c482d0a0e33e63416040c29204d86b696a86cf884ffcf7d1234656c1c70941c9c007d827fe0b1f958f886ff6e11f44dc1d2ff852ad8fc907836f052ffe1f851ea28b1f637776b0d188ccc03fc488f110e2de2afb216b77d6ee95ba60cf4aedb457279b803cc2d34476a43c0285e3b8bd91b43a24385b0f766a8969ed4c12ebe23d55d94774349524639218eeab883d91187b929f4eff43cd1e707ad099f667579ab487561258929d24407ae041f74004cbd8a920f03a519432d94a28a5b522b141e244ab76465bc45083ec87924711391c5981b556c7f60442ccb0dfe9237d1fc655098c287ad4670cc98f24396eacb5d65a5a7f1663adb596beb514451129246628aac5af225248d4a0d56bcba6e6666cd9d4e016e5d9c1304ce217e6c13bf855311e716e725c23cecd584af929a74b2767e7e5d2c97159aeed94c1933e391c34f4a97f522594f4992bcc077d6af6060e7d3e9d295c3cd3322e3586827deaaa629a11e746cbd15cdaa8e16837dab877e69c73ce39e75723619b26d3621aacaeaa46e65c935b5aa949d17eb452772c53b79fc59e9c73ce39e75c6b1797b3d3ebef17d7c9a9abeada38e79c73ceb9acaeeabc2e9d1c1797653cfbf4fa5d9e79b949eea9ab9a7572f48e7e6997d6d139bc63997a4dabaee69ca1e65b975d4dafefb5405a8afed125cbd4b7dfb36e3dbdbedd645b6c83d555dd48cef9c91a7169e6a2954cd2399cabaeaa0bb78bdc287238dc4d5d558ec48df6a53ee566dc8c9b71336ec6cdb81937722337a2f0466edf773afd7323377e276ea49c05531576d2cab22c698837d2ce68d7466ee446726dcbe88853ba4a9df255f29432994c2693510baa409465ea59bb5ed3a52eed4cfebed3e95f5c75aa618df7e5f4cfcc29a5ae6b419692e2106d6aea3ae79c73b6376b9776d9192ee3daa5b5eb6a9776715aa69c86b552fb74287e99beb86d4a29d537cb643299acb4604bcf2b34674b29dd3acbb22ccbb2ace4b2be9ca629dfc88c2da735bd71a835ccf5b96998cbb86cb64b7cbfa60c6638b8912ab1911556dee855bb8ce815c53107ce8ee213a41d8313d31eba98baf53a6ea9c6711997512ea35cf6d5b5ea9cd6d6b39de53ab4206ebc39ebfc5efe8ee7d06e9aa6f19fd686b9d365c61ab7a1385be032f51df2bdb51d62cbb3acd72de4ad09b3c35bbd4e6bf74bd71ceed77eadbab85fbd7e76d97ab3eb66d7cdae9b5d3714ab2cbb4a3b535d3ae5abe429c932465afa548a6e7d36a7f171463b43635af63539516dd4c65e1fcfe8e3b8665467565ff4a1e8f29b524eadb769d999184d7eb1538b9a4cf786a6deda404c83e173c10f8ef8415509420f3e185f0cbe1c7c475ab123321ca99932172d2b398a76a642c1745b2aad9ea801170d5feb891b30b1b94d5f4fe4c0873e90d7614fec00785a31954823cd133d10be09971616552a7be2074e84e17da1a04fe9ed41b1ac02f7550b2875fcba8b14898e1f77a91c7c5a0a07734eba699c6ca21049a8a74f5d5345eae9d372aa503dfd4a4e152c4fdf8e5345cbd3b7b3a9c2e5f5d38fdbbcf3409bc6d1b97701ed6cc2b880f75b40d2b380a5954f89961e8275fb2de07d16b01482b1f0174537ba7df0be0a34dd50752f8a8ce8f6efaf888c48798f024f9f1a0141811eb55da7ac6ba5b2957e02539830f45740ea02693961e897c04a92403b4e18fad314fa0b72b20963452f52ae356152477ce2768df2768d445826722e00813874ea03b12ab13474fb64bb40b2a942efedc2a4160bdd2868a12cb3aab2a98b2ac5cd061365b7f83b4abfebc2a7f51bc05fe8f8f38bf805ef35612cd792815a13c6884f2491645345f798c6d4477d7b3155a65c13a6e44acd3e3155727513aea902a74a4c6aa54c1bc4a54a318543b7af4932ae255e202613e595d1425966b6aaac4dd98bb2a7f03555acf419ce176edd17933deb9ebfeb9c112863529870420a0f60d2e3e4be481fd797525aeb0f6e4c0a134e488149cf8cf4b9eea5c0c5380213884d32450c12295dfc0850808509fc681e93c28413500ca084871999d991f471bdf5e0440a4225594e242e89939492e5b479c042844eff5960094599d2f7cbe61cc5045746665d9cb397a962f6d5541133428585232d2baab15345541c1d94523a448af561f3da41bb195bc7791fc9f10de25f17f24473ce39e79c7386287d111d4d12ad3f527e4a580cd62383c560241dabc5738a2554b1844e31add5e28b359df54643f7e636e71d0df5bcaea3f16868481412896ca52253c71aad9c50a6504733ea58b47f9bc44b1d8bee692a994cb652d3e9afc6985a4facc0bdd7ee5073f7de6eb1b51dcd4acf1d0bcd6d39e7cce57c73ce396f3a6b0c8a1608b51d65689475aeeacfc73645fafccba322d39ff3f73604fc0217ab2e9d8bd9194f0ac806bfe8955e6947386c43f04afb32729565af6f82551319e57d4e4eaaa927144d4c4653ca2a837faacc955265ac86f3e542ae7258e6952059e9fd7852bc29563655ccbed2356d2a9398489b3ba48b9306bd8f7bdc95822e9d1bed8c47ce97fa5a7ffa458fd4b4173db205b57aa4a81c911ed9eb9b6e4c6395b12372448ec81139224764af3f1ac146b12aa3591b131963dfb71ba03f69acf5ac66e72c46adad345e101dbf40ae1afbdec9e22952d817eaa5a8a05eedd767d9fae1d7f98586436d5f42bd7eadb86389543468aa1a9f783ba957d50c5fa9d7af5577618acab7d1ba4dcd79f65527dcb7cfbab5b6e8b6da2a7b83f0abdb8e46eb3cddaa5ef8f6d35dabb576df7beffe8d390731c6186f4dd3344ddbaa181ff7791b679ab463995da58cba44ada58ab175dd6fee0455172ea0d4b7d6dfa2a3c07f7b167ff509b3a9f877afdabe45efc20bf090c6850bc58dc625876228da3a35a79b10950d950d950d950d950d950d950d950d950d950d950d950dfa3434da933d5188a51795a934c43a2b4f718c29f322a6d1e963574829a5e206649dfb977da9645d550cc45cd58de2d79c2b4dc3a205f0ab6b1e7cf7ef0a680237e598ac32ff18881873b5aaaf4229c67ca90f9ac17cd11e843b1629d4bc30fd8b95df98567b27f64ca8a59f2ba0cafee95bac30812a1baa1c60fa955fc1f42b6f7f25ec800f1baeb0f2a7b003a85ff90e9c4a2790762c54303eccd24a610bb44a2fd94c1576445f14023501cdecccca57fe73ca9c98abfadfcf2903f27e25ebb5db10db58122896707afd11289a0885622986eda05004cda0d7f740110445c4a0b2e4123720eb16c02ffcb2aef992718c7922639d9e7f0b45ddf3af80789c2f3967158ecfbe3d61f64b7b50d243e945aa1b08d1886dba5eb9b9aa4fbfd70fbd9f8f694c3cce19fe13c79807a01fba80814fc433fa7cd06867bc10cf46175aa055acfdbd2fcfc1b9d1ce4cebdafbe78a4d3fe7fd86d6868f389d1640b4316674fa9886d08c526a77e51974e9784e6ebcf7af4782c6904f9da93db9d24a2badb02e72d008ba01e15499fbf5735865a271abfac8abab7ed8c809735fa5e413391c3c8a9b179b2a665e1017a7df8f4d98fba9275f15399c0b72d960e3986a29ed4eb8ab957d61ddaff5decaea50e0b5db36dba9b082ed58f4d6f957357e226f7151a8f76b7827cc84992c52bf8fef65897a7eed5f6cdf0a5ba835ee58a1908674c2ccef58f759b3df3085de6e77029735900add0a394c11a6b02f5b77e77dda3160f6bba9905f83400ea77dd162fa7c3a69f5ee37e05e80b57bfdeb8158f4ef6f5bc7e2fd3ece1aa727e7d9afd267c7001cd6dbe2d6ed62cfabbff9acd967c89282187d634f6cd15bc8fd3e0a2dd02a2015e68a9a17ddf37fb19f7bdaadd0d1e7e1fc15f6d8e7b3b47e9fa5f59fb54a0d5952f051d64acf5f4f3d5b8bbaf7a67a7e8c31566150d3344dcb1d8ba5e79c73ceb9a567150c9f785fb35959aa60d8ba05947afecd6927e8f0b5b06b14b610b742dcba008d4bcf4fe3923590dac7345aadd26f886d26cc7c6ca3f50fdc16ba6e19e016905ee7055d7a0a7606b7e64b7dadbfefe4c357012bda17b10d35ddb8166e71ad1a969e6cb5fab90350d0854cf1ce1ffd6dfbad850fb4baf61ce8825659c1fdd6342d7bd9becc02d89aad3a61766bf3aba0c3957db12ff35b74dab12a0af7ebb3eed7bd7b7d6b43157eed039cc8a894ce76f62464330300001d3317400020140c0a44922cc9812c4d73ed14000c5e74426454381aca84a15896e3300a83308818620c410020428c31c81461011a0dbae69e79f17659752ec7cbbef4c72eca575b70c437faa021233300faaa6b7f52e26b4ebca99421522625938182ba4aa00cd2bf81d20fb0102d98bc9a735b57c1db113660cab6072ed6ed89ed836dd7f22e8a511a0065d91527dacad612db98b3522fd06a21e35df8b550f09f2fb2e2ea4d7dd04941b3e8b49b50d72a2f2e0a3bc4305cb85e26b18f84128bfc0606052389661e32304e7cf44acf930e56665cea1833fe98e6335934b241b9ded17e4fa7b021f43b6fc7c1ca4a452f562d930dd9d6bb657def1e6c304c8ad0ddbb65c7700989ac4ea28ee2ba56d345f9b7c76e50d77479c227277cd48e1b3b47a8b26145d5851b6aa5f92971caf2228265595ba9ff512a476ef35125a31844815cd2cffb48fe38fb951f9eaae5c48518a8452d149b8a68709149eba610db86c7c096060c0bd4d8c2988a77c4eee4ec8a6f9f67547d4976c67975eeef214dff2bed7a47218eb150e5f1abd474641613eb7216006f63f15d0090420c61b7228f59b4ef09ef3fafda0470cd9ab3e490e3d4eec2ad2795cf1eb2c8185aac1755992af582469cd22d959efccdeb83e61eb819b182ea6b0d9f8d392d019e451c37ae1aa32588644bae0ee0329425e2113292c82c680356600a9c5cc5b3aec12eaa2c9abc80b380a862fece822af268cdc27bdfdb7554c3a01d19705eadc875b3a9830c49a7f02ba701a2bb6562116a00c6c05905520c7ee02002a820f21d1424b525bc9714198590b9538df67143c99c0398e208828616dd0757568755e0d6a10144956b6710edd23bc27df7e4360bd04fc892f44f146a67b6f10a85d99f7eb45c0753b78c7a70369a382385b1c358c3a8b27a20fcd404513f4b1fa50855a0bcd26ffa5960ba4b2952ce9971eb318a3a4dae57b91ac2b32e5f926fb7ce2a48c01b1025ae0f0f32922a8f553a3f8a57071e900803f8a78ee0d5f383544d4cad282d8e2fcaaea7f108dd238d74e2bf5621707c8b991331b3963d6328786861835f3f3a4ca491494e12528f115ec10534e690a41b5d9f1e93daecd423896ebd46f9c44d360b6df87f9a375e19d8995e72f87cdc2fd1da178399599d01f41b79aa831039337c2b4c18947f3d089e4e35120f2a10afb10e3bd8021166e11b00efaa86317b2232529c518376b54bd423b3d0e29e6b498ef217c10824080a9dfbaf361f59dc2a64c4f191a2e1b9039a48f105b95a9fd735becda31175abe956f87bf06f4f8e60c70ae6c2900d275b2f4ffd52cd802991bfa75fd8125c876950e4c4016bfb4fbc52b97ac98e06d417457612fab0bbc9e57b2cae25dd16a3ee3a2e8c1ba1d701db8960ac537e84132a03d613427ffcac5f76ab55c71c748145ec1851856557b9232fec0edb5c4d53e1e9152022b4830b3101d1b262612f2f33ae9ed11610ed447666530a79f9cc8ddca2e10e6e242fa80c17bbf53d807a59a36c1389e031d0d7de34af9d582280d30a14827e5b440c79cbf2a83f9a25031bf8c0a220c4c73233eec5a043c6931d9aa45a2536ca36021192dc042257845e3f112b84fd2d194616be80890a1854d4197771d0ab1c008fc0bf7b4e262f870dc3de3ce8bfb652277a77c2981d8cd7041abac6fee1d5e3b631b151fbcab687ad60a25dca8d7b0e32a5fb6e1e1245f58463198723a3c0bdc271e80933e3b353fdce93aecd20e9b873b5d4ec444c4b0f41465b05a53f2418f38a9fdceec2f6630230ae743b6ae486c40147907a275c4f7367db99010d9971943c282e17754e0174926cbb6bc2add4401136485df48ace8f3e0db15cdab00216192d122e70e9f6e1606a721d73d3c4a537417f81fdc54c4cd911976e2815cca2f9894b5b464206824b990a329640a964413d72a9b22fc1d4cc379ab8944c69bc3ae15215e474604a4a14d37a73cd819921580d5a23b810ea3f444152557d328f8b47f7bc90df67cfeed284fce02c3c14ab3adfc9ea7f41346ebbaf4a3db14580823ac15ded7e2d270f6c908a9e47835179bc8aef33190ba27a7100e43d808402e97c49c0703430ab60dec1aaaf56e8ee0772898ee82f2a2c0e16115a43140742da93838b1aad3f046f179aea094b2b356b1f420dd46f5558ec0678a67c37729c4e4425331bf19d51c4986b9e7643be7cd3cab1a33980d09a5c566f9b54d10cbcce3def8aec993a00c1740401833fb5424cdc45e4916c5ddbb1ef1147b48c0f7b94d68218a0c4e8c2ee51a0bf573f7d28dc8fbfb952448232999c3f0cc8adccd6da1283f920436e8ea28ee9b80d35c86e05eb7e592cbafda465dd242e7528295946d559e78cec0f10d39065360bf1098ccd543c779e9346ce7633ba5232e9cb3fefefe7d94b9c6e33e7dd6c7bffd2d7ee7edc3c1965463b84fdf2b673eeb8b4d434470e966cbcfe9edc0c38d64f38ce44c7529a5700f812d7aec9e667aa00416638885809beee962d408b8f6dbb7bd8b7e8668b425deb60c1994ca6e54d0ddecab02138395f5b66dba3345722342a3f8739fc01d781ae1ce2ad38cd7e3c9d8c3848f11c5607af42e4a8868d43f130343a3a8eeb9dd2934482082965960f65b3d4cbeb8467f4a3eb2557c7173d1c430ec83d9f7ffeaf223c059d15cdcb2c487a634fca4e8e1f706b6a7371883d51f731d3c58b2db7c5e00a46bf980d3f324862e8b2c505108ecf90c02a5ab994d03c6e67ab0fab40c6ee682511c81311540874387764700b5329ce713460d7addf12bf2a2f93ec73b4d5a19ec17465774117f4aa163d1fe27767b7a4b7c0c63584e7278d8a9fe6c32948588d2f35aec7d4385f5981befcbaaaf74022d211b60f5f933d52d45b9ede86cf881b4bc4501f5dd73f48595694421f22d564cf5a61dba0fedeca329550dc2e40ceebebb5cecc575a6bcb364e765c9fe5719b5b846e8212e1a8f0c9064442888fcaf596062503d714784cbcf225efedac0f5a2b2292658f159e5e3fc76aa6947b74a36283398505eaca6a63bfcf201c007449fd6480a4149205728cc29a7ced86aef03bac486be23ceb425122b9ff138e2a376e71b8fab1a2adcab8035c35c9fe836dc9c5d85849f2139278875a8b71a10ff8324bccfac05c509b1a303d5337fccc8d93f307fc16fa4c2c05fa6f5c91157d251f4d4a190f4d701a76e518d010497cef93fc287ed88c7a452b9ec4706a11b7417ce83a2251713f07d71800af7e8b16400ea1bc39d974a80e65415900d7a07a2fbfb43c5dff49a971e37e10174c3736aa5675bf639682c6645c9a6d8c10b5150e8d2cd0657e492e148d71ee4914fb444be878f9a6da89963002ab1768a281d4c9203462a2bda41d8b74ce40b544937c1836a6058c04abd5ed74200050bb337877b8ab65af950460312818401f57cc23fe13ee1e5ab920e5a87e6c4ed3db5248b4cb490a85a2870fdb7360200e1dd38df6225b0a8cadaf82251b4fd1f40441406e298877edab0e40f51fc2d3efd25f75fc957e968eb954b33769c304d695c8629ccee482df5964215d27a4f8c2c122015eab7f9778e222a7363b08cd13d8abf61c81308ff3d9e18afa9f3c3e08f7219998c2488cf85ec0e2446a7ecbd13b2d9d292619a1d0dc1bc7c91288dfd5788be62b75fc62ad1a616668af3a1099c87b6fde597a4f2605fd24af64f8ab190e13d918e49f819964ec6a792d32c460c55a6aa0886c3fb04d5b20cd122b50f100e4aba3d19ee8f1128c52cdbdd51326f2be693e81b05f870724f3db9258a5fb478a394cd2232620c05ea204f7d7811e69ece95410d637bb72732a7fa1f8850c0f110a51206f07921c8a4d9575e6f280c37d001b67e8e48160aca07b1267e1a88058dccbb58cebae1e5e03ee31d2df3f012a777127d64eb73d7c14c983ac287819d84f493cbf9a459cfc7bc21c18613bde8bc1b64584225d4c7711c5d85d89e9a4b9192c5c27af24f01f32f5beac27afe0b8fa178439aa84092cd4dac53aacf795a6a45ca8a42e6155354b697acff3f6e3bbab524107c5b6c3eccd66024a3f0556b59c19f372d8a9a8737a99e63a4948c60189f9c1cc31850147f088fd1c789d512ecf0dac36ceeb5c9606f76904d042feba387cf65011e00044381e60281a27e6c433914c61149f6ff0a71ad5675ccb7ffa7f2a56ce67d0eb057217f95fab2fb1868335e55542a4f0a11792de9fb3eeeb990a42b74488b0f7048185c3d5bc2625c7acf2ee61807a5f22b4c95ea70577d5895036b68b46a3c97168015cc38a994cd9f1f322168dbde31f2673e39e30f1a648edefd8198f0a242085e91a0e907c9977d5ffc1604c02e997dfa47c8ee45f12788739c45d9323e1e10c5aa99e483a9f256b45aa0b32cdf4b0bea58cdc64e6926371c9861ba8f5e03f8c42b2b6f121bac12b76332e6034ccb3034ad119180c7e87233a04acac4c316f363eab40e2fda9a04b12e98992cd5dd101efef6c6c29064c242e8e2dc61b18cb62cd100ca98bf418e47da956856da8f09cb55269c508776f2c2c7d7d62b794ab7c2399e279033129b9040e4dc437d27352871e645b0753fa8961f9322a6be3c2c5c6687e4588e4eb803046f28114b683fd241ea0ae15e7e5c2c5fe17feb36f8cd27bf1a2d8facfb559bc3158737806698970c8e9ca794ead76593db244571d3a01624737af9811c5de9bc9c07229ed08fa6053d0e8bf2ceed265eaf4d0067ae37202b861bc0214da145737b234e18c5e8dd04f26144a30549f7117d0346fb39dcc7611d0ae9c70fa54bbb81b8483cac0fdc8f412d6627c4c5073c68e03edaf66dbe7c63595480b3ff9cbdf9aa2f53aae0c31dc6a15e3bca8105d7e8738a74610fb2c91974c3234967dde2cfbf7ef93042d6452e701f4280ee5d8412fd7ef1c3b59211f0ba0ccff95e0d9032e07cacad072f13c357b08f371c674d8224ae59fb20930169724c00b6887ec52a86c2a08ceb9a9d7e0aade88382adb61227dd5d64658060bb08c50cf97256263d979b1f23531fc235460c40bf2a2de78324cfdaea05d35d926233d5f39e19de063221af990b62e4a648a4c5769810fb12493e61b38097d1895b52f1b16809dfd3be67c18bbc61bc8fcb6d1408e8406f2d088859bd9e184f735e3869cb1840091233aa7a4d398fd944a5d1208a17f96033a7274c1a137eaa865666739a47b63a78c39581654829d7068ac241e1e1d3c534a8565c709dab1de78822d46c52cd0b63a030f710be8488280350db081cd44f1bc5cf7191a04a0f7e87402d351cfa14f95195ca19bf870be6f8d0cb0f102f67ed07fd4d4d617eae65bd1274b93ac40c2e61d0ec4a3b6483c3cabfc49dc314f46030a59362cec515243c903aaa0db89d34ce12b6284afc923be15cbd50b2018ce57c02a840953cff4f0025f5611e9337c6d19364569b1f0e0f5d332048a807a34ca37f921afd7031902335c670b0449c2739e511d5da37ba3dad2d00739eadd90454d02f1c73eedbbd7e537d7fcde8db1b693a6d0a9c95858eafef0770a972040233e65431b6440a40dc45a87c5315cefe3da1df4914ba20af44c6009b09b4e5807e22af4a203d1de716d252ae34688ddbc4658298c1e0bada913f060037415d87777439b9c8f0ed097ab1c30ffbc8045c0de9b27f11d76c510d4e848f4c33a29c3ad51859d4846abee7eb134931efd02c5381edc815a35aec21bf60446b90ff890c182262767edc32722ec3704a936ebd173f063a0111f8258c5dda7d4869a3966e59ab90e3f3c8d89921c7bde724219cea2844bbc6e84c3c940b000dfb5799a92c42387acddd361ee0e4f62fd2eed26e1832548a9f94bf729c7610581b63e89e7a3907f30cc66698876dd3321e5c0abfe363db16dbc20663e2fe186e1206fd5d32141fc9f2856b34d61982bff58febcb14357dc0420e218a0072788cbe7ee3598ba18d3307ef42fbec79cd16ee838988ccc9f636032497a7e6bc1353a6f158ac285142cc3b49d9bb224580c28f83f81e0ff0a8e91c23e65dc17d128f7b951de106d1cf391f18a88c33dfca9a4b752016f5632aa1f826b0c210ade6fc654fb74ee2f33a73ec7810e81cf5b98b41fd099fb419740145b1ec187b00f45d1f012d0775ec76a01e353ecc20fc381deb5ab1fe4770dcf448e809b99f9c73402b884931e63bbc86bf447371eaaac3f5ae3e512dbc01b227e9e796702146b4b3f433be046fbc31cdc31b368b3ac7d7e7eee527ca7fbbfb2075cc5e143f83e16e659c2328470717bae1c2f4e7bfade4b4e429ecd8438e21f04d32c85d2dffccca2bcf1742ca29eec2961182e9d1b0e3a2906cbb8174afc9eca1f737bc600abbc221ea425c10651478628ec5bc3210cd2a44242dbb43abb746a2bc4376bca964e0530b9063bd0e0293591b764181e5a5fce998cbb07a06403ea070ee32405d16f729ef3b2e0c34fccda1323f50fe2fe2d9bc19186d51023da40eb72b054bbc59efada3ea724682e10aee5e6d23c0fdfb4f77cdc0274c6d59935951b6cdbc68d0e3cd8d75e35611ea02adebe5c9c71688726b7eda7cc3b8e6ae83604035d8e6cf2b55dd9c449c4be5254df3f81d12832466ddf9e1e338be7002aa6bfb67d26b020bd9929c3227344ceec1b44f099a4f75a6fb79b852c28c34cb9cd671276a03beb6023bab825139859af07e1bf11f90724e4ef610939e9331fdbd161273e4a0a14915c05d4e143a1b89c163e17352ff1fb04572bad38b0556078adfbfaf54a7675b7e42783afcae549c8b588181462a078270cd7119a60aa828884133ce832070d281fe09cfb4056c4fd83ed9621b758d4c32e1a50f22d768239f9403badcd6a225b71c1840bdf002000c0778b0e4d35c28fca82f090eb82b8417aa67bd099dda7ba07e08a92640492e79d599c27de90a76f1cef47b2895beeb9a1b247615415b9783e3971c9bf17b7ea676975f90dd0ce24f6ff0e79ba8161d2eae9cbb03736c1b395be5d1d013f2e54b9d69ab1fc2865f27d30e72202131ff84025a1212c815e8f9411dbaf0eddaf50424ead474e39ed7f4c8c55a862f48f173b3c68e8ab9903ee59c49f3e78cc7a828c16a3ec83887209a31f06865282351a12617362d3de7cc3278a2a0442013a68fc01ee18dbf8c385a2118bccc03cb08d924f075c8c75ed1e353733ef149fce4e0d258f717d195410746931311afd48a6c30819f79d933ff176ea646356a59b17de0f6212706f33ca73c27e34328bd9b667e8b96de9a745753270bf459755a7f8ce814c728d055b827d0de80578a0278bbe6db8a46472239b791cc930ad3d341985092bfffdd5d1bcb497525335daf81a0af42605c4db6da0e7c58a6507b355fd74c54e1bd1152fce2368518e0023761e5c5f16b3f8c5c8c3d0edbdd1f6977618d2c529f6f400bac0d8ff46ac7ad380a271a24dcbb3655ef640e3d65581b942d85b39d822479a0dbe3072a2a61369e4facb2e382a3fba49370ce991c39405a15fb9f620a355580aafb95ce91584b1ec2da49eeb68e6ad7d451d97069f0a67eff0791fada3d1ca0273ad47214f9e900aebce0512fa52e6ab516db086b2fcf5fc54f963eb89b46c3c11623caef5841a7a366c4848cd3e854bd977716a067c71ba68a8d2c625dc7b35a7b167599ca9b7e89d900c06371b4580b4fe5fd2e235935754ff5ee2401576629b0686ba032e8ff22ad88425d65b9ce9cac2295cd1473dd63b4e1c6cc4fe659e0f9c12654b1b33e66532b66e572aaac9059c39e958f623fb942f2576cef77c62290d23aaee67cde8ca7cca5034f9a9e3a24647623862abe4c8743969fdf7280cb54968166f57e310b49c917bf7795a88250d84c6b605dc730e4c60411843596198e2182a8c7fa86446c70bd9baf37d2aa7502fb8c3e656a0e88e148cfcba88a047f0d985d6c3349dbfe54b27814ba1979f615760e9e50fc174a3f41a0df5b318f27119d7910e0c71b7912731b311c2b56b390c9310dc9583eb9985fef88ecde85c812f5fbb29164373ef9c9b4731b6bfd2012cff7d987c63cc2489c4c643404b37962df6e67eeca5dbfbbf2202a6c95e2482228917fa7526b8f6a64f238c9a30c0ee9f74cad9f5108bff3887f5e36907d503112942fc25341e9cb9531c8461b2df82308e8372cad97d86642580efc95f793a9b155f720123f801f90d30665196a46dce95212d999fccc82c6791743786afe19af472988a21b419a891f4a2e71e9e7de86136d091eab27743b2067f2d3df9bc1cb25a21f3569b7dd90c9fa1a4f1902e396b7230bf37d2358526c93d0313be479e52dd054e6afa7272c8034c6d9516bdb29fed6b94e2db9e0a6756dd6346f2c7401adac4bcaefe11af9c3383614e9065e5534b8f90a5ecad1381fa212d8101ec12dee331e97536cc6526bc004a091cb49230f31f09184c3802d05ef82b816fbe7e53e03c13646eb329204eba985052b7d10bf4e676b29a5017fa6327cc3313b2b44905f484c302b6d607df016e58d0c5f8f766d4a2e7a028d050b50d6dcc7912a274a4cb54325dd1229eeb41956542e97fd15d6914634ae3b29e04468e7ea2302a28419eef458e5125c2266319e0ceccc4ec34719f7b861a845c205fc668eee06d84029c67d19b2e9a1d8c362d2e09cef7993b67d8530eb2c21d77916b7f528caf91f0035ea340191bbf3f178a46319a11d65265b61e9d5fa7657a55dcd29c2291920dfff1ab1a8509590da597ab61e5a180bb329336182ae74935dc198e4abe20519f7f3855dd6c6a63c5e362d97d724e1df406e92a26ef82a5ceb688e3462342d4b09b2198b67fd0e0ba0e46599c37224dde9fa2a0ec2f36d68525f5d3d8471183a55e013dd85cb44f9e5595223d851d682e9a8e16387a52a8930351a34208280ae78ce290f4d441e071467326265a6275fd89ca6ab7b9a1e596f5695774138391c550e3bbd54d8d43a6bf381189cdb6cd051f83a41127c295553f6ffb972a9bbefec3b502b3d905ee14552a8ef4d383d4f26ce50067bb6b49dfa07077826e4a3335908d961b8837026aab2c0643dc607f26c6797d33d8a0dc8d6b67105ce2250f62cf33ed30a6f6b23df08132d1d0c42f5d5c3958b103a6ded41e4e58421ff98aef1f93e4ceb546753e0113582f1abaa84b577b305074b849c2a8d4105e0834653514e7e868eaa8d3bbb270bf0a079674a2e50f2c3fc2cdc8bf9e96159794aee662ab8d5e894a39ed6d15a86d9e1710477e7e11c894773946beb4aefc9c08f9c75d9c9d08c62d6a1982577420a24d75f6255d08c5ce56d0f9d6d8d99c7fc07428c925d009f5b14ffce237355e1cf35ad726385ff71980582196fcef11c1056f2928d0b9381eaf1b9647456246f804e7fc95c91670a4f7477cb08e1fb74b7658ef1bccaec04b4e1cac3ce1c238b4cb2416143afa50e87a8942f77f143affa4d0558a355690266a58c1abde240ce418ec54f455b60d2b79da417da1dc87f7e3394ab82c5041d7f3c951f48ba0d34777bec011f45c28e517d9e165cc2c0b9c8dfdaf96eff8171ef67ea9065832dd3caa691333461f7fdf71a87874c61bd3b7f03b5222d55b650fd6e76f3ac597b3a108c702f427081ef7f3b70c3dc5929eac1c97f0a74b4eae80bf95ca9748e690f80b14f5b362a90352ec4e0f782a49d2968779657cf228d5e13af10f79a142b1e7118be0667067c35a801c50602230e1a2af4c2f631fa98acee89933747321c5e18978a450eb3b396e6e94c4172816f5658a5d5affc62612b7d9a211a199a0a67134dc7789e74d7dbba781781152d5e04a22dca1873d83e9ba14ff3f6059ff3a77e0cc760b1bfdfe0f4c3fbd1f6410eb071460cd29601f063164abb2808ea13aa7ac81cc13da58769093d9f21777b432b7d9452b79b03e3adaec28ca5753a558dd98c320097e156abf19b5c1ea9e8d0dfae2c48a771fc925045ee101ea203b23fdf86c5f3c3606b543cc6ac35a4d6884fce3cd5aed6610382365a3136e10aac12cd9209cc6f9183b132b964123595d32bdca46ec27f8bc253b307b988898be5c09dc465f82bffa3d9f67cfeb63544db0ead7d8b3d333aa9f99f47ffef356188ebf9fcaf78279d9f3f01f19175698f49f4ed1fde02b4d02725ebe7419df37a37c008b235985c10c9b20dec48bfe45e9daf888a3e2bb11f92de6a1e4984d6d46be78dfc5a17f9bc9dc4291ed1477ae0ad7842f0c6ff65bb81e39002afa6d7d92b01e2eaad713d38e9001eff708505901c398803e9394f127b4a3741917e2e0aa3d89620ae53b2f4790138f1edba7ddc725cfd36f20128866dc0cc4bf516854f3b25eeb72db808e321353da753b772bfb1b6d28361c2100d14e2019a442faef1240c5dbeeb5cab7f2b9e9d859413355ee36f819814737d2c62b85e6e6011ef667a144c93c581c79694417b32192a00061b3c782aeda620b32f6ab9f7dc472886d5fc3f9eb934d82b9d65bb7364f166fedbc785c87dbd8976b6a44aca66623e7573cb44c22e1d7a5f24b259b576aaaa6dafcaaea1348b43e7e431f34259f3a2d026c8e0d15313eee9bd5f55caef64196d4b3584837a574c6cb45aa07776b0ed1875468c9ac72cbf2116c237ec02515216f734a0bda1b8a56c70e939b33c9949e07ae1dcdd6cb011e09cb6d61b16da1470013fb4564a894b0c9913d493dd84acf0d0f19e04f9a202c8abd2f4f4e267b4cdedd8588dc646bbf89052353577a01a98f4e085abc18d2e15fd5f236758595416fa558d93c44f794df224b8b1c29e2b006ba1bcfa4b1c884586ff01fd9a554baab2122afc1ff0231ccb71b9e6e34b2d5017636b47eefe7cd6ef3fc429bf16c31d3679de4fc2c7806ab08c50bfe10a439bd591822fdd6a12614fa6f076f249f36b242b73074da4f17db0b7b9b28657ac028551fd57183f6a93e3b0cd16217811bd7f936b2c79dad926e8155295786061781235d8c267ca7c660e9eb8f7478d2f2078050cb99fed4f0f85655103ec48fbc2ecf379325e28264fca779b2ed834e84887e203580e92b5ac973330926ae27da7718bfeb0d83022e1ffca5aca645218b5610c17be9e4c9092299eb42aa37130fe0b635b825d330cfeaaeb19408d4161bd75fe10c0ed9e9a545c05615f460aa017347c84b2f7c3eca142846f2aabfb2302dccbfdf08a11e78d98d09fb6242176f017a8ca3080b610529978ae906e8d52d2c015fb09877dad2288798074ab2ed2d7d024cc0f223a2198d2fcd25d8bd3d3b1b52f853de801cc0740273b95e11ed0d3d2e627a75f40d4b3082dab9d6b0cc5e50f6fdc004baa3f5474dc02db8be5a343d149bb0f83c826317fab687fee6f09cd45d42596bba713d8918cd012ed633ce508e3188d8c6dc31fde0dedfa1170b08162db6df9c6bab88d6f82cf26e5cae9f3071e3e2fd881afda67048f5982e0fe78f9f7643e778c4b68fdb2113242fcffe951bc73261cc603fcf68338e16b9b728591fa2117b658291371a718650c331a127a1ac7cfb5da8ca7d6baad8fddf2a17bfd2ea755f0cd5579fcbbac46f652deac154a89aa0fb7faadc18e8564bf8da256ff1b629db58e3ef1d25067397c85e034618b4bdc5a92f421d64796ee01300d019f401fbdd8b927f39a936b77af225f3ff194fc84e3ffc1c9394d3849080319073b6fda40da2bc1392c1c5bb5e69678bcd5007322b16f4a8cd9bf1224412b3e45cb5fa6991c3239a999d68987c6245baeccab33129b63072fd630f243a6752dd2a1eff245bee4f26c6ffbd52bd8ee2d815e4a65aafb7e3731523c6a97ecb437d8af15e24533e34c2d4ea43055df3fc00a6a4beb46829c1ca4c784fa9427a8d592ff8006cb19ef95ad94167ddb0257739346285de4929ce21d7e66a17be567fbfd346b15d4fce5f0338adb5fe52c6a97df852e84371cb19c505949cc9a6e137771cd4b5d086eefc114ffafa31162abc4178fe6332ea39b1bab2602736bc68fc813305926c806c20d8d5779a42b17bfa9d0a10860df9f2c541d0711957b462f67e2fc34646343a103c548290b9b9ba145f0ac3bfb2f3ac2535682b2e836e720aa2ff62e3fc9194cec2127f92e1c894e3e037d9ff4c2e25549d81045316dc6ef8db6cfff6829aaf3be6b9b653b18c64fbaac676a6211c85483a4f347e856c39fbbfefdca1dd652c3468ab653f93c58f29fc1ac4ca6c8ca4ca0f20ebe56739542ca47910ef27e642d3b70b8a63cf83e44bd89a89632f4b46c60b127b90027d2242ff314574a1a6d21c778ee72846e09ae369df6b2535ad741fdd0bfd85a631b1957e44ef0f20749e0315a1a35f90ed3374327a319763e8b0ee60cede64cac13196e84af4b972fde5b143b662ded540b70ebd2fcd5f4075df76f66a22f61272ec8f5764374809bdc7ac4781d2243e37cd536097728a7d20e6b91243e0de242b43e14770dc9ca6b82279da21b24cc135c551a0983f39fdbb079b275f22bf242c09ef1f52cfa7e06cc305dad8968a8a0943241fc4b97eb6209048e9cd866ccf8d8a7f148a130956e96fb8279e1ead2cd5754d57e90068366cd5156af6ce2077b41b9d50de9c627cbaf59d50214975a279d6d2d50c659f2444ad1dfafe8b85f22e2326f1f936795d94317433a67da5de7b19e41e37672a6e29bbf65d6e4e2995a71889cc2654e602a02d4019d08d3c9acaddc33da6714162648a0af68f67342e267c89ea37e41355194907073e6f5b674f11e292886a8fae67f14a9187a290b1b72569f8923129f8c3d288dba2a91785e14c5d15784d5e4ae14b2f8fe8dc1c36615c68a951d9c2e4b94ec182fc4a5efed5eccbd1747a7408272b77fcc172387d26cac98b00cdfbb08e2acf80cc91153d3f72f308174009af66ff676d6ecb6761e6fd9984915228ef455fe01340627a15417a6ae704600222db61d1fc0790408c964c43674794b66ecdaecdc257c818ad6c25267fd9052b64a21f37c135c111f4adfa2fa766c5a3878217422bf3ea6e9e15f3671f891a1f08ef6cd04400e1a4090c1ce1bda68c0ba71d9cb358f38939996d226810245239650523807c19c40d804e539f8f1f2e20d682285d8a052b77ec788d9026ce3b995b931fff8f58866d8dce7ec432b35ede1ad1ce254e68b2c85e0ab7d5c0a0f379821dd878b7b09e826ccc02b2c2100aaed94374667ce6f7588be9b2937b91cbdb2cc9f636e3327d36c959926dde4c1d82648498104fa228de436463a47e9ea415a53bdbe7c885a654456611440362df69dcd32bb465111c449f6fd482d680f8706b3a112078a6b2d16f0bf9a15c6b241d47ca24061921d44df47cf8f96278fcb9378d73740052322863f6c2d41ccdfc8d6e50fba6244a9fd011d2d2f25ad44c982d5deafd312958c94ce4a98f6a2c0385aa0fbc249ca014655b67d761c6d7ac3961e17bfce9065ea5b3a16c60cb8eea681fc9da89b9c6d1d4307b40c6c4194ff4cd21b9feee56b823b9268b17d316e3a3c66955eff83b1c545d26a0d4efd6599d41711f7ca9a95865630544e63033338f0ecb83a7259e54e2d30c5184e2669cbbf466b16e1d37dfcefd1962d6fd59151761c1bfd71f79fe0ba9f0df7289d6cbfb3e7c13ab184b0adc018e426f674ca454173d2bbe2e70262a7891204fa0f4b14f129d1e9319ec5eaed50dadaa4648fac596f12ba07903ac18bf11e3db8e720b20e4701c4c7bb17913eff9ee036d0dccddaa00d7e18e22c587b993e258050fd585a3708276bd07bde4a00ea3ecf1da1277ebe072807a84f42e0199ffc1e51e2b40ee866cb207f35420afa11b89fad9ef806212a86fa22c224606998716d3c8e442c25e05d67759630733196d83d665b9d3e1faa43a130399196260d047d36474861e8c04415536c34ecf1092ba02ab4f8d969b79727859c100d2e7bffca0d9648d290ee01ff2bf8a440fcd0a8cf80ce6977a1bab36bb5a982886a59d10ea8eb2dfe6e0b959cc83ef35b744a9ba1406f22d3fd96b2a38f3420d0b80c19a38da06fba5db64f4d61654b10f809b37ff90b7155a649d679c7e1764284a70b4a0d85ac13cd9527c0b01a8ff3eaf294828c4e70398f480ca6a6e3d07b57106a6dd2e5446f5b807acc4bd3ce6ce6e128efe90528ad1516ac457b69f2b2e0e52a10392048fc2fe02c79f2bba9bd16f2dbb5b33ae09efc273b541db74a70b34ca4db844a5e959a2258c7d52ff7ca807e3048a4a3cbe92a073d4ba55895d643a64265d4925fc9bf4d808ceaf51dd4b0725fe0b8ff1e6e1bec914ae14ecbb99ab538d775520772a4fab287716c6cde0edab76e32c46ae410aba69d1e4098bbb83883a60338209cc8cd61b0b293bc0eb5884facadfdf70c1432e41bc0d0507c26e090f1d36b7b4d5c19d7f1427102ebe89c612627ef0e965ce8f4e16609edbae4ec5d4fe2bebab944ee265b5201f346dc5f4bf3ec1e747365a0f6f15492a66cccfeb0acb3850453e01b10bc038206b31b313f529658aea1b77f3365a107ab0ab9f4de8b1f90ea203fa919c3137ac8b25fa2870468db1af80d946803fe17fdac6d490c366f83a952956f6d5b538ed3b364d02e5d81496d8811a69e6b2644ff9b73e8c74159ba581708405237fd3123c88d3bc84d85805e32470072dd5c0e5ef5cf603ae7bd3ca91b80467b5c8862f67eb351d1afbe3949a948fcf808ea53b1aba846bd422a40d4e0f28b2e93f387f723257f62cd0607da296b48f8d8e0281b37eb9dfeae466a08dde99f2c0e8660ca0642403fdc1161732ff2b7200dd8b39a763f8839c96212f119b9222632573325af1ef3250c590458416d79a3be361f1b8f60df0c6366f8fcd505f714d2cc69f6b8fbfecc3d4a6edc8c09407eaa9a9cb1d2dfc03d0090e18c49249393f75a7c3817e5ad52586ed79d7ee468655515bc964a6d27dd08e7ea379aa5bede7e372bba94fb7e9a5d4568dbb5d7e70e489e6722911b2ef4ff21afccf23514bc2e9eca970ab04c0ed8b33db1f42875d27c664df46dfcb0b077fd8dcbf890d48df1424e9dca6bce83426242656ff94ae11ee394b59df48ba747911cd3dbf88e5357c01536a00486491692b07482fd179da6933302c8950e61eb67e83286fc7f68a9acad7d222309d0111aaca31388c8d6487b05c00f05a1cedc7f8af399d65f3c99e1bb4d8fa8e9c710210a081c5d401a95377aa6130fe0d683509ee04245c38e38e0687e8577150060e40c44bcfe21fa0012d826cdef82ed8e6a5cd7497b0e4569a3b7f2b0716fe81be286777232634aa0c48a3b6895cff1ad089b8b8d00a8d69aa0811d7b2f73911752435762d86dd698671b2a000b87001b5c60fceab5123451c4085f0b69a6be16fc918808d40bc10ea5989f5940ac5d776b4bcbba9c46b31054958610924fcb0891a921fad900785edfb2fd67b2b87a3e15dc3615907628af5ee707ab130039511c8032eff1522d3c5a6e43046eb5ca16874068a55e25dc848232e670374e0e31ee83816e41238a6d920bb3737683b3a09b975259e00e0a4003ddd3608882f7c11258e285a717d00ea9f921907773727c259d80ea26161a615acf9f7b807aa3f416e19709488b67c34c97ea6c2da3f2897cc48e1d27105fe7768009c83f4ff31242b654ab4658491711548281993ae3b16d4b8c4d7ab3f9ae9472e106a25b59030d6d0f672cafdb90a0e25a062d937d531d31ad43307a04513b1ebcd41264c1704a173749a357379719fe414cf16c9a1e827d219113f5ba24f8521b9771aec2629ecbdc8b86da87656df56cc45b53a606a82dd89c654dc19b9620e8e6145712bb1fb7f7c5652b0161b3a2dda86ef34d8174839560ec030a93f0ba2e76dc9b9f779b2b570a69cd12b4889de27837d28b72d38aafeedc7cbddc5969016a7677cec7508651183a6d85d0db25bf088f282a07cf3cd932fea8ec8831e8fb98ab828e3f5f753ebcb072a87b7e8f9e24a7c88a10405b9c39cf676b6d46e171a407e182c7e55dc2d882247052f1f2adae0d409d44cb327403bcf425f265e34650129795a81fe4a007a7a638307673454dcf7c62ba65bdb050ae3cf909bcb07a5cbd5642a7cb3370b7d467ab38ed1e462c93748a2188c557b31f84a80f06995e90f1cf48ae7adc40704e975faafd89eb61467b940ea010110da6f00e7b5a64180a58bbea514ea924068a52191ac73dc8778e85ceeff5e158aaa3d41889eab6f9d73b123a89190b92b1d0f96b024e9021bd561a244e172d43a452dc26c59eee86eb7438409d3dfe2fa3e2e97b918dcf5ef2e089acea27b0dcfca9649a5c413572ce9871d1fe549d8185fef092336204dad872215ef0d81b676d3d0f2dca370a990bb055943fca14490289d221ebe400baf331be1aa061efa48d044278dc1aab38cb5f513028e2a853a33065f6546de0af9f44666caea35fbfe19608c30ca14fc0efd9eb1473c61481beb2f33b302fd49d423038c19ea85c4f933265e602f89f164ce5ca05e1ae3c99cf8a05e2ade97519ba1b949651d4064aa0cf54b6c9d339b832703515e999cca9d81aea1b2e5f4ab9f44cd8154552cab44875bedc1dd5b6f06cf5c644ddecd4d96bbcc1a6edbdb56ce2d83d2db76d3ceb9642bbcbb1b5cf6266bf8f63759a6aa55ff356e76ceea9df3ce8ce58ddc489f9d53ed3424ccb28cff35843a26e133eca4352bef2aabd2dbd89b00fe44b7745c4258edfd43124a597533543ef9741f5e10f64a365bfa245c4926c23bbd8d2d6dd22b7f5ad02cffd59535da1a4a3039c3f6e1ffa1eac0834e7ae1152c94aea0b798c15c165ce621e4f7f7db8cf7be5453fe5bba0af87aaa085aec7fb585bc446dee919d5275c76def0af691267ade97c527ed73d148c8fe3f3c57c063fe7fab173ae26edb069f7acc46fbaac53a84f34128319ca489b10b96e16c383d6b458197c20fde18d3fef87d7c79b29ceaf7f3905d09d0900b75c4fbdc6727860ab2acb21e6df4d5aa1fa148cb36393d546a276da1ac8590899280fc94a0b3f4b784ec0d6d57589faa2b86d4d43b88fc10e2c8fc20f334ffdb233d5056d742b9a20b1f51062ba3c9c3bd5bfe9280986fc005736efd9d799b35ddbb1b3c7729637a763758e62d866f336e0396ec8ca5b4655fbd0090693e3b1f8a0c84e430330a50c845c88659292afadcf156122b2d0e4b98a6e921bf50a5e24188fad95418ddfd76b0977f2617910e7a9486189094744234691356e7c4579cdf0006ead460350a4e83f403a60e467958d20500543c5478dd2e93aa9542bf4d0bf0c64ad61f588404fb1ef818edd9f5d020a5c4ac10830131894df952a339d236068e28dba4a2936fe418782065c8519f5ecfcfe9743f07d2101246266f8591a71217cbea7b0ba807220931a6496fc8d94f914297f15509cbbc79b45a423d7f120430d44cb0d43112a73909b53d1f993b16ee114e9a5dadd0b7cbd7d77a0621441e32c8a51cdd780c0492af02d36a392197b34da3f98c790000e83b4ce820dc07902df5fe401b47bda0a40a50dacd87d6d1379e273ceef5ea61c55e4ae7d50f9f9de8d647984d6a308b4a7f740dec529abba0a5bc94c687ff33ad6433da905588a3c48a8d0d04de3b9658dc42e6f0573d3b8f559582518389beac34b6df1c18164828d64f8db3e5025c01cbb411fc3e6a922717890220be8286974b36fc3d2e185be6d4e0c7ba28d18350ebaca62393587a155780abd6be87c32f6e1e17ba6b61641cb61e6760512865f1a2c32d5dd993c1415fa38839a6d7fc28edbc98b9522cfc528931a1f79df63625bad143dacc77b6d8043989c590ada2b14adc8aacd693a1d061deec02483c50a7249c50ab47005031a3d7077991c014ef20bd717cb94e67a3cb5c3f4629c446059523445661bd67b2406d06ba1a28c8d3994dd760d5cfb03c3fad6cae453ce4da1bc9f9895d06dd95039f6bbb4c7634fe8f9cdfe238071b892dac6b5c23381c8f20c59854d20536b2aaeefa249f564fabf9a602a9d41dc2fcef31ac5ab934cfd8c6ee674a1bf32c6e32130fd70ec54779799e8a16eb44ba2299f0710e5475682dc97001f0ea9984e5561902dff8a49bfc3a41b16a7b6d7f2c51b3f96d2c8b56ed5f219478982ab12824d4677ed5d807ebb140d22dfc7e0ba7ae1cdc17c6c7caf0a19fcd94cab439bfdd49791ae4c1a33c9c1c576cf43aa93f84ea1d0aae4a8a523c6128af737f076a8ae1b9767b5471310f7d457ef50b79c2533894dec063988af309cb96118cecfc333c0e51db0e46913b9859bbd0928fabdb5515ebea1935f6f68000d21d6620bf32f557d9038af9ec038bb6549fb1b3edb2f60642778d3d038add1f1babbffff69c1c5a69efcdf4690948765561c2d346c77111f0054266324422d1a5be518cbb59613230f09f4f75b79cfdf7cb727e443cc40d8903dc9153703cc3d5d58b169b40d1257d0cb915f908624761c539bac4ad944018665ca66e0e7f28093d0d540a0e31480531fd8c533349f621e6e1d410d1578a5a336a134d8d538bc866f2680c744581d7a05acedd93284a85ab85547ca1fa53fec0a4d85e94b93f455a37e83e2b18376549c9ee1a81ba169c98bd52ab9717c4e610d62f7ace0610d20450e57a42e1de7c770e6f54b6994cb8f8cd258de56cb3155cc2795e8818f162d2c56a12150c39fe0dd7dae44ac174ecace1f6146251b90bd861051d38dd2935704a8218688848b7835942950c39993dde21216ac6e25bf31bf2910bcc3e79aaee751ef4e7cfa3d46707b2f96814e84721dbb3849c735e11a9b1347a9b011a403fcabc67954603433ca96ab412dd63474d1f2ed97f6d576388c8e7129a8c1b40c1d92a031dee22c6cd5685a15d6efdffc31663ff11bd641a6ee499a5c609f15e0740c106c82b580b3adb616564de2f9642f39b7caa92da00dc183c63cc2be5ece095b26dc31454ca6016c26028641d3ddff373dc253c833285f49f089c780bb15e992c9079340757b5035a6ee3609a475dd0b02c1c1f9b61a09b2d2445799fa21b28436f5268c89ed4bc8096ed25cc1988af914758d7dd52e25fc63486d3e868706589418c4572f15e50dec6f6535e58bf79b049cee705beea23e5dbdd96322baf91262c595578bf0cc2b05b530731ecd721bb867327b112ef91f3c33785cec30ee8ef7b1eac3bfa811c7a73dfacc87b195a5d12af43a989dcab10ea917a179aba645e6d9ea464457e5e43d1157948bdc55055fd4dc98b00b136f97e6ee851398c7c5a0f4f58ca18e2d04a1d7cc7b2f4cd54895c1ca7be85dfde32a1aa4a1bdb7ad3b913b8987addf434466762433eb8c564040197f8199ddd70dc61f5dced430d827e73d2c3dfad6370baac4f28a48d4dde6bf882bcab069d40cca0d1546cab50670a76332743b09b26e26c2b96e0aebff22a5eb87fa368befd2af57185d9af12f4918da4b750d422ed2d60408cd6da374c47dc6ee615aeed45631cd9e81d9bcae435ecf2f18264e2f0f72063666a66ad9733916c4bb4b9ecb1af9ac83e668e52e38f1b544f165340545595b64a571002e82703431f7fdcf5e6ef366bc6b9070e4d5fc1173fb72cf457baec27f440f4f17cd9e473420504636f8367fa1c02ecf40ccc7f922f2272b4dd4a966ab7f4df1eed2c60025edc0c55fd293be1edb31065c80912ee955731d2709b481396904f2bc291fa7c5a69d799b200d45b2810ff0eca974fba153779cbce85b67885dd171472b65348095908707b2d730ec2ea390839df0705a4713a3112edc73fe105888326d56ab37123b119bda02350bdc7a4640953734ba79789d7ae0508ff17f51f9a95cd398fe909f4b8393eed1d25de995ebaf30607084ab1955ded2114d466c77c5005bde0bfb629c863f160c15ed7fd462efef558fee13ec6910f704dea32a666365bca16abde86d2f10885ad227bf6403a207e495c948d6b3ed9975e0a8b04dfa5f00247fceb91f2cd261190303bca2ada4de10804158d4f16fa1de261c85a8bce0e8c85c8d4ed6c5f848670d9d9c053000bd56efd10a9f719315b7d312caba6ab1aa4d2d18584399ff5cd110f38f3416e053bc9f652025626e40ced80bf4255637ec793260b5cd778025cda4cf00d29bf2cbb501fab5a3c1093008c46a73f5d03d9de46f3e011bf762061d5d2ad00354f018c852401ea69f6945f6f92045dd7baa1ddc31cf1437f113dab424cc6acbb4340792dd232cf674cc202820417117091415d61634e9e48b2d8c537b3b65204c85ba29bfb95d19933b4b285917899c1de5b6afbd032c8d1d73c8ef0e61e6e29be4efbc2048814eee165fb532c9696e65bcc6fc78c7ceb51a555cca41eea888727050b56612cf6e3bdf37f76c2b36b746e0cfacdd6eb0844a9e402426aa17a646da5d4da49feb7fce2c37292141682c9ff5863d41bfe68da3b00cf5011e89a9378c542c7c32472c501229ffb7d718a3a6ff70670003086685e63ac8a70bb66e0464434901cbbad964d18b8cf0345a0ea88b49734ab14308642d4f934817917216f4a87769f97a3a043450e66c551d342b22d10696b9160cff41dc8f2f4604350249033a66a44bcc7883964a0766cd99c1b8e47a1366b150caf197760a91206c3d7677c447a71accaebbf385543e0e28bdfb96414a8e1efdf0de37c44ac2d6079d0da014cd99baa34a6420aaedfc48732f9f76f2f33c9f009d8db30afff7a01a2951970a316c2e83c29820da6dc02fb0f34973e13c74780ca3f378e702ebdc5e81c11c943a66673d0ec19b5a2fba2e7014b99b706a7a78cc7c4ea08214acafb03586fe0e53a2d66838b9a2f3ad51e6110d1850ab051a6a8fe8cb5ad6c74b508675659e26be192b3ae5ef65c0cd1fe8bc962ca3b1c8f34a6e490dcfcb1088f9890f4b16662258d61264060f51a393ab1a9176c51ab17dadbf481b76bb59e014e6d21c3a980910c6d6ad3a180ce1eaf2d5c42ff7406756e4fce2cca8778332ed8f36a1880d3e2ac6ae6c856a8c3a09ab36fcede2ae07c134aad8eef599b99594dc6e028067e4f16bac3cb77b1fac5d6118988c0b728046e655edb8ab98f97060fdb665e624459dc9858c52252ae7faff50914a94c03b1754ba11a6ab1c171f8b164fe2c7df11e488bb65e46b36369318d8e10d7ec32c52d5a7845d3e7fceff99450f9d5a937adec82878c2a26da36825458a0e9ffde70e8feb2b2c68081685b4ee6a0bfa6c25dfe8d776511824b8a5b5f000e2aa1b4ac7221de7a7e37f70330d2c600f1e5fef51e99fe7c962fe7d52ce7e2f31ff9d1cc08a186890a3f7b794d6bb3c135daf939e5bdf5efcdb67631ce570028692a05ab576169b4705dfa773d472d87ae8c01e1fa8e31d43998898dcce76ed3bb77987142eeae1963fda672c27a71fd5ac6926d84844d15a2d0e6a0050cfd31ccbd610310e55a066c826270b736032e7051dbb678a531c6c910d6612307c31e461decbe91809d163c61c7fb02099cc04ab33798d1d62e6adb649397d7b46a26dcdc1b927271cec2ccf890ffc5ecf3ab9a66d2bb7fa7926de006db4c796f9eeb23017b031078248707d33f7f86414594cc9bf023016a0ecc0fe8cc76b82d66962b6a93b7d02a6b19d12ea568e006df93e19489dcd198d3994cea630014ed3799ad66ac348caa94daf762c83f9437e4b642d0fef222562226278d05cc0d46d3295049b1abf2bdda5ede566c2fa1e678e046091da62ee011accb6e1830b859e0980cec4c9bc076e9a908fd89619103022a90900814061b2c555b591e95541a2662413055db6dd094213c51c1ed6698891498da4acdb60d13fc9a466f5cf9e1afb9913d9a4f300db102863298f90bf8add3c400b29994241c6a338b04a812bad24ff3a8d7aa84d6880eb8e841570bef98965b8573f137a8fcd9660579766ab1d7ee6067ddb956d72d3731827efaf7b7fd989c1be7cc12344cf99609e7f34a0a2e85740bec526d54c730a0de28037dc0821ffa3b146a6dee23878bea4d525e1500f512f0e1d6cedbf5a4ce5cad9410c6f1fb778a23737a3314f19db8b8e90e5ea2311e34bbc8d9f683657586e8b678414574084e654341cecc2c03e3ddfae21549341bf8b181cfc10b80c64c95984b4bbecc3bcb40bf922d25ebc22824a490d60b92cc7a094865956853694adc2f1e34fea8eeda2c31f4aaad98178fd46f03ba1f5001948ce1825dcc8d22563f7ef0194d11cb3965547a7da8dfaa1a9f96ddd69b1d5c22ed2f9afcb0db0df47160f45c56b57127ba5130bd97586dd08b5614ccede5b6362b34abdeb8f8d18dd7aef1278a8f3762d4c1448a6019a8900c857ae91992b6c6aa59426395e2c5f835bd50d5386c9dc359dd85b201742f77f92e1e69fca1b10d7825ae5c3e76689bec6ba2ed47fc4197c0ddd34c8aa3169930111a003889fd14c086c5fa197925c16fa322b385feb6a8cfc39713560b6a0b9f7788d95b1a2516c24df7c5b60e2b01720554171be4876d807781e2f85cfc4194e0919f3c906dff895a91ae911cb17dd66b6fb5fe8f5da2787e92dea6a83cd22bcec383f8d759e5b3adb75e08c40637970dc3e9348deea42cffc583c61f22b915ab89156697cb8d862b35ab1eba8c64239344103f63826b71b5319db8806e99c0016aefa01e06a8e7a58a46374d5b8f5764b6641d527114a207ca3430b7975b6d568a5614ccede5b6362b34abdeb8c89368bcc084fdba8f3df79e8c9a2c2b806903b1ea05bd1cbe4f327b1394124a0ea23029bd05beada2f91763a124a500a50bbdfa106ed4c0e9c202ac44032a5006755d5094554d1ba0dd506fc6a7ba58b18ce2fba28a8f733a8254adc0b0af56ec932491f628ee9a9234e261fd0ebb980908e84886c2258410022036fa5879e0bfa2b15374953601eb11f87fa3642519b0681661f5f1768925929248647777ef580711073007262653cacb6bfd095930fcd9e3c0fe3ecdb08dcd95978cd7cf1c965e27049d97f9f5b32dc6acb3eeed45e3f56bd57aa4a33be5d8f652798d188cf50773ce39e79c73ced9cd69619c73ce7befbd570a0109e1f89d4234a54242b5562124d65a2ba4e4de7baf10131d1db992ab9e9e564b881643934193614e914824ba775e0d880ce777b024df84c242065c912b9884d165175ce090e360cc1608210c1852066c1100c73b74551b79e4aeb530f03583e0898284243265208bf9f447d7a288f76ae7bf0bfd597b22576209268e50a10a4df83861852b3c01c54f8d42a6860ed8700387076e40e5f8000866420059a44bdc9606b2c87789560490457eadb5fe902bf287604450909004e03db27444967c64a9f6f470d973043805203d1f604109a28022091d95c61e008922881024c870c5f4b42c3940081c4418710322827044006a76e4a094520a84c5871835416319c822bf844f59d4daad01a5b9422bdd1ae0c25ca19fedbdb6928268132c4ad06b0fb2a0fd9279d5eef612b1cd85d2c7ae966d1867135982af6d2f2588a9b6b5eda5041f52edf1f6422964c95bb6699f350d37803ed61ec3db83407bd3b4911334cc0562143c3efda185eb828c8e11ae1f473874f58088ab0746eebd2326885690c57d131dc822d394756be07420cbac35d48a75b822f87c0cc4ea7531a2e051762f48c8b98c7a1c7ee9ae310631bdc23abbd71ab884528677fbdd0bf7b3fb9063bd7b217bbcabccdedd0b227ea594525e990c259c1302590151f1501117101d406e54199e55c4404db0d7323e8cf0638c31c618638c31c618638c31c618638c31c618638c31c6187f4609ea305c7e8c8c0548a207352493a7b6453ffe9cd95ec9977c9f2008d1983570d277f9848aeff248f75d22218da87c44f914df2987bf3b86a9ec8e691676d79d0a4d4af1547cca6fef460fa4ef5898ad396766c123fdc8b33cc5a5495ff22a3e679b8b0a8ca482b4bdbae291c5cdc8a39c0578803e55dde4242f45c586b2d479904baec28390a5247216bc17a0dec5ee48fb057bdf0b4ae9bc941f8dbccbb79792c233f9134fc447249736f992474131313129f15ac0562b7b146fb6bcf9c39b2ecf763d74c9e3f74ce4eac42be15d0fed4f3c92d689178a29cdbb138fe35d0fadffc403f1ae8789c99f781b1745930d65a964dbcd85e4e3d6629278f3698749b4005fa0d9679f7df6d96a4d1a9365e39c737b41fad06304986da9c8900a325cd153c870597fe4435360b2384cfcce06ba57518bdbc752d274c2e5635ca6339ce52ccb40bf25a12307e58d358777b6239f39f6aa0373016e2e9ba885615a05fb1612eb992d10eb89df5540ab4cd5b797cbb15d72d3ddd0bd4467c5e597e860a9c96a7159c223854cc6c4362df75a730ffa0c1a2da123e7382f724904d34c88f02c7e06eaf3075d15e1d84bba3ae97ca03bb6379045acbfd5d7eae7fab87e86575105057b9de3c1e58fbb43dcc1157f54561032a5b27a14b2c4d8834689cf5a2710464aac5831eaa174450d76609304156c006208337021013cd0418e9c235778810d2e20037aa0a0e3430e3cb44260673ee7d947bbd30c83394ae9cfbd3bcc8fbd38610b0e9f4f72935f88bc5b80d7981c06f4a2873090fcdc273fb537f9b97bc9630f839f3ef6eaf340c5f19ff0001543ffdc9dc6fcfa12a3d8f29e1bfe0568cffdc47bfbcd9b2f849ebef6fa0c3a306c3f77c7fd7cfcd4ebb8cf4f3b6ebfc09f3f3f7efd19cffc1646e4dad30f6d924bd38d378cf6dc8639794fdbf4e485f8271b8a768dc9499e3ede7dd3925d6372908ce8751af8c45ef6e21663722c01cebddd626c30f4edf3e34d65a9d7981cb43b07f89c3cef6d73bbd3af31f1e4f3e59c51d073faf8c0b9c9c0d953afcf30cadaebfae3f682e39edb1cdc5e74e8398ffee6cd90fe6ddb5e6f3b8301c7c8367e01866cc6c741f070fcf329eddaa31a00bde5f931fe197478e843dbe3369a9f72df41fb62fc32e4e9f51974ba1af4fc0d3f0d79771a387ef85d8b3cf320c75e56819e0c40175442d1811f1ea7062287a20350f0f8a296287244c0a18340e5084207efdf824fcba7a54410a81758d81ae2039ad371425b251871e388d6075a3b5a3db46ad0ca6905e183083e92e0e3081f4bf8b8424ba675c3c70d3c307dec00258bf80022c4061f3ee05dff10ac8f1b364c1f37481fa9948f9507a6407db07cb8a00691e78dec061f291f2b1c104208a1a651e210baa8cc967baf54b11e95db8b00b847aa7bd62923ac14443b8e448672095d05ad397401710408205c314629813002441108598e80d240d04268206e6083052207ab9c79d7313fdce009403a20644812a2606b58011128b582118dfd1ca177b0ca09020431a708a5263949f0ae7f02a94ad04074e6d095634465a5a05510057d7182680fe81ee81c558eff15393ac8cfdc8b284db4520b2dd0186b8ca5b9f22f99bbe042b59b8ca1ce7fc9bcbe643e37065962cc248c12b2b8cccf9ccbcaf61279d6e08c1983e8d74a27764339f932318cee2e867cfa2f7253cae7c314a43473317252bec41871631bdb91cfbd7790927631289d3464fa99a37476b6c61ceca575e259a3bd32ce4bb19a599c69ce53c65c338b6fc6b49b312ddb30e80328ede2d425c6af5be3cc159ae9960ae75e241a9d672456c369dec169de81d3cc03a739bc63ae6dbfafd6c92bacab296765551c4ef308aa8eb1c171ff719f8ef643e9fff457c3c54aa2f587d2ffcde80f06c485386e7b12ee49b690267992ad7e27093d365b4c3eb4af7e923721f91292d056b3bf14a5512314eaa3f9683e9a8fe61ee1d2e171018188a6dbb66d97da6da334fb8daba1e728e5fe94a2fdb403f7210f06f49023e1b8ed43dc872e3049381ae242dba85f0ae29e7bcdd13fd99c6ceeae2c28e49c737275abbf7d34dcb67da8edbf998d036d1bf7df0c57b36ddf428236eeb9dd3950e8411cf7daab415b76502889428d50a88fe6a3f9683e9ad46d41113bf00170e8d2f1d9ec9c73826eadf5a576529d54279b6800fc58d370d470d470d4fe94922da6861fa4691a7e90f620a94990b641784aed374f937fb239d99c6c4e36956571a0d9f9a1be996fe6a25020540d2de79c77aff1231290bd6999f376c7bb06bddf87a5f2b4f3ce396f8c8d7b4a9d6aee69077aaa39edb8f75ad70f5c40f87181d00308339b66826519efdd63ee3dd9a0e897b5428ae72f854dec4be12fa55fee7e3fcb1b959f7b0cdbaf7d686b2090e6b21a945f2dcb9b4645f18be967d9a43fe347f3d17c34a94dda1afca2f2a48a5ac3e2c329ab9427557652f1f92e8e000e5d3ea8f901ce0f523ccfcdf503192e000e5d3ed8e1596a71d34c7e5616c6fdde6cf778b239d940b13dfdcaaa3d2a4ecdb1f6e4ca2715cf18b3b692a4b598cd1fe5574c72d2db7a83a203b7d33e84d65a1269c3409ff42d6474b96fbdca3a31d1727b515976774d2b2bc6c62975aaa1a71db207832149ca532affa9269f766412e96433ea48ddee92e7938a5b4d49a8093da1b0a2208beee9c6f1056c779735d0a37cca29254dd8db3fd558294faa932a931e63294425291fb232db5eeccbd0b87d9d4a7938511e4e9046efc945492de4a986dbaf9db7fdc8cb1fff7ad8a778289e973aa564a9e2d75c999f6277ec64774cb44d764741096d153e7772d62aaab66ab5b5722fb717cbf16bb6e814d651546646712eb3380cfdcae277531991dbddfdfc3535bfa4514a5c85e3a0ea67bf7915b5bde487d1e82643e3f94bb3a5e61082cfa971f8fc934d3ce5934d3440fdec4fa9795271cc5a6b4f36a21687008819c5144ce4708d937ccb0737780238740d19c25d4376b88838e21ac2c397e299aba0b75fedd71dd2b4aa3b48537efbf5a6a2668bc997bcc55ef6f8e3ad39e6166383860ff549ecdbb17930dad3c023c645fb1bf69be43e1a98edb5a83dc9ee926fffa5b8fd18b726bf471b23ccf6da5b6b6f8c0d0cc3be1dbc5fbb517e1ff7af865fec4b7d1b86edfea5f8fd1a92cf96f9d863bbd7a0d8f78b4d0cc342dafeb68f5e7fbbbbaf3960b0f63f9434e1c7fe9b8927cbe597fa521c7f96496cb127c1b9c4839304db3d6ed8770e070cb3f6b1d6e279ab76b3946f9eb51a96853cd0739ef6f17ee661ea651f4a966e11eaf7d8e14ebd9126fd2a1c0e5847fd18b3ef2d2d3c5cc62ddbbe7ff3b56d7971bb6faf41f14720db86b1f82f0a6e30608fafb41fe3ee320b7770e0b5fe776b50be3df7bd65876bbba22aea4d3c3857b41ab465875f8dfec037d086ea5c0fafffd1c493b6bffc1f2ae72fc5b1cdacb53672f861262b90143d913b13cc19e75cad28a5b3d65aa7b5d6da79efbd377a1d122612e64ccd946cf5f4c4555c75082312f47c88d12d035964f46037276908975c3e8c19b2650a91c3e54329e5ca089b4b882bcc486d0d9724e0dd04be80743d4899821035e80881438810581e048942909e204c04e111a4479022d00722c8610948b021a2d584110429626483ce3c80f4c8394a0419c110417208b2023b0a0190201c8810204500d90192042046801cf1bf628922f3468c110559d0898d6024f011fc44be9167720d9752e2972c75426029f9d48b314ea87dc8b5d206481313ade9df174ab3c77e46f65886dd8d61746e5290bd29110c9b955639271de9444abb97baad0d19aea8f6b17b73bef7de69312274c7f8c910bae31e06f4f9b94d0aa2413b4359dae226e5f673ceed29a574fb5c3590576bad75fb6c359067adb5d66e9faf06f23a1dad7da6235775d5d3d36ac5d4fd0e5ff7efb63b1d8dfdfd6ce33ee3fc1db75fd0f8b67dde187f0c8f01fafc2fa0cdbaf9e106da1de8b9eff2c7706ecb00da2f447cb332fc7ede2f441c6fa9d327abdbd0e13466d6114745e80de18a9e5b94d627b9b4555916c0127df873255b6c6aaee48026bb92a5946cc93ceb035c49ada4c9de48cb0268b2318025fa94453fd7550ca08942a8007b43ff469a26ca077bc32908eeee73a2f6140177799544644b2d2257e8172962c40812481ca1b4cf1ba8b9eb4e2784eef666d4ed4dee8290d2627c9d2e88eed8567fd25a6bab5553296bad6db5eebdf76ad3c3300c9b9e8ecee6d20dd15dae7a7a5aad988aa90e5fbd85212ea420fa65caa0208b0c2f4472479ae8eb14e91886611816e9963a58892c2b9e81cd15696212b5b6012bd68bebe123b8077ef42cafabd98225eee996d0bdb2700fc6812cf22bc6015c913b2a45a00c8183e0b2e6ac642a0ba216dfc6289c52271b27642868fabd4480166494af801630302c7b98b964d1621209990823dce3d045c4877af8d97ad3f54ceebb1d135a7bf9d8eb158531bb2515aa0e490a5407b2789c9c96eac715b40eaacecc912b4ad278715a3fd487fa96c8342a12d6f27052b5843033c445841cd710565f8a240211e65245a1125c4225648f3b32d78f71e615749163f5348e8b0ce394389c0e64916fc283a35add1f3835bad7d5ea5e9d2b9052a4142985d13bb1b982daa979f7c5b0ac678b6808b8225f0786c3dc5d77dd42e9ba85164f551df0be0e1c70d041071ebda6200bec7eb636a7a00b0a2395a04dd573caf21c2795149b555ffbd656eb616d7b31ebf642d3b65a5bd105bc9b14024db50ab2885cf305591112e85e738acb2c7e26eab9321bed60c61965b8134c20438c9174439c9174439c9174439c9174439c9174439cf7de4bba21926ee8e921dda0e3434c4639ef8f67c00d1c1a0eed6b6aaee4d2dc628070a5e6802b546b144639b76d6bbb7b611fce39e7cffa5aae1ee8b5cf9aa6695a7e08d2be7a96571c69ea95c5359ebb0ab7b6b2e6ca8ed5bcfc953531765933bfcc8ff367f9b1fcf77bddb17ccb5f71a449db3dfedc5ed0cfbbe2c852fcbcaf576bcecb42a1aaaf0e097d7924c2e517a99d154838905f5f524841f1f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3935fd35ef4ba7bd560f2d75a9feeddb3e6410e52654c9f33714f7ff793c76028a1db8b9eee6ef2982c4d2e7ffdecd99c3f05fd92d0539d5f6214200acabfbdfe9937e82d25f9fa21afcfc00303e8e9ee74f53afddad74eef17f9b57f2dd3aefdcddb936c924bdbcf1b667bbd6152fcde291ebe8c5b8c141b9eec1a939774d1a626bbc6e4dcd72ff13a0d9c6a9f3f674fcb346b0a7aed3347bfc2a06d2a4b35bec6cc959256a0e90bcd8ff157fc79d4ab8a8742311cf4f4435abfde1a85c5495e6f245ee8413fea28a00fed5e551cb4b1b661f067bc7ba634e7a7d5eb28d5037d556920f2d04baee53c030f277992dd69c0c8430faada570ff4214f6b4f7290d7b9ad7939bfc6d33da129dd5ed407e1dc6978f184d6b28a6f1bf7b0057038cc80ce0c3c7ace0f18e1041e55f303461851594698448810214284cb32c4bcbb26b8ae09d20a8420048e7bbdb7bd51dd35a129e5842084ae099726ba2664483251903a40aae1458c8277affd8cfcf871442ab1a963acb5da7fa1bba27645f118e5a4d55eac7342cb9f4e640ca33efd97ba6bdd178851c470897dfd174c45dd1df6d977f56378b665c0f60b1575431639e326e7a494d65aabb5d6da7befbdb7f3d1f2e7ebd5d3e3a3337d51c1ad0a4ebdae7da042109e28e2c53307e786d100f654c34fa91be4c975559894d65aadb5f6de7b6f15b2b9bb2a6c2f30ea9631ecae329b4a39af4ece1a2b6b5ee8418f437731841ef43342f8413f43e3e73e474a65c5a6d52f906b7f3718906bbbbe1663db4c68bc7bf6127f8b49437e8b692b9c33079f4f038dc17232a187d5e3a462b158ac53cdc9c608cf4b966a12ac2458ac1e383860962a5249f1d3efa39f792d2f4c77457ddf8777b8e178855358c56b37858afabeeffbbeeffbbe6f0a5a7e9da9298c5595c82c560f1c1613b21374afae8cca285746b932ca65249b52b9469a6cde9157d294f34dbec93c9c52279b277cb28ccf135986c7e5e31aa24364a7c82b774cc82a8fcbc7354487c84e919711976b880e919d22af93cd694746f9b8b2cac73564e7898cca33ae8c3ad99c522e168be53ad964560622c315b4eee031827e7b14ad9b8031c658f7043d1fc356b8ae3a23344cf635631c9c835bd2f4439a2c1e8275a429633f7f67a70886fa68eac63cc8d28d2cb1f00d6649d38d190eab6b42fcecfbba232abe3142476c879bcac3c4f968be1a6edf45045e619734757c5357377c3ec62e2266f87c5c57f8a3a91f6af5d1ec8f0ff2b61ee35d7d34f8a5e5d82c08a46d191fea9b49d9ed356f8b315a5b6be5360988a62cfed1fcf0795c6baeccac7daf3ff069513f5f93b0696d5dc9d2dc814fcc8b2f37120edd31151e9fe274fa43c470200b15100f6481fd7ced093c7ef698fde8d95edf6e1e280e6499cfb9301c3ea38d7d6bfd7a81c9edb79056931a0d6cee98a061ee576e2bbf7f5251cda4d2cc92255b7b701b6316e32975efcf2de3de7bb39f5e16efbd11a390d33fa9a4a9fee71ce9a8d5c36da49462d845baa1addd24979e36367dfab5ae302898f03ad9f4f0f89c54469038f23ad9e4ac74cf2acb35cf2b9e54324ea953cd2975b239a94ea9930d76aa39a54e362715764a9d6c4229d6c926bf42a9532a7ec633a37b7dbd727e0c215284c8eb75529d6c705a2e9d9d9316f6a1c369113a45f4e0396a5d13340cf695d39734c52f5587485324e222a940f73cc3e99354a03be6994274f873e81ae206bf31a6c754978bc9c764d0cda5d6da624acf6258861f0af24af3c5eb558f72f8d1e4abc1d9922bae3b7e34355096e2cbcd8564d38e755d8291e4974b1589aaa88aaac8d35ce2d5482bddb58c5e7992570f7208ec19ba864f159f0f90a5a9656456d28402ed8a5a0af4bb88b626c32ad124d9111dfaf939bf66e6a9358a76ada26824f3c8d2fcfcd264a469669d01b2341f0239a4a0ec151acaf36080e2cd45f4f8d5234dd5088f341d91261f6992bb7a9a0e1d0169923fdf021298408d0d4419ca611045484119e5534e224acaa3fc8c944741f994944fd93652b40c47411672b6a4701bf71b4acaa3886aa4498412cd8852d294c2ea30614116f22d8f9691a6d0cfd737a449f4f3354acfe41c3af8cc2f1e3eeba7d4ddc580f2293f03e553b68d1eeadb48799419231b283faaa28fdb8b4a117298b020cbccdff34bcb78fa8646cdc892d452cce875f251bc2e06b967a4fce867748ff2dda3fc4df1505c37e4f5181eaa0fb790482412795dfe7d121bc3492e8cb917204bf32b0f64999fe2d51ef8d2282db3842ec9ad6529bec6b8ee9a6b14aea7fece1b7928a1d093889ee44bbcbbb9947c9e1bd531a1eb87ee7d9148e499884c48fe927c8b594212fa1632b4b15e5139563387a6654c7ee0f33339efa6504a5be902e68e48aa1802a1fc88437bee716c0f7a0ef4803d07b4e7de6e0fea62d09efb19da737bc6f6a00de78a95d8d3083dd20edd356cf78e098d5f3e86bda59f79311cbb2dae2a5da87cda44862b7a475ad5a0610159a2ad131d7ac29226c933942da396eee1ebced3e4ca08c36016ed88aa7612c2b91dfae49d104e08278413c209e1e49d13d18e68475444f412f1889010f5887c267475bd6bbdb0d1f14a2c42164c5a6f022cbb2370f2e3c425b704ac8bcebbe7d1772871d4fde867743f1a7d47ea9e54a589498aef938b1e9ebc88aabc8aee873475f8ea70baae254d294f497ef40a9db04e7a9ce09ce448137e2ada5902ef211cfbddc7b8907677318cbefb19a3ef547eb4678cbefb6edb50f1a30d27c977d1cee8055968c977e873c23ae9e19de0c8127dec9de4c8129d910137545ec567d2091e307a16fe01dd9338d0c30c154f7a0ea8ecce3e0b3f7a169e032a7637770c2cfce83bbb67b0b047dd8cef9eb4b34cf332d5503cfb70d3e69cdf4f5815653ffbed710cdeb2084c09c4ccf23269870e7d664116590a9ea4f86973a6f3c26dbd5e511d13da7eb7377c061f6d3deda717c32528d33228417d062e471ef729bc3ef9c9ee35c715b17a5d95785d93787db4044e3f847116c57956464696e80d59a2dfe100aed0d7a103071c749072431ae86865a84569a4558d27ac0e07b2b09906da992bf4a5bdc93bf341bb63d8b6bbe632ef6495bc83034effdadde1904077e8b3011b3228f9d44a6f44ca418fbefb3e8112fd6ed48d6a74cf3b273bd2248bbca489070969eac93ed877b931593291a50925a4588c1464edd6b804e0ece95e27f2919052ade0cc24f5435e867273d93e574d6e2ff821c4b964c3398364d71488dacd65d35abff43a26b4fcfad1eb33f850eee1169f7a313c72da83bcdee2e25a7ecdcbea0c3abdb22a4e652da125a6513836b137f160c5bcfd02c9cc2747e7d7346a46a7a4a9866365199434cdc455d63eff0ced737e2dc6c59d9e8d8f77e77ee0589025db3da7b28c976f64549e91a52ca393f14e943d51febc14c3b0aff17a31bcceac59433269f58009c0e8d0a5a12b46928a64021d35797dd0f43efe1817d8dd5d0cd9e39f913dde367ab86f23db312eaa0c4823d01dba4823d0fd1f3001180b80c10069eab0086d419a263595624fad82cc7aea0ae4048c9ccc6cc95ede3cf6312e3ab8b78d1ee0dbc03edb70aec4dd5b5e3330ded0058cbb57528ec63efb9ef5cc16e8423e7703b4822ce2673dd27424f3c99e70a2eac0bb3b4807ea0468055964d6931dc97cb2276449667a65255d41ae1f630a397b7a68b5af170b9c9262408ac10b22183386c7592f10120f120f120f120f120f120f120f120f120f120f120f120f1e788001e9a6071e52509a9072d076742d7c4196c9f1e03c9005fd69654a8ee073ce121ec832391c3ee38c5407b2cceb552b5ba3158f9f47241c342eb916c6c1381807fb149fd9525173257e2aa552ad5612d405a1b923525690452b45071ebf7347c494156489d8a487c71fc5ec0559e2ef395ad11ba880cc80091549875a493a9c5227153fb94ea9934a4eeb7da869739d1f4ade5a6fadb5d62f95035686711c8655ec6b94917bcbce47b365d983344c638ee33eff08d318b739aeae76f0ea4d0a6acb0e958fb1bf9bcb47130d203ff3b4867d7273c1308d9402fda5ac900c576499166f81439790142f024ebd9b8ccc638c5edc64d05a238fbdb22476b2c959c35e3fa5409d06c89e6cb697cd25d656fd214bd5655ffedd5c6e8c2414e8932a6eaf53a3c0bd58be4f2af931d3ab4ae8aea2a94016707a96774b68cb7b8c98a0ad8717159c33e6bc9a9a2bf77e943163e8a2b6b09c38ea0479d0c588948365f00d8c92a699ec06b290af3d5165ea0d1770b64889f1ded9632fbfdc76cf78643790a5aeb8b67b45d51bb254ff7a30db4e7df8bfc42878cef93bf5f05b2ca58d9dfae0542627145966071ae300beb011cc8391c07287cb22e0103088528e34d11a9f4ea8544eb47639ebd4cc000000004315002030140e07442281402c8d2541d41d14000b8d8e4864509b08035a9203290821070c32c410000000022033421a8100f24870c83a800ce3eae86d3d0f7e390d2ba27ca400b8423a61000c8de64282ffbb893e3c2e61384c521daa958ad762ef023a760035ee6e39162cbdbe5e07e3830192e56b9228429631375c2928c2f5aec630675f3eb28457664c49972935545dc4f73ca61f6e2d2333caaa7c9cee6880d1ef7d0b88423b35555004fb30243118aaef5406eae46b7a7c6a805c54cf8b2336074397cbfb201bb70946777f8b5d418f128d27837e0ad2232fc41d7137a5d7e2f0e1574c6de0f9548f3e9a67616c3cfd6ebd5c6b82f4dda89c9295ada9bb354e186451fce7e8018882bc3d8e129b52f8acc03d36de6aeeb783e8b51da540418174223181182135704c3d8a416cdd7ae95d74bb486ae45bd027f49c46694294a01d3dc3570458ee48a112d9e166876cd677ab10523ea293884a22e35b51ab9a2c38b33d25ea6819d3611e512ae38d7e31cf37f0b5a2b8c46212455f501071332e66a61bcfd85c03f94fe2817adcdc607d1388873e67eb71f3712c12dbf20c8a8570219e3b1d21bbcab5b31b16591c6b41bff53aee49c2df445b0c1cb7b33b0d111331534f108e935c07cb8c4087e0fe6113123043b8d575f58015a120a29a8dad9842e3d7d50c248db05cb884f10ac07441f7849877d2e850804359b43478c2f9721381b81fdba86620673f88e2e924d8d363c8a551f34666b08eeeb219d929a175d43cacfc4b7436dff678cbe3e5826f77a24826e1536fd751bd99b7d309744be385b6403a8f1bfcfa4264c14597bd5832ae3872c19ee0cd6423e441765ee69ce84fe5c81ecdee23e2355363e61ed13e42bf890127b4b4504d792a93b623f443e46d9f9233b5e2c8fb20228dc10634fe8689f170add37b479b1042b35e8cc596cbda64a5a215a86ac4aac89d883d86ee0ee72d095ae5d44d071384b43dfb095d4660b9aa2eba053565c727126ac5b33888651881aca877ed593b6bef7c4e93648308953e22556d0d33d51867fcf94bf6c8109b0a063fe9d984d929b07cfe32fdfcf10169c057c1164da5b3ef0c86368904ab48fb82f6d47f83390ee4db0a2c9507aa01a321bde00658d61150df1862e3a0a017ced37bbc442529290640034c341a2f0fe93a6fa431413150faac9e5a625c2ebd53d38e9adbc83757e7b35c10a45327e308d0b69094b7e86f97398c7111ccd5325d93b88f3dce36583e94e8601a0b7eef98906c60ef1c6a683649de971a3fff4f80c42574e4477009b206b43dc50cd71f0aa1d9c8e41b8645479213f74d4631ee1561216b1da8628f5cdaccd041a6b7753c1b7446f423201273ca12fa21e9191060403f47585c157d1ea07d3612856411d26cfa4e3806d4509a03d39e488c0763ea84f6c3d000203e0c8a9f2c239801515f84976d78424cba8251294bc322345c0339706c643c37c9d6ec4f708d98316a21e2680c1430491f4cd17097f2e535989f392f41573f2b5cc6140b6e4be674bd452cb99087ec87ec4341ae264f8002353cb2ba4b202ef904baca3d4bb7c10a3b57df78c368ba28b890380805b2a93668a931ec6f3312deca0aece055e5242a763085c4809fa1ef6f63d4823ea2ecce0a8874aebb52702598074bd2f446840ccdec9bf23b6cb455d1866113caa03129d28641a880062903acf1e3f0cc81a9df509ae138374b896fde35ff4d788db138ca5364874ceb285c75ac54ef4dfe24104a3be822729c3e2b13b407471ea50a8373dbb039c47241f17720dbd16c4f6f2b095a5fb8852e17b035ad69ebea0e9062e841ad311884b1f81a01b6b59d1661eeefe1abc83037a7d22e1009c6c5d52941f0f19eea1f02f36f1437e45f0905f6d42adb5e18d08b1397a7217e252d6e399dc02cab38f047d4b7e21915c9b9ae530e196cf567cbf62b8fe6511e01399e76ea8050f268285c30b674abec4a5d59fa3bde556cd9f950435c920a9bb5801afb382b3016fab87cf94db842c3b86d963cdca97ea50818fd9f461e3693274594ac2b8c8ee317dd1fe4124619f19b15e9409839f7f0257bdcb87abc9c3ffd380578ea43d9366936ea4f2828c91f87211dea219bbc57e2a9a9815c74e7d1104fc22f8869a2959d86c773fd7442e2c206c433ea7c55add4bcf309e202a0f8330809f4261ae00aaeca4e5fa101d2fa6844093cd682dfb368f64d7d00f0be59615965b49091c553ce8f91bc1143825470f973e600f1a95abd2793898d1949c2530fc42d91804e63113813b4c43a02b148809cb34a1b87b3008d90e8edb9c1aa317104fed0311a13fb4dcc8d5ac1ac062a573782227839503295a711511325533462ee68f423a9f45bfdfd333438d7ea2b68ddb6db82859a6afffba9e6b36e29a9be63203faca6c4d312d999bc9bf0204addbf7c45cb6c7cf5e35364ceda9231016be3fee620ae10e4bae2889469a37d0648d0ed44e3dcd69d5bc1ac42300766972cd11b46fe04f665d5f99186da0f10d6c1a8ea21b2b5b07e71a6bf3df408e253de73659207340bb5893e63caa4ab020c11ba27ab59715583d3e977520766de65c70e8d83f39c167cd44fc50bed8fce38feb7c84ec0a840af48ae22959392bb1a3f872274e55fc84a9750c271049d8bfb3cf80365da4f2445c93b702f5ea42cd44a455ebbe098d64ca9ad0ed6ed2d7cd87f0bf70c4ec049495e5a29f5449a299b0658bc3b49fe6ee41e75f44f63670b61c71f20aaaab8a710a1ff46baa4b4d8b601c860d8075350f244c7e02d09e4fed013a5d093e3495d1b9da0e13840fb6653ebf99eec24f4570bebc7136e17f33105c1574c08d4b3755f7fd997a3db24f4d3115b034095e8b3dc88ac781b382aeae234efa9913da93a5babf1b44c7d58c93f7634314cc2995401fb996d8d60e8833fb692b295a77b3269224e052e7b02da755afcaa738be2f301331c677ed22f7f5ba21cf60d7d7d107cea5410082f43c60cf358efe78f4cd09bdc2b82812e44af024db2d0b4f17fb4e46edfab60363203583cc640616ccce6edf9d75df8a6dad824ee0d9b66130b691a10168ef34154e8558c4bf98b9a0b6cda58cf3c4d8e1411b441d13d40589bb236b40ba714116e50d33d2719d3637ddad84c8b2c061f3ebb40c8c549a06ed9f0702a72e2b6c41913180b29b1631109fd080851b54872e157ec01224ebf765a5bcd37aca0cff4dd50b7f9afe164b3679a7130dc61d0e4c35a33a46b7604c939679d58b664da91cce0f8d7185b8d30d0812a8343b7751db2b78bca6108a6ebe9b6929bb580d969d492848eb92327a1b953ca9e7f25899830bf9d5f0921205ebbcf0a4d9ef83d0b1d9c647d7977ce94fe976357f9b21c811971d2ad1b916815321841ff58e110f42fd51262c27b98a8aaf6906172543e61fe423e118869054190ed5efda12b7996c69bd735850150dbbe9fd88ded80e55eac5cc3bb6a1afeaf86e3aee8153dc551525c7451b2751e5ab9c3d85d8ee01f93caa6e33cb63146719b3a77cf216e1d1782012eecb8595c5cf8930eec710d163e760c4d4909cfd1b13c9707f813c73435ebb0b4565514351f48eda35e603af62ecd45df959a2454bbd5152b6de39592426db67e04ee0a4d238ddb077040257dab12748fe5177ca428117cd0a3f2d921a817c12c352b42a79a3e18ac7c200aaab9751fb72f83dbdf49db92a910fb829d91fa085f2f2b812a04674eda97632b36404d1c227f62f0abc10f8e4c885d39f000051488eea7188610c0b44a51e86002f3a7679abc0fd1538cde2c7b3bc3c92fac772bf1f9d99e1f45c77bd1c450b4eeed7b83f2ef8b307180a8fc45905b2fb83b59f1a28f2ced05ad6365a5097f7fa2bc82b109639f21567e17d8e39899b9fcf26775f07d52481d46b12d2f519729dd0ee0076b191f0b044e5d3ea6b29324cba9cc6127cece6eaa50675bb767785bf88952a1a75c8c6123872ffea352203efc93a3960f733b589e8854333c0acd993ae2e3c93b72bba0e8d3976c7d716d5cd93f90ca54ecfa095020061ff600af0d3841301502f0033f876d9482f80120721a4d38c3ff4359c7306738851e83a9ce37c1f3fd1bfcfc49b420d38169fb617c6bd5be7c4d593deb014f734ee20929c7ab4a8221cffed113a41a6ef870ea84f621d031f4caa287f35b572105e9884106ecc8c8af4a873442c8339ebf39732a5f536e796d183afcc65a3d10d6700a088b23396b8b92b53129ec0841f9ea69cb13789d21cfef98a8069df3f65cc59a671a3c17d3177380c775f8da6a5576532af04b7d5c89fdf5a4405f1ba525069a86bbd6d6d0d50fe077726dfc4045a5098693950e899e5805a6815a2f5b025c1560a127e6b8033540c5e027b18ea225d113b161e8e09bbdc15b118f48acab48c9f4446c743af0e66feec899ea37e28801ab6b8c9e408ec488db4f767bc1d3acd5a44580b8c56e6082053d03dcc7139699c899aec9dcfa069749695ed20e03f2bf447539556039363029559f4fc50d33a85554b8721a14648f04395389bf2b9a0322f5a2a2b13ffdd0e0c74064c207cb78832e8814c0f5f4ce188f3af689734591a12ab82a156ebd8006d21b504128f3c728b332890c2082073cbe9153bb82460f08bcb9818dd462bf5cda3f6dee01c4be6fc7ed3ec84e8fc228b5fdda3de1a6587af42a5da2f325b2e00d7417d5cbbcbb815209bb65672718f1ded6533e76ad53205005901011627b612b025d21cd526dc7b8332f2ae1a06b1a992fb9eb1ac977cb968ccc3d91d90acf995f34e83372a751cf46a6aa3426ddb637627116cdfe8bfb2f19918dfe6a423536ce4b27bdeef9ce6a9b5e5c7c4f4a6ad9617f701dd911f92841c74d25fb20d6013f56cf28665bdfdc859defef172667ebccf0bac099081ed5b408f1b0da070591a8433693935dab7e0206e42c6ce02ecd6e65ac5b8897aeb3e76f73f1358594a09012b0dd19ef22bd41f8675933cf4835ea5bae942facdbf2c061e61c9b9f6420e6f9af2514a30ed9b5cbb43f6dcd813bc9ec7c0336ab3a76a330c05e560c4d2984c216d779940480a0412be82955504802fb02d9ec6d3428add1fe533c219f2471ba88213c0e122bd645fd4e9b9ae0995d68cf42d4e43079fa74f364eba71e88f053469c4cda7eb017c478226aa5f91ea89406fb5ab109f92817e3905b020243556df1f8c5494231391f4d160edaaa1e2dd3780a8cd75a65bea95edc71536cb6692c61c1506fe99ee99678230d82aabcb9350fac6f20b5b05bf994ea7706aeddb48cb869367a13aa390ee4bb8f8185e43f2dd4252202c77d15ad38e05683cf597ad0914519d895c8363fb28b6449bac0c85191df6a0ed7da7116388b0eb138ebea52a6c59f95e37fb76298214478f65bc4f99cc9c1478a607931e799595e08c6e7fb424393b06409aa90a225407a1d6d7e3ab4f332da04a26be6112172ef3e33ed18d662f24a9003306bd7b4fcb0a8c3a866964bd2d2b0f8572f9e0c81b2f83849c547d8577cb7539b2b74d560fe294f87ce818d989f5db96e4a6206e3e12a2ccc3ceb24d3adfb095c7d4e418de8e6ea0c0e234ef8d98646774c6b314410231113353bb473c03539465e4e689f818e485acee9758f13e960a204269908a7197d4d88081af678a42ebeb424354c6426318a0e850f042a49788b1aed07550fbbec05f6721e3ff7169c12e1fa6652a8e31c9079b09dff8b9cfd74cab9ab8f56957fb6db283954c42e51aa730ff493baee4ddc40ac34ca160c488c92f8cc3bc2434947544b5f74dc27d43d705bafc0ab5dbabb2711befbdd7051cf6012c1d9a6feb6fa8750fbf484893292de7e3ed34eac260d79b247db4d09ee28ecc502f02d0fcfbfa32f1d83850d736596db528a2861516ac44efaf31304c147863096875c7d6e9139e23e1beed013e8c942e4f2c8938ec65e964a85918eec632765833cfbe4e18947dc03c7a529c821c7970b70a87b9d3fc5b0be66b8d1b0ff6f2381a8714c91cf68d4f3b120203aa46d2736613a48f8a2a71bf1b8ff4e60f87e12b6e98e4be4ed62db28927d8449216ebc5f8a47441188a9f3be74fcf4a37b3b121e19efd2e3736c36bbc3d4bcb079bc213d8e14f4629101878819b4d013434a1e7f2a3cb863109dc3f9cb649e504b399819075afef27975a68c926ef972bfce2f448c2fdad88fc0a8700dd995b4e62d86ef6bb80c6b1cb0dd1c2a5186b6632eeb920afb3a797cf8cac9e759147926ca6513b2a04c9418fa89f2fc2ce205c3722c336119166fa279d10ac5f59529d4725a91f4ed0b102745c55de0ca88663a8a42316a2131795a9b00006fed79125fbdb1105458f5ff6020b753f886e705bc356e5f2cf3acd33c4e8523dbcfd0a7114f450b1c19defabd038ce47187ff9724f7dea9f7905815bdfa489abe786b334d7b533eead5ee2982867d39a088dadc913a226aebf2420cbcf94a460cbb9673dc7ee4254c1b8ffd7136ed113450d0f03ce4e96ba5e5c14c525d82e73508ed07c8ad2e122130dca15cf2302d4d021bfb66ad43f99cf72cff69d69d5f32cb62ab4df271534f53270b49d57d9614808e60b0e6338512779a4282aa9cae392888d421f748960d71df8ea546b9872f7530e8403fe722d69f258a9322ff13801372ddda838e845f88970d76871c5f1e9b38c4263582b657c978732d58fff0856ccfebdaa331c195075783fe1e3ec4042b43158c599342f23e87eb60f483748f3b20b95d18732fedeb8b29ca0cb7bf1ad61e637119021439516aeaf0383d8537e621b758417299b424233f2131939bff467b37e109954e36e8e1f9a6203e9c48dc41a46311fce3d4307434cab8f5220d4e413fe3275cb10dd5fdb9bca2279ffadc36d72b6727811f78b1d5a4e2b6a4ca0f83d713c49b621f8770447b4c40eaa4019f564bedcb164bd250556f3f2db40bd60657966490a0e346231e4b970ec12f0927f436453096006eb32f5a588b3f34c89dc110900113d3216e27ae658916290aa23a74c86bd9edacc79e8ae93e2ecb403e9cd7e63932488b944c701f7b7f90236693fdfd7d62c8cb49f2a8080a8886f93455b7c9837cba29aef606f8e3f2baaeab0909372272bd52ebec7ea5bd5102d3a6f6c69e37b90249f04ace36ae69115da976a3032b41b35123a989f42e159689c96540b752376153e0d7d2f02528da091e5280122d837121a32b054e5d59bb3667a95a01b8d37f73d47aa997d31b08f51047f90ea9bfba9e8a28ce5c4c3b308e062a0b92120aebe8b443d6382f83421a2613bfd8db48fecf9060c313c3619bf7ddb4dff44db0cfeda870031e5b50707e01e21cf8bf3bfefbfcdb0218b4f96ec9e8def78ba8e5ca5a94eb8a1ea64a6c45bf42944f7bfe7c8b8e3a99f8627f23fb3f3b825835082536aeb1390d135c355ce3c4b299fe4f601697cb6efe680a38b8b7b81ba8c096a9c7ca44ecddea909b68a56d7a018e1aed574eef09ed44f4ddfec9404c19e362cbb6ec6f479148cd880eb81946f1b50a0b7e7bbd154f20895d85bfbdde8e49289c7a803fc74b7cfc650845323e2a40871d228aac00f0043eedb2496b331981153a218349572d14bac6cba0328604e0d04301734559ee9036712ba7e11a96e85b41c4b4b8c346ba4d04a1b0d02502136b5902a04803b950fa313de0d026b6a5539d0206aa3014dbbd12473bd5af89dbb74251bac3adef63baa08a39d4bd5b897aa70ac58ed11580bea2208a0d985ce82a5e0df99dfdfa3ee06054136ad2bbbbc10f1b065695ad263959c9f51d29fe019e6ff6ee021b936a42585d25ee71c0e3c9babe6126ad0bd714b46bb74691eff21abeeefd0caf6a59f995f10a9e04ee2166c0ea84c6b360eb50197abb33c843a6af6d90aa83f932fd06810b1cb35c3b2cf791e16e8bb301c348cfbf052c218c891139b92e20d4704a085085f0074f9adc99220f79d36f1e2066dcc7dfafa00f25b117584bd38028a35a3b589c28f38ceb6c430351c35d138e63609510d15d7f20945b556697a0a333f660eff6c41e9252483f6793a6b45d8dbf8d5f47ceb225552702dd99e18179381d5695854d485b11337f1e09f492f946c60d846ebbf54a5e2c80f976424acfe5bb978682f4563b260c7e242e0c9c1950b9e95b3eccfc689f9e985cf53b95b7c959838d779baecda6ea7f51b7f404d6b8332ca425dfad1ba7f4bec83198d24572127fd59953363d05617d26bd2fc37f08607db82f9800ce3db769b7437c9ed51efcdc880801b97bb98a005595d0c29e70672c761c5fa31cec122dd8408d3862994072229dd0e5cc6869052e81ae0997c462af0d5ea93017f56fa5c4689626bd075fa54fa4cbc0d4dc2d863289708624173c114a68dc6835928fa2ebbc024a9c775d0cabbcbc24e9d6be1ba3460eb1183ca5375a71129d54f8c0d89f82f508d3b5c9cd9b1623f04907d76e0deae22327b38e1f390059f542a6cd4650fc3518ebe053e7b3f6a71cb20c80dde4df45d51868ff281f91a69e463dad4aa79ef48fe475ead6481f97f5d8c3785058a781c00edd1af9807f73c72f3509962691e49426dd15c5ac91d64a9b57445c412819f882fde81968f0846defef61f4473669bc41f07154a3416d4d04eed4e3cd93353e3448e5025c8acbd6d45c80b2e01ce1e1b52f8086aa41e2cd31cb148edb8205457cb89fef6d4a50a6b5cec237fbbd8ccd9483c174feca8af6f80b22e3b3c6828e049114aeeee01b7c2c109a811259b9c95976cb43c9f1ec13d36a04e1385d2253fb8ce0983fa6dd54933dfc19070ba48b33e44afc47e904ed14bb0d666d510551faad28462455377df3ba0b68d4c132dbbe20c2d62ae1e14ba1e75c3ea2742b32a902e2d1145b25cb4a0005f66021834680da4450c50700e9019f93c023bf2d609f4a723234811bd3125c3114422c497d3247a4f2b605f6cc85dcea86de4dce8d1f4a3b4138341af070a4727857054572c99cef8700188d37f2088072e1fa4b8029c7175b50319f729208d8df47785b67aa00db3a00a7dbd331ac87ea51e1b8e50540c2a9407826fc3c51d9bfcfaf1c4001a356eed516604ba15caaf47a5770cb587ab7a446b3d475b06876ea8a12386e75cd8202df8733946b0f302a52148cac9744c9da62b8a2af351ae6927c72e69bcdf442eb65ff269f8b869525a6e79f129ac5bfd38cedadb6af9d1c94beca6d4c17c3d3304727eb344ca06131cd262ce848d364166998a091751a26b0a4d1641a16d230c9721a26c482934ba38facd4dcbf2b18de6dd20d4a764e8343cd53baa8311f9a66798670113279d567fe14f46ba1ca55b034fa3303f5d30e8e0a8282be5d99dc8ec4c50b1486d15e2601c84579a25a721a0bccb70656f7e9182378bd203f6b2fb382c82522b417abbd4c7fc53507335a26c6b1e36b24d866dacb14d2644f006b6ad8cb8c0c94a17c50020df243692fb3732a49398f9a1c848dc3feadecfd1d6985ee7a3da999650971d109087b997a12492f1e3e104077c0874d622f63b8c1b1a574c141e0ca4c1eb7a680031bf29543947792a17fff94e05d6671a640d469abdcd5873463493b882bfe3642881707ba4524ae5e32dd549cc970196704c49ef056ce12fe9a9f52cef4103d870c53f81425c697435ae01e2e7c35a9ef356c05b497c157c05fcb8fb37910440e4b5153d12309f0f00eda6d2f73c79c337f43e52b39d4e939620ebf10ec8261a69501d071dabacc368df877985fb36974eb75971d2d6086adf3c2f332c6bec24e89b40495ae83eeb0838cf2aa5ce89c7fc95cc1bb36238cb9d498975954443a5458137c12ad109c64f96f96ce088d38eedb4ec300e59470213e2fa32fa1194b76301bbb6296da7f8f791953f10a5f0a69593b5dcef1e665b030e924a8841f278caa2acf705c22129df180abfc5a6194365f6434c530fb26c3a69335cccb5cc330c76b9e3a5907119b6fa76b788ef72ec0af51afa9afbc11116da9220e1410df33a7676150ca7a720936b6fef6219e612e508536397f617b0e84cc3aaf042d92782058bccb072ca615feb692b1141618c69f9749b1c2ff04785a28ea89b79c25c648019f92b12e93f573f5d5df77daee4de6f2c4e749391024abf9c2c92a1b2b03e2416e736465bc0f675556ee89820fc6dd945f22b89691967db79895d76456309afa4d56e7e559e106acdbcfa66070e39c6805989a6f9956fc0e4435e95b2c540e183255a45afebd595c34bb9a5a99a337842541970b6cbfe5a05b0e40106bad646c476c4594d8cdb495e6468c5b516f79b25b5ce00aa349f38d65c5ff8adcafa29cd3732840e04031237a685911715bc4d4e93d1193cfa23076a3a7b0132c4492c63f56e11c8eb6626784cd9dc81114d1beb902d6863bcc6f3628a25b1545e88eb8c5a8f137d81f19e15ca38c55c8bddaa5c3c2c5fdaa3f7d7be5176711ee61db2e1941fdf84258459bd2f1f9b7c3e1abf0a295bd78b0fd5e7010d51c15e1feb06d921b295edf1455e9c94c664ac8eb8b9f348df4de51bf46c157e16a05fc5acb35e3ce1120f286087db21777ecac39419ad264fa50dfb666c8d0993e4a7e7d24104aea75c66b43783b0855a8f1443ab08da9b36130e299307736e429400a085f805734e448e4a80b8b73202102ca6568177f887b653b317992319ee090e914557c2383b530aee5a1bd6c75f0d6c6b4465a247c880cb626a037da905c75bf7a0a266258c8a75a26075210fce8407635193459c561ce5ac401fc608b65f15c306065218866295e5135818406adc84e78ea59f163ed25a1e566a2482348414ccf88b962f0c521b21dedfcf65d210a6da3ef6fddbee3e67957df08539dd19b13dc3f7837f97ff507c6943ffb051fa6883e437218bbb9d81f4e4e37cefdd4673b7be29e458ece7038dcbb22ec0f98f758bd07e5582f30fb9d8e01b6485e2465c5c85f92f71c586c42f71d25ef1113a2def41aec95bc843728bad23291dfb9e4b9193c214133ecd269b762f21cef28281bc86cefb189a6012994e8793bab503479a349d0044da7f0953b72d20ca397463988a6bac9eb0a9331e65ca872317db9e308fa558322d46a1463808a657133939b54e8f3ed3c290e5fbbca0ad7f58b4294b7352b4419b27ba2269d707a24da8707ace94608aa961fed202d3f2e9ef4b965995a057ca8f59e4be345c995e2d11de7583b7b76fd805eb20a540ec087cb42d0a5a18b4d90e8e2eb7490bcb230d3084173ce5a53985810ac7e023ba5392edba452fa89d602b14569e206f32f0bc017c8de6524d7b306cfb87cffaa10bdc2ad63a45df6f1c2930c604fc06c59d410ed5256d17c97996f201b6ef6651f4f999bc60632fad1e9db0db11f0d6c3b204f31d61cc843f48520c5f28b1cf0273089cb55e18490004c81a741687567bf450ba780a8535ce26f3ee56da039e58f9a5bee20f40c439e842ddbdc5fd67bf54f0bbbb75a70dfd3ea58a5c95ff7bb87fcae14fd2f31b2cf5b424870a20317666cf57632c657318a71423c91f727a635de52151cf7772f12fe9d62dc2d2f88ef6ef98e6bc9a41080421f832967f189e07b0781924208f2ba3e194c7525b1f861f9b0fb0672a6cc1e7a6d252efff31574bcef1ae08bc6374da29090ab7607e3cf8c17e1aec9b68b4a259e2fbe128560805003a6db79291dba057dd22a80c3313cfb196e83c05da7937906847211bba8e54e741acb88332e7654829c2672a158a72f2e13f1475b87f8658020ce40a31410f3bb665ea4e8d4847d27a6da53ef8ef98dcdd30fa4269b72c438e5fc6587d7603e356792a856366fc0b20e6375c46cdf3d38a6c4980f0b30245e9075448f2864a61f40ea9ae240bee4818a60ec37e21a23f4b11930607ec9b085393f312e2be860ff1fab67f201e8714a00ece9e89d4de27784dd7169c720a21e178bb6ff5738344c35c032c89b2c1c728f033837675bb994d91df1e1b4574847ca57c165e1b4e459182c2f72f6c9983865bb4228993b8652d48555abc1b368e6e80239e7f55d0d86a376c815127337f347ecb7bfecea3f0d68bf4092df5b78577c8207d7542d667c9a09931b1d86a83e3ecc1ff62c30dee643b46c8500687bd2cad45a21839f63c38df0d946f2f9c1971c06cb172f7106907805125566894b44557823ae9b011bdbab19fee5e3c6e11ce737a27da640b6028e3f8232bafb3654714b945813d4916f4d755d3e25e03bc8d9f36d01f8de8e01cab9045e632e686687281512865faad6351c10a557cc6eabcfd02cc9ba4467e3391a05c0920d94be484aa04685b4dd5d59544844b2e2ca0ea1ad5b4d2a0c8fc11f1df93661f615f444622141cd9b2170467a5cbe912a3e75bb8fab0a20064b45f7756a6f034234187563b6bb2d577b29db808ad40a9502ec1bac79c593b649792ed2b940e89810b791233839e0ff1a5b7ab21c56f8e4048e5304cde8470e1eeb0308aefc213f8c4d0ec43612b8ca3d0949d51f57cd58df209b85049961637c3b18bc6d0471e3431908bdae7e298e4047fcc8b963bb0df8ba5b78697236655fcc4d1258c205bb676570e94f0ed0c4c6973df7ff7d40c452026ae9009d6aed236da9cf10c0da9e37ba9f67cf705b7d3c7134d9e65ffee261a69212d702e6ebfc32579134bb10ad63b5705bba50873f4a4bb519d253f1e849d3f8e74bf96f25ace1bac47aa0afccc85fddc879502c1396e30f5306168e85bd5d16b094fff41e338685b2b46df447c2d2baba22bcd3bd93f1a758283cd5c5b6948ac2e499baa56814a406c3daa5430a864133e14d7781fda4bf2bc5ab03ed965a54ed2481943fc52a0346e5043cc170a53bdfeea92ac95940badc4dcc5c5bd906cd9f4e46a4919eed01d2151c50072a02d51bb787fe933ca5cdbeff6c647db5a0f6f470b35a2e6fe258778c450c92c5bb303af1ecfefb80bffe894dba2287804adece5706f3bd8da6a67de8992dd404683ffe61715b70e696bc2e0be19c2c06af88569932fe70ec687e382009503882b5c68f8dc3ab3fda7619e69aa37daae006066adc7e763cc5f0498461df1eaf1da5fa14a4c2a5e42cff5fdfc3beef50e3828f7d62710e2811b2b583c122451ab92c07ab2f341eb5494b68451afc823b213e4c9462e97aa7cf2f10a75af5de44b3ba2bccc18803d675e2570be0ebce1292aa800a89839cf67582267998ea57f73e09b70228bf8f3cc9a2db9ad1f520c4734f6b6e901e259e4fce4cc64c0f321b5dd86e4c28a1709f0306583812cf0c07fc137d7515e95a95f32051771641a8f8b8e946335b992668dc5fd02a7a06d9cfc44f9696e702a7d014646fb7a7b3f1eb35e49339bea7443902a36f066690bc8c352c949c3c54f21f1730cbe2e904228cc7d66d067ae7be4cf1d928f00eb9cc1e6d4d334bdae4345c25735491b20a6b8694491038b1d9815ebb90e00851115b987fc4823d2761281ceaac42b15232a513cecd56c450b05c7ec0c746bcca005eeb4d05b164a3e3c639188657058ba535445fa4e84d9c759f73764d2e47ead9860f6c91c2e2bc741ad61b0b366d22c547b1a6ce230dc6e4754af9009de310c611084baacd0e66989fca25e9a9b2714895d19209c0488300dd6d2e21a79437523e1cfcd5dd421df05e95fae3953fe3222fa11ee3a38320909fe44b072dd525161a8488e6ce59819c65361899b058fd11e0038eba548aa1206e0d10ed77aca49c3facad615bc108871ae13dae2e45d8a31eb47ba8bafc3748f2dab22c9965d656d9cb36e56cf40821d9e23923e89d968a48ea17354e21085e3de4a8460c8828c2a5632a20049f4272da22e636ac4abf5975f5eafb4b344b4f59ea9070b3cdd01e4f42857306d8929f046b3654064554c5dbe928d2e4a63e6dcc0d7d3ccb267df36c8553163fa3981852f6f5804bc22c581a97ddde395f864f386f91ec8608bd82ba979d5bc2106c7d8be35bf06ccce21bf1bb31b5df0fc5c462f896f0d453b184a2ed86794ee4ae338b9f0a3a572310066a40210204a96643255b03b1a29fd0904224ed2140bdabed7456de88b95fdbd4a06da65875a11bff4a068474d2ed6f304efb23645b98e71ee16260fc0e7b570c3b0d6e39148d812da645055751fbbecc57d82f5fe6e5b7284692c9318cadab108495a01f0f07faba2dc9f64b713b51a1b2512c92951bd9af78f4808b152bb9e89a826c5e056c43e4e03bc684028d763c6c60d2598ffb35371b08384cc9615245616febe8d1f0e7011d260352dafe16b5b837a02376afdf411f207023f7d19a23c02b8717d0c1ad2ee1242d165244bb181631e4e0551f0837e7f4c860a89d2fc0fd5f41a0b715a0ad34830ba2e51014420718215ae3d9df6156b441ddcfec5030d14b192dbb6facb02558a63846b692ee5e91c171c25e927fd644aff5a3725355fd0d2381251c864612f19b56b73e5d08f306537f0d3b652c717dc001848b9da20f38c223ecde262d8274f077c2f61ce03fb9a9df1c00c0ece437e028b9b77836e3a782334ebb23ee9413d23c1c938f42c542db34e018ad5eeb510ca7729013f81d0ba305f88ce26e6f007bd07a5f21efc484b32a8ba76b98e40264e0bb6c3c314ca457d4e137c61bedeeb5aea062051c191eef31027c85aaf5fbf7a268b67412ae70bed65e1c918e6a9a03b8ebdcc2c157600465831605cd608d8be9f6249407480de003294a070dcb7a9f43a7e9230d46552025b0997d3f88eab21ad070dcc65998e540cce0719eff415f6264d8440bc66797579dd9bb7ef96dfa446160ee4f69f4046aa859bfbbe9c27a37fbcce37caa3f2b8e825885c5cff8effae9dbf4d12a2b1c19652da3181b821d1efa3c75f05491d7b82ce038e04505f0f8f33fa58c86d269420753b843ff6ee59c259cbea10dd0124b7594473e0edd4e86c79f8723c4558dc9d9382a02c781cfd4dff31d8e52569545d6440128d47cda336ec29c4a3d76796ba20d5df761c9443ad2a0d1b10dee0f1d7a1f9bdc67e1e20816461bd8558d7b80d88d7d367fab10cfbca25ce2855e27b380dd0f297a29e2142c1d0e97bd0d8c34cce68dad20c79dd5b1a9cc5b28c8ecc708bbd9def7e0f4a1c636229a106e8d66a6e1d2e210972f7c63c16d0348090b02f191f771ee755c916f7c77c8f0777e48dc78f552671e45eea24ae762fa3cff8cf9f06700f954a1912e03bb557039502f00b4bf432ae8ec78b78a2514acb5935805f0a29b12c1422d03cb20f9a26047ed12cb95391c14645c7bbc6aec54842eb6e6071403e316c1d308587e6c86121625c7544285df1406c2f29e631543e4073c41c68aaf74cc0d395c0fd21db9d2a3b1861c77be3227d333f10cf6a7bf62814549c7015eb5818bf04b8bd8903e7e5772428e50a4294c92eca612728c1ee64a0ddeec6b54b2d2724a0c7f6ee2003277aa7c841c6686347aa42a53c16cf1d416e2aa55506361469e3bb0903043727f8109fefa31beedd767e0192ee412606ebd1996fac58d10460ef70bc48a26ea89c72b0460865e04dcdef4ab763bdd7070b182464e98f19cc58b92958675e0d2fb6324ee26aebcc6847866465870976c249116f53c98d1f8ef19c12e3499ca08f7f873789932256e1096ad9ae94f75f266a9bafec1a3a41b4a0a949978289befd67f25683a3ed65935fed7b823ad88272abdd793f5a592d0e5caad0e8603223edf4e1c7284145d742a920107d8893835b10021df40025b8afccc79ce3f210b274145ea3c279ee3b2c418fbd16a3b3c47b330bf1b65cbbea95d2a3c47fd0aadb3bcb2a7a06556c70e0a6c1cc0e515e56ba0a9fdef1cd87fb492bc0c8010d8d144d497cc1c1bed103a110d3bb273be956a22266e98f88d90739637e0c4a1866a0024f5da04c6162ed1fc137e72de860405e81ef99d708a6b3edaf1281dba010647953e8a0b9d6b78e94514c2f2f94b07ce782e4a85c9604aef884684f77d9f5e1e8fef8fb017962e90d25096f41a222dd1a3b14c496debe099b437b05f1e0eee060eb78f1211d04aa51c611b81af462959dc36aef1d5de0481e9ed424d0409491da0e052f8cb7ce9591c47c22ad69523e602ced89bfa48d96539fd45581438dd115d07aa42bdc05e2d0fe03e5dda21ab37cde094755db6f5852f452c3e054d7c038823c3a68f10c09aff05d4ef6b145407a03247ca1562c64b42928a25f91c7406249105307962b5c49ec181f4da456bd61af165aa5d10e5944933a9768088302ed22ab9d1ec174d96c9c3d7a743560dc6acb167b160ab3cf82b17ef72613a8823c10f966145c71d72845006e0ca50542f4315203d97e93812cbecb4fd89e38889101abe8cd21619b256bda7ec84b81c2ae784e9bf151357c30e85074e2ccb003a2d5b99368e3ce4195fe470bfd10164fffd85830dae84244aa3b50e5bb2b307261a74b0d159708a250947fcb5ebeed1cf3d7f2deec37bc1e64480b8b79743885e9e38faea15aa061cf1a1ebcb907d2bf51f913051b67bab6d2007181ca2e09ffde0adfab4e56e6fa5b916326459ca963f5f793fc3d220dd3ce1cfda6a984ee8719792c9486d21421ddd260b7cdf3c5b031cc2090c595c18c84441b75263710a84fd42a05b0b56449ffa1bf7381427f69a75a1f2cea5ac1129464778421f98cc9240d1425edeea2d7d0d013dfaef6e9a6fd98868dcbadbdf450b9aedab8f916b66d57d3b41ad8de75b23ff938ed84d0eaa665787b800450603a8b5a6e59c4d8ccf3c0657dee1612a584f702c80861f67a57a67ffb7cab12a1520ec2d41b4b74eada2ac881a235fc2a5ea2b1f9422d945cf459a5a3cb8d119df56c5d02910010a0bfa13ab9e4bae9a75253bb7af30c22b96497908957746104982edd0137ae413910e39788cd839910e31427a4116e123ddc7c8166d10e3975a32dc8fb6cc29b2332aaaa889c3b7f14271b0fbcb1c04dd53023b717e97e494f2a9e5f08e096edf993347f2ff0f9152ce6c43a4d459554ec72761b38f7a7372dd2d80d2d8a9d73bb3c69f217bd6c61d949b6b35bffb9dc58b75068b0707dc6f37ee7207ac491bf057cea94ead82f7c167aec719463e2e18f978f5a892c60e91e951da2d9eaa7900a7b13501d1cf27b2d1c466ea967ab04a2f257d16ac43ea130f2c560fd54cd084370d7e88fcf204d66a20c7424ad5f2ba007ceb6826c21235adb51f85c4faefc0173151689aff883f9fc31ebd2fc5efeaf5db4267ec83ab80804cce1933a13013228f6e217e8add79db589b570001998ce85673a1998780be7ae8642db5330d4db58999ab142a6bc91200b09f6533bbc9b41bd3a230a5e1821809eb77613847347d7fe52446c639a37c5ca44d5cd8f18bcc24db7249dda7d488655f41cb688fb541e4599ed1878f93dbe858c9e017594eabedab8989e85c30b106e40cff524b1f43c6373c261e5587f7cc7370a8aa8245e19b173bb8be31c12d0a65adf5517c968484e347ab10e0eb51a0ac5e9a2e082d37f874daaf40fedd4833ca334cea0eb1868763eadd0eae8aa2c4a34b0ae6e871252291585d7e2ef11d3e0aef58447b2bf3117981db2292e1df34a159e631575f4942585d8c0df4bae8f90de0725e07dfbee3ce33b7ea5a81d93d64afc5e9276a846bda1ee44e176871aea1fc3ccdfdf9a0902f469330c3fda8bc3e1835464127c5f6c3ea597b8a9d4a86cee83a3f6741117893a3a9742e9b54b72f7f017979134640735672ad5cf68e05ab3935fcd7f4a68478531d53684857e80c2738a5e9a91cdc725252a30f04b03bedc69eae2da6887dfd38eab537ce2f01b1ed68a28ec61365df5de976970983e55c4a9bbaf0a06f371f691a8f0e76c7ba4f8ac82889cd262a7baf6a753598b15999466957799af6b86d6c6976662c3648623aa3ea4061baf5ae3e6b3022367405f8a773c5789f3b38e459bec2b8d4c78dfa11d9dbb642c824905e3f88817adc48c4468b18a67671550bf9adb1c4cfc55c282c12ed9b09e4af2083e81e91a4959eb746c5019b087690eb7bd6b05ee9cb3c928da52121245de083cc8fe66823ed3d89d9fa11d7923d7fa6ea4a182a79367bf2fe279370d7453c2a0d24663d5f8eba74afa994656e50909cba6a11bde747773d4541043fa0c7c159702d5d65c16b1fd0eb5b73fa717676958971b102610d779d4ecea07eadd23cb8f11e00ae78bfb323c565cdf14640931e30ee67c4368c1ab7eaef3a3865d8c303066d9882b3241554fffd70a3553023b39638bdd881fc55cca09ea55736c518d4dfd519bc9f018d290348835f06eea85984fba211a6a4b11134f4117d0e848f44afbc675e6af1cdda3dac026adb973a49d379380744767f1d4369001e5d68e39c962b4d4d67025c5eaca270d7879f0b4245f5029f28523e0dc628c7f03da68ba20fab0468a1d2c386206cf8b9724bfffffa11274e9d5daf0589c42ae187f46861241e43b83f524a051bd94c585cff5716ab637291e910c5a28735643a52b56b6cb9431bd104a360f164aa9f4434f54b444df935a7fe3af1e13b22e42d801b48167a3ca76c205901eb86927d51ca1212e0a648b074c9682fbf74a9d169d565071d66423aead2a339176875f98c5bd85abb4b9773ced3419ab11ec3e2cfb40b97f6e35b76a29bcdda27099a8df880b1150b0e361e82a2669379033fbff953bfd6963243308b788dd010a0b132b54c93e424d1dad11a3645a86fe3ff1beb5c197a23416bf173ad14ed848712f78782ea43e63ed89ee05e6b110497c6415bba43afda65d8ebcdb309af509ede742ec0d3330a068eaf2023192f02dc3f44b7d3e290156d3910481f112a3f9b6ca4017881633d34d6a13ca35d812f1700bfcccf2f2e21a6fc31d2e88c26a6d9b9bf44207197fca3c34fae97f1a662d0d7f0c4efd298c330ce2c77c248904178df04694c3a930b91a8367fab83f27f9028a87772417b4c6259c4a09534793b12cb1f71fd9c35b9c606e1e6971c997fe6515a7d37ac75c3847fc8f88d8f50ac6ce228283264c9273455f420f201ddb6e408f2d475f710e7cddfa84088430e0c83ba3ad4bbaf3db17a0b02c4f6f91dcdc9f67a4f40cb7e64ba646dc6de0297bcd01b8512301046fe2258d882618ff39c8ef3a73f5cb8ffb002322d528b7c48a563391c5a91fbfa9aeb881f953673cab3eb69bd66496fc194ac6963d5e40715c07813aeba28080aae1989076424e028984d07eb70ba2bcd1b3ac71a07176ec03f2097c4f11833af49e87ac39419ab4db4aa1bd74095901b3bf22f20f914bef8230769eb0c2be2870b0a0a4625dfc7623e246aa5a821586eb8d390a2c2a99f7147456b3729a468ef435e6a405a948e41889022f9ad7dd1a0f498183d825d3348d9150f9a0ee3eee5831909522cef64c26eba4db4cef52e2290b03b65a6f0d685732d482aea397e5a58121fbebfea84ae7551f932b9d95f0cd18f28b8714047a20870de5dafef6aefe265d31bc7119456aa1cf76bdfd6b2da47da0847554ceecc3030a6c33b5997528e4fe7c9036780cc754cdb3b52e5a86fa9135d61f0940ee36db9c9d665e0893ddcfe71d03d3f0b7d1109ef9ddeee35c7cd58f83fc1f882acc38a99aa90a8e163c779f1cb938eb8eff2a7075df85825d28d768f66eb87cf6937a86a1babb5ea021d269775edc4fd3a5f8fd928d2decf81de734bff9ce5e5ed2a7dace1b698e2038a9b182ab947a7f147032bcf6fbb4f378be307a235167d9d46054a944c4f550a13b689b2ea3c60aaad88559f682c336fd11c70764609624da5abea892c1954a07d3a4c5121f2c8e95131eda5726af65e83410f544dc550ad2b2d70226c56b1e31af684533ce63267a067b03a5fa9a883d524ede9f098c2c5fea28ced50b81349c8b1b775cb7baea4f2880f6a2597ec912b3b9a8956ee96713866a23183f33ec0373049d4afbb78ead371e14f13640f285c837dd68b3c59b370031ac462dacf62c8c697ce1b426ddd2e7189018bfd68f8fc2ca91817906249abc98e745a41638e10ca4b0336767465c56932b4f6e401c80f3bb90f3c87b6b6cf0be098c19591eb09ae2dd5a27080b4b8471d4f7d7414767d5f87de5285061c8603990f85cdce0f51b08c1ed412eddc06a5280d039bae723e730a5d2a305be9661224d3073d7fd081cee19a4299979cb8b0e0e628788fd865d1141438349929dff5f1012a2384907b32180e20e5c4d4d08dbf4b53c7db27f7823b92f66fae137c38093ef536db7caf7a693bfa85e70cd0eca1ed54583e92c7dfeabeb24cb86a8a9b15e755897bbf4f4ab2a5ea728c759d7bc981f32ea74c343583f9f033190758343afd1d42cb03a29728ebf210faa338463232298361d9fa35c7d13357de3de897f241376a9fd90efca06201298930f577b1c2197db9444e83b9ade5f202183de8a9bbdfa75e5b8777a682e2b3d4cb1f141aadc6562fdc1da148567a5615be6950b19cf8cefa69b5f53a3cf5b38e5c83b537a81211919366f4cac28d94412d1fcd81592d72196ff8e460adc6a2681a46f9e208b256c7fad159ac9943e74fd2b27a6a46c8ac6d316cb82079b8f9a4ca5ab4151da9539b332d423bb25d8e08eff1b37250f0f498adbd572619b78299a253b088f3e5a88b77050062c10ad1decae70ed0285e09f4308a01ad4aced9f61a53d17be70f792b6ad475d535487a5be8284ff5c39951687d0411ab96bfb8e073c875ea45d904673894b7602de62109dc33b7960471cf6da48e100613b515011222f8e598d01c21d2d7191dee29a7011627d378cdab2a94e7fdb4e71324f498cb0bbf3f0370949f0ed09a465a62f4f64f767d88543cca617adc8a4e874c586afbe9348b5951e0a8cf84bda5d5e920dedd6658b2f2fb3a2d776cd070e740f58dc94e8b196853ec21edd113d8b19d966ae0b49bb0a1a95a6f2067773a40dd3656f183f31681567326804ac809ad797c57c8d9bcef327aa297c9d72a308a7ff608a1dc35e6c2938df38d8d3db4b5fb16ea773a601b86c8eb425a87a0342ae111defa84a0a0fde144355eecfa7323b7e71cc353004eae3f48ff01bd90419c04c99e2217a1803d1933a6a71114d451aea169a08b8fd2c5f462a2a8512e5513b28f348381fa1f0fa731e6227221d24853d7d0431af2550f228902cac41237f5195bf50ec6a063d6bc5c8476b302a042d74b812d4b316d95f582dba8697b009e4f12ec231f35948856d609cd21c2923e7c6c41b8eccda152f5936119cc956669746a714fbe1d0261b73cb6ec27470d19e62993f3cb90479430d41b6c48da15a4976981ea4c6ac9a4ee990e74457f837f182140d127be12bab170f4e48204df8c36798acadb4b0dbc841a3f1c0923ab831479da02b9ba70bc6e83b45a785e6e4352858fb54c462d5c2f5a27a11a466fed93822a2af09bb7e0246b6fff73179354f231b039a224853d22cc520189364c1e2b67b149149fb94d28b66b5e6e04513271532bc9be8ce92a6446b89dfcc71c9ed328d188af2a531e18efd9aa28e8c92001639d53792163c96a0a125ca822a7d563100a302cdbf6551b1070aff09b91dcadf57c02f1f50ce88517412d4e06c4b4470d1cda4ad539ca7c0cefb7fb178df9056464285b2cf75bbf3894068fca9d17e2cbbb47941efad78832a31fa6476afddbcfd5dce1587d60b1297e49068304baebbc60fe8a8dc6f33af8c6ee5e764b3a13e3ce71715c1a23452e82346ce0219263fd1f43103f8b34cfe29debc2bdcd31330cdafdebb852a31e4c0d0019e6e05e36ef255ca65eeef9f0878090019727db1e9570304c109870d9b18e92c79ac16d1defb87edd95789167ae10522d2bdce3accb115ded55c96a0be802ead0492a7bf8346a4fa116e56725a1b9d216f8ed3db0e6fac942894b6cf13bbc493e157def8770f197e3eda5de62afbaac6b9c605fa188aed34240f6c3b9fee357c384aa0d00ac4ed4ba3865f608eaf283d5ddfe500e28f04085f748d669a64abd8f0ba0b2295f17d7f7e0d2ad8a894e45322b636f283d8c1f0c7f21be6905e5124ca5b5b91abaff7a485e533eef37c47eaddbbfad1c1de7a2fdfcb3bd47d538f6cd9707999c9a129be54a9ca062453f403ce1893671408ac4747ed5767f7c2ad3c476482732d927e65a8f959d2a3a8f7dd26111ac212af1f7e0d7b3165504ef6320310e6f822d10d72252d5676e9f75a439c8017c4a78b394d85f1ddbf15fcf8cc7433162806c2ee48812123f090a8d1d831db465c69e1a4cfd15417c65df6668004f0e892d685406d9187be9ccad45ab218c5c5320f0153a36a25e463d55ce82055e2ca70208d40643a0475621c1215388b4f2d29d88a93d7a6a1128e0c98223a8161e1f4a936ce9fdf13131694d33fb0b86f7ecd224e0ab9c7fb8b450102bb0d8dec9f9317c4afd1607cb4fa4162e72ccd868303a6a6778548d2217a4a43dca935a45d310d05b863cdd475a148821b568a76bf2861b7d9cf92b6a6f1d23ac125993dccbbe119c7fc821ad84cbf9bd57386cf7ae49ff97e6a741bcb5564409f8a89540d5451068e3e2c93997dab8c150b78e80b3e5f48ba4810a02c6d661342a70b1e53dd56caba6523b515e2c3193a9f72d2843d2cc26226385a6cfdd65f0768bbf0dd1bfc7db16b56a83761979bb852dd6a1b14cde6d51426b682e23ef5b30ab84991741702b80d579dadb648d53d72b328f4d228ed19649da107be1ad17d3fdf181af095f7d41f07005ed01423efe0379c088d493824680e09f1364f66dd08e89706f6cf1460dae0bd863025de417a48872d16786e8014f0516c6c9ef0093e101e60d1aee21bb16de53ba5ebfcfca9e450df4cf1657ed50d9262a2e875e0e8138ae0b6a969febfe487810d6a758ede6b4ddf262828f57908e230795e7055157b34d408ef1af9b1881450005b7f12799bc487cc0408201e408e57516425d3fe057a2d650190e9e964e7b03d31c8c706904ed297a79c08c46659165305b9e1f3effc9fa171fb0a7313652630c06cb9a775166c067ccc79370b627452bd4f669b90786eee4ebc0c1072b87acb20a14bab70d2a6dfa3ddcfa2e9dc9f016c746133b06e8f7a04c64d807961490e51f4de8a1aa228a31196f27f4ad3392495fc18538d00021c412749fd724d61a6f1c63332ea8651ce8e085bed6850dedff8643bea03864d376565856b6b815ceffee3e1e90ffaa81aad988922e8b5b5fcef4ec39b4148592126cae194b66c27edd421946a614ac9482a08eddf2399ac610430c902192002741cc33f5491770fef460aa971815d5e4ea706213aec1591ca89e9f694729ed600d300fccba63dee126b69543ae57f9962e3645502cb6808ebf36d495001a4c7824b8db67115aa3647c4ffa2a57753f443a0d604437bca7def36849c85b5a1b2deac569dd83a3b0e2c66e32fcfd0453f19ddfef417b1a298fe82293dbf8f1923e6007e9a02f113cd11cc7a2218493dbe509c8c7a715a16f012822bb82429f3061b98d57a07e3268f413ea21e5d99b4fe812fed56d89b041d92e420efec1bbef51cdc1321a1de1b4ce29aa1ed64822d52320e0a4318079ddf74230f929db67e39b29287131ef256dc4d600ddf9e6bb5907ed4bac1478374bef38907970b9a8a57f85daa3a7c9e6d330613405a5bf87608ef744c8a7c0ca83035607c6fd1c50df41d35c1d3c40ab01bb14c3012065e9feb86b8c74873fecaffbd7092f85f5e5c57e1cde2b72a60dede0227da67f62ab9d0045b3a3d01281e6f09856066a59a3353d371ce52a45a1a4890244cde40096d26f933d38004c5df5b7e25fa11b8dddc4a30187b18ea2a6ca84d34cf61ff5ec60668111581534c633f08b8f37fa16faf56428eb6259556fb582b456c5be7506ce5a3d19ec43f6cc5697e419304da8bdfa372daf8fcc0089f421d5ffc0681c1fc37718de9081ff5f3204b34bc3e84aad09f7fa759bd9c0d29037c99560b9a307710a8a29b251a53e62a417157c9c71c0c1d468cca51d03b9f51c5763bcb05e2e52f19372814a33f43da085d85cd7ea4abde2357e5d5a614e32484a1d497353bd2b8dfcb3653a9633d87648660bd10409d33eb27b6e51adb2b8d459805dc74daff73431062f4b056efcde442bfc159ba713e5e6ec848112bd8453d1f57b6422e7844dfb9eb9ee4032317795eb0e9251a13dabc55c80d370c48bea8088d31f68136bc7e1ddbc21f5e2dccea4898267ecae494da99762c3deb384824294b4f8d9ba7bdf17f5d619bd3add91879e60aececdd2da236491cd84e55ddea3b01d3ecce9e06ca793c3383cf163bd533fb890f8abed9a51d3bad1cc9699fd23487bb84caf4c888510e5f1232d790568839a976cce9695d29e86480e60e9f98ca2e7eac26855a179978fbb5cf9d35c09d89f5a6597c1535b801f1dd83667784a1a5b271743419a3ea8fe7a89bb6e1d8693ca1750d3474c77f1756d3baa819560204783b10c951b0a0d2ad6db23bb5809897879bd2e1798d806971d3d1c61a94effb7736ad7bf1fe1168d32a08216d1be6d9e184b51079b1a5ac70c68dc42104869bdc0e416137da1e88eb8055893955aab9fa0d364b855ab6352d6a2b56a54369c303100ebd30c5e0a028abeed97dc501498b34e5b24ce0249995a32c9dacda26a2d225d06bf62a10cf67763ce3767213e59fe39ddc168b22bb046ccf2de7aa668868aa81fd484c546097e12a4c4c70907dbec9599b08e1d9062a53898883aeedd7a98b8f248f055f9ffe45a43de8d0d236c89c19a054211121c260e47b32bbf085a5fe7afc5fef6146f8e71d22f376993604824baf867ddbe2c1dd491fa5f41b49736227031f9bad0caf6f15c3613a0e938eb4a15b9469ecc02c549c2b7908a95959af747a6d725bfbc605788184358c4243cd16245205586359970e9175e5179c3d077d52a7b8581729e0f7d17b7cf193591ae27ef3bbb1517fde9df1ce5543431123a7a45fdf2273b6ab44ce076ffd4ced7f8661a886d45f7afa32be6401b1e9c0e68d8d5174191126cc89d415fb7ccb6a8764e7ad21b52e09511afdbfd064750a86de6f16899dbe80a630d2c2b21749204726c68dd1d1a264a8ff9887f42a0cbf6e798b3f831b2520bafab3e514452fc99a12883addf3f06a30f574a8204016da12fd00fb524ec79ab247f9ddd857fd41bb62a52e15a85a552303f237c065af18d9299de9d34f8ccef424e667a86cb5d26e54068ce60057935041d37ef90279d8e77db9df238923d465ec9a451d4ad49c94d34b5d1af52132cd2879c86b6e0e09d88fdd4c79b983a7e3f1dc0c1bde04551a3a0431aa401ef23dc6923902f87880dad4aa23dc399b735c5a2863f92d8e69d9b4a883b27931221111cb0f8e28f1c9cc08baa1042d406010aa894399a829327f94bb9d98d1a420ea7f812453cb1086a0dde1e60349ecfea9b8ce464125a4c5a2f2b3f5fae54ea2def977477ae7243a5a3c1afb73608bb4b64607d14ff50a059048660d53ec71d55f072aba0b830999048b057f8954a76e46eb4a1fb01629185eca4bd7ea4c4da27cda8a786c6a7745d28c732164981e8fc8b49d9fc41d244ae594ddc6e828bddbd6ec452ebfa72ea25def11e02dd39ab30614a72954b298e1143bf409f9b3952253b51d242a786fbe655fafee859a783c1491f22eb4798579129390d7aa5e51b861c467c19a62bb1086b44ed4798618aceace778bac4921b7d81041bb386aebd5c8bdf2bf163aaf4566278f0f17a6b276c0ccf355ff130e0ffd58da2adada8b65c36bf529d2cdaca694b798dbddb2b42e8fd6f8eb4179747b55db8e841e78f3fa309beb4cf5bc21d23d35ff2c3ce498b46854c40fe60ed3e2526e7642659e33f8db99c91b027f46d76bd14a327efabd85ca9b8c230f0f381000ae12c7bd02b6a0437d85cc7285ef0b49f17a4cf0eb735ac8f32b846944e3c91f947528d6e8651be710c6743587c25ef141092798b32cb5425730cb944c2cbcfb98bca1b1133486e205cc5d53a20d27fc59ddf9de35ceb405a8485508c624650e059e3bf40bae7e85e570ef5da31bdac6cd3a9fa64f117fbce10abd343ef777f58b2c7cadfc673f5ca79d597e2afe3d222b2ccf31e0b34fe67e6ddf8f517ef22870fed5a915874a342d498d548cbee214c7a037d90c3de8300e49c62b3500b049354f0f3c3bfe561dbb68fb0d8d548495a28b41fae62cc153aa194046dbec7806e1156dd7893aae5aef4a5c9be3baa29da92fffea35869d5c7ed8665a1748bc2721ed9860e65e32d63df71482eab890b4ea8e5950011395829faf2449e66d66c2bf166501544aa8659c317d6b9ab137e202ba0f5088554a4f36bd576282eab261ece086ca3221459138e492e42412fdd72417af58a65ba479bc5acc5bd1a929254ed9164b43b80259037c4e2f9182e3aadc959428f6e2ca0081da2f4054b5d44ba784c06190e81170367788166092957c4e647ff4f981638e5ae11410498b11d2ac0e0c2206112b81dfebe057f79e0b80d9544e6285492b3640d80d1b533a71aa0e11c6c2922cf59005bfcb0c0e667b255d9aa31a3d8c8f9ef612d13213c9616e0c67f5f7d25aa13efe8f97a652c86129978d1e3ce6d7b0b9ea2087098d1dc8dc0e02f3a7c5cfff8578eaea64f754e61df41bf774e44721580858a74b45cdc6c262d31078396401efd00d2d66608e59d245db467c8fcfc8761ebe238cf998f312319e2ff9f3d575c7e22100ecff7304a1ab5979232d06d015d5906b1e82dad6fcc7dcbe66e4d0fce011d81a766b700605e014fc38140f5d4060f9458fe618503509006ff174073a43c0e853cfd24f99c2b8a5e5a7a8dd26e481088790e135235d029b952c62c040b23b7ff709d4112bc52c94f9652a38b51515852bb24b386a8277b05915a90bf1e0370e873c4a2a02d218e8e0b94b9879ef06905a3a34d0c6dc4ccd549446ba11acf9c1008087ddcee77926d8c048a146fd12ed2b770b494d4f02d384928caca3569725ba305965adde2b4b011f74ffd4248c1ad411ff53ade707ea707b70861a94717d13e7863e6533ff67deb462c91340f6390f29c1db16edd386183064cd9ee5100252252a7d300dca921181b2b7cb059efee3a128e21706847c496155cc2aa7e16ef3d621825dd1e6e37bba2518d846dc18d01f8996565f622519bf2074c0c388e08628e81e8b33f81a4a17d5a3cea374dda3b4707f15c4115734f38adac72d9af245d8af7081faeda8bb0b3086b8487243941b9741c00366bf85203b027b1063ac55946642c9209f1149602ef2b815d044c90050e6a85eb11561058aa4c3a74016796193ab7fe616ff8f5f757ff0ccbf5c765a54221db0636feade2fa9212b4a564dd526e514b4248da9bc84725d8b208e308b55fc72d0922c24314828611fb1c61d45627c05ae4de6e018408d93b051f11bb10a611b5b446b17fc2b93e05692c56e27991bc5e51362cf1d0eb15e585a20dda8269d5fb57eb31eae15311d350b4446a39fc1998290d437972df862576f2c47f161564a5bb9d3cf16ecadaec19975188829a74fbcff2fdb03e14edeb509450ac892ca7ac55b2335a952d12c509023d0048aabb39850ca1250408429ce8aef653362cf1749c8ff6f5bbf19f65212904d941772be956102dddfd4a633a5d2d46c1276d5842d1ac28665c698e284eda14f83310539a6e05290ae202d206101a0079019030802400c80dddbd23e430743c3c3c393c3d4ec3278a13fcfca9689e54f47ee831ebaed3e1dcf8677a695e74e45f9b5ca7c3e1fc1bbeeb74383d374dae3324e430741e9437621af5656b9f3e17c5094ec7b59f721b9638d79fe59268284cba2f24f4a5943f029f09beff758a4338592c6f84c06782bd2121f09978d956efda9837858f327e190d314871a537573d994b72862752b6f67a9f67c650787c42283e3f4e24a87452f561f539c050d3ebf57a8d6055502924880f52983482523d560475aba74cea564f4e164b9cafd7c26e3cd38ca3ba4a5443b77878449ce9f8b45a5a295197a9877f6db5331a9eb39c6ef1a4b0e3811db1a13cf13c777c7428e8a874974bebe1b59e239265e89366ea39f7a9beca2a9db3aba1f3a10386ad987ede4fcdfcf92cd3a9996be124ea964e4c4749ce1072b43494279e9313d26ac9f26136e778ce9df86d9803e628e5e4e4fa38ba9533030d3f5a30979316b34868480287f5a15a340069284f727eb8f1430a284f3c630b7a8e93271dc625266dc61e7ec65f4af9fb4d7ec4ece816ce173851e0283594277e9444fbeab7b4323a5aeb78d2a6877396a45b373118d2ad1b296ea27cb8d1509e78d51c3d479c6506a7e764b1c4e1e79785a5dfcff3afcc31edbae5e38aeece47141f26743511cfd90a6660c00c524e9e74a34fbfd6419ae76873392b0bdd9a210619c668284f6495d626e94e9e7438df084a85f5a13e97558acbfa39666feabc4f73fc9fa4bb57e8960c52c890bba13cc999e13973274fc879b3d739d2298250323cf9744b069f31a020062a31dc74f7e13c7ef29c30c9736ebebf659ed3659552cd99d732568633191d897ca2388d3e9fb5f41af4e8160c1d8041cc79519ca4e784d49d3ce94471823ea930adf3061860d87a3ca087548f189b0a74f7926ed97cdd0de58987d4739c3ce9c20fdf5a99d744d884d94b3aa6f5a9eee73aeb7d2a2b737196752e79173e49da1bd64418c5d892feb38c4b7b5f566788934bfb23cd36fbb5f492e1fc3bbd52bfa965548769a573fcb026e2d252aabfa4d7608ec12992f7e9e712e68fbd76bf94e2e167f0c9976ed9d4e06a7477cc7a6dd21cd31c3c69e15fdad32dbfa2bbbbfa0ff3a79a33c7515ee5dedddd8fc54a183849ec31eb53e0147d6272ceee3b7957a9e3d8db7d476ffc4b8ca9c593e67f77baf5c2190fd6bf1ffbbbd4ad17b2747787a7087ee836cfc03a75fe1a75b78f6ebdf075773f8b348683ba175ab93e16ea960b5d74be4f55e9f7f174cb852c1d9e1adddaec78ce94bae542d0b5311aa3b97e48bdbad02d175add9dacd26b634ddd8dd4ad16b6e8c2cf7fc92faa05265a40ea70c29f65fc97ac7a7f58a69dacfb9bfa5454d642eb2b1defe70ffd61d847148f2dbabb3b5c4ec773e6b570fe15a767ea0f568a9d3aa65ea45b3caa6a6963feb09967582d89ff5e0bfa1375b753b7787857ad0d31a5790d4714e78d579af56cebfd4a63a18bee6e0ab422be8f4b17459af1c5e5041fe6e1675beb04a7402bd6d27f16c5025597ff47cfb65e9b27cdb4867492e18d12aab2454343a2388d9e5eaf21198d556b846b508ea74709098da0141da9d2d010ce8dd0084a0d8da09450931194f2f929878886946816cba5f0990855d9a28c3f5441062a64e970a66eb38775d29f659ba730d10e2bbabba95b3b7468756b07530a6e74f797e20f31acc45e13f39dfe349b1dbfacce19b86424e5b53aef94fd755c7aedbeac5ad16dfd4b4b29ff944200ba5b48b752b84147b75228eaeea76ea1a04677385f4b9225f6cf75baf061f482f8439755fa30c73466ef2ce709d25a8ea305fd61778a25e853146dfee9160a3014604ef8c1094e9c4034d3ad13804c300348b74cd8d2dd5da61eb3fee3478b7f10a996da5c13f1a4d57e645b7f387e1b3aa633b18c61fc20586227f21792a826a6eec339566b6bf83f35f3387d2c475a35c79f658c5fa4b35ced6772fc9ca7e34cbdd2877d5573b44c5d4d1427cd779235b15abfd66be2e717c9d1821e82fec9f0f8f9fd59fe19cd6535acd4c3ffb03ed57b25ea3cc773c459a73f92eebea15b259c20a65b2584a004b2841d78cec05b924b465239b53a3d463d47e7b31dffea7453985ecff3615e9ba328529a576b7f963da7ce59a6d78e1e4bf2cefbfcdad84c56dafa6eb393a5e7e4e816094274f7ec16093732748b84956b4b7f588a60a598d218cdb6faf4f04edab5b19895d1bf4f65b1ff2cdf8fe13a6f5477c374cbd5d3dd50dd6ac9a0bbbd5bad0d74f754b75a50ad5777c3baa5630c1d3f7437d82d1d2f74b70dddcaa1821c5474b754b77238757754b770b871d42d1c58745e9be40ca4339a759b2b7de974feb0da5cf2ee3f56b3d9c3c7a5d7c0faf796a0ff2c8f9fa9677c69ac3ecc6d769dcee7274c02893eafd363d689fcf5d5eea4208d7d7dd8e73add4d8f94cdee61fd0f07a7eb6e9c3c27d7bfd3e9c647eaf387794d142748c95a7651a44dbc9be5aacfd51ef9ebcbf8c3cf75ba2523a912fcaaf7b04edabc61cc9213fcaf8ea7d79f9a5914674ef8b52c67d9c39052d94b62eaf83fe6d7c63cd3918ade7de367ea79ffb12f66fdda0c3e957ff8a3fee11c1d53ff90e26a3dd71f6b93f4ff245da57366b3fb686fcc717e8ca7ff68b1e76bc9173faabb99ba85e34877abd02d1c3e5a387c74b74eb76adae86e26ddaa91a2bb7b1136310ddde66b3d4756a958ced887feb3287f7dde0856790fabcdf1739d2e5bfbe40ff38ccb257f7d95cae8b59feb7438a563ea997c109c5e275869c61ffaccf5c1bf9eb11dfd67d963d46799d23cfcd082f5f39ce59fe51baf96e2fb54be6424e51f9bd8a7e787e26638a5eef6ba550343e729a354f34b7152f9f7d5327d3ebd5aff706ec87969b57cbdaaa5996cfa3c7c519ce0068ba17d755952a53ec9c9293254c471aa427a9363aff47ae5998c3edda61c7b2577f7fa6407af0aa9f7b2f0bd2970deec40e0c2032e14b8526428f6a20b1045843cd7d2824e83d717bd3c93d1eb85a25d55a144b398bc889171e8458a38ce4d7db1032f5ee2f1a117ba592828a15b3155f487b67a2d93e377f78f6ec55475fbeb430bfa8bb03903e7df5a7af8f7737d7f89b3decf951211801ed789898fbd48119f752f0b7f4a773be9164c168d6ff7a1ad3bff37c37670b534832f8a16dcf989853e4133d603e4f33383c4603d41c02140427a80fc00f9c07ac009c680262c28c6c33377c08ccbbb8343549211911391910b795548fd15be4867ec3ec6b3fbf512e7782d53f8599c483fd25ccbeb91b06bef45f26219daa59785f416855f694da2b5304f56e92d432fa493f4fe635ed824cf64f4695eae4fe25abedcee8e597b9d70381fe9f5fa6b412fdb704e8996f448583824043e93a1a052087c263e3fa50b026cac26cd3b9d70a57306ce3b498f84e1703e92cf4f592dcd145bd2fb7c2d39522f577d9de18914cecff88bbc8c1f36c31369dee934c373e685f3674c53a0158beec3c6b27a53a015615e9e2c21669e475fa7d2e799a18e7ebc34562d889f96d4ea5cb2362aa4e2a4aa49e18bf3a9f64ac08ff24f24ac26e1af530987b5b4b05c36e579bfbe8c8a3ef8374fb19697aca517fe7d2a2a7ee8653c454f9cd9e65ac61f8ad6abd4c3237e7a737def7e6d8a7fbdf0c33b697f6b19d6af54f6b02c96f89f66609e24a6474c2f9b18ffd283d5d630e9eb540aa9f7758a1fda2559a579de279b408a6751779bd0dd1de8ee26dde2bcf1ed429196309eeea7f3d1b1b14a755e9c20def9f109027f082c1c32c44708087e8fce68c10e56ce3295d56ec654f677c7caee0477c4095a71e7e27227fc2aa3184fcfb6fa2c4fa7fbe17ca44ab375aa2e9981798e982e656b9f5e36afcd5043e19324bd19bf463c6942dd8da33b2668334aa15b9bd7dd27746bd3d2dd998a24f62fcfafbbb76e6d45dd8d42b7b69cee26a15b1b55d58795ba38a9ecf8b708be5d68c71df0a9701cd31baf2fba903fcc43b1b433ec38dd2dd3cd85ddbda35b1b0bdf2e8f31aeafbdf7bd4e0fbfde32fff56a699eb9fef8223952cf74deecb24a739ee3b5319fdc4b77f3e8565775b7ab5b9d4477d3ac18bb4f65bd962938c909e2db814fe5d1e6dd012fde01ad28ce9dda9d1746c5ee7659b1ba9b3372450515a8400d32296c5105ce05bca0e214830d44c5be0092831a11c98dcd0d82c02881d6b5c1041a4ca21052c804f5c079a2078c125418a25b91f828062351b000400131298871c11834bd85152b44bd8a9101169ade1183c386739a6d2bc2518507ce637a5829aaa97119981996cfe0b0e1aeb0bcc6379b2a32be713238bc072b364118295af98ccd152c5bd1ca833052c462390e8f5af9b615d1d8e0c0e133348e652b9ab952734317c95ca9711cbed95499f14da647152ba2667cf32a34ce15cdd0d8e06021098cbf4cc1f498a2c6f94b8f296a2b371283c3a68b686caed0788ccd151c1e8417cdf886a38795d5952a3cac7ca68795d5152c5e84c3573872c06153858795d3f4b0b272ee061c512be7b8191b2c5bd18ccbd860d98a649cc6e64a10468a661ccb5644e3343537b01c87e7b0ba22d3a487221c3daa7895991a9b2a34453336415821c5088f0db4a4f41b442f3da6a6a8713e458de52c5fb17003cb398f5a39711e63e3c4f2c6e1e42bee0503459b0d162cac2b58583d8288590151a1c203e72be761739653b182ca152c5ee4e2417891155132eee23cc4f88c770f2a4178914b0f2a3cbcf84c0f2c5eb472991e3cc4f47071981e3cbc6ceed263e5ed3cbc6c9586c71631cd830b1e1d68ad705c9c07095a2c77a12917671d5929a16916c739c7ead75601168bc5f26ddbb6a92d4a8c6f3da4d05089b2b90d3dbc388b0a15e72e79e52e7e64e5b272a162430f4431de63ca65caa587142a44323da6629c731767f55845e17c83b1998a61b156ab156bc562b158ab55fb8643890ece9b1a40d32bce8686c6a55d7adcccf2971e549cbfb80bcb46ca0ea229ae4713cdf490420569238a91b22292e91185e55b0f295488605e7ce52b9ba9f656a283888bd9382d6878788086660b620be20a162ba418d93c08560f2ba418215a796fd14407912c449268972692858812449870f9808dcb16ab2cadb37917dccde62ccca0b5f90a2e60ac805cb66c588cd136ed2bcec6250bae3dcb4a082ee8a2055910b9f8102c323e44c6dbb96e98971887e9215886bcc86c1ccc8ac5c2a2755828a3c539162e58ac805cb07038eded1d0334c60a68f3e6baddc5379724b81b9722b830a18c13ca78a2070c8c14ad8d68f55246bf24b125892438212e2b226c65b0ca681c10614b194f74d14347190c68456975d105e73a6630d345129c239d80861adca6650b0556442e3da250178fe9118524e266449caf10654a19445c8f282b24ee66731e0a6845d99ce06eaab89b242bee66732a4a339bb7cc8b96558c6fbe51605b697191a122251585068c1930886468bc20da1cc666052425a74d4a017dc3b98b102d64d3c2dd60a971ce8589178e63712c560f169410713d58e0bc0722e768ba00434b8bf318b81b1a3056389bd3d86c2eb502a229638543e3456f0aa02901d1e6db1944db16345db4cec6c51003d1c6e389d68c1aadb3398f245a9b4779d982686bee460d196e86091dcd7571e4c505c625c645c6a5cac54547100f05040955c9b4609a8d9766a35faa78d9b2e5654b172f55341a31dd458c90cb4bcccb964dcbf6c2da02f3b2650b11c769e15a672ca015c59585e31c05265e62666ab6319aeb112566d58ae9f1d223ca0b515e7065198388f323abcd9585a68c1e2830d13a1c0f0270393131313131354374d1822aba6841132d28e2841bd70e2bdc4d538c8c095b092ee7fc08091ea5b7206ab55e8eac5aadf626c9f4290352b4d2a44ca03240764375f7d82d0c08a1bb65646466b0131f6dc6a5918c8ccc0c0610d07d345bbfafc76970fce9d07c0a9c30c79836c19e1f0abf08d6309351246ba3c20f8942b129fc9fb824dde2f09fa4420319e84b234b0feb7bf6515eb2372a909266782279e3e7a4dacd158905bc689f9f10dfeebe3782559e0536264c846fb773adce0856ed8c60d54e50104c08181bb203030261130603d203ce9f9e190ec1afd7b5155052019ef65758e994e7f8ebb5c4c847f59b88020bbafbe5794c4d49504f489878486a207a6a8a82f2bc1709ba1b4ab720800309cc98b6b4a0f1f7f99da40e3982553b78076f29c016da16215b76babf8fcabfafc93b9c1f3f7c06523f028b34b41c80069e3782559e53b5f46577ce8c26040c2076c7baa71541687cbbfb99ee7c29e5430f7caafcb8889087746419b18a9a25b4a39b15d4ac9d66d95851035211578146ffb55596e4795a34a78506262045d31a3045f7d88aa057e59f953227ad159bc8cc747f67f822e89e671299992e533c61d3ffcec0fa3117e7a4ea4d8719b6ee6d691b92a3846e1d74b3c001355df5dd1d051195d2197b9a691592d595212727e8d1be6e417a4bc28034ba1b8a7b917c5e9f48407723408b13ddddc4d7dd07e86e03743713ddbd44772bd1dd05e8161555b4a8d042809a01743712dd9da5bb8fe86e01748b8a272d2a9c8ce8ee2258dd44743705ba5b4ab7a6e0628a1ba6c802df6ee75a708a25ba3b866e4d81bbbbc37ffd490b62af147c2acf9b9a4f7bbdc22731f84cc29f9adf84a7a7ebd1019f4a14e758974091239da2511549941f0ae9a1702992b7840a6b19fba75a5924ab34142dd2e72a244f617d10ffd17cd1485629b6a21111395ea4a7fa649311ed99c8f1226570861f3e51131faa1194da4a90428d0ed7272d9df7480239ba5b031668057477956e49d1d38d6f977f077be17fa533708e3a3bf8a968a623bda42833536db54d5e89a09890b6d6577b84896c584285b55712d2b084eaee59b7a2b04177cbc8c8cc7432323233e1679c1d7cedbdaf83773068439d4ae7b86369d761d0863b3b2f4ce63cbb81e2c47f1625232333134516dd8d67eda7bcfcb896362c711445babb3967b94e7a27957fb54651a2388dbaf0a53811afdda74262c312aafb072d5a40e1c4f83969b4b9ce9993e74141797c7c7e68f3294caafab04e9ac5727cbdf0ed6054b4618977763e8cfdc48082767ea610203c3c3306fbfff1010224a6f379de8c6125dec1b9b43b99ca74b494dd6eab03c0091a0cfd38ab900d4ba8a7a7270b5ad036391a41297204a5a0a08c483ac39e0325746d12519c46e2347abd665e2f0b7ad8ab1f8674e651727a247ebd62e2e3211b96d8c76766a8276906878f3dcfc3a5588e2538f47a0d815484954e244964a3c25adaa2d72b06c7d3e72d8984e62de7108ee7f8fc9437deb5516698c434752d2d7fb5e1fe084a651a25d749bbb6d2d1fb3c2fe8553afe38eb8fb37aa38dd5a4b0d2a9fc5320cdf83f86abe688e44558698fc24a953c59cb9e5735c7aa395e4befe79a94b11dbd3cbdaa3986d41bc1aa244f456543d5d29a24043e131f2aa111947abd7ea8c297328695ce999147001749720ea08c6e004c74e10f20684ec773002d2224c6804d1d24aa1a679c298cceec259108eaee30b54c59cec88245775ba62c06c882d4b5bc6e33a561ea4e302bc2c823d4e8aee5c57646fd082cba0b7fca87163c628a94552ad219cd8996ccf09c791f0a8005023842004e0430c4881c18e14538af8c2c7342a24cd26c44018c30d27d7646fb269933966138499abf9cef3fe67dfe1f230aab8555cdb108308ab0d29d08c381dd7858c3a79a339c791355440a446c414415226ebabbef739b3f6c54e9d2e7e18f23f530acb6a8061a7d5db5fe54f345effe6808371ae63550d6ad217618828710637477fe345baf2c7ccf93a8fb5bca66323a93555a6b79318d257538f8c663569c1f5221b47487afcd53142de838374e452fac16e685d413e2060b1bdd58c6c0824537a658b47c89ef3b61a96a2c4e5dc592aa26799d780916cf5f3b4a722cdd798634ac1374dac7dcdaa8009cd1dd0100220048dd1d762b08336ea6efb539e6393305cbf0ef9741f410c477f7772b88eefe3cd2def0fe2cdf3246da1be6d80440140070ea50b433fc7796417b49276dc6ee318baf3ce9d6951fae0475775ed51c3d7204ab3205a94856bd87a395f95359d8f4eec712cfb1562710460021f4830d7e88e287f107a5ee9a7caed3794d1c4129728edff7fac259f4b9ce92911436ea960f32a0ddf2a1f42186eeeebeda043ff4bc9a383547eff1987522efaa95d94bfafd8c67ec3ecd8a4ed4fd0c9c229d5493c456b0e8c63e72a0ac2cb11264a54777c7bad54316fd79ddccf3e8cb2f6ff84a711ef9cb753afa242eb1cd1e8a36c7acf730eb41051e76d0dd4658fc9a4bbb5369e7b46424857f2cf13f0dcdf0449ae1390b1f97d7defb797ab93ee661d6dd9d38cb90523d0f47a6ac5739a3ca960e46b14fa7a52724af4455aababb4b965e89aa7c5d95235dc5715925a6bba99441658beeeec25944a5aafbb3139527b3e8f5029feaf50a33f6486c73387fb6c3cd148a1d4f71c3698a1853ac7447c7d9146f4a4c7777352a3ebf6831cc7a6dd69dac16d4e1d26df6199eb35a4c775745c5b5310be555566aa8f218cd78284cc238333c913ce7867644e5032a045039a1cae98ec8e98889e8a9861aecfb726c5e30fc9a15c90f3de7c13a33aef4c35847e7f3efc3168bf33e957559a5e1e3d2f3c224ea95587079249fd1dd7df9a5143c7396eeeefc61116c8177c04e380973b78bcbc4cddd5d6df2d717e6fa54483a5cdea78a597f7d9febbc70579b9feb745fd37f8ef1245f94d50729d57da8a1212121f0994851a3fbfa53512c858aee4c27cd31953293c233a348229058745fed73ec21f935e9fd758a3603764696985eeb98ce281b6463ba73f74a6b1754b6cebac311679eb11bff6651754727abf4f19c8d63d4603e3dfca5b14a07e73f56a9cd37db1f2d1e837ac4d14d453338bd3cbd90fe7bb1a69ab8a3362546f7ced493a99728178862447777e21c69a5a33f4c3645c7b24ac3520469ace2dcbff1fb39d31156e23c67b9c8bbaf5374eb5856e985554b71cc56d922cfa37d9d791e25bd5ef7c912cf3c432fd34a8786923c274a48c8861ae8166d44add09b190a663fcc8268a51e4beaa8dc5eeb9586759616366760fe98db7c2d084e4c7d7a2d46ef3391794036ca5288b511a322e6f46c74932c6d2dcf99e4b33cdecf5e694d04e7256d2e6ff6896947b3758a7a7e5c5a1cbc76e7cd1476a7d7ea0c61612dc15a9bf3669cad2dfa29fe87eeeef31aac36973afc6399c589f4415d7e4c4335ba3b31dca216155ae97019252ae4117ac60f0b45eb987a14980538c309c33bc33a33d6a9b6e2e92118d4853f858ab0b0521a6ccbf7398c00feb23b45b7366a083fad9a4bafd7d0509250f8d76628fcd4c3f53dfc60b5f7ef8730da7cf2a650ec5159d2ab9ae3ccf3289c8f14ce9fb95b32929a40ba5b32929aaeaf8cb695668f3e2ce8c704c55f5ff6914bbea087f9a89f77d853a3bbddeb4204875eaf2821f09978b4af9e10f845b0961eb3d3698627d250ae3fdaa5d72b8a66ab0e0fd0a1091d8ca00cc103502600050645e7c9184fb8e8befcd8bb2f8675cefcbd6a3fffa9995faf25213dfa3c666fbc67c479d8971f2aaa367dd332ddf897ebdf9fd5a4ef0b2dbdef349623ad74e9abc1fc61e438337e58f651f19c4d7190d64926f97dd21facf63dfc294b55e98f340f0d85499e03e5394c482fbb330f31790e9290d35f324c82c2659967c69ef77f63a3bd319b9fb4dac9164ea884363bf9c9a18ceeee9eac6571569aa4ab167cec42ba958396ee0e07ffe730ebee8e742b07ef70aea55473f6304c47bf3686c3183836df780885c3143848757787b1156f2e2b7559a53947487209a3613ba3f9a411e1c063edab5b5267b414152d4e4c6fce546ad62f96e1cf404ce54452479a866e49c5e0aab9e4e1e3f0c759999a8cd1ddb49d265474d3199326b4d6f94b16fddf9847fb2a5a0bf3685f5faf28266a20800901981ce9ae36c5fb54762c319e9e4b18784b8bb3b577d6746b8903963c59f2438c281fa268885a41a5008a0ba80bc5c213174fe3534e370cee5dc697621ff77139ff4e51d1332e6d709c2f6fbe71f27fca3fd779679993f17cd1f3acfa5cbb4ff58ef3589cb19b1c3c675e1f7bf3456b75ba18252908d575607ecfc139f87eadce1a8cfc4b7e2d6d2d27fce936cabbfbb01c9cef4bfadd38fe3c67197b2fc2a6e735b1da09e6fa3770e0062b3710714ac3890aa72bdd7dd9dafb39a6389ff7e3bcb1efc6738e5f4bfb4a2f692f494ec7f992c4996dd4e73fcb168b531469177e1ed6793f8bd32986a6359a70d0944417da59fe110671ccb3cea9cb607933f65797eba479696bfe23cd320c1902c5c984620a82e9bb1ba85b4c1d0b96ac2c2d2dad94b6282540298bd291d20b4a4480a45b4a7e50c2440909368411a45b3638d1dd8d45476e6beddaf147afd475ba29cfb6babb38c3707acdf9f0fc714e5d4bf3efa9a8383df27fcafbc25a8a33e3f2e8be4c466fc6612d63fee4d1be7ad5be5eb467aa4dee2e8a34f630a721761df7ae9669954f5c9b34f1f3ea04bdbf25aeffb98efbeba92bbdaa8a38fc99935ca77b2897792ec94aef9336479c20cdf4829e6dc5b9fe58caee3c729deeda0cceef1327cd9566451fddfdfe6f92cffffb1bf3909652a868c3cc06a1eea29a9a9ebc0683d569d43d089bb0254fb85a3cff4e8156e9ffc6c2fb1fab3fc5de9807623a3e58a7388beed358d253ddc7ff14d22124a12a5b34cb982a654c456f8649f9a1c20fede8d9b096f60e85499e0355cbf4da592e8966723cc7e8f59a6458e79579b54cc559692cd371b4e09010f84cf083757af8b3a542daa2bbc3b59ff2913af691a95bec394f45c5c75e7b25485a3a27e90c239196092908530a5a8c2766426ad57eea2966ed256b7052832719429227924c493203922f904c4182d439f8c95af6da4f55ea38333c33f61baf8919fc4b71ec9d75fe5813c559e2f2c6c7cfb886f1df309c8e7d1c2539b5e0c31e0a3f1d5d71944477df74eb483ceae9ee19ba754463240223021881463c8a6ad0ddf72bada9f64a3e7f85f43ee99fe96d023b729d0fd75e49ae7aa6cf73be585398c424e77be170fe13eca88ac66e7048bdda4fbd5eb8f64a5eafa8cf89bcfb707a8c5ca7cb555f69aea567f063b1cf3eaecd73b4e0c370769b2751f7e1da4f7df8a6f653f83194386b54edda3cc33a476c6335c9c62abdf6def7667822bd5e51333c674548fd243967b5a9e82624cbd757d4ddf32f48445406396931748b680b5196f64ad4dd45dd22fa21d2ad236c7477c7e4da6b8d4471821409386fae4a72448bec96a4bd3f45a7da6aa45b47a4ba1b2da8536d753c67340f430ad212eac88e86d53782c5e3f917fc906645f167512d23491871ea9e961fe63fceea3f8b0222a414450abbef547b25435f8a93c987161caabd92da4f0d396068090cdd1af2298286774636f45cda221928a2a5fbc3e9c1e95611b1bb331d7feacef1afeb54fb5373492806424308591122bbdba65b424d4245447c406401448ae84e14cb9863fa377f13ef6af7a972c2fa64f7e1801f5ab0c7753a9ca7a2b83aa6af1788e9f87a59ea91f8732c8a13bcf10f07eaa6e9f397cd3daee3e5c75e9de0e7b1aa0feafb3eb4e02ccbea8bfe197dfef76734ff70a09c0637ba69fa5ca7fbd0824e7b222bbc4af0c2d2dddde72996a038c1794b10bc1fd6d271eda760252edde66cedd3f56fe23f7efcf8d155d1d85f12e73b33f63c67798ae27c0575b7ff2c36d4ad216a7477e4fc99631fb9fe58eda554f5bd26c2c6c79808f64d5e36498f596fa15b43ba6851a44a8648d1dd8d9fe94321198a022969f3e339737a3c6724be3f96b85a70e67934f30cf1cd014e5c5eeb7083f617cecdcb734171c305658cee86f2048adbfce4a65b1469cc231f632f97f6b1278a74caab31e18602edbae140bb6ec068d70d0cda75438476e194d02e1c15da85a3834209353dd3dd2281d62e128c68170952b48b840bb48b0412b48b0416b48b041cb48b041fb48b0436da55424c0c0c09355a6cd12d2dbee8961633e8961638e8961640e8ee9a1250d8414bbb76c0a25d3b90a05d3bb8a05d3bf0a05d54647ca8a0c20a2e9840860add2243866e91a1d42d32bc6e9151d52d3294e8161911e8161924e8161964748b0c36ba550609dd2a63a75b652c75ab0c1dba55c6edee9a1472c8b8a41c699714a57649c9a15d52a4b44b0a10ed928244bba43ca05d52ae6897142eda25650526d4b4b040d22d2c96ba85c5ec161663b7b0a8750b8b20ba858512ddad2305124e7025e169571223ed4a6243bb92cc762589d2ae2454da95e488762591e2258517a6b4eb0520daf58210ed7a418976bd604577d794b082eb8b6ad7f7b5ebb3edfa7e68d7f744bb3e0c74b7cb4c4d8e191c2e24d0dcd02e1926ed9281b54b466c974c0fed9221a25d320568970c14ed929940bb64b668970c09da25a38276c9d0a05d3269b44bc608ed9ae1da35b342bb6672da3503a45d334672e0a8a19991e92e8104574b478c1752313238d882c5121a6023033dc80c5d99693ac3bfda28ce065713026c56746f556c0e18c006ccd011e9bc4ec43a6c2c2fe8b8243d9b2bf542aaa56ea47e79a14867e0ebc3d11db68b8909f876ae25653cbdb982e3fc9bb13fbdb97b636153810a9c1b7cbb1f2a57141bdd6ddb15d54337be9d0fd5eb45b60b8a89137cb40b4acae582e281e38cee9e81798e4e5b0d9eaa7822c013d99fdf5fc2f564e4f57abd02700315546cab1b82bafb862dc993e29444b7d3c7d138c5506b3aa3fb05aea6aaeedea8d81ab0c3c5d4457d8cdff39ede0c446b179391ef612758aaa2f3974db5f48b969cf0d7125e9a4d28ae251abadb8a502254f879d29b27f94ff9a1b055cab83c0a9f66c38a44098a921874b7121faae65237d28cdfabf527adcd3c1d4fe74305c5e4ddd4df982c674261ef6a62b5f8fb70f9dd9965d58af3731c8bd90fe7fdd182b92cf2eeabe23e34e439d77384a084463a8322e79217f1276b3bb8bbaddff7217118dc664c8eb3ea3b9c692da3704d7a2a2a1291ff4a644c6495ce3f7201e70349053a0d1711306e60b0712131a11b5f7182b65bec1680104745f4743d3ae2741d21b98e84a8a8521ab9d1fe7a6a7a7a42e23636363bda657401230b1839e1324aa2ef7b77079722794bece5782e23d28b1429d2bde560d463c4a3fd65e966435114454ab88aa874671a7e919322a4a222aea21d38de8dfb2bc75f33afd47574370eed22aaa2bb63888ababbabcd5bce5b4ed7911d5c477068b096377b8e577b7f0aa654f7c323b88820691e2356ba69a6782c8ad3db969022035b888cd4f1a4d1180ded4884de6c69d56f0268ec35b1dee94f6561491f5aa6a2a3a737cac33a5f2fd8585ea3de7c701569e17b3fd32180d041084bdbfb53446ff2c2e212a281e747079f5a9d8e7fbcd39d5c449ce8f6d7bce59cb79caf23803d9d4ff7a3037e68419d215756606273810843c839bae00c5750113f38c06680250a90812424179006687600c48373f3c3c48749f7758a5ac2f95ea488df125c4d9c9b29eb61dae160a782e3790ebe99818ae7783598e775b8d224e19d20a647444980c400c8d7fdb5e1ebf539f93e8f59d089bcfb6a8ffced1468452a7b5a97e78cb6c4ffd23aef30e8986a620feb9c560c61763e9eb479ed0c93b362c7b029ce8c3d27e7c230cdf9319d653afe8cfc8e8e1e3e94ef73325aa8cf31c5e1b535c93b1153eb95c82fcd503e4cf17da7cf498ccec0f91eb30fab16747fb905f18350901c3519bd5e9e283ecd0914273953c7caf05351199177a1f834369396c82a25c95a51f84e325a646d1451b64c50d93259a6b02e71dd2875b78d910f7be2d7fcb2af63629d9e8ad6ff7acd99de3ba9cfc00952dd87f224a89ad0d729e6fa778b19583697c26a1daed61f366f49e4d726711146717dec93864b5cbf3eb633d02f2ef3bc9324a787f692fee3c7a47d91779834428fbce3810987820d160ac030460bd98e504198233f344d815609cf3c8f68c01105e989ab47015c3d8268f02f4c7ab216557b2576e2cfabf6bbf345d0717ed90cbcf3b1d7eeac1426c226b6187b267f0a7c2aff984df20efb788c6b3fe5e197f85a3a7b98cb2a7d0ad2d1560b7e14f60e5f1babbd92f008d75e89656272bd70a4bbb3bd4e2e1e136829601b02cc09dc0a2cbc24a047cf85155a70a3050db47085e7853f8582b37e122e1e2ae8ae1403fdfcf4505cc3b9eabd79cb99af5d1a2dd457cb14c544093997fe6f54918b474ce7c73628c20a4c6c4b74370f39331e69c6b59ff2320cbe0101b142a7e052e10b0cd5dd13f861f3c18a6b871b3d741b1b0fdd1dd39b946b074f774fb952e8c0ce0fd50e7831145353529411d3121b3c29a2256fa3d2bdedd0bd4de9de6a1bad7ba3da72f786bbb7dbbd49e9dec8eecd766fb57b1bb7a9ee2d4af746bbb759f726ebde625bd8bd81dbdcbc0dcae6a47bcb81b54935d5e7e5c75f37920b05a9ee62d6a8dbc885424ecff0140ae707b94ed8d23b9fe75623d51d86444cb4a73d93cb0436da3f6fb1356122ce4cc5129373f4ba2970c26a3fcbfe7d8e6b12ef3208e573f2b09ad8e1a9e45d4e2dd35a8ea3056f7230ed93581b952d130e93882c53f8d9325ddb85af97cb45e536fb14af8ddd2761dd4bae1615dd983a0dd556ef7eb654427a3a1e1d1d9776b508d0dd38ed6a4581c1f374da5759a51349035c5431829e80eb25470cbac0788a46efa48108d8e0010b70c2e57245bb72acc0cb8fc36a615e0dcc8fa797ebe74abd199e4848f722996a4242ce4b73e1b081bfc879696eb3119e4ef83fc9ad4b9ea06e10e94c4292f346e19f9a518fef87b5f0692d9c4b727d2822178d105c34b9cb9af739ac8f5d334d4bfe96f5a1c21761b32e49ca96a996c911c93fe170164159a6906695f0146895b265cabf942d13fe4f922d135e529346b0ca0beb6397cc0cddfdd22e19cff8431446b0caf33edbea7a21d2fed271b94cb95c965c2e352e17177c3b10d37107c474ac6ad70a0dbc33033fd732bd93dcf13caf483a91f06f19fbd0a76b05c60a8c6e7fbdd1ddcd556eaeb4e8f0e7173fac492f0cf0a1f3d2a493557b69fea385f20ec34aec33b0ce5bf492859225085a8f858fcb17272eac486897d0a5896631110420e0dc789876b59ff26c566fb096340b09d04ff7e61303a354ab33b4dd5b4ff7c6b3ed746f1d4e15ae2ea37bbed39ea99f70b514958a4c32e06aa5ee49fb746f449b4e376bc3d9d2db0a74c045b7dee011da5aa7ad56acc1fcaf0e1c105339cf0f6dfd5eaf0f656093ee8d86eeed8716478c1004b13b006a24d1dd6d842b4670eac6095f0737c6687c3b7204abe658999055ba15204bb5b44a119868225b83e758a6ad6aba956db53b0c9cc6a10d345a6d4060ce3c8f7476701b3bba2b9db2e9b896d78954cda5d72bca87bc6a8e4430a3fd25042244800855fe753a8eacd23a6f3630886084084de5feda5cc00613ddfe029f8a0db1db5fdd1b114340c110ae1882136ce8b0e1021b280cc108dd3bfe0a1f975d919016790d21d61a82549197876184358ab0061afd370eb50605d670a2bb49a3ee8e8110ce1002098470c51a6b0875db845e13a1d620a1fdfecd35e808816c7fe1fbf9c77927298a36e614861062ba7129e5f190500df645109cd8094268c316a215841df8763ae14b212245b4f47a011152facfb228fa781a116ca96296487ea8fbd932f198adc13cd471aca4443496f8f52261f3e62a247fcb2c8ad3e8f502e9fdfab225e4fcd90b7c1257e5a8d70b2be1030bf8c0097ff91045f4017c80e4839ceeb6b1a1317caba2c027a1a6c09f1975cbd20842396eab6e238d2b3c0d265a280da1c6b71bc1aa3476b4bf42eacd3b49a61e64d18446e3c118dd84073b74b7e7e5e96159f5f0edd0e0020d2aedafaf53f41c1d0f538732cbb409a6b3a8d70b562239e9ee9b6e1cba8546ab7f3a9f9e8e47a7d2ec85cdf09ced000da48db5d1a8a07b8a9446000cced8b283afeb33c1d47490bb33497ef876d8bb4f8234ff68c5b683629a27aed35e708e4ee4ab9a630c2ee43855738c6147ca8d481ff41d97f2f5962eb6a52d07afc17c5b825bf5c682eecea15b3928a1bb3bf0435be9cd69f4a01b06dd313863072c7c7a33109f8e470716ea3cbd796ba17be3b1b1d0b91cc3dfcec04113bedd2cd33b773069433a9dfe93544b3b6c3349c1498e5424c7f9b42f7c3c614e8e254c9663a1bcc3f7e9cf68dff739111c1a9c88ff701afc3fe6d11b2fe224cd5e7e91080e49f30d284ed02be29e5fbc3689efecd09c8ccbfb7d9f773598e7e09034dff8148a756ae29d221dabbdb14f9ce3ad96cafe7aad96a4d9279e1966dd66ffa8becf2fb5b60749b38bb3cc9752f5f0cfa5cfa62f53b11c89bc065dd2140ae5d485d38ab5498a73a4495d48bd06ef4251240a69cc3a3997bcb3d5478b432babb4739c0f572bced977ad6c8a31ff3cc3b10c5faf1a382f68c70ff18e5fea3f66d1a777f72b6d7a4db44c5fa752d2cbee54b2360a27dda75fa752ad28fc4f328baa4949ff50f971c8960927d5398bc2d72e8dd74645754b0667b4bf64800515edaf289c1732c861b70c5a32382283d74fa1b8b3d93d064674770c70447ded3e9505313dfad8845a5a03064374b7ce1114395ea4fa4a484491e888f64cd8e7670992f8547b2534cb14264ddd39decfb309395e248c3fa4971c41a90fad91d37d27b096b65622225c7b25617834f33c924d27ebf422496f948d0a97e0a7f7a9487e925cf52f9bb24a27d58bff849d329e4a1f5a232647a194f128e3a9941faa064a599a4ae4084a4d81562c32cad822652b421925c9f8c5eec136c34603c185052fc0b70beb4f811326b43d114348a906aee869b615ba7b0ac544b038a926c6b79be139abe5ebb50213daf334ea15d10a685361dbd18d69f65e0bbb254824fc70fe9d659debe3693e6150a182234ee253bd1269a3c438428ff626d1d119bd189da56004ed79fa97b5eaebebe5b55200450aacb4bf3c4f9c2f6b3f75d3ab03a420c7fd4af3bcfc18df0edfa7f74ed8eb358255289809c38d30d0082305ed2fd0cbf1fc8573fc45c3bf463f35a376701854fe0a43060c31c2078302feb2d91d6330a0c018d2b9cd0ee5f245eb8b2d7a636dab8ddb447182dd5be73a694f45656fe0ff18eeee2374b711dce8ee22fc386b77b741043686308255ddbd8610ba3b081908dd3d811ff8a0bbd5e8ee347ac003234a6065099e5424022969414adaa8fc501fda25fc9f243f147e11363f5ba62553a055ba569cf83e15ad4bee7cd1a8deb2e8a77df0a28aaad799b79c5e5cc1e27cecf3a924f217c924dd1d96504f4ff765b3cea51ca333a8119432ca5548e62d89e62d896cac2639ada0bb55204677a7a0bb51d078dee9b4852008314475f716821a7477579180136ca0f1440846c0b4c4173b62a50217495ea4104194143574e8eead250507d0d866e06e46a0421b02f088a8ddbdd5800924a6c400830d18ba7b83820634b07812811636e8ee2d059956c680521b61743707a345ac0f6a40001d74f74645140d702a41035d0075f76664004e9ab20881802cba7b2362072fe05c17a1077437a784c4880d88f041c410ba7b75c21160f4600926b2404177bf1ca0006c6c62bc8e70a4bb3733848004153796a8015077b39af8600c3023411432ba7b1303e603822746488901aebd92150142f0a4bbc300a35b0c670b40e00010000102b25b6707ef8c60d5eb855fafebc1ca99072b673a2030a16dfefeccab686c48c7433ac95a3a1ed2f17c2d59f5fe32f257d35153d4193135f0f11fa168c9097631f19574b70edd92c095ad6c7da086c6b7abdd5c7f879c23185d83715f7051d5ade3e1fb140a47d562345ba68a24d787caf5a19284b3289c45229d493c20d5655b978ca42abd16876eb148e4dd175efc7d3e2995875f136be41c696d668c7f16e5e183749cb5168295c83becf39657b41656cb5467da4aa913f96a3d4f77ef70909f204184d8ea347b658ebfcfbfa9a66fde3aab384b29ffa0a5650f6bccea54bb1326dd271f86f394fd95c9a64379581fca468da054388bb4c0d2cd45a0d5bd227b15a557517c74afa0ac8854e85e1dc9828deef6d79291948ed392919453735278129960af6490c5ab93552a52ec5da53e85e2570d4c494c4f444dc811949ae19934c313e9f5aa761c2de88563a5d3eb25832090e47cbdbc8943f0c3ff0b054810214066603359bba0cf4f88b31571b622141021650c723c929caf9747e2213d4182f85c0c83587707f9f42c8941c00b8ae87ec11ab34c69af9747e22617a400df2e5358904f8f87ef93e2cf7c7eca209f9ed205df64d4cd3d71d22dde8b04b4a248d4020ab85ac0638c261aea8a6b0c202cc8fd2ce8a2bb27b953cb3948e1fb19974f2e7becb34cf47a757338c070593c5690868d0b132e45ace048055c88a194022d8404f1414a8113cd002b34e8186ee52908eaee2a9e20509045a596f4506000271faad76b68862792900cd7a6f821cd86a2b5a07767de5856309c0063c717547c21009f9ff20b16a0609f2758ad5198f47ab17002a7e4f46a9ebadf280a1350697c3bf043194cb0a3db045b09cae86e2f22e4fefa3ecf789254e11c0288124891600c1230d1cdddc0393591a0e5c3a412f530a13dd308a2bcb881cb0b26babbcbf865b2123f59cb8fe78cc90b9eeea2013c1e8fe7c384c7e3f17a98e07f12452210d3a5ff242f412e1154d1dd1ff30fd7d2a39aaa22bd3ab59f02eae980743c3a322b724b2e115c1141958dd524359124891ecf9953d4eb750508d0a0443eef5aef9b615a69cc8bf0e56ae092b4acd29ba13238499f9f7208e3297af7c9aa390a35793c61433e3fa550d51cc519f501db5d9b4ef871b04c2e2ea4b823467a479c2f0f3475d7759ef38b587caa97f8542f7aafeebf4970fd299b716d3afdc73c5c9b24c653043b30d58d6f37c37336c373b64516ddf9da19766bf3ebf17daabfce151102da2208df2e24c2812ebaf1edaa773f536f7c12ef804fe5e24015dd3d010e0ce9b07e59e9e8431ed69fcd9b6125766d20025a00753fd2d09050b543deebf5f4668fc49e0c8f33cf10c9e7a77465000c512c9358a6fbb3dc34f33c3a9a6af2f92985f494a2b5b08c69d57b581cc2f11c2caa9c718133f0bd40d50fe110e1f8fc94a094d193328616f80aa4c0550121570576a4e1ba424b4e0e5138ff299cff94ad08f55328544d22c78b6484841c2f52374703f703a7dbf3a078b0801260a0022351e4c0065e50d102212727bcf4105381195705be40c2353085ab40b0000f00d4000b19740040c900365970f8140001575ca1f3831d4aec38810a1a70c4030aa286282283176e8609e448e00ad7162c3819bab918381b0e866eae878d2b0245b8222015be0bdd5c0b1c8f22acd14d81d35ecb340304c22e3f46528300cb8a2abadb06bbacd8d133cfa32ab2546142773f408cb0fecba693eb012d74732ba8d0dc8eae322ace9f42712e6d6d2a199922866e35ac7c0d33c234ab5945b686d1b2852eae0da6756c2e286c39b6181f1650af9260b900c3c5c4b8b056dbd629705d73bdddc032b2350c1197d6f1b2f5b66d2e2e40381aaeb76d5b7148db06b371dbb67a99c26ddc06c3a1f0b46d2f1b4c731b8e6ec5bdbcdcccf8c6e23856968db56ddccc26046e9369978dc5ad7edba270dbc6c52ce158e036ee85c56d3be0b6ed85db6ed852d8b61517c385cb0a26c786c2b66d1b37843b2166b52160e3b6176edbb895b771ac1919205ccd3614b3b9c4b02ab2c1701cab6735c3711b8e0e21db0eac1e9a242b27ae59cd2d6063ad6e582edcb66a97026c34d89aeb56abd55b6fac8ddb7ab0c5703fb68d73e1b8d5b6fd6c3b5bcc56c3711cf762b4f9e03816b7f1ac3a1a156266d87090b002b7c4e6c2a9b0b1b6d5d69c13e7331bab39fad2fe6d46acda868bad8f84805b6d2f1b0b66c3b1ad98e0b898d76a0b5a6d1cc772ae46e66673d936181e56cf0b3c1b37f312c36ddbb6c16cdb6be3b817ac5e36198eb5a2da7a70e5b6ad7436191a6e8b81d9b8239c0b9c0b5c0bab5ef5a6637be1b80d07abb562712a701cb7bd6cacd82613c346e3c26d2e5b0e2016b824181a196e83c5d4c070db0d5bccb6adb88db5216d2d7043381e1c0d07c3b96cab8ddbb8edb5b5c0d17030ac6de5b2da36a06db5dd6c325bcdb66d443617b89915cc16c3dab86d7b6ddb0b0a9c091b8bdb5c58db108e665b712e6cab15c7b138d63684e3b615b8994d668b6171dbb6bdb616b81998d5c68a616ddb06e4db8e1f31422b1a5cb6d526b3b1766c32322f1a272cb8b0c1c0da38ee8583f9ad75020f0dd2b6711bb76d9b4ec334d939f4b0a981430fda7180e20c3070b031038d26930610583283323acac6050d0545180f35bfe82728b240c0938d6dba018a286a384d204794a0700108489307cf102876ec0c99391e0034031c3100d9cc6c4266b01941c8f7460284f811bafb8ae082ce4705975306036c8ef3fca0414b4e1a2f7c5ef382a740426b29014a7f2e48b4b380682e057cbb972b333290c00a8c0c8c0c2b478c101899550b35513e30f588b97999890962d5e8d858352f1e8bb6c2b172b1c600e200565e72703032ac1c1c2d0441c0ca0c939a909593c3070e080b2e4b58db8b9007ac14513a7201c1b166584a3843b072831f7e80e1c3e6f272bd70c4078eb5c24a87dc6c865c685081959797179cae9ca0a30800ab1a7c08801515649ad4985063c2942427903178660d2b4259a6866f002d1899150e9729961045f810c3caf1d2c2a6e3c7071c4421f363055762d858220747e372c4053384a5e49d6cf9a104979b99243333bc3c61fd8019f20306a0a8e107071c81428be70a0e5069880f58a129634d0081c347c7c684c78a088c0a427c988119f232139363059454c3aa8599246b58998149c2011583c40292243332483550a11006aca4c0ca1132f3f22a01c7cb0c0d921e3a30c0ca4c0c920b3022acb8ac5a2edbcb0d2f332e445e7e0c15d194323e62905e666054c0712413038b4926051378ccb0020a3f626850f234830f33311dcc0f8e1fa02427981a16cc0a2eaf9510991a960f3f02230333c3da7151c20a3ab2b9b0b0b27141814726065c0d41426447cc0c0a32403e1831d10089b961c5c08ae1c501567ee4601db9d0e0e2824c0c2c1e3a7a562dfc60a5dbd956b1950e6be5b2430f1d08b0015b23000130b6d81201074831440f55aaf054ecc14f87273948315902e5a46403d22be8e76606196070560840f0461a68e0c0c8900c3d565001468b094a3005036e4062e4d5c20935342f1fc8220356503185140a80420b0296dcf8d8e99a384014d41b6e040196431a3de080675d033882c68688ccc0508144162099168cd1002a5e4384a46002cd4c0c0c0f162e6081271f3631d99094044902042044004828238816118080037a68c20448c80e13586180010426434584c8c0bc4820880c4c200a250840c40e4f2420014734310d11f151b31561d506c7064b0def0177060b07dc0c5ccad85cc0b1805b818c0a6ac4e0c2e0bee0b2e024b08a0037c58ab2c5b0bd10d3820b0f1c2a702970276c26b84a702181736d2d560e8ec66586938989e160605c38d6b6dab697c6a163c5c26a03565846586129bdfcf00d46083eb41860c5c5a506660617f8c082a28795428d140f7819c20a4b490685191e3b80b02283032b87950b34f9a52626494cd0cbcc8ac88e1917125889c1c28a094f7c90015a21e0073564587179c38a4bcd0a2bb052937b6cc08acc8e1c1c0c11971d9724b09c40a3c2123fb08ab0a2c34687cd4ac7b513b3c3f5c60f2e34c404c108ad74ac8462a4b0b25aead9799979a9c2ca8ae66505560e4dcf6a63d5b0726692b8d00083c3cb0a2f332f516a4ca8314166870c0a3038ac8cf0c3cb0aab2d3fb87461458628acd13d195a60bd6a74bcb858db4a6765c472aa49e185836902c3020c93cb0939b6179a950bab66b55a712b9855cc4a6686076b05b30486041d4e332e9817986d05c302c2da616d312aac58f041e605970758c951850fae17160b31473fb0c8f8a187e7fdc61170d08231c200c38b2e4a191412599014192902646b1a3dd8f1d1d21103e3248726a6374e20060a5e38828789258e1c800c167c21020e5c200253ec74332851e623f015820fd480810ac40041057ae041872739483531d590e405175240210b0df4100108f050c56b8187ad2fe420430652462b6f1c61086b3c7192020a2e168820041de0c0052c208123042084a7030e4d9a9892be3841084ca00513482f70c1172200010736c00029880000103e5083052ae0c006b298c0152b1f262171e150c3066690e105083490812b1a8084111f7a3a3c71d224090991178e0bc71727f002014d2071e5431d6c484a8284c82b070e56dbc08c2f4e3082106820035748a0010310c0152836241521f20202d28343828bd56fd8c00c198cc08b1080400319904003068084008cb842030fca13233b9d1b455052a223470c7864298208a9263c51184144adca0a99375647e08ce05204ae8d9807b01cc035809b62f3e17a563c5b47a3b3a261e528bcc0f1d858805961a5c2b68395c286824c092490b06ab174d4e468e158a146051a1e332e1998981718eee5c5c565b5add033507c5de925773eb420ad250c1440a078d172042d637c7ee3446a77ce19e8530b03b444a12589ee8f8a18c78b08f94d1121d7126ac1a13bd76050baae739284f4b2024fb70b911e2e3e9e10e37ece93dcb9f909249ea082f3840e4f20b5e799a7e3273e016b24c00cbf36766709b802dfeecec0f1657c9099d2fde5cddddc0ca7a34b801287026702a763b3410fe9f121bfb7047f0c191234046888902141860019f233c46748cf109e21438282828082840405090212f413e413d413c413340428080808480850102020403f403e403d403c4043840409011222444810214084fc08f111d2238447c8902041418082080912240890203f417c82f404e10932044810102020428004010204c80f101f203d4078800cf909fa01fa11f213e407c8cfcf8fcf4fcf0fcfcf109f201f201f213e417c80f8fcf8f8f8f4f8f0f80ce909ea01ea11d213a40748cf4f8f4f4f4f0f4fcf109e201e201e213c417880f0fcf0f8f0f4f0f0f054f13c025a65ddc9630c8a93999c9b999b2a5ec57770cf99c999d9c1ddddbbb912ba3912ba3917a7c38912f88b8aef0727a89c089d50f27dfe9ac9f93e7fe5d88bee331f8ee7d88b375f9122456cf0732d1d4e9ce084099ee3c387e749eb24d1e87e35e145130d68c7f4cba56bef4552d4c495eeae6b224a13603711d4382e24e43d37ee00d7016070802d77aad23bc3b4afeb004bdd7d2d6c76732edd1cab9b5bad8480038ee3b6373623746f6e746f4570c26500210304194000ddfec2a5759b3d7cbb1e1d2f22e4dd5b1bdd1b11ba37361a533f5a756f2c267898d8d11d2ec146b7b7040cba1b8b74e60fbdffd8125d74db585d624b7727b144ee6e5cfba9277d7fea5a9a178bd9258e2c71d378899ac64a04a15b0918b4a74409669ee1cc3f2a718513ddad4411ad4455e3a7472811831235dd1d7ad98ab8004328000c6cf6d0859c26b61a4c6892a4b7190ac0ea6ea09e8e47275b7bdb38c2b6861082d06da8d0032034047ee00335b6347ad03d67554808b0340013f400a454bdcc054a989e9248712111019b106372dea8a2f0a792885f369dea549a791ee1584dc2384b159d254b16b1bd56c25e2fbc4424fb88315c4714a0bb7f160aa008dd7dcb9066c5184865560949782df84fb93e1592fbf7a778d5d611ac32a20d3bf33c2a22462b62887016155143440f88b082080674378d089ceebe7f3f871f51da354496114f1a3863d53221326306edb241b7cb0644dd9d423791fed12fd46047bb6ab0d3ae1af8d0ae1a64d1ae1abca05d66b8b4cb0c1ada65860ded3263b6cb0c2ced32a303edba0152bb6e30db7503266e808176d90007edba418e76dde006e8049925ed0a8249f7f64577c76c5cc0ac58249613495629cdf567ce0f95ce0856e9504dd98e8eebbc301662060b1bfeaad6fd052be70c8b969ec2a294f3226c2241f2542d93386156ac4156ed6df27a05202700ac6e57100ff097ac520f66c5227c3b990b005274b791aeeb8cf84bc7af1d4129ef876ac948caa1c0ac583482524eb613746f2628c1f60112746f22184108bcd844d0bd85a07bfb0008bab70f74673c959a667822d5a4703ed252f875c9959c06820d207a00c40a80c042c8dd6d06820820720082c88b0cf9fd9c1fe679d2a0aae65287c599b18fbfd4da19cec199e6e0bf3e4a5bf3183805ceabc4bb70fe2cff38999cba4fe7bb641ebd5227ef3e9c1b9bcfc519523c65ad92ab871d3c88dd4c8ea9f3d0060f3d70f1b045b78b072d3fc466513c0875f3d0eaaeb245952caa24a0bbfbdad8fc0fffebf8cbc62af50e8f3fcbd9daa70d0b1d0c008574ed10b583936b079ef6d7dfe9b467729b1d5898c2c6141e927e0a85f2a7399e589c454adce7e729101f9e1e3a6316a422ac243d84e1bf13d441a2446404f55328eeaae69293ff4a6c14eeea9c91d7bacdb6b4349713c433b01c1d4face3f8ef675a69ec437b65977e376773d634893caaa3fe1fde26eff03b9177b09f9ae30433cc6916c59c0f7f8e339df9f7e1506c0a1faa26b9a43429469309e8eea176915f7552c85fa2756242f2a17abdec19dd6d45603960b5e4f8cb5675b7fdac52fbcbce689e637f747bf876f73339c3e9d540572deb97e78ce6d50986600245b8c62074f774dfae8c5be019c588a55bc7a8d485148663ac81114b70f486f851d11d9718437717e21a8cb1e4c848109efe42071e3340d9f143912c45ae2733a801288ae886e971e8f09f5e14a992251f5aa312ca15054a9420501406dded1414040aeaf1b49966eb947762ee91e7786d92a02018f700a748fe0d0a0205e5e1fba8a109eca88bd9199e48e1df38f93f35bad21468c50ecfe0decdc8d9f7d7c66ac6b9f1c7e24ce2dde7f3537edf8fb37e195bd156bd05414ad618fd70725eafa89b6f863f6631ad7306e297d597893a7926a3a26390664c414a1209fdc4f97be33e33a6a27f435142e3fc1bb33ab9b4b656d922ef742af5c9c43a614abbb944e25d0e9277df07e51df681c939deb877f865367b2e4971e2e0cf3598577ae336cabba3a41f2713f82454928dc2e13f1368a38c8eaa65b2f773a8964914a70b7d72bbb71a2e6678ce7268999c707941d4685ee8f29a784e2eaf06cfc7e3f176ba49eb3832dcf8cb771c4706f71c19fc455acf91c16fdc5f390e25a900a8c4f028d7c1a205194334032000000000e310003038281a0d872362d9a0c24db50114000266c46a9a501bcab3308a31858c31c41000000000000000303310004a1258a65ae1fed656b92aa8fa7122590a3c8212b538e3c31596bb447c8664475bc7ed782249f5a68e05e76d66774d9cccb486ee38ff5359e6e36035c9f30919d01b2c4e0fa3de2438222b1b7e828ddafd2ba4ba0a81ca1112e797fc76118335a2575d9cdf0af84799e148751d368dc35f349e26e2ac7015aee966c83fba18828f9edf0104aefaa901023ba4cbaaa8718689eade6759c374e4eb597e04589bcf17667eecd586eafb8e4adadc92fc008990125a09072b2626d56d69efad456207deea495f5e3d5a8ede3d31fa74d80d23f6d4cf8516c40fedebde3decac7d149e23c293bfd56a36421f60e743d93272e53a4567fa7601c8dd45cb51b68b439923871a362e55d0bcd496f021b44eff2390a67ac10c34b9d397b5436e3b6a2557f0def6c14a078cd9decdfc330d84bf0583760fe8fbb44bd8ce495206085cdcc9b860dcbd2f99c045a8377e146b452179c9e08deacd5e3c9107625165aa07d1f24b025e77bd908345623fecde3f4139dce2c66423c225742b25ccbbd3dcf8a1f45e2cd16575a0ce2779da716d8ace5976b2e4cba11c4c8daacb7aaed68ed799852be7dac30c25e716e681d831ae6f9c2e98e07e6701134d2b9034f988e62a49edc5e342833bc53da718121f24244c57a417fea14333491133cda33534676baa9b9b63ca3e408e97eb71d93a807b378fd5352bfdf79bfa0e3f63fcb949f96dc94cb19da940b34d7a11e5d2fa516d0460f9b0bfb4d979b9939e2550b000e26021c77b059d667fdd1977e73704757046fe0dc5f795b78b5107691e6de409f60f95f77c565606f0a677d136e1c064c05a68fd3f83cc80f00fcd327ccfe0f6b790a7d876bb9c7fd08f479ca113aeb87f0cb07ddc85fb3dfe316d3b5570c858fea0bfb4ee496ff6f877c9b4ef0ce29db76ac6634334926f0ddbef4539b15010aea6158312df37adf287f73455ced4ace65580982dcc4c083447d90c33a0a73ee8f87002854c46a8be12a319f0756e98cf8a6e1b66522d243dc6bb780645ae3861a1e4a2f0fd2d577207c4ca2841e31b3d44b6ebaf55a7dbc2b77d52ccf3eb75595cf58e938d967f3c816c2aff4c503f76a48dd0f1e412be1d3bbc51a14f2b95c6213bf8fc30fe2bc7bab310017f8ec6558e054892fa3f4e8e76c11423d155d351511b1e09fba87c0f1af03218dad1e938e48b46dca6240702986aaf87f1e6ed677dfbfd6f02596d13d7b08cca6414f0fadb9996715529aa3ee41f0b8022df34bc90ced572de973a3a93169e2af9c00a7bdea762b3527ac60e1bbf6e151d9fb222282f3efab2e26217f1992bd565085ce91a2dec2179045bf06159c213314d4d3918d7bbffef312207804c18adc2bf75c951b5043d043a48c15ab972363bd82a52b5af6ba57e56227374f5dfde9b243990f8451b6e9ae17b1db5cdc4abaac8c5c5b26de5a0a37b60486c97d6106b546f14264f63ccfa8fe59ad2bb4a7d8dc5d507a10f45b5f5611210ab671725bdd12c384a92e0f7ead3937324a860586cf61410b93ccdb0598d5ad92313807e41685ed7b8fef46aa6c4a3353a1e05fc2f2ed547e2a5918c6e620463fa334cc16b0ef4d83f1bc4e2c54727404f10b62fc9cb10b4d6947bf6d6bf8adc9bb2971f0b2f8bab7437127656b0d3910346eeb2e8d1e89f07fd532e5747e91aff87aab643054219cdb7c44a6fe21eaee98f86eb2c1a59d25979a4868dd9867d396fd39675650a782cbcd5763cbd2ff26db5beef635cc6afb6b7141fea7cc94a91adbf42da21ff3a1b8ac1818233c9ab973a62d913f1b2c8c7e632e136518b9e52ee60762b68d91b926b5c02884d44a566ed714f6e31d3a0defa549fbe09c55ec3c242715b8016031e325ba164b38e2ab3c39086f29e2fdcb17e61f779f88439faf111d5aaa5598c27cdca7beb35a57159a53ba32db3b8ec3154bc50ec7ddc3b921f87e7695ca2fd4957e41033aa76d24e41ca126f72620152073bc4bd5b8b7d52bba935fd2dca770aeb7f90028754cbbadce48d9e902f184e984233a1b02ec1d548a5ca2c2c2a6a5724551f80411426b37e346ea35e0f20e1d1d825be9a4e055f0624dad4a042f51d075a978b279ca72a519ee7c9cb06f69e0e37008dd5fdf497dfb82af085f3f9df588fbf81cb5b0e02d55ea7546a1431a6ee7ecae2c70c2504f49b84380f8b887811fae7a4e262888768642d056b599a8dc4c9984d4505da0d1f3af4312e5d5eb36230413ffc2f49f51ef39101a2ca2b622f320b7a9e8ef7ced64ef2b41470e7c91c4ba1a55ab216035f417d6d18004d66b89e0a0198583cc9cbb1acbda48972c675043266900a0660db6e3f1fde9e747250328aed91360ce921984b78e81f9e8754da1d1abfd6a48d40b8400b10ccf6690facac4f3af023e0343b6eaf18bd84000fa779abde680550b00b5698292415aeb4b1ef2b1a39e4848ef9ce9c55be7af319d379e91f7e881600bec124f408bb827cf6347ca09384bbb51843edc530f8ce9b3151ff93ebf353d6b320e3a6f9fecc71907ad28d01e7eeefb4b8224a2a57ff9d7f9afafc717c0726cb03284121fb2f04513806742f6e82706fb6d643bcc5e8319d61898d5b151260b6b52ade976a3f62456f96ac9c6480809a8d681d7bc0fea10870cabd18d54b24889eede32dca9f1e143fb5c4dcd1d85a73920e743e08cb80d0ac6bc4ce834b168d1369bf5b717e81853aa5d77b05f6d5b108048c09104b17e0bab907f856788c4f1fe5a8f9d866652aeb1b9ce72ce5eb2ffa19f8955e6088a43a5125d6f00f969a5e44e09938c906f18c898b211edafbf8d8773edc637219d5cdfb7d0e0e737f58e1f56b05cb4f58fb5b37b4f45d86cff5e762a016fd2ca918c13bf2adcd191be108e61a4e791c44d437b990b3fc429002f237c1e20fda79021d05a01687a5a34986c75aba78371e6f37435cc713c7b43e9ea3a8764958bf5fb3493c1864a46f5708037131502ccd5767cdb0f5983749c8c4e74e6299c758508306b65c2bdb622d276903f1fd44736cecbcb82f2e3c6922daa41765612e3c8083123c63fec79813586ed9071736623805c65d467d066ce55e4a469ebe7ad3480e16e0f5fb37fc9dbc7dafbe5ed6db9a38e600edfe6f73301d27de01f0b6d320b2a4a5051d45a02e5204eec6b70aec865e1b97860ee9889d578da46fafb431f7115c9db85e3b1ea6558cc5cb00204f028b93929a38d6bccb6ef3a3a37dfef28318d79d63bffeda9950c4f721babb6f38f44adf96e3019a33db8146aeb3a589499e5432e0d2e2670add37ecbe32f71e740ebc4e360fafd0797cfbfbf36860f5237ff5faa0f21ff46d2e22fb30ee000f798bcd04fd6717fe46e1c06d5ad0bd8d12e6e2901e93998cac6c386de8bf650dfb93b5e3a32d8f3a1e36f4d5b11be7a57c06301a0f3dba88513efb4b4dfcfb77a8fe31acfaa0fa08d14364be9b0a1ecf5aa9df76010e2f6d8aa1cfe3a400fa05a530f657880f7bcefa4380b4b770d9945858c0cbbcb0555daf54df8e79c170f4b31a86e10b92fa48f6cb72c389aab71dbf92c20e33677e40b8ebb590c170f355956d9e16721553db3d0263ec6d29ca297df2e4a69abbaf1cba25a51992ab06315468ba32a8687f81adb1794d18f600e6ae567d619e56cff603a22de8548cd732c4eaa49485562f69ba7754703624ad0445aee788b83f07afd5769d1e4a06eb296061cdba35073616df1e9e0bcbd3fe8f66797d034b8e79f7b36eda3f382e34b0709182d63f8a321f3a7aa62e2db69e48f0073bd7607effeba7c4f13422881c60fd6993b478721f9ad26488dde4e308e5e7a1acd826b4f14473566dd203b1dd93b5cc71d1c7f6d9a5e7b32fa5a317e27519fa569485fe8d4f7f432759f7d98302a3eb696ff278e653db13cb1675909b5794a703ecfed0f839bcff3a1175869b777f5d90bcd33ca7482a1f7481d5302229249ae6fc8cd12fdeaffad9ef18090f33bfb0771faa7fd657ce7de87ebc481fcb52c5cfe7315c03212634e78de87952a8bb4d84da82071e9072440dba4b3c701886c98e4bf24a6bfb7c91fa18721eeecab10dfcb6316088afcda1c50b769b17b16399d0e9a71641e780511f381a71954fd4841190b05156780e415a78c5eb381fcd34128fb6183f6e6332e25ecb21a77af403a8c0381bbe9249df3439932d449b5c9bc418351f0f0d9e150d07e31d28d01e8981e2afe390e4f1f1a446a268b062f0ae4093837e9f1f4cf45d4fa3fa0158f0e8d4a41b1bbec2f1153f79ea7df68d41548fc083373e3ce38c302b4effbc457ebb0b2da8d580f7ce3ff872d59b51970583342dd5cb2aeca9e46aac96b4132f2d4b41f3d73dad96ae484a129f7d738f3c3b7c13234147cec35566ddfb51e28fbbee35a99079adf0193444c65a3f3829901c1d9089119f6b2b4f9b0bd837b326eda45826fe576f50f41960aab72cdae75231030cb82e7113685734f64cd7430df605c93aa07b76139c35509e6c2526eddb9be6f74e56d3884902c71fe28b86a73f92825964a786683f90eeee947b7cdb52df56ef88b2ef26a1b81fda42046a845e8da533b18dc5d1066976feaa51e93bf1dfdf2d0c903722ed2887c26f4fc5333ce25c5dde53549223976941478cf84e18fd41397f5b08f163d07ddb09db07e32ed9af13b4fff35b8fc2a0a40c41d8bf0006b7b9d5e83958dc4ee762467ad71ae47131cafe6715f44252aa95b63304c2d27524a9339d085769547ddddef7d1acd1bed0b37e64a448d70c2ae4dd8cccb928202eabf96306b2aea929c82eccc55978af5f95d4684d21e292a92ca5945e791ccc6a782c56f711384d64e57a7c72acf24291e7570de231b755aded5184a89b99186a0f1dfba225ee0a5a3114557aad479a056dee4113aa28b12db33248e233c7a40e1da4f0146aa9a76882c2307bd41dee7a85fe9c52c1892b817e8e7a059a4c00d21eb45e3b9a854aadd54e4672e09c4453ff7c6a5293d7c2cb98702d8e5b77c4aa499f83569215482c838f9f6773bb6dfbfb1fe6795d7453632a67d1cc5e0da4ef73793ebc7e0024be421782c713381cf9cacc82bb65214833784a8bbcc9e013509ca426e96ec1957d3bb86920a570d92d3451d0ae1372bf6cba1a5d7d37d927f0935f9a609842af5e4df726eaf7eaf5b400f96a30a0a9285b7005a7006303c43b34cb36ed78f3df647693f712328b30b603e4f1b7c853e60e666b73637179aed678d2aad01c0dffcbf978321709d711f4d76c35f1cab61ca2a7435193857219e4ddebadb8a47882ec11752da14f3a1cfc0687ea18566725e68a25b45a50e75cc270799aa9c14965fa3da6bea0e540f47ccfe27c062b72b88b3df8a7e45cc9244f382163e62b354930d389dad06980bfdb9d6fb2c2bc9c486abfd3c84047d8e09ec0f5f2a921cf9d0f7c76ccba18db66fc5d745557a309ceb50543882af0ca783a10a93171bbd64fef5f420cb1f4ef9248fc30ca0abb9ac184831920e7102fd843c2f01e5fdefed39a1704a1ea08b5bd723dc1bd5fc0bc1b835d17bbf4876012e32e6f5b28c5429de2fb7cdc72f3e2532d917ccdee16223f4dfcbe85e51eec10a8075e04dd4b48d90607afb54c7c41807c194d3e541022ef104e16ada1db3eaeab3f65f01d7f5520e106fc344c9a95a7f44bb79aa3cddf6ce36ecaa3878c5a02d42b6ae0cd8613284c2760caa97221d55e07a5307f0e27c483f30462c20389a4dc2827df90fb9ef9f09afd2f8f18d1f8bf0575968ce2001e3d919237974009d8c9128f91408de8f9b401c547e0708aa1a6ac9446106f7934424c3fdcc84f156eccd8433752145a96b72fb63cbc2479678891ecb6fb47e61c739e4fe62311b2c69f053ffdf86b4c82ddcb11203580276c1eb4504016139d59905d27b5acc5ea17299cf7c87ecc0f91606abf8aa2211b2e3a0a0a3e857fd479751ea95b31f0ee6f34fd8eca0956a7aef6668cd660227d603c6d09172eb5c3c20e3202e880fe3aa04d582d275306c8add9bb95ac3cf50bc5d61df88f8f581f648d23e00950b25a26e1e644f539005d2287b3e1634fc67e54cbc6c180239db7e79bab9baf16afbfe438650e5ae29696cf3fab9275a04bcc3d1002f3a3cc9426c378d1d017db34336391215b8439735d4500ccdc21a23fe6dff469bd6c311c0768ce9dea2869b20e6cffe4f7017e3d29176c60e0654a4a3b001b70888e8e1be3aa579030c2119f813f7dc1bf1e3d707fc269ce9eff2f2c9ad1cdba6ad35ab72974a2a7b1340285458005c68daf33e0cba1c6a1aad06c376e20621b5d2ded619b4b278ae49a82b88ccd2a760f0c404ca0f307ca5a8052da0ec88b757b80c8c74fcae636cdd86df2889a78426d138a937f19a2fba1740fcb7cf143f1a8580807ac3fead17ef433b15c6bdf5ee54691fcd2164ddb4da264b50c05181bece3ed7fd472e64f8a081703a6eaa4f633c24e64c9931f4a0384f97fc48ad40d7aec7d2e55f57cf0806b4651a5623adf15ebfa447c382ec181ed654390a9dcd893de46f410f397da6bb1be7f45056be80066e96a520da4f0c8dd79c74661288df174ef82c0b1fcc9843fa63c1139572617e8bf756c8d24497e3910fc1e9c40f41e3c7ea0549d86e28bbc0d5e0a76dbe0fc9f05e382623aad788beada56ce601b8c1abd689f662ae95560862a9e424b7f2447c0c39ceca047fa31701f8c0ce50cf73d750c4a50b0d42a3abc300c6eb47681074a14207927d04cf8d7f499c4c577ee9d88c158ca87d45d45d2ef4017ef84c68f2dda0c74d211f496f33df24e1f2d89994673ba252dec361f55c15f5b99a74890c0b5ed3d2f1d586f7acf21bb1ca746e73d16b94f3a227298cb309db343ef5733d44bd8adcc7aeabbb089312fd76c28f124ef80e6590f9f8824f87575c265f614e3590934c349d2838c58bf5b3dd1f5c214a31283c798a1088ae8330e506a98535f19220049879799ab2f8e507c48f9920b356d8aa700e75639ae2ba0b326ce06fbc1ecc4afc9bc94a13fb832d45426c8b909c7bc08afbc1bbb26fdf78f031e06f52bf335ebb35ceb6db57214db6ca8bfa028077e935c048bb17a41f2bf5fefec156a7a32ebdea15bfab21fa2d6038269d37c19a6abe6245b946ca51203f2bb7cde40cc174ab44b279f142363e0ecbc88205814fb9361ea09fb1961c4e3c207e8ca85e4a1f74595b26f1b6521d6389fcff05f4318983167dfb8b8fd47197c3bb81a93de2074123332951346dffb9632630610bc109f390a83e3c3343954a73829239b5a1a2148bc7975044dc27945a4781cf0022a2438fa7beffef1ba0578c11fc6311e3e3f15b8754351a3475a6db93a2fe40791d2ae9fb8d5f0cd9c6c895365caa72f6b8e3f3f84588dc225408a0116a01c32760693d54d81832a1f821d1ff9470920ba879ea126ebf29e8e950c6c24fbcf373b6fc9d6c9305f32aaf14743734dc32e9c927c49fc5c2467288f6c0b0e03540a084f5bdcf0d7a9b702d327f9b6f23f6f91645c6b4d2d10622a0756c6e1191ec6981b653db063dcac0f0d85558b40a6ab732a01082430974516ef704a8ba86f88c9114f080c3944424f676b00a1ea8c933ecdeb7eaeca67c751275993bfeb6ed55d08f8b5124be225075ceab64781417c7d1c4974a54e019914a1bc0e2cb7b411928cc469e2c76b0505d53ad43149d58e58dbac06ab342c803e484d4c7d5876e91f1f1ac6a04387c44c48f1480238b9c67e7b8c4824e6350dc9cde09e59d12e583a5d460b4af37968537049490a55ffdb21de4b7cd16b1875a3c380731abcf08dc6f77b41906c918a91e9cdc7b6b5cee5df835c9aa5e01c3a8cc687b74bced277916a53de521d051bd9eee7979bedc92dcd02f5f37c3f1d9f1750f9a0609719d105bba4fef047e3e608c9f248cc63f0c015790772c5d826842963557593d6f190627a7a0ae3e7115659e67d3645b47677804ce52f5f35e6a5181ef5dd2fc293d07da4a05b34d19e9282c96e2edd203d3badac7ef0f9e47b77ba243f92c121257af9296452c67b54b69830ce31bc0e0185242818563c21445064140a7570c64c7e4f0378515ce828e6584a6909ff3320c6596f47a4249672bdb443f341a8098c5fe7a8bc59cf964ae59db3c97fca25a9226f4cb81a3e863319ef0dbf09f12fdefabf0ba3d72e15812ff8c03d0e83007c40171f083b96a00917fb132690b533ae2f463e5634a68b726457c7983fc8c45e946cb7c93e71c0846c410d0ae389dc345e683777f7011a984502fe309ddd8b20f92e38b74f6aa1bf1125bb5fc493eeb9163e5275060224ab267003bc89ce8717e6e46760df35d97e6450a6da7e8d4baee93f4c40f5d9484b50fdc57a75ffc6f3c79dfaa8f45bc02f1789b562547805a49e8bf29196d44f93589a26d8e4e7fa3f003160aed360b9d5229f5bc81b3f388d4327f45454a889072d9527026b38d4a8e4e0b81e873b80367c476c9f0459a319b9443ef5121ea77552cc98172044fe97d8e3853b81823d1b570171bc5f26f81ef28e7c36760a12fc3756dde0ddc98554cba682889784dbb495b5e8fe4dbb292c3840275e052b8df1a134d464e8aba11c95463a3408988c0687dae6c3872ee67eff3fdcbedba7920c2564a6f988017145cb542d5e8dff767f51798d6f9a2ca41e51ccb45806e069172624564cdde45fc2fa5b0217918cccd863982ed6dc6a999a8fa0140dcb0d409205a1afbe034b568a520e49a838cecaa87bcae147c3c8f728ed440b730af11dab346c8c9759c3e9d21f416bbb2216f66a8f66a988b293cf747f0effb54b50de0219435a3e5da1dfe4a6279810fae1f06f12f97bd2e413a20fdb039fe88e784aca7268ed7d60cb00fb5cc745139e29cd6c9df3a7deec9470239a9caefb43666c40e087755ab2f3ae02c273a0f32329da955cb81318b73ed598abd7ed51e797cdef33c033b37fffc14ba7c1873c75c8317412c251911bf0389e105c473e66f4de34711c340bb9e30ca7bb735225425f2a5d1cf183e80bd222bb1aa346eb07da10d82327e4850c691df2a7559e0bc8740f23ea0dcbd286f756d052af27e8d88b272e1828a264a332e6d798a768d222e82a212a581a6e98ccf930c41914d2b49db1baf2cfc68b6926b0d3662bba857696128ecf6b4c8dbe3b6ba19eb177f9b016b9954dcc536d20b36ada01481658956574eb5ac4f45c00a671dbfd00b7b30a12d83ddbea27468f964ab9eaba902193eda9203164c3e6e12d6b6216467c1e92e63d361ee31e75b68ae9b75295ecc0581e81889cadb782058d4ec2ee83f1466fb3313fc240a010039045093e8b09a8ab20547397f47cf04c39b9c11f269be59c7b911dddcbe9d5714f54d8e6c48ebce88696587330c540bcde665f538930b2a1c1090991a072170cda2ce0995d2f643435643181036fa8f3d86690f4452e56f096543d48ac1db1bf09316e48d6d4ef614ce6da39ce9415b0eb95731b88cd9e6c50d75a151971fd787a7a0ab667dbae18745cdf2505c4ce7dd71de432f614c7f5af75c1ed48b67c062ed4bdf7808bbf6c5bb4f6fbbc4667efcd2b7795c5b98b5d50e4fbf2598e9fb03dd99ee9f0443a1bd00243be667a9316d3fadf79eaa3f0e885288b89aacc71d0cfc4c7c83e4fa6cdd715fa1f36c0f55062eaedde3583fc8b452acc2c31283139112ae19ed9c524cbbf582360ff405b9fe2f36a38ce47873a039611b29871fe87b2f95c9c828dc937bb5d355925a2509c24e537e10799d8bfe1f0486bd7d04173f6902bf53086dbe0cbb6672c22aeff3bd986feb064ff887cdf297efc868eba833d67bbf9d15ee97a53d99201a18005f6b788abdb631ce43971ef6d9a83e77a1ae07d5a4ab2cdb3a2154ad0044c2dcde562c1f53caa66cd5c8bfc9b5a1aec2bc7b52517f463c508bafc5f14f784b9d7d4deececd76f63244cbb499f84b3dc31b585298839d1ad84b9fae73ce8f7b32be1f2bdd97c5b0d42eb9924ae807d198ccfb94986d682da4cf159e367ff38f6e5c896cb5f6bedb6227f041a48700a654d1c7a71091f15c239a030461f355fe9b9fa2a43ed704322898646d1dbc58b1676a5738ed952d072dbfc19473c5955b9edbae66e621c9af2715516d908504b448b358c9060cd0b527c13074278275f4ef06817e413a726e333705f94c060dd7c5b60e833ca5dfd53c92c5d9626ee93d0ca556a0c51b7b42864ecbf2621e43c20d419d07d91fb08179e1d92d487a7ec0acfe1177fab2d89d62c719927bdf69c8297c83d46b902932b9b3b08092f211cc8e48555d505dcb8fb40279da7af25d36ec0362a9a2bf5758b840d7b75ad31da4c9ce700c79f652ef3fc11691f1cb85cd5cc862f276fddd6cddda9c2acb1fac4db6d9f91cef800c3cb66674b6c574298c86517e9dda37a708b5626d7cb3f0b902d5b79659598d08e991f474a2f408bc71ec2efa9c8d353a61963438ba57e01819f861e102cc04def7824ce28fcc9bb362118d23e7da605b1a7142f68e9ef9e6f07e39ec6f7cd408ff77a5379fe85edecc851a7fe4e5437c8762928fbace3feb7dedf961c5c1858dc91635025cec7521803e3529b8474fa910b2fa2163d635442d00d20f08da5a4eb716d89b67e348ce0ad62c299b3a3e7cd05fbc153b887decfd2c278dd3a3474dc25537d79ea2dd2d89a5a0fe79dd6bd4760a5cff3882f8ccb4a265cf850e4e04e0a9ba86998e1fbe74e6c76139968baa21a4405e85e30e8082574b156b8e129dfc40a11c42f57978f4a6cecc52b7d0d36203d5fbf3e0d341f2be0b0a16aad93f882f7491044518e251c2f3e11936b6cd04f98de91b8af3e4b85c2105c028f45bc7289ba193d9063a91f01c6a51b4094235c2730983cf749476e6cde6ec2c589f960aa3f8ee24f18eb3919e6390ffac207ea3e5c5a6b17ae2b840c265481af94d72221f95f1400bc0b1b962607fec34c43e8cf582dc2a4a82de61f8f2de2cf872bf1d27d09647d67dfcf7666dd758449b22a20cf00671c1c7e5a5a678688cf9d10bbf071c573253f91b8594a6cf840424b85445f193c82c94e6f47f78d91552b3e824c5de53cce4f807f36411f3b3b1d195216f9148825151690f386cf75bad108dddcbf86f47cab83ab5652bc35d0cbf3214d8c45616ec8c6474360addfe01b33fb4eb97f085d95fd006be1a0987686e0b1fcd6c961b5f233e033ebe5d6d3ade8a5ed4df5aa18dfff697b3c0d441413449894f7751f54bf7a0623923f6aa9129523c73b99699697b1347fc09c50d72e1ad006cb796ceade633fdf0b3d7fad50240f1ef15396c369f259cd74c9675ef83cee0fbfab9bf56fb5ceec56fa43789ed9f24f45a37caaa398de349f7500fcbcd353913795234299374f16c1fed17feafcd0ba717af25fb93af79036856f170ee18e13771cfcb019c7b1b91263fc6ede3e0b7203dffb62192135d1c0074ac9ba4ab1126b8271a880444771a4d1c9243030e029a7c636e327df4249a1b37b98c43c06269fc720bfde292617f315ef78b6ec67f32f034b9fdb3a12fc5f6d3977e4662e8af6941c71eb0d474321a7663ac6c3f37fe2ceb183383ab0c5701771b99678fd7d6ba7da593b783990f95af84d1ca6cb95d0f9d764a9be731d518fcdeaca1dd1a5f49f352f13e7172f0d273b40f77d2ca8b47eacfdd9d1c27125f661239ff1e254804a95ae757ef9c85dc4dd3928ebec8e9824f565a41d14d783d61184f199230d64ef95d27b15dd5a14c0b4db15b8a6df7352c6b782c5dc57147538ee7fb2af9096e553fca00ae5c33734d9a36e3e2c0ac7bbbd5600b5762aac6ae395ef9139fc2d3890a02c66b85658087388f3b2399299a7cda76bec22742ab01055ed1e8388f632f39fdfbaf80eebf8ac1b136fec7aa02eebb05ebb331f0e01e34259d293a7104724c8c7828b12635d1cb3ac0585ac0ecb4a6aa69152cb2feae0a43f79b731e73a79bf6daf6772989873db3083794faff695a140a1daf7fd09ad41c5f0a3dcd689b802ffcdb4db0227745180c5f6c6d9418092ab70d9f875a51757e3a6edae7c6cd56f75909750fae1ef8c771e02370e5173f6c1f019175908769b76853c703a9635c26c8885da3aa49eb02b99327d8627eb8e2e4729095375cd52fd4360f5c5ca9fc5ae1da3864439a76c9ef433a51e854221a7aa746bcef38912b41872815d610e5ba10e918777a84b6703c4860e512167a55d5355e2ab84a0d41b312927c31df6cf048069a96552a72611db810911b2321bd20d11c0d1f8f25c8e899c582c1dc8a9291c3f055010676264e46c27b24833ab8a6b8afb297c2d9111379c8ac34757aa912e0c13319c1f5d53a9fd5704db5a05ed18a90cced579fbc87c3e04c125e5ea79638486aadfc6baf71cd5fb8ac0a21245843b95a753a5c6c8b2cd1c0bd489cf0e42340d19bbc997ec9d38deee799e3e7bda8dba8751587b67deec40f6f361bfe290c841107368bff267f57a0e6ec87119b4de23f622e855f923b7298be61f9632cde9143252d7f46441aae9e41869f784bcb78a50c909a0a2dd498264462f7846d37cd1c6fb0ff3810175a8331aa0003730f2309bbd18b6b51e4f458a641c68dd39abba7a1abec334b48e3109bf424401aa850dff99c37db87ca6e995384ec0a1707d1db82d91dd7e7f3481fabc50b45032bad4e84a27e26f5331febb74a71454615823215dfed37e99bc301540468d8057b82e0eec7d919775770f4eee54cb9c140fa0e6e52e0d020e1b9a8565a041f32d519ef34f41b7ce9dac62153235b3f040bd2a5f2269b2295ea8e9c435bdeb3420c56a81a1f5eb4b2caadad246ba70dae5c9ebc0f6b5e3d52a49cfa012831fb8bc013b912bc372d9b2dda211dd7e7cddba0b93dce3252097378b623267eb22eea9c492b5195fa6c4b2b24a03b7618a9a44b1ee017bbd64c747ecc672289141f46fbcbccb7132b89eca23add6375857936b7e8d1b8bb27ac63b24cea7be5be61bb802a28a982a7f7e8975829d39313bbbcd60915ddc5364c72eac7274c9471f869b40be292d903d0b1c0f573a087b11f281baeffb89eeed3107b86f05eba940bf256a3b6d97937a3e1d0135a5ecced4b1eca5a5c650ac5867efea45a565f51150a3ba256dab0c35713435ba74634d0642f4463cf00ae09978a37d9ca77384fe56c8fa9fbda312b8e47393c11800f79e9e67ec01fbe2fdfda3b3f79edc13bf7b7215481bc17883f26ff0c2c3832a36c348a4b0d82e82d5fcd1692e6c0a47a6acbb5cb95e6d796a8efda32b5bc43c250d28f3777335fb78f4c0bf4b4ef82b0cf76fc951f4b039906bf8a4ffffae0ca805c55b31b1f6927e7ce88903c323ba5771e261d519c09a0cc3285927ed814ba62d0d85455603445995100fa27ffc8cd17b73a82329c322351e0459c460ad8a890c5b7343c89bb3a286b715b205eb7d131cd2852e13db7531d258b87ccc72e823bd1f42faeeff35840230ee7173eb30c61fe812dcf6429aa1df1e63d7825ce60e47586043d0db7ff2bf198d92b017123744c9ee5243d3593addcd07e81fcd2a4fcf9a50cc1483f00c87fc4c401ff3dd77ff61179d383374e28abb6afe392b8989dd27870fd6fcdf3d4ff6ef48c18919dd539ab00782a6141980740e381636d9f7fb65cf7b5c0941efbbf0cbe3018b415bca5f9aec59d187f92137b3e88482d8560fc1229637402b1285b9f543945f9933f16041e926a6a7d5bdbb16b2c3368f9ffd82d10d728631c0c39e9c8c723f732af660c5eb1234889230a1bfe00a272966b10c744d81af07230df97d6cfb7eb7f520326385e6f3106a1a58e4cf72d7c4199c8ebbd2dd844420722ba73526bc23acdde2cde7fde4e3d74580b00bc320aefb7cb248599aa8f0028d19b1ef8ca811132b0625167e4568dd4e2725c2bcb8a80633d4b06b9cacd68576bec2f8ff83093f49a153e9862832bdbeb5e19bc6049309b7f65e37f14eac75ef52e627d93b02950c7b66e61ecd47fcdeca6780f6764ac54f12560c77f5d8f44f325f7cf84baff2fe71b8df3e8727e430368790eebc8350f7838aac0ce88a66f719ad31a4d38710d4d73d39df2e08bbafa8dd17f1c6da7fc54eb6151b2b288a08621d7b3bfdcb9b34fa29c5fca6446f3f11219a5868793e7cb95f333c355ffceec65826f970241b1a243674872c814558d36ab1e88a2497543bc3097a4787f815d53c644db2bdd666e398d8fb60837808c83cd4b21bd7047a777dc4572f713d571b532fbc157582304a16cfb182c218960d9bb0ab61915a6948858b269de959123d86245310c9d026888fa471f8f1a7920c593ee5154171cfcec67cd311d3a969f31c44c76ce78f3c492487e0014c4b9205c65bb44e3df5e74af32fa3dee324eb2a50849c831baf085246dde094df04e16b248c3b42e53f9b56553e1d939579fe889d7c81b9fb19920352ecc4f72c43bfb917ab98598f5ffcb2186827269e9a6250d3c1db83d4329367a80901244440e565b5d367c6aff9813251e8f21be50c8c78033c044f4a58fc9411c2d468d4641ba41916944a0a0967c63a542b7a9141b131e78f49afd28e2b3f5c4c9cb681a199f9e45c69da8530cb66fa9c611a2cf6374009b1238c51faa8978bebb3412c0120325d7a6d94cbe44e1932e79390b5a8c27cc766d5ec86a78fc034a2a9d6e43c31f5f0d7bfc3a777967f5071923d6f3c6e6ca0f774f753b8acfecda3d54e15108b74fac5b28e13b10b49b52ab29b6e45db67073160c4c04b87bbe9a92d2b1d612d870a53e8c1fef9bbc38868f2dc7f3b30e663003a90b9c9d5871169904934f394af82ef3b3ad22813f97975d8162a6e9beef15d52436f694164eeed05836ba3b86684ac203598a67b0a9c081be8a530b89da8b077ad6e760713f5228a12b62cc6c8efa95965d236e30a9412f97525795635a7ad9b2d5ca157f907cbbb0d3f0339dd75b79f3bd9272ca0c1ae4180ea83eddd490d8a248c9dcbcbd84328284abfe162ec49a903af9e61ddef46f416c0769f319b762c2976ff426448dc4a08360ea7f63f393c8c54996c63af9a2557b61bed7e243475f49d6b020daf72cd596f158ce4fae517b821e2810c61a6561e0c67c58a48c1e8a2d65f3a42be4b4884c8ad35a5298215d9c78c37399908f914228283f4de9ff03a47ef7c7d66a162d85ae853920d20abbfbb0e7649166aeb51ffc17798e7b8f5cf75a94756a30b5532275d5768d8dc3700847844e707acf2163b18d161d77d90929e2ac1643425a3da39fea0d211ee4f52ff74e0b602783a8559cbe16e73b65c900cbf688a4d452fc87a1e1b36811636e5586a37e3ace6f32714970e4cc67d86a50fd5a20c39e22b19346d520461a466d6b4fa52b2fba29bb24fe6cc4a2de31c8dfbd6f79988d34736553dc2ff14339ec63858cbfa7f693ab5001e40846172fcfb6059c8fdd3dea8f080804b27ca2849dfd63c9781572cd82c09b40f067adcbb41755a8f6599d419a844059cea8e6f8fda0d1e597a2c52942db7c6c59efc33ef3287cccf2fb8746b4905f7f40c964f656119dee42d168e7505edaf543eec29f1a07927085bd231cb20b6f45f3bb6f4d702e8db76e7478edfef609a7e7bc6fb36556effb5dd0ea68232782ed5d006707fa11e6bbfdd7729bfa8406e7335b693e3a4e50ffcaef90b3858384cd85b0e03697a254ecc4bda7ade8afb2b2605d7bd99f4f1e9165745d6af945d7891d3f9fbf26ce664e13082c7bf7975c01b17ce3d29fd889e2b2cad1ffc6319e825c8a020fb11bb63f5a6d38f5eba0f30853ac0769339f56b04d38ce676a926e0dfefead2cf7c68e6831d5f93cb9ad12f6c6af902dc8b469c16ce59e8d0be835efbe4da22f0fadeeb0f63e2a763f70f09554fe4275d69faa030ae4674dff352d8455007438e58e6749bb712d6728091107d87423ddd6d2a6002ae14dd4247b9e53a6c763b02609c026687de250d051c63e28227e2702da225a32a9e7e0db499f51a367792e6d3603c2db04ebcbe4bec7ed197869ce4deca40bf5d232b54f6763d88053ffd30d85723dec5a3bbd7e6fe3accb283b267141dce8fc5c01f6c66ac5b49001c3c7a5623ae8774402e2ae8c25860b9c3e8fc957f0b876ce8fbb415d6fbde3c296e37bcd124fc73ab907a02e62ee00c10c59c4dfd50dde373372c1050dc93fc502be87ee0a4816ca209fbe7f72ff94b875ce7bc6f1cf23742fe9b58b0abccec3d1f033a3704b820d1657eae2e88db7badd8b7d3e3aa0a8ea294ddd5fd84b49c19628bd74156365d47964bc6e6688ff78b4798049bb425ea6d1867adc613eb3d95778a57fb6b31bbc842fa238596cef0393fd28818e20660698407dfb1bfe2ef5b9c63bdf51b0caab9aac0ba0f0ef4ac6bef12ec63dab7d70f15ed9f22fe27ee31facd064900a2f3344826dd7a17463dc0792addde71725ecae8b373745ab61f48ce160017a64710434695c4178795fdd6a836dd1bd7eaa3bd479a941d08a2a4b02ec1ccf75b49804087fd8cd17e3286098e4b3ab4281e7e4a4c2e0747e95b3d7b19d9e4acf5aaa40c9a2e96122e3683fae7f6716b7cdfc8fdc87662d023b8a847fff44d02d437eaa9da79c2f34c25fe4b8c3fcc454601190fbe8aff704e71492bc22f1c8a46d38bf079d4d7d9115da029598006533adaaa4d5012b44559cdd23ad52b5280ea763ace5bec179897f26e4bbe487a15384d6e597d5e2af5bbc5340895b2a9da29f5bb34bb148d38ce5adce1a64ecc1e175c7e711aa1bb2616b2a052edfb267e95ac5b78751e9dac6b54497d739d44ae546b58306326e5c1cbb4ad03443fdbe01b9c2a47a63ed38a5ba7faa248b75596034a1fe14b541b3c10491e3c7c2fac09b3f87f4bf73524fdeb815e2afe5fbaebda600d808f669b3c3d48da24bbf20bc4b4cabba180df386185462d937904fabb724df40424f1bf5dc0f73ea1a83ba2825127908b40d41b699a40011423011ad3fdff80e82b178bf78d68892762482af564bab8694c0f11dfcb3b56d8527fca1bee0fc5fbe4f84ecbeec2f0728f76fc32acc1ee8ce4e28d0be2cd118ff92565b48c7e1efe1e2c7c2e2777b3c8c26379e47cf0533cfe8cd1a9762070b996b31515b5cef7c7883e306a82d24f15d8d158e425465647633d2d56df429d0fe4bbe021becf627ede6173c37b3c599ffc5ff91a1c898805d5e85c3615ab3d1fde84703c37b9622b53c9d661626de44a7f307387be77a61c8744dd466217c45e1e44f24f438e53ae73b5707de9feef0d546780c38c389369993d70021c58c01e2b8cc09d97cd3d7b47cb26687c0cbee2c98bfee08d49283021af06752023f4f523dfe901a1d3509916f3fe31ae2ae21767e0d11a4631702f67c25361afcbeb01368da6b02ea98bcb2433706cf9bcc5f0b4d50300a5d343da6f4276e37e4ca57d71ef41cbefa9940b34e5435192a9b4ddce937ff9125511b7ed760b81cc256cbeb2443709307ff55e03127921cc1692a85eb4d0e32a727cd7e4c762c2701bdca85ac3840b3a19139f997dfc5c5570e5fbcb6299d64c537addf23121772e8f46e18de100569f268978f5704c7032465adff57af5d35db80644a465670416c586a65e696de798406aa3ac1999ab0bd814d41917c3cb9363ad738787ce6425a8245882ce430f5e3a3d97c6a065423eec70f644364dc53fac7747b027af5bf860860ad3f49ec37f4b5e166abb2a6648db6a52bbf0d7812b85f56ede61373e6123631692235c96af8e3f6a1568c7e4bb5b2a27f7c7d63e1f91078b85bbc235b156fa637aba45c4e8b7bd1f78fba9a00f98f37732428e0d88626558eb23e628da1f5cf766625daa75602ce83e6d5d53951f6191863e704c6bc26a5d31ac759cb4875d6818df37c48611f0e03c8d90907092bcbc8e3d6a49282dee176808614e099e589ef788c82f53a14ac55ee8cd60b38df2296395467b4b11cc4d4365504b71e9f4018b5bfe44018b3f403953356d01d1eaaa720a89a2f0a8e866e6a402369586b00fa8aed914153d87700e1c9269d6e40e701888c4e3611d92f6f407c5651e60438e1e20eea0f09442cdda2721025ac47135e35ac25aea5508e1e6d408d8704416992d3fff1c6cf2c75d1c34120910b5214bfaec8e310d3606855225ac6de33ae44ab80d301b7650a4e57950dab7fab2c04cf29323ceaa55aa1d1b71b15c2161ebfa5bce88bbb62a8e8f0a2008d95cbafd2f58b13f6b53ee0f5f8774226406fde8b9f2035a9260398059ec2a4afc67ad42a43a03939bf0d533afe4537fb3982063bbd117b1869a537541b6146faa08c5b4acd8f42d364ede60fec7ecf381c54cdf20d941a8f7d3dce9af9b6fc655b4788b191f18456af233ef58683a49fd26880af91b5a71d5ac4b5a58a57941101e8bbb6782d2ce1756463f1161f38accde6ced09505c191d3192ea96959ba1d0cad1b791b07436f7c4a7edd7d41eb94a4d1bc88cdd5a005b6e34f2977944a31b2d166352996544fd5500a10bfb71ad848f29d7becb2e264e7d5c8d69b407604fda8f62c976745788a9590241ac7e7b6a78990c3061978adc607554338bcc9fa96ca7ad94b8b38556e1ac0c7b0b374612940d6be83e71bb44099bb9f473c17489ec19dffa2bd79c97be167037ca18b96e7cb82496cba90652b57e9f89a45091269746590cb054f406fc8fdf6a1681baa54e6e113e4e10ceb49d8b4addad784ca4f3d303a89e7ea8e4bbe0d544d6d9c4896980818e0350d3433287b2408a67356825f8e31f864be8df246643e351b1849b2423b9ab5f1a29ce692a4c2d5041770e81f2c69ece00eec55d599f8ca1f05e284d7cc2b905049836ad71a02ee020680025e4091f689623bbbc06a26d87afba09dc54ab46cbfccd44dc26258741da5c524a1c4c35f32254a82923ef500b14471c844da25b15968b5a327d8d0524b0860dd55163c0b168020216cf1658025e8e36a2821e67431f95ea9531ce7ac7d219b5a9905a628a209e693c28b511ab98d790b835867206e565d2475fad2886006654611f6fa2950a4e22c98f311290b9b357993774ec1e999473c3ca9674f34d7194733fbca5c9a91b3ec5dcb1c1f310ec48c74ee8975074cf7f34ab49feda228e10e8cfb927a2c20b70843618b71c3f39b265da821546908daf1357c680051364f6fb59b1ab8aa6f51bfc909114fb119e3c3e9f7bfa056051b651c2e4eec061e9f48ead5f7edf62bb4da28d489e477ae9a964454929d2b9b9a98c356e2411af3bee2ccc0cce9e3def257bd98dc942a61d5cf20fee5ebf6a9518ca230bbaa48818b098e211d2732383f6386feee8fd193b8fa1eeab98ceb218fd02f9ade5ccfdab6d294356f8762a08d2e8935e2aa124fa4d66f3f47165f35f69b030918313cc0fb5327f28d3551b9e4ef341adbc064a241c33642b071499cff9380b5a53d098cd453360985a84d51f8720a04901bce62a73cc97eea468c95681cd9f09c75f107b97787d0a21f9b076021ae7b0f4a8dfc29b8db2ed7cbf45410d0a2438e789fef09a4b707f80f523ea689d13c4ee01a72208ea974c811bebaf3951e928ce22cdd1dc42933630067771df34dcd52b58037a8ce7b6d151252951df35a66e89feb1ba988e9f7b71f0fa2b3899242509a5e01dc4f01942589614b7dee21b1a42d5d78faff21fc785cf785648ed9ce752a1f7096db6c7da2facf57f4b25ecb257174d34f74dbd82b84e334b8c05878e5427fc6835f550e61869423ea253cf233b4385f228dea20fb93c0cd370dfaac5df3036797d21af38e2514d90ef0d7eeceee4df4c2432506c7e1c730978a079b1f57c8563f13460a802337aa37ec0f38a2ffd72a36e1251e99febfb2d6ba6392d8398dfd3bd974776f731e4e064f5c79cb2246a38b97b17fa7fddeb8b042f8bc9e7d24a1ab807cc32f51062afafb560caa39f7cfb22d10d58d0a6a6b48eabfce3fc4792a5ac6c53993a07931a1517d4fcaee1a8c326671853493728f336175432f74a5e7d7bf285abdbd1075863f8a969bd6ae388a59185a17a0b726c87a828cde6859cf19a610b1f37ca1fbdc08b00350a4ef49a8e1507d4e749c29c1b580abce411a5a80d663901598777223f2d0069a3104ab6edbe6db6f127a72cecb7102a759ce2ffa3d042ebfbc287015eafff100095d586a4fe9a57164c9d0404c240ec5b7fd2170a0553fcf8cbda0ea4bbeabf0fffbb9d608f8f6035ba32da635991ea3233709b764937425a34893bb89cbea9940fdddea0c006e0692613b8934cc7951c7d08f4d83c9b9a72f05767c788076432f653df342832690cb98ec03d532754710d792bb03fc41f48b48e0f38efa0c2cc33343ea13dbb40e05d44cb7e777fdea8ec6977c3939ee07cf32a389f7f01b4b8d391b0d54712e032bb2abea96152b3e0900ecf977c0532266096360ab566956bbca81492914319582fe7d893d72470c9a31e6093728143ae30392366815f9c43747da68a5f781dabcddceb1594dd636726dd9bf7dfc7353fbcf6039106bcacc930503c598c3e198eeaca833b99cfa000b0e5f70e924f31a4b9b06fd21a4579a5579e599b17eabac0ed9490e6a21511724c39a7e668d062d2742f041e32572d491b719a2b0a0f3f50e7b79bfa4b566941fd1f5efead09283bce3d7a696e2748a4a6a148b8616dd26227a238496a1eb8d5da8116e7a3a9deebcbb3f91ce7e69cb6fe39497ddbd1e73e51e7a3de9cdf5aca1f64b1925ca7b5af52de85bbc8c83cba41fd9bd358488467313b5af30abd9a7a102ebc69ff007c1343fe0b92f0ed219520f942b18101e22280f4a2cb4be20ab801e683b07566b28947461ea255685468d6be8da8bd749ef31e8f72f5d482ab35b0e3e2ed85df5da3c06e21ed149295054cdd983efbdcae5aeb5dd8cd29082eb76e5ace2160cb2ec66af26cf8f57be14f2db909fffa2857f33eb4f49c485d5bcfe8794efcae8d5287542077043597a7c2ac88baf77916b64058364d1883a10b35081ebd75bf589f56df211bbaed5238a52a31a81bf97a70a02241162b3f188c9cc46fd8812bfb38fa212ddc2e7e0f0f3236c3340ebd7f3d22e3c59381c7f283bfc22fa185db96dd0b2a8548560a4463556d8d4347e42e264e03d1dad082416af612f782742340a053443879453449bbc22500c100e07c53e1010c96b7bc310c5407b827c6fd9306c401e8a340c12a816937ad8ded72ce0257aa2123e4cacd16998df3798d3804ab7bff5a8f4c4dacc76fb794cf8a71105833ef35805c7913a50dd78372a61f79d163b7e98e511dbcb5aa347c80b04773be5d1ac2b0fb9b46c496bd01feab63f44c9731b34655a26e81dbb857a323db801872330dd39b064c7a6187f0d6f1a804de549e6336bbc137efa75d29946a678a447ea8f69bd4c0371ab392b77c45114616b5c8d5132ecba0d2b3bd6debb9c60c60dd72cfcdcabc3bd52df44380c692def3003b041224be775e2f25954a8531b8e02f9c6d7a23df1370dfaae590555f7452258ccecbd471ebfd322b0c27e07605573424fc0b3d857e2d4463cc2c398e3549bc89a99f6358ca97f2e9258fd6e8cd8e66dd3c9132220b1dd214dd74b74c6e3299c6547486c8bf75eae3013c60975f547ade016bf628eaa6625736f1816284bca08dbf8deba2e5aa7973bcdaa599510b2afa7a4a09069686a75cfc97490dd159ba6bec4ba574fbcd45a533103c99a632f6baf0d84613bd31fa3d542bd6aafe273d07808721ec4c5a938b5f3b30697e3f979fbcb76cfae5181d66824a83d560cd5c98d4584725bcb2ac53cf01e407f7f646a3f267b0bbb94aeaf791f357854965e3a574b16807ffbc9ee17936e7576dd025cfbc125759864657e6c4d9ac330efd23f308b7112167c3c899ba9d169ba2cf84e395006a872d7b5a628c49529ac1028255b3a7ed3cd2f697a3afa92676182ef59b2222618554f16432116068b7e7dde2a6e1cfa291dbdf8989647868b9b14419049700d8dcdaf867c794387e4dcb1e272f9f49b8251d3e7552417ef0fd5807f7ef38720c5161ba1dd3a187c903f6ea0ae50c7a700d4ab9b3834c9c7aaa7ea78d758df7d8e562d5ac16b0695ae5563a3b6329d41dbb81af17587f00333f6cb27faf3554c300c5a5914930d934b3b5b79341d246f4cf1a65f67405b3df413a7f5bc475473214fc5eeceae7a918a3dc6a53e7b55315d5598d9b0a9479d56aa143516223ee3a052a7c711618af67547680fea3c278432973a37a8fc56e12b695aa1828ef8bdb6be00f89d787558b4e9597546212ecb2a6d72239a9463c93ec5eee27759e1a19be9f2cfaec8e54c625a9777a7f9589a9175293c05cbbcc6ad416412d6586e89d4e9c89e74e28103620a2b087ea3af1cf515077727cfd44ff9fbba9b50672a3e4ad96dc749a6ec8ac7be298f94b90e9942feff41fee050af15ca978a4e7ca7c21b5af31f7da83014af797d4d303b99641783ad43166a2b2446ac234ee4aa30d54fff9f9471862bbdbcf87323492da19a71bb3e82093341be508b92f2924329a33a34910a034c9cc05ab53e55b33dc7425793d8e6631917fecc28d34e1dee20f233b03c69f518f6d27f4f614a661ac04e2e0ef83ca521b17e12a5aca8dca0e0dc3409a5b6c993f037a460443717762d9ab06cbaa0fe07283c0c0b4863226a8f520fce244be3c6a3297d10b9c3fcf696ed231d79f232e10a7183d5cd804c153b68b4189f9de665f3b9c9467647631fe7e3a97ef31e6769a6324056393420bb3c6c13217bf4bd34187238019b6897e3c81a6b47ef7c031a7b7d884717f18d0dda1b99510f5b7586d8562b40cf1a671acdf209469fc303957b8f16a5ab8d95ec968feb227226060fb504bd75254d60a027d23c5f50b11691351b6476c1bb2636a8982ae17ffd594bf1f2bcb7065ae6d5c0ae89268971d84fa5f7dcadd865b813a2528a88a70bdaf347cab3ccd25123c221b5b195c5fba1c53b2cdf57f8c7aae4ce3c313f5563fdf7e42e6ab7c3dcf894614c9aa7fa3cb3addd1636c8aa49a082a7d2033d0b1781ebea0f386899f592a36dff1a1ca453d041f94f64d58ec403d0921a3ffff117a727dfb9f124a67f06aa82575c3acdbf619a409461367fdda167fdf9b753dcf2d567242bc743f7fcdf5ace48065972302fd9b8eb36d9b00f804ce5b8b994b65d72ce2ca8e77de8a174fbbc15be42856d2b3c3c1b0858c71e1f616f0d565e86ba28c3adfb108dd52546ac5a6ab90942daf4000247d8eec984d99bae444c488c2341a1a84e05a9f82cfd6d3d706eaa314d4cc675255c1f873d10b2f1999f3071f3dd384e7003dc12e429f027be1b3eb574edf60f18d8d93f0536071b86d3fc29cfb1c0ed6064a63ca4f76cc5c93a1b7a6817d25ca82dac442bdd0ab4a497e6289968d76c18d3a9da7c909eb8244eaa7bcd3a9c8ad4876ff292007c76f1975721600343ba831f1175ad02929a390eed0fe3ddf3c40b28a3f485d3a2c7d452629542518d9b270f000252d9428f1c593fe22cc984936ab67f36fbb0a775d51f59d01e8b604fb7d582ba41cd7125ac875f14fc8f2384c33f196f8f0a6a4c5b35f1e12aba801021a1052539fa40ca0e8a378c8d5aa9f5ba22628179ecc701d44430e751b0f47890c5295d2913e0dd8f9134aef04b44da6603b4758d1e4da7b87db5d1680756669fd0c4238d73f3ee62a9e2649a5bf68b0b10c468bf968e7700fbb558f5607956ff34161b22898b03e57b6488c75b1f047f5c87752e3a3c8d36cfd2d46940292c1d47953f257c321e912ef897c730cfffcf7dbbdc5711335c8386f280330c7ce23013ef3bb48220cf97d6253e87626ff8e07b74943571d6a973436a47a8a0cd47826779419d8f1fdd4507871fb00d596026c508b3dfd0f8618c63308fca03b76d0378972b127df97e2c892837186822239c6f719036d9e761dc6d99b01cf86446a7d2d474ef085e74254e589065a59805c2e8042c17568a75f7ec3d949ca3369ad779248d45a9fab63c59330b7c6524b734f6354756583c378741ea65c6f661e4f76d5ff8acb47546e6d80af278f2b8ac20dac53ce8fc22588aab7aa7cd6610ca6edec0c5e4f41ff75cb1c58864f5f593c591806a2e98a1376f1ce68fa028e163e4fa13dc4c53977f01a7c2bd10b2601ac6498fe699bc31dfefc3af7aa4f0b145362b95b718bf0b06e57db346899a53097c4f90f8539b20d0483ed48ec2297183787f30c5e08a78ad1eb31ecabc4dd54f289a97ef806249a3e5c0a4ccbaefffde76cd69135309e42bc310ceb1a703a0df8b8776f5bef09d32cc58c4be8e5462234fe51aa2921685b4a8a35cde03a37ea865770a075c79a1175091748e5247b8005aa179f4f4c282c46208f754e32a989603f6cf0c2112143ff5d9c98cd7b8438df162206040dc7fa236a4d7fcee86dd9f2d91adbab205f9f56c028dbff65ddc45464b9aecb1315917fd07aa402cb46ad9260f8a65e2284c6c5cc28d5f0b791a70cc053ed3d288af8377a15d681a5b3d4fdab8f81932b1b2aed73ec4aac5b16b57fd4cb603038f3b1bb1529fa9f303fe71714d74d559a60941f8bdeb5a135449e0ce8ebf17128ce72367f9cbb048245c49b82c696d96eb984072976608961efe098cbd092c465d8ec12da1cc87dc002e7d7109b47a7ae25953ce78ea0fe630371da2154d643487cd183856fcc6f4efcc13e2e3a91ab84491cd7890f465653c61f09f88f91da25dfa443bb7d0d47e326122fa4b2ec5d013fb80e3b4872f9ccf137b8fc6c58c1a023591065f24e4617dcf7b5f3ddfa40b24297c71149473766cc7d30fc04a4bfabf4d4a33ed232cc2e7bc65e16116e9f3cd760c15ee0d571954c3449c5af7dd0dae273e4bd50a7f8e1d3b381f2baeddd3ac8f7e89e9d5fe75d538205cdc08fd3c194c6276c2f488ff739e4878f72445ccb5091a3142f60a59e38665a7a1ceb3f1c7bff0cf047b6f7bdcc66aace8f3bc47e25b0d4f3fc3b8f995fd1aff93079019b7dbd6d03f69620d420c9f541f9e93de3db5d6123e1591dd6d0e4b7c2fe729f1cdf3940e31b8465d2212b419d4efd3c476a3793d1d7eab55bc6a43f7b513f83046f9ab9efad3a403de765b29c99d3e66bcab5924451f023e3e4e0de3850ec04c3f7da647186a72950b9a7c4b84122a9dfd358c64cd06a813988b5be5d71454b8abb8d415d73796bfe873f6f8c65c7bbe9af21b60a72452716d14b67fcbff2fb26dedf31eea88fd09c9b48ce5788e2372249b298a22f39052f19c1f4390de2b49b4dca4df450d84da966fdb39aaafd2fe630555a4e216b4d9000becb8499da87db867665a01564dddbca5d7913668672ca526cd3fcbdf7d76c72e93d7c5ed03d08e3abea7a06f15e83de08244dc4f52ffd39592f631e599612ef83e9f48fe3601e3f5a63d897ebeb92f711ab311bee64a9cb9eb1154f078f580c96331679eba5253eb7180b8dd2d59958952a7163c3ba42c12400593dee7f192b9c27aa559866a070a6af2b3375af3eb08caddcf23351c4c6c9b987bac5e88656fa8c868b1b3b80e8f43174e3809109dd880abed5b63cffdab5927d417f93a3c9095268d72412f662c1c00c9a357e21ede9e2ad2889cbcb6b2a19f8ced00c9cd34a841d5db0d1b9398b5a055f34e22fc9138390782ad01ab2bfd208511bf8a4c6bcded921d00f28db6687a131730720b3865d9215ebdbce37a4bd3c4f0d44b3ab7341dbda301072dac7293d54aa59827a30b719ed3aaba611b31ec4601c3f7bc55ad042bd2ff863eb1374930fdb0120c6f4c7dd722af182493d3ce87968ce58e5cc68abfb691d343b6b4bf926043060b5c16ab458213521b2a88586be3670c7a82ed617782cca56682793be9cc02b48c1a24e6183c0e21f58b3c1a44312231d12ae9ec3ad3c6020728f92818c79e206e7583012053a7650ce57c836bd52cf1c19c2d0882ef102e676c091df51f8f8d8b5aaf680f06b3135236b8bd2ddc77645af084037c5caa4c4c1cc9bd1a9af192964810b4b3cb78df6621b29798737df05d27876452b69e82785b5999ed098ae6dc58ce30c66606ae7e5caea7f65022e7767865f1d27cf816d7fd7b623ff40fdfab4913e614362cda63135fb9124243854ef65281264f23e390e9ff1de2e22448a53362ec4e983c1729c0538ab83373804879e8a3a479ec40e4ee939729bff5ad83730a5773a84a83407d79c403082a69ec826e502585835a442c51737eae9084e17473aab8c52d346b4c6bc5a155fe978d8b2641e6a694782e44aa68fc5b3e94965e41c6673b3f7a97018549b7b018011a136e8ddbd99af677c5798c4f0f8fdaf92c896991b2ef76e36b75a20dfa8236e79b7073fe6343000394822ecd0f39e9e86e4f6902d3508761a283f7c486100f6b065537099e38ff8e8c3bc67cac1fb0279a6b4ed0b31df53c081f79e01d1d874a51c23fe626cf5fa6ca09c6f314aa223393cd842e4a17e64aa0b58a675433c02da79aa18c573e3ae9fb226d81d4958f1ae931537df44227939f017718c0864b8dbfceb76e03b847754cab646ee8dc76985adf5bcc259657c1b27f8b93fdd6dd834a1d877c81d308743a9e080ddc2759201bdc4a4b04626f45ba465a255c10510c954c3768c4bca894a5ed8815e602afac554a4fc859c9d1706b010113188eda3cd9e11d133280746e9dd87d7f845c7fdc96bfbef7852324ea16f472da5affab71943ea61ffe9c05561852b23fafaa1e4febaf206f7fc4f04e27a32e5d6156a73ff423c72949c65963035bfc702b6e2b7ddbce9116cfa79d08f216c5b6901e92a9b8bc138d81f78d7698f2398b426cacc89a3e11a636a0d2e09ba67f4f15d15bdebee2dbda501ec70d37ca5db2dcb11e073ff480e9d50171488995e46e65a014e7ef223925518eca9b4af239deefa98ffcfcf55b6551700c8007c371b23f81bce8f700bf26ef12bde59ef5768fbd66e3acd6bf8646465e5d1fc94fbf81a7fa6a4a8aa128d901f8620231ff89c174289abaa51466f30e4065a6be8c6e15e7d1583e01e78e48683de11416199ee6021e0961d91709a37c9ac4b8d3940b3ff676f83030f2182b135820e3cd3a40c06b89c10a8d622347961cce941b91893a1327308f5b6ca2760012a3957901336e6a4a853eabc3498c17b09f4e09e8bd98e297cd9279162da51608552f2d2162d1fcc022daaa4f4adda9ed8bc8f47e2edfd6e2fca90bc9ab5d23377f3d0ac40e4846cf9d889d5289644cd94e6ae23216cf59f29a8e2a2e07fd24de6b6514cd97750539a960b712e93cfb33cccf7f45860a9308e7659a90578960d690f6a5256c01567f6e7e31961f6ce79837e35fa23101544b9d40a7ea2e88bca180c33add2fdda685f89ad7fc18f558768ebd97192064b9d54b5e7da1b256064b9df8304356c10d62251ac95cf5ee30118d7dc694009409472b1e7598a59cea89395513e97032c2062bfa38979464a51f18ed961ce5b52f307455f75c0b5ae5c8a0d813c5ca7adbbca9bc5bc13ae573b9bcf5eedccb32703c56b0e889742cd06114987a37fad8de4f6782c14a7976e23e36410af7d84c4c42e156da96c5f53eab8782e4f5a87f45de36d579c27ef434028ceb9ab23036023744abbe1fc2108c1eb7f0e08d32e1b0b5e9b99e2b94874d7c213e883ef5e67daa17a821af25e564fefd75f9003d390492193b734548edb4841ed27221129da0dfd343e9d3b525ee37768da966d74549908f6fb78851a26fe594839f1224ba87ab3ca7d500b7278fffa2aa18dba8972d4878385d57f480d6dcd4fc5ecee98fab82c48eb0fdabbd1ca8175f5e118500403b5ac7b3c3ff1ae04cb81eb196286194321243a4063b25ac96d3990060a8582309a389c8f8272ca13ecb6d7358f4e1ac96501cad925261342e86b5464ed9e9d7b817f3b8005e382603e59648a83aea694e22a6122a0ede7441a0ceaeebf0d86785119debacba6bf634cb2bee174c8f85f45bfc5b35fe7d961d8ad4eb007037d4b4c83bb8cd1aa9fc19e718c075f74d0622d9c4c9e0bb5d0c2a459c4ebdf787d40b81e0a1e216e71db8de8707cbebee401d8f9d722f45bdc8c081b1ae91929a54ad99a88bbc4d2cdb31d38bfff7aee2b9f396ce16b8721cf95ddd73aedd64e0db0fb29c5427659fbe29aa1fb05bc41044f33dd369a9bbb7a4b134f1e8876096371f8031c089e02f788d74bab043517e921a8b123f8cad3d20d0ee575f38eccb05ddee368c46b87cc6a0eec07be4244078338d308efa1ea34bd9de0341efe9ea4e4ff5bcc7edfb37dfef540389ad557d3e03318d716d8f7ed9fb7074e1c601c21a78b48df32b941f6dd000eeeacf88488da287980380e9b61df9d3b6f483874156ac90d2601ef3b8d0cb6a877970918c185ade6eda0b984dc74600290f10af74a1619f891bf40c0a7abdd04c279b0223a34caee25fe0de0229e5ef37150c2e877a468b11deb816f001b849ad021a635947c48f761d320c090ca8aa72e938f028043b4d32de615f1472040e87cd29e428376f2316831d1be6f862b2598e0cde0f1298906248325b661177208240df3bc911387cd495ed4c1f0b881f929f65775f6e3c83b0000815be116ea553c60ce9029a9278a1047090c5ad12c9cfd5722278ea8bf969de478bcb5a0095bf39a477fe55e3f7b34cec6d72653f5ec7080cd1d023dc883a701149df4ab1f1307ea889eef64a654f696258795a55420a7e43e16c0171aea9a117706de0c960cb061760843bc8ca372aefa490513d15a0c7c7deef70fadb405ecba1b2c3c1d1d3c5ca0e7e0a92fbab6fdd537d87d368f4087146022fce61fd263e61f8779b281fd25c5dc55da20990c6b3137f57156fa372c83e3c0aafcddd17d4a8e3cfc233bcd3b2db43cc3970102e6805ee0652355105275ac3c0eba26f259a683b3283b805b9b8cb3b1c2d8b3a42bb9d5465b8dbee180badd26c515e77053a36d21440c011ba0931fa89bd3052698ebbe4bee39feb707d4ccd1c88367e7b6a122bc3da71b8902d29989d6deecdd6ab1cddc32f661f3e831c5fdeb30f49241c4af2c0dc46205469eccbef67557500cdb4efd0e097e914d7ecab567b5b3ef409d87b88b6dc9881225c0cfa3069bf7e620e24e099e1eb2fcff3acaae477af9affef4f499cf5eb7cf7f7ab24c663863303e8e8504b4e11292602c7a4dd8340d93c3cf9b7ce9dc753e9d8a1ed92fc38a56a2bd45340baf701e38c2c5fdfdec30f5071103a77efaf0faac08cbd471c0db334bdd134fd90932dc59f3afd4bfa4ebf4d6bfed32fd767ea97e132f4e4baa87dc2df5692442e07b52cbef9a73d11c7ce8226fa8992784591c431be580c83ed240c214662265fc28c4baaa11ada9944da37b68e13a760bd6f33d6a3d0cea7e1061c66490f37c4a26d8b6c391716dd847952a42a4e6ec2b68eeeeda913080f29c18f703aa378f1f824afeb95059fbec00d785bd9737e5f08ea06cd593cfe8b434dcd1b7dce08cf8aca495002f0404450ea1a8779d177766802058f5543bd0865bb43f47d936eac89d41f605b364b17607852cc9a83f2cc12aa2ab7a92725dc776637eac5992c6eb9c42579da095d7b2dbb750747ef417851ca5133bb72c80bfb878e2a3e39af9e8d62c6be202d00592b18c44806c13b944cd7cb14fe1d450d1be0bb309e271e4446ad37a6e03d35616cb7bbfcea08347c10233b47abdd23066fa0dff50d8cc34ba55c71a7ac4b45d62443a82b68f7b10161e9b52bfb1f7b1298cae63158ba8d817f1e105418f04f4a4d833a39b911ac5292fd4a09f502e7fa596397a6b395750ed46658f2272e3be9dc4467cc74fe6937c8dbaa6c5e1d714ed0ee4e209b99c0d93177ff80c016f0ea2be70d4011139764187267de6b973fad135865c07f41741dbae9a097870bc8bb2b3e718de3831dc803d68c5f7797cc3213c406a6a2bb1d673ebb1ac02f25146920e266edf6365d6362fb1d11b7ad280cab3135d945f125341754caa05544350c89674058c229ceef75eab7978fa786710cb44fc127736179008e03705254e80acc01986490732437593607dadaf25a93a661da2b2158ce68e07279c677eeedb68bdfb7789c416c113eb9581c0220f1706d4532bea0e0151a327ef352cd573930a85af2635f4ffac82f8af9cba07bf27d60e5e9634db266237c82333231f1657f2153a8a7c6e3b417ea8c758f8eaf05b96588d1d3f4551aee0a6ec9e95151109e7029d3be7ce0ca31345564cb3d369f5ffc140af23f9e7b9a70b13d619f9e9ca98e8521d2a2128a06bfe8e11195291eb0a01ee42adf94f98674cdf46299ca86885d89c652be99092c72fc6de1d746f1e459d2e1e1176c6f7d9e8e7099edafe562a215b9e17aa7dcd34963ce20b1091b0ec5b5a41e7c2dae3c03a1ae3447f52c2439222ae764b26b42007c9f22d80b8772ffa437523147a2dd14d249a396e28ecd35023a53c8fe08db0b1a29b126b3b52b9f584385992666bb12129054a2a2a4cc62f55a13bc6f248337659f7d8c2dc8ae603453e851ecfc29c57bad68ab0d22b16dc22f28291d67c5c70cd1f1c398b3fd5e591b4dba953b631d8d8c3fcb43d201f6953919d55eb7904b4fa506410824921c43e23ae9b564c7934220cd6220523e5ba1f624518cf0289d8c6892587b1eea9d314c8f861f4d549fe35fb7988cb6d8a63c3e16b9bfe7fb807fa9456075a2bf37d351f73f4f4de6d9e67baa79bbe24982517e3fe936b3e607de35ffe98f827209421e31aad2eb5b2f9f5a5efaa97300906f1610b2ab2a18544443d4a965d9e69eeafdc5db36b1c59b1b2c2b7d5aafa7126d6a7cd8367b9bdc23d6299cff4c26ad0ff504f0175844687c0a71620d5bae28ca63382cd631e3be0087f1bc7bdf3f610b9e9dd375f1ec204c4b845710d854b58e66b455ecca7544afb86cdd1cd492e5784bd4102f24596196397e683f7d21ab018066f5e2c4d0ce654bba84eb7729b6c29076e9b5a900892220fba6b722de7ae6ffa3f4c1e0751f70fac51dd58f60b21f4e188bb25cd9e027951ef2d796e015579f444b89384644b2383ebcc5a418f7700fa751d5806a833d4d641c0f0075e9c4bc828d31923e26cb36103a2afbbe967743b880e8f88eb4536e9fd9882b63f6835469ed0656ab86fc335f86298ffce793f429fe92df6c3d10b8cf595c15aa78d050bbf685268fc1b33a939ce3289bd0bac499530c3382b742482be7544881e8069e8b9301621e4ea33bb1c25fffda97bb516b9985d4129a6ba7d3a377a326ada8dd09ca6a55f88ef188cfc5ce652d3d831f741c4397cce1d9f76c00b43db59de68e53031bc088b2ff34fef2facb1575736cbfb74eb832dcd03118768e4dc0a5e0ffe2fc6d55421598b2840d79fd304a638c531371dd2d8ad8e674528c0eaeae678561881924e6a4b9cbc74374a5b6e5fa4dee4c3c4af5b8bad8acdb263696a91ce5d534bea8c523697cc8923c885d1bec45c69c87eac855097848792f327e95164ff4fde3f13097d0bf018aa382dcc2c344ae5a537155d2ee1f9e15507af3700e281159f16c785571f5be05b99c47451803de32a942799547a071435de616b7779e2ed7bdbd8209dd91c0779c002ab24061610795290cf18a390f839239e3f6dd8291d621e3c356b554e3fff36048ce7780a1eb5ac2dbefa6af625b80accc3e45b4ae370f50aaea458dee878c01b13316091f4d51daaeb99f50f3206d09fa98d82b91ed44e2f6c3edd13dbd29f600584887ae71a0479eb0d5b3f8af98b4a907ad742dff3386a9ef162e72bf3cee2f00e71fb6634ce7df1c3001d3f72eee0368621d23c7b858dbbf9847efcd71c7d18b970e3b4f2d47a7985914e523480974baf4301c47bdf19d8de8151dc4a85e26fe024c07fce863478a02c61eed7ffff865b89c3ce478194ee90a77cd01ec201334e9f81c77bb2cac8394ff8870dd5296d8999f94155176bff8a5b965eb34ae40cc4a5c1819c48fa612f3640f7fcff5fa1e6ea30a49ed5dbb8502e50d5c504788b2b60b2398bd23b3759d922e4e98bc9edb304116bcc10ab7a3fef6828ade783c3601efce59bd3311945bdfb502a31ffb8fd932d1bafa0f5de377048d86e29eeec4391e1aa6ce2e9db037bc41ffd8d1e897078c8f9f182a9021429a36fe62a9c9ba18d4226b536ae49f301489ba75fa3f58e1a205c39160a4464a315067b0a52e721e0aedaffc4b6c4a8228aaf1a539d98173d164fe142d3813b875db9e70e03c4f2181ce66481972d1414cdfa09c82bb43efc536ab6fdd21b80501a08c4bbed74584ac41de5f2d6067d35ef959f995eaecd3e5ac69ee77eeae20ce5dd3cfe0853592947c440605c9ef0b9acd7973878baba569de3582d83a33136e8e8e1fd9cb24f2e5bfd802e4cfd23ad807fcf7c63a51d7526f95e91463ffb18e005a387adc60a13ef5778ba34ec8925e8ed3ffbe310d1b4461b351b4c141d14d1222db2c253b03b190cb23a04217f156c4e647432009a28e3775679fd74ced55bec35723ff04c50f6d692c170e54f76e716dc1fe4aad5789894277a1df2636b1bbebad92dc23b61362155167a381de70de8707d1b50fbdb4aced8406f44d4033c520199157346ed71326d76b30188f143819947488d738cceccc487a6d1e038a7d86565bccc8edf7b6b3938ed15f80d735b5e8007e6c44d966ee237240dc21a5197eaa44d92ff72b03eba0b6389d6539ff9499a0ccfafc2551e29b0fdf4705c4078f1e268d5966d75f95fe2e0456f1fba49aac440a681ccc883a55e5c19ef34871784db70a5560a54298e76e7b06745cda8863b3f676c15bb6613a7c915c2034af3fb09296624f774c04ef722e02f3ec8a2364d0d9fb20567a4f61d9335282821110be8936b829f53d2e6e3508300fe28a3644caa3224f47339b9e7f31f36fbfbd4c3a7be00e3d32f9ee6fc32836da0041918add57bdcf05e4e8080610d70e2a1e8a20a802c03ad910f099678e745e525ee7bf1540dba755454b6597c9d6a75a90a02c2bb327ee7ed4ff3b7aaefbe50e9157259e7bd77d7df8b4be0b171f7e60fcc9d81cbd6072d22c592c988749a2daea53f98a3dd8f7dde95e4c755d7a99f5c84a05fc923511e5393c06ff2dac0bbf237bc4e4328c029e0d93efe6565df99ed23e2f082c4586539c2ec5c5ec4d4f0492744dbd3714d30bd45b6b3334917ece6a30c80b545ef35931d1e7fc3b4fc26a27eed764bbb65b2c62dce814e3f2614d2a875c9affb8ff76397ed06720e2accdb3baf244928eb1a589db95d5ea94b24e6f1999e19ae6377dc9199af089e69c0e9489a59efaf617c20505c6e8fc9ee03a6ed90c9e4e8628cf7be9811411afaef14a58f02a262b550e492a45b1823ee4e8cb1df808caf822fc97ab434070dd1d0b12f344dd01789394745eb40302107efa54886c39998cdc599429dff7491a2a993f2707cf24bc4cb81a1220fa83eb75cfff8cc0673db93141c58cfc986877a913b7f988f17f9563ae9913b4ce33f4c74eca3135f417bf9f436e84962e36cc0c509fd5ef7fde12e62b35e66dc4dac93ce56a1488e21a0b89886f87b8a778f6065a94067eed84e65719f9567614bc13da7b5f5b9eeb386f90d214092500167ee0ec7e8b7d719cf29ec547a130ce3ff94202eb77e11866699b0d7d9399b1d571938528b182b160cf85d798b9df06270bc24f691ed4738c94b68fc3b1fafa5790e5358397bf086ae51aee074905607ee78ccb53835edb97037cdb4a9f7241de0eb293d1610e1d1b55b00782b5b7befef625e7f10ef7cf2ae87681e80472df3295ab0675253a3c8f36b1bb22bfd8d7b04acaf76bb20e38ec6dc779cf5491ee0c2c1263ecc247ee16c69fc1908a646e369c40b4ebf12b258006b6156e564a2279d11f08f704ea837d944b7d42350bb35109f0fd681698697682733dbc8e205e5e49fb12f2bf7de4c6dc238a7c45d76a9c9c517b07c4c8e20b4ab6b1567a55e10525b69c36dcde8fca80c734cfcb8e8eef67d13ab4885d5b53c89584effcd40d3f4a20fb5c2c42dca73d60a56f2f278b87d8cf0eca31df28a0414728f38df8039cab41d2cd2471de220d8815a4cae114d759621ca99b7877c354ec44e1fa87f81defeb2f5002a684a8957369dcb7ed84d0e6e8d81a79f5969f7974998212cec03dd6b6b2bfe508d6a860e8740ed7f5dfb51fa4d0ea63bab8c2d12ca465a1885e8212225f6125c65cf826c6033c052ef9240db05cc9fd7266902412e1a456d948117611e43761b880c1be5e54186ced75a863a01ca7c0983d082e1bf4010f00ed952923b8d3a07b0112b3fb27afbd520bf53c78bc664b86bdba554390bf3a29683cd3f316869d8e0812b78c0dead748e92f43e2ed1127d4dcfefc9c3eb3539dfa4d18340bcf6268423c2bc2757cbf30ba37a8db62bca57866a6354e810a6fb850d65d14ef2c9c9ef3e45d36c79d31872338dc56b0febaf48e083c3fe7ebac7786db2500640acf0546c4a4b022e2e7f53705324d2d7ddd2ab03ea4133507c192938deac4b9278199a404e3e34ac35609f9338833e934dc31896799447d88032f43871a2ff4f040292bb874004a8ac36278b1eecdfde8f0f7fb0dea7fe8637e9470bbad00384baa9f87ef02ce1355e89546107598d78bf60b23fa9bd5a1335c2ac7abf39a8309fffebc52dbc77fe3ac0be30aca9cc4c063c4d1ce27d4d32be430c4c922db60d6a8c7641458229f58613fb109d803e0c2560b6828d00aea5fc3de827725607a14f1d055dd251131914b23fca8826cfccc64929ac81658f8e0570aff2335d6c43f77264e0820e8053516d17343a26173f8a14e3fe87a26a1a18d395aeee80b9971828022d97773a500e3c0c53b4734a47dddec72b6f180d4967bc473a3dd37e0396b8ffb1a53f8606b195bae7661401a5a881a743cf8ef59a075637327c97891a1a17d3774c6b75f616ff3c6f803e63c3fd5ccf4573361a3bf9df9d1bbecc730b90f82048fd96079131f5ddea17c21db6e7f3b7b436f815aa2dcfcc8012e99f656681ce33fead2dfd914ec390429940ddc71c0326385d0f955f9826eaf72de18f2a3f9a8a6493df9d868f437c5fd39141b6e9398bbe0890fcd799319c8de0c5b2a377cb1dc860526ffb60469624c0dad187e3affd2b43fc06ab59fd58b511d1d55b97dc212c7ae59b5a1562310b372992a2fbd9581afaf7c915fe5da55ec136698f5e395466512deca25ac1fafeb37792e5e2defc5b975ef7bf370fba64661f4b43dd260b5573cd62e1854d587106157858cf90423c529e997309da2285540d2cb01d7b5b883b9d7c6a73c14c39d78647f957e33fbacdd5a8edfcc7d3f36e3269c8fce1f0aa2c702506c21c059e80207f40f99abb320ccdb75ffc509f34763fbc1d6a04e524e68dbd0471fcc488ee8d2216ef1feaf4321f0cdd08c705df2bc6beb16cdc1d2571ff45f17b143c859c832076a4bb5705042a54eebbc5d3eac00c5b063c8c97c4f7871edd7512756806a59761fadf09d0dc2325abfb147719ab31e942c8e6115ca0ec4ccbfced57c82f08103e4130b5198722743c11b5a658a21a768a21e6508e4b9d1627a3de02315ad3364dfe8f0a0ea3b5d2ae1a5d87d6dd0d9101b1c553ab2888826c0bccde7535efa7d7756411da3a12ff0ed1e5aebc8b2261b3b470d6c5e900a2c084865b4c8bd7aed517f80e76bf08b975c41b124fcfc6e86c21ebcfd270da0103616874a8a9d406f5e036c939eedcf817ef31eb361863c5f8de0e42be05e4585b563787055bd299bf31a193701c18d1a0eb9301f14dcda9ed33e2613a98b31be60da8b9ad9042b59ddc1c1c41418724eb5ac9ddde86d9ef2534aaef0f5a1ddb77a3bea87d949cacc971c542f5698ae1b57f3127e784227ad879273a99aae8399ba50950f579227b2ec2f5c23197645955eaa3cbb104fa80cc09c38314c6f783c8c22417551407bafb60ae35d4abdbdbe6b32ee48695ed8ac46e9aee54d17a507ad83df2c53ac0f7d4163445b4819e40fb923540000516bd211e5ca55ff262fa51d635dcc23c27fdb4c7014e4e06dfd5d3d9218dd2c3d7e91e00dd1e5204a203055c279835905eaa87aceb3966a0f8e65227e70a77368afffa441e7e5fe50e9d2da2b5f010e6cee3f52c40c9d16b0dcc09cf2941fb71b084f588c84d58a4b19ac42bce31223e3ac8f395ecdc6ec821f727fec50118820f3e860c1dc5c783139589e8cc3e00d7f74043c3327308bf2e1afdafdcfdd0ca34a76eff71583c6994e6ff82d504b914a6a69f09ef6b94112f142c6afb6375ab6dbb8e36589e5f0a779cd72d5df7bb12d15165e6e88fd9f1f6efa0abacd8df27ae021561accac2ab45f624a248386c4cccf6d951d802ff96d0835421a36b05407ea9fea1de994d4550078ba3d0c044190c87056160a98b14f87b298b71e30bf9d378f0e9b6581afe3dba208b9da52f1525b83b8cbe8d532e6e5af41d6bfc471e8b7ea413ae676b7da23af550b9d07504342fbc5e6b639e7cc97587ed920647b222a5b82a16c90134b4e51bea6c8691df182afbc613f3e269fc053e30c350469f9586e72a976deb817bd605df989bd28195ecfe243a2433cb85aff7bceed38284a47a8d153e5d2eccef5f101524aae564f8609dd9c9333416cb377632473814398d54c48802de72f5b74c9aab19e8bc09fe89bbf17a2566361c7b4c6112767697bacb084fd7b2437fd0309e5ead51fba5e833de354e72e0e1226841c000ffea771160335ca52412012ecdb608a4b124503b8a037843bd05f71398fb965e1e9ed0dcf030b8dd67abfbff3c622162c97d36d93e2898f49f8dbbd37673317367bd1e32f7b20c55e51596b2929f6c1338511f5e149c93db7bfba9a6c8bc0c0fabe08582a7474a74e52eb7f2d2eb93b22358e4797b40c27400b354efe4fba63db6d531f298aba6f9b6e55a8405933d2d18f08edffa25e83e28b08bef93002c94fc28172002ca6fbcea451a864a243bfe5d940591e24b5754a8759620ca3aad6d4afd88c87721e2cc8a97d1289fe38a89a44d0062c03b3b15eaffb5d09d869cb8d19a60ffab6024195ee84caca0686373c32345dc1a04fc23318133f55284d5d95299231b450daa6a950d4c9543b4298279624ae51aecc3428ff8fbcd4e3983af02d1806d7a9ab2c4dca3e4b0db55926da73b4533f4145ffbeb04cfb1f9f8ee5cc24370eeb04fdda5a29393b2300c3e1dbb7a6917729f4eddf10f7674e4ef040114b19ead03ee1f124d19c3da030979a2d7aca08c526df30719feddf121b573dd23d2880db8b9b0d409e8c32ec89e564e19aba9bbd5553f6d9152679e4b4e3956c6b588561527d45a35391a7868d4edc2c6fd546e214347a896da2ae1dd16487e40789abe3ad2861f4b37c98e7275820756da386b0bb23abea32283faf3da7ffe06a71b365c0abf781f814e0dfdedcadbc5951a000f3b151c01af04ea12d4377351761df7759a03c46bf19023598e62696900a29cd6b26dc51a575f3fac6430a24f96de4cfe48416612df77dde00020bc485dd38892339c87628eddc5bad472ff3fd25da31fa5414d12710f72db9afacc448ca3cba05f89cf9015b14d3351719cb6db51063a22b49c9aa5244e8f209af9bff2884ac508605d0c7e3b112d7192ffba5b073b4568b97d731522683306701eaa0be0eab7fe313769d4a5cc6c40a0a62206af1e8b2c7de4d2e997f8fbf3487431f0bdeab302a9df0721d0784a2b9c905010c80409c3517d785ba1eed50217fb21b65b6b911b40c7846c2dd23072aea7c2bf13c2cc955d5f91c0ccc0d3cbce56c688d4223ee7ec9800de00474f110b4e38c617bdbae4431e6700331c25089cdd4b34c3cae8e424ddb87588f31a27ca36ff913d809b317e642ee5ca2fc3122777003d3bb11a851171bc1a4e123df2f813b1c84912ae89bfc5cd3796827b805e5644c9ed323dee8aa123b6c415f71b40775d175744ff57ab4ba300c45c54b45e960f6c900ab5572e4751803e395242acbabb8de239f58bb82d13fa04272b0e80fc84d8a3ea6e4fdd6177d1b1f27828472673dd2fda6183bd59c2f4c60674348e5d840a0b36cc6097ee3b4fbc237948b6edb4d0cae735741e50fad031266ba30e68d4c2661feb8744c2827ab297302ac709cf0c28fbe6b07550fbdaf9f70ebb2c7eab754f9b16ef86588bee8e53cdd6fe3154f720dc34240cc68f017da6af0af99bd51c1b9b6977c681a71ef47fdfe0208c52340b9e400387708e570cba31ad368001edfbd3c8f817cd67b1d158115bbb9800f0d450352ba0c21df5819d9f2bdf67ec8bbd9f300e3729d7e11005fa9e4bc0c6df2123f4695da1bb2f84cc28d861439b9d74d24551b1142e029560a9ffec296305e9c8b4d0ae9527c4bb02bfe8d63c1d29539a28dbecd283ee27f4c1830e7712b09e29e92402a29891057e10e7d824e733f849006d191086b071bda629a6835133c363e51087ad6262a22057de8c2403ae22c5270e3410b8a44907fcffffffffffffff5f3e10bb37f8660fad4dcb94a47c02f587fe533dbaf5c330a524a54c49c21257b8637d33b388042b10ed0cef0de7a4f58518328649e5d0ac6ba79f6b1ff48d2c0e8d627732a9b3aeab0987a6515a5da78e9b7e6f68789731cdcbfff5b3dcd0b46d8f16f9ae4f7fdc8666b3b713f639efd15d6c688e17229ee77f4e676b68b21be7b7677f54436e3534da525d75ab22a6f46968d0fd4129b53dcd9a8ed1d0ac1f237ae4bfc4c56768f8284776d4aaf6a8ba6668bc4fb5f6ed67b1b696a171961821568ab5d915191a3f9f4793fe1d2746636814a9b4a8f152ca9b1f1543e349f5cf3acbba216a6168ba35ef86e90de92a303448a1f31d6debf4507ea1c1c68cd1f35254ac5079a1c1fed4989ff61666eb42f39ceeb07de16a55ce85266926cbb63a2d5fcb690bcd2b9456bac6527975d342b3faf4a87b6fbe1a4a280b0d2a4e6d5743a5c8dbb0d0a0456655ccd921a37585469536e6ed126ffbb542d38abe7142c5985061151a96f4507a65aad8d9935468122b578dad86ccf4fc141a3ddc0ccfbb426fee526852fac6e37b8e324b2b0a8d42ab699d55efab5a43a1e9948dce41e85061524f683a35f59d74d4add5894e68d02995fb8d13526c75131ae4f94b35b448f33c33a16988e997f3d3d3265d42b39cbdcfb75247f4a984a67375aa37dac693f092d074f3a5ef8749e52324346d7d5bbaeb8becd08fd0b8f7ead23d2c31646c84e6b7192bbc7e45be2d42a3fafcda72543e97784ef4543ec584fd1c267cc9c464af071c266784418486e162a74e1fb45a3afe101a66e66871a3f4bcd2a7ca0bfaf1230d855b893084d01c5eb7166addd62fe43bd1fb677131b97b20417ca0bb44109acdd44d6d3bcdb871eb13bd39e458d5092034a8499b335c452855fa13bd39e43069fd2926e8dc7041c29047fca0b955dbc952ee62ebda7cd0b86a85baab19feb6ef1e342afdb4839db859779707cdc9db3e4cb7fda83a1aa4e16baa2b7f118f37ab945679c1a930705147c3ce3a4d3fe838f72c3ed1fb38fcd30a1dcda3e25dcf0e52ed7cfa895e1c2a2872b03858fec7a11c73348edcefb85d1953e7d6899e6a2f0d4a299d47bfac192b5b277a29fd2e2d2672a4bc2c1f6a94c4a182c2efd2a095ada1a764a99af9b73469bd4a7e86a99dc5ed2c0d1e3edafcf4ba1af31d34e76053eecfac3ab55c401a3fe4868829efb62979a5c13f46d610d1d9e4532acd9f6d4b7da9d6e76df9a3e1cb94eaa447b848179ee8c5a162a2eee3f0a65f0d374a7894b8a0844789cbcb1a990ccbcbc1d23f5a8f4c269361c1397c5f5141e92ec40b29cd31c4aacd6eb1e34a884d6eb5204773147a95941bcb4fb516ab1e70982c1d34ec10be42c6d8527ed03922a034efbbbe3b151e75cc53550f384c7270d2ac4c7aab09193b7acb4c0609713497a7b1e653aabefa596b14638b353badde522df5cda1857850ab6d6afd2d6fd4ba56a921aec34c9759b37bf2ac4bbf648d1d546335b8fcccf3b0272be76b3ede83499357a944872a39f64a67cccf05396810375c2de5b2df41c489c960d2a474c40e626bf162e8bf9f05ec051c348dddba162a3cd538d739c5851b34c9113b5fb3be45ebaca791c9647ac061d26cd09c57cde8d4f54a86d41b6ad0dcba54eb7b97fe4a9ee068fe1fe531edc496394a3468be61638eab31365d95de6896e61d5fcc12a35d3c8383c8981062b42a116eca6cdd78215ed5f6122a72a3694d214da98eca4b8f0e083268944fb55cec2b353e0a65322db8269748f0d1ac5aec572b6b87d4728d41f3c708d77b3bcda3f2da68d23974be1df59cf4e8c3a059be536ebea6b2d7616c34edb0234fea0f29d3532f4008f1494cbe5b74a9993a427ebf183adcdca349baffaa5b4b4b9dbdd63f0e49b046c3922646b752f2b49cd721821acd294a4ab31d66c99a638b2ec1050d2a52281b35ec764cd76d115ad0a84b8b147762bde69f5685904683cce7305e7f0b175b0bf22ef0010b1a64c6ee7ba58b5d3a27ad4912d068d2372166878cd399e2cc690f384cfc8c86d1a976aa354c8fcab369b657d72663c4e5f93ed05871514923c3c272067b2031c8644a7ca091c9b09f2393d1bc1e709884f1861534298f2fa4eaab7dd6f1440f4810172ef181464989a797921e252b5f4a7c94acf452d2569a4a269302e4074a6a4b0a901f2840e0d1bc36d6eb38516adb963ad10bf272dcb5047939f02e0566347ecc91f3428c7d6bfb6534673184be51395377524ff4e6d0121f68949c51e2438d921637f492c9b91e7098ac4005cd72e6e9bd787e7bef6990824635e26b42edba31c49c062868146aabe4464839edd113340899b96a3ecda9d55d0d1334ce487136f3d13eacad12b02274d025aea653874a6de6c71c292999668f72bedd5f88e954b98004cd61aa7b7dba23feb65ac6f1b27e05994c467bc061120737cec8fae0f274682de4d656588d6d702554c7944a98e998e42975ece748439b6eed5222ea46da8b77a26782a75c7eb454aab9568a8c1953aec74b4ff45c585605a951a7197fd9d23c69bd3ad133d1450d6adddba971267688b113bd3dcde6aca4946e3baa9d6c9e277a74acb0c471880d424b1d645785da7aa5277a2c2e2dd72c6cda586b5bd91a259fe8b9fc682c83d3286632bf624d193ada133d576993153f541a8ca0f17e74b835f1ec33d64ef44e5a5e80a8324a7894f828f1a146097b37e8e836f834f580c3448d4cb3dc7c3ad3fbef3686277a2c7dc24ccebba0b0cc209371f9d13299675961337079f96172520f384c66208246ad27e594582dd5523b27ba2f2567b45152e2438d1213151445161720f92c2b0cf580c3e4042168d2f95ea9d7d97c95923ad1f357014173b2b1a69c9fbcdb3674a2d7e2ff2d262e1f47ba121f68b474c9c19792672c2f2b31682393f9b1e22acd92e9121f6a949820ee018749184af88049cb0b8b8949043ce06fa2127ea0032cedc2010e40600311d0801cac574acaca074a2290810a6000021720a30316c04005244081091c0624d05c5e2270267f6262c25a507e4c00024851e00175acd0e1520107a884643460f14cf0e5e89465d227643020cf0238a00074595919c3fb593a90801020200e0e1ce05a3e608053000f10800303c00c08c0440301c01f2bcce4594c4c4e0e951008fb1605c00973e965d201314c4c32b0a38efe61019233594931894305258e3856522030f2a3022267f22ccd8465c5a52710424618676262e252c763003301474000118108e37fb4ca080410710b020c00a3884f88a20ccc15ac50052a4c410a5180c21398b0042524010947304211883004210401083ff0410f7810848e395e5c5a5876006445e547484a881c3a58980424420a980424c217221051c200308ae08426cac0842015128000068309a3092050050613878a4bb798a4740b4ba6452402180c060022101803043618c3181f6884c803441c60d2000f78a1640c0f0c600c0d58610c0de4608c1233f060c9988c9101398c91812f605a3c0d44ab6eada4982182803130c08003881820e4162205b8000bc620c30bfe03f06f09a25206b27ccb0bcb037989c10ff646082b4404908232839494323cc414188c4800fc5f9092520606230280160c46440c959786c188ec7094345af12ecfc2cac06044483e65a5872d648146302b22a22e48b3bc42240483c10cd22c2c65b4603021c100060683c159fca215184cc82b7c11031f2d28c060302fc08c118143377eb03af25ddee553529a8a0c52525ecef894d52b3d3c658505cf039418d4d16bb4342bc06042780164854545063f503a85c7d7d16bb4fc9fb1d2a35959f101a4597cb0075247f33fba471d4d4aff0a567ac8c156d458e9e1af22834f59bdd2a314bfd2236417981053e80283e969a020958b202f070613828b8341de0521b7b045b3bc0b52d0f01494131e4152545e80c184d4c285a581603021b4701496971436309890593c435971a385e5d78f5f4365e50c97967e355cca5801b2c2c2864a9ff8789695353c0d1459b0bc1c282a2b6b603021a6c03429cd50544ed8fbf83950e27835521a85a1f84b0b7410c40c0c26e415184c48296281c1c8c1b272461033fa47b7bcb8c055dea5e567d0c60c18ca8f97e659564e5a9a870aca0a734131038309810506830979852b78fc606fa084b42225e5a5070613c20a0c266415184c882a5211820a7f1f798a10537c4003648ca100068ca180048ca1000260424a410a0ca6e5599e05f706be916fa037d21bea0d7d63dfc0394840821fac8e16cc394840029cc35778ac20bd067b3750b0d254d230017b37585654507efc8f5641c18f1595f71f2b2a2d2d2d9e823203149880bd1b2871a8bc9ca1f2d25cddbab0341b417cc50c179606e26904f115335080024c1360cac0a5456585071921d817132cdecbbe047939907a7114cc97311090301b300303042514001384039cbc220c5884c18930fc10c61f0c6088114e2c0183c1a48119c3006f84840cc2022948c2180590420000b082310ab080304276817e410097428c31002708c20061008317bce084246092b004231ce1082c287029000683b2460a5e05858c1220191a2043e5530e1927b800192a9f9202c0461961941146026c71025cc4c103180ce60d180083195b90c20730184c14306c60dc484104c610a311984f590bc5fb59907a19438c10cc61420213202d63ec48c5183b3631c60e4578800c1284800c12fc0f141793f62c1df041c70a8f967d61ef460b0a90325a5080741c2a29407ea0903c620c92c5a081823146ece0873146d068795f511943e4102d3e5c3e06638808608c10160c17b060863854509460843076f02c738c817104a60c7900f22e2d72b4904186fb78202f58e911c6ef8065a58520000108400004a0d26604716141f9973212100c04860400c305f30b1f8cc117bdc06034d0035fb00083c194a429f4c20c2642f0410f78e10b97766909c14322789172070ca62501bb600383c18cc10e25ba30030653872edec0605672e1065d040043860a0a1975b84a0b4b026ce18404d8e28d04d4c20b188c4ab842878f97a353cc48e98fc1b3b0d12cdf2c4d4a29a594104208218410cacccccccc4444444444bcbbbbbbbb3b78f0e0c183070f1e3c789099999999797777777757555555555529a594524aa994524a29a594104208218410cacccccccc4444444444bcbbbbbb3be79c73ce39e798999999997777777777555555555595524a29a5944a29a594524a092184104208a1cccccccc4c44444444c4bbbbbbbb3bc7ab2aa1c45be3471a181a24601566c06030cdf223602832586179388298b1d2e35156e0292f2b33a06325e5cd78977e161ecf8315963342cac0f2272e6d060613428736567ab484cc01830991030613120795151419b411a4d58043c81b30188c1bbac559d3a0b8a4bcacc1a38565a5474bcbb7a15206cb0b632b2a65a0cc81b282567921e359fa517cb974cbcb22e30c7f153a1a480b1929fd2d2a2f64a8b407e85869f96f413a880b4a0aca6a95349ee5c5e5a5b1bcb82c324e5c585016194d864a46a5fb57a790d1d2749491697997159512c481d24246081bd680c1a4f4cb0e48210b0c26440d184c481a309810346012400a459ce155567a60302165f81f2b31c06042c880c1848c0183091143187cdca081238d1898aca059c10b78f4684cda78411b6d34268d1a3258c10bd63079a38c35ca507939567cec0b0a7ebc4b1a71aca4a0605fd278159438541a1ea3f881f140073061102085250c3588c2074196494b4b03097142135c545a05e5070a0b6b4171327ebca8fc8f96a6838c1516c4a182e2c2b2b2ca2023f364b088622404e5048369c16042e208692bf0675961616952daa5cf6028292b7aea7857e15152e2438d924ca605cfe4f0f620228986d9c2f3ebc591681af2f5eae7aaba95a7a751c2a32406253c4a7a6432ea9cc98940a26167eae0797514e7f5b3d14619256ca85182d48bc8231a67b4d27752c908b1b5239af5ff67fd4e713b7f97d2253ed428c964708e4ce607ab834d0ee7c01fac8e448248239a554ad92b5b2d75f6c21317143a18d12464f68baaf32cd2c6269765105944a396b75764b5b90d979de8398a499065729065c525064da645e54d80b49851c2a384054d26e352c76732af8272c2fa87099016114534ca3d2d74f2945aeb78221ac765ac1da4ac8920a241942badfa4bd6147113394493db5225e59799ccdb440cd11c55bf7ca93ee920b44da4100d2a6faf4b7335b76a132144e3abb5bc57da5072d2263288a61f513bbe87b59592361141342a9b615ba8c9d6a536914034e77ad0c2bb6387596a22806818e725fbe176a8106a227f681aa37407bf314d07fd8bf8a139ad3f1d74902ea410bf481f9adca6a919b11b43a82fc28786b12394c919d9ba4f2fb287e6702b6d0cdd674b782fa28746a1bef3a9d787d59d17c94393f637217dd785cdda45f0d02463b6da79e3c518b78bdca1e1b54e3abd44b8adea227668d84aa7354f2b694aa55ca40ecdfbd9c37ad61d1f4b2e4287c68fe26bc7095d2d45179943e37dad4bb1b6cb57e82272685ab6f38628f1f612ba481c9a4566d4ca7a74659f8bc0a1b95b8c3efff8ece271913734e99a4aa550a6f3f5c644dcd0f85e6b4cfbb493a7c644dad0a4bf86365762979d1a136143c3dab9b36eb9c294f099c81a9ac53eadb2b61e2152c544d4d07862e322968dd8e1642269689252e88e5d373d499389a0a1416ca9a37cb4a94e25133943c3780b9b8fa162f86d8998a171d5ed199ea7f35e6d8994a1592c7b1513bb644cb5254286e6a8a52e554327adfb5f22636898a59f7699edba8d2d1131349e89dbaecc4b89a95e22616814cb961843fdff077b8980a161a81c153abfca0879897ca169e7e8a4fee784bc778978a139ac879ee9606e7a5d225d68be57b6e453dca5489708179a4f9454753f4a8eaf96c8169a45cccee985e8fc795a225a68d461eb6dc9edd662698964a141958ea357f6dc6be512c142a3b6e51ecf45ab7e72895ca161db504bb8ab1ef9c9256285861152dd14674ada8d4ba40a0dde23c6da11b1fbe012a142738e42881a429e0ba15b225368d03384bfd43d2f42b744a4d03496da59cbcf3763ec2c91283427bb1d3fc751322e6f1128344cb795f2d6b62c256f912734ca8ea6f647353e8a598b38a1417d16d2637d52da632dd284a6173b84ceb95696ba5b8409cdfa9d362647dad46b8b2ca1698ca5c4cbd6e142d7165142a37f9e9bea697dcd528b24a1f13beda064ddb221862d8284e64fb5830edb94e9075be4088bb926f6099d1631427390393badbe6dddcf2245685c651f5fa7afb5e767112234d7f02ca7bd5cbbfc2c3284260f37b796acca1b3a8b08a151a86bfd619712525d1c8c467d3353ab9d4b99ba19188d3adceeecab43c9dcfc8b8625869421b6a4946db32f1ad6f658229534e5dae65e34fbecdcaa55fc9fb29917cd41cb91f269284f9f79174d331e3667e7f89833eba2698a176aa5c75a7d53ce4593aca15fdb0d1db3a58c8be6f4b5745a35bd4593cbe8a4e6b79ced52b668923b524a8f6a66e6a916cdadd2dd3cb95ab79568d1ec62e5d4ed3429d6d02c1aa47f2ef977624e45b268f29e25857b786f21e25834cbd5a536a62b5a448645b316ed66e21e5e878e5fd1e06a9ce8bef3a4e7b12b9adc6d681bf3a3cadbe15634ea96b9a385f215f2c3ac68fc98972dc412faf9e15534b792fe3af47e9cf9aa68d63742ee4c8f8e9aa7a241db961fb1d584788e8ac6fb39cfafd546b6d8299aa6fa30cd7594afb596291ac4a3703d537e53bc4ad19cb60e437b2ca953b848d1a8f27bd3e386eb648fa251e82c5fedc5705fb1289a83ce62aded97526c1b8a86315a0c135a0ce169bba068105a76c5ab0ba544693fd1206e96526173c4aea1f544d316c354844c29bd46db89e6ee56273baae8a86839d1b047ea96dfd7699e6837d1a43e8b560fa7f6a9b99a6874fb513bfb660dd967a261db9efa107263c58f89a6976ee3eaba5b88fa124d6a5fab6d9de5cfb22dd1fc61fbbd955ef51a57a2d186ed29ddb9b4ed4b89a6d5ee42d5d4612c914ea2f196daf6afa318e3a324d13063febb2e33df1945a269aa4c9ba975f6d22148347eae5c111e274799fa88e615a3c46ca9734a9d544734ad907f15fda5bb446d44d35072be1e474c6ea78c68de8f727f7ce5aaf5e9221afc86994d9d3faafea8221adf758469d94ae8d8d1443499ed383b6d212adc8688060faaab749ab35dfe8768ce427898f09b4fb3374483ddd241aa4cb5f4fc4234cd384f156b4d55f984681a2aa69a3543a74ffe201a46aba92e7affab664134b9cdbb3b75912dc58168b22f61aaa49ecf9001d1b8fbb6564409a1bffd4393e9ce26a243abda523f347d7e0e63b244e54afbd0a463b5f05a9d2376ca87e6747abb46d4f6cc92da43838db5f7f9e56fde49e9a1d97552766b0bf774239587c6d3a63c6ab5a6471d5278686e25773fc384d4375477681a4f1e2b5ae9ac944ed9a1c17c3e89eb3067e81c5587e6f0674a74c40df9353a3487d569d8687526543e8726253ddefcf1974acce4d060fa498fa89952e73bc5a16185ff978dd939993ac1a169499dc59672296cb8e90d0d9f76965ea1d669992637340b25452bd149a90dcdf1630a51f5a9bf636243b38cbaceb0394bcc4e5a4393d8aad9faae748d9a1a9ab5cd2a9da5bc16ca9686a6e54988d9f718657b3434feb7309dd1f791ff0c0829740d17a73743b394356dc48b78b1fb3234b75c9d5d8d2d233e8c0c0d3b5608353c5fb6878da14955ba9479fb93972b86c6d96fa56f0b9dd92d0a43a3bcb629f2cd4c291381a1593dd7945971ee6a445f68fa5cbeb344ebf030222f34898fe6762bdae689a80b0d5be8affdb0dca37c880bcd29c53e19d1a12d3487d8c9ec74b2f5d6b4d024434717c35467f75b161ad55e2d3c896957aa85852635d60d51d79b25b4aed020b5de071fd90fa26585266563db94e99ee2ad2a349d30752b7ca642d3502ad2c67fe99cf3141ad5ae795d420b93429542b3db8a91a5b4161ed48c42f3bc8bb999b6fe7534a1d09c7674d43db1f3ad2a439ed09c6407a52fe6ddaa94214e68d0b526638aaf511bca902634fd4c7d0fef37a59f19c28466179da1968f907fca0c594283fa8cd62d2f2a42cc102534481342e950ebee3bcc90243477afad59a3b68efd3204090dae6ea52a5d7abc7419728466e5d2c610e2c6baa1cb102334dc2ead679f86dde832a4088d224c2a3d3f716378194284a61375e6bafddce31f328466b5f5afe7dab3a6b3102134ee284f6a6fca0f190b46d3b0912f2164e78a17309ac67cebac4bc713e6fa45c332ada6d3bcd1170d4aa88edcb5d9f669ec4573d82ef50a5d22b633f2a2393f7becee199e45c65d34ccf0f0d2618ba96d51174dba73d376d6c7912be6a251e40891596ba4582ae2a2396c663edcbd12b3c45b348de770bb53fd8b37d1160d72ba1fe6e81c6699588b063156875b2e3f564ca445d310e2d656aba6424c9c45f39a7011fdf94d691165d1a8e7937a9db366481163d19cd6ce41bbe7202c9adc65dd74ef204b79f015cd2795abbd266abbf0a02b1a55c93c175bf78ca5d68a66cfb2e3f6761c1d1b2b9ac6ea5a996265a7aeada259cab3b9a1e769c95f15cd263bdcd94dfda6f4a9685affdf3aa91d6c073d2a1ab69c756263c95a433f4573e835ed375a743d6e8a06939d6e9b9242f98d95a2f1d312ddf1835a4b67a468b421fb4c557c84c746d1bc9e7f840ebbb4ce2d51340defaca74429f57b87a261ac8d1ed7595bed09140d7ab6aab731dd279a3c98da417dac3155749e68d8d16f7a4ecff539e83ad1ac5384506bb62e8fe571a2e16eaa27f5c9db44c3beb6754288b821e369a2e9952b615b8e5e2d759789c63b71628fd8eaad1b261a6cb9ccbd1d53e8ed976854db514cd9627dd6334b340b7997bdc26775d8956892b1c6162d1e443f4c8906a1f48bf6bf5bd3b69368fc2c579fd8e96fcf54120d5ac8541e23f75ea79168127accdca35629be8244937c0bdb9ffbb7c6ea110dba46b88c37b5b43439a2518f8feeda37e384a9118deb4a958b506244c35c35d656a78d5bd1221ad6f7b9f7aba13fab88c61bb2fcc3ddbcb14c44739f0d1f1d64e58b8988e6db59ec0eaff6c4c54334c7f6587a63e6eafc19a2e9d6d431f4c8d5723c278c42342ccfb13d95b2f1b774277a2b2e4a7bc061b21206219a86d86aebf2f1305bd7133d35833006d1f8f9678cd4f714216d41349edab93a9d7cbdaea94034d79fc8898aa9ff960988467331869cc8cf3556cc64329937520f384c4cc2f843b350725b6dbca7574ae487e6ba1d47dcea64cb84f2442f4fa111461f9a6bdeaa0f2a56f6c5e7439367f1d5bb42ef97d4f7d0ec26e5a94ee24b295dd24383122d95886ed7d1525c5131c943a3c75d73b546d44b2d3c34abe7ebb0e5ed36d569776856ef6af6c71a35e6b243b318fa86cdf41cc5e97c1d9ab5b72ce9598b3e37391d9a656c5c8b991ff75e7368569e2ebda1be44989cd039133790cca159a8fa903f6bcc280f311b48e4d0a4c5acd75ab5e7442f0e8dea65ea3095c9bb39d1895ecb1d81040ecd52ec4b9d3a4c35734b07595a32994b4890bca1c1ce3bbd7aabad422b4ff45e8e76414c5404891b1a54e44c39a3e2aa552c0d246de084d90ef6272df18146a281840dcd713e755019db6e26446b6432990c620f384c42a040b28626af512177eeedb41ea9a1612ae9b5a693c78b71b9a8346b371683b0b8a87c02d23fd284240d4d6f2a736c75772803123434cb19afe63d4a216b745c417286c655db399d548b47d5cdd028e3a3abd4aec4575eab202943b3b787f9f8528b2574cd64d0999cda0b0509191a67beee987cf2a8933493c964de28e151b246098f12377eb8b4bce8c0cbf83fc3dfe5e360691f6716246368dccf9f7c4dea4efb3e71aca8c16420114393dc4b255fa931bfc6380c0d5bcd15eac39eab8c080c246068d63ab6b7128fc286f95f6812634e687db2c5dc9761e085462db56bddb164f5ea5c179ae64f4a1df4add805122e34671be2b6ea9cc610717281640b0d2b964eb7d4bfaa399f13bd964f612a2f2626aed208151148b4d0243bed6e442d65ab55277a7b253ed0500949b2e0781afb57b94b2b418285a6b923c285ceb8fcf731ff407285e6dbc9f4fd9cf4442f4f354162854633252f4a6795bff19fe8dd5982a40a8d9e6c8cdd5bca46cc469050a141dca4eb92bf96f850a344b5132453680e62d78650ae1ff35e1e990c2948a4d0207677b6d51ab64ecb277ae85e50e2438d12974c06251e8f92d37c0a90171f256ca85182ca112451685ee539e647b49fba0b85e6f417196243474da573a2f73c40f9169606c27897ee090d3a8f18624ae764428b9dd030b59a6aabd2d9156b3c51a59ca409cd9f3fc7d4d83b6bbd4c0109131a4495dc29b13ea8ed1a2dc9129ae7c41652e7d7254b0dd91b990c0a0b1b253c4a9c4789e28144094d6edbf677850e6ba83909cdda5fdbe84e42dc4cd3895eae9514339e750a1b2c2f2f28e151c2f22e2f2b6dac951433322d6ff292c9e48104092aada66b79da860ad321394293d02364edc573b61f19a1c9cbd4b9966152449f2e244568d09ef549cfba456dcc89c0ac14b55e5babaed2428164084dfa323d98fcded7518b44080da6773fbfcdaab94783d1b0d53e5ce694cffd0a1829713a47f51d1e949c9d241617202c23bf6892d7b66fc4ff69ad947ca1dcec4d2f55156b5f2f1a6cdf8e2b4a0cbd7ba11d4678a1123b2bb55deea2c955aec88e129fcc96a35d902ed8a99fb572b942ec72c18b1a75724bfcc81bc385b2a4bc592b3b3cfdb7d84efb7a5073aaa39a2d9ab5dad9dabed56ad1d873b795f451f918a145837c09757bfc832c91611dbd46098f12951794f0287115141794f028f99794c60bf22c5fe2438d9273a90f23b368a84fb174e89935a2238b57ebaf7c4aa1c3541d144662a158a14c65e55d857a0a61faf9f973529b8e1e3c34d005253c4aee531690fe61728e65c5a5390c498cc0a269d489eadc1a9de8ede0b1c4071a253b68d8c86480fc602d994c2693477bc061b2465ed1a82e26a610b92e4a8b277a26790ddeaa188cb8a251e9fc5cda9550e7e279c3482b9a85d62eb50a3d437dd4133d4e3718614583cb39efd0ae2a5ba9595cea684a78f86732990c43d964c3c82a9a8554d75178bccea94408e130a28a462dbc85aed7ca83bcd089deeaa5a2f9555fd7706dafeb93277a2d2b40120c23a868d86afbf37b4a15ea4d277acf0204bf30728aa67953bff2e3d344279de8a9ac6c19464cd1b45c46e8475142d53e65327726a7b0071c2661d061a414cdb7839e71535f799b5c0b23a46812ea5ae47d7abb95d251348ab919ff75db96898fca6144144d439898cc556a33466c24144d76dee333a40e5b2c9f0b23a0687cdf57dd337614a1fb271ad452ffd5e2a6546fa6ac34e4d27cce138dbea76665a41663a45bc2a38447890f354a32192028272d67a4130d628aa8f0f8e2f5ef8d134dcba5db3ced9ff6add8269a7e947017275cd78d5713cd49f5eb67bd895143958986ad2f4b747225539b12130d43acb93aa52cddf15ea25187b025a4ce22ea9396680ef9f0612ab1b3e91c56a2d96698bb490f4b541796959673cf50525ad2084628d130f7b3b9d83ae7da3b262e1f875e890f345c1e8d4c66124d6b8ad57a4e8e68b1f1899e9244731a3dba8412b7259480a0d081828c44a2f984a7eadc713ad1a3e3559e2521d1243bf68f8f8710634b859147348c547afb4d67644d4961c4110d3366ea9f6db5df62e844af110da3f34cf1266bc694f2133d4634a7791cdf19cfb97c1795c6288c2ca2b9839ec893bfafc5aa262edff22c8a689672c6f2a88349e562894e184944a3c98a7b1dea4c9777be0511cd299ee58b15f59ed6d641268c1ca251958a1551fe2de4aebc841143349efabc31cb6e6fa710cd2e4aed526a5a84bb16211a667e7892a673766d4b07d11c96d85108216ffe6b2c88e6f03ab1e47b5aa73cdd4034fab95063eecde57bfe440f104d77b66c671d62e3c650452161e40fcd2e63a82542f968b552277e683c9dcf3c6afbace6dac808237d680efea75a7576d5b7b52d7c6890e141db7a1d557a5a0b4acabba09cd061e2f271eca1e9a36db166c516d95d47f4d02047e9ec0f9b2beb75f2d0fc65befac56ba8d82b3c34ea3bdb497498327de2133d24c2c81d9ad599d8695596f2ecae13bd2384113b34a7b186eb51adfff5d60b2525a59b0aca09c1481d1a5fecb3bd79f377eb3cd17379f96182c218c2081d9a5db418e69d75d8fcd989de7293963ed752c6ee1c9a6deaf8a0b3ed7bcc288726e9fd694a79392f557da2f7e3958e913834dc58ad9f555dad31ba133d3834d91ab5d4d8f7dc3fe989de1c236f68f6d131dbd59e1b9a3faba126c4d4cdd8fe446fdbd02c4cea3729fdb698149ee8b97c1cc9a3840566ac60390b4a789498b1821e7098b48cb0a1c1fea558b1d6b2a9ed5f4373bd7095d79d75b299a9a169688f4f21ae9f639cd2d0a8d47598ceb2b510a273a846d0d0e4b57e236e2c756aad9da1697cf6a9e52de23c8c4ef4da25e53ddda3b0b89c113334ad1652bb776badfbfb9dc9a953ed018749187c18294383485dd5f175e9b96af509ea0187090e46c8d0e46d33b41c1d22d4f247c6d02837d55d7aedb6d5592a28325871514923f580c364062362d8a5bb8a9a1a312d6d7fc29ac7a3b0f84869b6a24626c3a331c39f3590180479171c1201c5481856d16126e54c4a0f296a5e4aa4c8f59b6b9ee620ef82939666010f3356b0862a538c80a14188971b97f5e9d61add179aa64e73dbc98656db1d37329997353299ccf23b1617205718f142b3def4fc1c725bce4c65c52425a519339517a89c91c964322dbb2d4056c268c248171a7c4da48dd462ad8e1b171a86160fb76a2cf52b565b689a324fdf5bbe2969a733da28e90187091a235a685c5d57353f683f39337d309285268ffdbab245074f17169a4b9d124a47b6cc98d4151af6abe8ec99ba745c35208c58a169d42dadead65f85262d7574107d6b57a731151a6cc51af2e1c59ecd7c0a4d3aa850e169ead83a9b149ae66a25c5149d8314b68d42834ede22a65b2a9df3ab072350689a33b5cf9a714a858a4f68f2eca9f566db6c931e6b1d234e68166a5a775c4e9cd2594d68d6616dcfcee57a769ac784a64fea6527adf339a5c90a234b68b41962bf2a9762d7554a6892b35a997cd562bddc49689ca1e6dee8542bd5474283ee2b134b756bd91f8fd0f425f666c51269a7e61123348b101f75566eeb883c5284663f354674e5e4c8db23446850bab6fe2845fce8ac73a298dc181942d316f559db478e7e302284268fba662344c7f6dbe7fa402418cde16c448b13b35e0afd0ff646528208301a3ea7fef652626de537fc459388ab1bfddbb2be6b3c10f14593945da9e294e8dc14447ad128efa5b808751dc4ca8be612f36dbfdc8e1da713d945c374f9af57eac99df111d145b31cef15d3e695e9acb96856fbc595b0bb53ae4c5c347deab86dbe739e62768bc60f3ad45d0ca1ffab75da28e151726050c2a3e4b051c2861a259b02e4078ac9a70079713151b66852e62ee4e82065cab6b9a0a8a0348ba3b403915a348856a9d6a73526e7b9121f6a9424ca21428be670eaf75f52a821427ea2174465323c4a80f41a778ef5c2b883c82c1ac696dfe1e47a6c99d7109145a3cb1873a6986c33a90b4462d134c7b3f21c7e7a7afd444304160d9fb4ea346d3d48d762277a7945107945a38bcf5fed518ce99b277a77798d882b1a64cd6c9d2a751823446e45838e6a9c8c1de35b6e87e320c28ae659d3bb773ad4c39ec9989c5a45b3889e4f1bf2afd474a682882a9a7667113bceaf2e315c277a9fe228262a2b9a86ca0b3299163956564ed4214452d19c738b4ff5aa6aacec4ff482b0b8dce96d0f384c5420828aa6f5781d64b71059b79de8b9faf12e696432253ed02829a3c4871a5e464a4aff884126938906915334adaf7f29a6fbb984365920628a46a12a74eb0a1f2d9e2d61438d12ad8148299a54483d42d8da496b5b93a2e1c37416c3eda6f6584f74930d4446d1e4e9ad6c5faba14c4c4ff482b0b86cfe58613923088b8f4c06cd20228a86d57a757f0ce5715c8bd90b22a16870f55e9dc627f57d51174440d1e4fae1a30e367e3dbe9f684e3a6a6debae526d519fe86532253ed428a963c58c4cc6bf8e9585760e114f349e4d5ff11c6b224ab8dc89e677f1e243a888f7ac7551f93938d12c46ca970bf13ad153977a886ca26967a48a8dd16e7a57eaa220a289a67fd5f961685b6a4da1828248269ac4923564d98d59fbd530d1bcee614e2df5bd6a8b41098f4c2693c964140b229768d441ba183a4fbe4afd9de805592698b2da5f4506994c2693c9643299cc8abf9c18442cd1e42666d4d452ccd5732342a4124db34a844df5524a34dfabadf5c55653aaf59ce82994851099447318d3d57579d452148868d22ed438b1a5091dfa9982188768eea866c7955aa8473db6cbcb0f93202c2e8668d65929a14edb8ca83dfa979476232521885188c6537bd7fe4fbd0e3b219ae4549c69756288f5323888318846a1a6f2f570dfe2769a209ad47e165d1d6a46ca59023102d1bcd2a3897439f5aa76e94ce6553ac504250d4e3688018806711bf55fc25569fdffd0345e4adf88def21ca5128ae18746d5b7e663086562a9d9895e8b494b9006723048b3a15246903588d18726a1a37ed95f5b8ce12b3e34de07a58514366b2919fab813630f4d63d7a79eada76f88dc0531f4d0e4a52e568a29ea85d8f3d0ac4bdf9673aa4e8ba9f1d0b4a3d466ee7a943011dfa1e9834aefbc72d5562ad9a1c9744c9f35865a2236ac43b3aeeba4d7ed44fda743a3ccdb2aff6ebcfcd61c1a94129d65f477b2a5741e253c3688187268585b940bf55a76f6bae2d0a4c3f4dbee13c3a1618a5aeb5f87d8351f552288f186a6a9745a5a7cfadcbafa8b186e680eaab6b6d8f5b62f4caa458c3634adc8471b3aaabd101b1b1ae6497b254ec7d4d8d1891eab408c3568ffd2c5f5ac997a931a1ac5f92b17abfc6b4a511abab143d5abfbd52ac5b262c2f22a741c6c62a0a151ad9d9e530815dedad36b811867b8634e851417a542c4ea6c428c3164e88c4e5a2b0331ccd0b4671f32d5d2ae765ad528f1a146907781192b284373d459be3a35c7f4ea2e06197ef71d21eed63e86e6da27d276b8095f958ba1d984c897fe69af545a2b0ccda94eb7f4153ae70e6b3034d7dc57b66e6e5b8cfd85e6527b4d0c55f2ffb4eb85a6a9b7a3eb20dd2e34e9dbbf51ea7667d9592e34d90e2de4fd736c7bba85e67ccbf4aa55ea3ce793161ac57fa6981fe655cc9485a6132adf27a50e524b11161ac68787f9e249a452a12b34dbee94f7b4cfd55d6985a635a657c67d56159adf7510623e9c7efe910a4d273d28d151ac9aaf368506fdeb69afb6e8514327850619b3d377b2f936fb281c3fdedd63aece891ef311c48042c35023c4f47cf95abac9623ca1414e8a7aed31d46ba739a159558bf1a8d5d38faa6234a151c438359bd50c5c1e0d952206139a4fb8f87da9ed55e56d098de3a6b58e735a4a68ee34f29ec3dbd865da2434cbbfdd793563e9741509cd3a2e953a91a3a2644768da2326b6f872e51ea58cd0b41ee5ad4e357b554c318ad0ac85ea09a5c4edd62f13a15989f4acd389adb5a11a42a39c21d5d077911842683c19fa53087dbade96c1689a51a985d251e90ab10446a3b8a957ffec2eb1f6fda2598ae778a2be45d58bbe68903e6fde6a62ea45c329a5a66fcfe76ea99378d134cd66b91ab7e4e9dbd94583ccd662b7ab4b112fa4ba68d241290f63ca6e78efb968d2ba6caa89cdb775ea295c347cdcf87e6173bf5ced168dfb1e85ab2df7fd94ca160d4b78eb7c0fabd334af164d62f46a39ef67dbf4db2ecfa248b08316cd5294275dbba2b679d489ea193b66d1ac3b94b85242dcc9519de8dda960872c1aeff354dbf6b0c5ab3d16cdb9a3d8ce6928312e068b26bd667a3baa584a7a68c270ec7845f32a1b5ab54d4f2bd5ee8a261d53747fabfb646bab158d3ae72c2e6e2a74cbd00e5634a94d3ba93e3a799fda2a9a45a84ee15985bead5baa6892624665e7ac747ac8a968124a8b52ba86dedf1a77a0a249c6558a2da63c1b32768ac67fe9ebc2b392abbf3245b32b37a5c456cb9cc18e5234275ba9955a2d746d7391a2f173989a2f5bcac70b768ca269a592a16445cb11fb124573d8b9b5964ddbedf63ad16b3945d9118adc018a4fecf044d3da5ffa94eca43ac9ef3b3ad1ec2aaa73ee60a3b57fce496107279af4bfc8923b52f69d7c134d37e375548a5b2d4bd744f3a793fe5187a931de9189a62d66b7f3a48950ea8589a6e1724ae7d9fe599f2ed1ace6213f8d696ff9594b34ad7a7167a25c8968a94a34db1abb547ccf76e924251ae4a7729b2f3bb6d0c11d9368124a8ae1eb7d5b695b9344c3f86b5beab73cdded23d1b46c68dd23d5103711128d1fa6f8dfd977eb86fd88cbb46087231a54ea91993655d456423de03051d9d188e6da1536c5cf9c88f932a259c5b61b6abcab2a9d17d1a46e8955733f53aae84e117628a2f1c37d92265e9988e6e0a926d3b5981dd648b1137620a261b498adc6792b21c5ec108daa46c728213a4dbd19a259bcf99fed12dafff38568b429b5c856e2a3db9013a249766d17abe265424dadc9e510760ca2495e5f08fdcf265e8709a2698cccb7890f72ddc4dc1188e6f3371fef1a29bf8600d120474e5ceca4ea5bec3f34abd7b52bdd463cd8e187e60eebc4d61bca6bd9541f1a4666e8d4fa9ea3c4898e1d7c6818f15a9dd2b94b906572d2f202845d5e80b8bcbcc08c1d7b68bc8fc267cd5d76c998b9430f0dffe2566a35112a962b0f8de7fa74b58e69071e9a63ecf93c52678d1b3b30ecb843f39f8b993a6e2825c36587e6d33a4c6b7b7d1355d7a1c1a46e71627c9a52ca76071d9a3feb0e7a960a1f39af0b3be6d028942c79fbf6b4f8abc9a149a64e53662a1e6c763be2d0a8374c0d19a5cdd4773e5ce50e383822d373986e1576bca1e1f473ecb257bb476c3734eccbb6f79d3162e8dbf0ec3ceb2f527d454fc88626addea58912d38e355c6671b0430d3bd28018a53dcae954e59dee4043a38aafc973537f2a43699c3371c38e33348a5ba3c487cea767eb30ed3043f38fb78e2bc66bdde1e2512283121e2569b874ca19998ccabbb8744a9bc28e32346c532dd7db560ca16b6468f47493d96a4d2c1bf33134fba9165bf4eeb77689a1f9b6bf88abd6f39b79189afb85d242bd7ffc6e0743c35ab1a6c465ee97e85f68107a4b78d48f5329655e68bc3921eed9e40d25b5bad0e0b56245de4eaf5b65066107179ab57a1bf57262e67ed6169ad3b2b5f4dbb0531f590bcdad7b969c88193184360b0d9ebfa6e778a55a6c2c34bfaa4ff71d66959aafd034648735c492f52a27b542b3d6695ed5847499af2a34ccda4aa1d3e4874885062155cbb1bd36a5a7a7d034940e51b6f5ecf62c8526d5a7f3d6b510325e158566ff10e23bacf123e504854653b2f694fe5be7624f6850cbf587adfedf3c8a9cd0f8e75a97db966aa7994d681e9d2f5d479b9ea68d09cdb7a69491fda144ec71090dabcde3499debfdf4a78466b1f4ca1a5daf336f496834715d1d75c9fd131e243487546bf8ad9b8cd39d23342d25e58ff22826950e1ba169868f58bbaf3dbd6d2a841d45687c21cd4dd44c2bf77407119a4ccad62ef674ada572a9e355501aa5770ca161ceed5c22b7d656e7ce600674ec1042b39a9edf71b427bd21142065047139a30d14209d61f90549301acdf3bf47b5dd7ed92b37cc306159692629ec1d48cb1ee4061260342b31c6d5983ade164afc45d3ce3e1e74955453b1fba2e1eec4c8e8306ee7d2f5a2c94baa1ced6ebb7b563590f0a251d5d4a773ee6eb154e90a24bb685073528c3ee925d6551e24ba681a62b5ce426fcbced97e40928be6f4af85abe9a4d4fc5a144870d1a8b67da9cf396bb5ed160dfea152ecb076c850d9a2596788b7b49ddf5baa45a396d3a6d5faa7b1859e16cd69eb101eae6bbd52d92c9afbb45a1d3d9fac375b160d7b7adf216b47553fc7a241dff7a9f697f594824583d71653a79571a993f6158de21f6d4ed7da75bb2b9aa3ad569f642cb13ad68a6653774f3a77cea16c56349cb8a87ba1d7a316e92a1a4fea2e295a3e6bdb49aa68ceeb4ae8d3e2d43d2d15cdaaa6ddd0596cc7bba86816a2e7334dedc732d1291ad5bef69558d96248a5291a45fabe4ca1b62cbf95a2498c534bdd94cd37252445b354536ca85ca1b5fe378a26b59e5db92a316a4945d1a4c5f6abe99a2e43e947aba0121f68dc310249281ad65c29961697ba735a110f140d62b716faddf3a9eb6b0a249f68ae1a2ab47a8fb594cff2d2b292c24c165352143dd1fc2daebd3efa88d0f1133d4447b7010c924e34ac1aa1c4848e997ae7133d96202c2ee9509ac587154838d17c62ad1163bc4bc3e4d160924d344a2f35f4fd9ea9af3693d92d9068a259d4a79d86e8d14a9bee99ca8a4ac685470b8f202c6eac81f9921940194832d1a4d3d44a6bd73fe6e362a2d945cedcd55833f5d4279ad289e4128d52ad3973942751f942339058022bc12f24946834d367429a10d9751fb1b80069836412ac5929cd5327357432992693f9c10a0b1249206aeb9c6cbd105adc1b89bfb388e8e061a4f44ef44a5c5f4a7ea09cb464e62859fa5272100b09245ee5a1224fb6cb6ed7e5650d9533121b248f60a4e820428af32465454cd58e52c9f3936a2c9de8a5a434432a67601a248e68d2422d5719bb1bd1f03752a738f93774589e33394634dea9d269ab4ce9b376248b6852b1a2745cbb21e6121f6894005961594126936901b2e2031245348eaa1442f45ff795545652786432afb292c2039244349894b1d139bfb8946a3de030c901092296236be94f7fbd9d4c8760988b903362febd18211d9018a259dece4b37a9a01cf20d480ad16caa93986e9b177e734234eb273fa9226eab55ae4134b9e8907aba86bd0e6b4134a779ba32314baa99a240346a2185daa9e7b76e2901d160a29eefc48d57299e3f9c956b53f345dce9288390f8a1e1432a9d46a4d82ec4b2720e7140d287cbd441c20774ce5a3b77c8fe5142997640b28726756a4a1bb65dc9c793440f0d3b7dfc0ebb323a2ecf3d90e4a1e943acfdf9c66ba594961e48f0d09cc6a81f21cfe40b9ddea1e165eee9503fbb03891d1ac68a54f3d5de3a34ce2d1d2db7335ccd6c03123a3401915fa8844e0a15711c0c858120880110844cad1605b31400203020180f4683d134d1b4e90314800340545ca432281e14c822814818108401613020100cc3200803210c03411005ab24748f0018c71e4670c790eed24b1fb48aaa1a8e1f3cd090877c1a100c3dfbf503705f9e6dc341a64406d43a71909887b6f5c682ca2941074a15a5ce31d139bc22973002545540e6dd5163512b1ed9786adb4301d8fcfbc12396f764ded133d31af749400fad43080f4a8b573dd02bb3e3030f42c8c23dcf373609fb14b6fe876a81f2d81e74686841223dc84374112845e848ed67a78481cf359745eff5d54f81ebb57cbb048205fa283723f5d1efe08bc051611da2e5be72ea98e23930c73456c0d074c4e3209112483a02076f7e506c2c19cebd05e483559af7dd6d40d51cbf8c5919ce0f0fb0bd48663d31200fcecd25da915dd549f0a5fc7e7c31de9eb445d4e0208cc55b22e5c56c458f46ccbe4ce01a595fbc15ddc51190a63d123b4a633f6a7e80721f01125a230303f107233cb002a0c6a7ec04bcbdd00bdc63dac32775c92da95ed25ac32a8206f09cb17114030fdceda98170467ed2b72009c32345011773793328843812b5bdfa448ec210116f804d842c5187b5e97e737df537ae6691931ffb4a456b73c8da48c2bd5e901fd53dcfba553cdfcea696e6c460c106805d8d92bf1f2f40d762689c2980d18703a877681f2a0a4a70d4451478d025ae5e4cb2f7287774549430d405286e9426143a5058448bc3cd5c68a1531654fca5dcac0cb1ed76a01561197884818c500ebf9ba642caeac5364e6ccc4d47d99fc1089eae784cf8761b2a7c1c9a5f668bb6318de360bd90d5a0bafab6e251cb509a8fc28e13a1f13e5b0495b60bb85db828c62706b6ea2fa6960872c9740bef32d92451b1d65002d803331a4047b7df2b8df91415347d0784f900bed44c5c4cd5e57e9c80acea22be1f5ff5231ab8c83ef07a34daffd1b85cf0f39484b1e4e5a185e4410582e5ed58118cc889ec2280d7cf8807ce743b509667b469f46c661a439b94c2d5fe8a780c98b012592298e29131d7bbf66bd8debffe0b88c8a6e573bbd8ec0e4b114e003c147b57127c1118f2ea0bf29fc2a0abcec126679de0b420e8075a9700951f8001021b30c7aea3ba6a58667aa6e0e7c037db9d230302b742e7282692154e77d753c9c19b9c45b3f65b2a9a61e4f585019870c7f608627a7f0bb2b728c3c2880d7f88ab091a4a9a4d24ee9aa12bbdc351d30b3065da20b39e9ee63e984808f81776f5d6458c9bd82e6525abf2ec58291a83d12e4e14c5894ebdecb6f9ad0667afd8fa40551b7c25811cd4c9a408a78e2b24ad145c14ef1c8fd2f99a5f6d484c23d2ad60480d2f0eb046b714b11326325775881b91cab5c7a433b808d1118d42cf272f5fb9e75d6aaf0d7f169388552586bce7cb70918a949cf1f2a412693889a78280df1373de2cd1a7f6e43f8ff05fb99201c9d31bc4690d5b0c0614cc4a16d0ca5153dc09161288d8e103c8fd350dfce83961097db2f9d4082f6d5082d41216a83f5d0e527b1b2eedc304816fab3309a32c60271180023fa020925c737eba0f9bdd005e52202d947e9c36d1eeb204ed5082c6f8876222692fc3d83e3184a708cf2fbe32b4b0625688f80bd3dade7d699bb3c87a98a614848d27e83343cee06fc0c752540c14a2dd9a00fa20f034cb0889433337ddfc2b9543c28e07aac0dccf6ab776dfd8688258f1467f16779100c31ccf9032e20206384726edf817acd7845ac1a865e03129b81afa9a02c6bc92640c3bb09863a102eeaca0057cd0dd4dbd63e06caf08c3be10ed1c1ed8cb9314053788d197148989d9eb9ad63d6eb0d510a5563ea370d80e6e4388e3981c5b7a2c234541fe337aa4d4eed36907a373f3240e98619d2ab840db461573e7481090d4b4d856556fa56a7a7654c9265dda0c714658f1b0548eeb861996a0d401c3197af0fe64f7dbbf0767275a7fe51dee1e747a4e9a7e83234af723c8f53860e8c880523f819746d2a547a21d7bbc48674f74b37baa63d08bd387d5b88cee79ca7bd97a1d3dcbe14509add1e3a8597b2d6f57005de6db820bec436dbd367817ffb611be02c9fcb12008a7518b302f1044cf201fab41bc1e4dbf9c40113d213ebc183c249f377893778ec6ff28554b7faa214538929bf6a52502f917322dec470ccd94460fa9a3515c2b31fc7742d63ad44e9847d34f5993aba4f2191721a60eedc236d83e2dee9cf4332798f8d503e7c4198135e24082e6861b7cf314883e2c606649431f3ec95010fd0210a41f1ad37307aca193e9e1936046e81f72bbeedfa5ddf12c4a6a1d7b3772b385800d32d9f7af8065739c5f98d90bb5feb6531fe091864aea7905d1636a506b99a0632727c559f9b16cdabe4c44f7fb8a0a1a21fc7c86d17d94d67e7ddff97ed9885fa270b8c2958fdd268f46fbacd4a1eccfa5360fe0e218c1484a80564ebfa93f647b15526bf515763b10305d61c5ad05965e649599f7a3fadd2ad1bbe88cbf7b3f098b6e002da739eeff3799bb4c4d1b89a74d172f3d874e64d2c0d46d4273120c65f109cf546b692e1404d3504e56a6b8a1349af99ca246059aac3981f77441aa5df07e6b260141fa11efd493ad87e2560882f9294467ce2d1283227f8f5ac12d87ef4008089c5cbe257683ee87770bd7ed7aeacc61ac9dd87d7d8aacf607ff5ac6929d5f215aeae5a4c64112160c3de2d3dee43a5c750437c9c1b3873411faa85441f527eb2f7a4ca871d2089457769d5380c14e3175365813bd6743bbb6e117a9a2c16596a4c2617337b5474caa260cccb7f985b0d03b6970dfa743e942267bbffd8be83ba746b1a964a766c7c1de36241d390f2696541f2551ea070d4c18193f0b85e9864061940883712387f6d50d1a01f1b54e47fa71dc5482bf15a1e15a8414fb7953156d7a2c8155250d03d2aca6a8e9ead0f7bc251647846ae92c8799b96db1a797aade3bbe4c54d641fc464aa92e7574f5b560bdea20a782332aea674833d8b8525990853d5d1b03a21f78ad9634838df2c44a80ac0e1d9d7ae790fb6df0b29a43cd9eb4cb41718f4f39a566240e0e9940737249d65d224de27b4c6d27ce051c6095f47ff694f086cec30642819f15238693c7e29f6f65f6389e7e2216c67d305157922e1e431f392580d203402df241aa04b0f5b88811fc8403d3abffdad202db7ec59c8382fd0198a51960083c93bcdf07c13944a1d785c86bd178001c1a2a4198a377fe73b8a1b1fdbcad2c130424b22edad3c40724cbb4c9a56d4923c89a6523eb2eabb1c70a62a10210c5b3b8871b113369ecf7e9bbac61b5abe4a38b65a87d83492e55898825aa06bc8703df1bd38535cfbafa7c895d2adaa8efbf480eb69231a92a32c363bc14a2a64884be8b9fa4ecfa5407979336b99661a238e14e676d5e2277ebca3ec0bcbb55da751cd489fe539e703675f4a7edc7ffae6cbaff4c74feda80053d3c7ec0edb491cfe4aa57d349c45442c955fe39cc9904e06d89014f2551285c525aa1ac58ac5302707744164ea189084883d748e6f5df8c2188c33df501df67468dcf5005ea5535659ec3ece5c257fd773bc2807e8de748355cbfc5b19d2f12d376c4bf561351b81663226595df2224a786cf3226e448a726938fef6628a6eea4c6a1c6996627bad48e3ba9ec7cf8c7496966d40af2e70645165a0af2cf8ce886622377cf1a2e4bdeec9007a831a7a52e5fc6baf3488c4c01b27740ea63e223abd6663feb02602a1e18ce1a244afaa56962a6959278f94e86d436fb763f2acb73717ad938e52f9de2d3cccbe553e5867093d1972e31add23d455a651e89ecafbd3554af60fc2766015e437de15fa1973dd102187a310f2bce2171e699de3831795d75cd6200b2f7d8b99a0be0870bae49cc21628a1c8f03a4ec6afcbe73d9cda0c8828f27cddaa158e1b7b2e6809e2e741f388bbdf0ddcf64fbd203e440b9b96a0677b1c53373e3eada6e1512c28680d1b61bdf6f05d8d15ad9332e73b5cde53cbb6135b5945828ea5a999c0ccf1713f1f52191b35bee472e6e4b676c172f4e2c467ec27427e87d424fc7496cdf4d8a3537e0bd9f33ffee1e486b58e79d3ee91231e9986f8074566080fd510c3106291bc85931ae3d34412cfbb6ac6f7ae49ee646ba463e9956335621dce5e93f05cb734956eb91ada35e5b63f086c3a27233b6d700e4b1b3a1cf96352c1c7564178239297d92d82c5d1384390cd58e3c54ad042273a941e9b08dc12edf5605814e0f014d7372e2c22dfb546b99eb4a3b90f97680eb83dee27dc585e3fdefa340e14b5839b00ac75dccaa299b5965a4b4b080845a6c6483bdc14b83df437b828649b3c4de650c43975bacc262c17d8724bf44a61ee4f37764bd82cbbcd48964d71bf2153776157b70241a25763a93ab63cdec06c34d9342c4375cd8a50f94c1981816888cab43794246b16cae129d3080f4f9ba6205d51232006199fd038d2514c257811dff569d51f85c96dca32d87932b6130a0899b3ec6dbe0dfff95068d443f33fc0c8230ba22fbe5ba0ab0437a3444c527368ca444171c6a055b8c23e56ffb29f2b4d347bdbd880997f3103d8c0470ee00421588f938140876f0571780a47a51852d8f9ddc989fc50266fb6334082066845573de490231fac8dc5eb44907b73de5528f147e386eaadd63c2321d0eab51d4bfa1aad91a94ae6364a41d223418b1bc9d4ae078b7280915e202c3889e933e5a479ca5e376da80dd0d1b5e83611d78284598518e632eb8b1d5e8f7207a1301e0e650833d65fa64b87daa72ebcfd1dd45a93af6d3266b021f199843d241221fcd17d46ddecc9d458b00248dcea895bf3d7e769135bbfca201626323b166b433d42314bfd6aaa1e1b2ea6ddc7a81420d9a5228d29af619d7f50e03d8184ede7e41710f418b8bc24fe644375da5c4d7d99f5608fdc327f8af3ac518a7d1360c1b93a4080ea5190e6edcde1019b4cbd266539784dce06cc4b91ca42411aac9870bf0f47af134095855a950cb61fe85811b32029cde562b9541ce0962c05fafb074fa06608c8623df6f0a8617ad0c097d4d0d3025b15153b1b11ef0bad389c70d10be48d133da692c233de24ae16698ac57a589e08f4f49273da52f47548556e81a496715dc68aa2778d6c7979880c53445094d42dbe11f4b08724520e7e50bb4da220a461eed50bdf038a9c10e1e7ec290844c4d89819c23c928ce4d56feaa0c70afcea2c4255977aeff8897e3eecb9730e4b40054a0f75eed020932b1c6b03988cef707e65fef0192899cb0d7fa736daf5cdb1e12b7dd675c85f5be0acfdb03c4568cfa4698fd515255d67f0105646b4a7643bcaac8d801d0b92660b3228c925b791eb351e7b5c17956e4fe9bb42b41d64bc8ae8ddaea1f2b457670a9e0c7084459baaab6f473565a9754d0112021359df84ea85cc94f4e08f03aeaa157608a1bffb613617d639395f3d8ea9e1729c0e86b3ef0f98285a303e4474784c496ca8851c1e4500813449dd589d2578c4a49f3329d82af02a54be2b736a68c6646ba31e98e8848205135885fc8581179f1009e1e29136a1d69a2c2d377782e4aa48336fb098341066b3702b9b7d8c4064a68d87de482e1a63f6fceeee1325693c25d15125474c59ea6d19b417a021c698bb486e16235cd780e9a6f40f4eb769d543e49d08aa01e04ba1cce65d0793624172414164c0488bc2aebc22068f54f5c58223d6b7e5f01868e98b6f548d20ebf9a799fdd0364cd86b7f19225abdd3407798af49ba8c55f2bce9b70ad40a87816b588702101a6f7e4406f187d28cb68ad94cc50e172deb040b489026431e09727d89179d5820c3acbed2f139b9dfec96d266a656b00ebf2d95527b79bc30baafcc516fb1dbeb577b1e609de1f0d23c58b0e54834aac5b8251ab4285b24b298e3c322fca3be65579983fc142569347680cce7877312848ada376597a6ffecf4b7892eb537a7eedb8f64b8b672dbfa14a83f2fb380e11e8c5dc29ed555daee9b6d63190b38c345b87251d4d49d59fe8507af408bac9a4b84f085069d20a7da68afb56a5fcdcd7be654d16d1c557598e8c4321b3b5b9824d58d2a6b52852ed76370831c1f91dcf8ac8359dece5278849eeba42df21ce5b20dfcb96d7ec14578948f37be46bac93500ec9b78b82f455928f7f349360a557c0e89d058578e85d37226354d21a0ec2b10936aa210479a48d1a7a011f2fce7857deca58e98eb80adb9a7f0def8d61654a83241a422ec1c00d521e61fe42c64e2463f9dbc46217d2123a491264f4cd2d4ea1901c39114581509c026e74e4018b34297bb75289f80ea4d637fe7191a05f32eac527706f7c630186d6f8f156485433c9c08cf21db67d51aa04e14b8874096118c5d08dd5c8ad1f23db160c5d9f8b722a1701100c905080c2f4451e7e5a891eb83cfe382987fc98875c599611ba1ceeae04ffe05de95de61b57adf2e893f04c7959911d0c24cb803358d3bb189a6a2a5c9947f1cd8a8a1c6b0b7131b869dea1e4b905aa5c92d612e54384ca3d021021e6c6a3fa8cfcd709a2d0dec52fff6d5e3917e92154cb59ce7c5691e53c0e48fc3501fd93615743b9cc99abcb0daf4c070aed760a093c04c8440bcf463cd8e0c2722c10a8f95bc8cb6dd7e0b729bf4e5aa2ada456a5044d89092a66b029c9c30941177c98a3ef1ad7b08de27f5370bcd7c9b9b192579b21596b5bbf63a54a37ece23f4d2d2c477bde18fa3974b9dda3e520bfc121d4cced09dd34a5ef06b56d3d3b96bc6ed60d1582554f6fec15d5757fedee1fc0ef4d5c57d4c82f52128bbd778d661946556a3b982ea568acbfc165b20313b9463d9e0a97bf205a6fd99dd9ab44547ed486a2dd2c1e0e1e16de73125619130b2908e3ec4d298d801b96e23a9017e5839b4454bcb011cc0cf01bb8b8a70a3260b02d9f412305cece6f2e2c9907e37976e1f811e9641fdf5eac2389a51602f87dc4a21f31a21d0d0fd0517584a5b002323eeeb68c47aa9656c6e03c0c4cad37d8c06850af8d2948e6b3a929d172ad8989725222699d1ddb5d1a380dc078b7052a97d2d263d9b3167fe8e96a7bbea99455d57563771e989d56458a321a344cea97a2d4ce09b2c728c2be3f1f968e95a367edde6a0fdb9d4c570356d89fdf001f27d69817804819e4a6c587e69c16370469cbb4af546853d5dd044894543a6329f3164c06371791477260b9914c66248149a892a8e485438c32f0745a06453da338f438493a94b460880fde51b8f4e7829239ec8c727a90ab41dd4ee84bb53c007c41f1d534880b3335ddc04fa0d9dcd4131cf998c44474a88fe69ded30cdce3760cc2e8cc25b02bc576b1103c1145145d41a16657bb826456cf5785b29a2c1f5bc82f645aab96aa098c7091f6fc826488e4e984ad564bb876ae7d28da1f7110836926e80035c8a06bd4468b0047709466131a59aa16144d5c3d9b4b2e8ea25256e514074495a900201ed32dc5f94e59db4c5f40cd07c3faa96dbdb10f237b3734232df47629c86a214ee87299412d6a15d83568d3ce9edbce6a2d8b14684d10696fd7d575a96c33bb3231d825bb5f07c8006e347a7ed4e510c6bc94cc65825112f70aba5c35dd0ecb66d77b440286d49f685e758eba6169a0f28618a0a1b2bf439685f19c4985c5ce890e158f30f6c7d81886cdc9951a46be0b0192058bc8c19d580588272bb3122da4395357a3304c168669a1787a877258a47e60dcd12f3d48aaf0611839e4ddcd5e2ca52c85359173eae81edfa4f73db927414c6f3f925906f86f2669b88e66517a418462438b29bd06fe0d629e6192e45a99de236bb2b4f0c84c02fd2f843298265588d7cdad21e303256388acc09df6f364b2a651a39cacba0421f4686c132bbf894e574e208b917bde9d0381a8b1f0567ec5872e53f36e1ed9faf1bf4f127863f42a5872c1b6e25e0208928b4554e4b08a30f0675bb6bb82004027cfa887937abb6835f7fa1e3b734aa56c94038d1279065bf7a3da7db53c9cb32803c6d4e3b12381a9a246e649fae618e624aa59fdc042a3cfafad44ca0f4a73b2f4ab66b186c36d40b7f20d46d3135ad7b80928cdf6cc406f0a9cc9620b2ba654a0fae8507bba6e73f41642e49a0cb0f5c9cbc073b05acff1347c4f7e61c6d05eb012c71e601d3cdabb61ba8e550e24ab154a57abf51dd3f3a1612221c061177ee8c37be0ddfc543d0ff81ab0744fe00ef119e25f3b73feb6e64691f2c424e550a60fb2b24a446a7dac88268c80d1b2840c2d0573ed7f9edcc671978cf8069511b45e509b27d55ea903124aa28fbacb5b37bc10c7e6957ae4c77d85377060b8e38b1e8f2c0a4c495f01c0004a17ca6c7d01e1f8e2e3cc6f99d123d8583a0337662e6ff204ea6316ed0c369200ceea19b98217d2407c8d80dd54937160678a00687413db82933d047e0b08cba413b7523c0a007d7a030d023373103f8380e95a137b4136c240cf7a01a08437a701364988fc541337a0374e24687a11ea82161a0076c5a86f2886e949da61936064379528d05022638c243a81392ca103c124ab0a51269b73b933ca35e94f3cec13d0e439d62c91c21106062220a14ddab78003b9987dba37af05e4927e34019235122f4484d810802e95e681cd623e5e23961cff02876681eb50764518403a925389240d01448397b4a0fbd47e98038ac97cba1e7e41e8987ec80bc666691e88822012548e2a2f415cc33ed41191031a0aa64baa2e7fef02871e29c60158787d2c538cb0ee2734d0c9d72bb9edda3e8d2984d022438c243a891b216bd85b150e85d4c8f336773d4bd94ce1a5c2bc79cef28dde5656609498e200eecf01ca4613fc5066a3c1cc8e11af4265a4c1102017e2c8eab76e55fa7c5f029cd920bc1a79ca48af0883441110064a66a848644bda54b03a905919e88fd42f6229ec80ef3787b480fbb27e8621c574fcae1e7e81e277e1f99b2fe76e990ba521e2b16bac3e15b4ec613767170919eae47cdc139f21ea40371ad1e25a79e83ee465ff0d789e4ee593698404a7a500fd34cf1e7905cfe72f85ea42f119e8d110837aa0dfa4eaf189d9688580b4a2ff8bad4e0578aa4b0f0aae6ebf016eac08758a88b269035d3db60d9bd44001794626fa7af96bb9800a71953ecee17c8d1daa8d6a4dc857e4dbd266bcd88ca24f605a55091a74cda9959622a273952a4c4af6949199531e12e80cb58d566fa944e0b50f892eba2d768d294224a60968152b4a9b4a20466252c4e97ea42d39253e682b64c97324519353b680b75859b5506ba3e0a7257dbdc4c04290f14abce565a9984b4f25774ded69aa901c21204732c2f6818971977536ffedc66a0ccc2f7d2041d00cd69ac3bb5e747c7843b4eaf2b2f306ccb7daae670936a9e6180338177950e88a2df59cef3b86325696aa9233e1c53a78fb65ff4c1e90238c672d3147d1a3e323d91b9859cfd21f3598d934948e61b70ebd5ebb8b4b04226551e7155cc5e5215beab8a281feec5bb86f39f580abf0cc3dd5526be93943e1d2343d7025d1d7145e7cc6adf58f8268715e4c2519160a809d36e17d7dab71f1929fc2f2bf47f16f82f0afe070afc3705f93fa820f2bfc23d2b65769f5f32ec4e1a2a496a6ed244e104c3fef0847f592707238efc34bac0bd062ff552cdad642a27fc25659d44587c7fc445178472255a48ac0bf85b691b0a5db65fd9aff0a28fcaf0ac878efa4d1779aa279778845bbe69cbb9dd614e20bac9db813adf58ac7a16a37b5ab0c5e3f43fe71bc51267338b3e369b40dd181488ca8edaea2ed4fdc08e8e510a20585ac6a45dce827cf0c1d52aba30f978d1a284fda380e41ba61d03c2f68ee9fe2a3cc7f5bed70e73daff39d49b6a1cc84e69419d90f837719c6459535f53dd19180af34d81267ea6cc0687cd436256ffefd211d06a3605471a4aa748fca03168b90748e1574083c3c2e8360f83e3add23fa205dabef7c688d4cefd88979fcd200631a0dbe4794c160c4ba0878056e35dd114f5ea4a0d891b1819a998bbe9374f3b5de1b14bab51432747e55aed408472ee11347f5857fcfea23fe46c6a850b9e792f16f14b128d2e13fabab45a42fc640d15717974a4168f56ae70a11ffc3f9a6f472561c492d18e5386c2e887c91e157164b68b8031439010c14e1285aeb9938e47138d0c2277ba3d81492bcc445977b39e8013c65940afe7966af6f4b12770fef1cc66e0a15e0ca737d85e47f1e6f03c7aa893aa57d134fb33b719d16be5bfd35e59542ae463333622816f66929ad5ab07de0e9aef6b75ac604046839760be1093a0242edd12d05c675ec38c4f00876499f0ecb232340722e989f8880d078c258718e8ee2582f26b36c0a360075f9d09da7bb03e4f640350385ab3c6164aee53ba608a193c18cc77117c0b5064e538ff8d1436114c305a417857560e1f1d0d499b2dc223d49a731380859b5420b8f2326ec7ffd2776b14520a1f6720c399a5a5f9775c639f0b1648c21664b7968da606d0835ae904eb7f2cb8e2e39c5cc61506c2067a477dc60db8338d35f8a5a2d64d5b81891e97de1916141d7850dbcb0a7a33d0bd161e47eac9762adf72a1a432e2473c8d63f1e6947918663add0f6090ef5147eef1b0c326377a649e01bd14bfe620b6b1e33b76a8a02a6ebe83de695943308b8a788acd338cbe7a521671f82fa2419b8283a4c28bce24d4743bdebc18f144f7fbf0afa12d1ee549697f1b18629ce0c4d3d43738a3cf49781e0050af012ab111d8c141c19da5db646003645fe7e7b57ecb660ad38d81a1100bfc09c0c09b40221c2e32b6ca8c7432f6e09a17aab0349b97ff64b3a13aaa48af34877a5d7956baa7132d5ce201e635695b7dad9cb56a7a42cb510eba4bfe60e8fb6116b3402b7bcf4d65afaae9bc605587f19865b9d567c331533ddaa97ac9a0ced0d8aa07d850b2b1abd1328ad871ca0b3e66c088a3b9889bf72daf84f06d9a1a99c49b03f371d0ccc4a6f6570c7a5b40c85b6b63452171b0febe55be6b256aaebc8a9476a705013accce50e08e117177bbbb480ca6388912d05d3a9f43f45db3018a3de9c2181d0fdc6d6cf11c050a194a091b0905198a440d89051985448d89820c8584a684c20c4582a6c44286029998c66692196e033d7ca4fb5eed594d76a7b4c5538bed7fd0a6a65c743138edc3a2b04fc1f69ac2cd1084c92c06076c709cad6acaf5e3668616ba6393004b056427a5754737f4561de49b8686218de9621d38de4ee2c3b642c14a2435b63ef1746b9072a11639f5607849b84583f0afff0466ff3e7ccb2fcfd65516a3c0c5390024c2b0ac470560f99aee24df361bce2bc0bf2eef1c50ac91ac27e4eb1c80e963be36c3a74b19564344dfa4773b1c148ef7b4b7e04d1fb1e5f4a27909a37fccd8add1b4aee0d239925fe4333138d3f7f0b16a4a7921e29e131af3b86d235f03f3b5e122aec8983407e59068a0ed2ed98310b595ffcb40c99a9bd72e13d05c30aef074e24c3ffb269000c03c0eeaf17f58c03479a024f0c0f6f56d3db5dda6b3d6096c8cf12fa98b65a9f1aaec130455df908377090bec771a6c66e09bcabe37960e627f9531d57b8d2b05a51712604481913d3852cb5430932a5f978863c25958e13f6d206c722eebe148ac973d721c39ffcbf0ad5e9e9230c1a3a05b10c1c0e1bfe5c3ccee13f3fe4fa9545106bb23d36e3557f8f4f742500416adce2386b1a05dde61d14df9e9cfc7ef1e14fe29839161073cb152bea52b88409cebe7c2ddd27cc771c7ccfbe49ab7897c254f0a8bccd1c8b14a9cd4fd336daf1765d73e7eca7bc737d4fefea406f6c48e4ef3f3980dcea7360a24bfe07931fd7c315b926fb2bfe0be755d03edfab00989acca377596b2119338c0b5cd00de52abb9f51ea8ca5d9d7400cad5a790eb1eb4e51e0296d109b5716e10df02a2cc5185a3460baf44f7863f251c0a7e49e26a7f11613739852a7650bfbcd5cf44b09883137a736030e181511e5ab7d92849237ef9ec32853f97d17cdc3212e92f782c9018b7d7b033cbef6a5a752ed77385da624d1d95719c09f307fd12f2c370ad6a531eb5b12fb18881f329c0d532878a966495c0beb33dd426cd6c706673d9597d66b81e69926fa13e3684a40786fb819993fdfa9511a95b288e406e0ba99ddf75814ff89f25b4bf64a3c10b198e7f91e21449d62c747479260a2355dcf7ad89d800e2ee3ac4d4b54ac3e5c2030382f55b9705aa3c6943b5eef963052a5af766ed6c179abc29416e03212eaa1e0e24853a541619d86c42418dfeded0229b1c3af70fb29043f1df693d557caf8546fefadd93b7b22f345cdc3e7008efc9518686db36ee6c5bda34cd101dd5d0ce1684e2c8e04019cc0cb603473a4c70b33683e74c7e3811bf983c340e1c02b4ab4e431fddffaeeaf333981c7aa48df50ec73bd5487f9222945d1749e24e33fca03c25e69350876f06dfe78295e3dce8d2e24d523de41e82f55060a37140bef6c2dc1225c4fccff95969d7d72459d5fc9908dbd68f2bd0a774e6c6e87881a8c8db930f03912e54fcc93ee45a4cfd85f4df93eb6f6fd99cdf8322cdd0cba4692f1485b3fc08f70f1e0e4971fae35bc60e6c76d5d6c5541e038b1748feff354e2d38290ca6b2d3dec8e4b76d2c326c75bb1dad489bd02d5286600e47d08be9a951360b2e776850c79c75e68ec6f3c6aaca696ef67df33b7490d496f983a255756315144f2aff5660741ae7e68ed772a461f6ac6a6f9dae57ad1f05c6d6ccd8594f120ffe31287426340add1fdf31f351496a9551413e034593b8f71e5768a00b494d527c30f734c36243ee03436eeb290e47e0f7584396c9b94abb1e9e8b16045f86017a0e224bdf84e2d1ec80b0e14b27e44ba29199e281d24b5edffd011ecc77be7eb7cabcb2b24ff6ca6a7ecc34ca93b5bb8c13f4e4241f2062afda83eb045bcf59767d1371d6a65ee2dea83d31c0a8adbb1fb9de464adc6bf1706121f139243bff61c0a1e34202a61777f048b734131c880ebac12fbfd06d99c6726305deaf3c30a5501949dc56eaff9a1efdd95d7b8aeca63f04fdab772e80f6e6e72080dc383ae65718b21e299780918e41fe497c31fa8f7b3c52d080cb04a617db6e0b9fa482b3a2f175ee6bce59152baa730be0b6d3066205d14aa4087927cbea80f4bf1fd9f53f0c4b47be469f21b1cf2c60cfff68c794c924bdbd18826f0051c19448b4d3e4678372adb90dfd25295967e419ff89ab18e79c0e56c639f48a4e49e169f3bab387d0e3c083c5e0feaa6dc9d187f3af0f0f1451f2f721d32c1abdd576520a65ec0e6b1d09e048d27d7bec47027a3dc47d761bdc5c67e55d976f661096310a8cbca70e4bd0a60f4e399eee73f1dc929af64d5ee495dd85b057dc42e06ebc409846e15ac56262d7b85d6d6587a2807cdefcd9680c47604bd6091f850987bdb6b00dd8fda31d996e7efd7c700b0901c4557d7eeebc1f3821f9cce264a398e35f5ac211dc62a0f2efa120debe8d2ae604b78705e139e0cc20fefb4b3703ef3750be089ec2fbd585de8f988140ea8dc039742a0be4906454aebbda54f2e4b5a7397ce3d0fd9a7076f50a21fdbc426d60fd064d2faf5fdaeeac72c85c26234e7181299685f1ef65d87b710b4a231d5ba2c778b1948ae9c159441ebdabe6ba63c9e6bd614b335b6d9aa192ccde364d71405b7c8905dd1dae891b2e05a828d3d5146e161f485d23810b8af72e601fcd6aaf4cce9d04185abd04d1f4f1cc7d2fa3dc9ad6789db8b06adc8a6a0b78f66f2d9720659a9c2e65fc05e133057afe4c1732920e3a083ae6b7fc8649f575d15b275503038f542770f3f496a4b1bef55781b4ec4f937a6d242e7f0c23fdc425dbba2bb72dca60311a71629a7a9a7d072b50264588e1c20a517d684ead354421d03b5485d7f75dd36ee7b9cf4aeb3dfc02495cdda71d3ba20f9311c624068003c38fee488bb833575a41fc44abf144793b12f09beb84f0465a38ced1e1ec8fd8c6a61b1f3bea169d67a4386b3ff0a21613f31f8c45cec1a385da41a153ad732a3f6edef4bce747fb5634ea1b0cf4ed2797742b5bd8b303ce95e338c60d1fc879f36ffa89e23748cd9d90a398701300b3c427732f05afb142fb6b7f7b9db80ea239cf5aa48f35fbc460bd9737bd30177d27aff771dc6b7a7cdc31fcb307857f1b42c03cfec849d2994c6aab3a8d65f47ac35776514c07f061fc297ebf5f7affd7d837d93c15fc4e47b151f829f37df9735192d28baea173a80670c394cafa56a6fa474eeb2b989ce885f40d8c9221245abf32ff5eaa4917d8bfaf5bf2932c9ea464001f32da26adfbe14078dea6b5056bfa882da78be6a558fd63c66c8dd3be829bce91c6a2978dc199be861f3f8dafa3129efa3aaf68c71a8fd711af02e0779542dcf0f5443e71ca39c4103d52e42683a7b2108eb98b399e0f3bbb0094ce7a8c41fc9fd034bcb18b70c577e821a29186a4f4b49036a012a774f4a094143013636464888d1a91114364a02a5a16a868a3aea98e8a4e0270f324778cc898311a314446a6c8a8211a31454786c888111b31464686d8a8111931446386e888111933462386c8c81419354423a6e8c81019316223c6c8c8101b3522238668cc101d31221fddf92e6fb764969b20db8c69d336db2dd8185d13d79a31d534a5f6c06675b986aa52834a89464d49a34a49434d6943a54443a5ac41b544a3a2ac5155a2a1a2d4a452aaa152d2a45ad25051d2502bd158516a502bd5a82869509569a82a69a89469ac9434a89434559436a04ea3945c13e90ab9f2d18de36aa34bc6358bae060fe251dc46e09c84f288ec122fc1e7bb7cb491b5886ce70f72d8bead922c324ad14d651447a92db89778140e2f0d513bcfbe8a55d6e172b1b8f9b961f61045ebab554bd637b8cbf7f17bef62d88cf21235936f2a3ed0134eb5cb7057502321cd988f5d148de285cf8d9ee092e13e33e03be026d362a3aefd132b4f9c540c5af3da01376e542a9633635588fbfbf1e0daaef87ccc6fe6c6abe1c49cce7dd9f912d21f081bf9aa7a14a9358d364efa8f76173ef218b21aeead37849768c62fd304c4ce512408171749b07118512ae336c452728787ddc46d1594e511b47b6d41bf181abde65a78b045b40c637cafb07bfadbf5669ad1331a7a77972e571b93104416a682f1238df1730abf98c03c107eab5e392252ad0b94d2d4d5309ff01443ccd5b8219cfc3898c594249c0173a7b2d32dbbcf079ec36f2d4cac210f2a538b0bc0d2bba2ba1831aa24aa01c3c62c138469f682da08abb10d7a18736244943ded0256b080d8e7baca4302380281b687b558efb619368c709f23cc39cd8f82e8503b1f653d202dcd9dbe295a558da37679dd0ab6f3383850e55dc4b33986642324ab31a3a99e193dc83167833e9f16fda0040fd42cdca1cdb4f4b2ddd8835a922a1b4205d0a689f43fca527ee736021865ba87cf8b69e9cbf14b424b1b4dbee25c4d2f70b0f444844c822a4624c87026c7ea7b28167afdd38a411c045ad0f4abe6b5c0fc944c665dd82c78fd201f3d97abc9a9aa99cd5bbec1d7f1d68977ddcf7d157c4901f85b3d26c0c67972aaafb5a0be8610759f64d3d9d30138a64f025ce3ec9ad375eeda65bd2029b40ada5ea9e3947479ab996fd004672bf696c709d913a46f62a79e860c0a7f0ef4d335de7de54739314a558cfab52dc43202e47be598d714bde4107f89f72e90e248e0b296b053253cfce7c330667e606f66b590deecbdd163bca6a7d8fb5e32be996599253167a0dd26ec054c6f6995fea8655992b20db313b53b38b9940ee61ab4e202feb9042a0c3550b4960efcffffffffffffff8fd244686bd9ef2f5392b2a61591126b4929c994528a743820001000000000000000202222c41f0c02090c170c0b0c5becc1643a247921c66c26fe39baa0408efed163d8420fe6b33cd6bea78336bdf2d0bad211812e6c910783ca1d4f3e288df0eb6fc04e8c568105b6c083a3f3a2fbeddc3cb45085e18009e4e0220704acaab0c51d4c9dee3792c8ee748f71095bd8c11c45fe7c10c9a3ad4550d8a20ec6945157977a3cfc29eda8e2872de86090379feeb52eb7c8f4ecb0c51c4ca3c387f233df8f0feea825c416723027e923e455c89f4c7a8caa7c94c1618b3898d4cb4f8aa4f0252f0c07e36d7ef018699fe42819c0001d3a7c6cf106a3ba57049d3836a74e6e3089a9acff13a73618b4b8084922cef8ca890de66032bb635932f517b406939fbcd98c4b72923b6a3047086f274d64f9c86d1a4c5e72757dbd4727ada2c1a0d69f75ea84c8b9e8198c29c24276fe94ce4b3583d9545f9fd2749f13296530c91519c9e693ad9f4706435262ab4f8e1853790c066f0f992357e6455b6230cce9842bffc26056ed6022bff7e3ab028361ee529affd42b6f7ec1a44aa8fb18ca62e44740b085170cb27b274982124bc2d405d39d4e62f76d417cff5c3079dccfd051f62d182cdaddfce9a4823c212d98b458ca956eb162b959305afcc8a7d4f9f2a45830482d1d8b2d75732a573046b44b0d592167e78e150c3f5f61173fc41bbf0aa6ec174c352f8cce592a98ec43f8766d9d54750aa68c8f93bd74be2aafa5601e9d2cc42fcea99da2601a4b193b4925fdf6612818f44ecfe811a523c7fa04c366ce7f89c41e9d2f4e30a5f7795ed3fd4967130ca77c26e999f48a21134cd52ade79bcebae5b82b16fff848a97afd74a2598931c555a240879f161120c3ac1c37eb44a4ce59060ee8fe5e1737f2cad1fc1fcf12323b26d4630859df095345611ccf2f7b91ee5228241ce442a91629cd0f12198226c5ad07fe679ae104c95f54c07d9c92358648b2018ad557362f9bef53f50bbe06f0b2098b2cbac499cf907062176467ddcff4f9dfa1f5d0fd8c2075aa6cac60493ec25615b7b235bf4c014d1b4bbf8967a522169d88207c64bbafb84503276cddf80077a5c40033a708b1d18b584fcee68a6683f3918efc37274f1001d6334043c1046173a749cc08b044618a8942d74609e1d61a7ad7492a8a61c98a3790ede262388f0c1c00b1fdf630716186044c02ab0050ecca73dc48c08b57949f43d90025bdcc05c2905bb53297ce5fee2eb74172d680d94b5a07b1c5b20043b26b0850d4a2d61b19273747e98a38b07e4f0a24717950419b53069be29ed901523da4e0b73e75c91be7448b1f0ccc29cbae5d963254b77280bc39b1a9daaff218eae7868f90ef6818c58987330dd7bae5fb2ed630e64c0c27cff932cdfc2f456765e610e11a3e342f6ae3058eddd8909b9897e91d10a930afa63679f9583387968a5e0dfc70e309ec78f1d160219ac30859988b4bc5b0b1e0cab62ab307b9aaac975478f3076b4a07ba8c294266be50fe1fb61d74f5559a5013252619e207e62c8656c299df7a24717580019a8300425825bd6085fe15d9e818c5318438654f5eed71041640ae38c58c926a4b5677f61fcd8510a938ea4cbd7e3c5e90b18450ac3950c13dd4b93729830172940fd63033d1a5046610a425ba8505de915c51d68a454148614654384f959ec80d15f885195a38b07bc8761011d32426190617a3fc4962de98e307e1806acae24200314e6cee946f9470e717579686d8e2e1ef05ef4e8422d10821d2a90f10983a98c7c590c997561199e30e9a9d4baedb9d89d4e984ee81cf15349f0893127cc1e5288ae3a69e949f681dc8439d6ca3b2975f71e154d98d39d70b32b55b284ac8bf3808c4c98e7c6534a389f15a5420cc840960eaaa23b549bfe12e8021996e0e4e536dd735b39ce436b8c82289051094ee54fc152a88788d80f1f391ca0811c6b1f904189a2e9e753b773c9a72a750619933056fe9a7c3aad5ebc5e126689ee153d89cce89423612e61bda3f387f7ad17122699a9264b4d93f108c367899267f1ad217747184fadfbb9bbd5a7da5246230c263e64dbc6d404198c30858e9e2348ca49e79c8b30292529c7a57f8a30fde8e01d614f2da85322cc363aa493747f7a4a2c031126b12a351d26238730ea594a3db2645dd25f1c6418c29c32747a0b7f0939c70a196414c238a223899276d967db4208b3c58b8ea273490fe19641c81004b636f1476b1e5a2de81eff630c31541006ca2dc80884c9c4e6cdc44a223c90010853cea94787ad10e2fdfc83417b749892b9b6162e6c90e10783960769eaa3ed3a731f4c4aadc95342ef259955a541061f0caaf5463e598ab463d983215df987eca972d6577a30ac964cbe8f97112a98077387dcd7fe99f8f8919664e0c130f641896897be834146465facdfaaa4199340861dccff412d6dd6aedcf475307be82c4aa9ca39f92930c8a083d1737bc8cd074f72467330a88e7ad63f23b276922107f35e884e592f280ec65b137baf24f408b9dac1c1fca7da63e992fb89d21b8c9e3c68f7ce311e44fe61e0850f93e106435ef95037e2c243ab0b0ae4e0c2cc64b4c1a0477a3ef59f6383e1cc247f7213eba2a20c780758ed283ec85883d994c9ff6ea848dfabc1944c049d2dba9d46c564a4c11425e5522288cf1a55cb4083e9ce3d45fc988d561315649cc120e48775ca3e9e935da220c30c8668b9e37e9809292459190ce6397dc6e54206b3c7307d9f5354f590c33b051963306955aaecb19ae35b8bc1a4f32b5d9d8e7c275d45c80883c944cc43c9133d9d476030c5f794204cff05930a7bcd13394a38e1f182296fcc688b746b9f2e18462d5cbea7f7e439174c7ad4c5ddea6c0b26a1ae2b37cb3fc84d1a21430ba6dc5f4224e50a1ed4aa2508195930c976b0962c21a7bb601732b0603c4bf791d7cc52c298011957305c5e24f54f63f254ce18492920c30a86f49f391aaa5288e92932aa60ce0a72ec83fcfc924c55195430fa276dfb930b9ba1a71d0e46ef3803e3021cb8400e2e722c800b4b195330e7aff07229c8b7a519cb18a98231c0b81f2a4832a4609eb82563469d0e72291e5a581bd0a1e3bf18230c1e3e4a46148c22aa948ebc5ca253100fad2ffaea07188f7a3c069454046440c16c35e66b1d6f2ea4d2130c2e974e6ac8fea03ae40483fa129654a99b60501ff25509152c4ccc4ca8ae547abf1751ba0a838c25987765bb834a752b9f0c2598642f7704a9ff016424c1d47775e91dde420e1b09e608218af2cf1d65613a82aa1f4bc45a66873820c308e6ac60a5528ba93eb38b60904fcbcdd311a16c2582d9aaa2882482aaed55866010d253f420528460b6f41062e95c42fc9a413057901f7dbce7fc43060453f2349fdcf574e7ab1f18aeece3a4c976a32d3e309db20a69b154357444460f4c95425d479a24db4d3b7efc09b01e208307c64e79547ed83ae51db40383af2757b1f8b468b30e0c1224b8a80bca64f3cd81f952bf62a987ab4999063270609c1417bc7dc526dde906c6fe136b79ab61417c193630e56d4eb2f83f51d76b6150a72b5f6fd2c27c1d45ed47521647df5998fd36547bb794cc8564618e7f22273d6a7eed736261ecb6ce312d419cb40e2ccc75bf9eb5cfc944f015e632151ea9c7930a49ec0af3842442389d52d80f6d2b1c934f9fd4575cad9215e68c34723ce54a3a865661f2ac1a953f4aca3b125518b75754bddcaa481aa5a274100015069d2bf97b9e3193f9398529b7e4c85fedfee1318f1810c014c64f7ee13572ea9c4a5e0aa457d049b7e7fc713b29cc973c9bbfc6a57eedef6314e6a8efdfa2c5aefb1485f973bef1a4d4edc8974261d2493e88c9e5e4f97f5018fff4ea88542ea7843e61bccb6f59f96641c9c713c68b783aaf2b27136d3b6152b9634e54e4701da29c302813b35f56694caeb9098367d56c7ca74779356136e121cffb526cb364c2a4a183049d4ac44b121b430c4c387ea7e2fb7e09e345089b273c5e626e09739d8a31e955af84c133e227b71c4409e36ebec4512158d88a266138b97212f383a6762c097386bef029f7ec5d682361dec8394d547d8f1f5b4898726f47cb50217dbaf411c6f99422c7a9926b5f728449a55f9d2d936b32d1469863559820bc7ad75f46183f26f78bf96f8bb42cc260dea696ce53184b4a453c22bfc42a7122d07e233a840e1b2622ccd9c4248beaac58d23f847dc1a298e5a5d60dd1ac7eecb1be1004af969093162546680961d6339542458aa79db2419833b5624cd7273fcf04618cf8c12f7ad2ee1d2b10cebd7aaa0bb59f0b200c29e699befd3197b880007f209c72895939f5a6290c04f083692f9d105912d4a71cf6a1eedd915ad5f3d00ae7fb94ee1fcf83414727ddab3ba9e55478307dcfaf65adbf305bb983d192dafb77aead17e10e2bd0b083216fae7b1a1547be8666a051077366c8fbd22b67fa4c63a0410793c58f26ab9fb48d4acec178f94125f5fbf1de4459a0210773d2d193645fa42a21e30bd0888339f5c7740fd9534759c51a40030e26d349af7bd63911840e8d0c34de603629e6f55ba64507dfcb0d0675f6e7f3f5222eb13618a47b4774f31bf523d1a1839740830d4611e94423e6285d527c038d3598ccea3ebff64e1ae9abc1a075fe6c24e8ec8db4b181461a0c36334186ee130de61095a56584f4ab129a1c5ce838832988f853928288194ca2436a4921226f86476a4715a55106e3b56675527244830ce61eedfb97e2a564cadf917ca0310683f89bff5127c162e479685989d13d3ca043c7ee8086184c1b629d69aa84cef1941fd00883797d2c7adcd32977950b048106184c6abc76278ed24281c6174c4a3cfa9892f792a1f28221fce86cdee7b4ab9c2e940e24d0e042e948018d2d18f269778972910934b4609668a92b6ab759305e585491b5e6a9fb724fa08105b36fc44a5b1ff28bfa2de0051a57308748b1b81c618296489221d0b08279e2e5096d42984a9b106854c1fc793e63d4e477bd8d0615cc5a91429e143f29ed09a2310573db7bf67435e1fbe2625230b99e4c492bc931f0078d289854a5f0b127c46728090f2db31e34a0602a11969eca5efd12866f8241e309c68cb979b724eba28d5ca08386134c27e2f4464499a4e5eb1f3e920b349a603635fdd0d2aa94c4cb434b8c307ef8c01eb6051a4c30d59bfa16911e2ba8022ffa0b64a6051a4b30e9fd206ded92107a9ec30ad05082390451919024bb88706240035ce4c0c2458e1d2307062890c38a0968248106128c138258380d1d635455681c818611b408067b1b4d1799a03ea778685d40870e30c2200279db5464eac3c78ff7e1a3abb0406308468d5cfa3dc9503a4751173484608895fe6df3771dcf2e3b018d20143d724a96686152a4981c0e6800c1fc225e6f26ee851c45a4d64640e307c66a0b16497e3ba4c555a0e103b3bf675f2eed78feff08038ce2a20b0a1481460fccc1e3db6d97ed9a52a2c103a307115424ef1153a61268ecc02474105a27f9f98a2d78308e1c5b20043b0c0d1d9883299dd9a9f47869e5726034697593ceb332a08103b3e8c82167b4898d0f8d1b188436a5e742083222a86240c306a624e6b293f2ab8a6cb760462d0c3aa6df6fd797b7a96861f0cead6d1621b3307916895bc28434b9932ccc695b945aa9a9cfdf53e0bde8d1c58f19b130dfd8d97d52cadac73166c0c2a442f81c32f711fffb579864ef5dbdb8573221628110ec0063862bcc27cbd6c37d8e3265f24893c08c5698c7839948f62356982ce229190f397fca41ab309fae5afb1a4b156651513752448dc89c53617ae8c8395d825061ee0839e8c8c9729b883cb45e10468f1f3ede9502760a43b2b822b4bd4c8a1e738117894d61d27e2a16ee156f455f8a8256f36f719314e6586f9dae735bf58236c0c3474300edc7fb188591adcf724893ec3d4414067d51df57f663bbf7a130bba54ffa2397f604ed1761ec500ccc0085552ba3b6ce3de9cd9f30e8d415319268452d390fad1e6180e1c5056678e288bd9b9743524b4add892e881f4969b42d3f831306716a6172def85988f098b189f4551023a2dcea46af89f674c8ed91b5d2bd3261f0f2dbea0ec145b28b896b62cbe5fb582f61fc206f6a415a4a114e96305aa8dc31f7c278520b0fad84c172a7f7f653f75333254c27679d54864a934fc27059d9b5fb926b972909a3e7ac96f3cc29494ab3c248983ce89549a99d839b1812e68e574a4848595e4f7f844188b4a5befcf7cc5288596086230c162a05b9b9a712ede4a1958d309b0eb9b27c4c903f59469893564818613d42646711a6760bf2b47f99297915617cb7ece53a39276d61224cf27a743c214c4418f746c74fa974f2adcb671cc210fb92fe1a8d1375bb47183b1e0c2fbe00c30b84c00c4318e4edbb87e4bad5396046218c9e2ec7c90b12b7e56206210c2fe1fc6737f27778076138657943b28bebc4490a660802d57fe766999577185f94198130c8c58ff9091f2fe469158119803075a568a1e3681f25fe3f9877c4ab625c2cdf8ab2482d60861f4ce9fbe7fa374a379215c8c1458e2e6030a30fe6efb8ca081e29cbad1000c50c3e987445bcdfdaa5bc153ac18c3d18c5940ae55d421e5a5f7cd1cae38718ef7a305c888fbc48931979309aa90ff9435ed05f8cc1750230bc48011b60061e4c2a2307f57b97f52346abe0b2ca1d4cfa2b7d9712d926b3d9c114ac735410fbd3495533ea60ea7f4b5751e7629bc6436b7bfc0f2f5067d02167cc61861ccc5a396b8997944672639811076377d0cb155256795167c0c1ec92f38752ca6f25a56e0b33de60fcd71b51325d726c871c61861b8ced975208422e7495dec3b0c08c3698554689b8a69692aa6583d143a5ce77b9b5c32a6b306aea08ffdb486663a9c134124345cfef21240fd3609226bdfe6e25968aea0e31c6d8918230c2f8422d0339b8e0c2821968304baa7dedd2f39cdb9dc1204fc7529683853495c4436b86198ce1c1a25b4bea524ac943df8b1e3f4a1a04700d33ca60502e622968e54942725a10c68e307e5cbd8761017c0f030cdc1964305dd5d57f05bbf41909c18c313c9773c510ebbd62a118103977234208dfdabf0c6684218f936c5e2154a947c180bc2c324fa8b1f81e643998f185d37cdaabd4d11e39c5c73e0fd488d4d68f3154c033bc60942d934942757e112a35a30b66ff90ef2a82cb05c3683b3d4f49a58821ced882e1ecd63cec5f08c9453b5c8bcd0c2d18d343b25ec9bfda129330230b86116d975f647cd384b06088974b37e2e5b4a0b43e7cf01961c615cc292b2fa9d74fbf6d6240a9da516733ac602c79212fda45b7b95530fc8539935dfaf99ecfa082514db2e412c2623d4c664cc1942d64cb5e6af647da3cb428c087195230f7e4784a29f9f2d0225681195130c86abb30115bc3f4050ac63319212dc79f139223b787194f3048aab3902bf68356cbfb77d4cd70827172a4d42251d4ac844d305af6ae27ed9644268909e62b5f8dd75695f87dce58c2936cde2ff22535f3f0d11058eb194a30e648ca4155ccd57d20c04930e9882d774f194ad96501370309866439e7e09d9d83f4a42398edd22cc9c75fad3b19c1f0be9ea6efa7e39fa908a66c89a7ba5ad192e88960fe0ff1c2c4c5d0b1fb2198f65ffc2f485e0826b1b35f7b661ed22908060f739f74ee87650f0473c82d7651ff4377e8072639213f7d5a1493a90f0c923ea759e4f2adb007868fe149c64ede08f1c01c9e7368115e6196f31d982adf5e9b3cb5934f3a3047d07341acfff3b51c1844efabbf4ac8f3e2c0ac6e71cf2c04cdb881f184a4b90a6ded16cfb08179d443103a478514dd530bf35b4e6a42829f577968618a71ba15cbc74d8a6716c689dd72e9e15c5c3bb23089603e4a9dd0896f762ccc25b224b5f3d3f21d16a6ca9d64e991941fe4af30f78afe882234cf3cae30486a17253f4d7908732b0c566917fe4a2d3dcbac30e7fc6d1131cbb398bc0a738fd23631f4b643c2aa30c75516a13454bce5371526931472a689cffadea2c21823a382b29238c9db5318ad3a462a13f9d34ea630e9a06d94865b0a5345acf83029e49e28294cc2af634bc6e910c15118b2670d993f15563c1585a94549ac4f217e7969284c3df9949a0b6a494da030643d0f2a8e48aba5435ed03d7a74aa608b4f186342cc9724d573fac513464b2de6b9d5b4b22f9d30e9787dd6a75b4ae40b27cca3da72c871f73dd86513a60bb1f5a62dae989e9a3079ef5e76d0127cb49909537acb73212795d4041313e65035fa921ad1f9585ec22043435fcaa152ef474b988212a3c3695de97fb112268bd19d827213d94e4709732e91954724ad68f74dc27442a86a8705a1937c9230a79b4ef210dd9fe38b84f1d2847ac92374763d48982babbf87ee24fbeb1eb18523cc964ec77d92f47256234ca795f95d935b2fe41861485aa9ace65d7bd54598a4e99db419426a8798228c621756a7c487514f220c5924fb44ef9cd3418439a71cb592b4ea10a6b83db6fdbf2130b768d1cab4652ead4dfc628b4298c462686df7e4217f4298b2f36ba6a7ec41e88330d9554ace88be118404610c11eb1eefa9f306c260ed9f5288cb90e30184e1c64daa8f4af1a0fe832929b77822abe513d90f86393b216ce343f0601f4c25d977d24e080fadad2ffa2a035bf0c19cd7626fc9c512b73a021b10630cff06f88b3186bf7b21c96e11aabeea409892a4cf95c27d99970c08a387fdfacfa9ec808d3f18e7e3db999e117152d80fc6512b41d59fd28e90b5d1075398de53e236964a5e1b7c30885cf75c2a555ea98f8d3da80d3d707da2e9357b5b29f721860fcc43edb08107a39c8af569215a529f7407f36b956c086d32b2e67630dbe7957cb724d359540793ae11a363a6c7310bd1c170d967d474ce44987a0ec61017d6f4cfe4dc2339985c453da6a9e4af4fc6c12cda3b3fd22d870a271c4c6ea2d3be43aa4f267c83d1724b529c921b8c274cfd2aa90efae2d706b3be658a8696ef7431361844947a902a59e6ada4031b6b30a74bd515b265ddb4bc810d3598478d7a88182e3968cf38b09106f3564bca3fd34b7a2a34983de49b901e623b8c08d7c0c619ccbfb9f771eece54fccd600ad94dbb29191dd6eadac04619cc95fa5d5216b38e9892c118a632b2bb9ece578c0836c6600ecb96a6c23d6c5b2e068375ce4f579ff27ff684c16c27534de4b7486c0b180c29d94ad00a53fb4bd9f88269440a4a9bae3c29d2c80b0693d9cb49cf249124dbe88221e8d3b92a42481b03c0680ae05920043b04c0031b5c30965f690d35ef0b36b660b6f84bd331a284d88ebb2ed8d08239a88e3bf92b5c5229f3d0ba828d2c184bcb46dcc7a460030b86b73d1521238e7e4f47c1c6158c35ff93ecb5c27bb06105c35b5aadf5effa3979158c112e5f571cd111ff52c1746194704bb2d61d6c4cc154ef9244a52c6a44dca460ca9d63c288643a5de48e8d2898927da55f4890cbbe8b1b6c40c16ca962a9758ffb2109b5c1c613cca2a39998e71017d725c286130cafa124774932f1b77274f1801a848d2618f47a7256e4b4f37c79c106130c2aebf396241f021b4b3067b5701be25ffa4d65430926ab2fb125f2f9ec5406071b4930874bb4f291905227892a6c20c16cc983becaa72adcb54730ca0819da1f4f6eb6564e61c30866cd097a2c28d3e16ec74ed82882e932237da64f4eca430483d0718b223957c94a1a82314fc623850be2eb7e21187d7de4e71fa525ea41308fe7ff5c8b1772e10682f1f497f7f787e07dffc0944e9947b20c6556a90f0cf7ffa5f4b989aad90393940ffb51279ef55578600e3ae8abc85de7ced60e4c6a29b21ba29eb6441260430786fc25324d2d89f853ca812189c5f270e791431c074613263c999459b7cf0dcc95533f48089e8288bd0d1b989388e9d74125131ded5a98821c1711b2292dcc173e3e632605152a9d85b9d3e64908cafc52d80e0d356461cac92d59529d42895cc6c238f23f4b4c5de888f70c356061128daff18c0d91ecbdc2747f21f8e5b8bb5d21355c617ebf14b176737bd96e8539da5c9c52e22ee5cbd56005aec22025fd8e92da6fad630d5518fd629e50322b5ce42017c8c1450e62811c5ce42015c8c1450e42811c5ce420355261ec94e63ea79c15cc54cc891aa8309dc4cea7972fcd5d763151e314862d5117d47a3e1d3f4a440d53940e52a31446519e8485cb5022b2355d440d52983e94480c4b4945cee5086a8cc220ff743edd51ac4c0737504314060f2af9fc64a51437b3136a84c2d8ad233c9a8e6fb7c80f35406152a94285e8f727cc3d238450db1242f6747ac2202905c95e4955feba1306533febfad71c696e066a70c21427c95219999750631326375b5f0f8f1644eea0260c417cb0f3a81afa3acd436b47187e2ba89109a307f1a02fa50e8dab7968ed08c3338c1a983076e970952d992e97a44b98822993913d755dcc6f0983851a2f8dd851d4d74a98e3252d1d77534d878d12a69d302a6dc4348dbc9d1a6a4cc238e9e255565216c77c2cd4908429779afc9744867512f9682fbe58c00ef6448d489827e424928ddfa42c491e5ae97becf8d1e359b09030799824ea4da4ec11e6c83dee2647bda5be3bb40b351c61d0ca15614689daf945cb0a351a71253f4f0b2a5c8f8c38c40b35f1c36a267a8be8dd57d5b47fce3f114594725415bb2f11a990aa4a7b88502cd353d8ff1085899ff99fc2a8f01f43e47d25b372514d5d0874c364f6bba82c5912c20fbbbb11e635dc710230bc7881935a409a8f1a83308857909c277a9fe993200c9f71a376b6235523100659b14d29e521b4e41220cc2329d9087d3b0fadabf2c3c79fc00b87408d3f183dc9e7acd7c9a427bf861f0cf2438e5fbe776e8a3c748c1d7f35fa606a893f496da808419438f0450235f8604e7d2912846759630f268db8a5a4a907d3bd5dea1c467f7b97f2605041c542a98e24d9e478307d0e4aa98f91d3bba23b186c6459b724353a2b6c07936d6c7dc841ef8509a983c1d25fba588ce5ad4f07738585dcf13f6ebdef3918c4645d0b2a6639982ec75badd23befd6d5888329cd27a145298b58a10207539c754991b7fe428d371875b647b4fc93a8bde8d0e1e328a1861b4c6adcf256e6a98a9e1ca3461bcca3848b6aa76442ae285550830d869ca5a56657164d6a54a8b10653ba0dedea295ff6fdd460d02952c652a7a4c1204987cfc983d6aacba141df1271ce822829aa710653fa17dbcee5318371eb23c89972cb60b015d5d1a1cffcfa440653e56c5a3a764ce8f0180c2baaa2f5f54e344737a82106d3a5093a554c8eee12660c5020c78f0e83d9c24248ebbd81c19c845ed39e17e3a179841a5f30e41c63464c701731a04387116a7821e5797499d04a6bc1645564a8d105b56387d2bee173c1102496659bf42476f9168c9e62d9996fe4104cad854376a73bfb4bf9beb42c98d305616274f47fb1140b55e30aa5c30a26d5b1eb7c474948f252053e5356b256cacb7c4549b2278960badc22d4a082d92bf2fce4b97851b329983fa5f797381a5f9efa1380d11a10420d29983abaffd789b41e4e1205539c9febbf514a95447dbce027b041a80105e347f30f2afc4fdacd0ed0a1438c56810e1d359e900214c617e7460d2718429edcfaa726c9a90c6b34c1543a4e99fd67cb113213cc299cfe976012215a5ff658564309e648e24ee229ab245efe512309a6dd52254109a1a3e85e3590604e3f9652c3741aeba41a47308cf0fce822549660790d2398c3943cbd78b18b2554a308261549a9ce69259e85d42082c9c5c35625e9fd4ec21a4330a7fa5fb7e8f1bed52404c3c66a4c90e272322c542308a5e3d40082d1ce7b4cc5532a7f8a357e60aac959412ca74f9e92357c70ecee94535918199f7bfc10430a357a605017df279f9ef9d4411e6a8a821a3cb843e57c715311cbdbc13bd6313ba88935511ad4d04171418d1c88510307a66c8f1c793fb40bf8418d1b18f4798610f149c408196bd8c032959044899f6a61526d219cd887f34e39b4307d8b94749f2f7928ed2ccc5f29a9fb791031f2938539477db43e6eac592a16e693eb109f4c0e0ba397c66caca577af30dd785df0d3ce0fba55dd4580862b921eccf572fe051aad3056b2ccbdf83b2b0c7f15aa553b82aae8e1a145631566bff0b7e6f91e3e3f63c1aac298a1efd2451116fd3ea9307bd5e9ba1211a97562870a9375a8a99e9eec5fb1198d5318eb924ec943b6b7a417331aa630871a954f6eecc24a4a611ecb6184ec6adddae4a4408314e630757fc93d66466314e68ed9d117f7840add9ad1108529e51443e8493e312fc78c46284c99dbe152bc6edbf3663440610e796742dfaad2e5cc687cc2a41dcb72ca17b2165c331a9e309eb7c7f95f4ea576e7a1e56596011a9d20a94a965482a809343861b8966d5b99d196ed4d98fd45ab4f142d9684ac09f36d89139f92b6952866c2f02974592abd38359f0e1d582a402ec0020d4c98e4cb78773e8d147fbf84d1840e8993c2a3e87747f9d0b38461de45e6d4e95c258c992a736b55126f4b37d0a0443257497b27cf6ba0310983eeb4086bb92a95bc2561d0b1f3ed2676eb7838122649eae2fa76c8e1d4dec08e2a489824dabe48fbd3262ff608b3e7e748c8a5bc52cec411a6cf351f7354bc202906038d46988457b23c22e5f8acce0853dab6dc8e307eec782d15305a80c622cc239e72f1d1445ed02ac2f46a3997fc9c589c9817341261bafad04988b06c723444182e49af546d9e7f6ff1d0caea8202398859398d4318d645296dfb383be91bc23c9e6657475dcc948846218ca7e38d105751b4854d8310860b57415b9d059b5cd31884b183c91e51713ae48a82307f325713f39a8796d1088469b44b082b4ac7497e03c29423929022973a4eb834fe404a26c3744e896dfdf811060b780234fc600e793de269554c9b8f1d8d3e18b74e88d46a1f7fdfe5a1d58207a30a8c30bcf0e1e30d41830f9ddf965c0e41630fc64a9782cabda4603aab071a7a309dddce019bc011086375be09c2e2e5f4541c8030a8a94f1ad9911582b0e0f8030e3f18b4e55c7d129f2462185e10cb3e98736c52b2244c9f0e3e0f2d3e1894f47ccff9606501c71e4c4125996bf9afb7a8911760b4189738f460ca214b563b21c3d7f338f2f0fa876fd3bb923bc2432b056184c163c77fe15e98e901071e8cfb5a5ae5fd29089df2d04a1c773004131fcff9f6a2ec45d9020e3b18d7945e7b0af2e27b9c0b1e3fbcf0808f2fbe58400e2e727420071739b8c881a4120b38ea608a1d3b9f78132a8fd938e860ee94d7741c952dd5e870ccc19ce358debc32eb969d87d68502871c0c23cfe47dd0b4d57d75e8f0cfc1450e1ee847e188836962a54913bf4211e080834159c95b093e417b2adf609ec915ab457b4605cf0d06194ad693de65eaa8b80d06d36954591cd3fa79ac020e3698f5ee5324693bd1641fc0b106c37d2eb7eff7c997fa0570a8c1b8ee6f1d3dc56930c44f7fdd71394ea8af1801071aee0cacba8c59f96b031c6630f676a54e2eb2e33dc943abf4f821068e3298e33f47672df19083c943cb8a0ce6f289ee92f2eaeee9c760baec6841f69e5998470c260f9f45857db174931406f3ed78fbc689892c81c17479b9c3f75daea4e62f983bf642afb576b78d5e308c8e5e9eec635de04526679aa8b985e0f23d29f7eebd09511318cf824f22e0e08231e5b774db27b568150f2d1f5a3b1c2d0066c0b10543dcb0ff8d943c3f88f5f0f1c38b2fe0d082d97f634e46ee20546a5930eea458d9977416cbd233e0c0824168a410af48974cda573004892d419f4d2e11210e2b986452874a37429b028e2a182eeed4f87cf01cdc5275051c5430bcc90b1df9c29d88f0053fbcc0038e2998420aa3bd7b443a79f30e6270c0210573b797b650ea1d4c7ba2606cd912dbf6fafa161d01071412c96582889254c602f3c1f504d3bf5e9e7e13fa20030e2798cfcb249bf768fb7013cc6dc244b057bd70cb61825975e24dcc30fb9415c7128c6db2344ee71a49a2027028c1acaa9fa14b9cb0a0454930bc6aa8acb997bc9308120c49e9b14b713f8271c4e81479e2295cba4630cb9792f536d1502a5804a378d4906e7af31b311c4430afc52a4b5bf3ecb09c00c7104cee979428bda767f4a1031c42307da914fa93427648bebac0e8d001801de0088279532d8a8b100f7000c154fe963db4ee9e109f17e0f881e972ee0b69ebf2b1190e1f18278ea8c41c3da1e41c5d50e0878f3030c0e387171ec0d103538d4afa3016ac1ae0e00141b887705d591203c70ecc7f21e9ec974efae752a1000e1d18dcfd3474597ea7c8171c39e0628498267227f49c827f1f555fe0c0816926b589ad6f15bb51318e1b1827c8d5a95a87ac52e1a15506c06103e38fde7aaff4ff29be3cb4c6b8510ba3a8a9f825935289d87868ed00c38b13fc51417f8f1f1b58c10d5a98b4bba588e59cd5646916e6903fddbe62fa4f88b230ef68675519c98d58982e46a77c57e19d5f040be3c958efa09a57223daf30c7fb1ecf904917ae30ff5a5fb0bb54bb176a40870ed4e3462b4c3a459193aad4b845f0062b0cfa2ce877de5925f1f801c621e0c62accfd27c2786c0bb2df21b50eb8a18af77cdcd253773752b1375071e314374c81be197dd97451252e32cf0a374a611e11bf20ba43a43098cc52ea82a6a4d1d95198d383daf7cbe58628ac043742613825a9c4a2f7e7704982c2a0ce4479c8122e2995a9007d80dc27c80d4f984c6427513d4f3137e4a1652cb00adce88421cdbe28f9d7f710373861d4dcaa34b9384b92db8471e77264a96fd3679b268ca3d5745bab4a9f4a4c0e3732613a1129287f7933a14a4c9837d46289ec2de3a69df5e1c625cc5521858ef99de52b5c9630e68e7a8f5479c4ea540953c9b7dbfb5c5a41d3cbc30d4a18e762b5c40791f393d024cc7d997ad2f38787960a7a9413dc9084313c848e5c2657940445c220728cbf5c32ebb02a0f2d30c6e8210642c29cf2e5cc8a1c7c84b163ef5ac4d80aa9adb207a3bf3874c311e6bcd43941eefc241d6a8459b4edeacab47fcce70e371861083ae4ef590b51bd2a371661d2a1bf1ee5fdc24f754311063b9ba0e9e32a62d1449874e74e9f438520ea53fde206228c225b9f63554d7ecfaaed8d4394d2c68e9df9824fc18e33dc308469bca4694fd2f4c23b7cbce053b0238c1f3bf805370a61aa105dbdd408c9a2478430eea7393913c7362c83307eb8b11326e5c2adb225083b85651b6d3a57e97718146e04c2bca2729e902bfd95e7e306200c93c4a4d17ad192befcc1b093b446bc89a62ecf3d6ef8210737fad0c849c94f6afe85173c10a93c62850fa6d8e9bddd395cd27979282afb1b7b30ffc4ce4f4179486bb78f5223b8a107538ced7753177256a5f3603869123ce8cfa36adec30d3c98f2a5a82c3eb31224ae37ee60b438bd3fc9c24292af1d0c7abb15f4e55cb64e7530c9c4d8f2cff9e154420793e7abba9823962f7d73307b788b3fe24675e30edc9083d9bba429a5c765a4e8821b71308652b1ed52ab3cb48270030e867dd36e413b84879606da7e70e30d66d7731777316bcd0d26715742769df2d1561537da609cbfef77cfcb7f9905000737d8604c59cbd31e4c4810b3d81acc579644540b22e7932d0036b8a10653d851f93de64e32578ba5c194da479965734fde17c70b34867f0fad2ebe385e9c008c31920e1d358b1b6830fbe8d8eb9dac5e46050e37ce60c8a5239ea7860a752f1766308f38e9ed1523bfbe2e8369c6ee4d8f4f0653a820da4f88aca4463e06b324cbed693fe6e56a31984f54323d3971295f895a7c613007efd87e4a7a300d37c060aabfcd76d3cf7dc1e839beef4c8a1fd64230dcf082d19384bc2a32d205839eb0e517be6b74ccb860aade4e4975fecefab50563687a78c96551826df26e68c16e64c1d815d664ef3b744be80616cac78d2b9815ec46150c228b5cd21da2a794743cb448f9e006158cf3d5b3b2150fad2a37a6600cf5a3e48ea74e88190f2d07e37da0140cdeb5aa113ca98e75230c5020c70e1f5c704172004fb81105e3aae7453df370490f85c00d281852aaed98321397d3e3a1c51cb8f10483eca518da1d5fdfdf33860f2718d4e80b3adf4216251f637c21468f317c20066e34c134e2a183a9982d413b5ed0e38731c1f469e184e7f2d991a0173d7e8c15ec08e3c7120c492cb36394cd432bed86120c5fdd6982e90d3ba51e430c2666379260323dc923d77a07ed96c3700309e65a13526cc27878b8be70e308c68e9ddda35a2f5392cc831b4630efaf6fccbbfa995cbbc7186d811b45308ffc9490ddad93badfc1f02255750f30bc70800e1ddd638c2682e9eff358be7ae43cb6176e0cc1a09282d9ee86321381e1058f1d0f032f7c70136e08c114247e07f5c9a3e4f575e840c28d2020227d10c93b8909714725226e00c19c4f5c072b1dc45b528270e307e64e963ec792c70701c20d1f709d346488cd7c490f1695744ae282bef5899c1a6ef0400bf974678b33cd93d7706307a70ae592522e8d5a07565232f2eb599b974a0ebe3c72f1a24725a5180e0829a74a172265b871033cb5522ad78895e55d417dca5b6e687ab8610373a4583b2ea9e47afc550b731ec9c9a398704d1be5e02202b4281db330e8cd7ff4b17c604316a6ce12f9f631e3b3cc21d888854124f1756d6d9e4c070b634c981341a508512222041baf305e945339f121c9cb88d870854185bc973284522b8cf676b9e73de928e6af95f5c0062b4cf127f5fb4af4e52aadc224362de24d50a6ed3e5518f743fced47482a95a7c2906eb46a2bc4ed4841a8308df0604a4ddb4e544a878e1fa8c550010f6c9cc29cf19e233c8e1ad52553986eb55228d3a14bdd5f0ae3e5b22dd3b1233d7d4861d0ee921627a79476e9284c71ede53da8246ae41485496ffefd778d7b743114a6bc91f1b95c038579e62d7b50af1b11ff274c6b61912b76ef0953ca9552af9f8ace8877c2f4134b858b1693556c4e18dde267dda7f0a5abdd8451e2297d2a5a134e4f4d98726765d5259689ed2bbc9710db0eab63e213797625749ecff91dc7362e61d2eebc9ec4c52d61c85d722768f70f298575e898818d4a182de4ec5a22ff54f6a7842966f6f9aec8995e7e12a6a4245c6775bee62f09f3e724928a3f972174381286142b3bc456b37e0943c2103d565ece9c6727f711e64f89591e62e37bb28e30fde6d66f87ca766336c21463dd744af91861f42b19a62cc4fb092dc2203e2bc644cdb128170b6c28c29c53decd5f8aa5bce38930643353f1aa15c29b106150e9fd73def574933d8730dc8a4a5a627886786c0873685fe859901f9ec4b3510863e609d5c86b9f62458430f99b49902d6e1a4acd432b8c1f604420d9188439966a9e9cfbe42f7a4198637e1e8f53a8147126d348240c850281400c0231403cae00131308001834220cc68201896034941f1480034e3832483e36222820160a8622e138180e05428190200808848161188ea15892079a14f501c6a2f519398c78796186fd2b2c3e7f007a467d0b940f2b021ad7558c038d5427f5243e62f8995900774ee2ca7050d677bf8c9c906668c6a1714616f173a00d2b8f40a95785f23bb8941e696a4aba5558ba746aa1d5a979722babdc14861332299aab9a2f7c494beadba88fedabcefc6d8836338b8a8ac668c92cb21648aba8692ccca993d8961b6c8e911f0179e031b390ae523d2e379e87b6119fbf7d561b01fa10ded6f3feb170ab79afd10d9ef11a6ed8ce89e2574b971712efa61c3ab6cff3c52bafe198e349cc1b8918d5dde356dc023a695ba06ad9c1d811bae004b58b39d41cdd585bc8bd086ea0739340c7bf7c9c4175464534134a00f18756293610741a96f0ecbb93f282e294a0153b854e2cd1aa973b470fcf9533dcc432901be6026a556e5b571817cdee946812a1ffe7b19089523a3af971b8e4e9009f8299f6e8592f2fc841a655b3e1dce6f1f75d6712b1270012639129a85867adb5e7dbda1e800a7f70bea32b2afc82f09de67555a47d6bcb09058a63dd245e63a9c483dde4e2a1e7166d4891d183351c3dc6b62dc07a107e67bbe57099571ea32375d818a93fa065aa52011d07f194e0d827ebc100998a706140bbf03a1ff48a8f7a9d1fef5520f4321fdacbf171934d808ad120e023194d94bebdede1b4bbf31ab898d069fd55ed0c2077b6f9ee35c3b029c9618406bacda6e3c94837af848a3faf6434008aeac9b96799e5997a64aebca3c880cb808f4361854c2e2f8efdc1e0f9b649b6c253fd0dc7a4a4e53724eede8067958c9d3e25227248f2b4c9a8fac49bb6ab103c36778d402a2e08205c1d5413b88fdaa2d28ca74ead38b212be1050db2c7fe15c0b684ae142216a05a1a7ae86e657116abd3b4d283acb782a5f279da9bbbcda85877eb057a772e97ed9510e2fd2b93ab74cf4a26072e355f5851417df145c3c2a21075034b7606f3710da2c22a3636aaf8e35091bde86e4a2b1417d8062f22a0dc13f7bbf0cd20079d7e137348cb74e03d9618cee4bb7b8df0923d36dc415d1889380a59bd3009c17d5001aece633dd64ddcbdbce1aa646305b483d9c0d1b77a835e30aa6cb81e50318b3c4d873d031a690f970232c79530b05ddbd521eca2e83866508f2faface52a927892ed8d8095e4c11c98747203dcb19d122981e404ff967f5ff064bf9611c4811690758e9c65862806612d9208248a61e7f875875b41c26213adf60d26c876f4659b381d209640215890e1b0e4c497606167da13e093233d6a7c082f284ff4dcd8bac671465167c1ed2180d1b4372241ec9ec763a901863cb72c6e3c8c0b1df05b6c968b45e8b1ec64208fe4867e09997377c90cfa248f105537183a71060783e2d165a8f2df0a07627deb687a3f8ad7acc3be1a05346e55e842cd32f04ecfe5836cc26a9ecf10c320f77dd2bedb86e80d50c1d1302934064f219f4d363f94d46911c3bba80199b99322e7846808a599d3a232a0347025e6c5220f0445f45d291c9ffa092049e01bb1d82060eb88ec741a57103388679dfe6ded55ea7fab8fe64d2eed799526153fa7423cbb3360bd853c1132f620ce410e36301685cbc6510a5e744c05a5111c990a68f51f8e1c94db7b54998d9b2c56cbd39e3135e87145e3df3f8d312c1d472c07dad9752b94280a785c80a0799b1f314166f54f760e15dd34f49036a04c2fe1371c52ebb294013aaaa033e69686b9f33927d1c3b2fab922236e055d3fff42eb2aa9531ddd2e4ca8fe0e142397195e977593f154b7050d7e3201087e0e85abd68934267ae0888928a1908c8815caa2d2b97610f900a64e4445ef524306dffb982ed475cc9844c8160660ccf114a179feb8e8e151a506a73b706cf7406a53d8030a3f82b0a49c272885091adb1e9e72b8a9dc964093d105d19077c4d6a033f297954408ddb2981862dc409bb0dc80fadbbaeed1ff563620081abccad6ec715efe4eb1dfb64a23b3667859d0b98f90489e84d769d5395d49bcbe4e2f2bbeb12ead259c6d99c69cd4576614ba4e9bfa40118b20c05ede01996f101d8ef3979de0612368521b4bc5e5654c8afd73dcbc09de96573d6272c85a97ca2b2215a1486693579f7e8d3c1c83be880a4bf7008e0ced6bb4aeec2eb0c16ccc0d2b5633ff2b90ee9c59329fbfd7ffa6f5f8e7f52a86c300782f9d9cdaf96a043ae6101cf5c2a15632c54ae923aeb76552ad4ae9e9ce784a85766a799d8b11b443aada2a1da736a3d1c464157f60ba7c8eab410c190a9db7a8ce92b68808217647b9827dc3d6eba98f472eed73307c7d062f649768fcbdb1e0dce21ee2dfa4d5e2066b67d9a6d0fb64261eaf59298a2079c0152dd0088e766cf807550eb286b53c6282a95ea650ab5477ff809d8cb06bdef8080e0074a3c8441bdf74ce87f482100c8b57fe367ceeae49abc1f55a300f65c189e201f75c5fe642102d46fc83d81a82132c532f1d82d8d33635f944906d1f1947e969ca4c4d3721440ae373617c2750376611c1152699c4042c2f46d641d1985c7088c8c69408299d2d2d8b50c498d3edf31c27b3ee66cebfe4330222d9f9f339f8eb243a98bdfb868cd0525e272cb64efa92e4b565d9471db5523e82e4e144a75ecc7cccd9833ea54172c024437f1177dfb0ac20098c4362283155b7faf23c3b2000e7217b2da8078e5bdf311a160b611b94614d652e62ee0c0ed54c7a7eeed36c1bb95e08a59f2f74c449fb26310e3167fa861ff6a14421f87dd6b59f23955f603c4074a9db9cc07220af0cdb56013be214839105b8314040268a4e00ba412af21b23dc8b9b485688486e6885dff4762bbc6a120c04c1e4d8350be5807b420290eda2bd83d19624091676f758109261684e5bb3e5d0039c4cdb2d3bc074b3604983f9b43b8714ec081a80ea54cd1384010044eaa8a233f344b0700025981705cb7763a5584606e146a2fd01eb9a6d1e5d1dc27a02fbfe02f24208bc72ee84d599008bace6e9aa1b95352def3da04981a77c8956aac9939a78282ab601f4b40a42a7db3183440ffd3703001964a33df5b59ff1df0ea30f1b1f03a590557a4691d067d0c7665bec0895511c5fad4194ad5c594688b05129c6be2d872877ff26b1ebc7be0bcd69c9a06c867b2f63dafddf2d6b911b0d32d2d917551873332493351910839f7bcedeb9f63a41e299de4e275801e1a070a1d3613498e34d72312a3612f77233a4a7fb589a5adaee36fdb7be0b80d17b09fa595d564302518b68c6d62f57e7e318de3d5441d12c5c0ea02eb67b0cd09c2d13fd3b81c1ea41121cea34990b227ca065e88c8e12e9f223eaa731235797cce0a882b563d4bd194cd34dd87b5cb5a50c04ab6ebe381bf116180e505f93e31dbcce7d9eef2a7b9dd8dd0255a29c3ea1cad640d1387b8c63a923b2020744a7d00d21c8b0dcc1a1550328f285f81f976a3a35b479ce37087442f438b760d8d52bfb1ba5df3767b7c367ea07076cc81ed9c0ac3f5c708463733c800929be84f0d4194f9776ec27014a560a6907e79089a2a996a8e005d02b54ad34d87587f9dce654a7e744fd39439d526bcabd19a609bade44262e1ec029b56ec39277882bd714565b1ce843869bbc13c1b9332d7632688a5ee085d0a909d96f306ff9349210fb9aa52fa5d3bc7d21e3da23af29b2c34fdbe3b695cf942b763d49527f576af05d81ea2eaa28ae66937c442c62c83d6f6d0476eccdcec164a25e6ccc31b48e006ef92075615d2e5718cdd3df4244533920e3e2122827cb6098771ed8f8b8c7d51c6ec4766b3a964203ef1cc90f0a8e8a57d68e463990acad455221ac690ee8589403ff2c2153868a178c89439857c19b62cc99091f4b9f766759ca4dd9eacf4466bf22a8e8b4340e7d044d54971d1cb0c1db4e00219e83f50ccb63f1171870bbd694c3ca837a0fcf12dc7139597b0b082f94046ce53fe1b7f82a265fb5a43e91f1aa64f4368a17d27c3be27503bcd5cec03a67991367a3458ffc8f71b13fb5e290d85686c001ca3e0fa56a99217ec00858f0c35703657404400db5f3b5206749d4072394c0b884e32828ddd358272b7ea161eae03af361e2bf786cf4a459888d4af907544e046e3aa973c80d9ca5a434fba5035008c4c958f21c825b68cd057954538ba4d20d67f2c17d9bd7cb9efc8e5793942418e45efd937cc7a556119854dac110e2ddecbf3c3d1201ef5e6c8ba4cbc10730fb3521726ac57018301031d58aa82e648766b4049be4f5bab40868e4199b560ceb3340d5b85037f49e07dc939f320052e6d6cd283d2089b0b3d7d66eebb15e3bd50413f302f45ca704359c02ffc92e6d441404d61656e406dce4e0072be89558d38d4dd9455ee9ab5666fc2971ca9a0ee72905ed607cb81a791c263cd9bfa451958e446a9776aa3e42c1b5f2cb15ab44f26e8688069c860591adb8089b01a831451703f37c311c76357e868f0ef9ccaf236004d3f9c43ab6a07b9895e51b9ec2c7248b85a9cc63a80b189d1f2a3807b350846a85c958764aa67541bf24a7a8069cadaee7e571b292dcc80c7b4ec12276c91ea37a635808325821f04a903f50ea2d33cc868e74a0cabb201f5a16503c86a0039619f105dbe608941ec405fc15345881026ed054040d41a9ac97e6f796c0eb990117bb03326f39c5d6f95f85cbab4471b0dfcdeb5ccf5fb8233e4d71a2413c76fca2694063c9629c149690642416a8432729bd6087527bb2824211e40aa40ba422e11485df6505bebf8c41c149ee91b96c6da85186a256e2e50ae906b0ffa21613952a552e294f1b5f076274485f4772e0ebe5b2ca381c69ccdce54d46829b407ab7d3485ac26617a08ad59a28ec44c27c1f214843434b98338761d2073d2b50cc9a0864b2860f49d56a92f1064f9f3bbdc6fe62a3397172899d10a74c2873cde3f259230d57a4be8cada11e48e8ab66cf5f94fa33409c5fc1f8f6a252d4083d0ff66b1c692e9f794e6a617d316586df3ef6863a7d41aa040407dd8ce20a43287af36cf7625ed400b8d1db14f0e7bda32418aa03c8c6b714cb4c2665ea00d3ee17ad218f5a64fc1aebd835b3944f43294c760ec59a971d6b5e9956269d470d73692b137797401f4eda22552bc11d83f5641a2de431637da6b7a09f83332c2cfd55d4f8748bc39931fc6bb7a40b746605bbe7439c2e82589135d54e4f387e212f39432a856f24ee8881200673e124dccb10eccc28f3622f257cb70eb8d6c28e45c1e4425ab09ae90c7c5bdb6b99d2050a8eb16b72fe096f52ba86019e7c0e041fee1738907c0549dab4e352f598d1f747223e45f0e7b133a0ff8d5c33b946e1c3b503a4401cf3e39defc84c02add5f908698d729d173a98854d630480ef451581907eb5833a549ceaf4ab5f662ce4ff6d581cf3bb200651993af6fc125d44b415e4b159c3c24797a13aa8264aa96e7eea360486a8f904503c2b413aafb5051971ceca39bd6a9dc25f00662e7d39520e12e33e029f2b936fcdeb76e79f211be94076c687ea3dcd4a91790bd556d8159117de70ada5f96f54b4b133fad902aa136a8574584b9d6b62346018d85a460e6823850af809adf4a3aece520612c4cf0bff3666bb92aab50515b74e03ead83ea6e1733e5e51365346ca80295886a75bc921cbc901165a2b0bc8581cc02b1c12503d902a698fc37fe3542c6f5db9b052d79b5baaa9910bd0dd3b93045c0ee6634d041be23a2e4ba5e62aaceec833d3b0399afeda153c888ea2404fa1f9f5a08152c1582386b503d87c57560bc5929a424daefd06d3d51f984b9dbb8a3305ef50a890c582e6a013d91e171cf283f0cb7e3c3e4c95ca688a00ce755cb8e15a39a63f8518df1748cfb58329a74b48a9f813441517cddfdd8586113656c94363ae4474b9b36c5af7b77c00df0e1d9a8940df9aa007fa063af51d68a472afd31d6fb15d574fc675bdf2ad4cb34814bbf36ef99070369a8513a470cf507fa88d85c330740bf2868560439aabc8d8b13a4efcc9fd0e1a85e8a064c961c8cf1f5880c1c65fa581f14191d87dc43af7eaf79f3723e6030e6ac9af9b4be0881592c0692c3cb17cabf0eae3e2ad15bef69688f4c717be1d70602857c1639ed4740ac7cfb0febcf90b7989556e93e1778c82a0f189a92237d7f5bb00d7aa803339424740c619d185ba3d2d6cfe72d3fcdbb2473d3214dfc18a32f6237a6172a2ba329030a93e6338d8bcd949933f1ae8a81f340af9b06d5a193a8abd0e2d42d2fc1e12d39b7d4414aa90b6dc467c02835e0320eef37feb9ae39e53003c08445b2295742c64a924042a1ef1f326628e9bd6150b92e96997bfe356495f9931c72edd79df8abf8eb55e91e22862de1058541936a0ad4c4f3365fdae44a9b59ddd9e467ac0c3293858712fbb58013d129230234e1b226afdc3d176fd56039ec97ebbac14a44fae56c90dad8f93b1fe318c10e23f38c2d3dc2badd5e1d8c5be4ae6c3dbd6c3023c7559e4b61b58ea5241bfc96fd111d6ed237b8b4c82d4d662a25707f9bfa1b6050ada6d4f7de28ec53063c520072fb28615ac1d1abb600724213eb012e2a07805922ba6f49c6216b0d507ac230aeab3eea6fad66885edfc15f4ea4a67edd55d3923fcbbd4cb0ad8a3c661b5909d462edb7c827b3db005347987606f6edf86d1cd6f7a0657846cec2cad9ec923c920596fdacb9973909e75c9554084f1666b48f6c70a115a4d8ccf8b1079d51683c890510c349012f0cd04ef7ef3500987bc5d2ea1c23d7dfe6a7f9db770629550f5fc9089064c52cc72e70bbeeec9cda1428b4d5a7ee5afa2d8a28add8e2552269ddfc634ddb685363aaad163d669d69eb0f71c4861c435849c5c899d840c21e3369fba5dadfaf7688678f6c7145124290f436ec8977049213b5d46c7b7fe9b4de7565600b1ffa452b3c581d3f8965f52b6d7a51b39d30fa089ea8c1f442950cb50ded094d059451e21725d1c7a4bf37e5876360780b475768197f0226066ac608e60da49d3018e190d729b12069001bd5cc21b3780bf481bb6b4ab2dc3d461c996d02c942692d9b6dcc4bf707dc55c7967aee310a4248af4904b3922f9c72d6af2c070f2bbc304b81cea7ce26902905285e6b242b92cdae67b2a7de1e856d40363248abcd83f3401e713717c68a7cc59b1e18b6e4e87971b58096fccb2a0cf3be02225633f353f177e339595ac25913ccb8f4d36b0714ab4379273521d00079cdad895ebbc9028e5a0af09bf1270f0b107dc67033db1f383848679e7f909138bcd068dcca8ff1186939817a556144e85f6611325f17152f13ee1602bd530017523227ff21d8cdc1b22f561d79524b1683de58fe7995641b713369c98826fbcbb5dcf926b0ff514378f5bc910d5fe092c708b85813c5b0b580d7a494ab269f8550ed780fd1ff4e2b40051da6020906b1dca60c49aa6dc00584f7e38f600860856173e1e102f76a9dad8f9204bf318ecc8f73324a61864de13e16e1916c91240632fc941cf721bb06a74450f0399d82f036bd6c17a1b4d5b5551d0a306286ad6d08928b7e04d076ecf82bc946c7e4e68447f6d83380d11b1a70c7036e47992aef952411b9d918c46a8f6ea2baec4a7126ddbaede4daa5512fe28c945ff874f96e419dda5ff8cf9121f64034629012a17377f072bc8e6703b6ee4cf7839d77cff5cc1324ad1d51f907388d409d491c7c790874ece362c909cf5a09eb643b047a459bc523d03752edb2ee8b6afc64698e660d7f95d8b667c9e8e8ec90d456c6641eac26f4957fc6f4d08dd0a9f9dcfa25d205b1b62a308b682cfbbbaadc0616a63b380194db67221ffaf194da3d19094971a7d5f7512dfb99c3c169d728cc073f00a5548070f9607d4194c8e9c1e3faa87b710ca97c62906004e9fcf70b668d311d7076e312c89291e49f54320f5c855d6ff88ae78d3a6c3100031817236c3b8fa20fb96ae91bfaa96488e087bdda62b2cdadd4a805c41e77be0b0d0cf4545d0208ba1979f0e3570ccbd8562dcadebfd7848687115613b553a28652d85f5d58974d0d569bff5d3da4608107e15b344abcacfe9308149833a955f4e691fac9225e06271af96af3d0972514a0dc50b4d6ed3ecaf65e9556399b62ef29a3ec459ee686efc4d4ddf82abf38c481b4f40389206c0be26e56a0fb8e9607fa70c39f468919334c465a40af198d1921aff582c3e9066add5fd57f7966bc0ef934c310334589d5b5c0b6161684a21ed08408e80a363d614f89c3eda2dcb7ad95809ca0f3cb085eded98718da407797b9f8bd530f7d680639e1e7d7c8b20319fea5fba1707e3d9d52bff6795104343aceb442c7a3c449ec20127a8ba4ccc1847d36221683bbc5e3e2a5ba6d87c93b506c4f6bd87d048576ad89b1ea8b241f82387ee7bd612f970acc85a1e9e309288c223580306aaf8a886095cf5b014b4bb833ac896acfb55f5962f378786cd2f878f86a1aa761f4e18dce6d7b08d2436107a6228b2d899156669f8526afbb4424275e33dc0e5ab79945cc1ac63595185e6ce0f213a42877d21e77430cc600b99fef2ae1af29ede59350104ca85049763698f9d2caadc5c2d29bc9b4a60ba7455238c1915c89d99b73a7c5514a3b2401d4aa426b603abfc2003db129fb15a7e422f436d16e3698e8b934ff45cff62b38f8244395765811144b9ee45eb878b1d92fa3a3de4249b022bac18bda8e1beca7a35fff2b8ca18985446b70c72784a822b2a28c4446921f259b4b77286419066c01f3c1fc5a820c059c1911855c5188f8610268b644e0e23c508545ccc3f0ec027221d2d157f21109280ebe52fa04f8fb7bd0515cccb03331cbd530f4fe1ab46d4db8be9bff99959b806841e6cce217d3abc6dc509685a025e6596a89a68e61910609120bbb94401f82223ebf72de42dc8264d09f793e7e4a564ea7da27091a83126811195b7991da117ff524e46abbf33811be055af00cc1cc873df9ba7c57a818304c5d624b67fc15d788f26049de2a010084d62a9b1e45b09bf3468c558c74181a55919139b33c73a26d073ec37be0d7893da7285a174340d5188167d5d3ea02d0f86f19664cf4e9f6fdb9acb79672605e597e8bf485d1b8a4bcb1a4012ace813133a4cb8a79f73055e36e623aee1050638489ae582268bddbdb1d09d692fc03c80ba4ebe1d7bbf43a1645708723f875d236833b2bb0091e9ce19c3f5c1ddc284699937a8fc52246081cd3280271796d7b8131b8f22b45e03cb059b26b65ad861ffd5b0e8dd73429edd04a3d7cf900471e44bafe85752717900e6fb21e2aa409d215d0cb9e601f429480708783d08e682d8d6a4472ec9e22eddc55321845c79e5f85b182ac869905ae3612032485c5207222bc410a937e831b9d139b128020ca00ef10dbfaa188fb9b2ba4ca4d29a344cf7f501b8766e0ffd9ea9608faa2014d50b3f90f2679d322d28e4dae827c9038d593a6fb8fdbdadff89625f9b08e6dd83b77e50619255ca22892ee13b30fa452dc36703c0672c0f03b42554a3a4b24cb693872c2b3a191c64eb802c270f39a24e0cbbb52760c34c5f760a25ada119485d05e0143025dbf6ae72cda802d6586965fa14811b1ff9dfe7aa6099dbed3d52e30b4d0a45f89cc85e37e14b29a3451b0d322a255f3ee42e8ab42ae1f0f7e4c4b75ec38073e7e9f40fb45b100e7ec1344c52f4094fd58b934764e7184fd6454d2cf11d2cb427c3097726a9b8ff1534ca5b864e0f1094b9fe3a5a4d474a197807c828571b7efdb2471351b52464828c81259e6441cfe0081868fbd2be03a5b68ee970e0d04c3c066660997cb3dbc5f4d9df1bf9d8f92267c05fbe77e428cb21fd4a496a16f5db1dfd4419f27b16768c5ace075bb1b1ccd0ef3129def9413308a47bffcb9e803a06095f3ec1e8180f3a2205f70633501295ae5edb84835f637ed5e27fb1a409fae135ecf230855e9ebd44d41b2dd1c7d9dcca4041a8ab1debfa670389a68f12042e7bd24b2c987ee51f6542bdc2503865e7862c48520d8422f24790e57432b27a48e801d41c63769967141a164b0f2e14ba6b185f0584a9e9bc35c289443a0019d4a20e543211699cba0809bb2e121d83efad3c75cdc6466f6d7d7df8b58d0e400fb392712cdff2f2345683ab8a196f6ec3c6268c954767857c30a5a022a4add22a3e86deae3a3334b4cfed0203362c16112f26532e656b64a06a054ab5f66ad95291dd992146d2ad17a4d54bd2e7911de2d9d5872c2e1ce51e2d27bd5efbbda96a1f35a06c98d4572ef7e0ee2148ec5d34e9fd30cecbefd8bae04feb20759e8b371b997184d705d16b021a837fef13f886446a77500f43d30499040271006facf55a69807fbcf42f5db934aa8c5cda5e3d9f54e4ba1934f014fdc7b97639f39e69dbaf5eded8c9707755492dad4c7e4fb093d9276bc60d448ae4b496d5ca1fbaecfd9b34d5487400fb19a943653be394d9863a99bd0624938490331cdeedc69d02422f50b78775b12812cfd1e5b0190ce0b05489aa5ed55a24238d5fc195b8ab69988b7665b0561e9ee1e221a428c13e41ae9bd2a2a816b3c48f92f54bce8a696ef0c614d635720e2b04f6e194dc304587d3924de5257b248b276fdc75c94f65fb6cde572537c4cc6ae22dc0935699981c82c0313c9f9b38f7fa1d06aeaca90a35c16501313940b0ec26b086ae78ceaa622da0bbcf2acd53bc47742eec94c127181f7d7c2525681f6003bd17e3d94282a48b03631784cf5e286b46a04d5ddf98b771a36311a809f8e61c634932f543ba94c13351b4c255e31ded150fec8570c03c387fce3aa88af3c6d1614205babfcb3e202d24e7bab24b1c61c142245bd291b3cb91fb265c8db76b02e72410e9918d9552252ae44c1325cfd75af25516d1451a7979a0107877be6d86a4c0cbb477570b385f26a11ad30c0ec25d593075229f71c01eb6b1fb05a9d9fa95abf55bc201050f21505d3373c092c856182b3b2683859637edc2a37612d6802b1dbdbe954a9867f1f7ca853f224ec5271af32641641804879f12969b47ca5018314bacbe5c7c766012d6b381267f8a99e540e1dfae35859b2f929bb57fae2a0b33c8fe559a801bce717163979e6893e119c595b393d9a7af9912c7344c16578013e4153757cc5bd7961ae97c393ed461b87a8df6529624dfa178424341baad881cf3a538c359e092bc3238cf1c09aa1e6b2618b49fe188e45bccad7904d6ac71fcbedbb7b5e30a2831fffe5486a51b14bf234af6853e6fb7676fe28410308687dfd688567b31e8ebaf058b41eb50bbb41ade014336179446b4a64c825e092be164c64bb83192286a0154d7be8b031a994db818a4a2289930a47fc138e603f5c1c3d6ccbaf45a50c67a8927466e12ac9db5aa9070a87bb64764c5da533d44b0485a14a7fb34ada2929ae8b248ab5ca8b3b5f4eabac061b825506375619965a0c6649e5658cc5194101e65cc73a303919f7dc8952ba717479310c2b0223ef2d1ac5b904d81505a85bc43849b0b2749882cf99f1f35472dec36902ce2720d74b1496907486e85c399908b70a1ee2063eeea2efab3cc25033e3c602104b20905af43321e2ad6e0f3988728d1021cee45985da080daf4685280eb64f01938209ba9b0ce41fedb06cc7c38afc1f678b18ab03b40fb1e3f6a0bcc6e9aec80857b9ccb7a0e44cc2eed5687304c9f6701f17690e6799fd3e24ede9368427b35673a4e4f2af439eba8efb17347c7b8890ba80ac1c1f0e75ee6217565490512ecdb46b495168c06a375f53a1c19c3f06969e20abb1d0dd1cb05b2b8cf582b064c779630e7ce86a761d0bd8c5581dc646b3161f3a67ad22134ed3f28a8352a58c1499d58661ae6e437c1db441ee6f2fb47fd1ceb92294ec198dc9cbf1dc39cf27faca9ff02596b610069dd2548d7b5ea26a49c8f06e161c02100cf9771d24aa5c2b9678ca16d93ae9d4b1991ad3b7e51d47c5a6204f7f4ec0f82d1631d770f1fc8eeae7875ffb725fbac188d2594ab98c2e07830d01131d9c30f64de216507cd1c7e9391a9bf05b059728d181cbc081363a573ae9cc039ec3200cf49f42ea739d4138bcba7359f85f234690e2882addcb5f17f0c4c9af923ee792a3e1a6f8b9d63fdf93304664f8de0a8511264590ebe0d6002f21567f9702190195e0b89e5b305d3493e5197223fc008e2bb7e77f1a012b8250e042ce8d6bc84fe12cb8bafdc93ddd11a6287a914a3e0a750cf980c3f2951c7ae9061b0c0ba4704f708938b4aca37591fd67113356e817e5fb01f01cdc7d7994dea21f4dafe834e78d63dc4de1adf234fd89cee5d5d051cd0462361408e3b72b281bdf5be084898b62a6267226fdca86ac44800a48bcef7743b327c06a8e089e644bfca0fb44a3c8c01c033201391a6f10f037885d38aa102e2e5ce86368e3c4620888e9690017fc31de6db63af1ad7f987aa3703ee240ba57fa54ab3cd014c3301b357d8f2f502e87234d7b0e0424b4cbafd5bce856d41ed1b654b115916d826f2f337596d5afd2ac59fe1e1fa2217eaad65e181c3ade97cd99799612dcee7d8406e97f505d7861c468075d4c40ba8cae028a5c80f50fa722e586bfcc7fc369c735a7d128041ef69adf3410c075342848f09a8193176330cf79d0aee0c7f8e82653ee31a7fe159fd566cfd4c31f74c60308b16c378e2877720d8702802dc651a1869f48bafa41f4f00377d91fff74b8be07403b73cda08f788f7579feb2c2b7115a7597853ab2ab6adeb3cea58adea3affbf58619ca8b782411d5d3ea6594bd6a2f027b438d951b69c1609540effc53544f9d0c066f3805ec2b7647f13cd8aa87814b94a7b8de0081aa860c342a547a1c9a1bd3ce29af8fe3ee2303b33477261eb19c7c691f4e875a0b177c4d08992d86762dfcbd007766e970d329c1f7a64033ec6b9b5b9d6bd7add45972fe72e438a5dc391c0d9e9ee1c11c7761214a5e09b6f7232ff80996850655dd2a522c75f9c992138183c84394d15da0dc1c997d2dd3712c716ad9932a6e4c43740a62c152f77ba28e3bd3189d8e58bb54a7263ddb81c44ba6921811384d1404afac4e8b82a986bb8a35c9c0980cfaf6629be05354c3049632240c98a4dea67e6da2ed6d3a3cec35f3e13c2866c429c3438b22e26743019e41c3f9e2bf018c5f657774a38b7e10ad415c71c6b68b00b015dbc23e9079fc396b93363df87f1f21255c518f2de74e46a6cbe506edff1ea4e9bf83adf86ea5f81cd926af73dfefdf926c4b1f4fe93d2e2bdc81840fb4753ceeb2c825da988b208524e805632a4ae09eb91867496a7910a4fc0f3e5ab0a807d137a07d518744d59bd08f9c64e941a8d2741a6b801017c5bf9508aeed2e7f7e74712773c192e71759a5058daf0a20e0f4d5105c615d9345f721fc371885e710fff258859bf3c46e103f0c8a2e9a8ca6c83020747985200b1ce1c9ca9822a18b4122a5d49d1005b4bd9111b66e5231ffe8c055b0d8aeae083d5437d00fe4b6d24d656042d86afb8a29b709a9c39236c532237731ab5b7c6b33f0092c6116d262cfadde1d592ef684527efa3d3c9660559c56f1b08975d9b6ccb3686370d3901b357b9ed50b3fa562602a6415ed91b61c1757b677be2c7ec40c36513218879d100a979be29a535883e93bdfb358f6afe3cd3fa6595afb8b8e865f58028e4cf7761ad289d6a96974e0461099c5265ad8b38cf866068459a9d856b3bdae102a3d200e35e50d09c64a000bf55e39e0d3d873549fd75166fe6d278494bf19070dbc26ce7d04ffc1719a3f000a14a732c33ad5fdb74748bb183b15f43f9b562996eaec0dbda70facef0fe0676274ec9121d913742d47807838bbcafdc981602b7c858970e454fefd33e3f889058a36cf6cd9ac60e91adfdd731e8dec1d1642c49ea28c682ee7b6c5dc26f7bfbce3e53f6d9ef185f97de64ee004c4546140f3955a6710f9dfb2324c09eadae806da625dd3e077e8061bbf1852e434527ecf84d9ed6a91200ed468265448cba43776e39ce05b144bfc8b9aafabaa650157c26b00a8bc52add26e360618072a6ea8d70d4c9a42403b84f4d9c02b48e4d4d75e44f76fb4572b5a8924fcae9e449311c16238133925eb1dc3484057209a606753f5ad0909f963d36712f19b4c2ff2ca74ab425dca4149fee51d52638bd179d594690bec195d248df118bd0e5722808af1eaa82b1eb7dcad864c3affb88d82b876438450286ffc60dae416e1530d4c66755e205cb8018acd40b98d03c28130c904fa7c064585b640133c85d812a185336ad03365aff4dc51d04bca15ff7e5ec6f1e7a3a09e9587bd32da6db5aecfa58c4867f12b679a68c3ee9463a596a2c1a26d00b7d3d42123135389c834fc397748e22eb1cae4d4b967d086a1370121afd121c11947bfd73557a31d9d0b0665be04562b1baff7d84cec6ebc38662915d88a446c8481c5ed6161645977d741dbe6ba2ff5d731b2226e6216ac956e18d8abde4285e35744d3179458a021b9e5d63e3aef0f699fd900034869f83b8f2043e89481dcbf6a06c0363525700494a5ecef1d13103b58401a8c151ede63cf5cba4b1d12a394e1e5eea6cc8570d58ec34ce52aae51406957dcc3442d1e848ed3b4623277569a4a3c425b0c3dacd3c2eacb1b71ea01657c965295ed274d247100a7cef25acf460e35e98074190fad49eda7fd0a66008bb09debf6588f6ce330cff38d6b188087c2bd2021588838cec44756699cc45bbaaf49daf36e0a6144b5e46b75a1e77b4a9fb2afa55b9ef2544c485500b6487b927681f4ab81fb79bd1eaacfee7b017ddc9d8449fe2c4df4e0d6fd74b8253ac216c7d5c98848ad4c6da53b8c1d4914d2a6d287c6b7aaa37871701d592be3ef88a45578f4fa87009054337a5f7aba69a9add5cbd0ca9ebfa9bfd0e54dc199354cd591d3c6cd7528882fa9696ff20cc63f26f13fc3b197f0e7aeea78b64885272f1b402beed550e800418294f71b4779bf5554c874eddc5b86c25f20bd163b97bd983bd7c0fa788b3dfddd02fb60715d1b98d67c46599a934bfbb05482ea93bd31f69fc3884260bd35e89c750f59314ffd687015bba4c8f9be740e0bcbdff595e6d39a088dcb483c7132e3b3a2c41b1060889686afb58174cd00dab557508fbba2fd7326e75b3d7ebf055e56833970fe303988f099508efc28302842a13e052101e0b80a00435f392a7101cc280638029dd0d1111a3e9233041813608942b20b09e3784ff4c0cf087b15abc0e2fbf050b8e141d3664b6aaa975e0ec6616f065a47901684d3cc2163507a7031762ba3eda4e5bb2f911c93102b231981df4f20960e752ea1bb42ca18e5a32950d5d3ac0694b2486b0042aae2a4ea4625d6268ec5e5ebea5da4c0114b59ad46a11e69eda202b1468de880c0339ab0e8ed73148bd3cfc24f01bd09980972fd1780ac86b215d1bf7e139304cd827a9241b2ce9831ba6b8ce730c7d8c6dcc49071e3becfa9767c8e678d410b9af97355c4fcd8f27a5a640318d504f8f85cac08e70ec0d0e19b14341f0642268d0c5dde98541847cec8be859ee8de34ee3959c7f8ff66965d5a045f60303264b86d391d6825d608dee0001bf8c987949876a5cdea68cdfa0948bdb027056bdb54738ac457e68ae9e12d00ae15659a9c5dc3055afebccf6f2042255627583237ce64ae15bb87967a310797a8bd1d8b0ea2af40fe129f3c013c1d031e9045480e3540b0a6d0013c0c3c0c3c0c3c0c5c7536dffa5afbf85afb32c94d060328570e91524a29a5943c5dcfffef2bac5922fdb89d60df01b8d70e220e120eaa7dfa5e075b2efb2063077a400408652df9c3437ba61326ae821e3ffe0565f418418e1c39729061461921bf28a91d599ed6e194b43b437c51ec13df84ff7079a344ef4541c6dfcf744d6249b2f4e351f023080accf8471020c88bc2dff6781c212263a48ce7c1e6a347a216460a38701fb28b92fef6091e5ddb3d85548c105d144b26699b774509a2a71f8f7ef4b80b84e4a29c4cc99f43eb68dc871917650b9927b7c43a25590695f19981905b1447f9a888f53be2b6289f52627a1cf1e4c1c2329ec7c8a906426a51b4fdec31f5d42a95fd303a125a14e77478f5d14107ed263ad8d67e0401328bd266350d6f9fb36a92e8e011203bce5c168531912509c26c7d67d4c1a88c27e375f8303fc6503be39058144f0c5fd3237b93a9820081454147f6dc3cbcfa8a8230c1eac3678792724b57943fb47d8ea7f4181dc4561464d8cff41373e97dfb9415e5ce3fefbe1172a49a405651f2f6befe7c2f41358223236398711eeda9a2a07a714a5232a6830d191f98232415e5249ffb8bf87ac90d1e2aca299ae2b5aa1b3beb3ad8808cf44069fa2308103c4541ac44c6262d1de3634c5152f3eb9d3edfb9c51e528a629dacf0cfee9b2d7421a4286c12cd4fb9ae8c1a8ea25ca7848d90a5cf84d1340819094551da1cfa32b4da63cdfa31c623e3e3ecc7070962c8f8d16303de21a1f864588d887bd867668c042123f50642409129993bcc2cccef1dc935232de4139527fbf168824c075b88278ab3a159372819d484589d28c9e8239424c937e531a4834d5160d641c8484881104e1493e416faf24267f670246d13a54c1bbac9947479d2e9c89420470e203f7e8c104d143eec37779c641966a065a2f499c6946a5cd97e8c1963fcc080eb085e30d282104c9c5fda29337facd2d120e412e524c9a8485d0d27bb4d20c4120671dbf9d6677e1e414825ca194f3b76d0d4655b8e0069a34479cb939275e44994be63fd68d4bc31ffa6834d21102289b27ab869f54e4522512332960d89529eeca4fdf45eed731f64ecc0904714b3e9bebdfcdb112541aa8e96eb2487995448238adea722931c93e825cbc98e324c0972e4e891cad83112c288d29796937e639af025c9224a9fc39d86a83a791f3a78544439dec4cd5d33c4c62582188424a22c4289a33b98b618f172e4e0c1e39311bc60c405218828f5b8565acb9c9cafe732309023c71965989043943ea8d53521aef1b91d3f72a00c0831445648214a56aa9320e5cf444fa703e4811f2184c04b63d6c636d4adeacae498d94e4a9fdaf977f8f81d3e7a241b384118f623880f31840ca2f0aba97dfb3e89568d04420451f03539b67f90a3f2203bd2488f20231e080944395a674e3b52c3067d1eb380104094444d62706dddd3b1d23f943cd7b86e585ee6fc506cb913ccf47dd0a793fa50a93ce93bfa766d0721d4c34d10c2873c3b45d326c1e4a01e7b40f95b491f7ed53e08f5d0c335fab47a34eb4e10928782ea38aa041d7d90b16304fd0fa3034873081efe127feaec4fb44a772908b943c174897651c2e73023cd410f1e3c8c0e206c1b6207e6c4b05755b6996e0f327afcf0618782903a1446632cef9c33c99d355621742896e7581946891e7df23818e911046d0e25cf7e9a27747808598f243b82982040ce2c440e85d71393febdaf83ad3300e4839c7174042f18b99038b49539b9f989591bd7c1962347081cd8d6b54c9d7b2f07216f38811b4ed0860a61c30e1f18b09035b0400d277040481a121082869104849ca1472ac3033c78f410438c078498a101216518d9e10303090821c32765f4286364870f0c7c52468f03848c010121620072c6db40012161d8e103033fde063f7ab080040a0801c3c89bf16698a08c1e3bd08f318649c10342be00c40b0908e9420142b8306280902d848468c106490f1e3742b22012828500845ca14688154676f8c080f96106066e8454a160ff3934489bf524b33ad87a943172e7215428a8bfff2033f6ee29ad0e2ef311bc60240221532846b7fb12fcdd7c452152287f89d29d674fde54734814ca25c9aeee5df7ffa5040ac564428fc649f3d965f384c2e6b251ab394e288870f3b10dcae3c5d88462ad8c8fb9ca8472978f88273f3107cf9650cc647667adbf0e1ef911c447f5f8b1214ae0a412f49d1a5faf2c0c31c2383463032708e3f0109284c2c6a4c2834c9adb84db4b084142b9d49ea9d2a349a89584902394d4a6ca5232fe7d5ec8084513e799d9731b5284f248bb93e4d493f3ae2342c96a9318d38cb4d7d72e6408e570b28cc79c124e65fca2102284e2b907f5eb99369a3b288404a1b0664aaa0cb109014239e5a5de096fda2d55441abf28ea8f3049aa9a81f0a0e18b92506277b2eaa917e5984faa8c3a5a2d76bd3bd0e04561b3c77df17eed40631745318f1557a5a7dd73ba28967abafccad1bff97a1a68e4a21c231aea63f43393a33470512e133e64dea8656738d20884c62d0a52739bcef15593c9a911207e4174502c880e7e0488d9a2f4354a9fb2d1754a5f8b72ba170df7cff1b9a145f1e48534bd1326a6a75994459ae99fd3d8eb709245318b66508d2757dde76351cc66235fe53decc48745c1e784d9bed20df9a15794bb7f477d6c13579494fc9fd8a9ac3d666f4549f2f38b4d67b3a298e55d4eab7e8ad9781505dd3b193d72d3ed5d55143ef73f5e73fc9837a9287aa5989cc9dab9754445b104fb34266c788a72589aace979c2b97ea628beea499fc4fa33d973a3518a729e3a41c896983c428806290a3a8490494ebda97a0d8d519474d04992a284f909cd588086284aa579e4899fd7953385a22449d273ab869fce290245713c773825d3bac712fd4449129f3c9d09f1b77ba2a0434dc9661ed74f4350d0e844f947abe95327d6bf361d6c3b6870a2984dc9d5249320e222db44f1d53c77d6a07a9d952a6868a224ea097b1fda34058d4c94b4fb79d0d4d472f3395ea581896290a3aa63a8d635d5250aa3fde336ea8312ef6149d0b04425cad7aa495c9fd2a995fd4801236850a26025893249e718861861fc6fa0470a120e34265116fd246e08a1497a3691444166936f3f3d8d3e31fb3064208d48946a738ee341d490286e9e5ece8ce611a5b036396d7a53b2d9ec88921276d6c24abcd0db1a519a3bdbdd64a145c830a260ba43986cf7ffa44c8bc853c9922335bfc9194514e7e3fa6adba6e69c24a29c314d6b493f393e5f4454e268c9703a8ceab90ef1e892fa3fe97adc1f0d51eada20afe5468b30994294248f25e4974c72de3809515211e557e2e98f3af9411493cc993ff66a2a258782e8a4118892786ab6cf7793ff0b8882f85ccd74d61f8c9b9478dac3d74b26f143fa33b7c91334bd8c923e6c82109eb4492735a4880ff99a6ef8de18d53de41d547dd4207fd703324ff6f02c4f1ecec3a984f3132c9326133c54727286fa77d82ded644e5ae2d88cec7075f579272ba5c23a1c575a6ed69a63b3478792f6cfd983e59c92433a87c7e4a0be04d34caf961c52925a569b85b5e75c1c92e4bee764577127dd703079c9fae1bdf469d3bfc19149892f7292ce70a51bca6ea63a32747c028d361475e3f7743e17b91d67366cf206cf915ab206affd8365c6d5500e6f93ae269f864cbd9594af36254e101a8aa272f4276d910eb63ae34110861861fcb0f661c8403d434989571d773766889598a16c93b379b669cf67cb50d0e973bd234c12255528a04186720abd6d629f5607aba00c129c21193bbc0634c6503ad14eaad03822f23a0ad0104331c8ddf598af3386ea0a136884a19c4f895d52cc35ed361d3cda020d3014ec2441c67fd198748849e30b05d3b9af33951423dde485c2270df723af6e626acbb0a3d18584dd6899f111b53dd21fd0e042a9e4a04f6cfadb123637d20399a5e0d0d842d974af75de2eb19a4b8c2fd0d04279354c909a6fb7dadd2c6832886ff64a071b1b0bc2484108c240dc010d2c9447bdbbaface84f2756051a5728f58ce9d64d9d95020d2b946468ff8df953b3499f2a147feea3c6e6e4a184860a2531a5e47b7e279d4a3685626fb0d3257c091a64ea051a5228ff28d750d1dd156844a19c35c99fa36cccb9dda20185f2dec9f29b5f76477c46683c01bbd2d8ce8c99f3b0de99c49ee7ce55d17042d935cf84f97c55ddd1193f82f80c6834a124eeb6fed4989e0906349850d2e92b367fc814ab5b1829e08006dc0c349650de9ca1f3263d269c749786122ae962ef36d826061a4928a767d2f8ab3dc9f33c62677da0818492d0ec229b4b64b24f9d25681ca11844f796101d0e842146181b08430c0d88d1041a46286afb7712eff7cabb9350a05184c28b0e9df33cd98ecf3ffe0cc3c36910a124e9e997ac26dc7e85348650d03bed31b27f6ed38910caa9fc3f9996ab6c111f176804a12073f0123e8f9538a6a6018462583b39ece90bf3c7f330c374e00d60fca29cfdd2539f6de94f932f4ac2bb4ce79cab4e8d75b0a5200c318e191d684080d18b72525a54c9871b13d3880960f0a220838f9293593bbcd43ad83a0818bb28af96ced334d1a4d2c9d34541a9ca49933ccd9acc80918b52069904f5a03d682a1d1e407e60e02c29e30c2029013070514cca4d5d346eb078138c5b3818b638306a513cd9d489f7142aee01e95182325ad82c0aba57832675a19545316d89acd2eefeeea95814949fbe10ea049d1f755894ac4b694cb9b19bca1cb90318af28e7572bdd6d25d33cab030c571443d34b79cc79a21db7a254828b792ce1d921305851dc68eb1adb2a3c3d6715a53e49f7efe44927ea070c55946284d23741f5a928493a0751ab38796ca42360a0a29459e4cec987093da55394529334355a3be76d638af2e5a893bd93982f6f2b45f184e6b8499c4f2ea35e1bc0204541cc56bb283d3289761a45b9dadaee9360cad333828c9cf123c89a000c512c4aa3499ab1444351ec93e7c49a0f3f9a732000031465f7caae8c615927e7e800c6270a27a63aef7cf67f4aec89b29d0e2274909b533ed589f22849989bc991effb79303851cabcda326b93c963b4897297ec9b2e596487de780cc0d044b15a3608f3f0373a9899287fe60db2b3c7ed9c8e063030517e13e68395ec5ea2583d2e1a931271994b33806189827c9f11677a9d94772a515aadb52e71d5372ad7c1c61d008312e5ed93b4fd6c5a0d623289e2be8d36934f575989e960d40018922869ca8bd36aa34894ac3b577c29d122e5a3830d12a51026b98d90a9a233bf07301e511c351142737c3ea78707301c51f2dfdc18ce7b3580d1886296fca074775f3d857ab829c0604469acda44bbcd227b4d74b0f507ba9ccb67adab5d77d0c618caf2fa2786ccb93bdc440ca5396df2e77d71fd20b51186d229d98498cf26c55564030c655fffae38a165fa5a1b5f28962969eafe3a86d7a00d2f144f676c4631a99d7b75a1eaf6ea5a3bd3d777f36c3519a427cdbd1a1b5c2896b4cd541d5457e36d630be56b5bf5d9eb7427c936b450923cfd8e1e1d93b0496c64a15c57922821e63a84d06d60a1d42708bddefc31a7aaae5034d5abf137a73655cd3052c001337c94e18122d8b04229c694da69f3fcd3b58d2adcb97a57e9dd5a726f993d87930d2af069eeb1f76e9e3b1ab69d8d2914ec6c839254e6932429fac086140afe717233224dd235f7c046140a9e84926b44941ceb046d40a118546bfcdd68eaf3af53b0f18462f013cf3ba6be1d061b4e28092dd2aab38d8c60a309c5cf9c93248e14a13bfe4db0c184d2273313757479106ab3847266f393e35ac8041b4a28a589f641693bb143466d24a118ad3549a735886803092559b2e9ce66e308c5adcea2f44b6484526d5475df9c63a308c50fa7466af97c8dfa2c608308e59e133ee893261a4a6d6308e593523ef58369923f2b84622731998950796b82ca4610f8ced6f39ccfd6add7b0d4517b5f61bab5b70184a2e99bf693f1464cfe77357e512a69841ef3bdd0bb9f20403e8809410d5f143d89ba116969f1e1ce408d5e14d4c92a6d552ee273c90bde72ed3bc6526b57d53c73f896899fa7214fbb28993e49899327ba286719ef20ecae5c145f6d838d4cea2f931617a65d59acdabdd6ff7f0e4b137b84b8b728c99c34a9f7d059735f58a8618b92f8566fe71ca73afe558b725a4ddd7a52498b7267979d24e3cae745b328e7b0784d82f664510eb552923a194394d8c4a264e23cef53932c9f5a58f0a1966e7a3a73258ddc479b5f517ef3bf8daf9de4f48f183c0c116ab8a21893ce9e744b3f58a7ad2846bb784d9e54cd9a9415a5cd51337afed64966b20abcd5533cdc524dd47d8fabfa66d9aa28be271bcf9c4cd852515453a22b4d7083500315c5921337c9dcb6b1733db2c38719791f23232378c1488e1aa72898acbc933d49acd2243c927ea086290a23f33b425c4fbc453ad8c6e8c1033b50a31465cd8e254d3c792ae63052c08130c6e811025270e762b6a66de59ae26dfde9e4add2129b34c6183d42e0a3c70872e418a3070ffc11c4478d519476d3e8f7b9e98ba8841aa2289776cf41e7dc77259f80181d25c8918347ec8c1aa1289d55674fb939a5f230830ce461860872e4d01164c70f9323478e1c3f6a80a29c4ace41b7484f4ddaeb60db1a9f285b9ee0267912ae1a4b9e286f14552354f6e3675ea313c5134a0e9d4f9e70a2984bc97662f2cad31ab38982c9f93953be9588ff3551d0399f204fc59ca4eba4a5502313853b414f5be68eac7c5a0313e5986433e1e33a895fe21a9728a60dafef195ff5edd4e167895267e7c692e3677b95b85e332f6f533ce698667abda9e490b183478e1c35285192c3e836e9f762afd21a932827496e27a983be8ff149a2a4c43331e8e4d176bd8f44594698d6300fb2447f1aa80189e2c6d028eabf47947396fdb928fd95dd1c5154fbdd5a3bd1333c6c444179384f4ad4a4bba3c508e5e53d6bce3af34b4c74eacd9e1c4335165152615d2b1af4d566aaab504311854f5b6de7f107627888e156a8918892789327ef7d7ebb3f39c39420478e26d44044593d86aa3d0fe27d926a1c8213d9acd5135bd37dcdac33393658be27916b18a23c622ce3b7c504c80e32b210651531a1a3efb1f24e6e508310a54ddf1bdc3db4566310e53969fd41c482484b12c46f499e06a2246892348f58b1104dd5004439e99b4e72d021c4270935fe5052d95c62b0d9d4af6e0d3f947489a6f7e54cbe51a73e94d4c9b0cf1c7352bb960f25b3da3c4ad548371dda43b97dcc2ead753d949350b5a99468cc1a637928099ff583a9342d4a8cf050d87e3ba533d5eb6f77872e3b6e67eef42cab4b9f888f27c345678792c8f1b0dfafa635b735ea507011715ad776db734c87d27b7e4a3bc9f7eb338782edbefba5a818519a1c4ae9eab13f68a546ab8a50230ee59834cb427bb693dffe8f123ca1061c4af2ee7af814ed7d9c918228d47843e19458769bba947423fb3823056704393794f33b08fd0aef31afdb505a1b69926f920e430d3614641833759ad3668d0ffcd1a30442a8b186626b07bd1a3da9114295b602316aa8a19c395b54fed788c8a9461a8a71cb24e16e234e526eb2a1061aca4947467591ee19ab8a7186921cd38b6caad2709023471a0e30cd508cf3ebac79728e49496b94a1243d7a124b289dd7f417d72043d14e95e7969d98535e8d31944f507be7e124d94eb4108fdfb1811c3980ec19414e0c35c450da2fbbf494e272529d23478e1c67043923c899a146180a329759a7114a867e150c652b0bf9ec536b7ca1a05378bcff36d5f042a636470fd1cad4e842e1654b0921636f871a5c28072da332aea9eef8921c6a6ca170f399e4244c1c93458c871a5a2849b132134266dae7910f35b2508c322fa784d2e7c14e5828fa8693ab1bd1536258e30ac52023b654c9e24918d90d35ac50def0d4a132a8f6b5be46154af96579a1f1438572ea2c1ae4dc48fd0f03a9318562101bab394bd0ca0daa2185929fa7075d4d1285b257c921f4492d75e2040a25258b87be9dd3573d3fa12c9feda4d3ac3aa1a446e7f42841fcafef4d28f6be08f73cd9cdf56742e1f96215eef55d192a5b25bb79f3a4359650d27a57ef5c2efb5f76a186124aeab6a66f99fb246552861a49289d896b7afaca78e23e861a48287fc9b9eafc3b4c3afd166a1ca1a4faf2cf32a9129ea6166a18a17499b3b64975ea72826a14a124cba9c91b4463c5794428e58c1425f3c86a0ca11cb4c9204bd23d6a6d53430825b1171b9d63be8d3d0a42d9d3a62769aeac9390d70042d93edd4ab7067f51cc9ef2a21e76733fe68b5b4fc4e3eccb73357436bbc4124f92ede3ec45c13a3e7abfd7c9f8c88600031920410838b02b10e145f1459e072dd9a533b722bb286f8a358d266af64516d145c9bb4bdce041915c9454643a41346a27a51a115c143b28712f9a31b710b1454988ca3ef7d3afaab1482d0a278618d5e826c8911f115a94d744d3ffa0bcd6244f641605df1c6fef9a41dcc322b22849e599a4cde56910b6482c4a92b0994c899b4404169beca7aed8655896b0179bebb49a38b1c82b4a3a8a6e3465a2f8cfc90722ae487baebc1c3946ac1322ad288956680751e21a1aff6158041156943a06b5394e9f876d9019e931460b4164158517fdd22ee6e1212023237eaa289edcb79d5aa743241525c965cd74fe112a0a42959df4a6e323a8ee14652be13597f86eea93ea603345a9d466379363b092252e4579e36374cf27b36f57f923880f142145399ef2f313fd348a527ebd27934c1a0d22248a92c996d93e4567b68642518ec1f7b37dfe8f513fa028bc0927e8e85ca638cd278ab29a7da45ba9c7917ba2941a6c94951ee5bde14e14f64f7edb0a9313a55e5355f725353dba9b28868e874d5a35dcc3d5443928a536eaf5a69b8966a298eddc94f43998288586be26396c68e67989a27ea6bb93632c511c1de232b331bc6b5889d2cb69dafd690e271f258a9d04313ea7425f956c1225eddc57c2e5e9204e24514e92541e4d9f3ea9eb4814362693d4d656b9a74a0412e5de70f2ee8312b46d26f28892fc983bed696f8f1e114794d7e74e2651cdb74623d28892709b84df4e4222106144d1d4f8bfae2bb288c2ef69acf44f4af073e408f263e4c89123c7f216441451d2f5f50ea594c7946222ca9efac4854e7244105112e466adf36822872887ba073b9d476f3b6c228628659f3a49eebdd8cc26852875fa7832932094ca0d3110440851f0cf582aff64450651f4b7ccfb6492cce132558808a2f0f72a4ae8f196d1ad4820fc90165af3af49f9990820942d7d775517b17dbfd7249edd5d913f94ac9369294964123ec9e620e287c268f850a2271d7742d68754c738dfec2912e143b9d5c74caa07757a9f1144f6503ef9564ae37c7ecaa88772d8d2f8493e8d7703913c9464bf07d3fbe99e8411f640040fc5509f565976927bb57728c5ef95564f236f366e87c2e84d63fb5937a9491d4a4ac9b3a38489ce76ba0e361e3c76d8c03108227428069325f36492bd4de7db81c81c4a726cd11bc79368750910391493b0d97c4e9949f6771c8a9e641a319f4c506d62702857cfacb69c896d7dec0c913794547886ccaf1b7920e28692186972095be9e7412ed481481b4adaf309f7d023eab5eb21c286a2e8f4aee29e25b7d91acaa794b89576fcef4df58088a8a1f41fffd48c06d350d09a3127559f2756d63ad87e98e791341a0adb419618c694d2a7752743e40cc5cc1a4e89d87fc9138f6186f28a6dbdee5599d4b9ad814819ca799e756172d08f11203d74b0a5a5d940840c25dd21c2323c243286828bfda6f8e630cf256228c63a31a677d4ce1e5f2fa38048184ada2553dca4d1cc260806ec562be5cc62b5432cb62cf4846cfb75b00d41e40b85ab6f0fe349690d9317f1424969dfa0f4d68348178a7a92984f3e4f5207112e14c427a5acf6ba34a46b86c8160a32cd97eeec1d996346440bc52d3169d45d08a5633e0bc593d74ca82d9d3dc9712c9464aa321d6ed36ec47d85629049a9de2ae13a976b8552c8776a129d44e89645aa50f8fef64dc2c7dbb050840aca95ebab756e8cd68f9fd3438395660a65cfea7af71ecf4c9459109142699418338dbfc9be795b41240ac5d238f95866fbe5252250286e36d933eba2a39854e409c5f02432070da2d96452c4098bbde75a8ba568767ef024767cae838d05224d28dfc9d0f4983e3eab25c28482eef876a7ad4d2e2528b204d3285d4a9be791881216997bcd2c570dbdbd73cd5d6ef2d9273e20104942b1646e977529133a65224828fb65feb8b979cfaa9581c811dccaf332798d70a792b7d7bdc79334102942a932eb59a92fcd77a1138810a16cb28e105332bfc39a224328dbdec751cfa293b9688788100a73a2744bd21977bad4664482e0be28251e8fde52527b1213058f65e61fbd4431abc6bc47c465969628fa89901e2ad3086d254a629424aba84889b227eda6e1af9d445946569a9cd74aa214a7d47fea18c3c452240acab48508dfcf49fe9028e8a4c6a43449ee3ba14794e3a94bcf49e8fc337244b177add395a0f7d449234a61ea644db731321f4614335b3fa68f413b7716b19d8c8d228a9dab27e9b058cf9a4494f54fd04d62bc28d511510ca64b752961d5fe4394776be4fec6666d3644b1c4d4497b2cbd2772210a2a4b47477487f84a8892923c87d239c6adcb4114b38a9e31f313538c8228ac491936931888629e8e507bb127eb01a2187e629b66ec0f653969edc62df5434ac58850a9497d28df9d59e8aa66c9890fc5bc59b7f5419352fa3d946b43efe4ca2bd15e0fa5fe34da6f72de1244792867265972c7123c943e937492cb9de67eee50f8183de67327c94b76289c7a7528093a6e87bd9bd21c1d4a82ee9998323987e28ab9b7e9590ee50d9f31fb6d4dd9c6a1a04accf6591a4a87150ea51ea55e3a9db849d0e51b4a1d6cc4970e1b4c66e986b2dc6c99a9d998e5b7a158d26a791e9f0dc59426ea8c97d41aa13514939c95902656434992739ef4a374e80fa7a1e0b56a56ea4543d9ffd53fbd9e3cb9e2194a5e16a349bff7cb8a6628ac5bf99d32259e3eb10ca50a1b37318a9c0d1992a1bcb33b26958663280926a2a4289d97114331060fffe8dff7610a4359c3ac08f799bf180c2513cc3e6e8efa4dfe17ca7b15f23f73bc50148f93fbbde942e184a651928ce142e9cb4527311b84d0e92d94d6930ae5776ba1aca726e9f5344a3ee12c94fceacc7c64fa13652c14635de6a9d05ea12cf233697a92df3c5628c8f4aab249aced74150a32c91a4c56a5664fa9503e592f67a69d42b9b74d34496d470ac59ce25b69265128effc89d9e40c2ae7048592ec7f799b24bf13ff13cac9e4a0d2c3774271f5b4e6d8ef617350138a5aef59e999b73188092549c9d9bdc64b891a2da11833a9dcc6cb3d494709850de2da3509a5e43d4928c7fc959a1009052d934e8a1495df1da124d4a4fbe4266b2e638482061119ea2776eb4528ac6b78bd7053de39114a32e769f21f5af4432808fb354929f9df0409a124c3dd7732f14c49128452f95b670679257e2d002094b736fb4539853cf132d442269df9a2d872c2e53de83a99592f0a5b612137336fe8245e143d56d77be7ffbc41bb28ef29fdf9544f99215d1493aa2731a8925c9474b66eec33ae830e2e8a9bb398c96563759b5b947a84de289371d7765b94f39e70cd2e3167b3b52879385fdfd8de9ea745318fde064db11e6a9d4569749ff027d44fab2a8bb29bcc19254db32435160569c2df9b1cc2a25c3a356692b145ebf78a927fcc1853488d39ed8a925257b2c684119fb31bad6893741dd3fa646fb0a2e827e3c858fe9db8f08a70631545934f860e2a4e7834f91baa2859c898da16912789592a8a6e5f1a5b63f3262ba1a2185b72c24ee7f4fcec8d539463fcc9ace898b3be6d811ba628c7369dc44fbfa519bc1d3f8264298aaf1e73cca7e4bfa8cb468ad2e7c93efb5892ec1502328a92a4a3e8b7159d288aef1e5f3fa39a8927a72e14a50dbff52553846992048ab28a2ea94978dd5027fe445944741ab5275388b73d511ca59359099e84a9d98982d89339c99daae644d1e48cb1303957687313e512f9a267f34a125113057dbde2ef9ec944c1443706594df54cf906264ac2f59ddca55abd44499c9c5fafeb74fdb40e364b149392b6e45bc9df900e5ec74825fa5ed7b87f62debf7e8312799bd7c89ee759b77799dc984431b675fa32e1e4830c0f9c910120404c09529023c719c70b372451fcb5534a0ab976a434e2810d6c0088c9418e1c23772312e593242df3179e0eb65cf6c00d4814842a41644e9f27f59574b0f523ca226a7d3fb77a8e2886ddf71a4d75232ee3461463369d37689eb5f50d234a331bc7568468857e802ca270a12419364bfe290f15518c49db699bc79afa97888212d95c9726556c2e35228abf3147d5c4cc2e9d3f4439c6124c8ce96eeff73544d14b2ae94d46934294cb4ccdc4b9124e6ed98e1064f4e83132c68f911100dc1844b92a53c6f5fb587dbb0e36327af44005dc10042a63470eca3023e4462074f7183d2a101e3ff2a3c7880f4386190c6e0002d3dcacb0ccb355f5f4ec93a6f73aa68198fd4329349c641dde417bcc79f0d801821c39fc502a25ef8b528b11e1b60eb64ec18d3e14e3bb9992abf7b0b9c387a25ae5fc897d6298bb87b2c62c1f235be57d4e74b025a21b7a28dec5c9e9749864b9e7a19ca4b01f6166d2c19656821b78286a3a7311ad2b49d64a079bdfa120caea636b4dae8913b9dcb043a9e6f575344875b091b11fb85187b28b7abb92a26412b74ef9a34710e31db8418782095111b7b9a39e500437e6502ce147094a85f7891305321204480f1d2365fcb8b41e3b467c00b92187921e0deb414bf81c5abe118782124ef808cd682b7a73030ec53eb58fdf4986b9f974b00121a3c78f641ab8f1062df754656b7366cc4db764feaf78c30d2599fbb37fdf080ff7abe0461b0a22332833d119376eab83addb8c0d9c208c13dc60434134febf9b2e25a9cfaca124772aa19310f1fc9d5443d1b47b7eb20eed92848c1b6928c858aeb3b525a8d2e90d34143d4921e623db3ee94b851b6728894934b5c2b6549f243e7c181f3e0cde30434916a19352f239e7ce77190a1aa389dd194f6428c791f1e327a54dc6f68c616ff1d05611cf36edd97553d7365b622868e6a8d56d174a93347ab40ddc08437154d53535980c1094bcfee006180a4ae99cdf386b31aa7fa124ffd5485155923c4bbd504c5751e2dfe69efc71170a3a423b4cba8d339f03e106174ae3b9f5eef7744e4a76630ba538cfd18afb9ca2365a28867e87b2ebdc8d2c14639364daa49a49f74958288f89b6b0188d665772857218d91c83daf9183369855227bd103a8e0c42ba78a30a25ff9c9aa4bbcb7f0972106e50a12c5a4d8e416fc9dc69fb3f7a2443b83185929ce039e6605ddeafdd904241fb55bb4567df52a251287c3a9d3adde45048f646cbbb4ce55a5b3c4df24e7aff130a5abb25faf9271d5379a020478e203d78006923c20d27144fbcc4f5a079138a9a653f36af857cae985052dead5ea7369fa04b4b687674ae5d5b64d75ece549a2c7a3e4a28bcc979d408f3907d6d8e1c3972e4c861e6a67a3f80b0f96186e11b49282637a99ebced6b3d4642696b77eb745849a2ec4728ccb996ec1b5e256776c3087d887d55c69b8a89a66713ef1b45286586939f537f76d56bc30d229433345756863139cd98851b4328c9def018e3c909f59a2adc1042e94e4c93c46f1d11e16007420d789f3d8f63460fe439821b41282941a91813bc648dab6f00a1307ea2fc09b2de3ae62faa52abf4b65acf50c2c5456b7e744fe28be2c6951bd9bca525cb62d8e8454938c947a8dd79519239864fede31d73ebbb28876aecfe8f7126e4ed3e6ce8a264625b9acb895b576f9f99918bf2c546cdfb9e848b74b6dba4114aee1645117a7b4a64ce924a124b1bd8b04541959419748d6b9d7758031bb5289cf465720c25aed6c11e3d0e0f1bb428a8bbba07a1ab55c19f338c08ea82ec28634719668cfc39c3907146c693b1021bb32869b3316955d5f193b42c4a2766708f311b3b09828d5814e39db6d792ac4cefb4018b92658a698d3e7ff41829e3c77b80edc7efe811d87845d9c438f357f3911f625714f6a3fd0893a48ea2cc56946263f9f68c859b88e935f03ece48012b0aa6393d598aa938edaca26cfa64cee9ea94e841aa28ae78e7899b246eb63b15b99eba6557b9d5556c7dd092dca369b612ca12ece0e103053f8200715414bdc4bca643cd9b1cff5394bbf4e829bda17394db1425d30cba772c53ef88364a513049959c99924c38d9b5418aaf2a7c6b74ca4651369d44a6556b703de908624a9023478e3382181ba228cee6d85aed233378fc0f1fdc364251b891f9246d59bffa988e20a604a92388f1916704b9b4018a92d906a194d850075bd9f84469dd4f9b6a135e820d4f9432fcddf64b29257f3fe2c390c11db0d1896250824913d63153e6c7a39da09b13259d04f139e898be57a8830dc88e36203c760431a30236365138c94eaf28f9741aed68a27872c998ac336c23139fb0a161d54a305192c13e7410be9d45852e513a21c3ad7cd49628a83f19a2f9a41205ad59937c569bd7e450a2f8357a4549523c8962a912a73c7e85086949a2e81642e6a4e7c453491289a2ae5b5a8fe820512e0f26c7c599127465361ed1be698ed858cdeebd57965646b92fb5238ad971b36c652731fd37a29c94741d5c7e3c67c9da60447946ac2419ee5579af8d459404ad6fd53fc14d68491125d9bbfcfccd67ab631251729393e7959c464449b3492a6b36eb246964e3109ceab6da8b9e8d6b8589f66872ba7338d98e0d51ac13f4c8cfa5e111532b4459732e3176c7698310e5d6da117a948a2f31d483482cbdd4dc6c4356cf5c3b66d5d0b9eac08fcf418f1008a2f45ea64cde32abdc0e44b9e24ab587532a44c7793b7a9460cdcc06208a39f234dec91e4f96c9c61f0a613aa64b1f53fa166cf8a128aa5f746430e9ddbac3c139176cf4a19cb9f3c65f494a8cc91a830d3e143ecf6e9de4f61973d9d84349aabd31e175194c7b65f4e0a187d297f09a5de6c943b9ccb4868a0ec2437994aa66d0313556a76cdca1982646696f923a953aed505e334912bc4b5687a2d8c9a35465b834bda143616feb57545677553d87526a38dd417e954341c83f5342638aaeaae350dab4bec2019b93d7dc6a11b5306f3599b653f69d2e49d2df903466fd26c7f7dc5038f95373834cb6a1a0b94fe94cf7a649920321c3fc3025b039d86043c164d331a64129f5bbd460630de5283a961e5e549d12b3a186f295321d72d28c2757cb6ca461afd0af716e2b1b68288819ed1533d9632a491b67287d9f8e7a273663fed8061b662897243a5f6bb50799dd46194a1964e8a0839824a3f636c860955a590139cae8b103c88fc7c1164af59d63744e42002d94c4df9612ba3c367790005928f76cecd9ba888572d05bf2caf457289d9a29498c155ba1285a4d1ead331696e92a98ae65efa9d76a27b3accfe654333b158a3f6692e09f1f1d6cc93c0ff341802914839b2a175b3731c9e12910400a65cd789fc49c272310200aa50d37d6be19d642e45a2000144a3a7aba047842490ee9a145f5228013caa961c4c9f1a975aa2240138a22e49592846e72550d080198507a2bf1c326f5d0213e4b28298bcd5695a5839c93004a28e76beaa8e9f312552640124a55823439476acc512912d4aedb5bf7135bf3e2e3356f8e5010b712630444af4dad2bdf44636792732a3d32ccdb042842417d8b1619a1c35cfe91ba1ea90c0f201e3b7488e1250601885012c5e42449d031bc7d9c0043b8db5637d62d67c4abeabefd327b9600422846394964522e7b26bbbc2640100a9f5409df1b94080084828aea53d17a92ba93fe4551e54c63f343387c81be12296a73cc467ef4c85e94e237ec7f7b6ed0e7f1a29c447df2e0f1ee9390cd00c72ecaf562aad499b02e8a57527e925c378effe38394f1b9288ac618a366f60f22322e4a72f36ed0b8db27681be1b845f924dbae6a8bd338992d0a6a6b5f3e99fae0a845e9ce4a6a9e5c213c4d5a94c3f8c98ef92143c87716650fe3ad77f2d1111cb228c6efa75262d29a7fda58945db64c921baf0c70c0a2204fabab9f92ede9321d6caf7066dfc6b35d67f32d6b43a606b58e41cc84a280c315a53953b1ef5d9f59351cad488cbfebd8fc8f51468e1c45041cac28d98fd6a6f1d8e5225a4539ad4dde3f11ff70fd781d3930db030e55f4d69fb7639c8aa491fd12ea327ed81415059d214ccc1d99496cce294a3ab5849e485dd1ce1587298c7b7d95b3b91c4dd5bcd798376da3a414c50b79c2efe6838314259395e326e99a9d18846314c5b34f92a0e4524c795814b57e7a977bdbc9a955699abed1d7d7ffcce10845b17f93be12ddf94f058af2a99226e636fa8992c7a7dd18ca4d535e1c9e289b7fd8eeb53f41c486a3138cb55ade6ba8bc685a6da6af8aabdd4e4d130d3838515ecd3ba51f4a5b897c1325df4c620ed14e937b6aa274c28e97307a7a467b3251b2f8f1aabd4d279828e7b8f02a41b8021c97289b0ad9b761642c51d8e41b4f3c8df4dfb212c55423ef4b5a2f49afb653c04189c247cfb7b0d23bb6e5248a1d548b681ab59d4eeac707f1000e4994b7f64bc66c8dde6d3252c6f3b0eb008e4814bdc4357aa62cf16489063820510e7652dec893e151323ec88e9133ca3039f832467ef41831447b84d5b5b1a697ebb9b1b39b1d229e65745e8b7138a260995b2676f0924a72510c1c8d304354354fedde45c746bff3480ffff1931007234a1d949ff892741c8b289d303d2d25e38a2886acefa9e7ec0c424e44f94d8365ce4ed2e98e21a25442a8d995f02e310879a200c7210a9b39675b6c8b6bcae0304441a7fce849289d73d4288e4294bd4e4c92d45163d049c6c3ace028818310a552f95cc266cf2769aa83edc728c3fc305ee01844c93a6a9242c49356d7870f330451ca2e219360d95dba84efdec7192960038e4014845c91969aff613509204aa2fcf8895e727f68d732643e110e3f14fe53ea9a899d6476501f8a6573d965aed94de0e043b1a3680d23437ecce2d94339a85d97ed111b81430fe52a6155a3949b879220aa4dce2e27e72b0d071e4aca6544c91e993b94bb432771e4afd6c56687826b6efa18ab318973eb50d22245fd7e1cb9f9d5261c7428e6bbf28c414f83c93a87d2aaf688f468624de670c8a124bb8db9aee98e319ae250d816a91e47753814f3a6bce79c6fcb32f78692dfc953b2ef3987c670b8a124dc4916da3d9824fbc2d186d2ed49ca37ea95fecc1951020e3614a355dfabc7ccbfd55801c71a4a21fc641ca17e336ef02336485e47623c8c19372838d450ba8c3b213e447e92e49e8662f67811a3cf33556627703494cb94a039ee99b89ebbc27186921c17a162ee53f5a87f941c39cc8fdf81c30c05d5d94bdce99ce2f383812e4339092d4f9adc838e6fb8031c6428dda634f19c4c5daba80738c650f6204aaabc0f3d51e9085e3002031c62280953edbe6dd2c618533c0c8e30143629419a9453214b4fc9031c6028851e755295f97ef00be7fa6f90ef24cc8912fe08e20387174a2bba3a8f509bcf634e178a69a36abc4c4a8d6f0907178a99420831da4fa5c7ec16ce552526d37ba25a402f3dacdc4e742c353683072d6ed2290d8e2c14d427297d7c93a49f7c2c94aaf39e2c62629a6c325728c6f5cfd74a4fd2366e85922925b697858ca30a25e17772dc1e9f0ac5fd919ee4547392961c1c53288926493286d689965e38a4508c21333ccae7d9d4a6f4008e2894379d38b2bfcf0437792814fdf2e40f1d6412d4c99f50924b8f677c4eab3c2d0e27946b4b1a15132253368fa3092521dc4c4d83fa68d77c193bceba0007134a629e549d543e89db0cc7129c0b5713d7ee345a19942ed9c2a184f299cc67b2758ee2c1f33892500eea639295e8bb259be04042b154c61cfdd7e45cb763e03842956e753532377799263f3ae4c7549dc719388c50927be4546decfc004711caed59367d94d21c5414003103071110abe93fe7e5a46e087cfedb9231239349f7c021844e8b6b36495e93e4cd0d7004017db56aab105715dbaa7abd5c311945642ac88e1f2cc89103b10d7000e1de705a9dd44b06f08b82d076777192205f944563d0a57ceb45b9b3eb0799ee74a650f2a2249654254aec87f81ced5c30805d14943651f2e37e12d446ba28afc6911ef5e5e742938ba2673e112afac6c5b29fda7676765d1fefd9791e93243be7926400b72827cdf0e99d648bc25a9aa93ecdf764aa45d9940a2db1dad3fe6951d67e3d2b49dc244cd02c4a264c120d9f74de389145594cea8927cebd4a271605e141cdbb7b92593db028099e35c624d8f89ecc2bca353a1afc7357144c3c49fc98e35733de8a624e9f847ccd243adcac288bc992f57583f610a755144b9e26556206111d4daa282799a7f7ba4abb9529150553afd9849b2d5db2848a9209ad35a5b1748aa29bfe384aaab98c5a3245f9325e568e28318a954a515e9b0d3ffe69e20991a2e41993b8f7b9268e925114e375de64a26a429a88a27cb27fda8a3331772714054f4deaa70414e538a65affe95e73f389d29d5493f4aee7774f1437c79f4d999386dd899258790de336270acaa4d2609d6ea2649b94f7d57da74613c564a2cac878dfcf9928c9aba6ef151f1345d3db374af83cfee15f42ed1eef2d51703b497ce835f5f0be1285edfd36a143d693d853a22498127372737faef69328eee8bc4d5286d46c9244599390395afc7350279128e529e162c30e89c26752eb41c5b9fbfc8852db2875adb7a249eb8862504aecbb96ceff69234aa324a963caa0c3c91231a2b84926b1775a1fb7448b28dd0731763d2aa224b45d88889a1173224adbfedae271e4680f11e57ce2a7eebad5b20f51fef66cfd98d14365c81045d1fc181db5d4bb10052fb9676fdc7dd42744614e36f9314906514c927c7efdbb208aeea7bf438938b303510c2703a2d47a5f2fd62342c87f288edc52ba42f6434912bfbc24f93dfea40f852d6dcf6099be36c987f26bbc7433f750be0c15b277b6e418d44341acda4d305d928762f61525849636d72dc143d13a57c91d8ab1754b57c865a8c90e250d1b8f569f7468ea50cef85994c89e0e05a576d54d08cda19842658e696f749d20391474f3b7c589414925270ea5f0b2f62475e050526e7e7d2665936bde50ac534ad6ae6f5d3714c4ce79d6d84f1bca39fa6af2c86c286d87df17359ad4b86b28dae76b12b583284d8f1a8aeb3a26532931e84b4f1aca2b27c82446068f221e349443ba8f97a43e299d3b6728b78bf847abaa91a9638682dd9af0546f612b2a43499630daa762b3930c19f658951ee43c866295cceeb94b12e4d76228091a7ec408f3246c1d8692ff5629a964fdd03018ca4918cfd93e1b6412fd4249466d936fa25e28fa69f60cd32b4249174a27c5c670a198352669f4c22d14559448357552cdaf85922937412de53e4db2502af14451935f6d355828dfcceddacfc9abef0ac532b9844c5aee7356c60a85d7a4c49c9c64af4d158aee56bf9f4a5428e924c769ead0144a199bba94870f3e1b29146cbe4bccf3d6c9a4a350924d909bbf1e37970c85823e93bd4fbd4f28e83826991ca64e72d509c5f528daa48ea726a64d28061db7493861a99932a1fc5be641b5c90d652ea1282a83e793cb834ea3128af7b94af949288bde6849a1cbfb3b2494eae4a968d5b3123d4261373727ed79fc47334241e7d6104a30b1f0cb8b500c258e66927a22a084934dce984e86504edd8f498ceb26a78c100a1bcece72a3abba1e84d29ff02567da030042d9f34b38b1a75c2cfd45d177c3d97ed0bbf9f44541dec9d7fc1bb327d98ba27e7cf4b061377b9017ef8d3e310932d8bb28e5d9fac78ad26262ad8bf2076dcd11ef9796752e4a924713e593122eca22efd357589aa6e91625a18465c9e9d8c17d5b94478785e887a9aaa816c55c27a98f661e4ac9a145d94c30f7dc2aeaf43e8b6210428829e9c34dd46551ec187563a963515ccff5e92e4eb2b56151b22cc176e7571454f8c8cf86de24b32b8aa2be593ac656949349728b79ebef8bac28c74d73daa33509177915059973e8a0a1e38be75815e51332c7d6c4fba0394e4549850ceaef3c4645c933a52625da65b68e4f51f2d8d2490e2564ccc5a628c9b959ee73923dfe2f456947e820ee4446358814654f9df7e396dca2a351945a64248ac2cccab7a8d8fa982414c592e76b545c448a09288a57f2df4bd0270ade5a59da931c6d1e4f147e7d845092b727eb74a2289f499294dee0f1349c28e79ecdeacf31e8d437514c3751a77afbf1744d9453275592fb9c89627637058e83a8444ec5e2c1682c148604c2703014081e5f47003313000000101c108682d150382616c67d148003593c22463230221e1c1018160b8502613810060483a130100c0685c2a13020188cdb5295d603cb054c60548c0784f6397345e10bf91864179e38dbef2d58da42856dcd9ea57a73d4e80c2aa86d9d328280fbdd0b7b708d041d5df71eec76da3fab5abe9e45613bd4dd24a75127a7b8710dcf8fe7744892e6289db296f18b342ba9a637b3b1703bd23fc4ede7092cfc05c6e54ee63193261835cc49728bcdd448e3a5653a4b1d3088ec98e22fda517ad7f80b9c7b960d1a3750dadd9d3df8d503be34d5c5d2dcc74f63f434f29c0f46e353849df0017656ec65e9d30df0ccdea32cc178e77fb3a333266f23c9b994907c4889390fe6e48001859f89c0df5757c2a15a652573e9b8985ffd5c61a76f6d0d900f8e743232c176301f992d92489c788707940f79bb8481876965cc59f015c9fc2a947568e3be9f058baf0026df1e108be8c0faa6124042342fa51cd46c2654a0d319b6612fe175ed1eb503837c4d70570b256786337bba13f2a07eaadce9710ea1ccbe3a775c976ada286a9006199c7be6fc04d7767bbaefc726a1c9be22f7d4932f4ba423bb57b3e77b3c0ce3ac808f40d8d3de3da0e541ce80ce8a1b40cd2414f119dc587e9f9ba3cc0061620e9a96d92ede4f925b9a35a66f7b2c89b02df4af868a044f2818a8bb5c77b0a83dee9a3627c8fe68a462738bda98c65d3dc5d07200de6fbe26448f37f167959a25187029e197aa84988b31c3e6a30c04ca743cff0c58d7151e0b62f562a352d63271761dc5dae7725586bd9a7c8ee74ad5000641c547e032b7332bc9054acd42bbadd3c05d130a1a69659cf8d75f60784f3b88445377127bbda39ff9195eca8041a224325026209eb57d1bf251ea08e0654849bf4e8b3d47006b1b1517623c22505b1747c33418254420fbb1aed461891a267281e16fab5d720d0097309b934b31a8e801c4402087069983f52767331ac1958e93e3c267d19eaf0be92c434c5005b409c44260bf049aa01b0ecc126e09a3a054062258260f52d051afbc3cbcaa7dde100647046526b4b88ff836567e9195b1efa6b64333b3c12dbac33514521be0170b38ac5ea4aa86d1b8448dc868c80049c56b9bc07fcbed8aaf725927d0bd163bb8185b16dd4003259c428d1d32dcbebe03c2d2f1df069fc501f522985be8ad2c680213c56aa2d55b326529d7bbfb9f525739a1af8c9f6966d23fa9eabcec7e9e893c56a2bd07229574550ef4987d6179014d2844677de29e254a529f096aeedf460511efab751a0e142f6453cccf8112a9aa247f10122db2e6a8aa60b90d5e86600fe4a9c14d7ebd6d8276940b2c55a8e4654295122b6729cc1e2196aa9016481c9070a3a8ca5d82f17f709811fff44af5d02b6e56cdf3090ff16c5bf1764d9eca328254a37c32993b14f1686de8c8fa3a2672532eb7e31a10b9e680afee4127fe4455375925b1eccf17fcf8fd94af1dc95da2a778a8443647b725ea6874ca7be3429a368320fa59d7900ef85eaa4574fc71ffc8b8ac8852e1527ce95b4615b12b3ca46a6c9f33ddd8ac95b5d44685d2c885212c26aa3131166caf2c8cd09b0ae85505f150eecde26e9ebeaa96656f6be158a5503f1c23d1e8bbada6007a8e5596a696be729ed99cc67f3f2b6b5385df1af9d18966a8f642d0e1c84a8aeb725d2a14a1b00cbadf04313d796a5c9831088fe121552d7315d02585a78797c290f7eacb32204c64e1944de58bda0ed86d2dd1dd8b4026f52c937f081c7fe40e73b429e7a3d718e1ae4ece50f06af56f3208aa8e22bd0207545a74e9811e193494adaecde40824e3be7064762dbff8cbb39577e84bcc6a7961e3798ed3246c57a829883f20e9cde2f3540341078035a33be392278bf9ce6ca5b70bc72546204e9610d89117dc09709c87b66b2ef02d935776edc44076c48597feeed42426d0141ddbf88ab2994df92ca05b16d56456036d1377623267f43840f512052d630a23a1e758c76a83f936d169b23c638517413871530e6dfbed776e859b013deafd00167cefe00992c3530466710e21cfac2b111e8e169786636a0630e3374b2caba268b3580aba762eea38ae21dc1ab40664c5876ba656e1aee31ada063bc47131e5448ef2466a0458441be0bc34faad5970aa8ddf389edbc3ded4aa5d207e6c636512b07a4ca0abcba09fa71286cb5bd6fb305733f78594c6863776c291c3e0e3fb4e6d59c0839e9ebde149e2da2fb7571121eb5967b7ea5cdf3fef29a44ba52adda0e65aa6e8814314cce489691af69c2e59c7577be0094d531aae83d1ec12019ae686ced959da7074c903f0a985b6476984be61c283389ad39dd6cac2b209469c18c4a9829eb10425c1b8bcf78d2dbadb19e8d8dceba490fbda756185e9868edbdd15306f7d2f920d93b491081511307a25b1d2f5ea84e5c8462fcf4e555cdb9736fb2f8b5378001cb6b3eba07d0069f4dcbd436aafc4f5b02ac44e4d68ef17fd14c032f6e44fed56490ec8d579cbf03ee6e3c49339ed33ca70c15311f3392bfc1ed699abf4c6b6e7506063e4854815e2f4706abd832dbe1fd69684a4910a896fb5e276d85a27c63476795a5fe047cfcad28e11fd7092e60f674ef28bad8270e941eedb535a17090440ba08000081aa98643102cf865f7c327d89d07378c3126fa1f463fdb299bd88e57dfb757a0f7e9198db22c69be641581a527362a74e665c0f8d02a4d25ab514a16b863528367fc6efe83544c3daff50dff9f68b85a52a2a766cd32e12042a4d8442ef6693200702407449dac2e6009a1997c5ba128f1cf428bd19b70317c15bedc745cd535001ac2263f71c1dfd4cf4e6bfaeafd9f0e16c88f12433df04a551f123b35ff4b48f28190bd07b6abc79f733d71a3098c5006fd3eb7a545c09b7b3339b7f692403a7e346758eb1772505d4beca73f0b03613e90fd037eabec8144498cfd6241d8ea4b7e41e1a96dc680ac3c98b5a65a2afad8207ebd1f3d61ca087e36438985d4463defbcf9f2abc88577a4b35ba053bb3eabb01539007ccf0922441ad6a3d1ea8c116ba40a2952587c94172d8dfe68a1575e0a0c1397e436bad36f4c6a3cba4b7c4d970ce0e542ca2e16c6dee9e691c5600f02b3dcca96d3153912ebe48a899a20fa88b8274ed68ba56795717aa3be4d07b4c4fe92171d03041f328e54c67d69bd913711b6c6d89eac6be598e967be58b10a80861e9687fca82c7eec58d29b7f9d5cb2832039f11ed0c97c74938c80e5086bdd58460f0dbdfa21c23ec758c634403039811a8c49f23bdd7b25a1d8f824cc377cb51b241da53cb6811bc430699f1bcc345c8b342c3ee947266394d1191e8264b8e848fa07432631267190ca3792e4c5b901b7f4a7d5342132c53e286cc47022dd2088d13651f051ef6387f2b20e1cede43ed239cbfcb79db0e987da29c530047bd686b53e46a20f02dac2b5a664ef796138ab5f2186d03cd99756e9f9743c901f3099acfa76a28c649a506446260ca1028c43bada59408dbe4fa0710022f880b15295e5723b75a13a40858c0da3e07b7fc71bfab6d9b49e6bede56342cde64e79d47b799782a81d5202247025fe020c594cfd8bb6b0ae44d652e7604034dc85b23b8817fd3b7aad82071a056ba745affb326ca04847c864e185a3343b5a9af082ff5093738a2ec21a1d4c211ba64f6c1ce60764d93cfea225839998baa2919d027fe50051ed5e790fb1fd5dd840183dd00ff5923a23cd4802a50d429213fdba14b633fbfc1a4a804d5676a9d7957dc80186d5c6ebf1d753afe59ff985f31fe4ec2e728ad0d3c9383513244ce0eef74ce7f86c5b9e7bbfd811500d809aa6c5631f4af84dc6e89c339ab95727065bee6588eea276f86df69b0686390f690231c53eeb650e6df1158d355bb5bca6b1a26754d82b4a3d50a9190bc87e8196de44ab6dde5bbae0625212ee4d272c203bc8a0801ef7050b30e275cdd84df5dcbd66a2ba818e2d9ec818a31bfdf92c28083dfadab95963d4944262b795e8dd7a8855c2fa71767ae9996b03d854985a23adba0d9399231cdc1fc6fbd8a1a3a45ac13c3f11bc3fc0cca3f239bf5c72096840665c6b7590ec5c3192e46339460daf2ebe6e792c10cdb5bb5cf698c9c8194e6233a456a0d9016be74ee70ddf745bb6caf820bbcb3c14df9008a87fde04e77611e5af3ea1c5dfe7b0ab280af2b0da0ae4afa78861a05c36c02e8cc42aa826bac64aa4ea8464177d09f56faf311bece6ffee3c013140fa7ad8ba888e23af960ce34cf1d9e0fcdcd0a8b5bdf0390b57c9531843e261ca60fe4e9df97a27b069a6354d816ab5a4a8d95659593c3053a8b85c65c8f5fc6ac40fca13638b815175728905eb74a49575ef8c1392e81085e0974c230c6587c48d108b3530422d7056ca17fe9be29325be06450023c2081b950ff02c681bcba5584238267b17543f8d8b6c0be7cea95f7995e5528d67bf545cb0ab83692ad4dbbb84a0aaf4854022e64b07a3dda75b8e541020c5dd9ee09e64d017c578b1cf84253d60cc0292d5e6fe79a50613033a9f870e7db394249065548edb6de5b1fe2fb2605c4c3815e88d786d339c128808e18b5d9d2ed752aac942118122127bb5153a13a2301b080c72c03f5c92953d1bd8ae1da9359a1d162665bdcdf7cfa600172d0219a2fcb43711e71348e125a12167400ea1b631c213201c473b487fdea08dab0db5ebe4ded91ce8a6e8d0cc923c052d29f702219186008973bb2d487116d3b2086cd04b685cfc5e0303927ee26ba3580521cd312a2b73ae7bcb0a06458d2ed5cc4608cdab2a6010d95aa1b20143c1a2cf6494174412fc711ced275785ad3b64f1dd6548c0e31026cbbb0c156a4c9f20930ca7455a47078aa503fa58a59480d8254a9432c79201be7d433a0949953de30520b738f6c91aec6cc03644418a98662ab0e01212def4f64128a598ac16e08428e8532464660bc7a92cd908170543617062c3b476620692f12b627860c58bc984c8248b8e1bb893407a81564773a1ae494e1f11190450aea1e44814aa36e21e016a735733a9ad466ee4bfd2cac5c87c47ed2140006bed8ef47e15a100f344004b5cb23fe9d73dc59a1df17aa88f2c41aa732ba86427600d54304911359c9220e25302695cde96049b68e84ce4f9a1055cb75535a2961ec33d8a7728b1b3516129593f32f31c51d58bbdea7380b09490ca011949373afb67b15acaaeb3b6ffd701d769fb754e270e75c6b4d317ba1dc7774064482670aa671b295e260c1dd98ebd343be1916d7809791fe3778e23ab1f86bd22ca69552014c49ada4c9e4c00de074b260b4eff485dc48b02d7573ba486bda40b03502e441566a017a57a81976b7abcc53de6665831e5f5860701041b6a0e5fe6edd5ce47423e60af0ab05cb1553480c66a7e34aad39c1fbc1dcd82129e5ebb861b76898de449336ba6bfdbbb45a97de2687b9492e5cf3673de9d7c340c47907434c5fb1caa8fed036637abf78eac6e720b59185e4f6b0480908e6852a5ab6c5ae39d3872638f1af4b0c9a65809eec978e5a2e91b5971950ac72aaa5a8bc54fb61e5182d56adcb3163b869ba6210710b0116bcd4db5cb2071b5ce7684a168274b118a4c4d9b90e5d4544803501d8e871bb12b8a25dfdc1e920f7e014588c834d4386a0ff0e8a8f3806ccc8836a7194fd774d13c50475dffaf3a06b6fa0c47e9127f7c2ed15ab5773cc6403a0fd2e51a90163a73c6325db2458508acdc091c017d916fc29e3d500392f4e94cac927867007b3f3bee4070d7e78257e22d5be9282ba2f22b9aa6281240d56b0507b0b6480741b8e538da61249dde4726300809979a945b41146b730b2bdadd00c8d9e37b7e4882d55aad762348bbe76c58bb1211c5b648099c805bab360d8f81dc22ac09d57e8400b0333179185664f3ed22e84fa2c673a2b5b732948cfd2ba67d3f96d28a11da54f9282ee2a264fc55c4e4be603926e40cea5c6b853d5d5316c8986fa7c4ab571b00fbf8fa6c557fbcb9539d3e59a963eb07acf74279934d6514ae29300c61d395c25d6f9abe3cda2fb941f58d30b97882cef5a099c6bdd919970f0962a8441ab6425a44cd9c969d982e8d8626d5cb801cae00fc1c6a62e46c74ec7299352194620747dac03d50c70364570eb86e202a6dfd3ad683aa145bd3a27d143a7a9019cb7530cd1a6a4f5b3c5cbce1f0a985078ba130c23026012b01682cdd8ab350eeff95bb5f11cdae6311c840d4eb285e03ea79b0b099af3cb34edd89c2a246826e040d7c19997fe8e002424811b0b63026bc0ecc5abf22bad8e257d7c7baf7564b4856f899ccb8995283d19fa6162b8918c3410734e8df454b18d944548f410136136842f228abe2fa7a924167460d84963c2663cd66f525d92caa7c159a426d309fa71c454bcc99b05ae00ee33c0062d196caaf390a825aae77837f21d4441e8c39af007bc0ecfb8531ac56f537341028fb582cd2cde7f8942cdb48cf468b263a6beeebcdee6626bdbc423a12c24ebbdb50cc401b3b58e5eb52b63fd0f1629a8aa6aa87081c3e62b758b2249aa24a3bac632ad37ff9e7acf4315ad9a179c0d8b35573e77a07137eb401f7a13156e344834324ef3a6699ce28d9d9710d42fb158809f572aea3920a18e283697378fb04c9d6765a17a34bc1a420330269586a4b079a28100b4a9c9ed98882e987e1b55ff098b101159e2663a778c0af8fede0d33ad13ec78d52eb3801816f3afc7132358e7b7fa09c82e59ba89f997453c1c3b67e3b77f792f22e37e0a8de8b55f7056df7033343dca32fc95f7a62bd450b0393f2851dadc105fb1fc5c67da77ef6cfb3a056543933b6fd41c4c9d7eef1f821c3c88a03a9024faf3731db97c88ccb8b516cffe1357704139092a124055776435940ff9e1a04f2b24c0bad9edf1149d2964848b9baad0ee1c9026858f137227702b9023547a0180949c1d0f0dc7618617d12c383803a7a104d731be751cf50ea89a19b801886d4d88916b73c85ad16785b9959091f437de398426731992b523a4cd49ea274a3554296fbc909d004b6915c9506a80fb9a3e5080127a2abecd2a8454ffed3ad1543b48738fa08cbd73618d83b5a82e695c43c5b075ef9434d732de4392ad52b5488126fa473254ee765d61f39ae4da2d0165cf7b1c13cb0a15310b404f2aa383ad87ca319fd0c088b344eda9089d0e903e28d42abb6e943efa6446f3e700d24bea07954e8cd135e462dde134effd7c3951666469217831228d891001b738b095eb5ad25795114dde1a8a708a3c81a468ca88a41beb520895181589bd8b8e5411890d3ac5d7d933159fe9babb0c220c3e03c750cdb3819a87818fdf44e250274e456d691215b9294f002c6b84b2bdbee69d4ef9e228cc13192027716872134c32277d257260c90fe8a462f59eb9a09595bc9b23038dc0ea50cd589ec99385139e96b44c93a1c9963a7c4a3a92142300c91aa158c80e00238c34a71f27d08a6f4437b0a8fb119d4ce156fea6d62a62eea7634624ae14ff20f1e02850b16108a794cf17ed032c1f61b99ca1866349623ff3465c64641e845a513ddb27baa106c2683948eaa1446ff2211e45bd336fcbdcaad27ec92a099805512cc2b59f7d9f62be97f80cc92253185b43c65e1212118cc8c46e742d6ee50b29ed987b04b3408c0dd690be384fb99be7be6aba37c43f9290aac28650d0e6a4f508dcbede6cc92a75e79127126aa32c75242b4c97d67692db4d2f56ee202829501cbb1b9d6a82ca668684e2c231db61819998ab6c765fc6708e8231f76e5e9fa75b7279c5c2c4b3b52cf7b482faf2c711157dbbe358d0a5007714d5ba2954f44b40fade8df1d9b575e5c84b6f1c0d0998a8a79c547fdf17de0899cca7cc518cf139188c40d26f25b2905d683428168db889d1e01ee6a20db8e713a56594bb1cbeed42d37d9543786ba324f0e70cbe6cc6a191b8382ba785dd2777904097d87dc571ffd76d91d421c01e4d9cc4541a1ee54072d8ebf384ddf51d026a4d917aac523bca0065f2d9a94a7dbb6ee35180e58f44d82bd7c023eec65789ad7df23f81b84477b0826283398f7f164f7613bd187c877feddd9ba6d4a520a20990a02ba224bdb235e8382672bec92d713edb88c65532dcc52b794edf49d8d8cb0d6586d84be627caa05801fd1a7cf50e0fb16793606102a2e07d49314506e294f31facc52c31ae4aafe7e864e1f67c7836b0720a430edd61faab9b1403a9cfebdfff10aac1e10fcf122f5e867f195d098d6599af3186347d7b83e935e1f0301b7363a2fec3a5ad47260861df5f4da2c87f489d27cb658dd4d6798d85adac7a10a0301238204c966e185ad6c7547264df8c830e6062ea45049bd736eaeaac294d3daf9182457a6e4a8b9709d0a24259dc035a31a0810346bd3cae9f5f79649f70abcd6dca2907799c0ff13fd1085ba38dfbff7f1501f644b34c2e20b7ea27e5384eadfe3fc4f6b9a8425118eb982a329e1eea3138dccc583d9eb8877e4fb43b248d5fcbacab1b7b7558678bff0a099981aa74b521d488e02ab79a4bb80b5f0d92ad85a9e01d458ff21ca064faa182875ea5753196bd3388e1153cf0dc51f80dd903e8521b9a670bb4783b7813551b2699910adce52dde67c4e0d4d5ceda80197d6ae04f936f8fad80fd41e473f0d1ee48220c05d736d8d8880538e0469177a8807a0d937fc253192fbc2e518f289a35d1d276f136b66b126289e65017936a596b01187507abac4ad6b8d77ca992e5753db05fa1742d6448123a17976c6898702b2448f723c7267a66ab0c205e53d021fcd91b13f606b616833f2082d57990d70e11c4026239e2dc23af80750b1217fe2faac2a1657e987fc88d42db3da429735fdb8a11ce8927ef0821894ef4cef893730c49a214c03c4eca8766aa6db06df03d7af98c87386503e1c2461ebaf0caa814168b09e267a77008a239088d4c41e3c0958e168cd8b0bd97e2d7dfa9e14cc1ec60963e1734248dee4db0d0572515266c50420ed52b3107b362a58223229e33bb8e6f47e60a3d20f2e8757afe4adbe8299ac58f3ec3ca7155f93305d06c608f08304ee50e53013f23f1ce717e0f9c698d307801ccea2a40f502eda7e178770891f08b91fbd97c2398500c56856059995068ddb129642c4c8fb90084bc829e8080de5a96d3ba148b33e5f1f383573053b0fd25905d8251a6cc4ae3dddecd7d077164ba38285f05587b84a01090a45cd97db5f11b622f03721ae7e536ee8d77791551e13b2f170b4cf63c7a84f8e1c9e6a4679f9cd8116f52f688041ae4fc98068060a807b9deb995070897dcaf134f3e971dd98c71311636a9fd923472d99e1d7919e325675c9cfd003c4718170ba0e3477163bc9731ea15bf01776a2d72c31d9050772c14e706a49e58d225730306b6610b16bf17018d37107231e5f55c3229c81ad4c402eae4981547596bc7b6ed925c1e4383bfe9a47a754d31a4639f7c8c8ce2c12c645a95421c03cacccbd6b0c918c76e258e03fe30ebb0d8f43331fa16d4c55874a3a7d300a9189f0354c90f54d271b016e87dc360a7068bdbe4469850753908021b747f94c7f2d481a7f4ff5640f14c8701ab62e6697a7a24d199762b9f91931052edf014d4c6a44237cfe790810df13f124a7a905743621d2c8b7bb35b17a9b72b25e38a91f10255a49420a7c5164758bbde64138e5f5036ec9314fe4160744df16060c1a7043b3ca52f8dcbe6b59184a760a2da591d8410a3ec2d75349b255a200bd0abc33d34fa4a4ef5b2621293b5262c94e26d4c1aef7d6ac248c0217a5671017cf7160224f1d761c34b9f0c4cd4ecf64f0dd624d43ae31533f318451072620cb660f0ed7ad5467c8e23bc7bc2e7073177542cfd64d81d28b6aab35020efa3268a63217a239dc091f9455ae47a487cde2d9df56d1d4170b28e2ec89af2cc9b2655a75e0285165cdad94c35e3155309ce6036b3a4c7297b7f5f38b662310c4d033edabfa8b3087a39015dc25df6d508fbf619e17c2444c56fdb7ef20ded043ce60d4af863c49660cf123838ece9e74cf70084280864cf3878122ec6a6607ed6055fa13a54af3219979ec318c44e9496da351243400ffc07ebb89e0d27587328aa49fad8891cd4ecedbca5e17d8c25b63c2ba0104430c1fddec594bed3d1126b68f34836b96ac01426c6e53ba731587ccc53c8f2e01c6524bb44a15c9be20ac6fb8292fe63a0f46c2ccd2c64067d3e72e4395e43bb743e230e797491cff49bcd6b4fd90443305874705238e626448862f2d18da370b468d99094423afa6e443301ef7ab80bc9dce2e65fa1e410a5be8dd9d8fa96eb1f922485ad6987f30308cbd1b73a6940cfe861f6097eb8b71e77b20df91aed65cca2c941cf75b079ce1a08e3f0348229925f6c447abf2baf358dd0cfe8b7bc2c521ff9b593e141ab796834855d7f51daccd392a1f148e5ab0287cca014ef1e4b7872b2bd683ab31d2a93e3f311556235fe623a942234a94cb936364cc35fd2b582b8026450c5b8c2f86a75214b3a38b1010eeb9b05125e484a5038c7c0fea9444d98d0cdac0e8ae27cd85701f552627b75f0a966c63d39106b583c832e4c2058186214c45a17b8f3084270b382a4ac2626f73a7033b7e1927f51776d1cae845c1aadfc628cb6a02a70a1b38dde8ef69c1747f301ccc08a17c37315ec8b845d8d7c62703f1ae6f22da59cc659f30940b05e61d97b8905c2e394d314c6b2ac69b44ba1587e29a67ccca84cd582c348cf4ff7ed95d57a5f26376a8d869973038346e09921fd111532f4162e88d2d7afe389390b29a3293d2dd0da9f92610bb050aa5790967a053ac676c537ff86b2d01c3da489a1d917f4c2716c9276e5e095b40d9ea88ecf12ce4e0e797fa31c851aa5a436a32cffe85c1294ee0da8ab9c0d72b927b06a13c009f4c9e9d0c48b89b8bd7e1c34b1daebd214aaf20cb3c6ccb494d7c75a6e8c12850c7c3fe35073c3a22d1664e66c51183dfa1a506242091d188bc4f7f8a954542102e779f57c924669818179616f212763293ca7ad133c467ef5b443aaacd2a2e2139bfa2d2a683401873ec4356da588a6f976ed07e2cf4ae18df0b23e583f9cc67cab8dc4887c670c0698f6957c22929e1e1126580d804e0e9e6bd41d7460db7c130df0b4f82410a8b06bf07869fe6e89ac2c376d489f488fe5730c603454508e4a22484790b568eeed2e2865ffcb9e20b3195cd1f961c49b2f558c9870292bd978f5a75a77ea1d18a5b7f7170ea7d8aa5fad2ab4bf3c2c01329469508402624649e0c2f5b599a1612b8936b3c44efa48128cb0da2ab508c4d66ed129997c88efa1226b00fcb384ba1e2c91635f49337e04e339521b57a63a2df39704e3e516b0936a69453de0d4fcf2cbf401390caca759b283ce9546b3955a31f9bd14236d7872377fdad8ecce65d1b32275bf0d4c3ef3f47d90717b59f59c39cd8a25872634c96460c9edbd02cc0c6ab7e417dceaad3a0c9e340a1b6da0614cabb418aa5700839576fa625fe919f00395ca4fef86c7c7b055de446e7c860f2e563c76e9917ccd8933053b20f00e0796309853cddc82d341f776fc6cde1e44436d9f00b8cbc614ebbdc48500e9b9842567fe5adf2801f6e64f97e3c0e64dcadf2f325d613e617850d3591f2874245fa39422643c228565badc098c640a2047993c8f76113d883bccf2e34c58fa537c9843dc3fb3d6aec58aad70b3093316c416d8a967c0eb2eb10699b2f87b9b32c4e7458a94e962d1508a568f4ebece01cfbeb7b28acbd9463155961be79de601f1738cb1de129d10e6a21ab35841d555ad259a24226d7461c26b97e3535441ba34d19c499d703986880f28682fb99cb7bfb884bb826030566a11b60671f35a257fbc71aa5717f34a008c2e853db4c7c7944f585da294679852d517397af33ca36b0a2ff9f01b69ca701525e19be7eccf49247e0853c810ac762cbeddc7d3cc51b208b7a04d97b3e561ece9f44469747d7722cb5a17138211c535995f18ad252f818f82fa5e2d416de1f6fde3268e39b4fb89a28ad7b8af9990b1cb49677147592d490b9cf8a00a8d74510cb8ed09ada98b02bfbaf114edf376669a871bff3d8d628a6dcb0ed463d9005dcb36af96cc1b3d475188ef65111b906febdbbc7b65896d808b1448a7290f34deeccbe3cb6af8defa8c667089c65b62320123daf89ab940d3156d5fdd5c8fb8031c7b4acca012aa4a95eced7744848882e0cbfef5a39ca3c878de43e06942693e1d40ab73da48f683ddf5f34354547a85a882d0520cca4c6c4e5afe0921a2066089c24b7443e9199b72fa906849cc54f55ed22f865c622e61c375c0c5dc9470084f6eba62d5401e2ffd4f5e22b66999f61d00632493950906f7d4b0aae5df82567ce622fb2bd383f2e0bb691c3dbdfbac835e79f19eeb0dcb1b6f303587a3816e914f58be64025d81d1278837ff033329c0701833ab9d57bdda5f720792a107fff6a2f80902f263544254dcbdc3eb7351e76d2f762655c7ae8b30c8780be2ac2ff7dce6464f44f7c1f3606907229234e177b39bf6f6c53b9abafee94a4b179fba40cfdd73fef0b758d135b7dced05a37022b7ae3bb1ba3bde3d0bef2ebb7b6e4015b930e9d6f7bb7511f053bc4f784966097ccf79963b87d261880ccdc14b32cc54d0b9eea14ff2ebb82a0dcf4c345942ed9644daecaee4f780d53d8884654b5d4da2ae0623094eedee9189aa04202907f17c57979840f1c1aefc28f3464811a5e2c5c5086b060510ab4ff7124d9e41cb14e31b2bd011db226cfb14034cf2abd1134cc80ef1f1fc54673f054c7a9d9ee6fb0ac3b68108ac8cb59c6d63f9f21306b3c56529dc460c6a620ef72e8838ef8dc45aa1cf16ce323f230585ed8a9c7090a63a4fa0943ec4c4aaf76769f05806b4bd255640061f8068507050261547e2561a20cd680ba18d8e169b51f799c940be7a6dea6d2278d6bfd525f70397f4955c8c19ad34d905036042904042d6406368412a224feb310492e0dc1e2d60686f6afc2aa29b9ba14f0a3ba18de19709eed9f15c60f2193e855f724319298ed82b0832249300d32bc9bc045c670b478058bb23b0c861f763984aafa14d4df9f8273026951ea5a2193810b743badf459c94663a1dbf601b882de136264430014a90a1e3c7ea2eae8f20c36c40efa7b3b75a6a5218bcf5fb7e075a6d173e226cf6766ef39b920dfcd1cef0e53be293b63b1d9d00c37ccc76bf92c1b97bb7d3b5e3360458efc00873f78250071d5df87e351497de8f93aaed1c62358264eeda8f2360e19e6524c5dcc8970b4f23a65a6809e675f308d98aaaf2a82e330a2905cc15e5fde17a27d404b5c104dc32e1ce98e7f86738a9212331d79dec3dbb567e002f300b8503877c69258034849df17397ded4bce1edb7b6f51bc6e6aad1b03a33c954411d34bee200565f0c45a5be2b6c971a94316f28cbf90a6d6014c783133ed5c1516fc0c4638762caa51420ab1b435803e123adb0a7d179927b4fe401298dcfe468000874138290ff4366512262007e48002d848173e0a8adb00d0d0ccff1a0ce864429bdac1e0779eb0b1bfdb704ac5e2dd01f26bf6eb03c89567880152ed36224572f9b85f23e298465c7158e8a1a888f5a6ba831716ee662c9c0ab0a1d30a1455922f90db1ce86542b860bf3ae5078a84445b4441de5e47e55dbf632161cdd52605ebdf9b765b2e62d4b30a5b442e3660ec00e7089bb516a6d3efe91239f779d123112796a36f6844742a6a0709a075832b380049faa2e0904af1feb6ff50d482bcd449e44a4a190de608f44c081ad415421aea3b44c622dc05bc3da4dcb3ab257e63ac2169cf13624d2460fdda13bc7256e3287fb4a4b0c9ac8fdd0d1c7e141cbe6240d0698592c9c2a25a8a1510c0f87c10423f6e6bbb66e5875cf41dea3f88c81de1173bf7e9f8231879f6a9afa424e721ca4f17dcbac68124646a18e0c11a7247acfe9171e07fa5ed89d7db4c907ac22b127ccda14fb8945f787f4ea6a4c69f580a8a93796e04ecd14d7ae9516f9600ba81cd1e858c57c971064bcad9134cb2b94eaa438b8c6e8d1c5e20617f5f72154a5f44e515cd4c61bce684e38d2d58d9938a9d1a2e03c6741250282d19645adf095bc0f4fa8aa9591c51611dc3e53c02529e9f494c8a0b860dc42f24b85d7134b9ea50a72a68a0ae0bac20a8fb88192ee59c6998ce2b959bbec44805f35a419115458cc1ced294a17ba30392c61842716d16e027ba6700617b7ef8c11c23c7141d278b717d53bd922205ac3842ff070536ae0e6483611c9524d6a1239a23b84b9c198e4804dc8003f816feb6f5078ef10952aeabe5dfcba45d41f155a8cea52145458c5aa0ea4c0e05f39ff2cccb56897478ad6e5df441110490e7448cb231d9b5d5be9afb4739441b5010773b8a8578dca4483408a5f0862648c3dc2dc3e6ce97d398aeaadcab60c769ca58ccb13348cf9c3dcc9e401a408d017558ff4495c3d88d7f25aabd51205edbb4c7cd2837a2505024de3ce145426fe0d7fb45e64f402c79dbc0c4a0e0faf20009379cd4a64edcec883cbb90f3459a116f288f24b8fef6a15e14520eb9c388cf7612c0032dadc55b942837bdc080b65512daa7efa1da6d128c6ab3f0cf3475a4d2f985d25a059c591582bec0414df49b29db5e55a1f2a747483cd03fa0b2e0f5f44ee07357f413799789261bb617f2e18493cb0418c18866ebce99360b8a6375905bde93de807210c29860ae2ccb1e44a2123930f5f7ea46367a486af04d291a3e423b63481d7a87ff0393864f32c82215a0eff4054491e1171d2133b22904b0b3cd996013f0055f125491258fc96462c9a7fcf4ba01b5e8b4c5d46535807c3812ce9e9d4767b9e198f53f6efd3531cdaa6d85343b1dbead95dbadc18e517e626870bcac0f2a6d4ceec704417842ce79e4b841bb1742a10ba52aec00a80c741e3757502e182d0f7c31b8164b6b804e96a06dd5cf52a2ee3aae7563ca8eeea6c7e82f82156c29e43d692f90d159b7374f9550a48439f80565af1dbe565718fea436d4a2801112cd6ab1b27092a5b1885c959f5545abea5f07b699c2df23714e038a4acd721a2ffbb9ce7d5bfed0442ab39f6daac7c9040cace6e888e1753885591a4c48986a96682cb3917eaa75c8276cfc9d08815de897842945491cad19fd9e77157d2ec6d254f80da46c24f55e5959ca4f7d84d5c99049882115b479aa42276d13e256d03f1ec3615c87190336646c8cde1dfd7753a864e98ca3d104d8abe3a1c2735b2f35a18fd236aa5a3d5e518017ae6d94e3750b024e9f3ffffffffffffffffb73f03f57fcbaa6f2d4a59fb65da8944d5ef7a807af9a5945292292535adc8eed48146180210c7d90f0fcd04b804bb043930d8886a0511d51e5604c18129a88ee8ce2e29778a6e61cc0e3fa9593aa505912d4c26e8a8242f7ce7b4502d8ca32fcce65f88160613775626e6e9704966611a5992248bec9a51b92c4c52de0b4ab224562733164651f9203b5ea1920a2ccca9f925ff69e715263f694ef45eb35caae30a73b410bf22fc6e85717ff75450974fa974b3c294727e3f69292f5db657610cdd15a1c397aca6b62a0c9f3c4fa9b6b0cc512a0ca3b5bb247d720e9d46854157504ac8c896942a9fc2a0964d343997f2944d3685e14f3b09af914b618a562373e123c3362685419d65cd77d372e11f85a94fcb522ec93a25b4a230883613dc7cb4a130459f5b8bdba2fdbf0585c1aebbdeae4f78f9fa0993b8f112bd4b3c61b4973f5953564f693b61529742af8e494bc29c308d2cd5b7e6269c979b309ca4e414e646fda8144d186b9409da62c7bdd93261743ba5c4f48612e5524c18d743a7a0b7d2bde41206bd7d92fa5b3313479630c9c9d36739a92cfa5a09930795d6b994a026949430492a98a4353fa5982d2761cee1525a923f5b9f2449184c3ca9663929132a14099329492c415d7ee6af903028252c589e571e61bab85b26dd2d8e30ff49b29518a37fd408f305b153d9a45c7135238c5a1743fdf6d8d58b30a97cdecf29ddc3a98a30c55f117b233b7b4926c2e4f9979d4cf65c628588307adcedca265db6241dc25c42a992666135e4378449ae0acafc94a082c70b61de92a414d51f26d784307cf6a4b3a7a4648b77109f248ae59c94f43a04611272c2883ea9b33e5e47208ced96fd2cd72a3e781d803098bcdf2ddaf1f9afebf88349ca9e35397fadd25b871f8c9d46277f2fa5fae2d6d107a38a907695264ab4a475f0612d49f2a86aa3edc194dce7f4da45857cf46014f99fa7427930d9bf7fc9322b9e82d58107737a3225f907ad97628da30b1b30ca021d7730d58e651b79d28daa708d3383049fd810017f62a346c10e3b18d4cb829026d71828b83a987265b760e22bcf440a74d0a1b4faacb41d9f765dd17ecccfb5d4da310793fcd78a77d5d6bbfa8b37c1c7c7177f430ea63c3d6d82aebb78d566ad1d7130a95ffa24c90b71428dc8011280e0011e30c103d214d1018752aaa0f69682e5aab16e8d35134b8e8fddd8e90d06e9c94cb419d31adaec7083e1c28485d94ebdd0d106e3e757faeb3a134c996a78a0c6c7078ef71b5ed8c0c10683eea7afdb9e3a99710ed7603c659e35777fa4c85a6a74a8c15cea3b093725cc4aeb1d6930760e1ff9e5b26e227466c4a0030da6fc567dda2a7781a8e30c0671d2f66a5aac8b52fcbf683398925e7ed51eb598178db1d6050dc7e4c60874d05106831a71d55be9c6b4cc186b33f87b1588a1830ca6d015c5ce4c9e54aac73106e3abfa7dca0e93dae4c458cb917e4689d12106936be8242cc878d1ca6c7484c1143e946b5d8b095d79cae80083a99312daaa748fe58fe18d8e2f983aa83bc94e4ba9138cb41c1d5e306da7f4b5143bc274ca468e7619334e20828f8f62f633109251a3a30bc6ff9827ed725ce6ce1b7470c194265eb08f224abcd4b760f49c534990935544c41d5a3007937faae64d7ab2307764c1582664c74959d34fec3bb0603af973acd3a1730573561115de4411679d596173efaa32b3f4626a1645e856ee58a9c4f3245605b35a9838974fa869eb686083031f1f34b071e3860dc43be8a082f1a49ba59c7f8a9243018d16952585314fbd7fb41247a17d7b86cbd576b5c77b66d50553a1eea41721a2308d3a0b1e772c47694f284c232e94459384e7eb2008283e615263e22f25d9669e4697104f98fa724eff29492645afccf81b9d30eba7ff8a9ee54af42cb231830b183564c4c82183069c3068c895346aa93142c93000d9845a39b32d879798dc9593e4a6098367f595cb7f501bba7172c090811a8064c2303aeeb39a9c3e3d39709c19850993cef1aad6a22b077209b349258bc7ebea5cca4518104b184b12de74f2201f3f59953057b9df875c0e29614e523ac164359dbe5e9a8471cdc38d9849a1c47c499892641ba271712470f3e81e2a42050983f6399da6db7e84a9da040ff2a2234c694eca252559f600d20893b5e5b973b5a4b3fe3b4698835aa9d58f16bd362ec2ecf21fc75384c1bde4ee1d5dd2c929ba904498b4e7fbf5511fda468761314c90dc18419af127486a7861630310442c725e2f676f2b2ab3e5df5bcad28dccf77511904398a454fd418c969c32ad3b4398c2851df74e3ba410265952760a5b32e79e12c2e8e676691ea247251f3208c39a24b78d888230aa77b40ad7c1c273c80524101040e0073282fcc17aad39fd491a75e5dd00e207fca4916daf2fa40f06f90e9fda4609840f66ed183a27a5ef41f6602e13c2920a262607a207ce5c35e336cebd66ecc6e2f6bea46f06903c182f77ec4acc0bf9240f0382875665e4364d3353e5d2e99c961c2c873e3d668c85dce102103b986f3e7512aee259142da40e667f936221ceb6e3c5e960981d3b2988fb925abd39984c89ff9cf23b440ea6950bdb2a5a2a9f4a82c4c1a86b96f2587d14a15c3898df5e3e4ffdc59b4e7907903718566f4edeefa9684a47216e307fa8ad272b35f9d678006983394bf612f7923fa5b18f081036581f749e135eca5d83d9c2eac77559bb92e58d43023560ba4bd7a794dd7a1a0ca26f46e4e9a89ed92068c8cdd4d52e5cb7659a9b7b8f2ae1562f8f2067307cb4a484d99f8a0b2066305af2e416fc833cf9446530bcfeefe574e2daaa044206f3e5205b4cdef2929fc76052a37d3d1e34e3348688c1a86fd144fa6e8e12475b0348188c174dd2274c4c99bcbd0b08180caea17e57b25649dda7085a00e304902f98bf4fd07a32aff369442f205e30b9057513457b8a5a62e50b48178c557fbb26e7564f2504e142f24dac928fc5da93186bb702c81698b50ed5f373fbe1a40573ea3fa58352313c9664c1e415734c1ce963c1a8ad5d7a74ea573078aeb617136b2b98c6241d5feca29b7cb80a7ad6b8ac254d8fedb62a69491ee11e5f5d1840a8608aef31599d84a995ca14ccfb27f7eba9acea5a918251bde453a1ee954b08a3603cf1f54e8b1a14cc39ce86e5d7f9fc0483b61a0d4bca8238e190259bf006a40986535fb2bd6f564ff01c01c2049310a74b7f76ef7cae41966092847e3dcb7de97fa64194600a276246f498902498f357f07827c9a6764a4830dc8efb7c1c1dc1a46f525c6f7c50af18c1681e522d8c2839e73015c1ac323a071b5de28d4430c6a8efba8e52fddd0fc1a4334fceddf5d5878560d6b41f5df766bf571204733c11da843a6942f44030dce5ccadccca6ee90f4cabfe73153bf829a1fbc0a0fae4fc7b1c417a6032e5f317e6043de141101e98f483099526277377116407e6135bffdbc4835b164174b085322d2457588a2f240c7a544a3f25a5f826fadd230c32dba6041d3d098f9f3b47183bd48e59ec85b7cbdf35c29c66a14a08b1aa2bea648471d6d3c7b8694982c7298216c028416411f6918a30e60915c6fadff4f2788930e8d2654ab55cbc31f1106110f5112754ecc509fd0e611ccf155bdf4bcf8c8d6708c3de98dcc9e3df8a2ac12b847d1c218ca95eb93e6f26e860d4b05103060e19798330a7e55714154d9b247f2708535ebe6a575341977681305c05f7dd5ddf8ff7f10061f450529592e28df707ada2c9d85a18eb92d7f2d1f220c3cea43cbd88f8c124f65ef9e4545e0921d207939c75a73b07cf41590731c207f3872bb9b2f3af88e8ecc1ec3958aa2486a6589c64b089e8c1f871645ec9e1e46f74711f88e46105113c984b09233c58524a8d6877309d9af1f8bab61d0c16f43e3c7e3ccb35eb60d47093dc47ae5abaa509113a98c2fdd44627c152d49f83c1b35b44e97c729768cbc13ca7bd7979b471307cbffd9d56090783d27fae960455e40dba99c6c9aaccaadd89697625bfb05a8288ac98081137183eccd6e4bade30e9b7c13ed4236c30fb5cb2de9392246889770d26a992f8ff257f35184f4a4196ca2db5a1b3481a70537331ad1173bbcdd4226830a75cd62d9e44cf60de123cc4a2b7d696680693e5186fa6bd8a94c1f0e5a9e488d08efd6c840c863d1d4a9d9cd25fc25286c818cc77d2ec86b84a5391188c716a6a746cf912ec240ca66f533a844a3a184cda757445498b279e141c44be601262d55277f75c561bf182f1938a1e23c7bede928b23d20573565f532709251be18249f0cd107b674ae7186dc164e1d2d55aceab33fb225a30c9916ac95b4fad11c982e943c4e849b288b9e41f0cbb1944b060d0b94a6c4e52929a14bec815cceba574112b186458f83119179b1ec58b54a1a895c62abaa6855669c5c837f5aba7741ca182f14a6b9e78934fafda8c84c8144c616e372ec7ab52521129984e38c9e4b55c6d498723513025db4cd1fc52d2348582293b33a4f607234f30c75fe7ac56da7dd50f224e30d8dde97dd2a5ebecdc4813cceff1536787fa2b3339c204f3979da57d2e3df1cc8c2ca1a08812cc27c529a1a624c9e45125c1543bcae37dd43d331d120ca2845162a5ab8cc8110c777bf9835b122fa5ce8c88114ce2478449561f2d0731094cc0813343a408c68b9e2ce90f675174878508118c15547928c1b38b0cc1b42588144fdb7b4acc63ac71062242309678267e4a90be732119448260d2bb24a876b2731c9731830f87a5200204b355763dab4a4fe407262578ac94fde492ff5368d8b8e101ec42c407873d3d33f28332d20363a509b1bd4a4a4e1131c20393a835153f5fae0d4476601c15948ae835233a30fa9a5842d456bcf8d9480e4c9b66629fa0dd54504f0407a67613d7e4246af49a30e4162679af976e517c456683d8a24fa9e40ba416661deb4fdbb67f75265a98b4099e539263eac50e4166611eebedd1c93b76d09185c184d3e7c1d2f4e5edb1309f1c42092c4cd2df099e2fe7786a9757982de6e514eee49b38b97585e59e844af1f25e32af1587adb7dc578a6bb1d41631bbf7c9533065b1c27c15c6c6528696d3294280acc25469ea24f171559893ecdb7925e8a4c27c79a93ea26254dc953a94f6fd8b0590539853de2e29a72bbfff92290c4a3b49295ee3545fb414e6edec766d12489b80d904eeea1c90f6204e5527020ea8c18c1c31b890516302f5b1fa6110f01c38bcb8518000d4004d400100c881c30b13102000cfc5f1828b1a6706020480e3460252d081057800878c2e8e0202d00900e0c0813e08208093e3241f0508c0c971121b373e0e008001342001375e067e72e080016301000800030e60ac38f3ae0e33c6f299176734fe068c2f64243092b7818c19a72d470d1925f01804cac87123068e19306024c04310080306023c0251832e008132727c2123f9431734dc0f07f0e803cac891d8a0e1050c1809f0e083c98288f07e17f5b1d53d98564e70b7d01e3d1894f7869d7dec2a59a3f134563df26070d191db26d8a8b9ba701a4f43693c0dc45143468e1b3f434617346c787ae021c7e3a0f1340ce07107949163460c1c07068c0478d8c1246fdea64927874cd73dea6014b1944fba5eedac241e7430b8575f5abf246b29f939986329f1e24b28d93eb57230bf9a5d3ce250b2f1b42b79391e70306ae824cfda492de9471e6f309594462b08932495f2a181ebe10653e78fbba35715450563ac212f7058063cda80ce1f3f28398c5b540627078c19e7ac051e6c3088180ffb1baa3af529cd630d2639fba34b928331d6b6021e6a30a5b4bd26a898ee114a7028073cd2601245674d13c4ffa90e3f3e7008c1030dc597b53c6562a7dce30c36f030c395c13ec870ad6338317018ce030c7785ca655dba979d6dede85c5249269e7eabc4587b191c14d8f0f88251dd4efd72b78c32255e308a5b8fb78f55504914048f2e983e69f66abff8bb29f1e0824909a567743479fc6b6381c7168c27d2b2456f71cb51d5c2a2f55931bd6b564ff9e88d7713c4ee910583fcc99eaaf5ddb88e0716ccf771b2f33dbf9a24ee1c785ce1f0496a1d932b556e8542456bd3d2c557c5d57ca69ee5ad932f7854c1709d836ba79e50c19c644b313b94b833e314cc6fd2aeabaff86875a460ae5c9e4b7c1ad1ec2b0a663df973c8311f0a264bbf21272ea5028f2798e4b6ff441de17db99fe539c160ba72580a7fbf2709c9e0d104f37712af24ef4f15ecf66082f1c45db00ab3aa173d5d82c7128c1e6c54c7657dbe42237828c1e44149426e554efc94f548027a2001cf051e4730ddb8c7f44fb2af24b111ec051e45307eb59ddca3abe7e2c687e0e3e3091e44c8537fceccd7368d57e03104cf3aad644ba8fa3709a1ac8e97042541e8eaea2e6d78a5d02d3deba673cea13ce50184f5f881f94c4527a54e849b8b1e3ee8bcded2b344343f8e89631d4f4f25c6dad7b81998ee82867bf4c0245a177e540e83c18307e6d6d172ddf7bb73238f1dd8470d0f1d182d2e659b8c8d12d6f7c8817d78e0c03ef416678bfba8458416c6f22c263dcb9da4adcec2a07562da63c57afa5316f64123120b8585e1444f1b162fa556669157d8872becc3059156184e4c344c0e79841595588a5517ab2dd69cd2b3e079b7fe22b20a93877b497aeb8ca8c2d4df5ff949d653bb4b85f1c47ff5e92b820ab327134b6d07a15318b44e304b734fc414e6d1b72555e543f4f752182c9a3e53fa7d3f6c8c90c270f22de5a024c1844ba25198c24d4c2a21e74fb89f43c6f920220a83054b424789d4930453241486ad115baac2fddda423a0309e1aa5199653d9c51df984e9b3f7e32c684f26ab114f184f734476344148ff3fd20963bc55b9ea251d3bfa08274c2572a71df224d32a463661ce525efaa944e3eb8a68c2a04cd6df7811213f7432616e9354b4362915c18449f420972f27a14b20b48c1425689f6ec83824f8f8d044c4128995306585eb2476f696f88f12264177f6abe724b953bb9b84e104a5255c5787f5b8ee24613853274c3bf64e30ed2e1266ff589df428854aa853b75196c32808821808422086e56b920e8313503070401a8dc5e271891ee9fa1300418ae2e07030128d03e260481c08410cc5300c82200cc5200cc2300c637005d979bc62f63130b36589ba7ce7d9e5d1ffea47b4f9655e35ec945bab29af3e23582b974ffd70e05fedb2f3ab5a225fb162270aa868e2bd37c31c2663ce99616128361c10c6189d2c9370d89adec83512183e68126746eb68fb7ab4fc3dd619fc36c34b9af84f34e5c6cd4490ef0b0d8d962b530608cc16214b72459ae77cb9690d418b3d870919ac409a28199d9720a235191a58dba83f9f2814a7c9d460f19783e83e2b47e7ee75481c3a29ae359d27f4b108ead259242f3342155c8f4482da07897c9acf2540d73f748c109a440bb18b4f717de41812994799adfdc8571e7a9805ccab9d0c9a604511c0fdade1c95c583460d4fdf1b3d1f5cbd96a79686106babbaa2bd0f9cf706cf1db7a5165a52454a1d4447e8b82c6f816636e9328dba0cdee2f3e8722b7e62201b611b10e85a597eaf3a9d1cd36558b5fb84c70f68b38ba942b49f3549c6d58a16987a247bd7894ad1b29c3e12ac290baaf36dafc3fa79992f4421ab7c4cbee943ca64cc3af2e11c46aab63a5ee6988ace3f7eef0faa488c7c54d1debfa0f67776d11c8612f93b296d9cdf1132d9c1a0d733dfdabbd34ec0389553de28ed226bf53312ec50da6c6ae8823fa6ae1077f8bf3004658bae8a612404f33b1ca3f4c6d09d316252c3647101c3ee0f340a30945dd1a1fc8210ce8f7bbbebd23e01b60dd28f9e0eaccb0f072fb86561074ae4d73012a1cf81309b85d963591bbf0a274f40032fc270debd1d4eacfcd79528074251a0cd7173a53353b2bcd9e2a2128ffc4881001ed63ca6efb01b90ea5553e0031de199944c3492af3719a43026b5e368fa41d96daa1b22b493f722fb5247f09c930862f32d68e16c56136352b88da9196e46642207117e23ae64b48cbe1155c8b8ade35785ffae8b5707524cc19ec4c915f4ca13499fd526c93d2d342f832563717446e8979742c6b9911d6367a8eedd650d746167f409f2ec120945b1dad143da00af9b0437c00cba6140347c088cac92549894713fbd4ec03447e7dade80ab7a366242a71f4c27b826c3bf6412cd4d62f44fe1a9f142cb3df33358ad016ff86f3730885f12c108f6f0ffb769a2ea424e68ea41ecc8093fa76d880e81f0c240f551762285318bfd3b7836ecafdea967cbfe0bfd07a83293610602a056e0325df26732b78e44071415cb607a1109d3f336317c911e77cb475cc5712c75277430b6261843acb318c4150a8bb49c3e4b8018b791175ccc7d3d03595aa33028bd7af6af67e3b1d2713d1b3a56217e0e287ccec3f24031591178dc550848a1163e17781dc7186303b7654f184e4aa8de1aeec7f4b2f84c7b006149ca47ced69b453aee3c35d91d493f792e84277f872811c000e61df99834b9e0f6759468b2417e4ac1d52cccb9f596ea12b7cb9c0b7bb872a023a21434e2896f80a3b33f4fe1e8b40f03b0e7c20fb8cf64e32b32fab86b4cfd18c7426e65f3c139ea0a15191ab53df5ff07912530e052635f6b0d0308a1bd2f824c775096dd5905f506ca115a155807829cd8b7f5f66fc44c44b221308b1c8613534615acafad3d6631dc34cc9fc4a2086cfa2ba232e40732a4ec93fce6d1e0254078f22f2fa74a62cfaa69b13aefdc2fd0a4e9e2d2077d03ed255644f2ec34fbba5f0efc96502d2b6bf464833479a9fdefbb8c4ece33e20352e8710b667ef796e1fff5b8177278eb97c487c26ec80084a0381f3ba2c4a505ca52c56622b4230c2f54381107046f82993d725428c70d5799c44f5167b3b319a5a5f5e45be28fd574540e46878e433a6ca1021481e7e03275bacf10d0ae107a8058c408aa2f4ab58caafb1961880f794a7feb5bcb59931fdd16c4b6b612093b3993f0b41c1d4a2b175d9cb4aa568534065fe604fa673b61bea5622ab76ce0334e77c870a026a7d74eff068fff6b7d3ca28c462d69c665114f1c78abf040df6e4cef31bcee4e4bb9c62961e4b2a70b592acb5d152a26c9bab4e1168cb27fde9c174e9853fbc11a92bfdcfedd69b004b6fab98f70b397438080bcc61db579a24ff529af59c64735ec483cdf9cb304a5562910303b3226a71df9ff171369c1c6b0e110a7c9fe52a7c4909340aa065e1b96e21fa661c51c6e4464044a1b202756f2adc2bb366612925ca81d857a2e7a72ef57f72a7e5144ef4019d17eee8d98c0b5df9e75cef1db150720f755ecf95ea614b4bd2fa6262406ec2283314730081c43bd3a982b4c5fe03b5a205d41e8e0520127cb650df57e75725a9f12af4645f1f3f2def1571153997f8858cc67890a5fa3d0be27f15444984a1a6090e5c81f89083ccefce6356253eb3c675157faa8ccc1696cf0b2d52816d3568f0dfd8f5edc39f4afe85259e10129d3c86edacab29cf52a5c40f9c417a80c7c45782737bcc8b2e4524deb942c328035b52a22840cc104fa33fc2a0e9d4f1104df37d483a30a7dae084d405f424f7ac98b2dfbe00fba44bd0496e05f88ffc4519b28a3b7c7531c928d7a104357098b80f249f454b6426c2b291084a7fe1d2898f7edfc988684b098b8cdf473467a8adcf4ca7f279946c302238ddb39585d64aee16ccf01d34d52cfa30b3ea19c82295dd17b790d64ecd54b88300e7380b756d1c1dddec34175f91cce3c861a6a804941f2b4c8e1e123211dec5c105ad05cae072402ce47debb0969b7338a9593995990bdf2c56438241cb4d5719074096461cf2c02b6e9eb71502ea9378becd5f11b8fa4bf0b31306784665f13f69391be27e12062f1a0ab4e621791ebc9bb5f95763d1dfb940053910a9c51e792dc1bc71190ca7286ab62045d90449a4601286ff5391c076e8290bd93631e52618b641e0599ba6e9507cc5777b72fbe0c4889a1495758a33d0ce825294a36aca2c4493b2d1e510132480f4b2c21221e1a545f87e3f2aad3608d59f554c9713bf74e9cd2857bf4f0ef5121ffe7d8e34f5c0765139745447d23822236329d9dcbc3e313d522e612946a4ab0d65b5c81a3ea1188d6b61dde1da56d69203eb7d95dca060d02dd6ab89e336a984bef70176c5302f4b007f50982a7a37ff800d753b25674c14fe514f116847504991189561bc39425d2de0a353a43381211bcd401182508d64d5e99aa8cdb1960788ce4e73ea04e0d34cae4f904278436926015f1efda4fb6a4a6b7f24c6ccddecec9cb53eddf8adc55fb51f38a84fedfbfd402d241ed383e097338465cc006f9b8c2c54b83b5fb11e2ebc0fdbb6bca99dc3514a489bfea47b95a96dccae4532096855db5cfa9ba136cae012a03c74205446b79eb7bad806452a9a317fdd9e1c49ae80f7412241c6d2dfe5b05983808e511455ec09de62dbd40a957cf8062935b30013698af69884e1d6aea6b6b40325bdaaba115bd538301c7c67f19f2c79b05e1f265399aaf61e3dc2dcc106df34032dd935c2d3a44603779f8973bf1b08a0e1172420c532ec1e8e2a4997ec2ef58559c8391d837eedc5e1833e96be19c061cfa7975f27976df4dc4adc315e2c8455910e8752a6b0df326b0e51d4be038d0f808f53ba94fd3b5d7fb31d7fe5c1fa4cd08e68451d436497b12a34e2f08bb7191c7607a64871e03b70c11f4f95d6b2ae163d36d2edf20bfa14f19a3dc97e7d7f421a41b7083da1ed9ed973019202d6a3df08062e41100202578d21dbd233641079513bb2c1114ddb88eecccf278965965f11bf4292ca1bd89b68cb1096b11ea23d828c5226c54b2ac7b1dcad418c6e8e5dc0f8a66caaaa3020617f6cb970be5ad79723acb3e94c29a9e083745843c0c433374a610837bfb287c7e81f71092f85de62da004e7df15187f3dfe9170ba10f1d6368936e025758672b32f6e6f63aceb840bea38c14224b12b98ade25d6ce5a2a7dc4d7a9cdf15059682ea533112449b1ddf639b7c7ad38798e3029d1f85e8246b82f117fe920160938909ab37221eb4f221cc5d61dc85065dacb58970948602ee62d8d67b1568e90a882d8f302bb492d9e8d6f356255e938b9ee3b3b2690cb4a2fc684a85908520b6380459343d4bb13bd515d13713dc6d8620e230369694e03b945a01258811292adc93093fd5c9ce59e7bc2340d1850f46998ed94fa687d75dfb717e4b615ec8768e1ac8567dd566d87a3a60f401cc5dbd906ea176ce7663c050d0504d87ec4351f051414d51485b7a784dd251fc54ec61065b76a129ddbd725a465a4670b40b4bdae02b6207a49950094812e368cec46b1007e34ab8d25a957cda15e9a01c9740174c2252ab0c9fee3cc8ef5a63ab75e66f4c92582d6e045d3cd05b40084cab04435ba291a1798b49d53ca7bcff080ef3b72e0d0482795af2d5fcd75190d9f2a95f39e05592188011008bd8627b6fa2437d943024da2629f674a1453dbe14020fbf2c7776136dd083178a070459c198d58b3294c3312138d375657fc22e9e1f0ce3840f64d402c0e694381eb95901d33cb95a2670323761d7d94472360752625fb59167ef1236b07115464bc86cdc625e165b1c42665386302019c3d333d8ee701b3fbb382902ef6194c3629919599efa64016aeb24ff80be1cba9450e7cbd5ed909a2d50e83db096a94ce681a3954073e5a0649b7ad3e04eed4be2995516f199008ebe36ba6e45444061fb10084521211aeee0de2647a839c64739802987538a9e7c8c454a85d3b67c0a648f36700a81b5de56254b35e44bf95c19ca62017b2355c029e1d4eaa6524139e32ac8f6d02987d3331ce73d4401bc672686e31ad570f36c5c8bf611042c503690e31d14c4bc1193115f6d003daa0bc29ba08099bf21548124adc09d5ab4b7d8c7a0010155eb6ca7f75ef2950154da89680404e50084d806c45d63891aa6ce3412ff96eeb9336ca17d0d6f350f925f91bb262730b3255a91f3ab6235b124a42ab81e3ecd1e2efefd3163cf2798874b3dbe0fc7f898bd9c9a3ab9494bf8214c0cc6f90a339a43fa8c8cbf8310b0ccabc66323a5e8d9b3838663282b27df5ac041f764323d4b5fa8a5cf9e6027fe1f85d11b20c4a8bfda294453132a311026c49861a20c9d31abeb2b2a8538580f522820e86195d7aa7acc39213c42a1d64acbab3146e22b9f4f476898b18647dd9529243c95edbb07e1a695f73a9df83ef16772109a26a38fadd619d4a6a0c0f0932500e9d868a481c061f0765dc3919ab40b6d274b8ed680d93261225397dbfccfa33ab1aa60bc1fec8acb6b0d54af50632da13523e378cdc0838d282e6a2fbb091ccf009a8ff16136de1945afcba4b97455f8c3b28b1eac3ec6558d0ba13fa978598b01241ab0df3a92741c56e16b495080fba5ab33b2eea3d18fcab3cf8eaf7284a6fa7e005afceb79bd5f6edca8c1600c3687d6e2f9b801007b6be708055e1a941c3edc5c97755b8bf97e46489da9033c9217e20395d3c788ff57308cea45a11ed8ce4222a7641c1f5da012d892fb6009020606bc3a4b89552b6bd6263a724e398b49e31389445f4aeb814", "0x3a65787472696e7369635f696e646578": "0x00000000", "0x3c311d57d4daf52904616cf69648081e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x3c311d57d4daf52904616cf69648081e5e0621c4869aa60c02be9adcc98a0d1d": "0x10b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x45323df7cc47150b3930e2666b0aa3134e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x45323df7cc47150b3930e2666b0aa3134e7b9012096b41c4eb3aaf947f6ea429": "0x0200", "0x57f8dc2f5ab09467896f47300f0424384e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x57f8dc2f5ab09467896f47300f0424385e0621c4869aa60c02be9adcc98a0d1d": "0x10b8d9d88d2b1318a098588380c0bfaf43fa93881fd212f9c70069665c3f6b7575b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", "0x5c0d1176a568c1f92944340dbfed9e9c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x5c0d1176a568c1f92944340dbfed9e9c530ebca703c85910e7164cb7d1c9e47b": "0x52bc71c1eca5353749542dfdf0af97bf764f9c2f44e860cd485f1cd86400f649", "0x79e2fe5d327165001f8232643023ed8b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x7b3237373ffdfeb1cab4222e3b520d6b4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x7b3237373ffdfeb1cab4222e3b520d6b4e7b9012096b41c4eb3aaf947f6ea429": "0x0200", "0x88fbb13c02428a6ba0e3c362f503d78c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0x9ba1b78972885c5d3fc221d6771e8ba20f4cf0917788d791142ff6c1f216e7b3": "0x01", "0x9ba1b78972885c5d3fc221d6771e8ba24e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0xc2261276cc9d1f8598ea4b6a74b15c2f308ce9615de0775a82f8a94dc3d285a1": "0x01", "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00000000000000100000000000000000", - "0xcd5c1f6df63bc97f4a8ce37f14a50ca74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcd5c1f6df63bc97f4a8ce37f14a50ca74e7b9012096b41c4eb3aaf947f6ea429": "0x0100", "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3401bcd1e9f3885b9b0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34": "0xb0b0bb7e1c48209d1c515c54dc6b106e9dc8764697c67063a3df2f1cb9abbd34", "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb381d03c816fe51e89926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227": "0x926bc7fcc360e0e37ed3d4527e4c55d448318bd2fcc17bac149cdcc34b37c227", "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3ae9e7a6969af6726a8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20": "0xa8d37e9f0fb7f832fe8ce4b130004e19dc201f6741d816b9dc4108b0805c2d20", @@ -82,4 +86,4 @@ "childrenDefault": {} } } -} +} \ No newline at end of file From d2b7ee2575c5132ef050e2523955382e1fae00ec Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 1 Dec 2022 22:32:52 +0100 Subject: [PATCH 122/263] Squashed 'bridges/' content from commit 062554430 git-subtree-dir: bridges git-subtree-split: 0625544309ff299307f7e110f252f04eac383102 --- .config/lingua.dic | 242 + .config/spellcheck.toml | 13 + .dockerignore | 1 + .editorconfig | 19 + .github/dependabot.yml | 42 + .gitignore | 26 + .gitlab-ci.yml | 303 + .maintain/millau-weight-template.hbs | 106 + CODEOWNERS | 21 + CODE_OF_CONDUCT.md | 80 + Cargo.lock | 13269 ++++++++++++++++ Cargo.toml | 263 + Dockerfile | 72 + LICENSE | 675 + README.md | 252 + bin/.keep | 0 bin/millau/node/Cargo.toml | 59 + bin/millau/node/build.rs | 23 + bin/millau/node/src/chain_spec.rs | 235 + bin/millau/node/src/cli.rs | 72 + bin/millau/node/src/command.rs | 159 + bin/millau/node/src/lib.rs | 32 + bin/millau/node/src/main.rs | 30 + bin/millau/node/src/service.rs | 461 + bin/millau/runtime/Cargo.toml | 145 + bin/millau/runtime/build.rs | 25 + bin/millau/runtime/src/lib.rs | 1119 ++ bin/millau/runtime/src/rialto_messages.rs | 244 + .../runtime/src/rialto_parachain_messages.rs | 167 + bin/millau/runtime/src/xcm_config.rs | 327 + bin/rialto-parachain/node/Cargo.toml | 84 + bin/rialto-parachain/node/build.rs | 22 + bin/rialto-parachain/node/src/chain_spec.rs | 198 + bin/rialto-parachain/node/src/cli.rs | 140 + bin/rialto-parachain/node/src/command.rs | 446 + bin/rialto-parachain/node/src/lib.rs | 18 + bin/rialto-parachain/node/src/main.rs | 29 + bin/rialto-parachain/node/src/service.rs | 540 + bin/rialto-parachain/runtime/Cargo.toml | 136 + bin/rialto-parachain/runtime/build.rs | 25 + bin/rialto-parachain/runtime/src/lib.rs | 905 ++ .../runtime/src/millau_messages.rs | 163 + bin/rialto/node/Cargo.toml | 49 + bin/rialto/node/build.rs | 23 + bin/rialto/node/src/chain_spec.rs | 286 + bin/rialto/node/src/cli.rs | 87 + bin/rialto/node/src/command.rs | 222 + bin/rialto/node/src/main.rs | 28 + bin/rialto/runtime/Cargo.toml | 145 + bin/rialto/runtime/build.rs | 25 + bin/rialto/runtime/src/lib.rs | 1006 ++ bin/rialto/runtime/src/millau_messages.rs | 242 + bin/rialto/runtime/src/parachains.rs | 210 + bin/rialto/runtime/src/xcm_config.rs | 286 + bin/runtime-common/Cargo.toml | 84 + bin/runtime-common/src/integrity.rs | 291 + bin/runtime-common/src/lib.rs | 220 + bin/runtime-common/src/messages.rs | 1204 ++ bin/runtime-common/src/messages_api.rs | 66 + .../src/messages_benchmarking.rs | 157 + bin/runtime-common/src/messages_extension.rs | 231 + bin/runtime-common/src/messages_generation.rs | 146 + .../src/parachains_benchmarking.rs | 85 + ci.Dockerfile | 53 + deny.toml | 206 + deployments/README.md | 261 + .../bridges/common/generate_messages.sh | 64 + ...y-millau-to-rialto-messages-dashboard.json | 1153 ++ ...y-rialto-to-millau-messages-dashboard.json | 1145 ++ .../rialto-millau-maintenance-dashboard.json | 791 + .../dashboard/prometheus/targets.yml | 4 + .../bridges/rialto-millau/docker-compose.yml | 110 + ...ay-messages-millau-to-rialto-entrypoint.sh | 16 + ...ay-messages-rialto-to-millau-entrypoint.sh | 16 + ...messages-to-millau-generator-entrypoint.sh | 26 + ...messages-to-rialto-generator-entrypoint.sh | 26 + ...ssages-to-rialto-resubmitter-entrypoint.sh | 25 + .../relay-millau-rialto-entrypoint.sh | 36 + ...o-rialto-parachain-messages-dashboard.json | 910 ++ ...arachain-to-millau-messages-dashboard.json | 908 ++ ...arachain-millau-maintenance-dashboard.json | 713 + .../dashboard/prometheus/targets.yml | 3 + .../docker-compose.yml | 90 + ...messages-to-millau-generator-entrypoint.sh | 27 + ...o-rialto-parachain-generator-entrypoint.sh | 26 + ...rialto-parachain-resubmitter-entrypoint.sh | 25 + ...elay-millau-rialto-parachain-entrypoint.sh | 42 + ...y-westend-to-millau-headers-dashboard.json | 781 + ...estend-to-millau-parachains-dashboard.json | 200 + .../dashboard/prometheus/targets.yml | 3 + .../bridges/westend-millau/docker-compose.yml | 72 + ...ay-headers-westend-to-millau-entrypoint.sh | 26 + ...parachains-westend-to-millau-entrypoint.sh | 16 + .../local-scripts/bridge-entrypoint.sh | 7 + .../relay-messages-millau-to-rialto.sh | 20 + .../relay-messages-rialto-to-millau.sh | 20 + .../local-scripts/relay-millau-to-rialto.sh | 29 + .../local-scripts/relay-rialto-to-millau.sh | 27 + deployments/local-scripts/run-millau-node.sh | 11 + deployments/local-scripts/run-rialto-node.sh | 11 + deployments/local-scripts/run-westend-node.sh | 14 + .../monitoring/GrafanaMatrix.Dockerfile | 15 + deployments/monitoring/disabled.yml | 15 + deployments/monitoring/docker-compose.yml | 34 + .../monitoring/grafana-matrix/config.yml | 47 + .../monitoring/grafana/dashboards/nodes.json | 200 + .../dashboards/grafana-dashboard.yaml | 6 + .../datasources/grafana-datasource.yaml | 16 + .../notifiers/grafana-notifier.yaml | 15 + .../monitoring/prometheus/prometheus.yml | 7 + .../dashboard/grafana/beefy-dashboard.json | 585 + .../dashboard/prometheus/millau-targets.yml | 2 + .../dashboard/prometheus/rialto-targets.yml | 2 + .../rialto-chainspec-exporter-entrypoint.sh | 14 + .../rialto-parachain-registrar-entrypoint.sh | 11 + deployments/networks/millau.yml | 101 + deployments/networks/rialto-parachain.yml | 90 + deployments/networks/rialto.yml | 118 + deployments/reverse-proxy/README.md | 15 + deployments/reverse-proxy/docker-compose.yml | 45 + deployments/run.sh | 210 + deployments/types-millau.json | 192 + deployments/types-rialto.json | 192 + deployments/types/build.sh | 20 + deployments/types/common.json | 124 + deployments/types/millau.json | 17 + deployments/types/rialto-millau.json | 56 + deployments/types/rialto.json | 17 + deployments/ui/README.md | 23 + deployments/ui/docker-compose.yml | 13 + docs/high-level-overview.md | 165 + docs/high-level.html | 55 + docs/plan.md | 22 + docs/scenario1.html | 47 + docs/send-message.md | 131 + docs/testing-scenarios.md | 221 + fuzz/storage-proof/Cargo.lock | 2663 ++++ fuzz/storage-proof/Cargo.toml | 25 + fuzz/storage-proof/README.md | 34 + fuzz/storage-proof/src/main.rs | 77 + modules/beefy/Cargo.toml | 54 + modules/beefy/src/lib.rs | 644 + modules/beefy/src/mock.rs | 226 + modules/beefy/src/mock_chain.rs | 299 + modules/beefy/src/utils.rs | 363 + modules/grandpa/Cargo.toml | 59 + modules/grandpa/src/benchmarking.rs | 132 + modules/grandpa/src/extension.rs | 116 + modules/grandpa/src/lib.rs | 1258 ++ modules/grandpa/src/mock.rs | 131 + modules/grandpa/src/storage_types.rs | 66 + modules/grandpa/src/weights.rs | 79 + modules/messages/Cargo.toml | 52 + modules/messages/README.md | 424 + modules/messages/src/benchmarking.rs | 423 + modules/messages/src/inbound_lane.rs | 545 + modules/messages/src/lib.rs | 1934 +++ modules/messages/src/mock.rs | 423 + modules/messages/src/outbound_lane.rs | 436 + modules/messages/src/weights.rs | 163 + modules/messages/src/weights_ext.rs | 289 + modules/parachains/Cargo.toml | 55 + modules/parachains/src/benchmarking.rs | 106 + modules/parachains/src/extension.rs | 166 + modules/parachains/src/lib.rs | 1403 ++ modules/parachains/src/mock.rs | 194 + modules/parachains/src/weights.rs | 101 + modules/parachains/src/weights_ext.rs | 107 + modules/relayers/Cargo.toml | 51 + modules/relayers/src/benchmarking.rs | 41 + modules/relayers/src/lib.rs | 247 + modules/relayers/src/mock.rs | 161 + modules/relayers/src/payment_adapter.rs | 171 + modules/relayers/src/weights.rs | 75 + modules/shift-session-manager/Cargo.toml | 35 + modules/shift-session-manager/src/lib.rs | 268 + primitives/beefy/Cargo.toml | 48 + primitives/beefy/src/lib.rs | 152 + primitives/chain-bridge-hub-rococo/Cargo.toml | 37 + primitives/chain-bridge-hub-rococo/src/lib.rs | 107 + primitives/chain-bridge-hub-wococo/Cargo.toml | 32 + primitives/chain-bridge-hub-wococo/src/lib.rs | 78 + primitives/chain-kusama/Cargo.toml | 26 + primitives/chain-kusama/src/lib.rs | 30 + primitives/chain-millau/Cargo.toml | 56 + primitives/chain-millau/src/lib.rs | 220 + primitives/chain-millau/src/millau_hash.rs | 58 + primitives/chain-polkadot/Cargo.toml | 25 + primitives/chain-polkadot/src/lib.rs | 30 + primitives/chain-rialto-parachain/Cargo.toml | 36 + primitives/chain-rialto-parachain/src/lib.rs | 146 + primitives/chain-rialto/Cargo.toml | 36 + primitives/chain-rialto/src/lib.rs | 180 + primitives/chain-rococo/Cargo.toml | 26 + primitives/chain-rococo/src/lib.rs | 53 + primitives/chain-westend/Cargo.toml | 26 + primitives/chain-westend/src/lib.rs | 55 + primitives/chain-wococo/Cargo.toml | 26 + primitives/chain-wococo/src/lib.rs | 34 + primitives/header-chain/Cargo.toml | 47 + primitives/header-chain/src/justification.rs | 227 + primitives/header-chain/src/lib.rs | 147 + primitives/header-chain/src/storage_keys.rs | 78 + .../tests/implementation_match.rs | 328 + .../header-chain/tests/justification.rs | 192 + primitives/messages/Cargo.toml | 39 + primitives/messages/src/lib.rs | 418 + primitives/messages/src/source_chain.rs | 217 + primitives/messages/src/storage_keys.rs | 128 + primitives/messages/src/target_chain.rs | 171 + primitives/parachains/Cargo.toml | 32 + primitives/parachains/src/lib.rs | 90 + primitives/polkadot-core/Cargo.toml | 45 + primitives/polkadot-core/src/lib.rs | 387 + primitives/polkadot-core/src/parachains.rs | 100 + primitives/relayers/Cargo.toml | 32 + primitives/relayers/src/lib.rs | 50 + primitives/runtime/Cargo.toml | 48 + primitives/runtime/src/chain.rs | 347 + primitives/runtime/src/lib.rs | 508 + primitives/runtime/src/messages.rs | 39 + primitives/runtime/src/storage_proof.rs | 174 + primitives/runtime/src/storage_types.rs | 90 + primitives/test-utils/Cargo.toml | 32 + primitives/test-utils/src/keyring.rs | 94 + primitives/test-utils/src/lib.rs | 345 + relays/bin-substrate/Cargo.toml | 75 + ...ub_rococo_messages_to_bridge_hub_wococo.rs | 64 + ...ub_wococo_messages_to_bridge_hub_rococo.rs | 64 + relays/bin-substrate/src/chains/millau.rs | 63 + .../src/chains/millau_headers_to_rialto.rs | 57 + .../millau_headers_to_rialto_parachain.rs | 76 + .../src/chains/millau_messages_to_rialto.rs | 44 + .../millau_messages_to_rialto_parachain.rs | 44 + relays/bin-substrate/src/chains/mod.rs | 125 + relays/bin-substrate/src/chains/rialto.rs | 55 + .../src/chains/rialto_headers_to_millau.rs | 57 + .../src/chains/rialto_messages_to_millau.rs | 44 + .../src/chains/rialto_parachain.rs | 57 + .../rialto_parachain_messages_to_millau.rs | 44 + .../src/chains/rialto_parachains_to_millau.rs | 74 + relays/bin-substrate/src/chains/rococo.rs | 42 + .../rococo_headers_to_bridge_hub_wococo.rs | 53 + .../rococo_parachains_to_bridge_hub_wococo.rs | 85 + relays/bin-substrate/src/chains/westend.rs | 47 + .../src/chains/westend_headers_to_millau.rs | 51 + .../chains/westend_parachains_to_millau.rs | 66 + relays/bin-substrate/src/chains/wococo.rs | 42 + .../wococo_headers_to_bridge_hub_rococo.rs | 53 + .../wococo_parachains_to_bridge_hub_rococo.rs | 85 + relays/bin-substrate/src/cli/bridge.rs | 105 + relays/bin-substrate/src/cli/chain_schema.rs | 409 + .../bin-substrate/src/cli/encode_message.rs | 145 + relays/bin-substrate/src/cli/init_bridge.rs | 219 + relays/bin-substrate/src/cli/mod.rs | 300 + .../src/cli/register_parachain.rs | 359 + relays/bin-substrate/src/cli/relay_headers.rs | 128 + .../src/cli/relay_headers_and_messages/mod.rs | 732 + .../parachain_to_parachain.rs | 281 + .../relay_to_parachain.rs | 254 + .../relay_to_relay.rs | 194 + .../bin-substrate/src/cli/relay_messages.rs | 117 + .../bin-substrate/src/cli/relay_parachains.rs | 139 + .../src/cli/resubmit_transactions.rs | 561 + relays/bin-substrate/src/cli/send_message.rs | 195 + relays/bin-substrate/src/main.rs | 31 + relays/client-bridge-hub-rococo/Cargo.toml | 29 + relays/client-bridge-hub-rococo/src/lib.rs | 171 + .../src/runtime_wrapper.rs | 219 + relays/client-bridge-hub-wococo/Cargo.toml | 22 + relays/client-bridge-hub-wococo/src/lib.rs | 171 + .../src/runtime_wrapper.rs | 28 + relays/client-kusama/Cargo.toml | 19 + relays/client-kusama/src/lib.rs | 74 + relays/client-millau/Cargo.toml | 25 + relays/client-millau/src/lib.rs | 202 + relays/client-polkadot/Cargo.toml | 19 + relays/client-polkadot/src/lib.rs | 75 + relays/client-rialto-parachain/Cargo.toml | 25 + relays/client-rialto-parachain/src/lib.rs | 165 + relays/client-rialto/Cargo.toml | 25 + relays/client-rialto/src/lib.rs | 205 + relays/client-rococo/Cargo.toml | 19 + relays/client-rococo/src/lib.rs | 78 + relays/client-substrate/Cargo.toml | 49 + relays/client-substrate/src/chain.rs | 235 + relays/client-substrate/src/client.rs | 742 + relays/client-substrate/src/error.rs | 86 + relays/client-substrate/src/guard.rs | 373 + relays/client-substrate/src/lib.rs | 91 + .../src/metrics/float_storage_value.rs | 133 + relays/client-substrate/src/metrics/mod.rs | 21 + relays/client-substrate/src/rpc.rs | 170 + relays/client-substrate/src/sync_header.rs | 61 + relays/client-substrate/src/test_chain.rs | 68 + .../src/transaction_tracker.rs | 447 + relays/client-westend/Cargo.toml | 19 + relays/client-westend/src/lib.rs | 119 + relays/client-wococo/Cargo.toml | 18 + relays/client-wococo/src/lib.rs | 78 + relays/finality/Cargo.toml | 20 + relays/finality/src/finality_loop.rs | 761 + relays/finality/src/finality_loop_tests.rs | 598 + relays/finality/src/lib.rs | 61 + relays/finality/src/sync_loop_metrics.rs | 86 + relays/lib-substrate-relay/Cargo.toml | 57 + relays/lib-substrate-relay/src/error.rs | 63 + .../src/finality/engine.rs | 259 + .../src/finality/guards.rs | 48 + .../src/finality/initialize.rs | 149 + .../lib-substrate-relay/src/finality/mod.rs | 204 + .../src/finality/source.rs | 175 + .../src/finality/target.rs | 135 + relays/lib-substrate-relay/src/lib.rs | 98 + .../lib-substrate-relay/src/messages_lane.rs | 449 + .../src/messages_metrics.rs | 129 + .../src/messages_source.rs | 736 + .../src/messages_target.rs | 298 + .../src/on_demand/headers.rs | 457 + .../lib-substrate-relay/src/on_demand/mod.rs | 35 + .../src/on_demand/parachains.rs | 704 + .../lib-substrate-relay/src/parachains/mod.rs | 108 + .../src/parachains/source.rs | 188 + .../src/parachains/target.rs | 201 + relays/messages/Cargo.toml | 24 + relays/messages/src/lib.rs | 37 + relays/messages/src/message_lane.rs | 71 + relays/messages/src/message_lane_loop.rs | 1136 ++ relays/messages/src/message_race_delivery.rs | 984 ++ relays/messages/src/message_race_limits.rs | 200 + relays/messages/src/message_race_loop.rs | 663 + relays/messages/src/message_race_receiving.rs | 228 + relays/messages/src/message_race_strategy.rs | 517 + relays/messages/src/metrics.rs | 139 + relays/parachains/Cargo.toml | 24 + relays/parachains/src/lib.rs | 30 + relays/parachains/src/parachains_loop.rs | 1255 ++ .../parachains/src/parachains_loop_metrics.rs | 98 + relays/utils/Cargo.toml | 32 + relays/utils/src/error.rs | 46 + relays/utils/src/initialize.rs | 136 + relays/utils/src/lib.rs | 312 + relays/utils/src/metrics.rs | 165 + relays/utils/src/metrics/float_json_value.rs | 147 + relays/utils/src/metrics/global.rs | 118 + relays/utils/src/relay_loop.rs | 269 + rustfmt.toml | 24 + scripts/add_license.sh | 22 + scripts/build-containers.sh | 7 + scripts/ci-cache.sh | 19 + scripts/dump-logs.sh | 46 + scripts/license_header | 16 + scripts/send-message-from-millau-rialto.sh | 33 + scripts/send-message-from-rialto-millau.sh | 33 + scripts/update-weights-setup.sh | 33 + scripts/update-weights.sh | 55 + scripts/update_substrate.sh | 10 + 357 files changed, 79920 insertions(+) create mode 100644 .config/lingua.dic create mode 100644 .config/spellcheck.toml create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .github/dependabot.yml create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 .maintain/millau-weight-template.hbs create mode 100644 CODEOWNERS create mode 100644 CODE_OF_CONDUCT.md create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 bin/.keep create mode 100644 bin/millau/node/Cargo.toml create mode 100644 bin/millau/node/build.rs create mode 100644 bin/millau/node/src/chain_spec.rs create mode 100644 bin/millau/node/src/cli.rs create mode 100644 bin/millau/node/src/command.rs create mode 100644 bin/millau/node/src/lib.rs create mode 100644 bin/millau/node/src/main.rs create mode 100644 bin/millau/node/src/service.rs create mode 100644 bin/millau/runtime/Cargo.toml create mode 100644 bin/millau/runtime/build.rs create mode 100644 bin/millau/runtime/src/lib.rs create mode 100644 bin/millau/runtime/src/rialto_messages.rs create mode 100644 bin/millau/runtime/src/rialto_parachain_messages.rs create mode 100644 bin/millau/runtime/src/xcm_config.rs create mode 100644 bin/rialto-parachain/node/Cargo.toml create mode 100644 bin/rialto-parachain/node/build.rs create mode 100644 bin/rialto-parachain/node/src/chain_spec.rs create mode 100644 bin/rialto-parachain/node/src/cli.rs create mode 100644 bin/rialto-parachain/node/src/command.rs create mode 100644 bin/rialto-parachain/node/src/lib.rs create mode 100644 bin/rialto-parachain/node/src/main.rs create mode 100644 bin/rialto-parachain/node/src/service.rs create mode 100644 bin/rialto-parachain/runtime/Cargo.toml create mode 100644 bin/rialto-parachain/runtime/build.rs create mode 100644 bin/rialto-parachain/runtime/src/lib.rs create mode 100644 bin/rialto-parachain/runtime/src/millau_messages.rs create mode 100644 bin/rialto/node/Cargo.toml create mode 100644 bin/rialto/node/build.rs create mode 100644 bin/rialto/node/src/chain_spec.rs create mode 100644 bin/rialto/node/src/cli.rs create mode 100644 bin/rialto/node/src/command.rs create mode 100644 bin/rialto/node/src/main.rs create mode 100644 bin/rialto/runtime/Cargo.toml create mode 100644 bin/rialto/runtime/build.rs create mode 100644 bin/rialto/runtime/src/lib.rs create mode 100644 bin/rialto/runtime/src/millau_messages.rs create mode 100644 bin/rialto/runtime/src/parachains.rs create mode 100644 bin/rialto/runtime/src/xcm_config.rs create mode 100644 bin/runtime-common/Cargo.toml create mode 100644 bin/runtime-common/src/integrity.rs create mode 100644 bin/runtime-common/src/lib.rs create mode 100644 bin/runtime-common/src/messages.rs create mode 100644 bin/runtime-common/src/messages_api.rs create mode 100644 bin/runtime-common/src/messages_benchmarking.rs create mode 100644 bin/runtime-common/src/messages_extension.rs create mode 100644 bin/runtime-common/src/messages_generation.rs create mode 100644 bin/runtime-common/src/parachains_benchmarking.rs create mode 100644 ci.Dockerfile create mode 100644 deny.toml create mode 100644 deployments/README.md create mode 100644 deployments/bridges/common/generate_messages.sh create mode 100644 deployments/bridges/rialto-millau/dashboard/grafana/relay-millau-to-rialto-messages-dashboard.json create mode 100644 deployments/bridges/rialto-millau/dashboard/grafana/relay-rialto-to-millau-messages-dashboard.json create mode 100644 deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json create mode 100644 deployments/bridges/rialto-millau/dashboard/prometheus/targets.yml create mode 100644 deployments/bridges/rialto-millau/docker-compose.yml create mode 100755 deployments/bridges/rialto-millau/entrypoints/relay-messages-millau-to-rialto-entrypoint.sh create mode 100755 deployments/bridges/rialto-millau/entrypoints/relay-messages-rialto-to-millau-entrypoint.sh create mode 100755 deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh create mode 100755 deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh create mode 100755 deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-resubmitter-entrypoint.sh create mode 100755 deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh create mode 100644 deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-millau-to-rialto-parachain-messages-dashboard.json create mode 100644 deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-rialto-parachain-to-millau-messages-dashboard.json create mode 100644 deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json create mode 100644 deployments/bridges/rialto-parachain-millau/dashboard/prometheus/targets.yml create mode 100644 deployments/bridges/rialto-parachain-millau/docker-compose.yml create mode 100755 deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh create mode 100755 deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-generator-entrypoint.sh create mode 100755 deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-resubmitter-entrypoint.sh create mode 100755 deployments/bridges/rialto-parachain-millau/entrypoints/relay-millau-rialto-parachain-entrypoint.sh create mode 100644 deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-headers-dashboard.json create mode 100644 deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-parachains-dashboard.json create mode 100644 deployments/bridges/westend-millau/dashboard/prometheus/targets.yml create mode 100644 deployments/bridges/westend-millau/docker-compose.yml create mode 100755 deployments/bridges/westend-millau/entrypoints/relay-headers-westend-to-millau-entrypoint.sh create mode 100755 deployments/bridges/westend-millau/entrypoints/relay-parachains-westend-to-millau-entrypoint.sh create mode 100755 deployments/local-scripts/bridge-entrypoint.sh create mode 100755 deployments/local-scripts/relay-messages-millau-to-rialto.sh create mode 100755 deployments/local-scripts/relay-messages-rialto-to-millau.sh create mode 100755 deployments/local-scripts/relay-millau-to-rialto.sh create mode 100755 deployments/local-scripts/relay-rialto-to-millau.sh create mode 100755 deployments/local-scripts/run-millau-node.sh create mode 100755 deployments/local-scripts/run-rialto-node.sh create mode 100755 deployments/local-scripts/run-westend-node.sh create mode 100644 deployments/monitoring/GrafanaMatrix.Dockerfile create mode 100644 deployments/monitoring/disabled.yml create mode 100644 deployments/monitoring/docker-compose.yml create mode 100644 deployments/monitoring/grafana-matrix/config.yml create mode 100644 deployments/monitoring/grafana/dashboards/nodes.json create mode 100644 deployments/monitoring/grafana/provisioning/dashboards/grafana-dashboard.yaml create mode 100644 deployments/monitoring/grafana/provisioning/datasources/grafana-datasource.yaml create mode 100644 deployments/monitoring/grafana/provisioning/notifiers/grafana-notifier.yaml create mode 100644 deployments/monitoring/prometheus/prometheus.yml create mode 100644 deployments/networks/dashboard/grafana/beefy-dashboard.json create mode 100644 deployments/networks/dashboard/prometheus/millau-targets.yml create mode 100644 deployments/networks/dashboard/prometheus/rialto-targets.yml create mode 100755 deployments/networks/entrypoints/rialto-chainspec-exporter-entrypoint.sh create mode 100755 deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh create mode 100644 deployments/networks/millau.yml create mode 100644 deployments/networks/rialto-parachain.yml create mode 100644 deployments/networks/rialto.yml create mode 100644 deployments/reverse-proxy/README.md create mode 100644 deployments/reverse-proxy/docker-compose.yml create mode 100755 deployments/run.sh create mode 100644 deployments/types-millau.json create mode 100644 deployments/types-rialto.json create mode 100755 deployments/types/build.sh create mode 100644 deployments/types/common.json create mode 100644 deployments/types/millau.json create mode 100644 deployments/types/rialto-millau.json create mode 100644 deployments/types/rialto.json create mode 100644 deployments/ui/README.md create mode 100644 deployments/ui/docker-compose.yml create mode 100644 docs/high-level-overview.md create mode 100644 docs/high-level.html create mode 100644 docs/plan.md create mode 100644 docs/scenario1.html create mode 100644 docs/send-message.md create mode 100644 docs/testing-scenarios.md create mode 100644 fuzz/storage-proof/Cargo.lock create mode 100644 fuzz/storage-proof/Cargo.toml create mode 100644 fuzz/storage-proof/README.md create mode 100644 fuzz/storage-proof/src/main.rs create mode 100644 modules/beefy/Cargo.toml create mode 100644 modules/beefy/src/lib.rs create mode 100644 modules/beefy/src/mock.rs create mode 100644 modules/beefy/src/mock_chain.rs create mode 100644 modules/beefy/src/utils.rs create mode 100644 modules/grandpa/Cargo.toml create mode 100644 modules/grandpa/src/benchmarking.rs create mode 100644 modules/grandpa/src/extension.rs create mode 100644 modules/grandpa/src/lib.rs create mode 100644 modules/grandpa/src/mock.rs create mode 100644 modules/grandpa/src/storage_types.rs create mode 100644 modules/grandpa/src/weights.rs create mode 100644 modules/messages/Cargo.toml create mode 100644 modules/messages/README.md create mode 100644 modules/messages/src/benchmarking.rs create mode 100644 modules/messages/src/inbound_lane.rs create mode 100644 modules/messages/src/lib.rs create mode 100644 modules/messages/src/mock.rs create mode 100644 modules/messages/src/outbound_lane.rs create mode 100644 modules/messages/src/weights.rs create mode 100644 modules/messages/src/weights_ext.rs create mode 100644 modules/parachains/Cargo.toml create mode 100644 modules/parachains/src/benchmarking.rs create mode 100644 modules/parachains/src/extension.rs create mode 100644 modules/parachains/src/lib.rs create mode 100644 modules/parachains/src/mock.rs create mode 100644 modules/parachains/src/weights.rs create mode 100644 modules/parachains/src/weights_ext.rs create mode 100644 modules/relayers/Cargo.toml create mode 100644 modules/relayers/src/benchmarking.rs create mode 100644 modules/relayers/src/lib.rs create mode 100644 modules/relayers/src/mock.rs create mode 100644 modules/relayers/src/payment_adapter.rs create mode 100644 modules/relayers/src/weights.rs create mode 100644 modules/shift-session-manager/Cargo.toml create mode 100644 modules/shift-session-manager/src/lib.rs create mode 100644 primitives/beefy/Cargo.toml create mode 100644 primitives/beefy/src/lib.rs create mode 100644 primitives/chain-bridge-hub-rococo/Cargo.toml create mode 100644 primitives/chain-bridge-hub-rococo/src/lib.rs create mode 100644 primitives/chain-bridge-hub-wococo/Cargo.toml create mode 100644 primitives/chain-bridge-hub-wococo/src/lib.rs create mode 100644 primitives/chain-kusama/Cargo.toml create mode 100644 primitives/chain-kusama/src/lib.rs create mode 100644 primitives/chain-millau/Cargo.toml create mode 100644 primitives/chain-millau/src/lib.rs create mode 100644 primitives/chain-millau/src/millau_hash.rs create mode 100644 primitives/chain-polkadot/Cargo.toml create mode 100644 primitives/chain-polkadot/src/lib.rs create mode 100644 primitives/chain-rialto-parachain/Cargo.toml create mode 100644 primitives/chain-rialto-parachain/src/lib.rs create mode 100644 primitives/chain-rialto/Cargo.toml create mode 100644 primitives/chain-rialto/src/lib.rs create mode 100644 primitives/chain-rococo/Cargo.toml create mode 100644 primitives/chain-rococo/src/lib.rs create mode 100644 primitives/chain-westend/Cargo.toml create mode 100644 primitives/chain-westend/src/lib.rs create mode 100644 primitives/chain-wococo/Cargo.toml create mode 100644 primitives/chain-wococo/src/lib.rs create mode 100644 primitives/header-chain/Cargo.toml create mode 100644 primitives/header-chain/src/justification.rs create mode 100644 primitives/header-chain/src/lib.rs create mode 100644 primitives/header-chain/src/storage_keys.rs create mode 100644 primitives/header-chain/tests/implementation_match.rs create mode 100644 primitives/header-chain/tests/justification.rs create mode 100644 primitives/messages/Cargo.toml create mode 100644 primitives/messages/src/lib.rs create mode 100644 primitives/messages/src/source_chain.rs create mode 100644 primitives/messages/src/storage_keys.rs create mode 100644 primitives/messages/src/target_chain.rs create mode 100644 primitives/parachains/Cargo.toml create mode 100644 primitives/parachains/src/lib.rs create mode 100644 primitives/polkadot-core/Cargo.toml create mode 100644 primitives/polkadot-core/src/lib.rs create mode 100644 primitives/polkadot-core/src/parachains.rs create mode 100644 primitives/relayers/Cargo.toml create mode 100644 primitives/relayers/src/lib.rs create mode 100644 primitives/runtime/Cargo.toml create mode 100644 primitives/runtime/src/chain.rs create mode 100644 primitives/runtime/src/lib.rs create mode 100644 primitives/runtime/src/messages.rs create mode 100644 primitives/runtime/src/storage_proof.rs create mode 100644 primitives/runtime/src/storage_types.rs create mode 100644 primitives/test-utils/Cargo.toml create mode 100644 primitives/test-utils/src/keyring.rs create mode 100644 primitives/test-utils/src/lib.rs create mode 100644 relays/bin-substrate/Cargo.toml create mode 100644 relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs create mode 100644 relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs create mode 100644 relays/bin-substrate/src/chains/millau.rs create mode 100644 relays/bin-substrate/src/chains/millau_headers_to_rialto.rs create mode 100644 relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs create mode 100644 relays/bin-substrate/src/chains/millau_messages_to_rialto.rs create mode 100644 relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs create mode 100644 relays/bin-substrate/src/chains/mod.rs create mode 100644 relays/bin-substrate/src/chains/rialto.rs create mode 100644 relays/bin-substrate/src/chains/rialto_headers_to_millau.rs create mode 100644 relays/bin-substrate/src/chains/rialto_messages_to_millau.rs create mode 100644 relays/bin-substrate/src/chains/rialto_parachain.rs create mode 100644 relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs create mode 100644 relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs create mode 100644 relays/bin-substrate/src/chains/rococo.rs create mode 100644 relays/bin-substrate/src/chains/rococo_headers_to_bridge_hub_wococo.rs create mode 100644 relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs create mode 100644 relays/bin-substrate/src/chains/westend.rs create mode 100644 relays/bin-substrate/src/chains/westend_headers_to_millau.rs create mode 100644 relays/bin-substrate/src/chains/westend_parachains_to_millau.rs create mode 100644 relays/bin-substrate/src/chains/wococo.rs create mode 100644 relays/bin-substrate/src/chains/wococo_headers_to_bridge_hub_rococo.rs create mode 100644 relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs create mode 100644 relays/bin-substrate/src/cli/bridge.rs create mode 100644 relays/bin-substrate/src/cli/chain_schema.rs create mode 100644 relays/bin-substrate/src/cli/encode_message.rs create mode 100644 relays/bin-substrate/src/cli/init_bridge.rs create mode 100644 relays/bin-substrate/src/cli/mod.rs create mode 100644 relays/bin-substrate/src/cli/register_parachain.rs create mode 100644 relays/bin-substrate/src/cli/relay_headers.rs create mode 100644 relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs create mode 100644 relays/bin-substrate/src/cli/relay_headers_and_messages/parachain_to_parachain.rs create mode 100644 relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs create mode 100644 relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs create mode 100644 relays/bin-substrate/src/cli/relay_messages.rs create mode 100644 relays/bin-substrate/src/cli/relay_parachains.rs create mode 100644 relays/bin-substrate/src/cli/resubmit_transactions.rs create mode 100644 relays/bin-substrate/src/cli/send_message.rs create mode 100644 relays/bin-substrate/src/main.rs create mode 100644 relays/client-bridge-hub-rococo/Cargo.toml create mode 100644 relays/client-bridge-hub-rococo/src/lib.rs create mode 100644 relays/client-bridge-hub-rococo/src/runtime_wrapper.rs create mode 100644 relays/client-bridge-hub-wococo/Cargo.toml create mode 100644 relays/client-bridge-hub-wococo/src/lib.rs create mode 100644 relays/client-bridge-hub-wococo/src/runtime_wrapper.rs create mode 100644 relays/client-kusama/Cargo.toml create mode 100644 relays/client-kusama/src/lib.rs create mode 100644 relays/client-millau/Cargo.toml create mode 100644 relays/client-millau/src/lib.rs create mode 100644 relays/client-polkadot/Cargo.toml create mode 100644 relays/client-polkadot/src/lib.rs create mode 100644 relays/client-rialto-parachain/Cargo.toml create mode 100644 relays/client-rialto-parachain/src/lib.rs create mode 100644 relays/client-rialto/Cargo.toml create mode 100644 relays/client-rialto/src/lib.rs create mode 100644 relays/client-rococo/Cargo.toml create mode 100644 relays/client-rococo/src/lib.rs create mode 100644 relays/client-substrate/Cargo.toml create mode 100644 relays/client-substrate/src/chain.rs create mode 100644 relays/client-substrate/src/client.rs create mode 100644 relays/client-substrate/src/error.rs create mode 100644 relays/client-substrate/src/guard.rs create mode 100644 relays/client-substrate/src/lib.rs create mode 100644 relays/client-substrate/src/metrics/float_storage_value.rs create mode 100644 relays/client-substrate/src/metrics/mod.rs create mode 100644 relays/client-substrate/src/rpc.rs create mode 100644 relays/client-substrate/src/sync_header.rs create mode 100644 relays/client-substrate/src/test_chain.rs create mode 100644 relays/client-substrate/src/transaction_tracker.rs create mode 100644 relays/client-westend/Cargo.toml create mode 100644 relays/client-westend/src/lib.rs create mode 100644 relays/client-wococo/Cargo.toml create mode 100644 relays/client-wococo/src/lib.rs create mode 100644 relays/finality/Cargo.toml create mode 100644 relays/finality/src/finality_loop.rs create mode 100644 relays/finality/src/finality_loop_tests.rs create mode 100644 relays/finality/src/lib.rs create mode 100644 relays/finality/src/sync_loop_metrics.rs create mode 100644 relays/lib-substrate-relay/Cargo.toml create mode 100644 relays/lib-substrate-relay/src/error.rs create mode 100644 relays/lib-substrate-relay/src/finality/engine.rs create mode 100644 relays/lib-substrate-relay/src/finality/guards.rs create mode 100644 relays/lib-substrate-relay/src/finality/initialize.rs create mode 100644 relays/lib-substrate-relay/src/finality/mod.rs create mode 100644 relays/lib-substrate-relay/src/finality/source.rs create mode 100644 relays/lib-substrate-relay/src/finality/target.rs create mode 100644 relays/lib-substrate-relay/src/lib.rs create mode 100644 relays/lib-substrate-relay/src/messages_lane.rs create mode 100644 relays/lib-substrate-relay/src/messages_metrics.rs create mode 100644 relays/lib-substrate-relay/src/messages_source.rs create mode 100644 relays/lib-substrate-relay/src/messages_target.rs create mode 100644 relays/lib-substrate-relay/src/on_demand/headers.rs create mode 100644 relays/lib-substrate-relay/src/on_demand/mod.rs create mode 100644 relays/lib-substrate-relay/src/on_demand/parachains.rs create mode 100644 relays/lib-substrate-relay/src/parachains/mod.rs create mode 100644 relays/lib-substrate-relay/src/parachains/source.rs create mode 100644 relays/lib-substrate-relay/src/parachains/target.rs create mode 100644 relays/messages/Cargo.toml create mode 100644 relays/messages/src/lib.rs create mode 100644 relays/messages/src/message_lane.rs create mode 100644 relays/messages/src/message_lane_loop.rs create mode 100644 relays/messages/src/message_race_delivery.rs create mode 100644 relays/messages/src/message_race_limits.rs create mode 100644 relays/messages/src/message_race_loop.rs create mode 100644 relays/messages/src/message_race_receiving.rs create mode 100644 relays/messages/src/message_race_strategy.rs create mode 100644 relays/messages/src/metrics.rs create mode 100644 relays/parachains/Cargo.toml create mode 100644 relays/parachains/src/lib.rs create mode 100644 relays/parachains/src/parachains_loop.rs create mode 100644 relays/parachains/src/parachains_loop_metrics.rs create mode 100644 relays/utils/Cargo.toml create mode 100644 relays/utils/src/error.rs create mode 100644 relays/utils/src/initialize.rs create mode 100644 relays/utils/src/lib.rs create mode 100644 relays/utils/src/metrics.rs create mode 100644 relays/utils/src/metrics/float_json_value.rs create mode 100644 relays/utils/src/metrics/global.rs create mode 100644 relays/utils/src/relay_loop.rs create mode 100644 rustfmt.toml create mode 100755 scripts/add_license.sh create mode 100755 scripts/build-containers.sh create mode 100755 scripts/ci-cache.sh create mode 100755 scripts/dump-logs.sh create mode 100644 scripts/license_header create mode 100755 scripts/send-message-from-millau-rialto.sh create mode 100755 scripts/send-message-from-rialto-millau.sh create mode 100644 scripts/update-weights-setup.sh create mode 100755 scripts/update-weights.sh create mode 100755 scripts/update_substrate.sh diff --git a/.config/lingua.dic b/.config/lingua.dic new file mode 100644 index 00000000000..3e2d83b02dd --- /dev/null +++ b/.config/lingua.dic @@ -0,0 +1,242 @@ +90 + +&& +1KB +1MB +5MB += +API/SM +APIs +AccountId/MS +Apache-2.0/M +Autogenerated +BFT/M +BTC/S +Best/MS +BlockId +BlockNumber +BridgeStorage +BridgeHub +BridgeHubRococo +BridgeHubWococo +CLI/MS +Chain1 +Chain2 +ChainSpec +ChainTime +DOT/S +ERC-20 +Ethereum +FN +FinalizationError +GPL/M +GPLv3/M +GiB/S +Handler/MS +Hasher +HeaderA +HeaderId +InitiateChange +Instance1 +Instance2 +Instance42 +KSM/S +KYC/M +KeyPair +Kovan +Lane1 +Lane2 +Lane3 +LaneId +MIN_SIZE +MIT/M +MMR +MaxUnrewardedRelayerEntriesAtInboundLane +MaybeExtra +MaybeOrphan +Merklized +MessageNonce +MessageNonces +MessagePayload +MetricsParams +Millau/MS +OldHeader +OutboundMessages +PoA +PoV/MS +Pre +RLP +RPC/MS +Rialto/MS +RialtoParachain/MS +Relayer/MS +Runtime1 +Runtime2 +SIZE_FACTOR +SS58 +SS58Prefix +STALL_SYNC_TIMEOUT +SURI +ServiceFactory/MS +SignedExtension +Stringified +Submitter1 +S|N +TCP +ThisChain +TODO +U256 +Unparsed +Vec +WND/S +Westend/MS +Wococo/MS +XCM/S +XCMP/M +annualised/MS +api/SM +aren +arg +args +async +auth +auths/SM +backoff +benchmarking/MS +best_substrate_header +bitfield/MS +blake2/MS +blockchain/MS +borked +chain_getBlock +choosen +config/MS +crypto/MS +customizable/B +debian/M +decodable/MS +delivery_and_dispatch_fee +dev +dispatchable +dispatchables +doesn +ed25519 +enum/MS +entrypoint/MS +ethereum/MS +externality/MS +extrinsic/MS +extrinsics +fedora/M +functor +fuzzer +hasher +hardcoded +https +implementers +include/BG +inherent/MS +initialize/RG +instantiate/B +intrinsic/MS +invariant/MS +invariants +io +isn +isolate/BG +js +jsonrpsee +keccak +keccak256/M +keyring +keystore/MS +kusama/S +lane +malus +max_value +merkle/MS +metadata +millau +misbehavior/SM +misbehaviors +multivalidator/SM +natively +no_std +nonces +number +ok +oneshot/MS +others' +pallet_bridge_grandpa +pallet_bridge_messages +pallet_message_lane +parablock/MS +parachain/MS +param/MS +parameterize/D +plancks +polkadot/MS +pov-block/MS +precommit +promethius +promethius' +provisioner/MS +probabilistically +prune_depth +prune_end +receival +reconnection +redhat/M +repo/MS +runtime/MS +rustc/MS +relayer/MS +shouldn +source_at_target +source_latest_confirmed +source_latest_generated +sp_finality_grandpa +spawner +sr25519 +src +stringified +struct/MS +submitters/MS +subsystem/MS +subsystems' +subcommand/MS +synchronizer +target_at_source +target_latest_confirmed +target_latest_received +taskmanager/MS +teleport/RG +teleportation/SM +teleporter/SM +teleporters +testnet/MS +timeframe +tokio +timestamp +trie/MS +trustless/Y +tuple +u32 +ubuntu/M +undeliverable +unfinalized +union/MSG +unpruned +unservable/B +unsynced +updatable +validator/SM +ve +vec +verifier +w3f/MS +wakeup +wasm/M +websocket +x2 +~ diff --git a/.config/spellcheck.toml b/.config/spellcheck.toml new file mode 100644 index 00000000000..e061c29ac22 --- /dev/null +++ b/.config/spellcheck.toml @@ -0,0 +1,13 @@ +[hunspell] +lang = "en_US" +search_dirs = ["."] +extra_dictionaries = ["lingua.dic"] +skip_os_lookups = true +use_builtin = true + +[hunspell.quirks] +# `Type`'s +# 5x +transform_regex = ["^'([^\\s])'$", "^[0-9]+(?:\\.[0-9]*)?x$", "^'s$", "^\\+$", "[><+-]"] +allow_concatenation = true +allow_dashes = true diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000000..f4ceea78560 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +**/target/ diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..e2375881ea0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +root = true +[*] +indent_style=tab +indent_size=tab +tab_width=4 +end_of_line=lf +charset=utf-8 +trim_trailing_whitespace=true +max_line_length=100 +insert_final_newline=true + +[*.{yml,md,yaml,sh}] +indent_style=space +indent_size=2 +tab_width=8 +end_of_line=lf + +[*.md] +max_line_length=80 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..a06d573703d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,42 @@ +version: 2 +updates: +- package-ecosystem: cargo + directory: "/" + schedule: + interval: weekly + time: "03:00" + timezone: Europe/Berlin + open-pull-requests-limit: 20 + ignore: + - dependency-name: frame-* + versions: + - ">= 0" + - dependency-name: node-inspect + versions: + - ">= 0" + - dependency-name: pallet-* + versions: + - ">= 0" + - dependency-name: sc-* + versions: + - ">= 0" + - dependency-name: sp-* + versions: + - ">= 0" + - dependency-name: substrate-* + versions: + - ">= 0" + - dependency-name: vergen + versions: + - 4.0.1 + - 4.0.2 + - 4.1.0 + - 4.2.0 + - dependency-name: jsonrpc-core + versions: + - 17.0.0 + - dependency-name: finality-grandpa + versions: + - 0.13.0 + - 0.14.0 + rebase-strategy: disabled diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..5d10cfa41a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +**/target/ +**/.env +**/.env2 +**/rust-toolchain +hfuzz_target +hfuzz_workspace +**/Cargo.lock + +**/*.rs.bk + +*.o +*.so +*.rlib +*.dll +.gdb_history + +*.exe + +.DS_Store + +.cargo +.idea +.vscode +*.iml +*.swp +*.swo diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000000..eb01cfc67ea --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,303 @@ +stages: + - lint + - check + - test + - build + - publish + +workflow: + rules: + - if: $CI_COMMIT_TAG + - if: $CI_COMMIT_BRANCH + +variables: &default-vars + GIT_STRATEGY: fetch + GIT_DEPTH: 100 + CARGO_INCREMENTAL: 0 + ARCH: "x86_64" + CI_IMAGE: "paritytech/bridges-ci:production" + RUST_BACKTRACE: full + +default: + cache: {} + +.collect-artifacts: &collect-artifacts + artifacts: + name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" + when: on_success + expire_in: 7 days + paths: + - artifacts/ + +.kubernetes-build: &kubernetes-build + tags: + - kubernetes-parity-build + interruptible: true + +.docker-env: &docker-env + image: "${CI_IMAGE}" + before_script: + - rustup show + - cargo --version + - rustup +nightly show + - cargo +nightly --version + - sccache -s + retry: + max: 2 + when: + - runner_system_failure + - unknown_failure + - api_failure + interruptible: true + tags: + - linux-docker + +.test-refs: &test-refs + rules: + # FIXME: This is the cause why pipelines wouldn't start. The problem might be in our custom + # mirroring. This should be investigated further, but for now let's have the working + # pipeline. + # - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH + # changes: + # - '**.md' + # - diagrams/* + # - docs/* + # when: never + - if: $CI_PIPELINE_SOURCE == "pipeline" + - if: $CI_PIPELINE_SOURCE == "web" + - if: $CI_PIPELINE_SOURCE == "schedule" + - if: $CI_COMMIT_REF_NAME == "master" + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + +.build-refs: &build-refs + rules: + # won't run on the CI image update pipeline + - if: $CI_PIPELINE_SOURCE == "pipeline" + when: never + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]{4}-[0-9]{2}-[0-9]{2}.*$/ # i.e. v2021-09-27, v2021-09-27-1 + # there are two types of nightly pipelines: + # 1. this one is triggered by the schedule with $PIPELINE == "nightly", it's for releasing. + # this job runs only on nightly pipeline with the mentioned variable, against `master` branch + - if: $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "nightly" + +.nightly-test: &nightly-test + rules: + # 2. another is triggered by scripts repo $CI_PIPELINE_SOURCE == "pipeline" it's for the CI image + # update, it also runs all the nightly checks. + - if: $CI_PIPELINE_SOURCE == "pipeline" + +#### stage: lint + +clippy-nightly: + stage: lint + <<: *docker-env + <<: *test-refs + variables: + RUSTFLAGS: "-D warnings" + script: + # clippy currently is raising `derive-partial-eq-without-eq` warning even if `Eq` is actually derived + - SKIP_WASM_BUILD=1 cargo +nightly clippy --all-targets -- -A clippy::redundant_closure -A clippy::derive-partial-eq-without-eq -A clippy::or_fun_call + +fmt: + stage: lint + <<: *docker-env + <<: *test-refs + script: + - cargo +nightly fmt --all -- --check + +spellcheck: + stage: lint + <<: *docker-env + <<: *test-refs + script: + - cargo spellcheck check --cfg=.config/spellcheck.toml --checkers hunspell -m 1 + +#### stage: check + +check: + stage: check + <<: *docker-env + <<: *test-refs + script: &check-script + - SKIP_WASM_BUILD=1 time cargo check --locked --verbose --workspace + # Check Rialto benchmarks runtime + - SKIP_WASM_BUILD=1 time cargo check -p rialto-runtime --locked --features runtime-benchmarks --verbose + # Check Millau benchmarks runtime + - SKIP_WASM_BUILD=1 time cargo check -p millau-runtime --locked --features runtime-benchmarks --verbose + +check-nightly: + stage: check + <<: *docker-env + <<: *nightly-test + script: + - rustup default nightly + - *check-script + +#### stage: test + +test: + stage: test + <<: *docker-env + <<: *test-refs +# variables: +# RUSTFLAGS: "-D warnings" + script: &test-script + - time cargo fetch + - time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-runtime\").manifest_path"` + - time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"kusama-runtime\").manifest_path"` + - CARGO_NET_OFFLINE=true SKIP_POLKADOT_RUNTIME_WASM_BUILD=1 SKIP_KUSAMA_RUNTIME_WASM_BUILD=1 SKIP_POLKADOT_TEST_RUNTIME_WASM_BUILD=1 time cargo test --verbose --workspace + +test-nightly: + stage: test + <<: *docker-env + <<: *nightly-test + script: + - rustup default nightly + - *test-script + +deny: + stage: test + <<: *docker-env + <<: *nightly-test + <<: *collect-artifacts + script: + - cargo deny check advisories --hide-inclusion-graph + - cargo deny check bans sources --hide-inclusion-graph + after_script: + - mkdir -p ./artifacts + - echo "___Complete logs can be found in the artifacts___" + - cargo deny check advisories 2> advisories.log + - cargo deny check bans sources 2> bans_sources.log + # this job is allowed to fail, only licenses check is important + allow_failure: true + +deny-licenses: + stage: test + <<: *docker-env + <<: *test-refs + <<: *collect-artifacts + script: + - cargo deny check licenses --hide-inclusion-graph + after_script: + - mkdir -p ./artifacts + - echo "___Complete logs can be found in the artifacts___" + - cargo deny check licenses 2> licenses.log + +benchmarks-test: + stage: test + <<: *docker-env + <<: *nightly-test + script: + - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_messages --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 + - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_grandpa --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 + - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_parachains --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 + - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_relayers --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 + # we may live with failing benchmarks, it is just a signal for us + allow_failure: true + +#### stage: build + +build: + stage: build + <<: *docker-env + <<: *build-refs + <<: *collect-artifacts + # master + script: &build-script + - time cargo fetch + - time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-runtime\").manifest_path"` + - time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"kusama-runtime\").manifest_path"` + - CARGO_NET_OFFLINE=true SKIP_POLKADOT_RUNTIME_WASM_BUILD=1 SKIP_KUSAMA_RUNTIME_WASM_BUILD=1 SKIP_POLKADOT_TEST_RUNTIME_WASM_BUILD=1 time cargo build --release --verbose --workspace + after_script: + # Prepare artifacts + - mkdir -p ./artifacts + - strip ./target/release/rialto-bridge-node + - mv -v ./target/release/rialto-bridge-node ./artifacts/ + - strip ./target/release/rialto-parachain-collator + - mv -v ./target/release/rialto-parachain-collator ./artifacts/ + - strip ./target/release/millau-bridge-node + - mv -v ./target/release/millau-bridge-node ./artifacts/ + - strip ./target/release/substrate-relay + - mv -v ./target/release/substrate-relay ./artifacts/ + - mv -v ./deployments/local-scripts/bridge-entrypoint.sh ./artifacts/ + - mv -v ./ci.Dockerfile ./artifacts/ + +build-nightly: + stage: build + <<: *docker-env + <<: *collect-artifacts + <<: *nightly-test + script: + - rustup default nightly + - *build-script + +#### stage: publish + +.build-push-image: &build-push-image + <<: *kubernetes-build + image: quay.io/buildah/stable:v1.27 + <<: *build-refs + variables: &image-variables + GIT_STRATEGY: none + DOCKERFILE: ci.Dockerfile + IMAGE_NAME: docker.io/paritytech/$CI_JOB_NAME + needs: + - job: build + artifacts: true + before_script: &check-versions + - if [[ "${CI_COMMIT_TAG}" ]]; then + VERSION=${CI_COMMIT_TAG}; + elif [[ "${CI_COMMIT_REF_NAME}" ]]; then + VERSION=$(echo ${CI_COMMIT_REF_NAME} | sed -r 's#/+#-#g'); + fi + # When building from version tags (v1.0, v2.1rc1, ...) we'll use "production" to tag + # docker image. In all other cases, it'll be "latest". + - if [[ $CI_COMMIT_REF_NAME =~ ^v[0-9]+\.[0-9]+.*$ ]]; then + FLOATING_TAG="production"; + else + FLOATING_TAG="latest"; + fi + - echo "Effective tags = ${VERSION} sha-${CI_COMMIT_SHORT_SHA} ${FLOATING_TAG}" + script: + - test "${Docker_Hub_User_Parity}" -a "${Docker_Hub_Pass_Parity}" || + ( echo "no docker credentials provided"; exit 1 ) + - cd ./artifacts + - buildah bud + --format=docker + --build-arg VCS_REF="${CI_COMMIT_SHORT_SHA}" + --build-arg BUILD_DATE="$(date +%d-%m-%Y)" + --build-arg PROJECT="${CI_JOB_NAME}" + --build-arg VERSION="${VERSION}" + --tag "${IMAGE_NAME}:${VERSION}" + --tag "${IMAGE_NAME}:sha-${CI_COMMIT_SHORT_SHA}" + --tag "${IMAGE_NAME}:${FLOATING_TAG}" + --file "${DOCKERFILE}" . + # The job will success only on the protected branch + - echo "${Docker_Hub_Pass_Parity}" | + buildah login --username "${Docker_Hub_User_Parity}" --password-stdin docker.io + - buildah info + - buildah push --format=v2s2 "${IMAGE_NAME}:${VERSION}" + - buildah push --format=v2s2 "${IMAGE_NAME}:sha-${CI_COMMIT_SHORT_SHA}" + - buildah push --format=v2s2 "${IMAGE_NAME}:${FLOATING_TAG}" + after_script: + - env REGISTRY_AUTH_FILE= buildah logout --all + +rialto-bridge-node: + stage: publish + <<: *build-push-image + +rialto-parachain-collator: + stage: publish + <<: *build-push-image + +millau-bridge-node: + stage: publish + <<: *build-push-image + +substrate-relay: + stage: publish + <<: *build-push-image + +# FIXME: publish binaries diff --git a/.maintain/millau-weight-template.hbs b/.maintain/millau-weight-template.hbs new file mode 100644 index 00000000000..ac5c6b85b91 --- /dev/null +++ b/.maintain/millau-weight-template.hbs @@ -0,0 +1,106 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! 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}} +//! EXECUTION: {{cmd.execution}}, WASM-EXECUTION: {{cmd.wasm_execution}} +//! CHAIN: {{cmd.chain}}, DB CACHE: {{cmd.db_cache}} + +// Executed Command: +{{#each args as |arg|~}} +// {{arg}} +{{/each}} + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// 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}}` that are generated using one of the Bridge testnets. +/// +/// Those weights are test only and must never be used in production. +pub struct BridgeWeight(PhantomData); +impl WeightInfo for BridgeWeight { + {{~#each benchmarks as |benchmark|}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} + ) -> Weight { + Weight::from_ref_time({{underscore benchmark.base_weight}} as u64) + {{~#each benchmark.component_weight as |cw|}} + .saturating_add(Weight::from_ref_time({{underscore cw.slope}} as u64).saturating_mul({{cw.name}} as u64)) + {{~/each}} + {{~#if (ne benchmark.base_reads "0")}} + .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}} as u64)) + {{~/if}} + {{~#each benchmark.component_reads as |cr|}} + .saturating_add(T::DbWeight::get().reads(({{cr.slope}} as u64).saturating_mul({{cr.name}} as u64))) + {{~/each}} + {{~#if (ne benchmark.base_writes "0")}} + .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}} as u64)) + {{~/if}} + {{~#each benchmark.component_writes as |cw|}} + .saturating_add(T::DbWeight::get().writes(({{cw.slope}} as u64).saturating_mul({{cw.name}} as u64))) + {{~/each}} + } + {{~/each}} +} + +// For backwards compatibility and tests +impl WeightInfo for () { + {{~#each benchmarks as |benchmark|}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} + ) -> Weight { + Weight::from_ref_time({{underscore benchmark.base_weight}} as u64) + {{~#each benchmark.component_weight as |cw|}} + .saturating_add(Weight::from_ref_time({{underscore cw.slope}} as u64).saturating_mul({{cw.name}} as u64)) + {{~/each}} + {{~#if (ne benchmark.base_reads "0")}} + .saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}} as u64)) + {{~/if}} + {{~#each benchmark.component_reads as |cr|}} + .saturating_add(RocksDbWeight::get().reads(({{cr.slope}} as u64).saturating_mul({{cr.name}} as u64))) + {{~/each}} + {{~#if (ne benchmark.base_writes "0")}} + .saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}} as u64)) + {{~/if}} + {{~#each benchmark.component_writes as |cw|}} + .saturating_add(RocksDbWeight::get().writes(({{cw.slope}} as u64).saturating_mul({{cw.name}} as u64))) + {{~/each}} + } + {{~/each}} +} diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000000..3941ba8451a --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,21 @@ +# Lists some code owners. +# +# A codeowner just oversees some part of the codebase. If an owned file is changed then the +# corresponding codeowner receives a review request. An approval of the codeowner might be +# required for merging a PR (depends on repository settings). +# +# For details about syntax, see: +# https://help.github.com/en/articles/about-code-owners +# But here are some important notes: +# +# - Glob syntax is git-like, e.g. `/core` means the core directory in the root, unlike `core` +# which can be everywhere. +# - Multiple owners are supported. +# - Either handle (e.g, @github_user or @github_org/team) or email can be used. Keep in mind, +# that handles might work better because they are more recognizable on GitHub, +# eyou can use them for mentioning unlike an email. +# - The latest matching rule, if multiple, takes precedence. + +# CI +/.github/ @paritytech/ci +/.gitlab-ci.yml @paritytech/ci diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..70541fb72fa --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,80 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers +pledge to making participation in our project and our community a harassment-free experience for +everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity +and expression, level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit + permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +### Facilitation, Not Strongarming + +We recognise that this software is merely a tool for users to create and maintain their blockchain +of preference. We see that blockchains are naturally community platforms with users being the +ultimate decision makers. We assert that good software will maximise user agency by facilitate +user-expression on the network. As such: + +- This project will strive to give users as much choice as is both reasonable and possible over what + protocol they adhere to; but +- use of the project's technical forums, commenting systems, pull requests and issue trackers as a + means to express individual protocol preferences is forbidden. + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are +expected to take appropriate and fair corrective action in response to any instances of unacceptable +behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, +code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or +to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is +representing the project or its community. Examples of representing a project or community include +using an official project e-mail address, posting via an official social media account, or acting as +an appointed representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting +the project team at admin@parity.io. All complaints will be reviewed and investigated and will +result in a response that is deemed necessary and appropriate to the circumstances. The project team +is obligated to maintain confidentiality with regard to the reporter of an incident. Further +details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face +temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at +https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000000..969ed49c8c1 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,13269 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aes-gcm" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.3", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "always-assert" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf688625d06217d5b1bb0ea9d9c44a1635fd0ee3534466388d18203174f4d11" + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" + +[[package]] +name = "approx" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "072df7202e63b127ab55acfe16ce97013d5b97bf160489336d3f1840fd78e99e" +dependencies = [ + "num-traits", +] + +[[package]] +name = "arbitrary" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510c76ecefdceada737ea728f4f9a84bd2e1ef29f1ba555e560940fe279954de" + +[[package]] +name = "array-bytes" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a913633b0c922e6b745072795f50d90ebea78ba31a57e2ac8c2fc7b50950949" + +[[package]] +name = "array_tool" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "asn1_der" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22d1f4b888c298a027c99dc9048015fac177587de20fc30232a057dfbe24a21" + +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "async-channel" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-mutex", + "blocking", + "futures-lite", + "num_cpus", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" +dependencies = [ + "concurrent-queue", + "futures-lite", + "libc", + "log", + "once_cell", + "parking", + "polling", + "slab", + "socket2", + "waker-fn", + "winapi", +] + +[[package]] +name = "async-lock" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-process" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83137067e3a2a6a06d67168e49e68a0957d215410473a740cea95a2425c0b7c6" +dependencies = [ + "async-io", + "blocking", + "cfg-if 1.0.0", + "event-listener", + "futures-lite", + "libc", + "once_cell", + "signal-hook", + "winapi", +] + +[[package]] +name = "async-std" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52580991739c5cdb36cde8b2a516371c0a3b70dda36d916cc08b82372916808c" +dependencies = [ + "async-attributes", + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "num_cpus", + "once_cell", + "pin-project-lite 0.2.9", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-std-resolver" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba50e24d9ee0a8950d3d03fc6d0dd10aa14b5de3b101949b4e160f7fee7c723" +dependencies = [ + "async-std", + "async-trait", + "futures-io", + "futures-util", + "pin-utils", + "socket2", + "trust-dns-resolver", +] + +[[package]] +name = "async-task" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" + +[[package]] +name = "async-trait" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "asynchronous-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0de5164e5edbf51c45fb8c2d9664ae1c095cce1b265ecf7569093c0d66ef690" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite 0.2.9", +] + +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backoff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721c249ab59cbc483ad4294c9ee2671835c1e43e9ffc277e6b4ecfef733cfdc5" +dependencies = [ + "instant", + "rand 0.7.3", +] + +[[package]] +name = "backoff" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +dependencies = [ + "futures-core", + "getrandom 0.2.3", + "instant", + "pin-project-lite 0.2.9", + "rand 0.8.5", + "tokio", +] + +[[package]] +name = "backtrace" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object 0.27.1", + "rustc-demangle", +] + +[[package]] +name = "base-x" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "base64ct" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2b2456fd614d856680dcd9fcc660a51a820fa09daef2e49772b56a193c8474" + +[[package]] +name = "beef" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bed554bd50246729a1ec158d08aa3235d1b69d94ad120ebe187e28894787e736" +dependencies = [ + "serde", +] + +[[package]] +name = "beefy-gadget" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "array-bytes", + "async-trait", + "beefy-primitives", + "fnv", + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-chain-spec", + "sc-client-api", + "sc-consensus", + "sc-finality-grandpa", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-network-gossip", + "sc-utils", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-keystore", + "sp-mmr-primitives", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", + "wasm-timer", +] + +[[package]] +name = "beefy-gadget-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "beefy-gadget", + "beefy-primitives", + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-rpc", + "sc-utils", + "serde", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "beefy-merkle-tree" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "beefy-primitives", + "sp-api", + "sp-runtime", +] + +[[package]] +name = "beefy-primitives" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-mmr-primitives", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.60.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388" +dependencies = [ + "digest 0.10.3", +] + +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +dependencies = [ + "arrayvec 0.4.12", + "constant_time_eq", +] + +[[package]] +name = "blake2b_simd" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "constant_time_eq", +] + +[[package]] +name = "blake2s_simd" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "constant_time_eq", +] + +[[package]] +name = "blake3" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "cc", + "cfg-if 1.0.0", + "constant_time_eq", + "digest 0.10.3", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "block-buffer" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "blocking" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046e47d4b2d391b1f6f8b407b1deb8dee56c1852ccd868becf2710f601b5f427" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", +] + +[[package]] +name = "bounded-vec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3372be4090bf9d4da36bd8ba7ce6ca1669503d0cf6e667236c6df7f053153eb6" +dependencies = [ + "thiserror", +] + +[[package]] +name = "bp-beefy" +version = "0.1.0" +dependencies = [ + "beefy-merkle-tree", + "beefy-primitives", + "bp-runtime", + "frame-support", + "pallet-beefy-mmr", + "pallet-mmr", + "parity-scale-codec", + "scale-info", + "serde", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "static_assertions", +] + +[[package]] +name = "bp-bridge-hub-rococo" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "polkadot-runtime-constants", + "smallvec", + "sp-api", + "sp-std", +] + +[[package]] +name = "bp-bridge-hub-wococo" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-rococo", + "bp-messages", + "bp-runtime", + "frame-support", + "sp-api", + "sp-std", +] + +[[package]] +name = "bp-header-chain" +version = "0.1.0" +dependencies = [ + "bp-runtime", + "bp-test-utils", + "finality-grandpa", + "frame-support", + "hex", + "hex-literal", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-finality-grandpa", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "bp-kusama" +version = "0.1.0" +dependencies = [ + "bp-polkadot-core", + "bp-runtime", + "sp-api", +] + +[[package]] +name = "bp-messages" +version = "0.1.0" +dependencies = [ + "bp-runtime", + "frame-support", + "hex", + "hex-literal", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-std", +] + +[[package]] +name = "bp-millau" +version = "0.1.0" +dependencies = [ + "bp-beefy", + "bp-messages", + "bp-runtime", + "fixed-hash 0.7.0", + "frame-support", + "frame-system", + "hash256-std-hasher", + "impl-codec", + "impl-serde 0.3.2", + "parity-util-mem", + "scale-info", + "serde", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "bp-parachains" +version = "0.1.0" +dependencies = [ + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "parity-scale-codec", + "scale-info", + "sp-core", +] + +[[package]] +name = "bp-polkadot" +version = "0.1.0" +dependencies = [ + "bp-polkadot-core", + "bp-runtime", + "sp-api", +] + +[[package]] +name = "bp-polkadot-core" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-runtime", + "frame-support", + "frame-system", + "hex", + "parity-scale-codec", + "parity-util-mem", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-relayers" +version = "0.1.0" +dependencies = [ + "bp-messages", + "frame-support", + "hex", + "hex-literal", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-rialto" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-runtime", + "frame-support", + "frame-system", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-rialto-parachain" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-runtime", + "frame-support", + "frame-system", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-rococo" +version = "0.1.0" +dependencies = [ + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "sp-api", +] + +[[package]] +name = "bp-runtime" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "hash-db", + "hex-literal", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "trie-db", +] + +[[package]] +name = "bp-test-utils" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "ed25519-dalek", + "finality-grandpa", + "parity-scale-codec", + "sp-application-crypto", + "sp-finality-grandpa", + "sp-runtime", + "sp-std", + "xcm", +] + +[[package]] +name = "bp-westend" +version = "0.1.0" +dependencies = [ + "bp-polkadot-core", + "bp-runtime", + "sp-api", +] + +[[package]] +name = "bp-wococo" +version = "0.1.0" +dependencies = [ + "bp-polkadot-core", + "bp-rococo", + "bp-runtime", + "sp-api", +] + +[[package]] +name = "bridge-runtime-common" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-messages", + "bp-parachains", + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "frame-system", + "hash-db", + "log", + "millau-runtime", + "pallet-balances", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-trie", + "static_assertions", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "memchr", +] + +[[package]] +name = "build-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdce191bf3fa4995ce948c8c83b4640a1745457a149e73c6db75b4ffe36aad5f" +dependencies = [ + "semver 0.6.0", +] + +[[package]] +name = "bumpalo" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" + +[[package]] +name = "byte-slice-cast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c5fdd0166095e1d463fc6cc01aa8ce547ad77a4e84d42eb6762b084e28067e" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "cache-padded" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" + +[[package]] +name = "camino" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d74260d9bf6944e2208aa46841b4b8f0d7ffc0849a06837b2f510337f86b2b" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.4", + "serde", + "serde_json", +] + +[[package]] +name = "castaway" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed247d1586918e46f2bbe0f13b06498db8dab5a8c1093f156652e9f2e0a73fc3" + +[[package]] +name = "cc" +version = "1.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-expr" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chacha20" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", + "zeroize", +] + +[[package]] +name = "chacha20poly1305" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time 0.1.44", + "winapi", +] + +[[package]] +name = "cid" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" +dependencies = [ + "core2", + "multibase", + "multihash", + "serde", + "unsigned-varint", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "ckb-merkle-mountain-range" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f061f97d64fd1822664bdfb722f7ae5469a97b77567390f7442be5b5dc82a5b" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "clang-sys" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa66045b9cb23c2e9c1520732030608b02ee07e5cfaa5a521ec15ded7fa24c90" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term 0.11.0", + "atty", + "bitflags", + "strsim 0.8.0", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "clap" +version = "4.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2148adefda54e14492fb9bddcc600b4344c5d1a3123bd666dcb939c6f0e0e57e" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "once_cell", + "strsim 0.10.0", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "coarsetime" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "454038500439e141804c655b4cd1bc6a70bcb95cd2bc9463af5661b6956f0e46" +dependencies = [ + "libc", + "once_cell", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "comfy-table" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85914173c2f558d61613bfbbf1911f14e630895087a7ed2fafc0f5319e1536e7" +dependencies = [ + "strum 0.24.1", + "strum_macros 0.24.0", + "unicode-width", +] + +[[package]] +name = "concurrent-queue" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +dependencies = [ + "cache-padded", +] + +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpp_demangle" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "931ab2a3e6330a07900b8e7ca4e106cdcbb93f2b9a52df55e54ee53d8305b55d" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.88.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44409ccf2d0f663920cab563d2b79fcd6b2e9a2bcc6e929fef76c8f82ad6c17a" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.88.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de2018ad96eb97f621f7d6b900a0cc661aec8d02ea4a50e56ecb48e5a2fcaf" +dependencies = [ + "arrayvec 0.7.2", + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "cranelift-isle", + "gimli", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.88.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5287ce36e6c4758fbaf298bd1a8697ad97a4f2375a3d1b61142ea538db4877e5" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.88.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2855c24219e2f08827f3f4ffb2da92e134ae8d8ecc185b11ec8f9878cf5f588e" + +[[package]] +name = "cranelift-entity" +version = "0.88.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b65673279d75d34bf11af9660ae2dbd1c22e6d28f163f5c72f4e1dc56d56103" +dependencies = [ + "serde", +] + +[[package]] +name = "cranelift-frontend" +version = "0.88.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed2b3d7a4751163f6c4a349205ab1b7d9c00eecf19dcea48592ef1f7688eefc" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.88.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be64cecea9d90105fc6a2ba2d003e98c867c1d6c4c86cc878f97ad9fb916293" + +[[package]] +name = "cranelift-native" +version = "0.88.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a03a6ac1b063e416ca4b93f6247978c991475e8271465340caa6f92f3c16a4" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.88.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c699873f7b30bc5f20dd03a796b4183e073a46616c91704792ec35e45d13f913" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools", + "log", + "smallvec", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "crc32fast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3825b1e8580894917dc4468cb634a1b4e9745fddc854edad72d9c04644c0319f" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array 0.14.4", + "rand_core 0.6.3", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array 0.14.4", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + +[[package]] +name = "ctor" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher", +] + +[[package]] +name = "cumulus-client-cli" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "clap 4.0.26", + "parity-scale-codec", + "sc-chain-spec", + "sc-cli", + "sc-service", + "sp-core", + "sp-runtime", + "url", +] + +[[package]] +name = "cumulus-client-collator" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "cumulus-client-consensus-common", + "cumulus-client-network", + "cumulus-primitives-core", + "futures", + "parity-scale-codec", + "parking_lot 0.12.1", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-overseer", + "polkadot-primitives", + "sc-client-api", + "sp-api", + "sp-consensus", + "sp-core", + "sp-runtime", + "tracing", +] + +[[package]] +name = "cumulus-client-consensus-aura" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "async-trait", + "cumulus-client-consensus-common", + "cumulus-primitives-core", + "futures", + "parity-scale-codec", + "sc-client-api", + "sc-consensus", + "sc-consensus-aura", + "sc-consensus-slots", + "sc-telemetry", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "tracing", +] + +[[package]] +name = "cumulus-client-consensus-common" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "async-trait", + "cumulus-relay-chain-interface", + "dyn-clone", + "futures", + "parity-scale-codec", + "polkadot-primitives", + "sc-client-api", + "sc-consensus", + "sp-blockchain", + "sp-consensus", + "sp-runtime", + "sp-trie", + "tracing", +] + +[[package]] +name = "cumulus-client-network" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "async-trait", + "cumulus-relay-chain-interface", + "futures", + "futures-timer", + "parity-scale-codec", + "parking_lot 0.12.1", + "polkadot-node-primitives", + "polkadot-parachain", + "polkadot-primitives", + "sc-client-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-state-machine", + "tracing", +] + +[[package]] +name = "cumulus-client-pov-recovery" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "futures", + "futures-timer", + "parity-scale-codec", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-overseer", + "polkadot-primitives", + "rand 0.8.5", + "sc-client-api", + "sc-consensus", + "sp-consensus", + "sp-maybe-compressed-blob", + "sp-runtime", + "tracing", +] + +[[package]] +name = "cumulus-client-service" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "cumulus-client-cli", + "cumulus-client-collator", + "cumulus-client-consensus-common", + "cumulus-client-pov-recovery", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "parking_lot 0.12.1", + "polkadot-primitives", + "sc-client-api", + "sc-consensus", + "sc-service", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "cumulus-pallet-aura-ext" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "frame-support", + "frame-system", + "pallet-aura", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-aura", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "cumulus-pallet-dmp-queue" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", +] + +[[package]] +name = "cumulus-pallet-parachain-system" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "bytes", + "cumulus-pallet-parachain-system-proc-macro", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "environmental", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "polkadot-parachain", + "scale-info", + "sp-core", + "sp-externalities", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "sp-version", +] + +[[package]] +name = "cumulus-pallet-parachain-system-proc-macro" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cumulus-pallet-xcm" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", +] + +[[package]] +name = "cumulus-pallet-xcmp-queue" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "polkadot-runtime-common", + "rand_chacha 0.3.1", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", + "xcm-executor", +] + +[[package]] +name = "cumulus-primitives-core" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain", + "polkadot-primitives", + "sp-api", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "cumulus-primitives-parachain-inherent" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "cumulus-test-relay-sproof-builder", + "parity-scale-codec", + "sc-client-api", + "scale-info", + "sp-api", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-storage", + "sp-trie", + "tracing", +] + +[[package]] +name = "cumulus-primitives-timestamp" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "cumulus-primitives-core", + "futures", + "parity-scale-codec", + "sp-inherents", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "cumulus-relay-chain-inprocess-interface" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "futures", + "futures-timer", + "polkadot-cli", + "polkadot-client", + "polkadot-service", + "sc-cli", + "sc-client-api", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sp-api", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-state-machine", +] + +[[package]] +name = "cumulus-relay-chain-interface" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "futures", + "jsonrpsee-core", + "parity-scale-codec", + "polkadot-overseer", + "polkadot-service", + "sc-client-api", + "sp-api", + "sp-blockchain", + "sp-state-machine", + "thiserror", +] + +[[package]] +name = "cumulus-relay-chain-minimal-node" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "array-bytes", + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "cumulus-relay-chain-rpc-interface", + "futures", + "lru 0.8.1", + "polkadot-availability-distribution", + "polkadot-core-primitives", + "polkadot-network-bridge", + "polkadot-node-core-av-store", + "polkadot-node-network-protocol", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-primitives", + "polkadot-service", + "sc-authority-discovery", + "sc-client-api", + "sc-consensus", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-network-light", + "sc-network-sync", + "sc-service", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "sc-transaction-pool-api", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-runtime", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "cumulus-relay-chain-rpc-interface" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "async-trait", + "backoff 0.4.0", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "futures", + "futures-timer", + "jsonrpsee", + "parity-scale-codec", + "polkadot-service", + "sc-client-api", + "sc-rpc-api", + "sp-api", + "sp-authority-discovery", + "sp-consensus-babe", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-storage", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "cumulus-test-relay-sproof-builder" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "cumulus-primitives-core", + "parity-scale-codec", + "polkadot-primitives", + "sp-runtime", + "sp-state-machine", + "sp-std", +] + +[[package]] +name = "curl" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc6d233563261f8db6ffb83bbaad5a73837a6e6b28868e926337ebbdece0be3" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2", + "winapi", +] + +[[package]] +name = "curl-sys" +version = "0.4.51+curl-7.80.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d130987e6a6a34fe0889e1083022fa48cd90e6709a84be3fb8dd95801de5af20" +dependencies = [ + "cc", + "libc", + "libnghttp2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "winapi", +] + +[[package]] +name = "curve25519-dalek" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "4.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4033478fbf70d6acf2655ac70da91ee65852d69daf7a67bf7a2f518fb47aafcf" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.3", + "subtle", + "zeroize", +] + +[[package]] +name = "cxx" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "data-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" + +[[package]] +name = "data-encoding-macro" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86927b7cd2fe88fa698b87404b287ab98d1a0063a34071d92e575b72d3029aca" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" +dependencies = [ + "data-encoding", + "syn", +] + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.0", + "syn", +] + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer 0.10.0", + "crypto-common", + "subtle", +] + +[[package]] +name = "directories" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dns-parser" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea" +dependencies = [ + "byteorder", + "quick-error 1.2.3", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "dtoa" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6053ff46b5639ceb91756a85a4c8914668393a03170efd79c8884a529d80656" + +[[package]] +name = "dyn-clonable" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +dependencies = [ + "dyn-clonable-impl", + "dyn-clone", +] + +[[package]] +name = "dyn-clonable-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dyn-clone" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" + +[[package]] +name = "ecdsa" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.8", + "zeroize", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek 3.2.0", + "hashbrown", + "hex", + "rand_core 0.6.3", + "sha2 0.9.8", + "zeroize", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "elliptic-curve" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "ff", + "generic-array 0.14.4", + "group", + "rand_core 0.6.3", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74ea89a0a1b98f6332de42c95baff457ada66d1cb4030f9ff151b2041a1c746" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "enum-as-inner" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" +dependencies = [ + "heck 0.4.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "enumflags2" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb" +dependencies = [ + "enumflags2_derive", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "enumn" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038b1afa59052df211f9efd58f8b1d84c242935ede1c3dbaed26b018a9e06ae2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime 1.3.0", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "atty", + "humantime 2.1.0", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime 2.1.0", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "environmental" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b91989ae21441195d7d9b9993a2f9295c7e1a8c96255d8b729accddc124797" + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash 0.8.0", + "impl-rlp", + "impl-serde 0.4.0", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81224dc661606574f5a0f28c9947d0ee1d93ff11c5f1c4e7272f52e8c0b5483c" +dependencies = [ + "ethbloom", + "fixed-hash 0.8.0", + "impl-rlp", + "impl-serde 0.4.0", + "primitive-types", + "uint", +] + +[[package]] +name = "event-listener" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" + +[[package]] +name = "exit-future" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" +dependencies = [ + "futures", +] + +[[package]] +name = "expander" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a718c0675c555c5f976fff4ea9e2c150fa06cefa201cadef87cfbf9324075881" +dependencies = [ + "blake3", + "fs-err", + "proc-macro2", + "quote", +] + +[[package]] +name = "expander" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3774182a5df13c3d1690311ad32fbe913feef26baba609fa2dd5f72042bd2ab6" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "fatality" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad875162843b0d046276327afe0136e9ed3a23d5a754210fb6f1f33610d39ab" +dependencies = [ + "fatality-proc-macro", + "thiserror", +] + +[[package]] +name = "fatality-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5aa1e3ae159e592ad222dc90c5acbad632b527779ba88486abe92782ab268bd" +dependencies = [ + "expander 0.0.4", + "indexmap", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "thiserror", +] + +[[package]] +name = "fdlimit" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4c9e43643f5a3be4ca5b67d26b98031ff9db6806c3440ae32e02e3ceac3f1b" +dependencies = [ + "libc", +] + +[[package]] +name = "ff" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2958d04124b9f27f175eaeb9a9f383d026098aa837eadd8ba22c11f13a05b9e" +dependencies = [ + "rand_core 0.6.3", + "subtle", +] + +[[package]] +name = "file-per-thread-logger" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fdbe0d94371f9ce939b555dd342d0686cc4c0cadbcd4b61d70af5ff97eb4126" +dependencies = [ + "env_logger 0.7.1", + "log", +] + +[[package]] +name = "filetime" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "windows-sys 0.36.1", +] + +[[package]] +name = "finality-grandpa" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b22349c6a11563a202d95772a68e0fcf56119e74ea8a2a19cf2301460fcd0df5" +dependencies = [ + "either", + "futures", + "futures-timer", + "log", + "num-traits", + "parity-scale-codec", + "parking_lot 0.12.1", + "scale-info", +] + +[[package]] +name = "finality-relay" +version = "0.1.0" +dependencies = [ + "async-std", + "async-trait", + "backoff 0.2.1", + "bp-header-chain", + "futures", + "log", + "num-traits", + "parking_lot 0.11.2", + "relay-utils", +] + +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixedbitset" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e" + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "libz-sys", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fork-tree" +version = "3.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + +[[package]] +name = "frame-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support", + "frame-system", + "linregress", + "log", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std", + "sp-storage", +] + +[[package]] +name = "frame-benchmarking-cli" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "Inflector", + "array-bytes", + "chrono", + "clap 4.0.26", + "comfy-table", + "frame-benchmarking", + "frame-support", + "frame-system", + "gethostname", + "handlebars", + "hash-db", + "itertools", + "kvdb", + "lazy_static", + "linked-hash-map", + "log", + "memory-db", + "parity-scale-codec", + "rand 0.8.5", + "rand_pcg 0.3.1", + "sc-block-builder", + "sc-cli", + "sc-client-api", + "sc-client-db", + "sc-executor", + "sc-service", + "sc-sysinfo", + "serde", + "serde_json", + "serde_nanos", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-database", + "sp-externalities", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "sp-state-machine", + "sp-storage", + "sp-trie", + "tempfile", + "thiserror", + "thousands", +] + +[[package]] +name = "frame-election-provider-solution-type" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-election-provider-support" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-election-provider-solution-type", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-npos-elections", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "frame-executive" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-tracing", +] + +[[package]] +name = "frame-metadata" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" +dependencies = [ + "cfg-if 1.0.0", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "frame-support" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "bitflags", + "frame-metadata", + "frame-support-procedural", + "impl-trait-for-tuples", + "k256", + "log", + "once_cell", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "smallvec", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-core-hashing-proc-macro", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-weights", + "tt-call", +] + +[[package]] +name = "frame-support-procedural" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "Inflector", + "cfg-expr", + "frame-support-procedural-tools", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-support-procedural-tools" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support-procedural-tools-derive", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-support-procedural-tools-derive" +version = "3.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-system" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-version", + "sp-weights", +] + +[[package]] +name = "frame-system-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "frame-system-rpc-runtime-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", + "sp-api", +] + +[[package]] +name = "frame-try-runtime" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "fs-err" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ebd3504ad6116843b8375ad70df74e7bfe83cac77a1f3fe73200c844d43bfe0" + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fs_extra" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" + +[[package]] +name = "futures-executor" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" + +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite 0.2.9", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-rustls" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" +dependencies = [ + "futures-io", + "rustls", + "webpki", +] + +[[package]] +name = "futures-sink" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" + +[[package]] +name = "futures-task" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite 0.2.9", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", +] + +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug 0.3.0", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "globset" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "gloo-timers" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "group" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" +dependencies = [ + "ff", + "rand_core 0.6.3", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9de88456263e249e241fcd211d3954e2c9b0ef7ccfc235a444eb367cae3689" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util 0.6.9", + "tracing", +] + +[[package]] +name = "handlebars" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d6a30320f094710245150395bc763ad23128d6a1ebbad7594dc4164b62c56b" +dependencies = [ + "log", + "pest", + "pest_derive", + "quick-error 2.0.1", + "serde", + "serde_json", +] + +[[package]] +name = "hash-db" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array 0.14.4", + "hmac 0.8.1", +] + +[[package]] +name = "honggfuzz" +version = "0.5.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea09577d948a98a5f59b7c891e274c4fb35ad52f67782b3d0cb53b9c05301f1" +dependencies = [ + "arbitrary", + "lazy_static", + "memmap", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.1", +] + +[[package]] +name = "http-body" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +dependencies = [ + "bytes", + "http", + "pin-project-lite 0.2.9", +] + +[[package]] +name = "httparse" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error 1.2.3", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 0.4.8", + "pin-project-lite 0.2.9", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +dependencies = [ + "http", + "hyper", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "if-addrs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "if-watch" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065c008e570a43c00de6aed9714035e5ea6a498c255323db9091722af6ee67dd" +dependencies = [ + "async-io", + "core-foundation", + "fnv", + "futures", + "if-addrs", + "ipnet", + "log", + "rtnetlink", + "system-configuration", + "windows", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", + "serde", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "integer-encoding" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90c11140ffea82edce8dcd74137ce9324ec24b3cf0175fc9d7e29164da9915b8" + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "io-lifetimes" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06" + +[[package]] +name = "ip_network" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1" + +[[package]] +name = "ipconfig" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723519edce41262b05d4143ceb95050e4c614f483e78e9fd9e39a8275a84ad98" +dependencies = [ + "socket2", + "widestring", + "winapi", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" + +[[package]] +name = "isahc" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d140e84730d325378912ede32d7cd53ef1542725503b3353e5ec8113c7c6f588" +dependencies = [ + "async-channel", + "castaway", + "crossbeam-utils", + "curl", + "curl-sys", + "encoding_rs", + "event-listener", + "futures-lite", + "http", + "log", + "mime", + "once_cell", + "polling", + "slab", + "sluice", + "tracing", + "tracing-futures", + "url", + "waker-fn", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonpath_lib" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61352ec23883402b7d30b3313c16cbabefb8907361c4eb669d990cbb87ceee5a" +dependencies = [ + "array_tool", + "env_logger 0.7.1", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "jsonrpsee" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bd0d559d5e679b1ab2f869b486a11182923863b1b3ee8b421763cdd707b783a" +dependencies = [ + "jsonrpsee-core", + "jsonrpsee-http-server", + "jsonrpsee-proc-macros", + "jsonrpsee-types", + "jsonrpsee-ws-client", + "jsonrpsee-ws-server", + "tracing", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8752740ecd374bcbf8b69f3e80b0327942df76f793f8d4e60d3355650c31fb74" +dependencies = [ + "futures-util", + "http", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util 0.7.1", + "tracing", + "webpki-roots", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3dc3e9cf2ba50b7b1d7d76a667619f82846caa39e8e8daa8a4962d74acaddca" +dependencies = [ + "anyhow", + "arrayvec 0.7.2", + "async-lock", + "async-trait", + "beef", + "futures-channel", + "futures-timer", + "futures-util", + "globset", + "http", + "hyper", + "jsonrpsee-types", + "lazy_static", + "parking_lot 0.12.1", + "rand 0.8.5", + "rustc-hash", + "serde", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tracing", + "tracing-futures", + "unicase", +] + +[[package]] +name = "jsonrpsee-http-server" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03802f0373a38c2420c70b5144742d800b509e2937edc4afb116434f07120117" +dependencies = [ + "futures-channel", + "futures-util", + "hyper", + "jsonrpsee-core", + "jsonrpsee-types", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-futures", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd67957d4280217247588ac86614ead007b301ca2fa9f19c19f880a536f029e3" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e290bba767401b646812f608c099b922d8142603c9e73a50fb192d3ac86f4a0d" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ee5feddd5188e62ac08fcf0e56478138e581509d4730f3f7be9b57dd402a4ff" +dependencies = [ + "http", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", +] + +[[package]] +name = "jsonrpsee-ws-server" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d488ba74fb369e5ab68926feb75a483458b88e768d44319f37e4ecad283c7325" +dependencies = [ + "futures-channel", + "futures-util", + "http", + "jsonrpsee-core", + "jsonrpsee-types", + "serde_json", + "soketto", + "tokio", + "tokio-stream", + "tokio-util 0.7.1", + "tracing", + "tracing-futures", +] + +[[package]] +name = "k256" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa", + "elliptic-curve", + "sec1", +] + +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" + +[[package]] +name = "kusama-runtime" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "beefy-primitives", + "bitvec", + "frame-election-provider-support", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "kusama-runtime-constants", + "log", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-bags-list", + "pallet-balances", + "pallet-bounties", + "pallet-child-bounties", + "pallet-collective", + "pallet-conviction-voting", + "pallet-democracy", + "pallet-election-provider-multi-phase", + "pallet-elections-phragmen", + "pallet-fast-unstake", + "pallet-gilt", + "pallet-grandpa", + "pallet-identity", + "pallet-im-online", + "pallet-indices", + "pallet-membership", + "pallet-multisig", + "pallet-nomination-pools", + "pallet-nomination-pools-runtime-api", + "pallet-offences", + "pallet-preimage", + "pallet-proxy", + "pallet-ranked-collective", + "pallet-recovery", + "pallet-referenda", + "pallet-scheduler", + "pallet-session", + "pallet-society", + "pallet-staking", + "pallet-staking-reward-fn", + "pallet-timestamp", + "pallet-tips", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-vesting", + "pallet-whitelist", + "pallet-xcm", + "parity-scale-codec", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "rustc-hex", + "scale-info", + "serde", + "serde_derive", + "smallvec", + "sp-api", + "sp-arithmetic", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-core", + "sp-inherents", + "sp-io", + "sp-mmr-primitives", + "sp-npos-elections", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "sp-transaction-pool", + "sp-version", + "static_assertions", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "kusama-runtime-constants" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "frame-support", + "polkadot-primitives", + "polkadot-runtime-common", + "smallvec", + "sp-runtime", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "kvdb" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585089ceadba0197ffe9af6740ab350b325e3c1f5fccfbc3522e0250c750409b" +dependencies = [ + "parity-util-mem", + "smallvec", +] + +[[package]] +name = "kvdb-memorydb" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40d109c87bfb7759edd2a49b2649c1afe25af785d930ad6a38479b4dc70dd873" +dependencies = [ + "kvdb", + "parity-util-mem", + "parking_lot 0.12.1", +] + +[[package]] +name = "kvdb-rocksdb" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c076cc2cdbac89b9910c853a36c957d3862a779f31c2661174222cefb49ee597" +dependencies = [ + "kvdb", + "log", + "num_cpus", + "parity-util-mem", + "parking_lot 0.12.1", + "regex", + "rocksdb", + "smallvec", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" + +[[package]] +name = "libloading" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "libm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" + +[[package]] +name = "libnghttp2-sys" +version = "0.1.7+1.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ed28aba195b38d5ff02b9170cbff627e336a20925e43b4945390401c5dc93f" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "libp2p" +version = "0.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec878fda12ebec479186b3914ebc48ff180fa4c51847e11a1a68bf65249e02c1" +dependencies = [ + "bytes", + "futures", + "futures-timer", + "getrandom 0.2.3", + "instant", + "lazy_static", + "libp2p-core", + "libp2p-dns", + "libp2p-identify", + "libp2p-kad", + "libp2p-mdns", + "libp2p-metrics", + "libp2p-mplex", + "libp2p-noise", + "libp2p-ping", + "libp2p-request-response", + "libp2p-swarm", + "libp2p-swarm-derive", + "libp2p-tcp", + "libp2p-wasm-ext", + "libp2p-websocket", + "libp2p-yamux", + "multiaddr", + "parking_lot 0.12.1", + "pin-project", + "smallvec", +] + +[[package]] +name = "libp2p-core" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799676bb0807c788065e57551c6527d461ad572162b0519d1958946ff9e0539d" +dependencies = [ + "asn1_der", + "bs58", + "ed25519-dalek", + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "lazy_static", + "log", + "multiaddr", + "multihash", + "multistream-select", + "parking_lot 0.12.1", + "pin-project", + "prost", + "prost-build", + "rand 0.8.5", + "rw-stream-sink", + "sha2 0.10.5", + "smallvec", + "thiserror", + "unsigned-varint", + "void", + "zeroize", +] + +[[package]] +name = "libp2p-dns" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2322c9fb40d99101def6a01612ee30500c89abbbecb6297b3cd252903a4c1720" +dependencies = [ + "async-std-resolver", + "futures", + "libp2p-core", + "log", + "parking_lot 0.12.1", + "smallvec", + "trust-dns-resolver", +] + +[[package]] +name = "libp2p-identify" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf9a121f699e8719bda2e6e9e9b6ddafc6cff4602471d6481c1067930ccb29b" +dependencies = [ + "asynchronous-codec", + "futures", + "futures-timer", + "libp2p-core", + "libp2p-swarm", + "log", + "lru 0.8.1", + "prost", + "prost-build", + "prost-codec", + "smallvec", + "thiserror", + "void", +] + +[[package]] +name = "libp2p-kad" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6721c200e2021f6c3fab8b6cf0272ead8912d871610ee194ebd628cecf428f22" +dependencies = [ + "arrayvec 0.7.2", + "asynchronous-codec", + "bytes", + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-swarm", + "log", + "prost", + "prost-build", + "rand 0.8.5", + "sha2 0.10.5", + "smallvec", + "thiserror", + "uint", + "unsigned-varint", + "void", +] + +[[package]] +name = "libp2p-mdns" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "761704e727f7d68d58d7bc2231eafae5fc1b9814de24290f126df09d4bd37a15" +dependencies = [ + "async-io", + "data-encoding", + "dns-parser", + "futures", + "if-watch", + "libp2p-core", + "libp2p-swarm", + "log", + "rand 0.8.5", + "smallvec", + "socket2", + "void", +] + +[[package]] +name = "libp2p-metrics" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee31b08e78b7b8bfd1c4204a9dd8a87b4fcdf6dafc57eb51701c1c264a81cb9" +dependencies = [ + "libp2p-core", + "libp2p-identify", + "libp2p-kad", + "libp2p-ping", + "libp2p-swarm", + "prometheus-client", +] + +[[package]] +name = "libp2p-mplex" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692664acfd98652de739a8acbb0a0d670f1d67190a49be6b4395e22c37337d89" +dependencies = [ + "asynchronous-codec", + "bytes", + "futures", + "libp2p-core", + "log", + "nohash-hasher", + "parking_lot 0.12.1", + "rand 0.8.5", + "smallvec", + "unsigned-varint", +] + +[[package]] +name = "libp2p-noise" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048155686bd81fe6cb5efdef0c6290f25ad32a0a42e8f4f72625cf6a505a206f" +dependencies = [ + "bytes", + "curve25519-dalek 3.2.0", + "futures", + "lazy_static", + "libp2p-core", + "log", + "prost", + "prost-build", + "rand 0.8.5", + "sha2 0.10.5", + "snow", + "static_assertions", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "libp2p-ping" +version = "0.40.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7228b9318d34689521349a86eb39a3c3a802c9efc99a0568062ffb80913e3f91" +dependencies = [ + "futures", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-swarm", + "log", + "rand 0.8.5", + "void", +] + +[[package]] +name = "libp2p-request-response" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8827af16a017b65311a410bb626205a9ad92ec0473967618425039fa5231adc1" +dependencies = [ + "async-trait", + "bytes", + "futures", + "instant", + "libp2p-core", + "libp2p-swarm", + "log", + "rand 0.8.5", + "smallvec", + "unsigned-varint", +] + +[[package]] +name = "libp2p-swarm" +version = "0.40.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46d13df7c37807965d82930c0e4b04a659efcb6cca237373b206043db5398ecf" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-core", + "log", + "pin-project", + "rand 0.8.5", + "smallvec", + "thiserror", + "void", +] + +[[package]] +name = "libp2p-swarm-derive" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eddc4497a8b5a506013c40e8189864f9c3a00db2b25671f428ae9007f3ba32" +dependencies = [ + "heck 0.4.0", + "quote", + "syn", +] + +[[package]] +name = "libp2p-tcp" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9839d96761491c6d3e238e70554b856956fca0ab60feb9de2cd08eed4473fa92" +dependencies = [ + "async-io", + "futures", + "futures-timer", + "if-watch", + "libc", + "libp2p-core", + "log", + "socket2", +] + +[[package]] +name = "libp2p-wasm-ext" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b5b8e7a73e379e47b1b77f8a82c4721e97eca01abcd18e9cd91a23ca6ce97" +dependencies = [ + "futures", + "js-sys", + "libp2p-core", + "parity-send-wrapper", + "wasm-bindgen", + "wasm-bindgen-futures", +] + +[[package]] +name = "libp2p-websocket" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3758ae6f89b2531a24b6d9f5776bda6a626b60a57600d7185d43dfa75ca5ecc4" +dependencies = [ + "either", + "futures", + "futures-rustls", + "libp2p-core", + "log", + "parking_lot 0.12.1", + "quicksink", + "rw-stream-sink", + "soketto", + "url", + "webpki-roots", +] + +[[package]] +name = "libp2p-yamux" +version = "0.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6874d66543c4f7e26e3b8ca9a6bead351563a13ab4fafd43c7927f7c0d6c12" +dependencies = [ + "futures", + "libp2p-core", + "log", + "parking_lot 0.12.1", + "thiserror", + "yamux", +] + +[[package]] +name = "librocksdb-sys" +version = "0.8.0+7.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611804e4666a25136fcc5f8cf425ab4d26c7f74ea245ffe92ea23b85b6420b5d" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "glob", + "libc", + "libz-sys", + "tikv-jemalloc-sys", +] + +[[package]] +name = "libsecp256k1" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0452aac8bab02242429380e9b2f94ea20cea2b37e2c1777a1358799bbe97f37" +dependencies = [ + "arrayref", + "base64", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.8", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libz-sys" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "linked_hash_set" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "linregress" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c601a85f5ecd1aba625247bca0031585fb1c446461b142878a16f8245ddeb8" +dependencies = [ + "nalgebra", + "statrs", +] + +[[package]] +name = "linux-raw-sys" +version = "0.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" + +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if 1.0.0", + "value-bag", +] + +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "lru" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "lz4" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" +dependencies = [ + "libc", + "lz4-sys", +] + +[[package]] +name = "lz4-sys" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "matrixmultiply" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add85d4dd35074e6fedc608f8c8f513a3548619a9024b751949ef0e8e45a4d84" +dependencies = [ + "rawpointer", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memfd" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "480b5a5de855d11ff13195950bdc8b98b5e942ef47afc447f6615cdcc4e15d80" +dependencies = [ + "rustix", +] + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "memmap2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4647a11b578fead29cdbb34d4adef8dd3dc35b876c9c6d5240d83f205abfe96e" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memory-db" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac11bb793c28fa095b7554466f53b3a60a2cd002afdac01bcf135cbd73a269" +dependencies = [ + "hash-db", + "hashbrown", + "parity-util-mem", +] + +[[package]] +name = "memory-lru" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce95ae042940bad7e312857b929ee3d11b8f799a80cb7b9c7ec5125516906395" +dependencies = [ + "lru 0.8.1", +] + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "merlin" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "messages-relay" +version = "0.1.0" +dependencies = [ + "async-std", + "async-trait", + "bp-messages", + "bp-runtime", + "finality-relay", + "futures", + "hex", + "log", + "num-traits", + "parking_lot 0.11.2", + "relay-utils", + "sp-arithmetic", +] + +[[package]] +name = "mick-jaeger" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69672161530e8aeca1d1400fbf3f1a1747ff60ea604265a4e906c2442df20532" +dependencies = [ + "futures", + "rand 0.8.5", + "thrift", +] + +[[package]] +name = "millau-bridge-node" +version = "0.1.0" +dependencies = [ + "beefy-gadget", + "beefy-gadget-rpc", + "beefy-primitives", + "clap 4.0.26", + "frame-benchmarking", + "frame-benchmarking-cli", + "jsonrpsee", + "millau-runtime", + "node-inspect", + "pallet-mmr-rpc", + "pallet-transaction-payment-rpc", + "sc-basic-authorship", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-consensus-aura", + "sc-executor", + "sc-finality-grandpa", + "sc-finality-grandpa-rpc", + "sc-keystore", + "sc-rpc", + "sc-service", + "sc-telemetry", + "sc-transaction-pool", + "serde_json", + "sp-consensus", + "sp-consensus-aura", + "sp-core", + "sp-finality-grandpa", + "sp-runtime", + "sp-timestamp", + "substrate-build-script-utils", + "substrate-frame-rpc-system", +] + +[[package]] +name = "millau-runtime" +version = "0.1.0" +dependencies = [ + "beefy-primitives", + "bp-messages", + "bp-millau", + "bp-polkadot-core", + "bp-relayers", + "bp-rialto", + "bp-rialto-parachain", + "bp-runtime", + "bp-westend", + "bridge-runtime-common", + "env_logger 0.8.4", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "hex-literal", + "log", + "pallet-aura", + "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-bridge-relayers", + "pallet-grandpa", + "pallet-mmr", + "pallet-randomness-collective-flip", + "pallet-session", + "pallet-shift-session-manager", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-io", + "sp-mmr-primitives", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "static_assertions", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.42.0", +] + +[[package]] +name = "mockall" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e4a1c770583dac7ab5e2f6c139153b783a53a1bbee9729613f193e59828326" +dependencies = [ + "cfg-if 1.0.0", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832663583d5fa284ca8810bf7015e46c9fff9622d3cf34bd1eea5003fec06dd0" +dependencies = [ + "cfg-if 1.0.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "multiaddr" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c580bfdd8803cce319b047d239559a22f809094aaea4ac13902a1fdcfcd4261" +dependencies = [ + "arrayref", + "bs58", + "byteorder", + "data-encoding", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +dependencies = [ + "blake2b_simd", + "blake2s_simd", + "blake3", + "core2", + "digest 0.10.3", + "multihash-derive", + "sha2 0.10.5", + "sha3", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "multistream-select" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bc41247ec209813e2fd414d6e16b9d94297dacf3cd613fa6ef09cd4d9755c10" +dependencies = [ + "bytes", + "futures", + "log", + "pin-project", + "smallvec", + "unsigned-varint", +] + +[[package]] +name = "nalgebra" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "462fffe4002f4f2e1f6a9dcf12cc1a6fc0e15989014efc02a941d3e0f5dc2120" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational 0.4.0", + "num-traits", + "rand 0.8.5", + "rand_distr", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "names" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d66043b25d4a6cccb23619d10c19c25304b355a7dccd4a8e11423dd2382146" +dependencies = [ + "rand 0.8.5", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" + +[[package]] +name = "netlink-packet-core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" +dependencies = [ + "anyhow", + "byteorder", + "libc", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" +dependencies = [ + "anyhow", + "bitflags", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25af9cf0dc55498b7bd94a1508af7a78706aa0ab715a73c5169273e03c84845e" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror", +] + +[[package]] +name = "netlink-proto" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" +dependencies = [ + "bytes", + "futures", + "log", + "netlink-packet-core", + "netlink-sys", + "thiserror", + "tokio", +] + +[[package]] +name = "netlink-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92b654097027250401127914afb37cb1f311df6610a9891ff07a757e94199027" +dependencies = [ + "async-io", + "bytes", + "futures", + "libc", + "log", +] + +[[package]] +name = "nix" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "libc", +] + +[[package]] +name = "node-inspect" +version = "0.9.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "clap 4.0.26", + "parity-scale-codec", + "sc-cli", + "sc-client-api", + "sc-executor", + "sc-service", + "sp-blockchain", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +dependencies = [ + "memchr", + "minimal-lexical", + "version_check", +] + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-format" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465" +dependencies = [ + "arrayvec 0.4.12", + "itoa 0.4.8", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-bigint 0.2.6", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +dependencies = [ + "autocfg", + "num-bigint 0.4.3", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ba99ba6393e2c3734791401b66902d981cb03bf190af674ca69949b6d5fb15" +dependencies = [ + "libc", +] + +[[package]] +name = "object" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +dependencies = [ + "memchr", +] + +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "crc32fast", + "hashbrown", + "indexmap", + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl-probe" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" + +[[package]] +name = "openssl-sys" +version = "0.9.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df13d165e607909b363a4757a6f133f8a818a74e9d3a98d09c6128e15fa4c73" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "orchestra" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aab54694ddaa8a9b703724c6ef04272b2d27bc32d2c855aae5cdd1857216b43" +dependencies = [ + "async-trait", + "dyn-clonable", + "futures", + "futures-timer", + "orchestra-proc-macro", + "pin-project", + "prioritized-metered-channel", + "thiserror", + "tracing", +] + +[[package]] +name = "orchestra-proc-macro" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a702b2f6bf592b3eb06c00d80d05afaf7a8eff6b41bb361e397d799acc21b45a" +dependencies = [ + "expander 0.0.6", + "itertools", + "petgraph", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ordered-float" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" +dependencies = [ + "num-traits", +] + +[[package]] +name = "os_str_bytes" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" + +[[package]] +name = "pallet-aura" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support", + "frame-system", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-aura", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-authority-discovery" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support", + "frame-system", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-authority-discovery", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-authorship" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-authorship", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-babe" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-babe", + "sp-consensus-vrf", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-bags-list" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-tracing", +] + +[[package]] +name = "pallet-balances" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-beefy" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "beefy-primitives", + "frame-support", + "frame-system", + "pallet-session", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-beefy-mmr" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "array-bytes", + "beefy-merkle-tree", + "beefy-primitives", + "frame-support", + "frame-system", + "log", + "pallet-beefy", + "pallet-mmr", + "pallet-session", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-bounties" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-treasury", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-bridge-beefy" +version = "0.1.0" +dependencies = [ + "beefy-merkle-tree", + "beefy-primitives", + "bp-beefy", + "bp-runtime", + "bp-test-utils", + "ckb-merkle-mountain-range", + "frame-support", + "frame-system", + "log", + "pallet-beefy-mmr", + "pallet-mmr", + "parity-scale-codec", + "primitive-types", + "rand 0.8.5", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-bridge-grandpa" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-runtime", + "bp-test-utils", + "finality-grandpa", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-finality-grandpa", + "sp-io", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "pallet-bridge-messages" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-runtime", + "bp-test-utils", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "num-traits", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-bridge-parachains" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-parachains", + "bp-polkadot-core", + "bp-runtime", + "bp-test-utils", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-bridge-grandpa", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "pallet-bridge-relayers" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-relayers", + "bp-runtime", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "pallet-bridge-messages", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-child-bounties" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-bounties", + "pallet-treasury", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-collective" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-conviction-voting" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "assert_matches", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-democracy" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-election-provider-multi-phase" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-election-provider-support-benchmarking", + "parity-scale-codec", + "rand 0.7.3", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-std", + "static_assertions", + "strum 0.24.1", +] + +[[package]] +name = "pallet-election-provider-support-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-system", + "parity-scale-codec", + "sp-npos-elections", + "sp-runtime", +] + +[[package]] +name = "pallet-elections-phragmen" +version = "5.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-fast-unstake" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-gilt" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-grandpa" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-core", + "sp-finality-grandpa", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-identity" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "enumflags2", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-im-online" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-indices" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-membership" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-mmr" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "ckb-merkle-mountain-range", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-mmr-primitives", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-mmr-rpc" +version = "3.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "jsonrpsee", + "parity-scale-codec", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-mmr-primitives", + "sp-runtime", +] + +[[package]] +name = "pallet-multisig" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-nomination-pools" +version = "1.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-nomination-pools-runtime-api" +version = "1.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", + "sp-api", + "sp-std", +] + +[[package]] +name = "pallet-offences" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-preimage" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-proxy" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-randomness-collective-flip" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "safe-mix", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-ranked-collective" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-recovery" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-referenda" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-arithmetic", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-scheduler" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-session" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "sp-trie", +] + +[[package]] +name = "pallet-shift-session-manager" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-society" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "rand_chacha 0.2.2", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-staking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "scale-info", + "serde", + "sp-application-crypto", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-staking-reward-curve" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pallet-staking-reward-fn" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "log", + "sp-arithmetic", +] + +[[package]] +name = "pallet-sudo" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-timestamp" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "pallet-tips" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-treasury", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-transaction-payment" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-transaction-payment-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "jsonrpsee", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", +] + +[[package]] +name = "pallet-transaction-payment-rpc-runtime-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "pallet-transaction-payment", + "parity-scale-codec", + "sp-api", + "sp-runtime", +] + +[[package]] +name = "pallet-treasury" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-utility" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-vesting" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-whitelist" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-xcm" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", + "xcm-executor", +] + +[[package]] +name = "parachain-info" +version = "0.1.0" +source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "parachains-relay" +version = "0.1.0" +dependencies = [ + "async-std", + "async-trait", + "bp-parachains", + "bp-polkadot-core", + "futures", + "log", + "parity-scale-codec", + "relay-substrate-client", + "relay-utils", + "sp-core", +] + +[[package]] +name = "parity-db" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8fdb726a43661fa54b43e7114e6b88b2289cae388eb3ad766d9d1754d83fce" +dependencies = [ + "blake2-rfc", + "crc32fast", + "fs2", + "hex", + "libc", + "log", + "lz4", + "memmap2", + "parking_lot 0.12.1", + "rand 0.8.5", + "snap", +] + +[[package]] +name = "parity-scale-codec" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366e44391a8af4cfd6002ef6ba072bae071a96aafca98d7d448a34c5dca38b6a" +dependencies = [ + "arrayvec 0.7.2", + "bitvec", + "byte-slice-cast", + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9299338969a3d2f491d65f140b00ddec470858402f888af98e8642fb5e8965cd" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "parity-send-wrapper" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" + +[[package]] +name = "parity-util-mem" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d32c34f4f5ca7f9196001c0aba5a1f9a5a12382c8944b8b0f90233282d1e8f8" +dependencies = [ + "cfg-if 1.0.0", + "ethereum-types", + "hashbrown", + "impl-trait-for-tuples", + "lru 0.8.1", + "parity-util-mem-derive", + "parking_lot 0.12.1", + "primitive-types", + "smallvec", + "winapi", +] + +[[package]] +name = "parity-util-mem-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" +dependencies = [ + "proc-macro2", + "syn", + "synstructure", +] + +[[package]] +name = "parity-wasm" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.5", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.1", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.32.0", +] + +[[package]] +name = "paste" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" + +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac 0.8.0", +] + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac 0.11.1", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1 0.8.2", +] + +[[package]] +name = "petgraph" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +dependencies = [ + "der", + "spki", + "zeroize", +] + +[[package]] +name = "pkg-config" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" + +[[package]] +name = "platforms" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d0eef3571242013a0d5dc84861c3ae4a652e56e12adf8bdc26ff5f8cb34c94" + +[[package]] +name = "polkadot-approval-distribution" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "futures", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand 0.8.5", + "tracing-gum", +] + +[[package]] +name = "polkadot-availability-bitfield-distribution" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "futures", + "polkadot-node-network-protocol", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand 0.8.5", + "tracing-gum", +] + +[[package]] +name = "polkadot-availability-distribution" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "derive_more", + "fatality", + "futures", + "lru 0.8.1", + "parity-scale-codec", + "polkadot-erasure-coding", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand 0.8.5", + "sp-core", + "sp-keystore", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-availability-recovery" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "fatality", + "futures", + "lru 0.8.1", + "parity-scale-codec", + "polkadot-erasure-coding", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand 0.8.5", + "sc-network", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-cli" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "clap 4.0.26", + "frame-benchmarking-cli", + "futures", + "log", + "polkadot-client", + "polkadot-node-core-pvf", + "polkadot-node-metrics", + "polkadot-performance-test", + "polkadot-service", + "sc-cli", + "sc-service", + "sc-sysinfo", + "sc-tracing", + "sp-core", + "sp-keyring", + "sp-trie", + "substrate-build-script-utils", + "thiserror", + "try-runtime-cli", +] + +[[package]] +name = "polkadot-client" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "beefy-primitives", + "frame-benchmarking", + "frame-benchmarking-cli", + "frame-system", + "frame-system-rpc-runtime-api", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "polkadot-core-primitives", + "polkadot-node-core-parachains-inherent", + "polkadot-primitives", + "polkadot-runtime", + "polkadot-runtime-common", + "sc-client-api", + "sc-consensus", + "sc-executor", + "sc-service", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-core", + "sp-finality-grandpa", + "sp-inherents", + "sp-keyring", + "sp-mmr-primitives", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-storage", + "sp-timestamp", + "sp-transaction-pool", +] + +[[package]] +name = "polkadot-collator-protocol" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "always-assert", + "bitvec", + "fatality", + "futures", + "futures-timer", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sp-core", + "sp-keystore", + "sp-runtime", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-core-primitives" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "parity-scale-codec", + "parity-util-mem", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "polkadot-dispute-distribution" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "derive_more", + "fatality", + "futures", + "futures-timer", + "indexmap", + "lru 0.8.1", + "parity-scale-codec", + "polkadot-erasure-coding", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sc-network", + "sp-application-crypto", + "sp-keystore", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-erasure-coding" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "parity-scale-codec", + "polkadot-node-primitives", + "polkadot-primitives", + "reed-solomon-novelpoly", + "sp-core", + "sp-trie", + "thiserror", +] + +[[package]] +name = "polkadot-gossip-support" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "futures", + "futures-timer", + "polkadot-node-network-protocol", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand 0.8.5", + "rand_chacha 0.3.1", + "sc-network", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "tracing-gum", +] + +[[package]] +name = "polkadot-network-bridge" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "always-assert", + "async-trait", + "bytes", + "fatality", + "futures", + "parity-scale-codec", + "parking_lot 0.12.1", + "polkadot-node-network-protocol", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-primitives", + "sc-network", + "sc-network-common", + "sp-consensus", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-collation-generation" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "futures", + "parity-scale-codec", + "polkadot-erasure-coding", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sp-core", + "sp-maybe-compressed-blob", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-approval-voting" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "bitvec", + "derive_more", + "futures", + "futures-timer", + "kvdb", + "lru 0.8.1", + "merlin", + "parity-scale-codec", + "polkadot-node-jaeger", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-primitives", + "sc-keystore", + "schnorrkel", + "sp-application-crypto", + "sp-consensus", + "sp-consensus-slots", + "sp-runtime", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-av-store" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "bitvec", + "futures", + "futures-timer", + "kvdb", + "parity-scale-codec", + "polkadot-erasure-coding", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-primitives", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-backing" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "bitvec", + "fatality", + "futures", + "polkadot-erasure-coding", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "polkadot-statement-table", + "sp-keystore", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-bitfield-signing" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "futures", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sp-keystore", + "thiserror", + "tracing-gum", + "wasm-timer", +] + +[[package]] +name = "polkadot-node-core-candidate-validation" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "async-trait", + "futures", + "parity-scale-codec", + "polkadot-node-core-pvf", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-parachain", + "polkadot-primitives", + "sp-maybe-compressed-blob", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-chain-api" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "futures", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sc-client-api", + "sc-consensus-babe", + "sp-blockchain", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-chain-selection" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "futures", + "futures-timer", + "kvdb", + "parity-scale-codec", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-dispute-coordinator" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "fatality", + "futures", + "kvdb", + "lru 0.8.1", + "parity-scale-codec", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sc-keystore", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-parachains-inherent" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "polkadot-node-subsystem", + "polkadot-primitives", + "sp-blockchain", + "sp-inherents", + "sp-runtime", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-provisioner" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "bitvec", + "fatality", + "futures", + "futures-timer", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand 0.8.5", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-pvf" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "always-assert", + "assert_matches", + "async-process", + "async-std", + "futures", + "futures-timer", + "parity-scale-codec", + "pin-project", + "polkadot-core-primitives", + "polkadot-node-metrics", + "polkadot-parachain", + "rand 0.8.5", + "rayon", + "sc-executor", + "sc-executor-common", + "sc-executor-wasmtime", + "slotmap", + "sp-core", + "sp-externalities", + "sp-io", + "sp-maybe-compressed-blob", + "sp-tracing", + "sp-wasm-interface", + "tempfile", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-pvf-checker" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "futures", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-primitives", + "sp-keystore", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-runtime-api" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "futures", + "memory-lru", + "parity-util-mem", + "polkadot-node-subsystem", + "polkadot-node-subsystem-types", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sp-consensus-babe", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-jaeger" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "async-std", + "lazy_static", + "log", + "mick-jaeger", + "parity-scale-codec", + "parking_lot 0.12.1", + "polkadot-node-primitives", + "polkadot-primitives", + "sc-network", + "sp-core", + "thiserror", +] + +[[package]] +name = "polkadot-node-metrics" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "bs58", + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "polkadot-primitives", + "prioritized-metered-channel", + "sc-cli", + "sc-service", + "sc-tracing", + "substrate-prometheus-endpoint", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-network-protocol" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "async-trait", + "derive_more", + "fatality", + "futures", + "hex", + "parity-scale-codec", + "polkadot-node-jaeger", + "polkadot-node-primitives", + "polkadot-primitives", + "rand 0.8.5", + "sc-authority-discovery", + "sc-network", + "sc-network-common", + "strum 0.24.1", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-primitives" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "bounded-vec", + "futures", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "schnorrkel", + "serde", + "sp-application-crypto", + "sp-consensus-babe", + "sp-consensus-vrf", + "sp-core", + "sp-keystore", + "sp-maybe-compressed-blob", + "thiserror", + "zstd", +] + +[[package]] +name = "polkadot-node-subsystem" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "polkadot-node-jaeger", + "polkadot-node-subsystem-types", + "polkadot-overseer", +] + +[[package]] +name = "polkadot-node-subsystem-types" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "async-trait", + "derive_more", + "futures", + "orchestra", + "polkadot-node-jaeger", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-primitives", + "polkadot-statement-table", + "sc-network", + "smallvec", + "sp-api", + "sp-authority-discovery", + "sp-consensus-babe", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "polkadot-node-subsystem-util" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "async-trait", + "derive_more", + "fatality", + "futures", + "itertools", + "kvdb", + "lru 0.8.1", + "parity-db", + "parity-scale-codec", + "parity-util-mem", + "parking_lot 0.11.2", + "pin-project", + "polkadot-node-jaeger", + "polkadot-node-metrics", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-overseer", + "polkadot-primitives", + "prioritized-metered-channel", + "rand 0.8.5", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-overseer" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "lru 0.8.1", + "orchestra", + "parity-util-mem", + "parking_lot 0.12.1", + "polkadot-node-metrics", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem-types", + "polkadot-primitives", + "sc-client-api", + "sp-api", + "sp-core", + "tracing-gum", +] + +[[package]] +name = "polkadot-parachain" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "derive_more", + "frame-support", + "parity-scale-codec", + "parity-util-mem", + "polkadot-core-primitives", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "polkadot-performance-test" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "env_logger 0.9.0", + "kusama-runtime", + "log", + "polkadot-erasure-coding", + "polkadot-node-core-pvf", + "polkadot-node-primitives", + "quote", + "thiserror", +] + +[[package]] +name = "polkadot-primitives" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "bitvec", + "frame-system", + "hex-literal", + "parity-scale-codec", + "parity-util-mem", + "polkadot-core-primitives", + "polkadot-parachain", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-authority-discovery", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-staking", + "sp-std", + "sp-trie", + "sp-version", +] + +[[package]] +name = "polkadot-rpc" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "beefy-gadget", + "beefy-gadget-rpc", + "jsonrpsee", + "pallet-mmr-rpc", + "pallet-transaction-payment-rpc", + "polkadot-primitives", + "sc-chain-spec", + "sc-client-api", + "sc-consensus-babe", + "sc-consensus-babe-rpc", + "sc-consensus-epochs", + "sc-finality-grandpa", + "sc-finality-grandpa-rpc", + "sc-rpc", + "sc-sync-state-rpc", + "sc-transaction-pool-api", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-keystore", + "sp-runtime", + "substrate-frame-rpc-system", + "substrate-state-trie-migration-rpc", +] + +[[package]] +name = "polkadot-runtime" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "beefy-primitives", + "bitvec", + "frame-election-provider-support", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "log", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-bags-list", + "pallet-balances", + "pallet-bounties", + "pallet-child-bounties", + "pallet-collective", + "pallet-democracy", + "pallet-election-provider-multi-phase", + "pallet-elections-phragmen", + "pallet-fast-unstake", + "pallet-grandpa", + "pallet-identity", + "pallet-im-online", + "pallet-indices", + "pallet-membership", + "pallet-multisig", + "pallet-nomination-pools", + "pallet-nomination-pools-runtime-api", + "pallet-offences", + "pallet-preimage", + "pallet-proxy", + "pallet-scheduler", + "pallet-session", + "pallet-staking", + "pallet-staking-reward-curve", + "pallet-timestamp", + "pallet-tips", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-vesting", + "pallet-xcm", + "parity-scale-codec", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-constants", + "polkadot-runtime-parachains", + "rustc-hex", + "scale-info", + "serde", + "serde_derive", + "smallvec", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-core", + "sp-inherents", + "sp-io", + "sp-mmr-primitives", + "sp-npos-elections", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "sp-transaction-pool", + "sp-version", + "static_assertions", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "polkadot-runtime-common" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "beefy-primitives", + "bitvec", + "frame-election-provider-support", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "libsecp256k1", + "log", + "pallet-authorship", + "pallet-bags-list", + "pallet-balances", + "pallet-beefy-mmr", + "pallet-election-provider-multi-phase", + "pallet-session", + "pallet-staking", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-treasury", + "pallet-vesting", + "parity-scale-codec", + "polkadot-primitives", + "polkadot-runtime-parachains", + "rustc-hex", + "scale-info", + "serde", + "serde_derive", + "slot-range-helper", + "sp-api", + "sp-core", + "sp-inherents", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "static_assertions", + "xcm", +] + +[[package]] +name = "polkadot-runtime-constants" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "frame-support", + "polkadot-primitives", + "polkadot-runtime-common", + "smallvec", + "sp-runtime", +] + +[[package]] +name = "polkadot-runtime-metrics" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "bs58", + "parity-scale-codec", + "polkadot-primitives", + "sp-std", + "sp-tracing", +] + +[[package]] +name = "polkadot-runtime-parachains" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "bitflags", + "bitvec", + "derive_more", + "frame-support", + "frame-system", + "log", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-balances", + "pallet-session", + "pallet-staking", + "pallet-timestamp", + "pallet-vesting", + "parity-scale-codec", + "polkadot-primitives", + "polkadot-runtime-metrics", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rustc-hex", + "scale-info", + "serde", + "sp-api", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "xcm", + "xcm-executor", +] + +[[package]] +name = "polkadot-service" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "async-trait", + "beefy-gadget", + "beefy-primitives", + "frame-support", + "frame-system-rpc-runtime-api", + "futures", + "hex-literal", + "kvdb", + "kvdb-rocksdb", + "lru 0.8.1", + "pallet-babe", + "pallet-im-online", + "pallet-staking", + "pallet-transaction-payment-rpc-runtime-api", + "parity-db", + "polkadot-approval-distribution", + "polkadot-availability-bitfield-distribution", + "polkadot-availability-distribution", + "polkadot-availability-recovery", + "polkadot-client", + "polkadot-collator-protocol", + "polkadot-dispute-distribution", + "polkadot-gossip-support", + "polkadot-network-bridge", + "polkadot-node-collation-generation", + "polkadot-node-core-approval-voting", + "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-provisioner", + "polkadot-node-core-pvf-checker", + "polkadot-node-core-runtime-api", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-types", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-parachain", + "polkadot-primitives", + "polkadot-rpc", + "polkadot-runtime", + "polkadot-runtime-constants", + "polkadot-runtime-parachains", + "polkadot-statement-distribution", + "sc-authority-discovery", + "sc-basic-authorship", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-client-db", + "sc-consensus", + "sc-consensus-babe", + "sc-consensus-slots", + "sc-executor", + "sc-finality-grandpa", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-offchain", + "sc-service", + "sc-sync-state-rpc", + "sc-sysinfo", + "sc-telemetry", + "sc-transaction-pool", + "serde", + "serde_json", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-core", + "sp-finality-grandpa", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-state-machine", + "sp-storage", + "sp-timestamp", + "sp-transaction-pool", + "sp-trie", + "substrate-prometheus-endpoint", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-statement-distribution" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "arrayvec 0.5.2", + "fatality", + "futures", + "indexmap", + "parity-scale-codec", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sp-keystore", + "sp-staking", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-statement-table" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "parity-scale-codec", + "polkadot-primitives", + "sp-core", +] + +[[package]] +name = "polling" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "log", + "wepoll-ffi", + "winapi", +] + +[[package]] +name = "poly1305" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +dependencies = [ + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" + +[[package]] +name = "predicates" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6bd09a7f7e68f3f0bf710fb7ab9c4615a488b58b5f653382a687701e458c92" +dependencies = [ + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" + +[[package]] +name = "predicates-tree" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "primitive-types" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cfd65aea0c5fa0bfcc7c9e7ca828c921ef778f43d325325ec84bda371bfa75a" +dependencies = [ + "fixed-hash 0.8.0", + "impl-codec", + "impl-rlp", + "impl-serde 0.4.0", + "scale-info", + "uint", +] + +[[package]] +name = "prioritized-metered-channel" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382698e48a268c832d0b181ed438374a6bb708a82a8ca273bb0f61c74cf209c4" +dependencies = [ + "coarsetime", + "crossbeam-queue", + "derive_more", + "futures", + "futures-timer", + "nanorand", + "thiserror", + "tracing", +] + +[[package]] +name = "proc-macro-crate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +dependencies = [ + "once_cell", + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prometheus" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f64969ffd5dd8f39bd57a68ac53c163a095ed9d0fb707146da1b27025a3504" +dependencies = [ + "cfg-if 1.0.0", + "fnv", + "lazy_static", + "memchr", + "parking_lot 0.11.2", + "thiserror", +] + +[[package]] +name = "prometheus-client" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cd1b99916654a69008fd66b4f9397fbe08e6e51dfe23d4417acf5d3b8cb87c" +dependencies = [ + "dtoa", + "itoa 1.0.1", + "parking_lot 0.12.1", + "prometheus-client-derive-text-encode", +] + +[[package]] +name = "prometheus-client-derive-text-encode" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "399c3c31cdec40583bb68f0b18403400d01ec4289c383aa047560439952c4dd7" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f835c582e6bd972ba8347313300219fed5bfa52caf175298d860b61ff6069bb" +dependencies = [ + "bytes", + "heck 0.4.0", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prost", + "prost-types", + "regex", + "tempfile", + "which", +] + +[[package]] +name = "prost-codec" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "011ae9ff8359df7915f97302d591cdd9e0e27fbd5a4ddc5bd13b71079bb20987" +dependencies = [ + "asynchronous-codec", + "bytes", + "prost", + "thiserror", + "unsigned-varint", +] + +[[package]] +name = "prost-derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7345d5f0e08c0536d7ac7229952590239e77abf0a0100a1b1d890add6ea96364" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dfaa718ad76a44b3415e6c4d53b17c8f99160dcb3a99b10470fce8ad43f6e3e" +dependencies = [ + "bytes", + "prost", +] + +[[package]] +name = "psm" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd136ff4382c4753fc061cb9e4712ab2af263376b95bbd5bd8cd50c020b78e69" +dependencies = [ + "cc", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quicksink" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77de3c815e5a160b1539c6592796801df2043ae35e123b46d73380cfa57af858" +dependencies = [ + "futures-core", + "futures-sink", + "pin-project-lite 0.1.12", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg 0.2.1", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom 0.2.3", +] + +[[package]] +name = "rand_distr" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964d548f8e7d12e102ef183a0de7e98180c9f8729f555897a857b96e48122d2f" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" +dependencies = [ + "rand_core 0.6.3", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +dependencies = [ + "getrandom 0.2.3", + "redox_syscall", +] + +[[package]] +name = "reed-solomon-novelpoly" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd8f48b2066e9f69ab192797d66da804d1935bf22763204ed3675740cb0f221" +dependencies = [ + "derive_more", + "fs-err", + "itertools", + "static_init 0.5.2", + "thiserror", +] + +[[package]] +name = "ref-cast" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300f2a835d808734ee295d45007adacb9ebb29dd3ae2424acfa17930cae541da" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c38e3aecd2b21cb3959637b883bb3714bc7e43f0268b9a29d3743ee3e55cdd2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "regalloc2" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43a209257d978ef079f3d446331d0f1794f5e0fc19b306a199983857833a779" +dependencies = [ + "fxhash", + "log", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "relay-bridge-hub-rococo-client" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-rococo", + "bp-bridge-hub-wococo", + "bp-header-chain", + "bp-messages", + "bp-polkadot-core", + "bp-runtime", + "bridge-runtime-common", + "parity-scale-codec", + "relay-substrate-client", + "scale-info", + "sp-core", + "sp-finality-grandpa", + "sp-runtime", +] + +[[package]] +name = "relay-bridge-hub-wococo-client" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-wococo", + "bp-messages", + "parity-scale-codec", + "relay-bridge-hub-rococo-client", + "relay-substrate-client", + "scale-info", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "relay-kusama-client" +version = "0.1.0" +dependencies = [ + "bp-kusama", + "frame-support", + "relay-substrate-client", + "relay-utils", + "sp-core", +] + +[[package]] +name = "relay-millau-client" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-millau", + "frame-support", + "frame-system", + "millau-runtime", + "pallet-transaction-payment", + "parity-scale-codec", + "relay-substrate-client", + "relay-utils", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "relay-polkadot-client" +version = "0.1.0" +dependencies = [ + "bp-polkadot", + "frame-support", + "relay-substrate-client", + "relay-utils", + "sp-core", +] + +[[package]] +name = "relay-rialto-client" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-rialto", + "frame-support", + "frame-system", + "pallet-transaction-payment", + "parity-scale-codec", + "relay-substrate-client", + "relay-utils", + "rialto-runtime", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "relay-rialto-parachain-client" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-rialto-parachain", + "frame-support", + "frame-system", + "pallet-transaction-payment", + "parity-scale-codec", + "relay-substrate-client", + "relay-utils", + "rialto-parachain-runtime", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "relay-rococo-client" +version = "0.1.0" +dependencies = [ + "bp-rococo", + "frame-support", + "relay-substrate-client", + "relay-utils", + "sp-core", +] + +[[package]] +name = "relay-substrate-client" +version = "0.1.0" +dependencies = [ + "async-std", + "async-trait", + "bp-header-chain", + "bp-messages", + "bp-runtime", + "finality-relay", + "frame-support", + "frame-system", + "futures", + "jsonrpsee", + "log", + "num-traits", + "pallet-balances", + "pallet-bridge-messages", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "rand 0.7.3", + "relay-utils", + "sc-chain-spec", + "sc-rpc-api", + "sc-transaction-pool-api", + "sp-core", + "sp-finality-grandpa", + "sp-rpc", + "sp-runtime", + "sp-storage", + "sp-trie", + "sp-version", + "thiserror", + "tokio", +] + +[[package]] +name = "relay-utils" +version = "0.1.0" +dependencies = [ + "ansi_term 0.12.1", + "anyhow", + "async-std", + "async-trait", + "backoff 0.2.1", + "bp-runtime", + "env_logger 0.8.4", + "futures", + "isahc", + "jsonpath_lib", + "log", + "num-traits", + "serde_json", + "substrate-prometheus-endpoint", + "sysinfo", + "thiserror", + "time 0.3.7", + "tokio", +] + +[[package]] +name = "relay-westend-client" +version = "0.1.0" +dependencies = [ + "bp-westend", + "frame-support", + "relay-substrate-client", + "relay-utils", + "sp-core", +] + +[[package]] +name = "relay-wococo-client" +version = "0.1.0" +dependencies = [ + "bp-wococo", + "frame-support", + "relay-substrate-client", + "relay-utils", + "sp-core", +] + +[[package]] +name = "remote-externalities" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "env_logger 0.9.0", + "log", + "parity-scale-codec", + "serde", + "serde_json", + "sp-core", + "sp-io", + "sp-runtime", + "sp-version", + "substrate-rpc-client", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error 1.2.3", +] + +[[package]] +name = "rfc6979" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +dependencies = [ + "crypto-bigint", + "hmac 0.11.0", + "zeroize", +] + +[[package]] +name = "rialto-bridge-node" +version = "0.1.0" +dependencies = [ + "beefy-primitives", + "clap 4.0.26", + "frame-benchmarking", + "frame-benchmarking-cli", + "frame-support", + "node-inspect", + "polkadot-node-core-pvf", + "polkadot-primitives", + "polkadot-runtime-parachains", + "polkadot-service", + "rialto-runtime", + "sc-cli", + "sc-executor", + "sc-service", + "serde_json", + "sp-authority-discovery", + "sp-consensus-babe", + "sp-core", + "sp-finality-grandpa", + "sp-runtime", + "substrate-build-script-utils", +] + +[[package]] +name = "rialto-parachain-collator" +version = "0.1.0" +dependencies = [ + "clap 4.0.26", + "cumulus-client-cli", + "cumulus-client-consensus-aura", + "cumulus-client-consensus-common", + "cumulus-client-network", + "cumulus-client-service", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-relay-chain-inprocess-interface", + "cumulus-relay-chain-interface", + "cumulus-relay-chain-minimal-node", + "frame-benchmarking", + "frame-benchmarking-cli", + "jsonrpsee", + "log", + "pallet-transaction-payment-rpc", + "parity-scale-codec", + "polkadot-cli", + "polkadot-primitives", + "polkadot-service", + "rialto-parachain-runtime", + "sc-basic-authorship", + "sc-chain-spec", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-executor", + "sc-network", + "sc-rpc", + "sc-rpc-api", + "sc-service", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "serde", + "sp-api", + "sp-block-builder", + "sp-consensus", + "sp-consensus-aura", + "sp-core", + "sp-keystore", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-timestamp", + "sp-transaction-pool", + "substrate-build-script-utils", + "substrate-frame-rpc-system", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "rialto-parachain-runtime" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-millau", + "bp-relayers", + "bp-rialto-parachain", + "bp-runtime", + "bridge-runtime-common", + "cumulus-pallet-aura-ext", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "hex-literal", + "log", + "pallet-aura", + "pallet-balances", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-relayers", + "pallet-randomness-collective-flip", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-xcm", + "parachain-info", + "parity-scale-codec", + "polkadot-parachain", + "scale-info", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "rialto-runtime" +version = "0.1.0" +dependencies = [ + "beefy-primitives", + "bp-messages", + "bp-millau", + "bp-relayers", + "bp-rialto", + "bp-runtime", + "bridge-runtime-common", + "env_logger 0.8.4", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "log", + "pallet-authority-discovery", + "pallet-babe", + "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", + "pallet-bridge-beefy", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-relayers", + "pallet-grandpa", + "pallet-mmr", + "pallet-session", + "pallet-shift-session-manager", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-xcm", + "parity-scale-codec", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "scale-info", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-core", + "sp-inherents", + "sp-io", + "sp-mmr-primitives", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "static_assertions", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rlp" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "999508abb0ae792aabed2460c45b89106d97fe4adac593bdaef433c2605847b5" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rocksdb" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9562ea1d70c0cc63a34a22d977753b50cca91cc6b6527750463bd5dd8697bc" +dependencies = [ + "libc", + "librocksdb-sys", +] + +[[package]] +name = "rpassword" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b763cb66df1c928432cc35053f8bd4cec3335d8559fc16010017d16b3c1680" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "rtnetlink" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" +dependencies = [ + "async-global-executor", + "futures", + "log", + "netlink-packet-route", + "netlink-proto", + "nix", + "thiserror", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.4", +] + +[[package]] +name = "rustix" +version = "0.35.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c825b8aa8010eb9ee99b75f05e10180b9278d161583034d7574c9d617aeada" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.36.1", +] + +[[package]] +name = "rustls" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca9ebdfa27d3fc180e42879037b5338ab1c040c06affd00d8338598e7800943" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64", +] + +[[package]] +name = "rustversion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" + +[[package]] +name = "rw-stream-sink" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" +dependencies = [ + "futures", + "pin-project", + "static_assertions", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "safe-mix" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d3d055a2582e6b00ed7a31c1524040aa391092bf636328350813f3a0605215c" +dependencies = [ + "rustc_version 0.2.3", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "sc-allocator" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "log", + "sp-core", + "sp-wasm-interface", + "thiserror", +] + +[[package]] +name = "sc-authority-discovery" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "ip_network", + "libp2p", + "log", + "parity-scale-codec", + "prost", + "prost-build", + "rand 0.7.3", + "sc-client-api", + "sc-network-common", + "sp-api", + "sp-authority-discovery", + "sp-blockchain", + "sp-core", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-basic-authorship" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "sc-block-builder", + "sc-client-api", + "sc-proposer-metrics", + "sc-telemetry", + "sc-transaction-pool-api", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-inherents", + "sp-runtime", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "sc-block-builder" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", + "sc-client-api", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", +] + +[[package]] +name = "sc-chain-spec" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "impl-trait-for-tuples", + "memmap2", + "parity-scale-codec", + "sc-chain-spec-derive", + "sc-network-common", + "sc-telemetry", + "serde", + "serde_json", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "sc-chain-spec-derive" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sc-cli" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "array-bytes", + "chrono", + "clap 4.0.26", + "fdlimit", + "futures", + "libp2p", + "log", + "names", + "parity-scale-codec", + "rand 0.7.3", + "regex", + "rpassword", + "sc-client-api", + "sc-client-db", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-service", + "sc-telemetry", + "sc-tracing", + "sc-utils", + "serde", + "serde_json", + "sp-blockchain", + "sp-core", + "sp-keyring", + "sp-keystore", + "sp-panic-handler", + "sp-runtime", + "sp-version", + "thiserror", + "tiny-bip39", + "tokio", +] + +[[package]] +name = "sc-client-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "fnv", + "futures", + "hash-db", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-executor", + "sc-transaction-pool-api", + "sc-utils", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-database", + "sp-externalities", + "sp-keystore", + "sp-runtime", + "sp-state-machine", + "sp-storage", + "sp-trie", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "sc-client-db" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "hash-db", + "kvdb", + "kvdb-memorydb", + "kvdb-rocksdb", + "linked-hash-map", + "log", + "parity-db", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-client-api", + "sc-state-db", + "sp-arithmetic", + "sp-blockchain", + "sp-core", + "sp-database", + "sp-runtime", + "sp-state-machine", + "sp-trie", +] + +[[package]] +name = "sc-consensus" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "libp2p", + "log", + "parking_lot 0.12.1", + "sc-client-api", + "sc-utils", + "serde", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-state-machine", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-consensus-aura" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "futures", + "log", + "parity-scale-codec", + "sc-block-builder", + "sc-client-api", + "sc-consensus", + "sc-consensus-slots", + "sc-telemetry", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-consensus-babe" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "fork-tree", + "futures", + "log", + "merlin", + "num-bigint 0.2.6", + "num-rational 0.2.4", + "num-traits", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.7.3", + "sc-client-api", + "sc-consensus", + "sc-consensus-epochs", + "sc-consensus-slots", + "sc-keystore", + "sc-telemetry", + "schnorrkel", + "serde", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-consensus-slots", + "sp-consensus-vrf", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-version", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-consensus-babe-rpc" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "futures", + "jsonrpsee", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-rpc-api", + "serde", + "sp-api", + "sp-application-crypto", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-core", + "sp-keystore", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-consensus-epochs" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "fork-tree", + "parity-scale-codec", + "sc-client-api", + "sc-consensus", + "sp-blockchain", + "sp-runtime", +] + +[[package]] +name = "sc-consensus-slots" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "sc-client-api", + "sc-consensus", + "sc-telemetry", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", + "thiserror", +] + +[[package]] +name = "sc-executor" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "lazy_static", + "lru 0.7.8", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-executor-common", + "sc-executor-wasmi", + "sc-executor-wasmtime", + "sp-api", + "sp-core", + "sp-core-hashing-proc-macro", + "sp-externalities", + "sp-io", + "sp-panic-handler", + "sp-runtime-interface", + "sp-tasks", + "sp-trie", + "sp-version", + "sp-wasm-interface", + "tracing", + "wasmi", +] + +[[package]] +name = "sc-executor-common" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "environmental", + "parity-scale-codec", + "sc-allocator", + "sp-maybe-compressed-blob", + "sp-sandbox", + "sp-wasm-interface", + "thiserror", + "wasm-instrument", + "wasmi", +] + +[[package]] +name = "sc-executor-wasmi" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "log", + "parity-scale-codec", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface", + "sp-sandbox", + "sp-wasm-interface", + "wasmi", +] + +[[package]] +name = "sc-executor-wasmtime" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "log", + "once_cell", + "parity-scale-codec", + "parity-wasm", + "rustix", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface", + "sp-sandbox", + "sp-wasm-interface", + "wasmtime", +] + +[[package]] +name = "sc-finality-grandpa" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "ahash", + "array-bytes", + "async-trait", + "dyn-clone", + "finality-grandpa", + "fork-tree", + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-consensus", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-network-gossip", + "sc-telemetry", + "sc-utils", + "serde_json", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-finality-grandpa", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-finality-grandpa-rpc" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "finality-grandpa", + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "sc-client-api", + "sc-finality-grandpa", + "sc-rpc", + "serde", + "serde_json", + "sp-blockchain", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-informant" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "ansi_term 0.12.1", + "futures", + "futures-timer", + "log", + "parity-util-mem", + "sc-client-api", + "sc-network-common", + "sc-transaction-pool-api", + "sp-blockchain", + "sp-runtime", +] + +[[package]] +name = "sc-keystore" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "array-bytes", + "async-trait", + "parking_lot 0.12.1", + "serde_json", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "thiserror", +] + +[[package]] +name = "sc-network" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "array-bytes", + "async-trait", + "asynchronous-codec", + "bitflags", + "bytes", + "cid", + "either", + "fnv", + "fork-tree", + "futures", + "futures-timer", + "ip_network", + "libp2p", + "linked-hash-map", + "linked_hash_set", + "log", + "lru 0.7.8", + "parity-scale-codec", + "parking_lot 0.12.1", + "pin-project", + "prost", + "rand 0.7.3", + "sc-block-builder", + "sc-client-api", + "sc-consensus", + "sc-network-common", + "sc-peerset", + "sc-utils", + "serde", + "serde_json", + "smallvec", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", + "unsigned-varint", + "zeroize", +] + +[[package]] +name = "sc-network-bitswap" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "cid", + "futures", + "libp2p", + "log", + "prost", + "prost-build", + "sc-client-api", + "sc-network-common", + "sp-blockchain", + "sp-runtime", + "thiserror", + "unsigned-varint", + "void", +] + +[[package]] +name = "sc-network-common" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "bitflags", + "bytes", + "futures", + "futures-timer", + "libp2p", + "linked_hash_set", + "parity-scale-codec", + "prost-build", + "sc-consensus", + "sc-peerset", + "serde", + "smallvec", + "sp-blockchain", + "sp-consensus", + "sp-finality-grandpa", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-network-gossip" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "ahash", + "futures", + "futures-timer", + "libp2p", + "log", + "lru 0.7.8", + "sc-network-common", + "sc-peerset", + "sp-runtime", + "substrate-prometheus-endpoint", + "tracing", +] + +[[package]] +name = "sc-network-light" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "array-bytes", + "futures", + "libp2p", + "log", + "parity-scale-codec", + "prost", + "prost-build", + "sc-client-api", + "sc-network-common", + "sc-peerset", + "sp-blockchain", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-network-sync" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "array-bytes", + "fork-tree", + "futures", + "libp2p", + "log", + "lru 0.7.8", + "mockall", + "parity-scale-codec", + "prost", + "prost-build", + "sc-client-api", + "sc-consensus", + "sc-network-common", + "sc-peerset", + "sc-utils", + "smallvec", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-finality-grandpa", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-network-transactions" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "array-bytes", + "futures", + "hex", + "libp2p", + "log", + "parity-scale-codec", + "pin-project", + "sc-network-common", + "sc-peerset", + "sp-consensus", + "sp-runtime", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "sc-offchain" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "array-bytes", + "bytes", + "fnv", + "futures", + "futures-timer", + "hyper", + "hyper-rustls", + "libp2p", + "num_cpus", + "once_cell", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.7.3", + "sc-client-api", + "sc-network-common", + "sc-peerset", + "sc-utils", + "sp-api", + "sp-core", + "sp-offchain", + "sp-runtime", + "threadpool", + "tracing", +] + +[[package]] +name = "sc-peerset" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "futures", + "libp2p", + "log", + "sc-utils", + "serde_json", + "wasm-timer", +] + +[[package]] +name = "sc-proposer-metrics" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "log", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "sc-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "futures", + "hash-db", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-rpc-api", + "sc-tracing", + "sc-transaction-pool-api", + "sc-utils", + "serde_json", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-keystore", + "sp-offchain", + "sp-rpc", + "sp-runtime", + "sp-session", + "sp-version", +] + +[[package]] +name = "sc-rpc-api" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-chain-spec", + "sc-transaction-pool-api", + "scale-info", + "serde", + "serde_json", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-tracing", + "sp-version", + "thiserror", +] + +[[package]] +name = "sc-rpc-server" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "futures", + "jsonrpsee", + "log", + "serde_json", + "substrate-prometheus-endpoint", + "tokio", +] + +[[package]] +name = "sc-rpc-spec-v2" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "futures", + "hex", + "jsonrpsee", + "parity-scale-codec", + "sc-chain-spec", + "sc-transaction-pool-api", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-service" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "directories", + "exit-future", + "futures", + "futures-timer", + "hash-db", + "jsonrpsee", + "log", + "parity-scale-codec", + "parity-util-mem", + "parking_lot 0.12.1", + "pin-project", + "rand 0.7.3", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-client-db", + "sc-consensus", + "sc-executor", + "sc-informant", + "sc-keystore", + "sc-network", + "sc-network-bitswap", + "sc-network-common", + "sc-network-light", + "sc-network-sync", + "sc-network-transactions", + "sc-offchain", + "sc-rpc", + "sc-rpc-server", + "sc-rpc-spec-v2", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "sc-transaction-pool-api", + "sc-utils", + "serde", + "serde_json", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-externalities", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "sp-session", + "sp-state-machine", + "sp-storage", + "sp-tracing", + "sp-transaction-pool", + "sp-transaction-storage-proof", + "sp-trie", + "sp-version", + "static_init 1.0.3", + "substrate-prometheus-endpoint", + "tempfile", + "thiserror", + "tokio", + "tracing", + "tracing-futures", +] + +[[package]] +name = "sc-state-db" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "log", + "parity-scale-codec", + "parity-util-mem", + "parity-util-mem-derive", + "parking_lot 0.12.1", + "sc-client-api", + "sp-core", +] + +[[package]] +name = "sc-sync-state-rpc" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "jsonrpsee", + "parity-scale-codec", + "sc-chain-spec", + "sc-client-api", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-finality-grandpa", + "serde", + "serde_json", + "sp-blockchain", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-sysinfo" +version = "6.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "futures", + "libc", + "log", + "rand 0.7.3", + "rand_pcg 0.2.1", + "regex", + "sc-telemetry", + "serde", + "serde_json", + "sp-core", + "sp-io", + "sp-std", +] + +[[package]] +name = "sc-telemetry" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "chrono", + "futures", + "libp2p", + "log", + "parking_lot 0.12.1", + "pin-project", + "rand 0.7.3", + "serde", + "serde_json", + "thiserror", + "wasm-timer", +] + +[[package]] +name = "sc-tracing" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "ansi_term 0.12.1", + "atty", + "chrono", + "lazy_static", + "libc", + "log", + "once_cell", + "parking_lot 0.12.1", + "regex", + "rustc-hash", + "sc-client-api", + "sc-rpc-server", + "sc-tracing-proc-macro", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-tracing", + "thiserror", + "tracing", + "tracing-log", + "tracing-subscriber", +] + +[[package]] +name = "sc-tracing-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sc-transaction-pool" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "linked-hash-map", + "log", + "parity-scale-codec", + "parity-util-mem", + "parking_lot 0.12.1", + "sc-client-api", + "sc-transaction-pool-api", + "sc-utils", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-tracing", + "sp-transaction-pool", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-transaction-pool-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "futures", + "log", + "serde", + "sp-blockchain", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-utils" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "futures", + "futures-timer", + "lazy_static", + "log", + "parking_lot 0.12.1", + "prometheus", +] + +[[package]] +name = "scale-info" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d8a765117b237ef233705cc2cc4c6a27fccd46eea6ef0c8c6dae5f3ef407f8" +dependencies = [ + "bitvec", + "cfg-if 1.0.0", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "serde", +] + +[[package]] +name = "scale-info-derive" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdcd47b380d8c4541044e341dcd9475f55ba37ddc50c908d945fc036a8642496" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + +[[package]] +name = "schnorrkel" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "curve25519-dalek 2.1.3", + "getrandom 0.1.16", + "merlin", + "rand 0.7.3", + "rand_core 0.5.1", + "sha2 0.8.2", + "subtle", + "zeroize", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sec1" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +dependencies = [ + "der", + "generic-array 0.14.4", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7649a0b3ffb32636e60c7ce0d70511eda9c52c658cd0634e194d5a19943aeff" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7058dc8eaf3f2810d7828680320acda0b25a288f6d288e19278e249bbf74226b" +dependencies = [ + "cc", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" +dependencies = [ + "serde", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +dependencies = [ + "indexmap", + "itoa 1.0.1", + "ryu", + "serde", +] + +[[package]] +name = "serde_nanos" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e44969a61f5d316be20a42ff97816efb3b407a924d06824c3d8a49fa8450de0e" +dependencies = [ + "serde", +] + +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9db03534dff993187064c4e0c05a5708d2a9728ace9a8959b77bedf415dac5" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "sha3" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881bf8156c87b6301fc5ca6b27f11eeb2761224c7081e69b409d5a1951a70c86" +dependencies = [ + "digest 0.10.3", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "signal-hook" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" +dependencies = [ + "digest 0.9.0", + "rand_core 0.6.3", +] + +[[package]] +name = "simba" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e82063457853d00243beda9952e910b82593e4b07ae9f721b9278a99a0d3d5c" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "slice-group-by" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" + +[[package]] +name = "slot-range-helper" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "enumn", + "parity-scale-codec", + "paste", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "slotmap" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +dependencies = [ + "version_check", +] + +[[package]] +name = "sluice" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" +dependencies = [ + "async-channel", + "futures-core", + "futures-io", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "snap" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45456094d1983e2ee2a18fdfebce3189fa451699d0502cb8e3b49dba5ba41451" + +[[package]] +name = "snow" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774d05a3edae07ce6d68ea6984f3c05e9bba8927e3dd591e3b479e5b03213d0d" +dependencies = [ + "aes-gcm", + "blake2", + "chacha20poly1305", + "curve25519-dalek 4.0.0-pre.1", + "rand_core 0.6.3", + "ring", + "rustc_version 0.4.0", + "sha2 0.10.5", + "subtle", +] + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "soketto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +dependencies = [ + "base64", + "bytes", + "flate2", + "futures", + "httparse", + "log", + "rand 0.8.5", + "sha-1 0.9.8", +] + +[[package]] +name = "sp-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "sp-api-proc-macro", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "sp-version", + "thiserror", +] + +[[package]] +name = "sp-api-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "blake2", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-application-crypto" +version = "6.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-std", +] + +[[package]] +name = "sp-arithmetic" +version = "5.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-debug-derive", + "sp-std", + "static_assertions", +] + +[[package]] +name = "sp-authority-discovery" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-authorship" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "parity-scale-codec", + "sp-inherents", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-block-builder" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", + "sp-api", + "sp-inherents", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-blockchain" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "futures", + "log", + "lru 0.7.8", + "parity-scale-codec", + "parking_lot 0.12.1", + "sp-api", + "sp-consensus", + "sp-database", + "sp-runtime", + "sp-state-machine", + "thiserror", +] + +[[package]] +name = "sp-consensus" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-version", + "thiserror", +] + +[[package]] +name = "sp-consensus-aura" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-consensus", + "sp-consensus-slots", + "sp-inherents", + "sp-runtime", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "sp-consensus-babe" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "merlin", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-consensus", + "sp-consensus-slots", + "sp-consensus-vrf", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "sp-consensus-slots" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-arithmetic", + "sp-runtime", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "sp-consensus-vrf" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", + "scale-info", + "schnorrkel", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-core" +version = "6.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "array-bytes", + "base58", + "bitflags", + "blake2", + "byteorder", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db", + "hash256-std-hasher", + "impl-serde 0.4.0", + "lazy_static", + "libsecp256k1", + "log", + "merlin", + "num-traits", + "parity-scale-codec", + "parity-util-mem", + "parking_lot 0.12.1", + "primitive-types", + "rand 0.7.3", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "wasmi", + "zeroize", +] + +[[package]] +name = "sp-core-hashing" +version = "4.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "blake2", + "byteorder", + "digest 0.10.3", + "sha2 0.10.5", + "sha3", + "sp-std", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "proc-macro2", + "quote", + "sp-core-hashing", + "syn", +] + +[[package]] +name = "sp-database" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "kvdb", + "parking_lot 0.12.1", +] + +[[package]] +name = "sp-debug-derive" +version = "4.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-externalities" +version = "0.12.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std", + "sp-storage", +] + +[[package]] +name = "sp-finality-grandpa" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "finality-grandpa", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-inherents" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std", + "thiserror", +] + +[[package]] +name = "sp-io" +version = "6.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "bytes", + "futures", + "hash-db", + "libsecp256k1", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "secp256k1", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-trie", + "sp-wasm-interface", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-keyring" +version = "6.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "lazy_static", + "sp-core", + "sp-runtime", + "strum 0.24.1", +] + +[[package]] +name = "sp-keystore" +version = "0.12.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "futures", + "merlin", + "parity-scale-codec", + "parking_lot 0.12.1", + "schnorrkel", + "serde", + "sp-core", + "sp-externalities", + "thiserror", +] + +[[package]] +name = "sp-maybe-compressed-blob" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "thiserror", + "zstd", +] + +[[package]] +name = "sp-mmr-primitives" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-core", + "sp-debug-derive", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-npos-elections" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-arithmetic", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-offchain" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "sp-api", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "sp-panic-handler" +version = "4.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + +[[package]] +name = "sp-rpc" +version = "6.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "rustc-hash", + "serde", + "sp-core", +] + +[[package]] +name = "sp-runtime" +version = "6.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "parity-util-mem", + "paste", + "rand 0.7.3", + "scale-info", + "serde", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-std", + "sp-weights", +] + +[[package]] +name = "sp-runtime-interface" +version = "6.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std", + "sp-storage", + "sp-tracing", + "sp-wasm-interface", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-sandbox" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "log", + "parity-scale-codec", + "sp-core", + "sp-io", + "sp-std", + "sp-wasm-interface", + "wasmi", +] + +[[package]] +name = "sp-session" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "sp-staking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-state-machine" +version = "0.12.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "hash-db", + "log", + "num-traits", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.7.3", + "smallvec", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-std", + "sp-trie", + "thiserror", + "tracing", + "trie-root", +] + +[[package]] +name = "sp-std" +version = "4.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" + +[[package]] +name = "sp-storage" +version = "6.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "impl-serde 0.4.0", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive", + "sp-std", +] + +[[package]] +name = "sp-tasks" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "log", + "sp-core", + "sp-externalities", + "sp-io", + "sp-runtime-interface", + "sp-std", +] + +[[package]] +name = "sp-timestamp" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "futures-timer", + "log", + "parity-scale-codec", + "sp-api", + "sp-inherents", + "sp-runtime", + "sp-std", + "thiserror", +] + +[[package]] +name = "sp-tracing" +version = "5.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", + "sp-std", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sp-transaction-pool" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "sp-api", + "sp-runtime", +] + +[[package]] +name = "sp-transaction-storage-proof" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "sp-trie" +version = "6.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "ahash", + "hash-db", + "hashbrown", + "lazy_static", + "lru 0.7.8", + "memory-db", + "nohash-hasher", + "parity-scale-codec", + "parking_lot 0.12.1", + "scale-info", + "sp-core", + "sp-std", + "thiserror", + "tracing", + "trie-db", + "trie-root", +] + +[[package]] +name = "sp-version" +version = "5.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "impl-serde 0.4.0", + "parity-scale-codec", + "parity-wasm", + "scale-info", + "serde", + "sp-core-hashing-proc-macro", + "sp-runtime", + "sp-std", + "sp-version-proc-macro", + "thiserror", +] + +[[package]] +name = "sp-version-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "parity-scale-codec", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-wasm-interface" +version = "6.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std", + "wasmi", + "wasmtime", +] + +[[package]] +name = "sp-weights" +version = "4.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic", + "sp-core", + "sp-debug-derive", + "sp-std", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ss58-registry" +version = "1.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de151faef619cb7b5c26b32d42bc7ddccac0d202beb7a84344b44e9232b92f7" +dependencies = [ + "Inflector", + "num-format", + "proc-macro2", + "quote", + "serde", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "static_init" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11b73400442027c4adedda20a9f9b7945234a5bd8d5f7e86da22bd5d0622369c" +dependencies = [ + "cfg_aliases", + "libc", + "parking_lot 0.11.2", + "static_init_macro 0.5.0", +] + +[[package]] +name = "static_init" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6" +dependencies = [ + "bitflags", + "cfg_aliases", + "libc", + "parking_lot 0.11.2", + "parking_lot_core 0.8.5", + "static_init_macro 1.0.2", + "winapi", +] + +[[package]] +name = "static_init_macro" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2261c91034a1edc3fc4d1b80e89d82714faede0515c14a75da10cb941546bbf" +dependencies = [ + "cfg_aliases", + "memchr", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "static_init_macro" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" +dependencies = [ + "cfg_aliases", + "memchr", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "statrs" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05bdbb8e4e78216a85785a85d3ec3183144f98d0097b9281802c019bb07a6f05" +dependencies = [ + "approx", + "lazy_static", + "nalgebra", + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "storage-proof-fuzzer" +version = "0.1.0" +dependencies = [ + "bp-runtime", + "env_logger 0.8.4", + "honggfuzz", + "log", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "structopt" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" +dependencies = [ + "clap 2.33.3", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck 0.3.3", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "strum" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" +dependencies = [ + "strum_macros 0.21.1", +] + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros 0.24.0", +] + +[[package]] +name = "strum_macros" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "strum_macros" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef" +dependencies = [ + "heck 0.4.0", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "substrate-bip39" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" +dependencies = [ + "hmac 0.11.0", + "pbkdf2 0.8.0", + "schnorrkel", + "sha2 0.9.8", + "zeroize", +] + +[[package]] +name = "substrate-build-script-utils" +version = "3.0.0" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "platforms", +] + +[[package]] +name = "substrate-frame-rpc-system" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "frame-system-rpc-runtime-api", + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "sc-client-api", + "sc-rpc-api", + "sc-transaction-pool-api", + "serde_json", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "substrate-prometheus-endpoint" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "futures-util", + "hyper", + "log", + "prometheus", + "thiserror", + "tokio", +] + +[[package]] +name = "substrate-relay" +version = "1.0.1" +dependencies = [ + "anyhow", + "async-std", + "async-trait", + "bp-bridge-hub-rococo", + "bp-bridge-hub-wococo", + "bp-header-chain", + "bp-messages", + "bp-millau", + "bp-polkadot-core", + "bp-rialto", + "bp-rialto-parachain", + "bp-rococo", + "bp-runtime", + "bp-test-utils", + "bp-westend", + "bp-wococo", + "bridge-runtime-common", + "finality-grandpa", + "frame-support", + "futures", + "hex", + "hex-literal", + "log", + "messages-relay", + "millau-runtime", + "num-format", + "num-traits", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "parachains-relay", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "relay-bridge-hub-rococo-client", + "relay-bridge-hub-wococo-client", + "relay-millau-client", + "relay-rialto-client", + "relay-rialto-parachain-client", + "relay-rococo-client", + "relay-substrate-client", + "relay-utils", + "relay-westend-client", + "relay-wococo-client", + "rialto-parachain-runtime", + "rialto-runtime", + "sp-core", + "sp-keyring", + "sp-runtime", + "sp-version", + "structopt", + "strum 0.21.0", + "substrate-relay-helper", + "tempfile", + "xcm", +] + +[[package]] +name = "substrate-relay-helper" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-std", + "async-trait", + "bp-header-chain", + "bp-messages", + "bp-millau", + "bp-parachains", + "bp-polkadot-core", + "bp-rialto", + "bp-rococo", + "bp-runtime", + "bp-wococo", + "bridge-runtime-common", + "finality-grandpa", + "finality-relay", + "frame-support", + "frame-system", + "futures", + "log", + "messages-relay", + "num-traits", + "pallet-balances", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-transaction-payment", + "parachains-relay", + "parity-scale-codec", + "relay-rialto-client", + "relay-rococo-client", + "relay-substrate-client", + "relay-utils", + "relay-wococo-client", + "rialto-runtime", + "sp-core", + "sp-finality-grandpa", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "substrate-rpc-client" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "async-trait", + "jsonrpsee", + "log", + "sc-rpc-api", + "serde", + "sp-runtime", +] + +[[package]] +name = "substrate-state-trie-migration-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "jsonrpsee", + "log", + "parity-scale-codec", + "sc-client-api", + "sc-rpc-api", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "trie-db", +] + +[[package]] +name = "substrate-wasm-builder" +version = "5.0.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "ansi_term 0.12.1", + "build-helper", + "cargo_metadata", + "filetime", + "sp-maybe-compressed-blob", + "strum 0.24.1", + "tempfile", + "toml", + "walkdir", + "wasm-opt", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "sysinfo" +version = "0.15.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de94457a09609f33fec5e7fceaf907488967c6c7c75d64da6a7ce6ffdb8b5abd" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "core-foundation-sys", + "doc-comment", + "libc", + "ntapi", + "once_cell", + "rayon", + "winapi", +] + +[[package]] +name = "system-configuration" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bffcddbc2458fa3e6058414599e3c838a022abae82e5c67b4f7f80298d5bff" + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termtree" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thousands" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "thrift" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b82ca8f46f95b3ce96081fe3dd89160fdea970c254bb72925255d1b62aae692e" +dependencies = [ + "byteorder", + "integer-encoding", + "log", + "ordered-float", + "threadpool", +] + +[[package]] +name = "tikv-jemalloc-sys" +version = "0.5.2+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec45c14da997d0925c7835883e4d5c181f196fa142f8c19d7643d1e9af2592c3" +dependencies = [ + "cc", + "fs_extra", + "libc", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" +dependencies = [ + "itoa 1.0.1", + "libc", + "num_threads", +] + +[[package]] +name = "tiny-bip39" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +dependencies = [ + "anyhow", + "hmac 0.8.1", + "once_cell", + "pbkdf2 0.4.0", + "rand 0.7.3", + "rustc-hash", + "sha2 0.9.8", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot 0.12.1", + "pin-project-lite 0.2.9", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +dependencies = [ + "futures-core", + "pin-project-lite 0.2.9", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite 0.2.9", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite 0.2.9", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if 1.0.0", + "log", + "pin-project-lite 0.2.9", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-gum" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "polkadot-node-jaeger", + "polkadot-primitives", + "tracing", + "tracing-gum-proc-macro", +] + +[[package]] +name = "tracing-gum-proc-macro" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "expander 0.0.6", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term 0.12.1", + "chrono", + "lazy_static", + "matchers", + "parking_lot 0.11.2", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "trie-db" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "004e1e8f92535694b4cb1444dc5a8073ecf0815e3357f729638b9f8fc4062908" +dependencies = [ + "hash-db", + "hashbrown", + "log", + "rustc-hex", + "smallvec", +] + +[[package]] +name = "trie-root" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891" +dependencies = [ + "hash-db", +] + +[[package]] +name = "trust-dns-proto" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" +dependencies = [ + "async-trait", + "cfg-if 1.0.0", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.2.3", + "ipnet", + "lazy_static", + "rand 0.8.5", + "smallvec", + "thiserror", + "tinyvec", + "tracing", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe" +dependencies = [ + "cfg-if 1.0.0", + "futures-util", + "ipconfig", + "lazy_static", + "lru-cache", + "parking_lot 0.12.1", + "resolv-conf", + "smallvec", + "thiserror", + "tracing", + "trust-dns-proto", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "try-runtime-cli" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +dependencies = [ + "clap 4.0.26", + "log", + "parity-scale-codec", + "remote-externalities", + "sc-chain-spec", + "sc-cli", + "sc-executor", + "sc-service", + "serde", + "sp-core", + "sp-externalities", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-state-machine", + "sp-version", + "sp-weights", + "substrate-rpc-client", + "zstd", +] + +[[package]] +name = "tt-call" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e66dcbec4290c69dd03c57e76c2469ea5c7ce109c6dd4351c13055cf71ea055" + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if 1.0.0", + "digest 0.10.3", + "rand 0.8.5", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + +[[package]] +name = "uint" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-ident" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + +[[package]] +name = "unsigned-varint" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" +dependencies = [ + "asynchronous-codec", + "bytes", + "futures-io", + "futures-util", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna 0.3.0", + "percent-encoding", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "value-bag" +version = "1.0.0-alpha.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" +dependencies = [ + "ctor", + "version_check", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "wasm-instrument" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa1dafb3e60065305741e83db35c6c2584bb3725b692b5b66148a38d72ace6cd" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasm-opt" +version = "0.110.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b68e8037b4daf711393f4be2056246d12d975651b14d581520ad5d1f19219cec" +dependencies = [ + "anyhow", + "libc", + "strum 0.24.1", + "strum_macros 0.24.0", + "tempfile", + "thiserror", + "wasm-opt-cxx-sys", + "wasm-opt-sys", +] + +[[package]] +name = "wasm-opt-cxx-sys" +version = "0.110.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91adbad477e97bba3fbd21dd7bfb594e7ad5ceb9169ab1c93ab9cb0ada636b6f" +dependencies = [ + "anyhow", + "cxx", + "cxx-build", + "wasm-opt-sys", +] + +[[package]] +name = "wasm-opt-sys" +version = "0.110.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec4fa5a322a4e6ac22fd141f498d56afbdbf9df5debeac32380d2dcaa3e06941" +dependencies = [ + "anyhow", + "cc", + "cxx", + "cxx-build", + "regex", +] + +[[package]] +name = "wasm-timer" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.11.2", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmi" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" +dependencies = [ + "parity-wasm", + "wasmi-validation", + "wasmi_core", +] + +[[package]] +name = "wasmi-validation" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasmi_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +dependencies = [ + "downcast-rs", + "libm", + "memory_units", + "num-rational 0.4.0", + "num-traits", +] + +[[package]] +name = "wasmparser" +version = "0.89.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5d3e08b13876f96dd55608d03cd4883a0545884932d5adf11925876c96daef" +dependencies = [ + "indexmap", +] + +[[package]] +name = "wasmtime" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f511c4917c83d04da68333921107db75747c4e11a2f654a8e909cc5e0520dc" +dependencies = [ + "anyhow", + "bincode", + "cfg-if 1.0.0", + "indexmap", + "libc", + "log", + "object 0.29.0", + "once_cell", + "paste", + "psm", + "rayon", + "serde", + "target-lexicon", + "wasmparser", + "wasmtime-cache", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.36.1", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39bf3debfe744bf19dd3732990ce6f8c0ced7439e2370ba4e1d8f5a3660a3178" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "wasmtime-cache" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece42fa4676a263f7558cdaaf5a71c2592bebcbac22a0580e33cf3406c103da2" +dependencies = [ + "anyhow", + "base64", + "bincode", + "directories-next", + "file-per-thread-logger", + "log", + "rustix", + "serde", + "sha2 0.9.8", + "toml", + "windows-sys 0.36.1", + "zstd", +] + +[[package]] +name = "wasmtime-cranelift" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058217e28644b012bdcdf0e445f58d496d78c2e0b6a6dd93558e701591dad705" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli", + "log", + "object 0.29.0", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7af06848df28b7661471d9a80d30a973e0f401f2e3ed5396ad7e225ed217047" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli", + "indexmap", + "log", + "object 0.29.0", + "serde", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-jit" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9028fb63a54185b3c192b7500ef8039c7bb8d7f62bfc9e7c258483a33a3d13bb" +dependencies = [ + "addr2line", + "anyhow", + "bincode", + "cfg-if 1.0.0", + "cpp_demangle", + "gimli", + "log", + "object 0.29.0", + "rustc-demangle", + "rustix", + "serde", + "target-lexicon", + "thiserror", + "wasmtime-environ", + "wasmtime-jit-debug", + "wasmtime-runtime", + "windows-sys 0.36.1", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25e82d4ef93296785de7efca92f7679dc67fe68a13b625a5ecc8d7503b377a37" +dependencies = [ + "object 0.29.0", + "once_cell", + "rustix", +] + +[[package]] +name = "wasmtime-runtime" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f0e9bea7d517d114fe66b930b2124ee086516ee93eeebfd97f75f366c5b0553" +dependencies = [ + "anyhow", + "cc", + "cfg-if 1.0.0", + "indexmap", + "libc", + "log", + "mach", + "memfd", + "memoffset", + "paste", + "rand 0.8.5", + "rustix", + "thiserror", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys 0.36.1", +] + +[[package]] +name = "wasmtime-types" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b83e93ed41b8fdc936244cfd5e455480cf1eca1fd60c78a0040038b4ce5075" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror", + "wasmparser", +] + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449" +dependencies = [ + "webpki", +] + +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + +[[package]] +name = "which" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9" +dependencies = [ + "either", + "lazy_static", + "libc", +] + +[[package]] +name = "widestring" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f" +dependencies = [ + "windows_aarch64_msvc 0.34.0", + "windows_i686_gnu 0.34.0", + "windows_i686_msvc 0.34.0", + "windows_x86_64_gnu 0.34.0", + "windows_x86_64_msvc 0.34.0", +] + +[[package]] +name = "windows-sys" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" +dependencies = [ + "windows_aarch64_msvc 0.32.0", + "windows_i686_gnu 0.32.0", + "windows_i686_msvc 0.32.0", + "windows_x86_64_gnu 0.32.0", + "windows_x86_64_msvc 0.32.0", +] + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] + +[[package]] +name = "wyz" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" +dependencies = [ + "tap", +] + +[[package]] +name = "x25519-dalek" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" +dependencies = [ + "curve25519-dalek 3.2.0", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "xcm" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "derivative", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "xcm-procedural", +] + +[[package]] +name = "xcm-builder" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-transaction-payment", + "parity-scale-codec", + "polkadot-parachain", + "scale-info", + "sp-arithmetic", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", + "xcm-executor", +] + +[[package]] +name = "xcm-executor" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "frame-support", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", +] + +[[package]] +name = "xcm-procedural" +version = "0.9.31" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "yamux" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d9ba232399af1783a58d8eb26f6b5006fbefe2dc9ef36bd283324792d03ea5" +dependencies = [ + "futures", + "log", + "nohash-hasher", + "parking_lot 0.12.1", + "rand 0.8.5", + "static_assertions", +] + +[[package]] +name = "zeroize" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb5728b8afd3f280a869ce1d4c554ffaed35f45c231fc41bfbd0381bef50317" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.1+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b" +dependencies = [ + "cc", + "libc", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000000..9ee19ed7828 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,263 @@ +[workspace] +resolver = "2" + +members = [ + "bin/*/node", + "bin/*/runtime", + "fuzz/*", + "modules/*", + "primitives/*", + "relays/*", +] + +# we need to be able to work with XCMv3, but it is not yet in Polkadot master +# => manual patch is required. Because of https://github.com/rust-lang/cargo/issues/5478 +# we need to use double slash in the repo name. +# +# Once XCMv3 PR is merged, we may remove both Substrate and Polkadot patch section. + +[patch."https://github.com/paritytech/substrate"] +beefy-gadget = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +beefy-gadget-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +beefy-merkle-tree = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +beefy-primitives = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +fork-tree = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-benchmarking-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-election-provider-solution-type = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-election-provider-support = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-executive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-support = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-support-procedural = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-support-procedural-tools = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-support-procedural-tools-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-system = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-system-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +frame-try-runtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +node-inspect = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-bags-list = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-balances = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-beefy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-beefy-mmr = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-bounties = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-child-bounties = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-collective = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-conviction-voting = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-democracy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-election-provider-multi-phase = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-elections-phragmen = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-fast-unstake = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-gilt = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-identity = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-im-online = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-indices = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-membership = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-mmr = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-mmr-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-multisig = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-nomination-pools = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-nomination-pools-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-offences = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-preimage = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-proxy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-randomness-collective-flip = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-ranked-collective = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-recovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-referenda = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-scheduler = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-session = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-society = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-staking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-staking-reward-curve = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-staking-reward-fn = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-sudo = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-timestamp = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-tips = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-transaction-payment = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-transaction-payment-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-treasury = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-utility = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-vesting = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +pallet-whitelist = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +remote-externalities = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-allocator = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-basic-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-block-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-chain-spec = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-chain-spec-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-client-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-client-db = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-consensus = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-consensus-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-consensus-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-consensus-babe-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-consensus-epochs = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-consensus-slots = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-executor = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-executor-common = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-executor-wasmi = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-executor-wasmtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-finality-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-finality-grandpa-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-informant = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-keystore = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-network = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-network-common = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-network-gossip = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-network-light = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-network-sync = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-offchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-peerset = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-proposer-metrics = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-rpc-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-rpc-server = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-service = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-state-db = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-sync-state-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-sysinfo = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-telemetry = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-tracing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-tracing-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-transaction-pool = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-transaction-pool-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sc-utils = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-api-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-application-crypto = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-arithmetic = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-block-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-blockchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-consensus = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-consensus-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-consensus-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-consensus-slots = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-consensus-vrf = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-core = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-core-hashing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-core-hashing-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-database = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-debug-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-externalities = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-finality-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-inherents = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-io = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-keyring = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-keystore = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-maybe-compressed-blob = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-mmr-primitives = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-npos-elections = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-offchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-panic-handler = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-runtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-runtime-interface = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-runtime-interface-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-session = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-staking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-state-machine = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-std = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-storage = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-tasks = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-timestamp = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-tracing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-transaction-pool = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-transaction-storage-proof = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-trie = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-version = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-version-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +sp-wasm-interface = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +substrate-build-script-utils = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +substrate-frame-rpc-system = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +substrate-prometheus-endpoint = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +substrate-state-trie-migration-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +substrate-wasm-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +try-runtime-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } + +[patch."https://github.com/paritytech/polkadot"] +kusama-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +kusama-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +pallet-xcm = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-approval-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-availability-bitfield-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-availability-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-availability-recovery = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-cli = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-client = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-collator-protocol = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-core-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-dispute-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-erasure-coding = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-gossip-support = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-network-bridge = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-collation-generation = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-approval-voting = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-av-store = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-backing = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-bitfield-signing = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-candidate-validation = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-chain-api = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-chain-selection = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-dispute-coordinator = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-parachains-inherent = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-provisioner = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-pvf = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-pvf-checker = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-core-runtime-api = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-jaeger = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-metrics = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-network-protocol = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-subsystem = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-subsystem-types = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-node-subsystem-util = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-overseer = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-parachain = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-performance-test = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-rpc = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-runtime-common = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-runtime-metrics = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-runtime-parachains = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-service = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-statement-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-statement-table = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +slot-range-helper = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +tracing-gum = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +tracing-gum-proc-macro = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +xcm = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +xcm-builder = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +xcm-executor = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +xcm-procedural = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } + +[patch."https://github.com/paritytech/cumulus"] +cumulus-client-consensus-aura = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-client-consensus-common = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-client-cli = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-client-network = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-client-service = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-primitives-core = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-primitives-parachain-inherent = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-relay-chain-interface = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-relay-chain-inprocess-interface = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-relay-chain-minimal-node = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-pallet-aura-ext = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-pallet-parachain-system = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-pallet-dmp-queue = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-pallet-xcm = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-pallet-xcmp-queue = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +cumulus-primitives-timestamp = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } +parachain-info = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000000..a64b59e7081 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,72 @@ +# Builds images used by the bridge. +# +# In particular, it can be used to build Substrate nodes and bridge relayers. The binary that gets +# built can be specified with the `PROJECT` build-arg. For example, to build the `substrate-relay` +# you would do the following: +# +# `docker build . -t local/substrate-relay --build-arg=PROJECT=substrate-relay` +# +# See the `deployments/README.md` for all the available `PROJECT` values. + +FROM docker.io/paritytech/bridges-ci:production as builder +USER root +WORKDIR /parity-bridges-common + +COPY . . + +ARG PROJECT=substrate-relay +RUN cargo build --release --verbose -p ${PROJECT} && \ + strip ./target/release/${PROJECT} + +# In this final stage we copy over the final binary and do some checks +# to make sure that everything looks good. +FROM docker.io/library/ubuntu:20.04 as runtime + +# show backtraces +ENV RUST_BACKTRACE 1 +ENV DEBIAN_FRONTEND=noninteractive + +RUN set -eux; \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + curl ca-certificates libssl-dev && \ + update-ca-certificates && \ + groupadd -g 1000 user && \ + useradd -u 1000 -g user -s /bin/sh -m user && \ + # apt clean up + apt-get autoremove -y && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# switch to non-root user +USER user + +WORKDIR /home/user + +ARG PROJECT=substrate-relay + +COPY --chown=user:user --from=builder /parity-bridges-common/target/release/${PROJECT} ./ +COPY --chown=user:user --from=builder /parity-bridges-common/deployments/local-scripts/bridge-entrypoint.sh ./ + +# check if executable works in this container +RUN ./${PROJECT} --version + +ENV PROJECT=$PROJECT +ENTRYPOINT ["/home/user/bridge-entrypoint.sh"] + +# metadata +ARG VCS_REF=master +ARG BUILD_DATE="" +ARG VERSION="" + +LABEL org.opencontainers.image.title="${PROJECT}" \ + org.opencontainers.image.description="${PROJECT} - component of Parity Bridges Common" \ + org.opencontainers.image.source="https://github.com/paritytech/parity-bridges-common/blob/${VCS_REF}/Dockerfile" \ + org.opencontainers.image.url="https://github.com/paritytech/parity-bridges-common/blob/${VCS_REF}/Dockerfile" \ + org.opencontainers.image.documentation="https://github.com/paritytech/parity-bridges-common/blob/${VCS_REF}/README.md" \ + org.opencontainers.image.created="${BUILD_DATE}" \ + org.opencontainers.image.version="${VERSION}" \ + org.opencontainers.image.revision="${VCS_REF}" \ + org.opencontainers.image.authors="devops-team@parity.io" \ + org.opencontainers.image.vendor="Parity Technologies" \ + org.opencontainers.image.licenses="GPL-3.0 License" diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000000..733c072369c --- /dev/null +++ b/LICENSE @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/README.md b/README.md new file mode 100644 index 00000000000..d45b328b2bd --- /dev/null +++ b/README.md @@ -0,0 +1,252 @@ +# Parity Bridges Common + +This is a collection of components for building bridges. + +These components include Substrate pallets for syncing headers, passing arbitrary messages, as well +as libraries for building relayers to provide cross-chain communication capabilities. + +Three bridge nodes are also available. The nodes can be used to run test networks which bridge other +Substrate chains. + +🚧 The bridges are currently under construction - a hardhat is recommended beyond this point 🚧 + +**IMPORTANT**: this documentation is outdated and it is mostly related to the previous version of our +bridge. Right there's an ongoing work to make our bridge work with XCM messages. Old bridge is still +available at [encoded-calls-messaging](https://github.com/paritytech/parity-bridges-common/releases/tag/encoded-calls-messaging) +tag. + +## Contents + +- [Installation](#installation) +- [High-Level Architecture](#high-level-architecture) +- [Project Layout](#project-layout) +- [Running the Bridge](#running-the-bridge) +- [How to send a message](#how-to-send-a-message) +- [Community](#community) + +## Installation + +To get up and running you need both stable and nightly Rust. Rust nightly is used to build the Web +Assembly (WASM) runtime for the node. You can configure the WASM support as so: + +```bash +rustup install nightly +rustup target add wasm32-unknown-unknown --toolchain nightly +``` + +Once this is configured you can build and test the repo as follows: + +``` +git clone https://github.com/paritytech/parity-bridges-common.git +cd parity-bridges-common +cargo build --all +cargo test --all +``` + +Also you can build the repo with +[Parity CI Docker image](https://github.com/paritytech/scripts/tree/master/dockerfiles/bridges-ci): + +```bash +docker pull paritytech/bridges-ci:production +mkdir ~/cache +chown 1000:1000 ~/cache #processes in the container runs as "nonroot" user with UID 1000 +docker run --rm -it -w /shellhere/parity-bridges-common \ + -v /home/$(whoami)/cache/:/cache/ \ + -v "$(pwd)":/shellhere/parity-bridges-common \ + -e CARGO_HOME=/cache/cargo/ \ + -e SCCACHE_DIR=/cache/sccache/ \ + -e CARGO_TARGET_DIR=/cache/target/ paritytech/bridges-ci:production cargo build --all +#artifacts can be found in ~/cache/target +``` + +If you want to reproduce other steps of CI process you can use the following +[guide](https://github.com/paritytech/scripts#reproduce-ci-locally). + +If you need more information about setting up your development environment [Substrate's +Installation page](https://docs.substrate.io/main-docs/install/) is a good +resource. + +## High-Level Architecture + +This repo has support for bridging foreign chains together using a combination of Substrate pallets +and external processes called relayers. A bridge chain is one that is able to follow the consensus +of a foreign chain independently. For example, consider the case below where we want to bridge two +Substrate based chains. + +``` ++---------------+ +---------------+ +| | | | +| Rialto | | Millau | +| | | | ++-------+-------+ +-------+-------+ + ^ ^ + | +---------------+ | + | | | | + +-----> | Bridge Relay | <-------+ + | | + +---------------+ +``` + +The Millau chain must be able to accept Rialto headers and verify their integrity. It does this by +using a runtime module designed to track GRANDPA finality. Since two blockchains can't interact +directly they need an external service, called a relayer, to communicate. The relayer will subscribe +to new Rialto headers via RPC and submit them to the Millau chain for verification. + +Take a look at [Bridge High Level Documentation](./docs/high-level-overview.md) for more in-depth +description of the bridge interaction. + +## Project Layout + +Here's an overview of how the project is laid out. The main bits are the `node`, which is the actual +"blockchain", the `modules` which are used to build the blockchain's logic (a.k.a the runtime) and +the `relays` which are used to pass messages between chains. + +``` +├── bin // Node and Runtime for the various Substrate chains +│ └── ... +├── deployments // Useful tools for deploying test networks +│ └── ... +├── diagrams // Pretty pictures of the project architecture +│ └── ... +├── modules // Substrate Runtime Modules (a.k.a Pallets) +│ ├── grandpa // On-Chain GRANDPA Light Client +│ ├── messages // Cross Chain Message Passing +│ ├── dispatch // Target Chain Message Execution +│ └── ... +├── primitives // Code shared between modules, runtimes, and relays +│ └── ... +├── relays // Application for sending headers and messages between chains +│ └── ... +└── scripts // Useful development and maintenance scripts +``` + +## Running the Bridge + +To run the Bridge you need to be able to connect the bridge relay node to the RPC interface of nodes +on each side of the bridge (source and target chain). + +There are 2 ways to run the bridge, described below: + +- building & running from source +- running a Docker Compose setup (recommended). + +### Using the Source + +First you'll need to build the bridge nodes and relay. This can be done as follows: + +```bash +# In `parity-bridges-common` folder +cargo build -p rialto-bridge-node +cargo build -p millau-bridge-node +cargo build -p substrate-relay +``` + +### Running a Dev network + +We will launch a dev network to demonstrate how to relay a message between two Substrate based +chains (named Rialto and Millau). + +To do this we will need two nodes, two relayers which will relay headers, and two relayers which +will relay messages. + +#### Running from local scripts + +To run a simple dev network you can use the scripts located in the +[`deployments/local-scripts` folder](./deployments/local-scripts). + +First, we must run the two Substrate nodes. + +```bash +# In `parity-bridges-common` folder +./deployments/local-scripts/run-rialto-node.sh +./deployments/local-scripts/run-millau-node.sh +``` + +After the nodes are up we can run the header relayers. + +```bash +./deployments/local-scripts/relay-millau-to-rialto.sh +./deployments/local-scripts/relay-rialto-to-millau.sh +``` + +At this point you should see the relayer submitting headers from the Millau Substrate chain to the +Rialto Substrate chain. + +``` +# Header Relayer Logs +[Millau_to_Rialto_Sync] [date] DEBUG bridge Going to submit finality proof of Millau header #147 to Rialto +[...] [date] INFO bridge Synced 147 of 147 headers +[...] [date] DEBUG bridge Going to submit finality proof of Millau header #148 to Rialto +[...] [date] INFO bridge Synced 148 of 149 headers +``` + +Finally, we can run the message relayers. + +```bash +./deployments/local-scripts/relay-messages-millau-to-rialto.sh +./deployments/local-scripts/relay-messages-rialto-to-millau.sh +``` + +You will also see the message lane relayers listening for new messages. + +``` +# Message Relayer Logs +[Millau_to_Rialto_MessageLane_00000000] [date] DEBUG bridge Asking Millau::ReceivingConfirmationsDelivery about best message nonces +[...] [date] INFO bridge Synced Some(2) of Some(3) nonces in Millau::MessagesDelivery -> Rialto::MessagesDelivery race +[...] [date] DEBUG bridge Asking Millau::MessagesDelivery about message nonces +[...] [date] DEBUG bridge Received best nonces from Millau::ReceivingConfirmationsDelivery: TargetClientNonces { latest_nonce: 0, nonces_data: () } +[...] [date] DEBUG bridge Asking Millau::ReceivingConfirmationsDelivery about finalized message nonces +[...] [date] DEBUG bridge Received finalized nonces from Millau::ReceivingConfirmationsDelivery: TargetClientNonces { latest_nonce: 0, nonces_data: () } +[...] [date] DEBUG bridge Received nonces from Millau::MessagesDelivery: SourceClientNonces { new_nonces: {}, confirmed_nonce: Some(0) } +[...] [date] DEBUG bridge Asking Millau node about its state +[...] [date] DEBUG bridge Received state from Millau node: ClientState { best_self: HeaderId(1593, 0xacac***), best_finalized_self: HeaderId(1590, 0x0be81d...), best_finalized_peer_at_best_self: HeaderId(0, 0xdcdd89...) } +``` + +To send a message see the ["How to send a message" section](#how-to-send-a-message). + +### Full Network Docker Compose Setup + +For a more sophisticated deployment which includes bidirectional header sync, message passing, +monitoring dashboards, etc. see the [Deployments README](./deployments/README.md). + +You should note that you can find images for all the bridge components published on +[Docker Hub](https://hub.docker.com/u/paritytech). + +To run a Rialto node for example, you can use the following command: + +```bash +docker run -p 30333:30333 -p 9933:9933 -p 9944:9944 \ + -it paritytech/rialto-bridge-node --dev --tmp \ + --rpc-cors=all --unsafe-rpc-external --unsafe-ws-external +``` + +### How to send a message + +In this section we'll show you how to quickly send a bridge message, if you want to +interact with and test the bridge see more details in [send message](./docs/send-message.md) + +```bash +# In `parity-bridges-common` folder +./scripts/send-message-from-millau-rialto.sh remark +``` + +After sending a message you will see the following logs showing a message was successfully sent: + +``` +INFO bridge Sending message to Rialto. Size: 286. Dispatch weight: 1038000. Fee: 275,002,568 +INFO bridge Signed Millau Call: 0x7904... +TRACE bridge Sent transaction to Millau node: 0x5e68... +``` + +## Community + +Main hangout for the community is [Element](https://element.io/) (formerly Riot). Element is a chat +server like, for example, Discord. Most discussions around Polkadot and Substrate happen +in various Element "rooms" (channels). So, joining Element might be a good idea, anyway. + +If you are interested in information exchange and development of Polkadot related bridges please +feel free to join the [Polkadot Bridges](https://app.element.io/#/room/#bridges:web3.foundation) +Element channel. + +The [Substrate Technical](https://app.element.io/#/room/#substrate-technical:matrix.org) Element +channel is most suited for discussions regarding Substrate itself. diff --git a/bin/.keep b/bin/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/bin/millau/node/Cargo.toml b/bin/millau/node/Cargo.toml new file mode 100644 index 00000000000..a8d03c6f1a4 --- /dev/null +++ b/bin/millau/node/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "millau-bridge-node" +description = "Substrate node compatible with Millau runtime" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +build = "build.rs" +repository = "https://github.com/paritytech/parity-bridges-common/" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +clap = { version = "4.0.9", features = ["derive"] } +jsonrpsee = { version = "0.15.1", features = ["server"] } +serde_json = "1.0.79" + +# Bridge dependencies + +millau-runtime = { path = "../runtime" } + +# Substrate Dependencies + +beefy-gadget = { git = "https://github.com/paritytech/substrate", branch = "master" } +beefy-gadget-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } +node-inspect = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-mmr-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-basic-authorship = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master", features = ["wasmtime"] } +sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-finality-grandpa-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-service = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-telemetry = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master" } +substrate-frame-rpc-system = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[build-dependencies] +substrate-build-script-utils = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = [] +runtime-benchmarks = [ + "millau-runtime/runtime-benchmarks", +] diff --git a/bin/millau/node/build.rs b/bin/millau/node/build.rs new file mode 100644 index 00000000000..d9b50049e26 --- /dev/null +++ b/bin/millau/node/build.rs @@ -0,0 +1,23 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; + +fn main() { + generate_cargo_keys(); + + rerun_if_git_head_changed(); +} diff --git a/bin/millau/node/src/chain_spec.rs b/bin/millau/node/src/chain_spec.rs new file mode 100644 index 00000000000..2ded668fdee --- /dev/null +++ b/bin/millau/node/src/chain_spec.rs @@ -0,0 +1,235 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use beefy_primitives::crypto::AuthorityId as BeefyId; +use millau_runtime::{ + AccountId, AuraConfig, BalancesConfig, BeefyConfig, BridgeRialtoMessagesConfig, + BridgeRialtoParachainMessagesConfig, BridgeWestendGrandpaConfig, GenesisConfig, GrandpaConfig, + SessionConfig, SessionKeys, Signature, SudoConfig, SystemConfig, WASM_BINARY, +}; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_core::{sr25519, Pair, Public}; +use sp_finality_grandpa::AuthorityId as GrandpaId; +use sp_runtime::traits::{IdentifyAccount, Verify}; + +/// "Names" of the authorities accounts at local testnet. +const LOCAL_AUTHORITIES_ACCOUNTS: [&str; 5] = ["Alice", "Bob", "Charlie", "Dave", "Eve"]; +/// "Names" of the authorities accounts at development testnet. +const DEV_AUTHORITIES_ACCOUNTS: [&str; 1] = [LOCAL_AUTHORITIES_ACCOUNTS[0]]; +/// "Names" of all possible authorities accounts. +const ALL_AUTHORITIES_ACCOUNTS: [&str; 5] = LOCAL_AUTHORITIES_ACCOUNTS; +/// "Name" of the `sudo` account. +const SUDO_ACCOUNT: &str = "Sudo"; +/// "Name" of the account, which owns the with-Westend GRANDPA pallet. +const WESTEND_GRANDPA_PALLET_OWNER: &str = "Westend.GrandpaOwner"; +/// "Name" of the account, which owns the with-Rialto messages pallet. +const RIALTO_MESSAGES_PALLET_OWNER: &str = "Rialto.MessagesOwner"; +/// "Name" of the account, which owns the with-RialtoParachain messages pallet. +const RIALTO_PARACHAIN_MESSAGES_PALLET_OWNER: &str = "RialtoParachain.MessagesOwner"; + +/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. +pub type ChainSpec = sc_service::GenericChainSpec; + +/// The chain specification option. This is expected to come in from the CLI and +/// is little more than one of a number of alternatives which can easily be converted +/// from a string (`--chain=...`) into a `ChainSpec`. +#[derive(Clone, Debug)] +pub enum Alternative { + /// Whatever the current runtime is, with just Alice as an auth. + Development, + /// Whatever the current runtime is, with simple Alice/Bob/Charlie/Dave/Eve auths. + LocalTestnet, +} + +/// Helper function to generate a crypto pair from seed +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{seed}"), None) + .expect("static values are valid; qed") + .public() +} + +type AccountPublic = ::Signer; + +/// Helper function to generate an account ID from seed +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// Helper function to generate an authority key for Aura +pub fn get_authority_keys_from_seed(s: &str) -> (AccountId, AuraId, BeefyId, GrandpaId) { + ( + get_account_id_from_seed::(s), + get_from_seed::(s), + get_from_seed::(s), + get_from_seed::(s), + ) +} + +impl Alternative { + /// Get an actual chain config from one of the alternatives. + pub(crate) fn load(self) -> ChainSpec { + let properties = Some( + serde_json::json!({ + "tokenDecimals": 9, + "tokenSymbol": "MLAU" + }) + .as_object() + .expect("Map given; qed") + .clone(), + ); + match self { + Alternative::Development => ChainSpec::from_genesis( + "Millau Development", + "millau_dev", + sc_service::ChainType::Development, + || { + testnet_genesis( + DEV_AUTHORITIES_ACCOUNTS + .into_iter() + .map(get_authority_keys_from_seed) + .collect(), + get_account_id_from_seed::(SUDO_ACCOUNT), + endowed_accounts(), + true, + ) + }, + vec![], + None, + None, + None, + properties, + None, + ), + Alternative::LocalTestnet => ChainSpec::from_genesis( + "Millau Local", + "millau_local", + sc_service::ChainType::Local, + || { + testnet_genesis( + LOCAL_AUTHORITIES_ACCOUNTS + .into_iter() + .map(get_authority_keys_from_seed) + .collect(), + get_account_id_from_seed::(SUDO_ACCOUNT), + endowed_accounts(), + true, + ) + }, + vec![], + None, + None, + None, + properties, + None, + ), + } + } +} + +/// We're using the same set of endowed accounts on all Millau chains (dev/local) to make +/// sure that all accounts, required for bridge to be functional (e.g. relayers fund account, +/// accounts used by relayers in our test deployments, accounts used for demonstration +/// purposes), are all available on these chains. +fn endowed_accounts() -> Vec { + let all_authorities = ALL_AUTHORITIES_ACCOUNTS.iter().flat_map(|x| { + [ + get_account_id_from_seed::(x), + get_account_id_from_seed::(&format!("{x}//stash")), + ] + }); + vec![ + // Sudo account + get_account_id_from_seed::(SUDO_ACCOUNT), + // Regular (unused) accounts + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Ferdie//stash"), + // Accounts, used by Westend<>Millau bridge + get_account_id_from_seed::(WESTEND_GRANDPA_PALLET_OWNER), + get_account_id_from_seed::("Westend.HeadersRelay1"), + get_account_id_from_seed::("Westend.HeadersRelay2"), + get_account_id_from_seed::("Westend.WestmintHeaders1"), + get_account_id_from_seed::("Westend.WestmintHeaders2"), + // Accounts, used by Rialto<>Millau bridge + get_account_id_from_seed::(RIALTO_MESSAGES_PALLET_OWNER), + get_account_id_from_seed::("Rialto.HeadersAndMessagesRelay"), + get_account_id_from_seed::("Rialto.OutboundMessagesRelay.Lane00000001"), + get_account_id_from_seed::("Rialto.InboundMessagesRelay.Lane00000001"), + get_account_id_from_seed::("Rialto.MessagesSender"), + // Accounts, used by RialtoParachain<>Millau bridge + get_account_id_from_seed::(RIALTO_PARACHAIN_MESSAGES_PALLET_OWNER), + get_account_id_from_seed::("RialtoParachain.HeadersAndMessagesRelay1"), + get_account_id_from_seed::("RialtoParachain.HeadersAndMessagesRelay2"), + get_account_id_from_seed::("RialtoParachain.RialtoHeadersRelay1"), + get_account_id_from_seed::("RialtoParachain.RialtoHeadersRelay2"), + get_account_id_from_seed::("RialtoParachain.MessagesSender"), + ] + .into_iter() + .chain(all_authorities) + .collect() +} + +fn session_keys(aura: AuraId, beefy: BeefyId, grandpa: GrandpaId) -> SessionKeys { + SessionKeys { aura, beefy, grandpa } +} + +fn testnet_genesis( + initial_authorities: Vec<(AccountId, AuraId, BeefyId, GrandpaId)>, + root_key: AccountId, + endowed_accounts: Vec, + _enable_println: bool, +) -> GenesisConfig { + GenesisConfig { + system: SystemConfig { + code: WASM_BINARY.expect("Millau development WASM not available").to_vec(), + }, + balances: BalancesConfig { + balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 50)).collect(), + }, + aura: AuraConfig { authorities: Vec::new() }, + beefy: BeefyConfig { authorities: Vec::new() }, + grandpa: GrandpaConfig { authorities: Vec::new() }, + sudo: SudoConfig { key: Some(root_key) }, + session: SessionConfig { + keys: initial_authorities + .iter() + .map(|x| { + (x.0.clone(), x.0.clone(), session_keys(x.1.clone(), x.2.clone(), x.3.clone())) + }) + .collect::>(), + }, + bridge_westend_grandpa: BridgeWestendGrandpaConfig { + // for our deployments to avoid multiple same-nonces transactions: + // //Alice is already used to initialize Rialto<->Millau bridge + // => let's use //Westend.GrandpaOwner to initialize Westend->Millau bridge + owner: Some(get_account_id_from_seed::(WESTEND_GRANDPA_PALLET_OWNER)), + ..Default::default() + }, + bridge_rialto_messages: BridgeRialtoMessagesConfig { + owner: Some(get_account_id_from_seed::(RIALTO_MESSAGES_PALLET_OWNER)), + ..Default::default() + }, + bridge_rialto_parachain_messages: BridgeRialtoParachainMessagesConfig { + owner: Some(get_account_id_from_seed::( + RIALTO_PARACHAIN_MESSAGES_PALLET_OWNER, + )), + ..Default::default() + }, + xcm_pallet: Default::default(), + } +} diff --git a/bin/millau/node/src/cli.rs b/bin/millau/node/src/cli.rs new file mode 100644 index 00000000000..0280254bcad --- /dev/null +++ b/bin/millau/node/src/cli.rs @@ -0,0 +1,72 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use clap::Parser; +use sc_cli::RunCmd; + +#[derive(Debug, Parser)] +pub struct Cli { + #[structopt(subcommand)] + pub subcommand: Option, + + #[structopt(flatten)] + pub run: RunCmd, +} + +/// Possible subcommands of the main binary. +#[derive(Debug, Parser)] +pub enum Subcommand { + /// Key management CLI utilities + #[clap(subcommand)] + Key(sc_cli::KeySubcommand), + + /// Verify a signature for a message, provided on `STDIN`, with a given (public or secret) key. + Verify(sc_cli::VerifyCmd), + + /// Generate a seed that provides a vanity address. + Vanity(sc_cli::VanityCmd), + + /// Sign a message, with a given (secret) key. + Sign(sc_cli::SignCmd), + + /// Build a chain specification. + BuildSpec(sc_cli::BuildSpecCmd), + + /// Validate blocks. + CheckBlock(sc_cli::CheckBlockCmd), + + /// Export blocks. + ExportBlocks(sc_cli::ExportBlocksCmd), + + /// Export the state of a given block into a chain spec. + ExportState(sc_cli::ExportStateCmd), + + /// Import blocks. + ImportBlocks(sc_cli::ImportBlocksCmd), + + /// Remove the whole chain. + PurgeChain(sc_cli::PurgeChainCmd), + + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), + + /// Inspect blocks or extrinsics. + Inspect(node_inspect::cli::InspectCmd), + + /// Benchmark runtime pallets. + #[clap(subcommand)] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), +} diff --git a/bin/millau/node/src/command.rs b/bin/millau/node/src/command.rs new file mode 100644 index 00000000000..b8dff87f8f2 --- /dev/null +++ b/bin/millau/node/src/command.rs @@ -0,0 +1,159 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{ + cli::{Cli, Subcommand}, + service, + service::new_partial, +}; +use frame_benchmarking_cli::BenchmarkCmd; +use millau_runtime::{Block, RuntimeApi}; +use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli}; +use sc_service::PartialComponents; + +impl SubstrateCli for Cli { + fn impl_name() -> String { + "Millau Bridge Node".into() + } + + fn impl_version() -> String { + env!("CARGO_PKG_VERSION").into() + } + + fn description() -> String { + "Millau Bridge Node".into() + } + + fn author() -> String { + "Parity Technologies".into() + } + + fn support_url() -> String { + "https://github.com/paritytech/parity-bridges-common/".into() + } + + fn copyright_start_year() -> i32 { + 2019 + } + + fn executable_name() -> String { + "millau-bridge-node".into() + } + + fn native_runtime_version(_: &Box) -> &'static RuntimeVersion { + &millau_runtime::VERSION + } + + fn load_spec(&self, id: &str) -> Result, String> { + Ok(Box::new( + match id { + "" | "dev" => crate::chain_spec::Alternative::Development, + "local" => crate::chain_spec::Alternative::LocalTestnet, + _ => return Err(format!("Unsupported chain specification: {id}")), + } + .load(), + )) + } +} + +/// Parse and run command line arguments +pub fn run() -> sc_cli::Result<()> { + let cli = Cli::from_args(); + // make sure to set correct crypto version. + sp_core::crypto::set_default_ss58_version(sp_core::crypto::Ss58AddressFormat::custom( + millau_runtime::SS58Prefix::get() as u16, + )); + + match &cli.subcommand { + Some(Subcommand::Benchmark(cmd)) => { + let runner = cli.create_runner(cmd)?; + match cmd { + BenchmarkCmd::Pallet(cmd) => + if cfg!(feature = "runtime-benchmarks") { + runner + .sync_run(|config| cmd.run::(config)) + } else { + println!( + "Benchmarking wasn't enabled when building the node. \ + You can enable it with `--features runtime-benchmarks`." + ); + Ok(()) + }, + _ => Err("Unsupported benchmarking subcommand".into()), + } + }, + Some(Subcommand::Key(cmd)) => cmd.run(&cli), + Some(Subcommand::Sign(cmd)) => cmd.run(), + Some(Subcommand::Verify(cmd)) => cmd.run(), + Some(Subcommand::Vanity(cmd)) => cmd.run(), + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) + }, + Some(Subcommand::CheckBlock(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, import_queue, .. } = + new_partial(&config)?; + Ok((cmd.run(client, import_queue), task_manager)) + }) + }, + Some(Subcommand::ExportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, .. } = new_partial(&config)?; + Ok((cmd.run(client, config.database), task_manager)) + }) + }, + Some(Subcommand::ExportState(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, .. } = new_partial(&config)?; + Ok((cmd.run(client, config.chain_spec), task_manager)) + }) + }, + Some(Subcommand::ImportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, import_queue, .. } = + new_partial(&config)?; + Ok((cmd.run(client, import_queue), task_manager)) + }) + }, + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.database)) + }, + Some(Subcommand::Revert(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, backend, .. } = new_partial(&config)?; + Ok((cmd.run(client, backend, None), task_manager)) + }) + }, + Some(Subcommand::Inspect(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner + .sync_run(|config| cmd.run::(config)) + }, + None => { + let runner = cli.create_runner(&cli.run)?; + runner.run_node_until_exit(|config| async move { + service::new_full(config).map_err(sc_cli::Error::Service) + }) + }, + } +} diff --git a/bin/millau/node/src/lib.rs b/bin/millau/node/src/lib.rs new file mode 100644 index 00000000000..382d1c2d7fb --- /dev/null +++ b/bin/millau/node/src/lib.rs @@ -0,0 +1,32 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate Node Template CLI library. +#![warn(missing_docs)] + +mod chain_spec; +#[macro_use] +mod service; +mod cli; +mod command; + +/// Node run result. +pub type Result = sc_cli::Result<()>; + +/// Run node. +pub fn run() -> Result { + command::run() +} diff --git a/bin/millau/node/src/main.rs b/bin/millau/node/src/main.rs new file mode 100644 index 00000000000..cf6dd9f733a --- /dev/null +++ b/bin/millau/node/src/main.rs @@ -0,0 +1,30 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Millau bridge node. + +#![warn(missing_docs)] + +mod chain_spec; +#[macro_use] +mod service; +mod cli; +mod command; + +/// Run the Millau Node +fn main() -> sc_cli::Result<()> { + command::run() +} diff --git a/bin/millau/node/src/service.rs b/bin/millau/node/src/service.rs new file mode 100644 index 00000000000..e23e1fa56ca --- /dev/null +++ b/bin/millau/node/src/service.rs @@ -0,0 +1,461 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Service and ServiceFactory implementation. Specialized wrapper over substrate service. + +use jsonrpsee::RpcModule; +use millau_runtime::{self, opaque::Block, RuntimeApi}; +use sc_client_api::BlockBackend; +use sc_consensus_aura::{CompatibilityMode, ImportQueueParams, SlotProportion, StartAuraParams}; +pub use sc_executor::NativeElseWasmExecutor; +use sc_finality_grandpa::SharedVoterState; +use sc_keystore::LocalKeystore; +use sc_service::{error::Error as ServiceError, Configuration, TaskManager}; +use sc_telemetry::{Telemetry, TelemetryWorker}; +use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; +use std::{sync::Arc, time::Duration}; + +// Our native executor instance. +pub struct ExecutorDispatch; + +impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { + /// Only enable the benchmarking host functions when we actually want to benchmark. + #[cfg(feature = "runtime-benchmarks")] + type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; + /// Otherwise we only use the default Substrate host functions. + #[cfg(not(feature = "runtime-benchmarks"))] + type ExtendHostFunctions = (); + + fn dispatch(method: &str, data: &[u8]) -> Option> { + millau_runtime::api::dispatch(method, data) + } + + fn native_version() -> sc_executor::NativeVersion { + millau_runtime::native_version() + } +} + +type FullClient = + sc_service::TFullClient>; +type FullBackend = sc_service::TFullBackend; +type FullSelectChain = sc_consensus::LongestChain; + +#[allow(clippy::type_complexity)] +pub fn new_partial( + config: &Configuration, +) -> Result< + sc_service::PartialComponents< + FullClient, + FullBackend, + FullSelectChain, + sc_consensus::DefaultImportQueue, + sc_transaction_pool::FullPool, + ( + sc_finality_grandpa::GrandpaBlockImport< + FullBackend, + Block, + FullClient, + FullSelectChain, + >, + sc_finality_grandpa::LinkHalf, + beefy_gadget::BeefyVoterLinks, + beefy_gadget::BeefyRPCLinks, + Option, + ), + >, + ServiceError, +> { + if config.keystore_remote.is_some() { + return Err(ServiceError::Other("Remote Keystores are not supported.".into())) + } + + let telemetry = config + .telemetry_endpoints + .clone() + .filter(|x| !x.is_empty()) + .map(|endpoints| -> Result<_, sc_telemetry::Error> { + let worker = TelemetryWorker::new(16)?; + let telemetry = worker.handle().new_telemetry(endpoints); + Ok((worker, telemetry)) + }) + .transpose()?; + + let executor = NativeElseWasmExecutor::::new( + config.wasm_method, + config.default_heap_pages, + config.max_runtime_instances, + config.runtime_cache_size, + ); + + let (client, backend, keystore_container, task_manager) = + sc_service::new_full_parts::( + config, + telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), + executor, + )?; + let client = Arc::new(client); + + let telemetry = telemetry.map(|(worker, telemetry)| { + task_manager.spawn_handle().spawn("telemetry", None, worker.run()); + telemetry + }); + + let select_chain = sc_consensus::LongestChain::new(backend.clone()); + + let transaction_pool = sc_transaction_pool::BasicPool::new_full( + config.transaction_pool.clone(), + config.role.is_authority().into(), + config.prometheus_registry(), + task_manager.spawn_essential_handle(), + client.clone(), + ); + + let (grandpa_block_import, grandpa_link) = sc_finality_grandpa::block_import( + client.clone(), + &(client.clone() as Arc<_>), + select_chain.clone(), + telemetry.as_ref().map(|x| x.handle()), + )?; + + let (beefy_block_import, beefy_voter_links, beefy_rpc_links) = + beefy_gadget::beefy_block_import_and_links( + grandpa_block_import.clone(), + backend.clone(), + client.clone(), + ); + + let slot_duration = sc_consensus_aura::slot_duration(&*client)?; + + let import_queue = + sc_consensus_aura::import_queue::(ImportQueueParams { + block_import: beefy_block_import, + justification_import: Some(Box::new(grandpa_block_import.clone())), + client: client.clone(), + create_inherent_data_providers: move |_, ()| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + Ok((slot, timestamp)) + }, + spawner: &task_manager.spawn_essential_handle(), + registry: config.prometheus_registry(), + check_for_equivocation: Default::default(), + telemetry: telemetry.as_ref().map(|x| x.handle()), + compatibility_mode: CompatibilityMode::None, + })?; + + Ok(sc_service::PartialComponents { + client, + backend, + task_manager, + import_queue, + keystore_container, + select_chain, + transaction_pool, + other: (grandpa_block_import, grandpa_link, beefy_voter_links, beefy_rpc_links, telemetry), + }) +} + +fn remote_keystore(_url: &str) -> Result, &'static str> { + // FIXME: here would the concrete keystore be built, + // must return a concrete type (NOT `LocalKeystore`) that + // implements `CryptoStore` and `SyncCryptoStore` + Err("Remote Keystore not supported.") +} + +/// Builds a new service for a full client. +pub fn new_full(mut config: Configuration) -> Result { + let sc_service::PartialComponents { + client, + backend, + mut task_manager, + import_queue, + mut keystore_container, + select_chain, + transaction_pool, + other: (block_import, grandpa_link, beefy_voter_links, beefy_rpc_links, mut telemetry), + } = new_partial(&config)?; + + if let Some(url) = &config.keystore_remote { + match remote_keystore(url) { + Ok(k) => keystore_container.set_remote_keystore(k), + Err(e) => + return Err(ServiceError::Other(format!( + "Error hooking up remote keystore for {}: {}", + url, e + ))), + }; + } + + let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"); + + // Note: GrandPa is pushed before the Polkadot-specific protocols. This doesn't change + // anything in terms of behaviour, but makes the logs more consistent with the other + // Substrate nodes. + let grandpa_protocol_name = sc_finality_grandpa::protocol_standard_name( + &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), + &config.chain_spec, + ); + config + .network + .extra_sets + .push(sc_finality_grandpa::grandpa_peers_set_config(grandpa_protocol_name.clone())); + + let beefy_gossip_proto_name = + beefy_gadget::gossip_protocol_name(genesis_hash, config.chain_spec.fork_id()); + // `beefy_on_demand_justifications_handler` is given to `beefy-gadget` task to be run, + // while `beefy_req_resp_cfg` is added to `config.network.request_response_protocols`. + let (beefy_on_demand_justifications_handler, beefy_req_resp_cfg) = + beefy_gadget::communication::request_response::BeefyJustifsRequestHandler::new( + genesis_hash, + config.chain_spec.fork_id(), + client.clone(), + ); + config + .network + .extra_sets + .push(beefy_gadget::communication::beefy_peers_set_config(beefy_gossip_proto_name.clone())); + config.network.request_response_protocols.push(beefy_req_resp_cfg); + + let warp_sync = Arc::new(sc_finality_grandpa::warp_proof::NetworkProvider::new( + backend.clone(), + grandpa_link.shared_authority_set().clone(), + Vec::default(), + )); + + let (network, system_rpc_tx, tx_handler_controller, network_starter) = + sc_service::build_network(sc_service::BuildNetworkParams { + config: &config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + spawn_handle: task_manager.spawn_handle(), + import_queue, + block_announce_validator_builder: None, + warp_sync: Some(warp_sync), + })?; + + if config.offchain_worker.enabled { + sc_service::build_offchain_workers( + &config, + task_manager.spawn_handle(), + client.clone(), + network.clone(), + ); + } + + let role = config.role.clone(); + let force_authoring = config.force_authoring; + let backoff_authoring_blocks: Option<()> = None; + let name = config.network.node_name.clone(); + let enable_grandpa = !config.disable_grandpa; + let prometheus_registry = config.prometheus_registry().cloned(); + let shared_voter_state = SharedVoterState::empty(); + + let rpc_extensions_builder = { + use sc_finality_grandpa::FinalityProofProvider as GrandpaFinalityProofProvider; + + use beefy_gadget_rpc::{Beefy, BeefyApiServer}; + use pallet_mmr_rpc::{Mmr, MmrApiServer}; + use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; + use sc_finality_grandpa_rpc::{Grandpa, GrandpaApiServer}; + use sc_rpc::DenyUnsafe; + use substrate_frame_rpc_system::{System, SystemApiServer}; + + let backend = backend.clone(); + let client = client.clone(); + let pool = transaction_pool.clone(); + + let justification_stream = grandpa_link.justification_stream(); + let shared_authority_set = grandpa_link.shared_authority_set().clone(); + let shared_voter_state = shared_voter_state.clone(); + + let finality_proof_provider = GrandpaFinalityProofProvider::new_for_service( + backend, + Some(shared_authority_set.clone()), + ); + + Box::new(move |_, subscription_executor: sc_rpc::SubscriptionTaskExecutor| { + let mut io = RpcModule::new(()); + let map_err = |e| sc_service::Error::Other(format!("{e}")); + io.merge(System::new(client.clone(), pool.clone(), DenyUnsafe::No).into_rpc()) + .map_err(map_err)?; + io.merge(TransactionPayment::new(client.clone()).into_rpc()).map_err(map_err)?; + io.merge( + Grandpa::new( + subscription_executor.clone(), + shared_authority_set.clone(), + shared_voter_state.clone(), + justification_stream.clone(), + finality_proof_provider.clone(), + ) + .into_rpc(), + ) + .map_err(map_err)?; + io.merge( + Beefy::::new( + beefy_rpc_links.from_voter_justif_stream.clone(), + beefy_rpc_links.from_voter_best_beefy_stream.clone(), + subscription_executor, + ) + .map_err(|e| sc_service::Error::Other(format!("{e}")))? + .into_rpc(), + ) + .map_err(map_err)?; + io.merge(Mmr::new(client.clone()).into_rpc()).map_err(map_err)?; + Ok(io) + }) + }; + + let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { + network: network.clone(), + client: client.clone(), + keystore: keystore_container.sync_keystore(), + task_manager: &mut task_manager, + transaction_pool: transaction_pool.clone(), + rpc_builder: rpc_extensions_builder.clone(), + backend: backend.clone(), + system_rpc_tx, + config, + tx_handler_controller, + telemetry: telemetry.as_mut(), + })?; + + if role.is_authority() { + let proposer_factory = sc_basic_authorship::ProposerFactory::new( + task_manager.spawn_handle(), + client.clone(), + transaction_pool, + prometheus_registry.as_ref(), + telemetry.as_ref().map(|x| x.handle()), + ); + + let slot_duration = sc_consensus_aura::slot_duration(&*client)?; + + let aura = sc_consensus_aura::start_aura::( + StartAuraParams { + slot_duration, + client: client.clone(), + select_chain, + block_import, + proposer_factory, + create_inherent_data_providers: move |_, ()| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + Ok((slot, timestamp)) + }, + force_authoring, + backoff_authoring_blocks, + keystore: keystore_container.sync_keystore(), + sync_oracle: network.clone(), + justification_sync_link: network.clone(), + block_proposal_slot_portion: SlotProportion::new(2f32 / 3f32), + max_block_proposal_slot_portion: None, + telemetry: telemetry.as_ref().map(|x| x.handle()), + compatibility_mode: CompatibilityMode::None, + }, + )?; + + // the AURA authoring task is considered essential, i.e. if it + // fails we take down the service with it. + task_manager + .spawn_essential_handle() + .spawn_blocking("aura", Some("block-authoring"), aura); + } + + // if the node isn't actively participating in consensus then it doesn't + // need a keystore, regardless of which protocol we use below. + let keystore = + if role.is_authority() { Some(keystore_container.sync_keystore()) } else { None }; + + let justifications_protocol_name = beefy_on_demand_justifications_handler.protocol_name(); + let payload_provider = beefy_primitives::mmr::MmrRootProvider::new(client.clone()); + let beefy_params = beefy_gadget::BeefyParams { + client: client.clone(), + backend, + payload_provider, + runtime: client, + key_store: keystore.clone(), + network_params: beefy_gadget::BeefyNetworkParams { + network: network.clone(), + gossip_protocol_name: beefy_gossip_proto_name, + justifications_protocol_name, + _phantom: core::marker::PhantomData::, + }, + min_block_delta: 2, + prometheus_registry: prometheus_registry.clone(), + links: beefy_voter_links, + on_demand_justifications_handler: beefy_on_demand_justifications_handler, + }; + + // Start the BEEFY bridge gadget. + task_manager.spawn_essential_handle().spawn_blocking( + "beefy-gadget", + None, + beefy_gadget::start_beefy_gadget::<_, _, _, _, _, _>(beefy_params), + ); + + let grandpa_config = sc_finality_grandpa::Config { + // FIXME #1578 make this available through chainspec + gossip_duration: Duration::from_millis(333), + justification_period: 512, + name: Some(name), + observer_enabled: false, + keystore, + local_role: role, + telemetry: telemetry.as_ref().map(|x| x.handle()), + protocol_name: grandpa_protocol_name, + }; + + if enable_grandpa { + // start the full GRANDPA voter + // NOTE: non-authorities could run the GRANDPA observer protocol, but at + // this point the full voter should provide better guarantees of block + // and vote data availability than the observer. The observer has not + // been tested extensively yet and having most nodes in a network run it + // could lead to finality stalls. + let grandpa_config = sc_finality_grandpa::GrandpaParams { + config: grandpa_config, + link: grandpa_link, + network, + voting_rule: sc_finality_grandpa::VotingRulesBuilder::default().build(), + prometheus_registry, + shared_voter_state, + telemetry: telemetry.as_ref().map(|x| x.handle()), + }; + + // the GRANDPA voter task is considered infallible, i.e. + // if it fails we take down the service with it. + task_manager.spawn_essential_handle().spawn_blocking( + "grandpa-voter", + None, + sc_finality_grandpa::run_grandpa_voter(grandpa_config)?, + ); + } + + network_starter.start_network(); + Ok(task_manager) +} diff --git a/bin/millau/runtime/Cargo.toml b/bin/millau/runtime/Cargo.toml new file mode 100644 index 00000000000..b297dedfa53 --- /dev/null +++ b/bin/millau/runtime/Cargo.toml @@ -0,0 +1,145 @@ +[package] +name = "millau-runtime" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +repository = "https://github.com/paritytech/parity-bridges-common/" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +hex-literal = "0.3" +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } + +# Bridge dependencies + +bp-messages = { path = "../../../primitives/messages", default-features = false } +bp-millau = { path = "../../../primitives/chain-millau", default-features = false } +bp-polkadot-core = { path = "../../../primitives/polkadot-core", default-features = false } +bp-relayers = { path = "../../../primitives/relayers", default-features = false } +bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false } +bp-rialto-parachain = { path = "../../../primitives/chain-rialto-parachain", default-features = false } +bp-runtime = { path = "../../../primitives/runtime", default-features = false } +bp-westend = { path = "../../../primitives/chain-westend", default-features = false } +bridge-runtime-common = { path = "../../runtime-common", default-features = false } +pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false } +pallet-bridge-messages = { path = "../../../modules/messages", default-features = false } +pallet-bridge-parachains = { path = "../../../modules/parachains", default-features = false } +pallet-bridge-relayers = { path = "../../../modules/relayers", default-features = false } +pallet-shift-session-manager = { path = "../../../modules/shift-session-manager", default-features = false } + +# Substrate Dependencies + +beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-executive = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-aura = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-randomness-collective-flip = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +# Polkadot Dependencies +pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + +[dev-dependencies] +bridge-runtime-common = { path = "../../runtime-common", features = ["integrity-test"] } +env_logger = "0.8" +static_assertions = "1.1" + +[build-dependencies] +substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "beefy-primitives/std", + "bp-messages/std", + "bp-millau/std", + "bp-polkadot-core/std", + "bp-relayers/std", + "bp-rialto/std", + "bp-rialto-parachain/std", + "bp-runtime/std", + "bp-westend/std", + "bridge-runtime-common/std", + "codec/std", + "frame-executive/std", + "frame-support/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "log/std", + "pallet-aura/std", + "pallet-balances/std", + "pallet-beefy/std", + "pallet-beefy-mmr/std", + "pallet-bridge-grandpa/std", + "pallet-bridge-messages/std", + "pallet-bridge-parachains/std", + "pallet-bridge-relayers/std", + "pallet-grandpa/std", + "pallet-mmr/std", + "pallet-randomness-collective-flip/std", + "pallet-session/std", + "pallet-shift-session-manager/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "pallet-xcm/std", + "scale-info/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-core/std", + "sp-inherents/std", + "sp-io/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-version/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", +] +runtime-benchmarks = [ + "bridge-runtime-common/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-bridge-messages/runtime-benchmarks", + "pallet-bridge-parachains/runtime-benchmarks", + "pallet-bridge-relayers/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", +] diff --git a/bin/millau/runtime/build.rs b/bin/millau/runtime/build.rs new file mode 100644 index 00000000000..cc865704327 --- /dev/null +++ b/bin/millau/runtime/build.rs @@ -0,0 +1,25 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .import_memory() + .export_heap_base() + .build() +} diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs new file mode 100644 index 00000000000..b1f1e33d18a --- /dev/null +++ b/bin/millau/runtime/src/lib.rs @@ -0,0 +1,1119 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! The Millau runtime. This can be compiled with `#[no_std]`, ready for Wasm. + +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] +// Runtime-generated enums +#![allow(clippy::large_enum_variant)] +// From construct_runtime macro +#![allow(clippy::from_over_into)] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +pub mod rialto_messages; +pub mod rialto_parachain_messages; +pub mod xcm_config; + +use beefy_primitives::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; +use bp_runtime::{HeaderId, HeaderIdProvider}; +use codec::Decode; +use pallet_grandpa::{ + fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, +}; +use pallet_transaction_payment::{FeeDetails, Multiplier, RuntimeDispatchInfo}; +use sp_api::impl_runtime_apis; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_mmr_primitives::{DataOrHash, EncodableOpaqueLeaf, Error as MmrError, Proof as MmrProof}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{Block as BlockT, IdentityLookup, Keccak256, NumberFor, OpaqueKeys}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, FixedPointNumber, Perquintill, +}; +use sp_std::prelude::*; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +// to be able to use Millau runtime in `bridge-runtime-common` tests +pub use bridge_runtime_common; + +// A few exports that help ease life for downstream crates. +pub use frame_support::{ + construct_runtime, + dispatch::DispatchClass, + parameter_types, + traits::{Currency, ExistenceRequirement, Imbalance, KeyOwnerProofSystem}, + weights::{ + constants::WEIGHT_PER_SECOND, ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight, + }, + RuntimeDebug, StorageValue, +}; + +pub use frame_system::Call as SystemCall; +pub use pallet_balances::Call as BalancesCall; +pub use pallet_bridge_grandpa::Call as BridgeGrandpaCall; +pub use pallet_bridge_messages::Call as MessagesCall; +pub use pallet_bridge_parachains::Call as BridgeParachainsCall; +pub use pallet_sudo::Call as SudoCall; +pub use pallet_timestamp::Call as TimestampCall; +pub use pallet_xcm::Call as XcmCall; + +use bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages; +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; +pub use sp_runtime::{Perbill, Permill}; + +/// An index to a block. +pub type BlockNumber = bp_millau::BlockNumber; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = bp_millau::Signature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = bp_millau::AccountId; + +/// The type for looking up accounts. We don't expect more than 4 billion of them, but you +/// never know... +pub type AccountIndex = u32; + +/// Balance of an account. +pub type Balance = bp_millau::Balance; + +/// Index of a transaction in the chain. +pub type Index = bp_millau::Index; + +/// A hash of some data used by the chain. +pub type Hash = bp_millau::Hash; + +/// Hashing algorithm used by the chain. +pub type Hashing = bp_millau::Hasher; + +/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know +/// the specifics of the runtime. They can then be made to be agnostic over specific formats +/// of data like extrinsics, allowing for them to continue syncing the network through upgrades +/// to even the core data structures. +pub mod opaque { + use super::*; + + pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; + + /// Opaque block header type. + pub type Header = generic::Header; + /// Opaque block type. + pub type Block = generic::Block; + /// Opaque block identifier type. + pub type BlockId = generic::BlockId; +} + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + pub beefy: Beefy, + pub grandpa: Grandpa, + } +} + +/// This runtime version. +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("millau-runtime"), + impl_name: create_runtime_str!("millau-runtime"), + authoring_version: 1, + spec_version: 1, + impl_version: 1, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 0, +}; + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } +} + +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub const Version: RuntimeVersion = VERSION; + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { + read: 60_000_000, // ~0.06 ms = ~60 µs + write: 200_000_000, // ~0.2 ms = 200 µs + }; + pub const SS58Prefix: u8 = 60; +} + +impl frame_system::Config for Runtime { + /// The basic call filter to use in dispatchable. + type BaseCallFilter = frame_support::traits::Everything; + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type RuntimeCall = RuntimeCall; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = IdentityLookup; + /// The index type for storing how many extrinsics an account has signed. + type Index = Index; + /// The index type for blocks. + type BlockNumber = BlockNumber; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = Hashing; + /// The header type. + type Header = generic::Header; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// Version of the runtime. + type Version = Version; + /// Provides information about the pallet setup in the runtime. + type PalletInfo = PalletInfo; + /// What to do if a new account is created. + type OnNewAccount = (); + /// What to do if an account is fully reaped from the system. + type OnKilledAccount = (); + /// The data to be stored in an account. + type AccountData = pallet_balances::AccountData; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + /// Weight information for the extrinsics of this pallet. + type SystemWeightInfo = (); + /// Block and extrinsics weights: base values and limits. + type BlockWeights = bp_millau::BlockWeights; + /// The maximum length of a block (in bytes). + type BlockLength = bp_millau::BlockLength; + /// The weight of database operations that the runtime can invoke. + type DbWeight = DbWeight; + /// The designated SS58 prefix of this chain. + type SS58Prefix = SS58Prefix; + /// The set code logic, just the default since we're not a parachain. + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_randomness_collective_flip::Config for Runtime {} + +parameter_types! { + pub const MaxAuthorities: u32 = 10; +} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type MaxAuthorities = MaxAuthorities; + type DisabledValidators = (); +} + +impl pallet_beefy::Config for Runtime { + type BeefyId = BeefyId; + type MaxAuthorities = MaxAuthorities; + type OnNewValidatorSet = MmrLeaf; +} + +impl pallet_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type KeyOwnerProofSystem = (); + type KeyOwnerProof = + >::Proof; + type KeyOwnerIdentification = >::IdentificationTuple; + type HandleEquivocation = (); + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); + type MaxAuthorities = MaxAuthorities; +} + +/// MMR helper types. +mod mmr { + use super::Runtime; + pub use pallet_mmr::primitives::*; + use sp_runtime::traits::Keccak256; + + pub type Leaf = <::LeafData as LeafDataProvider>::LeafData; + pub type Hash = ::Output; + pub type Hashing = ::Hashing; +} + +impl pallet_mmr::Config for Runtime { + const INDEXING_PREFIX: &'static [u8] = b"mmr"; + type Hashing = Keccak256; + type Hash = mmr::Hash; + type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest; + type WeightInfo = (); + type LeafData = pallet_beefy_mmr::Pallet; +} + +parameter_types! { + /// Version of the produced MMR leaf. + /// + /// The version consists of two parts; + /// - `major` (3 bits) + /// - `minor` (5 bits) + /// + /// `major` should be updated only if decoding the previous MMR Leaf format from the payload + /// is not possible (i.e. backward incompatible change). + /// `minor` should be updated if fields are added to the previous MMR Leaf, which given SCALE + /// encoding does not prevent old leafs from being decoded. + /// + /// Hence we expect `major` to be changed really rarely (think never). + /// See [`MmrLeafVersion`] type documentation for more details. + pub LeafVersion: MmrLeafVersion = MmrLeafVersion::new(0, 0); +} + +pub struct BeefyDummyDataProvider; + +impl beefy_primitives::mmr::BeefyDataProvider<()> for BeefyDummyDataProvider { + fn extra_data() {} +} + +impl pallet_beefy_mmr::Config for Runtime { + type LeafVersion = LeafVersion; + type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; + type LeafExtra = (); + type BeefyDataProvider = BeefyDummyDataProvider; +} + +parameter_types! { + pub const MinimumPeriod: u64 = bp_millau::SLOT_DURATION / 2; +} + +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the UNIX epoch. + type Moment = u64; + type OnTimestampSet = Aura; + type MinimumPeriod = MinimumPeriod; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); +} + +parameter_types! { + pub const ExistentialDeposit: bp_millau::Balance = 500; + // For weight estimation, we assume that the most locks on an individual account will be 50. + // This number may need to be adjusted in the future if this assumption no longer holds true. + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); + type MaxLocks = MaxLocks; + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + +parameter_types! { + pub const TransactionBaseFee: Balance = 0; + pub const TransactionByteFee: Balance = 1; + pub const OperationalFeeMultiplier: u8 = 5; + // values for following parameters are copied from polkadot repo, but it is fine + // not to sync them - we're not going to make Rialto a full copy of one of Polkadot-like chains + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); + pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value(); +} + +impl pallet_transaction_payment::Config for Runtime { + type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; + type OperationalFeeMultiplier = OperationalFeeMultiplier; + type WeightToFee = bp_millau::WeightToFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< + Runtime, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + MaximumMultiplier, + >; + type RuntimeEvent = RuntimeEvent; +} + +impl pallet_sudo::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; +} + +parameter_types! { + /// Authorities are changing every 5 minutes. + pub const Period: BlockNumber = bp_millau::SESSION_LENGTH; + pub const Offset: BlockNumber = 0; +} + +impl pallet_session::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ValidatorId = ::AccountId; + type ValidatorIdOf = (); + type ShouldEndSession = pallet_session::PeriodicSessions; + type NextSessionRotation = pallet_session::PeriodicSessions; + type SessionManager = pallet_shift_session_manager::Pallet; + type SessionHandler = ::KeyTypeIdProviders; + type Keys = SessionKeys; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); +} + +parameter_types! { + // This is a pretty unscientific cap. + // + // Note that once this is hit the pallet will essentially throttle incoming requests down to one + // call per block. + pub const MaxRequests: u32 = 50; +} + +impl pallet_bridge_relayers::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Reward = Balance; + type PaymentProcedure = bp_relayers::MintReward, AccountId>; + type WeightInfo = (); +} + +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + /// Number of headers to keep in benchmarks. + /// + /// In benchmarks we always populate with full number of `HeadersToKeep` to make sure that + /// pruning is taken into account. + /// + /// Note: This is lower than regular value, to speed up benchmarking setup. + pub const HeadersToKeep: u32 = 1024; + /// Maximal number of authorities at Rialto. + /// + /// In benchmarks we're using sets of up to `1024` authorities to prepare for possible + /// upgrades in the future and see if performance degrades when number of authorities + /// grow. + pub const MaxAuthoritiesAtRialto: u32 = pallet_bridge_grandpa::benchmarking::MAX_VALIDATOR_SET_SIZE; +} + +#[cfg(not(feature = "runtime-benchmarks"))] +parameter_types! { + /// Number of headers to keep. + /// + /// Assuming the worst case of every header being finalized, we will keep headers at least for a + /// week. + pub const HeadersToKeep: u32 = 7 * bp_rialto::DAYS; + /// Maximal number of authorities at Rialto. + pub const MaxAuthoritiesAtRialto: u32 = bp_rialto::MAX_AUTHORITIES_COUNT; +} + +parameter_types! { + /// Maximal size of SCALE-encoded Rialto header. + pub const MaxRialtoHeaderSize: u32 = bp_rialto::MAX_HEADER_SIZE; + + /// Maximal number of authorities at Westend. + pub const MaxAuthoritiesAtWestend: u32 = bp_westend::MAX_AUTHORITIES_COUNT; + /// Maximal size of SCALE-encoded Westend header. + pub const MaxWestendHeaderSize: u32 = bp_westend::MAX_HEADER_SIZE; +} + +pub type RialtoGrandpaInstance = (); +impl pallet_bridge_grandpa::Config for Runtime { + type BridgedChain = bp_rialto::Rialto; + type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; + type MaxBridgedAuthorities = MaxAuthoritiesAtRialto; + type MaxBridgedHeaderSize = MaxRialtoHeaderSize; + + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; +} + +pub type WestendGrandpaInstance = pallet_bridge_grandpa::Instance1; +impl pallet_bridge_grandpa::Config for Runtime { + type BridgedChain = bp_westend::Westend; + type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; + type MaxBridgedAuthorities = MaxAuthoritiesAtWestend; + type MaxBridgedHeaderSize = MaxWestendHeaderSize; + + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; +} + +impl pallet_shift_session_manager::Config for Runtime {} + +parameter_types! { + pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; + pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = + bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + pub const RootAccountForPayments: Option = None; + pub const RialtoChainId: bp_runtime::ChainId = bp_runtime::RIALTO_CHAIN_ID; + pub const RialtoParachainChainId: bp_runtime::ChainId = bp_runtime::RIALTO_PARACHAIN_CHAIN_ID; + pub RialtoActiveOutboundLanes: &'static [bp_messages::LaneId] = &[rialto_messages::XCM_LANE]; + pub RialtoParachainActiveOutboundLanes: &'static [bp_messages::LaneId] = &[rialto_parachain_messages::XCM_LANE]; +} + +/// Instance of the messages pallet used to relay messages to/from Rialto chain. +pub type WithRialtoMessagesInstance = (); + +impl pallet_bridge_messages::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + type ActiveOutboundLanes = RialtoActiveOutboundLanes; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type MaximalOutboundPayloadSize = crate::rialto_messages::ToRialtoMaximalOutboundPayloadSize; + type OutboundPayload = crate::rialto_messages::ToRialtoMessagePayload; + + type InboundPayload = crate::rialto_messages::FromRialtoMessagePayload; + type InboundRelayer = bp_rialto::AccountId; + + type TargetHeaderChain = crate::rialto_messages::Rialto; + type LaneMessageVerifier = crate::rialto_messages::ToRialtoMessageVerifier; + type MessageDeliveryAndDispatchPayment = + pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter< + Runtime, + WithRialtoMessagesInstance, + >; + + type SourceHeaderChain = crate::rialto_messages::Rialto; + type MessageDispatch = crate::rialto_messages::FromRialtoMessageDispatch; + type BridgedChainId = RialtoChainId; +} + +/// Instance of the messages pallet used to relay messages to/from RialtoParachain chain. +pub type WithRialtoParachainMessagesInstance = pallet_bridge_messages::Instance1; + +impl pallet_bridge_messages::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + type ActiveOutboundLanes = RialtoParachainActiveOutboundLanes; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type MaximalOutboundPayloadSize = + crate::rialto_parachain_messages::ToRialtoParachainMaximalOutboundPayloadSize; + type OutboundPayload = crate::rialto_parachain_messages::ToRialtoParachainMessagePayload; + + type InboundPayload = crate::rialto_parachain_messages::FromRialtoParachainMessagePayload; + type InboundRelayer = bp_rialto_parachain::AccountId; + + type TargetHeaderChain = crate::rialto_parachain_messages::RialtoParachain; + type LaneMessageVerifier = crate::rialto_parachain_messages::ToRialtoParachainMessageVerifier; + type MessageDeliveryAndDispatchPayment = + pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter< + Runtime, + WithRialtoParachainMessagesInstance, + >; + + type SourceHeaderChain = crate::rialto_parachain_messages::RialtoParachain; + type MessageDispatch = crate::rialto_parachain_messages::FromRialtoParachainMessageDispatch; + type BridgedChainId = RialtoParachainChainId; +} + +parameter_types! { + pub const RialtoParasPalletName: &'static str = bp_rialto::PARAS_PALLET_NAME; + pub const WestendParasPalletName: &'static str = bp_westend::PARAS_PALLET_NAME; + pub const MaxRialtoParaHeadSize: u32 = bp_rialto::MAX_NESTED_PARACHAIN_HEAD_SIZE; + pub const MaxWestendParaHeadSize: u32 = bp_westend::MAX_NESTED_PARACHAIN_HEAD_SIZE; +} + +/// Instance of the with-Rialto parachains pallet. +pub type WithRialtoParachainsInstance = (); + +impl pallet_bridge_parachains::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; + type BridgesGrandpaPalletInstance = RialtoGrandpaInstance; + type ParasPalletName = RialtoParasPalletName; + type TrackedParachains = frame_support::traits::Everything; + type HeadsToKeep = HeadersToKeep; + type MaxParaHeadSize = MaxRialtoParaHeadSize; +} + +/// Instance of the with-Westend parachains pallet. +pub type WithWestendParachainsInstance = pallet_bridge_parachains::Instance1; + +impl pallet_bridge_parachains::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; + type BridgesGrandpaPalletInstance = WestendGrandpaInstance; + type ParasPalletName = WestendParasPalletName; + type TrackedParachains = frame_support::traits::Everything; + type HeadsToKeep = HeadersToKeep; + type MaxParaHeadSize = MaxWestendParaHeadSize; +} + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = opaque::Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Sudo: pallet_sudo::{Pallet, Call, Config, Storage, Event}, + + // Must be before session. + Aura: pallet_aura::{Pallet, Config}, + + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + + // Consensus support. + Session: pallet_session::{Pallet, Call, Storage, Event, Config}, + Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event}, + ShiftSessionManager: pallet_shift_session_manager::{Pallet}, + RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage}, + + // BEEFY Bridges support. + Beefy: pallet_beefy::{Pallet, Storage, Config}, + Mmr: pallet_mmr::{Pallet, Storage}, + MmrLeaf: pallet_beefy_mmr::{Pallet, Storage}, + + // Rialto bridge modules. + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, + BridgeRialtoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, + BridgeRialtoMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, + + // Westend bridge modules. + BridgeWestendGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Config, Storage}, + BridgeWestendParachains: pallet_bridge_parachains::::{Pallet, Call, Storage, Event}, + + // RialtoParachain bridge modules. + BridgeRialtoParachains: pallet_bridge_parachains::{Pallet, Call, Storage, Event}, + BridgeRialtoParachainMessages: pallet_bridge_messages::::{Pallet, Call, Storage, Event, Config}, + + // Pallet for sending XCM. + XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin, Config} = 99, + } +); + +generate_bridge_reject_obsolete_headers_and_messages! { + RuntimeCall, AccountId, + // Grandpa + BridgeRialtoGrandpa, BridgeWestendGrandpa, + // Parachains + BridgeRialtoParachains, + //Messages + BridgeRialtoMessages, BridgeRialtoParachainMessages +} + +/// The address format for describing accounts. +pub type Address = AccountId; +/// Block header type as expected by this runtime. +pub type Header = generic::Header; +/// Block type as expected by this runtime. +pub type Block = generic::Block; +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, + BridgeRejectObsoleteHeadersAndMessages, +); +/// The payload being signed in transactions. +pub type SignedPayload = generic::SignedPayload; +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = generic::CheckedExtrinsic; +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block); + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + } + + fn authorities() -> Vec { + Aura::authorities().to_vec() + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi< + Block, + Balance, + > for Runtime { + fn query_info(uxt: ::Extrinsic, len: u32) -> RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details(uxt: ::Extrinsic, len: u32) -> FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, sp_core::crypto::KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl beefy_primitives::BeefyApi for Runtime { + fn validator_set() -> Option> { + Beefy::validator_set() + } + } + + impl sp_mmr_primitives::MmrApi for Runtime { + fn generate_proof(block_number: BlockNumber) + -> Result<(EncodableOpaqueLeaf, MmrProof), MmrError> + { + Mmr::generate_batch_proof(vec![block_number]) + .and_then(|(leaves, proof)| Ok(( + mmr::EncodableOpaqueLeaf::from_leaf(&leaves[0]), + mmr::BatchProof::into_single_leaf_proof(proof)? + ))) + } + + fn verify_proof(leaf: EncodableOpaqueLeaf, proof: MmrProof) + -> Result<(), MmrError> + { + let leaf: mmr::Leaf = leaf + .into_opaque_leaf() + .try_decode() + .ok_or(MmrError::Verify)?; + Mmr::verify_leaves(vec![leaf], mmr::Proof::into_batch_proof(proof)) + } + + fn verify_proof_stateless( + root: mmr::Hash, + leaf: EncodableOpaqueLeaf, + proof: MmrProof + ) -> Result<(), MmrError> { + let node = DataOrHash::Data(leaf.into_opaque_leaf()); + pallet_mmr::verify_leaves_proof::( + root, + vec![node], + pallet_mmr::primitives::Proof::into_batch_proof(proof), + ) + } + + fn mmr_root() -> Result { + Ok(Mmr::mmr_root()) + } + + fn generate_batch_proof(block_numbers: Vec) + -> Result<(Vec, mmr::BatchProof), mmr::Error> + { + Mmr::generate_batch_proof(block_numbers) + .map(|(leaves, proof)| (leaves.into_iter().map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf)).collect(), proof)) + } + + fn generate_historical_batch_proof( + block_numbers: Vec, + best_known_block_number: BlockNumber + ) -> Result<(Vec, mmr::BatchProof), mmr::Error> { + Mmr::generate_historical_batch_proof(block_numbers, best_known_block_number).map( + |(leaves, proof)| { + ( + leaves + .into_iter() + .map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf)) + .collect(), + proof, + ) + }, + ) + } + + fn verify_batch_proof(leaves: Vec, proof: mmr::BatchProof) + -> Result<(), mmr::Error> + { + let leaves = leaves.into_iter().map(|leaf| + leaf.into_opaque_leaf() + .try_decode() + .ok_or(mmr::Error::Verify)).collect::, mmr::Error>>()?; + Mmr::verify_leaves(leaves, proof) + } + + fn verify_batch_proof_stateless( + root: mmr::Hash, + leaves: Vec, + proof: mmr::BatchProof + ) -> Result<(), mmr::Error> { + let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect(); + pallet_mmr::verify_leaves_proof::(root, nodes, proof) + } + } + + impl fg_primitives::GrandpaApi for Runtime { + fn current_set_id() -> fg_primitives::SetId { + Grandpa::current_set_id() + } + + fn grandpa_authorities() -> GrandpaAuthorityList { + Grandpa::grandpa_authorities() + } + + fn submit_report_equivocation_unsigned_extrinsic( + equivocation_proof: fg_primitives::EquivocationProof< + ::Hash, + NumberFor, + >, + key_owner_proof: fg_primitives::OpaqueKeyOwnershipProof, + ) -> Option<()> { + let key_owner_proof = key_owner_proof.decode()?; + + Grandpa::submit_unsigned_equivocation_report( + equivocation_proof, + key_owner_proof, + ) + } + + fn generate_key_ownership_proof( + _set_id: fg_primitives::SetId, + _authority_id: GrandpaId, + ) -> Option { + // NOTE: this is the only implementation possible since we've + // defined our key owner proof type as a bottom type (i.e. a type + // with no values). + None + } + } + + impl bp_rialto::RialtoFinalityApi for Runtime { + fn best_finalized() -> Option> { + BridgeRialtoGrandpa::best_finalized().map(|header| header.id()) + } + } + + impl bp_westend::WestendFinalityApi for Runtime { + fn best_finalized() -> Option> { + BridgeWestendGrandpa::best_finalized().map(|header| header.id()) + } + } + + impl bp_westend::WestmintFinalityApi for Runtime { + fn best_finalized() -> Option> { + // the parachains finality pallet is never decoding parachain heads, so it is + // only done in the integration code + use bp_westend::WESTMINT_PARACHAIN_ID; + let encoded_head = pallet_bridge_parachains::Pallet::< + Runtime, + WithWestendParachainsInstance, + >::best_parachain_head(WESTMINT_PARACHAIN_ID.into())?; + let head = bp_westend::Header::decode(&mut &encoded_head.0[..]).ok()?; + Some(head.id()) + } + } + + impl bp_rialto_parachain::RialtoParachainFinalityApi for Runtime { + fn best_finalized() -> Option> { + // the parachains finality pallet is never decoding parachain heads, so it is + // only done in the integration code + let encoded_head = pallet_bridge_parachains::Pallet::< + Runtime, + WithRialtoParachainsInstance, + >::best_parachain_head(bp_rialto_parachain::RIALTO_PARACHAIN_ID.into())?; + let head = bp_rialto_parachain::Header::decode(&mut &encoded_head.0[..]).ok()?; + Some(head.id()) + } + } + + impl bp_rialto::ToRialtoOutboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + begin: bp_messages::MessageNonce, + end: bp_messages::MessageNonce, + ) -> Vec { + bridge_runtime_common::messages_api::outbound_message_details::< + Runtime, + WithRialtoMessagesInstance, + >(lane, begin, end) + } + } + + impl bp_rialto::FromRialtoInboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + ) -> Vec { + bridge_runtime_common::messages_api::inbound_message_details::< + Runtime, + WithRialtoMessagesInstance, + >(lane, messages) + } + } + + impl bp_rialto_parachain::ToRialtoParachainOutboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + begin: bp_messages::MessageNonce, + end: bp_messages::MessageNonce, + ) -> Vec { + bridge_runtime_common::messages_api::outbound_message_details::< + Runtime, + WithRialtoParachainMessagesInstance, + >(lane, begin, end) + } + } + + impl bp_rialto_parachain::FromRialtoParachainInboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + ) -> Vec { + bridge_runtime_common::messages_api::inbound_message_details::< + Runtime, + WithRialtoParachainMessagesInstance, + >(lane, messages) + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata(extra: bool) -> ( + Vec, + Vec, + ) { + use frame_benchmarking::{list_benchmark, Benchmarking, BenchmarkList}; + use frame_support::traits::StorageInfoTrait; + + use pallet_bridge_messages::benchmarking::Pallet as MessagesBench; + use pallet_bridge_parachains::benchmarking::Pallet as ParachainsBench; + + let mut list = Vec::::new(); + + list_benchmark!(list, extra, pallet_bridge_messages, MessagesBench::); + list_benchmark!(list, extra, pallet_bridge_grandpa, BridgeRialtoGrandpa); + list_benchmark!(list, extra, pallet_bridge_parachains, ParachainsBench::); + list_benchmark!(list, extra, pallet_bridge_relayers, BridgeRelayers); + + let storage_info = AllPalletsWithSystem::storage_info(); + + return (list, storage_info) + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig, + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{Benchmarking, BenchmarkBatch, TrackedStorageKey, add_benchmark}; + + let whitelist: Vec = vec![ + // Block Number + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(), + // Execution Phase + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(), + // Event Count + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(), + // System Events + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(), + // Caller 0 Account + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da946c154ffd9992e395af90b5b13cc6f295c77033fce8a9045824a6690bbf99c6db269502f0a8d1d2a008542d5690a0749").to_vec().into(), + ]; + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + + use bridge_runtime_common::messages_benchmarking::{prepare_message_delivery_proof, prepare_message_proof}; + use pallet_bridge_messages::benchmarking::{ + Pallet as MessagesBench, + Config as MessagesConfig, + MessageDeliveryProofParams, + MessageProofParams, + }; + use pallet_bridge_parachains::benchmarking::{ + Pallet as ParachainsBench, + Config as ParachainsConfig, + }; + use rialto_messages::WithRialtoMessageBridge; + + impl MessagesConfig for Runtime { + fn bridged_relayer_id() -> Self::InboundRelayer { + [0u8; 32].into() + } + + fn endow_account(account: &Self::AccountId) { + pallet_balances::Pallet::::make_free_balance_be( + account, + Balance::MAX / 100, + ); + } + + fn prepare_message_proof( + params: MessageProofParams, + ) -> (rialto_messages::FromRialtoMessagesProof, Weight) { + prepare_message_proof::( + params, + ) + } + + fn prepare_message_delivery_proof( + params: MessageDeliveryProofParams, + ) -> rialto_messages::ToRialtoMessagesDeliveryProof { + prepare_message_delivery_proof::( + params, + ) + } + + fn is_message_dispatched(_nonce: bp_messages::MessageNonce) -> bool { + true + } + } + + impl ParachainsConfig for Runtime { + fn prepare_parachain_heads_proof( + parachains: &[bp_polkadot_core::parachains::ParaId], + parachain_head_size: u32, + proof_size: bp_runtime::StorageProofSize, + ) -> ( + pallet_bridge_parachains::RelayBlockNumber, + pallet_bridge_parachains::RelayBlockHash, + bp_polkadot_core::parachains::ParaHeadsProof, + Vec<(bp_polkadot_core::parachains::ParaId, bp_polkadot_core::parachains::ParaHash)>, + ) { + bridge_runtime_common::parachains_benchmarking::prepare_parachain_heads_proof::( + parachains, + parachain_head_size, + proof_size, + ) + } + } + + add_benchmark!( + params, + batches, + pallet_bridge_messages, + MessagesBench:: + ); + add_benchmark!(params, batches, pallet_bridge_grandpa, BridgeRialtoGrandpa); + add_benchmark!( + params, + batches, + pallet_bridge_parachains, + ParachainsBench:: + ); + add_benchmark!(params, batches, pallet_bridge_relayers, BridgeRelayers); + + Ok(batches) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn call_size() { + const BRIDGES_PALLETS_MAX_CALL_SIZE: usize = 200; + assert!( + core::mem::size_of::>() <= + BRIDGES_PALLETS_MAX_CALL_SIZE + ); + assert!( + core::mem::size_of::>() <= + BRIDGES_PALLETS_MAX_CALL_SIZE + ); + const MAX_CALL_SIZE: usize = 230; // value from polkadot-runtime tests + assert!(core::mem::size_of::() <= MAX_CALL_SIZE); + } +} diff --git a/bin/millau/runtime/src/rialto_messages.rs b/bin/millau/runtime/src/rialto_messages.rs new file mode 100644 index 00000000000..ff4b6ba1dda --- /dev/null +++ b/bin/millau/runtime/src/rialto_messages.rs @@ -0,0 +1,244 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything required to serve Millau <-> Rialto messages. + +use crate::{RialtoGrandpaInstance, Runtime, RuntimeCall, RuntimeOrigin}; + +use bp_messages::{ + source_chain::TargetHeaderChain, + target_chain::{ProvedMessages, SourceHeaderChain}, + InboundLaneData, LaneId, Message, MessageNonce, +}; +use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; +use bridge_runtime_common::messages::{self, MessageBridge}; +use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; + +/// Default lane that is used to send messages to Rialto. +pub const XCM_LANE: LaneId = [0, 0, 0, 0]; +/// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge +/// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual +/// tests, confirming that we don't break encoding somewhere between. +pub const BASE_XCM_WEIGHT_TWICE: u64 = 2 * crate::xcm_config::BASE_XCM_WEIGHT; + +parameter_types! { + /// Weight credit for our test messages. + /// + /// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge + /// (it is prepended with `UniversalOrigin` instruction). + pub const WeightCredit: Weight = Weight::from_ref_time(BASE_XCM_WEIGHT_TWICE); +} + +/// Message payload for Millau -> Rialto messages. +pub type ToRialtoMessagePayload = messages::source::FromThisChainMessagePayload; + +/// Message verifier for Millau -> Rialto messages. +pub type ToRialtoMessageVerifier = + messages::source::FromThisChainMessageVerifier; + +/// Message payload for Rialto -> Millau messages. +pub type FromRialtoMessagePayload = messages::target::FromBridgedChainMessagePayload; + +/// Messages proof for Rialto -> Millau messages. +pub type FromRialtoMessagesProof = messages::target::FromBridgedChainMessagesProof; + +/// Messages delivery proof for Millau -> Rialto messages. +pub type ToRialtoMessagesDeliveryProof = + messages::source::FromBridgedChainMessagesDeliveryProof; + +/// Call-dispatch based message dispatch for Rialto -> Millau messages. +pub type FromRialtoMessageDispatch = messages::target::FromBridgedChainMessageDispatch< + WithRialtoMessageBridge, + xcm_executor::XcmExecutor, + crate::xcm_config::XcmWeigher, + WeightCredit, +>; + +/// Maximal outbound payload size of Millau -> Rialto messages. +pub type ToRialtoMaximalOutboundPayloadSize = + messages::source::FromThisChainMaximalOutboundPayloadSize; + +/// Millau <-> Rialto message bridge. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct WithRialtoMessageBridge; + +impl MessageBridge for WithRialtoMessageBridge { + const THIS_CHAIN_ID: ChainId = MILLAU_CHAIN_ID; + const BRIDGED_CHAIN_ID: ChainId = RIALTO_CHAIN_ID; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME; + + type ThisChain = Millau; + type BridgedChain = Rialto; + type BridgedHeaderChain = + pallet_bridge_grandpa::GrandpaChainHeaders; +} + +/// Millau chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Millau; + +impl messages::UnderlyingChainProvider for Millau { + type Chain = bp_millau::Millau; +} + +impl messages::ThisChainWithMessages for Millau { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + + fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, _lane: &LaneId) -> bool { + true + } + + fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { + MessageNonce::MAX + } +} + +/// Rialto chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Rialto; + +impl messages::UnderlyingChainProvider for Rialto { + type Chain = bp_rialto::Rialto; +} + +impl messages::BridgedChainWithMessages for Rialto { + fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { + true + } +} + +impl TargetHeaderChain for Rialto { + type Error = &'static str; + // The proof is: + // - hash of the header this proof has been created with; + // - the storage proof or one or several keys; + // - id of the lane we prove state of. + type MessagesDeliveryProof = ToRialtoMessagesDeliveryProof; + + fn verify_message(payload: &ToRialtoMessagePayload) -> Result<(), Self::Error> { + messages::source::verify_chain_message::(payload) + } + + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData), Self::Error> { + messages::source::verify_messages_delivery_proof::(proof) + } +} + +impl SourceHeaderChain for Rialto { + type Error = &'static str; + // The proof is: + // - hash of the header this proof has been created with; + // - the storage proof or one or several keys; + // - id of the lane we prove messages for; + // - inclusive range of messages nonces that are proved. + type MessagesProof = FromRialtoMessagesProof; + + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result, Self::Error> { + messages::target::verify_messages_proof::(proof, messages_count) + .map_err(Into::into) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Runtime, WithRialtoMessagesInstance}; + + use bp_runtime::Chain; + use bridge_runtime_common::{ + assert_complete_bridge_types, + integrity::{ + assert_complete_bridge_constants, AssertBridgeMessagesPalletConstants, + AssertBridgePalletNames, AssertChainConstants, AssertCompleteBridgeConstants, + }, + messages, + }; + + #[test] + fn ensure_millau_message_lane_weights_are_correct() { + type Weights = pallet_bridge_messages::weights::BridgeWeight; + + pallet_bridge_messages::ensure_weights_are_correct::(); + + let max_incoming_message_proof_size = bp_rialto::EXTRA_STORAGE_PROOF_SIZE.saturating_add( + messages::target::maximal_incoming_message_size(bp_millau::Millau::max_extrinsic_size()), + ); + pallet_bridge_messages::ensure_able_to_receive_message::( + bp_millau::Millau::max_extrinsic_size(), + bp_millau::Millau::max_extrinsic_weight(), + max_incoming_message_proof_size, + messages::target::maximal_incoming_message_dispatch_weight( + bp_millau::Millau::max_extrinsic_weight(), + ), + ); + + let max_incoming_inbound_lane_data_proof_size = + bp_messages::InboundLaneData::<()>::encoded_size_hint_u32( + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX as _, + ); + pallet_bridge_messages::ensure_able_to_receive_confirmation::( + bp_millau::Millau::max_extrinsic_size(), + bp_millau::Millau::max_extrinsic_weight(), + max_incoming_inbound_lane_data_proof_size, + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + ); + } + + #[test] + fn ensure_bridge_integrity() { + assert_complete_bridge_types!( + runtime: Runtime, + with_bridged_chain_grandpa_instance: RialtoGrandpaInstance, + with_bridged_chain_messages_instance: WithRialtoMessagesInstance, + bridge: WithRialtoMessageBridge, + this_chain: bp_millau::Millau, + bridged_chain: bp_rialto::Rialto, + ); + + assert_complete_bridge_constants::< + Runtime, + RialtoGrandpaInstance, + WithRialtoMessagesInstance, + WithRialtoMessageBridge, + bp_millau::Millau, + >(AssertCompleteBridgeConstants { + this_chain_constants: AssertChainConstants { + block_length: bp_millau::BlockLength::get(), + block_weights: bp_millau::BlockWeights::get(), + }, + messages_pallet_constants: AssertBridgeMessagesPalletConstants { + max_unrewarded_relayers_in_bridged_confirmation_tx: + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + max_unconfirmed_messages_in_bridged_confirmation_tx: + bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + bridged_chain_id: bp_runtime::RIALTO_CHAIN_ID, + }, + pallet_names: AssertBridgePalletNames { + with_this_chain_messages_pallet_name: bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME, + with_bridged_chain_grandpa_pallet_name: bp_rialto::WITH_RIALTO_GRANDPA_PALLET_NAME, + with_bridged_chain_messages_pallet_name: + bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME, + }, + }); + } +} diff --git a/bin/millau/runtime/src/rialto_parachain_messages.rs b/bin/millau/runtime/src/rialto_parachain_messages.rs new file mode 100644 index 00000000000..b6e8f52d342 --- /dev/null +++ b/bin/millau/runtime/src/rialto_parachain_messages.rs @@ -0,0 +1,167 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything required to serve Millau <-> RialtoParachain messages. + +use crate::{Runtime, RuntimeCall, RuntimeOrigin, WithRialtoParachainsInstance}; + +use bp_messages::{ + source_chain::TargetHeaderChain, + target_chain::{ProvedMessages, SourceHeaderChain}, + InboundLaneData, LaneId, Message, MessageNonce, +}; +use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID}; +use bridge_runtime_common::messages::{self, MessageBridge}; +use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; + +/// Default lane that is used to send messages to Rialto parachain. +pub const XCM_LANE: LaneId = [0, 0, 0, 0]; +/// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge +/// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual +/// tests, confirming that we don't break encoding somewhere between. +pub const BASE_XCM_WEIGHT_TWICE: u64 = 2 * crate::xcm_config::BASE_XCM_WEIGHT; + +parameter_types! { + /// Weight credit for our test messages. + /// + /// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge + /// (it is prepended with `UniversalOrigin` instruction). + pub const WeightCredit: Weight = Weight::from_ref_time(BASE_XCM_WEIGHT_TWICE); +} + +/// Message payload for Millau -> RialtoParachain messages. +pub type ToRialtoParachainMessagePayload = messages::source::FromThisChainMessagePayload; + +/// Message verifier for Millau -> RialtoParachain messages. +pub type ToRialtoParachainMessageVerifier = + messages::source::FromThisChainMessageVerifier; + +/// Message payload for RialtoParachain -> Millau messages. +pub type FromRialtoParachainMessagePayload = + messages::target::FromBridgedChainMessagePayload; + +/// Messages proof for RialtoParachain -> Millau messages. +type FromRialtoParachainMessagesProof = + messages::target::FromBridgedChainMessagesProof; + +/// Messages delivery proof for Millau -> RialtoParachain messages. +type ToRialtoParachainMessagesDeliveryProof = + messages::source::FromBridgedChainMessagesDeliveryProof; + +/// Call-dispatch based message dispatch for RialtoParachain -> Millau messages. +pub type FromRialtoParachainMessageDispatch = messages::target::FromBridgedChainMessageDispatch< + WithRialtoParachainMessageBridge, + xcm_executor::XcmExecutor, + crate::xcm_config::XcmWeigher, + WeightCredit, +>; + +/// Maximal outbound payload size of Millau -> RialtoParachain messages. +pub type ToRialtoParachainMaximalOutboundPayloadSize = + messages::source::FromThisChainMaximalOutboundPayloadSize; + +/// Millau <-> RialtoParachain message bridge. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct WithRialtoParachainMessageBridge; + +impl MessageBridge for WithRialtoParachainMessageBridge { + const THIS_CHAIN_ID: ChainId = MILLAU_CHAIN_ID; + const BRIDGED_CHAIN_ID: ChainId = RIALTO_PARACHAIN_CHAIN_ID; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME; + + type ThisChain = Millau; + type BridgedChain = RialtoParachain; + type BridgedHeaderChain = pallet_bridge_parachains::ParachainHeaders< + Runtime, + WithRialtoParachainsInstance, + bp_rialto_parachain::RialtoParachain, + >; +} + +/// Millau chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Millau; + +impl messages::UnderlyingChainProvider for Millau { + type Chain = bp_millau::Millau; +} + +impl messages::ThisChainWithMessages for Millau { + type RuntimeCall = RuntimeCall; + type RuntimeOrigin = RuntimeOrigin; + + fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, _lane: &LaneId) -> bool { + true + } + + fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { + MessageNonce::MAX + } +} + +/// RialtoParachain chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct RialtoParachain; + +impl messages::UnderlyingChainProvider for RialtoParachain { + type Chain = bp_rialto_parachain::RialtoParachain; +} + +impl messages::BridgedChainWithMessages for RialtoParachain { + fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { + true + } +} + +impl TargetHeaderChain for RialtoParachain { + type Error = &'static str; + // The proof is: + // - hash of the header this proof has been created with; + // - the storage proof or one or several keys; + // - id of the lane we prove state of. + type MessagesDeliveryProof = ToRialtoParachainMessagesDeliveryProof; + + fn verify_message(payload: &ToRialtoParachainMessagePayload) -> Result<(), Self::Error> { + messages::source::verify_chain_message::(payload) + } + + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData), Self::Error> { + messages::source::verify_messages_delivery_proof::(proof) + } +} + +impl SourceHeaderChain for RialtoParachain { + type Error = &'static str; + // The proof is: + // - hash of the header this proof has been created with; + // - the storage proof or one or several keys; + // - id of the lane we prove messages for; + // - inclusive range of messages nonces that are proved. + type MessagesProof = FromRialtoParachainMessagesProof; + + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result, Self::Error> { + messages::target::verify_messages_proof::( + proof, + messages_count, + ) + .map_err(Into::into) + } +} diff --git a/bin/millau/runtime/src/xcm_config.rs b/bin/millau/runtime/src/xcm_config.rs new file mode 100644 index 00000000000..b5be117ac77 --- /dev/null +++ b/bin/millau/runtime/src/xcm_config.rs @@ -0,0 +1,327 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! XCM configurations for the Millau runtime. + +use super::{ + rialto_messages::{WithRialtoMessageBridge, XCM_LANE}, + rialto_parachain_messages::{WithRialtoParachainMessageBridge, XCM_LANE as XCM_LANE_PARACHAIN}, + AccountId, AllPalletsWithSystem, Balances, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + WithRialtoMessagesInstance, WithRialtoParachainMessagesInstance, XcmPallet, +}; +use bp_messages::LaneId; +use bp_millau::WeightToFee; +use bp_rialto_parachain::RIALTO_PARACHAIN_ID; +use bridge_runtime_common::{ + messages::source::{XcmBridge, XcmBridgeAdapter}, + CustomNetworkId, +}; +use frame_support::{ + parameter_types, + traits::{Everything, Nothing}, +}; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, + CurrencyAdapter as XcmCurrencyAdapter, IsConcrete, MintLocation, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, +}; + +parameter_types! { + /// The location of the `MLAU` token, from the context of this chain. Since this token is native to this + /// chain, we make it synonymous with it and thus it is the `Here` location, which means "equivalent to + /// the context". + pub const TokenLocation: MultiLocation = Here.into_location(); + /// The Millau network ID. + pub const ThisNetwork: NetworkId = CustomNetworkId::Millau.as_network_id(); + /// The Rialto network ID. + pub const RialtoNetwork: NetworkId = CustomNetworkId::Rialto.as_network_id(); + /// The RialtoParachain network ID. + pub const RialtoParachainNetwork: NetworkId = CustomNetworkId::RialtoParachain.as_network_id(); + + /// Our XCM location ancestry - i.e. our location within the Consensus Universe. + /// + /// Since Kusama is a top-level relay-chain with its own consensus, it's just our network ID. + pub UniversalLocation: InteriorMultiLocation = ThisNetwork::get().into(); + /// The check account, which holds any native assets that have been teleported out and not back in (yet). + pub CheckAccount: (AccountId, MintLocation) = (XcmPallet::check_account(), MintLocation::Local); +} + +/// The canonical means of converting a `MultiLocation` into an `AccountId`, used when we want to +/// determine the sovereign account controlled by a location. +pub type SovereignAccountOf = ( + // We can directly alias an `AccountId32` into a local account. + AccountId32Aliases, +); + +/// Our asset transactor. This is what allows us to interest with the runtime facilities from the +/// point of view of XCM-only concepts like `MultiLocation` and `MultiAsset`. +/// +/// Ours is only aware of the Balances pallet, which is mapped to `TokenLocation`. +pub type LocalAssetTransactor = XcmCurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // We can convert the MultiLocations with our converter above: + SovereignAccountOf, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We track our teleports in/out to keep total issuance correct. + CheckAccount, +>; + +/// The means that we convert the XCM message origin location into a local dispatch origin. +type LocalOriginConverter = ( + // A `Signed` origin of the sovereign account that the original location controls. + SovereignSignedViaLocation, + // The AccountId32 location type can be expressed natively as a `Signed` origin. + SignedAccountId32AsNative, +); + +/// The amount of weight an XCM operation takes. This is a safe overestimate. +pub const BASE_XCM_WEIGHT: u64 = 1_000_000_000; + +parameter_types! { + /// The amount of weight an XCM operation takes. This is a safe overestimate. + pub const BaseXcmWeight: u64 = BASE_XCM_WEIGHT; + /// Maximum number of instructions in a single XCM fragment. A sanity check against weight + /// calculations getting too crazy. + pub const MaxInstructions: u32 = 100; +} + +/// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our +/// individual routers. +pub type XcmRouter = ( + // Router to send messages to Rialto. + XcmBridgeAdapter, + // Router to send messages to RialtoParachains. + XcmBridgeAdapter, +); + +parameter_types! { + pub const MaxAssetsIntoHolding: u32 = 64; +} + +/// The barriers one of which must be passed for an XCM message to be executed. +pub type Barrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Expected responses are OK. + AllowKnownQueryResponses, +); + +/// XCM weigher type. +pub type XcmWeigher = xcm_builder::FixedWeightBounds; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = XcmWeigher; + // The weight trader piggybacks on the existing transaction-fee conversion logic. + type Trader = UsingComponents; + type ResponseHandler = XcmPallet; + type AssetTrap = XcmPallet; + type AssetLocker = (); + type AssetExchanger = (); + type AssetClaims = XcmPallet; + type SubscriptionService = XcmPallet; + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; +} + +/// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior +/// location of this chain. +pub type LocalOriginToLocation = ( + // Usual Signed origin to be used in XCM as a corresponding AccountId32 + SignedToAccountId32, +); + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + // We don't allow any messages to be sent via the transaction yet. This is basically safe to + // enable, (safe the possibility of someone spamming the parachain if they're willing to pay + // the DOT to send from the Relay-chain). But it's useless until we bring in XCM v3 which will + // make `DescendOrigin` a bit more useful. + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = XcmRouter; + // Anyone can execute XCM messages locally. + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = xcm_executor::XcmExecutor; + // Anyone is able to use teleportation regardless of who they are and what they want to + // teleport. + type XcmTeleportFilter = Everything; + // Anyone is able to use reserve transfers regardless of who they are and what they want to + // transfer. + type XcmReserveTransferFilter = Everything; + type Weigher = XcmWeigher; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = SovereignAccountOf; + type MaxLockers = frame_support::traits::ConstU32<8>; +} + +/// With-Rialto bridge. +pub struct ToRialtoBridge; + +impl XcmBridge for ToRialtoBridge { + type MessageBridge = WithRialtoMessageBridge; + type MessageSender = pallet_bridge_messages::Pallet; + + fn universal_location() -> InteriorMultiLocation { + UniversalLocation::get() + } + + fn verify_destination(dest: &MultiLocation) -> bool { + matches!(*dest, MultiLocation { parents: 1, interior: X1(GlobalConsensus(r)) } if r == RialtoNetwork::get()) + } + + fn build_destination() -> MultiLocation { + let dest: InteriorMultiLocation = RialtoNetwork::get().into(); + let here = UniversalLocation::get(); + dest.relative_to(&here) + } + + fn xcm_lane() -> LaneId { + XCM_LANE + } +} + +/// With-RialtoParachain bridge. +pub struct ToRialtoParachainBridge; + +impl XcmBridge for ToRialtoParachainBridge { + type MessageBridge = WithRialtoParachainMessageBridge; + type MessageSender = + pallet_bridge_messages::Pallet; + + fn universal_location() -> InteriorMultiLocation { + UniversalLocation::get() + } + + fn verify_destination(dest: &MultiLocation) -> bool { + matches!(*dest, MultiLocation { parents: 1, interior: X2(GlobalConsensus(r), Parachain(RIALTO_PARACHAIN_ID)) } if r == RialtoNetwork::get()) + } + + fn build_destination() -> MultiLocation { + let dest: InteriorMultiLocation = RialtoParachainNetwork::get().into(); + let here = UniversalLocation::get(); + dest.relative_to(&here) + } + + fn xcm_lane() -> LaneId { + XCM_LANE_PARACHAIN + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::rialto_messages::WeightCredit; + use bp_messages::{ + target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, + MessageKey, + }; + use bp_runtime::messages::MessageDispatchResult; + use bridge_runtime_common::messages::target::FromBridgedChainMessageDispatch; + use codec::Encode; + + fn new_test_ext() -> sp_io::TestExternalities { + sp_io::TestExternalities::new( + frame_system::GenesisConfig::default().build_storage::().unwrap(), + ) + } + + #[test] + fn xcm_messages_are_sent_using_bridge_router() { + new_test_ext().execute_with(|| { + let xcm: Xcm<()> = vec![Instruction::Trap(42)].into(); + let expected_fee = MultiAssets::from((Here, 1_000_000_u128)); + let expected_hash = + ([0u8, 0u8, 0u8, 0u8], 1u64).using_encoded(sp_io::hashing::blake2_256); + + // message 1 to Rialto + let dest = (Parent, X1(GlobalConsensus(RialtoNetwork::get()))); + let send_result = send_xcm::(dest.into(), xcm.clone()); + assert_eq!(send_result, Ok((expected_hash, expected_fee.clone()))); + + // message 2 to RialtoParachain (expected hash is the same, since other lane is used) + let dest = + (Parent, X2(GlobalConsensus(RialtoNetwork::get()), Parachain(RIALTO_PARACHAIN_ID))); + let send_result = send_xcm::(dest.into(), xcm); + assert_eq!(send_result, Ok((expected_hash, expected_fee))); + }) + } + + #[test] + fn xcm_messages_from_rialto_are_dispatched() { + type XcmExecutor = xcm_executor::XcmExecutor; + type MessageDispatcher = FromBridgedChainMessageDispatch< + WithRialtoMessageBridge, + XcmExecutor, + XcmWeigher, + WeightCredit, + >; + + new_test_ext().execute_with(|| { + let location: MultiLocation = + (Parent, X1(GlobalConsensus(RialtoNetwork::get()))).into(); + let xcm: Xcm = vec![Instruction::Trap(42)].into(); + + let mut incoming_message = DispatchMessage { + key: MessageKey { lane_id: [0, 0, 0, 0], nonce: 1 }, + data: DispatchMessageData { payload: Ok((location, xcm).into()) }, + }; + + let dispatch_weight = MessageDispatcher::dispatch_weight(&mut incoming_message); + assert_eq!( + dispatch_weight, + frame_support::weights::Weight::from_ref_time(1_000_000_000) + ); + + let dispatch_result = + MessageDispatcher::dispatch(&AccountId::from([0u8; 32]), incoming_message); + assert_eq!( + dispatch_result, + MessageDispatchResult { + unspent_weight: frame_support::weights::Weight::from_ref_time(0), + dispatch_fee_paid_during_dispatch: false, + dispatch_level_result: (), + } + ); + }) + } +} diff --git a/bin/rialto-parachain/node/Cargo.toml b/bin/rialto-parachain/node/Cargo.toml new file mode 100644 index 00000000000..03d1a28c6a4 --- /dev/null +++ b/bin/rialto-parachain/node/Cargo.toml @@ -0,0 +1,84 @@ +[package] +name = "rialto-parachain-collator" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +repository = "https://github.com/paritytech/parity-bridges-common/" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[build-dependencies] +substrate-build-script-utils = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[[bin]] +name = 'rialto-parachain-collator' + +[features] +default = [] +runtime-benchmarks = ['rialto-parachain-runtime/runtime-benchmarks'] + +[dependencies] +clap = { version = "4.0.9", features = ["derive"] } +log = '0.4.17' +codec = { package = 'parity-scale-codec', version = '3.1.5' } +serde = { version = '1.0', features = ['derive'] } + +# RPC related Dependencies +jsonrpsee = { version = "0.15.1", features = ["server"] } + +# Local Dependencies +rialto-parachain-runtime = { path = '../runtime' } + +# Substrate Dependencies +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } + +pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } + +substrate-frame-rpc-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +substrate-prometheus-endpoint = { git = "https://github.com/paritytech/substrate", branch = "master" } + +## Substrate Client Dependencies +sc-basic-authorship = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-chain-spec = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-network = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-rpc-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-service = { git = "https://github.com/paritytech/substrate", branch = "master", features = ['wasmtime'] } +sc-sysinfo = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-telemetry = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-tracing = { git = "https://github.com/paritytech/substrate", branch = "master" } + +## Substrate Primitive Dependencies +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-session = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master" } + +# Cumulus dependencies +cumulus-client-consensus-aura = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-client-consensus-common = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-client-cli = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-client-network = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-client-service = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-primitives-parachain-inherent = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-relay-chain-interface = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-relay-chain-inprocess-interface = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-relay-chain-minimal-node = { git = "https://github.com/paritytech/cumulus", branch = "master" } + +# Polkadot dependencies +polkadot-cli = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "master" } diff --git a/bin/rialto-parachain/node/build.rs b/bin/rialto-parachain/node/build.rs new file mode 100644 index 00000000000..8ba8a31e9a7 --- /dev/null +++ b/bin/rialto-parachain/node/build.rs @@ -0,0 +1,22 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; + +fn main() { + generate_cargo_keys(); + rerun_if_git_head_changed(); +} diff --git a/bin/rialto-parachain/node/src/chain_spec.rs b/bin/rialto-parachain/node/src/chain_spec.rs new file mode 100644 index 00000000000..bfce4f717c6 --- /dev/null +++ b/bin/rialto-parachain/node/src/chain_spec.rs @@ -0,0 +1,198 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use cumulus_primitives_core::ParaId; +use rialto_parachain_runtime::{AccountId, AuraId, BridgeMillauMessagesConfig, Signature}; +use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; +use sc_service::ChainType; +use serde::{Deserialize, Serialize}; +use sp_core::{sr25519, Pair, Public}; +use sp_runtime::traits::{IdentifyAccount, Verify}; + +/// "Names" of the authorities accounts at local testnet. +const LOCAL_AUTHORITIES_ACCOUNTS: [&str; 2] = ["Alice", "Bob"]; +/// "Names" of the authorities accounts at development testnet. +const DEV_AUTHORITIES_ACCOUNTS: [&str; 2] = LOCAL_AUTHORITIES_ACCOUNTS; +/// "Names" of all possible authorities accounts. +const ALL_AUTHORITIES_ACCOUNTS: [&str; 2] = LOCAL_AUTHORITIES_ACCOUNTS; +/// "Name" of the `sudo` account. +const SUDO_ACCOUNT: &str = "Sudo"; +/// "Name" of the account, which owns the with-Millau messages pallet. +const MILLAU_MESSAGES_PALLET_OWNER: &str = "Millau.MessagesOwner"; + +/// Specialized `ChainSpec` for the normal parachain runtime. +pub type ChainSpec = + sc_service::GenericChainSpec; + +/// Helper function to generate a crypto pair from seed +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{seed}"), None) + .expect("static values are valid; qed") + .public() +} + +/// The extensions for the [`ChainSpec`]. +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension, +)] +#[serde(deny_unknown_fields)] +pub struct Extensions { + /// The relay chain of the Parachain. + pub relay_chain: String, + /// The id of the Parachain. + pub para_id: u32, +} + +impl Extensions { + /// Try to get the extension from the given `ChainSpec`. + pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { + sc_chain_spec::get_extension(chain_spec.extensions()) + } +} + +type AccountPublic = ::Signer; + +/// Helper function to generate an account ID from seed +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// We're using the same set of endowed accounts on all RialtoParachain chains (dev/local) to make +/// sure that all accounts, required for bridge to be functional (e.g. relayers fund account, +/// accounts used by relayers in our test deployments, accounts used for demonstration +/// purposes), are all available on these chains. +fn endowed_accounts() -> Vec { + let all_authorities = ALL_AUTHORITIES_ACCOUNTS.iter().flat_map(|x| { + [ + get_account_id_from_seed::(x), + get_account_id_from_seed::(&format!("{x}//stash")), + ] + }); + vec![ + // Sudo account + get_account_id_from_seed::(SUDO_ACCOUNT), + // Regular (unused) accounts + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + // Accounts, used by RialtoParachain<>Millau bridge + get_account_id_from_seed::(MILLAU_MESSAGES_PALLET_OWNER), + get_account_id_from_seed::("Millau.HeadersAndMessagesRelay1"), + get_account_id_from_seed::("Millau.HeadersAndMessagesRelay2"), + get_account_id_from_seed::("Millau.MessagesSender"), + ] + .into_iter() + .chain(all_authorities) + .collect() +} + +pub fn development_config(id: ParaId) -> ChainSpec { + // Give your base currency a unit name and decimal places + let mut properties = sc_chain_spec::Properties::new(); + properties.insert("tokenSymbol".into(), "UNIT".into()); + properties.insert("tokenDecimals".into(), 12.into()); + + ChainSpec::from_genesis( + // Name + "Development", + // ID + "dev", + ChainType::Local, + move || { + testnet_genesis( + get_account_id_from_seed::(SUDO_ACCOUNT), + DEV_AUTHORITIES_ACCOUNTS.into_iter().map(get_from_seed::).collect(), + endowed_accounts(), + id, + ) + }, + vec![], + None, + None, + None, + None, + Extensions { + relay_chain: "rococo-local".into(), // You MUST set this to the correct network! + para_id: id.into(), + }, + ) +} + +pub fn local_testnet_config(id: ParaId) -> ChainSpec { + // Give your base currency a unit name and decimal places + let mut properties = sc_chain_spec::Properties::new(); + properties.insert("tokenSymbol".into(), "UNIT".into()); + properties.insert("tokenDecimals".into(), 12.into()); + + ChainSpec::from_genesis( + // Name + "Local Testnet", + // ID + "local_testnet", + ChainType::Local, + move || { + testnet_genesis( + get_account_id_from_seed::(SUDO_ACCOUNT), + LOCAL_AUTHORITIES_ACCOUNTS.into_iter().map(get_from_seed::).collect(), + endowed_accounts(), + id, + ) + }, + Vec::new(), + None, + None, + None, + None, + Extensions { + relay_chain: "rococo-local".into(), // You MUST set this to the correct network! + para_id: id.into(), + }, + ) +} + +fn testnet_genesis( + root_key: AccountId, + initial_authorities: Vec, + endowed_accounts: Vec, + id: ParaId, +) -> rialto_parachain_runtime::GenesisConfig { + rialto_parachain_runtime::GenesisConfig { + system: rialto_parachain_runtime::SystemConfig { + code: rialto_parachain_runtime::WASM_BINARY + .expect("WASM binary was not build, please build it!") + .to_vec(), + }, + balances: rialto_parachain_runtime::BalancesConfig { + balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), + }, + sudo: rialto_parachain_runtime::SudoConfig { key: Some(root_key) }, + parachain_info: rialto_parachain_runtime::ParachainInfoConfig { parachain_id: id }, + aura: rialto_parachain_runtime::AuraConfig { authorities: initial_authorities }, + aura_ext: Default::default(), + bridge_millau_messages: BridgeMillauMessagesConfig { + owner: Some(get_account_id_from_seed::(MILLAU_MESSAGES_PALLET_OWNER)), + ..Default::default() + }, + } +} diff --git a/bin/rialto-parachain/node/src/cli.rs b/bin/rialto-parachain/node/src/cli.rs new file mode 100644 index 00000000000..51fa1e9776c --- /dev/null +++ b/bin/rialto-parachain/node/src/cli.rs @@ -0,0 +1,140 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::chain_spec; +use clap::Parser; +use std::path::PathBuf; + +/// Sub-commands supported by the collator. +#[derive(Debug, Parser)] +pub enum Subcommand { + /// Export the genesis state of the parachain. + #[clap(name = "export-genesis-state")] + ExportGenesisState(ExportGenesisStateCommand), + + /// Export the genesis wasm of the parachain. + #[clap(name = "export-genesis-wasm")] + ExportGenesisWasm(ExportGenesisWasmCommand), + + /// Build a chain specification. + BuildSpec(sc_cli::BuildSpecCmd), + + /// Validate blocks. + CheckBlock(sc_cli::CheckBlockCmd), + + /// Export blocks. + ExportBlocks(sc_cli::ExportBlocksCmd), + + /// Export the state of a given block into a chain spec. + ExportState(sc_cli::ExportStateCmd), + + /// Import blocks. + ImportBlocks(sc_cli::ImportBlocksCmd), + + /// Remove the whole chain. + PurgeChain(cumulus_client_cli::PurgeChainCmd), + + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), + + /// The custom benchmark subcommand benchmarking runtime pallets. + #[clap(subcommand)] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), +} + +/// Command for exporting the genesis state of the parachain +#[derive(Debug, Parser)] +pub struct ExportGenesisStateCommand { + /// Output file name or stdout if unspecified. + #[clap(action)] + pub output: Option, + + /// Id of the parachain this state is for. + /// + /// Default: 100 + #[clap(long, conflicts_with = "chain")] + pub parachain_id: Option, + + /// Write output in binary. Default is to write in hex. + #[clap(short, long)] + pub raw: bool, + + /// The name of the chain for that the genesis state should be exported. + #[clap(long, conflicts_with = "parachain-id")] + pub chain: Option, +} + +/// Command for exporting the genesis wasm file. +#[derive(Debug, Parser)] +pub struct ExportGenesisWasmCommand { + /// Output file name or stdout if unspecified. + #[clap(action)] + pub output: Option, + + /// Write output in binary. Default is to write in hex. + #[clap(short, long)] + pub raw: bool, + + /// The name of the chain for that the genesis wasm file should be exported. + #[clap(long)] + pub chain: Option, +} + +#[derive(Debug, Parser)] +#[clap( + propagate_version = true, + args_conflicts_with_subcommands = true, + subcommand_negates_reqs = true +)] +pub struct Cli { + #[clap(subcommand)] + pub subcommand: Option, + + #[clap(long)] + pub parachain_id: Option, + + #[clap(flatten)] + pub run: cumulus_client_cli::RunCmd, + + /// Relaychain arguments + #[clap(raw = true)] + pub relaychain_args: Vec, +} + +#[derive(Debug)] +pub struct RelayChainCli { + /// The actual relay chain CLI object. + pub base: polkadot_cli::RunCmd, + + /// Optional chain id that should be passed to the relay chain. + pub chain_id: Option, + + /// The base path that should be used by the relay chain. + pub base_path: Option, +} + +impl RelayChainCli { + /// Parse the relay chain CLI parameters using the para chain `Configuration`. + pub fn new<'a>( + para_config: &sc_service::Configuration, + relay_chain_args: impl Iterator, + ) -> Self { + let extension = chain_spec::Extensions::try_get(&*para_config.chain_spec); + let chain_id = extension.map(|e| e.relay_chain.clone()); + let base_path = para_config.base_path.as_ref().map(|x| x.path().join("rialto-bridge-node")); + Self { base_path, chain_id, base: polkadot_cli::RunCmd::parse_from(relay_chain_args) } + } +} diff --git a/bin/rialto-parachain/node/src/command.rs b/bin/rialto-parachain/node/src/command.rs new file mode 100644 index 00000000000..dd9e95abbe5 --- /dev/null +++ b/bin/rialto-parachain/node/src/command.rs @@ -0,0 +1,446 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{ + chain_spec, + cli::{Cli, RelayChainCli, Subcommand}, + service::{new_partial, ParachainRuntimeExecutor}, +}; +use codec::Encode; +use cumulus_client_cli::generate_genesis_block; +use cumulus_primitives_core::ParaId; +use frame_benchmarking_cli::BenchmarkCmd; +use log::info; +use rialto_parachain_runtime::{Block, RuntimeApi}; +use sc_cli::{ + ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, + NetworkParams, Result, RuntimeVersion, SharedParams, SubstrateCli, +}; +use sc_service::config::{BasePath, PrometheusConfig}; +use sp_core::hexdisplay::HexDisplay; +use sp_runtime::traits::{AccountIdConversion, Block as BlockT}; +use std::{io::Write, net::SocketAddr}; + +fn load_spec( + id: &str, + para_id: ParaId, +) -> std::result::Result, String> { + Ok(match id { + "dev" => Box::new(chain_spec::development_config(para_id)), + "" | "local" => Box::new(chain_spec::local_testnet_config(para_id)), + path => Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?), + }) +} + +impl SubstrateCli for Cli { + fn impl_name() -> String { + "Parachain Collator Template".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + format!( + "Parachain Collator Template\n\nThe command-line arguments provided first will be \ + passed to the parachain node, while the arguments provided after -- will be passed \ + to the relaychain node.\n\n\ + {} [parachain-args] -- [relaychain-args]", + Self::executable_name() + ) + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/substrate-developer-hub/substrate-parachain-template/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2017 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + load_spec(id, self.parachain_id.unwrap_or(2000).into()) + } + + fn native_runtime_version(_: &Box) -> &'static RuntimeVersion { + &rialto_parachain_runtime::VERSION + } +} + +impl SubstrateCli for RelayChainCli { + fn impl_name() -> String { + "Parachain Collator Template".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + "Parachain Collator Template\n\nThe command-line arguments provided first will be \ + passed to the parachain node, while the arguments provided after -- will be passed \ + to the relaychain node.\n\n\ + parachain-collator [parachain-args] -- [relaychain-args]" + .into() + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/substrate-developer-hub/substrate-parachain-template/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2017 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id) + } + + fn native_runtime_version(chain_spec: &Box) -> &'static RuntimeVersion { + polkadot_cli::Cli::native_runtime_version(chain_spec) + } +} + +fn extract_genesis_wasm(chain_spec: &dyn sc_service::ChainSpec) -> Result> { + let mut storage = chain_spec.build_storage()?; + + storage + .top + .remove(sp_core::storage::well_known_keys::CODE) + .ok_or_else(|| "Could not find wasm file in genesis state!".into()) +} + +macro_rules! construct_async_run { + (|$components:ident, $cli:ident, $cmd:ident, $config:ident| $( $code:tt )* ) => {{ + let runner = $cli.create_runner($cmd)?; + runner.async_run(|$config| { + let $components = new_partial::< + RuntimeApi, + ParachainRuntimeExecutor, + _ + >( + &$config, + crate::service::parachain_build_import_queue, + )?; + let task_manager = $components.task_manager; + { $( $code )* }.map(|v| (v, task_manager)) + }) + }} +} + +/// Parse command line arguments into service configuration. +pub fn run() -> Result<()> { + let cli = Cli::from_args(); + sp_core::crypto::set_default_ss58_version(sp_core::crypto::Ss58AddressFormat::custom( + rialto_parachain_runtime::SS58Prefix::get() as u16, + )); + + match &cli.subcommand { + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) + }, + Some(Subcommand::CheckBlock(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, components.import_queue)) + }) + }, + Some(Subcommand::ExportBlocks(cmd)) => { + construct_async_run!(|components, cli, cmd, config| Ok( + cmd.run(components.client, config.database) + )) + }, + Some(Subcommand::ExportState(cmd)) => { + construct_async_run!(|components, cli, cmd, config| Ok( + cmd.run(components.client, config.chain_spec) + )) + }, + Some(Subcommand::ImportBlocks(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, components.import_queue)) + }) + }, + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + + runner.sync_run(|config| { + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()].iter().chain(cli.relaychain_args.iter()), + ); + + let polkadot_config = SubstrateCli::create_configuration( + &polkadot_cli, + &polkadot_cli, + config.tokio_handle.clone(), + ) + .map_err(|err| format!("Relay chain argument error: {err}"))?; + + cmd.run(config, polkadot_config) + }) + }, + Some(Subcommand::Revert(cmd)) => { + construct_async_run!(|components, cli, cmd, config| Ok(cmd.run( + components.client, + components.backend, + None + ))) + }, + Some(Subcommand::ExportGenesisState(params)) => { + let mut builder = sc_cli::LoggerBuilder::new(""); + builder.with_profiling(sc_tracing::TracingReceiver::Log, ""); + let _ = builder.init(); + + let spec = load_spec( + ¶ms.chain.clone().unwrap_or_default(), + params.parachain_id.expect("Missing ParaId").into(), + )?; + let state_version = Cli::native_runtime_version(&spec).state_version(); + let block: Block = generate_genesis_block(&*spec, state_version)?; + let raw_header = block.header().encode(); + let output_buf = if params.raw { + raw_header + } else { + format!("0x{:?}", HexDisplay::from(&block.header().encode())).into_bytes() + }; + + if let Some(output) = ¶ms.output { + std::fs::write(output, output_buf)?; + } else { + std::io::stdout().write_all(&output_buf)?; + } + + Ok(()) + }, + Some(Subcommand::ExportGenesisWasm(params)) => { + let mut builder = sc_cli::LoggerBuilder::new(""); + builder.with_profiling(sc_tracing::TracingReceiver::Log, ""); + let _ = builder.init(); + + let raw_wasm_blob = + extract_genesis_wasm(&*cli.load_spec(¶ms.chain.clone().unwrap_or_default())?)?; + let output_buf = if params.raw { + raw_wasm_blob + } else { + format!("0x{:?}", HexDisplay::from(&raw_wasm_blob)).into_bytes() + }; + + if let Some(output) = ¶ms.output { + std::fs::write(output, output_buf)?; + } else { + std::io::stdout().write_all(&output_buf)?; + } + + Ok(()) + }, + Some(Subcommand::Benchmark(cmd)) => { + let runner = cli.create_runner(cmd)?; + match cmd { + BenchmarkCmd::Pallet(cmd) => + if cfg!(feature = "runtime-benchmarks") { + runner.sync_run(|config| cmd.run::(config)) + } else { + println!( + "Benchmarking wasn't enabled when building the node. \ + You can enable it with `--features runtime-benchmarks`." + ); + Ok(()) + }, + _ => Err("Unsupported benchmarking subcommand".into()), + } + }, + None => { + let runner = cli.create_runner(&cli.run.normalize())?; + let collator_options = cli.run.collator_options(); + + runner.run_node_until_exit(|config| async move { + let para_id = + chain_spec::Extensions::try_get(&*config.chain_spec).map(|e| e.para_id); + + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()].iter().chain(cli.relaychain_args.iter()), + ); + + let id = ParaId::from(cli.parachain_id.or(para_id).expect("Missing ParaId")); + + let parachain_account = + AccountIdConversion::::into_account_truncating(&id); + + let state_version = + RelayChainCli::native_runtime_version(&config.chain_spec).state_version(); + let block: Block = generate_genesis_block(&*config.chain_spec, state_version) + .map_err(|e| format!("{e:?}"))?; + let genesis_state = format!("0x{:?}", HexDisplay::from(&block.header().encode())); + + let polkadot_config = SubstrateCli::create_configuration( + &polkadot_cli, + &polkadot_cli, + config.tokio_handle.clone(), + ) + .map_err(|err| format!("Relay chain argument error: {err}"))?; + + info!("Parachain id: {:?}", id); + info!("Parachain Account: {}", parachain_account); + info!("Parachain genesis state: {}", genesis_state); + info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" }); + + crate::service::start_node(config, polkadot_config, collator_options, id) + .await + .map(|r| r.0) + .map_err(Into::into) + }) + }, + } +} + +impl DefaultConfigurationValues for RelayChainCli { + fn p2p_listen_port() -> u16 { + 30334 + } + + fn rpc_ws_listen_port() -> u16 { + 9945 + } + + fn rpc_http_listen_port() -> u16 { + 9934 + } + + fn prometheus_listen_port() -> u16 { + 9616 + } +} + +impl CliConfiguration for RelayChainCli { + fn shared_params(&self) -> &SharedParams { + self.base.base.shared_params() + } + + fn import_params(&self) -> Option<&ImportParams> { + self.base.base.import_params() + } + + fn network_params(&self) -> Option<&NetworkParams> { + self.base.base.network_params() + } + + fn keystore_params(&self) -> Option<&KeystoreParams> { + self.base.base.keystore_params() + } + + fn base_path(&self) -> Result> { + Ok(self + .shared_params() + .base_path()? + .or_else(|| self.base_path.clone().map(Into::into))) + } + + fn rpc_http(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_http(default_listen_port) + } + + fn rpc_ipc(&self) -> Result> { + self.base.base.rpc_ipc() + } + + fn rpc_ws(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_ws(default_listen_port) + } + + fn prometheus_config( + &self, + default_listen_port: u16, + chain_spec: &Box, + ) -> Result> { + self.base.base.prometheus_config(default_listen_port, chain_spec) + } + + fn init( + &self, + _support_url: &String, + _impl_version: &String, + _logger_hook: F, + _config: &sc_service::Configuration, + ) -> Result<()> + where + F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), + { + unreachable!("PolkadotCli is never initialized; qed"); + } + + fn chain_id(&self, is_dev: bool) -> Result { + let chain_id = self.base.base.chain_id(is_dev)?; + + Ok(if chain_id.is_empty() { self.chain_id.clone().unwrap_or_default() } else { chain_id }) + } + + fn role(&self, is_dev: bool) -> Result { + self.base.base.role(is_dev) + } + + fn transaction_pool(&self, is_dev: bool) -> Result { + self.base.base.transaction_pool(is_dev) + } + + fn rpc_methods(&self) -> Result { + self.base.base.rpc_methods() + } + + fn rpc_ws_max_connections(&self) -> Result> { + self.base.base.rpc_ws_max_connections() + } + + fn rpc_cors(&self, is_dev: bool) -> Result>> { + self.base.base.rpc_cors(is_dev) + } + + fn default_heap_pages(&self) -> Result> { + self.base.base.default_heap_pages() + } + + fn force_authoring(&self) -> Result { + self.base.base.force_authoring() + } + + fn disable_grandpa(&self) -> Result { + self.base.base.disable_grandpa() + } + + fn max_runtime_instances(&self) -> Result> { + self.base.base.max_runtime_instances() + } + + fn announce_block(&self) -> Result { + self.base.base.announce_block() + } + + fn telemetry_endpoints( + &self, + chain_spec: &Box, + ) -> Result> { + self.base.base.telemetry_endpoints(chain_spec) + } +} diff --git a/bin/rialto-parachain/node/src/lib.rs b/bin/rialto-parachain/node/src/lib.rs new file mode 100644 index 00000000000..3ec291596b7 --- /dev/null +++ b/bin/rialto-parachain/node/src/lib.rs @@ -0,0 +1,18 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +pub mod chain_spec; +pub mod service; diff --git a/bin/rialto-parachain/node/src/main.rs b/bin/rialto-parachain/node/src/main.rs new file mode 100644 index 00000000000..2b4e0b438d1 --- /dev/null +++ b/bin/rialto-parachain/node/src/main.rs @@ -0,0 +1,29 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate Parachain Node Template CLI + +#![warn(missing_docs)] + +mod chain_spec; +#[macro_use] +mod service; +mod cli; +mod command; + +fn main() -> sc_cli::Result<()> { + command::run() +} diff --git a/bin/rialto-parachain/node/src/service.rs b/bin/rialto-parachain/node/src/service.rs new file mode 100644 index 00000000000..7dc25f6550d --- /dev/null +++ b/bin/rialto-parachain/node/src/service.rs @@ -0,0 +1,540 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rialto parachain node service. +//! +//! The code is mostly copy of `polkadot-parachains/src/service.rs` file from Cumulus +//! repository with some parts removed. We have added two RPC extensions to the original +//! service: `pallet_transaction_payment_rpc::TransactionPaymentApi` and +//! `substrate_frame_rpc_system::SystemApi`. + +// std +use std::{sync::Arc, time::Duration}; + +// Local Runtime Types +use rialto_parachain_runtime::RuntimeApi; + +// Cumulus Imports +use cumulus_client_cli::CollatorOptions; +use cumulus_client_consensus_aura::{AuraConsensus, BuildAuraConsensusParams, SlotProportion}; +use cumulus_client_consensus_common::{ParachainBlockImport, ParachainConsensus}; +use cumulus_client_network::BlockAnnounceValidator; +use cumulus_client_service::{ + prepare_node_config, start_collator, start_full_node, StartCollatorParams, StartFullNodeParams, +}; +use cumulus_primitives_core::ParaId; +use cumulus_relay_chain_inprocess_interface::build_inprocess_relay_chain; +use cumulus_relay_chain_interface::{RelayChainError, RelayChainInterface, RelayChainResult}; +use cumulus_relay_chain_minimal_node::build_minimal_relay_chain_node; +use polkadot_service::CollatorPair; + +// Substrate Imports +use sc_executor::{NativeElseWasmExecutor, NativeExecutionDispatch}; +use sc_network::{NetworkBlock, NetworkService}; +use sc_service::{Configuration, PartialComponents, TFullBackend, TFullClient, TaskManager}; +use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle}; +use sp_api::ConstructRuntimeApi; +use sp_keystore::SyncCryptoStorePtr; +use sp_runtime::traits::BlakeTwo256; +use substrate_prometheus_endpoint::Registry; + +// Runtime type overrides +type BlockNumber = u32; +type Header = sp_runtime::generic::Header; +pub type Block = sp_runtime::generic::Block; +type Hash = sp_core::H256; + +pub type ParachainRuntimeExecutor = ExecutorDispatch; + +// Our native executor instance. +pub struct ExecutorDispatch; + +impl NativeExecutionDispatch for ExecutorDispatch { + type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; + + fn dispatch(method: &str, data: &[u8]) -> Option> { + rialto_parachain_runtime::api::dispatch(method, data) + } + + fn native_version() -> sc_executor::NativeVersion { + rialto_parachain_runtime::native_version() + } +} + +/// Starts a `ServiceBuilder` for a full service. +/// +/// Use this macro if you don't actually need the full service, but just the builder in order to +/// be able to perform chain operations. +#[allow(clippy::type_complexity)] +pub fn new_partial( + config: &Configuration, + build_import_queue: BIQ, +) -> Result< + PartialComponents< + TFullClient>, + TFullBackend, + (), + sc_consensus::DefaultImportQueue< + Block, + TFullClient>, + >, + sc_transaction_pool::FullPool< + Block, + TFullClient>, + >, + (Option, Option), + >, + sc_service::Error, +> +where + RuntimeApi: ConstructRuntimeApi>> + + Send + + Sync + + 'static, + RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue + + sp_api::Metadata + + sp_session::SessionKeys + + sp_api::ApiExt< + Block, + StateBackend = sc_client_api::StateBackendFor, Block>, + > + sp_offchain::OffchainWorkerApi + + sp_block_builder::BlockBuilder, + sc_client_api::StateBackendFor, Block>: sp_api::StateBackend, + Executor: NativeExecutionDispatch + 'static, + BIQ: FnOnce( + Arc>>, + &Configuration, + Option, + &TaskManager, + ) -> Result< + sc_consensus::DefaultImportQueue< + Block, + TFullClient>, + >, + sc_service::Error, + >, +{ + let telemetry = config + .telemetry_endpoints + .clone() + .filter(|x| !x.is_empty()) + .map(|endpoints| -> Result<_, sc_telemetry::Error> { + let worker = TelemetryWorker::new(16)?; + let telemetry = worker.handle().new_telemetry(endpoints); + Ok((worker, telemetry)) + }) + .transpose()?; + + let executor = sc_executor::NativeElseWasmExecutor::::new( + config.wasm_method, + config.default_heap_pages, + config.max_runtime_instances, + config.runtime_cache_size, + ); + + let (client, backend, keystore_container, task_manager) = + sc_service::new_full_parts::( + config, + telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), + executor, + )?; + let client = Arc::new(client); + + let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle()); + + let telemetry = telemetry.map(|(worker, telemetry)| { + task_manager.spawn_handle().spawn("telemetry", None, worker.run()); + telemetry + }); + + let transaction_pool = sc_transaction_pool::BasicPool::new_full( + config.transaction_pool.clone(), + config.role.is_authority().into(), + config.prometheus_registry(), + task_manager.spawn_essential_handle(), + client.clone(), + ); + + let import_queue = build_import_queue( + client.clone(), + config, + telemetry.as_ref().map(|telemetry| telemetry.handle()), + &task_manager, + )?; + + let params = PartialComponents { + backend, + client, + import_queue, + keystore_container, + task_manager, + transaction_pool, + select_chain: (), + other: (telemetry, telemetry_worker_handle), + }; + + Ok(params) +} + +async fn build_relay_chain_interface( + polkadot_config: Configuration, + parachain_config: &Configuration, + telemetry_worker_handle: Option, + task_manager: &mut TaskManager, + collator_options: CollatorOptions, + hwbench: Option, +) -> RelayChainResult<(Arc<(dyn RelayChainInterface + 'static)>, Option)> { + match collator_options.relay_chain_rpc_url { + Some(relay_chain_url) => + build_minimal_relay_chain_node(polkadot_config, task_manager, relay_chain_url).await, + None => build_inprocess_relay_chain( + polkadot_config, + parachain_config, + telemetry_worker_handle, + task_manager, + hwbench, + ), + } +} + +/// Start a node with the given parachain `Configuration` and relay chain `Configuration`. +/// +/// This is the actual implementation that is abstract over the executor and the runtime api. +#[sc_tracing::logging::prefix_logs_with("Parachain")] +async fn start_node_impl( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + id: ParaId, + rpc_ext_builder: RB, + build_import_queue: BIQ, + build_consensus: BIC, +) -> sc_service::error::Result<( + TaskManager, + Arc>>, +)> +where + RuntimeApi: ConstructRuntimeApi>> + + Send + + Sync + + 'static, + RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue + + sp_api::Metadata + + sp_session::SessionKeys + + sp_api::ApiExt< + Block, + StateBackend = sc_client_api::StateBackendFor, Block>, + > + sp_offchain::OffchainWorkerApi + + sp_block_builder::BlockBuilder + + cumulus_primitives_core::CollectCollationInfo, + sc_client_api::StateBackendFor, Block>: sp_api::StateBackend, + Executor: NativeExecutionDispatch + 'static, + RB: Fn( + sc_rpc_api::DenyUnsafe, + Arc>>, + Arc< + sc_transaction_pool::FullPool< + Block, + TFullClient>, + >, + >, + ) -> Result, sc_service::Error> + + Send + + Clone + + 'static, + BIQ: FnOnce( + Arc>>, + &Configuration, + Option, + &TaskManager, + ) -> Result< + sc_consensus::DefaultImportQueue< + Block, + TFullClient>, + >, + sc_service::Error, + >, + BIC: FnOnce( + Arc>>, + Option<&Registry>, + Option, + &TaskManager, + Arc, + Arc< + sc_transaction_pool::FullPool< + Block, + TFullClient>, + >, + >, + Arc>, + SyncCryptoStorePtr, + bool, + ) -> Result>, sc_service::Error>, +{ + let parachain_config = prepare_node_config(parachain_config); + + let params = new_partial::(¶chain_config, build_import_queue)?; + let (mut telemetry, telemetry_worker_handle) = params.other; + + let mut task_manager = params.task_manager; + let (relay_chain_interface, collator_key) = build_relay_chain_interface( + polkadot_config, + ¶chain_config, + telemetry_worker_handle, + &mut task_manager, + collator_options, + None, + ) + .await + .map_err(|e| match e { + RelayChainError::ServiceError(polkadot_service::Error::Sub(x)) => x, + s => s.to_string().into(), + })?; + + let client = params.client.clone(); + let backend = params.backend.clone(); + let block_announce_validator = BlockAnnounceValidator::new(relay_chain_interface.clone(), id); + + let force_authoring = parachain_config.force_authoring; + let validator = parachain_config.role.is_authority(); + let prometheus_registry = parachain_config.prometheus_registry().cloned(); + let transaction_pool = params.transaction_pool.clone(); + let import_queue = cumulus_client_service::SharedImportQueue::new(params.import_queue); + let (network, system_rpc_tx, tx_handler_controller, start_network) = + sc_service::build_network(sc_service::BuildNetworkParams { + config: ¶chain_config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + spawn_handle: task_manager.spawn_handle(), + import_queue: import_queue.clone(), + block_announce_validator_builder: Some(Box::new(|_| { + Box::new(block_announce_validator) + })), + warp_sync: None, + })?; + + let rpc_client = client.clone(); + let rpc_transaction_pool = transaction_pool.clone(); + let rpc_extensions_builder = Box::new(move |deny_unsafe, _| { + rpc_ext_builder(deny_unsafe, rpc_client.clone(), rpc_transaction_pool.clone()) + }); + + sc_service::spawn_tasks(sc_service::SpawnTasksParams { + rpc_builder: rpc_extensions_builder.clone(), + client: client.clone(), + transaction_pool: transaction_pool.clone(), + task_manager: &mut task_manager, + config: parachain_config, + keystore: params.keystore_container.sync_keystore(), + backend: backend.clone(), + network: network.clone(), + system_rpc_tx, + tx_handler_controller, + telemetry: telemetry.as_mut(), + })?; + + let announce_block = { + let network = network.clone(); + Arc::new(move |hash, data| network.announce_block(hash, data)) + }; + + let relay_chain_slot_duration = Duration::from_secs(6); + + if validator { + let parachain_consensus = build_consensus( + client.clone(), + prometheus_registry.as_ref(), + telemetry.as_ref().map(|t| t.handle()), + &task_manager, + relay_chain_interface.clone(), + transaction_pool, + network, + params.keystore_container.sync_keystore(), + force_authoring, + )?; + + let spawner = task_manager.spawn_handle(); + + let params = StartCollatorParams { + para_id: id, + block_status: client.clone(), + announce_block, + client: client.clone(), + task_manager: &mut task_manager, + relay_chain_interface, + spawner, + parachain_consensus, + import_queue, + collator_key: collator_key.expect("Command line arguments do not allow this. qed"), + relay_chain_slot_duration, + }; + + start_collator(params).await?; + } else { + let params = StartFullNodeParams { + client: client.clone(), + announce_block, + task_manager: &mut task_manager, + para_id: id, + relay_chain_interface, + relay_chain_slot_duration, + import_queue, + }; + + start_full_node(params)?; + } + + start_network.start_network(); + + Ok((task_manager, client)) +} + +/// Build the import queue for the the parachain runtime. +#[allow(clippy::type_complexity)] +pub fn parachain_build_import_queue( + client: Arc>>, + config: &Configuration, + telemetry: Option, + task_manager: &TaskManager, +) -> Result< + sc_consensus::DefaultImportQueue< + Block, + TFullClient>, + >, + sc_service::Error, +> { + let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; + + cumulus_client_consensus_aura::import_queue::< + sp_consensus_aura::sr25519::AuthorityPair, + _, + _, + _, + _, + _, + >(cumulus_client_consensus_aura::ImportQueueParams { + block_import: ParachainBlockImport::new(client.clone()), + client, + create_inherent_data_providers: move |_, _| async move { + let time = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *time, + slot_duration, + ); + + Ok((slot, time)) + }, + registry: config.prometheus_registry(), + spawner: &task_manager.spawn_essential_handle(), + telemetry, + }) + .map_err(Into::into) +} + +/// Start a normal parachain node. +pub async fn start_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + id: ParaId, +) -> sc_service::error::Result<( + TaskManager, + Arc>>, +)> { + start_node_impl::( + parachain_config, + polkadot_config, + collator_options, + id, + |_deny_unsafe, client, pool| { + use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; + use sc_rpc::DenyUnsafe; + use substrate_frame_rpc_system::{System, SystemApiServer}; + + let mut io = jsonrpsee::RpcModule::new(()); + let map_err = |e| sc_service::Error::Other(format!("{e}")); + io.merge(System::new(client.clone(), pool, DenyUnsafe::No).into_rpc()) + .map_err(map_err)?; + io.merge(TransactionPayment::new(client).into_rpc()).map_err(map_err)?; + Ok(io) + }, + parachain_build_import_queue, + |client, + prometheus_registry, + telemetry, + task_manager, + relay_chain_interface, + transaction_pool, + sync_oracle, + keystore, + force_authoring| { + let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; + + let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( + task_manager.spawn_handle(), + client.clone(), + transaction_pool, + prometheus_registry, + telemetry.clone(), + ); + + Ok(AuraConsensus::build::( + BuildAuraConsensusParams { + proposer_factory, + create_inherent_data_providers: move |_, (relay_parent, validation_data)| { + let relay_chain_interface = relay_chain_interface.clone(); + async move { + let parachain_inherent = + cumulus_primitives_parachain_inherent::ParachainInherentData::create_at( + relay_parent, + &relay_chain_interface, + &validation_data, + id, + ).await; + let time = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *time, + slot_duration, + ); + + let parachain_inherent = parachain_inherent.ok_or_else(|| { + Box::::from( + "Failed to create parachain inherent", + ) + })?; + Ok((slot, time, parachain_inherent)) + } + }, + block_import: ParachainBlockImport::new(client.clone()), + para_client: client, + backoff_authoring_blocks: Option::<()>::None, + sync_oracle, + keystore, + force_authoring, + slot_duration, + // We got around 500ms for proposing + block_proposal_slot_portion: SlotProportion::new(1f32 / 24f32), + telemetry, + max_block_proposal_slot_portion: None, + }, + )) + }, + ) + .await +} diff --git a/bin/rialto-parachain/runtime/Cargo.toml b/bin/rialto-parachain/runtime/Cargo.toml new file mode 100644 index 00000000000..467fbd3699a --- /dev/null +++ b/bin/rialto-parachain/runtime/Cargo.toml @@ -0,0 +1,136 @@ +[package] +name = "rialto-parachain-runtime" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +repository = "https://github.com/paritytech/parity-bridges-common/" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[build-dependencies] +substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[dependencies] +codec = { package = 'parity-scale-codec', version = '3.1.5', default-features = false, features = ['derive']} +hex-literal = "0.3" +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } + +# Bridge depedencies + +bp-messages = { path = "../../../primitives/messages", default-features = false } +bp-millau = { path = "../../../primitives/chain-millau", default-features = false } +bp-relayers = { path = "../../../primitives/relayers", default-features = false } +bp-runtime = { path = "../../../primitives/runtime", default-features = false } +bp-rialto-parachain = { path = "../../../primitives/chain-rialto-parachain", default-features = false } +bridge-runtime-common = { path = "../../runtime-common", default-features = false } +pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false } +pallet-bridge-messages = { path = "../../../modules/messages", default-features = false } +pallet-bridge-relayers = { path = "../../../modules/relayers", default-features = false } + +# Substrate Dependencies +## Substrate Primitive Dependencies +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +## Substrate FRAME Dependencies +frame-executive = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +## Substrate Pallet Dependencies +pallet-aura = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-randomness-collective-flip = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +# Cumulus Dependencies +cumulus-pallet-aura-ext = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } +cumulus-pallet-parachain-system = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } +cumulus-pallet-dmp-queue = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } +cumulus-pallet-xcm = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } +cumulus-pallet-xcmp-queue = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } +cumulus-primitives-timestamp = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } +parachain-info = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } + +# Polkadot Dependencies +polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + +[features] +default = ['std'] +runtime-benchmarks = [ + 'sp-runtime/runtime-benchmarks', + 'frame-benchmarking', + 'frame-support/runtime-benchmarks', + 'frame-system-benchmarking', + 'frame-system/runtime-benchmarks', + 'pallet-balances/runtime-benchmarks', + 'pallet-timestamp/runtime-benchmarks', +] +std = [ + "bp-messages/std", + "bp-millau/std", + "bp-relayers/std", + "bp-runtime/std", + "bp-rialto-parachain/std", + "bridge-runtime-common/std", + "codec/std", + "log/std", + "scale-info/std", + "sp-api/std", + "sp-std/std", + "sp-io/std", + "sp-core/std", + "sp-runtime/std", + "sp-version/std", + "sp-offchain/std", + "sp-session/std", + "sp-block-builder/std", + "sp-transaction-pool/std", + "sp-inherents/std", + "frame-support/std", + "frame-executive/std", + "frame-system/std", + "pallet-balances/std", + "pallet-bridge-grandpa/std", + "pallet-bridge-messages/std", + "pallet-bridge-relayers/std", + "pallet-randomness-collective-flip/std", + "pallet-timestamp/std", + "pallet-sudo/std", + "pallet-transaction-payment/std", + "pallet-xcm/std", + "parachain-info/std", + "polkadot-parachain/std", + "cumulus-pallet-aura-ext/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-pallet-xcm/std", + "cumulus-primitives-core/std", + "cumulus-primitives-timestamp/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", + "pallet-aura/std", + "sp-consensus-aura/std", +] diff --git a/bin/rialto-parachain/runtime/build.rs b/bin/rialto-parachain/runtime/build.rs new file mode 100644 index 00000000000..65095bd1b7e --- /dev/null +++ b/bin/rialto-parachain/runtime/build.rs @@ -0,0 +1,25 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() +} diff --git a/bin/rialto-parachain/runtime/src/lib.rs b/bin/rialto-parachain/runtime/src/lib.rs new file mode 100644 index 00000000000..f946abf4c1d --- /dev/null +++ b/bin/rialto-parachain/runtime/src/lib.rs @@ -0,0 +1,905 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! The Rialto parachain runtime. This can be compiled with `#[no_std]`, ready for Wasm. +//! +//! Originally a copy of runtime from https://github.com/substrate-developer-hub/substrate-parachain-template. + +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +use crate::millau_messages::{WithMillauMessageBridge, XCM_LANE}; + +use bridge_runtime_common::messages::source::{XcmBridge, XcmBridgeAdapter}; +use cumulus_pallet_parachain_system::AnyRelayNumber; +use sp_api::impl_runtime_apis; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{AccountIdLookup, Block as BlockT}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, +}; + +use sp_std::prelude::*; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +// A few exports that help ease life for downstream crates. +use bp_runtime::{HeaderId, HeaderIdProvider}; +pub use frame_support::{ + construct_runtime, + dispatch::DispatchClass, + match_types, parameter_types, + traits::{Everything, IsInVec, Nothing, Randomness}, + weights::{ + constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, + IdentityFee, Weight, + }, + StorageValue, +}; +pub use frame_system::{Call as SystemCall, EnsureRoot}; +pub use pallet_balances::Call as BalancesCall; +pub use pallet_sudo::Call as SudoCall; +pub use pallet_timestamp::Call as TimestampCall; +pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; +pub use sp_runtime::{MultiAddress, Perbill, Permill}; + +pub use bp_rialto_parachain::{ + AccountId, Balance, BlockLength, BlockNumber, BlockWeights, Hash, Hasher as Hashing, Header, + Index, Signature, MAXIMUM_BLOCK_WEIGHT, +}; + +pub use pallet_bridge_grandpa::Call as BridgeGrandpaCall; +pub use pallet_bridge_messages::Call as MessagesCall; +pub use pallet_xcm::Call as XcmCall; + +// Polkadot & XCM imports +use bridge_runtime_common::CustomNetworkId; +use pallet_xcm::XcmPassthrough; +use polkadot_parachain::primitives::Sibling; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, + EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + UsingComponents, +}; +use xcm_executor::{Config, XcmExecutor}; + +pub mod millau_messages; + +/// The address format for describing accounts. +pub type Address = MultiAddress; +/// Block type as expected by this runtime. +pub type Block = generic::Block; +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = generic::CheckedExtrinsic; +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + } +} + +/// This runtime version. +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("template-parachain"), + impl_name: create_runtime_str!("template-parachain"), + authoring_version: 1, + spec_version: 1, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 0, +}; + +/// This determines the average expected block time that we are targeting. +/// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. +/// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked +/// up by `pallet_aura` to implement `fn slot_duration()`. +/// +/// Change this to adjust the block time. +pub const MILLISECS_PER_BLOCK: u64 = 12000; + +pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + +pub const EPOCH_DURATION_IN_BLOCKS: u32 = 10 * MINUTES; + +// Time is measured by number of blocks. +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; +pub const DAYS: BlockNumber = HOURS * 24; + +// Unit = the base number of indivisible units for balances +pub const UNIT: Balance = 1_000_000_000_000; +pub const MILLIUNIT: Balance = 1_000_000_000; +pub const MICROUNIT: Balance = 1_000_000; + +// 1 in 4 blocks (on average, not counting collisions) will be primary babe blocks. +pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4); + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } +} + +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub const Version: RuntimeVersion = VERSION; + pub const SS58Prefix: u8 = 48; +} + +// Configure FRAME pallets to include in runtime. + +impl frame_system::Config for Runtime { + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type RuntimeCall = RuntimeCall; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = AccountIdLookup; + /// The index type for storing how many extrinsics an account has signed. + type Index = Index; + /// The index type for blocks. + type BlockNumber = BlockNumber; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = Hashing; + /// The header type. + type Header = generic::Header; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// Runtime version. + type Version = Version; + /// Converts a module to an index of this module in the runtime. + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + /// What to do if a new account is created. + type OnNewAccount = (); + /// What to do if an account is fully reaped from the system. + type OnKilledAccount = (); + /// The weight of database operations that the runtime can invoke. + type DbWeight = (); + /// The basic call filter to use in dispatchable. + type BaseCallFilter = Everything; + /// Weight information for the extrinsics of this pallet. + type SystemWeightInfo = (); + /// Block & extrinsics weights: base values and limits. + type BlockWeights = BlockWeights; + /// The maximum length of a block (in bytes). + type BlockLength = BlockLength; + /// This is used as an identifier of the chain. 42 is the generic substrate prefix. + type SS58Prefix = SS58Prefix; + /// The action to take on a Runtime Upgrade + type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const MinimumPeriod: u64 = SLOT_DURATION / 2; +} + +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the Unix epoch. + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +parameter_types! { + pub const ExistentialDeposit: u128 = MILLIUNIT; + pub const TransferFee: u128 = MILLIUNIT; + pub const CreationFee: u128 = MILLIUNIT; + pub const TransactionByteFee: u128 = MICROUNIT; + pub const OperationalFeeMultiplier: u8 = 5; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = pallet_balances::weights::SubstrateWeight; + type MaxLocks = MaxLocks; + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + +impl pallet_transaction_payment::Config for Runtime { + type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; + type OperationalFeeMultiplier = OperationalFeeMultiplier; + type WeightToFee = IdentityFee; + type LengthToFee = IdentityFee; + type FeeMultiplierUpdate = (); + type RuntimeEvent = RuntimeEvent; +} + +impl pallet_sudo::Config for Runtime { + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnSystemEvent = (); + type SelfParaId = parachain_info::Pallet; + type OutboundXcmpMessageSource = XcmpQueue; + type DmpMessageHandler = DmpQueue; + type ReservedDmpWeight = ReservedDmpWeight; + type XcmpMessageHandler = XcmpQueue; + type ReservedXcmpWeight = ReservedXcmpWeight; + type CheckAssociatedRelayNumber = AnyRelayNumber; +} + +impl parachain_info::Config for Runtime {} + +impl cumulus_pallet_aura_ext::Config for Runtime {} + +impl pallet_randomness_collective_flip::Config for Runtime {} + +parameter_types! { + pub const RelayLocation: MultiLocation = MultiLocation::parent(); + pub const RelayNetwork: NetworkId = CustomNetworkId::Rialto.as_network_id(); + pub RelayOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); + /// The Millau network ID. + pub const MillauNetwork: NetworkId = CustomNetworkId::Millau.as_network_id(); + /// The RialtoParachain network ID. + pub const ThisNetwork: NetworkId = CustomNetworkId::RialtoParachain.as_network_id(); +} + +/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, +); + +/// Means for transacting assets on this chain. +pub type LocalAssetTransactor = CurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We don't track any teleports. + (), +>; + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with XCM `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a + // transaction from the Root origin. + ParentAsSuperuser, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `Origin::Signed` origin of the same 32-byte value. + SignedAccountId32AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + XcmPassthrough, +); + +// TODO: until https://github.com/paritytech/parity-bridges-common/issues/1417 is fixed (in either way), +// the following constant must match the similar constant in the Millau runtime. + +/// One XCM operation is `1_000_000_000` weight - almost certainly a conservative estimate. +pub const BASE_XCM_WEIGHT: u64 = 1_000_000_000; + +parameter_types! { + pub UnitWeightCost: u64 = BASE_XCM_WEIGHT; + // One UNIT buys 1 second of weight. + pub const WeightPrice: (MultiLocation, u128) = (MultiLocation::parent(), UNIT); + pub const MaxInstructions: u32 = 100; + pub const MaxAuthorities: u32 = 100_000; + pub MaxAssetsIntoHolding: u32 = 64; +} + +match_types! { + pub type ParentOrParentsUnitPlurality: impl Contains = { + MultiLocation { parents: 1, interior: Here } | + MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Unit, .. }) } + }; +} + +pub type Barrier = ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + AllowUnpaidExecutionFrom, + // ^^^ Parent & its unit plurality gets free execution +); + +/// XCM weigher type. +pub type XcmWeigher = FixedWeightBounds; + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = NativeAsset; + type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of UNIT + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = XcmWeigher; + type Trader = UsingComponents, RelayLocation, AccountId, Balances, ()>; + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; +} + +/// No local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = SignedToAccountId32; + +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = ( + // Bridge is used to communicate with other relay chain (Millau). + XcmBridgeAdapter, +); + +/// With-Millau bridge. +pub struct ToMillauBridge; + +impl XcmBridge for ToMillauBridge { + type MessageBridge = WithMillauMessageBridge; + type MessageSender = pallet_bridge_messages::Pallet; + + fn universal_location() -> InteriorMultiLocation { + UniversalLocation::get() + } + + fn verify_destination(dest: &MultiLocation) -> bool { + matches!(*dest, MultiLocation { parents: 1, interior: X1(GlobalConsensus(r)) } if r == MillauNetwork::get()) + } + + fn build_destination() -> MultiLocation { + let dest: InteriorMultiLocation = MillauNetwork::get().into(); + let here = UniversalLocation::get(); + dest.relative_to(&here) + } + + fn xcm_lane() -> bp_messages::LaneId { + XCM_LANE + } +} + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = XcmWeigher; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = frame_support::traits::ConstU32<8>; + type UniversalLocation = UniversalLocation; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; + type ChannelInfo = ParachainSystem; + type VersionWrapper = (); + type ExecuteOverweightOrigin = EnsureRoot; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type WeightInfo = (); + type PriceForSiblingDelivery = (); +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; + type ExecuteOverweightOrigin = frame_system::EnsureRoot; +} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = MaxAuthorities; +} + +impl pallet_bridge_relayers::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Reward = Balance; + type PaymentProcedure = bp_relayers::MintReward, AccountId>; + type WeightInfo = (); +} + +parameter_types! { + /// This is a pretty unscientific cap. + /// + /// Note that once this is hit the pallet will essentially throttle incoming requests down to one + /// call per block. + pub const MaxRequests: u32 = 50; + + /// Number of headers to keep. + /// + /// Assuming the worst case of every header being finalized, we will keep headers at least for a + /// week. + pub const HeadersToKeep: u32 = 7 * bp_millau::DAYS as u32; + + /// Maximal number of authorities at Millau. + pub const MaxAuthoritiesAtMillau: u32 = bp_millau::MAX_AUTHORITIES_COUNT; + /// Maximal size of SCALE-encoded Millau header. + pub const MaxMillauHeaderSize: u32 = bp_millau::MAX_HEADER_SIZE; +} + +pub type MillauGrandpaInstance = (); +impl pallet_bridge_grandpa::Config for Runtime { + type BridgedChain = bp_millau::Millau; + type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; + type MaxBridgedAuthorities = MaxAuthoritiesAtMillau; + type MaxBridgedHeaderSize = MaxMillauHeaderSize; + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; +} + +parameter_types! { + pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; + pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + pub const RootAccountForPayments: Option = None; + pub const BridgedChainId: bp_runtime::ChainId = bp_runtime::MILLAU_CHAIN_ID; + pub ActiveOutboundLanes: &'static [bp_messages::LaneId] = &[millau_messages::XCM_LANE]; +} + +/// Instance of the messages pallet used to relay messages to/from Millau chain. +pub type WithMillauMessagesInstance = (); + +impl pallet_bridge_messages::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + type ActiveOutboundLanes = ActiveOutboundLanes; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type MaximalOutboundPayloadSize = crate::millau_messages::ToMillauMaximalOutboundPayloadSize; + type OutboundPayload = crate::millau_messages::ToMillauMessagePayload; + + type InboundPayload = crate::millau_messages::FromMillauMessagePayload; + type InboundRelayer = bp_millau::AccountId; + + type TargetHeaderChain = crate::millau_messages::Millau; + type LaneMessageVerifier = crate::millau_messages::ToMillauMessageVerifier; + type MessageDeliveryAndDispatchPayment = + pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter< + Runtime, + WithMillauMessagesInstance, + >; + + type SourceHeaderChain = crate::millau_messages::Millau; + type MessageDispatch = crate::millau_messages::FromMillauMessageDispatch; + type BridgedChainId = BridgedChainId; +} + +// Create the runtime by composing the FRAME pallets that were previously configured. +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = generic::Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Sudo: pallet_sudo::{Pallet, Call, Storage, Config, Event}, + RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + + ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Storage, Inherent, Event} = 20, + ParachainInfo: parachain_info::{Pallet, Storage, Config} = 21, + + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event} = 30, + + Aura: pallet_aura::{Pallet, Config}, + AuraExt: cumulus_pallet_aura_ext::{Pallet, Config}, + + // XCM helpers. + XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event} = 50, + PolkadotXcm: pallet_xcm::{Pallet, Call, Event, Origin} = 51, + CumulusXcm: cumulus_pallet_xcm::{Pallet, Call, Event, Origin} = 52, + DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 53, + + // Millau bridge modules. + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, + BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, + BridgeMillauMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, + } +); + +impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic( + extrinsic: ::Extrinsic, + ) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_session::SessionKeys for Runtime { + fn decode_session_keys( + encoded: Vec, + ) -> Option, KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + } + + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + } + + fn authorities() -> Vec { + Aura::authorities().to_vec() + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { + fn query_info( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment::FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + } + + impl bp_millau::MillauFinalityApi for Runtime { + fn best_finalized() -> Option> { + BridgeMillauGrandpa::best_finalized().map(|header| header.id()) + } + } + + impl bp_millau::ToMillauOutboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + begin: bp_messages::MessageNonce, + end: bp_messages::MessageNonce, + ) -> Vec { + bridge_runtime_common::messages_api::outbound_message_details::< + Runtime, + WithMillauMessagesInstance, + >(lane, begin, end) + } + } + + impl bp_millau::FromMillauInboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + ) -> Vec { + bridge_runtime_common::messages_api::inbound_message_details::< + Runtime, + WithMillauMessagesInstance, + >(lane, messages) + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata(_extra: bool) -> ( + Vec, + Vec, + ) { + todo!("TODO: fix or remove") + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey}; + + use frame_system_benchmarking::Pallet as SystemBench; + impl frame_system_benchmarking::Config for Runtime {} + + let whitelist: Vec = vec![ + // Block Number + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(), + // Total Issuance + hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(), + // Execution Phase + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(), + // Event Count + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(), + // System Events + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(), + ]; + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + + add_benchmark!(params, batches, frame_system, SystemBench::); + add_benchmark!(params, batches, pallet_balances, Balances); + add_benchmark!(params, batches, pallet_timestamp, Timestamp); + + Ok(batches) + } + } +} + +struct CheckInherents; + +impl cumulus_pallet_parachain_system::CheckInherents for CheckInherents { + fn check_inherents( + block: &Block, + relay_state_proof: &cumulus_pallet_parachain_system::RelayChainStateProof, + ) -> sp_inherents::CheckInherentsResult { + let relay_chain_slot = relay_state_proof + .read_slot() + .expect("Could not read the relay chain slot from the proof"); + + let inherent_data = + cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration( + relay_chain_slot, + sp_std::time::Duration::from_secs(6), + ) + .create_inherent_data() + .expect("Could not create the timestamp inherent data"); + + inherent_data.check_extrinsics(block) + } +} + +cumulus_pallet_parachain_system::register_validate_block!( + Runtime = Runtime, + BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, + CheckInherents = CheckInherents, +); + +#[cfg(test)] +mod tests { + use super::*; + use crate::millau_messages::WeightCredit; + use bp_messages::{ + target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, + MessageKey, + }; + use bp_runtime::messages::MessageDispatchResult; + use bridge_runtime_common::messages::target::FromBridgedChainMessageDispatch; + use codec::Encode; + + fn new_test_ext() -> sp_io::TestExternalities { + sp_io::TestExternalities::new( + frame_system::GenesisConfig::default().build_storage::().unwrap(), + ) + } + + #[test] + fn xcm_messages_to_millau_are_sent() { + new_test_ext().execute_with(|| { + // the encoded message (origin ++ xcm) is 0x010109020419A8 + let dest = (Parent, X1(GlobalConsensus(MillauNetwork::get()))); + let xcm: Xcm<()> = vec![Instruction::Trap(42)].into(); + + let send_result = send_xcm::(dest.into(), xcm); + let expected_fee = MultiAssets::from((Here, Fungibility::Fungible(1_000_000_u128))); + let expected_hash = + ([0u8, 0u8, 0u8, 0u8], 1u64).using_encoded(sp_io::hashing::blake2_256); + assert_eq!(send_result, Ok((expected_hash, expected_fee)),); + }) + } + + #[test] + fn xcm_messages_from_millau_are_dispatched() { + type XcmExecutor = xcm_executor::XcmExecutor; + type MessageDispatcher = FromBridgedChainMessageDispatch< + WithMillauMessageBridge, + XcmExecutor, + XcmWeigher, + WeightCredit, + >; + + new_test_ext().execute_with(|| { + let location: MultiLocation = + (Parent, X1(GlobalConsensus(MillauNetwork::get()))).into(); + let xcm: Xcm = vec![Instruction::Trap(42)].into(); + + let mut incoming_message = DispatchMessage { + key: MessageKey { lane_id: [0, 0, 0, 0], nonce: 1 }, + data: DispatchMessageData { payload: Ok((location, xcm).into()) }, + }; + + let dispatch_weight = MessageDispatcher::dispatch_weight(&mut incoming_message); + assert_eq!( + dispatch_weight, + frame_support::weights::Weight::from_ref_time(1_000_000_000) + ); + + let dispatch_result = + MessageDispatcher::dispatch(&AccountId::from([0u8; 32]), incoming_message); + assert_eq!( + dispatch_result, + MessageDispatchResult { + unspent_weight: frame_support::weights::Weight::from_ref_time(0), + dispatch_fee_paid_during_dispatch: false, + dispatch_level_result: (), + } + ); + }) + } +} diff --git a/bin/rialto-parachain/runtime/src/millau_messages.rs b/bin/rialto-parachain/runtime/src/millau_messages.rs new file mode 100644 index 00000000000..25d37f15988 --- /dev/null +++ b/bin/rialto-parachain/runtime/src/millau_messages.rs @@ -0,0 +1,163 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything required to serve Millau <-> RialtoParachain messages. + +// TODO: this is almost exact copy of `millau_messages.rs` from Rialto runtime. +// Should be extracted to a separate crate and reused here. + +use crate::{MillauGrandpaInstance, Runtime, RuntimeCall, RuntimeOrigin}; + +use bp_messages::{ + source_chain::TargetHeaderChain, + target_chain::{ProvedMessages, SourceHeaderChain}, + InboundLaneData, LaneId, Message, MessageNonce, +}; +use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID}; +use bridge_runtime_common::messages::{self, MessageBridge}; +use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; + +/// Default lane that is used to send messages to Millau. +pub const XCM_LANE: LaneId = [0, 0, 0, 0]; +/// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge +/// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual +/// tests, confirming that we don't break encoding somewhere between. +pub const BASE_XCM_WEIGHT_TWICE: u64 = 2 * crate::BASE_XCM_WEIGHT; + +parameter_types! { + /// Weight credit for our test messages. + /// + /// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge + /// (it is prepended with `UniversalOrigin` instruction). + pub const WeightCredit: Weight = Weight::from_ref_time(BASE_XCM_WEIGHT_TWICE); +} + +/// Message payload for RialtoParachain -> Millau messages. +pub type ToMillauMessagePayload = messages::source::FromThisChainMessagePayload; + +/// Message verifier for RialtoParachain -> Millau messages. +pub type ToMillauMessageVerifier = + messages::source::FromThisChainMessageVerifier; + +/// Message payload for Millau -> RialtoParachain messages. +pub type FromMillauMessagePayload = messages::target::FromBridgedChainMessagePayload; + +/// Call-dispatch based message dispatch for Millau -> RialtoParachain messages. +pub type FromMillauMessageDispatch = messages::target::FromBridgedChainMessageDispatch< + WithMillauMessageBridge, + xcm_executor::XcmExecutor, + crate::XcmWeigher, + WeightCredit, +>; + +/// Messages proof for Millau -> RialtoParachain messages. +pub type FromMillauMessagesProof = messages::target::FromBridgedChainMessagesProof; + +/// Messages delivery proof for RialtoParachain -> Millau messages. +pub type ToMillauMessagesDeliveryProof = + messages::source::FromBridgedChainMessagesDeliveryProof; + +/// Maximal outbound payload size of Rialto -> Millau messages. +pub type ToMillauMaximalOutboundPayloadSize = + messages::source::FromThisChainMaximalOutboundPayloadSize; + +/// Millau <-> RialtoParachain message bridge. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct WithMillauMessageBridge; + +impl MessageBridge for WithMillauMessageBridge { + const THIS_CHAIN_ID: ChainId = RIALTO_PARACHAIN_CHAIN_ID; + const BRIDGED_CHAIN_ID: ChainId = MILLAU_CHAIN_ID; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = + bp_rialto_parachain::WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME; + + type ThisChain = RialtoParachain; + type BridgedChain = Millau; + type BridgedHeaderChain = + pallet_bridge_grandpa::GrandpaChainHeaders; +} + +/// RialtoParachain chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct RialtoParachain; + +impl messages::UnderlyingChainProvider for RialtoParachain { + type Chain = bp_rialto_parachain::RialtoParachain; +} + +impl messages::ThisChainWithMessages for RialtoParachain { + type RuntimeCall = RuntimeCall; + type RuntimeOrigin = RuntimeOrigin; + + fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, _lane: &LaneId) -> bool { + true + } + + fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { + MessageNonce::MAX + } +} + +/// Millau chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Millau; + +impl messages::UnderlyingChainProvider for Millau { + type Chain = bp_millau::Millau; +} + +impl messages::BridgedChainWithMessages for Millau { + fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { + true + } +} + +impl TargetHeaderChain for Millau { + type Error = &'static str; + // The proof is: + // - hash of the header this proof has been created with; + // - the storage proof of one or several keys; + // - id of the lane we prove state of. + type MessagesDeliveryProof = ToMillauMessagesDeliveryProof; + + fn verify_message(payload: &ToMillauMessagePayload) -> Result<(), Self::Error> { + messages::source::verify_chain_message::(payload) + } + + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData), Self::Error> { + messages::source::verify_messages_delivery_proof::(proof) + } +} + +impl SourceHeaderChain for Millau { + type Error = &'static str; + // The proof is: + // - hash of the header this proof has been created with; + // - the storage proof of one or several keys; + // - id of the lane we prove messages for; + // - inclusive range of messages nonces that are proved. + type MessagesProof = FromMillauMessagesProof; + + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result, Self::Error> { + messages::target::verify_messages_proof::(proof, messages_count) + .map_err(Into::into) + } +} diff --git a/bin/rialto/node/Cargo.toml b/bin/rialto/node/Cargo.toml new file mode 100644 index 00000000000..82b073896d9 --- /dev/null +++ b/bin/rialto/node/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "rialto-bridge-node" +description = "Substrate node compatible with Rialto runtime" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +build = "build.rs" +repository = "https://github.com/paritytech/parity-bridges-common/" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +clap = { version = "4.0.9", features = ["derive"] } +serde_json = "1.0.79" + +# Bridge dependencies + +rialto-runtime = { path = "../runtime" } + +# Substrate Dependencies + +beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +node-inspect = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master", features = ["wasmtime"] } +sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-service = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } + +# Polkadot Dependencies +polkadot-node-core-pvf = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false, features = [ "full-node", "polkadot-native" ] } + +[build-dependencies] +substrate-build-script-utils = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = [] +runtime-benchmarks = [ + "rialto-runtime/runtime-benchmarks", +] diff --git a/bin/rialto/node/build.rs b/bin/rialto/node/build.rs new file mode 100644 index 00000000000..d9b50049e26 --- /dev/null +++ b/bin/rialto/node/build.rs @@ -0,0 +1,23 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; + +fn main() { + generate_cargo_keys(); + + rerun_if_git_head_changed(); +} diff --git a/bin/rialto/node/src/chain_spec.rs b/bin/rialto/node/src/chain_spec.rs new file mode 100644 index 00000000000..0e9edb38ac0 --- /dev/null +++ b/bin/rialto/node/src/chain_spec.rs @@ -0,0 +1,286 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use beefy_primitives::crypto::AuthorityId as BeefyId; +use frame_support::weights::Weight; +use polkadot_primitives::v2::{AssignmentId, ValidatorId}; +use rialto_runtime::{ + AccountId, BabeConfig, BalancesConfig, BeefyConfig, BridgeMillauMessagesConfig, + ConfigurationConfig, GenesisConfig, GrandpaConfig, SessionConfig, SessionKeys, Signature, + SudoConfig, SystemConfig, WASM_BINARY, +}; +use serde_json::json; +use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; +use sp_consensus_babe::AuthorityId as BabeId; +use sp_core::{sr25519, Pair, Public}; +use sp_finality_grandpa::AuthorityId as GrandpaId; +use sp_runtime::traits::{IdentifyAccount, Verify}; + +/// "Names" of the authorities accounts at local testnet. +const LOCAL_AUTHORITIES_ACCOUNTS: [&str; 5] = ["Alice", "Bob", "Charlie", "Dave", "Eve"]; +/// "Names" of the authorities accounts at development testnet. +const DEV_AUTHORITIES_ACCOUNTS: [&str; 1] = [LOCAL_AUTHORITIES_ACCOUNTS[0]]; +/// "Names" of all possible authorities accounts. +const ALL_AUTHORITIES_ACCOUNTS: [&str; 5] = LOCAL_AUTHORITIES_ACCOUNTS; +/// "Name" of the `sudo` account. +const SUDO_ACCOUNT: &str = "Sudo"; +/// "Name" of the account, which owns the with-Millau messages pallet. +const MILLAU_MESSAGES_PALLET_OWNER: &str = "Millau.MessagesOwner"; + +/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. +pub type ChainSpec = + sc_service::GenericChainSpec; + +/// The chain specification option. This is expected to come in from the CLI and +/// is little more than one of a number of alternatives which can easily be converted +/// from a string (`--chain=...`) into a `ChainSpec`. +#[derive(Clone, Debug)] +pub enum Alternative { + /// Whatever the current runtime is, with just Alice as an auth. + Development, + /// Whatever the current runtime is, with simple Alice/Bob/Charlie/Dave/Eve auths. + LocalTestnet, +} + +/// Helper function to generate a crypto pair from seed +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{seed}"), None) + .expect("static values are valid; qed") + .public() +} + +type AccountPublic = ::Signer; + +/// Helper function to generate an account ID from seed +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// Helper function to generate authority keys. +pub fn get_authority_keys_from_seed( + s: &str, +) -> (AccountId, BabeId, BeefyId, GrandpaId, ValidatorId, AssignmentId, AuthorityDiscoveryId) { + ( + get_account_id_from_seed::(s), + get_from_seed::(s), + get_from_seed::(s), + get_from_seed::(s), + get_from_seed::(s), + get_from_seed::(s), + get_from_seed::(s), + ) +} + +impl Alternative { + /// Get an actual chain config from one of the alternatives. + pub(crate) fn load(self) -> ChainSpec { + let properties = Some( + json!({ + "tokenDecimals": 9, + "tokenSymbol": "RLT" + }) + .as_object() + .expect("Map given; qed") + .clone(), + ); + match self { + Alternative::Development => ChainSpec::from_genesis( + "Rialto Development", + "rialto_dev", + sc_service::ChainType::Development, + || { + testnet_genesis( + DEV_AUTHORITIES_ACCOUNTS + .into_iter() + .map(get_authority_keys_from_seed) + .collect(), + get_account_id_from_seed::(SUDO_ACCOUNT), + endowed_accounts(), + true, + ) + }, + vec![], + None, + None, + None, + properties, + Default::default(), + ), + Alternative::LocalTestnet => ChainSpec::from_genesis( + "Rialto Local", + "rialto_local", + sc_service::ChainType::Local, + || { + testnet_genesis( + LOCAL_AUTHORITIES_ACCOUNTS + .into_iter() + .map(get_authority_keys_from_seed) + .collect(), + get_account_id_from_seed::(SUDO_ACCOUNT), + endowed_accounts(), + true, + ) + }, + vec![], + None, + None, + None, + properties, + Default::default(), + ), + } + } +} + +/// We're using the same set of endowed accounts on all Millau chains (dev/local) to make +/// sure that all accounts, required for bridge to be functional (e.g. relayers fund account, +/// accounts used by relayers in our test deployments, accounts used for demonstration +/// purposes), are all available on these chains. +fn endowed_accounts() -> Vec { + let all_authorities = ALL_AUTHORITIES_ACCOUNTS.iter().flat_map(|x| { + [ + get_account_id_from_seed::(x), + get_account_id_from_seed::(&format!("{x}//stash")), + ] + }); + vec![ + // Sudo account + get_account_id_from_seed::(SUDO_ACCOUNT), + // Regular (unused) accounts + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Ferdie//stash"), + // Accounts, used by Rialto<>Millau bridge + get_account_id_from_seed::(MILLAU_MESSAGES_PALLET_OWNER), + get_account_id_from_seed::("Millau.HeadersAndMessagesRelay"), + get_account_id_from_seed::("Millau.OutboundMessagesRelay.Lane00000001"), + get_account_id_from_seed::("Millau.InboundMessagesRelay.Lane00000001"), + get_account_id_from_seed::("Millau.MessagesSender"), + ] + .into_iter() + .chain(all_authorities) + .collect() +} + +fn session_keys( + babe: BabeId, + beefy: BeefyId, + grandpa: GrandpaId, + para_validator: ValidatorId, + para_assignment: AssignmentId, + authority_discovery: AuthorityDiscoveryId, +) -> SessionKeys { + SessionKeys { babe, beefy, grandpa, para_validator, para_assignment, authority_discovery } +} + +fn testnet_genesis( + initial_authorities: Vec<( + AccountId, + BabeId, + BeefyId, + GrandpaId, + ValidatorId, + AssignmentId, + AuthorityDiscoveryId, + )>, + root_key: AccountId, + endowed_accounts: Vec, + _enable_println: bool, +) -> GenesisConfig { + GenesisConfig { + system: SystemConfig { + code: WASM_BINARY.expect("Rialto development WASM not available").to_vec(), + }, + balances: BalancesConfig { + balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 50)).collect(), + }, + babe: BabeConfig { + authorities: Vec::new(), + epoch_config: Some(rialto_runtime::BABE_GENESIS_EPOCH_CONFIG), + }, + beefy: BeefyConfig { authorities: Vec::new() }, + grandpa: GrandpaConfig { authorities: Vec::new() }, + sudo: SudoConfig { key: Some(root_key) }, + session: SessionConfig { + keys: initial_authorities + .iter() + .map(|x| { + ( + x.0.clone(), + x.0.clone(), + session_keys( + x.1.clone(), + x.2.clone(), + x.3.clone(), + x.4.clone(), + x.5.clone(), + x.6.clone(), + ), + ) + }) + .collect::>(), + }, + authority_discovery: Default::default(), + hrmp: Default::default(), + // this configuration is exact copy of configuration from Polkadot repo + // (see /node/service/src/chain_spec.rs:default_parachains_host_configuration) + configuration: ConfigurationConfig { + config: polkadot_runtime_parachains::configuration::HostConfiguration { + validation_upgrade_cooldown: 2u32, + validation_upgrade_delay: 2, + code_retention_period: 1200, + max_code_size: polkadot_primitives::v2::MAX_CODE_SIZE, + max_pov_size: polkadot_primitives::v2::MAX_POV_SIZE, + max_head_data_size: 32 * 1024, + group_rotation_frequency: 20, + chain_availability_period: 4, + thread_availability_period: 4, + max_upward_queue_count: 8, + max_upward_queue_size: 1024 * 1024, + max_downward_message_size: 1024 * 1024, + ump_service_total_weight: Weight::from_ref_time(100_000_000_000), + max_upward_message_size: 50 * 1024, + max_upward_message_num_per_candidate: 5, + hrmp_sender_deposit: 0, + hrmp_recipient_deposit: 0, + hrmp_channel_max_capacity: 8, + hrmp_channel_max_total_size: 8 * 1024, + hrmp_max_parachain_inbound_channels: 4, + hrmp_max_parathread_inbound_channels: 4, + hrmp_channel_max_message_size: 1024 * 1024, + hrmp_max_parachain_outbound_channels: 4, + hrmp_max_parathread_outbound_channels: 4, + hrmp_max_message_num_per_candidate: 5, + dispute_period: 6, + no_show_slots: 2, + n_delay_tranches: 25, + needed_approvals: 2, + relay_vrf_modulo_samples: 2, + zeroth_delay_tranche_width: 0, + minimum_validation_upgrade_delay: 5, + ..Default::default() + }, + }, + paras: Default::default(), + bridge_millau_messages: BridgeMillauMessagesConfig { + owner: Some(get_account_id_from_seed::(MILLAU_MESSAGES_PALLET_OWNER)), + ..Default::default() + }, + xcm_pallet: Default::default(), + } +} diff --git a/bin/rialto/node/src/cli.rs b/bin/rialto/node/src/cli.rs new file mode 100644 index 00000000000..0cdd25417e7 --- /dev/null +++ b/bin/rialto/node/src/cli.rs @@ -0,0 +1,87 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use clap::Parser; +use sc_cli::RunCmd; + +#[derive(Debug, Parser)] +pub struct Cli { + #[structopt(subcommand)] + pub subcommand: Option, + + #[structopt(flatten)] + pub run: RunCmd, +} + +/// Possible subcommands of the main binary. +#[derive(Debug, Parser)] +pub enum Subcommand { + /// Key management CLI utilities + #[clap(subcommand)] + Key(sc_cli::KeySubcommand), + + /// Verify a signature for a message, provided on `STDIN`, with a given (public or secret) key. + Verify(sc_cli::VerifyCmd), + + /// Generate a seed that provides a vanity address. + Vanity(sc_cli::VanityCmd), + + /// Sign a message, with a given (secret) key. + Sign(sc_cli::SignCmd), + + /// Build a chain specification. + BuildSpec(sc_cli::BuildSpecCmd), + + /// Validate blocks. + CheckBlock(sc_cli::CheckBlockCmd), + + /// Export blocks. + ExportBlocks(sc_cli::ExportBlocksCmd), + + /// Export the state of a given block into a chain spec. + ExportState(sc_cli::ExportStateCmd), + + /// Import blocks. + ImportBlocks(sc_cli::ImportBlocksCmd), + + /// Remove the whole chain. + PurgeChain(sc_cli::PurgeChainCmd), + + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), + + /// Inspect blocks or extrinsics. + Inspect(node_inspect::cli::InspectCmd), + + /// Benchmark runtime pallets. + #[clap(subcommand)] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), + + /// FOR INTERNAL USE: analog of the "prepare-worker" command of the polkadot binary. + #[clap(name = "prepare-worker", hide = true)] + PvfPrepareWorker(ValidationWorkerCommand), + + /// FOR INTERNAL USE: analog of the "execute-worker" command of the polkadot binary. + #[clap(name = "execute-worker", hide = true)] + PvfExecuteWorker(ValidationWorkerCommand), +} + +/// Validation worker command. +#[derive(Debug, Parser)] +pub struct ValidationWorkerCommand { + /// The path to the validation host's socket. + pub socket_path: String, +} diff --git a/bin/rialto/node/src/command.rs b/bin/rialto/node/src/command.rs new file mode 100644 index 00000000000..8286444d3ea --- /dev/null +++ b/bin/rialto/node/src/command.rs @@ -0,0 +1,222 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::cli::{Cli, Subcommand}; +use frame_benchmarking_cli::BenchmarkCmd; +use rialto_runtime::{Block, RuntimeApi}; +use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli}; + +impl SubstrateCli for Cli { + fn impl_name() -> String { + "Rialto Bridge Node".into() + } + + fn impl_version() -> String { + env!("CARGO_PKG_VERSION").into() + } + + fn description() -> String { + "Rialto Bridge Node".into() + } + + fn author() -> String { + "Parity Technologies".into() + } + + fn support_url() -> String { + "https://github.com/paritytech/parity-bridges-common/".into() + } + + fn copyright_start_year() -> i32 { + 2019 + } + + fn executable_name() -> String { + "rialto-bridge-node".into() + } + + fn native_runtime_version(_: &Box) -> &'static RuntimeVersion { + &rialto_runtime::VERSION + } + + fn load_spec(&self, id: &str) -> Result, String> { + Ok(Box::new( + match id { + "" | "dev" => crate::chain_spec::Alternative::Development, + "local" => crate::chain_spec::Alternative::LocalTestnet, + _ => return Err(format!("Unsupported chain specification: {id}")), + } + .load(), + )) + } +} + +// Rialto native executor instance. +pub struct ExecutorDispatch; + +impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { + type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; + + fn dispatch(method: &str, data: &[u8]) -> Option> { + rialto_runtime::api::dispatch(method, data) + } + + fn native_version() -> sc_executor::NativeVersion { + rialto_runtime::native_version() + } +} + +/// Parse and run command line arguments +pub fn run() -> sc_cli::Result<()> { + let cli = Cli::from_args(); + sp_core::crypto::set_default_ss58_version(sp_core::crypto::Ss58AddressFormat::custom( + rialto_runtime::SS58Prefix::get() as u16, + )); + + match &cli.subcommand { + Some(Subcommand::Benchmark(cmd)) => { + let runner = cli.create_runner(cmd)?; + match cmd { + BenchmarkCmd::Pallet(cmd) => + if cfg!(feature = "runtime-benchmarks") { + runner.sync_run(|config| cmd.run::(config)) + } else { + println!( + "Benchmarking wasn't enabled when building the node. \ + You can enable it with `--features runtime-benchmarks`." + ); + Ok(()) + }, + _ => Err("Unsupported benchmarking subcommand".into()), + } + }, + Some(Subcommand::Key(cmd)) => cmd.run(&cli), + Some(Subcommand::Sign(cmd)) => cmd.run(), + Some(Subcommand::Verify(cmd)) => cmd.run(), + Some(Subcommand::Vanity(cmd)) => cmd.run(), + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) + }, + Some(Subcommand::CheckBlock(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, _, import_queue, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; + Ok((cmd.run(client, import_queue), task_manager)) + }) + }, + Some(Subcommand::ExportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, _, _, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; + Ok((cmd.run(client, config.database), task_manager)) + }) + }, + Some(Subcommand::ExportState(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, _, _, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; + Ok((cmd.run(client, config.chain_spec), task_manager)) + }) + }, + Some(Subcommand::ImportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, _, import_queue, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; + Ok((cmd.run(client, import_queue), task_manager)) + }) + }, + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.database)) + }, + Some(Subcommand::Revert(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, backend, _, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; + Ok((cmd.run(client, backend, None), task_manager)) + }) + }, + Some(Subcommand::Inspect(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run::(config)) + }, + Some(Subcommand::PvfPrepareWorker(cmd)) => { + let mut builder = sc_cli::LoggerBuilder::new(""); + builder.with_colors(false); + let _ = builder.init(); + + polkadot_node_core_pvf::prepare_worker_entrypoint(&cmd.socket_path); + Ok(()) + }, + Some(crate::cli::Subcommand::PvfExecuteWorker(cmd)) => { + let mut builder = sc_cli::LoggerBuilder::new(""); + builder.with_colors(false); + let _ = builder.init(); + + polkadot_node_core_pvf::execute_worker_entrypoint(&cmd.socket_path); + Ok(()) + }, + None => { + let runner = cli.create_runner(&cli.run)?; + + // some parameters that are used by polkadot nodes, but that are not used by our binary + // let jaeger_agent = None; + // let grandpa_pause = None; + // let no_beefy = true; + // let telemetry_worker_handler = None; + // let is_collator = crate::service::IsCollator::No; + let overseer_gen = polkadot_service::overseer::RealOverseerGen; + runner.run_node_until_exit(|config| async move { + let is_collator = polkadot_service::IsCollator::No; + let grandpa_pause = None; + let enable_beefy = true; + let jaeger_agent = None; + let telemetry_worker_handle = None; + let program_path = None; + let overseer_enable_anyways = false; + + polkadot_service::new_full::( + config, + is_collator, + grandpa_pause, + enable_beefy, + jaeger_agent, + telemetry_worker_handle, + program_path, + overseer_enable_anyways, + overseer_gen, + None, + None, + None, + ) + .map(|full| full.task_manager) + .map_err(service_error) + }) + }, + } +} + +// We don't want to change 'service.rs' too much to ease future updates => it'll keep using +// its own error enum like original polkadot service does. +fn service_error(err: polkadot_service::Error) -> sc_cli::Error { + sc_cli::Error::Application(Box::new(err)) +} diff --git a/bin/rialto/node/src/main.rs b/bin/rialto/node/src/main.rs new file mode 100644 index 00000000000..6dea84a309b --- /dev/null +++ b/bin/rialto/node/src/main.rs @@ -0,0 +1,28 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rialto bridge node. + +#![warn(missing_docs)] + +mod chain_spec; +mod cli; +mod command; + +/// Run the Rialto Node +fn main() -> sc_cli::Result<()> { + command::run() +} diff --git a/bin/rialto/runtime/Cargo.toml b/bin/rialto/runtime/Cargo.toml new file mode 100644 index 00000000000..7220e5790a4 --- /dev/null +++ b/bin/rialto/runtime/Cargo.toml @@ -0,0 +1,145 @@ +[package] +name = "rialto-runtime" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +repository = "https://github.com/paritytech/parity-bridges-common/" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } + +# Bridge dependencies + +bp-messages = { path = "../../../primitives/messages", default-features = false } +bp-millau = { path = "../../../primitives/chain-millau", default-features = false } +bp-relayers = { path = "../../../primitives/relayers", default-features = false } +bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false } +bp-runtime = { path = "../../../primitives/runtime", default-features = false } +bridge-runtime-common = { path = "../../runtime-common", default-features = false } +pallet-bridge-beefy = { path = "../../../modules/beefy", default-features = false } +pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false } +pallet-bridge-messages = { path = "../../../modules/messages", default-features = false } +pallet-bridge-relayers = { path = "../../../modules/relayers", default-features = false } +pallet-shift-session-manager = { path = "../../../modules/shift-session-manager", default-features = false } + +# Substrate Dependencies + +beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-executive = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-babe = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +# Polkadot (parachain) Dependencies +pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + +[dev-dependencies] +bridge-runtime-common = { path = "../../runtime-common", features = ["integrity-test"] } +env_logger = "0.8" +static_assertions = "1.1" + +[build-dependencies] +substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "beefy-primitives/std", + "bp-messages/std", + "bp-millau/std", + "bp-relayers/std", + "bp-rialto/std", + "bp-runtime/std", + "bridge-runtime-common/std", + "codec/std", + "frame-benchmarking/std", + "frame-executive/std", + "frame-support/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "log/std", + "pallet-authority-discovery/std", + "pallet-babe/std", + "pallet-balances/std", + "pallet-beefy/std", + "pallet-beefy-mmr/std", + "pallet-bridge-beefy/std", + "pallet-bridge-grandpa/std", + "pallet-bridge-messages/std", + "pallet-bridge-relayers/std", + "pallet-grandpa/std", + "pallet-mmr/std", + "pallet-xcm/std", + "sp-mmr-primitives/std", + "pallet-shift-session-manager/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "polkadot-primitives/std", + "polkadot-runtime-common/std", + "polkadot-runtime-parachains/std", + "scale-info/std", + "sp-api/std", + "sp-authority-discovery/std", + "sp-block-builder/std", + "sp-consensus-babe/std", + "sp-core/std", + "sp-inherents/std", + "sp-io/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-version/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", +] +runtime-benchmarks = [ + "bridge-runtime-common/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-bridge-messages/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", +] diff --git a/bin/rialto/runtime/build.rs b/bin/rialto/runtime/build.rs new file mode 100644 index 00000000000..cc865704327 --- /dev/null +++ b/bin/rialto/runtime/build.rs @@ -0,0 +1,25 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .import_memory() + .export_heap_base() + .build() +} diff --git a/bin/rialto/runtime/src/lib.rs b/bin/rialto/runtime/src/lib.rs new file mode 100644 index 00000000000..25778705e5e --- /dev/null +++ b/bin/rialto/runtime/src/lib.rs @@ -0,0 +1,1006 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! The Rialto runtime. This can be compiled with `#[no_std]`, ready for Wasm. + +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] +// Runtime-generated enums +#![allow(clippy::large_enum_variant)] +// From construct_runtime macro +#![allow(clippy::from_over_into)] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +pub mod millau_messages; +pub mod parachains; +pub mod xcm_config; + +use beefy_primitives::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; +use bp_runtime::{HeaderId, HeaderIdProvider}; +use pallet_grandpa::{ + fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, +}; +use pallet_mmr::primitives as mmr; +use pallet_transaction_payment::{FeeDetails, Multiplier, RuntimeDispatchInfo}; +use sp_api::impl_runtime_apis; +use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_mmr_primitives::{ + DataOrHash, EncodableOpaqueLeaf, Error as MmrError, LeafDataProvider, Proof as MmrProof, +}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{AccountIdLookup, Block as BlockT, Keccak256, NumberFor, OpaqueKeys}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, FixedPointNumber, Perquintill, +}; +use sp_std::{collections::btree_map::BTreeMap, prelude::*}; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +// A few exports that help ease life for downstream crates. +pub use frame_support::{ + construct_runtime, + dispatch::DispatchClass, + parameter_types, + traits::{Currency, ExistenceRequirement, Imbalance, KeyOwnerProofSystem}, + weights::{constants::WEIGHT_PER_SECOND, IdentityFee, RuntimeDbWeight, Weight}, + StorageValue, +}; + +pub use frame_system::Call as SystemCall; +pub use pallet_balances::Call as BalancesCall; +pub use pallet_bridge_beefy::Call as BridgeBeefyCall; +pub use pallet_bridge_grandpa::Call as BridgeGrandpaCall; +pub use pallet_bridge_messages::Call as MessagesCall; +pub use pallet_sudo::Call as SudoCall; +pub use pallet_timestamp::Call as TimestampCall; +pub use pallet_xcm::Call as XcmCall; + +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; +pub use sp_runtime::{Perbill, Permill}; + +/// An index to a block. +pub type BlockNumber = bp_rialto::BlockNumber; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = bp_rialto::Signature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = bp_rialto::AccountId; + +/// The type for looking up accounts. We don't expect more than 4 billion of them, but you +/// never know... +pub type AccountIndex = u32; + +/// Balance of an account. +pub type Balance = bp_rialto::Balance; + +/// Index of a transaction in the chain. +pub type Index = bp_rialto::Index; + +/// A hash of some data used by the chain. +pub type Hash = bp_rialto::Hash; + +/// Hashing algorithm used by the chain. +pub type Hashing = bp_rialto::Hasher; + +/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know +/// the specifics of the runtime. They can then be made to be agnostic over specific formats +/// of data like extrinsics, allowing for them to continue syncing the network through upgrades +/// to even the core data structures. +pub mod opaque { + use super::*; + + pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; + + /// Opaque block header type. + pub type Header = generic::Header; + /// Opaque block type. + pub type Block = generic::Block; + /// Opaque block identifier type. + pub type BlockId = generic::BlockId; +} + +impl_opaque_keys! { + pub struct SessionKeys { + pub babe: Babe, + pub grandpa: Grandpa, + pub beefy: Beefy, + pub para_validator: Initializer, + pub para_assignment: SessionInfo, + pub authority_discovery: AuthorityDiscovery, + } +} + +/// This runtime version. +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("rialto-runtime"), + impl_name: create_runtime_str!("rialto-runtime"), + authoring_version: 1, + spec_version: 1, + impl_version: 1, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 1, +}; + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } +} + +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub const Version: RuntimeVersion = VERSION; + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { + read: 60_000_000, // ~0.06 ms = ~60 µs + write: 200_000_000, // ~0.2 ms = 200 µs + }; + pub const SS58Prefix: u8 = 48; +} + +impl frame_system::Config for Runtime { + /// The basic call filter to use in dispatchable. + type BaseCallFilter = frame_support::traits::Everything; + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type RuntimeCall = RuntimeCall; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = AccountIdLookup; + /// The index type for storing how many extrinsics an account has signed. + type Index = Index; + /// The index type for blocks. + type BlockNumber = BlockNumber; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = Hashing; + /// The header type. + type Header = generic::Header; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// Version of the runtime. + type Version = Version; + /// Provides information about the pallet setup in the runtime. + type PalletInfo = PalletInfo; + /// What to do if a new account is created. + type OnNewAccount = (); + /// What to do if an account is fully reaped from the system. + type OnKilledAccount = (); + /// The data to be stored in an account. + type AccountData = pallet_balances::AccountData; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + /// Weight information for the extrinsics of this pallet. + type SystemWeightInfo = (); + /// Block and extrinsics weights: base values and limits. + type BlockWeights = bp_rialto::BlockWeights; + /// The maximum length of a block (in bytes). + type BlockLength = bp_rialto::BlockLength; + /// The weight of database operations that the runtime can invoke. + type DbWeight = DbWeight; + /// The designated SS58 prefix of this chain. + type SS58Prefix = SS58Prefix; + /// The set code logic, just the default since we're not a parachain. + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +/// The BABE epoch configuration at genesis. +pub const BABE_GENESIS_EPOCH_CONFIG: sp_consensus_babe::BabeEpochConfiguration = + sp_consensus_babe::BabeEpochConfiguration { + c: bp_rialto::time_units::PRIMARY_PROBABILITY, + allowed_slots: sp_consensus_babe::AllowedSlots::PrimaryAndSecondaryVRFSlots, + }; + +parameter_types! { + pub const EpochDuration: u64 = bp_rialto::EPOCH_DURATION_IN_SLOTS as u64; + pub const ExpectedBlockTime: bp_rialto::Moment = bp_rialto::time_units::MILLISECS_PER_BLOCK; + pub const MaxAuthorities: u32 = 10; +} + +impl pallet_babe::Config for Runtime { + type EpochDuration = EpochDuration; + type ExpectedBlockTime = ExpectedBlockTime; + type MaxAuthorities = MaxAuthorities; + + // session module is the trigger + type EpochChangeTrigger = pallet_babe::ExternalTrigger; + + // equivocation related configuration - we don't expect any equivocations in our testnets + type KeyOwnerProofSystem = (); + type KeyOwnerProof = >::Proof; + type KeyOwnerIdentification = >::IdentificationTuple; + type HandleEquivocation = (); + + type DisabledValidators = (); + type WeightInfo = (); +} + +impl pallet_beefy::Config for Runtime { + type BeefyId = BeefyId; + type MaxAuthorities = MaxAuthorities; + type OnNewValidatorSet = MmrLeaf; +} + +impl pallet_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type MaxAuthorities = MaxAuthorities; + type KeyOwnerProofSystem = (); + type KeyOwnerProof = + >::Proof; + type KeyOwnerIdentification = >::IdentificationTuple; + type HandleEquivocation = (); + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); +} + +type MmrHash = ::Output; +type MmrHashing = ::Hashing; + +impl pallet_mmr::Config for Runtime { + const INDEXING_PREFIX: &'static [u8] = b"mmr"; + type Hashing = Keccak256; + type Hash = ::Output; + type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest; + type WeightInfo = (); + type LeafData = pallet_beefy_mmr::Pallet; +} + +parameter_types! { + /// Version of the produced MMR leaf. + /// + /// The version consists of two parts; + /// - `major` (3 bits) + /// - `minor` (5 bits) + /// + /// `major` should be updated only if decoding the previous MMR Leaf format from the payload + /// is not possible (i.e. backward incompatible change). + /// `minor` should be updated if fields are added to the previous MMR Leaf, which given SCALE + /// encoding does not prevent old leafs from being decoded. + /// + /// Hence we expect `major` to be changed really rarely (think never). + /// See [`MmrLeafVersion`] type documentation for more details. + pub LeafVersion: MmrLeafVersion = MmrLeafVersion::new(0, 0); +} + +pub struct BeefyDummyDataProvider; + +impl beefy_primitives::mmr::BeefyDataProvider<()> for BeefyDummyDataProvider { + fn extra_data() {} +} + +impl pallet_beefy_mmr::Config for Runtime { + type LeafVersion = LeafVersion; + type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; + type LeafExtra = (); + type BeefyDataProvider = BeefyDummyDataProvider; +} + +parameter_types! { + pub const MinimumPeriod: u64 = bp_rialto::SLOT_DURATION / 2; +} + +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the UNIX epoch. + type Moment = bp_rialto::Moment; + type OnTimestampSet = Babe; + type MinimumPeriod = MinimumPeriod; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); +} + +parameter_types! { + pub const ExistentialDeposit: bp_rialto::Balance = 500; + // For weight estimation, we assume that the most locks on an individual account will be 50. + // This number may need to be adjusted in the future if this assumption no longer holds true. + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); + type MaxLocks = MaxLocks; + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + +parameter_types! { + pub const TransactionBaseFee: Balance = 0; + pub const TransactionByteFee: Balance = 1; + pub const OperationalFeeMultiplier: u8 = 5; + // values for following parameters are copied from polkadot repo, but it is fine + // not to sync them - we're not going to make Rialto a full copy of one of Polkadot-like chains + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); + pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value(); +} + +impl pallet_transaction_payment::Config for Runtime { + type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; + type OperationalFeeMultiplier = OperationalFeeMultiplier; + type WeightToFee = bp_rialto::WeightToFee; + type LengthToFee = bp_rialto::WeightToFee; + type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< + Runtime, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + MaximumMultiplier, + >; + type RuntimeEvent = RuntimeEvent; +} + +impl pallet_sudo::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; +} + +impl pallet_session::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ValidatorId = ::AccountId; + type ValidatorIdOf = (); + type ShouldEndSession = Babe; + type NextSessionRotation = Babe; + type SessionManager = pallet_shift_session_manager::Pallet; + type SessionHandler = ::KeyTypeIdProviders; + type Keys = SessionKeys; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); +} + +impl pallet_authority_discovery::Config for Runtime { + type MaxAuthorities = MaxAuthorities; +} + +impl pallet_bridge_relayers::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Reward = Balance; + type PaymentProcedure = bp_relayers::MintReward, AccountId>; + type WeightInfo = (); +} + +parameter_types! { + /// This is a pretty unscientific cap. + /// + /// Note that once this is hit the pallet will essentially throttle incoming requests down to one + /// call per block. + pub const MaxRequests: u32 = 50; + + /// Number of headers to keep. + /// + /// Assuming the worst case of every header being finalized, we will keep headers at least for a + /// week. + pub const HeadersToKeep: u32 = 7 * bp_rialto::DAYS; + + /// Maximal number of authorities at Millau. + pub const MaxAuthoritiesAtMillau: u32 = bp_millau::MAX_AUTHORITIES_COUNT; + /// Maximal size of SCALE-encoded Millau header. + pub const MaxMillauHeaderSize: u32 = bp_millau::MAX_HEADER_SIZE; +} + +pub type MillauGrandpaInstance = (); +impl pallet_bridge_grandpa::Config for Runtime { + type BridgedChain = bp_millau::Millau; + type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; + type MaxBridgedAuthorities = MaxAuthoritiesAtMillau; + type MaxBridgedHeaderSize = MaxMillauHeaderSize; + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; +} + +impl pallet_shift_session_manager::Config for Runtime {} + +parameter_types! { + pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; + pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + pub const RootAccountForPayments: Option = None; + pub const BridgedChainId: bp_runtime::ChainId = bp_runtime::MILLAU_CHAIN_ID; + pub ActiveOutboundLanes: &'static [bp_messages::LaneId] = &[millau_messages::XCM_LANE]; +} + +/// Instance of the messages pallet used to relay messages to/from Millau chain. +pub type WithMillauMessagesInstance = (); + +impl pallet_bridge_messages::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + type ActiveOutboundLanes = ActiveOutboundLanes; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type MaximalOutboundPayloadSize = crate::millau_messages::ToMillauMaximalOutboundPayloadSize; + type OutboundPayload = crate::millau_messages::ToMillauMessagePayload; + + type InboundPayload = crate::millau_messages::FromMillauMessagePayload; + type InboundRelayer = bp_millau::AccountId; + + type TargetHeaderChain = crate::millau_messages::Millau; + type LaneMessageVerifier = crate::millau_messages::ToMillauMessageVerifier; + type MessageDeliveryAndDispatchPayment = + pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter< + Runtime, + WithMillauMessagesInstance, + >; + + type SourceHeaderChain = crate::millau_messages::Millau; + type MessageDispatch = crate::millau_messages::FromMillauMessageDispatch; + type BridgedChainId = BridgedChainId; +} + +pub type MillauBeefyInstance = (); +impl pallet_bridge_beefy::Config for Runtime { + type MaxRequests = frame_support::traits::ConstU32<16>; + type CommitmentsToKeep = frame_support::traits::ConstU32<8>; + type BridgedChain = bp_millau::Millau; +} + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = opaque::Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Sudo: pallet_sudo::{Pallet, Call, Config, Storage, Event}, + + // Must be before session. + Babe: pallet_babe::{Pallet, Call, Storage, Config, ValidateUnsigned}, + + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + + // Consensus support. + AuthorityDiscovery: pallet_authority_discovery::{Pallet, Config}, + Session: pallet_session::{Pallet, Call, Storage, Event, Config}, + Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event}, + ShiftSessionManager: pallet_shift_session_manager::{Pallet}, + + // BEEFY Bridges support. + Beefy: pallet_beefy::{Pallet, Storage, Config}, + Mmr: pallet_mmr::{Pallet, Storage}, + MmrLeaf: pallet_beefy_mmr::{Pallet, Storage}, + + // Millau bridge modules. + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, + BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, + BridgeMillauMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, + + // Millau bridge modules (BEEFY based). + BridgeMillauBeefy: pallet_bridge_beefy::{Pallet, Call, Storage}, + + // Parachain modules. + ParachainsOrigin: polkadot_runtime_parachains::origin::{Pallet, Origin}, + Configuration: polkadot_runtime_parachains::configuration::{Pallet, Call, Storage, Config}, + Shared: polkadot_runtime_parachains::shared::{Pallet, Call, Storage}, + Inclusion: polkadot_runtime_parachains::inclusion::{Pallet, Call, Storage, Event}, + ParasInherent: polkadot_runtime_parachains::paras_inherent::{Pallet, Call, Storage, Inherent}, + Scheduler: polkadot_runtime_parachains::scheduler::{Pallet, Storage}, + Paras: polkadot_runtime_parachains::paras::{Pallet, Call, Storage, Event, Config}, + Initializer: polkadot_runtime_parachains::initializer::{Pallet, Call, Storage}, + Dmp: polkadot_runtime_parachains::dmp::{Pallet, Call, Storage}, + Ump: polkadot_runtime_parachains::ump::{Pallet, Call, Storage, Event}, + Hrmp: polkadot_runtime_parachains::hrmp::{Pallet, Call, Storage, Event, Config}, + SessionInfo: polkadot_runtime_parachains::session_info::{Pallet, Storage}, + ParaSessionInfo: polkadot_runtime_parachains::session_info::{Pallet, Storage}, + ParasDisputes: polkadot_runtime_parachains::disputes::{Pallet, Call, Storage, Event}, + ParasSlashing: polkadot_runtime_parachains::disputes::slashing::{Pallet, Call, Storage, ValidateUnsigned}, + + // Parachain Onboarding Pallets + Registrar: polkadot_runtime_common::paras_registrar::{Pallet, Call, Storage, Event}, + Slots: polkadot_runtime_common::slots::{Pallet, Call, Storage, Event}, + ParasSudoWrapper: polkadot_runtime_common::paras_sudo_wrapper::{Pallet, Call}, + + // Pallet for sending XCM. + XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin, Config} = 99, + } +); + +/// The address format for describing accounts. +pub type Address = sp_runtime::MultiAddress; +/// Block header type as expected by this runtime. +pub type Header = generic::Header; +/// Block type as expected by this runtime. +pub type Block = generic::Block; +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); +/// The payload being signed in transactions. +pub type SignedPayload = generic::SignedPayload; +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = generic::CheckedExtrinsic; +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block); + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl beefy_primitives::BeefyApi for Runtime { + fn validator_set() -> Option> { + Beefy::validator_set() + } + } + + impl sp_mmr_primitives::MmrApi for Runtime { + fn generate_proof(block_number: BlockNumber) + -> Result<(EncodableOpaqueLeaf, MmrProof), MmrError> + { + Mmr::generate_batch_proof(vec![block_number]) + .and_then(|(leaves, proof)| Ok(( + mmr::EncodableOpaqueLeaf::from_leaf(&leaves[0]), + mmr::BatchProof::into_single_leaf_proof(proof)? + ))) + } + + fn verify_proof(leaf: EncodableOpaqueLeaf, proof: MmrProof) + -> Result<(), MmrError> + { + pub type Leaf = < + ::LeafData as LeafDataProvider + >::LeafData; + + let leaf: Leaf = leaf + .into_opaque_leaf() + .try_decode() + .ok_or(MmrError::Verify)?; + Mmr::verify_leaves(vec![leaf], mmr::Proof::into_batch_proof(proof)) + } + + fn verify_proof_stateless( + root: Hash, + leaf: EncodableOpaqueLeaf, + proof: MmrProof + ) -> Result<(), MmrError> { + let node = DataOrHash::Data(leaf.into_opaque_leaf()); + pallet_mmr::verify_leaves_proof::( + root, + vec![node], + pallet_mmr::primitives::Proof::into_batch_proof(proof), + ) + } + + fn mmr_root() -> Result { + Ok(Mmr::mmr_root()) + } + + fn generate_batch_proof(block_numbers: Vec) + -> Result<(Vec, mmr::BatchProof), mmr::Error> + { + Mmr::generate_batch_proof(block_numbers) + .map(|(leaves, proof)| (leaves.into_iter().map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf)).collect(), proof)) + } + + fn generate_historical_batch_proof( + block_numbers: Vec, + best_known_block_number: BlockNumber + ) -> Result<(Vec, mmr::BatchProof), mmr::Error> { + Mmr::generate_historical_batch_proof(block_numbers, best_known_block_number).map( + |(leaves, proof)| { + ( + leaves + .into_iter() + .map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf)) + .collect(), + proof, + ) + }, + ) + } + + fn verify_batch_proof(leaves: Vec, proof: mmr::BatchProof) + -> Result<(), mmr::Error> + { + type Leaf = < + ::LeafData as LeafDataProvider + >::LeafData; + let leaves = leaves.into_iter().map(|leaf| + leaf.into_opaque_leaf() + .try_decode() + .ok_or(mmr::Error::Verify)).collect::, mmr::Error>>()?; + Mmr::verify_leaves(leaves, proof) + } + + fn verify_batch_proof_stateless( + root: MmrHash, + leaves: Vec, + proof: mmr::BatchProof + ) -> Result<(), mmr::Error> { + let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect(); + pallet_mmr::verify_leaves_proof::(root, nodes, proof) + } + } + + impl bp_millau::MillauFinalityApi for Runtime { + fn best_finalized() -> Option> { + BridgeMillauGrandpa::best_finalized().map(|header| header.id()) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_consensus_babe::BabeApi for Runtime { + fn configuration() -> sp_consensus_babe::BabeConfiguration { + // The choice of `c` parameter (where `1 - c` represents the + // probability of a slot being empty), is done in accordance to the + // slot duration and expected target block time, for safely + // resisting network delays of maximum two seconds. + // + sp_consensus_babe::BabeConfiguration { + slot_duration: Babe::slot_duration(), + epoch_length: EpochDuration::get(), + c: BABE_GENESIS_EPOCH_CONFIG.c, + authorities: Babe::authorities().to_vec(), + randomness: Babe::randomness(), + allowed_slots: BABE_GENESIS_EPOCH_CONFIG.allowed_slots, + } + } + + fn current_epoch_start() -> sp_consensus_babe::Slot { + Babe::current_epoch_start() + } + + fn current_epoch() -> sp_consensus_babe::Epoch { + Babe::current_epoch() + } + + fn next_epoch() -> sp_consensus_babe::Epoch { + Babe::next_epoch() + } + + fn generate_key_ownership_proof( + _slot: sp_consensus_babe::Slot, + _authority_id: sp_consensus_babe::AuthorityId, + ) -> Option { + None + } + + fn submit_report_equivocation_unsigned_extrinsic( + equivocation_proof: sp_consensus_babe::EquivocationProof<::Header>, + key_owner_proof: sp_consensus_babe::OpaqueKeyOwnershipProof, + ) -> Option<()> { + let key_owner_proof = key_owner_proof.decode()?; + + Babe::submit_unsigned_equivocation_report( + equivocation_proof, + key_owner_proof, + ) + } + } + + impl polkadot_primitives::runtime_api::ParachainHost for Runtime { + fn validators() -> Vec { + polkadot_runtime_parachains::runtime_api_impl::v2::validators::() + } + + fn validator_groups() -> (Vec>, polkadot_primitives::v2::GroupRotationInfo) { + polkadot_runtime_parachains::runtime_api_impl::v2::validator_groups::() + } + + fn availability_cores() -> Vec> { + polkadot_runtime_parachains::runtime_api_impl::v2::availability_cores::() + } + + fn persisted_validation_data(para_id: polkadot_primitives::v2::Id, assumption: polkadot_primitives::v2::OccupiedCoreAssumption) + -> Option> { + polkadot_runtime_parachains::runtime_api_impl::v2::persisted_validation_data::(para_id, assumption) + } + + fn assumed_validation_data( + para_id: polkadot_primitives::v2::Id, + expected_persisted_validation_data_hash: Hash, + ) -> Option<(polkadot_primitives::v2::PersistedValidationData, polkadot_primitives::v2::ValidationCodeHash)> { + polkadot_runtime_parachains::runtime_api_impl::v2::assumed_validation_data::( + para_id, + expected_persisted_validation_data_hash, + ) + } + + fn check_validation_outputs( + para_id: polkadot_primitives::v2::Id, + outputs: polkadot_primitives::v2::CandidateCommitments, + ) -> bool { + polkadot_runtime_parachains::runtime_api_impl::v2::check_validation_outputs::(para_id, outputs) + } + + fn session_index_for_child() -> polkadot_primitives::v2::SessionIndex { + polkadot_runtime_parachains::runtime_api_impl::v2::session_index_for_child::() + } + + fn validation_code(para_id: polkadot_primitives::v2::Id, assumption: polkadot_primitives::v2::OccupiedCoreAssumption) + -> Option { + polkadot_runtime_parachains::runtime_api_impl::v2::validation_code::(para_id, assumption) + } + + fn candidate_pending_availability(para_id: polkadot_primitives::v2::Id) -> Option> { + polkadot_runtime_parachains::runtime_api_impl::v2::candidate_pending_availability::(para_id) + } + + fn candidate_events() -> Vec> { + polkadot_runtime_parachains::runtime_api_impl::v2::candidate_events::(|ev| { + match ev { + RuntimeEvent::Inclusion(ev) => { + Some(ev) + } + _ => None, + } + }) + } + + fn session_info(index: polkadot_primitives::v2::SessionIndex) -> Option { + polkadot_runtime_parachains::runtime_api_impl::v2::session_info::(index) + } + + fn dmq_contents(recipient: polkadot_primitives::v2::Id) -> Vec> { + polkadot_runtime_parachains::runtime_api_impl::v2::dmq_contents::(recipient) + } + + fn inbound_hrmp_channels_contents( + recipient: polkadot_primitives::v2::Id + ) -> BTreeMap>> { + polkadot_runtime_parachains::runtime_api_impl::v2::inbound_hrmp_channels_contents::(recipient) + } + + fn validation_code_by_hash(hash: polkadot_primitives::v2::ValidationCodeHash) -> Option { + polkadot_runtime_parachains::runtime_api_impl::v2::validation_code_by_hash::(hash) + } + + fn on_chain_votes() -> Option> { + polkadot_runtime_parachains::runtime_api_impl::v2::on_chain_votes::() + } + + fn submit_pvf_check_statement(stmt: polkadot_primitives::v2::PvfCheckStatement, signature: polkadot_primitives::v2::ValidatorSignature) { + polkadot_runtime_parachains::runtime_api_impl::v2::submit_pvf_check_statement::(stmt, signature) + } + + fn pvfs_require_precheck() -> Vec { + polkadot_runtime_parachains::runtime_api_impl::v2::pvfs_require_precheck::() + } + + fn validation_code_hash(para_id: polkadot_primitives::v2::Id, assumption: polkadot_primitives::v2::OccupiedCoreAssumption) + -> Option + { + polkadot_runtime_parachains::runtime_api_impl::v2::validation_code_hash::(para_id, assumption) + } + } + + impl sp_authority_discovery::AuthorityDiscoveryApi for Runtime { + fn authorities() -> Vec { + polkadot_runtime_parachains::runtime_api_impl::v2::relevant_authority_ids::() + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi< + Block, + Balance, + > for Runtime { + fn query_info(uxt: ::Extrinsic, len: u32) -> RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details(uxt: ::Extrinsic, len: u32) -> FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, sp_core::crypto::KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl fg_primitives::GrandpaApi for Runtime { + fn current_set_id() -> fg_primitives::SetId { + Grandpa::current_set_id() + } + + fn grandpa_authorities() -> GrandpaAuthorityList { + Grandpa::grandpa_authorities() + } + + fn submit_report_equivocation_unsigned_extrinsic( + equivocation_proof: fg_primitives::EquivocationProof< + ::Hash, + NumberFor, + >, + key_owner_proof: fg_primitives::OpaqueKeyOwnershipProof, + ) -> Option<()> { + let key_owner_proof = key_owner_proof.decode()?; + + Grandpa::submit_unsigned_equivocation_report( + equivocation_proof, + key_owner_proof, + ) + } + + fn generate_key_ownership_proof( + _set_id: fg_primitives::SetId, + _authority_id: GrandpaId, + ) -> Option { + // NOTE: this is the only implementation possible since we've + // defined our key owner proof type as a bottom type (i.e. a type + // with no values). + None + } + } + + impl bp_millau::ToMillauOutboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + begin: bp_messages::MessageNonce, + end: bp_messages::MessageNonce, + ) -> Vec { + bridge_runtime_common::messages_api::outbound_message_details::< + Runtime, + WithMillauMessagesInstance, + >(lane, begin, end) + } + } + + impl bp_millau::FromMillauInboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + ) -> Vec { + bridge_runtime_common::messages_api::inbound_message_details::< + Runtime, + WithMillauMessagesInstance, + >(lane, messages) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn call_size() { + const BRIDGES_PALLETS_MAX_CALL_SIZE: usize = 200; + assert!( + core::mem::size_of::>() <= + BRIDGES_PALLETS_MAX_CALL_SIZE + ); + assert!( + core::mem::size_of::>() <= + BRIDGES_PALLETS_MAX_CALL_SIZE + ); + // Largest inner Call is `pallet_session::Call` with a size of 224 bytes. This size is a + // result of large `SessionKeys` struct. + // Total size of Rialto runtime Call is 232. + const MAX_CALL_SIZE: usize = 232; + assert!(core::mem::size_of::() <= MAX_CALL_SIZE); + } +} diff --git a/bin/rialto/runtime/src/millau_messages.rs b/bin/rialto/runtime/src/millau_messages.rs new file mode 100644 index 00000000000..66480c3168d --- /dev/null +++ b/bin/rialto/runtime/src/millau_messages.rs @@ -0,0 +1,242 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything required to serve Millau <-> Rialto messages. + +use crate::{MillauGrandpaInstance, Runtime, RuntimeCall, RuntimeOrigin}; + +use bp_messages::{ + source_chain::TargetHeaderChain, + target_chain::{ProvedMessages, SourceHeaderChain}, + InboundLaneData, LaneId, Message, MessageNonce, +}; +use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; +use bridge_runtime_common::messages::{self, MessageBridge}; +use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; + +/// Lane that is used for XCM messages exchange. +pub const XCM_LANE: LaneId = [0, 0, 0, 0]; +/// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge +/// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual +/// tests, confirming that we don't break encoding somewhere between. +pub const BASE_XCM_WEIGHT_TWICE: u64 = 2 * crate::xcm_config::BASE_XCM_WEIGHT; + +parameter_types! { + /// Weight credit for our test messages. + /// + /// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge + /// (it is prepended with `UniversalOrigin` instruction). + pub const WeightCredit: Weight = Weight::from_ref_time(BASE_XCM_WEIGHT_TWICE); +} + +/// Message payload for Rialto -> Millau messages. +pub type ToMillauMessagePayload = messages::source::FromThisChainMessagePayload; + +/// Message verifier for Rialto -> Millau messages. +pub type ToMillauMessageVerifier = + messages::source::FromThisChainMessageVerifier; + +/// Message payload for Millau -> Rialto messages. +pub type FromMillauMessagePayload = messages::target::FromBridgedChainMessagePayload; + +/// Call-dispatch based message dispatch for Millau -> Rialto messages. +pub type FromMillauMessageDispatch = messages::target::FromBridgedChainMessageDispatch< + WithMillauMessageBridge, + xcm_executor::XcmExecutor, + crate::xcm_config::XcmWeigher, + WeightCredit, +>; + +/// Messages proof for Millau -> Rialto messages. +pub type FromMillauMessagesProof = messages::target::FromBridgedChainMessagesProof; + +/// Messages delivery proof for Rialto -> Millau messages. +pub type ToMillauMessagesDeliveryProof = + messages::source::FromBridgedChainMessagesDeliveryProof; + +/// Maximal outbound payload size of Rialto -> Millau messages. +pub type ToMillauMaximalOutboundPayloadSize = + messages::source::FromThisChainMaximalOutboundPayloadSize; + +/// Millau <-> Rialto message bridge. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct WithMillauMessageBridge; + +impl MessageBridge for WithMillauMessageBridge { + const THIS_CHAIN_ID: ChainId = RIALTO_CHAIN_ID; + const BRIDGED_CHAIN_ID: ChainId = MILLAU_CHAIN_ID; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME; + + type ThisChain = Rialto; + type BridgedChain = Millau; + type BridgedHeaderChain = + pallet_bridge_grandpa::GrandpaChainHeaders; +} + +/// Rialto chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Rialto; + +impl messages::UnderlyingChainProvider for Rialto { + type Chain = bp_rialto::Rialto; +} + +impl messages::ThisChainWithMessages for Rialto { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + + fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, _lane: &LaneId) -> bool { + true + } + + fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { + MessageNonce::MAX + } +} + +/// Millau chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Millau; + +impl messages::UnderlyingChainProvider for Millau { + type Chain = bp_millau::Millau; +} + +impl messages::BridgedChainWithMessages for Millau { + fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { + true + } +} + +impl TargetHeaderChain for Millau { + type Error = &'static str; + // The proof is: + // - hash of the header this proof has been created with; + // - the storage proof of one or several keys; + // - id of the lane we prove state of. + type MessagesDeliveryProof = ToMillauMessagesDeliveryProof; + + fn verify_message(payload: &ToMillauMessagePayload) -> Result<(), Self::Error> { + messages::source::verify_chain_message::(payload) + } + + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData), Self::Error> { + messages::source::verify_messages_delivery_proof::(proof) + } +} + +impl SourceHeaderChain for Millau { + type Error = &'static str; + // The proof is: + // - hash of the header this proof has been created with; + // - the storage proof of one or several keys; + // - id of the lane we prove messages for; + // - inclusive range of messages nonces that are proved. + type MessagesProof = FromMillauMessagesProof; + + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result, Self::Error> { + messages::target::verify_messages_proof::(proof, messages_count) + .map_err(Into::into) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{MillauGrandpaInstance, Runtime, WithMillauMessagesInstance}; + use bp_runtime::Chain; + use bridge_runtime_common::{ + assert_complete_bridge_types, + integrity::{ + assert_complete_bridge_constants, AssertBridgeMessagesPalletConstants, + AssertBridgePalletNames, AssertChainConstants, AssertCompleteBridgeConstants, + }, + }; + + #[test] + fn ensure_rialto_message_lane_weights_are_correct() { + type Weights = pallet_bridge_messages::weights::BridgeWeight; + + pallet_bridge_messages::ensure_weights_are_correct::(); + + let max_incoming_message_proof_size = bp_millau::EXTRA_STORAGE_PROOF_SIZE.saturating_add( + messages::target::maximal_incoming_message_size(bp_rialto::Rialto::max_extrinsic_size()), + ); + pallet_bridge_messages::ensure_able_to_receive_message::( + bp_rialto::Rialto::max_extrinsic_size(), + bp_rialto::Rialto::max_extrinsic_weight(), + max_incoming_message_proof_size, + messages::target::maximal_incoming_message_dispatch_weight( + bp_rialto::Rialto::max_extrinsic_weight(), + ), + ); + + let max_incoming_inbound_lane_data_proof_size = + bp_messages::InboundLaneData::<()>::encoded_size_hint_u32( + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX as _, + ); + pallet_bridge_messages::ensure_able_to_receive_confirmation::( + bp_rialto::Rialto::max_extrinsic_size(), + bp_rialto::Rialto::max_extrinsic_weight(), + max_incoming_inbound_lane_data_proof_size, + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + ); + } + + #[test] + fn ensure_bridge_integrity() { + assert_complete_bridge_types!( + runtime: Runtime, + with_bridged_chain_grandpa_instance: MillauGrandpaInstance, + with_bridged_chain_messages_instance: WithMillauMessagesInstance, + bridge: WithMillauMessageBridge, + this_chain: bp_rialto::Rialto, + bridged_chain: bp_millau::Millau, + ); + + assert_complete_bridge_constants::< + Runtime, + MillauGrandpaInstance, + WithMillauMessagesInstance, + WithMillauMessageBridge, + bp_rialto::Rialto, + >(AssertCompleteBridgeConstants { + this_chain_constants: AssertChainConstants { + block_length: bp_rialto::BlockLength::get(), + block_weights: bp_rialto::BlockWeights::get(), + }, + messages_pallet_constants: AssertBridgeMessagesPalletConstants { + max_unrewarded_relayers_in_bridged_confirmation_tx: + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + max_unconfirmed_messages_in_bridged_confirmation_tx: + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + bridged_chain_id: bp_runtime::MILLAU_CHAIN_ID, + }, + pallet_names: AssertBridgePalletNames { + with_this_chain_messages_pallet_name: bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME, + with_bridged_chain_grandpa_pallet_name: bp_millau::WITH_MILLAU_GRANDPA_PALLET_NAME, + with_bridged_chain_messages_pallet_name: + bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME, + }, + }); + } +} diff --git a/bin/rialto/runtime/src/parachains.rs b/bin/rialto/runtime/src/parachains.rs new file mode 100644 index 00000000000..960931a0b3c --- /dev/null +++ b/bin/rialto/runtime/src/parachains.rs @@ -0,0 +1,210 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Parachains support in Rialto runtime. + +use crate::{ + AccountId, Babe, Balance, Balances, BlockNumber, Registrar, Runtime, RuntimeCall, RuntimeEvent, + RuntimeOrigin, ShiftSessionManager, Slots, UncheckedExtrinsic, +}; + +use frame_support::{parameter_types, traits::KeyOwnerProofSystem, weights::Weight}; +use frame_system::EnsureRoot; +use polkadot_primitives::v2::{ValidatorId, ValidatorIndex}; +use polkadot_runtime_common::{paras_registrar, paras_sudo_wrapper, slots}; +use polkadot_runtime_parachains::{ + configuration as parachains_configuration, disputes as parachains_disputes, + disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, + inclusion as parachains_inclusion, initializer as parachains_initializer, + origin as parachains_origin, paras as parachains_paras, + paras_inherent as parachains_paras_inherent, scheduler as parachains_scheduler, + session_info as parachains_session_info, shared as parachains_shared, ump as parachains_ump, +}; +use sp_core::crypto::KeyTypeId; +use sp_runtime::transaction_validity::TransactionPriority; + +impl frame_system::offchain::SendTransactionTypes for Runtime +where + RuntimeCall: From, +{ + type Extrinsic = UncheckedExtrinsic; + type OverarchingCall = RuntimeCall; +} + +/// Special `RewardValidators` that does nothing ;) +pub struct RewardValidators; +impl polkadot_runtime_parachains::inclusion::RewardValidators for RewardValidators { + fn reward_backing(_: impl IntoIterator) {} + fn reward_bitfields(_: impl IntoIterator) {} +} + +// all required parachain modules from `polkadot-runtime-parachains` crate + +impl parachains_configuration::Config for Runtime { + type WeightInfo = parachains_configuration::TestWeightInfo; +} + +impl parachains_dmp::Config for Runtime {} + +impl parachains_hrmp::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type Currency = Balances; + type WeightInfo = parachains_hrmp::TestWeightInfo; +} + +impl parachains_inclusion::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RewardValidators = RewardValidators; + type DisputesHandler = (); +} + +impl parachains_initializer::Config for Runtime { + type Randomness = pallet_babe::RandomnessFromOneEpochAgo; + type ForceOrigin = EnsureRoot; + type WeightInfo = (); +} + +impl parachains_disputes::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RewardValidators = (); + type SlashingHandler = (); + type WeightInfo = parachains_disputes::TestWeightInfo; +} + +impl parachains_slashing::Config for Runtime { + type KeyOwnerProofSystem = (); + type KeyOwnerProof = + >::Proof; + type KeyOwnerIdentification = >::IdentificationTuple; + type HandleReports = (); + type WeightInfo = parachains_slashing::TestWeightInfo; + type BenchmarkingConfig = parachains_slashing::BenchConfig<200>; +} + +impl parachains_origin::Config for Runtime {} + +parameter_types! { + pub const ParasUnsignedPriority: TransactionPriority = TransactionPriority::max_value(); +} + +impl parachains_paras::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = parachains_paras::TestWeightInfo; + type UnsignedPriority = ParasUnsignedPriority; + type NextSessionRotation = Babe; +} + +impl parachains_paras_inherent::Config for Runtime { + type WeightInfo = parachains_paras_inherent::TestWeightInfo; +} + +impl parachains_scheduler::Config for Runtime {} + +impl parachains_session_info::Config for Runtime { + type ValidatorSet = ShiftSessionManager; +} + +impl parachains_shared::Config for Runtime {} + +parameter_types! { + pub const FirstMessageFactorPercent: u64 = 100; +} + +impl parachains_ump::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UmpSink = (); + type FirstMessageFactorPercent = FirstMessageFactorPercent; + type ExecuteOverweightOrigin = EnsureRoot; + type WeightInfo = parachains_ump::TestWeightInfo; +} + +// required onboarding pallets. We're not going to use auctions or crowdloans, so they're missing + +parameter_types! { + pub const ParaDeposit: Balance = 0; + pub const DataDepositPerByte: Balance = 0; +} + +impl paras_registrar::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type Currency = Balances; + type OnSwap = Slots; + type ParaDeposit = ParaDeposit; + type DataDepositPerByte = DataDepositPerByte; + type WeightInfo = paras_registrar::TestWeightInfo; +} + +parameter_types! { + pub const LeasePeriod: BlockNumber = 10 * bp_rialto::MINUTES; +} + +impl slots::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type Registrar = Registrar; + type LeasePeriod = LeasePeriod; + type WeightInfo = slots::TestWeightInfo; + type LeaseOffset = (); + type ForceOrigin = EnsureRoot; +} + +impl paras_sudo_wrapper::Config for Runtime {} + +pub struct ZeroWeights; + +impl polkadot_runtime_common::paras_registrar::WeightInfo for ZeroWeights { + fn reserve() -> Weight { + Weight::from_ref_time(0) + } + fn register() -> Weight { + Weight::from_ref_time(0) + } + fn force_register() -> Weight { + Weight::from_ref_time(0) + } + fn deregister() -> Weight { + Weight::from_ref_time(0) + } + fn swap() -> Weight { + Weight::from_ref_time(0) + } + fn schedule_code_upgrade(_: u32) -> Weight { + Weight::from_ref_time(0) + } + fn set_current_head(_: u32) -> Weight { + Weight::from_ref_time(0) + } +} + +impl polkadot_runtime_common::slots::WeightInfo for ZeroWeights { + fn force_lease() -> Weight { + Weight::from_ref_time(0) + } + fn manage_lease_period_start(_c: u32, _t: u32) -> Weight { + Weight::from_ref_time(0) + } + fn clear_all_leases() -> Weight { + Weight::from_ref_time(0) + } + fn trigger_onboard() -> Weight { + Weight::from_ref_time(0) + } +} diff --git a/bin/rialto/runtime/src/xcm_config.rs b/bin/rialto/runtime/src/xcm_config.rs new file mode 100644 index 00000000000..892c1ed747b --- /dev/null +++ b/bin/rialto/runtime/src/xcm_config.rs @@ -0,0 +1,286 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! XCM configurations for the Rialto runtime. + +use super::{ + millau_messages::WithMillauMessageBridge, AccountId, AllPalletsWithSystem, Balances, Runtime, + RuntimeCall, RuntimeEvent, RuntimeOrigin, WithMillauMessagesInstance, XcmPallet, +}; +use bp_rialto::WeightToFee; +use bridge_runtime_common::{ + messages::source::{XcmBridge, XcmBridgeAdapter}, + CustomNetworkId, +}; +use frame_support::{ + parameter_types, + traits::{Everything, Nothing}, +}; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, + CurrencyAdapter as XcmCurrencyAdapter, IsConcrete, MintLocation, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, +}; + +parameter_types! { + /// The location of the `MLAU` token, from the context of this chain. Since this token is native to this + /// chain, we make it synonymous with it and thus it is the `Here` location, which means "equivalent to + /// the context". + pub const TokenLocation: MultiLocation = Here.into_location(); + /// The Rialto network ID. + pub const ThisNetwork: NetworkId = CustomNetworkId::Rialto.as_network_id(); + /// The Millau network ID. + pub const MillauNetwork: NetworkId = CustomNetworkId::Millau.as_network_id(); + + /// Our XCM location ancestry - i.e. our location within the Consensus Universe. + /// + /// Since Polkadot is a top-level relay-chain with its own consensus, it's just our network ID. + pub UniversalLocation: InteriorMultiLocation = ThisNetwork::get().into(); + /// The check account, which holds any native assets that have been teleported out and not back in (yet). + pub CheckAccount: (AccountId, MintLocation) = (XcmPallet::check_account(), MintLocation::Local); +} + +/// The canonical means of converting a `MultiLocation` into an `AccountId`, used when we want to +/// determine the sovereign account controlled by a location. +pub type SovereignAccountOf = ( + // We can directly alias an `AccountId32` into a local account. + AccountId32Aliases, +); + +/// Our asset transactor. This is what allows us to interest with the runtime facilities from the +/// point of view of XCM-only concepts like `MultiLocation` and `MultiAsset`. +/// +/// Ours is only aware of the Balances pallet, which is mapped to `TokenLocation`. +pub type LocalAssetTransactor = XcmCurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // We can convert the MultiLocations with our converter above: + SovereignAccountOf, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We track our teleports in/out to keep total issuance correct. + CheckAccount, +>; + +/// The means that we convert the XCM message origin location into a local dispatch origin. +type LocalOriginConverter = ( + // A `Signed` origin of the sovereign account that the original location controls. + SovereignSignedViaLocation, + // The AccountId32 location type can be expressed natively as a `Signed` origin. + SignedAccountId32AsNative, +); + +/// The amount of weight an XCM operation takes. This is a safe overestimate. +pub const BASE_XCM_WEIGHT: u64 = 1_000_000_000; + +parameter_types! { + /// The amount of weight an XCM operation takes. This is a safe overestimate. + pub const BaseXcmWeight: u64 = BASE_XCM_WEIGHT; + /// Maximum number of instructions in a single XCM fragment. A sanity check against weight + /// calculations getting too crazy. + pub const MaxInstructions: u32 = 100; +} + +/// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our +/// individual routers. +pub type XcmRouter = ( + // Router to send messages to Millau. + XcmBridgeAdapter, +); + +parameter_types! { + pub const MaxAssetsIntoHolding: u32 = 64; +} + +/// The barriers one of which must be passed for an XCM message to be executed. +pub type Barrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Expected responses are OK. + AllowKnownQueryResponses, +); + +/// Incoming XCM weigher type. +pub type XcmWeigher = xcm_builder::FixedWeightBounds; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = XcmWeigher; + // The weight trader piggybacks on the existing transaction-fee conversion logic. + type Trader = UsingComponents; + type ResponseHandler = XcmPallet; + type AssetTrap = XcmPallet; + type AssetLocker = (); + type AssetExchanger = (); + type AssetClaims = XcmPallet; + type SubscriptionService = XcmPallet; + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; +} + +/// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior +/// location of this chain. +pub type LocalOriginToLocation = ( + // Usual Signed origin to be used in XCM as a corresponding AccountId32 + SignedToAccountId32, +); + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + // We don't allow any messages to be sent via the transaction yet. This is basically safe to + // enable, (safe the possibility of someone spamming the parachain if they're willing to pay + // the DOT to send from the Relay-chain). But it's useless until we bring in XCM v3 which will + // make `DescendOrigin` a bit more useful. + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = XcmRouter; + // Anyone can execute XCM messages locally. + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = xcm_executor::XcmExecutor; + // Anyone is able to use teleportation regardless of who they are and what they want to + // teleport. + type XcmTeleportFilter = Everything; + // Anyone is able to use reserve transfers regardless of who they are and what they want to + // transfer. + type XcmReserveTransferFilter = Everything; + type Weigher = XcmWeigher; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = SovereignAccountOf; + type MaxLockers = frame_support::traits::ConstU32<8>; +} + +/// With-Millau bridge. +pub struct ToMillauBridge; + +impl XcmBridge for ToMillauBridge { + type MessageBridge = WithMillauMessageBridge; + type MessageSender = pallet_bridge_messages::Pallet; + + fn universal_location() -> InteriorMultiLocation { + UniversalLocation::get() + } + + fn verify_destination(dest: &MultiLocation) -> bool { + matches!(*dest, MultiLocation { parents: 1, interior: X1(GlobalConsensus(r)) } if r == MillauNetwork::get()) + } + + fn build_destination() -> MultiLocation { + let dest: InteriorMultiLocation = MillauNetwork::get().into(); + let here = UniversalLocation::get(); + dest.relative_to(&here) + } + + fn xcm_lane() -> bp_messages::LaneId { + [0, 0, 0, 0] + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::millau_messages::WeightCredit; + use bp_messages::{ + target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, + MessageKey, + }; + use bp_runtime::messages::MessageDispatchResult; + use bridge_runtime_common::messages::target::FromBridgedChainMessageDispatch; + use codec::Encode; + + fn new_test_ext() -> sp_io::TestExternalities { + sp_io::TestExternalities::new( + frame_system::GenesisConfig::default().build_storage::().unwrap(), + ) + } + + #[test] + fn xcm_messages_to_millau_are_sent() { + new_test_ext().execute_with(|| { + // the encoded message (origin ++ xcm) is 0x010109030419A8 + let dest = (Parent, X1(GlobalConsensus(MillauNetwork::get()))); + let xcm: Xcm<()> = vec![Instruction::Trap(42)].into(); + + let send_result = send_xcm::(dest.into(), xcm); + let expected_fee = MultiAssets::from((Here, 1_000_000_u128)); + let expected_hash = + ([0u8, 0u8, 0u8, 0u8], 1u64).using_encoded(sp_io::hashing::blake2_256); + assert_eq!(send_result, Ok((expected_hash, expected_fee)),); + }) + } + + #[test] + fn xcm_messages_from_millau_are_dispatched() { + type XcmExecutor = xcm_executor::XcmExecutor; + type MessageDispatcher = FromBridgedChainMessageDispatch< + WithMillauMessageBridge, + XcmExecutor, + XcmWeigher, + WeightCredit, + >; + + new_test_ext().execute_with(|| { + let location: MultiLocation = + (Parent, X1(GlobalConsensus(MillauNetwork::get()))).into(); + let xcm: Xcm = vec![Instruction::Trap(42)].into(); + + let mut incoming_message = DispatchMessage { + key: MessageKey { lane_id: [0, 0, 0, 0], nonce: 1 }, + data: DispatchMessageData { payload: Ok((location, xcm).into()) }, + }; + + let dispatch_weight = MessageDispatcher::dispatch_weight(&mut incoming_message); + assert_eq!( + dispatch_weight, + frame_support::weights::Weight::from_ref_time(1_000_000_000) + ); + + let dispatch_result = + MessageDispatcher::dispatch(&AccountId::from([0u8; 32]), incoming_message); + assert_eq!( + dispatch_result, + MessageDispatchResult { + unspent_weight: frame_support::weights::Weight::from_ref_time(0), + dispatch_fee_paid_during_dispatch: false, + dispatch_level_result: (), + } + ); + }) + } +} diff --git a/bin/runtime-common/Cargo.toml b/bin/runtime-common/Cargo.toml new file mode 100644 index 00000000000..4f8f7e34c40 --- /dev/null +++ b/bin/runtime-common/Cargo.toml @@ -0,0 +1,84 @@ +[package] +name = "bridge-runtime-common" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +repository = "https://github.com/paritytech/parity-bridges-common/" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } +hash-db = { version = "0.15.2", default-features = false } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +static_assertions = { version = "1.1", optional = true } + +# Bridge dependencies + +bp-header-chain = { path = "../../primitives/header-chain", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } +bp-parachains = { path = "../../primitives/parachains", default-features = false } +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +pallet-bridge-grandpa = { path = "../../modules/grandpa", default-features = false } +pallet-bridge-messages = { path = "../../modules/messages", default-features = false } +pallet-bridge-parachains = { path = "../../modules/parachains", default-features = false } + +# Substrate dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +# Polkadot dependencies +pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + +[dev-dependencies] +millau-runtime = { path = "../millau/runtime" } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "bp-messages/std", + "bp-parachains/std", + "bp-polkadot-core/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "frame-system/std", + "hash-db/std", + "log/std", + "pallet-bridge-grandpa/std", + "pallet-bridge-messages/std", + "pallet-bridge-parachains/std", + "pallet-xcm/std", + "scale-info/std", + "sp-api/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", +] +runtime-benchmarks = [ + "pallet-balances", + "pallet-bridge-grandpa/runtime-benchmarks", + "pallet-bridge-messages/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", +] +integrity-test = [ + "static_assertions", +] diff --git a/bin/runtime-common/src/integrity.rs b/bin/runtime-common/src/integrity.rs new file mode 100644 index 00000000000..4c69a29b821 --- /dev/null +++ b/bin/runtime-common/src/integrity.rs @@ -0,0 +1,291 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Integrity tests for chain constants and pallets configuration. +//! +//! Most of the tests in this module assume that the bridge is using standard (see `crate::messages` +//! module for details) configuration. + +use crate::messages::MessageBridge; + +use bp_messages::MessageNonce; +use bp_runtime::{Chain, ChainId}; +use codec::Encode; +use frame_support::{storage::generator::StorageValue, traits::Get}; +use frame_system::limits; + +/// Macro that ensures that the runtime configuration and chain primitives crate are sharing +/// the same types (index, block number, hash, hasher, account id and header). +#[macro_export] +macro_rules! assert_chain_types( + ( runtime: $r:path, this_chain: $this:path ) => { + { + // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard + // configuration is used), or something has broke existing configuration (meaning that all bridged chains + // and relays will stop functioning) + use frame_system::Config as SystemConfig; + use static_assertions::assert_type_eq_all; + + assert_type_eq_all!(<$r as SystemConfig>::Index, bp_runtime::IndexOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::BlockNumber, bp_runtime::BlockNumberOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::Hash, bp_runtime::HashOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::Hashing, bp_runtime::HasherOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::AccountId, bp_runtime::AccountIdOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::Header, bp_runtime::HeaderOf<$this>); + } + } +); + +/// Macro that ensures that the bridge GRANDPA pallet is configured properly to bridge with given +/// chain. +#[macro_export] +macro_rules! assert_bridge_grandpa_pallet_types( + ( runtime: $r:path, with_bridged_chain_grandpa_instance: $i:path, bridged_chain: $bridged:path ) => { + { + // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard + // configuration is used), or something has broke existing configuration (meaning that all bridged chains + // and relays will stop functioning) + use pallet_bridge_grandpa::Config as GrandpaConfig; + use static_assertions::assert_type_eq_all; + + assert_type_eq_all!(<$r as GrandpaConfig<$i>>::BridgedChain, $bridged); + } + } +); + +/// Macro that ensures that the bridge messages pallet is configured properly to bridge using given +/// configuration. +#[macro_export] +macro_rules! assert_bridge_messages_pallet_types( + ( + runtime: $r:path, + with_bridged_chain_messages_instance: $i:path, + bridge: $bridge:path + ) => { + { + // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard + // configuration is used), or something has broke existing configuration (meaning that all bridged chains + // and relays will stop functioning) + use $crate::messages::{ + source::FromThisChainMessagePayload, + target::FromBridgedChainMessagePayload, + AccountIdOf, BalanceOf, BridgedChain, CallOf, ThisChain, + }; + use pallet_bridge_messages::Config as MessagesConfig; + use static_assertions::assert_type_eq_all; + + assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, FromThisChainMessagePayload); + + assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundPayload, FromBridgedChainMessagePayload>>); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundRelayer, AccountIdOf>); + + assert_type_eq_all!(<$r as MessagesConfig<$i>>::TargetHeaderChain, BridgedChain<$bridge>); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::SourceHeaderChain, BridgedChain<$bridge>); + } + } +); + +/// Macro that combines four other macro calls - `assert_chain_types`, `assert_bridge_types`, +/// `assert_bridge_grandpa_pallet_types` and `assert_bridge_messages_pallet_types`. It may be used +/// at the chain that is implemeting complete standard messages bridge (i.e. with bridge GRANDPA and +/// messages pallets deployed). +#[macro_export] +macro_rules! assert_complete_bridge_types( + ( + runtime: $r:path, + with_bridged_chain_grandpa_instance: $gi:path, + with_bridged_chain_messages_instance: $mi:path, + bridge: $bridge:path, + this_chain: $this:path, + bridged_chain: $bridged:path, + ) => { + $crate::assert_chain_types!(runtime: $r, this_chain: $this); + $crate::assert_bridge_grandpa_pallet_types!( + runtime: $r, + with_bridged_chain_grandpa_instance: $gi, + bridged_chain: $bridged + ); + $crate::assert_bridge_messages_pallet_types!( + runtime: $r, + with_bridged_chain_messages_instance: $mi, + bridge: $bridge + ); + } +); + +/// Parameters for asserting chain-related constants. +#[derive(Debug)] +pub struct AssertChainConstants { + /// Block length limits of the chain. + pub block_length: limits::BlockLength, + /// Block weight limits of the chain. + pub block_weights: limits::BlockWeights, +} + +/// Test that our hardcoded, chain-related constants, are matching chain runtime configuration. +/// +/// In particular, this test ensures that: +/// +/// 1) block weight limits are matching; +/// 2) block size limits are matching. +pub fn assert_chain_constants(params: AssertChainConstants) +where + R: frame_system::Config, + C: Chain, +{ + // we don't check runtime version here, because in our case we'll be building relay from one + // repo and runtime will live in another repo, along with outdated relay version. To avoid + // unneeded commits, let's not raise an error in case of version mismatch. + + // if one of following assert fails, it means that we may need to upgrade bridged chain and + // relay to use updated constants. If constants are now smaller than before, it may lead to + // undeliverable messages. + + // `BlockLength` struct is not implementing `PartialEq`, so we compare encoded values here. + assert_eq!( + R::BlockLength::get().encode(), + params.block_length.encode(), + "BlockLength from runtime ({:?}) differ from hardcoded: {:?}", + R::BlockLength::get(), + params.block_length, + ); + // `BlockWeights` struct is not implementing `PartialEq`, so we compare encoded values here + assert_eq!( + R::BlockWeights::get().encode(), + params.block_weights.encode(), + "BlockWeights from runtime ({:?}) differ from hardcoded: {:?}", + R::BlockWeights::get(), + params.block_weights, + ); +} + +/// Test that the constants, used in GRANDPA pallet configuration are valid. +pub fn assert_bridge_grandpa_pallet_constants() +where + R: pallet_bridge_grandpa::Config, + GI: 'static, +{ + assert!( + R::MaxRequests::get() > 0, + "MaxRequests ({}) must be larger than zero", + R::MaxRequests::get(), + ); +} + +/// Parameters for asserting messages pallet constants. +#[derive(Debug)] +pub struct AssertBridgeMessagesPalletConstants { + /// Maximal number of unrewarded relayer entries in a confirmation transaction at the bridged + /// chain. + pub max_unrewarded_relayers_in_bridged_confirmation_tx: MessageNonce, + /// Maximal number of unconfirmed messages in a confirmation transaction at the bridged chain. + pub max_unconfirmed_messages_in_bridged_confirmation_tx: MessageNonce, + /// Identifier of the bridged chain. + pub bridged_chain_id: ChainId, +} + +/// Test that the constants, used in messages pallet configuration are valid. +pub fn assert_bridge_messages_pallet_constants(params: AssertBridgeMessagesPalletConstants) +where + R: pallet_bridge_messages::Config, + MI: 'static, +{ + assert!( + !R::ActiveOutboundLanes::get().is_empty(), + "ActiveOutboundLanes ({:?}) must not be empty", + R::ActiveOutboundLanes::get(), + ); + assert!( + R::MaxUnrewardedRelayerEntriesAtInboundLane::get() <= params.max_unrewarded_relayers_in_bridged_confirmation_tx, + "MaxUnrewardedRelayerEntriesAtInboundLane ({}) must be <= than the hardcoded value for bridged chain: {}", + R::MaxUnrewardedRelayerEntriesAtInboundLane::get(), + params.max_unrewarded_relayers_in_bridged_confirmation_tx, + ); + assert!( + R::MaxUnconfirmedMessagesAtInboundLane::get() <= params.max_unconfirmed_messages_in_bridged_confirmation_tx, + "MaxUnrewardedRelayerEntriesAtInboundLane ({}) must be <= than the hardcoded value for bridged chain: {}", + R::MaxUnconfirmedMessagesAtInboundLane::get(), + params.max_unconfirmed_messages_in_bridged_confirmation_tx, + ); + assert_eq!(R::BridgedChainId::get(), params.bridged_chain_id); +} + +/// Parameters for asserting bridge pallet names. +#[derive(Debug)] +pub struct AssertBridgePalletNames<'a> { + /// Name of the messages pallet, deployed at the bridged chain and used to bridge with this + /// chain. + pub with_this_chain_messages_pallet_name: &'a str, + /// Name of the GRANDPA pallet, deployed at this chain and used to bridge with the bridged + /// chain. + pub with_bridged_chain_grandpa_pallet_name: &'a str, + /// Name of the messages pallet, deployed at this chain and used to bridge with the bridged + /// chain. + pub with_bridged_chain_messages_pallet_name: &'a str, +} + +/// Tests that bridge pallet names used in `construct_runtime!()` macro call are matching constants +/// from chain primitives crates. +pub fn assert_bridge_pallet_names(params: AssertBridgePalletNames) +where + B: MessageBridge, + R: pallet_bridge_grandpa::Config + pallet_bridge_messages::Config, + GI: 'static, + MI: 'static, +{ + assert_eq!(B::BRIDGED_MESSAGES_PALLET_NAME, params.with_this_chain_messages_pallet_name); + assert_eq!( + pallet_bridge_grandpa::PalletOwner::::storage_value_final_key().to_vec(), + bp_runtime::storage_value_key(params.with_bridged_chain_grandpa_pallet_name, "PalletOwner",).0, + ); + assert_eq!( + pallet_bridge_messages::PalletOwner::::storage_value_final_key().to_vec(), + bp_runtime::storage_value_key( + params.with_bridged_chain_messages_pallet_name, + "PalletOwner", + ) + .0, + ); +} + +/// Parameters for asserting complete standard messages bridge. +#[derive(Debug)] +pub struct AssertCompleteBridgeConstants<'a> { + /// Parameters to assert this chain constants. + pub this_chain_constants: AssertChainConstants, + /// Parameters to assert messages pallet constants. + pub messages_pallet_constants: AssertBridgeMessagesPalletConstants, + /// Parameters to assert pallet names constants. + pub pallet_names: AssertBridgePalletNames<'a>, +} + +/// All bridge-related constants tests for the complete standard messages bridge (i.e. with bridge +/// GRANDPA and messages pallets deployed). +pub fn assert_complete_bridge_constants(params: AssertCompleteBridgeConstants) +where + R: frame_system::Config + + pallet_bridge_grandpa::Config + + pallet_bridge_messages::Config, + GI: 'static, + MI: 'static, + B: MessageBridge, + This: Chain, +{ + assert_chain_constants::(params.this_chain_constants); + assert_bridge_grandpa_pallet_constants::(); + assert_bridge_messages_pallet_constants::(params.messages_pallet_constants); + assert_bridge_pallet_names::(params.pallet_names); +} diff --git a/bin/runtime-common/src/lib.rs b/bin/runtime-common/src/lib.rs new file mode 100644 index 00000000000..ca8f2268404 --- /dev/null +++ b/bin/runtime-common/src/lib.rs @@ -0,0 +1,220 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Common types/functions that may be used by runtimes of all bridged chains. + +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_runtime::FilterCall; +use sp_runtime::transaction_validity::TransactionValidity; +use xcm::v3::NetworkId; + +pub mod messages; +pub mod messages_api; +pub mod messages_benchmarking; +pub mod messages_extension; +pub mod parachains_benchmarking; + +mod messages_generation; + +#[cfg(feature = "integrity-test")] +pub mod integrity; + +/// A duplication of the `FilterCall` trait. +/// +/// We need this trait in order to be able to implement it for the messages pallet, +/// since the implementation is done outside of the pallet crate. +pub trait BridgeRuntimeFilterCall { + /// Checks if a runtime call is valid. + fn validate(call: &Call) -> TransactionValidity; +} + +impl BridgeRuntimeFilterCall for pallet_bridge_grandpa::Pallet +where + pallet_bridge_grandpa::Pallet: FilterCall, +{ + fn validate(call: &Call) -> TransactionValidity { + as FilterCall>::validate(call) + } +} + +impl BridgeRuntimeFilterCall for pallet_bridge_parachains::Pallet +where + pallet_bridge_parachains::Pallet: FilterCall, +{ + fn validate(call: &Call) -> TransactionValidity { + as FilterCall>::validate(call) + } +} + +/// Declares a runtime-specific `BridgeRejectObsoleteHeadersAndMessages` signed extension. +/// +/// ## Example +/// +/// ```nocompile +/// generate_bridge_reject_obsolete_headers_and_messages!{ +/// Call, AccountId +/// BridgeRialtoGrandpa, BridgeWestendGrandpa, +/// BridgeRialtoParachains +/// } +/// ``` +/// +/// The goal of this extension is to avoid "mining" transactions that provide outdated bridged +/// headers and messages. Without that extension, even honest relayers may lose their funds if +/// there are multiple relays running and submitting the same information. +#[macro_export] +macro_rules! generate_bridge_reject_obsolete_headers_and_messages { + ($call:ty, $account_id:ty, $($filter_call:ty),*) => { + #[derive(Clone, codec::Decode, codec::Encode, Eq, PartialEq, frame_support::RuntimeDebug, scale_info::TypeInfo)] + pub struct BridgeRejectObsoleteHeadersAndMessages; + impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteHeadersAndMessages { + const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages"; + type AccountId = $account_id; + type Call = $call; + type AdditionalSigned = (); + type Pre = (); + + fn additional_signed(&self) -> sp_std::result::Result< + (), + sp_runtime::transaction_validity::TransactionValidityError, + > { + Ok(()) + } + + fn validate( + &self, + _who: &Self::AccountId, + call: &Self::Call, + _info: &sp_runtime::traits::DispatchInfoOf, + _len: usize, + ) -> sp_runtime::transaction_validity::TransactionValidity { + let valid = sp_runtime::transaction_validity::ValidTransaction::default(); + $( + let valid = valid + .combine_with(<$filter_call as $crate::BridgeRuntimeFilterCall<$call>>::validate(call)?); + )* + Ok(valid) + } + + fn pre_dispatch( + self, + who: &Self::AccountId, + call: &Self::Call, + info: &sp_runtime::traits::DispatchInfoOf, + len: usize, + ) -> Result { + self.validate(who, call, info, len).map(drop) + } + } + }; +} + +/// A mapping over `NetworkId`. +/// Since `NetworkId` doesn't include `Millau`, `Rialto` and `RialtoParachain`, we create some +/// synthetic associations between these chains and `NetworkId` chains. +pub enum CustomNetworkId { + /// The Millau network ID, associated with Kusama. + Millau, + /// The Rialto network ID, associated with Polkadot. + Rialto, + /// The RialtoParachain network ID, associated with Westend. + RialtoParachain, +} + +impl CustomNetworkId { + pub const fn as_network_id(&self) -> NetworkId { + match *self { + CustomNetworkId::Millau => NetworkId::Kusama, + CustomNetworkId::Rialto => NetworkId::Polkadot, + CustomNetworkId::RialtoParachain => NetworkId::Westend, + } + } +} + +#[cfg(test)] +mod tests { + use crate::BridgeRuntimeFilterCall; + use frame_support::{assert_err, assert_ok}; + use sp_runtime::{ + traits::SignedExtension, + transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, + }; + + pub struct MockCall { + data: u32, + } + + impl sp_runtime::traits::Dispatchable for MockCall { + type RuntimeOrigin = (); + type Config = (); + type Info = (); + type PostInfo = (); + + fn dispatch( + self, + _origin: Self::RuntimeOrigin, + ) -> sp_runtime::DispatchResultWithInfo { + unimplemented!() + } + } + + struct FirstFilterCall; + impl BridgeRuntimeFilterCall for FirstFilterCall { + fn validate(call: &MockCall) -> TransactionValidity { + if call.data <= 1 { + return InvalidTransaction::Custom(1).into() + } + + Ok(ValidTransaction { priority: 1, ..Default::default() }) + } + } + + struct SecondFilterCall; + impl BridgeRuntimeFilterCall for SecondFilterCall { + fn validate(call: &MockCall) -> TransactionValidity { + if call.data <= 2 { + return InvalidTransaction::Custom(2).into() + } + + Ok(ValidTransaction { priority: 2, ..Default::default() }) + } + } + + #[test] + fn test() { + generate_bridge_reject_obsolete_headers_and_messages!( + MockCall, + (), + FirstFilterCall, + SecondFilterCall + ); + + assert_err!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 1 }, &(), 0), + InvalidTransaction::Custom(1) + ); + + assert_err!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 2 }, &(), 0), + InvalidTransaction::Custom(2) + ); + + assert_ok!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 3 }, &(), 0), + ValidTransaction { priority: 3, ..Default::default() } + ) + } +} diff --git a/bin/runtime-common/src/messages.rs b/bin/runtime-common/src/messages.rs new file mode 100644 index 00000000000..23c79b1c583 --- /dev/null +++ b/bin/runtime-common/src/messages.rs @@ -0,0 +1,1204 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types that allow runtime to act as a source/target endpoint of message lanes. +//! +//! Messages are assumed to be encoded `Call`s of the target chain. Call-dispatch +//! pallet is used to dispatch incoming messages. Message identified by a tuple +//! of to elements - message lane id and message nonce. + +use bp_header_chain::{HeaderChain, HeaderChainError}; +use bp_messages::{ + source_chain::LaneMessageVerifier, + target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages}, + InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, +}; +use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Size, StorageProofChecker}; +use codec::{Decode, DecodeLimit, Encode}; +use frame_support::{traits::Get, weights::Weight, RuntimeDebug}; +use hash_db::Hasher; +use scale_info::TypeInfo; +use sp_std::{convert::TryFrom, fmt::Debug, marker::PhantomData, vec::Vec}; +use sp_trie::StorageProof; +use xcm::latest::prelude::*; + +/// Bidirectional message bridge. +pub trait MessageBridge { + /// Identifier of this chain. + const THIS_CHAIN_ID: ChainId; + /// Identifier of the Bridged chain. + const BRIDGED_CHAIN_ID: ChainId; + /// Name of the paired messages pallet instance at the Bridged chain. + /// + /// Should be the name that is used in the `construct_runtime!()` macro. + const BRIDGED_MESSAGES_PALLET_NAME: &'static str; + + /// This chain in context of message bridge. + type ThisChain: ThisChainWithMessages; + /// Bridged chain in context of message bridge. + type BridgedChain: BridgedChainWithMessages; + /// Bridged header chain. + type BridgedHeaderChain: HeaderChain>; +} + +/// A trait that provides the type of the underlying chain. +pub trait UnderlyingChainProvider { + /// Underlying chain type. + type Chain: Chain; +} + +/// This chain that has `pallet-bridge-messages` module. +pub trait ThisChainWithMessages: UnderlyingChainProvider { + /// Call origin on the chain. + type RuntimeOrigin; + /// Call type on the chain. + type RuntimeCall: Encode + Decode; + + /// Do we accept message sent by given origin to given lane? + fn is_message_accepted(origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool; + + /// Maximal number of pending (not yet delivered) messages at This chain. + /// + /// Any messages over this limit, will be rejected. + fn maximal_pending_messages_at_outbound_lane() -> MessageNonce; +} + +/// Bridged chain that has `pallet-bridge-messages` module. +pub trait BridgedChainWithMessages: UnderlyingChainProvider { + /// Returns `true` if message dispatch weight is withing expected limits. `false` means + /// that the message is too heavy to be sent over the bridge and shall be rejected. + fn verify_dispatch_weight(message_payload: &[u8]) -> bool; +} + +/// This chain in context of message bridge. +pub type ThisChain = ::ThisChain; +/// Bridged chain in context of message bridge. +pub type BridgedChain = ::BridgedChain; +/// Underlying chain type. +pub type UnderlyingChainOf = ::Chain; +/// Hash used on the chain. +pub type HashOf = bp_runtime::HashOf<::Chain>; +/// Hasher used on the chain. +pub type HasherOf = bp_runtime::HasherOf>; +/// Account id used on the chain. +pub type AccountIdOf = bp_runtime::AccountIdOf>; +/// Type of balances that is used on the chain. +pub type BalanceOf = bp_runtime::BalanceOf>; +/// Type of origin that is used on the chain. +pub type OriginOf = ::RuntimeOrigin; +/// Type of call that is used on this chain. +pub type CallOf = ::RuntimeCall; + +/// Raw storage proof type (just raw trie nodes). +pub type RawStorageProof = Vec>; + +/// Sub-module that is declaring types required for processing This -> Bridged chain messages. +pub mod source { + use super::*; + + /// Message payload for This -> Bridged chain messages. + pub type FromThisChainMessagePayload = Vec; + + /// Maximal size of outbound message payload. + pub struct FromThisChainMaximalOutboundPayloadSize(PhantomData); + + impl Get for FromThisChainMaximalOutboundPayloadSize { + fn get() -> u32 { + maximal_message_size::() + } + } + + /// Messages delivery proof from bridged chain: + /// + /// - hash of finalized header; + /// - storage proof of inbound lane state; + /// - lane id. + #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] + pub struct FromBridgedChainMessagesDeliveryProof { + /// Hash of the bridge header the proof is for. + pub bridged_header_hash: BridgedHeaderHash, + /// Storage trie proof generated for [`Self::bridged_header_hash`]. + pub storage_proof: RawStorageProof, + /// Lane id of which messages were delivered and the proof is for. + pub lane: LaneId, + } + + impl Size for FromBridgedChainMessagesDeliveryProof { + fn size(&self) -> u32 { + u32::try_from( + self.storage_proof + .iter() + .fold(0usize, |sum, node| sum.saturating_add(node.len())), + ) + .unwrap_or(u32::MAX) + } + } + + /// 'Parsed' message delivery proof - inbound lane id and its state. + pub type ParsedMessagesDeliveryProofFromBridgedChain = + (LaneId, InboundLaneData>>); + + /// Message verifier that is doing all basic checks. + /// + /// This verifier assumes following: + /// + /// - all message lanes are equivalent, so all checks are the same; + /// + /// Following checks are made: + /// + /// - message is rejected if its lane is currently blocked; + /// - message is rejected if there are too many pending (undelivered) messages at the outbound + /// lane; + /// - check that the sender has rights to dispatch the call on target chain using provided + /// dispatch origin; + /// - check that the sender has paid enough funds for both message delivery and dispatch. + #[derive(RuntimeDebug)] + pub struct FromThisChainMessageVerifier(PhantomData); + + /// The error message returned from LaneMessageVerifier when outbound lane is disabled. + pub const MESSAGE_REJECTED_BY_OUTBOUND_LANE: &str = + "The outbound message lane has rejected the message."; + /// The error message returned from LaneMessageVerifier when too many pending messages at the + /// lane. + pub const TOO_MANY_PENDING_MESSAGES: &str = "Too many pending messages at the lane."; + /// The error message returned from LaneMessageVerifier when call origin is mismatch. + pub const BAD_ORIGIN: &str = "Unable to match the source origin to expected target origin."; + + impl LaneMessageVerifier>, FromThisChainMessagePayload> + for FromThisChainMessageVerifier + where + B: MessageBridge, + // matches requirements from the `frame_system::Config::Origin` + OriginOf>: Clone + + Into>>, OriginOf>>>, + AccountIdOf>: PartialEq + Clone, + { + type Error = &'static str; + + fn verify_message( + submitter: &OriginOf>, + lane: &LaneId, + lane_outbound_data: &OutboundLaneData, + _payload: &FromThisChainMessagePayload, + ) -> Result<(), Self::Error> { + // reject message if lane is blocked + if !ThisChain::::is_message_accepted(submitter, lane) { + return Err(MESSAGE_REJECTED_BY_OUTBOUND_LANE) + } + + // reject message if there are too many pending messages at this lane + let max_pending_messages = ThisChain::::maximal_pending_messages_at_outbound_lane(); + let pending_messages = lane_outbound_data + .latest_generated_nonce + .saturating_sub(lane_outbound_data.latest_received_nonce); + if pending_messages > max_pending_messages { + return Err(TOO_MANY_PENDING_MESSAGES) + } + + Ok(()) + } + } + + /// Return maximal message size of This -> Bridged chain message. + pub fn maximal_message_size() -> u32 { + super::target::maximal_incoming_message_size( + UnderlyingChainOf::>::max_extrinsic_size(), + ) + } + + /// Do basic Bridged-chain specific verification of This -> Bridged chain message. + /// + /// Ok result from this function means that the delivery transaction with this message + /// may be 'mined' by the target chain. But the lane may have its own checks (e.g. fee + /// check) that would reject message (see `FromThisChainMessageVerifier`). + pub fn verify_chain_message( + payload: &FromThisChainMessagePayload, + ) -> Result<(), &'static str> { + if !BridgedChain::::verify_dispatch_weight(payload) { + return Err("Incorrect message weight declared") + } + + // The maximal size of extrinsic at Substrate-based chain depends on the + // `frame_system::Config::MaximumBlockLength` and + // `frame_system::Config::AvailableBlockRatio` constants. This check is here to be sure that + // the lane won't stuck because message is too large to fit into delivery transaction. + // + // **IMPORTANT NOTE**: the delivery transaction contains storage proof of the message, not + // the message itself. The proof is always larger than the message. But unless chain state + // is enormously large, it should be several dozens/hundreds of bytes. The delivery + // transaction also contains signatures and signed extensions. Because of this, we reserve + // 1/3 of the the maximal extrinsic weight for this data. + if payload.len() > maximal_message_size::() as usize { + return Err("The message is too large to be sent over the lane") + } + + Ok(()) + } + + /// Verify proof of This -> Bridged chain messages delivery. + /// + /// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged + /// parachains, please use the `verify_messages_delivery_proof_from_parachain`. + pub fn verify_messages_delivery_proof( + proof: FromBridgedChainMessagesDeliveryProof>>, + ) -> Result, &'static str> { + let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } = + proof; + B::BridgedHeaderChain::parse_finalized_storage_proof( + bridged_header_hash, + StorageProof::new(storage_proof), + |storage| { + // Messages delivery proof is just proof of single storage key read => any error + // is fatal. + let storage_inbound_lane_data_key = + bp_messages::storage_keys::inbound_lane_data_key( + B::BRIDGED_MESSAGES_PALLET_NAME, + &lane, + ); + let raw_inbound_lane_data = storage + .read_value(storage_inbound_lane_data_key.0.as_ref()) + .map_err(|_| "Failed to read inbound lane state from storage proof")? + .ok_or("Inbound lane state is missing from the messages proof")?; + let inbound_lane_data = InboundLaneData::decode(&mut &raw_inbound_lane_data[..]) + .map_err(|_| "Failed to decode inbound lane state from the proof")?; + + Ok((lane, inbound_lane_data)) + }, + ) + .map_err(<&'static str>::from)? + } + + /// XCM bridge. + pub trait XcmBridge { + /// Runtime message bridge configuration. + type MessageBridge: MessageBridge; + /// Runtime message sender adapter. + type MessageSender: bp_messages::source_chain::MessagesBridge< + OriginOf>, + FromThisChainMessagePayload, + >; + + /// Our location within the Consensus Universe. + fn universal_location() -> InteriorMultiLocation; + /// Verify that the adapter is responsible for handling given XCM destination. + fn verify_destination(dest: &MultiLocation) -> bool; + /// Build route from this chain to the XCM destination. + fn build_destination() -> MultiLocation; + /// Return message lane used to deliver XCM messages. + fn xcm_lane() -> LaneId; + } + + /// XCM bridge adapter for `bridge-messages` pallet. + pub struct XcmBridgeAdapter(PhantomData); + + impl SendXcm for XcmBridgeAdapter + where + BalanceOf>: Into, + OriginOf>: From, + { + type Ticket = FromThisChainMessagePayload; + + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult { + let d = dest.take().ok_or(SendError::MissingArgument)?; + if !T::verify_destination(&d) { + *dest = Some(d); + return Err(SendError::NotApplicable) + } + + let route = T::build_destination(); + let msg = (route, msg.take().ok_or(SendError::MissingArgument)?).encode(); + + // let's just take fixed (out of thin air) fee per message in our test bridges + // (this code won't be used in production anyway) + let fee_assets = MultiAssets::from((Here, 1_000_000_u128)); + + Ok((msg, fee_assets)) + } + + fn deliver(ticket: Self::Ticket) -> Result { + use bp_messages::source_chain::MessagesBridge; + + let lane = T::xcm_lane(); + let msg = ticket; + let result = T::MessageSender::send_message( + pallet_xcm::Origin::from(MultiLocation::from(T::universal_location())).into(), + lane, + msg, + ); + result + .map(|artifacts| { + let hash = (lane, artifacts.nonce).using_encoded(sp_io::hashing::blake2_256); + log::debug!( + target: "runtime::bridge", + "Sent XCM message {:?}/{} to {:?}: {:?}", + lane, + artifacts.nonce, + T::MessageBridge::BRIDGED_CHAIN_ID, + hash, + ); + hash + }) + .map_err(|e| { + log::debug!( + target: "runtime::bridge", + "Failed to send XCM message over lane {:?} to {:?}: {:?}", + lane, + T::MessageBridge::BRIDGED_CHAIN_ID, + e, + ); + SendError::Transport("Bridge has rejected the message") + }) + } + } +} + +/// Sub-module that is declaring types required for processing Bridged -> This chain messages. +pub mod target { + use super::*; + + /// Decoded Bridged -> This message payload. + #[derive(RuntimeDebug, PartialEq, Eq)] + pub struct FromBridgedChainMessagePayload { + /// Data that is actually sent over the wire. + pub xcm: (xcm::v3::MultiLocation, xcm::v3::Xcm), + /// Weight of the message, computed by the weigher. Unknown initially. + pub weight: Option, + } + + impl Decode for FromBridgedChainMessagePayload { + fn decode(input: &mut I) -> Result { + let _: codec::Compact = Decode::decode(input)?; + type XcmPairType = (xcm::v3::MultiLocation, xcm::v3::Xcm); + Ok(FromBridgedChainMessagePayload { + xcm: XcmPairType::::decode_with_depth_limit( + sp_api::MAX_EXTRINSIC_DEPTH, + input, + )?, + weight: None, + }) + } + } + + impl From<(xcm::v3::MultiLocation, xcm::v3::Xcm)> + for FromBridgedChainMessagePayload + { + fn from(xcm: (xcm::v3::MultiLocation, xcm::v3::Xcm)) -> Self { + FromBridgedChainMessagePayload { xcm, weight: None } + } + } + + /// Messages proof from bridged chain: + /// + /// - hash of finalized header; + /// - storage proof of messages and (optionally) outbound lane state; + /// - lane id; + /// - nonces (inclusive range) of messages which are included in this proof. + #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] + pub struct FromBridgedChainMessagesProof { + /// Hash of the finalized bridged header the proof is for. + pub bridged_header_hash: BridgedHeaderHash, + /// A storage trie proof of messages being delivered. + pub storage_proof: RawStorageProof, + /// Messages in this proof are sent over this lane. + pub lane: LaneId, + /// Nonce of the first message being delivered. + pub nonces_start: MessageNonce, + /// Nonce of the last message being delivered. + pub nonces_end: MessageNonce, + } + + impl Size for FromBridgedChainMessagesProof { + fn size(&self) -> u32 { + u32::try_from( + self.storage_proof + .iter() + .fold(0usize, |sum, node| sum.saturating_add(node.len())), + ) + .unwrap_or(u32::MAX) + } + } + + /// Dispatching Bridged -> This chain messages. + #[derive(RuntimeDebug, Clone, Copy)] + pub struct FromBridgedChainMessageDispatch { + _marker: PhantomData<(B, XcmExecutor, XcmWeigher, WeightCredit)>, + } + + impl + MessageDispatch>> + for FromBridgedChainMessageDispatch + where + XcmExecutor: xcm::v3::ExecuteXcm>>, + XcmWeigher: xcm_executor::traits::WeightBounds>>, + WeightCredit: Get, + { + type DispatchPayload = FromBridgedChainMessagePayload>>; + type DispatchLevelResult = (); + + fn dispatch_weight( + message: &mut DispatchMessage, + ) -> frame_support::weights::Weight { + match message.data.payload { + Ok(ref mut payload) => { + // I have no idea why this method takes `&mut` reference and there's nothing + // about that in documentation. Hope it'll only mutate iff error is returned. + let weight = XcmWeigher::weight(&mut payload.xcm.1); + let weight = Weight::from_ref_time(weight.unwrap_or_else(|e| { + log::debug!( + target: "runtime::bridge-dispatch", + "Failed to compute dispatch weight of incoming XCM message {:?}/{}: {:?}", + message.key.lane_id, + message.key.nonce, + e, + ); + + // we shall return 0 and then the XCM executor will fail to execute XCM + // if we'll return something else (e.g. maximal value), the lane may stuck + 0 + })); + + payload.weight = Some(weight); + weight + }, + _ => Weight::zero(), + } + } + + fn dispatch( + _relayer_account: &AccountIdOf>, + message: DispatchMessage, + ) -> MessageDispatchResult { + let message_id = (message.key.lane_id, message.key.nonce); + let do_dispatch = move || -> sp_std::result::Result { + let FromBridgedChainMessagePayload { xcm: (location, xcm), weight: weight_limit } = + message.data.payload?; + log::trace!( + target: "runtime::bridge-dispatch", + "Going to execute message {:?} (weight limit: {:?}): {:?} {:?}", + message_id, + weight_limit, + location, + xcm, + ); + let hash = message_id.using_encoded(sp_io::hashing::blake2_256); + + // if this cod will end up in production, this most likely needs to be set to zero + let weight_credit = WeightCredit::get(); + + let xcm_outcome = XcmExecutor::execute_xcm_in_credit( + location, + xcm, + hash, + weight_limit.unwrap_or_else(Weight::zero).ref_time(), + weight_credit.ref_time(), + ); + Ok(xcm_outcome) + }; + + let xcm_outcome = do_dispatch(); + match xcm_outcome { + Ok(outcome) => { + log::trace!( + target: "runtime::bridge-dispatch", + "Incoming message {:?} dispatched with result: {:?}", + message_id, + outcome, + ); + match outcome.ensure_execution() { + Ok(_weight) => (), + Err(e) => { + log::error!( + target: "runtime::bridge-dispatch", + "Incoming message {:?} was not dispatched, error: {:?}", + message_id, + e, + ); + }, + } + }, + Err(e) => { + log::error!( + target: "runtime::bridge-dispatch", + "Incoming message {:?} was not dispatched, codec error: {:?}", + message_id, + e, + ); + }, + } + + MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_fee_paid_during_dispatch: false, + dispatch_level_result: (), + } + } + } + + /// Return maximal dispatch weight of the message we're able to receive. + pub fn maximal_incoming_message_dispatch_weight(maximal_extrinsic_weight: Weight) -> Weight { + maximal_extrinsic_weight / 2 + } + + /// Return maximal message size given maximal extrinsic size. + pub fn maximal_incoming_message_size(maximal_extrinsic_size: u32) -> u32 { + maximal_extrinsic_size / 3 * 2 + } + + /// Verify proof of Bridged -> This chain messages. + /// + /// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged + /// parachains, please use the `verify_messages_proof_from_parachain`. + /// + /// The `messages_count` argument verification (sane limits) is supposed to be made + /// outside of this function. This function only verifies that the proof declares exactly + /// `messages_count` messages. + pub fn verify_messages_proof( + proof: FromBridgedChainMessagesProof>>, + messages_count: u32, + ) -> Result, MessageProofError> { + let FromBridgedChainMessagesProof { + bridged_header_hash, + storage_proof, + lane, + nonces_start, + nonces_end, + } = proof; + + B::BridgedHeaderChain::parse_finalized_storage_proof( + bridged_header_hash, + StorageProof::new(storage_proof), + |storage| { + let parser = + StorageProofCheckerAdapter::<_, B> { storage, _dummy: Default::default() }; + + // receiving proofs where end < begin is ok (if proof includes outbound lane state) + let messages_in_the_proof = + if let Some(nonces_difference) = nonces_end.checked_sub(nonces_start) { + // let's check that the user (relayer) has passed correct `messages_count` + // (this bounds maximal capacity of messages vec below) + let messages_in_the_proof = nonces_difference.saturating_add(1); + if messages_in_the_proof != MessageNonce::from(messages_count) { + return Err(MessageProofError::MessagesCountMismatch) + } + + messages_in_the_proof + } else { + 0 + }; + + // Read messages first. All messages that are claimed to be in the proof must + // be in the proof. So any error in `read_value`, or even missing value is fatal. + // + // Mind that we allow proofs with no messages if outbound lane state is proved. + let mut messages = Vec::with_capacity(messages_in_the_proof as _); + for nonce in nonces_start..=nonces_end { + let message_key = MessageKey { lane_id: lane, nonce }; + let raw_message_data = parser + .read_raw_message(&message_key) + .ok_or(MessageProofError::MissingRequiredMessage)?; + let payload = MessagePayload::decode(&mut &raw_message_data[..]) + .map_err(|_| MessageProofError::FailedToDecodeMessage)?; + messages.push(Message { key: message_key, payload }); + } + + // Now let's check if proof contains outbound lane state proof. It is optional, so + // we simply ignore `read_value` errors and missing value. + let mut proved_lane_messages = ProvedLaneMessages { lane_state: None, messages }; + let raw_outbound_lane_data = parser.read_raw_outbound_lane_data(&lane); + if let Some(raw_outbound_lane_data) = raw_outbound_lane_data { + proved_lane_messages.lane_state = Some( + OutboundLaneData::decode(&mut &raw_outbound_lane_data[..]) + .map_err(|_| MessageProofError::FailedToDecodeOutboundLaneState)?, + ); + } + + // Now we may actually check if the proof is empty or not. + if proved_lane_messages.lane_state.is_none() && + proved_lane_messages.messages.is_empty() + { + return Err(MessageProofError::Empty) + } + + // We only support single lane messages in this generated_schema + let mut proved_messages = ProvedMessages::new(); + proved_messages.insert(lane, proved_lane_messages); + + Ok(proved_messages) + }, + ) + .map_err(MessageProofError::HeaderChain)? + } + + /// Error that happens during message proof verification. + #[derive(Debug, PartialEq, Eq)] + pub enum MessageProofError { + /// Error returned by the bridged header chain. + HeaderChain(HeaderChainError), + /// The message proof is empty. + Empty, + /// Declared messages count doesn't match actual value. + MessagesCountMismatch, + /// Message is missing from the proof. + MissingRequiredMessage, + /// Failed to decode message from the proof. + FailedToDecodeMessage, + /// Failed to decode outbound lane data from the proof. + FailedToDecodeOutboundLaneState, + } + + impl From for &'static str { + fn from(err: MessageProofError) -> &'static str { + match err { + MessageProofError::HeaderChain(err) => err.into(), + MessageProofError::Empty => "Messages proof is empty", + MessageProofError::MessagesCountMismatch => + "Declared messages count doesn't match actual value", + MessageProofError::MissingRequiredMessage => "Message is missing from the proof", + MessageProofError::FailedToDecodeMessage => + "Failed to decode message from the proof", + MessageProofError::FailedToDecodeOutboundLaneState => + "Failed to decode outbound lane data from the proof", + } + } + } + + struct StorageProofCheckerAdapter { + storage: StorageProofChecker, + _dummy: sp_std::marker::PhantomData, + } + + impl StorageProofCheckerAdapter { + fn read_raw_outbound_lane_data(&self, lane_id: &LaneId) -> Option> { + let storage_outbound_lane_data_key = bp_messages::storage_keys::outbound_lane_data_key( + B::BRIDGED_MESSAGES_PALLET_NAME, + lane_id, + ); + self.storage.read_value(storage_outbound_lane_data_key.0.as_ref()).ok()? + } + + fn read_raw_message(&self, message_key: &MessageKey) -> Option> { + let storage_message_key = bp_messages::storage_keys::message_key( + B::BRIDGED_MESSAGES_PALLET_NAME, + &message_key.lane_id, + message_key.nonce, + ); + self.storage.read_value(storage_message_key.0.as_ref()).ok()? + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::messages_generation::{ + encode_all_messages, encode_lane_data, prepare_messages_storage_proof, + }; + use bp_runtime::HeaderOf; + use codec::{Decode, Encode}; + use frame_support::weights::Weight; + use sp_core::H256; + use sp_runtime::traits::{BlakeTwo256, Header as _}; + use std::cell::RefCell; + + const BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT: usize = 5; + const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: usize = 2048; + const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024; + + /// Bridge that is deployed on ThisChain and allows sending/receiving messages to/from + /// BridgedChain. + #[derive(Debug, PartialEq, Eq)] + struct OnThisChainBridge; + + impl MessageBridge for OnThisChainBridge { + const THIS_CHAIN_ID: ChainId = *b"this"; + const BRIDGED_CHAIN_ID: ChainId = *b"brdg"; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; + + type ThisChain = ThisChain; + type BridgedChain = BridgedChain; + type BridgedHeaderChain = BridgedHeaderChain; + } + + /// Bridge that is deployed on BridgedChain and allows sending/receiving messages to/from + /// ThisChain; + #[derive(Debug, PartialEq, Eq)] + struct OnBridgedChainBridge; + + impl MessageBridge for OnBridgedChainBridge { + const THIS_CHAIN_ID: ChainId = *b"brdg"; + const BRIDGED_CHAIN_ID: ChainId = *b"this"; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; + + type ThisChain = BridgedChain; + type BridgedChain = ThisChain; + type BridgedHeaderChain = ThisHeaderChain; + } + + #[derive(Clone, Debug)] + struct ThisChainOrigin(Result, ()>); + + impl From + for Result, ThisChainOrigin> + { + fn from( + origin: ThisChainOrigin, + ) -> Result, ThisChainOrigin> { + origin.clone().0.map_err(|_| origin) + } + } + + #[derive(Clone, Debug)] + struct BridgedChainOrigin; + + impl From + for Result, BridgedChainOrigin> + { + fn from( + _origin: BridgedChainOrigin, + ) -> Result, BridgedChainOrigin> { + unreachable!() + } + } + + struct ThisUnderlyingChain; + type ThisChainHeader = sp_runtime::generic::Header; + type ThisChainAccountId = u32; + type ThisChainBalance = u32; + #[derive(Decode, Encode)] + struct ThisChainCall; + + impl Chain for ThisUnderlyingChain { + type BlockNumber = u64; + type Hash = H256; + type Hasher = BlakeTwo256; + type Header = ThisChainHeader; + type AccountId = ThisChainAccountId; + type Balance = ThisChainBalance; + type Index = u32; + type Signature = sp_runtime::MultiSignature; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } + } + + struct ThisChain; + + impl UnderlyingChainProvider for ThisChain { + type Chain = ThisUnderlyingChain; + } + + impl ThisChainWithMessages for ThisChain { + type RuntimeOrigin = ThisChainOrigin; + type RuntimeCall = ThisChainCall; + + fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool { + lane == TEST_LANE_ID + } + + fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { + MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE + } + } + + impl BridgedChainWithMessages for ThisChain { + fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { + unreachable!() + } + } + + struct BridgedUnderlyingChain; + type BridgedChainHeader = sp_runtime::generic::Header; + type BridgedChainAccountId = u128; + type BridgedChainBalance = u128; + #[derive(Decode, Encode)] + struct BridgedChainCall; + + impl Chain for BridgedUnderlyingChain { + type BlockNumber = u64; + type Hash = H256; + type Hasher = BlakeTwo256; + type Header = BridgedChainHeader; + type AccountId = BridgedChainAccountId; + type Balance = BridgedChainBalance; + type Index = u32; + type Signature = sp_runtime::MultiSignature; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } + } + + struct BridgedChain; + + impl UnderlyingChainProvider for BridgedChain { + type Chain = BridgedUnderlyingChain; + } + + impl ThisChainWithMessages for BridgedChain { + type RuntimeOrigin = BridgedChainOrigin; + type RuntimeCall = BridgedChainCall; + + fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, _lane: &LaneId) -> bool { + unreachable!() + } + + fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { + unreachable!() + } + } + + impl BridgedChainWithMessages for BridgedChain { + fn verify_dispatch_weight(message_payload: &[u8]) -> bool { + message_payload.len() >= BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT && + message_payload.len() <= BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT + } + } + + thread_local! { + static TEST_BRIDGED_HEADER: RefCell> = RefCell::new(None); + } + + struct BridgedHeaderChain; + + impl HeaderChain for BridgedHeaderChain { + fn finalized_header( + _hash: HashOf, + ) -> Option> { + TEST_BRIDGED_HEADER.with(|h| h.borrow().clone()) + } + } + + struct ThisHeaderChain; + + impl HeaderChain for ThisHeaderChain { + fn finalized_header(_hash: HashOf) -> Option> { + unreachable!() + } + } + + fn test_lane_outbound_data() -> OutboundLaneData { + OutboundLaneData::default() + } + + const TEST_LANE_ID: &LaneId = b"test"; + const MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE: MessageNonce = 32; + + fn regular_outbound_message_payload() -> source::FromThisChainMessagePayload { + vec![42] + } + + #[test] + fn message_is_rejected_when_sent_using_disabled_lane() { + assert_eq!( + source::FromThisChainMessageVerifier::::verify_message( + &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), + b"dsbl", + &test_lane_outbound_data(), + ®ular_outbound_message_payload(), + ), + Err(source::MESSAGE_REJECTED_BY_OUTBOUND_LANE) + ); + } + + #[test] + fn message_is_rejected_when_there_are_too_many_pending_messages_at_outbound_lane() { + assert_eq!( + source::FromThisChainMessageVerifier::::verify_message( + &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), + TEST_LANE_ID, + &OutboundLaneData { + latest_received_nonce: 100, + latest_generated_nonce: 100 + MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE + 1, + ..Default::default() + }, + ®ular_outbound_message_payload(), + ), + Err(source::TOO_MANY_PENDING_MESSAGES) + ); + } + + #[test] + fn verify_chain_message_rejects_message_with_too_small_declared_weight() { + assert!(source::verify_chain_message::(&vec![ + 42; + BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT - + 1 + ]) + .is_err()); + } + + #[test] + fn verify_chain_message_rejects_message_with_too_large_declared_weight() { + assert!(source::verify_chain_message::(&vec![ + 42; + BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT - + 1 + ]) + .is_err()); + } + + #[test] + fn verify_chain_message_rejects_message_too_large_message() { + assert!(source::verify_chain_message::(&vec![ + 0; + source::maximal_message_size::() + as usize + 1 + ],) + .is_err()); + } + + #[test] + fn verify_chain_message_accepts_maximal_message() { + assert_eq!( + source::verify_chain_message::(&vec![ + 0; + source::maximal_message_size::() + as _ + ],), + Ok(()), + ); + } + + fn using_messages_proof( + nonces_end: MessageNonce, + outbound_lane_data: Option, + encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option>, + encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec, + test: impl Fn(target::FromBridgedChainMessagesProof) -> R, + ) -> R { + let (state_root, storage_proof) = prepare_messages_storage_proof::( + *TEST_LANE_ID, + 1..=nonces_end, + outbound_lane_data, + bp_runtime::StorageProofSize::Minimal(0), + vec![42], + encode_message, + encode_outbound_lane_data, + ); + + TEST_BRIDGED_HEADER.with(|h| { + *h.borrow_mut() = Some(BridgedChainHeader::new( + 0, + Default::default(), + state_root, + Default::default(), + Default::default(), + )) + }); + + test(target::FromBridgedChainMessagesProof { + bridged_header_hash: Default::default(), + storage_proof, + lane: *TEST_LANE_ID, + nonces_start: 1, + nonces_end, + }) + } + + #[test] + fn messages_proof_is_rejected_if_declared_less_than_actual_number_of_messages() { + assert_eq!( + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { + target::verify_messages_proof::(proof, 5) + }), + Err(target::MessageProofError::MessagesCountMismatch), + ); + } + + #[test] + fn messages_proof_is_rejected_if_declared_more_than_actual_number_of_messages() { + assert_eq!( + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { + target::verify_messages_proof::(proof, 15) + }), + Err(target::MessageProofError::MessagesCountMismatch), + ); + } + + #[test] + fn message_proof_is_rejected_if_header_is_missing_from_the_chain() { + assert_eq!( + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { + TEST_BRIDGED_HEADER.with(|h| *h.borrow_mut() = None); + target::verify_messages_proof::(proof, 10) + }), + Err(target::MessageProofError::HeaderChain(HeaderChainError::UnknownHeader)), + ); + } + + #[test] + fn message_proof_is_rejected_if_header_state_root_mismatches() { + assert_eq!( + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { + TEST_BRIDGED_HEADER + .with(|h| h.borrow_mut().as_mut().unwrap().state_root = Default::default()); + target::verify_messages_proof::(proof, 10) + }), + Err(target::MessageProofError::HeaderChain(HeaderChainError::StorageRootMismatch)), + ); + } + + #[test] + fn message_proof_is_rejected_if_required_message_is_missing() { + assert_eq!( + using_messages_proof( + 10, + None, + |n, m| if n != 5 { Some(m.encode()) } else { None }, + encode_lane_data, + |proof| target::verify_messages_proof::(proof, 10) + ), + Err(target::MessageProofError::MissingRequiredMessage), + ); + } + + #[test] + fn message_proof_is_rejected_if_message_decode_fails() { + assert_eq!( + using_messages_proof( + 10, + None, + |n, m| { + let mut m = m.encode(); + if n == 5 { + m = vec![42] + } + Some(m) + }, + encode_lane_data, + |proof| target::verify_messages_proof::(proof, 10), + ), + Err(target::MessageProofError::FailedToDecodeMessage), + ); + } + + #[test] + fn message_proof_is_rejected_if_outbound_lane_state_decode_fails() { + assert_eq!( + using_messages_proof( + 10, + Some(OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 1, + latest_generated_nonce: 1, + }), + encode_all_messages, + |d| { + let mut d = d.encode(); + d.truncate(1); + d + }, + |proof| target::verify_messages_proof::(proof, 10), + ), + Err(target::MessageProofError::FailedToDecodeOutboundLaneState), + ); + } + + #[test] + fn message_proof_is_rejected_if_it_is_empty() { + assert_eq!( + using_messages_proof(0, None, encode_all_messages, encode_lane_data, |proof| { + target::verify_messages_proof::(proof, 0) + },), + Err(target::MessageProofError::Empty), + ); + } + + #[test] + fn non_empty_message_proof_without_messages_is_accepted() { + assert_eq!( + using_messages_proof( + 0, + Some(OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 1, + latest_generated_nonce: 1, + }), + encode_all_messages, + encode_lane_data, + |proof| target::verify_messages_proof::(proof, 0), + ), + Ok(vec![( + *TEST_LANE_ID, + ProvedLaneMessages { + lane_state: Some(OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 1, + latest_generated_nonce: 1, + }), + messages: Vec::new(), + }, + )] + .into_iter() + .collect()), + ); + } + + #[test] + fn non_empty_message_proof_is_accepted() { + assert_eq!( + using_messages_proof( + 1, + Some(OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 1, + latest_generated_nonce: 1, + }), + encode_all_messages, + encode_lane_data, + |proof| target::verify_messages_proof::(proof, 1), + ), + Ok(vec![( + *TEST_LANE_ID, + ProvedLaneMessages { + lane_state: Some(OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 1, + latest_generated_nonce: 1, + }), + messages: vec![Message { + key: MessageKey { lane_id: *TEST_LANE_ID, nonce: 1 }, + payload: vec![42], + }], + }, + )] + .into_iter() + .collect()), + ); + } + + #[test] + fn verify_messages_proof_does_not_panic_if_messages_count_mismatches() { + assert_eq!( + using_messages_proof(1, None, encode_all_messages, encode_lane_data, |mut proof| { + proof.nonces_end = u64::MAX; + target::verify_messages_proof::(proof, u32::MAX) + },), + Err(target::MessageProofError::MessagesCountMismatch), + ); + } +} diff --git a/bin/runtime-common/src/messages_api.rs b/bin/runtime-common/src/messages_api.rs new file mode 100644 index 00000000000..199e062fe98 --- /dev/null +++ b/bin/runtime-common/src/messages_api.rs @@ -0,0 +1,66 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Helpers for implementing various message-related runtime API mthods. + +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; +use sp_std::vec::Vec; + +/// Implementation of the `To*OutboundLaneApi::message_details`. +pub fn outbound_message_details( + lane: LaneId, + begin: MessageNonce, + end: MessageNonce, +) -> Vec +where + Runtime: pallet_bridge_messages::Config, + MessagesPalletInstance: 'static, +{ + (begin..=end) + .filter_map(|nonce| { + let message_data = + pallet_bridge_messages::Pallet::::outbound_message_data(lane, nonce)?; + Some(OutboundMessageDetails { + nonce, + // dispatch message weight is always zero at the source chain, since we're paying for + // dispatch at the target chain + dispatch_weight: frame_support::weights::Weight::zero(), + size: message_data.len() as _, + }) + }) + .collect() +} + +/// Implementation of the `To*InboundLaneApi::message_details`. +pub fn inbound_message_details( + lane: LaneId, + messages: Vec<(MessagePayload, OutboundMessageDetails)>, +) -> Vec +where + Runtime: pallet_bridge_messages::Config, + MessagesPalletInstance: 'static, +{ + messages + .into_iter() + .map(|(payload, details)| { + pallet_bridge_messages::Pallet::::inbound_message_data( + lane, payload, details, + ) + }) + .collect() +} diff --git a/bin/runtime-common/src/messages_benchmarking.rs b/bin/runtime-common/src/messages_benchmarking.rs new file mode 100644 index 00000000000..919c03583b8 --- /dev/null +++ b/bin/runtime-common/src/messages_benchmarking.rs @@ -0,0 +1,157 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything required to run benchmarks of messages module, based on +//! `bridge_runtime_common::messages` implementation. + +#![cfg(feature = "runtime-benchmarks")] + +use crate::{ + messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, + AccountIdOf, BalanceOf, BridgedChain, CallOf, HashOf, MessageBridge, ThisChain, + }, + messages_generation::{ + encode_all_messages, encode_lane_data, grow_trie, prepare_messages_storage_proof, + }, +}; + +use bp_messages::storage_keys; +use bp_runtime::{record_all_trie_keys, StorageProofSize}; +use codec::Encode; +use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; +use pallet_bridge_messages::benchmarking::{MessageDeliveryProofParams, MessageProofParams}; +use sp_core::Hasher; +use sp_runtime::traits::{Header, MaybeSerializeDeserialize, Zero}; +use sp_std::{fmt::Debug, prelude::*}; +use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; + +/// Prepare proof of messages for the `receive_messages_proof` call. +/// +/// In addition to returning valid messages proof, environment is prepared to verify this message +/// proof. +pub fn prepare_message_proof( + params: MessageProofParams, +) -> (FromBridgedChainMessagesProof>>, Weight) +where + R: frame_system::Config>> + + pallet_balances::Config>> + + pallet_bridge_grandpa::Config, + R::BridgedChain: bp_runtime::Chain>, Header = BH>, + B: MessageBridge, + BI: 'static, + FI: 'static, + BH: Header>>, + BHH: Hasher>>, + AccountIdOf>: PartialEq + sp_std::fmt::Debug, + AccountIdOf>: From<[u8; 32]>, + BalanceOf>: Debug + MaybeSerializeDeserialize, + CallOf>: From> + GetDispatchInfo, + HashOf>: Copy + Default, +{ + let message_payload = match params.size { + StorageProofSize::Minimal(ref size) => vec![0u8; *size as _], + _ => vec![], + }; + + // finally - prepare storage proof and update environment + let (state_root, storage_proof) = prepare_messages_storage_proof::( + params.lane, + params.message_nonces.clone(), + params.outbound_lane_data, + params.size, + message_payload, + encode_all_messages, + encode_lane_data, + ); + let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); + + ( + FromBridgedChainMessagesProof { + bridged_header_hash, + storage_proof, + lane: params.lane, + nonces_start: *params.message_nonces.start(), + nonces_end: *params.message_nonces.end(), + }, + Weight::zero(), + ) +} + +/// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call. +pub fn prepare_message_delivery_proof( + params: MessageDeliveryProofParams>>, +) -> FromBridgedChainMessagesDeliveryProof>> +where + R: pallet_bridge_grandpa::Config, + R::BridgedChain: bp_runtime::Chain>, Header = BH>, + FI: 'static, + B: MessageBridge, + BH: Header>>, + BHH: Hasher>>, + HashOf>: Copy + Default, +{ + // prepare Bridged chain storage with inbound lane state + let storage_key = + storage_keys::inbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, ¶ms.lane).0; + let mut root = Default::default(); + let mut mdb = MemoryDB::default(); + { + let mut trie = TrieDBMutBuilderV1::::new(&mut mdb, &mut root).build(); + trie.insert(&storage_key, ¶ms.inbound_lane_data.encode()) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + } + root = grow_trie(root, &mut mdb, params.size); + + // generate storage proof to be delivered to This chain + let mut proof_recorder = Recorder::>::new(); + record_all_trie_keys::, _>(&mdb, &root, &mut proof_recorder) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); + let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); + + // finally insert header with given state root to our storage + let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(root); + + FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: bridged_header_hash.into(), + storage_proof, + lane: params.lane, + } +} + +/// Insert header to the bridge GRANDPA pallet. +pub(crate) fn insert_header_to_grandpa_pallet( + state_root: bp_runtime::HashOf, +) -> (bp_runtime::BlockNumberOf, bp_runtime::HashOf) +where + R: pallet_bridge_grandpa::Config, + GI: 'static, + R::BridgedChain: bp_runtime::Chain, +{ + let bridged_block_number = Zero::zero(); + let bridged_header = bp_runtime::HeaderOf::::new( + bridged_block_number, + Default::default(), + state_root, + Default::default(), + Default::default(), + ); + let bridged_header_hash = bridged_header.hash(); + pallet_bridge_grandpa::initialize_for_benchmarks::(bridged_header); + (bridged_block_number, bridged_header_hash) +} diff --git a/bin/runtime-common/src/messages_extension.rs b/bin/runtime-common/src/messages_extension.rs new file mode 100644 index 00000000000..28606d58dad --- /dev/null +++ b/bin/runtime-common/src/messages_extension.rs @@ -0,0 +1,231 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{ + messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, + }, + BridgeRuntimeFilterCall, +}; +use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; +use pallet_bridge_messages::{Config, Pallet}; +use sp_runtime::transaction_validity::TransactionValidity; + +/// Validate messages in order to avoid "mining" messages delivery and delivery confirmation +/// transactions, that are delivering outdated messages/confirmations. Without this validation, +/// even honest relayers may lose their funds if there are multiple relays running and submitting +/// the same messages/confirmations. +impl< + BridgedHeaderHash, + SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain< + MessagesProof = FromBridgedChainMessagesProof, + >, + TargetHeaderChain: bp_messages::source_chain::TargetHeaderChain< + >::OutboundPayload, + ::AccountId, + MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof, + >, + Call: IsSubType, T>>, + T: frame_system::Config + + Config, + I: 'static, + > BridgeRuntimeFilterCall for Pallet +{ + fn validate(call: &Call) -> TransactionValidity { + match call.is_sub_type() { + Some(pallet_bridge_messages::Call::::receive_messages_proof { + ref proof, + .. + }) => { + let inbound_lane_data = + pallet_bridge_messages::InboundLanes::::get(proof.lane); + if proof.nonces_end <= inbound_lane_data.last_delivered_nonce() { + log::trace!( + target: pallet_bridge_messages::LOG_TARGET, + "Rejecting obsolete messages delivery transaction: \ + lane {:?}, bundled {:?}, best {:?}", + proof.lane, + proof.nonces_end, + inbound_lane_data.last_delivered_nonce(), + ); + + return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() + } + }, + Some(pallet_bridge_messages::Call::::receive_messages_delivery_proof { + ref proof, + ref relayers_state, + .. + }) => { + let latest_delivered_nonce = relayers_state.last_delivered_nonce; + + let outbound_lane_data = + pallet_bridge_messages::OutboundLanes::::get(proof.lane); + if latest_delivered_nonce <= outbound_lane_data.latest_received_nonce { + log::trace!( + target: pallet_bridge_messages::LOG_TARGET, + "Rejecting obsolete messages confirmation transaction: \ + lane {:?}, bundled {:?}, best {:?}", + proof.lane, + latest_delivered_nonce, + outbound_lane_data.latest_received_nonce, + ); + + return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() + } + }, + _ => {}, + } + + Ok(sp_runtime::transaction_validity::ValidTransaction::default()) + } +} + +#[cfg(test)] +mod tests { + use bp_messages::UnrewardedRelayersState; + use millau_runtime::{ + bridge_runtime_common::{ + messages::{ + source::FromBridgedChainMessagesDeliveryProof, + target::FromBridgedChainMessagesProof, + }, + BridgeRuntimeFilterCall, + }, + Runtime, RuntimeCall, WithRialtoMessagesInstance, + }; + + fn deliver_message_10() { + pallet_bridge_messages::InboundLanes::::insert( + [0, 0, 0, 0], + bp_messages::InboundLaneData { relayers: Default::default(), last_confirmed_nonce: 10 }, + ); + } + + fn validate_message_delivery( + nonces_start: bp_messages::MessageNonce, + nonces_end: bp_messages::MessageNonce, + ) -> bool { + pallet_bridge_messages::Pallet::::validate( + &RuntimeCall::BridgeRialtoMessages( + pallet_bridge_messages::Call::::receive_messages_proof { + relayer_id_at_bridged_chain: [0u8; 32].into(), + messages_count: (nonces_end - nonces_start + 1) as u32, + dispatch_weight: frame_support::weights::Weight::zero(), + proof: FromBridgedChainMessagesProof { + bridged_header_hash: Default::default(), + storage_proof: vec![], + lane: [0, 0, 0, 0], + nonces_start, + nonces_end, + }, + }, + ), + ) + .is_ok() + } + + #[test] + fn extension_rejects_obsolete_messages() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best delivered is message#10 and we're trying to deliver message#5 => tx + // is rejected + deliver_message_10(); + assert!(!validate_message_delivery(8, 9)); + }); + } + + #[test] + fn extension_rejects_same_message() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best delivered is message#10 and we're trying to import message#10 => tx + // is rejected + deliver_message_10(); + assert!(!validate_message_delivery(8, 10)); + }); + } + + #[test] + fn extension_accepts_new_message() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best delivered is message#10 and we're trying to deliver message#15 => + // tx is accepted + deliver_message_10(); + assert!(validate_message_delivery(10, 15)); + }); + } + + fn confirm_message_10() { + pallet_bridge_messages::OutboundLanes::::insert( + [0, 0, 0, 0], + bp_messages::OutboundLaneData { + oldest_unpruned_nonce: 0, + latest_received_nonce: 10, + latest_generated_nonce: 10, + }, + ); + } + + fn validate_message_confirmation(last_delivered_nonce: bp_messages::MessageNonce) -> bool { + pallet_bridge_messages::Pallet::::validate( + &RuntimeCall::BridgeRialtoMessages(pallet_bridge_messages::Call::< + Runtime, + WithRialtoMessagesInstance, + >::receive_messages_delivery_proof { + proof: FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: Default::default(), + storage_proof: Vec::new(), + lane: [0, 0, 0, 0], + }, + relayers_state: UnrewardedRelayersState { + last_delivered_nonce, + ..Default::default() + }, + }), + ) + .is_ok() + } + + #[test] + fn extension_rejects_obsolete_confirmations() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best confirmed is message#10 and we're trying to confirm message#5 => tx + // is rejected + confirm_message_10(); + assert!(!validate_message_confirmation(5)); + }); + } + + #[test] + fn extension_rejects_same_confirmation() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best confirmed is message#10 and we're trying to confirm message#10 => + // tx is rejected + confirm_message_10(); + assert!(!validate_message_confirmation(10)); + }); + } + + #[test] + fn extension_accepts_new_confirmation() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best confirmed is message#10 and we're trying to confirm message#15 => + // tx is accepted + confirm_message_10(); + assert!(validate_message_confirmation(15)); + }); + } +} diff --git a/bin/runtime-common/src/messages_generation.rs b/bin/runtime-common/src/messages_generation.rs new file mode 100644 index 00000000000..560033d122a --- /dev/null +++ b/bin/runtime-common/src/messages_generation.rs @@ -0,0 +1,146 @@ +// Copyright 2019-2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Helpers for generating message storage proofs, that are used by tests and by benchmarks. + +#![cfg(any(feature = "runtime-benchmarks", test))] + +use crate::messages::{BridgedChain, HashOf, HasherOf, MessageBridge, RawStorageProof}; + +use bp_messages::{ + storage_keys, LaneId, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, +}; +use bp_runtime::{record_all_trie_keys, StorageProofSize}; +use codec::Encode; +use sp_core::Hasher; +use sp_std::{ops::RangeInclusive, prelude::*}; +use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; + +/// Simple and correct message data encode function. +pub(crate) fn encode_all_messages(_: MessageNonce, m: &MessagePayload) -> Option> { + Some(m.encode()) +} + +/// Simple and correct outbound lane data encode function. +pub(crate) fn encode_lane_data(d: &OutboundLaneData) -> Vec { + d.encode() +} + +/// Prepare storage proof of given messages. +/// +/// Returns state trie root and nodes with prepared messages. +pub(crate) fn prepare_messages_storage_proof( + lane: LaneId, + message_nonces: RangeInclusive, + outbound_lane_data: Option, + size: StorageProofSize, + message_payload: MessagePayload, + encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option>, + encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec, +) -> (HashOf>, RawStorageProof) +where + B: MessageBridge, + HashOf>: Copy + Default, +{ + // prepare Bridged chain storage with messages and (optionally) outbound lane state + let message_count = message_nonces.end().saturating_sub(*message_nonces.start()) + 1; + let mut storage_keys = Vec::with_capacity(message_count as usize + 1); + let mut root = Default::default(); + let mut mdb = MemoryDB::default(); + { + let mut trie = + TrieDBMutBuilderV1::>>::new(&mut mdb, &mut root).build(); + + // insert messages + for nonce in message_nonces { + let message_key = MessageKey { lane_id: lane, nonce }; + let message_payload = match encode_message(nonce, &message_payload) { + Some(message_payload) => message_payload, + None => continue, + }; + let storage_key = storage_keys::message_key( + B::BRIDGED_MESSAGES_PALLET_NAME, + &message_key.lane_id, + message_key.nonce, + ) + .0; + trie.insert(&storage_key, &message_payload) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + storage_keys.push(storage_key); + } + + // insert outbound lane state + if let Some(outbound_lane_data) = outbound_lane_data.as_ref().map(encode_outbound_lane_data) + { + let storage_key = + storage_keys::outbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane).0; + trie.insert(&storage_key, &outbound_lane_data) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + storage_keys.push(storage_key); + } + } + root = grow_trie(root, &mut mdb, size); + + // generate storage proof to be delivered to This chain + let mut proof_recorder = Recorder::>>>::new(); + record_all_trie_keys::>>, _>( + &mdb, + &root, + &mut proof_recorder, + ) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); + let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); + + (root, storage_proof) +} + +/// Populate trie with dummy keys+values until trie has at least given size. +pub fn grow_trie( + mut root: H::Out, + mdb: &mut MemoryDB, + trie_size: StorageProofSize, +) -> H::Out { + let (iterations, leaf_size, minimal_trie_size) = match trie_size { + StorageProofSize::Minimal(_) => return root, + StorageProofSize::HasLargeLeaf(size) => (1, size, size), + StorageProofSize::HasExtraNodes(size) => (8, 1, size), + }; + + let mut key_index = 0; + loop { + // generate storage proof to be delivered to This chain + let mut proof_recorder = Recorder::>::new(); + record_all_trie_keys::, _>(mdb, &root, &mut proof_recorder) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); + let size: usize = proof_recorder.drain().into_iter().map(|n| n.data.len()).sum(); + if size > minimal_trie_size as _ { + return root + } + + let mut trie = TrieDBMutBuilderV1::::from_existing(mdb, &mut root).build(); + for _ in 0..iterations { + trie.insert(&key_index.encode(), &vec![42u8; leaf_size as _]) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + key_index += 1; + } + trie.commit(); + } +} diff --git a/bin/runtime-common/src/parachains_benchmarking.rs b/bin/runtime-common/src/parachains_benchmarking.rs new file mode 100644 index 00000000000..fcd32ea28b8 --- /dev/null +++ b/bin/runtime-common/src/parachains_benchmarking.rs @@ -0,0 +1,85 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything required to run benchmarks of parachains finality module. + +#![cfg(feature = "runtime-benchmarks")] + +use crate::{ + messages_benchmarking::insert_header_to_grandpa_pallet, messages_generation::grow_trie, +}; + +use bp_parachains::parachain_head_storage_key_at_source; +use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; +use bp_runtime::{record_all_trie_keys, StorageProofSize}; +use codec::Encode; +use frame_support::traits::Get; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use sp_std::prelude::*; +use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; + +/// Prepare proof of messages for the `receive_messages_proof` call. +/// +/// In addition to returning valid messages proof, environment is prepared to verify this message +/// proof. +pub fn prepare_parachain_heads_proof( + parachains: &[ParaId], + parachain_head_size: u32, + size: StorageProofSize, +) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) +where + R: pallet_bridge_parachains::Config + + pallet_bridge_grandpa::Config, + PI: 'static, + >::BridgedChain: + bp_runtime::Chain, +{ + let parachain_head = ParaHead(vec![0u8; parachain_head_size as usize]); + + // insert all heads to the trie + let mut parachain_heads = Vec::with_capacity(parachains.len()); + let mut storage_keys = Vec::with_capacity(parachains.len()); + let mut state_root = Default::default(); + let mut mdb = MemoryDB::default(); + { + let mut trie = + TrieDBMutBuilderV1::::new(&mut mdb, &mut state_root).build(); + + // insert parachain heads + for parachain in parachains { + let storage_key = + parachain_head_storage_key_at_source(R::ParasPalletName::get(), *parachain); + trie.insert(&storage_key.0, ¶chain_head.encode()) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + storage_keys.push(storage_key); + parachain_heads.push((*parachain, parachain_head.hash())) + } + } + state_root = grow_trie(state_root, &mut mdb, size); + + // generate heads storage proof + let mut proof_recorder = Recorder::>::new(); + record_all_trie_keys::, _>(&mdb, &state_root, &mut proof_recorder) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); + let proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); + + let (relay_block_number, relay_block_hash) = + insert_header_to_grandpa_pallet::(state_root); + + (relay_block_number, relay_block_hash, ParaHeadsProof(proof), parachain_heads) +} diff --git a/ci.Dockerfile b/ci.Dockerfile new file mode 100644 index 00000000000..b419f6be54d --- /dev/null +++ b/ci.Dockerfile @@ -0,0 +1,53 @@ +# This file is a "runtime" part from a builder-pattern in Dockerfile, it's used in CI. +# The only different part is that the compilation happens externally, +# so COPY has a different source. +FROM docker.io/library/ubuntu:20.04 + +# show backtraces +ENV RUST_BACKTRACE 1 +ENV DEBIAN_FRONTEND=noninteractive + +RUN set -eux; \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + curl ca-certificates libssl-dev && \ + update-ca-certificates && \ + groupadd -g 1000 user && \ + useradd -u 1000 -g user -s /bin/sh -m user && \ + # apt clean up + apt-get autoremove -y && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# switch to non-root user +USER user + +WORKDIR /home/user + +ARG PROJECT=substrate-relay + +COPY --chown=user:user ./${PROJECT} ./ +COPY --chown=user:user ./bridge-entrypoint.sh ./ + +# check if executable works in this container +RUN ./${PROJECT} --version + +ENV PROJECT=$PROJECT +ENTRYPOINT ["/home/user/bridge-entrypoint.sh"] + +# metadata +ARG VCS_REF=master +ARG BUILD_DATE="" +ARG VERSION="" + +LABEL org.opencontainers.image.title="${PROJECT}" \ + org.opencontainers.image.description="${PROJECT} - component of Parity Bridges Common" \ + org.opencontainers.image.source="https://github.com/paritytech/parity-bridges-common/blob/${VCS_REF}/ci.Dockerfile" \ + org.opencontainers.image.url="https://github.com/paritytech/parity-bridges-common/blob/${VCS_REF}/ci.Dockerfile" \ + org.opencontainers.image.documentation="https://github.com/paritytech/parity-bridges-common/blob/${VCS_REF}/README.md" \ + org.opencontainers.image.created="${BUILD_DATE}" \ + org.opencontainers.image.version="${VERSION}" \ + org.opencontainers.image.revision="${VCS_REF}" \ + org.opencontainers.image.authors="devops-team@parity.io" \ + org.opencontainers.image.vendor="Parity Technologies" \ + org.opencontainers.image.licenses="GPL-3.0 License" diff --git a/deny.toml b/deny.toml new file mode 100644 index 00000000000..3fa007bbe0a --- /dev/null +++ b/deny.toml @@ -0,0 +1,206 @@ +# This template contains all of the possible sections and their default values + +# Note that all fields that take a lint level have these possible values: +# * deny - An error will be produced and the check will fail +# * warn - A warning will be produced, but the check will not fail +# * allow - No warning or error will be produced, though in some cases a note +# will be + +# The values provided in this template are the default values that will be used +# when any section or field is not specified in your own configuration + +# If 1 or more target triples (and optionally, target_features) are specified, +# only the specified targets will be checked when running `cargo deny check`. +# This means, if a particular package is only ever used as a target specific +# dependency, such as, for example, the `nix` crate only being used via the +# `target_family = "unix"` configuration, that only having windows targets in +# this list would mean the nix crate, as well as any of its exclusive +# dependencies not shared by any other crates, would be ignored, as the target +# list here is effectively saying which targets you are building for. +targets = [ + # The triple can be any string, but only the target triples built in to + # rustc (as of 1.40) can be checked against actual config expressions + #{ triple = "x86_64-unknown-linux-musl" }, + # You can also specify which target_features you promise are enabled for a + # particular target. target_features are currently not validated against + # the actual valid features supported by the target architecture. + #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, +] + +# This section is considered when running `cargo deny check advisories` +# More documentation for the advisories section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html +[advisories] +# The path where the advisory database is cloned/fetched into +db-path = "~/.cargo/advisory-db" +# The url of the advisory database to use +db-urls = ["https://github.com/rustsec/advisory-db"] +# The lint level for security vulnerabilities +vulnerability = "deny" +# The lint level for unmaintained crates +unmaintained = "warn" +# The lint level for crates that have been yanked from their source registry +yanked = "warn" +# The lint level for crates with security notices. Note that as of +# 2019-12-17 there are no security notice advisories in +# https://github.com/rustsec/advisory-db +notice = "warn" +# A list of advisory IDs to ignore. Note that ignored advisories will still +# output a note when they are encountered. +ignore = [ + # Comes from honggfuzz via storage-proof-fuzzer: 'memmap' + "RUSTSEC-2020-0077", + # time (origin: Substrate RPC + benchmarking crates) + "RUSTSEC-2020-0071", + # chrono (origin: Substrate benchmarking + cli + ...) + "RUSTSEC-2020-0159", + # lru 0.6.6 (origin: libp2p) + "RUSTSEC-2021-0130", + # ansi_term (The maintainer has adviced that this crate is deprecated and will not receive any maintenance. + # Once other crates will move to some alternative, we'll do that too) + "RUSTSEC-2021-0139", + # rocksdb (origin: Substrate and Polkadot kvdb-rocksdb - we need to upgrade soon) + "RUSTSEC-2022-0046", + # owning_ref (origin: Substrate, libp2p) + "RUSTSEC-2022-0040", +] +# Threshold for security vulnerabilities, any vulnerability with a CVSS score +# lower than the range specified will be ignored. Note that ignored advisories +# will still output a note when they are encountered. +# * None - CVSS Score 0.0 +# * Low - CVSS Score 0.1 - 3.9 +# * Medium - CVSS Score 4.0 - 6.9 +# * High - CVSS Score 7.0 - 8.9 +# * Critical - CVSS Score 9.0 - 10.0 +#severity-threshold = + +# This section is considered when running `cargo deny check licenses` +# More documentation for the licenses section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html +[licenses] +# The lint level for crates which do not have a detectable license +unlicensed = "allow" +# List of explictly allowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.7 short identifier (+ optional exception)]. +allow = [ + "BlueOak-1.0.0" +] +# List of explictly disallowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.7 short identifier (+ optional exception)]. +deny = [ + #"Nokia", +] +# Lint level for licenses considered copyleft +copyleft = "allow" +# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses +# * both - The license will be approved if it is both OSI-approved *AND* FSF +# * either - The license will be approved if it is either OSI-approved *OR* FSF +# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF +# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved +# * neither - This predicate is ignored and the default lint level is used +allow-osi-fsf-free = "either" +# Lint level used when no other predicates are matched +# 1. License isn't in the allow or deny lists +# 2. License isn't copyleft +# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" +default = "deny" +# The confidence threshold for detecting a license from license text. +# The higher the value, the more closely the license text must be to the +# canonical license text of a valid SPDX license file. +# [possible values: any between 0.0 and 1.0]. +confidence-threshold = 0.9 +# Allow 1 or more licenses on a per-crate basis, so that particular licenses +# aren't accepted for every possible crate as with the normal allow list +exceptions = [ + # Each entry is the crate and version constraint, and its specific allow + # list + #{ allow = ["Zlib"], name = "adler32", version = "*" }, +] + +# Some crates don't have (easily) machine readable licensing information, +# adding a clarification entry for it allows you to manually specify the +# licensing information +[[licenses.clarify]] +# The name of the crate the clarification applies to +name = "ring" +# THe optional version constraint for the crate +#version = "*" +# The SPDX expression for the license requirements of the crate +expression = "OpenSSL" +# One or more files in the crate's source used as the "source of truth" for +# the license expression. If the contents match, the clarification will be used +# when running the license check, otherwise the clarification will be ignored +# and the crate will be checked normally, which may produce warnings or errors +# depending on the rest of your configuration +license-files = [ + # Each entry is a crate relative path, and the (opaque) hash of its contents + { path = "LICENSE", hash = 0xbd0eed23 } +] + +[[licenses.clarify]] +name = "webpki" +expression = "ISC" +license-files = [{ path = "LICENSE", hash = 0x001c7e6c }] + +[licenses.private] +# If true, ignores workspace crates that aren't published, or are only +# published to private registries +ignore = false +# One or more private registries that you might publish crates to, if a crate +# is only published to private registries, and ignore is true, the crate will +# not have its license(s) checked +registries = [ + #"https://sekretz.com/registry +] + +# This section is considered when running `cargo deny check bans`. +# More documentation about the 'bans' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html +[bans] +# Lint level for when multiple versions of the same crate are detected +multiple-versions = "warn" +# The graph highlighting used when creating dotgraphs for crates +# with multiple versions +# * lowest-version - The path to the lowest versioned duplicate is highlighted +# * simplest-path - The path to the version with the fewest edges is highlighted +# * all - Both lowest-version and simplest-path are used +highlight = "lowest-version" +# List of crates that are allowed. Use with care! +allow = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +# List of crates to deny +deny = [ + { name = "parity-util-mem", version = "<0.6" } + # Each entry the name of a crate and a version range. If version is + # not specified, all versions will be matched. +] +# Certain crates/versions that will be skipped when doing duplicate detection. +skip = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +# Similarly to `skip` allows you to skip certain crates during duplicate +# detection. Unlike skip, it also includes the entire tree of transitive +# dependencies starting at the specified crate, up to a certain depth, which is +# by default infinite +skip-tree = [ + #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, +] + +# This section is considered when running `cargo deny check sources`. +# More documentation about the 'sources' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html +[sources] +# Lint level for what to happen when a crate from a crate registry that is not +# in the allow list is encountered +unknown-registry = "deny" +# Lint level for what to happen when a crate from a git repository that is not +# in the allow list is encountered +unknown-git = "allow" +# List of URLs for allowed crate registries. Defaults to the crates.io index +# if not specified. If it is specified but empty, no registries are allowed. +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +# List of URLs for allowed Git repositories +allow-git = [] diff --git a/deployments/README.md b/deployments/README.md new file mode 100644 index 00000000000..2f804b3ad32 --- /dev/null +++ b/deployments/README.md @@ -0,0 +1,261 @@ +# Bridge Deployments + +## Requirements +Make sure to install `docker` and `docker-compose` to be able to run and test bridge deployments. If +for whatever reason you can't or don't want to use Docker, you can find some scripts for running the +bridge [here](https://github.com/svyatonik/parity-bridges-common.test). + +## Networks +One of the building blocks we use for our deployments are _networks_. A network is a collection of +homogenous blockchain nodes. We have Docker Compose files for each network that we want to bridge. +Each of the compose files found in the `./networks` folder is able to independently spin up a +network like so: + +```bash +docker-compose -f ./networks/rialto.yml up +``` + +After running this command we would have a network of several nodes producing blocks. + +## Bridges +A _bridge_ is a way for several _networks_ to connect to one another. Bridge deployments have their +own Docker Compose files which can be found in the `./bridges` folder. These Compose files typically +contain bridge relayers, which are services external to blockchain nodes, and other components such +as testing infrastructure, or user interfaces. + +Unlike the network Compose files, these *cannot* be deployed on their own. They must be combined +with different networks. + +In general, we can deploy the bridge using `docker-compose up` in the following way: + +```bash +docker-compose -f .yml \ + -f .yml \ + -f .yml \ + -f .yml up +``` + +If you want to see how the Compose commands are actually run, check out the source code of the +[`./run.sh`](./run.sh). + +One thing worth noting is that we have a _monitoring_ Compose file. This adds support for Prometheus +and Grafana. We cover these in more details in the [Monitoring](#monitoring) section. At the moment +the monitoring Compose file is _not_ optional, and must be included for bridge deployments. + +### Running and Updating Deployments +We currently support three bridge deployments +1. Rialto Substrate to Millau Substrate +2. Rialto Parachain Substrate to Millau Substrate +2. Westend Substrate to Millau Substrate + +These bridges can be deployed using our [`./run.sh`](./run.sh) script. + +The first argument it takes is the name of the bridge you want to run. Right now we only support three +bridges: `rialto-millau`, `rialto-parachain-millau` and `westend-millau`. + +```bash +./run.sh rialto-millau +``` + +If you add a second `update` argument to the script it will pull the latest images from Docker Hub +and restart the deployment. + +```bash +./run.sh rialto-millau update +``` + +You can also bring down a deployment using the script with the `stop` argument. + +```bash +./run.sh rialto-millau stop +``` + +### Adding Deployments +We need two main things when adding a new deployment. First, the new network which we want to +bridge. A compose file for the network should be added in the `/networks/` folder. Secondly we'll +need a new bridge Compose file in `./bridges/`. This should configure the bridge relayer nodes +correctly for the two networks, and add any additional components needed for the deployment. If you +want you can also add support in the `./run` script for the new deployment. While recommended it's +not strictly required. + +## General Notes + +Rialto authorities are named: `Alice`, `Bob`, `Charlie`, `Dave`, `Eve`. +Millau authorities are named: `Alice`, `Bob`, `Charlie`, `Dave`, `Eve`. +RialtoParachain authorities are named: `Alice`, `Bob`. + +`Sudo` is a sudo account on all chains. + +Both authorities and following accounts have enough funds (for test purposes) on corresponding Substrate chains: + +- on Rialto: `Ferdie`. +- on Millau: `Ferdie`. +- on RialtoParachain: `Charlie`, `Dave`, `Eve`, `Ferdie`. + +Names of accounts on Substrate (Rialto and Millau) chains may be prefixed with `//` and used as +seeds for the `sr25519` keys. This seed may also be used in the signer argument in Substrate relays. +Example: + +```bash +./substrate-relay relay-headers rialto-to-millau \ + --source-host rialto-node-alice \ + --source-port 9944 \ + --target-host millau-node-alice \ + --target-port 9944 \ + --source-signer //Ferdie \ + --prometheus-host=0.0.0.0 +``` + +Some accounts are used by bridge components. Using these accounts to sign other transactions +is not recommended, because this may lead to nonces conflict. + +Following accounts are used when `rialto-millau` bridge is running: + +- Millau's `Rialto.HeadersAndMessagesRelay` signs complex headers+messages relay transactions on Millau chain; +- Rialto's `Millau.HeadersAndMessagesRelay` signs complex headers+messages relay transactions on Rialto chain; +- Millau's `Rialto.MessagesSender` signs Millau transactions which contain messages for Rialto; +- Rialto's `Millau.MessagesSender` signs Rialto transactions which contain messages for Millau; +- Millau's `Rialto.OutboundMessagesRelay.Lane00000001` signs relay transactions with message delivery confirmations (lane 00000001) from Rialto to Millau; +- Rialto's `Millau.InboundMessagesRelay.Lane00000001` signs relay transactions with messages (lane 00000001) from Millau to Rialto; +- Millau's `Millau.OutboundMessagesRelay.Lane00000001` signs relay transactions with messages (lane 00000001) from Rialto to Millau; +- Rialto's `Rialto.InboundMessagesRelay.Lane00000001` signs relay transactions with message delivery confirmations (lane 00000001) from Millau to Rialto; +- Millau's `Rialto.MessagesOwner` signs relay transactions with updated Rialto -> Millau conversion rate; +- Rialto's `Millau.MessagesOwner` signs relay transactions with updated Millau -> Rialto conversion rate. + +Following accounts are used when `westend-millau` bridge is running: + +- Millau's `Westend.GrandpaOwner` is signing with-Westend GRANDPA pallet initialization transaction. +- Millau's `Westend.HeadersRelay1` and `Westend.HeadersRelay2` are signing transactions with new Westend headers. +- Millau's `Westend.WestmintHeaders1` and `Westend.WestmintHeaders2` is signing transactions with new Westming headers. + +Following accounts are used when `rialto-parachain-millau` bridge is running: + +- RialtoParachain's `Millau.MessagesSender` signs RialtoParachain transactions which contain messages for Millau; +- Millau's `RialtoParachain.MessagesSender` signs Millau transactions which contain messages for RialtoParachain; +- Millau's `RialtoParachain.HeadersAndMessagesRelay` signs complex headers+parachains+messages relay transactions on Millau chain; +- RialtoParachain's `Millau.HeadersAndMessagesRelay` signs complex headers+messages relay transactions on RialtoParachain chain. + +### Docker Usage +When the network is running you can query logs from individual nodes using: + +```bash +docker logs rialto_millau-node-charlie_1 -f +``` + +To kill all leftover containers and start the network from scratch next time: +```bash +docker ps -a --format "{{.ID}}" | xargs docker rm # This removes all containers! +``` + +### Docker Compose Usage +If you're not familiar with how to use `docker-compose` here are some useful commands you'll need +when interacting with the bridge deployments: + +```bash +docker-compose pull # Get the latest images from the Docker Hub +docker-compose build # This is going to build images +docker-compose up # Start all the nodes +docker-compose up -d # Start the nodes in detached mode. +docker-compose down # Stop the network. +``` + +Note that you'll also need to add the appropriate `-f` arguments that were mentioned in the +[Bridges](#bridges) section. You can read more about using multiple Compose files +[here](https://docs.docker.com/compose/extends/#multiple-compose-files). One thing worth noting is +that the _order_ the compose files are specified in matters. A different order will result in a +different configuration. + +You can sanity check the final config like so: + +```bash +docker-compose -f docker-compose.yml -f docker-compose.override.yml config > docker-compose.merged.yml +``` + +## Docker and Git Deployment +It is also possible to avoid using images from the Docker Hub and instead build +containers from Git. There are two ways to build the images this way. + +### Git Repo +If you have cloned the bridges repo you can build local Docker images by running the following +command at the top level of the repo: + +```bash +docker build . -t local/ --build-arg=PROJECT= +``` + +This will build a local image of a particular component with a tag of +`local/`. This tag can be used in Docker Compose files. + +You can configure the build using Docker +[build arguments](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg). +Here are the arguments currently supported: + - `BRIDGE_REPO`: Git repository of the bridge node and relay code + - `BRIDGE_HASH`: Commit hash within that repo (can also be a branch or tag) + - `ETHEREUM_REPO`: Git repository of the OpenEthereum client + - `ETHEREUM_HASH`: Commit hash within that repo (can also be a branch or tag) + - `PROJECT`: Project to build withing bridges repo. Can be one of: + - `rialto-bridge-node` + - `millau-bridge-node` + - `substrate-relay` + +### GitHub Actions +We have a nightly job which runs and publishes Docker images for the different nodes and relayers to +the [ParityTech Docker Hub](https://hub.docker.com/u/paritytech) organization. These images are used +for our ephemeral (temporary) test networks. Additionally, any time a tag in the form of `v*` is +pushed to GitHub the publishing job is run. This will build all the components (nodes, relayers) and +publish them. + +With images built using either method, all you have to do to use them in a deployment is change the +`image` field in the existing Docker Compose files to point to the tag of the image you want to use. + +### Monitoring +[Prometheus](https://prometheus.io/) is used by the bridge relay to monitor information such as system +resource use, and block data (e.g the best blocks it knows about). In order to visualize this data +a [Grafana](https://grafana.com/) dashboard can be used. + +As part of the Rialto `docker-compose` setup we spin up a Prometheus server and Grafana dashboard. The +Prometheus server connects to the Prometheus data endpoint exposed by the bridge relay. The Grafana +dashboard uses the Prometheus server as its data source. + +The default port for the bridge relay's Prometheus data is `9616`. The host and port can be +configured though the `--prometheus-host` and `--prometheus-port` flags. The Prometheus server's +dashboard can be accessed at `http://localhost:9090`. The Grafana dashboard can be accessed at +`http://localhost:3000`. Note that the default log-in credentials for Grafana are `admin:admin`. + +### Environment Variables +Here is an example `.env` file which is used for production deployments and network updates. For +security reasons it is not kept as part of version control. When deploying a network this +file should be correctly populated and kept in the appropriate [`bridges`](`./bridges`) deployment +folder. + +The `UI_SUBSTRATE_PROVIDER` variable lets you define the url of the Substrate node that the user +interface will connect to. `UI_ETHEREUM_PROVIDER` is used only as a guidance for users to connect +Metamask to the right Ethereum network. `UI_EXPECTED_ETHEREUM_NETWORK_ID` is used by +the user interface as a fail safe to prevent users from connecting their Metamask extension to an +unexpected network. + +```bash +GRAFANA_ADMIN_PASS=admin_pass +GRAFANA_SERVER_ROOT_URL=%(protocol)s://%(domain)s:%(http_port)s/ +GRAFANA_SERVER_DOMAIN=server.domain.io +MATRIX_ACCESS_TOKEN="access-token" +WITH_PROXY=1 # Optional +UI_SUBSTRATE_PROVIDER=ws://localhost:9944 +UI_ETHEREUM_PROVIDER=http://localhost:8545 +UI_EXPECTED_ETHEREUM_NETWORK_ID=105 +``` + +### UI + +Use [wss://rialto.bridges.test-installations.parity.io/](https://polkadot.js.org/apps/) +as a custom endpoint for [https://polkadot.js.org/apps/](https://polkadot.js.org/apps/). + +### Polkadot.js UI + +To teach the UI decode our custom types used in the pallet, go to: `Settings -> Developer` +and import the [`./types.json`](./types.json) + +## Scripts + +There are some bash scripts in `scripts` folder that allow testing `Relay` +without running the entire network within docker. Use if needed for development. diff --git a/deployments/bridges/common/generate_messages.sh b/deployments/bridges/common/generate_messages.sh new file mode 100644 index 00000000000..565b82bc917 --- /dev/null +++ b/deployments/bridges/common/generate_messages.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +# Script for generating messages from a source chain to a target chain. +# Prerequisites: mounting the common folder in the docker container (Adding the following volume entry): +# - ./bridges/common:/common +# It can be used by executing `source /common/generate_messages.sh` in a different script, +# after setting the following variables: +# SOURCE_CHAIN +# TARGET_CHAIN +# MAX_SUBMIT_DELAY_S +# SEND_MESSAGE - the command that is executed to send a message +# SECONDARY_EXTRA_ARGS - optional, for example "--use-xcm-pallet" +# EXTRA_ARGS - for example "--use-xcm-pallet" +# REGULAR_PAYLOAD +# BATCH_PAYLOAD +# MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE + +SECONDARY_EXTRA_ARGS=${SECONDARY_EXTRA_ARGS:-""} + +# Sleep a bit between messages +rand_sleep() { + SUBMIT_DELAY_S=`shuf -i 0-$MAX_SUBMIT_DELAY_S -n 1` + echo "Sleeping $SUBMIT_DELAY_S seconds..." + sleep $SUBMIT_DELAY_S + NOW=`date "+%Y-%m-%d %H:%M:%S"` + echo "Woke up at $NOW" +} + +# start sending large messages immediately +LARGE_MESSAGES_TIME=0 +# start sending message packs in a hour +BUNCH_OF_MESSAGES_TIME=3600 + +while true +do + rand_sleep + + # send regular message + echo "Sending Message from $SOURCE_CHAIN to $TARGET_CHAIN" + $SEND_MESSAGE $EXTRA_ARGS raw $REGULAR_PAYLOAD + + # every other hour we're sending 3 large (size, weight, size+weight) messages + if [ $SECONDS -ge $LARGE_MESSAGES_TIME ]; then + LARGE_MESSAGES_TIME=$((SECONDS + 7200)) + + rand_sleep + echo "Sending Maximal Size Message from $SOURCE_CHAIN to $TARGET_CHAIN" + $SEND_MESSAGE \ + sized max + fi + + # every other hour we're sending a bunch of small messages + if [ $SECONDS -ge $BUNCH_OF_MESSAGES_TIME ]; then + BUNCH_OF_MESSAGES_TIME=$((SECONDS + 7200)) + + for i in $(seq 0 $MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE); + do + $SEND_MESSAGE \ + $EXTRA_ARGS \ + raw $BATCH_PAYLOAD + done + + fi +done diff --git a/deployments/bridges/rialto-millau/dashboard/grafana/relay-millau-to-rialto-messages-dashboard.json b/deployments/bridges/rialto-millau/dashboard/grafana/relay-millau-to-rialto-messages-dashboard.json new file mode 100644 index 00000000000..af8749325de --- /dev/null +++ b/deployments/bridges/rialto-millau/dashboard/grafana/relay-millau-to-rialto-messages-dashboard.json @@ -0,0 +1,1153 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Millau_to_Rialto_MessageLane_00000000_best_target_block_number", + "instant": false, + "interval": "", + "legendFormat": "At Rialto", + "refId": "A" + }, + { + "expr": "Millau_to_Rialto_MessageLane_00000000_best_target_at_source_block_number", + "instant": false, + "interval": "", + "legendFormat": "At Millau", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized Rialto headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Millau_to_Rialto_MessageLane_00000000_best_source_block_number", + "interval": "", + "legendFormat": "At Millau", + "refId": "A" + }, + { + "expr": "Millau_to_Rialto_MessageLane_00000000_best_source_at_target_block_number", + "interval": "", + "legendFormat": "At Rialto", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized Millau headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Millau -> Rialto messages are not detected by relay", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from Rialto\", \"type\", \"source_latest_generated\"), \"type\", \"Latest Rialto message received by Millau\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + }, + { + "expr": "increase(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"source_latest_generated\"}[10m]) OR on() vector(0)", + "hide": true, + "interval": "", + "legendFormat": "Messages generated in last 5 minutes (Millau -> Rialto, 00000000)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest message confirmed by Rialto to Millau\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest Rialto message received by Millau\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "sum" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "7m", + "frequency": "1m", + "handler": 1, + "name": "Messages from Millau to Rialto are not being delivered", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 20 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"source_latest_generated\"}[2m])) - scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]))", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "Undelivered messages at Rialto", + "refId": "A" + }, + { + "expr": "increase(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[5m]) OR on() vector(0)", + "interval": "", + "legendFormat": "Millau Messages delivered to Rialto in last 5m", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 50 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Too many unconfirmed messages (Millau -> Rialto)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 20 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "legendFormat": "Unconfirmed messages at Millau", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Rewards are not being confirmed (Millau -> Rialto messages)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 20 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"source_latest_confirmed\"}[2m])) - scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_confirmed\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at Rialto", + "refId": "A" + }, + { + "expr": "(scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]) > bool min_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at Millau->Rialto (zero if messages are not being delivered to Rialto)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Reward lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 38 + }, + "id": 16, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "7.1.3", + "targets": [ + { + "expr": "avg_over_time(process_cpu_usage_percentage{instance='relay-millau-rialto:9616'}[1m])", + "instant": true, + "interval": "", + "legendFormat": "1 CPU = 100", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Relay process CPU usage (1 CPU = 100)", + "type": "gauge" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 38 + }, + "hiddenSeries": false, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "system_average_load{instance='relay-millau-rialto:9616'}", + "interval": "", + "legendFormat": "Average system load in last {{over}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "System load average", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 38 + }, + "hiddenSeries": false, + "id": 20, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "process_memory_usage_bytes{instance='relay-millau-rialto:9616'} / 1024 / 1024", + "interval": "", + "legendFormat": "Process memory, MB", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory used by relay process", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Millau to Rialto Message Sync Dashboard", + "uid": "relay-millau-to-rialto-messages", + "version": 1 +} diff --git a/deployments/bridges/rialto-millau/dashboard/grafana/relay-rialto-to-millau-messages-dashboard.json b/deployments/bridges/rialto-millau/dashboard/grafana/relay-rialto-to-millau-messages-dashboard.json new file mode 100644 index 00000000000..efee749c5c1 --- /dev/null +++ b/deployments/bridges/rialto-millau/dashboard/grafana/relay-rialto-to-millau-messages-dashboard.json @@ -0,0 +1,1145 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 4, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Rialto_to_Millau_MessageLane_00000000_best_target_block_number", + "instant": false, + "interval": "", + "legendFormat": "At Millau", + "refId": "A" + }, + { + "expr": "Rialto_to_Millau_MessageLane_00000000_best_target_at_source_block_number", + "instant": false, + "interval": "", + "legendFormat": "At Rialto", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized Millau headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Rialto_to_Millau_MessageLane_00000000_best_source_block_number", + "interval": "", + "legendFormat": "At Rialto", + "refId": "A" + }, + { + "expr": "Rialto_to_Millau_MessageLane_00000000_best_source_at_target_block_number", + "interval": "", + "legendFormat": "At Millau", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized Rialto headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Rialto -> Millau messages are not detected by relay", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from Millau\", \"type\", \"source_latest_generated\"), \"type\", \"Latest message received by Rialto\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + }, + { + "expr": "increase(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"source_latest_generated\"}[10m]) OR on() vector(0)", + "hide": true, + "interval": "", + "legendFormat": "Messages generated in last 5 minutes (Rialto -> Millau, 00000000)", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest message confirmed by Millau to Rialto\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest message received by Rialto\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "sum" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "7m", + "frequency": "1m", + "handler": 1, + "name": "Messages from Rialto to Millau are not being delivered", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 20 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"source_latest_generated\"}[2m])) - scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]))", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "Undelivered messages at Millau", + "refId": "A" + }, + { + "expr": "increase(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[5m]) OR on() vector(0)", + "interval": "", + "legendFormat": "Rialto Messages delivered to Millau in last 5m", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 50 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Too many unconfirmed messages (Rialto -> Millau)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 20 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "legendFormat": "Unconfirmed messages at Rialto", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Rewards are not being confirmed (Rialto -> Millau messages)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 20 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"source_latest_confirmed\"}[2m])) - scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_confirmed\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at Millau", + "refId": "A" + }, + { + "expr": "(scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]) > bool min_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at Millau (zero if messages are not being delivered to Millau)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Reward lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 38 + }, + "id": 16, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "7.1.3", + "targets": [ + { + "expr": "avg_over_time(process_cpu_usage_percentage{instance='relay-millau-rialto:9616'}[1m])", + "instant": true, + "interval": "", + "legendFormat": "1 CPU = 100", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Relay process CPU usage (1 CPU = 100)", + "type": "gauge" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 38 + }, + "hiddenSeries": false, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "system_average_load{instance='relay-millau-rialto:9616'}", + "interval": "", + "legendFormat": "Average system load in last {{over}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "System load average", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 38 + }, + "hiddenSeries": false, + "id": 20, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "process_memory_usage_bytes{instance='relay-millau-rialto:9616'} / 1024 / 1024", + "interval": "", + "legendFormat": "Process memory, MB", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory used by relay process", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Rialto to Millau Message Sync Dashboard", + "uid": "relay-rialto-to-millau-messages", + "version": 2 +} diff --git a/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json b/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json new file mode 100644 index 00000000000..e4695351105 --- /dev/null +++ b/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json @@ -0,0 +1,791 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "C", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "At-Rialto relay balances are too low", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "at_Rialto_relay_MillauHeaders_balance", + "interval": "", + "legendFormat": "With-Millau headers relay account balance", + "refId": "A" + }, + { + "expr": "at_Rialto_relay_MillauMessages_balance", + "interval": "", + "legendFormat": "With-Millau messages relay account balance", + "refId": "B" + }, + { + "expr": "at_Rialto_relay_MillauMessagesPalletOwner_balance", + "interval": "", + "legendFormat": "With-Millau messages pallet owner account balance", + "refId": "C" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1000 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Rialto relay balances", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "C", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "At-Millau relay balances are too low", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "hiddenSeries": false, + "id": 9, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "at_Millau_relay_RialtoHeaders_balance{instance=\"relay-millau-rialto:9616\"}", + "interval": "", + "legendFormat": "With-Rialto headers relay account balance", + "refId": "A" + }, + { + "expr": "at_Millau_relay_RialtoMessages_balance", + "interval": "", + "legendFormat": "With-Rialto messages relay account balance", + "refId": "B" + }, + { + "expr": "at_Millau_relay_RialtoMessagesPalletOwner_balance", + "interval": "", + "legendFormat": "With-Rialto messages pallet owner account balance", + "refId": "C" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1000 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Millau relay balances", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Whether with-Rialto-grandpa-pallet and Rialto itself are on different forks alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "hiddenSeries": false, + "id": 11, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Rialto_to_Millau_MessageLane_00000000_is_source_and_source_at_target_using_different_forks OR on() vector(0)", + "interval": "", + "legendFormat": "On different forks?", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Whether with-Rialto-grandpa-pallet and Rialto itself are on different forks", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Whether with-Rialto-grandpa-pallet and Rialto itself are on different forks alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Millau_to_Rialto_MessageLane_00000000_is_source_and_source_at_target_using_different_forks OR on() vector(0)", + "interval": "", + "legendFormat": "On different forks?", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Whether with-Millau-grandpa-pallet and Millau itself are on different forks", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 32 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Millau_to_Rialto_MessageLane_00000000_unprofitable_delivery_transactions", + "interval": "", + "legendFormat": "Millau -> Rialto, lane 00000000", + "refId": "A" + }, + { + "expr": "Rialto_to_Millau_MessageLane_00000000_unprofitable_delivery_transactions", + "interval": "", + "legendFormat": "Rialto -> Millau, lane 00000000", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Count of unprofitable message delivery transactions", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "10s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Rialto+Millau maintenance dashboard", + "uid": "7AuyrjlMz", + "version": 1 +} diff --git a/deployments/bridges/rialto-millau/dashboard/prometheus/targets.yml b/deployments/bridges/rialto-millau/dashboard/prometheus/targets.yml new file mode 100644 index 00000000000..16b798b5a25 --- /dev/null +++ b/deployments/bridges/rialto-millau/dashboard/prometheus/targets.yml @@ -0,0 +1,4 @@ +- targets: + - relay-millau-rialto:9616 + - relay-messages-millau-to-rialto-lane-00000001:9616 + - relay-messages-rialto-to-millau-lane-00000001:9616 diff --git a/deployments/bridges/rialto-millau/docker-compose.yml b/deployments/bridges/rialto-millau/docker-compose.yml new file mode 100644 index 00000000000..cd77dbbf122 --- /dev/null +++ b/deployments/bridges/rialto-millau/docker-compose.yml @@ -0,0 +1,110 @@ +# Exposed ports: 10016, 10116, 10216, 10316, 10416, 10516, 10716 + +version: '3.5' +services: + # We provide overrides for these particular nodes since they are public facing + # nodes which we use to connect from things like Polkadot JS Apps. + rialto-node-charlie: + environment: + VIRTUAL_HOST: wss.rialto.brucke.link + VIRTUAL_PORT: 9944 + LETSENCRYPT_HOST: wss.rialto.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + + millau-node-charlie: + environment: + VIRTUAL_HOST: wss.millau.brucke.link + VIRTUAL_PORT: 9944 + LETSENCRYPT_HOST: wss.millau.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + + relay-millau-rialto: &sub-bridge-relay + image: ${SUBSTRATE_RELAY_IMAGE:-paritytech/substrate-relay} + entrypoint: /entrypoints/relay-millau-rialto-entrypoint.sh + volumes: + - ./bridges/common:/common + - ./bridges/rialto-millau/entrypoints:/entrypoints + environment: + RUST_LOG: rpc=trace,bridge=trace + ports: + - "10016:9616" + depends_on: &all-nodes + - millau-node-alice + - millau-node-bob + - millau-node-charlie + - millau-node-dave + - millau-node-eve + - rialto-node-alice + - rialto-node-bob + - rialto-node-charlie + - rialto-node-dave + - rialto-node-eve + + relay-messages-millau-to-rialto-lane-00000001: + <<: *sub-bridge-relay + environment: + MSG_EXCHANGE_GEN_LANE: "00000001" + entrypoint: /entrypoints/relay-messages-millau-to-rialto-entrypoint.sh + ports: + - "10116:9616" + depends_on: + - relay-millau-rialto + + relay-messages-millau-to-rialto-generator: + <<: *sub-bridge-relay + environment: + RUST_LOG: bridge=trace + MSG_EXCHANGE_GEN_SECONDARY_LANE: "00000001" + entrypoint: /entrypoints/relay-messages-to-rialto-generator-entrypoint.sh + ports: + - "10216:9616" + depends_on: + - relay-millau-rialto + + relay-messages-millau-to-rialto-resubmitter: + <<: *sub-bridge-relay + environment: + RUST_LOG: bridge=trace + entrypoint: /entrypoints/relay-messages-to-rialto-resubmitter-entrypoint.sh + ports: + - "10316:9616" + depends_on: + - relay-messages-millau-to-rialto-generator + + relay-messages-rialto-to-millau-lane-00000001: + <<: *sub-bridge-relay + environment: + RUST_LOG: bridge=trace + MSG_EXCHANGE_GEN_LANE: "00000001" + entrypoint: /entrypoints/relay-messages-rialto-to-millau-entrypoint.sh + ports: + - "10416:9616" + depends_on: + - relay-millau-rialto + + relay-messages-rialto-to-millau-generator: + <<: *sub-bridge-relay + environment: + RUST_LOG: bridge=trace + MSG_EXCHANGE_GEN_SECONDARY_LANE: "00000001" + entrypoint: /entrypoints/relay-messages-to-millau-generator-entrypoint.sh + ports: + - "10516:9616" + depends_on: + - relay-millau-rialto + + # Note: These are being overridden from the top level `monitoring` compose file. + grafana-dashboard: + environment: + VIRTUAL_HOST: grafana.millau.brucke.link,grafana.rialto.brucke.link + VIRTUAL_PORT: 3000 + LETSENCRYPT_HOST: grafana.millau.brucke.link,grafana.rialto.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + volumes: + - ./bridges/rialto-millau/dashboard/grafana:/etc/grafana/dashboards/rialto-millau:ro + - ./networks/dashboard/grafana/beefy-dashboard.json:/etc/grafana/dashboards/beefy.json + + prometheus-metrics: + volumes: + - ./bridges/rialto-millau/dashboard/prometheus/targets.yml:/etc/prometheus/targets-rialto-millau.yml + depends_on: *all-nodes diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-messages-millau-to-rialto-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-messages-millau-to-rialto-entrypoint.sh new file mode 100755 index 00000000000..3a55cc782ca --- /dev/null +++ b/deployments/bridges/rialto-millau/entrypoints/relay-messages-millau-to-rialto-entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -xeu + +sleep 15 + +MESSAGE_LANE=${MSG_EXCHANGE_GEN_LANE:-00000000} + +/home/user/substrate-relay relay-messages millau-to-rialto \ + --lane $MESSAGE_LANE \ + --source-host millau-node-bob \ + --source-port 9944 \ + --source-signer //Rialto.OutboundMessagesRelay.Lane00000001 \ + --target-host rialto-node-bob \ + --target-port 9944 \ + --target-signer //Millau.InboundMessagesRelay.Lane00000001 \ + --prometheus-host=0.0.0.0 diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-messages-rialto-to-millau-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-messages-rialto-to-millau-entrypoint.sh new file mode 100755 index 00000000000..145cd20100d --- /dev/null +++ b/deployments/bridges/rialto-millau/entrypoints/relay-messages-rialto-to-millau-entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -xeu + +sleep 15 + +MESSAGE_LANE=${MSG_EXCHANGE_GEN_LANE:-00000000} + +/home/user/substrate-relay relay-messages rialto-to-millau \ + --lane $MESSAGE_LANE \ + --source-host rialto-node-bob \ + --source-port 9944 \ + --source-signer //Millau.OutboundMessagesRelay.Lane00000001 \ + --target-host millau-node-bob \ + --target-port 9944 \ + --target-signer //Rialto.InboundMessagesRelay.Lane00000001 \ + --prometheus-host=0.0.0.0 diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh new file mode 100755 index 00000000000..790e9c82110 --- /dev/null +++ b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# THIS SCRIPT IS NOT INTENDED FOR USE IN PRODUCTION ENVIRONMENT +# +# This scripts periodically calls the Substrate relay binary to generate messages. These messages +# are sent from the Rialto network to the Millau network. + +set -eu + +# Max delay before submitting transactions (s) +MAX_SUBMIT_DELAY_S=${MSG_EXCHANGE_GEN_MAX_SUBMIT_DELAY_S:-30} +MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE=1024 + +SHARED_CMD="/home/user/substrate-relay send-message rialto-to-millau" +SHARED_HOST="--source-host rialto-node-bob --source-port 9944" +SOURCE_SIGNER="--source-signer //Millau.MessagesSender" + +SEND_MESSAGE="$SHARED_CMD $SHARED_HOST $SOURCE_SIGNER" + +SOURCE_CHAIN="Rialto" +TARGET_CHAIN="Millau" +EXTRA_ARGS="" +REGULAR_PAYLOAD="020419ac" +BATCH_PAYLOAD="020419ac" + +source /common/generate_messages.sh diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh new file mode 100755 index 00000000000..3f3105a4d0e --- /dev/null +++ b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# THIS SCRIPT IS NOT INTENDED FOR USE IN PRODUCTION ENVIRONMENT +# +# This scripts periodically calls the Substrate relay binary to generate messages. These messages +# are sent from the Millau network to the Rialto network. + +set -eu + +# Max delay before submitting transactions (s) +MAX_SUBMIT_DELAY_S=${MSG_EXCHANGE_GEN_MAX_SUBMIT_DELAY_S:-30} +MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE=128 + +SHARED_CMD=" /home/user/substrate-relay send-message millau-to-rialto" +SHARED_HOST="--source-host millau-node-bob --source-port 9944" +SOURCE_SIGNER="--source-signer //Rialto.MessagesSender" + +SEND_MESSAGE="$SHARED_CMD $SHARED_HOST $SOURCE_SIGNER" + +SOURCE_CHAIN="Millau" +TARGET_CHAIN="Rialto" +EXTRA_ARGS="" +REGULAR_PAYLOAD="020419ac" +BATCH_PAYLOAD="020419ac" + +source /common/generate_messages.sh diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-resubmitter-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-resubmitter-entrypoint.sh new file mode 100755 index 00000000000..bd9bf800ad4 --- /dev/null +++ b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-resubmitter-entrypoint.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -xeu + +sleep 15 + +# //Rialto.MessagesSender is signing Millau -> Rialto message-send transactions, which are causing problems. +# +# When large message is being sent from Millau to Rialto AND other transactions are +# blocking it from being mined, we'll see something like this in logs: +# +# Millau transaction priority with tip=0: 17800827994. Target priority: +# 526186677695 +# +# So since fee multiplier in Millau is `1` and `WeightToFee` is `IdentityFee`, then +# we need tip around `526186677695 - 17800827994 = 508_385_849_701`. Let's round it +# up to `1_000_000_000_000`. + +/home/user/substrate-relay resubmit-transactions millau \ + --target-host millau-node-alice \ + --target-port 9944 \ + --target-signer //Rialto.MessagesSender \ + --stalled-blocks 5 \ + --tip-limit 1000000000000 \ + --tip-step 1000000000 \ + make-it-best-transaction diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh new file mode 100755 index 00000000000..367792121de --- /dev/null +++ b/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -xeu + +sleep 15 + +/home/user/substrate-relay init-bridge millau-to-rialto \ + --source-host millau-node-alice \ + --source-port 9944 \ + --target-host rialto-node-alice \ + --target-port 9944 \ + --target-signer //Sudo + +/home/user/substrate-relay init-bridge rialto-to-millau \ + --source-host rialto-node-alice \ + --source-port 9944 \ + --target-host millau-node-alice \ + --target-port 9944 \ + --target-signer //Sudo + +# Give chain a little bit of time to process initialization transaction +sleep 6 + +/home/user/substrate-relay relay-headers-and-messages millau-rialto \ + --millau-host millau-node-alice \ + --millau-port 9944 \ + --millau-signer //Rialto.HeadersAndMessagesRelay \ + --millau-messages-pallet-owner=//Rialto.MessagesOwner \ + --millau-transactions-mortality=64 \ + --rialto-host rialto-node-alice \ + --rialto-port 9944 \ + --rialto-signer //Millau.HeadersAndMessagesRelay \ + --rialto-messages-pallet-owner=//Millau.MessagesOwner \ + --rialto-transactions-mortality=64 \ + --lane=00000000 \ + --lane=73776170 \ + --prometheus-host=0.0.0.0 diff --git a/deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-millau-to-rialto-parachain-messages-dashboard.json b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-millau-to-rialto-parachain-messages-dashboard.json new file mode 100644 index 00000000000..b20348cc513 --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-millau-to-rialto-parachain-messages-dashboard.json @@ -0,0 +1,910 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Millau_to_RialtoParachain_MessageLane_00000000_best_target_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "instant": false, + "interval": "", + "legendFormat": "At RialtoParachain", + "refId": "A" + }, + { + "expr": "Millau_to_RialtoParachain_MessageLane_00000000_best_target_at_source_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "instant": false, + "interval": "", + "legendFormat": "At Millau", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized RialtoParachain headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Millau_to_RialtoParachain_MessageLane_00000000_best_source_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "At Millau", + "refId": "A" + }, + { + "expr": "Millau_to_RialtoParachain_MessageLane_00000000_best_source_at_target_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "At RialtoParachain", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized Millau headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Millau -> RialtoParachain messages are not detected by relay", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from RialtoParachain\", \"type\", \"source_latest_generated\"), \"type\", \"Latest RialtoParachain message received by Millau\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + }, + { + "expr": "increase(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_generated\"}[10m]) OR on() vector(0)", + "hide": true, + "interval": "", + "legendFormat": "Messages generated in last 5 minutes (Millau -> RialtoParachain, 00000000)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest message confirmed by RialtoParachain to Millau\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest RialtoParachain message received by Millau\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "sum" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "7m", + "frequency": "1m", + "handler": 1, + "name": "Messages from Millau to RialtoParachain are not being delivered", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 20 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_generated\"}[2m])) - scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]))", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "Undelivered messages at RialtoParachain", + "refId": "A" + }, + { + "expr": "increase(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[5m]) OR on() vector(0)", + "interval": "", + "legendFormat": "Millau Messages delivered to RialtoParachain in last 5m", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 50 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Too many unconfirmed messages (Millau -> RialtoParachain)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 20 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "legendFormat": "Unconfirmed messages at Millau", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Rewards are not being confirmed (Millau -> RialtoParachain messages)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 20 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_confirmed\"}[2m])) - scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_confirmed\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at RialtoParachain", + "refId": "A" + }, + { + "expr": "(scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]) > bool min_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at Millau->RaltoParachain (zero if messages are not being delivered to RialtoParachain)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Reward lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Millau to RialtoParachain Message Sync Dashboard", + "uid": "C61e-797z", + "version": 1 +} diff --git a/deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-rialto-parachain-to-millau-messages-dashboard.json b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-rialto-parachain-to-millau-messages-dashboard.json new file mode 100644 index 00000000000..064436145de --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-rialto-parachain-to-millau-messages-dashboard.json @@ -0,0 +1,908 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "RialtoParachain_to_Millau_MessageLane_00000000_best_target_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "instant": false, + "interval": "", + "legendFormat": "At Millau", + "refId": "A" + }, + { + "expr": "RialtoParachain_to_Millau_MessageLane_00000000_best_target_at_source_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "instant": false, + "interval": "", + "legendFormat": "At RialtoParachain", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized Millau headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "RialtoParachain_to_Millau_MessageLane_00000000_best_source_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "At RialtoParachain", + "refId": "A" + }, + { + "expr": "RialtoParachain_to_Millau_MessageLane_00000000_best_source_at_target_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "At Millau", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized RialtoParachain headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "RialtoParachain -> Millau messages are not detected by relay", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from Millau\", \"type\", \"source_latest_generated\"), \"type\", \"Latest Millau message received by RialtoParachain\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + }, + { + "expr": "increase(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_generated\"}[10m]) OR on() vector(0)", + "hide": true, + "interval": "", + "legendFormat": "Messages generated in last 5 minutes (RialtoParachain -> Millau, 00000000)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest message confirmed by Millau to RialtoParachain\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest Millau message received by RialtoParachain\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "sum" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "7m", + "frequency": "1m", + "handler": 1, + "name": "Messages from RialtoParachain to Millau are not being delivered", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 20 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_generated\"}[2m])) - scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]))", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "Undelivered messages at Millau", + "refId": "A" + }, + { + "expr": "increase(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[5m]) OR on() vector(0)", + "interval": "", + "legendFormat": "RialtoParachain Messages delivered to Millau in last 5m", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 50 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Too many unconfirmed messages (RialtoParachain -> Millau)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 20 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "legendFormat": "Unconfirmed messages at RialtoParachain", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Rewards are not being confirmed (RialtoParachain -> Millau messages)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 20 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_confirmed\"}[2m])) - scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_confirmed\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at Millau", + "refId": "A" + }, + { + "expr": "(scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]) > bool min_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at Millau (zero if messages are not being delivered to Millau)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Reward lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "RialtoParachain to Millau Message Sync Dashboard", + "uid": "-l61a7r7k", + "version": 1 +} diff --git a/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json new file mode 100644 index 00000000000..c125b08df32 --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json @@ -0,0 +1,713 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "C", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "At-Rialto relay balances are too low", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "at_RialtoParachain_relay_MillauHeaders_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "With-Millau headers relay account balance", + "refId": "A" + }, + { + "expr": "at_RialtoParachain_relay_MillauMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "With-Millau messages relay account balance", + "refId": "B" + }, + { + "expr": "at_RialtoParachain_relay_MillauMessagesPalletOwner_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "With-Millau messages pallet owner account balance", + "refId": "C" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1000 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "RialtoParachain relay balances", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "C", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "At-Millau relay balances are too low", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "at_Millau_relay_RialtoHeaders_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "With-Rialto headers relay account balance", + "refId": "A" + }, + { + "expr": "at_Millau_relay_RialtoParachainMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "With-RialtoParachain messages relay account balance", + "refId": "B" + }, + { + "expr": "at_Millau_relay_RialtoParachains_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "With-Rialto parachains relay account balance", + "refId": "C" + }, + { + "expr": "at_Millau_relay_RialtoParachainMessagesPalletOwner_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "With-RialtoParachain messages pallet owner account balance", + "refId": "D" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1000 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Millau relay balances", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Whether with-RialtoParachain-parachains-pallet and Rialto itself are on different forks alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "RialtoParachain_to_Millau_MessageLane_00000000_is_source_and_source_at_target_using_different_forks{instance=\"relay-millau-rialto-parachain-1:9616\"} OR on() vector(0)", + "instant": false, + "interval": "", + "legendFormat": "On different forks?", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Whether with-RialtoParachain-parachains-pallet and RialtoParachain itself are on different forks", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Whether with-Millau-grandpa-pallet and Millau itself are on different forks", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "hiddenSeries": false, + "id": 16, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Millau_to_RialtoParachain_MessageLane_00000000_is_source_and_source_at_target_using_different_forks{instance=\"relay-millau-rialto-parachain-1:9616\"} OR on() vector(0)", + "interval": "", + "legendFormat": "On different forks?", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Whether with-Millau-grandpa-pallet and Millau itself are on different forks", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "RialtoParachain+Millau maintenance dashboard", + "uid": "WALc3ajnk", + "version": 1 +} diff --git a/deployments/bridges/rialto-parachain-millau/dashboard/prometheus/targets.yml b/deployments/bridges/rialto-parachain-millau/dashboard/prometheus/targets.yml new file mode 100644 index 00000000000..ba683e3479a --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/dashboard/prometheus/targets.yml @@ -0,0 +1,3 @@ +- targets: + - relay-millau-rialto-parachain-1:9616 + - relay-millau-rialto-parachain-2:9616 diff --git a/deployments/bridges/rialto-parachain-millau/docker-compose.yml b/deployments/bridges/rialto-parachain-millau/docker-compose.yml new file mode 100644 index 00000000000..a62bda52eb3 --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/docker-compose.yml @@ -0,0 +1,90 @@ +# Exposed ports: 10816, 10916, 11016, 11017, 11018 + +version: '3.5' +services: + # We provide overrides for these particular nodes since they are public facing + # nodes which we use to connect from things like Polkadot JS Apps. + rialto-parachain-collator-charlie: + environment: + VIRTUAL_HOST: wss.rialto-parachain.brucke.link + VIRTUAL_PORT: 9944 + LETSENCRYPT_HOST: wss.rialto-parachain.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + + millau-node-charlie: + environment: + VIRTUAL_HOST: wss.millau.brucke.link + VIRTUAL_PORT: 9944 + LETSENCRYPT_HOST: wss.millau.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + + relay-millau-rialto-parachain-1: &sub-bridge-relay + image: ${SUBSTRATE_RELAY_IMAGE:-paritytech/substrate-relay} + entrypoint: /entrypoints/relay-millau-rialto-parachain-entrypoint.sh + volumes: + - ./bridges/common:/common + - ./bridges/rialto-parachain-millau/entrypoints:/entrypoints + environment: + RUST_LOG: rpc=trace,bridge=trace + ports: + - "10816:9616" + depends_on: &all-nodes + - millau-node-alice + - millau-node-bob + - millau-node-charlie + - millau-node-dave + - millau-node-eve + - rialto-parachain-collator-alice + - rialto-parachain-collator-bob + - rialto-parachain-collator-charlie + + relay-millau-rialto-parachain-2: + <<: *sub-bridge-relay + environment: + RUST_LOG: rpc=trace,bridge=trace + EXT_MILLAU_RELAY_ACCOUNT: //RialtoParachain.HeadersAndMessagesRelay2 + EXT_MILLAU_RELAY_ACCOUNT_HEADERS_OVERRIDE: //RialtoParachain.RialtoHeadersRelay2 + EXT_RIALTO_PARACHAIN_RELAY_ACCOUNT: //Millau.HeadersAndMessagesRelay2 + ports: + - "10916:9616" + relay-messages-millau-to-rialto-parachain-generator: + <<: *sub-bridge-relay + ports: + - "11016:9616" + entrypoint: /entrypoints/relay-messages-to-rialto-parachain-generator-entrypoint.sh + depends_on: + - relay-millau-rialto-parachain-1 + + relay-messages-rialto-parachain-to-millau-generator: + <<: *sub-bridge-relay + entrypoint: /entrypoints/relay-messages-to-millau-generator-entrypoint.sh + ports: + - "11017:9616" + depends_on: + - relay-millau-rialto-parachain-1 + + relay-messages-millau-to-rialto-parachain-resubmitter: + <<: *sub-bridge-relay + environment: + RUST_LOG: bridge=trace + entrypoint: /entrypoints/relay-messages-to-rialto-parachain-resubmitter-entrypoint.sh + ports: + - "11018:9616" + depends_on: + - relay-messages-millau-to-rialto-parachain-generator + + # Note: These are being overridden from the top level `monitoring` compose file. + grafana-dashboard: + environment: + VIRTUAL_HOST: grafana.millau.brucke.link,grafana.rialto.brucke.link + VIRTUAL_PORT: 3000 + LETSENCRYPT_HOST: grafana.millau.brucke.link,grafana.rialto.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + volumes: + - ./bridges/rialto-parachain-millau/dashboard/grafana:/etc/grafana/dashboards/rialto-parachain-millau:ro + - ./networks/dashboard/grafana/beefy-dashboard.json:/etc/grafana/dashboards/beefy.json + + prometheus-metrics: + volumes: + - ./bridges/rialto-parachain-millau/dashboard/prometheus/targets.yml:/etc/prometheus/targets-rialto-parachain-millau.yml + depends_on: *all-nodes diff --git a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh new file mode 100755 index 00000000000..cf0c910a05b --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# THIS SCRIPT IS NOT INTENDED FOR USE IN PRODUCTION ENVIRONMENT +# +# This scripts periodically calls the Substrate relay binary to generate messages. These messages +# are sent from the Rialto network to the Millau network. + +set -eu + +# Max delay before submitting transactions (s) +MAX_SUBMIT_DELAY_S=${MSG_EXCHANGE_GEN_MAX_SUBMIT_DELAY_S:-30} +MESSAGE_LANE=${MSG_EXCHANGE_GEN_LANE:-00000000} +MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE=1024 + +SHARED_CMD="/home/user/substrate-relay send-message rialto-parachain-to-millau" +SHARED_HOST="--source-host rialto-parachain-collator-bob --source-port 9944" +SOURCE_SIGNER="--source-signer //Millau.MessagesSender" + +SEND_MESSAGE="$SHARED_CMD $SHARED_HOST $SOURCE_SIGNER" + +SOURCE_CHAIN="RialtoParachain" +TARGET_CHAIN="Millau" +EXTRA_ARGS="" +REGULAR_PAYLOAD="020419ac" +BATCH_PAYLOAD="010109020419A8" + +source /common/generate_messages.sh diff --git a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-generator-entrypoint.sh b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-generator-entrypoint.sh new file mode 100755 index 00000000000..7917f7739d5 --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-generator-entrypoint.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# THIS SCRIPT IS NOT INTENDED FOR USE IN PRODUCTION ENVIRONMENT +# +# This scripts periodically calls the Substrate relay binary to generate messages. These messages +# are sent from the Millau network to the Rialto network. + +set -eu + +# Max delay before submitting transactions (s) +MAX_SUBMIT_DELAY_S=${MSG_EXCHANGE_GEN_MAX_SUBMIT_DELAY_S:-30} +MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE=1024 + +SHARED_CMD=" /home/user/substrate-relay send-message millau-to-rialto-parachain" +SHARED_HOST="--source-host millau-node-bob --source-port 9944" +SOURCE_SIGNER="--source-signer //RialtoParachain.MessagesSender" + +SEND_MESSAGE="$SHARED_CMD $SHARED_HOST $SOURCE_SIGNER" + +SOURCE_CHAIN="Millau" +TARGET_CHAIN="RialtoParachain" +EXTRA_ARGS="" +REGULAR_PAYLOAD="020419ac" +BATCH_PAYLOAD="010109020419A8" + +source /common/generate_messages.sh diff --git a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-resubmitter-entrypoint.sh b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-resubmitter-entrypoint.sh new file mode 100755 index 00000000000..cf4c4612d69 --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-resubmitter-entrypoint.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -xeu + +sleep 15 + +# //RialtoParachain.MessagesSender is signing Millau -> RialtoParachain message-send transactions, which are causing problems. +# +# When large message is being sent from Millau to RialtoParachain AND other transactions are +# blocking it from being mined, we'll see something like this in logs: +# +# Millau transaction priority with tip=0: 17800827994. Target priority: +# 526186677695 +# +# So since fee multiplier in Millau is `1` and `WeightToFee` is `IdentityFee`, then +# we need tip around `526186677695 - 17800827994 = 508_385_849_701`. Let's round it +# up to `1_000_000_000_000`. + +/home/user/substrate-relay resubmit-transactions millau \ + --target-host millau-node-alice \ + --target-port 9944 \ + --target-signer //RialtoParachain.MessagesSender \ + --stalled-blocks 7 \ + --tip-limit 1000000000000 \ + --tip-step 1000000000 \ + make-it-best-transaction diff --git a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-millau-rialto-parachain-entrypoint.sh b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-millau-rialto-parachain-entrypoint.sh new file mode 100755 index 00000000000..f2732e82f49 --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-millau-rialto-parachain-entrypoint.sh @@ -0,0 +1,42 @@ +#!/bin/bash +set -xeu + +sleep 15 + +MILLAU_RELAY_ACCOUNT=${EXT_MILLAU_RELAY_ACCOUNT:-//RialtoParachain.HeadersAndMessagesRelay1} +MILLAU_RELAY_ACCOUNT_HEADERS_OVERRIDE=${EXT_MILLAU_RELAY_ACCOUNT_HEADERS_OVERRIDE:-//RialtoParachain.RialtoHeadersRelay1} +RIALTO_PARACHAIN_RELAY_ACCOUNT=${EXT_RIALTO_PARACHAIN_RELAY_ACCOUNT:-//Millau.HeadersAndMessagesRelay1} + +/home/user/substrate-relay init-bridge millau-to-rialto-parachain \ + --source-host millau-node-alice \ + --source-port 9944 \ + --target-host rialto-parachain-collator-alice \ + --target-port 9944 \ + --target-signer //Sudo + +/home/user/substrate-relay init-bridge rialto-to-millau \ + --source-host rialto-node-alice \ + --source-port 9944 \ + --target-host millau-node-alice \ + --target-port 9944 \ + --target-signer //Sudo + +# Give chain a little bit of time to process initialization transaction +sleep 6 + +/home/user/substrate-relay relay-headers-and-messages millau-rialto-parachain \ + --millau-host millau-node-alice \ + --millau-port 9944 \ + --millau-signer $MILLAU_RELAY_ACCOUNT \ + --rialto-headers-to-millau-signer $MILLAU_RELAY_ACCOUNT_HEADERS_OVERRIDE \ + --millau-messages-pallet-owner=//RialtoParachain.MessagesOwner \ + --millau-transactions-mortality=64 \ + --rialto-parachain-host rialto-parachain-collator-charlie \ + --rialto-parachain-port 9944 \ + --rialto-parachain-signer $RIALTO_PARACHAIN_RELAY_ACCOUNT \ + --rialto-parachain-messages-pallet-owner=//Millau.MessagesOwner \ + --rialto-parachain-transactions-mortality=64 \ + --rialto-host rialto-node-alice \ + --rialto-port 9944 \ + --lane=00000000 \ + --prometheus-host=0.0.0.0 diff --git a/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-headers-dashboard.json b/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-headers-dashboard.json new file mode 100644 index 00000000000..8f740d2ba5e --- /dev/null +++ b/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-headers-dashboard.json @@ -0,0 +1,781 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 32 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "60m", + "frequency": "5m", + "handler": 1, + "message": "", + "name": "Synced Header Difference is Over 32 (Westend to Millau)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "Shows how many headers behind the target chain is from the source chain.", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "max(Westend_to_Millau_Sync_best_source_block_number) - max(Westend_to_Millau_Sync_best_source_at_target_block_number)", + "format": "table", + "instant": false, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 5 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Difference Between Source and Target Headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 32 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "2m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "60m", + "frequency": "5m", + "handler": 1, + "name": "No New Headers (Westend to Millau)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "How many headers has the relay synced from the source node in the last 2 mins?", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 16, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "max_over_time(Westend_to_Millau_Sync_best_source_block_number[10m])-min_over_time(Westend_to_Millau_Sync_best_source_block_number[10m])", + "interval": "", + "legendFormat": "Number of new Headers on Westend (Last 10 Mins)", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 5 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Headers Synced on Millau (Last 2 Mins)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": { + "align": null + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 2, + "interval": "5s", + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.1.3", + "targets": [ + { + "expr": "Westend_to_Millau_Sync_best_source_block_number", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "Best Known Westend Header at Westend", + "refId": "A" + }, + { + "expr": "Westend_to_Millau_Sync_best_source_at_target_block_number", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "Best Known Westend Header at Millau", + "refId": "B" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Best Blocks according to Relay", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 8 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "system_average_load{instance='relay-headers-westend-to-millau-1:9616'}", + "interval": "", + "legendFormat": "Average system load in last {{over}}", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": null + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Average System Load", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 8 + }, + "id": 12, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "7.1.3", + "targets": [ + { + "expr": "avg_over_time(process_cpu_usage_percentage{instance='relay-headers-westend-to-millau-1:9616'}[1m])", + "instant": true, + "interval": "", + "legendFormat": "1 CPU = 100", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Relay Process CPU Usage ", + "type": "gauge" + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Whether with-Westend-grandpa-pallet and Westend itself are on different forks alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 14 + }, + "hiddenSeries": false, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Westend_to_Millau_Sync_is_source_and_source_at_target_using_different_forks", + "interval": "", + "legendFormat": "On different forks?", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Whether with-Westend-grandpa-pallet and Westend itself are on different forks", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "process_memory_usage_bytes{instance='relay-headers-westend-to-millau-1:9616'} / 1024 / 1024", + "interval": "", + "legendFormat": "Process memory, MB", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Usage for Relay Process", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Westend to Millau Header Sync Dashboard", + "uid": "relay-westend-to-millau-headers", + "version": 1 +} diff --git a/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-parachains-dashboard.json b/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-parachains-dashboard.json new file mode 100644 index 00000000000..007b6ef9325 --- /dev/null +++ b/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-parachains-dashboard.json @@ -0,0 +1,200 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 9, + "links": [], + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 32 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Too many Westmint headers are missing at Millau", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Westend_to_Millau_Parachains_best_parachain_block_number_at_source{parachain='para_2000'}", + "interval": "", + "legendFormat": "At Westend", + "refId": "A" + }, + { + "expr": "Westend_to_Millau_Parachains_best_parachain_block_number_at_target{parachain='para_2000'}", + "interval": "", + "legendFormat": "At Millau", + "refId": "B" + }, + { + "expr": "Westend_to_Millau_Parachains_best_parachain_block_number_at_source{parachain='para_2000'} - Westend_to_Millau_Parachains_best_parachain_block_number_at_target{parachain='para_2000'}", + "hide": true, + "interval": "", + "legendFormat": "Missing Westmint headers at Millau", + "refId": "C" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 32 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Westmint headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Westend parachains at Millau", + "uid": "vUMhOlq7k", + "version": 1 + } + \ No newline at end of file diff --git a/deployments/bridges/westend-millau/dashboard/prometheus/targets.yml b/deployments/bridges/westend-millau/dashboard/prometheus/targets.yml new file mode 100644 index 00000000000..aed2d494582 --- /dev/null +++ b/deployments/bridges/westend-millau/dashboard/prometheus/targets.yml @@ -0,0 +1,3 @@ +- targets: + - relay-headers-westend-to-millau-1:9616 + - relay-parachains-westend-to-millau-1:9616 \ No newline at end of file diff --git a/deployments/bridges/westend-millau/docker-compose.yml b/deployments/bridges/westend-millau/docker-compose.yml new file mode 100644 index 00000000000..dfb6ebb0c64 --- /dev/null +++ b/deployments/bridges/westend-millau/docker-compose.yml @@ -0,0 +1,72 @@ +# Exposed ports: 10616, 10617, 10618, 10619 + +version: '3.5' +services: + relay-headers-westend-to-millau-1: + image: ${SUBSTRATE_RELAY_IMAGE:-paritytech/substrate-relay} + entrypoint: /entrypoints/relay-headers-westend-to-millau-entrypoint.sh + volumes: + - ./bridges/westend-millau/entrypoints:/entrypoints + environment: + RUST_LOG: rpc=trace,bridge=trace + ports: + - "10616:9616" + depends_on: + - millau-node-alice + + relay-headers-westend-to-millau-2: + image: ${SUBSTRATE_RELAY_IMAGE:-paritytech/substrate-relay} + entrypoint: /entrypoints/relay-headers-westend-to-millau-entrypoint.sh + volumes: + - ./bridges/westend-millau/entrypoints:/entrypoints + environment: + RUST_LOG: rpc=trace,bridge=trace + EXT_RELAY_ACCOUNT: //Westend.HeadersRelay2 + ports: + - "10617:9616" + depends_on: + - millau-node-alice + + relay-parachains-westend-to-millau-1: + image: ${SUBSTRATE_RELAY_IMAGE:-paritytech/substrate-relay} + entrypoint: /entrypoints/relay-parachains-westend-to-millau-entrypoint.sh + volumes: + - ./bridges/westend-millau/entrypoints:/entrypoints + environment: + RUST_LOG: rpc=trace,bridge=trace + ports: + - "10618:9616" + depends_on: + - millau-node-alice + + relay-parachains-westend-to-millau-2: + image: ${SUBSTRATE_RELAY_IMAGE:-paritytech/substrate-relay} + entrypoint: /entrypoints/relay-parachains-westend-to-millau-entrypoint.sh + volumes: + - ./bridges/westend-millau/entrypoints:/entrypoints + environment: + RUST_LOG: rpc=trace,bridge=trace + EXT_RELAY_ACCOUNT: //Westend.WestmintHeaders2 + ports: + - "10619:9616" + depends_on: + - millau-node-alice + + # Note: These are being overridden from the top level `monitoring` compose file. + grafana-dashboard: + environment: + VIRTUAL_HOST: grafana.millau.brucke.link,grafana.rialto.brucke.link + VIRTUAL_PORT: 3000 + LETSENCRYPT_HOST: grafana.millau.brucke.link,grafana.rialto.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + volumes: + - ./bridges/westend-millau/dashboard/grafana:/etc/grafana/dashboards/westend-millau:ro + + prometheus-metrics: + volumes: + - ./bridges/westend-millau/dashboard/prometheus/targets.yml:/etc/prometheus/targets-westend-millau.yml + depends_on: + - relay-headers-westend-to-millau-1 + - relay-headers-westend-to-millau-2 + - relay-parachains-westend-to-millau-1 + - relay-parachains-westend-to-millau-2 diff --git a/deployments/bridges/westend-millau/entrypoints/relay-headers-westend-to-millau-entrypoint.sh b/deployments/bridges/westend-millau/entrypoints/relay-headers-westend-to-millau-entrypoint.sh new file mode 100755 index 00000000000..8548e9f5a41 --- /dev/null +++ b/deployments/bridges/westend-millau/entrypoints/relay-headers-westend-to-millau-entrypoint.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -xeu + +sleep 15 + +RELAY_ACCOUNT=${EXT_RELAY_ACCOUNT:-//Westend.HeadersRelay1} + +/home/user/substrate-relay init-bridge westend-to-millau \ + --source-host westend-rpc.polkadot.io \ + --source-port 443 \ + --source-secure \ + --target-host millau-node-alice \ + --target-port 9944 \ + --target-signer //Westend.GrandpaOwner + +# Give chain a little bit of time to process initialization transaction +sleep 6 +/home/user/substrate-relay relay-headers westend-to-millau \ + --source-host westend-rpc.polkadot.io \ + --source-port 443 \ + --source-secure \ + --target-host millau-node-alice \ + --target-port 9944 \ + --target-signer $RELAY_ACCOUNT \ + --target-transactions-mortality=4\ + --prometheus-host=0.0.0.0 diff --git a/deployments/bridges/westend-millau/entrypoints/relay-parachains-westend-to-millau-entrypoint.sh b/deployments/bridges/westend-millau/entrypoints/relay-parachains-westend-to-millau-entrypoint.sh new file mode 100755 index 00000000000..fc7ccb3584f --- /dev/null +++ b/deployments/bridges/westend-millau/entrypoints/relay-parachains-westend-to-millau-entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -xeu + +sleep 15 + +RELAY_ACCOUNT=${EXT_RELAY_ACCOUNT:-//Westend.WestmintHeaders1} + +/home/user/substrate-relay relay-parachains westend-to-millau \ + --source-host westend-rpc.polkadot.io \ + --source-port 443 \ + --source-secure \ + --target-host millau-node-alice \ + --target-port 9944 \ + --target-signer $RELAY_ACCOUNT \ + --target-transactions-mortality=4\ + --prometheus-host=0.0.0.0 diff --git a/deployments/local-scripts/bridge-entrypoint.sh b/deployments/local-scripts/bridge-entrypoint.sh new file mode 100755 index 00000000000..5c1b6e90ec2 --- /dev/null +++ b/deployments/local-scripts/bridge-entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -xeu + +# This will allow us to run whichever binary the user wanted +# with arguments passed through `docker run` +# e.g `docker run -it rialto-bridge-node-dev --dev --tmp` +/home/user/$PROJECT $@ diff --git a/deployments/local-scripts/relay-messages-millau-to-rialto.sh b/deployments/local-scripts/relay-messages-millau-to-rialto.sh new file mode 100755 index 00000000000..d420dc56c26 --- /dev/null +++ b/deployments/local-scripts/relay-messages-millau-to-rialto.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# A script for relaying Millau messages to the Rialto chain. +# +# Will not work unless both the Rialto and Millau are running (see `run-rialto-node.sh` +# and `run-millau-node.sh). +set -xeu + +MILLAU_PORT="${MILLAU_PORT:-9945}" +RIALTO_PORT="${RIALTO_PORT:-9944}" + +RUST_LOG=bridge=debug \ +./target/debug/substrate-relay relay-messages millau-to-rialto \ + --lane 00000000 \ + --source-host localhost \ + --source-port $MILLAU_PORT \ + --source-signer //Bob \ + --target-host localhost \ + --target-port $RIALTO_PORT \ + --target-signer //Bob \ + --prometheus-host=0.0.0.0 diff --git a/deployments/local-scripts/relay-messages-rialto-to-millau.sh b/deployments/local-scripts/relay-messages-rialto-to-millau.sh new file mode 100755 index 00000000000..0cd73c00454 --- /dev/null +++ b/deployments/local-scripts/relay-messages-rialto-to-millau.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# A script for relaying Rialto messages to the Millau chain. +# +# Will not work unless both the Rialto and Millau are running (see `run-rialto-node.sh` +# and `run-millau-node.sh). +set -xeu + +MILLAU_PORT="${MILLAU_PORT:-9945}" +RIALTO_PORT="${RIALTO_PORT:-9944}" + +RUST_LOG=bridge=debug \ +./target/debug/substrate-relay relay-messages rialto-to-millau \ + --lane 00000000 \ + --source-host localhost \ + --source-port $RIALTO_PORT \ + --source-signer //Bob \ + --target-host localhost \ + --target-port $MILLAU_PORT \ + --target-signer //Bob \ + --prometheus-host=0.0.0.0 diff --git a/deployments/local-scripts/relay-millau-to-rialto.sh b/deployments/local-scripts/relay-millau-to-rialto.sh new file mode 100755 index 00000000000..4758173f72f --- /dev/null +++ b/deployments/local-scripts/relay-millau-to-rialto.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# A script for relaying Millau headers to the Rialto chain. +# +# Will not work unless both the Rialto and Millau are running (see `run-rialto-node.sh` +# and `run-millau-node.sh). + +MILLAU_PORT="${MILLAU_PORT:-9945}" +RIALTO_PORT="${RIALTO_PORT:-9944}" + +RUST_LOG=bridge=debug \ +./target/debug/substrate-relay init-bridge millau-to-rialto \ + --source-host localhost \ + --source-port $MILLAU_PORT \ + --target-host localhost \ + --target-port $RIALTO_PORT \ + --target-signer //Sudo \ + --source-version-mode Bundle \ + --target-version-mode Bundle + +sleep 5 +RUST_LOG=bridge=debug \ +./target/debug/substrate-relay relay-headers millau-to-rialto \ + --source-host localhost \ + --source-port $MILLAU_PORT \ + --target-host localhost \ + --target-port $RIALTO_PORT \ + --target-signer //Alice \ + --prometheus-host=0.0.0.0 diff --git a/deployments/local-scripts/relay-rialto-to-millau.sh b/deployments/local-scripts/relay-rialto-to-millau.sh new file mode 100755 index 00000000000..5fd27840214 --- /dev/null +++ b/deployments/local-scripts/relay-rialto-to-millau.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# A script for relaying Rialto headers to the Millau chain. +# +# Will not work unless both the Rialto and Millau are running (see `run-rialto-node.sh` +# and `run-millau-node.sh). + +MILLAU_PORT="${MILLAU_PORT:-9945}" +RIALTO_PORT="${RIALTO_PORT:-9944}" + +RUST_LOG=bridge=debug \ +./target/debug/substrate-relay init-bridge rialto-to-millau \ + --target-host localhost \ + --target-port $MILLAU_PORT \ + --source-host localhost \ + --source-port $RIALTO_PORT \ + --target-signer //Sudo \ + +sleep 5 +RUST_LOG=bridge=debug \ +./target/debug/substrate-relay relay-headers rialto-to-millau \ + --target-host localhost \ + --target-port $MILLAU_PORT \ + --source-host localhost \ + --source-port $RIALTO_PORT \ + --target-signer //Alice \ + --prometheus-host=0.0.0.0 diff --git a/deployments/local-scripts/run-millau-node.sh b/deployments/local-scripts/run-millau-node.sh new file mode 100755 index 00000000000..916f876c536 --- /dev/null +++ b/deployments/local-scripts/run-millau-node.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Run a development instance of the Millau Substrate bridge node. +# To override the default port just export MILLAU_PORT=9945 + +MILLAU_PORT="${MILLAU_PORT:-9945}" + +RUST_LOG=runtime=trace \ +./target/debug/millau-bridge-node --dev --tmp \ + --rpc-cors=all --unsafe-rpc-external --unsafe-ws-external \ + --port 33044 --rpc-port 9934 --ws-port $MILLAU_PORT \ diff --git a/deployments/local-scripts/run-rialto-node.sh b/deployments/local-scripts/run-rialto-node.sh new file mode 100755 index 00000000000..e7987e2af36 --- /dev/null +++ b/deployments/local-scripts/run-rialto-node.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Run a development instance of the Rialto Substrate bridge node. +# To override the default port just export RIALTO_PORT=9944 + +RIALTO_PORT="${RIALTO_PORT:-9944}" + +RUST_LOG=runtime=trace \ + ./target/debug/rialto-bridge-node --dev --tmp \ + --rpc-cors=all --unsafe-rpc-external --unsafe-ws-external \ + --port 33033 --rpc-port 9933 --ws-port $RIALTO_PORT \ diff --git a/deployments/local-scripts/run-westend-node.sh b/deployments/local-scripts/run-westend-node.sh new file mode 100755 index 00000000000..1bb490fc1a8 --- /dev/null +++ b/deployments/local-scripts/run-westend-node.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Run a development instance of the Westend Substrate bridge node. +# To override the default port just export WESTEND_PORT=9945 +# +# Note: This script will not work out of the box with the bridges +# repo since it relies on a Polkadot binary. + +WESTEND_PORT="${WESTEND_PORT:-9944}" + +RUST_LOG=runtime=trace,runtime::bridge=trace \ +./target/debug/polkadot --chain=westend-dev --alice --tmp \ + --rpc-cors=all --unsafe-rpc-external --unsafe-ws-external \ + --port 33033 --rpc-port 9933 --ws-port $WESTEND_PORT \ diff --git a/deployments/monitoring/GrafanaMatrix.Dockerfile b/deployments/monitoring/GrafanaMatrix.Dockerfile new file mode 100644 index 00000000000..be1c80d4059 --- /dev/null +++ b/deployments/monitoring/GrafanaMatrix.Dockerfile @@ -0,0 +1,15 @@ +FROM ruby:alpine3.13 + +RUN apk add --no-cache git + +ENV APP_HOME /app +ENV RACK_ENV production +RUN mkdir $APP_HOME +WORKDIR $APP_HOME + +RUN git clone https://github.com/ananace/ruby-grafana-matrix.git $APP_HOME +RUN bundle install --without development + +RUN mkdir /config && touch /config/config.yml && ln -s /config/config.yml ./config.yml + +CMD ["bundle", "exec", "rackup", "-p4567"] diff --git a/deployments/monitoring/disabled.yml b/deployments/monitoring/disabled.yml new file mode 100644 index 00000000000..a0b4ed3aad0 --- /dev/null +++ b/deployments/monitoring/disabled.yml @@ -0,0 +1,15 @@ +# A disabled version of monitoring. +# +# We replace each service with a no-op container. We can't simply not include this file, +# cause the bridge-specific compose files might have overrides. +version: '3.5' +services: + prometheus-metrics: + image: alpine + + grafana-dashboard: + image: alpine + + grafana-matrix-notifier: + image: alpine + diff --git a/deployments/monitoring/docker-compose.yml b/deployments/monitoring/docker-compose.yml new file mode 100644 index 00000000000..1fa50c68d0e --- /dev/null +++ b/deployments/monitoring/docker-compose.yml @@ -0,0 +1,34 @@ +version: '3.5' +services: + prometheus-metrics: + image: prom/prometheus:v2.38.0 + volumes: + - ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml + ports: + - "9090:9090" + + grafana-dashboard: + image: grafana/grafana:8.2.6 + environment: + GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASS:-admin} + GF_SERVER_ROOT_URL: ${GRAFANA_SERVER_ROOT_URL} + GF_SERVER_DOMAIN: ${GRAFANA_SERVER_DOMAIN} + volumes: + - ./monitoring/grafana/provisioning/:/etc/grafana/provisioning/:ro + - ./monitoring/grafana/dashboards/:/etc/grafana/dashboards/common:ro + ports: + - "3000:3000" + depends_on: + - prometheus-metrics + entrypoint: sh -c "echo 'sleeping for 10m' && sleep 600 && /run.sh" + + grafana-matrix-notifier: + build: + context: . + dockerfile: ./monitoring/GrafanaMatrix.Dockerfile + volumes: + - ./monitoring/grafana-matrix:/config + ports: + - "4567:4567" + depends_on: + - grafana-dashboard diff --git a/deployments/monitoring/grafana-matrix/config.yml b/deployments/monitoring/grafana-matrix/config.yml new file mode 100644 index 00000000000..123cf76a80b --- /dev/null +++ b/deployments/monitoring/grafana-matrix/config.yml @@ -0,0 +1,47 @@ +--- +# Webhook server configuration +# Or use the launch options `-o '::' -p 4567` + +# Set up your HS connections +matrix: +- name: matrix-parity-io + url: https://matrix.parity.io + # Create a user - log that user in using a post request + # curl -XPOST -d '{"type": "m.login.password", + # "user":"grafana", + # "password":"dummy-password"}' + # "https://my-matrix-server/_matrix/client/r0/login" + # Fill that access token in here + access_token: "" + #device_id: # Optional + +# The default message type for messages, should be either m.text or m.notice, +# defaults to m.text +msgtype: m.text + +# Set up notification ingress rules +rules: +- name: bridge # Name of the rule + room: "#bridges-rialto-millau-alerts:matrix.parity.io" # Room or ID + matrix: matrix-parity-io # The Matrix HS to use - defaults to first one + msgtype: m.notice + # The following values are optional: + image: true # Attach image to the notification? + embed_image: true # Upload and embed the image into the message? + #templates: + # Templates to use when rendering the notification, available placeholders: + # %TEMPLATES% - lib/grafana_matrix/templates + # $ - Environment variables + #html: "%TEMPLATES%/html.erb" # Path to HTML template + #plain: "%TEMPLATES%/plain.erb" # Path to plaintext template + #auth: + #user: example + #pass: any HTTP encodable string +#- name: other-hq +# room: "#hq:private.matrix.org +# matrix: matrix-priv + +# To use the webhook, you need to configure it into Grafana as: +# +# Url: http://:/hook?rule= +# Http Method: POST diff --git a/deployments/monitoring/grafana/dashboards/nodes.json b/deployments/monitoring/grafana/dashboards/nodes.json new file mode 100644 index 00000000000..db14c3cf1fa --- /dev/null +++ b/deployments/monitoring/grafana/dashboards/nodes.json @@ -0,0 +1,200 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "Running nodes", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "liveNow": false, + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "15m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "3m", + "frequency": "1m", + "handler": 1, + "name": "Nodes are not running", + "noDataState": "alerting", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "interval": null, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "maxDataPoints": null, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.2.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "up", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1, + "visible": true + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Running nodes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:111", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:112", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "", + "schemaVersion": 32, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Running nodes", + "uid": "DHl-xSW4z", + "version": 1 +} diff --git a/deployments/monitoring/grafana/provisioning/dashboards/grafana-dashboard.yaml b/deployments/monitoring/grafana/provisioning/dashboards/grafana-dashboard.yaml new file mode 100644 index 00000000000..d14ed2637d5 --- /dev/null +++ b/deployments/monitoring/grafana/provisioning/dashboards/grafana-dashboard.yaml @@ -0,0 +1,6 @@ +- name: 'default' + orgId: 1 + folder: '' + type: file + options: + path: '/etc/grafana/dashboards' \ No newline at end of file diff --git a/deployments/monitoring/grafana/provisioning/datasources/grafana-datasource.yaml b/deployments/monitoring/grafana/provisioning/datasources/grafana-datasource.yaml new file mode 100644 index 00000000000..b85cf06e2bd --- /dev/null +++ b/deployments/monitoring/grafana/provisioning/datasources/grafana-datasource.yaml @@ -0,0 +1,16 @@ +# list of datasources to insert/update depending +# whats available in the database +datasources: + # name of the datasource. Required +- name: Prometheus + # datasource type. Required + type: prometheus + # access mode. direct or proxy. Required + access: proxy + # org id. will default to orgId 1 if not specified + orgId: 1 + # url + url: http://prometheus-metrics:9090 + # mark as default datasource. Max one per org + isDefault: true + version: 1 diff --git a/deployments/monitoring/grafana/provisioning/notifiers/grafana-notifier.yaml b/deployments/monitoring/grafana/provisioning/notifiers/grafana-notifier.yaml new file mode 100644 index 00000000000..4eb6ea3863e --- /dev/null +++ b/deployments/monitoring/grafana/provisioning/notifiers/grafana-notifier.yaml @@ -0,0 +1,15 @@ +notifiers: + - name: Matrix + type: webhook + uid: notifier1 + is_default: true + send_reminder: true + frequency: 1h + disable_resolve_message: false + settings: + url: http://grafana-matrix-notifier:4567/hook?rule=bridge + http_method: POST + +delete_notifiers: + - name: Matrix + uid: notifier1 diff --git a/deployments/monitoring/prometheus/prometheus.yml b/deployments/monitoring/prometheus/prometheus.yml new file mode 100644 index 00000000000..7092bd27314 --- /dev/null +++ b/deployments/monitoring/prometheus/prometheus.yml @@ -0,0 +1,7 @@ +global: + scrape_interval: 15s +scrape_configs: + - job_name: dummy + file_sd_configs: + - files: + - /etc/prometheus/targets-*.yml diff --git a/deployments/networks/dashboard/grafana/beefy-dashboard.json b/deployments/networks/dashboard/grafana/beefy-dashboard.json new file mode 100644 index 00000000000..a5df3449e67 --- /dev/null +++ b/deployments/networks/dashboard/grafana/beefy-dashboard.json @@ -0,0 +1,585 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "D", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Beefy best blocks not advancing", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": { + "align": null + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 14, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "substrate_beefy_best_block{chain=\"rialto_local\"}", + "legendFormat": "Rialto(Charlie)", + "refId": "A" + }, + { + "expr": "substrate_beefy_best_block{chain=\"millau_local\"}", + "legendFormat": "Millau(Charlie)", + "refId": "B" + }, + { + "expr": "max_over_time(substrate_beefy_best_block{chain=\"millau_local\"}[5m]) - min_over_time(substrate_beefy_best_block{chain=\"millau_local\"}[5m])", + "hide": true, + "legendFormat": "Millau Best Beefy blocks count in last 5 minutes", + "refId": "C" + }, + { + "expr": "max_over_time(substrate_beefy_best_block{chain=\"rialto_local\"}[5m]) - min_over_time(substrate_beefy_best_block{chain=\"rialto_local\"}[5m])", + "hide": true, + "legendFormat": "Rialto Best Beefy blocks count in last 5 minutes", + "refId": "D" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Beefy Best block", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "yellow", + "value": null + }, + { + "color": "yellow", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 14, + "w": 11, + "x": 12, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.1.3", + "targets": [ + { + "expr": "substrate_beefy_should_vote_on{chain=\"rialto_local\"}", + "legendFormat": "Rialto(Charlie) Should-Vote-On", + "refId": "C" + }, + { + "expr": "substrate_beefy_round_concluded{chain=\"rialto_local\"}", + "legendFormat": "Rialto(Charlie) Round-Concluded", + "refId": "A" + }, + { + "expr": "substrate_beefy_should_vote_on{chain=\"millau_local\"}", + "legendFormat": "Millau(Charlie) Should-Vote-On", + "refId": "D" + }, + { + "expr": "substrate_beefy_round_concluded{chain=\"millau_local\"}", + "legendFormat": "Millau(Charlie) Round-Concluded", + "refId": "B" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Beefy Voting Rounds", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 18, + "x": 0, + "y": 14 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "substrate_beefy_votes_sent{chain=\"rialto_local\"}", + "legendFormat": "Rialto (node Charlie)", + "refId": "A" + }, + { + "expr": "substrate_beefy_votes_sent{chain=\"millau_local\"}", + "legendFormat": "Millau (node Charlie)", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Beefy Votes Sent", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Beefy Lagging Sessions alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": { + "align": null + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 5, + "x": 18, + "y": 14 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "substrate_beefy_lagging_sessions{chain=\"rialto_local\"}", + "legendFormat": "Rialto(Charlie)", + "refId": "A" + }, + { + "expr": "substrate_beefy_lagging_sessions{chain=\"millau_local\"}", + "legendFormat": "Millau(Charlie)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Beefy Lagging Sessions", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Beefy", + "uid": "j6cRDRh7z", + "version": 1 +} diff --git a/deployments/networks/dashboard/prometheus/millau-targets.yml b/deployments/networks/dashboard/prometheus/millau-targets.yml new file mode 100644 index 00000000000..c7a06509276 --- /dev/null +++ b/deployments/networks/dashboard/prometheus/millau-targets.yml @@ -0,0 +1,2 @@ +- targets: + - millau-node-charlie:9615 diff --git a/deployments/networks/dashboard/prometheus/rialto-targets.yml b/deployments/networks/dashboard/prometheus/rialto-targets.yml new file mode 100644 index 00000000000..9de26b9a2d7 --- /dev/null +++ b/deployments/networks/dashboard/prometheus/rialto-targets.yml @@ -0,0 +1,2 @@ +- targets: + - rialto-node-charlie:9615 diff --git a/deployments/networks/entrypoints/rialto-chainspec-exporter-entrypoint.sh b/deployments/networks/entrypoints/rialto-chainspec-exporter-entrypoint.sh new file mode 100755 index 00000000000..0898978096d --- /dev/null +++ b/deployments/networks/entrypoints/rialto-chainspec-exporter-entrypoint.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -xeu + +/home/user/rialto-bridge-node build-spec \ + --chain local \ + --raw \ + --disable-default-bootnode \ + > /rialto-share/rialto-relaychain-spec-raw.json + +# we're using local driver + tmpfs for shared `/rialto-share` volume, which is populated +# by the container running this script. If this script ends, the volume will be detached +# and our chain spec will be lost when it'll go online again. Hence the never-ending +# script which keeps volume online until container is stopped. +tail -f /dev/null diff --git a/deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh b/deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh new file mode 100755 index 00000000000..bb201ac5dad --- /dev/null +++ b/deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -xeu + +sleep 15 + +/home/user/substrate-relay register-parachain rialto-parachain \ + --parachain-host rialto-parachain-collator-alice \ + --parachain-port 9944 \ + --relaychain-host rialto-node-alice \ + --relaychain-port 9944 \ + --relaychain-signer //Sudo diff --git a/deployments/networks/millau.yml b/deployments/networks/millau.yml new file mode 100644 index 00000000000..6c57a3863ea --- /dev/null +++ b/deployments/networks/millau.yml @@ -0,0 +1,101 @@ +# Compose file for quickly spinning up a local instance of the Millau Substrate network. +# +# Note that the Millau network is only used for testing, so the configuration settings you see here +# are *not* recommended for a production environment. +# +# For example, do *not* keep your `node-key` in version control, and unless you're _really_ sure you +# want to provide public access to your nodes do *not* publicly expose RPC methods. +version: '3.5' +services: + millau-node-alice: &millau-bridge-node + image: ${MILLAU_BRIDGE_NODE_IMAGE:-paritytech/millau-bridge-node} + entrypoint: + - /home/user/millau-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/millau-node-bob/tcp/30333/p2p/12D3KooWM5LFR5ne4yTQ4sBSXJ75M4bDo2MAhAW2GhL3i8fe5aRb + - --alice + - --node-key=0f900c89f4e626f4a217302ab8c7d213737d00627115f318ad6fb169717ac8e0 + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + environment: + RUST_LOG: runtime=trace,rpc=debug,txpool=trace,runtime::bridge=trace,sc_basic_authorship=trace,beefy=debug + ports: + - "19933:9933" + - "19944:9944" + + millau-node-bob: + <<: *millau-bridge-node + entrypoint: + - /home/user/millau-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H + - --bob + - --node-key=db383639ff2905d79f8e936fd5dc4416ef46b514b2f83823ec3c42753d7557bb + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + ports: + - "20033:9933" + - "20044:9944" + + millau-node-charlie: + <<: *millau-bridge-node + entrypoint: + - /home/user/millau-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H + - --charlie + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + - --prometheus-external + ports: + - "20133:9933" + - "20144:9944" + - "20615:9615" + + millau-node-dave: + <<: *millau-bridge-node + entrypoint: + - /home/user/millau-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H + - --dave + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + ports: + - "20233:9933" + - "20244:9944" + + millau-node-eve: + <<: *millau-bridge-node + entrypoint: + - /home/user/millau-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H + - --eve + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + ports: + - "20333:9933" + - "20344:9944" + + # Note: These are being overridden from the top level `monitoring` compose file. + prometheus-metrics: + volumes: + - ./networks/dashboard/prometheus/millau-targets.yml:/etc/prometheus/targets-millau-nodes.yml + depends_on: + - millau-node-charlie diff --git a/deployments/networks/rialto-parachain.yml b/deployments/networks/rialto-parachain.yml new file mode 100644 index 00000000000..c6dab73ff72 --- /dev/null +++ b/deployments/networks/rialto-parachain.yml @@ -0,0 +1,90 @@ +# Compose file for quickly spinning up a local instance of the Rialto Parachain network. +# +# Since Rialto Parachain is unusable without Rialto, this file depends on some Rialto +# network nodes. +version: '3.5' +services: + rialto-parachain-collator-alice: &rialto-parachain-collator + image: ${RIALTO_PARACHAIN_COLLATOR_IMAGE:-paritytech/rialto-parachain-collator} + entrypoint: > + /home/user/rialto-parachain-collator + --alice + --collator + --force-authoring + --parachain-id 2000 + --rpc-port 9933 + --ws-port 9944 + --rpc-cors=all + --unsafe-rpc-external + --unsafe-ws-external + -- + --execution wasm + --chain /rialto-share/rialto-relaychain-spec-raw.json + --rpc-port 9934 + --ws-port 9945 + volumes: + - rialto-share:/rialto-share:z + environment: + RUST_LOG: runtime=trace,rpc=trace,txpool=trace,parachain=trace,parity_ws=trace,sc_basic_authorship=trace + depends_on: + - rialto-chainspec-exporter + ports: + - "20433:9933" + - "20444:9944" + + rialto-parachain-collator-bob: + <<: *rialto-parachain-collator + entrypoint: > + /home/user/rialto-parachain-collator + --bob + --collator + --force-authoring + --parachain-id 2000 + --rpc-port 9933 + --ws-port 9944 + --rpc-cors=all + --unsafe-rpc-external + --unsafe-ws-external + -- + --execution wasm + --chain /rialto-share/rialto-relaychain-spec-raw.json + --rpc-port 9934 + --ws-port 9945 + ports: + - "20533:9933" + - "20544:9944" + + rialto-parachain-collator-charlie: + <<: *rialto-parachain-collator + entrypoint: > + /home/user/rialto-parachain-collator + --charlie + --collator + --force-authoring + --parachain-id 2000 + --rpc-port 9933 + --ws-port 9944 + --rpc-cors=all + --unsafe-rpc-external + --unsafe-ws-external + -- + --execution wasm + --chain /rialto-share/rialto-relaychain-spec-raw.json + --rpc-port 9934 + --ws-port 9945 + ports: + - "20633:9933" + - "20644:9944" + + rialto-parachain-registrar: + image: ${SUBSTRATE_RELAY_IMAGE:-paritytech/substrate-relay} + entrypoint: /entrypoints/rialto-parachain-registrar-entrypoint.sh + volumes: + - ./networks/entrypoints:/entrypoints + - rialto-share:/rialto-share:z + environment: + RUST_LOG: bridge=trace + depends_on: + - rialto-node-alice + - rialto-parachain-collator-alice + diff --git a/deployments/networks/rialto.yml b/deployments/networks/rialto.yml new file mode 100644 index 00000000000..1e2bdd3e41b --- /dev/null +++ b/deployments/networks/rialto.yml @@ -0,0 +1,118 @@ +# Compose file for quickly spinning up a local instance of the Rialto Substrate network. +# +# Note that the Rialto network is only used for testing, so the configuration settings you see here +# are *not* recommended for a production environment. +# +# For example, do *not* keep your `node-key` in version control, and unless you're _really_ sure you +# want to provide public access to your nodes do *not* publicly expose RPC methods. +version: '3.5' +services: + rialto-node-alice: &rialto-bridge-node + image: ${RIALTO_BRIDGE_NODE_IMAGE:-paritytech/rialto-bridge-node} + entrypoint: + - /home/user/rialto-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/rialto-node-bob/tcp/30333/p2p/12D3KooWSEpHJj29HEzgPFcRYVc5X3sEuP3KgiUoqJNCet51NiMX + - --alice + - --node-key=79cf382988364291a7968ae7825c01f68c50d679796a8983237d07fe0ccf363b + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + environment: + RUST_LOG: runtime=trace,rpc=debug,txpool=trace,runtime::bridge=trace,beefy=debug + ports: + - "9933:9933" + - "9944:9944" + + rialto-node-bob: + <<: *rialto-bridge-node + entrypoint: + - /home/user/rialto-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE + - --bob + - --node-key=4f9d0146dd9b7b3bf5a8089e3880023d1df92057f89e96e07bb4d8c2ead75bbd + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + ports: + - "10033:9933" + - "10044:9944" + + rialto-node-charlie: + <<: *rialto-bridge-node + entrypoint: + - /home/user/rialto-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE + - --charlie + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + - --prometheus-external + ports: + - "10133:9933" + - "10144:9944" + - "10615:9615" + + rialto-node-dave: + <<: *rialto-bridge-node + entrypoint: + - /home/user/rialto-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE + - --dave + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + ports: + - "10233:9933" + - "10244:9944" + + rialto-node-eve: + <<: *rialto-bridge-node + entrypoint: + - /home/user/rialto-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE + - --eve + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + ports: + - "10333:9933" + - "10344:9944" + + rialto-chainspec-exporter: + image: ${RIALTO_BRIDGE_NODE_IMAGE:-paritytech/rialto-bridge-node} + entrypoint: /entrypoints/rialto-chainspec-exporter-entrypoint.sh + volumes: + - ./networks/entrypoints:/entrypoints + - rialto-share:/rialto-share:z + + # Note: These are being overridden from the top level `monitoring` compose file. + prometheus-metrics: + volumes: + - ./networks/dashboard/prometheus/rialto-targets.yml:/etc/prometheus/targets-rialto-nodes.yml + depends_on: + - rialto-node-charlie + +# we're using `/rialto-share` to expose Rialto chain spec to those who are interested. Right +# now it is Rialto Parachain collator nodes. Local + tmpfs combination allows sharing writable +# in-memory volumes, which are dropped when containers are stopped. +volumes: + rialto-share: + driver: local + driver_opts: + type: "tmpfs" + device: "tmpfs" diff --git a/deployments/reverse-proxy/README.md b/deployments/reverse-proxy/README.md new file mode 100644 index 00000000000..ded81f80a1b --- /dev/null +++ b/deployments/reverse-proxy/README.md @@ -0,0 +1,15 @@ +# nginx-proxy + +This is a nginx reverse proxy configuration with Let's encrypt companion. +Main purpose is to be able to use `https://polkadot.js.org/apps` to connect to +a running network. + +## How to? + +In current directory: +```bash +docker-compose up -d +``` + +Then start `rialto` network with the same command (one folder up). `nginx` should +pick up new containers being created and automatically create a proxy setup for `Charlie`. diff --git a/deployments/reverse-proxy/docker-compose.yml b/deployments/reverse-proxy/docker-compose.yml new file mode 100644 index 00000000000..ee49e96afdd --- /dev/null +++ b/deployments/reverse-proxy/docker-compose.yml @@ -0,0 +1,45 @@ +version: '2' +services: + nginx-proxy: + image: jwilder/nginx-proxy + container_name: nginx-proxy + networks: + - nginx-proxy + - deployments_default + ports: + - "80:80" + - "443:443" + volumes: + - conf:/etc/nginx/conf.d + - vhost:/etc/nginx/vhost.d + - html:/usr/share/nginx/html + - dhparam:/etc/nginx/dhparam + - certs:/etc/nginx/certs:ro + - /var/run/docker.sock:/tmp/docker.sock:ro + - acme:/etc/acme.sh + + letsencrypt: + image: jrcs/letsencrypt-nginx-proxy-companion + container_name: nginx-proxy-le + networks: + - nginx-proxy + volumes_from: + - nginx-proxy + volumes: + - certs:/etc/nginx/certs:rw + - /var/run/docker.sock:/var/run/docker.sock:ro + - acme:/etc/acme.sh + +volumes: + conf: + vhost: + html: + dhparam: + certs: + acme: + +networks: + nginx-proxy: + driver: bridge + deployments_default: + external: true diff --git a/deployments/run.sh b/deployments/run.sh new file mode 100755 index 00000000000..bbae4733176 --- /dev/null +++ b/deployments/run.sh @@ -0,0 +1,210 @@ +#!/bin/bash + +# Script used for running and updating bridge deployments. +# +# To deploy a network you can run this script with the name of the bridge (or multiple bridges) you want to run. +# +# `./run.sh westend-millau rialto-millau` +# +# To update a deployment to use the latest images available from the Docker Hub add the `update` +# argument after the bridge name. +# +# `./run.sh rialto-millau update` +# +# Once you've stopped having fun with your deployment you can take it down with: +# +# `./run.sh rialto-millau stop` +# +# Stopping the bridge will also bring down all networks that it uses. So if you have started multiple bridges +# that are using the same network (like Millau in rialto-millau and westend-millau bridges), then stopping one +# of these bridges will cause the other bridge to break. + +set -xeu + +# Since the Compose commands are using relative paths we need to `cd` into the `deployments` folder. +cd "$( dirname "${BASH_SOURCE[0]}" )" + +function show_help () { + set +x + echo " " + echo Error: $1 + echo " " + echo "Usage:" + echo " ./run.sh rialto-millau [stop|update] Run Rialto <> Millau Networks & Bridge" + echo " ./run.sh rialto-parachain-millau [stop|update] Run RialtoParachain <> Millau Networks & Bridge" + echo " ./run.sh westend-millau [stop|update] Run Westend -> Millau Networks & Bridge" + echo " ./run.sh everything|all [stop|update] Run all available Networks & Bridges" + echo " " + echo "Options:" + echo " --no-monitoring Disable monitoring" + echo " --no-ui Disable UI" + echo " --local Use prebuilt local images when starting relay and nodes" + echo " --local-substrate-relay Use prebuilt local/substrate-realy image when starting relay" + echo " --local-rialto Use prebuilt local/rialto-bridge-node image when starting nodes" + echo " --local-rialto-parachain Use prebuilt local/rialto-parachain-collator image when starting nodes" + echo " --local-millau Use prebuilt local/millau-bridge-node image when starting nodes" + echo " " + echo "You can start multiple bridges at once by passing several bridge names:" + echo " ./run.sh rialto-millau rialto-parachain-millau westend-millau [stop|update]" + exit 1 +} + +RIALTO=' -f ./networks/rialto.yml' +RIALTO_PARACHAIN=' -f ./networks/rialto-parachain.yml' +MILLAU=' -f ./networks/millau.yml' + +RIALTO_MILLAU='rialto-millau' +RIALTO_PARACHAIN_MILLAU='rialto-parachain-millau' +WESTEND_MILLAU='westend-millau' + +MONITORING=' -f ./monitoring/docker-compose.yml' +UI=' -f ./ui/docker-compose.yml' + +BRIDGES=() +NETWORKS='' +SUB_COMMAND='start' +for i in "$@" +do + case $i in + --no-monitoring) + MONITORING=" -f ./monitoring/disabled.yml" + shift + continue + ;; + --no-ui) + UI="" + shift + continue + ;; + --local) + export SUBSTRATE_RELAY_IMAGE=local/substrate-relay + export RIALTO_BRIDGE_NODE_IMAGE=local/rialto-bridge-node + export RIALTO_PARACHAIN_COLLATOR_IMAGE=local/rialto-parachain-collator + export MILLAU_BRIDGE_NODE_IMAGE=local/millau-bridge-node + shift + continue + ;; + --local-substrate-relay) + export SUBSTRATE_RELAY_IMAGE=local/substrate-relay + shift + continue + ;; + --local-rialto) + export RIALTO_BRIDGE_NODE_IMAGE=local/rialto-bridge-node + shift + continue + ;; + --local-rialto-parachain) + export RIALTO_PARACHAIN_COLLATOR_IMAGE=local/rialto-parachain-collator + shift + continue + ;; + --local-millau) + export MILLAU_BRIDGE_NODE_IMAGE=local/millau-bridge-node + shift + continue + ;; + everything|all) + BRIDGES=(${RIALTO_MILLAU:-} ${RIALTO_PARACHAIN_MILLAU:-} ${WESTEND_MILLAU:-}) + NETWORKS="${RIALTO:-} ${RIALTO_PARACHAIN:-} ${MILLAU:-}" + unset RIALTO RIALTO_PARACHAIN MILLAU RIALTO_MILLAU RIALTO_PARACHAIN_MILLAU WESTEND_MILLAU + shift + ;; + rialto-millau) + BRIDGES+=(${RIALTO_MILLAU:-}) + NETWORKS+="${RIALTO:-} ${MILLAU:-}" + unset RIALTO MILLAU RIALTO_MILLAU + shift + ;; + rialto-parachain-millau) + BRIDGES+=(${RIALTO_PARACHAIN_MILLAU:-}) + NETWORKS+="${RIALTO:-} ${RIALTO_PARACHAIN:-} ${MILLAU:-}" + unset RIALTO RIALTO_PARACHAIN MILLAU RIALTO_PARACHAIN_MILLAU + shift + ;; + westend-millau) + BRIDGES+=(${WESTEND_MILLAU:-}) + NETWORKS+=${MILLAU:-} + unset MILLAU WESTEND_MILLAU + shift + ;; + start|stop|update) + SUB_COMMAND=$i + shift + ;; + *) + show_help "Unknown option: $i" + ;; + esac +done + +if [ ${#BRIDGES[@]} -eq 0 ]; then + show_help "Missing bridge name." +fi + +COMPOSE_FILES=$NETWORKS$MONITORING$UI + +# Compose looks for .env files in the the current directory by default, we don't want that +COMPOSE_ARGS="--project-directory ." +# Path to env file that we want to use. Compose only accepts single `--env-file` argument, +# so we'll be using the last .env file we'll found. +COMPOSE_ENV_FILE='' + +for BRIDGE in "${BRIDGES[@]}" +do + BRIDGE_PATH="./bridges/$BRIDGE" + BRIDGE=" -f $BRIDGE_PATH/docker-compose.yml" + COMPOSE_FILES=$BRIDGE$COMPOSE_FILES + + # Remember .env file to use in docker-compose call + if [[ -f "$BRIDGE_PATH/.env" ]]; then + COMPOSE_ENV_FILE=" --env-file $BRIDGE_PATH/.env" + fi + + # Read and source variables from .env file so we can use them here + grep -e MATRIX_ACCESS_TOKEN -e WITH_PROXY $BRIDGE_PATH/.env > .env2 && . ./.env2 && rm .env2 + if [ ! -z ${MATRIX_ACCESS_TOKEN+x} ]; then + sed -i "s/access_token.*/access_token: \"$MATRIX_ACCESS_TOKEN\"/" ./monitoring/grafana-matrix/config.yml + fi +done + +# Final COMPOSE_ARGS +COMPOSE_ARGS="$COMPOSE_ARGS $COMPOSE_ENV_FILE" + +# Check the sub-command, perhaps we just mean to stop the network instead of starting it. +if [ "$SUB_COMMAND" == "stop" ]; then + + if [ ! -z ${WITH_PROXY+x} ]; then + cd ./reverse-proxy + docker-compose down + cd - + fi + + docker-compose $COMPOSE_ARGS $COMPOSE_FILES down + + exit 0 +fi + +# See if we want to update the docker images before starting the network. +if [ "$SUB_COMMAND" == "update" ]; then + + # Stop the proxy cause otherwise the network can't be stopped + if [ ! -z ${WITH_PROXY+x} ]; then + cd ./reverse-proxy + docker-compose down + cd - + fi + + + docker-compose $COMPOSE_ARGS $COMPOSE_FILES pull + docker-compose $COMPOSE_ARGS $COMPOSE_FILES down + docker-compose $COMPOSE_ARGS $COMPOSE_FILES build +fi + +docker-compose $COMPOSE_ARGS $COMPOSE_FILES up -d + +# Start the proxy if needed +if [ ! -z ${WITH_PROXY+x} ]; then + cd ./reverse-proxy + docker-compose up -d +fi diff --git a/deployments/types-millau.json b/deployments/types-millau.json new file mode 100644 index 00000000000..6d651b4c7cf --- /dev/null +++ b/deployments/types-millau.json @@ -0,0 +1,192 @@ +{ + "--1": "Millau Types", + "MillauAddress": "AccountId", + "MillauLookupSource": "AccountId", + "MillauBalance": "u64", + "MillauBlockHash": "H512", + "MillauBlockNumber": "u64", + "MillauHeader": { + "parent_Hash": "MillauBlockHash", + "number": "Compact", + "state_root": "MillauBlockHash", + "extrinsics_root": "MillauBlockHash", + "digest": "MillauDigest" + }, + "MillauDigest": { + "logs": "Vec" + }, + "MillauDigestItem": { + "_enum": { + "Other": "Vec", + "AuthoritiesChange": "Vec", + "ChangesTrieRoot": "MillauBlockHash", + "SealV0": "SealV0", + "Consensus": "Consensus", + "Seal": "Seal", + "PreRuntime": "PreRuntime" + } + }, + "--2": "Rialto Types", + "RialtoAddress": "MultiAddress", + "RialtoLookupSource": "MultiAddress", + "RialtoBalance": "u128", + "RialtoBlockHash": "H256", + "RialtoBlockNumber": "u32", + "RialtoHeader": { + "parent_Hash": "RialtoBlockHash", + "number": "Compact", + "state_root": "RialtoBlockHash", + "extrinsics_root": "RialtoBlockHash", + "digest": "RialtoDigest" + }, + "RialtoDigest": { + "logs": "Vec" + }, + "RialtoDigestItem": { + "_enum": { + "Other": "Vec", + "AuthoritiesChange": "Vec", + "ChangesTrieRoot": "RialtoBlockHash", + "SealV0": "SealV0", + "Consensus": "Consensus", + "Seal": "Seal", + "PreRuntime": "PreRuntime" + } + }, + "--3": "Common types", + "AccountSigner": "MultiSigner", + "SpecVersion": "u32", + "RelayerId": "AccountId", + "SourceAccountId": "AccountId", + "ImportedHeader": { + "header": "BridgedHeader", + "requires_justification": "bool", + "is_finalized": "bool", + "signal_hash": "Option" + }, + "AuthoritySet": { + "authorities": "AuthorityList", + "set_id": "SetId" + }, + "Id": "[u8; 4]", + "ChainId": "Id", + "LaneId": "Id", + "MessageNonce": "u64", + "BridgeMessageId": "(Id, u64)", + "MessageKey": { + "lane_id": "LaneId", + "nonce:": "MessageNonce" + }, + "InboundRelayer": "AccountId", + "InboundLaneData": { + "relayers": "Vec", + "last_confirmed_nonce": "MessageNonce" + }, + "UnrewardedRelayer": { + "relayer": "RelayerId", + "messages": "DeliveredMessages" + }, + "DeliveredMessages": { + "begin": "MessageNonce", + "end": "MessageNonce", + "dispatch_results": "BitVec" + }, + "OutboundLaneData": { + "oldest_unpruned_nonce": "MessageNonce", + "latest_received_nonce": "MessageNonce", + "latest_generated_nonce": "MessageNonce" + }, + "MessageData": { + "payload": "MessagePayload", + "fee": "Fee" + }, + "MessagePayload": "Vec", + "BridgedOpaqueCall": "Vec", + "OutboundMessageFee": "Fee", + "OutboundPayload": { + "spec_version": "SpecVersion", + "weight": "Weight", + "origin": "CallOrigin", + "dispatch_fee_payment": "DispatchFeePayment", + "call": "BridgedOpaqueCall" + }, + "CallOrigin": { + "_enum": { + "SourceRoot": "()", + "TargetAccount": "(SourceAccountId, MultiSigner, MultiSignature)", + "SourceAccount": "SourceAccountId" + } + }, + "DispatchFeePayment": { + "_enum": { + "AtSourceChain": "()", + "AtTargetChain": "()" + } + }, + "MultiSigner": { + "_enum": { + "Ed25519": "H256", + "Sr25519": "H256", + "Ecdsa": "[u8;33]" + } + }, + "MessagesProofOf": { + "bridged_header_hash": "BridgedBlockHash", + "storage_proof": "Vec", + "lane": "LaneId", + "nonces_start": "MessageNonce", + "nonces_end": "MessageNonce" + }, + "StorageProofItem": "Vec", + "MessagesDeliveryProofOf": { + "bridged_header_hash": "BridgedBlockHash", + "storage_proof": "Vec", + "lane": "LaneId" + }, + "UnrewardedRelayersState": { + "unrewarded_relayer_entries": "MessageNonce", + "messages_in_oldest_entry": "MessageNonce", + "total_messages": "MessageNonce" + }, + "AncestryProof": "()", + "MessageFeeData": { + "lane_id": "LaneId", + "payload": "OutboundPayload" + }, + "Precommit": { + "target_hash": "BridgedBlockHash", + "target_number": "BridgedBlockNumber" + }, + "AuthoritySignature": "[u8;64]", + "AuthorityId": "[u8;32]", + "SignedPrecommit": { + "precommit": "Precommit", + "signature": "AuthoritySignature", + "id": "AuthorityId" + }, + "Commit": { + "target_hash": "BridgedBlockHash", + "target_number": "BridgedBlockNumber", + "precommits": "Vec" + }, + "GrandpaJustification": { + "round": "u64", + "commit": "Commit", + "votes_ancestries": "Vec" + }, + "Address": "MillauAddress", + "LookupSource": "MillauLookupSource", + "Fee": "MillauBalance", + "Balance": "MillauBalance", + "Hash": "MillauBlockHash", + "BlockHash": "MillauBlockHash", + "BlockNumber": "MillauBlockNumber", + "BridgedBlockHash": "RialtoBlockHash", + "BridgedBlockNumber": "RialtoBlockNumber", + "BridgedHeader": "RialtoHeader", + "Parameter": { + "_enum": { + "MillauToRialtoConversionRate": "u128" + } + } +} diff --git a/deployments/types-rialto.json b/deployments/types-rialto.json new file mode 100644 index 00000000000..a574e117893 --- /dev/null +++ b/deployments/types-rialto.json @@ -0,0 +1,192 @@ +{ + "--1": "Millau Types", + "MillauAddress": "AccountId", + "MillauLookupSource": "AccountId", + "MillauBalance": "u64", + "MillauBlockHash": "H512", + "MillauBlockNumber": "u64", + "MillauHeader": { + "parent_Hash": "MillauBlockHash", + "number": "Compact", + "state_root": "MillauBlockHash", + "extrinsics_root": "MillauBlockHash", + "digest": "MillauDigest" + }, + "MillauDigest": { + "logs": "Vec" + }, + "MillauDigestItem": { + "_enum": { + "Other": "Vec", + "AuthoritiesChange": "Vec", + "ChangesTrieRoot": "MillauBlockHash", + "SealV0": "SealV0", + "Consensus": "Consensus", + "Seal": "Seal", + "PreRuntime": "PreRuntime" + } + }, + "--2": "Rialto Types", + "RialtoAddress": "MultiAddress", + "RialtoLookupSource": "MultiAddress", + "RialtoBalance": "u128", + "RialtoBlockHash": "H256", + "RialtoBlockNumber": "u32", + "RialtoHeader": { + "parent_Hash": "RialtoBlockHash", + "number": "Compact", + "state_root": "RialtoBlockHash", + "extrinsics_root": "RialtoBlockHash", + "digest": "RialtoDigest" + }, + "RialtoDigest": { + "logs": "Vec" + }, + "RialtoDigestItem": { + "_enum": { + "Other": "Vec", + "AuthoritiesChange": "Vec", + "ChangesTrieRoot": "RialtoBlockHash", + "SealV0": "SealV0", + "Consensus": "Consensus", + "Seal": "Seal", + "PreRuntime": "PreRuntime" + } + }, + "--3": "Common types", + "AccountSigner": "MultiSigner", + "SpecVersion": "u32", + "RelayerId": "AccountId", + "SourceAccountId": "AccountId", + "ImportedHeader": { + "header": "BridgedHeader", + "requires_justification": "bool", + "is_finalized": "bool", + "signal_hash": "Option" + }, + "AuthoritySet": { + "authorities": "AuthorityList", + "set_id": "SetId" + }, + "Id": "[u8; 4]", + "ChainId": "Id", + "LaneId": "Id", + "MessageNonce": "u64", + "BridgeMessageId": "(Id, u64)", + "MessageKey": { + "lane_id": "LaneId", + "nonce:": "MessageNonce" + }, + "InboundRelayer": "AccountId", + "InboundLaneData": { + "relayers": "Vec", + "last_confirmed_nonce": "MessageNonce" + }, + "UnrewardedRelayer": { + "relayer": "RelayerId", + "messages": "DeliveredMessages" + }, + "DeliveredMessages": { + "begin": "MessageNonce", + "end": "MessageNonce", + "dispatch_results": "BitVec" + }, + "OutboundLaneData": { + "oldest_unpruned_nonce": "MessageNonce", + "latest_received_nonce": "MessageNonce", + "latest_generated_nonce": "MessageNonce" + }, + "MessageData": { + "payload": "MessagePayload", + "fee": "Fee" + }, + "MessagePayload": "Vec", + "BridgedOpaqueCall": "Vec", + "OutboundMessageFee": "Fee", + "OutboundPayload": { + "spec_version": "SpecVersion", + "weight": "Weight", + "origin": "CallOrigin", + "dispatch_fee_payment": "DispatchFeePayment", + "call": "BridgedOpaqueCall" + }, + "CallOrigin": { + "_enum": { + "SourceRoot": "()", + "TargetAccount": "(SourceAccountId, MultiSigner, MultiSignature)", + "SourceAccount": "SourceAccountId" + } + }, + "DispatchFeePayment": { + "_enum": { + "AtSourceChain": "()", + "AtTargetChain": "()" + } + }, + "MultiSigner": { + "_enum": { + "Ed25519": "H256", + "Sr25519": "H256", + "Ecdsa": "[u8;33]" + } + }, + "MessagesProofOf": { + "bridged_header_hash": "BridgedBlockHash", + "storage_proof": "Vec", + "lane": "LaneId", + "nonces_start": "MessageNonce", + "nonces_end": "MessageNonce" + }, + "StorageProofItem": "Vec", + "MessagesDeliveryProofOf": { + "bridged_header_hash": "BridgedBlockHash", + "storage_proof": "Vec", + "lane": "LaneId" + }, + "UnrewardedRelayersState": { + "unrewarded_relayer_entries": "MessageNonce", + "messages_in_oldest_entry": "MessageNonce", + "total_messages": "MessageNonce" + }, + "AncestryProof": "()", + "MessageFeeData": { + "lane_id": "LaneId", + "payload": "OutboundPayload" + }, + "Precommit": { + "target_hash": "BridgedBlockHash", + "target_number": "BridgedBlockNumber" + }, + "AuthoritySignature": "[u8;64]", + "AuthorityId": "[u8;32]", + "SignedPrecommit": { + "precommit": "Precommit", + "signature": "AuthoritySignature", + "id": "AuthorityId" + }, + "Commit": { + "target_hash": "BridgedBlockHash", + "target_number": "BridgedBlockNumber", + "precommits": "Vec" + }, + "GrandpaJustification": { + "round": "u64", + "commit": "Commit", + "votes_ancestries": "Vec" + }, + "Address": "RialtoAddress", + "LookupSource": "RialtoLookupSource", + "Fee": "RialtoBalance", + "Balance": "RialtoBalance", + "BlockHash": "RialtoBlockHash", + "BlockNumber": "RialtoBlockNumber", + "BridgedBlockHash": "MillauBlockHash", + "BridgedBlockNumber": "MillauBlockNumber", + "BridgedHeader": "MillauHeader", + "Parameter": { + "_enum": { + "RialtoToMillauConversionRate": "u128" + } + }, + "ValidationCodeHash": "H256" +} diff --git a/deployments/types/build.sh b/deployments/types/build.sh new file mode 100755 index 00000000000..be96459a6da --- /dev/null +++ b/deployments/types/build.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# The script generates JSON type definition files in `./deployment` directory to be used for +# JS clients. +# +# It works by creating definitions for each side of the different bridge pairs we support +# (Rialto<>Millau at the moment). +# +# To avoid duplication each bridge pair has a JSON file with common definitions, as well as a +# general JSON file with common definitions regardless of the bridge pair. These files are then +# merged with chain-specific type definitions. + +set -eux + +# Make sure we are in the right dir. +cd $(dirname $(realpath $0)) + +# Create types for our supported bridge pairs (Rialto<>Millau) +jq -s '.[0] * .[1] * .[2]' rialto-millau.json common.json rialto.json > ../types-rialto.json +jq -s '.[0] * .[1] * .[2]' rialto-millau.json common.json millau.json > ../types-millau.json diff --git a/deployments/types/common.json b/deployments/types/common.json new file mode 100644 index 00000000000..4e129f7132b --- /dev/null +++ b/deployments/types/common.json @@ -0,0 +1,124 @@ +{ + "--3": "Common types", + "AccountSigner": "MultiSigner", + "SpecVersion": "u32", + "RelayerId": "AccountId", + "SourceAccountId": "AccountId", + "ImportedHeader": { + "header": "BridgedHeader", + "requires_justification": "bool", + "is_finalized": "bool", + "signal_hash": "Option" + }, + "AuthoritySet": { + "authorities": "AuthorityList", + "set_id": "SetId" + }, + "Id": "[u8; 4]", + "ChainId": "Id", + "LaneId": "Id", + "MessageNonce": "u64", + "BridgeMessageId": "(Id, u64)", + "MessageKey": { + "lane_id": "LaneId", + "nonce:": "MessageNonce" + }, + "InboundRelayer": "AccountId", + "InboundLaneData": { + "relayers": "Vec", + "last_confirmed_nonce": "MessageNonce" + }, + "UnrewardedRelayer": { + "relayer": "RelayerId", + "messages": "DeliveredMessages" + }, + "DeliveredMessages": { + "begin": "MessageNonce", + "end": "MessageNonce", + "dispatch_results": "BitVec" + }, + "OutboundLaneData": { + "oldest_unpruned_nonce": "MessageNonce", + "latest_received_nonce": "MessageNonce", + "latest_generated_nonce": "MessageNonce" + + }, + "MessageData": { + "payload": "MessagePayload", + "fee": "Fee" + }, + "MessagePayload": "Vec", + "BridgedOpaqueCall": "Vec", + "OutboundMessageFee": "Fee", + "OutboundPayload": { + "spec_version": "SpecVersion", + "weight": "Weight", + "origin": "CallOrigin", + "dispatch_fee_payment": "DispatchFeePayment", + "call": "BridgedOpaqueCall" + }, + "CallOrigin": { + "_enum": { + "SourceRoot": "()", + "TargetAccount": "(SourceAccountId, MultiSigner, MultiSignature)", + "SourceAccount": "SourceAccountId" + } + }, + "DispatchFeePayment": { + "_enum": { + "AtSourceChain": "()", + "AtTargetChain": "()" + } + }, + "MultiSigner": { + "_enum": { + "Ed25519": "H256", + "Sr25519": "H256", + "Ecdsa": "[u8;33]" + } + }, + "MessagesProofOf": { + "bridged_header_hash": "BridgedBlockHash", + "storage_proof": "Vec", + "lane": "LaneId", + "nonces_start": "MessageNonce", + "nonces_end": "MessageNonce" + }, + "StorageProofItem": "Vec", + "MessagesDeliveryProofOf": { + "bridged_header_hash": "BridgedBlockHash", + "storage_proof": "Vec", + "lane": "LaneId" + }, + "UnrewardedRelayersState": { + "unrewarded_relayer_entries": "MessageNonce", + "messages_in_oldest_entry": "MessageNonce", + "total_messages": "MessageNonce" + }, + "AncestryProof": "()", + "MessageFeeData": { + "lane_id": "LaneId", + "payload": "OutboundPayload" + }, + "Precommit": { + "target_hash": "BridgedBlockHash", + "target_number": "BridgedBlockNumber" + }, + "AuthoritySignature": "[u8;64]", + "AuthorityId": "[u8;32]", + "SignedPrecommit": { + "precommit": "Precommit", + "signature": "AuthoritySignature", + "id": "AuthorityId" + }, + "Commit": { + "target_hash": "BridgedBlockHash", + "target_number": "BridgedBlockNumber", + "precommits": "Vec" + }, + "GrandpaJustification": { + "round": "u64", + "commit": "Commit", + "votes_ancestries": "Vec" + } +} diff --git a/deployments/types/millau.json b/deployments/types/millau.json new file mode 100644 index 00000000000..589d5619df4 --- /dev/null +++ b/deployments/types/millau.json @@ -0,0 +1,17 @@ +{ + "Address": "MillauAddress", + "LookupSource": "MillauLookupSource", + "Fee": "MillauBalance", + "Balance": "MillauBalance", + "Hash": "MillauBlockHash", + "BlockHash": "MillauBlockHash", + "BlockNumber": "MillauBlockNumber", + "BridgedBlockHash": "RialtoBlockHash", + "BridgedBlockNumber": "RialtoBlockNumber", + "BridgedHeader": "RialtoHeader", + "Parameter": { + "_enum": { + "MillauToRialtoConversionRate": "u128" + } + } +} diff --git a/deployments/types/rialto-millau.json b/deployments/types/rialto-millau.json new file mode 100644 index 00000000000..971cf666d47 --- /dev/null +++ b/deployments/types/rialto-millau.json @@ -0,0 +1,56 @@ +{ + "--1": "Millau Types", + "MillauAddress": "AccountId", + "MillauLookupSource": "AccountId", + "MillauBalance": "u64", + "MillauBlockHash": "H512", + "MillauBlockNumber": "u64", + "MillauHeader": { + "parent_Hash": "MillauBlockHash", + "number": "Compact", + "state_root": "MillauBlockHash", + "extrinsics_root": "MillauBlockHash", + "digest": "MillauDigest" + }, + "MillauDigest": { + "logs": "Vec" + }, + "MillauDigestItem": { + "_enum": { + "Other": "Vec", + "AuthoritiesChange": "Vec", + "ChangesTrieRoot": "MillauBlockHash", + "SealV0": "SealV0", + "Consensus": "Consensus", + "Seal": "Seal", + "PreRuntime": "PreRuntime" + } + }, + "--2": "Rialto Types", + "RialtoAddress": "MultiAddress", + "RialtoLookupSource": "MultiAddress", + "RialtoBalance": "u128", + "RialtoBlockHash": "H256", + "RialtoBlockNumber": "u32", + "RialtoHeader": { + "parent_Hash": "RialtoBlockHash", + "number": "Compact", + "state_root": "RialtoBlockHash", + "extrinsics_root": "RialtoBlockHash", + "digest": "RialtoDigest" + }, + "RialtoDigest": { + "logs": "Vec" + }, + "RialtoDigestItem": { + "_enum": { + "Other": "Vec", + "AuthoritiesChange": "Vec", + "ChangesTrieRoot": "RialtoBlockHash", + "SealV0": "SealV0", + "Consensus": "Consensus", + "Seal": "Seal", + "PreRuntime": "PreRuntime" + } + } +} diff --git a/deployments/types/rialto.json b/deployments/types/rialto.json new file mode 100644 index 00000000000..77c30b7cc2d --- /dev/null +++ b/deployments/types/rialto.json @@ -0,0 +1,17 @@ +{ + "Address": "RialtoAddress", + "LookupSource": "RialtoLookupSource", + "Fee": "RialtoBalance", + "Balance": "RialtoBalance", + "BlockHash": "RialtoBlockHash", + "BlockNumber": "RialtoBlockNumber", + "BridgedBlockHash": "MillauBlockHash", + "BridgedBlockNumber": "MillauBlockNumber", + "BridgedHeader": "MillauHeader", + "Parameter": { + "_enum": { + "RialtoToMillauConversionRate": "u128" + } + }, + "ValidationCodeHash": "H256" +} diff --git a/deployments/ui/README.md b/deployments/ui/README.md new file mode 100644 index 00000000000..ad946fc699b --- /dev/null +++ b/deployments/ui/README.md @@ -0,0 +1,23 @@ +# bridges-ui + +This is a Bridges UI docker configuration file. The source of the Bridges UI code +can be found in [the repository](https://github.com/paritytech/parity-bridges-ui). +The CI should create and publish a docker image that is used by this configuration +file, so that the code is always using the latest version. +The UI is configured to point to local Rialto and Millau nodes to retrieve the require +data. + +This image can be used together with `nginx-proxy` to expose the UI externally. See +`VIRTUAL_*` and `LETSENCRYPT_*` environment variables. + +After start the UI is available at `http://localhost:8080` + +## How to? + +In current directory: +```bash +docker-compose up -d +``` + +Then start `rialto` & `millau` networks with the same command (one folder up) or +run the full setup by using `../run.sh` script. diff --git a/deployments/ui/docker-compose.yml b/deployments/ui/docker-compose.yml new file mode 100644 index 00000000000..8b3f8178c36 --- /dev/null +++ b/deployments/ui/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3.5' +services: + bridges-ui: + image: paritytech/parity-bridges-ui + environment: + VIRTUAL_HOST: ui.brucke.link + VIRTUAL_PORT: 80 + LETSENCRYPT_HOST: ui.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + CHAIN_1_SUBSTRATE_PROVIDER: ${UI_CHAIN_1:-ws://localhost:9944} + CHAIN_2_SUBSTRATE_PROVIDER: ${UI_CHAIN_2:-ws://localhost:19944} + ports: + - "8080:80" diff --git a/docs/high-level-overview.md b/docs/high-level-overview.md new file mode 100644 index 00000000000..2642c20c86a --- /dev/null +++ b/docs/high-level-overview.md @@ -0,0 +1,165 @@ +# High-Level Bridge Documentation + +## Purpose + +Trustless connecting between two Substrate-based chains using GRANDPA finality. + +## Overview + +Even though we support two-way bridging, the documentation will generally talk about a one-sided +interaction. That's to say, we will only talk about syncing headers and messages from a _source_ +chain to a _target_ chain. This is because the two-sided interaction is really just the one-sided +interaction with the source and target chains switched. + +To understand the full interaction with the bridge, take a look at the +[testing scenarios](./testing-scenarios.md) document. It describes potential use cases and describes +how each of the layers outlined below is involved. + +The bridge is built from various components. Here is a quick overview of the important ones. + +### Header Sync + +A light client of the source chain built into the target chain's runtime. It is a single FRAME +pallet. It provides a "source of truth" about the source chain headers which have been finalized. +This is useful for higher level applications. + +### Headers Relayer + +A standalone application connected to both chains. It submits every source chain header it sees to +the target chain through RPC. + +### Message Delivery + +A FRAME pallet built on top of the header sync pallet. It allows users to submit messages to the +source chain, which are to be delivered to the target chain. The delivery protocol doesn't care +about the payload more than it has to. Handles replay protection and message ordering. + +### Message Dispatch + +A FRAME pallet responsible for interpreting the payload of delivered messages. + +### Message Relayer + +A standalone application handling delivery of the messages from source chain to the target chain. + +## Processes + +High level sequence charts of the process can be found in [a separate document](./high-level.html). + +### Substrate (GRANDPA) Header Sync + +The header sync pallet (`pallet-bridge-grandpa`) is an on-chain light client for chains which use +GRANDPA finality. It is part of the target chain's runtime, and accepts finality proofs from the source +chain. Verify GRANDPA finality proofs (a.k.a justifications) and track GRANDPA finality set changes. + +The pallet does not care about what block production mechanism is used for the source chain +(e.g Aura or BABE) as long as it uses the GRANDPA finality gadget. In fact the pallet does not +necessarily store all produced headers, we only import headers with valid GRANDPA justifications. + +Referer to the [pallet documentation](../modules/grandpa/src/lib.rs) for more details. + +#### Header Relayer strategy + +There is currently no reward strategy for the relayers at all. They also are not required to be +staked or registered on-chain, unlike in other bridge designs. We consider the header sync to be +an essential part of the bridge and the incentivization should be happening on the higher layers. + +At the moment, signed transactions are the only way to submit headers to the header sync pallet. +However, in the future we would like to use unsigned transactions for headers delivery. This will +allow transaction de-duplication to be done at the transaction pool level and also remove the cost +for message relayers to run header relayers. + +### Message Passing + +Once header sync is maintained, the target side of the bridge can receive and verify proofs about +events happening on the source chain, or its internal state. On top of this, we built a message +passing protocol which consists of two parts described in following sections: message delivery and +message dispatch. + +#### Message Lanes Delivery + +The [Message delivery pallet](../modules/messages/src/lib.rs) is responsible for queueing up +messages and delivering them in order on the target chain. It also dispatches messages, but we will +cover that in the next section. + +The pallet supports multiple lanes (channels) where messages can be added. Every lane can be +considered completely independent from others, which allows them to make progress in parallel. +Different lanes can be configured to validated messages differently (e.g higher rewards, specific +types of payload, etc.) and may be associated with a particular "user application" built on top of +the bridge. Note that messages in the same lane MUST be delivered _in the same order_ they were +queued up. + +The message delivery protocol does not care about the payload it transports and can be coupled +with an arbitrary message dispatch mechanism that will interpret and execute the payload if delivery +conditions are met. Each delivery on the target chain is confirmed back to the source chain by the +relayer. This is so that she can collect the reward for delivering these messages. + +Users of the pallet add their messages to an "outbound lane" on the source chain. When a block is +finalized message relayers are responsible for reading the current queue of messages and submitting +some (or all) of them to the "inbound lane" of the target chain. Each message has a `nonce` +associated with it, which serves as the ordering of messages. The inbound lane stores the last +delivered nonce to prevent replaying messages. To successfully deliver the message to the inbound lane +on target chain the relayer has to present present a storage proof which shows that the message was +part of the outbound lane on the source chain. + +During delivery of messages they are immediately dispatched on the target chain and the relayer is +required to declare the correct `weight` to cater for all messages dispatch and pay all required +fees of the target chain. To make sure the relayer is incentivised to do so, on the source chain: +- the user provides a declared dispatch weight of the payload +- the pallet calculates the expected fee on the target chain based on the declared weight +- the pallet converts the target fee into source tokens (based on a price oracle) and reserves + enough tokens to cover for the delivery, dispatch, confirmation and additional relayers reward. + +If the declared weight turns out to be too low on the target chain the message is delivered but +it immediately fails to dispatch. The fee and reward is collected by the relayer upon confirmation +of delivery. + +Due to the fact that message lanes require delivery confirmation transactions, they also strictly +require bi-directional header sync (i.e. you can't use message delivery with one-way header sync). + +#### Dispatching Messages + +The [Message dispatch pallet](../modules/dispatch/src/lib.rs) is used to perform the actions +specified by messages which have come over the bridge. For Substrate-based chains this means +interpreting the source chain's message as a `Call` on the target chain. + +An example `Call` of the target chain would look something like this: + +```rust +target_runtime::Call::Balances(target_runtime::pallet_balances::Call::transfer(recipient, amount)) +``` + +When sending a `Call` it must first be SCALE encoded and then sent to the source chain. The `Call` +is then delivered by the message lane delivery mechanism from the source chain to the target chain. +When a message is received the inbound message lane on the target chain will try and decode the +message payload into a `Call` enum. If it's successful it will be dispatched after we check that the +weight of the call does not exceed the weight declared by the sender. The relayer pays fees for +executing the transaction on the target chain, but her costs should be covered by the sender on the +source chain. + +When dispatching messages there are three Origins which can be used by the target chain: +1. Root Origin +2. Source Origin +3. Target Origin + +Senders of a message can indicate which one of the three origins they would like to dispatch their +message with. However, there are restrictions on who/what is allowed to dispatch messages with a +particular origin. + +The Root origin represents the source chain's Root account on the target chain. This origin can can +only be dispatched on the target chain if the "send message" request was made by the Root origin of +the source chain - otherwise the message will fail to be dispatched. + +The Source origin represents an account without a private key on the target chain. This account will +be generated/derived using the account ID of the sender on the source chain. We don't necessarily +require the source account id to be associated with a private key on the source chain either. This +is useful for representing things such as source chain proxies or pallets. + +The Target origin represents an account with a private key on the target chain. The sender on the +source chain needs to prove ownership of this account by using their target chain private key to +sign: `(Call, SourceChainAccountId).encode()`. This will be included in the message payload and +verified by the target chain before dispatch. + +See [`CallOrigin` documentation](../primitives/message-dispatch/src/lib.rs) for more details. + +#### Message Relayers Strategy diff --git a/docs/high-level.html b/docs/high-level.html new file mode 100644 index 00000000000..3c4c6178c95 --- /dev/null +++ b/docs/high-level.html @@ -0,0 +1,55 @@ + + + + + + High Level Bridge Components + + +

Header Sync

+

Header pallet on the target chain, keeps track of the forks, but requires finality for blocks that perform authority set changes. That means, it won't sync a fork with authority set change unless that change finalized.

+
+ sequenceDiagram + participant Source Chain + participant Relayer + participant Target Chain + Note right of Target Chain: Best: 0, Finalized: 0 + Source Chain ->> Source Chain: Import Block 1 + Source Chain ->> Source Chain: Import Block 2 + Relayer ->> Target Chain: Submit Block 1 + Note right of Target Chain: Best: 1, Finalized: 0 + Relayer ->> Target Chain: Submit Block 2 + Note right of Target Chain: Best: 2, Finalized: 0 + Source Chain ->> Source Chain: Import Block 2' + Relayer ->> Target Chain: Submit Block 2' + Note right of Target Chain: Best: 2 or 2', Finalized: 0 + Source Chain ->> Source Chain: Finalize Block 2' + Relayer ->> Target Chain: Submit Finality of Block 2' + Note right of Target Chain: Best: 2', Finalized: 2' +
+

Message Delivery (single lane)

+

Pending messages are stored on-chain (source) so the relayer code is completely stateless - it can read all the details from the chain.

+

Delivering pending messages requires finality first.

+
+ sequenceDiagram + participant Source Chain + participant Relayer + participant Target Chain + Source Chain ->> Source Chain: Queue Message 1 + Source Chain ->> Source Chain: Queue Message 2 + Source Chain ->> Source Chain: Queue Message 3 + Note left of Source Chain: Queued Messages: [1, 2, 3, ] + Note left of Source Chain: Reward for [1, 2, 3, ] reserved + Relayer ->> Target Chain: Deliver Messages 1..2 + Note right of Target Chain: Target chain dispatches the messages.
To Confirm: {1..2 => relayer_1} + Relayer ->> Source Chain: Delivery Confirmation of 1..2 + Note left of Source Chain: Queued Messages: [3, ] + Note left of Source Chain: Reward payout for [1, 2, ] + Relayer -->> Target Chain: Confirmed Messages 1..2 + Note right of Target Chain: To Confirm: {} + Note over Relayer, Target Chain: (this is not a separate transaction,
it's bundled with the "Deliver Messages" proof) +
+ + + + diff --git a/docs/plan.md b/docs/plan.md new file mode 100644 index 00000000000..9c4106d9ade --- /dev/null +++ b/docs/plan.md @@ -0,0 +1,22 @@ +Plan for the Internal Audit: +1. High-level overview (describing layers, maybe with pictures) + - what have we done already. + [Tomek to present] + [Hernando to help with diagrams today] + +2. Demo? How to play with the network. + [Hernando] + +3. Demo of token transfer on Millau. + [Hernando] + +4. Go through the scenario description and let people ask questions in the meantime. + Jump to the code on demand. + [Tomek, Hernando, Slava] + + ... + +5. The roadmap + - outstanding issues. + [Tomek] + diff --git a/docs/scenario1.html b/docs/scenario1.html new file mode 100644 index 00000000000..808a0c34f0d --- /dev/null +++ b/docs/scenario1.html @@ -0,0 +1,47 @@ + + + + + + Flow Chart of Millau to Rialto Transfer + + +

Scenario: mDave sending RLT to rEve

+
+ sequenceDiagram + participant mDave + participant Millau + participant Bridge Relayer + participant Rialto + participant rEve + Rialto->>Rialto: Endow r(mDave) with RLT. + mDave->>Millau: send_message(transfer, 5 RLT, rEve) + Millau->>Millau: Locks fee & reward for the relayer and queues the message. + rect rgb(205, 226, 244) + Bridge Relayer->>+Millau: What's your best header? + Millau-->>-Bridge Relayer: It's header 5. + Bridge Relayer->>+Rialto: What's the best Millau header you know about? + Rialto-->>-Bridge Relayer: I only know about 4. + Bridge Relayer->>Rialto: Cool, here is Millau header 5 [`submit_signed_header()`]. + Bridge Relayer->>+Rialto: What's the best finalized Millau header you know about? + Rialto-->>-Bridge Relayer: I only know about 3. + Bridge Relayer->>+Millau: Do you have a finality proof for 4..5? + Millau-->>-Bridge Relayer: Yes I do, here it is. + Bridge Relayer->>Rialto: Here is the finality proof for 5 [`finalize_header()`]. + end + rect rgb(218, 195, 244) + Bridge Relayer->>+Millau: Do you have any messages for me to deliver (at 5)? + Millau-->>-Bridge Relayer: Yes, here they are. + Bridge Relayer->>+Rialto: I have some new messages for you [`receive_messages_proof()`]. + Rialto->>Rialto: Validate and Dispatch Message. + Rialto->>rEve: Transfer(5 RLT) from r(mDave). + Rialto-->>-Bridge Relayer: Event(Message Succesfully Dispatched). + Bridge Relayer->>Millau: I sent your message, can I get paid now [`receive_messages_delivery_proof`]? + Millau-->>Bridge Relayer: Yes, here you go $$$. + Bridge Relayer ->>Rialto: These messages are confirmed now, feel free to clean up. + end +
+ + + + diff --git a/docs/send-message.md b/docs/send-message.md new file mode 100644 index 00000000000..6984c56d67f --- /dev/null +++ b/docs/send-message.md @@ -0,0 +1,131 @@ +# How to send messages + +The Substrate-to-Substrate relay comes with a command line interface (CLI) which is implemented +by the `substrate-relay` binary. + +``` +Substrate-to-Substrate relay + +USAGE: + substrate-relay + +FLAGS: + -h, --help + Prints help information + + -V, --version + Prints version information + + +SUBCOMMANDS: + help Prints this message or the help of the given subcommand(s) + init-bridge Initialize on-chain bridge pallet with current header data + relay-headers Start headers relay between two chains + relay-messages Start messages relay between two chains + send-message Send custom message over the bridge +``` +The relay related commands `relay-headers` and `relay-messages` are basically continously running a +sync loop between the `Millau` and `Rialto` chains. The `init-bridge` command submitts initialization +transactions. An initialization transaction brings an initial header and authorities set from a source +chain to a target chain. The header synchronization then starts from that header. + +For sending custom messages over an avialable bridge, the `send-message` command is used. + +``` +Send custom message over the bridge. + +Allows interacting with the bridge by sending messages over `Messages` component. The message is being sent to the +source chain, delivered to the target chain and dispatched there. + +USAGE: + substrate-relay send-message + +FLAGS: + -h, --help Prints help information + -V, --version Prints version information + +SUBCOMMANDS: + help Prints this message or the help of the given subcommand(s) + millau-to-rialto Submit message to given Millau -> Rialto lane + rialto-to-millau Submit message to given Rialto -> Millau lane + +``` +Messages are send from a source chain to a target chain using a so called `message lane`. Message lanes handle +both, message transport and message dispatch. There is one command for submitting a message to each of the two +available bridges, namely `millau-to-rialto` and `rialto-to-millau`. + +Submitting a message requires a number of arguments to be provided. Those arguments are essentially the same +for both submit message commands, hence only the output for `millau-to-rialto` is shown below. + +``` +Submit message to given Millau -> Rialto lane + +USAGE: + substrate-relay send-message millau-to-rialto [OPTIONS] --lane --source-host --source-port --source-signer --origin --target-signer + +FLAGS: + -h, --help Prints help information + -V, --version Prints version information + +OPTIONS: + --fee + Delivery and dispatch fee. If not passed, determined automatically + + --lane Hex-encoded lane id + --source-host Connect to Source node at given host + --source-port Connect to Source node websocket server at given port + --source-signer + The SURI of secret key to use when transactions are submitted to the Source node + + --source-signer-password + The password for the SURI of secret key to use when transactions are submitted to the Source node + + --origin + The origin to use when dispatching the message on the target chain [possible values: Target, Source] + + --target-signer + The SURI of secret key to use when transactions are submitted to the Target node + + --target-signer-password + The password for the SURI of secret key to use when transactions are submitted to the Target node + + +SUBCOMMANDS: + help Prints this message or the help of the given subcommand(s) + remark Make an on-chain remark (comment) + transfer Transfer the specified `amount` of native tokens to a particular `recipient` + +``` +As can be seen from the output, there are two types of messages available: `remark` and `transfer`. +A remark is some opaque message which will be placed on-chain. For basic testing, a remark is +the easiest to go with. + +Usage of the arguments is best explained with an example. Below you can see, how a remark +would look like: + +``` +substrate-relay send-message millau-to-rialto \ + --source-host=127.0.0.1 \ + --source-port=10946 \ + --source-signer=//Dave \ + --target-signer=//Dave \ + --lane=00000000 \ + --origin Target \ + remark +``` +Messages are basically regular transactions. That means, they have to be signed. In order +to send a message, you have to control an account private key on both, the source and +the target chain. Those accounts are specified using the `--source-signer` and `--target-signer` +arguments in the example above. + +Message delivery and dispatch requires a fee to be paid. In the example above, we have not +specified the `--fee` argument. Hence, the fee will be estimated automatically. Note that +in order to pay the fee, the message sender account has to have sufficient funds available. + +The `--origin` argument allows to denote under which authority the message will be dispatched +on the target chain. Accepted values are `Target` and `Source`. + +Although not strictly necessary, it is recommended, to use one of the well-known development +accounts (`Alice`, `Bob`, `Charlie`, `Dave`, `Eve`) for message sending. Those accounts are +endowed with funds for fee payment. In addtion, the development `Seed URI` syntax +(like `//Dave`) for the signer can be used, which will remove the need for a password. diff --git a/docs/testing-scenarios.md b/docs/testing-scenarios.md new file mode 100644 index 00000000000..343720524ec --- /dev/null +++ b/docs/testing-scenarios.md @@ -0,0 +1,221 @@ +# Testing Scenarios + +In the scenarios, for simplicity, we call the chains Kusama (KSM token) and Polkadot (DOT token), +but they should be applicable to any other chains. The first scenario has detailed description about +the entire process (also see the [sequence diagram](./scenario1.html)). Other scenarios only contain +a simplified interaction focusing on things that are unique for that particular scenario. + +Notation: +- kX - user X interacting with Kusama chain. +- `k(kX)` - Kusama account id of user kX (native account id; usable on Kusama) +- `p(kX)` - Polkadot account id of user kX (account id derived from `k(kX)` usable on Polkadot) +- [Kusama] ... - Interaction happens on Kusama (e.g. the user interacts with Kusama chain) +- [Polkadot] ... - Interaction happens on Polkadot + +Basic Scenarios +=========================== + +Scenario 1: Kusama's Alice receiving & spending DOTs +--------------------------- + +Kusama's Alice (kAlice) receives 5 DOTs from Polkadot's Bob (pBob) and sends half of them to +kCharlie. + +1. Generate kAlice's DOT address (`p(kAlice)`). + See function: + + ```rust + bp_runtime::derive_account_id(b"pdot", kAlice) + ``` + + or: + + ```rust + let hash = bp_polkadot::derive_kusama_account_id(kAlice); + let p_kAlice = bp_polkadot::AccountIdConverter::convert(hash); + ``` + +2. [Polkadot] pBob transfers 5 DOTs to `p(kAlice)` + 1. Creates & Signs a transaction with `Call::Transfer(..)` + 1. It is included in block. + 1. kAlice observers Polkadot chain to see her balance at `p(kAlice)` updated. + +3. [Kusama] kAlice sends 2.5 DOTs to `p(kCharlie)` + 1. kAlice prepares: + ```rust + let call = polkadot::Call::Balances(polkadot::Balances::Transfer(p(kCharlie), 2.5DOT)).encode(); + let weight = call.get_dispatch_info().weight; + ``` + + 1. kAlice prepares Kusama transaction: + ```rust + kusama::Call::Messages::::send_message( + // dot-transfer-lane (truncated to 4bytes) + lane_id, + payload: MessagePayload { + // Get from current polkadot runtime (kind of hardcoded) + spec_version: 1, + // kAlice should know the exact dispatch weight of the call on the target + // source verifies: at least to cover call.length() and below max weight + weight, + // simply bytes, we don't know anything about that on the source chain + call, + // origin that should be used during dispatch on the target chain + origin: CallOrigin::SourceAccount(kAlice), + }, + delivery_and_dispatch_fee: { + (single_message_delivery_weight + // source weight = X * target weight + + convert_target_weight_to_source_weight(weight) + + confirmation_transaction_weight + ) + // This uses an on-chain oracle to convert weights of the target chain to source fee + * weight_to_fee + // additional reward for the relayer (pallet parameter) + + relayers_fee + }, + ) + ``` + + 1. [Kusama] kAlice sends Kusama transaction with the above `Call` and pays regular fees. The + dispatch additionally reservers target-chain delivery and dispatch fees (including relayer's + reward). + +4. [Kusama] kAlice's transaction is included in block `B1` + +### Syncing headers loop + +5. Relayer sees that `B1` has not yet been delivered to the target chain. + [Sync loop code](https://github.com/paritytech/parity-bridges-common/blob/8b327a94595c4a6fae6d7866e24ecf2390501e32/relays/headers-relay/src/sync_loop.rs#L199). + +1. Relayer prepares transaction which delivers `B1` and with all of the missing + ancestors to the target chain (one header per transaction). + +1. After the transaction is succesfully dispatched the Polkadot on-chain light client of the Kusama + chain learns about block `B1` - it is stored in the on-chain storage. + +### Syncing finality loop + +8. Relayer is subscribed to finality events on Kusama. Relayer gets a finality notification for + block `B3`. + +1. The header sync informs the target chain about `B1..B3` blocks (see point 6). + +1. Relayer learns about missing finalization of `B1..B3` on the target chain, see + [finality maintenance code](https://github.com/paritytech/parity-bridges-common/blob/8b327a94595c4a6fae6d7866e24ecf2390501e32/relays/substrate/src/headers_maintain.rs#L107). + +1. Relayer submits justification for `B3` to the target chain (`finalize_header`). + See [#421](https://github.com/paritytech/parity-bridges-common/issues/421) for multiple + authority set changes support in Relayer (i.e. what block the target chain expects, not only + what I have). + + Relayer is doing two things: + - syncing on demand (what blocks miss finality) + - and syncing as notifications are received (recently finalized on-chain) + +1. Eventually Polkadot on-chain light client of Kusama learns about finality of `B1`. + +### Syncing messages loop + +13. The relayer checks the on-chain storage (last finalized header on the source, best header on the + target): + - Kusama outbound lane + - Polkadot inbound lane + Lanes contains `latest_generated_nonce` and `latest_received_nonce` respectively. The relayer + syncs messages between that range. + +1. The relayer gets a proof for every message in that range (using the RPC of messages module) + +1. The relayer creates a message delivery transaction (but it has weight, size, and count limits). + The count limit is there to make the loop of delivery code bounded. + ```rust + receive_message_proof( + relayer_id, // account id of the source chain + proof, // messages + proofs (hash of source block `B1`, nonces, lane_id + storage proof) + dispatch_weight // relayer declares how much it will take to dispatch all messages in that transaction, + ) + ``` + The `proof` can also contain an update of outbound lane state of source chain, which indicates + the delivery confirmation of these messages and reward payment, so that the target chain can + truncate its unpayed rewards vector. + + The target chain stores `relayer_ids` that delivered messages because the relayer can generate + a storage proof to show that they did indeed deliver those messages. The reward is paid on the + source chain and we inform the target chain about that fact so it can prune these `relayer_ids`. + + It's totally fine if there are no messages, and we only include the reward payment proof + when calling that function. + +1. 🥳 the message is now delivered and dispatched on the target chain! + +1. The relayer now needs to confirm the delivery to claim her payment and reward on the source + chain. + +1. The relayer creates a transaction on the source chain with call: + + ```rust + receive_messages_delivery_proof( + proof, // hash of the finalized target chain block, lane_id, storage proof + ) + ``` + +### UI challenges + +- The UI should warn before (or prevent) sending to `k(kCharlie)`! + + +Scenario 2: Kusama's Alice nominating validators with her DOTs +--------------------------- + +kAlice receives 10 DOTs from pBob and nominates `p(pCharlie)` and `p(pDave)`. + +1. Generate kAlice's DOT address (`p(kAlice)`) +2. [Polkadot] pBob transfers 5 DOTs to `p(kAlice)` +3. [Kusama] kAlice sends a batch transaction: + - `staking::Bond` transaction to create stash account choosing `p(kAlice)` as the controller account. + - `staking::Nominate(vec![p(pCharlie)])` to nominate pCharlie using the controller account. + + +Scenario 3: Kusama Treasury receiving & spending DOTs +--------------------------- + +pBob sends 15 DOTs to Kusama Treasury which Kusama Governance decides to transfer to kCharlie. + +1. Generate source account for the treasury (`kTreasury`). +2. [Polkadot] pBob tarnsfers 15 DOTs to `p(kTreasury)`. +2. [Kusama] Send a governance proposal to send a bridge message which transfers funds to `p(kCharlie)`. +3. [Kusama] Dispatch the governance proposal using `kTreasury` account id. + +Extra scenarios +=========================== + +Scenario 4: Kusama's Alice setting up 1-of-2 multi-sig to spend from either Kusama or Polkadot +--------------------------- + +Assuming `p(pAlice)` has at least 7 DOTs already. + +1. Generate multisig account id: `pMultiSig = multi_account_id(&[p(kAlice), p(pAlice)], 1)`. +2. [Kusama] Transfer 7 DOTs to `pMultiSig` using `TargetAccount` origin of `pAlice`. +3. [Kusama] Transfer 2 DOTs to `p(kAlice)` from the multisig: + - Send `multisig::as_multi_threshold_1(vec![p(pAlice)], balances::Transfer(p(kAlice), 2))` + +Scenario 5: Kusama Treasury staking & nominating validators with DOTs +--------------------------- + +Scenario 6: Kusama Treasury voting in Polkadot's democracy proposal +--------------------------- + +Potentially interesting scenarios +=========================== + +Scenario 7: Polkadot's Bob spending his DOTs by using Kusama chain +--------------------------- + +We can assume he holds KSM. Problem: he can pay fees, but can't really send (sign) a transaction? +Shall we support some kind of dispatcher? + +Scenario 8: Kusama Governance taking over Kusama's Alice DOT holdings +--------------------------- + +We use `SourceRoot` call to transfer her's DOTs to Kusama treasury. Source chain root +should also be able to send messages as `CallOrigin::SourceAccount(Alice)` though. diff --git a/fuzz/storage-proof/Cargo.lock b/fuzz/storage-proof/Cargo.lock new file mode 100644 index 00000000000..1c40a1a0d3b --- /dev/null +++ b/fuzz/storage-proof/Cargo.lock @@ -0,0 +1,2663 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.7", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" + +[[package]] +name = "arbitrary" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c38b6b6b79f671c25e1a3e785b7b82d7562ffc9cd3efdc98627e5668a2472490" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "async-trait" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1489fcb93a5bb47da0462ca93ad252ad6af2145cce58d10d46a83931ba9f016b" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388" +dependencies = [ + "digest 0.10.3", +] + +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +dependencies = [ + "arrayvec 0.4.12", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "bp-runtime" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "hash-db", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", +] + +[[package]] +name = "bumpalo" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" + +[[package]] +name = "byte-slice-cast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c5fdd0166095e1d463fc6cc01aa8ce547ad77a4e84d42eb6762b084e28067e" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "winapi", +] + +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array 0.14.4", + "rand_core 0.6.1", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ccfd8c0ee4cce11e45b3fd6f9d5e69e0cc62912aa6a0cb1bf4617b0eba5a12f" +dependencies = [ + "generic-array 0.14.4", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434e1720189a637d44fe464f4df1e6eb900b4835255b14354497c78af37d9bb8" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f627126b946c25a4638eec0ea634fc52506dea98db118aae985118ce7c3d723f" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid", +] + +[[package]] +name = "derive_more" +version = "0.99.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "dyn-clonable" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +dependencies = [ + "dyn-clonable-impl", + "dyn-clone", +] + +[[package]] +name = "dyn-clonable-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dyn-clone" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" + +[[package]] +name = "ecdsa" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c66a534cbb46ab4ea03477eae19d5c22c01da8258030280b7bd9d8433fb6ef" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.0.2", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "elliptic-curve" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "ff", + "generic-array 0.14.4", + "group", + "rand_core 0.6.1", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "environmental" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b91989ae21441195d7d9b9993a2f9295c7e1a8c96255d8b729accddc124797" + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "ff" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924" +dependencies = [ + "rand_core 0.6.1", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.2", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "frame-metadata" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" +dependencies = [ + "cfg-if", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "frame-support" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "bitflags", + "frame-metadata", + "frame-support-procedural", + "impl-trait-for-tuples", + "k256", + "log", + "once_cell", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic", + "sp-core", + "sp-core-hashing-proc-macro", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-state-machine", + "sp-std", + "sp-tracing", + "tt-call", +] + +[[package]] +name = "frame-support-procedural" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "Inflector", + "frame-support-procedural-tools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-support-procedural-tools" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "frame-support-procedural-tools-derive", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-support-procedural-tools-derive" +version = "3.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-system" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "frame-support", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-version", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-executor" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" + +[[package]] +name = "futures-macro" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" + +[[package]] +name = "group" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" +dependencies = [ + "ff", + "rand_core 0.6.1", + "subtle", +] + +[[package]] +name = "hash-db" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" +dependencies = [ + "ahash", +] + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array 0.14.4", + "hmac 0.8.1", +] + +[[package]] +name = "honggfuzz" +version = "0.5.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea09577d948a98a5f59b7c891e274c4fb35ad52f67782b3d0cb53b9c05301f1" +dependencies = [ + "arbitrary", + "lazy_static", + "memmap", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-serde" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b47ca4d2b6931707a55fce5cf66aff80e2178c8b63bbb4ecb5695cbc870ddf6f" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + +[[package]] +name = "js-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sec1", +] + +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "libsecp256k1" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0452aac8bab02242429380e9b2f94ea20cea2b37e2c1777a1358799bbe97f37" +dependencies = [ + "arrayref", + "base64", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.2", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "memory-db" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6566c70c1016f525ced45d7b7f97730a2bafb037c788211d0c186ef5b2189f0a" +dependencies = [ + "hash-db", + "hashbrown", + "parity-util-mem", +] + +[[package]] +name = "memory_units" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" + +[[package]] +name = "merlin" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "miniz_oxide" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +dependencies = [ + "adler", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-format" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465" +dependencies = [ + "arrayvec 0.4.12", + "itoa 0.4.7", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parity-scale-codec" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8b44461635bbb1a0300f100a841e571e7d919c81c73075ef5d152ffdb521066" +dependencies = [ + "arrayvec 0.7.2", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c45ed1f39709f5a89338fab50e59816b2e8815f5bb58276e7ddf9afd495f73f8" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "parity-util-mem" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c32561d248d352148124f036cac253a644685a21dc9fea383eb4907d7bd35a8f" +dependencies = [ + "cfg-if", + "hashbrown", + "impl-trait-for-tuples", + "parity-util-mem-derive", + "parking_lot", + "primitive-types", + "winapi", +] + +[[package]] +name = "parity-util-mem-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" +dependencies = [ + "proc-macro2", + "syn", + "synstructure", +] + +[[package]] +name = "parity-wasm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "paste" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" + +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac 0.8.0", +] + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac 0.11.1", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "primitive-types" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-serde", + "scale-info", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18519b42a40024d661e1714153e9ad0c3de27cd495760ceb09710920f1098b1e" +dependencies = [ + "libc", + "rand_chacha 0.3.0", + "rand_core 0.6.1", + "rand_hc 0.3.1", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.1", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" +dependencies = [ + "getrandom 0.2.7", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core 0.6.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300f2a835d808734ee295d45007adacb9ebb29dd3ae2424acfa17930cae541da" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c38e3aecd2b21cb3959637b883bb3714bc7e43f0268b9a29d3743ee3e55cdd2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +dependencies = [ + "byteorder", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "rfc6979" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +dependencies = [ + "crypto-bigint", + "hmac 0.11.0", + "zeroize", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "scale-info" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c46be926081c9f4dd5dd9b6f1d3e3229f2360bc6502dd8836f84a93b7c75e99a" +dependencies = [ + "bitvec", + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "serde", +] + +[[package]] +name = "scale-info-derive" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e334bb10a245e28e5fd755cabcafd96cfcd167c99ae63a46924ca8d8703a3c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "schnorrkel" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "curve25519-dalek 2.1.2", + "getrandom 0.1.16", + "merlin", + "rand 0.7.3", + "rand_core 0.5.1", + "sha2 0.8.2", + "subtle", + "zeroize", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sec1" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +dependencies = [ + "der", + "generic-array 0.14.4", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c42e6f1735c5f00f51e43e28d6634141f2bcad10931b2609ddd74a86d751260" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" +dependencies = [ + "cc", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "serde" +version = "1.0.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +dependencies = [ + "itoa 1.0.2", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "sha3" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881bf8156c87b6301fc5ca6b27f11eeb2761224c7081e69b409d5a1951a70c86" +dependencies = [ + "digest 0.10.3", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79c719719ee05df97490f80a45acfc99e5a30ce98a1e4fb67aee422745ae14e3" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signature" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" +dependencies = [ + "digest 0.9.0", + "rand_core 0.6.1", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "sp-application-crypto" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-std", +] + +[[package]] +name = "sp-arithmetic" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-debug-derive", + "sp-std", + "static_assertions", +] + +[[package]] +name = "sp-core" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "base58", + "bitflags", + "blake2-rfc", + "byteorder", + "dyn-clonable", + "ed25519-dalek", + "futures", + "hash-db", + "hash256-std-hasher", + "hex", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log", + "merlin", + "num-traits", + "parity-scale-codec", + "parity-util-mem", + "parking_lot", + "primitive-types", + "rand 0.7.3", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "wasmi", + "zeroize", +] + +[[package]] +name = "sp-core-hashing" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "blake2", + "byteorder", + "digest 0.10.3", + "sha2 0.10.2", + "sha3", + "sp-std", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "proc-macro2", + "quote", + "sp-core-hashing", + "syn", +] + +[[package]] +name = "sp-debug-derive" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-externalities" +version = "0.12.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std", + "sp-storage", +] + +[[package]] +name = "sp-inherents" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "async-trait", + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std", + "thiserror", +] + +[[package]] +name = "sp-io" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "futures", + "hash-db", + "libsecp256k1", + "log", + "parity-scale-codec", + "parking_lot", + "secp256k1", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-trie", + "sp-wasm-interface", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-keystore" +version = "0.12.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "async-trait", + "futures", + "merlin", + "parity-scale-codec", + "parking_lot", + "schnorrkel", + "sp-core", + "sp-externalities", + "thiserror", +] + +[[package]] +name = "sp-panic-handler" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + +[[package]] +name = "sp-runtime" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "parity-util-mem", + "paste", + "rand 0.7.3", + "scale-info", + "serde", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-std", +] + +[[package]] +name = "sp-runtime-interface" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std", + "sp-storage", + "sp-tracing", + "sp-wasm-interface", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-staking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-state-machine" +version = "0.12.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "hash-db", + "log", + "num-traits", + "parity-scale-codec", + "parking_lot", + "rand 0.7.3", + "smallvec", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-std", + "sp-trie", + "thiserror", + "tracing", + "trie-root", +] + +[[package]] +name = "sp-std" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" + +[[package]] +name = "sp-storage" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive", + "sp-std", +] + +[[package]] +name = "sp-tracing" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "parity-scale-codec", + "sp-std", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sp-trie" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "hash-db", + "memory-db", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-std", + "thiserror", + "trie-db", + "trie-root", +] + +[[package]] +name = "sp-version" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "parity-wasm", + "scale-info", + "serde", + "sp-core-hashing-proc-macro", + "sp-runtime", + "sp-std", + "sp-version-proc-macro", + "thiserror", +] + +[[package]] +name = "sp-version-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "parity-scale-codec", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-wasm-interface" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std", + "wasmi", +] + +[[package]] +name = "ss58-registry" +version = "1.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ef98aedad3dc52e10995e7ed15f1279e11d4da35795f5dac7305742d0feb66" +dependencies = [ + "Inflector", + "num-format", + "proc-macro2", + "quote", + "serde", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "storage-proof-fuzzer" +version = "0.1.0" +dependencies = [ + "bp-runtime", + "env_logger", + "honggfuzz", + "log", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", +] + +[[package]] +name = "substrate-bip39" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" +dependencies = [ + "hmac 0.11.0", + "pbkdf2 0.8.0", + "schnorrkel", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "subtle" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tiny-bip39" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +dependencies = [ + "anyhow", + "hmac 0.8.1", + "once_cell", + "pbkdf2 0.4.0", + "rand 0.7.3", + "rustc-hash", + "sha2 0.9.9", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tinyvec" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "trie-db" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32d034c0d3db64b43c31de38e945f15b40cd4ca6d2dcfc26d4798ce8de4ab83" +dependencies = [ + "hash-db", + "hashbrown", + "log", + "rustc-hex", + "smallvec", +] + +[[package]] +name = "trie-root" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891" +dependencies = [ + "hash-db", +] + +[[package]] +name = "tt-call" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e66dcbec4290c69dd03c57e76c2469ea5c7ce109c6dd4351c13055cf71ea055" + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "digest 0.10.3", + "rand 0.8.2", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "uint" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11fe9a9348741cf134085ad57c249508345fe16411b3d7fb4ff2da2f1d6382e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" + +[[package]] +name = "unicode-normalization" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" + +[[package]] +name = "wasmi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca00c5147c319a8ec91ec1a0edbec31e566ce2c9cc93b3f9bb86a9efd0eb795d" +dependencies = [ + "downcast-rs", + "libc", + "memory_units", + "num-rational", + "num-traits", + "parity-wasm", + "wasmi-validation", +] + +[[package]] +name = "wasmi-validation" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "165343ecd6c018fc09ebcae280752702c9a2ef3e6f8d02f1cfcbdb53ef6d7937" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "wyz" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" +dependencies = [ + "tap", +] + +[[package]] +name = "zeroize" +version = "1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20b578acffd8516a6c3f2a1bdefc1ec37e547bb4e0fb8b6b01a4cafc886b4442" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/fuzz/storage-proof/Cargo.toml b/fuzz/storage-proof/Cargo.toml new file mode 100644 index 00000000000..b406054bc6e --- /dev/null +++ b/fuzz/storage-proof/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "storage-proof-fuzzer" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +honggfuzz = "0.5.54" +log = "0.4.0" +env_logger = "0.8.3" + +# Bridge Dependencies + +bp-runtime = { path = "../../primitives/runtime" } + +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/fuzz/storage-proof/README.md b/fuzz/storage-proof/README.md new file mode 100644 index 00000000000..1eeec7562a9 --- /dev/null +++ b/fuzz/storage-proof/README.md @@ -0,0 +1,34 @@ +# Storage Proof Fuzzer + +## How to run? + +Install dependencies: +``` +$ sudo apt install build-essential binutils-dev libunwind-dev +``` +or on nix: +``` +$ nix-shell -p honggfuzz +``` + +Install `cargo hfuzz` plugin: +``` +$ cargo install honggfuzz +``` + +Run: +``` +$ cargo hfuzz run storage-proof-fuzzer +``` + +Use `HFUZZ_RUN_ARGS` to customize execution: +``` +# 1 second of timeout +# use 12 fuzzing thread +# be verbose +# stop after 1000000 fuzzing iteration +# exit upon crash +HFUZZ_RUN_ARGS="-t 1 -n 12 -v -N 1000000 --exit_upon_crash" cargo hfuzz run example +``` + +More details in the [official documentation](https://docs.rs/honggfuzz/0.5.52/honggfuzz/#about-honggfuzz). diff --git a/fuzz/storage-proof/src/main.rs b/fuzz/storage-proof/src/main.rs new file mode 100644 index 00000000000..26564372cb0 --- /dev/null +++ b/fuzz/storage-proof/src/main.rs @@ -0,0 +1,77 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Storage Proof Checker fuzzer. + +#![warn(missing_docs)] + +use honggfuzz::fuzz; +// Logic for checking Substrate storage proofs. + +use sp_core::{Blake2Hasher, H256}; +use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend}; +use sp_std::vec::Vec; +use sp_trie::StorageProof; +use std::collections::HashMap; + +fn craft_known_storage_proof(input_vec: Vec<(Vec, Vec)>) -> (H256, StorageProof) { + let storage_proof_vec = + vec![(None, input_vec.iter().map(|x| (x.0.clone(), Some(x.1.clone()))).collect())]; + log::info!("Storage proof vec {:?}", storage_proof_vec); + let state_version = sp_runtime::StateVersion::default(); + let backend = >::from((storage_proof_vec, state_version)); + let root = backend.storage_root(std::iter::empty(), state_version).0; + let vector_element_proof = + prove_read(backend, input_vec.iter().map(|x| x.0.as_slice())).unwrap(); + (root, vector_element_proof) +} + +fn transform_into_unique(input_vec: Vec<(Vec, Vec)>) -> Vec<(Vec, Vec)> { + let mut output_hashmap = HashMap::new(); + let mut output_vec = Vec::new(); + for key_value_pair in input_vec { + output_hashmap.insert(key_value_pair.0, key_value_pair.1); //Only 1 value per key + } + for (key, val) in output_hashmap.iter() { + output_vec.push((key.clone(), val.clone())); + } + output_vec +} + +fn run_fuzzer() { + fuzz!(|input_vec: Vec<(Vec, Vec)>| { + if input_vec.is_empty() { + return + } + let unique_input_vec = transform_into_unique(input_vec); + let (root, craft_known_storage_proof) = craft_known_storage_proof(unique_input_vec.clone()); + let checker = + >::new(root, craft_known_storage_proof) + .expect("Valid proof passed; qed"); + for key_value_pair in unique_input_vec { + log::info!("Reading value for pair {:?}", key_value_pair); + assert_eq!(checker.read_value(&key_value_pair.0), Ok(Some(key_value_pair.1.clone()))); + } + }) +} + +fn main() { + env_logger::init(); + + loop { + run_fuzzer(); + } +} diff --git a/modules/beefy/Cargo.toml b/modules/beefy/Cargo.toml new file mode 100644 index 00000000000..25905126d53 --- /dev/null +++ b/modules/beefy/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "pallet-bridge-beefy" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +log = { version = "0.4.14", default-features = false } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true } + +# Bridge Dependencies + +bp-beefy = { path = "../../primitives/beefy", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } + +# Substrate Dependencies + +beefy-merkle-tree = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +primitive-types = { version = "0.12.0", default-features = false, features = ["impl-codec"] } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master" } +mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.3.2" } +pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master" } +rand = "0.8" +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } +bp-test-utils = { path = "../../primitives/test-utils" } + +[features] +default = ["std"] +std = [ + "beefy-merkle-tree/std", + "bp-beefy/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "frame-system/std", + "log/std", + "primitive-types/std", + "scale-info/std", + "serde", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/modules/beefy/src/lib.rs b/modules/beefy/src/lib.rs new file mode 100644 index 00000000000..5ef58c5a3cb --- /dev/null +++ b/modules/beefy/src/lib.rs @@ -0,0 +1,644 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! BEEFY bridge pallet. +//! +//! This pallet is an on-chain BEEFY light client for Substrate-based chains that are using the +//! following pallets bundle: `pallet-mmr`, `pallet-beefy` and `pallet-beefy-mmr`. +//! +//! The pallet is able to verify MMR leaf proofs and BEEFY commitments, so it has access +//! to the following data of the bridged chain: +//! +//! - header hashes +//! - changes of BEEFY authorities +//! - extra data of MMR leafs +//! +//! Given the header hash, other pallets are able to verify header-based proofs +//! (e.g. storage proofs, transaction inclusion proofs, etc.). + +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_beefy::{ChainWithBeefy, InitializationData}; +use sp_std::{boxed::Box, prelude::*}; + +// Re-export in crate namespace for `construct_runtime!` +pub use pallet::*; + +mod utils; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod mock_chain; + +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-beefy"; + +/// Configured bridged chain. +pub type BridgedChain = >::BridgedChain; +/// Block number, used by configured bridged chain. +pub type BridgedBlockNumber = bp_runtime::BlockNumberOf>; +/// Block hash, used by configured bridged chain. +pub type BridgedBlockHash = bp_runtime::HashOf>; + +/// Pallet initialization data. +pub type InitializationDataOf = + InitializationData, bp_beefy::MmrHashOf>>; +/// BEEFY commitment hasher, used by configured bridged chain. +pub type BridgedBeefyCommitmentHasher = bp_beefy::BeefyCommitmentHasher>; +/// BEEFY validator id, used by configured bridged chain. +pub type BridgedBeefyAuthorityId = bp_beefy::BeefyAuthorityIdOf>; +/// BEEFY validator set, used by configured bridged chain. +pub type BridgedBeefyAuthoritySet = bp_beefy::BeefyAuthoritySetOf>; +/// BEEFY authority set, used by configured bridged chain. +pub type BridgedBeefyAuthoritySetInfo = bp_beefy::BeefyAuthoritySetInfoOf>; +/// BEEFY signed commitment, used by configured bridged chain. +pub type BridgedBeefySignedCommitment = bp_beefy::BeefySignedCommitmentOf>; +/// MMR hashing algorithm, used by configured bridged chain. +pub type BridgedMmrHashing = bp_beefy::MmrHashingOf>; +/// MMR hashing output type of `BridgedMmrHashing`. +pub type BridgedMmrHash = bp_beefy::MmrHashOf>; +/// The type of the MMR leaf extra data used by the configured bridged chain. +pub type BridgedBeefyMmrLeafExtra = bp_beefy::BeefyMmrLeafExtraOf>; +/// BEEFY MMR proof type used by the pallet +pub type BridgedMmrProof = bp_beefy::MmrProofOf>; +/// MMR leaf type, used by configured bridged chain. +pub type BridgedBeefyMmrLeaf = bp_beefy::BeefyMmrLeafOf>; +/// Imported commitment data, stored by the pallet. +pub type ImportedCommitment = bp_beefy::ImportedCommitment< + BridgedBlockNumber, + BridgedBlockHash, + BridgedMmrHash, +>; + +/// Some high level info about the imported commitments. +#[derive(codec::Encode, codec::Decode, scale_info::TypeInfo)] +pub struct ImportedCommitmentsInfoData { + /// Best known block number, provided in a BEEFY commitment. However this is not + /// the best proven block. The best proven block is this block's parent. + best_block_number: BlockNumber, + /// The head of the `ImportedBlockNumbers` ring buffer. + next_block_number_index: u32, +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use bp_runtime::{BasicOperatingMode, OwnedBridgeModule}; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The upper bound on the number of requests allowed by the pallet. + /// + /// A request refers to an action which writes a header to storage. + /// + /// Once this bound is reached the pallet will reject all commitments + /// until the request count has decreased. + #[pallet::constant] + type MaxRequests: Get; + + /// Maximal number of imported commitments to keep in the storage. + /// + /// The setting is there to prevent growing the on-chain state indefinitely. Note + /// the setting does not relate to block numbers - we will simply keep as much items + /// in the storage, so it doesn't guarantee any fixed timeframe for imported commitments. + #[pallet::constant] + type CommitmentsToKeep: Get; + + /// The chain we are bridging to here. + type BridgedChain: ChainWithBeefy; + } + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet { + fn on_initialize(_n: T::BlockNumber) -> frame_support::weights::Weight { + >::mutate(|count| *count = count.saturating_sub(1)); + + Weight::from_ref_time(0) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + } + + impl, I: 'static> OwnedBridgeModule for Pallet { + const LOG_TARGET: &'static str = LOG_TARGET; + type OwnerStorage = PalletOwner; + type OperatingMode = BasicOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + } + + #[pallet::call] + impl, I: 'static> Pallet + where + BridgedMmrHashing: 'static + Send + Sync, + { + /// Initialize pallet with BEEFY authority set and best known finalized block number. + #[pallet::weight((T::DbWeight::get().reads_writes(2, 3), DispatchClass::Operational))] + pub fn initialize( + origin: OriginFor, + init_data: InitializationDataOf, + ) -> DispatchResult { + Self::ensure_owner_or_root(origin)?; + + let is_initialized = >::exists(); + ensure!(!is_initialized, >::AlreadyInitialized); + + log::info!(target: LOG_TARGET, "Initializing bridge BEEFY pallet: {:?}", init_data); + Ok(initialize::(init_data)?) + } + + /// Change `PalletOwner`. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { + >::set_owner(origin, new_owner) + } + + /// Halt or resume all pallet operations. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_operating_mode( + origin: OriginFor, + operating_mode: BasicOperatingMode, + ) -> DispatchResult { + >::set_operating_mode(origin, operating_mode) + } + + /// Submit a commitment generated by BEEFY authority set. + /// + /// It will use the underlying storage pallet to fetch information about the current + /// authority set and best finalized block number in order to verify that the commitment + /// is valid. + /// + /// If successful in verification, it will update the underlying storage with the data + /// provided in the newly submitted commitment. + #[pallet::weight(0)] + pub fn submit_commitment( + origin: OriginFor, + commitment: BridgedBeefySignedCommitment, + validator_set: BridgedBeefyAuthoritySet, + mmr_leaf: Box>, + mmr_proof: BridgedMmrProof, + ) -> DispatchResult + where + BridgedBeefySignedCommitment: Clone, + { + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + ensure_signed(origin)?; + + ensure!(Self::request_count() < T::MaxRequests::get(), >::TooManyRequests); + + // Ensure that the commitment is for a better block. + let commitments_info = + ImportedCommitmentsInfo::::get().ok_or(Error::::NotInitialized)?; + ensure!( + commitment.commitment.block_number > commitments_info.best_block_number, + Error::::OldCommitment + ); + + // Verify commitment and mmr leaf. + let current_authority_set_info = CurrentAuthoritySetInfo::::get(); + let mmr_root = utils::verify_commitment::( + &commitment, + ¤t_authority_set_info, + &validator_set, + )?; + utils::verify_beefy_mmr_leaf::(&mmr_leaf, mmr_proof, mmr_root)?; + + // Update request count. + RequestCount::::mutate(|count| *count += 1); + // Update authority set if needed. + if mmr_leaf.beefy_next_authority_set.id > current_authority_set_info.id { + CurrentAuthoritySetInfo::::put(mmr_leaf.beefy_next_authority_set); + } + + // Import commitment. + let block_number_index = commitments_info.next_block_number_index; + let to_prune = ImportedBlockNumbers::::try_get(block_number_index); + ImportedCommitments::::insert( + commitment.commitment.block_number, + ImportedCommitment:: { + parent_number_and_hash: mmr_leaf.parent_number_and_hash, + mmr_root, + }, + ); + ImportedBlockNumbers::::insert( + block_number_index, + commitment.commitment.block_number, + ); + ImportedCommitmentsInfo::::put(ImportedCommitmentsInfoData { + best_block_number: commitment.commitment.block_number, + next_block_number_index: (block_number_index + 1) % T::CommitmentsToKeep::get(), + }); + if let Ok(old_block_number) = to_prune { + log::debug!( + target: LOG_TARGET, + "Pruning commitment for old block: {:?}.", + old_block_number + ); + ImportedCommitments::::remove(old_block_number); + } + + log::info!( + target: LOG_TARGET, + "Successfully imported commitment for block {:?}", + commitment.commitment.block_number, + ); + + Ok(()) + } + } + + /// The current number of requests which have written to storage. + /// + /// If the `RequestCount` hits `MaxRequests`, no more calls will be allowed to the pallet until + /// the request capacity is increased. + /// + /// The `RequestCount` is decreased by one at the beginning of every block. This is to ensure + /// that the pallet can always make progress. + #[pallet::storage] + #[pallet::getter(fn request_count)] + pub type RequestCount, I: 'static = ()> = StorageValue<_, u32, ValueQuery>; + + /// High level info about the imported commitments. + /// + /// Contains the following info: + /// - best known block number of the bridged chain, finalized by BEEFY + /// - the head of the `ImportedBlockNumbers` ring buffer + #[pallet::storage] + pub type ImportedCommitmentsInfo, I: 'static = ()> = + StorageValue<_, ImportedCommitmentsInfoData>>; + + /// A ring buffer containing the block numbers of the commitments that we have imported, + /// ordered by the insertion time. + #[pallet::storage] + pub(super) type ImportedBlockNumbers, I: 'static = ()> = + StorageMap<_, Identity, u32, BridgedBlockNumber>; + + /// All the commitments that we have imported and haven't been pruned yet. + #[pallet::storage] + pub type ImportedCommitments, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, BridgedBlockNumber, ImportedCommitment>; + + /// The current BEEFY authority set at the bridged chain. + #[pallet::storage] + pub type CurrentAuthoritySetInfo, I: 'static = ()> = + StorageValue<_, BridgedBeefyAuthoritySetInfo, ValueQuery>; + + /// Optional pallet owner. + /// + /// Pallet owner has the right to halt all pallet operations and then resume it. If it is + /// `None`, then there are no direct ways to halt/resume pallet operations, but other + /// runtime methods may still be used to do that (i.e. `democracy::referendum` to update halt + /// flag directly or calling `halt_operations`). + #[pallet::storage] + pub type PalletOwner, I: 'static = ()> = + StorageValue<_, T::AccountId, OptionQuery>; + + /// The current operating mode of the pallet. + /// + /// Depending on the mode either all, or no transactions will be allowed. + #[pallet::storage] + pub type PalletOperatingMode, I: 'static = ()> = + StorageValue<_, BasicOperatingMode, ValueQuery>; + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + /// Optional module owner account. + pub owner: Option, + /// Optional module initialization data. + pub init_data: Option>, + } + + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { owner: None, init_data: None } + } + } + + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + if let Some(ref owner) = self.owner { + >::put(owner); + } + + if let Some(init_data) = self.init_data.clone() { + initialize::(init_data) + .expect("invalid initialization data of BEEFY bridge pallet"); + } else { + // Since the bridge hasn't been initialized we shouldn't allow anyone to perform + // transactions. + >::put(BasicOperatingMode::Halted); + } + } + } + + #[pallet::error] + pub enum Error { + /// The pallet has not been initialized yet. + NotInitialized, + /// The pallet has already been initialized. + AlreadyInitialized, + /// Invalid initial authority set. + InvalidInitialAuthoritySet, + /// There are too many requests for the current window to handle. + TooManyRequests, + /// The imported commitment is older than the best commitment known to the pallet. + OldCommitment, + /// The commitment is signed by unknown validator set. + InvalidCommitmentValidatorSetId, + /// The id of the provided validator set is invalid. + InvalidValidatorSetId, + /// The number of signatures in the commitment is invalid. + InvalidCommitmentSignaturesLen, + /// The number of validator ids provided is invalid. + InvalidValidatorSetLen, + /// There aren't enough correct signatures in the commitment to finalize the block. + NotEnoughCorrectSignatures, + /// MMR root is missing from the commitment. + MmrRootMissingFromCommitment, + /// MMR proof verification has failed. + MmrProofVerificationFailed, + /// The validators are not matching the merkle tree root of the authority set. + InvalidValidatorSetRoot, + /// Error generated by the `OwnedBridgeModule` trait. + BridgeModule(bp_runtime::OwnedBridgeModuleError), + } + + /// Initialize pallet with given parameters. + pub(super) fn initialize, I: 'static>( + init_data: InitializationDataOf, + ) -> Result<(), Error> { + if init_data.authority_set.len == 0 { + return Err(Error::::InvalidInitialAuthoritySet) + } + CurrentAuthoritySetInfo::::put(init_data.authority_set); + + >::put(init_data.operating_mode); + ImportedCommitmentsInfo::::put(ImportedCommitmentsInfoData { + best_block_number: init_data.best_block_number, + next_block_number_index: 0, + }); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use beefy_primitives::mmr::BeefyAuthoritySet; + use bp_runtime::{BasicOperatingMode, OwnedBridgeModuleError}; + use bp_test_utils::generate_owned_bridge_module_tests; + use frame_support::{assert_noop, assert_ok, traits::Get}; + use mock::*; + use mock_chain::*; + use sp_runtime::DispatchError; + + fn next_block() { + use frame_support::traits::OnInitialize; + + let current_number = frame_system::Pallet::::block_number(); + frame_system::Pallet::::set_block_number(current_number + 1); + let _ = Pallet::::on_initialize(current_number); + } + + fn import_header_chain(headers: Vec) { + for header in headers { + if header.commitment.is_some() { + assert_ok!(import_commitment(header)); + } + } + } + + #[test] + fn fails_to_initialize_if_already_initialized() { + run_test_with_initialize(32, || { + assert_noop!( + Pallet::::initialize( + RuntimeOrigin::root(), + InitializationData { + operating_mode: BasicOperatingMode::Normal, + best_block_number: 0, + authority_set: BeefyAuthoritySet { id: 0, len: 1, root: [0u8; 32].into() } + } + ), + Error::::AlreadyInitialized, + ); + }); + } + + #[test] + fn fails_to_initialize_if_authority_set_is_empty() { + run_test(|| { + assert_noop!( + Pallet::::initialize( + RuntimeOrigin::root(), + InitializationData { + operating_mode: BasicOperatingMode::Normal, + best_block_number: 0, + authority_set: BeefyAuthoritySet { id: 0, len: 0, root: [0u8; 32].into() } + } + ), + Error::::InvalidInitialAuthoritySet, + ); + }); + } + + #[test] + fn fails_to_import_commitment_if_halted() { + run_test_with_initialize(1, || { + assert_ok!(Pallet::::set_operating_mode( + RuntimeOrigin::root(), + BasicOperatingMode::Halted + )); + assert_noop!( + import_commitment(ChainBuilder::new(1).append_finalized_header().to_header()), + Error::::BridgeModule(OwnedBridgeModuleError::Halted), + ); + }) + } + + #[test] + fn fails_to_import_commitment_if_too_many_requests() { + run_test_with_initialize(1, || { + let max_requests = <::MaxRequests as Get>::get() as u64; + let mut chain = ChainBuilder::new(1); + for _ in 0..max_requests + 2 { + chain = chain.append_finalized_header(); + } + + // import `max_request` headers + for i in 0..max_requests { + assert_ok!(import_commitment(chain.header(i + 1))); + } + + // try to import next header: it fails because we are no longer accepting commitments + assert_noop!( + import_commitment(chain.header(max_requests + 1)), + Error::::TooManyRequests, + ); + + // when next block is "started", we allow import of next header + next_block(); + assert_ok!(import_commitment(chain.header(max_requests + 1))); + + // but we can't import two headers until next block and so on + assert_noop!( + import_commitment(chain.header(max_requests + 2)), + Error::::TooManyRequests, + ); + }) + } + + #[test] + fn fails_to_import_commitment_if_not_initialized() { + run_test(|| { + assert_noop!( + import_commitment(ChainBuilder::new(1).append_finalized_header().to_header()), + Error::::NotInitialized, + ); + }) + } + + #[test] + fn submit_commitment_works_with_long_chain_with_handoffs() { + run_test_with_initialize(3, || { + let chain = ChainBuilder::new(3) + .append_finalized_header() + .append_default_headers(16) // 2..17 + .append_finalized_header() // 18 + .append_default_headers(16) // 19..34 + .append_handoff_header(9) // 35 + .append_default_headers(8) // 36..43 + .append_finalized_header() // 44 + .append_default_headers(8) // 45..52 + .append_handoff_header(17) // 53 + .append_default_headers(4) // 54..57 + .append_finalized_header() // 58 + .append_default_headers(4); // 59..63 + import_header_chain(chain.to_chain()); + + assert_eq!( + ImportedCommitmentsInfo::::get().unwrap().best_block_number, + 58 + ); + assert_eq!(CurrentAuthoritySetInfo::::get().id, 2); + assert_eq!(CurrentAuthoritySetInfo::::get().len, 17); + + let imported_commitment = ImportedCommitments::::get(58).unwrap(); + assert_eq!( + imported_commitment, + bp_beefy::ImportedCommitment { + parent_number_and_hash: (57, chain.header(57).header.hash()), + mmr_root: chain.header(58).mmr_root, + }, + ); + }) + } + + #[test] + fn commitment_pruning_works() { + run_test_with_initialize(3, || { + let commitments_to_keep = >::CommitmentsToKeep::get(); + let commitments_to_import: Vec = ChainBuilder::new(3) + .append_finalized_headers(commitments_to_keep as usize + 2) + .to_chain(); + + // import exactly `CommitmentsToKeep` commitments + for index in 0..commitments_to_keep { + next_block(); + import_commitment(commitments_to_import[index as usize].clone()) + .expect("must succeed"); + assert_eq!( + ImportedCommitmentsInfo::::get().unwrap().next_block_number_index, + (index + 1) % commitments_to_keep + ); + } + + // ensure that all commitments are in the storage + assert_eq!( + ImportedCommitmentsInfo::::get().unwrap().best_block_number, + commitments_to_keep as TestBridgedBlockNumber + ); + assert_eq!( + ImportedCommitmentsInfo::::get().unwrap().next_block_number_index, + 0 + ); + for index in 0..commitments_to_keep { + assert!(ImportedCommitments::::get( + index as TestBridgedBlockNumber + 1 + ) + .is_some()); + assert_eq!( + ImportedBlockNumbers::::get(index), + Some(index + 1).map(Into::into) + ); + } + + // import next commitment + next_block(); + import_commitment(commitments_to_import[commitments_to_keep as usize].clone()) + .expect("must succeed"); + assert_eq!( + ImportedCommitmentsInfo::::get().unwrap().next_block_number_index, + 1 + ); + assert!(ImportedCommitments::::get( + commitments_to_keep as TestBridgedBlockNumber + 1 + ) + .is_some()); + assert_eq!( + ImportedBlockNumbers::::get(0), + Some(commitments_to_keep + 1).map(Into::into) + ); + // the side effect of the import is that the commitment#1 is pruned + assert!(ImportedCommitments::::get(1).is_none()); + + // import next commitment + next_block(); + import_commitment(commitments_to_import[commitments_to_keep as usize + 1].clone()) + .expect("must succeed"); + assert_eq!( + ImportedCommitmentsInfo::::get().unwrap().next_block_number_index, + 2 + ); + assert!(ImportedCommitments::::get( + commitments_to_keep as TestBridgedBlockNumber + 2 + ) + .is_some()); + assert_eq!( + ImportedBlockNumbers::::get(1), + Some(commitments_to_keep + 2).map(Into::into) + ); + // the side effect of the import is that the commitment#2 is pruned + assert!(ImportedCommitments::::get(1).is_none()); + assert!(ImportedCommitments::::get(2).is_none()); + }); + } + + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); +} diff --git a/modules/beefy/src/mock.rs b/modules/beefy/src/mock.rs new file mode 100644 index 00000000000..927c39ff9aa --- /dev/null +++ b/modules/beefy/src/mock.rs @@ -0,0 +1,226 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate as beefy; +use crate::{ + utils::get_authorities_mmr_root, BridgedBeefyAuthoritySet, BridgedBeefyAuthoritySetInfo, + BridgedBeefyCommitmentHasher, BridgedBeefyMmrLeafExtra, BridgedBeefySignedCommitment, + BridgedMmrHash, BridgedMmrHashing, BridgedMmrProof, +}; + +use bp_beefy::{BeefyValidatorSignatureOf, ChainWithBeefy, Commitment, MmrDataOrHash}; +use bp_runtime::{BasicOperatingMode, Chain}; +use codec::Encode; +use frame_support::{construct_runtime, parameter_types, weights::Weight}; +use sp_core::{sr25519::Signature, Pair}; +use sp_runtime::{ + testing::{Header, H256}, + traits::{BlakeTwo256, Hash, IdentityLookup}, + Perbill, +}; + +pub use beefy_primitives::crypto::{AuthorityId as BeefyId, Pair as BeefyPair}; +use sp_core::crypto::Wraps; +use sp_runtime::traits::Keccak256; + +pub type TestAccountId = u64; +pub type TestBridgedBlockNumber = u64; +pub type TestBridgedBlockHash = H256; +pub type TestBridgedHeader = Header; +pub type TestBridgedAuthoritySetInfo = BridgedBeefyAuthoritySetInfo; +pub type TestBridgedValidatorSet = BridgedBeefyAuthoritySet; +pub type TestBridgedCommitment = BridgedBeefySignedCommitment; +pub type TestBridgedValidatorSignature = BeefyValidatorSignatureOf; +pub type TestBridgedCommitmentHasher = BridgedBeefyCommitmentHasher; +pub type TestBridgedMmrHashing = BridgedMmrHashing; +pub type TestBridgedMmrHash = BridgedMmrHash; +pub type TestBridgedBeefyMmrLeafExtra = BridgedBeefyMmrLeafExtra; +pub type TestBridgedMmrProof = BridgedMmrProof; +pub type TestBridgedRawMmrLeaf = beefy_primitives::mmr::MmrLeaf< + TestBridgedBlockNumber, + TestBridgedBlockHash, + TestBridgedMmrHash, + TestBridgedBeefyMmrLeafExtra, +>; +pub type TestBridgedMmrNode = MmrDataOrHash; + +type TestBlock = frame_system::mocking::MockBlock; +type TestUncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + +construct_runtime! { + pub enum TestRuntime where + Block = TestBlock, + NodeBlock = TestBlock, + UncheckedExtrinsic = TestUncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Beefy: beefy::{Pallet}, + } +} + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} + +impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = TestAccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = (); + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type DbWeight = (); + type BlockWeights = (); + type BlockLength = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl beefy::Config for TestRuntime { + type MaxRequests = frame_support::traits::ConstU32<16>; + type BridgedChain = TestBridgedChain; + type CommitmentsToKeep = frame_support::traits::ConstU32<16>; +} + +#[derive(Debug)] +pub struct TestBridgedChain; + +impl Chain for TestBridgedChain { + type BlockNumber = TestBridgedBlockNumber; + type Hash = H256; + type Hasher = BlakeTwo256; + type Header = ::Header; + + type AccountId = TestAccountId; + type Balance = u64; + type Index = u64; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + fn max_extrinsic_weight() -> Weight { + unreachable!() + } +} + +impl ChainWithBeefy for TestBridgedChain { + type CommitmentHasher = Keccak256; + type MmrHashing = Keccak256; + type MmrHash = ::Output; + type BeefyMmrLeafExtra = (); + type AuthorityId = BeefyId; + type Signature = beefy_primitives::crypto::AuthoritySignature; + type AuthorityIdToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; +} + +/// Run test within test runtime. +pub fn run_test(test: impl FnOnce() -> T) -> T { + sp_io::TestExternalities::new(Default::default()).execute_with(test) +} + +/// Initialize pallet and run test. +pub fn run_test_with_initialize(initial_validators_count: u32, test: impl FnOnce() -> T) -> T { + run_test(|| { + let validators = validator_ids(0, initial_validators_count); + let authority_set = authority_set_info(0, &validators); + + crate::Pallet::::initialize( + RuntimeOrigin::root(), + bp_beefy::InitializationData { + operating_mode: BasicOperatingMode::Normal, + best_block_number: 0, + authority_set, + }, + ) + .expect("initialization data is correct"); + + test() + }) +} + +/// Import given commitment. +pub fn import_commitment( + header: crate::mock_chain::HeaderAndCommitment, +) -> sp_runtime::DispatchResult { + crate::Pallet::::submit_commitment( + RuntimeOrigin::signed(1), + header + .commitment + .expect("thou shall not call import_commitment on header without commitment"), + header.validator_set, + Box::new(header.leaf), + header.leaf_proof, + ) +} + +pub fn validator_pairs(index: u32, count: u32) -> Vec { + (index..index + count) + .map(|index| { + let mut seed = [1u8; 32]; + seed[0..8].copy_from_slice(&(index as u64).encode()); + BeefyPair::from_seed(&seed) + }) + .collect() +} + +/// Return identifiers of validators, starting at given index. +pub fn validator_ids(index: u32, count: u32) -> Vec { + validator_pairs(index, count).into_iter().map(|pair| pair.public()).collect() +} + +pub fn authority_set_info(id: u64, validators: &Vec) -> TestBridgedAuthoritySetInfo { + let merkle_root = get_authorities_mmr_root::(validators.iter()); + + TestBridgedAuthoritySetInfo { id, len: validators.len() as u32, root: merkle_root } +} + +/// Sign BEEFY commitment. +pub fn sign_commitment( + commitment: Commitment, + validator_pairs: &[BeefyPair], + signature_count: usize, +) -> TestBridgedCommitment { + let total_validators = validator_pairs.len(); + let random_validators = + rand::seq::index::sample(&mut rand::thread_rng(), total_validators, signature_count); + + let commitment_hash = TestBridgedCommitmentHasher::hash(&commitment.encode()); + let mut signatures = vec![None; total_validators]; + for validator_idx in random_validators.iter() { + let validator = &validator_pairs[validator_idx]; + signatures[validator_idx] = + Some(validator.as_inner_ref().sign_prehashed(commitment_hash.as_fixed_bytes()).into()); + } + + TestBridgedCommitment { commitment, signatures } +} diff --git a/modules/beefy/src/mock_chain.rs b/modules/beefy/src/mock_chain.rs new file mode 100644 index 00000000000..4896f9bf909 --- /dev/null +++ b/modules/beefy/src/mock_chain.rs @@ -0,0 +1,299 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Utilities to build bridged chain and BEEFY+MMR structures. + +use crate::{ + mock::{ + sign_commitment, validator_pairs, BeefyPair, TestBridgedBlockNumber, TestBridgedCommitment, + TestBridgedHeader, TestBridgedMmrHash, TestBridgedMmrHashing, TestBridgedMmrNode, + TestBridgedMmrProof, TestBridgedRawMmrLeaf, TestBridgedValidatorSet, + TestBridgedValidatorSignature, TestRuntime, + }, + utils::get_authorities_mmr_root, +}; + +use beefy_primitives::mmr::{BeefyNextAuthoritySet, MmrLeafVersion}; +use bp_beefy::{BeefyPayload, Commitment, ValidatorSetId, MMR_ROOT_PAYLOAD_ID}; +use codec::Encode; +use pallet_mmr::NodeIndex; +use rand::Rng; +use sp_core::Pair; +use sp_runtime::traits::{Hash, Header as HeaderT}; +use std::collections::HashMap; + +#[derive(Debug, Clone)] +pub struct HeaderAndCommitment { + pub header: TestBridgedHeader, + pub commitment: Option, + pub validator_set: TestBridgedValidatorSet, + pub leaf: TestBridgedRawMmrLeaf, + pub leaf_proof: TestBridgedMmrProof, + pub mmr_root: TestBridgedMmrHash, +} + +impl HeaderAndCommitment { + pub fn customize_signatures( + &mut self, + f: impl FnOnce(&mut Vec>), + ) { + if let Some(commitment) = &mut self.commitment { + f(&mut commitment.signatures); + } + } + + pub fn customize_commitment( + &mut self, + f: impl FnOnce(&mut Commitment), + validator_pairs: &[BeefyPair], + signature_count: usize, + ) { + if let Some(mut commitment) = self.commitment.take() { + f(&mut commitment.commitment); + self.commitment = + Some(sign_commitment(commitment.commitment, validator_pairs, signature_count)); + } + } +} + +pub struct ChainBuilder { + headers: Vec, + validator_set_id: ValidatorSetId, + validator_keys: Vec, + mmr: mmr_lib::MMR, +} + +struct BridgedMmrStorage { + nodes: HashMap, +} + +impl mmr_lib::MMRStore for BridgedMmrStorage { + fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result> { + Ok(self.nodes.get(&pos).cloned()) + } + + fn append(&mut self, pos: NodeIndex, elems: Vec) -> mmr_lib::Result<()> { + for (i, elem) in elems.into_iter().enumerate() { + self.nodes.insert(pos + i as NodeIndex, elem); + } + Ok(()) + } +} + +impl ChainBuilder { + /// Creates new chain builder with given validator set size. + pub fn new(initial_validators_count: u32) -> Self { + ChainBuilder { + headers: Vec::new(), + validator_set_id: 0, + validator_keys: validator_pairs(0, initial_validators_count), + mmr: mmr_lib::MMR::new(0, BridgedMmrStorage { nodes: HashMap::new() }), + } + } + + /// Get header with given number. + pub fn header(&self, number: TestBridgedBlockNumber) -> HeaderAndCommitment { + self.headers[number as usize - 1].clone() + } + + /// Returns single built header. + pub fn to_header(&self) -> HeaderAndCommitment { + assert_eq!(self.headers.len(), 1); + self.headers[0].clone() + } + + /// Returns built chain. + pub fn to_chain(&self) -> Vec { + self.headers.clone() + } + + /// Appends header, that has been finalized by BEEFY (so it has a linked signed commitment). + pub fn append_finalized_header(self) -> Self { + let next_validator_set_id = self.validator_set_id; + let next_validator_keys = self.validator_keys.clone(); + HeaderBuilder::with_chain(self, next_validator_set_id, next_validator_keys).finalize() + } + + /// Append multiple finalized headers at once. + pub fn append_finalized_headers(mut self, count: usize) -> Self { + for _ in 0..count { + self = self.append_finalized_header(); + } + self + } + + /// Appends header, that enacts new validator set. + /// + /// Such headers are explicitly finalized by BEEFY. + pub fn append_handoff_header(self, next_validators_len: u32) -> Self { + let new_validator_set_id = self.validator_set_id + 1; + let new_validator_pairs = + validator_pairs(rand::thread_rng().gen::() % (u32::MAX / 2), next_validators_len); + + HeaderBuilder::with_chain(self, new_validator_set_id, new_validator_pairs).finalize() + } + + /// Append several default header without commitment. + pub fn append_default_headers(mut self, count: usize) -> Self { + for _ in 0..count { + let next_validator_set_id = self.validator_set_id; + let next_validator_keys = self.validator_keys.clone(); + self = + HeaderBuilder::with_chain(self, next_validator_set_id, next_validator_keys).build() + } + self + } +} + +/// Custom header builder. +pub struct HeaderBuilder { + chain: ChainBuilder, + header: TestBridgedHeader, + leaf: TestBridgedRawMmrLeaf, + leaf_proof: Option, + next_validator_set_id: ValidatorSetId, + next_validator_keys: Vec, +} + +impl HeaderBuilder { + fn with_chain( + chain: ChainBuilder, + next_validator_set_id: ValidatorSetId, + next_validator_keys: Vec, + ) -> Self { + // we're starting with header#1, since header#0 is always finalized + let header_number = chain.headers.len() as TestBridgedBlockNumber + 1; + let header = TestBridgedHeader::new( + header_number, + Default::default(), + Default::default(), + chain.headers.last().map(|h| h.header.hash()).unwrap_or_default(), + Default::default(), + ); + + let next_validators = + next_validator_keys.iter().map(|pair| pair.public()).collect::>(); + let next_validators_mmr_root = + get_authorities_mmr_root::(next_validators.iter()); + let leaf = beefy_primitives::mmr::MmrLeaf { + version: MmrLeafVersion::new(1, 0), + parent_number_and_hash: (header.number().saturating_sub(1), *header.parent_hash()), + beefy_next_authority_set: BeefyNextAuthoritySet { + id: next_validator_set_id, + len: next_validators.len() as u32, + root: next_validators_mmr_root, + }, + leaf_extra: (), + }; + + HeaderBuilder { + chain, + header, + leaf, + leaf_proof: None, + next_validator_keys, + next_validator_set_id, + } + } + + /// Customize generated proof of header MMR leaf. + /// + /// Can only be called once. + pub fn customize_proof( + mut self, + f: impl FnOnce(TestBridgedMmrProof) -> TestBridgedMmrProof, + ) -> Self { + assert!(self.leaf_proof.is_none()); + + let leaf_hash = TestBridgedMmrHashing::hash(&self.leaf.encode()); + let node = TestBridgedMmrNode::Hash(leaf_hash); + let leaf_position = self.chain.mmr.push(node).unwrap(); + + let proof = self.chain.mmr.gen_proof(vec![leaf_position]).unwrap(); + // genesis has no leaf => leaf index is header number minus 1 + let leaf_index = *self.header.number() - 1; + let leaf_count = *self.header.number(); + self.leaf_proof = Some(f(TestBridgedMmrProof { + leaf_index, + leaf_count, + items: proof.proof_items().iter().map(|i| i.hash()).collect(), + })); + + self + } + + /// Build header without commitment. + pub fn build(mut self) -> ChainBuilder { + if self.leaf_proof.is_none() { + self = self.customize_proof(|proof| proof); + } + + let validators = + self.chain.validator_keys.iter().map(|pair| pair.public()).collect::>(); + self.chain.headers.push(HeaderAndCommitment { + header: self.header, + commitment: None, + validator_set: TestBridgedValidatorSet::new(validators, self.chain.validator_set_id) + .unwrap(), + leaf: self.leaf, + leaf_proof: self.leaf_proof.expect("guaranteed by the customize_proof call above; qed"), + mmr_root: self.chain.mmr.get_root().unwrap().hash(), + }); + + self.chain.validator_set_id = self.next_validator_set_id; + self.chain.validator_keys = self.next_validator_keys; + + self.chain + } + + /// Build header with commitment. + pub fn finalize(self) -> ChainBuilder { + let validator_count = self.chain.validator_keys.len(); + let current_validator_set_id = self.chain.validator_set_id; + let current_validator_set_keys = self.chain.validator_keys.clone(); + let mut chain = self.build(); + + let last_header = chain.headers.last_mut().expect("added by append_header; qed"); + last_header.commitment = Some(sign_commitment( + Commitment { + payload: BeefyPayload::from_single_entry( + MMR_ROOT_PAYLOAD_ID, + chain.mmr.get_root().unwrap().hash().encode(), + ), + block_number: *last_header.header.number(), + validator_set_id: current_validator_set_id, + }, + ¤t_validator_set_keys, + validator_count * 2 / 3 + 1, + )); + + chain + } +} + +/// Default Merging & Hashing behavior for MMR. +pub struct BridgedMmrHashMerge; + +impl mmr_lib::Merge for BridgedMmrHashMerge { + type Item = TestBridgedMmrNode; + + fn merge(left: &Self::Item, right: &Self::Item) -> Self::Item { + let mut concat = left.hash().as_ref().to_vec(); + concat.extend_from_slice(right.hash().as_ref()); + + TestBridgedMmrNode::Hash(TestBridgedMmrHashing::hash(&concat)) + } +} diff --git a/modules/beefy/src/utils.rs b/modules/beefy/src/utils.rs new file mode 100644 index 00000000000..c8a7d2cee14 --- /dev/null +++ b/modules/beefy/src/utils.rs @@ -0,0 +1,363 @@ +use crate::{ + BridgedBeefyAuthorityId, BridgedBeefyAuthoritySet, BridgedBeefyAuthoritySetInfo, + BridgedBeefyCommitmentHasher, BridgedBeefyMmrLeaf, BridgedBeefySignedCommitment, BridgedChain, + BridgedMmrHash, BridgedMmrHashing, BridgedMmrProof, Config, Error, LOG_TARGET, +}; +use bp_beefy::{merkle_root, verify_mmr_leaves_proof, BeefyVerify, MmrDataOrHash, MmrProof}; +use codec::Encode; +use frame_support::ensure; +use sp_runtime::traits::{Convert, Hash}; +use sp_std::{vec, vec::Vec}; + +type BridgedMmrDataOrHash = MmrDataOrHash, BridgedBeefyMmrLeaf>; +/// A way to encode validator id to the BEEFY merkle tree leaf. +type BridgedBeefyAuthorityIdToMerkleLeaf = + bp_beefy::BeefyAuthorityIdToMerkleLeafOf>; + +/// Get the MMR root for a collection of validators. +pub(crate) fn get_authorities_mmr_root< + 'a, + T: Config, + I: 'static, + V: Iterator>, +>( + authorities: V, +) -> BridgedMmrHash { + let merkle_leafs = authorities + .cloned() + .map(BridgedBeefyAuthorityIdToMerkleLeaf::::convert) + .collect::>(); + merkle_root::, _>(merkle_leafs) +} + +fn verify_authority_set, I: 'static>( + authority_set_info: &BridgedBeefyAuthoritySetInfo, + authority_set: &BridgedBeefyAuthoritySet, +) -> Result<(), Error> { + ensure!(authority_set.id() == authority_set_info.id, Error::::InvalidValidatorSetId); + ensure!( + authority_set.len() == authority_set_info.len as usize, + Error::::InvalidValidatorSetLen + ); + + // Ensure that the authority set that signed the commitment is the expected one. + let root = get_authorities_mmr_root::(authority_set.validators().iter()); + ensure!(root == authority_set_info.root, Error::::InvalidValidatorSetRoot); + + Ok(()) +} + +/// Number of correct signatures, required from given validators set to accept signed +/// commitment. +/// +/// We're using 'conservative' approach here, where signatures of `2/3+1` validators are +/// required.. +pub(crate) fn signatures_required, I: 'static>(validators_len: usize) -> usize { + validators_len - validators_len.saturating_sub(1) / 3 +} + +fn verify_signatures, I: 'static>( + commitment: &BridgedBeefySignedCommitment, + authority_set: &BridgedBeefyAuthoritySet, +) -> Result<(), Error> { + ensure!( + commitment.signatures.len() == authority_set.len(), + Error::::InvalidCommitmentSignaturesLen + ); + + // Ensure that the commitment was signed by enough authorities. + let msg = commitment.commitment.encode(); + let mut missing_signatures = signatures_required::(authority_set.len()); + for (idx, (authority, maybe_sig)) in + authority_set.validators().iter().zip(commitment.signatures.iter()).enumerate() + { + if let Some(sig) = maybe_sig { + if BeefyVerify::>::verify(sig, &msg, authority) { + missing_signatures = missing_signatures.saturating_sub(1); + if missing_signatures == 0 { + break + } + } else { + log::debug!( + target: LOG_TARGET, + "Signed commitment contains incorrect signature of validator {} ({:?}): {:?}", + idx, + authority, + sig, + ); + } + } + } + ensure!(missing_signatures == 0, Error::::NotEnoughCorrectSignatures); + + Ok(()) +} + +/// Extract MMR root from commitment payload. +fn extract_mmr_root, I: 'static>( + commitment: &BridgedBeefySignedCommitment, +) -> Result, Error> { + commitment + .commitment + .payload + .get_decoded(&bp_beefy::MMR_ROOT_PAYLOAD_ID) + .ok_or(Error::MmrRootMissingFromCommitment) +} + +pub(crate) fn verify_commitment, I: 'static>( + commitment: &BridgedBeefySignedCommitment, + authority_set_info: &BridgedBeefyAuthoritySetInfo, + authority_set: &BridgedBeefyAuthoritySet, +) -> Result, Error> { + // Ensure that the commitment is signed by the best known BEEFY validator set. + ensure!( + commitment.commitment.validator_set_id == authority_set_info.id, + Error::::InvalidCommitmentValidatorSetId + ); + ensure!( + commitment.signatures.len() == authority_set_info.len as usize, + Error::::InvalidCommitmentSignaturesLen + ); + + verify_authority_set(authority_set_info, authority_set)?; + verify_signatures(commitment, authority_set)?; + + extract_mmr_root(commitment) +} + +/// Verify MMR proof of given leaf. +pub(crate) fn verify_beefy_mmr_leaf, I: 'static>( + mmr_leaf: &BridgedBeefyMmrLeaf, + mmr_proof: BridgedMmrProof, + mmr_root: BridgedMmrHash, +) -> Result<(), Error> { + let mmr_proof_leaf_index = mmr_proof.leaf_index; + let mmr_proof_leaf_count = mmr_proof.leaf_count; + let mmr_proof_length = mmr_proof.items.len(); + + // Verify the mmr proof for the provided leaf. + let mmr_leaf_hash = BridgedMmrHashing::::hash(&mmr_leaf.encode()); + verify_mmr_leaves_proof( + mmr_root, + vec![BridgedMmrDataOrHash::::Hash(mmr_leaf_hash)], + MmrProof::into_batch_proof(mmr_proof), + ) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "MMR proof of leaf {:?} (root: {:?}, leaf: {}, leaf count: {}, len: {}) \ + verification has failed with error: {:?}", + mmr_leaf_hash, + mmr_root, + mmr_proof_leaf_index, + mmr_proof_leaf_count, + mmr_proof_length, + e, + ); + + Error::::MmrProofVerificationFailed + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{mock::*, mock_chain::*, *}; + use beefy_primitives::ValidatorSet; + use bp_beefy::{BeefyPayload, MMR_ROOT_PAYLOAD_ID}; + use frame_support::{assert_noop, assert_ok}; + + #[test] + fn submit_commitment_checks_metadata() { + run_test_with_initialize(8, || { + // Fails if `commitment.commitment.validator_set_id` differs. + let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); + header.customize_commitment( + |commitment| { + commitment.validator_set_id += 1; + }, + &validator_pairs(0, 8), + 6, + ); + assert_noop!( + import_commitment(header), + Error::::InvalidCommitmentValidatorSetId, + ); + + // Fails if `commitment.signatures.len()` differs. + let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); + header.customize_signatures(|signatures| { + signatures.pop(); + }); + assert_noop!( + import_commitment(header), + Error::::InvalidCommitmentSignaturesLen, + ); + }); + } + + #[test] + fn submit_commitment_checks_validator_set() { + run_test_with_initialize(8, || { + // Fails if `ValidatorSet::id` differs. + let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); + header.validator_set = ValidatorSet::new(validator_ids(0, 8), 1).unwrap(); + assert_noop!( + import_commitment(header), + Error::::InvalidValidatorSetId, + ); + + // Fails if `ValidatorSet::len()` differs. + let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); + header.validator_set = ValidatorSet::new(validator_ids(0, 5), 0).unwrap(); + assert_noop!( + import_commitment(header), + Error::::InvalidValidatorSetLen, + ); + + // Fails if the validators differ. + let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); + header.validator_set = ValidatorSet::new(validator_ids(3, 8), 0).unwrap(); + assert_noop!( + import_commitment(header), + Error::::InvalidValidatorSetRoot, + ); + }); + } + + #[test] + fn submit_commitment_checks_signatures() { + run_test_with_initialize(20, || { + // Fails when there aren't enough signatures. + let mut header = ChainBuilder::new(20).append_finalized_header().to_header(); + header.customize_signatures(|signatures| { + let first_signature_idx = signatures.iter().position(Option::is_some).unwrap(); + signatures[first_signature_idx] = None; + }); + assert_noop!( + import_commitment(header), + Error::::NotEnoughCorrectSignatures, + ); + + // Fails when there aren't enough correct signatures. + let mut header = ChainBuilder::new(20).append_finalized_header().to_header(); + header.customize_signatures(|signatures| { + let first_signature_idx = signatures.iter().position(Option::is_some).unwrap(); + let last_signature_idx = signatures.len() - + signatures.iter().rev().position(Option::is_some).unwrap() - + 1; + signatures[first_signature_idx] = signatures[last_signature_idx].clone(); + }); + assert_noop!( + import_commitment(header), + Error::::NotEnoughCorrectSignatures, + ); + + // Returns Ok(()) when there are enough signatures, even if some are incorrect. + let mut header = ChainBuilder::new(20).append_finalized_header().to_header(); + header.customize_signatures(|signatures| { + let first_signature_idx = signatures.iter().position(Option::is_some).unwrap(); + let first_missing_signature_idx = + signatures.iter().position(Option::is_none).unwrap(); + signatures[first_missing_signature_idx] = signatures[first_signature_idx].clone(); + }); + assert_ok!(import_commitment(header)); + }); + } + + #[test] + fn submit_commitment_checks_mmr_proof() { + run_test_with_initialize(1, || { + let validators = validator_pairs(0, 1); + + // Fails if leaf is not for parent. + let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); + header.leaf.parent_number_and_hash.0 += 1; + assert_noop!( + import_commitment(header), + Error::::MmrProofVerificationFailed, + ); + + // Fails if mmr proof is incorrect. + let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); + header.leaf_proof.leaf_index += 1; + assert_noop!( + import_commitment(header), + Error::::MmrProofVerificationFailed, + ); + + // Fails if mmr root is incorrect. + let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); + // Replace MMR root with zeroes. + header.customize_commitment( + |commitment| { + commitment.payload = + BeefyPayload::from_single_entry(MMR_ROOT_PAYLOAD_ID, [0u8; 32].encode()); + }, + &validators, + 1, + ); + assert_noop!( + import_commitment(header), + Error::::MmrProofVerificationFailed, + ); + }); + } + + #[test] + fn submit_commitment_extracts_mmr_root() { + run_test_with_initialize(1, || { + let validators = validator_pairs(0, 1); + + // Fails if there is no mmr root in the payload. + let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); + // Remove MMR root from the payload. + header.customize_commitment( + |commitment| { + commitment.payload = BeefyPayload::from_single_entry(*b"xy", vec![]); + }, + &validators, + 1, + ); + assert_noop!( + import_commitment(header), + Error::::MmrRootMissingFromCommitment, + ); + + // Fails if mmr root can't be decoded. + let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); + // MMR root is a 32-byte array and we have replaced it with single byte + header.customize_commitment( + |commitment| { + commitment.payload = + BeefyPayload::from_single_entry(MMR_ROOT_PAYLOAD_ID, vec![42]); + }, + &validators, + 1, + ); + assert_noop!( + import_commitment(header), + Error::::MmrRootMissingFromCommitment, + ); + }); + } + + #[test] + fn submit_commitment_stores_valid_data() { + run_test_with_initialize(20, || { + let header = ChainBuilder::new(20).append_handoff_header(30).to_header(); + assert_ok!(import_commitment(header.clone())); + + assert_eq!(ImportedCommitmentsInfo::::get().unwrap().best_block_number, 1); + assert_eq!(CurrentAuthoritySetInfo::::get().id, 1); + assert_eq!(CurrentAuthoritySetInfo::::get().len, 30); + assert_eq!( + ImportedCommitments::::get(1).unwrap(), + bp_beefy::ImportedCommitment { + parent_number_and_hash: (0, [0; 32].into()), + mmr_root: header.mmr_root, + }, + ); + }); + } +} diff --git a/modules/grandpa/Cargo.toml b/modules/grandpa/Cargo.toml new file mode 100644 index 00000000000..45f314a0bbd --- /dev/null +++ b/modules/grandpa/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "pallet-bridge-grandpa" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +finality-grandpa = { version = "0.16.0", default-features = false } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } + +# Bridge Dependencies + +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-header-chain = { path = "../../primitives/header-chain", default-features = false } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +# Optional Benchmarking Dependencies +bp-test-utils = { path = "../../primitives/test-utils", default-features = false, optional = true } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } + +[dev-dependencies] +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "bp-runtime/std", + "bp-test-utils/std", + "codec/std", + "finality-grandpa/std", + "frame-support/std", + "frame-system/std", + "frame-benchmarking/std", + "log/std", + "scale-info/std", + "sp-finality-grandpa/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", +] +runtime-benchmarks = [ + "bp-test-utils", + "frame-benchmarking/runtime-benchmarks", +] diff --git a/modules/grandpa/src/benchmarking.rs b/modules/grandpa/src/benchmarking.rs new file mode 100644 index 00000000000..e937f7a0bf4 --- /dev/null +++ b/modules/grandpa/src/benchmarking.rs @@ -0,0 +1,132 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Benchmarks for the GRANDPA Pallet. +//! +//! The main dispatchable for the GRANDPA pallet is `submit_finality_proof`, so these benchmarks are +//! based around that. There are to main factors which affect finality proof verification: +//! +//! 1. The number of `votes-ancestries` in the justification +//! 2. The number of `pre-commits` in the justification +//! +//! Vote ancestries are the headers between (`finality_target`, `head_of_chain`], where +//! `header_of_chain` is a descendant of `finality_target`. +//! +//! Pre-commits are messages which are signed by validators at the head of the chain they think is +//! the best. +//! +//! Consider the following: +//! +//! / [B'] <- [C'] +//! [A] <- [B] <- [C] +//! +//! The common ancestor of both forks is block A, so this is what GRANDPA will finalize. In order to +//! verify this we will have vote ancestries of `[B, C, B', C']` and pre-commits `[C, C']`. +//! +//! Note that the worst case scenario here would be a justification where each validator has it's +//! own fork which is `SESSION_LENGTH` blocks long. + +use crate::*; + +use bp_runtime::BasicOperatingMode; +use bp_test_utils::{ + accounts, make_justification_for_header, JustificationGeneratorParams, TEST_GRANDPA_ROUND, + TEST_GRANDPA_SET_ID, +}; +use frame_benchmarking::{benchmarks_instance_pallet, whitelisted_caller}; +use frame_support::traits::Get; +use frame_system::RawOrigin; +use sp_finality_grandpa::AuthorityId; +use sp_runtime::traits::Zero; +use sp_std::vec::Vec; + +// The maximum number of vote ancestries to include in a justification. +// +// In practice this would be limited by the session length (number of blocks a single authority set +// can produce) of a given chain. +const MAX_VOTE_ANCESTRIES: u32 = 1000; + +// The maximum number of pre-commits to include in a justification. In practice this scales with the +// number of validators. +pub const MAX_VALIDATOR_SET_SIZE: u32 = 1024; + +// `1..MAX_VALIDATOR_SET_SIZE` and `1..MAX_VOTE_ANCESTRIES` are too large && benchmarks are +// running for almost 40m (steps=50, repeat=20) on a decent laptop, which is too much. Since +// we're building linear function here, let's just select some limited subrange for benchmarking. +const VALIDATOR_SET_SIZE_RANGE_BEGIN: u32 = MAX_VALIDATOR_SET_SIZE / 20; +const VALIDATOR_SET_SIZE_RANGE_END: u32 = + VALIDATOR_SET_SIZE_RANGE_BEGIN + VALIDATOR_SET_SIZE_RANGE_BEGIN; +const MAX_VOTE_ANCESTRIES_RANGE_BEGIN: u32 = MAX_VOTE_ANCESTRIES / 20; +const MAX_VOTE_ANCESTRIES_RANGE_END: u32 = + MAX_VOTE_ANCESTRIES_RANGE_BEGIN + MAX_VOTE_ANCESTRIES_RANGE_BEGIN; + +/// Returns number of first header to be imported. +/// +/// Since we bootstrap the pallet with `HeadersToKeep` already imported headers, +/// this function computes the next expected header number to import. +fn header_number, I: 'static, N: From>() -> N { + (T::HeadersToKeep::get() + 1).into() +} + +/// Prepare header and its justification to submit using `submit_finality_proof`. +fn prepare_benchmark_data, I: 'static>( + precommits: u32, + ancestors: u32, +) -> (BridgedHeader, GrandpaJustification>) { + let authority_list = accounts(precommits as u16) + .iter() + .map(|id| (AuthorityId::from(*id), 1)) + .collect::>(); + + let init_data = InitializationData { + header: Box::new(bp_test_utils::test_header(Zero::zero())), + authority_list, + set_id: TEST_GRANDPA_SET_ID, + operating_mode: BasicOperatingMode::Normal, + }; + + bootstrap_bridge::(init_data); + + let header: BridgedHeader = bp_test_utils::test_header(header_number::()); + let params = JustificationGeneratorParams { + header: header.clone(), + round: TEST_GRANDPA_ROUND, + set_id: TEST_GRANDPA_SET_ID, + authorities: accounts(precommits as u16).iter().map(|k| (*k, 1)).collect::>(), + ancestors, + forks: 1, + }; + let justification = make_justification_for_header(params); + (header, justification) +} + +benchmarks_instance_pallet! { + // This is the "gold standard" benchmark for this extrinsic, and it's what should be used to + // annotate the weight in the pallet. + submit_finality_proof { + let p in VALIDATOR_SET_SIZE_RANGE_BEGIN..VALIDATOR_SET_SIZE_RANGE_END; + let v in MAX_VOTE_ANCESTRIES_RANGE_BEGIN..MAX_VOTE_ANCESTRIES_RANGE_END; + let caller: T::AccountId = whitelisted_caller(); + let (header, justification) = prepare_benchmark_data::(p, v); + }: submit_finality_proof(RawOrigin::Signed(caller), Box::new(header), justification) + verify { + let header: BridgedHeader = bp_test_utils::test_header(header_number::()); + let expected_hash = header.hash(); + + assert_eq!(>::get().unwrap().1, expected_hash); + assert!(>::contains_key(expected_hash)); + } +} diff --git a/modules/grandpa/src/extension.rs b/modules/grandpa/src/extension.rs new file mode 100644 index 00000000000..c0f02da751e --- /dev/null +++ b/modules/grandpa/src/extension.rs @@ -0,0 +1,116 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{Config, Pallet}; +use bp_runtime::FilterCall; +use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; +use sp_runtime::{ + traits::Header, + transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, +}; + +/// Validate Grandpa headers in order to avoid "mining" transactions that provide outdated +/// bridged chain headers. Without this validation, even honest relayers may lose their funds +/// if there are multiple relays running and submitting the same information. +impl< + Call: IsSubType, T>>, + T: frame_system::Config + Config, + I: 'static, + > FilterCall for Pallet +{ + fn validate(call: &::RuntimeCall) -> TransactionValidity { + let bundled_block_number = match call.is_sub_type() { + Some(crate::Call::::submit_finality_proof { ref finality_target, .. }) => + *finality_target.number(), + _ => return Ok(ValidTransaction::default()), + }; + + let best_finalized = crate::BestFinalized::::get(); + let best_finalized_number = match best_finalized { + Some((best_finalized_number, _)) => best_finalized_number, + None => return InvalidTransaction::Call.into(), + }; + + if best_finalized_number >= bundled_block_number { + log::trace!( + target: crate::LOG_TARGET, + "Rejecting obsolete bridged header: bundled {:?}, best {:?}", + bundled_block_number, + best_finalized_number, + ); + + return InvalidTransaction::Stale.into() + } + + Ok(ValidTransaction::default()) + } +} + +#[cfg(test)] +mod tests { + use super::FilterCall; + use crate::{ + mock::{run_test, test_header, RuntimeCall, TestNumber, TestRuntime}, + BestFinalized, + }; + use bp_test_utils::make_default_justification; + + fn validate_block_submit(num: TestNumber) -> bool { + crate::Pallet::::validate(&RuntimeCall::Grandpa(crate::Call::< + TestRuntime, + (), + >::submit_finality_proof { + finality_target: Box::new(test_header(num)), + justification: make_default_justification(&test_header(num)), + })) + .is_ok() + } + + fn sync_to_header_10() { + let header10_hash = sp_core::H256::default(); + BestFinalized::::put((10, header10_hash)); + } + + #[test] + fn extension_rejects_obsolete_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5 => tx is + // rejected + sync_to_header_10(); + assert!(!validate_block_submit(5)); + }); + } + + #[test] + fn extension_rejects_same_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_header_10(); + assert!(!validate_block_submit(10)); + }); + } + + #[test] + fn extension_accepts_new_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#15 => tx is + // accepted + sync_to_header_10(); + assert!(validate_block_submit(15)); + }); + } +} diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs new file mode 100644 index 00000000000..2402b2512e2 --- /dev/null +++ b/modules/grandpa/src/lib.rs @@ -0,0 +1,1258 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate GRANDPA Pallet +//! +//! This pallet is an on-chain GRANDPA light client for Substrate based chains. +//! +//! This pallet achieves this by trustlessly verifying GRANDPA finality proofs on-chain. Once +//! verified, finalized headers are stored in the pallet, thereby creating a sparse header chain. +//! This sparse header chain can be used as a source of truth for other higher-level applications. +//! +//! The pallet is responsible for tracking GRANDPA validator set hand-offs. We only import headers +//! with justifications signed by the current validator set we know of. The header is inspected for +//! a `ScheduledChanges` digest item, which is then used to update to next validator set. +//! +//! Since this pallet only tracks finalized headers it does not deal with forks. Forks can only +//! occur if the GRANDPA validator set on the bridged chain is either colluding or there is a severe +//! bug causing resulting in an equivocation. Such events are outside the scope of this pallet. +//! Shall the fork occur on the bridged chain governance intervention will be required to +//! re-initialize the bridge and track the right fork. + +#![cfg_attr(not(feature = "std"), no_std)] +// Runtime-generated enums +#![allow(clippy::large_enum_variant)] + +use storage_types::StoredAuthoritySet; + +use bp_header_chain::{justification::GrandpaJustification, HeaderChain, InitializationData}; +use bp_runtime::{ + BlockNumberOf, BoundedStorageValue, Chain, HashOf, HasherOf, HeaderOf, OwnedBridgeModule, +}; +use finality_grandpa::voter_set::VoterSet; +use frame_support::{ensure, fail}; +use frame_system::ensure_signed; +use sp_finality_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID}; +use sp_runtime::traits::{Header as HeaderT, Zero}; +use sp_std::{boxed::Box, convert::TryInto}; + +mod extension; +#[cfg(test)] +mod mock; +mod storage_types; + +/// Module, containing weights for this pallet. +pub mod weights; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + +// Re-export in crate namespace for `construct_runtime!` +pub use pallet::*; +pub use weights::WeightInfo; + +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-grandpa"; + +/// Bridged chain from the pallet configuration. +pub type BridgedChain = >::BridgedChain; +/// Block number of the bridged chain. +pub type BridgedBlockNumber = BlockNumberOf<>::BridgedChain>; +/// Block hash of the bridged chain. +pub type BridgedBlockHash = HashOf<>::BridgedChain>; +/// Hasher of the bridged chain. +pub type BridgedBlockHasher = HasherOf<>::BridgedChain>; +/// Header of the bridged chain. +pub type BridgedHeader = HeaderOf<>::BridgedChain>; +/// Stored header of the bridged chain. +pub type StoredBridgedHeader = + BoundedStorageValue<>::MaxBridgedHeaderSize, BridgedHeader>; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use bp_runtime::BasicOperatingMode; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The chain we are bridging to here. + type BridgedChain: Chain; + + /// The upper bound on the number of requests allowed by the pallet. + /// + /// A request refers to an action which writes a header to storage. + /// + /// Once this bound is reached the pallet will not allow any dispatchables to be called + /// until the request count has decreased. + #[pallet::constant] + type MaxRequests: Get; + + /// Maximal number of finalized headers to keep in the storage. + /// + /// The setting is there to prevent growing the on-chain state indefinitely. Note + /// the setting does not relate to block numbers - we will simply keep as much items + /// in the storage, so it doesn't guarantee any fixed timeframe for finality headers. + /// + /// Incautious change of this constant may lead to orphan entries in the runtime storage. + #[pallet::constant] + type HeadersToKeep: Get; + + /// Max number of authorities at the bridged chain. + #[pallet::constant] + type MaxBridgedAuthorities: Get; + /// Maximal size (in bytes) of the SCALE-encoded bridged chain header. + /// + /// This constant must be selected with care. The pallet requires mandatory headers to be + /// submitted to be able to proceed. Mandatory headers contain public keys of all GRANDPA + /// authorities. E.g. for 1024 authorities, the size of encoded keys will be at least 32 KB. + /// The same header may also contain other digest items as well, so some reserve here + /// is required. + #[pallet::constant] + type MaxBridgedHeaderSize: Get; + + /// Weights gathered through benchmarking. + type WeightInfo: WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet { + fn on_initialize(_n: T::BlockNumber) -> frame_support::weights::Weight { + >::mutate(|count| *count = count.saturating_sub(1)); + + T::DbWeight::get().reads_writes(1, 1) + } + } + + impl, I: 'static> OwnedBridgeModule for Pallet { + const LOG_TARGET: &'static str = LOG_TARGET; + type OwnerStorage = PalletOwner; + type OperatingMode = BasicOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + } + + #[pallet::call] + impl, I: 'static> Pallet { + /// Verify a target header is finalized according to the given finality proof. + /// + /// It will use the underlying storage pallet to fetch information about the current + /// authorities and best finalized header in order to verify that the header is finalized. + /// + /// If successful in verification, it will write the target header to the underlying storage + /// pallet. + #[pallet::weight(T::WeightInfo::submit_finality_proof( + justification.commit.precommits.len().try_into().unwrap_or(u32::MAX), + justification.votes_ancestries.len().try_into().unwrap_or(u32::MAX), + ))] + pub fn submit_finality_proof( + origin: OriginFor, + finality_target: Box>, + justification: GrandpaJustification>, + ) -> DispatchResultWithPostInfo { + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + let _ = ensure_signed(origin)?; + + ensure!(Self::request_count() < T::MaxRequests::get(), >::TooManyRequests); + + let (hash, number) = (finality_target.hash(), finality_target.number()); + log::trace!( + target: LOG_TARGET, + "Going to try and finalize header {:?}", + finality_target + ); + + let best_finalized = BestFinalized::::get(); + let best_finalized = + best_finalized.and_then(|(_, hash)| ImportedHeaders::::get(hash)); + let best_finalized = match best_finalized { + Some(best_finalized) => best_finalized, + None => { + log::error!( + target: LOG_TARGET, + "Cannot finalize header {:?} because pallet is not yet initialized", + finality_target, + ); + fail!(>::NotInitialized); + }, + }; + + // We do a quick check here to ensure that our header chain is making progress and isn't + // "travelling back in time" (which could be indicative of something bad, e.g a + // hard-fork). + ensure!(best_finalized.number() < number, >::OldHeader); + + let authority_set = >::get(); + let set_id = authority_set.set_id; + verify_justification::(&justification, hash, *number, authority_set.into())?; + + let is_authorities_change_enacted = + try_enact_authority_change::(&finality_target, set_id)?; + let finality_target = StoredBridgedHeader::::try_from_inner(*finality_target) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Size of header {:?} ({}) is larger that the configured value {}", + hash, + e.value_size, + e.maximal_size, + ); + + Error::::TooLargeHeader + })?; + >::mutate(|count| *count += 1); + insert_header::(finality_target, hash); + log::info!( + target: LOG_TARGET, + "Successfully imported finalized header with hash {:?}!", + hash + ); + + // mandatory header is a header that changes authorities set. The pallet can't go + // further without importing this header. So every bridge MUST import mandatory headers. + // + // We don't want to charge extra costs for mandatory operations. So relayer is not + // paying fee for mandatory headers import transactions. + let is_mandatory_header = is_authorities_change_enacted; + let pays_fee = if is_mandatory_header { Pays::No } else { Pays::Yes }; + + Ok(pays_fee.into()) + } + + /// Bootstrap the bridge pallet with an initial header and authority set from which to sync. + /// + /// The initial configuration provided does not need to be the genesis header of the bridged + /// chain, it can be any arbitrary header. You can also provide the next scheduled set + /// change if it is already know. + /// + /// This function is only allowed to be called from a trusted origin and writes to storage + /// with practically no checks in terms of the validity of the data. It is important that + /// you ensure that valid data is being passed in. + #[pallet::weight((T::DbWeight::get().reads_writes(2, 5), DispatchClass::Operational))] + pub fn initialize( + origin: OriginFor, + init_data: super::InitializationData>, + ) -> DispatchResultWithPostInfo { + Self::ensure_owner_or_root(origin)?; + + let init_allowed = !>::exists(); + ensure!(init_allowed, >::AlreadyInitialized); + initialize_bridge::(init_data.clone())?; + + log::info!( + target: LOG_TARGET, + "Pallet has been initialized with the following parameters: {:?}", + init_data + ); + + Ok(().into()) + } + + /// Change `PalletOwner`. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { + >::set_owner(origin, new_owner) + } + + /// Halt or resume all pallet operations. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_operating_mode( + origin: OriginFor, + operating_mode: BasicOperatingMode, + ) -> DispatchResult { + >::set_operating_mode(origin, operating_mode) + } + } + + /// The current number of requests which have written to storage. + /// + /// If the `RequestCount` hits `MaxRequests`, no more calls will be allowed to the pallet until + /// the request capacity is increased. + /// + /// The `RequestCount` is decreased by one at the beginning of every block. This is to ensure + /// that the pallet can always make progress. + #[pallet::storage] + #[pallet::getter(fn request_count)] + pub(super) type RequestCount, I: 'static = ()> = StorageValue<_, u32, ValueQuery>; + + /// Hash of the header used to bootstrap the pallet. + #[pallet::storage] + pub(super) type InitialHash, I: 'static = ()> = + StorageValue<_, BridgedBlockHash, ValueQuery>; + + /// Hash of the best finalized header. + #[pallet::storage] + pub type BestFinalized, I: 'static = ()> = + StorageValue<_, (BridgedBlockNumber, BridgedBlockHash), OptionQuery>; + + /// A ring buffer of imported hashes. Ordered by the insertion time. + #[pallet::storage] + pub(super) type ImportedHashes, I: 'static = ()> = + StorageMap<_, Identity, u32, BridgedBlockHash>; + + /// Current ring buffer position. + #[pallet::storage] + pub(super) type ImportedHashesPointer, I: 'static = ()> = + StorageValue<_, u32, ValueQuery>; + + /// Headers which have been imported into the pallet. + #[pallet::storage] + pub type ImportedHeaders, I: 'static = ()> = + StorageMap<_, Identity, BridgedBlockHash, StoredBridgedHeader>; + + /// The current GRANDPA Authority set. + #[pallet::storage] + pub(super) type CurrentAuthoritySet, I: 'static = ()> = + StorageValue<_, StoredAuthoritySet, ValueQuery>; + + /// Optional pallet owner. + /// + /// Pallet owner has a right to halt all pallet operations and then resume it. If it is + /// `None`, then there are no direct ways to halt/resume pallet operations, but other + /// runtime methods may still be used to do that (i.e. democracy::referendum to update halt + /// flag directly or call the `halt_operations`). + #[pallet::storage] + pub type PalletOwner, I: 'static = ()> = + StorageValue<_, T::AccountId, OptionQuery>; + + /// The current operating mode of the pallet. + /// + /// Depending on the mode either all, or no transactions will be allowed. + #[pallet::storage] + pub type PalletOperatingMode, I: 'static = ()> = + StorageValue<_, BasicOperatingMode, ValueQuery>; + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + /// Optional module owner account. + pub owner: Option, + /// Optional module initialization data. + pub init_data: Option>>, + } + + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { owner: None, init_data: None } + } + } + + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + if let Some(ref owner) = self.owner { + >::put(owner); + } + + if let Some(init_data) = self.init_data.clone() { + initialize_bridge::(init_data).expect("genesis config is correct; qed"); + } else { + // Since the bridge hasn't been initialized we shouldn't allow anyone to perform + // transactions. + >::put(BasicOperatingMode::Halted); + } + } + } + + #[pallet::error] + pub enum Error { + /// The given justification is invalid for the given header. + InvalidJustification, + /// The authority set from the underlying header chain is invalid. + InvalidAuthoritySet, + /// There are too many requests for the current window to handle. + TooManyRequests, + /// The header being imported is older than the best finalized header known to the pallet. + OldHeader, + /// The scheduled authority set change found in the header is unsupported by the pallet. + /// + /// This is the case for non-standard (e.g forced) authority set changes. + UnsupportedScheduledChange, + /// The pallet is not yet initialized. + NotInitialized, + /// The pallet has already been initialized. + AlreadyInitialized, + /// Too many authorities in the set. + TooManyAuthoritiesInSet, + /// Too large header. + TooLargeHeader, + /// Error generated by the `OwnedBridgeModule` trait. + BridgeModule(bp_runtime::OwnedBridgeModuleError), + } + + /// Check the given header for a GRANDPA scheduled authority set change. If a change + /// is found it will be enacted immediately. + /// + /// This function does not support forced changes, or scheduled changes with delays + /// since these types of changes are indicative of abnormal behavior from GRANDPA. + /// + /// Returned value will indicate if a change was enacted or not. + pub(crate) fn try_enact_authority_change, I: 'static>( + header: &BridgedHeader, + current_set_id: sp_finality_grandpa::SetId, + ) -> Result { + let mut change_enacted = false; + + // We don't support forced changes - at that point governance intervention is required. + ensure!( + super::find_forced_change(header).is_none(), + >::UnsupportedScheduledChange + ); + + if let Some(change) = super::find_scheduled_change(header) { + // GRANDPA only includes a `delay` for forced changes, so this isn't valid. + ensure!(change.delay == Zero::zero(), >::UnsupportedScheduledChange); + + // TODO [#788]: Stop manually increasing the `set_id` here. + let next_authorities = StoredAuthoritySet:: { + authorities: change + .next_authorities + .try_into() + .map_err(|_| Error::::TooManyAuthoritiesInSet)?, + set_id: current_set_id + 1, + }; + + // Since our header schedules a change and we know the delay is 0, it must also enact + // the change. + >::put(&next_authorities); + change_enacted = true; + + log::info!( + target: LOG_TARGET, + "Transitioned from authority set {} to {}! New authorities are: {:?}", + current_set_id, + current_set_id + 1, + next_authorities, + ); + }; + + Ok(change_enacted) + } + + /// Verify a GRANDPA justification (finality proof) for a given header. + /// + /// Will use the GRANDPA current authorities known to the pallet. + /// + /// If successful it returns the decoded GRANDPA justification so we can refund any weight which + /// was overcharged in the initial call. + pub(crate) fn verify_justification, I: 'static>( + justification: &GrandpaJustification>, + hash: BridgedBlockHash, + number: BridgedBlockNumber, + authority_set: bp_header_chain::AuthoritySet, + ) -> Result<(), sp_runtime::DispatchError> { + use bp_header_chain::justification::verify_justification; + + let voter_set = + VoterSet::new(authority_set.authorities).ok_or(>::InvalidAuthoritySet)?; + let set_id = authority_set.set_id; + + Ok(verify_justification::>( + (hash, number), + set_id, + &voter_set, + justification, + ) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Received invalid justification for {:?}: {:?}", + hash, + e, + ); + >::InvalidJustification + })?) + } + + /// Import a previously verified header to the storage. + /// + /// Note this function solely takes care of updating the storage and pruning old entries, + /// but does not verify the validity of such import. + pub(crate) fn insert_header, I: 'static>( + header: StoredBridgedHeader, + hash: BridgedBlockHash, + ) { + let index = >::get(); + let pruning = >::try_get(index); + >::put((*header.number(), hash)); + >::insert(hash, header); + >::insert(index, hash); + + // Update ring buffer pointer and remove old header. + >::put((index + 1) % T::HeadersToKeep::get()); + if let Ok(hash) = pruning { + log::debug!(target: LOG_TARGET, "Pruning old header: {:?}.", hash); + >::remove(hash); + } + } + + /// Since this writes to storage with no real checks this should only be used in functions that + /// were called by a trusted origin. + pub(crate) fn initialize_bridge, I: 'static>( + init_params: super::InitializationData>, + ) -> Result<(), Error> { + let super::InitializationData { header, authority_list, set_id, operating_mode } = + init_params; + let authority_set_length = authority_list.len(); + let authority_set = StoredAuthoritySet::::try_new(authority_list, set_id) + .map_err(|_| { + log::error!( + target: LOG_TARGET, + "Failed to initialize bridge. Number of authorities in the set {} is larger than the configured value {}", + authority_set_length, + T::MaxBridgedAuthorities::get(), + ); + + Error::TooManyAuthoritiesInSet + })?; + let initial_hash = header.hash(); + let header = StoredBridgedHeader::::try_from_inner(*header).map_err(|e| { + log::error!( + target: LOG_TARGET, + "Failed to initialize bridge. Size of header {:?} ({}) is larger that the configured value {}", + initial_hash, + e.value_size, + e.maximal_size, + ); + + Error::::TooLargeHeader + })?; + + >::put(initial_hash); + >::put(0); + insert_header::(header, initial_hash); + + >::put(authority_set); + + >::put(operating_mode); + + Ok(()) + } + + #[cfg(feature = "runtime-benchmarks")] + pub(crate) fn bootstrap_bridge, I: 'static>( + init_params: super::InitializationData>, + ) { + let start_number = *init_params.header.number(); + let end_number = start_number + T::HeadersToKeep::get().into(); + initialize_bridge::(init_params).expect("benchmarks are correct"); + + let mut number = start_number; + while number < end_number { + number = number + sp_runtime::traits::One::one(); + let header = >::new( + number, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ); + let hash = header.hash(); + insert_header::( + StoredBridgedHeader::::try_from_inner(header) + .expect("only used from benchmarks; benchmarks are correct; qed"), + hash, + ); + } + } +} + +impl, I: 'static> Pallet { + /// Get the best finalized header the pallet knows of. + pub fn best_finalized() -> Option> { + let (_, hash) = >::get()?; + >::get(hash).map(|h| h.into_inner()) + } + + /// Check if a particular header is known to the bridge pallet. + pub fn is_known_header(hash: BridgedBlockHash) -> bool { + >::contains_key(hash) + } +} + +/// Bridge GRANDPA pallet as header chain. +pub type GrandpaChainHeaders = Pallet; + +impl, I: 'static> HeaderChain> for GrandpaChainHeaders { + fn finalized_header(hash: HashOf>) -> Option>> { + ImportedHeaders::::get(hash).map(|h| h.into_inner()) + } +} + +pub(crate) fn find_scheduled_change( + header: &H, +) -> Option> { + use sp_runtime::generic::OpaqueDigestItemId; + + let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); + + let filter_log = |log: ConsensusLog| match log { + ConsensusLog::ScheduledChange(change) => Some(change), + _ => None, + }; + + // find the first consensus digest with the right ID which converts to + // the right kind of consensus log. + header.digest().convert_first(|l| l.try_to(id).and_then(filter_log)) +} + +/// Checks the given header for a consensus digest signaling a **forced** scheduled change and +/// extracts it. +pub(crate) fn find_forced_change( + header: &H, +) -> Option<(H::Number, sp_finality_grandpa::ScheduledChange)> { + use sp_runtime::generic::OpaqueDigestItemId; + + let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); + + let filter_log = |log: ConsensusLog| match log { + ConsensusLog::ForcedChange(delay, change) => Some((delay, change)), + _ => None, + }; + + // find the first consensus digest with the right ID which converts to + // the right kind of consensus log. + header.digest().convert_first(|l| l.try_to(id).and_then(filter_log)) +} + +/// (Re)initialize bridge with given header for using it in `pallet-bridge-messages` benchmarks. +#[cfg(feature = "runtime-benchmarks")] +pub fn initialize_for_benchmarks, I: 'static>(header: BridgedHeader) { + initialize_bridge::(InitializationData { + header: Box::new(header), + authority_list: sp_std::vec::Vec::new(), /* we don't verify any proofs in external + * benchmarks */ + set_id: 0, + operating_mode: bp_runtime::BasicOperatingMode::Normal, + }) + .expect("only used from benchmarks; benchmarks are correct; qed"); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{ + run_test, test_header, RuntimeOrigin, TestHeader, TestNumber, TestRuntime, + MAX_BRIDGED_AUTHORITIES, MAX_HEADER_SIZE, + }; + use bp_runtime::BasicOperatingMode; + use bp_test_utils::{ + authority_list, generate_owned_bridge_module_tests, make_default_justification, + make_justification_for_header, JustificationGeneratorParams, ALICE, BOB, + }; + use codec::Encode; + use frame_support::{ + assert_err, assert_noop, assert_ok, dispatch::PostDispatchInfo, + storage::generator::StorageValue, + }; + use sp_runtime::{Digest, DigestItem, DispatchError}; + + fn initialize_substrate_bridge() { + assert_ok!(init_with_origin(RuntimeOrigin::root())); + } + + fn init_with_origin( + origin: RuntimeOrigin, + ) -> Result< + InitializationData, + sp_runtime::DispatchErrorWithPostInfo, + > { + let genesis = test_header(0); + + let init_data = InitializationData { + header: Box::new(genesis), + authority_list: authority_list(), + set_id: 1, + operating_mode: BasicOperatingMode::Normal, + }; + + Pallet::::initialize(origin, init_data.clone()).map(|_| init_data) + } + + fn submit_finality_proof(header: u8) -> frame_support::dispatch::DispatchResultWithPostInfo { + let header = test_header(header.into()); + let justification = make_default_justification(&header); + + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification, + ) + } + + fn next_block() { + use frame_support::traits::OnInitialize; + + let current_number = frame_system::Pallet::::block_number(); + frame_system::Pallet::::set_block_number(current_number + 1); + let _ = Pallet::::on_initialize(current_number); + } + + fn change_log(delay: u64) -> Digest { + let consensus_log = + ConsensusLog::::ScheduledChange(sp_finality_grandpa::ScheduledChange { + next_authorities: vec![(ALICE.into(), 1), (BOB.into(), 1)], + delay, + }); + + Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] } + } + + fn forced_change_log(delay: u64) -> Digest { + let consensus_log = ConsensusLog::::ForcedChange( + delay, + sp_finality_grandpa::ScheduledChange { + next_authorities: vec![(ALICE.into(), 1), (BOB.into(), 1)], + delay, + }, + ); + + Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] } + } + + fn many_authorities_log() -> Digest { + let consensus_log = + ConsensusLog::::ScheduledChange(sp_finality_grandpa::ScheduledChange { + next_authorities: std::iter::repeat((ALICE.into(), 1)) + .take(MAX_BRIDGED_AUTHORITIES as usize + 1) + .collect(), + delay: 0, + }); + + Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] } + } + + fn large_digest() -> Digest { + Digest { logs: vec![DigestItem::Other(vec![42; MAX_HEADER_SIZE as _])] } + } + + #[test] + fn init_root_or_owner_origin_can_initialize_pallet() { + run_test(|| { + assert_noop!(init_with_origin(RuntimeOrigin::signed(1)), DispatchError::BadOrigin); + assert_ok!(init_with_origin(RuntimeOrigin::root())); + + // Reset storage so we can initialize the pallet again + BestFinalized::::kill(); + PalletOwner::::put(2); + assert_ok!(init_with_origin(RuntimeOrigin::signed(2))); + }) + } + + #[test] + fn init_storage_entries_are_correctly_initialized() { + run_test(|| { + assert_eq!(BestFinalized::::get(), None,); + assert_eq!(Pallet::::best_finalized(), None); + + let init_data = init_with_origin(RuntimeOrigin::root()).unwrap(); + + assert!(>::contains_key(init_data.header.hash())); + assert_eq!(BestFinalized::::get().unwrap().1, init_data.header.hash()); + assert_eq!( + CurrentAuthoritySet::::get().authorities, + init_data.authority_list + ); + assert_eq!(PalletOperatingMode::::get(), BasicOperatingMode::Normal); + }) + } + + #[test] + fn init_can_only_initialize_pallet_once() { + run_test(|| { + initialize_substrate_bridge(); + assert_noop!( + init_with_origin(RuntimeOrigin::root()), + >::AlreadyInitialized + ); + }) + } + + #[test] + fn init_fails_if_there_are_too_many_authorities_in_the_set() { + run_test(|| { + let genesis = test_header(0); + let init_data = InitializationData { + header: Box::new(genesis), + authority_list: std::iter::repeat(authority_list().remove(0)) + .take(MAX_BRIDGED_AUTHORITIES as usize + 1) + .collect(), + set_id: 1, + operating_mode: BasicOperatingMode::Normal, + }; + + assert_noop!( + Pallet::::initialize(RuntimeOrigin::root(), init_data), + Error::::TooManyAuthoritiesInSet, + ); + }); + } + + #[test] + fn init_fails_if_header_is_too_large() { + run_test(|| { + let mut genesis = test_header(0); + genesis.digest = large_digest(); + let init_data = InitializationData { + header: Box::new(genesis), + authority_list: authority_list(), + set_id: 1, + operating_mode: BasicOperatingMode::Normal, + }; + + assert_noop!( + Pallet::::initialize(RuntimeOrigin::root(), init_data), + Error::::TooLargeHeader, + ); + }); + } + + #[test] + fn pallet_rejects_transactions_if_halted() { + run_test(|| { + initialize_substrate_bridge(); + + assert_ok!(Pallet::::set_operating_mode( + RuntimeOrigin::root(), + BasicOperatingMode::Halted + )); + assert_noop!( + submit_finality_proof(1), + Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted) + ); + + assert_ok!(Pallet::::set_operating_mode( + RuntimeOrigin::root(), + BasicOperatingMode::Normal + )); + assert_ok!(submit_finality_proof(1)); + }) + } + + #[test] + fn pallet_rejects_header_if_not_initialized_yet() { + run_test(|| { + assert_noop!(submit_finality_proof(1), Error::::NotInitialized); + }); + } + + #[test] + fn succesfully_imports_header_with_valid_finality() { + run_test(|| { + initialize_substrate_bridge(); + assert_ok!( + submit_finality_proof(1), + PostDispatchInfo { + actual_weight: None, + pays_fee: frame_support::dispatch::Pays::Yes, + }, + ); + + let header = test_header(1); + assert_eq!(>::get().unwrap().1, header.hash()); + assert!(>::contains_key(header.hash())); + }) + } + + #[test] + fn rejects_justification_that_skips_authority_set_transition() { + run_test(|| { + initialize_substrate_bridge(); + + let header = test_header(1); + + let params = + JustificationGeneratorParams:: { set_id: 2, ..Default::default() }; + let justification = make_justification_for_header(params); + + assert_err!( + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification, + ), + >::InvalidJustification + ); + }) + } + + #[test] + fn does_not_import_header_with_invalid_finality_proof() { + run_test(|| { + initialize_substrate_bridge(); + + let header = test_header(1); + let mut justification = make_default_justification(&header); + justification.round = 42; + + assert_err!( + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification, + ), + >::InvalidJustification + ); + }) + } + + #[test] + fn disallows_invalid_authority_set() { + run_test(|| { + let genesis = test_header(0); + + let invalid_authority_list = vec![(ALICE.into(), u64::MAX), (BOB.into(), u64::MAX)]; + let init_data = InitializationData { + header: Box::new(genesis), + authority_list: invalid_authority_list, + set_id: 1, + operating_mode: BasicOperatingMode::Normal, + }; + + assert_ok!(Pallet::::initialize(RuntimeOrigin::root(), init_data)); + + let header = test_header(1); + let justification = make_default_justification(&header); + + assert_err!( + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification, + ), + >::InvalidAuthoritySet + ); + }) + } + + #[test] + fn importing_header_ensures_that_chain_is_extended() { + run_test(|| { + initialize_substrate_bridge(); + + assert_ok!(submit_finality_proof(4)); + assert_err!(submit_finality_proof(3), Error::::OldHeader); + assert_ok!(submit_finality_proof(5)); + }) + } + + #[test] + fn importing_header_enacts_new_authority_set() { + run_test(|| { + initialize_substrate_bridge(); + + let next_set_id = 2; + let next_authorities = vec![(ALICE.into(), 1), (BOB.into(), 1)]; + + // Need to update the header digest to indicate that our header signals an authority set + // change. The change will be enacted when we import our header. + let mut header = test_header(2); + header.digest = change_log(0); + + // Create a valid justification for the header + let justification = make_default_justification(&header); + + // Let's import our test header + assert_ok!( + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header.clone()), + justification + ), + PostDispatchInfo { + actual_weight: None, + pays_fee: frame_support::dispatch::Pays::No, + }, + ); + + // Make sure that our header is the best finalized + assert_eq!(>::get().unwrap().1, header.hash()); + assert!(>::contains_key(header.hash())); + + // Make sure that the authority set actually changed upon importing our header + assert_eq!( + >::get(), + StoredAuthoritySet::::try_new(next_authorities, next_set_id) + .unwrap(), + ); + }) + } + + #[test] + fn importing_header_rejects_header_with_scheduled_change_delay() { + run_test(|| { + initialize_substrate_bridge(); + + // Need to update the header digest to indicate that our header signals an authority set + // change. However, the change doesn't happen until the next block. + let mut header = test_header(2); + header.digest = change_log(1); + + // Create a valid justification for the header + let justification = make_default_justification(&header); + + // Should not be allowed to import this header + assert_err!( + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification + ), + >::UnsupportedScheduledChange + ); + }) + } + + #[test] + fn importing_header_rejects_header_with_forced_changes() { + run_test(|| { + initialize_substrate_bridge(); + + // Need to update the header digest to indicate that it signals a forced authority set + // change. + let mut header = test_header(2); + header.digest = forced_change_log(0); + + // Create a valid justification for the header + let justification = make_default_justification(&header); + + // Should not be allowed to import this header + assert_err!( + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification + ), + >::UnsupportedScheduledChange + ); + }) + } + + #[test] + fn importing_header_rejects_header_with_too_many_authorities() { + run_test(|| { + initialize_substrate_bridge(); + + // Need to update the header digest to indicate that our header signals an authority set + // change. However, the change doesn't happen until the next block. + let mut header = test_header(2); + header.digest = many_authorities_log(); + + // Create a valid justification for the header + let justification = make_default_justification(&header); + + // Should not be allowed to import this header + assert_err!( + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification + ), + >::TooManyAuthoritiesInSet + ); + }); + } + + #[test] + fn importing_header_rejects_header_if_it_is_too_large() { + run_test(|| { + initialize_substrate_bridge(); + + // Need to update the header digest to indicate that our header signals an authority set + // change. However, the change doesn't happen until the next block. + let mut header = test_header(2); + header.digest = large_digest(); + + // Create a valid justification for the header + let justification = make_default_justification(&header); + + // Should not be allowed to import this header + assert_err!( + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification + ), + >::TooLargeHeader + ); + }); + } + + #[test] + fn parse_finalized_storage_proof_rejects_proof_on_unknown_header() { + run_test(|| { + assert_noop!( + Pallet::::parse_finalized_storage_proof( + Default::default(), + sp_trie::StorageProof::new(vec![]), + |_| (), + ), + bp_header_chain::HeaderChainError::UnknownHeader, + ); + }); + } + + #[test] + fn parse_finalized_storage_accepts_valid_proof() { + run_test(|| { + let (state_root, storage_proof) = bp_runtime::craft_valid_storage_proof(); + + let mut header = test_header(2); + header.set_state_root(state_root); + + let hash = header.hash(); + >::put((2, hash)); + >::insert( + hash, + StoredBridgedHeader::::try_from_inner(header).unwrap(), + ); + + assert_ok!( + Pallet::::parse_finalized_storage_proof(hash, storage_proof, |_| (),), + (), + ); + }); + } + + #[test] + fn rate_limiter_disallows_imports_once_limit_is_hit_in_single_block() { + run_test(|| { + initialize_substrate_bridge(); + + assert_ok!(submit_finality_proof(1)); + assert_ok!(submit_finality_proof(2)); + assert_err!(submit_finality_proof(3), >::TooManyRequests); + }) + } + + #[test] + fn rate_limiter_invalid_requests_do_not_count_towards_request_count() { + run_test(|| { + let submit_invalid_request = || { + let header = test_header(1); + let mut invalid_justification = make_default_justification(&header); + invalid_justification.round = 42; + + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + invalid_justification, + ) + }; + + initialize_substrate_bridge(); + + for _ in 0..::MaxRequests::get() + 1 { + // Notice that the error here *isn't* `TooManyRequests` + assert_err!(submit_invalid_request(), >::InvalidJustification); + } + + // Can still submit `MaxRequests` requests afterwards + assert_ok!(submit_finality_proof(1)); + assert_ok!(submit_finality_proof(2)); + assert_err!(submit_finality_proof(3), >::TooManyRequests); + }) + } + + #[test] + fn rate_limiter_allows_request_after_new_block_has_started() { + run_test(|| { + initialize_substrate_bridge(); + assert_ok!(submit_finality_proof(1)); + assert_ok!(submit_finality_proof(2)); + + next_block(); + assert_ok!(submit_finality_proof(3)); + }) + } + + #[test] + fn rate_limiter_disallows_imports_once_limit_is_hit_across_different_blocks() { + run_test(|| { + initialize_substrate_bridge(); + assert_ok!(submit_finality_proof(1)); + assert_ok!(submit_finality_proof(2)); + + next_block(); + assert_ok!(submit_finality_proof(3)); + assert_err!(submit_finality_proof(4), >::TooManyRequests); + }) + } + + #[test] + fn rate_limiter_allows_max_requests_after_long_time_with_no_activity() { + run_test(|| { + initialize_substrate_bridge(); + assert_ok!(submit_finality_proof(1)); + assert_ok!(submit_finality_proof(2)); + + next_block(); + next_block(); + + next_block(); + assert_ok!(submit_finality_proof(5)); + assert_ok!(submit_finality_proof(7)); + }) + } + + #[test] + fn should_prune_headers_over_headers_to_keep_parameter() { + run_test(|| { + initialize_substrate_bridge(); + assert_ok!(submit_finality_proof(1)); + let first_header = Pallet::::best_finalized().unwrap(); + next_block(); + + assert_ok!(submit_finality_proof(2)); + next_block(); + assert_ok!(submit_finality_proof(3)); + next_block(); + assert_ok!(submit_finality_proof(4)); + next_block(); + assert_ok!(submit_finality_proof(5)); + next_block(); + + assert_ok!(submit_finality_proof(6)); + + assert!( + !Pallet::::is_known_header(first_header.hash()), + "First header should be pruned." + ); + }) + } + + #[test] + fn storage_keys_computed_properly() { + assert_eq!( + PalletOperatingMode::::storage_value_final_key().to_vec(), + bp_header_chain::storage_keys::pallet_operating_mode_key("Grandpa").0, + ); + + assert_eq!( + BestFinalized::::storage_value_final_key().to_vec(), + bp_header_chain::storage_keys::best_finalized_key("Grandpa").0, + ); + } + + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); +} diff --git a/modules/grandpa/src/mock.rs b/modules/grandpa/src/mock.rs new file mode 100644 index 00000000000..0f83bd528d6 --- /dev/null +++ b/modules/grandpa/src/mock.rs @@ -0,0 +1,131 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +// From construct_runtime macro +#![allow(clippy::from_over_into)] + +use bp_runtime::Chain; +use frame_support::{construct_runtime, parameter_types, weights::Weight}; +use sp_core::sr25519::Signature; +use sp_runtime::{ + testing::{Header, H256}, + traits::{BlakeTwo256, IdentityLookup}, + Perbill, +}; + +pub type AccountId = u64; +pub type TestHeader = crate::BridgedHeader; +pub type TestNumber = crate::BridgedBlockNumber; + +type Block = frame_system::mocking::MockBlock; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + +pub const MAX_BRIDGED_AUTHORITIES: u32 = 2048; +pub const MAX_HEADER_SIZE: u32 = 65536; + +use crate as grandpa; + +construct_runtime! { + pub enum TestRuntime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Grandpa: grandpa::{Pallet, Call}, + } +} + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} + +impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = (); + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type DbWeight = (); + type BlockWeights = (); + type BlockLength = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const MaxRequests: u32 = 2; + pub const HeadersToKeep: u32 = 5; + pub const SessionLength: u64 = 5; + pub const NumValidators: u32 = 5; +} + +impl grandpa::Config for TestRuntime { + type BridgedChain = TestBridgedChain; + type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; + type MaxBridgedAuthorities = frame_support::traits::ConstU32; + type MaxBridgedHeaderSize = frame_support::traits::ConstU32; + type WeightInfo = (); +} + +#[derive(Debug)] +pub struct TestBridgedChain; + +impl Chain for TestBridgedChain { + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hasher = ::Hashing; + type Header = ::Header; + + type AccountId = AccountId; + type Balance = u64; + type Index = u64; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + fn max_extrinsic_weight() -> Weight { + unreachable!() + } +} + +pub fn run_test(test: impl FnOnce() -> T) -> T { + sp_io::TestExternalities::new(Default::default()).execute_with(test) +} + +pub fn test_header(num: TestNumber) -> TestHeader { + // We wrap the call to avoid explicit type annotations in our tests + bp_test_utils::test_header(num) +} diff --git a/modules/grandpa/src/storage_types.rs b/modules/grandpa/src/storage_types.rs new file mode 100644 index 00000000000..d930dbadbc6 --- /dev/null +++ b/modules/grandpa/src/storage_types.rs @@ -0,0 +1,66 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Wrappers for public types that are implementing `MaxEncodedLen` + +use crate::Config; + +use bp_header_chain::AuthoritySet; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{BoundedVec, RuntimeDebugNoBound}; +use scale_info::TypeInfo; +use sp_finality_grandpa::{AuthorityId, AuthorityList, AuthorityWeight, SetId}; + +/// A bounded list of Grandpa authorities with associated weights. +pub type StoredAuthorityList = + BoundedVec<(AuthorityId, AuthorityWeight), MaxBridgedAuthorities>; + +/// A bounded GRANDPA Authority List and ID. +#[derive(Clone, Decode, Encode, Eq, TypeInfo, MaxEncodedLen, RuntimeDebugNoBound)] +#[scale_info(skip_type_params(T, I))] +pub struct StoredAuthoritySet, I: 'static> { + /// List of GRANDPA authorities for the current round. + pub authorities: StoredAuthorityList<>::MaxBridgedAuthorities>, + /// Monotonic identifier of the current GRANDPA authority set. + pub set_id: SetId, +} + +impl, I: 'static> StoredAuthoritySet { + /// Try to create a new bounded GRANDPA Authority Set from unbounded list. + /// + /// Returns error if number of authorities in the provided list is too large. + pub fn try_new(authorities: AuthorityList, set_id: SetId) -> Result { + Ok(Self { authorities: TryFrom::try_from(authorities).map_err(drop)?, set_id }) + } +} + +impl, I: 'static> PartialEq for StoredAuthoritySet { + fn eq(&self, other: &Self) -> bool { + self.set_id == other.set_id && self.authorities == other.authorities + } +} + +impl, I: 'static> Default for StoredAuthoritySet { + fn default() -> Self { + StoredAuthoritySet { authorities: BoundedVec::default(), set_id: 0 } + } +} + +impl, I: 'static> From> for AuthoritySet { + fn from(t: StoredAuthoritySet) -> Self { + AuthoritySet { authorities: t.authorities.into(), set_id: t.set_id } + } +} diff --git a/modules/grandpa/src/weights.rs b/modules/grandpa/src/weights.rs new file mode 100644 index 00000000000..6997713face --- /dev/null +++ b/modules/grandpa/src/weights.rs @@ -0,0 +1,79 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Autogenerated weights for `pallet_bridge_grandpa` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-11-17, STEPS: 50, REPEAT: 20 +//! LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled +//! CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/millau-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_bridge_grandpa +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./modules/grandpa/src/weights.rs +// --template=./.maintain/millau-weight-template.hbs + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for `pallet_bridge_grandpa`. +pub trait WeightInfo { + fn submit_finality_proof(p: u32, v: u32) -> Weight; +} + +/// Weights for `pallet_bridge_grandpa` that are generated using one of the Bridge testnets. +/// +/// Those weights are test only and must never be used in production. +pub struct BridgeWeight(PhantomData); +impl WeightInfo for BridgeWeight { + fn submit_finality_proof(p: u32, v: u32) -> Weight { + Weight::from_ref_time(198_274_668 as u64) + .saturating_add(Weight::from_ref_time(39_830_948 as u64).saturating_mul(p as u64)) + .saturating_add(Weight::from_ref_time(1_535_681 as u64).saturating_mul(v as u64)) + .saturating_add(T::DbWeight::get().reads(7 as u64)) + .saturating_add(T::DbWeight::get().writes(6 as u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn submit_finality_proof(p: u32, v: u32) -> Weight { + Weight::from_ref_time(198_274_668 as u64) + .saturating_add(Weight::from_ref_time(39_830_948 as u64).saturating_mul(p as u64)) + .saturating_add(Weight::from_ref_time(1_535_681 as u64).saturating_mul(v as u64)) + .saturating_add(RocksDbWeight::get().reads(7 as u64)) + .saturating_add(RocksDbWeight::get().writes(6 as u64)) + } +} diff --git a/modules/messages/Cargo.toml b/modules/messages/Cargo.toml new file mode 100644 index 00000000000..0c2fecb0be3 --- /dev/null +++ b/modules/messages/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "pallet-bridge-messages" +description = "Module that allows bridged chains to exchange messages using lane concept." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +log = { version = "0.4.17", default-features = false } +num-traits = { version = "0.2", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } + +# Bridge dependencies + +bp-messages = { path = "../../primitives/messages", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } + +# Substrate Dependencies + +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +bp-test-utils = { path = "../../primitives/test-utils" } + +[features] +default = ["std"] +std = [ + "bp-messages/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "frame-system/std", + "frame-benchmarking/std", + "log/std", + "num-traits/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", +] diff --git a/modules/messages/README.md b/modules/messages/README.md new file mode 100644 index 00000000000..2dc56296842 --- /dev/null +++ b/modules/messages/README.md @@ -0,0 +1,424 @@ +# Messages Module + +The messages module is used to deliver messages from source chain to target chain. Message is +(almost) opaque to the module and the final goal is to hand message to the message dispatch +mechanism. + +## Contents +- [Overview](#overview) +- [Message Workflow](#message-workflow) +- [Integrating Message Lane Module into Runtime](#integrating-messages-module-into-runtime) +- [Non-Essential Functionality](#non-essential-functionality) +- [Weights of Module Extrinsics](#weights-of-module-extrinsics) + +## Overview + +Message lane is an unidirectional channel, where messages are sent from source chain to the target +chain. At the same time, a single instance of messages module supports both outbound lanes and +inbound lanes. So the chain where the module is deployed (this chain), may act as a source chain for +outbound messages (heading to a bridged chain) and as a target chain for inbound messages (coming +from a bridged chain). + +Messages module supports multiple message lanes. Every message lane is identified with a 4-byte +identifier. Messages sent through the lane are assigned unique (for this lane) increasing integer +value that is known as nonce ("number that can only be used once"). Messages that are sent over the +same lane are guaranteed to be delivered to the target chain in the same order they're sent from +the source chain. In other words, message with nonce `N` will be delivered right before delivering a +message with nonce `N+1`. + +Single message lane may be seen as a transport channel for single application (onchain, offchain or +mixed). At the same time the module itself never dictates any lane or message rules. In the end, it +is the runtime developer who defines what message lane and message mean for this runtime. + +## Message Workflow + +The message "appears" when its submitter calls the `send_message()` function of the module. The +submitter specifies the lane that he's willing to use, the message itself and the fee that he's +willing to pay for the message delivery and dispatch. If a message passes all checks, the nonce is +assigned and the message is stored in the module storage. The message is in an "undelivered" state +now. + +We assume that there are external, offchain actors, called relayers, that are submitting module +related transactions to both target and source chains. The pallet itself has no assumptions about +relayers incentivization scheme, but it has some callbacks for paying rewards. See +[Integrating Messages Module into runtime](#Integrating-Messages-Module-into-runtime) +for details. + +Eventually, some relayer would notice this message in the "undelivered" state and it would decide to +deliver this message. Relayer then crafts `receive_messages_proof()` transaction (aka delivery +transaction) for the messages module instance, deployed at the target chain. Relayer provides +his account id at the source chain, the proof of message (or several messages), the number of +messages in the transaction and their cumulative dispatch weight. Once a transaction is mined, the +message is considered "delivered". + +Once a message is delivered, the relayer may want to confirm delivery back to the source chain. +There are two reasons why he would want to do that. The first is that we intentionally limit number +of "delivered", but not yet "confirmed" messages at inbound lanes +(see [What about other Constants in the Messages Module Configuration Trait](#What-about-other-Constants-in-the-Messages-Module-Configuration-Trait) for explanation). +So at some point, the target chain may stop accepting new messages until relayers confirm some of +these. The second is that if the relayer wants to be rewarded for delivery, he must prove the fact +that he has actually delivered the message. And this proof may only be generated after the delivery +transaction is mined. So relayer crafts the `receive_messages_delivery_proof()` transaction (aka +confirmation transaction) for the messages module instance, deployed at the source chain. Once +this transaction is mined, the message is considered "confirmed". + +The "confirmed" state is the final state of the message. But there's one last thing related to the +message - the fact that it is now "confirmed" and reward has been paid to the relayer (or at least +callback for this has been called), must be confirmed to the target chain. Otherwise, we may reach +the limit of "unconfirmed" messages at the target chain and it will stop accepting new messages. So +relayer sometimes includes a nonce of the latest "confirmed" message in the next +`receive_messages_proof()` transaction, proving that some messages have been confirmed. + +## Integrating Messages Module into Runtime + +As it has been said above, the messages module supports both outbound and inbound message lanes. +So if we will integrate a module in some runtime, it may act as the source chain runtime for +outbound messages and as the target chain runtime for inbound messages. In this section, we'll +sometimes refer to the chain we're currently integrating with, as this chain and the other chain as +bridged chain. + +Messages module doesn't simply accept transactions that are claiming that the bridged chain has +some updated data for us. Instead of this, the module assumes that the bridged chain is able to +prove that updated data in some way. The proof is abstracted from the module and may be of any kind. +In our Substrate-to-Substrate bridge we're using runtime storage proofs. Other bridges may use +transaction proofs, Substrate header digests or anything else that may be proved. + +**IMPORTANT NOTE**: everything below in this chapter describes details of the messages module +configuration. But if you interested in well-probed and relatively easy integration of two +Substrate-based chains, you may want to look at the +[bridge-runtime-common](../../bin/runtime-common/README.md) crate. This crate is providing a lot of +helpers for integration, which may be directly used from within your runtime. Then if you'll decide +to change something in this scheme, get back here for detailed information. + +### General Information + +The messages module supports instances. Every module instance is supposed to bridge this chain +and some bridged chain. To bridge with another chain, using another instance is suggested (this +isn't forced anywhere in the code, though). + +Message submitters may track message progress by inspecting module events. When Message is accepted, +the `MessageAccepted` event is emitted in the `send_message()` transaction. The event contains both +message lane identifier and nonce that has been assigned to the message. When a message is delivered +to the target chain, the `MessagesDelivered` event is emitted from the +`receive_messages_delivery_proof()` transaction. The `MessagesDelivered` contains the message lane +identifier, inclusive range of delivered message nonces and their single-bit dispatch results. + +Please note that the meaning of the 'dispatch result' is determined by the message dispatcher at +the target chain. For example, in case of immediate call dispatcher it will be the `true` if call +has been successfully dispatched and `false` if it has only been delivered. This simple mechanism +built into the messages module allows building basic bridge applications, which only care whether +their messages have been successfully dispatched or not. More sophisticated applications may use +their own dispatch result delivery mechanism to deliver something larger than single bit. + +### How to plug-in Messages Module to Send Messages to the Bridged Chain? + +The `pallet_bridge_messages::Config` trait has 3 main associated types that are used to work with +outbound messages. The `pallet_bridge_messages::Config::TargetHeaderChain` defines how we see the +bridged chain as the target for our outbound messages. It must be able to check that the bridged +chain may accept our message - like that the message has size below maximal possible transaction +size of the chain and so on. And when the relayer sends us a confirmation transaction, this +implementation must be able to parse and verify the proof of messages delivery. Normally, you would +reuse the same (configurable) type on all chains that are sending messages to the same bridged +chain. + +The `pallet_bridge_messages::Config::LaneMessageVerifier` defines a single callback to verify outbound +messages. The simplest callback may just accept all messages. But in this case you'll need to answer +many questions first. Who will pay for the delivery and confirmation transaction? Are we sure that +someone will ever deliver this message to the bridged chain? Are we sure that we don't bloat our +runtime storage by accepting this message? What if the message is improperly encoded or has some +fields set to invalid values? Answering all those (and similar) questions would lead to correct +implementation. + +There's another thing to consider when implementing type for use in +`pallet_bridge_messages::Config::LaneMessageVerifier`. It is whether we treat all message lanes +identically, or they'll have different sets of verification rules? For example, you may reserve +lane#1 for messages coming from some 'wrapped-token' pallet - then you may verify in your +implementation that the origin is associated with this pallet. Lane#2 may be reserved for 'system' +messages and you may charge zero fee for such messages. You may have some rate limiting for messages +sent over the lane#3. Or you may just verify the same rules set for all outbound messages - it is +all up to the `pallet_bridge_messages::Config::LaneMessageVerifier` implementation. + +The last type is the `pallet_bridge_messages::Config::MessageDeliveryAndDispatchPayment`. When all +checks are made and we have decided to accept the message, we're calling the +`pay_delivery_and_dispatch_fee()` callback, passing the corresponding argument of the `send_message` +function. Later, when message delivery is confirmed, we're calling `pay_relayers_rewards()` +callback, passing accounts of relayers and messages that they have delivered. The simplest +implementation of this trait is in the [`instant_payments.rs`](./src/instant_payments.rs) module and +simply calls `Currency::transfer()` when those callbacks are called. So `Currency` units are +transferred between submitter, 'relayers fund' and relayers accounts. Other implementations may use +more or less sophisticated techniques - the whole relayers incentivization scheme is not a part of +the messages module. + +### I have a Messages Module in my Runtime, but I Want to Reject all Outbound Messages. What shall I do? + +You should be looking at the `bp_messages::source_chain::ForbidOutboundMessages` structure +[`bp_messages::source_chain`](../../primitives/messages/src/source_chain.rs). It implements +all required traits and will simply reject all transactions, related to outbound messages. + +### How to plug-in Messages Module to Receive Messages from the Bridged Chain? + +The `pallet_bridge_messages::Config` trait has 2 main associated types that are used to work with +inbound messages. The `pallet_bridge_messages::Config::SourceHeaderChain` defines how we see the +bridged chain as the source or our inbound messages. When relayer sends us a delivery transaction, +this implementation must be able to parse and verify the proof of messages wrapped in this +transaction. Normally, you would reuse the same (configurable) type on all chains that are sending +messages to the same bridged chain. + +The `pallet_bridge_messages::Config::MessageDispatch` defines a way on how to dispatch delivered +messages. Apart from actually dispatching the message, the implementation must return the correct +dispatch weight of the message before dispatch is called. + +### I have a Messages Module in my Runtime, but I Want to Reject all Inbound Messages. What +shall I do? + +You should be looking at the `bp_messages::target_chain::ForbidInboundMessages` structure from +the [`bp_messages::target_chain`](../../primitives/messages/src/target_chain.rs) module. It +implements all required traits and will simply reject all transactions, related to inbound messages. + +### What about other Constants in the Messages Module Configuration Trait? + +Message is being stored in the source chain storage until its delivery will be confirmed. After +that, we may safely remove the message from the storage. Lane messages are removed (pruned) when +someone sends a new message using the same lane. So the message submitter pays for that pruning. To +avoid pruning too many messages in a single transaction, there's +`pallet_bridge_messages::Config::MaxMessagesToPruneAtOnce` configuration parameter. We will never prune +more than this number of messages in the single transaction. That said, the value should not be too +big to avoid waste of resources when there are no messages to prune. + +To be able to reward the relayer for delivering messages, we store a map of message nonces range => +identifier of the relayer that has delivered this range at the target chain runtime storage. If a +relayer delivers multiple consequent ranges, they're merged into single entry. So there may be more +than one entry for the same relayer. Eventually, this whole map must be delivered back to the source +chain to confirm delivery and pay rewards. So to make sure we are able to craft this confirmation +transaction, we need to: (1) keep the size of this map below a certain limit and (2) make sure that +the weight of processing this map is below a certain limit. Both size and processing weight mostly +depend on the number of entries. The number of entries is limited with the +`pallet_bridge_messages::ConfigMaxUnrewardedRelayerEntriesAtInboundLane` parameter. Processing weight +also depends on the total number of messages that are being confirmed, because every confirmed +message needs to be read. So there's another +`pallet_bridge_messages::Config::MaxUnconfirmedMessagesAtInboundLane` parameter for that. + +When choosing values for these parameters, you must also keep in mind that if proof in your scheme +is based on finality of headers (and it is the most obvious option for Substrate-based chains with +finality notion), then choosing too small values for these parameters may cause significant delays +in message delivery. That's because there are too many actors involved in this scheme: 1) authorities +that are finalizing headers of the target chain need to finalize header with non-empty map; 2) the +headers relayer then needs to submit this header and its finality proof to the source chain; 3) the +messages relayer must then send confirmation transaction (storage proof of this map) to the source +chain; 4) when the confirmation transaction will be mined at some header, source chain authorities +must finalize this header; 5) the headers relay then needs to submit this header and its finality +proof to the target chain; 6) only now the messages relayer may submit new messages from the source +to target chain and prune the entry from the map. + +Delivery transaction requires the relayer to provide both number of entries and total number of +messages in the map. This means that the module never charges an extra cost for delivering a map - +the relayer would need to pay exactly for the number of entries+messages it has delivered. So the +best guess for values of these parameters would be the pair that would occupy `N` percent of the +maximal transaction size and weight of the source chain. The `N` should be large enough to process +large maps, at the same time keeping reserve for future source chain upgrades. + +## Non-Essential Functionality + +Apart from the message related calls, the module exposes a set of auxiliary calls. They fall in two +groups, described in the next two paragraphs. + +There may be a special account in every runtime where the messages module is deployed. This +account, named 'module owner', is like a module-level sudo account - he's able to halt all and +result all module operations without requiring runtime upgrade. The module may have no message +owner, but we suggest to use it at least for initial deployment. To calls that are related to this +account are: +- `fn set_owner()`: current module owner may call it to transfer "ownership" to another account; +- `fn halt_operations()`: the module owner (or sudo account) may call this function to stop all + module operations. After this call, all message-related transactions will be rejected until + further `resume_operations` call'. This call may be used when something extraordinary happens with + the bridge; +- `fn resume_operations()`: module owner may call this function to resume bridge operations. The + module will resume its regular operations after this call. + +Apart from halting and resuming the bridge, the module owner may also tune module configuration +parameters without runtime upgrades. The set of parameters needs to be designed in advance, though. +The module configuration trait has associated `Parameter` type, which may be e.g. enum and represent +a set of parameters that may be updated by the module owner. For example, if your bridge needs to +convert sums between different tokens, you may define a 'conversion rate' parameter and let the +module owner update this parameter when there are significant changes in the rate. The corresponding +module call is `fn update_pallet_parameter()`. + +## Weights of Module Extrinsics + +The main assumptions behind weight formulas is: +- all possible costs are paid in advance by the message submitter; +- whenever possible, relayer tries to minimize cost of its transactions. So e.g. even though sender + always pays for delivering outbound lane state proof, relayer may not include it in the delivery + transaction (unless messages module on target chain requires that); +- weight formula should incentivize relayer to not to submit any redundant data in the extrinsics + arguments; +- the extrinsic shall never be executing slower (i.e. has larger actual weight) than defined by the + formula. + +### Weight of `send_message` call + +#### Related benchmarks + +| Benchmark | Description | +|-----------------------------------|-----------------------------------------------------| +`send_minimal_message_worst_case` | Sends 0-size message with worst possible conditions | +`send_1_kb_message_worst_case` | Sends 1KB-size message with worst possible conditions | +`send_16_kb_message_worst_case` | Sends 16KB-size message with worst possible conditions | + +#### Weight formula + +The weight formula is: +``` +Weight = BaseWeight + MessageSizeInKilobytes * MessageKiloByteSendWeight +``` + +Where: + +| Component | How it is computed? | Description | +|-----------------------------|------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------| +| `SendMessageOverhead` | `send_minimal_message_worst_case` | Weight of sending minimal (0 bytes) message | +| `MessageKiloByteSendWeight` | `(send_16_kb_message_worst_case - send_1_kb_message_worst_case)/15` | Weight of sending every additional kilobyte of the message | + +### Weight of `receive_messages_proof` call + +#### Related benchmarks + +| Benchmark | Description* | +|---------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------| +| `receive_single_message_proof` | Receives proof of single `EXPECTED_DEFAULT_MESSAGE_LENGTH` message | +| `receive_two_messages_proof` | Receives proof of two identical `EXPECTED_DEFAULT_MESSAGE_LENGTH` messages | +| `receive_single_message_proof_with_outbound_lane_state` | Receives proof of single `EXPECTED_DEFAULT_MESSAGE_LENGTH` message and proof of outbound lane state at the source chain | +| `receive_single_message_proof_1_kb` | Receives proof of single message. The proof has size of approximately 1KB** | +| `receive_single_message_proof_16_kb` | Receives proof of single message. The proof has size of approximately 16KB** | + +*\* - In all benchmarks all received messages are dispatched and their dispatch cost is near to zero* + +*\*\* - Trie leafs are assumed to have minimal values. The proof is derived from the minimal proof +by including more trie nodes. That's because according to our additioal benchmarks, increasing proof +by including more nodes has slightly larger impact on performance than increasing values stored in leafs*. + +#### Weight formula + +The weight formula is: +``` +Weight = BaseWeight + OutboundStateDeliveryWeight + + MessagesCount * MessageDeliveryWeight + + MessagesDispatchWeight + + Max(0, ActualProofSize - ExpectedProofSize) * ProofByteDeliveryWeight +``` + +Where: + +| Component | How it is computed? | Description | +|-------------------------------|------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `BaseWeight` | `2*receive_single_message_proof - receive_two_messages_proof` | Weight of receiving and parsing minimal proof | +| `OutboundStateDeliveryWeight` | `receive_single_message_proof_with_outbound_lane_state - receive_single_message_proof` | Additional weight when proof includes outbound lane state | +| `MessageDeliveryWeight` | `receive_two_messages_proof - receive_single_message_proof` | Weight of of parsing and dispatching (without actual dispatch cost) of every message | +| `MessagesCount` | | Provided by relayer | +| `MessagesDispatchWeight` | | Provided by relayer | +| `ActualProofSize` | | Provided by relayer | +| `ExpectedProofSize` | `EXPECTED_DEFAULT_MESSAGE_LENGTH * MessagesCount + EXTRA_STORAGE_PROOF_SIZE` | Size of proof that we are expecting. This only includes `EXTRA_STORAGE_PROOF_SIZE` once, because we assume that intermediate nodes likely to be included in the proof only once. This may be wrong, but since weight of processing proof with many nodes is almost equal to processing proof with large leafs, additional cost will be covered because we're charging for extra proof bytes anyway | +| `ProofByteDeliveryWeight` | `(receive_single_message_proof_16_kb - receive_single_message_proof_1_kb) / (15 * 1024)` | Weight of processing every additional proof byte over `ExpectedProofSize` limit | + +#### Why for every message sent using `send_message` we will be able to craft `receive_messages_proof` transaction? + +We have following checks in `send_message` transaction on the source chain: +- message size should be less than or equal to `2/3` of maximal extrinsic size on the target chain; +- message dispatch weight should be less than or equal to the `1/2` of maximal extrinsic dispatch + weight on the target chain. + +Delivery transaction is an encoded delivery call and signed extensions. So we have `1/3` of maximal +extrinsic size reserved for: +- storage proof, excluding the message itself. Currently, on our test chains, the overhead is always + within `EXTRA_STORAGE_PROOF_SIZE` limits (1024 bytes); +- signed extras and other call arguments (`relayer_id: SourceChain::AccountId`, `messages_count: + u32`, `dispatch_weight: u64`). + +On Millau chain, maximal extrinsic size is `0.75 * 2MB`, so `1/3` is `512KB` (`524_288` bytes). This +should be enough to cover these extra arguments and signed extensions. + +Let's exclude message dispatch cost from single message delivery transaction weight formula: +``` +Weight = BaseWeight + OutboundStateDeliveryWeight + MessageDeliveryWeight + + Max(0, ActualProofSize - ExpectedProofSize) * ProofByteDeliveryWeight +``` + +So we have `1/2` of maximal extrinsic weight to cover these components. `BaseWeight`, +`OutboundStateDeliveryWeight` and `MessageDeliveryWeight` are determined using benchmarks and are +hardcoded into runtime. Adequate relayer would only include required trie nodes into the proof. So +if message size would be maximal (`2/3` of `MaximalExtrinsicSize`), then the extra proof size would +be `MaximalExtrinsicSize / 3 * 2 - EXPECTED_DEFAULT_MESSAGE_LENGTH`. + +Both conditions are verified by `pallet_bridge_messages::ensure_weights_are_correct` and +`pallet_bridge_messages::ensure_able_to_receive_messages` functions, which must be called from every +runtime's tests. + +#### Post-dispatch weight refunds of the `receive_messages_proof` call + +Weight formula of the `receive_messages_proof` call assumes that the dispatch fee of every message is +paid at the target chain (where call is executed), that every message will be dispatched and that +dispatch weight of the message will be exactly the weight that is returned from the +`MessageDispatch::dispatch_weight` method call. This isn't true for all messages, so the call returns +actual weight used to dispatch messages. + +This actual weight is the weight, returned by the weight formula, minus: +- the weight of undispatched messages, if we have failed to dispatch because of different issues; +- the unspent dispatch weight if the declared weight of some messages is less than their actual post-dispatch weight; +- the pay-dispatch-fee weight for every message that had dispatch fee paid at the source chain. + +The last component is computed as a difference between two benchmarks results - the `receive_single_message_proof` +benchmark (that assumes that the fee is paid during dispatch) and the `receive_single_prepaid_message_proof` +(that assumes that the dispatch fee is already paid). + +### Weight of `receive_messages_delivery_proof` call + +#### Related benchmarks + +| Benchmark | Description | +|-------------------------------------------------------------|------------------------------------------------------------------------------------------| +| `receive_delivery_proof_for_single_message` | Receives proof of single message delivery | +| `receive_delivery_proof_for_two_messages_by_single_relayer` | Receives proof of two messages delivery. Both messages are delivered by the same relayer | +| `receive_delivery_proof_for_two_messages_by_two_relayers` | Receives proof of two messages delivery. Messages are delivered by different relayers | + +#### Weight formula + +The weight formula is: +``` +Weight = BaseWeight + MessagesCount * MessageConfirmationWeight + + RelayersCount * RelayerRewardWeight + + Max(0, ActualProofSize - ExpectedProofSize) * ProofByteDeliveryWeight + + MessagesCount * (DbReadWeight + DbWriteWeight) +``` + +Where: + +| Component | How it is computed? | Description | +|---------------------------|-----------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `BaseWeight` | `2*receive_delivery_proof_for_single_message - receive_delivery_proof_for_two_messages_by_single_relayer` | Weight of receiving and parsing minimal delivery proof | +| `MessageDeliveryWeight` | `receive_delivery_proof_for_two_messages_by_single_relayer - receive_delivery_proof_for_single_message` | Weight of confirming every additional message | +| `MessagesCount` | | Provided by relayer | +| `RelayerRewardWeight` | `receive_delivery_proof_for_two_messages_by_two_relayers - receive_delivery_proof_for_two_messages_by_single_relayer` | Weight of rewarding every additional relayer | +| `RelayersCount` | | Provided by relayer | +| `ActualProofSize` | | Provided by relayer | +| `ExpectedProofSize` | `EXTRA_STORAGE_PROOF_SIZE` | Size of proof that we are expecting | +| `ProofByteDeliveryWeight` | `(receive_single_message_proof_16_kb - receive_single_message_proof_1_kb) / (15 * 1024)` | Weight of processing every additional proof byte over `ExpectedProofSize` limit. We're using the same formula, as for message delivery, because proof mechanism is assumed to be the same in both cases | + +#### Post-dispatch weight refunds of the `receive_messages_delivery_proof` call + +Weight formula of the `receive_messages_delivery_proof` call assumes that all messages in the proof +are actually delivered (so there are no already confirmed messages) and every messages is processed +by the `OnDeliveryConfirmed` callback. This means that for every message, we're adding single db read +weight and single db write weight. If, by some reason, messages are not processed by the +`OnDeliveryConfirmed` callback, or their processing is faster than that additional weight, the +difference is refunded to the submitter. + +#### Why we're always able to craft `receive_messages_delivery_proof` transaction? + +There can be at most `::MaxUnconfirmedMessagesAtInboundLane` +messages and at most +`::MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded +relayers in the single delivery confirmation transaction. + +We're checking that this transaction may be crafted in the +`pallet_bridge_messages::ensure_able_to_receive_confirmation` function, which must be called from every +runtime' tests. diff --git a/modules/messages/src/benchmarking.rs b/modules/messages/src/benchmarking.rs new file mode 100644 index 00000000000..b8360facacb --- /dev/null +++ b/modules/messages/src/benchmarking.rs @@ -0,0 +1,423 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Messages pallet benchmarking. + +use crate::{ + inbound_lane::InboundLaneStorage, inbound_lane_storage, outbound_lane, + weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, Call, OutboundLanes, +}; + +use bp_messages::{ + source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, DeliveredMessages, + InboundLaneData, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer, + UnrewardedRelayersState, +}; +use bp_runtime::StorageProofSize; +use frame_benchmarking::{account, benchmarks_instance_pallet}; +use frame_support::weights::Weight; +use frame_system::RawOrigin; +use sp_std::{ops::RangeInclusive, prelude::*}; + +const SEED: u32 = 0; + +/// Pallet we're benchmarking here. +pub struct Pallet, I: 'static>(crate::Pallet); + +/// Benchmark-specific message proof parameters. +#[derive(Debug)] +pub struct MessageProofParams { + /// Id of the lane. + pub lane: LaneId, + /// Range of messages to include in the proof. + pub message_nonces: RangeInclusive, + /// If `Some`, the proof needs to include this outbound lane data. + pub outbound_lane_data: Option, + /// Proof size requirements. + pub size: StorageProofSize, +} + +/// Benchmark-specific message delivery proof parameters. +#[derive(Debug)] +pub struct MessageDeliveryProofParams { + /// Id of the lane. + pub lane: LaneId, + /// The proof needs to include this inbound lane data. + pub inbound_lane_data: InboundLaneData, + /// Proof size requirements. + pub size: StorageProofSize, +} + +/// Trait that must be implemented by runtime. +pub trait Config: crate::Config { + /// Lane id to use in benchmarks. + fn bench_lane_id() -> LaneId { + Default::default() + } + /// Return id of relayer account at the bridged chain. + fn bridged_relayer_id() -> Self::InboundRelayer; + /// Create given account and give it enough balance for test purposes. + fn endow_account(account: &Self::AccountId); + /// Prepare messages proof to receive by the module. + fn prepare_message_proof( + params: MessageProofParams, + ) -> (::MessagesProof, Weight); + /// Prepare messages delivery proof to receive by the module. + fn prepare_message_delivery_proof( + params: MessageDeliveryProofParams, + ) -> >::MessagesDeliveryProof; + /// Returns true if message has been dispatched (either successfully or not). + fn is_message_dispatched(nonce: MessageNonce) -> bool; +} + +benchmarks_instance_pallet! { + // + // Benchmarks that are used directly by the runtime. + // + + // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: + // * proof does not include outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is successfully dispatched; + // * message requires all heavy checks done by dispatcher; + // * message dispatch fee is paid at target (this) chain. + // + // This is base benchmark for all other message delivery benchmarks. + receive_single_message_proof { + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + T::endow_account(&relayer_id_on_target); + + // mark messages 1..=20 as delivered + receive_messages::(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: T::bench_lane_id(), + message_nonces: 21..=21, + outbound_lane_data: None, + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) + verify { + assert_eq!( + crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), + 21, + ); + assert!(T::is_message_dispatched(21)); + } + + // Benchmark `receive_messages_proof` extrinsic with two minimal-weight messages and following conditions: + // * proof does not include outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is successfully dispatched; + // * message requires all heavy checks done by dispatcher; + // * message dispatch fee is paid at target (this) chain. + // + // The weight of single message delivery could be approximated as + // `weight(receive_two_messages_proof) - weight(receive_single_message_proof)`. + // This won't be super-accurate if message has non-zero dispatch weight, but estimation should + // be close enough to real weight. + receive_two_messages_proof { + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + T::endow_account(&relayer_id_on_target); + + // mark messages 1..=20 as delivered + receive_messages::(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: T::bench_lane_id(), + message_nonces: 21..=22, + outbound_lane_data: None, + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 2, dispatch_weight) + verify { + assert_eq!( + crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), + 22, + ); + assert!(T::is_message_dispatched(22)); + } + + // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: + // * proof includes outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is successfully dispatched; + // * message requires all heavy checks done by dispatcher; + // * message dispatch fee is paid at target (this) chain. + // + // The weight of outbound lane state delivery would be + // `weight(receive_single_message_proof_with_outbound_lane_state) - weight(receive_single_message_proof)`. + // This won't be super-accurate if message has non-zero dispatch weight, but estimation should + // be close enough to real weight. + receive_single_message_proof_with_outbound_lane_state { + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + T::endow_account(&relayer_id_on_target); + + // mark messages 1..=20 as delivered + receive_messages::(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: T::bench_lane_id(), + message_nonces: 21..=21, + outbound_lane_data: Some(OutboundLaneData { + oldest_unpruned_nonce: 21, + latest_received_nonce: 20, + latest_generated_nonce: 21, + }), + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) + verify { + let lane_state = crate::InboundLanes::::get(&T::bench_lane_id()); + assert_eq!(lane_state.last_delivered_nonce(), 21); + assert_eq!(lane_state.last_confirmed_nonce, 20); + assert!(T::is_message_dispatched(21)); + } + + // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: + // * the proof has many redundand trie nodes with total size of approximately 1KB; + // * proof does not include outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is successfully dispatched; + // * message requires all heavy checks done by dispatcher. + // + // With single KB of messages proof, the weight of the call is increased (roughly) by + // `(receive_single_message_proof_16KB - receive_single_message_proof_1_kb) / 15`. + receive_single_message_proof_1_kb { + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + T::endow_account(&relayer_id_on_target); + + // mark messages 1..=20 as delivered + receive_messages::(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: T::bench_lane_id(), + message_nonces: 21..=21, + outbound_lane_data: None, + size: StorageProofSize::HasExtraNodes(1024), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) + verify { + assert_eq!( + crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), + 21, + ); + assert!(T::is_message_dispatched(21)); + } + + // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: + // * the proof has many redundand trie nodes with total size of approximately 16KB; + // * proof does not include outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is successfully dispatched; + // * message requires all heavy checks done by dispatcher. + // + // Size of proof grows because it contains extra trie nodes in it. + // + // With single KB of messages proof, the weight of the call is increased (roughly) by + // `(receive_single_message_proof_16KB - receive_single_message_proof) / 15`. + receive_single_message_proof_16_kb { + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + T::endow_account(&relayer_id_on_target); + + // mark messages 1..=20 as delivered + receive_messages::(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: T::bench_lane_id(), + message_nonces: 21..=21, + outbound_lane_data: None, + size: StorageProofSize::HasExtraNodes(16 * 1024), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) + verify { + assert_eq!( + crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), + 21, + ); + assert!(T::is_message_dispatched(21)); + } + + // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: + // * proof does not include outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is successfully dispatched; + // * message requires all heavy checks done by dispatcher; + // * message dispatch fee is paid at source (bridged) chain. + // + // This benchmark is used to compute extra weight spent at target chain when fee is paid there. Then we use + // this information in two places: (1) to reduce weight of delivery tx if sender pays fee at the source chain + // and (2) to refund relayer with this weight if fee has been paid at the source chain. + receive_single_prepaid_message_proof { + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + T::endow_account(&relayer_id_on_target); + + // mark messages 1..=20 as delivered + receive_messages::(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: T::bench_lane_id(), + message_nonces: 21..=21, + outbound_lane_data: None, + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) + verify { + assert_eq!( + crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), + 21, + ); + assert!(T::is_message_dispatched(21)); + } + + // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: + // * single relayer is rewarded for relaying single message; + // * relayer account does not exist (in practice it needs to exist in production environment). + // + // This is base benchmark for all other confirmations delivery benchmarks. + receive_delivery_proof_for_single_message { + let relayer_id: T::AccountId = account("relayer", 0, SEED); + + // send message that we're going to confirm + send_regular_message::(); + + let relayers_state = UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }; + let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { + lane: T::bench_lane_id(), + inbound_lane_data: InboundLaneData { + relayers: vec![UnrewardedRelayer { + relayer: relayer_id.clone(), + messages: DeliveredMessages::new(1), + }].into_iter().collect(), + last_confirmed_nonce: 0, + }, + size: StorageProofSize::Minimal(0), + }); + }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) + verify { + assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 1); + } + + // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: + // * single relayer is rewarded for relaying two messages; + // * relayer account does not exist (in practice it needs to exist in production environment). + // + // Additional weight for paying single-message reward to the same relayer could be computed + // as `weight(receive_delivery_proof_for_two_messages_by_single_relayer) + // - weight(receive_delivery_proof_for_single_message)`. + receive_delivery_proof_for_two_messages_by_single_relayer { + let relayer_id: T::AccountId = account("relayer", 0, SEED); + + // send message that we're going to confirm + send_regular_message::(); + send_regular_message::(); + + let relayers_state = UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 2, + total_messages: 2, + last_delivered_nonce: 2, + }; + let mut delivered_messages = DeliveredMessages::new(1); + delivered_messages.note_dispatched_message(); + let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { + lane: T::bench_lane_id(), + inbound_lane_data: InboundLaneData { + relayers: vec![UnrewardedRelayer { + relayer: relayer_id.clone(), + messages: delivered_messages, + }].into_iter().collect(), + last_confirmed_nonce: 0, + }, + size: StorageProofSize::Minimal(0), + }); + }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) + verify { + assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); + } + + // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: + // * two relayers are rewarded for relaying single message each; + // * relayer account does not exist (in practice it needs to exist in production environment). + // + // Additional weight for paying reward to the next relayer could be computed + // as `weight(receive_delivery_proof_for_two_messages_by_two_relayers) + // - weight(receive_delivery_proof_for_two_messages_by_single_relayer)`. + receive_delivery_proof_for_two_messages_by_two_relayers { + let relayer1_id: T::AccountId = account("relayer1", 1, SEED); + let relayer2_id: T::AccountId = account("relayer2", 2, SEED); + + // send message that we're going to confirm + send_regular_message::(); + send_regular_message::(); + + let relayers_state = UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + messages_in_oldest_entry: 1, + total_messages: 2, + last_delivered_nonce: 2, + }; + let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { + lane: T::bench_lane_id(), + inbound_lane_data: InboundLaneData { + relayers: vec![ + UnrewardedRelayer { + relayer: relayer1_id.clone(), + messages: DeliveredMessages::new(1), + }, + UnrewardedRelayer { + relayer: relayer2_id.clone(), + messages: DeliveredMessages::new(2), + }, + ].into_iter().collect(), + last_confirmed_nonce: 0, + }, + size: StorageProofSize::Minimal(0), + }); + }: receive_messages_delivery_proof(RawOrigin::Signed(relayer1_id.clone()), proof, relayers_state) + verify { + assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); + } +} + +fn send_regular_message, I: 'static>() { + let mut outbound_lane = outbound_lane::(T::bench_lane_id()); + outbound_lane.send_message(vec![]); +} + +fn receive_messages, I: 'static>(nonce: MessageNonce) { + let mut inbound_lane_storage = inbound_lane_storage::(T::bench_lane_id()); + inbound_lane_storage.set_data(InboundLaneData { + relayers: vec![UnrewardedRelayer { + relayer: T::bridged_relayer_id(), + messages: DeliveredMessages::new(nonce), + }] + .into_iter() + .collect(), + last_confirmed_nonce: 0, + }); +} diff --git a/modules/messages/src/inbound_lane.rs b/modules/messages/src/inbound_lane.rs new file mode 100644 index 00000000000..00c63b5d670 --- /dev/null +++ b/modules/messages/src/inbound_lane.rs @@ -0,0 +1,545 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything about incoming messages receival. + +use crate::Config; + +use bp_messages::{ + target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, + DeliveredMessages, InboundLaneData, LaneId, MessageKey, MessageNonce, OutboundLaneData, + ReceivalResult, UnrewardedRelayer, +}; +use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; +use frame_support::{traits::Get, RuntimeDebug}; +use scale_info::{Type, TypeInfo}; +use sp_std::prelude::PartialEq; + +/// Inbound lane storage. +pub trait InboundLaneStorage { + /// Id of relayer on source chain. + type Relayer: Clone + PartialEq; + + /// Lane id. + fn id(&self) -> LaneId; + /// Return maximal number of unrewarded relayer entries in inbound lane. + fn max_unrewarded_relayer_entries(&self) -> MessageNonce; + /// Return maximal number of unconfirmed messages in inbound lane. + fn max_unconfirmed_messages(&self) -> MessageNonce; + /// Get lane data from the storage. + fn data(&self) -> InboundLaneData; + /// Update lane data in the storage. + fn set_data(&mut self, data: InboundLaneData); +} + +/// Inbound lane data wrapper that implements `MaxEncodedLen`. +/// +/// We have already had `MaxEncodedLen`-like functionality before, but its usage has +/// been localized and we haven't been passing bounds (maximal count of unrewarded relayer entries, +/// maximal count of unconfirmed messages) everywhere. This wrapper allows us to avoid passing +/// these generic bounds all over the code. +/// +/// The encoding of this type matches encoding of the corresponding `MessageData`. +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq)] +pub struct StoredInboundLaneData, I: 'static>(pub InboundLaneData); + +impl, I: 'static> sp_std::ops::Deref for StoredInboundLaneData { + type Target = InboundLaneData; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl, I: 'static> sp_std::ops::DerefMut for StoredInboundLaneData { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl, I: 'static> Default for StoredInboundLaneData { + fn default() -> Self { + StoredInboundLaneData(Default::default()) + } +} + +impl, I: 'static> From> + for InboundLaneData +{ + fn from(data: StoredInboundLaneData) -> Self { + data.0 + } +} + +impl, I: 'static> EncodeLike> + for InboundLaneData +{ +} + +impl, I: 'static> TypeInfo for StoredInboundLaneData { + type Identity = Self; + + fn type_info() -> Type { + InboundLaneData::::type_info() + } +} + +impl, I: 'static> MaxEncodedLen for StoredInboundLaneData { + fn max_encoded_len() -> usize { + InboundLaneData::::encoded_size_hint( + T::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize, + ) + .unwrap_or(usize::MAX) + } +} + +/// Inbound messages lane. +pub struct InboundLane { + storage: S, +} + +impl InboundLane { + /// Create new inbound lane backed by given storage. + pub fn new(storage: S) -> Self { + InboundLane { storage } + } + + /// Receive state of the corresponding outbound lane. + pub fn receive_state_update( + &mut self, + outbound_lane_data: OutboundLaneData, + ) -> Option { + let mut data = self.storage.data(); + let last_delivered_nonce = data.last_delivered_nonce(); + + if outbound_lane_data.latest_received_nonce > last_delivered_nonce { + // this is something that should never happen if proofs are correct + return None + } + if outbound_lane_data.latest_received_nonce <= data.last_confirmed_nonce { + return None + } + + let new_confirmed_nonce = outbound_lane_data.latest_received_nonce; + data.last_confirmed_nonce = new_confirmed_nonce; + // Firstly, remove all of the records where higher nonce <= new confirmed nonce + while data + .relayers + .front() + .map(|entry| entry.messages.end <= new_confirmed_nonce) + .unwrap_or(false) + { + data.relayers.pop_front(); + } + // Secondly, update the next record with lower nonce equal to new confirmed nonce if needed. + // Note: There will be max. 1 record to update as we don't allow messages from relayers to + // overlap. + match data.relayers.front_mut() { + Some(entry) if entry.messages.begin < new_confirmed_nonce => { + entry.messages.begin = new_confirmed_nonce + 1; + }, + _ => {}, + } + + self.storage.set_data(data); + Some(outbound_lane_data.latest_received_nonce) + } + + /// Receive new message. + pub fn receive_message, AccountId>( + &mut self, + relayer_at_bridged_chain: &S::Relayer, + relayer_at_this_chain: &AccountId, + nonce: MessageNonce, + message_data: DispatchMessageData, + ) -> ReceivalResult { + let mut data = self.storage.data(); + let is_correct_message = nonce == data.last_delivered_nonce() + 1; + if !is_correct_message { + return ReceivalResult::InvalidNonce + } + + // if there are more unrewarded relayer entries than we may accept, reject this message + if data.relayers.len() as MessageNonce >= self.storage.max_unrewarded_relayer_entries() { + return ReceivalResult::TooManyUnrewardedRelayers + } + + // if there are more unconfirmed messages than we may accept, reject this message + let unconfirmed_messages_count = nonce.saturating_sub(data.last_confirmed_nonce); + if unconfirmed_messages_count > self.storage.max_unconfirmed_messages() { + return ReceivalResult::TooManyUnconfirmedMessages + } + + // then, dispatch message + let dispatch_result = Dispatch::dispatch( + relayer_at_this_chain, + DispatchMessage { + key: MessageKey { lane_id: self.storage.id(), nonce }, + data: message_data, + }, + ); + + // now let's update inbound lane storage + let push_new = match data.relayers.back_mut() { + Some(entry) if entry.relayer == *relayer_at_bridged_chain => { + entry.messages.note_dispatched_message(); + false + }, + _ => true, + }; + if push_new { + data.relayers.push_back(UnrewardedRelayer { + relayer: (*relayer_at_bridged_chain).clone(), + messages: DeliveredMessages::new(nonce), + }); + } + self.storage.set_data(data); + + ReceivalResult::Dispatched(dispatch_result) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + inbound_lane, + mock::{ + dispatch_result, inbound_message_data, run_test, unrewarded_relayer, + TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, + TEST_RELAYER_B, TEST_RELAYER_C, + }, + RuntimeInboundLaneStorage, + }; + + fn receive_regular_message( + lane: &mut InboundLane>, + nonce: MessageNonce, + ) { + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + nonce, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::Dispatched(dispatch_result(0)) + ); + } + + #[test] + fn receive_status_update_ignores_status_from_the_future() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + receive_regular_message(&mut lane, 1); + assert_eq!( + lane.receive_state_update(OutboundLaneData { + latest_received_nonce: 10, + ..Default::default() + }), + None, + ); + + assert_eq!(lane.storage.data().last_confirmed_nonce, 0); + }); + } + + #[test] + fn receive_status_update_ignores_obsolete_status() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + receive_regular_message(&mut lane, 1); + receive_regular_message(&mut lane, 2); + receive_regular_message(&mut lane, 3); + assert_eq!( + lane.receive_state_update(OutboundLaneData { + latest_received_nonce: 3, + ..Default::default() + }), + Some(3), + ); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); + + assert_eq!( + lane.receive_state_update(OutboundLaneData { + latest_received_nonce: 3, + ..Default::default() + }), + None, + ); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); + }); + } + + #[test] + fn receive_status_update_works() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + receive_regular_message(&mut lane, 1); + receive_regular_message(&mut lane, 2); + receive_regular_message(&mut lane, 3); + assert_eq!(lane.storage.data().last_confirmed_nonce, 0); + assert_eq!( + lane.storage.data().relayers, + vec![unrewarded_relayer(1, 3, TEST_RELAYER_A)] + ); + + assert_eq!( + lane.receive_state_update(OutboundLaneData { + latest_received_nonce: 2, + ..Default::default() + }), + Some(2), + ); + assert_eq!(lane.storage.data().last_confirmed_nonce, 2); + assert_eq!( + lane.storage.data().relayers, + vec![unrewarded_relayer(3, 3, TEST_RELAYER_A)] + ); + + assert_eq!( + lane.receive_state_update(OutboundLaneData { + latest_received_nonce: 3, + ..Default::default() + }), + Some(3), + ); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().relayers, vec![]); + }); + } + + #[test] + fn receive_status_update_works_with_batches_from_relayers() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + let mut seed_storage_data = lane.storage.data(); + // Prepare data + seed_storage_data.last_confirmed_nonce = 0; + seed_storage_data.relayers.push_back(unrewarded_relayer(1, 1, TEST_RELAYER_A)); + // Simulate messages batch (2, 3, 4) from relayer #2 + seed_storage_data.relayers.push_back(unrewarded_relayer(2, 4, TEST_RELAYER_B)); + seed_storage_data.relayers.push_back(unrewarded_relayer(5, 5, TEST_RELAYER_C)); + lane.storage.set_data(seed_storage_data); + // Check + assert_eq!( + lane.receive_state_update(OutboundLaneData { + latest_received_nonce: 3, + ..Default::default() + }), + Some(3), + ); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); + assert_eq!( + lane.storage.data().relayers, + vec![ + unrewarded_relayer(4, 4, TEST_RELAYER_B), + unrewarded_relayer(5, 5, TEST_RELAYER_C) + ] + ); + }); + } + + #[test] + fn fails_to_receive_message_with_incorrect_nonce() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + 10, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::InvalidNonce + ); + assert_eq!(lane.storage.data().last_delivered_nonce(), 0); + }); + } + + #[test] + fn fails_to_receive_messages_above_unrewarded_relayer_entries_limit_per_lane() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + let max_nonce = + ::MaxUnrewardedRelayerEntriesAtInboundLane::get(); + for current_nonce in 1..max_nonce + 1 { + assert_eq!( + lane.receive_message::( + &(TEST_RELAYER_A + current_nonce), + &(TEST_RELAYER_A + current_nonce), + current_nonce, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::Dispatched(dispatch_result(0)) + ); + } + // Fails to dispatch new message from different than latest relayer. + assert_eq!( + lane.receive_message::( + &(TEST_RELAYER_A + max_nonce + 1), + &(TEST_RELAYER_A + max_nonce + 1), + max_nonce + 1, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::TooManyUnrewardedRelayers, + ); + // Fails to dispatch new messages from latest relayer. Prevents griefing attacks. + assert_eq!( + lane.receive_message::( + &(TEST_RELAYER_A + max_nonce), + &(TEST_RELAYER_A + max_nonce), + max_nonce + 1, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::TooManyUnrewardedRelayers, + ); + }); + } + + #[test] + fn fails_to_receive_messages_above_unconfirmed_messages_limit_per_lane() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + let max_nonce = ::MaxUnconfirmedMessagesAtInboundLane::get(); + for current_nonce in 1..=max_nonce { + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + current_nonce, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::Dispatched(dispatch_result(0)) + ); + } + // Fails to dispatch new message from different than latest relayer. + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_B, + &TEST_RELAYER_B, + max_nonce + 1, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::TooManyUnconfirmedMessages, + ); + // Fails to dispatch new messages from latest relayer. + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + max_nonce + 1, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::TooManyUnconfirmedMessages, + ); + }); + } + + #[test] + fn correctly_receives_following_messages_from_two_relayers_alternately() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + 1, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::Dispatched(dispatch_result(0)) + ); + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_B, + &TEST_RELAYER_B, + 2, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::Dispatched(dispatch_result(0)) + ); + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + 3, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::Dispatched(dispatch_result(0)) + ); + assert_eq!( + lane.storage.data().relayers, + vec![ + unrewarded_relayer(1, 1, TEST_RELAYER_A), + unrewarded_relayer(2, 2, TEST_RELAYER_B), + unrewarded_relayer(3, 3, TEST_RELAYER_A) + ] + ); + }); + } + + #[test] + fn rejects_same_message_from_two_different_relayers() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + 1, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::Dispatched(dispatch_result(0)) + ); + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_B, + &TEST_RELAYER_B, + 1, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::InvalidNonce, + ); + }); + } + + #[test] + fn correct_message_is_processed_instantly() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + receive_regular_message(&mut lane, 1); + assert_eq!(lane.storage.data().last_delivered_nonce(), 1); + }); + } + + #[test] + fn unspent_weight_is_returned_by_receive_message() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + let mut payload = REGULAR_PAYLOAD; + *payload.dispatch_result.unspent_weight.ref_time_mut() = 1; + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + 1, + inbound_message_data(payload) + ), + ReceivalResult::Dispatched(dispatch_result(1)) + ); + }); + } +} diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs new file mode 100644 index 00000000000..0df6c686cc6 --- /dev/null +++ b/modules/messages/src/lib.rs @@ -0,0 +1,1934 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Runtime module that allows sending and receiving messages using lane concept: +//! +//! 1) the message is sent using `send_message()` call; +//! 2) every outbound message is assigned nonce; +//! 3) the messages are stored in the storage; +//! 4) external component (relay) delivers messages to bridged chain; +//! 5) messages are processed in order (ordered by assigned nonce); +//! 6) relay may send proof-of-delivery back to this chain. +//! +//! Once message is sent, its progress can be tracked by looking at module events. +//! The assigned nonce is reported using `MessageAccepted` event. When message is +//! delivered to the the bridged chain, it is reported using `MessagesDelivered` event. +//! +//! **IMPORTANT NOTE**: after generating weights (custom `WeighInfo` implementation) for +//! your runtime (where this module is plugged to), please add test for these weights. +//! The test should call the `ensure_weights_are_correct` function from this module. +//! If this test fails with your weights, then either weights are computed incorrectly, +//! or some benchmarks assumptions are broken for your runtime. + +#![cfg_attr(not(feature = "std"), no_std)] +// Generated by `decl_event!` +#![allow(clippy::unused_unit)] + +pub use inbound_lane::StoredInboundLaneData; +pub use outbound_lane::StoredMessagePayload; +pub use weights::WeightInfo; +pub use weights_ext::{ + ensure_able_to_receive_confirmation, ensure_able_to_receive_message, + ensure_weights_are_correct, WeightInfoExt, EXPECTED_DEFAULT_MESSAGE_LENGTH, +}; + +use crate::{ + inbound_lane::{InboundLane, InboundLaneStorage}, + outbound_lane::{OutboundLane, OutboundLaneStorage, ReceivalConfirmationResult}, +}; + +use bp_messages::{ + source_chain::{ + LaneMessageVerifier, MessageDeliveryAndDispatchPayment, RelayersRewards, + SendMessageArtifacts, TargetHeaderChain, + }, + target_chain::{ + DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain, + }, + total_unrewarded_messages, DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId, + MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData, + OutboundMessageDetails, UnrewardedRelayer, UnrewardedRelayersState, +}; +use bp_runtime::{BasicOperatingMode, ChainId, OwnedBridgeModule, Size}; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get}; +use sp_runtime::traits::UniqueSaturatedFrom; +use sp_std::{ + cell::RefCell, collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive, + prelude::*, +}; + +mod inbound_lane; +mod outbound_lane; +mod weights_ext; + +pub mod weights; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + +#[cfg(test)] +mod mock; + +pub use pallet::*; + +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-messages"; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use bp_messages::{ReceivalResult, ReceivedMessages}; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + // General types + + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + /// Benchmarks results from runtime we're plugged into. + type WeightInfo: WeightInfoExt; + + /// Gets the chain id value from the instance. + #[pallet::constant] + type BridgedChainId: Get; + + /// Get all active outbound lanes that the message pallet is serving. + type ActiveOutboundLanes: Get<&'static [LaneId]>; + /// Maximal number of unrewarded relayer entries at inbound lane. Unrewarded means that the + /// relayer has delivered messages, but either confirmations haven't been delivered back to + /// the source chain, or we haven't received reward confirmations yet. + /// + /// This constant limits maximal number of entries in the `InboundLaneData::relayers`. Keep + /// in mind that the same relayer account may take several (non-consecutive) entries in this + /// set. + type MaxUnrewardedRelayerEntriesAtInboundLane: Get; + /// Maximal number of unconfirmed messages at inbound lane. Unconfirmed means that the + /// message has been delivered, but either confirmations haven't been delivered back to the + /// source chain, or we haven't received reward confirmations for these messages yet. + /// + /// This constant limits difference between last message from last entry of the + /// `InboundLaneData::relayers` and first message at the first entry. + /// + /// There is no point of making this parameter lesser than + /// MaxUnrewardedRelayerEntriesAtInboundLane, because then maximal number of relayer entries + /// will be limited by maximal number of messages. + /// + /// This value also represents maximal number of messages in single delivery transaction. + /// Transaction that is declaring more messages than this value, will be rejected. Even if + /// these messages are from different lanes. + type MaxUnconfirmedMessagesAtInboundLane: Get; + + /// Maximal encoded size of the outbound payload. + #[pallet::constant] + type MaximalOutboundPayloadSize: Get; + /// Payload type of outbound messages. This payload is dispatched on the bridged chain. + type OutboundPayload: Parameter + Size; + + /// Payload type of inbound messages. This payload is dispatched on this chain. + type InboundPayload: Decode; + /// Identifier of relayer that deliver messages to this chain. Relayer reward is paid on the + /// bridged chain. + type InboundRelayer: Parameter + MaxEncodedLen; + + // Types that are used by outbound_lane (on source chain). + + /// Target header chain. + type TargetHeaderChain: TargetHeaderChain; + /// Message payload verifier. + type LaneMessageVerifier: LaneMessageVerifier; + /// Message delivery payment. + type MessageDeliveryAndDispatchPayment: MessageDeliveryAndDispatchPayment< + Self::RuntimeOrigin, + Self::AccountId, + >; + + // Types that are used by inbound_lane (on target chain). + + /// Source header chain, as it is represented on target chain. + type SourceHeaderChain: SourceHeaderChain; + /// Message dispatch. + type MessageDispatch: MessageDispatch< + Self::AccountId, + DispatchPayload = Self::InboundPayload, + >; + } + + /// Shortcut to messages proof type for Config. + type MessagesProofOf = + <>::SourceHeaderChain as SourceHeaderChain>::MessagesProof; + /// Shortcut to messages delivery proof type for Config. + type MessagesDeliveryProofOf = + <>::TargetHeaderChain as TargetHeaderChain< + >::OutboundPayload, + ::AccountId, + >>::MessagesDeliveryProof; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(PhantomData<(T, I)>); + + impl, I: 'static> OwnedBridgeModule for Pallet { + const LOG_TARGET: &'static str = LOG_TARGET; + type OwnerStorage = PalletOwner; + type OperatingMode = MessagesOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + } + + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet + where + u32: TryFrom<::BlockNumber>, + { + fn on_idle(_block: T::BlockNumber, remaining_weight: Weight) -> Weight { + // we'll need at least to read outbound lane state, kill a message and update lane state + let db_weight = T::DbWeight::get(); + if !remaining_weight.all_gte(db_weight.reads_writes(1, 2)) { + return Weight::zero() + } + + // messages from lane with index `i` in `ActiveOutboundLanes` are pruned when + // `System::block_number() % lanes.len() == i`. Otherwise we need to read lane states on + // every block, wasting the whole `remaining_weight` for nothing and causing starvation + // of the last lane pruning + let active_lanes = T::ActiveOutboundLanes::get(); + let active_lanes_len = (active_lanes.len() as u32).into(); + let active_lane_index = u32::unique_saturated_from( + frame_system::Pallet::::block_number() % active_lanes_len, + ); + let active_lane_id = active_lanes[active_lane_index as usize]; + + // first db read - outbound lane state + let mut active_lane = outbound_lane::(active_lane_id); + let mut used_weight = db_weight.reads(1); + // and here we'll have writes + used_weight += active_lane.prune_messages(db_weight, remaining_weight - used_weight); + + // we already checked we have enough `remaining_weight` to cover this `used_weight` + used_weight + } + } + + #[pallet::call] + impl, I: 'static> Pallet { + /// Change `PalletOwner`. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { + >::set_owner(origin, new_owner) + } + + /// Halt or resume all/some pallet operations. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_operating_mode( + origin: OriginFor, + operating_mode: MessagesOperatingMode, + ) -> DispatchResult { + >::set_operating_mode(origin, operating_mode) + } + + /// Receive messages proof from bridged chain. + /// + /// The weight of the call assumes that the transaction always brings outbound lane + /// state update. Because of that, the submitter (relayer) has no benefit of not including + /// this data in the transaction, so reward confirmations lags should be minimal. + #[pallet::weight(T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight))] + pub fn receive_messages_proof( + origin: OriginFor, + relayer_id_at_bridged_chain: T::InboundRelayer, + proof: MessagesProofOf, + messages_count: u32, + dispatch_weight: Weight, + ) -> DispatchResultWithPostInfo { + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + let relayer_id_at_this_chain = ensure_signed(origin)?; + + // reject transactions that are declaring too many messages + ensure!( + MessageNonce::from(messages_count) <= T::MaxUnconfirmedMessagesAtInboundLane::get(), + Error::::TooManyMessagesInTheProof + ); + + // why do we need to know the weight of this (`receive_messages_proof`) call? Because + // we may want to return some funds for not-dispatching (or partially dispatching) some + // messages to the call origin (relayer). And this is done by returning actual weight + // from the call. But we only know dispatch weight of every messages. So to refund + // relayer because we have not dispatched Message, we need to: + // + // ActualWeight = DeclaredWeight - Message.DispatchWeight + // + // The DeclaredWeight is exactly what's computed here. Unfortunately it is impossible + // to get pre-computed value (and it has been already computed by the executive). + let declared_weight = T::WeightInfo::receive_messages_proof_weight( + &proof, + messages_count, + dispatch_weight, + ); + let mut actual_weight = declared_weight; + + // verify messages proof && convert proof into messages + let messages = verify_and_decode_messages_proof::< + T::SourceHeaderChain, + T::InboundPayload, + >(proof, messages_count) + .map_err(|err| { + log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,); + + Error::::InvalidMessagesProof + })?; + + // dispatch messages and (optionally) update lane(s) state(s) + let mut total_messages = 0; + let mut valid_messages = 0; + let mut messages_received_status = Vec::with_capacity(messages.len()); + let mut dispatch_weight_left = dispatch_weight; + for (lane_id, lane_data) in messages { + let mut lane = inbound_lane::(lane_id); + + if let Some(lane_state) = lane_data.lane_state { + let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state); + if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce { + log::trace!( + target: LOG_TARGET, + "Received lane {:?} state update: latest_confirmed_nonce={}", + lane_id, + updated_latest_confirmed_nonce, + ); + } + } + + let mut lane_messages_received_status = + ReceivedMessages::new(lane_id, Vec::with_capacity(lane_data.messages.len())); + let mut is_lane_processing_stopped_no_weight_left = false; + + for mut message in lane_data.messages { + debug_assert_eq!(message.key.lane_id, lane_id); + total_messages += 1; + + if is_lane_processing_stopped_no_weight_left { + lane_messages_received_status + .push_skipped_for_not_enough_weight(message.key.nonce); + continue + } + + // ensure that relayer has declared enough weight for dispatching next message + // on this lane. We can't dispatch lane messages out-of-order, so if declared + // weight is not enough, let's move to next lane + let message_dispatch_weight = T::MessageDispatch::dispatch_weight(&mut message); + if message_dispatch_weight.any_gt(dispatch_weight_left) { + log::trace!( + target: LOG_TARGET, + "Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}", + lane_id, + message_dispatch_weight, + dispatch_weight_left, + ); + lane_messages_received_status + .push_skipped_for_not_enough_weight(message.key.nonce); + is_lane_processing_stopped_no_weight_left = true; + continue + } + + let receival_result = lane.receive_message::( + &relayer_id_at_bridged_chain, + &relayer_id_at_this_chain, + message.key.nonce, + message.data, + ); + + // note that we're returning unspent weight to relayer even if message has been + // rejected by the lane. This allows relayers to submit spam transactions with + // e.g. the same set of already delivered messages over and over again, without + // losing funds for messages dispatch. But keep in mind that relayer pays base + // delivery transaction cost anyway. And base cost covers everything except + // dispatch, so we have a balance here. + let (unspent_weight, refund_pay_dispatch_fee) = match &receival_result { + ReceivalResult::Dispatched(dispatch_result) => { + valid_messages += 1; + ( + dispatch_result.unspent_weight, + !dispatch_result.dispatch_fee_paid_during_dispatch, + ) + }, + ReceivalResult::InvalidNonce | + ReceivalResult::TooManyUnrewardedRelayers | + ReceivalResult::TooManyUnconfirmedMessages => (message_dispatch_weight, true), + }; + lane_messages_received_status.push(message.key.nonce, receival_result); + + let unspent_weight = unspent_weight.min(message_dispatch_weight); + dispatch_weight_left -= message_dispatch_weight - unspent_weight; + actual_weight = actual_weight.saturating_sub(unspent_weight).saturating_sub( + // delivery call weight formula assumes that the fee is paid at + // this (target) chain. If the message is prepaid at the source + // chain, let's refund relayer with this extra cost. + if refund_pay_dispatch_fee { + T::WeightInfo::pay_inbound_dispatch_fee_overhead() + } else { + Weight::zero() + }, + ); + } + + messages_received_status.push(lane_messages_received_status); + } + + log::debug!( + target: LOG_TARGET, + "Received messages: total={}, valid={}. Weight used: {}/{}", + total_messages, + valid_messages, + actual_weight, + declared_weight, + ); + + Self::deposit_event(Event::MessagesReceived(messages_received_status)); + + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) + } + + /// Receive messages delivery proof from bridged chain. + #[pallet::weight(T::WeightInfo::receive_messages_delivery_proof_weight( + proof, + relayers_state, + ))] + pub fn receive_messages_delivery_proof( + origin: OriginFor, + proof: MessagesDeliveryProofOf, + relayers_state: UnrewardedRelayersState, + ) -> DispatchResult { + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + + let confirmation_relayer = ensure_signed(origin)?; + let (lane_id, lane_data) = T::TargetHeaderChain::verify_messages_delivery_proof(proof) + .map_err(|err| { + log::trace!( + target: LOG_TARGET, + "Rejecting invalid messages delivery proof: {:?}", + err, + ); + + Error::::InvalidMessagesDeliveryProof + })?; + + // verify that the relayer has declared correct `lane_data::relayers` state + // (we only care about total number of entries and messages, because this affects call + // weight) + ensure!( + total_unrewarded_messages(&lane_data.relayers).unwrap_or(MessageNonce::MAX) == + relayers_state.total_messages && + lane_data.relayers.len() as MessageNonce == + relayers_state.unrewarded_relayer_entries, + Error::::InvalidUnrewardedRelayersState + ); + // the `last_delivered_nonce` field may also be used by the signed extension. Even + // though providing wrong value isn't critical, let's also check it here. + ensure!( + lane_data.last_delivered_nonce() == relayers_state.last_delivered_nonce, + Error::::InvalidUnrewardedRelayersState + ); + + // mark messages as delivered + let mut lane = outbound_lane::(lane_id); + let last_delivered_nonce = lane_data.last_delivered_nonce(); + let confirmed_messages = match lane.confirm_delivery( + relayers_state.total_messages, + last_delivered_nonce, + &lane_data.relayers, + ) { + ReceivalConfirmationResult::ConfirmedMessages(confirmed_messages) => + Some(confirmed_messages), + ReceivalConfirmationResult::NoNewConfirmations => None, + ReceivalConfirmationResult::TryingToConfirmMoreMessagesThanExpected( + to_confirm_messages_count, + ) => { + log::trace!( + target: LOG_TARGET, + "Messages delivery proof contains too many messages to confirm: {} vs declared {}", + to_confirm_messages_count, + relayers_state.total_messages, + ); + + fail!(Error::::TryingToConfirmMoreMessagesThanExpected); + }, + error => { + log::trace!( + target: LOG_TARGET, + "Messages delivery proof contains invalid unrewarded relayers vec: {:?}", + error, + ); + + fail!(Error::::InvalidUnrewardedRelayers); + }, + }; + + if let Some(confirmed_messages) = confirmed_messages { + // emit 'delivered' event + let received_range = confirmed_messages.begin..=confirmed_messages.end; + Self::deposit_event(Event::MessagesDelivered { + lane_id, + messages: confirmed_messages, + }); + + // if some new messages have been confirmed, reward relayers + >::MessageDeliveryAndDispatchPayment::pay_relayers_rewards( + lane_id, + lane_data.relayers, + &confirmation_relayer, + &received_range, + ); + } + + log::trace!( + target: LOG_TARGET, + "Received messages delivery proof up to (and including) {} at lane {:?}", + last_delivered_nonce, + lane_id, + ); + + Ok(()) + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// Message has been accepted and is waiting to be delivered. + MessageAccepted { lane_id: LaneId, nonce: MessageNonce }, + /// Messages have been received from the bridged chain. + MessagesReceived( + Vec< + ReceivedMessages< + >::DispatchLevelResult, + >, + >, + ), + /// Messages in the inclusive range have been delivered to the bridged chain. + MessagesDelivered { lane_id: LaneId, messages: DeliveredMessages }, + } + + #[pallet::error] + pub enum Error { + /// Pallet is not in Normal operating mode. + NotOperatingNormally, + /// The outbound lane is inactive. + InactiveOutboundLane, + /// The message is too large to be sent over the bridge. + MessageIsTooLarge, + /// Message has been treated as invalid by chain verifier. + MessageRejectedByChainVerifier, + /// Message has been treated as invalid by lane verifier. + MessageRejectedByLaneVerifier, + /// Submitter has failed to pay fee for delivering and dispatching messages. + FailedToWithdrawMessageFee, + /// The transaction brings too many messages. + TooManyMessagesInTheProof, + /// Invalid messages has been submitted. + InvalidMessagesProof, + /// Invalid messages delivery proof has been submitted. + InvalidMessagesDeliveryProof, + /// The bridged chain has invalid `UnrewardedRelayers` in its storage (fatal for the lane). + InvalidUnrewardedRelayers, + /// The relayer has declared invalid unrewarded relayers state in the + /// `receive_messages_delivery_proof` call. + InvalidUnrewardedRelayersState, + /// The message someone is trying to work with (i.e. increase fee) is already-delivered. + MessageIsAlreadyDelivered, + /// The message someone is trying to work with (i.e. increase fee) is not yet sent. + MessageIsNotYetSent, + /// The number of actually confirmed messages is going to be larger than the number of + /// messages in the proof. This may mean that this or bridged chain storage is corrupted. + TryingToConfirmMoreMessagesThanExpected, + /// Error generated by the `OwnedBridgeModule` trait. + BridgeModule(bp_runtime::OwnedBridgeModuleError), + } + + /// Optional pallet owner. + /// + /// Pallet owner has a right to halt all pallet operations and then resume it. If it is + /// `None`, then there are no direct ways to halt/resume pallet operations, but other + /// runtime methods may still be used to do that (i.e. democracy::referendum to update halt + /// flag directly or call the `halt_operations`). + #[pallet::storage] + #[pallet::getter(fn module_owner)] + pub type PalletOwner, I: 'static = ()> = StorageValue<_, T::AccountId>; + + /// The current operating mode of the pallet. + /// + /// Depending on the mode either all, some, or no transactions will be allowed. + #[pallet::storage] + #[pallet::getter(fn operating_mode)] + pub type PalletOperatingMode, I: 'static = ()> = + StorageValue<_, MessagesOperatingMode, ValueQuery>; + + /// Map of lane id => inbound lane data. + #[pallet::storage] + pub type InboundLanes, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, LaneId, StoredInboundLaneData, ValueQuery>; + + /// Map of lane id => outbound lane data. + #[pallet::storage] + pub type OutboundLanes, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, LaneId, OutboundLaneData, ValueQuery>; + + /// All queued outbound messages. + #[pallet::storage] + pub type OutboundMessages, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, MessageKey, StoredMessagePayload>; + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + /// Initial pallet operating mode. + pub operating_mode: MessagesOperatingMode, + /// Initial pallet owner. + pub owner: Option, + /// Dummy marker. + pub phantom: sp_std::marker::PhantomData, + } + + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { + operating_mode: Default::default(), + owner: Default::default(), + phantom: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + PalletOperatingMode::::put(self.operating_mode); + if let Some(ref owner) = self.owner { + PalletOwner::::put(owner); + } + } + } + + impl, I: 'static> Pallet { + /// Get stored data of the outbound message with given nonce. + pub fn outbound_message_data(lane: LaneId, nonce: MessageNonce) -> Option { + OutboundMessages::::get(MessageKey { lane_id: lane, nonce }).map(Into::into) + } + + /// Prepare data, related to given inbound message. + pub fn inbound_message_data( + lane: LaneId, + payload: MessagePayload, + outbound_details: OutboundMessageDetails, + ) -> InboundMessageDetails { + let mut dispatch_message = DispatchMessage { + key: MessageKey { lane_id: lane, nonce: outbound_details.nonce }, + data: payload.into(), + }; + InboundMessageDetails { + dispatch_weight: T::MessageDispatch::dispatch_weight(&mut dispatch_message), + } + } + } +} + +impl bp_messages::source_chain::MessagesBridge + for Pallet +where + T: Config, + I: 'static, +{ + type Error = sp_runtime::DispatchErrorWithPostInfo; + + fn send_message( + sender: T::RuntimeOrigin, + lane: LaneId, + message: T::OutboundPayload, + ) -> Result { + crate::send_message::(sender, lane, message) + } +} + +/// Function that actually sends message. +fn send_message, I: 'static>( + submitter: T::RuntimeOrigin, + lane_id: LaneId, + payload: T::OutboundPayload, +) -> sp_std::result::Result< + SendMessageArtifacts, + sp_runtime::DispatchErrorWithPostInfo, +> { + ensure_normal_operating_mode::()?; + + // let's check if outbound lane is active + ensure!(T::ActiveOutboundLanes::get().contains(&lane_id), Error::::InactiveOutboundLane,); + + // let's first check if message can be delivered to target chain + T::TargetHeaderChain::verify_message(&payload).map_err(|err| { + log::trace!( + target: LOG_TARGET, + "Message to lane {:?} is rejected by target chain: {:?}", + lane_id, + err, + ); + + Error::::MessageRejectedByChainVerifier + })?; + + // now let's enforce any additional lane rules + let mut lane = outbound_lane::(lane_id); + T::LaneMessageVerifier::verify_message(&submitter, &lane_id, &lane.data(), &payload).map_err( + |err| { + log::trace!( + target: LOG_TARGET, + "Message to lane {:?} is rejected by lane verifier: {:?}", + lane_id, + err, + ); + + Error::::MessageRejectedByLaneVerifier + }, + )?; + + // finally, save message in outbound storage and emit event + let encoded_payload = payload.encode(); + let encoded_payload_len = encoded_payload.len(); + ensure!( + encoded_payload_len <= T::MaximalOutboundPayloadSize::get() as usize, + Error::::MessageIsTooLarge + ); + let nonce = lane.send_message(encoded_payload); + + log::trace!( + target: LOG_TARGET, + "Accepted message {} to lane {:?}. Message size: {:?}", + nonce, + lane_id, + encoded_payload_len, + ); + + Pallet::::deposit_event(Event::MessageAccepted { lane_id, nonce }); + + // we may introduce benchmarks for that, but no heavy ops planned here apart from + // db reads and writes. There are currently 2 db reads and 2 db writes: + // - one db read for operation mode check (`ensure_normal_operating_mode`); + // - one db read for outbound lane state (`outbound_lane`); + // - one db write for outbound lane state (`send_message`); + // - one db write for the message (`send_message`); + let actual_weight = T::DbWeight::get().reads_writes(2, 2); + + Ok(SendMessageArtifacts { nonce, weight: actual_weight }) +} + +/// Calculate the number of messages that the relayers have delivered. +pub fn calc_relayers_rewards( + messages_relayers: VecDeque>, + received_range: &RangeInclusive, +) -> RelayersRewards +where + T: frame_system::Config + crate::Config, + I: 'static, +{ + // remember to reward relayers that have delivered messages + // this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain + let mut relayers_rewards = RelayersRewards::new(); + for entry in messages_relayers { + let nonce_begin = sp_std::cmp::max(entry.messages.begin, *received_range.start()); + let nonce_end = sp_std::cmp::min(entry.messages.end, *received_range.end()); + + // loop won't proceed if current entry is ahead of received range (begin > end). + // this loop is bound by `T::MaxUnconfirmedMessagesAtInboundLane` on the bridged chain + if nonce_end >= nonce_begin { + *relayers_rewards.entry(entry.relayer).or_default() += nonce_end - nonce_begin + 1; + } + } + relayers_rewards +} + +/// Ensure that the pallet is in normal operational mode. +fn ensure_normal_operating_mode, I: 'static>() -> Result<(), Error> { + if PalletOperatingMode::::get() == + MessagesOperatingMode::Basic(BasicOperatingMode::Normal) + { + return Ok(()) + } + + Err(Error::::NotOperatingNormally) +} + +/// Creates new inbound lane object, backed by runtime storage. +fn inbound_lane, I: 'static>( + lane_id: LaneId, +) -> InboundLane> { + InboundLane::new(inbound_lane_storage::(lane_id)) +} + +/// Creates new runtime inbound lane storage. +fn inbound_lane_storage, I: 'static>( + lane_id: LaneId, +) -> RuntimeInboundLaneStorage { + RuntimeInboundLaneStorage { + lane_id, + cached_data: RefCell::new(None), + _phantom: Default::default(), + } +} + +/// Creates new outbound lane object, backed by runtime storage. +fn outbound_lane, I: 'static>( + lane_id: LaneId, +) -> OutboundLane> { + OutboundLane::new(RuntimeOutboundLaneStorage { lane_id, _phantom: Default::default() }) +} + +/// Runtime inbound lane storage. +struct RuntimeInboundLaneStorage, I: 'static = ()> { + lane_id: LaneId, + cached_data: RefCell>>, + _phantom: PhantomData, +} + +impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage { + type Relayer = T::InboundRelayer; + + fn id(&self) -> LaneId { + self.lane_id + } + + fn max_unrewarded_relayer_entries(&self) -> MessageNonce { + T::MaxUnrewardedRelayerEntriesAtInboundLane::get() + } + + fn max_unconfirmed_messages(&self) -> MessageNonce { + T::MaxUnconfirmedMessagesAtInboundLane::get() + } + + fn data(&self) -> InboundLaneData { + match self.cached_data.clone().into_inner() { + Some(data) => data, + None => { + let data: InboundLaneData = + InboundLanes::::get(self.lane_id).into(); + *self.cached_data.try_borrow_mut().expect( + "we're in the single-threaded environment;\ + we have no recursive borrows; qed", + ) = Some(data.clone()); + data + }, + } + } + + fn set_data(&mut self, data: InboundLaneData) { + *self.cached_data.try_borrow_mut().expect( + "we're in the single-threaded environment;\ + we have no recursive borrows; qed", + ) = Some(data.clone()); + InboundLanes::::insert(self.lane_id, StoredInboundLaneData::(data)) + } +} + +/// Runtime outbound lane storage. +struct RuntimeOutboundLaneStorage { + lane_id: LaneId, + _phantom: PhantomData<(T, I)>, +} + +impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { + fn id(&self) -> LaneId { + self.lane_id + } + + fn data(&self) -> OutboundLaneData { + OutboundLanes::::get(self.lane_id) + } + + fn set_data(&mut self, data: OutboundLaneData) { + OutboundLanes::::insert(self.lane_id, data) + } + + #[cfg(test)] + fn message(&self, nonce: &MessageNonce) -> Option { + OutboundMessages::::get(MessageKey { lane_id: self.lane_id, nonce: *nonce }) + .map(Into::into) + } + + fn save_message(&mut self, nonce: MessageNonce, message_payload: MessagePayload) { + OutboundMessages::::insert( + MessageKey { lane_id: self.lane_id, nonce }, + StoredMessagePayload::::try_from(message_payload).expect( + "save_message is called after all checks in send_message; \ + send_message checks message size; \ + qed", + ), + ); + } + + fn remove_message(&mut self, nonce: &MessageNonce) { + OutboundMessages::::remove(MessageKey { lane_id: self.lane_id, nonce: *nonce }); + } +} + +/// Verify messages proof and return proved messages with decoded payload. +fn verify_and_decode_messages_proof( + proof: Chain::MessagesProof, + messages_count: u32, +) -> Result>, Chain::Error> { + // `receive_messages_proof` weight formula and `MaxUnconfirmedMessagesAtInboundLane` check + // guarantees that the `message_count` is sane and Vec may be allocated. + // (tx with too many messages will either be rejected from the pool, or will fail earlier) + Chain::verify_messages_proof(proof, messages_count).map(|messages_by_lane| { + messages_by_lane + .into_iter() + .map(|(lane, lane_data)| { + ( + lane, + ProvedLaneMessages { + lane_state: lane_data.lane_state, + messages: lane_data.messages.into_iter().map(Into::into).collect(), + }, + ) + }) + .collect() + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{ + message, message_payload, run_test, unrewarded_relayer, DbWeight, + RuntimeEvent as TestEvent, RuntimeOrigin, TestMessageDeliveryAndDispatchPayment, + TestMessagesDeliveryProof, TestMessagesProof, TestRuntime, MAX_OUTBOUND_PAYLOAD_SIZE, + PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_LANE_ID_2, + TEST_LANE_ID_3, TEST_RELAYER_A, TEST_RELAYER_B, + }; + use bp_messages::{UnrewardedRelayer, UnrewardedRelayersState}; + use bp_test_utils::generate_owned_bridge_module_tests; + use frame_support::{ + assert_noop, assert_ok, + storage::generator::{StorageMap, StorageValue}, + traits::Hooks, + weights::Weight, + }; + use frame_system::{EventRecord, Pallet as System, Phase}; + use sp_runtime::DispatchError; + + fn get_ready_for_events() { + System::::set_block_number(1); + System::::reset_events(); + } + + fn inbound_unrewarded_relayers_state( + lane: bp_messages::LaneId, + ) -> bp_messages::UnrewardedRelayersState { + let inbound_lane_data = InboundLanes::::get(lane).0; + let last_delivered_nonce = inbound_lane_data.last_delivered_nonce(); + let relayers = inbound_lane_data.relayers; + bp_messages::UnrewardedRelayersState { + unrewarded_relayer_entries: relayers.len() as _, + messages_in_oldest_entry: relayers + .front() + .map(|entry| 1 + entry.messages.end - entry.messages.begin) + .unwrap_or(0), + total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX), + last_delivered_nonce, + } + } + + fn send_regular_message() -> Weight { + get_ready_for_events(); + + let message_nonce = + outbound_lane::(TEST_LANE_ID).data().latest_generated_nonce + 1; + let weight = send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + REGULAR_PAYLOAD, + ) + .expect("send_message has failed") + .weight; + + // check event with assigned nonce + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Messages(Event::MessageAccepted { + lane_id: TEST_LANE_ID, + nonce: message_nonce + }), + topics: vec![], + }], + ); + + weight + } + + fn receive_messages_delivery_proof() { + System::::set_block_number(1); + System::::reset_events(); + + assert_ok!(Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 1, + relayers: vec![UnrewardedRelayer { + relayer: 0, + messages: DeliveredMessages::new(1), + }] + .into_iter() + .collect(), + }, + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + last_delivered_nonce: 1, + ..Default::default() + }, + )); + + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Messages(Event::MessagesDelivered { + lane_id: TEST_LANE_ID, + messages: DeliveredMessages::new(1), + }), + topics: vec![], + }], + ); + } + + #[test] + fn pallet_rejects_transactions_if_halted() { + run_test(|| { + // send message first to be able to check that delivery_proof fails later + send_regular_message(); + + PalletOperatingMode::::put(MessagesOperatingMode::Basic( + BasicOperatingMode::Halted, + )); + + assert_noop!( + send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + REGULAR_PAYLOAD, + ), + Error::::NotOperatingNormally, + ); + + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok(vec![message(2, REGULAR_PAYLOAD)]).into(), + 1, + REGULAR_PAYLOAD.declared_weight, + ), + Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), + ); + + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 1, + relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] + .into_iter() + .collect(), + }, + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }, + ), + Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), + ); + }); + } + + #[test] + fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { + run_test(|| { + // send message first to be able to check that delivery_proof fails later + send_regular_message(); + + PalletOperatingMode::::put( + MessagesOperatingMode::RejectingOutboundMessages, + ); + + assert_noop!( + send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + REGULAR_PAYLOAD, + ), + Error::::NotOperatingNormally, + ); + + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), + 1, + REGULAR_PAYLOAD.declared_weight, + ),); + + assert_ok!(Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 1, + relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] + .into_iter() + .collect(), + }, + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }, + )); + }); + } + + #[test] + fn send_message_works() { + run_test(|| { + send_regular_message(); + }); + } + + #[test] + fn send_message_rejects_too_large_message() { + run_test(|| { + let mut message_payload = message_payload(1, 0); + // the payload isn't simply extra, so it'll definitely overflow + // `MAX_OUTBOUND_PAYLOAD_SIZE` if we add `MAX_OUTBOUND_PAYLOAD_SIZE` bytes to extra + message_payload + .extra + .extend_from_slice(&[0u8; MAX_OUTBOUND_PAYLOAD_SIZE as usize]); + assert_noop!( + send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + message_payload.clone(), + ), + Error::::MessageIsTooLarge, + ); + + // let's check that we're able to send `MAX_OUTBOUND_PAYLOAD_SIZE` messages + while message_payload.encoded_size() as u32 > MAX_OUTBOUND_PAYLOAD_SIZE { + message_payload.extra.pop(); + } + assert_eq!(message_payload.encoded_size() as u32, MAX_OUTBOUND_PAYLOAD_SIZE); + assert_ok!(send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + message_payload, + ),); + }) + } + + #[test] + fn chain_verifier_rejects_invalid_message_in_send_message() { + run_test(|| { + // messages with this payload are rejected by target chain verifier + assert_noop!( + send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + PAYLOAD_REJECTED_BY_TARGET_CHAIN, + ), + Error::::MessageRejectedByChainVerifier, + ); + }); + } + + #[test] + fn lane_verifier_rejects_invalid_message_in_send_message() { + run_test(|| { + // messages with zero fee are rejected by lane verifier + let mut message = REGULAR_PAYLOAD; + message.reject_by_lane_verifier = true; + assert_noop!( + send_message::(RuntimeOrigin::signed(1), TEST_LANE_ID, message,), + Error::::MessageRejectedByLaneVerifier, + ); + }); + } + + #[test] + fn receive_messages_proof_works() { + run_test(|| { + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), + 1, + REGULAR_PAYLOAD.declared_weight, + )); + + assert_eq!(InboundLanes::::get(TEST_LANE_ID).0.last_delivered_nonce(), 1); + }); + } + + #[test] + fn receive_messages_proof_updates_confirmed_message_nonce() { + run_test(|| { + // say we have received 10 messages && last confirmed message is 8 + InboundLanes::::insert( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 8, + relayers: vec![ + unrewarded_relayer(9, 9, TEST_RELAYER_A), + unrewarded_relayer(10, 10, TEST_RELAYER_B), + ] + .into_iter() + .collect(), + }, + ); + assert_eq!( + inbound_unrewarded_relayers_state(TEST_LANE_ID), + UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + messages_in_oldest_entry: 1, + total_messages: 2, + last_delivered_nonce: 10, + }, + ); + + // message proof includes outbound lane state with latest confirmed message updated to 9 + let mut message_proof: TestMessagesProof = + Ok(vec![message(11, REGULAR_PAYLOAD)]).into(); + message_proof.result.as_mut().unwrap()[0].1.lane_state = + Some(OutboundLaneData { latest_received_nonce: 9, ..Default::default() }); + + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + message_proof, + 1, + REGULAR_PAYLOAD.declared_weight, + )); + + assert_eq!( + InboundLanes::::get(TEST_LANE_ID).0, + InboundLaneData { + last_confirmed_nonce: 9, + relayers: vec![ + unrewarded_relayer(10, 10, TEST_RELAYER_B), + unrewarded_relayer(11, 11, TEST_RELAYER_A) + ] + .into_iter() + .collect(), + }, + ); + assert_eq!( + inbound_unrewarded_relayers_state(TEST_LANE_ID), + UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + messages_in_oldest_entry: 1, + total_messages: 2, + last_delivered_nonce: 11, + }, + ); + }); + } + + #[test] + fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enough() { + run_test(|| { + let mut declared_weight = REGULAR_PAYLOAD.declared_weight; + *declared_weight.ref_time_mut() -= 1; + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), + 1, + declared_weight, + )); + assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); + }); + } + + #[test] + fn receive_messages_proof_rejects_invalid_proof() { + run_test(|| { + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Err(()).into(), + 1, + Weight::from_ref_time(0), + ), + Error::::InvalidMessagesProof, + ); + }); + } + + #[test] + fn receive_messages_proof_rejects_proof_with_too_many_messages() { + run_test(|| { + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), + u32::MAX, + Weight::from_ref_time(0), + ), + Error::::TooManyMessagesInTheProof, + ); + }); + } + + #[test] + fn receive_messages_delivery_proof_works() { + run_test(|| { + send_regular_message(); + receive_messages_delivery_proof(); + + assert_eq!( + OutboundLanes::::get(TEST_LANE_ID).latest_received_nonce, + 1, + ); + }); + } + + #[test] + fn receive_messages_delivery_proof_rewards_relayers() { + run_test(|| { + assert_ok!(send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + REGULAR_PAYLOAD, + )); + assert_ok!(send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + REGULAR_PAYLOAD, + )); + + // this reports delivery of message 1 => reward is paid to TEST_RELAYER_A + assert_ok!(Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] + .into_iter() + .collect(), + ..Default::default() + } + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + last_delivered_nonce: 1, + ..Default::default() + }, + )); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 1)); + assert!(!TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_B, 1)); + + // this reports delivery of both message 1 and message 2 => reward is paid only to + // TEST_RELAYER_B + assert_ok!(Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![ + unrewarded_relayer(1, 1, TEST_RELAYER_A), + unrewarded_relayer(2, 2, TEST_RELAYER_B) + ] + .into_iter() + .collect(), + ..Default::default() + } + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + total_messages: 2, + last_delivered_nonce: 2, + ..Default::default() + }, + )); + assert!(!TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 1)); + assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_B, 1)); + }); + } + + #[test] + fn receive_messages_delivery_proof_rejects_invalid_proof() { + run_test(|| { + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Err(())), + Default::default(), + ), + Error::::InvalidMessagesDeliveryProof, + ); + }); + } + + #[test] + fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_invalid() { + run_test(|| { + // when number of relayers entries is invalid + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![ + unrewarded_relayer(1, 1, TEST_RELAYER_A), + unrewarded_relayer(2, 2, TEST_RELAYER_B) + ] + .into_iter() + .collect(), + ..Default::default() + } + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 2, + last_delivered_nonce: 2, + ..Default::default() + }, + ), + Error::::InvalidUnrewardedRelayersState, + ); + + // when number of messages is invalid + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![ + unrewarded_relayer(1, 1, TEST_RELAYER_A), + unrewarded_relayer(2, 2, TEST_RELAYER_B) + ] + .into_iter() + .collect(), + ..Default::default() + } + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + total_messages: 1, + last_delivered_nonce: 2, + ..Default::default() + }, + ), + Error::::InvalidUnrewardedRelayersState, + ); + + // when last delivered nonce is invalid + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![ + unrewarded_relayer(1, 1, TEST_RELAYER_A), + unrewarded_relayer(2, 2, TEST_RELAYER_B) + ] + .into_iter() + .collect(), + ..Default::default() + } + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + total_messages: 2, + last_delivered_nonce: 8, + ..Default::default() + }, + ), + Error::::InvalidUnrewardedRelayersState, + ); + }); + } + + #[test] + fn receive_messages_accepts_single_message_with_invalid_payload() { + run_test(|| { + let mut invalid_message = message(1, REGULAR_PAYLOAD); + invalid_message.payload = Vec::new(); + + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok(vec![invalid_message]).into(), + 1, + Weight::from_ref_time(0), /* weight may be zero in this case (all messages are + * improperly encoded) */ + ),); + + assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 1,); + }); + } + + #[test] + fn receive_messages_accepts_batch_with_message_with_invalid_payload() { + run_test(|| { + let mut invalid_message = message(2, REGULAR_PAYLOAD); + invalid_message.payload = Vec::new(); + + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok( + vec![message(1, REGULAR_PAYLOAD), invalid_message, message(3, REGULAR_PAYLOAD),] + ) + .into(), + 3, + REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight, + ),); + + assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 3,); + }); + } + + #[test] + fn actual_dispatch_weight_does_not_overlow() { + run_test(|| { + let message1 = message(1, message_payload(0, u64::MAX / 2)); + let message2 = message(2, message_payload(0, u64::MAX / 2)); + let message3 = message(3, message_payload(0, u64::MAX / 2)); + + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + // this may cause overflow if source chain storage is invalid + Ok(vec![message1, message2, message3]).into(), + 3, + Weight::MAX, + )); + assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 2); + }); + } + + #[test] + fn weight_refund_from_receive_messages_proof_works() { + run_test(|| { + fn submit_with_unspent_weight( + nonce: MessageNonce, + unspent_weight: u64, + is_prepaid: bool, + ) -> (Weight, Weight) { + let mut payload = REGULAR_PAYLOAD; + *payload.dispatch_result.unspent_weight.ref_time_mut() = unspent_weight; + payload.dispatch_result.dispatch_fee_paid_during_dispatch = !is_prepaid; + let proof = Ok(vec![message(nonce, payload)]).into(); + let messages_count = 1; + let pre_dispatch_weight = + ::WeightInfo::receive_messages_proof_weight( + &proof, + messages_count, + REGULAR_PAYLOAD.declared_weight, + ); + let post_dispatch_weight = Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + proof, + messages_count, + REGULAR_PAYLOAD.declared_weight, + ) + .expect("delivery has failed") + .actual_weight + .expect("receive_messages_proof always returns Some"); + + (pre_dispatch_weight, post_dispatch_weight) + } + + // when dispatch is returning `unspent_weight < declared_weight` + let (pre, post) = submit_with_unspent_weight(1, 1, false); + assert_eq!(post.ref_time(), pre.ref_time() - 1); + + // when dispatch is returning `unspent_weight = declared_weight` + let (pre, post) = + submit_with_unspent_weight(2, REGULAR_PAYLOAD.declared_weight.ref_time(), false); + assert_eq!( + post.ref_time(), + pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time() + ); + + // when dispatch is returning `unspent_weight > declared_weight` + let (pre, post) = submit_with_unspent_weight( + 3, + REGULAR_PAYLOAD.declared_weight.ref_time() + 1, + false, + ); + assert_eq!( + post.ref_time(), + pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time() + ); + + // when there's no unspent weight + let (pre, post) = submit_with_unspent_weight(4, 0, false); + assert_eq!(post, pre); + + // when dispatch is returning `unspent_weight < declared_weight` AND message is prepaid + let (pre, post) = submit_with_unspent_weight(5, 1, true); + assert_eq!( + post.ref_time(), + pre.ref_time() - + 1 - ::WeightInfo::pay_inbound_dispatch_fee_overhead() + .ref_time() + ); + }); + } + + #[test] + fn messages_delivered_callbacks_are_called() { + run_test(|| { + send_regular_message(); + send_regular_message(); + send_regular_message(); + + // messages 1+2 are confirmed in 1 tx, message 3 in a separate tx + // dispatch of message 2 has failed + let mut delivered_messages_1_and_2 = DeliveredMessages::new(1); + delivered_messages_1_and_2.note_dispatched_message(); + let messages_1_and_2_proof = Ok(( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 0, + relayers: vec![UnrewardedRelayer { + relayer: 0, + messages: delivered_messages_1_and_2.clone(), + }] + .into_iter() + .collect(), + }, + )); + let delivered_message_3 = DeliveredMessages::new(3); + let messages_3_proof = Ok(( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 0, + relayers: vec![UnrewardedRelayer { relayer: 0, messages: delivered_message_3 }] + .into_iter() + .collect(), + }, + )); + + // first tx with messages 1+2 + assert_ok!(Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(messages_1_and_2_proof), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 2, + last_delivered_nonce: 2, + ..Default::default() + }, + )); + // second tx with message 3 + assert_ok!(Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(messages_3_proof), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + last_delivered_nonce: 3, + ..Default::default() + }, + )); + }); + } + + #[test] + fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messages_than_expected( + ) { + run_test(|| { + // send message first to be able to check that delivery_proof fails later + send_regular_message(); + + // 1) InboundLaneData declares that the `last_confirmed_nonce` is 1; + // 2) InboundLaneData has no entries => `InboundLaneData::last_delivered_nonce()` + // returns `last_confirmed_nonce`; + // 3) it means that we're going to confirm delivery of messages 1..=1; + // 4) so the number of declared messages (see `UnrewardedRelayersState`) is `0` and + // numer of actually confirmed messages is `1`. + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { last_confirmed_nonce: 1, relayers: Default::default() }, + ))), + UnrewardedRelayersState { last_delivered_nonce: 1, ..Default::default() }, + ), + Error::::TryingToConfirmMoreMessagesThanExpected, + ); + }); + } + + #[test] + fn storage_keys_computed_properly() { + assert_eq!( + PalletOperatingMode::::storage_value_final_key().to_vec(), + bp_messages::storage_keys::operating_mode_key("Messages").0, + ); + + assert_eq!( + OutboundMessages::::storage_map_final_key(MessageKey { + lane_id: TEST_LANE_ID, + nonce: 42 + }), + bp_messages::storage_keys::message_key("Messages", &TEST_LANE_ID, 42).0, + ); + + assert_eq!( + OutboundLanes::::storage_map_final_key(TEST_LANE_ID), + bp_messages::storage_keys::outbound_lane_data_key("Messages", &TEST_LANE_ID).0, + ); + + assert_eq!( + InboundLanes::::storage_map_final_key(TEST_LANE_ID), + bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0, + ); + } + + #[test] + fn inbound_message_details_works() { + run_test(|| { + assert_eq!( + Pallet::::inbound_message_data( + TEST_LANE_ID, + REGULAR_PAYLOAD.encode(), + OutboundMessageDetails { + nonce: 0, + dispatch_weight: Weight::from_ref_time(0), + size: 0, + }, + ), + InboundMessageDetails { dispatch_weight: REGULAR_PAYLOAD.declared_weight }, + ); + }); + } + + #[test] + fn on_idle_callback_respects_remaining_weight() { + run_test(|| { + send_regular_message(); + send_regular_message(); + send_regular_message(); + send_regular_message(); + + assert_ok!(Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 4, + relayers: vec![unrewarded_relayer(1, 4, TEST_RELAYER_A)] + .into_iter() + .collect(), + }, + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 4, + total_messages: 4, + last_delivered_nonce: 4, + }, + )); + + // all 4 messages may be pruned now + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().latest_received_nonce, + 4 + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 1 + ); + System::::set_block_number(2); + + // if passed wight is too low to do anything + let dbw = DbWeight::get(); + assert_eq!( + Pallet::::on_idle(0, dbw.reads_writes(1, 1)), + Weight::zero(), + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 1 + ); + + // if passed wight is enough to prune single message + assert_eq!( + Pallet::::on_idle(0, dbw.reads_writes(1, 2)), + dbw.reads_writes(1, 2), + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 2 + ); + + // if passed wight is enough to prune two more messages + assert_eq!( + Pallet::::on_idle(0, dbw.reads_writes(1, 3)), + dbw.reads_writes(1, 3), + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 4 + ); + + // if passed wight is enough to prune many messages + assert_eq!( + Pallet::::on_idle(0, dbw.reads_writes(100, 100)), + dbw.reads_writes(1, 2), + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 5 + ); + }); + } + + #[test] + fn on_idle_callback_is_rotating_lanes_to_prune() { + run_test(|| { + // send + receive confirmation for lane 1 + send_regular_message(); + receive_messages_delivery_proof(); + // send + receive confirmation for lane 2 + assert_ok!(send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID_2, + REGULAR_PAYLOAD, + )); + assert_ok!(Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID_2, + InboundLaneData { + last_confirmed_nonce: 1, + relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] + .into_iter() + .collect(), + }, + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }, + )); + + // nothing is pruned yet + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().latest_received_nonce, + 1 + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 1 + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID_2).data().latest_received_nonce, + 1 + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, + 1 + ); + + // in block#2.on_idle lane messages of lane 1 are pruned + let dbw = DbWeight::get(); + System::::set_block_number(2); + assert_eq!( + Pallet::::on_idle(0, dbw.reads_writes(100, 100)), + dbw.reads_writes(1, 2), + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 2 + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, + 1 + ); + + // in block#3.on_idle lane messages of lane 2 are pruned + System::::set_block_number(3); + + assert_eq!( + Pallet::::on_idle(0, dbw.reads_writes(100, 100)), + dbw.reads_writes(1, 2), + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 2 + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, + 2 + ); + }); + } + + #[test] + fn outbound_message_from_unconfigured_lane_is_rejected() { + run_test(|| { + assert_noop!( + send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID_3, + REGULAR_PAYLOAD, + ), + Error::::InactiveOutboundLane, + ); + }); + } + + generate_owned_bridge_module_tests!( + MessagesOperatingMode::Basic(BasicOperatingMode::Normal), + MessagesOperatingMode::Basic(BasicOperatingMode::Halted) + ); +} diff --git a/modules/messages/src/mock.rs b/modules/messages/src/mock.rs new file mode 100644 index 00000000000..36c38677e53 --- /dev/null +++ b/modules/messages/src/mock.rs @@ -0,0 +1,423 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +// From construct_runtime macro +#![allow(clippy::from_over_into)] + +use crate::{calc_relayers_rewards, Config}; + +use bp_messages::{ + source_chain::{LaneMessageVerifier, MessageDeliveryAndDispatchPayment, TargetHeaderChain}, + target_chain::{ + DispatchMessage, DispatchMessageData, MessageDispatch, ProvedLaneMessages, ProvedMessages, + SourceHeaderChain, + }, + DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, + OutboundLaneData, UnrewardedRelayer, +}; +use bp_runtime::{messages::MessageDispatchResult, Size}; +use codec::{Decode, Encode}; +use frame_support::{ + parameter_types, + weights::{RuntimeDbWeight, Weight}, +}; +use scale_info::TypeInfo; +use sp_core::H256; +use sp_runtime::{ + testing::Header as SubstrateHeader, + traits::{BlakeTwo256, IdentityLookup}, + Perbill, +}; +use std::{ + collections::{BTreeMap, VecDeque}, + ops::RangeInclusive, +}; + +pub type AccountId = u64; +pub type Balance = u64; +#[derive(Decode, Encode, Clone, Debug, PartialEq, Eq, TypeInfo)] +pub struct TestPayload { + /// Field that may be used to identify messages. + pub id: u64, + /// Reject this message by lane verifier? + pub reject_by_lane_verifier: bool, + /// Dispatch weight that is declared by the message sender. + pub declared_weight: Weight, + /// Message dispatch result. + /// + /// Note: in correct code `dispatch_result.unspent_weight` will always be <= `declared_weight`, + /// but for test purposes we'll be making it larger than `declared_weight` sometimes. + pub dispatch_result: MessageDispatchResult, + /// Extra bytes that affect payload size. + pub extra: Vec, +} +pub type TestMessageFee = u64; +pub type TestRelayer = u64; +pub type TestDispatchLevelResult = (); + +type Block = frame_system::mocking::MockBlock; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + +use crate as pallet_bridge_messages; + +frame_support::construct_runtime! { + pub enum TestRuntime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Event}, + Messages: pallet_bridge_messages::{Pallet, Call, Event}, + } +} + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; +} + +impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = SubstrateHeader; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type DbWeight = DbWeight; + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 1; +} + +impl pallet_balances::Config for TestRuntime { + type MaxLocks = (); + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = frame_system::Pallet; + type WeightInfo = (); + type MaxReserves = (); + type ReserveIdentifier = (); +} + +parameter_types! { + pub const MaxMessagesToPruneAtOnce: u64 = 10; + pub const MaxUnrewardedRelayerEntriesAtInboundLane: u64 = 16; + pub const MaxUnconfirmedMessagesAtInboundLane: u64 = 32; + pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; + pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID, TEST_LANE_ID_2]; +} + +impl Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type ActiveOutboundLanes = ActiveOutboundLanes; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type MaximalOutboundPayloadSize = frame_support::traits::ConstU32; + type OutboundPayload = TestPayload; + + type InboundPayload = TestPayload; + type InboundRelayer = TestRelayer; + + type TargetHeaderChain = TestTargetHeaderChain; + type LaneMessageVerifier = TestLaneMessageVerifier; + type MessageDeliveryAndDispatchPayment = TestMessageDeliveryAndDispatchPayment; + + type SourceHeaderChain = TestSourceHeaderChain; + type MessageDispatch = TestMessageDispatch; + type BridgedChainId = TestBridgedChainId; +} + +impl Size for TestPayload { + fn size(&self) -> u32 { + 16 + self.extra.len() as u32 + } +} + +/// Maximal outbound payload size. +pub const MAX_OUTBOUND_PAYLOAD_SIZE: u32 = 4096; + +/// Account that has balance to use in tests. +pub const ENDOWED_ACCOUNT: AccountId = 0xDEAD; + +/// Account id of test relayer. +pub const TEST_RELAYER_A: AccountId = 100; + +/// Account id of additional test relayer - B. +pub const TEST_RELAYER_B: AccountId = 101; + +/// Account id of additional test relayer - C. +pub const TEST_RELAYER_C: AccountId = 102; + +/// Error that is returned by all test implementations. +pub const TEST_ERROR: &str = "Test error"; + +/// Lane that we're using in tests. +pub const TEST_LANE_ID: LaneId = [0, 0, 0, 1]; + +/// Secondary lane that we're using in tests. +pub const TEST_LANE_ID_2: LaneId = [0, 0, 0, 2]; + +/// Inactive outbound lane. +pub const TEST_LANE_ID_3: LaneId = [0, 0, 0, 3]; + +/// Regular message payload. +pub const REGULAR_PAYLOAD: TestPayload = message_payload(0, 50); + +/// Payload that is rejected by `TestTargetHeaderChain`. +pub const PAYLOAD_REJECTED_BY_TARGET_CHAIN: TestPayload = message_payload(1, 50); + +/// Vec of proved messages, grouped by lane. +pub type MessagesByLaneVec = Vec<(LaneId, ProvedLaneMessages)>; + +/// Test messages proof. +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] +pub struct TestMessagesProof { + pub result: Result, +} + +impl Size for TestMessagesProof { + fn size(&self) -> u32 { + 0 + } +} + +impl From, ()>> for TestMessagesProof { + fn from(result: Result, ()>) -> Self { + Self { + result: result.map(|messages| { + let mut messages_by_lane: BTreeMap> = + BTreeMap::new(); + for message in messages { + messages_by_lane.entry(message.key.lane_id).or_default().messages.push(message); + } + messages_by_lane.into_iter().collect() + }), + } + } +} + +/// Messages delivery proof used in tests. +#[derive(Debug, Encode, Decode, Eq, Clone, PartialEq, TypeInfo)] +pub struct TestMessagesDeliveryProof(pub Result<(LaneId, InboundLaneData), ()>); + +impl Size for TestMessagesDeliveryProof { + fn size(&self) -> u32 { + 0 + } +} + +/// Target header chain that is used in tests. +#[derive(Debug, Default)] +pub struct TestTargetHeaderChain; + +impl TargetHeaderChain for TestTargetHeaderChain { + type Error = &'static str; + + type MessagesDeliveryProof = TestMessagesDeliveryProof; + + fn verify_message(payload: &TestPayload) -> Result<(), Self::Error> { + if *payload == PAYLOAD_REJECTED_BY_TARGET_CHAIN { + Err(TEST_ERROR) + } else { + Ok(()) + } + } + + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData), Self::Error> { + proof.0.map_err(|_| TEST_ERROR) + } +} + +/// Lane message verifier that is used in tests. +#[derive(Debug, Default)] +pub struct TestLaneMessageVerifier; + +impl LaneMessageVerifier for TestLaneMessageVerifier { + type Error = &'static str; + + fn verify_message( + _submitter: &RuntimeOrigin, + _lane: &LaneId, + _lane_outbound_data: &OutboundLaneData, + payload: &TestPayload, + ) -> Result<(), Self::Error> { + if !payload.reject_by_lane_verifier { + Ok(()) + } else { + Err(TEST_ERROR) + } + } +} + +/// Message fee payment system that is used in tests. +#[derive(Debug, Default)] +pub struct TestMessageDeliveryAndDispatchPayment; + +impl TestMessageDeliveryAndDispatchPayment { + /// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is + /// cleared after the call. + pub fn is_reward_paid(relayer: AccountId, fee: TestMessageFee) -> bool { + let key = (b":relayer-reward:", relayer, fee).encode(); + frame_support::storage::unhashed::take::(&key).is_some() + } +} + +impl MessageDeliveryAndDispatchPayment + for TestMessageDeliveryAndDispatchPayment +{ + type Error = &'static str; + + fn pay_relayers_rewards( + _lane_id: LaneId, + message_relayers: VecDeque>, + _confirmation_relayer: &AccountId, + received_range: &RangeInclusive, + ) { + let relayers_rewards = + calc_relayers_rewards::(message_relayers, received_range); + for (relayer, reward) in &relayers_rewards { + let key = (b":relayer-reward:", relayer, reward).encode(); + frame_support::storage::unhashed::put(&key, &true); + } + } +} + +/// Source header chain that is used in tests. +#[derive(Debug)] +pub struct TestSourceHeaderChain; + +impl SourceHeaderChain for TestSourceHeaderChain { + type Error = &'static str; + + type MessagesProof = TestMessagesProof; + + fn verify_messages_proof( + proof: Self::MessagesProof, + _messages_count: u32, + ) -> Result, Self::Error> { + proof.result.map(|proof| proof.into_iter().collect()).map_err(|_| TEST_ERROR) + } +} + +/// Source header chain that is used in tests. +#[derive(Debug)] +pub struct TestMessageDispatch; + +impl MessageDispatch for TestMessageDispatch { + type DispatchPayload = TestPayload; + type DispatchLevelResult = TestDispatchLevelResult; + + fn dispatch_weight(message: &mut DispatchMessage) -> Weight { + match message.data.payload.as_ref() { + Ok(payload) => payload.declared_weight, + Err(_) => Weight::from_ref_time(0), + } + } + + fn dispatch( + _relayer_account: &AccountId, + message: DispatchMessage, + ) -> MessageDispatchResult { + match message.data.payload.as_ref() { + Ok(payload) => payload.dispatch_result.clone(), + Err(_) => dispatch_result(0), + } + } +} + +/// Return test lane message with given nonce and payload. +pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message { + Message { key: MessageKey { lane_id: TEST_LANE_ID, nonce }, payload: payload.encode() } +} + +/// Return valid outbound message data, constructed from given payload. +pub fn outbound_message_data(payload: TestPayload) -> MessagePayload { + payload.encode() +} + +/// Return valid inbound (dispatch) message data, constructed from given payload. +pub fn inbound_message_data(payload: TestPayload) -> DispatchMessageData { + DispatchMessageData { payload: Ok(payload) } +} + +/// Constructs message payload using given arguments and zero unspent weight. +pub const fn message_payload(id: u64, declared_weight: u64) -> TestPayload { + TestPayload { + id, + reject_by_lane_verifier: false, + declared_weight: Weight::from_ref_time(declared_weight), + dispatch_result: dispatch_result(0), + extra: Vec::new(), + } +} + +/// Returns message dispatch result with given unspent weight. +pub const fn dispatch_result( + unspent_weight: u64, +) -> MessageDispatchResult { + MessageDispatchResult { + unspent_weight: Weight::from_ref_time(unspent_weight), + dispatch_fee_paid_during_dispatch: true, + dispatch_level_result: (), + } +} + +/// Constructs unrewarded relayer entry from nonces range and relayer id. +pub fn unrewarded_relayer( + begin: MessageNonce, + end: MessageNonce, + relayer: TestRelayer, +) -> UnrewardedRelayer { + UnrewardedRelayer { relayer, messages: DeliveredMessages { begin, end } } +} + +/// Run pallet test. +pub fn run_test(test: impl FnOnce() -> T) -> T { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig:: { balances: vec![(ENDOWED_ACCOUNT, 1_000_000)] } + .assimilate_storage(&mut t) + .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(test) +} diff --git a/modules/messages/src/outbound_lane.rs b/modules/messages/src/outbound_lane.rs new file mode 100644 index 00000000000..33a58a40400 --- /dev/null +++ b/modules/messages/src/outbound_lane.rs @@ -0,0 +1,436 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything about outgoing messages sending. + +use crate::Config; + +use bp_messages::{ + DeliveredMessages, LaneId, MessageNonce, MessagePayload, OutboundLaneData, UnrewardedRelayer, +}; +use frame_support::{ + weights::{RuntimeDbWeight, Weight}, + BoundedVec, RuntimeDebug, +}; +use num_traits::Zero; +use sp_std::collections::vec_deque::VecDeque; + +/// Outbound lane storage. +pub trait OutboundLaneStorage { + /// Lane id. + fn id(&self) -> LaneId; + /// Get lane data from the storage. + fn data(&self) -> OutboundLaneData; + /// Update lane data in the storage. + fn set_data(&mut self, data: OutboundLaneData); + /// Returns saved outbound message payload. + #[cfg(test)] + fn message(&self, nonce: &MessageNonce) -> Option; + /// Save outbound message in the storage. + fn save_message(&mut self, nonce: MessageNonce, message_payload: MessagePayload); + /// Remove outbound message from the storage. + fn remove_message(&mut self, nonce: &MessageNonce); +} + +/// Outbound message data wrapper that implements `MaxEncodedLen`. +pub type StoredMessagePayload = BoundedVec>::MaximalOutboundPayloadSize>; + +/// Result of messages receival confirmation. +#[derive(RuntimeDebug, PartialEq, Eq)] +pub enum ReceivalConfirmationResult { + /// New messages have been confirmed by the confirmation transaction. + ConfirmedMessages(DeliveredMessages), + /// Confirmation transaction brings no new confirmation. This may be a result of relayer + /// error or several relayers running. + NoNewConfirmations, + /// Bridged chain is trying to confirm more messages than we have generated. May be a result + /// of invalid bridged chain storage. + FailedToConfirmFutureMessages, + /// The unrewarded relayers vec contains an empty entry. May be a result of invalid bridged + /// chain storage. + EmptyUnrewardedRelayerEntry, + /// The unrewarded relayers vec contains non-consecutive entries. May be a result of invalid + /// bridged chain storage. + NonConsecutiveUnrewardedRelayerEntries, + /// The chain has more messages that need to be confirmed than there is in the proof. + TryingToConfirmMoreMessagesThanExpected(MessageNonce), +} + +/// Outbound messages lane. +pub struct OutboundLane { + storage: S, +} + +impl OutboundLane { + /// Create new outbound lane backed by given storage. + pub fn new(storage: S) -> Self { + OutboundLane { storage } + } + + /// Get this lane data. + pub fn data(&self) -> OutboundLaneData { + self.storage.data() + } + + /// Send message over lane. + /// + /// Returns new message nonce. + pub fn send_message(&mut self, message_payload: MessagePayload) -> MessageNonce { + let mut data = self.storage.data(); + let nonce = data.latest_generated_nonce + 1; + data.latest_generated_nonce = nonce; + + self.storage.save_message(nonce, message_payload); + self.storage.set_data(data); + + nonce + } + + /// Confirm messages delivery. + pub fn confirm_delivery( + &mut self, + max_allowed_messages: MessageNonce, + latest_delivered_nonce: MessageNonce, + relayers: &VecDeque>, + ) -> ReceivalConfirmationResult { + let mut data = self.storage.data(); + if latest_delivered_nonce <= data.latest_received_nonce { + return ReceivalConfirmationResult::NoNewConfirmations + } + if latest_delivered_nonce > data.latest_generated_nonce { + return ReceivalConfirmationResult::FailedToConfirmFutureMessages + } + if latest_delivered_nonce - data.latest_received_nonce > max_allowed_messages { + // that the relayer has declared correct number of messages that the proof contains (it + // is checked outside of the function). But it may happen (but only if this/bridged + // chain storage is corrupted, though) that the actual number of confirmed messages if + // larger than declared. This would mean that 'reward loop' will take more time than the + // weight formula accounts, so we can't allow that. + return ReceivalConfirmationResult::TryingToConfirmMoreMessagesThanExpected( + latest_delivered_nonce - data.latest_received_nonce, + ) + } + + if let Err(e) = ensure_unrewarded_relayers_are_correct(latest_delivered_nonce, relayers) { + return e + } + + let prev_latest_received_nonce = data.latest_received_nonce; + data.latest_received_nonce = latest_delivered_nonce; + self.storage.set_data(data); + + ReceivalConfirmationResult::ConfirmedMessages(DeliveredMessages { + begin: prev_latest_received_nonce + 1, + end: latest_delivered_nonce, + }) + } + + /// Prune at most `max_messages_to_prune` already received messages. + /// + /// Returns weight, consumed by messages pruning and lane state update. + pub fn prune_messages( + &mut self, + db_weight: RuntimeDbWeight, + mut remaining_weight: Weight, + ) -> Weight { + let write_weight = db_weight.writes(1); + let two_writes_weight = write_weight + write_weight; + let mut spent_weight = Weight::zero(); + let mut data = self.storage.data(); + while remaining_weight.all_gte(two_writes_weight) && + data.oldest_unpruned_nonce <= data.latest_received_nonce + { + self.storage.remove_message(&data.oldest_unpruned_nonce); + + spent_weight += write_weight; + remaining_weight -= write_weight; + data.oldest_unpruned_nonce += 1; + } + + if !spent_weight.is_zero() { + spent_weight += write_weight; + self.storage.set_data(data); + } + + spent_weight + } +} + +/// Verifies unrewarded relayers vec. +/// +/// Returns `Err(_)` if unrewarded relayers vec contains invalid data, meaning that the bridged +/// chain has invalid runtime storage. +fn ensure_unrewarded_relayers_are_correct( + latest_received_nonce: MessageNonce, + relayers: &VecDeque>, +) -> Result<(), ReceivalConfirmationResult> { + let mut last_entry_end: Option = None; + for entry in relayers { + // unrewarded relayer entry must have at least 1 unconfirmed message + // (guaranteed by the `InboundLane::receive_message()`) + if entry.messages.end < entry.messages.begin { + return Err(ReceivalConfirmationResult::EmptyUnrewardedRelayerEntry) + } + // every entry must confirm range of messages that follows previous entry range + // (guaranteed by the `InboundLane::receive_message()`) + if let Some(last_entry_end) = last_entry_end { + let expected_entry_begin = last_entry_end.checked_add(1); + if expected_entry_begin != Some(entry.messages.begin) { + return Err(ReceivalConfirmationResult::NonConsecutiveUnrewardedRelayerEntries) + } + } + last_entry_end = Some(entry.messages.end); + // entry can't confirm messages larger than `inbound_lane_data.latest_received_nonce()` + // (guaranteed by the `InboundLane::receive_message()`) + if entry.messages.end > latest_received_nonce { + // technically this will be detected in the next loop iteration as + // `InvalidNumberOfDispatchResults` but to guarantee safety of loop operations below + // this is detected now + return Err(ReceivalConfirmationResult::FailedToConfirmFutureMessages) + } + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + mock::{ + outbound_message_data, run_test, unrewarded_relayer, TestRelayer, TestRuntime, + REGULAR_PAYLOAD, TEST_LANE_ID, + }, + outbound_lane, + }; + use frame_support::weights::constants::RocksDbWeight; + use sp_std::ops::RangeInclusive; + + fn unrewarded_relayers( + nonces: RangeInclusive, + ) -> VecDeque> { + vec![unrewarded_relayer(*nonces.start(), *nonces.end(), 0)] + .into_iter() + .collect() + } + + fn delivered_messages(nonces: RangeInclusive) -> DeliveredMessages { + DeliveredMessages { begin: *nonces.start(), end: *nonces.end() } + } + + fn assert_3_messages_confirmation_fails( + latest_received_nonce: MessageNonce, + relayers: &VecDeque>, + ) -> ReceivalConfirmationResult { + run_test(|| { + let mut lane = outbound_lane::(TEST_LANE_ID); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + assert_eq!(lane.storage.data().latest_generated_nonce, 3); + assert_eq!(lane.storage.data().latest_received_nonce, 0); + let result = lane.confirm_delivery(3, latest_received_nonce, relayers); + assert_eq!(lane.storage.data().latest_generated_nonce, 3); + assert_eq!(lane.storage.data().latest_received_nonce, 0); + result + }) + } + + #[test] + fn send_message_works() { + run_test(|| { + let mut lane = outbound_lane::(TEST_LANE_ID); + assert_eq!(lane.storage.data().latest_generated_nonce, 0); + assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); + assert!(lane.storage.message(&1).is_some()); + assert_eq!(lane.storage.data().latest_generated_nonce, 1); + }); + } + + #[test] + fn confirm_delivery_works() { + run_test(|| { + let mut lane = outbound_lane::(TEST_LANE_ID); + assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); + assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 2); + assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3); + assert_eq!(lane.storage.data().latest_generated_nonce, 3); + assert_eq!(lane.storage.data().latest_received_nonce, 0); + assert_eq!( + lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), + ReceivalConfirmationResult::ConfirmedMessages(delivered_messages(1..=3)), + ); + assert_eq!(lane.storage.data().latest_generated_nonce, 3); + assert_eq!(lane.storage.data().latest_received_nonce, 3); + }); + } + + #[test] + fn confirm_delivery_rejects_nonce_lesser_than_latest_received() { + run_test(|| { + let mut lane = outbound_lane::(TEST_LANE_ID); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + assert_eq!(lane.storage.data().latest_generated_nonce, 3); + assert_eq!(lane.storage.data().latest_received_nonce, 0); + assert_eq!( + lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), + ReceivalConfirmationResult::ConfirmedMessages(delivered_messages(1..=3)), + ); + assert_eq!( + lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), + ReceivalConfirmationResult::NoNewConfirmations, + ); + assert_eq!(lane.storage.data().latest_generated_nonce, 3); + assert_eq!(lane.storage.data().latest_received_nonce, 3); + + assert_eq!( + lane.confirm_delivery(1, 2, &unrewarded_relayers(1..=1)), + ReceivalConfirmationResult::NoNewConfirmations, + ); + assert_eq!(lane.storage.data().latest_generated_nonce, 3); + assert_eq!(lane.storage.data().latest_received_nonce, 3); + }); + } + + #[test] + fn confirm_delivery_rejects_nonce_larger_than_last_generated() { + assert_eq!( + assert_3_messages_confirmation_fails(10, &unrewarded_relayers(1..=10),), + ReceivalConfirmationResult::FailedToConfirmFutureMessages, + ); + } + + #[test] + fn confirm_delivery_fails_if_entry_confirms_future_messages() { + assert_eq!( + assert_3_messages_confirmation_fails( + 3, + &unrewarded_relayers(1..=1) + .into_iter() + .chain(unrewarded_relayers(2..=30).into_iter()) + .chain(unrewarded_relayers(3..=3).into_iter()) + .collect(), + ), + ReceivalConfirmationResult::FailedToConfirmFutureMessages, + ); + } + + #[test] + #[allow(clippy::reversed_empty_ranges)] + fn confirm_delivery_fails_if_entry_is_empty() { + assert_eq!( + assert_3_messages_confirmation_fails( + 3, + &unrewarded_relayers(1..=1) + .into_iter() + .chain(unrewarded_relayers(2..=1).into_iter()) + .chain(unrewarded_relayers(2..=3).into_iter()) + .collect(), + ), + ReceivalConfirmationResult::EmptyUnrewardedRelayerEntry, + ); + } + + #[test] + fn confirm_delivery_fails_if_entries_are_non_consecutive() { + assert_eq!( + assert_3_messages_confirmation_fails( + 3, + &unrewarded_relayers(1..=1) + .into_iter() + .chain(unrewarded_relayers(3..=3).into_iter()) + .chain(unrewarded_relayers(2..=2).into_iter()) + .collect(), + ), + ReceivalConfirmationResult::NonConsecutiveUnrewardedRelayerEntries, + ); + } + + #[test] + fn prune_messages_works() { + run_test(|| { + let mut lane = outbound_lane::(TEST_LANE_ID); + // when lane is empty, nothing is pruned + assert_eq!( + lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), + Weight::zero() + ); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); + // when nothing is confirmed, nothing is pruned + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + assert!(lane.storage.message(&1).is_some()); + assert!(lane.storage.message(&2).is_some()); + assert!(lane.storage.message(&3).is_some()); + assert_eq!( + lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), + Weight::zero() + ); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); + // after confirmation, some messages are received + assert_eq!( + lane.confirm_delivery(2, 2, &unrewarded_relayers(1..=2)), + ReceivalConfirmationResult::ConfirmedMessages(delivered_messages(1..=2)), + ); + assert_eq!( + lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), + RocksDbWeight::get().writes(3), + ); + assert!(lane.storage.message(&1).is_none()); + assert!(lane.storage.message(&2).is_none()); + assert!(lane.storage.message(&3).is_some()); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 3); + // after last message is confirmed, everything is pruned + assert_eq!( + lane.confirm_delivery(1, 3, &unrewarded_relayers(3..=3)), + ReceivalConfirmationResult::ConfirmedMessages(delivered_messages(3..=3)), + ); + assert_eq!( + lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), + RocksDbWeight::get().writes(2), + ); + assert!(lane.storage.message(&1).is_none()); + assert!(lane.storage.message(&2).is_none()); + assert!(lane.storage.message(&3).is_none()); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4); + }); + } + + #[test] + fn confirm_delivery_detects_when_more_than_expected_messages_are_confirmed() { + run_test(|| { + let mut lane = outbound_lane::(TEST_LANE_ID); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + assert_eq!( + lane.confirm_delivery(0, 3, &unrewarded_relayers(1..=3)), + ReceivalConfirmationResult::TryingToConfirmMoreMessagesThanExpected(3), + ); + assert_eq!( + lane.confirm_delivery(2, 3, &unrewarded_relayers(1..=3)), + ReceivalConfirmationResult::TryingToConfirmMoreMessagesThanExpected(3), + ); + assert_eq!( + lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), + ReceivalConfirmationResult::ConfirmedMessages(delivered_messages(1..=3)), + ); + }); + } +} diff --git a/modules/messages/src/weights.rs b/modules/messages/src/weights.rs new file mode 100644 index 00000000000..2ae60c17faf --- /dev/null +++ b/modules/messages/src/weights.rs @@ -0,0 +1,163 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Autogenerated weights for `pallet_bridge_messages` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-11-17, STEPS: 50, REPEAT: 20 +//! LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled +//! CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/millau-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_bridge_messages +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./modules/messages/src/weights.rs +// --template=./.maintain/millau-weight-template.hbs + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for `pallet_bridge_messages`. +pub trait WeightInfo { + fn receive_single_message_proof() -> Weight; + fn receive_two_messages_proof() -> Weight; + fn receive_single_message_proof_with_outbound_lane_state() -> Weight; + fn receive_single_message_proof_1_kb() -> Weight; + fn receive_single_message_proof_16_kb() -> Weight; + fn receive_single_prepaid_message_proof() -> Weight; + fn receive_delivery_proof_for_single_message() -> Weight; + fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight; + fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight; +} + +/// Weights for `pallet_bridge_messages` that are generated using one of the Bridge testnets. +/// +/// Those weights are test only and must never be used in production. +pub struct BridgeWeight(PhantomData); +impl WeightInfo for BridgeWeight { + fn receive_single_message_proof() -> Weight { + Weight::from_ref_time(50_596_000 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + fn receive_two_messages_proof() -> Weight { + Weight::from_ref_time(77_041_000 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + fn receive_single_message_proof_with_outbound_lane_state() -> Weight { + Weight::from_ref_time(58_331_000 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + fn receive_single_message_proof_1_kb() -> Weight { + Weight::from_ref_time(48_061_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + fn receive_single_message_proof_16_kb() -> Weight { + Weight::from_ref_time(101_601_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + fn receive_single_prepaid_message_proof() -> Weight { + Weight::from_ref_time(49_646_000 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + fn receive_delivery_proof_for_single_message() -> Weight { + Weight::from_ref_time(55_108_000 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { + Weight::from_ref_time(53_917_000 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { + Weight::from_ref_time(57_335_000 as u64) + .saturating_add(T::DbWeight::get().reads(5 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn receive_single_message_proof() -> Weight { + Weight::from_ref_time(50_596_000 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + fn receive_two_messages_proof() -> Weight { + Weight::from_ref_time(77_041_000 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + fn receive_single_message_proof_with_outbound_lane_state() -> Weight { + Weight::from_ref_time(58_331_000 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + fn receive_single_message_proof_1_kb() -> Weight { + Weight::from_ref_time(48_061_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + fn receive_single_message_proof_16_kb() -> Weight { + Weight::from_ref_time(101_601_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + fn receive_single_prepaid_message_proof() -> Weight { + Weight::from_ref_time(49_646_000 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + fn receive_delivery_proof_for_single_message() -> Weight { + Weight::from_ref_time(55_108_000 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { + Weight::from_ref_time(53_917_000 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { + Weight::from_ref_time(57_335_000 as u64) + .saturating_add(RocksDbWeight::get().reads(5 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) + } +} diff --git a/modules/messages/src/weights_ext.rs b/modules/messages/src/weights_ext.rs new file mode 100644 index 00000000000..1604832c079 --- /dev/null +++ b/modules/messages/src/weights_ext.rs @@ -0,0 +1,289 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Weight-related utilities. + +use crate::weights::WeightInfo; + +use bp_messages::{MessageNonce, UnrewardedRelayersState}; +use bp_runtime::{PreComputedSize, Size}; +use frame_support::weights::Weight; + +/// Size of the message being delivered in benchmarks. +pub const EXPECTED_DEFAULT_MESSAGE_LENGTH: u32 = 128; + +/// We assume that size of signed extensions on all our chains and size of all 'small' arguments of +/// calls we're checking here would fit 1KB. +const SIGNED_EXTENSIONS_SIZE: u32 = 1024; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// Rialto chain. This mostly depends on number of entries (and their density) in the storage trie. +/// Some reserve is reserved to account future chain growth. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// Ensure that weights from `WeightInfoExt` implementation are looking correct. +pub fn ensure_weights_are_correct() { + // verify `receive_messages_proof` weight components + assert_ne!(W::receive_messages_proof_overhead(), Weight::zero()); + assert_ne!(W::receive_messages_proof_messages_overhead(1), Weight::zero()); + assert_ne!(W::receive_messages_proof_outbound_lane_state_overhead(), Weight::zero()); + assert_ne!(W::storage_proof_size_overhead(1), Weight::zero()); + + // verify `receive_messages_delivery_proof` weight components + assert_ne!(W::receive_messages_delivery_proof_overhead(), Weight::zero()); + assert_ne!(W::storage_proof_size_overhead(1), Weight::zero()); +} + +/// Ensure that we're able to receive maximal (by-size and by-weight) message from other chain. +pub fn ensure_able_to_receive_message( + max_extrinsic_size: u32, + max_extrinsic_weight: Weight, + max_incoming_message_proof_size: u32, + max_incoming_message_dispatch_weight: Weight, +) { + // verify that we're able to receive proof of maximal-size message + let max_delivery_transaction_size = + max_incoming_message_proof_size.saturating_add(SIGNED_EXTENSIONS_SIZE); + assert!( + max_delivery_transaction_size <= max_extrinsic_size, + "Size of maximal message delivery transaction {} + {} is larger than maximal possible transaction size {}", + max_incoming_message_proof_size, + SIGNED_EXTENSIONS_SIZE, + max_extrinsic_size, + ); + + // verify that we're able to receive proof of maximal-size message with maximal dispatch weight + let max_delivery_transaction_dispatch_weight = W::receive_messages_proof_weight( + &PreComputedSize( + (max_incoming_message_proof_size + W::expected_extra_storage_proof_size()) as usize, + ), + 1, + max_incoming_message_dispatch_weight, + ); + assert!( + max_delivery_transaction_dispatch_weight.all_lte(max_extrinsic_weight), + "Weight of maximal message delivery transaction + {} is larger than maximal possible transaction weight {}", + max_delivery_transaction_dispatch_weight, + max_extrinsic_weight, + ); +} + +/// Ensure that we're able to receive maximal confirmation from other chain. +pub fn ensure_able_to_receive_confirmation( + max_extrinsic_size: u32, + max_extrinsic_weight: Weight, + max_inbound_lane_data_proof_size_from_peer_chain: u32, + max_unrewarded_relayer_entries_at_peer_inbound_lane: MessageNonce, + max_unconfirmed_messages_at_inbound_lane: MessageNonce, +) { + // verify that we're able to receive confirmation of maximal-size + let max_confirmation_transaction_size = + max_inbound_lane_data_proof_size_from_peer_chain.saturating_add(SIGNED_EXTENSIONS_SIZE); + assert!( + max_confirmation_transaction_size <= max_extrinsic_size, + "Size of maximal message delivery confirmation transaction {} + {} is larger than maximal possible transaction size {}", + max_inbound_lane_data_proof_size_from_peer_chain, + SIGNED_EXTENSIONS_SIZE, + max_extrinsic_size, + ); + + // verify that we're able to reward maximal number of relayers that have delivered maximal + // number of messages + let max_confirmation_transaction_dispatch_weight = W::receive_messages_delivery_proof_weight( + &PreComputedSize(max_inbound_lane_data_proof_size_from_peer_chain as usize), + &UnrewardedRelayersState { + unrewarded_relayer_entries: max_unrewarded_relayer_entries_at_peer_inbound_lane, + total_messages: max_unconfirmed_messages_at_inbound_lane, + ..Default::default() + }, + ); + assert!( + max_confirmation_transaction_dispatch_weight.all_lte(max_extrinsic_weight), + "Weight of maximal confirmation transaction {} is larger than maximal possible transaction weight {}", + max_confirmation_transaction_dispatch_weight, + max_extrinsic_weight, + ); +} + +/// Extended weight info. +pub trait WeightInfoExt: WeightInfo { + /// Size of proof that is already included in the single message delivery weight. + /// + /// The message submitter (at source chain) has already covered this cost. But there are two + /// factors that may increase proof size: (1) the message size may be larger than predefined + /// and (2) relayer may add extra trie nodes to the proof. So if proof size is larger than + /// this value, we're going to charge relayer for that. + fn expected_extra_storage_proof_size() -> u32; + + // Functions that are directly mapped to extrinsics weights. + + /// Weight of message delivery extrinsic. + fn receive_messages_proof_weight( + proof: &impl Size, + messages_count: u32, + dispatch_weight: Weight, + ) -> Weight { + // basic components of extrinsic weight + let transaction_overhead = Self::receive_messages_proof_overhead(); + let outbound_state_delivery_weight = + Self::receive_messages_proof_outbound_lane_state_overhead(); + let messages_delivery_weight = + Self::receive_messages_proof_messages_overhead(MessageNonce::from(messages_count)); + let messages_dispatch_weight = dispatch_weight; + + // proof size overhead weight + let expected_proof_size = EXPECTED_DEFAULT_MESSAGE_LENGTH + .saturating_mul(messages_count.saturating_sub(1)) + .saturating_add(Self::expected_extra_storage_proof_size()); + let actual_proof_size = proof.size(); + let proof_size_overhead = Self::storage_proof_size_overhead( + actual_proof_size.saturating_sub(expected_proof_size), + ); + + transaction_overhead + .saturating_add(outbound_state_delivery_weight) + .saturating_add(messages_delivery_weight) + .saturating_add(messages_dispatch_weight) + .saturating_add(proof_size_overhead) + } + + /// Weight of confirmation delivery extrinsic. + fn receive_messages_delivery_proof_weight( + proof: &impl Size, + relayers_state: &UnrewardedRelayersState, + ) -> Weight { + // basic components of extrinsic weight + let transaction_overhead = Self::receive_messages_delivery_proof_overhead(); + let messages_overhead = + Self::receive_messages_delivery_proof_messages_overhead(relayers_state.total_messages); + let relayers_overhead = Self::receive_messages_delivery_proof_relayers_overhead( + relayers_state.unrewarded_relayer_entries, + ); + + // proof size overhead weight + let expected_proof_size = Self::expected_extra_storage_proof_size(); + let actual_proof_size = proof.size(); + let proof_size_overhead = Self::storage_proof_size_overhead( + actual_proof_size.saturating_sub(expected_proof_size), + ); + + transaction_overhead + .saturating_add(messages_overhead) + .saturating_add(relayers_overhead) + .saturating_add(proof_size_overhead) + } + + // Functions that are used by extrinsics weights formulas. + + /// Returns weight overhead of message delivery transaction (`receive_messages_proof`). + fn receive_messages_proof_overhead() -> Weight { + let weight_of_two_messages_and_two_tx_overheads = + Self::receive_single_message_proof().saturating_mul(2); + let weight_of_two_messages_and_single_tx_overhead = Self::receive_two_messages_proof(); + weight_of_two_messages_and_two_tx_overheads + .saturating_sub(weight_of_two_messages_and_single_tx_overhead) + } + + /// Returns weight that needs to be accounted when receiving given a number of messages with + /// message delivery transaction (`receive_messages_proof`). + fn receive_messages_proof_messages_overhead(messages: MessageNonce) -> Weight { + let weight_of_two_messages_and_single_tx_overhead = Self::receive_two_messages_proof(); + let weight_of_single_message_and_single_tx_overhead = Self::receive_single_message_proof(); + weight_of_two_messages_and_single_tx_overhead + .saturating_sub(weight_of_single_message_and_single_tx_overhead) + .saturating_mul(messages as _) + } + + /// Returns weight that needs to be accounted when message delivery transaction + /// (`receive_messages_proof`) is carrying outbound lane state proof. + fn receive_messages_proof_outbound_lane_state_overhead() -> Weight { + let weight_of_single_message_and_lane_state = + Self::receive_single_message_proof_with_outbound_lane_state(); + let weight_of_single_message = Self::receive_single_message_proof(); + weight_of_single_message_and_lane_state.saturating_sub(weight_of_single_message) + } + + /// Returns weight overhead of delivery confirmation transaction + /// (`receive_messages_delivery_proof`). + fn receive_messages_delivery_proof_overhead() -> Weight { + let weight_of_two_messages_and_two_tx_overheads = + Self::receive_delivery_proof_for_single_message().saturating_mul(2); + let weight_of_two_messages_and_single_tx_overhead = + Self::receive_delivery_proof_for_two_messages_by_single_relayer(); + weight_of_two_messages_and_two_tx_overheads + .saturating_sub(weight_of_two_messages_and_single_tx_overhead) + } + + /// Returns weight that needs to be accounted when receiving confirmations for given a number of + /// messages with delivery confirmation transaction (`receive_messages_delivery_proof`). + fn receive_messages_delivery_proof_messages_overhead(messages: MessageNonce) -> Weight { + let weight_of_two_messages = + Self::receive_delivery_proof_for_two_messages_by_single_relayer(); + let weight_of_single_message = Self::receive_delivery_proof_for_single_message(); + weight_of_two_messages + .saturating_sub(weight_of_single_message) + .saturating_mul(messages as _) + } + + /// Returns weight that needs to be accounted when receiving confirmations for given a number of + /// relayers entries with delivery confirmation transaction (`receive_messages_delivery_proof`). + fn receive_messages_delivery_proof_relayers_overhead(relayers: MessageNonce) -> Weight { + let weight_of_two_messages_by_two_relayers = + Self::receive_delivery_proof_for_two_messages_by_two_relayers(); + let weight_of_two_messages_by_single_relayer = + Self::receive_delivery_proof_for_two_messages_by_single_relayer(); + weight_of_two_messages_by_two_relayers + .saturating_sub(weight_of_two_messages_by_single_relayer) + .saturating_mul(relayers as _) + } + + /// Returns weight that needs to be accounted when storage proof of given size is received + /// (either in `receive_messages_proof` or `receive_messages_delivery_proof`). + /// + /// **IMPORTANT**: this overhead is already included in the 'base' transaction cost - e.g. proof + /// size depends on messages count or number of entries in the unrewarded relayers set. So this + /// shouldn't be added to cost of transaction, but instead should act as a minimal cost that the + /// relayer must pay when it relays proof of given size (even if cost based on other parameters + /// is less than that cost). + fn storage_proof_size_overhead(proof_size: u32) -> Weight { + let proof_size_in_bytes = proof_size; + let byte_weight = (Self::receive_single_message_proof_16_kb() - + Self::receive_single_message_proof_1_kb()) / + (15 * 1024); + proof_size_in_bytes * byte_weight + } + + /// Returns weight of the pay-dispatch-fee operation for inbound messages. + /// + /// This function may return zero if runtime doesn't support pay-dispatch-fee-at-target-chain + /// option. + fn pay_inbound_dispatch_fee_overhead() -> Weight { + Self::receive_single_message_proof() + .saturating_sub(Self::receive_single_prepaid_message_proof()) + } +} + +impl WeightInfoExt for () { + fn expected_extra_storage_proof_size() -> u32 { + EXTRA_STORAGE_PROOF_SIZE + } +} + +impl WeightInfoExt for crate::weights::BridgeWeight { + fn expected_extra_storage_proof_size() -> u32 { + EXTRA_STORAGE_PROOF_SIZE + } +} diff --git a/modules/parachains/Cargo.toml b/modules/parachains/Cargo.toml new file mode 100644 index 00000000000..ce674459eac --- /dev/null +++ b/modules/parachains/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "pallet-bridge-parachains" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } + +# Bridge Dependencies + +bp-header-chain = { path = "../../primitives/header-chain", default-features = false } +bp-parachains = { path = "../../primitives/parachains", default-features = false } +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +pallet-bridge-grandpa = { path = "../grandpa", default-features = false } + +# Substrate Dependencies + +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +bp-header-chain = { path = "../../primitives/header-chain" } +bp-test-utils = { path = "../../primitives/test-utils" } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "bp-parachains/std", + "bp-polkadot-core/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "frame-system/std", + "frame-benchmarking/std", + "log/std", + "pallet-bridge-grandpa/std", + "scale-info/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", +] diff --git a/modules/parachains/src/benchmarking.rs b/modules/parachains/src/benchmarking.rs new file mode 100644 index 00000000000..aba296dfc1d --- /dev/null +++ b/modules/parachains/src/benchmarking.rs @@ -0,0 +1,106 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Parachains finality pallet benchmarking. + +use crate::{ + weights_ext::DEFAULT_PARACHAIN_HEAD_SIZE, Call, RelayBlockHash, RelayBlockHasher, + RelayBlockNumber, +}; + +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use bp_runtime::StorageProofSize; +use frame_benchmarking::{account, benchmarks_instance_pallet}; +use frame_system::RawOrigin; +use sp_std::prelude::*; + +/// Pallet we're benchmarking here. +pub struct Pallet, I: 'static>(crate::Pallet); + +/// Trait that must be implemented by runtime to benchmark the parachains finality pallet. +pub trait Config: crate::Config { + /// Generate parachain heads proof and prepare environment for verifying this proof. + fn prepare_parachain_heads_proof( + parachains: &[ParaId], + parachain_head_size: u32, + proof_size: StorageProofSize, + ) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>); +} + +benchmarks_instance_pallet! { + where_clause { + where + >::BridgedChain: + bp_runtime::Chain< + BlockNumber = RelayBlockNumber, + Hash = RelayBlockHash, + Hasher = RelayBlockHasher, + >, + } + + // Benchmark `submit_parachain_heads` extrinsic with different number of parachains. + submit_parachain_heads_with_n_parachains { + let p in 1..1024; + + let sender = account("sender", 0, 0); + let parachains = (1..=p).map(ParaId).collect::>(); + let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::Minimal(0), + ); + let at_relay_block = (relay_block_number, relay_block_hash); + }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } + + // Benchmark `submit_parachain_heads` extrinsic with 1kb proof size. + submit_parachain_heads_with_1kb_proof { + let sender = account("sender", 0, 0); + let parachains = vec![ParaId(1)]; + let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::HasExtraNodes(1024), + ); + let at_relay_block = (relay_block_number, relay_block_hash); + }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } + + // Benchmark `submit_parachain_heads` extrinsic with 16kb proof size. + submit_parachain_heads_with_16kb_proof { + let sender = account("sender", 0, 0); + let parachains = vec![ParaId(1)]; + let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::HasExtraNodes(16 * 1024), + ); + let at_relay_block = (relay_block_number, relay_block_hash); + }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } +} diff --git a/modules/parachains/src/extension.rs b/modules/parachains/src/extension.rs new file mode 100644 index 00000000000..5c2f54257ff --- /dev/null +++ b/modules/parachains/src/extension.rs @@ -0,0 +1,166 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{Config, Pallet, RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use bp_runtime::FilterCall; +use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; +use sp_runtime::transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}; + +/// Validate parachain heads in order to avoid "mining" transactions that provide +/// outdated bridged parachain heads. Without this validation, even honest relayers +/// may lose their funds if there are multiple relays running and submitting the +/// same information. +/// +/// This validation only works with transactions that are updating single parachain +/// head. We can't use unbounded validation - it may take too long and either break +/// block production, or "eat" significant portion of block production time literally +/// for nothing. In addition, the single-parachain-head-per-transaction is how the +/// pallet will be used in our environment. +impl< + Call: IsSubType, T>>, + T: frame_system::Config + Config, + I: 'static, + > FilterCall for Pallet +where + >::BridgedChain: + bp_runtime::Chain< + BlockNumber = RelayBlockNumber, + Hash = RelayBlockHash, + Hasher = RelayBlockHasher, + >, +{ + fn validate(call: &Call) -> TransactionValidity { + let (updated_at_relay_block_number, parachains) = match call.is_sub_type() { + Some(crate::Call::::submit_parachain_heads { + ref at_relay_block, + ref parachains, + .. + }) => (at_relay_block.0, parachains), + _ => return Ok(ValidTransaction::default()), + }; + let (parachain, parachain_head_hash) = match parachains.as_slice() { + &[(parachain, parachain_head_hash)] => (parachain, parachain_head_hash), + _ => return Ok(ValidTransaction::default()), + }; + + let maybe_stored_best_head = crate::ParasInfo::::get(parachain); + let is_valid = Self::validate_updated_parachain_head( + parachain, + &maybe_stored_best_head, + updated_at_relay_block_number, + parachain_head_hash, + "Rejecting obsolete parachain-head transaction", + ); + + if is_valid { + Ok(ValidTransaction::default()) + } else { + InvalidTransaction::Stale.into() + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + extension::FilterCall, + mock::{run_test, RuntimeCall, TestRuntime}, + ParaInfo, ParasInfo, RelayBlockNumber, + }; + use bp_parachains::BestParaHeadHash; + use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; + + fn validate_submit_parachain_heads( + num: RelayBlockNumber, + parachains: Vec<(ParaId, ParaHash)>, + ) -> bool { + crate::Pallet::::validate(&RuntimeCall::Parachains(crate::Call::< + TestRuntime, + (), + >::submit_parachain_heads { + at_relay_block: (num, Default::default()), + parachains, + parachain_heads_proof: ParaHeadsProof(Vec::new()), + })) + .is_ok() + } + + fn sync_to_relay_header_10() { + ParasInfo::::insert( + ParaId(1), + ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 10, + head_hash: [1u8; 32].into(), + }, + next_imported_hash_position: 0, + }, + ); + } + + #[test] + fn extension_rejects_header_from_the_obsolete_relay_block() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(5, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_rejects_header_from_the_same_relay_block() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(10, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_rejects_header_from_new_relay_block_with_same_hash() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(20, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_accepts_new_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#15 => tx is + // accepted + sync_to_relay_header_10(); + assert!(validate_submit_parachain_heads(15, vec![(ParaId(1), [2u8; 32].into())])); + }); + } + + #[test] + fn extension_accepts_if_more_than_one_parachain_is_submitted() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5, but another + // parachain head is also supplied => tx is accepted + sync_to_relay_header_10(); + assert!(validate_submit_parachain_heads( + 5, + vec![(ParaId(1), [1u8; 32].into()), (ParaId(2), [1u8; 32].into())] + )); + }); + } +} diff --git a/modules/parachains/src/lib.rs b/modules/parachains/src/lib.rs new file mode 100644 index 00000000000..e6625476a3d --- /dev/null +++ b/modules/parachains/src/lib.rs @@ -0,0 +1,1403 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Parachains finality module. +//! +//! This module needs to be deployed with GRANDPA module, which is syncing relay +//! chain blocks. The main entry point of this module is `submit_parachain_heads`, which +//! accepts storage proof of some parachain `Heads` entries from bridged relay chain. +//! It requires corresponding relay headers to be already synced. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use weights::WeightInfo; +pub use weights_ext::WeightInfoExt; + +use bp_header_chain::HeaderChain; +use bp_parachains::{parachain_head_storage_key_at_source, ParaInfo}; +use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; +use bp_runtime::{HashOf, HeaderOf, Parachain, StorageProofError}; +use codec::Decode; +use frame_support::{dispatch::PostDispatchInfo, traits::Contains}; +use sp_runtime::traits::Header as HeaderT; +use sp_std::{marker::PhantomData, vec::Vec}; + +// Re-export in crate namespace for `construct_runtime!`. +pub use pallet::*; + +pub mod weights; +pub mod weights_ext; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + +mod extension; +#[cfg(test)] +mod mock; + +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-parachains"; + +/// Block hash of the bridged relay chain. +pub type RelayBlockHash = bp_polkadot_core::Hash; +/// Block number of the bridged relay chain. +pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; +/// Hasher of the bridged relay chain. +pub type RelayBlockHasher = bp_polkadot_core::Hasher; + +/// Artifacts of the parachains head update. +struct UpdateParachainHeadArtifacts { + /// New best head of the parachain. + pub best_head: ParaInfo, + /// If `true`, some old parachain head has been pruned during update. + pub prune_happened: bool, +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use bp_parachains::{BestParaHeadHash, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider}; + use bp_runtime::{ + BasicOperatingMode, BoundedStorageValue, OwnedBridgeModule, StorageDoubleMapKeyProvider, + StorageMapKeyProvider, + }; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + /// Stored parachain head of given parachains pallet. + pub type StoredParaHeadOf = + BoundedStorageValue<>::MaxParaHeadSize, ParaHead>; + /// Weight info of the given parachains pallet. + pub type WeightInfoOf = >::WeightInfo; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// The caller has provided head of parachain that the pallet is not configured to track. + UntrackedParachainRejected { parachain: ParaId }, + /// The caller has declared that he has provided given parachain head, but it is missing + /// from the storage proof. + MissingParachainHead { parachain: ParaId }, + /// The caller has provided parachain head hash that is not matching the hash read from the + /// storage proof. + IncorrectParachainHeadHash { + parachain: ParaId, + parachain_head_hash: ParaHash, + actual_parachain_head_hash: ParaHash, + }, + /// The caller has provided obsolete parachain head, which is already known to the pallet. + RejectedObsoleteParachainHead { parachain: ParaId, parachain_head_hash: ParaHash }, + /// The caller has provided parachain head that exceeds the maximal configured head size. + RejectedLargeParachainHead { + parachain: ParaId, + parachain_head_hash: ParaHash, + parachain_head_size: u32, + }, + /// Parachain head has been updated. + UpdatedParachainHead { parachain: ParaId, parachain_head_hash: ParaHash }, + } + + #[pallet::error] + pub enum Error { + /// Relay chain block hash is unknown to us. + UnknownRelayChainBlock, + /// The number of stored relay block is different from what the relayer has provided. + InvalidRelayChainBlockNumber, + /// Invalid storage proof has been passed. + InvalidStorageProof, + /// Given parachain head is unknown. + UnknownParaHead, + /// The storage proof doesn't contains storage root. So it is invalid for given header. + StorageRootMismatch, + /// Failed to extract state root from given parachain head. + FailedToExtractStateRoot, + /// Error generated by the `OwnedBridgeModule` trait. + BridgeModule(bp_runtime::OwnedBridgeModuleError), + } + + #[pallet::config] + #[pallet::disable_frame_system_supertrait_check] + pub trait Config: + pallet_bridge_grandpa::Config + { + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + /// Benchmarks results from runtime we're plugged into. + type WeightInfo: WeightInfoExt; + + /// Instance of bridges GRANDPA pallet (within this runtime) that this pallet is linked to. + /// + /// The GRANDPA pallet instance must be configured to import headers of relay chain that + /// we're interested in. + type BridgesGrandpaPalletInstance: 'static; + + /// Name of the original `paras` pallet in the `construct_runtime!()` call at the bridged + /// chain. + /// + /// Please keep in mind that this should be the name of the `runtime_parachains::paras` + /// pallet from polkadot repository, not the `pallet-bridge-parachains`. + #[pallet::constant] + type ParasPalletName: Get<&'static str>; + + /// Set of parachains that are tracked by this pallet. + /// + /// The set may be extended easily, without requiring any runtime upgrades. Removing tracked + /// parachain requires special handling - pruning existing heads and cleaning related data + /// structures. + type TrackedParachains: Contains; + + /// Maximal number of single parachain heads to keep in the storage. + /// + /// The setting is there to prevent growing the on-chain state indefinitely. Note + /// the setting does not relate to parachain block numbers - we will simply keep as much + /// items in the storage, so it doesn't guarantee any fixed timeframe for heads. + /// + /// Incautious change of this constant may lead to orphan entries in the runtime storage. + #[pallet::constant] + type HeadsToKeep: Get; + + /// Maximal size (in bytes) of the SCALE-encoded parachain head. + /// + /// Keep in mind that the size of any tracked parachain header must not exceed this value. + /// So if you're going to track multiple parachains, one of which is storing large digests + /// in its headers, you shall choose this maximal value. + /// + /// There's no mandatory headers in this pallet, so it can't stall if there's some header + /// that exceeds this bound. + #[pallet::constant] + type MaxParaHeadSize: Get; + } + + /// Optional pallet owner. + /// + /// Pallet owner has a right to halt all pallet operations and then resume them. If it is + /// `None`, then there are no direct ways to halt/resume pallet operations, but other + /// runtime methods may still be used to do that (i.e. democracy::referendum to update halt + /// flag directly or call the `halt_operations`). + #[pallet::storage] + pub type PalletOwner, I: 'static = ()> = + StorageValue<_, T::AccountId, OptionQuery>; + + /// The current operating mode of the pallet. + /// + /// Depending on the mode either all, or no transactions will be allowed. + #[pallet::storage] + pub type PalletOperatingMode, I: 'static = ()> = + StorageValue<_, BasicOperatingMode, ValueQuery>; + + /// Parachains info. + /// + /// Contains the following info: + /// - best parachain head hash + /// - the head of the `ImportedParaHashes` ring buffer + #[pallet::storage] + pub type ParasInfo, I: 'static = ()> = StorageMap< + _, + ::Hasher, + ::Key, + ::Value, + >; + + /// Parachain heads which have been imported into the pallet. + #[pallet::storage] + pub type ImportedParaHeads, I: 'static = ()> = StorageDoubleMap< + _, + ::Hasher1, + ::Key1, + ::Hasher2, + ::Key2, + StoredParaHeadOf, + >; + + /// A ring buffer of imported parachain head hashes. Ordered by the insertion time. + #[pallet::storage] + pub(super) type ImportedParaHashes, I: 'static = ()> = + StorageDoubleMap<_, Blake2_128Concat, ParaId, Twox64Concat, u32, ParaHash>; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(PhantomData<(T, I)>); + + impl, I: 'static> OwnedBridgeModule for Pallet { + const LOG_TARGET: &'static str = LOG_TARGET; + type OwnerStorage = PalletOwner; + type OperatingMode = BasicOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + } + + #[pallet::call] + impl, I: 'static> Pallet + where + >::BridgedChain: + bp_runtime::Chain< + BlockNumber = RelayBlockNumber, + Hash = RelayBlockHash, + Hasher = RelayBlockHasher, + >, + { + /// Submit proof of one or several parachain heads. + /// + /// The proof is supposed to be proof of some `Heads` entries from the + /// `polkadot-runtime-parachains::paras` pallet instance, deployed at the bridged chain. + /// The proof is supposed to be crafted at the `relay_header_hash` that must already be + /// imported by corresponding GRANDPA pallet at this chain. + #[pallet::weight(WeightInfoOf::::submit_parachain_heads_weight( + T::DbWeight::get(), + parachain_heads_proof, + parachains.len() as _, + ))] + pub fn submit_parachain_heads( + _origin: OriginFor, + at_relay_block: (RelayBlockNumber, RelayBlockHash), + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> DispatchResultWithPostInfo { + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + // we'll need relay chain header to verify that parachains heads are always increasing. + let (relay_block_number, relay_block_hash) = at_relay_block; + let relay_block = pallet_bridge_grandpa::ImportedHeaders::< + T, + T::BridgesGrandpaPalletInstance, + >::get(relay_block_hash) + .ok_or(Error::::UnknownRelayChainBlock)?; + ensure!( + *relay_block.number() == relay_block_number, + Error::::InvalidRelayChainBlockNumber, + ); + + // now parse storage proof and read parachain heads + let mut actual_weight = WeightInfoOf::::submit_parachain_heads_weight( + T::DbWeight::get(), + ¶chain_heads_proof, + parachains.len() as _, + ); + + pallet_bridge_grandpa::Pallet::::parse_finalized_storage_proof( + relay_block_hash, + sp_trie::StorageProof::new(parachain_heads_proof.0), + move |storage| { + for (parachain, parachain_head_hash) in parachains { + // if we're not tracking this parachain, we'll just ignore its head proof here + if !T::TrackedParachains::contains(¶chain) { + log::trace!( + target: LOG_TARGET, + "The head of parachain {:?} has been provided, but it is not tracked by the pallet", + parachain, + ); + Self::deposit_event(Event::UntrackedParachainRejected { parachain }); + continue; + } + + let parachain_head = match Pallet::::read_parachain_head(&storage, parachain) { + Ok(Some(parachain_head)) => parachain_head, + Ok(None) => { + log::trace!( + target: LOG_TARGET, + "The head of parachain {:?} is None. {}", + parachain, + if ParasInfo::::contains_key(parachain) { + "Looks like it is not yet registered at the source relay chain" + } else { + "Looks like it has been deregistered from the source relay chain" + }, + ); + Self::deposit_event(Event::MissingParachainHead { parachain }); + continue; + }, + Err(e) => { + log::trace!( + target: LOG_TARGET, + "The read of head of parachain {:?} has failed: {:?}", + parachain, + e, + ); + Self::deposit_event(Event::MissingParachainHead { parachain }); + continue; + }, + }; + + // if relayer has specified invalid parachain head hash, ignore the head + // (this isn't strictly necessary, but better safe than sorry) + let actual_parachain_head_hash = parachain_head.hash(); + if parachain_head_hash != actual_parachain_head_hash { + log::trace!( + target: LOG_TARGET, + "The submitter has specified invalid parachain {:?} head hash: {:?} vs {:?}", + parachain, + parachain_head_hash, + actual_parachain_head_hash, + ); + Self::deposit_event(Event::IncorrectParachainHeadHash { + parachain, + parachain_head_hash, + actual_parachain_head_hash, + }); + continue; + } + + let update_result: Result<_, ()> = ParasInfo::::try_mutate(parachain, |stored_best_head| { + let artifacts = Pallet::::update_parachain_head( + parachain, + stored_best_head.take(), + relay_block_number, + parachain_head, + parachain_head_hash, + )?; + *stored_best_head = Some(artifacts.best_head); + Ok(artifacts.prune_happened) + }); + + // we're refunding weight if update has not happened and if pruning has not happened + let is_update_happened = matches!(update_result, Ok(_)); + if !is_update_happened { + actual_weight = actual_weight + .saturating_sub(WeightInfoOf::::parachain_head_storage_write_weight(T::DbWeight::get())); + } + let is_prune_happened = matches!(update_result, Ok(true)); + if !is_prune_happened { + actual_weight = actual_weight + .saturating_sub(WeightInfoOf::::parachain_head_pruning_weight(T::DbWeight::get())); + } + } + }, + ) + .map_err(|_| Error::::InvalidStorageProof)?; + + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) + } + + /// Change `PalletOwner`. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { + >::set_owner(origin, new_owner) + } + + /// Halt or resume all pallet operations. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_operating_mode( + origin: OriginFor, + operating_mode: BasicOperatingMode, + ) -> DispatchResult { + >::set_operating_mode(origin, operating_mode) + } + } + + impl, I: 'static> Pallet { + /// Get best finalized header of the given parachain. + pub fn best_parachain_head(parachain: ParaId) -> Option { + let best_para_head_hash = ParasInfo::::get(parachain)?.best_head_hash.head_hash; + ImportedParaHeads::::get(parachain, best_para_head_hash).map(|h| h.into_inner()) + } + + /// Get parachain head with given hash. + pub fn parachain_head(parachain: ParaId, hash: ParaHash) -> Option { + ImportedParaHeads::::get(parachain, hash).map(|h| h.into_inner()) + } + + /// Read parachain head from storage proof. + fn read_parachain_head( + storage: &bp_runtime::StorageProofChecker, + parachain: ParaId, + ) -> Result, StorageProofError> { + let parachain_head_key = + parachain_head_storage_key_at_source(T::ParasPalletName::get(), parachain); + storage.read_and_decode_value(parachain_head_key.0.as_ref()) + } + + /// Check if para head has been already updated at better relay chain block. + /// Without this check, we may import heads in random order. + /// + /// Returns `true` if the pallet is ready to import given parachain head. + /// Returns `false` if the pallet already knows the same or better parachain head. + #[must_use] + pub fn validate_updated_parachain_head( + parachain: ParaId, + maybe_stored_best_head: &Option, + updated_at_relay_block_number: RelayBlockNumber, + updated_head_hash: ParaHash, + err_log_prefix: &str, + ) -> bool { + let stored_best_head = match maybe_stored_best_head { + Some(stored_best_head) => stored_best_head, + None => return true, + }; + + if stored_best_head.best_head_hash.at_relay_block_number >= + updated_at_relay_block_number + { + log::trace!( + target: LOG_TARGET, + "{}. The parachain head for {:?} was already updated at better relay chain block {} >= {}.", + err_log_prefix, + parachain, + stored_best_head.best_head_hash.at_relay_block_number, + updated_at_relay_block_number + ); + return false + } + + if stored_best_head.best_head_hash.head_hash == updated_head_hash { + log::trace!( + target: LOG_TARGET, + "{}. The parachain head hash for {:?} was already updated to {} at block {} < {}.", + err_log_prefix, + parachain, + updated_head_hash, + stored_best_head.best_head_hash.at_relay_block_number, + updated_at_relay_block_number + ); + return false + } + + true + } + + /// Try to update parachain head. + pub(super) fn update_parachain_head( + parachain: ParaId, + stored_best_head: Option, + updated_at_relay_block_number: RelayBlockNumber, + updated_head: ParaHead, + updated_head_hash: ParaHash, + ) -> Result { + // check if head has been already updated at better relay chain block. Without this + // check, we may import heads in random order + let err_log_prefix = "The parachain head can't be updated"; + let is_valid = Self::validate_updated_parachain_head( + parachain, + &stored_best_head, + updated_at_relay_block_number, + updated_head_hash, + err_log_prefix, + ); + if !is_valid { + Self::deposit_event(Event::RejectedObsoleteParachainHead { + parachain, + parachain_head_hash: updated_head_hash, + }); + return Err(()) + } + + // verify that the parachain head size is <= `MaxParaHeadSize` + let updated_head = match StoredParaHeadOf::::try_from_inner(updated_head) { + Ok(updated_head) => updated_head, + Err(e) => { + log::trace!( + target: LOG_TARGET, + "{}. The parachain head size for {:?} is {}. It exceeds maximal configured size {}.", + err_log_prefix, + parachain, + e.value_size, + e.maximal_size, + ); + + Self::deposit_event(Event::RejectedLargeParachainHead { + parachain, + parachain_head_hash: updated_head_hash, + parachain_head_size: e.value_size as _, + }); + + return Err(()) + }, + }; + + let next_imported_hash_position = stored_best_head + .map_or(0, |stored_best_head| stored_best_head.next_imported_hash_position); + + // insert updated best parachain head + let head_hash_to_prune = + ImportedParaHashes::::try_get(parachain, next_imported_hash_position); + let updated_best_para_head = ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: updated_at_relay_block_number, + head_hash: updated_head_hash, + }, + next_imported_hash_position: (next_imported_hash_position + 1) % + T::HeadsToKeep::get(), + }; + ImportedParaHashes::::insert( + parachain, + next_imported_hash_position, + updated_head_hash, + ); + ImportedParaHeads::::insert(parachain, updated_head_hash, updated_head); + log::trace!( + target: LOG_TARGET, + "Updated head of parachain {:?} to {}", + parachain, + updated_head_hash, + ); + + // remove old head + let prune_happened = head_hash_to_prune.is_ok(); + if let Ok(head_hash_to_prune) = head_hash_to_prune { + log::trace!( + target: LOG_TARGET, + "Pruning old head of parachain {:?}: {}", + parachain, + head_hash_to_prune, + ); + ImportedParaHeads::::remove(parachain, head_hash_to_prune); + } + Self::deposit_event(Event::UpdatedParachainHead { + parachain, + parachain_head_hash: updated_head_hash, + }); + + Ok(UpdateParachainHeadArtifacts { best_head: updated_best_para_head, prune_happened }) + } + } + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + /// Initial pallet operating mode. + pub operating_mode: BasicOperatingMode, + /// Initial pallet owner. + pub owner: Option, + /// Dummy marker. + pub phantom: sp_std::marker::PhantomData, + } + + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { + operating_mode: Default::default(), + owner: Default::default(), + phantom: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + PalletOperatingMode::::put(self.operating_mode); + if let Some(ref owner) = self.owner { + PalletOwner::::put(owner); + } + } + } +} + +/// Single parachain header chain adapter. +pub struct ParachainHeaders(PhantomData<(T, I, C)>); + +impl, I: 'static, C: Parachain> HeaderChain + for ParachainHeaders +{ + fn finalized_header(hash: HashOf) -> Option> { + Pallet::::parachain_head(ParaId(C::PARACHAIN_ID), hash) + .and_then(|head| Decode::decode(&mut &head.0[..]).ok()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{ + run_test, test_relay_header, RuntimeEvent as TestEvent, RuntimeOrigin, TestRuntime, + MAXIMAL_PARACHAIN_HEAD_SIZE, PARAS_PALLET_NAME, UNTRACKED_PARACHAIN_ID, + }; + use codec::Encode; + + use bp_parachains::{BestParaHeadHash, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider}; + use bp_runtime::{ + record_all_trie_keys, BasicOperatingMode, OwnedBridgeModuleError, + StorageDoubleMapKeyProvider, StorageMapKeyProvider, + }; + use bp_test_utils::{ + authority_list, generate_owned_bridge_module_tests, make_default_justification, + }; + use frame_support::{ + assert_noop, assert_ok, + dispatch::DispatchResultWithPostInfo, + storage::generator::{StorageDoubleMap, StorageMap}, + traits::{Get, OnInitialize}, + weights::Weight, + }; + use frame_system::{EventRecord, Pallet as System, Phase}; + use sp_runtime::DispatchError; + use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; + + type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; + type WeightInfo = ::WeightInfo; + type DbWeight = ::DbWeight; + + fn initialize(state_root: RelayBlockHash) { + pallet_bridge_grandpa::Pallet::::initialize( + RuntimeOrigin::root(), + bp_header_chain::InitializationData { + header: Box::new(test_relay_header(0, state_root)), + authority_list: authority_list(), + set_id: 1, + operating_mode: BasicOperatingMode::Normal, + }, + ) + .unwrap(); + } + + fn proceed(num: RelayBlockNumber, state_root: RelayBlockHash) { + pallet_bridge_grandpa::Pallet::::on_initialize( + 0, + ); + + let header = test_relay_header(num, state_root); + let justification = make_default_justification(&header); + assert_ok!( + pallet_bridge_grandpa::Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification, + ) + ); + } + + fn prepare_parachain_heads_proof( + heads: Vec<(u32, ParaHead)>, + ) -> (RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) { + let mut parachains = Vec::with_capacity(heads.len()); + let mut root = Default::default(); + let mut mdb = MemoryDB::default(); + { + let mut trie = TrieDBMutBuilderV1::::new(&mut mdb, &mut root).build(); + for (parachain, head) in heads { + let storage_key = + parachain_head_storage_key_at_source(PARAS_PALLET_NAME, ParaId(parachain)); + trie.insert(&storage_key.0, &head.encode()) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in tests"); + parachains.push((ParaId(parachain), head.hash())); + } + } + + // generate storage proof to be delivered to This chain + let mut proof_recorder = Recorder::>::new(); + record_all_trie_keys::, _>(&mdb, &root, &mut proof_recorder) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); + let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); + + (root, ParaHeadsProof(storage_proof), parachains) + } + + fn initial_best_head(parachain: u32) -> ParaInfo { + ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(parachain, 0).hash(), + }, + next_imported_hash_position: 1, + } + } + + fn head_data(parachain: u32, head_number: u32) -> ParaHead { + ParaHead((parachain, head_number).encode()) + } + + fn large_head_data(parachain: u32, head_number: u32) -> ParaHead { + ParaHead( + (parachain, head_number, vec![42u8; MAXIMAL_PARACHAIN_HEAD_SIZE as usize]).encode(), + ) + } + + fn head_hash(parachain: u32, head_number: u32) -> ParaHash { + head_data(parachain, head_number).hash() + } + + fn import_parachain_1_head( + relay_chain_block: RelayBlockNumber, + relay_state_root: RelayBlockHash, + parachains: Vec<(ParaId, ParaHash)>, + proof: ParaHeadsProof, + ) -> DispatchResultWithPostInfo { + Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (relay_chain_block, test_relay_header(relay_chain_block, relay_state_root).hash()), + parachains, + proof, + ) + } + + fn weight_of_import_parachain_1_head(proof: &ParaHeadsProof, prune_expected: bool) -> Weight { + let db_weight = ::DbWeight::get(); + WeightInfoOf::::submit_parachain_heads_weight(db_weight, proof, 1) + .saturating_sub(if prune_expected { + Weight::zero() + } else { + WeightInfoOf::::parachain_head_pruning_weight(db_weight) + }) + } + + #[test] + fn submit_parachain_heads_checks_operating_mode() { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + + run_test(|| { + initialize(state_root); + + // `submit_parachain_heads()` should fail when the pallet is halted. + PalletOperatingMode::::put(BasicOperatingMode::Halted); + assert_noop!( + Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains.clone(), + proof.clone(), + ), + Error::::BridgeModule(OwnedBridgeModuleError::Halted) + ); + + // `submit_parachain_heads()` should succeed now that the pallet is resumed. + PalletOperatingMode::::put(BasicOperatingMode::Normal); + assert_ok!(Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + ),); + }); + } + + #[test] + fn imports_initial_parachain_heads() { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 0)), (3, head_data(3, 10))]); + run_test(|| { + initialize(state_root); + + // we're trying to update heads of parachains 1, 2 and 3 + let expected_weight = + WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 2); + let result = Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + ); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); + + // but only 1 and 2 are updated, because proof is missing head of parachain#2 + assert_eq!(ParasInfo::::get(ParaId(1)), Some(initial_best_head(1))); + assert_eq!(ParasInfo::::get(ParaId(2)), None); + assert_eq!( + ParasInfo::::get(ParaId(3)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(3, 10).hash() + }, + next_imported_hash_position: 1, + }) + ); + + assert_eq!( + ImportedParaHeads::::get( + ParaId(1), + initial_best_head(1).best_head_hash.head_hash + ) + .map(|h| h.into_inner()), + Some(head_data(1, 0)) + ); + assert_eq!( + ImportedParaHeads::::get( + ParaId(2), + initial_best_head(2).best_head_hash.head_hash + ) + .map(|h| h.into_inner()), + None + ); + assert_eq!( + ImportedParaHeads::::get(ParaId(3), head_hash(3, 10)) + .map(|h| h.into_inner()), + Some(head_data(3, 10)) + ); + + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(3), + parachain_head_hash: head_data(3, 10).hash(), + }), + topics: vec![], + } + ], + ); + }); + } + + #[test] + fn imports_parachain_heads_is_able_to_progress() { + let (state_root_5, proof_5, parachains_5) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root_10, proof_10, parachains_10) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); + run_test(|| { + // start with relay block #0 and import head#5 of parachain#1 + initialize(state_root_5); + assert_ok!(import_parachain_1_head(0, state_root_5, parachains_5, proof_5)); + assert_eq!( + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(1, 5).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!( + ImportedParaHeads::::get(ParaId(1), head_data(1, 5).hash()) + .map(|h| h.into_inner()), + Some(head_data(1, 5)) + ); + assert_eq!( + ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()) + .map(|h| h.into_inner()), + None + ); + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + }], + ); + + // import head#10 of parachain#1 at relay block #1 + proceed(1, state_root_10); + assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10)); + assert_eq!( + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 1, + head_hash: head_data(1, 10).hash() + }, + next_imported_hash_position: 2, + }) + ); + assert_eq!( + ImportedParaHeads::::get(ParaId(1), head_data(1, 5).hash()) + .map(|h| h.into_inner()), + Some(head_data(1, 5)) + ); + assert_eq!( + ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()) + .map(|h| h.into_inner()), + Some(head_data(1, 10)) + ); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 10).hash(), + }), + topics: vec![], + } + ], + ); + }); + } + + #[test] + fn ignores_untracked_parachain() { + let (state_root, proof, parachains) = prepare_parachain_heads_proof(vec![ + (1, head_data(1, 5)), + (UNTRACKED_PARACHAIN_ID, head_data(1, 5)), + (2, head_data(1, 5)), + ]); + run_test(|| { + // start with relay block #0 and try to import head#5 of parachain#1 and untracked + // parachain + let expected_weight = + WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 3) + .saturating_sub(WeightInfo::parachain_head_storage_write_weight( + DbWeight::get(), + )); + initialize(state_root); + let result = Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + ); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); + assert_eq!( + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(1, 5).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!(ParasInfo::::get(ParaId(UNTRACKED_PARACHAIN_ID)), None,); + assert_eq!( + ParasInfo::::get(ParaId(2)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(1, 5).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UntrackedParachainRejected { + parachain: ParaId(UNTRACKED_PARACHAIN_ID), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(2), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + } + ], + ); + }); + } + + #[test] + fn does_nothing_when_already_imported_this_head_at_previous_relay_header() { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + run_test(|| { + // import head#0 of parachain#1 at relay block#0 + initialize(state_root); + assert_ok!(import_parachain_1_head(0, state_root, parachains.clone(), proof.clone())); + assert_eq!(ParasInfo::::get(ParaId(1)), Some(initial_best_head(1))); + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, + }), + topics: vec![], + }], + ); + + // try to import head#0 of parachain#1 at relay block#1 + // => call succeeds, but nothing is changed + proceed(1, state_root); + assert_ok!(import_parachain_1_head(1, state_root, parachains, proof)); + assert_eq!(ParasInfo::::get(ParaId(1)), Some(initial_best_head(1))); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead { + parachain: ParaId(1), + parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, + }), + topics: vec![], + } + ], + ); + }); + } + + #[test] + fn does_nothing_when_already_imported_head_at_better_relay_header() { + let (state_root_5, proof_5, parachains_5) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root_10, proof_10, parachains_10) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); + run_test(|| { + // start with relay block #0 + initialize(state_root_5); + + // head#10 of parachain#1 at relay block#1 + proceed(1, state_root_10); + assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10)); + assert_eq!( + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 1, + head_hash: head_data(1, 10).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 10).hash(), + }), + topics: vec![], + }], + ); + + // now try to import head#5 at relay block#0 + // => nothing is changed, because better head has already been imported + assert_ok!(import_parachain_1_head(0, state_root_5, parachains_5, proof_5)); + assert_eq!( + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 1, + head_hash: head_data(1, 10).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 10).hash(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + } + ], + ); + }); + } + + #[test] + fn does_nothing_when_parachain_head_is_too_large() { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5)), (2, large_head_data(1, 5))]); + run_test(|| { + // start with relay block #0 and try to import head#5 of parachain#1 and untracked + // parachain + initialize(state_root); + let result = Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + ); + assert_ok!(result); + assert_eq!( + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(1, 5).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!(ParasInfo::::get(ParaId(2)), None); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::RejectedLargeParachainHead { + parachain: ParaId(2), + parachain_head_hash: large_head_data(1, 5).hash(), + parachain_head_size: large_head_data(1, 5).encoded_size() as u32, + }), + topics: vec![], + }, + ], + ); + }); + } + + #[test] + fn prunes_old_heads() { + run_test(|| { + let heads_to_keep = crate::mock::HeadsToKeep::get(); + + // import exactly `HeadsToKeep` headers + for i in 0..heads_to_keep { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, i))]); + if i == 0 { + initialize(state_root); + } else { + proceed(i, state_root); + } + + let expected_weight = weight_of_import_parachain_1_head(&proof, false); + let result = import_parachain_1_head(i, state_root, parachains, proof); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); + } + + // nothing is pruned yet + for i in 0..heads_to_keep { + assert!(ImportedParaHeads::::get(ParaId(1), head_data(1, i).hash()) + .is_some()); + } + + // import next relay chain header and next parachain head + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, heads_to_keep))]); + proceed(heads_to_keep, state_root); + let expected_weight = weight_of_import_parachain_1_head(&proof, true); + let result = import_parachain_1_head(heads_to_keep, state_root, parachains, proof); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); + + // and the head#0 is pruned + assert!( + ImportedParaHeads::::get(ParaId(1), head_data(1, 0).hash()).is_none() + ); + for i in 1..=heads_to_keep { + assert!(ImportedParaHeads::::get(ParaId(1), head_data(1, i).hash()) + .is_some()); + } + }); + } + + #[test] + fn fails_on_unknown_relay_chain_block() { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + run_test(|| { + // start with relay block #0 + initialize(state_root); + + // try to import head#5 of parachain#1 at unknown relay chain block #1 + assert_noop!( + import_parachain_1_head(1, state_root, parachains, proof), + Error::::UnknownRelayChainBlock + ); + }); + } + + #[test] + fn fails_on_invalid_storage_proof() { + let (_state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + run_test(|| { + // start with relay block #0 + initialize(Default::default()); + + // try to import head#5 of parachain#1 at relay chain block #0 + assert_noop!( + import_parachain_1_head(0, Default::default(), parachains, proof), + Error::::InvalidStorageProof + ); + }); + } + + #[test] + fn is_not_rewriting_existing_head_if_failed_to_read_updated_head() { + let (state_root_5, proof_5, parachains_5) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root_10_at_20, proof_10_at_20, parachains_10_at_20) = + prepare_parachain_heads_proof(vec![(2, head_data(2, 10))]); + let (state_root_10_at_30, proof_10_at_30, parachains_10_at_30) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); + run_test(|| { + // we've already imported head#5 of parachain#1 at relay block#10 + initialize(state_root_5); + import_parachain_1_head(0, state_root_5, parachains_5, proof_5).expect("ok"); + assert_eq!( + Pallet::::best_parachain_head(ParaId(1)), + Some(head_data(1, 5)) + ); + + // then if someone is pretending to provide updated head#10 of parachain#1 at relay + // block#20, but fails to do that + // + // => we'll leave previous value + proceed(20, state_root_10_at_20); + assert_ok!(Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (20, test_relay_header(20, state_root_10_at_20).hash()), + parachains_10_at_20, + proof_10_at_20, + ),); + assert_eq!( + Pallet::::best_parachain_head(ParaId(1)), + Some(head_data(1, 5)) + ); + + // then if someone is pretending to provide updated head#10 of parachain#1 at relay + // block#30, and actualy provides it + // + // => we'll update value + proceed(30, state_root_10_at_30); + assert_ok!(Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (30, test_relay_header(30, state_root_10_at_30).hash()), + parachains_10_at_30, + proof_10_at_30, + ),); + assert_eq!( + Pallet::::best_parachain_head(ParaId(1)), + Some(head_data(1, 10)) + ); + }); + } + + #[test] + fn storage_keys_computed_properly() { + assert_eq!( + ParasInfo::::storage_map_final_key(ParaId(42)).to_vec(), + ParasInfoKeyProvider::final_key("Parachains", &ParaId(42)).0 + ); + + assert_eq!( + ImportedParaHeads::::storage_double_map_final_key( + ParaId(42), + ParaHash::from([21u8; 32]) + ) + .to_vec(), + ImportedParaHeadsKeyProvider::final_key( + "Parachains", + &ParaId(42), + &ParaHash::from([21u8; 32]) + ) + .0, + ); + } + + #[test] + fn ignores_parachain_head_if_it_is_missing_from_storage_proof() { + let (state_root, proof, _) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + let parachains = vec![(ParaId(2), Default::default())]; + run_test(|| { + initialize(state_root); + assert_ok!(Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + )); + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::MissingParachainHead { + parachain: ParaId(2), + }), + topics: vec![], + }], + ); + }); + } + + #[test] + fn ignores_parachain_head_if_parachain_head_hash_is_wrong() { + let (state_root, proof, _) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + let parachains = vec![(ParaId(1), head_data(1, 10).hash())]; + run_test(|| { + initialize(state_root); + assert_ok!(Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + )); + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::IncorrectParachainHeadHash { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 10).hash(), + actual_parachain_head_hash: head_data(1, 0).hash(), + }), + topics: vec![], + }], + ); + }); + } + + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); +} diff --git a/modules/parachains/src/mock.rs b/modules/parachains/src/mock.rs new file mode 100644 index 00000000000..9638472aa98 --- /dev/null +++ b/modules/parachains/src/mock.rs @@ -0,0 +1,194 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use bp_polkadot_core::parachains::ParaId; +use bp_runtime::Chain; +use frame_support::{construct_runtime, parameter_types, traits::IsInVec, weights::Weight}; +use sp_runtime::{ + testing::{Header, H256}, + traits::{BlakeTwo256, Header as HeaderT, IdentityLookup}, + Perbill, +}; + +use crate as pallet_bridge_parachains; + +pub type AccountId = u64; +pub type TestNumber = u64; + +pub type RelayBlockHeader = + sp_runtime::generic::Header; + +type Block = frame_system::mocking::MockBlock; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + +pub const PARAS_PALLET_NAME: &str = "Paras"; +pub const UNTRACKED_PARACHAIN_ID: u32 = 10; +pub const MAXIMAL_PARACHAIN_HEAD_SIZE: u32 = 512; + +construct_runtime! { + pub enum TestRuntime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Grandpa1: pallet_bridge_grandpa::::{Pallet}, + Grandpa2: pallet_bridge_grandpa::::{Pallet}, + Parachains: pallet_bridge_parachains::{Call, Pallet, Event}, + } +} + +parameter_types! { + pub const BlockHashCount: TestNumber = 250; + pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} + +impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = TestNumber; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type DbWeight = (); + type BlockWeights = (); + type BlockLength = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const MaxRequests: u32 = 2; + pub const HeadersToKeep: u32 = 5; + pub const SessionLength: u64 = 5; + pub const NumValidators: u32 = 5; +} + +impl pallet_bridge_grandpa::Config for TestRuntime { + type BridgedChain = TestBridgedChain; + type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; + type MaxBridgedAuthorities = frame_support::traits::ConstU32<5>; + type MaxBridgedHeaderSize = frame_support::traits::ConstU32<512>; + type WeightInfo = (); +} + +impl pallet_bridge_grandpa::Config for TestRuntime { + type BridgedChain = TestBridgedChain; + type MaxRequests = MaxRequests; + type HeadersToKeep = HeadersToKeep; + type MaxBridgedAuthorities = frame_support::traits::ConstU32<5>; + type MaxBridgedHeaderSize = frame_support::traits::ConstU32<512>; + type WeightInfo = (); +} + +parameter_types! { + pub const HeadsToKeep: u32 = 4; + pub const ParasPalletName: &'static str = PARAS_PALLET_NAME; + pub GetTenFirstParachains: Vec = (0..10).map(ParaId).collect(); +} + +impl pallet_bridge_parachains::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; + type ParasPalletName = ParasPalletName; + type TrackedParachains = IsInVec; + type HeadsToKeep = HeadsToKeep; + type MaxParaHeadSize = frame_support::traits::ConstU32; +} + +#[derive(Debug)] +pub struct TestBridgedChain; + +impl Chain for TestBridgedChain { + type BlockNumber = crate::RelayBlockNumber; + type Hash = crate::RelayBlockHash; + type Hasher = crate::RelayBlockHasher; + type Header = RelayBlockHeader; + + type AccountId = AccountId; + type Balance = u32; + type Index = u32; + type Signature = sp_runtime::testing::TestSignature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + + fn max_extrinsic_weight() -> Weight { + unreachable!() + } +} + +#[derive(Debug)] +pub struct OtherBridgedChain; + +impl Chain for OtherBridgedChain { + type BlockNumber = u64; + type Hash = crate::RelayBlockHash; + type Hasher = crate::RelayBlockHasher; + type Header = sp_runtime::generic::Header; + + type AccountId = AccountId; + type Balance = u32; + type Index = u32; + type Signature = sp_runtime::testing::TestSignature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + + fn max_extrinsic_weight() -> Weight { + unreachable!() + } +} + +pub fn run_test(test: impl FnOnce() -> T) -> T { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + System::set_block_number(1); + System::reset_events(); + test() + }) +} + +pub fn test_relay_header( + num: crate::RelayBlockNumber, + state_root: crate::RelayBlockHash, +) -> RelayBlockHeader { + RelayBlockHeader::new( + num, + Default::default(), + state_root, + Default::default(), + Default::default(), + ) +} diff --git a/modules/parachains/src/weights.rs b/modules/parachains/src/weights.rs new file mode 100644 index 00000000000..5c9206d0ca0 --- /dev/null +++ b/modules/parachains/src/weights.rs @@ -0,0 +1,101 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Autogenerated weights for `pallet_bridge_parachains` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-11-17, STEPS: 50, REPEAT: 20 +//! LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled +//! CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/millau-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_bridge_parachains +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./modules/parachains/src/weights.rs +// --template=./.maintain/millau-weight-template.hbs + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for `pallet_bridge_parachains`. +pub trait WeightInfo { + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight; + fn submit_parachain_heads_with_1kb_proof() -> Weight; + fn submit_parachain_heads_with_16kb_proof() -> Weight; +} + +/// Weights for `pallet_bridge_parachains` that are generated using one of the Bridge testnets. +/// +/// Those weights are test only and must never be used in production. +pub struct BridgeWeight(PhantomData); +impl WeightInfo for BridgeWeight { + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { + Weight::from_ref_time(51_173_000 as u64) + .saturating_add(Weight::from_ref_time(24_495_968 as u64).saturating_mul(p as u64)) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().reads((2 as u64).saturating_mul(p as u64))) + .saturating_add(T::DbWeight::get().writes((3 as u64).saturating_mul(p as u64))) + } + fn submit_parachain_heads_with_1kb_proof() -> Weight { + Weight::from_ref_time(58_175_000 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + } + fn submit_parachain_heads_with_16kb_proof() -> Weight { + Weight::from_ref_time(101_796_000 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { + Weight::from_ref_time(51_173_000 as u64) + .saturating_add(Weight::from_ref_time(24_495_968 as u64).saturating_mul(p as u64)) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().reads((2 as u64).saturating_mul(p as u64))) + .saturating_add(RocksDbWeight::get().writes((3 as u64).saturating_mul(p as u64))) + } + fn submit_parachain_heads_with_1kb_proof() -> Weight { + Weight::from_ref_time(58_175_000 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) + } + fn submit_parachain_heads_with_16kb_proof() -> Weight { + Weight::from_ref_time(101_796_000 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) + } +} diff --git a/modules/parachains/src/weights_ext.rs b/modules/parachains/src/weights_ext.rs new file mode 100644 index 00000000000..eecdfe90359 --- /dev/null +++ b/modules/parachains/src/weights_ext.rs @@ -0,0 +1,107 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Weight-related utilities. + +use crate::weights::{BridgeWeight, WeightInfo}; + +use bp_runtime::Size; +use frame_support::weights::{RuntimeDbWeight, Weight}; + +/// Size of the regular parachain head. +/// +/// It's not that we are expecting all parachain heads to share the same size or that we would +/// reject all heads that have larger/lesser size. It is about head size that we use in benchmarks. +/// Relayer would need to pay additional fee for extra bytes. +/// +/// 384 is a bit larger (1.3 times) than the size of the randomly chosen Polkadot block. +pub const DEFAULT_PARACHAIN_HEAD_SIZE: u32 = 384; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// the Rialto chain. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// Extended weight info. +pub trait WeightInfoExt: WeightInfo { + /// Storage proof overhead, that is included in every storage proof. + /// + /// The relayer would pay some extra fee for additional proof bytes, since they mean + /// more hashing operations. + fn expected_extra_storage_proof_size() -> u32; + + /// Weight of the parachain heads delivery extrinsic. + fn submit_parachain_heads_weight( + db_weight: RuntimeDbWeight, + proof: &impl Size, + parachains_count: u32, + ) -> Weight { + // weight of the `submit_parachain_heads` with exactly `parachains_count` parachain + // heads of the default size (`DEFAULT_PARACHAIN_HEAD_SIZE`) + let base_weight = Self::submit_parachain_heads_with_n_parachains(parachains_count); + + // overhead because of extra storage proof bytes + let expected_proof_size = parachains_count + .saturating_mul(DEFAULT_PARACHAIN_HEAD_SIZE) + .saturating_add(Self::expected_extra_storage_proof_size()); + let actual_proof_size = proof.size(); + let proof_size_overhead = Self::storage_proof_size_overhead( + actual_proof_size.saturating_sub(expected_proof_size), + ); + + // potential pruning weight (refunded if hasn't happened) + let pruning_weight = + Self::parachain_head_pruning_weight(db_weight).saturating_mul(parachains_count as u64); + + base_weight.saturating_add(proof_size_overhead).saturating_add(pruning_weight) + } + + /// Returns weight of single parachain head storage update. + /// + /// This weight only includes db write operations that happens if parachain head is actually + /// updated. All extra weights (weight of storage proof validation, additional checks, ...) is + /// not included. + fn parachain_head_storage_write_weight(db_weight: RuntimeDbWeight) -> Weight { + // it's just a couple of operations - we need to write the hash (`ImportedParaHashes`) and + // the head itself (`ImportedParaHeads`. Pruning is not included here + db_weight.writes(2) + } + + /// Returns weight of single parachain head pruning. + fn parachain_head_pruning_weight(db_weight: RuntimeDbWeight) -> Weight { + // it's just one write operation, we don't want any benchmarks for that + db_weight.writes(1) + } + + /// Returns weight that needs to be accounted when storage proof of given size is received. + fn storage_proof_size_overhead(extra_proof_bytes: u32) -> Weight { + let extra_byte_weight = (Self::submit_parachain_heads_with_16kb_proof() - + Self::submit_parachain_heads_with_1kb_proof()) / + (15 * 1024); + extra_byte_weight.saturating_mul(extra_proof_bytes as u64) + } +} + +impl WeightInfoExt for () { + fn expected_extra_storage_proof_size() -> u32 { + EXTRA_STORAGE_PROOF_SIZE + } +} + +impl WeightInfoExt for BridgeWeight { + fn expected_extra_storage_proof_size() -> u32 { + EXTRA_STORAGE_PROOF_SIZE + } +} diff --git a/modules/relayers/Cargo.toml b/modules/relayers/Cargo.toml new file mode 100644 index 00000000000..00d1bb59120 --- /dev/null +++ b/modules/relayers/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "pallet-bridge-relayers" +description = "Module used to store relayer rewards and coordinate relayers set." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } + +# Bridge dependencies + +bp-messages = { path = "../../primitives/messages", default-features = false } +bp-relayers = { path = "../../primitives/relayers", default-features = false } +pallet-bridge-messages = { path = "../messages", default-features = false } + +# Substrate Dependencies + +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +bp-runtime = { path = "../../primitives/runtime" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "bp-messages/std", + "bp-relayers/std", + "codec/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-bridge-messages/std", + "scale-info/std", + "sp-arithmetic/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", +] diff --git a/modules/relayers/src/benchmarking.rs b/modules/relayers/src/benchmarking.rs new file mode 100644 index 00000000000..b79c0fb3ff0 --- /dev/null +++ b/modules/relayers/src/benchmarking.rs @@ -0,0 +1,41 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Benchmarks for the relayers Pallet. + +#![cfg(feature = "runtime-benchmarks")] + +use crate::*; + +use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_system::RawOrigin; + +/// Reward amount that is (hopefully) is larger than existential deposit across all chains. +const REWARD_AMOUNT: u32 = u32::MAX; + +benchmarks! { + // Benchmark `claim_rewards` call. + claim_rewards { + let lane = [0, 0, 0, 0]; + let relayer: T::AccountId = whitelisted_caller(); + RelayerRewards::::insert(&relayer, lane, T::Reward::from(REWARD_AMOUNT)); + }: _(RawOrigin::Signed(relayer), lane) + verify { + // we can't check anything here, because `PaymentProcedure` is responsible for + // payment logic, so we assume that if call has succeeded, the procedure has + // also completed successfully + } +} diff --git a/modules/relayers/src/lib.rs b/modules/relayers/src/lib.rs new file mode 100644 index 00000000000..921ec0e4d98 --- /dev/null +++ b/modules/relayers/src/lib.rs @@ -0,0 +1,247 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Runtime module that is used to store relayer rewards and (in the future) to +//! coordinate relations between relayers. + +#![cfg_attr(not(feature = "std"), no_std)] +#![warn(missing_docs)] + +use bp_messages::LaneId; +use bp_relayers::PaymentProcedure; +use frame_support::sp_runtime::Saturating; +use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero}; +use sp_std::marker::PhantomData; +use weights::WeightInfo; + +pub use pallet::*; +pub use payment_adapter::MessageDeliveryAndDispatchPaymentAdapter; + +mod benchmarking; +mod mock; +mod payment_adapter; + +pub mod weights; + +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-relayers"; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Type of relayer reward. + type Reward: AtLeast32BitUnsigned + Copy + Parameter + MaxEncodedLen; + /// Pay rewards adapter. + type PaymentProcedure: PaymentProcedure; + /// Pallet call weights. + type WeightInfo: WeightInfo; + } + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(PhantomData); + + #[pallet::call] + impl Pallet { + /// Claim accumulated rewards. + #[pallet::weight(T::WeightInfo::claim_rewards())] + pub fn claim_rewards(origin: OriginFor, lane_id: LaneId) -> DispatchResult { + let relayer = ensure_signed(origin)?; + + RelayerRewards::::try_mutate_exists( + &relayer, + lane_id, + |maybe_reward| -> DispatchResult { + let reward = maybe_reward.take().ok_or(Error::::NoRewardForRelayer)?; + T::PaymentProcedure::pay_reward(&relayer, lane_id, reward).map_err(|e| { + log::trace!( + target: LOG_TARGET, + "Failed to pay {:?} rewards to {:?}: {:?}", + lane_id, + relayer, + e, + ); + Error::::FailedToPayReward + })?; + + Self::deposit_event(Event::::RewardPaid { + relayer: relayer.clone(), + lane_id, + reward, + }); + Ok(()) + }, + ) + } + } + + impl Pallet { + /// Register reward for given relayer. + pub fn register_relayer_reward(lane_id: LaneId, relayer: &T::AccountId, reward: T::Reward) { + if reward.is_zero() { + return + } + + RelayerRewards::::mutate(relayer, lane_id, |old_reward: &mut Option| { + let new_reward = old_reward.unwrap_or_else(Zero::zero).saturating_add(reward); + *old_reward = Some(new_reward); + + log::trace!( + target: crate::LOG_TARGET, + "Relayer {:?} can now claim reward: {:?}", + relayer, + new_reward, + ); + }); + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Reward has been paid to the relayer. + RewardPaid { + /// Relayer account that has been rewarded. + relayer: T::AccountId, + /// Relayer has received reward for serving this lane. + lane_id: LaneId, + /// Reward amount. + reward: T::Reward, + }, + } + + #[pallet::error] + pub enum Error { + /// No reward can be claimed by given relayer. + NoRewardForRelayer, + /// Reward payment procedure has failed. + FailedToPayReward, + } + + /// Map of the relayer => accumulated reward. + #[pallet::storage] + pub type RelayerRewards = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, + Identity, + LaneId, + T::Reward, + OptionQuery, + >; +} + +#[cfg(test)] +mod tests { + use super::*; + use mock::{RuntimeEvent as TestEvent, *}; + + use crate::Event::RewardPaid; + use frame_support::{assert_noop, assert_ok, traits::fungible::Inspect}; + use frame_system::{EventRecord, Pallet as System, Phase}; + use sp_runtime::DispatchError; + + fn get_ready_for_events() { + System::::set_block_number(1); + System::::reset_events(); + } + + #[test] + fn root_cant_claim_anything() { + run_test(|| { + assert_noop!( + Pallet::::claim_rewards(RuntimeOrigin::root(), TEST_LANE_ID), + DispatchError::BadOrigin, + ); + }); + } + + #[test] + fn relayer_cant_claim_if_no_reward_exists() { + run_test(|| { + assert_noop!( + Pallet::::claim_rewards( + RuntimeOrigin::signed(REGULAR_RELAYER), + TEST_LANE_ID + ), + Error::::NoRewardForRelayer, + ); + }); + } + + #[test] + fn relayer_cant_claim_if_payment_procedure_fails() { + run_test(|| { + RelayerRewards::::insert(FAILING_RELAYER, TEST_LANE_ID, 100); + assert_noop!( + Pallet::::claim_rewards( + RuntimeOrigin::signed(FAILING_RELAYER), + TEST_LANE_ID + ), + Error::::FailedToPayReward, + ); + }); + } + + #[test] + fn relayer_can_claim_reward() { + run_test(|| { + get_ready_for_events(); + + RelayerRewards::::insert(REGULAR_RELAYER, TEST_LANE_ID, 100); + assert_ok!(Pallet::::claim_rewards( + RuntimeOrigin::signed(REGULAR_RELAYER), + TEST_LANE_ID + )); + assert_eq!(RelayerRewards::::get(REGULAR_RELAYER, TEST_LANE_ID), None); + + //Check if the `RewardPaid` event was emitted. + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Relayers(RewardPaid { + relayer: REGULAR_RELAYER, + lane_id: TEST_LANE_ID, + reward: 100 + }), + topics: vec![], + }], + ); + }); + } + + #[test] + fn mint_reward_payment_procedure_actually_mints_tokens() { + type Balances = pallet_balances::Pallet; + + run_test(|| { + assert_eq!(Balances::balance(&1), 0); + assert_eq!(Balances::total_issuance(), 0); + bp_relayers::MintReward::::pay_reward(&1, TEST_LANE_ID, 100) + .unwrap(); + assert_eq!(Balances::balance(&1), 100); + assert_eq!(Balances::total_issuance(), 100); + }); + } +} diff --git a/modules/relayers/src/mock.rs b/modules/relayers/src/mock.rs new file mode 100644 index 00000000000..55db2d575e7 --- /dev/null +++ b/modules/relayers/src/mock.rs @@ -0,0 +1,161 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg(test)] + +use crate as pallet_bridge_relayers; + +use bp_messages::{ + source_chain::ForbidOutboundMessages, target_chain::ForbidInboundMessages, LaneId, +}; +use bp_relayers::PaymentProcedure; +use frame_support::{parameter_types, weights::RuntimeDbWeight}; +use sp_core::H256; +use sp_runtime::{ + testing::Header as SubstrateHeader, + traits::{BlakeTwo256, IdentityLookup}, +}; + +pub type AccountId = u64; +pub type Balance = u64; + +type Block = frame_system::mocking::MockBlock; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + +frame_support::construct_runtime! { + pub enum TestRuntime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Event}, + Messages: pallet_bridge_messages::{Pallet, Event}, + Relayers: pallet_bridge_relayers::{Pallet, Call, Event}, + } +} + +parameter_types! { + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; +} + +impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = SubstrateHeader; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = frame_support::traits::ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type DbWeight = DbWeight; + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for TestRuntime { + type MaxLocks = (); + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = frame_support::traits::ConstU64<1>; + type AccountStore = frame_system::Pallet; + type WeightInfo = (); + type MaxReserves = (); + type ReserveIdentifier = (); +} + +parameter_types! { + pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; + pub ActiveOutboundLanes: &'static [bp_messages::LaneId] = &[[0, 0, 0, 0]]; +} + +// we're not testing messages pallet here, so values in this config might be crazy +impl pallet_bridge_messages::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type ActiveOutboundLanes = ActiveOutboundLanes; + type MaxUnrewardedRelayerEntriesAtInboundLane = frame_support::traits::ConstU64<8>; + type MaxUnconfirmedMessagesAtInboundLane = frame_support::traits::ConstU64<8>; + + type MaximalOutboundPayloadSize = frame_support::traits::ConstU32<1024>; + type OutboundPayload = (); + + type InboundPayload = (); + type InboundRelayer = AccountId; + + type TargetHeaderChain = ForbidOutboundMessages; + type LaneMessageVerifier = ForbidOutboundMessages; + type MessageDeliveryAndDispatchPayment = (); + + type SourceHeaderChain = ForbidInboundMessages; + type MessageDispatch = ForbidInboundMessages; + type BridgedChainId = TestBridgedChainId; +} + +impl pallet_bridge_relayers::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type Reward = Balance; + type PaymentProcedure = TestPaymentProcedure; + type WeightInfo = (); +} + +/// Message lane that we're using in tests. +pub const TEST_LANE_ID: LaneId = [0, 0, 0, 0]; + +/// Regular relayer that may receive rewards. +pub const REGULAR_RELAYER: AccountId = 1; + +/// Relayer that can't receive rewards. +pub const FAILING_RELAYER: AccountId = 2; + +/// Payment procedure that rejects payments to the `FAILING_RELAYER`. +pub struct TestPaymentProcedure; + +impl PaymentProcedure for TestPaymentProcedure { + type Error = (); + + fn pay_reward( + relayer: &AccountId, + _lane_id: LaneId, + _reward: Balance, + ) -> Result<(), Self::Error> { + match *relayer { + FAILING_RELAYER => Err(()), + _ => Ok(()), + } + } +} + +/// Run pallet test. +pub fn run_test(test: impl FnOnce() -> T) -> T { + let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(test) +} diff --git a/modules/relayers/src/payment_adapter.rs b/modules/relayers/src/payment_adapter.rs new file mode 100644 index 00000000000..4939d274c60 --- /dev/null +++ b/modules/relayers/src/payment_adapter.rs @@ -0,0 +1,171 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Code that allows relayers pallet to be used as a delivery+dispatch payment mechanism +//! for the messages pallet. + +use crate::{Config, Pallet}; + +use bp_messages::source_chain::{MessageDeliveryAndDispatchPayment, RelayersRewards}; +use frame_support::sp_runtime::SaturatedConversion; +use sp_arithmetic::traits::{Saturating, UniqueSaturatedFrom, Zero}; +use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive}; + +/// Adapter that allows relayers pallet to be used as a delivery+dispatch payment mechanism +/// for the messages pallet. +pub struct MessageDeliveryAndDispatchPaymentAdapter( + PhantomData<(T, MessagesInstance)>, +); + +impl MessageDeliveryAndDispatchPayment + for MessageDeliveryAndDispatchPaymentAdapter +where + T: Config + pallet_bridge_messages::Config, + MessagesInstance: 'static, +{ + type Error = &'static str; + + fn pay_relayers_rewards( + lane_id: bp_messages::LaneId, + messages_relayers: VecDeque>, + confirmation_relayer: &T::AccountId, + received_range: &RangeInclusive, + ) { + let relayers_rewards = pallet_bridge_messages::calc_relayers_rewards::( + messages_relayers, + received_range, + ); + + register_relayers_rewards::( + confirmation_relayer, + relayers_rewards, + lane_id, + // TODO (https://github.com/paritytech/parity-bridges-common/issues/1318): this shall be fixed + // in some way. ATM the future of the `register_relayer_reward` is not yet known + 100_000_u32.into(), + 10_000_u32.into(), + ); + } +} + +// Update rewards to given relayers, optionally rewarding confirmation relayer. +fn register_relayers_rewards( + confirmation_relayer: &T::AccountId, + relayers_rewards: RelayersRewards, + lane_id: bp_messages::LaneId, + delivery_fee: T::Reward, + confirmation_fee: T::Reward, +) { + // reward every relayer except `confirmation_relayer` + let mut confirmation_relayer_reward = T::Reward::zero(); + for (relayer, messages) in relayers_rewards { + // sane runtime configurations guarantee that the number of messages will be below + // `u32::MAX` + let mut relayer_reward = + T::Reward::unique_saturated_from(messages).saturating_mul(delivery_fee); + + if relayer != *confirmation_relayer { + // If delivery confirmation is submitted by other relayer, let's deduct confirmation fee + // from relayer reward. + // + // If confirmation fee has been increased (or if it was the only component of message + // fee), then messages relayer may receive zero reward. + let mut confirmation_reward = + T::Reward::saturated_from(messages).saturating_mul(confirmation_fee); + confirmation_reward = sp_std::cmp::min(confirmation_reward, relayer_reward); + relayer_reward = relayer_reward.saturating_sub(confirmation_reward); + confirmation_relayer_reward = + confirmation_relayer_reward.saturating_add(confirmation_reward); + Pallet::::register_relayer_reward(lane_id, &relayer, relayer_reward); + } else { + // If delivery confirmation is submitted by this relayer, let's add confirmation fee + // from other relayers to this relayer reward. + confirmation_relayer_reward = + confirmation_relayer_reward.saturating_add(relayer_reward); + } + } + + // finally - pay reward to confirmation relayer + Pallet::::register_relayer_reward( + lane_id, + confirmation_relayer, + confirmation_relayer_reward, + ); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{mock::*, RelayerRewards}; + + const RELAYER_1: AccountId = 1; + const RELAYER_2: AccountId = 2; + const RELAYER_3: AccountId = 3; + + fn relayers_rewards() -> RelayersRewards { + vec![(RELAYER_1, 2), (RELAYER_2, 3)].into_iter().collect() + } + + #[test] + fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() { + run_test(|| { + register_relayers_rewards::( + &RELAYER_2, + relayers_rewards(), + TEST_LANE_ID, + 50, + 10, + ); + + assert_eq!(RelayerRewards::::get(RELAYER_1, TEST_LANE_ID), Some(80)); + assert_eq!(RelayerRewards::::get(RELAYER_2, TEST_LANE_ID), Some(170)); + }); + } + + #[test] + fn confirmation_relayer_is_rewarded_if_it_has_not_delivered_any_delivered_messages() { + run_test(|| { + register_relayers_rewards::( + &RELAYER_3, + relayers_rewards(), + TEST_LANE_ID, + 50, + 10, + ); + + assert_eq!(RelayerRewards::::get(RELAYER_1, TEST_LANE_ID), Some(80)); + assert_eq!(RelayerRewards::::get(RELAYER_2, TEST_LANE_ID), Some(120)); + assert_eq!(RelayerRewards::::get(RELAYER_3, TEST_LANE_ID), Some(50)); + }); + } + + #[test] + fn only_confirmation_relayer_is_rewarded_if_confirmation_fee_has_significantly_increased() { + run_test(|| { + register_relayers_rewards::( + &RELAYER_3, + relayers_rewards(), + TEST_LANE_ID, + 50, + 1000, + ); + + assert_eq!(RelayerRewards::::get(RELAYER_1, TEST_LANE_ID), None); + assert_eq!(RelayerRewards::::get(RELAYER_2, TEST_LANE_ID), None); + assert_eq!(RelayerRewards::::get(RELAYER_3, TEST_LANE_ID), Some(250)); + }); + } +} diff --git a/modules/relayers/src/weights.rs b/modules/relayers/src/weights.rs new file mode 100644 index 00000000000..7bcd8711a9e --- /dev/null +++ b/modules/relayers/src/weights.rs @@ -0,0 +1,75 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Autogenerated weights for `pallet_bridge_relayers` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-11-17, STEPS: 50, REPEAT: 20 +//! LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled +//! CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/millau-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_bridge_relayers +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./modules/relayers/src/weights.rs +// --template=./.maintain/millau-weight-template.hbs + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for `pallet_bridge_relayers`. +pub trait WeightInfo { + fn claim_rewards() -> Weight; +} + +/// Weights for `pallet_bridge_relayers` that are generated using one of the Bridge testnets. +/// +/// Those weights are test only and must never be used in production. +pub struct BridgeWeight(PhantomData); +impl WeightInfo for BridgeWeight { + fn claim_rewards() -> Weight { + Weight::from_ref_time(59_334_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn claim_rewards() -> Weight { + Weight::from_ref_time(59_334_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } +} diff --git a/modules/shift-session-manager/Cargo.toml b/modules/shift-session-manager/Cargo.toml new file mode 100644 index 00000000000..5dae3e00fd3 --- /dev/null +++ b/modules/shift-session-manager/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "pallet-shift-session-manager" +description = "A Substrate Runtime module that selects 2/3 of initial validators for every session" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "pallet-session/std", + "scale-info/std", + "sp-staking/std", + "sp-std/std", +] diff --git a/modules/shift-session-manager/src/lib.rs b/modules/shift-session-manager/src/lib.rs new file mode 100644 index 00000000000..073013d92d0 --- /dev/null +++ b/modules/shift-session-manager/src/lib.rs @@ -0,0 +1,268 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate session manager that selects 2/3 validators from initial set, +//! starting from session 2. + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::traits::{ValidatorSet, ValidatorSetWithIdentification}; +use sp_std::prelude::*; + +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + #[pallet::disable_frame_system_supertrait_check] + pub trait Config: pallet_session::Config {} + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + #[pallet::without_storage_info] + pub struct Pallet(PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet {} + + /// Validators of first two sessions. + #[pallet::storage] + pub(super) type InitialValidators = StorageValue<_, Vec>; +} + +impl ValidatorSet for Pallet { + type ValidatorId = T::ValidatorId; + type ValidatorIdOf = T::ValidatorIdOf; + + fn session_index() -> sp_staking::SessionIndex { + pallet_session::Pallet::::current_index() + } + + fn validators() -> Vec { + pallet_session::Pallet::::validators() + } +} + +impl ValidatorSetWithIdentification for Pallet { + type Identification = (); + type IdentificationOf = (); +} + +impl pallet_session::SessionManager for Pallet { + fn end_session(_: sp_staking::SessionIndex) {} + fn start_session(_: sp_staking::SessionIndex) {} + fn new_session(session_index: sp_staking::SessionIndex) -> Option> { + // we don't want to add even more fields to genesis config => just return None + if session_index == 0 || session_index == 1 { + return None + } + + // the idea that on first call (i.e. when session 1 ends) we're reading current + // set of validators from session module (they are initial validators) and save + // in our 'local storage'. + // then for every session we select (deterministically) 2/3 of these initial + // validators to serve validators of new session + let available_validators = InitialValidators::::get().unwrap_or_else(|| { + let validators = >::validators(); + InitialValidators::::put(validators.clone()); + validators + }); + + Some(Self::select_validators(session_index, &available_validators)) + } +} + +impl Pallet { + /// Select validators for session. + fn select_validators( + session_index: sp_staking::SessionIndex, + available_validators: &[T::ValidatorId], + ) -> Vec { + let available_validators_count = available_validators.len(); + let count = sp_std::cmp::max(1, 2 * available_validators_count / 3); + let offset = session_index as usize % available_validators_count; + let end = offset + count; + let session_validators = match end.overflowing_sub(available_validators_count) { + (wrapped_end, false) if wrapped_end != 0 => available_validators[offset..] + .iter() + .chain(available_validators[..wrapped_end].iter()) + .cloned() + .collect(), + _ => available_validators[offset..end].to_vec(), + }; + + session_validators + } +} + +#[cfg(test)] +mod tests { + // From construct_runtime macro + #![allow(clippy::from_over_into)] + + use super::*; + use frame_support::{ + parameter_types, + sp_io::TestExternalities, + sp_runtime::{ + testing::{Header, UintAuthorityId}, + traits::{BlakeTwo256, ConvertInto, IdentityLookup}, + Perbill, RuntimeAppPublic, + }, + traits::GenesisBuild, + weights::Weight, + BasicExternalities, + }; + use sp_core::H256; + + type AccountId = u64; + + type Block = frame_system::mocking::MockBlock; + type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + + frame_support::construct_runtime! { + pub enum TestRuntime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Session: pallet_session::{Pallet}, + } + } + + parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + } + + impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = (); + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + } + + parameter_types! { + pub const Period: u64 = 1; + pub const Offset: u64 = 0; + } + + impl pallet_session::Config for TestRuntime { + type RuntimeEvent = (); + type ValidatorId = ::AccountId; + type ValidatorIdOf = ConvertInto; + type ShouldEndSession = pallet_session::PeriodicSessions; + type NextSessionRotation = pallet_session::PeriodicSessions; + type SessionManager = (); + type SessionHandler = TestSessionHandler; + type Keys = UintAuthorityId; + type WeightInfo = (); + } + + impl Config for TestRuntime {} + + pub struct TestSessionHandler; + impl pallet_session::SessionHandler for TestSessionHandler { + const KEY_TYPE_IDS: &'static [sp_runtime::KeyTypeId] = &[UintAuthorityId::ID]; + + fn on_genesis_session(_validators: &[(AccountId, Ks)]) { + } + + fn on_new_session( + _: bool, + _: &[(AccountId, Ks)], + _: &[(AccountId, Ks)], + ) { + } + + fn on_disabled(_: u32) {} + } + + fn new_test_ext() -> TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + let keys = vec![ + (1, 1, UintAuthorityId(1)), + (2, 2, UintAuthorityId(2)), + (3, 3, UintAuthorityId(3)), + (4, 4, UintAuthorityId(4)), + (5, 5, UintAuthorityId(5)), + ]; + + BasicExternalities::execute_with_storage(&mut t, || { + for (ref k, ..) in &keys { + frame_system::Pallet::::inc_providers(k); + } + }); + + pallet_session::GenesisConfig:: { keys } + .assimilate_storage(&mut t) + .unwrap(); + TestExternalities::new(t) + } + + #[test] + fn shift_session_manager_works() { + new_test_ext().execute_with(|| { + let all_accs = vec![1, 2, 3, 4, 5]; + + // at least 1 validator is selected + assert_eq!(Pallet::::select_validators(0, &[1]), vec![1],); + + // at session#0, shift is also 0 + assert_eq!(Pallet::::select_validators(0, &all_accs), vec![1, 2, 3],); + + // at session#1, shift is also 1 + assert_eq!(Pallet::::select_validators(1, &all_accs), vec![2, 3, 4],); + + // at session#3, we're wrapping + assert_eq!(Pallet::::select_validators(3, &all_accs), vec![4, 5, 1],); + + // at session#5, we're starting from the beginning again + assert_eq!(Pallet::::select_validators(5, &all_accs), vec![1, 2, 3],); + }); + } +} diff --git a/primitives/beefy/Cargo.toml b/primitives/beefy/Cargo.toml new file mode 100644 index 00000000000..6b169e66113 --- /dev/null +++ b/primitives/beefy/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "bp-beefy" +description = "Primitives of pallet-bridge-beefy module." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "bit-vec"] } +scale-info = { version = "2.0.1", default-features = false, features = ["bit-vec", "derive"] } +serde = { version = "1.0", optional = true } +static_assertions = "1.1" + +# Bridge Dependencies + +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Dependencies + +beefy-merkle-tree = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "beefy-primitives/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "pallet-beefy-mmr/std", + "pallet-mmr/std", + "scale-info/std", + "serde", + "sp-application-crypto/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std" +] diff --git a/primitives/beefy/src/lib.rs b/primitives/beefy/src/lib.rs new file mode 100644 index 00000000000..a0a096bdce1 --- /dev/null +++ b/primitives/beefy/src/lib.rs @@ -0,0 +1,152 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives that are used to interact with BEEFY bridge pallet. + +#![cfg_attr(not(feature = "std"), no_std)] +#![warn(missing_docs)] + +pub use beefy_merkle_tree::{merkle_root, Keccak256 as BeefyKeccak256}; +pub use beefy_primitives::{ + crypto::{AuthorityId as EcdsaValidatorId, AuthoritySignature as EcdsaValidatorSignature}, + known_payloads::MMR_ROOT_ID as MMR_ROOT_PAYLOAD_ID, + mmr::{BeefyAuthoritySet, MmrLeafVersion}, + BeefyAuthorityId, BeefyVerify, Commitment, Payload as BeefyPayload, SignedCommitment, + ValidatorSet, ValidatorSetId, BEEFY_ENGINE_ID, +}; +pub use pallet_beefy_mmr::BeefyEcdsaToEthereum; +pub use pallet_mmr::{ + primitives::{DataOrHash as MmrDataOrHash, Proof as MmrProof}, + verify_leaves_proof as verify_mmr_leaves_proof, +}; + +use bp_runtime::{BasicOperatingMode, BlockNumberOf, Chain, HashOf}; +use codec::{Decode, Encode}; +use frame_support::Parameter; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{Convert, MaybeSerializeDeserialize}, + RuntimeDebug, +}; +use sp_std::prelude::*; + +/// Substrate-based chain with BEEFY && MMR pallets deployed. +/// +/// Both BEEFY and MMR pallets and their clients may be configured to use different +/// primitives. Some of types can be configured in low-level pallets, but are constrained +/// when BEEFY+MMR bundle is used. +pub trait ChainWithBeefy: Chain { + /// The hashing algorithm used to compute the digest of the BEEFY commitment. + /// + /// Corresponds to the hashing algorithm, used by `beefy_gadget::BeefyKeystore`. + type CommitmentHasher: sp_runtime::traits::Hash; + + /// The hashing algorithm used to build the MMR. + /// + /// The same algorithm is also used to compute merkle roots in BEEFY + /// (e.g. validator addresses root in leaf data). + /// + /// Corresponds to the `Hashing` field of the `pallet-mmr` configuration. + type MmrHashing: sp_runtime::traits::Hash; + + /// The output type of the hashing algorithm used to build the MMR. + /// + /// This type is actually stored in the MMR. + + /// Corresponds to the `Hash` field of the `pallet-mmr` configuration. + type MmrHash: sp_std::hash::Hash + + Parameter + + Copy + + AsRef<[u8]> + + Default + + MaybeSerializeDeserialize; + + /// The type expected for the MMR leaf extra data. + type BeefyMmrLeafExtra: Parameter; + + /// A way to identify a BEEFY validator. + /// + /// Corresponds to the `BeefyId` field of the `pallet-beefy` configuration. + type AuthorityId: BeefyAuthorityId + Parameter; + + /// The signature type used by BEEFY. + /// + /// Corresponds to the `BeefyId` field of the `pallet-beefy` configuration. + type Signature: BeefyVerify + Parameter; + + /// A way to convert validator id to its raw representation in the BEEFY merkle tree. + /// + /// Corresponds to the `BeefyAuthorityToMerkleLeaf` field of the `pallet-beefy-mmr` + /// configuration. + type AuthorityIdToMerkleLeaf: Convert>; +} + +/// BEEFY validator id used by given Substrate chain. +pub type BeefyAuthorityIdOf = ::AuthorityId; +/// BEEFY validator set, containing both validator identifiers and the numeric set id. +pub type BeefyAuthoritySetOf = ValidatorSet>; +/// BEEFY authority set, containing both validator identifiers and the numeric set id. +pub type BeefyAuthoritySetInfoOf = beefy_primitives::mmr::BeefyAuthoritySet>; +/// BEEFY validator signature used by given Substrate chain. +pub type BeefyValidatorSignatureOf = ::Signature; +/// Signed BEEFY commitment used by given Substrate chain. +pub type BeefySignedCommitmentOf = + SignedCommitment, BeefyValidatorSignatureOf>; +/// Hash algorithm, used to compute the digest of the BEEFY commitment before signing it. +pub type BeefyCommitmentHasher = ::CommitmentHasher; +/// Hash algorithm used in Beefy MMR construction by given Substrate chain. +pub type MmrHashingOf = ::MmrHashing; +/// Hash type, used in MMR construction by given Substrate chain. +pub type MmrHashOf = ::MmrHash; +/// BEEFY MMR proof type used by the given Substrate chain. +pub type MmrProofOf = MmrProof>; +/// The type of the MMR leaf extra data used by the given Substrate chain. +pub type BeefyMmrLeafExtraOf = ::BeefyMmrLeafExtra; +/// A way to convert a validator id to its raw representation in the BEEFY merkle tree, used by +/// the given Substrate chain. +pub type BeefyAuthorityIdToMerkleLeafOf = ::AuthorityIdToMerkleLeaf; +/// Actual type of leafs in the BEEFY MMR. +pub type BeefyMmrLeafOf = beefy_primitives::mmr::MmrLeaf< + BlockNumberOf, + HashOf, + MmrHashOf, + BeefyMmrLeafExtraOf, +>; + +/// Data required for initializing the BEEFY pallet. +/// +/// Provides the initial context that the bridge needs in order to know +/// where to start the sync process from. +#[derive(Encode, Decode, RuntimeDebug, PartialEq, Clone, TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct InitializationData { + /// Pallet operating mode. + pub operating_mode: BasicOperatingMode, + /// Number of the best block, finalized by BEEFY. + pub best_block_number: BlockNumber, + /// BEEFY authority set that will be finalizing descendants of the `best_beefy_block_number` + /// block. + pub authority_set: BeefyAuthoritySet, +} + +/// Basic data, stored by the pallet for every imported commitment. +#[derive(Encode, Decode, RuntimeDebug, PartialEq, TypeInfo)] +pub struct ImportedCommitment { + /// Block number and hash of the finalized block parent. + pub parent_number_and_hash: (BlockNumber, BlockHash), + /// MMR root at the imported block. + pub mmr_root: MmrHash, +} diff --git a/primitives/chain-bridge-hub-rococo/Cargo.toml b/primitives/chain-bridge-hub-rococo/Cargo.toml new file mode 100644 index 00000000000..51bc88baf0e --- /dev/null +++ b/primitives/chain-bridge-hub-rococo/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "bp-bridge-hub-rococo" +description = "Primitives of BridgeHubRococo parachain runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +smallvec = "1.10.0" + +# Bridge Dependencies + +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +# Polkadot Dependencies +polkadot-runtime-constants = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } + +[features] +default = ["std"] +std = [ + "bp-polkadot-core/std", + "bp-messages/std", + "bp-runtime/std", + "frame-support/std", + "sp-api/std", + "sp-std/std", + "polkadot-runtime-constants/std", +] diff --git a/primitives/chain-bridge-hub-rococo/src/lib.rs b/primitives/chain-bridge-hub-rococo/src/lib.rs new file mode 100644 index 00000000000..aa65f378542 --- /dev/null +++ b/primitives/chain-bridge-hub-rococo/src/lib.rs @@ -0,0 +1,107 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Module with configuration which reflects BridgeHubRococo runtime setup (AccountId, Headers, +//! Hashes...) + +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_messages::*; +pub use bp_polkadot_core::*; +use bp_runtime::{ + decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, Parachain, +}; +use frame_support::{ + dispatch::DispatchClass, + parameter_types, + sp_runtime::{MultiAddress, MultiSigner}, + weights::{ + constants::ExtrinsicBaseWeight, WeightToFeeCoefficient, WeightToFeeCoefficients, + WeightToFeePolynomial, + }, + RuntimeDebug, +}; +use sp_std::prelude::*; + +/// BridgeHubRococo parachain. +#[derive(RuntimeDebug)] +pub struct BridgeHubRococo; + +impl Chain for BridgeHubRococo { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +impl Parachain for BridgeHubRococo { + const PARACHAIN_ID: u32 = BRIDGE_HUB_ROCOCO_PARACHAIN_ID; +} + +/// [`WeightToFee`] should reflect cumulus/bridge-hub-rococo-runtime [`WeightToFee`] +pub struct WeightToFee; +impl WeightToFeePolynomial for WeightToFee { + type Balance = Balance; + fn polynomial() -> WeightToFeeCoefficients { + pub const CENTS: Balance = polkadot_runtime_constants::currency::CENTS; + + // in Rococo, extrinsic base weight (smallest non-zero weight) is mapped to 1/10 CENT: + // in BridgeHub, we map to 1/10 of that, or 1/100 CENT + let p = CENTS; + let q = 100 * Balance::from(ExtrinsicBaseWeight::get().ref_time()); + smallvec::smallvec![WeightToFeeCoefficient { + degree: 1, + negative: false, + coeff_frac: Perbill::from_rational(p % q, q), + coeff_integer: p / q, + }] + } +} + +/// Public key of the chain account that may be used to verify signatures. +pub type AccountSigner = MultiSigner; + +/// The address format for describing accounts. +pub type Address = MultiAddress; + +/// Identifier of BridgeHubRococo in the Rococo relay chain. +pub const BRIDGE_HUB_ROCOCO_PARACHAIN_ID: u32 = 1013; + +/// Name of the With-BridgeHubRococo messages pallet instance that is deployed at bridged chains. +pub const WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME: &str = "BridgeRococoMessages"; + +parameter_types! { + pub const SS58Prefix: u16 = 42; +} + +decl_bridge_finality_runtime_apis!(bridge_hub_rococo); +decl_bridge_messages_runtime_apis!(bridge_hub_rococo); diff --git a/primitives/chain-bridge-hub-wococo/Cargo.toml b/primitives/chain-bridge-hub-wococo/Cargo.toml new file mode 100644 index 00000000000..94ac501fbd5 --- /dev/null +++ b/primitives/chain-bridge-hub-wococo/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "bp-bridge-hub-wococo" +description = "Primitives of BridgeHubWococo parachain runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-bridge-hub-rococo = { path = "../chain-bridge-hub-rococo", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-runtime/std", + "bp-messages/std", + "frame-support/std", + "sp-api/std", + "sp-std/std", + "bp-bridge-hub-rococo/std", +] diff --git a/primitives/chain-bridge-hub-wococo/src/lib.rs b/primitives/chain-bridge-hub-wococo/src/lib.rs new file mode 100644 index 00000000000..a2c3f2ad37a --- /dev/null +++ b/primitives/chain-bridge-hub-wococo/src/lib.rs @@ -0,0 +1,78 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Module with configuration which reflects BridgeHubWococo runtime setup +//! (AccountId, Headers, Hashes...) +//! +//! but actually this is just reexported BridgeHubRococo stuff, because they are supposed to be +//! identical, at least uses the same parachain runtime + +#![cfg_attr(not(feature = "std"), no_std)] + +// Re-export only what is really needed +pub use bp_bridge_hub_rococo::{ + AccountId, AccountInfoStorageMapKeyProvider, AccountPublic, AccountSigner, Address, Balance, + BlockLength, BlockNumber, BlockWeights, Hash, Hasher, Hashing, Header, Index, Nonce, + SS58Prefix, Signature, SignedBlock, SignedExtensions, UncheckedExtrinsic, WeightToFee, + MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + TX_EXTRA_BYTES, +}; +use bp_messages::*; +use bp_runtime::{ + decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, Parachain, +}; +use frame_support::{dispatch::DispatchClass, RuntimeDebug}; +use sp_std::prelude::*; + +/// BridgeHubWococo parachain. +#[derive(RuntimeDebug)] +pub struct BridgeHubWococo; + +impl Chain for BridgeHubWococo { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +impl Parachain for BridgeHubWococo { + const PARACHAIN_ID: u32 = BRIDGE_HUB_WOCOCO_PARACHAIN_ID; +} + +/// Identifier of BridgeHubWococo in the Wococo relay chain. +pub const BRIDGE_HUB_WOCOCO_PARACHAIN_ID: u32 = 1014; + +/// Name of the With-BridgeHubWococo messages pallet instance that is deployed at bridged chains. +pub const WITH_BRIDGE_HUB_WOCOCO_MESSAGES_PALLET_NAME: &str = "BridgeWococoMessages"; + +decl_bridge_finality_runtime_apis!(bridge_hub_wococo); +decl_bridge_messages_runtime_apis!(bridge_hub_wococo); diff --git a/primitives/chain-kusama/Cargo.toml b/primitives/chain-kusama/Cargo.toml new file mode 100644 index 00000000000..3bca5f4c3f0 --- /dev/null +++ b/primitives/chain-kusama/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "bp-kusama" +description = "Primitives of Kusama runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-polkadot-core = { path = "../polkadot-core", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies + +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-polkadot-core/std", + "bp-runtime/std", + "sp-api/std", +] diff --git a/primitives/chain-kusama/src/lib.rs b/primitives/chain-kusama/src/lib.rs new file mode 100644 index 00000000000..27d5f125ee2 --- /dev/null +++ b/primitives/chain-kusama/src/lib.rs @@ -0,0 +1,30 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +pub use bp_polkadot_core::*; +use bp_runtime::decl_bridge_finality_runtime_apis; + +/// Kusama Chain +pub type Kusama = PolkadotLike; + +/// Name of the With-Kusama GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_KUSAMA_GRANDPA_PALLET_NAME: &str = "BridgeKusamaGrandpa"; + +decl_bridge_finality_runtime_apis!(kusama); diff --git a/primitives/chain-millau/Cargo.toml b/primitives/chain-millau/Cargo.toml new file mode 100644 index 00000000000..b422e1545d6 --- /dev/null +++ b/primitives/chain-millau/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "bp-millau" +description = "Primitives of Millau runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-beefy = { path = "../beefy", default-features = false } +bp-messages = { path = "../messages", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } +fixed-hash = { version = "0.7.0", default-features = false } +hash256-std-hasher = { version = "0.15.2", default-features = false } +impl-codec = { version = "0.6", default-features = false } +impl-serde = { version = "0.3.1", optional = true } +parity-util-mem = { version = "0.12", default-features = false, features = ["primitive-types"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-beefy/std", + "bp-messages/std", + "bp-runtime/std", + "fixed-hash/std", + "frame-support/std", + "frame-system/std", + "hash256-std-hasher/std", + "impl-codec/std", + "impl-serde", + "parity-util-mem/std", + "scale-info/std", + "serde", + "sp-api/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", +] diff --git a/primitives/chain-millau/src/lib.rs b/primitives/chain-millau/src/lib.rs new file mode 100644 index 00000000000..ceb4be21b81 --- /dev/null +++ b/primitives/chain-millau/src/lib.rs @@ -0,0 +1,220 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +mod millau_hash; + +use bp_beefy::ChainWithBeefy; +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; +use bp_runtime::{decl_bridge_runtime_apis, Chain}; +use frame_support::{ + dispatch::DispatchClass, + weights::{constants::WEIGHT_PER_SECOND, IdentityFee, Weight}, + RuntimeDebug, +}; +use frame_system::limits; +use scale_info::TypeInfo; +use sp_core::{storage::StateVersion, Hasher as HasherT}; +use sp_runtime::{ + traits::{IdentifyAccount, Verify}, + MultiSignature, MultiSigner, Perbill, +}; +use sp_std::prelude::*; +use sp_trie::{LayoutV0, LayoutV1, TrieConfiguration}; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; +use sp_runtime::traits::Keccak256; + +pub use millau_hash::MillauHash; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// Millau chain. This mostly depends on number of entries (and their density) in the storage trie. +/// Some reserve is reserved to account future chain growth. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// Number of bytes, included in the signed Millau transaction apart from the encoded call itself. +/// +/// Can be computed by subtracting encoded call size from raw transaction size. +pub const TX_EXTRA_BYTES: u32 = 103; + +/// Maximum weight of single Millau block. +/// +/// This represents 0.5 seconds of compute assuming a target block time of six seconds. +// TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` +pub const MAXIMUM_BLOCK_WEIGHT: Weight = WEIGHT_PER_SECOND.set_proof_size(1_000).saturating_div(2); + +/// Represents the portion of a block that will be used by Normal extrinsics. +pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// Maximal number of unrewarded relayer entries in Millau confirmation transaction. +pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 128; + +/// Maximal number of unconfirmed messages in Millau confirmation transaction. +pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128; + +/// The target length of a session (how often authorities change) on Millau measured in of number of +/// blocks. +/// +/// Note that since this is a target sessions may change before/after this time depending on network +/// conditions. +pub const SESSION_LENGTH: BlockNumber = 5 * time_units::MINUTES; + +/// Maximal number of GRANDPA authorities at Millau. +pub const MAX_AUTHORITIES_COUNT: u32 = 5; + +/// Maximal SCALE-encoded header size (in bytes) at Millau. +pub const MAX_HEADER_SIZE: u32 = 1024; + +/// Re-export `time_units` to make usage easier. +pub use time_units::*; + +/// Human readable time units defined in terms of number of blocks. +pub mod time_units { + use super::BlockNumber; + + pub const MILLISECS_PER_BLOCK: u64 = 6000; + pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + + pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); + pub const HOURS: BlockNumber = MINUTES * 60; + pub const DAYS: BlockNumber = HOURS * 24; +} + +/// Block number type used in Millau. +pub type BlockNumber = u64; + +/// Hash type used in Millau. +pub type Hash = ::Out; + +/// Type of object that can produce hashes on Millau. +pub type Hasher = BlakeTwoAndKeccak256; + +/// The header type used by Millau. +pub type Header = sp_runtime::generic::Header; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = MultiSignature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +/// Public key of the chain account that may be used to verify signatures. +pub type AccountSigner = MultiSigner; + +/// Balance of an account. +pub type Balance = u64; + +/// Index of a transaction in the chain. +pub type Index = u32; + +/// Weight-to-Fee type used by Millau. +pub type WeightToFee = IdentityFee; + +/// Millau chain. +#[derive(RuntimeDebug)] +pub struct Millau; + +impl Chain for Millau { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +impl ChainWithBeefy for Millau { + type CommitmentHasher = Keccak256; + type MmrHashing = Keccak256; + type MmrHash = ::Output; + type BeefyMmrLeafExtra = (); + type AuthorityId = bp_beefy::EcdsaValidatorId; + type Signature = bp_beefy::EcdsaValidatorSignature; + type AuthorityIdToMerkleLeaf = bp_beefy::BeefyEcdsaToEthereum; +} + +/// Millau Hasher (Blake2-256 ++ Keccak-256) implementation. +#[derive(PartialEq, Eq, Clone, Copy, RuntimeDebug, TypeInfo)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct BlakeTwoAndKeccak256; + +impl sp_core::Hasher for BlakeTwoAndKeccak256 { + type Out = MillauHash; + type StdHasher = hash256_std_hasher::Hash256StdHasher; + const LENGTH: usize = 64; + + fn hash(s: &[u8]) -> Self::Out { + let mut combined_hash = MillauHash::default(); + combined_hash.as_mut()[..32].copy_from_slice(&sp_io::hashing::blake2_256(s)); + combined_hash.as_mut()[32..].copy_from_slice(&sp_io::hashing::keccak_256(s)); + combined_hash + } +} + +impl sp_runtime::traits::Hash for BlakeTwoAndKeccak256 { + type Output = MillauHash; + + fn trie_root(input: Vec<(Vec, Vec)>, state_version: StateVersion) -> Self::Output { + match state_version { + StateVersion::V0 => LayoutV0::::trie_root(input), + StateVersion::V1 => LayoutV1::::trie_root(input), + } + } + + fn ordered_trie_root(input: Vec>, state_version: StateVersion) -> Self::Output { + match state_version { + StateVersion::V0 => LayoutV0::::ordered_trie_root(input), + StateVersion::V1 => LayoutV1::::ordered_trie_root(input), + } + } +} + +frame_support::parameter_types! { + pub BlockLength: limits::BlockLength = + limits::BlockLength::max_with_normal_ratio(2 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub BlockWeights: limits::BlockWeights = + limits::BlockWeights::with_sensible_defaults(MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO); +} + +/// Name of the With-Millau GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_MILLAU_GRANDPA_PALLET_NAME: &str = "BridgeMillauGrandpa"; +/// Name of the With-Millau messages pallet instance that is deployed at bridged chains. +pub const WITH_MILLAU_MESSAGES_PALLET_NAME: &str = "BridgeMillauMessages"; +/// Name of the transaction payment pallet at the Millau runtime. +pub const TRANSACTION_PAYMENT_PALLET_NAME: &str = "TransactionPayment"; + +decl_bridge_runtime_apis!(millau); diff --git a/primitives/chain-millau/src/millau_hash.rs b/primitives/chain-millau/src/millau_hash.rs new file mode 100644 index 00000000000..11968b2f282 --- /dev/null +++ b/primitives/chain-millau/src/millau_hash.rs @@ -0,0 +1,58 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use parity_util_mem::MallocSizeOf; +use scale_info::TypeInfo; +use sp_runtime::traits::CheckEqual; + +// `sp_core::H512` can't be used, because it doesn't implement `CheckEqual`, which is required +// by `frame_system::Config::Hash`. + +fixed_hash::construct_fixed_hash! { + /// Hash type used in Millau chain. + #[derive(MallocSizeOf, TypeInfo)] + pub struct MillauHash(64); +} + +#[cfg(feature = "std")] +impl_serde::impl_fixed_hash_serde!(MillauHash, 64); + +impl_codec::impl_fixed_hash_codec!(MillauHash, 64); + +impl CheckEqual for MillauHash { + #[cfg(feature = "std")] + fn check_equal(&self, other: &Self) { + use sp_core::hexdisplay::HexDisplay; + if self != other { + println!( + "Hash: given={}, expected={}", + HexDisplay::from(self.as_fixed_bytes()), + HexDisplay::from(other.as_fixed_bytes()), + ); + } + } + + #[cfg(not(feature = "std"))] + fn check_equal(&self, other: &Self) { + use frame_support::Printable; + + if self != other { + "Hash not equal".print(); + self.as_bytes().print(); + other.as_bytes().print(); + } + } +} diff --git a/primitives/chain-polkadot/Cargo.toml b/primitives/chain-polkadot/Cargo.toml new file mode 100644 index 00000000000..b26093a0570 --- /dev/null +++ b/primitives/chain-polkadot/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "bp-polkadot" +description = "Primitives of Polkadot runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +# Bridge Dependencies + +bp-polkadot-core = { path = "../polkadot-core", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies + +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-polkadot-core/std", + "bp-runtime/std", + "sp-api/std", +] diff --git a/primitives/chain-polkadot/src/lib.rs b/primitives/chain-polkadot/src/lib.rs new file mode 100644 index 00000000000..0cada4e49a9 --- /dev/null +++ b/primitives/chain-polkadot/src/lib.rs @@ -0,0 +1,30 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +pub use bp_polkadot_core::*; +use bp_runtime::decl_bridge_finality_runtime_apis; + +/// Polkadot Chain +pub type Polkadot = PolkadotLike; + +/// Name of the With-Polkadot GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_POLKADOT_GRANDPA_PALLET_NAME: &str = "BridgePolkadotGrandpa"; + +decl_bridge_finality_runtime_apis!(polkadot); diff --git a/primitives/chain-rialto-parachain/Cargo.toml b/primitives/chain-rialto-parachain/Cargo.toml new file mode 100644 index 00000000000..a15c4092957 --- /dev/null +++ b/primitives/chain-rialto-parachain/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "bp-rialto-parachain" +description = "Primitives of Rialto parachain runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-messages = { path = "../messages", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-messages/std", + "bp-runtime/std", + "frame-support/std", + "frame-system/std", + "sp-api/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/primitives/chain-rialto-parachain/src/lib.rs b/primitives/chain-rialto-parachain/src/lib.rs new file mode 100644 index 00000000000..82b6a12c182 --- /dev/null +++ b/primitives/chain-rialto-parachain/src/lib.rs @@ -0,0 +1,146 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; +use bp_runtime::{decl_bridge_runtime_apis, Chain, Parachain}; +use frame_support::{ + dispatch::DispatchClass, + weights::{constants::WEIGHT_PER_SECOND, IdentityFee, Weight}, + RuntimeDebug, +}; +use frame_system::limits; +use sp_core::Hasher as HasherT; +use sp_runtime::{ + traits::{BlakeTwo256, IdentifyAccount, Verify}, + MultiSignature, MultiSigner, Perbill, +}; +use sp_std::vec::Vec; + +/// Identifier of RialtoParachain in the Rialto relay chain. +/// +/// This identifier is not something that is declared either by Rialto or RialtoParachain. This +/// is an identifier of registration. So in theory it may be changed. But since bridge is going +/// to be deployed after parachain registration AND since parachain de-registration is highly +/// likely impossible, it is fine to declare this constant here. +pub const RIALTO_PARACHAIN_ID: u32 = 2000; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// RialtoParachain chain. This mostly depends on number of entries (and their density) in the +/// storage trie. Some reserve is reserved to account future chain growth. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// Can be computed by subtracting encoded call size from raw transaction size. +pub const TX_EXTRA_BYTES: u32 = 104; + +/// Maximal weight of single RialtoParachain block. +/// +/// This represents two seconds of compute assuming a target block time of six seconds. +// TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` +pub const MAXIMUM_BLOCK_WEIGHT: Weight = WEIGHT_PER_SECOND.set_proof_size(1_000).saturating_mul(2); + +/// Represents the portion of a block that will be used by Normal extrinsics. +pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// Maximal number of unrewarded relayer entries in Rialto confirmation transaction. +pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; + +/// Maximal number of unconfirmed messages in Rialto confirmation transaction. +pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1024; + +/// Block number type used in Rialto. +pub type BlockNumber = u32; + +/// Hash type used in Rialto. +pub type Hash = ::Out; + +/// The type of object that can produce hashes on Rialto. +pub type Hasher = BlakeTwo256; + +/// The header type used by Rialto. +pub type Header = sp_runtime::generic::Header; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = MultiSignature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +/// Public key of the chain account that may be used to verify signatures. +pub type AccountSigner = MultiSigner; + +/// Balance of an account. +pub type Balance = u128; + +/// An instant or duration in time. +pub type Moment = u64; + +/// Index of a transaction in the parachain. +pub type Index = u32; + +/// Weight-to-Fee type used by Rialto parachain. +pub type WeightToFee = IdentityFee; + +/// Rialto parachain. +#[derive(RuntimeDebug)] +pub struct RialtoParachain; + +impl Chain for RialtoParachain { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +impl Parachain for RialtoParachain { + const PARACHAIN_ID: u32 = RIALTO_PARACHAIN_ID; +} + +frame_support::parameter_types! { + pub BlockLength: limits::BlockLength = + limits::BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub BlockWeights: limits::BlockWeights = + limits::BlockWeights::with_sensible_defaults(MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO); +} + +/// Name of the With-Rialto-Parachain messages pallet instance that is deployed at bridged chains. +pub const WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME: &str = "BridgeRialtoParachainMessages"; +/// Name of the transaction payment pallet at the Rialto parachain runtime. +pub const TRANSACTION_PAYMENT_PALLET_NAME: &str = "TransactionPayment"; + +decl_bridge_runtime_apis!(rialto_parachain); diff --git a/primitives/chain-rialto/Cargo.toml b/primitives/chain-rialto/Cargo.toml new file mode 100644 index 00000000000..663f9076657 --- /dev/null +++ b/primitives/chain-rialto/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "bp-rialto" +description = "Primitives of Rialto runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-messages = { path = "../messages", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-messages/std", + "bp-runtime/std", + "frame-support/std", + "frame-system/std", + "sp-api/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/primitives/chain-rialto/src/lib.rs b/primitives/chain-rialto/src/lib.rs new file mode 100644 index 00000000000..dfb727829d9 --- /dev/null +++ b/primitives/chain-rialto/src/lib.rs @@ -0,0 +1,180 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; +use bp_runtime::{decl_bridge_runtime_apis, Chain}; +use frame_support::{ + dispatch::DispatchClass, + weights::{constants::WEIGHT_PER_SECOND, IdentityFee, Weight}, + RuntimeDebug, +}; +use frame_system::limits; +use sp_core::Hasher as HasherT; +use sp_runtime::{ + traits::{BlakeTwo256, IdentifyAccount, Verify}, + MultiSignature, MultiSigner, Perbill, +}; +use sp_std::prelude::*; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// Rialto chain. This mostly depends on number of entries (and their density) in the storage trie. +/// Some reserve is reserved to account future chain growth. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// Number of bytes, included in the signed Rialto transaction apart from the encoded call itself. +/// +/// Can be computed by subtracting encoded call size from raw transaction size. +pub const TX_EXTRA_BYTES: u32 = 104; + +/// Maximal weight of single Rialto block. +/// +/// This represents two seconds of compute assuming a target block time of six seconds. +// TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` +pub const MAXIMUM_BLOCK_WEIGHT: Weight = WEIGHT_PER_SECOND.set_proof_size(1_000).saturating_mul(2); + +/// Represents the portion of a block that will be used by Normal extrinsics. +pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// Maximal number of unrewarded relayer entries in Rialto confirmation transaction. +pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; + +/// Maximal number of unconfirmed messages in Rialto confirmation transaction. +pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1024; + +/// The target length of a session (how often authorities change) on Rialto measured in of number of +/// blocks. +/// +/// Note that since this is a target sessions may change before/after this time depending on network +/// conditions. +pub const SESSION_LENGTH: BlockNumber = 4; + +/// Maximal number of GRANDPA authorities at Rialto. +pub const MAX_AUTHORITIES_COUNT: u32 = 5; + +/// Maximal SCALE-encoded header size (in bytes) at Rialto. +pub const MAX_HEADER_SIZE: u32 = 1024; + +/// Maximal SCALE-encoded size of parachains headers that are stored at Rialto `Paras` pallet. +pub const MAX_NESTED_PARACHAIN_HEAD_SIZE: u32 = MAX_HEADER_SIZE; + +/// Re-export `time_units` to make usage easier. +pub use time_units::*; + +/// Human readable time units defined in terms of number of blocks. +pub mod time_units { + use super::{BlockNumber, SESSION_LENGTH}; + + pub const MILLISECS_PER_BLOCK: u64 = 6000; + pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + + pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); + pub const HOURS: BlockNumber = MINUTES * 60; + pub const DAYS: BlockNumber = HOURS * 24; + + pub const EPOCH_DURATION_IN_SLOTS: BlockNumber = SESSION_LENGTH; + + // 1 in 4 blocks (on average, not counting collisions) will be primary babe blocks. + pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4); +} + +/// Block number type used in Rialto. +pub type BlockNumber = u32; + +/// Hash type used in Rialto. +pub type Hash = ::Out; + +/// The type of object that can produce hashes on Rialto. +pub type Hasher = BlakeTwo256; + +/// The header type used by Rialto. +pub type Header = sp_runtime::generic::Header; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = MultiSignature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +/// Public key of the chain account that may be used to verify signatures. +pub type AccountSigner = MultiSigner; + +/// Balance of an account. +pub type Balance = u128; + +/// An instant or duration in time. +pub type Moment = u64; + +/// Index of a transaction in the chain. +pub type Index = u32; + +/// Weight-to-Fee type used by Rialto. +pub type WeightToFee = IdentityFee; + +/// Rialto chain. +#[derive(RuntimeDebug)] +pub struct Rialto; + +impl Chain for Rialto { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +frame_support::parameter_types! { + pub BlockLength: limits::BlockLength = + limits::BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub BlockWeights: limits::BlockWeights = + limits::BlockWeights::with_sensible_defaults(MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO); +} + +/// Name of the With-Rialto GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_RIALTO_GRANDPA_PALLET_NAME: &str = "BridgeRialtoGrandpa"; +/// Name of the With-Rialto messages pallet instance that is deployed at bridged chains. +pub const WITH_RIALTO_MESSAGES_PALLET_NAME: &str = "BridgeRialtoMessages"; +/// Name of the With-Rialto parachains bridge pallet instance that is deployed at bridged chains. +pub const WITH_RIALTO_BRIDGE_PARAS_PALLET_NAME: &str = "BridgeRialtoParachains"; + +/// Name of the parachain registrar pallet in the Rialto runtime. +pub const PARAS_REGISTRAR_PALLET_NAME: &str = "Registrar"; + +/// Name of the parachains pallet in the Rialto runtime. +pub const PARAS_PALLET_NAME: &str = "Paras"; + +decl_bridge_runtime_apis!(rialto); diff --git a/primitives/chain-rococo/Cargo.toml b/primitives/chain-rococo/Cargo.toml new file mode 100644 index 00000000000..73a2450cd17 --- /dev/null +++ b/primitives/chain-rococo/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "bp-rococo" +description = "Primitives of Rococo runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies +bp-polkadot-core = { path = "../polkadot-core", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-polkadot-core/std", + "bp-runtime/std", + "sp-api/std", + "frame-support/std", +] diff --git a/primitives/chain-rococo/src/lib.rs b/primitives/chain-rococo/src/lib.rs new file mode 100644 index 00000000000..6fd7228fd75 --- /dev/null +++ b/primitives/chain-rococo/src/lib.rs @@ -0,0 +1,53 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +pub use bp_polkadot_core::*; +use bp_runtime::decl_bridge_finality_runtime_apis; +use frame_support::parameter_types; + +/// Rococo Chain +pub type Rococo = PolkadotLike; + +parameter_types! { + pub const SS58Prefix: u8 = 42; +} + +/// Name of the parachains pallet in the Rococo runtime. +pub const PARAS_PALLET_NAME: &str = "Paras"; + +/// Name of the With-Rococo GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_ROCOCO_GRANDPA_PALLET_NAME: &str = "BridgeRococoGrandpa"; + +/// Maximal SCALE-encoded header size (in bytes) at Rococo. +/// +/// Let's assume that the largest header is header that enacts new authorities set with +/// `MAX_AUTHORITES_COUNT`. Every authority means 32-byte key and 8-byte weight. Let's also have +/// some fixed reserve for other things (digest, block hash and number, ...) as well. +pub const MAX_HEADER_SIZE: u32 = 4096 + MAX_AUTHORITIES_COUNT * 40; + +/// Maximal SCALE-encoded size of parachains headers that are stored at Rococo `Paras` pallet. +pub const MAX_NESTED_PARACHAIN_HEAD_SIZE: u32 = MAX_HEADER_SIZE; + +/// Maximal number of GRANDPA authorities at Rococo. +/// +/// Corresponds to the `MaxAuthorities` constant value from the Rococo runtime configuration. +pub const MAX_AUTHORITIES_COUNT: u32 = 100_000; + +decl_bridge_finality_runtime_apis!(rococo); diff --git a/primitives/chain-westend/Cargo.toml b/primitives/chain-westend/Cargo.toml new file mode 100644 index 00000000000..7d74362b09a --- /dev/null +++ b/primitives/chain-westend/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "bp-westend" +description = "Primitives of Westend runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-polkadot-core = { path = "../polkadot-core", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies + +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-polkadot-core/std", + "bp-runtime/std", + "sp-api/std", +] diff --git a/primitives/chain-westend/src/lib.rs b/primitives/chain-westend/src/lib.rs new file mode 100644 index 00000000000..9f4a98baedc --- /dev/null +++ b/primitives/chain-westend/src/lib.rs @@ -0,0 +1,55 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +pub use bp_polkadot_core::*; +use bp_runtime::decl_bridge_finality_runtime_apis; + +/// Westend Chain +pub type Westend = PolkadotLike; + +/// Name of the parachains pallet at the Westend runtime. +pub const PARAS_PALLET_NAME: &str = "Paras"; + +/// Name of the With-Westend GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_WESTEND_GRANDPA_PALLET_NAME: &str = "BridgeWestendGrandpa"; +/// Name of the With-Westend parachains bridge pallet instance that is deployed at bridged chains. +pub const WITH_WESTEND_BRIDGE_PARAS_PALLET_NAME: &str = "BridgeWestendParachains"; + +/// Maximal number of GRANDPA authorities at Westend. +/// +/// Corresponds to the `MaxAuthorities` constant value from the Westend runtime configuration. +pub const MAX_AUTHORITIES_COUNT: u32 = 100_000; + +/// Maximal SCALE-encoded header size (in bytes) at Westend. +/// +/// Let's assume that the largest header is header that enacts new authorities set with +/// `MAX_AUTHORITES_COUNT`. Every authority means 32-byte key and 8-byte weight. Let's also have +/// some fixed reserve for other things (digest, block hash and number, ...) as well. +pub const MAX_HEADER_SIZE: u32 = 4096 + MAX_AUTHORITIES_COUNT * 40; + +/// Maximal SCALE-encoded size of parachains headers that are stored at Westend `Paras` pallet. +pub const MAX_NESTED_PARACHAIN_HEAD_SIZE: u32 = MAX_HEADER_SIZE; + +/// Identifier of Westmint parachain at the Westend relay chain. +pub const WESTMINT_PARACHAIN_ID: u32 = 2000; + +decl_bridge_finality_runtime_apis!(westend); + +decl_bridge_finality_runtime_apis!(westmint); diff --git a/primitives/chain-wococo/Cargo.toml b/primitives/chain-wococo/Cargo.toml new file mode 100644 index 00000000000..6b741cd4b73 --- /dev/null +++ b/primitives/chain-wococo/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "bp-wococo" +description = "Primitives of Wococo runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies +bp-polkadot-core = { path = "../polkadot-core", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } +bp-rococo = { path = "../chain-rococo", default-features = false } + +# Substrate Based Dependencies +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-polkadot-core/std", + "bp-runtime/std", + "bp-rococo/std", + "sp-api/std", +] diff --git a/primitives/chain-wococo/src/lib.rs b/primitives/chain-wococo/src/lib.rs new file mode 100644 index 00000000000..0436b03c036 --- /dev/null +++ b/primitives/chain-wococo/src/lib.rs @@ -0,0 +1,34 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +pub use bp_polkadot_core::*; +pub use bp_rococo::{ + SS58Prefix, MAX_AUTHORITIES_COUNT, MAX_HEADER_SIZE, MAX_NESTED_PARACHAIN_HEAD_SIZE, + PARAS_PALLET_NAME, +}; +use bp_runtime::decl_bridge_finality_runtime_apis; + +/// Wococo Chain +pub type Wococo = PolkadotLike; + +/// Name of the With-Wococo GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_WOCOCO_GRANDPA_PALLET_NAME: &str = "BridgeWococoGrandpa"; + +decl_bridge_finality_runtime_apis!(wococo); diff --git a/primitives/header-chain/Cargo.toml b/primitives/header-chain/Cargo.toml new file mode 100644 index 00000000000..3b7ea58cb96 --- /dev/null +++ b/primitives/header-chain/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "bp-header-chain" +description = "A common interface for describing what a bridge pallet should be able to do." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +finality-grandpa = { version = "0.16.0", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true } + +# Bridge dependencies + +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +bp-test-utils = { path = "../test-utils" } +hex = "0.4" +hex-literal = "0.3" + +[features] +default = ["std"] +std = [ + "bp-runtime/std", + "codec/std", + "finality-grandpa/std", + "serde/std", + "frame-support/std", + "scale-info/std", + "sp-core/std", + "sp-finality-grandpa/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", +] diff --git a/primitives/header-chain/src/justification.rs b/primitives/header-chain/src/justification.rs new file mode 100644 index 00000000000..dadd48a4850 --- /dev/null +++ b/primitives/header-chain/src/justification.rs @@ -0,0 +1,227 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Pallet for checking GRANDPA Finality Proofs. +//! +//! Adapted copy of substrate/client/finality-grandpa/src/justification.rs. If origin +//! will ever be moved to the sp_finality_grandpa, we should reuse that implementation. + +use codec::{Decode, Encode}; +use finality_grandpa::voter_set::VoterSet; +use frame_support::RuntimeDebug; +use scale_info::TypeInfo; +use sp_finality_grandpa::{AuthorityId, AuthoritySignature, SetId}; +use sp_runtime::traits::Header as HeaderT; +use sp_std::{ + collections::{btree_map::BTreeMap, btree_set::BTreeSet}, + prelude::*, +}; + +/// A GRANDPA Justification is a proof that a given header was finalized +/// at a certain height and with a certain set of authorities. +/// +/// This particular proof is used to prove that headers on a bridged chain +/// (so not our chain) have been finalized correctly. +#[derive(Encode, Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] +pub struct GrandpaJustification { + /// The round (voting period) this justification is valid for. + pub round: u64, + /// The set of votes for the chain which is to be finalized. + pub commit: + finality_grandpa::Commit, + /// A proof that the chain of blocks in the commit are related to each other. + pub votes_ancestries: Vec
, +} + +impl crate::FinalityProof for GrandpaJustification { + fn target_header_number(&self) -> H::Number { + self.commit.target_number + } +} + +/// Justification verification error. +#[derive(Eq, RuntimeDebug, PartialEq)] +pub enum Error { + /// Failed to decode justification. + JustificationDecode, + /// Justification is finalizing unexpected header. + InvalidJustificationTarget, + /// The authority has provided an invalid signature. + InvalidAuthoritySignature, + /// The justification contains precommit for header that is not a descendant of the commit + /// header. + PrecommitIsNotCommitDescendant, + /// The cumulative weight of all votes in the justification is not enough to justify commit + /// header finalization. + TooLowCumulativeWeight, + /// The justification contains extra (unused) headers in its `votes_ancestries` field. + ExtraHeadersInVotesAncestries, +} + +/// Decode justification target. +pub fn decode_justification_target( + raw_justification: &[u8], +) -> Result<(Header::Hash, Header::Number), Error> { + GrandpaJustification::
::decode(&mut &*raw_justification) + .map(|justification| (justification.commit.target_hash, justification.commit.target_number)) + .map_err(|_| Error::JustificationDecode) +} + +/// Verify that justification, that is generated by given authority set, finalizes given header. +pub fn verify_justification( + finalized_target: (Header::Hash, Header::Number), + authorities_set_id: SetId, + authorities_set: &VoterSet, + justification: &GrandpaJustification
, +) -> Result<(), Error> +where + Header::Number: finality_grandpa::BlockNumberOps, +{ + // ensure that it is justification for the expected header + if (justification.commit.target_hash, justification.commit.target_number) != finalized_target { + return Err(Error::InvalidJustificationTarget) + } + + let mut chain = AncestryChain::new(&justification.votes_ancestries); + let mut signature_buffer = Vec::new(); + let mut votes = BTreeSet::new(); + let mut cumulative_weight = 0u64; + for signed in &justification.commit.precommits { + // authority must be in the set + let authority_info = match authorities_set.get(&signed.id) { + Some(authority_info) => authority_info, + None => { + // just ignore precommit from unknown authority as + // `finality_grandpa::import_precommit` does + continue + }, + }; + + // check if authority has already voted in the same round. + // + // there's a lot of code in `validate_commit` and `import_precommit` functions inside + // `finality-grandpa` crate (mostly related to reporting equivocations). But the only thing + // that we care about is that only first vote from the authority is accepted + if !votes.insert(signed.id.clone()) { + continue + } + + // everything below this line can't just `continue`, because state is already altered + + // precommits aren't allowed for block lower than the target + if signed.precommit.target_number < justification.commit.target_number { + return Err(Error::PrecommitIsNotCommitDescendant) + } + // all precommits must be descendants of target block + chain = chain + .ensure_descendant(&justification.commit.target_hash, &signed.precommit.target_hash)?; + // since we know now that the precommit target is the descendant of the justification + // target, we may increase 'weight' of the justification target + // + // there's a lot of code in the `VoteGraph::insert` method inside `finality-grandpa` crate, + // but in the end it is only used to find GHOST, which we don't care about. The only thing + // that we care about is that the justification target has enough weight + cumulative_weight = cumulative_weight.checked_add(authority_info.weight().0.into()).expect( + "sum of weights of ALL authorities is expected not to overflow - this is guaranteed by\ + existence of VoterSet;\ + the order of loop conditions guarantees that we can account vote from same authority\ + multiple times;\ + thus we'll never overflow the u64::MAX;\ + qed", + ); + // verify authority signature + if !sp_finality_grandpa::check_message_signature_with_buffer( + &finality_grandpa::Message::Precommit(signed.precommit.clone()), + &signed.id, + &signed.signature, + justification.round, + authorities_set_id, + &mut signature_buffer, + ) { + return Err(Error::InvalidAuthoritySignature) + } + } + + // check that there are no extra headers in the justification + if !chain.unvisited.is_empty() { + return Err(Error::ExtraHeadersInVotesAncestries) + } + + // check that the cumulative weight of validators voted for the justification target (or one + // of its descendents) is larger than required threshold. + let threshold = authorities_set.threshold().0.into(); + if cumulative_weight >= threshold { + Ok(()) + } else { + Err(Error::TooLowCumulativeWeight) + } +} + +/// Votes ancestries with useful methods. +#[derive(RuntimeDebug)] +pub struct AncestryChain { + /// Header hash => parent header hash mapping. + pub parents: BTreeMap, + /// Hashes of headers that were not visited by `is_ancestor` method. + pub unvisited: BTreeSet, +} + +impl AncestryChain
{ + /// Create new ancestry chain. + pub fn new(ancestry: &[Header]) -> AncestryChain
{ + let mut parents = BTreeMap::new(); + let mut unvisited = BTreeSet::new(); + for ancestor in ancestry { + let hash = ancestor.hash(); + let parent_hash = *ancestor.parent_hash(); + parents.insert(hash, parent_hash); + unvisited.insert(hash); + } + AncestryChain { parents, unvisited } + } + + /// Returns `Ok(_)` if `precommit_target` is a descendant of the `commit_target` block and + /// `Err(_)` otherwise. + pub fn ensure_descendant( + mut self, + commit_target: &Header::Hash, + precommit_target: &Header::Hash, + ) -> Result { + let mut current_hash = *precommit_target; + loop { + if current_hash == *commit_target { + break + } + + let is_visited_before = !self.unvisited.remove(¤t_hash); + current_hash = match self.parents.get(¤t_hash) { + Some(parent_hash) => { + if is_visited_before { + // `Some(parent_hash)` means that the `current_hash` is in the `parents` + // container `is_visited_before` means that it has been visited before in + // some of previous calls => since we assume that previous call has finished + // with `true`, this also will be finished with `true` + return Ok(self) + } + + *parent_hash + }, + None => return Err(Error::PrecommitIsNotCommitDescendant), + }; + } + Ok(self) + } +} diff --git a/primitives/header-chain/src/lib.rs b/primitives/header-chain/src/lib.rs new file mode 100644 index 00000000000..2b4e0802a5a --- /dev/null +++ b/primitives/header-chain/src/lib.rs @@ -0,0 +1,147 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Defines traits which represent a common interface for Substrate pallets which want to +//! incorporate bridge functionality. + +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_runtime::{BasicOperatingMode, Chain, HashOf, HasherOf, HeaderOf, StorageProofChecker}; +use codec::{Codec, Decode, Encode, EncodeLike}; +use core::{clone::Clone, cmp::Eq, default::Default, fmt::Debug}; +use frame_support::PalletError; +use scale_info::TypeInfo; +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; +use sp_finality_grandpa::{AuthorityList, ConsensusLog, SetId, GRANDPA_ENGINE_ID}; +use sp_runtime::{traits::Header as HeaderT, Digest, RuntimeDebug}; +use sp_std::boxed::Box; +use sp_trie::StorageProof; + +pub mod justification; +pub mod storage_keys; + +/// Header chain error. +#[derive(Clone, Copy, Decode, Encode, Eq, PalletError, PartialEq, RuntimeDebug, TypeInfo)] +pub enum HeaderChainError { + /// Header with given hash is missing from the chain. + UnknownHeader, + /// The storage proof doesn't contains storage root. + StorageRootMismatch, +} + +impl From for &'static str { + fn from(err: HeaderChainError) -> &'static str { + match err { + HeaderChainError::UnknownHeader => "UnknownHeader", + HeaderChainError::StorageRootMismatch => "StorageRootMismatch", + } + } +} + +/// Substrate header chain, abstracted from the way it is stored. +pub trait HeaderChain { + /// Returns finalized header by its hash. + fn finalized_header(hash: HashOf) -> Option>; + /// Parse storage proof using finalized header. + fn parse_finalized_storage_proof( + hash: HashOf, + storage_proof: StorageProof, + parse: impl FnOnce(StorageProofChecker>) -> R, + ) -> Result { + let header = Self::finalized_header(hash).ok_or(HeaderChainError::UnknownHeader)?; + let storage_proof_checker = + bp_runtime::StorageProofChecker::new(*header.state_root(), storage_proof) + .map_err(|_| HeaderChainError::StorageRootMismatch)?; + + Ok(parse(storage_proof_checker)) + } +} + +/// A type that can be used as a parameter in a dispatchable function. +/// +/// When using `decl_module` all arguments for call functions must implement this trait. +pub trait Parameter: Codec + EncodeLike + Clone + Eq + Debug + TypeInfo {} +impl Parameter for T where T: Codec + EncodeLike + Clone + Eq + Debug + TypeInfo {} + +/// A GRANDPA Authority List and ID. +#[derive(Default, Encode, Eq, Decode, RuntimeDebug, PartialEq, Clone, TypeInfo)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct AuthoritySet { + /// List of GRANDPA authorities for the current round. + pub authorities: AuthorityList, + /// Monotonic identifier of the current GRANDPA authority set. + pub set_id: SetId, +} + +impl AuthoritySet { + /// Create a new GRANDPA Authority Set. + pub fn new(authorities: AuthorityList, set_id: SetId) -> Self { + Self { authorities, set_id } + } +} + +/// Data required for initializing the bridge pallet. +/// +/// The bridge needs to know where to start its sync from, and this provides that initial context. +#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, Clone, TypeInfo)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct InitializationData { + /// The header from which we should start syncing. + pub header: Box, + /// The initial authorities of the pallet. + pub authority_list: AuthorityList, + /// The ID of the initial authority set. + pub set_id: SetId, + /// Pallet operating mode. + pub operating_mode: BasicOperatingMode, +} + +/// Abstract finality proof that is justifying block finality. +pub trait FinalityProof: Clone + Send + Sync + Debug { + /// Return number of header that this proof is generated for. + fn target_header_number(&self) -> Number; +} + +/// A trait that provides helper methods for querying the consensus log. +pub trait ConsensusLogReader { + /// Returns true if digest contains item that schedules authorities set change. + fn schedules_authorities_change(digest: &Digest) -> bool; +} + +/// A struct that provides helper methods for querying the GRANDPA consensus log. +pub struct GrandpaConsensusLogReader(sp_std::marker::PhantomData); + +impl GrandpaConsensusLogReader { + pub fn find_authorities_change( + digest: &Digest, + ) -> Option> { + // find the first consensus digest with the right ID which converts to + // the right kind of consensus log. + digest + .convert_first(|log| log.consensus_try_to(&GRANDPA_ENGINE_ID)) + .and_then(|log| match log { + ConsensusLog::ScheduledChange(change) => Some(change), + _ => None, + }) + } +} + +impl ConsensusLogReader for GrandpaConsensusLogReader { + fn schedules_authorities_change(digest: &Digest) -> bool { + GrandpaConsensusLogReader::::find_authorities_change(digest).is_some() + } +} diff --git a/primitives/header-chain/src/storage_keys.rs b/primitives/header-chain/src/storage_keys.rs new file mode 100644 index 00000000000..bb642b1817f --- /dev/null +++ b/primitives/header-chain/src/storage_keys.rs @@ -0,0 +1,78 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Storage keys of bridge GRANDPA pallet. + +/// Name of the `IsHalted` storage value. +pub const PALLET_OPERATING_MODE_VALUE_NAME: &str = "PalletOperatingMode"; +/// Name of the `BestFinalized` storage value. +pub const BEST_FINALIZED_VALUE_NAME: &str = "BestFinalized"; + +use sp_core::storage::StorageKey; + +/// Storage key of the `PalletOperatingMode` variable in the runtime storage. +pub fn pallet_operating_mode_key(pallet_prefix: &str) -> StorageKey { + StorageKey( + bp_runtime::storage_value_final_key( + pallet_prefix.as_bytes(), + PALLET_OPERATING_MODE_VALUE_NAME.as_bytes(), + ) + .to_vec(), + ) +} + +/// Storage key of the best finalized header number and hash value in the runtime storage. +pub fn best_finalized_key(pallet_prefix: &str) -> StorageKey { + StorageKey( + bp_runtime::storage_value_final_key( + pallet_prefix.as_bytes(), + BEST_FINALIZED_VALUE_NAME.as_bytes(), + ) + .to_vec(), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use hex_literal::hex; + + #[test] + fn pallet_operating_mode_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is breaking + // compatibility with previous pallet. + let storage_key = pallet_operating_mode_key("BridgeGrandpa").0; + assert_eq!( + storage_key, + hex!("0b06f475eddb98cf933a12262e0388de0f4cf0917788d791142ff6c1f216e7b3").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } + + #[test] + fn best_finalized_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is breaking + // compatibility with previous pallet. + let storage_key = best_finalized_key("BridgeGrandpa").0; + assert_eq!( + storage_key, + hex!("0b06f475eddb98cf933a12262e0388dea4ebafdd473c549fdb24c5c991c5591c").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } +} diff --git a/primitives/header-chain/tests/implementation_match.rs b/primitives/header-chain/tests/implementation_match.rs new file mode 100644 index 00000000000..aaa19d4b918 --- /dev/null +++ b/primitives/header-chain/tests/implementation_match.rs @@ -0,0 +1,328 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Tests inside this module are made to ensure that our custom justification verification +//! implementation works exactly as `fn finality_grandpa::validate_commit`. +//! +//! Some of tests in this module may partially duplicate tests from `justification.rs`, +//! but their purpose is different. + +use bp_header_chain::justification::{verify_justification, Error, GrandpaJustification}; +use bp_test_utils::{ + header_id, make_justification_for_header, signed_precommit, test_header, Account, + JustificationGeneratorParams, ALICE, BOB, CHARLIE, DAVE, EVE, TEST_GRANDPA_SET_ID, +}; +use finality_grandpa::voter_set::VoterSet; +use sp_finality_grandpa::{AuthorityId, AuthorityWeight}; +use sp_runtime::traits::Header as HeaderT; + +type TestHeader = sp_runtime::testing::Header; +type TestHash = ::Hash; +type TestNumber = ::Number; + +/// Implementation of `finality_grandpa::Chain` that is used in tests. +struct AncestryChain(bp_header_chain::justification::AncestryChain); + +impl AncestryChain { + fn new(ancestry: &[TestHeader]) -> Self { + Self(bp_header_chain::justification::AncestryChain::new(ancestry)) + } +} + +impl finality_grandpa::Chain for AncestryChain { + fn ancestry( + &self, + base: TestHash, + block: TestHash, + ) -> Result, finality_grandpa::Error> { + let mut route = Vec::new(); + let mut current_hash = block; + loop { + if current_hash == base { + break + } + match self.0.parents.get(¤t_hash).cloned() { + Some(parent_hash) => { + current_hash = parent_hash; + route.push(current_hash); + }, + _ => return Err(finality_grandpa::Error::NotDescendent), + } + } + route.pop(); // remove the base + + Ok(route) + } +} + +/// Get a full set of accounts. +fn full_accounts_set() -> Vec<(Account, AuthorityWeight)> { + vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1), (DAVE, 1), (EVE, 1)] +} + +/// Get a full set of GRANDPA authorities. +fn full_voter_set() -> VoterSet { + VoterSet::new(full_accounts_set().iter().map(|(id, w)| (AuthorityId::from(*id), *w))).unwrap() +} + +/// Get a minimal set of accounts. +fn minimal_accounts_set() -> Vec<(Account, AuthorityWeight)> { + // there are 5 accounts in the full set => we need 2/3 + 1 accounts, which results in 4 accounts + vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1), (DAVE, 1)] +} + +/// Get a minimal subset of GRANDPA authorities that have enough cumulative vote weight to justify a +/// header finality. +pub fn minimal_voter_set() -> VoterSet { + VoterSet::new(minimal_accounts_set().iter().map(|(id, w)| (AuthorityId::from(*id), *w))) + .unwrap() +} + +/// Make a valid GRANDPA justification with sensible defaults. +pub fn make_default_justification(header: &TestHeader) -> GrandpaJustification { + make_justification_for_header(JustificationGeneratorParams { + header: header.clone(), + authorities: minimal_accounts_set(), + ..Default::default() + }) +} + +// the `finality_grandpa::validate_commit` function has two ways to report an unsuccessful +// commit validation: +// +// 1) to return `Err()` (which only may happen if `finality_grandpa::Chain` implementation +// returns an error); +// 2) to return `Ok(validation_result)` if `validation_result.is_valid()` is false. +// +// Our implementation would just return error in both cases. + +#[test] +fn same_result_when_precommit_target_has_lower_number_than_commit_target() { + let mut justification = make_default_justification(&test_header(1)); + // the number of header in precommit (0) is lower than number of header in commit (1) + justification.commit.precommits[0].precommit.target_number = 0; + + // our implementation returns an error + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Err(Error::PrecommitIsNotCommitDescendant), + ); + + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == false`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(!result.is_valid()); +} + +#[test] +fn same_result_when_precommit_target_is_not_descendant_of_commit_target() { + let not_descendant = test_header::(10); + let mut justification = make_default_justification(&test_header(1)); + // the route from header of commit (1) to header of precommit (10) is missing from + // the votes ancestries + justification.commit.precommits[0].precommit.target_number = *not_descendant.number(); + justification.commit.precommits[0].precommit.target_hash = not_descendant.hash(); + justification.votes_ancestries.push(not_descendant); + + // our implementation returns an error + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Err(Error::PrecommitIsNotCommitDescendant), + ); + + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == false`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(!result.is_valid()); +} + +#[test] +fn same_result_when_justification_contains_duplicate_vote() { + let mut justification = make_justification_for_header(JustificationGeneratorParams { + header: test_header(1), + authorities: minimal_accounts_set(), + ancestors: 0, + ..Default::default() + }); + // the justification may contain exactly the same vote (i.e. same precommit and same signature) + // multiple times && it isn't treated as an error by original implementation + justification.commit.precommits.push(justification.commit.precommits[0].clone()); + justification.commit.precommits.push(justification.commit.precommits[0].clone()); + + // our implementation succeeds + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Ok(()), + ); + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == true`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(result.is_valid()); +} + +#[test] +fn same_result_when_authority_equivocates_once_in_a_round() { + let mut justification = make_justification_for_header(JustificationGeneratorParams { + header: test_header(1), + authorities: minimal_accounts_set(), + ancestors: 0, + ..Default::default() + }); + // the justification original implementation allows authority to submit two different + // votes in a single round, of which only first is 'accepted' + justification.commit.precommits.push(signed_precommit::( + &ALICE, + header_id::(1), + justification.round, + TEST_GRANDPA_SET_ID, + )); + + // our implementation succeeds + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Ok(()), + ); + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == true`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(result.is_valid()); +} + +#[test] +fn same_result_when_authority_equivocates_twice_in_a_round() { + let mut justification = make_justification_for_header(JustificationGeneratorParams { + header: test_header(1), + authorities: minimal_accounts_set(), + ancestors: 0, + ..Default::default() + }); + // there's some code in the original implementation that should return an error when + // same authority submits more than two different votes in a single round: + // https://github.com/paritytech/finality-grandpa/blob/6aeea2d1159d0f418f0b86e70739f2130629ca09/src/lib.rs#L473 + // but there's also a code that prevents this from happening: + // https://github.com/paritytech/finality-grandpa/blob/6aeea2d1159d0f418f0b86e70739f2130629ca09/src/round.rs#L287 + // => so now we are also just ignoring all votes from the same authority, except the first one + justification.commit.precommits.push(signed_precommit::( + &ALICE, + header_id::(1), + justification.round, + TEST_GRANDPA_SET_ID, + )); + justification.commit.precommits.push(signed_precommit::( + &ALICE, + header_id::(1), + justification.round, + TEST_GRANDPA_SET_ID, + )); + + // our implementation succeeds + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Ok(()), + ); + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == true`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(result.is_valid()); +} + +#[test] +fn same_result_when_there_are_not_enough_cumulative_weight_to_finalize_commit_target() { + // just remove one authority from the minimal set and we shall not reach the threshold + let mut authorities_set = minimal_accounts_set(); + authorities_set.pop(); + let justification = make_justification_for_header(JustificationGeneratorParams { + header: test_header(1), + authorities: authorities_set, + ..Default::default() + }); + + // our implementation returns an error + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Err(Error::TooLowCumulativeWeight), + ); + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == false`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(!result.is_valid()); +} diff --git a/primitives/header-chain/tests/justification.rs b/primitives/header-chain/tests/justification.rs new file mode 100644 index 00000000000..5b4981a0f69 --- /dev/null +++ b/primitives/header-chain/tests/justification.rs @@ -0,0 +1,192 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Tests for Grandpa Justification code. + +use bp_header_chain::justification::{verify_justification, Error}; +use bp_test_utils::*; + +type TestHeader = sp_runtime::testing::Header; + +#[test] +fn valid_justification_accepted() { + let authorities = vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1), (DAVE, 1)]; + let params = JustificationGeneratorParams { + header: test_header(1), + round: TEST_GRANDPA_ROUND, + set_id: TEST_GRANDPA_SET_ID, + authorities: authorities.clone(), + ancestors: 7, + forks: 3, + }; + + let justification = make_justification_for_header::(params.clone()); + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + &justification, + ), + Ok(()), + ); + + assert_eq!(justification.commit.precommits.len(), authorities.len()); + assert_eq!(justification.votes_ancestries.len(), params.ancestors as usize); +} + +#[test] +fn valid_justification_accepted_with_single_fork() { + let params = JustificationGeneratorParams { + header: test_header(1), + round: TEST_GRANDPA_ROUND, + set_id: TEST_GRANDPA_SET_ID, + authorities: vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1), (DAVE, 1), (EVE, 1)], + ancestors: 5, + forks: 1, + }; + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + &make_justification_for_header::(params) + ), + Ok(()), + ); +} + +#[test] +fn valid_justification_accepted_with_arbitrary_number_of_authorities() { + use finality_grandpa::voter_set::VoterSet; + use sp_finality_grandpa::AuthorityId; + + let n = 15; + let authorities = accounts(n).iter().map(|k| (*k, 1)).collect::>(); + + let params = JustificationGeneratorParams { + header: test_header(1), + round: TEST_GRANDPA_ROUND, + set_id: TEST_GRANDPA_SET_ID, + authorities: authorities.clone(), + ancestors: n.into(), + forks: n.into(), + }; + + let authorities = authorities + .iter() + .map(|(id, w)| (AuthorityId::from(*id), *w)) + .collect::>(); + let voter_set = VoterSet::new(authorities).unwrap(); + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set, + &make_justification_for_header::(params) + ), + Ok(()), + ); +} + +#[test] +fn justification_with_invalid_target_rejected() { + assert_eq!( + verify_justification::( + header_id::(2), + TEST_GRANDPA_SET_ID, + &voter_set(), + &make_default_justification::(&test_header(1)), + ), + Err(Error::InvalidJustificationTarget), + ); +} + +#[test] +fn justification_with_invalid_commit_rejected() { + let mut justification = make_default_justification::(&test_header(1)); + justification.commit.precommits.clear(); + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + &justification, + ), + Err(Error::ExtraHeadersInVotesAncestries), + ); +} + +#[test] +fn justification_with_invalid_authority_signature_rejected() { + let mut justification = make_default_justification::(&test_header(1)); + justification.commit.precommits[0].signature = + sp_core::crypto::UncheckedFrom::unchecked_from([1u8; 64]); + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + &justification, + ), + Err(Error::InvalidAuthoritySignature), + ); +} + +#[test] +fn justification_with_invalid_precommit_ancestry() { + let mut justification = make_default_justification::(&test_header(1)); + justification.votes_ancestries.push(test_header(10)); + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + &justification, + ), + Err(Error::ExtraHeadersInVotesAncestries), + ); +} + +#[test] +fn justification_is_invalid_if_we_dont_meet_threshold() { + // Need at least three authorities to sign off or else the voter set threshold can't be reached + let authorities = vec![(ALICE, 1), (BOB, 1)]; + + let params = JustificationGeneratorParams { + header: test_header(1), + round: TEST_GRANDPA_ROUND, + set_id: TEST_GRANDPA_SET_ID, + authorities: authorities.clone(), + ancestors: 2 * authorities.len() as u32, + forks: 2, + }; + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + &make_justification_for_header::(params) + ), + Err(Error::TooLowCumulativeWeight), + ); +} diff --git a/primitives/messages/Cargo.toml b/primitives/messages/Cargo.toml new file mode 100644 index 00000000000..4899a1f22da --- /dev/null +++ b/primitives/messages/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "bp-messages" +description = "Primitives of messages module." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive", "bit-vec"] } +impl-trait-for-tuples = "0.2" +scale-info = { version = "2.1.1", default-features = false, features = ["bit-vec", "derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } + +# Bridge dependencies + +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +hex = "0.4" +hex-literal = "0.3" + +[features] +default = ["std"] +std = [ + "bp-runtime/std", + "codec/std", + "frame-support/std", + "scale-info/std", + "serde", + "sp-core/std", + "sp-std/std" +] diff --git a/primitives/messages/src/lib.rs b/primitives/messages/src/lib.rs new file mode 100644 index 00000000000..daaa2800988 --- /dev/null +++ b/primitives/messages/src/lib.rs @@ -0,0 +1,418 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives of messages module. + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +use bp_runtime::{BasicOperatingMode, OperatingMode}; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::RuntimeDebug; +use scale_info::TypeInfo; +use sp_std::{collections::vec_deque::VecDeque, prelude::*}; + +pub mod source_chain; +pub mod storage_keys; +pub mod target_chain; + +use bp_runtime::messages::MessageDispatchResult; +// Weight is reexported to avoid additional frame-support dependencies in related crates. +pub use frame_support::weights::Weight; + +/// Messages pallet operating mode. +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub enum MessagesOperatingMode { + /// Basic operating mode (Normal/Halted) + Basic(BasicOperatingMode), + /// The pallet is not accepting outbound messages. Inbound messages and receiving proofs + /// are still accepted. + /// + /// This mode may be used e.g. when bridged chain expects upgrade. Then to avoid dispatch + /// failures, the pallet owner may stop accepting new messages, while continuing to deliver + /// queued messages to the bridged chain. Once upgrade is completed, the mode may be switched + /// back to `Normal`. + RejectingOutboundMessages, +} + +impl Default for MessagesOperatingMode { + fn default() -> Self { + MessagesOperatingMode::Basic(BasicOperatingMode::Normal) + } +} + +impl OperatingMode for MessagesOperatingMode { + fn is_halted(&self) -> bool { + match self { + Self::Basic(operating_mode) => operating_mode.is_halted(), + _ => false, + } + } +} + +/// Lane identifier. +pub type LaneId = [u8; 4]; + +/// Message nonce. Valid messages will never have 0 nonce. +pub type MessageNonce = u64; + +/// Message id as a tuple. +pub type BridgeMessageId = (LaneId, MessageNonce); + +/// Opaque message payload. We only decode this payload when it is dispatched. +pub type MessagePayload = Vec; + +/// Message key (unique message identifier) as it is stored in the storage. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub struct MessageKey { + /// ID of the message lane. + pub lane_id: LaneId, + /// Message nonce. + pub nonce: MessageNonce, +} + +/// Message as it is stored in the storage. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct Message { + /// Message key. + pub key: MessageKey, + /// Message payload. + pub payload: MessagePayload, +} + +/// Inbound lane data. +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct InboundLaneData { + /// Identifiers of relayers and messages that they have delivered to this lane (ordered by + /// message nonce). + /// + /// This serves as a helper storage item, to allow the source chain to easily pay rewards + /// to the relayers who successfully delivered messages to the target chain (inbound lane). + /// + /// It is guaranteed to have at most N entries, where N is configured at the module level. + /// If there are N entries in this vec, then: + /// 1) all incoming messages are rejected if they're missing corresponding + /// `proof-of(outbound-lane.state)`; 2) all incoming messages are rejected if + /// `proof-of(outbound-lane.state).last_delivered_nonce` is equal to + /// `self.last_confirmed_nonce`. Given what is said above, all nonces in this queue are in + /// range: `(self.last_confirmed_nonce; self.last_delivered_nonce()]`. + /// + /// When a relayer sends a single message, both of MessageNonces are the same. + /// When relayer sends messages in a batch, the first arg is the lowest nonce, second arg the + /// highest nonce. Multiple dispatches from the same relayer are allowed. + pub relayers: VecDeque>, + + /// Nonce of the last message that + /// a) has been delivered to the target (this) chain and + /// b) the delivery has been confirmed on the source chain + /// + /// that the target chain knows of. + /// + /// This value is updated indirectly when an `OutboundLane` state of the source + /// chain is received alongside with new messages delivery. + pub last_confirmed_nonce: MessageNonce, +} + +impl Default for InboundLaneData { + fn default() -> Self { + InboundLaneData { relayers: VecDeque::new(), last_confirmed_nonce: 0 } + } +} + +impl InboundLaneData { + /// Returns approximate size of the struct, given a number of entries in the `relayers` set and + /// size of each entry. + /// + /// Returns `None` if size overflows `usize` limits. + pub fn encoded_size_hint(relayers_entries: usize) -> Option + where + RelayerId: MaxEncodedLen, + { + let message_nonce_size = MessageNonce::max_encoded_len(); + let relayer_id_encoded_size = RelayerId::max_encoded_len(); + let relayers_entry_size = relayer_id_encoded_size.checked_add(2 * message_nonce_size)?; + let relayers_size = relayers_entries.checked_mul(relayers_entry_size)?; + relayers_size.checked_add(message_nonce_size) + } + + /// Returns the approximate size of the struct as u32, given a number of entries in the + /// `relayers` set and the size of each entry. + /// + /// Returns `u32::MAX` if size overflows `u32` limits. + pub fn encoded_size_hint_u32(relayers_entries: usize) -> u32 + where + RelayerId: MaxEncodedLen, + { + Self::encoded_size_hint(relayers_entries) + .and_then(|x| u32::try_from(x).ok()) + .unwrap_or(u32::MAX) + } + + /// Nonce of the last message that has been delivered to this (target) chain. + pub fn last_delivered_nonce(&self) -> MessageNonce { + self.relayers + .back() + .map(|entry| entry.messages.end) + .unwrap_or(self.last_confirmed_nonce) + } +} + +/// Outbound message details, returned by runtime APIs. +#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq)] +pub struct OutboundMessageDetails { + /// Nonce assigned to the message. + pub nonce: MessageNonce, + /// Message dispatch weight. + /// + /// Depending on messages pallet configuration, it may be declared by the message submitter, + /// computed automatically or just be zero if dispatch fee is paid at the target chain. + pub dispatch_weight: Weight, + /// Size of the encoded message. + pub size: u32, +} + +/// Inbound message details, returned by runtime APIs. +#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq)] +pub struct InboundMessageDetails { + /// Computed message dispatch weight. + /// + /// Runtime API guarantees that it will match the value, returned by + /// `target_chain::MessageDispatch::dispatch_weight`. This means that if the runtime + /// has failed to decode the message, it will be zero - that's because `undecodable` + /// message cannot be dispatched. + pub dispatch_weight: Weight, +} + +/// Unrewarded relayer entry stored in the inbound lane data. +/// +/// This struct represents a continuous range of messages that have been delivered by the same +/// relayer and whose confirmations are still pending. +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct UnrewardedRelayer { + /// Identifier of the relayer. + pub relayer: RelayerId, + /// Messages range, delivered by this relayer. + pub messages: DeliveredMessages, +} + +/// Received messages with their dispatch result. +#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct ReceivedMessages { + /// Id of the lane which is receiving messages. + pub lane: LaneId, + /// Result of messages which we tried to dispatch + pub receive_results: Vec<(MessageNonce, ReceivalResult)>, + /// Messages which were skipped and never dispatched + pub skipped_for_not_enough_weight: Vec, +} + +impl ReceivedMessages { + pub fn new( + lane: LaneId, + receive_results: Vec<(MessageNonce, ReceivalResult)>, + ) -> Self { + ReceivedMessages { lane, receive_results, skipped_for_not_enough_weight: Vec::new() } + } + + pub fn push(&mut self, message: MessageNonce, result: ReceivalResult) { + self.receive_results.push((message, result)); + } + + pub fn push_skipped_for_not_enough_weight(&mut self, message: MessageNonce) { + self.skipped_for_not_enough_weight.push(message); + } +} + +/// Result of single message receival. +#[derive(RuntimeDebug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)] +pub enum ReceivalResult { + /// Message has been received and dispatched. Note that we don't care whether dispatch has + /// been successful or not - in both case message falls into this category. + /// + /// The message dispatch result is also returned. + Dispatched(MessageDispatchResult), + /// Message has invalid nonce and lane has rejected to accept this message. + InvalidNonce, + /// There are too many unrewarded relayer entries at the lane. + TooManyUnrewardedRelayers, + /// There are too many unconfirmed messages at the lane. + TooManyUnconfirmedMessages, +} + +/// Delivered messages with their dispatch result. +#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct DeliveredMessages { + /// Nonce of the first message that has been delivered (inclusive). + pub begin: MessageNonce, + /// Nonce of the last message that has been delivered (inclusive). + pub end: MessageNonce, +} + +impl DeliveredMessages { + /// Create new `DeliveredMessages` struct that confirms delivery of single nonce with given + /// dispatch result. + pub fn new(nonce: MessageNonce) -> Self { + DeliveredMessages { begin: nonce, end: nonce } + } + + /// Return total count of delivered messages. + pub fn total_messages(&self) -> MessageNonce { + if self.end >= self.begin { + self.end - self.begin + 1 + } else { + 0 + } + } + + /// Note new dispatched message. + pub fn note_dispatched_message(&mut self) { + self.end += 1; + } + + /// Returns true if delivered messages contain message with given nonce. + pub fn contains_message(&self, nonce: MessageNonce) -> bool { + (self.begin..=self.end).contains(&nonce) + } +} + +/// Gist of `InboundLaneData::relayers` field used by runtime APIs. +#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct UnrewardedRelayersState { + /// Number of entries in the `InboundLaneData::relayers` set. + pub unrewarded_relayer_entries: MessageNonce, + /// Number of messages in the oldest entry of `InboundLaneData::relayers`. This is the + /// minimal number of reward proofs required to push out this entry from the set. + pub messages_in_oldest_entry: MessageNonce, + /// Total number of messages in the relayers vector. + pub total_messages: MessageNonce, + /// Nonce of the latest message that has been delivered to the target chain. + /// + /// This corresponds to the result of the `InboundLaneData::last_delivered_nonce` call + /// at the bridged chain. + pub last_delivered_nonce: MessageNonce, +} + +/// Outbound lane data. +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub struct OutboundLaneData { + /// Nonce of the oldest message that we haven't yet pruned. May point to not-yet-generated + /// message if all sent messages are already pruned. + pub oldest_unpruned_nonce: MessageNonce, + /// Nonce of the latest message, received by bridged chain. + pub latest_received_nonce: MessageNonce, + /// Nonce of the latest message, generated by us. + pub latest_generated_nonce: MessageNonce, +} + +impl Default for OutboundLaneData { + fn default() -> Self { + OutboundLaneData { + // it is 1 because we're pruning everything in [oldest_unpruned_nonce; + // latest_received_nonce] + oldest_unpruned_nonce: 1, + latest_received_nonce: 0, + latest_generated_nonce: 0, + } + } +} + +/// Returns total number of messages in the `InboundLaneData::relayers` vector. +/// +/// Returns `None` if there are more messages that `MessageNonce` may fit (i.e. `MessageNonce + 1`). +pub fn total_unrewarded_messages( + relayers: &VecDeque>, +) -> Option { + match (relayers.front(), relayers.back()) { + (Some(front), Some(back)) => { + if let Some(difference) = back.messages.end.checked_sub(front.messages.begin) { + difference.checked_add(1) + } else { + Some(0) + } + }, + _ => Some(0), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn total_unrewarded_messages_does_not_overflow() { + assert_eq!( + total_unrewarded_messages( + &vec![ + UnrewardedRelayer { relayer: 1, messages: DeliveredMessages::new(0) }, + UnrewardedRelayer { + relayer: 2, + messages: DeliveredMessages::new(MessageNonce::MAX) + }, + ] + .into_iter() + .collect() + ), + None, + ); + } + + #[test] + fn inbound_lane_data_returns_correct_hint() { + let test_cases = vec![ + // single relayer, multiple messages + (1, 128u8), + // multiple relayers, single message per relayer + (128u8, 128u8), + // several messages per relayer + (13u8, 128u8), + ]; + for (relayer_entries, messages_count) in test_cases { + let expected_size = InboundLaneData::::encoded_size_hint(relayer_entries as _); + let actual_size = InboundLaneData { + relayers: (1u8..=relayer_entries) + .map(|i| UnrewardedRelayer { + relayer: i, + messages: DeliveredMessages::new(i as _), + }) + .collect(), + last_confirmed_nonce: messages_count as _, + } + .encode() + .len(); + let difference = (expected_size.unwrap() as f64 - actual_size as f64).abs(); + assert!( + difference / (std::cmp::min(actual_size, expected_size.unwrap()) as f64) < 0.1, + "Too large difference between actual ({}) and expected ({:?}) inbound lane data size. Test case: {}+{}", + actual_size, + expected_size, + relayer_entries, + messages_count, + ); + } + } + + #[test] + fn contains_result_works() { + let delivered_messages = DeliveredMessages { begin: 100, end: 150 }; + + assert!(!delivered_messages.contains_message(99)); + assert!(delivered_messages.contains_message(100)); + assert!(delivered_messages.contains_message(150)); + assert!(!delivered_messages.contains_message(151)); + } +} diff --git a/primitives/messages/src/source_chain.rs b/primitives/messages/src/source_chain.rs new file mode 100644 index 00000000000..1cac449ee63 --- /dev/null +++ b/primitives/messages/src/source_chain.rs @@ -0,0 +1,217 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives of messages module, that are used on the source chain. + +use crate::{InboundLaneData, LaneId, MessageNonce, OutboundLaneData}; + +use crate::UnrewardedRelayer; +use bp_runtime::Size; +use frame_support::{weights::Weight, Parameter, RuntimeDebug}; +use sp_std::{ + collections::{btree_map::BTreeMap, vec_deque::VecDeque}, + fmt::Debug, + ops::RangeInclusive, +}; + +/// Number of messages, delivered by relayers. +pub type RelayersRewards = BTreeMap; + +/// Target chain API. Used by source chain to verify target chain proofs. +/// +/// All implementations of this trait should only work with finalized data that +/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane +/// that's stuck) and/or processing messages without paying fees. +/// +/// The `Payload` type here means the payload of the message that is sent from the +/// source chain to the target chain. The `AccountId` type here means the account +/// type used by the source chain. +pub trait TargetHeaderChain { + /// Error type. + type Error: Debug + Into<&'static str>; + + /// Proof that messages have been received by target chain. + type MessagesDeliveryProof: Parameter + Size; + + /// Verify message payload before we accept it. + /// + /// **CAUTION**: this is very important function. Incorrect implementation may lead + /// to stuck lanes and/or relayers loses. + /// + /// The proper implementation must ensure that the delivery-transaction with this + /// payload would (at least) be accepted into target chain transaction pool AND + /// eventually will be successfully mined. The most obvious incorrect implementation + /// example would be implementation for BTC chain that accepts payloads larger than + /// 1MB. BTC nodes aren't accepting transactions that are larger than 1MB, so relayer + /// will be unable to craft valid transaction => this (and all subsequent) messages will + /// never be delivered. + fn verify_message(payload: &Payload) -> Result<(), Self::Error>; + + /// Verify messages delivery proof and return lane && nonce of the latest received message. + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData), Self::Error>; +} + +/// Lane message verifier. +/// +/// Runtime developer may implement any additional validation logic over message-lane mechanism. +/// E.g. if lanes should have some security (e.g. you can only accept Lane1 messages from +/// Submitter1, Lane2 messages for those who has submitted first message to this lane, disable +/// Lane3 until some block, ...), then it may be built using this verifier. +/// +/// Any fee requirements should also be enforced here. +pub trait LaneMessageVerifier { + /// Error type. + type Error: Debug + Into<&'static str>; + + /// Verify message payload and return Ok(()) if message is valid and allowed to be sent over the + /// lane. + fn verify_message( + submitter: &SenderOrigin, + lane: &LaneId, + outbound_data: &OutboundLaneData, + payload: &Payload, + ) -> Result<(), Self::Error>; +} + +/// Message delivery payment. It is called as a part of submit-message transaction. Transaction +/// submitter is paying (in source chain tokens/assets) for: +/// +/// 1) submit-message-transaction-fee itself. This fee is not included in the +/// `delivery_and_dispatch_fee` and is withheld by the regular transaction payment mechanism; +/// 2) message-delivery-transaction-fee. It is submitted to the target node by relayer; +/// 3) message-dispatch fee. It is paid by relayer for processing message by target chain; +/// 4) message-receiving-delivery-transaction-fee. It is submitted to the source node +/// by relayer. +pub trait MessageDeliveryAndDispatchPayment { + /// Error type. + type Error: Debug + Into<&'static str>; + + /// Pay rewards for delivering messages to the given relayers. + /// + /// The implementation may also choose to pay reward to the `confirmation_relayer`, which is + /// a relayer that has submitted delivery confirmation transaction. + fn pay_relayers_rewards( + lane_id: LaneId, + messages_relayers: VecDeque>, + confirmation_relayer: &AccountId, + received_range: &RangeInclusive, + ); +} + +impl MessageDeliveryAndDispatchPayment for () { + type Error = &'static str; + + fn pay_relayers_rewards( + _lane_id: LaneId, + _messages_relayers: VecDeque>, + _confirmation_relayer: &AccountId, + _received_range: &RangeInclusive, + ) { + } +} + +/// Send message artifacts. +#[derive(Eq, RuntimeDebug, PartialEq)] +pub struct SendMessageArtifacts { + /// Nonce of the message. + pub nonce: MessageNonce, + /// Actual weight of send message call. + pub weight: Weight, +} + +/// Messages bridge API to be used from other pallets. +pub trait MessagesBridge { + /// Error type. + type Error: Debug; + + /// Send message over the bridge. + /// + /// Returns unique message nonce or error if send has failed. + fn send_message( + sender: SenderOrigin, + lane: LaneId, + message: Payload, + ) -> Result; +} + +/// Bridge that does nothing when message is being sent. +#[derive(Eq, RuntimeDebug, PartialEq)] +pub struct NoopMessagesBridge; + +impl MessagesBridge for NoopMessagesBridge { + type Error = &'static str; + + fn send_message( + _sender: SenderOrigin, + _lane: LaneId, + _message: Payload, + ) -> Result { + Ok(SendMessageArtifacts { nonce: 0, weight: Weight::zero() }) + } +} + +/// Structure that may be used in place of `TargetHeaderChain`, `LaneMessageVerifier` and +/// `MessageDeliveryAndDispatchPayment` on chains, where outbound messages are forbidden. +pub struct ForbidOutboundMessages; + +/// Error message that is used in `ForbidOutboundMessages` implementation. +const ALL_OUTBOUND_MESSAGES_REJECTED: &str = + "This chain is configured to reject all outbound messages"; + +impl TargetHeaderChain for ForbidOutboundMessages { + type Error = &'static str; + + type MessagesDeliveryProof = (); + + fn verify_message(_payload: &Payload) -> Result<(), Self::Error> { + Err(ALL_OUTBOUND_MESSAGES_REJECTED) + } + + fn verify_messages_delivery_proof( + _proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData), Self::Error> { + Err(ALL_OUTBOUND_MESSAGES_REJECTED) + } +} + +impl LaneMessageVerifier for ForbidOutboundMessages { + type Error = &'static str; + + fn verify_message( + _submitter: &SenderOrigin, + _lane: &LaneId, + _outbound_data: &OutboundLaneData, + _payload: &Payload, + ) -> Result<(), Self::Error> { + Err(ALL_OUTBOUND_MESSAGES_REJECTED) + } +} + +impl MessageDeliveryAndDispatchPayment + for ForbidOutboundMessages +{ + type Error = &'static str; + + fn pay_relayers_rewards( + _lane_id: LaneId, + _messages_relayers: VecDeque>, + _confirmation_relayer: &AccountId, + _received_range: &RangeInclusive, + ) { + } +} diff --git a/primitives/messages/src/storage_keys.rs b/primitives/messages/src/storage_keys.rs new file mode 100644 index 00000000000..4db11edec34 --- /dev/null +++ b/primitives/messages/src/storage_keys.rs @@ -0,0 +1,128 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Storage keys of bridge messages pallet. + +/// Name of the `OPERATING_MODE_VALUE_NAME` storage value. +pub const OPERATING_MODE_VALUE_NAME: &str = "PalletOperatingMode"; +/// Name of the `OutboundMessages` storage map. +pub const OUTBOUND_MESSAGES_MAP_NAME: &str = "OutboundMessages"; +/// Name of the `OutboundLanes` storage map. +pub const OUTBOUND_LANES_MAP_NAME: &str = "OutboundLanes"; +/// Name of the `InboundLanes` storage map. +pub const INBOUND_LANES_MAP_NAME: &str = "InboundLanes"; + +use crate::{LaneId, MessageKey, MessageNonce}; + +use codec::Encode; +use frame_support::Blake2_128Concat; +use sp_core::storage::StorageKey; + +/// Storage key of the `PalletOperatingMode` value in the runtime storage. +pub fn operating_mode_key(pallet_prefix: &str) -> StorageKey { + StorageKey( + bp_runtime::storage_value_final_key( + pallet_prefix.as_bytes(), + OPERATING_MODE_VALUE_NAME.as_bytes(), + ) + .to_vec(), + ) +} + +/// Storage key of the outbound message in the runtime storage. +pub fn message_key(pallet_prefix: &str, lane: &LaneId, nonce: MessageNonce) -> StorageKey { + bp_runtime::storage_map_final_key::( + pallet_prefix, + OUTBOUND_MESSAGES_MAP_NAME, + &MessageKey { lane_id: *lane, nonce }.encode(), + ) +} + +/// Storage key of the outbound message lane state in the runtime storage. +pub fn outbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { + bp_runtime::storage_map_final_key::( + pallet_prefix, + OUTBOUND_LANES_MAP_NAME, + lane, + ) +} + +/// Storage key of the inbound message lane state in the runtime storage. +pub fn inbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { + bp_runtime::storage_map_final_key::( + pallet_prefix, + INBOUND_LANES_MAP_NAME, + lane, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use hex_literal::hex; + + #[test] + fn operating_mode_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is possibly + // breaking all existing message relays. + let storage_key = operating_mode_key("BridgeMessages").0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed010f4cf0917788d791142ff6c1f216e7b3").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } + + #[test] + fn storage_message_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is breaking + // all previously crafted messages proofs. + let storage_key = message_key("BridgeMessages", b"test", 42).0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } + + #[test] + fn outbound_lane_data_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is breaking + // all previously crafted outbound lane state proofs. + let storage_key = outbound_lane_data_key("BridgeMessages", b"test").0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1f44a8995dd50b6657a037a7839304535b74657374").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } + + #[test] + fn inbound_lane_data_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is breaking + // all previously crafted inbound lane state proofs. + let storage_key = inbound_lane_data_key("BridgeMessages", b"test").0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } +} diff --git a/primitives/messages/src/target_chain.rs b/primitives/messages/src/target_chain.rs new file mode 100644 index 00000000000..9c6b60e1e15 --- /dev/null +++ b/primitives/messages/src/target_chain.rs @@ -0,0 +1,171 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives of messages module, that are used on the target chain. + +use crate::{LaneId, Message, MessageKey, MessagePayload, OutboundLaneData}; + +use bp_runtime::{messages::MessageDispatchResult, Size}; +use codec::{Decode, Encode, Error as CodecError}; +use frame_support::{weights::Weight, Parameter, RuntimeDebug}; +use scale_info::TypeInfo; +use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, prelude::*}; + +/// Proved messages from the source chain. +pub type ProvedMessages = BTreeMap>; + +/// Proved messages from single lane of the source chain. +#[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] +pub struct ProvedLaneMessages { + /// Optional outbound lane state. + pub lane_state: Option, + /// Messages sent through this lane. + pub messages: Vec, +} + +/// Message data with decoded dispatch payload. +#[derive(RuntimeDebug)] +pub struct DispatchMessageData { + /// Result of dispatch payload decoding. + pub payload: Result, +} + +/// Message with decoded dispatch payload. +#[derive(RuntimeDebug)] +pub struct DispatchMessage { + /// Message key. + pub key: MessageKey, + /// Message data with decoded dispatch payload. + pub data: DispatchMessageData, +} + +/// Source chain API. Used by target chain, to verify source chain proofs. +/// +/// All implementations of this trait should only work with finalized data that +/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane +/// that's stuck) and/or processing messages without paying fees. +pub trait SourceHeaderChain { + /// Error type. + type Error: Debug + Into<&'static str>; + + /// Proof that messages are sent from source chain. This may also include proof + /// of corresponding outbound lane states. + type MessagesProof: Parameter + Size; + + /// Verify messages proof and return proved messages. + /// + /// Returns error if either proof is incorrect, or the number of messages in the proof + /// is not matching the `messages_count`. + /// + /// Messages vector is required to be sorted by nonce within each lane. Out-of-order + /// messages will be rejected. + /// + /// The `messages_count` argument verification (sane limits) is supposed to be made + /// outside this function. This function only verifies that the proof declares exactly + /// `messages_count` messages. + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result, Self::Error>; +} + +/// Called when inbound message is received. +pub trait MessageDispatch { + /// Decoded message payload type. Valid message may contain invalid payload. In this case + /// message is delivered, but dispatch fails. Therefore, two separate types of payload + /// (opaque `MessagePayload` used in delivery and this `DispatchPayload` used in dispatch). + type DispatchPayload: Decode; + + /// Fine-grained result of single message dispatch (for better diagnostic purposes) + type DispatchLevelResult: Clone + sp_std::fmt::Debug + Eq; + + /// Estimate dispatch weight. + /// + /// This function must return correct upper bound of dispatch weight. The return value + /// of this function is expected to match return value of the corresponding + /// `FromInboundLaneApi::message_details().dispatch_weight` call. + fn dispatch_weight(message: &mut DispatchMessage) -> Weight; + + /// Called when inbound message is received. + /// + /// It is up to the implementers of this trait to determine whether the message + /// is invalid (i.e. improperly encoded, has too large weight, ...) or not. + /// + /// If your configuration allows paying dispatch fee at the target chain, then + /// it must be paid inside this method to the `relayer_account`. + fn dispatch( + relayer_account: &AccountId, + message: DispatchMessage, + ) -> MessageDispatchResult; +} + +impl Default for ProvedLaneMessages { + fn default() -> Self { + ProvedLaneMessages { lane_state: None, messages: Vec::new() } + } +} + +impl From for DispatchMessage { + fn from(message: Message) -> Self { + DispatchMessage { key: message.key, data: message.payload.into() } + } +} + +impl From for DispatchMessageData { + fn from(payload: MessagePayload) -> Self { + DispatchMessageData { payload: DispatchPayload::decode(&mut &payload[..]) } + } +} + +/// Structure that may be used in place of `SourceHeaderChain` and `MessageDispatch` on chains, +/// where inbound messages are forbidden. +pub struct ForbidInboundMessages; + +/// Error message that is used in `ForbidOutboundMessages` implementation. +const ALL_INBOUND_MESSAGES_REJECTED: &str = + "This chain is configured to reject all inbound messages"; + +impl SourceHeaderChain for ForbidInboundMessages { + type Error = &'static str; + type MessagesProof = (); + + fn verify_messages_proof( + _proof: Self::MessagesProof, + _messages_count: u32, + ) -> Result, Self::Error> { + Err(ALL_INBOUND_MESSAGES_REJECTED) + } +} + +impl MessageDispatch for ForbidInboundMessages { + type DispatchPayload = (); + type DispatchLevelResult = (); + + fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { + Weight::MAX + } + + fn dispatch( + _: &AccountId, + _: DispatchMessage, + ) -> MessageDispatchResult { + MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_fee_paid_during_dispatch: false, + dispatch_level_result: (), + } + } +} diff --git a/primitives/parachains/Cargo.toml b/primitives/parachains/Cargo.toml new file mode 100644 index 00000000000..0aa2adfead1 --- /dev/null +++ b/primitives/parachains/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "bp-parachains" +description = "Primitives of parachains module." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } + +# Bridge dependencies + +bp-polkadot-core = { path = "../polkadot-core", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-polkadot-core/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "scale-info/std", + "sp-core/std", +] diff --git a/primitives/parachains/src/lib.rs b/primitives/parachains/src/lib.rs new file mode 100644 index 00000000000..f2edebf8a22 --- /dev/null +++ b/primitives/parachains/src/lib.rs @@ -0,0 +1,90 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives of parachains module. + +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_polkadot_core::{ + parachains::{ParaHash, ParaHead, ParaId}, + BlockNumber as RelayBlockNumber, +}; +use bp_runtime::{StorageDoubleMapKeyProvider, StorageMapKeyProvider}; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{Blake2_128Concat, RuntimeDebug, Twox64Concat}; +use scale_info::TypeInfo; +use sp_core::storage::StorageKey; + +/// Best known parachain head hash. +#[derive(Clone, Decode, Encode, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] +pub struct BestParaHeadHash { + /// Number of relay block where this head has been read. + /// + /// Parachain head is opaque to relay chain. So we can't simply decode it as a header of + /// parachains and call `block_number()` on it. Instead, we're using the fact that parachain + /// head is always built on top of previous head (because it is blockchain) and relay chain + /// always imports parachain heads in order. What it means for us is that at any given + /// **finalized** relay block `B`, head of parachain will be ancestor (or the same) of all + /// parachain heads available at descendants of `B`. + pub at_relay_block_number: RelayBlockNumber, + /// Hash of parachain head. + pub head_hash: ParaHash, +} + +/// Best known parachain head as it is stored in the runtime storage. +#[derive(Decode, Encode, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] +pub struct ParaInfo { + /// Best known parachain head hash. + pub best_head_hash: BestParaHeadHash, + /// Current ring buffer position for this parachain. + pub next_imported_hash_position: u32, +} + +/// Returns runtime storage key of given parachain head at the source chain. +/// +/// The head is stored by the `paras` pallet in the `Heads` map. +pub fn parachain_head_storage_key_at_source( + paras_pallet_name: &str, + para_id: ParaId, +) -> StorageKey { + bp_runtime::storage_map_final_key::(paras_pallet_name, "Heads", ¶_id.encode()) +} + +/// Can be use to access the runtime storage key of the parachains info at the target chain. +/// +/// The info is stored by the `pallet-bridge-parachains` pallet in the `ParasInfo` map. +pub struct ParasInfoKeyProvider; +impl StorageMapKeyProvider for ParasInfoKeyProvider { + const MAP_NAME: &'static str = "ParasInfo"; + + type Hasher = Blake2_128Concat; + type Key = ParaId; + type Value = ParaInfo; +} + +/// Can be use to access the runtime storage key of the parachain head at the target chain. +/// +/// The head is stored by the `pallet-bridge-parachains` pallet in the `ImportedParaHeads` map. +pub struct ImportedParaHeadsKeyProvider; +impl StorageDoubleMapKeyProvider for ImportedParaHeadsKeyProvider { + const MAP_NAME: &'static str = "ImportedParaHeads"; + + type Hasher1 = Blake2_128Concat; + type Key1 = ParaId; + type Hasher2 = Blake2_128Concat; + type Key2 = ParaHash; + type Value = ParaHead; +} diff --git a/primitives/polkadot-core/Cargo.toml b/primitives/polkadot-core/Cargo.toml new file mode 100644 index 00000000000..a9db53a8bf0 --- /dev/null +++ b/primitives/polkadot-core/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "bp-polkadot-core" +description = "Primitives of Polkadot-like runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } +parity-util-mem = { version = "0.12.0", optional = true } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } + +# Bridge Dependencies + +bp-messages = { path = "../messages", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +hex = "0.4" + +[features] +default = ["std"] +std = [ + "bp-messages/std", + "bp-runtime/std", + "frame-support/std", + "frame-system/std", + "codec/std", + "parity-util-mem", + "scale-info/std", + "serde", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/primitives/polkadot-core/src/lib.rs b/primitives/polkadot-core/src/lib.rs new file mode 100644 index 00000000000..9e1f4b0452f --- /dev/null +++ b/primitives/polkadot-core/src/lib.rs @@ -0,0 +1,387 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_messages::MessageNonce; +use bp_runtime::{Chain, EncodedOrDecodedCall, StorageMapKeyProvider}; +use codec::Compact; +use frame_support::{ + dispatch::{DispatchClass, Dispatchable}, + parameter_types, + weights::{ + constants::{BlockExecutionWeight, WEIGHT_PER_SECOND}, + Weight, + }, + Blake2_128Concat, RuntimeDebug, +}; +use frame_system::limits; +use scale_info::{StaticTypeInfo, TypeInfo}; +use sp_core::{storage::StorageKey, Hasher as HasherT}; +use sp_runtime::{ + generic, + traits::{BlakeTwo256, DispatchInfoOf, IdentifyAccount, Verify}, + transaction_validity::TransactionValidityError, + MultiAddress, MultiSignature, OpaqueExtrinsic, +}; +use sp_std::prelude::Vec; + +// Re-export's to avoid extra substrate dependencies in chain-specific crates. +pub use frame_support::{weights::constants::ExtrinsicBaseWeight, Parameter}; +pub use sp_runtime::{traits::Convert, Perbill}; + +pub mod parachains; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// Polkadot-like chain. This mostly depends on number of entries in the storage trie. +/// Some reserve is reserved to account future chain growth. +/// +/// To compute this value, we've synced Kusama chain blocks [0; 6545733] to see if there were +/// any significant changes of the storage proof size (NO): +/// +/// - at block 3072 the storage proof size overhead was 579 bytes; +/// - at block 2479616 it was 578 bytes; +/// - at block 4118528 it was 711 bytes; +/// - at block 6540800 it was 779 bytes. +/// +/// The number of storage entries at the block 6546170 was 351207 and number of trie nodes in +/// the storage proof was 5 (log(16, 351207) ~ 4.6). +/// +/// So the assumption is that the storage proof size overhead won't be larger than 1024 in the +/// nearest future. If it'll ever break this barrier, then we'll need to update this constant +/// at next runtime upgrade. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// All Polkadot-like chains allow normal extrinsics to fill block up to 75 percent. +/// +/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// All Polkadot-like chains allow 2 seconds of compute with a 6-second average block time. +/// +/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. +// TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` +pub const MAXIMUM_BLOCK_WEIGHT: Weight = WEIGHT_PER_SECOND.set_proof_size(1_000).saturating_mul(2); + +/// All Polkadot-like chains assume that an on-initialize consumes 1 percent of the weight on +/// average, hence a single extrinsic will not be allowed to consume more than +/// `AvailableBlockRatio - 1 percent`. +/// +/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. +pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(1); + +parameter_types! { + /// All Polkadot-like chains have maximal block size set to 5MB. + /// + /// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. + pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio( + 5 * 1024 * 1024, + NORMAL_DISPATCH_RATIO, + ); + /// All Polkadot-like chains have the same block weights. + /// + /// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. + pub BlockWeights: limits::BlockWeights = limits::BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + // Operational transactions have an extra reserved space, so that they + // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT, + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); +} + +// TODO [#78] may need to be updated after https://github.com/paritytech/parity-bridges-common/issues/78 +/// Maximal number of messages in single delivery transaction. +pub const MAX_MESSAGES_IN_DELIVERY_TRANSACTION: MessageNonce = 128; + +/// Maximal number of unrewarded relayer entries at inbound lane. +pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 128; + +// TODO [#438] should be selected keeping in mind: +// finality delay on both chains + reward payout cost + messages throughput. +/// Maximal number of unconfirmed messages at inbound lane. +pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 8192; + +/// Maximal number of bytes, included in the signed Polkadot-like transaction apart from the encoded +/// call itself. +/// +/// Can be computed by subtracting encoded call size from raw transaction size. +pub const TX_EXTRA_BYTES: u32 = 256; + +/// Re-export `time_units` to make usage easier. +pub use time_units::*; + +/// Human readable time units defined in terms of number of blocks. +pub mod time_units { + use super::BlockNumber; + + pub const MILLISECS_PER_BLOCK: u64 = 6000; + pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + + pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); + pub const HOURS: BlockNumber = MINUTES * 60; + pub const DAYS: BlockNumber = HOURS * 24; +} + +/// Block number type used in Polkadot-like chains. +pub type BlockNumber = u32; + +/// Hash type used in Polkadot-like chains. +pub type Hash = ::Out; + +/// Account Index (a.k.a. nonce). +pub type Index = u32; + +/// Hashing type. +pub type Hashing = BlakeTwo256; + +/// The type of object that can produce hashes on Polkadot-like chains. +pub type Hasher = BlakeTwo256; + +/// The header type used by Polkadot-like chains. +pub type Header = generic::Header; + +/// Signature type used by Polkadot-like chains. +pub type Signature = MultiSignature; + +/// Public key of account on Polkadot-like chains. +pub type AccountPublic = ::Signer; + +/// Id of account on Polkadot-like chains. +pub type AccountId = ::AccountId; + +/// Address of account on Polkadot-like chains. +pub type AccountAddress = MultiAddress; + +/// Index of a transaction on the Polkadot-like chains. +pub type Nonce = u32; + +/// Block type of Polkadot-like chains. +pub type Block = generic::Block; + +/// Polkadot-like block signed with a Justification. +pub type SignedBlock = generic::SignedBlock; + +/// The balance of an account on Polkadot-like chain. +pub type Balance = u128; + +/// Unchecked Extrinsic type. +pub type UncheckedExtrinsic = generic::UncheckedExtrinsic< + AccountAddress, + EncodedOrDecodedCall, + Signature, + SignedExtensions, +>; + +/// Account address, used by the Polkadot-like chain. +pub type Address = MultiAddress; + +/// A type of the data encoded as part of the transaction. +pub type SignedExtra = + ((), (), (), (), sp_runtime::generic::Era, Compact, (), Compact); + +/// Parameters which are part of the payload used to produce transaction signature, +/// but don't end up in the transaction itself (i.e. inherent part of the runtime). +pub type AdditionalSigned = ((), u32, u32, Hash, Hash, (), (), ()); + +/// A simplified version of signed extensions meant for producing signed transactions +/// and signed payload in the client code. +#[derive(PartialEq, Eq, Clone, RuntimeDebug, TypeInfo)] +pub struct SignedExtensions { + encode_payload: SignedExtra, + // It may be set to `None` if extensions are decoded. We are never reconstructing transactions + // (and it makes no sense to do that) => decoded version of `SignedExtensions` is only used to + // read fields of `encode_payload`. And when resigning transaction, we're reconstructing + // `SignedExtensions` from the scratch. + additional_signed: Option, + _data: sp_std::marker::PhantomData, +} + +impl codec::Encode for SignedExtensions { + fn using_encoded R>(&self, f: F) -> R { + self.encode_payload.using_encoded(f) + } +} + +impl codec::Decode for SignedExtensions { + fn decode(input: &mut I) -> Result { + SignedExtra::decode(input).map(|encode_payload| SignedExtensions { + encode_payload, + additional_signed: None, + _data: Default::default(), + }) + } +} + +impl SignedExtensions { + pub fn new( + spec_version: u32, + transaction_version: u32, + era: bp_runtime::TransactionEraOf, + genesis_hash: Hash, + nonce: Nonce, + tip: Balance, + ) -> Self { + Self { + encode_payload: ( + (), // non-zero sender + (), // spec version + (), // tx version + (), // genesis + era.frame_era(), // era + nonce.into(), // nonce (compact encoding) + (), // Check weight + tip.into(), // transaction payment / tip (compact encoding) + ), + additional_signed: Some(( + (), + spec_version, + transaction_version, + genesis_hash, + era.signed_payload(genesis_hash), + (), + (), + (), + )), + _data: Default::default(), + } + } +} + +impl SignedExtensions { + /// Return signer nonce, used to craft transaction. + pub fn nonce(&self) -> Nonce { + self.encode_payload.5.into() + } + + /// Return transaction tip. + pub fn tip(&self) -> Balance { + self.encode_payload.7.into() + } +} + +impl sp_runtime::traits::SignedExtension for SignedExtensions +where + Call: codec::Codec + sp_std::fmt::Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, + Call: Dispatchable, +{ + const IDENTIFIER: &'static str = "Not needed."; + + type AccountId = AccountId; + type Call = Call; + type AdditionalSigned = AdditionalSigned; + type Pre = (); + + fn additional_signed( + &self, + ) -> Result { + // we shall not ever see this error in relay, because we are never signing decoded + // transactions. Instead we're constructing and signing new transactions. So the error code + // is kinda random here + self.additional_signed + .ok_or(frame_support::unsigned::TransactionValidityError::Unknown( + frame_support::unsigned::UnknownTransaction::Custom(0xFF), + )) + } + + fn pre_dispatch( + self, + _who: &Self::AccountId, + _call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + Ok(()) + } +} + +/// Polkadot-like chain. +#[derive(RuntimeDebug)] +pub struct PolkadotLike; + +impl Chain for PolkadotLike { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +/// Provides a storage key for account data. +/// +/// We need to use this approach when we don't have access to the runtime. +/// The equivalent command to invoke in case full `Runtime` is known is this: +/// `let key = frame_system::Account::::storage_map_final_key(&account_id);` +pub struct AccountInfoStorageMapKeyProvider; + +impl StorageMapKeyProvider for AccountInfoStorageMapKeyProvider { + const MAP_NAME: &'static str = "Account"; + type Hasher = Blake2_128Concat; + type Key = AccountId; + // This should actually be `AccountInfo`, but we don't use this property in order to decode the + // data. So we use `Vec` as if we would work with encoded data. + type Value = Vec; +} + +impl AccountInfoStorageMapKeyProvider { + const PALLET_NAME: &'static str = "System"; + + pub fn final_key(id: &AccountId) -> StorageKey { + ::final_key(Self::PALLET_NAME, id) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_generate_storage_key() { + let acc = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, + ] + .into(); + let key = AccountInfoStorageMapKeyProvider::final_key(&acc); + assert_eq!(hex::encode(key), "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92dccd599abfe1920a1cff8a7358231430102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"); + } +} diff --git a/primitives/polkadot-core/src/parachains.rs b/primitives/polkadot-core/src/parachains.rs new file mode 100644 index 00000000000..51fcd59cae1 --- /dev/null +++ b/primitives/polkadot-core/src/parachains.rs @@ -0,0 +1,100 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives of polkadot-like chains, that are related to parachains functionality. +//! +//! Even though this (bridges) repository references polkadot repository, we can't +//! reference polkadot crates from pallets. That's because bridges repository is +//! included in the polkadot repository and included pallets are used by polkadot +//! chains. Having pallets that are referencing polkadot, would mean that there may +//! be two versions of polkadot crates included in the runtime. Which is bad. + +use bp_runtime::Size; +use codec::{CompactAs, Decode, Encode, MaxEncodedLen}; +use frame_support::RuntimeDebug; +use scale_info::TypeInfo; +use sp_core::Hasher; +use sp_std::vec::Vec; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "std")] +use parity_util_mem::MallocSizeOf; + +/// Parachain id. +/// +/// This is an equivalent of the `polkadot_parachain::Id`, which is a compact-encoded `u32`. +#[derive( + Clone, + CompactAs, + Copy, + Decode, + Default, + Encode, + Eq, + Hash, + MaxEncodedLen, + Ord, + PartialEq, + PartialOrd, + RuntimeDebug, + TypeInfo, +)] +pub struct ParaId(pub u32); + +impl From for ParaId { + fn from(id: u32) -> Self { + ParaId(id) + } +} + +/// Parachain head. +/// +/// This is an equivalent of the `polkadot_parachain::HeadData`. +/// +/// The parachain head means (at least in Cumulus) a SCALE-encoded parachain header. Keep in mind +/// that in Polkadot it is twice-encoded (so `header.encode().encode()`). We'll also do it to keep +/// it binary-compatible (implies hash-compatibility) with other parachain pallets. +#[derive( + PartialEq, Eq, Clone, PartialOrd, Ord, Encode, Decode, RuntimeDebug, TypeInfo, Default, +)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, MallocSizeOf))] +pub struct ParaHead(pub Vec); + +impl ParaHead { + /// Returns the hash of this head data. + pub fn hash(&self) -> crate::Hash { + sp_runtime::traits::BlakeTwo256::hash(&self.0) + } +} + +/// Parachain head hash. +pub type ParaHash = crate::Hash; + +/// Parachain head hasher. +pub type ParaHasher = crate::Hasher; + +/// Raw storage proof of parachain heads, stored in polkadot-like chain runtime. +#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] +pub struct ParaHeadsProof(pub Vec>); + +impl Size for ParaHeadsProof { + fn size(&self) -> u32 { + u32::try_from(self.0.iter().fold(0usize, |sum, node| sum.saturating_add(node.len()))) + .unwrap_or(u32::MAX) + } +} diff --git a/primitives/relayers/Cargo.toml b/primitives/relayers/Cargo.toml new file mode 100644 index 00000000000..4dc09f8d126 --- /dev/null +++ b/primitives/relayers/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "bp-relayers" +description = "Primitives of relayers module." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-messages = { path = "../messages", default-features = false } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +hex = "0.4" +hex-literal = "0.3" + +[features] +default = ["std"] +std = [ + "bp-messages/std", + "frame-support/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/primitives/relayers/src/lib.rs b/primitives/relayers/src/lib.rs new file mode 100644 index 00000000000..bd456d36310 --- /dev/null +++ b/primitives/relayers/src/lib.rs @@ -0,0 +1,50 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives of messages module. + +#![warn(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_messages::LaneId; +use sp_std::{fmt::Debug, marker::PhantomData}; + +/// Reward payment procedure. +pub trait PaymentProcedure { + /// Error that may be returned by the procedure. + type Error: Debug; + + /// Pay reward to the relayer for serving given message lane. + fn pay_reward(relayer: &Relayer, lane_id: LaneId, reward: Reward) -> Result<(), Self::Error>; +} + +/// Reward payment procedure that is simply minting given amount of tokens. +pub struct MintReward(PhantomData<(T, Relayer)>); + +impl PaymentProcedure for MintReward +where + T: frame_support::traits::fungible::Mutate, +{ + type Error = sp_runtime::DispatchError; + + fn pay_reward( + relayer: &Relayer, + _lane_id: LaneId, + reward: T::Balance, + ) -> Result<(), Self::Error> { + T::mint_into(relayer, reward) + } +} diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml new file mode 100644 index 00000000000..79f2b9fe03c --- /dev/null +++ b/primitives/runtime/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "bp-runtime" +description = "Primitives that may be used at (bridges) runtime level." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +hash-db = { version = "0.15.2", default-features = false } +num-traits = { version = "0.2", default-features = false } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +trie-db = { version = "0.24.0", default-features = false } + +[dev-dependencies] +hex-literal = "0.3" + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "hash-db/std", + "num-traits/std", + "scale-info/std", + "serde", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "sp-state-machine/std", + "sp-trie/std", + "trie-db/std", +] diff --git a/primitives/runtime/src/chain.rs b/primitives/runtime/src/chain.rs new file mode 100644 index 00000000000..d4a8d5aa020 --- /dev/null +++ b/primitives/runtime/src/chain.rs @@ -0,0 +1,347 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::HeaderIdProvider; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{weights::Weight, Parameter}; +use num_traits::{AsPrimitive, Bounded, CheckedSub, Saturating, SaturatingAdd, Zero}; +use sp_runtime::{ + traits::{ + AtLeast32Bit, AtLeast32BitUnsigned, Hash as HashT, Header as HeaderT, MaybeDisplay, + MaybeMallocSizeOf, MaybeSerialize, MaybeSerializeDeserialize, Member, SimpleBitOps, Verify, + }, + FixedPointOperand, +}; +use sp_std::{convert::TryFrom, fmt::Debug, hash::Hash, str::FromStr, vec, vec::Vec}; + +/// Chain call, that is either SCALE-encoded, or decoded. +#[derive(Debug, Clone, PartialEq)] +pub enum EncodedOrDecodedCall { + /// The call that is SCALE-encoded. + /// + /// This variant is used when we the chain runtime is not bundled with the relay, but + /// we still need the represent call in some RPC calls or transactions. + Encoded(Vec), + /// The decoded call. + Decoded(ChainCall), +} + +impl EncodedOrDecodedCall { + /// Returns decoded call. + pub fn to_decoded(&self) -> Result { + match self { + Self::Encoded(ref encoded_call) => + ChainCall::decode(&mut &encoded_call[..]).map_err(Into::into), + Self::Decoded(ref decoded_call) => Ok(decoded_call.clone()), + } + } + + /// Converts self to decoded call. + pub fn into_decoded(self) -> Result { + match self { + Self::Encoded(encoded_call) => + ChainCall::decode(&mut &encoded_call[..]).map_err(Into::into), + Self::Decoded(decoded_call) => Ok(decoded_call), + } + } +} + +impl From for EncodedOrDecodedCall { + fn from(call: ChainCall) -> EncodedOrDecodedCall { + EncodedOrDecodedCall::Decoded(call) + } +} + +impl Decode for EncodedOrDecodedCall { + fn decode(input: &mut I) -> Result { + // having encoded version is better than decoded, because decoding isn't required + // everywhere and for mocked calls it may lead to **unneeded** errors + match input.remaining_len()? { + Some(remaining_len) => { + let mut encoded_call = vec![0u8; remaining_len]; + input.read(&mut encoded_call)?; + Ok(EncodedOrDecodedCall::Encoded(encoded_call)) + }, + None => Ok(EncodedOrDecodedCall::Decoded(ChainCall::decode(input)?)), + } + } +} + +impl Encode for EncodedOrDecodedCall { + fn encode(&self) -> Vec { + match *self { + Self::Encoded(ref encoded_call) => encoded_call.clone(), + Self::Decoded(ref decoded_call) => decoded_call.encode(), + } + } +} + +/// Minimal Substrate-based chain representation that may be used from no_std environment. +pub trait Chain: Send + Sync + 'static { + /// A type that fulfills the abstract idea of what a Substrate block number is. + // Constraits come from the associated Number type of `sp_runtime::traits::Header` + // See here for more info: + // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Number + // + // Note that the `AsPrimitive` trait is required by the GRANDPA justification + // verifier, and is not usually part of a Substrate Header's Number type. + type BlockNumber: Parameter + + Member + + MaybeSerializeDeserialize + + Hash + + Copy + + Default + + MaybeDisplay + + AtLeast32BitUnsigned + + FromStr + + MaybeMallocSizeOf + + AsPrimitive + + Default + + Saturating + + MaxEncodedLen + // original `sp_runtime::traits::Header::BlockNumber` doesn't have this trait, but + // `sp_runtime::generic::Era` requires block number -> `u64` conversion. + + Into; + + /// A type that fulfills the abstract idea of what a Substrate hash is. + // Constraits come from the associated Hash type of `sp_runtime::traits::Header` + // See here for more info: + // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Hash + type Hash: Parameter + + Member + + MaybeSerializeDeserialize + + Hash + + Ord + + Copy + + MaybeDisplay + + Default + + SimpleBitOps + + AsRef<[u8]> + + AsMut<[u8]> + + MaybeMallocSizeOf + + MaxEncodedLen; + + /// A type that fulfills the abstract idea of what a Substrate hasher (a type + /// that produces hashes) is. + // Constraits come from the associated Hashing type of `sp_runtime::traits::Header` + // See here for more info: + // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Hashing + type Hasher: HashT; + + /// A type that fulfills the abstract idea of what a Substrate header is. + // See here for more info: + // https://crates.parity.io/sp_runtime/traits/trait.Header.html + type Header: Parameter + + HeaderT + + HeaderIdProvider + + MaybeSerializeDeserialize; + + /// The user account identifier type for the runtime. + type AccountId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + Ord + + MaxEncodedLen; + /// Balance of an account in native tokens. + /// + /// The chain may support multiple tokens, but this particular type is for token that is used + /// to pay for transaction dispatch, to reward different relayers (headers, messages), etc. + type Balance: AtLeast32BitUnsigned + + FixedPointOperand + + Parameter + + Member + + MaybeSerializeDeserialize + + Clone + + Copy + + Bounded + + CheckedSub + + PartialOrd + + SaturatingAdd + + Zero + + TryFrom + + MaxEncodedLen; + /// Index of a transaction used by the chain. + type Index: Parameter + + Member + + MaybeSerialize + + Debug + + Default + + MaybeDisplay + + MaybeSerializeDeserialize + + AtLeast32Bit + + Copy + + MaxEncodedLen; + /// Signature type, used on this chain. + type Signature: Parameter + Verify; + + /// Get the maximum size (in bytes) of a Normal extrinsic at this chain. + fn max_extrinsic_size() -> u32; + /// Get the maximum weight (compute time) that a Normal extrinsic at this chain can use. + fn max_extrinsic_weight() -> Weight; +} + +/// Minimal parachain representation that may be used from no_std environment. +pub trait Parachain: Chain { + /// Parachain identifier. + const PARACHAIN_ID: u32; +} + +/// Block number used by the chain. +pub type BlockNumberOf = ::BlockNumber; + +/// Hash type used by the chain. +pub type HashOf = ::Hash; + +/// Hasher type used by the chain. +pub type HasherOf = ::Hasher; + +/// Header type used by the chain. +pub type HeaderOf = ::Header; + +/// Account id type used by the chain. +pub type AccountIdOf = ::AccountId; + +/// Balance type used by the chain. +pub type BalanceOf = ::Balance; + +/// Transaction index type used by the chain. +pub type IndexOf = ::Index; + +/// Signature type used by the chain. +pub type SignatureOf = ::Signature; + +/// Account public type used by the chain. +pub type AccountPublicOf = as Verify>::Signer; + +/// Transaction era used by the chain. +pub type TransactionEraOf = crate::TransactionEra, HashOf>; + +/// Convenience macro that declares bridge finality runtime apis and related constants for a chain. +/// This includes: +/// - chain-specific bridge runtime APIs: +/// - `FinalityApi` +/// - constants that are stringified names of runtime API methods: +/// - `BEST_FINALIZED__HEADER_METHOD` +/// The name of the chain has to be specified in snake case (e.g. `rialto_parachain`). +#[macro_export] +macro_rules! decl_bridge_finality_runtime_apis { + ($chain: ident) => { + bp_runtime::paste::item! { + mod [<$chain _finality_api>] { + use super::*; + + /// Name of the `FinalityApi::best_finalized` runtime method. + pub const []: &str = + stringify!([<$chain:camel FinalityApi_best_finalized>]); + + sp_api::decl_runtime_apis! { + /// API for querying information about the finalized chain headers. + /// + /// This API is implemented by runtimes that are receiving messages from this chain, not by this + /// chain's runtime itself. + pub trait [<$chain:camel FinalityApi>] { + /// Returns number and hash of the best finalized header known to the bridge module. + fn best_finalized() -> Option>; + } + } + } + + pub use [<$chain _finality_api>]::*; + } + }; +} + +/// Convenience macro that declares bridge messages runtime apis and related constants for a chain. +/// This includes: +/// - chain-specific bridge runtime APIs: +/// - `ToOutboundLaneApi` +/// - `FromInboundLaneApi` +/// - constants that are stringified names of runtime API methods: +/// - `TO__ESTIMATE_MESSAGE_FEE_METHOD` +/// - `TO__MESSAGE_DETAILS_METHOD` +/// - `FROM__MESSAGE_DETAILS_METHOD`, +/// The name of the chain has to be specified in snake case (e.g. `rialto_parachain`). +#[macro_export] +macro_rules! decl_bridge_messages_runtime_apis { + ($chain: ident) => { + bp_runtime::paste::item! { + mod [<$chain _messages_api>] { + use super::*; + + /// Name of the `ToOutboundLaneApi::estimate_message_delivery_and_dispatch_fee` runtime + /// method. + pub const []: &str = + stringify!([]); + /// Name of the `ToOutboundLaneApi::message_details` runtime method. + pub const []: &str = + stringify!([]); + + /// Name of the `FromInboundLaneApi::message_details` runtime method. + pub const []: &str = + stringify!([]); + + sp_api::decl_runtime_apis! { + /// Outbound message lane API for messages that are sent to this chain. + /// + /// This API is implemented by runtimes that are receiving messages from this chain, not by this + /// chain's runtime itself. + pub trait [] { + /// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all + /// messages in given inclusive range. + /// + /// If some (or all) messages are missing from the storage, they'll also will + /// be missing from the resulting vector. The vector is ordered by the nonce. + fn message_details( + lane: LaneId, + begin: MessageNonce, + end: MessageNonce, + ) -> Vec; + } + + /// Inbound message lane API for messages sent by this chain. + /// + /// This API is implemented by runtimes that are receiving messages from this chain, not by this + /// chain's runtime itself. + /// + /// Entries of the resulting vector are matching entries of the `messages` vector. Entries of the + /// `messages` vector may (and need to) be read using `ToOutboundLaneApi::message_details`. + pub trait [] { + /// Return details of given inbound messages. + fn message_details( + lane: LaneId, + messages: Vec<(MessagePayload, OutboundMessageDetails)>, + ) -> Vec; + } + } + } + + pub use [<$chain _messages_api>]::*; + } + }; +} + +/// Convenience macro that declares bridge finality runtime apis, bridge messages runtime apis +/// and related constants for a chain. +/// The name of the chain has to be specified in snake case (e.g. `rialto_parachain`). +#[macro_export] +macro_rules! decl_bridge_runtime_apis { + ($chain: ident) => { + bp_runtime::decl_bridge_finality_runtime_apis!($chain); + bp_runtime::decl_bridge_messages_runtime_apis!($chain); + }; +} diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs new file mode 100644 index 00000000000..75d75c2beff --- /dev/null +++ b/primitives/runtime/src/lib.rs @@ -0,0 +1,508 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives that may be used at (bridges) runtime level. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode, FullCodec, MaxEncodedLen}; +use frame_support::{ + log, pallet_prelude::DispatchResult, weights::Weight, PalletError, RuntimeDebug, StorageHasher, + StorageValue, +}; +use frame_system::RawOrigin; +use scale_info::TypeInfo; +use sp_core::{hash::H256, storage::StorageKey}; +use sp_io::hashing::blake2_256; +use sp_runtime::traits::{BadOrigin, Header as HeaderT}; +use sp_std::{convert::TryFrom, fmt::Debug, vec, vec::Vec}; + +pub use chain::{ + AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf, + HasherOf, HeaderOf, IndexOf, Parachain, SignatureOf, TransactionEraOf, +}; +pub use frame_support::storage::storage_prefix as storage_value_final_key; +use num_traits::{CheckedSub, One}; +use sp_runtime::transaction_validity::TransactionValidity; +pub use storage_proof::{ + record_all_keys as record_all_trie_keys, Error as StorageProofError, + ProofSize as StorageProofSize, StorageProofChecker, +}; +pub use storage_types::BoundedStorageValue; + +#[cfg(feature = "std")] +pub use storage_proof::craft_valid_storage_proof; + +pub mod messages; + +mod chain; +mod storage_proof; +mod storage_types; + +// Re-export macro to aviod include paste dependency everywhere +pub use sp_runtime::paste; + +/// Use this when something must be shared among all instances. +pub const NO_INSTANCE_ID: ChainId = [0, 0, 0, 0]; + +/// Bridge-with-Rialto instance id. +pub const RIALTO_CHAIN_ID: ChainId = *b"rlto"; + +/// Bridge-with-RialtoParachain instance id. +pub const RIALTO_PARACHAIN_CHAIN_ID: ChainId = *b"rlpa"; + +/// Bridge-with-Millau instance id. +pub const MILLAU_CHAIN_ID: ChainId = *b"mlau"; + +/// Bridge-with-Polkadot instance id. +pub const POLKADOT_CHAIN_ID: ChainId = *b"pdot"; + +/// Bridge-with-Kusama instance id. +pub const KUSAMA_CHAIN_ID: ChainId = *b"ksma"; + +/// Bridge-with-Rococo instance id. +pub const ROCOCO_CHAIN_ID: ChainId = *b"roco"; + +/// Bridge-with-Wococo instance id. +pub const WOCOCO_CHAIN_ID: ChainId = *b"woco"; + +/// Bridge-with-BridgeHubRococo instance id. +pub const BRIDGE_HUB_ROCOCO_CHAIN_ID: ChainId = *b"bhro"; + +/// Bridge-with-BridgeHubWococo instance id. +pub const BRIDGE_HUB_WOCOCO_CHAIN_ID: ChainId = *b"bhwo"; + +/// Call-dispatch module prefix. +pub const CALL_DISPATCH_MODULE_PREFIX: &[u8] = b"pallet-bridge/dispatch"; + +/// A unique prefix for entropy when generating cross-chain account IDs. +pub const ACCOUNT_DERIVATION_PREFIX: &[u8] = b"pallet-bridge/account-derivation/account"; + +/// A unique prefix for entropy when generating a cross-chain account ID for the Root account. +pub const ROOT_ACCOUNT_DERIVATION_PREFIX: &[u8] = b"pallet-bridge/account-derivation/root"; + +/// Generic header Id. +#[derive( + RuntimeDebug, Default, Clone, Encode, Decode, Copy, Eq, Hash, PartialEq, PartialOrd, Ord, +)] +pub struct HeaderId(pub Number, pub Hash); + +/// Generic header id provider. +pub trait HeaderIdProvider { + // Get the header id. + fn id(&self) -> HeaderId; + + // Get the header id for the parent block. + fn parent_id(&self) -> Option>; +} + +impl HeaderIdProvider
for Header { + fn id(&self) -> HeaderId { + HeaderId(*self.number(), self.hash()) + } + + fn parent_id(&self) -> Option> { + self.number() + .checked_sub(&One::one()) + .map(|parent_number| HeaderId(parent_number, *self.parent_hash())) + } +} + +/// Unique identifier of the chain. +/// +/// In addition to its main function (identifying the chain), this type may also be used to +/// identify module instance. We have a bunch of pallets that may be used in different bridges. E.g. +/// messages pallet may be deployed twice in the same runtime to bridge ThisChain with Chain1 and +/// Chain2. Sometimes we need to be able to identify deployed instance dynamically. This type may be +/// used for that. +pub type ChainId = [u8; 4]; + +/// Type of accounts on the source chain. +pub enum SourceAccount { + /// An account that belongs to Root (privileged origin). + Root, + /// A non-privileged account. + /// + /// The embedded account ID may or may not have a private key depending on the "owner" of the + /// account (private key, pallet, proxy, etc.). + Account(T), +} + +/// Derive an account ID from a foreign account ID. +/// +/// This function returns an encoded Blake2 hash. It is the responsibility of the caller to ensure +/// this can be successfully decoded into an AccountId. +/// +/// The `bridge_id` is used to provide extra entropy when producing account IDs. This helps prevent +/// AccountId collisions between different bridges on a single target chain. +/// +/// Note: If the same `bridge_id` is used across different chains (for example, if one source chain +/// is bridged to multiple target chains), then all the derived accounts would be the same across +/// the different chains. This could negatively impact users' privacy across chains. +pub fn derive_account_id(bridge_id: ChainId, id: SourceAccount) -> H256 +where + AccountId: Encode, +{ + match id { + SourceAccount::Root => + (ROOT_ACCOUNT_DERIVATION_PREFIX, bridge_id).using_encoded(blake2_256), + SourceAccount::Account(id) => + (ACCOUNT_DERIVATION_PREFIX, bridge_id, id).using_encoded(blake2_256), + } + .into() +} + +/// Anything that has size. +pub trait Size { + /// Return size of this object (in bytes). + fn size(&self) -> u32; +} + +impl Size for () { + fn size(&self) -> u32 { + 0 + } +} + +impl Size for Vec { + fn size(&self) -> u32 { + self.len() as _ + } +} + +/// Pre-computed size. +pub struct PreComputedSize(pub usize); + +impl Size for PreComputedSize { + fn size(&self) -> u32 { + u32::try_from(self.0).unwrap_or(u32::MAX) + } +} + +/// Era of specific transaction. +#[derive(RuntimeDebug, Clone, Copy, PartialEq)] +pub enum TransactionEra { + /// Transaction is immortal. + Immortal, + /// Transaction is valid for a given number of blocks, starting from given block. + Mortal(HeaderId, u32), +} + +impl, BlockHash: Copy> TransactionEra { + /// Prepare transaction era, based on mortality period and current best block number. + pub fn new( + best_block_id: HeaderId, + mortality_period: Option, + ) -> Self { + mortality_period + .map(|mortality_period| TransactionEra::Mortal(best_block_id, mortality_period)) + .unwrap_or(TransactionEra::Immortal) + } + + /// Create new immortal transaction era. + pub fn immortal() -> Self { + TransactionEra::Immortal + } + + /// Returns mortality period if transaction is mortal. + pub fn mortality_period(&self) -> Option { + match *self { + TransactionEra::Immortal => None, + TransactionEra::Mortal(_, period) => Some(period), + } + } + + /// Returns era that is used by FRAME-based runtimes. + pub fn frame_era(&self) -> sp_runtime::generic::Era { + match *self { + TransactionEra::Immortal => sp_runtime::generic::Era::immortal(), + TransactionEra::Mortal(header_id, period) => + sp_runtime::generic::Era::mortal(period as _, header_id.0.into()), + } + } + + /// Returns header hash that needs to be included in the signature payload. + pub fn signed_payload(&self, genesis_hash: BlockHash) -> BlockHash { + match *self { + TransactionEra::Immortal => genesis_hash, + TransactionEra::Mortal(header_id, _) => header_id.1, + } + } +} + +/// This is a copy of the +/// `frame_support::storage::generator::StorageMap::storage_map_final_key` for maps based +/// on selected hasher. +/// +/// We're using it because to call `storage_map_final_key` directly, we need access to the runtime +/// and pallet instance, which (sometimes) is impossible. +pub fn storage_map_final_key( + pallet_prefix: &str, + map_name: &str, + key: &[u8], +) -> StorageKey { + let key_hashed = H::hash(key); + let pallet_prefix_hashed = frame_support::Twox128::hash(pallet_prefix.as_bytes()); + let storage_prefix_hashed = frame_support::Twox128::hash(map_name.as_bytes()); + + let mut final_key = Vec::with_capacity( + pallet_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.as_ref().len(), + ); + + final_key.extend_from_slice(&pallet_prefix_hashed[..]); + final_key.extend_from_slice(&storage_prefix_hashed[..]); + final_key.extend_from_slice(key_hashed.as_ref()); + + StorageKey(final_key) +} + +/// This is how a storage key of storage parameter (`parameter_types! { storage Param: bool = false; +/// }`) is computed. +/// +/// Copied from `frame_support::parameter_types` macro. +pub fn storage_parameter_key(parameter_name: &str) -> StorageKey { + let mut buffer = Vec::with_capacity(1 + parameter_name.len() + 1); + buffer.push(b':'); + buffer.extend_from_slice(parameter_name.as_bytes()); + buffer.push(b':'); + StorageKey(sp_io::hashing::twox_128(&buffer).to_vec()) +} + +/// This is how a storage key of storage value is computed. +/// +/// Copied from `frame_support::storage::storage_prefix`. +pub fn storage_value_key(pallet_prefix: &str, value_name: &str) -> StorageKey { + let pallet_hash = sp_io::hashing::twox_128(pallet_prefix.as_bytes()); + let storage_hash = sp_io::hashing::twox_128(value_name.as_bytes()); + + let mut final_key = vec![0u8; 32]; + final_key[..16].copy_from_slice(&pallet_hash); + final_key[16..].copy_from_slice(&storage_hash); + + StorageKey(final_key) +} + +/// Can be use to access the runtime storage key of a `StorageMap`. +pub trait StorageMapKeyProvider { + /// The name of the variable that holds the `StorageMap`. + const MAP_NAME: &'static str; + + /// The same as `StorageMap::Hasher1`. + type Hasher: StorageHasher; + /// The same as `StorageMap::Key1`. + type Key: FullCodec; + /// The same as `StorageMap::Value`. + type Value: FullCodec; + + /// This is a copy of the + /// `frame_support::storage::generator::StorageMap::storage_map_final_key`. + /// + /// We're using it because to call `storage_map_final_key` directly, we need access + /// to the runtime and pallet instance, which (sometimes) is impossible. + fn final_key(pallet_prefix: &str, key: &Self::Key) -> StorageKey { + storage_map_final_key::(pallet_prefix, Self::MAP_NAME, &key.encode()) + } +} + +/// Can be use to access the runtime storage key of a `StorageDoubleMap`. +pub trait StorageDoubleMapKeyProvider { + /// The name of the variable that holds the `StorageDoubleMap`. + const MAP_NAME: &'static str; + + /// The same as `StorageDoubleMap::Hasher1`. + type Hasher1: StorageHasher; + /// The same as `StorageDoubleMap::Key1`. + type Key1: FullCodec; + /// The same as `StorageDoubleMap::Hasher2`. + type Hasher2: StorageHasher; + /// The same as `StorageDoubleMap::Key2`. + type Key2: FullCodec; + /// The same as `StorageDoubleMap::Value`. + type Value: FullCodec; + + /// This is a copy of the + /// `frame_support::storage::generator::StorageDoubleMap::storage_double_map_final_key`. + /// + /// We're using it because to call `storage_double_map_final_key` directly, we need access + /// to the runtime and pallet instance, which (sometimes) is impossible. + fn final_key(pallet_prefix: &str, key1: &Self::Key1, key2: &Self::Key2) -> StorageKey { + let key1_hashed = Self::Hasher1::hash(&key1.encode()); + let key2_hashed = Self::Hasher2::hash(&key2.encode()); + let pallet_prefix_hashed = frame_support::Twox128::hash(pallet_prefix.as_bytes()); + let storage_prefix_hashed = frame_support::Twox128::hash(Self::MAP_NAME.as_bytes()); + + let mut final_key = Vec::with_capacity( + pallet_prefix_hashed.len() + + storage_prefix_hashed.len() + + key1_hashed.as_ref().len() + + key2_hashed.as_ref().len(), + ); + + final_key.extend_from_slice(&pallet_prefix_hashed[..]); + final_key.extend_from_slice(&storage_prefix_hashed[..]); + final_key.extend_from_slice(key1_hashed.as_ref()); + final_key.extend_from_slice(key2_hashed.as_ref()); + + StorageKey(final_key) + } +} + +/// Error generated by the `OwnedBridgeModule` trait. +#[derive(Encode, Decode, TypeInfo, PalletError)] +pub enum OwnedBridgeModuleError { + /// All pallet operations are halted. + Halted, +} + +/// Operating mode for a bridge module. +pub trait OperatingMode: Send + Copy + Debug + FullCodec { + // Returns true if the bridge module is halted. + fn is_halted(&self) -> bool; +} + +/// Basic operating modes for a bridges module (Normal/Halted). +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub enum BasicOperatingMode { + /// Normal mode, when all operations are allowed. + Normal, + /// The pallet is halted. All operations (except operating mode change) are prohibited. + Halted, +} + +impl Default for BasicOperatingMode { + fn default() -> Self { + Self::Normal + } +} + +impl OperatingMode for BasicOperatingMode { + fn is_halted(&self) -> bool { + *self == BasicOperatingMode::Halted + } +} + +/// Bridge module that has owner and operating mode +pub trait OwnedBridgeModule { + /// The target that will be used when publishing logs related to this module. + const LOG_TARGET: &'static str; + + type OwnerStorage: StorageValue>; + type OperatingMode: OperatingMode; + type OperatingModeStorage: StorageValue; + + /// Check if the module is halted. + fn is_halted() -> bool { + Self::OperatingModeStorage::get().is_halted() + } + + /// Ensure that the origin is either root, or `PalletOwner`. + fn ensure_owner_or_root(origin: T::RuntimeOrigin) -> Result<(), BadOrigin> { + match origin.into() { + Ok(RawOrigin::Root) => Ok(()), + Ok(RawOrigin::Signed(ref signer)) + if Self::OwnerStorage::get().as_ref() == Some(signer) => + Ok(()), + _ => Err(BadOrigin), + } + } + + /// Ensure that the module is not halted. + fn ensure_not_halted() -> Result<(), OwnedBridgeModuleError> { + match Self::is_halted() { + true => Err(OwnedBridgeModuleError::Halted), + false => Ok(()), + } + } + + /// Change the owner of the module. + fn set_owner(origin: T::RuntimeOrigin, maybe_owner: Option) -> DispatchResult { + Self::ensure_owner_or_root(origin)?; + match maybe_owner { + Some(owner) => { + Self::OwnerStorage::put(&owner); + log::info!(target: Self::LOG_TARGET, "Setting pallet Owner to: {:?}", owner); + }, + None => { + Self::OwnerStorage::kill(); + log::info!(target: Self::LOG_TARGET, "Removed Owner of pallet."); + }, + } + + Ok(()) + } + + /// Halt or resume all/some module operations. + fn set_operating_mode( + origin: T::RuntimeOrigin, + operating_mode: Self::OperatingMode, + ) -> DispatchResult { + Self::ensure_owner_or_root(origin)?; + Self::OperatingModeStorage::put(operating_mode); + log::info!(target: Self::LOG_TARGET, "Setting operating mode to {:?}.", operating_mode); + Ok(()) + } +} + +/// A trait for querying whether a runtime call is valid. +pub trait FilterCall { + /// Checks if a runtime call is valid. + fn validate(call: &Call) -> TransactionValidity; +} + +/// All extra operations with weights that we need in bridges. +pub trait WeightExtraOps { + /// Checked division of individual components of two weights. + /// + /// Divides components and returns minimal division result. Returns `None` if one + /// of `other` weight components is zero. + fn min_components_checked_div(&self, other: Weight) -> Option; +} + +impl WeightExtraOps for Weight { + fn min_components_checked_div(&self, other: Weight) -> Option { + Some(sp_std::cmp::min( + self.ref_time().checked_div(other.ref_time())?, + self.proof_size().checked_div(other.proof_size())?, + )) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn storage_parameter_key_works() { + assert_eq!( + storage_parameter_key("MillauToRialtoConversionRate"), + StorageKey(hex_literal::hex!("58942375551bb0af1682f72786b59d04").to_vec()), + ); + } + + #[test] + fn storage_value_key_works() { + assert_eq!( + storage_value_key("PalletTransactionPayment", "NextFeeMultiplier"), + StorageKey( + hex_literal::hex!( + "f0e954dfcca51a255ab12c60c789256a3f2edf3bdf381debe331ab7446addfdc" + ) + .to_vec() + ), + ); + } +} diff --git a/primitives/runtime/src/messages.rs b/primitives/runtime/src/messages.rs new file mode 100644 index 00000000000..0bb09090df9 --- /dev/null +++ b/primitives/runtime/src/messages.rs @@ -0,0 +1,39 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives that may be used by different message delivery and dispatch mechanisms. + +use codec::{Decode, Encode}; +use frame_support::{weights::Weight, RuntimeDebug}; +use scale_info::TypeInfo; + +/// Message dispatch result. +#[derive(Encode, Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] +pub struct MessageDispatchResult { + /// Unspent dispatch weight. This weight that will be deducted from total delivery transaction + /// weight, thus reducing the transaction cost. This shall not be zero in (at least) two cases: + /// + /// 1) if message has been dispatched successfully, but post-dispatch weight is less than + /// the weight, declared by the message sender; + /// 2) if message has not been dispatched at all. + pub unspent_weight: Weight, + /// Whether the message dispatch fee has been paid during dispatch. This will be true if your + /// configuration supports pay-dispatch-fee-at-target-chain option and message sender has + /// enabled this option. + pub dispatch_fee_paid_during_dispatch: bool, + /// Fine-grained result of single message dispatch (for better diagnostic purposes) + pub dispatch_level_result: DispatchLevelResult, +} diff --git a/primitives/runtime/src/storage_proof.rs b/primitives/runtime/src/storage_proof.rs new file mode 100644 index 00000000000..e1465d2fa16 --- /dev/null +++ b/primitives/runtime/src/storage_proof.rs @@ -0,0 +1,174 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Logic for checking Substrate storage proofs. + +use codec::Decode; +use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; +use sp_runtime::RuntimeDebug; +use sp_std::{boxed::Box, vec::Vec}; +use sp_trie::{ + read_trie_value, LayoutV1, MemoryDB, Recorder, StorageProof, Trie, TrieConfiguration, + TrieDBBuilder, TrieError, TrieHash, +}; + +/// Storage proof size requirements. +/// +/// This is currently used by benchmarks when generating storage proofs. +#[derive(Clone, Copy, Debug)] +pub enum ProofSize { + /// The proof is expected to be minimal. If value size may be changed, then it is expected to + /// have given size. + Minimal(u32), + /// The proof is expected to have at least given size and grow by increasing number of trie + /// nodes included in the proof. + HasExtraNodes(u32), + /// The proof is expected to have at least given size and grow by increasing value that is + /// stored in the trie. + HasLargeLeaf(u32), +} + +/// This struct is used to read storage values from a subset of a Merklized database. The "proof" +/// is a subset of the nodes in the Merkle structure of the database, so that it provides +/// authentication against a known Merkle root as well as the values in the database themselves. +pub struct StorageProofChecker +where + H: Hasher, +{ + root: H::Out, + db: MemoryDB, +} + +impl StorageProofChecker +where + H: Hasher, +{ + /// Constructs a new storage proof checker. + /// + /// This returns an error if the given proof is invalid with respect to the given root. + pub fn new(root: H::Out, proof: StorageProof) -> Result { + let db = proof.into_memory_db(); + if !db.contains(&root, EMPTY_PREFIX) { + return Err(Error::StorageRootMismatch) + } + + let checker = StorageProofChecker { root, db }; + Ok(checker) + } + + /// Reads a value from the available subset of storage. If the value cannot be read due to an + /// incomplete or otherwise invalid proof, this function returns an error. + pub fn read_value(&self, key: &[u8]) -> Result>, Error> { + // LayoutV1 or LayoutV0 is identical for proof that only read values. + read_trie_value::, _>(&self.db, &self.root, key, None, None) + .map_err(|_| Error::StorageValueUnavailable) + } + + /// Reads and decodes a value from the available subset of storage. If the value cannot be read + /// due to an incomplete or otherwise invalid proof, this function returns an error. If value is + /// read, but decoding fails, this function returns an error. + pub fn read_and_decode_value(&self, key: &[u8]) -> Result, Error> { + self.read_value(key).and_then(|v| { + v.map(|v| T::decode(&mut &v[..]).map_err(Error::StorageValueDecodeFailed)) + .transpose() + }) + } +} + +#[derive(Eq, RuntimeDebug, PartialEq)] +pub enum Error { + StorageRootMismatch, + StorageValueUnavailable, + StorageValueDecodeFailed(codec::Error), +} + +/// Return valid storage proof and state root. +/// +/// NOTE: This should only be used for **testing**. +#[cfg(feature = "std")] +pub fn craft_valid_storage_proof() -> (sp_core::H256, StorageProof) { + use codec::Encode; + use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend}; + + let state_version = sp_runtime::StateVersion::default(); + + // construct storage proof + let backend = >::from(( + vec![ + (None, vec![(b"key1".to_vec(), Some(b"value1".to_vec()))]), + (None, vec![(b"key2".to_vec(), Some(b"value2".to_vec()))]), + (None, vec![(b"key3".to_vec(), Some(b"value3".to_vec()))]), + (None, vec![(b"key4".to_vec(), Some((42u64, 42u32, 42u16, 42u8).encode()))]), + // Value is too big to fit in a branch node + (None, vec![(b"key11".to_vec(), Some(vec![0u8; 32]))]), + ], + state_version, + )); + let root = backend.storage_root(std::iter::empty(), state_version).0; + let proof = + prove_read(backend, &[&b"key1"[..], &b"key2"[..], &b"key4"[..], &b"key22"[..]]).unwrap(); + + (root, proof) +} + +/// Record all keys for a given root. +pub fn record_all_keys( + db: &DB, + root: &TrieHash, + recorder: &mut Recorder, +) -> Result<(), Box>> +where + DB: hash_db::HashDBRef, +{ + let trie = TrieDBBuilder::::new(db, root).with_recorder(recorder).build(); + for x in trie.iter()? { + let (key, _) = x?; + trie.get(&key)?; + } + + Ok(()) +} + +#[cfg(test)] +pub mod tests { + use super::*; + use codec::Encode; + + #[test] + fn storage_proof_check() { + let (root, proof) = craft_valid_storage_proof(); + + // check proof in runtime + let checker = + >::new(root, proof.clone()).unwrap(); + assert_eq!(checker.read_value(b"key1"), Ok(Some(b"value1".to_vec()))); + assert_eq!(checker.read_value(b"key2"), Ok(Some(b"value2".to_vec()))); + assert_eq!(checker.read_value(b"key4"), Ok(Some((42u64, 42u32, 42u16, 42u8).encode()))); + assert_eq!(checker.read_value(b"key11111"), Err(Error::StorageValueUnavailable)); + assert_eq!(checker.read_value(b"key22"), Ok(None)); + assert_eq!(checker.read_and_decode_value(b"key4"), Ok(Some((42u64, 42u32, 42u16, 42u8))),); + assert!(matches!( + checker.read_and_decode_value::<[u8; 64]>(b"key4"), + Err(Error::StorageValueDecodeFailed(_)), + )); + + // checking proof against invalid commitment fails + assert_eq!( + >::new(sp_core::H256::random(), proof).err(), + Some(Error::StorageRootMismatch) + ); + } +} diff --git a/primitives/runtime/src/storage_types.rs b/primitives/runtime/src/storage_types.rs new file mode 100644 index 00000000000..b37f779d00b --- /dev/null +++ b/primitives/runtime/src/storage_types.rs @@ -0,0 +1,90 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Wrapper for a runtime storage value that checks if value exceeds given maximum +//! during conversion. + +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{traits::Get, RuntimeDebug}; +use scale_info::{Type, TypeInfo}; +use sp_std::{marker::PhantomData, ops::Deref}; + +/// Error that is returned when the value size exceeds maximal configured size. +#[derive(RuntimeDebug)] +pub struct MaximalSizeExceededError { + /// Size of the value. + pub value_size: usize, + /// Maximal configured size. + pub maximal_size: usize, +} + +/// A bounded runtime storage value. +#[derive(Clone, Decode, Encode, Eq, PartialEq)] +pub struct BoundedStorageValue { + value: V, + _phantom: PhantomData, +} + +impl sp_std::fmt::Debug for BoundedStorageValue { + fn fmt(&self, fmt: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + self.value.fmt(fmt) + } +} + +impl, V: Encode> BoundedStorageValue { + /// Construct `BoundedStorageValue` from the underlying `value` with all required checks. + /// + /// Returns error if value size exceeds given bounds. + pub fn try_from_inner(value: V) -> Result { + // this conversion is heavy (since we do encoding here), so we may want to optimize it later + // (e.g. by introducing custom Encode implementation, and turning `BoundedStorageValue` into + // `enum BoundedStorageValue { Decoded(V), Encoded(Vec) }`) + let value_size = value.encoded_size(); + let maximal_size = B::get() as usize; + if value_size > maximal_size { + Err(MaximalSizeExceededError { value_size, maximal_size }) + } else { + Ok(BoundedStorageValue { value, _phantom: Default::default() }) + } + } + + /// Convert into the inner type + pub fn into_inner(self) -> V { + self.value + } +} + +impl Deref for BoundedStorageValue { + type Target = V; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + +impl TypeInfo for BoundedStorageValue { + type Identity = Self; + + fn type_info() -> Type { + V::type_info() + } +} + +impl, V: Encode> MaxEncodedLen for BoundedStorageValue { + fn max_encoded_len() -> usize { + B::get() as usize + } +} diff --git a/primitives/test-utils/Cargo.toml b/primitives/test-utils/Cargo.toml new file mode 100644 index 00000000000..2bc77e632e5 --- /dev/null +++ b/primitives/test-utils/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "bp-test-utils" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +bp-header-chain = { path = "../header-chain", default-features = false } +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +ed25519-dalek = { version = "1.0", default-features = false, features = ["u64_backend"] } +finality-grandpa = { version = "0.16.0", default-features = false } +sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "codec/std", + "ed25519-dalek/std", + "finality-grandpa/std", + "sp-application-crypto/std", + "sp-finality-grandpa/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/primitives/test-utils/src/keyring.rs b/primitives/test-utils/src/keyring.rs new file mode 100644 index 00000000000..f827c729434 --- /dev/null +++ b/primitives/test-utils/src/keyring.rs @@ -0,0 +1,94 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Utilities for working with test accounts. + +use codec::Encode; +use ed25519_dalek::{Keypair, PublicKey, SecretKey, Signature}; +use finality_grandpa::voter_set::VoterSet; +use sp_finality_grandpa::{AuthorityId, AuthorityList, AuthorityWeight}; +use sp_runtime::RuntimeDebug; +use sp_std::prelude::*; + +/// Set of test accounts with friendly names. +pub const ALICE: Account = Account(0); +pub const BOB: Account = Account(1); +pub const CHARLIE: Account = Account(2); +pub const DAVE: Account = Account(3); +pub const EVE: Account = Account(4); +pub const FERDIE: Account = Account(5); + +/// A test account which can be used to sign messages. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Account(pub u16); + +impl Account { + pub fn public(&self) -> PublicKey { + (&self.secret()).into() + } + + pub fn secret(&self) -> SecretKey { + let data = self.0.encode(); + let mut bytes = [0_u8; 32]; + bytes[0..data.len()].copy_from_slice(&data); + SecretKey::from_bytes(&bytes) + .expect("A static array of the correct length is a known good.") + } + + pub fn pair(&self) -> Keypair { + let mut pair: [u8; 64] = [0; 64]; + + let secret = self.secret(); + pair[..32].copy_from_slice(&secret.to_bytes()); + + let public = self.public(); + pair[32..].copy_from_slice(&public.to_bytes()); + + Keypair::from_bytes(&pair) + .expect("We expect the SecretKey to be good, so this must also be good.") + } + + pub fn sign(&self, msg: &[u8]) -> Signature { + use ed25519_dalek::Signer; + self.pair().sign(msg) + } +} + +impl From for AuthorityId { + fn from(p: Account) -> Self { + sp_application_crypto::UncheckedFrom::unchecked_from(p.public().to_bytes()) + } +} + +/// Get a valid set of voters for a Grandpa round. +pub fn voter_set() -> VoterSet { + VoterSet::new(authority_list()).unwrap() +} + +/// Convenience function to get a list of Grandpa authorities. +pub fn authority_list() -> AuthorityList { + test_keyring().iter().map(|(id, w)| (AuthorityId::from(*id), *w)).collect() +} + +/// Get the corresponding identities from the keyring for the "standard" authority set. +pub fn test_keyring() -> Vec<(Account, AuthorityWeight)> { + vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1)] +} + +/// Get a list of "unique" accounts. +pub fn accounts(len: u16) -> Vec { + (0..len).into_iter().map(Account).collect() +} diff --git a/primitives/test-utils/src/lib.rs b/primitives/test-utils/src/lib.rs new file mode 100644 index 00000000000..186d192014b --- /dev/null +++ b/primitives/test-utils/src/lib.rs @@ -0,0 +1,345 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Utilities for testing runtime code. + +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_header_chain::justification::GrandpaJustification; +use codec::Encode; +use sp_finality_grandpa::{AuthorityId, AuthoritySignature, AuthorityWeight, SetId}; +use sp_runtime::traits::{Header as HeaderT, One, Zero}; +use sp_std::prelude::*; + +// Re-export all our test account utilities +pub use keyring::*; + +mod keyring; + +pub const TEST_GRANDPA_ROUND: u64 = 1; +pub const TEST_GRANDPA_SET_ID: SetId = 1; + +/// Configuration parameters when generating test GRANDPA justifications. +#[derive(Clone)] +pub struct JustificationGeneratorParams { + /// The header which we want to finalize. + pub header: H, + /// The GRANDPA round number for the current authority set. + pub round: u64, + /// The current authority set ID. + pub set_id: SetId, + /// The current GRANDPA authority set. + /// + /// The size of the set will determine the number of pre-commits in our justification. + pub authorities: Vec<(Account, AuthorityWeight)>, + /// The total number of precommit ancestors in the `votes_ancestries` field our justification. + /// + /// These may be distributed among many forks. + pub ancestors: u32, + /// The number of forks. + /// + /// Useful for creating a "worst-case" scenario in which each authority is on its own fork. + pub forks: u32, +} + +impl Default for JustificationGeneratorParams { + fn default() -> Self { + Self { + header: test_header(One::one()), + round: TEST_GRANDPA_ROUND, + set_id: TEST_GRANDPA_SET_ID, + authorities: test_keyring(), + ancestors: 2, + forks: 1, + } + } +} + +/// Make a valid GRANDPA justification with sensible defaults +pub fn make_default_justification(header: &H) -> GrandpaJustification { + let params = JustificationGeneratorParams:: { header: header.clone(), ..Default::default() }; + + make_justification_for_header(params) +} + +/// Generate justifications in a way where we are able to tune the number of pre-commits +/// and vote ancestries which are included in the justification. +/// +/// This is useful for benchmarkings where we want to generate valid justifications with +/// a specific number of pre-commits (tuned with the number of "authorities") and/or a specific +/// number of vote ancestries (tuned with the "votes" parameter). +/// +/// Note: This needs at least three authorities or else the verifier will complain about +/// being given an invalid commit. +pub fn make_justification_for_header( + params: JustificationGeneratorParams, +) -> GrandpaJustification { + let JustificationGeneratorParams { header, round, set_id, authorities, mut ancestors, forks } = + params; + let (target_hash, target_number) = (header.hash(), *header.number()); + let mut votes_ancestries = vec![]; + let mut precommits = vec![]; + + assert!(forks != 0, "Need at least one fork to have a chain.."); + assert!( + forks as usize <= authorities.len(), + "If we have more forks than authorities we can't create valid pre-commits for all the forks." + ); + + // Roughly, how many vote ancestries do we want per fork + let target_depth = (ancestors + forks - 1) / forks; + + let mut unsigned_precommits = vec![]; + for i in 0..forks { + let depth = if ancestors >= target_depth { + ancestors -= target_depth; + target_depth + } else { + ancestors + }; + + // Note: Adding 1 to account for the target header + let chain = generate_chain(i, depth + 1, &header); + + // We don't include our finality target header in the vote ancestries + for child in &chain[1..] { + votes_ancestries.push(child.clone()); + } + + // The header we need to use when pre-commiting is the one at the highest height + // on our chain. + let precommit_candidate = chain.last().map(|h| (h.hash(), *h.number())).unwrap(); + unsigned_precommits.push(precommit_candidate); + } + + for (i, (id, _weight)) in authorities.iter().enumerate() { + // Assign authorities to sign pre-commits in a round-robin fashion + let target = unsigned_precommits[i % forks as usize]; + let precommit = signed_precommit::(id, target, round, set_id); + + precommits.push(precommit); + } + + GrandpaJustification { + round, + commit: finality_grandpa::Commit { target_hash, target_number, precommits }, + votes_ancestries, + } +} + +fn generate_chain(fork_id: u32, depth: u32, ancestor: &H) -> Vec { + let mut headers = vec![ancestor.clone()]; + + for i in 1..depth { + let parent = &headers[(i - 1) as usize]; + let (hash, num) = (parent.hash(), *parent.number()); + + let mut header = test_header::(num + One::one()); + header.set_parent_hash(hash); + + // Modifying the digest so headers at the same height but in different forks have different + // hashes + header.digest_mut().logs.push(sp_runtime::DigestItem::Other(fork_id.encode())); + + headers.push(header); + } + + headers +} + +/// Create signed precommit with given target. +pub fn signed_precommit( + signer: &Account, + target: (H::Hash, H::Number), + round: u64, + set_id: SetId, +) -> finality_grandpa::SignedPrecommit { + let precommit = finality_grandpa::Precommit { target_hash: target.0, target_number: target.1 }; + + let encoded = sp_finality_grandpa::localized_payload( + round, + set_id, + &finality_grandpa::Message::Precommit(precommit.clone()), + ); + + let signature = signer.sign(&encoded); + let raw_signature: Vec = signature.to_bytes().into(); + + // Need to wrap our signature and id types that they match what our `SignedPrecommit` is + // expecting + let signature = AuthoritySignature::try_from(raw_signature).expect( + "We know our Keypair is good, + so our signature must also be good.", + ); + let id = (*signer).into(); + + finality_grandpa::SignedPrecommit { precommit, signature, id } +} + +/// Get a header for testing. +/// +/// The correct parent hash will be used if given a non-zero header. +pub fn test_header(number: H::Number) -> H { + let default = |num| { + H::new(num, Default::default(), Default::default(), Default::default(), Default::default()) + }; + + let mut header = default(number); + if number != Zero::zero() { + let parent_hash = default(number - One::one()).hash(); + header.set_parent_hash(parent_hash); + } + + header +} + +/// Convenience function for generating a Header ID at a given block number. +pub fn header_id(index: u8) -> (H::Hash, H::Number) { + (test_header::(index.into()).hash(), index.into()) +} + +#[macro_export] +/// Adds methods for testing the `set_owner()` and `set_operating_mode()` for a pallet. +/// Some values are hardcoded like: +/// - `run_test()` +/// - `Pallet::` +/// - `PalletOwner::` +/// - `PalletOperatingMode::` +/// While this is not ideal, all the pallets use the same names, so it works for the moment. +/// We can revisit this in the future if anything changes. +macro_rules! generate_owned_bridge_module_tests { + ($normal_operating_mode: expr, $halted_operating_mode: expr) => { + #[test] + fn test_set_owner() { + run_test(|| { + PalletOwner::::put(1); + + // The root should be able to change the owner. + assert_ok!(Pallet::::set_owner(RuntimeOrigin::root(), Some(2))); + assert_eq!(PalletOwner::::get(), Some(2)); + + // The owner should be able to change the owner. + assert_ok!(Pallet::::set_owner(RuntimeOrigin::signed(2), Some(3))); + assert_eq!(PalletOwner::::get(), Some(3)); + + // Other users shouldn't be able to change the owner. + assert_noop!( + Pallet::::set_owner(RuntimeOrigin::signed(1), Some(4)), + DispatchError::BadOrigin + ); + assert_eq!(PalletOwner::::get(), Some(3)); + }); + } + + #[test] + fn test_set_operating_mode() { + run_test(|| { + PalletOwner::::put(1); + PalletOperatingMode::::put($normal_operating_mode); + + // The root should be able to halt the pallet. + assert_ok!(Pallet::::set_operating_mode( + RuntimeOrigin::root(), + $halted_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + // The root should be able to resume the pallet. + assert_ok!(Pallet::::set_operating_mode( + RuntimeOrigin::root(), + $normal_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + + // The owner should be able to halt the pallet. + assert_ok!(Pallet::::set_operating_mode( + RuntimeOrigin::signed(1), + $halted_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + // The owner should be able to resume the pallet. + assert_ok!(Pallet::::set_operating_mode( + RuntimeOrigin::signed(1), + $normal_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + + // Other users shouldn't be able to halt the pallet. + assert_noop!( + Pallet::::set_operating_mode( + RuntimeOrigin::signed(2), + $halted_operating_mode + ), + DispatchError::BadOrigin + ); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + // Other users shouldn't be able to resume the pallet. + PalletOperatingMode::::put($halted_operating_mode); + assert_noop!( + Pallet::::set_operating_mode( + RuntimeOrigin::signed(2), + $normal_operating_mode + ), + DispatchError::BadOrigin + ); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + }); + } + }; +} + +#[cfg(test)] +mod tests { + use codec::Encode; + use sp_application_crypto::sp_core::{hexdisplay, hexdisplay::HexDisplay}; + use xcm::VersionedXcm; + + fn print_xcm(xcm: &VersionedXcm) { + println!("-----------------"); + println!("xcm (plain): {:?}", xcm); + println!("xcm (bytes): {:?}", xcm.encode()); + println!("xcm (hex): {:?}", hexdisplay::HexDisplay::from(&xcm.encode())); + } + + fn as_hex(xcm: &VersionedXcm) -> String { + HexDisplay::from(&xcm.encode()).to_string() + } + + pub type RuntimeCall = (); + + #[test] + fn generate_versioned_xcm_message_hex_bytes() { + let xcm: xcm::v2::Xcm = xcm::v2::Xcm(vec![xcm::v2::Instruction::Trap(43)]); + let xcm: VersionedXcm = From::from(xcm); + print_xcm(&xcm); + assert_eq!("020419ac", format!("{}", as_hex(&xcm))); + + let xcm: xcm::v3::Xcm = vec![xcm::v3::Instruction::Trap(43)].into(); + let xcm: VersionedXcm = From::from(xcm); + print_xcm(&xcm); + assert_eq!("030419ac", format!("{}", as_hex(&xcm))); + + let xcm: xcm::v3::Xcm = vec![ + xcm::v3::Instruction::ClearError, + xcm::v3::Instruction::ClearTopic, + xcm::v3::Instruction::ClearTransactStatus, + xcm::v3::Instruction::Trap(43), + ] + .into(); + let xcm: VersionedXcm = From::from(xcm); + print_xcm(&xcm); + assert_eq!("0310172c2319ac", format!("{}", as_hex(&xcm))); + } +} diff --git a/relays/bin-substrate/Cargo.toml b/relays/bin-substrate/Cargo.toml new file mode 100644 index 00000000000..9ae6a98868e --- /dev/null +++ b/relays/bin-substrate/Cargo.toml @@ -0,0 +1,75 @@ +[package] +name = "substrate-relay" +version = "1.0.1" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +anyhow = "1.0" +async-std = "1.9.0" +async-trait = "0.1" +codec = { package = "parity-scale-codec", version = "3.1.5" } +futures = "0.3.12" +hex = "0.4" +log = "0.4.17" +num-format = "0.4" +num-traits = "0.2" +structopt = "0.3" +strum = { version = "0.21.0", features = ["derive"] } + +# Bridge dependencies + +bp-bridge-hub-rococo = { path = "../../primitives/chain-bridge-hub-rococo" } +bp-bridge-hub-wococo = { path = "../../primitives/chain-bridge-hub-wococo" } +bp-header-chain = { path = "../../primitives/header-chain" } +bp-messages = { path = "../../primitives/messages" } +bp-millau = { path = "../../primitives/chain-millau" } +bp-polkadot-core = { path = "../../primitives/polkadot-core" } +bp-rialto = { path = "../../primitives/chain-rialto" } +bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" } +bp-rococo = { path = "../../primitives/chain-rococo" } +bp-runtime = { path = "../../primitives/runtime" } +bp-westend = { path = "../../primitives/chain-westend" } +bp-wococo = { path = "../../primitives/chain-wococo" } +bridge-runtime-common = { path = "../../bin/runtime-common" } +messages-relay = { path = "../messages" } +millau-runtime = { path = "../../bin/millau/runtime" } +pallet-bridge-messages = { path = "../../modules/messages" } +pallet-bridge-parachains = { path = "../../modules/parachains" } +parachains-relay = { path = "../parachains" } +relay-millau-client = { path = "../client-millau" } +relay-rialto-client = { path = "../client-rialto" } +relay-rialto-parachain-client = { path = "../client-rialto-parachain" } +relay-bridge-hub-rococo-client = { path = "../client-bridge-hub-rococo" } +relay-bridge-hub-wococo-client = { path = "../client-bridge-hub-wococo" } +relay-rococo-client = { path = "../client-rococo" } +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } +relay-westend-client = { path = "../client-westend" } +relay-wococo-client = { path = "../client-wococo" } +rialto-parachain-runtime = { path = "../../bin/rialto-parachain/runtime" } +rialto-runtime = { path = "../../bin/rialto/runtime" } +substrate-relay-helper = { path = "../lib-substrate-relay" } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-version = { git = "https://github.com/paritytech/substrate", branch = "master" } + +# Polkadot Dependencies +polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "master" } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + + +[dev-dependencies] +bp-test-utils = { path = "../../primitives/test-utils" } +hex-literal = "0.3" +sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } +tempfile = "3.2" +finality-grandpa = { version = "0.16.0" } diff --git a/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs new file mode 100644 index 00000000000..78ef00f6d62 --- /dev/null +++ b/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs @@ -0,0 +1,64 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! BridgeHubRococo-to-BridgeHubWococo messages sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge}; +use bp_messages::Weight; +use relay_bridge_hub_rococo_client::BridgeHubRococo; +use relay_bridge_hub_wococo_client::BridgeHubWococo; +use substrate_relay_helper::messages_lane::SubstrateMessageLane; + +pub struct BridgeHubRococoToBridgeHubWococoMessagesCliBridge {} + +impl CliBridgeBase for BridgeHubRococoToBridgeHubWococoMessagesCliBridge { + type Source = BridgeHubRococo; + type Target = BridgeHubWococo; +} + +impl MessagesCliBridge for BridgeHubRococoToBridgeHubWococoMessagesCliBridge { + const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = + "TODO: not needed now, used for send_message and estimate_fee CLI"; + type MessagesLane = BridgeHubRococoMessagesToBridgeHubWococoMessageLane; +} + +substrate_relay_helper::generate_mocked_receive_message_proof_call_builder!( + BridgeHubRococoMessagesToBridgeHubWococoMessageLane, + BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesProofCallBuilder, + relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoMessages, + relay_bridge_hub_wococo_client::runtime::BridgeRococoMessagesCall::receive_messages_proof +); + +substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_builder!( + BridgeHubRococoMessagesToBridgeHubWococoMessageLane, + BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesDeliveryProofCallBuilder, + relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoMessages, + relay_bridge_hub_rococo_client::runtime::BridgeWococoMessagesCall::receive_messages_delivery_proof +); + +/// Description of BridgeHubRococo -> BridgeHubWococo messages bridge. +#[derive(Clone, Debug)] +pub struct BridgeHubRococoMessagesToBridgeHubWococoMessageLane; + +impl SubstrateMessageLane for BridgeHubRococoMessagesToBridgeHubWococoMessageLane { + type SourceChain = BridgeHubRococo; + type TargetChain = BridgeHubWococo; + + type ReceiveMessagesProofCallBuilder = + BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesProofCallBuilder; + type ReceiveMessagesDeliveryProofCallBuilder = + BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesDeliveryProofCallBuilder; +} diff --git a/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs new file mode 100644 index 00000000000..51e1020e749 --- /dev/null +++ b/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs @@ -0,0 +1,64 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! BridgeHubWococo-to-BridgeHubRococo messages sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge}; +use bp_messages::Weight; +use relay_bridge_hub_rococo_client::BridgeHubRococo; +use relay_bridge_hub_wococo_client::BridgeHubWococo; +use substrate_relay_helper::messages_lane::SubstrateMessageLane; + +pub struct BridgeHubWococoToBridgeHubRococoMessagesCliBridge {} + +impl CliBridgeBase for BridgeHubWococoToBridgeHubRococoMessagesCliBridge { + type Source = BridgeHubWococo; + type Target = BridgeHubRococo; +} + +impl MessagesCliBridge for BridgeHubWococoToBridgeHubRococoMessagesCliBridge { + const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = + "TODO: not needed now, used for send_message and estimate_fee CLI"; + type MessagesLane = BridgeHubWococoMessagesToBridgeHubRococoMessageLane; +} + +substrate_relay_helper::generate_mocked_receive_message_proof_call_builder!( + BridgeHubWococoMessagesToBridgeHubRococoMessageLane, + BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesProofCallBuilder, + relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoMessages, + relay_bridge_hub_rococo_client::runtime::BridgeWococoMessagesCall::receive_messages_proof +); + +substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_builder!( + BridgeHubWococoMessagesToBridgeHubRococoMessageLane, + BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesDeliveryProofCallBuilder, + relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoMessages, + relay_bridge_hub_wococo_client::runtime::BridgeRococoMessagesCall::receive_messages_delivery_proof +); + +/// Description of BridgeHubWococo -> BridgeHubRococo messages bridge. +#[derive(Clone, Debug)] +pub struct BridgeHubWococoMessagesToBridgeHubRococoMessageLane; + +impl SubstrateMessageLane for BridgeHubWococoMessagesToBridgeHubRococoMessageLane { + type SourceChain = BridgeHubWococo; + type TargetChain = BridgeHubRococo; + + type ReceiveMessagesProofCallBuilder = + BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesProofCallBuilder; + type ReceiveMessagesDeliveryProofCallBuilder = + BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesDeliveryProofCallBuilder; +} diff --git a/relays/bin-substrate/src/chains/millau.rs b/relays/bin-substrate/src/chains/millau.rs new file mode 100644 index 00000000000..705755506f7 --- /dev/null +++ b/relays/bin-substrate/src/chains/millau.rs @@ -0,0 +1,63 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Millau chain specification for CLI. + +use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain}; +use bp_rialto_parachain::RIALTO_PARACHAIN_ID; +use bp_runtime::EncodedOrDecodedCall; +use relay_millau_client::Millau; +use sp_version::RuntimeVersion; +use xcm::latest::prelude::*; + +impl CliEncodeMessage for Millau { + fn encode_send_xcm( + message: xcm::VersionedXcm<()>, + bridge_instance_index: u8, + ) -> anyhow::Result> { + let dest = match bridge_instance_index { + bridge::MILLAU_TO_RIALTO_INDEX => + (Parent, X1(GlobalConsensus(millau_runtime::xcm_config::RialtoNetwork::get()))), + bridge::MILLAU_TO_RIALTO_PARACHAIN_INDEX => ( + Parent, + X2( + GlobalConsensus(millau_runtime::xcm_config::RialtoNetwork::get()), + Parachain(RIALTO_PARACHAIN_ID), + ), + ), + _ => anyhow::bail!( + "Unsupported target bridge pallet with instance index: {}", + bridge_instance_index + ), + }; + + Ok(millau_runtime::RuntimeCall::XcmPallet(millau_runtime::XcmCall::send { + dest: Box::new(dest.into()), + message: Box::new(message), + }) + .into()) + } +} + +impl CliChain for Millau { + const RUNTIME_VERSION: Option = Some(millau_runtime::VERSION); + + type KeyPair = sp_core::sr25519::Pair; + + fn ss58_format() -> u16 { + millau_runtime::SS58Prefix::get() as u16 + } +} diff --git a/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs b/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs new file mode 100644 index 00000000000..e0dbabd86b4 --- /dev/null +++ b/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs @@ -0,0 +1,57 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Millau-to-Rialto headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, RelayToRelayHeadersCliBridge}; +use substrate_relay_helper::finality::{ + engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder, + SubstrateFinalitySyncPipeline, +}; + +/// Description of Millau -> Rialto finalized headers bridge. +#[derive(Clone, Debug)] +pub struct MillauFinalityToRialto; + +impl SubstrateFinalitySyncPipeline for MillauFinalityToRialto { + type SourceChain = relay_millau_client::Millau; + type TargetChain = relay_rialto_client::Rialto; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder< + Self, + rialto_runtime::Runtime, + rialto_runtime::MillauGrandpaInstance, + >; +} + +//// `Millau` to `Rialto` bridge definition. +pub struct MillauToRialtoCliBridge {} + +impl CliBridgeBase for MillauToRialtoCliBridge { + type Source = relay_millau_client::Millau; + type Target = relay_rialto_client::Rialto; +} + +impl RelayToRelayHeadersCliBridge for MillauToRialtoCliBridge { + type Finality = MillauFinalityToRialto; +} + +impl MessagesCliBridge for MillauToRialtoCliBridge { + const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = + bp_rialto::TO_RIALTO_ESTIMATE_MESSAGE_FEE_METHOD; + type MessagesLane = crate::chains::millau_messages_to_rialto::MillauMessagesToRialto; +} diff --git a/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs b/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs new file mode 100644 index 00000000000..cdb3999db6e --- /dev/null +++ b/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs @@ -0,0 +1,76 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Millau-to-RialtoParachain headers sync entrypoint. + +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Millau-to-RialtoParachain headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, RelayToRelayHeadersCliBridge}; +use substrate_relay_helper::finality::{ + engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder, + SubstrateFinalitySyncPipeline, +}; + +/// Description of Millau -> Rialto finalized headers bridge. +#[derive(Clone, Debug)] +pub struct MillauFinalityToRialtoParachain; + +impl SubstrateFinalitySyncPipeline for MillauFinalityToRialtoParachain { + type SourceChain = relay_millau_client::Millau; + type TargetChain = relay_rialto_parachain_client::RialtoParachain; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder< + Self, + rialto_parachain_runtime::Runtime, + rialto_parachain_runtime::MillauGrandpaInstance, + >; +} + +//// `Millau` to `RialtoParachain` bridge definition. +pub struct MillauToRialtoParachainCliBridge {} + +impl CliBridgeBase for MillauToRialtoParachainCliBridge { + type Source = relay_millau_client::Millau; + type Target = relay_rialto_parachain_client::RialtoParachain; +} + +impl RelayToRelayHeadersCliBridge for MillauToRialtoParachainCliBridge { + type Finality = MillauFinalityToRialtoParachain; +} + +impl MessagesCliBridge for MillauToRialtoParachainCliBridge { + const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = + bp_rialto_parachain::TO_RIALTO_PARACHAIN_ESTIMATE_MESSAGE_FEE_METHOD; + type MessagesLane = + crate::chains::millau_messages_to_rialto_parachain::MillauMessagesToRialtoParachain; +} diff --git a/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs b/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs new file mode 100644 index 00000000000..b9920db53d8 --- /dev/null +++ b/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs @@ -0,0 +1,44 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Millau-to-Rialto messages sync entrypoint. + +use relay_millau_client::Millau; +use relay_rialto_client::Rialto; +use substrate_relay_helper::messages_lane::{ + DirectReceiveMessagesDeliveryProofCallBuilder, DirectReceiveMessagesProofCallBuilder, + SubstrateMessageLane, +}; + +/// Description of Millau -> Rialto messages bridge. +#[derive(Clone, Debug)] +pub struct MillauMessagesToRialto; + +impl SubstrateMessageLane for MillauMessagesToRialto { + type SourceChain = Millau; + type TargetChain = Rialto; + + type ReceiveMessagesProofCallBuilder = DirectReceiveMessagesProofCallBuilder< + Self, + rialto_runtime::Runtime, + rialto_runtime::WithMillauMessagesInstance, + >; + type ReceiveMessagesDeliveryProofCallBuilder = DirectReceiveMessagesDeliveryProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WithRialtoMessagesInstance, + >; +} diff --git a/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs b/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs new file mode 100644 index 00000000000..70cb887fa35 --- /dev/null +++ b/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs @@ -0,0 +1,44 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Millau-to-RialtoParachain messages sync entrypoint. + +use relay_millau_client::Millau; +use relay_rialto_parachain_client::RialtoParachain; +use substrate_relay_helper::messages_lane::{ + DirectReceiveMessagesDeliveryProofCallBuilder, DirectReceiveMessagesProofCallBuilder, + SubstrateMessageLane, +}; + +/// Description of Millau -> RialtoParachain messages bridge. +#[derive(Clone, Debug)] +pub struct MillauMessagesToRialtoParachain; + +impl SubstrateMessageLane for MillauMessagesToRialtoParachain { + type SourceChain = Millau; + type TargetChain = RialtoParachain; + + type ReceiveMessagesProofCallBuilder = DirectReceiveMessagesProofCallBuilder< + Self, + rialto_parachain_runtime::Runtime, + rialto_parachain_runtime::WithMillauMessagesInstance, + >; + type ReceiveMessagesDeliveryProofCallBuilder = DirectReceiveMessagesDeliveryProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WithRialtoParachainMessagesInstance, + >; +} diff --git a/relays/bin-substrate/src/chains/mod.rs b/relays/bin-substrate/src/chains/mod.rs new file mode 100644 index 00000000000..7caba7561dd --- /dev/null +++ b/relays/bin-substrate/src/chains/mod.rs @@ -0,0 +1,125 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Chain-specific relayer configuration. + +pub mod bridge_hub_rococo_messages_to_bridge_hub_wococo; +pub mod bridge_hub_wococo_messages_to_bridge_hub_rococo; +pub mod millau_headers_to_rialto; +pub mod millau_headers_to_rialto_parachain; +pub mod millau_messages_to_rialto; +pub mod millau_messages_to_rialto_parachain; +pub mod rialto_headers_to_millau; +pub mod rialto_messages_to_millau; +pub mod rialto_parachain_messages_to_millau; +pub mod rialto_parachains_to_millau; +pub mod rococo_headers_to_bridge_hub_wococo; +pub mod rococo_parachains_to_bridge_hub_wococo; +pub mod westend_headers_to_millau; +pub mod westend_parachains_to_millau; +pub mod wococo_headers_to_bridge_hub_rococo; +pub mod wococo_parachains_to_bridge_hub_rococo; + +mod millau; +mod rialto; +mod rialto_parachain; +mod rococo; +mod westend; +mod wococo; + +#[cfg(test)] +mod tests { + use crate::cli::encode_message; + use bp_messages::source_chain::TargetHeaderChain; + use bp_runtime::Chain as _; + use codec::Encode; + use relay_millau_client::Millau; + use relay_rialto_client::Rialto; + use relay_substrate_client::{ChainWithTransactions, SignParam, UnsignedTransaction}; + + #[test] + fn maximal_rialto_to_millau_message_size_is_computed_correctly() { + use rialto_runtime::millau_messages::Millau; + + let maximal_message_size = encode_message::compute_maximal_message_size( + bp_rialto::Rialto::max_extrinsic_size(), + bp_millau::Millau::max_extrinsic_size(), + ); + + let message = vec![42; maximal_message_size as _]; + assert_eq!(Millau::verify_message(&message), Ok(())); + + let message = vec![42; (maximal_message_size + 1) as _]; + assert!(Millau::verify_message(&message).is_err()); + } + + #[test] + fn maximal_size_remark_to_rialto_is_generated_correctly() { + assert!( + bridge_runtime_common::messages::target::maximal_incoming_message_size( + bp_rialto::Rialto::max_extrinsic_size() + ) > bp_millau::Millau::max_extrinsic_size(), + "We can't actually send maximal messages to Rialto from Millau, because Millau extrinsics can't be that large", + ) + } + #[test] + fn rialto_tx_extra_bytes_constant_is_correct() { + let rialto_call = rialto_runtime::RuntimeCall::System(rialto_runtime::SystemCall::remark { + remark: vec![], + }); + let rialto_tx = Rialto::sign_transaction( + SignParam { + spec_version: 1, + transaction_version: 1, + genesis_hash: Default::default(), + signer: sp_keyring::AccountKeyring::Alice.pair(), + }, + UnsignedTransaction::new(rialto_call.clone().into(), 0), + ) + .unwrap(); + let extra_bytes_in_transaction = rialto_tx.encode().len() - rialto_call.encode().len(); + assert!( + bp_rialto::TX_EXTRA_BYTES as usize >= extra_bytes_in_transaction, + "Hardcoded number of extra bytes in Rialto transaction {} is lower than actual value: {}", + bp_rialto::TX_EXTRA_BYTES, + extra_bytes_in_transaction, + ); + } + + #[test] + fn millau_tx_extra_bytes_constant_is_correct() { + let millau_call = millau_runtime::RuntimeCall::System(millau_runtime::SystemCall::remark { + remark: vec![], + }); + let millau_tx = Millau::sign_transaction( + SignParam { + spec_version: 0, + transaction_version: 0, + genesis_hash: Default::default(), + signer: sp_keyring::AccountKeyring::Alice.pair(), + }, + UnsignedTransaction::new(millau_call.clone().into(), 0), + ) + .unwrap(); + let extra_bytes_in_transaction = millau_tx.encode().len() - millau_call.encode().len(); + assert!( + bp_millau::TX_EXTRA_BYTES as usize >= extra_bytes_in_transaction, + "Hardcoded number of extra bytes in Millau transaction {} is lower than actual value: {}", + bp_millau::TX_EXTRA_BYTES, + extra_bytes_in_transaction, + ); + } +} diff --git a/relays/bin-substrate/src/chains/rialto.rs b/relays/bin-substrate/src/chains/rialto.rs new file mode 100644 index 00000000000..83bccf626b9 --- /dev/null +++ b/relays/bin-substrate/src/chains/rialto.rs @@ -0,0 +1,55 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rialto chain specification for CLI. + +use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain}; +use bp_runtime::EncodedOrDecodedCall; +use relay_rialto_client::Rialto; +use sp_version::RuntimeVersion; +use xcm::latest::prelude::*; + +impl CliEncodeMessage for Rialto { + fn encode_send_xcm( + message: xcm::VersionedXcm<()>, + bridge_instance_index: u8, + ) -> anyhow::Result> { + let dest = match bridge_instance_index { + bridge::RIALTO_TO_MILLAU_INDEX => + (Parent, X1(GlobalConsensus(rialto_runtime::xcm_config::MillauNetwork::get()))), + _ => anyhow::bail!( + "Unsupported target bridge pallet with instance index: {}", + bridge_instance_index + ), + }; + + Ok(rialto_runtime::RuntimeCall::XcmPallet(rialto_runtime::XcmCall::send { + dest: Box::new(dest.into()), + message: Box::new(message), + }) + .into()) + } +} + +impl CliChain for Rialto { + const RUNTIME_VERSION: Option = Some(rialto_runtime::VERSION); + + type KeyPair = sp_core::sr25519::Pair; + + fn ss58_format() -> u16 { + rialto_runtime::SS58Prefix::get() as u16 + } +} diff --git a/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs b/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs new file mode 100644 index 00000000000..b1ab4a8537b --- /dev/null +++ b/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs @@ -0,0 +1,57 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rialto-to-Millau headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, RelayToRelayHeadersCliBridge}; +use substrate_relay_helper::finality::{ + engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder, + SubstrateFinalitySyncPipeline, +}; + +/// Description of Millau -> Rialto finalized headers bridge. +#[derive(Clone, Debug)] +pub struct RialtoFinalityToMillau; + +impl SubstrateFinalitySyncPipeline for RialtoFinalityToMillau { + type SourceChain = relay_rialto_client::Rialto; + type TargetChain = relay_millau_client::Millau; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::RialtoGrandpaInstance, + >; +} + +//// `Rialto` to `Millau` bridge definition. +pub struct RialtoToMillauCliBridge {} + +impl CliBridgeBase for RialtoToMillauCliBridge { + type Source = relay_rialto_client::Rialto; + type Target = relay_millau_client::Millau; +} + +impl RelayToRelayHeadersCliBridge for RialtoToMillauCliBridge { + type Finality = RialtoFinalityToMillau; +} + +impl MessagesCliBridge for RialtoToMillauCliBridge { + const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = + bp_millau::TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD; + type MessagesLane = crate::chains::rialto_messages_to_millau::RialtoMessagesToMillau; +} diff --git a/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs b/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs new file mode 100644 index 00000000000..80b6b9fdbc6 --- /dev/null +++ b/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs @@ -0,0 +1,44 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rialto-to-Millau messages sync entrypoint. + +use relay_millau_client::Millau; +use relay_rialto_client::Rialto; +use substrate_relay_helper::messages_lane::{ + DirectReceiveMessagesDeliveryProofCallBuilder, DirectReceiveMessagesProofCallBuilder, + SubstrateMessageLane, +}; + +/// Description of Rialto -> Millau messages bridge. +#[derive(Clone, Debug)] +pub struct RialtoMessagesToMillau; + +impl SubstrateMessageLane for RialtoMessagesToMillau { + type SourceChain = Rialto; + type TargetChain = Millau; + + type ReceiveMessagesProofCallBuilder = DirectReceiveMessagesProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WithRialtoMessagesInstance, + >; + type ReceiveMessagesDeliveryProofCallBuilder = DirectReceiveMessagesDeliveryProofCallBuilder< + Self, + rialto_runtime::Runtime, + rialto_runtime::WithMillauMessagesInstance, + >; +} diff --git a/relays/bin-substrate/src/chains/rialto_parachain.rs b/relays/bin-substrate/src/chains/rialto_parachain.rs new file mode 100644 index 00000000000..a480dc3eebb --- /dev/null +++ b/relays/bin-substrate/src/chains/rialto_parachain.rs @@ -0,0 +1,57 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rialto parachain specification for CLI. + +use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain}; +use bp_runtime::EncodedOrDecodedCall; +use relay_rialto_parachain_client::RialtoParachain; +use sp_version::RuntimeVersion; +use xcm::latest::prelude::*; + +impl CliEncodeMessage for RialtoParachain { + fn encode_send_xcm( + message: xcm::VersionedXcm<()>, + bridge_instance_index: u8, + ) -> anyhow::Result> { + let dest = match bridge_instance_index { + bridge::RIALTO_PARACHAIN_TO_MILLAU_INDEX => + (Parent, X1(GlobalConsensus(rialto_parachain_runtime::MillauNetwork::get()))), + _ => anyhow::bail!( + "Unsupported target bridge pallet with instance index: {}", + bridge_instance_index + ), + }; + + Ok(rialto_parachain_runtime::RuntimeCall::PolkadotXcm( + rialto_parachain_runtime::XcmCall::send { + dest: Box::new(dest.into()), + message: Box::new(message), + }, + ) + .into()) + } +} + +impl CliChain for RialtoParachain { + const RUNTIME_VERSION: Option = Some(rialto_parachain_runtime::VERSION); + + type KeyPair = sp_core::sr25519::Pair; + + fn ss58_format() -> u16 { + rialto_parachain_runtime::SS58Prefix::get() as u16 + } +} diff --git a/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs b/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs new file mode 100644 index 00000000000..5cca26105b8 --- /dev/null +++ b/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs @@ -0,0 +1,44 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! RialtoParachain-to-Millau messages sync entrypoint. + +use relay_millau_client::Millau; +use relay_rialto_parachain_client::RialtoParachain; +use substrate_relay_helper::messages_lane::{ + DirectReceiveMessagesDeliveryProofCallBuilder, DirectReceiveMessagesProofCallBuilder, + SubstrateMessageLane, +}; + +/// Description of RialtoParachain -> Millau messages bridge. +#[derive(Clone, Debug)] +pub struct RialtoParachainMessagesToMillau; + +impl SubstrateMessageLane for RialtoParachainMessagesToMillau { + type SourceChain = RialtoParachain; + type TargetChain = Millau; + + type ReceiveMessagesProofCallBuilder = DirectReceiveMessagesProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WithRialtoParachainMessagesInstance, + >; + type ReceiveMessagesDeliveryProofCallBuilder = DirectReceiveMessagesDeliveryProofCallBuilder< + Self, + rialto_parachain_runtime::Runtime, + rialto_parachain_runtime::WithMillauMessagesInstance, + >; +} diff --git a/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs b/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs new file mode 100644 index 00000000000..911d4399389 --- /dev/null +++ b/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs @@ -0,0 +1,74 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rialto-to-Millau parachains sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}; +use parachains_relay::ParachainsPipeline; +use relay_millau_client::Millau; +use relay_rialto_client::Rialto; +use relay_rialto_parachain_client::RialtoParachain; +use substrate_relay_helper::parachains::{ + DirectSubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, +}; + +/// Rialto-to-Millau parachains sync description. +#[derive(Clone, Debug)] +pub struct RialtoParachainsToMillau; + +impl ParachainsPipeline for RialtoParachainsToMillau { + type SourceChain = Rialto; + type TargetChain = Millau; +} + +impl SubstrateParachainsPipeline for RialtoParachainsToMillau { + type SourceParachain = RialtoParachain; + type SourceRelayChain = Rialto; + type TargetChain = Millau; + + type SubmitParachainHeadsCallBuilder = RialtoParachainsToMillauSubmitParachainHeadsCallBuilder; + + const SOURCE_PARACHAIN_PARA_ID: u32 = bp_rialto_parachain::RIALTO_PARACHAIN_ID; +} + +/// `submit_parachain_heads` call builder for Rialto-to-Millau parachains sync pipeline. +pub type RialtoParachainsToMillauSubmitParachainHeadsCallBuilder = + DirectSubmitParachainHeadsCallBuilder< + RialtoParachainsToMillau, + millau_runtime::Runtime, + millau_runtime::WithRialtoParachainsInstance, + >; + +//// `RialtoParachain` to `Millau` bridge definition. +pub struct RialtoParachainToMillauCliBridge {} + +impl CliBridgeBase for RialtoParachainToMillauCliBridge { + type Source = RialtoParachain; + type Target = Millau; +} + +impl ParachainToRelayHeadersCliBridge for RialtoParachainToMillauCliBridge { + type SourceRelay = Rialto; + type ParachainFinality = RialtoParachainsToMillau; + type RelayFinality = crate::chains::rialto_headers_to_millau::RialtoFinalityToMillau; +} + +impl MessagesCliBridge for RialtoParachainToMillauCliBridge { + const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = + bp_millau::TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD; + type MessagesLane = + crate::chains::rialto_parachain_messages_to_millau::RialtoParachainMessagesToMillau; +} diff --git a/relays/bin-substrate/src/chains/rococo.rs b/relays/bin-substrate/src/chains/rococo.rs new file mode 100644 index 00000000000..6a99eab6cb3 --- /dev/null +++ b/relays/bin-substrate/src/chains/rococo.rs @@ -0,0 +1,42 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rococo + Rococo parachains specification for CLI. + +use crate::cli::CliChain; +use relay_bridge_hub_rococo_client::BridgeHubRococo; +use relay_rococo_client::Rococo; +use sp_version::RuntimeVersion; + +impl CliChain for Rococo { + const RUNTIME_VERSION: Option = None; + + type KeyPair = sp_core::sr25519::Pair; + + fn ss58_format() -> u16 { + bp_rococo::SS58Prefix::get() as u16 + } +} + +impl CliChain for BridgeHubRococo { + const RUNTIME_VERSION: Option = None; + + type KeyPair = sp_core::sr25519::Pair; + + fn ss58_format() -> u16 { + relay_bridge_hub_rococo_client::runtime::SS58Prefix::get() + } +} diff --git a/relays/bin-substrate/src/chains/rococo_headers_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/chains/rococo_headers_to_bridge_hub_wococo.rs new file mode 100644 index 00000000000..2ec13614a34 --- /dev/null +++ b/relays/bin-substrate/src/chains/rococo_headers_to_bridge_hub_wococo.rs @@ -0,0 +1,53 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rococo-to-Wococo bridge hubs headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, RelayToRelayHeadersCliBridge}; +use substrate_relay_helper::finality::{ + engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline, +}; + +/// Description of Rococo -> Wococo finalized headers bridge. +#[derive(Clone, Debug)] +pub struct RococoFinalityToBridgeHubWococo; + +substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!( + RococoFinalityToBridgeHubWococo, + RococoFinalityToBridgeHubWococoCallBuilder, + relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoGrandpa, + relay_bridge_hub_wococo_client::runtime::BridgeGrandpaRococoCall::submit_finality_proof +); + +impl SubstrateFinalitySyncPipeline for RococoFinalityToBridgeHubWococo { + type SourceChain = relay_rococo_client::Rococo; + type TargetChain = relay_bridge_hub_wococo_client::BridgeHubWococo; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = RococoFinalityToBridgeHubWococoCallBuilder; +} + +/// `Rococo` to BridgeHub `Wococo` bridge definition. +pub struct RococoToBridgeHubWococoCliBridge {} + +impl CliBridgeBase for RococoToBridgeHubWococoCliBridge { + type Source = relay_rococo_client::Rococo; + type Target = relay_bridge_hub_wococo_client::BridgeHubWococo; +} + +impl RelayToRelayHeadersCliBridge for RococoToBridgeHubWococoCliBridge { + type Finality = RococoFinalityToBridgeHubWococo; +} diff --git a/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs new file mode 100644 index 00000000000..8b208712db5 --- /dev/null +++ b/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs @@ -0,0 +1,85 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Wococo-to-Rococo parachains sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}; +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use parachains_relay::ParachainsPipeline; +use relay_substrate_client::{CallOf, HeaderIdOf}; +use substrate_relay_helper::parachains::{ + SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, +}; + +/// BridgeHub-to-BridgeHub parachain sync description. +#[derive(Clone, Debug)] +pub struct BridgeHubRococoToBridgeHubWococo; + +impl ParachainsPipeline for BridgeHubRococoToBridgeHubWococo { + type SourceChain = relay_rococo_client::Rococo; + type TargetChain = relay_bridge_hub_wococo_client::BridgeHubWococo; +} + +impl SubstrateParachainsPipeline for BridgeHubRococoToBridgeHubWococo { + type SourceParachain = relay_bridge_hub_rococo_client::BridgeHubRococo; + type SourceRelayChain = relay_rococo_client::Rococo; + type TargetChain = relay_bridge_hub_wococo_client::BridgeHubWococo; + + type SubmitParachainHeadsCallBuilder = BridgeHubRococoToBridgeHubWococoCallBuilder; + + const SOURCE_PARACHAIN_PARA_ID: u32 = bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID; +} + +pub struct BridgeHubRococoToBridgeHubWococoCallBuilder; +impl SubmitParachainHeadsCallBuilder + for BridgeHubRococoToBridgeHubWococoCallBuilder +{ + fn build_submit_parachain_heads_call( + at_relay_block: HeaderIdOf, + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> CallOf { + relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoParachain( + relay_bridge_hub_wococo_client::runtime::BridgeParachainCall::submit_parachain_heads( + (at_relay_block.0, at_relay_block.1), + parachains, + parachain_heads_proof, + ), + ) + } +} + +/// `BridgeHubParachain` to `BridgeHubParachain` bridge definition. +pub struct BridgeHubRococoToBridgeHubWococoCliBridge {} + +impl ParachainToRelayHeadersCliBridge for BridgeHubRococoToBridgeHubWococoCliBridge { + type SourceRelay = relay_rococo_client::Rococo; + type ParachainFinality = BridgeHubRococoToBridgeHubWococo; + type RelayFinality = + crate::chains::rococo_headers_to_bridge_hub_wococo::RococoFinalityToBridgeHubWococo; +} + +impl CliBridgeBase for BridgeHubRococoToBridgeHubWococoCliBridge { + type Source = relay_bridge_hub_rococo_client::BridgeHubRococo; + type Target = relay_bridge_hub_wococo_client::BridgeHubWococo; +} + +impl MessagesCliBridge for BridgeHubRococoToBridgeHubWococoCliBridge { + const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = + bp_bridge_hub_wococo::TO_BRIDGE_HUB_WOCOCO_ESTIMATE_MESSAGE_FEE_METHOD; + type MessagesLane = + crate::chains::bridge_hub_rococo_messages_to_bridge_hub_wococo::BridgeHubRococoMessagesToBridgeHubWococoMessageLane; +} diff --git a/relays/bin-substrate/src/chains/westend.rs b/relays/bin-substrate/src/chains/westend.rs new file mode 100644 index 00000000000..1627bc015d3 --- /dev/null +++ b/relays/bin-substrate/src/chains/westend.rs @@ -0,0 +1,47 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Westend chain specification for CLI. + +use crate::cli::CliChain; +use relay_westend_client::{Westend, Westmint}; +use sp_version::RuntimeVersion; + +impl CliChain for Westend { + const RUNTIME_VERSION: Option = None; + + type KeyPair = sp_core::sr25519::Pair; + + fn ss58_format() -> u16 { + sp_core::crypto::Ss58AddressFormat::from( + sp_core::crypto::Ss58AddressFormatRegistry::SubstrateAccount, + ) + .into() + } +} + +impl CliChain for Westmint { + const RUNTIME_VERSION: Option = None; + + type KeyPair = sp_core::sr25519::Pair; + + fn ss58_format() -> u16 { + sp_core::crypto::Ss58AddressFormat::from( + sp_core::crypto::Ss58AddressFormatRegistry::SubstrateAccount, + ) + .into() + } +} diff --git a/relays/bin-substrate/src/chains/westend_headers_to_millau.rs b/relays/bin-substrate/src/chains/westend_headers_to_millau.rs new file mode 100644 index 00000000000..2a253756c2b --- /dev/null +++ b/relays/bin-substrate/src/chains/westend_headers_to_millau.rs @@ -0,0 +1,51 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Westend-to-Millau headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, RelayToRelayHeadersCliBridge}; +use substrate_relay_helper::finality::{ + engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder, + SubstrateFinalitySyncPipeline, +}; + +/// Description of Westend -> Millau finalized headers bridge. +#[derive(Clone, Debug)] +pub struct WestendFinalityToMillau; + +impl SubstrateFinalitySyncPipeline for WestendFinalityToMillau { + type SourceChain = relay_westend_client::Westend; + type TargetChain = relay_millau_client::Millau; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WestendGrandpaInstance, + >; +} + +//// `Westend` to `Millau` bridge definition. +pub struct WestendToMillauCliBridge {} + +impl CliBridgeBase for WestendToMillauCliBridge { + type Source = relay_westend_client::Westend; + type Target = relay_millau_client::Millau; +} + +impl RelayToRelayHeadersCliBridge for WestendToMillauCliBridge { + type Finality = WestendFinalityToMillau; +} diff --git a/relays/bin-substrate/src/chains/westend_parachains_to_millau.rs b/relays/bin-substrate/src/chains/westend_parachains_to_millau.rs new file mode 100644 index 00000000000..73409e65569 --- /dev/null +++ b/relays/bin-substrate/src/chains/westend_parachains_to_millau.rs @@ -0,0 +1,66 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Westend-to-Millau parachains sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, ParachainToRelayHeadersCliBridge}; +use parachains_relay::ParachainsPipeline; +use relay_millau_client::Millau; +use relay_westend_client::{Westend, Westmint}; +use substrate_relay_helper::parachains::{ + DirectSubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, +}; + +/// Westend-to-Millau parachains sync description. +#[derive(Clone, Debug)] +pub struct WestendParachainsToMillau; + +impl ParachainsPipeline for WestendParachainsToMillau { + type SourceChain = Westend; + type TargetChain = Millau; +} + +impl SubstrateParachainsPipeline for WestendParachainsToMillau { + type SourceParachain = Westmint; + type SourceRelayChain = Westend; + type TargetChain = Millau; + + type SubmitParachainHeadsCallBuilder = WestendParachainsToMillauSubmitParachainHeadsCallBuilder; + + const SOURCE_PARACHAIN_PARA_ID: u32 = bp_westend::WESTMINT_PARACHAIN_ID; +} + +/// `submit_parachain_heads` call builder for Rialto-to-Millau parachains sync pipeline. +pub type WestendParachainsToMillauSubmitParachainHeadsCallBuilder = + DirectSubmitParachainHeadsCallBuilder< + WestendParachainsToMillau, + millau_runtime::Runtime, + millau_runtime::WithWestendParachainsInstance, + >; + +//// `WestendParachain` to `Millau` bridge definition. +pub struct WestmintToMillauCliBridge {} + +impl ParachainToRelayHeadersCliBridge for WestmintToMillauCliBridge { + type SourceRelay = Westend; + type ParachainFinality = WestendParachainsToMillau; + type RelayFinality = crate::chains::westend_headers_to_millau::WestendFinalityToMillau; +} + +impl CliBridgeBase for WestmintToMillauCliBridge { + type Source = Westmint; + type Target = Millau; +} diff --git a/relays/bin-substrate/src/chains/wococo.rs b/relays/bin-substrate/src/chains/wococo.rs new file mode 100644 index 00000000000..c44f3ac811f --- /dev/null +++ b/relays/bin-substrate/src/chains/wococo.rs @@ -0,0 +1,42 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Wococo + Wococo parachains specification for CLI. + +use crate::cli::CliChain; +use relay_bridge_hub_wococo_client::BridgeHubWococo; +use relay_wococo_client::Wococo; +use sp_version::RuntimeVersion; + +impl CliChain for Wococo { + const RUNTIME_VERSION: Option = None; + + type KeyPair = sp_core::sr25519::Pair; + + fn ss58_format() -> u16 { + bp_wococo::SS58Prefix::get() as u16 + } +} + +impl CliChain for BridgeHubWococo { + const RUNTIME_VERSION: Option = None; + + type KeyPair = sp_core::sr25519::Pair; + + fn ss58_format() -> u16 { + relay_bridge_hub_wococo_client::runtime::SS58Prefix::get() + } +} diff --git a/relays/bin-substrate/src/chains/wococo_headers_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/chains/wococo_headers_to_bridge_hub_rococo.rs new file mode 100644 index 00000000000..30d6af00cd3 --- /dev/null +++ b/relays/bin-substrate/src/chains/wococo_headers_to_bridge_hub_rococo.rs @@ -0,0 +1,53 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Wococo-to-Rococo bridge hubs headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, RelayToRelayHeadersCliBridge}; +use substrate_relay_helper::finality::{ + engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline, +}; + +/// Description of Wococo -> Rococo finalized headers bridge. +#[derive(Clone, Debug)] +pub struct WococoFinalityToBridgeHubRococo; + +substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!( + WococoFinalityToBridgeHubRococo, + WococoFinalityToBridgeHubRococoCallBuilder, + relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoGrandpa, + relay_bridge_hub_rococo_client::runtime::BridgeWococoGrandpaCall::submit_finality_proof +); + +impl SubstrateFinalitySyncPipeline for WococoFinalityToBridgeHubRococo { + type SourceChain = relay_wococo_client::Wococo; + type TargetChain = relay_bridge_hub_rococo_client::BridgeHubRococo; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = WococoFinalityToBridgeHubRococoCallBuilder; +} + +/// `Wococo` to BridgeHub `Rococo` bridge definition. +pub struct WococoToBridgeHubRococoCliBridge {} + +impl CliBridgeBase for WococoToBridgeHubRococoCliBridge { + type Source = relay_wococo_client::Wococo; + type Target = relay_bridge_hub_rococo_client::BridgeHubRococo; +} + +impl RelayToRelayHeadersCliBridge for WococoToBridgeHubRococoCliBridge { + type Finality = WococoFinalityToBridgeHubRococo; +} diff --git a/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs new file mode 100644 index 00000000000..453455a7b56 --- /dev/null +++ b/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs @@ -0,0 +1,85 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rococo-to-Wococo parachains sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}; +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use parachains_relay::ParachainsPipeline; +use relay_substrate_client::{CallOf, HeaderIdOf}; +use substrate_relay_helper::parachains::{ + SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, +}; + +/// BridgeHub-to-BridgeHub parachain sync description. +#[derive(Clone, Debug)] +pub struct BridgeHubWococoToBridgeHubRococo; + +impl ParachainsPipeline for BridgeHubWococoToBridgeHubRococo { + type SourceChain = relay_wococo_client::Wococo; + type TargetChain = relay_bridge_hub_rococo_client::BridgeHubRococo; +} + +impl SubstrateParachainsPipeline for BridgeHubWococoToBridgeHubRococo { + type SourceParachain = relay_bridge_hub_wococo_client::BridgeHubWococo; + type SourceRelayChain = relay_wococo_client::Wococo; + type TargetChain = relay_bridge_hub_rococo_client::BridgeHubRococo; + + type SubmitParachainHeadsCallBuilder = BridgeHubWococoToBridgeHubRococoCallBuilder; + + const SOURCE_PARACHAIN_PARA_ID: u32 = bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID; +} + +pub struct BridgeHubWococoToBridgeHubRococoCallBuilder; +impl SubmitParachainHeadsCallBuilder + for BridgeHubWococoToBridgeHubRococoCallBuilder +{ + fn build_submit_parachain_heads_call( + at_relay_block: HeaderIdOf, + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> CallOf { + relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoParachain( + relay_bridge_hub_rococo_client::runtime::BridgeParachainCall::submit_parachain_heads( + (at_relay_block.0, at_relay_block.1), + parachains, + parachain_heads_proof, + ), + ) + } +} + +/// `BridgeHubParachain` to `BridgeHubParachain` bridge definition. +pub struct BridgeHubWococoToBridgeHubRococoCliBridge {} + +impl ParachainToRelayHeadersCliBridge for BridgeHubWococoToBridgeHubRococoCliBridge { + type SourceRelay = relay_wococo_client::Wococo; + type ParachainFinality = BridgeHubWococoToBridgeHubRococo; + type RelayFinality = + crate::chains::wococo_headers_to_bridge_hub_rococo::WococoFinalityToBridgeHubRococo; +} + +impl CliBridgeBase for BridgeHubWococoToBridgeHubRococoCliBridge { + type Source = relay_bridge_hub_wococo_client::BridgeHubWococo; + type Target = relay_bridge_hub_rococo_client::BridgeHubRococo; +} + +impl MessagesCliBridge for BridgeHubWococoToBridgeHubRococoCliBridge { + const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = + bp_bridge_hub_rococo::TO_BRIDGE_HUB_ROCOCO_ESTIMATE_MESSAGE_FEE_METHOD; + type MessagesLane = + crate::chains::bridge_hub_wococo_messages_to_bridge_hub_rococo::BridgeHubWococoMessagesToBridgeHubRococoMessageLane; +} diff --git a/relays/bin-substrate/src/cli/bridge.rs b/relays/bin-substrate/src/cli/bridge.rs new file mode 100644 index 00000000000..ae2a36cd1d5 --- /dev/null +++ b/relays/bin-substrate/src/cli/bridge.rs @@ -0,0 +1,105 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::cli::CliChain; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use parachains_relay::ParachainsPipeline; +use relay_substrate_client::{AccountKeyPairOf, Chain, ChainWithTransactions, RelayChain}; +use strum::{EnumString, EnumVariantNames}; +use substrate_relay_helper::{ + finality::SubstrateFinalitySyncPipeline, messages_lane::SubstrateMessageLane, + parachains::SubstrateParachainsPipeline, +}; + +#[derive(Debug, PartialEq, Eq, EnumString, EnumVariantNames)] +#[strum(serialize_all = "kebab_case")] +/// Supported full bridges (headers + messages). +pub enum FullBridge { + MillauToRialto, + RialtoToMillau, + MillauToRialtoParachain, + RialtoParachainToMillau, + BridgeHubRococoToBridgeHubWococo, + BridgeHubWococoToBridgeHubRococo, +} + +impl FullBridge { + /// Return instance index of the bridge pallet in source runtime. + pub fn bridge_instance_index(&self) -> u8 { + match self { + Self::MillauToRialto => MILLAU_TO_RIALTO_INDEX, + Self::RialtoToMillau => RIALTO_TO_MILLAU_INDEX, + Self::MillauToRialtoParachain => MILLAU_TO_RIALTO_PARACHAIN_INDEX, + Self::RialtoParachainToMillau => RIALTO_PARACHAIN_TO_MILLAU_INDEX, + Self::BridgeHubRococoToBridgeHubWococo | Self::BridgeHubWococoToBridgeHubRococo => + unimplemented!("Relay doesn't support send-message subcommand on bridge hubs"), + } + } +} + +pub const RIALTO_TO_MILLAU_INDEX: u8 = 0; +pub const MILLAU_TO_RIALTO_INDEX: u8 = 0; +pub const MILLAU_TO_RIALTO_PARACHAIN_INDEX: u8 = 1; +pub const RIALTO_PARACHAIN_TO_MILLAU_INDEX: u8 = 0; + +/// Minimal bridge representation that can be used from the CLI. +/// It connects a source chain to a target chain. +pub trait CliBridgeBase: Sized { + /// The source chain. + type Source: Chain + CliChain; + /// The target chain. + type Target: ChainWithTransactions + CliChain>; +} + +/// Bridge representation that can be used from the CLI for relaying headers +/// from a relay chain to a relay chain. +pub trait RelayToRelayHeadersCliBridge: CliBridgeBase { + /// Finality proofs synchronization pipeline. + type Finality: SubstrateFinalitySyncPipeline< + SourceChain = Self::Source, + TargetChain = Self::Target, + >; +} + +/// Bridge representation that can be used from the CLI for relaying headers +/// from a parachain to a relay chain. +pub trait ParachainToRelayHeadersCliBridge: CliBridgeBase { + // The `CliBridgeBase` type represents the parachain in this situation. + // We need to add an extra type for the relay chain. + type SourceRelay: Chain + + CliChain + + RelayChain; + /// Finality proofs synchronization pipeline (source parachain -> target). + type ParachainFinality: SubstrateParachainsPipeline< + SourceRelayChain = Self::SourceRelay, + SourceParachain = Self::Source, + TargetChain = Self::Target, + > + ParachainsPipeline; + /// Finality proofs synchronization pipeline (source relay chain -> target). + type RelayFinality: SubstrateFinalitySyncPipeline< + SourceChain = Self::SourceRelay, + TargetChain = Self::Target, + >; +} + +/// Bridge representation that can be used from the CLI for relaying messages. +pub trait MessagesCliBridge: CliBridgeBase { + /// Name of the runtime method used to estimate the message dispatch and delivery fee for the + /// defined bridge. + const ESTIMATE_MESSAGE_FEE_METHOD: &'static str; + /// The Source -> Destination messages synchronization pipeline. + type MessagesLane: SubstrateMessageLane; +} diff --git a/relays/bin-substrate/src/cli/chain_schema.rs b/relays/bin-substrate/src/cli/chain_schema.rs new file mode 100644 index 00000000000..8023fd9b0f7 --- /dev/null +++ b/relays/bin-substrate/src/cli/chain_schema.rs @@ -0,0 +1,409 @@ +// Copyright 2019-2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use sp_core::Pair; +use structopt::StructOpt; +use strum::{EnumString, EnumVariantNames}; + +use crate::cli::CliChain; +pub use relay_substrate_client::ChainRuntimeVersion; +use substrate_relay_helper::TransactionParams; + +#[doc = "Runtime version params."] +#[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy, EnumString, EnumVariantNames)] +pub enum RuntimeVersionType { + /// Auto query version from chain + Auto, + /// Custom `spec_version` and `transaction_version` + Custom, + /// Read version from bundle dependencies directly. + Bundle, +} + +/// Create chain-specific set of runtime version parameters. +#[macro_export] +macro_rules! declare_chain_runtime_version_params_cli_schema { + ($chain:ident, $chain_prefix:ident) => { + bp_runtime::paste::item! { + #[doc = $chain " runtime version params."] + #[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy)] + pub struct [<$chain RuntimeVersionParams>] { + #[doc = "The type of runtime version for chain " $chain] + #[structopt(long, default_value = "Bundle")] + pub [<$chain_prefix _version_mode>]: RuntimeVersionType, + #[doc = "The custom sepc_version for chain " $chain] + #[structopt(long)] + pub [<$chain_prefix _spec_version>]: Option, + #[doc = "The custom transaction_version for chain " $chain] + #[structopt(long)] + pub [<$chain_prefix _transaction_version>]: Option, + } + + impl [<$chain RuntimeVersionParams>] { + /// Converts self into `ChainRuntimeVersion`. + pub fn into_runtime_version( + self, + bundle_runtime_version: Option, + ) -> anyhow::Result { + Ok(match self.[<$chain_prefix _version_mode>] { + RuntimeVersionType::Auto => ChainRuntimeVersion::Auto, + RuntimeVersionType::Custom => { + let except_spec_version = self.[<$chain_prefix _spec_version>] + .ok_or_else(|| anyhow::Error::msg(format!("The {}-spec-version is required when choose custom mode", stringify!($chain_prefix))))?; + let except_transaction_version = self.[<$chain_prefix _transaction_version>] + .ok_or_else(|| anyhow::Error::msg(format!("The {}-transaction-version is required when choose custom mode", stringify!($chain_prefix))))?; + ChainRuntimeVersion::Custom( + except_spec_version, + except_transaction_version + ) + }, + RuntimeVersionType::Bundle => match bundle_runtime_version { + Some(runtime_version) => ChainRuntimeVersion::Custom( + runtime_version.spec_version, + runtime_version.transaction_version + ), + None => ChainRuntimeVersion::Auto + }, + }) + } + } + } + }; +} + +/// Create chain-specific set of runtime version parameters. +#[macro_export] +macro_rules! declare_chain_connection_params_cli_schema { + ($chain:ident, $chain_prefix:ident) => { + bp_runtime::paste::item! { + #[doc = $chain " connection params."] + #[derive(StructOpt, Debug, PartialEq, Eq, Clone)] + pub struct [<$chain ConnectionParams>] { + #[doc = "Connect to " $chain " node at given host."] + #[structopt(long, default_value = "127.0.0.1")] + pub [<$chain_prefix _host>]: String, + #[doc = "Connect to " $chain " node websocket server at given port."] + #[structopt(long, default_value = "9944")] + pub [<$chain_prefix _port>]: u16, + #[doc = "Use secure websocket connection."] + #[structopt(long)] + pub [<$chain_prefix _secure>]: bool, + #[doc = "Custom runtime version"] + #[structopt(flatten)] + pub [<$chain_prefix _runtime_version>]: [<$chain RuntimeVersionParams>], + } + + impl [<$chain ConnectionParams>] { + /// Convert connection params into Substrate client. + #[allow(dead_code)] + pub async fn into_client( + self, + ) -> anyhow::Result> { + let chain_runtime_version = self + .[<$chain_prefix _runtime_version>] + .into_runtime_version(Chain::RUNTIME_VERSION)?; + Ok(relay_substrate_client::Client::new(relay_substrate_client::ConnectionParams { + host: self.[<$chain_prefix _host>], + port: self.[<$chain_prefix _port>], + secure: self.[<$chain_prefix _secure>], + chain_runtime_version, + }) + .await + ) + } + } + } + }; +} + +/// Helper trait to override transaction parameters differently. +pub trait TransactionParamsProvider { + /// Returns `true` if transaction parameters are defined by this provider. + fn is_defined(&self) -> bool; + /// Returns transaction parameters. + fn transaction_params( + &self, + ) -> anyhow::Result>; + + /// Returns transaction parameters, defined by `self` provider or, if they're not defined, + /// defined by `other` provider. + fn transaction_params_or( + &self, + other: &T, + ) -> anyhow::Result> { + if self.is_defined() { + self.transaction_params::() + } else { + other.transaction_params::() + } + } +} + +/// Create chain-specific set of signing parameters. +#[macro_export] +macro_rules! declare_chain_signing_params_cli_schema { + ($chain:ident, $chain_prefix:ident) => { + bp_runtime::paste::item! { + #[doc = $chain " signing params."] + #[derive(StructOpt, Debug, PartialEq, Eq, Clone)] + pub struct [<$chain SigningParams>] { + #[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."] + #[structopt(long)] + pub [<$chain_prefix _signer>]: Option, + #[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."] + #[structopt(long)] + pub [<$chain_prefix _signer_password>]: Option, + + #[doc = "Path to the file, that contains SURI of secret key to use when transactions are submitted to the " $chain " node. Can be overridden with " $chain_prefix "_signer option."] + #[structopt(long)] + pub [<$chain_prefix _signer_file>]: Option, + #[doc = "Path to the file, that password for the SURI of secret key to use when transactions are submitted to the " $chain " node. Can be overridden with " $chain_prefix "_signer_password option."] + #[structopt(long)] + pub [<$chain_prefix _signer_password_file>]: Option, + + #[doc = "Transactions mortality period, in blocks. MUST be a power of two in [4; 65536] range. MAY NOT be larger than `BlockHashCount` parameter of the chain system module."] + #[structopt(long)] + pub [<$chain_prefix _transactions_mortality>]: Option, + } + + impl [<$chain SigningParams>] { + /// Return transactions mortality. + #[allow(dead_code)] + pub fn transactions_mortality(&self) -> anyhow::Result> { + self.[<$chain_prefix _transactions_mortality>] + .map(|transactions_mortality| { + if !(4..=65536).contains(&transactions_mortality) + || !transactions_mortality.is_power_of_two() + { + Err(anyhow::format_err!( + "Transactions mortality {} is not a power of two in a [4; 65536] range", + transactions_mortality, + )) + } else { + Ok(transactions_mortality) + } + }) + .transpose() + } + + /// Parse signing params into chain-specific KeyPair. + #[allow(dead_code)] + pub fn to_keypair(&self) -> anyhow::Result { + let suri = match (self.[<$chain_prefix _signer>].as_ref(), self.[<$chain_prefix _signer_file>].as_ref()) { + (Some(suri), _) => suri.to_owned(), + (None, Some(suri_file)) => std::fs::read_to_string(suri_file) + .map_err(|err| anyhow::format_err!( + "Failed to read SURI from file {:?}: {}", + suri_file, + err, + ))?, + (None, None) => return Err(anyhow::format_err!( + "One of options must be specified: '{}' or '{}'", + stringify!([<$chain_prefix _signer>]), + stringify!([<$chain_prefix _signer_file>]), + )), + }; + + let suri_password = match ( + self.[<$chain_prefix _signer_password>].as_ref(), + self.[<$chain_prefix _signer_password_file>].as_ref(), + ) { + (Some(suri_password), _) => Some(suri_password.to_owned()), + (None, Some(suri_password_file)) => std::fs::read_to_string(suri_password_file) + .map(Some) + .map_err(|err| anyhow::format_err!( + "Failed to read SURI password from file {:?}: {}", + suri_password_file, + err, + ))?, + _ => None, + }; + + use sp_core::crypto::Pair; + + Chain::KeyPair::from_string( + &suri, + suri_password.as_deref() + ).map_err(|e| anyhow::format_err!("{:?}", e)) + } + } + + #[allow(dead_code)] + impl TransactionParamsProvider for [<$chain SigningParams>] { + fn is_defined(&self) -> bool { + self.[<$chain_prefix _signer>].is_some() || self.[<$chain_prefix _signer_file>].is_some() + } + + fn transaction_params(&self) -> anyhow::Result> { + Ok(TransactionParams { + mortality: self.transactions_mortality()?, + signer: self.to_keypair::()?, + }) + } + } + } + }; +} + +/// Create chain-specific set of messages pallet owner signing parameters. +#[macro_export] +macro_rules! declare_chain_messages_pallet_owner_signing_params_cli_schema { + ($chain:ident, $chain_prefix:ident) => { + bp_runtime::paste::item! { + #[doc = "Parameters required to sign transaction on behalf of owner of the messages pallet at " $chain "."] + #[derive(StructOpt, Debug, PartialEq, Eq)] + pub struct [<$chain MessagesPalletOwnerSigningParams>] { + #[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."] + #[structopt(long)] + pub [<$chain_prefix _messages_pallet_owner>]: Option, + #[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."] + #[structopt(long)] + pub [<$chain_prefix _messages_pallet_owner_password>]: Option, + } + + #[allow(dead_code)] + impl [<$chain MessagesPalletOwnerSigningParams>] { + /// Parse signing params into chain-specific KeyPair. + pub fn to_keypair(&self) -> anyhow::Result> { + let [<$chain_prefix _messages_pallet_owner>] = match self.[<$chain_prefix _messages_pallet_owner>] { + Some(ref messages_pallet_owner) => messages_pallet_owner, + None => return Ok(None), + }; + Chain::KeyPair::from_string( + [<$chain_prefix _messages_pallet_owner>], + self.[<$chain_prefix _messages_pallet_owner_password>].as_deref() + ).map_err(|e| anyhow::format_err!("{:?}", e)).map(Some) + } + } + } + }; +} + +/// Create chain-specific set of configuration objects: connection parameters, +/// signing parameters and bridge initialization parameters. +#[macro_export] +macro_rules! declare_chain_cli_schema { + ($chain:ident, $chain_prefix:ident) => { + $crate::declare_chain_runtime_version_params_cli_schema!($chain, $chain_prefix); + $crate::declare_chain_connection_params_cli_schema!($chain, $chain_prefix); + $crate::declare_chain_signing_params_cli_schema!($chain, $chain_prefix); + $crate::declare_chain_messages_pallet_owner_signing_params_cli_schema!( + $chain, + $chain_prefix + ); + }; +} + +declare_chain_cli_schema!(Source, source); +declare_chain_cli_schema!(Target, target); +declare_chain_cli_schema!(Relaychain, relaychain); +declare_chain_cli_schema!(Parachain, parachain); + +#[cfg(test)] +mod tests { + use super::*; + use sp_core::Pair; + + #[test] + fn reads_suri_from_file() { + const ALICE: &str = "//Alice"; + const BOB: &str = "//Bob"; + const ALICE_PASSWORD: &str = "alice_password"; + const BOB_PASSWORD: &str = "bob_password"; + + let alice: sp_core::sr25519::Pair = Pair::from_string(ALICE, Some(ALICE_PASSWORD)).unwrap(); + let bob: sp_core::sr25519::Pair = Pair::from_string(BOB, Some(BOB_PASSWORD)).unwrap(); + let bob_with_alice_password = + sp_core::sr25519::Pair::from_string(BOB, Some(ALICE_PASSWORD)).unwrap(); + + let temp_dir = tempfile::tempdir().unwrap(); + let mut suri_file_path = temp_dir.path().to_path_buf(); + let mut password_file_path = temp_dir.path().to_path_buf(); + suri_file_path.push("suri"); + password_file_path.push("password"); + std::fs::write(&suri_file_path, BOB.as_bytes()).unwrap(); + std::fs::write(&password_file_path, BOB_PASSWORD.as_bytes()).unwrap(); + + // when both seed and password are read from file + assert_eq!( + TargetSigningParams { + target_signer: Some(ALICE.into()), + target_signer_password: Some(ALICE_PASSWORD.into()), + + target_signer_file: None, + target_signer_password_file: None, + + target_transactions_mortality: None, + } + .to_keypair::() + .map(|p| p.public()) + .map_err(drop), + Ok(alice.public()), + ); + + // when both seed and password are read from file + assert_eq!( + TargetSigningParams { + target_signer: None, + target_signer_password: None, + + target_signer_file: Some(suri_file_path.clone()), + target_signer_password_file: Some(password_file_path.clone()), + + target_transactions_mortality: None, + } + .to_keypair::() + .map(|p| p.public()) + .map_err(drop), + Ok(bob.public()), + ); + + // when password are is overriden by cli option + assert_eq!( + TargetSigningParams { + target_signer: None, + target_signer_password: Some(ALICE_PASSWORD.into()), + + target_signer_file: Some(suri_file_path.clone()), + target_signer_password_file: Some(password_file_path.clone()), + + target_transactions_mortality: None, + } + .to_keypair::() + .map(|p| p.public()) + .map_err(drop), + Ok(bob_with_alice_password.public()), + ); + + // when both seed and password are overriden by cli options + assert_eq!( + TargetSigningParams { + target_signer: Some(ALICE.into()), + target_signer_password: Some(ALICE_PASSWORD.into()), + + target_signer_file: Some(suri_file_path), + target_signer_password_file: Some(password_file_path), + + target_transactions_mortality: None, + } + .to_keypair::() + .map(|p| p.public()) + .map_err(drop), + Ok(alice.public()), + ); + } +} diff --git a/relays/bin-substrate/src/cli/encode_message.rs b/relays/bin-substrate/src/cli/encode_message.rs new file mode 100644 index 00000000000..c7ca5d51f79 --- /dev/null +++ b/relays/bin-substrate/src/cli/encode_message.rs @@ -0,0 +1,145 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::cli::{ExplicitOrMaximal, HexBytes}; +use bp_runtime::EncodedOrDecodedCall; +use codec::Encode; +use relay_substrate_client::Chain; +use structopt::StructOpt; + +/// All possible messages that may be delivered to generic Substrate chain. +/// +/// Note this enum may be used in the context of both Source (as part of `encode-call`) +/// and Target chain (as part of `encode-message/send-message`). +#[derive(StructOpt, Debug, PartialEq, Eq)] +pub enum Message { + /// Raw bytes for the message. + Raw { + /// Raw message bytes. + data: HexBytes, + }, + /// Message with given size. + Sized { + /// Sized of the message. + size: ExplicitOrMaximal, + }, +} + +/// Raw, SCALE-encoded message payload used in expected deployment. +pub type RawMessage = Vec; + +pub trait CliEncodeMessage: Chain { + /// Encode a send XCM call of the XCM pallet. + fn encode_send_xcm( + message: xcm::VersionedXcm<()>, + bridge_instance_index: u8, + ) -> anyhow::Result>; +} + +/// Encode message payload passed through CLI flags. +pub(crate) fn encode_message( + message: &Message, +) -> anyhow::Result { + Ok(match message { + Message::Raw { ref data } => data.0.clone(), + Message::Sized { ref size } => { + let expected_xcm_size = match *size { + ExplicitOrMaximal::Explicit(size) => size, + ExplicitOrMaximal::Maximal => compute_maximal_message_size( + Source::max_extrinsic_size(), + Target::max_extrinsic_size(), + ), + }; + + // there's no way to craft XCM of the given size - we'll be using `ExpectPallet` + // instruction, which has byte vector inside + let mut current_vec_size = expected_xcm_size; + let xcm = loop { + let xcm = xcm::VersionedXcm::<()>::V3( + vec![xcm::v3::Instruction::ExpectPallet { + index: 0, + name: vec![42; current_vec_size as usize], + module_name: vec![], + crate_major: 0, + min_crate_minor: 0, + }] + .into(), + ); + if xcm.encode().len() <= expected_xcm_size as usize { + break xcm + } + + current_vec_size -= 1; + }; + xcm.encode() + }, + }) +} + +/// Compute maximal message size, given max extrinsic size at source and target chains. +pub(crate) fn compute_maximal_message_size( + maximal_source_extrinsic_size: u32, + maximal_target_extrinsic_size: u32, +) -> u32 { + // assume that both signed extensions and other arguments fit 1KB + let service_tx_bytes_on_source_chain = 1024; + let maximal_source_extrinsic_size = + maximal_source_extrinsic_size - service_tx_bytes_on_source_chain; + let maximal_message_size = + bridge_runtime_common::messages::target::maximal_incoming_message_size( + maximal_target_extrinsic_size, + ); + if maximal_message_size > maximal_source_extrinsic_size { + maximal_source_extrinsic_size + } else { + maximal_message_size + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::send_message::decode_xcm; + use bp_runtime::Chain; + use relay_millau_client::Millau; + use relay_rialto_client::Rialto; + + #[test] + fn encode_explicit_size_message_works() { + let msg = encode_message::(&Message::Sized { + size: ExplicitOrMaximal::Explicit(100), + }) + .unwrap(); + assert_eq!(msg.len(), 100); + // check that it decodes to valid xcm + let _ = decode_xcm(msg).unwrap(); + } + + #[test] + fn encode_maximal_size_message_works() { + let maximal_size = compute_maximal_message_size( + Rialto::max_extrinsic_size(), + Millau::max_extrinsic_size(), + ); + + let msg = + encode_message::(&Message::Sized { size: ExplicitOrMaximal::Maximal }) + .unwrap(); + assert_eq!(msg.len(), maximal_size as usize); + // check that it decodes to valid xcm + let _ = decode_xcm(msg).unwrap(); + } +} diff --git a/relays/bin-substrate/src/cli/init_bridge.rs b/relays/bin-substrate/src/cli/init_bridge.rs new file mode 100644 index 00000000000..52a3a6fce5a --- /dev/null +++ b/relays/bin-substrate/src/cli/init_bridge.rs @@ -0,0 +1,219 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use async_trait::async_trait; +use codec::Encode; + +use crate::{ + chains::{ + millau_headers_to_rialto::MillauToRialtoCliBridge, + millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + rococo_headers_to_bridge_hub_wococo::RococoToBridgeHubWococoCliBridge, + westend_headers_to_millau::WestendToMillauCliBridge, + wococo_headers_to_bridge_hub_rococo::WococoToBridgeHubRococoCliBridge, + }, + cli::{bridge::CliBridgeBase, chain_schema::*}, +}; +use bp_runtime::Chain as ChainBase; +use relay_substrate_client::{AccountKeyPairOf, Chain, SignParam, UnsignedTransaction}; +use sp_core::Pair; +use structopt::StructOpt; +use strum::{EnumString, EnumVariantNames, VariantNames}; +use substrate_relay_helper::finality::engine::{Engine, Grandpa as GrandpaFinalityEngine}; + +/// Initialize bridge pallet. +#[derive(StructOpt)] +pub struct InitBridge { + /// A bridge instance to initialize. + #[structopt(possible_values = InitBridgeName::VARIANTS, case_insensitive = true)] + bridge: InitBridgeName, + #[structopt(flatten)] + source: SourceConnectionParams, + #[structopt(flatten)] + target: TargetConnectionParams, + #[structopt(flatten)] + target_sign: TargetSigningParams, + /// Generates all required data, but does not submit extrinsic + #[structopt(long)] + dry_run: bool, +} + +#[derive(Debug, EnumString, EnumVariantNames)] +#[strum(serialize_all = "kebab_case")] +/// Bridge to initialize. +pub enum InitBridgeName { + MillauToRialto, + RialtoToMillau, + WestendToMillau, + MillauToRialtoParachain, + RococoToBridgeHubWococo, + WococoToBridgeHubRococo, +} + +#[async_trait] +trait BridgeInitializer: CliBridgeBase +where + ::AccountId: From< as Pair>::Public>, +{ + type Engine: Engine; + + /// Get the encoded call to init the bridge. + fn encode_init_bridge( + init_data: >::InitializationData, + ) -> ::Call; + + /// Initialize the bridge. + async fn init_bridge(data: InitBridge) -> anyhow::Result<()> { + let source_client = data.source.into_client::().await?; + let target_client = data.target.into_client::().await?; + let target_sign = data.target_sign.to_keypair::()?; + let dry_run = data.dry_run; + + let (spec_version, transaction_version) = target_client.simple_runtime_version().await?; + substrate_relay_helper::finality::initialize::initialize::( + source_client, + target_client.clone(), + target_sign.public().into(), + SignParam { + spec_version, + transaction_version, + genesis_hash: *target_client.genesis_hash(), + signer: target_sign, + }, + move |transaction_nonce, initialization_data| { + let call = Self::encode_init_bridge(initialization_data); + log::info!( + target: "bridge", + "Initialize bridge call encoded as hex string: {:?}", + format!("0x{}", hex::encode(call.encode())) + ); + Ok(UnsignedTransaction::new(call.into(), transaction_nonce)) + }, + dry_run, + ) + .await; + + Ok(()) + } +} + +impl BridgeInitializer for MillauToRialtoCliBridge { + type Engine = GrandpaFinalityEngine; + + fn encode_init_bridge( + init_data: >::InitializationData, + ) -> ::Call { + rialto_runtime::SudoCall::sudo { + call: Box::new(rialto_runtime::BridgeGrandpaCall::initialize { init_data }.into()), + } + .into() + } +} + +impl BridgeInitializer for MillauToRialtoParachainCliBridge { + type Engine = GrandpaFinalityEngine; + + fn encode_init_bridge( + init_data: >::InitializationData, + ) -> ::Call { + let initialize_call = rialto_parachain_runtime::BridgeGrandpaCall::< + rialto_parachain_runtime::Runtime, + rialto_parachain_runtime::MillauGrandpaInstance, + >::initialize { + init_data, + }; + rialto_parachain_runtime::SudoCall::sudo { call: Box::new(initialize_call.into()) }.into() + } +} + +impl BridgeInitializer for RialtoToMillauCliBridge { + type Engine = GrandpaFinalityEngine; + + fn encode_init_bridge( + init_data: >::InitializationData, + ) -> ::Call { + let initialize_call = millau_runtime::BridgeGrandpaCall::< + millau_runtime::Runtime, + millau_runtime::RialtoGrandpaInstance, + >::initialize { + init_data, + }; + millau_runtime::SudoCall::sudo { call: Box::new(initialize_call.into()) }.into() + } +} + +impl BridgeInitializer for WestendToMillauCliBridge { + type Engine = GrandpaFinalityEngine; + + fn encode_init_bridge( + init_data: >::InitializationData, + ) -> ::Call { + // at Westend -> Millau initialization we're not using sudo, because otherwise + // our deployments may fail, because we need to initialize both Rialto -> Millau + // and Westend -> Millau bridge. => since there's single possible sudo account, + // one of transaction may fail with duplicate nonce error + millau_runtime::BridgeGrandpaCall::< + millau_runtime::Runtime, + millau_runtime::WestendGrandpaInstance, + >::initialize { + init_data, + } + .into() + } +} + +impl BridgeInitializer for RococoToBridgeHubWococoCliBridge { + type Engine = GrandpaFinalityEngine; + + fn encode_init_bridge( + init_data: >::InitializationData, + ) -> ::Call { + relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoGrandpa( + relay_bridge_hub_wococo_client::runtime::BridgeGrandpaRococoCall::initialize(init_data), + ) + } +} + +impl BridgeInitializer for WococoToBridgeHubRococoCliBridge { + type Engine = GrandpaFinalityEngine; + + fn encode_init_bridge( + init_data: >::InitializationData, + ) -> ::Call { + relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoGrandpa( + relay_bridge_hub_rococo_client::runtime::BridgeWococoGrandpaCall::initialize(init_data), + ) + } +} + +impl InitBridge { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + match self.bridge { + InitBridgeName::MillauToRialto => MillauToRialtoCliBridge::init_bridge(self), + InitBridgeName::RialtoToMillau => RialtoToMillauCliBridge::init_bridge(self), + InitBridgeName::WestendToMillau => WestendToMillauCliBridge::init_bridge(self), + InitBridgeName::MillauToRialtoParachain => + MillauToRialtoParachainCliBridge::init_bridge(self), + InitBridgeName::RococoToBridgeHubWococo => + RococoToBridgeHubWococoCliBridge::init_bridge(self), + InitBridgeName::WococoToBridgeHubRococo => + WococoToBridgeHubRococoCliBridge::init_bridge(self), + } + .await + } +} diff --git a/relays/bin-substrate/src/cli/mod.rs b/relays/bin-substrate/src/cli/mod.rs new file mode 100644 index 00000000000..2086008bc95 --- /dev/null +++ b/relays/bin-substrate/src/cli/mod.rs @@ -0,0 +1,300 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Deal with CLI args of substrate-to-substrate relay. + +use std::convert::TryInto; + +use codec::{Decode, Encode}; +use structopt::{clap::arg_enum, StructOpt}; +use strum::{EnumString, EnumVariantNames}; + +use bp_messages::LaneId; + +pub(crate) mod bridge; +pub(crate) mod encode_message; +pub(crate) mod send_message; + +mod chain_schema; +mod init_bridge; +mod register_parachain; +mod relay_headers; +mod relay_headers_and_messages; +mod relay_messages; +mod relay_parachains; +mod resubmit_transactions; + +/// Parse relay CLI args. +pub fn parse_args() -> Command { + Command::from_args() +} + +/// Substrate-to-Substrate bridge utilities. +#[derive(StructOpt)] +#[structopt(about = "Substrate-to-Substrate relay")] +pub enum Command { + /// Start headers relay between two chains. + /// + /// The on-chain bridge component should have been already initialized with + /// `init-bridge` sub-command. + RelayHeaders(relay_headers::RelayHeaders), + /// Start messages relay between two chains. + /// + /// Ties up to `Messages` pallets on both chains and starts relaying messages. + /// Requires the header relay to be already running. + RelayMessages(relay_messages::RelayMessages), + /// Start headers and messages relay between two Substrate chains. + /// + /// This high-level relay internally starts four low-level relays: two `RelayHeaders` + /// and two `RelayMessages` relays. Headers are only relayed when they are required by + /// the message relays - i.e. when there are messages or confirmations that needs to be + /// relayed between chains. + RelayHeadersAndMessages(Box), + /// Initialize on-chain bridge pallet with current header data. + /// + /// Sends initialization transaction to bootstrap the bridge with current finalized block data. + InitBridge(init_bridge::InitBridge), + /// Send custom message over the bridge. + /// + /// Allows interacting with the bridge by sending messages over `Messages` component. + /// The message is being sent to the source chain, delivered to the target chain and dispatched + /// there. + SendMessage(send_message::SendMessage), + /// Resubmit transactions with increased tip if they are stalled. + ResubmitTransactions(resubmit_transactions::ResubmitTransactions), + /// Register parachain. + RegisterParachain(register_parachain::RegisterParachain), + /// + RelayParachains(relay_parachains::RelayParachains), +} + +impl Command { + // Initialize logger depending on the command. + fn init_logger(&self) { + use relay_utils::initialize::{initialize_logger, initialize_relay}; + + match self { + Self::RelayHeaders(_) | + Self::RelayMessages(_) | + Self::RelayHeadersAndMessages(_) | + Self::InitBridge(_) => { + initialize_relay(); + }, + _ => { + initialize_logger(false); + }, + } + } + + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + self.init_logger(); + match self { + Self::RelayHeaders(arg) => arg.run().await?, + Self::RelayMessages(arg) => arg.run().await?, + Self::RelayHeadersAndMessages(arg) => arg.run().await?, + Self::InitBridge(arg) => arg.run().await?, + Self::SendMessage(arg) => arg.run().await?, + Self::ResubmitTransactions(arg) => arg.run().await?, + Self::RegisterParachain(arg) => arg.run().await?, + Self::RelayParachains(arg) => arg.run().await?, + } + Ok(()) + } +} + +arg_enum! { + #[derive(Debug)] + /// The origin to use when dispatching the message on the target chain. + /// + /// - `Target` uses account existing on the target chain (requires target private key). + /// - `Origin` uses account derived from the source-chain account. + pub enum Origins { + Target, + Source, + } +} + +/// Generic balance type. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Balance(pub u128); + +impl std::fmt::Display for Balance { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + use num_format::{Locale, ToFormattedString}; + write!(fmt, "{}", self.0.to_formatted_string(&Locale::en)) + } +} + +impl std::str::FromStr for Balance { + type Err = ::Err; + + fn from_str(s: &str) -> Result { + Ok(Self(s.parse()?)) + } +} + +impl Balance { + /// Cast balance to `u64` type, panicking if it's too large. + pub fn cast(&self) -> u64 { + self.0.try_into().expect("Balance is too high for this chain.") + } +} + +// Bridge-supported network definition. +/// +/// Used to abstract away CLI commands. +pub trait CliChain: relay_substrate_client::Chain { + /// Current version of the chain runtime, known to relay. + /// + /// can be `None` if relay is not going to submit transactions to that chain. + const RUNTIME_VERSION: Option; + + /// Crypto KeyPair type used to send messages. + /// + /// In case of chains supporting multiple cryptos, pick one used by the CLI. + type KeyPair: sp_core::crypto::Pair; + + /// Numeric value of SS58 format. + fn ss58_format() -> u16; +} + +/// Lane id. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct HexLaneId(pub LaneId); + +impl From for LaneId { + fn from(lane_id: HexLaneId) -> LaneId { + lane_id.0 + } +} + +impl std::str::FromStr for HexLaneId { + type Err = hex::FromHexError; + + fn from_str(s: &str) -> Result { + let mut lane_id = LaneId::default(); + hex::decode_to_slice(s, &mut lane_id)?; + Ok(HexLaneId(lane_id)) + } +} + +/// Nicer formatting for raw bytes vectors. +#[derive(Default, Encode, Decode, PartialEq, Eq)] +pub struct HexBytes(pub Vec); + +impl std::str::FromStr for HexBytes { + type Err = hex::FromHexError; + + fn from_str(s: &str) -> Result { + Ok(Self(hex::decode(s)?)) + } +} + +impl std::fmt::Debug for HexBytes { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "0x{self}") + } +} + +impl std::fmt::Display for HexBytes { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "{}", hex::encode(&self.0)) + } +} + +/// Prometheus metrics params. +#[derive(Clone, Debug, PartialEq, StructOpt)] +pub struct PrometheusParams { + /// Do not expose a Prometheus metric endpoint. + #[structopt(long)] + pub no_prometheus: bool, + /// Expose Prometheus endpoint at given interface. + #[structopt(long, default_value = "127.0.0.1")] + pub prometheus_host: String, + /// Expose Prometheus endpoint at given port. + #[structopt(long, default_value = "9616")] + pub prometheus_port: u16, +} + +impl From for relay_utils::metrics::MetricsParams { + fn from(cli_params: PrometheusParams) -> relay_utils::metrics::MetricsParams { + if !cli_params.no_prometheus { + Some(relay_utils::metrics::MetricsAddress { + host: cli_params.prometheus_host, + port: cli_params.prometheus_port, + }) + .into() + } else { + None.into() + } + } +} + +/// Either explicit or maximal allowed value. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ExplicitOrMaximal { + /// User has explicitly specified argument value. + Explicit(V), + /// Maximal allowed value for this argument. + Maximal, +} + +impl std::str::FromStr for ExplicitOrMaximal +where + V::Err: std::fmt::Debug, +{ + type Err = String; + + fn from_str(s: &str) -> Result { + if s.to_lowercase() == "max" { + return Ok(ExplicitOrMaximal::Maximal) + } + + V::from_str(s) + .map(ExplicitOrMaximal::Explicit) + .map_err(|e| format!("Failed to parse '{e:?}'. Expected 'max' or explicit value")) + } +} + +#[doc = "Runtime version params."] +#[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy, EnumString, EnumVariantNames)] +pub enum RuntimeVersionType { + /// Auto query version from chain + Auto, + /// Custom `spec_version` and `transaction_version` + Custom, + /// Read version from bundle dependencies directly. + Bundle, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn hex_bytes_display_matches_from_str_for_clap() { + // given + let hex = HexBytes(vec![1, 2, 3, 4]); + let display = format!("{hex}"); + + // when + let hex2: HexBytes = display.parse().unwrap(); + + // then + assert_eq!(hex.0, hex2.0); + } +} diff --git a/relays/bin-substrate/src/cli/register_parachain.rs b/relays/bin-substrate/src/cli/register_parachain.rs new file mode 100644 index 00000000000..4febc4b04a8 --- /dev/null +++ b/relays/bin-substrate/src/cli/register_parachain.rs @@ -0,0 +1,359 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::cli::{chain_schema::*, Balance}; + +use codec::Encode; +use frame_support::Twox64Concat; +use num_traits::Zero; +use polkadot_parachain::primitives::{ + HeadData as ParaHeadData, Id as ParaId, ValidationCode as ParaValidationCode, +}; +use polkadot_runtime_common::{ + paras_registrar::Call as ParaRegistrarCall, slots::Call as ParaSlotsCall, +}; +use polkadot_runtime_parachains::paras::ParaLifecycle; +use relay_substrate_client::{AccountIdOf, CallOf, Chain, Client, SignParam, UnsignedTransaction}; +use relay_utils::{TrackedTransactionStatus, TransactionTracker}; +use rialto_runtime::SudoCall; +use sp_core::{ + storage::{well_known_keys::CODE, StorageKey}, + Pair, +}; +use structopt::StructOpt; +use strum::{EnumString, EnumVariantNames, VariantNames}; + +/// Name of the `NextFreeParaId` value in the `polkadot_runtime_common::paras_registrar` pallet. +const NEXT_FREE_PARA_ID_STORAGE_NAME: &str = "NextFreeParaId"; +/// Name of the `ParaLifecycles` map in the `polkadot_runtime_parachains::paras` pallet. +const PARAS_LIFECYCLES_STORAGE_NAME: &str = "ParaLifecycles"; + +/// Register parachain. +#[derive(StructOpt, Debug, PartialEq, Eq)] +pub struct RegisterParachain { + /// A parachain to register. + #[structopt(possible_values = Parachain::VARIANTS, case_insensitive = true)] + parachain: Parachain, + /// Parachain deposit. + #[structopt(long, default_value = "0")] + deposit: Balance, + /// Lease begin. + #[structopt(long, default_value = "0")] + lease_begin: u32, + /// Lease end. + #[structopt(long, default_value = "256")] + lease_end: u32, + #[structopt(flatten)] + relay_connection: RelaychainConnectionParams, + #[structopt(flatten)] + relay_sign: RelaychainSigningParams, + #[structopt(flatten)] + para_connection: ParachainConnectionParams, +} + +/// Parachain to register. +#[derive(Debug, EnumString, EnumVariantNames, PartialEq, Eq)] +#[strum(serialize_all = "kebab_case")] +pub enum Parachain { + RialtoParachain, +} + +macro_rules! select_bridge { + ($bridge: expr, $generic: tt) => { + match $bridge { + Parachain::RialtoParachain => { + type Relaychain = relay_rialto_client::Rialto; + type Parachain = relay_rialto_parachain_client::RialtoParachain; + + use bp_rialto::{PARAS_PALLET_NAME, PARAS_REGISTRAR_PALLET_NAME}; + + $generic + }, + } + }; +} + +impl RegisterParachain { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + select_bridge!(self.parachain, { + let relay_client = self.relay_connection.into_client::().await?; + let relay_sign = self.relay_sign.to_keypair::()?; + let para_client = self.para_connection.into_client::().await?; + + // hopefully we're the only actor that is registering parachain right now + // => read next parachain id + let para_id_key = bp_runtime::storage_value_final_key( + PARAS_REGISTRAR_PALLET_NAME.as_bytes(), + NEXT_FREE_PARA_ID_STORAGE_NAME.as_bytes(), + ); + let para_id: ParaId = relay_client + .storage_value(StorageKey(para_id_key.to_vec()), None) + .await? + .unwrap_or(polkadot_primitives::v2::LOWEST_PUBLIC_ID) + .max(polkadot_primitives::v2::LOWEST_PUBLIC_ID); + log::info!(target: "bridge", "Going to reserve parachain id: {:?}", para_id); + + // step 1: reserve a parachain id + let relay_genesis_hash = *relay_client.genesis_hash(); + let relay_sudo_account: AccountIdOf = relay_sign.public().into(); + let reserve_parachain_id_call: CallOf = + ParaRegistrarCall::reserve {}.into(); + let reserve_parachain_signer = relay_sign.clone(); + let (spec_version, transaction_version) = relay_client.simple_runtime_version().await?; + let reserve_result = relay_client + .submit_and_watch_signed_extrinsic( + relay_sudo_account.clone(), + SignParam:: { + spec_version, + transaction_version, + genesis_hash: relay_genesis_hash, + signer: reserve_parachain_signer, + }, + move |_, transaction_nonce| { + Ok(UnsignedTransaction::new( + reserve_parachain_id_call.into(), + transaction_nonce, + )) + }, + ) + .await? + .wait() + .await; + if reserve_result == TrackedTransactionStatus::Lost { + return Err(anyhow::format_err!( + "Failed to finalize `reserve-parachain-id` transaction" + )) + } + log::info!(target: "bridge", "Reserved parachain id: {:?}", para_id); + + // step 2: register parathread + let para_genesis_header = para_client.header_by_number(Zero::zero()).await?; + let para_code = para_client + .raw_storage_value(StorageKey(CODE.to_vec()), Some(para_genesis_header.hash())) + .await? + .ok_or_else(|| { + anyhow::format_err!("Cannot fetch validation code of {}", Parachain::NAME) + })? + .0; + log::info!( + target: "bridge", + "Going to register parachain {:?}: genesis len = {} code len = {}", + para_id, + para_genesis_header.encode().len(), + para_code.len(), + ); + let register_parathread_call: CallOf = ParaRegistrarCall::register { + id: para_id, + genesis_head: ParaHeadData(para_genesis_header.encode()), + validation_code: ParaValidationCode(para_code), + } + .into(); + let register_parathread_signer = relay_sign.clone(); + let register_result = relay_client + .submit_and_watch_signed_extrinsic( + relay_sudo_account.clone(), + SignParam:: { + spec_version, + transaction_version, + genesis_hash: relay_genesis_hash, + signer: register_parathread_signer, + }, + move |_, transaction_nonce| { + Ok(UnsignedTransaction::new( + register_parathread_call.into(), + transaction_nonce, + )) + }, + ) + .await? + .wait() + .await; + if register_result == TrackedTransactionStatus::Lost { + return Err(anyhow::format_err!( + "Failed to finalize `register-parathread` transaction" + )) + } + log::info!(target: "bridge", "Registered parachain: {:?}. Waiting for onboarding", para_id); + + // wait until parathread is onboarded + let para_state_key = bp_runtime::storage_map_final_key::( + PARAS_PALLET_NAME, + PARAS_LIFECYCLES_STORAGE_NAME, + ¶_id.encode(), + ); + wait_para_state( + &relay_client, + ¶_state_key.0, + &[ParaLifecycle::Onboarding, ParaLifecycle::Parathread], + ParaLifecycle::Parathread, + ) + .await?; + + // step 3: force parachain leases + let lease_begin = self.lease_begin; + let lease_end = self.lease_end; + let para_deposit = self.deposit.cast().into(); + log::info!( + target: "bridge", + "Going to force leases of parachain {:?}: [{}; {}]", + para_id, + lease_begin, + lease_end, + ); + let force_lease_call: CallOf = SudoCall::sudo { + call: Box::new( + ParaSlotsCall::force_lease { + para: para_id, + leaser: relay_sudo_account.clone(), + amount: para_deposit, + period_begin: lease_begin, + period_count: lease_end.saturating_sub(lease_begin).saturating_add(1), + } + .into(), + ), + } + .into(); + let force_lease_signer = relay_sign.clone(); + relay_client + .submit_signed_extrinsic( + relay_sudo_account, + SignParam:: { + spec_version, + transaction_version, + genesis_hash: relay_genesis_hash, + signer: force_lease_signer, + }, + move |_, transaction_nonce| { + Ok(UnsignedTransaction::new(force_lease_call.into(), transaction_nonce)) + }, + ) + .await?; + log::info!(target: "bridge", "Registered parachain leases: {:?}. Waiting for onboarding", para_id); + + // wait until parachain is onboarded + wait_para_state( + &relay_client, + ¶_state_key.0, + &[ + ParaLifecycle::Onboarding, + ParaLifecycle::UpgradingParathread, + ParaLifecycle::Parathread, + ], + ParaLifecycle::Parachain, + ) + .await?; + + Ok(()) + }) + } +} + +/// Wait until parachain state is changed. +async fn wait_para_state( + relay_client: &Client, + para_state_key: &[u8], + from_states: &[ParaLifecycle], + to_state: ParaLifecycle, +) -> anyhow::Result<()> { + loop { + let para_state: ParaLifecycle = relay_client + .storage_value(StorageKey(para_state_key.to_vec()), None) + .await? + .ok_or_else(|| { + anyhow::format_err!( + "Cannot fetch next free parachain lifecycle from the runtime storage of {}", + Relaychain::NAME, + ) + })?; + if para_state == to_state { + log::info!(target: "bridge", "Parachain state is now: {:?}", to_state); + return Ok(()) + } + if !from_states.contains(¶_state) { + return Err(anyhow::format_err!("Invalid parachain lifecycle: {:?}", para_state)) + } + + log::info!(target: "bridge", "Parachain state: {:?}. Waiting for {:?}", para_state, to_state); + async_std::task::sleep(Relaychain::AVERAGE_BLOCK_INTERVAL).await; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn register_rialto_parachain() { + let register_parachain = RegisterParachain::from_iter(vec![ + "register-parachain", + "rialto-parachain", + "--parachain-host", + "127.0.0.1", + "--parachain-port", + "11949", + "--relaychain-host", + "127.0.0.1", + "--relaychain-port", + "9944", + "--relaychain-signer", + "//Alice", + "--deposit", + "42", + "--lease-begin", + "100", + "--lease-end", + "200", + ]); + + assert_eq!( + register_parachain, + RegisterParachain { + parachain: Parachain::RialtoParachain, + deposit: Balance(42), + lease_begin: 100, + lease_end: 200, + relay_connection: RelaychainConnectionParams { + relaychain_host: "127.0.0.1".into(), + relaychain_port: 9944, + relaychain_secure: false, + relaychain_runtime_version: RelaychainRuntimeVersionParams { + relaychain_version_mode: RuntimeVersionType::Bundle, + relaychain_spec_version: None, + relaychain_transaction_version: None, + } + }, + relay_sign: RelaychainSigningParams { + relaychain_signer: Some("//Alice".into()), + relaychain_signer_password: None, + relaychain_signer_file: None, + relaychain_signer_password_file: None, + relaychain_transactions_mortality: None, + }, + para_connection: ParachainConnectionParams { + parachain_host: "127.0.0.1".into(), + parachain_port: 11949, + parachain_secure: false, + parachain_runtime_version: ParachainRuntimeVersionParams { + parachain_version_mode: RuntimeVersionType::Bundle, + parachain_spec_version: None, + parachain_transaction_version: None, + } + }, + } + ); + } +} diff --git a/relays/bin-substrate/src/cli/relay_headers.rs b/relays/bin-substrate/src/cli/relay_headers.rs new file mode 100644 index 00000000000..37b805d720b --- /dev/null +++ b/relays/bin-substrate/src/cli/relay_headers.rs @@ -0,0 +1,128 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use async_trait::async_trait; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf}; +use sp_core::Pair; +use structopt::StructOpt; +use strum::{EnumString, EnumVariantNames, VariantNames}; + +use crate::chains::{ + millau_headers_to_rialto::MillauToRialtoCliBridge, + millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + rococo_headers_to_bridge_hub_wococo::RococoToBridgeHubWococoCliBridge, + westend_headers_to_millau::WestendToMillauCliBridge, + wococo_headers_to_bridge_hub_rococo::WococoToBridgeHubRococoCliBridge, +}; +use relay_utils::metrics::{GlobalMetrics, StandaloneMetric}; +use substrate_relay_helper::finality::SubstrateFinalitySyncPipeline; + +use crate::cli::{bridge::*, chain_schema::*, PrometheusParams}; + +/// Start headers relayer process. +#[derive(StructOpt)] +pub struct RelayHeaders { + /// A bridge instance to relay headers for. + #[structopt(possible_values = RelayHeadersBridge::VARIANTS, case_insensitive = true)] + bridge: RelayHeadersBridge, + /// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) + /// are relayed. + #[structopt(long)] + only_mandatory_headers: bool, + #[structopt(flatten)] + source: SourceConnectionParams, + #[structopt(flatten)] + target: TargetConnectionParams, + #[structopt(flatten)] + target_sign: TargetSigningParams, + #[structopt(flatten)] + prometheus_params: PrometheusParams, +} + +#[derive(Debug, EnumString, EnumVariantNames)] +#[strum(serialize_all = "kebab_case")] +/// Headers relay bridge. +pub enum RelayHeadersBridge { + MillauToRialto, + RialtoToMillau, + WestendToMillau, + MillauToRialtoParachain, + RococoToBridgeHubWococo, + WococoToBridgeHubRococo, +} + +#[async_trait] +trait HeadersRelayer: RelayToRelayHeadersCliBridge +where + AccountIdOf: From< as Pair>::Public>, +{ + /// Relay headers. + async fn relay_headers(data: RelayHeaders) -> anyhow::Result<()> { + let source_client = data.source.into_client::().await?; + let target_client = data.target.into_client::().await?; + let target_transactions_mortality = data.target_sign.target_transactions_mortality; + let target_sign = data.target_sign.to_keypair::()?; + + let metrics_params: relay_utils::metrics::MetricsParams = data.prometheus_params.into(); + GlobalMetrics::new()?.register_and_spawn(&metrics_params.registry)?; + + let target_transactions_params = substrate_relay_helper::TransactionParams { + signer: target_sign, + mortality: target_transactions_mortality, + }; + Self::Finality::start_relay_guards( + &target_client, + &target_transactions_params, + target_client.can_start_version_guard(), + ) + .await?; + + substrate_relay_helper::finality::run::( + source_client, + target_client, + data.only_mandatory_headers, + target_transactions_params, + metrics_params, + ) + .await + } +} + +impl HeadersRelayer for MillauToRialtoCliBridge {} +impl HeadersRelayer for RialtoToMillauCliBridge {} +impl HeadersRelayer for WestendToMillauCliBridge {} +impl HeadersRelayer for MillauToRialtoParachainCliBridge {} +impl HeadersRelayer for RococoToBridgeHubWococoCliBridge {} +impl HeadersRelayer for WococoToBridgeHubRococoCliBridge {} + +impl RelayHeaders { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + match self.bridge { + RelayHeadersBridge::MillauToRialto => MillauToRialtoCliBridge::relay_headers(self), + RelayHeadersBridge::RialtoToMillau => RialtoToMillauCliBridge::relay_headers(self), + RelayHeadersBridge::WestendToMillau => WestendToMillauCliBridge::relay_headers(self), + RelayHeadersBridge::MillauToRialtoParachain => + MillauToRialtoParachainCliBridge::relay_headers(self), + RelayHeadersBridge::RococoToBridgeHubWococo => + RococoToBridgeHubWococoCliBridge::relay_headers(self), + RelayHeadersBridge::WococoToBridgeHubRococo => + WococoToBridgeHubRococoCliBridge::relay_headers(self), + } + .await + } +} diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs new file mode 100644 index 00000000000..f84174563e1 --- /dev/null +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs @@ -0,0 +1,732 @@ +// Copyright 2019-2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Complex 2-ways headers+messages relays support. +//! +//! To add new complex relay between `ChainA` and `ChainB`, you must: +//! +//! 1) ensure that there's a `declare_chain_cli_schema!(...)` for both chains. +//! 2) add `declare_chain_to_chain_bridge_schema!(...)` or +//! `declare_chain_to_parachain_bridge_schema` for the bridge. +//! 3) declare a new struct for the added bridge and implement the `Full2WayBridge` trait for it. + +#[macro_use] +mod parachain_to_parachain; +#[macro_use] +mod relay_to_relay; +#[macro_use] +mod relay_to_parachain; + +use async_trait::async_trait; +use std::{marker::PhantomData, sync::Arc}; +use structopt::StructOpt; + +use futures::{FutureExt, TryFutureExt}; +use relay_to_parachain::*; +use relay_to_relay::*; + +use crate::{ + chains::{ + millau_headers_to_rialto::MillauToRialtoCliBridge, + millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, + rococo_parachains_to_bridge_hub_wococo::BridgeHubRococoToBridgeHubWococoCliBridge, + wococo_parachains_to_bridge_hub_rococo::BridgeHubWococoToBridgeHubRococoCliBridge, + }, + cli::{ + bridge::{ + CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge, + RelayToRelayHeadersCliBridge, + }, + chain_schema::*, + relay_headers_and_messages::parachain_to_parachain::ParachainToParachainBridge, + CliChain, HexLaneId, PrometheusParams, + }, + declare_chain_cli_schema, +}; +use bp_messages::LaneId; +use bp_runtime::{BalanceOf, BlockNumberOf}; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, Chain, ChainWithBalances, ChainWithTransactions, Client, +}; +use relay_utils::metrics::MetricsParams; +use sp_core::Pair; +use substrate_relay_helper::{ + messages_lane::MessagesRelayParams, on_demand::OnDemandRelay, TaggedAccount, TransactionParams, +}; + +/// Parameters that have the same names across all bridges. +#[derive(Debug, PartialEq, StructOpt)] +pub struct HeadersAndMessagesSharedParams { + /// Hex-encoded lane identifiers that should be served by the complex relay. + #[structopt(long, default_value = "00000000")] + pub lane: Vec, + /// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) + /// are relayed. + #[structopt(long)] + pub only_mandatory_headers: bool, + #[structopt(flatten)] + pub prometheus_params: PrometheusParams, +} + +/// Bridge parameters, shared by all bridge types. +pub struct Full2WayBridgeCommonParams< + Left: ChainWithTransactions + CliChain, + Right: ChainWithTransactions + CliChain, +> { + /// Shared parameters. + pub shared: HeadersAndMessagesSharedParams, + /// Parameters of the left chain. + pub left: BridgeEndCommonParams, + /// Parameters of the right chain. + pub right: BridgeEndCommonParams, + + /// Common metric parameters. + pub metrics_params: MetricsParams, +} + +impl + Full2WayBridgeCommonParams +{ + /// Creates new bridge parameters from its components. + pub fn new>( + shared: HeadersAndMessagesSharedParams, + left: BridgeEndCommonParams, + right: BridgeEndCommonParams, + ) -> anyhow::Result { + // Create metrics registry. + let metrics_params = shared.prometheus_params.clone().into(); + let metrics_params = relay_utils::relay_metrics(metrics_params).into_params(); + + Ok(Self { shared, left, right, metrics_params }) + } +} + +/// Parameters that are associated with one side of the bridge. +pub struct BridgeEndCommonParams { + /// Chain client. + pub client: Client, + /// Transactions signer. + pub sign: AccountKeyPairOf, + /// Transactions mortality. + pub transactions_mortality: Option, + /// Account that "owns" messages pallet. + pub messages_pallet_owner: Option>, + /// Accounts, which balances are exposed as metrics by the relay process. + pub accounts: Vec>>, +} + +/// All data of the bidirectional complex relay. +struct FullBridge< + 'a, + Source: ChainWithTransactions + CliChain, + Target: ChainWithTransactions + CliChain, + Bridge: MessagesCliBridge, +> { + source: &'a mut BridgeEndCommonParams, + target: &'a mut BridgeEndCommonParams, + metrics_params: &'a MetricsParams, + _phantom_data: PhantomData, +} + +impl< + 'a, + Source: ChainWithTransactions + CliChain, + Target: ChainWithTransactions + CliChain, + Bridge: MessagesCliBridge, + > FullBridge<'a, Source, Target, Bridge> +where + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, + BalanceOf: TryFrom> + Into, +{ + /// Construct complex relay given it components. + fn new( + source: &'a mut BridgeEndCommonParams, + target: &'a mut BridgeEndCommonParams, + metrics_params: &'a MetricsParams, + ) -> Self { + Self { source, target, metrics_params, _phantom_data: Default::default() } + } + + /// Returns message relay parameters. + fn messages_relay_params( + &self, + source_to_target_headers_relay: Arc>>, + target_to_source_headers_relay: Arc>>, + lane_id: LaneId, + ) -> MessagesRelayParams { + MessagesRelayParams { + source_client: self.source.client.clone(), + source_transaction_params: TransactionParams { + signer: self.source.sign.clone(), + mortality: self.source.transactions_mortality, + }, + target_client: self.target.client.clone(), + target_transaction_params: TransactionParams { + signer: self.target.sign.clone(), + mortality: self.target.transactions_mortality, + }, + source_to_target_headers_relay: Some(source_to_target_headers_relay), + target_to_source_headers_relay: Some(target_to_source_headers_relay), + lane_id, + metrics_params: self.metrics_params.clone().disable(), + } + } +} + +// All supported chains. +declare_chain_cli_schema!(Millau, millau); +declare_chain_cli_schema!(Rialto, rialto); +declare_chain_cli_schema!(RialtoParachain, rialto_parachain); +declare_chain_cli_schema!(Rococo, rococo); +declare_chain_cli_schema!(BridgeHubRococo, bridge_hub_rococo); +declare_chain_cli_schema!(Wococo, wococo); +declare_chain_cli_schema!(BridgeHubWococo, bridge_hub_wococo); +// Means to override signers of different layer transactions. +declare_chain_cli_schema!(MillauHeadersToRialto, millau_headers_to_rialto); +declare_chain_cli_schema!(MillauHeadersToRialtoParachain, millau_headers_to_rialto_parachain); +declare_chain_cli_schema!(RialtoHeadersToMillau, rialto_headers_to_millau); +declare_chain_cli_schema!(RialtoParachainsToMillau, rialto_parachains_to_millau); +declare_chain_cli_schema!(RococoHeadersToBridgeHubWococo, rococo_headers_to_bridge_hub_wococo); +declare_chain_cli_schema!( + RococoParachainsToBridgeHubWococo, + rococo_parachains_to_bridge_hub_wococo +); +declare_chain_cli_schema!(WococoHeadersToBridgeHubRococo, wococo_headers_to_bridge_hub_rococo); +declare_chain_cli_schema!( + WococoParachainsToBridgeHubRococo, + wococo_parachains_to_bridge_hub_rococo +); +// All supported bridges. +declare_relay_to_relay_bridge_schema!(Millau, Rialto); +declare_relay_to_parachain_bridge_schema!(Millau, RialtoParachain, Rialto); +declare_parachain_to_parachain_bridge_schema!(BridgeHubRococo, Rococo, BridgeHubWococo, Wococo); + +/// Base portion of the bidirectional complex relay. +/// +/// This main purpose of extracting this trait is that in different relays the implementation +/// of `start_on_demand_headers_relayers` method will be different. But the number of +/// implementations is limited to relay <> relay, parachain <> relay and parachain <> parachain. +/// This trait allows us to reuse these implementations in different bridges. +#[async_trait] +trait Full2WayBridgeBase: Sized + Send + Sync { + /// The CLI params for the bridge. + type Params; + /// The left relay chain. + type Left: ChainWithTransactions + CliChain>; + /// The right destination chain (it can be a relay or a parachain). + type Right: ChainWithTransactions + CliChain>; + + /// Reference to common relay parameters. + fn common(&self) -> &Full2WayBridgeCommonParams; + + /// Mutable reference to common relay parameters. + fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams; + + /// Start on-demand headers relays. + async fn start_on_demand_headers_relayers( + &mut self, + ) -> anyhow::Result<( + Arc>>, + Arc>>, + )>; +} + +/// Bidirectional complex relay. +#[async_trait] +trait Full2WayBridge: Sized + Sync +where + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, + BalanceOf: TryFrom> + Into, + BalanceOf: TryFrom> + Into, +{ + /// Base portion of the bidirectional complex relay. + type Base: Full2WayBridgeBase; + + /// The left relay chain. + type Left: ChainWithTransactions + + ChainWithBalances + + CliChain>; + /// The right relay chain. + type Right: ChainWithTransactions + + ChainWithBalances + + CliChain>; + + /// Left to Right bridge. + type L2R: MessagesCliBridge; + /// Right to Left bridge + type R2L: MessagesCliBridge; + + /// Construct new bridge. + fn new(params: ::Params) -> anyhow::Result; + + /// Reference to the base relay portion. + fn base(&self) -> &Self::Base; + + /// Mutable reference to the base relay portion. + fn mut_base(&mut self) -> &mut Self::Base; + + /// Creates and returns Left to Right complex relay. + fn left_to_right(&mut self) -> FullBridge { + let common = self.mut_base().mut_common(); + FullBridge::<_, _, Self::L2R>::new( + &mut common.left, + &mut common.right, + &common.metrics_params, + ) + } + + /// Creates and returns Right to Left complex relay. + fn right_to_left(&mut self) -> FullBridge { + let common = self.mut_base().mut_common(); + FullBridge::<_, _, Self::R2L>::new( + &mut common.right, + &mut common.left, + &common.metrics_params, + ) + } + + /// Start complex relay. + async fn run(&mut self) -> anyhow::Result<()> { + // Register standalone metrics. + { + let common = self.mut_base().mut_common(); + common.left.accounts.push(TaggedAccount::Messages { + id: common.left.sign.public().into(), + bridged_chain: Self::Right::NAME.to_string(), + }); + common.right.accounts.push(TaggedAccount::Messages { + id: common.right.sign.public().into(), + bridged_chain: Self::Left::NAME.to_string(), + }); + } + + // start on-demand header relays + let (left_to_right_on_demand_headers, right_to_left_on_demand_headers) = + self.mut_base().start_on_demand_headers_relayers().await?; + + // add balance-related metrics + { + let common = self.mut_base().mut_common(); + substrate_relay_helper::messages_metrics::add_relay_balances_metrics( + common.left.client.clone(), + &mut common.metrics_params, + &common.left.accounts, + ) + .await?; + substrate_relay_helper::messages_metrics::add_relay_balances_metrics( + common.right.client.clone(), + &mut common.metrics_params, + &common.right.accounts, + ) + .await?; + } + + let lanes = self.base().common().shared.lane.clone(); + // Need 2x capacity since we consider both directions for each lane + let mut message_relays = Vec::with_capacity(lanes.len() * 2); + for lane in lanes { + let lane = lane.into(); + + let left_to_right_messages = substrate_relay_helper::messages_lane::run::< + ::MessagesLane, + >(self.left_to_right().messages_relay_params( + left_to_right_on_demand_headers.clone(), + right_to_left_on_demand_headers.clone(), + lane, + )) + .map_err(|e| anyhow::format_err!("{}", e)) + .boxed(); + message_relays.push(left_to_right_messages); + + let right_to_left_messages = substrate_relay_helper::messages_lane::run::< + ::MessagesLane, + >(self.right_to_left().messages_relay_params( + right_to_left_on_demand_headers.clone(), + left_to_right_on_demand_headers.clone(), + lane, + )) + .map_err(|e| anyhow::format_err!("{}", e)) + .boxed(); + message_relays.push(right_to_left_messages); + } + + relay_utils::relay_metrics(self.base().common().metrics_params.clone()) + .expose() + .await + .map_err(|e| anyhow::format_err!("{}", e))?; + + futures::future::select_all(message_relays).await.0 + } +} + +/// Millau <> Rialto complex relay. +pub struct MillauRialtoFull2WayBridge { + base: ::Base, +} + +#[async_trait] +impl Full2WayBridge for MillauRialtoFull2WayBridge { + type Base = RelayToRelayBridge; + type Left = relay_millau_client::Millau; + type Right = relay_rialto_client::Rialto; + type L2R = MillauToRialtoCliBridge; + type R2L = RialtoToMillauCliBridge; + + fn new(base: Self::Base) -> anyhow::Result { + Ok(Self { base }) + } + + fn base(&self) -> &Self::Base { + &self.base + } + + fn mut_base(&mut self) -> &mut Self::Base { + &mut self.base + } +} + +/// Millau <> RialtoParachain complex relay. +pub struct MillauRialtoParachainFull2WayBridge { + base: ::Base, +} + +#[async_trait] +impl Full2WayBridge for MillauRialtoParachainFull2WayBridge { + type Base = RelayToParachainBridge; + type Left = relay_millau_client::Millau; + type Right = relay_rialto_parachain_client::RialtoParachain; + type L2R = MillauToRialtoParachainCliBridge; + type R2L = RialtoParachainToMillauCliBridge; + + fn new(base: Self::Base) -> anyhow::Result { + Ok(Self { base }) + } + + fn base(&self) -> &Self::Base { + &self.base + } + + fn mut_base(&mut self) -> &mut Self::Base { + &mut self.base + } +} + +/// BridgeHubRococo <> BridgeHubWococo complex relay. +pub struct BridgeHubRococoBridgeHubWococoFull2WayBridge { + base: ::Base, +} + +#[async_trait] +impl Full2WayBridge for BridgeHubRococoBridgeHubWococoFull2WayBridge { + type Base = ParachainToParachainBridge; + type Left = relay_bridge_hub_rococo_client::BridgeHubRococo; + type Right = relay_bridge_hub_wococo_client::BridgeHubWococo; + type L2R = BridgeHubRococoToBridgeHubWococoCliBridge; + type R2L = BridgeHubWococoToBridgeHubRococoCliBridge; + + fn new(base: Self::Base) -> anyhow::Result { + Ok(Self { base }) + } + + fn base(&self) -> &Self::Base { + &self.base + } + + fn mut_base(&mut self) -> &mut Self::Base { + &mut self.base + } +} + +/// Complex headers+messages relay. +#[derive(Debug, PartialEq, StructOpt)] +pub enum RelayHeadersAndMessages { + /// Millau <> Rialto relay. + MillauRialto(MillauRialtoHeadersAndMessages), + /// Millau <> RialtoParachain relay. + MillauRialtoParachain(MillauRialtoParachainHeadersAndMessages), + /// BridgeHubRococo <> BridgeHubWococo relay. + BridgeHubRococoBridgeHubWococo(BridgeHubRococoBridgeHubWococoHeadersAndMessages), +} + +impl RelayHeadersAndMessages { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + match self { + RelayHeadersAndMessages::MillauRialto(params) => + MillauRialtoFull2WayBridge::new(params.into_bridge().await?)?.run().await, + RelayHeadersAndMessages::MillauRialtoParachain(params) => + MillauRialtoParachainFull2WayBridge::new(params.into_bridge().await?)? + .run() + .await, + RelayHeadersAndMessages::BridgeHubRococoBridgeHubWococo(params) => + BridgeHubRococoBridgeHubWococoFull2WayBridge::new(params.into_bridge().await?)? + .run() + .await, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_parse_relay_to_relay_options() { + // when + let res = RelayHeadersAndMessages::from_iter(vec![ + "relay-headers-and-messages", + "millau-rialto", + "--millau-host", + "millau-node-alice", + "--millau-port", + "9944", + "--millau-signer", + "//Charlie", + "--millau-messages-pallet-owner", + "//RialtoMessagesOwner", + "--millau-transactions-mortality", + "64", + "--rialto-host", + "rialto-node-alice", + "--rialto-port", + "9944", + "--rialto-signer", + "//Charlie", + "--rialto-messages-pallet-owner", + "//MillauMessagesOwner", + "--rialto-transactions-mortality", + "64", + "--lane", + "00000000", + "--lane", + "73776170", + "--prometheus-host", + "0.0.0.0", + ]); + + // then + assert_eq!( + res, + RelayHeadersAndMessages::MillauRialto(MillauRialtoHeadersAndMessages { + shared: HeadersAndMessagesSharedParams { + lane: vec![ + HexLaneId([0x00, 0x00, 0x00, 0x00]), + HexLaneId([0x73, 0x77, 0x61, 0x70]) + ], + only_mandatory_headers: false, + prometheus_params: PrometheusParams { + no_prometheus: false, + prometheus_host: "0.0.0.0".into(), + prometheus_port: 9616, + }, + }, + left: MillauConnectionParams { + millau_host: "millau-node-alice".into(), + millau_port: 9944, + millau_secure: false, + millau_runtime_version: MillauRuntimeVersionParams { + millau_version_mode: RuntimeVersionType::Bundle, + millau_spec_version: None, + millau_transaction_version: None, + }, + }, + left_sign: MillauSigningParams { + millau_signer: Some("//Charlie".into()), + millau_signer_password: None, + millau_signer_file: None, + millau_signer_password_file: None, + millau_transactions_mortality: Some(64), + }, + left_messages_pallet_owner: MillauMessagesPalletOwnerSigningParams { + millau_messages_pallet_owner: Some("//RialtoMessagesOwner".into()), + millau_messages_pallet_owner_password: None, + }, + left_headers_to_right_sign_override: MillauHeadersToRialtoSigningParams { + millau_headers_to_rialto_signer: None, + millau_headers_to_rialto_signer_password: None, + millau_headers_to_rialto_signer_file: None, + millau_headers_to_rialto_signer_password_file: None, + millau_headers_to_rialto_transactions_mortality: None, + }, + right: RialtoConnectionParams { + rialto_host: "rialto-node-alice".into(), + rialto_port: 9944, + rialto_secure: false, + rialto_runtime_version: RialtoRuntimeVersionParams { + rialto_version_mode: RuntimeVersionType::Bundle, + rialto_spec_version: None, + rialto_transaction_version: None, + }, + }, + right_sign: RialtoSigningParams { + rialto_signer: Some("//Charlie".into()), + rialto_signer_password: None, + rialto_signer_file: None, + rialto_signer_password_file: None, + rialto_transactions_mortality: Some(64), + }, + right_messages_pallet_owner: RialtoMessagesPalletOwnerSigningParams { + rialto_messages_pallet_owner: Some("//MillauMessagesOwner".into()), + rialto_messages_pallet_owner_password: None, + }, + right_headers_to_left_sign_override: RialtoHeadersToMillauSigningParams { + rialto_headers_to_millau_signer: None, + rialto_headers_to_millau_signer_password: None, + rialto_headers_to_millau_signer_file: None, + rialto_headers_to_millau_signer_password_file: None, + rialto_headers_to_millau_transactions_mortality: None, + }, + }), + ); + } + + #[test] + fn should_parse_relay_to_parachain_options() { + // when + let res = RelayHeadersAndMessages::from_iter(vec![ + "relay-headers-and-messages", + "millau-rialto-parachain", + "--millau-host", + "millau-node-alice", + "--millau-port", + "9944", + "--millau-signer", + "//Iden", + "--rialto-headers-to-millau-signer", + "//Ken", + "--millau-messages-pallet-owner", + "//RialtoParachainMessagesOwner", + "--millau-transactions-mortality", + "64", + "--rialto-parachain-host", + "rialto-parachain-collator-charlie", + "--rialto-parachain-port", + "9944", + "--rialto-parachain-signer", + "//George", + "--rialto-parachain-messages-pallet-owner", + "//MillauMessagesOwner", + "--rialto-parachain-transactions-mortality", + "64", + "--rialto-host", + "rialto-node-alice", + "--rialto-port", + "9944", + "--lane", + "00000000", + "--prometheus-host", + "0.0.0.0", + ]); + + // then + assert_eq!( + res, + RelayHeadersAndMessages::MillauRialtoParachain( + MillauRialtoParachainHeadersAndMessages { + shared: HeadersAndMessagesSharedParams { + lane: vec![HexLaneId([0x00, 0x00, 0x00, 0x00])], + only_mandatory_headers: false, + prometheus_params: PrometheusParams { + no_prometheus: false, + prometheus_host: "0.0.0.0".into(), + prometheus_port: 9616, + }, + }, + left: MillauConnectionParams { + millau_host: "millau-node-alice".into(), + millau_port: 9944, + millau_secure: false, + millau_runtime_version: MillauRuntimeVersionParams { + millau_version_mode: RuntimeVersionType::Bundle, + millau_spec_version: None, + millau_transaction_version: None, + }, + }, + left_sign: MillauSigningParams { + millau_signer: Some("//Iden".into()), + millau_signer_password: None, + millau_signer_file: None, + millau_signer_password_file: None, + millau_transactions_mortality: Some(64), + }, + left_messages_pallet_owner: MillauMessagesPalletOwnerSigningParams { + millau_messages_pallet_owner: Some("//RialtoParachainMessagesOwner".into()), + millau_messages_pallet_owner_password: None, + }, + left_headers_to_right_sign_override: + MillauHeadersToRialtoParachainSigningParams { + millau_headers_to_rialto_parachain_signer: None, + millau_headers_to_rialto_parachain_signer_password: None, + millau_headers_to_rialto_parachain_signer_file: None, + millau_headers_to_rialto_parachain_signer_password_file: None, + millau_headers_to_rialto_parachain_transactions_mortality: None, + }, + right: RialtoParachainConnectionParams { + rialto_parachain_host: "rialto-parachain-collator-charlie".into(), + rialto_parachain_port: 9944, + rialto_parachain_secure: false, + rialto_parachain_runtime_version: RialtoParachainRuntimeVersionParams { + rialto_parachain_version_mode: RuntimeVersionType::Bundle, + rialto_parachain_spec_version: None, + rialto_parachain_transaction_version: None, + }, + }, + right_sign: RialtoParachainSigningParams { + rialto_parachain_signer: Some("//George".into()), + rialto_parachain_signer_password: None, + rialto_parachain_signer_file: None, + rialto_parachain_signer_password_file: None, + rialto_parachain_transactions_mortality: Some(64), + }, + right_messages_pallet_owner: RialtoParachainMessagesPalletOwnerSigningParams { + rialto_parachain_messages_pallet_owner: Some( + "//MillauMessagesOwner".into() + ), + rialto_parachain_messages_pallet_owner_password: None, + }, + right_relay_headers_to_left_sign_override: RialtoHeadersToMillauSigningParams { + rialto_headers_to_millau_signer: Some("//Ken".into()), + rialto_headers_to_millau_signer_password: None, + rialto_headers_to_millau_signer_file: None, + rialto_headers_to_millau_signer_password_file: None, + rialto_headers_to_millau_transactions_mortality: None, + }, + right_parachains_to_left_sign_override: RialtoParachainsToMillauSigningParams { + rialto_parachains_to_millau_signer: None, + rialto_parachains_to_millau_signer_password: None, + rialto_parachains_to_millau_signer_file: None, + rialto_parachains_to_millau_signer_password_file: None, + rialto_parachains_to_millau_transactions_mortality: None, + }, + right_relay: RialtoConnectionParams { + rialto_host: "rialto-node-alice".into(), + rialto_port: 9944, + rialto_secure: false, + rialto_runtime_version: RialtoRuntimeVersionParams { + rialto_version_mode: RuntimeVersionType::Bundle, + rialto_spec_version: None, + rialto_transaction_version: None, + }, + }, + } + ), + ); + } +} diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages/parachain_to_parachain.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages/parachain_to_parachain.rs new file mode 100644 index 00000000000..e7328a837eb --- /dev/null +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages/parachain_to_parachain.rs @@ -0,0 +1,281 @@ +// Copyright 2019-2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![allow(unused_macros)] // TODO (https://github.com/paritytech/parity-bridges-common/issues/1629): remove me + +use async_trait::async_trait; +use std::sync::Arc; + +use crate::cli::{ + bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}, + relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams}, + CliChain, +}; +use bp_polkadot_core::parachains::ParaHash; +use bp_runtime::BlockNumberOf; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, Chain, ChainWithTransactions, Client}; +use sp_core::Pair; +use substrate_relay_helper::{ + finality::SubstrateFinalitySyncPipeline, + on_demand::{ + headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay, + }, + TaggedAccount, TransactionParams, +}; + +/// A base relay between two parachain from different consensus systems. +/// +/// Such relay starts 2 messages relay. It also starts 2 on-demand header relays and 2 on-demand +/// parachain heads relay. +pub struct ParachainToParachainBridge< + L2R: MessagesCliBridge + ParachainToRelayHeadersCliBridge, + R2L: MessagesCliBridge + ParachainToRelayHeadersCliBridge, +> { + /// Parameters that are shared by all bridge types. + pub common: + Full2WayBridgeCommonParams<::Target, ::Target>, + /// Client of the left relay chain. + pub left_relay: Client<::SourceRelay>, + /// Client of the right relay chain. + pub right_relay: Client<::SourceRelay>, + + /// Override for right_relay->left headers signer. + pub right_headers_to_left_transaction_params: + TransactionParams::Target>>, + /// Override for left_relay->right headers signer. + pub left_headers_to_right_transaction_params: + TransactionParams::Target>>, + + /// Override for right->left parachains signer. + pub right_parachains_to_left_transaction_params: + TransactionParams::Target>>, + /// Override for left->right parachains signer. + pub left_parachains_to_right_transaction_params: + TransactionParams::Target>>, +} + +macro_rules! declare_parachain_to_parachain_bridge_schema { + // left-parachain, relay-chain-of-left-parachain, right-parachain, relay-chain-of-right-parachain + ($left_parachain:ident, $left_chain:ident, $right_parachain:ident, $right_chain:ident) => { + bp_runtime::paste::item! { + #[doc = $left_parachain ", " $left_chain ", " $right_parachain " and " $right_chain " headers+parachains+messages relay params."] + #[derive(Debug, PartialEq, StructOpt)] + pub struct [<$left_parachain $right_parachain HeadersAndMessages>] { + // shared parameters + #[structopt(flatten)] + shared: HeadersAndMessagesSharedParams, + + #[structopt(flatten)] + left: [<$left_parachain ConnectionParams>], + #[structopt(flatten)] + left_relay: [<$left_chain ConnectionParams>], + + // default signer, which is always used to sign messages relay transactions on the left chain + #[structopt(flatten)] + left_sign: [<$left_parachain SigningParams>], + // signer used to sign parameter update transactions at the left parachain + #[structopt(flatten)] + left_messages_pallet_owner: [<$left_parachain MessagesPalletOwnerSigningParams>], + + #[structopt(flatten)] + right: [<$right_parachain ConnectionParams>], + #[structopt(flatten)] + right_relay: [<$right_chain ConnectionParams>], + + // default signer, which is always used to sign messages relay transactions on the right chain + #[structopt(flatten)] + right_sign: [<$right_parachain SigningParams>], + // signer used to sign parameter update transactions at the right parachain + #[structopt(flatten)] + right_messages_pallet_owner: [<$right_parachain MessagesPalletOwnerSigningParams>], + + // override for right_relay->left-parachain headers signer + #[structopt(flatten)] + right_relay_headers_to_left_sign_override: [<$right_chain HeadersTo $left_parachain SigningParams>], + // override for left_relay->right-parachain headers signer + #[structopt(flatten)] + left_relay_headers_to_right_sign_override: [<$left_chain HeadersTo $right_parachain SigningParams>], + + // override for right->left parachains signer + #[structopt(flatten)] + right_parachains_to_left_sign_override: [<$right_chain ParachainsTo $left_parachain SigningParams>], + // override for left->right parachains signer + #[structopt(flatten)] + left_parachains_to_right_sign_override: [<$left_chain ParachainsTo $right_parachain SigningParams>], + } + + impl [<$left_parachain $right_parachain HeadersAndMessages>] { + async fn into_bridge< + Left: ChainWithTransactions + CliChain>, + LeftRelay: CliChain, + Right: ChainWithTransactions + CliChain>, + RightRelay: CliChain, + L2R: CliBridgeBase + + MessagesCliBridge + + ParachainToRelayHeadersCliBridge, + R2L: CliBridgeBase + + MessagesCliBridge + + ParachainToRelayHeadersCliBridge, + >( + self, + ) -> anyhow::Result> { + Ok(ParachainToParachainBridge { + common: Full2WayBridgeCommonParams::new::( + self.shared, + BridgeEndCommonParams { + client: self.left.into_client::().await?, + sign: self.left_sign.to_keypair::()?, + transactions_mortality: self.left_sign.transactions_mortality()?, + messages_pallet_owner: self.left_messages_pallet_owner.to_keypair::()?, + accounts: vec![], + }, + BridgeEndCommonParams { + client: self.right.into_client::().await?, + sign: self.right_sign.to_keypair::()?, + transactions_mortality: self.right_sign.transactions_mortality()?, + messages_pallet_owner: self.right_messages_pallet_owner.to_keypair::()?, + accounts: vec![], + }, + )?, + left_relay: self.left_relay.into_client::().await?, + right_relay: self.right_relay.into_client::().await?, + right_headers_to_left_transaction_params: self + .right_relay_headers_to_left_sign_override + .transaction_params_or::(&self.left_sign)?, + left_headers_to_right_transaction_params: self + .left_relay_headers_to_right_sign_override + .transaction_params_or::(&self.right_sign)?, + right_parachains_to_left_transaction_params: self + .right_parachains_to_left_sign_override + .transaction_params_or::(&self.left_sign)?, + left_parachains_to_right_transaction_params: self + .left_parachains_to_right_sign_override + .transaction_params_or::(&self.right_sign)?, + }) + } + } + } + }; +} + +#[async_trait] +impl< + Left: Chain + ChainWithTransactions + CliChain>, + Right: Chain + + ChainWithTransactions + + CliChain>, + LeftRelay: Chain + + CliChain, + RightRelay: Chain + + CliChain, + L2R: CliBridgeBase + + MessagesCliBridge + + ParachainToRelayHeadersCliBridge, + R2L: CliBridgeBase + + MessagesCliBridge + + ParachainToRelayHeadersCliBridge, + > Full2WayBridgeBase for ParachainToParachainBridge +where + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, +{ + type Params = ParachainToParachainBridge; + type Left = Left; + type Right = Right; + + fn common(&self) -> &Full2WayBridgeCommonParams { + &self.common + } + + fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams { + &mut self.common + } + + async fn start_on_demand_headers_relayers( + &mut self, + ) -> anyhow::Result<( + Arc>>, + Arc>>, + )> { + self.common.left.accounts.push(TaggedAccount::Headers { + id: self.right_headers_to_left_transaction_params.signer.public().into(), + bridged_chain: RightRelay::NAME.to_string(), + }); + self.common.left.accounts.push(TaggedAccount::Parachains { + id: self.right_parachains_to_left_transaction_params.signer.public().into(), + bridged_chain: RightRelay::NAME.to_string(), + }); + self.common.right.accounts.push(TaggedAccount::Headers { + id: self.left_headers_to_right_transaction_params.signer.public().into(), + bridged_chain: Left::NAME.to_string(), + }); + self.common.right.accounts.push(TaggedAccount::Parachains { + id: self.left_parachains_to_right_transaction_params.signer.public().into(), + bridged_chain: LeftRelay::NAME.to_string(), + }); + + ::RelayFinality::start_relay_guards( + &self.common.right.client, + &self.left_headers_to_right_transaction_params, + self.common.right.client.can_start_version_guard(), + ) + .await?; + ::RelayFinality::start_relay_guards( + &self.common.left.client, + &self.right_headers_to_left_transaction_params, + self.common.left.client.can_start_version_guard(), + ) + .await?; + + let left_relay_to_right_on_demand_headers = + OnDemandHeadersRelay::new::<::RelayFinality>( + self.left_relay.clone(), + self.common.right.client.clone(), + self.left_headers_to_right_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + ); + let right_relay_to_left_on_demand_headers = + OnDemandHeadersRelay::new::<::RelayFinality>( + self.right_relay.clone(), + self.common.left.client.clone(), + self.right_headers_to_left_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + ); + + let left_to_right_on_demand_parachains = OnDemandParachainsRelay::new::< + ::ParachainFinality, + >( + self.left_relay.clone(), + self.common.right.client.clone(), + self.left_parachains_to_right_transaction_params.clone(), + Arc::new(left_relay_to_right_on_demand_headers), + ); + let right_to_left_on_demand_parachains = OnDemandParachainsRelay::new::< + ::ParachainFinality, + >( + self.right_relay.clone(), + self.common.left.client.clone(), + self.right_parachains_to_left_transaction_params.clone(), + Arc::new(right_relay_to_left_on_demand_headers), + ); + + Ok(( + Arc::new(left_to_right_on_demand_parachains), + Arc::new(right_to_left_on_demand_parachains), + )) + } +} diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs new file mode 100644 index 00000000000..4070783df09 --- /dev/null +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs @@ -0,0 +1,254 @@ +// Copyright 2019-2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use async_trait::async_trait; +use std::sync::Arc; + +use crate::cli::{ + bridge::{ + CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge, + RelayToRelayHeadersCliBridge, + }, + relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams}, + CliChain, +}; +use bp_polkadot_core::parachains::ParaHash; +use bp_runtime::BlockNumberOf; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, Chain, ChainWithTransactions, Client}; +use sp_core::Pair; +use substrate_relay_helper::{ + finality::SubstrateFinalitySyncPipeline, + on_demand::{ + headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay, + }, + TaggedAccount, TransactionParams, +}; + +/// A base relay between standalone (relay) chain and a parachain from another consensus system. +/// +/// Such relay starts 2 messages relay. It also starts 2 on-demand header relays and 1 on-demand +/// parachain heads relay. +pub struct RelayToParachainBridge< + L2R: MessagesCliBridge + RelayToRelayHeadersCliBridge, + R2L: MessagesCliBridge + ParachainToRelayHeadersCliBridge, +> { + /// Parameters that are shared by all bridge types. + pub common: + Full2WayBridgeCommonParams<::Target, ::Target>, + /// Client of the right relay chain. + pub right_relay: Client<::SourceRelay>, + + /// Override for right_relay->left headers signer. + pub right_headers_to_left_transaction_params: + TransactionParams::Target>>, + /// Override for right->left parachains signer. + pub right_parachains_to_left_transaction_params: + TransactionParams::Target>>, + /// Override for left->right headers signer. + pub left_headers_to_right_transaction_params: + TransactionParams::Target>>, +} + +macro_rules! declare_relay_to_parachain_bridge_schema { + // chain, parachain, relay-chain-of-parachain + ($left_chain:ident, $right_parachain:ident, $right_chain:ident) => { + bp_runtime::paste::item! { + #[doc = $left_chain ", " $right_parachain " and " $right_chain " headers+parachains+messages relay params."] + #[derive(Debug, PartialEq, StructOpt)] + pub struct [<$left_chain $right_parachain HeadersAndMessages>] { + // shared parameters + #[structopt(flatten)] + shared: HeadersAndMessagesSharedParams, + + #[structopt(flatten)] + left: [<$left_chain ConnectionParams>], + + // default signer, which is always used to sign messages relay transactions on the left chain + #[structopt(flatten)] + left_sign: [<$left_chain SigningParams>], + // signer used to sign parameter update transactions at the left chain + #[structopt(flatten)] + left_messages_pallet_owner: [<$left_chain MessagesPalletOwnerSigningParams>], + + #[structopt(flatten)] + right: [<$right_parachain ConnectionParams>], + #[structopt(flatten)] + right_relay: [<$right_chain ConnectionParams>], + + // default signer, which is always used to sign messages relay transactions on the right chain + #[structopt(flatten)] + right_sign: [<$right_parachain SigningParams>], + // signer used to sign parameter update transactions at the left chain + #[structopt(flatten)] + right_messages_pallet_owner: [<$right_parachain MessagesPalletOwnerSigningParams>], + + + // override for right_relay->left headers signer + #[structopt(flatten)] + right_relay_headers_to_left_sign_override: [<$right_chain HeadersTo $left_chain SigningParams>], + // override for left->right headers signer + #[structopt(flatten)] + left_headers_to_right_sign_override: [<$left_chain HeadersTo $right_parachain SigningParams>], + + // override for right->left parachains signer + #[structopt(flatten)] + right_parachains_to_left_sign_override: [<$right_chain ParachainsTo $left_chain SigningParams>], + } + + impl [<$left_chain $right_parachain HeadersAndMessages>] { + async fn into_bridge< + Left: ChainWithTransactions + CliChain>, + Right: ChainWithTransactions + CliChain>, + RightRelay: CliChain, + L2R: CliBridgeBase + MessagesCliBridge + RelayToRelayHeadersCliBridge, + R2L: CliBridgeBase + + MessagesCliBridge + + ParachainToRelayHeadersCliBridge, + >( + self, + ) -> anyhow::Result> { + Ok(RelayToParachainBridge { + common: Full2WayBridgeCommonParams::new::( + self.shared, + BridgeEndCommonParams { + client: self.left.into_client::().await?, + sign: self.left_sign.to_keypair::()?, + transactions_mortality: self.left_sign.transactions_mortality()?, + messages_pallet_owner: self.left_messages_pallet_owner.to_keypair::()?, + accounts: vec![], + }, + BridgeEndCommonParams { + client: self.right.into_client::().await?, + sign: self.right_sign.to_keypair::()?, + transactions_mortality: self.right_sign.transactions_mortality()?, + messages_pallet_owner: self.right_messages_pallet_owner.to_keypair::()?, + accounts: vec![], + }, + )?, + right_relay: self.right_relay.into_client::().await?, + right_headers_to_left_transaction_params: self + .right_relay_headers_to_left_sign_override + .transaction_params_or::( + &self.left_sign, + )?, + right_parachains_to_left_transaction_params: self + .right_parachains_to_left_sign_override + .transaction_params_or::( + &self.left_sign, + )?, + left_headers_to_right_transaction_params: self + .left_headers_to_right_sign_override + .transaction_params_or::(&self.right_sign)?, + }) + } + } + } + }; +} + +#[async_trait] +impl< + Left: ChainWithTransactions + CliChain>, + Right: Chain + + ChainWithTransactions + + CliChain>, + RightRelay: Chain + + CliChain, + L2R: CliBridgeBase + + MessagesCliBridge + + RelayToRelayHeadersCliBridge, + R2L: CliBridgeBase + + MessagesCliBridge + + ParachainToRelayHeadersCliBridge, + > Full2WayBridgeBase for RelayToParachainBridge +where + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, +{ + type Params = RelayToParachainBridge; + type Left = Left; + type Right = Right; + + fn common(&self) -> &Full2WayBridgeCommonParams { + &self.common + } + + fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams { + &mut self.common + } + + async fn start_on_demand_headers_relayers( + &mut self, + ) -> anyhow::Result<( + Arc>>, + Arc>>, + )> { + self.common.left.accounts.push(TaggedAccount::Headers { + id: self.right_headers_to_left_transaction_params.signer.public().into(), + bridged_chain: RightRelay::NAME.to_string(), + }); + self.common.left.accounts.push(TaggedAccount::Parachains { + id: self.right_parachains_to_left_transaction_params.signer.public().into(), + bridged_chain: RightRelay::NAME.to_string(), + }); + self.common.right.accounts.push(TaggedAccount::Headers { + id: self.left_headers_to_right_transaction_params.signer.public().into(), + bridged_chain: Left::NAME.to_string(), + }); + + ::Finality::start_relay_guards( + &self.common.right.client, + &self.left_headers_to_right_transaction_params, + self.common.right.client.can_start_version_guard(), + ) + .await?; + ::RelayFinality::start_relay_guards( + &self.common.left.client, + &self.right_headers_to_left_transaction_params, + self.common.left.client.can_start_version_guard(), + ) + .await?; + + let left_to_right_on_demand_headers = + OnDemandHeadersRelay::new::<::Finality>( + self.common.left.client.clone(), + self.common.right.client.clone(), + self.left_headers_to_right_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + ); + let right_relay_to_left_on_demand_headers = + OnDemandHeadersRelay::new::<::RelayFinality>( + self.right_relay.clone(), + self.common.left.client.clone(), + self.right_headers_to_left_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + ); + let right_to_left_on_demand_parachains = OnDemandParachainsRelay::new::< + ::ParachainFinality, + >( + self.right_relay.clone(), + self.common.left.client.clone(), + self.right_parachains_to_left_transaction_params.clone(), + Arc::new(right_relay_to_left_on_demand_headers), + ); + + Ok(( + Arc::new(left_to_right_on_demand_headers), + Arc::new(right_to_left_on_demand_parachains), + )) + } +} diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs new file mode 100644 index 00000000000..bda532a2afd --- /dev/null +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs @@ -0,0 +1,194 @@ +// Copyright 2019-2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use async_trait::async_trait; +use std::sync::Arc; + +use crate::cli::{ + bridge::{CliBridgeBase, MessagesCliBridge, RelayToRelayHeadersCliBridge}, + relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams}, + CliChain, +}; +use bp_runtime::BlockNumberOf; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, ChainWithTransactions}; +use sp_core::Pair; +use substrate_relay_helper::{ + finality::SubstrateFinalitySyncPipeline, + on_demand::{headers::OnDemandHeadersRelay, OnDemandRelay}, + TaggedAccount, TransactionParams, +}; + +/// A base relay between two standalone (relay) chains. +/// +/// Such relay starts 2 messages relay and 2 on-demand header relays. +pub struct RelayToRelayBridge< + L2R: MessagesCliBridge + RelayToRelayHeadersCliBridge, + R2L: MessagesCliBridge + RelayToRelayHeadersCliBridge, +> { + /// Parameters that are shared by all bridge types. + pub common: + Full2WayBridgeCommonParams<::Target, ::Target>, + /// Override for right->left headers signer. + pub right_to_left_transaction_params: + TransactionParams::Target>>, + /// Override for left->right headers signer. + pub left_to_right_transaction_params: + TransactionParams::Target>>, +} + +macro_rules! declare_relay_to_relay_bridge_schema { + ($left_chain:ident, $right_chain:ident) => { + bp_runtime::paste::item! { + #[doc = $left_chain " and " $right_chain " headers+messages relay params."] + #[derive(Debug, PartialEq, StructOpt)] + pub struct [<$left_chain $right_chain HeadersAndMessages>] { + #[structopt(flatten)] + shared: HeadersAndMessagesSharedParams, + // default signer, which is always used to sign messages relay transactions on the left chain + #[structopt(flatten)] + left: [<$left_chain ConnectionParams>], + // override for right->left headers signer + #[structopt(flatten)] + right_headers_to_left_sign_override: [<$right_chain HeadersTo $left_chain SigningParams>], + #[structopt(flatten)] + left_sign: [<$left_chain SigningParams>], + #[structopt(flatten)] + left_messages_pallet_owner: [<$left_chain MessagesPalletOwnerSigningParams>], + // default signer, which is always used to sign messages relay transactions on the right chain + #[structopt(flatten)] + right: [<$right_chain ConnectionParams>], + // override for left->right headers signer + #[structopt(flatten)] + left_headers_to_right_sign_override: [<$left_chain HeadersTo $right_chain SigningParams>], + #[structopt(flatten)] + right_sign: [<$right_chain SigningParams>], + #[structopt(flatten)] + right_messages_pallet_owner: [<$right_chain MessagesPalletOwnerSigningParams>], + } + + impl [<$left_chain $right_chain HeadersAndMessages>] { + async fn into_bridge< + Left: ChainWithTransactions + CliChain>, + Right: ChainWithTransactions + CliChain>, + L2R: CliBridgeBase + MessagesCliBridge + RelayToRelayHeadersCliBridge, + R2L: CliBridgeBase + MessagesCliBridge + RelayToRelayHeadersCliBridge, + >( + self, + ) -> anyhow::Result> { + Ok(RelayToRelayBridge { + common: Full2WayBridgeCommonParams::new::( + self.shared, + BridgeEndCommonParams { + client: self.left.into_client::().await?, + sign: self.left_sign.to_keypair::()?, + transactions_mortality: self.left_sign.transactions_mortality()?, + messages_pallet_owner: self.left_messages_pallet_owner.to_keypair::()?, + accounts: vec![], + }, + BridgeEndCommonParams { + client: self.right.into_client::().await?, + sign: self.right_sign.to_keypair::()?, + transactions_mortality: self.right_sign.transactions_mortality()?, + messages_pallet_owner: self.right_messages_pallet_owner.to_keypair::()?, + accounts: vec![], + }, + )?, + right_to_left_transaction_params: self + .right_headers_to_left_sign_override + .transaction_params_or::(&self.left_sign)?, + left_to_right_transaction_params: self + .left_headers_to_right_sign_override + .transaction_params_or::(&self.right_sign)?, + }) + } + } + } + }; +} + +#[async_trait] +impl< + Left: ChainWithTransactions + CliChain>, + Right: ChainWithTransactions + CliChain>, + L2R: CliBridgeBase + + MessagesCliBridge + + RelayToRelayHeadersCliBridge, + R2L: CliBridgeBase + + MessagesCliBridge + + RelayToRelayHeadersCliBridge, + > Full2WayBridgeBase for RelayToRelayBridge +where + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, +{ + type Params = RelayToRelayBridge; + type Left = Left; + type Right = Right; + + fn common(&self) -> &Full2WayBridgeCommonParams { + &self.common + } + + fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams { + &mut self.common + } + + async fn start_on_demand_headers_relayers( + &mut self, + ) -> anyhow::Result<( + Arc>>, + Arc>>, + )> { + self.common.right.accounts.push(TaggedAccount::Headers { + id: self.left_to_right_transaction_params.signer.public().into(), + bridged_chain: Self::Left::NAME.to_string(), + }); + self.common.left.accounts.push(TaggedAccount::Headers { + id: self.right_to_left_transaction_params.signer.public().into(), + bridged_chain: Self::Right::NAME.to_string(), + }); + + ::Finality::start_relay_guards( + &self.common.right.client, + &self.left_to_right_transaction_params, + self.common.right.client.can_start_version_guard(), + ) + .await?; + ::Finality::start_relay_guards( + &self.common.left.client, + &self.right_to_left_transaction_params, + self.common.left.client.can_start_version_guard(), + ) + .await?; + + let left_to_right_on_demand_headers = + OnDemandHeadersRelay::new::<::Finality>( + self.common.left.client.clone(), + self.common.right.client.clone(), + self.left_to_right_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + ); + let right_to_left_on_demand_headers = + OnDemandHeadersRelay::new::<::Finality>( + self.common.right.client.clone(), + self.common.left.client.clone(), + self.right_to_left_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + ); + + Ok((Arc::new(left_to_right_on_demand_headers), Arc::new(right_to_left_on_demand_headers))) + } +} diff --git a/relays/bin-substrate/src/cli/relay_messages.rs b/relays/bin-substrate/src/cli/relay_messages.rs new file mode 100644 index 00000000000..7f4dc34ac8b --- /dev/null +++ b/relays/bin-substrate/src/cli/relay_messages.rs @@ -0,0 +1,117 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use async_trait::async_trait; +use sp_core::Pair; +use structopt::StructOpt; +use strum::VariantNames; + +use crate::chains::{ + bridge_hub_rococo_messages_to_bridge_hub_wococo::BridgeHubRococoToBridgeHubWococoMessagesCliBridge, + bridge_hub_wococo_messages_to_bridge_hub_rococo::BridgeHubWococoToBridgeHubRococoMessagesCliBridge, + millau_headers_to_rialto::MillauToRialtoCliBridge, + millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, +}; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, BalanceOf, ChainWithTransactions}; +use substrate_relay_helper::{messages_lane::MessagesRelayParams, TransactionParams}; + +use crate::cli::{bridge::*, chain_schema::*, CliChain, HexLaneId, PrometheusParams}; + +/// Start messages relayer process. +#[derive(StructOpt)] +pub struct RelayMessages { + /// A bridge instance to relay messages for. + #[structopt(possible_values = FullBridge::VARIANTS, case_insensitive = true)] + bridge: FullBridge, + /// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`. + #[structopt(long, default_value = "00000000")] + lane: HexLaneId, + #[structopt(flatten)] + source: SourceConnectionParams, + #[structopt(flatten)] + source_sign: SourceSigningParams, + #[structopt(flatten)] + target: TargetConnectionParams, + #[structopt(flatten)] + target_sign: TargetSigningParams, + #[structopt(flatten)] + prometheus_params: PrometheusParams, +} + +#[async_trait] +trait MessagesRelayer: MessagesCliBridge +where + Self::Source: ChainWithTransactions + CliChain>, + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, + BalanceOf: TryFrom>, +{ + async fn relay_messages(data: RelayMessages) -> anyhow::Result<()> { + let source_client = data.source.into_client::().await?; + let source_sign = data.source_sign.to_keypair::()?; + let source_transactions_mortality = data.source_sign.transactions_mortality()?; + let target_client = data.target.into_client::().await?; + let target_sign = data.target_sign.to_keypair::()?; + let target_transactions_mortality = data.target_sign.transactions_mortality()?; + + substrate_relay_helper::messages_lane::run::(MessagesRelayParams { + source_client, + source_transaction_params: TransactionParams { + signer: source_sign, + mortality: source_transactions_mortality, + }, + target_client, + target_transaction_params: TransactionParams { + signer: target_sign, + mortality: target_transactions_mortality, + }, + source_to_target_headers_relay: None, + target_to_source_headers_relay: None, + lane_id: data.lane.into(), + metrics_params: data.prometheus_params.into(), + }) + .await + .map_err(|e| anyhow::format_err!("{}", e)) + } +} + +impl MessagesRelayer for MillauToRialtoCliBridge {} +impl MessagesRelayer for RialtoToMillauCliBridge {} +impl MessagesRelayer for MillauToRialtoParachainCliBridge {} +impl MessagesRelayer for RialtoParachainToMillauCliBridge {} +impl MessagesRelayer for BridgeHubRococoToBridgeHubWococoMessagesCliBridge {} +impl MessagesRelayer for BridgeHubWococoToBridgeHubRococoMessagesCliBridge {} + +impl RelayMessages { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + match self.bridge { + FullBridge::MillauToRialto => MillauToRialtoCliBridge::relay_messages(self), + FullBridge::RialtoToMillau => RialtoToMillauCliBridge::relay_messages(self), + FullBridge::MillauToRialtoParachain => + MillauToRialtoParachainCliBridge::relay_messages(self), + FullBridge::RialtoParachainToMillau => + RialtoParachainToMillauCliBridge::relay_messages(self), + FullBridge::BridgeHubRococoToBridgeHubWococo => + BridgeHubRococoToBridgeHubWococoMessagesCliBridge::relay_messages(self), + FullBridge::BridgeHubWococoToBridgeHubRococo => + BridgeHubWococoToBridgeHubRococoMessagesCliBridge::relay_messages(self), + } + .await + } +} diff --git a/relays/bin-substrate/src/cli/relay_parachains.rs b/relays/bin-substrate/src/cli/relay_parachains.rs new file mode 100644 index 00000000000..286cecc914b --- /dev/null +++ b/relays/bin-substrate/src/cli/relay_parachains.rs @@ -0,0 +1,139 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::chains::{ + rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, + rococo_parachains_to_bridge_hub_wococo::BridgeHubRococoToBridgeHubWococoCliBridge, + westend_parachains_to_millau::WestmintToMillauCliBridge, + wococo_parachains_to_bridge_hub_rococo::BridgeHubWococoToBridgeHubRococoCliBridge, +}; +use async_std::sync::Mutex; +use async_trait::async_trait; +use bp_polkadot_core::parachains::ParaId; +use parachains_relay::parachains_loop::{ + AvailableHeader, ParachainSyncParams, SourceClient, TargetClient, +}; +use relay_utils::metrics::{GlobalMetrics, StandaloneMetric}; +use std::sync::Arc; +use structopt::StructOpt; +use strum::{EnumString, EnumVariantNames, VariantNames}; +use substrate_relay_helper::{ + parachains::{ + source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter, + SubstrateParachainsPipeline, + }, + TransactionParams, +}; + +use crate::cli::{bridge::ParachainToRelayHeadersCliBridge, chain_schema::*, PrometheusParams}; + +/// Start parachain heads relayer process. +#[derive(StructOpt)] +pub struct RelayParachains { + /// A bridge instance to relay parachains heads for. + #[structopt(possible_values = RelayParachainsBridge::VARIANTS, case_insensitive = true)] + bridge: RelayParachainsBridge, + #[structopt(flatten)] + source: SourceConnectionParams, + #[structopt(flatten)] + target: TargetConnectionParams, + #[structopt(flatten)] + target_sign: TargetSigningParams, + #[structopt(flatten)] + prometheus_params: PrometheusParams, +} + +/// Parachain heads relay bridge. +#[derive(Debug, EnumString, EnumVariantNames)] +#[strum(serialize_all = "kebab_case")] +pub enum RelayParachainsBridge { + RialtoToMillau, + // TODO:check-parameter - rename to WestmintToMillau? + WestendToMillau, + BridgeHubRococoToBridgeHubWococo, + BridgeHubWococoToBridgeHubRococo, +} + +#[async_trait] +trait ParachainsRelayer: ParachainToRelayHeadersCliBridge +where + ParachainsSource: + SourceClient>, + ParachainsTarget: + TargetClient>, +{ + async fn relay_headers(data: RelayParachains) -> anyhow::Result<()> { + let source_client = data.source.into_client::().await?; + let source_client = ParachainsSource::::new( + source_client, + Arc::new(Mutex::new(AvailableHeader::Missing)), + ); + + let target_transaction_params = TransactionParams { + signer: data.target_sign.to_keypair::()?, + mortality: data.target_sign.target_transactions_mortality, + }; + let target_client = data.target.into_client::().await?; + let target_client = ParachainsTarget::::new( + target_client.clone(), + target_transaction_params, + ); + + let metrics_params: relay_utils::metrics::MetricsParams = data.prometheus_params.into(); + GlobalMetrics::new()?.register_and_spawn(&metrics_params.registry)?; + + parachains_relay::parachains_loop::run( + source_client, + target_client, + ParachainSyncParams { + parachains: vec![ + ParaId(::SOURCE_PARACHAIN_PARA_ID) + ], + stall_timeout: std::time::Duration::from_secs(60), + strategy: parachains_relay::parachains_loop::ParachainSyncStrategy::Any, + }, + metrics_params, + futures::future::pending(), + ) + .await + .map_err(|e| anyhow::format_err!("{}", e)) + } +} + +impl ParachainsRelayer for RialtoParachainToMillauCliBridge {} + +impl ParachainsRelayer for WestmintToMillauCliBridge {} + +impl ParachainsRelayer for BridgeHubRococoToBridgeHubWococoCliBridge {} + +impl ParachainsRelayer for BridgeHubWococoToBridgeHubRococoCliBridge {} + +impl RelayParachains { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + match self.bridge { + RelayParachainsBridge::RialtoToMillau => + RialtoParachainToMillauCliBridge::relay_headers(self), + RelayParachainsBridge::WestendToMillau => + WestmintToMillauCliBridge::relay_headers(self), + RelayParachainsBridge::BridgeHubRococoToBridgeHubWococo => + BridgeHubRococoToBridgeHubWococoCliBridge::relay_headers(self), + RelayParachainsBridge::BridgeHubWococoToBridgeHubRococo => + BridgeHubWococoToBridgeHubRococoCliBridge::relay_headers(self), + } + .await + } +} diff --git a/relays/bin-substrate/src/cli/resubmit_transactions.rs b/relays/bin-substrate/src/cli/resubmit_transactions.rs new file mode 100644 index 00000000000..c69f9c8ae4e --- /dev/null +++ b/relays/bin-substrate/src/cli/resubmit_transactions.rs @@ -0,0 +1,561 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::cli::{chain_schema::*, Balance}; + +use bp_runtime::HeaderIdProvider; +use codec::{Decode, Encode}; +use num_traits::{One, Zero}; +use relay_substrate_client::{ + AccountKeyPairOf, BlockWithJustification, Chain, ChainWithTransactions, Client, + Error as SubstrateError, HeaderIdOf, HeaderOf, SignParam, +}; +use relay_utils::FailedClient; +use sp_core::Bytes; +use sp_runtime::{ + traits::{Hash, Header as HeaderT}, + transaction_validity::TransactionPriority, +}; +use structopt::StructOpt; +use strum::{EnumString, EnumVariantNames, VariantNames}; +use substrate_relay_helper::TransactionParams; + +/// Start resubmit transactions process. +#[derive(StructOpt)] +pub struct ResubmitTransactions { + /// A bridge instance to relay headers for. + #[structopt(possible_values = RelayChain::VARIANTS, case_insensitive = true)] + chain: RelayChain, + #[structopt(flatten)] + target: TargetConnectionParams, + #[structopt(flatten)] + target_sign: TargetSigningParams, + /// Number of blocks we see before considering queued transaction as stalled. + #[structopt(long, default_value = "5")] + stalled_blocks: u32, + /// Tip limit. We'll never submit transaction with larger tip. + #[structopt(long)] + tip_limit: Balance, + /// Tip increase step. We'll be checking updated transaction priority by increasing its tip by + /// this step. + #[structopt(long)] + tip_step: Balance, + /// Priority selection strategy. + #[structopt(subcommand)] + strategy: PrioritySelectionStrategy, +} + +/// Chain, which transactions we're going to track && resubmit. +#[derive(Debug, EnumString, EnumVariantNames)] +#[strum(serialize_all = "kebab_case")] +pub enum RelayChain { + Millau, +} + +/// Strategy to use for priority selection. +#[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy)] +pub enum PrioritySelectionStrategy { + /// Strategy selects tip that changes transaction priority to be better than priority of + /// the first transaction of previous block. + /// + /// It only makes sense to use this strategy for Millau transactions. Millau has transactions + /// that are close to block limits, so if there are any other queued transactions, 'large' + /// transaction won't fit the block && will be postponed. To avoid this, we change its priority + /// to some large value, making it best transaction => it'll be 'mined' first. + MakeItBestTransaction, + /// Strategy selects tip that changes transaction priority to be better than priority of + /// selected queued transaction. + /// + /// When we first see stalled transaction, we make it better than worst 1/4 of queued + /// transactions. If it is still stalled, we'll make it better than 1/3 of queued transactions, + /// ... + MakeItBetterThanQueuedTransaction, +} + +macro_rules! select_bridge { + ($bridge: expr, $generic: tt) => { + match $bridge { + RelayChain::Millau => { + type Target = relay_millau_client::Millau; + type TargetSign = relay_millau_client::Millau; + + $generic + }, + } + }; +} + +impl ResubmitTransactions { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + select_bridge!(self.chain, { + let relay_loop_name = format!("ResubmitTransactions{}", Target::NAME); + let client = self.target.into_client::().await?; + let transaction_params = TransactionParams { + signer: self.target_sign.to_keypair::()?, + mortality: self.target_sign.target_transactions_mortality, + }; + + relay_utils::relay_loop((), client) + .run(relay_loop_name, move |_, client, _| { + run_until_connection_lost( + client, + transaction_params.clone(), + Context { + strategy: self.strategy, + best_header: HeaderOf::::new( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ), + transaction: None, + resubmitted: 0, + stalled_for: Zero::zero(), + stalled_for_limit: self.stalled_blocks as _, + tip_step: self.tip_step.cast() as _, + tip_limit: self.tip_limit.cast() as _, + }, + ) + }) + .await + .map_err(Into::into) + }) + } +} + +impl PrioritySelectionStrategy { + /// Select target priority. + async fn select_target_priority( + &self, + client: &Client, + context: &Context, + ) -> Result, SubstrateError> { + match *self { + PrioritySelectionStrategy::MakeItBestTransaction => + read_previous_block_best_priority(client, context).await, + PrioritySelectionStrategy::MakeItBetterThanQueuedTransaction => + select_priority_from_queue(client, context).await, + } + } +} + +#[derive(Debug)] +struct Context { + /// Priority selection strategy. + strategy: PrioritySelectionStrategy, + /// Best known block header. + best_header: C::Header, + /// Hash of the (potentially) stalled transaction. + transaction: Option, + /// How many times we have resubmitted this `transaction`? + resubmitted: u32, + /// This transaction is in pool for `stalled_for` wakeup intervals. + stalled_for: C::BlockNumber, + /// When `stalled_for` reaching this limit, transaction is considered stalled. + stalled_for_limit: C::BlockNumber, + /// Tip step interval. + tip_step: C::Balance, + /// Maximal tip. + tip_limit: C::Balance, +} + +impl Context { + /// Return true if transaction has stalled. + fn is_stalled(&self) -> bool { + self.stalled_for >= self.stalled_for_limit + } + + /// Notice resubmitted transaction. + fn notice_resubmitted_transaction(mut self, transaction: C::Hash) -> Self { + self.transaction = Some(transaction); + self.stalled_for = Zero::zero(); + self.resubmitted += 1; + self + } + + /// Notice transaction from the transaction pool. + fn notice_transaction(mut self, transaction: C::Hash) -> Self { + if self.transaction == Some(transaction) { + self.stalled_for += One::one(); + } else { + self.transaction = Some(transaction); + self.stalled_for = One::one(); + self.resubmitted = 0; + } + self + } +} + +/// Run resubmit transactions loop. +async fn run_until_connection_lost( + client: Client, + transaction_params: TransactionParams>, + mut context: Context, +) -> Result<(), FailedClient> { + loop { + async_std::task::sleep(C::AVERAGE_BLOCK_INTERVAL).await; + + let result = run_loop_iteration(client.clone(), transaction_params.clone(), context).await; + context = match result { + Ok(context) => context, + Err(error) => { + log::error!( + target: "bridge", + "Resubmit {} transactions loop has failed with error: {:?}", + C::NAME, + error, + ); + return Err(FailedClient::Target) + }, + }; + } +} + +/// Run single loop iteration. +async fn run_loop_iteration( + client: Client, + transaction_params: TransactionParams>, + mut context: Context, +) -> Result, SubstrateError> { + // correct best header is required for all other actions + context.best_header = client.best_header().await?; + + // check if there's queued transaction, signed by given author + let original_transaction = + match lookup_signer_transaction(&client, &transaction_params.signer).await? { + Some(original_transaction) => original_transaction, + None => { + log::trace!(target: "bridge", "No {} transactions from required signer in the txpool", C::NAME); + return Ok(context) + }, + }; + let original_transaction_hash = C::Hasher::hash(&original_transaction.encode()); + let context = context.notice_transaction(original_transaction_hash); + + // if transaction hasn't been mined for `stalled_blocks`, we'll need to resubmit it + if !context.is_stalled() { + log::trace!( + target: "bridge", + "{} transaction {:?} is not yet stalled ({:?}/{:?})", + C::NAME, + context.transaction, + context.stalled_for, + context.stalled_for_limit, + ); + return Ok(context) + } + + // select priority for updated transaction + let target_priority = match context.strategy.select_target_priority(&client, &context).await? { + Some(target_priority) => target_priority, + None => { + log::trace!(target: "bridge", "Failed to select target priority"); + return Ok(context) + }, + }; + + // update transaction tip + let (is_updated, updated_transaction) = update_transaction_tip( + &client, + &transaction_params, + context.best_header.id(), + original_transaction, + context.tip_step, + context.tip_limit, + target_priority, + ) + .await?; + + if !is_updated { + log::trace!(target: "bridge", "{} transaction tip can not be updated. Reached limit?", C::NAME); + return Ok(context) + } + + let updated_transaction = updated_transaction.encode(); + let updated_transaction_hash = C::Hasher::hash(&updated_transaction); + client.submit_unsigned_extrinsic(Bytes(updated_transaction)).await?; + + log::info!( + target: "bridge", + "Replaced {} transaction {} with {} in txpool", + C::NAME, + original_transaction_hash, + updated_transaction_hash, + ); + + Ok(context.notice_resubmitted_transaction(updated_transaction_hash)) +} + +/// Search transaction pool for transaction, signed by given key pair. +async fn lookup_signer_transaction( + client: &Client, + key_pair: &AccountKeyPairOf, +) -> Result, SubstrateError> { + let pending_transactions = client.pending_extrinsics().await?; + for pending_transaction in pending_transactions { + let pending_transaction = C::SignedTransaction::decode(&mut &pending_transaction.0[..]) + .map_err(SubstrateError::ResponseParseFailed)?; + if !C::is_signed_by(key_pair, &pending_transaction) { + continue + } + + return Ok(Some(pending_transaction)) + } + + Ok(None) +} + +/// Read priority of best signed transaction of previous block. +async fn read_previous_block_best_priority( + client: &Client, + context: &Context, +) -> Result, SubstrateError> { + let best_block = client.get_block(Some(context.best_header.hash())).await?; + let best_transaction = best_block + .extrinsics() + .iter() + .filter_map(|xt| C::SignedTransaction::decode(&mut &xt[..]).ok()) + .find(|xt| C::is_signed(xt)); + match best_transaction { + Some(best_transaction) => Ok(Some( + client + .validate_transaction(*context.best_header.parent_hash(), best_transaction) + .await?? + .priority, + )), + None => Ok(None), + } +} + +/// Select priority of some queued transaction. +async fn select_priority_from_queue( + client: &Client, + context: &Context, +) -> Result, SubstrateError> { + // select transaction from the queue + let queued_transactions = client.pending_extrinsics().await?; + let selected_transaction = match select_transaction_from_queue(queued_transactions, context) { + Some(selected_transaction) => selected_transaction, + None => return Ok(None), + }; + + let selected_transaction = C::SignedTransaction::decode(&mut &selected_transaction[..]) + .map_err(SubstrateError::ResponseParseFailed)?; + let target_priority = client + .validate_transaction(context.best_header.hash(), selected_transaction) + .await?? + .priority; + Ok(Some(target_priority)) +} + +/// Select transaction with target priority from the vec of queued transactions. +fn select_transaction_from_queue( + mut queued_transactions: Vec, + context: &Context, +) -> Option { + if queued_transactions.is_empty() { + return None + } + + // the more times we resubmit transaction (`context.resubmitted`), the closer we move + // to the front of the transaction queue + let total_transactions = queued_transactions.len(); + let resubmitted_factor = context.resubmitted; + let divisor = + 1usize.saturating_add(1usize.checked_shl(resubmitted_factor).unwrap_or(usize::MAX)); + let transactions_to_skip = total_transactions / divisor; + + Some( + queued_transactions + .swap_remove(std::cmp::min(total_transactions - 1, transactions_to_skip)), + ) +} + +/// Try to find appropriate tip for transaction so that its priority is larger than given. +async fn update_transaction_tip( + client: &Client, + transaction_params: &TransactionParams>, + at_block: HeaderIdOf, + tx: C::SignedTransaction, + tip_step: C::Balance, + tip_limit: C::Balance, + target_priority: TransactionPriority, +) -> Result<(bool, C::SignedTransaction), SubstrateError> { + let stx = format!("{tx:?}"); + let mut current_priority = client.validate_transaction(at_block.1, tx.clone()).await??.priority; + let mut unsigned_tx = C::parse_transaction(tx).ok_or_else(|| { + SubstrateError::Custom(format!("Failed to parse {} transaction {stx}", C::NAME,)) + })?; + let old_tip = unsigned_tx.tip; + + let (spec_version, transaction_version) = client.simple_runtime_version().await?; + while current_priority < target_priority { + let next_tip = unsigned_tx.tip + tip_step; + if next_tip > tip_limit { + break + } + + log::trace!( + target: "bridge", + "{} transaction priority with tip={:?}: {}. Target priority: {}", + C::NAME, + unsigned_tx.tip, + current_priority, + target_priority, + ); + + unsigned_tx.tip = next_tip; + current_priority = client + .validate_transaction( + at_block.1, + C::sign_transaction( + SignParam { + spec_version, + transaction_version, + genesis_hash: *client.genesis_hash(), + signer: transaction_params.signer.clone(), + }, + unsigned_tx.clone(), + )?, + ) + .await?? + .priority; + } + + log::debug!( + target: "bridge", + "{} transaction tip has changed from {:?} to {:?}", + C::NAME, + old_tip, + unsigned_tx.tip, + ); + + Ok(( + old_tip != unsigned_tx.tip, + C::sign_transaction( + SignParam { + spec_version, + transaction_version, + genesis_hash: *client.genesis_hash(), + signer: transaction_params.signer.clone(), + }, + unsigned_tx.era(relay_substrate_client::TransactionEra::new( + at_block, + transaction_params.mortality, + )), + )?, + )) +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_rialto::Hash; + use relay_rialto_client::Rialto; + + fn context() -> Context { + Context { + strategy: PrioritySelectionStrategy::MakeItBestTransaction, + best_header: HeaderOf::::new( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ), + transaction: None, + resubmitted: 0, + stalled_for: Zero::zero(), + stalled_for_limit: 3, + tip_step: 100, + tip_limit: 1000, + } + } + + #[test] + fn context_works() { + let mut context = context(); + + // when transaction is noticed 2/3 times, it isn't stalled + context = context.notice_transaction(Default::default()); + assert!(!context.is_stalled()); + assert_eq!(context.stalled_for, 1); + assert_eq!(context.resubmitted, 0); + context = context.notice_transaction(Default::default()); + assert!(!context.is_stalled()); + assert_eq!(context.stalled_for, 2); + assert_eq!(context.resubmitted, 0); + + // when transaction is noticed for 3rd time in a row, it is considered stalled + context = context.notice_transaction(Default::default()); + assert!(context.is_stalled()); + assert_eq!(context.stalled_for, 3); + assert_eq!(context.resubmitted, 0); + + // and after we resubmit it, we forget previous transaction + context = context.notice_resubmitted_transaction(Hash::from([1; 32])); + assert_eq!(context.transaction, Some(Hash::from([1; 32]))); + assert_eq!(context.resubmitted, 1); + assert_eq!(context.stalled_for, 0); + } + + #[test] + fn select_transaction_from_queue_works_with_empty_queue() { + assert_eq!(select_transaction_from_queue(vec![], &context()), None); + } + + #[test] + fn select_transaction_from_queue_works() { + let mut context = context(); + let queued_transactions = vec![ + Bytes(vec![1]), + Bytes(vec![2]), + Bytes(vec![3]), + Bytes(vec![4]), + Bytes(vec![5]), + Bytes(vec![6]), + ]; + + // when we resubmit tx for the first time, 1/2 of queue is skipped + assert_eq!( + select_transaction_from_queue(queued_transactions.clone(), &context), + Some(Bytes(vec![4])), + ); + + // when we resubmit tx for the second time, 1/3 of queue is skipped + context = context.notice_resubmitted_transaction(Hash::from([1; 32])); + assert_eq!( + select_transaction_from_queue(queued_transactions.clone(), &context), + Some(Bytes(vec![3])), + ); + + // when we resubmit tx for the third time, 1/5 of queue is skipped + context = context.notice_resubmitted_transaction(Hash::from([2; 32])); + assert_eq!( + select_transaction_from_queue(queued_transactions.clone(), &context), + Some(Bytes(vec![2])), + ); + + // when we resubmit tx for the second time, 1/9 of queue is skipped + context = context.notice_resubmitted_transaction(Hash::from([3; 32])); + assert_eq!( + select_transaction_from_queue(queued_transactions, &context), + Some(Bytes(vec![1])), + ); + } +} diff --git a/relays/bin-substrate/src/cli/send_message.rs b/relays/bin-substrate/src/cli/send_message.rs new file mode 100644 index 00000000000..d492d3422c7 --- /dev/null +++ b/relays/bin-substrate/src/cli/send_message.rs @@ -0,0 +1,195 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{ + chains::{ + millau_headers_to_rialto::MillauToRialtoCliBridge, + millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, + }, + cli::{ + bridge::{FullBridge, MessagesCliBridge}, + chain_schema::*, + encode_message::{self, CliEncodeMessage, RawMessage}, + CliChain, + }, +}; +use async_trait::async_trait; +use codec::{Decode, Encode}; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, Chain, ChainBase, ChainWithTransactions, SignParam, + UnsignedTransaction, +}; +use sp_core::Pair; +use sp_runtime::AccountId32; +use std::fmt::Display; +use structopt::StructOpt; +use strum::VariantNames; + +/// Send bridge message. +#[derive(StructOpt)] +pub struct SendMessage { + /// A bridge instance to encode call for. + #[structopt(possible_values = FullBridge::VARIANTS, case_insensitive = true)] + bridge: FullBridge, + #[structopt(flatten)] + source: SourceConnectionParams, + #[structopt(flatten)] + source_sign: SourceSigningParams, + /// Message type. + #[structopt(subcommand)] + message: crate::cli::encode_message::Message, +} + +#[async_trait] +trait MessageSender: MessagesCliBridge +where + Self::Source: ChainBase + + ChainWithTransactions + + CliChain> + + CliEncodeMessage, + ::Balance: Display + From + Into, + ::Call: Sync, + ::SignedTransaction: Sync, + AccountIdOf: From< as Pair>::Public>, + AccountId32: From< as Pair>::Public>, +{ + async fn send_message(data: SendMessage) -> anyhow::Result<()> { + let payload = encode_message::encode_message::(&data.message)?; + + let source_client = data.source.into_client::().await?; + let source_sign = data.source_sign.to_keypair::()?; + + let payload_len = payload.encoded_size(); + let send_message_call = Self::Source::encode_send_xcm( + decode_xcm(payload)?, + data.bridge.bridge_instance_index(), + )?; + + let source_genesis_hash = *source_client.genesis_hash(); + let (spec_version, transaction_version) = source_client.simple_runtime_version().await?; + source_client + .submit_signed_extrinsic( + source_sign.public().into(), + SignParam:: { + spec_version, + transaction_version, + genesis_hash: source_genesis_hash, + signer: source_sign.clone(), + }, + move |_, transaction_nonce| { + let unsigned = UnsignedTransaction::new(send_message_call, transaction_nonce); + log::info!( + target: "bridge", + "Sending message to {}. Size: {}", + Self::Target::NAME, + payload_len, + ); + Ok(unsigned) + }, + ) + .await?; + + Ok(()) + } +} + +impl MessageSender for MillauToRialtoCliBridge {} +impl MessageSender for RialtoToMillauCliBridge {} +impl MessageSender for MillauToRialtoParachainCliBridge {} +impl MessageSender for RialtoParachainToMillauCliBridge {} + +impl SendMessage { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + match self.bridge { + FullBridge::MillauToRialto => MillauToRialtoCliBridge::send_message(self), + FullBridge::RialtoToMillau => RialtoToMillauCliBridge::send_message(self), + FullBridge::MillauToRialtoParachain => + MillauToRialtoParachainCliBridge::send_message(self), + FullBridge::RialtoParachainToMillau => + RialtoParachainToMillauCliBridge::send_message(self), + FullBridge::BridgeHubRococoToBridgeHubWococo => unimplemented!( + "Sending message from BridgeHubRococo to BridgeHubWococo is not supported" + ), + FullBridge::BridgeHubWococoToBridgeHubRococo => unimplemented!( + "Sending message from BridgeHubWococo to BridgeHubRococo is not supported" + ), + } + .await + } +} + +/// Decode SCALE encoded raw XCM message. +pub(crate) fn decode_xcm(message: RawMessage) -> anyhow::Result> { + Decode::decode(&mut &message[..]) + .map_err(|e| anyhow::format_err!("Failed to decode XCM program: {:?}", e)) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::{ExplicitOrMaximal, HexBytes}; + + #[test] + fn send_raw_rialto_to_millau() { + // given + let send_message = SendMessage::from_iter(vec![ + "send-message", + "rialto-to-millau", + "--source-port", + "1234", + "--source-signer", + "//Alice", + "raw", + "dead", + ]); + + // then + assert_eq!(send_message.bridge, FullBridge::RialtoToMillau); + assert_eq!(send_message.source.source_port, 1234); + assert_eq!(send_message.source_sign.source_signer, Some("//Alice".into())); + assert_eq!( + send_message.message, + crate::cli::encode_message::Message::Raw { data: HexBytes(vec![0xDE, 0xAD]) } + ); + } + + #[test] + fn send_sized_rialto_to_millau() { + // given + let send_message = SendMessage::from_iter(vec![ + "send-message", + "rialto-to-millau", + "--source-port", + "1234", + "--source-signer", + "//Alice", + "sized", + "max", + ]); + + // then + assert_eq!(send_message.bridge, FullBridge::RialtoToMillau); + assert_eq!(send_message.source.source_port, 1234); + assert_eq!(send_message.source_sign.source_signer, Some("//Alice".into())); + assert_eq!( + send_message.message, + crate::cli::encode_message::Message::Sized { size: ExplicitOrMaximal::Maximal } + ); + } +} diff --git a/relays/bin-substrate/src/main.rs b/relays/bin-substrate/src/main.rs new file mode 100644 index 00000000000..bc84786ee27 --- /dev/null +++ b/relays/bin-substrate/src/main.rs @@ -0,0 +1,31 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate-to-substrate relay entrypoint. + +#![warn(missing_docs)] + +mod chains; +mod cli; + +fn main() { + let command = cli::parse_args(); + let run = command.run(); + let result = async_std::task::block_on(run); + if let Err(error) = result { + log::error!(target: "bridge", "substrate-relay: {}", error); + } +} diff --git a/relays/client-bridge-hub-rococo/Cargo.toml b/relays/client-bridge-hub-rococo/Cargo.toml new file mode 100644 index 00000000000..4a01d81e777 --- /dev/null +++ b/relays/client-bridge-hub-rococo/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "relay-bridge-hub-rococo-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +relay-substrate-client = { path = "../client-substrate" } + +# Bridge dependencies + +bp-bridge-hub-rococo = { path = "../../primitives/chain-bridge-hub-rococo" } +bp-bridge-hub-wococo = { path = "../../primitives/chain-bridge-hub-wococo" } +bp-header-chain = { path = "../../primitives/header-chain" } +bp-messages = { path = "../../primitives/messages" } +bp-polkadot-core = { path = "../../primitives/polkadot-core" } +bp-runtime = { path = "../../primitives/runtime" } + +bridge-runtime-common = { path = "../../bin/runtime-common" } +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[dev-dependencies] +sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-bridge-hub-rococo/src/lib.rs b/relays/client-bridge-hub-rococo/src/lib.rs new file mode 100644 index 00000000000..1de0e26d436 --- /dev/null +++ b/relays/client-bridge-hub-rococo/src/lib.rs @@ -0,0 +1,171 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the BridgeHub-Rococo-Substrate parachain. + +use bp_messages::{MessageNonce, Weight}; +use codec::Encode; +use relay_substrate_client::{ + Chain, ChainBase, ChainWithBalances, ChainWithMessages, ChainWithTransactions, + Error as SubstrateError, SignParam, UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; +use std::time::Duration; + +/// Re-export runtime wrapper +pub mod runtime_wrapper; +pub use runtime_wrapper as runtime; + +/// Rococo chain definition +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct BridgeHubRococo; + +impl ChainBase for BridgeHubRococo { + type BlockNumber = bp_bridge_hub_rococo::BlockNumber; + type Hash = bp_bridge_hub_rococo::Hash; + type Hasher = bp_bridge_hub_rococo::Hashing; + type Header = bp_bridge_hub_rococo::Header; + + type AccountId = bp_bridge_hub_rococo::AccountId; + type Balance = bp_bridge_hub_rococo::Balance; + type Index = bp_bridge_hub_rococo::Nonce; + type Signature = bp_bridge_hub_rococo::Signature; + + fn max_extrinsic_size() -> u32 { + bp_bridge_hub_rococo::BridgeHubRococo::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_bridge_hub_rococo::BridgeHubRococo::max_extrinsic_weight() + } +} + +impl Chain for BridgeHubRococo { + const NAME: &'static str = "BridgeHubRococo"; + const TOKEN_ID: Option<&'static str> = None; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_bridge_hub_rococo::BEST_FINALIZED_BRIDGE_HUB_ROCOCO_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type SignedBlock = bp_bridge_hub_rococo::SignedBlock; + type Call = runtime::Call; +} + +impl ChainWithBalances for BridgeHubRococo { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + bp_bridge_hub_rococo::AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +impl ChainWithTransactions for BridgeHubRococo { + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = runtime::UncheckedExtrinsic; + + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result { + let raw_payload = SignedPayload::new( + unsigned.call, + bp_bridge_hub_rococo::SignedExtensions::new( + param.spec_version, + param.transaction_version, + unsigned.era, + param.genesis_hash, + unsigned.nonce, + unsigned.tip, + ), + )?; + + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(bp_bridge_hub_rococo::UncheckedExtrinsic::new_signed( + call, + signer.into_account().into(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| { + *address == bp_bridge_hub_rococo::Address::Id(signer.public().into()) + }) + .unwrap_or(false) + } + + fn parse_transaction(tx: Self::SignedTransaction) -> Option> { + let extra = &tx.signature.as_ref()?.2; + Some(UnsignedTransaction::new(tx.function, extra.nonce()).tip(extra.tip())) + } +} + +impl ChainWithMessages for BridgeHubRococo { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME; + + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_rococo::TO_BRIDGE_HUB_ROCOCO_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_rococo::FROM_BRIDGE_HUB_ROCOCO_MESSAGE_DETAILS_METHOD; + + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + + type WeightToFee = bp_bridge_hub_rococo::WeightToFee; + // TODO: fix (https://github.com/paritytech/parity-bridges-common/issues/1640) + type WeightInfo = (); +} + +#[cfg(test)] +mod tests { + use super::*; + use relay_substrate_client::TransactionEra; + + #[test] + fn parse_transaction_works() { + let unsigned = UnsignedTransaction { + call: runtime::Call::System(runtime::SystemCall::remark(b"Hello world!".to_vec())) + .into(), + nonce: 777, + tip: 888, + era: TransactionEra::immortal(), + }; + let signed_transaction = BridgeHubRococo::sign_transaction( + SignParam { + spec_version: 42, + transaction_version: 50000, + genesis_hash: [42u8; 32].into(), + signer: sp_core::sr25519::Pair::from_seed_slice(&[1u8; 32]).unwrap(), + }, + unsigned.clone(), + ) + .unwrap(); + let parsed_transaction = BridgeHubRococo::parse_transaction(signed_transaction).unwrap(); + assert_eq!(parsed_transaction, unsigned); + } +} diff --git a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs new file mode 100644 index 00000000000..6f83257cf4d --- /dev/null +++ b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs @@ -0,0 +1,219 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +// TODO: join with primitives do we need this here or move to the primitives? + +//! Types that are specific to the BridgeHubRococo runtime. + +use bp_polkadot_core::PolkadotLike; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; + +pub use bp_bridge_hub_rococo::SS58Prefix; +use bp_messages::UnrewardedRelayersState; +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use bp_runtime::Chain; + +// TODO:check-parameter - check SignedExtension +/// Unchecked BridgeHubRococo extrinsic. +pub type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic; + +/// Rococo Runtime `Call` enum. +/// +/// The enum represents a subset of possible `Call`s we can send to Rococo chain. +/// Ideally this code would be auto-generated from metadata, because we want to +/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. +/// +/// All entries here (like pretty much in the entire file) must be kept in sync with Rococo +/// `construct_runtime`, so that we maintain SCALE-compatibility. +/// +/// // TODO:check-parameter -> change bko-bridge-rococo-wococo when merged to master in cumulus +/// See: [link](https://github.com/paritytech/cumulus/blob/bko-bridge-rococo-wococo/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs) +#[allow(clippy::large_enum_variant)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum Call { + /// System pallet. + #[codec(index = 0)] + System(SystemCall), + /// Wococo bridge pallet. + #[codec(index = 41)] + BridgeWococoGrandpa(BridgeWococoGrandpaCall), + /// Rococo bridge pallet. + #[codec(index = 43)] + BridgeRococoGrandpa(BridgeRococoGrandpaCall), + + /// Wococo parachain bridge pallet. + #[codec(index = 42)] + BridgeWococoParachain(BridgeParachainCall), + /// Rococo parachain bridge pallet. + #[codec(index = 44)] + BridgeRococoParachain(BridgeParachainCall), + + /// Wococo messages bridge pallet. + #[codec(index = 46)] + BridgeWococoMessages(BridgeWococoMessagesCall), + /// Rococo messages bridge pallet. + #[codec(index = 45)] + BridgeRococoMessages(BridgeRococoMessagesCall), +} + +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum SystemCall { + #[codec(index = 1)] + remark(Vec), +} + +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeWococoGrandpaCall { + #[codec(index = 0)] + submit_finality_proof( + Box<::Header>, + bp_header_chain::justification::GrandpaJustification<::Header>, + ), + #[codec(index = 1)] + initialize(bp_header_chain::InitializationData<::Header>), +} + +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeRococoGrandpaCall { + #[codec(index = 0)] + submit_finality_proof( + Box<::Header>, + bp_header_chain::justification::GrandpaJustification<::Header>, + ), + #[codec(index = 1)] + initialize(bp_header_chain::InitializationData<::Header>), +} + +pub type RelayBlockHash = bp_polkadot_core::Hash; +pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; + +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeParachainCall { + #[codec(index = 0)] + submit_parachain_heads( + (RelayBlockNumber, RelayBlockHash), + Vec<(ParaId, ParaHash)>, + ParaHeadsProof, + ), +} + +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeWococoMessagesCall { + #[codec(index = 2)] + receive_messages_proof( + relay_substrate_client::AccountIdOf, + bridge_runtime_common::messages::target::FromBridgedChainMessagesProof< + relay_substrate_client::HashOf, + >, + u32, + bp_messages::Weight, + ), + + #[codec(index = 3)] + receive_messages_delivery_proof( + bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof< + relay_substrate_client::HashOf, + >, + UnrewardedRelayersState, + ), +} + +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeRococoMessagesCall { + #[codec(index = 2)] + receive_messages_proof( + relay_substrate_client::AccountIdOf, + bridge_runtime_common::messages::target::FromBridgedChainMessagesProof< + relay_substrate_client::HashOf, + >, + u32, + bp_messages::Weight, + ), + + #[codec(index = 3)] + receive_messages_delivery_proof( + bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof< + relay_substrate_client::HashOf, + >, + UnrewardedRelayersState, + ), +} + +impl sp_runtime::traits::Dispatchable for Call { + type RuntimeOrigin = (); + type Config = (); + type Info = (); + type PostInfo = (); + + fn dispatch( + self, + _origin: Self::RuntimeOrigin, + ) -> sp_runtime::DispatchResultWithInfo { + unimplemented!("The Call is not expected to be dispatched.") + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_runtime::BasicOperatingMode; + use sp_core::hexdisplay::HexDisplay; + use sp_finality_grandpa::AuthorityList; + use sp_runtime::traits::Header; + use std::str::FromStr; + + pub type RelayBlockHasher = bp_polkadot_core::Hasher; + pub type RelayBlockHeader = sp_runtime::generic::Header; + + #[test] + fn encode_decode_calls() { + let header = RelayBlockHeader::new( + 75, + bp_polkadot_core::Hash::from_str( + "0xd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d", + ) + .expect("invalid value"), + bp_polkadot_core::Hash::from_str( + "0x92b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141b", + ) + .expect("invalid value"), + bp_polkadot_core::Hash::from_str( + "0xae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d", + ) + .expect("invalid value"), + Default::default(), + ); + let init_data = bp_header_chain::InitializationData { + header: Box::new(header), + authority_list: AuthorityList::default(), + set_id: 6, + operating_mode: BasicOperatingMode::Normal, + }; + let call = BridgeRococoGrandpaCall::initialize(init_data); + let tx = Call::BridgeRococoGrandpa(call); + + // encode call as hex string + let hex_encoded_call = format!("0x{:?}", HexDisplay::from(&Encode::encode(&tx))); + assert_eq!(hex_encoded_call, "0x2b01ae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d2d0192b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141bd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d0000060000000000000000"); + } +} diff --git a/relays/client-bridge-hub-wococo/Cargo.toml b/relays/client-bridge-hub-wococo/Cargo.toml new file mode 100644 index 00000000000..fa28dfde202 --- /dev/null +++ b/relays/client-bridge-hub-wococo/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "relay-bridge-hub-wococo-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +relay-substrate-client = { path = "../client-substrate" } + +# Bridge dependencies + +bp-bridge-hub-wococo = { path = "../../primitives/chain-bridge-hub-wococo" } +bp-messages = { path = "../../primitives/messages" } +relay-bridge-hub-rococo-client = { path = "../client-bridge-hub-rococo" } + +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-bridge-hub-wococo/src/lib.rs b/relays/client-bridge-hub-wococo/src/lib.rs new file mode 100644 index 00000000000..46bac2e826e --- /dev/null +++ b/relays/client-bridge-hub-wococo/src/lib.rs @@ -0,0 +1,171 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the BridgeHub-Wococo-Substrate parachain. + +use bp_messages::{MessageNonce, Weight}; +use codec::Encode; +use relay_substrate_client::{ + Chain, ChainBase, ChainWithBalances, ChainWithMessages, ChainWithTransactions, + Error as SubstrateError, SignParam, UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; +use std::time::Duration; + +/// Re-export runtime wrapper +pub mod runtime_wrapper; +pub use runtime_wrapper as runtime; + +/// Wococo chain definition +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct BridgeHubWococo; + +impl ChainBase for BridgeHubWococo { + type BlockNumber = bp_bridge_hub_wococo::BlockNumber; + type Hash = bp_bridge_hub_wococo::Hash; + type Hasher = bp_bridge_hub_wococo::Hashing; + type Header = bp_bridge_hub_wococo::Header; + + type AccountId = bp_bridge_hub_wococo::AccountId; + type Balance = bp_bridge_hub_wococo::Balance; + type Index = bp_bridge_hub_wococo::Nonce; + type Signature = bp_bridge_hub_wococo::Signature; + + fn max_extrinsic_size() -> u32 { + bp_bridge_hub_wococo::BridgeHubWococo::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_bridge_hub_wococo::BridgeHubWococo::max_extrinsic_weight() + } +} + +impl Chain for BridgeHubWococo { + const NAME: &'static str = "BridgeHubWococo"; + const TOKEN_ID: Option<&'static str> = None; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_bridge_hub_wococo::BEST_FINALIZED_BRIDGE_HUB_WOCOCO_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type SignedBlock = bp_bridge_hub_wococo::SignedBlock; + type Call = runtime::Call; +} + +impl ChainWithBalances for BridgeHubWococo { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + bp_bridge_hub_wococo::AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +impl ChainWithTransactions for BridgeHubWococo { + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = runtime::UncheckedExtrinsic; + + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result { + let raw_payload = SignedPayload::new( + unsigned.call, + bp_bridge_hub_wococo::SignedExtensions::new( + param.spec_version, + param.transaction_version, + unsigned.era, + param.genesis_hash, + unsigned.nonce, + unsigned.tip, + ), + )?; + + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(bp_bridge_hub_wococo::UncheckedExtrinsic::new_signed( + call, + signer.into_account().into(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| { + *address == bp_bridge_hub_wococo::Address::Id(signer.public().into()) + }) + .unwrap_or(false) + } + + fn parse_transaction(tx: Self::SignedTransaction) -> Option> { + let extra = &tx.signature.as_ref()?.2; + Some(UnsignedTransaction::new(tx.function, extra.nonce()).tip(extra.tip())) + } +} + +impl ChainWithMessages for BridgeHubWococo { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_bridge_hub_wococo::WITH_BRIDGE_HUB_WOCOCO_MESSAGES_PALLET_NAME; + + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_wococo::TO_BRIDGE_HUB_WOCOCO_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_wococo::FROM_BRIDGE_HUB_WOCOCO_MESSAGE_DETAILS_METHOD; + + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_wococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_wococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + + type WeightToFee = bp_bridge_hub_wococo::WeightToFee; + // TODO: fix (https://github.com/paritytech/parity-bridges-common/issues/1640) + type WeightInfo = (); +} + +#[cfg(test)] +mod tests { + use super::*; + use relay_substrate_client::TransactionEra; + + #[test] + fn parse_transaction_works() { + let unsigned = UnsignedTransaction { + call: runtime::Call::System(runtime::SystemCall::remark(b"Hello world!".to_vec())) + .into(), + nonce: 777, + tip: 888, + era: TransactionEra::immortal(), + }; + let signed_transaction = BridgeHubWococo::sign_transaction( + SignParam { + spec_version: 42, + transaction_version: 50000, + genesis_hash: [42u8; 32].into(), + signer: sp_core::sr25519::Pair::from_seed_slice(&[1u8; 32]).unwrap(), + }, + unsigned.clone(), + ) + .unwrap(); + let parsed_transaction = BridgeHubWococo::parse_transaction(signed_transaction).unwrap(); + assert_eq!(parsed_transaction, unsigned); + } +} diff --git a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs new file mode 100644 index 00000000000..8d94e7f9026 --- /dev/null +++ b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs @@ -0,0 +1,28 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types that are specific to the BridgeHubWococo runtime. + +pub use bp_bridge_hub_wococo::SS58Prefix; + +// We reuse everything from rococo runtime wrapper +pub type Call = relay_bridge_hub_rococo_client::runtime::Call; +pub type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic; +pub type BridgeGrandpaRococoCall = relay_bridge_hub_rococo_client::runtime::BridgeRococoGrandpaCall; +pub type BridgeParachainCall = relay_bridge_hub_rococo_client::runtime::BridgeParachainCall; +pub type BridgeRococoMessagesCall = + relay_bridge_hub_rococo_client::runtime::BridgeRococoMessagesCall; +pub type SystemCall = relay_bridge_hub_rococo_client::runtime::SystemCall; diff --git a/relays/client-kusama/Cargo.toml b/relays/client-kusama/Cargo.toml new file mode 100644 index 00000000000..2efe62d8ec8 --- /dev/null +++ b/relays/client-kusama/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "relay-kusama-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-kusama = { path = "../../primitives/chain-kusama" } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-kusama/src/lib.rs b/relays/client-kusama/src/lib.rs new file mode 100644 index 00000000000..25d7bb76e59 --- /dev/null +++ b/relays/client-kusama/src/lib.rs @@ -0,0 +1,74 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Kusama chain. + +use bp_kusama::AccountInfoStorageMapKeyProvider; +use frame_support::weights::Weight; +use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, ChainWithGrandpa}; +use sp_core::storage::StorageKey; +use std::time::Duration; + +/// Kusama header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Kusama chain definition +#[derive(Debug, Clone, Copy)] +pub struct Kusama; + +impl ChainBase for Kusama { + type BlockNumber = bp_kusama::BlockNumber; + type Hash = bp_kusama::Hash; + type Hasher = bp_kusama::Hasher; + type Header = bp_kusama::Header; + + type AccountId = bp_kusama::AccountId; + type Balance = bp_kusama::Balance; + type Index = bp_kusama::Nonce; + type Signature = bp_kusama::Signature; + + fn max_extrinsic_size() -> u32 { + bp_kusama::Kusama::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_kusama::Kusama::max_extrinsic_weight() + } +} + +impl Chain for Kusama { + const NAME: &'static str = "Kusama"; + const TOKEN_ID: Option<&'static str> = Some("kusama"); + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type SignedBlock = bp_kusama::SignedBlock; + type Call = (); +} + +impl ChainWithGrandpa for Kusama { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_kusama::WITH_KUSAMA_GRANDPA_PALLET_NAME; +} + +impl ChainWithBalances for Kusama { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +/// Kusama header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; diff --git a/relays/client-millau/Cargo.toml b/relays/client-millau/Cargo.toml new file mode 100644 index 00000000000..66395d5de89 --- /dev/null +++ b/relays/client-millau/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "relay-millau-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5" } +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Supported Chains + +bp-messages = { path = "../../primitives/messages" } +bp-millau = { path = "../../primitives/chain-millau" } +millau-runtime = { path = "../../bin/millau/runtime" } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-millau/src/lib.rs b/relays/client-millau/src/lib.rs new file mode 100644 index 00000000000..f8b350d3d0c --- /dev/null +++ b/relays/client-millau/src/lib.rs @@ -0,0 +1,202 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Millau-Substrate chain. + +use bp_messages::MessageNonce; +use codec::{Compact, Decode, Encode}; +use frame_support::weights::Weight; +use relay_substrate_client::{ + BalanceOf, Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, + ChainWithTransactions, Error as SubstrateError, IndexOf, SignParam, UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; +use std::time::Duration; + +/// Millau header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Millau chain definition. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Millau; + +impl ChainBase for Millau { + type BlockNumber = millau_runtime::BlockNumber; + type Hash = millau_runtime::Hash; + type Hasher = millau_runtime::Hashing; + type Header = millau_runtime::Header; + + type AccountId = millau_runtime::AccountId; + type Balance = millau_runtime::Balance; + type Index = millau_runtime::Index; + type Signature = millau_runtime::Signature; + + fn max_extrinsic_size() -> u32 { + bp_millau::Millau::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_millau::Millau::max_extrinsic_weight() + } +} + +impl ChainWithGrandpa for Millau { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_GRANDPA_PALLET_NAME; +} + +impl ChainWithMessages for Millau { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME; + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_millau::TO_MILLAU_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_millau::FROM_MILLAU_MESSAGE_DETAILS_METHOD; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + type WeightToFee = bp_millau::WeightToFee; + type WeightInfo = (); +} + +impl Chain for Millau { + const NAME: &'static str = "Millau"; + // Rialto token has no value, but we associate it with KSM token + const TOKEN_ID: Option<&'static str> = Some("kusama"); + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); + + type SignedBlock = millau_runtime::SignedBlock; + type Call = millau_runtime::RuntimeCall; +} + +impl ChainWithBalances for Millau { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + use frame_support::storage::generator::StorageMap; + StorageKey(frame_system::Account::::storage_map_final_key( + account_id, + )) + } +} + +impl ChainWithTransactions for Millau { + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = millau_runtime::UncheckedExtrinsic; + + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result { + let raw_payload = SignedPayload::from_raw( + unsigned.call.clone(), + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(unsigned.era.frame_era()), + frame_system::CheckNonce::::from(unsigned.nonce), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(unsigned.tip), + millau_runtime::BridgeRejectObsoleteHeadersAndMessages, + ), + ( + (), + param.spec_version, + param.transaction_version, + param.genesis_hash, + unsigned.era.signed_payload(param.genesis_hash), + (), + (), + (), + (), + ), + ); + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(millau_runtime::UncheckedExtrinsic::new_signed( + call.into_decoded()?, + signer.into_account(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| { + *address == millau_runtime::Address::from(*signer.public().as_array_ref()) + }) + .unwrap_or(false) + } + + fn parse_transaction(tx: Self::SignedTransaction) -> Option> { + let extra = &tx.signature.as_ref()?.2; + Some( + UnsignedTransaction::new( + tx.function.into(), + Compact::>::decode(&mut &extra.5.encode()[..]).ok()?.into(), + ) + .tip(Compact::>::decode(&mut &extra.7.encode()[..]).ok()?.into()), + ) + } +} + +/// Millau signing params. +pub type SigningParams = sp_core::sr25519::Pair; + +/// Millau header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + +#[cfg(test)] +mod tests { + use super::*; + use relay_substrate_client::TransactionEra; + + #[test] + fn parse_transaction_works() { + let unsigned = UnsignedTransaction { + call: millau_runtime::RuntimeCall::System(millau_runtime::SystemCall::remark { + remark: b"Hello world!".to_vec(), + }) + .into(), + nonce: 777, + tip: 888, + era: TransactionEra::immortal(), + }; + let signed_transaction = Millau::sign_transaction( + SignParam { + spec_version: 42, + transaction_version: 50000, + genesis_hash: [42u8; 64].into(), + signer: sp_core::sr25519::Pair::from_seed_slice(&[1u8; 32]).unwrap(), + }, + unsigned.clone(), + ) + .unwrap(); + let parsed_transaction = Millau::parse_transaction(signed_transaction).unwrap(); + assert_eq!(parsed_transaction, unsigned); + } +} diff --git a/relays/client-polkadot/Cargo.toml b/relays/client-polkadot/Cargo.toml new file mode 100644 index 00000000000..aefbadfdd18 --- /dev/null +++ b/relays/client-polkadot/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "relay-polkadot-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-polkadot = { path = "../../primitives/chain-polkadot" } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-polkadot/src/lib.rs b/relays/client-polkadot/src/lib.rs new file mode 100644 index 00000000000..8bba686cfe9 --- /dev/null +++ b/relays/client-polkadot/src/lib.rs @@ -0,0 +1,75 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Polkadot chain. + +use bp_polkadot::AccountInfoStorageMapKeyProvider; +use frame_support::weights::Weight; +use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, ChainWithGrandpa}; +use sp_core::storage::StorageKey; +use std::time::Duration; + +/// Polkadot header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Polkadot chain definition +#[derive(Debug, Clone, Copy)] +pub struct Polkadot; + +impl ChainBase for Polkadot { + type BlockNumber = bp_polkadot::BlockNumber; + type Hash = bp_polkadot::Hash; + type Hasher = bp_polkadot::Hasher; + type Header = bp_polkadot::Header; + + type AccountId = bp_polkadot::AccountId; + type Balance = bp_polkadot::Balance; + type Index = bp_polkadot::Nonce; + type Signature = bp_polkadot::Signature; + + fn max_extrinsic_size() -> u32 { + bp_polkadot::Polkadot::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_polkadot::Polkadot::max_extrinsic_weight() + } +} + +impl Chain for Polkadot { + const NAME: &'static str = "Polkadot"; + const TOKEN_ID: Option<&'static str> = Some("polkadot"); + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type SignedBlock = bp_polkadot::SignedBlock; + type Call = (); +} + +impl ChainWithGrandpa for Polkadot { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = + bp_polkadot::WITH_POLKADOT_GRANDPA_PALLET_NAME; +} + +impl ChainWithBalances for Polkadot { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +/// Polkadot header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; diff --git a/relays/client-rialto-parachain/Cargo.toml b/relays/client-rialto-parachain/Cargo.toml new file mode 100644 index 00000000000..915d0e786cc --- /dev/null +++ b/relays/client-rialto-parachain/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "relay-rialto-parachain-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5" } +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-messages = { path = "../../primitives/messages" } +bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" } +rialto-parachain-runtime = { path = "../../bin/rialto-parachain/runtime" } + +# Substrate Dependencies + +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-rialto-parachain/src/lib.rs b/relays/client-rialto-parachain/src/lib.rs new file mode 100644 index 00000000000..8cf74bdf9b1 --- /dev/null +++ b/relays/client-rialto-parachain/src/lib.rs @@ -0,0 +1,165 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Rialto-Substrate chain. + +use bp_messages::MessageNonce; +use codec::Encode; +use frame_support::weights::Weight; +use relay_substrate_client::{ + Chain, ChainBase, ChainWithBalances, ChainWithMessages, ChainWithTransactions, + Error as SubstrateError, SignParam, UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; +use std::time::Duration; + +/// Rialto header id. +pub type HeaderId = + relay_utils::HeaderId; + +/// Rialto parachain definition +#[derive(Debug, Clone, Copy)] +pub struct RialtoParachain; + +impl ChainBase for RialtoParachain { + type BlockNumber = rialto_parachain_runtime::BlockNumber; + type Hash = rialto_parachain_runtime::Hash; + type Hasher = rialto_parachain_runtime::Hashing; + type Header = rialto_parachain_runtime::Header; + + type AccountId = rialto_parachain_runtime::AccountId; + type Balance = rialto_parachain_runtime::Balance; + type Index = rialto_parachain_runtime::Index; + type Signature = rialto_parachain_runtime::Signature; + + fn max_extrinsic_size() -> u32 { + bp_rialto_parachain::RialtoParachain::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_rialto_parachain::RialtoParachain::max_extrinsic_weight() + } +} + +impl Chain for RialtoParachain { + const NAME: &'static str = "RialtoParachain"; + // RialtoParachain token has no value, but we associate it with DOT token + const TOKEN_ID: Option<&'static str> = Some("polkadot"); + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_rialto_parachain::BEST_FINALIZED_RIALTO_PARACHAIN_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); + + type SignedBlock = rialto_parachain_runtime::SignedBlock; + type Call = rialto_parachain_runtime::RuntimeCall; +} + +impl ChainWithBalances for RialtoParachain { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + use frame_support::storage::generator::StorageMap; + StorageKey( + frame_system::Account::::storage_map_final_key( + account_id, + ), + ) + } +} + +impl ChainWithMessages for RialtoParachain { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_rialto_parachain::WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME; + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_rialto_parachain::TO_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_rialto_parachain::FROM_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_rialto_parachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_rialto_parachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + type WeightToFee = bp_rialto_parachain::WeightToFee; + type WeightInfo = (); +} + +impl ChainWithTransactions for RialtoParachain { + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = rialto_parachain_runtime::UncheckedExtrinsic; + + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result { + let raw_payload = SignedPayload::from_raw( + unsigned.call, + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from( + unsigned.era.frame_era(), + ), + frame_system::CheckNonce::::from(unsigned.nonce), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::< + rialto_parachain_runtime::Runtime, + >::from(unsigned.tip), + ), + ( + (), + param.spec_version, + param.transaction_version, + param.genesis_hash, + unsigned.era.signed_payload(param.genesis_hash), + (), + (), + (), + ), + ); + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(rialto_parachain_runtime::UncheckedExtrinsic::new_signed( + call.into_decoded()?, + signer.into_account().into(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| { + *address == rialto_parachain_runtime::Address::Id(signer.public().into()) + }) + .unwrap_or(false) + } + + fn parse_transaction(_tx: Self::SignedTransaction) -> Option> { + None + } +} + +/// RialtoParachain signing params. +pub type SigningParams = sp_core::sr25519::Pair; + +/// RialtoParachain header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; diff --git a/relays/client-rialto/Cargo.toml b/relays/client-rialto/Cargo.toml new file mode 100644 index 00000000000..ef339feb8a5 --- /dev/null +++ b/relays/client-rialto/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "relay-rialto-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5" } +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-messages = { path = "../../primitives/messages" } +bp-rialto = { path = "../../primitives/chain-rialto" } +rialto-runtime = { path = "../../bin/rialto/runtime" } + +# Substrate Dependencies + +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-rialto/src/lib.rs b/relays/client-rialto/src/lib.rs new file mode 100644 index 00000000000..23ecbbd47f6 --- /dev/null +++ b/relays/client-rialto/src/lib.rs @@ -0,0 +1,205 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Rialto-Substrate chain. + +use bp_messages::MessageNonce; +use codec::{Compact, Decode, Encode}; +use frame_support::weights::Weight; +use relay_substrate_client::{ + BalanceOf, Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, + ChainWithTransactions, Error as SubstrateError, IndexOf, RelayChain, SignParam, + UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; +use std::time::Duration; + +/// Rialto header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Rialto chain definition +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Rialto; + +impl ChainBase for Rialto { + type BlockNumber = rialto_runtime::BlockNumber; + type Hash = rialto_runtime::Hash; + type Hasher = rialto_runtime::Hashing; + type Header = rialto_runtime::Header; + + type AccountId = rialto_runtime::AccountId; + type Balance = rialto_runtime::Balance; + type Index = rialto_runtime::Index; + type Signature = rialto_runtime::Signature; + + fn max_extrinsic_size() -> u32 { + bp_rialto::Rialto::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_rialto::Rialto::max_extrinsic_weight() + } +} + +impl Chain for Rialto { + const NAME: &'static str = "Rialto"; + // Rialto token has no value, but we associate it with DOT token + const TOKEN_ID: Option<&'static str> = Some("polkadot"); + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); + + type SignedBlock = rialto_runtime::SignedBlock; + type Call = rialto_runtime::RuntimeCall; +} + +impl RelayChain for Rialto { + const PARAS_PALLET_NAME: &'static str = bp_rialto::PARAS_PALLET_NAME; + const PARACHAINS_FINALITY_PALLET_NAME: &'static str = + bp_rialto::WITH_RIALTO_BRIDGE_PARAS_PALLET_NAME; +} + +impl ChainWithGrandpa for Rialto { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_rialto::WITH_RIALTO_GRANDPA_PALLET_NAME; +} + +impl ChainWithMessages for Rialto { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME; + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_rialto::TO_RIALTO_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_rialto::FROM_RIALTO_MESSAGE_DETAILS_METHOD; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + type WeightToFee = bp_rialto::WeightToFee; + type WeightInfo = (); +} + +impl ChainWithBalances for Rialto { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + use frame_support::storage::generator::StorageMap; + StorageKey(frame_system::Account::::storage_map_final_key( + account_id, + )) + } +} + +impl ChainWithTransactions for Rialto { + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = rialto_runtime::UncheckedExtrinsic; + + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result { + let raw_payload = SignedPayload::from_raw( + unsigned.call.clone(), + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(unsigned.era.frame_era()), + frame_system::CheckNonce::::from(unsigned.nonce), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(unsigned.tip), + ), + ( + (), + param.spec_version, + param.transaction_version, + param.genesis_hash, + unsigned.era.signed_payload(param.genesis_hash), + (), + (), + (), + ), + ); + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(rialto_runtime::UncheckedExtrinsic::new_signed( + call.into_decoded()?, + signer.into_account().into(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| *address == rialto_runtime::Address::Id(signer.public().into())) + .unwrap_or(false) + } + + fn parse_transaction(tx: Self::SignedTransaction) -> Option> { + let extra = &tx.signature.as_ref()?.2; + Some( + UnsignedTransaction::new( + tx.function.into(), + Compact::>::decode(&mut &extra.5.encode()[..]).ok()?.into(), + ) + .tip(Compact::>::decode(&mut &extra.7.encode()[..]).ok()?.into()), + ) + } +} + +/// Rialto signing params. +pub type SigningParams = sp_core::sr25519::Pair; + +/// Rialto header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + +#[cfg(test)] +mod tests { + use super::*; + use relay_substrate_client::TransactionEra; + + #[test] + fn parse_transaction_works() { + let unsigned = UnsignedTransaction { + call: rialto_runtime::RuntimeCall::System(rialto_runtime::SystemCall::remark { + remark: b"Hello world!".to_vec(), + }) + .into(), + nonce: 777, + tip: 888, + era: TransactionEra::immortal(), + }; + let signed_transaction = Rialto::sign_transaction( + SignParam { + spec_version: 42, + transaction_version: 50000, + genesis_hash: [42u8; 32].into(), + signer: sp_core::sr25519::Pair::from_seed_slice(&[1u8; 32]).unwrap(), + }, + unsigned.clone(), + ) + .unwrap(); + let parsed_transaction = Rialto::parse_transaction(signed_transaction).unwrap(); + assert_eq!(parsed_transaction, unsigned); + } +} diff --git a/relays/client-rococo/Cargo.toml b/relays/client-rococo/Cargo.toml new file mode 100644 index 00000000000..14d3c8ca232 --- /dev/null +++ b/relays/client-rococo/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "relay-rococo-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-rococo = { path = "../../primitives/chain-rococo" } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-rococo/src/lib.rs b/relays/client-rococo/src/lib.rs new file mode 100644 index 00000000000..38a95042ff3 --- /dev/null +++ b/relays/client-rococo/src/lib.rs @@ -0,0 +1,78 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Rococo-Substrate chain. + +use frame_support::weights::Weight; +use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, RelayChain}; +use sp_core::storage::StorageKey; +use std::time::Duration; + +/// Rococo header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Rococo header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + +/// Rococo chain definition +#[derive(Debug, Clone, Copy)] +pub struct Rococo; + +impl ChainBase for Rococo { + type BlockNumber = bp_rococo::BlockNumber; + type Hash = bp_rococo::Hash; + type Hasher = bp_rococo::Hashing; + type Header = bp_rococo::Header; + + type AccountId = bp_rococo::AccountId; + type Balance = bp_rococo::Balance; + type Index = bp_rococo::Nonce; + type Signature = bp_rococo::Signature; + + fn max_extrinsic_size() -> u32 { + bp_rococo::Rococo::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_rococo::Rococo::max_extrinsic_weight() + } +} + +impl Chain for Rococo { + const NAME: &'static str = "Rococo"; + const TOKEN_ID: Option<&'static str> = None; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type SignedBlock = bp_rococo::SignedBlock; + type Call = (); +} + +impl ChainWithGrandpa for Rococo { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_rococo::WITH_ROCOCO_GRANDPA_PALLET_NAME; +} + +impl ChainWithBalances for Rococo { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + bp_rococo::AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +impl RelayChain for Rococo { + const PARAS_PALLET_NAME: &'static str = bp_rococo::PARAS_PALLET_NAME; + const PARACHAINS_FINALITY_PALLET_NAME: &'static str = "bridgeRococoParachain"; +} diff --git a/relays/client-substrate/Cargo.toml b/relays/client-substrate/Cargo.toml new file mode 100644 index 00000000000..60ef1c67a26 --- /dev/null +++ b/relays/client-substrate/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "relay-substrate-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +async-std = { version = "1.6.5", features = ["attributes"] } +async-trait = "0.1" +codec = { package = "parity-scale-codec", version = "3.1.5" } +futures = "0.3.7" +jsonrpsee = { version = "0.15", features = ["macros", "ws-client"] } +log = "0.4.17" +num-traits = "0.2" +rand = "0.7" +tokio = { version = "1.8", features = ["rt-multi-thread"] } +thiserror = "1.0.26" + +# Bridge dependencies + +bp-header-chain = { path = "../../primitives/header-chain" } +bp-messages = { path = "../../primitives/messages" } +bp-runtime = { path = "../../primitives/runtime" } +pallet-bridge-messages = { path = "../../modules/messages" } +finality-relay = { path = "../finality" } +relay-utils = { path = "../utils" } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-chain-spec = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-rpc-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-transaction-pool-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-storage = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-version = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = [] +test-helpers = [] diff --git a/relays/client-substrate/src/chain.rs b/relays/client-substrate/src/chain.rs new file mode 100644 index 00000000000..0e19cf92b77 --- /dev/null +++ b/relays/client-substrate/src/chain.rs @@ -0,0 +1,235 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use bp_messages::MessageNonce; +use bp_runtime::{ + Chain as ChainBase, EncodedOrDecodedCall, HashOf, TransactionEra, TransactionEraOf, +}; +use codec::{Codec, Encode}; +use frame_support::weights::WeightToFee; +use jsonrpsee::core::{DeserializeOwned, Serialize}; +use num_traits::Zero; +use sc_transaction_pool_api::TransactionStatus; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{ + generic::SignedBlock, + traits::{Block as BlockT, Dispatchable, Member}, + ConsensusEngineId, EncodedJustification, +}; +use std::{fmt::Debug, time::Duration}; + +/// Substrate-based chain from minimal relay-client point of view. +pub trait Chain: ChainBase + Clone { + /// Chain name. + const NAME: &'static str; + /// Identifier of the basic token of the chain (if applicable). + /// + /// This identifier is used to fetch token price. In case of testnets, you may either + /// set it to `None`, or associate testnet with one of the existing tokens. + const TOKEN_ID: Option<&'static str>; + /// Name of the runtime API method that is returning best known finalized header number + /// and hash (as tuple). + /// + /// Keep in mind that this method is normally provided by the other chain, which is + /// bridged with this chain. + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str; + + /// Average block interval. + /// + /// How often blocks are produced on that chain. It's suggested to set this value + /// to match the block time of the chain. + const AVERAGE_BLOCK_INTERVAL: Duration; + + /// Block type. + type SignedBlock: Member + Serialize + DeserializeOwned + BlockWithJustification; + /// The aggregated `Call` type. + type Call: Clone + Codec + Dispatchable + Debug + Send; +} + +/// Substrate-based relay chain that supports parachains. +/// +/// We assume that the parachains are supported using `runtime_parachains::paras` pallet. +pub trait RelayChain: Chain { + /// Name of the `runtime_parachains::paras` pallet in the runtime of this chain. + const PARAS_PALLET_NAME: &'static str; + /// Name of the bridge parachains pallet (used in `construct_runtime` macro call) that is + /// deployed at the **bridged** chain. + /// + /// We assume that all chains that are bridging with this `ChainWithGrandpa` are using + /// the same name. + const PARACHAINS_FINALITY_PALLET_NAME: &'static str; +} + +/// Substrate-based chain that is using direct GRANDPA finality from minimal relay-client point of +/// view. +/// +/// Keep in mind that parachains are relying on relay chain GRANDPA, so they should not implement +/// this trait. +pub trait ChainWithGrandpa: Chain { + /// Name of the bridge GRANDPA pallet (used in `construct_runtime` macro call) that is deployed + /// at some other chain to bridge with this `ChainWithGrandpa`. + /// + /// We assume that all chains that are bridging with this `ChainWithGrandpa` are using + /// the same name. + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str; +} + +/// Substrate-based chain with messaging support from minimal relay-client point of view. +pub trait ChainWithMessages: Chain { + /// Name of the bridge messages pallet (used in `construct_runtime` macro call) that is deployed + /// at some other chain to bridge with this `ChainWithMessages`. + /// + /// We assume that all chains that are bridging with this `ChainWithMessages` are using + /// the same name. + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str; + + /// Name of the `ToOutboundLaneApi::message_details` runtime API method. + /// The method is provided by the runtime that is bridged with this `ChainWithMessages`. + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str; + + /// Name of the `FromInboundLaneApi::message_details` runtime API method. + /// The method is provided by the runtime that is bridged with this `ChainWithMessages`. + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str; + + /// Maximal number of unrewarded relayers in a single confirmation transaction at this + /// `ChainWithMessages`. + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce; + /// Maximal number of unconfirmed messages in a single confirmation transaction at this + /// `ChainWithMessages`. + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce; + + /// Type that is used by the chain, to convert from weight to fee. + type WeightToFee: WeightToFee; + /// Weights of message pallet calls. + type WeightInfo: pallet_bridge_messages::WeightInfoExt; +} + +/// Call type used by the chain. +pub type CallOf = ::Call; +/// Weight-to-Fee type used by the chain. +pub type WeightToFeeOf = ::WeightToFee; +/// Transaction status of the chain. +pub type TransactionStatusOf = TransactionStatus, HashOf>; + +/// Substrate-based chain with `AccountData` generic argument of `frame_system::AccountInfo` set to +/// the `pallet_balances::AccountData`. +pub trait ChainWithBalances: Chain { + /// Return runtime storage key for getting `frame_system::AccountInfo` of given account. + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey; +} + +/// SCALE-encoded extrinsic. +pub type EncodedExtrinsic = Vec; + +/// Block with justification. +pub trait BlockWithJustification
{ + /// Return block header. + fn header(&self) -> Header; + /// Return encoded block extrinsics. + fn extrinsics(&self) -> Vec; + /// Return block justification, if known. + fn justification(&self, engine_id: ConsensusEngineId) -> Option<&EncodedJustification>; +} + +/// Transaction before it is signed. +#[derive(Clone, Debug, PartialEq)] +pub struct UnsignedTransaction { + /// Runtime call of this transaction. + pub call: EncodedOrDecodedCall, + /// Transaction nonce. + pub nonce: C::Index, + /// Tip included into transaction. + pub tip: C::Balance, + /// Transaction era used by the chain. + pub era: TransactionEraOf, +} + +impl UnsignedTransaction { + /// Create new unsigned transaction with given call, nonce, era and zero tip. + pub fn new(call: EncodedOrDecodedCall, nonce: C::Index) -> Self { + Self { call, nonce, era: TransactionEra::Immortal, tip: Zero::zero() } + } + + /// Set transaction tip. + #[must_use] + pub fn tip(mut self, tip: C::Balance) -> Self { + self.tip = tip; + self + } + + /// Set transaction era. + #[must_use] + pub fn era(mut self, era: TransactionEraOf) -> Self { + self.era = era; + self + } +} + +/// Account key pair used by transactions signing scheme. +pub type AccountKeyPairOf = ::AccountKeyPair; + +/// Substrate-based chain transactions signing scheme. +pub trait ChainWithTransactions: Chain { + /// Type of key pairs used to sign transactions. + type AccountKeyPair: Pair; + /// Signed transaction. + type SignedTransaction: Clone + Debug + Codec + Send + 'static; + + /// Create transaction for given runtime call, signed by given account. + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result + where + Self: Sized; + + /// Returns true if transaction is signed. + fn is_signed(tx: &Self::SignedTransaction) -> bool; + + /// Returns true if transaction is signed by given signer. + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool; + + /// Parse signed transaction into its unsigned part. + /// + /// Returns `None` if signed transaction has unsupported format. + fn parse_transaction(tx: Self::SignedTransaction) -> Option>; +} + +/// Sign transaction parameters +pub struct SignParam { + /// Version of the runtime specification. + pub spec_version: u32, + /// Transaction version + pub transaction_version: u32, + /// Hash of the genesis block. + pub genesis_hash: HashOf, + /// Signer account + pub signer: AccountKeyPairOf, +} + +impl BlockWithJustification for SignedBlock { + fn header(&self) -> Block::Header { + self.block.header().clone() + } + + fn extrinsics(&self) -> Vec { + self.block.extrinsics().iter().map(Encode::encode).collect() + } + + fn justification(&self, engine_id: ConsensusEngineId) -> Option<&EncodedJustification> { + self.justifications.as_ref().and_then(|j| j.get(engine_id)) + } +} diff --git a/relays/client-substrate/src/client.rs b/relays/client-substrate/src/client.rs new file mode 100644 index 00000000000..4f783291ee3 --- /dev/null +++ b/relays/client-substrate/src/client.rs @@ -0,0 +1,742 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate node client. + +use crate::{ + chain::{Chain, ChainWithBalances, ChainWithTransactions}, + rpc::{ + SubstrateAuthorClient, SubstrateChainClient, SubstrateFinalityClient, + SubstrateFrameSystemClient, SubstrateStateClient, SubstrateSystemClient, + SubstrateTransactionPaymentClient, + }, + transaction_stall_timeout, ConnectionParams, Error, HashOf, HeaderIdOf, Result, SignParam, + TransactionTracker, UnsignedTransaction, +}; + +use async_std::sync::{Arc, Mutex}; +use async_trait::async_trait; +use bp_runtime::{HeaderIdProvider, StorageDoubleMapKeyProvider, StorageMapKeyProvider}; +use codec::{Decode, Encode}; +use frame_system::AccountInfo; +use futures::{SinkExt, StreamExt}; +use jsonrpsee::{ + core::DeserializeOwned, + ws_client::{WsClient as RpcClient, WsClientBuilder as RpcClientBuilder}, +}; +use num_traits::{Bounded, Zero}; +use pallet_balances::AccountData; +use pallet_transaction_payment::InclusionFee; +use relay_utils::{relay_loop::RECONNECT_DELAY, STALL_TIMEOUT}; +use sp_core::{ + storage::{StorageData, StorageKey}, + Bytes, Hasher, +}; +use sp_runtime::{ + traits::Header as HeaderT, + transaction_validity::{TransactionSource, TransactionValidity}, +}; +use sp_trie::StorageProof; +use sp_version::RuntimeVersion; +use std::{convert::TryFrom, future::Future}; + +const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities"; +const SUB_API_TXPOOL_VALIDATE_TRANSACTION: &str = "TaggedTransactionQueue_validate_transaction"; +const MAX_SUBSCRIPTION_CAPACITY: usize = 4096; + +/// Opaque justifications subscription type. +pub struct Subscription(pub(crate) Mutex>>); + +/// Opaque GRANDPA authorities set. +pub type OpaqueGrandpaAuthoritiesSet = Vec; + +/// Chain runtime version in client +#[derive(Clone, Debug)] +pub enum ChainRuntimeVersion { + /// Auto query from chain. + Auto, + /// Custom runtime version, defined by user. + /// the first is `spec_version` + /// the second is `transaction_version` + Custom(u32, u32), +} + +/// Substrate client type. +/// +/// Cloning `Client` is a cheap operation. +pub struct Client { + /// Tokio runtime handle. + tokio: Arc, + /// Client connection params. + params: Arc, + /// Substrate RPC client. + client: Arc, + /// Genesis block hash. + genesis_hash: HashOf, + /// If several tasks are submitting their transactions simultaneously using + /// `submit_signed_extrinsic` method, they may get the same transaction nonce. So one of + /// transactions will be rejected from the pool. This lock is here to prevent situations like + /// that. + submit_signed_extrinsic_lock: Arc>, + /// Saved chain runtime version + chain_runtime_version: ChainRuntimeVersion, +} + +#[async_trait] +impl relay_utils::relay_loop::Client for Client { + type Error = Error; + + async fn reconnect(&mut self) -> Result<()> { + let (tokio, client) = Self::build_client(&self.params).await?; + self.tokio = tokio; + self.client = client; + Ok(()) + } +} + +impl Clone for Client { + fn clone(&self) -> Self { + Client { + tokio: self.tokio.clone(), + params: self.params.clone(), + client: self.client.clone(), + genesis_hash: self.genesis_hash, + submit_signed_extrinsic_lock: self.submit_signed_extrinsic_lock.clone(), + chain_runtime_version: self.chain_runtime_version.clone(), + } + } +} + +impl std::fmt::Debug for Client { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.debug_struct("Client").field("genesis_hash", &self.genesis_hash).finish() + } +} + +impl Client { + /// Returns client that is able to call RPCs on Substrate node over websocket connection. + /// + /// This function will keep connecting to given Substrate node until connection is established + /// and is functional. If attempt fail, it will wait for `RECONNECT_DELAY` and retry again. + pub async fn new(params: ConnectionParams) -> Self { + let params = Arc::new(params); + loop { + match Self::try_connect(params.clone()).await { + Ok(client) => return client, + Err(error) => log::error!( + target: "bridge", + "Failed to connect to {} node: {:?}. Going to retry in {}s", + C::NAME, + error, + RECONNECT_DELAY.as_secs(), + ), + } + + async_std::task::sleep(RECONNECT_DELAY).await; + } + } + + /// Try to connect to Substrate node over websocket. Returns Substrate RPC client if connection + /// has been established or error otherwise. + pub async fn try_connect(params: Arc) -> Result { + let (tokio, client) = Self::build_client(¶ms).await?; + + let number: C::BlockNumber = Zero::zero(); + let genesis_hash_client = client.clone(); + let genesis_hash = tokio + .spawn(async move { + SubstrateChainClient::::block_hash(&*genesis_hash_client, Some(number)).await + }) + .await??; + + let chain_runtime_version = params.chain_runtime_version.clone(); + Ok(Self { + tokio, + params, + client, + genesis_hash, + submit_signed_extrinsic_lock: Arc::new(Mutex::new(())), + chain_runtime_version, + }) + } + + /// Build client to use in connection. + async fn build_client( + params: &ConnectionParams, + ) -> Result<(Arc, Arc)> { + let tokio = tokio::runtime::Runtime::new()?; + let uri = format!( + "{}://{}:{}", + if params.secure { "wss" } else { "ws" }, + params.host, + params.port, + ); + log::info!(target: "bridge", "Connecting to {} node at {}", C::NAME, uri); + + let client = tokio + .spawn(async move { + RpcClientBuilder::default() + .max_notifs_per_subscription(MAX_SUBSCRIPTION_CAPACITY) + .build(&uri) + .await + }) + .await??; + + Ok((Arc::new(tokio), Arc::new(client))) + } +} + +impl Client { + /// Return simple runtime version, only include `spec_version` and `transaction_version`. + pub async fn simple_runtime_version(&self) -> Result<(u32, u32)> { + let (spec_version, transaction_version) = match self.chain_runtime_version { + ChainRuntimeVersion::Auto => { + let runtime_version = self.runtime_version().await?; + (runtime_version.spec_version, runtime_version.transaction_version) + }, + ChainRuntimeVersion::Custom(spec_version, transaction_version) => + (spec_version, transaction_version), + }; + Ok((spec_version, transaction_version)) + } + + /// Returns true if client is connected to at least one peer and is in synced state. + pub async fn ensure_synced(&self) -> Result<()> { + self.jsonrpsee_execute(|client| async move { + let health = SubstrateSystemClient::::health(&*client).await?; + let is_synced = !health.is_syncing && (!health.should_have_peers || health.peers > 0); + if is_synced { + Ok(()) + } else { + Err(Error::ClientNotSynced(health)) + } + }) + .await + } + + /// Return hash of the genesis block. + pub fn genesis_hash(&self) -> &C::Hash { + &self.genesis_hash + } + + /// Return hash of the best finalized block. + pub async fn best_finalized_header_hash(&self) -> Result { + self.jsonrpsee_execute(|client| async move { + Ok(SubstrateChainClient::::finalized_head(&*client).await?) + }) + .await + } + + /// Return number of the best finalized block. + pub async fn best_finalized_header_number(&self) -> Result { + Ok(*self.header_by_hash(self.best_finalized_header_hash().await?).await?.number()) + } + + /// Return header of the best finalized block. + pub async fn best_finalized_header(&self) -> Result { + self.header_by_hash(self.best_finalized_header_hash().await?).await + } + + /// Returns the best Substrate header. + pub async fn best_header(&self) -> Result + where + C::Header: DeserializeOwned, + { + self.jsonrpsee_execute(|client| async move { + Ok(SubstrateChainClient::::header(&*client, None).await?) + }) + .await + } + + /// Get a Substrate block from its hash. + pub async fn get_block(&self, block_hash: Option) -> Result { + self.jsonrpsee_execute(move |client| async move { + Ok(SubstrateChainClient::::block(&*client, block_hash).await?) + }) + .await + } + + /// Get a Substrate header by its hash. + pub async fn header_by_hash(&self, block_hash: C::Hash) -> Result + where + C::Header: DeserializeOwned, + { + self.jsonrpsee_execute(move |client| async move { + Ok(SubstrateChainClient::::header(&*client, Some(block_hash)).await?) + }) + .await + } + + /// Get a Substrate block hash by its number. + pub async fn block_hash_by_number(&self, number: C::BlockNumber) -> Result { + self.jsonrpsee_execute(move |client| async move { + Ok(SubstrateChainClient::::block_hash(&*client, Some(number)).await?) + }) + .await + } + + /// Get a Substrate header by its number. + pub async fn header_by_number(&self, block_number: C::BlockNumber) -> Result + where + C::Header: DeserializeOwned, + { + let block_hash = Self::block_hash_by_number(self, block_number).await?; + let header_by_hash = Self::header_by_hash(self, block_hash).await?; + Ok(header_by_hash) + } + + /// Return runtime version. + pub async fn runtime_version(&self) -> Result { + self.jsonrpsee_execute(move |client| async move { + Ok(SubstrateStateClient::::runtime_version(&*client).await?) + }) + .await + } + + /// Read value from runtime storage. + pub async fn storage_value( + &self, + storage_key: StorageKey, + block_hash: Option, + ) -> Result> { + self.raw_storage_value(storage_key, block_hash) + .await? + .map(|encoded_value| { + T::decode(&mut &encoded_value.0[..]).map_err(Error::ResponseParseFailed) + }) + .transpose() + } + + /// Read `MapStorage` value from runtime storage. + pub async fn storage_map_value( + &self, + pallet_prefix: &str, + key: &T::Key, + block_hash: Option, + ) -> Result> { + let storage_key = T::final_key(pallet_prefix, key); + + self.raw_storage_value(storage_key, block_hash) + .await? + .map(|encoded_value| { + T::Value::decode(&mut &encoded_value.0[..]).map_err(Error::ResponseParseFailed) + }) + .transpose() + } + + /// Read `DoubleMapStorage` value from runtime storage. + pub async fn storage_double_map_value( + &self, + pallet_prefix: &str, + key1: &T::Key1, + key2: &T::Key2, + block_hash: Option, + ) -> Result> { + let storage_key = T::final_key(pallet_prefix, key1, key2); + + self.raw_storage_value(storage_key, block_hash) + .await? + .map(|encoded_value| { + T::Value::decode(&mut &encoded_value.0[..]).map_err(Error::ResponseParseFailed) + }) + .transpose() + } + + /// Read raw value from runtime storage. + pub async fn raw_storage_value( + &self, + storage_key: StorageKey, + block_hash: Option, + ) -> Result> { + self.jsonrpsee_execute(move |client| async move { + Ok(SubstrateStateClient::::storage(&*client, storage_key, block_hash).await?) + }) + .await + } + + /// Return native tokens balance of the account. + pub async fn free_native_balance(&self, account: C::AccountId) -> Result + where + C: ChainWithBalances, + { + self.jsonrpsee_execute(move |client| async move { + let storage_key = C::account_info_storage_key(&account); + let encoded_account_data = + SubstrateStateClient::::storage(&*client, storage_key, None) + .await? + .ok_or(Error::AccountDoesNotExist)?; + let decoded_account_data = AccountInfo::>::decode( + &mut &encoded_account_data.0[..], + ) + .map_err(Error::ResponseParseFailed)?; + Ok(decoded_account_data.data.free) + }) + .await + } + + /// Get the nonce of the given Substrate account. + /// + /// Note: It's the caller's responsibility to make sure `account` is a valid SS58 address. + pub async fn next_account_index(&self, account: C::AccountId) -> Result { + self.jsonrpsee_execute(move |client| async move { + Ok(SubstrateFrameSystemClient::::account_next_index(&*client, account).await?) + }) + .await + } + + /// Submit unsigned extrinsic for inclusion in a block. + /// + /// Note: The given transaction needs to be SCALE encoded beforehand. + pub async fn submit_unsigned_extrinsic(&self, transaction: Bytes) -> Result { + self.jsonrpsee_execute(move |client| async move { + let tx_hash = SubstrateAuthorClient::::submit_extrinsic(&*client, transaction) + .await + .map_err(|e| { + log::error!(target: "bridge", "Failed to send transaction to {} node: {:?}", C::NAME, e); + e + })?; + log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash); + Ok(tx_hash) + }) + .await + } + + /// Submit an extrinsic signed by given account. + /// + /// All calls of this method are synchronized, so there can't be more than one active + /// `submit_signed_extrinsic()` call. This guarantees that no nonces collision may happen + /// if all client instances are clones of the same initial `Client`. + /// + /// Note: The given transaction needs to be SCALE encoded beforehand. + pub async fn submit_signed_extrinsic( + &self, + extrinsic_signer: C::AccountId, + signing_data: SignParam, + prepare_extrinsic: impl FnOnce(HeaderIdOf, C::Index) -> Result> + + Send + + 'static, + ) -> Result + where + C: ChainWithTransactions, + { + let _guard = self.submit_signed_extrinsic_lock.lock().await; + let transaction_nonce = self.next_account_index(extrinsic_signer).await?; + let best_header = self.best_header().await?; + + // By using parent of best block here, we are protecing again best-block reorganizations. + // E.g. transaction may have been submitted when the best block was `A[num=100]`. Then it + // has been changed to `B[num=100]`. Hash of `A` has been included into transaction + // signature payload. So when signature will be checked, the check will fail and transaction + // will be dropped from the pool. + let best_header_id = best_header.parent_id().unwrap_or_else(|| best_header.id()); + + self.jsonrpsee_execute(move |client| async move { + let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?; + let signed_extrinsic = C::sign_transaction(signing_data, extrinsic)?.encode(); + let tx_hash = + SubstrateAuthorClient::::submit_extrinsic(&*client, Bytes(signed_extrinsic)) + .await + .map_err(|e| { + log::error!(target: "bridge", "Failed to send transaction to {} node: {:?}", C::NAME, e); + e + })?; + log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash); + Ok(tx_hash) + }) + .await + } + + /// Does exactly the same as `submit_signed_extrinsic`, but keeps watching for extrinsic status + /// after submission. + pub async fn submit_and_watch_signed_extrinsic( + &self, + extrinsic_signer: C::AccountId, + signing_data: SignParam, + prepare_extrinsic: impl FnOnce(HeaderIdOf, C::Index) -> Result> + + Send + + 'static, + ) -> Result> + where + C: ChainWithTransactions, + { + let self_clone = self.clone(); + let _guard = self.submit_signed_extrinsic_lock.lock().await; + let transaction_nonce = self.next_account_index(extrinsic_signer).await?; + let best_header = self.best_header().await?; + let best_header_id = best_header.id(); + let (sender, receiver) = futures::channel::mpsc::channel(MAX_SUBSCRIPTION_CAPACITY); + let (tracker, subscription) = self + .jsonrpsee_execute(move |client| async move { + let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?; + let stall_timeout = transaction_stall_timeout( + extrinsic.era.mortality_period(), + C::AVERAGE_BLOCK_INTERVAL, + STALL_TIMEOUT, + ); + let signed_extrinsic = C::sign_transaction(signing_data, extrinsic)?.encode(); + let tx_hash = C::Hasher::hash(&signed_extrinsic); + let subscription = SubstrateAuthorClient::::submit_and_watch_extrinsic( + &*client, + Bytes(signed_extrinsic), + ) + .await + .map_err(|e| { + log::error!(target: "bridge", "Failed to send transaction to {} node: {:?}", C::NAME, e); + e + })?; + log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash); + let tracker = TransactionTracker::new( + self_clone, + stall_timeout, + tx_hash, + Subscription(Mutex::new(receiver)), + ); + Ok((tracker, subscription)) + }) + .await?; + self.tokio.spawn(Subscription::background_worker( + C::NAME.into(), + "extrinsic".into(), + subscription, + sender, + )); + Ok(tracker) + } + + /// Returns pending extrinsics from transaction pool. + pub async fn pending_extrinsics(&self) -> Result> { + self.jsonrpsee_execute(move |client| async move { + Ok(SubstrateAuthorClient::::pending_extrinsics(&*client).await?) + }) + .await + } + + /// Validate transaction at given block state. + pub async fn validate_transaction( + &self, + at_block: C::Hash, + transaction: SignedTransaction, + ) -> Result { + self.jsonrpsee_execute(move |client| async move { + let call = SUB_API_TXPOOL_VALIDATE_TRANSACTION.to_string(); + let data = Bytes((TransactionSource::External, transaction, at_block).encode()); + + let encoded_response = + SubstrateStateClient::::call(&*client, call, data, Some(at_block)).await?; + let validity = TransactionValidity::decode(&mut &encoded_response.0[..]) + .map_err(Error::ResponseParseFailed)?; + + Ok(validity) + }) + .await + } + + /// Estimate fee that will be spent on given extrinsic. + pub async fn estimate_extrinsic_fee( + &self, + transaction: Bytes, + ) -> Result> { + self.jsonrpsee_execute(move |client| async move { + let fee_details = + SubstrateTransactionPaymentClient::::fee_details(&*client, transaction, None) + .await?; + let inclusion_fee = fee_details + .inclusion_fee + .map(|inclusion_fee| InclusionFee { + base_fee: C::Balance::try_from(inclusion_fee.base_fee.into_u256()) + .unwrap_or_else(|_| C::Balance::max_value()), + len_fee: C::Balance::try_from(inclusion_fee.len_fee.into_u256()) + .unwrap_or_else(|_| C::Balance::max_value()), + adjusted_weight_fee: C::Balance::try_from( + inclusion_fee.adjusted_weight_fee.into_u256(), + ) + .unwrap_or_else(|_| C::Balance::max_value()), + }) + .unwrap_or_else(|| InclusionFee { + base_fee: Zero::zero(), + len_fee: Zero::zero(), + adjusted_weight_fee: Zero::zero(), + }); + Ok(inclusion_fee) + }) + .await + } + + /// Get the GRANDPA authority set at given block. + pub async fn grandpa_authorities_set( + &self, + block: C::Hash, + ) -> Result { + self.jsonrpsee_execute(move |client| async move { + let call = SUB_API_GRANDPA_AUTHORITIES.to_string(); + let data = Bytes(Vec::new()); + + let encoded_response = + SubstrateStateClient::::call(&*client, call, data, Some(block)).await?; + let authority_list = encoded_response.0; + + Ok(authority_list) + }) + .await + } + + /// Execute runtime call at given block, provided the input and output types. + /// It also performs the input encode and output decode. + pub async fn typed_state_call( + &self, + method_name: String, + input: Input, + at_block: Option, + ) -> Result { + let encoded_output = self.state_call(method_name, Bytes(input.encode()), at_block).await?; + Output::decode(&mut &encoded_output.0[..]).map_err(Error::ResponseParseFailed) + } + + /// Execute runtime call at given block. + pub async fn state_call( + &self, + method: String, + data: Bytes, + at_block: Option, + ) -> Result { + self.jsonrpsee_execute(move |client| async move { + SubstrateStateClient::::call(&*client, method, data, at_block) + .await + .map_err(Into::into) + }) + .await + } + + /// Returns storage proof of given storage keys. + pub async fn prove_storage( + &self, + keys: Vec, + at_block: C::Hash, + ) -> Result { + self.jsonrpsee_execute(move |client| async move { + SubstrateStateClient::::prove_storage(&*client, keys, Some(at_block)) + .await + .map(|proof| { + StorageProof::new(proof.proof.into_iter().map(|b| b.0).collect::>()) + }) + .map_err(Into::into) + }) + .await + } + + /// Return `tokenDecimals` property from the set of chain properties. + pub async fn token_decimals(&self) -> Result> { + self.jsonrpsee_execute(move |client| async move { + let system_properties = SubstrateSystemClient::::properties(&*client).await?; + Ok(system_properties.get("tokenDecimals").and_then(|v| v.as_u64())) + }) + .await + } + + /// Return new finality justifications stream. + pub async fn subscribe_finality_justifications>( + &self, + ) -> Result> { + let subscription = self + .jsonrpsee_execute(move |client| async move { + Ok(FC::subscribe_justifications(&client).await?) + }) + .await?; + let (sender, receiver) = futures::channel::mpsc::channel(MAX_SUBSCRIPTION_CAPACITY); + self.tokio.spawn(Subscription::background_worker( + C::NAME.into(), + "justification".into(), + subscription, + sender, + )); + Ok(Subscription(Mutex::new(receiver))) + } + + /// Execute jsonrpsee future in tokio context. + async fn jsonrpsee_execute(&self, make_jsonrpsee_future: MF) -> Result + where + MF: FnOnce(Arc) -> F + Send + 'static, + F: Future> + Send, + T: Send + 'static, + { + let client = self.client.clone(); + self.tokio.spawn(async move { make_jsonrpsee_future(client).await }).await? + } + + /// Returns `true` if version guard can be started. + /// + /// There's no reason to run version guard when version mode is set to `Auto`. It can + /// lead to relay shutdown when chain is upgraded, even though we have explicitly + /// said that we don't want to shutdown. + pub fn can_start_version_guard(&self) -> bool { + !matches!(self.chain_runtime_version, ChainRuntimeVersion::Auto) + } +} + +impl Subscription { + /// Consumes subscription and returns future statuses stream. + pub fn into_stream(self) -> impl futures::Stream { + futures::stream::unfold(self, |this| async { + let item = this.0.lock().await.next().await.unwrap_or(None); + item.map(|i| (i, this)) + }) + } + + /// Return next item from the subscription. + pub async fn next(&self) -> Result> { + let mut receiver = self.0.lock().await; + let item = receiver.next().await; + Ok(item.unwrap_or(None)) + } + + /// Background worker that is executed in tokio context as `jsonrpsee` requires. + async fn background_worker( + chain_name: String, + item_type: String, + mut subscription: jsonrpsee::core::client::Subscription, + mut sender: futures::channel::mpsc::Sender>, + ) { + loop { + match subscription.next().await { + Some(Ok(item)) => + if sender.send(Some(item)).await.is_err() { + break + }, + Some(Err(e)) => { + log::trace!( + target: "bridge", + "{} {} subscription stream has returned '{:?}'. Stream needs to be restarted.", + chain_name, + item_type, + e, + ); + let _ = sender.send(None).await; + break + }, + None => { + log::trace!( + target: "bridge", + "{} {} subscription stream has returned None. Stream needs to be restarted.", + chain_name, + item_type, + ); + let _ = sender.send(None).await; + break + }, + } + } + } +} diff --git a/relays/client-substrate/src/error.rs b/relays/client-substrate/src/error.rs new file mode 100644 index 00000000000..9323b757221 --- /dev/null +++ b/relays/client-substrate/src/error.rs @@ -0,0 +1,86 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate node RPC errors. + +use jsonrpsee::core::Error as RpcError; +use relay_utils::MaybeConnectionError; +use sc_rpc_api::system::Health; +use sp_runtime::transaction_validity::TransactionValidityError; +use thiserror::Error; + +/// Result type used by Substrate client. +pub type Result = std::result::Result; + +/// Errors that can occur only when interacting with +/// a Substrate node through RPC. +#[derive(Error, Debug)] +pub enum Error { + /// IO error. + #[error("IO error: {0}")] + Io(#[from] std::io::Error), + /// An error that can occur when making a request to + /// an JSON-RPC server. + #[error("RPC error: {0}")] + RpcError(#[from] RpcError), + /// The response from the server could not be SCALE decoded. + #[error("Response parse failed: {0}")] + ResponseParseFailed(#[from] codec::Error), + /// Account does not exist on the chain. + #[error("Account does not exist on the chain.")] + AccountDoesNotExist, + /// Runtime storage is missing some mandatory value. + #[error("Mandatory storage value is missing from the runtime storage.")] + MissingMandatoryStorageValue, + /// The client we're connected to is not synced, so we can't rely on its state. + #[error("Substrate client is not synced {0}.")] + ClientNotSynced(Health), + /// The bridge pallet is halted and all transactions will be rejected. + #[error("Bridge pallet is halted.")] + BridgePalletIsHalted, + /// The bridge pallet is not yet initialized and all transactions will be rejected. + #[error("Bridge pallet is not initialized.")] + BridgePalletIsNotInitialized, + /// An error has happened when we have tried to parse storage proof. + #[error("Error when parsing storage proof: {0:?}.")] + StorageProofError(bp_runtime::StorageProofError), + /// The Substrate transaction is invalid. + #[error("Substrate transaction is invalid: {0:?}")] + TransactionInvalid(#[from] TransactionValidityError), + /// Custom logic error. + #[error("{0}")] + Custom(String), +} + +impl From for Error { + fn from(error: tokio::task::JoinError) -> Self { + Error::Custom(format!("Failed to wait tokio task: {error}")) + } +} + +impl MaybeConnectionError for Error { + fn is_connection_error(&self) -> bool { + matches!( + *self, + Error::RpcError(RpcError::Transport(_)) + // right now if connection to the ws server is dropped (after it is already established), + // we're getting this error + | Error::RpcError(RpcError::Internal(_)) + | Error::RpcError(RpcError::RestartNeeded(_)) + | Error::ClientNotSynced(_), + ) + } +} diff --git a/relays/client-substrate/src/guard.rs b/relays/client-substrate/src/guard.rs new file mode 100644 index 00000000000..1afbf0d3d17 --- /dev/null +++ b/relays/client-substrate/src/guard.rs @@ -0,0 +1,373 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Pallet provides a set of guard functions that are running in background threads +//! and are aborting process if some condition fails. + +use crate::{error::Error, Chain, ChainWithBalances, Client}; + +use async_trait::async_trait; +use num_traits::CheckedSub; +use sp_version::RuntimeVersion; +use std::{ + collections::VecDeque, + fmt::Display, + time::{Duration, Instant}, +}; + +/// Guards environment. +#[async_trait] +pub trait Environment: Send + Sync + 'static { + /// Error type. + type Error: Display + Send + Sync + 'static; + + /// Return current runtime version. + async fn runtime_version(&mut self) -> Result; + /// Return free native balance of the account on the chain. + async fn free_native_balance( + &mut self, + account: C::AccountId, + ) -> Result; + + /// Return current time. + fn now(&self) -> Instant { + Instant::now() + } + + /// Sleep given amount of time. + async fn sleep(&mut self, duration: Duration) { + async_std::task::sleep(duration).await + } + + /// Abort current process. Called when guard condition check fails. + async fn abort(&mut self) { + std::process::abort(); + } +} + +/// Abort when runtime spec version is different from specified. +pub fn abort_on_spec_version_change( + mut env: impl Environment, + expected_spec_version: u32, +) { + async_std::task::spawn(async move { + log::info!( + target: "bridge-guard", + "Starting spec_version guard for {}. Expected spec_version: {}", + C::NAME, + expected_spec_version, + ); + + loop { + let actual_spec_version = env.runtime_version().await; + match actual_spec_version { + Ok(version) if version.spec_version == expected_spec_version => (), + Ok(version) => { + log::error!( + target: "bridge-guard", + "{} runtime spec version has changed from {} to {}. Aborting relay", + C::NAME, + expected_spec_version, + version.spec_version, + ); + + env.abort().await; + }, + Err(error) => log::warn!( + target: "bridge-guard", + "Failed to read {} runtime version: {}. Relay may need to be stopped manually", + C::NAME, + error, + ), + } + + env.sleep(conditions_check_delay::()).await; + } + }); +} + +/// Abort if, during 24 hours, free balance of given account is decreased at least by given value. +/// Other components may increase (or decrease) balance of account and it WILL affect logic of the +/// guard. +pub fn abort_when_account_balance_decreased( + mut env: impl Environment, + account_id: C::AccountId, + maximal_decrease: C::Balance, +) { + const DAY: Duration = Duration::from_secs(60 * 60 * 24); + + async_std::task::spawn(async move { + log::info!( + target: "bridge-guard", + "Starting balance guard for {}/{:?}. Maximal decrease: {:?}", + C::NAME, + account_id, + maximal_decrease, + ); + + let mut balances = VecDeque::new(); + + loop { + let current_time = env.now(); + + // remember balances that are beyound 24h border + if let Some(time_border) = current_time.checked_sub(DAY) { + while balances.front().map(|(time, _)| *time < time_border).unwrap_or(false) { + balances.pop_front(); + } + } + + // read balance of the account + let current_balance = env.free_native_balance(account_id.clone()).await; + + // remember balance and check difference + match current_balance { + Ok(current_balance) => { + // remember balance + balances.push_back((current_time, current_balance)); + + // check if difference between current and oldest balance is too large + let (oldest_time, oldest_balance) = + balances.front().expect("pushed to queue couple of lines above; qed"); + let balances_difference = oldest_balance.checked_sub(¤t_balance); + if balances_difference > Some(maximal_decrease) { + log::error!( + target: "bridge-guard", + "Balance of {} account {:?} has decreased from {:?} to {:?} in {} minutes. Aborting relay", + C::NAME, + account_id, + oldest_balance, + current_balance, + current_time.duration_since(*oldest_time).as_secs() / 60, + ); + + env.abort().await; + } + }, + Err(error) => { + log::warn!( + target: "bridge-guard", + "Failed to read {} account {:?} balance: {}. Relay may need to be stopped manually", + C::NAME, + account_id, + error, + ); + }, + }; + + env.sleep(conditions_check_delay::()).await; + } + }); +} + +/// Delay between conditions check. +fn conditions_check_delay() -> Duration { + C::AVERAGE_BLOCK_INTERVAL * (10 + rand::random::() % 10) +} + +#[async_trait] +impl Environment for Client { + type Error = Error; + + async fn runtime_version(&mut self) -> Result { + Client::::runtime_version(self).await + } + + async fn free_native_balance( + &mut self, + account: C::AccountId, + ) -> Result { + Client::::free_native_balance(self, account).await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_chain::TestChain; + use futures::{ + channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}, + future::FutureExt, + stream::StreamExt, + SinkExt, + }; + + struct TestEnvironment { + runtime_version_rx: UnboundedReceiver, + free_native_balance_rx: UnboundedReceiver, + slept_tx: UnboundedSender<()>, + aborted_tx: UnboundedSender<()>, + } + + #[async_trait] + impl Environment for TestEnvironment { + type Error = Error; + + async fn runtime_version(&mut self) -> Result { + Ok(self.runtime_version_rx.next().await.unwrap_or_default()) + } + + async fn free_native_balance(&mut self, _account: u32) -> Result { + Ok(self.free_native_balance_rx.next().await.unwrap_or_default()) + } + + async fn sleep(&mut self, _duration: Duration) { + let _ = self.slept_tx.send(()).await; + } + + async fn abort(&mut self) { + let _ = self.aborted_tx.send(()).await; + // simulate process abort :) + async_std::task::sleep(Duration::from_secs(60)).await; + } + } + + #[test] + fn aborts_when_spec_version_is_changed() { + async_std::task::block_on(async { + let ( + (mut runtime_version_tx, runtime_version_rx), + (_free_native_balance_tx, free_native_balance_rx), + (slept_tx, mut slept_rx), + (aborted_tx, mut aborted_rx), + ) = (unbounded(), unbounded(), unbounded(), unbounded()); + abort_on_spec_version_change( + TestEnvironment { + runtime_version_rx, + free_native_balance_rx, + slept_tx, + aborted_tx, + }, + 0, + ); + + // client responds with wrong version + runtime_version_tx + .send(RuntimeVersion { spec_version: 42, ..Default::default() }) + .await + .unwrap(); + + // then the `abort` function is called + aborted_rx.next().await; + // and we do not reach the `sleep` function call + assert!(slept_rx.next().now_or_never().is_none()); + }); + } + + #[test] + fn does_not_aborts_when_spec_version_is_unchanged() { + async_std::task::block_on(async { + let ( + (mut runtime_version_tx, runtime_version_rx), + (_free_native_balance_tx, free_native_balance_rx), + (slept_tx, mut slept_rx), + (aborted_tx, mut aborted_rx), + ) = (unbounded(), unbounded(), unbounded(), unbounded()); + abort_on_spec_version_change( + TestEnvironment { + runtime_version_rx, + free_native_balance_rx, + slept_tx, + aborted_tx, + }, + 42, + ); + + // client responds with the same version + runtime_version_tx + .send(RuntimeVersion { spec_version: 42, ..Default::default() }) + .await + .unwrap(); + + // then the `sleep` function is called + slept_rx.next().await; + // and the `abort` function is not called + assert!(aborted_rx.next().now_or_never().is_none()); + }); + } + + #[test] + fn aborts_when_balance_is_too_low() { + async_std::task::block_on(async { + let ( + (_runtime_version_tx, runtime_version_rx), + (mut free_native_balance_tx, free_native_balance_rx), + (slept_tx, mut slept_rx), + (aborted_tx, mut aborted_rx), + ) = (unbounded(), unbounded(), unbounded(), unbounded()); + abort_when_account_balance_decreased( + TestEnvironment { + runtime_version_rx, + free_native_balance_rx, + slept_tx, + aborted_tx, + }, + 0, + 100, + ); + + // client responds with initial balance + free_native_balance_tx.send(1000).await.unwrap(); + + // then the guard sleeps + slept_rx.next().await; + + // and then client responds with updated balance, which is too low + free_native_balance_tx.send(899).await.unwrap(); + + // then the `abort` function is called + aborted_rx.next().await; + // and we do not reach next `sleep` function call + assert!(slept_rx.next().now_or_never().is_none()); + }); + } + + #[test] + fn does_not_aborts_when_balance_is_enough() { + async_std::task::block_on(async { + let ( + (_runtime_version_tx, runtime_version_rx), + (mut free_native_balance_tx, free_native_balance_rx), + (slept_tx, mut slept_rx), + (aborted_tx, mut aborted_rx), + ) = (unbounded(), unbounded(), unbounded(), unbounded()); + abort_when_account_balance_decreased( + TestEnvironment { + runtime_version_rx, + free_native_balance_rx, + slept_tx, + aborted_tx, + }, + 0, + 100, + ); + + // client responds with initial balance + free_native_balance_tx.send(1000).await.unwrap(); + + // then the guard sleeps + slept_rx.next().await; + + // and then client responds with updated balance, which is enough + free_native_balance_tx.send(950).await.unwrap(); + + // then the `sleep` function is called + slept_rx.next().await; + // and `abort` is not called + assert!(aborted_rx.next().now_or_never().is_none()); + }); + } +} diff --git a/relays/client-substrate/src/lib.rs b/relays/client-substrate/src/lib.rs new file mode 100644 index 00000000000..99ff0fbe394 --- /dev/null +++ b/relays/client-substrate/src/lib.rs @@ -0,0 +1,91 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Tools to interact with Substrate node using RPC methods. + +#![warn(missing_docs)] + +mod chain; +mod client; +mod error; +mod rpc; +mod sync_header; +mod transaction_tracker; + +pub mod guard; +pub mod metrics; +pub mod test_chain; + +use std::time::Duration; + +pub use crate::{ + chain::{ + AccountKeyPairOf, BlockWithJustification, CallOf, Chain, ChainWithBalances, + ChainWithGrandpa, ChainWithMessages, ChainWithTransactions, RelayChain, SignParam, + TransactionStatusOf, UnsignedTransaction, WeightToFeeOf, + }, + client::{ChainRuntimeVersion, Client, OpaqueGrandpaAuthoritiesSet, Subscription}, + error::{Error, Result}, + rpc::{SubstrateBeefyFinalityClient, SubstrateFinalityClient, SubstrateGrandpaFinalityClient}, + sync_header::SyncHeader, + transaction_tracker::TransactionTracker, +}; +pub use bp_runtime::{ + AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain as ChainBase, HashOf, HeaderOf, + IndexOf, SignatureOf, TransactionEra, TransactionEraOf, +}; + +/// Header id used by the chain. +pub type HeaderIdOf = relay_utils::HeaderId, BlockNumberOf>; + +/// Substrate-over-websocket connection params. +#[derive(Debug, Clone)] +pub struct ConnectionParams { + /// Websocket server host name. + pub host: String, + /// Websocket server TCP port. + pub port: u16, + /// Use secure websocket connection. + pub secure: bool, + /// Defined chain runtime version + pub chain_runtime_version: ChainRuntimeVersion, +} + +impl Default for ConnectionParams { + fn default() -> Self { + ConnectionParams { + host: "localhost".into(), + port: 9944, + secure: false, + chain_runtime_version: ChainRuntimeVersion::Auto, + } + } +} + +/// Returns stall timeout for relay loop. +/// +/// Relay considers himself stalled if he has submitted transaction to the node, but it has not +/// been mined for this period. +pub fn transaction_stall_timeout( + mortality_period: Option, + average_block_interval: Duration, + default_stall_timeout: Duration, +) -> Duration { + // 1 extra block for transaction to reach the pool && 1 for relayer to awake after it is mined + mortality_period + .map(|mortality_period| average_block_interval.saturating_mul(mortality_period + 1 + 1)) + .unwrap_or(default_stall_timeout) +} diff --git a/relays/client-substrate/src/metrics/float_storage_value.rs b/relays/client-substrate/src/metrics/float_storage_value.rs new file mode 100644 index 00000000000..7bb92693b38 --- /dev/null +++ b/relays/client-substrate/src/metrics/float_storage_value.rs @@ -0,0 +1,133 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{chain::Chain, client::Client, Error as SubstrateError}; + +use async_std::sync::{Arc, RwLock}; +use async_trait::async_trait; +use codec::Decode; +use num_traits::One; +use relay_utils::metrics::{ + metric_name, register, F64SharedRef, Gauge, Metric, PrometheusError, Registry, + StandaloneMetric, F64, +}; +use sp_core::storage::{StorageData, StorageKey}; +use sp_runtime::{traits::UniqueSaturatedInto, FixedPointNumber, FixedU128}; +use std::{marker::PhantomData, time::Duration}; + +/// Storage value update interval (in blocks). +const UPDATE_INTERVAL_IN_BLOCKS: u32 = 5; + +/// Fied-point storage value and the way it is decoded from the raw storage value. +pub trait FloatStorageValue: 'static + Clone + Send + Sync { + /// Type of the value. + type Value: FixedPointNumber; + /// Try to decode value from the raw storage value. + fn decode( + &self, + maybe_raw_value: Option, + ) -> Result, SubstrateError>; +} + +/// Implementation of `FloatStorageValue` that expects encoded `FixedU128` value and returns `1` if +/// value is missing from the storage. +#[derive(Clone, Debug, Default)] +pub struct FixedU128OrOne; + +impl FloatStorageValue for FixedU128OrOne { + type Value = FixedU128; + + fn decode( + &self, + maybe_raw_value: Option, + ) -> Result, SubstrateError> { + maybe_raw_value + .map(|raw_value| { + FixedU128::decode(&mut &raw_value.0[..]) + .map_err(SubstrateError::ResponseParseFailed) + .map(Some) + }) + .unwrap_or_else(|| Ok(Some(FixedU128::one()))) + } +} + +/// Metric that represents fixed-point runtime storage value as float gauge. +#[derive(Clone, Debug)] +pub struct FloatStorageValueMetric { + value_converter: V, + client: Client, + storage_key: StorageKey, + metric: Gauge, + shared_value_ref: F64SharedRef, + _phantom: PhantomData, +} + +impl FloatStorageValueMetric { + /// Create new metric. + pub fn new( + value_converter: V, + client: Client, + storage_key: StorageKey, + name: String, + help: String, + ) -> Result { + let shared_value_ref = Arc::new(RwLock::new(None)); + Ok(FloatStorageValueMetric { + value_converter, + client, + storage_key, + metric: Gauge::new(metric_name(None, &name), help)?, + shared_value_ref, + _phantom: Default::default(), + }) + } + + /// Get shared reference to metric value. + pub fn shared_value_ref(&self) -> F64SharedRef { + self.shared_value_ref.clone() + } +} + +impl Metric for FloatStorageValueMetric { + fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { + register(self.metric.clone(), registry).map(drop) + } +} + +#[async_trait] +impl StandaloneMetric for FloatStorageValueMetric { + fn update_interval(&self) -> Duration { + C::AVERAGE_BLOCK_INTERVAL * UPDATE_INTERVAL_IN_BLOCKS + } + + async fn update(&self) { + let value = self + .client + .raw_storage_value(self.storage_key.clone(), None) + .await + .and_then(|maybe_storage_value| { + self.value_converter.decode(maybe_storage_value).map(|maybe_fixed_point_value| { + maybe_fixed_point_value.map(|fixed_point_value| { + fixed_point_value.into_inner().unique_saturated_into() as f64 / + V::Value::DIV.unique_saturated_into() as f64 + }) + }) + }) + .map_err(|e| e.to_string()); + relay_utils::metrics::set_gauge_value(&self.metric, value.clone()); + *self.shared_value_ref.write().await = value.ok().and_then(|x| x); + } +} diff --git a/relays/client-substrate/src/metrics/mod.rs b/relays/client-substrate/src/metrics/mod.rs new file mode 100644 index 00000000000..fe200e2d3dc --- /dev/null +++ b/relays/client-substrate/src/metrics/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Contains several Substrate-specific metrics that may be exposed by relay. + +pub use float_storage_value::{FixedU128OrOne, FloatStorageValue, FloatStorageValueMetric}; + +mod float_storage_value; diff --git a/relays/client-substrate/src/rpc.rs b/relays/client-substrate/src/rpc.rs new file mode 100644 index 00000000000..083b1dea761 --- /dev/null +++ b/relays/client-substrate/src/rpc.rs @@ -0,0 +1,170 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! The most generic Substrate node RPC interface. + +use async_trait::async_trait; + +use crate::{Chain, ChainWithGrandpa, TransactionStatusOf}; + +use jsonrpsee::{ + core::{client::Subscription, RpcResult}, + proc_macros::rpc, + ws_client::WsClient, +}; +use pallet_transaction_payment_rpc_runtime_api::FeeDetails; +use sc_rpc_api::{state::ReadProof, system::Health}; +use sp_core::{ + storage::{StorageData, StorageKey}, + Bytes, +}; +use sp_rpc::number::NumberOrHex; +use sp_version::RuntimeVersion; + +/// RPC methods of Substrate `system` namespace, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "system")] +pub(crate) trait SubstrateSystem { + /// Return node health. + #[method(name = "health")] + async fn health(&self) -> RpcResult; + /// Return system properties. + #[method(name = "properties")] + async fn properties(&self) -> RpcResult; +} + +/// RPC methods of Substrate `chain` namespace, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "chain")] +pub(crate) trait SubstrateChain { + /// Get block hash by its number. + #[method(name = "getBlockHash")] + async fn block_hash(&self, block_number: Option) -> RpcResult; + /// Return block header by its hash. + #[method(name = "getHeader")] + async fn header(&self, block_hash: Option) -> RpcResult; + /// Return best finalized block hash. + #[method(name = "getFinalizedHead")] + async fn finalized_head(&self) -> RpcResult; + /// Return signed block (with justifications) by its hash. + #[method(name = "getBlock")] + async fn block(&self, block_hash: Option) -> RpcResult; +} + +/// RPC methods of Substrate `author` namespace, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "author")] +pub(crate) trait SubstrateAuthor { + /// Submit extrinsic to the transaction pool. + #[method(name = "submitExtrinsic")] + async fn submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult; + /// Return vector of pending extrinsics from the transaction pool. + #[method(name = "pendingExtrinsics")] + async fn pending_extrinsics(&self) -> RpcResult>; + /// Submit and watch for extrinsic state. + #[subscription(name = "submitAndWatchExtrinsic", unsubscribe = "unwatchExtrinsic", item = TransactionStatusOf)] + fn submit_and_watch_extrinsic(&self, extrinsic: Bytes); +} + +/// RPC methods of Substrate `state` namespace, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "state")] +pub(crate) trait SubstrateState { + /// Get current runtime version. + #[method(name = "getRuntimeVersion")] + async fn runtime_version(&self) -> RpcResult; + /// Call given runtime method. + #[method(name = "call")] + async fn call( + &self, + method: String, + data: Bytes, + at_block: Option, + ) -> RpcResult; + /// Get value of the runtime storage. + #[method(name = "getStorage")] + async fn storage( + &self, + key: StorageKey, + at_block: Option, + ) -> RpcResult>; + /// Get proof of the runtime storage value. + #[method(name = "getReadProof")] + async fn prove_storage( + &self, + keys: Vec, + hash: Option, + ) -> RpcResult>; +} + +/// RPC methods that we are using for a certain finality gadget. +#[async_trait] +pub trait SubstrateFinalityClient { + /// Subscribe to finality justifications. + async fn subscribe_justifications(client: &WsClient) -> RpcResult>; +} + +/// RPC methods of Substrate `grandpa` namespace, that we are using. +#[rpc(client, client_bounds(C: ChainWithGrandpa), namespace = "grandpa")] +pub(crate) trait SubstrateGrandpa { + /// Subscribe to GRANDPA justifications. + #[subscription(name = "subscribeJustifications", unsubscribe = "unsubscribeJustifications", item = Bytes)] + fn subscribe_justifications(&self); +} + +/// RPC finality methods of Substrate `grandpa` namespace, that we are using. +pub struct SubstrateGrandpaFinalityClient; +#[async_trait] +impl SubstrateFinalityClient for SubstrateGrandpaFinalityClient { + async fn subscribe_justifications(client: &WsClient) -> RpcResult> { + SubstrateGrandpaClient::::subscribe_justifications(client).await + } +} + +// TODO: Use `ChainWithBeefy` instead of `Chain` after #1606 is merged +/// RPC methods of Substrate `beefy` namespace, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "beefy")] +pub(crate) trait SubstrateBeefy { + /// Subscribe to BEEFY justifications. + #[subscription(name = "subscribeJustifications", unsubscribe = "unsubscribeJustifications", item = Bytes)] + fn subscribe_justifications(&self); +} + +/// RPC finality methods of Substrate `beefy` namespace, that we are using. +pub struct SubstrateBeefyFinalityClient; +// TODO: Use `ChainWithBeefy` instead of `Chain` after #1606 is merged +#[async_trait] +impl SubstrateFinalityClient for SubstrateBeefyFinalityClient { + async fn subscribe_justifications(client: &WsClient) -> RpcResult> { + SubstrateBeefyClient::::subscribe_justifications(client).await + } +} + +/// RPC methods of Substrate `system` frame pallet, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "system")] +pub(crate) trait SubstrateFrameSystem { + /// Return index of next account transaction. + #[method(name = "accountNextIndex")] + async fn account_next_index(&self, account_id: C::AccountId) -> RpcResult; +} + +/// RPC methods of Substrate `pallet_transaction_payment` frame pallet, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "payment")] +pub(crate) trait SubstrateTransactionPayment { + /// Query transaction fee details. + #[method(name = "queryFeeDetails")] + async fn fee_details( + &self, + extrinsic: Bytes, + at_block: Option, + ) -> RpcResult>; +} diff --git a/relays/client-substrate/src/sync_header.rs b/relays/client-substrate/src/sync_header.rs new file mode 100644 index 00000000000..fdfd1f22ce9 --- /dev/null +++ b/relays/client-substrate/src/sync_header.rs @@ -0,0 +1,61 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use bp_header_chain::ConsensusLogReader; +use finality_relay::SourceHeader as FinalitySourceHeader; +use sp_runtime::traits::Header as HeaderT; + +/// Generic wrapper for `sp_runtime::traits::Header` based headers, that +/// implements `finality_relay::SourceHeader` and may be used in headers sync directly. +#[derive(Clone, Debug, PartialEq)] +pub struct SyncHeader
(Header); + +impl
SyncHeader
{ + /// Extracts wrapped header from self. + pub fn into_inner(self) -> Header { + self.0 + } +} + +impl
std::ops::Deref for SyncHeader
{ + type Target = Header; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl
From
for SyncHeader
{ + fn from(header: Header) -> Self { + Self(header) + } +} + +impl FinalitySourceHeader + for SyncHeader
+{ + fn hash(&self) -> Header::Hash { + self.0.hash() + } + + fn number(&self) -> Header::Number { + *self.0.number() + } + + fn is_mandatory(&self) -> bool { + R::schedules_authorities_change(self.digest()) + } +} diff --git a/relays/client-substrate/src/test_chain.rs b/relays/client-substrate/src/test_chain.rs new file mode 100644 index 00000000000..4589687d39d --- /dev/null +++ b/relays/client-substrate/src/test_chain.rs @@ -0,0 +1,68 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Pallet provides a set of guard functions that are running in background threads +//! and are aborting process if some condition fails. + +//! Test chain implementation to use in tests. + +#![cfg(any(feature = "test-helpers", test))] + +use crate::{Chain, ChainWithBalances}; +use frame_support::weights::Weight; +use std::time::Duration; + +/// Chain that may be used in tests. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TestChain; + +impl bp_runtime::Chain for TestChain { + type BlockNumber = u32; + type Hash = sp_core::H256; + type Hasher = sp_runtime::traits::BlakeTwo256; + type Header = sp_runtime::generic::Header; + + type AccountId = u32; + type Balance = u32; + type Index = u32; + type Signature = sp_runtime::testing::TestSignature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + + fn max_extrinsic_weight() -> Weight { + unreachable!() + } +} + +impl Chain for TestChain { + const NAME: &'static str = "Test"; + const TOKEN_ID: Option<&'static str> = None; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "TestMethod"; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(0); + + type SignedBlock = sp_runtime::generic::SignedBlock< + sp_runtime::generic::Block, + >; + type Call = (); +} + +impl ChainWithBalances for TestChain { + fn account_info_storage_key(_account_id: &u32) -> sp_core::storage::StorageKey { + unreachable!() + } +} diff --git a/relays/client-substrate/src/transaction_tracker.rs b/relays/client-substrate/src/transaction_tracker.rs new file mode 100644 index 00000000000..211f7faab0e --- /dev/null +++ b/relays/client-substrate/src/transaction_tracker.rs @@ -0,0 +1,447 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Helper for tracking transaction invalidation events. + +use crate::{Chain, Client, Error, HashOf, HeaderIdOf, Subscription, TransactionStatusOf}; + +use async_trait::async_trait; +use futures::{future::Either, Future, FutureExt, Stream, StreamExt}; +use relay_utils::{HeaderId, TrackedTransactionStatus}; +use sp_runtime::traits::Header as _; +use std::time::Duration; + +/// Transaction tracker environment. +#[async_trait] +pub trait Environment: Send + Sync { + /// Returns header id by its hash. + async fn header_id_by_hash(&self, hash: HashOf) -> Result, Error>; +} + +#[async_trait] +impl Environment for Client { + async fn header_id_by_hash(&self, hash: HashOf) -> Result, Error> { + self.header_by_hash(hash).await.map(|h| HeaderId(*h.number(), hash)) + } +} + +/// Substrate transaction tracker implementation. +/// +/// Substrate node provides RPC API to submit and watch for transaction events. This way +/// we may know when transaction is included into block, finalized or rejected. There are +/// some edge cases, when we can't fully trust this mechanism - e.g. transaction may broadcasted +/// and then dropped out of node transaction pool (some other cases are also possible - node +/// restarts, connection lost, ...). Then we can't know for sure - what is currently happening +/// with our transaction. Is the transaction really lost? Is it still alive on the chain network? +/// +/// We have several options to handle such cases: +/// +/// 1) hope that the transaction is still alive and wait for its mining until it is spoiled; +/// +/// 2) assume that the transaction is lost and resubmit another transaction instantly; +/// +/// 3) wait for some time (if transaction is mortal - then until block where it dies; if it is +/// immortal - then for some time that we assume is long enough to mine it) and assume that +/// it is lost. +/// +/// This struct implements third option as it seems to be the most optimal. +pub struct TransactionTracker { + environment: E, + transaction_hash: HashOf, + stall_timeout: Duration, + subscription: Subscription>, +} + +impl> TransactionTracker { + /// Create transaction tracker. + pub fn new( + environment: E, + stall_timeout: Duration, + transaction_hash: HashOf, + subscription: Subscription>, + ) -> Self { + Self { environment, stall_timeout, transaction_hash, subscription } + } + + /// Wait for final transaction status and return it along with last known internal invalidation + /// status. + async fn do_wait( + self, + wait_for_stall_timeout: impl Future, + wait_for_stall_timeout_rest: impl Future, + ) -> (TrackedTransactionStatus>, Option>>) { + // sometimes we want to wait for the rest of the stall timeout even if + // `wait_for_invalidation` has been "select"ed first => it is shared + let wait_for_invalidation = watch_transaction_status::<_, C, _>( + self.environment, + self.transaction_hash, + self.subscription.into_stream(), + ); + futures::pin_mut!(wait_for_stall_timeout, wait_for_invalidation); + + match futures::future::select(wait_for_stall_timeout, wait_for_invalidation).await { + Either::Left((_, _)) => { + log::trace!( + target: "bridge", + "{} transaction {:?} is considered lost after timeout (no status response from the node)", + C::NAME, + self.transaction_hash, + ); + + (TrackedTransactionStatus::Lost, None) + }, + Either::Right((invalidation_status, _)) => match invalidation_status { + InvalidationStatus::Finalized(at_block) => + (TrackedTransactionStatus::Finalized(at_block), Some(invalidation_status)), + InvalidationStatus::Invalid => + (TrackedTransactionStatus::Lost, Some(invalidation_status)), + InvalidationStatus::Lost => { + // wait for the rest of stall timeout - this way we'll be sure that the + // transaction is actually dead if it has been crafted properly + wait_for_stall_timeout_rest.await; + // if someone is still watching for our transaction, then we're reporting + // an error here (which is treated as "transaction lost") + log::trace!( + target: "bridge", + "{} transaction {:?} is considered lost after timeout", + C::NAME, + self.transaction_hash, + ); + + (TrackedTransactionStatus::Lost, Some(invalidation_status)) + }, + }, + } + } +} + +#[async_trait] +impl> relay_utils::TransactionTracker for TransactionTracker { + type HeaderId = HeaderIdOf; + + async fn wait(self) -> TrackedTransactionStatus> { + let wait_for_stall_timeout = async_std::task::sleep(self.stall_timeout).shared(); + let wait_for_stall_timeout_rest = wait_for_stall_timeout.clone(); + self.do_wait(wait_for_stall_timeout, wait_for_stall_timeout_rest).await.0 + } +} + +/// Transaction invalidation status. +/// +/// Note that in places where the `TransactionTracker` is used, the finalization event will be +/// ignored - relay loops are detecting the mining/finalization using their own +/// techniques. That's why we're using `InvalidationStatus` here. +#[derive(Debug, PartialEq)] +enum InvalidationStatus { + /// Transaction has been included into block and finalized at given block. + Finalized(BlockId), + /// Transaction has been invalidated. + Invalid, + /// We have lost track of transaction status. + Lost, +} + +/// Watch for transaction status until transaction is finalized or we lose track of its status. +async fn watch_transaction_status< + E: Environment, + C: Chain, + S: Stream>, +>( + environment: E, + transaction_hash: HashOf, + subscription: S, +) -> InvalidationStatus> { + futures::pin_mut!(subscription); + + loop { + match subscription.next().await { + Some(TransactionStatusOf::::Finalized((block_hash, _))) => { + // the only "successful" outcome of this method is when the block with transaction + // has been finalized + log::trace!( + target: "bridge", + "{} transaction {:?} has been finalized at block: {:?}", + C::NAME, + transaction_hash, + block_hash, + ); + + let header_id = match environment.header_id_by_hash(block_hash).await { + Ok(header_id) => header_id, + Err(e) => { + log::error!( + target: "bridge", + "Failed to read header {:?} when watching for {} transaction {:?}: {:?}", + block_hash, + C::NAME, + transaction_hash, + e, + ); + // that's the best option we have here + return InvalidationStatus::Lost + }, + }; + return InvalidationStatus::Finalized(header_id) + }, + Some(TransactionStatusOf::::Invalid) => { + // if node says that the transaction is invalid, there are still chances that + // it is not actually invalid - e.g. if the block where transaction has been + // revalidated is retracted and transaction (at some other node pool) becomes + // valid again on other fork. But let's assume that the chances of this event + // are almost zero - there's a lot of things that must happen for this to be the + // case. + log::trace!( + target: "bridge", + "{} transaction {:?} has been invalidated", + C::NAME, + transaction_hash, + ); + return InvalidationStatus::Invalid + }, + Some(TransactionStatusOf::::Future) | + Some(TransactionStatusOf::::Ready) | + Some(TransactionStatusOf::::Broadcast(_)) => { + // nothing important (for us) has happened + }, + Some(TransactionStatusOf::::InBlock(block_hash)) => { + // TODO: read matching system event (ExtrinsicSuccess or ExtrinsicFailed), log it + // here and use it later (on finality) for reporting invalid transaction + // https://github.com/paritytech/parity-bridges-common/issues/1464 + log::trace!( + target: "bridge", + "{} transaction {:?} has been included in block: {:?}", + C::NAME, + transaction_hash, + block_hash, + ); + }, + Some(TransactionStatusOf::::Retracted(block_hash)) => { + log::trace!( + target: "bridge", + "{} transaction {:?} at block {:?} has been retracted", + C::NAME, + transaction_hash, + block_hash, + ); + }, + Some(TransactionStatusOf::::FinalityTimeout(block_hash)) => { + // finality is lagging? let's wait a bit more and report a stall + log::trace!( + target: "bridge", + "{} transaction {:?} block {:?} has not been finalized for too long", + C::NAME, + transaction_hash, + block_hash, + ); + return InvalidationStatus::Lost + }, + Some(TransactionStatusOf::::Usurped(new_transaction_hash)) => { + // this may be result of our transaction resubmitter work or some manual + // intervention. In both cases - let's start stall timeout, because the meaning + // of transaction may have changed + log::trace!( + target: "bridge", + "{} transaction {:?} has been usurped by new transaction: {:?}", + C::NAME, + transaction_hash, + new_transaction_hash, + ); + return InvalidationStatus::Lost + }, + Some(TransactionStatusOf::::Dropped) => { + // the transaction has been removed from the pool because of its limits. Let's wait + // a bit and report a stall + log::trace!( + target: "bridge", + "{} transaction {:?} has been dropped from the pool", + C::NAME, + transaction_hash, + ); + return InvalidationStatus::Lost + }, + None => { + // the status of transaction is unknown to us (the subscription has been closed?). + // Let's wait a bit and report a stall + return InvalidationStatus::Lost + }, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_chain::TestChain; + use futures::{FutureExt, SinkExt}; + use sc_transaction_pool_api::TransactionStatus; + + struct TestEnvironment(Result, Error>); + + #[async_trait] + impl Environment for TestEnvironment { + async fn header_id_by_hash( + &self, + _hash: HashOf, + ) -> Result, Error> { + self.0.as_ref().map_err(|_| Error::BridgePalletIsNotInitialized).cloned() + } + } + + async fn on_transaction_status( + status: TransactionStatus, HashOf>, + ) -> Option<( + TrackedTransactionStatus>, + InvalidationStatus>, + )> { + let (mut sender, receiver) = futures::channel::mpsc::channel(1); + let tx_tracker = TransactionTracker::::new( + TestEnvironment(Ok(HeaderId(0, Default::default()))), + Duration::from_secs(0), + Default::default(), + Subscription(async_std::sync::Mutex::new(receiver)), + ); + + let wait_for_stall_timeout = futures::future::pending(); + let wait_for_stall_timeout_rest = futures::future::ready(()); + sender.send(Some(status)).await.unwrap(); + tx_tracker + .do_wait(wait_for_stall_timeout, wait_for_stall_timeout_rest) + .now_or_never() + .map(|(ts, is)| (ts, is.unwrap())) + } + + #[async_std::test] + async fn returns_finalized_on_finalized() { + assert_eq!( + on_transaction_status(TransactionStatus::Finalized(Default::default())).await, + Some(( + TrackedTransactionStatus::Finalized(Default::default()), + InvalidationStatus::Finalized(Default::default()) + )), + ); + } + + #[async_std::test] + async fn returns_lost_on_finalized_and_environment_error() { + assert_eq!( + watch_transaction_status::<_, TestChain, _>( + TestEnvironment(Err(Error::BridgePalletIsNotInitialized)), + Default::default(), + futures::stream::iter([TransactionStatus::Finalized(Default::default())]) + ) + .now_or_never(), + Some(InvalidationStatus::Lost), + ); + } + + #[async_std::test] + async fn returns_invalid_on_invalid() { + assert_eq!( + on_transaction_status(TransactionStatus::Invalid).await, + Some((TrackedTransactionStatus::Lost, InvalidationStatus::Invalid)), + ); + } + + #[async_std::test] + async fn waits_on_future() { + assert_eq!(on_transaction_status(TransactionStatus::Future).await, None,); + } + + #[async_std::test] + async fn waits_on_ready() { + assert_eq!(on_transaction_status(TransactionStatus::Ready).await, None,); + } + + #[async_std::test] + async fn waits_on_broadcast() { + assert_eq!( + on_transaction_status(TransactionStatus::Broadcast(Default::default())).await, + None, + ); + } + + #[async_std::test] + async fn waits_on_in_block() { + assert_eq!( + on_transaction_status(TransactionStatus::InBlock(Default::default())).await, + None, + ); + } + + #[async_std::test] + async fn waits_on_retracted() { + assert_eq!( + on_transaction_status(TransactionStatus::Retracted(Default::default())).await, + None, + ); + } + + #[async_std::test] + async fn lost_on_finality_timeout() { + assert_eq!( + on_transaction_status(TransactionStatus::FinalityTimeout(Default::default())).await, + Some((TrackedTransactionStatus::Lost, InvalidationStatus::Lost)), + ); + } + + #[async_std::test] + async fn lost_on_usurped() { + assert_eq!( + on_transaction_status(TransactionStatus::Usurped(Default::default())).await, + Some((TrackedTransactionStatus::Lost, InvalidationStatus::Lost)), + ); + } + + #[async_std::test] + async fn lost_on_dropped() { + assert_eq!( + on_transaction_status(TransactionStatus::Dropped).await, + Some((TrackedTransactionStatus::Lost, InvalidationStatus::Lost)), + ); + } + + #[async_std::test] + async fn lost_on_subscription_error() { + assert_eq!( + watch_transaction_status::<_, TestChain, _>( + TestEnvironment(Ok(HeaderId(0, Default::default()))), + Default::default(), + futures::stream::iter([]) + ) + .now_or_never(), + Some(InvalidationStatus::Lost), + ); + } + + #[async_std::test] + async fn lost_on_timeout_when_waiting_for_invalidation_status() { + let (_sender, receiver) = futures::channel::mpsc::channel(1); + let tx_tracker = TransactionTracker::::new( + TestEnvironment(Ok(HeaderId(0, Default::default()))), + Duration::from_secs(0), + Default::default(), + Subscription(async_std::sync::Mutex::new(receiver)), + ); + + let wait_for_stall_timeout = futures::future::ready(()).shared(); + let wait_for_stall_timeout_rest = wait_for_stall_timeout.clone(); + let wait_result = tx_tracker + .do_wait(wait_for_stall_timeout, wait_for_stall_timeout_rest) + .now_or_never(); + + assert_eq!(wait_result, Some((TrackedTransactionStatus::Lost, None))); + } +} diff --git a/relays/client-westend/Cargo.toml b/relays/client-westend/Cargo.toml new file mode 100644 index 00000000000..57d2ca3b1e7 --- /dev/null +++ b/relays/client-westend/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "relay-westend-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-westend = { path = "../../primitives/chain-westend" } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-westend/src/lib.rs b/relays/client-westend/src/lib.rs new file mode 100644 index 00000000000..9132a38710a --- /dev/null +++ b/relays/client-westend/src/lib.rs @@ -0,0 +1,119 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Westend chain. + +use frame_support::weights::Weight; +use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, RelayChain}; +use sp_core::storage::StorageKey; +use std::time::Duration; + +/// Westend header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Westend header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + +/// Westend chain definition +#[derive(Debug, Clone, Copy)] +pub struct Westend; + +impl ChainBase for Westend { + type BlockNumber = bp_westend::BlockNumber; + type Hash = bp_westend::Hash; + type Hasher = bp_westend::Hasher; + type Header = bp_westend::Header; + + type AccountId = bp_westend::AccountId; + type Balance = bp_westend::Balance; + type Index = bp_westend::Nonce; + type Signature = bp_westend::Signature; + + fn max_extrinsic_size() -> u32 { + bp_westend::Westend::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_westend::Westend::max_extrinsic_weight() + } +} + +impl Chain for Westend { + const NAME: &'static str = "Westend"; + const TOKEN_ID: Option<&'static str> = None; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_westend::BEST_FINALIZED_WESTEND_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type SignedBlock = bp_westend::SignedBlock; + type Call = (); +} + +impl RelayChain for Westend { + const PARAS_PALLET_NAME: &'static str = bp_westend::PARAS_PALLET_NAME; + const PARACHAINS_FINALITY_PALLET_NAME: &'static str = + bp_westend::WITH_WESTEND_BRIDGE_PARAS_PALLET_NAME; +} + +impl ChainWithGrandpa for Westend { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = + bp_westend::WITH_WESTEND_GRANDPA_PALLET_NAME; +} + +impl ChainWithBalances for Westend { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + bp_westend::AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +/// Westmint parachain definition +#[derive(Debug, Clone, Copy)] +pub struct Westmint; + +// Westmint seems to use the same configuration as all Polkadot-like chains, so we'll use Westend +// primitives here. +impl ChainBase for Westmint { + type BlockNumber = bp_westend::BlockNumber; + type Hash = bp_westend::Hash; + type Hasher = bp_westend::Hasher; + type Header = bp_westend::Header; + + type AccountId = bp_westend::AccountId; + type Balance = bp_westend::Balance; + type Index = bp_westend::Nonce; + type Signature = bp_westend::Signature; + + fn max_extrinsic_size() -> u32 { + bp_westend::Westend::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_westend::Westend::max_extrinsic_weight() + } +} + +// Westmint seems to use the same configuration as all Polkadot-like chains, so we'll use Westend +// primitives here. +impl Chain for Westmint { + const NAME: &'static str = "Westmint"; + const TOKEN_ID: Option<&'static str> = None; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_westend::BEST_FINALIZED_WESTMINT_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type SignedBlock = bp_westend::SignedBlock; + type Call = (); +} diff --git a/relays/client-wococo/Cargo.toml b/relays/client-wococo/Cargo.toml new file mode 100644 index 00000000000..5b97694af1c --- /dev/null +++ b/relays/client-wococo/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "relay-wococo-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-wococo = { path = "../../primitives/chain-wococo" } + +# Substrate Dependencies +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-wococo/src/lib.rs b/relays/client-wococo/src/lib.rs new file mode 100644 index 00000000000..6fc3714f667 --- /dev/null +++ b/relays/client-wococo/src/lib.rs @@ -0,0 +1,78 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Wococo-Substrate chain. + +use frame_support::weights::Weight; +use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, RelayChain}; +use sp_core::storage::StorageKey; +use std::time::Duration; + +/// Wococo header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Wococo header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + +/// Wococo chain definition +#[derive(Debug, Clone, Copy)] +pub struct Wococo; + +impl ChainBase for Wococo { + type BlockNumber = bp_wococo::BlockNumber; + type Hash = bp_wococo::Hash; + type Hasher = bp_wococo::Hashing; + type Header = bp_wococo::Header; + + type AccountId = bp_wococo::AccountId; + type Balance = bp_wococo::Balance; + type Index = bp_wococo::Nonce; + type Signature = bp_wococo::Signature; + + fn max_extrinsic_size() -> u32 { + bp_wococo::Wococo::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_wococo::Wococo::max_extrinsic_weight() + } +} + +impl Chain for Wococo { + const NAME: &'static str = "Wococo"; + const TOKEN_ID: Option<&'static str> = None; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type SignedBlock = bp_wococo::SignedBlock; + type Call = (); +} + +impl ChainWithGrandpa for Wococo { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_wococo::WITH_WOCOCO_GRANDPA_PALLET_NAME; +} + +impl ChainWithBalances for Wococo { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + bp_wococo::AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +impl RelayChain for Wococo { + const PARAS_PALLET_NAME: &'static str = bp_wococo::PARAS_PALLET_NAME; + const PARACHAINS_FINALITY_PALLET_NAME: &'static str = "bridgeWococoParachain"; +} diff --git a/relays/finality/Cargo.toml b/relays/finality/Cargo.toml new file mode 100644 index 00000000000..ae4be5b5524 --- /dev/null +++ b/relays/finality/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "finality-relay" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +description = "Finality proofs relay" + +[dependencies] +async-std = "1.6.5" +async-trait = "0.1" +backoff = "0.2" +bp-header-chain = { path = "../../primitives/header-chain" } +futures = "0.3.5" +log = "0.4.17" +num-traits = "0.2" +relay-utils = { path = "../utils" } + +[dev-dependencies] +parking_lot = "0.11.0" diff --git a/relays/finality/src/finality_loop.rs b/relays/finality/src/finality_loop.rs new file mode 100644 index 00000000000..1ee1a8d9db6 --- /dev/null +++ b/relays/finality/src/finality_loop.rs @@ -0,0 +1,761 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! The loop basically reads all missing headers and their finality proofs from the source client. +//! The proof for the best possible header is then submitted to the target node. The only exception +//! is the mandatory headers, which we always submit to the target node. For such headers, we +//! assume that the persistent proof either exists, or will eventually become available. + +use crate::{ + sync_loop_metrics::SyncLoopMetrics, FinalityProof, FinalitySyncPipeline, SourceHeader, +}; + +use async_trait::async_trait; +use backoff::backoff::Backoff; +use futures::{select, Future, FutureExt, Stream, StreamExt}; +use num_traits::{One, Saturating}; +use relay_utils::{ + metrics::MetricsParams, relay_loop::Client as RelayClient, retry_backoff, FailedClient, + HeaderId, MaybeConnectionError, TrackedTransactionStatus, TransactionTracker, +}; +use std::{ + fmt::Debug, + pin::Pin, + time::{Duration, Instant}, +}; + +/// Finality proof synchronization loop parameters. +#[derive(Debug, Clone)] +pub struct FinalitySyncParams { + /// Interval at which we check updates on both clients. Normally should be larger than + /// `min(source_block_time, target_block_time)`. + /// + /// This parameter may be used to limit transactions rate. Increase the value && you'll get + /// infrequent updates => sparse headers => potential slow down of bridge applications, but + /// pallet storage won't be super large. Decrease the value to near `source_block_time` and + /// you'll get transaction for (almost) every block of the source chain => all source headers + /// will be known to the target chain => bridge applications will run faster, but pallet + /// storage may explode (but if pruning is there, then it's fine). + pub tick: Duration, + /// Number of finality proofs to keep in internal buffer between loop iterations. + /// + /// While in "major syncing" state, we still read finality proofs from the stream. They're + /// stored in the internal buffer between loop iterations. When we're close to the tip of the + /// chain, we may meet finality delays if headers are not finalized frequently. So instead of + /// waiting for next finality proof to appear in the stream, we may use existing proof from + /// that buffer. + pub recent_finality_proofs_limit: usize, + /// Timeout before we treat our transactions as lost and restart the whole sync process. + pub stall_timeout: Duration, + /// If true, only mandatory headers are relayed. + pub only_mandatory_headers: bool, +} + +/// Source client used in finality synchronization loop. +#[async_trait] +pub trait SourceClient: RelayClient { + /// Stream of new finality proofs. The stream is allowed to miss proofs for some + /// headers, even if those headers are mandatory. + type FinalityProofsStream: Stream + Send; + + /// Get best finalized block number. + async fn best_finalized_block_number(&self) -> Result; + + /// Get canonical header and its finality proof by number. + async fn header_and_finality_proof( + &self, + number: P::Number, + ) -> Result<(P::Header, Option), Self::Error>; + + /// Subscribe to new finality proofs. + async fn finality_proofs(&self) -> Result; +} + +/// Target client used in finality synchronization loop. +#[async_trait] +pub trait TargetClient: RelayClient { + /// Transaction tracker to track submitted transactions. + type TransactionTracker: TransactionTracker; + + /// Get best finalized source block number. + async fn best_finalized_source_block_id( + &self, + ) -> Result, Self::Error>; + + /// Submit header finality proof. + async fn submit_finality_proof( + &self, + header: P::Header, + proof: P::FinalityProof, + ) -> Result; +} + +/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs +/// sync loop. +pub fn metrics_prefix() -> String { + format!("{}_to_{}_Sync", P::SOURCE_NAME, P::TARGET_NAME) +} + +/// Run finality proofs synchronization loop. +pub async fn run( + source_client: impl SourceClient

, + target_client: impl TargetClient

, + sync_params: FinalitySyncParams, + metrics_params: MetricsParams, + exit_signal: impl Future + 'static + Send, +) -> Result<(), relay_utils::Error> { + let exit_signal = exit_signal.shared(); + relay_utils::relay_loop(source_client, target_client) + .with_metrics(metrics_params) + .loop_metric(SyncLoopMetrics::new( + Some(&metrics_prefix::

()), + "source", + "source_at_target", + )?)? + .expose() + .await? + .run(metrics_prefix::

(), move |source_client, target_client, metrics| { + run_until_connection_lost( + source_client, + target_client, + sync_params.clone(), + metrics, + exit_signal.clone(), + ) + }) + .await +} + +/// Unjustified headers container. Ordered by header number. +pub(crate) type UnjustifiedHeaders = Vec; +/// Finality proofs container. Ordered by target header number. +pub(crate) type FinalityProofs

= + Vec<(

::Number,

::FinalityProof)>; +/// Reference to finality proofs container. +pub(crate) type FinalityProofsRef<'a, P> = + &'a [(

::Number,

::FinalityProof)]; + +/// Error that may happen inside finality synchronization loop. +#[derive(Debug)] +pub(crate) enum Error { + /// Source client request has failed with given error. + Source(SourceError), + /// Target client request has failed with given error. + Target(TargetError), + /// Finality proof for mandatory header is missing from the source node. + MissingMandatoryFinalityProof(P::Number), +} + +impl Error +where + P: FinalitySyncPipeline, + SourceError: MaybeConnectionError, + TargetError: MaybeConnectionError, +{ + fn fail_if_connection_error(&self) -> Result<(), FailedClient> { + match *self { + Error::Source(ref error) if error.is_connection_error() => Err(FailedClient::Source), + Error::Target(ref error) if error.is_connection_error() => Err(FailedClient::Target), + _ => Ok(()), + } + } +} + +/// Information about transaction that we have submitted. +#[derive(Debug, Clone)] +pub(crate) struct Transaction { + /// Submitted transaction tracker. + pub tracker: Tracker, + /// The number of the header we have submitted. + pub submitted_header_number: Number, +} + +impl Transaction { + pub async fn submit< + C: TargetClient, + P: FinalitySyncPipeline, + >( + target_client: &C, + header: P::Header, + justification: P::FinalityProof, + ) -> Result { + let submitted_header_number = header.number(); + log::debug!( + target: "bridge", + "Going to submit finality proof of {} header #{:?} to {}", + P::SOURCE_NAME, + submitted_header_number, + P::TARGET_NAME, + ); + + let tracker = target_client.submit_finality_proof(header, justification).await?; + Ok(Transaction { tracker, submitted_header_number }) + } + + pub async fn track, P: FinalitySyncPipeline>( + self, + target_client: &C, + ) -> Result<(), String> { + match self.tracker.wait().await { + TrackedTransactionStatus::Finalized(_) => { + // The transaction has been finalized, but it may have been finalized in the + // "failed" state. So let's check if the block number was actually updated. + // If it wasn't then we are stalled. + // + // Please also note that we're returning an error if we fail to read required data + // from the target client - that's the best we can do here to avoid actual stall. + target_client + .best_finalized_source_block_id() + .await + .map_err(|e| format!("failed to read best block from target node: {e:?}")) + .and_then(|best_id_at_target| { + if self.submitted_header_number > best_id_at_target.0 { + return Err(format!( + "best block at target after tx is {:?} and we've submitted {:?}", + best_id_at_target.0, self.submitted_header_number, + )) + } + Ok(()) + }) + }, + TrackedTransactionStatus::Lost => Err("transaction failed".to_string()), + } + } +} + +/// Finality proofs stream that may be restarted. +pub(crate) struct RestartableFinalityProofsStream { + /// Flag that the stream needs to be restarted. + pub(crate) needs_restart: bool, + /// The stream itself. + stream: Pin>, +} + +impl RestartableFinalityProofsStream { + pub async fn create_raw_stream< + C: SourceClient, + P: FinalitySyncPipeline, + >( + source_client: &C, + ) -> Result { + source_client.finality_proofs().await.map_err(|error| { + log::error!( + target: "bridge", + "Failed to subscribe to {} justifications: {:?}. Going to reconnect", + P::SOURCE_NAME, + error, + ); + + FailedClient::Source + }) + } + + pub async fn restart_if_scheduled< + C: SourceClient, + P: FinalitySyncPipeline, + >( + &mut self, + source_client: &C, + ) -> Result<(), FailedClient> { + if self.needs_restart { + log::warn!(target: "bridge", "{} finality proofs stream is being restarted", P::SOURCE_NAME); + + self.needs_restart = false; + self.stream = Box::pin(Self::create_raw_stream(source_client).await?); + } + Ok(()) + } + + pub fn next(&mut self) -> Option { + match self.stream.next().now_or_never() { + Some(Some(finality_proof)) => Some(finality_proof), + Some(None) => { + self.needs_restart = true; + None + }, + None => None, + } + } +} + +impl From for RestartableFinalityProofsStream { + fn from(stream: S) -> Self { + RestartableFinalityProofsStream { needs_restart: false, stream: Box::pin(stream) } + } +} + +/// Finality synchronization loop state. +pub(crate) struct FinalityLoopState<'a, P: FinalitySyncPipeline, FinalityProofsStream> { + /// Synchronization loop progress. + pub(crate) progress: &'a mut (Instant, Option), + /// Finality proofs stream. + pub(crate) finality_proofs_stream: + &'a mut RestartableFinalityProofsStream, + /// Recent finality proofs that we have read from the stream. + pub(crate) recent_finality_proofs: &'a mut FinalityProofs

, + /// Number of the last header, submitted to the target node. + pub(crate) submitted_header_number: Option, +} + +/// Run finality relay loop until connection to one of nodes is lost. +pub(crate) async fn run_until_connection_lost( + source_client: impl SourceClient

, + target_client: impl TargetClient

, + sync_params: FinalitySyncParams, + metrics_sync: Option, + exit_signal: impl Future, +) -> Result<(), FailedClient> { + let last_transaction_tracker = futures::future::Fuse::terminated(); + let exit_signal = exit_signal.fuse(); + futures::pin_mut!(last_transaction_tracker, exit_signal); + + let mut finality_proofs_stream = + RestartableFinalityProofsStream::create_raw_stream(&source_client).await?.into(); + let mut recent_finality_proofs = Vec::new(); + + let mut progress = (Instant::now(), None); + let mut retry_backoff = retry_backoff(); + let mut last_submitted_header_number = None; + + loop { + // run loop iteration + let iteration_result = run_loop_iteration( + &source_client, + &target_client, + FinalityLoopState { + progress: &mut progress, + finality_proofs_stream: &mut finality_proofs_stream, + recent_finality_proofs: &mut recent_finality_proofs, + submitted_header_number: last_submitted_header_number, + }, + &sync_params, + &metrics_sync, + ) + .await; + + // deal with errors + let next_tick = match iteration_result { + Ok(Some(updated_transaction)) => { + last_submitted_header_number = Some(updated_transaction.submitted_header_number); + last_transaction_tracker.set(updated_transaction.track(&target_client).fuse()); + retry_backoff.reset(); + sync_params.tick + }, + Ok(None) => { + retry_backoff.reset(); + sync_params.tick + }, + Err(error) => { + log::error!(target: "bridge", "Finality sync loop iteration has failed with error: {:?}", error); + error.fail_if_connection_error()?; + retry_backoff.next_backoff().unwrap_or(relay_utils::relay_loop::RECONNECT_DELAY) + }, + }; + finality_proofs_stream.restart_if_scheduled(&source_client).await?; + + // wait till exit signal, or new source block + select! { + transaction_result = last_transaction_tracker => { + transaction_result.map_err(|e| { + log::error!( + target: "bridge", + "Finality synchronization from {} to {} has stalled with error: {}. Going to restart", + P::SOURCE_NAME, + P::TARGET_NAME, + e, + ); + + // Restart the loop if we're stalled. + FailedClient::Both + })? + }, + _ = async_std::task::sleep(next_tick).fuse() => {}, + _ = exit_signal => return Ok(()), + } + } +} + +pub(crate) async fn run_loop_iteration( + source_client: &SC, + target_client: &TC, + state: FinalityLoopState<'_, P, SC::FinalityProofsStream>, + sync_params: &FinalitySyncParams, + metrics_sync: &Option, +) -> Result>, Error> +where + P: FinalitySyncPipeline, + SC: SourceClient

, + TC: TargetClient

, +{ + // read best source headers ids from source and target nodes + let best_number_at_source = + source_client.best_finalized_block_number().await.map_err(Error::Source)?; + let best_id_at_target = + target_client.best_finalized_source_block_id().await.map_err(Error::Target)?; + let best_number_at_target = best_id_at_target.0; + + let different_hash_at_source = ensure_same_fork::(&best_id_at_target, source_client) + .await + .map_err(Error::Source)?; + let using_same_fork = different_hash_at_source.is_none(); + if let Some(ref different_hash_at_source) = different_hash_at_source { + log::error!( + target: "bridge", + "Source node ({}) and pallet at target node ({}) have different headers at the same height {:?}: \ + at-source {:?} vs at-target {:?}", + P::SOURCE_NAME, + P::TARGET_NAME, + best_number_at_target, + different_hash_at_source, + best_id_at_target.1, + ); + } + + if let Some(ref metrics_sync) = *metrics_sync { + metrics_sync.update_best_block_at_source(best_number_at_source); + metrics_sync.update_best_block_at_target(best_number_at_target); + metrics_sync.update_using_same_fork(using_same_fork); + } + *state.progress = + print_sync_progress::

(*state.progress, best_number_at_source, best_number_at_target); + + // if we have already submitted header, then we just need to wait for it + // if we're waiting too much, then we believe our transaction has been lost and restart sync + if let Some(submitted_header_number) = state.submitted_header_number { + if best_number_at_target >= submitted_header_number { + // transaction has been mined && we can continue + } else { + return Ok(None) + } + } + + // submit new header if we have something new + match select_header_to_submit( + source_client, + target_client, + state.finality_proofs_stream, + state.recent_finality_proofs, + best_number_at_source, + best_number_at_target, + sync_params, + ) + .await? + { + Some((header, justification)) => { + let transaction = Transaction::submit(target_client, header, justification) + .await + .map_err(Error::Target)?; + Ok(Some(transaction)) + }, + None => Ok(None), + } +} + +pub(crate) async fn select_header_to_submit( + source_client: &SC, + target_client: &TC, + finality_proofs_stream: &mut RestartableFinalityProofsStream, + recent_finality_proofs: &mut FinalityProofs

, + best_number_at_source: P::Number, + best_number_at_target: P::Number, + sync_params: &FinalitySyncParams, +) -> Result, Error> +where + P: FinalitySyncPipeline, + SC: SourceClient

, + TC: TargetClient

, +{ + // to see that the loop is progressing + log::trace!( + target: "bridge", + "Considering range of headers ({:?}; {:?}]", + best_number_at_target, + best_number_at_source, + ); + + // read missing headers. if we see that the header schedules GRANDPA change, we need to + // submit this header + let selected_finality_proof = read_missing_headers::( + source_client, + target_client, + best_number_at_source, + best_number_at_target, + ) + .await?; + let (mut unjustified_headers, mut selected_finality_proof) = match selected_finality_proof { + SelectedFinalityProof::Mandatory(header, finality_proof) => + return Ok(Some((header, finality_proof))), + _ if sync_params.only_mandatory_headers => { + // we are not reading finality proofs from the stream, so eventually it'll break + // but we don't care about transient proofs at all, so it is acceptable + return Ok(None) + }, + SelectedFinalityProof::Regular(unjustified_headers, header, finality_proof) => + (unjustified_headers, Some((header, finality_proof))), + SelectedFinalityProof::None(unjustified_headers) => (unjustified_headers, None), + }; + + // all headers that are missing from the target client are non-mandatory + // => even if we have already selected some header and its persistent finality proof, + // we may try to select better header by reading non-persistent proofs from the stream + read_finality_proofs_from_stream::(finality_proofs_stream, recent_finality_proofs); + selected_finality_proof = select_better_recent_finality_proof::

( + recent_finality_proofs, + &mut unjustified_headers, + selected_finality_proof, + ); + + // remove obsolete 'recent' finality proofs + keep its size under certain limit + let oldest_finality_proof_to_keep = selected_finality_proof + .as_ref() + .map(|(header, _)| header.number()) + .unwrap_or(best_number_at_target); + prune_recent_finality_proofs::

( + oldest_finality_proof_to_keep, + recent_finality_proofs, + sync_params.recent_finality_proofs_limit, + ); + + Ok(selected_finality_proof) +} + +/// Ensures that both clients are on the same fork. +/// +/// Returns `Some(_)` with header has at the source client if headers are different. +async fn ensure_same_fork>( + best_id_at_target: &HeaderId, + source_client: &SC, +) -> Result, SC::Error> { + let header_at_source = source_client.header_and_finality_proof(best_id_at_target.0).await?.0; + let header_hash_at_source = header_at_source.hash(); + Ok(if best_id_at_target.1 == header_hash_at_source { + None + } else { + Some(header_hash_at_source) + }) +} + +/// Finality proof that has been selected by the `read_missing_headers` function. +pub(crate) enum SelectedFinalityProof { + /// Mandatory header and its proof has been selected. We shall submit proof for this header. + Mandatory(Header, FinalityProof), + /// Regular header and its proof has been selected. We may submit this proof, or proof for + /// some better header. + Regular(UnjustifiedHeaders

, Header, FinalityProof), + /// We haven't found any missing header with persistent proof at the target client. + None(UnjustifiedHeaders
), +} + +/// Read missing headers and their persistent finality proofs from the target client. +/// +/// If we have found some header with known proof, it is returned. +/// Otherwise, `SelectedFinalityProof::None` is returned. +/// +/// Unless we have found mandatory header, all missing headers are collected and returned. +pub(crate) async fn read_missing_headers< + P: FinalitySyncPipeline, + SC: SourceClient

, + TC: TargetClient

, +>( + source_client: &SC, + _target_client: &TC, + best_number_at_source: P::Number, + best_number_at_target: P::Number, +) -> Result, Error> { + let mut unjustified_headers = Vec::new(); + let mut selected_finality_proof = None; + let mut header_number = best_number_at_target + One::one(); + while header_number <= best_number_at_source { + let (header, finality_proof) = source_client + .header_and_finality_proof(header_number) + .await + .map_err(Error::Source)?; + let is_mandatory = header.is_mandatory(); + + match (is_mandatory, finality_proof) { + (true, Some(finality_proof)) => { + log::trace!(target: "bridge", "Header {:?} is mandatory", header_number); + return Ok(SelectedFinalityProof::Mandatory(header, finality_proof)) + }, + (true, None) => return Err(Error::MissingMandatoryFinalityProof(header.number())), + (false, Some(finality_proof)) => { + log::trace!(target: "bridge", "Header {:?} has persistent finality proof", header_number); + unjustified_headers.clear(); + selected_finality_proof = Some((header, finality_proof)); + }, + (false, None) => { + unjustified_headers.push(header); + }, + } + + header_number = header_number + One::one(); + } + + log::trace!( + target: "bridge", + "Read {} {} headers. Selected finality proof for header: {:?}", + best_number_at_source.saturating_sub(best_number_at_target), + P::SOURCE_NAME, + selected_finality_proof.as_ref().map(|(header, _)| header), + ); + + Ok(match selected_finality_proof { + Some((header, proof)) => SelectedFinalityProof::Regular(unjustified_headers, header, proof), + None => SelectedFinalityProof::None(unjustified_headers), + }) +} + +/// Read finality proofs from the stream. +pub(crate) fn read_finality_proofs_from_stream< + P: FinalitySyncPipeline, + FPS: Stream, +>( + finality_proofs_stream: &mut RestartableFinalityProofsStream, + recent_finality_proofs: &mut FinalityProofs

, +) { + let mut proofs_count = 0; + let mut first_header_number = None; + let mut last_header_number = None; + while let Some(finality_proof) = finality_proofs_stream.next() { + let target_header_number = finality_proof.target_header_number(); + if first_header_number.is_none() { + first_header_number = Some(target_header_number); + } + last_header_number = Some(target_header_number); + proofs_count += 1; + + recent_finality_proofs.push((target_header_number, finality_proof)); + } + + if proofs_count != 0 { + log::trace!( + target: "bridge", + "Read {} finality proofs from {} finality stream for headers in range [{:?}; {:?}]", + proofs_count, + P::SOURCE_NAME, + first_header_number, + last_header_number, + ); + } +} + +/// Try to select better header and its proof, given finality proofs that we +/// have recently read from the stream. +pub(crate) fn select_better_recent_finality_proof( + recent_finality_proofs: FinalityProofsRef

, + unjustified_headers: &mut UnjustifiedHeaders, + selected_finality_proof: Option<(P::Header, P::FinalityProof)>, +) -> Option<(P::Header, P::FinalityProof)> { + if unjustified_headers.is_empty() || recent_finality_proofs.is_empty() { + log::trace!( + target: "bridge", + "Can not improve selected {} finality proof {:?}. No unjustified headers and recent proofs", + P::SOURCE_NAME, + selected_finality_proof.as_ref().map(|(h, _)| h.number()), + ); + return selected_finality_proof + } + + const NOT_EMPTY_PROOF: &str = "we have checked that the vec is not empty; qed"; + + // we need proofs for headers in range unjustified_range_begin..=unjustified_range_end + let unjustified_range_begin = unjustified_headers.first().expect(NOT_EMPTY_PROOF).number(); + let unjustified_range_end = unjustified_headers.last().expect(NOT_EMPTY_PROOF).number(); + + // we have proofs for headers in range buffered_range_begin..=buffered_range_end + let buffered_range_begin = recent_finality_proofs.first().expect(NOT_EMPTY_PROOF).0; + let buffered_range_end = recent_finality_proofs.last().expect(NOT_EMPTY_PROOF).0; + + // we have two ranges => find intersection + let intersection_begin = std::cmp::max(unjustified_range_begin, buffered_range_begin); + let intersection_end = std::cmp::min(unjustified_range_end, buffered_range_end); + let intersection = intersection_begin..=intersection_end; + + // find last proof from intersection + let selected_finality_proof_index = recent_finality_proofs + .binary_search_by_key(intersection.end(), |(number, _)| *number) + .unwrap_or_else(|index| index.saturating_sub(1)); + let (selected_header_number, finality_proof) = + &recent_finality_proofs[selected_finality_proof_index]; + let has_selected_finality_proof = intersection.contains(selected_header_number); + log::trace!( + target: "bridge", + "Trying to improve selected {} finality proof {:?}. Headers range: [{:?}; {:?}]. Proofs range: [{:?}; {:?}].\ + Trying to improve to: {:?}. Result: {}", + P::SOURCE_NAME, + selected_finality_proof.as_ref().map(|(h, _)| h.number()), + unjustified_range_begin, + unjustified_range_end, + buffered_range_begin, + buffered_range_end, + selected_header_number, + if has_selected_finality_proof { "improved" } else { "not improved" }, + ); + if !has_selected_finality_proof { + return selected_finality_proof + } + + // now remove all obsolete headers and extract selected header + let selected_header_position = unjustified_headers + .binary_search_by_key(selected_header_number, |header| header.number()) + .expect("unjustified_headers contain all headers from intersection; qed"); + let selected_header = unjustified_headers.swap_remove(selected_header_position); + Some((selected_header, finality_proof.clone())) +} + +pub(crate) fn prune_recent_finality_proofs( + justified_header_number: P::Number, + recent_finality_proofs: &mut FinalityProofs

, + recent_finality_proofs_limit: usize, +) { + let justified_header_idx = recent_finality_proofs + .binary_search_by_key(&justified_header_number, |(header_number, _)| *header_number) + .map(|idx| idx + 1) + .unwrap_or_else(|idx| idx); + let proofs_limit_idx = + recent_finality_proofs.len().saturating_sub(recent_finality_proofs_limit); + + *recent_finality_proofs = + recent_finality_proofs.split_off(std::cmp::max(justified_header_idx, proofs_limit_idx)); +} + +fn print_sync_progress( + progress_context: (Instant, Option), + best_number_at_source: P::Number, + best_number_at_target: P::Number, +) -> (Instant, Option) { + let (prev_time, prev_best_number_at_target) = progress_context; + let now = Instant::now(); + + let need_update = now - prev_time > Duration::from_secs(10) || + prev_best_number_at_target + .map(|prev_best_number_at_target| { + best_number_at_target.saturating_sub(prev_best_number_at_target) > 10.into() + }) + .unwrap_or(true); + + if !need_update { + return (prev_time, prev_best_number_at_target) + } + + log::info!( + target: "bridge", + "Synced {:?} of {:?} headers", + best_number_at_target, + best_number_at_source, + ); + (now, Some(best_number_at_target)) +} diff --git a/relays/finality/src/finality_loop_tests.rs b/relays/finality/src/finality_loop_tests.rs new file mode 100644 index 00000000000..1853c095f70 --- /dev/null +++ b/relays/finality/src/finality_loop_tests.rs @@ -0,0 +1,598 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Tests for finality synchronization loop. + +#![cfg(test)] + +use crate::{ + finality_loop::{ + prune_recent_finality_proofs, read_finality_proofs_from_stream, run_loop_iteration, + run_until_connection_lost, select_better_recent_finality_proof, select_header_to_submit, + FinalityLoopState, FinalityProofs, FinalitySyncParams, RestartableFinalityProofsStream, + SourceClient, TargetClient, + }, + sync_loop_metrics::SyncLoopMetrics, + FinalityProof, FinalitySyncPipeline, SourceHeader, +}; + +use async_trait::async_trait; +use bp_header_chain::GrandpaConsensusLogReader; +use futures::{FutureExt, Stream, StreamExt}; +use parking_lot::Mutex; +use relay_utils::{ + relay_loop::Client as RelayClient, FailedClient, HeaderId, MaybeConnectionError, + TrackedTransactionStatus, TransactionTracker, +}; +use std::{ + collections::HashMap, + pin::Pin, + sync::Arc, + time::{Duration, Instant}, +}; + +type IsMandatory = bool; +type TestNumber = u64; +type TestHash = u64; + +#[derive(Clone, Debug)] +struct TestTransactionTracker(TrackedTransactionStatus>); + +impl Default for TestTransactionTracker { + fn default() -> TestTransactionTracker { + TestTransactionTracker(TrackedTransactionStatus::Finalized(Default::default())) + } +} + +#[async_trait] +impl TransactionTracker for TestTransactionTracker { + type HeaderId = HeaderId; + + async fn wait(self) -> TrackedTransactionStatus> { + self.0 + } +} + +#[derive(Debug, Clone)] +enum TestError { + NonConnection, +} + +impl MaybeConnectionError for TestError { + fn is_connection_error(&self) -> bool { + false + } +} + +#[derive(Debug, Clone)] +struct TestFinalitySyncPipeline; + +impl FinalitySyncPipeline for TestFinalitySyncPipeline { + const SOURCE_NAME: &'static str = "TestSource"; + const TARGET_NAME: &'static str = "TestTarget"; + + type Hash = TestHash; + type Number = TestNumber; + type ConsensusLogReader = GrandpaConsensusLogReader; + type Header = TestSourceHeader; + type FinalityProof = TestFinalityProof; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct TestSourceHeader(IsMandatory, TestNumber, TestHash); + +impl SourceHeader> + for TestSourceHeader +{ + fn hash(&self) -> TestHash { + self.2 + } + + fn number(&self) -> TestNumber { + self.1 + } + + fn is_mandatory(&self) -> bool { + self.0 + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct TestFinalityProof(TestNumber); + +impl FinalityProof for TestFinalityProof { + fn target_header_number(&self) -> TestNumber { + self.0 + } +} + +#[derive(Debug, Clone, Default)] +struct ClientsData { + source_best_block_number: TestNumber, + source_headers: HashMap)>, + source_proofs: Vec, + + target_best_block_id: HeaderId, + target_headers: Vec<(TestSourceHeader, TestFinalityProof)>, + target_transaction_tracker: TestTransactionTracker, +} + +#[derive(Clone)] +struct TestSourceClient { + on_method_call: Arc, + data: Arc>, +} + +#[async_trait] +impl RelayClient for TestSourceClient { + type Error = TestError; + + async fn reconnect(&mut self) -> Result<(), TestError> { + unreachable!() + } +} + +#[async_trait] +impl SourceClient for TestSourceClient { + type FinalityProofsStream = Pin + 'static + Send>>; + + async fn best_finalized_block_number(&self) -> Result { + let mut data = self.data.lock(); + (self.on_method_call)(&mut data); + Ok(data.source_best_block_number) + } + + async fn header_and_finality_proof( + &self, + number: TestNumber, + ) -> Result<(TestSourceHeader, Option), TestError> { + let mut data = self.data.lock(); + (self.on_method_call)(&mut data); + data.source_headers.get(&number).cloned().ok_or(TestError::NonConnection) + } + + async fn finality_proofs(&self) -> Result { + let mut data = self.data.lock(); + (self.on_method_call)(&mut data); + Ok(futures::stream::iter(data.source_proofs.clone()).boxed()) + } +} + +#[derive(Clone)] +struct TestTargetClient { + on_method_call: Arc, + data: Arc>, +} + +#[async_trait] +impl RelayClient for TestTargetClient { + type Error = TestError; + + async fn reconnect(&mut self) -> Result<(), TestError> { + unreachable!() + } +} + +#[async_trait] +impl TargetClient for TestTargetClient { + type TransactionTracker = TestTransactionTracker; + + async fn best_finalized_source_block_id( + &self, + ) -> Result, TestError> { + let mut data = self.data.lock(); + (self.on_method_call)(&mut data); + Ok(data.target_best_block_id) + } + + async fn submit_finality_proof( + &self, + header: TestSourceHeader, + proof: TestFinalityProof, + ) -> Result { + let mut data = self.data.lock(); + (self.on_method_call)(&mut data); + data.target_best_block_id = HeaderId(header.number(), header.hash()); + data.target_headers.push((header, proof)); + (self.on_method_call)(&mut data); + Ok(data.target_transaction_tracker.clone()) + } +} + +fn prepare_test_clients( + exit_sender: futures::channel::mpsc::UnboundedSender<()>, + state_function: impl Fn(&mut ClientsData) -> bool + Send + Sync + 'static, + source_headers: HashMap)>, +) -> (TestSourceClient, TestTargetClient) { + let internal_state_function: Arc = + Arc::new(move |data| { + if state_function(data) { + exit_sender.unbounded_send(()).unwrap(); + } + }); + let clients_data = Arc::new(Mutex::new(ClientsData { + source_best_block_number: 10, + source_headers, + source_proofs: vec![TestFinalityProof(12), TestFinalityProof(14)], + + target_best_block_id: HeaderId(5, 5), + target_headers: vec![], + target_transaction_tracker: TestTransactionTracker(TrackedTransactionStatus::Finalized( + Default::default(), + )), + })); + ( + TestSourceClient { + on_method_call: internal_state_function.clone(), + data: clients_data.clone(), + }, + TestTargetClient { on_method_call: internal_state_function, data: clients_data }, + ) +} + +fn test_sync_params() -> FinalitySyncParams { + FinalitySyncParams { + tick: Duration::from_secs(0), + recent_finality_proofs_limit: 1024, + stall_timeout: Duration::from_secs(1), + only_mandatory_headers: false, + } +} + +fn run_sync_loop( + state_function: impl Fn(&mut ClientsData) -> bool + Send + Sync + 'static, +) -> (ClientsData, Result<(), FailedClient>) { + let (exit_sender, exit_receiver) = futures::channel::mpsc::unbounded(); + let (source_client, target_client) = prepare_test_clients( + exit_sender, + state_function, + vec![ + (5, (TestSourceHeader(false, 5, 5), None)), + (6, (TestSourceHeader(false, 6, 6), None)), + (7, (TestSourceHeader(false, 7, 7), Some(TestFinalityProof(7)))), + (8, (TestSourceHeader(true, 8, 8), Some(TestFinalityProof(8)))), + (9, (TestSourceHeader(false, 9, 9), Some(TestFinalityProof(9)))), + (10, (TestSourceHeader(false, 10, 10), None)), + ] + .into_iter() + .collect(), + ); + let sync_params = test_sync_params(); + + let clients_data = source_client.data.clone(); + let result = async_std::task::block_on(run_until_connection_lost( + source_client, + target_client, + sync_params, + None, + exit_receiver.into_future().map(|(_, _)| ()), + )); + + let clients_data = clients_data.lock().clone(); + (clients_data, result) +} + +#[test] +fn finality_sync_loop_works() { + let (client_data, result) = run_sync_loop(|data| { + // header#7 has persistent finality proof, but it isn't mandatory => it isn't submitted, + // because header#8 has persistent finality proof && it is mandatory => it is submitted + // header#9 has persistent finality proof, but it isn't mandatory => it is submitted, + // because there are no more persistent finality proofs + // + // once this ^^^ is done, we generate more blocks && read proof for blocks 12 and 14 from + // the stream + if data.target_best_block_id.0 == 9 { + data.source_best_block_number = 14; + data.source_headers.insert(11, (TestSourceHeader(false, 11, 11), None)); + data.source_headers + .insert(12, (TestSourceHeader(false, 12, 12), Some(TestFinalityProof(12)))); + data.source_headers.insert(13, (TestSourceHeader(false, 13, 13), None)); + data.source_headers + .insert(14, (TestSourceHeader(false, 14, 14), Some(TestFinalityProof(14)))); + } + // once this ^^^ is done, we generate more blocks && read persistent proof for block 16 + if data.target_best_block_id.0 == 14 { + data.source_best_block_number = 17; + data.source_headers.insert(15, (TestSourceHeader(false, 15, 15), None)); + data.source_headers + .insert(16, (TestSourceHeader(false, 16, 16), Some(TestFinalityProof(16)))); + data.source_headers.insert(17, (TestSourceHeader(false, 17, 17), None)); + } + + data.target_best_block_id.0 == 16 + }); + + assert_eq!(result, Ok(())); + assert_eq!( + client_data.target_headers, + vec![ + // before adding 11..14: finality proof for mandatory header#8 + (TestSourceHeader(true, 8, 8), TestFinalityProof(8)), + // before adding 11..14: persistent finality proof for non-mandatory header#9 + (TestSourceHeader(false, 9, 9), TestFinalityProof(9)), + // after adding 11..14: ephemeral finality proof for non-mandatory header#14 + (TestSourceHeader(false, 14, 14), TestFinalityProof(14)), + // after adding 15..17: persistent finality proof for non-mandatory header#16 + (TestSourceHeader(false, 16, 16), TestFinalityProof(16)), + ], + ); +} + +fn run_only_mandatory_headers_mode_test( + only_mandatory_headers: bool, + has_mandatory_headers: bool, +) -> Option<(TestSourceHeader, TestFinalityProof)> { + let (exit_sender, _) = futures::channel::mpsc::unbounded(); + let (source_client, target_client) = prepare_test_clients( + exit_sender, + |_| false, + vec![ + (6, (TestSourceHeader(false, 6, 6), Some(TestFinalityProof(6)))), + (7, (TestSourceHeader(false, 7, 7), Some(TestFinalityProof(7)))), + (8, (TestSourceHeader(has_mandatory_headers, 8, 8), Some(TestFinalityProof(8)))), + (9, (TestSourceHeader(false, 9, 9), Some(TestFinalityProof(9)))), + (10, (TestSourceHeader(false, 10, 10), Some(TestFinalityProof(10)))), + ] + .into_iter() + .collect(), + ); + async_std::task::block_on(select_header_to_submit( + &source_client, + &target_client, + &mut RestartableFinalityProofsStream::from(futures::stream::empty().boxed()), + &mut vec![], + 10, + 5, + &FinalitySyncParams { + tick: Duration::from_secs(0), + recent_finality_proofs_limit: 0, + stall_timeout: Duration::from_secs(0), + only_mandatory_headers, + }, + )) + .unwrap() +} + +#[test] +fn select_header_to_submit_skips_non_mandatory_headers_when_only_mandatory_headers_are_required() { + assert_eq!(run_only_mandatory_headers_mode_test(true, false), None); + assert_eq!( + run_only_mandatory_headers_mode_test(false, false), + Some((TestSourceHeader(false, 10, 10), TestFinalityProof(10))), + ); +} + +#[test] +fn select_header_to_submit_selects_mandatory_headers_when_only_mandatory_headers_are_required() { + assert_eq!( + run_only_mandatory_headers_mode_test(true, true), + Some((TestSourceHeader(true, 8, 8), TestFinalityProof(8))), + ); + assert_eq!( + run_only_mandatory_headers_mode_test(false, true), + Some((TestSourceHeader(true, 8, 8), TestFinalityProof(8))), + ); +} + +#[test] +fn select_better_recent_finality_proof_works() { + // if there are no unjustified headers, nothing is changed + assert_eq!( + select_better_recent_finality_proof::( + &[(5, TestFinalityProof(5))], + &mut vec![], + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ); + + // if there are no recent finality proofs, nothing is changed + assert_eq!( + select_better_recent_finality_proof::( + &[], + &mut vec![TestSourceHeader(false, 5, 5)], + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ); + + // if there's no intersection between recent finality proofs and unjustified headers, nothing is + // changed + let mut unjustified_headers = + vec![TestSourceHeader(false, 9, 9), TestSourceHeader(false, 10, 10)]; + assert_eq!( + select_better_recent_finality_proof::( + &[(1, TestFinalityProof(1)), (4, TestFinalityProof(4))], + &mut unjustified_headers, + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ); + + // if there's intersection between recent finality proofs and unjustified headers, but there are + // no proofs in this intersection, nothing is changed + let mut unjustified_headers = vec![ + TestSourceHeader(false, 8, 8), + TestSourceHeader(false, 9, 9), + TestSourceHeader(false, 10, 10), + ]; + assert_eq!( + select_better_recent_finality_proof::( + &[(7, TestFinalityProof(7)), (11, TestFinalityProof(11))], + &mut unjustified_headers, + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ); + assert_eq!( + unjustified_headers, + vec![ + TestSourceHeader(false, 8, 8), + TestSourceHeader(false, 9, 9), + TestSourceHeader(false, 10, 10) + ] + ); + + // if there's intersection between recent finality proofs and unjustified headers and there's + // a proof in this intersection: + // - this better (last from intersection) proof is selected; + // - 'obsolete' unjustified headers are pruned. + let mut unjustified_headers = vec![ + TestSourceHeader(false, 8, 8), + TestSourceHeader(false, 9, 9), + TestSourceHeader(false, 10, 10), + ]; + assert_eq!( + select_better_recent_finality_proof::( + &[(7, TestFinalityProof(7)), (9, TestFinalityProof(9))], + &mut unjustified_headers, + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ), + Some((TestSourceHeader(false, 9, 9), TestFinalityProof(9))), + ); +} + +#[test] +fn read_finality_proofs_from_stream_works() { + // when stream is currently empty, nothing is changed + let mut recent_finality_proofs = vec![(1, TestFinalityProof(1))]; + let mut stream = futures::stream::pending().into(); + read_finality_proofs_from_stream::( + &mut stream, + &mut recent_finality_proofs, + ); + assert_eq!(recent_finality_proofs, vec![(1, TestFinalityProof(1))]); + assert!(!stream.needs_restart); + + // when stream has entry with target, it is added to the recent proofs container + let mut stream = futures::stream::iter(vec![TestFinalityProof(4)]) + .chain(futures::stream::pending()) + .into(); + read_finality_proofs_from_stream::( + &mut stream, + &mut recent_finality_proofs, + ); + assert_eq!(recent_finality_proofs, vec![(1, TestFinalityProof(1)), (4, TestFinalityProof(4))]); + assert!(!stream.needs_restart); + + // when stream has ended, we'll need to restart it + let mut stream = futures::stream::empty().into(); + read_finality_proofs_from_stream::( + &mut stream, + &mut recent_finality_proofs, + ); + assert_eq!(recent_finality_proofs, vec![(1, TestFinalityProof(1)), (4, TestFinalityProof(4))]); + assert!(stream.needs_restart); +} + +#[test] +fn prune_recent_finality_proofs_works() { + let original_recent_finality_proofs: FinalityProofs = vec![ + (10, TestFinalityProof(10)), + (13, TestFinalityProof(13)), + (15, TestFinalityProof(15)), + (17, TestFinalityProof(17)), + (19, TestFinalityProof(19)), + ] + .into_iter() + .collect(); + + // when there's proof for justified header in the vec + let mut recent_finality_proofs = original_recent_finality_proofs.clone(); + prune_recent_finality_proofs::(10, &mut recent_finality_proofs, 1024); + assert_eq!(&original_recent_finality_proofs[1..], recent_finality_proofs,); + + // when there are no proof for justified header in the vec + let mut recent_finality_proofs = original_recent_finality_proofs.clone(); + prune_recent_finality_proofs::(11, &mut recent_finality_proofs, 1024); + assert_eq!(&original_recent_finality_proofs[1..], recent_finality_proofs,); + + // when there are too many entries after initial prune && they also need to be pruned + let mut recent_finality_proofs = original_recent_finality_proofs.clone(); + prune_recent_finality_proofs::(10, &mut recent_finality_proofs, 2); + assert_eq!(&original_recent_finality_proofs[3..], recent_finality_proofs,); + + // when last entry is pruned + let mut recent_finality_proofs = original_recent_finality_proofs.clone(); + prune_recent_finality_proofs::(19, &mut recent_finality_proofs, 2); + assert_eq!(&original_recent_finality_proofs[5..], recent_finality_proofs,); + + // when post-last entry is pruned + let mut recent_finality_proofs = original_recent_finality_proofs.clone(); + prune_recent_finality_proofs::(20, &mut recent_finality_proofs, 2); + assert_eq!(&original_recent_finality_proofs[5..], recent_finality_proofs,); +} + +#[test] +fn different_forks_at_source_and_at_target_are_detected() { + let (exit_sender, _exit_receiver) = futures::channel::mpsc::unbounded(); + let (source_client, target_client) = prepare_test_clients( + exit_sender, + |_| false, + vec![ + (5, (TestSourceHeader(false, 5, 42), None)), + (6, (TestSourceHeader(false, 6, 6), None)), + (7, (TestSourceHeader(false, 7, 7), None)), + (8, (TestSourceHeader(false, 8, 8), None)), + (9, (TestSourceHeader(false, 9, 9), None)), + (10, (TestSourceHeader(false, 10, 10), None)), + ] + .into_iter() + .collect(), + ); + + let mut progress = (Instant::now(), None); + let mut finality_proofs_stream = futures::stream::iter(vec![]).boxed().into(); + let mut recent_finality_proofs = Vec::new(); + let metrics_sync = SyncLoopMetrics::new(None, "source", "target").unwrap(); + async_std::task::block_on(run_loop_iteration::( + &source_client, + &target_client, + FinalityLoopState { + progress: &mut progress, + finality_proofs_stream: &mut finality_proofs_stream, + recent_finality_proofs: &mut recent_finality_proofs, + submitted_header_number: None, + }, + &test_sync_params(), + &Some(metrics_sync.clone()), + )) + .unwrap(); + + assert!(!metrics_sync.is_using_same_fork()); +} + +#[test] +fn stalls_when_transaction_tracker_returns_error() { + let (_, result) = run_sync_loop(|data| { + data.target_transaction_tracker = TestTransactionTracker(TrackedTransactionStatus::Lost); + data.target_best_block_id = HeaderId(5, 5); + data.target_best_block_id.0 == 16 + }); + + assert_eq!(result, Err(FailedClient::Both)); +} + +#[test] +fn stalls_when_transaction_tracker_returns_finalized_but_transaction_fails() { + let (_, result) = run_sync_loop(|data| { + data.target_best_block_id = HeaderId(5, 5); + data.target_best_block_id.0 == 16 + }); + + assert_eq!(result, Err(FailedClient::Both)); +} diff --git a/relays/finality/src/lib.rs b/relays/finality/src/lib.rs new file mode 100644 index 00000000000..dca47c6a572 --- /dev/null +++ b/relays/finality/src/lib.rs @@ -0,0 +1,61 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! This crate has single entrypoint to run synchronization loop that is built around finality +//! proofs, as opposed to headers synchronization loop, which is built around headers. The headers +//! are still submitted to the target node, but are treated as auxiliary data as we are not trying +//! to submit all source headers to the target node. + +pub use crate::{ + finality_loop::{metrics_prefix, run, FinalitySyncParams, SourceClient, TargetClient}, + sync_loop_metrics::SyncLoopMetrics, +}; + +use bp_header_chain::{ConsensusLogReader, FinalityProof}; +use std::fmt::Debug; + +mod finality_loop; +mod finality_loop_tests; +mod sync_loop_metrics; + +/// Finality proofs synchronization pipeline. +pub trait FinalitySyncPipeline: 'static + Clone + Debug + Send + Sync { + /// Name of the finality proofs source. + const SOURCE_NAME: &'static str; + /// Name of the finality proofs target. + const TARGET_NAME: &'static str; + + /// Headers we're syncing are identified by this hash. + type Hash: Eq + Clone + Copy + Send + Sync + Debug; + /// Headers we're syncing are identified by this number. + type Number: relay_utils::BlockNumberBase; + /// A reader that can extract the consensus log from the header digest and interpret it. + type ConsensusLogReader: ConsensusLogReader; + /// Type of header that we're syncing. + type Header: SourceHeader; + /// Finality proof type. + type FinalityProof: FinalityProof; +} + +/// Header that we're receiving from source node. +pub trait SourceHeader: Clone + Debug + PartialEq + Send + Sync { + /// Returns hash of header. + fn hash(&self) -> Hash; + /// Returns number of header. + fn number(&self) -> Number; + /// Returns true if this header needs to be submitted to target node. + fn is_mandatory(&self) -> bool; +} diff --git a/relays/finality/src/sync_loop_metrics.rs b/relays/finality/src/sync_loop_metrics.rs new file mode 100644 index 00000000000..ae73bbedc4f --- /dev/null +++ b/relays/finality/src/sync_loop_metrics.rs @@ -0,0 +1,86 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Metrics for headers synchronization relay loop. + +use relay_utils::metrics::{metric_name, register, IntGauge, Metric, PrometheusError, Registry}; + +/// Headers sync metrics. +#[derive(Clone)] +pub struct SyncLoopMetrics { + /// Best syncing header at the source. + best_source_block_number: IntGauge, + /// Best syncing header at the target. + best_target_block_number: IntGauge, + /// Flag that has `0` value when best source headers at the source node and at-target-chain + /// are matching and `1` otherwise. + using_different_forks: IntGauge, +} + +impl SyncLoopMetrics { + /// Create and register headers loop metrics. + pub fn new( + prefix: Option<&str>, + at_source_chain_label: &str, + at_target_chain_label: &str, + ) -> Result { + Ok(SyncLoopMetrics { + best_source_block_number: IntGauge::new( + metric_name(prefix, &format!("best_{at_source_chain_label}_block_number")), + format!("Best block number at the {at_source_chain_label}"), + )?, + best_target_block_number: IntGauge::new( + metric_name(prefix, &format!("best_{at_target_chain_label}_block_number")), + format!("Best block number at the {at_target_chain_label}"), + )?, + using_different_forks: IntGauge::new( + metric_name(prefix, &format!("is_{at_source_chain_label}_and_{at_target_chain_label}_using_different_forks")), + "Whether the best finalized source block at target node is different (value 1) from the \ + corresponding block at the source node", + )?, + }) + } + + /// Returns current value of the using-same-fork flag. + #[cfg(test)] + pub(crate) fn is_using_same_fork(&self) -> bool { + self.using_different_forks.get() == 0 + } + + /// Update best block number at source. + pub fn update_best_block_at_source>(&self, source_best_number: Number) { + self.best_source_block_number.set(source_best_number.into()); + } + + /// Update best block number at target. + pub fn update_best_block_at_target>(&self, target_best_number: Number) { + self.best_target_block_number.set(target_best_number.into()); + } + + /// Update using-same-fork flag. + pub fn update_using_same_fork(&self, using_same_fork: bool) { + self.using_different_forks.set((!using_same_fork).into()) + } +} + +impl Metric for SyncLoopMetrics { + fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { + register(self.best_source_block_number.clone(), registry)?; + register(self.best_target_block_number.clone(), registry)?; + register(self.using_different_forks.clone(), registry)?; + Ok(()) + } +} diff --git a/relays/lib-substrate-relay/Cargo.toml b/relays/lib-substrate-relay/Cargo.toml new file mode 100644 index 00000000000..bdf49d42eab --- /dev/null +++ b/relays/lib-substrate-relay/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "substrate-relay-helper" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +anyhow = "1.0" +thiserror = "1.0.26" +async-std = "1.9.0" +async-trait = "0.1" +codec = { package = "parity-scale-codec", version = "3.1.5" } +futures = "0.3.12" +num-traits = "0.2" +log = "0.4.17" + +# Bridge dependencies + +bp-header-chain = { path = "../../primitives/header-chain" } +bp-parachains = { path = "../../primitives/parachains" } +bp-polkadot-core = { path = "../../primitives/polkadot-core" } +bridge-runtime-common = { path = "../../bin/runtime-common" } + +finality-grandpa = { version = "0.16.0" } +finality-relay = { path = "../finality" } +parachains-relay = { path = "../parachains" } +relay-utils = { path = "../utils" } +messages-relay = { path = "../messages" } +relay-substrate-client = { path = "../client-substrate" } + +pallet-bridge-grandpa = { path = "../../modules/grandpa" } +pallet-bridge-messages = { path = "../../modules/messages" } +pallet-bridge-parachains = { path = "../../modules/parachains" } + +bp-runtime = { path = "../../primitives/runtime" } +bp-messages = { path = "../../primitives/messages" } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[dev-dependencies] +bp-millau = { path = "../../primitives/chain-millau" } +bp-rialto = { path = "../../primitives/chain-rialto" } +bp-rococo = { path = "../../primitives/chain-rococo" } +bp-wococo = { path = "../../primitives/chain-wococo" } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } +relay-rialto-client = { path = "../client-rialto" } +relay-rococo-client = { path = "../client-rococo" } +relay-wococo-client = { path = "../client-wococo" } +rialto-runtime = { path = "../../bin/rialto/runtime" } diff --git a/relays/lib-substrate-relay/src/error.rs b/relays/lib-substrate-relay/src/error.rs new file mode 100644 index 00000000000..b41870a181d --- /dev/null +++ b/relays/lib-substrate-relay/src/error.rs @@ -0,0 +1,63 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Relay errors. + +use relay_substrate_client as client; +use sp_finality_grandpa::AuthorityList; +use sp_runtime::traits::MaybeDisplay; +use std::fmt::Debug; +use thiserror::Error; + +/// Relay errors. +#[derive(Error, Debug)] +pub enum Error { + /// Failed to submit signed extrinsic from to the target chain. + #[error("Failed to submit {0} transaction: {1:?}")] + SubmitTransaction(&'static str, client::Error), + /// Failed subscribe to justification stream of the source chain. + #[error("Failed to subscribe to {0} justifications: {1:?}")] + Subscribe(&'static str, client::Error), + /// Failed subscribe to read justification from the source chain (client error). + #[error("Failed to read {0} justification from the stream: {1}")] + ReadJustification(&'static str, client::Error), + /// Failed subscribe to read justification from the source chain (stream ended). + #[error("Failed to read {0} justification from the stream: stream has ended unexpectedly")] + ReadJustificationStreamEnded(&'static str), + /// Failed subscribe to decode justification from the source chain. + #[error("Failed to decode {0} justification: {1:?}")] + DecodeJustification(&'static str, codec::Error), + /// GRANDPA authorities read from the source chain are invalid. + #[error("Read invalid {0} authorities set: {1:?}")] + ReadInvalidAuthorities(&'static str, AuthorityList), + /// Failed to guess initial GRANDPA authorities at the given header of the source chain. + #[error("Failed to guess initial {0} GRANDPA authorities set id: checked all possible ids in range [0; {1}]")] + GuessInitialAuthorities(&'static str, HeaderNumber), + /// Failed to retrieve GRANDPA authorities at the given header from the source chain. + #[error("Failed to retrive {0} GRANDPA authorities set at header {1}: {2:?}")] + RetrieveAuthorities(&'static str, Hash, client::Error), + /// Failed to decode GRANDPA authorities at the given header of the source chain. + #[error("Failed to decode {0} GRANDPA authorities set at header {1}: {2:?}")] + DecodeAuthorities(&'static str, Hash, codec::Error), + /// Failed to retrieve header by the hash from the source chain. + #[error("Failed to retrieve {0} header with hash {1}: {2:?}")] + RetrieveHeader(&'static str, Hash, client::Error), + /// Failed to submit signed extrinsic from to the target chain. + #[error( + "Failed to retrieve `is_initialized` flag of the with-{0} finality pallet at {1}: {2:?}" + )] + IsInitializedRetrieve(&'static str, &'static str, client::Error), +} diff --git a/relays/lib-substrate-relay/src/finality/engine.rs b/relays/lib-substrate-relay/src/finality/engine.rs new file mode 100644 index 00000000000..4c2da5a5319 --- /dev/null +++ b/relays/lib-substrate-relay/src/finality/engine.rs @@ -0,0 +1,259 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Support of different finality engines, available in Substrate. + +use crate::error::Error; +use async_trait::async_trait; +use bp_header_chain::{ + justification::{verify_justification, GrandpaJustification}, + ConsensusLogReader, FinalityProof, GrandpaConsensusLogReader, +}; +use bp_runtime::{BasicOperatingMode, OperatingMode}; +use codec::{Decode, Encode}; +use finality_grandpa::voter_set::VoterSet; +use num_traits::{One, Zero}; +use relay_substrate_client::{ + BlockNumberOf, Chain, ChainWithGrandpa, Client, Error as SubstrateError, HashOf, HeaderOf, + Subscription, SubstrateFinalityClient, SubstrateGrandpaFinalityClient, +}; +use sp_core::{storage::StorageKey, Bytes}; +use sp_finality_grandpa::{AuthorityList as GrandpaAuthoritiesSet, GRANDPA_ENGINE_ID}; +use sp_runtime::{traits::Header, ConsensusEngineId}; +use std::marker::PhantomData; + +/// Finality engine, used by the Substrate chain. +#[async_trait] +pub trait Engine: Send { + /// Unique consensus engine identifier. + const ID: ConsensusEngineId; + /// A reader that can extract the consensus log from the header digest and interpret it. + type ConsensusLogReader: ConsensusLogReader; + /// Type of Finality RPC client used by this engine. + type FinalityClient: SubstrateFinalityClient; + /// Type of finality proofs, used by consensus engine. + type FinalityProof: FinalityProof> + Decode + Encode; + /// Type of bridge pallet initialization data. + type InitializationData: std::fmt::Debug + Send + Sync + 'static; + /// Type of bridge pallet operating mode. + type OperatingMode: OperatingMode + 'static; + + /// Returns storage at the bridged (target) chain that corresponds to some value that is + /// missing from the storage until bridge pallet is initialized. + /// + /// Note that we don't care about type of the value - just if it present or not. + fn is_initialized_key() -> StorageKey; + + /// Returns `Ok(true)` if finality pallet at the bridged chain has already been initialized. + async fn is_initialized( + target_client: &Client, + ) -> Result { + Ok(target_client + .raw_storage_value(Self::is_initialized_key(), None) + .await? + .is_some()) + } + + /// Returns storage key at the bridged (target) chain that corresponds to the variable + /// that holds the operating mode of the pallet. + fn pallet_operating_mode_key() -> StorageKey; + + /// Returns `Ok(true)` if finality pallet at the bridged chain is halted. + async fn is_halted( + target_client: &Client, + ) -> Result { + Ok(target_client + .storage_value::(Self::pallet_operating_mode_key(), None) + .await? + .map(|operating_mode| operating_mode.is_halted()) + .unwrap_or(false)) + } + + /// A method to subscribe to encoded finality proofs, given source client. + async fn finality_proofs(client: &Client) -> Result, SubstrateError> { + client.subscribe_finality_justifications::().await + } + + /// Prepare initialization data for the finality bridge pallet. + async fn prepare_initialization_data( + client: Client, + ) -> Result, BlockNumberOf>>; +} + +/// GRANDPA finality engine. +pub struct Grandpa(PhantomData); + +impl Grandpa { + /// Read header by hash from the source client. + async fn source_header( + source_client: &Client, + header_hash: C::Hash, + ) -> Result, BlockNumberOf>> { + source_client + .header_by_hash(header_hash) + .await + .map_err(|err| Error::RetrieveHeader(C::NAME, header_hash, err)) + } + + /// Read GRANDPA authorities set at given header. + async fn source_authorities_set( + source_client: &Client, + header_hash: C::Hash, + ) -> Result, BlockNumberOf>> { + let raw_authorities_set = source_client + .grandpa_authorities_set(header_hash) + .await + .map_err(|err| Error::RetrieveAuthorities(C::NAME, header_hash, err))?; + GrandpaAuthoritiesSet::decode(&mut &raw_authorities_set[..]) + .map_err(|err| Error::DecodeAuthorities(C::NAME, header_hash, err)) + } +} + +#[async_trait] +impl Engine for Grandpa { + const ID: ConsensusEngineId = GRANDPA_ENGINE_ID; + type ConsensusLogReader = GrandpaConsensusLogReader<::Number>; + type FinalityClient = SubstrateGrandpaFinalityClient; + type FinalityProof = GrandpaJustification>; + type InitializationData = bp_header_chain::InitializationData; + type OperatingMode = BasicOperatingMode; + + fn is_initialized_key() -> StorageKey { + bp_header_chain::storage_keys::best_finalized_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME) + } + + fn pallet_operating_mode_key() -> StorageKey { + bp_header_chain::storage_keys::pallet_operating_mode_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME) + } + + /// Prepare initialization data for the GRANDPA verifier pallet. + async fn prepare_initialization_data( + source_client: Client, + ) -> Result, BlockNumberOf>> { + // In ideal world we just need to get best finalized header and then to read GRANDPA + // authorities set (`pallet_grandpa::CurrentSetId` + `GrandpaApi::grandpa_authorities()`) at + // this header. + // + // But now there are problems with this approach - `CurrentSetId` may return invalid value. + // So here we're waiting for the next justification, read the authorities set and then try + // to figure out the set id with bruteforce. + let justifications = Self::finality_proofs(&source_client) + .await + .map_err(|err| Error::Subscribe(C::NAME, err))?; + // Read next justification - the header that it finalizes will be used as initial header. + let justification = justifications + .next() + .await + .map_err(|e| Error::ReadJustification(C::NAME, e)) + .and_then(|justification| { + justification.ok_or(Error::ReadJustificationStreamEnded(C::NAME)) + })?; + + // Read initial header. + let justification: GrandpaJustification = + Decode::decode(&mut &justification.0[..]) + .map_err(|err| Error::DecodeJustification(C::NAME, err))?; + + let (initial_header_hash, initial_header_number) = + (justification.commit.target_hash, justification.commit.target_number); + + let initial_header = Self::source_header(&source_client, initial_header_hash).await?; + log::trace!(target: "bridge", "Selected {} initial header: {}/{}", + C::NAME, + initial_header_number, + initial_header_hash, + ); + + // Read GRANDPA authorities set at initial header. + let initial_authorities_set = + Self::source_authorities_set(&source_client, initial_header_hash).await?; + log::trace!(target: "bridge", "Selected {} initial authorities set: {:?}", + C::NAME, + initial_authorities_set, + ); + + // If initial header changes the GRANDPA authorities set, then we need previous authorities + // to verify justification. + let mut authorities_for_verification = initial_authorities_set.clone(); + let scheduled_change = + GrandpaConsensusLogReader::>::find_authorities_change( + initial_header.digest(), + ); + assert!( + scheduled_change.as_ref().map(|c| c.delay.is_zero()).unwrap_or(true), + "GRANDPA authorities change at {} scheduled to happen in {:?} blocks. We expect\ + regular change to have zero delay", + initial_header_hash, + scheduled_change.as_ref().map(|c| c.delay), + ); + let schedules_change = scheduled_change.is_some(); + if schedules_change { + authorities_for_verification = + Self::source_authorities_set(&source_client, *initial_header.parent_hash()).await?; + log::trace!( + target: "bridge", + "Selected {} header is scheduling GRANDPA authorities set changes. Using previous set: {:?}", + C::NAME, + authorities_for_verification, + ); + } + + // Now let's try to guess authorities set id by verifying justification. + let mut initial_authorities_set_id = 0; + let mut min_possible_block_number = C::BlockNumber::zero(); + let authorities_for_verification = VoterSet::new(authorities_for_verification.clone()) + .ok_or(Error::ReadInvalidAuthorities(C::NAME, authorities_for_verification))?; + loop { + log::trace!( + target: "bridge", "Trying {} GRANDPA authorities set id: {}", + C::NAME, + initial_authorities_set_id, + ); + + let is_valid_set_id = verify_justification::( + (initial_header_hash, initial_header_number), + initial_authorities_set_id, + &authorities_for_verification, + &justification, + ) + .is_ok(); + + if is_valid_set_id { + break + } + + initial_authorities_set_id += 1; + min_possible_block_number += One::one(); + if min_possible_block_number > initial_header_number { + // there can't be more authorities set changes than headers => if we have reached + // `initial_block_number` and still have not found correct value of + // `initial_authorities_set_id`, then something else is broken => fail + return Err(Error::GuessInitialAuthorities(C::NAME, initial_header_number)) + } + } + + Ok(bp_header_chain::InitializationData { + header: Box::new(initial_header), + authority_list: initial_authorities_set, + set_id: if schedules_change { + initial_authorities_set_id + 1 + } else { + initial_authorities_set_id + }, + operating_mode: BasicOperatingMode::Normal, + }) + } +} diff --git a/relays/lib-substrate-relay/src/finality/guards.rs b/relays/lib-substrate-relay/src/finality/guards.rs new file mode 100644 index 00000000000..1451b549cc0 --- /dev/null +++ b/relays/lib-substrate-relay/src/finality/guards.rs @@ -0,0 +1,48 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Tools for starting guards of finality relays. + +use crate::TransactionParams; + +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, ChainWithBalances, ChainWithTransactions, +}; +use sp_core::Pair; + +/// Start finality relay guards. +pub async fn start( + target_client: &relay_substrate_client::Client, + transaction_params: &TransactionParams>, + enable_version_guard: bool, + maximal_balance_decrease_per_day: C::Balance, +) -> relay_substrate_client::Result<()> +where + AccountIdOf: From< as Pair>::Public>, +{ + if enable_version_guard { + relay_substrate_client::guard::abort_on_spec_version_change( + target_client.clone(), + target_client.simple_runtime_version().await?.0, + ); + } + relay_substrate_client::guard::abort_when_account_balance_decreased( + target_client.clone(), + transaction_params.signer.public().into(), + maximal_balance_decrease_per_day, + ); + Ok(()) +} diff --git a/relays/lib-substrate-relay/src/finality/initialize.rs b/relays/lib-substrate-relay/src/finality/initialize.rs new file mode 100644 index 00000000000..938f1330b2e --- /dev/null +++ b/relays/lib-substrate-relay/src/finality/initialize.rs @@ -0,0 +1,149 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Initialize Substrate -> Substrate finality bridge. +//! +//! Initialization is a transaction that calls `initialize()` function of the +//! finality pallet (GRANDPA/BEEFY/...). This transaction brings initial header +//! and authorities set from source to target chain. The finality sync starts +//! with this header. + +use crate::{error::Error, finality::engine::Engine}; + +use relay_substrate_client::{ + Chain, ChainWithTransactions, Client, Error as SubstrateError, SignParam, UnsignedTransaction, +}; +use sp_runtime::traits::Header as HeaderT; + +/// Submit headers-bridge initialization transaction. +pub async fn initialize< + E: Engine, + SourceChain: Chain, + TargetChain: ChainWithTransactions, + F, +>( + source_client: Client, + target_client: Client, + target_transactions_signer: TargetChain::AccountId, + target_signing_data: SignParam, + prepare_initialize_transaction: F, + dry_run: bool, +) where + F: FnOnce( + TargetChain::Index, + E::InitializationData, + ) -> Result, SubstrateError> + + Send + + 'static, +{ + let result = do_initialize::( + source_client, + target_client, + target_transactions_signer, + target_signing_data, + prepare_initialize_transaction, + dry_run, + ) + .await; + + match result { + Ok(Some(tx_hash)) => log::info!( + target: "bridge", + "Successfully submitted {}-headers bridge initialization transaction to {}: {:?}", + SourceChain::NAME, + TargetChain::NAME, + tx_hash, + ), + Ok(None) => (), + Err(err) => log::error!( + target: "bridge", + "Failed to submit {}-headers bridge initialization transaction to {}: {:?}", + SourceChain::NAME, + TargetChain::NAME, + err, + ), + } +} + +/// Craft and submit initialization transaction, returning any error that may occur. +async fn do_initialize< + E: Engine, + SourceChain: Chain, + TargetChain: ChainWithTransactions, + F, +>( + source_client: Client, + target_client: Client, + target_transactions_signer: TargetChain::AccountId, + target_signing_data: SignParam, + prepare_initialize_transaction: F, + dry_run: bool, +) -> Result< + Option, + Error::Number>, +> +where + F: FnOnce( + TargetChain::Index, + E::InitializationData, + ) -> Result, SubstrateError> + + Send + + 'static, +{ + let is_initialized = E::is_initialized(&target_client) + .await + .map_err(|e| Error::IsInitializedRetrieve(SourceChain::NAME, TargetChain::NAME, e))?; + if is_initialized { + log::info!( + target: "bridge", + "{}-headers bridge at {} is already initialized. Skipping", + SourceChain::NAME, + TargetChain::NAME, + ); + if !dry_run { + return Ok(None) + } + } + + let initialization_data = E::prepare_initialization_data(source_client).await?; + log::info!( + target: "bridge", + "Prepared initialization data for {}-headers bridge at {}: {:?}", + SourceChain::NAME, + TargetChain::NAME, + initialization_data, + ); + + let initialization_tx_hash = target_client + .submit_signed_extrinsic( + target_transactions_signer, + target_signing_data, + move |_, transaction_nonce| { + let tx = prepare_initialize_transaction(transaction_nonce, initialization_data); + if dry_run { + Err(SubstrateError::Custom( + "Not submitting extrinsic in `dry-run` mode!".to_string(), + )) + } else { + tx + } + }, + ) + .await + .map_err(|err| Error::SubmitTransaction(TargetChain::NAME, err))?; + + Ok(Some(initialization_tx_hash)) +} diff --git a/relays/lib-substrate-relay/src/finality/mod.rs b/relays/lib-substrate-relay/src/finality/mod.rs new file mode 100644 index 00000000000..5d7b52cd1de --- /dev/null +++ b/relays/lib-substrate-relay/src/finality/mod.rs @@ -0,0 +1,204 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types and functions intended to ease adding of new Substrate -> Substrate +//! finality proofs synchronization pipelines. + +use crate::{ + finality::{ + engine::Engine, + source::{SubstrateFinalityProof, SubstrateFinalitySource}, + target::SubstrateFinalityTarget, + }, + TransactionParams, +}; + +use async_trait::async_trait; +use bp_header_chain::justification::GrandpaJustification; +use finality_relay::FinalitySyncPipeline; +use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig}; +use relay_substrate_client::{ + transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, + ChainWithTransactions, Client, HashOf, HeaderOf, SyncHeader, +}; +use relay_utils::metrics::MetricsParams; +use sp_core::Pair; +use std::{fmt::Debug, marker::PhantomData}; + +pub mod engine; +pub mod guards; +pub mod initialize; +pub mod source; +pub mod target; + +/// Default limit of recent finality proofs. +/// +/// Finality delay of 4096 blocks is unlikely to happen in practice in +/// Substrate+GRANDPA based chains (good to know). +pub(crate) const RECENT_FINALITY_PROOFS_LIMIT: usize = 4096; + +/// Substrate -> Substrate finality proofs synchronization pipeline. +#[async_trait] +pub trait SubstrateFinalitySyncPipeline: 'static + Clone + Debug + Send + Sync { + /// Headers of this chain are submitted to the `TargetChain`. + type SourceChain: Chain; + /// Headers of the `SourceChain` are submitted to this chain. + type TargetChain: ChainWithTransactions; + + /// Finality engine. + type FinalityEngine: Engine; + /// How submit finality proof call is built? + type SubmitFinalityProofCallBuilder: SubmitFinalityProofCallBuilder; + + /// Add relay guards if required. + async fn start_relay_guards( + _target_client: &Client, + _transaction_params: &TransactionParams>, + _enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + Ok(()) + } +} + +/// Adapter that allows all `SubstrateFinalitySyncPipeline` to act as `FinalitySyncPipeline`. +#[derive(Clone, Debug)] +pub struct FinalitySyncPipelineAdapter { + _phantom: PhantomData

, +} + +impl FinalitySyncPipeline for FinalitySyncPipelineAdapter

{ + const SOURCE_NAME: &'static str = P::SourceChain::NAME; + const TARGET_NAME: &'static str = P::TargetChain::NAME; + + type Hash = HashOf; + type Number = BlockNumberOf; + type ConsensusLogReader = >::ConsensusLogReader; + type Header = SyncHeader>; + type FinalityProof = SubstrateFinalityProof

; +} + +/// Different ways of building `submit_finality_proof` calls. +pub trait SubmitFinalityProofCallBuilder { + /// Given source chain header and its finality proofs, build call of `submit_finality_proof` + /// function of bridge GRANDPA module at the target chain. + fn build_submit_finality_proof_call( + header: SyncHeader>, + proof: SubstrateFinalityProof

, + ) -> CallOf; +} + +/// Building `submit_finality_proof` call when you have direct access to the target +/// chain runtime. +pub struct DirectSubmitGrandpaFinalityProofCallBuilder { + _phantom: PhantomData<(P, R, I)>, +} + +impl SubmitFinalityProofCallBuilder

+ for DirectSubmitGrandpaFinalityProofCallBuilder +where + P: SubstrateFinalitySyncPipeline, + R: BridgeGrandpaConfig, + I: 'static, + R::BridgedChain: bp_runtime::Chain

>, + CallOf: From>, + P::FinalityEngine: + Engine>>, +{ + fn build_submit_finality_proof_call( + header: SyncHeader>, + proof: GrandpaJustification>, + ) -> CallOf { + BridgeGrandpaCall::::submit_finality_proof { + finality_target: Box::new(header.into_inner()), + justification: proof, + } + .into() + } +} + +/// Macro that generates `SubmitFinalityProofCallBuilder` implementation for the case when +/// you only have an access to the mocked version of target chain runtime. In this case you +/// should provide "name" of the call variant for the bridge GRANDPA calls and the "name" of +/// the variant for the `submit_finality_proof` call within that first option. +#[rustfmt::skip] +#[macro_export] +macro_rules! generate_mocked_submit_finality_proof_call_builder { + ($pipeline:ident, $mocked_builder:ident, $bridge_grandpa:path, $submit_finality_proof:path) => { + pub struct $mocked_builder; + + impl $crate::finality::SubmitFinalityProofCallBuilder<$pipeline> + for $mocked_builder + { + fn build_submit_finality_proof_call( + header: relay_substrate_client::SyncHeader< + relay_substrate_client::HeaderOf< + <$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::SourceChain + > + >, + proof: bp_header_chain::justification::GrandpaJustification< + relay_substrate_client::HeaderOf< + <$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::SourceChain + > + >, + ) -> relay_substrate_client::CallOf< + <$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::TargetChain + > { + $bridge_grandpa($submit_finality_proof(Box::new(header.into_inner()), proof)) + } + } + }; +} + +/// Run Substrate-to-Substrate finality sync loop. +pub async fn run( + source_client: Client, + target_client: Client, + only_mandatory_headers: bool, + transaction_params: TransactionParams>, + metrics_params: MetricsParams, +) -> anyhow::Result<()> +where + AccountIdOf: From< as Pair>::Public>, +{ + log::info!( + target: "bridge", + "Starting {} -> {} finality proof relay", + P::SourceChain::NAME, + P::TargetChain::NAME, + ); + + finality_relay::run( + SubstrateFinalitySource::

::new(source_client, None), + SubstrateFinalityTarget::

::new(target_client, transaction_params.clone()), + finality_relay::FinalitySyncParams { + tick: std::cmp::max( + P::SourceChain::AVERAGE_BLOCK_INTERVAL, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + ), + recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT, + stall_timeout: transaction_stall_timeout( + transaction_params.mortality, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + relay_utils::STALL_TIMEOUT, + ), + only_mandatory_headers, + }, + metrics_params, + futures::future::pending(), + ) + .await + .map_err(|e| anyhow::format_err!("{}", e)) +} diff --git a/relays/lib-substrate-relay/src/finality/source.rs b/relays/lib-substrate-relay/src/finality/source.rs new file mode 100644 index 00000000000..e75862a8227 --- /dev/null +++ b/relays/lib-substrate-relay/src/finality/source.rs @@ -0,0 +1,175 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Default generic implementation of finality source for basic Substrate client. + +use crate::finality::{engine::Engine, FinalitySyncPipelineAdapter, SubstrateFinalitySyncPipeline}; + +use async_std::sync::{Arc, Mutex}; +use async_trait::async_trait; +use codec::Decode; +use finality_relay::SourceClient; +use futures::stream::{unfold, Stream, StreamExt}; +use relay_substrate_client::{ + BlockNumberOf, BlockWithJustification, Chain, Client, Error, HeaderOf, +}; +use relay_utils::relay_loop::Client as RelayClient; +use std::pin::Pin; + +/// Shared updatable reference to the maximal header number that we want to sync from the source. +pub type RequiredHeaderNumberRef = Arc::BlockNumber>>; + +/// Substrate finality proofs stream. +pub type SubstrateFinalityProofsStream

= + Pin> + Send>>; + +/// Substrate finality proof. Specific to the used `FinalityEngine`. +pub type SubstrateFinalityProof

= + <

::FinalityEngine as Engine< +

::SourceChain, + >>::FinalityProof; + +/// Substrate node as finality source. +pub struct SubstrateFinalitySource { + client: Client, + maximal_header_number: Option>, +} + +impl SubstrateFinalitySource

{ + /// Create new headers source using given client. + pub fn new( + client: Client, + maximal_header_number: Option>, + ) -> Self { + SubstrateFinalitySource { client, maximal_header_number } + } + + /// Returns reference to the underlying RPC client. + pub fn client(&self) -> &Client { + &self.client + } + + /// Returns best finalized block number. + pub async fn on_chain_best_finalized_block_number( + &self, + ) -> Result, Error> { + // we **CAN** continue to relay finality proofs if source node is out of sync, because + // target node may be missing proofs that are already available at the source + self.client.best_finalized_header_number().await + } +} + +impl Clone for SubstrateFinalitySource

{ + fn clone(&self) -> Self { + SubstrateFinalitySource { + client: self.client.clone(), + maximal_header_number: self.maximal_header_number.clone(), + } + } +} + +#[async_trait] +impl RelayClient for SubstrateFinalitySource

{ + type Error = Error; + + async fn reconnect(&mut self) -> Result<(), Error> { + self.client.reconnect().await + } +} + +#[async_trait] +impl SourceClient> + for SubstrateFinalitySource

+{ + type FinalityProofsStream = SubstrateFinalityProofsStream

; + + async fn best_finalized_block_number(&self) -> Result, Error> { + let mut finalized_header_number = self.on_chain_best_finalized_block_number().await?; + // never return block number larger than requested. This way we'll never sync headers + // past `maximal_header_number` + if let Some(ref maximal_header_number) = self.maximal_header_number { + let maximal_header_number = *maximal_header_number.lock().await; + if finalized_header_number > maximal_header_number { + finalized_header_number = maximal_header_number; + } + } + Ok(finalized_header_number) + } + + async fn header_and_finality_proof( + &self, + number: BlockNumberOf, + ) -> Result< + ( + relay_substrate_client::SyncHeader>, + Option>, + ), + Error, + > { + let header_hash = self.client.block_hash_by_number(number).await?; + let signed_block = self.client.get_block(Some(header_hash)).await?; + + let justification = signed_block + .justification(P::FinalityEngine::ID) + .map(|raw_justification| { + SubstrateFinalityProof::

::decode(&mut raw_justification.as_slice()) + }) + .transpose() + .map_err(Error::ResponseParseFailed)?; + + Ok((signed_block.header().into(), justification)) + } + + async fn finality_proofs(&self) -> Result { + Ok(unfold( + P::FinalityEngine::finality_proofs(&self.client).await?, + move |subscription| async move { + loop { + let log_error = |err| { + log::error!( + target: "bridge", + "Failed to read justification target from the {} justifications stream: {:?}", + P::SourceChain::NAME, + err, + ); + }; + + let next_justification = subscription + .next() + .await + .map_err(|err| log_error(err.to_string())) + .ok()??; + + let decoded_justification = + >::FinalityProof::decode( + &mut &next_justification[..], + ); + + let justification = match decoded_justification { + Ok(j) => j, + Err(err) => { + log_error(format!("decode failed with error {err:?}")); + continue + }, + }; + + return Some((justification, subscription)) + } + }, + ) + .boxed()) + } +} diff --git a/relays/lib-substrate-relay/src/finality/target.rs b/relays/lib-substrate-relay/src/finality/target.rs new file mode 100644 index 00000000000..09d9ad15f54 --- /dev/null +++ b/relays/lib-substrate-relay/src/finality/target.rs @@ -0,0 +1,135 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate client as Substrate finality proof target. + +use crate::{ + finality::{ + engine::Engine, source::SubstrateFinalityProof, FinalitySyncPipelineAdapter, + SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, + }, + TransactionParams, +}; + +use async_trait::async_trait; +use finality_relay::TargetClient; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, Chain, Client, Error, HeaderIdOf, HeaderOf, SignParam, + SyncHeader, TransactionEra, TransactionTracker, UnsignedTransaction, +}; +use relay_utils::relay_loop::Client as RelayClient; +use sp_core::Pair; + +/// Substrate client as Substrate finality target. +pub struct SubstrateFinalityTarget { + client: Client, + transaction_params: TransactionParams>, +} + +impl SubstrateFinalityTarget

{ + /// Create new Substrate headers target. + pub fn new( + client: Client, + transaction_params: TransactionParams>, + ) -> Self { + SubstrateFinalityTarget { client, transaction_params } + } + + /// Ensure that the bridge pallet at target chain is active. + pub async fn ensure_pallet_active(&self) -> Result<(), Error> { + let is_halted = P::FinalityEngine::is_halted(&self.client).await?; + if is_halted { + return Err(Error::BridgePalletIsHalted) + } + + let is_initialized = P::FinalityEngine::is_initialized(&self.client).await?; + if !is_initialized { + return Err(Error::BridgePalletIsNotInitialized) + } + + Ok(()) + } +} + +impl Clone for SubstrateFinalityTarget

{ + fn clone(&self) -> Self { + SubstrateFinalityTarget { + client: self.client.clone(), + transaction_params: self.transaction_params.clone(), + } + } +} + +#[async_trait] +impl RelayClient for SubstrateFinalityTarget

{ + type Error = Error; + + async fn reconnect(&mut self) -> Result<(), Error> { + self.client.reconnect().await + } +} + +#[async_trait] +impl TargetClient> + for SubstrateFinalityTarget

+where + AccountIdOf: From< as Pair>::Public>, +{ + type TransactionTracker = TransactionTracker>; + + async fn best_finalized_source_block_id(&self) -> Result, Error> { + // we can't continue to relay finality if target node is out of sync, because + // it may have already received (some of) headers that we're going to relay + self.client.ensure_synced().await?; + // we can't relay finality if bridge pallet at target chain is halted + self.ensure_pallet_active().await?; + + Ok(crate::messages_source::read_client_state::( + &self.client, + None, + P::SourceChain::BEST_FINALIZED_HEADER_ID_METHOD, + ) + .await? + .best_finalized_peer_at_best_self) + } + + async fn submit_finality_proof( + &self, + header: SyncHeader>, + proof: SubstrateFinalityProof

, + ) -> Result { + let genesis_hash = *self.client.genesis_hash(); + let transaction_params = self.transaction_params.clone(); + let call = + P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof); + let (spec_version, transaction_version) = self.client.simple_runtime_version().await?; + self.client + .submit_and_watch_signed_extrinsic( + self.transaction_params.signer.public().into(), + SignParam:: { + spec_version, + transaction_version, + genesis_hash, + signer: transaction_params.signer.clone(), + }, + move |best_block_id, transaction_nonce| { + Ok(UnsignedTransaction::new(call.into(), transaction_nonce) + .era(TransactionEra::new(best_block_id, transaction_params.mortality))) + }, + ) + .await + } +} diff --git a/relays/lib-substrate-relay/src/lib.rs b/relays/lib-substrate-relay/src/lib.rs new file mode 100644 index 00000000000..62ae756e003 --- /dev/null +++ b/relays/lib-substrate-relay/src/lib.rs @@ -0,0 +1,98 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! The library of substrate relay. contains some public codes to provide to substrate relay. + +#![warn(missing_docs)] + +pub mod error; +pub mod finality; +pub mod messages_lane; +pub mod messages_metrics; +pub mod messages_source; +pub mod messages_target; +pub mod on_demand; +pub mod parachains; + +/// Transaction creation parameters. +#[derive(Clone, Debug)] +pub struct TransactionParams { + /// Transactions author. + pub signer: TS, + /// Transactions mortality. + pub mortality: Option, +} + +/// Tagged relay account, which balance may be exposed as metrics by the relay. +#[derive(Clone, Debug)] +pub enum TaggedAccount { + /// Account, used to sign headers relay transactions from given bridged chain. + Headers { + /// Account id. + id: AccountId, + /// Name of the bridged chain, which headers are relayed. + bridged_chain: String, + }, + /// Account, used to sign parachains relay transactions from given bridged relay chain. + Parachains { + /// Account id. + id: AccountId, + /// Name of the bridged relay chain with parachain heads. + bridged_chain: String, + }, + /// Account, used to sign message relay transactions from given bridged chain. + Messages { + /// Account id. + id: AccountId, + /// Name of the bridged chain, which sends us messages or delivery confirmations. + bridged_chain: String, + }, + /// Account, used to sign messages with-bridged-chain pallet parameters update transactions. + MessagesPalletOwner { + /// Account id. + id: AccountId, + /// Name of the chain, bridged using messages pallet at our chain. + bridged_chain: String, + }, +} + +impl TaggedAccount { + /// Returns reference to the account id. + pub fn id(&self) -> &AccountId { + match *self { + TaggedAccount::Headers { ref id, .. } => id, + TaggedAccount::Parachains { ref id, .. } => id, + TaggedAccount::Messages { ref id, .. } => id, + TaggedAccount::MessagesPalletOwner { ref id, .. } => id, + } + } + + /// Returns stringified account tag. + pub fn tag(&self) -> String { + match *self { + TaggedAccount::Headers { ref bridged_chain, .. } => format!("{bridged_chain}Headers"), + TaggedAccount::Parachains { ref bridged_chain, .. } => { + format!("{bridged_chain}Parachains") + }, + TaggedAccount::Messages { ref bridged_chain, .. } => { + format!("{bridged_chain}Messages") + }, + TaggedAccount::MessagesPalletOwner { ref bridged_chain, .. } => { + format!("{bridged_chain}MessagesPalletOwner") + }, + } + } +} diff --git a/relays/lib-substrate-relay/src/messages_lane.rs b/relays/lib-substrate-relay/src/messages_lane.rs new file mode 100644 index 00000000000..da138a3d125 --- /dev/null +++ b/relays/lib-substrate-relay/src/messages_lane.rs @@ -0,0 +1,449 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Tools for supporting message lanes between two Substrate-based chains. + +use crate::{ + messages_source::{SubstrateMessagesProof, SubstrateMessagesSource}, + messages_target::{SubstrateMessagesDeliveryProof, SubstrateMessagesTarget}, + on_demand::OnDemandRelay, + TransactionParams, +}; + +use async_std::sync::Arc; +use bp_messages::{LaneId, MessageNonce}; +use bp_runtime::{AccountIdOf, Chain as _, WeightExtraOps}; +use bridge_runtime_common::messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, +}; +use codec::Encode; +use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; +use messages_relay::message_lane::MessageLane; +use pallet_bridge_messages::{Call as BridgeMessagesCall, Config as BridgeMessagesConfig}; +use relay_substrate_client::{ + transaction_stall_timeout, AccountKeyPairOf, BalanceOf, BlockNumberOf, CallOf, Chain, + ChainWithMessages, ChainWithTransactions, Client, HashOf, +}; +use relay_utils::{ + metrics::{GlobalMetrics, MetricsParams, StandaloneMetric}, + STALL_TIMEOUT, +}; +use sp_core::Pair; +use std::{convert::TryFrom, fmt::Debug, marker::PhantomData}; + +/// Substrate -> Substrate messages synchronization pipeline. +pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync { + /// Messages of this chain are relayed to the `TargetChain`. + type SourceChain: ChainWithMessages + ChainWithTransactions; + /// Messages from the `SourceChain` are dispatched on this chain. + type TargetChain: ChainWithMessages + ChainWithTransactions; + + /// How receive messages proof call is built? + type ReceiveMessagesProofCallBuilder: ReceiveMessagesProofCallBuilder; + /// How receive messages delivery proof call is built? + type ReceiveMessagesDeliveryProofCallBuilder: ReceiveMessagesDeliveryProofCallBuilder; +} + +/// Adapter that allows all `SubstrateMessageLane` to act as `MessageLane`. +#[derive(Clone, Debug)] +pub(crate) struct MessageLaneAdapter { + _phantom: PhantomData

, +} + +impl MessageLane for MessageLaneAdapter

{ + const SOURCE_NAME: &'static str = P::SourceChain::NAME; + const TARGET_NAME: &'static str = P::TargetChain::NAME; + + type MessagesProof = SubstrateMessagesProof; + type MessagesReceivingProof = SubstrateMessagesDeliveryProof; + + type SourceChainBalance = BalanceOf; + type SourceHeaderNumber = BlockNumberOf; + type SourceHeaderHash = HashOf; + + type TargetHeaderNumber = BlockNumberOf; + type TargetHeaderHash = HashOf; +} + +/// Substrate <-> Substrate messages relay parameters. +pub struct MessagesRelayParams { + /// Messages source client. + pub source_client: Client, + /// Source transaction params. + pub source_transaction_params: TransactionParams>, + /// Messages target client. + pub target_client: Client, + /// Target transaction params. + pub target_transaction_params: TransactionParams>, + /// Optional on-demand source to target headers relay. + pub source_to_target_headers_relay: + Option>>>, + /// Optional on-demand target to source headers relay. + pub target_to_source_headers_relay: + Option>>>, + /// Identifier of lane that needs to be served. + pub lane_id: LaneId, + /// Metrics parameters. + pub metrics_params: MetricsParams, +} + +/// Run Substrate-to-Substrate messages sync loop. +pub async fn run(params: MessagesRelayParams

) -> anyhow::Result<()> +where + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, + BalanceOf: TryFrom>, +{ + let source_client = params.source_client; + let target_client = params.target_client; + let relayer_id_at_source: AccountIdOf = + params.source_transaction_params.signer.public().into(); + + // 2/3 is reserved for proofs and tx overhead + let max_messages_size_in_single_batch = P::TargetChain::max_extrinsic_size() / 3; + // we don't know exact weights of the Polkadot runtime. So to guess weights we'll be using + // weights from Rialto and then simply dividing it by x2. + let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = + crate::messages_lane::select_delivery_transaction_limits::< + ::WeightInfo, + >( + P::TargetChain::max_extrinsic_weight(), + P::SourceChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + ); + let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = + (max_messages_in_single_batch / 2, max_messages_weight_in_single_batch / 2); + + log::info!( + target: "bridge", + "Starting {} -> {} messages relay.\n\t\ + {} relayer account id: {:?}\n\t\ + Max messages in single transaction: {}\n\t\ + Max messages size in single transaction: {}\n\t\ + Max messages weight in single transaction: {}\n\t\ + Tx mortality: {:?} (~{}m)/{:?} (~{}m)", + P::SourceChain::NAME, + P::TargetChain::NAME, + P::SourceChain::NAME, + relayer_id_at_source, + max_messages_in_single_batch, + max_messages_size_in_single_batch, + max_messages_weight_in_single_batch, + params.source_transaction_params.mortality, + transaction_stall_timeout( + params.source_transaction_params.mortality, + P::SourceChain::AVERAGE_BLOCK_INTERVAL, + STALL_TIMEOUT, + ).as_secs_f64() / 60.0f64, + params.target_transaction_params.mortality, + transaction_stall_timeout( + params.target_transaction_params.mortality, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + STALL_TIMEOUT, + ).as_secs_f64() / 60.0f64, + ); + + messages_relay::message_lane_loop::run( + messages_relay::message_lane_loop::Params { + lane: params.lane_id, + source_tick: P::SourceChain::AVERAGE_BLOCK_INTERVAL, + target_tick: P::TargetChain::AVERAGE_BLOCK_INTERVAL, + reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY, + delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams { + max_unrewarded_relayer_entries_at_target: + P::SourceChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + max_unconfirmed_nonces_at_target: + P::SourceChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + max_messages_in_single_batch, + max_messages_weight_in_single_batch, + max_messages_size_in_single_batch, + }, + }, + SubstrateMessagesSource::

::new( + source_client.clone(), + target_client.clone(), + params.lane_id, + params.source_transaction_params, + params.target_to_source_headers_relay, + ), + SubstrateMessagesTarget::

::new( + target_client, + source_client, + params.lane_id, + relayer_id_at_source, + params.target_transaction_params, + params.source_to_target_headers_relay, + ), + { + GlobalMetrics::new()?.register_and_spawn(¶ms.metrics_params.registry)?; + params.metrics_params + }, + futures::future::pending(), + ) + .await + .map_err(Into::into) +} + +/// Different ways of building `receive_messages_proof` calls. +pub trait ReceiveMessagesProofCallBuilder { + /// Given messages proof, build call of `receive_messages_proof` function of bridge + /// messages module at the target chain. + fn build_receive_messages_proof_call( + relayer_id_at_source: AccountIdOf, + proof: SubstrateMessagesProof, + messages_count: u32, + dispatch_weight: Weight, + trace_call: bool, + ) -> CallOf; +} + +/// Building `receive_messages_proof` call when you have direct access to the target +/// chain runtime. +pub struct DirectReceiveMessagesProofCallBuilder { + _phantom: PhantomData<(P, R, I)>, +} + +impl ReceiveMessagesProofCallBuilder

for DirectReceiveMessagesProofCallBuilder +where + P: SubstrateMessageLane, + R: BridgeMessagesConfig>, + I: 'static, + R::SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain< + MessagesProof = FromBridgedChainMessagesProof>, + >, + CallOf: From> + GetDispatchInfo, +{ + fn build_receive_messages_proof_call( + relayer_id_at_source: AccountIdOf, + proof: SubstrateMessagesProof, + messages_count: u32, + dispatch_weight: Weight, + trace_call: bool, + ) -> CallOf { + let call: CallOf = BridgeMessagesCall::::receive_messages_proof { + relayer_id_at_bridged_chain: relayer_id_at_source, + proof: proof.1, + messages_count, + dispatch_weight, + } + .into(); + if trace_call { + // this trace isn't super-accurate, because limits are for transactions and we + // have a call here, but it provides required information + log::trace!( + target: "bridge", + "Prepared {} -> {} messages delivery call. Weight: {}/{}, size: {}/{}", + P::SourceChain::NAME, + P::TargetChain::NAME, + call.get_dispatch_info().weight, + P::TargetChain::max_extrinsic_weight(), + call.encode().len(), + P::TargetChain::max_extrinsic_size(), + ); + } + call + } +} + +/// Macro that generates `ReceiveMessagesProofCallBuilder` implementation for the case when +/// you only have an access to the mocked version of target chain runtime. In this case you +/// should provide "name" of the call variant for the bridge messages calls and the "name" of +/// the variant for the `receive_messages_proof` call within that first option. +#[rustfmt::skip] +#[macro_export] +macro_rules! generate_mocked_receive_message_proof_call_builder { + ($pipeline:ident, $mocked_builder:ident, $bridge_messages:path, $receive_messages_proof:path) => { + pub struct $mocked_builder; + + impl $crate::messages_lane::ReceiveMessagesProofCallBuilder<$pipeline> + for $mocked_builder + { + fn build_receive_messages_proof_call( + relayer_id_at_source: relay_substrate_client::AccountIdOf< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::SourceChain + >, + proof: $crate::messages_source::SubstrateMessagesProof< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::SourceChain + >, + messages_count: u32, + dispatch_weight: Weight, + _trace_call: bool, + ) -> relay_substrate_client::CallOf< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::TargetChain + > { + $bridge_messages($receive_messages_proof( + relayer_id_at_source, + proof.1, + messages_count, + dispatch_weight, + )) + } + } + }; +} + +/// Different ways of building `receive_messages_delivery_proof` calls. +pub trait ReceiveMessagesDeliveryProofCallBuilder { + /// Given messages delivery proof, build call of `receive_messages_delivery_proof` function of + /// bridge messages module at the source chain. + fn build_receive_messages_delivery_proof_call( + proof: SubstrateMessagesDeliveryProof, + trace_call: bool, + ) -> CallOf; +} + +/// Building `receive_messages_delivery_proof` call when you have direct access to the source +/// chain runtime. +pub struct DirectReceiveMessagesDeliveryProofCallBuilder { + _phantom: PhantomData<(P, R, I)>, +} + +impl ReceiveMessagesDeliveryProofCallBuilder

+ for DirectReceiveMessagesDeliveryProofCallBuilder +where + P: SubstrateMessageLane, + R: BridgeMessagesConfig, + I: 'static, + R::TargetHeaderChain: bp_messages::source_chain::TargetHeaderChain< + R::OutboundPayload, + R::AccountId, + MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof>, + >, + CallOf: From> + GetDispatchInfo, +{ + fn build_receive_messages_delivery_proof_call( + proof: SubstrateMessagesDeliveryProof, + trace_call: bool, + ) -> CallOf { + let call: CallOf = + BridgeMessagesCall::::receive_messages_delivery_proof { + proof: proof.1, + relayers_state: proof.0, + } + .into(); + if trace_call { + // this trace isn't super-accurate, because limits are for transactions and we + // have a call here, but it provides required information + log::trace!( + target: "bridge", + "Prepared {} -> {} delivery confirmation transaction. Weight: {}/{}, size: {}/{}", + P::TargetChain::NAME, + P::SourceChain::NAME, + call.get_dispatch_info().weight, + P::SourceChain::max_extrinsic_weight(), + call.encode().len(), + P::SourceChain::max_extrinsic_size(), + ); + } + call + } +} + +/// Macro that generates `ReceiveMessagesDeliveryProofCallBuilder` implementation for the case when +/// you only have an access to the mocked version of source chain runtime. In this case you +/// should provide "name" of the call variant for the bridge messages calls and the "name" of +/// the variant for the `receive_messages_delivery_proof` call within that first option. +#[rustfmt::skip] +#[macro_export] +macro_rules! generate_mocked_receive_message_delivery_proof_call_builder { + ($pipeline:ident, $mocked_builder:ident, $bridge_messages:path, $receive_messages_delivery_proof:path) => { + pub struct $mocked_builder; + + impl $crate::messages_lane::ReceiveMessagesDeliveryProofCallBuilder<$pipeline> + for $mocked_builder + { + fn build_receive_messages_delivery_proof_call( + proof: $crate::messages_target::SubstrateMessagesDeliveryProof< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::TargetChain + >, + _trace_call: bool, + ) -> relay_substrate_client::CallOf< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::SourceChain + > { + $bridge_messages($receive_messages_delivery_proof(proof.1, proof.0)) + } + } + }; +} + +/// Returns maximal number of messages and their maximal cumulative dispatch weight, based +/// on given chain parameters. +pub fn select_delivery_transaction_limits( + max_extrinsic_weight: Weight, + max_unconfirmed_messages_at_inbound_lane: MessageNonce, +) -> (MessageNonce, Weight) { + // We may try to guess accurate value, based on maximal number of messages and per-message + // weight overhead, but the relay loop isn't using this info in a super-accurate way anyway. + // So just a rough guess: let's say 1/3 of max tx weight is for tx itself and the rest is + // for messages dispatch. + + // Another thing to keep in mind is that our runtimes (when this code was written) accept + // messages with dispatch weight <= max_extrinsic_weight/2. So we can't reserve less than + // that for dispatch. + + let weight_for_delivery_tx = max_extrinsic_weight / 3; + let weight_for_messages_dispatch = max_extrinsic_weight - weight_for_delivery_tx; + + let delivery_tx_base_weight = W::receive_messages_proof_overhead() + + W::receive_messages_proof_outbound_lane_state_overhead(); + let delivery_tx_weight_rest = weight_for_delivery_tx - delivery_tx_base_weight; + + let max_number_of_messages = std::cmp::min( + delivery_tx_weight_rest + .min_components_checked_div(W::receive_messages_proof_messages_overhead(1)) + .unwrap_or(u64::MAX), + max_unconfirmed_messages_at_inbound_lane, + ); + + assert!( + max_number_of_messages > 0, + "Relay should fit at least one message in every delivery transaction", + ); + assert!( + weight_for_messages_dispatch.ref_time() >= max_extrinsic_weight.ref_time() / 2, + "Relay shall be able to deliver messages with dispatch weight = max_extrinsic_weight / 2", + ); + + (max_number_of_messages, weight_for_messages_dispatch) +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_runtime::Chain; + + type RialtoToMillauMessagesWeights = + pallet_bridge_messages::weights::BridgeWeight; + + #[test] + fn select_delivery_transaction_limits_works() { + let (max_count, max_weight) = + select_delivery_transaction_limits::( + bp_millau::Millau::max_extrinsic_weight(), + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + ); + assert_eq!( + (max_count, max_weight), + // We don't actually care about these values, so feel free to update them whenever test + // fails. The only thing to do before that is to ensure that new values looks sane: + // i.e. weight reserved for messages dispatch allows dispatch of non-trivial messages. + // + // Any significant change in this values should attract additional attention. + // + // TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` + (1024, Weight::from_ref_time(216_609_134_667).set_proof_size(217)), + ); + } +} diff --git a/relays/lib-substrate-relay/src/messages_metrics.rs b/relays/lib-substrate-relay/src/messages_metrics.rs new file mode 100644 index 00000000000..37a6d67baae --- /dev/null +++ b/relays/lib-substrate-relay/src/messages_metrics.rs @@ -0,0 +1,129 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Tools for supporting message lanes between two Substrate-based chains. + +use crate::TaggedAccount; + +use codec::Decode; +use frame_system::AccountInfo; +use pallet_balances::AccountData; +use relay_substrate_client::{ + metrics::{FloatStorageValue, FloatStorageValueMetric}, + AccountIdOf, BalanceOf, Chain, ChainWithBalances, Client, Error as SubstrateError, IndexOf, +}; +use relay_utils::metrics::{MetricsParams, StandaloneMetric}; +use sp_core::storage::StorageData; +use sp_runtime::{FixedPointNumber, FixedU128}; +use std::{convert::TryFrom, fmt::Debug, marker::PhantomData}; + +/// Add relay accounts balance metrics. +pub async fn add_relay_balances_metrics( + client: Client, + metrics: &mut MetricsParams, + relay_accounts: &Vec>>, +) -> anyhow::Result<()> +where + BalanceOf: Into + std::fmt::Debug, +{ + if relay_accounts.is_empty() { + return Ok(()) + } + + // if `tokenDecimals` is missing from system properties, we'll be using + let token_decimals = client + .token_decimals() + .await? + .map(|token_decimals| { + log::info!(target: "bridge", "Read `tokenDecimals` for {}: {}", C::NAME, token_decimals); + token_decimals + }) + .unwrap_or_else(|| { + // turns out it is normal not to have this property - e.g. when polkadot binary is + // started using `polkadot-local` chain. Let's use minimal nominal here + log::info!(target: "bridge", "Using default (zero) `tokenDecimals` value for {}", C::NAME); + 0 + }); + let token_decimals = u32::try_from(token_decimals).map_err(|e| { + anyhow::format_err!( + "Token decimals value ({}) of {} doesn't fit into u32: {:?}", + token_decimals, + C::NAME, + e, + ) + })?; + + for account in relay_accounts { + let relay_account_balance_metric = FloatStorageValueMetric::new( + FreeAccountBalance:: { token_decimals, _phantom: Default::default() }, + client.clone(), + C::account_info_storage_key(account.id()), + format!("at_{}_relay_{}_balance", C::NAME, account.tag()), + format!("Balance of the {} relay account at the {}", account.tag(), C::NAME), + )?; + relay_account_balance_metric.register_and_spawn(&metrics.registry)?; + } + + Ok(()) +} + +/// Adapter for `FloatStorageValueMetric` to decode account free balance. +#[derive(Clone, Debug)] +struct FreeAccountBalance { + token_decimals: u32, + _phantom: PhantomData, +} + +impl FloatStorageValue for FreeAccountBalance +where + C: Chain, + BalanceOf: Into, +{ + type Value = FixedU128; + + fn decode( + &self, + maybe_raw_value: Option, + ) -> Result, SubstrateError> { + maybe_raw_value + .map(|raw_value| { + AccountInfo::, AccountData>>::decode(&mut &raw_value.0[..]) + .map_err(SubstrateError::ResponseParseFailed) + .map(|account_data| { + convert_to_token_balance(account_data.data.free.into(), self.token_decimals) + }) + }) + .transpose() + } +} + +/// Convert from raw `u128` balance (nominated in smallest chain token units) to the float regular +/// tokens value. +fn convert_to_token_balance(balance: u128, token_decimals: u32) -> FixedU128 { + FixedU128::from_inner(balance.saturating_mul(FixedU128::DIV / 10u128.pow(token_decimals))) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn token_decimals_used_properly() { + let plancks = 425_000_000_000; + let token_decimals = 10; + let dots = convert_to_token_balance(plancks, token_decimals); + assert_eq!(dots, FixedU128::saturating_from_rational(425, 10)); + } +} diff --git a/relays/lib-substrate-relay/src/messages_source.rs b/relays/lib-substrate-relay/src/messages_source.rs new file mode 100644 index 00000000000..57f84eb6bb1 --- /dev/null +++ b/relays/lib-substrate-relay/src/messages_source.rs @@ -0,0 +1,736 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate client as Substrate messages source. The chain we connect to should have +//! runtime that implements `HeaderApi` to allow bridging with +//! chain. + +use crate::{ + messages_lane::{ + MessageLaneAdapter, ReceiveMessagesDeliveryProofCallBuilder, SubstrateMessageLane, + }, + messages_target::SubstrateMessagesDeliveryProof, + on_demand::OnDemandRelay, + TransactionParams, +}; + +use async_std::sync::Arc; +use async_trait::async_trait; +use bp_messages::{ + storage_keys::{operating_mode_key, outbound_lane_data_key}, + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, MessagesOperatingMode, + OutboundLaneData, OutboundMessageDetails, +}; +use bp_runtime::{BasicOperatingMode, HeaderIdProvider}; +use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; +use codec::{Decode, Encode}; +use frame_support::weights::Weight; +use messages_relay::{ + message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, + message_lane_loop::{ + ClientState, MessageDetails, MessageDetailsMap, MessageProofParameters, SourceClient, + SourceClientState, + }, +}; +use num_traits::Zero; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages, Client, + Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam, TransactionEra, + TransactionTracker, UnsignedTransaction, +}; +use relay_utils::{relay_loop::Client as RelayClient, HeaderId}; +use sp_core::{Bytes, Pair}; +use sp_runtime::{traits::Header as HeaderT, DeserializeOwned}; +use std::ops::RangeInclusive; + +/// Intermediate message proof returned by the source Substrate node. Includes everything +/// required to submit to the target node: cumulative dispatch weight of bundled messages and +/// the proof itself. +pub type SubstrateMessagesProof = (Weight, FromBridgedChainMessagesProof>); +type MessagesToRefine<'a> = Vec<(MessagePayload, &'a mut OutboundMessageDetails)>; + +/// Substrate client as Substrate messages source. +pub struct SubstrateMessagesSource { + source_client: Client, + target_client: Client, + lane_id: LaneId, + transaction_params: TransactionParams>, + target_to_source_headers_relay: Option>>>, +} + +impl SubstrateMessagesSource

{ + /// Create new Substrate headers source. + pub fn new( + source_client: Client, + target_client: Client, + lane_id: LaneId, + transaction_params: TransactionParams>, + target_to_source_headers_relay: Option< + Arc>>, + >, + ) -> Self { + SubstrateMessagesSource { + source_client, + target_client, + lane_id, + transaction_params, + target_to_source_headers_relay, + } + } + + /// Read outbound lane state from the on-chain storage at given block. + async fn outbound_lane_data( + &self, + id: SourceHeaderIdOf>, + ) -> Result, SubstrateError> { + self.source_client + .storage_value( + outbound_lane_data_key( + P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + ), + Some(id.1), + ) + .await + } + + /// Ensure that the messages pallet at source chain is active. + async fn ensure_pallet_active(&self) -> Result<(), SubstrateError> { + ensure_messages_pallet_active::(&self.source_client).await + } +} + +impl Clone for SubstrateMessagesSource

{ + fn clone(&self) -> Self { + Self { + source_client: self.source_client.clone(), + target_client: self.target_client.clone(), + lane_id: self.lane_id, + transaction_params: self.transaction_params.clone(), + target_to_source_headers_relay: self.target_to_source_headers_relay.clone(), + } + } +} + +#[async_trait] +impl RelayClient for SubstrateMessagesSource

{ + type Error = SubstrateError; + + async fn reconnect(&mut self) -> Result<(), SubstrateError> { + self.source_client.reconnect().await?; + self.target_client.reconnect().await + } +} + +#[async_trait] +impl SourceClient> for SubstrateMessagesSource

+where + AccountIdOf: From< as Pair>::Public>, +{ + type TransactionTracker = TransactionTracker>; + + async fn state(&self) -> Result>, SubstrateError> { + // we can't continue to deliver confirmations if source node is out of sync, because + // it may have already received confirmations that we're going to deliver + // + // we can't continue to deliver messages if target node is out of sync, because + // it may have already received (some of) messages that we're going to deliver + self.source_client.ensure_synced().await?; + self.target_client.ensure_synced().await?; + // we can't relay confirmations if messages pallet at source chain is halted + self.ensure_pallet_active().await?; + + read_client_state( + &self.source_client, + Some(&self.target_client), + P::TargetChain::BEST_FINALIZED_HEADER_ID_METHOD, + ) + .await + } + + async fn latest_generated_nonce( + &self, + id: SourceHeaderIdOf>, + ) -> Result<(SourceHeaderIdOf>, MessageNonce), SubstrateError> { + // lane data missing from the storage is fine until first message is sent + let latest_generated_nonce = self + .outbound_lane_data(id) + .await? + .map(|data| data.latest_generated_nonce) + .unwrap_or(0); + Ok((id, latest_generated_nonce)) + } + + async fn latest_confirmed_received_nonce( + &self, + id: SourceHeaderIdOf>, + ) -> Result<(SourceHeaderIdOf>, MessageNonce), SubstrateError> { + // lane data missing from the storage is fine until first message is sent + let latest_received_nonce = self + .outbound_lane_data(id) + .await? + .map(|data| data.latest_received_nonce) + .unwrap_or(0); + Ok((id, latest_received_nonce)) + } + + async fn generated_message_details( + &self, + id: SourceHeaderIdOf>, + nonces: RangeInclusive, + ) -> Result>, SubstrateError> { + let mut out_msgs_details = self + .source_client + .typed_state_call::<_, Vec<_>>( + P::TargetChain::TO_CHAIN_MESSAGE_DETAILS_METHOD.into(), + (self.lane_id, *nonces.start(), *nonces.end()), + Some(id.1), + ) + .await?; + validate_out_msgs_details::(&out_msgs_details, nonces)?; + + // prepare arguments of the inbound message details call (if we need it) + let mut msgs_to_refine = vec![]; + for out_msg_details in out_msgs_details.iter_mut() { + // in our current strategy all messages are supposed to be paid at the target chain + + // for pay-at-target messages we may want to ask target chain for + // refined dispatch weight + let msg_key = bp_messages::storage_keys::message_key( + P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + out_msg_details.nonce, + ); + let msg_payload: MessagePayload = + self.source_client.storage_value(msg_key, Some(id.1)).await?.ok_or_else(|| { + SubstrateError::Custom(format!( + "Message to {} {:?}/{} is missing from runtime the storage of {} at {:?}", + P::TargetChain::NAME, + self.lane_id, + out_msg_details.nonce, + P::SourceChain::NAME, + id, + )) + })?; + + msgs_to_refine.push((msg_payload, out_msg_details)); + } + + for mut msgs_to_refine_batch in + split_msgs_to_refine::(self.lane_id, msgs_to_refine)? + { + let in_msgs_details = self + .target_client + .typed_state_call::<_, Vec>( + P::SourceChain::FROM_CHAIN_MESSAGE_DETAILS_METHOD.into(), + (self.lane_id, &msgs_to_refine_batch), + None, + ) + .await?; + if in_msgs_details.len() != msgs_to_refine_batch.len() { + return Err(SubstrateError::Custom(format!( + "Call of {} at {} has returned {} entries instead of expected {}", + P::SourceChain::FROM_CHAIN_MESSAGE_DETAILS_METHOD, + P::TargetChain::NAME, + in_msgs_details.len(), + msgs_to_refine_batch.len(), + ))) + } + for ((_, out_msg_details), in_msg_details) in + msgs_to_refine_batch.iter_mut().zip(in_msgs_details) + { + log::trace!( + target: "bridge", + "Refined weight of {}->{} message {:?}/{}: at-source: {}, at-target: {}", + P::SourceChain::NAME, + P::TargetChain::NAME, + self.lane_id, + out_msg_details.nonce, + out_msg_details.dispatch_weight, + in_msg_details.dispatch_weight, + ); + out_msg_details.dispatch_weight = in_msg_details.dispatch_weight; + } + } + + let mut msgs_details_map = MessageDetailsMap::new(); + for out_msg_details in out_msgs_details { + msgs_details_map.insert( + out_msg_details.nonce, + MessageDetails { + dispatch_weight: out_msg_details.dispatch_weight, + size: out_msg_details.size as _, + reward: Zero::zero(), + }, + ); + } + + Ok(msgs_details_map) + } + + async fn prove_messages( + &self, + id: SourceHeaderIdOf>, + nonces: RangeInclusive, + proof_parameters: MessageProofParameters, + ) -> Result< + ( + SourceHeaderIdOf>, + RangeInclusive, + as MessageLane>::MessagesProof, + ), + SubstrateError, + > { + let mut storage_keys = + Vec::with_capacity(nonces.end().saturating_sub(*nonces.start()) as usize + 1); + let mut message_nonce = *nonces.start(); + while message_nonce <= *nonces.end() { + let message_key = bp_messages::storage_keys::message_key( + P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + message_nonce, + ); + storage_keys.push(message_key); + message_nonce += 1; + } + if proof_parameters.outbound_state_proof_required { + storage_keys.push(bp_messages::storage_keys::outbound_lane_data_key( + P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + )); + } + + let proof = self + .source_client + .prove_storage(storage_keys, id.1) + .await? + .into_iter_nodes() + .collect(); + let proof = FromBridgedChainMessagesProof { + bridged_header_hash: id.1, + storage_proof: proof, + lane: self.lane_id, + nonces_start: *nonces.start(), + nonces_end: *nonces.end(), + }; + Ok((id, nonces, (proof_parameters.dispatch_weight, proof))) + } + + async fn submit_messages_receiving_proof( + &self, + _generated_at_block: TargetHeaderIdOf>, + proof: as MessageLane>::MessagesReceivingProof, + ) -> Result { + let genesis_hash = *self.source_client.genesis_hash(); + let transaction_params = self.transaction_params.clone(); + let (spec_version, transaction_version) = + self.source_client.simple_runtime_version().await?; + self.source_client + .submit_and_watch_signed_extrinsic( + self.transaction_params.signer.public().into(), + SignParam:: { + spec_version, + transaction_version, + genesis_hash, + signer: self.transaction_params.signer.clone(), + }, + move |best_block_id, transaction_nonce| { + make_messages_delivery_proof_transaction::

( + &transaction_params, + best_block_id, + transaction_nonce, + proof, + true, + ) + }, + ) + .await + } + + async fn require_target_header_on_source(&self, id: TargetHeaderIdOf>) { + if let Some(ref target_to_source_headers_relay) = self.target_to_source_headers_relay { + target_to_source_headers_relay.require_more_headers(id.0).await; + } + } +} + +/// Ensure that the messages pallet at source chain is active. +pub(crate) async fn ensure_messages_pallet_active( + client: &Client, +) -> Result<(), SubstrateError> +where + AtChain: ChainWithMessages, + WithChain: ChainWithMessages, +{ + let operating_mode = client + .storage_value(operating_mode_key(WithChain::WITH_CHAIN_MESSAGES_PALLET_NAME), None) + .await?; + let is_halted = + operating_mode == Some(MessagesOperatingMode::Basic(BasicOperatingMode::Halted)); + if is_halted { + Err(SubstrateError::BridgePalletIsHalted) + } else { + Ok(()) + } +} + +/// Make messages delivery proof transaction from given proof. +fn make_messages_delivery_proof_transaction( + source_transaction_params: &TransactionParams>, + source_best_block_id: HeaderIdOf, + transaction_nonce: IndexOf, + proof: SubstrateMessagesDeliveryProof, + trace_call: bool, +) -> Result, SubstrateError> { + let call = + P::ReceiveMessagesDeliveryProofCallBuilder::build_receive_messages_delivery_proof_call( + proof, trace_call, + ); + Ok(UnsignedTransaction::new(call.into(), transaction_nonce) + .era(TransactionEra::new(source_best_block_id, source_transaction_params.mortality))) +} + +/// Read best blocks from given client. +/// +/// This function assumes that the chain that is followed by the `self_client` has +/// bridge GRANDPA pallet deployed and it provides `best_finalized_header_id_method_name` +/// runtime API to read the best finalized Bridged chain header. +/// +/// If `peer_client` is `None`, the value of `actual_best_finalized_peer_at_best_self` will +/// always match the `best_finalized_peer_at_best_self`. +pub async fn read_client_state( + self_client: &Client, + peer_client: Option<&Client>, + best_finalized_header_id_method_name: &str, +) -> Result, HeaderIdOf>, SubstrateError> +where + SelfChain: Chain, + SelfChain::Header: DeserializeOwned, + SelfChain::Index: DeserializeOwned, + PeerChain: Chain, +{ + // let's read our state first: we need best finalized header hash on **this** chain + let self_best_finalized_header_hash = self_client.best_finalized_header_hash().await?; + let self_best_finalized_header = + self_client.header_by_hash(self_best_finalized_header_hash).await?; + let self_best_finalized_id = self_best_finalized_header.id(); + + // now let's read our best header on **this** chain + let self_best_header = self_client.best_header().await?; + let self_best_hash = self_best_header.hash(); + let self_best_id = self_best_header.id(); + + // now let's read id of best finalized peer header at our best finalized block + let peer_on_self_best_finalized_id = + best_finalized_peer_header_at_self::( + self_client, + self_best_hash, + best_finalized_header_id_method_name, + ) + .await?; + + // read actual header, matching the `peer_on_self_best_finalized_id` from the peer chain + let actual_peer_on_self_best_finalized_id = match peer_client { + Some(peer_client) => { + let actual_peer_on_self_best_finalized = + peer_client.header_by_number(peer_on_self_best_finalized_id.0).await?; + actual_peer_on_self_best_finalized.id() + }, + None => peer_on_self_best_finalized_id, + }; + + Ok(ClientState { + best_self: self_best_id, + best_finalized_self: self_best_finalized_id, + best_finalized_peer_at_best_self: peer_on_self_best_finalized_id, + actual_best_finalized_peer_at_best_self: actual_peer_on_self_best_finalized_id, + }) +} + +/// Reads best `PeerChain` header known to the `SelfChain` using provided runtime API method. +/// +/// Method is supposed to be the `FinalityApi::best_finalized()` method. +pub async fn best_finalized_peer_header_at_self( + self_client: &Client, + at_self_hash: HashOf, + best_finalized_header_id_method_name: &str, +) -> Result, SubstrateError> +where + SelfChain: Chain, + PeerChain: Chain, +{ + // now let's read id of best finalized peer header at our best finalized block + let encoded_best_finalized_peer_on_self = self_client + .state_call( + best_finalized_header_id_method_name.into(), + Bytes(Vec::new()), + Some(at_self_hash), + ) + .await?; + + Option::, BlockNumberOf>>::decode( + &mut &encoded_best_finalized_peer_on_self.0[..], + ) + .map_err(SubstrateError::ResponseParseFailed)? + .map(Ok) + .unwrap_or(Err(SubstrateError::BridgePalletIsNotInitialized)) +} + +fn validate_out_msgs_details( + out_msgs_details: &[OutboundMessageDetails], + nonces: RangeInclusive, +) -> Result<(), SubstrateError> { + let make_missing_nonce_error = |expected_nonce| { + Err(SubstrateError::Custom(format!( + "Missing nonce {} in message_details call result. Expected all nonces from {:?}", + expected_nonce, nonces, + ))) + }; + + if out_msgs_details.len() > nonces.clone().count() { + return Err(SubstrateError::Custom( + "More messages than requested returned by the message_details call.".into(), + )) + } + + // Check if last nonce is missing. The loop below is not checking this. + if out_msgs_details.is_empty() && !nonces.is_empty() { + return make_missing_nonce_error(*nonces.end()) + } + + let mut nonces_iter = nonces.clone().rev().peekable(); + let mut out_msgs_details_iter = out_msgs_details.iter().rev(); + while let Some((out_msg_details, &nonce)) = out_msgs_details_iter.next().zip(nonces_iter.peek()) + { + nonces_iter.next(); + if out_msg_details.nonce != nonce { + // Some nonces are missing from the middle/tail of the range. This is critical error. + return make_missing_nonce_error(nonce) + } + } + + // Check if some nonces from the beginning of the range are missing. This may happen if + // some messages were already pruned from the source node. This is not a critical error + // and will be auto-resolved by messages lane (and target node). + if nonces_iter.peek().is_some() { + log::info!( + target: "bridge", + "Some messages are missing from the {} node: {:?}. Target node may be out of sync?", + C::NAME, + nonces_iter.rev().collect::>(), + ); + } + + Ok(()) +} + +fn split_msgs_to_refine( + lane_id: LaneId, + msgs_to_refine: MessagesToRefine, +) -> Result, SubstrateError> { + let max_batch_size = Target::max_extrinsic_size() as usize; + let mut batches = vec![]; + + let mut current_msgs_batch = msgs_to_refine; + while !current_msgs_batch.is_empty() { + let mut next_msgs_batch = vec![]; + while (lane_id, ¤t_msgs_batch).encoded_size() > max_batch_size { + if current_msgs_batch.len() <= 1 { + return Err(SubstrateError::Custom(format!( + "Call of {} at {} can't be executed even if only one message is supplied. \ + max_extrinsic_size(): {}", + Source::FROM_CHAIN_MESSAGE_DETAILS_METHOD, + Target::NAME, + Target::max_extrinsic_size(), + ))) + } + + if let Some(msg) = current_msgs_batch.pop() { + next_msgs_batch.insert(0, msg); + } + } + + batches.push(current_msgs_batch); + current_msgs_batch = next_msgs_batch; + } + + Ok(batches) +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_runtime::Chain as ChainBase; + use relay_rialto_client::Rialto; + use relay_rococo_client::Rococo; + use relay_wococo_client::Wococo; + + fn message_details_from_rpc( + nonces: RangeInclusive, + ) -> Vec { + nonces + .into_iter() + .map(|nonce| bp_messages::OutboundMessageDetails { + nonce, + dispatch_weight: Weight::zero(), + size: 0, + }) + .collect() + } + + #[test] + fn validate_out_msgs_details_succeeds_if_no_messages_are_missing() { + assert!( + validate_out_msgs_details::(&message_details_from_rpc(1..=3), 1..=3,).is_ok() + ); + } + + #[test] + fn validate_out_msgs_details_succeeds_if_head_messages_are_missing() { + assert!( + validate_out_msgs_details::(&message_details_from_rpc(2..=3), 1..=3,).is_ok() + ) + } + + #[test] + fn validate_out_msgs_details_fails_if_mid_messages_are_missing() { + let mut message_details_from_rpc = message_details_from_rpc(1..=3); + message_details_from_rpc.remove(1); + assert!(matches!( + validate_out_msgs_details::(&message_details_from_rpc, 1..=3,), + Err(SubstrateError::Custom(_)) + )); + } + + #[test] + fn validate_out_msgs_details_map_fails_if_tail_messages_are_missing() { + assert!(matches!( + validate_out_msgs_details::(&message_details_from_rpc(1..=2), 1..=3,), + Err(SubstrateError::Custom(_)) + )); + } + + #[test] + fn validate_out_msgs_details_fails_if_all_messages_are_missing() { + assert!(matches!( + validate_out_msgs_details::(&[], 1..=3), + Err(SubstrateError::Custom(_)) + )); + } + + #[test] + fn validate_out_msgs_details_fails_if_more_messages_than_nonces() { + assert!(matches!( + validate_out_msgs_details::(&message_details_from_rpc(1..=5), 2..=5,), + Err(SubstrateError::Custom(_)) + )); + } + + fn check_split_msgs_to_refine( + payload_sizes: Vec, + expected_batches: Result, ()>, + ) { + let mut out_msgs_details = vec![]; + for (idx, _) in payload_sizes.iter().enumerate() { + out_msgs_details.push(OutboundMessageDetails { + nonce: idx as MessageNonce, + dispatch_weight: Weight::zero(), + size: 0, + }); + } + + let mut msgs_to_refine = vec![]; + for (&payload_size, out_msg_details) in + payload_sizes.iter().zip(out_msgs_details.iter_mut()) + { + let payload = vec![1u8; payload_size]; + msgs_to_refine.push((payload, out_msg_details)); + } + + let maybe_batches = split_msgs_to_refine::([0, 0, 0, 0], msgs_to_refine); + match expected_batches { + Ok(expected_batches) => { + let batches = maybe_batches.unwrap(); + let mut idx = 0; + assert_eq!(batches.len(), expected_batches.len()); + for (batch, &expected_batch_size) in batches.iter().zip(expected_batches.iter()) { + assert_eq!(batch.len(), expected_batch_size); + for msg_to_refine in batch { + assert_eq!(msg_to_refine.0.len(), payload_sizes[idx]); + idx += 1; + } + } + }, + Err(_) => { + matches!(maybe_batches, Err(SubstrateError::Custom(_))); + }, + } + } + + #[test] + fn test_split_msgs_to_refine() { + let max_extrinsic_size = Rococo::max_extrinsic_size() as usize; + + // Check that an error is returned when one of the messages is too big. + check_split_msgs_to_refine(vec![max_extrinsic_size], Err(())); + check_split_msgs_to_refine(vec![50, 100, max_extrinsic_size, 200], Err(())); + + // Otherwise check that the split is valid. + check_split_msgs_to_refine(vec![100, 200, 300, 400], Ok(vec![4])); + check_split_msgs_to_refine( + vec![ + 50, + 100, + max_extrinsic_size - 500, + 500, + 1000, + 1500, + max_extrinsic_size - 3500, + 5000, + 10000, + ], + Ok(vec![3, 4, 2]), + ); + check_split_msgs_to_refine( + vec![ + 50, + 100, + max_extrinsic_size - 150, + 500, + 1000, + 1500, + max_extrinsic_size - 3000, + 5000, + 10000, + ], + Ok(vec![2, 1, 3, 1, 2]), + ); + check_split_msgs_to_refine( + vec![ + 5000, + 10000, + max_extrinsic_size - 3500, + 500, + 1000, + 1500, + max_extrinsic_size - 500, + 50, + 100, + ], + Ok(vec![2, 4, 3]), + ); + } +} diff --git a/relays/lib-substrate-relay/src/messages_target.rs b/relays/lib-substrate-relay/src/messages_target.rs new file mode 100644 index 00000000000..22a50acf37e --- /dev/null +++ b/relays/lib-substrate-relay/src/messages_target.rs @@ -0,0 +1,298 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate client as Substrate messages target. The chain we connect to should have +//! runtime that implements `HeaderApi` to allow bridging with +//! chain. + +use crate::{ + messages_lane::{MessageLaneAdapter, ReceiveMessagesProofCallBuilder, SubstrateMessageLane}, + messages_source::{ensure_messages_pallet_active, read_client_state, SubstrateMessagesProof}, + on_demand::OnDemandRelay, + TransactionParams, +}; + +use async_std::sync::Arc; +use async_trait::async_trait; +use bp_messages::{ + storage_keys::inbound_lane_data_key, total_unrewarded_messages, InboundLaneData, LaneId, + MessageNonce, UnrewardedRelayersState, +}; +use bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof; +use messages_relay::{ + message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, + message_lane_loop::{NoncesSubmitArtifacts, TargetClient, TargetClientState}, +}; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages, Client, + Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam, TransactionEra, + TransactionTracker, UnsignedTransaction, +}; +use relay_utils::relay_loop::Client as RelayClient; +use sp_core::Pair; +use std::{collections::VecDeque, convert::TryFrom, ops::RangeInclusive}; + +/// Message receiving proof returned by the target Substrate node. +pub type SubstrateMessagesDeliveryProof = + (UnrewardedRelayersState, FromBridgedChainMessagesDeliveryProof>); + +/// Substrate client as Substrate messages target. +pub struct SubstrateMessagesTarget { + target_client: Client, + source_client: Client, + lane_id: LaneId, + relayer_id_at_source: AccountIdOf, + transaction_params: TransactionParams>, + source_to_target_headers_relay: Option>>>, +} + +impl SubstrateMessagesTarget

{ + /// Create new Substrate headers target. + pub fn new( + target_client: Client, + source_client: Client, + lane_id: LaneId, + relayer_id_at_source: AccountIdOf, + transaction_params: TransactionParams>, + source_to_target_headers_relay: Option< + Arc>>, + >, + ) -> Self { + SubstrateMessagesTarget { + target_client, + source_client, + lane_id, + relayer_id_at_source, + transaction_params, + source_to_target_headers_relay, + } + } + + /// Read inbound lane state from the on-chain storage at given block. + async fn inbound_lane_data( + &self, + id: TargetHeaderIdOf>, + ) -> Result>>, SubstrateError> { + self.target_client + .storage_value( + inbound_lane_data_key( + P::SourceChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + ), + Some(id.1), + ) + .await + } + + /// Ensure that the messages pallet at target chain is active. + async fn ensure_pallet_active(&self) -> Result<(), SubstrateError> { + ensure_messages_pallet_active::(&self.target_client).await + } +} + +impl Clone for SubstrateMessagesTarget

{ + fn clone(&self) -> Self { + Self { + target_client: self.target_client.clone(), + source_client: self.source_client.clone(), + lane_id: self.lane_id, + relayer_id_at_source: self.relayer_id_at_source.clone(), + transaction_params: self.transaction_params.clone(), + source_to_target_headers_relay: self.source_to_target_headers_relay.clone(), + } + } +} + +#[async_trait] +impl RelayClient for SubstrateMessagesTarget

{ + type Error = SubstrateError; + + async fn reconnect(&mut self) -> Result<(), SubstrateError> { + self.target_client.reconnect().await?; + self.source_client.reconnect().await + } +} + +#[async_trait] +impl TargetClient> for SubstrateMessagesTarget

+where + AccountIdOf: From< as Pair>::Public>, + BalanceOf: TryFrom>, +{ + type TransactionTracker = TransactionTracker>; + + async fn state(&self) -> Result>, SubstrateError> { + // we can't continue to deliver confirmations if source node is out of sync, because + // it may have already received confirmations that we're going to deliver + // + // we can't continue to deliver messages if target node is out of sync, because + // it may have already received (some of) messages that we're going to deliver + self.source_client.ensure_synced().await?; + self.target_client.ensure_synced().await?; + // we can't relay messages if messages pallet at target chain is halted + self.ensure_pallet_active().await?; + + read_client_state( + &self.target_client, + Some(&self.source_client), + P::SourceChain::BEST_FINALIZED_HEADER_ID_METHOD, + ) + .await + } + + async fn latest_received_nonce( + &self, + id: TargetHeaderIdOf>, + ) -> Result<(TargetHeaderIdOf>, MessageNonce), SubstrateError> { + // lane data missing from the storage is fine until first message is received + let latest_received_nonce = self + .inbound_lane_data(id) + .await? + .map(|data| data.last_delivered_nonce()) + .unwrap_or(0); + Ok((id, latest_received_nonce)) + } + + async fn latest_confirmed_received_nonce( + &self, + id: TargetHeaderIdOf>, + ) -> Result<(TargetHeaderIdOf>, MessageNonce), SubstrateError> { + // lane data missing from the storage is fine until first message is received + let last_confirmed_nonce = self + .inbound_lane_data(id) + .await? + .map(|data| data.last_confirmed_nonce) + .unwrap_or(0); + Ok((id, last_confirmed_nonce)) + } + + async fn unrewarded_relayers_state( + &self, + id: TargetHeaderIdOf>, + ) -> Result<(TargetHeaderIdOf>, UnrewardedRelayersState), SubstrateError> + { + let inbound_lane_data = self.inbound_lane_data(id).await?; + let last_delivered_nonce = + inbound_lane_data.as_ref().map(|data| data.last_delivered_nonce()).unwrap_or(0); + let relayers = inbound_lane_data.map(|data| data.relayers).unwrap_or_else(VecDeque::new); + let unrewarded_relayers_state = bp_messages::UnrewardedRelayersState { + unrewarded_relayer_entries: relayers.len() as _, + messages_in_oldest_entry: relayers + .front() + .map(|entry| 1 + entry.messages.end - entry.messages.begin) + .unwrap_or(0), + total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX), + last_delivered_nonce, + }; + Ok((id, unrewarded_relayers_state)) + } + + async fn prove_messages_receiving( + &self, + id: TargetHeaderIdOf>, + ) -> Result< + ( + TargetHeaderIdOf>, + as MessageLane>::MessagesReceivingProof, + ), + SubstrateError, + > { + let (id, relayers_state) = self.unrewarded_relayers_state(id).await?; + let inbound_data_key = bp_messages::storage_keys::inbound_lane_data_key( + P::SourceChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + ); + let proof = self + .target_client + .prove_storage(vec![inbound_data_key], id.1) + .await? + .into_iter_nodes() + .collect(); + let proof = FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: id.1, + storage_proof: proof, + lane: self.lane_id, + }; + Ok((id, (relayers_state, proof))) + } + + async fn submit_messages_proof( + &self, + _generated_at_header: SourceHeaderIdOf>, + nonces: RangeInclusive, + proof: as MessageLane>::MessagesProof, + ) -> Result, SubstrateError> { + let genesis_hash = *self.target_client.genesis_hash(); + let transaction_params = self.transaction_params.clone(); + let relayer_id_at_source = self.relayer_id_at_source.clone(); + let nonces_clone = nonces.clone(); + let (spec_version, transaction_version) = + self.target_client.simple_runtime_version().await?; + let tx_tracker = self + .target_client + .submit_and_watch_signed_extrinsic( + self.transaction_params.signer.public().into(), + SignParam:: { + spec_version, + transaction_version, + genesis_hash, + signer: self.transaction_params.signer.clone(), + }, + move |best_block_id, transaction_nonce| { + make_messages_delivery_transaction::

( + &transaction_params, + best_block_id, + transaction_nonce, + relayer_id_at_source, + nonces_clone, + proof, + true, + ) + }, + ) + .await?; + Ok(NoncesSubmitArtifacts { nonces, tx_tracker }) + } + + async fn require_source_header_on_target(&self, id: SourceHeaderIdOf>) { + if let Some(ref source_to_target_headers_relay) = self.source_to_target_headers_relay { + source_to_target_headers_relay.require_more_headers(id.0).await; + } + } +} + +/// Make messages delivery transaction from given proof. +fn make_messages_delivery_transaction( + target_transaction_params: &TransactionParams>, + target_best_block_id: HeaderIdOf, + transaction_nonce: IndexOf, + relayer_id_at_source: AccountIdOf, + nonces: RangeInclusive, + proof: SubstrateMessagesProof, + trace_call: bool, +) -> Result, SubstrateError> { + let messages_count = nonces.end() - nonces.start() + 1; + let dispatch_weight = proof.0; + let call = P::ReceiveMessagesProofCallBuilder::build_receive_messages_proof_call( + relayer_id_at_source, + proof, + messages_count as _, + dispatch_weight, + trace_call, + ); + Ok(UnsignedTransaction::new(call.into(), transaction_nonce) + .era(TransactionEra::new(target_best_block_id, target_transaction_params.mortality))) +} diff --git a/relays/lib-substrate-relay/src/on_demand/headers.rs b/relays/lib-substrate-relay/src/on_demand/headers.rs new file mode 100644 index 00000000000..c0603cda8cd --- /dev/null +++ b/relays/lib-substrate-relay/src/on_demand/headers.rs @@ -0,0 +1,457 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! On-demand Substrate -> Substrate header finality relay. + +use async_std::sync::{Arc, Mutex}; +use async_trait::async_trait; +use bp_header_chain::ConsensusLogReader; +use futures::{select, FutureExt}; +use num_traits::{One, Zero}; +use sp_runtime::traits::Header; + +use finality_relay::{FinalitySyncParams, TargetClient as FinalityTargetClient}; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, Client}; +use relay_utils::{ + metrics::MetricsParams, relay_loop::Client as RelayClient, FailedClient, MaybeConnectionError, + STALL_TIMEOUT, +}; + +use crate::{ + finality::{ + engine::Engine, + source::{RequiredHeaderNumberRef, SubstrateFinalitySource}, + target::SubstrateFinalityTarget, + SubstrateFinalitySyncPipeline, RECENT_FINALITY_PROOFS_LIMIT, + }, + on_demand::OnDemandRelay, + TransactionParams, +}; + +/// On-demand Substrate <-> Substrate header finality relay. +/// +/// This relay may be requested to sync more headers, whenever some other relay (e.g. messages +/// relay) needs it to continue its regular work. When enough headers are relayed, on-demand stops +/// syncing headers. +#[derive(Clone)] +pub struct OnDemandHeadersRelay { + /// Relay task name. + relay_task_name: String, + /// Shared reference to maximal required finalized header number. + required_header_number: RequiredHeaderNumberRef, +} + +impl OnDemandHeadersRelay { + /// Create new on-demand headers relay. + pub fn new>( + source_client: Client, + target_client: Client, + target_transaction_params: TransactionParams>, + only_mandatory_headers: bool, + ) -> Self + where + AccountIdOf: + From< as sp_core::Pair>::Public>, + { + let required_header_number = Arc::new(Mutex::new(Zero::zero())); + let this = OnDemandHeadersRelay { + relay_task_name: on_demand_headers_relay_name::(), + required_header_number: required_header_number.clone(), + }; + async_std::task::spawn(async move { + background_task::

( + source_client, + target_client, + target_transaction_params, + only_mandatory_headers, + required_header_number, + ) + .await; + }); + + this + } +} + +#[async_trait] +impl OnDemandRelay> + for OnDemandHeadersRelay +{ + async fn require_more_headers(&self, required_header: BlockNumberOf) { + let mut required_header_number = self.required_header_number.lock().await; + if required_header > *required_header_number { + log::trace!( + target: "bridge", + "[{}] More {} headers required. Going to sync up to the {}", + self.relay_task_name, + SourceChain::NAME, + required_header, + ); + + *required_header_number = required_header; + } + } +} + +/// Background task that is responsible for starting headers relay. +async fn background_task( + source_client: Client, + target_client: Client, + target_transaction_params: TransactionParams>, + only_mandatory_headers: bool, + required_header_number: RequiredHeaderNumberRef, +) where + AccountIdOf: From< as sp_core::Pair>::Public>, +{ + let relay_task_name = on_demand_headers_relay_name::(); + let target_transactions_mortality = target_transaction_params.mortality; + let mut finality_source = SubstrateFinalitySource::

::new( + source_client.clone(), + Some(required_header_number.clone()), + ); + let mut finality_target = + SubstrateFinalityTarget::new(target_client.clone(), target_transaction_params); + let mut latest_non_mandatory_at_source = Zero::zero(); + + let mut restart_relay = true; + let finality_relay_task = futures::future::Fuse::terminated(); + futures::pin_mut!(finality_relay_task); + + loop { + select! { + _ = async_std::task::sleep(P::TargetChain::AVERAGE_BLOCK_INTERVAL).fuse() => {}, + _ = finality_relay_task => { + // this should never happen in practice given the current code + restart_relay = true; + }, + } + + // read best finalized source header number from source + let best_finalized_source_header_at_source = + best_finalized_source_header_at_source(&finality_source, &relay_task_name).await; + if matches!(best_finalized_source_header_at_source, Err(ref e) if e.is_connection_error()) { + relay_utils::relay_loop::reconnect_failed_client( + FailedClient::Source, + relay_utils::relay_loop::RECONNECT_DELAY, + &mut finality_source, + &mut finality_target, + ) + .await; + continue + } + + // read best finalized source header number from target + let best_finalized_source_header_at_target = + best_finalized_source_header_at_target::

(&finality_target, &relay_task_name).await; + if matches!(best_finalized_source_header_at_target, Err(ref e) if e.is_connection_error()) { + relay_utils::relay_loop::reconnect_failed_client( + FailedClient::Target, + relay_utils::relay_loop::RECONNECT_DELAY, + &mut finality_source, + &mut finality_target, + ) + .await; + continue + } + + // submit mandatory header if some headers are missing + let best_finalized_source_header_at_source_fmt = + format!("{best_finalized_source_header_at_source:?}"); + let best_finalized_source_header_at_target_fmt = + format!("{best_finalized_source_header_at_target:?}"); + let required_header_number_value = *required_header_number.lock().await; + let mandatory_scan_range = mandatory_headers_scan_range::( + best_finalized_source_header_at_source.ok(), + best_finalized_source_header_at_target.ok(), + required_header_number_value, + ) + .await; + + log::trace!( + target: "bridge", + "[{}] Mandatory headers scan range: ({:?}, {:?}, {:?}) -> {:?}", + relay_task_name, + required_header_number_value, + best_finalized_source_header_at_source_fmt, + best_finalized_source_header_at_target_fmt, + mandatory_scan_range, + ); + + if let Some(mandatory_scan_range) = mandatory_scan_range { + let relay_mandatory_header_result = relay_mandatory_header_from_range( + &finality_source, + &required_header_number, + best_finalized_source_header_at_target_fmt, + ( + std::cmp::max(mandatory_scan_range.0, latest_non_mandatory_at_source), + mandatory_scan_range.1, + ), + &relay_task_name, + ) + .await; + match relay_mandatory_header_result { + Ok(true) => (), + Ok(false) => { + // there are no (or we don't need to relay them) mandatory headers in the range + // => to avoid scanning the same headers over and over again, remember that + latest_non_mandatory_at_source = mandatory_scan_range.1; + + log::trace!( + target: "bridge", + "[{}] No mandatory {} headers in the range {:?}", + relay_task_name, + P::SourceChain::NAME, + mandatory_scan_range, + ); + }, + Err(e) => { + log::warn!( + target: "bridge", + "[{}] Failed to scan mandatory {} headers range ({:?}): {:?}", + relay_task_name, + P::SourceChain::NAME, + mandatory_scan_range, + e, + ); + + if e.is_connection_error() { + relay_utils::relay_loop::reconnect_failed_client( + FailedClient::Source, + relay_utils::relay_loop::RECONNECT_DELAY, + &mut finality_source, + &mut finality_target, + ) + .await; + continue + } + }, + } + } + + // start/restart relay + if restart_relay { + let stall_timeout = relay_substrate_client::transaction_stall_timeout( + target_transactions_mortality, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + STALL_TIMEOUT, + ); + + log::info!( + target: "bridge", + "[{}] Starting on-demand headers relay task\n\t\ + Only mandatory headers: {}\n\t\ + Tx mortality: {:?} (~{}m)\n\t\ + Stall timeout: {:?}", + relay_task_name, + only_mandatory_headers, + target_transactions_mortality, + stall_timeout.as_secs_f64() / 60.0f64, + stall_timeout, + ); + + finality_relay_task.set( + finality_relay::run( + finality_source.clone(), + finality_target.clone(), + FinalitySyncParams { + tick: std::cmp::max( + P::SourceChain::AVERAGE_BLOCK_INTERVAL, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + ), + recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT, + stall_timeout, + only_mandatory_headers, + }, + MetricsParams::disabled(), + futures::future::pending(), + ) + .fuse(), + ); + + restart_relay = false; + } + } +} + +/// Returns `Some()` with inclusive range of headers which must be scanned for mandatory headers +/// and the first of such headers must be submitted to the target node. +async fn mandatory_headers_scan_range( + best_finalized_source_header_at_source: Option, + best_finalized_source_header_at_target: Option, + required_header_number: BlockNumberOf, +) -> Option<(C::BlockNumber, C::BlockNumber)> { + // if we have been unable to read header number from the target, then let's assume + // that it is the same as required header number. Otherwise we risk submitting + // unneeded transactions + let best_finalized_source_header_at_target = + best_finalized_source_header_at_target.unwrap_or(required_header_number); + + // if we have been unable to read header number from the source, then let's assume + // that it is the same as at the target + let best_finalized_source_header_at_source = + best_finalized_source_header_at_source.unwrap_or(best_finalized_source_header_at_target); + + // if relay is already asked to sync more headers than we have at source, don't do anything yet + if required_header_number >= best_finalized_source_header_at_source { + return None + } + + Some(( + best_finalized_source_header_at_target + One::one(), + best_finalized_source_header_at_source, + )) +} + +/// Try to find mandatory header in the inclusive headers range and, if one is found, ask to relay +/// it. +/// +/// Returns `true` if header was found and (asked to be) relayed and `false` otherwise. +async fn relay_mandatory_header_from_range( + finality_source: &SubstrateFinalitySource

, + required_header_number: &RequiredHeaderNumberRef, + best_finalized_source_header_at_target: String, + range: (BlockNumberOf, BlockNumberOf), + relay_task_name: &str, +) -> Result { + // search for mandatory header first + let mandatory_source_header_number = + find_mandatory_header_in_range(finality_source, range).await?; + + // if there are no mandatory headers - we have nothing to do + let mandatory_source_header_number = match mandatory_source_header_number { + Some(mandatory_source_header_number) => mandatory_source_header_number, + None => return Ok(false), + }; + + // `find_mandatory_header` call may take a while => check if `required_header_number` is still + // less than our `mandatory_source_header_number` before logging anything + let mut required_header_number = required_header_number.lock().await; + if *required_header_number >= mandatory_source_header_number { + return Ok(false) + } + + log::trace!( + target: "bridge", + "[{}] Too many {} headers missing at target ({} vs {}). Going to sync up to the mandatory {}", + relay_task_name, + P::SourceChain::NAME, + best_finalized_source_header_at_target, + range.1, + mandatory_source_header_number, + ); + + *required_header_number = mandatory_source_header_number; + Ok(true) +} + +/// Read best finalized source block number from source client. +/// +/// Returns `None` if we have failed to read the number. +async fn best_finalized_source_header_at_source( + finality_source: &SubstrateFinalitySource

, + relay_task_name: &str, +) -> Result, relay_substrate_client::Error> { + finality_source.on_chain_best_finalized_block_number().await.map_err(|error| { + log::error!( + target: "bridge", + "[{}] Failed to read best finalized source header from source: {:?}", + relay_task_name, + error, + ); + + error + }) +} + +/// Read best finalized source block number from target client. +/// +/// Returns `None` if we have failed to read the number. +async fn best_finalized_source_header_at_target( + finality_target: &SubstrateFinalityTarget

, + relay_task_name: &str, +) -> Result, as RelayClient>::Error> +where + AccountIdOf: From< as sp_core::Pair>::Public>, +{ + finality_target + .best_finalized_source_block_id() + .await + .map_err(|error| { + log::error!( + target: "bridge", + "[{}] Failed to read best finalized source header from target: {:?}", + relay_task_name, + error, + ); + + error + }) + .map(|id| id.0) +} + +/// Read first mandatory header in given inclusive range. +/// +/// Returns `Ok(None)` if there were no mandatory headers in the range. +async fn find_mandatory_header_in_range( + finality_source: &SubstrateFinalitySource

, + range: (BlockNumberOf, BlockNumberOf), +) -> Result>, relay_substrate_client::Error> { + let mut current = range.0; + while current <= range.1 { + let header = finality_source.client().header_by_number(current).await?; + if >::ConsensusLogReader::schedules_authorities_change( + header.digest(), + ) { + return Ok(Some(current)) + } + + current += One::one(); + } + + Ok(None) +} + +/// On-demand headers relay task name. +fn on_demand_headers_relay_name() -> String { + format!("{}-to-{}-on-demand-headers", SourceChain::NAME, TargetChain::NAME) +} + +#[cfg(test)] +mod tests { + use super::*; + + type TestChain = relay_rococo_client::Rococo; + + const AT_SOURCE: Option = Some(10); + const AT_TARGET: Option = Some(1); + + #[async_std::test] + async fn mandatory_headers_scan_range_selects_range_if_some_headers_are_missing() { + assert_eq!( + mandatory_headers_scan_range::(AT_SOURCE, AT_TARGET, 0,).await, + Some((AT_TARGET.unwrap() + 1, AT_SOURCE.unwrap())), + ); + } + + #[async_std::test] + async fn mandatory_headers_scan_range_selects_nothing_if_already_queued() { + assert_eq!( + mandatory_headers_scan_range::(AT_SOURCE, AT_TARGET, AT_SOURCE.unwrap(),) + .await, + None, + ); + } +} diff --git a/relays/lib-substrate-relay/src/on_demand/mod.rs b/relays/lib-substrate-relay/src/on_demand/mod.rs new file mode 100644 index 00000000000..7a2dfc9c153 --- /dev/null +++ b/relays/lib-substrate-relay/src/on_demand/mod.rs @@ -0,0 +1,35 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types and functions intended to ease adding of new Substrate -> Substrate +//! on-demand pipelines. + +use async_trait::async_trait; + +pub mod headers; +pub mod parachains; + +/// On-demand headers relay that is relaying finalizing headers only when requested. +#[async_trait] +pub trait OnDemandRelay: Send + Sync { + /// Ask relay to relay source header with given number to the target chain. + /// + /// Depending on implementation, on-demand relay may also relay `required_header` ancestors + /// (e.g. if they're mandatory), or its descendants. The request is considered complete if + /// the best avbailable header at the target chain has number that is larger than or equal + /// to the `required_header`. + async fn require_more_headers(&self, required_header: SourceHeaderNumber); +} diff --git a/relays/lib-substrate-relay/src/on_demand/parachains.rs b/relays/lib-substrate-relay/src/on_demand/parachains.rs new file mode 100644 index 00000000000..35ef8244ae6 --- /dev/null +++ b/relays/lib-substrate-relay/src/on_demand/parachains.rs @@ -0,0 +1,704 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! On-demand Substrate -> Substrate parachain finality relay. + +use crate::{ + messages_source::best_finalized_peer_header_at_self, + on_demand::OnDemandRelay, + parachains::{ + source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter, + SubstrateParachainsPipeline, + }, + TransactionParams, +}; + +use async_std::{ + channel::{unbounded, Receiver, Sender}, + sync::{Arc, Mutex}, +}; +use async_trait::async_trait; +use bp_polkadot_core::parachains::ParaHash; +use bp_runtime::HeaderIdProvider; +use futures::{select, FutureExt}; +use num_traits::Zero; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use parachains_relay::parachains_loop::{AvailableHeader, ParachainSyncParams, TargetClient}; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, +}; +use relay_utils::{ + metrics::MetricsParams, relay_loop::Client as RelayClient, FailedClient, HeaderId, +}; +use std::fmt::Debug; + +/// On-demand Substrate <-> Substrate parachain finality relay. +/// +/// This relay may be requested to sync more parachain headers, whenever some other relay +/// (e.g. messages relay) needs it to continue its regular work. When enough parachain headers +/// are relayed, on-demand stops syncing headers. +#[derive(Clone)] +pub struct OnDemandParachainsRelay { + /// Relay task name. + relay_task_name: String, + /// Channel used to communicate with background task and ask for relay of parachain heads. + required_header_number_sender: Sender>, +} + +impl OnDemandParachainsRelay { + /// Create new on-demand parachains relay. + /// + /// Note that the argument is the source relay chain client, not the parachain client. + /// That's because parachain finality is determined by the relay chain and we don't + /// need to connect to the parachain itself here. + pub fn new>( + source_relay_client: Client, + target_client: Client, + target_transaction_params: TransactionParams>, + on_demand_source_relay_to_target_headers: Arc< + dyn OnDemandRelay>, + >, + ) -> Self + where + P::SourceParachain: Chain, + P::SourceRelayChain: + Chain, + AccountIdOf: + From< as sp_core::Pair>::Public>, + { + let (required_header_number_sender, required_header_number_receiver) = unbounded(); + let this = OnDemandParachainsRelay { + relay_task_name: on_demand_parachains_relay_name::(), + required_header_number_sender, + }; + async_std::task::spawn(async move { + background_task::

( + source_relay_client, + target_client, + target_transaction_params, + on_demand_source_relay_to_target_headers, + required_header_number_receiver, + ) + .await; + }); + + this + } +} + +#[async_trait] +impl OnDemandRelay> + for OnDemandParachainsRelay +where + SourceParachain: Chain, +{ + async fn require_more_headers(&self, required_header: BlockNumberOf) { + if let Err(e) = self.required_header_number_sender.send(required_header).await { + log::trace!( + target: "bridge", + "[{}] Failed to request {} header {:?}: {:?}", + self.relay_task_name, + SourceParachain::NAME, + required_header, + e, + ); + } + } +} + +/// Background task that is responsible for starting parachain headers relay. +async fn background_task( + source_relay_client: Client, + target_client: Client, + target_transaction_params: TransactionParams>, + on_demand_source_relay_to_target_headers: Arc< + dyn OnDemandRelay>, + >, + required_parachain_header_number_receiver: Receiver>, +) where + P::SourceParachain: Chain, + P::SourceRelayChain: + Chain, + AccountIdOf: From< as sp_core::Pair>::Public>, +{ + let relay_task_name = on_demand_parachains_relay_name::(); + let target_transactions_mortality = target_transaction_params.mortality; + + let mut relay_state = RelayState::Idle; + let mut required_parachain_header_number = Zero::zero(); + let required_para_header_number_ref = Arc::new(Mutex::new(AvailableHeader::Unavailable)); + + let mut restart_relay = true; + let parachains_relay_task = futures::future::Fuse::terminated(); + futures::pin_mut!(parachains_relay_task); + + let mut parachains_source = ParachainsSource::

::new( + source_relay_client.clone(), + required_para_header_number_ref.clone(), + ); + let mut parachains_target = + ParachainsTarget::

::new(target_client.clone(), target_transaction_params.clone()); + + loop { + select! { + new_required_parachain_header_number = required_parachain_header_number_receiver.recv().fuse() => { + let new_required_parachain_header_number = match new_required_parachain_header_number { + Ok(new_required_parachain_header_number) => new_required_parachain_header_number, + Err(e) => { + log::error!( + target: "bridge", + "[{}] Background task has exited with error: {:?}", + relay_task_name, + e, + ); + + return; + }, + }; + + // keep in mind that we are not updating `required_para_header_number_ref` here, because + // then we'll be submitting all previous headers as well (while required relay headers are + // delivered) and we want to avoid that (to reduce cost) + required_parachain_header_number = std::cmp::max( + required_parachain_header_number, + new_required_parachain_header_number, + ); + }, + _ = async_std::task::sleep(P::TargetChain::AVERAGE_BLOCK_INTERVAL).fuse() => {}, + _ = parachains_relay_task => { + // this should never happen in practice given the current code + restart_relay = true; + }, + } + + // the workflow of the on-demand parachains relay is: + // + // 1) message relay (or any other dependent relay) sees new message at parachain header + // `PH`; + // + // 2) it sees that the target chain does not know `PH`; + // + // 3) it asks on-demand parachains relay to relay `PH` to the target chain; + // + // Phase#1: relaying relay chain header + // + // 4) on-demand parachains relay waits for GRANDPA-finalized block of the source relay chain + // `RH` that is storing `PH` or its descendant. Let it be `PH'`; + // 5) it asks on-demand headers relay to relay `RH` to the target chain; + // 6) it waits until `RH` (or its descendant) is relayed to the target chain; + // + // Phase#2: relaying parachain header + // + // 7) on-demand parachains relay sets `ParachainsSource::maximal_header_number` to the + // `PH'.number()`. + // 8) parachains finality relay sees that the parachain head has been + // updated and relays `PH'` to the target chain. + + // select headers to relay + let relay_data = read_relay_data( + ¶chains_source, + ¶chains_target, + required_parachain_header_number, + ) + .await; + match relay_data { + Ok(relay_data) => { + let prev_relay_state = relay_state; + relay_state = select_headers_to_relay(&relay_data, relay_state); + log::trace!( + target: "bridge", + "[{}] Selected new relay state: {:?} using old state {:?} and data {:?}", + relay_task_name, + relay_state, + prev_relay_state, + relay_data, + ); + }, + Err(failed_client) => { + relay_utils::relay_loop::reconnect_failed_client( + failed_client, + relay_utils::relay_loop::RECONNECT_DELAY, + &mut parachains_source, + &mut parachains_target, + ) + .await; + continue + }, + } + + // we have selected our new 'state' => let's notify our source clients about our new + // requirements + match relay_state { + RelayState::Idle => (), + RelayState::RelayingRelayHeader(required_relay_header) => { + on_demand_source_relay_to_target_headers + .require_more_headers(required_relay_header) + .await; + }, + RelayState::RelayingParaHeader(required_para_header) => { + *required_para_header_number_ref.lock().await = + AvailableHeader::Available(required_para_header); + }, + } + + // start/restart relay + if restart_relay { + let stall_timeout = relay_substrate_client::transaction_stall_timeout( + target_transactions_mortality, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + relay_utils::STALL_TIMEOUT, + ); + + log::info!( + target: "bridge", + "[{}] Starting on-demand-parachains relay task\n\t\ + Tx mortality: {:?} (~{}m)\n\t\ + Stall timeout: {:?}", + relay_task_name, + target_transactions_mortality, + stall_timeout.as_secs_f64() / 60.0f64, + stall_timeout, + ); + + parachains_relay_task.set( + parachains_relay::parachains_loop::run( + parachains_source.clone(), + parachains_target.clone(), + ParachainSyncParams { + parachains: vec![P::SOURCE_PARACHAIN_PARA_ID.into()], + stall_timeout: std::time::Duration::from_secs(60), + strategy: parachains_relay::parachains_loop::ParachainSyncStrategy::Any, + }, + MetricsParams::disabled(), + futures::future::pending(), + ) + .fuse(), + ); + + restart_relay = false; + } + } +} + +/// On-demand parachains relay task name. +fn on_demand_parachains_relay_name() -> String { + format!("{}-to-{}-on-demand-parachain", SourceChain::NAME, TargetChain::NAME) +} + +/// On-demand relay state. +#[derive(Clone, Copy, Debug, PartialEq)] +enum RelayState { + /// On-demand relay is not doing anything. + Idle, + /// Relaying given relay header to relay given parachain header later. + RelayingRelayHeader(RelayNumber), + /// Relaying given parachain header. + RelayingParaHeader(HeaderId), +} + +/// Data gathered from source and target clients, used by on-demand relay. +#[derive(Debug)] +struct RelayData { + /// Parachain header number that is required at the target chain. + pub required_para_header: ParaNumber, + /// Parachain header number, known to the target chain. + pub para_header_at_target: Option, + /// Parachain header id, known to the source (relay) chain. + pub para_header_at_source: Option>, + /// Parachain header, that is available at the source relay chain at `relay_header_at_target` + /// block. + pub para_header_at_relay_header_at_target: Option>, + /// Relay header number at the source chain. + pub relay_header_at_source: RelayNumber, + /// Relay header number at the target chain. + pub relay_header_at_target: RelayNumber, +} + +/// Read required data from source and target clients. +async fn read_relay_data( + source: &ParachainsSource

, + target: &ParachainsTarget

, + required_header_number: BlockNumberOf, +) -> Result< + RelayData< + HashOf, + BlockNumberOf, + BlockNumberOf, + >, + FailedClient, +> +where + ParachainsTarget

: + TargetClient> + RelayClient, +{ + let map_target_err = |e| { + log::error!( + target: "bridge", + "[{}] Failed to read relay data from {} client: {:?}", + on_demand_parachains_relay_name::(), + P::TargetChain::NAME, + e, + ); + FailedClient::Target + }; + let map_source_err = |e| { + log::error!( + target: "bridge", + "[{}] Failed to read relay data from {} client: {:?}", + on_demand_parachains_relay_name::(), + P::SourceRelayChain::NAME, + e, + ); + FailedClient::Source + }; + + let best_target_block_hash = target.best_block().await.map_err(map_target_err)?.1; + let para_header_at_target = + best_finalized_peer_header_at_self::( + target.client(), + best_target_block_hash, + P::SourceParachain::BEST_FINALIZED_HEADER_ID_METHOD, + ) + .await; + // if there are no parachain heads at the target (`BridgePalletIsNotInitialized`), we'll need + // to submit at least one. Otherwise the pallet will be treated as uninitialized and messages + // sync will stall. + let para_header_at_target = match para_header_at_target { + Ok(para_header_at_target) => Some(para_header_at_target.0), + Err(SubstrateError::BridgePalletIsNotInitialized) => None, + Err(e) => return Err(map_target_err(e)), + }; + + let best_finalized_relay_header = + source.client().best_finalized_header().await.map_err(map_source_err)?; + let best_finalized_relay_block_id = best_finalized_relay_header.id(); + let para_header_at_source = source + .on_chain_para_head_id(best_finalized_relay_block_id, P::SOURCE_PARACHAIN_PARA_ID.into()) + .await + .map_err(map_source_err)?; + + let relay_header_at_source = best_finalized_relay_block_id.0; + let relay_header_at_target = + best_finalized_peer_header_at_self::( + target.client(), + best_target_block_hash, + P::SourceRelayChain::BEST_FINALIZED_HEADER_ID_METHOD, + ) + .await + .map_err(map_target_err)?; + + let para_header_at_relay_header_at_target = source + .on_chain_para_head_id(relay_header_at_target, P::SOURCE_PARACHAIN_PARA_ID.into()) + .await + .map_err(map_source_err)?; + + Ok(RelayData { + required_para_header: required_header_number, + para_header_at_target, + para_header_at_source, + relay_header_at_source, + relay_header_at_target: relay_header_at_target.0, + para_header_at_relay_header_at_target, + }) +} + +/// Select relay and parachain headers that need to be relayed. +fn select_headers_to_relay( + data: &RelayData, + mut state: RelayState, +) -> RelayState +where + ParaHash: Clone, + ParaNumber: Copy + PartialOrd + Zero, + RelayNumber: Copy + Debug + Ord, +{ + // Process the `RelayingRelayHeader` state. + if let &RelayState::RelayingRelayHeader(relay_header_number) = &state { + if data.relay_header_at_target < relay_header_number { + // The required relay header hasn't yet been relayed. Ask / wait for it. + return state + } + + // We may switch to `RelayingParaHeader` if parachain head is available. + state = data + .para_header_at_relay_header_at_target + .clone() + .map_or(RelayState::Idle, RelayState::RelayingParaHeader); + } + + // Process the `RelayingParaHeader` state. + if let RelayState::RelayingParaHeader(para_header_id) = &state { + let para_header_at_target_or_zero = data.para_header_at_target.unwrap_or_else(Zero::zero); + if para_header_at_target_or_zero < para_header_id.0 { + // The required parachain header hasn't yet been relayed. Ask / wait for it. + return state + } + } + + // if we haven't read para head from the source, we can't yet do anything + let para_header_at_source = match data.para_header_at_source { + Some(ref para_header_at_source) => para_header_at_source.clone(), + None => return RelayState::Idle, + }; + + // if we have parachain head at the source, but no parachain heads at the target, we'll need + // to deliver at least one parachain head + let (required_para_header, para_header_at_target) = match data.para_header_at_target { + Some(para_header_at_target) => (data.required_para_header, para_header_at_target), + None => (para_header_at_source.0, Zero::zero()), + }; + + // if we have already satisfied our "customer", do nothing + if required_para_header <= para_header_at_target { + return RelayState::Idle + } + + // if required header is not available even at the source chain, let's wait + if required_para_header > para_header_at_source.0 { + return RelayState::Idle + } + + // we will always try to sync latest parachain/relay header, even if we've been asked for some + // its ancestor + + // we need relay chain header first + if data.relay_header_at_target < data.relay_header_at_source { + return RelayState::RelayingRelayHeader(data.relay_header_at_source) + } + + // if all relay headers synced, we may start directly with parachain header + RelayState::RelayingParaHeader(para_header_at_source) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn relay_waits_for_relay_header_to_be_delivered() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 90, + para_header_at_target: Some(50), + para_header_at_source: Some(HeaderId(110, 110)), + relay_header_at_source: 800, + relay_header_at_target: 700, + para_header_at_relay_header_at_target: Some(HeaderId(100, 100)), + }, + RelayState::RelayingRelayHeader(750), + ), + RelayState::RelayingRelayHeader(750), + ); + } + + #[test] + fn relay_starts_relaying_requested_para_header_after_relay_header_is_delivered() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 90, + para_header_at_target: Some(50), + para_header_at_source: Some(HeaderId(110, 110)), + relay_header_at_source: 800, + relay_header_at_target: 750, + para_header_at_relay_header_at_target: Some(HeaderId(100, 100)), + }, + RelayState::RelayingRelayHeader(750), + ), + RelayState::RelayingParaHeader(HeaderId(100, 100)), + ); + } + + #[test] + fn relay_selects_better_para_header_after_better_relay_header_is_delivered() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 90, + para_header_at_target: Some(50), + para_header_at_source: Some(HeaderId(110, 110)), + relay_header_at_source: 800, + relay_header_at_target: 780, + para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), + }, + RelayState::RelayingRelayHeader(750), + ), + RelayState::RelayingParaHeader(HeaderId(105, 105)), + ); + } + #[test] + fn relay_waits_for_para_header_to_be_delivered() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 90, + para_header_at_target: Some(50), + para_header_at_source: Some(HeaderId(110, 110)), + relay_header_at_source: 800, + relay_header_at_target: 780, + para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), + }, + RelayState::RelayingParaHeader(HeaderId(105, 105)), + ), + RelayState::RelayingParaHeader(HeaderId(105, 105)), + ); + } + + #[test] + fn relay_stays_idle_if_required_para_header_is_already_delivered() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 90, + para_header_at_target: Some(105), + para_header_at_source: Some(HeaderId(110, 110)), + relay_header_at_source: 800, + relay_header_at_target: 780, + para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), + }, + RelayState::Idle, + ), + RelayState::Idle, + ); + } + + #[test] + fn relay_waits_for_required_para_header_to_appear_at_source_1() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 120, + para_header_at_target: Some(105), + para_header_at_source: None, + relay_header_at_source: 800, + relay_header_at_target: 780, + para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), + }, + RelayState::Idle, + ), + RelayState::Idle, + ); + } + + #[test] + fn relay_waits_for_required_para_header_to_appear_at_source_2() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 120, + para_header_at_target: Some(105), + para_header_at_source: Some(HeaderId(110, 110)), + relay_header_at_source: 800, + relay_header_at_target: 780, + para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), + }, + RelayState::Idle, + ), + RelayState::Idle, + ); + } + + #[test] + fn relay_starts_relaying_relay_header_when_new_para_header_is_requested() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 120, + para_header_at_target: Some(105), + para_header_at_source: Some(HeaderId(125, 125)), + relay_header_at_source: 800, + relay_header_at_target: 780, + para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), + }, + RelayState::Idle, + ), + RelayState::RelayingRelayHeader(800), + ); + } + + #[test] + fn relay_starts_relaying_para_header_when_new_para_header_is_requested() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 120, + para_header_at_target: Some(105), + para_header_at_source: Some(HeaderId(125, 125)), + relay_header_at_source: 800, + relay_header_at_target: 800, + para_header_at_relay_header_at_target: Some(HeaderId(125, 125)), + }, + RelayState::Idle, + ), + RelayState::RelayingParaHeader(HeaderId(125, 125)), + ); + } + + #[test] + fn relay_goes_idle_when_parachain_is_deregistered() { + assert_eq!( + select_headers_to_relay::( + &RelayData { + required_para_header: 120, + para_header_at_target: Some(105), + para_header_at_source: None, + relay_header_at_source: 800, + relay_header_at_target: 800, + para_header_at_relay_header_at_target: None, + }, + RelayState::RelayingRelayHeader(800), + ), + RelayState::Idle, + ); + } + + #[test] + fn relay_starts_relaying_first_parachain_header() { + assert_eq!( + select_headers_to_relay::( + &RelayData { + required_para_header: 0, + para_header_at_target: None, + para_header_at_source: Some(HeaderId(125, 125)), + relay_header_at_source: 800, + relay_header_at_target: 800, + para_header_at_relay_header_at_target: Some(HeaderId(125, 125)), + }, + RelayState::Idle, + ), + RelayState::RelayingParaHeader(HeaderId(125, 125)), + ); + } + + #[test] + fn relay_starts_relaying_relay_header_to_relay_first_parachain_header() { + assert_eq!( + select_headers_to_relay::( + &RelayData { + required_para_header: 0, + para_header_at_target: None, + para_header_at_source: Some(HeaderId(125, 125)), + relay_header_at_source: 800, + relay_header_at_target: 700, + para_header_at_relay_header_at_target: Some(HeaderId(125, 125)), + }, + RelayState::Idle, + ), + RelayState::RelayingRelayHeader(800), + ); + } +} diff --git a/relays/lib-substrate-relay/src/parachains/mod.rs b/relays/lib-substrate-relay/src/parachains/mod.rs new file mode 100644 index 00000000000..1d744a30e4e --- /dev/null +++ b/relays/lib-substrate-relay/src/parachains/mod.rs @@ -0,0 +1,108 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types and functions intended to ease adding of new Substrate -> Substrate +//! parachain finality proofs synchronization pipelines. + +use async_trait::async_trait; +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use pallet_bridge_parachains::{ + Call as BridgeParachainsCall, Config as BridgeParachainsConfig, RelayBlockHash, + RelayBlockHasher, RelayBlockNumber, +}; +use parachains_relay::ParachainsPipeline; +use relay_substrate_client::{CallOf, Chain, ChainWithTransactions, HeaderIdOf, RelayChain}; +use std::{fmt::Debug, marker::PhantomData}; + +pub mod source; +pub mod target; + +/// Substrate -> Substrate parachain finality proofs synchronization pipeline. +/// +/// This is currently restricted to the single parachain, because it is how it +/// will be used (at least) initially. +#[async_trait] +pub trait SubstrateParachainsPipeline: 'static + Clone + Debug + Send + Sync { + /// Headers of this parachain are submitted to the `Self::TargetChain`. + type SourceParachain: Chain; + /// Relay chain that is storing headers of `Self::SourceParachain`. + type SourceRelayChain: RelayChain; + /// Target chain where `Self::SourceParachain` headers are submitted. + type TargetChain: ChainWithTransactions; + + /// How submit parachains heads call is built? + type SubmitParachainHeadsCallBuilder: SubmitParachainHeadsCallBuilder; + + /// Id of the `Self::SourceParachain`, used for registration in `Self::SourceRelayChain`. + const SOURCE_PARACHAIN_PARA_ID: u32; +} + +/// Adapter that allows all `SubstrateParachainsPipeline` to act as `ParachainsPipeline`. +#[derive(Clone, Debug)] +pub struct ParachainsPipelineAdapter { + _phantom: PhantomData

, +} + +impl ParachainsPipeline for ParachainsPipelineAdapter

{ + type SourceChain = P::SourceRelayChain; + type TargetChain = P::TargetChain; +} + +/// Different ways of building `submit_parachain_heads` calls. +pub trait SubmitParachainHeadsCallBuilder: + 'static + Send + Sync +{ + /// Given parachains and their heads proof, build call of `submit_parachain_heads` + /// function of bridge parachains module at the target chain. + fn build_submit_parachain_heads_call( + at_relay_block: HeaderIdOf, + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> CallOf; +} + +/// Building `submit_parachain_heads` call when you have direct access to the target +/// chain runtime. +pub struct DirectSubmitParachainHeadsCallBuilder { + _phantom: PhantomData<(P, R, I)>, +} + +impl SubmitParachainHeadsCallBuilder

for DirectSubmitParachainHeadsCallBuilder +where + P: SubstrateParachainsPipeline, + P::SourceRelayChain: Chain, + R: BridgeParachainsConfig + Send + Sync, + I: 'static + Send + Sync, + R::BridgedChain: bp_runtime::Chain< + BlockNumber = RelayBlockNumber, + Hash = RelayBlockHash, + Hasher = RelayBlockHasher, + >, + CallOf: From>, +{ + fn build_submit_parachain_heads_call( + at_relay_block: HeaderIdOf, + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> CallOf { + BridgeParachainsCall::::submit_parachain_heads { + at_relay_block: (at_relay_block.0, at_relay_block.1), + parachains, + parachain_heads_proof, + } + .into() + } +} diff --git a/relays/lib-substrate-relay/src/parachains/source.rs b/relays/lib-substrate-relay/src/parachains/source.rs new file mode 100644 index 00000000000..2cae7f1a224 --- /dev/null +++ b/relays/lib-substrate-relay/src/parachains/source.rs @@ -0,0 +1,188 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Parachain heads source. + +use crate::parachains::{ParachainsPipelineAdapter, SubstrateParachainsPipeline}; + +use async_std::sync::{Arc, Mutex}; +use async_trait::async_trait; +use bp_parachains::parachain_head_storage_key_at_source; +use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; +use bp_runtime::HeaderIdProvider; +use codec::Decode; +use parachains_relay::{ + parachains_loop::{AvailableHeader, SourceClient}, + parachains_loop_metrics::ParachainsLoopMetrics, +}; +use relay_substrate_client::{ + Chain, Client, Error as SubstrateError, HeaderIdOf, HeaderOf, RelayChain, +}; +use relay_utils::relay_loop::Client as RelayClient; + +/// Shared updatable reference to the maximal parachain header id that we want to sync from the +/// source. +pub type RequiredHeaderIdRef = Arc>>>; + +/// Substrate client as parachain heads source. +#[derive(Clone)] +pub struct ParachainsSource { + client: Client, + max_head_id: RequiredHeaderIdRef, +} + +impl ParachainsSource

{ + /// Creates new parachains source client. + pub fn new( + client: Client, + max_head_id: RequiredHeaderIdRef, + ) -> Self { + ParachainsSource { client, max_head_id } + } + + /// Returns reference to the underlying RPC client. + pub fn client(&self) -> &Client { + &self.client + } + + /// Return decoded head of given parachain. + pub async fn on_chain_para_head_id( + &self, + at_block: HeaderIdOf, + para_id: ParaId, + ) -> Result>, SubstrateError> { + let storage_key = + parachain_head_storage_key_at_source(P::SourceRelayChain::PARAS_PALLET_NAME, para_id); + let para_head = self.client.raw_storage_value(storage_key, Some(at_block.1)).await?; + let para_head = para_head.map(|h| ParaHead::decode(&mut &h.0[..])).transpose()?; + let para_head = match para_head { + Some(para_head) => para_head, + None => return Ok(None), + }; + let para_head: HeaderOf = Decode::decode(&mut ¶_head.0[..])?; + Ok(Some(para_head.id())) + } +} + +#[async_trait] +impl RelayClient for ParachainsSource

{ + type Error = SubstrateError; + + async fn reconnect(&mut self) -> Result<(), SubstrateError> { + self.client.reconnect().await + } +} + +#[async_trait] +impl SourceClient> + for ParachainsSource

+where + P::SourceParachain: Chain, +{ + async fn ensure_synced(&self) -> Result { + match self.client.ensure_synced().await { + Ok(_) => Ok(true), + Err(SubstrateError::ClientNotSynced(_)) => Ok(false), + Err(e) => Err(e), + } + } + + async fn parachain_head( + &self, + at_block: HeaderIdOf, + metrics: Option<&ParachainsLoopMetrics>, + para_id: ParaId, + ) -> Result, Self::Error> { + // we don't need to support many parachains now + if para_id.0 != P::SOURCE_PARACHAIN_PARA_ID { + return Err(SubstrateError::Custom(format!( + "Parachain id {} is not matching expected {}", + para_id.0, + P::SOURCE_PARACHAIN_PARA_ID, + ))) + } + + let mut para_head_id = AvailableHeader::Missing; + if let Some(on_chain_para_head_id) = self.on_chain_para_head_id(at_block, para_id).await? { + // Never return head that is larger than requested. This way we'll never sync + // headers past `max_header_id`. + para_head_id = match *self.max_head_id.lock().await { + AvailableHeader::Unavailable => AvailableHeader::Unavailable, + AvailableHeader::Missing => { + // `max_header_id` is not set. There is no limit. + AvailableHeader::Available(on_chain_para_head_id) + }, + AvailableHeader::Available(max_head_id) => { + // We report at most `max_header_id`. + AvailableHeader::Available(std::cmp::min(on_chain_para_head_id, max_head_id)) + }, + } + } + + if let (Some(metrics), AvailableHeader::Available(para_head_id)) = (metrics, para_head_id) { + metrics.update_best_parachain_block_at_source(para_id, para_head_id.0); + } + + Ok(para_head_id.map(|para_head_id| para_head_id.1)) + } + + async fn prove_parachain_heads( + &self, + at_block: HeaderIdOf, + parachains: &[ParaId], + ) -> Result<(ParaHeadsProof, Vec), Self::Error> { + let parachain = ParaId(P::SOURCE_PARACHAIN_PARA_ID); + if parachains != [parachain] { + return Err(SubstrateError::Custom(format!( + "Trying to prove unexpected parachains {:?}. Expected {:?}", + parachains, parachain, + ))) + } + + let parachain = parachains[0]; + let storage_key = + parachain_head_storage_key_at_source(P::SourceRelayChain::PARAS_PALLET_NAME, parachain); + let parachain_heads_proof = self + .client + .prove_storage(vec![storage_key.clone()], at_block.1) + .await? + .into_iter_nodes() + .collect(); + + // why we're reading parachain head here once again (it has already been read at the + // `parachain_head`)? that's because `parachain_head` sometimes returns obsolete parachain + // head and loop sometimes asks to prove this obsolete head and gets other (actual) head + // instead + // + // => since we want to provide proper hashes in our `submit_parachain_heads` call, we're + // rereading actual value here + let parachain_head = self + .client + .raw_storage_value(storage_key, Some(at_block.1)) + .await? + .map(|h| ParaHead::decode(&mut &h.0[..])) + .transpose()? + .ok_or_else(|| { + SubstrateError::Custom(format!( + "Failed to read expected parachain {:?} head at {:?}", + parachain, at_block + )) + })?; + let parachain_head_hash = parachain_head.hash(); + + Ok((ParaHeadsProof(parachain_heads_proof), vec![parachain_head_hash])) + } +} diff --git a/relays/lib-substrate-relay/src/parachains/target.rs b/relays/lib-substrate-relay/src/parachains/target.rs new file mode 100644 index 00000000000..68fd72765aa --- /dev/null +++ b/relays/lib-substrate-relay/src/parachains/target.rs @@ -0,0 +1,201 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Parachain heads target. + +use crate::{ + parachains::{ + ParachainsPipelineAdapter, SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, + }, + TransactionParams, +}; + +use async_trait::async_trait; +use bp_parachains::{BestParaHeadHash, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider}; +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use bp_runtime::HeaderIdProvider; +use codec::Decode; +use parachains_relay::{ + parachains_loop::TargetClient, parachains_loop_metrics::ParachainsLoopMetrics, +}; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, + HeaderIdOf, HeaderOf, RelayChain, SignParam, TransactionEra, TransactionTracker, + UnsignedTransaction, +}; +use relay_utils::{relay_loop::Client as RelayClient, HeaderId}; +use sp_core::{Bytes, Pair}; +use sp_runtime::traits::Header as HeaderT; + +/// Substrate client as parachain heads source. +pub struct ParachainsTarget { + client: Client, + transaction_params: TransactionParams>, +} + +impl ParachainsTarget

{ + /// Creates new parachains target client. + pub fn new( + client: Client, + transaction_params: TransactionParams>, + ) -> Self { + ParachainsTarget { client, transaction_params } + } + + /// Returns reference to the underlying RPC client. + pub fn client(&self) -> &Client { + &self.client + } +} + +impl Clone for ParachainsTarget

{ + fn clone(&self) -> Self { + ParachainsTarget { + client: self.client.clone(), + transaction_params: self.transaction_params.clone(), + } + } +} + +#[async_trait] +impl RelayClient for ParachainsTarget

{ + type Error = SubstrateError; + + async fn reconnect(&mut self) -> Result<(), SubstrateError> { + self.client.reconnect().await + } +} + +#[async_trait] +impl

TargetClient> for ParachainsTarget

+where + P: SubstrateParachainsPipeline, + AccountIdOf: From< as Pair>::Public>, +{ + type TransactionTracker = TransactionTracker>; + + async fn best_block(&self) -> Result, Self::Error> { + let best_header = self.client.best_header().await?; + let best_id = best_header.id(); + + Ok(best_id) + } + + async fn best_finalized_source_block( + &self, + at_block: &HeaderIdOf, + ) -> Result, Self::Error> { + let encoded_best_finalized_source_block = self + .client + .state_call( + P::SourceRelayChain::BEST_FINALIZED_HEADER_ID_METHOD.into(), + Bytes(Vec::new()), + Some(at_block.1), + ) + .await?; + + Option::, BlockNumberOf>>::decode( + &mut &encoded_best_finalized_source_block.0[..], + ) + .map_err(SubstrateError::ResponseParseFailed)? + .map(Ok) + .unwrap_or(Err(SubstrateError::BridgePalletIsNotInitialized)) + } + + async fn parachain_head( + &self, + at_block: HeaderIdOf, + metrics: Option<&ParachainsLoopMetrics>, + para_id: ParaId, + ) -> Result, Self::Error> { + let best_para_head_hash: Option = self + .client + .storage_map_value::( + P::SourceRelayChain::PARACHAINS_FINALITY_PALLET_NAME, + ¶_id, + Some(at_block.1), + ) + .await? + .map(|para_info| para_info.best_head_hash); + + if let (Some(metrics), Some(best_para_head_hash)) = (metrics, &best_para_head_hash) { + let imported_para_head = self + .client + .storage_double_map_value::( + P::SourceRelayChain::PARACHAINS_FINALITY_PALLET_NAME, + ¶_id, + &best_para_head_hash.head_hash, + Some(at_block.1), + ) + .await + .and_then(|maybe_encoded_head| match maybe_encoded_head { + Some(encoded_head) => + HeaderOf::::decode(&mut &encoded_head.0[..]) + .map(Some) + .map_err(Self::Error::ResponseParseFailed), + None => Ok(None), + }) + .map_err(|e| { + log::error!( + target: "bridge-metrics", + "Failed to read or decode {} parachain header at {}: {:?}. Metric will have obsolete value", + P::SourceParachain::NAME, + P::TargetChain::NAME, + e, + ); + e + }) + .unwrap_or(None); + if let Some(imported_para_head) = imported_para_head { + metrics + .update_best_parachain_block_at_target(para_id, *imported_para_head.number()); + } + } + + Ok(best_para_head_hash) + } + + async fn submit_parachain_heads_proof( + &self, + at_relay_block: HeaderIdOf, + updated_parachains: Vec<(ParaId, ParaHash)>, + proof: ParaHeadsProof, + ) -> Result { + let genesis_hash = *self.client.genesis_hash(); + let transaction_params = self.transaction_params.clone(); + let (spec_version, transaction_version) = self.client.simple_runtime_version().await?; + let call = P::SubmitParachainHeadsCallBuilder::build_submit_parachain_heads_call( + at_relay_block, + updated_parachains, + proof, + ); + self.client + .submit_and_watch_signed_extrinsic( + self.transaction_params.signer.public().into(), + SignParam:: { + spec_version, + transaction_version, + genesis_hash, + signer: transaction_params.signer, + }, + move |best_block_id, transaction_nonce| { + Ok(UnsignedTransaction::new(call.into(), transaction_nonce) + .era(TransactionEra::new(best_block_id, transaction_params.mortality))) + }, + ) + .await + } +} diff --git a/relays/messages/Cargo.toml b/relays/messages/Cargo.toml new file mode 100644 index 00000000000..02e453b1c32 --- /dev/null +++ b/relays/messages/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "messages-relay" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +async-std = { version = "1.6.5", features = ["attributes"] } +async-trait = "0.1" +futures = "0.3.5" +hex = "0.4" +log = "0.4.17" +num-traits = "0.2" +parking_lot = "0.11.0" + +# Bridge Dependencies + +bp-messages = { path = "../../primitives/messages" } +bp-runtime = { path = "../../primitives/runtime" } +finality-relay = { path = "../finality" } +relay-utils = { path = "../utils" } + +sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/messages/src/lib.rs b/relays/messages/src/lib.rs new file mode 100644 index 00000000000..9c62cee5ee3 --- /dev/null +++ b/relays/messages/src/lib.rs @@ -0,0 +1,37 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Relaying [`pallet-bridge-messages`](../pallet_bridge_messages/index.html) application specific +//! data. Message lane allows sending arbitrary messages between bridged chains. This +//! module provides entrypoint that starts reading messages from given message lane +//! of source chain and submits proof-of-message-at-source-chain transactions to the +//! target chain. Additionally, proofs-of-messages-delivery are sent back from the +//! target chain to the source chain. + +// required for futures::select! +#![recursion_limit = "1024"] +#![warn(missing_docs)] + +mod metrics; + +pub mod message_lane; +pub mod message_lane_loop; + +mod message_race_delivery; +mod message_race_limits; +mod message_race_loop; +mod message_race_receiving; +mod message_race_strategy; diff --git a/relays/messages/src/message_lane.rs b/relays/messages/src/message_lane.rs new file mode 100644 index 00000000000..5c9728ad93a --- /dev/null +++ b/relays/messages/src/message_lane.rs @@ -0,0 +1,71 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! One-way message lane types. Within single one-way lane we have three 'races' where we try to: +//! +//! 1) relay new messages from source to target node; +//! 2) relay proof-of-delivery from target to source node. + +use num_traits::{SaturatingAdd, Zero}; +use relay_utils::{BlockNumberBase, HeaderId}; +use sp_arithmetic::traits::AtLeast32BitUnsigned; +use std::{fmt::Debug, ops::Sub}; + +/// One-way message lane. +pub trait MessageLane: 'static + Clone + Send + Sync { + /// Name of the messages source. + const SOURCE_NAME: &'static str; + /// Name of the messages target. + const TARGET_NAME: &'static str; + + /// Messages proof. + type MessagesProof: Clone + Debug + Send + Sync; + /// Messages receiving proof. + type MessagesReceivingProof: Clone + Debug + Send + Sync; + + /// The type of the source chain token balance, that is used to: + /// + /// 1) pay transaction fees; + /// 2) pay message delivery and dispatch fee; + /// 3) pay relayer rewards. + type SourceChainBalance: AtLeast32BitUnsigned + + Clone + + Copy + + Debug + + PartialOrd + + Sub + + SaturatingAdd + + Zero + + Send + + Sync; + /// Number of the source header. + type SourceHeaderNumber: BlockNumberBase; + /// Hash of the source header. + type SourceHeaderHash: Clone + Debug + Default + PartialEq + Send + Sync; + + /// Number of the target header. + type TargetHeaderNumber: BlockNumberBase; + /// Hash of the target header. + type TargetHeaderHash: Clone + Debug + Default + PartialEq + Send + Sync; +} + +/// Source header id within given one-way message lane. +pub type SourceHeaderIdOf

= + HeaderId<

::SourceHeaderHash,

::SourceHeaderNumber>; + +/// Target header id within given one-way message lane. +pub type TargetHeaderIdOf

= + HeaderId<

::TargetHeaderHash,

::TargetHeaderNumber>; diff --git a/relays/messages/src/message_lane_loop.rs b/relays/messages/src/message_lane_loop.rs new file mode 100644 index 00000000000..6b28dcbaa60 --- /dev/null +++ b/relays/messages/src/message_lane_loop.rs @@ -0,0 +1,1136 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Message delivery loop. Designed to work with messages pallet. +//! +//! Single relay instance delivers messages of single lane in single direction. +//! To serve two-way lane, you would need two instances of relay. +//! To serve N two-way lanes, you would need N*2 instances of relay. +//! +//! Please keep in mind that the best header in this file is actually best +//! finalized header. I.e. when talking about headers in lane context, we +//! only care about finalized headers. + +use std::{collections::BTreeMap, fmt::Debug, future::Future, ops::RangeInclusive, time::Duration}; + +use async_trait::async_trait; +use futures::{channel::mpsc::unbounded, future::FutureExt, stream::StreamExt}; + +use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState, Weight}; +use relay_utils::{ + interval, metrics::MetricsParams, process_future_result, relay_loop::Client as RelayClient, + retry_backoff, FailedClient, TransactionTracker, +}; + +use crate::{ + message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, + message_race_delivery::run as run_message_delivery_race, + message_race_receiving::run as run_message_receiving_race, + metrics::MessageLaneLoopMetrics, +}; + +/// Message lane loop configuration params. +#[derive(Debug, Clone)] +pub struct Params { + /// Id of lane this loop is servicing. + pub lane: LaneId, + /// Interval at which we ask target node about its updates. + pub source_tick: Duration, + /// Interval at which we ask target node about its updates. + pub target_tick: Duration, + /// Delay between moments when connection error happens and our reconnect attempt. + pub reconnect_delay: Duration, + /// Message delivery race parameters. + pub delivery_params: MessageDeliveryParams, +} + +/// Message delivery race parameters. +#[derive(Debug, Clone)] +pub struct MessageDeliveryParams { + /// Maximal number of unconfirmed relayer entries at the inbound lane. If there's that number + /// of entries in the `InboundLaneData::relayers` set, all new messages will be rejected until + /// reward payment will be proved (by including outbound lane state to the message delivery + /// transaction). + pub max_unrewarded_relayer_entries_at_target: MessageNonce, + /// Message delivery race will stop delivering messages if there are + /// `max_unconfirmed_nonces_at_target` unconfirmed nonces on the target node. The race would + /// continue once they're confirmed by the receiving race. + pub max_unconfirmed_nonces_at_target: MessageNonce, + /// Maximal number of relayed messages in single delivery transaction. + pub max_messages_in_single_batch: MessageNonce, + /// Maximal cumulative dispatch weight of relayed messages in single delivery transaction. + pub max_messages_weight_in_single_batch: Weight, + /// Maximal cumulative size of relayed messages in single delivery transaction. + pub max_messages_size_in_single_batch: u32, +} + +/// Message details. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MessageDetails { + /// Message dispatch weight. + pub dispatch_weight: Weight, + /// Message size (number of bytes in encoded payload). + pub size: u32, + /// The relayer reward paid in the source chain tokens. + pub reward: SourceChainBalance, +} + +/// Messages details map. +pub type MessageDetailsMap = + BTreeMap>; + +/// Message delivery race proof parameters. +#[derive(Debug, PartialEq, Eq)] +pub struct MessageProofParameters { + /// Include outbound lane state proof? + pub outbound_state_proof_required: bool, + /// Cumulative dispatch weight of messages that we're building proof for. + pub dispatch_weight: Weight, +} + +/// Artifacts of submitting nonces proof. +pub struct NoncesSubmitArtifacts { + /// Submitted nonces range. + pub nonces: RangeInclusive, + /// Submitted transaction tracker. + pub tx_tracker: T, +} + +/// Source client trait. +#[async_trait] +pub trait SourceClient: RelayClient { + /// Transaction tracker to track submitted transactions. + type TransactionTracker: TransactionTracker>; + + /// Returns state of the client. + async fn state(&self) -> Result, Self::Error>; + + /// Get nonce of instance of latest generated message. + async fn latest_generated_nonce( + &self, + id: SourceHeaderIdOf

, + ) -> Result<(SourceHeaderIdOf

, MessageNonce), Self::Error>; + + /// Get nonce of the latest message, which receiving has been confirmed by the target chain. + async fn latest_confirmed_received_nonce( + &self, + id: SourceHeaderIdOf

, + ) -> Result<(SourceHeaderIdOf

, MessageNonce), Self::Error>; + + /// Returns mapping of message nonces, generated on this client, to their weights. + /// + /// Some messages may be missing from returned map, if corresponding messages were pruned at + /// the source chain. + async fn generated_message_details( + &self, + id: SourceHeaderIdOf

, + nonces: RangeInclusive, + ) -> Result, Self::Error>; + + /// Prove messages in inclusive range [begin; end]. + async fn prove_messages( + &self, + id: SourceHeaderIdOf

, + nonces: RangeInclusive, + proof_parameters: MessageProofParameters, + ) -> Result<(SourceHeaderIdOf

, RangeInclusive, P::MessagesProof), Self::Error>; + + /// Submit messages receiving proof. + async fn submit_messages_receiving_proof( + &self, + generated_at_block: TargetHeaderIdOf

, + proof: P::MessagesReceivingProof, + ) -> Result; + + /// We need given finalized target header on source to continue synchronization. + async fn require_target_header_on_source(&self, id: TargetHeaderIdOf

); +} + +/// Target client trait. +#[async_trait] +pub trait TargetClient: RelayClient { + /// Transaction tracker to track submitted transactions. + type TransactionTracker: TransactionTracker>; + + /// Returns state of the client. + async fn state(&self) -> Result, Self::Error>; + + /// Get nonce of latest received message. + async fn latest_received_nonce( + &self, + id: TargetHeaderIdOf

, + ) -> Result<(TargetHeaderIdOf

, MessageNonce), Self::Error>; + + /// Get nonce of the latest confirmed message. + async fn latest_confirmed_received_nonce( + &self, + id: TargetHeaderIdOf

, + ) -> Result<(TargetHeaderIdOf

, MessageNonce), Self::Error>; + + /// Get state of unrewarded relayers set at the inbound lane. + async fn unrewarded_relayers_state( + &self, + id: TargetHeaderIdOf

, + ) -> Result<(TargetHeaderIdOf

, UnrewardedRelayersState), Self::Error>; + + /// Prove messages receiving at given block. + async fn prove_messages_receiving( + &self, + id: TargetHeaderIdOf

, + ) -> Result<(TargetHeaderIdOf

, P::MessagesReceivingProof), Self::Error>; + + /// Submit messages proof. + async fn submit_messages_proof( + &self, + generated_at_header: SourceHeaderIdOf

, + nonces: RangeInclusive, + proof: P::MessagesProof, + ) -> Result, Self::Error>; + + /// We need given finalized source header on target to continue synchronization. + async fn require_source_header_on_target(&self, id: SourceHeaderIdOf

); +} + +/// State of the client. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct ClientState { + /// The best header id of this chain. + pub best_self: SelfHeaderId, + /// Best finalized header id of this chain. + pub best_finalized_self: SelfHeaderId, + /// Best finalized header id of the peer chain read at the best block of this chain (at + /// `best_finalized_self`). + pub best_finalized_peer_at_best_self: PeerHeaderId, + /// Header id of the peer chain with the number, matching the + /// `best_finalized_peer_at_best_self`. + pub actual_best_finalized_peer_at_best_self: PeerHeaderId, +} + +/// State of source client in one-way message lane. +pub type SourceClientState

= ClientState, TargetHeaderIdOf

>; + +/// State of target client in one-way message lane. +pub type TargetClientState

= ClientState, SourceHeaderIdOf

>; + +/// Both clients state. +#[derive(Debug, Default)] +pub struct ClientsState { + /// Source client state. + pub source: Option>, + /// Target client state. + pub target: Option>, +} + +/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs +/// sync loop. +pub fn metrics_prefix(lane: &LaneId) -> String { + format!("{}_to_{}_MessageLane_{}", P::SOURCE_NAME, P::TARGET_NAME, hex::encode(lane)) +} + +/// Run message lane service loop. +pub async fn run( + params: Params, + source_client: impl SourceClient

, + target_client: impl TargetClient

, + metrics_params: MetricsParams, + exit_signal: impl Future + Send + 'static, +) -> Result<(), relay_utils::Error> { + let exit_signal = exit_signal.shared(); + relay_utils::relay_loop(source_client, target_client) + .reconnect_delay(params.reconnect_delay) + .with_metrics(metrics_params) + .loop_metric(MessageLaneLoopMetrics::new(Some(&metrics_prefix::

(¶ms.lane)))?)? + .expose() + .await? + .run(metrics_prefix::

(¶ms.lane), move |source_client, target_client, metrics| { + run_until_connection_lost( + params.clone(), + source_client, + target_client, + metrics, + exit_signal.clone(), + ) + }) + .await +} + +/// Run one-way message delivery loop until connection with target or source node is lost, or exit +/// signal is received. +async fn run_until_connection_lost, TC: TargetClient

>( + params: Params, + source_client: SC, + target_client: TC, + metrics_msg: Option, + exit_signal: impl Future, +) -> Result<(), FailedClient> { + let mut source_retry_backoff = retry_backoff(); + let mut source_client_is_online = false; + let mut source_state_required = true; + let source_state = source_client.state().fuse(); + let source_go_offline_future = futures::future::Fuse::terminated(); + let source_tick_stream = interval(params.source_tick).fuse(); + + let mut target_retry_backoff = retry_backoff(); + let mut target_client_is_online = false; + let mut target_state_required = true; + let target_state = target_client.state().fuse(); + let target_go_offline_future = futures::future::Fuse::terminated(); + let target_tick_stream = interval(params.target_tick).fuse(); + + let ( + (delivery_source_state_sender, delivery_source_state_receiver), + (delivery_target_state_sender, delivery_target_state_receiver), + ) = (unbounded(), unbounded()); + let delivery_race_loop = run_message_delivery_race( + source_client.clone(), + delivery_source_state_receiver, + target_client.clone(), + delivery_target_state_receiver, + metrics_msg.clone(), + params.delivery_params, + ) + .fuse(); + + let ( + (receiving_source_state_sender, receiving_source_state_receiver), + (receiving_target_state_sender, receiving_target_state_receiver), + ) = (unbounded(), unbounded()); + let receiving_race_loop = run_message_receiving_race( + source_client.clone(), + receiving_source_state_receiver, + target_client.clone(), + receiving_target_state_receiver, + metrics_msg.clone(), + ) + .fuse(); + + let exit_signal = exit_signal.fuse(); + + futures::pin_mut!( + source_state, + source_go_offline_future, + source_tick_stream, + target_state, + target_go_offline_future, + target_tick_stream, + delivery_race_loop, + receiving_race_loop, + exit_signal + ); + + loop { + futures::select! { + new_source_state = source_state => { + source_state_required = false; + + source_client_is_online = process_future_result( + new_source_state, + &mut source_retry_backoff, + |new_source_state| { + log::debug!( + target: "bridge", + "Received state from {} node: {:?}", + P::SOURCE_NAME, + new_source_state, + ); + let _ = delivery_source_state_sender.unbounded_send(new_source_state.clone()); + let _ = receiving_source_state_sender.unbounded_send(new_source_state.clone()); + + if let Some(metrics_msg) = metrics_msg.as_ref() { + metrics_msg.update_source_state::

(new_source_state); + } + }, + &mut source_go_offline_future, + async_std::task::sleep, + || format!("Error retrieving state from {} node", P::SOURCE_NAME), + ).fail_if_connection_error(FailedClient::Source)?; + }, + _ = source_go_offline_future => { + source_client_is_online = true; + }, + _ = source_tick_stream.next() => { + source_state_required = true; + }, + new_target_state = target_state => { + target_state_required = false; + + target_client_is_online = process_future_result( + new_target_state, + &mut target_retry_backoff, + |new_target_state| { + log::debug!( + target: "bridge", + "Received state from {} node: {:?}", + P::TARGET_NAME, + new_target_state, + ); + let _ = delivery_target_state_sender.unbounded_send(new_target_state.clone()); + let _ = receiving_target_state_sender.unbounded_send(new_target_state.clone()); + + if let Some(metrics_msg) = metrics_msg.as_ref() { + metrics_msg.update_target_state::

(new_target_state); + } + }, + &mut target_go_offline_future, + async_std::task::sleep, + || format!("Error retrieving state from {} node", P::TARGET_NAME), + ).fail_if_connection_error(FailedClient::Target)?; + }, + _ = target_go_offline_future => { + target_client_is_online = true; + }, + _ = target_tick_stream.next() => { + target_state_required = true; + }, + + delivery_error = delivery_race_loop => { + match delivery_error { + Ok(_) => unreachable!("only ends with error; qed"), + Err(err) => return Err(err), + } + }, + receiving_error = receiving_race_loop => { + match receiving_error { + Ok(_) => unreachable!("only ends with error; qed"), + Err(err) => return Err(err), + } + }, + + () = exit_signal => { + return Ok(()); + } + } + + if source_client_is_online && source_state_required { + log::debug!(target: "bridge", "Asking {} node about its state", P::SOURCE_NAME); + source_state.set(source_client.state().fuse()); + source_client_is_online = false; + } + + if target_client_is_online && target_state_required { + log::debug!(target: "bridge", "Asking {} node about its state", P::TARGET_NAME); + target_state.set(target_client.state().fuse()); + target_client_is_online = false; + } + } +} + +#[cfg(test)] +pub(crate) mod tests { + use std::sync::Arc; + + use futures::stream::StreamExt; + use parking_lot::Mutex; + + use relay_utils::{HeaderId, MaybeConnectionError, TrackedTransactionStatus}; + + use super::*; + + pub fn header_id(number: TestSourceHeaderNumber) -> TestSourceHeaderId { + HeaderId(number, number) + } + + pub type TestSourceChainBalance = u64; + pub type TestSourceHeaderId = HeaderId; + pub type TestTargetHeaderId = HeaderId; + + pub type TestMessagesProof = (RangeInclusive, Option); + pub type TestMessagesReceivingProof = MessageNonce; + + pub type TestSourceHeaderNumber = u64; + pub type TestSourceHeaderHash = u64; + + pub type TestTargetHeaderNumber = u64; + pub type TestTargetHeaderHash = u64; + + #[derive(Debug)] + pub struct TestError; + + impl MaybeConnectionError for TestError { + fn is_connection_error(&self) -> bool { + true + } + } + + #[derive(Clone)] + pub struct TestMessageLane; + + impl MessageLane for TestMessageLane { + const SOURCE_NAME: &'static str = "TestSource"; + const TARGET_NAME: &'static str = "TestTarget"; + + type MessagesProof = TestMessagesProof; + type MessagesReceivingProof = TestMessagesReceivingProof; + + type SourceChainBalance = TestSourceChainBalance; + type SourceHeaderNumber = TestSourceHeaderNumber; + type SourceHeaderHash = TestSourceHeaderHash; + + type TargetHeaderNumber = TestTargetHeaderNumber; + type TargetHeaderHash = TestTargetHeaderHash; + } + + #[derive(Clone, Debug)] + pub struct TestTransactionTracker(TrackedTransactionStatus); + + impl Default for TestTransactionTracker { + fn default() -> TestTransactionTracker { + TestTransactionTracker(TrackedTransactionStatus::Finalized(Default::default())) + } + } + + #[async_trait] + impl TransactionTracker for TestTransactionTracker { + type HeaderId = TestTargetHeaderId; + + async fn wait(self) -> TrackedTransactionStatus { + self.0 + } + } + + #[derive(Debug, Clone)] + pub struct TestClientData { + is_source_fails: bool, + is_source_reconnected: bool, + source_state: SourceClientState, + source_latest_generated_nonce: MessageNonce, + source_latest_confirmed_received_nonce: MessageNonce, + source_tracked_transaction_status: TrackedTransactionStatus, + submitted_messages_receiving_proofs: Vec, + is_target_fails: bool, + is_target_reconnected: bool, + target_state: SourceClientState, + target_latest_received_nonce: MessageNonce, + target_latest_confirmed_received_nonce: MessageNonce, + target_tracked_transaction_status: TrackedTransactionStatus, + submitted_messages_proofs: Vec, + target_to_source_header_required: Option, + target_to_source_header_requirements: Vec, + source_to_target_header_required: Option, + source_to_target_header_requirements: Vec, + } + + impl Default for TestClientData { + fn default() -> TestClientData { + TestClientData { + is_source_fails: false, + is_source_reconnected: false, + source_state: Default::default(), + source_latest_generated_nonce: 0, + source_latest_confirmed_received_nonce: 0, + source_tracked_transaction_status: TrackedTransactionStatus::Finalized(HeaderId( + 0, + Default::default(), + )), + submitted_messages_receiving_proofs: Vec::new(), + is_target_fails: false, + is_target_reconnected: false, + target_state: Default::default(), + target_latest_received_nonce: 0, + target_latest_confirmed_received_nonce: 0, + target_tracked_transaction_status: TrackedTransactionStatus::Finalized(HeaderId( + 0, + Default::default(), + )), + submitted_messages_proofs: Vec::new(), + target_to_source_header_required: None, + target_to_source_header_requirements: Vec::new(), + source_to_target_header_required: None, + source_to_target_header_requirements: Vec::new(), + } + } + } + + #[derive(Clone)] + pub struct TestSourceClient { + data: Arc>, + tick: Arc, + post_tick: Arc, + } + + impl Default for TestSourceClient { + fn default() -> Self { + TestSourceClient { + data: Arc::new(Mutex::new(TestClientData::default())), + tick: Arc::new(|_| {}), + post_tick: Arc::new(|_| {}), + } + } + } + + #[async_trait] + impl RelayClient for TestSourceClient { + type Error = TestError; + + async fn reconnect(&mut self) -> Result<(), TestError> { + { + let mut data = self.data.lock(); + (self.tick)(&mut data); + data.is_source_reconnected = true; + (self.post_tick)(&mut data); + } + Ok(()) + } + } + + #[async_trait] + impl SourceClient for TestSourceClient { + type TransactionTracker = TestTransactionTracker; + + async fn state(&self) -> Result, TestError> { + let mut data = self.data.lock(); + (self.tick)(&mut data); + if data.is_source_fails { + return Err(TestError) + } + (self.post_tick)(&mut data); + Ok(data.source_state.clone()) + } + + async fn latest_generated_nonce( + &self, + id: SourceHeaderIdOf, + ) -> Result<(SourceHeaderIdOf, MessageNonce), TestError> { + let mut data = self.data.lock(); + (self.tick)(&mut data); + if data.is_source_fails { + return Err(TestError) + } + (self.post_tick)(&mut data); + Ok((id, data.source_latest_generated_nonce)) + } + + async fn latest_confirmed_received_nonce( + &self, + id: SourceHeaderIdOf, + ) -> Result<(SourceHeaderIdOf, MessageNonce), TestError> { + let mut data = self.data.lock(); + (self.tick)(&mut data); + (self.post_tick)(&mut data); + Ok((id, data.source_latest_confirmed_received_nonce)) + } + + async fn generated_message_details( + &self, + _id: SourceHeaderIdOf, + nonces: RangeInclusive, + ) -> Result, TestError> { + Ok(nonces + .map(|nonce| { + ( + nonce, + MessageDetails { + dispatch_weight: Weight::from_ref_time(1), + size: 1, + reward: 1, + }, + ) + }) + .collect()) + } + + async fn prove_messages( + &self, + id: SourceHeaderIdOf, + nonces: RangeInclusive, + proof_parameters: MessageProofParameters, + ) -> Result< + (SourceHeaderIdOf, RangeInclusive, TestMessagesProof), + TestError, + > { + let mut data = self.data.lock(); + (self.tick)(&mut data); + (self.post_tick)(&mut data); + Ok(( + id, + nonces.clone(), + ( + nonces, + if proof_parameters.outbound_state_proof_required { + Some(data.source_latest_confirmed_received_nonce) + } else { + None + }, + ), + )) + } + + async fn submit_messages_receiving_proof( + &self, + _generated_at_block: TargetHeaderIdOf, + proof: TestMessagesReceivingProof, + ) -> Result { + let mut data = self.data.lock(); + (self.tick)(&mut data); + data.source_state.best_self = + HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.1 + 1); + data.source_state.best_finalized_self = data.source_state.best_self; + data.submitted_messages_receiving_proofs.push(proof); + data.source_latest_confirmed_received_nonce = proof; + (self.post_tick)(&mut data); + Ok(TestTransactionTracker(data.source_tracked_transaction_status)) + } + + async fn require_target_header_on_source(&self, id: TargetHeaderIdOf) { + let mut data = self.data.lock(); + data.target_to_source_header_required = Some(id); + data.target_to_source_header_requirements.push(id); + (self.tick)(&mut data); + (self.post_tick)(&mut data); + } + } + + #[derive(Clone)] + pub struct TestTargetClient { + data: Arc>, + tick: Arc, + post_tick: Arc, + } + + impl Default for TestTargetClient { + fn default() -> Self { + TestTargetClient { + data: Arc::new(Mutex::new(TestClientData::default())), + tick: Arc::new(|_| {}), + post_tick: Arc::new(|_| {}), + } + } + } + + #[async_trait] + impl RelayClient for TestTargetClient { + type Error = TestError; + + async fn reconnect(&mut self) -> Result<(), TestError> { + { + let mut data = self.data.lock(); + (self.tick)(&mut data); + data.is_target_reconnected = true; + (self.post_tick)(&mut data); + } + Ok(()) + } + } + + #[async_trait] + impl TargetClient for TestTargetClient { + type TransactionTracker = TestTransactionTracker; + + async fn state(&self) -> Result, TestError> { + let mut data = self.data.lock(); + (self.tick)(&mut data); + if data.is_target_fails { + return Err(TestError) + } + (self.post_tick)(&mut data); + Ok(data.target_state.clone()) + } + + async fn latest_received_nonce( + &self, + id: TargetHeaderIdOf, + ) -> Result<(TargetHeaderIdOf, MessageNonce), TestError> { + let mut data = self.data.lock(); + (self.tick)(&mut data); + if data.is_target_fails { + return Err(TestError) + } + (self.post_tick)(&mut data); + Ok((id, data.target_latest_received_nonce)) + } + + async fn unrewarded_relayers_state( + &self, + id: TargetHeaderIdOf, + ) -> Result<(TargetHeaderIdOf, UnrewardedRelayersState), TestError> { + Ok(( + id, + UnrewardedRelayersState { + unrewarded_relayer_entries: 0, + messages_in_oldest_entry: 0, + total_messages: 0, + last_delivered_nonce: 0, + }, + )) + } + + async fn latest_confirmed_received_nonce( + &self, + id: TargetHeaderIdOf, + ) -> Result<(TargetHeaderIdOf, MessageNonce), TestError> { + let mut data = self.data.lock(); + (self.tick)(&mut data); + if data.is_target_fails { + return Err(TestError) + } + (self.post_tick)(&mut data); + Ok((id, data.target_latest_confirmed_received_nonce)) + } + + async fn prove_messages_receiving( + &self, + id: TargetHeaderIdOf, + ) -> Result<(TargetHeaderIdOf, TestMessagesReceivingProof), TestError> { + Ok((id, self.data.lock().target_latest_received_nonce)) + } + + async fn submit_messages_proof( + &self, + _generated_at_header: SourceHeaderIdOf, + nonces: RangeInclusive, + proof: TestMessagesProof, + ) -> Result, TestError> { + let mut data = self.data.lock(); + (self.tick)(&mut data); + if data.is_target_fails { + return Err(TestError) + } + data.target_state.best_self = + HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1); + data.target_state.best_finalized_self = data.target_state.best_self; + data.target_latest_received_nonce = *proof.0.end(); + if let Some(target_latest_confirmed_received_nonce) = proof.1 { + data.target_latest_confirmed_received_nonce = + target_latest_confirmed_received_nonce; + } + data.submitted_messages_proofs.push(proof); + (self.post_tick)(&mut data); + Ok(NoncesSubmitArtifacts { + nonces, + tx_tracker: TestTransactionTracker(data.target_tracked_transaction_status), + }) + } + + async fn require_source_header_on_target(&self, id: SourceHeaderIdOf) { + let mut data = self.data.lock(); + data.source_to_target_header_required = Some(id); + data.source_to_target_header_requirements.push(id); + (self.tick)(&mut data); + (self.post_tick)(&mut data); + } + } + + fn run_loop_test( + data: TestClientData, + source_tick: Arc, + source_post_tick: Arc, + target_tick: Arc, + target_post_tick: Arc, + exit_signal: impl Future + 'static + Send, + ) -> TestClientData { + async_std::task::block_on(async { + let data = Arc::new(Mutex::new(data)); + + let source_client = TestSourceClient { + data: data.clone(), + tick: source_tick, + post_tick: source_post_tick, + }; + let target_client = TestTargetClient { + data: data.clone(), + tick: target_tick, + post_tick: target_post_tick, + }; + let _ = run( + Params { + lane: [0, 0, 0, 0], + source_tick: Duration::from_millis(100), + target_tick: Duration::from_millis(100), + reconnect_delay: Duration::from_millis(0), + delivery_params: MessageDeliveryParams { + max_unrewarded_relayer_entries_at_target: 4, + max_unconfirmed_nonces_at_target: 4, + max_messages_in_single_batch: 4, + max_messages_weight_in_single_batch: Weight::from_ref_time(4), + max_messages_size_in_single_batch: 4, + }, + }, + source_client, + target_client, + MetricsParams::disabled(), + exit_signal, + ) + .await; + let result = data.lock().clone(); + result + }) + } + + #[test] + fn message_lane_loop_is_able_to_recover_from_connection_errors() { + // with this configuration, source client will return Err, making source client + // reconnect. Then the target client will fail with Err + reconnect. Then we finally + // able to deliver messages. + let (exit_sender, exit_receiver) = unbounded(); + let result = run_loop_test( + TestClientData { + is_source_fails: true, + source_state: ClientState { + best_self: HeaderId(0, 0), + best_finalized_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: HeaderId(0, 0), + actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + }, + source_latest_generated_nonce: 1, + target_state: ClientState { + best_self: HeaderId(0, 0), + best_finalized_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: HeaderId(0, 0), + actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + }, + target_latest_received_nonce: 0, + ..Default::default() + }, + Arc::new(|data: &mut TestClientData| { + if data.is_source_reconnected { + data.is_source_fails = false; + data.is_target_fails = true; + } + }), + Arc::new(|_| {}), + Arc::new(move |data: &mut TestClientData| { + if data.is_target_reconnected { + data.is_target_fails = false; + } + if data.target_state.best_finalized_peer_at_best_self.0 < 10 { + data.target_state.best_finalized_peer_at_best_self = HeaderId( + data.target_state.best_finalized_peer_at_best_self.0 + 1, + data.target_state.best_finalized_peer_at_best_self.0 + 1, + ); + } + if !data.submitted_messages_proofs.is_empty() { + exit_sender.unbounded_send(()).unwrap(); + } + }), + Arc::new(|_| {}), + exit_receiver.into_future().map(|(_, _)| ()), + ); + + assert_eq!(result.submitted_messages_proofs, vec![(1..=1, None)],); + } + + #[test] + fn message_lane_loop_is_able_to_recover_from_race_stall() { + // with this configuration, both source and target clients will lose their transactions => + // reconnect will happen + let (source_exit_sender, exit_receiver) = unbounded(); + let target_exit_sender = source_exit_sender.clone(); + let result = run_loop_test( + TestClientData { + source_state: ClientState { + best_self: HeaderId(0, 0), + best_finalized_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: HeaderId(0, 0), + actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + }, + source_latest_generated_nonce: 1, + source_tracked_transaction_status: TrackedTransactionStatus::Lost, + target_state: ClientState { + best_self: HeaderId(0, 0), + best_finalized_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: HeaderId(0, 0), + actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + }, + target_latest_received_nonce: 0, + target_tracked_transaction_status: TrackedTransactionStatus::Lost, + ..Default::default() + }, + Arc::new(move |data: &mut TestClientData| { + if data.is_source_reconnected { + data.source_tracked_transaction_status = + TrackedTransactionStatus::Finalized(Default::default()); + } + if data.is_source_reconnected && data.is_target_reconnected { + source_exit_sender.unbounded_send(()).unwrap(); + } + }), + Arc::new(|_| {}), + Arc::new(move |data: &mut TestClientData| { + if data.is_target_reconnected { + data.target_tracked_transaction_status = + TrackedTransactionStatus::Finalized(Default::default()); + } + if data.is_source_reconnected && data.is_target_reconnected { + target_exit_sender.unbounded_send(()).unwrap(); + } + }), + Arc::new(|_| {}), + exit_receiver.into_future().map(|(_, _)| ()), + ); + + assert!(result.is_source_reconnected); + } + + #[test] + fn message_lane_loop_is_able_to_recover_from_unsuccessful_transaction() { + // with this configuration, both source and target clients will mine their transactions, but + // their corresponding nonce won't be udpated => reconnect will happen + let (exit_sender, exit_receiver) = unbounded(); + let result = run_loop_test( + TestClientData { + source_state: ClientState { + best_self: HeaderId(0, 0), + best_finalized_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: HeaderId(0, 0), + actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + }, + source_latest_generated_nonce: 1, + target_state: ClientState { + best_self: HeaderId(0, 0), + best_finalized_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: HeaderId(0, 0), + actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + }, + target_latest_received_nonce: 0, + ..Default::default() + }, + Arc::new(move |data: &mut TestClientData| { + // blocks are produced on every tick + data.source_state.best_self = + HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.1 + 1); + data.source_state.best_finalized_self = data.source_state.best_self; + // syncing target headers -> source chain + if let Some(last_requirement) = data.target_to_source_header_requirements.last() { + if *last_requirement != data.source_state.best_finalized_peer_at_best_self { + data.source_state.best_finalized_peer_at_best_self = *last_requirement; + } + } + }), + Arc::new(move |data: &mut TestClientData| { + // if it is the first time we're submitting delivery proof, let's revert changes + // to source status => then the delivery confirmation transaction is "finalized", + // but the state is not altered + if data.submitted_messages_receiving_proofs.len() == 1 { + data.source_latest_confirmed_received_nonce = 0; + } + }), + Arc::new(move |data: &mut TestClientData| { + // blocks are produced on every tick + data.target_state.best_self = + HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1); + data.target_state.best_finalized_self = data.target_state.best_self; + // syncing source headers -> target chain + if let Some(last_requirement) = data.source_to_target_header_requirements.last() { + if *last_requirement != data.target_state.best_finalized_peer_at_best_self { + data.target_state.best_finalized_peer_at_best_self = *last_requirement; + } + } + // if source has received all messages receiving confirmations => stop + if data.source_latest_confirmed_received_nonce == 1 { + exit_sender.unbounded_send(()).unwrap(); + } + }), + Arc::new(move |data: &mut TestClientData| { + // if it is the first time we're submitting messages proof, let's revert changes + // to target status => then the messages delivery transaction is "finalized", but + // the state is not altered + if data.submitted_messages_proofs.len() == 1 { + data.target_latest_received_nonce = 0; + data.target_latest_confirmed_received_nonce = 0; + } + }), + exit_receiver.into_future().map(|(_, _)| ()), + ); + + assert!(result.is_source_reconnected); + assert_eq!(result.submitted_messages_proofs.len(), 2); + assert_eq!(result.submitted_messages_receiving_proofs.len(), 2); + } + + #[test] + fn message_lane_loop_works() { + let (exit_sender, exit_receiver) = unbounded(); + let result = run_loop_test( + TestClientData { + source_state: ClientState { + best_self: HeaderId(10, 10), + best_finalized_self: HeaderId(10, 10), + best_finalized_peer_at_best_self: HeaderId(0, 0), + actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + }, + source_latest_generated_nonce: 10, + target_state: ClientState { + best_self: HeaderId(0, 0), + best_finalized_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: HeaderId(0, 0), + actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + }, + target_latest_received_nonce: 0, + ..Default::default() + }, + Arc::new(|data: &mut TestClientData| { + // blocks are produced on every tick + data.source_state.best_self = + HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.1 + 1); + data.source_state.best_finalized_self = data.source_state.best_self; + // headers relay must only be started when we need new target headers at source node + if data.target_to_source_header_required.is_some() { + assert!( + data.source_state.best_finalized_peer_at_best_self.0 < + data.target_state.best_self.0 + ); + data.target_to_source_header_required = None; + } + // syncing target headers -> source chain + if let Some(last_requirement) = data.target_to_source_header_requirements.last() { + if *last_requirement != data.source_state.best_finalized_peer_at_best_self { + data.source_state.best_finalized_peer_at_best_self = *last_requirement; + } + } + }), + Arc::new(|_| {}), + Arc::new(move |data: &mut TestClientData| { + // blocks are produced on every tick + data.target_state.best_self = + HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1); + data.target_state.best_finalized_self = data.target_state.best_self; + // headers relay must only be started when we need new source headers at target node + if data.source_to_target_header_required.is_some() { + assert!( + data.target_state.best_finalized_peer_at_best_self.0 < + data.source_state.best_self.0 + ); + data.source_to_target_header_required = None; + } + // syncing source headers -> target chain + if let Some(last_requirement) = data.source_to_target_header_requirements.last() { + if *last_requirement != data.target_state.best_finalized_peer_at_best_self { + data.target_state.best_finalized_peer_at_best_self = *last_requirement; + } + } + // if source has received all messages receiving confirmations => stop + if data.source_latest_confirmed_received_nonce == 10 { + exit_sender.unbounded_send(()).unwrap(); + } + }), + Arc::new(|_| {}), + exit_receiver.into_future().map(|(_, _)| ()), + ); + + // there are no strict restrictions on when reward confirmation should come + // (because `max_unconfirmed_nonces_at_target` is `100` in tests and this confirmation + // depends on the state of both clients) + // => we do not check it here + assert_eq!(result.submitted_messages_proofs[0].0, 1..=4); + assert_eq!(result.submitted_messages_proofs[1].0, 5..=8); + assert_eq!(result.submitted_messages_proofs[2].0, 9..=10); + assert!(!result.submitted_messages_receiving_proofs.is_empty()); + + // check that we have at least once required new source->target or target->source headers + assert!(!result.target_to_source_header_requirements.is_empty()); + assert!(!result.source_to_target_header_requirements.is_empty()); + } +} diff --git a/relays/messages/src/message_race_delivery.rs b/relays/messages/src/message_race_delivery.rs new file mode 100644 index 00000000000..b49a05dac5c --- /dev/null +++ b/relays/messages/src/message_race_delivery.rs @@ -0,0 +1,984 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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. + +//! Message delivery race delivers proof-of-messages from "lane.source" to "lane.target". + +use std::{collections::VecDeque, marker::PhantomData, ops::RangeInclusive}; + +use async_trait::async_trait; +use futures::stream::FusedStream; + +use bp_messages::{MessageNonce, UnrewardedRelayersState, Weight}; +use relay_utils::FailedClient; + +use crate::{ + message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, + message_lane_loop::{ + MessageDeliveryParams, MessageDetailsMap, MessageProofParameters, NoncesSubmitArtifacts, + SourceClient as MessageLaneSourceClient, SourceClientState, + TargetClient as MessageLaneTargetClient, TargetClientState, + }, + message_race_limits::{MessageRaceLimits, RelayMessagesBatchReference}, + message_race_loop::{ + MessageRace, NoncesRange, RaceState, RaceStrategy, SourceClient, SourceClientNonces, + TargetClient, TargetClientNonces, + }, + message_race_strategy::BasicStrategy, + metrics::MessageLaneLoopMetrics, +}; + +/// Run message delivery race. +pub async fn run( + source_client: impl MessageLaneSourceClient

, + source_state_updates: impl FusedStream>, + target_client: impl MessageLaneTargetClient

, + target_state_updates: impl FusedStream>, + metrics_msg: Option, + params: MessageDeliveryParams, +) -> Result<(), FailedClient> { + crate::message_race_loop::run( + MessageDeliveryRaceSource { + client: source_client.clone(), + metrics_msg: metrics_msg.clone(), + _phantom: Default::default(), + }, + source_state_updates, + MessageDeliveryRaceTarget { + client: target_client.clone(), + metrics_msg: metrics_msg.clone(), + _phantom: Default::default(), + }, + target_state_updates, + MessageDeliveryStrategy:: { + lane_source_client: source_client, + lane_target_client: target_client, + max_unrewarded_relayer_entries_at_target: params + .max_unrewarded_relayer_entries_at_target, + max_unconfirmed_nonces_at_target: params.max_unconfirmed_nonces_at_target, + max_messages_in_single_batch: params.max_messages_in_single_batch, + max_messages_weight_in_single_batch: params.max_messages_weight_in_single_batch, + max_messages_size_in_single_batch: params.max_messages_size_in_single_batch, + latest_confirmed_nonces_at_source: VecDeque::new(), + target_nonces: None, + strategy: BasicStrategy::new(), + metrics_msg, + }, + ) + .await +} + +/// Message delivery race. +struct MessageDeliveryRace

(std::marker::PhantomData

); + +impl MessageRace for MessageDeliveryRace

{ + type SourceHeaderId = SourceHeaderIdOf

; + type TargetHeaderId = TargetHeaderIdOf

; + + type MessageNonce = MessageNonce; + type Proof = P::MessagesProof; + + fn source_name() -> String { + format!("{}::MessagesDelivery", P::SOURCE_NAME) + } + + fn target_name() -> String { + format!("{}::MessagesDelivery", P::TARGET_NAME) + } +} + +/// Message delivery race source, which is a source of the lane. +struct MessageDeliveryRaceSource { + client: C, + metrics_msg: Option, + _phantom: PhantomData

, +} + +#[async_trait] +impl SourceClient> for MessageDeliveryRaceSource +where + P: MessageLane, + C: MessageLaneSourceClient

, +{ + type Error = C::Error; + type NoncesRange = MessageDetailsMap; + type ProofParameters = MessageProofParameters; + + async fn nonces( + &self, + at_block: SourceHeaderIdOf

, + prev_latest_nonce: MessageNonce, + ) -> Result<(SourceHeaderIdOf

, SourceClientNonces), Self::Error> { + let (at_block, latest_generated_nonce) = + self.client.latest_generated_nonce(at_block).await?; + let (at_block, latest_confirmed_nonce) = + self.client.latest_confirmed_received_nonce(at_block).await?; + + if let Some(metrics_msg) = self.metrics_msg.as_ref() { + metrics_msg.update_source_latest_generated_nonce::

(latest_generated_nonce); + metrics_msg.update_source_latest_confirmed_nonce::

(latest_confirmed_nonce); + } + + let new_nonces = if latest_generated_nonce > prev_latest_nonce { + self.client + .generated_message_details( + at_block.clone(), + prev_latest_nonce + 1..=latest_generated_nonce, + ) + .await? + } else { + MessageDetailsMap::new() + }; + + Ok(( + at_block, + SourceClientNonces { new_nonces, confirmed_nonce: Some(latest_confirmed_nonce) }, + )) + } + + async fn generate_proof( + &self, + at_block: SourceHeaderIdOf

, + nonces: RangeInclusive, + proof_parameters: Self::ProofParameters, + ) -> Result<(SourceHeaderIdOf

, RangeInclusive, P::MessagesProof), Self::Error> + { + self.client.prove_messages(at_block, nonces, proof_parameters).await + } +} + +/// Message delivery race target, which is a target of the lane. +struct MessageDeliveryRaceTarget { + client: C, + metrics_msg: Option, + _phantom: PhantomData

, +} + +#[async_trait] +impl TargetClient> for MessageDeliveryRaceTarget +where + P: MessageLane, + C: MessageLaneTargetClient

, +{ + type Error = C::Error; + type TargetNoncesData = DeliveryRaceTargetNoncesData; + type TransactionTracker = C::TransactionTracker; + + async fn require_source_header(&self, id: SourceHeaderIdOf

) { + self.client.require_source_header_on_target(id).await + } + + async fn nonces( + &self, + at_block: TargetHeaderIdOf

, + update_metrics: bool, + ) -> Result<(TargetHeaderIdOf

, TargetClientNonces), Self::Error> + { + let (at_block, latest_received_nonce) = self.client.latest_received_nonce(at_block).await?; + let (at_block, latest_confirmed_nonce) = + self.client.latest_confirmed_received_nonce(at_block).await?; + let (at_block, unrewarded_relayers) = + self.client.unrewarded_relayers_state(at_block).await?; + + if update_metrics { + if let Some(metrics_msg) = self.metrics_msg.as_ref() { + metrics_msg.update_target_latest_received_nonce::

(latest_received_nonce); + metrics_msg.update_target_latest_confirmed_nonce::

(latest_confirmed_nonce); + } + } + + Ok(( + at_block, + TargetClientNonces { + latest_nonce: latest_received_nonce, + nonces_data: DeliveryRaceTargetNoncesData { + confirmed_nonce: latest_confirmed_nonce, + unrewarded_relayers, + }, + }, + )) + } + + async fn submit_proof( + &self, + generated_at_block: SourceHeaderIdOf

, + nonces: RangeInclusive, + proof: P::MessagesProof, + ) -> Result, Self::Error> { + self.client.submit_messages_proof(generated_at_block, nonces, proof).await + } +} + +/// Additional nonces data from the target client used by message delivery race. +#[derive(Debug, Clone)] +struct DeliveryRaceTargetNoncesData { + /// The latest nonce that we know: (1) has been delivered to us (2) has been confirmed + /// back to the source node (by confirmations race) and (3) relayer has received + /// reward for (and this has been confirmed by the message delivery race). + confirmed_nonce: MessageNonce, + /// State of the unrewarded relayers set at the target node. + unrewarded_relayers: UnrewardedRelayersState, +} + +/// Messages delivery strategy. +struct MessageDeliveryStrategy { + /// The client that is connected to the message lane source node. + lane_source_client: SC, + /// The client that is connected to the message lane target node. + lane_target_client: TC, + /// Maximal unrewarded relayer entries at target client. + max_unrewarded_relayer_entries_at_target: MessageNonce, + /// Maximal unconfirmed nonces at target client. + max_unconfirmed_nonces_at_target: MessageNonce, + /// Maximal number of messages in the single delivery transaction. + max_messages_in_single_batch: MessageNonce, + /// Maximal cumulative messages weight in the single delivery transaction. + max_messages_weight_in_single_batch: Weight, + /// Maximal messages size in the single delivery transaction. + max_messages_size_in_single_batch: u32, + /// Latest confirmed nonces at the source client + the header id where we have first met this + /// nonce. + latest_confirmed_nonces_at_source: VecDeque<(SourceHeaderIdOf

, MessageNonce)>, + /// Target nonces from the source client. + target_nonces: Option>, + /// Basic delivery strategy. + strategy: MessageDeliveryStrategyBase

, + /// Message lane metrics. + metrics_msg: Option, +} + +type MessageDeliveryStrategyBase

= BasicStrategy< +

::SourceHeaderNumber, +

::SourceHeaderHash, +

::TargetHeaderNumber, +

::TargetHeaderHash, + MessageDetailsMap<

::SourceChainBalance>, +

::MessagesProof, +>; + +impl std::fmt::Debug for MessageDeliveryStrategy { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.debug_struct("MessageDeliveryStrategy") + .field( + "max_unrewarded_relayer_entries_at_target", + &self.max_unrewarded_relayer_entries_at_target, + ) + .field("max_unconfirmed_nonces_at_target", &self.max_unconfirmed_nonces_at_target) + .field("max_messages_in_single_batch", &self.max_messages_in_single_batch) + .field("max_messages_weight_in_single_batch", &self.max_messages_weight_in_single_batch) + .field("max_messages_size_in_single_batch", &self.max_messages_size_in_single_batch) + .field("latest_confirmed_nonces_at_source", &self.latest_confirmed_nonces_at_source) + .field("target_nonces", &self.target_nonces) + .field("strategy", &self.strategy) + .finish() + } +} + +impl MessageDeliveryStrategy { + /// Returns total weight of all undelivered messages. + fn total_queued_dispatch_weight(&self) -> Weight { + self.strategy + .source_queue() + .iter() + .flat_map(|(_, range)| range.values().map(|details| details.dispatch_weight)) + .fold(Weight::zero(), |total, weight| total.saturating_add(weight)) + } +} + +#[async_trait] +impl RaceStrategy, TargetHeaderIdOf

, P::MessagesProof> + for MessageDeliveryStrategy +where + P: MessageLane, + SC: MessageLaneSourceClient

, + TC: MessageLaneTargetClient

, +{ + type SourceNoncesRange = MessageDetailsMap; + type ProofParameters = MessageProofParameters; + type TargetNoncesData = DeliveryRaceTargetNoncesData; + + fn is_empty(&self) -> bool { + self.strategy.is_empty() + } + + fn required_source_header_at_target( + &self, + current_best: &SourceHeaderIdOf

, + ) -> Option> { + let header_required_for_messages_delivery = + self.strategy.required_source_header_at_target(current_best); + let header_required_for_reward_confirmations_delivery = + self.latest_confirmed_nonces_at_source.back().map(|(id, _)| id.clone()); + match ( + header_required_for_messages_delivery, + header_required_for_reward_confirmations_delivery, + ) { + (Some(id1), Some(id2)) => Some(if id1.0 > id2.0 { id1 } else { id2 }), + (a, b) => a.or(b), + } + } + + fn best_at_source(&self) -> Option { + self.strategy.best_at_source() + } + + fn best_at_target(&self) -> Option { + self.strategy.best_at_target() + } + + fn source_nonces_updated( + &mut self, + at_block: SourceHeaderIdOf

, + nonces: SourceClientNonces, + ) { + if let Some(confirmed_nonce) = nonces.confirmed_nonce { + let is_confirmed_nonce_updated = self + .latest_confirmed_nonces_at_source + .back() + .map(|(_, prev_nonce)| *prev_nonce != confirmed_nonce) + .unwrap_or(true); + if is_confirmed_nonce_updated { + self.latest_confirmed_nonces_at_source + .push_back((at_block.clone(), confirmed_nonce)); + } + } + self.strategy.source_nonces_updated(at_block, nonces) + } + + fn best_target_nonces_updated( + &mut self, + nonces: TargetClientNonces, + race_state: &mut RaceState, TargetHeaderIdOf

, P::MessagesProof>, + ) { + // best target nonces must always be ge than finalized target nonces + let mut target_nonces = self.target_nonces.take().unwrap_or_else(|| nonces.clone()); + target_nonces.nonces_data = nonces.nonces_data.clone(); + target_nonces.latest_nonce = std::cmp::max(target_nonces.latest_nonce, nonces.latest_nonce); + self.target_nonces = Some(target_nonces); + + self.strategy.best_target_nonces_updated( + TargetClientNonces { latest_nonce: nonces.latest_nonce, nonces_data: () }, + race_state, + ) + } + + fn finalized_target_nonces_updated( + &mut self, + nonces: TargetClientNonces, + race_state: &mut RaceState, TargetHeaderIdOf

, P::MessagesProof>, + ) { + if let Some(ref best_finalized_source_header_id_at_best_target) = + race_state.best_finalized_source_header_id_at_best_target + { + let oldest_header_number_to_keep = best_finalized_source_header_id_at_best_target.0; + while self + .latest_confirmed_nonces_at_source + .front() + .map(|(id, _)| id.0 < oldest_header_number_to_keep) + .unwrap_or(false) + { + self.latest_confirmed_nonces_at_source.pop_front(); + } + } + + if let Some(ref mut target_nonces) = self.target_nonces { + target_nonces.latest_nonce = + std::cmp::max(target_nonces.latest_nonce, nonces.latest_nonce); + } + + self.strategy.finalized_target_nonces_updated( + TargetClientNonces { latest_nonce: nonces.latest_nonce, nonces_data: () }, + race_state, + ) + } + + async fn select_nonces_to_deliver( + &mut self, + race_state: RaceState, TargetHeaderIdOf

, P::MessagesProof>, + ) -> Option<(RangeInclusive, Self::ProofParameters)> { + let best_finalized_source_header_id_at_best_target = + race_state.best_finalized_source_header_id_at_best_target.clone()?; + let latest_confirmed_nonce_at_source = self + .latest_confirmed_nonces_at_source + .iter() + .take_while(|(id, _)| id.0 <= best_finalized_source_header_id_at_best_target.0) + .last() + .map(|(_, nonce)| *nonce)?; + let target_nonces = self.target_nonces.as_ref()?; + + // There's additional condition in the message delivery race: target would reject messages + // if there are too much unconfirmed messages at the inbound lane. + + // The receiving race is responsible to deliver confirmations back to the source chain. So + // if there's a lot of unconfirmed messages, let's wait until it'll be able to do its job. + let latest_received_nonce_at_target = target_nonces.latest_nonce; + let confirmations_missing = + latest_received_nonce_at_target.checked_sub(latest_confirmed_nonce_at_source); + match confirmations_missing { + Some(confirmations_missing) + if confirmations_missing >= self.max_unconfirmed_nonces_at_target => + { + log::debug!( + target: "bridge", + "Cannot deliver any more messages from {} to {}. Too many unconfirmed nonces \ + at target: target.latest_received={:?}, source.latest_confirmed={:?}, max={:?}", + MessageDeliveryRace::

::source_name(), + MessageDeliveryRace::

::target_name(), + latest_received_nonce_at_target, + latest_confirmed_nonce_at_source, + self.max_unconfirmed_nonces_at_target, + ); + + return None + }, + _ => (), + } + + // Ok - we may have new nonces to deliver. But target may still reject new messages, because + // we haven't notified it that (some) messages have been confirmed. So we may want to + // include updated `source.latest_confirmed` in the proof. + // + // Important note: we're including outbound state lane proof whenever there are unconfirmed + // nonces on the target chain. Other strategy is to include it only if it's absolutely + // necessary. + let latest_confirmed_nonce_at_target = target_nonces.nonces_data.confirmed_nonce; + let outbound_state_proof_required = + latest_confirmed_nonce_at_target < latest_confirmed_nonce_at_source; + + // The target node would also reject messages if there are too many entries in the + // "unrewarded relayers" set. If we are unable to prove new rewards to the target node, then + // we should wait for confirmations race. + let unrewarded_relayer_entries_limit_reached = + target_nonces.nonces_data.unrewarded_relayers.unrewarded_relayer_entries >= + self.max_unrewarded_relayer_entries_at_target; + if unrewarded_relayer_entries_limit_reached { + // so there are already too many unrewarded relayer entries in the set + // + // => check if we can prove enough rewards. If not, we should wait for more rewards to + // be paid + let number_of_rewards_being_proved = + latest_confirmed_nonce_at_source.saturating_sub(latest_confirmed_nonce_at_target); + let enough_rewards_being_proved = number_of_rewards_being_proved >= + target_nonces.nonces_data.unrewarded_relayers.messages_in_oldest_entry; + if !enough_rewards_being_proved { + return None + } + } + + // If we're here, then the confirmations race did its job && sending side now knows that + // messages have been delivered. Now let's select nonces that we want to deliver. + // + // We may deliver at most: + // + // max_unconfirmed_nonces_at_target - (latest_received_nonce_at_target - + // latest_confirmed_nonce_at_target) + // + // messages in the batch. But since we're including outbound state proof in the batch, then + // it may be increased to: + // + // max_unconfirmed_nonces_at_target - (latest_received_nonce_at_target - + // latest_confirmed_nonce_at_source) + let future_confirmed_nonce_at_target = if outbound_state_proof_required { + latest_confirmed_nonce_at_source + } else { + latest_confirmed_nonce_at_target + }; + let max_nonces = latest_received_nonce_at_target + .checked_sub(future_confirmed_nonce_at_target) + .and_then(|diff| self.max_unconfirmed_nonces_at_target.checked_sub(diff)) + .unwrap_or_default(); + let max_nonces = std::cmp::min(max_nonces, self.max_messages_in_single_batch); + let max_messages_weight_in_single_batch = self.max_messages_weight_in_single_batch; + let max_messages_size_in_single_batch = self.max_messages_size_in_single_batch; + let lane_source_client = self.lane_source_client.clone(); + let lane_target_client = self.lane_target_client.clone(); + + let maximal_source_queue_index = + self.strategy.maximal_available_source_queue_index(race_state)?; + let previous_total_dispatch_weight = self.total_queued_dispatch_weight(); + let source_queue = self.strategy.source_queue(); + + let reference = RelayMessagesBatchReference { + max_messages_in_this_batch: max_nonces, + max_messages_weight_in_single_batch, + max_messages_size_in_single_batch, + lane_source_client: lane_source_client.clone(), + lane_target_client: lane_target_client.clone(), + nonces_queue: source_queue.clone(), + nonces_queue_range: 0..maximal_source_queue_index + 1, + metrics: self.metrics_msg.clone(), + }; + + let range_end = MessageRaceLimits::decide(reference).await?; + + let range_begin = source_queue[0].1.begin(); + let selected_nonces = range_begin..=range_end; + self.strategy.remove_le_nonces_from_source_queue(range_end); + + let new_total_dispatch_weight = self.total_queued_dispatch_weight(); + let dispatch_weight = previous_total_dispatch_weight - new_total_dispatch_weight; + + Some(( + selected_nonces, + MessageProofParameters { outbound_state_proof_required, dispatch_weight }, + )) + } +} + +impl NoncesRange for MessageDetailsMap { + fn begin(&self) -> MessageNonce { + self.keys().next().cloned().unwrap_or_default() + } + + fn end(&self) -> MessageNonce { + self.keys().next_back().cloned().unwrap_or_default() + } + + fn greater_than(mut self, nonce: MessageNonce) -> Option { + let gte = self.split_off(&(nonce + 1)); + if gte.is_empty() { + None + } else { + Some(gte) + } + } +} + +#[cfg(test)] +mod tests { + use crate::message_lane_loop::{ + tests::{ + header_id, TestMessageLane, TestMessagesProof, TestSourceChainBalance, + TestSourceClient, TestSourceHeaderId, TestTargetClient, TestTargetHeaderId, + }, + MessageDetails, + }; + + use super::*; + + const DEFAULT_DISPATCH_WEIGHT: Weight = Weight::from_ref_time(1); + const DEFAULT_SIZE: u32 = 1; + + type TestRaceState = RaceState; + type TestStrategy = + MessageDeliveryStrategy; + + fn source_nonces( + new_nonces: RangeInclusive, + confirmed_nonce: MessageNonce, + reward: TestSourceChainBalance, + ) -> SourceClientNonces> { + SourceClientNonces { + new_nonces: new_nonces + .into_iter() + .map(|nonce| { + ( + nonce, + MessageDetails { + dispatch_weight: DEFAULT_DISPATCH_WEIGHT, + size: DEFAULT_SIZE, + reward, + }, + ) + }) + .into_iter() + .collect(), + confirmed_nonce: Some(confirmed_nonce), + } + } + + fn prepare_strategy() -> (TestRaceState, TestStrategy) { + let mut race_state = RaceState { + best_finalized_source_header_id_at_source: Some(header_id(1)), + best_finalized_source_header_id_at_best_target: Some(header_id(1)), + best_target_header_id: Some(header_id(1)), + best_finalized_target_header_id: Some(header_id(1)), + nonces_to_submit: None, + nonces_submitted: None, + }; + + let mut race_strategy = TestStrategy { + max_unrewarded_relayer_entries_at_target: 4, + max_unconfirmed_nonces_at_target: 4, + max_messages_in_single_batch: 4, + max_messages_weight_in_single_batch: Weight::from_ref_time(4), + max_messages_size_in_single_batch: 4, + latest_confirmed_nonces_at_source: vec![(header_id(1), 19)].into_iter().collect(), + lane_source_client: TestSourceClient::default(), + lane_target_client: TestTargetClient::default(), + metrics_msg: None, + target_nonces: Some(TargetClientNonces { + latest_nonce: 19, + nonces_data: DeliveryRaceTargetNoncesData { + confirmed_nonce: 19, + unrewarded_relayers: UnrewardedRelayersState { + unrewarded_relayer_entries: 0, + messages_in_oldest_entry: 0, + total_messages: 0, + last_delivered_nonce: 0, + }, + }, + }), + strategy: BasicStrategy::new(), + }; + + race_strategy + .strategy + .source_nonces_updated(header_id(1), source_nonces(20..=23, 19, 0)); + + let target_nonces = TargetClientNonces { latest_nonce: 19, nonces_data: () }; + race_strategy + .strategy + .best_target_nonces_updated(target_nonces.clone(), &mut race_state); + race_strategy + .strategy + .finalized_target_nonces_updated(target_nonces, &mut race_state); + + (race_state, race_strategy) + } + + fn proof_parameters(state_required: bool, weight: u32) -> MessageProofParameters { + MessageProofParameters { + outbound_state_proof_required: state_required, + dispatch_weight: Weight::from_ref_time(weight as u64), + } + } + + #[test] + fn weights_map_works_as_nonces_range() { + fn build_map( + range: RangeInclusive, + ) -> MessageDetailsMap { + range + .map(|idx| { + ( + idx, + MessageDetails { + dispatch_weight: Weight::from_ref_time(idx), + size: idx as _, + reward: idx as _, + }, + ) + }) + .collect() + } + + let map = build_map(20..=30); + + assert_eq!(map.begin(), 20); + assert_eq!(map.end(), 30); + assert_eq!(map.clone().greater_than(10), Some(build_map(20..=30))); + assert_eq!(map.clone().greater_than(19), Some(build_map(20..=30))); + assert_eq!(map.clone().greater_than(20), Some(build_map(21..=30))); + assert_eq!(map.clone().greater_than(25), Some(build_map(26..=30))); + assert_eq!(map.clone().greater_than(29), Some(build_map(30..=30))); + assert_eq!(map.greater_than(30), None); + } + + #[async_std::test] + async fn message_delivery_strategy_selects_messages_to_deliver() { + let (state, mut strategy) = prepare_strategy(); + + // both sides are ready to relay new messages + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=23), proof_parameters(false, 4))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_selects_nothing_if_too_many_confirmations_missing() { + let (state, mut strategy) = prepare_strategy(); + + // if there are already `max_unconfirmed_nonces_at_target` messages on target, + // we need to wait until confirmations will be delivered by receiving race + strategy.latest_confirmed_nonces_at_source = vec![( + header_id(1), + strategy.target_nonces.as_ref().unwrap().latest_nonce - + strategy.max_unconfirmed_nonces_at_target, + )] + .into_iter() + .collect(); + assert_eq!(strategy.select_nonces_to_deliver(state).await, None); + } + + #[async_std::test] + async fn message_delivery_strategy_includes_outbound_state_proof_when_new_nonces_are_available() + { + let (state, mut strategy) = prepare_strategy(); + + // if there are new confirmed nonces on source, we want to relay this information + // to target to prune rewards queue + let prev_confirmed_nonce_at_source = + strategy.latest_confirmed_nonces_at_source.back().unwrap().1; + strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = + prev_confirmed_nonce_at_source - 1; + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=23), proof_parameters(true, 4))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_selects_nothing_if_there_are_too_many_unrewarded_relayers() { + let (state, mut strategy) = prepare_strategy(); + + // if there are already `max_unrewarded_relayer_entries_at_target` entries at target, + // we need to wait until rewards will be paid + { + let mut unrewarded_relayers = + &mut strategy.target_nonces.as_mut().unwrap().nonces_data.unrewarded_relayers; + unrewarded_relayers.unrewarded_relayer_entries = + strategy.max_unrewarded_relayer_entries_at_target; + unrewarded_relayers.messages_in_oldest_entry = 4; + } + assert_eq!(strategy.select_nonces_to_deliver(state).await, None); + } + + #[async_std::test] + async fn message_delivery_strategy_selects_nothing_if_proved_rewards_is_not_enough_to_remove_oldest_unrewarded_entry( + ) { + let (state, mut strategy) = prepare_strategy(); + + // if there are already `max_unrewarded_relayer_entries_at_target` entries at target, + // we need to prove at least `messages_in_oldest_entry` rewards + let prev_confirmed_nonce_at_source = + strategy.latest_confirmed_nonces_at_source.back().unwrap().1; + { + let mut nonces_data = &mut strategy.target_nonces.as_mut().unwrap().nonces_data; + nonces_data.confirmed_nonce = prev_confirmed_nonce_at_source - 1; + let mut unrewarded_relayers = &mut nonces_data.unrewarded_relayers; + unrewarded_relayers.unrewarded_relayer_entries = + strategy.max_unrewarded_relayer_entries_at_target; + unrewarded_relayers.messages_in_oldest_entry = 4; + } + assert_eq!(strategy.select_nonces_to_deliver(state).await, None); + } + + #[async_std::test] + async fn message_delivery_strategy_includes_outbound_state_proof_if_proved_rewards_is_enough() { + let (state, mut strategy) = prepare_strategy(); + + // if there are already `max_unrewarded_relayer_entries_at_target` entries at target, + // we need to prove at least `messages_in_oldest_entry` rewards + let prev_confirmed_nonce_at_source = + strategy.latest_confirmed_nonces_at_source.back().unwrap().1; + { + let mut nonces_data = &mut strategy.target_nonces.as_mut().unwrap().nonces_data; + nonces_data.confirmed_nonce = prev_confirmed_nonce_at_source - 3; + let mut unrewarded_relayers = &mut nonces_data.unrewarded_relayers; + unrewarded_relayers.unrewarded_relayer_entries = + strategy.max_unrewarded_relayer_entries_at_target; + unrewarded_relayers.messages_in_oldest_entry = 3; + } + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=23), proof_parameters(true, 4))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_limits_batch_by_messages_weight() { + let (state, mut strategy) = prepare_strategy(); + + // not all queued messages may fit in the batch, because batch has max weight + strategy.max_messages_weight_in_single_batch = Weight::from_ref_time(3); + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=22), proof_parameters(false, 3))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_accepts_single_message_even_if_its_weight_overflows_maximal_weight( + ) { + let (state, mut strategy) = prepare_strategy(); + + // first message doesn't fit in the batch, because it has weight (10) that overflows max + // weight (4) + strategy.strategy.source_queue_mut()[0].1.get_mut(&20).unwrap().dispatch_weight = + Weight::from_ref_time(10); + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=20), proof_parameters(false, 10))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_limits_batch_by_messages_size() { + let (state, mut strategy) = prepare_strategy(); + + // not all queued messages may fit in the batch, because batch has max weight + strategy.max_messages_size_in_single_batch = 3; + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=22), proof_parameters(false, 3))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_accepts_single_message_even_if_its_weight_overflows_maximal_size( + ) { + let (state, mut strategy) = prepare_strategy(); + + // first message doesn't fit in the batch, because it has weight (10) that overflows max + // weight (4) + strategy.strategy.source_queue_mut()[0].1.get_mut(&20).unwrap().size = 10; + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=20), proof_parameters(false, 1))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_limits_batch_by_messages_count_when_there_is_upper_limit() { + let (state, mut strategy) = prepare_strategy(); + + // not all queued messages may fit in the batch, because batch has max number of messages + // limit + strategy.max_messages_in_single_batch = 3; + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=22), proof_parameters(false, 3))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_limits_batch_by_messages_count_when_there_are_unconfirmed_nonces( + ) { + let (state, mut strategy) = prepare_strategy(); + + // 1 delivery confirmation from target to source is still missing, so we may only + // relay 3 new messages + let prev_confirmed_nonce_at_source = + strategy.latest_confirmed_nonces_at_source.back().unwrap().1; + strategy.latest_confirmed_nonces_at_source = + vec![(header_id(1), prev_confirmed_nonce_at_source - 1)].into_iter().collect(); + strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = + prev_confirmed_nonce_at_source - 1; + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=22), proof_parameters(false, 3))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_waits_for_confirmed_nonce_header_to_appear_on_target() { + // 1 delivery confirmation from target to source is still missing, so we may deliver + // reward confirmation with our message delivery transaction. But the problem is that + // the reward has been paid at header 2 && this header is still unknown to target node. + // + // => so we can't deliver more than 3 messages + let (mut state, mut strategy) = prepare_strategy(); + let prev_confirmed_nonce_at_source = + strategy.latest_confirmed_nonces_at_source.back().unwrap().1; + strategy.latest_confirmed_nonces_at_source = vec![ + (header_id(1), prev_confirmed_nonce_at_source - 1), + (header_id(2), prev_confirmed_nonce_at_source), + ] + .into_iter() + .collect(); + strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = + prev_confirmed_nonce_at_source - 1; + state.best_finalized_source_header_id_at_best_target = Some(header_id(1)); + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=22), proof_parameters(false, 3))) + ); + + // the same situation, but the header 2 is known to the target node, so we may deliver + // reward confirmation + let (mut state, mut strategy) = prepare_strategy(); + let prev_confirmed_nonce_at_source = + strategy.latest_confirmed_nonces_at_source.back().unwrap().1; + strategy.latest_confirmed_nonces_at_source = vec![ + (header_id(1), prev_confirmed_nonce_at_source - 1), + (header_id(2), prev_confirmed_nonce_at_source), + ] + .into_iter() + .collect(); + strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = + prev_confirmed_nonce_at_source - 1; + state.best_finalized_source_header_id_at_source = Some(header_id(2)); + state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=23), proof_parameters(true, 4))) + ); + } + + #[async_std::test] + async fn source_header_is_required_when_confirmations_are_required() { + // let's prepare situation when: + // - all messages [20; 23] have been generated at source block#1; + let (mut state, mut strategy) = prepare_strategy(); + // + // - messages [20; 21] have been delivered, but messages [11; 20] can't be delivered because + // of unrewarded relayers vector capacity; + strategy.max_unconfirmed_nonces_at_target = 2; + assert_eq!( + strategy.select_nonces_to_deliver(state.clone()).await, + Some(((20..=21), proof_parameters(false, 2))) + ); + strategy.finalized_target_nonces_updated( + TargetClientNonces { + latest_nonce: 21, + nonces_data: DeliveryRaceTargetNoncesData { + confirmed_nonce: 19, + unrewarded_relayers: UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + messages_in_oldest_entry: 2, + total_messages: 2, + last_delivered_nonce: 19, + }, + }, + }, + &mut state, + ); + assert_eq!(strategy.select_nonces_to_deliver(state).await, None); + // + // - messages [1; 10] receiving confirmation has been delivered at source block#2; + strategy.source_nonces_updated( + header_id(2), + SourceClientNonces { new_nonces: MessageDetailsMap::new(), confirmed_nonce: Some(21) }, + ); + // + // - so now we'll need to relay source block#11 to be able to accept messages [11; 20]. + assert_eq!(strategy.required_source_header_at_target(&header_id(1)), Some(header_id(2))); + } + + #[async_std::test] + async fn relayer_uses_flattened_view_of_the_source_queue_to_select_nonces() { + // Real scenario that has happened on test deployments: + // 1) relayer witnessed M1 at block 1 => it has separate entry in the `source_queue` + // 2) relayer witnessed M2 at block 2 => it has separate entry in the `source_queue` + // 3) if block 2 is known to the target node, then both M1 and M2 are selected for single + // delivery, even though weight(M1+M2) > larger than largest allowed weight + // + // This was happening because selector (`select_nonces_for_delivery_transaction`) has been + // called for every `source_queue` entry separately without preserving any context. + let (mut state, mut strategy) = prepare_strategy(); + let nonces = source_nonces(24..=25, 19, 0); + strategy.strategy.source_nonces_updated(header_id(2), nonces); + strategy.max_unrewarded_relayer_entries_at_target = 100; + strategy.max_unconfirmed_nonces_at_target = 100; + strategy.max_messages_in_single_batch = 5; + strategy.max_messages_weight_in_single_batch = Weight::from_ref_time(100); + strategy.max_messages_size_in_single_batch = 100; + state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); + + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=24), proof_parameters(false, 5))) + ); + } +} diff --git a/relays/messages/src/message_race_limits.rs b/relays/messages/src/message_race_limits.rs new file mode 100644 index 00000000000..a28d9ba63da --- /dev/null +++ b/relays/messages/src/message_race_limits.rs @@ -0,0 +1,200 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! enforcement strategy + +use num_traits::Zero; +use std::ops::Range; + +use bp_messages::{MessageNonce, Weight}; + +use crate::{ + message_lane::MessageLane, + message_lane_loop::{ + MessageDetails, MessageDetailsMap, SourceClient as MessageLaneSourceClient, + TargetClient as MessageLaneTargetClient, + }, + message_race_loop::NoncesRange, + message_race_strategy::SourceRangesQueue, + metrics::MessageLaneLoopMetrics, +}; + +/// Reference data for participating in relay +pub struct RelayReference< + P: MessageLane, + SourceClient: MessageLaneSourceClient

, + TargetClient: MessageLaneTargetClient

, +> { + /// The client that is connected to the message lane source node. + pub lane_source_client: SourceClient, + /// The client that is connected to the message lane target node. + pub lane_target_client: TargetClient, + /// Metrics reference. + pub metrics: Option, + /// Messages size summary + pub selected_size: u32, + + /// Hard check begin nonce + pub hard_selected_begin_nonce: MessageNonce, + + /// Index by all ready nonces + pub index: usize, + /// Current nonce + pub nonce: MessageNonce, + /// Current nonce details + pub details: MessageDetails, +} + +/// Relay reference data +pub struct RelayMessagesBatchReference< + P: MessageLane, + SourceClient: MessageLaneSourceClient

, + TargetClient: MessageLaneTargetClient

, +> { + /// Maximal number of relayed messages in single delivery transaction. + pub max_messages_in_this_batch: MessageNonce, + /// Maximal cumulative dispatch weight of relayed messages in single delivery transaction. + pub max_messages_weight_in_single_batch: Weight, + /// Maximal cumulative size of relayed messages in single delivery transaction. + pub max_messages_size_in_single_batch: u32, + /// The client that is connected to the message lane source node. + pub lane_source_client: SourceClient, + /// The client that is connected to the message lane target node. + pub lane_target_client: TargetClient, + /// Metrics reference. + pub metrics: Option, + /// Source queue. + pub nonces_queue: SourceRangesQueue< + P::SourceHeaderHash, + P::SourceHeaderNumber, + MessageDetailsMap, + >, + /// Source queue range + pub nonces_queue_range: Range, +} + +/// Limits of the message race transactions. +#[derive(Clone)] +pub struct MessageRaceLimits; + +impl MessageRaceLimits { + pub async fn decide< + P: MessageLane, + SourceClient: MessageLaneSourceClient

, + TargetClient: MessageLaneTargetClient

, + >( + reference: RelayMessagesBatchReference, + ) -> Option { + let mut hard_selected_count = 0; + + let mut selected_weight = Weight::zero(); + let mut selected_count: MessageNonce = 0; + + let hard_selected_begin_nonce = + reference.nonces_queue[reference.nonces_queue_range.start].1.begin(); + + // relay reference + let mut relay_reference = RelayReference { + lane_source_client: reference.lane_source_client.clone(), + lane_target_client: reference.lane_target_client.clone(), + metrics: reference.metrics.clone(), + + selected_size: 0, + + hard_selected_begin_nonce, + + index: 0, + nonce: 0, + details: MessageDetails { + dispatch_weight: Weight::zero(), + size: 0, + reward: P::SourceChainBalance::zero(), + }, + }; + + let all_ready_nonces = reference + .nonces_queue + .range(reference.nonces_queue_range.clone()) + .flat_map(|(_, ready_nonces)| ready_nonces.iter()) + .enumerate(); + for (index, (nonce, details)) in all_ready_nonces { + relay_reference.index = index; + relay_reference.nonce = *nonce; + relay_reference.details = *details; + + // Since we (hopefully) have some reserves in `max_messages_weight_in_single_batch` + // and `max_messages_size_in_single_batch`, we may still try to submit transaction + // with single message if message overflows these limits. The worst case would be if + // transaction will be rejected by the target runtime, but at least we have tried. + + // limit messages in the batch by weight + let new_selected_weight = match selected_weight.checked_add(&details.dispatch_weight) { + Some(new_selected_weight) + if new_selected_weight + .all_lte(reference.max_messages_weight_in_single_batch) => + new_selected_weight, + new_selected_weight if selected_count == 0 => { + log::warn!( + target: "bridge", + "Going to submit message delivery transaction with declared dispatch \ + weight {:?} that overflows maximal configured weight {}", + new_selected_weight, + reference.max_messages_weight_in_single_batch, + ); + new_selected_weight.unwrap_or(Weight::MAX) + }, + _ => break, + }; + + // limit messages in the batch by size + let new_selected_size = match relay_reference.selected_size.checked_add(details.size) { + Some(new_selected_size) + if new_selected_size <= reference.max_messages_size_in_single_batch => + new_selected_size, + new_selected_size if selected_count == 0 => { + log::warn!( + target: "bridge", + "Going to submit message delivery transaction with message \ + size {:?} that overflows maximal configured size {}", + new_selected_size, + reference.max_messages_size_in_single_batch, + ); + new_selected_size.unwrap_or(u32::MAX) + }, + _ => break, + }; + + // limit number of messages in the batch + let new_selected_count = selected_count + 1; + if new_selected_count > reference.max_messages_in_this_batch { + break + } + relay_reference.selected_size = new_selected_size; + + hard_selected_count = index + 1; + selected_weight = new_selected_weight; + selected_count = new_selected_count; + } + + if hard_selected_count != 0 { + let selected_max_nonce = + hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1; + Some(selected_max_nonce) + } else { + None + } + } +} diff --git a/relays/messages/src/message_race_loop.rs b/relays/messages/src/message_race_loop.rs new file mode 100644 index 00000000000..4f59b635ae6 --- /dev/null +++ b/relays/messages/src/message_race_loop.rs @@ -0,0 +1,663 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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. + +//! Loop that is serving single race within message lane. This could be +//! message delivery race, receiving confirmations race or processing +//! confirmations race. +//! +//! The idea of the race is simple - we have `nonce`-s on source and target +//! nodes. We're trying to prove that the source node has this nonce (and +//! associated data - like messages, lane state, etc) to the target node by +//! generating and submitting proof. + +use crate::message_lane_loop::{ClientState, NoncesSubmitArtifacts}; + +use async_trait::async_trait; +use bp_messages::MessageNonce; +use futures::{ + future::FutureExt, + stream::{FusedStream, StreamExt}, +}; +use relay_utils::{ + process_future_result, retry_backoff, FailedClient, MaybeConnectionError, + TrackedTransactionStatus, TransactionTracker, +}; +use std::{ + fmt::Debug, + ops::RangeInclusive, + time::{Duration, Instant}, +}; + +/// One of races within lane. +pub trait MessageRace { + /// Header id of the race source. + type SourceHeaderId: Debug + Clone + PartialEq; + /// Header id of the race source. + type TargetHeaderId: Debug + Clone + PartialEq; + + /// Message nonce used in the race. + type MessageNonce: Debug + Clone; + /// Proof that is generated and delivered in this race. + type Proof: Debug + Clone; + + /// Name of the race source. + fn source_name() -> String; + /// Name of the race target. + fn target_name() -> String; +} + +/// State of race source client. +type SourceClientState

= + ClientState<

::SourceHeaderId,

::TargetHeaderId>; + +/// State of race target client. +type TargetClientState

= + ClientState<

::TargetHeaderId,

::SourceHeaderId>; + +/// Inclusive nonces range. +pub trait NoncesRange: Debug + Sized { + /// Get begin of the range. + fn begin(&self) -> MessageNonce; + /// Get end of the range. + fn end(&self) -> MessageNonce; + /// Returns new range with current range nonces that are greater than the passed `nonce`. + /// If there are no such nonces, `None` is returned. + fn greater_than(self, nonce: MessageNonce) -> Option; +} + +/// Nonces on the race source client. +#[derive(Debug, Clone)] +pub struct SourceClientNonces { + /// New nonces range known to the client. `New` here means all nonces generated after + /// `prev_latest_nonce` passed to the `SourceClient::nonces` method. + pub new_nonces: NoncesRange, + /// The latest nonce that is confirmed to the bridged client. This nonce only makes + /// sense in some races. In other races it is `None`. + pub confirmed_nonce: Option, +} + +/// Nonces on the race target client. +#[derive(Debug, Clone)] +pub struct TargetClientNonces { + /// The latest nonce that is known to the target client. + pub latest_nonce: MessageNonce, + /// Additional data from target node that may be used by the race. + pub nonces_data: TargetNoncesData, +} + +/// One of message lane clients, which is source client for the race. +#[async_trait] +pub trait SourceClient { + /// Type of error these clients returns. + type Error: std::fmt::Debug + MaybeConnectionError; + /// Type of nonces range returned by the source client. + type NoncesRange: NoncesRange; + /// Additional proof parameters required to generate proof. + type ProofParameters; + + /// Return nonces that are known to the source client. + async fn nonces( + &self, + at_block: P::SourceHeaderId, + prev_latest_nonce: MessageNonce, + ) -> Result<(P::SourceHeaderId, SourceClientNonces), Self::Error>; + /// Generate proof for delivering to the target client. + async fn generate_proof( + &self, + at_block: P::SourceHeaderId, + nonces: RangeInclusive, + proof_parameters: Self::ProofParameters, + ) -> Result<(P::SourceHeaderId, RangeInclusive, P::Proof), Self::Error>; +} + +/// One of message lane clients, which is target client for the race. +#[async_trait] +pub trait TargetClient { + /// Type of error these clients returns. + type Error: std::fmt::Debug + MaybeConnectionError; + /// Type of the additional data from the target client, used by the race. + type TargetNoncesData: std::fmt::Debug; + /// Transaction tracker to track submitted transactions. + type TransactionTracker: TransactionTracker; + + /// Ask headers relay to relay finalized headers up to (and including) given header + /// from race source to race target. + async fn require_source_header(&self, id: P::SourceHeaderId); + + /// Return nonces that are known to the target client. + async fn nonces( + &self, + at_block: P::TargetHeaderId, + update_metrics: bool, + ) -> Result<(P::TargetHeaderId, TargetClientNonces), Self::Error>; + /// Submit proof to the target client. + async fn submit_proof( + &self, + generated_at_block: P::SourceHeaderId, + nonces: RangeInclusive, + proof: P::Proof, + ) -> Result, Self::Error>; +} + +/// Race strategy. +#[async_trait] +pub trait RaceStrategy: Debug { + /// Type of nonces range expected from the source client. + type SourceNoncesRange: NoncesRange; + /// Additional proof parameters required to generate proof. + type ProofParameters; + /// Additional data expected from the target client. + type TargetNoncesData; + + /// Should return true if nothing has to be synced. + fn is_empty(&self) -> bool; + /// Return id of source header that is required to be on target to continue synchronization. + fn required_source_header_at_target( + &self, + current_best: &SourceHeaderId, + ) -> Option; + /// Return the best nonce at source node. + /// + /// `Some` is returned only if we are sure that the value is greater or equal + /// than the result of `best_at_target`. + fn best_at_source(&self) -> Option; + /// Return the best nonce at target node. + /// + /// May return `None` if value is yet unknown. + fn best_at_target(&self) -> Option; + + /// Called when nonces are updated at source node of the race. + fn source_nonces_updated( + &mut self, + at_block: SourceHeaderId, + nonces: SourceClientNonces, + ); + /// Called when best nonces are updated at target node of the race. + fn best_target_nonces_updated( + &mut self, + nonces: TargetClientNonces, + race_state: &mut RaceState, + ); + /// Called when finalized nonces are updated at target node of the race. + fn finalized_target_nonces_updated( + &mut self, + nonces: TargetClientNonces, + race_state: &mut RaceState, + ); + /// Should return `Some(nonces)` if we need to deliver proof of `nonces` (and associated + /// data) from source to target node. + /// Additionally, parameters required to generate proof are returned. + async fn select_nonces_to_deliver( + &mut self, + race_state: RaceState, + ) -> Option<(RangeInclusive, Self::ProofParameters)>; +} + +/// State of the race. +#[derive(Debug, Clone)] +pub struct RaceState { + /// Best finalized source header id at the source client. + pub best_finalized_source_header_id_at_source: Option, + /// Best finalized source header id at the best block on the target + /// client (at the `best_finalized_source_header_id_at_best_target`). + pub best_finalized_source_header_id_at_best_target: Option, + /// The best header id at the target client. + pub best_target_header_id: Option, + /// Best finalized header id at the target client. + pub best_finalized_target_header_id: Option, + /// Range of nonces that we have selected to submit. + pub nonces_to_submit: Option<(SourceHeaderId, RangeInclusive, Proof)>, + /// Range of nonces that is currently submitted. + pub nonces_submitted: Option>, +} + +/// Run race loop until connection with target or source node is lost. +pub async fn run, TC: TargetClient

>( + race_source: SC, + race_source_updated: impl FusedStream>, + race_target: TC, + race_target_updated: impl FusedStream>, + mut strategy: impl RaceStrategy< + P::SourceHeaderId, + P::TargetHeaderId, + P::Proof, + SourceNoncesRange = SC::NoncesRange, + ProofParameters = SC::ProofParameters, + TargetNoncesData = TC::TargetNoncesData, + >, +) -> Result<(), FailedClient> { + let mut progress_context = Instant::now(); + let mut race_state = RaceState::default(); + + let mut source_retry_backoff = retry_backoff(); + let mut source_client_is_online = true; + let mut source_nonces_required = false; + let source_nonces = futures::future::Fuse::terminated(); + let source_generate_proof = futures::future::Fuse::terminated(); + let source_go_offline_future = futures::future::Fuse::terminated(); + + let mut target_retry_backoff = retry_backoff(); + let mut target_client_is_online = true; + let mut target_best_nonces_required = false; + let mut target_finalized_nonces_required = false; + let target_best_nonces = futures::future::Fuse::terminated(); + let target_finalized_nonces = futures::future::Fuse::terminated(); + let target_submit_proof = futures::future::Fuse::terminated(); + let target_tx_tracker = futures::future::Fuse::terminated(); + let target_go_offline_future = futures::future::Fuse::terminated(); + + futures::pin_mut!( + race_source_updated, + source_nonces, + source_generate_proof, + source_go_offline_future, + race_target_updated, + target_best_nonces, + target_finalized_nonces, + target_submit_proof, + target_tx_tracker, + target_go_offline_future, + ); + + loop { + futures::select! { + // when headers ids are updated + source_state = race_source_updated.next() => { + if let Some(source_state) = source_state { + let is_source_state_updated = race_state.best_finalized_source_header_id_at_source.as_ref() + != Some(&source_state.best_finalized_self); + if is_source_state_updated { + source_nonces_required = true; + race_state.best_finalized_source_header_id_at_source = Some(source_state.best_finalized_self); + } + } + }, + target_state = race_target_updated.next() => { + if let Some(target_state) = target_state { + let is_target_best_state_updated = race_state.best_target_header_id.as_ref() + != Some(&target_state.best_self); + + if is_target_best_state_updated { + target_best_nonces_required = true; + race_state.best_target_header_id = Some(target_state.best_self); + race_state.best_finalized_source_header_id_at_best_target + = Some(target_state.best_finalized_peer_at_best_self); + } + + let is_target_finalized_state_updated = race_state.best_finalized_target_header_id.as_ref() + != Some(&target_state.best_finalized_self); + if is_target_finalized_state_updated { + target_finalized_nonces_required = true; + race_state.best_finalized_target_header_id = Some(target_state.best_finalized_self); + } + } + }, + + // when nonces are updated + nonces = source_nonces => { + source_nonces_required = false; + + source_client_is_online = process_future_result( + nonces, + &mut source_retry_backoff, + |(at_block, nonces)| { + log::debug!( + target: "bridge", + "Received nonces from {}: {:?}", + P::source_name(), + nonces, + ); + + strategy.source_nonces_updated(at_block, nonces); + }, + &mut source_go_offline_future, + async_std::task::sleep, + || format!("Error retrieving nonces from {}", P::source_name()), + ).fail_if_connection_error(FailedClient::Source)?; + + // ask for more headers if we have nonces to deliver and required headers are missing + let required_source_header_id = race_state + .best_finalized_source_header_id_at_best_target + .as_ref() + .and_then(|best|strategy.required_source_header_at_target(best)); + if let Some(required_source_header_id) = required_source_header_id { + race_target.require_source_header(required_source_header_id).await; + } + }, + nonces = target_best_nonces => { + target_best_nonces_required = false; + + target_client_is_online = process_future_result( + nonces, + &mut target_retry_backoff, + |(_, nonces)| { + log::debug!( + target: "bridge", + "Received best nonces from {}: {:?}", + P::target_name(), + nonces, + ); + + strategy.best_target_nonces_updated(nonces, &mut race_state); + }, + &mut target_go_offline_future, + async_std::task::sleep, + || format!("Error retrieving best nonces from {}", P::target_name()), + ).fail_if_connection_error(FailedClient::Target)?; + }, + nonces = target_finalized_nonces => { + target_finalized_nonces_required = false; + + target_client_is_online = process_future_result( + nonces, + &mut target_retry_backoff, + |(_, nonces)| { + log::debug!( + target: "bridge", + "Received finalized nonces from {}: {:?}", + P::target_name(), + nonces, + ); + + strategy.finalized_target_nonces_updated(nonces, &mut race_state); + }, + &mut target_go_offline_future, + async_std::task::sleep, + || format!("Error retrieving finalized nonces from {}", P::target_name()), + ).fail_if_connection_error(FailedClient::Target)?; + }, + + // proof generation and submission + proof = source_generate_proof => { + source_client_is_online = process_future_result( + proof, + &mut source_retry_backoff, + |(at_block, nonces_range, proof)| { + log::debug!( + target: "bridge", + "Received proof for nonces in range {:?} from {}", + nonces_range, + P::source_name(), + ); + + race_state.nonces_to_submit = Some((at_block, nonces_range, proof)); + }, + &mut source_go_offline_future, + async_std::task::sleep, + || format!("Error generating proof at {}", P::source_name()), + ).fail_if_error(FailedClient::Source).map(|_| true)?; + }, + proof_submit_result = target_submit_proof => { + target_client_is_online = process_future_result( + proof_submit_result, + &mut target_retry_backoff, + |artifacts: NoncesSubmitArtifacts| { + log::debug!( + target: "bridge", + "Successfully submitted proof of nonces {:?} to {}", + artifacts.nonces, + P::target_name(), + ); + + race_state.nonces_to_submit = None; + race_state.nonces_submitted = Some(artifacts.nonces); + target_tx_tracker.set(artifacts.tx_tracker.wait().fuse()); + }, + &mut target_go_offline_future, + async_std::task::sleep, + || format!("Error submitting proof {}", P::target_name()), + ).fail_if_error(FailedClient::Target).map(|_| true)?; + }, + target_transaction_status = target_tx_tracker => { + match (target_transaction_status, race_state.nonces_submitted.as_ref()) { + (TrackedTransactionStatus::Finalized(at_block), Some(nonces_submitted)) => { + // our transaction has been mined, but was it successful or not? let's check the best + // nonce at the target node. + race_target.nonces(at_block, false) + .await + .map_err(|e| format!("failed to read nonces from target node: {e:?}")) + .and_then(|(_, nonces_at_target)| { + if nonces_at_target.latest_nonce < *nonces_submitted.end() { + Err(format!( + "best nonce at target after tx is {:?} and we've submitted {:?}", + nonces_at_target.latest_nonce, + nonces_submitted.end(), + )) + } else { + Ok(()) + } + }) + .map_err(|e| { + log::error!( + target: "bridge", + "{} -> {} race has stalled. Transaction failed: {}. Going to restart", + P::source_name(), + P::target_name(), + e, + ); + + FailedClient::Both + })?; + }, + (TrackedTransactionStatus::Lost, _) => { + log::warn!( + target: "bridge", + "{} -> {} race has stalled. State: {:?}. Strategy: {:?}", + P::source_name(), + P::target_name(), + race_state, + strategy, + ); + + return Err(FailedClient::Both); + }, + _ => (), + } + }, + + // when we're ready to retry request + _ = source_go_offline_future => { + source_client_is_online = true; + }, + _ = target_go_offline_future => { + target_client_is_online = true; + }, + } + + progress_context = print_race_progress::(progress_context, &strategy); + + if source_client_is_online { + source_client_is_online = false; + + let nonces_to_deliver = + select_nonces_to_deliver(race_state.clone(), &mut strategy).await; + let best_at_source = strategy.best_at_source(); + + if let Some((at_block, nonces_range, proof_parameters)) = nonces_to_deliver { + log::debug!( + target: "bridge", + "Asking {} to prove nonces in range {:?} at block {:?}", + P::source_name(), + nonces_range, + at_block, + ); + source_generate_proof.set( + race_source.generate_proof(at_block, nonces_range, proof_parameters).fuse(), + ); + } else if source_nonces_required && best_at_source.is_some() { + log::debug!(target: "bridge", "Asking {} about message nonces", P::source_name()); + let at_block = race_state + .best_finalized_source_header_id_at_source + .as_ref() + .expect( + "source_nonces_required is only true when\ + best_finalized_source_header_id_at_source is Some; qed", + ) + .clone(); + source_nonces.set( + race_source + .nonces(at_block, best_at_source.expect("guaranteed by if condition; qed")) + .fuse(), + ); + } else { + source_client_is_online = true; + } + } + + if target_client_is_online { + target_client_is_online = false; + + if let Some((at_block, nonces_range, proof)) = race_state.nonces_to_submit.as_ref() { + log::debug!( + target: "bridge", + "Going to submit proof of messages in range {:?} to {} node", + nonces_range, + P::target_name(), + ); + target_submit_proof.set( + race_target + .submit_proof(at_block.clone(), nonces_range.clone(), proof.clone()) + .fuse(), + ); + } else if target_best_nonces_required { + log::debug!(target: "bridge", "Asking {} about best message nonces", P::target_name()); + let at_block = race_state + .best_target_header_id + .as_ref() + .expect("target_best_nonces_required is only true when best_target_header_id is Some; qed") + .clone(); + target_best_nonces.set(race_target.nonces(at_block, false).fuse()); + } else if target_finalized_nonces_required { + log::debug!(target: "bridge", "Asking {} about finalized message nonces", P::target_name()); + let at_block = race_state + .best_finalized_target_header_id + .as_ref() + .expect( + "target_finalized_nonces_required is only true when\ + best_finalized_target_header_id is Some; qed", + ) + .clone(); + target_finalized_nonces.set(race_target.nonces(at_block, true).fuse()); + } else { + target_client_is_online = true; + } + } + } +} + +impl Default + for RaceState +{ + fn default() -> Self { + RaceState { + best_finalized_source_header_id_at_source: None, + best_finalized_source_header_id_at_best_target: None, + best_target_header_id: None, + best_finalized_target_header_id: None, + nonces_to_submit: None, + nonces_submitted: None, + } + } +} + +/// Print race progress. +fn print_race_progress(prev_time: Instant, strategy: &S) -> Instant +where + P: MessageRace, + S: RaceStrategy, +{ + let now_time = Instant::now(); + + let need_update = now_time.saturating_duration_since(prev_time) > Duration::from_secs(10); + if !need_update { + return prev_time + } + + let now_best_nonce_at_source = strategy.best_at_source(); + let now_best_nonce_at_target = strategy.best_at_target(); + log::info!( + target: "bridge", + "Synced {:?} of {:?} nonces in {} -> {} race", + now_best_nonce_at_target, + now_best_nonce_at_source, + P::source_name(), + P::target_name(), + ); + now_time +} + +async fn select_nonces_to_deliver( + race_state: RaceState, + strategy: &mut Strategy, +) -> Option<(SourceHeaderId, RangeInclusive, Strategy::ProofParameters)> +where + SourceHeaderId: Clone, + Strategy: RaceStrategy, +{ + let best_finalized_source_header_id_at_best_target = + race_state.best_finalized_source_header_id_at_best_target.clone()?; + strategy + .select_nonces_to_deliver(race_state) + .await + .map(|(nonces_range, proof_parameters)| { + (best_finalized_source_header_id_at_best_target, nonces_range, proof_parameters) + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::message_race_strategy::BasicStrategy; + use relay_utils::HeaderId; + + #[async_std::test] + async fn proof_is_generated_at_best_block_known_to_target_node() { + const GENERATED_AT: u64 = 6; + const BEST_AT_SOURCE: u64 = 10; + const BEST_AT_TARGET: u64 = 8; + + // target node only knows about source' BEST_AT_TARGET block + // source node has BEST_AT_SOURCE > BEST_AT_TARGET block + let mut race_state = RaceState::<_, _, ()> { + best_finalized_source_header_id_at_source: Some(HeaderId( + BEST_AT_SOURCE, + BEST_AT_SOURCE, + )), + best_finalized_source_header_id_at_best_target: Some(HeaderId( + BEST_AT_TARGET, + BEST_AT_TARGET, + )), + best_target_header_id: Some(HeaderId(0, 0)), + best_finalized_target_header_id: Some(HeaderId(0, 0)), + nonces_to_submit: None, + nonces_submitted: None, + }; + + // we have some nonces to deliver and they're generated at GENERATED_AT < BEST_AT_SOURCE + let mut strategy = BasicStrategy::new(); + strategy.source_nonces_updated( + HeaderId(GENERATED_AT, GENERATED_AT), + SourceClientNonces { new_nonces: 0..=10, confirmed_nonce: None }, + ); + strategy.best_target_nonces_updated( + TargetClientNonces { latest_nonce: 5u64, nonces_data: () }, + &mut race_state, + ); + + // the proof will be generated on source, but using BEST_AT_TARGET block + assert_eq!( + select_nonces_to_deliver(race_state, &mut strategy).await, + Some((HeaderId(BEST_AT_TARGET, BEST_AT_TARGET), 6..=10, (),)) + ); + } +} diff --git a/relays/messages/src/message_race_receiving.rs b/relays/messages/src/message_race_receiving.rs new file mode 100644 index 00000000000..c3d65d0e86a --- /dev/null +++ b/relays/messages/src/message_race_receiving.rs @@ -0,0 +1,228 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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. + +//! Message receiving race delivers proof-of-messages-delivery from "lane.target" to "lane.source". + +use crate::{ + message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, + message_lane_loop::{ + NoncesSubmitArtifacts, SourceClient as MessageLaneSourceClient, SourceClientState, + TargetClient as MessageLaneTargetClient, TargetClientState, + }, + message_race_loop::{ + MessageRace, NoncesRange, SourceClient, SourceClientNonces, TargetClient, + TargetClientNonces, + }, + message_race_strategy::BasicStrategy, + metrics::MessageLaneLoopMetrics, +}; + +use async_trait::async_trait; +use bp_messages::MessageNonce; +use futures::stream::FusedStream; +use relay_utils::FailedClient; +use std::{marker::PhantomData, ops::RangeInclusive}; + +/// Message receiving confirmations delivery strategy. +type ReceivingConfirmationsBasicStrategy

= BasicStrategy< +

::TargetHeaderNumber, +

::TargetHeaderHash, +

::SourceHeaderNumber, +

::SourceHeaderHash, + RangeInclusive, +

::MessagesReceivingProof, +>; + +/// Run receiving confirmations race. +pub async fn run( + source_client: impl MessageLaneSourceClient

, + source_state_updates: impl FusedStream>, + target_client: impl MessageLaneTargetClient

, + target_state_updates: impl FusedStream>, + metrics_msg: Option, +) -> Result<(), FailedClient> { + crate::message_race_loop::run( + ReceivingConfirmationsRaceSource { + client: target_client, + metrics_msg: metrics_msg.clone(), + _phantom: Default::default(), + }, + target_state_updates, + ReceivingConfirmationsRaceTarget { + client: source_client, + metrics_msg, + _phantom: Default::default(), + }, + source_state_updates, + ReceivingConfirmationsBasicStrategy::

::new(), + ) + .await +} + +/// Messages receiving confirmations race. +struct ReceivingConfirmationsRace

(std::marker::PhantomData

); + +impl MessageRace for ReceivingConfirmationsRace

{ + type SourceHeaderId = TargetHeaderIdOf

; + type TargetHeaderId = SourceHeaderIdOf

; + + type MessageNonce = MessageNonce; + type Proof = P::MessagesReceivingProof; + + fn source_name() -> String { + format!("{}::ReceivingConfirmationsDelivery", P::TARGET_NAME) + } + + fn target_name() -> String { + format!("{}::ReceivingConfirmationsDelivery", P::SOURCE_NAME) + } +} + +/// Message receiving confirmations race source, which is a target of the lane. +struct ReceivingConfirmationsRaceSource { + client: C, + metrics_msg: Option, + _phantom: PhantomData

, +} + +#[async_trait] +impl SourceClient> for ReceivingConfirmationsRaceSource +where + P: MessageLane, + C: MessageLaneTargetClient

, +{ + type Error = C::Error; + type NoncesRange = RangeInclusive; + type ProofParameters = (); + + async fn nonces( + &self, + at_block: TargetHeaderIdOf

, + prev_latest_nonce: MessageNonce, + ) -> Result<(TargetHeaderIdOf

, SourceClientNonces), Self::Error> { + let (at_block, latest_received_nonce) = self.client.latest_received_nonce(at_block).await?; + if let Some(metrics_msg) = self.metrics_msg.as_ref() { + metrics_msg.update_target_latest_received_nonce::

(latest_received_nonce); + } + Ok(( + at_block, + SourceClientNonces { + new_nonces: prev_latest_nonce + 1..=latest_received_nonce, + confirmed_nonce: None, + }, + )) + } + + #[allow(clippy::unit_arg)] + async fn generate_proof( + &self, + at_block: TargetHeaderIdOf

, + nonces: RangeInclusive, + _proof_parameters: Self::ProofParameters, + ) -> Result< + (TargetHeaderIdOf

, RangeInclusive, P::MessagesReceivingProof), + Self::Error, + > { + self.client + .prove_messages_receiving(at_block) + .await + .map(|(at_block, proof)| (at_block, nonces, proof)) + } +} + +/// Message receiving confirmations race target, which is a source of the lane. +struct ReceivingConfirmationsRaceTarget { + client: C, + metrics_msg: Option, + _phantom: PhantomData

, +} + +#[async_trait] +impl TargetClient> for ReceivingConfirmationsRaceTarget +where + P: MessageLane, + C: MessageLaneSourceClient

, +{ + type Error = C::Error; + type TargetNoncesData = (); + type TransactionTracker = C::TransactionTracker; + + async fn require_source_header(&self, id: TargetHeaderIdOf

) { + self.client.require_target_header_on_source(id).await + } + + async fn nonces( + &self, + at_block: SourceHeaderIdOf

, + update_metrics: bool, + ) -> Result<(SourceHeaderIdOf

, TargetClientNonces<()>), Self::Error> { + let (at_block, latest_confirmed_nonce) = + self.client.latest_confirmed_received_nonce(at_block).await?; + if update_metrics { + if let Some(metrics_msg) = self.metrics_msg.as_ref() { + metrics_msg.update_source_latest_confirmed_nonce::

(latest_confirmed_nonce); + } + } + Ok((at_block, TargetClientNonces { latest_nonce: latest_confirmed_nonce, nonces_data: () })) + } + + async fn submit_proof( + &self, + generated_at_block: TargetHeaderIdOf

, + nonces: RangeInclusive, + proof: P::MessagesReceivingProof, + ) -> Result, Self::Error> { + let tx_tracker = + self.client.submit_messages_receiving_proof(generated_at_block, proof).await?; + Ok(NoncesSubmitArtifacts { nonces, tx_tracker }) + } +} + +impl NoncesRange for RangeInclusive { + fn begin(&self) -> MessageNonce { + *RangeInclusive::::start(self) + } + + fn end(&self) -> MessageNonce { + *RangeInclusive::::end(self) + } + + fn greater_than(self, nonce: MessageNonce) -> Option { + let next_nonce = nonce + 1; + let end = *self.end(); + if next_nonce > end { + None + } else { + Some(std::cmp::max(self.begin(), next_nonce)..=end) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn range_inclusive_works_as_nonces_range() { + let range = 20..=30; + + assert_eq!(NoncesRange::begin(&range), 20); + assert_eq!(NoncesRange::end(&range), 30); + assert_eq!(range.clone().greater_than(10), Some(20..=30)); + assert_eq!(range.clone().greater_than(19), Some(20..=30)); + assert_eq!(range.clone().greater_than(20), Some(21..=30)); + assert_eq!(range.clone().greater_than(25), Some(26..=30)); + assert_eq!(range.clone().greater_than(29), Some(30..=30)); + assert_eq!(range.greater_than(30), None); + } +} diff --git a/relays/messages/src/message_race_strategy.rs b/relays/messages/src/message_race_strategy.rs new file mode 100644 index 00000000000..9b9091b979f --- /dev/null +++ b/relays/messages/src/message_race_strategy.rs @@ -0,0 +1,517 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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. + +//! Basic delivery strategy. The strategy selects nonces if: +//! +//! 1) there are more nonces on the source side than on the target side; +//! 2) new nonces may be proved to target node (i.e. they have appeared at the +//! block, which is known to the target node). + +use crate::message_race_loop::{ + NoncesRange, RaceState, RaceStrategy, SourceClientNonces, TargetClientNonces, +}; + +use async_trait::async_trait; +use bp_messages::MessageNonce; +use relay_utils::HeaderId; +use std::{collections::VecDeque, fmt::Debug, marker::PhantomData, ops::RangeInclusive}; + +/// Queue of nonces known to the source node. +pub type SourceRangesQueue = + VecDeque<(HeaderId, SourceNoncesRange)>; + +/// Nonces delivery strategy. +#[derive(Debug)] +pub struct BasicStrategy< + SourceHeaderNumber, + SourceHeaderHash, + TargetHeaderNumber, + TargetHeaderHash, + SourceNoncesRange, + Proof, +> { + /// All queued nonces. + source_queue: SourceRangesQueue, + /// The best nonce known to target node (at its best block). `None` if it has not been received + /// yet. + best_target_nonce: Option, + /// Unused generic types dump. + _phantom: PhantomData<(TargetHeaderNumber, TargetHeaderHash, Proof)>, +} + +impl< + SourceHeaderNumber, + SourceHeaderHash, + TargetHeaderNumber, + TargetHeaderHash, + SourceNoncesRange, + Proof, + > + BasicStrategy< + SourceHeaderNumber, + SourceHeaderHash, + TargetHeaderNumber, + TargetHeaderHash, + SourceNoncesRange, + Proof, + > where + SourceHeaderHash: Clone, + SourceHeaderNumber: Clone + Ord, + SourceNoncesRange: NoncesRange, +{ + /// Create new delivery strategy. + pub fn new() -> Self { + BasicStrategy { + source_queue: VecDeque::new(), + best_target_nonce: None, + _phantom: Default::default(), + } + } + + /// Reference to source queue. + pub(crate) fn source_queue( + &self, + ) -> &VecDeque<(HeaderId, SourceNoncesRange)> { + &self.source_queue + } + + /// Mutable reference to source queue to use in tests. + #[cfg(test)] + pub(crate) fn source_queue_mut( + &mut self, + ) -> &mut VecDeque<(HeaderId, SourceNoncesRange)> { + &mut self.source_queue + } + + /// Returns index of the latest source queue entry, that may be delivered to the target node. + /// + /// Returns `None` if no entries may be delivered. All entries before and including the + /// `Some(_)` index are guaranteed to be witnessed at source blocks that are known to be + /// finalized at the target node. + pub fn maximal_available_source_queue_index( + &self, + race_state: RaceState< + HeaderId, + HeaderId, + Proof, + >, + ) -> Option { + // if we do not know best nonce at target node, we can't select anything + let _ = self.best_target_nonce?; + + // if we have already selected nonces that we want to submit, do nothing + if race_state.nonces_to_submit.is_some() { + return None + } + + // if we already submitted some nonces, do nothing + if race_state.nonces_submitted.is_some() { + return None + } + + // 1) we want to deliver all nonces, starting from `target_nonce + 1` + // 2) we can't deliver new nonce until header, that has emitted this nonce, is finalized + // by target client + // 3) selector is used for more complicated logic + // + // => let's first select range of entries inside deque that are already finalized at + // the target client and pass this range to the selector + let best_header_at_target = race_state.best_finalized_source_header_id_at_best_target?; + self.source_queue + .iter() + .enumerate() + .take_while(|(_, (queued_at, _))| queued_at.0 <= best_header_at_target.0) + .map(|(index, _)| index) + .last() + } + + /// Remove all nonces that are less than or equal to given nonce from the source queue. + pub fn remove_le_nonces_from_source_queue(&mut self, nonce: MessageNonce) { + while let Some((queued_at, queued_range)) = self.source_queue.pop_front() { + if let Some(range_to_requeue) = queued_range.greater_than(nonce) { + self.source_queue.push_front((queued_at, range_to_requeue)); + break + } + } + } +} + +#[async_trait] +impl< + SourceHeaderNumber, + SourceHeaderHash, + TargetHeaderNumber, + TargetHeaderHash, + SourceNoncesRange, + Proof, + > + RaceStrategy< + HeaderId, + HeaderId, + Proof, + > + for BasicStrategy< + SourceHeaderNumber, + SourceHeaderHash, + TargetHeaderNumber, + TargetHeaderHash, + SourceNoncesRange, + Proof, + > where + SourceHeaderHash: Clone + Debug + Send, + SourceHeaderNumber: Clone + Ord + Debug + Send, + SourceNoncesRange: NoncesRange + Debug + Send, + TargetHeaderHash: Debug + Send, + TargetHeaderNumber: Debug + Send, + Proof: Debug + Send, +{ + type SourceNoncesRange = SourceNoncesRange; + type ProofParameters = (); + type TargetNoncesData = (); + + fn is_empty(&self) -> bool { + self.source_queue.is_empty() + } + + fn required_source_header_at_target( + &self, + current_best: &HeaderId, + ) -> Option> { + self.source_queue + .back() + .and_then(|(h, _)| if h.0 > current_best.0 { Some(h.clone()) } else { None }) + } + + fn best_at_source(&self) -> Option { + let best_in_queue = self.source_queue.back().map(|(_, range)| range.end()); + match (best_in_queue, self.best_target_nonce) { + (Some(best_in_queue), Some(best_target_nonce)) if best_in_queue > best_target_nonce => + Some(best_in_queue), + (_, Some(best_target_nonce)) => Some(best_target_nonce), + (_, None) => None, + } + } + + fn best_at_target(&self) -> Option { + self.best_target_nonce + } + + fn source_nonces_updated( + &mut self, + at_block: HeaderId, + nonces: SourceClientNonces, + ) { + let best_in_queue = self + .source_queue + .back() + .map(|(_, range)| range.end()) + .or(self.best_target_nonce) + .unwrap_or_default(); + self.source_queue.extend( + nonces + .new_nonces + .greater_than(best_in_queue) + .into_iter() + .map(move |range| (at_block.clone(), range)), + ) + } + + fn best_target_nonces_updated( + &mut self, + nonces: TargetClientNonces<()>, + race_state: &mut RaceState< + HeaderId, + HeaderId, + Proof, + >, + ) { + let nonce = nonces.latest_nonce; + + if let Some(best_target_nonce) = self.best_target_nonce { + if nonce < best_target_nonce { + return + } + } + + while let Some(true) = self.source_queue.front().map(|(_, range)| range.begin() <= nonce) { + let maybe_subrange = self.source_queue.pop_front().and_then(|(at_block, range)| { + range.greater_than(nonce).map(|subrange| (at_block, subrange)) + }); + if let Some((at_block, subrange)) = maybe_subrange { + self.source_queue.push_front((at_block, subrange)); + break + } + } + + let need_to_select_new_nonces = race_state + .nonces_to_submit + .as_ref() + .map(|(_, nonces, _)| *nonces.end() <= nonce) + .unwrap_or(false); + if need_to_select_new_nonces { + race_state.nonces_to_submit = None; + } + + let need_new_nonces_to_submit = race_state + .nonces_submitted + .as_ref() + .map(|nonces| *nonces.end() <= nonce) + .unwrap_or(false); + if need_new_nonces_to_submit { + race_state.nonces_submitted = None; + } + + self.best_target_nonce = + Some(std::cmp::max(self.best_target_nonce.unwrap_or(nonces.latest_nonce), nonce)); + } + + fn finalized_target_nonces_updated( + &mut self, + nonces: TargetClientNonces<()>, + _race_state: &mut RaceState< + HeaderId, + HeaderId, + Proof, + >, + ) { + self.best_target_nonce = Some(std::cmp::max( + self.best_target_nonce.unwrap_or(nonces.latest_nonce), + nonces.latest_nonce, + )); + } + + async fn select_nonces_to_deliver( + &mut self, + race_state: RaceState< + HeaderId, + HeaderId, + Proof, + >, + ) -> Option<(RangeInclusive, Self::ProofParameters)> { + let maximal_source_queue_index = self.maximal_available_source_queue_index(race_state)?; + let range_begin = self.source_queue[0].1.begin(); + let range_end = self.source_queue[maximal_source_queue_index].1.end(); + self.remove_le_nonces_from_source_queue(range_end); + Some((range_begin..=range_end, ())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + message_lane::MessageLane, + message_lane_loop::tests::{ + header_id, TestMessageLane, TestMessagesProof, TestSourceHeaderHash, + TestSourceHeaderNumber, + }, + }; + + type SourceNoncesRange = RangeInclusive; + + type BasicStrategy

= super::BasicStrategy< +

::SourceHeaderNumber, +

::SourceHeaderHash, +

::TargetHeaderNumber, +

::TargetHeaderHash, + SourceNoncesRange, +

::MessagesProof, + >; + + fn source_nonces(new_nonces: SourceNoncesRange) -> SourceClientNonces { + SourceClientNonces { new_nonces, confirmed_nonce: None } + } + + fn target_nonces(latest_nonce: MessageNonce) -> TargetClientNonces<()> { + TargetClientNonces { latest_nonce, nonces_data: () } + } + + #[test] + fn strategy_is_empty_works() { + let mut strategy = BasicStrategy::::new(); + assert!(strategy.is_empty()); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=1)); + assert!(!strategy.is_empty()); + } + + #[test] + fn best_at_source_is_never_lower_than_target_nonce() { + let mut strategy = BasicStrategy::::new(); + assert_eq!(strategy.best_at_source(), None); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); + assert_eq!(strategy.best_at_source(), None); + strategy.best_target_nonces_updated(target_nonces(10), &mut Default::default()); + assert_eq!(strategy.source_queue, vec![]); + assert_eq!(strategy.best_at_source(), Some(10)); + } + + #[test] + fn source_nonce_is_never_lower_than_known_target_nonce() { + let mut strategy = BasicStrategy::::new(); + strategy.best_target_nonces_updated(target_nonces(10), &mut Default::default()); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); + assert_eq!(strategy.source_queue, vec![]); + } + + #[test] + fn source_nonce_is_never_lower_than_latest_known_source_nonce() { + let mut strategy = BasicStrategy::::new(); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); + strategy.source_nonces_updated(header_id(2), source_nonces(1..=3)); + strategy.source_nonces_updated(header_id(2), source_nonces(1..=5)); + assert_eq!(strategy.source_queue, vec![(header_id(1), 1..=5)]); + } + + #[test] + fn target_nonce_is_never_lower_than_latest_known_target_nonce() { + let mut strategy = BasicStrategy::::new(); + assert_eq!(strategy.best_target_nonce, None); + strategy.best_target_nonces_updated(target_nonces(10), &mut Default::default()); + assert_eq!(strategy.best_target_nonce, Some(10)); + strategy.best_target_nonces_updated(target_nonces(5), &mut Default::default()); + assert_eq!(strategy.best_target_nonce, Some(10)); + } + + #[test] + fn updated_target_nonce_removes_queued_entries() { + let mut strategy = BasicStrategy::::new(); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); + strategy.source_nonces_updated(header_id(2), source_nonces(6..=10)); + strategy.source_nonces_updated(header_id(3), source_nonces(11..=15)); + strategy.source_nonces_updated(header_id(4), source_nonces(16..=20)); + strategy.best_target_nonces_updated(target_nonces(15), &mut Default::default()); + assert_eq!(strategy.source_queue, vec![(header_id(4), 16..=20)]); + strategy.best_target_nonces_updated(target_nonces(17), &mut Default::default()); + assert_eq!(strategy.source_queue, vec![(header_id(4), 18..=20)]); + } + + #[test] + fn selected_nonces_are_dropped_on_target_nonce_update() { + let mut state = RaceState::default(); + let mut strategy = BasicStrategy::::new(); + state.nonces_to_submit = Some((header_id(1), 5..=10, (5..=10, None))); + strategy.best_target_nonces_updated(target_nonces(7), &mut state); + assert!(state.nonces_to_submit.is_some()); + strategy.best_target_nonces_updated(target_nonces(10), &mut state); + assert!(state.nonces_to_submit.is_none()); + } + + #[test] + fn submitted_nonces_are_dropped_on_target_nonce_update() { + let mut state = RaceState::default(); + let mut strategy = BasicStrategy::::new(); + state.nonces_submitted = Some(5..=10); + strategy.best_target_nonces_updated(target_nonces(7), &mut state); + assert!(state.nonces_submitted.is_some()); + strategy.best_target_nonces_updated(target_nonces(10), &mut state); + assert!(state.nonces_submitted.is_none()); + } + + #[async_std::test] + async fn nothing_is_selected_if_something_is_already_selected() { + let mut state = RaceState::default(); + let mut strategy = BasicStrategy::::new(); + state.nonces_to_submit = Some((header_id(1), 1..=10, (1..=10, None))); + strategy.best_target_nonces_updated(target_nonces(0), &mut state); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=10)); + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); + } + + #[async_std::test] + async fn nothing_is_selected_if_something_is_already_submitted() { + let mut state = RaceState::default(); + let mut strategy = BasicStrategy::::new(); + state.nonces_submitted = Some(1..=10); + strategy.best_target_nonces_updated(target_nonces(0), &mut state); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=10)); + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); + } + + #[async_std::test] + async fn select_nonces_to_deliver_works() { + let mut state = RaceState::<_, _, TestMessagesProof>::default(); + let mut strategy = BasicStrategy::::new(); + strategy.best_target_nonces_updated(target_nonces(0), &mut state); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=1)); + strategy.source_nonces_updated(header_id(2), source_nonces(2..=2)); + strategy.source_nonces_updated(header_id(3), source_nonces(3..=6)); + strategy.source_nonces_updated(header_id(5), source_nonces(7..=8)); + + state.best_finalized_source_header_id_at_best_target = Some(header_id(4)); + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((1..=6, ()))); + strategy.best_target_nonces_updated(target_nonces(6), &mut state); + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); + + state.best_finalized_source_header_id_at_best_target = Some(header_id(5)); + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((7..=8, ()))); + strategy.best_target_nonces_updated(target_nonces(8), &mut state); + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); + } + + #[test] + fn maximal_available_source_queue_index_works() { + let mut state = RaceState::<_, _, TestMessagesProof>::default(); + let mut strategy = BasicStrategy::::new(); + strategy.best_target_nonces_updated(target_nonces(0), &mut state); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=3)); + strategy.source_nonces_updated(header_id(2), source_nonces(4..=6)); + strategy.source_nonces_updated(header_id(3), source_nonces(7..=9)); + + state.best_finalized_source_header_id_at_best_target = Some(header_id(0)); + assert_eq!(strategy.maximal_available_source_queue_index(state.clone()), None); + + state.best_finalized_source_header_id_at_best_target = Some(header_id(1)); + assert_eq!(strategy.maximal_available_source_queue_index(state.clone()), Some(0)); + + state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); + assert_eq!(strategy.maximal_available_source_queue_index(state.clone()), Some(1)); + + state.best_finalized_source_header_id_at_best_target = Some(header_id(3)); + assert_eq!(strategy.maximal_available_source_queue_index(state.clone()), Some(2)); + + state.best_finalized_source_header_id_at_best_target = Some(header_id(4)); + assert_eq!(strategy.maximal_available_source_queue_index(state), Some(2)); + } + + #[test] + fn remove_le_nonces_from_source_queue_works() { + let mut state = RaceState::<_, _, TestMessagesProof>::default(); + let mut strategy = BasicStrategy::::new(); + strategy.best_target_nonces_updated(target_nonces(0), &mut state); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=3)); + strategy.source_nonces_updated(header_id(2), source_nonces(4..=6)); + strategy.source_nonces_updated(header_id(3), source_nonces(7..=9)); + + fn source_queue_nonces( + source_queue: &SourceRangesQueue< + TestSourceHeaderHash, + TestSourceHeaderNumber, + SourceNoncesRange, + >, + ) -> Vec { + source_queue.iter().flat_map(|(_, range)| range.clone()).collect() + } + + strategy.remove_le_nonces_from_source_queue(1); + assert_eq!(source_queue_nonces(&strategy.source_queue), vec![2, 3, 4, 5, 6, 7, 8, 9],); + + strategy.remove_le_nonces_from_source_queue(5); + assert_eq!(source_queue_nonces(&strategy.source_queue), vec![6, 7, 8, 9],); + + strategy.remove_le_nonces_from_source_queue(9); + assert_eq!(source_queue_nonces(&strategy.source_queue), Vec::::new(),); + + strategy.remove_le_nonces_from_source_queue(100); + assert_eq!(source_queue_nonces(&strategy.source_queue), Vec::::new(),); + } +} diff --git a/relays/messages/src/metrics.rs b/relays/messages/src/metrics.rs new file mode 100644 index 00000000000..4decb7e092e --- /dev/null +++ b/relays/messages/src/metrics.rs @@ -0,0 +1,139 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Metrics for message lane relay loop. + +use crate::{ + message_lane::MessageLane, + message_lane_loop::{SourceClientState, TargetClientState}, +}; + +use bp_messages::MessageNonce; +use finality_relay::SyncLoopMetrics; +use relay_utils::metrics::{ + metric_name, register, GaugeVec, Metric, Opts, PrometheusError, Registry, U64, +}; + +/// Message lane relay metrics. +/// +/// Cloning only clones references. +#[derive(Clone)] +pub struct MessageLaneLoopMetrics { + /// Best finalized block numbers - "source", "source_at_target", "target_at_source". + source_to_target_finality_metrics: SyncLoopMetrics, + /// Best finalized block numbers - "source", "target", "source_at_target", "target_at_source". + target_to_source_finality_metrics: SyncLoopMetrics, + /// Lane state nonces: "source_latest_generated", "source_latest_confirmed", + /// "target_latest_received", "target_latest_confirmed". + lane_state_nonces: GaugeVec, +} + +impl MessageLaneLoopMetrics { + /// Create and register messages loop metrics. + pub fn new(prefix: Option<&str>) -> Result { + Ok(MessageLaneLoopMetrics { + source_to_target_finality_metrics: SyncLoopMetrics::new( + prefix, + "source", + "source_at_target", + )?, + target_to_source_finality_metrics: SyncLoopMetrics::new( + prefix, + "target", + "target_at_source", + )?, + lane_state_nonces: GaugeVec::new( + Opts::new(metric_name(prefix, "lane_state_nonces"), "Nonces of the lane state"), + &["type"], + )?, + }) + } + + /// Update source client state metrics. + pub fn update_source_state(&self, source_client_state: SourceClientState

) { + self.source_to_target_finality_metrics + .update_best_block_at_source(source_client_state.best_self.0.into()); + self.target_to_source_finality_metrics.update_best_block_at_target( + source_client_state.best_finalized_peer_at_best_self.0.into(), + ); + self.target_to_source_finality_metrics.update_using_same_fork( + source_client_state.best_finalized_peer_at_best_self.1 == + source_client_state.actual_best_finalized_peer_at_best_self.1, + ); + } + + /// Update target client state metrics. + pub fn update_target_state(&self, target_client_state: TargetClientState

) { + self.target_to_source_finality_metrics + .update_best_block_at_source(target_client_state.best_self.0.into()); + self.source_to_target_finality_metrics.update_best_block_at_target( + target_client_state.best_finalized_peer_at_best_self.0.into(), + ); + self.source_to_target_finality_metrics.update_using_same_fork( + target_client_state.best_finalized_peer_at_best_self.1 == + target_client_state.actual_best_finalized_peer_at_best_self.1, + ); + } + + /// Update latest generated nonce at source. + pub fn update_source_latest_generated_nonce( + &self, + source_latest_generated_nonce: MessageNonce, + ) { + self.lane_state_nonces + .with_label_values(&["source_latest_generated"]) + .set(source_latest_generated_nonce); + } + + /// Update the latest confirmed nonce at source. + pub fn update_source_latest_confirmed_nonce( + &self, + source_latest_confirmed_nonce: MessageNonce, + ) { + self.lane_state_nonces + .with_label_values(&["source_latest_confirmed"]) + .set(source_latest_confirmed_nonce); + } + + /// Update the latest received nonce at target. + pub fn update_target_latest_received_nonce( + &self, + target_latest_generated_nonce: MessageNonce, + ) { + self.lane_state_nonces + .with_label_values(&["target_latest_received"]) + .set(target_latest_generated_nonce); + } + + /// Update the latest confirmed nonce at target. + pub fn update_target_latest_confirmed_nonce( + &self, + target_latest_confirmed_nonce: MessageNonce, + ) { + self.lane_state_nonces + .with_label_values(&["target_latest_confirmed"]) + .set(target_latest_confirmed_nonce); + } +} + +impl Metric for MessageLaneLoopMetrics { + fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { + self.source_to_target_finality_metrics.register(registry)?; + self.target_to_source_finality_metrics.register(registry)?; + register(self.lane_state_nonces.clone(), registry)?; + Ok(()) + } +} diff --git a/relays/parachains/Cargo.toml b/relays/parachains/Cargo.toml new file mode 100644 index 00000000000..2af091c7ebb --- /dev/null +++ b/relays/parachains/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "parachains-relay" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +async-std = "1.6.5" +async-trait = "0.1.40" +futures = "0.3.5" +log = "0.4.17" +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-parachains = { path = "../../primitives/parachains" } +bp-polkadot-core = { path = "../../primitives/polkadot-core" } +relay-substrate-client = { path = "../client-substrate" } + +[dev-dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5" } +relay-substrate-client = { path = "../client-substrate", features = ["test-helpers"] } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/parachains/src/lib.rs b/relays/parachains/src/lib.rs new file mode 100644 index 00000000000..94b3ce3ec76 --- /dev/null +++ b/relays/parachains/src/lib.rs @@ -0,0 +1,30 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use std::fmt::Debug; + +use relay_substrate_client::Chain; + +pub mod parachains_loop; +pub mod parachains_loop_metrics; + +/// Finality proofs synchronization pipeline. +pub trait ParachainsPipeline: 'static + Clone + Debug + Send + Sync { + /// Relay chain which is storing parachain heads in its `paras` module. + type SourceChain: Chain; + /// Target chain (either relay or para) which wants to know about new parachain heads. + type TargetChain: Chain; +} diff --git a/relays/parachains/src/parachains_loop.rs b/relays/parachains/src/parachains_loop.rs new file mode 100644 index 00000000000..3ef9a7f7a73 --- /dev/null +++ b/relays/parachains/src/parachains_loop.rs @@ -0,0 +1,1255 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{parachains_loop_metrics::ParachainsLoopMetrics, ParachainsPipeline}; + +use async_trait::async_trait; +use bp_parachains::BestParaHeadHash; +use bp_polkadot_core::{ + parachains::{ParaHash, ParaHeadsProof, ParaId}, + BlockNumber as RelayBlockNumber, +}; +use futures::{ + future::{FutureExt, Shared}, + poll, select_biased, +}; +use relay_substrate_client::{BlockNumberOf, Chain, HeaderIdOf}; +use relay_utils::{ + metrics::MetricsParams, relay_loop::Client as RelayClient, FailedClient, + TrackedTransactionStatus, TransactionTracker, +}; +use std::{ + collections::{BTreeMap, BTreeSet}, + future::Future, + pin::Pin, + task::Poll, + time::Duration, +}; + +/// Parachain heads synchronization params. +#[derive(Clone, Debug)] +pub struct ParachainSyncParams { + /// Parachains that we're relaying here. + pub parachains: Vec, + /// Parachain heads update strategy. + pub strategy: ParachainSyncStrategy, + /// Stall timeout. If we have submitted transaction and we see no state updates for this + /// period, we consider our transaction lost. + pub stall_timeout: Duration, +} + +/// Parachain heads update strategy. +#[derive(Clone, Copy, Debug)] +pub enum ParachainSyncStrategy { + /// Update whenever any parachain head is updated. + Any, + /// Wait till all parachain heads are updated. + All, +} + +/// Parachain header availability at a certain chain. +#[derive(Clone, Copy, Debug)] +pub enum AvailableHeader { + /// The client refuses to report parachain head at this moment. + /// + /// It is a "mild" error, which may appear when e.g. on-demand parachains relay is used. + /// This variant must be treated as "we don't want to update parachain head value at the + /// target chain at this moment". + Unavailable, + /// There's no parachain header at the relay chain. + /// + /// Normally it means that the parachain is not registered there. + Missing, + /// Parachain head with given hash is available at the source chain. + Available(T), +} + +impl AvailableHeader { + /// Transform contained value. + pub fn map(self, f: F) -> AvailableHeader + where + F: FnOnce(T) -> U, + { + match self { + AvailableHeader::Unavailable => AvailableHeader::Unavailable, + AvailableHeader::Missing => AvailableHeader::Missing, + AvailableHeader::Available(val) => AvailableHeader::Available(f(val)), + } + } +} + +/// Source client used in parachain heads synchronization loop. +#[async_trait] +pub trait SourceClient: RelayClient { + /// Returns `Ok(true)` if client is in synced state. + async fn ensure_synced(&self) -> Result; + + /// Get parachain head hash at given block. + /// + /// The implementation may call `ParachainsLoopMetrics::update_best_parachain_block_at_source` + /// on provided `metrics` object to update corresponding metric value. + async fn parachain_head( + &self, + at_block: HeaderIdOf, + metrics: Option<&ParachainsLoopMetrics>, + para_id: ParaId, + ) -> Result, Self::Error>; + + /// Get parachain heads proof. + /// + /// The number and order of entries in the resulting parachain head hashes vector must match the + /// number and order of parachains in the `parachains` vector. The incorrect implementation will + /// result in panic. + async fn prove_parachain_heads( + &self, + at_block: HeaderIdOf, + parachains: &[ParaId], + ) -> Result<(ParaHeadsProof, Vec), Self::Error>; +} + +/// Target client used in parachain heads synchronization loop. +#[async_trait] +pub trait TargetClient: RelayClient { + /// Transaction tracker to track submitted transactions. + type TransactionTracker: TransactionTracker>; + + /// Get best block id. + async fn best_block(&self) -> Result, Self::Error>; + + /// Get best finalized source block id. + async fn best_finalized_source_block( + &self, + at_block: &HeaderIdOf, + ) -> Result, Self::Error>; + + /// Get parachain head hash at given block. + /// + /// The implementation may call `ParachainsLoopMetrics::update_best_parachain_block_at_target` + /// on provided `metrics` object to update corresponding metric value. + async fn parachain_head( + &self, + at_block: HeaderIdOf, + metrics: Option<&ParachainsLoopMetrics>, + para_id: ParaId, + ) -> Result, Self::Error>; + + /// Submit parachain heads proof. + async fn submit_parachain_heads_proof( + &self, + at_source_block: HeaderIdOf, + updated_parachains: Vec<(ParaId, ParaHash)>, + proof: ParaHeadsProof, + ) -> Result; +} + +/// Return prefix that will be used by default to expose Prometheus metrics of the parachains +/// sync loop. +pub fn metrics_prefix() -> String { + format!("{}_to_{}_Parachains", P::SourceChain::NAME, P::TargetChain::NAME) +} + +/// Run parachain heads synchronization. +pub async fn run( + source_client: impl SourceClient

, + target_client: impl TargetClient

, + sync_params: ParachainSyncParams, + metrics_params: MetricsParams, + exit_signal: impl Future + 'static + Send, +) -> Result<(), relay_utils::Error> +where + P::SourceChain: Chain, +{ + let exit_signal = exit_signal.shared(); + relay_utils::relay_loop(source_client, target_client) + .with_metrics(metrics_params) + .loop_metric(ParachainsLoopMetrics::new(Some(&metrics_prefix::

()))?)? + .expose() + .await? + .run(metrics_prefix::

(), move |source_client, target_client, metrics| { + run_until_connection_lost( + source_client, + target_client, + sync_params.clone(), + metrics, + exit_signal.clone(), + ) + }) + .await +} + +/// Run parachain heads synchronization. +async fn run_until_connection_lost( + source_client: impl SourceClient

, + target_client: impl TargetClient

, + sync_params: ParachainSyncParams, + metrics: Option, + exit_signal: impl Future + Send, +) -> Result<(), FailedClient> +where + P::SourceChain: Chain, +{ + let exit_signal = exit_signal.fuse(); + let min_block_interval = std::cmp::min( + P::SourceChain::AVERAGE_BLOCK_INTERVAL, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + ); + + let mut submitted_heads_tracker: Option> = None; + + futures::pin_mut!(exit_signal); + + // Note that the internal loop breaks with `FailedClient` error even if error is non-connection. + // It is Ok for now, but it may need to be fixed in the future to use exponential backoff for + // regular errors. + + loop { + // Either wait for new block, or exit signal. + // Please note that we are prioritizing the exit signal since if both events happen at once + // it doesn't make sense to perform one more loop iteration. + select_biased! { + _ = exit_signal => return Ok(()), + _ = async_std::task::sleep(min_block_interval).fuse() => {}, + } + + // if source client is not yet synced, we'll need to sleep. Otherwise we risk submitting too + // much redundant transactions + match source_client.ensure_synced().await { + Ok(true) => (), + Ok(false) => { + log::warn!( + target: "bridge", + "{} client is syncing. Won't do anything until it is synced", + P::SourceChain::NAME, + ); + continue + }, + Err(e) => { + log::warn!( + target: "bridge", + "{} client has failed to return its sync status: {:?}", + P::SourceChain::NAME, + e, + ); + return Err(FailedClient::Target) + }, + } + + // if we have active transaction, we'll need to wait until it is mined or dropped + let best_target_block = target_client.best_block().await.map_err(|e| { + log::warn!(target: "bridge", "Failed to read best {} block: {:?}", P::SourceChain::NAME, e); + FailedClient::Target + })?; + let heads_at_target = read_heads_at_target( + &target_client, + metrics.as_ref(), + &best_target_block, + &sync_params.parachains, + ) + .await?; + + // check if our transaction has been mined + if let Some(tracker) = submitted_heads_tracker.take() { + match tracker.update(&best_target_block, &heads_at_target).await { + SubmittedHeadsStatus::Waiting(tracker) => { + // no news about our transaction and we shall keep waiting + submitted_heads_tracker = Some(tracker); + continue + }, + SubmittedHeadsStatus::Final(TrackedTransactionStatus::Finalized(_)) => { + // all heads have been updated, we don't need this tracker anymore + }, + SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost) => { + log::warn!( + target: "bridge", + "Parachains synchronization from {} to {} has stalled. Going to restart", + P::SourceChain::NAME, + P::TargetChain::NAME, + ); + + return Err(FailedClient::Both) + }, + } + } + + // we have no active transaction and may need to update heads, but do we have something for + // update? + let best_finalized_relay_block = target_client + .best_finalized_source_block(&best_target_block) + .await + .map_err(|e| { + log::warn!( + target: "bridge", + "Failed to read best finalized {} block from {}: {:?}", + P::SourceChain::NAME, + P::TargetChain::NAME, + e, + ); + FailedClient::Target + })?; + let heads_at_source = read_heads_at_source( + &source_client, + metrics.as_ref(), + &best_finalized_relay_block, + &sync_params.parachains, + ) + .await?; + let updated_ids = select_parachains_to_update::

( + heads_at_source, + heads_at_target, + best_finalized_relay_block, + ); + let is_update_required = is_update_required(&sync_params, &updated_ids); + + log::info!( + target: "bridge", + "Total {} parachains: {}. Up-to-date at {}: {}. Needs update at {}: {}.", + P::SourceChain::NAME, + sync_params.parachains.len(), + P::TargetChain::NAME, + sync_params.parachains.len() - updated_ids.len(), + P::TargetChain::NAME, + updated_ids.len(), + ); + + if is_update_required { + let (heads_proofs, head_hashes) = source_client + .prove_parachain_heads(best_finalized_relay_block, &updated_ids) + .await + .map_err(|e| { + log::warn!( + target: "bridge", + "Failed to prove {} parachain heads: {:?}", + P::SourceChain::NAME, + e, + ); + FailedClient::Source + })?; + log::info!( + target: "bridge", + "Submitting {} parachain heads update transaction to {}", + P::SourceChain::NAME, + P::TargetChain::NAME, + ); + + assert_eq!( + head_hashes.len(), + updated_ids.len(), + "Incorrect parachains SourceClient implementation" + ); + + let transaction_tracker = target_client + .submit_parachain_heads_proof( + best_finalized_relay_block, + updated_ids.iter().cloned().zip(head_hashes).collect(), + heads_proofs, + ) + .await + .map_err(|e| { + log::warn!( + target: "bridge", + "Failed to submit {} parachain heads proof to {}: {:?}", + P::SourceChain::NAME, + P::TargetChain::NAME, + e, + ); + FailedClient::Target + })?; + submitted_heads_tracker = Some(SubmittedHeadsTracker::

::new( + updated_ids, + best_finalized_relay_block.0, + transaction_tracker, + )); + } + } +} + +/// Given heads at source and target clients, returns set of heads that are out of sync. +fn select_parachains_to_update( + heads_at_source: BTreeMap>, + heads_at_target: BTreeMap>, + best_finalized_relay_block: HeaderIdOf, +) -> Vec +where + P::SourceChain: Chain, +{ + log::trace!( + target: "bridge", + "Selecting {} parachains to update at {} (relay block: {:?}):\n\t\ + At {}: {:?}\n\t\ + At {}: {:?}", + P::SourceChain::NAME, + P::TargetChain::NAME, + best_finalized_relay_block, + P::SourceChain::NAME, + heads_at_source, + P::TargetChain::NAME, + heads_at_target, + ); + + heads_at_source + .into_iter() + .zip(heads_at_target.into_iter()) + .filter(|((para, head_at_source), (_, head_at_target))| { + let needs_update = match (head_at_source, head_at_target) { + (AvailableHeader::Unavailable, _) => { + // source client has politely asked us not to update current parachain head + // at the target chain + false + }, + (AvailableHeader::Available(head_at_source), Some(head_at_target)) + if head_at_target.at_relay_block_number < best_finalized_relay_block.0 && + head_at_target.head_hash != *head_at_source => + { + // source client knows head that is better than the head known to the target + // client + true + }, + (AvailableHeader::Available(_), Some(_)) => { + // this is normal case when relay has recently updated heads, when parachain is + // not progressing, or when our source client is still syncing + false + }, + (AvailableHeader::Available(_), None) => { + // parachain is not yet known to the target client. This is true when parachain + // or bridge has been just onboarded/started + true + }, + (AvailableHeader::Missing, Some(_)) => { + // parachain/parathread has been offboarded removed from the system. It needs to + // be propageted to the target client + true + }, + (AvailableHeader::Missing, None) => { + // all's good - parachain is unknown to both clients + false + }, + }; + if needs_update { + log::trace!( + target: "bridge", + "{} parachain {:?} needs update at {}: {:?} vs {:?}", + P::SourceChain::NAME, + para, + P::TargetChain::NAME, + head_at_source, + head_at_target, + ); + } + + needs_update + }) + .map(|((para, _), _)| para) + .collect() +} + +/// Returns true if we need to submit update transactions to the target node. +fn is_update_required(sync_params: &ParachainSyncParams, updated_ids: &[ParaId]) -> bool { + match sync_params.strategy { + ParachainSyncStrategy::All => updated_ids.len() == sync_params.parachains.len(), + ParachainSyncStrategy::Any => !updated_ids.is_empty(), + } +} + +/// Reads given parachains heads from the source client. +/// +/// Guarantees that the returning map will have an entry for every parachain from `parachains`. +async fn read_heads_at_source( + source_client: &impl SourceClient

, + metrics: Option<&ParachainsLoopMetrics>, + at_relay_block: &HeaderIdOf, + parachains: &[ParaId], +) -> Result>, FailedClient> { + let mut para_head_hashes = BTreeMap::new(); + for para in parachains { + let para_head = source_client.parachain_head(*at_relay_block, metrics, *para).await; + match para_head { + Ok(para_head) => { + para_head_hashes.insert(*para, para_head); + }, + Err(e) => { + log::warn!( + target: "bridge", + "Failed to read head of {} parachain {:?}: {:?}", + P::SourceChain::NAME, + para, + e, + ); + return Err(FailedClient::Source) + }, + } + } + Ok(para_head_hashes) +} + +/// Reads given parachains heads from the source client. +/// +/// Guarantees that the returning map will have an entry for every parachain from `parachains`. +async fn read_heads_at_target( + target_client: &impl TargetClient

, + metrics: Option<&ParachainsLoopMetrics>, + at_block: &HeaderIdOf, + parachains: &[ParaId], +) -> Result>, FailedClient> { + let mut para_best_head_hashes = BTreeMap::new(); + for para in parachains { + let para_best_head = target_client.parachain_head(*at_block, metrics, *para).await; + match para_best_head { + Ok(para_best_head) => { + para_best_head_hashes.insert(*para, para_best_head); + }, + Err(e) => { + log::warn!( + target: "bridge", + "Failed to read head of {} parachain {:?} at {}: {:?}", + P::SourceChain::NAME, + para, + P::TargetChain::NAME, + e, + ); + return Err(FailedClient::Target) + }, + } + } + Ok(para_best_head_hashes) +} + +/// Submitted heads status. +enum SubmittedHeadsStatus { + /// Heads are not yet updated. + Waiting(SubmittedHeadsTracker

), + /// Heads transaction has either been finalized or lost (i.e. received its "final" status). + Final(TrackedTransactionStatus>), +} + +/// Type of the transaction tracker that the `SubmittedHeadsTracker` is using. +/// +/// It needs to be shared because of `poll` macro and our consuming `update` method. +type SharedTransactionTracker

= Shared< + Pin< + Box< + dyn Future< + Output = TrackedTransactionStatus< + HeaderIdOf<

::TargetChain>, + >, + > + Send, + >, + >, +>; + +/// Submitted parachain heads transaction. +struct SubmittedHeadsTracker { + /// Ids of parachains which heads were updated in the tracked transaction. + awaiting_update: BTreeSet, + /// Number of relay chain block that has been used to craft parachain heads proof. + relay_block_number: BlockNumberOf, + /// Future that waits for submitted transaction finality or loss. + /// + /// It needs to be shared because of `poll` macro and our consuming `update` method. + transaction_tracker: SharedTransactionTracker

, +} + +impl SubmittedHeadsTracker

+where + P::SourceChain: Chain, +{ + /// Creates new parachain heads transaction tracker. + pub fn new( + awaiting_update: impl IntoIterator, + relay_block_number: BlockNumberOf, + transaction_tracker: impl TransactionTracker> + 'static, + ) -> Self { + SubmittedHeadsTracker { + awaiting_update: awaiting_update.into_iter().collect(), + relay_block_number, + transaction_tracker: transaction_tracker.wait().fuse().boxed().shared(), + } + } + + /// Returns `None` if all submitted parachain heads have been updated. + pub async fn update( + mut self, + at_target_block: &HeaderIdOf, + heads_at_target: &BTreeMap>, + ) -> SubmittedHeadsStatus

{ + // remove all pending heads that were synced + for (para, best_para_head) in heads_at_target { + if best_para_head + .as_ref() + .map(|best_para_head| { + best_para_head.at_relay_block_number >= self.relay_block_number + }) + .unwrap_or(false) + { + self.awaiting_update.remove(para); + + log::trace!( + target: "bridge", + "Head of parachain {:?} has been updated at {}: {:?}. Outdated parachains remaining: {}", + para, + P::TargetChain::NAME, + best_para_head, + self.awaiting_update.len(), + ); + } + } + + // if we have synced all required heads, we are done + if self.awaiting_update.is_empty() { + return SubmittedHeadsStatus::Final(TrackedTransactionStatus::Finalized( + *at_target_block, + )) + } + + // if underlying transaction tracker has reported that the transaction is lost, we may + // then restart our sync + let transaction_tracker = self.transaction_tracker.clone(); + match poll!(transaction_tracker) { + Poll::Ready(TrackedTransactionStatus::Lost) => + return SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost), + Poll::Ready(TrackedTransactionStatus::Finalized(_)) => { + // so we are here and our transaction is mined+finalized, but some of heads were not + // updated => we're considering our loop as stalled + return SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost) + }, + _ => (), + } + + SubmittedHeadsStatus::Waiting(self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use async_std::sync::{Arc, Mutex}; + use codec::Encode; + use futures::{SinkExt, StreamExt}; + use relay_substrate_client::test_chain::TestChain; + use relay_utils::{HeaderId, MaybeConnectionError}; + use sp_core::H256; + + const PARA_ID: u32 = 0; + const PARA_0_HASH: ParaHash = H256([1u8; 32]); + const PARA_1_HASH: ParaHash = H256([2u8; 32]); + + #[derive(Clone, Debug)] + enum TestError { + Error, + MissingParachainHeadProof, + } + + impl MaybeConnectionError for TestError { + fn is_connection_error(&self) -> bool { + false + } + } + + #[derive(Clone, Debug, PartialEq, Eq)] + struct TestParachainsPipeline; + + impl ParachainsPipeline for TestParachainsPipeline { + type SourceChain = TestChain; + type TargetChain = TestChain; + } + + #[derive(Clone, Debug)] + struct TestClient { + data: Arc>, + } + + #[derive(Clone, Debug)] + struct TestTransactionTracker(Option>>); + + #[async_trait] + impl TransactionTracker for TestTransactionTracker { + type HeaderId = HeaderIdOf; + + async fn wait(self) -> TrackedTransactionStatus> { + match self.0 { + Some(status) => status, + None => futures::future::pending().await, + } + } + } + + #[derive(Clone, Debug)] + struct TestClientData { + source_sync_status: Result, + source_heads: BTreeMap, TestError>>, + source_proofs: BTreeMap, TestError>>, + + target_best_block: Result, TestError>, + target_best_finalized_source_block: Result, TestError>, + target_heads: BTreeMap>, + target_submit_result: Result<(), TestError>, + + exit_signal_sender: Option>>, + } + + impl TestClientData { + pub fn minimal() -> Self { + TestClientData { + source_sync_status: Ok(true), + source_heads: vec![(PARA_ID, Ok(AvailableHeader::Available(PARA_0_HASH)))] + .into_iter() + .collect(), + source_proofs: vec![(PARA_ID, Ok(PARA_0_HASH.encode()))].into_iter().collect(), + + target_best_block: Ok(HeaderId(0, Default::default())), + target_best_finalized_source_block: Ok(HeaderId(0, Default::default())), + target_heads: BTreeMap::new(), + target_submit_result: Ok(()), + + exit_signal_sender: None, + } + } + + pub fn with_exit_signal_sender( + sender: futures::channel::mpsc::UnboundedSender<()>, + ) -> Self { + let mut client = Self::minimal(); + client.exit_signal_sender = Some(Box::new(sender)); + client + } + } + + impl From for TestClient { + fn from(data: TestClientData) -> TestClient { + TestClient { data: Arc::new(Mutex::new(data)) } + } + } + + #[async_trait] + impl RelayClient for TestClient { + type Error = TestError; + + async fn reconnect(&mut self) -> Result<(), TestError> { + unimplemented!() + } + } + + #[async_trait] + impl SourceClient for TestClient { + async fn ensure_synced(&self) -> Result { + self.data.lock().await.source_sync_status.clone() + } + + async fn parachain_head( + &self, + _at_block: HeaderIdOf, + _metrics: Option<&ParachainsLoopMetrics>, + para_id: ParaId, + ) -> Result, TestError> { + match self.data.lock().await.source_heads.get(¶_id.0).cloned() { + Some(result) => result, + None => Ok(AvailableHeader::Missing), + } + } + + async fn prove_parachain_heads( + &self, + _at_block: HeaderIdOf, + parachains: &[ParaId], + ) -> Result<(ParaHeadsProof, Vec), TestError> { + let mut proofs = Vec::new(); + for para_id in parachains { + proofs.push( + self.data + .lock() + .await + .source_proofs + .get(¶_id.0) + .cloned() + .transpose()? + .ok_or(TestError::MissingParachainHeadProof)?, + ); + } + Ok((ParaHeadsProof(proofs), vec![Default::default(); parachains.len()])) + } + } + + #[async_trait] + impl TargetClient for TestClient { + type TransactionTracker = TestTransactionTracker; + + async fn best_block(&self) -> Result, TestError> { + self.data.lock().await.target_best_block.clone() + } + + async fn best_finalized_source_block( + &self, + _at_block: &HeaderIdOf, + ) -> Result, TestError> { + self.data.lock().await.target_best_finalized_source_block.clone() + } + + async fn parachain_head( + &self, + _at_block: HeaderIdOf, + _metrics: Option<&ParachainsLoopMetrics>, + para_id: ParaId, + ) -> Result, TestError> { + self.data.lock().await.target_heads.get(¶_id.0).cloned().transpose() + } + + async fn submit_parachain_heads_proof( + &self, + _at_source_block: HeaderIdOf, + _updated_parachains: Vec<(ParaId, ParaHash)>, + _proof: ParaHeadsProof, + ) -> Result { + let mut data = self.data.lock().await; + data.target_submit_result.clone()?; + + if let Some(mut exit_signal_sender) = data.exit_signal_sender.take() { + exit_signal_sender.send(()).await.unwrap(); + } + Ok(TestTransactionTracker(Some( + TrackedTransactionStatus::Finalized(Default::default()), + ))) + } + } + + fn default_sync_params() -> ParachainSyncParams { + ParachainSyncParams { + parachains: vec![ParaId(PARA_ID)], + strategy: ParachainSyncStrategy::Any, + stall_timeout: Duration::from_secs(60), + } + } + + #[test] + fn when_source_client_fails_to_return_sync_state() { + let mut test_source_client = TestClientData::minimal(); + test_source_client.source_sync_status = Err(TestError::Error); + + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(test_source_client), + TestClient::from(TestClientData::minimal()), + default_sync_params(), + None, + futures::future::pending(), + )), + Err(FailedClient::Target), + ); + } + + #[test] + fn when_target_client_fails_to_return_best_block() { + let mut test_target_client = TestClientData::minimal(); + test_target_client.target_best_block = Err(TestError::Error); + + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(TestClientData::minimal()), + TestClient::from(test_target_client), + default_sync_params(), + None, + futures::future::pending(), + )), + Err(FailedClient::Target), + ); + } + + #[test] + fn when_target_client_fails_to_read_heads() { + let mut test_target_client = TestClientData::minimal(); + test_target_client.target_heads.insert(PARA_ID, Err(TestError::Error)); + + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(TestClientData::minimal()), + TestClient::from(test_target_client), + default_sync_params(), + None, + futures::future::pending(), + )), + Err(FailedClient::Target), + ); + } + + #[test] + fn when_target_client_fails_to_read_best_finalized_source_block() { + let mut test_target_client = TestClientData::minimal(); + test_target_client.target_best_finalized_source_block = Err(TestError::Error); + + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(TestClientData::minimal()), + TestClient::from(test_target_client), + default_sync_params(), + None, + futures::future::pending(), + )), + Err(FailedClient::Target), + ); + } + + #[test] + fn when_source_client_fails_to_read_heads() { + let mut test_source_client = TestClientData::minimal(); + test_source_client.source_heads.insert(PARA_ID, Err(TestError::Error)); + + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(test_source_client), + TestClient::from(TestClientData::minimal()), + default_sync_params(), + None, + futures::future::pending(), + )), + Err(FailedClient::Source), + ); + } + + #[test] + fn when_source_client_fails_to_prove_heads() { + let mut test_source_client = TestClientData::minimal(); + test_source_client.source_proofs.insert(PARA_ID, Err(TestError::Error)); + + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(test_source_client), + TestClient::from(TestClientData::minimal()), + default_sync_params(), + None, + futures::future::pending(), + )), + Err(FailedClient::Source), + ); + } + + #[test] + fn when_target_client_rejects_update_transaction() { + let mut test_target_client = TestClientData::minimal(); + test_target_client.target_submit_result = Err(TestError::Error); + + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(TestClientData::minimal()), + TestClient::from(test_target_client), + default_sync_params(), + None, + futures::future::pending(), + )), + Err(FailedClient::Target), + ); + } + + #[test] + fn minimal_working_case() { + let (exit_signal_sender, exit_signal) = futures::channel::mpsc::unbounded(); + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(TestClientData::minimal()), + TestClient::from(TestClientData::with_exit_signal_sender(exit_signal_sender)), + default_sync_params(), + None, + exit_signal.into_future().map(|(_, _)| ()), + )), + Ok(()), + ); + } + + const PARA_1_ID: u32 = PARA_ID + 1; + const SOURCE_BLOCK_NUMBER: u32 = 100; + + fn test_tx_tracker() -> SubmittedHeadsTracker { + SubmittedHeadsTracker::new( + vec![ParaId(PARA_ID), ParaId(PARA_1_ID)], + SOURCE_BLOCK_NUMBER, + TestTransactionTracker(None), + ) + } + + fn all_expected_tracker_heads() -> BTreeMap> { + vec![ + ( + ParaId(PARA_ID), + Some(BestParaHeadHash { + at_relay_block_number: SOURCE_BLOCK_NUMBER, + head_hash: PARA_0_HASH, + }), + ), + ( + ParaId(PARA_1_ID), + Some(BestParaHeadHash { + at_relay_block_number: SOURCE_BLOCK_NUMBER, + head_hash: PARA_0_HASH, + }), + ), + ] + .into_iter() + .collect() + } + + impl From> for Option> { + fn from(status: SubmittedHeadsStatus) -> Option> { + match status { + SubmittedHeadsStatus::Waiting(tracker) => Some(tracker.awaiting_update), + _ => None, + } + } + } + + #[async_std::test] + async fn tx_tracker_update_when_nothing_is_updated() { + assert_eq!( + Some(test_tx_tracker().awaiting_update), + test_tx_tracker() + .update(&HeaderId(0, Default::default()), &vec![].into_iter().collect()) + .await + .into(), + ); + } + + #[async_std::test] + async fn tx_tracker_update_when_one_of_heads_is_updated_to_previous_value() { + assert_eq!( + Some(test_tx_tracker().awaiting_update), + test_tx_tracker() + .update( + &HeaderId(0, Default::default()), + &vec![( + ParaId(PARA_ID), + Some(BestParaHeadHash { + at_relay_block_number: SOURCE_BLOCK_NUMBER - 1, + head_hash: PARA_0_HASH, + }) + )] + .into_iter() + .collect() + ) + .await + .into(), + ); + } + + #[async_std::test] + async fn tx_tracker_update_when_one_of_heads_is_updated() { + assert_eq!( + Some(vec![ParaId(PARA_1_ID)].into_iter().collect::>()), + test_tx_tracker() + .update( + &HeaderId(0, Default::default()), + &vec![( + ParaId(PARA_ID), + Some(BestParaHeadHash { + at_relay_block_number: SOURCE_BLOCK_NUMBER, + head_hash: PARA_0_HASH, + }) + )] + .into_iter() + .collect() + ) + .await + .into(), + ); + } + + #[async_std::test] + async fn tx_tracker_update_when_all_heads_are_updated() { + assert_eq!( + Option::>::None, + test_tx_tracker() + .update(&HeaderId(0, Default::default()), &all_expected_tracker_heads()) + .await + .into(), + ); + } + + #[async_std::test] + async fn tx_tracker_update_when_tx_is_lost() { + let mut tx_tracker = test_tx_tracker(); + tx_tracker.transaction_tracker = + futures::future::ready(TrackedTransactionStatus::Lost).boxed().shared(); + assert!(matches!( + tx_tracker + .update(&HeaderId(0, Default::default()), &vec![].into_iter().collect()) + .await, + SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost), + )); + } + + #[async_std::test] + async fn tx_tracker_update_when_tx_is_finalized_but_heads_are_not_updated() { + let mut tx_tracker = test_tx_tracker(); + tx_tracker.transaction_tracker = + futures::future::ready(TrackedTransactionStatus::Finalized(Default::default())) + .boxed() + .shared(); + assert!(matches!( + tx_tracker + .update(&HeaderId(0, Default::default()), &vec![].into_iter().collect()) + .await, + SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost), + )); + } + + #[async_std::test] + async fn tx_tracker_update_when_tx_is_finalized_and_heads_are_updated() { + let mut tx_tracker = test_tx_tracker(); + tx_tracker.transaction_tracker = + futures::future::ready(TrackedTransactionStatus::Finalized(Default::default())) + .boxed() + .shared(); + assert!(matches!( + tx_tracker + .update(&HeaderId(0, Default::default()), &all_expected_tracker_heads()) + .await, + SubmittedHeadsStatus::Final(TrackedTransactionStatus::Finalized(_)), + )); + } + + #[test] + fn parachain_is_not_updated_if_it_is_unknown_to_both_clients() { + assert_eq!( + select_parachains_to_update::( + vec![(ParaId(PARA_ID), AvailableHeader::Missing)].into_iter().collect(), + vec![(ParaId(PARA_ID), None)].into_iter().collect(), + HeaderId(10, Default::default()), + ), + Vec::::new(), + ); + } + + #[test] + fn parachain_is_not_updated_if_it_has_been_updated_at_better_relay_block() { + assert_eq!( + select_parachains_to_update::( + vec![(ParaId(PARA_ID), AvailableHeader::Available(PARA_0_HASH))] + .into_iter() + .collect(), + vec![( + ParaId(PARA_ID), + Some(BestParaHeadHash { at_relay_block_number: 20, head_hash: PARA_1_HASH }) + )] + .into_iter() + .collect(), + HeaderId(10, Default::default()), + ), + Vec::::new(), + ); + } + + #[test] + fn parachain_is_not_updated_if_hash_is_the_same_at_next_relay_block() { + assert_eq!( + select_parachains_to_update::( + vec![(ParaId(PARA_ID), AvailableHeader::Available(PARA_0_HASH))] + .into_iter() + .collect(), + vec![( + ParaId(PARA_ID), + Some(BestParaHeadHash { at_relay_block_number: 0, head_hash: PARA_0_HASH }) + )] + .into_iter() + .collect(), + HeaderId(10, Default::default()), + ), + Vec::::new(), + ); + } + + #[test] + fn parachain_is_updated_after_offboarding() { + assert_eq!( + select_parachains_to_update::( + vec![(ParaId(PARA_ID), AvailableHeader::Missing)].into_iter().collect(), + vec![( + ParaId(PARA_ID), + Some(BestParaHeadHash { + at_relay_block_number: 0, + head_hash: Default::default(), + }) + )] + .into_iter() + .collect(), + HeaderId(10, Default::default()), + ), + vec![ParaId(PARA_ID)], + ); + } + + #[test] + fn parachain_is_updated_after_onboarding() { + assert_eq!( + select_parachains_to_update::( + vec![(ParaId(PARA_ID), AvailableHeader::Available(PARA_0_HASH))] + .into_iter() + .collect(), + vec![(ParaId(PARA_ID), None)].into_iter().collect(), + HeaderId(10, Default::default()), + ), + vec![ParaId(PARA_ID)], + ); + } + + #[test] + fn parachain_is_updated_if_newer_head_is_known() { + assert_eq!( + select_parachains_to_update::( + vec![(ParaId(PARA_ID), AvailableHeader::Available(PARA_1_HASH))] + .into_iter() + .collect(), + vec![( + ParaId(PARA_ID), + Some(BestParaHeadHash { at_relay_block_number: 0, head_hash: PARA_0_HASH }) + )] + .into_iter() + .collect(), + HeaderId(10, Default::default()), + ), + vec![ParaId(PARA_ID)], + ); + } + + #[test] + fn parachain_is_not_updated_if_source_head_is_unavailable() { + assert_eq!( + select_parachains_to_update::( + vec![(ParaId(PARA_ID), AvailableHeader::Unavailable)].into_iter().collect(), + vec![( + ParaId(PARA_ID), + Some(BestParaHeadHash { at_relay_block_number: 0, head_hash: PARA_0_HASH }) + )] + .into_iter() + .collect(), + HeaderId(10, Default::default()), + ), + vec![], + ); + } + + #[test] + fn is_update_required_works() { + let mut sync_params = ParachainSyncParams { + parachains: vec![ParaId(PARA_ID), ParaId(PARA_1_ID)], + strategy: ParachainSyncStrategy::Any, + stall_timeout: Duration::from_secs(60), + }; + + assert!(!is_update_required(&sync_params, &[])); + assert!(is_update_required(&sync_params, &[ParaId(PARA_ID)])); + assert!(is_update_required(&sync_params, &[ParaId(PARA_ID), ParaId(PARA_1_ID)])); + + sync_params.strategy = ParachainSyncStrategy::All; + assert!(!is_update_required(&sync_params, &[])); + assert!(!is_update_required(&sync_params, &[ParaId(PARA_ID)])); + assert!(is_update_required(&sync_params, &[ParaId(PARA_ID), ParaId(PARA_1_ID)])); + } +} diff --git a/relays/parachains/src/parachains_loop_metrics.rs b/relays/parachains/src/parachains_loop_metrics.rs new file mode 100644 index 00000000000..ff8bace2744 --- /dev/null +++ b/relays/parachains/src/parachains_loop_metrics.rs @@ -0,0 +1,98 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use bp_polkadot_core::parachains::ParaId; +use relay_utils::metrics::{ + metric_name, register, GaugeVec, Metric, Opts, PrometheusError, Registry, U64, +}; + +/// Parachains sync metrics. +#[derive(Clone)] +pub struct ParachainsLoopMetrics { + /// Best parachains header numbers at the source. + best_source_block_numbers: GaugeVec, + /// Best parachains header numbers at the target. + best_target_block_numbers: GaugeVec, +} + +impl ParachainsLoopMetrics { + /// Create and register parachains loop metrics. + pub fn new(prefix: Option<&str>) -> Result { + Ok(ParachainsLoopMetrics { + best_source_block_numbers: GaugeVec::new( + Opts::new( + metric_name(prefix, "best_parachain_block_number_at_source"), + "Best parachain block numbers at the source relay chain".to_string(), + ), + &["parachain"], + )?, + best_target_block_numbers: GaugeVec::new( + Opts::new( + metric_name(prefix, "best_parachain_block_number_at_target"), + "Best parachain block numbers at the target chain".to_string(), + ), + &["parachain"], + )?, + }) + } + + /// Update best block number at source. + pub fn update_best_parachain_block_at_source>( + &self, + parachain: ParaId, + block_number: Number, + ) { + let block_number = block_number.into(); + let label = parachain_label(¶chain); + log::trace!( + target: "bridge-metrics", + "Updated value of metric 'best_parachain_block_number_at_source[{}]': {:?}", + label, + block_number, + ); + self.best_source_block_numbers.with_label_values(&[&label]).set(block_number); + } + + /// Update best block number at target. + pub fn update_best_parachain_block_at_target>( + &self, + parachain: ParaId, + block_number: Number, + ) { + let block_number = block_number.into(); + let label = parachain_label(¶chain); + log::trace!( + target: "bridge-metrics", + "Updated value of metric 'best_parachain_block_number_at_target[{}]': {:?}", + label, + block_number, + ); + self.best_target_block_numbers.with_label_values(&[&label]).set(block_number); + } +} + +impl Metric for ParachainsLoopMetrics { + fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { + register(self.best_source_block_numbers.clone(), registry)?; + register(self.best_target_block_numbers.clone(), registry)?; + Ok(()) + } +} + +/// Return metric label for the parachain. +fn parachain_label(parachain: &ParaId) -> String { + format!("para_{}", parachain.0) +} diff --git a/relays/utils/Cargo.toml b/relays/utils/Cargo.toml new file mode 100644 index 00000000000..ebbfa74c6ff --- /dev/null +++ b/relays/utils/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "relay-utils" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +ansi_term = "0.12" +anyhow = "1.0" +async-std = "1.6.5" +async-trait = "0.1" +backoff = "0.2" +isahc = "1.2" +env_logger = "0.8.2" +futures = "0.3.5" +jsonpath_lib = "0.2" +log = "0.4.17" +num-traits = "0.2" +serde_json = "1.0" +sysinfo = "0.15" +time = { version = "0.3", features = ["formatting", "local-offset", "std"] } +tokio = { version = "1.8", features = ["rt"] } +thiserror = "1.0.26" + +# Bridge dependencies + +bp-runtime = { path = "../../primitives/runtime" } + +# Substrate dependencies + +substrate-prometheus-endpoint = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/utils/src/error.rs b/relays/utils/src/error.rs new file mode 100644 index 00000000000..26f1d0cacef --- /dev/null +++ b/relays/utils/src/error.rs @@ -0,0 +1,46 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. +// Parity Bridges Common 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. +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use std::net::AddrParseError; +use thiserror::Error; + +/// Result type used by relay utilities. +pub type Result = std::result::Result; + +/// Relay utilities errors. +#[derive(Error, Debug)] +pub enum Error { + /// Failed to request a float value from HTTP service. + #[error("Failed to fetch token price from remote server: {0}")] + FetchTokenPrice(#[source] anyhow::Error), + /// Failed to parse the response from HTTP service. + #[error("Failed to parse HTTP service response: {0:?}. Response: {1:?}")] + ParseHttp(serde_json::Error, String), + /// Failed to select response value from the Json response. + #[error("Failed to select value from response: {0:?}. Response: {1:?}")] + SelectResponseValue(jsonpath_lib::JsonPathError, String), + /// Failed to parse float value from the selected value. + #[error( + "Failed to parse float value {0:?} from response. It is assumed to be positive and normal" + )] + ParseFloat(f64), + /// Couldn't found value in the JSON response. + #[error("Missing required value from response: {0:?}")] + MissingResponseValue(String), + /// Invalid host address was used for exposing Prometheus metrics. + #[error("Invalid host {0} is used to expose Prometheus metrics: {1}")] + ExposingMetricsInvalidHost(String, AddrParseError), + /// Prometheus error. + #[error("{0}")] + Prometheus(#[from] substrate_prometheus_endpoint::prometheus::Error), +} diff --git a/relays/utils/src/initialize.rs b/relays/utils/src/initialize.rs new file mode 100644 index 00000000000..8224c1803ad --- /dev/null +++ b/relays/utils/src/initialize.rs @@ -0,0 +1,136 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Relayer initialization functions. + +use std::{cell::RefCell, fmt::Display, io::Write}; + +async_std::task_local! { + pub(crate) static LOOP_NAME: RefCell = RefCell::new(String::default()); +} + +/// Initialize relay environment. +pub fn initialize_relay() { + initialize_logger(true); +} + +/// Initialize Relay logger instance. +pub fn initialize_logger(with_timestamp: bool) { + let format = time::format_description::parse( + "[year]-[month]-[day] \ + [hour repr:24]:[minute]:[second] [offset_hour sign:mandatory]", + ) + .expect("static format string is valid"); + + let mut builder = env_logger::Builder::new(); + builder.filter_level(log::LevelFilter::Warn); + builder.filter_module("bridge", log::LevelFilter::Info); + builder.parse_default_env(); + if with_timestamp { + builder.format(move |buf, record| { + let timestamp = time::OffsetDateTime::now_local() + .unwrap_or_else(|_| time::OffsetDateTime::now_utc()); + let timestamp = timestamp.format(&format).unwrap_or_else(|_| timestamp.to_string()); + + let log_level = color_level(record.level()); + let log_target = color_target(record.target()); + let timestamp = if cfg!(windows) { + Either::Left(timestamp) + } else { + Either::Right(ansi_term::Colour::Fixed(8).bold().paint(timestamp)) + }; + + writeln!( + buf, + "{}{} {} {} {}", + loop_name_prefix(), + timestamp, + log_level, + log_target, + record.args(), + ) + }); + } else { + builder.format(move |buf, record| { + let log_level = color_level(record.level()); + let log_target = color_target(record.target()); + + writeln!(buf, "{}{log_level} {log_target} {}", loop_name_prefix(), record.args(),) + }); + } + + builder.init(); +} + +/// Initialize relay loop. Must only be called once per every loop task. +pub(crate) fn initialize_loop(loop_name: String) { + LOOP_NAME.with(|g_loop_name| *g_loop_name.borrow_mut() = loop_name); +} + +/// Returns loop name prefix to use in logs. The prefix is initialized with the `initialize_loop` +/// call. +fn loop_name_prefix() -> String { + // try_with to avoid panic outside of async-std task context + LOOP_NAME + .try_with(|loop_name| { + // using borrow is ok here, because loop is only initialized once (=> borrow_mut will + // only be called once) + let loop_name = loop_name.borrow(); + if loop_name.is_empty() { + String::new() + } else { + format!("[{loop_name}] ") + } + }) + .unwrap_or_else(|_| String::new()) +} + +enum Either { + Left(A), + Right(B), +} +impl Display for Either { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Left(a) => write!(fmt, "{a}"), + Self::Right(b) => write!(fmt, "{b}"), + } + } +} + +fn color_target(target: &str) -> impl Display + '_ { + if cfg!(windows) { + Either::Left(target) + } else { + Either::Right(ansi_term::Colour::Fixed(8).paint(target)) + } +} + +fn color_level(level: log::Level) -> impl Display { + if cfg!(windows) { + Either::Left(level) + } else { + let s = level.to_string(); + use ansi_term::Colour as Color; + Either::Right(match level { + log::Level::Error => Color::Fixed(9).bold().paint(s), + log::Level::Warn => Color::Fixed(11).bold().paint(s), + log::Level::Info => Color::Fixed(10).paint(s), + log::Level::Debug => Color::Fixed(14).paint(s), + log::Level::Trace => Color::Fixed(12).paint(s), + }) + } +} diff --git a/relays/utils/src/lib.rs b/relays/utils/src/lib.rs new file mode 100644 index 00000000000..42bf86ebf5b --- /dev/null +++ b/relays/utils/src/lib.rs @@ -0,0 +1,312 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Utilities used by different relays. + +pub use bp_runtime::HeaderId; +pub use error::Error; +pub use relay_loop::{relay_loop, relay_metrics}; + +use async_trait::async_trait; +use backoff::{backoff::Backoff, ExponentialBackoff}; +use futures::future::FutureExt; +use std::time::Duration; +use thiserror::Error; + +/// Default relay loop stall timeout. If transactions generated by relay are immortal, then +/// this timeout is used. +/// +/// There are no any strict requirements on block time in Substrate. But we assume here that all +/// Substrate-based chains will be designed to produce relatively fast (compared to the slowest +/// blockchains) blocks. So 1 hour seems to be a good guess for (even congested) chains to mine +/// transaction, or remove it from the pool. +pub const STALL_TIMEOUT: Duration = Duration::from_secs(60 * 60); + +/// Max delay after connection-unrelated error happened before we'll try the +/// same request again. +pub const MAX_BACKOFF_INTERVAL: Duration = Duration::from_secs(60); +/// Delay after connection-related error happened before we'll try +/// reconnection again. +pub const CONNECTION_ERROR_DELAY: Duration = Duration::from_secs(10); + +pub mod error; +pub mod initialize; +pub mod metrics; +pub mod relay_loop; + +/// Block number traits shared by all chains that relay is able to serve. +pub trait BlockNumberBase: + 'static + + From + + Into + + Ord + + Clone + + Copy + + Default + + Send + + Sync + + std::fmt::Debug + + std::fmt::Display + + std::hash::Hash + + std::ops::Add + + std::ops::Sub + + num_traits::CheckedSub + + num_traits::Saturating + + num_traits::Zero + + num_traits::One +{ +} + +impl BlockNumberBase for T where + T: 'static + + From + + Into + + Ord + + Clone + + Copy + + Default + + Send + + Sync + + std::fmt::Debug + + std::fmt::Display + + std::hash::Hash + + std::ops::Add + + std::ops::Sub + + num_traits::CheckedSub + + num_traits::Saturating + + num_traits::Zero + + num_traits::One +{ +} + +/// Macro that returns (client, Err(error)) tuple from function if result is Err(error). +#[macro_export] +macro_rules! bail_on_error { + ($result: expr) => { + match $result { + (client, Ok(result)) => (client, result), + (client, Err(error)) => return (client, Err(error)), + } + }; +} + +/// Macro that returns (client, Err(error)) tuple from function if result is Err(error). +#[macro_export] +macro_rules! bail_on_arg_error { + ($result: expr, $client: ident) => { + match $result { + Ok(result) => result, + Err(error) => return ($client, Err(error)), + } + }; +} + +/// Error type that can signal connection errors. +pub trait MaybeConnectionError { + /// Returns true if error (maybe) represents connection error. + fn is_connection_error(&self) -> bool; +} + +/// Final status of the tracked transaction. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum TrackedTransactionStatus { + /// Transaction has been lost. + Lost, + /// Transaction has been mined and finalized at given block. + Finalized(BlockId), +} + +/// Transaction tracker. +#[async_trait] +pub trait TransactionTracker: Send { + /// Header id, used by the chain. + type HeaderId: Clone + Send; + + /// Wait until transaction is either finalized or invalidated/lost. + async fn wait(self) -> TrackedTransactionStatus; +} + +/// Stringified error that may be either connection-related or not. +#[derive(Error, Debug)] +pub enum StringifiedMaybeConnectionError { + /// The error is connection-related error. + #[error("{0}")] + Connection(String), + /// The error is connection-unrelated error. + #[error("{0}")] + NonConnection(String), +} + +impl StringifiedMaybeConnectionError { + /// Create new stringified connection error. + pub fn new(is_connection_error: bool, error: String) -> Self { + if is_connection_error { + StringifiedMaybeConnectionError::Connection(error) + } else { + StringifiedMaybeConnectionError::NonConnection(error) + } + } +} + +impl MaybeConnectionError for StringifiedMaybeConnectionError { + fn is_connection_error(&self) -> bool { + match *self { + StringifiedMaybeConnectionError::Connection(_) => true, + StringifiedMaybeConnectionError::NonConnection(_) => false, + } + } +} + +/// Exponential backoff for connection-unrelated errors retries. +pub fn retry_backoff() -> ExponentialBackoff { + ExponentialBackoff { + // we do not want relayer to stop + max_elapsed_time: None, + max_interval: MAX_BACKOFF_INTERVAL, + ..Default::default() + } +} + +/// Compact format of IDs vector. +pub fn format_ids(mut ids: impl ExactSizeIterator) -> String { + const NTH_PROOF: &str = "we have checked len; qed"; + match ids.len() { + 0 => "".into(), + 1 => format!("{:?}", ids.next().expect(NTH_PROOF)), + 2 => { + let id0 = ids.next().expect(NTH_PROOF); + let id1 = ids.next().expect(NTH_PROOF); + format!("[{id0:?}, {id1:?}]") + }, + len => { + let id0 = ids.next().expect(NTH_PROOF); + let id_last = ids.last().expect(NTH_PROOF); + format!("{len}:[{id0:?} ... {id_last:?}]") + }, + } +} + +/// Stream that emits item every `timeout_ms` milliseconds. +pub fn interval(timeout: Duration) -> impl futures::Stream { + futures::stream::unfold((), move |_| async move { + async_std::task::sleep(timeout).await; + Some(((), ())) + }) +} + +/// Which client has caused error. +#[derive(Debug, Eq, Clone, Copy, PartialEq)] +pub enum FailedClient { + /// It is the source client who has caused error. + Source, + /// It is the target client who has caused error. + Target, + /// Both clients are failing, or we just encountered some other error that + /// should be treated like that. + Both, +} + +/// Future process result. +#[derive(Debug, Clone, Copy)] +pub enum ProcessFutureResult { + /// Future has been processed successfully. + Success, + /// Future has failed with non-connection error. + Failed, + /// Future has failed with connection error. + ConnectionFailed, +} + +impl ProcessFutureResult { + /// Returns true if result is Success. + pub fn is_ok(self) -> bool { + match self { + ProcessFutureResult::Success => true, + ProcessFutureResult::Failed | ProcessFutureResult::ConnectionFailed => false, + } + } + + /// Returns `Ok(())` if future has succeeded. + /// Returns `Err(failed_client)` otherwise. + pub fn fail_if_error(self, failed_client: FailedClient) -> Result<(), FailedClient> { + if self.is_ok() { + Ok(()) + } else { + Err(failed_client) + } + } + + /// Returns Ok(true) if future has succeeded. + /// Returns Ok(false) if future has failed with non-connection error. + /// Returns Err if future is `ConnectionFailed`. + pub fn fail_if_connection_error( + self, + failed_client: FailedClient, + ) -> Result { + match self { + ProcessFutureResult::Success => Ok(true), + ProcessFutureResult::Failed => Ok(false), + ProcessFutureResult::ConnectionFailed => Err(failed_client), + } + } +} + +/// Process result of the future from a client. +pub fn process_future_result( + result: Result, + retry_backoff: &mut ExponentialBackoff, + on_success: impl FnOnce(TResult), + go_offline_future: &mut std::pin::Pin<&mut futures::future::Fuse>, + go_offline: impl FnOnce(Duration) -> TGoOfflineFuture, + error_pattern: impl FnOnce() -> String, +) -> ProcessFutureResult +where + TError: std::fmt::Debug + MaybeConnectionError, + TGoOfflineFuture: FutureExt, +{ + match result { + Ok(result) => { + on_success(result); + retry_backoff.reset(); + ProcessFutureResult::Success + }, + Err(error) if error.is_connection_error() => { + log::error!( + target: "bridge", + "{}: {:?}. Going to restart", + error_pattern(), + error, + ); + + retry_backoff.reset(); + go_offline_future.set(go_offline(CONNECTION_ERROR_DELAY).fuse()); + ProcessFutureResult::ConnectionFailed + }, + Err(error) => { + let retry_delay = retry_backoff.next_backoff().unwrap_or(CONNECTION_ERROR_DELAY); + log::error!( + target: "bridge", + "{}: {:?}. Retrying in {}", + error_pattern(), + error, + retry_delay.as_secs_f64(), + ); + + go_offline_future.set(go_offline(retry_delay).fuse()); + ProcessFutureResult::Failed + }, + } +} diff --git a/relays/utils/src/metrics.rs b/relays/utils/src/metrics.rs new file mode 100644 index 00000000000..fa7a79a71c1 --- /dev/null +++ b/relays/utils/src/metrics.rs @@ -0,0 +1,165 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +pub use float_json_value::FloatJsonValueMetric; +pub use global::GlobalMetrics; +pub use substrate_prometheus_endpoint::{ + prometheus::core::{Atomic, Collector}, + register, Counter, CounterVec, Gauge, GaugeVec, Opts, PrometheusError, Registry, F64, I64, U64, +}; + +use async_std::sync::{Arc, RwLock}; +use async_trait::async_trait; +use std::{fmt::Debug, time::Duration}; + +mod float_json_value; +mod global; + +/// Shared reference to `f64` value that is updated by the metric. +pub type F64SharedRef = Arc>>; +/// Int gauge metric type. +pub type IntGauge = Gauge; + +/// Unparsed address that needs to be used to expose Prometheus metrics. +#[derive(Debug, Clone)] +pub struct MetricsAddress { + /// Serve HTTP requests at given host. + pub host: String, + /// Serve HTTP requests at given port. + pub port: u16, +} + +/// Prometheus endpoint MetricsParams. +#[derive(Debug, Clone)] +pub struct MetricsParams { + /// Interface and TCP port to be used when exposing Prometheus metrics. + pub address: Option, + /// Metrics registry. May be `Some(_)` if several components share the same endpoint. + pub registry: Registry, +} + +/// Metric API. +pub trait Metric: Clone + Send + Sync + 'static { + fn register(&self, registry: &Registry) -> Result<(), PrometheusError>; +} + +/// Standalone metric API. +/// +/// Metrics of this kind know how to update themselves, so we may just spawn and forget the +/// asynchronous self-update task. +#[async_trait] +pub trait StandaloneMetric: Metric { + /// Update metric values. + async fn update(&self); + + /// Metrics update interval. + fn update_interval(&self) -> Duration; + + /// Register and spawn metric. Metric is only spawned if it is registered for the first time. + fn register_and_spawn(self, registry: &Registry) -> Result<(), PrometheusError> { + match self.register(registry) { + Ok(()) => { + self.spawn(); + Ok(()) + }, + Err(PrometheusError::AlreadyReg) => Ok(()), + Err(e) => Err(e), + } + } + + /// Spawn the self update task that will keep update metric value at given intervals. + fn spawn(self) { + async_std::task::spawn(async move { + let update_interval = self.update_interval(); + loop { + self.update().await; + async_std::task::sleep(update_interval).await; + } + }); + } +} + +impl Default for MetricsAddress { + fn default() -> Self { + MetricsAddress { host: "127.0.0.1".into(), port: 9616 } + } +} + +impl MetricsParams { + /// Creates metrics params so that metrics are not exposed. + pub fn disabled() -> Self { + MetricsParams { address: None, registry: Registry::new() } + } + + /// Do not expose metrics. + #[must_use] + pub fn disable(mut self) -> Self { + self.address = None; + self + } +} + +impl From> for MetricsParams { + fn from(address: Option) -> Self { + MetricsParams { address, registry: Registry::new() } + } +} + +/// Returns metric name optionally prefixed with given prefix. +pub fn metric_name(prefix: Option<&str>, name: &str) -> String { + if let Some(prefix) = prefix { + format!("{prefix}_{name}") + } else { + name.into() + } +} + +/// Set value of gauge metric. +/// +/// If value is `Ok(None)` or `Err(_)`, metric would have default value. +pub fn set_gauge_value, E: Debug>( + gauge: &Gauge, + value: Result, E>, +) { + gauge.set(match value { + Ok(Some(value)) => { + log::trace!( + target: "bridge-metrics", + "Updated value of metric '{:?}': {:?}", + gauge.desc().first().map(|d| &d.fq_name), + value, + ); + value + }, + Ok(None) => { + log::warn!( + target: "bridge-metrics", + "Failed to update metric '{:?}': value is empty", + gauge.desc().first().map(|d| &d.fq_name), + ); + Default::default() + }, + Err(error) => { + log::warn!( + target: "bridge-metrics", + "Failed to update metric '{:?}': {:?}", + gauge.desc().first().map(|d| &d.fq_name), + error, + ); + Default::default() + }, + }) +} diff --git a/relays/utils/src/metrics/float_json_value.rs b/relays/utils/src/metrics/float_json_value.rs new file mode 100644 index 00000000000..17b09e05097 --- /dev/null +++ b/relays/utils/src/metrics/float_json_value.rs @@ -0,0 +1,147 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{ + error::{self, Error}, + metrics::{ + metric_name, register, F64SharedRef, Gauge, Metric, PrometheusError, Registry, + StandaloneMetric, F64, + }, +}; + +use async_std::sync::{Arc, RwLock}; +use async_trait::async_trait; +use std::time::Duration; + +/// Value update interval. +const UPDATE_INTERVAL: Duration = Duration::from_secs(300); + +/// Metric that represents float value received from HTTP service as float gauge. +/// +/// The float value returned by the service is assumed to be normal (`f64::is_normal` +/// should return `true`) and strictly positive. +#[derive(Debug, Clone)] +pub struct FloatJsonValueMetric { + url: String, + json_path: String, + metric: Gauge, + shared_value_ref: F64SharedRef, +} + +impl FloatJsonValueMetric { + /// Create new metric instance with given name and help. + pub fn new( + url: String, + json_path: String, + name: String, + help: String, + ) -> Result { + let shared_value_ref = Arc::new(RwLock::new(None)); + Ok(FloatJsonValueMetric { + url, + json_path, + metric: Gauge::new(metric_name(None, &name), help)?, + shared_value_ref, + }) + } + + /// Get shared reference to metric value. + pub fn shared_value_ref(&self) -> F64SharedRef { + self.shared_value_ref.clone() + } + + /// Request value from HTTP service. + async fn request_value(&self) -> anyhow::Result { + use isahc::{AsyncReadResponseExt, HttpClient, Request}; + + let request = Request::get(&self.url).header("Accept", "application/json").body(())?; + let raw_response = HttpClient::new()?.send_async(request).await?.text().await?; + Ok(raw_response) + } + + /// Read value from HTTP service. + async fn read_value(&self) -> error::Result { + let raw_response = self.request_value().await.map_err(Error::FetchTokenPrice)?; + parse_service_response(&self.json_path, &raw_response) + } +} + +impl Metric for FloatJsonValueMetric { + fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { + register(self.metric.clone(), registry).map(drop) + } +} + +#[async_trait] +impl StandaloneMetric for FloatJsonValueMetric { + fn update_interval(&self) -> Duration { + UPDATE_INTERVAL + } + + async fn update(&self) { + let value = self.read_value().await; + let maybe_ok = value.as_ref().ok().copied(); + crate::metrics::set_gauge_value(&self.metric, value.map(Some)); + *self.shared_value_ref.write().await = maybe_ok; + } +} + +/// Parse HTTP service response. +fn parse_service_response(json_path: &str, response: &str) -> error::Result { + let json = + serde_json::from_str(response).map_err(|err| Error::ParseHttp(err, response.to_owned()))?; + + let mut selector = jsonpath_lib::selector(&json); + let maybe_selected_value = + selector(json_path).map_err(|err| Error::SelectResponseValue(err, response.to_owned()))?; + let selected_value = maybe_selected_value + .first() + .and_then(|v| v.as_f64()) + .ok_or_else(|| Error::MissingResponseValue(response.to_owned()))?; + if !selected_value.is_normal() || selected_value < 0.0 { + return Err(Error::ParseFloat(selected_value)) + } + + Ok(selected_value) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_service_response_works() { + assert_eq!( + parse_service_response("$.kusama.usd", r#"{"kusama":{"usd":433.05}}"#).map_err(drop), + Ok(433.05), + ); + } + + #[test] + fn parse_service_response_rejects_negative_numbers() { + assert!(parse_service_response("$.kusama.usd", r#"{"kusama":{"usd":-433.05}}"#).is_err()); + } + + #[test] + fn parse_service_response_rejects_zero_numbers() { + assert!(parse_service_response("$.kusama.usd", r#"{"kusama":{"usd":0.0}}"#).is_err()); + } + + #[test] + fn parse_service_response_rejects_nan() { + assert!(parse_service_response("$.kusama.usd", r#"{"kusama":{"usd":NaN}}"#).is_err()); + } +} diff --git a/relays/utils/src/metrics/global.rs b/relays/utils/src/metrics/global.rs new file mode 100644 index 00000000000..df90a2c4823 --- /dev/null +++ b/relays/utils/src/metrics/global.rs @@ -0,0 +1,118 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Global system-wide Prometheus metrics exposed by relays. + +use crate::metrics::{ + metric_name, register, Gauge, GaugeVec, Metric, Opts, PrometheusError, Registry, + StandaloneMetric, F64, U64, +}; + +use async_std::sync::{Arc, Mutex}; +use async_trait::async_trait; +use std::time::Duration; +use sysinfo::{ProcessExt, RefreshKind, System, SystemExt}; + +/// Global metrics update interval. +const UPDATE_INTERVAL: Duration = Duration::from_secs(10); + +/// Global Prometheus metrics. +#[derive(Debug, Clone)] +pub struct GlobalMetrics { + system: Arc>, + system_average_load: GaugeVec, + process_cpu_usage_percentage: Gauge, + process_memory_usage_bytes: Gauge, +} + +impl GlobalMetrics { + /// Create and register global metrics. + pub fn new() -> Result { + Ok(GlobalMetrics { + system: Arc::new(Mutex::new(System::new_with_specifics(RefreshKind::everything()))), + system_average_load: GaugeVec::new( + Opts::new(metric_name(None, "system_average_load"), "System load average"), + &["over"], + )?, + process_cpu_usage_percentage: Gauge::new( + metric_name(None, "process_cpu_usage_percentage"), + "Process CPU usage", + )?, + process_memory_usage_bytes: Gauge::new( + metric_name(None, "process_memory_usage_bytes"), + "Process memory (resident set size) usage", + )?, + }) + } +} + +impl Metric for GlobalMetrics { + fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { + register(self.system_average_load.clone(), registry)?; + register(self.process_cpu_usage_percentage.clone(), registry)?; + register(self.process_memory_usage_bytes.clone(), registry)?; + Ok(()) + } +} + +#[async_trait] +impl StandaloneMetric for GlobalMetrics { + async fn update(&self) { + // update system-wide metrics + let mut system = self.system.lock().await; + let load = system.get_load_average(); + self.system_average_load.with_label_values(&["1min"]).set(load.one); + self.system_average_load.with_label_values(&["5min"]).set(load.five); + self.system_average_load.with_label_values(&["15min"]).set(load.fifteen); + + // update process-related metrics + let pid = sysinfo::get_current_pid().expect( + "only fails where pid is unavailable (os=unknown || arch=wasm32);\ + relay is not supposed to run in such MetricsParamss;\ + qed", + ); + let is_process_refreshed = system.refresh_process(pid); + match (is_process_refreshed, system.get_process(pid)) { + (true, Some(process_info)) => { + let cpu_usage = process_info.cpu_usage() as f64; + let memory_usage = process_info.memory() * 1024; + log::trace!( + target: "bridge-metrics", + "Refreshed process metrics: CPU={}, memory={}", + cpu_usage, + memory_usage, + ); + + self.process_cpu_usage_percentage.set(if cpu_usage.is_finite() { + cpu_usage + } else { + 0f64 + }); + self.process_memory_usage_bytes.set(memory_usage); + }, + _ => { + log::warn!( + target: "bridge-metrics", + "Failed to refresh process information. Metrics may show obsolete values", + ); + }, + } + } + + fn update_interval(&self) -> Duration { + UPDATE_INTERVAL + } +} diff --git a/relays/utils/src/relay_loop.rs b/relays/utils/src/relay_loop.rs new file mode 100644 index 00000000000..11e14744a07 --- /dev/null +++ b/relays/utils/src/relay_loop.rs @@ -0,0 +1,269 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{ + error::Error, + metrics::{Metric, MetricsAddress, MetricsParams}, + FailedClient, MaybeConnectionError, +}; + +use async_trait::async_trait; +use std::{fmt::Debug, future::Future, net::SocketAddr, time::Duration}; +use substrate_prometheus_endpoint::{init_prometheus, Registry}; + +/// Default pause between reconnect attempts. +pub const RECONNECT_DELAY: Duration = Duration::from_secs(10); + +/// Basic blockchain client from relay perspective. +#[async_trait] +pub trait Client: 'static + Clone + Send + Sync { + /// Type of error these clients returns. + type Error: 'static + Debug + MaybeConnectionError + Send + Sync; + + /// Try to reconnect to source node. + async fn reconnect(&mut self) -> Result<(), Self::Error>; +} + +#[async_trait] +impl Client for () { + type Error = crate::StringifiedMaybeConnectionError; + + async fn reconnect(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} + +/// Returns generic loop that may be customized and started. +pub fn relay_loop(source_client: SC, target_client: TC) -> Loop { + Loop { reconnect_delay: RECONNECT_DELAY, source_client, target_client, loop_metric: None } +} + +/// Returns generic relay loop metrics that may be customized and used in one or several relay +/// loops. +pub fn relay_metrics(params: MetricsParams) -> LoopMetrics<(), (), ()> { + LoopMetrics { + relay_loop: Loop { + reconnect_delay: RECONNECT_DELAY, + source_client: (), + target_client: (), + loop_metric: None, + }, + address: params.address, + registry: params.registry, + loop_metric: None, + } +} + +/// Generic relay loop. +pub struct Loop { + reconnect_delay: Duration, + source_client: SC, + target_client: TC, + loop_metric: Option, +} + +/// Relay loop metrics builder. +pub struct LoopMetrics { + relay_loop: Loop, + address: Option, + registry: Registry, + loop_metric: Option, +} + +impl Loop { + /// Customize delay between reconnect attempts. + #[must_use] + pub fn reconnect_delay(mut self, reconnect_delay: Duration) -> Self { + self.reconnect_delay = reconnect_delay; + self + } + + /// Start building loop metrics using given prefix. + pub fn with_metrics(self, params: MetricsParams) -> LoopMetrics { + LoopMetrics { + relay_loop: Loop { + reconnect_delay: self.reconnect_delay, + source_client: self.source_client, + target_client: self.target_client, + loop_metric: None, + }, + address: params.address, + registry: params.registry, + loop_metric: None, + } + } + + /// Run relay loop. + /// + /// This function represents an outer loop, which in turn calls provided `run_loop` function to + /// do actual job. When `run_loop` returns, this outer loop reconnects to failed client (source, + /// target or both) and calls `run_loop` again. + pub async fn run(mut self, loop_name: String, run_loop: R) -> Result<(), Error> + where + R: 'static + Send + Fn(SC, TC, Option) -> F, + F: 'static + Send + Future>, + SC: 'static + Client, + TC: 'static + Client, + LM: 'static + Send + Clone, + { + let run_loop_task = async move { + crate::initialize::initialize_loop(loop_name); + + loop { + let loop_metric = self.loop_metric.clone(); + let future_result = + run_loop(self.source_client.clone(), self.target_client.clone(), loop_metric); + let result = future_result.await; + + match result { + Ok(()) => break, + Err(failed_client) => + reconnect_failed_client( + failed_client, + self.reconnect_delay, + &mut self.source_client, + &mut self.target_client, + ) + .await, + } + + log::debug!(target: "bridge", "Restarting relay loop"); + } + + Ok(()) + }; + + async_std::task::spawn(run_loop_task).await + } +} + +impl LoopMetrics { + /// Add relay loop metrics. + /// + /// Loop metrics will be passed to the loop callback. + pub fn loop_metric( + self, + metric: NewLM, + ) -> Result, Error> { + metric.register(&self.registry)?; + + Ok(LoopMetrics { + relay_loop: self.relay_loop, + address: self.address, + registry: self.registry, + loop_metric: Some(metric), + }) + } + + /// Convert into `MetricsParams` structure so that metrics registry may be extended later. + pub fn into_params(self) -> MetricsParams { + MetricsParams { address: self.address, registry: self.registry } + } + + /// Expose metrics using address passed at creation. + /// + /// If passed `address` is `None`, metrics are not exposed. + pub async fn expose(self) -> Result, Error> { + if let Some(address) = self.address { + let socket_addr = SocketAddr::new( + address + .host + .parse() + .map_err(|err| Error::ExposingMetricsInvalidHost(address.host.clone(), err))?, + address.port, + ); + + let registry = self.registry; + async_std::task::spawn(async move { + let runtime = + match tokio::runtime::Builder::new_current_thread().enable_all().build() { + Ok(runtime) => runtime, + Err(err) => { + log::trace!( + target: "bridge-metrics", + "Failed to create tokio runtime. Prometheus meterics are not available: {:?}", + err, + ); + return + }, + }; + + runtime.block_on(async move { + log::trace!( + target: "bridge-metrics", + "Starting prometheus endpoint at: {:?}", + socket_addr, + ); + let result = init_prometheus(socket_addr, registry).await; + log::trace!( + target: "bridge-metrics", + "Prometheus endpoint has exited with result: {:?}", + result, + ); + }); + }); + } + + Ok(Loop { + reconnect_delay: self.relay_loop.reconnect_delay, + source_client: self.relay_loop.source_client, + target_client: self.relay_loop.target_client, + loop_metric: self.loop_metric, + }) + } +} + +/// Deal with the client who has returned connection error. +pub async fn reconnect_failed_client( + failed_client: FailedClient, + reconnect_delay: Duration, + source_client: &mut impl Client, + target_client: &mut impl Client, +) { + loop { + async_std::task::sleep(reconnect_delay).await; + if failed_client == FailedClient::Both || failed_client == FailedClient::Source { + match source_client.reconnect().await { + Ok(()) => (), + Err(error) => { + log::warn!( + target: "bridge", + "Failed to reconnect to source client. Going to retry in {}s: {:?}", + reconnect_delay.as_secs(), + error, + ); + continue + }, + } + } + if failed_client == FailedClient::Both || failed_client == FailedClient::Target { + match target_client.reconnect().await { + Ok(()) => (), + Err(error) => { + log::warn!( + target: "bridge", + "Failed to reconnect to target client. Going to retry in {}s: {:?}", + reconnect_delay.as_secs(), + error, + ); + continue + }, + } + } + + break + } +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 00000000000..082150daf04 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,24 @@ +# Basic +hard_tabs = true +max_width = 100 +use_small_heuristics = "Max" +# Imports +imports_granularity = "Crate" +reorder_imports = true +# Consistency +newline_style = "Unix" +# Format comments +comment_width = 100 +wrap_comments = true +# Misc +chain_width = 80 +spaces_around_ranges = false +binop_separator = "Back" +reorder_impl_items = false +match_arm_leading_pipes = "Preserve" +match_arm_blocks = false +match_block_trailing_comma = true +trailing_comma = "Vertical" +trailing_semicolon = false +use_field_init_shorthand = true + diff --git a/scripts/add_license.sh b/scripts/add_license.sh new file mode 100755 index 00000000000..49864b47c05 --- /dev/null +++ b/scripts/add_license.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +PAT_GPL="^// Copyright.*If not, see \.$" +PAT_OTHER="^// Copyright" + +SCRIPTS_DIR=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) + +for f in $(find . -type f | egrep '\.(c|cpp|rs)$'); do + HEADER=$(head -16 $f) + if [[ $HEADER =~ $PAT_GPL ]]; then + BODY=$(tail -n +17 $f) + cat $SCRIPTS_DIR/license_header > temp + echo "$BODY" >> temp + mv temp $f + elif [[ $HEADER =~ $PAT_OTHER ]]; then + echo "Other license was found do nothing" + else + echo "$f was missing header" + cat $SCRIPTS_DIR/license_header $f > temp + mv temp $f + fi +done diff --git a/scripts/build-containers.sh b/scripts/build-containers.sh new file mode 100755 index 00000000000..d0af254d93f --- /dev/null +++ b/scripts/build-containers.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -eux + +time docker build . -t local/substrate-relay --build-arg=PROJECT=substrate-relay +time docker build . -t local/rialto-bridge-node --build-arg=PROJECT=rialto-bridge-node +time docker build . -t local/millau-bridge-node --build-arg=PROJECT=millau-bridge-node +time docker build . -t local/rialto-parachain-collator --build-arg=PROJECT=rialto-parachain-collator diff --git a/scripts/ci-cache.sh b/scripts/ci-cache.sh new file mode 100755 index 00000000000..040d44fa74a --- /dev/null +++ b/scripts/ci-cache.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -xeu + +echo $CARGO_TARGET_DIR; +mkdir -p $CARGO_TARGET_DIR; +echo "Current Rust nightly version:"; +rustc +nightly --version; +echo "Cached Rust nightly version:"; +if [ ! -f $CARGO_TARGET_DIR/check_nightly_rust ]; then + echo "" > $CARGO_TARGET_DIR/check_nightly_rust; +fi +cat $CARGO_TARGET_DIR/check_nightly_rust; +if [[ $(cat $CARGO_TARGET_DIR/check_nightly_rust) == $(rustc +nightly --version) ]]; then + echo "The Rust nightly version has not changed"; +else + echo "The Rust nightly version has changed. Clearing the cache"; + rm -rf $CARGO_TARGET_DIR/*; +fi diff --git a/scripts/dump-logs.sh b/scripts/dump-logs.sh new file mode 100755 index 00000000000..51e8518872a --- /dev/null +++ b/scripts/dump-logs.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# A script to dump logs from selected important docker containers +# to make it easier to analyze locally. + +set -xeu + +DATE=$(date +"%Y-%m-%d-%T") +LOGS_DIR="${DATE//:/-}-logs" +mkdir $LOGS_DIR +cd $LOGS_DIR + +# From $ docker ps --format '{{.Names}}' + +SERVICES=(\ + deployments_relay-messages-millau-to-rialto-generator_1 \ + deployments_relay-messages-rialto-to-millau-generator_1 \ + deployments_relay-messages-millau-to-rialto-lane-00000001_1 \ + deployments_relay-messages-rialto-to-millau-lane-00000001_1 \ + deployments_relay-millau-rialto_1 \ + deployments_relay-headers-westend-to-millau-1_1 \ + deployments_relay-headers-westend-to-millau-2_1 \ + deployments_relay-parachains-westend-to-millau-1_1 \ + deployments_relay-parachains-westend-to-millau-1_2 \ + deployments_relay-messages-millau-to-rialto-parachain-generator_1 \ + deployments_relay-messages-rialto-parachain-to-millau-generator_1 \ + deployments_relay-millau-rialto-parachain-1_1 \ + deployments_relay-millau-rialto-parachain-2_1 \ + deployments_rialto-node-alice_1 \ + deployments_rialto-node-bob_1 \ + deployments_millau-node-alice_1 \ + deployments_millau-node-bob_1 \ + deployments_rialto-parachain-collator-alice_1 \ + deployments_rialto-parachain-collator-bob_1 \ + deployments_relay-messages-millau-to-rialto-resubmitter_1 \ + deployments_relay-messages-millau-to-rialto-parachain-resubmitter_1 \ +) + +for SVC in ${SERVICES[*]} +do + SHORT_NAME="${SVC//deployments_/}" + docker logs $SVC &> $SHORT_NAME.log | true +done + +cd - +tar cvjf $LOGS_DIR.tar.bz2 $LOGS_DIR diff --git a/scripts/license_header b/scripts/license_header new file mode 100644 index 00000000000..f9b301209bb --- /dev/null +++ b/scripts/license_header @@ -0,0 +1,16 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + diff --git a/scripts/send-message-from-millau-rialto.sh b/scripts/send-message-from-millau-rialto.sh new file mode 100755 index 00000000000..539ca5fc06c --- /dev/null +++ b/scripts/send-message-from-millau-rialto.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Used for manually sending a message to a running network. +# +# You could for example spin up a full network using the Docker Compose files +# we have (to make sure the message relays are running), but remove the message +# generator service. From there you may submit messages manually using this script. + +# TODO: Fix demeo scripts https://github.com/paritytech/parity-bridges-common/issues/1406 + +MILLAU_PORT="${RIALTO_PORT:-9945}" + +case "$1" in + remark) + RUST_LOG=runtime=trace,substrate-relay=trace,bridge=trace \ + ./target/debug/substrate-relay send-message millau-to-rialto \ + --source-host localhost \ + --source-port $MILLAU_PORT \ + --source-signer //Alice \ + raw 020419ac + ;; + transfer) + RUST_LOG=runtime=trace,substrate-relay=trace,bridge=trace \ + ./target/debug/substrate-relay send-message millau-to-rialto \ + --source-host localhost \ + --source-port $MILLAU_PORT \ + --source-signer //Alice \ + transfer \ + --amount 100000000000000 \ + --recipient 5DZvVvd1udr61vL7Xks17TFQ4fi9NiagYLaBobnbPCP14ewA \ + ;; + *) echo "A message type is require. Supported messages: remark, transfer."; exit 1;; +esac diff --git a/scripts/send-message-from-rialto-millau.sh b/scripts/send-message-from-rialto-millau.sh new file mode 100755 index 00000000000..923f588ea47 --- /dev/null +++ b/scripts/send-message-from-rialto-millau.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Used for manually sending a message to a running network. +# +# You could for example spin up a full network using the Docker Compose files +# we have (to make sure the message relays are running), but remove the message +# generator service. From there you may submit messages manually using this script. + +# TODO: Fix demeo scripts https://github.com/paritytech/parity-bridges-common/issues/1406 + +RIALTO_PORT="${RIALTO_PORT:-9944}" + +case "$1" in + remark) + RUST_LOG=runtime=trace,substrate-relay=trace,bridge=trace \ + ./target/debug/substrate-relay send-message rialto-to-millau \ + --source-host localhost \ + --source-port $RIALTO_PORT \ + --source-signer //Bob \ + raw 020419ac + ;; + transfer) + RUST_LOG=runtime=trace,substrate-relay=trace,bridge=trace \ + ./target/debug/substrate-relay send-message rialto-to-millau \ + --source-host localhost \ + --source-port $RIALTO_PORT \ + --source-signer //Bob \ + transfer \ + --amount 100000000000000 \ + --recipient 5DZvVvd1udr61vL7Xks17TFQ4fi9NiagYLaBobnbPCP14ewA \ + ;; + *) echo "A message type is require. Supported messages: remark, transfer."; exit 1;; +esac diff --git a/scripts/update-weights-setup.sh b/scripts/update-weights-setup.sh new file mode 100644 index 00000000000..72534423d63 --- /dev/null +++ b/scripts/update-weights-setup.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -exu + +# Set up the standardized machine and run `update-weights.sh` script. +# The system is assumed to be pristine Ubuntu 20.04 and we install +# all required dependencies. + +# To avoid interruptions you might want to run this script in `screen` cause it will take a while +# to finish. + +# We start off with upgrading the system +apt update && apt dist-upgrade + +# and installing `git` and other required deps. +apt install -y git clang curl libssl-dev llvm libudev-dev screen + +# Now we clone the repository +git clone https://github.com/paritytech/parity-bridges-common.git +cd parity-bridges-common + +# Install rustup & toolchain +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | bash -s -- -y + +# Source config +source ~/.cargo/env + +# Add nightly and WASM +rustup install nightly +rustup target add wasm32-unknown-unknown --toolchain nightly + +# Update the weights +./scripts/update-weights.sh diff --git a/scripts/update-weights.sh b/scripts/update-weights.sh new file mode 100755 index 00000000000..ac24da62b3c --- /dev/null +++ b/scripts/update-weights.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# +# Runtime benchmarks for the `pallet-bridge-messages` and `pallet-bridge-grandpa` pallets. +# +# Run this script from root of the repo. + +set -eux + +time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet \ + --chain=dev \ + --steps=50 \ + --repeat=20 \ + --pallet=pallet_bridge_messages \ + --extrinsic=* \ + --execution=wasm \ + --wasm-execution=Compiled \ + --heap-pages=4096 \ + --output=./modules/messages/src/weights.rs \ + --template=./.maintain/millau-weight-template.hbs + +time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet \ + --chain=dev \ + --steps=50 \ + --repeat=20 \ + --pallet=pallet_bridge_grandpa \ + --extrinsic=* \ + --execution=wasm \ + --wasm-execution=Compiled \ + --heap-pages=4096 \ + --output=./modules/grandpa/src/weights.rs \ + --template=./.maintain/millau-weight-template.hbs + +time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet \ + --chain=dev \ + --steps=50 \ + --repeat=20 \ + --pallet=pallet_bridge_parachains \ + --extrinsic=* \ + --execution=wasm \ + --wasm-execution=Compiled \ + --heap-pages=4096 \ + --output=./modules/parachains/src/weights.rs \ + --template=./.maintain/millau-weight-template.hbs + +time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet \ + --chain=dev \ + --steps=50 \ + --repeat=20 \ + --pallet=pallet_bridge_relayers \ + --extrinsic=* \ + --execution=wasm \ + --wasm-execution=Compiled \ + --heap-pages=4096 \ + --output=./modules/relayers/src/weights.rs \ + --template=./.maintain/millau-weight-template.hbs diff --git a/scripts/update_substrate.sh b/scripts/update_substrate.sh new file mode 100755 index 00000000000..f7715bda5d1 --- /dev/null +++ b/scripts/update_substrate.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# One-liner to update between Substrate releases +# Usage: ./update_substrate.sh 2.0.0-rc6 2.0.0 +set -xeu + +OLD_VERSION=$1 +NEW_VERSION=$2 + +find . -type f -name 'Cargo.toml' -exec sed -i '' -e "s/$OLD_VERSION/$NEW_VERSION/g" {} \; From 605795293e95e11ea33879157e87fcd9961e4529 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sun, 4 Dec 2022 01:23:16 +0900 Subject: [PATCH 123/263] Add SafeCallFilter --- .../assets/statemine/src/xcm_config.rs | 91 ++++++++++++++++++- .../assets/statemint/src/xcm_config.rs | 91 ++++++++++++++++++- .../assets/westmint/src/xcm_config.rs | 91 ++++++++++++++++++- .../bridge-hub-kusama/src/xcm_config.rs | 44 ++++++++- .../bridge-hub-rococo/src/xcm_config.rs | 44 ++++++++- .../collectives-polkadot/src/xcm_config.rs | 55 ++++++++++- .../contracts-rococo/src/xcm_config.rs | 1 + .../runtimes/starters/shell/src/xcm_config.rs | 7 +- .../runtimes/testing/penpal/src/xcm_config.rs | 1 + 9 files changed, 410 insertions(+), 15 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 9c37035bc35..06b9a9b2f98 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -40,7 +40,10 @@ use xcm_builder::{ SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, }; -use xcm_executor::{traits::JustTry, XcmExecutor}; +use xcm_executor::{ + traits::{JustTry, WithOriginFilter}, + XcmExecutor, +}; parameter_types! { pub const KsmLocation: MultiLocation = MultiLocation::parent(); @@ -142,6 +145,89 @@ match_types! { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(_) } }; + + // A call filter for the XCM Transact instruction. This is a temporary measure until we properly + // account for proof size weights. + // + // Calls that are allowed through this filter must: + // 1. Have a fixed weight; + // 2. Cannot lead to another call being made; + // 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. + pub type SafeCallFilter: impl Contains = { + RuntimeCall::System( + frame_system::Call::set_heap_pages { .. } | + frame_system::Call::set_code { .. } | + frame_system::Call::set_code_without_checks { .. } | + frame_system::Call::kill_prefix { .. } + ) | + RuntimeCall::ParachainSystem(..) | + RuntimeCall::Timestamp(..) | + RuntimeCall::Balances(..) | + RuntimeCall::CollatorSelection( + pallet_collator_selection::Call::set_desired_candidates { .. } | + pallet_collator_selection::Call::set_candidacy_bond { .. } | + pallet_collator_selection::Call::register_as_candidate { .. } | + pallet_collator_selection::Call::leave_intent { .. } + ) | + RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | + RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | + RuntimeCall::Assets( + pallet_assets::Call::create { .. } | + pallet_assets::Call::force_create { .. } | + pallet_assets::Call::start_destroy { .. } | + pallet_assets::Call::destroy_accounts { .. } | + pallet_assets::Call::destroy_approvals { .. } | + pallet_assets::Call::finish_destroy { .. } | + pallet_assets::Call::mint { .. } | + pallet_assets::Call::burn { .. } | + pallet_assets::Call::transfer { .. } | + pallet_assets::Call::transfer_keep_alive { .. } | + pallet_assets::Call::force_transfer { .. } | + pallet_assets::Call::freeze { .. } | + pallet_assets::Call::thaw { .. } | + pallet_assets::Call::freeze_asset { .. } | + pallet_assets::Call::thaw_asset { .. } | + pallet_assets::Call::transfer_ownership { .. } | + pallet_assets::Call::set_team { .. } | + pallet_assets::Call::clear_metadata { .. } | + pallet_assets::Call::force_clear_metadata { .. } | + pallet_assets::Call::force_asset_status { .. } | + pallet_assets::Call::approve_transfer { .. } | + pallet_assets::Call::cancel_approval { .. } | + pallet_assets::Call::force_cancel_approval { .. } | + pallet_assets::Call::transfer_approved { .. } | + pallet_assets::Call::touch { .. } | + pallet_assets::Call::refund { .. } + ) | + RuntimeCall::Uniques( + pallet_uniques::Call::create { .. } | + pallet_uniques::Call::force_create { .. } | + pallet_uniques::Call::destroy { .. } | + pallet_uniques::Call::mint { .. } | + pallet_uniques::Call::burn { .. } | + pallet_uniques::Call::transfer { .. } | + pallet_uniques::Call::freeze { .. } | + pallet_uniques::Call::thaw { .. } | + pallet_uniques::Call::freeze_collection { .. } | + pallet_uniques::Call::thaw_collection { .. } | + pallet_uniques::Call::transfer_ownership { .. } | + pallet_uniques::Call::set_team { .. } | + pallet_uniques::Call::approve_transfer { .. } | + pallet_uniques::Call::cancel_approval { .. } | + pallet_uniques::Call::force_item_status { .. } | + pallet_uniques::Call::set_attribute { .. } | + pallet_uniques::Call::clear_attribute { .. } | + pallet_uniques::Call::set_metadata { .. } | + pallet_uniques::Call::clear_metadata { .. } | + pallet_uniques::Call::set_collection_metadata { .. } | + pallet_uniques::Call::clear_collection_metadata { .. } | + pallet_uniques::Call::set_accept_ownership { .. } | + pallet_uniques::Call::set_collection_max_supply { .. } | + pallet_uniques::Call::set_price { .. } | + pallet_uniques::Call::buy_item { .. } + ) + }; } pub type Barrier = DenyThenTry< @@ -212,7 +298,8 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; - type CallDispatcher = RuntimeCall; + type CallDispatcher = WithOriginFilter; + type SafeCallFilter = SafeCallFilter; } /// Converts a local signed origin into an XCM multilocation. diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index a4a018721fb..25d77ab4d19 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -40,7 +40,10 @@ use xcm_builder::{ SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, }; -use xcm_executor::{traits::JustTry, XcmExecutor}; +use xcm_executor::{ + traits::{JustTry, WithOriginFilter}, + XcmExecutor, +}; parameter_types! { pub const DotLocation: MultiLocation = MultiLocation::parent(); @@ -142,6 +145,89 @@ match_types! { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(_) } }; + + // A call filter for the XCM Transact instruction. This is a temporary measure until we properly + // account for proof size weights. + // + // Calls that are allowed through this filter must: + // 1. Have a fixed weight; + // 2. Cannot lead to another call being made; + // 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. + pub type SafeCallFilter: impl Contains = { + RuntimeCall::System( + frame_system::Call::set_heap_pages { .. } | + frame_system::Call::set_code { .. } | + frame_system::Call::set_code_without_checks { .. } | + frame_system::Call::kill_prefix { .. } + ) | + RuntimeCall::ParachainSystem(..) | + RuntimeCall::Timestamp(..) | + RuntimeCall::Balances(..) | + RuntimeCall::CollatorSelection( + pallet_collator_selection::Call::set_desired_candidates { .. } | + pallet_collator_selection::Call::set_candidacy_bond { .. } | + pallet_collator_selection::Call::register_as_candidate { .. } | + pallet_collator_selection::Call::leave_intent { .. } + ) | + RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | + RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | + RuntimeCall::Assets( + pallet_assets::Call::create { .. } | + pallet_assets::Call::force_create { .. } | + pallet_assets::Call::start_destroy { .. } | + pallet_assets::Call::destroy_accounts { .. } | + pallet_assets::Call::destroy_approvals { .. } | + pallet_assets::Call::finish_destroy { .. } | + pallet_assets::Call::mint { .. } | + pallet_assets::Call::burn { .. } | + pallet_assets::Call::transfer { .. } | + pallet_assets::Call::transfer_keep_alive { .. } | + pallet_assets::Call::force_transfer { .. } | + pallet_assets::Call::freeze { .. } | + pallet_assets::Call::thaw { .. } | + pallet_assets::Call::freeze_asset { .. } | + pallet_assets::Call::thaw_asset { .. } | + pallet_assets::Call::transfer_ownership { .. } | + pallet_assets::Call::set_team { .. } | + pallet_assets::Call::clear_metadata { .. } | + pallet_assets::Call::force_clear_metadata { .. } | + pallet_assets::Call::force_asset_status { .. } | + pallet_assets::Call::approve_transfer { .. } | + pallet_assets::Call::cancel_approval { .. } | + pallet_assets::Call::force_cancel_approval { .. } | + pallet_assets::Call::transfer_approved { .. } | + pallet_assets::Call::touch { .. } | + pallet_assets::Call::refund { .. } + ) | + RuntimeCall::Uniques( + pallet_uniques::Call::create { .. } | + pallet_uniques::Call::force_create { .. } | + pallet_uniques::Call::destroy { .. } | + pallet_uniques::Call::mint { .. } | + pallet_uniques::Call::burn { .. } | + pallet_uniques::Call::transfer { .. } | + pallet_uniques::Call::freeze { .. } | + pallet_uniques::Call::thaw { .. } | + pallet_uniques::Call::freeze_collection { .. } | + pallet_uniques::Call::thaw_collection { .. } | + pallet_uniques::Call::transfer_ownership { .. } | + pallet_uniques::Call::set_team { .. } | + pallet_uniques::Call::approve_transfer { .. } | + pallet_uniques::Call::cancel_approval { .. } | + pallet_uniques::Call::force_item_status { .. } | + pallet_uniques::Call::set_attribute { .. } | + pallet_uniques::Call::clear_attribute { .. } | + pallet_uniques::Call::set_metadata { .. } | + pallet_uniques::Call::clear_metadata { .. } | + pallet_uniques::Call::set_collection_metadata { .. } | + pallet_uniques::Call::clear_collection_metadata { .. } | + pallet_uniques::Call::set_accept_ownership { .. } | + pallet_uniques::Call::set_collection_max_supply { .. } | + pallet_uniques::Call::set_price { .. } | + pallet_uniques::Call::buy_item { .. } + ) + }; } pub type Barrier = DenyThenTry< @@ -212,7 +298,8 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; - type CallDispatcher = RuntimeCall; + type CallDispatcher = WithOriginFilter; + type SafeCallFilter = SafeCallFilter; } /// Converts a local signed origin into an XCM multilocation. diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index c99b66bb5c3..6be182c181e 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -40,7 +40,10 @@ use xcm_builder::{ SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, }; -use xcm_executor::{traits::JustTry, XcmExecutor}; +use xcm_executor::{ + traits::{JustTry, WithOriginFilter}, + XcmExecutor, +}; parameter_types! { pub const WestendLocation: MultiLocation = MultiLocation::parent(); @@ -138,6 +141,89 @@ match_types! { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(Plurality { .. }) } }; + + // A call filter for the XCM Transact instruction. This is a temporary measure until we properly + // account for proof size weights. + // + // Calls that are allowed through this filter must: + // 1. Have a fixed weight; + // 2. Cannot lead to another call being made; + // 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. + pub type SafeCallFilter: impl Contains = { + RuntimeCall::System( + frame_system::Call::set_heap_pages { .. } | + frame_system::Call::set_code { .. } | + frame_system::Call::set_code_without_checks { .. } | + frame_system::Call::kill_prefix { .. } + ) | + RuntimeCall::ParachainSystem(..) | + RuntimeCall::Timestamp(..) | + RuntimeCall::Balances(..) | + RuntimeCall::CollatorSelection( + pallet_collator_selection::Call::set_desired_candidates { .. } | + pallet_collator_selection::Call::set_candidacy_bond { .. } | + pallet_collator_selection::Call::register_as_candidate { .. } | + pallet_collator_selection::Call::leave_intent { .. } + ) | + RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | + RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | + RuntimeCall::Assets( + pallet_assets::Call::create { .. } | + pallet_assets::Call::force_create { .. } | + pallet_assets::Call::start_destroy { .. } | + pallet_assets::Call::destroy_accounts { .. } | + pallet_assets::Call::destroy_approvals { .. } | + pallet_assets::Call::finish_destroy { .. } | + pallet_assets::Call::mint { .. } | + pallet_assets::Call::burn { .. } | + pallet_assets::Call::transfer { .. } | + pallet_assets::Call::transfer_keep_alive { .. } | + pallet_assets::Call::force_transfer { .. } | + pallet_assets::Call::freeze { .. } | + pallet_assets::Call::thaw { .. } | + pallet_assets::Call::freeze_asset { .. } | + pallet_assets::Call::thaw_asset { .. } | + pallet_assets::Call::transfer_ownership { .. } | + pallet_assets::Call::set_team { .. } | + pallet_assets::Call::clear_metadata { .. } | + pallet_assets::Call::force_clear_metadata { .. } | + pallet_assets::Call::force_asset_status { .. } | + pallet_assets::Call::approve_transfer { .. } | + pallet_assets::Call::cancel_approval { .. } | + pallet_assets::Call::force_cancel_approval { .. } | + pallet_assets::Call::transfer_approved { .. } | + pallet_assets::Call::touch { .. } | + pallet_assets::Call::refund { .. } + ) | + RuntimeCall::Uniques( + pallet_uniques::Call::create { .. } | + pallet_uniques::Call::force_create { .. } | + pallet_uniques::Call::destroy { .. } | + pallet_uniques::Call::mint { .. } | + pallet_uniques::Call::burn { .. } | + pallet_uniques::Call::transfer { .. } | + pallet_uniques::Call::freeze { .. } | + pallet_uniques::Call::thaw { .. } | + pallet_uniques::Call::freeze_collection { .. } | + pallet_uniques::Call::thaw_collection { .. } | + pallet_uniques::Call::transfer_ownership { .. } | + pallet_uniques::Call::set_team { .. } | + pallet_uniques::Call::approve_transfer { .. } | + pallet_uniques::Call::cancel_approval { .. } | + pallet_uniques::Call::force_item_status { .. } | + pallet_uniques::Call::set_attribute { .. } | + pallet_uniques::Call::clear_attribute { .. } | + pallet_uniques::Call::set_metadata { .. } | + pallet_uniques::Call::clear_metadata { .. } | + pallet_uniques::Call::set_collection_metadata { .. } | + pallet_uniques::Call::clear_collection_metadata { .. } | + pallet_uniques::Call::set_accept_ownership { .. } | + pallet_uniques::Call::set_collection_max_supply { .. } | + pallet_uniques::Call::set_price { .. } | + pallet_uniques::Call::buy_item { .. } + ) + }; } pub type Barrier = DenyThenTry< @@ -208,7 +294,8 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; - type CallDispatcher = RuntimeCall; + type CallDispatcher = WithOriginFilter; + type SafeCallFilter = SafeCallFilter; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index 317655663f0..509027434c7 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -15,8 +15,8 @@ // along with Cumulus. If not, see . use super::{ - AccountId, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, }; use frame_support::{ match_types, parameter_types, @@ -36,7 +36,7 @@ use xcm_builder::{ SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; -use xcm_executor::XcmExecutor; +use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; parameter_types! { pub const KsmRelayLocation: MultiLocation = MultiLocation::parent(); @@ -97,6 +97,7 @@ parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. pub UnitWeightCost: u64 = 1_000_000_000; pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; } match_types! { @@ -109,6 +110,34 @@ match_types! { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(_) } }; + + // A call filter for the XCM Transact instruction. This is a temporary measure until we properly + // account for proof size weights. + // + // Calls that are allowed through this filter must: + // 1. Have a fixed weight; + // 2. Cannot lead to another call being made; + // 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. + pub type SafeCallFilter: impl Contains = { + RuntimeCall::System( + frame_system::Call::set_heap_pages { .. } | + frame_system::Call::set_code { .. } | + frame_system::Call::set_code_without_checks { .. } | + frame_system::Call::kill_prefix { .. } + ) | + RuntimeCall::ParachainSystem(..) | + RuntimeCall::Timestamp(..) | + RuntimeCall::Balances(..) | + RuntimeCall::CollatorSelection( + pallet_collator_selection::Call::set_desired_candidates { .. } | + pallet_collator_selection::Call::set_candidacy_bond { .. } | + pallet_collator_selection::Call::register_as_candidate { .. } | + pallet_collator_selection::Call::leave_intent { .. } + ) | + RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | + RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) + }; } pub type Barrier = DenyThenTry< @@ -148,6 +177,15 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = WithOriginFilter; + type SafeCallFilter = SafeCallFilter; } /// Converts a local signed origin into an XCM multilocation. diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 77b18b28cbb..9c2badb0674 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -15,8 +15,8 @@ // along with Cumulus. If not, see . use super::{ - AccountId, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, }; use frame_support::{ match_types, parameter_types, @@ -36,7 +36,7 @@ use xcm_builder::{ SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; -use xcm_executor::XcmExecutor; +use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); @@ -96,6 +96,7 @@ parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. pub UnitWeightCost: u64 = 1_000_000_000; pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; } match_types! { @@ -107,6 +108,34 @@ match_types! { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(_) } }; + + // A call filter for the XCM Transact instruction. This is a temporary measure until we properly + // account for proof size weights. + // + // Calls that are allowed through this filter must: + // 1. Have a fixed weight; + // 2. Cannot lead to another call being made; + // 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. + pub type SafeCallFilter: impl Contains = { + RuntimeCall::System( + frame_system::Call::set_heap_pages { .. } | + frame_system::Call::set_code { .. } | + frame_system::Call::set_code_without_checks { .. } | + frame_system::Call::kill_prefix { .. } + ) | + RuntimeCall::ParachainSystem(..) | + RuntimeCall::Timestamp(..) | + RuntimeCall::Balances(..) | + RuntimeCall::CollatorSelection( + pallet_collator_selection::Call::set_desired_candidates { .. } | + pallet_collator_selection::Call::set_candidacy_bond { .. } | + pallet_collator_selection::Call::register_as_candidate { .. } | + pallet_collator_selection::Call::leave_intent { .. } + ) | + RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | + RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) + }; } pub type Barrier = DenyThenTry< @@ -145,6 +174,15 @@ impl xcm_executor::Config for XcmConfig { type AssetTrap = PolkadotXcm; type AssetClaims = PolkadotXcm; type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = WithOriginFilter; + type SafeCallFilter = SafeCallFilter; } /// Converts a local signed origin into an XCM multilocation. diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index 6eebe074507..54fc60d4bce 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -36,7 +36,7 @@ use xcm_builder::{ SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; -use xcm_executor::XcmExecutor; +use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; parameter_types! { pub const DotLocation: MultiLocation = MultiLocation::parent(); @@ -113,6 +113,56 @@ match_types! { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(_) } }; + + // A call filter for the XCM Transact instruction. This is a temporary measure until we properly + // account for proof size weights. + // + // Calls that are allowed through this filter must: + // 1. Have a fixed weight; + // 2. Cannot lead to another call being made; + // 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. + pub type SafeCallFilter: impl Contains = { + RuntimeCall::System( + frame_system::Call::set_heap_pages { .. } | + frame_system::Call::set_code { .. } | + frame_system::Call::set_code_without_checks { .. } | + frame_system::Call::kill_prefix { .. } + ) | + RuntimeCall::ParachainSystem(..) | + RuntimeCall::Timestamp(..) | + RuntimeCall::Balances(..) | + RuntimeCall::CollatorSelection( + pallet_collator_selection::Call::set_desired_candidates { .. } | + pallet_collator_selection::Call::set_candidacy_bond { .. } | + pallet_collator_selection::Call::register_as_candidate { .. } | + pallet_collator_selection::Call::leave_intent { .. } + ) | + RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | + RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | + RuntimeCall::Alliance( + pallet_alliance::Call::vote { .. } | + pallet_alliance::Call::close_old_weight { .. } | + pallet_alliance::Call::disband { .. } | + pallet_alliance::Call::set_rule { .. } | + pallet_alliance::Call::announce { .. } | + pallet_alliance::Call::remove_announcement { .. } | + pallet_alliance::Call::join_alliance { .. } | + pallet_alliance::Call::nominate_ally { .. } | + pallet_alliance::Call::elevate_ally { .. } | + pallet_alliance::Call::give_retirement_notice { .. } | + pallet_alliance::Call::retire { .. } | + pallet_alliance::Call::kick_member { .. } | + pallet_alliance::Call::close { .. } | + pallet_alliance::Call::abdicate_fellow_status { .. } + ) | + RuntimeCall::AllianceMotion( + pallet_collective::Call::vote { .. } | + pallet_collective::Call::close_old_weight { .. } | + pallet_collective::Call::disapprove_proposal { .. } | + pallet_collective::Call::close { .. } + ) + }; } pub type Barrier = DenyThenTry< @@ -158,7 +208,8 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; - type CallDispatcher = RuntimeCall; + type CallDispatcher = WithOriginFilter; + type SafeCallFilter = SafeCallFilter; } /// Converts a local signed origin into an XCM multilocation. diff --git a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index 2cfb3e56729..70bdcc31f4b 100644 --- a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -156,6 +156,7 @@ impl xcm_executor::Config for XcmConfig { type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; } /// Converts a local signed origin into an XCM multilocation. diff --git a/parachains/runtimes/starters/shell/src/xcm_config.rs b/parachains/runtimes/starters/shell/src/xcm_config.rs index 1bbdf15b44f..be83cb9fbfd 100644 --- a/parachains/runtimes/starters/shell/src/xcm_config.rs +++ b/parachains/runtimes/starters/shell/src/xcm_config.rs @@ -17,7 +17,11 @@ use super::{ AccountId, AllPalletsWithSystem, ParachainInfo, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, }; -use frame_support::{match_types, parameter_types, traits::Nothing, weights::Weight}; +use frame_support::{ + match_types, parameter_types, + traits::{Everything, Nothing}, + weights::Weight, +}; use xcm::latest::prelude::*; use xcm_builder::{ AllowUnpaidExecutionFrom, FixedWeightBounds, ParentAsSuperuser, ParentIsPreset, @@ -78,6 +82,7 @@ impl xcm_executor::Config for XcmConfig { type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index 56956d1f885..87c8569f552 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -281,6 +281,7 @@ impl xcm_executor::Config for XcmConfig { type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. From 33404f238f377951a34401dfa0044d16a4eae719 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sun, 4 Dec 2022 01:36:39 +0900 Subject: [PATCH 124/263] Add missing config items --- .../bridge-hub-kusama/src/xcm_config.rs | 26 ++++++++++++------- .../bridge-hub-rococo/src/xcm_config.rs | 26 ++++++++++++------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index 509027434c7..2053f805cc9 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -20,7 +20,8 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{Everything, Nothing}, + traits::{ConstU32, Everything, Nothing}, + weights::Weight, }; use pallet_xcm::XcmPassthrough; use parachains_common::xcm_config::{ @@ -32,17 +33,18 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FixedWeightBounds, IsConcrete, LocationInverter, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + FixedWeightBounds, IsConcrete, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; parameter_types! { pub const KsmRelayLocation: MultiLocation = MultiLocation::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Kusama; + pub const RelayNetwork: Option = Some(NetworkId::Kusama); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); + pub UniversalLocation: InteriorMultiLocation = + Parachain(ParachainInfo::parachain_id().into()).into(); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used @@ -95,7 +97,7 @@ pub type XcmOriginToTransactDispatchOrigin = ( parameter_types! { // TODO: change to meassured weights - https://github.com/paritytech/parity-bridges-common/issues/391 // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: u64 = 1_000_000_000; + pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } @@ -167,7 +169,7 @@ impl xcm_executor::Config for XcmConfig { type IsReserve = (); /// Only allow teleportation of KSM. type IsTeleporter = ConcreteNativeAssetFrom; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Barrier = Barrier; // TODO: change to meassured weights - https://github.com/paritytech/parity-bridges-common/issues/391 type Weigher = FixedWeightBounds; @@ -216,11 +218,17 @@ impl pallet_xcm::Config for Runtime { // TODO: change to meassured weights - https://github.com/paritytech/parity-bridges-common/issues/391 type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 9c2badb0674..d6bd800789b 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -20,7 +20,8 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{Everything, Nothing}, + traits::{ConstU32, Everything, Nothing}, + weights::Weight, }; use pallet_xcm::XcmPassthrough; use parachains_common::xcm_config::{ @@ -32,17 +33,18 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FixedWeightBounds, IsConcrete, LocationInverter, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + FixedWeightBounds, IsConcrete, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Any; + pub const RelayNetwork: Option = None; pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); + pub UniversalLocation: InteriorMultiLocation = + Parachain(ParachainInfo::parachain_id().into()).into(); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used @@ -94,7 +96,7 @@ pub type XcmOriginToTransactDispatchOrigin = ( parameter_types! { // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: u64 = 1_000_000_000; + pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } @@ -165,7 +167,7 @@ impl xcm_executor::Config for XcmConfig { type IsReserve = (); /// Only allow teleportation of NativeToken of relay chain. type IsTeleporter = ConcreteNativeAssetFrom; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; type Trader = @@ -211,11 +213,17 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Nothing; // This parachain is not meant as a reserve location. type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; + type UniversalLocation = UniversalLocation; type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; } impl cumulus_pallet_xcm::Config for Runtime { From ff250267395998a7d150e5e46182bc1af9625455 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sun, 4 Dec 2022 01:44:52 +0900 Subject: [PATCH 125/263] Add TODO --- .../runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs | 2 +- .../runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index 2053f805cc9..cb78aec275f 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -198,7 +198,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // TODO: Figure out 3rd type arg // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index d6bd800789b..8d924f93b33 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -195,7 +195,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // TODO: Figure out 3rd type arg // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); From 9982003d5893c20c71fe184091c0543f0f415ca1 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 5 Dec 2022 10:56:28 +0100 Subject: [PATCH 126/263] Fixes (xcm Superuser + DispatchLevelResult) --- parachains/chain-specs/bridge-hub-rococo.json | 6 +++- parachains/chain-specs/bridge-hub-wococo.json | 8 +++-- .../src/bridge_common_config.rs | 35 +++++++++++++------ .../bridge-hub-rococo/src/xcm_config.rs | 19 +++++----- 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/parachains/chain-specs/bridge-hub-rococo.json b/parachains/chain-specs/bridge-hub-rococo.json index e94373a11f2..6bc55f916b7 100644 --- a/parachains/chain-specs/bridge-hub-rococo.json +++ b/parachains/chain-specs/bridge-hub-rococo.json @@ -14,7 +14,11 @@ ], "telemetryEndpoints": null, "protocolId": null, - "properties": {}, + "properties": { + "ss58Format": 42, + "tokenDecimals": 12, + "tokenSymbol": "ROC" + }, "relay_chain": "rococo", "para_id": 1013, "codeSubstitutes": {}, diff --git a/parachains/chain-specs/bridge-hub-wococo.json b/parachains/chain-specs/bridge-hub-wococo.json index bf41ef82154..dd02842f2f4 100644 --- a/parachains/chain-specs/bridge-hub-wococo.json +++ b/parachains/chain-specs/bridge-hub-wococo.json @@ -14,7 +14,11 @@ ], "telemetryEndpoints": null, "protocolId": null, - "properties": {}, + "properties": { + "ss58Format": 42, + "tokenDecimals": 12, + "tokenSymbol": "WOOK" + }, "relay_chain": "wococo", "para_id": 1014, "codeSubstitutes": {}, @@ -86,4 +90,4 @@ "childrenDefault": {} } } -} \ No newline at end of file +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index fd1672d9fcb..a035e3554c6 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -20,8 +20,9 @@ use bp_messages::{ LaneId, }; use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, Chain}; -use codec::Encode; -use frame_support::{dispatch::Weight, parameter_types}; +use codec::{Decode, Encode}; +use frame_support::{dispatch::Weight, parameter_types, CloneNoBound, EqNoBound, PartialEqNoBound}; +use scale_info::TypeInfo; use xcm::latest::prelude::*; use xcm_builder::{DispatchBlob, DispatchBlobError, HaulBlob}; @@ -36,6 +37,13 @@ parameter_types! { pub const HeadersToKeep: u32 = 1024; } +#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] +pub enum XcmBlobMessageDispatchResult { + InvalidPayload, + Dispatched, + NotDispatched(#[codec(skip)] &'static str), +} + /// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages from other BridgeHub pub struct XcmBlobMessageDispatch { _marker: @@ -47,6 +55,7 @@ impl { type DispatchPayload = XcmAsPlainPayload; + type DispatchLevelResult = XcmBlobMessageDispatchResult; fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { log::error!( @@ -60,7 +69,7 @@ impl, message: DispatchMessage, - ) -> MessageDispatchResult { + ) -> MessageDispatchResult { log::warn!( target: crate::LOG_TARGET, "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob triggering - message_nonce: {:?}", @@ -79,15 +88,19 @@ impl log::debug!( - target: crate::LOG_TARGET, - "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}", - message.key.nonce - ), + let dispatch_level_result = match BlobDispatcher::dispatch_blob(payload) { + Ok(_) => { + log::debug!( + target: crate::LOG_TARGET, + "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}", + message.key.nonce + ); + XcmBlobMessageDispatchResult::Dispatched + }, Err(e) => { let e = match e { DispatchBlobError::Unbridgable => "DispatchBlobError::Unbridgable", @@ -106,12 +119,14 @@ impl, - // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when - // recognized. + // Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when + // recognised. RelayChainAsNative, // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when - // recognized. + // recognised. SiblingParachainAsNative, + // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a + // transaction from the Root origin. + ParentAsSuperuser, // Native signed account converter; this just converts an `AccountId32` origin into a normal - // `Origin::Signed` origin of the same 32-byte value. + // `RuntimeOrigin::Signed` origin of the same 32-byte value. SignedAccountId32AsNative, // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. XcmPassthrough, From 46858d6da92363f4860b29da7e96b831190ffeff Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 5 Dec 2022 11:16:54 +0100 Subject: [PATCH 127/263] Fix cargo --- Cargo.lock | 52 ++++++++++++---------------------------------------- Cargo.toml | 4 ++++ 2 files changed, 16 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5c3d6d46531..be9714f442c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -854,6 +854,7 @@ dependencies = [ name = "bp-relayers" version = "0.1.0" dependencies = [ + "bp-messages", "frame-support", "hex", "hex-literal", @@ -4443,7 +4444,7 @@ dependencies = [ "pallet-vesting", "pallet-whitelist", "pallet-xcm", - "pallet-xcm-benchmarks 0.9.31 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "pallet-xcm-benchmarks", "parity-scale-codec", "polkadot-primitives", "polkadot-runtime-common", @@ -6959,23 +6960,6 @@ dependencies = [ "xcm-executor", ] -[[package]] -name = "pallet-xcm-benchmarks" -version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "parity-scale-codec", - "scale-info", - "sp-runtime", - "sp-std", - "xcm", - "xcm-executor", -] - [[package]] name = "parachain-info" version = "0.1.0" @@ -8819,7 +8803,7 @@ dependencies = [ "sp-transaction-pool", "sp-version", "substrate-wasm-builder", - "test-runtime-constants 0.9.31 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "test-runtime-constants", "xcm", "xcm-builder", "xcm-executor", @@ -8828,7 +8812,7 @@ dependencies = [ [[package]] name = "polkadot-test-service" version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" dependencies = [ "frame-benchmarking", "frame-system", @@ -8874,7 +8858,7 @@ dependencies = [ "sp-state-machine", "substrate-test-client", "tempfile", - "test-runtime-constants 0.9.31 (git+https://github.com/paritytech/polkadot?branch=master)", + "test-runtime-constants", "tokio", "tracing-gum", ] @@ -9580,7 +9564,7 @@ dependencies = [ "pallet-utility", "pallet-vesting", "pallet-xcm", - "pallet-xcm-benchmarks 0.9.31 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "pallet-xcm-benchmarks", "parity-scale-codec", "polkadot-parachain 0.9.31", "polkadot-primitives", @@ -12161,7 +12145,7 @@ dependencies = [ "pallet-uniques", "pallet-utility", "pallet-xcm", - "pallet-xcm-benchmarks 0.9.31 (git+https://github.com/paritytech/polkadot?branch=master)", + "pallet-xcm-benchmarks", "parachain-info", "parachains-common", "parity-scale-codec", @@ -12225,7 +12209,7 @@ dependencies = [ "pallet-uniques", "pallet-utility", "pallet-xcm", - "pallet-xcm-benchmarks 0.9.31 (git+https://github.com/paritytech/polkadot?branch=master)", + "pallet-xcm-benchmarks", "parachain-info", "parachains-common", "parity-scale-codec", @@ -12470,7 +12454,7 @@ dependencies = [ [[package]] name = "substrate-test-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#4e1e17cccd499dfe49e8c1bed01957953aa4c839" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "futures", "substrate-test-utils-derive", @@ -12480,7 +12464,7 @@ dependencies = [ [[package]] name = "substrate-test-utils-derive" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#4e1e17cccd499dfe49e8c1bed01957953aa4c839" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -12608,18 +12592,6 @@ dependencies = [ "sp-runtime", ] -[[package]] -name = "test-runtime-constants" -version = "0.9.31" -source = "git+https://github.com/paritytech/polkadot?branch=master#40aefb4ac396bcd098755c6d57dac7b284a343e7" -dependencies = [ - "frame-support", - "polkadot-primitives", - "polkadot-runtime-common", - "smallvec", - "sp-runtime", -] - [[package]] name = "textwrap" version = "0.15.1" @@ -13697,7 +13669,7 @@ dependencies = [ "pallet-utility", "pallet-vesting", "pallet-xcm", - "pallet-xcm-benchmarks 0.9.31 (git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges)", + "pallet-xcm-benchmarks", "parity-scale-codec", "polkadot-parachain 0.9.31", "polkadot-primitives", @@ -13781,7 +13753,7 @@ dependencies = [ "pallet-uniques", "pallet-utility", "pallet-xcm", - "pallet-xcm-benchmarks 0.9.31 (git+https://github.com/paritytech/polkadot?branch=master)", + "pallet-xcm-benchmarks", "parachain-info", "parachains-common", "parity-scale-codec", diff --git a/Cargo.toml b/Cargo.toml index 925a43226d9..a0d0e4e3b6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -252,11 +252,13 @@ pallet-election-provider-support-benchmarking = { git = "https://github.com/pari pallet-nomination-pools-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } pallet-offences-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } pallet-session-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +substrate-test-utils = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } [patch."https://github.com/paritytech/polkadot"] kusama-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } kusama-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } pallet-xcm = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +pallet-xcm-benchmarks = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } polkadot-approval-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } polkadot-availability-bitfield-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } polkadot-availability-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } @@ -312,3 +314,5 @@ xcm = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-ga xcm-builder = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } xcm-executor = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } xcm-procedural = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +test-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +polkadot-test-service = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } From 0d69b256ad8bae8c335ea14fa9b80bde35b969b1 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 5 Dec 2022 12:42:54 +0100 Subject: [PATCH 128/263] Change runtime version --- parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 7d4f003ac2b..a398e177241 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -170,7 +170,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("bridge-hub-rococo"), impl_name: create_runtime_str!("bridge-hub-rococo"), authoring_version: 1, - spec_version: 9300, + spec_version: 9301, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 96b567a3d968113aa6873e8bfc49cf665fbeffec Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 5 Dec 2022 14:05:39 +0100 Subject: [PATCH 129/263] Unit-tests for dispatch bridging messages and XCM routing on BridgeHubs + HRMP --- Cargo.lock | 62 ++++++ .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 4 + .../bridge-hub-rococo/tests/tests.rs | 183 ++++++++++++++++++ .../bridge-hubs/test-utils/Cargo.toml | 50 +++++ .../bridge-hubs/test-utils/src/lib.rs | 171 ++++++++++++++++ .../bridge_hub_rococo_local_network.toml | 12 ++ .../bridge_hub_wococo_local_network.toml | 12 ++ 7 files changed, 494 insertions(+) create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs create mode 100644 parachains/runtimes/bridge-hubs/test-utils/Cargo.toml create mode 100644 parachains/runtimes/bridge-hubs/test-utils/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index be9714f442c..fa5f25f2bb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -965,6 +965,7 @@ dependencies = [ "bp-rococo", "bp-runtime", "bp-wococo", + "bridge-hub-test-utils", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", @@ -1008,6 +1009,7 @@ dependencies = [ "polkadot-runtime-constants", "scale-info", "serde", + "serial_test", "smallvec", "sp-api", "sp-block-builder", @@ -1027,6 +1029,27 @@ dependencies = [ "xcm-executor", ] +[[package]] +name = "bridge-hub-test-utils" +version = "0.1.0" +dependencies = [ + "bp-messages", + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-test-relay-sproof-builder", + "frame-support", + "frame-system", + "pallet-xcm", + "pallet-xcm-benchmarks", + "parachain-info", + "parachains-common", + "polkadot-parachain 0.9.31", + "xcm", + "xcm-builder", + "xcm-executor", +] + [[package]] name = "bridge-runtime-common" version = "0.1.0" @@ -2652,6 +2675,19 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if 1.0.0", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core 0.9.3", +] + [[package]] name = "data-encoding" version = "2.3.2" @@ -11078,6 +11114,32 @@ dependencies = [ "serde", ] +[[package]] +name = "serial_test" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92761393ee4dc3ff8f4af487bd58f4307c9329bbedea02cac0089ad9c411e153" +dependencies = [ + "dashmap", + "futures", + "lazy_static", + "log", + "parking_lot 0.12.1", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6f5d1c3087fb119617cff2966fe3808a80e5eb59a8c1601d5994d66f4346a5" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sha-1" version = "0.9.8" diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 25bdd1bcbe8..6adb8e1c15b 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -83,6 +83,10 @@ pallet-bridge-relayers = { path = "../../../../bridges/modules/relayers", defaul pallet-shift-session-manager = { path = "../../../../bridges/modules/shift-session-manager", default-features = false } bridge-runtime-common = { path = "../../../../bridges/bin/runtime-common", default-features = false } +[dev-dependencies] +serial_test = "0.9.0" +bridge-hub-test-utils = { path = "../test-utils"} + [features] default = [ "std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs new file mode 100644 index 00000000000..11d3fb9681d --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -0,0 +1,183 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +use bp_messages::target_chain::MessageDispatch; +use bp_runtime::messages::MessageDispatchResult; +use bridge_hub_rococo_runtime::bridge_common_config::XcmBlobMessageDispatchResult; +pub use bridge_hub_rococo_runtime::{ + runtime_api, + xcm_config::{XcmConfig, XcmRouter}, + Runtime, *, +}; +use xcm::latest::prelude::*; + +use bridge_hub_test_utils::*; + +fn execute_on_runtime( + with_para_id: u32, + open_hrmp_to_para_id: Option, + execute: impl FnOnce() -> R, +) -> R { + new_test_ext::(with_para_id.into(), 3).execute_with(|| { + if let Some(open_hrmp_to_para_id) = open_hrmp_to_para_id { + mock_open_hrmp_channel::( + with_para_id.into(), + open_hrmp_to_para_id.into(), + ); + } + execute() + }) +} + +#[test] +#[serial_test::serial] +fn test_bridge_hub_wococo_dispatch_blob_and_xcm_routing_works() { + let universal_source_as_senders = + vec![X1(GlobalConsensus(Rococo)), X2(GlobalConsensus(Rococo), Parachain(1000))]; + let runtime_para_id = bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID; + let destination_network_id = Wococo; + let destination_para_id = 1000; + + for univeral_source_as_sender in universal_source_as_senders { + // 1. message is sent to other global consensus - Wococo(Here) + let bridging_message = + simulate_export_message::( + univeral_source_as_sender, + destination_network_id, + Here, + dummy_xcm(), + ); + let result: MessageDispatchResult = execute_on_runtime( + runtime_para_id, + None, + || { + <>::MessageDispatch as MessageDispatch<_>>::dispatch( + &dummy_account(), + wrap_as_dispatch_message(bridging_message) + ) + }, + ); + assert_eq!(result.dispatch_level_result, XcmBlobMessageDispatchResult::Dispatched); + + // 2. message is sent to other global consensus and its parachains - Wococo(Here) + let bridging_message = + simulate_export_message::( + univeral_source_as_sender, + destination_network_id, + X1(Parachain(destination_para_id)), + dummy_xcm(), + ); + + // 2.1. WITHOUT hrmp channel -> RoutingError + let result: MessageDispatchResult = execute_on_runtime( + runtime_para_id, + None, + || { + <>::MessageDispatch as MessageDispatch<_>>::dispatch( + &dummy_account(), + wrap_as_dispatch_message(bridging_message.clone()) + ) + }, + ); + assert_eq!( + result.dispatch_level_result, + XcmBlobMessageDispatchResult::NotDispatched("DispatchBlobError::RoutingError") + ); + + // 2.1. WITH hrmp channel -> Ok + let result: MessageDispatchResult = execute_on_runtime( + runtime_para_id, + Some(destination_para_id), + || { + <>::MessageDispatch as MessageDispatch<_>>::dispatch( + &dummy_account(), + wrap_as_dispatch_message(bridging_message.clone()) + ) + }, + ); + assert_eq!(result.dispatch_level_result, XcmBlobMessageDispatchResult::Dispatched); + } +} + +#[test] +#[serial_test::serial] +fn test_bridge_hub_rococo_dispatch_blob_and_xcm_routing_works() { + let universal_source_as_senders = + vec![X1(GlobalConsensus(Wococo)), X2(GlobalConsensus(Wococo), Parachain(1000))]; + let runtime_para_id = bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID; + let destination_network_id = Rococo; + let destination_para_id = 1000; + + for univeral_source_as_sender in universal_source_as_senders { + // 1. message is sent to other global consensus - Wococo(Here) + let bridging_message = + simulate_export_message::( + univeral_source_as_sender, + destination_network_id, + Here, + dummy_xcm(), + ); + let result: MessageDispatchResult = execute_on_runtime( + runtime_para_id, + None, + || { + <>::MessageDispatch as MessageDispatch<_>>::dispatch( + &dummy_account(), + wrap_as_dispatch_message(bridging_message) + ) + }, + ); + assert_eq!(result.dispatch_level_result, XcmBlobMessageDispatchResult::Dispatched); + + // 2. message is sent to other global consensus and its parachains - Wococo(Here) + let bridging_message = + simulate_export_message::( + univeral_source_as_sender, + destination_network_id, + X1(Parachain(destination_para_id)), + dummy_xcm(), + ); + + // 2.1. WITHOUT hrmp channel -> RoutingError + let result: MessageDispatchResult = execute_on_runtime( + runtime_para_id, + None, + || { + <>::MessageDispatch as MessageDispatch<_>>::dispatch( + &dummy_account(), + wrap_as_dispatch_message(bridging_message.clone()) + ) + }, + ); + assert_eq!( + result.dispatch_level_result, + XcmBlobMessageDispatchResult::NotDispatched("DispatchBlobError::RoutingError") + ); + + // 2.1. WITH hrmp channel -> Ok + let result: MessageDispatchResult = execute_on_runtime( + runtime_para_id, + Some(destination_para_id), + || { + <>::MessageDispatch as MessageDispatch<_>>::dispatch( + &dummy_account(), + wrap_as_dispatch_message(bridging_message.clone()) + ) + }, + ); + assert_eq!(result.dispatch_level_result, XcmBlobMessageDispatchResult::Dispatched); + } +} diff --git a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml new file mode 100644 index 00000000000..2418111ebb4 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "bridge-hub-test-utils" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +description = "Utils for BridgeHub testing" + +[dependencies] + +# Substrate +frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } + +# Cumulus +parachains-common = { path = "../../../common", default-features = false } +cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } +cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false } +cumulus-primitives-parachain-inherent = { path = "../../../../primitives/parachain-inherent", default-features = false } +cumulus-test-relay-sproof-builder = { path = "../../../../test/relay-sproof-builder", default-features = false } +parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false } + +# Polkadot +polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +pallet-xcm-benchmarks = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false, optional = true } +xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } + +# Bridges +bp-messages = { path = "../../../../bridges/primitives/messages", default-features = false } + +[features] +default = [ "std" ] +std = [ + "frame-support/std", + "frame-system/std", + "bp-messages/std", + "parachains-common/std", + "parachain-info/std", + "cumulus-primitives-core/std", + "cumulus-pallet-parachain-system/std", + "cumulus-primitives-parachain-inherent/std", + "cumulus-test-relay-sproof-builder/std", + "polkadot-parachain/std", + "pallet-xcm/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", +] diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs new file mode 100644 index 00000000000..568ea39b573 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs @@ -0,0 +1,171 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +use bp_messages::{ + target_chain::{DispatchMessage, DispatchMessageData}, + MessageKey, +}; +use cumulus_primitives_core::{AbridgedHrmpChannel, ParaId, PersistedValidationData}; +use cumulus_primitives_parachain_inherent::ParachainInherentData; +use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; +use frame_support::{ + dispatch::{RawOrigin, UnfilteredDispatchable}, + inherent::{InherentData, ProvideInherent}, + sp_io, + traits::Get, +}; +use parachains_common::AccountId; +use polkadot_parachain::primitives::{HrmpChannelId, RelayChainBlockNumber}; +use xcm::{latest::prelude::*, prelude::XcmVersion}; +use xcm_builder::{HaulBlob, HaulBlobExporter}; +use xcm_executor::traits::{validate_export, ExportXcm}; + +/// Dummy xcm +pub fn dummy_xcm() -> Xcm<()> { + vec![Trap(42)].into() +} + +pub fn wrap_as_dispatch_message(payload: Vec) -> DispatchMessage> { + DispatchMessage { + key: MessageKey { lane_id: [0, 0, 0, 0], nonce: 1 }, + data: DispatchMessageData { payload: Ok(payload) }, + } +} + +/// Dummy account +pub fn dummy_account() -> AccountId { + AccountId::from([0u8; 32]) +} + +/// Macro used for simulate_export_message and capturing bytes +macro_rules! grab_haul_blob ( + ($name:ident, $grabbed_payload:ident) => { + static mut $grabbed_payload: Option> = None; + struct $name; + impl HaulBlob for $name { + fn haul_blob(blob: Vec) { + unsafe { + $grabbed_payload = Some(blob); + } + } + } + } +); + +/// Simulates HaulBlobExporter and all its wrapping and captures generated plain bytes +pub fn simulate_export_message>( + sender: Junctions, + destination_network: NetworkId, + destination: Junctions, + xcm: xcm::v3::Xcm<()>, +) -> Vec { + grab_haul_blob!(GrabbingHaulBlob, GRABBED_HAUL_BLOB_PAYLOAD); + + let channel = 1_u32; + let universal_source = sender; + + // simulate XCM message export + let (ticket, fee) = validate_export::>( + destination_network, + channel, + universal_source, + destination, + xcm, + ) + .expect("validate_export error"); + println!("[MessageExporter::fee] {:?}", fee); + let result = HaulBlobExporter::::deliver(ticket) + .expect("deliver error"); + println!("[MessageExporter::deliver] {:?}", result); + + unsafe { GRABBED_HAUL_BLOB_PAYLOAD.as_ref().unwrap().clone() } +} + +/// Initialize runtime/externalities +pub fn new_test_ext( + para_id: ParaId, + xcm_version: XcmVersion, +) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + >::assimilate_storage( + &pallet_xcm::GenesisConfig { safe_xcm_version: Some(xcm_version) }, + &mut t, + ) + .unwrap(); + >::assimilate_storage( + ¶chain_info::GenesisConfig { parachain_id: para_id }, + &mut t, + ) + .unwrap(); + + sp_io::TestExternalities::new(t) +} + +/// Helper function which emulates opening HRMP channel which is needed for XcmpQueue xcm router to pass +pub fn mock_open_hrmp_channel< + C: cumulus_pallet_parachain_system::Config, + T: ProvideInherent>, +>( + sender: ParaId, + recipient: ParaId, +) { + let n = 1_u32; + let mut sproof_builder = RelayStateSproofBuilder::default(); + sproof_builder.para_id = sender; + sproof_builder.hrmp_channels.insert( + HrmpChannelId { sender, recipient }, + AbridgedHrmpChannel { + max_capacity: 10, + max_total_size: 10_000_000_u32, + max_message_size: 10_000_000_u32, + msg_count: 10, + total_size: 10_000_000_u32, + mqc_head: None, + }, + ); + sproof_builder.hrmp_egress_channel_index = Some(vec![recipient]); + + let (relay_parent_storage_root, relay_chain_state) = sproof_builder.into_state_root_and_proof(); + let vfp = PersistedValidationData { + relay_parent_number: n as RelayChainBlockNumber, + relay_parent_storage_root, + ..Default::default() + }; + // It is insufficient to push the validation function params + // to storage; they must also be included in the inherent data. + let inherent_data = { + let mut inherent_data = InherentData::default(); + let system_inherent_data = ParachainInherentData { + validation_data: vfp.clone(), + relay_chain_state, + downward_messages: Default::default(), + horizontal_messages: Default::default(), + }; + inherent_data + .put_data( + cumulus_primitives_parachain_inherent::INHERENT_IDENTIFIER, + &system_inherent_data, + ) + .expect("failed to put VFP inherent"); + inherent_data + }; + + // execute the block + T::create_inherent(&inherent_data) + .expect("got an inherent") + .dispatch_bypass_filter(RawOrigin::None.into()) + .expect("dispatch succeeded"); +} diff --git a/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml b/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml index 0baba72ecd9..845e165c31c 100644 --- a/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml +++ b/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml @@ -79,3 +79,15 @@ cumulus_based = true "--no-mdns", "--bootnodes {{'rockmine-collator1'|zombie('multiAddress')}}", "-- --port 51433 --rpc-port 58833 --ws-port 58843 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" ] + +[[hrmp_channels]] +sender = 1000 +recipient = 1013 +max_capacity = 4 +max_message_size = 524288 + +[[hrmp_channels]] +sender = 1013 +recipient = 1000 +max_capacity = 4 +max_message_size = 524288 diff --git a/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml b/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml index ba498c0a785..8c86fca58e5 100644 --- a/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml +++ b/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml @@ -79,3 +79,15 @@ cumulus_based = true "--no-mdns", "--bootnodes {{'wockmint-collator1'|zombie('multiAddress')}}", "-- --port 31433 --rpc-port 38833 --ws-port 38843 --no-mdns", "--bootnodes {{'alice-validator-wo'|zombie('multiAddress')}}" ] + +[[hrmp_channels]] +sender = 1000 +recipient = 1014 +max_capacity = 4 +max_message_size = 524288 + +[[hrmp_channels]] +sender = 1014 +recipient = 1000 +max_capacity = 4 +max_message_size = 524288 From a2d2a5cc9b0ddd0083a07ad15cb8fee3ab52485b Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 5 Dec 2022 16:10:14 +0100 Subject: [PATCH 130/263] Removed Sudo pallet --- Cargo.lock | 1 - .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 2 -- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 8 -------- polkadot-parachain/src/chain_spec/bridge_hubs.rs | 13 ------------- 4 files changed, 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fa5f25f2bb5..a12f111e275 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -995,7 +995,6 @@ dependencies = [ "pallet-collator-selection", "pallet-session", "pallet-shift-session-manager", - "pallet-sudo", "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 6adb8e1c15b..6b453580ddf 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -28,7 +28,6 @@ pallet-aura = { git = "https://github.com/paritytech/substrate", default-feature pallet-authorship = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } pallet-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -pallet-sudo = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } pallet-timestamp = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } @@ -125,7 +124,6 @@ std = [ "pallet-shift-session-manager/std", "pallet-collator-selection/std", "pallet-session/std", - "pallet-sudo/std", "pallet-timestamp/std", "pallet-transaction-payment-rpc-runtime-api/std", "pallet-transaction-payment/std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index a398e177241..b673e95fc87 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -452,11 +452,6 @@ impl pallet_collator_selection::Config for Runtime { type WeightInfo = (); } -impl pallet_sudo::Config for Runtime { - type RuntimeCall = RuntimeCall; - type RuntimeEvent = RuntimeEvent; -} - // Add bridge pallets (GPA) /// Add granda bridge pallet to track Wococo relay chain on Rococo BridgeHub @@ -626,9 +621,6 @@ construct_runtime!( BridgeRococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Config} = 43, BridgeRococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage, Event} = 44, BridgeRococoMessages: pallet_bridge_messages::::{Pallet, Call, Storage, Event, Config} = 45, - - // Sudo - Sudo: pallet_sudo::{Pallet, Call, Config, Event, Storage} = 100, } ); diff --git a/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/polkadot-parachain/src/chain_spec/bridge_hubs.rs index 96072825f00..e03fcde35d1 100644 --- a/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -73,7 +73,6 @@ impl BridgeHubRuntimeType { "rococo", ParaId::new(1013), None, - None, ))) } else { Ok(Box::new(rococo::BridgeHubChainSpec::from_json_bytes( @@ -86,7 +85,6 @@ impl BridgeHubRuntimeType { ChainType::Local, "rococo-local", ParaId::new(1013), - Some("Alice".to_string()), Some("Bob".to_string()), ))), BridgeHubRuntimeType::Wococo { default_config } => @@ -98,7 +96,6 @@ impl BridgeHubRuntimeType { "wococo", ParaId::new(1014), None, - None, ))) } else { Ok(Box::new(rococo::BridgeHubChainSpec::from_json_bytes( @@ -111,7 +108,6 @@ impl BridgeHubRuntimeType { ChainType::Local, "wococo-local", ParaId::new(1014), - Some("Alice".to_string()), Some("Bob".to_string()), ))), } @@ -166,7 +162,6 @@ pub mod rococo { chain_type: ChainType, relay_chain: &str, para_id: ParaId, - root_key_seed: Option, bridges_pallet_owner_seed: Option, ) -> BridgeHubChainSpec { let properties = sc_chain_spec::Properties::new(); @@ -209,9 +204,6 @@ pub mod rococo { get_account_id_from_seed::("Ferdie//stash"), ], para_id, - root_key_seed - .as_ref() - .map(|seed| get_account_id_from_seed::(&seed)), bridges_pallet_owner_seed .as_ref() .map(|seed| get_account_id_from_seed::(&seed)), @@ -230,7 +222,6 @@ pub mod rococo { invulnerables: Vec<(AccountId, AuraId)>, endowed_accounts: Vec, id: ParaId, - root_key: Option, bridges_pallet_owner: Option, ) -> bridge_hub_rococo_runtime::GenesisConfig { bridge_hub_rococo_runtime::GenesisConfig { @@ -267,8 +258,6 @@ pub mod rococo { polkadot_xcm: bridge_hub_rococo_runtime::PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION), }, - // TODO: when go live, check it: https://github.com/paritytech/parity-bridges-common/issues/1551 - sudo: bridge_hub_rococo_runtime::SudoConfig { key: root_key }, bridge_wococo_grandpa: bridge_hub_rococo_runtime::BridgeWococoGrandpaConfig { owner: bridges_pallet_owner.clone(), ..Default::default() @@ -308,7 +297,6 @@ pub mod wococo { chain_type: ChainType, relay_chain: &str, para_id: ParaId, - root_key_seed: Option, bridges_pallet_owner_seed: Option, ) -> BridgeHubChainSpec { rococo::default_config( @@ -317,7 +305,6 @@ pub mod wococo { chain_type, relay_chain, para_id, - root_key_seed, bridges_pallet_owner_seed, ) } From 080c7f0d613d15039512def106a66b37404274fb Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 6 Dec 2022 15:56:13 +0900 Subject: [PATCH 131/263] Use () as the PriceForParentDelivery --- .../runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs | 2 +- .../runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index cb78aec275f..a207fec531a 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -198,7 +198,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, // TODO: Figure out 3rd type arg + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 8d924f93b33..2fa665e9adb 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -195,7 +195,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, // TODO: Figure out 3rd type arg + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); From 9bad64e6cd9b98c22e4e3cd6ff89676f4bc22731 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 7 Dec 2022 04:07:30 +0900 Subject: [PATCH 132/263] Fixes --- parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs | 1 + parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs index 71f32026ba3..7660251a2e6 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs @@ -305,6 +305,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = RootOrExecutiveSimpleMajority; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; + type PriceForSiblingDelivery = (); } impl cumulus_pallet_dmp_queue::Config for Runtime { diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index cdd07a48ec4..26590d912ca 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -336,6 +336,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; + type PriceForSiblingDelivery = (); } impl cumulus_pallet_dmp_queue::Config for Runtime { From 9ad8152a03dade512b01387ddc6f468eab9b24b3 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 7 Dec 2022 04:24:00 +0900 Subject: [PATCH 133/263] Fixes --- parachain-template/runtime/src/xcm_config.rs | 1 + parachains/runtimes/testing/rococo-parachain/src/lib.rs | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/parachain-template/runtime/src/xcm_config.rs b/parachain-template/runtime/src/xcm_config.rs index 5d7dc8ca5ff..715770873cc 100644 --- a/parachain-template/runtime/src/xcm_config.rs +++ b/parachain-template/runtime/src/xcm_config.rs @@ -189,6 +189,7 @@ impl xcm_executor::Config for XcmConfig { type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 697a50207e2..64c165db8ff 100644 --- a/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -23,7 +23,6 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; -use frame_support::traits::{ConstU32, Nothing}; use sp_api::impl_runtime_apis; use sp_core::OpaqueMetadata; use sp_runtime::{ @@ -44,7 +43,7 @@ pub use frame_support::{ match_types, parameter_types, traits::{ AsEnsureOriginWithArg, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, IsInVec, - Randomness, + Nothing, Randomness, }, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, From f366c7f52057bc143c55ad1e9ed3ea7a3a7d8b7b Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 7 Dec 2022 04:41:07 +0900 Subject: [PATCH 134/263] Fixes --- parachains/runtimes/testing/rococo-parachain/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 64c165db8ff..79b3c45f754 100644 --- a/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -419,6 +419,7 @@ impl xcm_executor::Config for XcmConfig { type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; } /// Local origins on this chain are allowed to dispatch XCM sends/executions. From 92ace0569e6873c4c2319ae09f26497c3f87cc7a Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 7 Dec 2022 06:02:59 +0900 Subject: [PATCH 135/263] Fixes --- pallets/xcmp-queue/src/mock.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pallets/xcmp-queue/src/mock.rs b/pallets/xcmp-queue/src/mock.rs index 2215bd59d7c..a7bad5dc057 100644 --- a/pallets/xcmp-queue/src/mock.rs +++ b/pallets/xcmp-queue/src/mock.rs @@ -20,7 +20,7 @@ use cumulus_pallet_parachain_system::AnyRelayNumber; use cumulus_primitives_core::{IsSystem, ParaId}; use frame_support::{ parameter_types, - traits::{Nothing, OriginTrait}, + traits::{Everything, Nothing, OriginTrait}, }; use frame_system::EnsureRoot; use sp_core::H256; @@ -59,7 +59,7 @@ parameter_types! { type AccountId = u64; impl frame_system::Config for Test { - type BaseCallFilter = frame_support::traits::Everything; + type BaseCallFilter = Everything; type BlockWeights = (); type BlockLength = (); type DbWeight = (); @@ -163,6 +163,7 @@ impl xcm_executor::Config for XcmConfig { type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; } pub type XcmRouter = ( From ffc7926e6d1b72cf7bcc538796aca13860b274ed Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 8 Dec 2022 15:55:54 +0900 Subject: [PATCH 136/263] Update transact_origin to transact_origin_and_runtime_call --- parachains/runtimes/assets/statemine/src/lib.rs | 4 ++-- parachains/runtimes/assets/statemint/src/lib.rs | 4 ++-- parachains/runtimes/assets/westmint/src/lib.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/lib.rs b/parachains/runtimes/assets/statemine/src/lib.rs index 88430123ca8..eaed51a06b4 100644 --- a/parachains/runtimes/assets/statemine/src/lib.rs +++ b/parachains/runtimes/assets/statemine/src/lib.rs @@ -928,8 +928,8 @@ impl_runtime_apis! { Err(BenchmarkError::Skip) } - fn transact_origin() -> Result { - Ok(KsmLocation::get()) + fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> { + Ok((KsmLocation::get(), pallet_session::Call::purge_keys {}.into())) } fn subscribe_origin() -> Result { diff --git a/parachains/runtimes/assets/statemint/src/lib.rs b/parachains/runtimes/assets/statemint/src/lib.rs index e1ee4ae2da9..94a9684b08b 100644 --- a/parachains/runtimes/assets/statemint/src/lib.rs +++ b/parachains/runtimes/assets/statemint/src/lib.rs @@ -940,8 +940,8 @@ impl_runtime_apis! { Err(BenchmarkError::Skip) } - fn transact_origin() -> Result { - Ok(DotLocation::get()) + fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> { + Ok((DotLocation::get(), pallet_session::Call::purge_keys {}.into())) } fn subscribe_origin() -> Result { diff --git a/parachains/runtimes/assets/westmint/src/lib.rs b/parachains/runtimes/assets/westmint/src/lib.rs index c59c8c9bbd0..df2e70a7aef 100644 --- a/parachains/runtimes/assets/westmint/src/lib.rs +++ b/parachains/runtimes/assets/westmint/src/lib.rs @@ -900,8 +900,8 @@ impl_runtime_apis! { Err(BenchmarkError::Skip) } - fn transact_origin() -> Result { - Ok(WestendLocation::get()) + fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> { + Ok((WestendLocation::get(), pallet_session::Call::purge_keys {}.into())) } fn subscribe_origin() -> Result { From 7c25f95a22f4c5f040d74850a5bd07ad1de7ae8d Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Fri, 9 Dec 2022 00:05:14 +0900 Subject: [PATCH 137/263] Add ReachableDest config item to XCM pallet --- parachain-template/runtime/src/xcm_config.rs | 7 +++++++ parachains/runtimes/assets/statemine/src/xcm_config.rs | 7 +++++++ parachains/runtimes/assets/statemint/src/xcm_config.rs | 7 +++++++ parachains/runtimes/assets/westmint/src/xcm_config.rs | 7 +++++++ .../bridge-hubs/bridge-hub-kusama/src/xcm_config.rs | 7 +++++++ .../bridge-hubs/bridge-hub-rococo/src/xcm_config.rs | 7 +++++++ .../collectives/collectives-polkadot/src/xcm_config.rs | 7 +++++++ .../runtimes/contracts/contracts-rococo/src/xcm_config.rs | 7 +++++++ parachains/runtimes/testing/penpal/src/xcm_config.rs | 7 +++++++ parachains/runtimes/testing/rococo-parachain/src/lib.rs | 7 +++++++ 10 files changed, 70 insertions(+) diff --git a/parachain-template/runtime/src/xcm_config.rs b/parachain-template/runtime/src/xcm_config.rs index 715770873cc..7816b7f65d4 100644 --- a/parachain-template/runtime/src/xcm_config.rs +++ b/parachain-template/runtime/src/xcm_config.rs @@ -204,6 +204,11 @@ pub type XcmRouter = ( XcmpQueue, ); +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SendXcmOrigin = EnsureXcmOrigin; @@ -229,6 +234,8 @@ impl pallet_xcm::Config for Runtime { type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 06b9a9b2f98..13146359b3d 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -315,6 +315,11 @@ pub type XcmRouter = ( XcmpQueue, ); +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; // We want to disallow users sending (arbitrary) XCMs from this chain. @@ -345,6 +350,8 @@ impl pallet_xcm::Config for Runtime { type MaxLockers = ConstU32<8>; // FIXME: Replace with benchmarked weight info type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index 25d77ab4d19..fb5e11b7176 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -315,6 +315,11 @@ pub type XcmRouter = ( XcmpQueue, ); +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; // We want to disallow users sending (arbitrary) XCMs from this chain. @@ -344,6 +349,8 @@ impl pallet_xcm::Config for Runtime { type MaxLockers = ConstU32<8>; // FIXME: Replace with benchmarked weight info type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index 6be182c181e..fb43b2835f8 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -310,6 +310,11 @@ pub type XcmRouter = ( XcmpQueue, ); +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SendXcmOrigin = EnsureXcmOrigin; @@ -336,6 +341,8 @@ impl pallet_xcm::Config for Runtime { type MaxLockers = ConstU32<8>; // FIXME: Replace with benchmarked weight info type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index a207fec531a..62eed99afac 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -203,6 +203,11 @@ pub type XcmRouter = ( XcmpQueue, ); +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; // We want to disallow users sending (arbitrary) XCMs from this chain. @@ -229,6 +234,8 @@ impl pallet_xcm::Config for Runtime { type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 2fa665e9adb..dc5218b4005 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -200,6 +200,11 @@ pub type XcmRouter = ( XcmpQueue, ); +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; // We want to disallow users sending (arbitrary) XCMs from this chain. @@ -224,6 +229,8 @@ impl pallet_xcm::Config for Runtime { type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index 54fc60d4bce..c75ee7a8a2d 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -225,6 +225,11 @@ pub type XcmRouter = ( XcmpQueue, ); +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; // We want to disallow users sending (arbitrary) XCMs from this chain. @@ -250,6 +255,8 @@ impl pallet_xcm::Config for Runtime { type MaxLockers = ConstU32<8>; // FIXME: Replace with benchmarked weight info type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index 70bdcc31f4b..8a49540eff0 100644 --- a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -172,6 +172,11 @@ pub type XcmRouter = ( XcmpQueue, ); +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; // We want to disallow users sending (arbitrary) XCMs from this chain. @@ -197,6 +202,8 @@ impl pallet_xcm::Config for Runtime { type MaxLockers = ConstU32<8>; // FIXME: Replace with benchmarked weight info type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index 87c8569f552..7af7ae2091f 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -296,6 +296,11 @@ pub type XcmRouter = ( XcmpQueue, ); +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SendXcmOrigin = EnsureXcmOrigin; @@ -321,6 +326,8 @@ impl pallet_xcm::Config for Runtime { type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } impl cumulus_pallet_xcm::Config for Runtime { diff --git a/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 39799f6bffd..172e8c608ea 100644 --- a/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -434,6 +434,11 @@ pub type XcmRouter = ( XcmpQueue, ); +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SendXcmOrigin = EnsureXcmOrigin; @@ -455,6 +460,8 @@ impl pallet_xcm::Config for Runtime { type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } impl cumulus_pallet_xcm::Config for Runtime { From d42acfdd35db3500cb51945b882795d1d71e1f33 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Thu, 8 Dec 2022 18:17:54 +0200 Subject: [PATCH 138/263] Add BridgeRejectObsoleteHeadersAndMessages to bridge hubs (#1972) --- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index b673e95fc87..a91787fc118 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -122,8 +122,7 @@ pub type SignedExtra = ( frame_system::CheckNonce, frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, - // TODO: do we need? - // BridgeRejectObsoleteHeadersAndMessages, + BridgeRejectObsoleteHeadersAndMessages, ); /// Unchecked extrinsic type as expected by this runtime. @@ -624,6 +623,16 @@ construct_runtime!( } ); +bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages! { + RuntimeCall, AccountId, + // Grandpa + BridgeRococoGrandpa, BridgeWococoGrandpa, + // Parachains + BridgeRococoParachain, BridgeWococoParachain, + // Messages + BridgeRococoMessages, BridgeWococoMessages +} + #[cfg(feature = "runtime-benchmarks")] #[macro_use] extern crate frame_benchmarking; From b144b78619c727783eab8a332607656955e343f1 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sat, 10 Dec 2022 04:10:20 +0900 Subject: [PATCH 139/263] Update SafeCallFilter to allow remark_with_event in runtime benchmarks --- .../runtimes/assets/statemine/src/lib.rs | 2 +- .../assets/statemine/src/xcm_config.rs | 179 ++++++++++-------- .../runtimes/assets/statemint/src/lib.rs | 2 +- .../assets/statemint/src/xcm_config.rs | 179 ++++++++++-------- .../runtimes/assets/westmint/src/lib.rs | 2 +- .../assets/westmint/src/xcm_config.rs | 178 +++++++++-------- .../bridge-hub-kusama/src/xcm_config.rs | 68 ++++--- .../bridge-hub-rococo/src/xcm_config.rs | 69 ++++--- .../collectives-polkadot/src/xcm_config.rs | 113 ++++++----- 9 files changed, 434 insertions(+), 358 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/lib.rs b/parachains/runtimes/assets/statemine/src/lib.rs index 3b635cc5e23..45aed372cc4 100644 --- a/parachains/runtimes/assets/statemine/src/lib.rs +++ b/parachains/runtimes/assets/statemine/src/lib.rs @@ -929,7 +929,7 @@ impl_runtime_apis! { } fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> { - Ok((KsmLocation::get(), pallet_session::Call::purge_keys {}.into())) + Ok((KsmLocation::get(), frame_system::Call::remark_with_event { remark: vec![] }.into())) } fn subscribe_origin() -> Result { diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 13146359b3d..bb8cb3ed7f2 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -20,7 +20,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{ConstU32, Everything, Nothing, PalletInfoAccess}, + traits::{ConstU32, Contains, Everything, Nothing, PalletInfoAccess}, }; use pallet_xcm::XcmPassthrough; use parachains_common::{ @@ -145,89 +145,102 @@ match_types! { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(_) } }; +} - // A call filter for the XCM Transact instruction. This is a temporary measure until we properly - // account for proof size weights. - // - // Calls that are allowed through this filter must: - // 1. Have a fixed weight; - // 2. Cannot lead to another call being made; - // 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. - pub type SafeCallFilter: impl Contains = { - RuntimeCall::System( - frame_system::Call::set_heap_pages { .. } | - frame_system::Call::set_code { .. } | - frame_system::Call::set_code_without_checks { .. } | - frame_system::Call::kill_prefix { .. } - ) | - RuntimeCall::ParachainSystem(..) | - RuntimeCall::Timestamp(..) | - RuntimeCall::Balances(..) | - RuntimeCall::CollatorSelection( - pallet_collator_selection::Call::set_desired_candidates { .. } | - pallet_collator_selection::Call::set_candidacy_bond { .. } | - pallet_collator_selection::Call::register_as_candidate { .. } | - pallet_collator_selection::Call::leave_intent { .. } - ) | - RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | - RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | - RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | - RuntimeCall::Assets( - pallet_assets::Call::create { .. } | - pallet_assets::Call::force_create { .. } | - pallet_assets::Call::start_destroy { .. } | - pallet_assets::Call::destroy_accounts { .. } | - pallet_assets::Call::destroy_approvals { .. } | - pallet_assets::Call::finish_destroy { .. } | - pallet_assets::Call::mint { .. } | - pallet_assets::Call::burn { .. } | - pallet_assets::Call::transfer { .. } | - pallet_assets::Call::transfer_keep_alive { .. } | - pallet_assets::Call::force_transfer { .. } | - pallet_assets::Call::freeze { .. } | - pallet_assets::Call::thaw { .. } | - pallet_assets::Call::freeze_asset { .. } | - pallet_assets::Call::thaw_asset { .. } | - pallet_assets::Call::transfer_ownership { .. } | - pallet_assets::Call::set_team { .. } | - pallet_assets::Call::clear_metadata { .. } | - pallet_assets::Call::force_clear_metadata { .. } | - pallet_assets::Call::force_asset_status { .. } | - pallet_assets::Call::approve_transfer { .. } | - pallet_assets::Call::cancel_approval { .. } | - pallet_assets::Call::force_cancel_approval { .. } | - pallet_assets::Call::transfer_approved { .. } | - pallet_assets::Call::touch { .. } | - pallet_assets::Call::refund { .. } - ) | - RuntimeCall::Uniques( - pallet_uniques::Call::create { .. } | - pallet_uniques::Call::force_create { .. } | - pallet_uniques::Call::destroy { .. } | - pallet_uniques::Call::mint { .. } | - pallet_uniques::Call::burn { .. } | - pallet_uniques::Call::transfer { .. } | - pallet_uniques::Call::freeze { .. } | - pallet_uniques::Call::thaw { .. } | - pallet_uniques::Call::freeze_collection { .. } | - pallet_uniques::Call::thaw_collection { .. } | - pallet_uniques::Call::transfer_ownership { .. } | - pallet_uniques::Call::set_team { .. } | - pallet_uniques::Call::approve_transfer { .. } | - pallet_uniques::Call::cancel_approval { .. } | - pallet_uniques::Call::force_item_status { .. } | - pallet_uniques::Call::set_attribute { .. } | - pallet_uniques::Call::clear_attribute { .. } | - pallet_uniques::Call::set_metadata { .. } | - pallet_uniques::Call::clear_metadata { .. } | - pallet_uniques::Call::set_collection_metadata { .. } | - pallet_uniques::Call::clear_collection_metadata { .. } | - pallet_uniques::Call::set_accept_ownership { .. } | - pallet_uniques::Call::set_collection_max_supply { .. } | - pallet_uniques::Call::set_price { .. } | - pallet_uniques::Call::buy_item { .. } - ) - }; +/// A call filter for the XCM Transact instruction. This is a temporary measure until we properly +/// account for proof size weights. +/// +/// Calls that are allowed through this filter must: +/// 1. Have a fixed weight; +/// 2. Cannot lead to another call being made; +/// 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. +pub struct SafeCallFilter; +impl Contains for SafeCallFilter { + fn contains(call: &RuntimeCall) -> bool { + #[cfg(feature = "runtime-benchmarks")] + { + if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event {..})) { + return true + } + } + + match call { + RuntimeCall::System( + frame_system::Call::set_heap_pages { .. } | + frame_system::Call::set_code { .. } | + frame_system::Call::set_code_without_checks { .. } | + frame_system::Call::kill_prefix { .. } + ) | + RuntimeCall::ParachainSystem(..) | + RuntimeCall::Timestamp(..) | + RuntimeCall::Balances(..) | + RuntimeCall::CollatorSelection( + pallet_collator_selection::Call::set_desired_candidates { .. } | + pallet_collator_selection::Call::set_candidacy_bond { .. } | + pallet_collator_selection::Call::register_as_candidate { .. } | + pallet_collator_selection::Call::leave_intent { .. } + ) | + RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | + RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | + RuntimeCall::Assets( + pallet_assets::Call::create { .. } | + pallet_assets::Call::force_create { .. } | + pallet_assets::Call::start_destroy { .. } | + pallet_assets::Call::destroy_accounts { .. } | + pallet_assets::Call::destroy_approvals { .. } | + pallet_assets::Call::finish_destroy { .. } | + pallet_assets::Call::mint { .. } | + pallet_assets::Call::burn { .. } | + pallet_assets::Call::transfer { .. } | + pallet_assets::Call::transfer_keep_alive { .. } | + pallet_assets::Call::force_transfer { .. } | + pallet_assets::Call::freeze { .. } | + pallet_assets::Call::thaw { .. } | + pallet_assets::Call::freeze_asset { .. } | + pallet_assets::Call::thaw_asset { .. } | + pallet_assets::Call::transfer_ownership { .. } | + pallet_assets::Call::set_team { .. } | + pallet_assets::Call::clear_metadata { .. } | + pallet_assets::Call::force_clear_metadata { .. } | + pallet_assets::Call::force_asset_status { .. } | + pallet_assets::Call::approve_transfer { .. } | + pallet_assets::Call::cancel_approval { .. } | + pallet_assets::Call::force_cancel_approval { .. } | + pallet_assets::Call::transfer_approved { .. } | + pallet_assets::Call::touch { .. } | + pallet_assets::Call::refund { .. } + ) | + RuntimeCall::Uniques( + pallet_uniques::Call::create { .. } | + pallet_uniques::Call::force_create { .. } | + pallet_uniques::Call::destroy { .. } | + pallet_uniques::Call::mint { .. } | + pallet_uniques::Call::burn { .. } | + pallet_uniques::Call::transfer { .. } | + pallet_uniques::Call::freeze { .. } | + pallet_uniques::Call::thaw { .. } | + pallet_uniques::Call::freeze_collection { .. } | + pallet_uniques::Call::thaw_collection { .. } | + pallet_uniques::Call::transfer_ownership { .. } | + pallet_uniques::Call::set_team { .. } | + pallet_uniques::Call::approve_transfer { .. } | + pallet_uniques::Call::cancel_approval { .. } | + pallet_uniques::Call::force_item_status { .. } | + pallet_uniques::Call::set_attribute { .. } | + pallet_uniques::Call::clear_attribute { .. } | + pallet_uniques::Call::set_metadata { .. } | + pallet_uniques::Call::clear_metadata { .. } | + pallet_uniques::Call::set_collection_metadata { .. } | + pallet_uniques::Call::clear_collection_metadata { .. } | + pallet_uniques::Call::set_accept_ownership { .. } | + pallet_uniques::Call::set_collection_max_supply { .. } | + pallet_uniques::Call::set_price { .. } | + pallet_uniques::Call::buy_item { .. } + ) => true, + _ => false, + } + } } pub type Barrier = DenyThenTry< diff --git a/parachains/runtimes/assets/statemint/src/lib.rs b/parachains/runtimes/assets/statemint/src/lib.rs index 6bbfd142012..ff69fc18b81 100644 --- a/parachains/runtimes/assets/statemint/src/lib.rs +++ b/parachains/runtimes/assets/statemint/src/lib.rs @@ -941,7 +941,7 @@ impl_runtime_apis! { } fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> { - Ok((DotLocation::get(), pallet_session::Call::purge_keys {}.into())) + Ok((DotLocation::get(), frame_system::Call::remark_with_event { remark: vec![] }.into())) } fn subscribe_origin() -> Result { diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index fb5e11b7176..b3ab6b1e3e6 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -20,7 +20,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{ConstU32, Everything, Nothing, PalletInfoAccess}, + traits::{ConstU32, Contains, Everything, Nothing, PalletInfoAccess}, }; use pallet_xcm::XcmPassthrough; use parachains_common::{ @@ -145,89 +145,102 @@ match_types! { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(_) } }; +} - // A call filter for the XCM Transact instruction. This is a temporary measure until we properly - // account for proof size weights. - // - // Calls that are allowed through this filter must: - // 1. Have a fixed weight; - // 2. Cannot lead to another call being made; - // 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. - pub type SafeCallFilter: impl Contains = { - RuntimeCall::System( - frame_system::Call::set_heap_pages { .. } | - frame_system::Call::set_code { .. } | - frame_system::Call::set_code_without_checks { .. } | - frame_system::Call::kill_prefix { .. } - ) | - RuntimeCall::ParachainSystem(..) | - RuntimeCall::Timestamp(..) | - RuntimeCall::Balances(..) | - RuntimeCall::CollatorSelection( - pallet_collator_selection::Call::set_desired_candidates { .. } | - pallet_collator_selection::Call::set_candidacy_bond { .. } | - pallet_collator_selection::Call::register_as_candidate { .. } | - pallet_collator_selection::Call::leave_intent { .. } - ) | - RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | - RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | - RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | - RuntimeCall::Assets( - pallet_assets::Call::create { .. } | - pallet_assets::Call::force_create { .. } | - pallet_assets::Call::start_destroy { .. } | - pallet_assets::Call::destroy_accounts { .. } | - pallet_assets::Call::destroy_approvals { .. } | - pallet_assets::Call::finish_destroy { .. } | - pallet_assets::Call::mint { .. } | - pallet_assets::Call::burn { .. } | - pallet_assets::Call::transfer { .. } | - pallet_assets::Call::transfer_keep_alive { .. } | - pallet_assets::Call::force_transfer { .. } | - pallet_assets::Call::freeze { .. } | - pallet_assets::Call::thaw { .. } | - pallet_assets::Call::freeze_asset { .. } | - pallet_assets::Call::thaw_asset { .. } | - pallet_assets::Call::transfer_ownership { .. } | - pallet_assets::Call::set_team { .. } | - pallet_assets::Call::clear_metadata { .. } | - pallet_assets::Call::force_clear_metadata { .. } | - pallet_assets::Call::force_asset_status { .. } | - pallet_assets::Call::approve_transfer { .. } | - pallet_assets::Call::cancel_approval { .. } | - pallet_assets::Call::force_cancel_approval { .. } | - pallet_assets::Call::transfer_approved { .. } | - pallet_assets::Call::touch { .. } | - pallet_assets::Call::refund { .. } - ) | - RuntimeCall::Uniques( - pallet_uniques::Call::create { .. } | - pallet_uniques::Call::force_create { .. } | - pallet_uniques::Call::destroy { .. } | - pallet_uniques::Call::mint { .. } | - pallet_uniques::Call::burn { .. } | - pallet_uniques::Call::transfer { .. } | - pallet_uniques::Call::freeze { .. } | - pallet_uniques::Call::thaw { .. } | - pallet_uniques::Call::freeze_collection { .. } | - pallet_uniques::Call::thaw_collection { .. } | - pallet_uniques::Call::transfer_ownership { .. } | - pallet_uniques::Call::set_team { .. } | - pallet_uniques::Call::approve_transfer { .. } | - pallet_uniques::Call::cancel_approval { .. } | - pallet_uniques::Call::force_item_status { .. } | - pallet_uniques::Call::set_attribute { .. } | - pallet_uniques::Call::clear_attribute { .. } | - pallet_uniques::Call::set_metadata { .. } | - pallet_uniques::Call::clear_metadata { .. } | - pallet_uniques::Call::set_collection_metadata { .. } | - pallet_uniques::Call::clear_collection_metadata { .. } | - pallet_uniques::Call::set_accept_ownership { .. } | - pallet_uniques::Call::set_collection_max_supply { .. } | - pallet_uniques::Call::set_price { .. } | - pallet_uniques::Call::buy_item { .. } - ) - }; +/// A call filter for the XCM Transact instruction. This is a temporary measure until we properly +/// account for proof size weights. +/// +/// Calls that are allowed through this filter must: +/// 1. Have a fixed weight; +/// 2. Cannot lead to another call being made; +/// 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. +pub struct SafeCallFilter; +impl Contains for SafeCallFilter { + fn contains(call: &RuntimeCall) -> bool { + #[cfg(feature = "runtime-benchmarks")] + { + if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event {..})) { + return true + } + } + + match call { + RuntimeCall::System( + frame_system::Call::set_heap_pages { .. } | + frame_system::Call::set_code { .. } | + frame_system::Call::set_code_without_checks { .. } | + frame_system::Call::kill_prefix { .. } + ) | + RuntimeCall::ParachainSystem(..) | + RuntimeCall::Timestamp(..) | + RuntimeCall::Balances(..) | + RuntimeCall::CollatorSelection( + pallet_collator_selection::Call::set_desired_candidates { .. } | + pallet_collator_selection::Call::set_candidacy_bond { .. } | + pallet_collator_selection::Call::register_as_candidate { .. } | + pallet_collator_selection::Call::leave_intent { .. } + ) | + RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | + RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | + RuntimeCall::Assets( + pallet_assets::Call::create { .. } | + pallet_assets::Call::force_create { .. } | + pallet_assets::Call::start_destroy { .. } | + pallet_assets::Call::destroy_accounts { .. } | + pallet_assets::Call::destroy_approvals { .. } | + pallet_assets::Call::finish_destroy { .. } | + pallet_assets::Call::mint { .. } | + pallet_assets::Call::burn { .. } | + pallet_assets::Call::transfer { .. } | + pallet_assets::Call::transfer_keep_alive { .. } | + pallet_assets::Call::force_transfer { .. } | + pallet_assets::Call::freeze { .. } | + pallet_assets::Call::thaw { .. } | + pallet_assets::Call::freeze_asset { .. } | + pallet_assets::Call::thaw_asset { .. } | + pallet_assets::Call::transfer_ownership { .. } | + pallet_assets::Call::set_team { .. } | + pallet_assets::Call::clear_metadata { .. } | + pallet_assets::Call::force_clear_metadata { .. } | + pallet_assets::Call::force_asset_status { .. } | + pallet_assets::Call::approve_transfer { .. } | + pallet_assets::Call::cancel_approval { .. } | + pallet_assets::Call::force_cancel_approval { .. } | + pallet_assets::Call::transfer_approved { .. } | + pallet_assets::Call::touch { .. } | + pallet_assets::Call::refund { .. } + ) | + RuntimeCall::Uniques( + pallet_uniques::Call::create { .. } | + pallet_uniques::Call::force_create { .. } | + pallet_uniques::Call::destroy { .. } | + pallet_uniques::Call::mint { .. } | + pallet_uniques::Call::burn { .. } | + pallet_uniques::Call::transfer { .. } | + pallet_uniques::Call::freeze { .. } | + pallet_uniques::Call::thaw { .. } | + pallet_uniques::Call::freeze_collection { .. } | + pallet_uniques::Call::thaw_collection { .. } | + pallet_uniques::Call::transfer_ownership { .. } | + pallet_uniques::Call::set_team { .. } | + pallet_uniques::Call::approve_transfer { .. } | + pallet_uniques::Call::cancel_approval { .. } | + pallet_uniques::Call::force_item_status { .. } | + pallet_uniques::Call::set_attribute { .. } | + pallet_uniques::Call::clear_attribute { .. } | + pallet_uniques::Call::set_metadata { .. } | + pallet_uniques::Call::clear_metadata { .. } | + pallet_uniques::Call::set_collection_metadata { .. } | + pallet_uniques::Call::clear_collection_metadata { .. } | + pallet_uniques::Call::set_accept_ownership { .. } | + pallet_uniques::Call::set_collection_max_supply { .. } | + pallet_uniques::Call::set_price { .. } | + pallet_uniques::Call::buy_item { .. } + ) => true, + _ => false, + } + } } pub type Barrier = DenyThenTry< diff --git a/parachains/runtimes/assets/westmint/src/lib.rs b/parachains/runtimes/assets/westmint/src/lib.rs index 120bf9528cb..905e3569505 100644 --- a/parachains/runtimes/assets/westmint/src/lib.rs +++ b/parachains/runtimes/assets/westmint/src/lib.rs @@ -901,7 +901,7 @@ impl_runtime_apis! { } fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> { - Ok((WestendLocation::get(), pallet_session::Call::purge_keys {}.into())) + Ok((WestendLocation::get(), frame_system::Call::remark_with_event { remark: vec![] }.into())) } fn subscribe_origin() -> Result { diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index fb43b2835f8..4f32571ceaf 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -20,7 +20,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{ConstU32, Everything, Nothing, PalletInfoAccess}, + traits::{ConstU32, Contains, Everything, Nothing, PalletInfoAccess}, }; use pallet_xcm::XcmPassthrough; use parachains_common::{ @@ -141,89 +141,101 @@ match_types! { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(Plurality { .. }) } }; +} +/// A call filter for the XCM Transact instruction. This is a temporary measure until we properly +/// account for proof size weights. +/// +/// Calls that are allowed through this filter must: +/// 1. Have a fixed weight; +/// 2. Cannot lead to another call being made; +/// 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. +pub struct SafeCallFilter; +impl Contains for SafeCallFilter { + fn contains(call: &RuntimeCall) -> bool { + #[cfg(feature = "runtime-benchmarks")] + { + if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event {..})) { + return true + } + } - // A call filter for the XCM Transact instruction. This is a temporary measure until we properly - // account for proof size weights. - // - // Calls that are allowed through this filter must: - // 1. Have a fixed weight; - // 2. Cannot lead to another call being made; - // 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. - pub type SafeCallFilter: impl Contains = { - RuntimeCall::System( - frame_system::Call::set_heap_pages { .. } | - frame_system::Call::set_code { .. } | - frame_system::Call::set_code_without_checks { .. } | - frame_system::Call::kill_prefix { .. } - ) | - RuntimeCall::ParachainSystem(..) | - RuntimeCall::Timestamp(..) | - RuntimeCall::Balances(..) | - RuntimeCall::CollatorSelection( - pallet_collator_selection::Call::set_desired_candidates { .. } | - pallet_collator_selection::Call::set_candidacy_bond { .. } | - pallet_collator_selection::Call::register_as_candidate { .. } | - pallet_collator_selection::Call::leave_intent { .. } - ) | - RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | - RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | - RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | - RuntimeCall::Assets( - pallet_assets::Call::create { .. } | - pallet_assets::Call::force_create { .. } | - pallet_assets::Call::start_destroy { .. } | - pallet_assets::Call::destroy_accounts { .. } | - pallet_assets::Call::destroy_approvals { .. } | - pallet_assets::Call::finish_destroy { .. } | - pallet_assets::Call::mint { .. } | - pallet_assets::Call::burn { .. } | - pallet_assets::Call::transfer { .. } | - pallet_assets::Call::transfer_keep_alive { .. } | - pallet_assets::Call::force_transfer { .. } | - pallet_assets::Call::freeze { .. } | - pallet_assets::Call::thaw { .. } | - pallet_assets::Call::freeze_asset { .. } | - pallet_assets::Call::thaw_asset { .. } | - pallet_assets::Call::transfer_ownership { .. } | - pallet_assets::Call::set_team { .. } | - pallet_assets::Call::clear_metadata { .. } | - pallet_assets::Call::force_clear_metadata { .. } | - pallet_assets::Call::force_asset_status { .. } | - pallet_assets::Call::approve_transfer { .. } | - pallet_assets::Call::cancel_approval { .. } | - pallet_assets::Call::force_cancel_approval { .. } | - pallet_assets::Call::transfer_approved { .. } | - pallet_assets::Call::touch { .. } | - pallet_assets::Call::refund { .. } - ) | - RuntimeCall::Uniques( - pallet_uniques::Call::create { .. } | - pallet_uniques::Call::force_create { .. } | - pallet_uniques::Call::destroy { .. } | - pallet_uniques::Call::mint { .. } | - pallet_uniques::Call::burn { .. } | - pallet_uniques::Call::transfer { .. } | - pallet_uniques::Call::freeze { .. } | - pallet_uniques::Call::thaw { .. } | - pallet_uniques::Call::freeze_collection { .. } | - pallet_uniques::Call::thaw_collection { .. } | - pallet_uniques::Call::transfer_ownership { .. } | - pallet_uniques::Call::set_team { .. } | - pallet_uniques::Call::approve_transfer { .. } | - pallet_uniques::Call::cancel_approval { .. } | - pallet_uniques::Call::force_item_status { .. } | - pallet_uniques::Call::set_attribute { .. } | - pallet_uniques::Call::clear_attribute { .. } | - pallet_uniques::Call::set_metadata { .. } | - pallet_uniques::Call::clear_metadata { .. } | - pallet_uniques::Call::set_collection_metadata { .. } | - pallet_uniques::Call::clear_collection_metadata { .. } | - pallet_uniques::Call::set_accept_ownership { .. } | - pallet_uniques::Call::set_collection_max_supply { .. } | - pallet_uniques::Call::set_price { .. } | - pallet_uniques::Call::buy_item { .. } - ) - }; + match call { + RuntimeCall::System( + frame_system::Call::set_heap_pages { .. } | + frame_system::Call::set_code { .. } | + frame_system::Call::set_code_without_checks { .. } | + frame_system::Call::kill_prefix { .. } + ) | + RuntimeCall::ParachainSystem(..) | + RuntimeCall::Timestamp(..) | + RuntimeCall::Balances(..) | + RuntimeCall::CollatorSelection( + pallet_collator_selection::Call::set_desired_candidates { .. } | + pallet_collator_selection::Call::set_candidacy_bond { .. } | + pallet_collator_selection::Call::register_as_candidate { .. } | + pallet_collator_selection::Call::leave_intent { .. } + ) | + RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | + RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | + RuntimeCall::Assets( + pallet_assets::Call::create { .. } | + pallet_assets::Call::force_create { .. } | + pallet_assets::Call::start_destroy { .. } | + pallet_assets::Call::destroy_accounts { .. } | + pallet_assets::Call::destroy_approvals { .. } | + pallet_assets::Call::finish_destroy { .. } | + pallet_assets::Call::mint { .. } | + pallet_assets::Call::burn { .. } | + pallet_assets::Call::transfer { .. } | + pallet_assets::Call::transfer_keep_alive { .. } | + pallet_assets::Call::force_transfer { .. } | + pallet_assets::Call::freeze { .. } | + pallet_assets::Call::thaw { .. } | + pallet_assets::Call::freeze_asset { .. } | + pallet_assets::Call::thaw_asset { .. } | + pallet_assets::Call::transfer_ownership { .. } | + pallet_assets::Call::set_team { .. } | + pallet_assets::Call::clear_metadata { .. } | + pallet_assets::Call::force_clear_metadata { .. } | + pallet_assets::Call::force_asset_status { .. } | + pallet_assets::Call::approve_transfer { .. } | + pallet_assets::Call::cancel_approval { .. } | + pallet_assets::Call::force_cancel_approval { .. } | + pallet_assets::Call::transfer_approved { .. } | + pallet_assets::Call::touch { .. } | + pallet_assets::Call::refund { .. } + ) | + RuntimeCall::Uniques( + pallet_uniques::Call::create { .. } | + pallet_uniques::Call::force_create { .. } | + pallet_uniques::Call::destroy { .. } | + pallet_uniques::Call::mint { .. } | + pallet_uniques::Call::burn { .. } | + pallet_uniques::Call::transfer { .. } | + pallet_uniques::Call::freeze { .. } | + pallet_uniques::Call::thaw { .. } | + pallet_uniques::Call::freeze_collection { .. } | + pallet_uniques::Call::thaw_collection { .. } | + pallet_uniques::Call::transfer_ownership { .. } | + pallet_uniques::Call::set_team { .. } | + pallet_uniques::Call::approve_transfer { .. } | + pallet_uniques::Call::cancel_approval { .. } | + pallet_uniques::Call::force_item_status { .. } | + pallet_uniques::Call::set_attribute { .. } | + pallet_uniques::Call::clear_attribute { .. } | + pallet_uniques::Call::set_metadata { .. } | + pallet_uniques::Call::clear_metadata { .. } | + pallet_uniques::Call::set_collection_metadata { .. } | + pallet_uniques::Call::clear_collection_metadata { .. } | + pallet_uniques::Call::set_accept_ownership { .. } | + pallet_uniques::Call::set_collection_max_supply { .. } | + pallet_uniques::Call::set_price { .. } | + pallet_uniques::Call::buy_item { .. } + ) => true, + _ => false, + } + } } pub type Barrier = DenyThenTry< diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index 62eed99afac..2a423614401 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -20,7 +20,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{ConstU32, Everything, Nothing}, + traits::{ConstU32, Contains, Everything, Nothing}, weights::Weight, }; use pallet_xcm::XcmPassthrough; @@ -112,34 +112,46 @@ match_types! { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(_) } }; +} +/// A call filter for the XCM Transact instruction. This is a temporary measure until we properly +/// account for proof size weights. +/// +/// Calls that are allowed through this filter must: +/// 1. Have a fixed weight; +/// 2. Cannot lead to another call being made; +/// 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. +pub struct SafeCallFilter; +impl Contains for SafeCallFilter { + fn contains(call: &RuntimeCall) -> bool { + #[cfg(feature = "runtime-benchmarks")] + { + if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event {..})) { + return true + } + } - // A call filter for the XCM Transact instruction. This is a temporary measure until we properly - // account for proof size weights. - // - // Calls that are allowed through this filter must: - // 1. Have a fixed weight; - // 2. Cannot lead to another call being made; - // 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. - pub type SafeCallFilter: impl Contains = { - RuntimeCall::System( - frame_system::Call::set_heap_pages { .. } | - frame_system::Call::set_code { .. } | - frame_system::Call::set_code_without_checks { .. } | - frame_system::Call::kill_prefix { .. } - ) | - RuntimeCall::ParachainSystem(..) | - RuntimeCall::Timestamp(..) | - RuntimeCall::Balances(..) | - RuntimeCall::CollatorSelection( - pallet_collator_selection::Call::set_desired_candidates { .. } | - pallet_collator_selection::Call::set_candidacy_bond { .. } | - pallet_collator_selection::Call::register_as_candidate { .. } | - pallet_collator_selection::Call::leave_intent { .. } - ) | - RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | - RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | - RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) - }; + match call { + RuntimeCall::System( + frame_system::Call::set_heap_pages { .. } | + frame_system::Call::set_code { .. } | + frame_system::Call::set_code_without_checks { .. } | + frame_system::Call::kill_prefix { .. } + ) | + RuntimeCall::ParachainSystem(..) | + RuntimeCall::Timestamp(..) | + RuntimeCall::Balances(..) | + RuntimeCall::CollatorSelection( + pallet_collator_selection::Call::set_desired_candidates { .. } | + pallet_collator_selection::Call::set_candidacy_bond { .. } | + pallet_collator_selection::Call::register_as_candidate { .. } | + pallet_collator_selection::Call::leave_intent { .. } + ) | + RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | + RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) => true, + _ => false, + } + } } pub type Barrier = DenyThenTry< diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index dc5218b4005..f99deeaba48 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -20,7 +20,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{ConstU32, Everything, Nothing}, + traits::{ConstU32, Contains, Everything, Nothing}, weights::Weight, }; use pallet_xcm::XcmPassthrough; @@ -110,34 +110,47 @@ match_types! { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(_) } }; +} - // A call filter for the XCM Transact instruction. This is a temporary measure until we properly - // account for proof size weights. - // - // Calls that are allowed through this filter must: - // 1. Have a fixed weight; - // 2. Cannot lead to another call being made; - // 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. - pub type SafeCallFilter: impl Contains = { - RuntimeCall::System( - frame_system::Call::set_heap_pages { .. } | - frame_system::Call::set_code { .. } | - frame_system::Call::set_code_without_checks { .. } | - frame_system::Call::kill_prefix { .. } - ) | - RuntimeCall::ParachainSystem(..) | - RuntimeCall::Timestamp(..) | - RuntimeCall::Balances(..) | - RuntimeCall::CollatorSelection( - pallet_collator_selection::Call::set_desired_candidates { .. } | - pallet_collator_selection::Call::set_candidacy_bond { .. } | - pallet_collator_selection::Call::register_as_candidate { .. } | - pallet_collator_selection::Call::leave_intent { .. } - ) | - RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | - RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | - RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) - }; +/// A call filter for the XCM Transact instruction. This is a temporary measure until we properly +/// account for proof size weights. +/// +/// Calls that are allowed through this filter must: +/// 1. Have a fixed weight; +/// 2. Cannot lead to another call being made; +/// 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. +pub struct SafeCallFilter; +impl Contains for SafeCallFilter { + fn contains(call: &RuntimeCall) -> bool { + #[cfg(feature = "runtime-benchmarks")] + { + if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event {..})) { + return true + } + } + + match call { + RuntimeCall::System( + frame_system::Call::set_heap_pages { .. } | + frame_system::Call::set_code { .. } | + frame_system::Call::set_code_without_checks { .. } | + frame_system::Call::kill_prefix { .. } + ) | + RuntimeCall::ParachainSystem(..) | + RuntimeCall::Timestamp(..) | + RuntimeCall::Balances(..) | + RuntimeCall::CollatorSelection( + pallet_collator_selection::Call::set_desired_candidates { .. } | + pallet_collator_selection::Call::set_candidacy_bond { .. } | + pallet_collator_selection::Call::register_as_candidate { .. } | + pallet_collator_selection::Call::leave_intent { .. } + ) | + RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | + RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) => true, + _ => false, + } + } } pub type Barrier = DenyThenTry< diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index c75ee7a8a2d..6571759522c 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -19,7 +19,7 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{ConstU32, Everything, Nothing}, + traits::{ConstU32, Contains, Everything, Nothing}, weights::Weight, }; use pallet_xcm::XcmPassthrough; @@ -113,56 +113,69 @@ match_types! { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(_) } }; +} - // A call filter for the XCM Transact instruction. This is a temporary measure until we properly - // account for proof size weights. - // - // Calls that are allowed through this filter must: - // 1. Have a fixed weight; - // 2. Cannot lead to another call being made; - // 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. - pub type SafeCallFilter: impl Contains = { - RuntimeCall::System( - frame_system::Call::set_heap_pages { .. } | - frame_system::Call::set_code { .. } | - frame_system::Call::set_code_without_checks { .. } | - frame_system::Call::kill_prefix { .. } - ) | - RuntimeCall::ParachainSystem(..) | - RuntimeCall::Timestamp(..) | - RuntimeCall::Balances(..) | - RuntimeCall::CollatorSelection( - pallet_collator_selection::Call::set_desired_candidates { .. } | - pallet_collator_selection::Call::set_candidacy_bond { .. } | - pallet_collator_selection::Call::register_as_candidate { .. } | - pallet_collator_selection::Call::leave_intent { .. } - ) | - RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | - RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | - RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | - RuntimeCall::Alliance( - pallet_alliance::Call::vote { .. } | - pallet_alliance::Call::close_old_weight { .. } | - pallet_alliance::Call::disband { .. } | - pallet_alliance::Call::set_rule { .. } | - pallet_alliance::Call::announce { .. } | - pallet_alliance::Call::remove_announcement { .. } | - pallet_alliance::Call::join_alliance { .. } | - pallet_alliance::Call::nominate_ally { .. } | - pallet_alliance::Call::elevate_ally { .. } | - pallet_alliance::Call::give_retirement_notice { .. } | - pallet_alliance::Call::retire { .. } | - pallet_alliance::Call::kick_member { .. } | - pallet_alliance::Call::close { .. } | - pallet_alliance::Call::abdicate_fellow_status { .. } - ) | - RuntimeCall::AllianceMotion( - pallet_collective::Call::vote { .. } | - pallet_collective::Call::close_old_weight { .. } | - pallet_collective::Call::disapprove_proposal { .. } | - pallet_collective::Call::close { .. } - ) - }; +/// A call filter for the XCM Transact instruction. This is a temporary measure until we properly +/// account for proof size weights. +/// +/// Calls that are allowed through this filter must: +/// 1. Have a fixed weight; +/// 2. Cannot lead to another call being made; +/// 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters. +pub struct SafeCallFilter; +impl Contains for SafeCallFilter { + fn contains(call: &RuntimeCall) -> bool { + #[cfg(feature = "runtime-benchmarks")] + { + if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event {..})) { + return true + } + } + + match call { + RuntimeCall::System( + frame_system::Call::set_heap_pages { .. } | + frame_system::Call::set_code { .. } | + frame_system::Call::set_code_without_checks { .. } | + frame_system::Call::kill_prefix { .. } + ) | + RuntimeCall::ParachainSystem(..) | + RuntimeCall::Timestamp(..) | + RuntimeCall::Balances(..) | + RuntimeCall::CollatorSelection( + pallet_collator_selection::Call::set_desired_candidates { .. } | + pallet_collator_selection::Call::set_candidacy_bond { .. } | + pallet_collator_selection::Call::register_as_candidate { .. } | + pallet_collator_selection::Call::leave_intent { .. } + ) | + RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | + RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | + RuntimeCall::Alliance( + pallet_alliance::Call::vote { .. } | + pallet_alliance::Call::close_old_weight { .. } | + pallet_alliance::Call::disband { .. } | + pallet_alliance::Call::set_rule { .. } | + pallet_alliance::Call::announce { .. } | + pallet_alliance::Call::remove_announcement { .. } | + pallet_alliance::Call::join_alliance { .. } | + pallet_alliance::Call::nominate_ally { .. } | + pallet_alliance::Call::elevate_ally { .. } | + pallet_alliance::Call::give_retirement_notice { .. } | + pallet_alliance::Call::retire { .. } | + pallet_alliance::Call::kick_member { .. } | + pallet_alliance::Call::close { .. } | + pallet_alliance::Call::abdicate_fellow_status { .. } + ) | + RuntimeCall::AllianceMotion( + pallet_collective::Call::vote { .. } | + pallet_collective::Call::close_old_weight { .. } | + pallet_collective::Call::disapprove_proposal { .. } | + pallet_collective::Call::close { .. } + ) => true, + _ => false, + } + } } pub type Barrier = DenyThenTry< From 06c43f565325b3cfbd52e6c2765532886aea2a9a Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sat, 10 Dec 2022 04:30:30 +0900 Subject: [PATCH 140/263] cargo fmt --- .../runtimes/assets/statemine/src/xcm_config.rs | 13 +++++++------ .../runtimes/assets/statemint/src/xcm_config.rs | 13 +++++++------ .../runtimes/assets/westmint/src/xcm_config.rs | 13 +++++++------ .../bridge-hubs/bridge-hub-kusama/src/xcm_config.rs | 9 +++++---- .../bridge-hubs/bridge-hub-rococo/src/xcm_config.rs | 9 +++++---- .../collectives-polkadot/src/xcm_config.rs | 13 +++++++------ 6 files changed, 38 insertions(+), 32 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index bb8cb3ed7f2..7584547de66 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -159,7 +159,7 @@ impl Contains for SafeCallFilter { fn contains(call: &RuntimeCall) -> bool { #[cfg(feature = "runtime-benchmarks")] { - if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event {..})) { + if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event { .. })) { return true } } @@ -169,7 +169,7 @@ impl Contains for SafeCallFilter { frame_system::Call::set_heap_pages { .. } | frame_system::Call::set_code { .. } | frame_system::Call::set_code_without_checks { .. } | - frame_system::Call::kill_prefix { .. } + frame_system::Call::kill_prefix { .. }, ) | RuntimeCall::ParachainSystem(..) | RuntimeCall::Timestamp(..) | @@ -178,10 +178,11 @@ impl Contains for SafeCallFilter { pallet_collator_selection::Call::set_desired_candidates { .. } | pallet_collator_selection::Call::set_candidacy_bond { .. } | pallet_collator_selection::Call::register_as_candidate { .. } | - pallet_collator_selection::Call::leave_intent { .. } + pallet_collator_selection::Call::leave_intent { .. }, ) | RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | - RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::XcmpQueue(..) | + RuntimeCall::DmpQueue(..) | RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | RuntimeCall::Assets( pallet_assets::Call::create { .. } | @@ -209,7 +210,7 @@ impl Contains for SafeCallFilter { pallet_assets::Call::force_cancel_approval { .. } | pallet_assets::Call::transfer_approved { .. } | pallet_assets::Call::touch { .. } | - pallet_assets::Call::refund { .. } + pallet_assets::Call::refund { .. }, ) | RuntimeCall::Uniques( pallet_uniques::Call::create { .. } | @@ -236,7 +237,7 @@ impl Contains for SafeCallFilter { pallet_uniques::Call::set_accept_ownership { .. } | pallet_uniques::Call::set_collection_max_supply { .. } | pallet_uniques::Call::set_price { .. } | - pallet_uniques::Call::buy_item { .. } + pallet_uniques::Call::buy_item { .. }, ) => true, _ => false, } diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index b3ab6b1e3e6..22b95110ee5 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -159,7 +159,7 @@ impl Contains for SafeCallFilter { fn contains(call: &RuntimeCall) -> bool { #[cfg(feature = "runtime-benchmarks")] { - if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event {..})) { + if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event { .. })) { return true } } @@ -169,7 +169,7 @@ impl Contains for SafeCallFilter { frame_system::Call::set_heap_pages { .. } | frame_system::Call::set_code { .. } | frame_system::Call::set_code_without_checks { .. } | - frame_system::Call::kill_prefix { .. } + frame_system::Call::kill_prefix { .. }, ) | RuntimeCall::ParachainSystem(..) | RuntimeCall::Timestamp(..) | @@ -178,10 +178,11 @@ impl Contains for SafeCallFilter { pallet_collator_selection::Call::set_desired_candidates { .. } | pallet_collator_selection::Call::set_candidacy_bond { .. } | pallet_collator_selection::Call::register_as_candidate { .. } | - pallet_collator_selection::Call::leave_intent { .. } + pallet_collator_selection::Call::leave_intent { .. }, ) | RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | - RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::XcmpQueue(..) | + RuntimeCall::DmpQueue(..) | RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | RuntimeCall::Assets( pallet_assets::Call::create { .. } | @@ -209,7 +210,7 @@ impl Contains for SafeCallFilter { pallet_assets::Call::force_cancel_approval { .. } | pallet_assets::Call::transfer_approved { .. } | pallet_assets::Call::touch { .. } | - pallet_assets::Call::refund { .. } + pallet_assets::Call::refund { .. }, ) | RuntimeCall::Uniques( pallet_uniques::Call::create { .. } | @@ -236,7 +237,7 @@ impl Contains for SafeCallFilter { pallet_uniques::Call::set_accept_ownership { .. } | pallet_uniques::Call::set_collection_max_supply { .. } | pallet_uniques::Call::set_price { .. } | - pallet_uniques::Call::buy_item { .. } + pallet_uniques::Call::buy_item { .. }, ) => true, _ => false, } diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index 4f32571ceaf..638085a5d3a 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -154,7 +154,7 @@ impl Contains for SafeCallFilter { fn contains(call: &RuntimeCall) -> bool { #[cfg(feature = "runtime-benchmarks")] { - if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event {..})) { + if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event { .. })) { return true } } @@ -164,7 +164,7 @@ impl Contains for SafeCallFilter { frame_system::Call::set_heap_pages { .. } | frame_system::Call::set_code { .. } | frame_system::Call::set_code_without_checks { .. } | - frame_system::Call::kill_prefix { .. } + frame_system::Call::kill_prefix { .. }, ) | RuntimeCall::ParachainSystem(..) | RuntimeCall::Timestamp(..) | @@ -173,10 +173,11 @@ impl Contains for SafeCallFilter { pallet_collator_selection::Call::set_desired_candidates { .. } | pallet_collator_selection::Call::set_candidacy_bond { .. } | pallet_collator_selection::Call::register_as_candidate { .. } | - pallet_collator_selection::Call::leave_intent { .. } + pallet_collator_selection::Call::leave_intent { .. }, ) | RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | - RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::XcmpQueue(..) | + RuntimeCall::DmpQueue(..) | RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | RuntimeCall::Assets( pallet_assets::Call::create { .. } | @@ -204,7 +205,7 @@ impl Contains for SafeCallFilter { pallet_assets::Call::force_cancel_approval { .. } | pallet_assets::Call::transfer_approved { .. } | pallet_assets::Call::touch { .. } | - pallet_assets::Call::refund { .. } + pallet_assets::Call::refund { .. }, ) | RuntimeCall::Uniques( pallet_uniques::Call::create { .. } | @@ -231,7 +232,7 @@ impl Contains for SafeCallFilter { pallet_uniques::Call::set_accept_ownership { .. } | pallet_uniques::Call::set_collection_max_supply { .. } | pallet_uniques::Call::set_price { .. } | - pallet_uniques::Call::buy_item { .. } + pallet_uniques::Call::buy_item { .. }, ) => true, _ => false, } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index 2a423614401..947ab24f127 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -125,7 +125,7 @@ impl Contains for SafeCallFilter { fn contains(call: &RuntimeCall) -> bool { #[cfg(feature = "runtime-benchmarks")] { - if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event {..})) { + if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event { .. })) { return true } } @@ -135,7 +135,7 @@ impl Contains for SafeCallFilter { frame_system::Call::set_heap_pages { .. } | frame_system::Call::set_code { .. } | frame_system::Call::set_code_without_checks { .. } | - frame_system::Call::kill_prefix { .. } + frame_system::Call::kill_prefix { .. }, ) | RuntimeCall::ParachainSystem(..) | RuntimeCall::Timestamp(..) | @@ -144,10 +144,11 @@ impl Contains for SafeCallFilter { pallet_collator_selection::Call::set_desired_candidates { .. } | pallet_collator_selection::Call::set_candidacy_bond { .. } | pallet_collator_selection::Call::register_as_candidate { .. } | - pallet_collator_selection::Call::leave_intent { .. } + pallet_collator_selection::Call::leave_intent { .. }, ) | RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | - RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::XcmpQueue(..) | + RuntimeCall::DmpQueue(..) | RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) => true, _ => false, } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index f99deeaba48..bb6f9ff1ad2 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -124,7 +124,7 @@ impl Contains for SafeCallFilter { fn contains(call: &RuntimeCall) -> bool { #[cfg(feature = "runtime-benchmarks")] { - if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event {..})) { + if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event { .. })) { return true } } @@ -134,7 +134,7 @@ impl Contains for SafeCallFilter { frame_system::Call::set_heap_pages { .. } | frame_system::Call::set_code { .. } | frame_system::Call::set_code_without_checks { .. } | - frame_system::Call::kill_prefix { .. } + frame_system::Call::kill_prefix { .. }, ) | RuntimeCall::ParachainSystem(..) | RuntimeCall::Timestamp(..) | @@ -143,10 +143,11 @@ impl Contains for SafeCallFilter { pallet_collator_selection::Call::set_desired_candidates { .. } | pallet_collator_selection::Call::set_candidacy_bond { .. } | pallet_collator_selection::Call::register_as_candidate { .. } | - pallet_collator_selection::Call::leave_intent { .. } + pallet_collator_selection::Call::leave_intent { .. }, ) | RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | - RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::XcmpQueue(..) | + RuntimeCall::DmpQueue(..) | RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) => true, _ => false, } diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index 6571759522c..f776297d69d 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -127,7 +127,7 @@ impl Contains for SafeCallFilter { fn contains(call: &RuntimeCall) -> bool { #[cfg(feature = "runtime-benchmarks")] { - if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event {..})) { + if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event { .. })) { return true } } @@ -137,7 +137,7 @@ impl Contains for SafeCallFilter { frame_system::Call::set_heap_pages { .. } | frame_system::Call::set_code { .. } | frame_system::Call::set_code_without_checks { .. } | - frame_system::Call::kill_prefix { .. } + frame_system::Call::kill_prefix { .. }, ) | RuntimeCall::ParachainSystem(..) | RuntimeCall::Timestamp(..) | @@ -146,10 +146,11 @@ impl Contains for SafeCallFilter { pallet_collator_selection::Call::set_desired_candidates { .. } | pallet_collator_selection::Call::set_candidacy_bond { .. } | pallet_collator_selection::Call::register_as_candidate { .. } | - pallet_collator_selection::Call::leave_intent { .. } + pallet_collator_selection::Call::leave_intent { .. }, ) | RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | - RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | + RuntimeCall::XcmpQueue(..) | + RuntimeCall::DmpQueue(..) | RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | RuntimeCall::Alliance( pallet_alliance::Call::vote { .. } | @@ -165,13 +166,13 @@ impl Contains for SafeCallFilter { pallet_alliance::Call::retire { .. } | pallet_alliance::Call::kick_member { .. } | pallet_alliance::Call::close { .. } | - pallet_alliance::Call::abdicate_fellow_status { .. } + pallet_alliance::Call::abdicate_fellow_status { .. }, ) | RuntimeCall::AllianceMotion( pallet_collective::Call::vote { .. } | pallet_collective::Call::close_old_weight { .. } | pallet_collective::Call::disapprove_proposal { .. } | - pallet_collective::Call::close { .. } + pallet_collective::Call::close { .. }, ) => true, _ => false, } From fef449a184cfd1b17c45215ec7935a0254bd96ef Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sat, 10 Dec 2022 15:03:27 +0900 Subject: [PATCH 141/263] Update substrate --- Cargo.lock | 376 +++++++++++++++++++++++++++-------------------------- 1 file changed, 189 insertions(+), 187 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5256510f3f..2c1e985a778 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -450,7 +450,7 @@ dependencies = [ [[package]] name = "beefy-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -487,7 +487,7 @@ dependencies = [ [[package]] name = "beefy-gadget-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "beefy-gadget", "futures", @@ -507,7 +507,7 @@ dependencies = [ [[package]] name = "beefy-merkle-tree" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "sp-api", "sp-beefy", @@ -2929,7 +2929,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", ] @@ -2952,7 +2952,7 @@ checksum = "85dcb89d2b10c5f6133de2efd8c11959ce9dbb46a2f7a4cab208c4eeda6ce1ab" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "frame-system", @@ -2975,7 +2975,7 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "Inflector", "array-bytes 4.2.0", @@ -3027,7 +3027,7 @@ dependencies = [ [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -3038,7 +3038,7 @@ dependencies = [ [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-election-provider-solution-type", "frame-support", @@ -3054,7 +3054,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "frame-system", @@ -3083,7 +3083,7 @@ dependencies = [ [[package]] name = "frame-remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "env_logger", "log", @@ -3100,7 +3100,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "bitflags", "frame-metadata", @@ -3132,7 +3132,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "Inflector", "cfg-expr", @@ -3146,7 +3146,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", @@ -3158,7 +3158,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "proc-macro2", "quote", @@ -3168,7 +3168,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "log", @@ -3186,7 +3186,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -3201,7 +3201,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", "sp-api", @@ -3210,7 +3210,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "parity-scale-codec", @@ -4941,7 +4941,7 @@ dependencies = [ [[package]] name = "mmr-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "futures", "log", @@ -4961,7 +4961,7 @@ dependencies = [ [[package]] name = "mmr-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "anyhow", "jsonrpsee", @@ -5402,7 +5402,7 @@ checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "pallet-alliance" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#11c50578549969979121577cde987ad3f9d95bd8" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "array-bytes 4.2.0", "frame-benchmarking", @@ -5423,7 +5423,7 @@ dependencies = [ [[package]] name = "pallet-asset-tx-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#11c50578549969979121577cde987ad3f9d95bd8" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5441,7 +5441,7 @@ dependencies = [ [[package]] name = "pallet-assets" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#11c50578549969979121577cde987ad3f9d95bd8" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5455,7 +5455,7 @@ dependencies = [ [[package]] name = "pallet-aura" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#11c50578549969979121577cde987ad3f9d95bd8" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "frame-system", @@ -5471,7 +5471,7 @@ dependencies = [ [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "frame-system", @@ -5487,7 +5487,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "frame-system", @@ -5502,7 +5502,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5526,7 +5526,7 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5546,7 +5546,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5561,7 +5561,7 @@ dependencies = [ [[package]] name = "pallet-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "frame-system", @@ -5577,7 +5577,7 @@ dependencies = [ [[package]] name = "pallet-beefy-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "array-bytes 4.2.0", "beefy-merkle-tree", @@ -5600,7 +5600,7 @@ dependencies = [ [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5618,7 +5618,7 @@ dependencies = [ [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5662,7 +5662,7 @@ dependencies = [ [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5679,7 +5679,7 @@ dependencies = [ [[package]] name = "pallet-contracts" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#11c50578549969979121577cde987ad3f9d95bd8" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "bitflags", "frame-benchmarking", @@ -5708,7 +5708,7 @@ dependencies = [ [[package]] name = "pallet-contracts-primitives" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#11c50578549969979121577cde987ad3f9d95bd8" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "bitflags", "parity-scale-codec", @@ -5720,7 +5720,7 @@ dependencies = [ [[package]] name = "pallet-contracts-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#11c50578549969979121577cde987ad3f9d95bd8" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "proc-macro2", "quote", @@ -5730,7 +5730,7 @@ dependencies = [ [[package]] name = "pallet-conviction-voting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "assert_matches", "frame-benchmarking", @@ -5747,7 +5747,7 @@ dependencies = [ [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5765,7 +5765,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5789,7 +5789,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-support-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5802,7 +5802,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5820,7 +5820,7 @@ dependencies = [ [[package]] name = "pallet-fast-unstake" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5839,7 +5839,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5862,7 +5862,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "enumflags2", "frame-benchmarking", @@ -5878,7 +5878,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5898,7 +5898,7 @@ dependencies = [ [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5915,7 +5915,7 @@ dependencies = [ [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5932,7 +5932,7 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5949,7 +5949,7 @@ dependencies = [ [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5965,7 +5965,7 @@ dependencies = [ [[package]] name = "pallet-nis" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -5981,7 +5981,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "frame-system", @@ -5998,7 +5998,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-benchmarking" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6018,7 +6018,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", "sp-api", @@ -6028,7 +6028,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "frame-system", @@ -6045,7 +6045,7 @@ dependencies = [ [[package]] name = "pallet-offences-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6068,7 +6068,7 @@ dependencies = [ [[package]] name = "pallet-preimage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -6085,7 +6085,7 @@ dependencies = [ [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -6100,7 +6100,7 @@ dependencies = [ [[package]] name = "pallet-randomness-collective-flip" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#11c50578549969979121577cde987ad3f9d95bd8" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "frame-system", @@ -6114,7 +6114,7 @@ dependencies = [ [[package]] name = "pallet-ranked-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -6132,7 +6132,7 @@ dependencies = [ [[package]] name = "pallet-recovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -6147,7 +6147,7 @@ dependencies = [ [[package]] name = "pallet-referenda" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "assert_matches", "frame-benchmarking", @@ -6166,7 +6166,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -6183,7 +6183,7 @@ dependencies = [ [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "frame-system", @@ -6204,7 +6204,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -6220,7 +6220,7 @@ dependencies = [ [[package]] name = "pallet-society" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "frame-system", @@ -6234,7 +6234,7 @@ dependencies = [ [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6258,7 +6258,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -6269,7 +6269,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "log", "sp-arithmetic", @@ -6278,7 +6278,7 @@ dependencies = [ [[package]] name = "pallet-state-trie-migration" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -6295,7 +6295,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "frame-system", @@ -6324,7 +6324,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -6342,7 +6342,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -6361,7 +6361,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-support", "frame-system", @@ -6377,7 +6377,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", @@ -6393,7 +6393,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -6405,7 +6405,7 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -6422,7 +6422,7 @@ dependencies = [ [[package]] name = "pallet-uniques" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#11c50578549969979121577cde987ad3f9d95bd8" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -6437,7 +6437,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -6453,7 +6453,7 @@ dependencies = [ [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -6468,7 +6468,7 @@ dependencies = [ [[package]] name = "pallet-whitelist" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-benchmarking", "frame-support", @@ -9279,7 +9279,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "log", "sp-core", @@ -9290,7 +9290,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "futures", @@ -9317,7 +9317,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "futures", "futures-timer", @@ -9340,7 +9340,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -9356,7 +9356,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "impl-trait-for-tuples", "memmap2", @@ -9373,7 +9373,7 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -9384,7 +9384,7 @@ dependencies = [ [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "array-bytes 4.2.0", "chrono", @@ -9424,7 +9424,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "fnv", "futures", @@ -9452,7 +9452,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "hash-db", "kvdb", @@ -9477,13 +9477,14 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "futures", "futures-timer", "libp2p", "log", + "mockall", "parking_lot 0.12.1", "sc-client-api", "sc-utils", @@ -9501,7 +9502,7 @@ dependencies = [ [[package]] name = "sc-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#11c50578549969979121577cde987ad3f9d95bd8" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "futures", @@ -9530,7 +9531,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "fork-tree", @@ -9571,7 +9572,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "futures", "jsonrpsee", @@ -9593,7 +9594,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "fork-tree", "parity-scale-codec", @@ -9606,7 +9607,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "futures", @@ -9630,7 +9631,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "lru", "parity-scale-codec", @@ -9654,7 +9655,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", @@ -9667,7 +9668,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "log", "sc-allocator", @@ -9680,7 +9681,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "cfg-if", "libc", @@ -9697,7 +9698,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "ahash", "array-bytes 4.2.0", @@ -9738,7 +9739,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "finality-grandpa", "futures", @@ -9759,7 +9760,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "ansi_term", "futures", @@ -9775,7 +9776,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -9790,7 +9791,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -9837,7 +9838,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "cid", "futures", @@ -9857,7 +9858,7 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "bitflags", @@ -9883,7 +9884,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "ahash", "futures", @@ -9901,7 +9902,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "array-bytes 4.2.0", "futures", @@ -9922,7 +9923,7 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -9947,13 +9948,14 @@ dependencies = [ "sp-core", "sp-finality-grandpa", "sp-runtime", + "substrate-prometheus-endpoint", "thiserror", ] [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "array-bytes 4.2.0", "futures", @@ -9972,7 +9974,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "array-bytes 4.2.0", "bytes", @@ -10002,7 +10004,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "futures", "libp2p", @@ -10015,7 +10017,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -10024,7 +10026,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "futures", "hash-db", @@ -10054,7 +10056,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "futures", "jsonrpsee", @@ -10077,7 +10079,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "futures", "jsonrpsee", @@ -10090,7 +10092,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "futures", "hex", @@ -10109,7 +10111,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "directories", @@ -10179,7 +10181,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "log", "parity-scale-codec", @@ -10191,7 +10193,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -10210,7 +10212,7 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "futures", "libc", @@ -10229,7 +10231,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "chrono", "futures", @@ -10247,7 +10249,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "ansi_term", "atty", @@ -10278,7 +10280,7 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -10289,7 +10291,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "futures", @@ -10315,7 +10317,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "futures", @@ -10329,7 +10331,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "futures", "futures-timer", @@ -10832,7 +10834,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "hash-db", "log", @@ -10850,7 +10852,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "blake2", "proc-macro-crate", @@ -10862,7 +10864,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", "scale-info", @@ -10875,7 +10877,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "integer-sqrt", "num-traits", @@ -10890,7 +10892,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", "scale-info", @@ -10903,7 +10905,7 @@ dependencies = [ [[package]] name = "sp-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "parity-scale-codec", @@ -10915,7 +10917,7 @@ dependencies = [ [[package]] name = "sp-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", "scale-info", @@ -10932,7 +10934,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", "sp-api", @@ -10944,7 +10946,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "futures", "log", @@ -10962,7 +10964,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "futures", @@ -10981,7 +10983,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#11c50578549969979121577cde987ad3f9d95bd8" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "parity-scale-codec", @@ -10999,7 +11001,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "merlin", @@ -11022,7 +11024,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", "scale-info", @@ -11036,7 +11038,7 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", "scale-info", @@ -11049,7 +11051,7 @@ dependencies = [ [[package]] name = "sp-core" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "array-bytes 4.2.0", "base58", @@ -11094,7 +11096,7 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "blake2", "byteorder", @@ -11108,7 +11110,7 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "proc-macro2", "quote", @@ -11119,7 +11121,7 @@ dependencies = [ [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -11128,7 +11130,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "proc-macro2", "quote", @@ -11138,7 +11140,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "environmental", "parity-scale-codec", @@ -11149,7 +11151,7 @@ dependencies = [ [[package]] name = "sp-finality-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "finality-grandpa", "log", @@ -11167,7 +11169,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -11181,7 +11183,7 @@ dependencies = [ [[package]] name = "sp-io" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "bytes", "ed25519-dalek", @@ -11208,7 +11210,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "lazy_static", "sp-core", @@ -11219,7 +11221,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "futures", @@ -11236,7 +11238,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "thiserror", "zstd", @@ -11245,7 +11247,7 @@ dependencies = [ [[package]] name = "sp-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "ckb-merkle-mountain-range", "log", @@ -11263,7 +11265,7 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", "scale-info", @@ -11277,7 +11279,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "sp-api", "sp-core", @@ -11287,7 +11289,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "backtrace", "lazy_static", @@ -11297,7 +11299,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "rustc-hash", "serde", @@ -11307,7 +11309,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "either", "hash256-std-hasher", @@ -11329,7 +11331,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -11347,7 +11349,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "Inflector", "proc-macro-crate", @@ -11359,7 +11361,7 @@ dependencies = [ [[package]] name = "sp-serializer" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#11c50578549969979121577cde987ad3f9d95bd8" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "serde", "serde_json", @@ -11368,7 +11370,7 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", "scale-info", @@ -11382,7 +11384,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", "scale-info", @@ -11393,7 +11395,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "hash-db", "log", @@ -11415,12 +11417,12 @@ dependencies = [ [[package]] name = "sp-std" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" [[package]] name = "sp-storage" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "impl-serde", "parity-scale-codec", @@ -11433,7 +11435,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "futures-timer", @@ -11449,7 +11451,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", "sp-std", @@ -11461,7 +11463,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "sp-api", "sp-runtime", @@ -11470,7 +11472,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "log", @@ -11486,7 +11488,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "ahash", "hash-db", @@ -11509,7 +11511,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "impl-serde", "parity-scale-codec", @@ -11526,7 +11528,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -11537,7 +11539,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "impl-trait-for-tuples", "log", @@ -11550,7 +11552,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -11853,7 +11855,7 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "platforms", ] @@ -11861,7 +11863,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "frame-system-rpc-runtime-api", "futures", @@ -11882,7 +11884,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "futures-util", "hyper", @@ -11895,7 +11897,7 @@ dependencies = [ [[package]] name = "substrate-rpc-client" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "async-trait", "jsonrpsee", @@ -11908,7 +11910,7 @@ dependencies = [ [[package]] name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "jsonrpsee", "log", @@ -11929,7 +11931,7 @@ dependencies = [ [[package]] name = "substrate-test-client" version = "2.0.1" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -11955,7 +11957,7 @@ dependencies = [ [[package]] name = "substrate-test-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#11c50578549969979121577cde987ad3f9d95bd8" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "futures", "substrate-test-utils-derive", @@ -11965,7 +11967,7 @@ dependencies = [ [[package]] name = "substrate-test-utils-derive" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#11c50578549969979121577cde987ad3f9d95bd8" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -11976,7 +11978,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "ansi_term", "build-helper", @@ -12504,7 +12506,7 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f0b6e79a8d21856743acc5370b5d5c3271048cb9" +source = "git+https://github.com/paritytech/substrate?branch=master#15cfd9c5dbbfa1f3ed49623eb55b67354d1645e9" dependencies = [ "clap 4.0.29", "frame-remote-externalities", From 1fcb5d522861e5deef90b9b30cac625f502519cc Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Mon, 12 Dec 2022 15:59:45 +0900 Subject: [PATCH 142/263] Fix worst_case_holding --- .../runtimes/assets/statemine/src/lib.rs | 22 +++++++++---------- .../runtimes/assets/statemint/src/lib.rs | 22 +++++++++---------- .../runtimes/assets/westmint/src/lib.rs | 22 +++++++++---------- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/lib.rs b/parachains/runtimes/assets/statemine/src/lib.rs index 45aed372cc4..ff3fd494c31 100644 --- a/parachains/runtimes/assets/statemine/src/lib.rs +++ b/parachains/runtimes/assets/statemine/src/lib.rs @@ -853,7 +853,7 @@ impl_runtime_apis! { impl cumulus_pallet_session_benchmarking::Config for Runtime {} use xcm::latest::prelude::*; - use xcm_config::KsmLocation; + use xcm_config::{KsmLocation, MaxAssetsIntoHolding}; use pallet_xcm_benchmarks::asset_instance_from; impl pallet_xcm_benchmarks::Config for Runtime { @@ -862,12 +862,12 @@ impl_runtime_apis! { fn valid_destination() -> Result { Ok(KsmLocation::get()) } - fn worst_case_holding(_depositable_count: u32) -> MultiAssets { + fn worst_case_holding(depositable_count: u32) -> MultiAssets { // A mix of fungible, non-fungible, and concrete assets. - const HOLDING_FUNGIBLES: u32 = 100; - const HOLDING_NON_FUNGIBLES: u32 = 100; + let holding_non_fungibles = MaxAssetsIntoHolding::get() / 2 - depositable_count; + let holding_fungibles = holding_non_fungibles.saturating_sub(1); let fungibles_amount: u128 = 100; - let mut assets = (0..HOLDING_FUNGIBLES) + let mut assets = (0..holding_fungibles) .map(|i| { MultiAsset { id: Concrete(GeneralIndex(i as u128).into()), @@ -876,17 +876,17 @@ impl_runtime_apis! { .into() }) .chain(core::iter::once(MultiAsset { id: Concrete(Here.into()), fun: Fungible(u128::MAX) })) - .chain((0..HOLDING_NON_FUNGIBLES).map(|i| MultiAsset { + .chain((0..holding_non_fungibles).map(|i| MultiAsset { id: Concrete(GeneralIndex(i as u128).into()), fun: NonFungible(asset_instance_from(i)), })) .collect::>(); - assets.push(MultiAsset{ - id: Concrete(KsmLocation::get()), - fun: Fungible(1_000_000 * UNITS), - }); - assets.into() + assets.push(MultiAsset { + id: Concrete(KsmLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }); + assets.into() } } diff --git a/parachains/runtimes/assets/statemint/src/lib.rs b/parachains/runtimes/assets/statemint/src/lib.rs index ff69fc18b81..2d3df44b09c 100644 --- a/parachains/runtimes/assets/statemint/src/lib.rs +++ b/parachains/runtimes/assets/statemint/src/lib.rs @@ -866,7 +866,7 @@ impl_runtime_apis! { impl cumulus_pallet_session_benchmarking::Config for Runtime {} use xcm::latest::prelude::*; - use xcm_config::DotLocation; + use xcm_config::{DotLocation, MaxAssetsIntoHolding}; use pallet_xcm_benchmarks::asset_instance_from; impl pallet_xcm_benchmarks::Config for Runtime { @@ -875,12 +875,12 @@ impl_runtime_apis! { fn valid_destination() -> Result { Ok(DotLocation::get()) } - fn worst_case_holding(_depositable_count: u32) -> MultiAssets { + fn worst_case_holding(depositable_count: u32) -> MultiAssets { // A mix of fungible, non-fungible, and concrete assets. - const HOLDING_FUNGIBLES: u32 = 100; - const HOLDING_NON_FUNGIBLES: u32 = 100; + let holding_non_fungibles = MaxAssetsIntoHolding::get() / 2 - depositable_count; + let holding_fungibles = holding_non_fungibles - 1; let fungibles_amount: u128 = 100; - let mut assets = (0..HOLDING_FUNGIBLES) + let mut assets = (0..holding_fungibles) .map(|i| { MultiAsset { id: Concrete(GeneralIndex(i as u128).into()), @@ -889,17 +889,17 @@ impl_runtime_apis! { .into() }) .chain(core::iter::once(MultiAsset { id: Concrete(Here.into()), fun: Fungible(u128::MAX) })) - .chain((0..HOLDING_NON_FUNGIBLES).map(|i| MultiAsset { + .chain((0..holding_non_fungibles).map(|i| MultiAsset { id: Concrete(GeneralIndex(i as u128).into()), fun: NonFungible(asset_instance_from(i)), })) .collect::>(); - assets.push(MultiAsset{ - id: Concrete(DotLocation::get()), - fun: Fungible(1_000_000 * UNITS), - }); - assets.into() + assets.push(MultiAsset { + id: Concrete(DotLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }); + assets.into() } } diff --git a/parachains/runtimes/assets/westmint/src/lib.rs b/parachains/runtimes/assets/westmint/src/lib.rs index 905e3569505..72680dfed83 100644 --- a/parachains/runtimes/assets/westmint/src/lib.rs +++ b/parachains/runtimes/assets/westmint/src/lib.rs @@ -825,7 +825,7 @@ impl_runtime_apis! { impl cumulus_pallet_session_benchmarking::Config for Runtime {} use xcm::latest::prelude::*; - use xcm_config::WestendLocation; + use xcm_config::{MaxAssetsIntoHolding, WestendLocation}; use pallet_xcm_benchmarks::asset_instance_from; impl pallet_xcm_benchmarks::Config for Runtime { @@ -834,12 +834,12 @@ impl_runtime_apis! { fn valid_destination() -> Result { Ok(WestendLocation::get()) } - fn worst_case_holding(_depositable_count: u32) -> MultiAssets { + fn worst_case_holding(depositable_count: u32) -> MultiAssets { // A mix of fungible, non-fungible, and concrete assets. - const HOLDING_FUNGIBLES: u32 = 100; - const HOLDING_NON_FUNGIBLES: u32 = 100; + let holding_non_fungibles = MaxAssetsIntoHolding::get() / 2 - depositable_count; + let holding_fungibles = holding_non_fungibles - 1; let fungibles_amount: u128 = 100; - let mut assets = (0..HOLDING_FUNGIBLES) + let mut assets = (0..holding_fungibles) .map(|i| { MultiAsset { id: Concrete(GeneralIndex(i as u128).into()), @@ -848,17 +848,17 @@ impl_runtime_apis! { .into() }) .chain(core::iter::once(MultiAsset { id: Concrete(Here.into()), fun: Fungible(u128::MAX) })) - .chain((0..HOLDING_NON_FUNGIBLES).map(|i| MultiAsset { + .chain((0..holding_non_fungibles).map(|i| MultiAsset { id: Concrete(GeneralIndex(i as u128).into()), fun: NonFungible(asset_instance_from(i)), })) .collect::>(); - assets.push(MultiAsset{ - id: Concrete(WestendLocation::get()), - fun: Fungible(1_000_000 * UNITS), - }); - assets.into() + assets.push(MultiAsset { + id: Concrete(WestendLocation::get()), + fun: Fungible(1_000_000 * UNITS), + }); + assets.into() } } From dd74806d45633f679eabdd984c30383a6b91fb41 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Mon, 12 Dec 2022 17:05:27 +0900 Subject: [PATCH 143/263] Fix DMQ queue unit tests --- pallets/dmp-queue/src/lib.rs | 44 ++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 51526d245cc..650c307b3a9 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -538,11 +538,11 @@ mod tests { } fn msg_complete(weight: u64) -> (Xcm, Outcome) { - (msg(weight), Outcome::Complete(Weight::from_ref_time(weight))) + (msg(weight), Outcome::Complete(Weight::from_parts(weight, weight))) } fn msg_limit_reached(weight: u64) -> (Xcm, Outcome) { - (msg(weight), Outcome::Error(XcmError::WeightLimitReached(Weight::from_ref_time(weight)))) + (msg(weight), Outcome::Error(XcmError::WeightLimitReached(Weight::from_parts(weight, weight)))) } fn pages_queued() -> PageCounter { @@ -574,7 +574,7 @@ mod tests { new_test_ext().execute_with(|| { let incoming = vec![msg(1000), msg(1001)]; let weight_used = handle_messages(&incoming, Weight::from_parts(2500, 2500)); - assert_eq!(weight_used, Weight::from_ref_time(2001)); + assert_eq!(weight_used, Weight::from_parts(2001, 2001)); assert_eq!(take_trace(), vec![msg_complete(1000), msg_complete(1001)]); assert!(queue_is_empty()); }); @@ -586,7 +586,7 @@ mod tests { let enqueued = vec![msg(1000), msg(1001), msg(1002)]; enqueue(&enqueued); let weight_used = handle_messages(&[], Weight::from_parts(2500, 2500)); - assert_eq!(weight_used, Weight::from_ref_time(2001)); + assert_eq!(weight_used, Weight::from_parts(2001, 2001)); assert_eq!( take_trace(), vec![msg_complete(1000), msg_complete(1001), msg_limit_reached(1002),] @@ -608,14 +608,14 @@ mod tests { assert_eq!(take_trace(), vec![msg_limit_reached(1000)]); let weight_used = handle_messages(&[], Weight::from_parts(2500, 2500)); - assert_eq!(weight_used, Weight::from_ref_time(2001)); + assert_eq!(weight_used, Weight::from_parts(2001, 2001)); assert_eq!( take_trace(), vec![msg_complete(1000), msg_complete(1001), msg_limit_reached(1002),] ); let weight_used = handle_messages(&[], Weight::from_parts(2500, 2500)); - assert_eq!(weight_used, Weight::from_ref_time(1002)); + assert_eq!(weight_used, Weight::from_parts(1002, 1002)); assert_eq!(take_trace(), vec![msg_complete(1002),]); assert!(queue_is_empty()); }); @@ -626,13 +626,13 @@ mod tests { new_test_ext().execute_with(|| { let incoming = vec![msg(1000), msg(1001), msg(1002)]; let weight_used = handle_messages(&incoming, Weight::from_parts(1500, 1500)); - assert_eq!(weight_used, Weight::from_ref_time(1000)); + assert_eq!(weight_used, Weight::from_parts(1000, 1000)); assert_eq!(pages_queued(), 1); assert_eq!(Pages::::get(0).len(), 2); assert_eq!(take_trace(), vec![msg_complete(1000), msg_limit_reached(1001),]); let weight_used = handle_messages(&[], Weight::from_parts(2500, 2500)); - assert_eq!(weight_used, Weight::from_ref_time(2003)); + assert_eq!(weight_used, Weight::from_parts(2003, 2003)); assert_eq!(take_trace(), vec![msg_complete(1001), msg_complete(1002),]); assert!(queue_is_empty()); }); @@ -645,7 +645,7 @@ mod tests { let incoming = vec![msg(1002), msg(1003)]; enqueue(&enqueued); let weight_used = handle_messages(&incoming, Weight::from_parts(5000, 5000)); - assert_eq!(weight_used, Weight::from_ref_time(4006)); + assert_eq!(weight_used, Weight::from_parts(4006, 4006)); assert_eq!( take_trace(), vec![ @@ -666,7 +666,7 @@ mod tests { let incoming = vec![msg(1002), msg(1003)]; enqueue(&enqueued); let weight_used = handle_messages(&incoming, Weight::from_parts(5000, 5000)); - assert_eq!(weight_used, Weight::from_ref_time(1000)); + assert_eq!(weight_used, Weight::from_parts(1000, 1000)); assert_eq!(take_trace(), vec![msg_complete(1000), msg_limit_reached(10001),]); assert_eq!(pages_queued(), 2); @@ -677,7 +677,7 @@ mod tests { // 20000 is now enough to process everything. let weight_used = handle_messages(&[], Weight::from_parts(20000, 20000)); - assert_eq!(weight_used, Weight::from_ref_time(12006)); + assert_eq!(weight_used, Weight::from_parts(12006, 12006)); assert_eq!( take_trace(), vec![msg_complete(10001), msg_complete(1002), msg_complete(1003),] @@ -693,7 +693,7 @@ mod tests { let incoming = vec![msg(10002), msg(1003)]; enqueue(&enqueued); let weight_used = handle_messages(&incoming, Weight::from_parts(5000, 5000)); - assert_eq!(weight_used, Weight::from_ref_time(2001)); + assert_eq!(weight_used, Weight::from_parts(2001, 2001)); assert_eq!( take_trace(), vec![msg_complete(1000), msg_complete(1001), msg_limit_reached(10002),] @@ -702,7 +702,7 @@ mod tests { // 20000 is now enough to process everything. let weight_used = handle_messages(&[], Weight::from_parts(20000, 20000)); - assert_eq!(weight_used, Weight::from_ref_time(11005)); + assert_eq!(weight_used, Weight::from_parts(11005, 11005)); assert_eq!(take_trace(), vec![msg_complete(10002), msg_complete(1003),]); assert!(queue_is_empty()); }); @@ -715,7 +715,7 @@ mod tests { let incoming = vec![msg(1002), msg(10003)]; enqueue(&enqueued); let weight_used = handle_messages(&incoming, Weight::from_parts(5000, 5000)); - assert_eq!(weight_used, Weight::from_ref_time(3003)); + assert_eq!(weight_used, Weight::from_parts(3003, 3003)); assert_eq!( take_trace(), vec![ @@ -729,7 +729,7 @@ mod tests { // 20000 is now enough to process everything. let weight_used = handle_messages(&[], Weight::from_parts(20000, 20000)); - assert_eq!(weight_used, Weight::from_ref_time(10003)); + assert_eq!(weight_used, Weight::from_parts(10003, 10003)); assert_eq!(take_trace(), vec![msg_complete(10003),]); assert!(queue_is_empty()); }); @@ -741,19 +741,19 @@ mod tests { let enqueued = vec![msg(1000), msg(1001)]; enqueue(&enqueued); let weight_used = handle_messages(&vec![msg(1002)], Weight::from_parts(1500, 1500)); - assert_eq!(weight_used, Weight::from_ref_time(1000)); + assert_eq!(weight_used, Weight::from_parts(1000, 1000)); assert_eq!(take_trace(), vec![msg_complete(1000), msg_limit_reached(1001),]); assert_eq!(pages_queued(), 2); assert_eq!(PageIndex::::get().begin_used, 0); let weight_used = handle_messages(&vec![msg(1003)], Weight::from_parts(1500, 1500)); - assert_eq!(weight_used, Weight::from_ref_time(1001)); + assert_eq!(weight_used, Weight::from_parts(1001, 1001)); assert_eq!(take_trace(), vec![msg_complete(1001), msg_limit_reached(1002),]); assert_eq!(pages_queued(), 2); assert_eq!(PageIndex::::get().begin_used, 1); let weight_used = handle_messages(&vec![msg(1004)], Weight::from_parts(1500, 1500)); - assert_eq!(weight_used, Weight::from_ref_time(1002)); + assert_eq!(weight_used, Weight::from_parts(1002, 1002)); assert_eq!(take_trace(), vec![msg_complete(1002), msg_limit_reached(1003),]); assert_eq!(pages_queued(), 2); assert_eq!(PageIndex::::get().begin_used, 2); @@ -770,7 +770,7 @@ mod tests { let incoming = vec![msg(1000), msg(10001), msg(1002)]; let weight_used = handle_messages(&incoming, Weight::from_parts(2500, 2500)); - assert_eq!(weight_used, Weight::from_ref_time(2002)); + assert_eq!(weight_used, Weight::from_parts(2002, 2002)); assert!(queue_is_empty()); assert_eq!( take_trace(), @@ -833,7 +833,7 @@ mod tests { ) .unwrap(); let actual_weight = info.actual_weight.unwrap(); - assert_eq!(actual_weight, base_weight + Weight::from_ref_time(10000)); + assert_eq!(actual_weight, base_weight + Weight::from_parts(10000, 10000)); assert_eq!(take_trace(), vec![msg_complete(10000)]); assert!(overweights().is_empty()); @@ -855,8 +855,8 @@ mod tests { enqueue(&vec![msg(1002), msg(1003)]); enqueue(&vec![msg(1004), msg(1005)]); - let weight_used = DmpQueue::on_idle(1, Weight::from_ref_time(6000)); - assert_eq!(weight_used, Weight::from_ref_time(5010)); + let weight_used = DmpQueue::on_idle(1, Weight::from_parts(6000, 6000)); + assert_eq!(weight_used, Weight::from_parts(5010, 5010)); assert_eq!( take_trace(), vec![ From 97d8c80d88afc6e79e2972b416a690fb245a9957 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Mon, 12 Dec 2022 17:06:24 +0900 Subject: [PATCH 144/263] Remove unused label --- pallets/dmp-queue/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 650c307b3a9..8f8da85b610 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -221,7 +221,7 @@ pub mod pallet { messages_processed: &mut u8, ) -> Weight { let mut used = Weight::zero(); - 'page: while page_index.begin_used < page_index.end_used { + while page_index.begin_used < page_index.end_used { let page = Pages::::take(page_index.begin_used); for (i, &(sent_at, ref data)) in page.iter().enumerate() { if *messages_processed >= MAX_MESSAGES_PER_BLOCK { From 36a767cc9e3dab113a2f8bccc0a2d49ba000953f Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Mon, 12 Dec 2022 17:06:59 +0900 Subject: [PATCH 145/263] cargo fmt --- pallets/dmp-queue/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 8f8da85b610..26f618002a3 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -542,7 +542,10 @@ mod tests { } fn msg_limit_reached(weight: u64) -> (Xcm, Outcome) { - (msg(weight), Outcome::Error(XcmError::WeightLimitReached(Weight::from_parts(weight, weight)))) + ( + msg(weight), + Outcome::Error(XcmError::WeightLimitReached(Weight::from_parts(weight, weight))), + ) } fn pages_queued() -> PageCounter { From 1d6f7ae578d2477a18b810f35499e8aff633741b Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Mon, 12 Dec 2022 18:09:25 +0900 Subject: [PATCH 146/263] Actually process incoming XCMs --- pallets/dmp-queue/src/lib.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 26f618002a3..21ae50f0ab3 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -489,7 +489,19 @@ mod tests { _hash: XcmHash, _weight_limit: Weight, ) -> Outcome { - unreachable!() + let o = match (message.0.len(), &message.0.first()) { + (1, Some(Transact { require_weight_at_most, .. })) => { + if require_weight_at_most.all_lte(weight_limit) { + Outcome::Complete(*require_weight_at_most) + } else { + Outcome::Error(XcmError::WeightLimitReached(*require_weight_at_most)) + } + }, + // use 1000 to decide that it's not supported. + _ => Outcome::Incomplete(Weight::from_parts(1000, 1000).min(weight_limit), XcmError::Unimplemented), + }; + TRACE.with(|q| q.borrow_mut().push((message, o.clone()))); + o } fn charge_fees(_location: impl Into, _fees: MultiAssets) -> XcmResult { From 90b7e77662536e8bed4ed25186a68978f2b6c0e0 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 13 Dec 2022 00:36:12 +0900 Subject: [PATCH 147/263] Fixes --- pallets/dmp-queue/src/lib.rs | 54 ++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 21ae50f0ab3..c7f6c3f4abb 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -468,42 +468,60 @@ mod tests { }) } - pub enum Weightless {} - impl PreparedMessage for Weightless { + pub struct WeightOutcome(Outcome); + impl PreparedMessage for WeightOutcome { fn weight_of(&self) -> Weight { - unreachable!() + self.0.weight_used() } } pub struct MockExec; impl ExecuteXcm for MockExec { - type Prepared = Weightless; + type Prepared = WeightOutcome; fn prepare(message: Xcm) -> Result { - Err(message) + let o = match (message.0.len(), &message.0.first()) { + (1, Some(Transact { require_weight_at_most, .. })) => { + Outcome::Complete(*require_weight_at_most) + }, + // use 1000 to decide that it's not supported. + _ => Outcome::Incomplete(Weight::from_parts(1000, 1000), XcmError::Unimplemented), + }; + Ok(o) } fn execute( _origin: impl Into, - _: Weightless, + WeightOutcome(o): WeightOutcome, _hash: XcmHash, - _weight_limit: Weight, + _weight_credit: Weight, ) -> Outcome { - let o = match (message.0.len(), &message.0.first()) { - (1, Some(Transact { require_weight_at_most, .. })) => { - if require_weight_at_most.all_lte(weight_limit) { - Outcome::Complete(*require_weight_at_most) - } else { - Outcome::Error(XcmError::WeightLimitReached(*require_weight_at_most)) - } - }, - // use 1000 to decide that it's not supported. - _ => Outcome::Incomplete(Weight::from_parts(1000, 1000).min(weight_limit), XcmError::Unimplemented), - }; TRACE.with(|q| q.borrow_mut().push((message, o.clone()))); o } + fn execute_xcm_in_credit( + origin: impl Into, + message: Xcm, + hash: XcmHash, + weight_limit: Weight, + weight_credit: Weight, + ) -> Outcome { + match Self::prepare(message) { + Ok(pre @ WeightOutcome(Outcome::Complete(..))) => { + let xcm_weight = pre.weight_of(); + if xcm_weight.any_gt(weight_limt) { + return Outcome::Error(Error::WeightLimitReached(xcm_weight)) + } + Self::execute(origin, pre, hash, weight_credit) + }, + Ok(WeightOutcome(Outcome::Incomplete(w, XcmError::Unimplemented))) => { + Outcome::Incomplete(w.min(weight_limit), XcmError::Unimplemented) + }, + _ => Outcome::Error(Error::WeightNotComputable), + } + } + fn charge_fees(_location: impl Into, _fees: MultiAssets) -> XcmResult { Err(XcmError::Unimplemented) } From 838656ae5f056605179f2206fe3ca2f23cce0398 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 13 Dec 2022 03:20:30 +0900 Subject: [PATCH 148/263] Fixes --- pallets/dmp-queue/src/lib.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index c7f6c3f4abb..cb4949aac3f 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -487,17 +487,16 @@ mod tests { // use 1000 to decide that it's not supported. _ => Outcome::Incomplete(Weight::from_parts(1000, 1000), XcmError::Unimplemented), }; - Ok(o) + Ok(WeightOutcome(o)) } fn execute( _origin: impl Into, - WeightOutcome(o): WeightOutcome, + _pre: WeightOutcome, _hash: XcmHash, _weight_credit: Weight, ) -> Outcome { - TRACE.with(|q| q.borrow_mut().push((message, o.clone()))); - o + unreachable!() } fn execute_xcm_in_credit( @@ -508,17 +507,18 @@ mod tests { weight_credit: Weight, ) -> Outcome { match Self::prepare(message) { - Ok(pre @ WeightOutcome(Outcome::Complete(..))) => { - let xcm_weight = pre.weight_of(); - if xcm_weight.any_gt(weight_limt) { + Ok(WeightOutcome(o @ Outcome::Complete(..))) => { + let xcm_weight = o.weight_used(); + if xcm_weight.any_gt(weight_limit) { return Outcome::Error(Error::WeightLimitReached(xcm_weight)) } - Self::execute(origin, pre, hash, weight_credit) + TRACE.with(|q| q.borrow_mut().push((message, o.clone()))); + o }, Ok(WeightOutcome(Outcome::Incomplete(w, XcmError::Unimplemented))) => { Outcome::Incomplete(w.min(weight_limit), XcmError::Unimplemented) }, - _ => Outcome::Error(Error::WeightNotComputable), + _ => Outcome::Error(XcmError::WeightNotComputable), } } From a50aed64f6008e5b590ad15671cb644e491a366c Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 13 Dec 2022 03:35:32 +0900 Subject: [PATCH 149/263] Fixes --- pallets/dmp-queue/src/lib.rs | 54 ++++++++++++++---------------------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index cb4949aac3f..10872de9504 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -468,31 +468,17 @@ mod tests { }) } - pub struct WeightOutcome(Outcome); - impl PreparedMessage for WeightOutcome { - fn weight_of(&self) -> Weight { - self.0.weight_used() - } - } - pub struct MockExec; impl ExecuteXcm for MockExec { - type Prepared = WeightOutcome; + type Prepared = (); - fn prepare(message: Xcm) -> Result { - let o = match (message.0.len(), &message.0.first()) { - (1, Some(Transact { require_weight_at_most, .. })) => { - Outcome::Complete(*require_weight_at_most) - }, - // use 1000 to decide that it's not supported. - _ => Outcome::Incomplete(Weight::from_parts(1000, 1000), XcmError::Unimplemented), - }; - Ok(WeightOutcome(o)) + fn prepare(_message: Xcm) -> Result { + unreachable!() } fn execute( _origin: impl Into, - _pre: WeightOutcome, + _pre: (), _hash: XcmHash, _weight_credit: Weight, ) -> Outcome { @@ -500,26 +486,28 @@ mod tests { } fn execute_xcm_in_credit( - origin: impl Into, + _origin: impl Into, message: Xcm, - hash: XcmHash, + _hash: XcmHash, weight_limit: Weight, - weight_credit: Weight, + _weight_credit: Weight, ) -> Outcome { - match Self::prepare(message) { - Ok(WeightOutcome(o @ Outcome::Complete(..))) => { - let xcm_weight = o.weight_used(); - if xcm_weight.any_gt(weight_limit) { - return Outcome::Error(Error::WeightLimitReached(xcm_weight)) + let o = match (message.0.len(), &message.0.first()) { + (1, Some(Transact { require_weight_at_most, .. })) => { + if require_weight_at_most.all_lte(weight_limit) { + Outcome::Complete(*require_weight_at_most) + } else { + Outcome::Error(XcmError::WeightLimitReached(*require_weight_at_most)) } - TRACE.with(|q| q.borrow_mut().push((message, o.clone()))); - o - }, - Ok(WeightOutcome(Outcome::Incomplete(w, XcmError::Unimplemented))) => { - Outcome::Incomplete(w.min(weight_limit), XcmError::Unimplemented) }, - _ => Outcome::Error(XcmError::WeightNotComputable), - } + // use 1000 to decide that it's not supported. + _ => Outcome::Incomplete( + Weight::from_parts(1000, 1000).min(weight_limit), + XcmError::Unimplemented, + ), + }; + TRACE.with(|q| q.borrow_mut().push((message, o.clone()))); + o } fn charge_fees(_location: impl Into, _fees: MultiAssets) -> XcmResult { From ccb74d9e2e77fda88d3d9e5cedde6bf9f70626c1 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 13 Dec 2022 13:24:04 +0100 Subject: [PATCH 150/263] Fixes - return back Weightless --- pallets/dmp-queue/src/lib.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pallets/dmp-queue/src/lib.rs b/pallets/dmp-queue/src/lib.rs index 10872de9504..a9357260526 100644 --- a/pallets/dmp-queue/src/lib.rs +++ b/pallets/dmp-queue/src/lib.rs @@ -468,9 +468,16 @@ mod tests { }) } + pub enum Weightless {} + impl PreparedMessage for Weightless { + fn weight_of(&self) -> Weight { + unreachable!() + } + } + pub struct MockExec; impl ExecuteXcm for MockExec { - type Prepared = (); + type Prepared = Weightless; fn prepare(_message: Xcm) -> Result { unreachable!() @@ -478,7 +485,7 @@ mod tests { fn execute( _origin: impl Into, - _pre: (), + _pre: Weightless, _hash: XcmHash, _weight_credit: Weight, ) -> Outcome { From 88a3e2973e56a7bdc3e331419f2005e2e62f7140 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 13 Dec 2022 15:20:36 +0100 Subject: [PATCH 151/263] Simplify local run + readme --- parachains/runtimes/bridge-hubs/README.md | 15 +++-- scripts/bridges_rococo_wococo.sh | 66 ++++++++++++++----- .../bridge_hub_rococo_local_network.toml | 4 +- 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 75f05bf5483..31b9f60828a 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -66,13 +66,19 @@ POLKADOT_PARACHAIN_BINARY_PATH_FOR_WOCKMINT=~/local_bridge_testing/bin/polkadot- **Accounts of BridgeHub parachains:** - `Bob` is pallet owner of all bridge pallets -- `Alice` is `Sudo` -**1. Init bridges** +#### Run with script (alternative 1) +``` +cd +./scripts/bridges_rococo_wococo.sh run-relay +``` +#### Run with binary (alternative 2) Need to wait for parachain activation (start producing blocks), then run: ``` +# 1. Init bridges: + # Rococo -> Wococo RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay init-bridge rococo-to-bridge-hub-wococo \ @@ -90,11 +96,8 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ --target-host localhost \ --target-port 8943 \ --target-signer //Bob -``` - -**2. Relay relay-chain headers, parachain headers and messages** -``` +# 2. Relay relay-chain headers, parachain headers and messages** RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay relay-headers-and-messages bridge-hub-rococo-bridge-hub-wococo \ --rococo-host localhost \ diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh index f6d2630af6e..94edec489b9 100755 --- a/scripts/bridges_rococo_wococo.sh +++ b/scripts/bridges_rococo_wococo.sh @@ -68,6 +68,49 @@ function check_parachain_collator() { echo "" } +function init_ro_wo() { + RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay init-bridge rococo-to-bridge-hub-wococo \ + --source-host localhost \ + --source-port 9942 \ + --target-host localhost \ + --target-port 8945 \ + --target-signer //Bob +} + +function init_wo_ro() { + RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay init-bridge wococo-to-bridge-hub-rococo \ + --source-host localhost \ + --source-port 9945 \ + --target-host localhost \ + --target-port 8943 \ + --target-signer //Bob +} + +function run_relay() { + RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ + ~/local_bridge_testing/bin/substrate-relay relay-headers-and-messages bridge-hub-rococo-bridge-hub-wococo \ + --rococo-host localhost \ + --rococo-port 9942 \ + --bridge-hub-rococo-host localhost \ + --bridge-hub-rococo-port 8943 \ + --bridge-hub-rococo-signer //Charlie \ + --wococo-headers-to-bridge-hub-rococo-signer //Bob \ + --wococo-parachains-to-bridge-hub-rococo-signer //Bob \ + --bridge-hub-rococo-transactions-mortality 4 \ + --wococo-host localhost \ + --wococo-port 9945 \ + --bridge-hub-wococo-host localhost \ + --bridge-hub-wococo-port 8945 \ + --bridge-hub-wococo-signer //Charlie \ + --rococo-headers-to-bridge-hub-wococo-signer //Bob \ + --rococo-parachains-to-bridge-hub-wococo-signer //Bob \ + --bridge-hub-wococo-transactions-mortality 4 \ + --lane 00000001 \ + --lane 00000002 +} + ensure_binaries case "$1" in @@ -135,27 +178,20 @@ case "$1" in ;; init-ro-wo) # Init bridge Rococo->Wococo - RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - ~/local_bridge_testing/bin/substrate-relay init-bridge rococo-to-bridge-hub-wococo \ - --source-host localhost \ - --source-port 9942 \ - --target-host localhost \ - --target-port 8945 \ - --target-signer //Bob + init_ro_wo ;; init-wo-ro) # Init bridge Wococo->Rococo - RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - ~/local_bridge_testing/bin/substrate-relay init-bridge wococo-to-bridge-hub-rococo \ - --source-host localhost \ - --source-port 9945 \ - --target-host localhost \ - --target-port 8943 \ - --target-signer //Bob + init_wo_ro + ;; + run-relay) + init_ro_wo + init_wo_ro + run_relay ;; stop) pkill -f polkadot pkill -f parachain ;; - *) echo "A command is require. Supported commands: start-rococo, start-wococo, init-ro-wo, init-wo-ro, stop"; exit 1;; + *) echo "A command is require. Supported commands: start-rococo, start-wococo, init-ro-wo, init-wo-ro, run-relay, stop"; exit 1;; esac diff --git a/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml b/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml index 845e165c31c..279952130cd 100644 --- a/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml +++ b/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml @@ -66,7 +66,7 @@ cumulus_based = true ws_port = 9910 command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ROCKMINE}}" extra_args = [ - "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", + "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,runtime::bridge-assets-transfer=trace", "--no-mdns", "--bootnodes {{'rockmine-collator2'|zombie('multiAddress')}}", "-- --port 51333 --rpc-port 58933 --ws-port 58943 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" ] @@ -75,7 +75,7 @@ cumulus_based = true name = "rockmine-collator2" command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ROCKMINE}}" extra_args = [ - "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", + "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,runtime::bridge-assets-transfer=trace", "--no-mdns", "--bootnodes {{'rockmine-collator1'|zombie('multiAddress')}}", "-- --port 51433 --rpc-port 58833 --ws-port 58843 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" ] From 221d6be47a392c85cd1fb37769a12880ef2a7b0a Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 13 Dec 2022 16:34:19 +0100 Subject: [PATCH 152/263] Added measured benchmarks for `pallet_xcm` (#1968) * Fix Fix Fix * Fix * Fixes for transact benchmark * Fixes add pallet_xcm to benchmarks * Revert remark_with_event * ".git/.scripts/bench-bot.sh" xcm statemine assets pallet_xcm_benchmarks::generic * Fixes * TMP * Fix for reserve_asset_deposited * ".git/.scripts/bench-bot.sh" pallet statemine assets pallet_xcm * Fix * ".git/.scripts/bench-bot.sh" pallet statemint assets pallet_xcm * Fix * ".git/.scripts/bench-bot.sh" pallet westmint assets pallet_xcm * Fix westmint * ".git/.scripts/bench-bot.sh" xcm statemine assets pallet_xcm_benchmarks::generic * Fix * ".git/.scripts/bench-bot.sh" xcm westmint assets pallet_xcm_benchmarks::generic * ".git/.scripts/bench-bot.sh" xcm statemint assets pallet_xcm_benchmarks::generic * ".git/.scripts/bench-bot.sh" pallet collectives-polkadot collectives pallet_xcm * Fix for collectives * ".git/.scripts/bench-bot.sh" pallet bridge-hub-kusama bridge-hubs pallet_xcm * ".git/.scripts/bench-bot.sh" pallet bridge-hub-rococo bridge-hubs pallet_xcm * Fixes for bridge-hubs * Fixes - return back Weightless * Fix - removed MigrateToTrackInactive for contracts-rococo Co-authored-by: command-bot <> --- pallets/xcmp-queue/src/benchmarking.rs | 2 +- .../runtimes/assets/statemine/src/lib.rs | 5 +- .../assets/statemine/src/weights/mod.rs | 1 + .../statemine/src/weights/pallet_xcm.rs | 117 ++++++++++++++++++ .../assets/statemine/src/weights/xcm/mod.rs | 3 +- .../xcm/pallet_xcm_benchmarks_generic.rs | 76 ++++++------ .../assets/statemine/src/xcm_config.rs | 12 +- .../runtimes/assets/statemint/src/lib.rs | 5 +- .../assets/statemint/src/weights/mod.rs | 1 + .../statemint/src/weights/pallet_xcm.rs | 117 ++++++++++++++++++ .../assets/statemint/src/weights/xcm/mod.rs | 3 +- .../xcm/pallet_xcm_benchmarks_generic.rs | 76 ++++++------ .../assets/statemint/src/xcm_config.rs | 12 +- .../runtimes/assets/westmint/src/lib.rs | 5 +- .../assets/westmint/src/weights/mod.rs | 1 + .../assets/westmint/src/weights/pallet_xcm.rs | 116 +++++++++++++++++ .../assets/westmint/src/weights/xcm/mod.rs | 3 +- .../xcm/pallet_xcm_benchmarks_generic.rs | 76 ++++++------ .../assets/westmint/src/xcm_config.rs | 12 +- .../bridge-hubs/bridge-hub-kusama/src/lib.rs | 1 + .../bridge-hub-kusama/src/weights/mod.rs | 1 + .../src/weights/pallet_xcm.rs | 116 +++++++++++++++++ .../bridge-hub-kusama/src/xcm_config.rs | 2 +- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 1 + .../bridge-hub-rococo/src/weights/mod.rs | 1 + .../src/weights/pallet_xcm.rs | 116 +++++++++++++++++ .../bridge-hub-rococo/src/xcm_config.rs | 2 +- .../collectives-polkadot/src/lib.rs | 1 + .../collectives-polkadot/src/weights/mod.rs | 1 + .../src/weights/pallet_xcm.rs | 116 +++++++++++++++++ .../collectives-polkadot/src/xcm_config.rs | 3 +- .../contracts/contracts-rococo/src/lib.rs | 2 +- .../testing/rococo-parachain/Cargo.toml | 1 + 33 files changed, 869 insertions(+), 138 deletions(-) create mode 100644 parachains/runtimes/assets/statemine/src/weights/pallet_xcm.rs create mode 100644 parachains/runtimes/assets/statemint/src/weights/pallet_xcm.rs create mode 100644 parachains/runtimes/assets/westmint/src/weights/pallet_xcm.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_xcm.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs create mode 100644 parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_xcm.rs diff --git a/pallets/xcmp-queue/src/benchmarking.rs b/pallets/xcmp-queue/src/benchmarking.rs index 5d34d499dbe..5ac78bee763 100644 --- a/pallets/xcmp-queue/src/benchmarking.rs +++ b/pallets/xcmp-queue/src/benchmarking.rs @@ -22,7 +22,7 @@ use frame_system::RawOrigin; benchmarks! { set_config_with_u32 {}: update_resume_threshold(RawOrigin::Root, 100) - set_config_with_weight {}: update_weight_restrict_decay(RawOrigin::Root, 3_000_000) + set_config_with_weight {}: update_weight_restrict_decay(RawOrigin::Root, Weight::from_ref_time(3_000_000)) } impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/parachains/runtimes/assets/statemine/src/lib.rs b/parachains/runtimes/assets/statemine/src/lib.rs index c2051108e73..fdec9c6ff38 100644 --- a/parachains/runtimes/assets/statemine/src/lib.rs +++ b/parachains/runtimes/assets/statemine/src/lib.rs @@ -672,6 +672,7 @@ mod benches { [pallet_collator_selection, CollatorSelection] [cumulus_pallet_xcmp_queue, XcmpQueue] // XCM + [pallet_xcm, PolkadotXcm] // NOTE: Make sure you point to the individual modules below. [pallet_xcm_benchmarks::fungible, XcmBalances] [pallet_xcm_benchmarks::generic, XcmGeneric] @@ -859,7 +860,7 @@ impl_runtime_apis! { impl cumulus_pallet_session_benchmarking::Config for Runtime {} use xcm::latest::prelude::*; - use xcm_config::{KsmLocation, MaxAssetsIntoHolding}; + use xcm_config::{LocalCheckAccount, KsmLocation, MaxAssetsIntoHolding}; use pallet_xcm_benchmarks::asset_instance_from; impl pallet_xcm_benchmarks::Config for Runtime { @@ -908,7 +909,7 @@ impl_runtime_apis! { impl pallet_xcm_benchmarks::fungible::Config for Runtime { type TransactAsset = Balances; - type CheckedAccount = CheckedAccount; + type CheckedAccount = LocalCheckAccount; type TrustedTeleporter = TrustedTeleporter; fn get_multi_asset() -> MultiAsset { diff --git a/parachains/runtimes/assets/statemine/src/weights/mod.rs b/parachains/runtimes/assets/statemine/src/weights/mod.rs index 080584f2cfa..5dd6ffd662e 100644 --- a/parachains/runtimes/assets/statemine/src/weights/mod.rs +++ b/parachains/runtimes/assets/statemine/src/weights/mod.rs @@ -11,6 +11,7 @@ pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_uniques; pub mod pallet_utility; +pub mod pallet_xcm; pub mod paritydb_weights; pub mod rocksdb_weights; pub mod xcm; diff --git a/parachains/runtimes/assets/statemine/src/weights/pallet_xcm.rs b/parachains/runtimes/assets/statemine/src/weights/pallet_xcm.rs new file mode 100644 index 00000000000..49dffce4223 --- /dev/null +++ b/parachains/runtimes/assets/statemine/src/weights/pallet_xcm.rs @@ -0,0 +1,117 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `pallet_xcm` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemine-dev"), DB CACHE: 1024 + +// Executed Command: +// /home/benchbot/cargo_target_dir/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=statemine-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/assets/statemine/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_xcm`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm::WeightInfo for WeightInfo { + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn send() -> Weight { + // Minimum execution time: 25_562 nanoseconds. + Weight::from_ref_time(26_177_000) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn teleport_assets() -> Weight { + // Minimum execution time: 31_555 nanoseconds. + Weight::from_ref_time(32_297_000) + .saturating_add(T::DbWeight::get().reads(1)) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn reserve_transfer_assets() -> Weight { + // Minimum execution time: 24_516 nanoseconds. + Weight::from_ref_time(25_126_000) + .saturating_add(T::DbWeight::get().reads(1)) + } + // Storage: Benchmark Override (r:0 w:0) + fn execute() -> Weight { + // Minimum execution time: 18_446_744_073_709_551 nanoseconds. + Weight::from_ref_time(18_446_744_073_709_551_000) + } + // Storage: PolkadotXcm SupportedVersion (r:0 w:1) + fn force_xcm_version() -> Weight { + // Minimum execution time: 14_521 nanoseconds. + Weight::from_ref_time(14_865_000) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) + fn force_default_xcm_version() -> Weight { + // Minimum execution time: 4_325 nanoseconds. + Weight::from_ref_time(4_578_000) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) + // Storage: PolkadotXcm QueryCounter (r:1 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + // Storage: PolkadotXcm Queries (r:0 w:1) + fn force_subscribe_version_notify() -> Weight { + // Minimum execution time: 29_836 nanoseconds. + Weight::from_ref_time(30_213_000) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + // Storage: PolkadotXcm Queries (r:0 w:1) + fn force_unsubscribe_version_notify() -> Weight { + // Minimum execution time: 31_333 nanoseconds. + Weight::from_ref_time(31_808_000) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} diff --git a/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs b/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs index eddabcd1dc1..d38e1c62e51 100644 --- a/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/statemine/src/weights/xcm/mod.rs @@ -53,7 +53,8 @@ impl XcmWeightInfo for StatemineXcmWeight { } // Currently there is no trusted reserve fn reserve_asset_deposited(_assets: &MultiAssets) -> Weight { - Weight::MAX + // TODO: hardcoded - fix https://github.com/paritytech/cumulus/issues/1974 + Weight::from_ref_time(1_000_000_000 as u64) } fn receive_teleported_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::receive_teleported_asset()) diff --git a/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 76c40db7914..192173926fa 100644 --- a/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -18,23 +18,23 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-09-30, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemine-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// /home/benchbot/cargo_target_dir/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=statemine-dev +// --steps=50 +// --repeat=20 +// --extrinsic=* // --execution=wasm // --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::generic -// --extrinsic=* -// --steps=50 -// --repeat=20 -// --json +// --chain=statemine-dev // --header=./file_header.txt // --template=./templates/xcm-bench-template.hbs // --output=./parachains/runtimes/assets/statemine/src/weights/xcm/ @@ -56,38 +56,40 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_holding() -> Weight { - Weight::from_ref_time(1_303_495_000 as u64) + Weight::from_ref_time(1_007_454_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn buy_execution() -> Weight { - Weight::from_ref_time(8_667_000 as u64) + Weight::from_ref_time(8_069_000 as u64) } // Storage: PolkadotXcm Queries (r:1 w:0) pub(crate) fn query_response() -> Weight { - Weight::from_ref_time(19_292_000 as u64) + Weight::from_ref_time(17_719_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) } + // Storage: Session NextKeys (r:1 w:0) pub(crate) fn transact() -> Weight { - Weight::from_ref_time(37_996_000 as u64) + Weight::from_ref_time(15_102_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) } pub(crate) fn refund_surplus() -> Weight { - Weight::from_ref_time(9_076_000 as u64) + Weight::from_ref_time(8_103_000 as u64) } pub(crate) fn set_error_handler() -> Weight { - Weight::from_ref_time(6_410_000 as u64) + Weight::from_ref_time(5_803_000 as u64) } pub(crate) fn set_appendix() -> Weight { - Weight::from_ref_time(6_412_000 as u64) + Weight::from_ref_time(5_761_000 as u64) } pub(crate) fn clear_error() -> Weight { - Weight::from_ref_time(6_311_000 as u64) + Weight::from_ref_time(5_665_000 as u64) } pub(crate) fn descend_origin() -> Weight { - Weight::from_ref_time(7_355_000 as u64) + Weight::from_ref_time(6_640_000 as u64) } pub(crate) fn clear_origin() -> Weight { - Weight::from_ref_time(6_389_000 as u64) + Weight::from_ref_time(5_832_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -96,18 +98,18 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_error() -> Weight { - Weight::from_ref_time(23_020_000 as u64) + Weight::from_ref_time(21_808_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: PolkadotXcm AssetTraps (r:1 w:1) pub(crate) fn claim_asset() -> Weight { - Weight::from_ref_time(13_613_000 as u64) + Weight::from_ref_time(21_743_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } pub(crate) fn trap() -> Weight { - Weight::from_ref_time(6_457_000 as u64) + Weight::from_ref_time(5_793_000 as u64) } // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -116,13 +118,13 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn subscribe_version() -> Weight { - Weight::from_ref_time(31_677_000 as u64) + Weight::from_ref_time(27_736_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) pub(crate) fn unsubscribe_version() -> Weight { - Weight::from_ref_time(9_613_000 as u64) + Weight::from_ref_time(7_913_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: ParachainInfo ParachainId (r:1 w:0) @@ -132,21 +134,21 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_reserve_withdraw() -> Weight { - Weight::from_ref_time(1_588_580_000 as u64) + Weight::from_ref_time(1_181_673_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn burn_asset() -> Weight { - Weight::from_ref_time(497_452_000 as u64) + Weight::from_ref_time(415_651_000 as u64) } pub(crate) fn expect_asset() -> Weight { - Weight::from_ref_time(38_502_000 as u64) + Weight::from_ref_time(38_939_000 as u64) } pub(crate) fn expect_origin() -> Weight { - Weight::from_ref_time(6_427_000 as u64) + Weight::from_ref_time(5_918_000 as u64) } pub(crate) fn expect_error() -> Weight { - Weight::from_ref_time(6_303_000 as u64) + Weight::from_ref_time(5_829_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -155,12 +157,12 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn query_pallet() -> Weight { - Weight::from_ref_time(25_510_000 as u64) + Weight::from_ref_time(24_557_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn expect_pallet() -> Weight { - Weight::from_ref_time(7_909_000 as u64) + Weight::from_ref_time(7_407_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -169,21 +171,23 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_transact_status() -> Weight { - Weight::from_ref_time(22_949_000 as u64) + Weight::from_ref_time(21_839_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn clear_transact_status() -> Weight { - Weight::from_ref_time(6_491_000 as u64) + Weight::from_ref_time(5_796_000 as u64) } pub(crate) fn set_topic() -> Weight { - Weight::from_ref_time(6_527_000 as u64) + Weight::from_ref_time(5_808_000 as u64) } pub(crate) fn clear_topic() -> Weight { - Weight::from_ref_time(6_440_000 as u64) + Weight::from_ref_time(5_790_000 as u64) } pub(crate) fn set_fees_mode() -> Weight { - Weight::from_ref_time(6_426_000 as u64) + Weight::from_ref_time(5_774_000 as u64) + } + pub(crate) fn unpaid_execution() -> Weight { + Weight::from_ref_time(5_930_000 as u64) } - pub(crate) fn unpaid_execution() -> Weight { Weight::from_ref_time(3_111_000 as u64) } } diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 7584547de66..70e60530a45 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -36,9 +36,10 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, - NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, + MintLocation, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + WeightInfoBounds, }; use xcm_executor::{ traits::{JustTry, WithOriginFilter}, @@ -54,6 +55,8 @@ parameter_types! { pub AssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + /// The check account that is allowed to mint assets locally. + pub LocalCheckAccount: (AccountId, MintLocation) = (CheckingAccount::get(), MintLocation::Local); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used @@ -362,8 +365,7 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; - // FIXME: Replace with benchmarked weight info - type WeightInfo = pallet_xcm::TestWeightInfo; + type WeightInfo = crate::weights::pallet_xcm::WeightInfo; #[cfg(feature = "runtime-benchmarks")] type ReachableDest = ReachableDest; } diff --git a/parachains/runtimes/assets/statemint/src/lib.rs b/parachains/runtimes/assets/statemint/src/lib.rs index 453516b45ad..a33d64abb76 100644 --- a/parachains/runtimes/assets/statemint/src/lib.rs +++ b/parachains/runtimes/assets/statemint/src/lib.rs @@ -685,6 +685,7 @@ mod benches { [pallet_collator_selection, CollatorSelection] [cumulus_pallet_xcmp_queue, XcmpQueue] // XCM + [pallet_xcm, PolkadotXcm] // NOTE: Make sure you point to the individual modules below. [pallet_xcm_benchmarks::fungible, XcmBalances] [pallet_xcm_benchmarks::generic, XcmGeneric] @@ -872,7 +873,7 @@ impl_runtime_apis! { impl cumulus_pallet_session_benchmarking::Config for Runtime {} use xcm::latest::prelude::*; - use xcm_config::{DotLocation, MaxAssetsIntoHolding}; + use xcm_config::{DotLocation, LocalCheckAccount, MaxAssetsIntoHolding}; use pallet_xcm_benchmarks::asset_instance_from; impl pallet_xcm_benchmarks::Config for Runtime { @@ -920,7 +921,7 @@ impl_runtime_apis! { impl pallet_xcm_benchmarks::fungible::Config for Runtime { type TransactAsset = Balances; - type CheckedAccount = CheckedAccount; + type CheckedAccount = LocalCheckAccount; type TrustedTeleporter = TrustedTeleporter; fn get_multi_asset() -> MultiAsset { diff --git a/parachains/runtimes/assets/statemint/src/weights/mod.rs b/parachains/runtimes/assets/statemint/src/weights/mod.rs index 080584f2cfa..5dd6ffd662e 100644 --- a/parachains/runtimes/assets/statemint/src/weights/mod.rs +++ b/parachains/runtimes/assets/statemint/src/weights/mod.rs @@ -11,6 +11,7 @@ pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_uniques; pub mod pallet_utility; +pub mod pallet_xcm; pub mod paritydb_weights; pub mod rocksdb_weights; pub mod xcm; diff --git a/parachains/runtimes/assets/statemint/src/weights/pallet_xcm.rs b/parachains/runtimes/assets/statemint/src/weights/pallet_xcm.rs new file mode 100644 index 00000000000..df19ab323ca --- /dev/null +++ b/parachains/runtimes/assets/statemint/src/weights/pallet_xcm.rs @@ -0,0 +1,117 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `pallet_xcm` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemint-dev"), DB CACHE: 1024 + +// Executed Command: +// /home/benchbot/cargo_target_dir/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=statemint-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/assets/statemint/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_xcm`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm::WeightInfo for WeightInfo { + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn send() -> Weight { + // Minimum execution time: 25_514 nanoseconds. + Weight::from_ref_time(26_283_000) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn teleport_assets() -> Weight { + // Minimum execution time: 32_293 nanoseconds. + Weight::from_ref_time(33_089_000) + .saturating_add(T::DbWeight::get().reads(1)) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn reserve_transfer_assets() -> Weight { + // Minimum execution time: 24_544 nanoseconds. + Weight::from_ref_time(24_953_000) + .saturating_add(T::DbWeight::get().reads(1)) + } + // Storage: Benchmark Override (r:0 w:0) + fn execute() -> Weight { + // Minimum execution time: 18_446_744_073_709_551 nanoseconds. + Weight::from_ref_time(18_446_744_073_709_551_000) + } + // Storage: PolkadotXcm SupportedVersion (r:0 w:1) + fn force_xcm_version() -> Weight { + // Minimum execution time: 14_894 nanoseconds. + Weight::from_ref_time(15_309_000) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) + fn force_default_xcm_version() -> Weight { + // Minimum execution time: 4_351 nanoseconds. + Weight::from_ref_time(4_503_000) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) + // Storage: PolkadotXcm QueryCounter (r:1 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + // Storage: PolkadotXcm Queries (r:0 w:1) + fn force_subscribe_version_notify() -> Weight { + // Minimum execution time: 30_102 nanoseconds. + Weight::from_ref_time(30_938_000) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + // Storage: PolkadotXcm Queries (r:0 w:1) + fn force_unsubscribe_version_notify() -> Weight { + // Minimum execution time: 31_133 nanoseconds. + Weight::from_ref_time(31_540_000) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} diff --git a/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs b/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs index b1a84cb9ccb..ed48d433170 100644 --- a/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/statemint/src/weights/xcm/mod.rs @@ -53,7 +53,8 @@ impl XcmWeightInfo for StatemintXcmWeight { } // Currently there is no trusted reserve fn reserve_asset_deposited(_assets: &MultiAssets) -> Weight { - Weight::MAX + // TODO: hardcoded - fix https://github.com/paritytech/cumulus/issues/1974 + Weight::from_ref_time(1_000_000_000 as u64) } fn receive_teleported_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::receive_teleported_asset()) diff --git a/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 79066a2b9e5..7ff13672a6d 100644 --- a/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -18,23 +18,23 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-09-30, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemint-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// /home/benchbot/cargo_target_dir/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=statemint-dev +// --steps=50 +// --repeat=20 +// --extrinsic=* // --execution=wasm // --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::generic -// --extrinsic=* -// --steps=50 -// --repeat=20 -// --json +// --chain=statemint-dev // --header=./file_header.txt // --template=./templates/xcm-bench-template.hbs // --output=./parachains/runtimes/assets/statemint/src/weights/xcm/ @@ -56,38 +56,40 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_holding() -> Weight { - Weight::from_ref_time(1_305_689_000 as u64) + Weight::from_ref_time(997_603_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn buy_execution() -> Weight { - Weight::from_ref_time(8_843_000 as u64) + Weight::from_ref_time(8_032_000 as u64) } // Storage: PolkadotXcm Queries (r:1 w:0) pub(crate) fn query_response() -> Weight { - Weight::from_ref_time(19_216_000 as u64) + Weight::from_ref_time(18_256_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) } + // Storage: Session NextKeys (r:1 w:0) pub(crate) fn transact() -> Weight { - Weight::from_ref_time(22_708_000 as u64) + Weight::from_ref_time(15_436_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) } pub(crate) fn refund_surplus() -> Weight { - Weight::from_ref_time(9_040_000 as u64) + Weight::from_ref_time(8_689_000 as u64) } pub(crate) fn set_error_handler() -> Weight { - Weight::from_ref_time(6_222_000 as u64) + Weight::from_ref_time(5_977_000 as u64) } pub(crate) fn set_appendix() -> Weight { - Weight::from_ref_time(6_411_000 as u64) + Weight::from_ref_time(5_988_000 as u64) } pub(crate) fn clear_error() -> Weight { - Weight::from_ref_time(6_222_000 as u64) + Weight::from_ref_time(5_933_000 as u64) } pub(crate) fn descend_origin() -> Weight { - Weight::from_ref_time(7_112_000 as u64) + Weight::from_ref_time(7_105_000 as u64) } pub(crate) fn clear_origin() -> Weight { - Weight::from_ref_time(6_340_000 as u64) + Weight::from_ref_time(6_047_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -96,18 +98,18 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_error() -> Weight { - Weight::from_ref_time(22_943_000 as u64) + Weight::from_ref_time(22_105_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: PolkadotXcm AssetTraps (r:1 w:1) pub(crate) fn claim_asset() -> Weight { - Weight::from_ref_time(13_178_000 as u64) + Weight::from_ref_time(21_843_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } pub(crate) fn trap() -> Weight { - Weight::from_ref_time(6_333_000 as u64) + Weight::from_ref_time(6_099_000 as u64) } // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -116,13 +118,13 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn subscribe_version() -> Weight { - Weight::from_ref_time(31_798_000 as u64) + Weight::from_ref_time(28_520_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) pub(crate) fn unsubscribe_version() -> Weight { - Weight::from_ref_time(9_728_000 as u64) + Weight::from_ref_time(8_113_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: ParachainInfo ParachainId (r:1 w:0) @@ -132,21 +134,21 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_reserve_withdraw() -> Weight { - Weight::from_ref_time(1_583_652_000 as u64) + Weight::from_ref_time(1_226_787_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn burn_asset() -> Weight { - Weight::from_ref_time(497_448_000 as u64) + Weight::from_ref_time(417_404_000 as u64) } pub(crate) fn expect_asset() -> Weight { - Weight::from_ref_time(38_383_000 as u64) + Weight::from_ref_time(38_060_000 as u64) } pub(crate) fn expect_origin() -> Weight { - Weight::from_ref_time(6_308_000 as u64) + Weight::from_ref_time(6_034_000 as u64) } pub(crate) fn expect_error() -> Weight { - Weight::from_ref_time(6_327_000 as u64) + Weight::from_ref_time(5_967_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -155,12 +157,12 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn query_pallet() -> Weight { - Weight::from_ref_time(26_011_000 as u64) + Weight::from_ref_time(24_608_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn expect_pallet() -> Weight { - Weight::from_ref_time(8_008_000 as u64) + Weight::from_ref_time(7_574_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -169,21 +171,23 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_transact_status() -> Weight { - Weight::from_ref_time(22_963_000 as u64) + Weight::from_ref_time(22_429_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn clear_transact_status() -> Weight { - Weight::from_ref_time(6_378_000 as u64) + Weight::from_ref_time(5_928_000 as u64) } pub(crate) fn set_topic() -> Weight { - Weight::from_ref_time(6_313_000 as u64) + Weight::from_ref_time(5_911_000 as u64) } pub(crate) fn clear_topic() -> Weight { - Weight::from_ref_time(6_324_000 as u64) + Weight::from_ref_time(5_844_000 as u64) } pub(crate) fn set_fees_mode() -> Weight { - Weight::from_ref_time(6_336_000 as u64) + Weight::from_ref_time(5_881_000 as u64) + } + pub(crate) fn unpaid_execution() -> Weight { + Weight::from_ref_time(6_052_000 as u64) } - pub(crate) fn unpaid_execution() -> Weight { Weight::from_ref_time(3_111_000 as u64) } } diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index 22b95110ee5..84a6131828b 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -36,9 +36,10 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, - NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, + MintLocation, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + WeightInfoBounds, }; use xcm_executor::{ traits::{JustTry, WithOriginFilter}, @@ -54,6 +55,8 @@ parameter_types! { pub AssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + /// The check account that is allowed to mint assets locally. + pub LocalCheckAccount: (AccountId, MintLocation) = (CheckingAccount::get(), MintLocation::Local); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used @@ -361,8 +364,7 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; - // FIXME: Replace with benchmarked weight info - type WeightInfo = pallet_xcm::TestWeightInfo; + type WeightInfo = crate::weights::pallet_xcm::WeightInfo; #[cfg(feature = "runtime-benchmarks")] type ReachableDest = ReachableDest; } diff --git a/parachains/runtimes/assets/westmint/src/lib.rs b/parachains/runtimes/assets/westmint/src/lib.rs index 65a5586f116..1178885b3ad 100644 --- a/parachains/runtimes/assets/westmint/src/lib.rs +++ b/parachains/runtimes/assets/westmint/src/lib.rs @@ -645,6 +645,7 @@ mod benches { [pallet_collator_selection, CollatorSelection] [cumulus_pallet_xcmp_queue, XcmpQueue] // XCM + [pallet_xcm, PolkadotXcm] // NOTE: Make sure you point to the individual modules below. [pallet_xcm_benchmarks::fungible, XcmBalances] [pallet_xcm_benchmarks::generic, XcmGeneric] @@ -831,7 +832,7 @@ impl_runtime_apis! { impl cumulus_pallet_session_benchmarking::Config for Runtime {} use xcm::latest::prelude::*; - use xcm_config::{MaxAssetsIntoHolding, WestendLocation}; + use xcm_config::{LocalCheckAccount, MaxAssetsIntoHolding, WestendLocation}; use pallet_xcm_benchmarks::asset_instance_from; impl pallet_xcm_benchmarks::Config for Runtime { @@ -880,7 +881,7 @@ impl_runtime_apis! { impl pallet_xcm_benchmarks::fungible::Config for Runtime { type TransactAsset = Balances; - type CheckedAccount = CheckedAccount; + type CheckedAccount = LocalCheckAccount; type TrustedTeleporter = TrustedTeleporter; fn get_multi_asset() -> MultiAsset { diff --git a/parachains/runtimes/assets/westmint/src/weights/mod.rs b/parachains/runtimes/assets/westmint/src/weights/mod.rs index 080584f2cfa..5dd6ffd662e 100644 --- a/parachains/runtimes/assets/westmint/src/weights/mod.rs +++ b/parachains/runtimes/assets/westmint/src/weights/mod.rs @@ -11,6 +11,7 @@ pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_uniques; pub mod pallet_utility; +pub mod pallet_xcm; pub mod paritydb_weights; pub mod rocksdb_weights; pub mod xcm; diff --git a/parachains/runtimes/assets/westmint/src/weights/pallet_xcm.rs b/parachains/runtimes/assets/westmint/src/weights/pallet_xcm.rs new file mode 100644 index 00000000000..7ccd41d3d20 --- /dev/null +++ b/parachains/runtimes/assets/westmint/src/weights/pallet_xcm.rs @@ -0,0 +1,116 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `pallet_xcm` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westmint-dev"), DB CACHE: 1024 + +// Executed Command: +// /home/benchbot/cargo_target_dir/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=westmint-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/assets/westmint/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_xcm`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm::WeightInfo for WeightInfo { + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn send() -> Weight { + // Minimum execution time: 26_652 nanoseconds. + Weight::from_ref_time(27_278_000) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn teleport_assets() -> Weight { + // Minimum execution time: 32_711 nanoseconds. + Weight::from_ref_time(33_447_000) + .saturating_add(T::DbWeight::get().reads(1)) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn reserve_transfer_assets() -> Weight { + // Minimum execution time: 24_850 nanoseconds. + Weight::from_ref_time(25_427_000) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn execute() -> Weight { + // Minimum execution time: 14_909 nanoseconds. + Weight::from_ref_time(15_229_000) + } + // Storage: PolkadotXcm SupportedVersion (r:0 w:1) + fn force_xcm_version() -> Weight { + // Minimum execution time: 14_930 nanoseconds. + Weight::from_ref_time(15_396_000) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) + fn force_default_xcm_version() -> Weight { + // Minimum execution time: 4_328 nanoseconds. + Weight::from_ref_time(4_510_000) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) + // Storage: PolkadotXcm QueryCounter (r:1 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + // Storage: PolkadotXcm Queries (r:0 w:1) + fn force_subscribe_version_notify() -> Weight { + // Minimum execution time: 30_943 nanoseconds. + Weight::from_ref_time(31_593_000) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + // Storage: PolkadotXcm Queries (r:0 w:1) + fn force_unsubscribe_version_notify() -> Weight { + // Minimum execution time: 31_950 nanoseconds. + Weight::from_ref_time(32_364_000) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} diff --git a/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs b/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs index 5dfd526b7db..ee63ee3f2ad 100644 --- a/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs +++ b/parachains/runtimes/assets/westmint/src/weights/xcm/mod.rs @@ -53,7 +53,8 @@ impl XcmWeightInfo for WestmintXcmWeight { } // Currently there is no trusted reserve fn reserve_asset_deposited(_assets: &MultiAssets) -> Weight { - Weight::MAX + // TODO: hardcoded - fix https://github.com/paritytech/cumulus/issues/1974 + Weight::from_ref_time(1_000_000_000 as u64) } fn receive_teleported_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::receive_teleported_asset()) diff --git a/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index e6f01884049..3587e720f51 100644 --- a/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -18,23 +18,23 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-09-30, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westmint-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// /home/benchbot/cargo_target_dir/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=westmint-dev +// --steps=50 +// --repeat=20 +// --extrinsic=* // --execution=wasm // --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::generic -// --extrinsic=* -// --steps=50 -// --repeat=20 -// --json +// --chain=westmint-dev // --header=./file_header.txt // --template=./templates/xcm-bench-template.hbs // --output=./parachains/runtimes/assets/westmint/src/weights/xcm/ @@ -56,38 +56,40 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_holding() -> Weight { - Weight::from_ref_time(1_324_853_000 as u64) + Weight::from_ref_time(1_021_862_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn buy_execution() -> Weight { - Weight::from_ref_time(8_533_000 as u64) + Weight::from_ref_time(7_993_000 as u64) } // Storage: PolkadotXcm Queries (r:1 w:0) pub(crate) fn query_response() -> Weight { - Weight::from_ref_time(19_435_000 as u64) + Weight::from_ref_time(18_053_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) } + // Storage: Session NextKeys (r:1 w:0) pub(crate) fn transact() -> Weight { - Weight::from_ref_time(22_656_000 as u64) + Weight::from_ref_time(15_191_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) } pub(crate) fn refund_surplus() -> Weight { - Weight::from_ref_time(8_900_000 as u64) + Weight::from_ref_time(8_307_000 as u64) } pub(crate) fn set_error_handler() -> Weight { - Weight::from_ref_time(6_255_000 as u64) + Weight::from_ref_time(5_916_000 as u64) } pub(crate) fn set_appendix() -> Weight { - Weight::from_ref_time(6_268_000 as u64) + Weight::from_ref_time(5_943_000 as u64) } pub(crate) fn clear_error() -> Weight { - Weight::from_ref_time(6_304_000 as u64) + Weight::from_ref_time(6_024_000 as u64) } pub(crate) fn descend_origin() -> Weight { - Weight::from_ref_time(7_279_000 as u64) + Weight::from_ref_time(6_608_000 as u64) } pub(crate) fn clear_origin() -> Weight { - Weight::from_ref_time(6_297_000 as u64) + Weight::from_ref_time(5_885_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -96,18 +98,18 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_error() -> Weight { - Weight::from_ref_time(23_025_000 as u64) + Weight::from_ref_time(21_690_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: PolkadotXcm AssetTraps (r:1 w:1) pub(crate) fn claim_asset() -> Weight { - Weight::from_ref_time(13_001_000 as u64) + Weight::from_ref_time(21_761_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } pub(crate) fn trap() -> Weight { - Weight::from_ref_time(6_266_000 as u64) + Weight::from_ref_time(5_854_000 as u64) } // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -116,13 +118,13 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn subscribe_version() -> Weight { - Weight::from_ref_time(31_348_000 as u64) + Weight::from_ref_time(27_989_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) pub(crate) fn unsubscribe_version() -> Weight { - Weight::from_ref_time(9_534_000 as u64) + Weight::from_ref_time(7_971_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: ParachainInfo ParachainId (r:1 w:0) @@ -132,21 +134,21 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_reserve_withdraw() -> Weight { - Weight::from_ref_time(1_558_814_000 as u64) + Weight::from_ref_time(1_228_949_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn burn_asset() -> Weight { - Weight::from_ref_time(496_802_000 as u64) + Weight::from_ref_time(416_374_000 as u64) } pub(crate) fn expect_asset() -> Weight { - Weight::from_ref_time(38_299_000 as u64) + Weight::from_ref_time(38_018_000 as u64) } pub(crate) fn expect_origin() -> Weight { - Weight::from_ref_time(6_354_000 as u64) + Weight::from_ref_time(5_996_000 as u64) } pub(crate) fn expect_error() -> Weight { - Weight::from_ref_time(6_234_000 as u64) + Weight::from_ref_time(5_922_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -155,12 +157,12 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn query_pallet() -> Weight { - Weight::from_ref_time(25_150_000 as u64) + Weight::from_ref_time(24_130_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn expect_pallet() -> Weight { - Weight::from_ref_time(7_969_000 as u64) + Weight::from_ref_time(7_238_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -169,21 +171,23 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_transact_status() -> Weight { - Weight::from_ref_time(23_099_000 as u64) + Weight::from_ref_time(21_209_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn clear_transact_status() -> Weight { - Weight::from_ref_time(6_366_000 as u64) + Weight::from_ref_time(5_810_000 as u64) } pub(crate) fn set_topic() -> Weight { - Weight::from_ref_time(6_422_000 as u64) + Weight::from_ref_time(5_876_000 as u64) } pub(crate) fn clear_topic() -> Weight { - Weight::from_ref_time(6_405_000 as u64) + Weight::from_ref_time(5_793_000 as u64) } pub(crate) fn set_fees_mode() -> Weight { - Weight::from_ref_time(6_392_000 as u64) + Weight::from_ref_time(5_902_000 as u64) + } + pub(crate) fn unpaid_execution() -> Weight { + Weight::from_ref_time(6_092_000 as u64) } - pub(crate) fn unpaid_execution() -> Weight { Weight::from_ref_time(3_111_000 as u64) } } diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index 638085a5d3a..41d0f901cb0 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -36,9 +36,10 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, - NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, + MintLocation, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + WeightInfoBounds, }; use xcm_executor::{ traits::{JustTry, WithOriginFilter}, @@ -54,6 +55,8 @@ parameter_types! { pub AssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + /// The check account that is allowed to mint assets locally. + pub LocalCheckAccount: (AccountId, MintLocation) = (CheckingAccount::get(), MintLocation::Local); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used @@ -352,8 +355,7 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; - // FIXME: Replace with benchmarked weight info - type WeightInfo = pallet_xcm::TestWeightInfo; + type WeightInfo = crate::weights::pallet_xcm::WeightInfo; #[cfg(feature = "runtime-benchmarks")] type ReachableDest = ReachableDest; } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs index 705bbfda9e5..7fa2fc6154e 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs @@ -440,6 +440,7 @@ mod benches { [pallet_timestamp, Timestamp] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_xcmp_queue, XcmpQueue] + [pallet_xcm, PolkadotXcm] ); } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/mod.rs index 56c03468aa7..9652048d249 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/mod.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/mod.rs @@ -27,6 +27,7 @@ pub mod pallet_multisig; pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_utility; +pub mod pallet_xcm; pub mod paritydb_weights; pub mod rocksdb_weights; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_xcm.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_xcm.rs new file mode 100644 index 00000000000..cd24496f035 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_xcm.rs @@ -0,0 +1,116 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `pallet_xcm` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-12-11, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-kusama-dev"), DB CACHE: 1024 + +// Executed Command: +// /home/benchbot/cargo_target_dir/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=bridge-hub-kusama-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_xcm`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm::WeightInfo for WeightInfo { + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn send() -> Weight { + // Minimum execution time: 26_345 nanoseconds. + Weight::from_ref_time(26_996_000) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn teleport_assets() -> Weight { + // Minimum execution time: 32_889 nanoseconds. + Weight::from_ref_time(33_380_000) + .saturating_add(T::DbWeight::get().reads(1)) + } + // Storage: Benchmark Override (r:0 w:0) + fn reserve_transfer_assets() -> Weight { + // Minimum execution time: 18_446_744_073_709_551 nanoseconds. + Weight::from_ref_time(18_446_744_073_709_551_000) + } + // Storage: Benchmark Override (r:0 w:0) + fn execute() -> Weight { + // Minimum execution time: 18_446_744_073_709_551 nanoseconds. + Weight::from_ref_time(18_446_744_073_709_551_000) + } + // Storage: PolkadotXcm SupportedVersion (r:0 w:1) + fn force_xcm_version() -> Weight { + // Minimum execution time: 14_707 nanoseconds. + Weight::from_ref_time(14_851_000) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) + fn force_default_xcm_version() -> Weight { + // Minimum execution time: 4_465 nanoseconds. + Weight::from_ref_time(4_580_000) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) + // Storage: PolkadotXcm QueryCounter (r:1 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + // Storage: PolkadotXcm Queries (r:0 w:1) + fn force_subscribe_version_notify() -> Weight { + // Minimum execution time: 30_482 nanoseconds. + Weight::from_ref_time(31_091_000) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + // Storage: PolkadotXcm Queries (r:0 w:1) + fn force_unsubscribe_version_notify() -> Weight { + // Minimum execution time: 31_341 nanoseconds. + Weight::from_ref_time(31_876_000) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index 947ab24f127..3d96de89721 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -246,7 +246,7 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; - type WeightInfo = pallet_xcm::TestWeightInfo; + type WeightInfo = crate::weights::pallet_xcm::WeightInfo; #[cfg(feature = "runtime-benchmarks")] type ReachableDest = ReachableDest; } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 26590d912ca..d7f59ecff30 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -469,6 +469,7 @@ mod benches { [pallet_timestamp, Timestamp] [pallet_collator_selection, CollatorSelection] [cumulus_pallet_xcmp_queue, XcmpQueue] + [pallet_xcm, PolkadotXcm] ); } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs index 56c03468aa7..9652048d249 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs @@ -27,6 +27,7 @@ pub mod pallet_multisig; pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_utility; +pub mod pallet_xcm; pub mod paritydb_weights; pub mod rocksdb_weights; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs new file mode 100644 index 00000000000..de92cd8c026 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs @@ -0,0 +1,116 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `pallet_xcm` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-12-11, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// /home/benchbot/cargo_target_dir/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_xcm`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm::WeightInfo for WeightInfo { + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn send() -> Weight { + // Minimum execution time: 26_277 nanoseconds. + Weight::from_ref_time(27_202_000) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn teleport_assets() -> Weight { + // Minimum execution time: 32_941 nanoseconds. + Weight::from_ref_time(33_458_000) + .saturating_add(T::DbWeight::get().reads(1)) + } + // Storage: Benchmark Override (r:0 w:0) + fn reserve_transfer_assets() -> Weight { + // Minimum execution time: 18_446_744_073_709_551 nanoseconds. + Weight::from_ref_time(18_446_744_073_709_551_000) + } + // Storage: Benchmark Override (r:0 w:0) + fn execute() -> Weight { + // Minimum execution time: 18_446_744_073_709_551 nanoseconds. + Weight::from_ref_time(18_446_744_073_709_551_000) + } + // Storage: PolkadotXcm SupportedVersion (r:0 w:1) + fn force_xcm_version() -> Weight { + // Minimum execution time: 14_836 nanoseconds. + Weight::from_ref_time(15_261_000) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) + fn force_default_xcm_version() -> Weight { + // Minimum execution time: 4_373 nanoseconds. + Weight::from_ref_time(4_537_000) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) + // Storage: PolkadotXcm QueryCounter (r:1 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + // Storage: PolkadotXcm Queries (r:0 w:1) + fn force_subscribe_version_notify() -> Weight { + // Minimum execution time: 30_536 nanoseconds. + Weight::from_ref_time(31_384_000) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + // Storage: PolkadotXcm Queries (r:0 w:1) + fn force_unsubscribe_version_notify() -> Weight { + // Minimum execution time: 31_667 nanoseconds. + Weight::from_ref_time(32_335_000) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index bb6f9ff1ad2..d79564ea2ea 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -242,7 +242,7 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; - type WeightInfo = pallet_xcm::TestWeightInfo; + type WeightInfo = crate::weights::pallet_xcm::WeightInfo; #[cfg(feature = "runtime-benchmarks")] type ReachableDest = ReachableDest; } diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/lib.rs b/parachains/runtimes/collectives/collectives-polkadot/src/lib.rs index a329f80e39e..3ae7ef27580 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/lib.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/lib.rs @@ -587,6 +587,7 @@ mod benches { [cumulus_pallet_xcmp_queue, XcmpQueue] [pallet_alliance, Alliance] [pallet_collective, AllianceMotion] + [pallet_xcm, PolkadotXcm] ); } diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/weights/mod.rs b/parachains/runtimes/collectives/collectives-polkadot/src/weights/mod.rs index bf736022ca7..76fbc081bc5 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/weights/mod.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/weights/mod.rs @@ -11,6 +11,7 @@ pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_utility; +pub mod pallet_xcm; pub mod paritydb_weights; pub mod rocksdb_weights; diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_xcm.rs b/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_xcm.rs new file mode 100644 index 00000000000..ea7701e2016 --- /dev/null +++ b/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_xcm.rs @@ -0,0 +1,116 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `pallet_xcm` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-12-11, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("collectives-polkadot-dev"), DB CACHE: 1024 + +// Executed Command: +// /home/benchbot/cargo_target_dir/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_xcm +// --chain=collectives-polkadot-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/collectives/collectives-polkadot/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_xcm`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm::WeightInfo for WeightInfo { + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn send() -> Weight { + // Minimum execution time: 25_986 nanoseconds. + Weight::from_ref_time(26_603_000) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn teleport_assets() -> Weight { + // Minimum execution time: 32_738 nanoseconds. + Weight::from_ref_time(33_574_000) + .saturating_add(T::DbWeight::get().reads(1)) + } + // Storage: Benchmark Override (r:0 w:0) + fn reserve_transfer_assets() -> Weight { + // Minimum execution time: 18_446_744_073_709_551 nanoseconds. + Weight::from_ref_time(18_446_744_073_709_551_000) + } + // Storage: Benchmark Override (r:0 w:0) + fn execute() -> Weight { + // Minimum execution time: 18_446_744_073_709_551 nanoseconds. + Weight::from_ref_time(18_446_744_073_709_551_000) + } + // Storage: PolkadotXcm SupportedVersion (r:0 w:1) + fn force_xcm_version() -> Weight { + // Minimum execution time: 14_431 nanoseconds. + Weight::from_ref_time(14_923_000) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) + fn force_default_xcm_version() -> Weight { + // Minimum execution time: 4_413 nanoseconds. + Weight::from_ref_time(4_640_000) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) + // Storage: PolkadotXcm QueryCounter (r:1 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + // Storage: PolkadotXcm Queries (r:0 w:1) + fn force_subscribe_version_notify() -> Weight { + // Minimum execution time: 30_293 nanoseconds. + Weight::from_ref_time(30_769_000) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(5)) + } + // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + // Storage: PolkadotXcm Queries (r:0 w:1) + fn force_unsubscribe_version_notify() -> Weight { + // Minimum execution time: 31_044 nanoseconds. + Weight::from_ref_time(31_600_000) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(4)) + } +} diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index f776297d69d..a06a5524c11 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -267,8 +267,7 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<8>; - // FIXME: Replace with benchmarked weight info - type WeightInfo = pallet_xcm::TestWeightInfo; + type WeightInfo = crate::weights::pallet_xcm::WeightInfo; #[cfg(feature = "runtime-benchmarks")] type ReachableDest = ReachableDest; } diff --git a/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index ce18548d005..85208f94946 100644 --- a/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -99,7 +99,6 @@ pub type CheckedExtrinsic = generic::CheckedExtrinsic, - pallet_balances::migration::MigrateToTrackInactive, ); /// Executive: handles dispatch to the various modules. @@ -385,6 +384,7 @@ mod benches { [pallet_timestamp, Timestamp] [pallet_collator_selection, CollatorSelection] [pallet_contracts, Contracts] + [pallet_xcm, PolkadotXcm] ); } diff --git a/parachains/runtimes/testing/rococo-parachain/Cargo.toml b/parachains/runtimes/testing/rococo-parachain/Cargo.toml index a50349c4f3d..7bb3361d32a 100644 --- a/parachains/runtimes/testing/rococo-parachain/Cargo.toml +++ b/parachains/runtimes/testing/rococo-parachain/Cargo.toml @@ -103,4 +103,5 @@ std = [ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "pallet-assets/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", ] From 27f9798882df466e99e7ba13c8f4d919be88514f Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 13 Dec 2022 16:45:34 +0100 Subject: [PATCH 153/263] cargo fmt --- parachains/runtimes/contracts/contracts-rococo/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index 85208f94946..b490b0436d7 100644 --- a/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -97,9 +97,7 @@ pub type UncheckedExtrinsic = /// Extrinsic type that has already been checked. pub type CheckedExtrinsic = generic::CheckedExtrinsic; -pub type Migrations = ( - pallet_contracts::Migration, -); +pub type Migrations = (pallet_contracts::Migration,); /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< From bb821eb00f0e503cbce31ea9860e123b87574c19 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 14 Dec 2022 17:15:25 +0900 Subject: [PATCH 154/263] Fix benchmarks --- parachains/runtimes/assets/statemine/src/lib.rs | 6 +++--- parachains/runtimes/assets/statemine/src/xcm_config.rs | 9 +++------ parachains/runtimes/assets/statemint/src/lib.rs | 6 +++--- parachains/runtimes/assets/statemint/src/xcm_config.rs | 9 +++------ parachains/runtimes/assets/westmint/src/lib.rs | 4 ++-- parachains/runtimes/assets/westmint/src/xcm_config.rs | 9 +++------ 6 files changed, 17 insertions(+), 26 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/lib.rs b/parachains/runtimes/assets/statemine/src/lib.rs index fdec9c6ff38..694de2a46b3 100644 --- a/parachains/runtimes/assets/statemine/src/lib.rs +++ b/parachains/runtimes/assets/statemine/src/lib.rs @@ -860,7 +860,7 @@ impl_runtime_apis! { impl cumulus_pallet_session_benchmarking::Config for Runtime {} use xcm::latest::prelude::*; - use xcm_config::{LocalCheckAccount, KsmLocation, MaxAssetsIntoHolding}; + use xcm_config::{KsmLocation, MaxAssetsIntoHolding}; use pallet_xcm_benchmarks::asset_instance_from; impl pallet_xcm_benchmarks::Config for Runtime { @@ -902,14 +902,14 @@ impl_runtime_apis! { KsmLocation::get(), MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(KsmLocation::get()) }, )); - pub const CheckedAccount: Option = None; + pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None; } impl pallet_xcm_benchmarks::fungible::Config for Runtime { type TransactAsset = Balances; - type CheckedAccount = LocalCheckAccount; + type CheckedAccount = CheckedAccount; type TrustedTeleporter = TrustedTeleporter; fn get_multi_asset() -> MultiAsset { diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 70e60530a45..a659ac06ba3 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -36,10 +36,9 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, - MintLocation, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, - WeightInfoBounds, + NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, }; use xcm_executor::{ traits::{JustTry, WithOriginFilter}, @@ -55,8 +54,6 @@ parameter_types! { pub AssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); - /// The check account that is allowed to mint assets locally. - pub LocalCheckAccount: (AccountId, MintLocation) = (CheckingAccount::get(), MintLocation::Local); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used diff --git a/parachains/runtimes/assets/statemint/src/lib.rs b/parachains/runtimes/assets/statemint/src/lib.rs index a33d64abb76..f07a2e8408b 100644 --- a/parachains/runtimes/assets/statemint/src/lib.rs +++ b/parachains/runtimes/assets/statemint/src/lib.rs @@ -873,7 +873,7 @@ impl_runtime_apis! { impl cumulus_pallet_session_benchmarking::Config for Runtime {} use xcm::latest::prelude::*; - use xcm_config::{DotLocation, LocalCheckAccount, MaxAssetsIntoHolding}; + use xcm_config::{DotLocation, MaxAssetsIntoHolding}; use pallet_xcm_benchmarks::asset_instance_from; impl pallet_xcm_benchmarks::Config for Runtime { @@ -915,13 +915,13 @@ impl_runtime_apis! { DotLocation::get(), MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(DotLocation::get()) }, )); - pub const CheckedAccount: Option = None; + pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None; } impl pallet_xcm_benchmarks::fungible::Config for Runtime { type TransactAsset = Balances; - type CheckedAccount = LocalCheckAccount; + type CheckedAccount = CheckedAccount; type TrustedTeleporter = TrustedTeleporter; fn get_multi_asset() -> MultiAsset { diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index 84a6131828b..b6c6b959cb6 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -36,10 +36,9 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, - MintLocation, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, - WeightInfoBounds, + NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, }; use xcm_executor::{ traits::{JustTry, WithOriginFilter}, @@ -55,8 +54,6 @@ parameter_types! { pub AssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); - /// The check account that is allowed to mint assets locally. - pub LocalCheckAccount: (AccountId, MintLocation) = (CheckingAccount::get(), MintLocation::Local); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used diff --git a/parachains/runtimes/assets/westmint/src/lib.rs b/parachains/runtimes/assets/westmint/src/lib.rs index 1178885b3ad..1bf530380f5 100644 --- a/parachains/runtimes/assets/westmint/src/lib.rs +++ b/parachains/runtimes/assets/westmint/src/lib.rs @@ -874,14 +874,14 @@ impl_runtime_apis! { WestendLocation::get(), MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(WestendLocation::get()) }, )); - pub const CheckedAccount: Option = None; + pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None; } impl pallet_xcm_benchmarks::fungible::Config for Runtime { type TransactAsset = Balances; - type CheckedAccount = LocalCheckAccount; + type CheckedAccount = CheckedAccount; type TrustedTeleporter = TrustedTeleporter; fn get_multi_asset() -> MultiAsset { diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index 41d0f901cb0..d7833eb96f9 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -36,10 +36,9 @@ use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, - MintLocation, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, - WeightInfoBounds, + NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, }; use xcm_executor::{ traits::{JustTry, WithOriginFilter}, @@ -55,8 +54,6 @@ parameter_types! { pub AssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); - /// The check account that is allowed to mint assets locally. - pub LocalCheckAccount: (AccountId, MintLocation) = (CheckingAccount::get(), MintLocation::Local); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used From c70a11fc50a908d2438cac2c21206e32ecce759c Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 14 Dec 2022 16:12:01 +0100 Subject: [PATCH 155/263] Bko gav xcm v3 (#1993) * Fix * ".git/.scripts/bench-bot.sh" xcm statemint assets pallet_xcm_benchmarks::fungible * ".git/.scripts/bench-bot.sh" xcm statemine assets pallet_xcm_benchmarks::fungible * ".git/.scripts/bench-bot.sh" xcm westmint assets pallet_xcm_benchmarks::fungible * ".git/.scripts/bench-bot.sh" xcm statemine assets pallet_xcm_benchmarks::generic * ".git/.scripts/bench-bot.sh" xcm statemint assets pallet_xcm_benchmarks::generic * ".git/.scripts/bench-bot.sh" xcm westmint assets pallet_xcm_benchmarks::generic * ".git/.scripts/bench-bot.sh" pallet statemine assets pallet_xcm * ".git/.scripts/bench-bot.sh" pallet westmint assets pallet_xcm * ".git/.scripts/bench-bot.sh" pallet statemint assets pallet_xcm * ".git/.scripts/bench-bot.sh" pallet collectives-polkadot collectives pallet_xcm * ".git/.scripts/bench-bot.sh" pallet bridge-hub-kusama bridge-hubs pallet_xcm * ".git/.scripts/bench-bot.sh" pallet bridge-hub-rococo bridge-hubs pallet_xcm Co-authored-by: command-bot <> --- .../statemine/src/weights/pallet_xcm.rs | 30 +++++----- .../xcm/pallet_xcm_benchmarks_fungible.rs | 35 +++++------ .../xcm/pallet_xcm_benchmarks_generic.rs | 60 +++++++++---------- .../statemint/src/weights/pallet_xcm.rs | 30 +++++----- .../xcm/pallet_xcm_benchmarks_fungible.rs | 35 +++++------ .../xcm/pallet_xcm_benchmarks_generic.rs | 60 +++++++++---------- .../runtimes/assets/westmint/src/lib.rs | 2 +- .../assets/westmint/src/weights/pallet_xcm.rs | 34 +++++------ .../xcm/pallet_xcm_benchmarks_fungible.rs | 35 +++++------ .../xcm/pallet_xcm_benchmarks_generic.rs | 60 +++++++++---------- .../src/weights/pallet_xcm.rs | 26 ++++---- .../src/weights/pallet_xcm.rs | 26 ++++---- .../src/weights/pallet_xcm.rs | 26 ++++---- 13 files changed, 228 insertions(+), 231 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/weights/pallet_xcm.rs b/parachains/runtimes/assets/statemine/src/weights/pallet_xcm.rs index 49dffce4223..84b38c418b1 100644 --- a/parachains/runtimes/assets/statemine/src/weights/pallet_xcm.rs +++ b/parachains/runtimes/assets/statemine/src/weights/pallet_xcm.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemine-dev"), DB CACHE: 1024 @@ -53,21 +53,21 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) fn send() -> Weight { - // Minimum execution time: 25_562 nanoseconds. - Weight::from_ref_time(26_177_000) + // Minimum execution time: 27_480 nanoseconds. + Weight::from_ref_time(27_952_000) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn teleport_assets() -> Weight { - // Minimum execution time: 31_555 nanoseconds. - Weight::from_ref_time(32_297_000) + // Minimum execution time: 32_034 nanoseconds. + Weight::from_ref_time(32_516_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn reserve_transfer_assets() -> Weight { - // Minimum execution time: 24_516 nanoseconds. - Weight::from_ref_time(25_126_000) + // Minimum execution time: 25_008 nanoseconds. + Weight::from_ref_time(25_304_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: Benchmark Override (r:0 w:0) @@ -77,14 +77,14 @@ impl pallet_xcm::WeightInfo for WeightInfo { } // Storage: PolkadotXcm SupportedVersion (r:0 w:1) fn force_xcm_version() -> Weight { - // Minimum execution time: 14_521 nanoseconds. - Weight::from_ref_time(14_865_000) + // Minimum execution time: 14_853 nanoseconds. + Weight::from_ref_time(15_204_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) fn force_default_xcm_version() -> Weight { - // Minimum execution time: 4_325 nanoseconds. - Weight::from_ref_time(4_578_000) + // Minimum execution time: 4_423 nanoseconds. + Weight::from_ref_time(4_533_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) @@ -96,8 +96,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_subscribe_version_notify() -> Weight { - // Minimum execution time: 29_836 nanoseconds. - Weight::from_ref_time(30_213_000) + // Minimum execution time: 30_668 nanoseconds. + Weight::from_ref_time(31_262_000) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -109,8 +109,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_unsubscribe_version_notify() -> Weight { - // Minimum execution time: 31_333 nanoseconds. - Weight::from_ref_time(31_808_000) + // Minimum execution time: 31_955 nanoseconds. + Weight::from_ref_time(32_364_000) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } diff --git a/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 27e7bcf448f..52ae41f2d63 100644 --- a/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemine-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// /home/benchbot/cargo_target_dir/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=statemine-dev +// --steps=50 +// --repeat=20 +// --extrinsic=* // --execution=wasm // --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::fungible -// --extrinsic=* -// --steps=50 -// --repeat=20 -// --json +// --chain=statemine-dev // --header=./file_header.txt -// --output=./parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +// --template=./templates/xcm-bench-template.hbs +// --output=./parachains/runtimes/assets/statemine/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,13 +51,13 @@ pub struct WeightInfo(PhantomData); impl WeightInfo { // Storage: System Account (r:1 w:1) pub(crate) fn withdraw_asset() -> Weight { - Weight::from_ref_time(35_859_000 as u64) + Weight::from_ref_time(31_498_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: System Account (r:2 w:2) pub(crate) fn transfer_asset() -> Weight { - Weight::from_ref_time(41_350_000 as u64) + Weight::from_ref_time(39_782_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } @@ -68,16 +69,16 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn transfer_reserve_asset() -> Weight { - Weight::from_ref_time(57_851_000 as u64) + Weight::from_ref_time(55_814_000 as u64) .saturating_add(T::DbWeight::get().reads(8 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } pub(crate) fn receive_teleported_asset() -> Weight { - Weight::from_ref_time(7_180_000 as u64) + Weight::from_ref_time(7_366_000 as u64) } // Storage: System Account (r:1 w:1) pub(crate) fn deposit_asset() -> Weight { - Weight::from_ref_time(37_404_000 as u64) + Weight::from_ref_time(59_346_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } @@ -89,7 +90,7 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn deposit_reserve_asset() -> Weight { - Weight::from_ref_time(54_907_000 as u64) + Weight::from_ref_time(49_991_000 as u64) .saturating_add(T::DbWeight::get().reads(7 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } @@ -100,7 +101,7 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_teleport() -> Weight { - Weight::from_ref_time(29_005_000 as u64) + Weight::from_ref_time(26_751_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } diff --git a/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 192173926fa..96040ac7e85 100644 --- a/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/statemine/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemine-dev"), DB CACHE: 1024 @@ -56,40 +56,38 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_holding() -> Weight { - Weight::from_ref_time(1_007_454_000 as u64) + Weight::from_ref_time(337_149_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn buy_execution() -> Weight { - Weight::from_ref_time(8_069_000 as u64) + Weight::from_ref_time(7_033_000 as u64) } // Storage: PolkadotXcm Queries (r:1 w:0) pub(crate) fn query_response() -> Weight { - Weight::from_ref_time(17_719_000 as u64) + Weight::from_ref_time(17_915_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) } - // Storage: Session NextKeys (r:1 w:0) pub(crate) fn transact() -> Weight { - Weight::from_ref_time(15_102_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + Weight::from_ref_time(20_973_000 as u64) } pub(crate) fn refund_surplus() -> Weight { - Weight::from_ref_time(8_103_000 as u64) + Weight::from_ref_time(7_210_000 as u64) } pub(crate) fn set_error_handler() -> Weight { - Weight::from_ref_time(5_803_000 as u64) + Weight::from_ref_time(5_940_000 as u64) } pub(crate) fn set_appendix() -> Weight { - Weight::from_ref_time(5_761_000 as u64) + Weight::from_ref_time(5_869_000 as u64) } pub(crate) fn clear_error() -> Weight { - Weight::from_ref_time(5_665_000 as u64) + Weight::from_ref_time(5_860_000 as u64) } pub(crate) fn descend_origin() -> Weight { - Weight::from_ref_time(6_640_000 as u64) + Weight::from_ref_time(6_556_000 as u64) } pub(crate) fn clear_origin() -> Weight { - Weight::from_ref_time(5_832_000 as u64) + Weight::from_ref_time(5_862_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -98,18 +96,18 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_error() -> Weight { - Weight::from_ref_time(21_808_000 as u64) + Weight::from_ref_time(21_918_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: PolkadotXcm AssetTraps (r:1 w:1) pub(crate) fn claim_asset() -> Weight { - Weight::from_ref_time(21_743_000 as u64) + Weight::from_ref_time(22_211_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } pub(crate) fn trap() -> Weight { - Weight::from_ref_time(5_793_000 as u64) + Weight::from_ref_time(5_844_000 as u64) } // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -118,13 +116,13 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn subscribe_version() -> Weight { - Weight::from_ref_time(27_736_000 as u64) + Weight::from_ref_time(28_788_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) pub(crate) fn unsubscribe_version() -> Weight { - Weight::from_ref_time(7_913_000 as u64) + Weight::from_ref_time(8_975_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: ParachainInfo ParachainId (r:1 w:0) @@ -134,21 +132,21 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_reserve_withdraw() -> Weight { - Weight::from_ref_time(1_181_673_000 as u64) + Weight::from_ref_time(417_939_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn burn_asset() -> Weight { - Weight::from_ref_time(415_651_000 as u64) + Weight::from_ref_time(137_412_000 as u64) } pub(crate) fn expect_asset() -> Weight { - Weight::from_ref_time(38_939_000 as u64) + Weight::from_ref_time(16_437_000 as u64) } pub(crate) fn expect_origin() -> Weight { - Weight::from_ref_time(5_918_000 as u64) + Weight::from_ref_time(5_890_000 as u64) } pub(crate) fn expect_error() -> Weight { - Weight::from_ref_time(5_829_000 as u64) + Weight::from_ref_time(5_859_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -157,12 +155,12 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn query_pallet() -> Weight { - Weight::from_ref_time(24_557_000 as u64) + Weight::from_ref_time(25_004_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn expect_pallet() -> Weight { - Weight::from_ref_time(7_407_000 as u64) + Weight::from_ref_time(7_708_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -171,23 +169,23 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_transact_status() -> Weight { - Weight::from_ref_time(21_839_000 as u64) + Weight::from_ref_time(22_445_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn clear_transact_status() -> Weight { - Weight::from_ref_time(5_796_000 as u64) + Weight::from_ref_time(5_822_000 as u64) } pub(crate) fn set_topic() -> Weight { - Weight::from_ref_time(5_808_000 as u64) + Weight::from_ref_time(5_763_000 as u64) } pub(crate) fn clear_topic() -> Weight { - Weight::from_ref_time(5_790_000 as u64) + Weight::from_ref_time(5_766_000 as u64) } pub(crate) fn set_fees_mode() -> Weight { - Weight::from_ref_time(5_774_000 as u64) + Weight::from_ref_time(5_907_000 as u64) } pub(crate) fn unpaid_execution() -> Weight { - Weight::from_ref_time(5_930_000 as u64) + Weight::from_ref_time(5_996_000 as u64) } } diff --git a/parachains/runtimes/assets/statemint/src/weights/pallet_xcm.rs b/parachains/runtimes/assets/statemint/src/weights/pallet_xcm.rs index df19ab323ca..cb9e07533a6 100644 --- a/parachains/runtimes/assets/statemint/src/weights/pallet_xcm.rs +++ b/parachains/runtimes/assets/statemint/src/weights/pallet_xcm.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemint-dev"), DB CACHE: 1024 @@ -53,21 +53,21 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) fn send() -> Weight { - // Minimum execution time: 25_514 nanoseconds. - Weight::from_ref_time(26_283_000) + // Minimum execution time: 27_134 nanoseconds. + Weight::from_ref_time(27_929_000) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn teleport_assets() -> Weight { - // Minimum execution time: 32_293 nanoseconds. - Weight::from_ref_time(33_089_000) + // Minimum execution time: 32_099 nanoseconds. + Weight::from_ref_time(32_640_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn reserve_transfer_assets() -> Weight { - // Minimum execution time: 24_544 nanoseconds. - Weight::from_ref_time(24_953_000) + // Minimum execution time: 24_795 nanoseconds. + Weight::from_ref_time(25_143_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: Benchmark Override (r:0 w:0) @@ -77,14 +77,14 @@ impl pallet_xcm::WeightInfo for WeightInfo { } // Storage: PolkadotXcm SupportedVersion (r:0 w:1) fn force_xcm_version() -> Weight { - // Minimum execution time: 14_894 nanoseconds. - Weight::from_ref_time(15_309_000) + // Minimum execution time: 15_099 nanoseconds. + Weight::from_ref_time(15_360_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) fn force_default_xcm_version() -> Weight { - // Minimum execution time: 4_351 nanoseconds. - Weight::from_ref_time(4_503_000) + // Minimum execution time: 4_460 nanoseconds. + Weight::from_ref_time(4_646_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) @@ -96,8 +96,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_subscribe_version_notify() -> Weight { - // Minimum execution time: 30_102 nanoseconds. - Weight::from_ref_time(30_938_000) + // Minimum execution time: 31_019 nanoseconds. + Weight::from_ref_time(31_816_000) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -109,8 +109,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_unsubscribe_version_notify() -> Weight { - // Minimum execution time: 31_133 nanoseconds. - Weight::from_ref_time(31_540_000) + // Minimum execution time: 32_139 nanoseconds. + Weight::from_ref_time(32_607_000) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } diff --git a/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index a8a87b7a0a4..286623a5ab7 100644 --- a/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemint-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// /home/benchbot/cargo_target_dir/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=statemint-dev +// --steps=50 +// --repeat=20 +// --extrinsic=* // --execution=wasm // --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::fungible -// --extrinsic=* -// --steps=50 -// --repeat=20 -// --json +// --chain=statemint-dev // --header=./file_header.txt -// --output=./parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +// --template=./templates/xcm-bench-template.hbs +// --output=./parachains/runtimes/assets/statemint/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,13 +51,13 @@ pub struct WeightInfo(PhantomData); impl WeightInfo { // Storage: System Account (r:1 w:1) pub(crate) fn withdraw_asset() -> Weight { - Weight::from_ref_time(36_776_000 as u64) + Weight::from_ref_time(30_093_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: System Account (r:2 w:2) pub(crate) fn transfer_asset() -> Weight { - Weight::from_ref_time(42_233_000 as u64) + Weight::from_ref_time(38_287_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } @@ -68,16 +69,16 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn transfer_reserve_asset() -> Weight { - Weight::from_ref_time(58_980_000 as u64) + Weight::from_ref_time(55_153_000 as u64) .saturating_add(T::DbWeight::get().reads(8 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } pub(crate) fn receive_teleported_asset() -> Weight { - Weight::from_ref_time(7_369_000 as u64) + Weight::from_ref_time(7_127_000 as u64) } // Storage: System Account (r:1 w:1) pub(crate) fn deposit_asset() -> Weight { - Weight::from_ref_time(37_737_000 as u64) + Weight::from_ref_time(31_534_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } @@ -89,7 +90,7 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn deposit_reserve_asset() -> Weight { - Weight::from_ref_time(56_116_000 as u64) + Weight::from_ref_time(50_137_000 as u64) .saturating_add(T::DbWeight::get().reads(7 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } @@ -100,7 +101,7 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_teleport() -> Weight { - Weight::from_ref_time(30_648_000 as u64) + Weight::from_ref_time(26_702_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } diff --git a/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 7ff13672a6d..27c05868787 100644 --- a/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/statemint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemint-dev"), DB CACHE: 1024 @@ -56,40 +56,38 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_holding() -> Weight { - Weight::from_ref_time(997_603_000 as u64) + Weight::from_ref_time(331_611_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn buy_execution() -> Weight { - Weight::from_ref_time(8_032_000 as u64) + Weight::from_ref_time(6_432_000 as u64) } // Storage: PolkadotXcm Queries (r:1 w:0) pub(crate) fn query_response() -> Weight { - Weight::from_ref_time(18_256_000 as u64) + Weight::from_ref_time(17_465_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) } - // Storage: Session NextKeys (r:1 w:0) pub(crate) fn transact() -> Weight { - Weight::from_ref_time(15_436_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + Weight::from_ref_time(21_163_000 as u64) } pub(crate) fn refund_surplus() -> Weight { - Weight::from_ref_time(8_689_000 as u64) + Weight::from_ref_time(7_024_000 as u64) } pub(crate) fn set_error_handler() -> Weight { - Weight::from_ref_time(5_977_000 as u64) + Weight::from_ref_time(5_758_000 as u64) } pub(crate) fn set_appendix() -> Weight { - Weight::from_ref_time(5_988_000 as u64) + Weight::from_ref_time(5_832_000 as u64) } pub(crate) fn clear_error() -> Weight { - Weight::from_ref_time(5_933_000 as u64) + Weight::from_ref_time(5_763_000 as u64) } pub(crate) fn descend_origin() -> Weight { - Weight::from_ref_time(7_105_000 as u64) + Weight::from_ref_time(6_560_000 as u64) } pub(crate) fn clear_origin() -> Weight { - Weight::from_ref_time(6_047_000 as u64) + Weight::from_ref_time(5_765_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -98,18 +96,18 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_error() -> Weight { - Weight::from_ref_time(22_105_000 as u64) + Weight::from_ref_time(21_465_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: PolkadotXcm AssetTraps (r:1 w:1) pub(crate) fn claim_asset() -> Weight { - Weight::from_ref_time(21_843_000 as u64) + Weight::from_ref_time(21_284_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } pub(crate) fn trap() -> Weight { - Weight::from_ref_time(6_099_000 as u64) + Weight::from_ref_time(5_723_000 as u64) } // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -118,13 +116,13 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn subscribe_version() -> Weight { - Weight::from_ref_time(28_520_000 as u64) + Weight::from_ref_time(27_907_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) pub(crate) fn unsubscribe_version() -> Weight { - Weight::from_ref_time(8_113_000 as u64) + Weight::from_ref_time(7_971_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: ParachainInfo ParachainId (r:1 w:0) @@ -134,21 +132,21 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_reserve_withdraw() -> Weight { - Weight::from_ref_time(1_226_787_000 as u64) + Weight::from_ref_time(384_329_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn burn_asset() -> Weight { - Weight::from_ref_time(417_404_000 as u64) + Weight::from_ref_time(127_341_000 as u64) } pub(crate) fn expect_asset() -> Weight { - Weight::from_ref_time(38_060_000 as u64) + Weight::from_ref_time(15_151_000 as u64) } pub(crate) fn expect_origin() -> Weight { - Weight::from_ref_time(6_034_000 as u64) + Weight::from_ref_time(5_828_000 as u64) } pub(crate) fn expect_error() -> Weight { - Weight::from_ref_time(5_967_000 as u64) + Weight::from_ref_time(5_758_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -157,12 +155,12 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn query_pallet() -> Weight { - Weight::from_ref_time(24_608_000 as u64) + Weight::from_ref_time(23_974_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn expect_pallet() -> Weight { - Weight::from_ref_time(7_574_000 as u64) + Weight::from_ref_time(7_579_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -171,23 +169,23 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_transact_status() -> Weight { - Weight::from_ref_time(22_429_000 as u64) + Weight::from_ref_time(21_711_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn clear_transact_status() -> Weight { - Weight::from_ref_time(5_928_000 as u64) + Weight::from_ref_time(5_798_000 as u64) } pub(crate) fn set_topic() -> Weight { - Weight::from_ref_time(5_911_000 as u64) + Weight::from_ref_time(5_876_000 as u64) } pub(crate) fn clear_topic() -> Weight { - Weight::from_ref_time(5_844_000 as u64) + Weight::from_ref_time(5_690_000 as u64) } pub(crate) fn set_fees_mode() -> Weight { - Weight::from_ref_time(5_881_000 as u64) + Weight::from_ref_time(5_668_000 as u64) } pub(crate) fn unpaid_execution() -> Weight { - Weight::from_ref_time(6_052_000 as u64) + Weight::from_ref_time(6_005_000 as u64) } } diff --git a/parachains/runtimes/assets/westmint/src/lib.rs b/parachains/runtimes/assets/westmint/src/lib.rs index 1bf530380f5..890cfe040ba 100644 --- a/parachains/runtimes/assets/westmint/src/lib.rs +++ b/parachains/runtimes/assets/westmint/src/lib.rs @@ -832,7 +832,7 @@ impl_runtime_apis! { impl cumulus_pallet_session_benchmarking::Config for Runtime {} use xcm::latest::prelude::*; - use xcm_config::{LocalCheckAccount, MaxAssetsIntoHolding, WestendLocation}; + use xcm_config::{MaxAssetsIntoHolding, WestendLocation}; use pallet_xcm_benchmarks::asset_instance_from; impl pallet_xcm_benchmarks::Config for Runtime { diff --git a/parachains/runtimes/assets/westmint/src/weights/pallet_xcm.rs b/parachains/runtimes/assets/westmint/src/weights/pallet_xcm.rs index 7ccd41d3d20..5d9e6c7a95d 100644 --- a/parachains/runtimes/assets/westmint/src/weights/pallet_xcm.rs +++ b/parachains/runtimes/assets/westmint/src/weights/pallet_xcm.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westmint-dev"), DB CACHE: 1024 @@ -53,37 +53,37 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) fn send() -> Weight { - // Minimum execution time: 26_652 nanoseconds. - Weight::from_ref_time(27_278_000) + // Minimum execution time: 26_837 nanoseconds. + Weight::from_ref_time(28_006_000) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn teleport_assets() -> Weight { - // Minimum execution time: 32_711 nanoseconds. - Weight::from_ref_time(33_447_000) + // Minimum execution time: 31_634 nanoseconds. + Weight::from_ref_time(32_373_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn reserve_transfer_assets() -> Weight { - // Minimum execution time: 24_850 nanoseconds. - Weight::from_ref_time(25_427_000) + // Minimum execution time: 24_484 nanoseconds. + Weight::from_ref_time(24_978_000) .saturating_add(T::DbWeight::get().reads(1)) } fn execute() -> Weight { - // Minimum execution time: 14_909 nanoseconds. - Weight::from_ref_time(15_229_000) + // Minimum execution time: 14_813 nanoseconds. + Weight::from_ref_time(15_181_000) } // Storage: PolkadotXcm SupportedVersion (r:0 w:1) fn force_xcm_version() -> Weight { - // Minimum execution time: 14_930 nanoseconds. - Weight::from_ref_time(15_396_000) + // Minimum execution time: 14_791 nanoseconds. + Weight::from_ref_time(15_228_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) fn force_default_xcm_version() -> Weight { - // Minimum execution time: 4_328 nanoseconds. - Weight::from_ref_time(4_510_000) + // Minimum execution time: 4_323 nanoseconds. + Weight::from_ref_time(4_495_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) @@ -95,8 +95,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_subscribe_version_notify() -> Weight { - // Minimum execution time: 30_943 nanoseconds. - Weight::from_ref_time(31_593_000) + // Minimum execution time: 30_640 nanoseconds. + Weight::from_ref_time(31_310_000) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -108,8 +108,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_unsubscribe_version_notify() -> Weight { - // Minimum execution time: 31_950 nanoseconds. - Weight::from_ref_time(32_364_000) + // Minimum execution time: 31_891 nanoseconds. + Weight::from_ref_time(32_469_000) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } diff --git a/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index c89a2becdb6..bcf175e7c5e 100644 --- a/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -18,25 +18,26 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm4`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westmint-dev"), DB CACHE: 1024 // Executed Command: -// ./artifacts/polkadot-parachain +// /home/benchbot/cargo_target_dir/production/polkadot-parachain // benchmark // pallet -// --template=./templates/xcm-bench-template.hbs -// --chain=westmint-dev +// --steps=50 +// --repeat=20 +// --extrinsic=* // --execution=wasm // --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_xcm_benchmarks::fungible -// --extrinsic=* -// --steps=50 -// --repeat=20 -// --json +// --chain=westmint-dev // --header=./file_header.txt -// --output=./parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +// --template=./templates/xcm-bench-template.hbs +// --output=./parachains/runtimes/assets/westmint/src/weights/xcm/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -50,13 +51,13 @@ pub struct WeightInfo(PhantomData); impl WeightInfo { // Storage: System Account (r:1 w:1) pub(crate) fn withdraw_asset() -> Weight { - Weight::from_ref_time(36_432_000 as u64) + Weight::from_ref_time(30_986_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: System Account (r:2 w:2) pub(crate) fn transfer_asset() -> Weight { - Weight::from_ref_time(41_725_000 as u64) + Weight::from_ref_time(39_149_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } @@ -68,16 +69,16 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn transfer_reserve_asset() -> Weight { - Weight::from_ref_time(60_182_000 as u64) + Weight::from_ref_time(56_235_000 as u64) .saturating_add(T::DbWeight::get().reads(8 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } pub(crate) fn receive_teleported_asset() -> Weight { - Weight::from_ref_time(7_509_000 as u64) + Weight::from_ref_time(7_616_000 as u64) } // Storage: System Account (r:1 w:1) pub(crate) fn deposit_asset() -> Weight { - Weight::from_ref_time(37_620_000 as u64) + Weight::from_ref_time(34_864_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } @@ -89,7 +90,7 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn deposit_reserve_asset() -> Weight { - Weight::from_ref_time(56_103_000 as u64) + Weight::from_ref_time(55_915_000 as u64) .saturating_add(T::DbWeight::get().reads(7 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } @@ -100,7 +101,7 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_teleport() -> Weight { - Weight::from_ref_time(30_157_000 as u64) + Weight::from_ref_time(30_050_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } diff --git a/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 3587e720f51..0e88b1349e2 100644 --- a/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/assets/westmint/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westmint-dev"), DB CACHE: 1024 @@ -56,40 +56,38 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_holding() -> Weight { - Weight::from_ref_time(1_021_862_000 as u64) + Weight::from_ref_time(337_762_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn buy_execution() -> Weight { - Weight::from_ref_time(7_993_000 as u64) + Weight::from_ref_time(6_682_000 as u64) } // Storage: PolkadotXcm Queries (r:1 w:0) pub(crate) fn query_response() -> Weight { - Weight::from_ref_time(18_053_000 as u64) + Weight::from_ref_time(17_801_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) } - // Storage: Session NextKeys (r:1 w:0) pub(crate) fn transact() -> Weight { - Weight::from_ref_time(15_191_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + Weight::from_ref_time(21_889_000 as u64) } pub(crate) fn refund_surplus() -> Weight { - Weight::from_ref_time(8_307_000 as u64) + Weight::from_ref_time(7_081_000 as u64) } pub(crate) fn set_error_handler() -> Weight { - Weight::from_ref_time(5_916_000 as u64) + Weight::from_ref_time(6_050_000 as u64) } pub(crate) fn set_appendix() -> Weight { - Weight::from_ref_time(5_943_000 as u64) + Weight::from_ref_time(5_982_000 as u64) } pub(crate) fn clear_error() -> Weight { - Weight::from_ref_time(6_024_000 as u64) + Weight::from_ref_time(5_948_000 as u64) } pub(crate) fn descend_origin() -> Weight { - Weight::from_ref_time(6_608_000 as u64) + Weight::from_ref_time(7_022_000 as u64) } pub(crate) fn clear_origin() -> Weight { - Weight::from_ref_time(5_885_000 as u64) + Weight::from_ref_time(6_019_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -98,18 +96,18 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_error() -> Weight { - Weight::from_ref_time(21_690_000 as u64) + Weight::from_ref_time(21_836_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: PolkadotXcm AssetTraps (r:1 w:1) pub(crate) fn claim_asset() -> Weight { - Weight::from_ref_time(21_761_000 as u64) + Weight::from_ref_time(21_512_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } pub(crate) fn trap() -> Weight { - Weight::from_ref_time(5_854_000 as u64) + Weight::from_ref_time(5_971_000 as u64) } // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -118,13 +116,13 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn subscribe_version() -> Weight { - Weight::from_ref_time(27_989_000 as u64) + Weight::from_ref_time(27_912_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) pub(crate) fn unsubscribe_version() -> Weight { - Weight::from_ref_time(7_971_000 as u64) + Weight::from_ref_time(7_870_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: ParachainInfo ParachainId (r:1 w:0) @@ -134,21 +132,21 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_reserve_withdraw() -> Weight { - Weight::from_ref_time(1_228_949_000 as u64) + Weight::from_ref_time(390_638_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn burn_asset() -> Weight { - Weight::from_ref_time(416_374_000 as u64) + Weight::from_ref_time(128_254_000 as u64) } pub(crate) fn expect_asset() -> Weight { - Weight::from_ref_time(38_018_000 as u64) + Weight::from_ref_time(15_386_000 as u64) } pub(crate) fn expect_origin() -> Weight { - Weight::from_ref_time(5_996_000 as u64) + Weight::from_ref_time(6_071_000 as u64) } pub(crate) fn expect_error() -> Weight { - Weight::from_ref_time(5_922_000 as u64) + Weight::from_ref_time(6_044_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -157,12 +155,12 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn query_pallet() -> Weight { - Weight::from_ref_time(24_130_000 as u64) + Weight::from_ref_time(24_744_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn expect_pallet() -> Weight { - Weight::from_ref_time(7_238_000 as u64) + Weight::from_ref_time(7_558_000 as u64) } // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -171,23 +169,23 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_transact_status() -> Weight { - Weight::from_ref_time(21_209_000 as u64) + Weight::from_ref_time(22_068_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn clear_transact_status() -> Weight { - Weight::from_ref_time(5_810_000 as u64) + Weight::from_ref_time(5_945_000 as u64) } pub(crate) fn set_topic() -> Weight { - Weight::from_ref_time(5_876_000 as u64) + Weight::from_ref_time(5_863_000 as u64) } pub(crate) fn clear_topic() -> Weight { - Weight::from_ref_time(5_793_000 as u64) + Weight::from_ref_time(5_807_000 as u64) } pub(crate) fn set_fees_mode() -> Weight { - Weight::from_ref_time(5_902_000 as u64) + Weight::from_ref_time(5_825_000 as u64) } pub(crate) fn unpaid_execution() -> Weight { - Weight::from_ref_time(6_092_000 as u64) + Weight::from_ref_time(5_920_000 as u64) } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_xcm.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_xcm.rs index cd24496f035..045afa295bd 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_xcm.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_xcm.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-11, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-kusama-dev"), DB CACHE: 1024 @@ -53,15 +53,15 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) fn send() -> Weight { - // Minimum execution time: 26_345 nanoseconds. - Weight::from_ref_time(26_996_000) + // Minimum execution time: 26_322 nanoseconds. + Weight::from_ref_time(27_595_000) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn teleport_assets() -> Weight { - // Minimum execution time: 32_889 nanoseconds. - Weight::from_ref_time(33_380_000) + // Minimum execution time: 32_648 nanoseconds. + Weight::from_ref_time(33_335_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: Benchmark Override (r:0 w:0) @@ -76,14 +76,14 @@ impl pallet_xcm::WeightInfo for WeightInfo { } // Storage: PolkadotXcm SupportedVersion (r:0 w:1) fn force_xcm_version() -> Weight { - // Minimum execution time: 14_707 nanoseconds. - Weight::from_ref_time(14_851_000) + // Minimum execution time: 15_024 nanoseconds. + Weight::from_ref_time(15_383_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) fn force_default_xcm_version() -> Weight { - // Minimum execution time: 4_465 nanoseconds. - Weight::from_ref_time(4_580_000) + // Minimum execution time: 4_611 nanoseconds. + Weight::from_ref_time(4_751_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) @@ -95,8 +95,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_subscribe_version_notify() -> Weight { - // Minimum execution time: 30_482 nanoseconds. - Weight::from_ref_time(31_091_000) + // Minimum execution time: 30_923 nanoseconds. + Weight::from_ref_time(31_736_000) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -108,8 +108,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_unsubscribe_version_notify() -> Weight { - // Minimum execution time: 31_341 nanoseconds. - Weight::from_ref_time(31_876_000) + // Minimum execution time: 32_167 nanoseconds. + Weight::from_ref_time(32_822_000) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs index de92cd8c026..d3308ed68cb 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-11, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 @@ -53,15 +53,15 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) fn send() -> Weight { - // Minimum execution time: 26_277 nanoseconds. - Weight::from_ref_time(27_202_000) + // Minimum execution time: 26_918 nanoseconds. + Weight::from_ref_time(28_481_000) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn teleport_assets() -> Weight { - // Minimum execution time: 32_941 nanoseconds. - Weight::from_ref_time(33_458_000) + // Minimum execution time: 33_157 nanoseconds. + Weight::from_ref_time(33_584_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: Benchmark Override (r:0 w:0) @@ -76,14 +76,14 @@ impl pallet_xcm::WeightInfo for WeightInfo { } // Storage: PolkadotXcm SupportedVersion (r:0 w:1) fn force_xcm_version() -> Weight { - // Minimum execution time: 14_836 nanoseconds. - Weight::from_ref_time(15_261_000) + // Minimum execution time: 14_206 nanoseconds. + Weight::from_ref_time(14_525_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) fn force_default_xcm_version() -> Weight { - // Minimum execution time: 4_373 nanoseconds. - Weight::from_ref_time(4_537_000) + // Minimum execution time: 4_493 nanoseconds. + Weight::from_ref_time(4_573_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) @@ -95,8 +95,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_subscribe_version_notify() -> Weight { - // Minimum execution time: 30_536 nanoseconds. - Weight::from_ref_time(31_384_000) + // Minimum execution time: 31_132 nanoseconds. + Weight::from_ref_time(31_788_000) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -108,8 +108,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_unsubscribe_version_notify() -> Weight { - // Minimum execution time: 31_667 nanoseconds. - Weight::from_ref_time(32_335_000) + // Minimum execution time: 32_099 nanoseconds. + Weight::from_ref_time(32_731_000) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_xcm.rs b/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_xcm.rs index ea7701e2016..16964cac506 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_xcm.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_xcm.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-11, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("collectives-polkadot-dev"), DB CACHE: 1024 @@ -53,15 +53,15 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) fn send() -> Weight { - // Minimum execution time: 25_986 nanoseconds. - Weight::from_ref_time(26_603_000) + // Minimum execution time: 26_568 nanoseconds. + Weight::from_ref_time(27_534_000) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn teleport_assets() -> Weight { - // Minimum execution time: 32_738 nanoseconds. - Weight::from_ref_time(33_574_000) + // Minimum execution time: 33_083 nanoseconds. + Weight::from_ref_time(34_126_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: Benchmark Override (r:0 w:0) @@ -76,14 +76,14 @@ impl pallet_xcm::WeightInfo for WeightInfo { } // Storage: PolkadotXcm SupportedVersion (r:0 w:1) fn force_xcm_version() -> Weight { - // Minimum execution time: 14_431 nanoseconds. - Weight::from_ref_time(14_923_000) + // Minimum execution time: 14_497 nanoseconds. + Weight::from_ref_time(14_849_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) fn force_default_xcm_version() -> Weight { - // Minimum execution time: 4_413 nanoseconds. - Weight::from_ref_time(4_640_000) + // Minimum execution time: 4_309 nanoseconds. + Weight::from_ref_time(4_647_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) @@ -95,8 +95,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_subscribe_version_notify() -> Weight { - // Minimum execution time: 30_293 nanoseconds. - Weight::from_ref_time(30_769_000) + // Minimum execution time: 30_585 nanoseconds. + Weight::from_ref_time(31_735_000) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -108,8 +108,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_unsubscribe_version_notify() -> Weight { - // Minimum execution time: 31_044 nanoseconds. - Weight::from_ref_time(31_600_000) + // Minimum execution time: 31_687 nanoseconds. + Weight::from_ref_time(32_241_000) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } From 5cf2a723cf04a3a64b949e0f72d30906a3cb6c7b Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Thu, 15 Dec 2022 23:06:54 +0900 Subject: [PATCH 156/263] Change AllowUnpaidExecutionFrom to be explicit --- .../runtimes/assets/statemine/src/xcm_config.rs | 4 ++-- .../runtimes/assets/statemint/src/xcm_config.rs | 4 ++-- parachains/runtimes/assets/westmint/src/xcm_config.rs | 4 ++-- .../bridge-hubs/bridge-hub-kusama/src/xcm_config.rs | 10 +++++----- .../bridge-hubs/bridge-hub-rococo/src/xcm_config.rs | 10 +++++----- .../collectives-polkadot/src/xcm_config.rs | 11 ++++++----- .../contracts/contracts-rococo/src/xcm_config.rs | 6 +++--- parachains/runtimes/starters/shell/src/xcm_config.rs | 4 ++-- .../runtimes/testing/rococo-parachain/src/lib.rs | 10 +++++----- 9 files changed, 32 insertions(+), 31 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index a659ac06ba3..fac1d18a7df 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -34,7 +34,7 @@ use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, + AllowTopLevelPaidExecutionFrom, AllowExplicitUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, @@ -250,7 +250,7 @@ pub type Barrier = DenyThenTry< TakeWeightCredit, AllowTopLevelPaidExecutionFrom, // Parent and its exec plurality get free execution - AllowUnpaidExecutionFrom, + AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, // Subscriptions for version tracking are OK. diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index b6c6b959cb6..a92fa017678 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -34,7 +34,7 @@ use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, + AllowTopLevelPaidExecutionFrom, AllowExplicitUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, @@ -250,7 +250,7 @@ pub type Barrier = DenyThenTry< TakeWeightCredit, AllowTopLevelPaidExecutionFrom, // Parent and its exec plurality get free execution - AllowUnpaidExecutionFrom, + AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, // Subscriptions for version tracking are OK. diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index d7833eb96f9..e65f840925f 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -34,7 +34,7 @@ use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, + AllowTopLevelPaidExecutionFrom, AllowExplicitUnpaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, @@ -245,7 +245,7 @@ pub type Barrier = DenyThenTry< TakeWeightCredit, AllowTopLevelPaidExecutionFrom, // Parent and its plurality get free execution - AllowUnpaidExecutionFrom, + AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, // Subscriptions for version tracking are OK. diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index 3d96de89721..1e175aa6ae3 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -32,10 +32,10 @@ use polkadot_runtime_common::impls::ToAuthor; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FixedWeightBounds, IsConcrete, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + AllowTopLevelPaidExecutionFrom, AllowExplicitUnpaidExecutionFrom, CurrencyAdapter, + EnsureXcmOrigin, FixedWeightBounds, IsConcrete, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -161,7 +161,7 @@ pub type Barrier = DenyThenTry< // Allow local users to buy weight credit. TakeWeightCredit, // Parent and its exec plurality get free execution. - AllowUnpaidExecutionFrom, + AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, // Subscriptions for version tracking are OK. diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index d79564ea2ea..a1be8b50627 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -32,10 +32,10 @@ use polkadot_runtime_common::impls::ToAuthor; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FixedWeightBounds, IsConcrete, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + AllowTopLevelPaidExecutionFrom, AllowExplicitUnpaidExecutionFrom, CurrencyAdapter, + EnsureXcmOrigin, FixedWeightBounds, IsConcrete, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -160,7 +160,7 @@ pub type Barrier = DenyThenTry< // Allow local users to buy weight credit. TakeWeightCredit, // Parent and its exec plurality get free execution. - AllowUnpaidExecutionFrom, + AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, // Subscriptions for version tracking are OK. diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index a06a5524c11..b2f98298d5a 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -31,10 +31,11 @@ use polkadot_parachain::primitives::Sibling; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FixedWeightBounds, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + AllowTopLevelPaidExecutionFrom, AllowExplicitUnpaidExecutionFrom, CurrencyAdapter, + EnsureXcmOrigin, FixedWeightBounds, IsConcrete, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + UsingComponents, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -185,7 +186,7 @@ pub type Barrier = DenyThenTry< // Allow local users to buy weight credit. TakeWeightCredit, // Parent and its exec plurality get free execution. - AllowUnpaidExecutionFrom, + AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, // Subscriptions for version tracking are OK. diff --git a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index 8a49540eff0..0455c1a128f 100644 --- a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -29,8 +29,8 @@ use polkadot_parachain::primitives::Sibling; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, + AllowTopLevelPaidExecutionFrom, AllowExplicitUnpaidExecutionFrom, CurrencyAdapter, + EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, @@ -124,7 +124,7 @@ pub type Barrier = DenyThenTry< TakeWeightCredit, AllowTopLevelPaidExecutionFrom, // Parent and its exec plurality get free execution - AllowUnpaidExecutionFrom, + AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, // Subscriptions for version tracking are OK. diff --git a/parachains/runtimes/starters/shell/src/xcm_config.rs b/parachains/runtimes/starters/shell/src/xcm_config.rs index be83cb9fbfd..ca15fd65e45 100644 --- a/parachains/runtimes/starters/shell/src/xcm_config.rs +++ b/parachains/runtimes/starters/shell/src/xcm_config.rs @@ -24,7 +24,7 @@ use frame_support::{ }; use xcm::latest::prelude::*; use xcm_builder::{ - AllowUnpaidExecutionFrom, FixedWeightBounds, ParentAsSuperuser, ParentIsPreset, + AllowExplicitUnpaidExecutionFrom, FixedWeightBounds, ParentAsSuperuser, ParentIsPreset, SovereignSignedViaLocation, }; @@ -67,7 +67,7 @@ impl xcm_executor::Config for XcmConfig { type IsReserve = (); // balances not supported type IsTeleporter = (); // balances not supported type UniversalLocation = UniversalLocation; - type Barrier = AllowUnpaidExecutionFrom; + type Barrier = AllowExplicitUnpaidExecutionFrom; type Weigher = FixedWeightBounds; // balances not supported type Trader = (); // balances not supported type ResponseHandler = (); // Don't handle responses for now. diff --git a/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/parachains/runtimes/testing/rococo-parachain/src/lib.rs index ffeb7bc1fa5..6acd8fb07f1 100644 --- a/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -79,9 +79,9 @@ use pallet_xcm::{EnsureXcm, IsMajorityOfBody, XcmPassthrough}; use polkadot_parachain::primitives::Sibling; use xcm::latest::prelude::*; use xcm_builder::{ - AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, - EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowExplicitUnpaidExecutionFrom, + CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, + ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; @@ -377,9 +377,9 @@ match_types! { pub type Barrier = ( TakeWeightCredit, AllowTopLevelPaidExecutionFrom, - AllowUnpaidExecutionFrom, + AllowExplicitUnpaidExecutionFrom, // ^^^ Parent & its unit plurality gets free execution - AllowUnpaidExecutionFrom, + AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, // Subscriptions for version tracking are OK. From 5841747ce0d841d26a1a2fa21c6ac61473cf4c01 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 22 Dec 2022 11:19:37 +0100 Subject: [PATCH 157/263] Turn on more xcm logs for parachains --- .../bridge_hub_rococo_local_network.toml | 18 +++++++++++++----- .../bridge_hub_wococo_local_network.toml | 18 +++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml b/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml index 279952130cd..2268abffbe2 100644 --- a/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml +++ b/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml @@ -1,6 +1,6 @@ [relaychain] default_command = "{{POLKADOT_BINARY_PATH}}" -default_args = [ "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace" ] +default_args = [ "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace" ] chain = "rococo-local" [[relaychain.nodes]] @@ -36,8 +36,10 @@ cumulus_based = true command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" rpc_port = 8933 ws_port = 8943 + args = [ + "-lparachain=debug,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace", + ] extra_args = [ - "-lparachain=debug,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", "--force-authoring", "--no-mdns", "--bootnodes {{'bob-collator'|zombie('multiAddress')}}", "-- --port 41333 --rpc-port 48933 --ws-port 48943 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" ] @@ -49,8 +51,10 @@ cumulus_based = true command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" rpc_port = 8934 ws_port = 8944 + args = [ + "-lparachain=trace,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace", + ] extra_args = [ - "-lparachain=trace,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", "--force-authoring", "--no-mdns", "--bootnodes {{'alice-collator'|zombie('multiAddress')}}", "-- --port 41334 --rpc-port 48934 --ws-port 48944 --no-mdns", "--bootnodes {{'bob-validator'|zombie('multiAddress')}}" ] @@ -65,8 +69,10 @@ cumulus_based = true rpc_port = 9911 ws_port = 9910 command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ROCKMINE}}" + args = [ + "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,runtime::bridge-assets-transfer=trace,xcm::should_execute=trace", + ] extra_args = [ - "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,runtime::bridge-assets-transfer=trace", "--no-mdns", "--bootnodes {{'rockmine-collator2'|zombie('multiAddress')}}", "-- --port 51333 --rpc-port 58933 --ws-port 58943 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" ] @@ -74,8 +80,10 @@ cumulus_based = true [[parachains.collators]] name = "rockmine-collator2" command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ROCKMINE}}" + args = [ + "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,runtime::bridge-assets-transfer=trace,xcm::should_execute=trace", + ] extra_args = [ - "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,runtime::bridge-assets-transfer=trace", "--no-mdns", "--bootnodes {{'rockmine-collator1'|zombie('multiAddress')}}", "-- --port 51433 --rpc-port 58833 --ws-port 58843 --no-mdns", "--bootnodes {{'alice-validator'|zombie('multiAddress')}}" ] diff --git a/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml b/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml index 8c86fca58e5..c68e64a767e 100644 --- a/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml +++ b/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml @@ -1,6 +1,6 @@ [relaychain] default_command = "{{POLKADOT_BINARY_PATH}}" -default_args = [ "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace" ] +default_args = [ "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace" ] chain = "wococo-local" [[relaychain.nodes]] @@ -36,8 +36,10 @@ cumulus_based = true command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" rpc_port = 8935 ws_port = 8945 + args = [ + "-lparachain=debug,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace", + ] extra_args = [ - "-lparachain=debug,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", "--force-authoring", "--no-mdns", "--bootnodes {{'bob-collator-wo'|zombie('multiAddress')}}", "-- --port 41335 --rpc-port 48935 --ws-port 48945 --no-mdns", "--bootnodes {{'alice-validator-wo'|zombie('multiAddress')}}" ] @@ -49,8 +51,10 @@ cumulus_based = true command = "{{POLKADOT_PARACHAIN_BINARY_PATH}}" rpc_port = 8936 ws_port = 8946 + args = [ + "-lparachain=trace,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace", + ] extra_args = [ - "-lparachain=trace,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", "--force-authoring", "--no-mdns", "--bootnodes {{'alice-collator-wo'|zombie('multiAddress')}}", "-- --port 41336 --rpc-port 48936 --ws-port 48946 --no-mdns", "--bootnodes {{'bob-validator-wo'|zombie('multiAddress')}}" ] @@ -65,8 +69,10 @@ cumulus_based = true rpc_port = 9011 ws_port = 9010 command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_WOCKMINT}}" + args = [ + "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace", + ] extra_args = [ - "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", "--no-mdns", "--bootnodes {{'wockmint-collator2'|zombie('multiAddress')}}", "-- --port 31333 --rpc-port 38933 --ws-port 38943 --no-mdns", "--bootnodes {{'alice-validator-wo'|zombie('multiAddress')}}" ] @@ -74,8 +80,10 @@ cumulus_based = true [[parachains.collators]] name = "wockmint-collator2" command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_WOCKMINT}}" + args = [ + "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace", + ] extra_args = [ - "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace", "--no-mdns", "--bootnodes {{'wockmint-collator1'|zombie('multiAddress')}}", "-- --port 31433 --rpc-port 38833 --ws-port 38843 --no-mdns", "--bootnodes {{'alice-validator-wo'|zombie('multiAddress')}}" ] From 14e3f8610cb7ac566bdeb95266ce44241f2b197d Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 22 Dec 2022 13:20:39 +0100 Subject: [PATCH 158/263] Added polkadot-parachain-mint binary instructions --- parachains/runtimes/bridge-hubs/README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 31b9f60828a..eeef2addf5a 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -34,14 +34,22 @@ cp target/release/polkadot ~/local_bridge_testing/bin/polkadot # 3. Build cumulus polkadot-parachain binary cd +git checkout -b bridge-hub-rococo-wococo --track origin/bridge-hub-rococo-wococo cargo build --release --locked -p polkadot-parachain@0.9.300 cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain +cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain-mint # 4. Build substrate-relay binary git clone https://github.com/paritytech/parity-bridges-common.git cd parity-bridges-common cargo build --release -p substrate-relay cp target/release/substrate-relay ~/local_bridge_testing/bin/substrate-relay + +# (Optional) 5. Build polkadot-parachain-mint binary with statemine/westmint for moving assets +cd +git checkout -b bko-transfer-asset-via-bridge --track origin/bko-transfer-asset-via-bridge +cargo build --release --locked -p polkadot-parachain@0.9.300 +cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain-mint ``` ### Run chains (Rococo + BridgeHub, Wococo + BridgeHub) with zombienet @@ -50,7 +58,7 @@ cp target/release/substrate-relay ~/local_bridge_testing/bin/substrate-relay # Rococo + BridgeHubRococo + Rockmine (mirroring Kusama) POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ -POLKADOT_PARACHAIN_BINARY_PATH_FOR_ROCKMINE=~/local_bridge_testing/bin/polkadot-parachain \ +POLKADOT_PARACHAIN_BINARY_PATH_FOR_ROCKMINE=~/local_bridge_testing/bin/polkadot-parachain-mint \ ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml ``` @@ -58,7 +66,7 @@ POLKADOT_PARACHAIN_BINARY_PATH_FOR_ROCKMINE=~/local_bridge_testing/bin/polkadot- # Wococo + BridgeHubWococo + Wockmint (mirroring Polkadot) POLKADOT_BINARY_PATH=~/local_bridge_testing/bin/polkadot \ POLKADOT_PARACHAIN_BINARY_PATH=~/local_bridge_testing/bin/polkadot-parachain \ -POLKADOT_PARACHAIN_BINARY_PATH_FOR_WOCKMINT=~/local_bridge_testing/bin/polkadot-parachain \ +POLKADOT_PARACHAIN_BINARY_PATH_FOR_WOCKMINT=~/local_bridge_testing/bin/polkadot-parachain-mint \ ~/local_bridge_testing/bin/zombienet-linux --provider native spawn ./zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml ``` From 5cdc48a80e334fba69b6f5924025e46f3e08fccf Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 23 Dec 2022 21:00:45 +0100 Subject: [PATCH 159/263] xcm-v3 benchmarks, weights, fixes for bridge-hubs (#2035) * Dumy weights to get compile * Change UniversalLocation according to https://github.com/paritytech/polkadot/pull/4097 (Location Inversion Removed) * Fix bridge-hubs weights * ".git/.scripts/bench-bot.sh" pallet statemine assets pallet_xcm * ".git/.scripts/bench-bot.sh" pallet statemint assets pallet_xcm * ".git/.scripts/bench-bot.sh" pallet collectives-polkadot collectives pallet_xcm * ".git/.scripts/bench-bot.sh" pallet westmint assets pallet_xcm * ".git/.scripts/bench-bot.sh" xcm bridge-hub-kusama bridge-hubs pallet_xcm_benchmarks::generic * ".git/.scripts/bench-bot.sh" xcm bridge-hub-kusama bridge-hubs pallet_xcm_benchmarks::fungible * ".git/.scripts/bench-bot.sh" pallet bridge-hub-kusama bridge-hubs pallet_xcm * ".git/.scripts/bench-bot.sh" pallet bridge-hub-rococo bridge-hubs pallet_xcm * ".git/.scripts/bench-bot.sh" xcm bridge-hub-rococo bridge-hubs pallet_xcm_benchmarks::fungible * ".git/.scripts/bench-bot.sh" xcm bridge-hub-rococo bridge-hubs pallet_xcm_benchmarks::generic * Change NetworkId to Option Co-authored-by: command-bot <> Co-authored-by: Keith Yeung --- .../statemine/src/weights/pallet_xcm.rs | 87 ++++++-- .../assets/statemine/src/xcm_config.rs | 5 +- .../statemint/src/weights/pallet_xcm.rs | 87 ++++++-- .../assets/statemint/src/xcm_config.rs | 5 +- .../assets/westmint/src/weights/pallet_xcm.rs | 91 ++++++-- .../assets/westmint/src/xcm_config.rs | 5 +- .../bridge-hubs/bridge-hub-kusama/src/lib.rs | 22 +- .../src/weights/pallet_xcm.rs | 83 +++++-- .../bridge-hub-kusama/src/weights/xcm/mod.rs | 210 +++++++++++------- .../xcm/pallet_xcm_benchmarks_fungible.rs | 16 +- .../xcm/pallet_xcm_benchmarks_generic.rs | 91 ++++++-- .../bridge-hub-kusama/src/xcm_config.rs | 3 +- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 22 +- .../src/weights/pallet_xcm.rs | 83 +++++-- .../bridge-hub-rococo/src/weights/xcm/mod.rs | 210 +++++++++++------- .../xcm/pallet_xcm_benchmarks_fungible.rs | 16 +- .../xcm/pallet_xcm_benchmarks_generic.rs | 91 ++++++-- .../bridge-hub-rococo/src/xcm_config.rs | 5 +- .../src/weights/pallet_xcm.rs | 83 +++++-- .../collectives-polkadot/src/xcm_config.rs | 5 +- .../runtimes/starters/shell/src/xcm_config.rs | 2 +- .../testing/rococo-parachain/src/lib.rs | 2 +- 22 files changed, 899 insertions(+), 325 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/weights/pallet_xcm.rs b/parachains/runtimes/assets/statemine/src/weights/pallet_xcm.rs index 84b38c418b1..1b862af5342 100644 --- a/parachains/runtimes/assets/statemine/src/weights/pallet_xcm.rs +++ b/parachains/runtimes/assets/statemine/src/weights/pallet_xcm.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemine-dev"), DB CACHE: 1024 @@ -53,21 +53,21 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) fn send() -> Weight { - // Minimum execution time: 27_480 nanoseconds. - Weight::from_ref_time(27_952_000) + // Minimum execution time: 26_577 nanoseconds. + Weight::from_ref_time(27_239_000) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn teleport_assets() -> Weight { - // Minimum execution time: 32_034 nanoseconds. - Weight::from_ref_time(32_516_000) + // Minimum execution time: 32_703 nanoseconds. + Weight::from_ref_time(33_200_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn reserve_transfer_assets() -> Weight { - // Minimum execution time: 25_008 nanoseconds. - Weight::from_ref_time(25_304_000) + // Minimum execution time: 25_142 nanoseconds. + Weight::from_ref_time(25_808_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: Benchmark Override (r:0 w:0) @@ -77,14 +77,14 @@ impl pallet_xcm::WeightInfo for WeightInfo { } // Storage: PolkadotXcm SupportedVersion (r:0 w:1) fn force_xcm_version() -> Weight { - // Minimum execution time: 14_853 nanoseconds. - Weight::from_ref_time(15_204_000) + // Minimum execution time: 15_036 nanoseconds. + Weight::from_ref_time(15_456_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) fn force_default_xcm_version() -> Weight { - // Minimum execution time: 4_423 nanoseconds. - Weight::from_ref_time(4_533_000) + // Minimum execution time: 4_404 nanoseconds. + Weight::from_ref_time(4_598_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) @@ -96,8 +96,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_subscribe_version_notify() -> Weight { - // Minimum execution time: 30_668 nanoseconds. - Weight::from_ref_time(31_262_000) + // Minimum execution time: 30_942 nanoseconds. + Weight::from_ref_time(31_608_000) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -109,9 +109,66 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_unsubscribe_version_notify() -> Weight { - // Minimum execution time: 31_955 nanoseconds. - Weight::from_ref_time(32_364_000) + // Minimum execution time: 32_327 nanoseconds. + Weight::from_ref_time(32_698_000) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } + // Storage: PolkadotXcm SupportedVersion (r:4 w:2) + fn migrate_supported_version() -> Weight { + // Minimum execution time: 13_522 nanoseconds. + Weight::from_ref_time(13_862_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifiers (r:4 w:2) + fn migrate_version_notifiers() -> Weight { + // Minimum execution time: 13_621 nanoseconds. + Weight::from_ref_time(14_028_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:5 w:0) + fn already_notified_target() -> Weight { + // Minimum execution time: 15_511 nanoseconds. + Weight::from_ref_time(15_825_000) + .saturating_add(T::DbWeight::get().reads(5)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:2 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn notify_current_targets() -> Weight { + // Minimum execution time: 27_062 nanoseconds. + Weight::from_ref_time(27_514_000) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:3 w:0) + fn notify_target_migration_fail() -> Weight { + // Minimum execution time: 7_062 nanoseconds. + Weight::from_ref_time(7_325_000) + .saturating_add(T::DbWeight::get().reads(3)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) + fn migrate_version_notify_targets() -> Weight { + // Minimum execution time: 14_362 nanoseconds. + Weight::from_ref_time(14_724_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn migrate_and_notify_old_targets() -> Weight { + // Minimum execution time: 32_435 nanoseconds. + Weight::from_ref_time(33_042_000) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } } diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index aad9d19333c..37317f9d1e0 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -47,9 +47,10 @@ use xcm_executor::{ parameter_types! { pub const KsmLocation: MultiLocation = MultiLocation::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Kusama; + pub const RelayNetwork: Option = Some(NetworkId::Kusama); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); + pub UniversalLocation: InteriorMultiLocation = + X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); pub const Local: MultiLocation = Here.into_location(); pub AssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); diff --git a/parachains/runtimes/assets/statemint/src/weights/pallet_xcm.rs b/parachains/runtimes/assets/statemint/src/weights/pallet_xcm.rs index cb9e07533a6..69729e7f385 100644 --- a/parachains/runtimes/assets/statemint/src/weights/pallet_xcm.rs +++ b/parachains/runtimes/assets/statemint/src/weights/pallet_xcm.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("statemint-dev"), DB CACHE: 1024 @@ -53,21 +53,21 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) fn send() -> Weight { - // Minimum execution time: 27_134 nanoseconds. - Weight::from_ref_time(27_929_000) + // Minimum execution time: 27_694 nanoseconds. + Weight::from_ref_time(28_545_000) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn teleport_assets() -> Weight { - // Minimum execution time: 32_099 nanoseconds. - Weight::from_ref_time(32_640_000) + // Minimum execution time: 33_053 nanoseconds. + Weight::from_ref_time(33_789_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn reserve_transfer_assets() -> Weight { - // Minimum execution time: 24_795 nanoseconds. - Weight::from_ref_time(25_143_000) + // Minimum execution time: 24_781 nanoseconds. + Weight::from_ref_time(25_259_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: Benchmark Override (r:0 w:0) @@ -77,14 +77,14 @@ impl pallet_xcm::WeightInfo for WeightInfo { } // Storage: PolkadotXcm SupportedVersion (r:0 w:1) fn force_xcm_version() -> Weight { - // Minimum execution time: 15_099 nanoseconds. - Weight::from_ref_time(15_360_000) + // Minimum execution time: 15_439 nanoseconds. + Weight::from_ref_time(15_750_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) fn force_default_xcm_version() -> Weight { - // Minimum execution time: 4_460 nanoseconds. - Weight::from_ref_time(4_646_000) + // Minimum execution time: 4_576 nanoseconds. + Weight::from_ref_time(4_758_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) @@ -96,8 +96,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_subscribe_version_notify() -> Weight { - // Minimum execution time: 31_019 nanoseconds. - Weight::from_ref_time(31_816_000) + // Minimum execution time: 31_540 nanoseconds. + Weight::from_ref_time(32_380_000) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -109,9 +109,66 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_unsubscribe_version_notify() -> Weight { - // Minimum execution time: 32_139 nanoseconds. - Weight::from_ref_time(32_607_000) + // Minimum execution time: 32_627 nanoseconds. + Weight::from_ref_time(32_934_000) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } + // Storage: PolkadotXcm SupportedVersion (r:4 w:2) + fn migrate_supported_version() -> Weight { + // Minimum execution time: 13_968 nanoseconds. + Weight::from_ref_time(14_356_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifiers (r:4 w:2) + fn migrate_version_notifiers() -> Weight { + // Minimum execution time: 14_263 nanoseconds. + Weight::from_ref_time(14_540_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:5 w:0) + fn already_notified_target() -> Weight { + // Minimum execution time: 15_825 nanoseconds. + Weight::from_ref_time(16_101_000) + .saturating_add(T::DbWeight::get().reads(5)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:2 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn notify_current_targets() -> Weight { + // Minimum execution time: 27_436 nanoseconds. + Weight::from_ref_time(28_083_000) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:3 w:0) + fn notify_target_migration_fail() -> Weight { + // Minimum execution time: 7_187 nanoseconds. + Weight::from_ref_time(7_399_000) + .saturating_add(T::DbWeight::get().reads(3)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) + fn migrate_version_notify_targets() -> Weight { + // Minimum execution time: 14_904 nanoseconds. + Weight::from_ref_time(15_191_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn migrate_and_notify_old_targets() -> Weight { + // Minimum execution time: 33_269 nanoseconds. + Weight::from_ref_time(34_064_000) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } } diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index f5a143d777c..78919d0ae17 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -47,9 +47,10 @@ use xcm_executor::{ parameter_types! { pub const DotLocation: MultiLocation = MultiLocation::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Polkadot; + pub const RelayNetwork: Option = Some(NetworkId::Polkadot); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); + pub UniversalLocation: InteriorMultiLocation = + X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); pub const Local: MultiLocation = MultiLocation::here(); pub AssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); diff --git a/parachains/runtimes/assets/westmint/src/weights/pallet_xcm.rs b/parachains/runtimes/assets/westmint/src/weights/pallet_xcm.rs index 5d9e6c7a95d..457e551e52d 100644 --- a/parachains/runtimes/assets/westmint/src/weights/pallet_xcm.rs +++ b/parachains/runtimes/assets/westmint/src/weights/pallet_xcm.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westmint-dev"), DB CACHE: 1024 @@ -53,37 +53,37 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) fn send() -> Weight { - // Minimum execution time: 26_837 nanoseconds. - Weight::from_ref_time(28_006_000) + // Minimum execution time: 27_623 nanoseconds. + Weight::from_ref_time(28_375_000) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn teleport_assets() -> Weight { - // Minimum execution time: 31_634 nanoseconds. - Weight::from_ref_time(32_373_000) + // Minimum execution time: 33_363 nanoseconds. + Weight::from_ref_time(33_942_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn reserve_transfer_assets() -> Weight { - // Minimum execution time: 24_484 nanoseconds. - Weight::from_ref_time(24_978_000) + // Minimum execution time: 25_113 nanoseconds. + Weight::from_ref_time(25_549_000) .saturating_add(T::DbWeight::get().reads(1)) } fn execute() -> Weight { - // Minimum execution time: 14_813 nanoseconds. - Weight::from_ref_time(15_181_000) + // Minimum execution time: 15_464 nanoseconds. + Weight::from_ref_time(15_772_000) } // Storage: PolkadotXcm SupportedVersion (r:0 w:1) fn force_xcm_version() -> Weight { - // Minimum execution time: 14_791 nanoseconds. - Weight::from_ref_time(15_228_000) + // Minimum execution time: 15_375 nanoseconds. + Weight::from_ref_time(15_631_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) fn force_default_xcm_version() -> Weight { - // Minimum execution time: 4_323 nanoseconds. - Weight::from_ref_time(4_495_000) + // Minimum execution time: 4_575 nanoseconds. + Weight::from_ref_time(4_708_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) @@ -95,8 +95,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_subscribe_version_notify() -> Weight { - // Minimum execution time: 30_640 nanoseconds. - Weight::from_ref_time(31_310_000) + // Minimum execution time: 31_521 nanoseconds. + Weight::from_ref_time(32_350_000) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -108,9 +108,66 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_unsubscribe_version_notify() -> Weight { - // Minimum execution time: 31_891 nanoseconds. - Weight::from_ref_time(32_469_000) + // Minimum execution time: 32_589 nanoseconds. + Weight::from_ref_time(33_282_000) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } + // Storage: PolkadotXcm SupportedVersion (r:4 w:2) + fn migrate_supported_version() -> Weight { + // Minimum execution time: 13_642 nanoseconds. + Weight::from_ref_time(13_969_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifiers (r:4 w:2) + fn migrate_version_notifiers() -> Weight { + // Minimum execution time: 13_569 nanoseconds. + Weight::from_ref_time(14_168_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:5 w:0) + fn already_notified_target() -> Weight { + // Minimum execution time: 15_406 nanoseconds. + Weight::from_ref_time(15_731_000) + .saturating_add(T::DbWeight::get().reads(5)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:2 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn notify_current_targets() -> Weight { + // Minimum execution time: 27_357 nanoseconds. + Weight::from_ref_time(27_804_000) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:3 w:0) + fn notify_target_migration_fail() -> Weight { + // Minimum execution time: 6_940 nanoseconds. + Weight::from_ref_time(7_147_000) + .saturating_add(T::DbWeight::get().reads(3)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) + fn migrate_version_notify_targets() -> Weight { + // Minimum execution time: 14_204 nanoseconds. + Weight::from_ref_time(14_529_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn migrate_and_notify_old_targets() -> Weight { + // Minimum execution time: 32_809 nanoseconds. + Weight::from_ref_time(33_346_000) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } } diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index 7c8b3a15fe9..6e839044d0a 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -47,9 +47,10 @@ use xcm_executor::{ parameter_types! { pub const WestendLocation: MultiLocation = MultiLocation::parent(); - pub RelayNetwork: NetworkId = NetworkId::Westend; + pub RelayNetwork: Option = Some(NetworkId::Westend); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); + pub UniversalLocation: InteriorMultiLocation = + X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); pub const Local: MultiLocation = Here.into_location(); pub AssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs index c0f063cec8e..e0946f99a0c 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs @@ -618,7 +618,7 @@ impl_runtime_apis! { fn valid_destination() -> Result { Ok(KsmRelayLocation::get()) } - fn worst_case_holding() -> MultiAssets { + fn worst_case_holding(_depositable_count: u32) -> MultiAssets { // just concrete assets according to relay chain. let assets: Vec = vec![ MultiAsset { @@ -635,8 +635,7 @@ impl_runtime_apis! { KsmRelayLocation::get(), MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(KsmRelayLocation::get()) }, )); - pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = None; - pub const CheckedAccount: Option = None; + pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None; } impl pallet_xcm_benchmarks::fungible::Config for Runtime { @@ -644,7 +643,6 @@ impl_runtime_apis! { type CheckedAccount = CheckedAccount; type TrustedTeleporter = TrustedTeleporter; - type TrustedReserve = TrustedReserve; fn get_multi_asset() -> MultiAsset { MultiAsset { @@ -661,8 +659,16 @@ impl_runtime_apis! { (0u64, Response::Version(Default::default())) } - fn transact_origin() -> Result { - Ok(KsmRelayLocation::get()) + fn worst_case_asset_exchange() -> Result<(MultiAssets, MultiAssets), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn universal_alias() -> Result { + Err(BenchmarkError::Skip) + } + + fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> { + Ok((KsmRelayLocation::get(), frame_system::Call::remark_with_event { remark: vec![] }.into())) } fn subscribe_origin() -> Result { @@ -675,6 +681,10 @@ impl_runtime_apis! { let ticket = MultiLocation { parents: 0, interior: Here }; Ok((origin, ticket, assets)) } + + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { + Err(BenchmarkError::Skip) + } } type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_xcm.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_xcm.rs index 045afa295bd..672b3f75421 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_xcm.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/pallet_xcm.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-kusama-dev"), DB CACHE: 1024 @@ -53,15 +53,15 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) fn send() -> Weight { - // Minimum execution time: 26_322 nanoseconds. - Weight::from_ref_time(27_595_000) + // Minimum execution time: 26_824 nanoseconds. + Weight::from_ref_time(27_272_000) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn teleport_assets() -> Weight { - // Minimum execution time: 32_648 nanoseconds. - Weight::from_ref_time(33_335_000) + // Minimum execution time: 32_322 nanoseconds. + Weight::from_ref_time(32_760_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: Benchmark Override (r:0 w:0) @@ -76,14 +76,14 @@ impl pallet_xcm::WeightInfo for WeightInfo { } // Storage: PolkadotXcm SupportedVersion (r:0 w:1) fn force_xcm_version() -> Weight { - // Minimum execution time: 15_024 nanoseconds. - Weight::from_ref_time(15_383_000) + // Minimum execution time: 14_919 nanoseconds. + Weight::from_ref_time(15_388_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) fn force_default_xcm_version() -> Weight { - // Minimum execution time: 4_611 nanoseconds. - Weight::from_ref_time(4_751_000) + // Minimum execution time: 4_754 nanoseconds. + Weight::from_ref_time(4_857_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) @@ -95,8 +95,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_subscribe_version_notify() -> Weight { - // Minimum execution time: 30_923 nanoseconds. - Weight::from_ref_time(31_736_000) + // Minimum execution time: 31_107 nanoseconds. + Weight::from_ref_time(31_928_000) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -108,9 +108,66 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_unsubscribe_version_notify() -> Weight { - // Minimum execution time: 32_167 nanoseconds. - Weight::from_ref_time(32_822_000) + // Minimum execution time: 31_745 nanoseconds. + Weight::from_ref_time(32_243_000) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } + // Storage: PolkadotXcm SupportedVersion (r:4 w:2) + fn migrate_supported_version() -> Weight { + // Minimum execution time: 13_855 nanoseconds. + Weight::from_ref_time(14_060_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifiers (r:4 w:2) + fn migrate_version_notifiers() -> Weight { + // Minimum execution time: 13_693 nanoseconds. + Weight::from_ref_time(13_984_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:5 w:0) + fn already_notified_target() -> Weight { + // Minimum execution time: 15_148 nanoseconds. + Weight::from_ref_time(15_399_000) + .saturating_add(T::DbWeight::get().reads(5)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:2 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn notify_current_targets() -> Weight { + // Minimum execution time: 26_582 nanoseconds. + Weight::from_ref_time(27_284_000) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:3 w:0) + fn notify_target_migration_fail() -> Weight { + // Minimum execution time: 6_961 nanoseconds. + Weight::from_ref_time(7_139_000) + .saturating_add(T::DbWeight::get().reads(3)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) + fn migrate_version_notify_targets() -> Weight { + // Minimum execution time: 14_094 nanoseconds. + Weight::from_ref_time(14_474_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn migrate_and_notify_old_targets() -> Weight { + // Minimum execution time: 31_931 nanoseconds. + Weight::from_ref_time(32_628_000) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/xcm/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/xcm/mod.rs index 5051255d827..82b29187960 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/xcm/mod.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/xcm/mod.rs @@ -21,168 +21,220 @@ use crate::Runtime; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; -use sp_std::{cmp, prelude::*}; -use xcm::{ - latest::{prelude::*, Weight as XCMWeight}, - DoubleEncoded, -}; +use sp_std::prelude::*; +use xcm::{latest::prelude::*, DoubleEncoded}; trait WeighMultiAssets { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight; + fn weigh_multi_assets(&self, weight: Weight) -> Weight; } const MAX_ASSETS: u32 = 100; impl WeighMultiAssets for MultiAssetFilter { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight { - let weight = match self { + fn weigh_multi_assets(&self, weight: Weight) -> Weight { + match self { Self::Definite(assets) => weight.saturating_mul(assets.inner().into_iter().count() as u64), Self::Wild(_) => weight.saturating_mul(MAX_ASSETS as u64), - }; - weight.ref_time() + } } } impl WeighMultiAssets for MultiAssets { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight { - weight.saturating_mul(self.inner().into_iter().count() as u64).ref_time() + fn weigh_multi_assets(&self, weight: Weight) -> Weight { + weight.saturating_mul(self.inner().into_iter().count() as u64) } } pub struct BridgeHubKusamaXcmWeight(core::marker::PhantomData); impl XcmWeightInfo for BridgeHubKusamaXcmWeight { - fn withdraw_asset(assets: &MultiAssets) -> XCMWeight { + fn withdraw_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::withdraw_asset()) } // Currently there is no trusted reserve - fn reserve_asset_deposited(_assets: &MultiAssets) -> XCMWeight { - u64::MAX + fn reserve_asset_deposited(_assets: &MultiAssets) -> Weight { + // TODO: hardcoded - fix https://github.com/paritytech/cumulus/issues/1974 + Weight::from_ref_time(1_000_000_000 as u64) } - fn receive_teleported_asset(assets: &MultiAssets) -> XCMWeight { + fn receive_teleported_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::receive_teleported_asset()) } - fn query_response(_query_id: &u64, _response: &Response, _max_weight: &u64) -> XCMWeight { - XcmGeneric::::query_response().ref_time() + fn query_response( + _query_id: &u64, + _response: &Response, + _max_weight: &Weight, + _querier: &Option, + ) -> Weight { + XcmGeneric::::query_response() } - fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> XCMWeight { + fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::transfer_asset()) } fn transfer_reserve_asset( assets: &MultiAssets, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::transfer_reserve_asset()) } fn transact( _origin_type: &OriginKind, - _require_weight_at_most: &u64, + _require_weight_at_most: &Weight, _call: &DoubleEncoded, - ) -> XCMWeight { - XcmGeneric::::transact().ref_time() + ) -> Weight { + XcmGeneric::::transact() } fn hrmp_new_channel_open_request( _sender: &u32, _max_message_size: &u32, _max_capacity: &u32, - ) -> XCMWeight { + ) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn hrmp_channel_accepted(_recipient: &u32) -> XCMWeight { + fn hrmp_channel_accepted(_recipient: &u32) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> XCMWeight { + fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn clear_origin() -> XCMWeight { - XcmGeneric::::clear_origin().ref_time() + fn clear_origin() -> Weight { + XcmGeneric::::clear_origin() } - fn descend_origin(_who: &InteriorMultiLocation) -> XCMWeight { - XcmGeneric::::descend_origin().ref_time() + fn descend_origin(_who: &InteriorMultiLocation) -> Weight { + XcmGeneric::::descend_origin() } - fn report_error( - _query_id: &QueryId, - _dest: &MultiLocation, - _max_response_weight: &u64, - ) -> XCMWeight { - XcmGeneric::::report_error().ref_time() + fn report_error(_query_response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::report_error() } - fn deposit_asset( - assets: &MultiAssetFilter, - _max_assets: &u32, - _dest: &MultiLocation, - ) -> XCMWeight { + fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> Weight { // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_ref_time(1_000_000_000 as u64).ref_time(); + let hardcoded_weight = Weight::from_ref_time(1_000_000_000 as u64); let weight = assets.weigh_multi_assets(XcmFungibleWeight::::deposit_asset()); - cmp::min(hardcoded_weight, weight) + hardcoded_weight.min(weight) } fn deposit_reserve_asset( assets: &MultiAssetFilter, - _max_assets: &u32, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::deposit_reserve_asset()) } - fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets) -> XCMWeight { - Weight::MAX.ref_time() + fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets, _maximal: &bool) -> Weight { + Weight::MAX } fn initiate_reserve_withdraw( assets: &MultiAssetFilter, _reserve: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmGeneric::::initiate_reserve_withdraw()) } fn initiate_teleport( assets: &MultiAssetFilter, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_ref_time(200_000_000 as u64).ref_time(); + let hardcoded_weight = Weight::from_ref_time(200_000_000 as u64); let weight = assets.weigh_multi_assets(XcmFungibleWeight::::initiate_teleport()); - cmp::min(hardcoded_weight, weight) + hardcoded_weight.min(weight) } - fn query_holding( - _query_id: &u64, - _dest: &MultiLocation, - _assets: &MultiAssetFilter, - _max_response_weight: &u64, - ) -> XCMWeight { - XcmGeneric::::query_holding().ref_time() + fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> Weight { + XcmGeneric::::report_holding() + } + fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> Weight { + XcmGeneric::::buy_execution() + } + fn refund_surplus() -> Weight { + XcmGeneric::::refund_surplus() + } + fn set_error_handler(_xcm: &Xcm) -> Weight { + XcmGeneric::::set_error_handler() + } + fn set_appendix(_xcm: &Xcm) -> Weight { + XcmGeneric::::set_appendix() + } + fn clear_error() -> Weight { + XcmGeneric::::clear_error() + } + fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> Weight { + XcmGeneric::::claim_asset() + } + fn trap(_code: &u64) -> Weight { + XcmGeneric::::trap() + } + fn subscribe_version(_query_id: &QueryId, _max_response_weight: &Weight) -> Weight { + XcmGeneric::::subscribe_version() + } + fn unsubscribe_version() -> Weight { + XcmGeneric::::unsubscribe_version() + } + fn burn_asset(assets: &MultiAssets) -> Weight { + assets.weigh_multi_assets(XcmGeneric::::burn_asset()) + } + fn expect_asset(assets: &MultiAssets) -> Weight { + assets.weigh_multi_assets(XcmGeneric::::expect_asset()) + } + fn expect_origin(_origin: &Option) -> Weight { + XcmGeneric::::expect_origin() + } + fn expect_error(_error: &Option<(u32, XcmError)>) -> Weight { + XcmGeneric::::expect_error() + } + fn query_pallet(_module_name: &Vec, _response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::query_pallet() + } + fn expect_pallet( + _index: &u32, + _name: &Vec, + _module_name: &Vec, + _crate_major: &u32, + _min_crate_minor: &u32, + ) -> Weight { + XcmGeneric::::expect_pallet() + } + fn report_transact_status(_response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::report_transact_status() + } + fn clear_transact_status() -> Weight { + XcmGeneric::::clear_transact_status() + } + fn universal_origin(_: &Junction) -> Weight { + Weight::MAX + } + fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { + Weight::MAX } - fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> XCMWeight { - XcmGeneric::::buy_execution().ref_time() + fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn refund_surplus() -> XCMWeight { - XcmGeneric::::refund_surplus().ref_time() + fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn set_error_handler(_xcm: &Xcm) -> XCMWeight { - XcmGeneric::::set_error_handler().ref_time() + fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn set_appendix(_xcm: &Xcm) -> XCMWeight { - XcmGeneric::::set_appendix().ref_time() + fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn clear_error() -> XCMWeight { - XcmGeneric::::clear_error().ref_time() + fn set_fees_mode(_: &bool) -> Weight { + XcmGeneric::::set_fees_mode() } - fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> XCMWeight { - XcmGeneric::::claim_asset().ref_time() + fn set_topic(_topic: &[u8; 32]) -> Weight { + XcmGeneric::::set_topic() } - fn trap(_code: &u64) -> XCMWeight { - XcmGeneric::::trap().ref_time() + fn clear_topic() -> Weight { + XcmGeneric::::clear_topic() } - fn subscribe_version(_query_id: &QueryId, _max_response_weight: &u64) -> XCMWeight { - XcmGeneric::::subscribe_version().ref_time() + fn alias_origin(_: &MultiLocation) -> Weight { + // XCM Executor does not currently support alias origin operations + Weight::MAX } - fn unsubscribe_version() -> XCMWeight { - XcmGeneric::::unsubscribe_version().ref_time() + fn unpaid_execution(_: &WeightLimit, _: &Option) -> Weight { + XcmGeneric::::unpaid_execution() } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index c3e83e116cf..c5cf471c74d 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-15, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-kusama-dev"), DB CACHE: 1024 @@ -51,13 +51,13 @@ pub struct WeightInfo(PhantomData); impl WeightInfo { // Storage: System Account (r:1 w:1) pub(crate) fn withdraw_asset() -> Weight { - Weight::from_ref_time(25_434_000 as u64) + Weight::from_ref_time(27_070_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: System Account (r:2 w:2) pub(crate) fn transfer_asset() -> Weight { - Weight::from_ref_time(36_180_000 as u64) + Weight::from_ref_time(38_614_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } @@ -69,16 +69,16 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn transfer_reserve_asset() -> Weight { - Weight::from_ref_time(83_476_000 as u64) + Weight::from_ref_time(55_875_000 as u64) .saturating_add(T::DbWeight::get().reads(8 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } pub(crate) fn receive_teleported_asset() -> Weight { - Weight::from_ref_time(6_009_000 as u64) + Weight::from_ref_time(7_121_000 as u64) } // Storage: System Account (r:1 w:1) pub(crate) fn deposit_asset() -> Weight { - Weight::from_ref_time(27_729_000 as u64) + Weight::from_ref_time(30_450_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } @@ -90,7 +90,7 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn deposit_reserve_asset() -> Weight { - Weight::from_ref_time(44_605_000 as u64) + Weight::from_ref_time(49_122_000 as u64) .saturating_add(T::DbWeight::get().reads(7 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } @@ -101,7 +101,7 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_teleport() -> Weight { - Weight::from_ref_time(21_605_000 as u64) + Weight::from_ref_time(25_016_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 003a0788721..0a4498c56e7 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-15, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-kusama-dev"), DB CACHE: 1024 @@ -55,58 +55,59 @@ impl WeightInfo { // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - pub(crate) fn query_holding() -> Weight { - Weight::from_ref_time(21_039_000 as u64) + pub(crate) fn report_holding() -> Weight { + Weight::from_ref_time(28_638_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn buy_execution() -> Weight { - Weight::from_ref_time(5_375_000 as u64) + Weight::from_ref_time(5_869_000 as u64) } // Storage: PolkadotXcm Queries (r:1 w:0) pub(crate) fn query_response() -> Weight { - Weight::from_ref_time(16_091_000 as u64) + Weight::from_ref_time(18_018_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) } pub(crate) fn transact() -> Weight { - Weight::from_ref_time(19_057_000 as u64) + Weight::from_ref_time(21_240_000 as u64) } pub(crate) fn refund_surplus() -> Weight { - Weight::from_ref_time(5_397_000 as u64) + Weight::from_ref_time(6_118_000 as u64) } pub(crate) fn set_error_handler() -> Weight { - Weight::from_ref_time(5_347_000 as u64) + Weight::from_ref_time(5_855_000 as u64) } pub(crate) fn set_appendix() -> Weight { - Weight::from_ref_time(5_346_000 as u64) + Weight::from_ref_time(5_901_000 as u64) } pub(crate) fn clear_error() -> Weight { - Weight::from_ref_time(5_310_000 as u64) + Weight::from_ref_time(5_762_000 as u64) } pub(crate) fn descend_origin() -> Weight { - Weight::from_ref_time(6_273_000 as u64) + Weight::from_ref_time(6_717_000 as u64) } pub(crate) fn clear_origin() -> Weight { - Weight::from_ref_time(5_279_000 as u64) + Weight::from_ref_time(5_924_000 as u64) } + // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_error() -> Weight { - Weight::from_ref_time(14_974_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) + Weight::from_ref_time(22_264_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: PolkadotXcm AssetTraps (r:1 w:1) pub(crate) fn claim_asset() -> Weight { - Weight::from_ref_time(20_711_000 as u64) + Weight::from_ref_time(21_620_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } pub(crate) fn trap() -> Weight { - Weight::from_ref_time(5_154_000 as u64) + Weight::from_ref_time(5_795_000 as u64) } // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -115,13 +116,13 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn subscribe_version() -> Weight { - Weight::from_ref_time(17_872_000 as u64) + Weight::from_ref_time(29_039_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) pub(crate) fn unsubscribe_version() -> Weight { - Weight::from_ref_time(7_356_000 as u64) + Weight::from_ref_time(8_794_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: ParachainInfo ParachainId (r:1 w:0) @@ -131,8 +132,60 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_reserve_withdraw() -> Weight { - Weight::from_ref_time(22_312_000 as u64) + Weight::from_ref_time(26_793_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + pub(crate) fn burn_asset() -> Weight { + Weight::from_ref_time(7_912_000 as u64) + } + pub(crate) fn expect_asset() -> Weight { + Weight::from_ref_time(6_981_000 as u64) + } + pub(crate) fn expect_origin() -> Weight { + Weight::from_ref_time(6_880_000 as u64) + } + pub(crate) fn expect_error() -> Weight { + Weight::from_ref_time(6_802_000 as u64) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn query_pallet() -> Weight { + Weight::from_ref_time(31_039_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } + pub(crate) fn expect_pallet() -> Weight { + Weight::from_ref_time(9_291_000 as u64) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn report_transact_status() -> Weight { + Weight::from_ref_time(28_012_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + pub(crate) fn clear_transact_status() -> Weight { + Weight::from_ref_time(7_491_000 as u64) + } + pub(crate) fn set_topic() -> Weight { + Weight::from_ref_time(7_373_000 as u64) + } + pub(crate) fn clear_topic() -> Weight { + Weight::from_ref_time(7_490_000 as u64) + } + pub(crate) fn set_fees_mode() -> Weight { + Weight::from_ref_time(11_585_000 as u64) + } + pub(crate) fn unpaid_execution() -> Weight { + Weight::from_ref_time(7_582_000 as u64) + } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index f31d84a091a..6fca60ef532 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -21,7 +21,6 @@ use super::{ use frame_support::{ match_types, parameter_types, traits::{ConstU32, Contains, Everything, Nothing}, - weights::Weight, }; use pallet_xcm::XcmPassthrough; use parachains_common::xcm_config::{ @@ -44,7 +43,7 @@ parameter_types! { pub const RelayNetwork: Option = Some(NetworkId::Kusama); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = - Parachain(ParachainInfo::parachain_id().into()).into(); + X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 71f0a42a5d4..40f3c4bbe87 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -646,7 +646,7 @@ impl_runtime_apis! { fn valid_destination() -> Result { Ok(RelayLocation::get()) } - fn worst_case_holding() -> MultiAssets { + fn worst_case_holding(_depositable_count: u32) -> MultiAssets { // just concrete assets according to relay chain. let assets: Vec = vec![ MultiAsset { @@ -663,8 +663,7 @@ impl_runtime_apis! { RelayLocation::get(), MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(RelayLocation::get()) }, )); - pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = None; - pub const CheckedAccount: Option = None; + pub const CheckedAccount: Option<(AccountId, xcm_builder::MintLocation)> = None; } impl pallet_xcm_benchmarks::fungible::Config for Runtime { @@ -672,7 +671,6 @@ impl_runtime_apis! { type CheckedAccount = CheckedAccount; type TrustedTeleporter = TrustedTeleporter; - type TrustedReserve = TrustedReserve; fn get_multi_asset() -> MultiAsset { MultiAsset { @@ -689,8 +687,16 @@ impl_runtime_apis! { (0u64, Response::Version(Default::default())) } - fn transact_origin() -> Result { - Ok(RelayLocation::get()) + fn worst_case_asset_exchange() -> Result<(MultiAssets, MultiAssets), BenchmarkError> { + Err(BenchmarkError::Skip) + } + + fn universal_alias() -> Result { + Err(BenchmarkError::Skip) + } + + fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> { + Ok((RelayLocation::get(), frame_system::Call::remark_with_event { remark: vec![] }.into())) } fn subscribe_origin() -> Result { @@ -703,6 +709,10 @@ impl_runtime_apis! { let ticket = MultiLocation { parents: 0, interior: Here }; Ok((origin, ticket, assets)) } + + fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { + Err(BenchmarkError::Skip) + } } type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs index d3308ed68cb..7d0e4a3546c 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 @@ -53,15 +53,15 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) fn send() -> Weight { - // Minimum execution time: 26_918 nanoseconds. - Weight::from_ref_time(28_481_000) + // Minimum execution time: 27_797 nanoseconds. + Weight::from_ref_time(28_356_000) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn teleport_assets() -> Weight { - // Minimum execution time: 33_157 nanoseconds. - Weight::from_ref_time(33_584_000) + // Minimum execution time: 32_656 nanoseconds. + Weight::from_ref_time(33_011_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: Benchmark Override (r:0 w:0) @@ -76,14 +76,14 @@ impl pallet_xcm::WeightInfo for WeightInfo { } // Storage: PolkadotXcm SupportedVersion (r:0 w:1) fn force_xcm_version() -> Weight { - // Minimum execution time: 14_206 nanoseconds. - Weight::from_ref_time(14_525_000) + // Minimum execution time: 15_101 nanoseconds. + Weight::from_ref_time(15_535_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) fn force_default_xcm_version() -> Weight { - // Minimum execution time: 4_493 nanoseconds. - Weight::from_ref_time(4_573_000) + // Minimum execution time: 4_700 nanoseconds. + Weight::from_ref_time(4_896_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) @@ -95,8 +95,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_subscribe_version_notify() -> Weight { - // Minimum execution time: 31_132 nanoseconds. - Weight::from_ref_time(31_788_000) + // Minimum execution time: 31_532 nanoseconds. + Weight::from_ref_time(32_180_000) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -108,9 +108,66 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_unsubscribe_version_notify() -> Weight { - // Minimum execution time: 32_099 nanoseconds. - Weight::from_ref_time(32_731_000) + // Minimum execution time: 32_515 nanoseconds. + Weight::from_ref_time(33_097_000) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } + // Storage: PolkadotXcm SupportedVersion (r:4 w:2) + fn migrate_supported_version() -> Weight { + // Minimum execution time: 13_811 nanoseconds. + Weight::from_ref_time(14_149_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifiers (r:4 w:2) + fn migrate_version_notifiers() -> Weight { + // Minimum execution time: 13_708 nanoseconds. + Weight::from_ref_time(14_067_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:5 w:0) + fn already_notified_target() -> Weight { + // Minimum execution time: 15_277 nanoseconds. + Weight::from_ref_time(15_626_000) + .saturating_add(T::DbWeight::get().reads(5)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:2 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn notify_current_targets() -> Weight { + // Minimum execution time: 29_263 nanoseconds. + Weight::from_ref_time(29_831_000) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:3 w:0) + fn notify_target_migration_fail() -> Weight { + // Minimum execution time: 7_787 nanoseconds. + Weight::from_ref_time(7_858_000) + .saturating_add(T::DbWeight::get().reads(3)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) + fn migrate_version_notify_targets() -> Weight { + // Minimum execution time: 15_252 nanoseconds. + Weight::from_ref_time(15_558_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn migrate_and_notify_old_targets() -> Weight { + // Minimum execution time: 35_105 nanoseconds. + Weight::from_ref_time(35_719_000) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs index 4d0d2c6ca9e..0b911aec2d5 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs @@ -21,168 +21,220 @@ use crate::Runtime; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; -use sp_std::{cmp, prelude::*}; -use xcm::{ - latest::{prelude::*, Weight as XCMWeight}, - DoubleEncoded, -}; +use sp_std::prelude::*; +use xcm::{latest::prelude::*, DoubleEncoded}; trait WeighMultiAssets { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight; + fn weigh_multi_assets(&self, weight: Weight) -> Weight; } const MAX_ASSETS: u32 = 100; impl WeighMultiAssets for MultiAssetFilter { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight { - let weight = match self { + fn weigh_multi_assets(&self, weight: Weight) -> Weight { + match self { Self::Definite(assets) => weight.saturating_mul(assets.inner().into_iter().count() as u64), Self::Wild(_) => weight.saturating_mul(MAX_ASSETS as u64), - }; - weight.ref_time() + } } } impl WeighMultiAssets for MultiAssets { - fn weigh_multi_assets(&self, weight: Weight) -> XCMWeight { - weight.saturating_mul(self.inner().into_iter().count() as u64).ref_time() + fn weigh_multi_assets(&self, weight: Weight) -> Weight { + weight.saturating_mul(self.inner().into_iter().count() as u64) } } pub struct BridgeHubRococoXcmWeight(core::marker::PhantomData); impl XcmWeightInfo for BridgeHubRococoXcmWeight { - fn withdraw_asset(assets: &MultiAssets) -> XCMWeight { + fn withdraw_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::withdraw_asset()) } // Currently there is no trusted reserve - fn reserve_asset_deposited(_assets: &MultiAssets) -> XCMWeight { - u64::MAX + fn reserve_asset_deposited(_assets: &MultiAssets) -> Weight { + // TODO: hardcoded - fix https://github.com/paritytech/cumulus/issues/1974 + Weight::from_ref_time(1_000_000_000 as u64) } - fn receive_teleported_asset(assets: &MultiAssets) -> XCMWeight { + fn receive_teleported_asset(assets: &MultiAssets) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::receive_teleported_asset()) } - fn query_response(_query_id: &u64, _response: &Response, _max_weight: &u64) -> XCMWeight { - XcmGeneric::::query_response().ref_time() + fn query_response( + _query_id: &u64, + _response: &Response, + _max_weight: &Weight, + _querier: &Option, + ) -> Weight { + XcmGeneric::::query_response() } - fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> XCMWeight { + fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::transfer_asset()) } fn transfer_reserve_asset( assets: &MultiAssets, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::transfer_reserve_asset()) } fn transact( _origin_type: &OriginKind, - _require_weight_at_most: &u64, + _require_weight_at_most: &Weight, _call: &DoubleEncoded, - ) -> XCMWeight { - XcmGeneric::::transact().ref_time() + ) -> Weight { + XcmGeneric::::transact() } fn hrmp_new_channel_open_request( _sender: &u32, _max_message_size: &u32, _max_capacity: &u32, - ) -> XCMWeight { + ) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn hrmp_channel_accepted(_recipient: &u32) -> XCMWeight { + fn hrmp_channel_accepted(_recipient: &u32) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> XCMWeight { + fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> Weight { // XCM Executor does not currently support HRMP channel operations - Weight::MAX.ref_time() + Weight::MAX } - fn clear_origin() -> XCMWeight { - XcmGeneric::::clear_origin().ref_time() + fn clear_origin() -> Weight { + XcmGeneric::::clear_origin() } - fn descend_origin(_who: &InteriorMultiLocation) -> XCMWeight { - XcmGeneric::::descend_origin().ref_time() + fn descend_origin(_who: &InteriorMultiLocation) -> Weight { + XcmGeneric::::descend_origin() } - fn report_error( - _query_id: &QueryId, - _dest: &MultiLocation, - _max_response_weight: &u64, - ) -> XCMWeight { - XcmGeneric::::report_error().ref_time() + fn report_error(_query_response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::report_error() } - fn deposit_asset( - assets: &MultiAssetFilter, - _max_assets: &u32, - _dest: &MultiLocation, - ) -> XCMWeight { + fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> Weight { // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_ref_time(1_000_000_000 as u64).ref_time(); + let hardcoded_weight = Weight::from_ref_time(1_000_000_000 as u64); let weight = assets.weigh_multi_assets(XcmFungibleWeight::::deposit_asset()); - cmp::min(hardcoded_weight, weight) + hardcoded_weight.min(weight) } fn deposit_reserve_asset( assets: &MultiAssetFilter, - _max_assets: &u32, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmFungibleWeight::::deposit_reserve_asset()) } - fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets) -> XCMWeight { - Weight::MAX.ref_time() + fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets, _maximal: &bool) -> Weight { + Weight::MAX } fn initiate_reserve_withdraw( assets: &MultiAssetFilter, _reserve: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { assets.weigh_multi_assets(XcmGeneric::::initiate_reserve_withdraw()) } fn initiate_teleport( assets: &MultiAssetFilter, _dest: &MultiLocation, _xcm: &Xcm<()>, - ) -> XCMWeight { + ) -> Weight { // Hardcoded till the XCM pallet is fixed - let hardcoded_weight = Weight::from_ref_time(200_000_000 as u64).ref_time(); + let hardcoded_weight = Weight::from_ref_time(200_000_000 as u64); let weight = assets.weigh_multi_assets(XcmFungibleWeight::::initiate_teleport()); - cmp::min(hardcoded_weight, weight) + hardcoded_weight.min(weight) } - fn query_holding( - _query_id: &u64, - _dest: &MultiLocation, - _assets: &MultiAssetFilter, - _max_response_weight: &u64, - ) -> XCMWeight { - XcmGeneric::::query_holding().ref_time() + fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> Weight { + XcmGeneric::::report_holding() + } + fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> Weight { + XcmGeneric::::buy_execution() + } + fn refund_surplus() -> Weight { + XcmGeneric::::refund_surplus() + } + fn set_error_handler(_xcm: &Xcm) -> Weight { + XcmGeneric::::set_error_handler() + } + fn set_appendix(_xcm: &Xcm) -> Weight { + XcmGeneric::::set_appendix() + } + fn clear_error() -> Weight { + XcmGeneric::::clear_error() + } + fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> Weight { + XcmGeneric::::claim_asset() + } + fn trap(_code: &u64) -> Weight { + XcmGeneric::::trap() + } + fn subscribe_version(_query_id: &QueryId, _max_response_weight: &Weight) -> Weight { + XcmGeneric::::subscribe_version() + } + fn unsubscribe_version() -> Weight { + XcmGeneric::::unsubscribe_version() + } + fn burn_asset(assets: &MultiAssets) -> Weight { + assets.weigh_multi_assets(XcmGeneric::::burn_asset()) + } + fn expect_asset(assets: &MultiAssets) -> Weight { + assets.weigh_multi_assets(XcmGeneric::::expect_asset()) + } + fn expect_origin(_origin: &Option) -> Weight { + XcmGeneric::::expect_origin() + } + fn expect_error(_error: &Option<(u32, XcmError)>) -> Weight { + XcmGeneric::::expect_error() + } + fn query_pallet(_module_name: &Vec, _response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::query_pallet() + } + fn expect_pallet( + _index: &u32, + _name: &Vec, + _module_name: &Vec, + _crate_major: &u32, + _min_crate_minor: &u32, + ) -> Weight { + XcmGeneric::::expect_pallet() + } + fn report_transact_status(_response_info: &QueryResponseInfo) -> Weight { + XcmGeneric::::report_transact_status() + } + fn clear_transact_status() -> Weight { + XcmGeneric::::clear_transact_status() + } + fn universal_origin(_: &Junction) -> Weight { + Weight::MAX + } + fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { + Weight::MAX } - fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> XCMWeight { - XcmGeneric::::buy_execution().ref_time() + fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn refund_surplus() -> XCMWeight { - XcmGeneric::::refund_surplus().ref_time() + fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn set_error_handler(_xcm: &Xcm) -> XCMWeight { - XcmGeneric::::set_error_handler().ref_time() + fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn set_appendix(_xcm: &Xcm) -> XCMWeight { - XcmGeneric::::set_appendix().ref_time() + fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> Weight { + Weight::MAX } - fn clear_error() -> XCMWeight { - XcmGeneric::::clear_error().ref_time() + fn set_fees_mode(_: &bool) -> Weight { + XcmGeneric::::set_fees_mode() } - fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> XCMWeight { - XcmGeneric::::claim_asset().ref_time() + fn set_topic(_topic: &[u8; 32]) -> Weight { + XcmGeneric::::set_topic() } - fn trap(_code: &u64) -> XCMWeight { - XcmGeneric::::trap().ref_time() + fn clear_topic() -> Weight { + XcmGeneric::::clear_topic() } - fn subscribe_version(_query_id: &QueryId, _max_response_weight: &u64) -> XCMWeight { - XcmGeneric::::subscribe_version().ref_time() + fn alias_origin(_: &MultiLocation) -> Weight { + // XCM Executor does not currently support alias origin operations + Weight::MAX } - fn unsubscribe_version() -> XCMWeight { - XcmGeneric::::unsubscribe_version().ref_time() + fn unpaid_execution(_: &WeightLimit, _: &Option) -> Weight { + XcmGeneric::::unpaid_execution() } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 213ddc25d38..fcd1d27780b 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-15, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 @@ -51,13 +51,13 @@ pub struct WeightInfo(PhantomData); impl WeightInfo { // Storage: System Account (r:1 w:1) pub(crate) fn withdraw_asset() -> Weight { - Weight::from_ref_time(26_254_000 as u64) + Weight::from_ref_time(27_244_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: System Account (r:2 w:2) pub(crate) fn transfer_asset() -> Weight { - Weight::from_ref_time(36_415_000 as u64) + Weight::from_ref_time(38_254_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } @@ -69,16 +69,16 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn transfer_reserve_asset() -> Weight { - Weight::from_ref_time(52_145_000 as u64) + Weight::from_ref_time(55_608_000 as u64) .saturating_add(T::DbWeight::get().reads(8 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } pub(crate) fn receive_teleported_asset() -> Weight { - Weight::from_ref_time(5_889_000 as u64) + Weight::from_ref_time(7_053_000 as u64) } // Storage: System Account (r:1 w:1) pub(crate) fn deposit_asset() -> Weight { - Weight::from_ref_time(27_689_000 as u64) + Weight::from_ref_time(29_030_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } @@ -90,7 +90,7 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn deposit_reserve_asset() -> Weight { - Weight::from_ref_time(44_857_000 as u64) + Weight::from_ref_time(48_233_000 as u64) .saturating_add(T::DbWeight::get().reads(7 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } @@ -101,7 +101,7 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_teleport() -> Weight { - Weight::from_ref_time(21_586_000 as u64) + Weight::from_ref_time(24_691_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 898e440420a..85c0fb34ac4 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-15, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 @@ -55,58 +55,59 @@ impl WeightInfo { // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) - pub(crate) fn query_holding() -> Weight { - Weight::from_ref_time(21_192_000 as u64) + pub(crate) fn report_holding() -> Weight { + Weight::from_ref_time(27_691_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub(crate) fn buy_execution() -> Weight { - Weight::from_ref_time(5_394_000 as u64) + Weight::from_ref_time(5_927_000 as u64) } // Storage: PolkadotXcm Queries (r:1 w:0) pub(crate) fn query_response() -> Weight { - Weight::from_ref_time(16_081_000 as u64) + Weight::from_ref_time(17_594_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) } pub(crate) fn transact() -> Weight { - Weight::from_ref_time(19_230_000 as u64) + Weight::from_ref_time(20_743_000 as u64) } pub(crate) fn refund_surplus() -> Weight { - Weight::from_ref_time(5_286_000 as u64) + Weight::from_ref_time(5_960_000 as u64) } pub(crate) fn set_error_handler() -> Weight { - Weight::from_ref_time(5_275_000 as u64) + Weight::from_ref_time(5_857_000 as u64) } pub(crate) fn set_appendix() -> Weight { - Weight::from_ref_time(5_190_000 as u64) + Weight::from_ref_time(5_819_000 as u64) } pub(crate) fn clear_error() -> Weight { - Weight::from_ref_time(5_113_000 as u64) + Weight::from_ref_time(5_826_000 as u64) } pub(crate) fn descend_origin() -> Weight { - Weight::from_ref_time(6_169_000 as u64) + Weight::from_ref_time(6_641_000 as u64) } pub(crate) fn clear_origin() -> Weight { - Weight::from_ref_time(5_158_000 as u64) + Weight::from_ref_time(5_754_000 as u64) } + // Storage: ParachainInfo ParachainId (r:1 w:0) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn report_error() -> Weight { - Weight::from_ref_time(14_826_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) + Weight::from_ref_time(21_742_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: PolkadotXcm AssetTraps (r:1 w:1) pub(crate) fn claim_asset() -> Weight { - Weight::from_ref_time(20_278_000 as u64) + Weight::from_ref_time(20_833_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } pub(crate) fn trap() -> Weight { - Weight::from_ref_time(5_100_000 as u64) + Weight::from_ref_time(5_773_000 as u64) } // Storage: PolkadotXcm VersionNotifyTargets (r:1 w:1) // Storage: PolkadotXcm SupportedVersion (r:1 w:0) @@ -115,13 +116,13 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn subscribe_version() -> Weight { - Weight::from_ref_time(17_701_000 as u64) + Weight::from_ref_time(28_365_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: PolkadotXcm VersionNotifyTargets (r:0 w:1) pub(crate) fn unsubscribe_version() -> Weight { - Weight::from_ref_time(7_143_000 as u64) + Weight::from_ref_time(7_961_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: ParachainInfo ParachainId (r:1 w:0) @@ -131,8 +132,60 @@ impl WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) pub(crate) fn initiate_reserve_withdraw() -> Weight { - Weight::from_ref_time(21_895_000 as u64) + Weight::from_ref_time(24_970_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + pub(crate) fn burn_asset() -> Weight { + Weight::from_ref_time(7_400_000 as u64) + } + pub(crate) fn expect_asset() -> Weight { + Weight::from_ref_time(6_018_000 as u64) + } + pub(crate) fn expect_origin() -> Weight { + Weight::from_ref_time(5_862_000 as u64) + } + pub(crate) fn expect_error() -> Weight { + Weight::from_ref_time(5_857_000 as u64) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn query_pallet() -> Weight { + Weight::from_ref_time(24_420_000 as u64) .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } + pub(crate) fn expect_pallet() -> Weight { + Weight::from_ref_time(7_212_000 as u64) + } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + pub(crate) fn report_transact_status() -> Weight { + Weight::from_ref_time(22_227_000 as u64) + .saturating_add(T::DbWeight::get().reads(6 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + pub(crate) fn clear_transact_status() -> Weight { + Weight::from_ref_time(5_890_000 as u64) + } + pub(crate) fn set_topic() -> Weight { + Weight::from_ref_time(5_684_000 as u64) + } + pub(crate) fn clear_topic() -> Weight { + Weight::from_ref_time(5_763_000 as u64) + } + pub(crate) fn set_fees_mode() -> Weight { + Weight::from_ref_time(5_812_000 as u64) + } + pub(crate) fn unpaid_execution() -> Weight { + Weight::from_ref_time(6_043_000 as u64) + } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index ac75427425e..4613f9f19fc 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -21,7 +21,6 @@ use super::{ use frame_support::{ match_types, parameter_types, traits::{ConstU32, Contains, Everything, Nothing}, - weights::Weight, }; use pallet_xcm::XcmPassthrough; use parachains_common::xcm_config::{ @@ -41,10 +40,10 @@ use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); - pub const RelayNetwork: Option = None; + pub const RelayNetwork: Option = Some(NetworkId::Rococo); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = - Parachain(ParachainInfo::parachain_id().into()).into(); + X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_xcm.rs b/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_xcm.rs index 16964cac506..ec9974dd295 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_xcm.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/weights/pallet_xcm.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("collectives-polkadot-dev"), DB CACHE: 1024 @@ -53,15 +53,15 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem HostConfiguration (r:1 w:0) // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) fn send() -> Weight { - // Minimum execution time: 26_568 nanoseconds. - Weight::from_ref_time(27_534_000) + // Minimum execution time: 27_016 nanoseconds. + Weight::from_ref_time(27_587_000) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } // Storage: ParachainInfo ParachainId (r:1 w:0) fn teleport_assets() -> Weight { - // Minimum execution time: 33_083 nanoseconds. - Weight::from_ref_time(34_126_000) + // Minimum execution time: 33_807 nanoseconds. + Weight::from_ref_time(34_960_000) .saturating_add(T::DbWeight::get().reads(1)) } // Storage: Benchmark Override (r:0 w:0) @@ -76,14 +76,14 @@ impl pallet_xcm::WeightInfo for WeightInfo { } // Storage: PolkadotXcm SupportedVersion (r:0 w:1) fn force_xcm_version() -> Weight { - // Minimum execution time: 14_497 nanoseconds. - Weight::from_ref_time(14_849_000) + // Minimum execution time: 15_322 nanoseconds. + Weight::from_ref_time(15_782_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm SafeXcmVersion (r:0 w:1) fn force_default_xcm_version() -> Weight { - // Minimum execution time: 4_309 nanoseconds. - Weight::from_ref_time(4_647_000) + // Minimum execution time: 4_390 nanoseconds. + Weight::from_ref_time(4_564_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: PolkadotXcm VersionNotifiers (r:1 w:1) @@ -95,8 +95,8 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_subscribe_version_notify() -> Weight { - // Minimum execution time: 30_585 nanoseconds. - Weight::from_ref_time(31_735_000) + // Minimum execution time: 31_238 nanoseconds. + Weight::from_ref_time(32_021_000) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -108,9 +108,66 @@ impl pallet_xcm::WeightInfo for WeightInfo { // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) // Storage: PolkadotXcm Queries (r:0 w:1) fn force_unsubscribe_version_notify() -> Weight { - // Minimum execution time: 31_687 nanoseconds. - Weight::from_ref_time(32_241_000) + // Minimum execution time: 32_459 nanoseconds. + Weight::from_ref_time(32_802_000) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } + // Storage: PolkadotXcm SupportedVersion (r:4 w:2) + fn migrate_supported_version() -> Weight { + // Minimum execution time: 13_433 nanoseconds. + Weight::from_ref_time(13_890_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifiers (r:4 w:2) + fn migrate_version_notifiers() -> Weight { + // Minimum execution time: 13_798 nanoseconds. + Weight::from_ref_time(14_104_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:5 w:0) + fn already_notified_target() -> Weight { + // Minimum execution time: 15_012 nanoseconds. + Weight::from_ref_time(15_360_000) + .saturating_add(T::DbWeight::get().reads(5)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:2 w:1) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn notify_current_targets() -> Weight { + // Minimum execution time: 26_911 nanoseconds. + Weight::from_ref_time(27_686_000) + .saturating_add(T::DbWeight::get().reads(7)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:3 w:0) + fn notify_target_migration_fail() -> Weight { + // Minimum execution time: 7_067 nanoseconds. + Weight::from_ref_time(7_207_000) + .saturating_add(T::DbWeight::get().reads(3)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) + fn migrate_version_notify_targets() -> Weight { + // Minimum execution time: 13_882 nanoseconds. + Weight::from_ref_time(14_067_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + // Storage: PolkadotXcm VersionNotifyTargets (r:4 w:2) + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Storage: ParachainSystem HostConfiguration (r:1 w:0) + // Storage: ParachainSystem PendingUpwardMessages (r:1 w:1) + fn migrate_and_notify_old_targets() -> Weight { + // Minimum execution time: 32_227 nanoseconds. + Weight::from_ref_time(33_068_000) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } } diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index d59062e58ab..f6485a88efd 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -40,9 +40,10 @@ use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; parameter_types! { pub const DotLocation: MultiLocation = MultiLocation::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Polkadot; + pub const RelayNetwork: Option = Some(NetworkId::Polkadot); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); + pub UniversalLocation: InteriorMultiLocation = + X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); pub const Local: MultiLocation = Here.into_location(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); } diff --git a/parachains/runtimes/starters/shell/src/xcm_config.rs b/parachains/runtimes/starters/shell/src/xcm_config.rs index ca15fd65e45..8a7b3f21339 100644 --- a/parachains/runtimes/starters/shell/src/xcm_config.rs +++ b/parachains/runtimes/starters/shell/src/xcm_config.rs @@ -30,7 +30,7 @@ use xcm_builder::{ parameter_types! { pub const RococoLocation: MultiLocation = MultiLocation::parent(); - pub const RococoNetwork: NetworkId = NetworkId::Polkadot; + pub const RococoNetwork: Option = Some(NetworkId::Rococo); pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); } diff --git a/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/parachains/runtimes/testing/rococo-parachain/src/lib.rs index c5cc41f100f..f8e28aee4c7 100644 --- a/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -276,7 +276,7 @@ impl cumulus_pallet_aura_ext::Config for Runtime {} parameter_types! { pub const RocLocation: MultiLocation = MultiLocation::parent(); - pub const RococoNetwork: NetworkId = NetworkId::Polkadot; + pub const RococoNetwork: Option = Some(NetworkId::Rococo); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); From 28a604015be706744d8ec6c25da05c7a401e8af6 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 2 Jan 2023 12:01:38 +0300 Subject: [PATCH 160/263] remove shift session manager from bridge-hub-rococo (#2047) * remove shift session manager from bridge-hub-rococo * also remove from Cargo.toml --- Cargo.lock | 1 - Cargo.toml | 1 - .../runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml | 2 -- .../runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 6 ------ 4 files changed, 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a12f111e275..291ad7b88da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -994,7 +994,6 @@ dependencies = [ "pallet-bridge-relayers", "pallet-collator-selection", "pallet-session", - "pallet-shift-session-manager", "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", diff --git a/Cargo.toml b/Cargo.toml index a0d0e4e3b6b..7da5666d69b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ members = [ "bridges/modules/messages", "bridges/modules/parachains", "bridges/modules/relayers", - "bridges/modules/shift-session-manager", "bridges/primitives/messages", "bridges/primitives/runtime", "bridges/primitives/chain-bridge-hub-rococo", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 6b453580ddf..195bbd62bd3 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -79,7 +79,6 @@ pallet-bridge-grandpa = { path = "../../../../bridges/modules/grandpa", default- pallet-bridge-messages = { path = "../../../../bridges/modules/messages", default-features = false } pallet-bridge-parachains = { path = "../../../../bridges/modules/parachains", default-features = false } pallet-bridge-relayers = { path = "../../../../bridges/modules/relayers", default-features = false } -pallet-shift-session-manager = { path = "../../../../bridges/modules/shift-session-manager", default-features = false } bridge-runtime-common = { path = "../../../../bridges/bin/runtime-common", default-features = false } [dev-dependencies] @@ -121,7 +120,6 @@ std = [ "pallet-bridge-messages/std", "pallet-bridge-parachains/std", "pallet-bridge-relayers/std", - "pallet-shift-session-manager/std", "pallet-collator-selection/std", "pallet-session/std", "pallet-timestamp/std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index a91787fc118..c6326ef2aaf 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -573,9 +573,6 @@ impl pallet_bridge_messages::Config for Run >; } -/// Add shift session manager -impl pallet_shift_session_manager::Config for Runtime {} - // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime where @@ -608,9 +605,6 @@ construct_runtime!( CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 32, DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 33, - // Consensus support. - ShiftSessionManager: pallet_shift_session_manager::{Pallet} = 40, - // Wococo bridge modules BridgeWococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Config} = 41, BridgeWococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage, Event} = 42, From e2a3354486acbe0d0b90223e043c77dcbafca9c7 Mon Sep 17 00:00:00 2001 From: girazoki Date: Tue, 3 Jan 2023 10:49:55 +0100 Subject: [PATCH 161/263] Add event for showing the hash of an UMP sent message (#1228) * Add UpwardMessageSent event in parachain-system * additional fixes * Message Id * Fix errors from merge * fmt * more fmt * Remove todo * more formatting --- Cargo.lock | 1 + pallets/parachain-system/src/lib.rs | 16 ++++++++++++---- pallets/xcmp-queue/src/lib.rs | 2 -- primitives/core/Cargo.toml | 1 + primitives/core/src/lib.rs | 7 +++++-- primitives/utility/src/lib.rs | 4 +--- 6 files changed, 20 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66a06633446..8cbcf7b0bc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1975,6 +1975,7 @@ dependencies = [ "sp-runtime", "sp-std", "sp-trie", + "xcm", ] [[package]] diff --git a/pallets/parachain-system/src/lib.rs b/pallets/parachain-system/src/lib.rs index f328ea1758f..0fa0c8c3441 100644 --- a/pallets/parachain-system/src/lib.rs +++ b/pallets/parachain-system/src/lib.rs @@ -53,6 +53,7 @@ use sp_runtime::{ }, }; use sp_std::{cmp, collections::btree_map::BTreeMap, prelude::*}; +use xcm::latest::XcmHash; mod migration; mod relay_state_snapshot; @@ -482,6 +483,8 @@ pub mod pallet { DownwardMessagesReceived { count: u32 }, /// Downward messages were processed using the given weight. DownwardMessagesProcessed { weight_used: Weight, dmq_head: relay_chain::Hash }, + /// An upward message was sent to the relay chain. + UpwardMessageSent { message_hash: Option }, } #[pallet::error] @@ -1034,7 +1037,7 @@ impl frame_system::SetCode for ParachainSetCode { } impl Pallet { - pub fn send_upward_message(message: UpwardMessage) -> Result { + pub fn send_upward_message(message: UpwardMessage) -> Result<(u32, XcmHash), MessageSendError> { // Check if the message fits into the relay-chain constraints. // // Note, that we are using `host_configuration` here which may be from the previous @@ -1064,13 +1067,18 @@ impl Pallet { // Thus fall through here. }, }; - >::append(message); - Ok(0) + >::append(message.clone()); + + // The relay ump does not use using_encoded + // We apply the same this to use the same hash + let hash = sp_io::hashing::blake2_256(message); + Self::deposit_event(Event::UpwardMessageSent { message_hash: Some(hash) }); + Ok((0, hash)) } } impl UpwardMessageSender for Pallet { - fn send_upward_message(message: UpwardMessage) -> Result { + fn send_upward_message(message: UpwardMessage) -> Result<(u32, XcmHash), MessageSendError> { Self::send_upward_message(message) } } diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index 865df3d13b7..e31041b190e 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -290,8 +290,6 @@ pub mod pallet { BadVersion { message_hash: Option }, /// Bad XCM format used. BadFormat { message_hash: Option }, - /// An upward message was sent to the relay chain. - UpwardMessageSent { message_hash: Option }, /// An HRMP message was sent to a sibling parachain. XcmpMessageSent { message_hash: Option }, /// An XCM exceeded the individual message weight budget. diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 9a67cac16c5..bc00a655563 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -17,6 +17,7 @@ sp-trie = { git = "https://github.com/paritytech/substrate", default-features = polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } polkadot-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } [features] default = [ "std" ] diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index 516ff817dcf..23f50fe374a 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -32,6 +32,8 @@ pub use polkadot_primitives::v2::{ AbridgedHostConfiguration, AbridgedHrmpChannel, PersistedValidationData, }; +pub use xcm::latest::prelude::*; + /// A module that re-exports relevant relay chain definitions. pub mod relay_chain { pub use polkadot_core_primitives::*; @@ -94,10 +96,11 @@ pub trait GetChannelInfo { pub trait UpwardMessageSender { /// Send the given UMP message; return the expected number of blocks before the message will /// be dispatched or an error if the message cannot be sent. - fn send_upward_message(msg: UpwardMessage) -> Result; + /// return the hash of the message sent + fn send_upward_message(msg: UpwardMessage) -> Result<(u32, XcmHash), MessageSendError>; } impl UpwardMessageSender for () { - fn send_upward_message(_msg: UpwardMessage) -> Result { + fn send_upward_message(_msg: UpwardMessage) -> Result<(u32, XcmHash), MessageSendError> { Err(MessageSendError::NoChannel) } } diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index 2d771a0d5cc..2af92003e2e 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -91,9 +91,7 @@ where } fn deliver(data: Vec) -> Result { - let hash = data.using_encoded(sp_io::hashing::blake2_256); - - T::send_upward_message(data).map_err(|e| match e { + let (_, hash) = T::send_upward_message(data).map_err(|e| match e { MessageSendError::TooBig => SendError::ExceedsMaxMessageSize, e => SendError::Transport(e.into()), })?; From 63a18c2c3b1e5c042a942574ce2ac3d3961f981c Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 3 Jan 2023 18:57:44 +0900 Subject: [PATCH 162/263] Fixes --- Cargo.lock | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ccd570d0230..0a40c0659fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,7 +18,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" dependencies = [ - "gimli 0.26.1", + "gimli 0.26.2", ] [[package]] @@ -1360,7 +1360,7 @@ dependencies = [ "cranelift-codegen-shared", "cranelift-entity", "cranelift-isle", - "gimli 0.26.1", + "gimli 0.26.2", "log", "regalloc2", "smallvec", @@ -2910,7 +2910,7 @@ checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" dependencies = [ "crc32fast", "libz-sys", - "miniz_oxide 0.4.4", + "miniz_oxide 0.5.4", ] [[package]] @@ -12951,7 +12951,7 @@ dependencies = [ "indexmap", "libc", "log", - "object", + "object 0.29.0", "once_cell", "paste", "psm", @@ -13008,9 +13008,9 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli 0.26.1", + "gimli 0.26.2", "log", - "object", + "object 0.29.0", "target-lexicon", "thiserror", "wasmparser", @@ -13025,10 +13025,10 @@ checksum = "5c587c62e91c5499df62012b87b88890d0eb470b2ffecc5964e9da967b70c77c" dependencies = [ "anyhow", "cranelift-entity", - "gimli 0.26.1", + "gimli 0.26.2", "indexmap", "log", - "object", + "object 0.29.0", "serde", "target-lexicon", "thiserror", @@ -13047,9 +13047,9 @@ dependencies = [ "bincode", "cfg-if", "cpp_demangle", - "gimli 0.26.1", + "gimli 0.26.2", "log", - "object", + "object 0.29.0", "rustc-demangle", "rustix 0.35.10", "serde", @@ -13067,7 +13067,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b299569abf6f99b7b8e020afaf84a700e8636c6a42e242069267322cd5818235" dependencies = [ - "object", + "object 0.29.0", "once_cell", "rustix 0.35.10", ] From f9a039042338c9db2098ee4eab6d8e8f24c33bf5 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 3 Jan 2023 23:04:28 +0900 Subject: [PATCH 163/263] Fixes --- primitives/utility/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index 2af92003e2e..c9747b67db6 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -349,7 +349,7 @@ mod tests { /// Impl [`UpwardMessageSender`] that return `Other` error struct OtherErrorUpwardMessageSender; impl UpwardMessageSender for OtherErrorUpwardMessageSender { - fn send_upward_message(_: UpwardMessage) -> Result { + fn send_upward_message(_: UpwardMessage) -> Result<(u32, XcmHash), MessageSendError> { Err(MessageSendError::Other) } } From 893bc5c5b3a389f0081eb578393e140bac48aabe Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 3 Jan 2023 23:50:50 +0900 Subject: [PATCH 164/263] Fixes --- Cargo.lock | 1 + pallets/parachain-system/Cargo.toml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 0a40c0659fb..97ee45aecb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1878,6 +1878,7 @@ dependencies = [ "sp-tracing", "sp-trie", "sp-version", + "xcm", ] [[package]] diff --git a/pallets/parachain-system/Cargo.toml b/pallets/parachain-system/Cargo.toml index 7602de269f3..abd333470eb 100644 --- a/pallets/parachain-system/Cargo.toml +++ b/pallets/parachain-system/Cargo.toml @@ -28,6 +28,7 @@ sp-version = { git = "https://github.com/paritytech/substrate", default-features # Polkadot polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, features = [ "wasm-api" ], branch = "master" } +xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } # Cumulus cumulus-pallet-parachain-system-proc-macro = { path = "proc-macro", default-features = false } @@ -67,6 +68,7 @@ std = [ "sp-state-machine/std", "sp-std/std", "sp-trie/std", + "xcm/std", ] runtime-benchmarks = [ From e303fb22571f71fb020bb1b2e018fd59209c5519 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 4 Jan 2023 00:14:59 +0900 Subject: [PATCH 165/263] Fixes --- pallets/parachain-system/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/parachain-system/src/lib.rs b/pallets/parachain-system/src/lib.rs index 0fa0c8c3441..a0254dc8d79 100644 --- a/pallets/parachain-system/src/lib.rs +++ b/pallets/parachain-system/src/lib.rs @@ -1071,7 +1071,7 @@ impl Pallet { // The relay ump does not use using_encoded // We apply the same this to use the same hash - let hash = sp_io::hashing::blake2_256(message); + let hash = sp_io::hashing::blake2_256(&message); Self::deposit_event(Event::UpwardMessageSent { message_hash: Some(hash) }); Ok((0, hash)) } From 9f39173f1c9e92dbcf7d3f13f6b06065fde6e5d6 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 5 Jan 2023 23:53:48 +0100 Subject: [PATCH 166/263] Updated README.md and scripts/bridges_rococo_wococo.sh for sending messages (local, live) --- parachains/runtimes/bridge-hubs/README.md | 39 ++++- scripts/bridges_rococo_wococo.sh | 191 +++++++++++++++++++++- 2 files changed, 226 insertions(+), 4 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index eeef2addf5a..b6f07414854 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -148,8 +148,43 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - Pallet: **bridgeRococoParachain** - Keys: **bestParaHeads()** ---- - +### Send messages (Rococo, Wococo) + + +#### Local Rococo:Statemine -> Wococo:Westmint +- check that relayers are up and running (see above) +- uses account seed `//Alice` + ``` + cd + + ./scripts/bridges_rococo_wococo.sh send-remark-local + or + ./scripts/bridges_rococo_wococo.sh send-trap-local + ``` +- open explorers: (see zombienets) + - Statemine (see `polkadotXcm.Sent`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9910#/explorer + - BridgeHubRococo (see `bridgeWococoMessages.MessageAccepted`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8943#/explorer + - BridgeHubWococo (see `bridgeRococoMessages.MessagesReceived`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8945#/explorer + - Westmint (see `xcmpQueue.Success` for `remark` and `xcmpQueue.Fail` for `trap`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9010#/explorer + - BridgeHubRococo (see `bridgeWococoMessages.MessagesDelivered`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8943#/explorer + +#### Live Rococo:Rockmine2 -> Wococo:Wockmint +- uses account seed on Live Rococo:Rockmine2 + ``` + cd + + ./scripts/bridges_rococo_wococo.sh send-remark-rococo + or + ./scripts/bridges_rococo_wococo.sh send-trap-rococo + ``` +- open explorers: (see https://github.com/paritytech/parity-bridges-common/issues/1671) + - Rockmine2 (see `polkadotXcm.Sent`) + - BridgeHubRococo (see `bridgeWococoMessages.MessageAccepted`) + - BridgeHubWococo (see `bridgeRococoMessages.MessagesReceived`) + - Wockmint (see `xcmpQueue.Success` for `remark` and `xcmpQueue.Fail` for `trap`) + - BridgeHubRococo (see `bridgeWococoMessages.MessagesDelivered`) + +---- ## Git subtree `./bridges` Add Bridges repo as a local remote and synchronize it with latest `master` from bridges repo: diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh index 94edec489b9..0416cb6d749 100755 --- a/scripts/bridges_rococo_wococo.sh +++ b/scripts/bridges_rococo_wococo.sh @@ -13,6 +13,9 @@ function ensure_binaries() { echo " Please, check ./parachains/runtimes/bridge-hubs/README.md (Prepare/Build/Deploy)" exit 1 fi +} + +function ensure_relayer() { if [[ ! -f ~/local_bridge_testing/bin/substrate-relay ]]; then echo " Required substrate-relay binary '~/local_bridge_testing/bin/substrate-relay' does not exist!" echo " You need to build it and copy to this location!" @@ -21,6 +24,146 @@ function ensure_binaries() { fi } +function ensure_polkadot_js_api() { + if ! which polkadot-js-api &> /dev/null; then + echo '' + echo 'Required command `polkadot-js-api` not in PATH, please, install, e.g.:' + echo "npm install -g @polkadot/api-cli@beta" + echo " or" + echo "yarn global add @polkadot/api-cli" + echo '' + exit 1 + fi + if ! which jq &> /dev/null; then + echo '' + echo 'Required command `jq` not in PATH, please, install, e.g.:' + echo "apt install -y jq" + echo '' + exit 1 + fi +} + +STATEMINE_ACCOUNT_SEED_FOR_LOCAL="//Alice" +ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO="scatter feed race company oxygen trip extra elbow slot bundle auto canoe" + +function send_xcm_transact_remark_from_statemine() { + local url=$1 + local seed=$2 + local bridge_hub_para_id=$3 + local target_network=$4 + local target_network_para_id=$5 + echo " calling send_xcm_transact_remark_from_statemine:" + echo " url: ${url}" + echo " seed: ${seed}" + echo " bridge_hub_para_id: ${bridge_hub_para_id}" + echo " params:" + + local dest=$(jq --null-input \ + --arg bridge_hub_para_id "$bridge_hub_para_id" \ + '{ "V3": { "parents": 1, "interior": { "X1": { "Parachain": $bridge_hub_para_id } } } }') + + local message=$(jq --null-input \ + --arg target_network "$target_network" \ + --arg target_network_para_id "$target_network_para_id" \ + '{ + "V3": [ + { + "ExportMessage": { + "network": $target_network, + "destination": { + "X1": { + "Parachain": $target_network_para_id + } + }, + "xcm": [ + { + "Transact": { + "origin_kind": "SovereignAccount", + "require_weight_at_most": 1000000000, + "call": { + "encoded": [0, 8, 20, 104, 101, 108, 108, 111 ] + } + } + } + ] + } + } + ] + }') + + echo "" + echo " dest:" + echo "${dest}" + echo "" + echo " message:" + echo "${message}" + echo "" + echo "--------------------------------------------------" + + polkadot-js-api \ + --ws "${url?}" \ + --seed "${seed?}" \ + tx.polkadotXcm.send \ + "${dest}" \ + "${message}" +} + +function send_xcm_trap_from_statemine() { + local url=$1 + local seed=$2 + local bridge_hub_para_id=$3 + local target_network=$4 + local target_network_para_id=$5 + echo " calling send_xcm_trap_from_statemine:" + echo " url: ${url}" + echo " seed: ${seed}" + echo " bridge_hub_para_id: ${bridge_hub_para_id}" + echo " params:" + + local dest=$(jq --null-input \ + --arg bridge_hub_para_id "$bridge_hub_para_id" \ + '{ "V3": { "parents": 1, "interior": { "X1": { "Parachain": $bridge_hub_para_id } } } }') + + local message=$(jq --null-input \ + --arg target_network "$target_network" \ + --arg target_network_para_id "$target_network_para_id" \ + '{ + "V3": [ + { + "ExportMessage": { + "network": $target_network, + "destination": { + "X1": { + "Parachain": $target_network_para_id + } + }, + "xcm": [ + { + "Trap": 12345 + } + ] + } + } + ] + }') + + echo "" + echo " dest:" + echo "${dest}" + echo "" + echo " message:" + echo "${message}" + echo "" + echo "--------------------------------------------------" + + polkadot-js-api \ + --ws "${url?}" \ + --seed "${seed?}" \ + tx.polkadotXcm.send \ + "${dest}" \ + "${message}" +} + function register_parachain() { local PORT=$1 local PARA_ID=$2 @@ -69,6 +212,8 @@ function check_parachain_collator() { } function init_ro_wo() { + ensure_relayer + RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay init-bridge rococo-to-bridge-hub-wococo \ --source-host localhost \ @@ -79,6 +224,8 @@ function init_ro_wo() { } function init_wo_ro() { + ensure_relayer + RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay init-bridge wococo-to-bridge-hub-rococo \ --source-host localhost \ @@ -89,6 +236,8 @@ function init_wo_ro() { } function run_relay() { + ensure_relayer + RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay relay-headers-and-messages bridge-hub-rococo-bridge-hub-wococo \ --rococo-host localhost \ @@ -111,10 +260,10 @@ function run_relay() { --lane 00000002 } -ensure_binaries - case "$1" in start-rococo) + ensure_binaries + # TODO: change to generate: ./bin/polkadot key generate-node-key VALIDATOR_KEY_ALICE=(12D3KooWRD6kEQuKDn7BCsMfW71U8AEFDYTc7N2dFQthRsjSR8J5 aa5dc9a97f82f9af38d1f16fbc9c72e915577c3ca654f7d33601645ca5b9a8a5) VALIDATOR_KEY_BOB=(12D3KooWELR4gpc8unmH8WiauK133v397KoMfkkjESWJrzg7Y3Pq 556a36beaeeb95225eb84ae2633bd8135e5ace22f03291853654f07bc5da20ae) @@ -145,6 +294,8 @@ case "$1" in check_parachain_collator 8943 bridgeWococoGrandpa ;; start-wococo) + ensure_binaries + # TODO: change to generate: ./bin/polkadot key generate-node-key VALIDATOR_KEY_ALICE=(12D3KooWKB4SqNJAttmDHXEKtETLPr8Nixso8gUNzNKDS9b6HtYj 8c87694d3a3b3a0e201caceaae095aac66a76ba37545c439f7e0d986670da8e6) VALIDATOR_KEY_BOB=(12D3KooWJq6xkfyV3LFxoNgsCskAwLv6VfLhL3cjHfW4UxDNwroF 9ebcd7371b427d63c087762fe3dc5a1d4b01875cb8611c263feda21364d4a329) @@ -189,6 +340,42 @@ case "$1" in init_wo_ro run_relay ;; + send-remark-local) + ensure_polkadot_js_api + send_xcm_transact_remark_from_statemine \ + "ws://127.0.0.1:9910#/explorer" \ + "${STATEMINE_ACCOUNT_SEED_FOR_LOCAL}" \ + 1013 \ + "Wococo" \ + 1000 + ;; + send-trap-local) + ensure_polkadot_js_api + send_xcm_trap_from_statemine \ + "ws://127.0.0.1:9910#/explorer" \ + "${STATEMINE_ACCOUNT_SEED_FOR_LOCAL}" \ + 1013 \ + "Wococo" \ + 1000 + ;; + send-remark-rococo) + ensure_polkadot_js_api + send_xcm_transact_remark_from_statemine \ + "wss://ws-rococo-rockmine2-collator-node-0.parity-testnet.parity.io" \ + "${ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO}" \ + 1013 \ + "Wococo" \ + 1000 + ;; + send-trap-rococo) + ensure_polkadot_js_api + send_xcm_trap_from_statemine \ + "wss://ws-rococo-rockmine2-collator-node-0.parity-testnet.parity.io" \ + "${ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO}" \ + 1013 \ + "Wococo" \ + 1000 + ;; stop) pkill -f polkadot pkill -f parachain From 8fe432bca3a346b5ccbc6a5ed5c348222c707189 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sat, 7 Jan 2023 19:14:10 +0900 Subject: [PATCH 167/263] Allow explicit unpaid executions from the relay chains for system parachains (#2060) * Allow explicit unpaid executions from the relay chains for system parachains * Put origin-filtering barriers into WithComputedOrigin * Use ConstU32<8> * Small nits * formatting * cargo fmt --- parachain-template/runtime/src/xcm_config.rs | 18 ++++++++---- .../assets/statemine/src/xcm_config.rs | 19 +++++++++---- .../assets/statemint/src/xcm_config.rs | 19 +++++++++---- .../assets/westmint/src/xcm_config.rs | 19 +++++++++---- .../bridge-hub-kusama/src/xcm_config.rs | 19 +++++++++---- .../bridge-hub-rococo/src/xcm_config.rs | 19 +++++++++---- .../collectives-polkadot/src/xcm_config.rs | 20 +++++++++---- .../contracts-rococo/src/xcm_config.rs | 20 +++++++++---- .../runtimes/testing/penpal/src/xcm_config.rs | 28 +++++++++++++------ 9 files changed, 127 insertions(+), 54 deletions(-) diff --git a/parachain-template/runtime/src/xcm_config.rs b/parachain-template/runtime/src/xcm_config.rs index 7816b7f65d4..fa056fc65d3 100644 --- a/parachain-template/runtime/src/xcm_config.rs +++ b/parachain-template/runtime/src/xcm_config.rs @@ -13,11 +13,11 @@ use polkadot_parachain::primitives::Sibling; use polkadot_runtime_common::impls::ToAuthor; use xcm::latest::prelude::*; use xcm_builder::{ - AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, - EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentIsPreset, + AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, + CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - UsingComponents, + UsingComponents, WithComputedOrigin, }; use xcm_executor::{traits::ShouldExecute, XcmExecutor}; @@ -157,9 +157,15 @@ pub type Barrier = DenyThenTry< DenyReserveTransferToRelayChain, ( TakeWeightCredit, - AllowTopLevelPaidExecutionFrom, - AllowUnpaidExecutionFrom, - // ^^^ Parent and its exec plurality get free execution + WithComputedOrigin< + ( + AllowTopLevelPaidExecutionFrom, + AllowExplicitUnpaidExecutionFrom, + // ^^^ Parent and its exec plurality get free execution + ), + UniversalLocation, + ConstU32<8>, + >, ), >; diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 37317f9d1e0..721732993b9 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -39,6 +39,7 @@ use xcm_builder::{ NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, + WithComputedOrigin, }; use xcm_executor::{ traits::{JustTry, WithOriginFilter}, @@ -249,13 +250,21 @@ pub type Barrier = DenyThenTry< DenyReserveTransferToRelayChain, ( TakeWeightCredit, - AllowTopLevelPaidExecutionFrom, - // Parent and its exec plurality get free execution - AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, + // Allow XCMs with some computed origins to pass through. + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Parent and its executive plurality (i.e. governance) gets free execution. + AllowExplicitUnpaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, ), >; diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index 78919d0ae17..0aee1c97b16 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -39,6 +39,7 @@ use xcm_builder::{ NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, + WithComputedOrigin, }; use xcm_executor::{ traits::{JustTry, WithOriginFilter}, @@ -249,13 +250,21 @@ pub type Barrier = DenyThenTry< DenyReserveTransferToRelayChain, ( TakeWeightCredit, - AllowTopLevelPaidExecutionFrom, - // Parent and its exec plurality get free execution - AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, + // Allow XCMs with some computed origins to pass through. + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Parent and its executive plurality (i.e. governance) gets free execution. + AllowExplicitUnpaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, ), >; diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index 6e839044d0a..81578bc4da7 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -39,6 +39,7 @@ use xcm_builder::{ NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, + WithComputedOrigin, }; use xcm_executor::{ traits::{JustTry, WithOriginFilter}, @@ -244,13 +245,21 @@ pub type Barrier = DenyThenTry< DenyReserveTransferToRelayChain, ( TakeWeightCredit, - AllowTopLevelPaidExecutionFrom, - // Parent and its plurality get free execution - AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, + // Allow XCMs with some computed origins to pass through. + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Parent or its plurality (i.e. governance bodies) gets free execution. + AllowExplicitUnpaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, ), >; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index 6fca60ef532..c2e95c6ed4e 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -35,6 +35,7 @@ use xcm_builder::{ IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, + WithComputedOrigin, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -156,14 +157,20 @@ pub type Barrier = DenyThenTry< ( // Allow local users to buy weight credit. TakeWeightCredit, - // Parent and its exec plurality get free execution. - AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, - // Allow anything to pay for execution. - AllowTopLevelPaidExecutionFrom, + WithComputedOrigin< + ( + // Allow anything to pay for execution. + AllowTopLevelPaidExecutionFrom, + // Parent and its exec plurality get free execution. + AllowExplicitUnpaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, ), >; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 4613f9f19fc..5e2511d6eb5 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -35,6 +35,7 @@ use xcm_builder::{ IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, + WithComputedOrigin, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -156,14 +157,20 @@ pub type Barrier = DenyThenTry< ( // Allow local users to buy weight credit. TakeWeightCredit, - // Parent and its exec plurality get free execution. - AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, - // Allow anything to pay for execution. - AllowTopLevelPaidExecutionFrom, + WithComputedOrigin< + ( + // Allow anything to pay for execution. + AllowTopLevelPaidExecutionFrom, + // Parent and its exec plurality get free execution. + AllowExplicitUnpaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, ), >; diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index f6485a88efd..984d42ba50e 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -35,6 +35,7 @@ use xcm_builder::{ FixedWeightBounds, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + WithComputedOrigin, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -185,14 +186,21 @@ pub type Barrier = DenyThenTry< ( // Allow local users to buy weight credit. TakeWeightCredit, - // Parent and its exec plurality get free execution. - AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, - // Allow anything to pay for execution. - AllowTopLevelPaidExecutionFrom, + // Allow XCMs with some computed origins to pass through. + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Parent and its executive plurality (i.e. governance) gets free execution. + AllowExplicitUnpaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, ), >; diff --git a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index 6e5bcb2d9a8..068d0ded4d3 100644 --- a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -33,7 +33,7 @@ use xcm_builder::{ FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - UsingComponents, + UsingComponents, WithComputedOrigin, }; use xcm_executor::XcmExecutor; @@ -122,13 +122,21 @@ pub type Barrier = DenyThenTry< DenyReserveTransferToRelayChain, ( TakeWeightCredit, - AllowTopLevelPaidExecutionFrom, - // Parent and its exec plurality get free execution - AllowExplicitUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, + // Allow XCMs with some computed origins to pass through. + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Parent and its executive plurality (i.e. governance) gets free execution. + AllowExplicitUnpaidExecutionFrom, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, ), >; diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs index 7af7ae2091f..cdc030318de 100644 --- a/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -43,12 +43,13 @@ use polkadot_runtime_common::impls::ToAuthor; use sp_runtime::traits::Zero; use xcm::latest::prelude::*; use xcm_builder::{ - AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, + AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, + AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + WithComputedOrigin, }; use xcm_executor::{traits::JustTry, XcmExecutor}; @@ -152,15 +153,24 @@ pub type Barrier = DenyThenTry< DenyReserveTransferToRelayChain, ( TakeWeightCredit, - AllowTopLevelPaidExecutionFrom, - // Parent and its exec plurality get free execution - AllowUnpaidExecutionFrom, - // Assets Common Good parachain gets free execution - AllowUnpaidExecutionFrom, // Expected responses are OK. AllowKnownQueryResponses, - // Subscriptions for version tracking are OK. - AllowSubscriptionsFrom, + // Allow XCMs with some computed origins to pass through. + WithComputedOrigin< + ( + // If the message is one that immediately attemps to pay for execution, then allow it. + AllowTopLevelPaidExecutionFrom, + // Common Good Assets parachain, parent and its exec plurality get free execution + AllowExplicitUnpaidExecutionFrom<( + CommonGoodAssetsParachain, + ParentOrParentsExecutivePlurality, + )>, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, + ), + UniversalLocation, + ConstU32<8>, + >, ), >; From 9bf5a7eaa0e8f7d7a6d3a48d24bd9500e2107a5d Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 9 Jan 2023 12:18:38 +0100 Subject: [PATCH 168/263] Align laneId to 00000001 --- parachains/runtimes/bridge-hubs/README.md | 3 +-- .../bridge-hub-rococo/src/bridge_hub_rococo_config.rs | 2 +- .../bridge-hub-rococo/src/bridge_hub_wococo_config.rs | 2 +- parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 2 +- scripts/bridges_rococo_wococo.sh | 3 +-- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index b6f07414854..7d35930e7f5 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -124,8 +124,7 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ --rococo-headers-to-bridge-hub-wococo-signer //Bob \ --rococo-parachains-to-bridge-hub-wococo-signer //Bob \ --bridge-hub-wococo-transactions-mortality 4 \ - --lane 00000001 \ - --lane 00000002 + --lane 00000001 ``` **Check relay-chain headers relaying:** diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index 383189aff30..c7506e6d74a 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -61,7 +61,6 @@ pub type ToBridgeHubWococoHaulBlobExporter = HaulBlobExporter< (), >; pub struct ToBridgeHubWococoXcmBlobHauler; -pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO: LaneId = [0, 0, 0, 2]; impl XcmBlobHauler for ToBridgeHubWococoXcmBlobHauler { type MessageSender = pallet_bridge_messages::Pallet; @@ -74,6 +73,7 @@ impl XcmBlobHauler for ToBridgeHubWococoXcmBlobHauler { DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO } } +const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO: LaneId = [0, 0, 0, 1]; /// Messaging Bridge configuration for BridgeHubRococo -> BridgeHubWococo pub struct WithBridgeHubWococoMessageBridge; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 8973d9f179c..01759671382 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -61,7 +61,6 @@ pub type ToBridgeHubRococoHaulBlobExporter = HaulBlobExporter< (), >; pub struct ToBridgeHubRococoXcmBlobHauler; -pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO: LaneId = [0, 0, 0, 1]; impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { type MessageSender = pallet_bridge_messages::Pallet; @@ -74,6 +73,7 @@ impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO } } +const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO: LaneId = [0, 0, 0, 1]; /// Messaging Bridge configuration for BridgeHubWococo -> BridgeHubRococo pub struct WithBridgeHubRococoMessageBridge; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index c6326ef2aaf..953ac368283 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -169,7 +169,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("bridge-hub-rococo"), impl_name: create_runtime_str!("bridge-hub-rococo"), authoring_version: 1, - spec_version: 9301, + spec_version: 9302, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh index 0416cb6d749..1491db61394 100755 --- a/scripts/bridges_rococo_wococo.sh +++ b/scripts/bridges_rococo_wococo.sh @@ -256,8 +256,7 @@ function run_relay() { --rococo-headers-to-bridge-hub-wococo-signer //Bob \ --rococo-parachains-to-bridge-hub-wococo-signer //Bob \ --bridge-hub-wococo-transactions-mortality 4 \ - --lane 00000001 \ - --lane 00000002 + --lane 00000001 } case "$1" in From 87e0ea11645b649abcb5d7cf86b8586263df59ba Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Tue, 10 Jan 2023 21:14:11 +0900 Subject: [PATCH 169/263] Allow receiving XCMs from any relay chain plurality --- parachains/runtimes/assets/statemine/src/xcm_config.rs | 8 ++++---- parachains/runtimes/assets/statemint/src/xcm_config.rs | 8 ++++---- .../collectives/collectives-polkadot/src/xcm_config.rs | 4 ++-- .../runtimes/contracts/contracts-rococo/src/xcm_config.rs | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 721732993b9..a86828e0fe9 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -139,9 +139,9 @@ parameter_types! { } match_types! { - pub type ParentOrParentsExecutivePlurality: impl Contains = { + pub type ParentOrParentsPlurality: impl Contains = { MultiLocation { parents: 1, interior: Here } | - MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Executive, .. }) } + MultiLocation { parents: 1, interior: X1(Plurality { .. }) } }; pub type ParentOrSiblings: impl Contains = { MultiLocation { parents: 1, interior: Here } | @@ -257,8 +257,8 @@ pub type Barrier = DenyThenTry< ( // If the message is one that immediately attemps to pay for execution, then allow it. AllowTopLevelPaidExecutionFrom, - // Parent and its executive plurality (i.e. governance) gets free execution. - AllowExplicitUnpaidExecutionFrom, + // Parent and its plurality (i.e. governance bodies) gets free execution. + AllowExplicitUnpaidExecutionFrom, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, ), diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index 0aee1c97b16..8b76134da31 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -139,9 +139,9 @@ parameter_types! { } match_types! { - pub type ParentOrParentsExecutivePlurality: impl Contains = { + pub type ParentOrParentsPlurality: impl Contains = { MultiLocation { parents: 1, interior: Here } | - MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Executive, .. }) } + MultiLocation { parents: 1, interior: X1(Plurality { .. }) } }; pub type ParentOrSiblings: impl Contains = { MultiLocation { parents: 1, interior: Here } | @@ -257,8 +257,8 @@ pub type Barrier = DenyThenTry< ( // If the message is one that immediately attemps to pay for execution, then allow it. AllowTopLevelPaidExecutionFrom, - // Parent and its executive plurality (i.e. governance) gets free execution. - AllowExplicitUnpaidExecutionFrom, + // Parent and its plurality (i.e. governance bodies) gets free execution. + AllowExplicitUnpaidExecutionFrom, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, ), diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index 984d42ba50e..98c54de16cf 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -193,8 +193,8 @@ pub type Barrier = DenyThenTry< ( // If the message is one that immediately attemps to pay for execution, then allow it. AllowTopLevelPaidExecutionFrom, - // Parent and its executive plurality (i.e. governance) gets free execution. - AllowExplicitUnpaidExecutionFrom, + // Parent and its plurality (i.e. governance bodies) gets free execution. + AllowExplicitUnpaidExecutionFrom, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, ), diff --git a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index 068d0ded4d3..c71a60107e3 100644 --- a/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -108,9 +108,9 @@ parameter_types! { } match_types! { - pub type ParentOrParentsExecutivePlurality: impl Contains = { + pub type ParentOrParentsPlurality: impl Contains = { MultiLocation { parents: 1, interior: Here } | - MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Executive, .. }) } + MultiLocation { parents: 1, interior: X1(Plurality { .. }) } }; pub type ParentOrSiblings: impl Contains = { MultiLocation { parents: 1, interior: Here } | @@ -129,8 +129,8 @@ pub type Barrier = DenyThenTry< ( // If the message is one that immediately attemps to pay for execution, then allow it. AllowTopLevelPaidExecutionFrom, - // Parent and its executive plurality (i.e. governance) gets free execution. - AllowExplicitUnpaidExecutionFrom, + // Parent and its plurality (i.e. governance bodies) gets free execution. + AllowExplicitUnpaidExecutionFrom, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, ), From 146c742e407b5f3c83cee4b8eb8ec7684949a40b Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Wed, 11 Jan 2023 01:58:11 +0900 Subject: [PATCH 170/263] Fixes --- .../collectives/collectives-polkadot/src/xcm_config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index 98c54de16cf..a97c7677570 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -107,9 +107,9 @@ parameter_types! { } match_types! { - pub type ParentOrParentsExecutivePlurality: impl Contains = { + pub type ParentOrParentsPlurality: impl Contains = { MultiLocation { parents: 1, interior: Here } | - MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Executive, .. }) } + MultiLocation { parents: 1, interior: X1(Plurality { .. }) } }; pub type ParentOrSiblings: impl Contains = { MultiLocation { parents: 1, interior: Here } | From 5835109fc976211eff6afaa028061c38ec52e4f9 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Thu, 12 Jan 2023 12:28:29 +0200 Subject: [PATCH 171/263] Use Rococo/Wococo runtime APIs defined in bridge primitives (#2080) --- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 32 +++++-------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 953ac368283..3ac42e94580 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -206,22 +206,6 @@ pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } } -// TODO:check-parameter - move to bridges/primitives, once rebased and would compile with bp_bridge_hub_xyz dependencies -pub mod runtime_api { - use super::{BlockNumber, Hash}; - bp_runtime::decl_bridge_finality_runtime_apis!(rococo); - bp_runtime::decl_bridge_finality_runtime_apis!(wococo); - bp_runtime::decl_bridge_finality_runtime_apis!(bridge_hub_rococo); - bp_runtime::decl_bridge_finality_runtime_apis!(bridge_hub_wococo); - - use bp_messages::{ - InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, - }; - use sp_std::prelude::Vec; - bp_runtime::decl_bridge_messages_runtime_apis!(bridge_hub_rococo); - bp_runtime::decl_bridge_messages_runtime_apis!(bridge_hub_wococo); -} - parameter_types! { pub const Version: RuntimeVersion = VERSION; pub RuntimeBlockLength: BlockLength = @@ -754,20 +738,20 @@ impl_runtime_apis! { } } - impl runtime_api::RococoFinalityApi for Runtime { + impl bp_rococo::RococoFinalityApi for Runtime { fn best_finalized() -> Option> { BridgeRococoGrandpa::best_finalized().map(|header| header.id()) } } - impl runtime_api::WococoFinalityApi for Runtime { + impl bp_wococo::WococoFinalityApi for Runtime { fn best_finalized() -> Option> { BridgeWococoGrandpa::best_finalized().map(|header| header.id()) } } - impl runtime_api::BridgeHubRococoFinalityApi for Runtime { + impl bp_bridge_hub_rococo::BridgeHubRococoFinalityApi for Runtime { fn best_finalized() -> Option> { let encoded_head = BridgeRococoParachain::best_parachain_head(bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID.into())?; let head = bp_bridge_hub_rococo::Header::decode(&mut &encoded_head.0[..]).ok()?; @@ -775,7 +759,7 @@ impl_runtime_apis! { } } - impl runtime_api::BridgeHubWococoFinalityApi for Runtime { + impl bp_bridge_hub_wococo::BridgeHubWococoFinalityApi for Runtime { fn best_finalized() -> Option> { let encoded_head = BridgeWococoParachain::best_parachain_head(bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID.into())?; let head = bp_bridge_hub_wococo::Header::decode(&mut &encoded_head.0[..]).ok()?; @@ -784,7 +768,7 @@ impl_runtime_apis! { } // This exposed by BridgeHubRococo - impl runtime_api::FromBridgeHubWococoInboundLaneApi for Runtime { + impl bp_bridge_hub_wococo::FromBridgeHubWococoInboundLaneApi for Runtime { fn message_details( lane: bp_messages::LaneId, messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, @@ -796,7 +780,7 @@ impl_runtime_apis! { } } - impl runtime_api::ToBridgeHubWococoOutboundLaneApi for Runtime { + impl bp_bridge_hub_wococo::ToBridgeHubWococoOutboundLaneApi for Runtime { fn message_details( lane: bp_messages::LaneId, begin: bp_messages::MessageNonce, @@ -810,7 +794,7 @@ impl_runtime_apis! { } // This is exposed by BridgeHubWococo - impl runtime_api::FromBridgeHubRococoInboundLaneApi for Runtime { + impl bp_bridge_hub_rococo::FromBridgeHubRococoInboundLaneApi for Runtime { fn message_details( lane: bp_messages::LaneId, messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, @@ -822,7 +806,7 @@ impl_runtime_apis! { } } - impl runtime_api::ToBridgeHubRococoOutboundLaneApi for Runtime { + impl bp_bridge_hub_rococo::ToBridgeHubRococoOutboundLaneApi for Runtime { fn message_details( lane: bp_messages::LaneId, begin: bp_messages::MessageNonce, From f6e1b01beafc3009d63c2d7498520c949ad72620 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Sun, 15 Jan 2023 14:47:18 +0100 Subject: [PATCH 172/263] Patched dependencies (polkadot, substrate) for xcm-v3 (compiles + tests work) --- Cargo.lock | 525 +++++++++++++++++++++++++++-------------------------- Cargo.toml | 475 +++++++++++++++++++++++++----------------------- 2 files changed, 518 insertions(+), 482 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65cacc89553..bb7350ded59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -462,7 +462,7 @@ dependencies = [ [[package]] name = "beefy-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -496,7 +496,7 @@ dependencies = [ [[package]] name = "beefy-gadget-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "beefy-gadget", "futures", @@ -515,7 +515,7 @@ dependencies = [ [[package]] name = "beefy-merkle-tree" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "sp-api", "sp-beefy", @@ -3121,7 +3121,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", ] @@ -3144,7 +3144,7 @@ checksum = "85dcb89d2b10c5f6133de2efd8c11959ce9dbb46a2f7a4cab208c4eeda6ce1ab" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "frame-system", @@ -3167,7 +3167,7 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "Inflector", "array-bytes 4.2.0", @@ -3214,7 +3214,7 @@ dependencies = [ [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -3225,7 +3225,7 @@ dependencies = [ [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-election-provider-solution-type", "frame-support", @@ -3242,7 +3242,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "frame-system", @@ -3271,7 +3271,7 @@ dependencies = [ [[package]] name = "frame-remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "futures", "log", @@ -3287,7 +3287,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "bitflags", "frame-metadata", @@ -3319,7 +3319,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "Inflector", "cfg-expr", @@ -3333,7 +3333,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", @@ -3345,7 +3345,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "proc-macro2", "quote", @@ -3355,7 +3355,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "log", @@ -3373,7 +3373,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -3388,7 +3388,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "sp-api", @@ -3397,7 +3397,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "parity-scale-codec", @@ -4327,7 +4327,7 @@ checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" [[package]] name = "kusama-runtime" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "bitvec", "frame-benchmarking", @@ -4424,7 +4424,7 @@ dependencies = [ [[package]] name = "kusama-runtime-constants" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "frame-support", "polkadot-primitives", @@ -5250,7 +5250,7 @@ dependencies = [ [[package]] name = "mmr-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "futures", "log", @@ -5269,7 +5269,7 @@ dependencies = [ [[package]] name = "mmr-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "anyhow", "jsonrpsee", @@ -5761,7 +5761,7 @@ dependencies = [ [[package]] name = "pallet-alliance" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "array-bytes 4.2.0", "frame-benchmarking", @@ -5782,7 +5782,7 @@ dependencies = [ [[package]] name = "pallet-asset-tx-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -5800,7 +5800,7 @@ dependencies = [ [[package]] name = "pallet-assets" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -5815,7 +5815,7 @@ dependencies = [ [[package]] name = "pallet-aura" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "frame-system", @@ -5831,7 +5831,7 @@ dependencies = [ [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "frame-system", @@ -5847,7 +5847,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "frame-system", @@ -5862,7 +5862,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -5886,7 +5886,7 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5906,7 +5906,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -5921,7 +5921,7 @@ dependencies = [ [[package]] name = "pallet-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "frame-system", @@ -5937,7 +5937,7 @@ dependencies = [ [[package]] name = "pallet-beefy-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "array-bytes 4.2.0", "beefy-merkle-tree", @@ -5960,7 +5960,7 @@ dependencies = [ [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -5978,7 +5978,7 @@ dependencies = [ [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6022,7 +6022,7 @@ dependencies = [ [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6039,7 +6039,7 @@ dependencies = [ [[package]] name = "pallet-contracts" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "bitflags", "frame-benchmarking", @@ -6068,7 +6068,7 @@ dependencies = [ [[package]] name = "pallet-contracts-primitives" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "bitflags", "parity-scale-codec", @@ -6080,7 +6080,7 @@ dependencies = [ [[package]] name = "pallet-contracts-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "proc-macro2", "quote", @@ -6090,7 +6090,7 @@ dependencies = [ [[package]] name = "pallet-conviction-voting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "assert_matches", "frame-benchmarking", @@ -6107,7 +6107,7 @@ dependencies = [ [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6125,7 +6125,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6148,7 +6148,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-support-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6161,7 +6161,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6179,7 +6179,7 @@ dependencies = [ [[package]] name = "pallet-fast-unstake" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6197,7 +6197,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6220,7 +6220,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "enumflags2", "frame-benchmarking", @@ -6236,7 +6236,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6256,7 +6256,7 @@ dependencies = [ [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6273,7 +6273,7 @@ dependencies = [ [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6290,7 +6290,7 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6307,7 +6307,7 @@ dependencies = [ [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6323,7 +6323,7 @@ dependencies = [ [[package]] name = "pallet-nfts" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "enumflags2", "frame-benchmarking", @@ -6340,7 +6340,7 @@ dependencies = [ [[package]] name = "pallet-nis" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6356,7 +6356,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "frame-system", @@ -6373,7 +6373,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-benchmarking" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6393,7 +6393,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "sp-api", @@ -6403,7 +6403,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "frame-system", @@ -6420,7 +6420,7 @@ dependencies = [ [[package]] name = "pallet-offences-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6443,7 +6443,7 @@ dependencies = [ [[package]] name = "pallet-preimage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6460,7 +6460,7 @@ dependencies = [ [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6475,7 +6475,7 @@ dependencies = [ [[package]] name = "pallet-randomness-collective-flip" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "frame-system", @@ -6489,7 +6489,7 @@ dependencies = [ [[package]] name = "pallet-ranked-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6507,7 +6507,7 @@ dependencies = [ [[package]] name = "pallet-recovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6522,7 +6522,7 @@ dependencies = [ [[package]] name = "pallet-referenda" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "assert_matches", "frame-benchmarking", @@ -6541,7 +6541,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6558,7 +6558,7 @@ dependencies = [ [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "frame-system", @@ -6579,7 +6579,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6595,7 +6595,7 @@ dependencies = [ [[package]] name = "pallet-society" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "frame-system", @@ -6609,7 +6609,7 @@ dependencies = [ [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6632,7 +6632,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -6643,7 +6643,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "log", "sp-arithmetic", @@ -6652,7 +6652,7 @@ dependencies = [ [[package]] name = "pallet-state-trie-migration" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6669,7 +6669,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "frame-system", @@ -6698,7 +6698,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6716,7 +6716,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6735,7 +6735,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-support", "frame-system", @@ -6751,7 +6751,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", @@ -6767,7 +6767,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -6779,7 +6779,7 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6796,7 +6796,7 @@ dependencies = [ [[package]] name = "pallet-uniques" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6811,7 +6811,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6827,7 +6827,7 @@ dependencies = [ [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6842,7 +6842,7 @@ dependencies = [ [[package]] name = "pallet-whitelist" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-benchmarking", "frame-support", @@ -6857,8 +6857,9 @@ dependencies = [ [[package]] name = "pallet-xcm" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "log", @@ -6866,6 +6867,7 @@ dependencies = [ "scale-info", "serde", "sp-core", + "sp-io", "sp-runtime", "sp-std", "xcm", @@ -6875,7 +6877,7 @@ dependencies = [ [[package]] name = "pallet-xcm-benchmarks" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "frame-benchmarking", "frame-support", @@ -6883,9 +6885,11 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", + "sp-io", "sp-runtime", "sp-std", "xcm", + "xcm-builder", "xcm-executor", ] @@ -7409,7 +7413,7 @@ dependencies = [ [[package]] name = "polkadot-approval-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "futures", "polkadot-node-metrics", @@ -7424,7 +7428,7 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "futures", "polkadot-node-network-protocol", @@ -7438,7 +7442,7 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "derive_more", "fatality", @@ -7461,7 +7465,7 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "fatality", "futures", @@ -7482,7 +7486,7 @@ dependencies = [ [[package]] name = "polkadot-cli" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "clap 4.0.32", "frame-benchmarking-cli", @@ -7509,7 +7513,7 @@ dependencies = [ [[package]] name = "polkadot-client" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "async-trait", "frame-benchmarking", @@ -7552,7 +7556,7 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "always-assert", "bitvec", @@ -7574,7 +7578,7 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "parity-scale-codec", "scale-info", @@ -7586,7 +7590,7 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "derive_more", "fatality", @@ -7611,7 +7615,7 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", @@ -7625,7 +7629,7 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "futures", "futures-timer", @@ -7645,7 +7649,7 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "always-assert", "async-trait", @@ -7669,7 +7673,7 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "futures", "parity-scale-codec", @@ -7687,7 +7691,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "bitvec", "derive_more", @@ -7716,7 +7720,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "bitvec", "futures", @@ -7736,7 +7740,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "bitvec", "fatality", @@ -7755,7 +7759,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "futures", "polkadot-node-subsystem", @@ -7770,7 +7774,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "async-trait", "futures", @@ -7789,7 +7793,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "futures", "polkadot-node-metrics", @@ -7804,7 +7808,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "futures", "futures-timer", @@ -7821,7 +7825,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "fatality", "futures", @@ -7840,7 +7844,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "async-trait", "futures", @@ -7857,7 +7861,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "bitvec", "fatality", @@ -7875,7 +7879,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "always-assert", "assert_matches", @@ -7907,7 +7911,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-checker" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "futures", "polkadot-node-primitives", @@ -7923,7 +7927,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "futures", "lru", @@ -7938,7 +7942,7 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "lazy_static", "log", @@ -7956,7 +7960,7 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "bs58", "futures", @@ -7975,7 +7979,7 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "async-trait", "derive_more", @@ -7998,7 +8002,7 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "bounded-vec", "futures", @@ -8020,7 +8024,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -8030,7 +8034,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-test-helpers" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "async-trait", "futures", @@ -8048,7 +8052,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "async-trait", "derive_more", @@ -8071,7 +8075,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "async-trait", "derive_more", @@ -8104,7 +8108,7 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "async-trait", "futures", @@ -8127,7 +8131,7 @@ dependencies = [ [[package]] name = "polkadot-parachain" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "derive_more", "frame-support", @@ -8224,7 +8228,7 @@ dependencies = [ [[package]] name = "polkadot-performance-test" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "env_logger 0.9.0", "kusama-runtime", @@ -8239,7 +8243,7 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "bitvec", "hex-literal", @@ -8265,7 +8269,7 @@ dependencies = [ [[package]] name = "polkadot-rpc" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", @@ -8297,7 +8301,7 @@ dependencies = [ [[package]] name = "polkadot-runtime" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "bitvec", "frame-benchmarking", @@ -8386,7 +8390,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "bitvec", "frame-benchmarking", @@ -8434,7 +8438,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-constants" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "frame-support", "polkadot-primitives", @@ -8448,7 +8452,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-metrics" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "bs58", "parity-scale-codec", @@ -8460,7 +8464,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "bitflags", "bitvec", @@ -8503,7 +8507,7 @@ dependencies = [ [[package]] name = "polkadot-service" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "async-trait", "beefy-gadget", @@ -8610,7 +8614,7 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "arrayvec 0.5.2", "fatality", @@ -8631,7 +8635,7 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -8641,7 +8645,7 @@ dependencies = [ [[package]] name = "polkadot-test-client" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "parity-scale-codec", "polkadot-node-subsystem", @@ -8666,7 +8670,7 @@ dependencies = [ [[package]] name = "polkadot-test-runtime" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "bitvec", "frame-election-provider-support", @@ -8727,7 +8731,7 @@ dependencies = [ [[package]] name = "polkadot-test-service" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "frame-benchmarking", "frame-system", @@ -9453,7 +9457,7 @@ dependencies = [ [[package]] name = "rococo-runtime" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "beefy-merkle-tree", "frame-benchmarking", @@ -9539,7 +9543,7 @@ dependencies = [ [[package]] name = "rococo-runtime-constants" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "frame-support", "polkadot-primitives", @@ -9763,7 +9767,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "log", "sp-core", @@ -9774,7 +9778,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "futures", @@ -9801,7 +9805,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "futures", "futures-timer", @@ -9824,7 +9828,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -9840,7 +9844,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "memmap2", "sc-chain-spec-derive", @@ -9855,7 +9859,7 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -9866,7 +9870,7 @@ dependencies = [ [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "array-bytes 4.2.0", "chrono", @@ -9906,7 +9910,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "fnv", "futures", @@ -9932,7 +9936,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "hash-db", "kvdb", @@ -9957,7 +9961,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "futures", @@ -9982,7 +9986,7 @@ dependencies = [ [[package]] name = "sc-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "futures", @@ -10011,7 +10015,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "fork-tree", @@ -10049,7 +10053,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "futures", "jsonrpsee", @@ -10071,7 +10075,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "fork-tree", "parity-scale-codec", @@ -10084,7 +10088,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "futures", @@ -10107,7 +10111,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "lru", "parity-scale-codec", @@ -10131,7 +10135,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", @@ -10144,7 +10148,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "log", "sc-allocator", @@ -10157,7 +10161,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "cfg-if", "libc", @@ -10174,7 +10178,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "ahash", "array-bytes 4.2.0", @@ -10214,7 +10218,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "finality-grandpa", "futures", @@ -10234,7 +10238,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "ansi_term", "futures", @@ -10249,7 +10253,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10264,7 +10268,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10306,7 +10310,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "cid", "futures", @@ -10325,7 +10329,7 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "bitflags", @@ -10351,7 +10355,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "ahash", "futures", @@ -10369,7 +10373,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10390,7 +10394,7 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10422,7 +10426,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10441,7 +10445,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "array-bytes 4.2.0", "bytes", @@ -10471,7 +10475,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "futures", "libp2p", @@ -10484,7 +10488,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -10493,7 +10497,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "futures", "jsonrpsee", @@ -10522,7 +10526,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -10541,7 +10545,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "http", "jsonrpsee", @@ -10556,7 +10560,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10582,7 +10586,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "directories", @@ -10647,7 +10651,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "log", "parity-scale-codec", @@ -10658,7 +10662,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -10677,7 +10681,7 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "futures", "libc", @@ -10696,7 +10700,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "chrono", "futures", @@ -10715,7 +10719,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "ansi_term", "atty", @@ -10746,7 +10750,7 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -10757,7 +10761,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "futures", @@ -10783,7 +10787,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "futures", @@ -10797,7 +10801,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "backtrace", "futures", @@ -11226,7 +11230,7 @@ checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" [[package]] name = "slot-range-helper" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "enumn", "parity-scale-codec", @@ -11303,7 +11307,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "hash-db", "log", @@ -11321,7 +11325,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "blake2", "proc-macro-crate", @@ -11333,7 +11337,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "scale-info", @@ -11346,7 +11350,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "integer-sqrt", "num-traits", @@ -11360,7 +11364,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "scale-info", @@ -11373,7 +11377,7 @@ dependencies = [ [[package]] name = "sp-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "parity-scale-codec", @@ -11385,7 +11389,7 @@ dependencies = [ [[package]] name = "sp-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "scale-info", @@ -11402,7 +11406,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "sp-api", @@ -11414,7 +11418,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "futures", "log", @@ -11432,7 +11436,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "futures", @@ -11450,7 +11454,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "parity-scale-codec", @@ -11468,7 +11472,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "merlin", @@ -11491,7 +11495,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "scale-info", @@ -11503,7 +11507,7 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "scale-info", @@ -11516,7 +11520,7 @@ dependencies = [ [[package]] name = "sp-core" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "array-bytes 4.2.0", "base58", @@ -11558,7 +11562,7 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "blake2", "byteorder", @@ -11572,7 +11576,7 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "proc-macro2", "quote", @@ -11583,7 +11587,7 @@ dependencies = [ [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -11592,7 +11596,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "proc-macro2", "quote", @@ -11602,7 +11606,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "environmental", "parity-scale-codec", @@ -11613,7 +11617,7 @@ dependencies = [ [[package]] name = "sp-finality-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "finality-grandpa", "log", @@ -11631,7 +11635,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -11645,7 +11649,7 @@ dependencies = [ [[package]] name = "sp-io" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "bytes", "ed25519", @@ -11670,7 +11674,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "lazy_static", "sp-core", @@ -11681,7 +11685,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "futures", @@ -11698,7 +11702,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "thiserror", "zstd", @@ -11707,7 +11711,7 @@ dependencies = [ [[package]] name = "sp-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "ckb-merkle-mountain-range", "log", @@ -11725,7 +11729,7 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "scale-info", @@ -11739,7 +11743,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "sp-api", "sp-core", @@ -11749,7 +11753,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "backtrace", "lazy_static", @@ -11759,7 +11763,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "rustc-hash", "serde", @@ -11769,7 +11773,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "either", "hash256-std-hasher", @@ -11791,7 +11795,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -11809,7 +11813,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "Inflector", "proc-macro-crate", @@ -11821,7 +11825,7 @@ dependencies = [ [[package]] name = "sp-serializer" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "serde", "serde_json", @@ -11830,7 +11834,7 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "scale-info", @@ -11844,7 +11848,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "scale-info", @@ -11856,7 +11860,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "hash-db", "log", @@ -11876,12 +11880,12 @@ dependencies = [ [[package]] name = "sp-std" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" [[package]] name = "sp-storage" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "impl-serde", "parity-scale-codec", @@ -11894,7 +11898,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "futures-timer", @@ -11909,7 +11913,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "sp-std", @@ -11921,7 +11925,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "sp-api", "sp-runtime", @@ -11930,7 +11934,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "log", @@ -11946,7 +11950,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "ahash", "hash-db", @@ -11969,7 +11973,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "impl-serde", "parity-scale-codec", @@ -11986,7 +11990,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -11997,7 +12001,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "impl-trait-for-tuples", "log", @@ -12010,7 +12014,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "parity-scale-codec", "scale-info", @@ -12331,7 +12335,7 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "platforms", ] @@ -12339,7 +12343,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "frame-system-rpc-runtime-api", "futures", @@ -12358,7 +12362,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "hyper", "log", @@ -12370,7 +12374,7 @@ dependencies = [ [[package]] name = "substrate-rpc-client" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "async-trait", "jsonrpsee", @@ -12383,7 +12387,7 @@ dependencies = [ [[package]] name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "jsonrpsee", "log", @@ -12402,7 +12406,7 @@ dependencies = [ [[package]] name = "substrate-test-client" version = "2.0.1" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -12428,7 +12432,7 @@ dependencies = [ [[package]] name = "substrate-test-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "futures", "substrate-test-utils-derive", @@ -12438,7 +12442,7 @@ dependencies = [ [[package]] name = "substrate-test-utils-derive" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -12449,7 +12453,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "ansi_term", "build-helper", @@ -12566,7 +12570,7 @@ checksum = "13a4ec180a2de59b57434704ccfad967f789b12737738798fa08798cd5824c16" [[package]] name = "test-runtime-constants" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "frame-support", "polkadot-primitives", @@ -12923,7 +12927,7 @@ dependencies = [ [[package]] name = "tracing-gum" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "polkadot-node-jaeger", "polkadot-primitives", @@ -12934,7 +12938,7 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "expander 0.0.6", "proc-macro-crate", @@ -13064,7 +13068,7 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#946507ba9ef13e263534176b7b74e26fc56efbd4" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" dependencies = [ "clap 4.0.32", "frame-remote-externalities", @@ -13974,7 +13978,7 @@ dependencies = [ [[package]] name = "westend-runtime" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "bitvec", "frame-benchmarking", @@ -14018,6 +14022,7 @@ dependencies = [ "pallet-society", "pallet-staking", "pallet-staking-reward-curve", + "pallet-state-trie-migration", "pallet-sudo", "pallet-timestamp", "pallet-transaction-payment", @@ -14064,7 +14069,7 @@ dependencies = [ [[package]] name = "westend-runtime-constants" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "frame-support", "polkadot-primitives", @@ -14455,24 +14460,27 @@ dependencies = [ [[package]] name = "xcm" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "derivative", "impl-trait-for-tuples", "log", "parity-scale-codec", "scale-info", - "sp-runtime", + "serde", + "sp-core", + "sp-weights", "xcm-procedural", ] [[package]] name = "xcm-builder" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "frame-support", "frame-system", + "impl-trait-for-tuples", "log", "pallet-transaction-payment", "parity-scale-codec", @@ -14489,8 +14497,9 @@ dependencies = [ [[package]] name = "xcm-executor" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ + "environmental", "frame-benchmarking", "frame-support", "impl-trait-for-tuples", @@ -14501,13 +14510,14 @@ dependencies = [ "sp-io", "sp-runtime", "sp-std", + "sp-weights", "xcm", ] [[package]] name = "xcm-procedural" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#dfa8c5ae321e0523f88fdc43afc6d743812ee49e" +source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges-2#b85e12f0bea43ded9e853a3aeae33cfa71387823" dependencies = [ "Inflector", "proc-macro2", @@ -14587,3 +14597,8 @@ dependencies = [ "cc", "libc", ] + +[[patch.unused]] +name = "node-inspect" +version = "0.9.0-dev" +source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges-2#4e383428ffc6d4eba173ca257c1aaf53d1ec339c" diff --git a/Cargo.toml b/Cargo.toml index d6aaeee87fc..5ae1c3c699c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,232 +66,253 @@ codegen-units = 1 # Once XCMv3 PR is merged, we may remove both Substrate and Polkadot patch section. [patch."https://github.com/paritytech/substrate"] -beefy-gadget = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -beefy-gadget-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -beefy-merkle-tree = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -beefy-primitives = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -fork-tree = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-benchmarking-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-election-provider-solution-type = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-election-provider-support = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-executive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-support = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-support-procedural = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-support-procedural-tools = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-support-procedural-tools-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-system = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-system-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-system-rpc-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-try-runtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -node-inspect = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-bags-list = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-balances = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-beefy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-beefy-mmr = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-bounties = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-child-bounties = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-collective = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-contracts = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-contracts-primitives = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-conviction-voting = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-democracy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-election-provider-multi-phase = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-elections-phragmen = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-fast-unstake = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-gilt = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-identity = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-im-online = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-indices = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-membership = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-mmr = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-mmr-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-multisig = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-nomination-pools = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-nomination-pools-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-offences = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-preimage = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-proxy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-randomness-collective-flip = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-ranked-collective = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-recovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-referenda = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-scheduler = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-session = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-society = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-staking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-staking-reward-curve = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-staking-reward-fn = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-sudo = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-timestamp = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-tips = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-transaction-payment = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-transaction-payment-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-treasury = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-utility = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-uniques = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-vesting = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-whitelist = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -remote-externalities = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-allocator = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-basic-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-block-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-chain-spec = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-chain-spec-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-client-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-client-db = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-consensus = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-consensus-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-consensus-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-consensus-babe-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-consensus-epochs = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-consensus-slots = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-executor = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-executor-common = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-executor-wasmi = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-executor-wasmtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-finality-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-finality-grandpa-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-informant = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-keystore = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-network = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-network-common = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-network-gossip = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-network-light = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-network-sync = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-offchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-peerset = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-proposer-metrics = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-rpc-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-rpc-server = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-service = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-state-db = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-sync-state-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-sysinfo = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-telemetry = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-tracing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-tracing-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-transaction-pool = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-transaction-pool-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-utils = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-api-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-application-crypto = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-arithmetic = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-block-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-blockchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-consensus = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-consensus-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-consensus-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-consensus-slots = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-consensus-vrf = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-core = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-core-hashing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-core-hashing-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-database = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-debug-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-externalities = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-finality-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-inherents = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-io = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-keyring = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-keystore = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-maybe-compressed-blob = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-mmr-primitives = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-npos-elections = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-offchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-panic-handler = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-runtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-runtime-interface = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-runtime-interface-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-session = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-staking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-state-machine = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-std = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-storage = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-tasks = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-timestamp = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-tracing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-transaction-pool = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-transaction-storage-proof = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-trie = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-version = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-version-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-wasm-interface = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -substrate-build-script-utils = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -substrate-frame-rpc-system = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -substrate-prometheus-endpoint = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -substrate-state-trie-migration-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -substrate-wasm-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -try-runtime-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } +beefy-gadget = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +beefy-gadget-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +beefy-merkle-tree = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +fork-tree = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +frame-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +frame-benchmarking-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +frame-election-provider-solution-type = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +frame-election-provider-support = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +frame-executive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +frame-support = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +frame-support-procedural = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +frame-support-procedural-tools = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +frame-support-procedural-tools-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +frame-system = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +frame-system-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +frame-try-runtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +mmr-gadget = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +mmr-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +node-inspect = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-assets = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-asset-tx-payment = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-bags-list = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-balances = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-beefy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-beefy-mmr = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-bounties = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-child-bounties = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-collective = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-conviction-voting = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-democracy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-election-provider-multi-phase = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-elections-phragmen = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-fast-unstake = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-identity = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-im-online = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-indices = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-membership = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-mmr = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-multisig = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-nis = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-nomination-pools = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-nomination-pools-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-offences = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-preimage = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-proxy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-randomness-collective-flip = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-ranked-collective = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-recovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-referenda = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-scheduler = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-session = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-society = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-staking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-staking-reward-curve = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-staking-reward-fn = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-sudo = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-timestamp = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-tips = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-transaction-payment = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-transaction-payment-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-treasury = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-utility = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-vesting = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-whitelist = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-allocator = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-basic-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-block-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-chain-spec = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-chain-spec-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-client-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-client-db = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-consensus = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-consensus-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-consensus-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-consensus-babe-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-consensus-epochs = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-consensus-slots = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-executor = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-executor-common = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-executor-wasmi = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-executor-wasmtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-finality-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-finality-grandpa-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-informant = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-keystore = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-network = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-network-common = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-network-gossip = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-network-light = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-network-sync = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-offchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-peerset = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-proposer-metrics = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-rpc-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-rpc-server = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-service = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-state-db = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-sync-state-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-sysinfo = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-telemetry = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-tracing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-tracing-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-transaction-pool = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-transaction-pool-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sc-utils = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-api-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-application-crypto = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-arithmetic = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-beefy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-block-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-blockchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-consensus = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-consensus-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-consensus-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-consensus-slots = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-consensus-vrf = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-core = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-core-hashing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-core-hashing-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-database = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-debug-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-externalities = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-finality-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-inherents = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-io = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-keyring = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-keystore = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-maybe-compressed-blob = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-mmr-primitives = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-npos-elections = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-offchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-panic-handler = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-runtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-runtime-interface = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-runtime-interface-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-session = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-staking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-state-machine = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-std = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-storage = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-timestamp = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-tracing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-transaction-pool = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-transaction-storage-proof = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-trie = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-version = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-version-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-wasm-interface = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-weights = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +substrate-build-script-utils = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +substrate-frame-rpc-system = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +substrate-prometheus-endpoint = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +substrate-state-trie-migration-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +substrate-wasm-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +try-runtime-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-uniques = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-contracts = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-contracts-primitives = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-contracts-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-alliance = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-nfts = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-state-trie-migration = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +sp-serializer = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +substrate-test-client = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +substrate-test-utils-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +substrate-test-utils = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-election-provider-support-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-nomination-pools-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-offences-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } +pallet-session-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges-2" } [patch."https://github.com/paritytech/polkadot"] -kusama-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -kusama-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -pallet-xcm = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-approval-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-availability-bitfield-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-availability-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-availability-recovery = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-cli = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-client = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-collator-protocol = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-core-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-dispute-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-erasure-coding = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-gossip-support = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-network-bridge = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-collation-generation = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-approval-voting = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-av-store = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-backing = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-bitfield-signing = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-candidate-validation = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-chain-api = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-chain-selection = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-dispute-coordinator = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-parachains-inherent = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-provisioner = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-pvf = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-pvf-checker = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-runtime-api = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-jaeger = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-metrics = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-network-protocol = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-subsystem = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-subsystem-types = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-subsystem-util = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-overseer = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-parachain = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-performance-test = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-rpc = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-runtime-common = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-runtime-metrics = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-runtime-parachains = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-service = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-statement-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-statement-table = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-test-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -slot-range-helper = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -tracing-gum = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -tracing-gum-proc-macro = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -xcm = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -xcm-builder = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -xcm-executor = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -xcm-procedural = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } +kusama-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +kusama-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +pallet-xcm = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-approval-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-availability-bitfield-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-availability-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-availability-recovery = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-cli = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-client = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-collator-protocol = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-core-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-dispute-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-erasure-coding = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-gossip-support = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-network-bridge = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-collation-generation = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-core-approval-voting = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-core-av-store = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-core-backing = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-core-bitfield-signing = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-core-candidate-validation = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-core-chain-api = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-core-chain-selection = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-core-dispute-coordinator = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-core-parachains-inherent = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-core-provisioner = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-core-pvf = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-core-pvf-checker = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-core-runtime-api = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-jaeger = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-metrics = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-network-protocol = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-subsystem = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-subsystem-types = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-subsystem-util = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-overseer = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-parachain = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-performance-test = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-rpc = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-runtime-common = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-runtime-metrics = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-runtime-parachains = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-service = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-statement-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-statement-table = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +slot-range-helper = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +tracing-gum = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +tracing-gum-proc-macro = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +xcm = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +xcm-builder = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +xcm-executor = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +xcm-procedural = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +pallet-xcm-benchmarks = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +westend-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +rococo-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-test-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +test-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-node-subsystem-test-helpers = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-test-client = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } +polkadot-test-service = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges-2" } From 6419dade97183968d576dd0f8055fbb427fd8ec0 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 16 Jan 2023 12:16:31 +0100 Subject: [PATCH 173/263] Replace serial_test and fix with thread_local --- Cargo.lock | 40 ------------------- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 1 - .../bridge-hub-rococo/tests/tests.rs | 3 -- .../bridge-hubs/test-utils/src/lib.rs | 11 ++--- 4 files changed, 6 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 291ad7b88da..7b052902709 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1007,7 +1007,6 @@ dependencies = [ "polkadot-runtime-constants", "scale-info", "serde", - "serial_test", "smallvec", "sp-api", "sp-block-builder", @@ -2673,19 +2672,6 @@ dependencies = [ "syn", ] -[[package]] -name = "dashmap" -version = "5.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" -dependencies = [ - "cfg-if 1.0.0", - "hashbrown", - "lock_api", - "once_cell", - "parking_lot_core 0.9.3", -] - [[package]] name = "data-encoding" version = "2.3.2" @@ -11112,32 +11098,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serial_test" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92761393ee4dc3ff8f4af487bd58f4307c9329bbedea02cac0089ad9c411e153" -dependencies = [ - "dashmap", - "futures", - "lazy_static", - "log", - "parking_lot 0.12.1", - "serial_test_derive", -] - -[[package]] -name = "serial_test_derive" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6f5d1c3087fb119617cff2966fe3808a80e5eb59a8c1601d5994d66f4346a5" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "sha-1" version = "0.9.8" diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 195bbd62bd3..ab2a920e4c1 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -82,7 +82,6 @@ pallet-bridge-relayers = { path = "../../../../bridges/modules/relayers", defaul bridge-runtime-common = { path = "../../../../bridges/bin/runtime-common", default-features = false } [dev-dependencies] -serial_test = "0.9.0" bridge-hub-test-utils = { path = "../test-utils"} [features] diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index 11d3fb9681d..0fda693cc5d 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -18,7 +18,6 @@ use bp_messages::target_chain::MessageDispatch; use bp_runtime::messages::MessageDispatchResult; use bridge_hub_rococo_runtime::bridge_common_config::XcmBlobMessageDispatchResult; pub use bridge_hub_rococo_runtime::{ - runtime_api, xcm_config::{XcmConfig, XcmRouter}, Runtime, *, }; @@ -43,7 +42,6 @@ fn execute_on_runtime( } #[test] -#[serial_test::serial] fn test_bridge_hub_wococo_dispatch_blob_and_xcm_routing_works() { let universal_source_as_senders = vec![X1(GlobalConsensus(Rococo)), X2(GlobalConsensus(Rococo), Parachain(1000))]; @@ -113,7 +111,6 @@ fn test_bridge_hub_wococo_dispatch_blob_and_xcm_routing_works() { } #[test] -#[serial_test::serial] fn test_bridge_hub_rococo_dispatch_blob_and_xcm_routing_works() { let universal_source_as_senders = vec![X1(GlobalConsensus(Wococo)), X2(GlobalConsensus(Wococo), Parachain(1000))]; diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs index 568ea39b573..ec438829da3 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs @@ -53,13 +53,14 @@ pub fn dummy_account() -> AccountId { /// Macro used for simulate_export_message and capturing bytes macro_rules! grab_haul_blob ( ($name:ident, $grabbed_payload:ident) => { - static mut $grabbed_payload: Option> = None; + std::thread_local! { + static $grabbed_payload: std::cell::RefCell>> = std::cell::RefCell::new(None); + } + struct $name; impl HaulBlob for $name { fn haul_blob(blob: Vec) { - unsafe { - $grabbed_payload = Some(blob); - } + $grabbed_payload.with(|rm| *rm.borrow_mut() = Some(blob)); } } } @@ -91,7 +92,7 @@ pub fn simulate_export_message>( .expect("deliver error"); println!("[MessageExporter::deliver] {:?}", result); - unsafe { GRABBED_HAUL_BLOB_PAYLOAD.as_ref().unwrap().clone() } + GRABBED_HAUL_BLOB_PAYLOAD.with(|r| r.take().expect("xcm::ExportMessage should be here")) } /// Initialize runtime/externalities From 97b56a94875d8797424b55afe1fdb1abcf95feee Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 18 Jan 2023 00:37:43 +0100 Subject: [PATCH 174/263] Very init of script for bumping bridges repo --- bridges/primitives/beefy/Cargo.toml | 2 +- scripts/bridges_update_subtree.sh | 60 +++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100755 scripts/bridges_update_subtree.sh diff --git a/bridges/primitives/beefy/Cargo.toml b/bridges/primitives/beefy/Cargo.toml index 0c0d3f6b2ed..769fb7e591b 100644 --- a/bridges/primitives/beefy/Cargo.toml +++ b/bridges/primitives/beefy/Cargo.toml @@ -32,7 +32,6 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", d [features] default = ["std"] std = [ - "sp-beefy/std", "bp-runtime/std", "codec/std", "frame-support/std", @@ -43,6 +42,7 @@ std = [ "sp-application-crypto/std", "sp-core/std", "sp-io/std", + "sp-beefy/std", "sp-runtime/std", "sp-std/std" ] diff --git a/scripts/bridges_update_subtree.sh b/scripts/bridges_update_subtree.sh new file mode 100755 index 00000000000..0cfa40f36f2 --- /dev/null +++ b/scripts/bridges_update_subtree.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +function fetch() { + # TODO: check if already existing remote bridges + # if not add one: git remote add -f bridges git@github.com:paritytech/parity-bridges-common.git + + BRIDGES_BRANCH="${BRANCH:-master}" + echo "Syncing with branch: '$BRIDGES_BRANCH'" + + # rm -R bridges + # git add --all + # echo "... check YubiKey" + # git commit -S -m "updating bridges subtree" + # echo "... check YubiKey" + #git subtree add --prefix=bridges bridges $BRIDGES_BRANCH --squash + + # OR + + echo "... check YubiKey" + git fetch bridges --prune + git subtree pull --prefix=bridges bridges $BRIDGES_BRANCH --squash + echo ".. if there are any conflict, please, resolve and then 'git merge --continue'" +} + +function remove() { + # remove unneded stuff + rm -R bridges/.config + rm -R bridges/deployments + rm -R bridges/docs + rm -R bridges/fuzz + rm -R bridges/.github + rm -R bridges/.maintain + rm -R bridges/relays + rm -R bridges/scripts + rm -R bridges/bin/millau/node + rm -R bridges/bin/rialto + rm -R bridges/bin/rialto-parachain + rm -R bridges/bin/.keep + + # remove all file from top directory + find ./bridges -maxdepth 1 -type f -exec rm "{}" \; + + + git add --all + # TODO: add some specific message + git commit --amend -S -m "updating bridges subtree" +} + +case "$1" in + fetch) + fetch + ;; + remove) + remove + ;; + all) + fetch + remove + ;; +esac From d3c0763718f035bc7dfaac59a791a1b4edf653d4 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 18 Jan 2023 01:20:48 +0100 Subject: [PATCH 175/263] Squashed 'bridges/' changes from 062554430..984749ba0 984749ba0 Define separate signed extension for BHR/BHW (#1776) 72b03d463 update Substrate/Polkadot/Cumulus deps to master (#1775) 3065c7903 Added crate-level docs for the parachains pallet (#1772) a0f41b2d8 added/updated pallet level docs to grandpa and messages pallets (#1771) 6d69d1f4d docs: add Security Policy doc (#1770) ff8c0f727 Fix cargo deny issues (#1769) 6fc931d07 Bump xcm-v3 + substrate (#1767) 5840197c3 Define method for checking message lane weights (#1766) 881af0219 increase MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX and MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX for RBH/WBH (#1765) 41d91e961 fixed receive_delivery_proof_for_two_messages_by_two_relayers (#1764) ac0cf7b78 Fix some cargo-deny issues (#1763) 6d9dc6367 `cargo machete` removed unused deps (#1761) c265b5430 Remove sp-version dependency from bin-substrate (#1758) 1327c9d97 Set `R/WococoBridgeHub` bundle runtime version (#1756) a3a2a06ae Expose relay version metric (#1750) 13f4a0164 Use indirect runtime calls for RialtoParachain (#1753) 9563f9eee fix nightly clippy again (#1752) 21b75b893 no-grafana-startup-delay option in run.sh (#1751) a5fe0dfc4 Remove TODO (#1749) 6c826a6c3 Deduplicate pallet call structs used for indirect runtime calls (#1744) e575269e5 fix nightly clippy (#1746) 209cba353 Update project level docs (#1734) b05cef5b4 Improve relayer initialization behaviour (#1743) c7b6bae9d Make debug display of LaneId compatible with its previous version (#1740) 221e4e80c Remove CliChain::KeyPair (#1741) 3d9d2907f Use TypedLaneId instead of LaneId (#1738) 6683b8136 Simplify read_client_state() (#1739) 3f7353b82 Expose metrics of on-demand relay chain headers sync from with-parachain complex relays (#1737) ab65d84e0 Handle `SIGTERM` for the docker containers + relay (#1735) b9050e90c Replace `BATCH_CALL_SUPPORTED` (#1733) c28b3ff66 Updated db weights and some experiments (#1732) 023689c6c Do not require new headers if lane is empty (#1725) bddf1fa19 remove messages pallet owner relay argument (#1728) ef55226c6 more traces + remove signer override (#1727) 4d50df6ed remove BatchDeliveryTransaction::new and BatchConfirmationTransaction::new to avoid expects (#1726) 15244e53e Batch transactions in complex relays (#1669) c209bb9ac fix pallet names at bridge hubs (#1722) 036e6696e tests (#1720) 3d56e2089 Check origin? (#1718) af9abbeb8 Remove SOURCE_PARACHAIN_PARA_ID (#1716) d1cb5d1a8 fix parachains benchmarks (#1717) 84bdf864b Changed docker image name for substrate-relay (#1714) 5698fb465 Remove WeightToFee (#1713) 9f4106bc1 Fix bridge hub rococo/wococo weights (#1712) 114b1502f Only store header state root (pallet-bridge-parachains) (#1701) 92e86f07b New relayer rewards scheme integration (#1652) 8649d12af Signed extension to refund relayer at the target chain (#1657) ec6bafaf0 DeliveryConfirmationPayments trait for paying relayer rewards at the source chain (#1653) fab2344f4 only store header state root (pallet-bridge-grandpa) (#1699) b5e916f64 fixed benchmarks of relayers pallet (#1700) 5cce3e86d fix clippy (#1698) f78e8867b removed MintReward and added PayLaneRewardFromAccount (#1693) 0c19db305 added version guards to RBH<>WBH GRANDPA finality (and complex) relay (#1697) c003b951d removed ESTIMATE_MESSAGE_FEE_METHOD (#1696) 4903b7929 refund_pay_dispatch_fee removed (#1695) 61c3b22ca Replace const parameters types (#1691) git-subtree-dir: bridges git-subtree-split: 984749ba021b5b8ec16f65cd1e50b234640d838b --- .gitlab-ci.yml | 16 +- Cargo.lock | 5119 ++++++++++------- Cargo.toml | 252 - README.md | 68 +- SECURITY.md | 14 + bin/millau/node/Cargo.toml | 9 +- bin/millau/node/src/chain_spec.rs | 2 +- bin/millau/node/src/service.rs | 9 +- bin/millau/runtime/Cargo.toml | 11 +- bin/millau/runtime/src/lib.rs | 239 +- bin/millau/runtime/src/rialto_messages.rs | 35 +- .../runtime/src/rialto_parachain_messages.rs | 2 +- bin/millau/runtime/src/xcm_config.rs | 24 +- bin/rialto-parachain/node/Cargo.toml | 8 +- bin/rialto-parachain/node/src/cli.rs | 2 + bin/rialto-parachain/node/src/command.rs | 1 - bin/rialto-parachain/node/src/service.rs | 202 +- bin/rialto-parachain/runtime/Cargo.toml | 2 - bin/rialto-parachain/runtime/src/lib.rs | 63 +- .../runtime/src/millau_messages.rs | 2 +- bin/rialto/node/Cargo.toml | 4 +- bin/rialto/node/src/chain_spec.rs | 2 +- bin/rialto/runtime/Cargo.toml | 8 +- bin/rialto/runtime/src/lib.rs | 157 +- bin/rialto/runtime/src/millau_messages.rs | 34 +- bin/rialto/runtime/src/parachains.rs | 6 +- bin/rialto/runtime/src/xcm_config.rs | 28 +- bin/runtime-common/Cargo.toml | 8 + bin/runtime-common/src/integrity.rs | 34 +- bin/runtime-common/src/lib.rs | 3 +- bin/runtime-common/src/messages.rs | 45 +- bin/runtime-common/src/messages_extension.rs | 8 +- .../src/refund_relayer_extension.rs | 926 +++ deny.toml | 16 +- deployments/README.md | 41 +- .../bridges/common/generate_messages.sh | 4 +- .../rialto-millau-maintenance-dashboard.json | 12 - .../relay-millau-rialto-entrypoint.sh | 2 - ...arachain-millau-maintenance-dashboard.json | 12 - ...rialto-parachain-resubmitter-entrypoint.sh | 2 +- ...elay-millau-rialto-parachain-entrypoint.sh | 5 +- deployments/monitoring/docker-compose.yml | 4 +- .../rialto-chainspec-exporter-entrypoint.sh | 4 +- .../rialto-parachain-registrar-entrypoint.sh | 2 +- deployments/run.sh | 7 + deployments/ui/docker-compose.yml | 1 + docs/complex-relay.html | 85 + docs/grandpa-finality-relay.html | 47 + docs/high-level-overview.md | 256 +- docs/high-level.html | 55 - docs/messages-relay.html | 78 + docs/parachains-finality-relay.html | 55 + docs/plan.md | 22 - docs/polkadot-kusama-bridge-overview.md | 132 + docs/polkadot-kusama-bridge.html | 67 + docs/scenario1.html | 47 - docs/send-message.md | 131 - docs/testing-scenarios.md | 221 - modules/beefy/Cargo.toml | 6 +- modules/beefy/src/lib.rs | 6 +- modules/beefy/src/mock.rs | 10 +- modules/beefy/src/mock_chain.rs | 6 +- modules/beefy/src/utils.rs | 18 +- modules/grandpa/README.md | 101 + modules/grandpa/src/extension.rs | 5 +- modules/grandpa/src/lib.rs | 199 +- modules/grandpa/src/mock.rs | 7 +- modules/grandpa/src/weights.rs | 18 +- modules/messages/README.md | 286 +- modules/messages/src/benchmarking.rs | 39 +- modules/messages/src/lib.rs | 205 +- modules/messages/src/mock.rs | 72 +- modules/messages/src/weights.rs | 45 +- modules/messages/src/weights_ext.rs | 27 +- modules/parachains/Cargo.toml | 1 + modules/parachains/README.md | 90 + modules/parachains/src/benchmarking.rs | 16 +- modules/parachains/src/lib.rs | 268 +- modules/parachains/src/mock.rs | 125 +- modules/parachains/src/weights.rs | 30 +- modules/relayers/Cargo.toml | 4 +- modules/relayers/src/benchmarking.rs | 16 +- modules/relayers/src/lib.rs | 40 +- modules/relayers/src/mock.rs | 35 +- modules/relayers/src/payment_adapter.rs | 34 +- modules/relayers/src/weights.rs | 6 +- modules/shift-session-manager/src/lib.rs | 3 +- primitives/beefy/Cargo.toml | 11 +- primitives/beefy/src/lib.rs | 39 +- .../chain-bridge-hub-cumulus/Cargo.toml | 33 + .../chain-bridge-hub-cumulus/src/lib.rs | 116 + primitives/chain-bridge-hub-rococo/Cargo.toml | 10 +- primitives/chain-bridge-hub-rococo/src/lib.rs | 31 +- primitives/chain-bridge-hub-wococo/Cargo.toml | 4 +- primitives/chain-bridge-hub-wococo/src/lib.rs | 12 +- primitives/chain-millau/src/lib.rs | 10 +- primitives/chain-rialto-parachain/src/lib.rs | 6 +- primitives/chain-rialto/src/lib.rs | 17 +- primitives/chain-rococo/src/lib.rs | 13 +- primitives/chain-westend/Cargo.toml | 2 + primitives/chain-westend/src/lib.rs | 45 +- primitives/chain-wococo/src/lib.rs | 3 +- primitives/header-chain/src/lib.rs | 55 +- primitives/messages/Cargo.toml | 1 - primitives/messages/src/lib.rs | 71 +- primitives/messages/src/source_chain.rs | 26 +- primitives/messages/src/storage_keys.rs | 10 +- primitives/messages/src/target_chain.rs | 40 +- primitives/parachains/Cargo.toml | 7 + primitives/parachains/src/lib.rs | 83 +- primitives/polkadot-core/src/lib.rs | 247 +- primitives/polkadot-core/src/parachains.rs | 4 +- primitives/relayers/Cargo.toml | 1 + primitives/relayers/src/lib.rs | 52 +- primitives/runtime/Cargo.toml | 1 + primitives/runtime/src/chain.rs | 54 +- primitives/runtime/src/extensions.rs | 136 + primitives/runtime/src/lib.rs | 43 +- primitives/runtime/src/messages.rs | 4 - primitives/test-utils/src/keyring.rs | 2 +- primitives/test-utils/src/lib.rs | 2 +- relays/bin-substrate/Cargo.toml | 14 +- ...ub_rococo_messages_to_bridge_hub_wococo.rs | 6 +- ...ub_wococo_messages_to_bridge_hub_rococo.rs | 6 +- relays/bin-substrate/src/chains/millau.rs | 11 +- .../src/chains/millau_headers_to_rialto.rs | 2 - .../millau_headers_to_rialto_parachain.rs | 18 +- .../src/chains/millau_messages_to_rialto.rs | 3 + .../millau_messages_to_rialto_parachain.rs | 20 +- relays/bin-substrate/src/chains/rialto.rs | 11 +- .../src/chains/rialto_headers_to_millau.rs | 2 - .../src/chains/rialto_messages_to_millau.rs | 3 + .../src/chains/rialto_parachain.rs | 23 +- .../rialto_parachain_messages_to_millau.rs | 23 +- .../src/chains/rialto_parachains_to_millau.rs | 4 - relays/bin-substrate/src/chains/rococo.rs | 19 +- .../rococo_headers_to_bridge_hub_wococo.rs | 25 +- .../rococo_parachains_to_bridge_hub_wococo.rs | 4 - relays/bin-substrate/src/chains/westend.rs | 24 +- .../chains/westend_parachains_to_millau.rs | 2 - relays/bin-substrate/src/chains/wococo.rs | 19 +- .../wococo_headers_to_bridge_hub_rococo.rs | 23 +- .../wococo_parachains_to_bridge_hub_rococo.rs | 6 +- relays/bin-substrate/src/cli/bridge.rs | 12 +- relays/bin-substrate/src/cli/chain_schema.rs | 73 +- relays/bin-substrate/src/cli/init_bridge.rs | 27 +- relays/bin-substrate/src/cli/mod.rs | 83 +- .../src/cli/register_parachain.rs | 61 +- relays/bin-substrate/src/cli/relay_headers.rs | 3 +- .../src/cli/relay_headers_and_messages/mod.rs | 53 +- .../parachain_to_parachain.rs | 48 +- .../relay_to_parachain.rs | 42 +- .../relay_to_relay.rs | 25 +- .../bin-substrate/src/cli/relay_messages.rs | 4 +- .../bin-substrate/src/cli/relay_parachains.rs | 13 +- .../src/cli/resubmit_transactions.rs | 10 +- relays/bin-substrate/src/cli/send_message.rs | 39 +- relays/bin-substrate/src/main.rs | 5 +- relays/client-bridge-hub-rococo/Cargo.toml | 8 +- relays/client-bridge-hub-rococo/src/lib.rs | 36 +- .../src/runtime_wrapper.rs | 191 +- relays/client-bridge-hub-wococo/Cargo.toml | 12 +- relays/client-bridge-hub-wococo/src/lib.rs | 30 +- .../src/runtime_wrapper.rs | 98 +- relays/client-kusama/Cargo.toml | 1 - relays/client-kusama/src/lib.rs | 23 +- relays/client-millau/src/lib.rs | 33 +- relays/client-polkadot/Cargo.toml | 1 - relays/client-polkadot/src/lib.rs | 23 +- relays/client-rialto-parachain/Cargo.toml | 13 +- relays/client-rialto-parachain/src/lib.rs | 96 +- .../src/runtime_wrapper.rs | 57 + relays/client-rialto/src/lib.rs | 26 +- relays/client-rococo/Cargo.toml | 1 - relays/client-rococo/src/lib.rs | 27 +- relays/client-substrate/Cargo.toml | 10 +- relays/client-substrate/src/calls.rs | 50 + relays/client-substrate/src/chain.rs | 45 +- relays/client-substrate/src/client.rs | 82 +- relays/client-substrate/src/error.rs | 7 + relays/client-substrate/src/lib.rs | 19 +- relays/client-westend/Cargo.toml | 1 - relays/client-westend/src/lib.rs | 47 +- relays/client-wococo/Cargo.toml | 1 - relays/client-wococo/src/lib.rs | 27 +- relays/finality/src/sync_loop_metrics.rs | 19 +- .../src/finality/guards.rs | 2 +- .../src/finality/initialize.rs | 78 +- .../src/finality/source.rs | 151 +- .../src/finality/target.rs | 15 +- relays/lib-substrate-relay/src/lib.rs | 55 +- .../lib-substrate-relay/src/messages_lane.rs | 70 +- .../src/messages_source.rs | 133 +- .../src/messages_target.rs | 87 +- .../src/on_demand/headers.rs | 61 +- .../lib-substrate-relay/src/on_demand/mod.rs | 14 +- .../src/on_demand/parachains.rs | 365 +- .../lib-substrate-relay/src/parachains/mod.rs | 9 +- .../src/parachains/source.rs | 14 +- .../src/parachains/target.rs | 30 +- relays/messages/Cargo.toml | 1 - relays/messages/src/message_lane_loop.rs | 216 +- relays/messages/src/message_race_delivery.rs | 123 +- relays/messages/src/message_race_loop.rs | 88 +- relays/messages/src/message_race_receiving.rs | 13 +- relays/messages/src/metrics.rs | 14 +- .../parachains/src/parachains_loop_metrics.rs | 13 +- relays/utils/Cargo.toml | 1 + relays/utils/src/lib.rs | 5 +- relays/utils/src/metrics.rs | 39 +- scripts/send-message-from-millau-rialto.sh | 29 +- scripts/send-message-from-rialto-millau.sh | 27 +- 212 files changed, 9002 insertions(+), 5961 deletions(-) create mode 100644 SECURITY.md create mode 100644 bin/runtime-common/src/refund_relayer_extension.rs mode change 100644 => 100755 deployments/bridges/common/generate_messages.sh create mode 100644 docs/complex-relay.html create mode 100644 docs/grandpa-finality-relay.html delete mode 100644 docs/high-level.html create mode 100644 docs/messages-relay.html create mode 100644 docs/parachains-finality-relay.html delete mode 100644 docs/plan.md create mode 100644 docs/polkadot-kusama-bridge-overview.md create mode 100644 docs/polkadot-kusama-bridge.html delete mode 100644 docs/scenario1.html delete mode 100644 docs/send-message.md delete mode 100644 docs/testing-scenarios.md create mode 100644 modules/grandpa/README.md create mode 100644 modules/parachains/README.md create mode 100644 primitives/chain-bridge-hub-cumulus/Cargo.toml create mode 100644 primitives/chain-bridge-hub-cumulus/src/lib.rs create mode 100644 primitives/runtime/src/extensions.rs create mode 100644 relays/client-rialto-parachain/src/runtime_wrapper.rs create mode 100644 relays/client-substrate/src/calls.rs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index eb01cfc67ea..698dc09e79e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -242,11 +242,14 @@ build-nightly: variables: &image-variables GIT_STRATEGY: none DOCKERFILE: ci.Dockerfile - IMAGE_NAME: docker.io/paritytech/$CI_JOB_NAME + BRIDGES_PROJECT: "${CI_JOB_NAME}" + DOCKER_IMAGE_NAME: "${CI_JOB_NAME}" + IMAGE_NAME: docker.io/paritytech/$DOCKER_IMAGE_NAME needs: - job: build artifacts: true before_script: &check-versions + - echo "Starting docker image build/push with name '${IMAGE_NAME}' for '${BRIDGES_PROJECT}' with Dockerfile = '${DOCKERFILE}'" - if [[ "${CI_COMMIT_TAG}" ]]; then VERSION=${CI_COMMIT_TAG}; elif [[ "${CI_COMMIT_REF_NAME}" ]]; then @@ -260,6 +263,7 @@ build-nightly: FLOATING_TAG="latest"; fi - echo "Effective tags = ${VERSION} sha-${CI_COMMIT_SHORT_SHA} ${FLOATING_TAG}" + - echo "Full docker image name = ${IMAGE_NAME}" script: - test "${Docker_Hub_User_Parity}" -a "${Docker_Hub_Pass_Parity}" || ( echo "no docker credentials provided"; exit 1 ) @@ -268,7 +272,7 @@ build-nightly: --format=docker --build-arg VCS_REF="${CI_COMMIT_SHORT_SHA}" --build-arg BUILD_DATE="$(date +%d-%m-%Y)" - --build-arg PROJECT="${CI_JOB_NAME}" + --build-arg PROJECT="${BRIDGES_PROJECT}" --build-arg VERSION="${VERSION}" --tag "${IMAGE_NAME}:${VERSION}" --tag "${IMAGE_NAME}:sha-${CI_COMMIT_SHORT_SHA}" @@ -300,4 +304,12 @@ substrate-relay: stage: publish <<: *build-push-image +bridges-common-relay: + stage: publish + <<: *build-push-image + variables: + <<: *image-variables + BRIDGES_PROJECT: substrate-relay + DOCKER_IMAGE_NAME: bridges-common-relay + # FIXME: publish binaries diff --git a/Cargo.lock b/Cargo.lock index 969ed49c8c1..bdbfa407da9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,7 +18,16 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" dependencies = [ - "gimli", + "gimli 0.26.2", +] + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.0", ] [[package]] @@ -27,13 +36,34 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +dependencies = [ + "generic-array 0.14.6", +] + [[package]] name = "aead" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.6", + "rand_core 0.6.4", +] + +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher 0.2.5", ] [[package]] @@ -43,41 +73,75 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ "cfg-if 1.0.0", - "cipher", + "cipher 0.3.0", "cpufeatures", "opaque-debug 0.3.0", ] +[[package]] +name = "aes-gcm" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" +dependencies = [ + "aead 0.3.2", + "aes 0.6.0", + "cipher 0.2.5", + "ctr 0.6.0", + "ghash 0.3.1", + "subtle", +] + [[package]] name = "aes-gcm" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", + "aead 0.4.3", + "aes 0.7.5", + "cipher 0.3.0", + "ctr 0.8.0", + "ghash 0.4.4", "subtle", ] +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher 0.2.5", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher 0.2.5", + "opaque-debug 0.3.0", +] + [[package]] name = "ahash" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.3", + "getrandom 0.2.8", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -89,12 +153,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf688625d06217d5b1bb0ea9d9c44a1635fd0ee3534466388d18203174f4d11" [[package]] -name = "ansi_term" -version = "0.11.0" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "winapi", + "libc", ] [[package]] @@ -108,30 +172,42 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "approx" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "072df7202e63b127ab55acfe16ce97013d5b97bf160489336d3f1840fd78e99e" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" dependencies = [ "num-traits", ] [[package]] name = "arbitrary" -version = "1.0.3" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0224938f92e7aef515fac2ff2d18bd1115c1394ddf4a092e0c87e8be9499ee5" + +[[package]] +name = "arc-swap" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510c76ecefdceada737ea728f4f9a84bd2e1ef29f1ba555e560940fe279954de" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" [[package]] name = "array-bytes" -version = "4.1.0" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" + +[[package]] +name = "array-bytes" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a913633b0c922e6b745072795f50d90ebea78ba31a57e2ac8c2fc7b50950949" +checksum = "22f72e9d6fac4bc80778ea470b20197b88d28c292bb7d60c3fb099280003cd19" [[package]] name = "array_tool" @@ -145,15 +221,6 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" -[[package]] -name = "arrayvec" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -dependencies = [ - "nodrop", -] - [[package]] name = "arrayvec" version = "0.5.2" @@ -166,6 +233,73 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +[[package]] +name = "asn1-rs" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ff05a702273012438132f449575dbc804e27b2f3cbe3069aa237d26c98fa33" +dependencies = [ + "asn1-rs-derive 0.1.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time 0.3.17", +] + +[[package]] +name = "asn1-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf6690c370453db30743b373a60ba498fc0d6d83b11f4abfd87a84a075db5dd4" +dependencies = [ + "asn1-rs-derive 0.4.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time 0.3.17", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" +dependencies = [ + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", + "synstructure", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", +] + [[package]] name = "asn1_der" version = "0.7.5" @@ -184,15 +318,15 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" dependencies = [ - "quote", - "syn", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "async-channel" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" dependencies = [ "concurrent-queue", "event-listener", @@ -201,100 +335,74 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" dependencies = [ + "async-lock", "async-task", "concurrent-queue", "fastrand", "futures-lite", - "once_cell", "slab", ] [[package]] name = "async-global-executor" -version = "2.0.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" dependencies = [ "async-channel", "async-executor", "async-io", - "async-mutex", + "async-lock", "blocking", "futures-lite", - "num_cpus", "once_cell", ] [[package]] name = "async-io" -version = "1.6.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" +checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794" dependencies = [ + "async-lock", + "autocfg", "concurrent-queue", "futures-lite", "libc", "log", - "once_cell", "parking", "polling", "slab", "socket2", "waker-fn", - "winapi", + "windows-sys 0.42.0", ] [[package]] name = "async-lock" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-mutex" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-process" -version = "1.3.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83137067e3a2a6a06d67168e49e68a0957d215410473a740cea95a2425c0b7c6" +checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" dependencies = [ - "async-io", - "blocking", - "cfg-if 1.0.0", "event-listener", "futures-lite", - "libc", - "once_cell", - "signal-hook", - "winapi", ] [[package]] name = "async-std" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52580991739c5cdb36cde8b2a516371c0a3b70dda36d916cc08b82372916808c" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ "async-attributes", "async-channel", "async-global-executor", "async-io", "async-lock", - "async-process", "crossbeam-utils", "futures-channel", "futures-core", @@ -304,7 +412,6 @@ dependencies = [ "kv-log-macro", "log", "memchr", - "num_cpus", "once_cell", "pin-project-lite 0.2.9", "pin-utils", @@ -312,43 +419,28 @@ dependencies = [ "wasm-bindgen-futures", ] -[[package]] -name = "async-std-resolver" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba50e24d9ee0a8950d3d03fc6d0dd10aa14b5de3b101949b4e160f7fee7c723" -dependencies = [ - "async-std", - "async-trait", - "futures-io", - "futures-util", - "pin-utils", - "socket2", - "trust-dns-resolver", -] - [[package]] name = "async-task" -version = "4.0.3" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" +checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.58" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" +checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "asynchronous-codec" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0de5164e5edbf51c45fb8c2d9664ae1c095cce1b265ecf7569093c0d66ef690" +checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182" dependencies = [ "bytes", "futures-sink", @@ -369,7 +461,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -390,40 +482,26 @@ dependencies = [ "rand 0.7.3", ] -[[package]] -name = "backoff" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" -dependencies = [ - "futures-core", - "getrandom 0.2.3", - "instant", - "pin-project-lite 0.2.9", - "rand 0.8.5", - "tokio", -] - [[package]] name = "backtrace" -version = "0.3.64" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ - "addr2line", + "addr2line 0.19.0", "cc", "cfg-if 1.0.0", "libc", "miniz_oxide", - "object 0.27.1", + "object 0.30.2", "rustc-demangle", ] [[package]] name = "base-x" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" [[package]] name = "base16ct" @@ -439,21 +517,27 @@ checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" [[package]] name = "base64" -version = "0.13.0" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "base64ct" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2b2456fd614d856680dcd9fcc660a51a820fa09daef2e49772b56a193c8474" +checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" [[package]] name = "beef" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bed554bd50246729a1ec158d08aa3235d1b69d94ad120ebe187e28894787e736" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" dependencies = [ "serde", ] @@ -461,21 +545,17 @@ dependencies = [ [[package]] name = "beefy-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "async-trait", - "beefy-primitives", "fnv", "futures", - "futures-timer", "log", "parity-scale-codec", "parking_lot 0.12.1", - "sc-chain-spec", "sc-client-api", "sc-consensus", - "sc-finality-grandpa", "sc-keystore", "sc-network", "sc-network-common", @@ -484,6 +564,7 @@ dependencies = [ "sp-api", "sp-application-crypto", "sp-arithmetic", + "sp-beefy", "sp-blockchain", "sp-consensus", "sp-core", @@ -498,18 +579,17 @@ dependencies = [ [[package]] name = "beefy-gadget-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "beefy-gadget", - "beefy-primitives", "futures", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "parity-scale-codec", "parking_lot 0.12.1", "sc-rpc", - "sc-utils", "serde", + "sp-beefy", "sp-core", "sp-runtime", "thiserror", @@ -518,28 +598,11 @@ dependencies = [ [[package]] name = "beefy-merkle-tree" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" -dependencies = [ - "beefy-primitives", - "sp-api", - "sp-runtime", -] - -[[package]] -name = "beefy-primitives" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", "sp-api", - "sp-application-crypto", - "sp-core", - "sp-io", - "sp-mmr-primitives", + "sp-beefy", "sp-runtime", - "sp-std", ] [[package]] @@ -563,8 +626,8 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "proc-macro2", - "quote", + "proc-macro2 1.0.49", + "quote 1.0.23", "regex", "rustc-hash", "shlex", @@ -590,21 +653,11 @@ dependencies = [ [[package]] name = "blake2" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388" -dependencies = [ - "digest 0.10.3", -] - -[[package]] -name = "blake2-rfc" -version = "0.2.18" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "arrayvec 0.4.12", - "constant_time_eq", + "digest 0.10.6", ] [[package]] @@ -615,7 +668,7 @@ checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" dependencies = [ "arrayref", "arrayvec 0.7.2", - "constant_time_eq", + "constant_time_eq 0.1.5", ] [[package]] @@ -626,21 +679,21 @@ checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" dependencies = [ "arrayref", "arrayvec 0.7.2", - "constant_time_eq", + "constant_time_eq 0.1.5", ] [[package]] name = "blake3" -version = "1.3.1" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" +checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" dependencies = [ "arrayref", "arrayvec 0.7.2", "cc", "cfg-if 1.0.0", - "constant_time_eq", - "digest 0.10.3", + "constant_time_eq 0.2.4", + "digest 0.10.6", ] [[package]] @@ -649,7 +702,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding", + "block-padding 0.1.5", "byte-tools", "byteorder", "generic-array 0.12.4", @@ -661,16 +714,26 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.6", ] [[package]] name = "block-buffer" -version = "0.10.0" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "block-modes" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" +checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" dependencies = [ - "generic-array 0.14.4", + "block-padding 0.2.1", + "cipher 0.2.5", ] [[package]] @@ -682,18 +745,24 @@ dependencies = [ "byte-tools", ] +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + [[package]] name = "blocking" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046e47d4b2d391b1f6f8b407b1deb8dee56c1852ccd868becf2710f601b5f427" +checksum = "3c67b173a56acffd6d2326fb7ab938ba0b00a71480e14902b2591c87bc5741e8" dependencies = [ "async-channel", + "async-lock", "async-task", "atomic-waker", "fastrand", "futures-lite", - "once_cell", ] [[package]] @@ -710,7 +779,6 @@ name = "bp-beefy" version = "0.1.0" dependencies = [ "beefy-merkle-tree", - "beefy-primitives", "bp-runtime", "frame-support", "pallet-beefy-mmr", @@ -718,24 +786,31 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-application-crypto", - "sp-core", - "sp-io", + "sp-beefy", "sp-runtime", "sp-std", - "static_assertions", ] [[package]] -name = "bp-bridge-hub-rococo" +name = "bp-bridge-hub-cumulus" version = "0.1.0" dependencies = [ "bp-messages", "bp-polkadot-core", + "frame-support", + "frame-system", + "polkadot-primitives", + "sp-api", +] + +[[package]] +name = "bp-bridge-hub-rococo" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-messages", "bp-runtime", "frame-support", - "polkadot-runtime-constants", - "smallvec", "sp-api", "sp-std", ] @@ -744,7 +819,7 @@ dependencies = [ name = "bp-bridge-hub-wococo" version = "0.1.0" dependencies = [ - "bp-bridge-hub-rococo", + "bp-bridge-hub-cumulus", "bp-messages", "bp-runtime", "frame-support", @@ -789,7 +864,6 @@ dependencies = [ "frame-support", "hex", "hex-literal", - "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "serde", @@ -825,12 +899,16 @@ dependencies = [ name = "bp-parachains" version = "0.1.0" dependencies = [ + "bp-header-chain", "bp-polkadot-core", "bp-runtime", "frame-support", + "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "sp-core", + "sp-runtime", + "sp-std", ] [[package]] @@ -865,6 +943,7 @@ name = "bp-relayers" version = "0.1.0" dependencies = [ "bp-messages", + "bp-rialto", "frame-support", "hex", "hex-literal", @@ -918,6 +997,7 @@ dependencies = [ "frame-system", "hash-db", "hex-literal", + "impl-trait-for-tuples", "num-traits", "parity-scale-codec", "scale-info", @@ -952,6 +1032,7 @@ version = "0.1.0" dependencies = [ "bp-polkadot-core", "bp-runtime", + "frame-support", "sp-api", ] @@ -973,7 +1054,9 @@ dependencies = [ "bp-messages", "bp-parachains", "bp-polkadot-core", + "bp-rialto", "bp-runtime", + "bp-test-utils", "frame-support", "frame-system", "hash-db", @@ -983,6 +1066,9 @@ dependencies = [ "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", + "pallet-bridge-relayers", + "pallet-transaction-payment", + "pallet-utility", "pallet-xcm", "parity-scale-codec", "scale-info", @@ -1006,11 +1092,12 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "0.2.17" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" dependencies = [ "memchr", + "serde", ] [[package]] @@ -1024,15 +1111,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.8.0" +version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" [[package]] name = "byte-slice-cast" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c5fdd0166095e1d463fc6cc01aa8ce547ad77a4e84d42eb6762b084e28067e" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "byte-tools" @@ -1048,9 +1135,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] name = "bzip2-sys" @@ -1063,17 +1150,11 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "cache-padded" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" - [[package]] name = "camino" -version = "1.0.5" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d74260d9bf6944e2208aa46841b4b8f0d7ffc0849a06837b2f510337f86b2b" +checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055" dependencies = [ "serde", ] @@ -1095,26 +1176,37 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver 1.0.4", + "semver 1.0.16", "serde", "serde_json", ] [[package]] name = "castaway" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed247d1586918e46f2bbe0f13b06498db8dab5a8c1093f156652e9f2e0a73fc3" +checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.0.77" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" dependencies = [ "jobserver", ] +[[package]] +name = "ccm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca1a8fbc20b50ac9673ff014abfb2b5f4085ee1a850d408f14a159c5853ac7" +dependencies = [ + "aead 0.3.2", + "cipher 0.2.5", + "subtle", +] + [[package]] name = "cexpr" version = "0.6.0" @@ -1158,7 +1250,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" dependencies = [ "cfg-if 1.0.0", - "cipher", + "cipher 0.3.0", "cpufeatures", "zeroize", ] @@ -1169,23 +1261,25 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" dependencies = [ - "aead", + "aead 0.4.3", "chacha20", - "cipher", + "cipher 0.3.0", "poly1305", "zeroize", ] [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ - "libc", + "iana-time-zone", + "js-sys", "num-integer", "num-traits", - "time 0.1.44", + "time 0.1.45", + "wasm-bindgen", "winapi", ] @@ -1202,13 +1296,22 @@ dependencies = [ "unsigned-varint", ] +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array 0.14.6", +] + [[package]] name = "cipher" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.6", ] [[package]] @@ -1220,11 +1323,20 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "ckb-merkle-mountain-range" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ccb671c5921be8a84686e6212ca184cb1d7c51cadcdbfcbd1cc3f042f5dfb8" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "clang-sys" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa66045b9cb23c2e9c1520732030608b02ee07e5cfaa5a521ec15ded7fa24c90" +checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" dependencies = [ "glob", "libc", @@ -1233,11 +1345,11 @@ dependencies = [ [[package]] name = "clap" -version = "2.33.3" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "ansi_term 0.11.0", + "ansi_term", "atty", "bitflags", "strsim 0.8.0", @@ -1248,14 +1360,14 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.26" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2148adefda54e14492fb9bddcc600b4344c5d1a3123bd666dcb939c6f0e0e57e" +checksum = "aa91278560fc226a5d9d736cc21e485ff9aad47d26b8ffe1f54cba868b684b9f" dependencies = [ - "atty", "bitflags", "clap_derive", "clap_lex", + "is-terminal", "once_cell", "strsim 0.10.0", "termcolor", @@ -1263,22 +1375,22 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.0.21" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" dependencies = [ "heck 0.4.0", "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "clap_lex" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" dependencies = [ "os_str_bytes", ] @@ -1307,29 +1419,29 @@ dependencies = [ [[package]] name = "comfy-table" -version = "6.1.0" +version = "6.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85914173c2f558d61613bfbbf1911f14e630895087a7ed2fafc0f5319e1536e7" +checksum = "6e7b787b0dc42e8111badfdbe4c3059158ccb2db8780352fa1b01e8ccf45cc4d" dependencies = [ "strum 0.24.1", - "strum_macros 0.24.0", + "strum_macros 0.24.3", "unicode-width", ] [[package]] name = "concurrent-queue" -version = "1.2.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b" dependencies = [ - "cache-padded", + "crossbeam-utils", ] [[package]] name = "const-oid" -version = "0.7.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" +checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" [[package]] name = "constant_time_eq" @@ -1337,6 +1449,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "constant_time_eq" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" + [[package]] name = "convert_case" version = "0.4.0" @@ -1345,9 +1463,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys", "libc", @@ -1370,13 +1488,23 @@ dependencies = [ [[package]] name = "cpp_demangle" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931ab2a3e6330a07900b8e7ca4e106cdcbb93f2b9a52df55e54ee53d8305b55d" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "cpu-time" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e393a7668fe1fad3075085b86c781883000b4ede868f43627b34a87c8b7ded" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "cpufeatures" version = "0.2.5" @@ -1386,20 +1514,26 @@ dependencies = [ "libc", ] +[[package]] +name = "cpuid-bool" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" + [[package]] name = "cranelift-bforest" -version = "0.88.1" +version = "0.88.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44409ccf2d0f663920cab563d2b79fcd6b2e9a2bcc6e929fef76c8f82ad6c17a" +checksum = "52056f6d0584484b57fa6c1a65c1fcb15f3780d8b6a758426d9e3084169b2ddd" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.88.1" +version = "0.88.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de2018ad96eb97f621f7d6b900a0cc661aec8d02ea4a50e56ecb48e5a2fcaf" +checksum = "18fed94c8770dc25d01154c3ffa64ed0b3ba9d583736f305fed7beebe5d9cf74" dependencies = [ "arrayvec 0.7.2", "bumpalo", @@ -1408,7 +1542,7 @@ dependencies = [ "cranelift-codegen-shared", "cranelift-entity", "cranelift-isle", - "gimli", + "gimli 0.26.2", "log", "regalloc2", "smallvec", @@ -1417,33 +1551,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.88.1" +version = "0.88.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5287ce36e6c4758fbaf298bd1a8697ad97a4f2375a3d1b61142ea538db4877e5" +checksum = "1c451b81faf237d11c7e4f3165eeb6bac61112762c5cfe7b4c0fb7241474358f" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.88.1" +version = "0.88.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2855c24219e2f08827f3f4ffb2da92e134ae8d8ecc185b11ec8f9878cf5f588e" +checksum = "e7c940133198426d26128f08be2b40b0bd117b84771fd36798969c4d712d81fc" [[package]] name = "cranelift-entity" -version = "0.88.1" +version = "0.88.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b65673279d75d34bf11af9660ae2dbd1c22e6d28f163f5c72f4e1dc56d56103" +checksum = "87a0f1b2fdc18776956370cf8d9b009ded3f855350c480c1c52142510961f352" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.88.1" +version = "0.88.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed2b3d7a4751163f6c4a349205ab1b7d9c00eecf19dcea48592ef1f7688eefc" +checksum = "34897538b36b216cc8dd324e73263596d51b8cf610da6498322838b2546baf8a" dependencies = [ "cranelift-codegen", "log", @@ -1453,15 +1587,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.88.1" +version = "0.88.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be64cecea9d90105fc6a2ba2d003e98c867c1d6c4c86cc878f97ad9fb916293" +checksum = "1b2629a569fae540f16a76b70afcc87ad7decb38dc28fa6c648ac73b51e78470" [[package]] name = "cranelift-native" -version = "0.88.1" +version = "0.88.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a03a6ac1b063e416ca4b93f6247978c991475e8271465340caa6f92f3c16a4" +checksum = "20937dab4e14d3e225c5adfc9c7106bafd4ac669bdb43027b911ff794c6fb318" dependencies = [ "cranelift-codegen", "libc", @@ -1470,9 +1604,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.88.1" +version = "0.88.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c699873f7b30bc5f20dd03a796b4183e073a46616c91704792ec35e45d13f913" +checksum = "80fc2288957a94fd342a015811479de1837850924166d1f1856d8406e6f3609b" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1484,20 +1618,35 @@ dependencies = [ "wasmtime-types", ] +[[package]] +name = "crc" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + [[package]] name = "crc32fast" -version = "1.2.2" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3825b1e8580894917dc4468cb634a1b4e9745fddc854edad72d9c04644c0319f" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "crossbeam-channel" -version = "0.5.1" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -1505,9 +1654,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", @@ -1516,22 +1665,22 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.5" +version = "0.9.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" dependencies = [ + "autocfg", "cfg-if 1.0.0", "crossbeam-utils", - "lazy_static", - "memoffset", + "memoffset 0.7.1", "scopeguard", ] [[package]] name = "crossbeam-queue" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -1539,12 +1688,11 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.11" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if 1.0.0", - "once_cell", ] [[package]] @@ -1555,23 +1703,23 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.3.2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ - "generic-array 0.14.4", - "rand_core 0.6.3", + "generic-array 0.14.6", + "rand_core 0.6.4", "subtle", "zeroize", ] [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.6", "typenum", ] @@ -1581,7 +1729,17 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.6", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" +dependencies = [ + "generic-array 0.14.6", "subtle", ] @@ -1591,18 +1749,27 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.6", "subtle", ] [[package]] name = "ctor" -version = "0.1.21" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ - "quote", - "syn", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "ctr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +dependencies = [ + "cipher 0.2.5", ] [[package]] @@ -1611,15 +1778,15 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" dependencies = [ - "cipher", + "cipher 0.3.0", ] [[package]] name = "cumulus-client-cli" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ - "clap 4.0.26", + "clap 4.1.0", "parity-scale-codec", "sc-chain-spec", "sc-cli", @@ -1632,7 +1799,7 @@ dependencies = [ [[package]] name = "cumulus-client-collator" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "cumulus-client-consensus-common", "cumulus-client-network", @@ -1655,7 +1822,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-aura" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "async-trait", "cumulus-client-consensus-common", @@ -1684,12 +1851,15 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-common" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "async-trait", + "cumulus-client-pov-recovery", + "cumulus-primitives-core", "cumulus-relay-chain-interface", "dyn-clone", "futures", + "log", "parity-scale-codec", "polkadot-primitives", "sc-client-api", @@ -1704,7 +1874,7 @@ dependencies = [ [[package]] name = "cumulus-client-network" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "async-trait", "cumulus-relay-chain-interface", @@ -1727,7 +1897,7 @@ dependencies = [ [[package]] name = "cumulus-client-pov-recovery" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "cumulus-primitives-core", "cumulus-relay-chain-interface", @@ -1750,19 +1920,24 @@ dependencies = [ [[package]] name = "cumulus-client-service" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-common", "cumulus-client-pov-recovery", "cumulus-primitives-core", + "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", + "cumulus-relay-chain-minimal-node", + "futures", "parking_lot 0.12.1", "polkadot-primitives", "sc-client-api", "sc-consensus", "sc-service", + "sc-sysinfo", + "sc-telemetry", "sp-api", "sp-blockchain", "sp-consensus", @@ -1773,7 +1948,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-aura-ext" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "frame-support", "frame-system", @@ -1789,7 +1964,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-dmp-queue" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -1806,7 +1981,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-parachain-system" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "bytes", "cumulus-pallet-parachain-system-proc-macro", @@ -1829,23 +2004,24 @@ dependencies = [ "sp-std", "sp-trie", "sp-version", + "xcm", ] [[package]] name = "cumulus-pallet-parachain-system-proc-macro" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "cumulus-pallet-xcm" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -1861,7 +2037,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-xcmp-queue" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -1881,7 +2057,7 @@ dependencies = [ [[package]] name = "cumulus-primitives-core" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "parity-scale-codec", "polkadot-core-primitives", @@ -1891,12 +2067,13 @@ dependencies = [ "sp-runtime", "sp-std", "sp-trie", + "xcm", ] [[package]] name = "cumulus-primitives-parachain-inherent" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -1919,7 +2096,7 @@ dependencies = [ [[package]] name = "cumulus-primitives-timestamp" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "cumulus-primitives-core", "futures", @@ -1932,7 +2109,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-inprocess-interface" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -1957,12 +2134,12 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-interface" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "async-trait", "cumulus-primitives-core", "futures", - "jsonrpsee-core", + "jsonrpsee-core 0.16.2", "parity-scale-codec", "polkadot-overseer", "polkadot-service", @@ -1971,24 +2148,23 @@ dependencies = [ "sp-blockchain", "sp-state-machine", "thiserror", + "tokio", ] [[package]] name = "cumulus-relay-chain-minimal-node" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ - "array-bytes", + "array-bytes 6.0.0", "async-trait", "cumulus-primitives-core", "cumulus-relay-chain-interface", "cumulus-relay-chain-rpc-interface", "futures", - "lru 0.8.1", - "polkadot-availability-distribution", + "lru", "polkadot-core-primitives", "polkadot-network-bridge", - "polkadot-node-core-av-store", "polkadot-node-network-protocol", "polkadot-node-subsystem-util", "polkadot-overseer", @@ -2000,8 +2176,6 @@ dependencies = [ "sc-keystore", "sc-network", "sc-network-common", - "sc-network-light", - "sc-network-sync", "sc-service", "sc-telemetry", "sc-tracing", @@ -2020,24 +2194,25 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-rpc-interface" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "async-trait", - "backoff 0.4.0", "cumulus-primitives-core", "cumulus-relay-chain-interface", "futures", "futures-timer", - "jsonrpsee", + "jsonrpsee 0.16.2", + "lru", "parity-scale-codec", "polkadot-service", "sc-client-api", "sc-rpc-api", + "serde", + "serde_json", "sp-api", "sp-authority-discovery", "sp-consensus-babe", "sp-core", - "sp-runtime", "sp-state-machine", "sp-storage", "tokio", @@ -2048,7 +2223,7 @@ dependencies = [ [[package]] name = "cumulus-test-relay-sproof-builder" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "cumulus-primitives-core", "parity-scale-codec", @@ -2060,9 +2235,9 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.41" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc6d233563261f8db6ffb83bbaad5a73837a6e6b28868e926337ebbdece0be3" +checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" dependencies = [ "curl-sys", "libc", @@ -2075,9 +2250,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.51+curl-7.80.0" +version = "0.4.59+curl-7.86.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d130987e6a6a34fe0889e1083022fa48cd90e6709a84be3fb8dd95801de5af20" +checksum = "6cfce34829f448b08f55b7db6d0009e23e2e86a34e8c2b366269bf5799b4a407" dependencies = [ "cc", "libc", @@ -2117,22 +2292,23 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0-pre.1" +version = "4.0.0-pre.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4033478fbf70d6acf2655ac70da91ee65852d69daf7a67bf7a2f518fb47aafcf" +checksum = "67bc65846be335cb20f4e52d49a437b773a2c1fdb42b19fc84e79e6f6771536f" dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.6.3", + "cfg-if 1.0.0", + "fiat-crypto", + "packed_simd_2", + "platforms 3.0.2", "subtle", "zeroize", ] [[package]] name = "cxx" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453" +checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" dependencies = [ "cc", "cxxbridge-flags", @@ -2142,41 +2318,76 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0" +checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70" dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2", - "quote", + "proc-macro2 1.0.49", + "quote 1.0.23", "scratch", - "syn", + "syn 1.0.107", ] [[package]] name = "cxxbridge-flags" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71" +checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" [[package]] name = "cxxbridge-macro" -version = "1.0.82" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" +dependencies = [ + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "darling" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470" +checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" dependencies = [ - "proc-macro2", - "quote", - "syn", + "fnv", + "ident_case", + "proc-macro2 1.0.49", + "quote 1.0.23", + "strsim 0.10.0", + "syn 1.0.107", +] + +[[package]] +name = "darling_macro" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +dependencies = [ + "darling_core", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "data-encoding" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" [[package]] name = "data-encoding-macro" @@ -2195,16 +2406,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" dependencies = [ "data-encoding", - "syn", + "syn 1.0.107", ] [[package]] name = "der" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe398ac75057914d7d07307bf67dc7f3f574a26783b4fc7805a20ffa9f506e82" +dependencies = [ + "asn1-rs 0.3.1", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der-parser" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" +dependencies = [ + "asn1-rs 0.5.1", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", ] [[package]] @@ -2213,9 +2454,40 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "derive_builder" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" +dependencies = [ + "darling", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "derive_builder_macro" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" +dependencies = [ + "derive_builder_core", + "syn 1.0.107", ] [[package]] @@ -2225,10 +2497,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2", - "quote", + "proc-macro2 1.0.49", + "quote 1.0.23", "rustc_version 0.4.0", - "syn", + "syn 1.0.107", ] [[package]] @@ -2252,16 +2524,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.6", ] [[package]] name = "digest" -version = "0.10.3" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer 0.10.0", + "block-buffer 0.10.3", "crypto-common", "subtle", ] @@ -2287,9 +2559,9 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", @@ -2308,13 +2580,14 @@ dependencies = [ ] [[package]] -name = "dns-parser" -version = "0.8.0" +name = "displaydoc" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea" +checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ - "byteorder", - "quick-error 1.2.3", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2337,9 +2610,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "dtoa" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6053ff46b5639ceb91756a85a4c8914668393a03170efd79c8884a529d80656" +checksum = "c00704156a7de8df8da0911424e30c2049957b0a714542a44e05fe693dd85313" [[package]] name = "dyn-clonable" @@ -2357,22 +2630,22 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "dyn-clone" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" +checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" [[package]] name = "ecdsa" -version = "0.13.4" +version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ "der", "elliptic-curve", @@ -2382,9 +2655,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "1.3.0" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ "signature", ] @@ -2399,7 +2672,7 @@ dependencies = [ "ed25519", "rand 0.7.3", "serde", - "sha2 0.9.8", + "sha2 0.9.9", "zeroize", ] @@ -2412,30 +2685,34 @@ dependencies = [ "curve25519-dalek 3.2.0", "hashbrown", "hex", - "rand_core 0.6.3", - "sha2 0.9.8", + "rand_core 0.6.4", + "sha2 0.9.9", "zeroize", ] [[package]] name = "either" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "elliptic-curve" -version = "0.11.12" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ "base16ct", "crypto-bigint", "der", + "digest 0.10.6", "ff", - "generic-array 0.14.4", + "generic-array 0.14.6", "group", - "rand_core 0.6.3", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", "sec1", "subtle", "zeroize", @@ -2443,9 +2720,9 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.29" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74ea89a0a1b98f6332de42c95baff457ada66d1cb4030f9ff151b2041a1c746" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" dependencies = [ "cfg-if 1.0.0", ] @@ -2457,9 +2734,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ "heck 0.4.0", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2477,20 +2754,20 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "enumn" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038b1afa59052df211f9efd58f8b1d84c242935ede1c3dbaed26b018a9e06ae2" +checksum = "e88bcb3a067a6555d577aba299e75eff9942da276e6506fc6274327daa026132" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2521,9 +2798,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.9.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" dependencies = [ "atty", "humantime 2.1.0", @@ -2532,11 +2809,24 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime 2.1.0", + "is-terminal", + "log", + "regex", + "termcolor", +] + [[package]] name = "environmental" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b91989ae21441195d7d9b9993a2f9295c7e1a8c96255d8b729accddc124797" +checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" [[package]] name = "errno" @@ -2574,9 +2864,9 @@ dependencies = [ [[package]] name = "ethereum-types" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81224dc661606574f5a0f28c9947d0ee1d93ff11c5f1c4e7272f52e8c0b5483c" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ "ethbloom", "fixed-hash 0.8.0", @@ -2588,9 +2878,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "2.5.1" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "exit-future" @@ -2609,8 +2899,8 @@ checksum = "a718c0675c555c5f976fff4ea9e2c150fa06cefa201cadef87cfbf9324075881" dependencies = [ "blake3", "fs-err", - "proc-macro2", - "quote", + "proc-macro2 1.0.49", + "quote 1.0.23", ] [[package]] @@ -2621,9 +2911,9 @@ checksum = "3774182a5df13c3d1690311ad32fbe913feef26baba609fa2dd5f72042bd2ab6" dependencies = [ "blake2", "fs-err", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2666,9 +2956,9 @@ dependencies = [ "expander 0.0.4", "indexmap", "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", "thiserror", ] @@ -2683,41 +2973,47 @@ dependencies = [ [[package]] name = "ff" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2958d04124b9f27f175eaeb9a9f383d026098aa837eadd8ba22c11f13a05b9e" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" dependencies = [ - "rand_core 0.6.3", + "rand_core 0.6.4", "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a214f5bb88731d436478f3ae1f8a277b62124089ba9fb67f4f93fb100ef73c90" + [[package]] name = "file-per-thread-logger" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fdbe0d94371f9ce939b555dd342d0686cc4c0cadbcd4b61d70af5ff97eb4126" +checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" dependencies = [ - "env_logger 0.7.1", + "env_logger 0.10.0", "log", ] [[package]] name = "filetime" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" +checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] name = "finality-grandpa" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b22349c6a11563a202d95772a68e0fcf56119e74ea8a2a19cf2301460fcd0df5" +checksum = "e24e6c429951433ccb7c87fd528c60084834dcd14763182c1f83291bcde24c34" dependencies = [ "either", "futures", @@ -2735,7 +3031,7 @@ version = "0.1.0" dependencies = [ "async-std", "async-trait", - "backoff 0.2.1", + "backoff", "bp-header-chain", "futures", "log", @@ -2770,19 +3066,17 @@ dependencies = [ [[package]] name = "fixedbitset" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.22" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ - "cfg-if 1.0.0", "crc32fast", - "libc", "libz-sys", "miniz_oxide", ] @@ -2805,7 +3099,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "parity-scale-codec", ] @@ -2828,7 +3122,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-support", "frame-system", @@ -2851,28 +3145,25 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "Inflector", - "array-bytes", + "array-bytes 4.2.0", "chrono", - "clap 4.0.26", + "clap 4.1.0", "comfy-table", "frame-benchmarking", "frame-support", "frame-system", "gethostname", "handlebars", - "hash-db", "itertools", - "kvdb", "lazy_static", "linked-hash-map", "log", - "memory-db", "parity-scale-codec", "rand 0.8.5", - "rand_pcg 0.3.1", + "rand_pcg", "sc-block-builder", "sc-cli", "sc-client-api", @@ -2882,7 +3173,6 @@ dependencies = [ "sc-sysinfo", "serde", "serde_json", - "serde_nanos", "sp-api", "sp-blockchain", "sp-core", @@ -2892,9 +3182,9 @@ dependencies = [ "sp-keystore", "sp-runtime", "sp-state-machine", + "sp-std", "sp-storage", "sp-trie", - "tempfile", "thiserror", "thousands", ] @@ -2902,18 +3192,18 @@ dependencies = [ [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-election-provider-solution-type", "frame-support", @@ -2921,6 +3211,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-arithmetic", + "sp-core", "sp-npos-elections", "sp-runtime", "sp-std", @@ -2929,7 +3220,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-support", "frame-system", @@ -2954,10 +3245,26 @@ dependencies = [ "serde", ] +[[package]] +name = "frame-remote-externalities" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +dependencies = [ + "futures", + "log", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "substrate-rpc-client", + "tokio", +] + [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "bitflags", "frame-metadata", @@ -2989,43 +3296,43 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "Inflector", "cfg-expr", "frame-support-procedural-tools", "itertools", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-support", "log", @@ -3043,7 +3350,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -3058,7 +3365,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "parity-scale-codec", "sp-api", @@ -3067,7 +3374,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-support", "parity-scale-codec", @@ -3078,9 +3385,9 @@ dependencies = [ [[package]] name = "fs-err" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ebd3504ad6116843b8375ad70df74e7bfe83cac77a1f3fe73200c844d43bfe0" +checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" [[package]] name = "fs2" @@ -3106,9 +3413,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" dependencies = [ "futures-channel", "futures-core", @@ -3121,9 +3428,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ "futures-core", "futures-sink", @@ -3131,15 +3438,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" [[package]] name = "futures-executor" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" dependencies = [ "futures-core", "futures-task", @@ -3149,9 +3456,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" [[package]] name = "futures-lite" @@ -3170,13 +3477,13 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -3186,21 +3493,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" dependencies = [ "futures-io", - "rustls", - "webpki", + "rustls 0.20.8", + "webpki 0.22.0", ] [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" [[package]] name = "futures-timer" @@ -3210,9 +3517,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ "futures-channel", "futures-core", @@ -3246,9 +3553,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.4" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", @@ -3271,21 +3578,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if 1.0.0", - "js-sys", "libc", "wasi 0.9.0+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi 0.10.0+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "ghash" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" +dependencies = [ + "opaque-debug 0.3.0", + "polyval 0.4.5", ] [[package]] @@ -3295,31 +3610,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" dependencies = [ "opaque-debug 0.3.0", - "polyval", + "polyval 0.5.3", ] [[package]] name = "gimli" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ "fallible-iterator", "indexmap", "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" + [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.8" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ "aho-corasick", "bstr", @@ -3330,33 +3651,32 @@ dependencies = [ [[package]] name = "gloo-timers" -version = "0.2.1" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" +checksum = "98c4a8d6391675c6b2ee1a6c8d06e8e2d03605c44cec1270675985a4c2a5500b" dependencies = [ "futures-channel", "futures-core", "js-sys", "wasm-bindgen", - "web-sys", ] [[package]] name = "group" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ "ff", - "rand_core 0.6.3", + "rand_core 0.6.4", "subtle", ] [[package]] name = "h2" -version = "0.3.10" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9de88456263e249e241fcd211d3954e2c9b0ef7ccfc235a444eb367cae3689" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" dependencies = [ "bytes", "fnv", @@ -3367,22 +3687,22 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.6.9", + "tokio-util", "tracing", ] [[package]] name = "handlebars" -version = "4.2.2" +version = "4.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d6a30320f094710245150395bc763ad23128d6a1ebbad7594dc4164b62c56b" +checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" dependencies = [ "log", "pest", "pest_derive", - "quick-error 2.0.1", "serde", "serde_json", + "thiserror", ] [[package]] @@ -3433,6 +3753,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.3" @@ -3445,6 +3774,15 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac 0.12.1", +] + [[package]] name = "hmac" version = "0.8.1" @@ -3455,6 +3793,16 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "hmac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +dependencies = [ + "crypto-mac 0.10.1", + "digest 0.9.0", +] + [[package]] name = "hmac" version = "0.11.0" @@ -3465,6 +3813,15 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + [[package]] name = "hmac-drbg" version = "0.3.0" @@ -3472,19 +3829,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array 0.14.4", + "generic-array 0.14.6", "hmac 0.8.1", ] [[package]] name = "honggfuzz" -version = "0.5.54" +version = "0.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea09577d948a98a5f59b7c891e274c4fb35ad52f67782b3d0cb53b9c05301f1" +checksum = "848e9c511092e0daa0a35a63e8e6e475a3e8f870741448b9f6028d69b142f18e" dependencies = [ "arbitrary", "lazy_static", - "memmap", + "memmap2", + "rustc_version 0.4.0", ] [[package]] @@ -3506,25 +3864,31 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa 1.0.1", + "itoa", ] [[package]] name = "http-body" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", "pin-project-lite 0.2.9", ] +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + [[package]] name = "httparse" -version = "1.5.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -3538,7 +3902,7 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ - "quick-error 1.2.3", + "quick-error", ] [[package]] @@ -3549,9 +3913,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.16" +version = "0.14.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" dependencies = [ "bytes", "futures-channel", @@ -3562,7 +3926,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 0.4.8", + "itoa", "pin-project-lite 0.2.9", "socket2", "tokio", @@ -3573,19 +3937,49 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.0" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ "http", "hyper", "log", - "rustls", + "rustls 0.20.8", "rustls-native-certs", "tokio", "tokio-rustls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.2.3" @@ -3619,9 +4013,9 @@ dependencies = [ [[package]] name = "if-watch" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065c008e570a43c00de6aed9714035e5ea6a498c255323db9091722af6ee67dd" +checksum = "ba7abdbb86e485125dad06c2691e1e393bf3b08c7b743b43aa162a00fd39062e" dependencies = [ "async-io", "core-foundation", @@ -3632,6 +4026,7 @@ dependencies = [ "log", "rtnetlink", "system-configuration", + "tokio", "windows", ] @@ -3677,16 +4072,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", @@ -3704,9 +4099,9 @@ dependencies = [ [[package]] name = "integer-encoding" -version = "3.0.2" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90c11140ffea82edce8dcd74137ce9324ec24b3cf0175fc9d7e29164da9915b8" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" [[package]] name = "integer-sqrt" @@ -3717,11 +4112,40 @@ dependencies = [ "num-traits", ] +[[package]] +name = "interceptor" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8a11ae2da61704edada656798b61c94b35ecac2c58eb955156987d5e6be90b" +dependencies = [ + "async-trait", + "bytes", + "log", + "rand 0.8.5", + "rtcp", + "rtp", + "thiserror", + "tokio", + "waitgroup", + "webrtc-srtp", + "webrtc-util", +] + [[package]] name = "io-lifetimes" -version = "0.7.3" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" + +[[package]] +name = "io-lifetimes" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06" +checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +dependencies = [ + "libc", + "windows-sys 0.42.0", +] [[package]] name = "ip_network" @@ -3731,9 +4155,9 @@ checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1" [[package]] name = "ipconfig" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723519edce41262b05d4143ceb95050e4c614f483e78e9fd9e39a8275a84ad98" +checksum = "bd302af1b90f2463a98fa5ad469fc212c8e3175a41c3068601bfa2727591c5be" dependencies = [ "socket2", "widestring", @@ -3743,15 +4167,27 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.3.1" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + +[[package]] +name = "is-terminal" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes 1.0.4", + "rustix 0.36.6", + "windows-sys 0.42.0", +] [[package]] name = "isahc" -version = "1.6.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d140e84730d325378912ede32d7cd53ef1542725503b3353e5ec8113c7c6f588" +checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" dependencies = [ "async-channel", "castaway", @@ -3776,39 +4212,33 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.1" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "jobserver" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.55" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] @@ -3832,12 +4262,24 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bd0d559d5e679b1ab2f869b486a11182923863b1b3ee8b421763cdd707b783a" dependencies = [ - "jsonrpsee-core", - "jsonrpsee-http-server", - "jsonrpsee-proc-macros", - "jsonrpsee-types", - "jsonrpsee-ws-client", - "jsonrpsee-ws-server", + "jsonrpsee-core 0.15.1", + "jsonrpsee-proc-macros 0.15.1", + "jsonrpsee-types 0.15.1", + "jsonrpsee-ws-client 0.15.1", + "tracing", +] + +[[package]] +name = "jsonrpsee" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" +dependencies = [ + "jsonrpsee-core 0.16.2", + "jsonrpsee-proc-macros 0.16.2", + "jsonrpsee-server", + "jsonrpsee-types 0.16.2", + "jsonrpsee-ws-client 0.16.2", "tracing", ] @@ -3849,15 +4291,36 @@ checksum = "8752740ecd374bcbf8b69f3e80b0327942df76f793f8d4e60d3355650c31fb74" dependencies = [ "futures-util", "http", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.15.1", + "jsonrpsee-types 0.15.1", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", + "webpki-roots", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965de52763f2004bc91ac5bcec504192440f0b568a5d621c59d9dbd6f886c3fb" +dependencies = [ + "futures-util", + "http", + "jsonrpsee-core 0.16.2", + "jsonrpsee-types 0.16.2", "pin-project", "rustls-native-certs", "soketto", "thiserror", "tokio", "tokio-rustls", - "tokio-util 0.7.1", + "tokio-util", "tracing", "webpki-roots", ] @@ -3867,6 +4330,29 @@ name = "jsonrpsee-core" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3dc3e9cf2ba50b7b1d7d76a667619f82846caa39e8e8daa8a4962d74acaddca" +dependencies = [ + "anyhow", + "async-lock", + "async-trait", + "beef", + "futures-channel", + "futures-timer", + "futures-util", + "jsonrpsee-types 0.15.1", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "tracing-futures", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" dependencies = [ "anyhow", "arrayvec 0.7.2", @@ -3877,10 +4363,8 @@ dependencies = [ "futures-timer", "futures-util", "globset", - "http", "hyper", - "jsonrpsee-types", - "lazy_static", + "jsonrpsee-types 0.16.2", "parking_lot 0.12.1", "rand 0.8.5", "rustc-hash", @@ -3890,45 +4374,74 @@ dependencies = [ "thiserror", "tokio", "tracing", - "tracing-futures", - "unicase", ] [[package]] -name = "jsonrpsee-http-server" +name = "jsonrpsee-proc-macros" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03802f0373a38c2420c70b5144742d800b509e2937edc4afb116434f07120117" +checksum = "bd67957d4280217247588ac86614ead007b301ca2fa9f19c19f880a536f029e3" +dependencies = [ + "proc-macro-crate", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa6da1e4199c10d7b1d0a6e5e8bd8e55f351163b6f4b3cbb044672a69bd4c1c" +dependencies = [ + "heck 0.4.0", + "proc-macro-crate", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb69dad85df79527c019659a992498d03f8495390496da2f07e6c24c2b356fc" dependencies = [ "futures-channel", "futures-util", + "http", "hyper", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.16.2", + "jsonrpsee-types 0.16.2", "serde", "serde_json", + "soketto", "tokio", + "tokio-stream", + "tokio-util", + "tower", "tracing", - "tracing-futures", ] [[package]] -name = "jsonrpsee-proc-macros" +name = "jsonrpsee-types" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd67957d4280217247588ac86614ead007b301ca2fa9f19c19f880a536f029e3" +checksum = "e290bba767401b646812f608c099b922d8142603c9e73a50fb192d3ac86f4a0d" dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", ] [[package]] name = "jsonrpsee-types" -version = "0.15.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e290bba767401b646812f608c099b922d8142603c9e73a50fb192d3ac86f4a0d" +checksum = "5bd522fe1ce3702fd94812965d7bb7a3364b1c9aba743944c5a00529aae80f8c" dependencies = [ "anyhow", "beef", @@ -3945,55 +4458,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ee5feddd5188e62ac08fcf0e56478138e581509d4730f3f7be9b57dd402a4ff" dependencies = [ "http", - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-client-transport 0.15.1", + "jsonrpsee-core 0.15.1", + "jsonrpsee-types 0.15.1", ] [[package]] -name = "jsonrpsee-ws-server" -version = "0.15.1" +name = "jsonrpsee-ws-client" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d488ba74fb369e5ab68926feb75a483458b88e768d44319f37e4ecad283c7325" +checksum = "0b83daeecfc6517cfe210df24e570fb06213533dfb990318fae781f4c7119dd9" dependencies = [ - "futures-channel", - "futures-util", "http", - "jsonrpsee-core", - "jsonrpsee-types", - "serde_json", - "soketto", - "tokio", - "tokio-stream", - "tokio-util 0.7.1", - "tracing", - "tracing-futures", + "jsonrpsee-client-transport 0.16.2", + "jsonrpsee-core 0.16.2", + "jsonrpsee-types 0.16.2", ] [[package]] name = "k256" -version = "0.10.4" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if 1.0.0", "ecdsa", "elliptic-curve", - "sec1", + "sha2 0.10.6", ] [[package]] name = "keccak" -version = "0.1.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] [[package]] name = "kusama-runtime" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ - "beefy-primitives", "bitvec", "frame-election-provider-support", "frame-executive", @@ -4016,13 +4523,13 @@ dependencies = [ "pallet-election-provider-multi-phase", "pallet-elections-phragmen", "pallet-fast-unstake", - "pallet-gilt", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-membership", "pallet-multisig", + "pallet-nis", "pallet-nomination-pools", "pallet-nomination-pools-runtime-api", "pallet-offences", @@ -4035,7 +4542,6 @@ dependencies = [ "pallet-session", "pallet-society", "pallet-staking", - "pallet-staking-reward-fn", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", @@ -4057,6 +4563,7 @@ dependencies = [ "sp-api", "sp-arithmetic", "sp-authority-discovery", + "sp-beefy", "sp-block-builder", "sp-consensus-babe", "sp-core", @@ -4080,14 +4587,16 @@ dependencies = [ [[package]] name = "kusama-runtime-constants" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "frame-support", "polkadot-primitives", "polkadot-runtime-common", "smallvec", + "sp-core", "sp-runtime", + "sp-weights", ] [[package]] @@ -4101,35 +4610,31 @@ dependencies = [ [[package]] name = "kvdb" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585089ceadba0197ffe9af6740ab350b325e3c1f5fccfbc3522e0250c750409b" +checksum = "e7d770dcb02bf6835887c3a979b5107a04ff4bbde97a5f0928d27404a155add9" dependencies = [ - "parity-util-mem", "smallvec", ] [[package]] name = "kvdb-memorydb" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40d109c87bfb7759edd2a49b2649c1afe25af785d930ad6a38479b4dc70dd873" +checksum = "bf7a85fe66f9ff9cd74e169fdd2c94c6e1e74c412c99a73b4df3200b5d3760b2" dependencies = [ "kvdb", - "parity-util-mem", "parking_lot 0.12.1", ] [[package]] name = "kvdb-rocksdb" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c076cc2cdbac89b9910c853a36c957d3862a779f31c2661174222cefb49ee597" +checksum = "2182b8219fee6bd83aacaab7344e840179ae079d5216aa4e249b4d704646a844" dependencies = [ "kvdb", - "log", "num_cpus", - "parity-util-mem", "parking_lot 0.12.1", "regex", "rocksdb", @@ -4150,15 +4655,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.132" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libloading" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if 1.0.0", "winapi", @@ -4166,13 +4671,19 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" [[package]] -name = "libnghttp2-sys" -version = "0.1.7+1.45.0" +name = "libm" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" + +[[package]] +name = "libnghttp2-sys" +version = "0.1.7+1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57ed28aba195b38d5ff02b9170cbff627e336a20925e43b4945390401c5dc93f" dependencies = [ @@ -4182,16 +4693,15 @@ dependencies = [ [[package]] name = "libp2p" -version = "0.49.0" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec878fda12ebec479186b3914ebc48ff180fa4c51847e11a1a68bf65249e02c1" +checksum = "2e0a0d2f693675f49ded13c5d510c48b78069e23cbd9108d7ccd59f6dc568819" dependencies = [ "bytes", "futures", "futures-timer", - "getrandom 0.2.3", + "getrandom 0.2.8", "instant", - "lazy_static", "libp2p-core", "libp2p-dns", "libp2p-identify", @@ -4201,11 +4711,12 @@ dependencies = [ "libp2p-mplex", "libp2p-noise", "libp2p-ping", + "libp2p-quic", "libp2p-request-response", "libp2p-swarm", - "libp2p-swarm-derive", "libp2p-tcp", "libp2p-wasm-ext", + "libp2p-webrtc", "libp2p-websocket", "libp2p-yamux", "multiaddr", @@ -4216,9 +4727,9 @@ dependencies = [ [[package]] name = "libp2p-core" -version = "0.37.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799676bb0807c788065e57551c6527d461ad572162b0519d1958946ff9e0539d" +checksum = "b6a8fcd392ff67af6cc3f03b1426c41f7f26b6b9aff2dc632c1c56dd649e571f" dependencies = [ "asn1_der", "bs58", @@ -4228,18 +4739,19 @@ dependencies = [ "futures", "futures-timer", "instant", - "lazy_static", "log", "multiaddr", "multihash", "multistream-select", + "once_cell", "parking_lot 0.12.1", "pin-project", "prost", "prost-build", "rand 0.8.5", "rw-stream-sink", - "sha2 0.10.5", + "sec1", + "sha2 0.10.6", "smallvec", "thiserror", "unsigned-varint", @@ -4249,11 +4761,10 @@ dependencies = [ [[package]] name = "libp2p-dns" -version = "0.37.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2322c9fb40d99101def6a01612ee30500c89abbbecb6297b3cd252903a4c1720" +checksum = "8e42a271c1b49f789b92f7fc87749fa79ce5c7bdc88cbdfacb818a4bca47fec5" dependencies = [ - "async-std-resolver", "futures", "libp2p-core", "log", @@ -4264,9 +4775,9 @@ dependencies = [ [[package]] name = "libp2p-identify" -version = "0.40.0" +version = "0.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf9a121f699e8719bda2e6e9e9b6ddafc6cff4602471d6481c1067930ccb29b" +checksum = "c052d0026f4817b44869bfb6810f4e1112f43aec8553f2cb38881c524b563abf" dependencies = [ "asynchronous-codec", "futures", @@ -4274,7 +4785,7 @@ dependencies = [ "libp2p-core", "libp2p-swarm", "log", - "lru 0.8.1", + "lru", "prost", "prost-build", "prost-codec", @@ -4285,9 +4796,9 @@ dependencies = [ [[package]] name = "libp2p-kad" -version = "0.41.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6721c200e2021f6c3fab8b6cf0272ead8912d871610ee194ebd628cecf428f22" +checksum = "2766dcd2be8c87d5e1f35487deb22d765f49c6ae1251b3633efe3b25698bd3d2" dependencies = [ "arrayvec 0.7.2", "asynchronous-codec", @@ -4303,7 +4814,7 @@ dependencies = [ "prost", "prost-build", "rand 0.8.5", - "sha2 0.10.5", + "sha2 0.10.6", "smallvec", "thiserror", "uint", @@ -4313,13 +4824,11 @@ dependencies = [ [[package]] name = "libp2p-mdns" -version = "0.41.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "761704e727f7d68d58d7bc2231eafae5fc1b9814de24290f126df09d4bd37a15" +checksum = "04f378264aade9872d6ccd315c0accc18be3a35d15fc1b9c36e5b6f983b62b5b" dependencies = [ - "async-io", "data-encoding", - "dns-parser", "futures", "if-watch", "libp2p-core", @@ -4328,14 +4837,16 @@ dependencies = [ "rand 0.8.5", "smallvec", "socket2", + "tokio", + "trust-dns-proto", "void", ] [[package]] name = "libp2p-metrics" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee31b08e78b7b8bfd1c4204a9dd8a87b4fcdf6dafc57eb51701c1c264a81cb9" +checksum = "5ad8a64f29da86005c86a4d2728b8a0719e9b192f4092b609fd8790acb9dec55" dependencies = [ "libp2p-core", "libp2p-identify", @@ -4347,9 +4858,9 @@ dependencies = [ [[package]] name = "libp2p-mplex" -version = "0.37.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692664acfd98652de739a8acbb0a0d670f1d67190a49be6b4395e22c37337d89" +checksum = "03805b44107aa013e7cbbfa5627b31c36cbedfdfb00603c0311998882bc4bace" dependencies = [ "asynchronous-codec", "bytes", @@ -4365,31 +4876,32 @@ dependencies = [ [[package]] name = "libp2p-noise" -version = "0.40.0" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048155686bd81fe6cb5efdef0c6290f25ad32a0a42e8f4f72625cf6a505a206f" +checksum = "a978cb57efe82e892ec6f348a536bfbd9fee677adbe5689d7a93ad3a9bffbf2e" dependencies = [ "bytes", "curve25519-dalek 3.2.0", "futures", - "lazy_static", "libp2p-core", "log", + "once_cell", "prost", "prost-build", "rand 0.8.5", - "sha2 0.10.5", + "sha2 0.10.6", "snow", "static_assertions", - "x25519-dalek", + "thiserror", + "x25519-dalek 1.1.1", "zeroize", ] [[package]] name = "libp2p-ping" -version = "0.40.1" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7228b9318d34689521349a86eb39a3c3a802c9efc99a0568062ffb80913e3f91" +checksum = "929fcace45a112536e22b3dcfd4db538723ef9c3cb79f672b98be2cc8e25f37f" dependencies = [ "futures", "futures-timer", @@ -4401,11 +4913,32 @@ dependencies = [ "void", ] +[[package]] +name = "libp2p-quic" +version = "0.7.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01e7c867e95c8130667b24409d236d37598270e6da69b3baf54213ba31ffca59" +dependencies = [ + "bytes", + "futures", + "futures-timer", + "if-watch", + "libp2p-core", + "libp2p-tls", + "log", + "parking_lot 0.12.1", + "quinn-proto", + "rand 0.8.5", + "rustls 0.20.8", + "thiserror", + "tokio", +] + [[package]] name = "libp2p-request-response" -version = "0.22.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8827af16a017b65311a410bb626205a9ad92ec0473967618425039fa5231adc1" +checksum = "3236168796727bfcf4927f766393415361e2c644b08bedb6a6b13d957c9a4884" dependencies = [ "async-trait", "bytes", @@ -4421,9 +4954,9 @@ dependencies = [ [[package]] name = "libp2p-swarm" -version = "0.40.1" +version = "0.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46d13df7c37807965d82930c0e4b04a659efcb6cca237373b206043db5398ecf" +checksum = "b2a35472fe3276b3855c00f1c032ea8413615e030256429ad5349cdf67c6e1a0" dependencies = [ "either", "fnv", @@ -4431,32 +4964,33 @@ dependencies = [ "futures-timer", "instant", "libp2p-core", + "libp2p-swarm-derive", "log", "pin-project", "rand 0.8.5", "smallvec", "thiserror", + "tokio", "void", ] [[package]] name = "libp2p-swarm-derive" -version = "0.30.1" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0eddc4497a8b5a506013c40e8189864f9c3a00db2b25671f428ae9007f3ba32" +checksum = "9d527d5827582abd44a6d80c07ff8b50b4ee238a8979e05998474179e79dc400" dependencies = [ "heck 0.4.0", - "quote", - "syn", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "libp2p-tcp" -version = "0.37.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9839d96761491c6d3e238e70554b856956fca0ab60feb9de2cd08eed4473fa92" +checksum = "b4b257baf6df8f2df39678b86c578961d48cc8b68642a12f0f763f56c8e5858d" dependencies = [ - "async-io", "futures", "futures-timer", "if-watch", @@ -4464,13 +4998,32 @@ dependencies = [ "libp2p-core", "log", "socket2", + "tokio", +] + +[[package]] +name = "libp2p-tls" +version = "0.1.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7905ce0d040576634e8a3229a7587cc8beab83f79db6023800f1792895defa8" +dependencies = [ + "futures", + "futures-rustls", + "libp2p-core", + "rcgen 0.10.0", + "ring", + "rustls 0.20.8", + "thiserror", + "webpki 0.22.0", + "x509-parser 0.14.0", + "yasna", ] [[package]] name = "libp2p-wasm-ext" -version = "0.37.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b5b8e7a73e379e47b1b77f8a82c4721e97eca01abcd18e9cd91a23ca6ce97" +checksum = "1bb1a35299860e0d4b3c02a3e74e3b293ad35ae0cee8a056363b0c862d082069" dependencies = [ "futures", "js-sys", @@ -4480,11 +5033,42 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "libp2p-webrtc" +version = "0.4.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb6cd86dd68cba72308ea05de1cebf3ba0ae6e187c40548167955d4e3970f6a" +dependencies = [ + "async-trait", + "asynchronous-codec", + "bytes", + "futures", + "futures-timer", + "hex", + "if-watch", + "libp2p-core", + "libp2p-noise", + "log", + "multihash", + "prost", + "prost-build", + "prost-codec", + "rand 0.8.5", + "rcgen 0.9.3", + "serde", + "stun", + "thiserror", + "tinytemplate", + "tokio", + "tokio-util", + "webrtc", +] + [[package]] name = "libp2p-websocket" -version = "0.39.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3758ae6f89b2531a24b6d9f5776bda6a626b60a57600d7185d43dfa75ca5ecc4" +checksum = "1d705506030d5c0aaf2882437c70dab437605f21c5f9811978f694e6917a3b54" dependencies = [ "either", "futures", @@ -4501,9 +5085,9 @@ dependencies = [ [[package]] name = "libp2p-yamux" -version = "0.41.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6874d66543c4f7e26e3b8ca9a6bead351563a13ab4fafd43c7927f7c0d6c12" +checksum = "4f63594a0aa818642d9d4915c791945053877253f08a3626f13416b5cd928a29" dependencies = [ "futures", "libp2p-core", @@ -4530,12 +5114,12 @@ dependencies = [ [[package]] name = "libsecp256k1" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0452aac8bab02242429380e9b2f94ea20cea2b37e2c1777a1358799bbe97f37" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ "arrayref", - "base64", + "base64 0.13.1", "digest 0.9.0", "hmac-drbg", "libsecp256k1-core", @@ -4543,7 +5127,7 @@ dependencies = [ "libsecp256k1-gen-genmult", "rand 0.8.5", "serde", - "sha2 0.9.8", + "sha2 0.9.9", "typenum", ] @@ -4578,9 +5162,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.3" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66" +checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" dependencies = [ "cc", "libc", @@ -4590,18 +5174,18 @@ dependencies = [ [[package]] name = "link-cplusplus" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" dependencies = [ "cc", ] [[package]] name = "linked-hash-map" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linked_hash_set" @@ -4628,12 +5212,19 @@ version = "0.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "lock_api" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ + "autocfg", "scopeguard", ] @@ -4647,15 +5238,6 @@ dependencies = [ "value-bag", ] -[[package]] -name = "lru" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" -dependencies = [ - "hashbrown", -] - [[package]] name = "lru" version = "0.8.1" @@ -4703,12 +5285,6 @@ dependencies = [ "libc", ] -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - [[package]] name = "match_cfg" version = "0.1.0" @@ -4739,67 +5315,65 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest 0.10.6", +] + [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memfd" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "480b5a5de855d11ff13195950bdc8b98b5e942ef47afc447f6615cdcc4e15d80" +checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" dependencies = [ - "rustix", + "rustix 0.36.6", ] [[package]] -name = "memmap" -version = "0.7.0" +name = "memmap2" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" dependencies = [ "libc", - "winapi", ] [[package]] -name = "memmap2" -version = "0.5.0" +name = "memoffset" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4647a11b578fead29cdbb34d4adef8dd3dc35b876c9c6d5240d83f205abfe96e" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "libc", + "autocfg", ] [[package]] name = "memoffset" -version = "0.6.4" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ "autocfg", ] [[package]] name = "memory-db" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac11bb793c28fa095b7554466f53b3a60a2cd002afdac01bcf135cbd73a269" +checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" dependencies = [ "hash-db", "hashbrown", - "parity-util-mem", -] - -[[package]] -name = "memory-lru" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce95ae042940bad7e312857b929ee3d11b8f799a80cb7b9c7ec5125516906395" -dependencies = [ - "lru 0.8.1", ] [[package]] @@ -4827,7 +5401,6 @@ dependencies = [ "async-std", "async-trait", "bp-messages", - "bp-runtime", "finality-relay", "futures", "hex", @@ -4855,14 +5428,13 @@ version = "0.1.0" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", - "beefy-primitives", - "clap 4.0.26", + "clap 4.1.0", "frame-benchmarking", "frame-benchmarking-cli", - "jsonrpsee", + "jsonrpsee 0.16.2", "millau-runtime", + "mmr-rpc", "node-inspect", - "pallet-mmr-rpc", "pallet-transaction-payment-rpc", "sc-basic-authorship", "sc-cli", @@ -4878,7 +5450,7 @@ dependencies = [ "sc-telemetry", "sc-transaction-pool", "serde_json", - "sp-consensus", + "sp-beefy", "sp-consensus-aura", "sp-core", "sp-finality-grandpa", @@ -4892,9 +5464,9 @@ dependencies = [ name = "millau-runtime" version = "0.1.0" dependencies = [ - "beefy-primitives", "bp-messages", "bp-millau", + "bp-parachains", "bp-polkadot-core", "bp-relayers", "bp-rialto", @@ -4909,7 +5481,6 @@ dependencies = [ "frame-system", "frame-system-rpc-runtime-api", "hex-literal", - "log", "pallet-aura", "pallet-balances", "pallet-beefy", @@ -4927,16 +5498,17 @@ dependencies = [ "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", + "pallet-utility", "pallet-xcm", "parity-scale-codec", "scale-info", "sp-api", + "sp-beefy", "sp-block-builder", "sp-consensus-aura", "sp-core", "sp-inherents", "sp-io", - "sp-mmr-primitives", "sp-offchain", "sp-runtime", "sp-session", @@ -4964,12 +5536,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.4.4" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ "adler", - "autocfg", ] [[package]] @@ -4984,6 +5555,41 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "mmr-gadget" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +dependencies = [ + "futures", + "log", + "parity-scale-codec", + "sc-client-api", + "sc-offchain", + "sp-api", + "sp-beefy", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-mmr-primitives", + "sp-runtime", +] + +[[package]] +name = "mmr-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +dependencies = [ + "anyhow", + "jsonrpsee 0.16.2", + "parity-scale-codec", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-mmr-primitives", + "sp-runtime", +] + [[package]] name = "mockall" version = "0.11.3" @@ -5006,21 +5612,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "832663583d5fa284ca8810bf7015e46c9fff9622d3cf34bd1eea5003fec06dd0" dependencies = [ "cfg-if 1.0.0", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "multiaddr" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c580bfdd8803cce319b047d239559a22f809094aaea4ac13902a1fdcfcd4261" +checksum = "a4aebdb21e90f81d13ed01dc84123320838e53963c2ca94b60b305d3fa64f31e" dependencies = [ "arrayref", - "bs58", "byteorder", "data-encoding", + "multibase", "multihash", "percent-encoding", "serde", @@ -5050,9 +5656,9 @@ dependencies = [ "blake2s_simd", "blake3", "core2", - "digest 0.10.3", + "digest 0.10.6", "multihash-derive", - "sha2 0.10.5", + "sha2 0.10.6", "sha3", "unsigned-varint", ] @@ -5065,9 +5671,9 @@ checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" dependencies = [ "proc-macro-crate", "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", "synstructure", ] @@ -5079,9 +5685,9 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "multistream-select" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bc41247ec209813e2fd414d6e16b9d94297dacf3cd613fa6ef09cd4d9755c10" +checksum = "c8552ab875c1313b97b8d20cb857b9fd63e2d1d6a0a1b53ce9821e575405f27a" dependencies = [ "bytes", "futures", @@ -5101,7 +5707,7 @@ dependencies = [ "matrixmultiply", "nalgebra-macros", "num-complex", - "num-rational 0.4.0", + "num-rational", "num-traits", "rand 0.8.5", "rand_distr", @@ -5115,9 +5721,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -5194,30 +5800,31 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92b654097027250401127914afb37cb1f311df6610a9891ff07a757e94199027" dependencies = [ - "async-io", "bytes", "futures", "libc", "log", + "tokio", ] [[package]] name = "nix" -version = "0.24.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ "bitflags", "cfg-if 1.0.0", "libc", + "memoffset 0.6.5", ] [[package]] name = "node-inspect" version = "0.9.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "clap 4.0.26", + "clap 4.1.0", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -5229,12 +5836,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - [[package]] name = "nohash-hasher" version = "0.2.0" @@ -5243,13 +5844,12 @@ checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "nom" -version = "7.1.0" +version = "7.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c" dependencies = [ "memchr", "minimal-lexical", - "version_check", ] [[package]] @@ -5260,24 +5860,13 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "ntapi" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" dependencies = [ "winapi", ] -[[package]] -name = "num-bigint" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-bigint" version = "0.4.3" @@ -5291,112 +5880,118 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" +checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" dependencies = [ "num-traits", ] [[package]] name = "num-format" -version = "0.4.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" dependencies = [ - "arrayvec 0.4.12", - "itoa 0.4.8", + "arrayvec 0.7.2", + "itoa", ] [[package]] name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.2.4" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", - "num-bigint 0.2.6", - "num-integer", "num-traits", ] [[package]] name = "num-rational" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", - "num-bigint 0.4.3", + "num-bigint", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", - "libm", + "libm 0.2.6", ] [[package]] name = "num_cpus" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] [[package]] name = "num_threads" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ba99ba6393e2c3734791401b66902d981cb03bf190af674ca69949b6d5fb15" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" dependencies = [ "libc", ] [[package]] name = "object" -version = "0.27.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ + "crc32fast", + "hashbrown", + "indexmap", "memchr", ] [[package]] name = "object" -version = "0.29.0" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "2b8c786513eb403643f2a88c244c2aaa270ef2153f55094587d0c48a3cf22a83" dependencies = [ - "crc32fast", - "hashbrown", - "indexmap", "memchr", ] +[[package]] +name = "oid-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e20717fa0541f39bd146692035c37bedfa532b3e5071b35761082407546b2a" +dependencies = [ + "asn1-rs 0.3.1", +] + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs 0.5.1", +] + [[package]] name = "once_cell" -version = "1.13.1" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "opaque-debug" @@ -5412,15 +6007,15 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl-probe" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.71" +version = "0.9.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df13d165e607909b363a4757a6f133f8a818a74e9d3a98d09c6128e15fa4c73" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" dependencies = [ "autocfg", "cc", @@ -5431,9 +6026,9 @@ dependencies = [ [[package]] name = "orchestra" -version = "0.0.2" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aab54694ddaa8a9b703724c6ef04272b2d27bc32d2c855aae5cdd1857216b43" +checksum = "17e7d5b6bb115db09390bed8842c94180893dd83df3dfce7354f2a2aa090a4ee" dependencies = [ "async-trait", "dyn-clonable", @@ -5448,17 +6043,17 @@ dependencies = [ [[package]] name = "orchestra-proc-macro" -version = "0.0.2" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a702b2f6bf592b3eb06c00d80d05afaf7a8eff6b41bb361e397d799acc21b45a" +checksum = "c2af4dabb2286b0be0e9711d2d24e25f6217048b71210cffd3daddc3b5c84e1f" dependencies = [ "expander 0.0.6", "itertools", "petgraph", "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -5472,30 +6067,62 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.0.0" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] -name = "pallet-aura" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ - "frame-support", - "frame-system", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "sp-application-crypto", - "sp-consensus-aura", - "sp-runtime", - "sp-std", -] + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "p384" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2 0.10.6", +] + +[[package]] +name = "packed_simd_2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" +dependencies = [ + "cfg-if 1.0.0", + "libm 0.1.4", +] + +[[package]] +name = "pallet-aura" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +dependencies = [ + "frame-support", + "frame-system", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-aura", + "sp-runtime", + "sp-std", +] [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-support", "frame-system", @@ -5511,7 +6138,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-support", "frame-system", @@ -5526,7 +6153,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -5550,7 +6177,7 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5570,7 +6197,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -5585,15 +6212,15 @@ dependencies = [ [[package]] name = "pallet-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "beefy-primitives", "frame-support", "frame-system", "pallet-session", "parity-scale-codec", "scale-info", "serde", + "sp-beefy", "sp-runtime", "sp-std", ] @@ -5601,11 +6228,10 @@ dependencies = [ [[package]] name = "pallet-beefy-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "beefy-merkle-tree", - "beefy-primitives", "frame-support", "frame-system", "log", @@ -5615,6 +6241,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", + "sp-beefy", "sp-core", "sp-io", "sp-runtime", @@ -5624,7 +6251,7 @@ dependencies = [ [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -5643,22 +6270,20 @@ dependencies = [ name = "pallet-bridge-beefy" version = "0.1.0" dependencies = [ - "beefy-merkle-tree", - "beefy-primitives", "bp-beefy", "bp-runtime", "bp-test-utils", - "ckb-merkle-mountain-range", + "ckb-merkle-mountain-range 0.3.2", "frame-support", "frame-system", "log", "pallet-beefy-mmr", "pallet-mmr", "parity-scale-codec", - "primitive-types", "rand 0.8.5", "scale-info", "serde", + "sp-beefy", "sp-core", "sp-io", "sp-runtime", @@ -5724,6 +6349,7 @@ dependencies = [ "pallet-bridge-grandpa", "parity-scale-codec", "scale-info", + "sp-core", "sp-io", "sp-runtime", "sp-std", @@ -5742,7 +6368,6 @@ dependencies = [ "frame-system", "log", "pallet-balances", - "pallet-bridge-messages", "parity-scale-codec", "scale-info", "sp-arithmetic", @@ -5755,7 +6380,7 @@ dependencies = [ [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -5774,7 +6399,7 @@ dependencies = [ [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -5791,7 +6416,7 @@ dependencies = [ [[package]] name = "pallet-conviction-voting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "assert_matches", "frame-benchmarking", @@ -5808,7 +6433,7 @@ dependencies = [ [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -5826,7 +6451,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5835,7 +6460,7 @@ dependencies = [ "log", "pallet-election-provider-support-benchmarking", "parity-scale-codec", - "rand 0.7.3", + "rand 0.8.5", "scale-info", "sp-arithmetic", "sp-core", @@ -5843,14 +6468,13 @@ dependencies = [ "sp-npos-elections", "sp-runtime", "sp-std", - "static_assertions", "strum 0.24.1", ] [[package]] name = "pallet-election-provider-support-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5863,7 +6487,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -5881,7 +6505,7 @@ dependencies = [ [[package]] name = "pallet-fast-unstake" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5896,25 +6520,10 @@ dependencies = [ "sp-std", ] -[[package]] -name = "pallet-gilt" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "sp-arithmetic", - "sp-runtime", - "sp-std", -] - [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -5937,7 +6546,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "enumflags2", "frame-benchmarking", @@ -5953,7 +6562,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -5973,7 +6582,7 @@ dependencies = [ [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -5990,7 +6599,7 @@ dependencies = [ [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -6007,9 +6616,8 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "ckb-merkle-mountain-range", "frame-benchmarking", "frame-support", "frame-system", @@ -6023,32 +6631,33 @@ dependencies = [ ] [[package]] -name = "pallet-mmr-rpc" -version = "3.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +name = "pallet-multisig" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "jsonrpsee", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", "parity-scale-codec", - "serde", - "sp-api", - "sp-blockchain", - "sp-core", - "sp-mmr-primitives", + "scale-info", + "sp-io", "sp-runtime", + "sp-std", ] [[package]] -name = "pallet-multisig" +name = "pallet-nis" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", - "log", "parity-scale-codec", "scale-info", - "sp-io", + "sp-arithmetic", + "sp-core", "sp-runtime", "sp-std", ] @@ -6056,7 +6665,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools" version = "1.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-support", "frame-system", @@ -6073,7 +6682,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "parity-scale-codec", "sp-api", @@ -6083,7 +6692,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-support", "frame-system", @@ -6100,7 +6709,7 @@ dependencies = [ [[package]] name = "pallet-preimage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -6117,7 +6726,7 @@ dependencies = [ [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -6132,7 +6741,7 @@ dependencies = [ [[package]] name = "pallet-randomness-collective-flip" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-support", "frame-system", @@ -6146,7 +6755,7 @@ dependencies = [ [[package]] name = "pallet-ranked-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -6164,7 +6773,7 @@ dependencies = [ [[package]] name = "pallet-recovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -6179,11 +6788,12 @@ dependencies = [ [[package]] name = "pallet-referenda" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "log", "parity-scale-codec", "scale-info", "serde", @@ -6196,7 +6806,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -6207,12 +6817,13 @@ dependencies = [ "sp-io", "sp-runtime", "sp-std", + "sp-weights", ] [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-support", "frame-system", @@ -6248,7 +6859,7 @@ dependencies = [ [[package]] name = "pallet-society" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-support", "frame-system", @@ -6262,7 +6873,7 @@ dependencies = [ [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6284,18 +6895,18 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "log", "sp-arithmetic", @@ -6304,7 +6915,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-support", "frame-system", @@ -6318,7 +6929,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -6336,7 +6947,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -6355,7 +6966,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-support", "frame-system", @@ -6371,9 +6982,9 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "jsonrpsee", + "jsonrpsee 0.16.2", "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", "sp-api", @@ -6381,23 +6992,25 @@ dependencies = [ "sp-core", "sp-rpc", "sp-runtime", + "sp-weights", ] [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", "sp-api", "sp-runtime", + "sp-weights", ] [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -6414,7 +7027,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -6430,7 +7043,7 @@ dependencies = [ [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -6445,7 +7058,7 @@ dependencies = [ [[package]] name = "pallet-whitelist" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-benchmarking", "frame-support", @@ -6459,9 +7072,10 @@ dependencies = [ [[package]] name = "pallet-xcm" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "log", @@ -6479,7 +7093,7 @@ dependencies = [ [[package]] name = "parachain-info" version = "0.1.0" -source = "git+https://github.com/paritytech//cumulus?branch=cumulus-locked-for-gav-xcm-v3-and-bridges#6ece1b652f805ffa7bd0052c77a38bd9fce11542" +source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -6506,11 +7120,11 @@ dependencies = [ [[package]] name = "parity-db" -version = "0.3.17" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8fdb726a43661fa54b43e7114e6b88b2289cae388eb3ad766d9d1754d83fce" +checksum = "3a7511a0bec4a336b5929999d02b560d2439c993cccf98c26481484e811adc43" dependencies = [ - "blake2-rfc", + "blake2", "crc32fast", "fs2", "hex", @@ -6545,9 +7159,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9299338969a3d2f491d65f140b00ddec470858402f888af98e8642fb5e8965cd" dependencies = [ "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -6566,7 +7180,7 @@ dependencies = [ "ethereum-types", "hashbrown", "impl-trait-for-tuples", - "lru 0.8.1", + "lru", "parity-util-mem-derive", "parking_lot 0.12.1", "primitive-types", @@ -6580,8 +7194,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "proc-macro2", - "syn", + "proc-macro2 1.0.49", + "syn 1.0.107", "synstructure", ] @@ -6605,7 +7219,7 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core 0.8.5", + "parking_lot_core 0.8.6", ] [[package]] @@ -6615,14 +7229,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.1", + "parking_lot_core 0.9.6", ] [[package]] name = "parking_lot_core" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ "cfg-if 1.0.0", "instant", @@ -6634,39 +7248,39 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.1" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall", "smallvec", - "windows-sys 0.32.0", + "windows-sys 0.42.0", ] [[package]] name = "paste" -version = "1.0.6" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" [[package]] name = "pbkdf2" -version = "0.4.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" dependencies = [ - "crypto-mac 0.8.0", + "crypto-mac 0.11.1", ] [[package]] name = "pbkdf2" -version = "0.8.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "crypto-mac 0.11.1", + "digest 0.10.6", ] [[package]] @@ -6675,6 +7289,24 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "pem-rfc7468" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.2.0" @@ -6683,18 +7315,19 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.1.3" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +checksum = "4257b4a04d91f7e9e6290be5d3da4804dd5784fafde3a497d73eb2b4a158c30a" dependencies = [ + "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.1.0" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +checksum = "241cda393b0cdd65e62e07e12454f1f25d57017dcc514b1514cd3c4645e3a0a6" dependencies = [ "pest", "pest_generator", @@ -6702,33 +7335,33 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.1.3" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +checksum = "46b53634d8c8196302953c74d5352f33d0c512a9499bd2ce468fc9f4128fa27c" dependencies = [ "pest", "pest_meta", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "pest_meta" -version = "2.1.3" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +checksum = "0ef4f1332a8d4678b41966bb4cc1d0676880e84183a1ecc3f4b69f03e99c7a51" dependencies = [ - "maplit", + "once_cell", "pest", - "sha-1 0.8.2", + "sha2 0.10.6", ] [[package]] name = "petgraph" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" dependencies = [ "fixedbitset", "indexmap", @@ -6749,9 +7382,9 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -6774,20 +7407,19 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs8" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ "der", "spki", - "zeroize", ] [[package]] name = "pkg-config" -version = "0.3.22" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "platforms" @@ -6795,16 +7427,22 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8d0eef3571242013a0d5dc84861c3ae4a652e56e12adf8bdc26ff5f8cb34c94" +[[package]] +name = "platforms" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" + [[package]] name = "polkadot-approval-distribution" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "futures", + "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", - "polkadot-node-subsystem-util", "polkadot-primitives", "rand 0.8.5", "tracing-gum", @@ -6812,8 +7450,8 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "futures", "polkadot-node-network-protocol", @@ -6826,13 +7464,13 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "derive_more", "fatality", "futures", - "lru 0.8.1", + "lru", "parity-scale-codec", "polkadot-erasure-coding", "polkadot-node-network-protocol", @@ -6849,12 +7487,12 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "fatality", "futures", - "lru 0.8.1", + "lru", "parity-scale-codec", "polkadot-erasure-coding", "polkadot-node-network-protocol", @@ -6870,10 +7508,10 @@ dependencies = [ [[package]] name = "polkadot-cli" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ - "clap 4.0.26", + "clap 4.1.0", "frame-benchmarking-cli", "futures", "log", @@ -6883,12 +7521,13 @@ dependencies = [ "polkadot-performance-test", "polkadot-service", "sc-cli", + "sc-executor", "sc-service", "sc-sysinfo", "sc-tracing", "sp-core", + "sp-io", "sp-keyring", - "sp-trie", "substrate-build-script-utils", "thiserror", "try-runtime-cli", @@ -6896,14 +7535,15 @@ dependencies = [ [[package]] name = "polkadot-client" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ - "beefy-primitives", + "async-trait", "frame-benchmarking", "frame-benchmarking-cli", "frame-system", "frame-system-rpc-runtime-api", + "futures", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "polkadot-core-primitives", @@ -6917,6 +7557,7 @@ dependencies = [ "sc-service", "sp-api", "sp-authority-discovery", + "sp-beefy", "sp-block-builder", "sp-blockchain", "sp-consensus", @@ -6936,8 +7577,8 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "always-assert", "bitvec", @@ -6958,11 +7599,10 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "parity-scale-codec", - "parity-util-mem", "scale-info", "sp-core", "sp-runtime", @@ -6971,15 +7611,15 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "derive_more", "fatality", "futures", "futures-timer", "indexmap", - "lru 0.8.1", + "lru", "parity-scale-codec", "polkadot-erasure-coding", "polkadot-node-network-protocol", @@ -6996,8 +7636,8 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", @@ -7010,8 +7650,8 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "futures", "futures-timer", @@ -7030,8 +7670,8 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "always-assert", "async-trait", @@ -7040,9 +7680,9 @@ dependencies = [ "futures", "parity-scale-codec", "parking_lot 0.12.1", + "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-subsystem", - "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-primitives", "sc-network", @@ -7054,8 +7694,8 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "futures", "parity-scale-codec", @@ -7072,15 +7712,15 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "bitvec", "derive_more", "futures", "futures-timer", "kvdb", - "lru 0.8.1", + "lru", "merlin", "parity-scale-codec", "polkadot-node-jaeger", @@ -7101,8 +7741,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "bitvec", "futures", @@ -7121,8 +7761,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "bitvec", "fatality", @@ -7140,8 +7780,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "futures", "polkadot-node-subsystem", @@ -7155,16 +7795,17 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "async-trait", "futures", + "futures-timer", "parity-scale-codec", "polkadot-node-core-pvf", + "polkadot-node-metrics", "polkadot-node-primitives", "polkadot-node-subsystem", - "polkadot-node-subsystem-util", "polkadot-parachain", "polkadot-primitives", "sp-maybe-compressed-blob", @@ -7173,12 +7814,12 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "futures", + "polkadot-node-metrics", "polkadot-node-subsystem", - "polkadot-node-subsystem-util", "polkadot-primitives", "sc-client-api", "sc-consensus-babe", @@ -7188,8 +7829,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "futures", "futures-timer", @@ -7205,13 +7846,13 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "fatality", "futures", "kvdb", - "lru 0.8.1", + "lru", "parity-scale-codec", "polkadot-node-primitives", "polkadot-node-subsystem", @@ -7224,25 +7865,25 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "async-trait", "futures", "futures-timer", "polkadot-node-subsystem", + "polkadot-overseer", "polkadot-primitives", "sp-blockchain", "sp-inherents", - "sp-runtime", "thiserror", "tracing-gum", ] [[package]] name = "polkadot-node-core-provisioner" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "bitvec", "fatality", @@ -7259,13 +7900,12 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "always-assert", "assert_matches", - "async-process", - "async-std", + "cpu-time", "futures", "futures-timer", "parity-scale-codec", @@ -7286,13 +7926,14 @@ dependencies = [ "sp-tracing", "sp-wasm-interface", "tempfile", + "tokio", "tracing-gum", ] [[package]] name = "polkadot-node-core-pvf-checker" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "futures", "polkadot-node-primitives", @@ -7307,15 +7948,14 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "futures", - "memory-lru", - "parity-util-mem", + "lru", + "polkadot-node-metrics", "polkadot-node-subsystem", "polkadot-node-subsystem-types", - "polkadot-node-subsystem-util", "polkadot-primitives", "sp-consensus-babe", "tracing-gum", @@ -7323,10 +7963,9 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ - "async-std", "lazy_static", "log", "mick-jaeger", @@ -7337,12 +7976,13 @@ dependencies = [ "sc-network", "sp-core", "thiserror", + "tokio", ] [[package]] name = "polkadot-node-metrics" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "bs58", "futures", @@ -7360,8 +8000,8 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "async-trait", "derive_more", @@ -7383,8 +8023,8 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "bounded-vec", "futures", @@ -7405,8 +8045,8 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -7415,8 +8055,8 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "async-trait", "derive_more", @@ -7438,19 +8078,19 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "async-trait", "derive_more", "fatality", "futures", + "futures-channel", "itertools", "kvdb", - "lru 0.8.1", + "lru", "parity-db", "parity-scale-codec", - "parity-util-mem", "parking_lot 0.11.2", "pin-project", "polkadot-node-jaeger", @@ -7471,15 +8111,14 @@ dependencies = [ [[package]] name = "polkadot-overseer" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "async-trait", "futures", "futures-timer", - "lru 0.8.1", + "lru", "orchestra", - "parity-util-mem", "parking_lot 0.12.1", "polkadot-node-metrics", "polkadot-node-network-protocol", @@ -7489,18 +8128,18 @@ dependencies = [ "sc-client-api", "sp-api", "sp-core", + "tikv-jemalloc-ctl", "tracing-gum", ] [[package]] name = "polkadot-parachain" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "derive_more", "frame-support", "parity-scale-codec", - "parity-util-mem", "polkadot-core-primitives", "scale-info", "serde", @@ -7511,29 +8150,27 @@ dependencies = [ [[package]] name = "polkadot-performance-test" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ - "env_logger 0.9.0", + "env_logger 0.9.3", "kusama-runtime", "log", "polkadot-erasure-coding", "polkadot-node-core-pvf", "polkadot-node-primitives", - "quote", + "quote 1.0.23", "thiserror", ] [[package]] name = "polkadot-primitives" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "bitvec", - "frame-system", "hex-literal", "parity-scale-codec", - "parity-util-mem", "polkadot-core-primitives", "polkadot-parachain", "scale-info", @@ -7550,19 +8187,17 @@ dependencies = [ "sp-runtime", "sp-staking", "sp-std", - "sp-trie", - "sp-version", ] [[package]] name = "polkadot-rpc" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", - "jsonrpsee", - "pallet-mmr-rpc", + "jsonrpsee 0.16.2", + "mmr-rpc", "pallet-transaction-payment-rpc", "polkadot-primitives", "sc-chain-spec", @@ -7588,10 +8223,9 @@ dependencies = [ [[package]] name = "polkadot-runtime" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ - "beefy-primitives", "bitvec", "frame-election-provider-support", "frame-executive", @@ -7647,6 +8281,7 @@ dependencies = [ "smallvec", "sp-api", "sp-authority-discovery", + "sp-beefy", "sp-block-builder", "sp-consensus-babe", "sp-core", @@ -7670,10 +8305,9 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ - "beefy-primitives", "bitvec", "frame-election-provider-support", "frame-support", @@ -7688,6 +8322,7 @@ dependencies = [ "pallet-election-provider-multi-phase", "pallet-session", "pallet-staking", + "pallet-staking-reward-fn", "pallet-timestamp", "pallet-transaction-payment", "pallet-treasury", @@ -7701,6 +8336,7 @@ dependencies = [ "serde_derive", "slot-range-helper", "sp-api", + "sp-beefy", "sp-core", "sp-inherents", "sp-io", @@ -7715,20 +8351,22 @@ dependencies = [ [[package]] name = "polkadot-runtime-constants" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "frame-support", "polkadot-primitives", "polkadot-runtime-common", "smallvec", + "sp-core", "sp-runtime", + "sp-weights", ] [[package]] name = "polkadot-runtime-metrics" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "bs58", "parity-scale-codec", @@ -7739,8 +8377,8 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "bitflags", "bitvec", @@ -7779,19 +8417,19 @@ dependencies = [ [[package]] name = "polkadot-service" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "async-trait", "beefy-gadget", - "beefy-primitives", "frame-support", "frame-system-rpc-runtime-api", "futures", "hex-literal", "kvdb", "kvdb-rocksdb", - "lru 0.8.1", + "lru", + "mmr-gadget", "pallet-babe", "pallet-im-online", "pallet-staking", @@ -7856,6 +8494,7 @@ dependencies = [ "serde_json", "sp-api", "sp-authority-discovery", + "sp-beefy", "sp-block-builder", "sp-blockchain", "sp-consensus", @@ -7865,6 +8504,7 @@ dependencies = [ "sp-inherents", "sp-io", "sp-keystore", + "sp-mmr-primitives", "sp-offchain", "sp-runtime", "sp-session", @@ -7880,8 +8520,8 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "arrayvec 0.5.2", "fatality", @@ -7901,8 +8541,8 @@ dependencies = [ [[package]] name = "polkadot-statement-table" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -7911,15 +8551,16 @@ dependencies = [ [[package]] name = "polling" -version = "2.2.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" dependencies = [ + "autocfg", "cfg-if 1.0.0", "libc", "log", "wepoll-ffi", - "winapi", + "windows-sys 0.42.0", ] [[package]] @@ -7933,6 +8574,17 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "polyval" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" +dependencies = [ + "cpuid-bool", + "opaque-debug 0.3.0", + "universal-hash", +] + [[package]] name = "polyval" version = "0.5.3" @@ -7947,15 +8599,15 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "predicates" -version = "2.1.3" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6bd09a7f7e68f3f0bf710fb7ab9c4615a488b58b5f653382a687701e458c92" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ "difflib", "float-cmp", @@ -7981,11 +8633,21 @@ dependencies = [ "termtree", ] +[[package]] +name = "prettyplease" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" +dependencies = [ + "proc-macro2 1.0.49", + "syn 1.0.107", +] + [[package]] name = "primitive-types" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cfd65aea0c5fa0bfcc7c9e7ca828c921ef778f43d325325ec84bda371bfa75a" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" dependencies = [ "fixed-hash 0.8.0", "impl-codec", @@ -8029,9 +8691,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", "version_check", ] @@ -8041,31 +8703,40 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.49", + "quote 1.0.23", "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.47" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" dependencies = [ "unicode-ident", ] [[package]] name = "prometheus" -version = "0.13.0" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f64969ffd5dd8f39bd57a68ac53c163a095ed9d0fb707146da1b27025a3504" +checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" dependencies = [ "cfg-if 1.0.0", "fnv", "lazy_static", "memchr", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "thiserror", ] @@ -8076,7 +8747,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83cd1b99916654a69008fd66b4f9397fbe08e6e51dfe23d4417acf5d3b8cb87c" dependencies = [ "dtoa", - "itoa 1.0.1", + "itoa", "parking_lot 0.12.1", "prometheus-client-derive-text-encode", ] @@ -8087,16 +8758,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "prost" -version = "0.11.0" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c3c31cdec40583bb68f0b18403400d01ec4289c383aa047560439952c4dd7" +checksum = "21dc42e00223fc37204bd4aa177e69420c604ca4a183209a8f9de30c6d934698" dependencies = [ "bytes", "prost-derive", @@ -8104,9 +8775,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.1" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f835c582e6bd972ba8347313300219fed5bfa52caf175298d860b61ff6069bb" +checksum = "a3f8ad728fb08fe212df3c05169e940fbb6d9d16a877ddde14644a983ba2012e" dependencies = [ "bytes", "heck 0.4.0", @@ -8115,18 +8786,20 @@ dependencies = [ "log", "multimap", "petgraph", + "prettyplease", "prost", "prost-types", "regex", + "syn 1.0.107", "tempfile", "which", ] [[package]] name = "prost-codec" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "011ae9ff8359df7915f97302d591cdd9e0e27fbd5a4ddc5bd13b71079bb20987" +checksum = "0dc34979ff898b6e141106178981ce2596c387ea6e62533facfc61a37fc879c0" dependencies = [ "asynchronous-codec", "bytes", @@ -8137,22 +8810,22 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.0" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7345d5f0e08c0536d7ac7229952590239e77abf0a0100a1b1d890add6ea96364" +checksum = "8bda8c0881ea9f722eb9629376db3d0b903b462477c1aafcb0566610ac28ac5d" dependencies = [ "anyhow", "itertools", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "prost-types" -version = "0.11.1" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dfaa718ad76a44b3415e6c4d53b17c8f99160dcb3a99b10470fce8ad43f6e3e" +checksum = "a5e0526209433e96d83d750dd81a99118edbc55739e7e61a46764fd2ad537788" dependencies = [ "bytes", "prost", @@ -8160,9 +8833,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.16" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd136ff4382c4753fc061cb9e4712ab2af263376b95bbd5bd8cd50c020b78e69" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" dependencies = [ "cc", ] @@ -8173,12 +8846,6 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quicksink" version = "0.1.2" @@ -8190,13 +8857,40 @@ dependencies = [ "pin-project-lite 0.1.12", ] +[[package]] +name = "quinn-proto" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4ced82a24bb281af338b9e8f94429b6eca01b4e66d899f40031f074e74c9" +dependencies = [ + "bytes", + "rand 0.8.5", + "ring", + "rustc-hash", + "rustls 0.20.8", + "slab", + "thiserror", + "tinyvec", + "tracing", + "webpki 0.22.0", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.49", ] [[package]] @@ -8216,7 +8910,6 @@ dependencies = [ "rand_chacha 0.2.2", "rand_core 0.5.1", "rand_hc", - "rand_pcg 0.2.1", ] [[package]] @@ -8227,7 +8920,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -8247,7 +8940,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -8261,18 +8954,18 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.3", + "getrandom 0.2.8", ] [[package]] name = "rand_distr" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "964d548f8e7d12e102ef183a0de7e98180c9f8729f555897a857b96e48122d2f" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" dependencies = [ "num-traits", "rand 0.8.5", @@ -8287,22 +8980,13 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - [[package]] name = "rand_pcg" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" dependencies = [ - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -8313,79 +8997,122 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.5.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" dependencies = [ - "autocfg", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.1" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "lazy_static", "num_cpus", ] [[package]] -name = "redox_syscall" -version = "0.2.10" +name = "rbtag" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "72c64936fcc0b811890a9d90020f3df5cec9c604efde88af7db6a35d365132a3" dependencies = [ - "bitflags", + "rbtag_derive", ] [[package]] -name = "redox_users" -version = "0.4.0" +name = "rbtag_derive" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +checksum = "b75511b710ccca8adbb211e04763bd8c78fed585b0ec188a20ed9b0dd95567c4" dependencies = [ - "getrandom 0.2.3", - "redox_syscall", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", ] [[package]] -name = "reed-solomon-novelpoly" -version = "1.0.0" +name = "rcgen" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd8f48b2066e9f69ab192797d66da804d1935bf22763204ed3675740cb0f221" +checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ - "derive_more", - "fs-err", - "itertools", - "static_init 0.5.2", - "thiserror", + "pem", + "ring", + "time 0.3.17", + "x509-parser 0.13.2", + "yasna", +] + +[[package]] +name = "rcgen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +dependencies = [ + "pem", + "ring", + "time 0.3.17", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.8", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "reed-solomon-novelpoly" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd8f48b2066e9f69ab192797d66da804d1935bf22763204ed3675740cb0f221" +dependencies = [ + "derive_more", + "fs-err", + "itertools", + "static_init 0.5.2", + "thiserror", ] [[package]] name = "ref-cast" -version = "1.0.6" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300f2a835d808734ee295d45007adacb9ebb29dd3ae2424acfa17930cae541da" +checksum = "8c78fb8c9293bcd48ef6fce7b4ca950ceaf21210de6e105a883ee280c0f7b9ed" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.6" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c38e3aecd2b21cb3959637b883bb3714bc7e43f0268b9a29d3743ee3e55cdd2" +checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -8402,9 +9129,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -8434,14 +9161,13 @@ dependencies = [ "bp-bridge-hub-wococo", "bp-header-chain", "bp-messages", - "bp-polkadot-core", - "bp-runtime", + "bp-parachains", + "bp-wococo", "bridge-runtime-common", "parity-scale-codec", "relay-substrate-client", "scale-info", "sp-core", - "sp-finality-grandpa", "sp-runtime", ] @@ -8449,13 +9175,20 @@ dependencies = [ name = "relay-bridge-hub-wococo-client" version = "0.1.0" dependencies = [ + "bp-bridge-hub-rococo", "bp-bridge-hub-wococo", + "bp-header-chain", "bp-messages", + "bp-parachains", + "bp-polkadot-core", + "bp-rococo", + "bp-runtime", + "bridge-runtime-common", "parity-scale-codec", - "relay-bridge-hub-rococo-client", "relay-substrate-client", "scale-info", "sp-core", + "sp-finality-grandpa", "sp-runtime", ] @@ -8464,7 +9197,6 @@ name = "relay-kusama-client" version = "0.1.0" dependencies = [ "bp-kusama", - "frame-support", "relay-substrate-client", "relay-utils", "sp-core", @@ -8492,7 +9224,6 @@ name = "relay-polkadot-client" version = "0.1.0" dependencies = [ "bp-polkadot", - "frame-support", "relay-substrate-client", "relay-utils", "sp-core", @@ -8519,15 +9250,15 @@ dependencies = [ name = "relay-rialto-parachain-client" version = "0.1.0" dependencies = [ + "bp-header-chain", "bp-messages", + "bp-millau", + "bp-polkadot-core", "bp-rialto-parachain", - "frame-support", - "frame-system", - "pallet-transaction-payment", + "bridge-runtime-common", "parity-scale-codec", "relay-substrate-client", - "relay-utils", - "rialto-parachain-runtime", + "scale-info", "sp-core", "sp-runtime", ] @@ -8537,7 +9268,6 @@ name = "relay-rococo-client" version = "0.1.0" dependencies = [ "bp-rococo", - "frame-support", "relay-substrate-client", "relay-utils", "sp-core", @@ -8551,44 +9281,47 @@ dependencies = [ "async-trait", "bp-header-chain", "bp-messages", + "bp-polkadot-core", "bp-runtime", "finality-relay", "frame-support", "frame-system", "futures", - "jsonrpsee", + "jsonrpsee 0.15.1", "log", "num-traits", "pallet-balances", "pallet-bridge-messages", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", + "pallet-utility", "parity-scale-codec", "rand 0.7.3", "relay-utils", "sc-chain-spec", "sc-rpc-api", "sc-transaction-pool-api", + "scale-info", "sp-core", - "sp-finality-grandpa", "sp-rpc", "sp-runtime", - "sp-storage", + "sp-std", "sp-trie", "sp-version", "thiserror", "tokio", + "xcm", ] [[package]] name = "relay-utils" version = "0.1.0" dependencies = [ - "ansi_term 0.12.1", + "ansi_term", "anyhow", "async-std", "async-trait", - "backoff 0.2.1", + "backoff", "bp-runtime", "env_logger 0.8.4", "futures", @@ -8597,10 +9330,11 @@ dependencies = [ "log", "num-traits", "serde_json", + "sp-runtime", "substrate-prometheus-endpoint", "sysinfo", "thiserror", - "time 0.3.7", + "time 0.3.17", "tokio", ] @@ -8609,7 +9343,6 @@ name = "relay-westend-client" version = "0.1.0" dependencies = [ "bp-westend", - "frame-support", "relay-substrate-client", "relay-utils", "sp-core", @@ -8620,29 +9353,11 @@ name = "relay-wococo-client" version = "0.1.0" dependencies = [ "bp-wococo", - "frame-support", "relay-substrate-client", "relay-utils", "sp-core", ] -[[package]] -name = "remote-externalities" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" -dependencies = [ - "env_logger 0.9.0", - "log", - "parity-scale-codec", - "serde", - "serde_json", - "sp-core", - "sp-io", - "sp-runtime", - "sp-version", - "substrate-rpc-client", -] - [[package]] name = "remove_dir_all" version = "0.5.3" @@ -8659,17 +9374,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" dependencies = [ "hostname", - "quick-error 1.2.3", + "quick-error", ] [[package]] name = "rfc6979" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ "crypto-bigint", - "hmac 0.11.0", + "hmac 0.12.1", "zeroize", ] @@ -8677,8 +9392,7 @@ dependencies = [ name = "rialto-bridge-node" version = "0.1.0" dependencies = [ - "beefy-primitives", - "clap 4.0.26", + "clap 4.1.0", "frame-benchmarking", "frame-benchmarking-cli", "frame-support", @@ -8693,6 +9407,7 @@ dependencies = [ "sc-service", "serde_json", "sp-authority-discovery", + "sp-beefy", "sp-consensus-babe", "sp-core", "sp-finality-grandpa", @@ -8704,7 +9419,7 @@ dependencies = [ name = "rialto-parachain-collator" version = "0.1.0" dependencies = [ - "clap 4.0.26", + "clap 4.1.0", "cumulus-client-cli", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", @@ -8712,12 +9427,10 @@ dependencies = [ "cumulus-client-service", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", - "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", - "cumulus-relay-chain-minimal-node", "frame-benchmarking", "frame-benchmarking-cli", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "pallet-transaction-payment-rpc", "parity-scale-codec", @@ -8735,14 +9448,12 @@ dependencies = [ "sc-rpc", "sc-rpc-api", "sc-service", - "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "serde", "sp-api", "sp-block-builder", - "sp-consensus", "sp-consensus-aura", "sp-core", "sp-keystore", @@ -8780,7 +9491,6 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "hex-literal", - "log", "pallet-aura", "pallet-balances", "pallet-bridge-grandpa", @@ -8818,7 +9528,6 @@ dependencies = [ name = "rialto-runtime" version = "0.1.0" dependencies = [ - "beefy-primitives", "bp-messages", "bp-millau", "bp-relayers", @@ -8831,7 +9540,6 @@ dependencies = [ "frame-support", "frame-system", "frame-system-rpc-runtime-api", - "log", "pallet-authority-discovery", "pallet-babe", "pallet-balances", @@ -8857,12 +9565,12 @@ dependencies = [ "scale-info", "sp-api", "sp-authority-discovery", + "sp-beefy", "sp-block-builder", "sp-consensus-babe", "sp-core", "sp-inherents", "sp-io", - "sp-mmr-primitives", "sp-offchain", "sp-runtime", "sp-session", @@ -8893,9 +9601,9 @@ dependencies = [ [[package]] name = "rlp" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999508abb0ae792aabed2460c45b89106d97fe4adac593bdaef433c2605847b5" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ "bytes", "rustc-hex", @@ -8913,27 +9621,63 @@ dependencies = [ [[package]] name = "rpassword" -version = "7.0.0" +version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b763cb66df1c928432cc35053f8bd4cec3335d8559fc16010017d16b3c1680" +checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" dependencies = [ "libc", + "rtoolbox", "winapi", ] +[[package]] +name = "rtcp" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1919efd6d4a6a85d13388f9487549bb8e359f17198cc03ffd72f79b553873691" +dependencies = [ + "bytes", + "thiserror", + "webrtc-util", +] + [[package]] name = "rtnetlink" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" dependencies = [ - "async-global-executor", "futures", "log", "netlink-packet-route", "netlink-proto", "nix", "thiserror", + "tokio", +] + +[[package]] +name = "rtoolbox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "rtp" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a095411ff00eed7b12e4c6a118ba984d113e1079582570d56a5ee723f11f80" +dependencies = [ + "async-trait", + "bytes", + "rand 0.8.5", + "serde", + "thiserror", + "webrtc-util", ] [[package]] @@ -8969,40 +9713,76 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.4", + "semver 1.0.16", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", ] [[package]] name = "rustix" -version = "0.35.9" +version = "0.35.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c825b8aa8010eb9ee99b75f05e10180b9278d161583034d7574c9d617aeada" +checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" dependencies = [ "bitflags", "errno", - "io-lifetimes", + "io-lifetimes 0.7.5", "libc", - "linux-raw-sys", - "windows-sys 0.36.1", + "linux-raw-sys 0.0.46", + "windows-sys 0.42.0", +] + +[[package]] +name = "rustix" +version = "0.36.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes 1.0.4", + "libc", + "linux-raw-sys 0.1.4", + "windows-sys 0.42.0", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64 0.13.1", + "log", + "ring", + "sct 0.6.1", + "webpki 0.21.4", ] [[package]] name = "rustls" -version = "0.20.4" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", - "sct", - "webpki", + "sct 0.7.0", + "webpki 0.22.0", ] [[package]] name = "rustls-native-certs" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca9ebdfa27d3fc180e42879037b5338ab1c040c06affd00d8338598e7800943" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" dependencies = [ "openssl-probe", "rustls-pemfile", @@ -9012,18 +9792,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "0.2.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64", + "base64 0.21.0", ] [[package]] name = "rustversion" -version = "1.0.5" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" [[package]] name = "rw-stream-sink" @@ -9038,9 +9818,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "safe-mix" @@ -9063,7 +9843,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "log", "sp-core", @@ -9074,7 +9854,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "futures", @@ -9085,7 +9865,7 @@ dependencies = [ "parity-scale-codec", "prost", "prost-build", - "rand 0.7.3", + "rand 0.8.5", "sc-client-api", "sc-network-common", "sp-api", @@ -9101,7 +9881,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "futures", "futures-timer", @@ -9124,7 +9904,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -9140,11 +9920,9 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "impl-trait-for-tuples", "memmap2", - "parity-scale-codec", "sc-chain-spec-derive", "sc-network-common", "sc-telemetry", @@ -9157,29 +9935,29 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "chrono", - "clap 4.0.26", + "clap 4.1.0", "fdlimit", "futures", "libp2p", "log", "names", "parity-scale-codec", - "rand 0.7.3", + "rand 0.8.5", "regex", "rpassword", "sc-client-api", @@ -9208,11 +9986,10 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "fnv", "futures", - "hash-db", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -9229,14 +10006,13 @@ dependencies = [ "sp-runtime", "sp-state-machine", "sp-storage", - "sp-trie", "substrate-prometheus-endpoint", ] [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "hash-db", "kvdb", @@ -9261,13 +10037,14 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "futures", "futures-timer", "libp2p", "log", + "mockall", "parking_lot 0.12.1", "sc-client-api", "sc-utils", @@ -9285,7 +10062,7 @@ dependencies = [ [[package]] name = "sc-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "futures", @@ -9314,19 +10091,18 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "fork-tree", "futures", "log", "merlin", - "num-bigint 0.2.6", - "num-rational 0.2.4", + "num-bigint", + "num-rational", "num-traits", "parity-scale-codec", "parking_lot 0.12.1", - "rand 0.7.3", "sc-client-api", "sc-consensus", "sc-consensus-epochs", @@ -9334,7 +10110,6 @@ dependencies = [ "sc-keystore", "sc-telemetry", "schnorrkel", - "serde", "sp-api", "sp-application-crypto", "sp-block-builder", @@ -9345,10 +10120,8 @@ dependencies = [ "sp-consensus-vrf", "sp-core", "sp-inherents", - "sp-io", "sp-keystore", "sp-runtime", - "sp-version", "substrate-prometheus-endpoint", "thiserror", ] @@ -9356,10 +10129,10 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "futures", - "jsonrpsee", + "jsonrpsee 0.16.2", "sc-consensus-babe", "sc-consensus-epochs", "sc-rpc-api", @@ -9378,7 +10151,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "fork-tree", "parity-scale-codec", @@ -9391,7 +10164,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "futures", @@ -9409,16 +10182,14 @@ dependencies = [ "sp-inherents", "sp-runtime", "sp-state-machine", - "thiserror", ] [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "lazy_static", - "lru 0.7.8", + "lru", "parity-scale-codec", "parking_lot 0.12.1", "sc-executor-common", @@ -9426,12 +10197,10 @@ dependencies = [ "sc-executor-wasmtime", "sp-api", "sp-core", - "sp-core-hashing-proc-macro", "sp-externalities", "sp-io", "sp-panic-handler", "sp-runtime-interface", - "sp-tasks", "sp-trie", "sp-version", "sp-wasm-interface", @@ -9442,13 +10211,10 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "environmental", - "parity-scale-codec", "sc-allocator", "sp-maybe-compressed-blob", - "sp-sandbox", "sp-wasm-interface", "thiserror", "wasm-instrument", @@ -9458,14 +10224,12 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "log", - "parity-scale-codec", "sc-allocator", "sc-executor-common", "sp-runtime-interface", - "sp-sandbox", "sp-wasm-interface", "wasmi", ] @@ -9473,19 +10237,16 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "cfg-if 1.0.0", "libc", "log", "once_cell", - "parity-scale-codec", - "parity-wasm", - "rustix", + "rustix 0.35.13", "sc-allocator", "sc-executor-common", "sp-runtime-interface", - "sp-sandbox", "sp-wasm-interface", "wasmtime", ] @@ -9493,10 +10254,10 @@ dependencies = [ [[package]] name = "sc-finality-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "ahash", - "array-bytes", + "array-bytes 4.2.0", "async-trait", "dyn-clone", "finality-grandpa", @@ -9511,7 +10272,6 @@ dependencies = [ "sc-chain-spec", "sc-client-api", "sc-consensus", - "sc-keystore", "sc-network", "sc-network-common", "sc-network-gossip", @@ -9534,18 +10294,17 @@ dependencies = [ [[package]] name = "sc-finality-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "finality-grandpa", "futures", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "parity-scale-codec", "sc-client-api", "sc-finality-grandpa", "sc-rpc", "serde", - "serde_json", "sp-blockchain", "sp-core", "sp-runtime", @@ -9555,16 +10314,14 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "ansi_term 0.12.1", + "ansi_term", "futures", "futures-timer", "log", - "parity-util-mem", "sc-client-api", "sc-network-common", - "sc-transaction-pool-api", "sp-blockchain", "sp-runtime", ] @@ -9572,9 +10329,9 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "async-trait", "parking_lot 0.12.1", "serde_json", @@ -9587,30 +10344,25 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "async-trait", "asynchronous-codec", - "bitflags", + "backtrace", "bytes", - "cid", "either", "fnv", - "fork-tree", "futures", "futures-timer", "ip_network", "libp2p", - "linked-hash-map", - "linked_hash_set", "log", - "lru 0.7.8", + "lru", "parity-scale-codec", "parking_lot 0.12.1", "pin-project", - "prost", - "rand 0.7.3", + "rand 0.8.5", "sc-block-builder", "sc-client-api", "sc-consensus", @@ -9634,7 +10386,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "cid", "futures", @@ -9648,13 +10400,12 @@ dependencies = [ "sp-runtime", "thiserror", "unsigned-varint", - "void", ] [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "bitflags", @@ -9680,14 +10431,14 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "ahash", "futures", "futures-timer", "libp2p", "log", - "lru 0.7.8", + "lru", "sc-network-common", "sc-peerset", "sp-runtime", @@ -9698,9 +10449,9 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "futures", "libp2p", "log", @@ -9719,14 +10470,15 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", + "async-trait", "fork-tree", "futures", "libp2p", "log", - "lru 0.7.8", + "lru", "mockall", "parity-scale-codec", "prost", @@ -9743,23 +10495,24 @@ dependencies = [ "sp-core", "sp-finality-grandpa", "sp-runtime", + "substrate-prometheus-endpoint", "thiserror", ] [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "futures", - "hex", "libp2p", "log", "parity-scale-codec", "pin-project", "sc-network-common", "sc-peerset", + "sc-utils", "sp-consensus", "sp-runtime", "substrate-prometheus-endpoint", @@ -9768,9 +10521,9 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "bytes", "fnv", "futures", @@ -9782,7 +10535,7 @@ dependencies = [ "once_cell", "parity-scale-codec", "parking_lot 0.12.1", - "rand 0.7.3", + "rand 0.8.5", "sc-client-api", "sc-network-common", "sc-peerset", @@ -9798,7 +10551,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "futures", "libp2p", @@ -9811,7 +10564,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -9820,11 +10573,10 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "futures", - "hash-db", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -9850,13 +10602,10 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "futures", - "jsonrpsee", - "log", + "jsonrpsee 0.16.2", "parity-scale-codec", - "parking_lot 0.12.1", "sc-chain-spec", "sc-transaction-pool-api", "scale-info", @@ -9865,7 +10614,6 @@ dependencies = [ "sp-core", "sp-rpc", "sp-runtime", - "sp-tracing", "sp-version", "thiserror", ] @@ -9873,53 +10621,60 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "futures", - "jsonrpsee", + "http", + "jsonrpsee 0.16.2", "log", "serde_json", "substrate-prometheus-endpoint", "tokio", + "tower", + "tower-http", ] [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ + "array-bytes 4.2.0", "futures", + "futures-util", "hex", - "jsonrpsee", + "jsonrpsee 0.16.2", + "log", "parity-scale-codec", + "parking_lot 0.12.1", "sc-chain-spec", + "sc-client-api", "sc-transaction-pool-api", "serde", "sp-api", "sp-blockchain", "sp-core", "sp-runtime", + "sp-version", "thiserror", + "tokio-stream", ] [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "directories", "exit-future", "futures", "futures-timer", - "hash-db", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "parity-scale-codec", - "parity-util-mem", "parking_lot 0.12.1", "pin-project", - "rand 0.7.3", + "rand 0.8.5", "sc-block-builder", "sc-chain-spec", "sc-client-api", @@ -9947,19 +10702,15 @@ dependencies = [ "serde", "serde_json", "sp-api", - "sp-application-crypto", - "sp-block-builder", "sp-blockchain", "sp-consensus", "sp-core", "sp-externalities", - "sp-inherents", "sp-keystore", "sp-runtime", "sp-session", "sp-state-machine", "sp-storage", - "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", @@ -9976,23 +10727,20 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "log", "parity-scale-codec", - "parity-util-mem", - "parity-util-mem-derive", "parking_lot 0.12.1", - "sc-client-api", "sp-core", ] [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "jsonrpsee", + "jsonrpsee 0.16.2", "parity-scale-codec", "sc-chain-spec", "sc-client-api", @@ -10009,13 +10757,13 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "futures", "libc", "log", - "rand 0.7.3", - "rand_pcg 0.2.1", + "rand 0.8.5", + "rand_pcg", "regex", "sc-telemetry", "serde", @@ -10028,7 +10776,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "chrono", "futures", @@ -10036,7 +10784,8 @@ dependencies = [ "log", "parking_lot 0.12.1", "pin-project", - "rand 0.7.3", + "rand 0.8.5", + "sc-utils", "serde", "serde_json", "thiserror", @@ -10046,9 +10795,9 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "ansi_term 0.12.1", + "ansi_term", "atty", "chrono", "lazy_static", @@ -10077,26 +10826,26 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "futures", "futures-timer", "linked-hash-map", "log", + "num-traits", "parity-scale-codec", - "parity-util-mem", "parking_lot 0.12.1", "sc-client-api", "sc-transaction-pool-api", @@ -10115,7 +10864,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "futures", @@ -10129,8 +10878,9 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ + "backtrace", "futures", "futures-timer", "lazy_static", @@ -10141,9 +10891,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d8a765117b237ef233705cc2cc4c6a27fccd46eea6ef0c8c6dae5f3ef407f8" +checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608" dependencies = [ "bitvec", "cfg-if 1.0.0", @@ -10155,24 +10905,23 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcd47b380d8c4541044e341dcd9475f55ba37ddc50c908d945fc036a8642496" +checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" dependencies = [ "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "schannel" -version = "0.1.19" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "winapi", + "windows-sys 0.42.0", ] [[package]] @@ -10201,47 +10950,70 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scratch" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" [[package]] name = "sct" -version = "0.7.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" dependencies = [ "ring", "untrusted", ] [[package]] -name = "sec1" -version = "0.2.1" +name = "sct" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "der", - "generic-array 0.14.4", - "pkcs8", - "subtle", + "ring", + "untrusted", +] + +[[package]] +name = "sdp" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d22a5ef407871893fd72b4562ee15e4742269b173959db4b8df6f538c414e13" +dependencies = [ + "rand 0.8.5", + "substring", + "thiserror", + "url", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.6", + "pkcs8", + "subtle", "zeroize", ] [[package]] name = "secp256k1" -version = "0.24.0" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7649a0b3ffb32636e60c7ce0d70511eda9c52c658cd0634e194d5a19943aeff" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" dependencies = [ "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7058dc8eaf3f2810d7828680320acda0b25a288f6d288e19278e249bbf74226b" +checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" dependencies = [ "cc", ] @@ -10257,9 +11029,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.4.2" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" dependencies = [ "bitflags", "core-foundation", @@ -10270,9 +11042,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.4.2" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" dependencies = [ "core-foundation-sys", "libc", @@ -10298,9 +11070,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.4" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" dependencies = [ "serde", ] @@ -10313,57 +11085,36 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.144" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ "indexmap", - "itoa 1.0.1", + "itoa", "ryu", "serde", ] -[[package]] -name = "serde_nanos" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e44969a61f5d316be20a42ff97816efb3b407a924d06824c3d8a49fa8450de0e" -dependencies = [ - "serde", -] - -[[package]] -name = "sha-1" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - [[package]] name = "sha-1" version = "0.9.8" @@ -10391,9 +11142,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", "cfg-if 1.0.0", @@ -10404,22 +11155,22 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9db03534dff993187064c4e0c05a5708d2a9728ace9a8959b77bedf415dac5" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.3", + "digest 0.10.6", ] [[package]] name = "sha3" -version = "0.10.1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881bf8156c87b6301fc5ca6b27f11eeb2761224c7081e69b409d5a1951a70c86" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" dependencies = [ - "digest 0.10.3", + "digest 0.10.6", "keccak", ] @@ -10440,14 +11191,26 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "signal-hook" -version = "0.3.10" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1" +checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" dependencies = [ "libc", "signal-hook-registry", ] +[[package]] +name = "signal-hook-async-std" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4aa94397e2023af5b7cff5b8d4785e935cfb77f0e4aab0cae3b26258ace556" +dependencies = [ + "async-io", + "futures-lite", + "libc", + "signal-hook", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -10459,12 +11222,12 @@ dependencies = [ [[package]] name = "signature" -version = "1.4.0" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.9.0", - "rand_core 0.6.3", + "digest 0.10.6", + "rand_core 0.6.4", ] [[package]] @@ -10481,9 +11244,12 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] [[package]] name = "slice-group-by" @@ -10493,8 +11259,8 @@ checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" [[package]] name = "slot-range-helper" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "enumn", "parity-scale-codec", @@ -10531,9 +11297,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "snap" -version = "1.0.5" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45456094d1983e2ee2a18fdfebce3189fa451699d0502cb8e3b49dba5ba41451" +checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" [[package]] name = "snow" @@ -10541,22 +11307,22 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "774d05a3edae07ce6d68ea6984f3c05e9bba8927e3dd591e3b479e5b03213d0d" dependencies = [ - "aes-gcm", + "aes-gcm 0.9.4", "blake2", "chacha20poly1305", - "curve25519-dalek 4.0.0-pre.1", - "rand_core 0.6.3", + "curve25519-dalek 4.0.0-pre.5", + "rand_core 0.6.4", "ring", "rustc_version 0.4.0", - "sha2 0.10.5", + "sha2 0.10.6", "subtle", ] [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi", @@ -10568,20 +11334,21 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" dependencies = [ - "base64", + "base64 0.13.1", "bytes", "flate2", "futures", + "http", "httparse", "log", "rand 0.8.5", - "sha-1 0.9.8", + "sha-1", ] [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "hash-db", "log", @@ -10599,19 +11366,19 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "blake2", "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "sp-application-crypto" -version = "6.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "parity-scale-codec", "scale-info", @@ -10623,15 +11390,14 @@ dependencies = [ [[package]] name = "sp-arithmetic" -version = "5.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "integer-sqrt", "num-traits", "parity-scale-codec", "scale-info", "serde", - "sp-debug-derive", "sp-std", "static_assertions", ] @@ -10639,7 +11405,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "parity-scale-codec", "scale-info", @@ -10652,7 +11418,7 @@ dependencies = [ [[package]] name = "sp-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "parity-scale-codec", @@ -10661,10 +11427,27 @@ dependencies = [ "sp-std", ] +[[package]] +name = "sp-beefy" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-mmr-primitives", + "sp-runtime", + "sp-std", +] + [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "parity-scale-codec", "sp-api", @@ -10676,11 +11459,11 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "futures", "log", - "lru 0.7.8", + "lru", "parity-scale-codec", "parking_lot 0.12.1", "sp-api", @@ -10694,11 +11477,10 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "futures", - "futures-timer", "log", "parity-scale-codec", "sp-core", @@ -10713,7 +11495,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "parity-scale-codec", @@ -10731,7 +11513,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "merlin", @@ -10754,13 +11536,11 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-arithmetic", - "sp-runtime", "sp-std", "sp-timestamp", ] @@ -10768,7 +11548,7 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "parity-scale-codec", "scale-info", @@ -10780,14 +11560,13 @@ dependencies = [ [[package]] name = "sp-core" -version = "6.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "array-bytes", + "array-bytes 4.2.0", "base58", "bitflags", "blake2", - "byteorder", "dyn-clonable", "ed25519-zebra", "futures", @@ -10798,12 +11577,10 @@ dependencies = [ "libsecp256k1", "log", "merlin", - "num-traits", "parity-scale-codec", - "parity-util-mem", "parking_lot 0.12.1", "primitive-types", - "rand 0.7.3", + "rand 0.8.5", "regex", "scale-info", "schnorrkel", @@ -10820,19 +11597,18 @@ dependencies = [ "substrate-bip39", "thiserror", "tiny-bip39", - "wasmi", "zeroize", ] [[package]] name = "sp-core-hashing" -version = "4.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "blake2", "byteorder", - "digest 0.10.3", - "sha2 0.10.5", + "digest 0.10.6", + "sha2 0.10.6", "sha3", "sp-std", "twox-hash", @@ -10841,18 +11617,18 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.49", + "quote 1.0.23", "sp-core-hashing", - "syn", + "syn 1.0.107", ] [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -10860,18 +11636,18 @@ dependencies = [ [[package]] name = "sp-debug-derive" -version = "4.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "sp-externalities" -version = "0.12.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "environmental", "parity-scale-codec", @@ -10882,7 +11658,7 @@ dependencies = [ [[package]] name = "sp-finality-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "finality-grandpa", "log", @@ -10900,7 +11676,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -10913,16 +11689,16 @@ dependencies = [ [[package]] name = "sp-io" -version = "6.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "bytes", + "ed25519", + "ed25519-dalek", "futures", - "hash-db", "libsecp256k1", "log", "parity-scale-codec", - "parking_lot 0.12.1", "secp256k1", "sp-core", "sp-externalities", @@ -10932,15 +11708,14 @@ dependencies = [ "sp-std", "sp-tracing", "sp-trie", - "sp-wasm-interface", "tracing", "tracing-core", ] [[package]] name = "sp-keyring" -version = "6.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "lazy_static", "sp-core", @@ -10950,8 +11725,8 @@ dependencies = [ [[package]] name = "sp-keystore" -version = "0.12.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "futures", @@ -10968,7 +11743,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "thiserror", "zstd", @@ -10977,8 +11752,9 @@ dependencies = [ [[package]] name = "sp-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ + "ckb-merkle-mountain-range 0.5.2", "log", "parity-scale-codec", "scale-info", @@ -10988,12 +11764,13 @@ dependencies = [ "sp-debug-derive", "sp-runtime", "sp-std", + "thiserror", ] [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "parity-scale-codec", "scale-info", @@ -11007,7 +11784,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "sp-api", "sp-core", @@ -11016,8 +11793,8 @@ dependencies = [ [[package]] name = "sp-panic-handler" -version = "4.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "backtrace", "lazy_static", @@ -11027,7 +11804,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "rustc-hash", "serde", @@ -11036,17 +11813,16 @@ dependencies = [ [[package]] name = "sp-runtime" -version = "6.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "either", "hash256-std-hasher", "impl-trait-for-tuples", "log", "parity-scale-codec", - "parity-util-mem", "paste", - "rand 0.7.3", + "rand 0.8.5", "scale-info", "serde", "sp-application-crypto", @@ -11059,8 +11835,8 @@ dependencies = [ [[package]] name = "sp-runtime-interface" -version = "6.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -11077,34 +11853,20 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" -version = "5.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "Inflector", "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sp-sandbox" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" -dependencies = [ - "log", - "parity-scale-codec", - "sp-core", - "sp-io", - "sp-std", - "sp-wasm-interface", - "wasmi", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "parity-scale-codec", "scale-info", @@ -11118,25 +11880,25 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "parity-scale-codec", "scale-info", + "sp-core", "sp-runtime", "sp-std", ] [[package]] name = "sp-state-machine" -version = "0.12.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "hash-db", "log", - "num-traits", "parity-scale-codec", "parking_lot 0.12.1", - "rand 0.7.3", + "rand 0.8.5", "smallvec", "sp-core", "sp-externalities", @@ -11145,18 +11907,17 @@ dependencies = [ "sp-trie", "thiserror", "tracing", - "trie-root", ] [[package]] name = "sp-std" -version = "4.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" [[package]] name = "sp-storage" -version = "6.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", @@ -11166,29 +11927,15 @@ dependencies = [ "sp-std", ] -[[package]] -name = "sp-tasks" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" -dependencies = [ - "log", - "sp-core", - "sp-externalities", - "sp-io", - "sp-runtime-interface", - "sp-std", -] - [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "futures-timer", "log", "parity-scale-codec", - "sp-api", "sp-inherents", "sp-runtime", "sp-std", @@ -11197,8 +11944,8 @@ dependencies = [ [[package]] name = "sp-tracing" -version = "5.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "parity-scale-codec", "sp-std", @@ -11210,7 +11957,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "sp-api", "sp-runtime", @@ -11219,7 +11966,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", "log", @@ -11234,14 +11981,14 @@ dependencies = [ [[package]] name = "sp-trie" -version = "6.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "ahash", "hash-db", "hashbrown", "lazy_static", - "lru 0.7.8", + "lru", "memory-db", "nohash-hasher", "parity-scale-codec", @@ -11258,7 +12005,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", @@ -11275,18 +12022,18 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "parity-scale-codec", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "sp-wasm-interface" -version = "6.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "impl-trait-for-tuples", "log", @@ -11299,9 +12046,8 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "serde", @@ -11320,9 +12066,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spki" -version = "0.5.4" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", "der", @@ -11330,17 +12076,17 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.31.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de151faef619cb7b5c26b32d42bc7ddccac0d202beb7a84344b44e9232b92f7" +checksum = "d44528162f980c0e03c71e005d334332c8da0aec9f2b0b4bdc557ed4a9f24776" dependencies = [ "Inflector", "num-format", - "proc-macro2", - "quote", + "proc-macro2 1.0.49", + "quote 1.0.23", "serde", "serde_json", - "unicode-xid", + "unicode-xid 0.2.4", ] [[package]] @@ -11377,7 +12123,7 @@ dependencies = [ "cfg_aliases", "libc", "parking_lot 0.11.2", - "parking_lot_core 0.8.5", + "parking_lot_core 0.8.6", "static_init_macro 1.0.2", "winapi", ] @@ -11390,9 +12136,9 @@ checksum = "f2261c91034a1edc3fc4d1b80e89d82714faede0515c14a75da10cb941546bbf" dependencies = [ "cfg_aliases", "memchr", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -11403,9 +12149,9 @@ checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" dependencies = [ "cfg_aliases", "memchr", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -11450,11 +12196,11 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "structopt" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" dependencies = [ - "clap 2.33.3", + "clap 2.34.0", "lazy_static", "structopt-derive", ] @@ -11467,9 +12213,9 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -11487,7 +12233,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" dependencies = [ - "strum_macros 0.24.0", + "strum_macros 0.24.3", ] [[package]] @@ -11497,22 +12243,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" dependencies = [ "heck 0.3.3", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "strum_macros" -version = "0.24.0" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.0", - "proc-macro2", - "quote", + "proc-macro2 1.0.49", + "quote 1.0.23", "rustversion", - "syn", + "syn 1.0.107", +] + +[[package]] +name = "stun" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25" +dependencies = [ + "base64 0.13.1", + "crc", + "lazy_static", + "md-5", + "rand 0.8.5", + "ring", + "subtle", + "thiserror", + "tokio", + "url", + "webrtc-util", ] [[package]] @@ -11524,32 +12289,30 @@ dependencies = [ "hmac 0.11.0", "pbkdf2 0.8.0", "schnorrkel", - "sha2 0.9.8", + "sha2 0.9.9", "zeroize", ] [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "platforms", + "platforms 2.0.0", ] [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "frame-system-rpc-runtime-api", "futures", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "parity-scale-codec", - "sc-client-api", "sc-rpc-api", "sc-transaction-pool-api", - "serde_json", "sp-api", "sp-block-builder", "sp-blockchain", @@ -11560,9 +12323,8 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "futures-util", "hyper", "log", "prometheus", @@ -11577,19 +12339,15 @@ dependencies = [ "anyhow", "async-std", "async-trait", - "bp-bridge-hub-rococo", - "bp-bridge-hub-wococo", "bp-header-chain", "bp-messages", "bp-millau", + "bp-parachains", "bp-polkadot-core", "bp-rialto", "bp-rialto-parachain", - "bp-rococo", "bp-runtime", "bp-test-utils", - "bp-westend", - "bp-wococo", "bridge-runtime-common", "finality-grandpa", "frame-support", @@ -11597,11 +12355,9 @@ dependencies = [ "hex", "hex-literal", "log", - "messages-relay", "millau-runtime", "num-format", "num-traits", - "pallet-bridge-messages", "pallet-bridge-parachains", "parachains-relay", "parity-scale-codec", @@ -11609,6 +12365,7 @@ dependencies = [ "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-parachains", + "rbtag", "relay-bridge-hub-rococo-client", "relay-bridge-hub-wococo-client", "relay-millau-client", @@ -11619,12 +12376,12 @@ dependencies = [ "relay-utils", "relay-westend-client", "relay-wococo-client", - "rialto-parachain-runtime", "rialto-runtime", + "signal-hook", + "signal-hook-async-std", "sp-core", "sp-keyring", "sp-runtime", - "sp-version", "structopt", "strum 0.21.0", "substrate-relay-helper", @@ -11679,10 +12436,10 @@ dependencies = [ [[package]] name = "substrate-rpc-client" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ "async-trait", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "sc-rpc-api", "serde", @@ -11692,9 +12449,9 @@ dependencies = [ [[package]] name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "parity-scale-codec", "sc-client-api", @@ -11702,10 +12459,8 @@ dependencies = [ "scale-info", "serde", "sp-core", - "sp-io", "sp-runtime", "sp-state-machine", - "sp-std", "sp-trie", "trie-db", ] @@ -11713,9 +12468,9 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "ansi_term 0.12.1", + "ansi_term", "build-helper", "cargo_metadata", "filetime", @@ -11727,6 +12482,15 @@ dependencies = [ "wasm-opt", ] +[[package]] +name = "substring" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ee6433ecef213b2e72f587ef64a2f5943e7cd16fbd82dbe8bc07486c534c86" +dependencies = [ + "autocfg", +] + [[package]] name = "subtle" version = "2.4.1" @@ -11735,12 +12499,23 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.103" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] + +[[package]] +name = "syn" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.49", + "quote 1.0.23", "unicode-ident", ] @@ -11750,10 +12525,10 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", + "unicode-xid 0.2.4", ] [[package]] @@ -11802,9 +12577,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.2" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9bffcddbc2458fa3e6058414599e3c838a022abae82e5c67b4f7f80298d5bff" +checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" [[package]] name = "tempfile" @@ -11822,9 +12597,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] @@ -11846,22 +12621,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -11901,6 +12676,17 @@ dependencies = [ "threadpool", ] +[[package]] +name = "tikv-jemalloc-ctl" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37706572f4b151dff7a0146e040804e9c26fe3a3118591112f05cf12a4216c1" +dependencies = [ + "libc", + "paste", + "tikv-jemalloc-sys", +] + [[package]] name = "tikv-jemalloc-sys" version = "0.5.2+5.3.0-patched" @@ -11914,9 +12700,9 @@ dependencies = [ [[package]] name = "time" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", @@ -11925,28 +12711,46 @@ dependencies = [ [[package]] name = "time" -version = "0.3.7" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ - "itoa 1.0.1", + "itoa", "libc", "num_threads", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +dependencies = [ + "time-core", ] [[package]] name = "tiny-bip39" -version = "0.8.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" dependencies = [ "anyhow", - "hmac 0.8.1", + "hmac 0.12.1", "once_cell", - "pbkdf2 0.4.0", - "rand 0.7.3", + "pbkdf2 0.11.0", + "rand 0.8.5", "rustc-hash", - "sha2 0.9.8", + "sha2 0.10.6", "thiserror", "unicode-normalization", "wasm-bindgen", @@ -11962,11 +12766,21 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -11979,9 +12793,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.22.0" +version = "1.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" +checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" dependencies = [ "autocfg", "bytes", @@ -11994,84 +12808,107 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.42.0", ] [[package]] name = "tokio-macros" -version = "1.7.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "tokio-rustls" -version = "0.23.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls", + "rustls 0.20.8", "tokio", - "webpki", + "webpki 0.22.0", ] [[package]] name = "tokio-stream" -version = "0.1.8" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" dependencies = [ "futures-core", "pin-project-lite 0.2.9", "tokio", + "tokio-util", ] [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", - "log", "pin-project-lite 0.2.9", "tokio", + "tracing", ] [[package]] -name = "tokio-util" -version = "0.7.1" +name = "toml" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +dependencies = [ + "serde", +] + +[[package]] +name = "tower" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" +dependencies = [ + "bitflags", "bytes", "futures-core", - "futures-io", - "futures-sink", + "futures-util", + "http", + "http-body", + "http-range-header", "pin-project-lite 0.2.9", - "tokio", + "tower-layer", + "tower-service", ] [[package]] -name = "toml" -version = "0.5.8" +name = "tower-layer" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" @@ -12092,9 +12929,9 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -12119,8 +12956,8 @@ dependencies = [ [[package]] name = "tracing-gum" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "polkadot-node-jaeger", "polkadot-primitives", @@ -12130,14 +12967,14 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "expander 0.0.6", "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -12153,9 +12990,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" dependencies = [ "serde", "tracing-core", @@ -12167,7 +13004,7 @@ version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" dependencies = [ - "ansi_term 0.12.1", + "ansi_term", "chrono", "lazy_static", "matchers", @@ -12224,8 +13061,10 @@ dependencies = [ "lazy_static", "rand 0.8.5", "smallvec", + "socket2", "thiserror", "tinyvec", + "tokio", "tracing", "url", ] @@ -12245,34 +13084,39 @@ dependencies = [ "resolv-conf", "smallvec", "thiserror", + "tokio", "tracing", "trust-dns-proto", ] [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech//substrate?branch=sv-locked-for-gav-xcm-v3-and-bridges#87f3fdea8f227d33322c439d45a9e1796637e972" +source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" dependencies = [ - "clap 4.0.26", + "clap 4.1.0", + "frame-remote-externalities", + "hex", "log", "parity-scale-codec", - "remote-externalities", - "sc-chain-spec", "sc-cli", "sc-executor", "sc-service", "serde", + "serde_json", + "sp-api", "sp-core", + "sp-debug-derive", "sp-externalities", "sp-io", "sp-keystore", + "sp-rpc", "sp-runtime", "sp-state-machine", "sp-version", @@ -12283,9 +13127,28 @@ dependencies = [ [[package]] name = "tt-call" -version = "1.0.8" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f195fd851901624eee5a58c4bb2b4f06399148fcd0ed336e6f1cb60a9881df" + +[[package]] +name = "turn" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e66dcbec4290c69dd03c57e76c2469ea5c7ce109c6dd4351c13055cf71ea055" +checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" +dependencies = [ + "async-trait", + "base64 0.13.1", + "futures", + "log", + "md-5", + "rand 0.8.5", + "ring", + "stun", + "thiserror", + "tokio", + "webrtc-util", +] [[package]] name = "twox-hash" @@ -12293,29 +13156,29 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 1.0.0", - "digest 0.10.3", + "cfg-if 0.1.10", + "digest 0.10.6", "rand 0.8.5", "static_assertions", ] [[package]] name = "typenum" -version = "1.14.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "uint" -version = "0.9.1" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", "crunchy", @@ -12323,53 +13186,50 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + +[[package]] +name = "unicode-xid" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" @@ -12377,7 +13237,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.6", "subtle", ] @@ -12410,6 +13270,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +dependencies = [ + "getrandom 0.2.8", +] + [[package]] name = "valuable" version = "0.1.0" @@ -12440,9 +13309,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "void" @@ -12450,6 +13319,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "waitgroup" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1f50000a783467e6c0200f9d10642f4bc424e39efc1b770203e88b488f79292" +dependencies = [ + "atomic-waker", +] + [[package]] name = "waker-fn" version = "1.1.0" @@ -12497,9 +13375,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.78" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -12507,24 +13385,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.78" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", - "lazy_static", "log", - "proc-macro2", - "quote", - "syn", + "once_cell", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.28" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -12534,32 +13412,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.78" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ - "quote", + "quote 1.0.23", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.78" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.78" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "wasm-instrument" @@ -12579,7 +13457,7 @@ dependencies = [ "anyhow", "libc", "strum 0.24.1", - "strum_macros 0.24.0", + "strum_macros 0.24.3", "tempfile", "thiserror", "wasm-opt-cxx-sys", @@ -12653,9 +13531,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" dependencies = [ "downcast-rs", - "libm", + "libm 0.2.6", "memory_units", - "num-rational 0.4.0", + "num-rational", "num-traits", ] @@ -12670,9 +13548,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f511c4917c83d04da68333921107db75747c4e11a2f654a8e909cc5e0520dc" +checksum = "4ad5af6ba38311282f2a21670d96e78266e8c8e2f38cbcd52c254df6ccbc7731" dependencies = [ "anyhow", "bincode", @@ -12698,28 +13576,28 @@ dependencies = [ [[package]] name = "wasmtime-asm-macros" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39bf3debfe744bf19dd3732990ce6f8c0ced7439e2370ba4e1d8f5a3660a3178" +checksum = "45de63ddfc8b9223d1adc8f7b2ee5f35d1f6d112833934ad7ea66e4f4339e597" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "wasmtime-cache" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece42fa4676a263f7558cdaaf5a71c2592bebcbac22a0580e33cf3406c103da2" +checksum = "bcd849399d17d2270141cfe47fa0d91ee52d5f8ea9b98cf7ddde0d53e5f79882" dependencies = [ "anyhow", - "base64", + "base64 0.13.1", "bincode", "directories-next", "file-per-thread-logger", "log", - "rustix", + "rustix 0.35.13", "serde", - "sha2 0.9.8", + "sha2 0.9.9", "toml", "windows-sys 0.36.1", "zstd", @@ -12727,9 +13605,9 @@ dependencies = [ [[package]] name = "wasmtime-cranelift" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058217e28644b012bdcdf0e445f58d496d78c2e0b6a6dd93558e701591dad705" +checksum = "4bd91339b742ff20bfed4532a27b73c86b5bcbfedd6bea2dcdf2d64471e1b5c6" dependencies = [ "anyhow", "cranelift-codegen", @@ -12737,7 +13615,7 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli", + "gimli 0.26.2", "log", "object 0.29.0", "target-lexicon", @@ -12748,13 +13626,13 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7af06848df28b7661471d9a80d30a973e0f401f2e3ed5396ad7e225ed217047" +checksum = "ebb881c61f4f627b5d45c54e629724974f8a8890d455bcbe634330cc27309644" dependencies = [ "anyhow", "cranelift-entity", - "gimli", + "gimli 0.26.2", "indexmap", "log", "object 0.29.0", @@ -12767,20 +13645,20 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9028fb63a54185b3c192b7500ef8039c7bb8d7f62bfc9e7c258483a33a3d13bb" +checksum = "1985c628011fe26adf5e23a5301bdc79b245e0e338f14bb58b39e4e25e4d8681" dependencies = [ - "addr2line", + "addr2line 0.17.0", "anyhow", "bincode", "cfg-if 1.0.0", "cpp_demangle", - "gimli", + "gimli 0.26.2", "log", "object 0.29.0", "rustc-demangle", - "rustix", + "rustix 0.35.13", "serde", "target-lexicon", "thiserror", @@ -12792,20 +13670,20 @@ dependencies = [ [[package]] name = "wasmtime-jit-debug" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e82d4ef93296785de7efca92f7679dc67fe68a13b625a5ecc8d7503b377a37" +checksum = "f671b588486f5ccec8c5a3dba6b4c07eac2e66ab8c60e6f4e53717c77f709731" dependencies = [ "object 0.29.0", "once_cell", - "rustix", + "rustix 0.35.13", ] [[package]] name = "wasmtime-runtime" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f0e9bea7d517d114fe66b930b2124ee086516ee93eeebfd97f75f366c5b0553" +checksum = "ee8f92ad4b61736339c29361da85769ebc200f184361959d1792832e592a1afd" dependencies = [ "anyhow", "cc", @@ -12815,10 +13693,10 @@ dependencies = [ "log", "mach", "memfd", - "memoffset", + "memoffset 0.6.5", "paste", "rand 0.8.5", - "rustix", + "rustix 0.35.13", "thiserror", "wasmtime-asm-macros", "wasmtime-environ", @@ -12828,9 +13706,9 @@ dependencies = [ [[package]] name = "wasmtime-types" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b83e93ed41b8fdc936244cfd5e455480cf1eca1fd60c78a0040038b4ce5075" +checksum = "d23d61cb4c46e837b431196dd06abb11731541021916d03476a178b54dc07aeb" dependencies = [ "cranelift-entity", "serde", @@ -12840,14 +13718,24 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.55" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "webpki" version = "0.22.0" @@ -12860,11 +13748,223 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.2" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki 0.22.0", +] + +[[package]] +name = "webrtc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3bc9049bdb2cea52f5fd4f6f728184225bdb867ed0dc2410eab6df5bdd67bb" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "hex", + "interceptor", + "lazy_static", + "log", + "rand 0.8.5", + "rcgen 0.9.3", + "regex", + "ring", + "rtcp", + "rtp", + "rustls 0.19.1", + "sdp", + "serde", + "serde_json", + "sha2 0.10.6", + "stun", + "thiserror", + "time 0.3.17", + "tokio", + "turn", + "url", + "waitgroup", + "webrtc-data", + "webrtc-dtls", + "webrtc-ice", + "webrtc-mdns", + "webrtc-media", + "webrtc-sctp", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "webrtc-data" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef36a4d12baa6e842582fe9ec16a57184ba35e1a09308307b67d43ec8883100" +dependencies = [ + "bytes", + "derive_builder", + "log", + "thiserror", + "tokio", + "webrtc-sctp", + "webrtc-util", +] + +[[package]] +name = "webrtc-dtls" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7021987ae0a2ed6c8cd33f68e98e49bb6e74ffe9543310267b48a1bbe3900e5f" +dependencies = [ + "aes 0.6.0", + "aes-gcm 0.8.0", + "async-trait", + "bincode", + "block-modes", + "byteorder", + "ccm", + "curve25519-dalek 3.2.0", + "der-parser 8.1.0", + "elliptic-curve", + "hkdf", + "hmac 0.10.1", + "log", + "oid-registry 0.6.1", + "p256", + "p384", + "rand 0.8.5", + "rand_core 0.6.4", + "rcgen 0.9.3", + "ring", + "rustls 0.19.1", + "sec1", + "serde", + "sha-1", + "sha2 0.9.9", + "signature", + "subtle", + "thiserror", + "tokio", + "webpki 0.21.4", + "webrtc-util", + "x25519-dalek 2.0.0-pre.1", + "x509-parser 0.13.2", +] + +[[package]] +name = "webrtc-ice" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494483fbb2f5492620871fdc78b084aed8807377f6e3fe88b2e49f0a9c9c41d7" +dependencies = [ + "arc-swap", + "async-trait", + "crc", + "log", + "rand 0.8.5", + "serde", + "serde_json", + "stun", + "thiserror", + "tokio", + "turn", + "url", + "uuid", + "waitgroup", + "webrtc-mdns", + "webrtc-util", +] + +[[package]] +name = "webrtc-mdns" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f08dfd7a6e3987e255c4dbe710dde5d94d0f0574f8a21afa95d171376c143106" +dependencies = [ + "log", + "socket2", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-media" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449" +checksum = "ee2a3c157a040324e5049bcbd644ffc9079e6738fa2cfab2bcff64e5cc4c00d7" dependencies = [ - "webpki", + "byteorder", + "bytes", + "derive_builder", + "displaydoc", + "rand 0.8.5", + "rtp", + "thiserror", + "webrtc-util", +] + +[[package]] +name = "webrtc-sctp" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d47adcd9427eb3ede33d5a7f3424038f63c965491beafcc20bc650a2f6679c0" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "crc", + "log", + "rand 0.8.5", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-srtp" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6183edc4c1c6c0175f8812eefdce84dfa0aea9c3ece71c2bf6ddd3c964de3da5" +dependencies = [ + "aead 0.4.3", + "aes 0.7.5", + "aes-gcm 0.9.4", + "async-trait", + "byteorder", + "bytes", + "ctr 0.8.0", + "hmac 0.11.0", + "log", + "rtcp", + "rtp", + "sha-1", + "subtle", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-util" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" +dependencies = [ + "async-trait", + "bitflags", + "bytes", + "cc", + "ipnet", + "lazy_static", + "libc", + "log", + "nix", + "rand 0.8.5", + "thiserror", + "tokio", + "winapi", ] [[package]] @@ -12878,13 +13978,13 @@ dependencies = [ [[package]] name = "which" -version = "4.2.2" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9" +checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" dependencies = [ "either", - "lazy_static", "libc", + "once_cell", ] [[package]] @@ -12937,19 +14037,6 @@ dependencies = [ "windows_x86_64_msvc 0.34.0", ] -[[package]] -name = "windows-sys" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" -dependencies = [ - "windows_aarch64_msvc 0.32.0", - "windows_i686_gnu 0.32.0", - "windows_i686_msvc 0.32.0", - "windows_x86_64_gnu 0.32.0", - "windows_x86_64_msvc 0.32.0", -] - [[package]] name = "windows-sys" version = "0.36.1" @@ -12970,25 +14057,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_x86_64_msvc 0.42.1", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.32.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" @@ -13004,15 +14085,9 @@ checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" - -[[package]] -name = "windows_i686_gnu" -version = "0.32.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" @@ -13028,15 +14103,9 @@ checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" - -[[package]] -name = "windows_i686_msvc" -version = "0.32.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" @@ -13052,15 +14121,9 @@ checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" @@ -13076,21 +14139,15 @@ checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.32.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" @@ -13106,24 +14163,24 @@ checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winreg" -version = "0.7.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ "winapi", ] [[package]] name = "wyz" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] @@ -13139,10 +14196,58 @@ dependencies = [ "zeroize", ] +[[package]] +name = "x25519-dalek" +version = "2.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5da623d8af10a62342bcbbb230e33e58a63255a58012f8653c578e54bab48df" +dependencies = [ + "curve25519-dalek 3.2.0", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9bace5b5589ffead1afb76e43e34cff39cd0f3ce7e170ae0c29e53b88eb1c" +dependencies = [ + "asn1-rs 0.3.1", + "base64 0.13.1", + "data-encoding", + "der-parser 7.0.0", + "lazy_static", + "nom", + "oid-registry 0.4.0", + "ring", + "rusticata-macros", + "thiserror", + "time 0.3.17", +] + +[[package]] +name = "x509-parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" +dependencies = [ + "asn1-rs 0.5.1", + "base64 0.13.1", + "data-encoding", + "der-parser 8.1.0", + "lazy_static", + "nom", + "oid-registry 0.6.1", + "rusticata-macros", + "thiserror", + "time 0.3.17", +] + [[package]] name = "xcm" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "derivative", "impl-trait-for-tuples", @@ -13150,15 +14255,15 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-io", - "sp-runtime", + "sp-core", + "sp-weights", "xcm-procedural", ] [[package]] name = "xcm-builder" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "frame-support", "frame-system", @@ -13178,9 +14283,10 @@ dependencies = [ [[package]] name = "xcm-executor" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ + "environmental", "frame-support", "impl-trait-for-tuples", "log", @@ -13190,18 +14296,19 @@ dependencies = [ "sp-io", "sp-runtime", "sp-std", + "sp-weights", "xcm", ] [[package]] name = "xcm-procedural" -version = "0.9.31" -source = "git+https://github.com/paritytech//polkadot?branch=locked-for-gav-xcm-v3-and-bridges#9fc6b88ccc7abc1418ff5260e8cc492e647306a0" +version = "0.9.33" +source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" dependencies = [ "Inflector", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -13218,24 +14325,33 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "yasna" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aed2e7a52e3744ab4d0c05c20aa065258e84c49fd4226f5191b2ed29712710b4" +dependencies = [ + "time 0.3.17", +] + [[package]] name = "zeroize" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb5728b8afd3f280a869ce1d4c554ffaed35f45c231fc41bfbd0381bef50317" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.3.2" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.49", + "quote 1.0.23", + "syn 1.0.107", "synstructure", ] @@ -13260,10 +14376,11 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.1+zstd.1.5.2" +version = "2.0.5+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b" +checksum = "edc50ffce891ad571e9f9afe5039c4837bede781ac4bb13052ed7ae695518596" dependencies = [ "cc", "libc", + "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index 9ee19ed7828..1090a0fe5ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,255 +9,3 @@ members = [ "primitives/*", "relays/*", ] - -# we need to be able to work with XCMv3, but it is not yet in Polkadot master -# => manual patch is required. Because of https://github.com/rust-lang/cargo/issues/5478 -# we need to use double slash in the repo name. -# -# Once XCMv3 PR is merged, we may remove both Substrate and Polkadot patch section. - -[patch."https://github.com/paritytech/substrate"] -beefy-gadget = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -beefy-gadget-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -beefy-merkle-tree = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -beefy-primitives = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -fork-tree = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-benchmarking-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-election-provider-solution-type = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-election-provider-support = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-executive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-support = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-support-procedural = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-support-procedural-tools = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-support-procedural-tools-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-system = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-system-benchmarking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-system-rpc-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -frame-try-runtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -node-inspect = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-bags-list = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-balances = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-beefy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-beefy-mmr = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-bounties = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-child-bounties = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-collective = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-conviction-voting = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-democracy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-election-provider-multi-phase = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-elections-phragmen = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-fast-unstake = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-gilt = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-identity = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-im-online = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-indices = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-membership = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-mmr = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-mmr-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-multisig = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-nomination-pools = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-nomination-pools-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-offences = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-preimage = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-proxy = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-randomness-collective-flip = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-ranked-collective = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-recovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-referenda = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-scheduler = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-session = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-society = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-staking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-staking-reward-curve = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-staking-reward-fn = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-sudo = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-timestamp = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-tips = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-transaction-payment = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-transaction-payment-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-treasury = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-utility = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-vesting = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -pallet-whitelist = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -remote-externalities = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-allocator = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-basic-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-block-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-chain-spec = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-chain-spec-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-client-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-client-db = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-consensus = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-consensus-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-consensus-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-consensus-babe-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-consensus-epochs = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-consensus-slots = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-executor = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-executor-common = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-executor-wasmi = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-executor-wasmtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-finality-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-finality-grandpa-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-informant = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-keystore = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-network = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-network-common = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-network-gossip = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-network-light = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-network-sync = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-offchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-peerset = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-proposer-metrics = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-rpc-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-rpc-server = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-service = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-state-db = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-sync-state-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-sysinfo = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-telemetry = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-tracing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-tracing-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-transaction-pool = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-transaction-pool-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sc-utils = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-api = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-api-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-application-crypto = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-arithmetic = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-authority-discovery = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-authorship = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-block-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-blockchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-consensus = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-consensus-aura = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-consensus-babe = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-consensus-slots = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-consensus-vrf = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-core = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-core-hashing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-core-hashing-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-database = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-debug-derive = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-externalities = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-finality-grandpa = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-inherents = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-io = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-keyring = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-keystore = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-maybe-compressed-blob = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-mmr-primitives = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-npos-elections = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-offchain = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-panic-handler = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-runtime = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-runtime-interface = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-runtime-interface-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-session = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-staking = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-state-machine = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-std = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-storage = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-tasks = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-timestamp = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-tracing = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-transaction-pool = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-transaction-storage-proof = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-trie = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-version = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-version-proc-macro = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -sp-wasm-interface = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -substrate-build-script-utils = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -substrate-frame-rpc-system = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -substrate-prometheus-endpoint = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -substrate-state-trie-migration-rpc = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -substrate-wasm-builder = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } -try-runtime-cli = { git = "https://github.com/paritytech//substrate", branch = "sv-locked-for-gav-xcm-v3-and-bridges" } - -[patch."https://github.com/paritytech/polkadot"] -kusama-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -kusama-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -pallet-xcm = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-approval-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-availability-bitfield-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-availability-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-availability-recovery = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-cli = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-client = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-collator-protocol = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-core-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-dispute-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-erasure-coding = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-gossip-support = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-network-bridge = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-collation-generation = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-approval-voting = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-av-store = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-backing = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-bitfield-signing = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-candidate-validation = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-chain-api = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-chain-selection = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-dispute-coordinator = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-parachains-inherent = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-provisioner = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-pvf = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-pvf-checker = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-core-runtime-api = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-jaeger = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-metrics = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-network-protocol = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-subsystem = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-subsystem-types = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-node-subsystem-util = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-overseer = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-parachain = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-performance-test = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-primitives = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-rpc = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-runtime = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-runtime-common = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-runtime-constants = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-runtime-metrics = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-runtime-parachains = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-service = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-statement-distribution = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -polkadot-statement-table = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -slot-range-helper = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -tracing-gum = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -tracing-gum-proc-macro = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -xcm = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -xcm-builder = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -xcm-executor = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } -xcm-procedural = { git = "https://github.com/paritytech//polkadot", branch = "locked-for-gav-xcm-v3-and-bridges" } - -[patch."https://github.com/paritytech/cumulus"] -cumulus-client-consensus-aura = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-client-consensus-common = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-client-cli = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-client-network = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-client-service = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-primitives-core = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-primitives-parachain-inherent = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-relay-chain-interface = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-relay-chain-inprocess-interface = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-relay-chain-minimal-node = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-pallet-aura-ext = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-pallet-parachain-system = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-pallet-dmp-queue = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-pallet-xcm = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-pallet-xcmp-queue = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -cumulus-primitives-timestamp = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } -parachain-info = { git = "https://github.com/paritytech//cumulus", branch = "cumulus-locked-for-gav-xcm-v3-and-bridges" } diff --git a/README.md b/README.md index d45b328b2bd..b5885026426 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,6 @@ Substrate chains. 🚧 The bridges are currently under construction - a hardhat is recommended beyond this point 🚧 -**IMPORTANT**: this documentation is outdated and it is mostly related to the previous version of our -bridge. Right there's an ongoing work to make our bridge work with XCM messages. Old bridge is still -available at [encoded-calls-messaging](https://github.com/paritytech/parity-bridges-common/releases/tag/encoded-calls-messaging) -tag. - ## Contents - [Installation](#installation) @@ -97,7 +92,7 @@ description of the bridge interaction. ## Project Layout -Here's an overview of how the project is laid out. The main bits are the `node`, which is the actual +Here's an overview of how the project is laid out. The main bits are the `bin`, which is the actual "blockchain", the `modules` which are used to build the blockchain's logic (a.k.a the runtime) and the `relays` which are used to pass messages between chains. @@ -106,16 +101,16 @@ the `relays` which are used to pass messages between chains. │ └── ... ├── deployments // Useful tools for deploying test networks │ └── ... -├── diagrams // Pretty pictures of the project architecture -│ └── ... ├── modules // Substrate Runtime Modules (a.k.a Pallets) +│ ├── beefy // On-Chain BEEFY Light Client (in progress) │ ├── grandpa // On-Chain GRANDPA Light Client │ ├── messages // Cross Chain Message Passing -│ ├── dispatch // Target Chain Message Execution +│ ├── parachains // On-Chain Parachains Light Client +│ ├── relayers // Relayer rewards registry │ └── ... ├── primitives // Code shared between modules, runtimes, and relays │ └── ... -├── relays // Application for sending headers and messages between chains +├── relays // Application for sending finality proofs and messages between chains │ └── ... └── scripts // Useful development and maintenance scripts ``` @@ -127,8 +122,11 @@ on each side of the bridge (source and target chain). There are 2 ways to run the bridge, described below: -- building & running from source -- running a Docker Compose setup (recommended). +- building & running from source: with this option, you'll be able to run the bridge between two standalone +chains that are running GRANDPA finality gadget to achieve finality; + +- running a Docker Compose setup: this is a recommended option, where you'll see bridges with parachains, +complex relays and more. ### Using the Source @@ -204,7 +202,33 @@ You will also see the message lane relayers listening for new messages. To send a message see the ["How to send a message" section](#how-to-send-a-message). -### Full Network Docker Compose Setup +### How to send a message + +In this section we'll show you how to quickly send a bridge message. The message is just an encoded XCM +`Trap(43)` message. + +```bash +# In `parity-bridges-common` folder +./scripts/send-message-from-millau-rialto.sh +``` + +After sending a message you will see the following logs showing a message was successfully sent: + +``` +INFO bridge Sending message to Rialto. Size: 5. +TRACE bridge Sent transaction to Millau node: 0x5e68... +``` + +And at the Rialto node logs you'll something like this: + +``` +... runtime::bridge-dispatch: Going to execute message ([0, 0, 0, 0], 1) (...), Trap(43)]) +... runtime::bridge-dispatch: Incoming message ([0, 0, 0, 0], 1) dispatched with result: Incomplete(2000000000, Trap(43)) +``` + +It means that the message has been delivered and successfully dispatched. + +## Full Network Docker Compose Setup For a more sophisticated deployment which includes bidirectional header sync, message passing, monitoring dashboards, etc. see the [Deployments README](./deployments/README.md). @@ -220,24 +244,6 @@ docker run -p 30333:30333 -p 9933:9933 -p 9944:9944 \ --rpc-cors=all --unsafe-rpc-external --unsafe-ws-external ``` -### How to send a message - -In this section we'll show you how to quickly send a bridge message, if you want to -interact with and test the bridge see more details in [send message](./docs/send-message.md) - -```bash -# In `parity-bridges-common` folder -./scripts/send-message-from-millau-rialto.sh remark -``` - -After sending a message you will see the following logs showing a message was successfully sent: - -``` -INFO bridge Sending message to Rialto. Size: 286. Dispatch weight: 1038000. Fee: 275,002,568 -INFO bridge Signed Millau Call: 0x7904... -TRACE bridge Sent transaction to Millau node: 0x5e68... -``` - ## Community Main hangout for the community is [Element](https://element.io/) (formerly Riot). Element is a chat diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..65f2f3bff05 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,14 @@ +# Security Policy + +Thanks for helping make the Parity ecosystem more secure. Security is one of our first priorities. + +## Reporting a vulnerability + +If you find something that can be treated as a security vulnerability, please do not use the issue tracker or discuss it in the public forum as it can cause more damage, rather than giving real help to the ecosystem. + +Security vulnerabilities should be reported by the [contact form](https://security-submission.parity.io/). + +If you think that your report might be eligible for the Bug Bounty Program, please mark this during the submission. Please check up-to-date [Parity Bug Bounty Program rules](https://www.parity.io/bug-bounty) to find out the information about our Bug Bounty Program. + +**Warning**: This is an unified SECURITY.md file for Paritytech GitHub Organization. The presence of this file does not mean that this repository is covered by the Bug Bounty program. Please always check the Bug Bounty Program scope for information. + diff --git a/bin/millau/node/Cargo.toml b/bin/millau/node/Cargo.toml index a8d03c6f1a4..08661951eed 100644 --- a/bin/millau/node/Cargo.toml +++ b/bin/millau/node/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] clap = { version = "4.0.9", features = ["derive"] } -jsonrpsee = { version = "0.15.1", features = ["server"] } +jsonrpsee = { version = "0.16.2", features = ["server"] } serde_json = "1.0.79" # Bridge dependencies @@ -21,14 +21,14 @@ millau-runtime = { path = "../runtime" } beefy-gadget = { git = "https://github.com/paritytech/substrate", branch = "master" } beefy-gadget-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } -beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-beefy = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } node-inspect = { git = "https://github.com/paritytech/substrate", branch = "master" } -pallet-mmr-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +mmr-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-basic-authorship = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master", features = ["wasmtime"] } +sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master"} sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -40,7 +40,6 @@ sc-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-service = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-telemetry = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/bin/millau/node/src/chain_spec.rs b/bin/millau/node/src/chain_spec.rs index 2ded668fdee..8b0bc28ed14 100644 --- a/bin/millau/node/src/chain_spec.rs +++ b/bin/millau/node/src/chain_spec.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use beefy_primitives::crypto::AuthorityId as BeefyId; use millau_runtime::{ AccountId, AuraConfig, BalancesConfig, BeefyConfig, BridgeRialtoMessagesConfig, BridgeRialtoParachainMessagesConfig, BridgeWestendGrandpaConfig, GenesisConfig, GrandpaConfig, SessionConfig, SessionKeys, Signature, SudoConfig, SystemConfig, WASM_BINARY, }; +use sp_beefy::crypto::AuthorityId as BeefyId; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{sr25519, Pair, Public}; use sp_finality_grandpa::AuthorityId as GrandpaId; diff --git a/bin/millau/node/src/service.rs b/bin/millau/node/src/service.rs index e23e1fa56ca..6ad63387da4 100644 --- a/bin/millau/node/src/service.rs +++ b/bin/millau/node/src/service.rs @@ -199,8 +199,7 @@ pub fn new_full(mut config: Configuration) -> Result Ok(k) => keystore_container.set_remote_keystore(k), Err(e) => return Err(ServiceError::Other(format!( - "Error hooking up remote keystore for {}: {}", - url, e + "Error hooking up remote keystore for {url}: {e}" ))), }; } @@ -273,7 +272,7 @@ pub fn new_full(mut config: Configuration) -> Result use sc_finality_grandpa::FinalityProofProvider as GrandpaFinalityProofProvider; use beefy_gadget_rpc::{Beefy, BeefyApiServer}; - use pallet_mmr_rpc::{Mmr, MmrApiServer}; + use mmr_rpc::{Mmr, MmrApiServer}; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; use sc_finality_grandpa_rpc::{Grandpa, GrandpaApiServer}; use sc_rpc::DenyUnsafe; @@ -330,7 +329,7 @@ pub fn new_full(mut config: Configuration) -> Result keystore: keystore_container.sync_keystore(), task_manager: &mut task_manager, transaction_pool: transaction_pool.clone(), - rpc_builder: rpc_extensions_builder.clone(), + rpc_builder: rpc_extensions_builder, backend: backend.clone(), system_rpc_tx, config, @@ -392,7 +391,7 @@ pub fn new_full(mut config: Configuration) -> Result if role.is_authority() { Some(keystore_container.sync_keystore()) } else { None }; let justifications_protocol_name = beefy_on_demand_justifications_handler.protocol_name(); - let payload_provider = beefy_primitives::mmr::MmrRootProvider::new(client.clone()); + let payload_provider = sp_beefy::mmr::MmrRootProvider::new(client.clone()); let beefy_params = beefy_gadget::BeefyParams { client: client.clone(), backend, diff --git a/bin/millau/runtime/Cargo.toml b/bin/millau/runtime/Cargo.toml index b297dedfa53..0e5868f845e 100644 --- a/bin/millau/runtime/Cargo.toml +++ b/bin/millau/runtime/Cargo.toml @@ -9,13 +9,13 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] hex-literal = "0.3" codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } # Bridge dependencies bp-messages = { path = "../../../primitives/messages", default-features = false } bp-millau = { path = "../../../primitives/chain-millau", default-features = false } +bp-parachains = { path = "../../../primitives/parachains", default-features = false } bp-polkadot-core = { path = "../../../primitives/polkadot-core", default-features = false } bp-relayers = { path = "../../../primitives/relayers", default-features = false } bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false } @@ -31,7 +31,7 @@ pallet-shift-session-manager = { path = "../../../modules/shift-session-manager" # Substrate Dependencies -beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } frame-executive = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -49,13 +49,13 @@ pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "maste pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-utility = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -80,9 +80,10 @@ substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", bran [features] default = ["std"] std = [ - "beefy-primitives/std", + "sp-beefy/std", "bp-messages/std", "bp-millau/std", + "bp-parachains/std", "bp-polkadot-core/std", "bp-relayers/std", "bp-rialto/std", @@ -95,7 +96,6 @@ std = [ "frame-support/std", "frame-system-rpc-runtime-api/std", "frame-system/std", - "log/std", "pallet-aura/std", "pallet-balances/std", "pallet-beefy/std", @@ -113,6 +113,7 @@ std = [ "pallet-timestamp/std", "pallet-transaction-payment-rpc-runtime-api/std", "pallet-transaction-payment/std", + "pallet-utility/std", "pallet-xcm/std", "scale-info/std", "sp-api/std", diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index b1f1e33d18a..dbff7f5940c 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -32,17 +32,16 @@ pub mod rialto_messages; pub mod rialto_parachain_messages; pub mod xcm_config; -use beefy_primitives::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; -use bp_runtime::{HeaderId, HeaderIdProvider}; -use codec::Decode; +use bp_parachains::SingleParaStoredHeaderDataBuilder; +use bp_runtime::HeaderId; use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; use pallet_transaction_payment::{FeeDetails, Multiplier, RuntimeDispatchInfo}; use sp_api::impl_runtime_apis; +use sp_beefy::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; -use sp_mmr_primitives::{DataOrHash, EncodableOpaqueLeaf, Error as MmrError, Proof as MmrProof}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{Block as BlockT, IdentityLookup, Keccak256, NumberFor, OpaqueKeys}, @@ -62,9 +61,10 @@ pub use frame_support::{ construct_runtime, dispatch::DispatchClass, parameter_types, - traits::{Currency, ExistenceRequirement, Imbalance, KeyOwnerProofSystem}, + traits::{ConstU32, ConstU8, Currency, ExistenceRequirement, Imbalance, KeyOwnerProofSystem}, weights::{ - constants::WEIGHT_PER_SECOND, ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight, + constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, IdentityFee, RuntimeDbWeight, + Weight, }, RuntimeDebug, StorageValue, }; @@ -215,19 +215,15 @@ impl frame_system::Config for Runtime { impl pallet_randomness_collective_flip::Config for Runtime {} -parameter_types! { - pub const MaxAuthorities: u32 = 10; -} - impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; - type MaxAuthorities = MaxAuthorities; + type MaxAuthorities = ConstU32<10>; type DisabledValidators = (); } impl pallet_beefy::Config for Runtime { type BeefyId = BeefyId; - type MaxAuthorities = MaxAuthorities; + type MaxAuthorities = ConstU32<10>; type OnNewValidatorSet = MmrLeaf; } @@ -243,7 +239,7 @@ impl pallet_grandpa::Config for Runtime { type HandleEquivocation = (); // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) type WeightInfo = (); - type MaxAuthorities = MaxAuthorities; + type MaxAuthorities = ConstU32<10>; } /// MMR helper types. @@ -285,7 +281,7 @@ parameter_types! { pub struct BeefyDummyDataProvider; -impl beefy_primitives::mmr::BeefyDataProvider<()> for BeefyDummyDataProvider { +impl sp_beefy::mmr::BeefyDataProvider<()> for BeefyDummyDataProvider { fn extra_data() {} } @@ -311,10 +307,6 @@ impl pallet_timestamp::Config for Runtime { parameter_types! { pub const ExistentialDeposit: bp_millau::Balance = 500; - // For weight estimation, we assume that the most locks on an individual account will be 50. - // This number may need to be adjusted in the future if this assumption no longer holds true. - pub const MaxLocks: u32 = 50; - pub const MaxReserves: u32 = 50; } impl pallet_balances::Config for Runtime { @@ -327,15 +319,16 @@ impl pallet_balances::Config for Runtime { type AccountStore = System; // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) type WeightInfo = (); - type MaxLocks = MaxLocks; - type MaxReserves = MaxReserves; + // For weight estimation, we assume that the most locks on an individual account will be 50. + // This number may need to be adjusted in the future if this assumption no longer holds true. + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<50>; type ReserveIdentifier = [u8; 8]; } parameter_types! { pub const TransactionBaseFee: Balance = 0; pub const TransactionByteFee: Balance = 1; - pub const OperationalFeeMultiplier: u8 = 5; // values for following parameters are copied from polkadot repo, but it is fine // not to sync them - we're not going to make Rialto a full copy of one of Polkadot-like chains pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); @@ -346,7 +339,7 @@ parameter_types! { impl pallet_transaction_payment::Config for Runtime { type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; - type OperationalFeeMultiplier = OperationalFeeMultiplier; + type OperationalFeeMultiplier = ConstU8<5>; type WeightToFee = bp_millau::WeightToFee; type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< @@ -383,18 +376,11 @@ impl pallet_session::Config for Runtime { type WeightInfo = (); } -parameter_types! { - // This is a pretty unscientific cap. - // - // Note that once this is hit the pallet will essentially throttle incoming requests down to one - // call per block. - pub const MaxRequests: u32 = 50; -} - impl pallet_bridge_relayers::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reward = Balance; - type PaymentProcedure = bp_relayers::MintReward, AccountId>; + type PaymentProcedure = + bp_relayers::PayLaneRewardFromAccount, AccountId>; type WeightInfo = (); } @@ -427,22 +413,20 @@ parameter_types! { } parameter_types! { - /// Maximal size of SCALE-encoded Rialto header. - pub const MaxRialtoHeaderSize: u32 = bp_rialto::MAX_HEADER_SIZE; - /// Maximal number of authorities at Westend. pub const MaxAuthoritiesAtWestend: u32 = bp_westend::MAX_AUTHORITIES_COUNT; - /// Maximal size of SCALE-encoded Westend header. - pub const MaxWestendHeaderSize: u32 = bp_westend::MAX_HEADER_SIZE; } pub type RialtoGrandpaInstance = (); impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_rialto::Rialto; - type MaxRequests = MaxRequests; + // This is a pretty unscientific cap. + // + // Note that once this is hit the pallet will essentially throttle incoming requests down to one + // call per block. + type MaxRequests = ConstU32<50>; type HeadersToKeep = HeadersToKeep; type MaxBridgedAuthorities = MaxAuthoritiesAtRialto; - type MaxBridgedHeaderSize = MaxRialtoHeaderSize; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } @@ -450,10 +434,9 @@ impl pallet_bridge_grandpa::Config for Runtime { pub type WestendGrandpaInstance = pallet_bridge_grandpa::Instance1; impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_westend::Westend; - type MaxRequests = MaxRequests; + type MaxRequests = ConstU32<50>; type HeadersToKeep = HeadersToKeep; type MaxBridgedAuthorities = MaxAuthoritiesAtWestend; - type MaxBridgedHeaderSize = MaxWestendHeaderSize; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } @@ -488,14 +471,15 @@ impl pallet_bridge_messages::Config for Runtime { type InboundPayload = crate::rialto_messages::FromRialtoMessagePayload; type InboundRelayer = bp_rialto::AccountId; + type DeliveryPayments = (); type TargetHeaderChain = crate::rialto_messages::Rialto; type LaneMessageVerifier = crate::rialto_messages::ToRialtoMessageVerifier; - type MessageDeliveryAndDispatchPayment = - pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter< - Runtime, - WithRialtoMessagesInstance, - >; + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + Runtime, + frame_support::traits::ConstU64<100_000>, + frame_support::traits::ConstU64<10_000>, + >; type SourceHeaderChain = crate::rialto_messages::Rialto; type MessageDispatch = crate::rialto_messages::FromRialtoMessageDispatch; @@ -518,14 +502,15 @@ impl pallet_bridge_messages::Config for Run type InboundPayload = crate::rialto_parachain_messages::FromRialtoParachainMessagePayload; type InboundRelayer = bp_rialto_parachain::AccountId; + type DeliveryPayments = (); type TargetHeaderChain = crate::rialto_parachain_messages::RialtoParachain; type LaneMessageVerifier = crate::rialto_parachain_messages::ToRialtoParachainMessageVerifier; - type MessageDeliveryAndDispatchPayment = - pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter< - Runtime, - WithRialtoParachainMessagesInstance, - >; + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + Runtime, + frame_support::traits::ConstU64<100_000>, + frame_support::traits::ConstU64<10_000>, + >; type SourceHeaderChain = crate::rialto_parachain_messages::RialtoParachain; type MessageDispatch = crate::rialto_parachain_messages::FromRialtoParachainMessageDispatch; @@ -533,10 +518,12 @@ impl pallet_bridge_messages::Config for Run } parameter_types! { + pub const RialtoParachainMessagesLane: bp_messages::LaneId = rialto_parachain_messages::XCM_LANE; + pub const RialtoParachainId: u32 = bp_rialto_parachain::RIALTO_PARACHAIN_ID; pub const RialtoParasPalletName: &'static str = bp_rialto::PARAS_PALLET_NAME; pub const WestendParasPalletName: &'static str = bp_westend::PARAS_PALLET_NAME; - pub const MaxRialtoParaHeadSize: u32 = bp_rialto::MAX_NESTED_PARACHAIN_HEAD_SIZE; - pub const MaxWestendParaHeadSize: u32 = bp_westend::MAX_NESTED_PARACHAIN_HEAD_SIZE; + pub const MaxRialtoParaHeadDataSize: u32 = bp_rialto::MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE; + pub const MaxWestendParaHeadDataSize: u32 = bp_westend::MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE; } /// Instance of the with-Rialto parachains pallet. @@ -547,9 +534,10 @@ impl pallet_bridge_parachains::Config for Runtime type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; type BridgesGrandpaPalletInstance = RialtoGrandpaInstance; type ParasPalletName = RialtoParasPalletName; - type TrackedParachains = frame_support::traits::Everything; + type ParaStoredHeaderDataBuilder = + SingleParaStoredHeaderDataBuilder; type HeadsToKeep = HeadersToKeep; - type MaxParaHeadSize = MaxRialtoParaHeadSize; + type MaxParaHeadDataSize = MaxRialtoParaHeadDataSize; } /// Instance of the with-Westend parachains pallet. @@ -560,9 +548,16 @@ impl pallet_bridge_parachains::Config for Runtime type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; type BridgesGrandpaPalletInstance = WestendGrandpaInstance; type ParasPalletName = WestendParasPalletName; - type TrackedParachains = frame_support::traits::Everything; + type ParaStoredHeaderDataBuilder = SingleParaStoredHeaderDataBuilder; type HeadsToKeep = HeadersToKeep; - type MaxParaHeadSize = MaxWestendParaHeadSize; + type MaxParaHeadDataSize = MaxWestendParaHeadDataSize; +} + +impl pallet_utility::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); } construct_runtime!( @@ -573,6 +568,7 @@ construct_runtime!( { System: frame_system::{Pallet, Call, Config, Storage, Event}, Sudo: pallet_sudo::{Pallet, Call, Config, Storage, Event}, + Utility: pallet_utility, // Must be before session. Aura: pallet_aura::{Pallet, Config}, @@ -620,6 +616,19 @@ generate_bridge_reject_obsolete_headers_and_messages! { BridgeRialtoMessages, BridgeRialtoParachainMessages } +/// Signed extension that refunds relayers that are delivering messages from the Rialto parachain. +pub type BridgeRefundRialtoParachainRelayers = + bridge_runtime_common::refund_relayer_extension::RefundRelayerForMessagesFromParachain< + Runtime, + RialtoGrandpaInstance, + WithRialtoParachainsInstance, + WithRialtoParachainMessagesInstance, + BridgeRejectObsoleteHeadersAndMessages, + RialtoParachainId, + RialtoParachainMessagesLane, + Runtime, + >; + /// The address format for describing accounts. pub type Address = AccountId; /// Block header type as expected by this runtime. @@ -641,6 +650,7 @@ pub type SignedExtra = ( frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, BridgeRejectObsoleteHeadersAndMessages, + BridgeRefundRialtoParachainRelayers, ); /// The payload being signed in transactions. pub type SignedPayload = generic::SignedPayload; @@ -756,62 +766,30 @@ impl_runtime_apis! { } } - impl beefy_primitives::BeefyApi for Runtime { + impl sp_beefy::BeefyApi for Runtime { fn validator_set() -> Option> { Beefy::validator_set() } } - impl sp_mmr_primitives::MmrApi for Runtime { - fn generate_proof(block_number: BlockNumber) - -> Result<(EncodableOpaqueLeaf, MmrProof), MmrError> - { - Mmr::generate_batch_proof(vec![block_number]) - .and_then(|(leaves, proof)| Ok(( - mmr::EncodableOpaqueLeaf::from_leaf(&leaves[0]), - mmr::BatchProof::into_single_leaf_proof(proof)? - ))) - } - - fn verify_proof(leaf: EncodableOpaqueLeaf, proof: MmrProof) - -> Result<(), MmrError> - { - let leaf: mmr::Leaf = leaf - .into_opaque_leaf() - .try_decode() - .ok_or(MmrError::Verify)?; - Mmr::verify_leaves(vec![leaf], mmr::Proof::into_batch_proof(proof)) - } - - fn verify_proof_stateless( - root: mmr::Hash, - leaf: EncodableOpaqueLeaf, - proof: MmrProof - ) -> Result<(), MmrError> { - let node = DataOrHash::Data(leaf.into_opaque_leaf()); - pallet_mmr::verify_leaves_proof::( - root, - vec![node], - pallet_mmr::primitives::Proof::into_batch_proof(proof), - ) - } - - fn mmr_root() -> Result { + impl pallet_mmr::primitives::MmrApi< + Block, + mmr::Hash, + BlockNumber, + > for Runtime { + fn mmr_root() -> Result { Ok(Mmr::mmr_root()) } - fn generate_batch_proof(block_numbers: Vec) - -> Result<(Vec, mmr::BatchProof), mmr::Error> - { - Mmr::generate_batch_proof(block_numbers) - .map(|(leaves, proof)| (leaves.into_iter().map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf)).collect(), proof)) + fn mmr_leaf_count() -> Result { + Ok(Mmr::mmr_leaves()) } - fn generate_historical_batch_proof( + fn generate_proof( block_numbers: Vec, - best_known_block_number: BlockNumber - ) -> Result<(Vec, mmr::BatchProof), mmr::Error> { - Mmr::generate_historical_batch_proof(block_numbers, best_known_block_number).map( + best_known_block_number: Option, + ) -> Result<(Vec, mmr::Proof), mmr::Error> { + Mmr::generate_proof(block_numbers, best_known_block_number).map( |(leaves, proof)| { ( leaves @@ -824,7 +802,7 @@ impl_runtime_apis! { ) } - fn verify_batch_proof(leaves: Vec, proof: mmr::BatchProof) + fn verify_proof(leaves: Vec, proof: mmr::Proof) -> Result<(), mmr::Error> { let leaves = leaves.into_iter().map(|leaf| @@ -834,10 +812,10 @@ impl_runtime_apis! { Mmr::verify_leaves(leaves, proof) } - fn verify_batch_proof_stateless( + fn verify_proof_stateless( root: mmr::Hash, leaves: Vec, - proof: mmr::BatchProof + proof: mmr::Proof ) -> Result<(), mmr::Error> { let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect(); pallet_mmr::verify_leaves_proof::(root, nodes, proof) @@ -881,40 +859,31 @@ impl_runtime_apis! { impl bp_rialto::RialtoFinalityApi for Runtime { fn best_finalized() -> Option> { - BridgeRialtoGrandpa::best_finalized().map(|header| header.id()) + BridgeRialtoGrandpa::best_finalized() } } impl bp_westend::WestendFinalityApi for Runtime { fn best_finalized() -> Option> { - BridgeWestendGrandpa::best_finalized().map(|header| header.id()) + BridgeWestendGrandpa::best_finalized() } } impl bp_westend::WestmintFinalityApi for Runtime { fn best_finalized() -> Option> { - // the parachains finality pallet is never decoding parachain heads, so it is - // only done in the integration code - use bp_westend::WESTMINT_PARACHAIN_ID; - let encoded_head = pallet_bridge_parachains::Pallet::< + pallet_bridge_parachains::Pallet::< Runtime, WithWestendParachainsInstance, - >::best_parachain_head(WESTMINT_PARACHAIN_ID.into())?; - let head = bp_westend::Header::decode(&mut &encoded_head.0[..]).ok()?; - Some(head.id()) + >::best_parachain_head_id::().unwrap_or(None) } } impl bp_rialto_parachain::RialtoParachainFinalityApi for Runtime { fn best_finalized() -> Option> { - // the parachains finality pallet is never decoding parachain heads, so it is - // only done in the integration code - let encoded_head = pallet_bridge_parachains::Pallet::< + pallet_bridge_parachains::Pallet::< Runtime, WithRialtoParachainsInstance, - >::best_parachain_head(bp_rialto_parachain::RIALTO_PARACHAIN_ID.into())?; - let head = bp_rialto_parachain::Header::decode(&mut &encoded_head.0[..]).ok()?; - Some(head.id()) + >::best_parachain_head_id::().unwrap_or(None) } } @@ -979,13 +948,14 @@ impl_runtime_apis! { use pallet_bridge_messages::benchmarking::Pallet as MessagesBench; use pallet_bridge_parachains::benchmarking::Pallet as ParachainsBench; + use pallet_bridge_relayers::benchmarking::Pallet as RelayersBench; let mut list = Vec::::new(); list_benchmark!(list, extra, pallet_bridge_messages, MessagesBench::); list_benchmark!(list, extra, pallet_bridge_grandpa, BridgeRialtoGrandpa); list_benchmark!(list, extra, pallet_bridge_parachains, ParachainsBench::); - list_benchmark!(list, extra, pallet_bridge_relayers, BridgeRelayers); + list_benchmark!(list, extra, pallet_bridge_relayers, RelayersBench::); let storage_info = AllPalletsWithSystem::storage_info(); @@ -1024,6 +994,10 @@ impl_runtime_apis! { Pallet as ParachainsBench, Config as ParachainsConfig, }; + use pallet_bridge_relayers::benchmarking::{ + Pallet as RelayersBench, + Config as RelayersConfig, + }; use rialto_messages::WithRialtoMessageBridge; impl MessagesConfig for Runtime { @@ -1031,6 +1005,10 @@ impl_runtime_apis! { [0u8; 32].into() } + fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { + pallet_bridge_relayers::Pallet::::relayer_reward(relayer, &Self::bench_lane_id()).is_some() + } + fn endow_account(account: &Self::AccountId) { pallet_balances::Pallet::::make_free_balance_be( account, @@ -1060,6 +1038,11 @@ impl_runtime_apis! { } impl ParachainsConfig for Runtime { + fn parachains() -> Vec { + use bp_runtime::Parachain; + vec![bp_polkadot_core::parachains::ParaId(bp_rialto_parachain::RialtoParachain::PARACHAIN_ID)] + } + fn prepare_parachain_heads_proof( parachains: &[bp_polkadot_core::parachains::ParaId], parachain_head_size: u32, @@ -1078,6 +1061,20 @@ impl_runtime_apis! { } } + impl RelayersConfig for Runtime { + fn prepare_environment( + lane: bp_messages::LaneId, + reward: Balance, + ) { + use frame_support::traits::fungible::Mutate; + let lane_rewards_account = bp_relayers::PayLaneRewardFromAccount::< + Balances, + AccountId + >::lane_rewards_account(lane); + Balances::mint_into(&lane_rewards_account, reward).unwrap(); + } + } + add_benchmark!( params, batches, @@ -1091,7 +1088,7 @@ impl_runtime_apis! { pallet_bridge_parachains, ParachainsBench:: ); - add_benchmark!(params, batches, pallet_bridge_relayers, BridgeRelayers); + add_benchmark!(params, batches, pallet_bridge_relayers, RelayersBench::); Ok(batches) } diff --git a/bin/millau/runtime/src/rialto_messages.rs b/bin/millau/runtime/src/rialto_messages.rs index ff4b6ba1dda..ac203a2f6dd 100644 --- a/bin/millau/runtime/src/rialto_messages.rs +++ b/bin/millau/runtime/src/rialto_messages.rs @@ -28,7 +28,7 @@ use bridge_runtime_common::messages::{self, MessageBridge}; use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; /// Default lane that is used to send messages to Rialto. -pub const XCM_LANE: LaneId = [0, 0, 0, 0]; +pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); /// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge /// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual /// tests, confirming that we don't break encoding somewhere between. @@ -163,42 +163,19 @@ mod tests { use super::*; use crate::{Runtime, WithRialtoMessagesInstance}; - use bp_runtime::Chain; use bridge_runtime_common::{ assert_complete_bridge_types, integrity::{ - assert_complete_bridge_constants, AssertBridgeMessagesPalletConstants, - AssertBridgePalletNames, AssertChainConstants, AssertCompleteBridgeConstants, + assert_complete_bridge_constants, check_message_lane_weights, + AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants, + AssertCompleteBridgeConstants, }, - messages, }; #[test] fn ensure_millau_message_lane_weights_are_correct() { - type Weights = pallet_bridge_messages::weights::BridgeWeight; - - pallet_bridge_messages::ensure_weights_are_correct::(); - - let max_incoming_message_proof_size = bp_rialto::EXTRA_STORAGE_PROOF_SIZE.saturating_add( - messages::target::maximal_incoming_message_size(bp_millau::Millau::max_extrinsic_size()), - ); - pallet_bridge_messages::ensure_able_to_receive_message::( - bp_millau::Millau::max_extrinsic_size(), - bp_millau::Millau::max_extrinsic_weight(), - max_incoming_message_proof_size, - messages::target::maximal_incoming_message_dispatch_weight( - bp_millau::Millau::max_extrinsic_weight(), - ), - ); - - let max_incoming_inbound_lane_data_proof_size = - bp_messages::InboundLaneData::<()>::encoded_size_hint_u32( - bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX as _, - ); - pallet_bridge_messages::ensure_able_to_receive_confirmation::( - bp_millau::Millau::max_extrinsic_size(), - bp_millau::Millau::max_extrinsic_weight(), - max_incoming_inbound_lane_data_proof_size, + check_message_lane_weights::( + bp_rialto::EXTRA_STORAGE_PROOF_SIZE, bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, ); diff --git a/bin/millau/runtime/src/rialto_parachain_messages.rs b/bin/millau/runtime/src/rialto_parachain_messages.rs index b6e8f52d342..097e2798227 100644 --- a/bin/millau/runtime/src/rialto_parachain_messages.rs +++ b/bin/millau/runtime/src/rialto_parachain_messages.rs @@ -28,7 +28,7 @@ use bridge_runtime_common::messages::{self, MessageBridge}; use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; /// Default lane that is used to send messages to Rialto parachain. -pub const XCM_LANE: LaneId = [0, 0, 0, 0]; +pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); /// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge /// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual /// tests, confirming that we don't break encoding somewhere between. diff --git a/bin/millau/runtime/src/xcm_config.rs b/bin/millau/runtime/src/xcm_config.rs index b5be117ac77..17bdc43da3d 100644 --- a/bin/millau/runtime/src/xcm_config.rs +++ b/bin/millau/runtime/src/xcm_config.rs @@ -31,7 +31,8 @@ use bridge_runtime_common::{ }; use frame_support::{ parameter_types, - traits::{Everything, Nothing}, + traits::{ConstU32, Everything, Nothing}, + weights::Weight, }; use xcm::latest::prelude::*; use xcm_builder::{ @@ -97,7 +98,8 @@ pub const BASE_XCM_WEIGHT: u64 = 1_000_000_000; parameter_types! { /// The amount of weight an XCM operation takes. This is a safe overestimate. - pub const BaseXcmWeight: u64 = BASE_XCM_WEIGHT; + // TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - check `set_proof_size` 0 or 64*1024 or 1026? + pub const BaseXcmWeight: Weight = Weight::from_parts(BASE_XCM_WEIGHT, 0); /// Maximum number of instructions in a single XCM fragment. A sanity check against weight /// calculations getting too crazy. pub const MaxInstructions: u32 = 100; @@ -112,10 +114,6 @@ pub type XcmRouter = ( XcmBridgeAdapter, ); -parameter_types! { - pub const MaxAssetsIntoHolding: u32 = 64; -} - /// The barriers one of which must be passed for an XCM message to be executed. pub type Barrier = ( // Weight that is paid for may be consumed. @@ -149,11 +147,12 @@ impl xcm_executor::Config for XcmConfig { type AssetClaims = XcmPallet; type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MaxAssetsIntoHolding = ConstU32<64>; type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; } /// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior @@ -163,6 +162,11 @@ pub type LocalOriginToLocation = ( SignedToAccountId32, ); +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = todo!("We dont use benchmarks for pallet_xcm, so if you hit this message, you need to remove this and define value instead"); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; // We don't allow any messages to be sent via the transaction yet. This is basically safe to @@ -192,6 +196,9 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = SovereignAccountOf; type MaxLockers = frame_support::traits::ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } /// With-Rialto bridge. @@ -302,7 +309,7 @@ mod tests { let xcm: Xcm = vec![Instruction::Trap(42)].into(); let mut incoming_message = DispatchMessage { - key: MessageKey { lane_id: [0, 0, 0, 0], nonce: 1 }, + key: MessageKey { lane_id: LaneId([0, 0, 0, 0]), nonce: 1 }, data: DispatchMessageData { payload: Ok((location, xcm).into()) }, }; @@ -318,7 +325,6 @@ mod tests { dispatch_result, MessageDispatchResult { unspent_weight: frame_support::weights::Weight::from_ref_time(0), - dispatch_fee_paid_during_dispatch: false, dispatch_level_result: (), } ); diff --git a/bin/rialto-parachain/node/Cargo.toml b/bin/rialto-parachain/node/Cargo.toml index 03d1a28c6a4..7b81de4fd3a 100644 --- a/bin/rialto-parachain/node/Cargo.toml +++ b/bin/rialto-parachain/node/Cargo.toml @@ -23,7 +23,7 @@ codec = { package = 'parity-scale-codec', version = '3.1.5' } serde = { version = '1.0', features = ['derive'] } # RPC related Dependencies -jsonrpsee = { version = "0.15.1", features = ["server"] } +jsonrpsee = { version = "0.16.2", features = ["server"] } # Local Dependencies rialto-parachain-runtime = { path = '../runtime' } @@ -47,8 +47,7 @@ sc-executor = { git = "https://github.com/paritytech/substrate", branch = "maste sc-network = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-rpc-api = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-service = { git = "https://github.com/paritytech/substrate", branch = "master", features = ['wasmtime'] } -sc-sysinfo = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-service = { git = "https://github.com/paritytech/substrate", branch = "master"} sc-telemetry = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-tracing = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -56,7 +55,6 @@ sc-tracing = { git = "https://github.com/paritytech/substrate", branch = "master ## Substrate Primitive Dependencies sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -75,8 +73,6 @@ cumulus-client-service = { git = "https://github.com/paritytech/cumulus", branch cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "master" } cumulus-primitives-parachain-inherent = { git = "https://github.com/paritytech/cumulus", branch = "master" } cumulus-relay-chain-interface = { git = "https://github.com/paritytech/cumulus", branch = "master" } -cumulus-relay-chain-inprocess-interface = { git = "https://github.com/paritytech/cumulus", branch = "master" } -cumulus-relay-chain-minimal-node = { git = "https://github.com/paritytech/cumulus", branch = "master" } # Polkadot dependencies polkadot-cli = { git = "https://github.com/paritytech/polkadot", branch = "master" } diff --git a/bin/rialto-parachain/node/src/cli.rs b/bin/rialto-parachain/node/src/cli.rs index 51fa1e9776c..a003c91113c 100644 --- a/bin/rialto-parachain/node/src/cli.rs +++ b/bin/rialto-parachain/node/src/cli.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . +#![allow(clippy::large_enum_variant)] + use crate::chain_spec; use clap::Parser; use std::path::PathBuf; diff --git a/bin/rialto-parachain/node/src/command.rs b/bin/rialto-parachain/node/src/command.rs index dd9e95abbe5..2cb08ec4ea2 100644 --- a/bin/rialto-parachain/node/src/command.rs +++ b/bin/rialto-parachain/node/src/command.rs @@ -138,7 +138,6 @@ macro_rules! construct_async_run { runner.async_run(|$config| { let $components = new_partial::< RuntimeApi, - ParachainRuntimeExecutor, _ >( &$config, diff --git a/bin/rialto-parachain/node/src/service.rs b/bin/rialto-parachain/node/src/service.rs index 7dc25f6550d..254340a8ad4 100644 --- a/bin/rialto-parachain/node/src/service.rs +++ b/bin/rialto-parachain/node/src/service.rs @@ -30,17 +30,16 @@ use rialto_parachain_runtime::RuntimeApi; // Cumulus Imports use cumulus_client_cli::CollatorOptions; use cumulus_client_consensus_aura::{AuraConsensus, BuildAuraConsensusParams, SlotProportion}; -use cumulus_client_consensus_common::{ParachainBlockImport, ParachainConsensus}; +use cumulus_client_consensus_common::{ + ParachainBlockImport as TParachainBlockImport, ParachainConsensus, +}; use cumulus_client_network::BlockAnnounceValidator; use cumulus_client_service::{ prepare_node_config, start_collator, start_full_node, StartCollatorParams, StartFullNodeParams, }; use cumulus_primitives_core::ParaId; -use cumulus_relay_chain_inprocess_interface::build_inprocess_relay_chain; -use cumulus_relay_chain_interface::{RelayChainError, RelayChainInterface, RelayChainResult}; -use cumulus_relay_chain_minimal_node::build_minimal_relay_chain_node; -use polkadot_service::CollatorPair; - +use cumulus_relay_chain_interface::{RelayChainError, RelayChainInterface}; +use sc_consensus::ImportQueue; // Substrate Imports use sc_executor::{NativeElseWasmExecutor, NativeExecutionDispatch}; use sc_network::{NetworkBlock, NetworkService}; @@ -57,6 +56,12 @@ type Header = sp_runtime::generic::Header; type Hash = sp_core::H256; +type ParachainClient = + TFullClient>; +type ParachainBackend = TFullBackend; +type ParachainBlockImport = + TParachainBlockImport>, ParachainBackend>; + pub type ParachainRuntimeExecutor = ExecutorDispatch; // Our native executor instance. @@ -79,51 +84,39 @@ impl NativeExecutionDispatch for ExecutorDispatch { /// Use this macro if you don't actually need the full service, but just the builder in order to /// be able to perform chain operations. #[allow(clippy::type_complexity)] -pub fn new_partial( +pub fn new_partial( config: &Configuration, build_import_queue: BIQ, ) -> Result< PartialComponents< - TFullClient>, - TFullBackend, + ParachainClient, + ParachainBackend, (), - sc_consensus::DefaultImportQueue< - Block, - TFullClient>, - >, - sc_transaction_pool::FullPool< - Block, - TFullClient>, - >, - (Option, Option), + sc_consensus::DefaultImportQueue>, + sc_transaction_pool::FullPool>, + (ParachainBlockImport, Option, Option), >, sc_service::Error, > where - RuntimeApi: ConstructRuntimeApi>> - + Send - + Sync - + 'static, + RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue + sp_api::Metadata + sp_session::SessionKeys + sp_api::ApiExt< Block, - StateBackend = sc_client_api::StateBackendFor, Block>, + StateBackend = sc_client_api::StateBackendFor, > + sp_offchain::OffchainWorkerApi + sp_block_builder::BlockBuilder, - sc_client_api::StateBackendFor, Block>: sp_api::StateBackend, - Executor: NativeExecutionDispatch + 'static, + sc_client_api::StateBackendFor: sp_api::StateBackend, BIQ: FnOnce( - Arc>>, + Arc>, + ParachainBlockImport, &Configuration, Option, &TaskManager, ) -> Result< - sc_consensus::DefaultImportQueue< - Block, - TFullClient>, - >, + sc_consensus::DefaultImportQueue>, sc_service::Error, >, { @@ -138,7 +131,7 @@ where }) .transpose()?; - let executor = sc_executor::NativeElseWasmExecutor::::new( + let executor = sc_executor::NativeElseWasmExecutor::::new( config.wasm_method, config.default_heap_pages, config.max_runtime_instances, @@ -168,8 +161,11 @@ where client.clone(), ); + let block_import = ParachainBlockImport::new(client.clone(), backend.clone()); + let import_queue = build_import_queue( client.clone(), + block_import.clone(), config, telemetry.as_ref().map(|telemetry| telemetry.handle()), &task_manager, @@ -183,38 +179,17 @@ where task_manager, transaction_pool, select_chain: (), - other: (telemetry, telemetry_worker_handle), + other: (block_import, telemetry, telemetry_worker_handle), }; Ok(params) } -async fn build_relay_chain_interface( - polkadot_config: Configuration, - parachain_config: &Configuration, - telemetry_worker_handle: Option, - task_manager: &mut TaskManager, - collator_options: CollatorOptions, - hwbench: Option, -) -> RelayChainResult<(Arc<(dyn RelayChainInterface + 'static)>, Option)> { - match collator_options.relay_chain_rpc_url { - Some(relay_chain_url) => - build_minimal_relay_chain_node(polkadot_config, task_manager, relay_chain_url).await, - None => build_inprocess_relay_chain( - polkadot_config, - parachain_config, - telemetry_worker_handle, - task_manager, - hwbench, - ), - } -} - /// Start a node with the given parachain `Configuration` and relay chain `Configuration`. /// /// This is the actual implementation that is abstract over the executor and the runtime api. #[sc_tracing::logging::prefix_logs_with("Parachain")] -async fn start_node_impl( +async fn start_node_impl( parachain_config: Configuration, polkadot_config: Configuration, collator_options: CollatorOptions, @@ -222,63 +197,45 @@ async fn start_node_impl( rpc_ext_builder: RB, build_import_queue: BIQ, build_consensus: BIC, -) -> sc_service::error::Result<( - TaskManager, - Arc>>, -)> +) -> sc_service::error::Result<(TaskManager, Arc>)> where - RuntimeApi: ConstructRuntimeApi>> - + Send - + Sync - + 'static, + RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue + sp_api::Metadata + sp_session::SessionKeys + sp_api::ApiExt< Block, - StateBackend = sc_client_api::StateBackendFor, Block>, + StateBackend = sc_client_api::StateBackendFor, > + sp_offchain::OffchainWorkerApi + sp_block_builder::BlockBuilder + cumulus_primitives_core::CollectCollationInfo, - sc_client_api::StateBackendFor, Block>: sp_api::StateBackend, - Executor: NativeExecutionDispatch + 'static, + sc_client_api::StateBackendFor: sp_api::StateBackend, RB: Fn( sc_rpc_api::DenyUnsafe, - Arc>>, - Arc< - sc_transaction_pool::FullPool< - Block, - TFullClient>, - >, - >, + Arc>, + Arc>>, ) -> Result, sc_service::Error> + Send + Clone + 'static, BIQ: FnOnce( - Arc>>, + Arc>, + ParachainBlockImport, &Configuration, Option, &TaskManager, ) -> Result< - sc_consensus::DefaultImportQueue< - Block, - TFullClient>, - >, + sc_consensus::DefaultImportQueue>, sc_service::Error, >, BIC: FnOnce( - Arc>>, + Arc>, + ParachainBlockImport, Option<&Registry>, Option, &TaskManager, Arc, - Arc< - sc_transaction_pool::FullPool< - Block, - TFullClient>, - >, - >, + Arc>>, Arc>, SyncCryptoStorePtr, bool, @@ -286,23 +243,24 @@ where { let parachain_config = prepare_node_config(parachain_config); - let params = new_partial::(¶chain_config, build_import_queue)?; - let (mut telemetry, telemetry_worker_handle) = params.other; + let params = new_partial::(¶chain_config, build_import_queue)?; + let (block_import, mut telemetry, telemetry_worker_handle) = params.other; let mut task_manager = params.task_manager; - let (relay_chain_interface, collator_key) = build_relay_chain_interface( - polkadot_config, - ¶chain_config, - telemetry_worker_handle, - &mut task_manager, - collator_options, - None, - ) - .await - .map_err(|e| match e { - RelayChainError::ServiceError(polkadot_service::Error::Sub(x)) => x, - s => s.to_string().into(), - })?; + let (relay_chain_interface, collator_key) = + cumulus_client_service::build_relay_chain_interface( + polkadot_config, + ¶chain_config, + telemetry_worker_handle, + &mut task_manager, + collator_options, + None, + ) + .await + .map_err(|e| match e { + RelayChainError::ServiceError(polkadot_service::Error::Sub(x)) => x, + s => s.to_string().into(), + })?; let client = params.client.clone(); let backend = params.backend.clone(); @@ -312,14 +270,15 @@ where let validator = parachain_config.role.is_authority(); let prometheus_registry = parachain_config.prometheus_registry().cloned(); let transaction_pool = params.transaction_pool.clone(); - let import_queue = cumulus_client_service::SharedImportQueue::new(params.import_queue); + let import_queue_service = params.import_queue.service(); + let (network, system_rpc_tx, tx_handler_controller, start_network) = sc_service::build_network(sc_service::BuildNetworkParams { config: ¶chain_config, client: client.clone(), transaction_pool: transaction_pool.clone(), spawn_handle: task_manager.spawn_handle(), - import_queue: import_queue.clone(), + import_queue: params.import_queue, block_announce_validator_builder: Some(Box::new(|_| { Box::new(block_announce_validator) })), @@ -356,6 +315,7 @@ where if validator { let parachain_consensus = build_consensus( client.clone(), + block_import, prometheus_registry.as_ref(), telemetry.as_ref().map(|t| t.handle()), &task_manager, @@ -377,7 +337,7 @@ where relay_chain_interface, spawner, parachain_consensus, - import_queue, + import_queue: import_queue_service, collator_key: collator_key.expect("Command line arguments do not allow this. qed"), relay_chain_slot_duration, }; @@ -391,7 +351,7 @@ where para_id: id, relay_chain_interface, relay_chain_slot_duration, - import_queue, + import_queue: import_queue_service, }; start_full_node(params)?; @@ -405,17 +365,13 @@ where /// Build the import queue for the the parachain runtime. #[allow(clippy::type_complexity)] pub fn parachain_build_import_queue( - client: Arc>>, + client: Arc>, + block_import: ParachainBlockImport, config: &Configuration, telemetry: Option, task_manager: &TaskManager, -) -> Result< - sc_consensus::DefaultImportQueue< - Block, - TFullClient>, - >, - sc_service::Error, -> { +) -> Result>, sc_service::Error> +{ let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; cumulus_client_consensus_aura::import_queue::< @@ -426,18 +382,18 @@ pub fn parachain_build_import_queue( _, _, >(cumulus_client_consensus_aura::ImportQueueParams { - block_import: ParachainBlockImport::new(client.clone()), + block_import, client, create_inherent_data_providers: move |_, _| async move { - let time = sp_timestamp::InherentDataProvider::from_system_time(); + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( - *time, + *timestamp, slot_duration, ); - Ok((slot, time)) + Ok((slot, timestamp)) }, registry: config.prometheus_registry(), spawner: &task_manager.spawn_essential_handle(), @@ -452,11 +408,8 @@ pub async fn start_node( polkadot_config: Configuration, collator_options: CollatorOptions, id: ParaId, -) -> sc_service::error::Result<( - TaskManager, - Arc>>, -)> { - start_node_impl::( +) -> sc_service::error::Result<(TaskManager, Arc>)> { + start_node_impl::( parachain_config, polkadot_config, collator_options, @@ -475,6 +428,7 @@ pub async fn start_node( }, parachain_build_import_queue, |client, + block_import, prometheus_registry, telemetry, task_manager, @@ -483,11 +437,13 @@ pub async fn start_node( sync_oracle, keystore, force_authoring| { + let client2 = client.clone(); + let block_import2 = block_import; let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( task_manager.spawn_handle(), - client.clone(), + client, transaction_pool, prometheus_registry, telemetry.clone(), @@ -521,8 +477,8 @@ pub async fn start_node( Ok((slot, time, parachain_inherent)) } }, - block_import: ParachainBlockImport::new(client.clone()), - para_client: client, + block_import: block_import2, + para_client: client2, backoff_authoring_blocks: Option::<()>::None, sync_oracle, keystore, diff --git a/bin/rialto-parachain/runtime/Cargo.toml b/bin/rialto-parachain/runtime/Cargo.toml index 467fbd3699a..2327a864c2c 100644 --- a/bin/rialto-parachain/runtime/Cargo.toml +++ b/bin/rialto-parachain/runtime/Cargo.toml @@ -12,7 +12,6 @@ substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", bran [dependencies] codec = { package = 'parity-scale-codec', version = '3.1.5', default-features = false, features = ['derive']} hex-literal = "0.3" -log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } # Bridge depedencies @@ -95,7 +94,6 @@ std = [ "bp-rialto-parachain/std", "bridge-runtime-common/std", "codec/std", - "log/std", "scale-info/std", "sp-api/std", "sp-std/std", diff --git a/bin/rialto-parachain/runtime/src/lib.rs b/bin/rialto-parachain/runtime/src/lib.rs index f946abf4c1d..b9f4c236d86 100644 --- a/bin/rialto-parachain/runtime/src/lib.rs +++ b/bin/rialto-parachain/runtime/src/lib.rs @@ -45,14 +45,16 @@ use sp_version::NativeVersion; use sp_version::RuntimeVersion; // A few exports that help ease life for downstream crates. -use bp_runtime::{HeaderId, HeaderIdProvider}; +use bp_runtime::HeaderId; pub use frame_support::{ construct_runtime, dispatch::DispatchClass, match_types, parameter_types, - traits::{Everything, IsInVec, Nothing, Randomness}, + traits::{ConstU32, Everything, IsInVec, Nothing, Randomness}, weights::{ - constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, + constants::{ + BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, + }, IdentityFee, Weight, }, StorageValue, @@ -249,8 +251,6 @@ parameter_types! { pub const CreationFee: u128 = MILLIUNIT; pub const TransactionByteFee: u128 = MICROUNIT; pub const OperationalFeeMultiplier: u8 = 5; - pub const MaxLocks: u32 = 50; - pub const MaxReserves: u32 = 50; } impl pallet_balances::Config for Runtime { @@ -262,8 +262,8 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = pallet_balances::weights::SubstrateWeight; - type MaxLocks = MaxLocks; - type MaxReserves = MaxReserves; + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<50>; type ReserveIdentifier = [u8; 8]; } @@ -372,7 +372,9 @@ pub type XcmOriginToTransactDispatchOrigin = ( pub const BASE_XCM_WEIGHT: u64 = 1_000_000_000; parameter_types! { - pub UnitWeightCost: u64 = BASE_XCM_WEIGHT; + /// The amount of weight an XCM operation takes. This is a safe overestimate. + // TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - check `set_proof_size` 0 or 64*1024 or 1026? + pub UnitWeightCost: Weight = Weight::from_parts(BASE_XCM_WEIGHT, 0); // One UNIT buys 1 second of weight. pub const WeightPrice: (MultiLocation, u128) = (MultiLocation::parent(), UNIT); pub const MaxInstructions: u32 = 100; @@ -421,6 +423,7 @@ impl Config for XcmConfig { type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; } /// No local origins on this chain are allowed to dispatch XCM sends/executions. @@ -459,6 +462,11 @@ impl XcmBridge for ToMillauBridge { } } +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = todo!("We dont use benchmarks for pallet_xcm, so if you hit this message, you need to remove this and define value instead"); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SendXcmOrigin = EnsureXcmOrigin; @@ -479,6 +487,9 @@ impl pallet_xcm::Config for Runtime { type SovereignAccountOf = (); type MaxLockers = frame_support::traits::ConstU32<8>; type UniversalLocation = UniversalLocation; + type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } impl cumulus_pallet_xcm::Config for Runtime { @@ -513,17 +524,12 @@ impl pallet_aura::Config for Runtime { impl pallet_bridge_relayers::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reward = Balance; - type PaymentProcedure = bp_relayers::MintReward, AccountId>; + type PaymentProcedure = + bp_relayers::PayLaneRewardFromAccount, AccountId>; type WeightInfo = (); } parameter_types! { - /// This is a pretty unscientific cap. - /// - /// Note that once this is hit the pallet will essentially throttle incoming requests down to one - /// call per block. - pub const MaxRequests: u32 = 50; - /// Number of headers to keep. /// /// Assuming the worst case of every header being finalized, we will keep headers at least for a @@ -532,17 +538,18 @@ parameter_types! { /// Maximal number of authorities at Millau. pub const MaxAuthoritiesAtMillau: u32 = bp_millau::MAX_AUTHORITIES_COUNT; - /// Maximal size of SCALE-encoded Millau header. - pub const MaxMillauHeaderSize: u32 = bp_millau::MAX_HEADER_SIZE; } pub type MillauGrandpaInstance = (); impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_millau::Millau; - type MaxRequests = MaxRequests; + /// This is a pretty unscientific cap. + /// + /// Note that once this is hit the pallet will essentially throttle incoming requests down to + /// one call per block. + type MaxRequests = ConstU32<50>; type HeadersToKeep = HeadersToKeep; type MaxBridgedAuthorities = MaxAuthoritiesAtMillau; - type MaxBridgedHeaderSize = MaxMillauHeaderSize; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } @@ -572,14 +579,15 @@ impl pallet_bridge_messages::Config for Runtime { type InboundPayload = crate::millau_messages::FromMillauMessagePayload; type InboundRelayer = bp_millau::AccountId; + type DeliveryPayments = (); type TargetHeaderChain = crate::millau_messages::Millau; type LaneMessageVerifier = crate::millau_messages::ToMillauMessageVerifier; - type MessageDeliveryAndDispatchPayment = - pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter< - Runtime, - WithMillauMessagesInstance, - >; + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + Runtime, + frame_support::traits::ConstU128<100_000>, + frame_support::traits::ConstU128<100_000>, + >; type SourceHeaderChain = crate::millau_messages::Millau; type MessageDispatch = crate::millau_messages::FromMillauMessageDispatch; @@ -731,7 +739,7 @@ impl_runtime_apis! { impl bp_millau::MillauFinalityApi for Runtime { fn best_finalized() -> Option> { - BridgeMillauGrandpa::best_finalized().map(|header| header.id()) + BridgeMillauGrandpa::best_finalized() } } @@ -837,7 +845,7 @@ mod tests { use crate::millau_messages::WeightCredit; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - MessageKey, + LaneId, MessageKey, }; use bp_runtime::messages::MessageDispatchResult; use bridge_runtime_common::messages::target::FromBridgedChainMessageDispatch; @@ -880,7 +888,7 @@ mod tests { let xcm: Xcm = vec![Instruction::Trap(42)].into(); let mut incoming_message = DispatchMessage { - key: MessageKey { lane_id: [0, 0, 0, 0], nonce: 1 }, + key: MessageKey { lane_id: LaneId([0, 0, 0, 0]), nonce: 1 }, data: DispatchMessageData { payload: Ok((location, xcm).into()) }, }; @@ -896,7 +904,6 @@ mod tests { dispatch_result, MessageDispatchResult { unspent_weight: frame_support::weights::Weight::from_ref_time(0), - dispatch_fee_paid_during_dispatch: false, dispatch_level_result: (), } ); diff --git a/bin/rialto-parachain/runtime/src/millau_messages.rs b/bin/rialto-parachain/runtime/src/millau_messages.rs index 25d37f15988..e21cf76a82c 100644 --- a/bin/rialto-parachain/runtime/src/millau_messages.rs +++ b/bin/rialto-parachain/runtime/src/millau_messages.rs @@ -31,7 +31,7 @@ use bridge_runtime_common::messages::{self, MessageBridge}; use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; /// Default lane that is used to send messages to Millau. -pub const XCM_LANE: LaneId = [0, 0, 0, 0]; +pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); /// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge /// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual /// tests, confirming that we don't break encoding somewhere between. diff --git a/bin/rialto/node/Cargo.toml b/bin/rialto/node/Cargo.toml index 82b073896d9..aec1870efd8 100644 --- a/bin/rialto/node/Cargo.toml +++ b/bin/rialto/node/Cargo.toml @@ -18,12 +18,12 @@ rialto-runtime = { path = "../runtime" } # Substrate Dependencies -beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-beefy = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } node-inspect = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master", features = ["wasmtime"] } +sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master"} sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-service = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/bin/rialto/node/src/chain_spec.rs b/bin/rialto/node/src/chain_spec.rs index 0e9edb38ac0..5085c2dc71c 100644 --- a/bin/rialto/node/src/chain_spec.rs +++ b/bin/rialto/node/src/chain_spec.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use beefy_primitives::crypto::AuthorityId as BeefyId; use frame_support::weights::Weight; use polkadot_primitives::v2::{AssignmentId, ValidatorId}; use rialto_runtime::{ @@ -24,6 +23,7 @@ use rialto_runtime::{ }; use serde_json::json; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; +use sp_beefy::crypto::AuthorityId as BeefyId; use sp_consensus_babe::AuthorityId as BabeId; use sp_core::{sr25519, Pair, Public}; use sp_finality_grandpa::AuthorityId as GrandpaId; diff --git a/bin/rialto/runtime/Cargo.toml b/bin/rialto/runtime/Cargo.toml index 7220e5790a4..b4d387e3829 100644 --- a/bin/rialto/runtime/Cargo.toml +++ b/bin/rialto/runtime/Cargo.toml @@ -8,7 +8,6 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } -log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } # Bridge dependencies @@ -27,7 +26,7 @@ pallet-shift-session-manager = { path = "../../../modules/shift-session-manager" # Substrate Dependencies -beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } frame-executive = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -52,7 +51,6 @@ sp-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -80,7 +78,7 @@ substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", bran [features] default = ["std"] std = [ - "beefy-primitives/std", + "sp-beefy/std", "bp-messages/std", "bp-millau/std", "bp-relayers/std", @@ -93,7 +91,6 @@ std = [ "frame-support/std", "frame-system-rpc-runtime-api/std", "frame-system/std", - "log/std", "pallet-authority-discovery/std", "pallet-babe/std", "pallet-balances/std", @@ -106,7 +103,6 @@ std = [ "pallet-grandpa/std", "pallet-mmr/std", "pallet-xcm/std", - "sp-mmr-primitives/std", "pallet-shift-session-manager/std", "pallet-sudo/std", "pallet-timestamp/std", diff --git a/bin/rialto/runtime/src/lib.rs b/bin/rialto/runtime/src/lib.rs index 25778705e5e..123cd477d97 100644 --- a/bin/rialto/runtime/src/lib.rs +++ b/bin/rialto/runtime/src/lib.rs @@ -32,19 +32,15 @@ pub mod millau_messages; pub mod parachains; pub mod xcm_config; -use beefy_primitives::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; -use bp_runtime::{HeaderId, HeaderIdProvider}; +use bp_runtime::HeaderId; use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; -use pallet_mmr::primitives as mmr; use pallet_transaction_payment::{FeeDetails, Multiplier, RuntimeDispatchInfo}; use sp_api::impl_runtime_apis; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; +use sp_beefy::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; -use sp_mmr_primitives::{ - DataOrHash, EncodableOpaqueLeaf, Error as MmrError, LeafDataProvider, Proof as MmrProof, -}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{AccountIdLookup, Block as BlockT, Keccak256, NumberFor, OpaqueKeys}, @@ -61,8 +57,8 @@ pub use frame_support::{ construct_runtime, dispatch::DispatchClass, parameter_types, - traits::{Currency, ExistenceRequirement, Imbalance, KeyOwnerProofSystem}, - weights::{constants::WEIGHT_PER_SECOND, IdentityFee, RuntimeDbWeight, Weight}, + traits::{ConstU32, ConstU8, Currency, ExistenceRequirement, Imbalance, KeyOwnerProofSystem}, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, IdentityFee, RuntimeDbWeight, Weight}, StorageValue, }; @@ -222,13 +218,12 @@ pub const BABE_GENESIS_EPOCH_CONFIG: sp_consensus_babe::BabeEpochConfiguration = parameter_types! { pub const EpochDuration: u64 = bp_rialto::EPOCH_DURATION_IN_SLOTS as u64; pub const ExpectedBlockTime: bp_rialto::Moment = bp_rialto::time_units::MILLISECS_PER_BLOCK; - pub const MaxAuthorities: u32 = 10; } impl pallet_babe::Config for Runtime { type EpochDuration = EpochDuration; type ExpectedBlockTime = ExpectedBlockTime; - type MaxAuthorities = MaxAuthorities; + type MaxAuthorities = ConstU32<10>; // session module is the trigger type EpochChangeTrigger = pallet_babe::ExternalTrigger; @@ -251,13 +246,13 @@ impl pallet_babe::Config for Runtime { impl pallet_beefy::Config for Runtime { type BeefyId = BeefyId; - type MaxAuthorities = MaxAuthorities; + type MaxAuthorities = ConstU32<10>; type OnNewValidatorSet = MmrLeaf; } impl pallet_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type MaxAuthorities = MaxAuthorities; + type MaxAuthorities = ConstU32<10>; type KeyOwnerProofSystem = (); type KeyOwnerProof = >::Proof; @@ -270,9 +265,6 @@ impl pallet_grandpa::Config for Runtime { type WeightInfo = (); } -type MmrHash = ::Output; -type MmrHashing = ::Hashing; - impl pallet_mmr::Config for Runtime { const INDEXING_PREFIX: &'static [u8] = b"mmr"; type Hashing = Keccak256; @@ -301,7 +293,7 @@ parameter_types! { pub struct BeefyDummyDataProvider; -impl beefy_primitives::mmr::BeefyDataProvider<()> for BeefyDummyDataProvider { +impl sp_beefy::mmr::BeefyDataProvider<()> for BeefyDummyDataProvider { fn extra_data() {} } @@ -327,10 +319,6 @@ impl pallet_timestamp::Config for Runtime { parameter_types! { pub const ExistentialDeposit: bp_rialto::Balance = 500; - // For weight estimation, we assume that the most locks on an individual account will be 50. - // This number may need to be adjusted in the future if this assumption no longer holds true. - pub const MaxLocks: u32 = 50; - pub const MaxReserves: u32 = 50; } impl pallet_balances::Config for Runtime { @@ -343,15 +331,16 @@ impl pallet_balances::Config for Runtime { type AccountStore = System; // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) type WeightInfo = (); - type MaxLocks = MaxLocks; - type MaxReserves = MaxReserves; + // For weight estimation, we assume that the most locks on an individual account will be 50. + // This number may need to be adjusted in the future if this assumption no longer holds true. + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<50>; type ReserveIdentifier = [u8; 8]; } parameter_types! { pub const TransactionBaseFee: Balance = 0; pub const TransactionByteFee: Balance = 1; - pub const OperationalFeeMultiplier: u8 = 5; // values for following parameters are copied from polkadot repo, but it is fine // not to sync them - we're not going to make Rialto a full copy of one of Polkadot-like chains pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); @@ -362,7 +351,7 @@ parameter_types! { impl pallet_transaction_payment::Config for Runtime { type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; - type OperationalFeeMultiplier = OperationalFeeMultiplier; + type OperationalFeeMultiplier = ConstU8<5>; type WeightToFee = bp_rialto::WeightToFee; type LengthToFee = bp_rialto::WeightToFee; type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< @@ -394,23 +383,18 @@ impl pallet_session::Config for Runtime { } impl pallet_authority_discovery::Config for Runtime { - type MaxAuthorities = MaxAuthorities; + type MaxAuthorities = ConstU32<10>; } impl pallet_bridge_relayers::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reward = Balance; - type PaymentProcedure = bp_relayers::MintReward, AccountId>; + type PaymentProcedure = + bp_relayers::PayLaneRewardFromAccount, AccountId>; type WeightInfo = (); } parameter_types! { - /// This is a pretty unscientific cap. - /// - /// Note that once this is hit the pallet will essentially throttle incoming requests down to one - /// call per block. - pub const MaxRequests: u32 = 50; - /// Number of headers to keep. /// /// Assuming the worst case of every header being finalized, we will keep headers at least for a @@ -419,17 +403,18 @@ parameter_types! { /// Maximal number of authorities at Millau. pub const MaxAuthoritiesAtMillau: u32 = bp_millau::MAX_AUTHORITIES_COUNT; - /// Maximal size of SCALE-encoded Millau header. - pub const MaxMillauHeaderSize: u32 = bp_millau::MAX_HEADER_SIZE; } pub type MillauGrandpaInstance = (); impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_millau::Millau; - type MaxRequests = MaxRequests; + /// This is a pretty unscientific cap. + /// + /// Note that once this is hit the pallet will essentially throttle incoming requests down to + /// one call per block. + type MaxRequests = ConstU32<50>; type HeadersToKeep = HeadersToKeep; type MaxBridgedAuthorities = MaxAuthoritiesAtMillau; - type MaxBridgedHeaderSize = MaxMillauHeaderSize; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } @@ -461,14 +446,15 @@ impl pallet_bridge_messages::Config for Runtime { type InboundPayload = crate::millau_messages::FromMillauMessagePayload; type InboundRelayer = bp_millau::AccountId; + type DeliveryPayments = (); type TargetHeaderChain = crate::millau_messages::Millau; type LaneMessageVerifier = crate::millau_messages::ToMillauMessageVerifier; - type MessageDeliveryAndDispatchPayment = - pallet_bridge_relayers::MessageDeliveryAndDispatchPaymentAdapter< - Runtime, - WithMillauMessagesInstance, - >; + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + Runtime, + frame_support::traits::ConstU128<100_000>, + frame_support::traits::ConstU128<100_000>, + >; type SourceHeaderChain = crate::millau_messages::Millau; type MessageDispatch = crate::millau_messages::FromMillauMessageDispatch; @@ -581,6 +567,16 @@ pub type Executive = frame_executive::Executive< AllPalletsWithSystem, >; +/// MMR helper types. +mod mmr { + use super::Runtime; + pub use pallet_mmr::primitives::*; + + pub type Leaf = <::LeafData as LeafDataProvider>::LeafData; + pub type Hash = ::Hash; + pub type Hashing = ::Hashing; +} + impl_runtime_apis! { impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { @@ -629,66 +625,30 @@ impl_runtime_apis! { } } - impl beefy_primitives::BeefyApi for Runtime { + impl sp_beefy::BeefyApi for Runtime { fn validator_set() -> Option> { Beefy::validator_set() } } - impl sp_mmr_primitives::MmrApi for Runtime { - fn generate_proof(block_number: BlockNumber) - -> Result<(EncodableOpaqueLeaf, MmrProof), MmrError> - { - Mmr::generate_batch_proof(vec![block_number]) - .and_then(|(leaves, proof)| Ok(( - mmr::EncodableOpaqueLeaf::from_leaf(&leaves[0]), - mmr::BatchProof::into_single_leaf_proof(proof)? - ))) - } - - fn verify_proof(leaf: EncodableOpaqueLeaf, proof: MmrProof) - -> Result<(), MmrError> - { - pub type Leaf = < - ::LeafData as LeafDataProvider - >::LeafData; - - let leaf: Leaf = leaf - .into_opaque_leaf() - .try_decode() - .ok_or(MmrError::Verify)?; - Mmr::verify_leaves(vec![leaf], mmr::Proof::into_batch_proof(proof)) - } - - fn verify_proof_stateless( - root: Hash, - leaf: EncodableOpaqueLeaf, - proof: MmrProof - ) -> Result<(), MmrError> { - let node = DataOrHash::Data(leaf.into_opaque_leaf()); - pallet_mmr::verify_leaves_proof::( - root, - vec![node], - pallet_mmr::primitives::Proof::into_batch_proof(proof), - ) - } - - fn mmr_root() -> Result { + impl pallet_mmr::primitives::MmrApi< + Block, + mmr::Hash, + BlockNumber, + > for Runtime { + fn mmr_root() -> Result { Ok(Mmr::mmr_root()) } - fn generate_batch_proof(block_numbers: Vec) - -> Result<(Vec, mmr::BatchProof), mmr::Error> - { - Mmr::generate_batch_proof(block_numbers) - .map(|(leaves, proof)| (leaves.into_iter().map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf)).collect(), proof)) + fn mmr_leaf_count() -> Result { + Ok(Mmr::mmr_leaves()) } - fn generate_historical_batch_proof( + fn generate_proof( block_numbers: Vec, - best_known_block_number: BlockNumber - ) -> Result<(Vec, mmr::BatchProof), mmr::Error> { - Mmr::generate_historical_batch_proof(block_numbers, best_known_block_number).map( + best_known_block_number: Option, + ) -> Result<(Vec, mmr::Proof), mmr::Error> { + Mmr::generate_proof(block_numbers, best_known_block_number).map( |(leaves, proof)| { ( leaves @@ -701,32 +661,29 @@ impl_runtime_apis! { ) } - fn verify_batch_proof(leaves: Vec, proof: mmr::BatchProof) + fn verify_proof(leaves: Vec, proof: mmr::Proof) -> Result<(), mmr::Error> { - type Leaf = < - ::LeafData as LeafDataProvider - >::LeafData; let leaves = leaves.into_iter().map(|leaf| leaf.into_opaque_leaf() .try_decode() - .ok_or(mmr::Error::Verify)).collect::, mmr::Error>>()?; + .ok_or(mmr::Error::Verify)).collect::, mmr::Error>>()?; Mmr::verify_leaves(leaves, proof) } - fn verify_batch_proof_stateless( - root: MmrHash, + fn verify_proof_stateless( + root: mmr::Hash, leaves: Vec, - proof: mmr::BatchProof + proof: mmr::Proof ) -> Result<(), mmr::Error> { let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect(); - pallet_mmr::verify_leaves_proof::(root, nodes, proof) + pallet_mmr::verify_leaves_proof::(root, nodes, proof) } } impl bp_millau::MillauFinalityApi for Runtime { fn best_finalized() -> Option> { - BridgeMillauGrandpa::best_finalized().map(|header| header.id()) + BridgeMillauGrandpa::best_finalized() } } diff --git a/bin/rialto/runtime/src/millau_messages.rs b/bin/rialto/runtime/src/millau_messages.rs index 66480c3168d..90a63a733c3 100644 --- a/bin/rialto/runtime/src/millau_messages.rs +++ b/bin/rialto/runtime/src/millau_messages.rs @@ -28,7 +28,7 @@ use bridge_runtime_common::messages::{self, MessageBridge}; use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; /// Lane that is used for XCM messages exchange. -pub const XCM_LANE: LaneId = [0, 0, 0, 0]; +pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); /// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge /// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual /// tests, confirming that we don't break encoding somewhere between. @@ -162,41 +162,19 @@ impl SourceHeaderChain for Millau { mod tests { use super::*; use crate::{MillauGrandpaInstance, Runtime, WithMillauMessagesInstance}; - use bp_runtime::Chain; use bridge_runtime_common::{ assert_complete_bridge_types, integrity::{ - assert_complete_bridge_constants, AssertBridgeMessagesPalletConstants, - AssertBridgePalletNames, AssertChainConstants, AssertCompleteBridgeConstants, + assert_complete_bridge_constants, check_message_lane_weights, + AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants, + AssertCompleteBridgeConstants, }, }; #[test] fn ensure_rialto_message_lane_weights_are_correct() { - type Weights = pallet_bridge_messages::weights::BridgeWeight; - - pallet_bridge_messages::ensure_weights_are_correct::(); - - let max_incoming_message_proof_size = bp_millau::EXTRA_STORAGE_PROOF_SIZE.saturating_add( - messages::target::maximal_incoming_message_size(bp_rialto::Rialto::max_extrinsic_size()), - ); - pallet_bridge_messages::ensure_able_to_receive_message::( - bp_rialto::Rialto::max_extrinsic_size(), - bp_rialto::Rialto::max_extrinsic_weight(), - max_incoming_message_proof_size, - messages::target::maximal_incoming_message_dispatch_weight( - bp_rialto::Rialto::max_extrinsic_weight(), - ), - ); - - let max_incoming_inbound_lane_data_proof_size = - bp_messages::InboundLaneData::<()>::encoded_size_hint_u32( - bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX as _, - ); - pallet_bridge_messages::ensure_able_to_receive_confirmation::( - bp_rialto::Rialto::max_extrinsic_size(), - bp_rialto::Rialto::max_extrinsic_weight(), - max_incoming_inbound_lane_data_proof_size, + check_message_lane_weights::( + bp_millau::EXTRA_STORAGE_PROOF_SIZE, bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, ); diff --git a/bin/rialto/runtime/src/parachains.rs b/bin/rialto/runtime/src/parachains.rs index 960931a0b3c..1c7280198a4 100644 --- a/bin/rialto/runtime/src/parachains.rs +++ b/bin/rialto/runtime/src/parachains.rs @@ -123,14 +123,10 @@ impl parachains_session_info::Config for Runtime { impl parachains_shared::Config for Runtime {} -parameter_types! { - pub const FirstMessageFactorPercent: u64 = 100; -} - impl parachains_ump::Config for Runtime { type RuntimeEvent = RuntimeEvent; type UmpSink = (); - type FirstMessageFactorPercent = FirstMessageFactorPercent; + type FirstMessageFactorPercent = frame_support::traits::ConstU64<100>; type ExecuteOverweightOrigin = EnsureRoot; type WeightInfo = parachains_ump::TestWeightInfo; } diff --git a/bin/rialto/runtime/src/xcm_config.rs b/bin/rialto/runtime/src/xcm_config.rs index 892c1ed747b..a86593f47f9 100644 --- a/bin/rialto/runtime/src/xcm_config.rs +++ b/bin/rialto/runtime/src/xcm_config.rs @@ -27,7 +27,8 @@ use bridge_runtime_common::{ }; use frame_support::{ parameter_types, - traits::{Everything, Nothing}, + traits::{ConstU32, Everything, Nothing}, + weights::Weight, }; use xcm::latest::prelude::*; use xcm_builder::{ @@ -91,7 +92,8 @@ pub const BASE_XCM_WEIGHT: u64 = 1_000_000_000; parameter_types! { /// The amount of weight an XCM operation takes. This is a safe overestimate. - pub const BaseXcmWeight: u64 = BASE_XCM_WEIGHT; + // TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - check `set_proof_size` 0 or 64*1024 or 1026? + pub const BaseXcmWeight: Weight = Weight::from_parts(BASE_XCM_WEIGHT, 0); /// Maximum number of instructions in a single XCM fragment. A sanity check against weight /// calculations getting too crazy. pub const MaxInstructions: u32 = 100; @@ -104,10 +106,6 @@ pub type XcmRouter = ( XcmBridgeAdapter, ); -parameter_types! { - pub const MaxAssetsIntoHolding: u32 = 64; -} - /// The barriers one of which must be passed for an XCM message to be executed. pub type Barrier = ( // Weight that is paid for may be consumed. @@ -141,11 +139,12 @@ impl xcm_executor::Config for XcmConfig { type AssetClaims = XcmPallet; type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type MaxAssetsIntoHolding = ConstU32<64>; type FeeManager = (); type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; } /// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior @@ -155,6 +154,11 @@ pub type LocalOriginToLocation = ( SignedToAccountId32, ); +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = todo!("We dont use benchmarks for pallet_xcm, so if you hit this message, you need to remove this and define value instead"); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; // We don't allow any messages to be sent via the transaction yet. This is basically safe to @@ -184,6 +188,9 @@ impl pallet_xcm::Config for Runtime { type TrustedLockers = (); type SovereignAccountOf = SovereignAccountOf; type MaxLockers = frame_support::traits::ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } /// With-Millau bridge. @@ -208,7 +215,7 @@ impl XcmBridge for ToMillauBridge { } fn xcm_lane() -> bp_messages::LaneId { - [0, 0, 0, 0] + bp_messages::LaneId([0, 0, 0, 0]) } } @@ -218,7 +225,7 @@ mod tests { use crate::millau_messages::WeightCredit; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - MessageKey, + LaneId, MessageKey, }; use bp_runtime::messages::MessageDispatchResult; use bridge_runtime_common::messages::target::FromBridgedChainMessageDispatch; @@ -261,7 +268,7 @@ mod tests { let xcm: Xcm = vec![Instruction::Trap(42)].into(); let mut incoming_message = DispatchMessage { - key: MessageKey { lane_id: [0, 0, 0, 0], nonce: 1 }, + key: MessageKey { lane_id: LaneId([0, 0, 0, 0]), nonce: 1 }, data: DispatchMessageData { payload: Ok((location, xcm).into()) }, }; @@ -277,7 +284,6 @@ mod tests { dispatch_result, MessageDispatchResult { unspent_weight: frame_support::weights::Weight::from_ref_time(0), - dispatch_fee_paid_during_dispatch: false, dispatch_level_result: (), } ); diff --git a/bin/runtime-common/Cargo.toml b/bin/runtime-common/Cargo.toml index 4f8f7e34c40..9153d7a05dd 100644 --- a/bin/runtime-common/Cargo.toml +++ b/bin/runtime-common/Cargo.toml @@ -23,12 +23,15 @@ bp-runtime = { path = "../../primitives/runtime", default-features = false } pallet-bridge-grandpa = { path = "../../modules/grandpa", default-features = false } pallet-bridge-messages = { path = "../../modules/messages", default-features = false } pallet-bridge-parachains = { path = "../../modules/parachains", default-features = false } +pallet-bridge-relayers = { path = "../../modules/relayers", default-features = false } # Substrate dependencies frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-utility = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -43,6 +46,8 @@ xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } [dev-dependencies] +bp-rialto = { path = "../../primitives/chain-rialto" } +bp-test-utils = { path = "../../primitives/test-utils" } millau-runtime = { path = "../millau/runtime" } [features] @@ -61,6 +66,9 @@ std = [ "pallet-bridge-grandpa/std", "pallet-bridge-messages/std", "pallet-bridge-parachains/std", + "pallet-bridge-relayers/std", + "pallet-transaction-payment/std", + "pallet-utility/std", "pallet-xcm/std", "scale-info/std", "sp-api/std", diff --git a/bin/runtime-common/src/integrity.rs b/bin/runtime-common/src/integrity.rs index 4c69a29b821..9c4553ad136 100644 --- a/bin/runtime-common/src/integrity.rs +++ b/bin/runtime-common/src/integrity.rs @@ -19,9 +19,9 @@ //! Most of the tests in this module assume that the bridge is using standard (see `crate::messages` //! module for details) configuration. -use crate::messages::MessageBridge; +use crate::{messages, messages::MessageBridge}; -use bp_messages::MessageNonce; +use bp_messages::{InboundLaneData, MessageNonce}; use bp_runtime::{Chain, ChainId}; use codec::Encode; use frame_support::{storage::generator::StorageValue, traits::Get}; @@ -289,3 +289,33 @@ where assert_bridge_messages_pallet_constants::(params.messages_pallet_constants); assert_bridge_pallet_names::(params.pallet_names); } + +/// Check that the message lane weights are correct. +pub fn check_message_lane_weights( + bridged_chain_extra_storage_proof_size: u32, + this_chain_max_unrewarded_relayers: MessageNonce, + this_chain_max_unconfirmed_messages: MessageNonce, +) { + type Weights = pallet_bridge_messages::weights::BridgeWeight; + + pallet_bridge_messages::ensure_weights_are_correct::>(); + + let max_incoming_message_proof_size = bridged_chain_extra_storage_proof_size + .saturating_add(messages::target::maximal_incoming_message_size(C::max_extrinsic_size())); + pallet_bridge_messages::ensure_able_to_receive_message::>( + C::max_extrinsic_size(), + C::max_extrinsic_weight(), + max_incoming_message_proof_size, + messages::target::maximal_incoming_message_dispatch_weight(C::max_extrinsic_weight()), + ); + + let max_incoming_inbound_lane_data_proof_size = + InboundLaneData::<()>::encoded_size_hint_u32(this_chain_max_unrewarded_relayers as _); + pallet_bridge_messages::ensure_able_to_receive_confirmation::>( + C::max_extrinsic_size(), + C::max_extrinsic_weight(), + max_incoming_inbound_lane_data_proof_size, + this_chain_max_unrewarded_relayers, + this_chain_max_unconfirmed_messages, + ); +} diff --git a/bin/runtime-common/src/lib.rs b/bin/runtime-common/src/lib.rs index ca8f2268404..f8d2e7a039e 100644 --- a/bin/runtime-common/src/lib.rs +++ b/bin/runtime-common/src/lib.rs @@ -27,6 +27,7 @@ pub mod messages_api; pub mod messages_benchmarking; pub mod messages_extension; pub mod parachains_benchmarking; +pub mod refund_relayer_extension; mod messages_generation; @@ -78,7 +79,7 @@ where #[macro_export] macro_rules! generate_bridge_reject_obsolete_headers_and_messages { ($call:ty, $account_id:ty, $($filter_call:ty),*) => { - #[derive(Clone, codec::Decode, codec::Encode, Eq, PartialEq, frame_support::RuntimeDebug, scale_info::TypeInfo)] + #[derive(Clone, codec::Decode, Default, codec::Encode, Eq, PartialEq, frame_support::RuntimeDebug, scale_info::TypeInfo)] pub struct BridgeRejectObsoleteHeadersAndMessages; impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteHeadersAndMessages { const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages"; diff --git a/bin/runtime-common/src/messages.rs b/bin/runtime-common/src/messages.rs index 23c79b1c583..3974966daad 100644 --- a/bin/runtime-common/src/messages.rs +++ b/bin/runtime-common/src/messages.rs @@ -27,6 +27,7 @@ use bp_messages::{ InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, }; use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Size, StorageProofChecker}; +pub use bp_runtime::{UnderlyingChainOf, UnderlyingChainProvider}; use codec::{Decode, DecodeLimit, Encode}; use frame_support::{traits::Get, weights::Weight, RuntimeDebug}; use hash_db::Hasher; @@ -54,12 +55,6 @@ pub trait MessageBridge { type BridgedHeaderChain: HeaderChain>; } -/// A trait that provides the type of the underlying chain. -pub trait UnderlyingChainProvider { - /// Underlying chain type. - type Chain: Chain; -} - /// This chain that has `pallet-bridge-messages` module. pub trait ThisChainWithMessages: UnderlyingChainProvider { /// Call origin on the chain. @@ -87,8 +82,6 @@ pub trait BridgedChainWithMessages: UnderlyingChainProvider { pub type ThisChain = ::ThisChain; /// Bridged chain in context of message bridge. pub type BridgedChain = ::BridgedChain; -/// Underlying chain type. -pub type UnderlyingChainOf = ::Chain; /// Hash used on the chain. pub type HashOf = bp_runtime::HashOf<::Chain>; /// Hasher used on the chain. @@ -459,7 +452,7 @@ pub mod target { // I have no idea why this method takes `&mut` reference and there's nothing // about that in documentation. Hope it'll only mutate iff error is returned. let weight = XcmWeigher::weight(&mut payload.xcm.1); - let weight = Weight::from_ref_time(weight.unwrap_or_else(|e| { + let weight = weight.unwrap_or_else(|e| { log::debug!( target: "runtime::bridge-dispatch", "Failed to compute dispatch weight of incoming XCM message {:?}/{}: {:?}", @@ -470,8 +463,8 @@ pub mod target { // we shall return 0 and then the XCM executor will fail to execute XCM // if we'll return something else (e.g. maximal value), the lane may stuck - 0 - })); + Weight::zero() + }); payload.weight = Some(weight); weight @@ -505,8 +498,8 @@ pub mod target { location, xcm, hash, - weight_limit.unwrap_or_else(Weight::zero).ref_time(), - weight_credit.ref_time(), + weight_limit.unwrap_or_else(Weight::zero), + weight_credit, ); Ok(xcm_outcome) }; @@ -542,11 +535,7 @@ pub mod target { }, } - MessageDispatchResult { - unspent_weight: Weight::zero(), - dispatch_fee_paid_during_dispatch: false, - dispatch_level_result: (), - } + MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } } } @@ -703,13 +692,19 @@ pub mod target { } } +/// The `BridgeMessagesCall` used by a chain. +pub type BridgeMessagesCallOf = bp_messages::BridgeMessagesCall< + bp_runtime::AccountIdOf, + target::FromBridgedChainMessagesProof>, + source::FromBridgedChainMessagesDeliveryProof>, +>; + #[cfg(test)] mod tests { use super::*; use crate::messages_generation::{ encode_all_messages, encode_lane_data, prepare_messages_storage_proof, }; - use bp_runtime::HeaderOf; use codec::{Decode, Encode}; use frame_support::weights::Weight; use sp_core::H256; @@ -885,17 +880,17 @@ mod tests { struct BridgedHeaderChain; impl HeaderChain for BridgedHeaderChain { - fn finalized_header( + fn finalized_header_state_root( _hash: HashOf, - ) -> Option> { - TEST_BRIDGED_HEADER.with(|h| h.borrow().clone()) + ) -> Option> { + TEST_BRIDGED_HEADER.with(|h| h.borrow().clone()).map(|h| *h.state_root()) } } struct ThisHeaderChain; impl HeaderChain for ThisHeaderChain { - fn finalized_header(_hash: HashOf) -> Option> { + fn finalized_header_state_root(_hash: HashOf) -> Option> { unreachable!() } } @@ -904,7 +899,7 @@ mod tests { OutboundLaneData::default() } - const TEST_LANE_ID: &LaneId = b"test"; + const TEST_LANE_ID: &LaneId = &LaneId(*b"test"); const MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE: MessageNonce = 32; fn regular_outbound_message_payload() -> source::FromThisChainMessagePayload { @@ -916,7 +911,7 @@ mod tests { assert_eq!( source::FromThisChainMessageVerifier::::verify_message( &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), - b"dsbl", + &LaneId(*b"dsbl"), &test_lane_outbound_data(), ®ular_outbound_message_payload(), ), diff --git a/bin/runtime-common/src/messages_extension.rs b/bin/runtime-common/src/messages_extension.rs index 28606d58dad..0f680f84e49 100644 --- a/bin/runtime-common/src/messages_extension.rs +++ b/bin/runtime-common/src/messages_extension.rs @@ -110,7 +110,7 @@ mod tests { fn deliver_message_10() { pallet_bridge_messages::InboundLanes::::insert( - [0, 0, 0, 0], + bp_messages::LaneId([0, 0, 0, 0]), bp_messages::InboundLaneData { relayers: Default::default(), last_confirmed_nonce: 10 }, ); } @@ -128,7 +128,7 @@ mod tests { proof: FromBridgedChainMessagesProof { bridged_header_hash: Default::default(), storage_proof: vec![], - lane: [0, 0, 0, 0], + lane: bp_messages::LaneId([0, 0, 0, 0]), nonces_start, nonces_end, }, @@ -170,7 +170,7 @@ mod tests { fn confirm_message_10() { pallet_bridge_messages::OutboundLanes::::insert( - [0, 0, 0, 0], + bp_messages::LaneId([0, 0, 0, 0]), bp_messages::OutboundLaneData { oldest_unpruned_nonce: 0, latest_received_nonce: 10, @@ -188,7 +188,7 @@ mod tests { proof: FromBridgedChainMessagesDeliveryProof { bridged_header_hash: Default::default(), storage_proof: Vec::new(), - lane: [0, 0, 0, 0], + lane: bp_messages::LaneId([0, 0, 0, 0]), }, relayers_state: UnrewardedRelayersState { last_delivered_nonce, diff --git a/bin/runtime-common/src/refund_relayer_extension.rs b/bin/runtime-common/src/refund_relayer_extension.rs new file mode 100644 index 00000000000..0038af975db --- /dev/null +++ b/bin/runtime-common/src/refund_relayer_extension.rs @@ -0,0 +1,926 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Signed extension that refunds relayer if he has delivered some new messages. +//! It also refunds transaction cost if the transaction is an `utility.batchAll()` +//! with calls that are: delivering new messsage and all necessary underlying headers +//! (parachain or relay chain). + +// hack because we have circular (test-level) dependency between `millau-runtime` +// and `bridge-runtime-common` crates +#[cfg(not(test))] +use crate::messages::target::FromBridgedChainMessagesProof; +#[cfg(test)] +use millau_runtime::bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; + +use bp_messages::{target_chain::SourceHeaderChain, LaneId, MessageNonce}; +use bp_polkadot_core::parachains::ParaId; +use bp_runtime::{Chain, HashOf}; +use codec::{Decode, Encode}; +use frame_support::{ + dispatch::{CallableCallFor, DispatchInfo, Dispatchable, PostDispatchInfo}, + traits::IsSubType, + CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, +}; +use pallet_bridge_grandpa::{ + BridgedChain, Call as GrandpaCall, Config as GrandpaConfig, Pallet as GrandpaPallet, +}; +use pallet_bridge_messages::{ + Call as MessagesCall, Config as MessagesConfig, Pallet as MessagesPallet, +}; +use pallet_bridge_parachains::{ + Call as ParachainsCall, Config as ParachainsConfig, Pallet as ParachainsPallet, RelayBlockHash, + RelayBlockHasher, RelayBlockNumber, +}; +use pallet_bridge_relayers::{Config as RelayersConfig, Pallet as RelayersPallet}; +use pallet_transaction_payment::{Config as TransactionPaymentConfig, OnChargeTransaction}; +use pallet_utility::{Call as UtilityCall, Config as UtilityConfig, Pallet as UtilityPallet}; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{DispatchInfoOf, Get, Header as HeaderT, PostDispatchInfoOf, SignedExtension, Zero}, + transaction_validity::{TransactionValidity, TransactionValidityError, ValidTransaction}, + DispatchResult, FixedPointOperand, +}; +use sp_std::marker::PhantomData; + +// TODO (https://github.com/paritytech/parity-bridges-common/issues/1667): +// support multiple bridges in this extension + +/// Transaction fee calculation. +pub trait TransactionFeeCalculation { + /// Compute fee that is paid for given transaction. The fee is later refunded to relayer. + fn compute_fee( + info: &DispatchInfo, + post_info: &PostDispatchInfo, + len: usize, + tip: Balance, + ) -> Balance; +} + +impl TransactionFeeCalculation> for R +where + R: TransactionPaymentConfig, + ::RuntimeCall: + Dispatchable, + BalanceOf: FixedPointOperand, +{ + fn compute_fee( + info: &DispatchInfo, + post_info: &PostDispatchInfo, + len: usize, + tip: BalanceOf, + ) -> BalanceOf { + pallet_transaction_payment::Pallet::::compute_actual_fee(len as _, info, post_info, tip) + } +} +/// Signed extension that refunds relayer for new messages coming from the parachain. +/// +/// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) +/// with message delivery transaction. Batch may deliver either both relay chain header and +/// parachain head, or just parachain head. Corresponding headers must be used in messages +/// proof verification. +/// +/// Extension does not refund transaction tip due to security reasons. +#[derive( + CloneNoBound, + Decode, + DefaultNoBound, + Encode, + EqNoBound, + PartialEqNoBound, + RuntimeDebugNoBound, + TypeInfo, +)] +#[scale_info(skip_type_params(RT, GI, PI, MI, BE, PID, LID, FEE))] +#[allow(clippy::type_complexity)] // TODO: get rid of that in https://github.com/paritytech/parity-bridges-common/issues/1666 +pub struct RefundRelayerForMessagesFromParachain( + PhantomData<(RT, GI, PI, MI, BE, PID, LID, FEE)>, +); + +/// Data that is crafted in `pre_dispatch` method and used at `post_dispatch`. +#[derive(PartialEq)] +#[cfg_attr(test, derive(Debug))] +pub struct PreDispatchData { + /// Transaction submitter (relayer) account. + pub relayer: AccountId, + /// Type of the call. + pub call_type: CallType, +} + +/// Type of the call that the extension recognizes. +#[derive(Clone, Copy, PartialEq, RuntimeDebugNoBound)] +pub enum CallType { + /// Relay chain finality + parachain finality + message delivery calls. + AllFinalityAndDelivery(ExpectedRelayChainState, ExpectedParachainState, MessagesState), + /// Parachain finality + message delivery calls. + ParachainFinalityAndDelivery(ExpectedParachainState, MessagesState), + /// Standalone message delivery call. + Delivery(MessagesState), +} + +impl CallType { + /// Returns the pre-dispatch messages pallet state. + fn pre_dispatch_messages_state(&self) -> MessagesState { + match *self { + Self::AllFinalityAndDelivery(_, _, messages_state) => messages_state, + Self::ParachainFinalityAndDelivery(_, messages_state) => messages_state, + Self::Delivery(messages_state) => messages_state, + } + } +} + +/// Expected post-dispatch state of the relay chain pallet. +#[derive(Clone, Copy, PartialEq, RuntimeDebugNoBound)] +pub struct ExpectedRelayChainState { + /// Best known relay chain block number. + pub best_block_number: RelayBlockNumber, +} + +/// Expected post-dispatch state of the parachain pallet. +#[derive(Clone, Copy, PartialEq, RuntimeDebugNoBound)] +pub struct ExpectedParachainState { + /// At which relay block the parachain head has been updated? + pub at_relay_block_number: RelayBlockNumber, +} + +/// Pre-dispatch state of messages pallet. +/// +/// This struct is for pre-dispatch state of the pallet, not the expected post-dispatch state. +/// That's because message delivery transaction may deliver some of messages that it brings. +/// If this happens, we consider it "helpful" and refund its cost. If transaction fails to +/// deliver at least one message, it is considered wrong and is not refunded. +#[derive(Clone, Copy, PartialEq, RuntimeDebugNoBound)] +pub struct MessagesState { + /// Best delivered message nonce. + pub best_nonce: MessageNonce, +} + +// without this typedef rustfmt fails with internal err +type BalanceOf = + <::OnChargeTransaction as OnChargeTransaction>::Balance; +type CallOf = ::RuntimeCall; + +impl SignedExtension + for RefundRelayerForMessagesFromParachain +where + R: 'static + + Send + + Sync + + frame_system::Config + + UtilityConfig> + + GrandpaConfig + + ParachainsConfig + + MessagesConfig + + RelayersConfig, + GI: 'static + Send + Sync, + PI: 'static + Send + Sync, + MI: 'static + Send + Sync, + BE: 'static + + Send + + Sync + + Default + + SignedExtension>, + PID: 'static + Send + Sync + Get, + LID: 'static + Send + Sync + Get, + FEE: 'static + Send + Sync + TransactionFeeCalculation<::Reward>, + ::RuntimeCall: + Dispatchable, + CallOf: IsSubType, R>> + + IsSubType, R>> + + IsSubType, R>> + + IsSubType, R>>, + >::BridgedChain: + Chain, + >::SourceHeaderChain: SourceHeaderChain< + MessagesProof = FromBridgedChainMessagesProof>>, + >, +{ + const IDENTIFIER: &'static str = "RefundRelayerForMessagesFromParachain"; + type AccountId = R::AccountId; + type Call = CallOf; + type AdditionalSigned = (); + type Pre = Option>; + + fn additional_signed(&self) -> Result<(), TransactionValidityError> { + Ok(()) + } + + fn validate( + &self, + who: &Self::AccountId, + call: &Self::Call, + info: &DispatchInfoOf, + len: usize, + ) -> TransactionValidity { + // reject batch transactions with obsolete headers + if let Some(UtilityCall::::batch_all { ref calls }) = call.is_sub_type() { + for nested_call in calls { + let reject_obsolete_transactions = BE::default(); + reject_obsolete_transactions.pre_dispatch(who, nested_call, info, len)?; + } + } + + Ok(ValidTransaction::default()) + } + + fn pre_dispatch( + self, + who: &Self::AccountId, + call: &Self::Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result { + // reject batch transactions with obsolete headers + self.validate(who, call, info, len).map(drop)?; + + // now try to check if tx matches one of types we support + let parse_call_type = || { + if let Some(UtilityCall::::batch_all { ref calls }) = call.is_sub_type() { + if calls.len() == 3 { + return Some(CallType::AllFinalityAndDelivery( + extract_expected_relay_chain_state::(&calls[0])?, + extract_expected_parachain_state::(&calls[1])?, + extract_messages_state::(&calls[2])?, + )) + } + if calls.len() == 2 { + return Some(CallType::ParachainFinalityAndDelivery( + extract_expected_parachain_state::(&calls[0])?, + extract_messages_state::(&calls[1])?, + )) + } + return None + } + + Some(CallType::Delivery(extract_messages_state::(call)?)) + }; + + Ok(parse_call_type() + .map(|call_type| { + log::trace!( + target: "runtime::bridge", + "RefundRelayerForMessagesFromParachain from parachain {} via {:?} parsed bridge transaction in pre-dispatch: {:?}", + PID::get(), + LID::get(), + call_type, + ); + PreDispatchData { relayer: who.clone(), call_type } + }) + ) + } + + fn post_dispatch( + pre: Option, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + // we never refund anything if it is not bridge transaction or if it is a bridge + // transaction that we do not support here + let (relayer, call_type) = match pre { + Some(Some(pre)) => (pre.relayer, pre.call_type), + _ => return Ok(()), + }; + + // we never refund anything if transaction has failed + if result.is_err() { + return Ok(()) + } + + // check if relay chain state has been updated + if let CallType::AllFinalityAndDelivery(expected_relay_chain_state, _, _) = call_type { + let actual_relay_chain_state = relay_chain_state::(); + if actual_relay_chain_state != Some(expected_relay_chain_state) { + // we only refund relayer if all calls have updated chain state + return Ok(()) + } + + // there's a conflict between how bridge GRANDPA pallet works and the + // `AllFinalityAndDelivery` transaction. If relay chain header is mandatory, the GRANDPA + // pallet returns `Pays::No`, because such transaction is mandatory for operating the + // bridge. But `utility.batchAll` transaction always requires payment. But in both cases + // we'll refund relayer - either explicitly here, or using `Pays::No` if he's choosing + // to submit dedicated transaction. + } + + // check if parachain state has been updated + match call_type { + CallType::AllFinalityAndDelivery(_, expected_parachain_state, _) | + CallType::ParachainFinalityAndDelivery(expected_parachain_state, _) => { + let actual_parachain_state = parachain_state::(); + if actual_parachain_state != Some(expected_parachain_state) { + // we only refund relayer if all calls have updated chain state + return Ok(()) + } + }, + _ => (), + } + + // check if messages have been delivered + let actual_messages_state = messages_state::(); + let pre_dispatch_messages_state = call_type.pre_dispatch_messages_state(); + if actual_messages_state == Some(pre_dispatch_messages_state) { + // we only refund relayer if all calls have updated chain state + return Ok(()) + } + + // regarding the tip - refund that happens here (at this side of the bridge) isn't the whole + // relayer compensation. He'll receive some amount at the other side of the bridge. It shall + // (in theory) cover the tip here. Otherwise, if we'll be compensating tip here, some + // malicious relayer may use huge tips, effectively depleting account that pay rewards. The + // cost of this attack is nothing. Hence we use zero as tip here. + let tip = Zero::zero(); + + // compute the relayer reward + let reward = FEE::compute_fee(info, post_info, len, tip); + + // finally - register reward in relayers pallet + RelayersPallet::::register_relayer_reward(LID::get(), &relayer, reward); + + log::trace!( + target: "runtime::bridge", + "RefundRelayerForMessagesFromParachain from parachain {} via {:?} has registered {:?} reward: {:?}", + PID::get(), + LID::get(), + relayer, + reward, + ); + + Ok(()) + } +} + +/// Extracts expected relay chain state from the call. +fn extract_expected_relay_chain_state(call: &CallOf) -> Option +where + R: GrandpaConfig, + GI: 'static, + >::BridgedChain: Chain, + CallOf: IsSubType, R>>, +{ + if let Some(GrandpaCall::::submit_finality_proof { ref finality_target, .. }) = + call.is_sub_type() + { + return Some(ExpectedRelayChainState { best_block_number: *finality_target.number() }) + } + None +} + +/// Extracts expected parachain state from the call. +fn extract_expected_parachain_state( + call: &CallOf, +) -> Option +where + R: GrandpaConfig + ParachainsConfig, + GI: 'static, + PI: 'static, + PID: Get, + >::BridgedChain: + Chain, + CallOf: IsSubType, R>>, +{ + if let Some(ParachainsCall::::submit_parachain_heads { + ref at_relay_block, + ref parachains, + .. + }) = call.is_sub_type() + { + if parachains.len() != 1 || parachains[0].0 != ParaId(PID::get()) { + return None + } + + return Some(ExpectedParachainState { at_relay_block_number: at_relay_block.0 }) + } + None +} + +/// Extracts messages state from the call. +fn extract_messages_state(call: &CallOf) -> Option +where + R: GrandpaConfig + MessagesConfig, + GI: 'static, + MI: 'static, + LID: Get, + CallOf: IsSubType, R>>, + >::SourceHeaderChain: SourceHeaderChain< + MessagesProof = FromBridgedChainMessagesProof>>, + >, +{ + if let Some(MessagesCall::::receive_messages_proof { ref proof, .. }) = + call.is_sub_type() + { + if LID::get() != proof.lane { + return None + } + + return Some(MessagesState { + best_nonce: MessagesPallet::::inbound_lane_data(proof.lane) + .last_delivered_nonce(), + }) + } + None +} + +/// Returns relay chain state that we are interested in. +fn relay_chain_state() -> Option +where + R: GrandpaConfig, + GI: 'static, + >::BridgedChain: Chain, +{ + GrandpaPallet::::best_finalized_number() + .map(|best_block_number| ExpectedRelayChainState { best_block_number }) +} + +/// Returns parachain state that we are interested in. +fn parachain_state() -> Option +where + R: ParachainsConfig, + PI: 'static, + PID: Get, +{ + ParachainsPallet::::best_parachain_info(ParaId(PID::get())).map(|para_info| { + ExpectedParachainState { + at_relay_block_number: para_info.best_head_hash.at_relay_block_number, + } + }) +} + +/// Returns messages state that we are interested in. +fn messages_state() -> Option +where + R: MessagesConfig, + MI: 'static, + LID: Get, +{ + Some(MessagesState { + best_nonce: MessagesPallet::::inbound_lane_data(LID::get()).last_delivered_nonce(), + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_messages::InboundLaneData; + use bp_parachains::{BestParaHeadHash, ParaInfo}; + use bp_polkadot_core::parachains::ParaHeadsProof; + use bp_runtime::HeaderId; + use bp_test_utils::make_default_justification; + use frame_support::{assert_storage_noop, parameter_types, weights::Weight}; + use millau_runtime::{ + RialtoGrandpaInstance, Runtime, RuntimeCall, WithRialtoParachainMessagesInstance, + WithRialtoParachainsInstance, + }; + use sp_runtime::{transaction_validity::InvalidTransaction, DispatchError}; + + parameter_types! { + pub TestParachain: u32 = 1000; + pub TestLaneId: LaneId = LaneId([0, 0, 0, 0]); + } + + type TestExtension = RefundRelayerForMessagesFromParachain< + millau_runtime::Runtime, + RialtoGrandpaInstance, + WithRialtoParachainsInstance, + WithRialtoParachainMessagesInstance, + millau_runtime::BridgeRejectObsoleteHeadersAndMessages, + TestParachain, + TestLaneId, + millau_runtime::Runtime, + >; + + fn relayer_account() -> millau_runtime::AccountId { + [0u8; 32].into() + } + + fn initialize_environment( + best_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: RelayBlockNumber, + best_delivered_message: MessageNonce, + ) { + let best_relay_header = HeaderId(best_relay_header_number, RelayBlockHash::default()); + pallet_bridge_grandpa::BestFinalized::::put( + best_relay_header, + ); + + let para_id = ParaId(TestParachain::get()); + let para_info = ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: parachain_head_at_relay_header_number, + head_hash: Default::default(), + }, + next_imported_hash_position: 0, + }; + pallet_bridge_parachains::ParasInfo::::insert( + para_id, para_info, + ); + + let lane_id = TestLaneId::get(); + let lane_data = + InboundLaneData { last_confirmed_nonce: best_delivered_message, ..Default::default() }; + pallet_bridge_messages::InboundLanes::::insert(lane_id, lane_data); + } + + fn submit_relay_header_call(relay_header_number: RelayBlockNumber) -> RuntimeCall { + let relay_header = bp_rialto::Header::new( + relay_header_number, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ); + let relay_justification = make_default_justification(&relay_header); + + RuntimeCall::BridgeRialtoGrandpa(GrandpaCall::submit_finality_proof { + finality_target: Box::new(relay_header), + justification: relay_justification, + }) + } + + fn submit_parachain_head_call( + parachain_head_at_relay_header_number: RelayBlockNumber, + ) -> RuntimeCall { + RuntimeCall::BridgeRialtoParachains(ParachainsCall::submit_parachain_heads { + at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()), + parachains: vec![(ParaId(TestParachain::get()), [1u8; 32].into())], + parachain_heads_proof: ParaHeadsProof(vec![]), + }) + } + + fn message_delivery_call(best_message: MessageNonce) -> RuntimeCall { + RuntimeCall::BridgeRialtoParachainMessages(MessagesCall::receive_messages_proof { + relayer_id_at_bridged_chain: relayer_account(), + proof: millau_runtime::bridge_runtime_common::messages::target::FromBridgedChainMessagesProof { + bridged_header_hash: Default::default(), + storage_proof: vec![], + lane: TestLaneId::get(), + nonces_start: best_message, + nonces_end: best_message, + }, + messages_count: 1, + dispatch_weight: Weight::zero(), + }) + } + + fn parachain_finality_and_delivery_batch_call( + parachain_head_at_relay_header_number: RelayBlockNumber, + best_message: MessageNonce, + ) -> RuntimeCall { + RuntimeCall::Utility(UtilityCall::batch_all { + calls: vec![ + submit_parachain_head_call(parachain_head_at_relay_header_number), + message_delivery_call(best_message), + ], + }) + } + + fn all_finality_and_delivery_batch_call( + relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: RelayBlockNumber, + best_message: MessageNonce, + ) -> RuntimeCall { + RuntimeCall::Utility(UtilityCall::batch_all { + calls: vec![ + submit_relay_header_call(relay_header_number), + submit_parachain_head_call(parachain_head_at_relay_header_number), + message_delivery_call(best_message), + ], + }) + } + + fn all_finality_pre_dispatch_data() -> PreDispatchData { + PreDispatchData { + relayer: relayer_account(), + call_type: CallType::AllFinalityAndDelivery( + ExpectedRelayChainState { best_block_number: 200 }, + ExpectedParachainState { at_relay_block_number: 200 }, + MessagesState { best_nonce: 100 }, + ), + } + } + + fn parachain_finality_pre_dispatch_data() -> PreDispatchData { + PreDispatchData { + relayer: relayer_account(), + call_type: CallType::ParachainFinalityAndDelivery( + ExpectedParachainState { at_relay_block_number: 200 }, + MessagesState { best_nonce: 100 }, + ), + } + } + + fn delivery_pre_dispatch_data() -> PreDispatchData { + PreDispatchData { + relayer: relayer_account(), + call_type: CallType::Delivery(MessagesState { best_nonce: 100 }), + } + } + + fn run_test(test: impl FnOnce()) { + sp_io::TestExternalities::new(Default::default()).execute_with(test) + } + + fn run_validate(call: RuntimeCall) -> TransactionValidity { + let extension: TestExtension = RefundRelayerForMessagesFromParachain(PhantomData); + extension.validate(&relayer_account(), &call, &DispatchInfo::default(), 0) + } + + fn run_pre_dispatch( + call: RuntimeCall, + ) -> Result>, TransactionValidityError> { + let extension: TestExtension = RefundRelayerForMessagesFromParachain(PhantomData); + extension.pre_dispatch(&relayer_account(), &call, &DispatchInfo::default(), 0) + } + + fn dispatch_info() -> DispatchInfo { + DispatchInfo { + weight: Weight::from_ref_time( + frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, + ), + class: frame_support::dispatch::DispatchClass::Normal, + pays_fee: frame_support::dispatch::Pays::Yes, + } + } + + fn post_dispatch_info() -> PostDispatchInfo { + PostDispatchInfo { actual_weight: None, pays_fee: frame_support::dispatch::Pays::Yes } + } + + fn run_post_dispatch( + pre_dispatch_data: Option>, + dispatch_result: DispatchResult, + ) { + let post_dispatch_result = TestExtension::post_dispatch( + Some(pre_dispatch_data), + &dispatch_info(), + &post_dispatch_info(), + 1024, + &dispatch_result, + ); + assert_eq!(post_dispatch_result, Ok(())); + } + + fn expected_reward() -> millau_runtime::Balance { + pallet_transaction_payment::Pallet::::compute_actual_fee( + 1024, + &dispatch_info(), + &post_dispatch_info(), + Zero::zero(), + ) + } + + #[test] + fn validate_allows_non_obsolete_transactions() { + run_test(|| { + initialize_environment(100, 100, 100); + + assert_eq!(run_validate(message_delivery_call(200)), Ok(ValidTransaction::default()),); + + assert_eq!( + run_validate(parachain_finality_and_delivery_batch_call(200, 200)), + Ok(ValidTransaction::default()), + ); + + assert_eq!( + run_validate(all_finality_and_delivery_batch_call(200, 200, 200)), + Ok(ValidTransaction::default()), + ); + }); + } + + #[test] + fn ext_rejects_batch_with_obsolete_relay_chain_header() { + run_test(|| { + initialize_environment(100, 100, 100); + + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call(100, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_validate(all_finality_and_delivery_batch_call(100, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + }); + } + + #[test] + fn ext_rejects_batch_with_obsolete_parachain_head() { + run_test(|| { + initialize_environment(100, 100, 100); + + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call(101, 100, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_pre_dispatch(parachain_finality_and_delivery_batch_call(100, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_validate(all_finality_and_delivery_batch_call(101, 100, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_validate(parachain_finality_and_delivery_batch_call(100, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + }); + } + + #[test] + fn ext_rejects_batch_with_obsolete_messages() { + run_test(|| { + initialize_environment(100, 100, 100); + + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_validate(all_finality_and_delivery_batch_call(200, 200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_validate(parachain_finality_and_delivery_batch_call(200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + }); + } + + #[test] + fn pre_dispatch_parses_batch_with_relay_chain_and_parachain_headers() { + run_test(|| { + initialize_environment(100, 100, 100); + + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), + Ok(Some(all_finality_pre_dispatch_data())), + ); + }); + } + + #[test] + fn pre_dispatch_parses_batch_with_parachain_header() { + run_test(|| { + initialize_environment(100, 100, 100); + + assert_eq!( + run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), + Ok(Some(parachain_finality_pre_dispatch_data())), + ); + }); + } + + #[test] + fn pre_dispatch_fails_to_parse_batch_with_multiple_parachain_headers() { + run_test(|| { + initialize_environment(100, 100, 100); + + let call = RuntimeCall::Utility(UtilityCall::batch_all { + calls: vec![ + RuntimeCall::BridgeRialtoParachains(ParachainsCall::submit_parachain_heads { + at_relay_block: (100, RelayBlockHash::default()), + parachains: vec![ + (ParaId(TestParachain::get()), [1u8; 32].into()), + (ParaId(TestParachain::get() + 1), [1u8; 32].into()), + ], + parachain_heads_proof: ParaHeadsProof(vec![]), + }), + message_delivery_call(200), + ], + }); + + assert_eq!(run_pre_dispatch(call), Ok(None),); + }); + } + + #[test] + fn pre_dispatch_parses_message_delivery_transaction() { + run_test(|| { + initialize_environment(100, 100, 100); + + assert_eq!( + run_pre_dispatch(message_delivery_call(200)), + Ok(Some(delivery_pre_dispatch_data())), + ); + }); + } + + #[test] + fn post_dispatch_ignores_unknown_transaction() { + run_test(|| { + assert_storage_noop!(run_post_dispatch(None, Ok(()))); + }); + } + + #[test] + fn post_dispatch_ignores_failed_transaction() { + run_test(|| { + assert_storage_noop!(run_post_dispatch( + Some(all_finality_pre_dispatch_data()), + Err(DispatchError::BadOrigin) + )); + }); + } + + #[test] + fn post_dispatch_ignores_transaction_that_has_not_updated_relay_chain_state() { + run_test(|| { + initialize_environment(100, 200, 200); + + assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); + }); + } + + #[test] + fn post_dispatch_ignores_transaction_that_has_not_updated_parachain_state() { + run_test(|| { + initialize_environment(200, 100, 200); + + assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); + assert_storage_noop!(run_post_dispatch( + Some(parachain_finality_pre_dispatch_data()), + Ok(()) + )); + }); + } + + #[test] + fn post_dispatch_ignores_transaction_that_has_not_delivered_any_messages() { + run_test(|| { + initialize_environment(200, 200, 100); + + assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); + assert_storage_noop!(run_post_dispatch( + Some(parachain_finality_pre_dispatch_data()), + Ok(()) + )); + assert_storage_noop!(run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(()))); + }); + } + + #[test] + fn post_dispatch_refunds_relayer_in_all_finality_batch() { + run_test(|| { + initialize_environment(200, 200, 200); + + run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(())); + assert_eq!( + RelayersPallet::::relayer_reward(relayer_account(), TestLaneId::get()), + Some(expected_reward()), + ); + }); + } + + #[test] + fn post_dispatch_refunds_relayer_in_parachain_finality_batch() { + run_test(|| { + initialize_environment(200, 200, 200); + + run_post_dispatch(Some(parachain_finality_pre_dispatch_data()), Ok(())); + assert_eq!( + RelayersPallet::::relayer_reward(relayer_account(), TestLaneId::get()), + Some(expected_reward()), + ); + }); + } + + #[test] + fn post_dispatch_refunds_relayer_in_message_delivery_transaction() { + run_test(|| { + initialize_environment(200, 200, 200); + + run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(())); + assert_eq!( + RelayersPallet::::relayer_reward(relayer_account(), TestLaneId::get()), + Some(expected_reward()), + ); + }); + } +} diff --git a/deny.toml b/deny.toml index 3fa007bbe0a..264a37bd989 100644 --- a/deny.toml +++ b/deny.toml @@ -48,21 +48,17 @@ notice = "warn" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. ignore = [ - # Comes from honggfuzz via storage-proof-fuzzer: 'memmap' - "RUSTSEC-2020-0077", # time (origin: Substrate RPC + benchmarking crates) "RUSTSEC-2020-0071", - # chrono (origin: Substrate benchmarking + cli + ...) - "RUSTSEC-2020-0159", - # lru 0.6.6 (origin: libp2p) - "RUSTSEC-2021-0130", # ansi_term (The maintainer has adviced that this crate is deprecated and will not receive any maintenance. # Once other crates will move to some alternative, we'll do that too) "RUSTSEC-2021-0139", - # rocksdb (origin: Substrate and Polkadot kvdb-rocksdb - we need to upgrade soon) - "RUSTSEC-2022-0046", - # owning_ref (origin: Substrate, libp2p) - "RUSTSEC-2022-0040", + # deprecated parity-wasm (origin: Substrate) + "RUSTSEC-2022-0061", + # atty (origin: Substrate, clap) + "RUSTSEC-2021-0145", + # wasmtime (origin: Substrate) + "RUSTSEC-2022-0076", ] # Threshold for security vulnerabilities, any vulnerability with a CVSS score # lower than the range specified will be ignored. Note that ignored advisories diff --git a/deployments/README.md b/deployments/README.md index 2f804b3ad32..e478f7df84a 100644 --- a/deployments/README.md +++ b/deployments/README.md @@ -1,11 +1,13 @@ # Bridge Deployments ## Requirements + Make sure to install `docker` and `docker-compose` to be able to run and test bridge deployments. If for whatever reason you can't or don't want to use Docker, you can find some scripts for running the -bridge [here](https://github.com/svyatonik/parity-bridges-common.test). +bridge [here](./local-scripts/). ## Networks + One of the building blocks we use for our deployments are _networks_. A network is a collection of homogenous blockchain nodes. We have Docker Compose files for each network that we want to bridge. Each of the compose files found in the `./networks` folder is able to independently spin up a @@ -18,6 +20,7 @@ docker-compose -f ./networks/rialto.yml up After running this command we would have a network of several nodes producing blocks. ## Bridges + A _bridge_ is a way for several _networks_ to connect to one another. Bridge deployments have their own Docker Compose files which can be found in the `./bridges` folder. These Compose files typically contain bridge relayers, which are services external to blockchain nodes, and other components such @@ -43,10 +46,11 @@ and Grafana. We cover these in more details in the [Monitoring](#monitoring) sec the monitoring Compose file is _not_ optional, and must be included for bridge deployments. ### Running and Updating Deployments + We currently support three bridge deployments 1. Rialto Substrate to Millau Substrate 2. Rialto Parachain Substrate to Millau Substrate -2. Westend Substrate to Millau Substrate +2. Westend Substrate to Millau Substrate (only finality bridge) These bridges can be deployed using our [`./run.sh`](./run.sh) script. @@ -71,11 +75,12 @@ You can also bring down a deployment using the script with the `stop` argument. ``` ### Adding Deployments + We need two main things when adding a new deployment. First, the new network which we want to bridge. A compose file for the network should be added in the `/networks/` folder. Secondly we'll need a new bridge Compose file in `./bridges/`. This should configure the bridge relayer nodes correctly for the two networks, and add any additional components needed for the deployment. If you -want you can also add support in the `./run` script for the new deployment. While recommended it's +want you can also add support in the `./run.sh` script for the new deployment. While recommended it's not strictly required. ## General Notes @@ -111,8 +116,8 @@ is not recommended, because this may lead to nonces conflict. Following accounts are used when `rialto-millau` bridge is running: -- Millau's `Rialto.HeadersAndMessagesRelay` signs complex headers+messages relay transactions on Millau chain; -- Rialto's `Millau.HeadersAndMessagesRelay` signs complex headers+messages relay transactions on Rialto chain; +- Millau's `Rialto.HeadersAndMessagesRelay1` signs complex headers+messages relay transactions on Millau chain; +- Rialto's `Millau.HeadersAndMessagesRelay1` signs complex headers+messages relay transactions on Rialto chain; - Millau's `Rialto.MessagesSender` signs Millau transactions which contain messages for Rialto; - Rialto's `Millau.MessagesSender` signs Rialto transactions which contain messages for Millau; - Millau's `Rialto.OutboundMessagesRelay.Lane00000001` signs relay transactions with message delivery confirmations (lane 00000001) from Rialto to Millau; @@ -136,18 +141,22 @@ Following accounts are used when `rialto-parachain-millau` bridge is running: - RialtoParachain's `Millau.HeadersAndMessagesRelay` signs complex headers+messages relay transactions on RialtoParachain chain. ### Docker Usage + When the network is running you can query logs from individual nodes using: ```bash docker logs rialto_millau-node-charlie_1 -f ``` +You may use the [dump-logs.sh](../scripts/dump-logs.sh) to dump logs of most of running containers. + To kill all leftover containers and start the network from scratch next time: ```bash docker ps -a --format "{{.ID}}" | xargs docker rm # This removes all containers! ``` ### Docker Compose Usage + If you're not familiar with how to use `docker-compose` here are some useful commands you'll need when interacting with the bridge deployments: @@ -172,10 +181,12 @@ docker-compose -f docker-compose.yml -f docker-compose.override.yml config > doc ``` ## Docker and Git Deployment + It is also possible to avoid using images from the Docker Hub and instead build containers from Git. There are two ways to build the images this way. ### Git Repo + If you have cloned the bridges repo you can build local Docker images by running the following command at the top level of the repo: @@ -189,16 +200,17 @@ This will build a local image of a particular component with a tag of You can configure the build using Docker [build arguments](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg). Here are the arguments currently supported: - - `BRIDGE_REPO`: Git repository of the bridge node and relay code - - `BRIDGE_HASH`: Commit hash within that repo (can also be a branch or tag) - - `ETHEREUM_REPO`: Git repository of the OpenEthereum client - - `ETHEREUM_HASH`: Commit hash within that repo (can also be a branch or tag) - `PROJECT`: Project to build withing bridges repo. Can be one of: - `rialto-bridge-node` - `millau-bridge-node` + - `rialto-parachain-collator` - `substrate-relay` +You may use the [build-containers.sh](../scripts/build-containers.sh) script to build all available +containers. + ### GitHub Actions + We have a nightly job which runs and publishes Docker images for the different nodes and relayers to the [ParityTech Docker Hub](https://hub.docker.com/u/paritytech) organization. These images are used for our ephemeral (temporary) test networks. Additionally, any time a tag in the form of `v*` is @@ -209,6 +221,7 @@ With images built using either method, all you have to do to use them in a deplo `image` field in the existing Docker Compose files to point to the tag of the image you want to use. ### Monitoring + [Prometheus](https://prometheus.io/) is used by the bridge relay to monitor information such as system resource use, and block data (e.g the best blocks it knows about). In order to visualize this data a [Grafana](https://grafana.com/) dashboard can be used. @@ -223,6 +236,7 @@ dashboard can be accessed at `http://localhost:9090`. The Grafana dashboard can `http://localhost:3000`. Note that the default log-in credentials for Grafana are `admin:admin`. ### Environment Variables + Here is an example `.env` file which is used for production deployments and network updates. For security reasons it is not kept as part of version control. When deploying a network this file should be correctly populated and kept in the appropriate [`bridges`](`./bridges`) deployment @@ -247,13 +261,8 @@ UI_EXPECTED_ETHEREUM_NETWORK_ID=105 ### UI -Use [wss://rialto.bridges.test-installations.parity.io/](https://polkadot.js.org/apps/) -as a custom endpoint for [https://polkadot.js.org/apps/](https://polkadot.js.org/apps/). - -### Polkadot.js UI - -To teach the UI decode our custom types used in the pallet, go to: `Settings -> Developer` -and import the [`./types.json`](./types.json) +Use [wss://wss.rialto.brucke.link](https://polkadot.js.org/apps/) as a custom endpoint for +[https://polkadot.js.org/apps/](https://polkadot.js.org/apps/). ## Scripts diff --git a/deployments/bridges/common/generate_messages.sh b/deployments/bridges/common/generate_messages.sh old mode 100644 new mode 100755 index 565b82bc917..156fb5b5d2b --- a/deployments/bridges/common/generate_messages.sh +++ b/deployments/bridges/common/generate_messages.sh @@ -17,11 +17,13 @@ SECONDARY_EXTRA_ARGS=${SECONDARY_EXTRA_ARGS:-""} +trap "echo Exiting... TERM; exit $?" TERM + # Sleep a bit between messages rand_sleep() { SUBMIT_DELAY_S=`shuf -i 0-$MAX_SUBMIT_DELAY_S -n 1` echo "Sleeping $SUBMIT_DELAY_S seconds..." - sleep $SUBMIT_DELAY_S + sleep $SUBMIT_DELAY_S & wait $! NOW=`date "+%Y-%m-%d %H:%M:%S"` echo "Woke up at $NOW" } diff --git a/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json b/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json index e4695351105..ecb06074d7f 100644 --- a/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json +++ b/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json @@ -153,12 +153,6 @@ "interval": "", "legendFormat": "With-Millau messages relay account balance", "refId": "B" - }, - { - "expr": "at_Rialto_relay_MillauMessagesPalletOwner_balance", - "interval": "", - "legendFormat": "With-Millau messages pallet owner account balance", - "refId": "C" } ], "thresholds": [ @@ -346,12 +340,6 @@ "interval": "", "legendFormat": "With-Rialto messages relay account balance", "refId": "B" - }, - { - "expr": "at_Millau_relay_RialtoMessagesPalletOwner_balance", - "interval": "", - "legendFormat": "With-Rialto messages pallet owner account balance", - "refId": "C" } ], "thresholds": [ diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh index 367792121de..c6162765351 100755 --- a/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh +++ b/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh @@ -24,12 +24,10 @@ sleep 6 --millau-host millau-node-alice \ --millau-port 9944 \ --millau-signer //Rialto.HeadersAndMessagesRelay \ - --millau-messages-pallet-owner=//Rialto.MessagesOwner \ --millau-transactions-mortality=64 \ --rialto-host rialto-node-alice \ --rialto-port 9944 \ --rialto-signer //Millau.HeadersAndMessagesRelay \ - --rialto-messages-pallet-owner=//Millau.MessagesOwner \ --rialto-transactions-mortality=64 \ --lane=00000000 \ --lane=73776170 \ diff --git a/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json index c125b08df32..0da5b8cb4dc 100644 --- a/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json +++ b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json @@ -153,12 +153,6 @@ "interval": "", "legendFormat": "With-Millau messages relay account balance", "refId": "B" - }, - { - "expr": "at_RialtoParachain_relay_MillauMessagesPalletOwner_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", - "interval": "", - "legendFormat": "With-Millau messages pallet owner account balance", - "refId": "C" } ], "thresholds": [ @@ -352,12 +346,6 @@ "interval": "", "legendFormat": "With-Rialto parachains relay account balance", "refId": "C" - }, - { - "expr": "at_Millau_relay_RialtoParachainMessagesPalletOwner_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", - "interval": "", - "legendFormat": "With-RialtoParachain messages pallet owner account balance", - "refId": "D" } ], "thresholds": [ diff --git a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-resubmitter-entrypoint.sh b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-resubmitter-entrypoint.sh index cf4c4612d69..4653ae396c6 100755 --- a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-resubmitter-entrypoint.sh +++ b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-resubmitter-entrypoint.sh @@ -15,7 +15,7 @@ sleep 15 # we need tip around `526186677695 - 17800827994 = 508_385_849_701`. Let's round it # up to `1_000_000_000_000`. -/home/user/substrate-relay resubmit-transactions millau \ +exec /home/user/substrate-relay resubmit-transactions millau \ --target-host millau-node-alice \ --target-port 9944 \ --target-signer //RialtoParachain.MessagesSender \ diff --git a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-millau-rialto-parachain-entrypoint.sh b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-millau-rialto-parachain-entrypoint.sh index f2732e82f49..7685f5f5ec2 100755 --- a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-millau-rialto-parachain-entrypoint.sh +++ b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-millau-rialto-parachain-entrypoint.sh @@ -24,17 +24,14 @@ RIALTO_PARACHAIN_RELAY_ACCOUNT=${EXT_RIALTO_PARACHAIN_RELAY_ACCOUNT:-//Millau.He # Give chain a little bit of time to process initialization transaction sleep 6 -/home/user/substrate-relay relay-headers-and-messages millau-rialto-parachain \ +exec /home/user/substrate-relay relay-headers-and-messages millau-rialto-parachain \ --millau-host millau-node-alice \ --millau-port 9944 \ --millau-signer $MILLAU_RELAY_ACCOUNT \ - --rialto-headers-to-millau-signer $MILLAU_RELAY_ACCOUNT_HEADERS_OVERRIDE \ - --millau-messages-pallet-owner=//RialtoParachain.MessagesOwner \ --millau-transactions-mortality=64 \ --rialto-parachain-host rialto-parachain-collator-charlie \ --rialto-parachain-port 9944 \ --rialto-parachain-signer $RIALTO_PARACHAIN_RELAY_ACCOUNT \ - --rialto-parachain-messages-pallet-owner=//Millau.MessagesOwner \ --rialto-parachain-transactions-mortality=64 \ --rialto-host rialto-node-alice \ --rialto-port 9944 \ diff --git a/deployments/monitoring/docker-compose.yml b/deployments/monitoring/docker-compose.yml index 1fa50c68d0e..4f7d958da4a 100644 --- a/deployments/monitoring/docker-compose.yml +++ b/deployments/monitoring/docker-compose.yml @@ -20,7 +20,9 @@ services: - "3000:3000" depends_on: - prometheus-metrics - entrypoint: sh -c "echo 'sleeping for 10m' && sleep 600 && /run.sh" + # SIGTERM won't work because of our custom entrypoint. Should be ok to use SIGKILL. + stop_signal: SIGKILL + entrypoint: sh -c "${NO_GRAFANA_STARTUP_DELAY:-echo 'sleeping for 10m' && sleep 600} && /run.sh" grafana-matrix-notifier: build: diff --git a/deployments/networks/entrypoints/rialto-chainspec-exporter-entrypoint.sh b/deployments/networks/entrypoints/rialto-chainspec-exporter-entrypoint.sh index 0898978096d..eac2a80de87 100755 --- a/deployments/networks/entrypoints/rialto-chainspec-exporter-entrypoint.sh +++ b/deployments/networks/entrypoints/rialto-chainspec-exporter-entrypoint.sh @@ -1,6 +1,8 @@ #!/bin/bash set -xeu +trap "echo Exiting... TERM; exit $?" TERM + /home/user/rialto-bridge-node build-spec \ --chain local \ --raw \ @@ -11,4 +13,4 @@ set -xeu # by the container running this script. If this script ends, the volume will be detached # and our chain spec will be lost when it'll go online again. Hence the never-ending # script which keeps volume online until container is stopped. -tail -f /dev/null +tail -f /dev/null & wait $! diff --git a/deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh b/deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh index bb201ac5dad..1c33dd00841 100755 --- a/deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh +++ b/deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh @@ -3,7 +3,7 @@ set -xeu sleep 15 -/home/user/substrate-relay register-parachain rialto-parachain \ +exec /home/user/substrate-relay register-parachain rialto-parachain \ --parachain-host rialto-parachain-collator-alice \ --parachain-port 9944 \ --relaychain-host rialto-node-alice \ diff --git a/deployments/run.sh b/deployments/run.sh index bbae4733176..a2a1900c1f0 100755 --- a/deployments/run.sh +++ b/deployments/run.sh @@ -43,6 +43,7 @@ function show_help () { echo " --local-rialto Use prebuilt local/rialto-bridge-node image when starting nodes" echo " --local-rialto-parachain Use prebuilt local/rialto-parachain-collator image when starting nodes" echo " --local-millau Use prebuilt local/millau-bridge-node image when starting nodes" + echo " --no-grafana-startup-delay Start Grafana without any delay (you may see some false alerts during startup)" echo " " echo "You can start multiple bridges at once by passing several bridge names:" echo " ./run.sh rialto-millau rialto-parachain-millau westend-millau [stop|update]" @@ -81,6 +82,7 @@ do export RIALTO_BRIDGE_NODE_IMAGE=local/rialto-bridge-node export RIALTO_PARACHAIN_COLLATOR_IMAGE=local/rialto-parachain-collator export MILLAU_BRIDGE_NODE_IMAGE=local/millau-bridge-node + export IMMEDIATE_ shift continue ;; @@ -104,6 +106,11 @@ do shift continue ;; + --no-grafana-startup-delay) + export NO_GRAFANA_STARTUP_DELAY="echo 'No Grafana startup delay'" + shift + continue + ;; everything|all) BRIDGES=(${RIALTO_MILLAU:-} ${RIALTO_PARACHAIN_MILLAU:-} ${WESTEND_MILLAU:-}) NETWORKS="${RIALTO:-} ${RIALTO_PARACHAIN:-} ${MILLAU:-}" diff --git a/deployments/ui/docker-compose.yml b/deployments/ui/docker-compose.yml index 8b3f8178c36..2d400609c44 100644 --- a/deployments/ui/docker-compose.yml +++ b/deployments/ui/docker-compose.yml @@ -9,5 +9,6 @@ services: LETSENCRYPT_EMAIL: admin@parity.io CHAIN_1_SUBSTRATE_PROVIDER: ${UI_CHAIN_1:-ws://localhost:9944} CHAIN_2_SUBSTRATE_PROVIDER: ${UI_CHAIN_2:-ws://localhost:19944} + stop_signal: SIGKILL ports: - "8080:80" diff --git a/docs/complex-relay.html b/docs/complex-relay.html new file mode 100644 index 00000000000..21524bfd049 --- /dev/null +++ b/docs/complex-relay.html @@ -0,0 +1,85 @@ + + + + + + Complex Relay + + +

Complex Relay

+

+ Both Source Chain and Target Chains have Bridge Messages pallets deployed. They also have required + finality pallets deployed - we don't care about finality type here - they can be either Bridge GRANDPA, + or Bridge Parachains finality pallets, or any combination of those.
+

+

+ There are 4-6 relayer subprocesses inside the Complex Relayer. They include two message relayers, + serving the lane in both directions and 2-4 Complex Relayers (depending on the finality type of Source + and Target Chains).
+

+

+ The following diagram shows the way the complex relayer serves the lane in single direction. Everything + below may be applied to the opposite direction if you'll swap the Source and Target Chains. +

+
+ sequenceDiagram + participant Source Chain + participant Complex Relayer + participant Target Chain + + Note right of Source Chain: Finalized: 480, Target Finalized: 50, Sent Messages: 42, Confirmed Messages: 42 + Note left of Target Chain: Finalized: 60, Source Finalized: 420, Received Messages: 42 + + Source Chain ->> Source Chain: someone Sends Message 43 + Source Chain ->> Source Chain: Import and Finalize Block 481 + + Source Chain ->> Complex Relayer: notes new outbound message 43 at Source Chain Block 481 + Note right of Complex Relayer: can't deliver message 43, Source Chain Block 481 is not relayed + Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Source Chain Block 481 + + Source Chain ->> Complex Relayer: Read Finality Proof of Block 481 + Complex Relayer ->> Target Chain: Submit Finality Proof of Block 481 + Target Chain ->> Target Chain: Import and Finalize Block 61 + Note left of Target Chain: Finalized: 61, Source Finalized: 481, Received Messages: 42 + + Source Chain ->> Complex Relayer: Read Proof of Message 43 at Block 481 + Complex Relayer ->> Target Chain: Submit Proof of Message 43 at Block 481 + Target Chain ->> Target Chain: Import and Finalize Block 62 + Note left of Target Chain: Finalized: 62, Source Finalized: 481, Received Messages: { rewarded: 42, messages-relayer-account: [43] } + + Target Chain ->> Complex Relayer: notes new unrewarded relayer at Target Chain Block 62 + Note right of Complex Relayer: can't relay delivery confirmations because Target Chain Block 62 is not relayed + Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Target Chain Block 62 + + Target Chain ->> Complex Relayer: Read Finality Proof of Block 62 + Complex Relayer ->> Source Chain: Submit Finality Proof of Block 62 + Source Chain ->> Source Chain: Import and Finalize Block 482 + Note right of Source Chain: Finalized: 482, Target Finalized: 62, Confirmed Messages: 42 + + Target Chain ->> Complex Relayer: Read Proof of Message 43 Delivery at Block 62 + Complex Relayer ->> Source Chain: Submit Proof of Message 43 Delivery at Block 612 + Source Chain ->> Source Chain: rewards messages-relayer-account for delivering message [43] + Source Chain ->> Source Chain: prune delivered message 43 from runtime storage + Note right of Source Chain: Finalized: 482, Target Finalized: 61, Confirmed Messages: 43 + + Source Chain ->> Source Chain: someone Sends Message 44 + Source Chain ->> Source Chain: Import and Finalize Block 483 + + Source Chain ->> Complex Relayer: notes new outbound message 44 at Source Chain Block 483 and new confirmed message 43 + Note right of Complex Relayer: can't deliver message 44, Source Chain Block 483 is not relayed + Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Source Chain Block 483 + + Source Chain ->> Complex Relayer: Read Finality Proof of Block 483 + Complex Relayer ->> Target Chain: Submit Finality Proof of Block 483 + Target Chain ->> Target Chain: Import and Finalize Block 63 + Note left of Target Chain: Finalized: 63, Source Finalized: 483, Received Messages: { rewarded: 42, messages-relayer-account: [43] } + + Source Chain ->> Complex Relayer: Read Proof of Message 44 and Proof of Message 43 reward at Block 483 + Complex Relayer ->> Target Chain: Submit Proof of Message 44 and Proof of Message 43 reward at Block 483 + Target Chain ->> Target Chain: Import and Finalize Block 64 + Note left of Target Chain: Finalized: 64, Source Finalized: 483, Received Messages: { rewarded: 43, messages-relayer-account: [44] }--> +
+ + + + diff --git a/docs/grandpa-finality-relay.html b/docs/grandpa-finality-relay.html new file mode 100644 index 00000000000..4136621b1a4 --- /dev/null +++ b/docs/grandpa-finality-relay.html @@ -0,0 +1,47 @@ + + + + + + GRANDPA Finality Relay + + +

GRANDPA Finality Relay

+

+ Source Chain is running GRANDPA Finality Gadget. Bridge GRANDPA finality pallet is deployed at + Target Chain runtime. Relayer is configured to relay Source Chain finality to Target Chain. +

+
+ sequenceDiagram + participant Source Chain + participant Relayer + participant Target Chain + Note left of Source Chain: Best: 500, Finalized: 480, Authorities Set Index: 42 + Note right of Target Chain: Uninitialized + + Source Chain ->> Relayer: Read Initialization Data + Relayer ->> Target Chain: Initialize Bridge GRANDPA Finality Pallet + Note right of Target Chain: Finalized: 480, Authorities Set Index: 42 + + Source Chain ->> Source Chain: Import Block 501 + Source Chain ->> Source Chain: Import Block 502 + Source Chain ->> Source Chain: Finalize Block 495 + Source Chain ->> Relayer: Read Finality Proof of Block 495 + Relayer ->> Target Chain: Finality Proof of Block 495 + Note right of Target Chain: Finalized: 495, Authorities Set Index: 42 + + Source Chain ->> Source Chain: Import Block 503 that changes Authorities Set to 43 + Source Chain ->> Source Chain: Finalize Block 500 + Note left of Relayer: Relayer Misses Finality Notification for Block 500 + + Source Chain ->> Source Chain: Import Block 504 + Source Chain ->> Source Chain: Finalize Mandatory Block 503 + Source Chain ->> Source Chain: Finalize Block 504 + Source Chain ->> Relayer: Read Finality Proof of Mandatory Block 503 + Relayer ->> Target Chain: Finality Proof of Block 503 + Note right of Target Chain: Finalized: 503, Authorities Set Index: 43 +
+ + + + diff --git a/docs/high-level-overview.md b/docs/high-level-overview.md index 2642c20c86a..5a76347ce4f 100644 --- a/docs/high-level-overview.md +++ b/docs/high-level-overview.md @@ -1,165 +1,181 @@ # High-Level Bridge Documentation +This document gives a brief, abstract description of main components that may be found in this repository. +If you want to see how we're using them to build Rococo <> Wococo (Kusama <> Polkadot) bridge, please +refer to the [Polkadot <> Kusama Bridge](./polkadot-kusama-bridge-overview.md). + ## Purpose -Trustless connecting between two Substrate-based chains using GRANDPA finality. +This repo contains all components required to build a trustless connection between standalone Substrate chains, +that are using GRANDPA finality, their parachains or any combination of those. On top of this connection, we +offer a messaging pallet that provides means to organize messages exchange. + +On top of that layered infrastructure, anyone may build their own bridge applications - e.g. [XCM messaging](./polkadot-kusama-bridge-overview.md), +[encoded calls messaging](https://github.com/paritytech/parity-bridges-common/releases/tag/encoded-calls-messaging) and so on. + +## Terminology + +Even though we support (and require) two-way bridging, the documentation will generally talk about +a one-sided interaction. That's to say, we will only talk about syncing finality proofs and messages +from a _source_ chain to a _target_ chain. This is because the two-sided interaction is really just the +one-sided interaction with the source and target chains switched. + +The bridge has both on-chain (pallets) and offchain (relayers) components. + +## On-chain components -## Overview +On-chain bridge components are pallets that are deployed at the chain runtime. Finality pallets require +deployment at the target chain, while messages pallet needs to be deployed at both, source +and target chains. -Even though we support two-way bridging, the documentation will generally talk about a one-sided -interaction. That's to say, we will only talk about syncing headers and messages from a _source_ -chain to a _target_ chain. This is because the two-sided interaction is really just the one-sided -interaction with the source and target chains switched. +### Bridge GRANDPA Finality Pallet -To understand the full interaction with the bridge, take a look at the -[testing scenarios](./testing-scenarios.md) document. It describes potential use cases and describes -how each of the layers outlined below is involved. +A GRANDPA light client of the source chain built into the target chain's runtime. It provides a "source of truth" +about the source chain headers which have been finalized. This is useful for higher level applications. -The bridge is built from various components. Here is a quick overview of the important ones. +The pallet tracks current GRANDPA authorities set and only accepts finality proofs (GRANDPA justifications), +generated by the current authorities set. The GRANDPA protocol itself requires current authorities set to +generate explicit justificaion for the header that enacts next authorities set. Such headers and their finality +proofs are called mandatory in the pallet and relayer pays no fee for such headers submission. -### Header Sync +The pallet does not require all headers to be imported or provided. The relayer itself chooses which headers +he wants to submit (with the exception of mandatory headers). -A light client of the source chain built into the target chain's runtime. It is a single FRAME -pallet. It provides a "source of truth" about the source chain headers which have been finalized. -This is useful for higher level applications. +More: [code](../modules/grandpa/). -### Headers Relayer +### Bridge Parachains Finality Pallet -A standalone application connected to both chains. It submits every source chain header it sees to -the target chain through RPC. +Parachains are not supposed to have their own finality, so we can't use bridge GRANDPA pallet to verify their +finality proofs. Instead, they rely on their relay chain finality. The parachain header is considered final, +when it is accepted by the [`paras` pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras) +at its relay chain. Obviously, the relay chain block, where it is accepted, must also be finalized by the relay +chain GRANDPA gadget. -### Message Delivery +That said, the bridge parachains pallet accepts storage proof of one or several parachain heads, inserted to the +[`Heads`](https://github.com/paritytech/polkadot/blob/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras/mod.rs#L642) +map of the [`paras` pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras). +To verify this storage proof, the pallet uses relay chain header, imported earlier by the bridge GRANDPA pallet. -A FRAME pallet built on top of the header sync pallet. It allows users to submit messages to the -source chain, which are to be delivered to the target chain. The delivery protocol doesn't care -about the payload more than it has to. Handles replay protection and message ordering. +The pallet may track multiple parachains at once and those parachains may use different primitives. So the +parachain header decoding never happens at the pallet level. For maintaining the headers order, the pallet +uses relay chain header number. -### Message Dispatch +More: [code](../modules/parachains/). -A FRAME pallet responsible for interpreting the payload of delivered messages. +### Bridge Messages Pallet -### Message Relayer +The pallet is responsible for queuing messages at the source chain and receiving the messages proofs at the +target chain. The messages are sent to the particular _lane_, where they are guaranteed to be received in the +same order they are sent. The pallet supports many lanes. -A standalone application handling delivery of the messages from source chain to the target chain. +The lane has two ends. Outbound lane end is storing number of messages that have been sent and the number of +messages that have been received. Inbound lane end stores the number of messages that have been received and +also a map that maps messages to relayers that have delivered those messages to the target chain. -## Processes +The pallet has three main entrypoints: +- the `send_message` may be used by the other runtime pallets to send the messages; +- the `receive_messages_proof` is responsible for parsing the messages proof and handing messages over to the +dispatch code; +- the `receive_messages_delivery_proof` is responsible for parsing the messages delivery proof and rewarding +relayers that have delivered the message. -High level sequence charts of the process can be found in [a separate document](./high-level.html). +Many things are abstracted by the pallet: +- the message itself may mean anything, the pallet doesn't care about its content; +- the message dispatch happens during delivery, but it is decoupled from the pallet code; +- the messages proof and messages delivery proof are verified outside of the pallet; +- the relayers incentivization scheme is defined outside of the pallet. -### Substrate (GRANDPA) Header Sync +Outside of the messaging pallet, we have a set of adapters, where messages and delivery proofs are regular +storage proofs. The proofs are generated at the bridged chain and require bridged chain finality. So messages +pallet, in this case, depends on one of the finality pallets. The messages are XCM messages and we are using +XCM executor to dispatch them on receival. You may find more info in [Polkadot <> Kusama Bridge](./polkadot-kusama-bridge-overview.md) +document. -The header sync pallet (`pallet-bridge-grandpa`) is an on-chain light client for chains which use -GRANDPA finality. It is part of the target chain's runtime, and accepts finality proofs from the source -chain. Verify GRANDPA finality proofs (a.k.a justifications) and track GRANDPA finality set changes. +More: [code](../modules/messages/). -The pallet does not care about what block production mechanism is used for the source chain -(e.g Aura or BABE) as long as it uses the GRANDPA finality gadget. In fact the pallet does not -necessarily store all produced headers, we only import headers with valid GRANDPA justifications. +### Bridge Relayers Pallet -Referer to the [pallet documentation](../modules/grandpa/src/lib.rs) for more details. +The pallet is quite simple. It just registers relayer rewards and has an entrypoint to collect them. When +the rewards are registered and the reward amount is configured outside of the pallet. -#### Header Relayer strategy +More: [code](../modules/relayers/). -There is currently no reward strategy for the relayers at all. They also are not required to be -staked or registered on-chain, unlike in other bridge designs. We consider the header sync to be -an essential part of the bridge and the incentivization should be happening on the higher layers. +## Offchain Components -At the moment, signed transactions are the only way to submit headers to the header sync pallet. -However, in the future we would like to use unsigned transactions for headers delivery. This will -allow transaction de-duplication to be done at the transaction pool level and also remove the cost -for message relayers to run header relayers. +Offchain bridge components are separate processes, called relayers. Relayers are connected both to the +source chain and target chain nodes. Relayers are reading state of the source chain, compare it to the +state of the target chain and, if state at target chain needs to be updated, submits target chain +transaction. -### Message Passing +### GRANDPA Finality Relay -Once header sync is maintained, the target side of the bridge can receive and verify proofs about -events happening on the source chain, or its internal state. On top of this, we built a message -passing protocol which consists of two parts described in following sections: message delivery and -message dispatch. +The task of relay is to submit source chain GRANDPA justifications and their corresponding headers to +the Bridge GRANDPA Finality Pallet, deployed at the target chain. For that, the relay subscribes to +the source chain GRANDPA justifications stream and submits every new justification it sees to the +target chain GRANDPA light client. In addition, relay is searching for mandatory headers and +submits their justifications - without that the pallet will be unable to move forward. -#### Message Lanes Delivery +More: [GRANDPA Finality Relay Sequence Diagram](./grandpa-finality-relay.html), [pallet level documentation and code](../relays/finality/). -The [Message delivery pallet](../modules/messages/src/lib.rs) is responsible for queueing up -messages and delivering them in order on the target chain. It also dispatches messages, but we will -cover that in the next section. +### Parachains Finality Relay -The pallet supports multiple lanes (channels) where messages can be added. Every lane can be -considered completely independent from others, which allows them to make progress in parallel. -Different lanes can be configured to validated messages differently (e.g higher rewards, specific -types of payload, etc.) and may be associated with a particular "user application" built on top of -the bridge. Note that messages in the same lane MUST be delivered _in the same order_ they were -queued up. +The relay connects to the source _relay_ chain and the target chain nodes. It doesn't need to connect to the +tracked parachain nodes. The relay looks at the [`Heads`](https://github.com/paritytech/polkadot/blob/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras/mod.rs#L642) +map of the [`paras` pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras) +in source chain, and compares the value with the best parachain head, stored in the bridge parachains pallet at +the taget chain. If new parachain head appears at the relay chain block `B`, the relay process **waits** +until header `B` or one of its ancestors appears at the target chain. Once it is available, the storage +proof of the map entry is generated and is submitted to the target chain. -The message delivery protocol does not care about the payload it transports and can be coupled -with an arbitrary message dispatch mechanism that will interpret and execute the payload if delivery -conditions are met. Each delivery on the target chain is confirmed back to the source chain by the -relayer. This is so that she can collect the reward for delivering these messages. +As its on-chain component (which requires bridge GRANDPA pallet to be deployed nearby), the parachains +finality relay requires GRANDPA finality relay to be running in parallel. Without it, the header `B` or +any of its children's finality at source won't be relayed at target, and target chain +won't be able to verify generated storage proof. -Users of the pallet add their messages to an "outbound lane" on the source chain. When a block is -finalized message relayers are responsible for reading the current queue of messages and submitting -some (or all) of them to the "inbound lane" of the target chain. Each message has a `nonce` -associated with it, which serves as the ordering of messages. The inbound lane stores the last -delivered nonce to prevent replaying messages. To successfully deliver the message to the inbound lane -on target chain the relayer has to present present a storage proof which shows that the message was -part of the outbound lane on the source chain. +More: [Parachains Finality Relay Sequence Diagram](./parachains-finality-relay.html), [code](../relays/parachains/). -During delivery of messages they are immediately dispatched on the target chain and the relayer is -required to declare the correct `weight` to cater for all messages dispatch and pay all required -fees of the target chain. To make sure the relayer is incentivised to do so, on the source chain: -- the user provides a declared dispatch weight of the payload -- the pallet calculates the expected fee on the target chain based on the declared weight -- the pallet converts the target fee into source tokens (based on a price oracle) and reserves - enough tokens to cover for the delivery, dispatch, confirmation and additional relayers reward. +### Messages Relay -If the declared weight turns out to be too low on the target chain the message is delivered but -it immediately fails to dispatch. The fee and reward is collected by the relayer upon confirmation -of delivery. +Messages relay is actually two relays that are running in a single process: messages delivery relay and +delivery confirmation relay. Even though they are more complex and have many caveats, the overall algorithm +is the same as in other relays. -Due to the fact that message lanes require delivery confirmation transactions, they also strictly -require bi-directional header sync (i.e. you can't use message delivery with one-way header sync). +Message delivery relay connects to the source chain and looks at the outbound lane end, waiting until new +messages are queued there. Once they appear at the source block `B`, the relay start waiting for the block +`B` or its descendant appear at the target chain. Then the messages storage proof is generated and submitted +to the bridge messages pallet at the target chain. In addition, the transaction may include the storage proof +of the outbound lane state - that proves that relayer rewards have been paid and this data (map of relay +accounts to the delivered messages) may be pruned from the inbound lane state at the target chain. -#### Dispatching Messages +Delivery confirmation relay connects to the target chain and starts watching the inbound lane end. When new +messages are delivered to the target chain, the corresponding _source chain account_ is inserted to the +map in the inbound lane data. Relay detects that, say, at the target chain block `B` and waits until that +block or its descendant appears at the source chain. Once that happens, the relay crafts a storage proof of +that data and sends it to the messages pallet, deployed at the source chain. -The [Message dispatch pallet](../modules/dispatch/src/lib.rs) is used to perform the actions -specified by messages which have come over the bridge. For Substrate-based chains this means -interpreting the source chain's message as a `Call` on the target chain. +As you can see, the messages relay also requires finality relay to be operating in parallel. Since messages +relay submits transactions to both source and target chains, it requires both _source-to-target_ and +_target-to-source_ finality relays. They can be GRANDPA finality relays or GRANDPA+parachains finality relays, +depending on the type of connected chain. -An example `Call` of the target chain would look something like this: +More: [Messages Relay Sequence Diagram](./messages-relay.html), [pallet level documentation and code](../relays/messages/). -```rust -target_runtime::Call::Balances(target_runtime::pallet_balances::Call::transfer(recipient, amount)) -``` +### Complex Relay -When sending a `Call` it must first be SCALE encoded and then sent to the source chain. The `Call` -is then delivered by the message lane delivery mechanism from the source chain to the target chain. -When a message is received the inbound message lane on the target chain will try and decode the -message payload into a `Call` enum. If it's successful it will be dispatched after we check that the -weight of the call does not exceed the weight declared by the sender. The relayer pays fees for -executing the transaction on the target chain, but her costs should be covered by the sender on the -source chain. +Every relay transaction has its cost. The only transaction, that is "free" to relayer is when the mandatory +GRANDPA header is submitted. The relay that feeds the bridge with every relay chain and/or parachain head it +sees, will have to pay a (quite large) cost. And if no messages are sent through the bridge, that is just +waste of money. -When dispatching messages there are three Origins which can be used by the target chain: -1. Root Origin -2. Source Origin -3. Target Origin +We have a special relay mode, called _complex relay_, where relay mostly sleeps and only submits transactions +that are required for the messages/confirmations delivery. This mode starts two message relays (in both +directions). All required finality relays are also started in a special _on-demand_ mode. In this mode they +do not submit any headers without special request. As always, the only exception is when GRANDPA finality +relay sees the mandatory header - it is submitted without such request. -Senders of a message can indicate which one of the three origins they would like to dispatch their -message with. However, there are restrictions on who/what is allowed to dispatch messages with a -particular origin. +The message relays are watching their lanes and when, at some block `B`, they see new messages/confirmations +to be delivered, they are asking on-demand relays to relay this block `B`. On-demand relays does that and +then message relay may perform its job. If on-demand relay is a parachain finality relay, it also runs its +own on-demand GRANDPA relay, which is used to relay required relay chain headers. -The Root origin represents the source chain's Root account on the target chain. This origin can can -only be dispatched on the target chain if the "send message" request was made by the Root origin of -the source chain - otherwise the message will fail to be dispatched. - -The Source origin represents an account without a private key on the target chain. This account will -be generated/derived using the account ID of the sender on the source chain. We don't necessarily -require the source account id to be associated with a private key on the source chain either. This -is useful for representing things such as source chain proxies or pallets. - -The Target origin represents an account with a private key on the target chain. The sender on the -source chain needs to prove ownership of this account by using their target chain private key to -sign: `(Call, SourceChainAccountId).encode()`. This will be included in the message payload and -verified by the target chain before dispatch. - -See [`CallOrigin` documentation](../primitives/message-dispatch/src/lib.rs) for more details. - -#### Message Relayers Strategy +More: [Complex Relay Sequence Diagram](./complex-relay.html), [code](../relays/bin-substrate/src/cli/relay_headers_and_messages/). diff --git a/docs/high-level.html b/docs/high-level.html deleted file mode 100644 index 3c4c6178c95..00000000000 --- a/docs/high-level.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - High Level Bridge Components - - -

Header Sync

-

Header pallet on the target chain, keeps track of the forks, but requires finality for blocks that perform authority set changes. That means, it won't sync a fork with authority set change unless that change finalized.

-
- sequenceDiagram - participant Source Chain - participant Relayer - participant Target Chain - Note right of Target Chain: Best: 0, Finalized: 0 - Source Chain ->> Source Chain: Import Block 1 - Source Chain ->> Source Chain: Import Block 2 - Relayer ->> Target Chain: Submit Block 1 - Note right of Target Chain: Best: 1, Finalized: 0 - Relayer ->> Target Chain: Submit Block 2 - Note right of Target Chain: Best: 2, Finalized: 0 - Source Chain ->> Source Chain: Import Block 2' - Relayer ->> Target Chain: Submit Block 2' - Note right of Target Chain: Best: 2 or 2', Finalized: 0 - Source Chain ->> Source Chain: Finalize Block 2' - Relayer ->> Target Chain: Submit Finality of Block 2' - Note right of Target Chain: Best: 2', Finalized: 2' -
-

Message Delivery (single lane)

-

Pending messages are stored on-chain (source) so the relayer code is completely stateless - it can read all the details from the chain.

-

Delivering pending messages requires finality first.

-
- sequenceDiagram - participant Source Chain - participant Relayer - participant Target Chain - Source Chain ->> Source Chain: Queue Message 1 - Source Chain ->> Source Chain: Queue Message 2 - Source Chain ->> Source Chain: Queue Message 3 - Note left of Source Chain: Queued Messages: [1, 2, 3, ] - Note left of Source Chain: Reward for [1, 2, 3, ] reserved - Relayer ->> Target Chain: Deliver Messages 1..2 - Note right of Target Chain: Target chain dispatches the messages.
To Confirm: {1..2 => relayer_1} - Relayer ->> Source Chain: Delivery Confirmation of 1..2 - Note left of Source Chain: Queued Messages: [3, ] - Note left of Source Chain: Reward payout for [1, 2, ] - Relayer -->> Target Chain: Confirmed Messages 1..2 - Note right of Target Chain: To Confirm: {} - Note over Relayer, Target Chain: (this is not a separate transaction,
it's bundled with the "Deliver Messages" proof) -
- - - - diff --git a/docs/messages-relay.html b/docs/messages-relay.html new file mode 100644 index 00000000000..c4dab9901e0 --- /dev/null +++ b/docs/messages-relay.html @@ -0,0 +1,78 @@ + + + + + + Messages Relay + + +

Messages Relay

+

+ Both Source Chain and Target Chains have Bridge Messages pallets deployed. They also have required + finality pallets deployed - we don't care about finality type here - they can be either Bridge GRANDPA, + or Bridge Parachains finality pallets, or any combination of those. +

+

+ Finality Relayer represents two actual relayers - one relays Source Chain Finality to Target Chain. + And another one relays Target Chain Finality to Source Chain. +

+
+ sequenceDiagram + participant Source Chain + participant Finality Relayer + participant Messages Relayer + participant Target Chain + + Note right of Source Chain: Finalized: 480, Target Finalized: 50, Sent Messages: 42, Confirmed Messages: 42 + Note left of Target Chain: Finalized: 60, Source Finalized: 420, Received Messages: 42 + + Source Chain ->> Source Chain: someone Sends Message 43 + Source Chain ->> Source Chain: Import and Finalize Block 481 + + Source Chain ->> Messages Relayer: notes new outbound message 43 at Source Chain Block 481 + Note right of Messages Relayer: can't deliver message 43, Source Chain Block 481 is not relayed + + Source Chain ->> Finality Relayer: Read Finality Proof of Block 481 + Finality Relayer ->> Target Chain: Submit Finality Proof of Block 481 + Target Chain ->> Target Chain: Import and Finalize Block 61 + Note left of Target Chain: Finalized: 61, Source Finalized: 481, Received Messages: 42 + + Source Chain ->> Messages Relayer: Read Proof of Message 43 at Block 481 + Messages Relayer ->> Target Chain: Submit Proof of Message 43 at Block 481 + Target Chain ->> Target Chain: Import and Finalize Block 62 + Note left of Target Chain: Finalized: 62, Source Finalized: 481, Received Messages: { rewarded: 42, messages-relayer-account: [43] } + + Target Chain ->> Messages Relayer: notes new unrewarded relayer at Target Chain Block 62 + Note right of Messages Relayer: can't relay delivery confirmations because Target Chain Block 62 is not relayed + + Target Chain ->> Finality Relayer: Read Finality Proof of Block 62 + Finality Relayer ->> Source Chain: Submit Finality Proof of Block 62 + Source Chain ->> Source Chain: Import and Finalize Block 482 + Note right of Source Chain: Finalized: 482, Target Finalized: 62, Confirmed Messages: 42 + + Target Chain ->> Messages Relayer: Read Proof of Message 43 Delivery at Block 62 + Messages Relayer ->> Source Chain: Submit Proof of Message 43 Delivery at Block 612 + Source Chain ->> Source Chain: rewards messages-relayer-account for delivering message [43] + Source Chain ->> Source Chain: prune delivered message 43 from runtime storage + Note right of Source Chain: Finalized: 482, Target Finalized: 61, Confirmed Messages: 43 + + Source Chain ->> Source Chain: someone Sends Message 44 + Source Chain ->> Source Chain: Import and Finalize Block 483 + + Source Chain ->> Messages Relayer: notes new outbound message 44 at Source Chain Block 483 and new confirmed message 43 + Note right of Messages Relayer: can't deliver message 44, Source Chain Block 483 is not relayed + + Source Chain ->> Finality Relayer: Read Finality Proof of Block 483 + Finality Relayer ->> Target Chain: Submit Finality Proof of Block 483 + Target Chain ->> Target Chain: Import and Finalize Block 63 + Note left of Target Chain: Finalized: 63, Source Finalized: 483, Received Messages: { rewarded: 42, messages-relayer-account: [43] } + + Source Chain ->> Messages Relayer: Read Proof of Message 44 and Proof of Message 43 reward at Block 483 + Messages Relayer ->> Target Chain: Submit Proof of Message 44 and Proof of Message 43 reward at Block 483 + Target Chain ->> Target Chain: Import and Finalize Block 64 + Note left of Target Chain: Finalized: 64, Source Finalized: 483, Received Messages: { rewarded: 43, messages-relayer-account: [44] } +
+ + + + diff --git a/docs/parachains-finality-relay.html b/docs/parachains-finality-relay.html new file mode 100644 index 00000000000..4fc1392b87d --- /dev/null +++ b/docs/parachains-finality-relay.html @@ -0,0 +1,55 @@ + + + + + + Parachains Finality Relay + + +

Parachains Finality Relay

+

+ Source Relay Chain is running GRANDPA Finality Gadget. Source Parachain is a parachain of the Source + Relay Chain. Bridge GRANDPA finality pallet is deployed at Target Chain runtime and is "connected" + to the Source Relay Chain. Bridge Parachains finality pallet is deployed at Target Chain and is + configured to track the Source Parachain. GRANDPA Relayer is configured to relay Source Relay Chain + finality to Target Chain. Parachains Relayer is configured to relay Source Parachain headers finality + to Target Chain. +

+
+ sequenceDiagram + participant Source Parachain + participant Source Relay Chain + participant GRANDPA Relayer + participant Parachains Relayer + participant Target Chain + + Note left of Source Parachain: Best: 125 + Note left of Source Relay Chain: Finalized: 500, Best Parachain at Finalized: 120 + Note right of Target Chain: Best Relay: 480, Best Parachain: 110 + + Source Parachain ->> Source Parachain: Import Block 126 + Source Parachain ->> Source Relay Chain: Receives the Parachain block 126 + + Source Relay Chain ->> Source Relay Chain: Import block 501 + Source Relay Chain ->> Source Relay Chain: Finalize block 501 + Note left of Source Relay Chain: Finalized: 501, Best Parachain at Finalized: 126 + + Source Relay Chain ->> Parachains Relayer: notes new Source Parachain Block 126 + Note left of Parachains Relayer: can't relay Source Parachain Block 126, because it requires at least Source Relay Block 501 at Target Chain + + Source Relay Chain ->> Source Relay Chain: Import block 502 + Source Relay Chain ->> Source Relay Chain: Finalize block 502 + + Source Relay Chain ->> GRANDPA Relayer: read GRANDPA Finality Proof of Block 502 + GRANDPA Relayer ->> Target Chain: submit GRANDPA Finality Proof of Block 502 + Note right of Target Chain: Best Relay: 502, Best Parachain: 110 + + Target Chain ->> Parachains Relayer: notes finalized Source Relay Block 502 at Target Chain + Source Relay Chain ->> Parachains Relayer: read Parachain Finality Proof at Relay Block 502 + Parachains Relayer ->> Target Chain: submit Parachain Finality Proof at Relay Block 502 + Note right of Target Chain: Best Relay: 502, Best Parachain: 126 +
+ + + + diff --git a/docs/plan.md b/docs/plan.md deleted file mode 100644 index 9c4106d9ade..00000000000 --- a/docs/plan.md +++ /dev/null @@ -1,22 +0,0 @@ -Plan for the Internal Audit: -1. High-level overview (describing layers, maybe with pictures) - - what have we done already. - [Tomek to present] - [Hernando to help with diagrams today] - -2. Demo? How to play with the network. - [Hernando] - -3. Demo of token transfer on Millau. - [Hernando] - -4. Go through the scenario description and let people ask questions in the meantime. - Jump to the code on demand. - [Tomek, Hernando, Slava] - - ... - -5. The roadmap - - outstanding issues. - [Tomek] - diff --git a/docs/polkadot-kusama-bridge-overview.md b/docs/polkadot-kusama-bridge-overview.md new file mode 100644 index 00000000000..302196718b9 --- /dev/null +++ b/docs/polkadot-kusama-bridge-overview.md @@ -0,0 +1,132 @@ +# Polkadot <> Kusama Bridge Overview + +This document describes how we use all components, described in the [High-Level Bridge Documentation](./high-level-overview.md), +to build the XCM bridge between Kusama and Polkadot. In this case, our components merely work as a XCM transport +(like XCMP/UMP/HRMP), between chains that are not a part of the same consensus system. + +The overall architecture may be seen in [this diagram](./polkadot-kusama-bridge.html). + +## Bridge Hubs + +All operations at relay chain are expensive. Ideally all non-mandatory transactions must happen on parachains. +That's why we are planning to have two parachains - Polkadot Bridge Hub under Polkadot consensus and Kusama +Bridge Hub under Kusama consensus. + +The Bridge Hub will have all required bridge pallets in its runtime. We hope that later, other teams will be able to +use our bridge hubs too and have their pallets there. + +The Bridge Hub will use the base token of the ecosystem - KSM at Kusama Bridge Hub and DOT at Polkadot Bridge Hub. +The runtime will have minimal set of non-bridge pallets, so there's not much you can do directly on bridge hubs. + +## Connecting Parachains + +You won't be able to directly use bridge hub transactions to send XCM messages over the bridge. Instead, you'll need +to use other parachains transactions, which will use HRMP to deliver message to the Bridge Hub. The Bridge Hub will +just queue this messages in its outbound lane, which is dedicated to deliver messages between two parachains. + +Our first planned bridge will connect the Polkadot' Statemint and Kusama' Statemine. Bridge between those two +parachains would allow Statemint accounts to hold wrapped KSM tokens and Statemine accounts to hold wrapped DOT +tokens. + +For that bridge (pair of parachains under different consensus systems) we'll be using the lane 00000000. Later, +when other parachains will join the bridge, they will be using other lanes for their messages. + +## Running Relayers + +We are planning to run our own complex relayer for the lane 00000000. The relayer will relay Kusama/Polkadot GRANDPA +justifications to the bridge hubs at the other side. It'll also relay finalized Kusama Bridge Hub and Polkadot Bridge +Hub heads. This will only happen when messages will be queued at hubs. So most of time relayer will be idle. + +There's no any active relayer sets, or something like that. Anyone may start its own relayer and relay queued messages. +We are not against that and, as always, appreciate any community efforts. Of course, running relayer has the cost. +Apart from paying for the CPU and network, the relayer pays for transactions at both sides of the bridge. We have +a mechanism for rewarding relayers. + +### Compensating the Cost of Message Delivery Transactions + +One part of our rewarding scheme is that the cost of message delivery, for honest relayer, is zero. The honest relayer +is the relayer, which is following our rules: + +- we do not reward relayers for submitting GRANDPA finality transactions. The only exception is submitting mandatory + headers (headers which are changing the GRANDPA authorities set) - the cost of such transaction is zero. The relayer + will pay the full cost for submitting all other headers; + +- we do not reward relayers for submitting parachain finality transactions. The relayer will pay the full cost for + submitting parachain finality transactions; + +- we compensate the cost of message delivery transactions that have actually delivered the messages. So if your + transaction has claimed to deliver messages `[42, 43, 44]`, but, because of some reasons, has actually delivered + messages `[42, 43]`, the transaction will be free for relayer. If it has not delivered any messages, then + the relayer pays the full cost of the transaction; + +- we compensate the cost of message delivery and all required finality calls, if they are part of the same + [`frame_utility::batch_all`](https://github.com/paritytech/substrate/blob/891d6a5c870ab88521183facafc811a203bb6541/frame/utility/src/lib.rs#L326) + transaction. Of course, the calls inside the batch must be linked - e.g. the submitted parachain head must be used + to prove messages. Relay header must be used to prove parachain head finality. If one of calls fails, or if they + are not linked together, the relayer pays the full transaction cost. + +Please keep in mind that the fee of "zero-cost" transactions is still withdrawn from the relayer account. But the +compensation is registered in the `pallet_bridge_relayers::RelayerRewards` map at the target bridge hub. The relayer +may later claim all its rewards later, using the `pallet_bridge_relayers::claim_rewards` call. + +*A side note*: why we don't simply set the cost of useful transactions to zero? That's because the bridge has its cost. +If we won't take any fees, it would mean that the sender is not obliged to pay for its messages. And Bridge Hub +collators (and, maybe, "treasury") are not receiving any payment for including transactions. More about this later, +in the [Who is Rewarding Relayers](#who-is-rewarding-relayers) section. + +### Message Delivery Confirmation Rewards + +In addition to the "zero-cost" message delivery transactions, the relayer is also rewarded for: + +- delivering every message. The reward is registered during delivery confirmation transaction at the Source Bridge + Hub.; + +- submitting delivery confirmation transaction. The relayer may submit delivery confirmation that e.g. confirms + delivery of four messages, of which the only one (or zero) messages is actually delivered by this relayer. It + receives some fee for confirming messages, delivered by other relayers. + +Both rewards may be claimed using the `pallet_bridge_relayers::claim_rewards` call at the Source Bridge Hub. + +### Who is Rewarding Relayers + +Obviously, there should be someone who is paying relayer rewards. We want bridge transactions to have a cost, so we +can't use fees for rewards. Instead, the parachains using the bridge, use sovereign accounts on both sides +of the bridge to cover relayer rewards. + +Bridged Parachains will have sovereign accounts at bridge hubs. For example, the Statemine (Kusama Parachain) will +have an account at the Polkadot Bridge Hub. The Statemint (Polkadot Parachain) will have an account at the Kusama +Bridge Hub. The sovereign accounts are used as a source of funds when the relayer is calling the +`pallet_bridge_relayers::claim_rewards`. + +Since messages lane is only used by the pair of parachains, there's no collision between different bridges. E.g. +Statemine will only reward relayers that are delivering messages from Statemine. The Statemine sovereign account +is not used to cover rewards of bridging with some other Polkadot Parachain. + +### Multiple Relayers and Rewards + +Our goal is to incentivize running honest relayers. But we have no relayers sets, so at any time anyone may submit +message delivery transaction, hoping that the cost of this transaction will be compensated. So what if some message is +currently queued and two relayers are submitting two identical message delivery transactions at once? Without any +special means, the cost of first included transacton will be compensated and the cost of the other one won't. A honest, +but unlucky relayer will lose some money. In addition, we'll waste some portion of block size and weight, which +may be used by other useful transactions. + +To solve the problem, we have two signed extensions ([generate_bridge_reject_obsolete_headers_and_messages! {}](../bin/runtime-common/src/lib.rs) +and [RefundRelayerForMessagesFromParachain](../bin/runtime-common/src/refund_relayer_extension.rs)), that are +preventing bridge transactions with obsolete data from including into the block. We are rejecting following +transactions: + +- transactions, that are submitting the GRANDPA justification for the best finalized header, or one of its ancestors; + +- transactions, that are submitting the proof of the current best parachain head, or one of its ancestors; + +- transactions, that are delivering already delivered messages. If at least one of messages is not yet delivered, + the transaction is not rejected; + +- transactions, that are confirming delivery of already confirmed messages. If at least one of confirmations is new, + the transaction is not rejected; + +- [`frame_utility::batch_all`](https://github.com/paritytech/substrate/blob/891d6a5c870ab88521183facafc811a203bb6541/frame/utility/src/lib.rs#L326) + transactions, that have both finality and message delivery calls. All restrictions from the + [Compensating the Cost of Message Delivery Transactions](#compensating-the-cost-of-message-delivery-transactions) + are applied. diff --git a/docs/polkadot-kusama-bridge.html b/docs/polkadot-kusama-bridge.html new file mode 100644 index 00000000000..dcbae0e7b17 --- /dev/null +++ b/docs/polkadot-kusama-bridge.html @@ -0,0 +1,67 @@ + + + + + + Polkadot <> Kusama Bridge + + +

Polkadot <> Kusama Bridge

+

+ Our bridge connects two parachains - Kusama Bridge Hub and Polkadot Bridge Hub. Messages that + are sent over bridge have XCM format and we are using existing architecture to dispatch them. + Since both Polkadot, Kusama and their parachains already have means to exchange XCM messages + within the same consensus system (HRMP, VMP, ...), it means that we are able to connect all those + chains with our bridge. +

+

+ In our architecture, the lane that is used to relay messages over the bridge is determined by + the XCM source and destinations. So e.g. bridge between Statemint and Statemine (and opposite direction) + will use the lane 00000000, bridge between some other Polkadot Parachain and some other Kusama Parachain + will use the lane 00000001 and so on. +

+
+ flowchart LR + subgraph Polkadot Consensus + polkadot(((Polkadot))) + statemint(((Statemint))) + polkadot_bh(((Polkadot Bridge Hub))) + + polkadot---statemint + polkadot---polkadot_bh + + statemint-->|Send Message Using HRMP|polkadot_bh + + polkadot_bh-->|Send Message Using HRMP|statemint + statemint-->|Dispatch the Message|statemint + end + subgraph Kusama Consensus + kusama_bh(((Kusama Bridge Hub))) + statemine(((Statemine))) + kusama(((Kusama))) + + kusama---statemine + kusama---kusama_bh + + kusama_bh-->|Send Message Using HRMP|statemine + statemine-->|Dispatch the Message|statemine + + statemine-->|Send Message Using HRMP|kusama_bh + end + + polkadot_bh<===>|Message is relayed to the Bridged Chain using lane 00000000|kusama_bh + + linkStyle 2 stroke:red + linkStyle 7 stroke:red + linkStyle 8 stroke:red + + linkStyle 3 stroke:green + linkStyle 4 stroke:green + linkStyle 9 stroke:green +
+ + + \ No newline at end of file diff --git a/docs/scenario1.html b/docs/scenario1.html deleted file mode 100644 index 808a0c34f0d..00000000000 --- a/docs/scenario1.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - Flow Chart of Millau to Rialto Transfer - - -

Scenario: mDave sending RLT to rEve

-
- sequenceDiagram - participant mDave - participant Millau - participant Bridge Relayer - participant Rialto - participant rEve - Rialto->>Rialto: Endow r(mDave) with RLT. - mDave->>Millau: send_message(transfer, 5 RLT, rEve) - Millau->>Millau: Locks fee & reward for the relayer and queues the message. - rect rgb(205, 226, 244) - Bridge Relayer->>+Millau: What's your best header? - Millau-->>-Bridge Relayer: It's header 5. - Bridge Relayer->>+Rialto: What's the best Millau header you know about? - Rialto-->>-Bridge Relayer: I only know about 4. - Bridge Relayer->>Rialto: Cool, here is Millau header 5 [`submit_signed_header()`]. - Bridge Relayer->>+Rialto: What's the best finalized Millau header you know about? - Rialto-->>-Bridge Relayer: I only know about 3. - Bridge Relayer->>+Millau: Do you have a finality proof for 4..5? - Millau-->>-Bridge Relayer: Yes I do, here it is. - Bridge Relayer->>Rialto: Here is the finality proof for 5 [`finalize_header()`]. - end - rect rgb(218, 195, 244) - Bridge Relayer->>+Millau: Do you have any messages for me to deliver (at 5)? - Millau-->>-Bridge Relayer: Yes, here they are. - Bridge Relayer->>+Rialto: I have some new messages for you [`receive_messages_proof()`]. - Rialto->>Rialto: Validate and Dispatch Message. - Rialto->>rEve: Transfer(5 RLT) from r(mDave). - Rialto-->>-Bridge Relayer: Event(Message Succesfully Dispatched). - Bridge Relayer->>Millau: I sent your message, can I get paid now [`receive_messages_delivery_proof`]? - Millau-->>Bridge Relayer: Yes, here you go $$$. - Bridge Relayer ->>Rialto: These messages are confirmed now, feel free to clean up. - end -
- - - - diff --git a/docs/send-message.md b/docs/send-message.md deleted file mode 100644 index 6984c56d67f..00000000000 --- a/docs/send-message.md +++ /dev/null @@ -1,131 +0,0 @@ -# How to send messages - -The Substrate-to-Substrate relay comes with a command line interface (CLI) which is implemented -by the `substrate-relay` binary. - -``` -Substrate-to-Substrate relay - -USAGE: - substrate-relay - -FLAGS: - -h, --help - Prints help information - - -V, --version - Prints version information - - -SUBCOMMANDS: - help Prints this message or the help of the given subcommand(s) - init-bridge Initialize on-chain bridge pallet with current header data - relay-headers Start headers relay between two chains - relay-messages Start messages relay between two chains - send-message Send custom message over the bridge -``` -The relay related commands `relay-headers` and `relay-messages` are basically continously running a -sync loop between the `Millau` and `Rialto` chains. The `init-bridge` command submitts initialization -transactions. An initialization transaction brings an initial header and authorities set from a source -chain to a target chain. The header synchronization then starts from that header. - -For sending custom messages over an avialable bridge, the `send-message` command is used. - -``` -Send custom message over the bridge. - -Allows interacting with the bridge by sending messages over `Messages` component. The message is being sent to the -source chain, delivered to the target chain and dispatched there. - -USAGE: - substrate-relay send-message - -FLAGS: - -h, --help Prints help information - -V, --version Prints version information - -SUBCOMMANDS: - help Prints this message or the help of the given subcommand(s) - millau-to-rialto Submit message to given Millau -> Rialto lane - rialto-to-millau Submit message to given Rialto -> Millau lane - -``` -Messages are send from a source chain to a target chain using a so called `message lane`. Message lanes handle -both, message transport and message dispatch. There is one command for submitting a message to each of the two -available bridges, namely `millau-to-rialto` and `rialto-to-millau`. - -Submitting a message requires a number of arguments to be provided. Those arguments are essentially the same -for both submit message commands, hence only the output for `millau-to-rialto` is shown below. - -``` -Submit message to given Millau -> Rialto lane - -USAGE: - substrate-relay send-message millau-to-rialto [OPTIONS] --lane --source-host --source-port --source-signer --origin --target-signer - -FLAGS: - -h, --help Prints help information - -V, --version Prints version information - -OPTIONS: - --fee - Delivery and dispatch fee. If not passed, determined automatically - - --lane Hex-encoded lane id - --source-host Connect to Source node at given host - --source-port Connect to Source node websocket server at given port - --source-signer - The SURI of secret key to use when transactions are submitted to the Source node - - --source-signer-password - The password for the SURI of secret key to use when transactions are submitted to the Source node - - --origin - The origin to use when dispatching the message on the target chain [possible values: Target, Source] - - --target-signer - The SURI of secret key to use when transactions are submitted to the Target node - - --target-signer-password - The password for the SURI of secret key to use when transactions are submitted to the Target node - - -SUBCOMMANDS: - help Prints this message or the help of the given subcommand(s) - remark Make an on-chain remark (comment) - transfer Transfer the specified `amount` of native tokens to a particular `recipient` - -``` -As can be seen from the output, there are two types of messages available: `remark` and `transfer`. -A remark is some opaque message which will be placed on-chain. For basic testing, a remark is -the easiest to go with. - -Usage of the arguments is best explained with an example. Below you can see, how a remark -would look like: - -``` -substrate-relay send-message millau-to-rialto \ - --source-host=127.0.0.1 \ - --source-port=10946 \ - --source-signer=//Dave \ - --target-signer=//Dave \ - --lane=00000000 \ - --origin Target \ - remark -``` -Messages are basically regular transactions. That means, they have to be signed. In order -to send a message, you have to control an account private key on both, the source and -the target chain. Those accounts are specified using the `--source-signer` and `--target-signer` -arguments in the example above. - -Message delivery and dispatch requires a fee to be paid. In the example above, we have not -specified the `--fee` argument. Hence, the fee will be estimated automatically. Note that -in order to pay the fee, the message sender account has to have sufficient funds available. - -The `--origin` argument allows to denote under which authority the message will be dispatched -on the target chain. Accepted values are `Target` and `Source`. - -Although not strictly necessary, it is recommended, to use one of the well-known development -accounts (`Alice`, `Bob`, `Charlie`, `Dave`, `Eve`) for message sending. Those accounts are -endowed with funds for fee payment. In addtion, the development `Seed URI` syntax -(like `//Dave`) for the signer can be used, which will remove the need for a password. diff --git a/docs/testing-scenarios.md b/docs/testing-scenarios.md deleted file mode 100644 index 343720524ec..00000000000 --- a/docs/testing-scenarios.md +++ /dev/null @@ -1,221 +0,0 @@ -# Testing Scenarios - -In the scenarios, for simplicity, we call the chains Kusama (KSM token) and Polkadot (DOT token), -but they should be applicable to any other chains. The first scenario has detailed description about -the entire process (also see the [sequence diagram](./scenario1.html)). Other scenarios only contain -a simplified interaction focusing on things that are unique for that particular scenario. - -Notation: -- kX - user X interacting with Kusama chain. -- `k(kX)` - Kusama account id of user kX (native account id; usable on Kusama) -- `p(kX)` - Polkadot account id of user kX (account id derived from `k(kX)` usable on Polkadot) -- [Kusama] ... - Interaction happens on Kusama (e.g. the user interacts with Kusama chain) -- [Polkadot] ... - Interaction happens on Polkadot - -Basic Scenarios -=========================== - -Scenario 1: Kusama's Alice receiving & spending DOTs ---------------------------- - -Kusama's Alice (kAlice) receives 5 DOTs from Polkadot's Bob (pBob) and sends half of them to -kCharlie. - -1. Generate kAlice's DOT address (`p(kAlice)`). - See function: - - ```rust - bp_runtime::derive_account_id(b"pdot", kAlice) - ``` - - or: - - ```rust - let hash = bp_polkadot::derive_kusama_account_id(kAlice); - let p_kAlice = bp_polkadot::AccountIdConverter::convert(hash); - ``` - -2. [Polkadot] pBob transfers 5 DOTs to `p(kAlice)` - 1. Creates & Signs a transaction with `Call::Transfer(..)` - 1. It is included in block. - 1. kAlice observers Polkadot chain to see her balance at `p(kAlice)` updated. - -3. [Kusama] kAlice sends 2.5 DOTs to `p(kCharlie)` - 1. kAlice prepares: - ```rust - let call = polkadot::Call::Balances(polkadot::Balances::Transfer(p(kCharlie), 2.5DOT)).encode(); - let weight = call.get_dispatch_info().weight; - ``` - - 1. kAlice prepares Kusama transaction: - ```rust - kusama::Call::Messages::::send_message( - // dot-transfer-lane (truncated to 4bytes) - lane_id, - payload: MessagePayload { - // Get from current polkadot runtime (kind of hardcoded) - spec_version: 1, - // kAlice should know the exact dispatch weight of the call on the target - // source verifies: at least to cover call.length() and below max weight - weight, - // simply bytes, we don't know anything about that on the source chain - call, - // origin that should be used during dispatch on the target chain - origin: CallOrigin::SourceAccount(kAlice), - }, - delivery_and_dispatch_fee: { - (single_message_delivery_weight - // source weight = X * target weight - + convert_target_weight_to_source_weight(weight) - + confirmation_transaction_weight - ) - // This uses an on-chain oracle to convert weights of the target chain to source fee - * weight_to_fee - // additional reward for the relayer (pallet parameter) - + relayers_fee - }, - ) - ``` - - 1. [Kusama] kAlice sends Kusama transaction with the above `Call` and pays regular fees. The - dispatch additionally reservers target-chain delivery and dispatch fees (including relayer's - reward). - -4. [Kusama] kAlice's transaction is included in block `B1` - -### Syncing headers loop - -5. Relayer sees that `B1` has not yet been delivered to the target chain. - [Sync loop code](https://github.com/paritytech/parity-bridges-common/blob/8b327a94595c4a6fae6d7866e24ecf2390501e32/relays/headers-relay/src/sync_loop.rs#L199). - -1. Relayer prepares transaction which delivers `B1` and with all of the missing - ancestors to the target chain (one header per transaction). - -1. After the transaction is succesfully dispatched the Polkadot on-chain light client of the Kusama - chain learns about block `B1` - it is stored in the on-chain storage. - -### Syncing finality loop - -8. Relayer is subscribed to finality events on Kusama. Relayer gets a finality notification for - block `B3`. - -1. The header sync informs the target chain about `B1..B3` blocks (see point 6). - -1. Relayer learns about missing finalization of `B1..B3` on the target chain, see - [finality maintenance code](https://github.com/paritytech/parity-bridges-common/blob/8b327a94595c4a6fae6d7866e24ecf2390501e32/relays/substrate/src/headers_maintain.rs#L107). - -1. Relayer submits justification for `B3` to the target chain (`finalize_header`). - See [#421](https://github.com/paritytech/parity-bridges-common/issues/421) for multiple - authority set changes support in Relayer (i.e. what block the target chain expects, not only - what I have). - - Relayer is doing two things: - - syncing on demand (what blocks miss finality) - - and syncing as notifications are received (recently finalized on-chain) - -1. Eventually Polkadot on-chain light client of Kusama learns about finality of `B1`. - -### Syncing messages loop - -13. The relayer checks the on-chain storage (last finalized header on the source, best header on the - target): - - Kusama outbound lane - - Polkadot inbound lane - Lanes contains `latest_generated_nonce` and `latest_received_nonce` respectively. The relayer - syncs messages between that range. - -1. The relayer gets a proof for every message in that range (using the RPC of messages module) - -1. The relayer creates a message delivery transaction (but it has weight, size, and count limits). - The count limit is there to make the loop of delivery code bounded. - ```rust - receive_message_proof( - relayer_id, // account id of the source chain - proof, // messages + proofs (hash of source block `B1`, nonces, lane_id + storage proof) - dispatch_weight // relayer declares how much it will take to dispatch all messages in that transaction, - ) - ``` - The `proof` can also contain an update of outbound lane state of source chain, which indicates - the delivery confirmation of these messages and reward payment, so that the target chain can - truncate its unpayed rewards vector. - - The target chain stores `relayer_ids` that delivered messages because the relayer can generate - a storage proof to show that they did indeed deliver those messages. The reward is paid on the - source chain and we inform the target chain about that fact so it can prune these `relayer_ids`. - - It's totally fine if there are no messages, and we only include the reward payment proof - when calling that function. - -1. 🥳 the message is now delivered and dispatched on the target chain! - -1. The relayer now needs to confirm the delivery to claim her payment and reward on the source - chain. - -1. The relayer creates a transaction on the source chain with call: - - ```rust - receive_messages_delivery_proof( - proof, // hash of the finalized target chain block, lane_id, storage proof - ) - ``` - -### UI challenges - -- The UI should warn before (or prevent) sending to `k(kCharlie)`! - - -Scenario 2: Kusama's Alice nominating validators with her DOTs ---------------------------- - -kAlice receives 10 DOTs from pBob and nominates `p(pCharlie)` and `p(pDave)`. - -1. Generate kAlice's DOT address (`p(kAlice)`) -2. [Polkadot] pBob transfers 5 DOTs to `p(kAlice)` -3. [Kusama] kAlice sends a batch transaction: - - `staking::Bond` transaction to create stash account choosing `p(kAlice)` as the controller account. - - `staking::Nominate(vec![p(pCharlie)])` to nominate pCharlie using the controller account. - - -Scenario 3: Kusama Treasury receiving & spending DOTs ---------------------------- - -pBob sends 15 DOTs to Kusama Treasury which Kusama Governance decides to transfer to kCharlie. - -1. Generate source account for the treasury (`kTreasury`). -2. [Polkadot] pBob tarnsfers 15 DOTs to `p(kTreasury)`. -2. [Kusama] Send a governance proposal to send a bridge message which transfers funds to `p(kCharlie)`. -3. [Kusama] Dispatch the governance proposal using `kTreasury` account id. - -Extra scenarios -=========================== - -Scenario 4: Kusama's Alice setting up 1-of-2 multi-sig to spend from either Kusama or Polkadot ---------------------------- - -Assuming `p(pAlice)` has at least 7 DOTs already. - -1. Generate multisig account id: `pMultiSig = multi_account_id(&[p(kAlice), p(pAlice)], 1)`. -2. [Kusama] Transfer 7 DOTs to `pMultiSig` using `TargetAccount` origin of `pAlice`. -3. [Kusama] Transfer 2 DOTs to `p(kAlice)` from the multisig: - - Send `multisig::as_multi_threshold_1(vec![p(pAlice)], balances::Transfer(p(kAlice), 2))` - -Scenario 5: Kusama Treasury staking & nominating validators with DOTs ---------------------------- - -Scenario 6: Kusama Treasury voting in Polkadot's democracy proposal ---------------------------- - -Potentially interesting scenarios -=========================== - -Scenario 7: Polkadot's Bob spending his DOTs by using Kusama chain ---------------------------- - -We can assume he holds KSM. Problem: he can pay fees, but can't really send (sign) a transaction? -Shall we support some kind of dispatcher? - -Scenario 8: Kusama Governance taking over Kusama's Alice DOT holdings ---------------------------- - -We use `SourceRoot` call to transfer her's DOTs to Kusama treasury. Source chain root -should also be able to send messages as `CallOrigin::SourceAccount(Alice)` though. diff --git a/modules/beefy/Cargo.toml b/modules/beefy/Cargo.toml index 25905126d53..2dca89d82e5 100644 --- a/modules/beefy/Cargo.toml +++ b/modules/beefy/Cargo.toml @@ -18,16 +18,14 @@ bp-runtime = { path = "../../primitives/runtime", default-features = false } # Substrate Dependencies -beefy-merkle-tree = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -primitive-types = { version = "0.12.0", default-features = false, features = ["impl-codec"] } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [dev-dependencies] -beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-beefy = { git = "https://github.com/paritytech/substrate", branch = "master" } mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.3.2" } pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -38,14 +36,12 @@ bp-test-utils = { path = "../../primitives/test-utils" } [features] default = ["std"] std = [ - "beefy-merkle-tree/std", "bp-beefy/std", "bp-runtime/std", "codec/std", "frame-support/std", "frame-system/std", "log/std", - "primitive-types/std", "scale-info/std", "serde", "sp-core/std", diff --git a/modules/beefy/src/lib.rs b/modules/beefy/src/lib.rs index 5ef58c5a3cb..e05a4119463 100644 --- a/modules/beefy/src/lib.rs +++ b/modules/beefy/src/lib.rs @@ -152,6 +152,7 @@ pub mod pallet { BridgedMmrHashing: 'static + Send + Sync, { /// Initialize pallet with BEEFY authority set and best known finalized block number. + #[pallet::call_index(0)] #[pallet::weight((T::DbWeight::get().reads_writes(2, 3), DispatchClass::Operational))] pub fn initialize( origin: OriginFor, @@ -169,6 +170,7 @@ pub mod pallet { /// Change `PalletOwner`. /// /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(1)] #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { >::set_owner(origin, new_owner) @@ -177,6 +179,7 @@ pub mod pallet { /// Halt or resume all pallet operations. /// /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(2)] #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] pub fn set_operating_mode( origin: OriginFor, @@ -193,6 +196,7 @@ pub mod pallet { /// /// If successful in verification, it will update the underlying storage with the data /// provided in the newly submitted commitment. + #[pallet::call_index(3)] #[pallet::weight(0)] pub fn submit_commitment( origin: OriginFor, @@ -410,12 +414,12 @@ pub mod pallet { #[cfg(test)] mod tests { use super::*; - use beefy_primitives::mmr::BeefyAuthoritySet; use bp_runtime::{BasicOperatingMode, OwnedBridgeModuleError}; use bp_test_utils::generate_owned_bridge_module_tests; use frame_support::{assert_noop, assert_ok, traits::Get}; use mock::*; use mock_chain::*; + use sp_beefy::mmr::BeefyAuthoritySet; use sp_runtime::DispatchError; fn next_block() { diff --git a/modules/beefy/src/mock.rs b/modules/beefy/src/mock.rs index 927c39ff9aa..212c6c5d434 100644 --- a/modules/beefy/src/mock.rs +++ b/modules/beefy/src/mock.rs @@ -24,7 +24,7 @@ use crate::{ use bp_beefy::{BeefyValidatorSignatureOf, ChainWithBeefy, Commitment, MmrDataOrHash}; use bp_runtime::{BasicOperatingMode, Chain}; use codec::Encode; -use frame_support::{construct_runtime, parameter_types, weights::Weight}; +use frame_support::{construct_runtime, parameter_types, traits::ConstU64, weights::Weight}; use sp_core::{sr25519::Signature, Pair}; use sp_runtime::{ testing::{Header, H256}, @@ -32,7 +32,7 @@ use sp_runtime::{ Perbill, }; -pub use beefy_primitives::crypto::{AuthorityId as BeefyId, Pair as BeefyPair}; +pub use sp_beefy::crypto::{AuthorityId as BeefyId, Pair as BeefyPair}; use sp_core::crypto::Wraps; use sp_runtime::traits::Keccak256; @@ -49,7 +49,7 @@ pub type TestBridgedMmrHashing = BridgedMmrHashing; pub type TestBridgedMmrHash = BridgedMmrHash; pub type TestBridgedBeefyMmrLeafExtra = BridgedBeefyMmrLeafExtra; pub type TestBridgedMmrProof = BridgedMmrProof; -pub type TestBridgedRawMmrLeaf = beefy_primitives::mmr::MmrLeaf< +pub type TestBridgedRawMmrLeaf = sp_beefy::mmr::MmrLeaf< TestBridgedBlockNumber, TestBridgedBlockHash, TestBridgedMmrHash, @@ -72,7 +72,6 @@ construct_runtime! { } parameter_types! { - pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); @@ -89,7 +88,7 @@ impl frame_system::Config for TestRuntime { type Lookup = IdentityLookup; type Header = Header; type RuntimeEvent = (); - type BlockHashCount = BlockHashCount; + type BlockHashCount = ConstU64<250>; type Version = (); type PalletInfo = PalletInfo; type AccountData = (); @@ -139,7 +138,6 @@ impl ChainWithBeefy for TestBridgedChain { type MmrHash = ::Output; type BeefyMmrLeafExtra = (); type AuthorityId = BeefyId; - type Signature = beefy_primitives::crypto::AuthoritySignature; type AuthorityIdToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; } diff --git a/modules/beefy/src/mock_chain.rs b/modules/beefy/src/mock_chain.rs index 4896f9bf909..ca7e84b121a 100644 --- a/modules/beefy/src/mock_chain.rs +++ b/modules/beefy/src/mock_chain.rs @@ -26,11 +26,11 @@ use crate::{ utils::get_authorities_mmr_root, }; -use beefy_primitives::mmr::{BeefyNextAuthoritySet, MmrLeafVersion}; use bp_beefy::{BeefyPayload, Commitment, ValidatorSetId, MMR_ROOT_PAYLOAD_ID}; use codec::Encode; use pallet_mmr::NodeIndex; use rand::Rng; +use sp_beefy::mmr::{BeefyNextAuthoritySet, MmrLeafVersion}; use sp_core::Pair; use sp_runtime::traits::{Hash, Header as HeaderT}; use std::collections::HashMap; @@ -188,7 +188,7 @@ impl HeaderBuilder { next_validator_keys.iter().map(|pair| pair.public()).collect::>(); let next_validators_mmr_root = get_authorities_mmr_root::(next_validators.iter()); - let leaf = beefy_primitives::mmr::MmrLeaf { + let leaf = sp_beefy::mmr::MmrLeaf { version: MmrLeafVersion::new(1, 0), parent_number_and_hash: (header.number().saturating_sub(1), *header.parent_hash()), beefy_next_authority_set: BeefyNextAuthoritySet { @@ -227,7 +227,7 @@ impl HeaderBuilder { let leaf_index = *self.header.number() - 1; let leaf_count = *self.header.number(); self.leaf_proof = Some(f(TestBridgedMmrProof { - leaf_index, + leaf_indices: vec![leaf_index], leaf_count, items: proof.proof_items().iter().map(|i| i.hash()).collect(), })); diff --git a/modules/beefy/src/utils.rs b/modules/beefy/src/utils.rs index c8a7d2cee14..028e61e96b4 100644 --- a/modules/beefy/src/utils.rs +++ b/modules/beefy/src/utils.rs @@ -1,9 +1,9 @@ use crate::{ BridgedBeefyAuthorityId, BridgedBeefyAuthoritySet, BridgedBeefyAuthoritySetInfo, - BridgedBeefyCommitmentHasher, BridgedBeefyMmrLeaf, BridgedBeefySignedCommitment, BridgedChain, - BridgedMmrHash, BridgedMmrHashing, BridgedMmrProof, Config, Error, LOG_TARGET, + BridgedBeefyMmrLeaf, BridgedBeefySignedCommitment, BridgedChain, BridgedMmrHash, + BridgedMmrHashing, BridgedMmrProof, Config, Error, LOG_TARGET, }; -use bp_beefy::{merkle_root, verify_mmr_leaves_proof, BeefyVerify, MmrDataOrHash, MmrProof}; +use bp_beefy::{merkle_root, verify_mmr_leaves_proof, BeefyAuthorityId, MmrDataOrHash}; use codec::Encode; use frame_support::ensure; use sp_runtime::traits::{Convert, Hash}; @@ -72,7 +72,7 @@ fn verify_signatures, I: 'static>( authority_set.validators().iter().zip(commitment.signatures.iter()).enumerate() { if let Some(sig) = maybe_sig { - if BeefyVerify::>::verify(sig, &msg, authority) { + if authority.verify(sig, &msg) { missing_signatures = missing_signatures.saturating_sub(1); if missing_signatures == 0 { break @@ -131,7 +131,6 @@ pub(crate) fn verify_beefy_mmr_leaf, I: 'static>( mmr_proof: BridgedMmrProof, mmr_root: BridgedMmrHash, ) -> Result<(), Error> { - let mmr_proof_leaf_index = mmr_proof.leaf_index; let mmr_proof_leaf_count = mmr_proof.leaf_count; let mmr_proof_length = mmr_proof.items.len(); @@ -140,16 +139,15 @@ pub(crate) fn verify_beefy_mmr_leaf, I: 'static>( verify_mmr_leaves_proof( mmr_root, vec![BridgedMmrDataOrHash::::Hash(mmr_leaf_hash)], - MmrProof::into_batch_proof(mmr_proof), + mmr_proof, ) .map_err(|e| { log::error!( target: LOG_TARGET, - "MMR proof of leaf {:?} (root: {:?}, leaf: {}, leaf count: {}, len: {}) \ + "MMR proof of leaf {:?} (root: {:?}, leaf count: {}, len: {}) \ verification has failed with error: {:?}", mmr_leaf_hash, mmr_root, - mmr_proof_leaf_index, mmr_proof_leaf_count, mmr_proof_length, e, @@ -163,9 +161,9 @@ pub(crate) fn verify_beefy_mmr_leaf, I: 'static>( mod tests { use super::*; use crate::{mock::*, mock_chain::*, *}; - use beefy_primitives::ValidatorSet; use bp_beefy::{BeefyPayload, MMR_ROOT_PAYLOAD_ID}; use frame_support::{assert_noop, assert_ok}; + use sp_beefy::ValidatorSet; #[test] fn submit_commitment_checks_metadata() { @@ -280,7 +278,7 @@ mod tests { // Fails if mmr proof is incorrect. let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); - header.leaf_proof.leaf_index += 1; + header.leaf_proof.leaf_indices[0] += 1; assert_noop!( import_commitment(header), Error::::MmrProofVerificationFailed, diff --git a/modules/grandpa/README.md b/modules/grandpa/README.md new file mode 100644 index 00000000000..eaa62e8b206 --- /dev/null +++ b/modules/grandpa/README.md @@ -0,0 +1,101 @@ +# Bridge GRANDPA Pallet + +The bridge GRANDPA pallet is a light client for the GRANDPA finality gadget, running at the bridged chain. +It may import headers and their GRANDPA finality proofs (justifications) of the bridged chain. Imported +headers then may be used to verify storage proofs by other pallets. This makes the bridge GRANDPA pallet +a basic pallet of all bridges with Substrate-based chains. It is used by all bridge types (bridge between +standalone chains, between parachains and any combination of those) and is used by other bridge pallets. +It is used by the parachains light client (bridge parachains pallet) and by messages pallet. + +## A Brief Introduction into GRANDPA Finality + +You can find detailed information on GRANDPA, by exploring its [repository](https://github.com/paritytech/finality-grandpa). +Here is the minimal reqiuired GRANDPA information to understand how pallet works. + +Any Substrate chain may use different block authorship algorithms (like BABE or Aura) to determine block producers and +generate blocks. This has nothing common with finality, though - the task of block authorship is to coordinate +blocks generation. Any block may be reverted (if there's a fork) if it is not finalized. The finality solution +for (standalone) Substrate-based chains is the GRANDPA finality gadget. If some block is finalized by the gadget, it +can't be reverted. + +In GRANDPA, there are validators, identified by their public keys. They select some generated block and produce +signatures on this block hash. If there are enough (more than `2 / 3 * N`, where `N` is number of validators) +signatures, then the block is considered finalized. The set of signatures for the block is called justification. +Anyone who knows the public keys of validators is able to verify GRANDPA justification and that it is generated +for provided header. + +There are two main things in GRANDPA that help building light clients: + +- there's no need to import all headers of the bridged chain. Light client may import finalized headers or just + some of finalized headders that it consider useful. While the validators set stays the same, the client may + import any header that is finalized by this set; + +- when validators set changes, the GRANDPA gadget adds next set to the header. So light client doesn't need to + verify storage proofs when this happens - it only needs to look at the header and see if it changes the set. + Once set is changed, all following justifications are generated by the new set. Header that is changing the + set is called "mandatory" in the pallet. As the name says, the light client need to import all such headers + to be able to operate properly. + +## Pallet Operations + +The main entrypoint of the pallet is the `submit_finality_proof` call. It has two arguments - the finalized +headers and associated GRANDPA justification. The call simply verifies the justification using current +validators set and checks if header is better than the previous best header. If both checks are passed, the +header (only its useful fields) is inserted into the runtime storage and may be used by other pallets to verify +storage proofs. + +The submitter pays regular fee for submitting all headers, except for the mandatory header. Since it is +required for the pallet operations, submitting such header is free. So if you're ok with session-length +lags (meaning that there's exactly 1 mandatory header per session), the cost of pallet calls is zero. + +When the pallet sees mandatory header, it updates the validators set with the set from the header. All +following justifications (until next mandatory header) must be generated by this new set. + +## Pallet Initialization + +As the previous section states, there are two things that are mandatory for pallet operations: best finalized +header and the current validators set. Without it the pallet can't import any headers. But how to provide +initial values for these fields? There are two options. + +First option, while it is easier, doesn't work in all cases. It is to start chain with initial header and +validators set specified in the chain specification. This won't work, however, if we want to add bridge +to already started chain. + +For the latter case we have the `initialize` call. It accepts the initial header and initial validators set. +The call may be called by the governance, root or by the pallet owner (if it is set). + +## Non-Essential Functionality + +There may be a special account in every runtime where the bridge GRANDPA module is deployed. This +account, named 'module owner', is like a module-level sudo account - he's able to halt and +resume all module operations without requiring runtime upgrade. Calls that are related to this +account are: + +- `fn set_owner()`: current module owner may call it to transfer "ownership" to another account; + +- `fn set_operating_mode()`: the module owner (or sudo account) may call this function to stop all + module operations. After this call, all finality proofs will be rejected until further `set_operating_mode` call'. + This call may be used when something extraordinary happens with the bridge; + +- `fn initialize()`: module owner may call this function to initialize the bridge. + +If pallet owner is not defined, the governance may be used to make those calls. + +## Signed Extension to Reject Obsolete Headers + +It'd be better for anyone (for chain and for submitters) to reject all transactions that are submitting +already known headers to the pallet. This way, we leave block space to other useful transactions and +we don't charge concurrent submitters for their honest actions. + +To deal with that, we have a [signed extension](./src/extension.rs) that may be added to the runtime. +It does exactly what is required - rejects all transactions with already known headers. The submitter +pays nothing for such transactions - they're simply removed from the transaction pool, when the block +is built. + +You may also take a look at the [`generate_bridge_reject_obsolete_headers_and_messages`](../../bin/runtime-common/src/lib.rs) +macro that bundles several similar signed extensions in a single one. + +## GRANDPA Finality Relay + +We have an offchain actor, who is watching for GRANDPA justifications and submits them to the bridged chain. +It is the finality relay - you may look at the [crate level documentation and the code](../../relays/finality/). diff --git a/modules/grandpa/src/extension.rs b/modules/grandpa/src/extension.rs index c0f02da751e..87075ee5d58 100644 --- a/modules/grandpa/src/extension.rs +++ b/modules/grandpa/src/extension.rs @@ -40,7 +40,7 @@ impl< let best_finalized = crate::BestFinalized::::get(); let best_finalized_number = match best_finalized { - Some((best_finalized_number, _)) => best_finalized_number, + Some(best_finalized_id) => best_finalized_id.number(), None => return InvalidTransaction::Call.into(), }; @@ -66,6 +66,7 @@ mod tests { mock::{run_test, test_header, RuntimeCall, TestNumber, TestRuntime}, BestFinalized, }; + use bp_runtime::HeaderId; use bp_test_utils::make_default_justification; fn validate_block_submit(num: TestNumber) -> bool { @@ -81,7 +82,7 @@ mod tests { fn sync_to_header_10() { let header10_hash = sp_core::H256::default(); - BestFinalized::::put((10, header10_hash)); + BestFinalized::::put(HeaderId(10, header10_hash)); } #[test] diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index 2402b2512e2..d3335693adf 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -38,13 +38,13 @@ use storage_types::StoredAuthoritySet; -use bp_header_chain::{justification::GrandpaJustification, HeaderChain, InitializationData}; -use bp_runtime::{ - BlockNumberOf, BoundedStorageValue, Chain, HashOf, HasherOf, HeaderOf, OwnedBridgeModule, +use bp_header_chain::{ + justification::GrandpaJustification, HeaderChain, InitializationData, StoredHeaderData, + StoredHeaderDataBuilder, }; +use bp_runtime::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderId, HeaderOf, OwnedBridgeModule}; use finality_grandpa::voter_set::VoterSet; use frame_support::{ensure, fail}; -use frame_system::ensure_signed; use sp_finality_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID}; use sp_runtime::traits::{Header as HeaderT, Zero}; use sp_std::{boxed::Box, convert::TryInto}; @@ -73,13 +73,15 @@ pub type BridgedChain = >::BridgedChain; pub type BridgedBlockNumber = BlockNumberOf<>::BridgedChain>; /// Block hash of the bridged chain. pub type BridgedBlockHash = HashOf<>::BridgedChain>; +/// Block id of the bridged chain. +pub type BridgedBlockId = HeaderId, BridgedBlockNumber>; /// Hasher of the bridged chain. pub type BridgedBlockHasher = HasherOf<>::BridgedChain>; /// Header of the bridged chain. pub type BridgedHeader = HeaderOf<>::BridgedChain>; -/// Stored header of the bridged chain. -pub type StoredBridgedHeader = - BoundedStorageValue<>::MaxBridgedHeaderSize, BridgedHeader>; +/// Header data of the bridged chain that is stored at this chain by this pallet. +pub type BridgedStoredHeaderData = + StoredHeaderData, BridgedBlockHash>; #[frame_support::pallet] pub mod pallet { @@ -115,15 +117,6 @@ pub mod pallet { /// Max number of authorities at the bridged chain. #[pallet::constant] type MaxBridgedAuthorities: Get; - /// Maximal size (in bytes) of the SCALE-encoded bridged chain header. - /// - /// This constant must be selected with care. The pallet requires mandatory headers to be - /// submitted to be able to proceed. Mandatory headers contain public keys of all GRANDPA - /// authorities. E.g. for 1024 authorities, the size of encoded keys will be at least 32 KB. - /// The same header may also contain other digest items as well, so some reserve here - /// is required. - #[pallet::constant] - type MaxBridgedHeaderSize: Get; /// Weights gathered through benchmarking. type WeightInfo: WeightInfo; @@ -157,17 +150,17 @@ pub mod pallet { /// /// If successful in verification, it will write the target header to the underlying storage /// pallet. + #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::submit_finality_proof( justification.commit.precommits.len().try_into().unwrap_or(u32::MAX), justification.votes_ancestries.len().try_into().unwrap_or(u32::MAX), ))] pub fn submit_finality_proof( - origin: OriginFor, + _origin: OriginFor, finality_target: Box>, justification: GrandpaJustification>, ) -> DispatchResultWithPostInfo { Self::ensure_not_halted().map_err(Error::::BridgeModule)?; - let _ = ensure_signed(origin)?; ensure!(Self::request_count() < T::MaxRequests::get(), >::TooManyRequests); @@ -178,11 +171,8 @@ pub mod pallet { finality_target ); - let best_finalized = BestFinalized::::get(); - let best_finalized = - best_finalized.and_then(|(_, hash)| ImportedHeaders::::get(hash)); - let best_finalized = match best_finalized { - Some(best_finalized) => best_finalized, + let best_finalized_number = match BestFinalized::::get() { + Some(best_finalized_id) => best_finalized_id.number(), None => { log::error!( target: LOG_TARGET, @@ -196,7 +186,7 @@ pub mod pallet { // We do a quick check here to ensure that our header chain is making progress and isn't // "travelling back in time" (which could be indicative of something bad, e.g a // hard-fork). - ensure!(best_finalized.number() < number, >::OldHeader); + ensure!(best_finalized_number < *number, >::OldHeader); let authority_set = >::get(); let set_id = authority_set.set_id; @@ -204,20 +194,8 @@ pub mod pallet { let is_authorities_change_enacted = try_enact_authority_change::(&finality_target, set_id)?; - let finality_target = StoredBridgedHeader::::try_from_inner(*finality_target) - .map_err(|e| { - log::error!( - target: LOG_TARGET, - "Size of header {:?} ({}) is larger that the configured value {}", - hash, - e.value_size, - e.maximal_size, - ); - - Error::::TooLargeHeader - })?; >::mutate(|count| *count += 1); - insert_header::(finality_target, hash); + insert_header::(*finality_target, hash); log::info!( target: LOG_TARGET, "Successfully imported finalized header with hash {:?}!", @@ -244,6 +222,7 @@ pub mod pallet { /// This function is only allowed to be called from a trusted origin and writes to storage /// with practically no checks in terms of the validity of the data. It is important that /// you ensure that valid data is being passed in. + #[pallet::call_index(1)] #[pallet::weight((T::DbWeight::get().reads_writes(2, 5), DispatchClass::Operational))] pub fn initialize( origin: OriginFor, @@ -267,6 +246,7 @@ pub mod pallet { /// Change `PalletOwner`. /// /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(2)] #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { >::set_owner(origin, new_owner) @@ -275,6 +255,7 @@ pub mod pallet { /// Halt or resume all pallet operations. /// /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(3)] #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] pub fn set_operating_mode( origin: OriginFor, @@ -302,8 +283,9 @@ pub mod pallet { /// Hash of the best finalized header. #[pallet::storage] + #[pallet::getter(fn best_finalized)] pub type BestFinalized, I: 'static = ()> = - StorageValue<_, (BridgedBlockNumber, BridgedBlockHash), OptionQuery>; + StorageValue<_, BridgedBlockId, OptionQuery>; /// A ring buffer of imported hashes. Ordered by the insertion time. #[pallet::storage] @@ -315,10 +297,10 @@ pub mod pallet { pub(super) type ImportedHashesPointer, I: 'static = ()> = StorageValue<_, u32, ValueQuery>; - /// Headers which have been imported into the pallet. + /// Relevant fields of imported headers. #[pallet::storage] pub type ImportedHeaders, I: 'static = ()> = - StorageMap<_, Identity, BridgedBlockHash, StoredBridgedHeader>; + StorageMap<_, Identity, BridgedBlockHash, BridgedStoredHeaderData>; /// The current GRANDPA Authority set. #[pallet::storage] @@ -394,8 +376,6 @@ pub mod pallet { AlreadyInitialized, /// Too many authorities in the set. TooManyAuthoritiesInSet, - /// Too large header. - TooLargeHeader, /// Error generated by the `OwnedBridgeModule` trait. BridgeModule(bp_runtime::OwnedBridgeModuleError), } @@ -489,13 +469,13 @@ pub mod pallet { /// Note this function solely takes care of updating the storage and pruning old entries, /// but does not verify the validity of such import. pub(crate) fn insert_header, I: 'static>( - header: StoredBridgedHeader, + header: BridgedHeader, hash: BridgedBlockHash, ) { let index = >::get(); let pruning = >::try_get(index); - >::put((*header.number(), hash)); - >::insert(hash, header); + >::put(HeaderId(*header.number(), hash)); + >::insert(hash, header.build()); >::insert(index, hash); // Update ring buffer pointer and remove old header. @@ -526,21 +506,10 @@ pub mod pallet { Error::TooManyAuthoritiesInSet })?; let initial_hash = header.hash(); - let header = StoredBridgedHeader::::try_from_inner(*header).map_err(|e| { - log::error!( - target: LOG_TARGET, - "Failed to initialize bridge. Size of header {:?} ({}) is larger that the configured value {}", - initial_hash, - e.value_size, - e.maximal_size, - ); - - Error::::TooLargeHeader - })?; >::put(initial_hash); >::put(0); - insert_header::(header, initial_hash); + insert_header::(*header, initial_hash); >::put(authority_set); @@ -568,25 +537,15 @@ pub mod pallet { Default::default(), ); let hash = header.hash(); - insert_header::( - StoredBridgedHeader::::try_from_inner(header) - .expect("only used from benchmarks; benchmarks are correct; qed"), - hash, - ); + insert_header::(header, hash); } } } impl, I: 'static> Pallet { - /// Get the best finalized header the pallet knows of. - pub fn best_finalized() -> Option> { - let (_, hash) = >::get()?; - >::get(hash).map(|h| h.into_inner()) - } - - /// Check if a particular header is known to the bridge pallet. - pub fn is_known_header(hash: BridgedBlockHash) -> bool { - >::contains_key(hash) + /// Get the best finalized block number. + pub fn best_finalized_number() -> Option> { + BestFinalized::::get().map(|id| id.number()) } } @@ -594,8 +553,10 @@ impl, I: 'static> Pallet { pub type GrandpaChainHeaders = Pallet; impl, I: 'static> HeaderChain> for GrandpaChainHeaders { - fn finalized_header(hash: HashOf>) -> Option>> { - ImportedHeaders::::get(hash).map(|h| h.into_inner()) + fn finalized_header_state_root( + header_hash: HashOf>, + ) -> Option>> { + ImportedHeaders::::get(header_hash).map(|h| h.state_root) } } @@ -653,8 +614,9 @@ mod tests { use super::*; use crate::mock::{ run_test, test_header, RuntimeOrigin, TestHeader, TestNumber, TestRuntime, - MAX_BRIDGED_AUTHORITIES, MAX_HEADER_SIZE, + MAX_BRIDGED_AUTHORITIES, }; + use bp_header_chain::BridgeGrandpaCall; use bp_runtime::BasicOperatingMode; use bp_test_utils::{ authority_list, generate_owned_bridge_module_tests, make_default_justification, @@ -742,10 +704,6 @@ mod tests { Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] } } - fn large_digest() -> Digest { - Digest { logs: vec![DigestItem::Other(vec![42; MAX_HEADER_SIZE as _])] } - } - #[test] fn init_root_or_owner_origin_can_initialize_pallet() { run_test(|| { @@ -808,25 +766,6 @@ mod tests { }); } - #[test] - fn init_fails_if_header_is_too_large() { - run_test(|| { - let mut genesis = test_header(0); - genesis.digest = large_digest(); - let init_data = InitializationData { - header: Box::new(genesis), - authority_list: authority_list(), - set_id: 1, - operating_mode: BasicOperatingMode::Normal, - }; - - assert_noop!( - Pallet::::initialize(RuntimeOrigin::root(), init_data), - Error::::TooLargeHeader, - ); - }); - } - #[test] fn pallet_rejects_transactions_if_halted() { run_test(|| { @@ -1073,31 +1012,6 @@ mod tests { }); } - #[test] - fn importing_header_rejects_header_if_it_is_too_large() { - run_test(|| { - initialize_substrate_bridge(); - - // Need to update the header digest to indicate that our header signals an authority set - // change. However, the change doesn't happen until the next block. - let mut header = test_header(2); - header.digest = large_digest(); - - // Create a valid justification for the header - let justification = make_default_justification(&header); - - // Should not be allowed to import this header - assert_err!( - Pallet::::submit_finality_proof( - RuntimeOrigin::signed(1), - Box::new(header), - justification - ), - >::TooLargeHeader - ); - }); - } - #[test] fn parse_finalized_storage_proof_rejects_proof_on_unknown_header() { run_test(|| { @@ -1121,11 +1035,8 @@ mod tests { header.set_state_root(state_root); let hash = header.hash(); - >::put((2, hash)); - >::insert( - hash, - StoredBridgedHeader::::try_from_inner(header).unwrap(), - ); + >::put(HeaderId(2, hash)); + >::insert(hash, header.build()); assert_ok!( Pallet::::parse_finalized_storage_proof(hash, storage_proof, |_| (),), @@ -1220,7 +1131,7 @@ mod tests { run_test(|| { initialize_substrate_bridge(); assert_ok!(submit_finality_proof(1)); - let first_header = Pallet::::best_finalized().unwrap(); + let first_header_hash = Pallet::::best_finalized().unwrap().hash(); next_block(); assert_ok!(submit_finality_proof(2)); @@ -1235,8 +1146,8 @@ mod tests { assert_ok!(submit_finality_proof(6)); assert!( - !Pallet::::is_known_header(first_header.hash()), - "First header should be pruned." + !ImportedHeaders::::contains_key(first_header_hash), + "First header should be pruned.", ); }) } @@ -1254,5 +1165,33 @@ mod tests { ); } + #[test] + fn test_bridge_grandpa_call_is_correctly_defined() { + let header = test_header(0); + let init_data = InitializationData { + header: Box::new(header.clone()), + authority_list: authority_list(), + set_id: 1, + operating_mode: BasicOperatingMode::Normal, + }; + let justification = make_default_justification(&header); + + let direct_initialize_call = + Call::::initialize { init_data: init_data.clone() }; + let indirect_initialize_call = BridgeGrandpaCall::::initialize(init_data); + assert_eq!(direct_initialize_call.encode(), indirect_initialize_call.encode()); + + let direct_submit_finality_proof_call = Call::::submit_finality_proof { + finality_target: Box::new(header.clone()), + justification: justification.clone(), + }; + let indirect_submit_finality_proof_call = + BridgeGrandpaCall::::submit_finality_proof(Box::new(header), justification); + assert_eq!( + direct_submit_finality_proof_call.encode(), + indirect_submit_finality_proof_call.encode() + ); + } + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); } diff --git a/modules/grandpa/src/mock.rs b/modules/grandpa/src/mock.rs index 0f83bd528d6..ba6f176e823 100644 --- a/modules/grandpa/src/mock.rs +++ b/modules/grandpa/src/mock.rs @@ -18,7 +18,7 @@ #![allow(clippy::from_over_into)] use bp_runtime::Chain; -use frame_support::{construct_runtime, parameter_types, weights::Weight}; +use frame_support::{construct_runtime, parameter_types, traits::ConstU64, weights::Weight}; use sp_core::sr25519::Signature; use sp_runtime::{ testing::{Header, H256}, @@ -34,7 +34,6 @@ type Block = frame_system::mocking::MockBlock; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; pub const MAX_BRIDGED_AUTHORITIES: u32 = 2048; -pub const MAX_HEADER_SIZE: u32 = 65536; use crate as grandpa; @@ -50,7 +49,6 @@ construct_runtime! { } parameter_types! { - pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); @@ -67,7 +65,7 @@ impl frame_system::Config for TestRuntime { type Lookup = IdentityLookup; type Header = Header; type RuntimeEvent = (); - type BlockHashCount = BlockHashCount; + type BlockHashCount = ConstU64<250>; type Version = (); type PalletInfo = PalletInfo; type AccountData = (); @@ -95,7 +93,6 @@ impl grandpa::Config for TestRuntime { type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; type MaxBridgedAuthorities = frame_support::traits::ConstU32; - type MaxBridgedHeaderSize = frame_support::traits::ConstU32; type WeightInfo = (); } diff --git a/modules/grandpa/src/weights.rs b/modules/grandpa/src/weights.rs index 6997713face..d6c3d596d88 100644 --- a/modules/grandpa/src/weights.rs +++ b/modules/grandpa/src/weights.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_bridge_grandpa` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-17, STEPS: 50, REPEAT: 20 +//! DATE: 2022-12-21, STEPS: 50, REPEAT: 20 //! LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled //! CHAIN: Some("dev"), DB CACHE: 1024 @@ -59,10 +59,10 @@ pub trait WeightInfo { pub struct BridgeWeight(PhantomData); impl WeightInfo for BridgeWeight { fn submit_finality_proof(p: u32, v: u32) -> Weight { - Weight::from_ref_time(198_274_668 as u64) - .saturating_add(Weight::from_ref_time(39_830_948 as u64).saturating_mul(p as u64)) - .saturating_add(Weight::from_ref_time(1_535_681 as u64).saturating_mul(v as u64)) - .saturating_add(T::DbWeight::get().reads(7 as u64)) + Weight::from_ref_time(192_130_822 as u64) + .saturating_add(Weight::from_ref_time(39_781_096 as u64).saturating_mul(p as u64)) + .saturating_add(Weight::from_ref_time(1_365_108 as u64).saturating_mul(v as u64)) + .saturating_add(T::DbWeight::get().reads(6 as u64)) .saturating_add(T::DbWeight::get().writes(6 as u64)) } } @@ -70,10 +70,10 @@ impl WeightInfo for BridgeWeight { // For backwards compatibility and tests impl WeightInfo for () { fn submit_finality_proof(p: u32, v: u32) -> Weight { - Weight::from_ref_time(198_274_668 as u64) - .saturating_add(Weight::from_ref_time(39_830_948 as u64).saturating_mul(p as u64)) - .saturating_add(Weight::from_ref_time(1_535_681 as u64).saturating_mul(v as u64)) - .saturating_add(RocksDbWeight::get().reads(7 as u64)) + Weight::from_ref_time(192_130_822 as u64) + .saturating_add(Weight::from_ref_time(39_781_096 as u64).saturating_mul(p as u64)) + .saturating_add(Weight::from_ref_time(1_365_108 as u64).saturating_mul(v as u64)) + .saturating_add(RocksDbWeight::get().reads(6 as u64)) .saturating_add(RocksDbWeight::get().writes(6 as u64)) } } diff --git a/modules/messages/README.md b/modules/messages/README.md index 2dc56296842..0d1ea7fe44a 100644 --- a/modules/messages/README.md +++ b/modules/messages/README.md @@ -1,10 +1,11 @@ -# Messages Module +# Bridge Messages Pallet -The messages module is used to deliver messages from source chain to target chain. Message is +The messages pallet is used to deliver messages from source chain to target chain. Message is (almost) opaque to the module and the final goal is to hand message to the message dispatch mechanism. ## Contents + - [Overview](#overview) - [Message Workflow](#message-workflow) - [Integrating Message Lane Module into Runtime](#integrating-messages-module-into-runtime) @@ -30,13 +31,22 @@ Single message lane may be seen as a transport channel for single application (o mixed). At the same time the module itself never dictates any lane or message rules. In the end, it is the runtime developer who defines what message lane and message mean for this runtime. +In our [Kusama<>Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md) we are using lane +as a channel of communication between two parachains of different relay chains. For example, lane +`[0, 0, 0, 0]` is used for Statemint <> Statemine communications. Other lanes may be used to bridge +another parachains. + ## Message Workflow -The message "appears" when its submitter calls the `send_message()` function of the module. The -submitter specifies the lane that he's willing to use, the message itself and the fee that he's -willing to pay for the message delivery and dispatch. If a message passes all checks, the nonce is -assigned and the message is stored in the module storage. The message is in an "undelivered" state -now. +The pallet is not intended to be used by end users and provides no public calls to send the message. +Instead, it provides runtime-internal method that allows other pallets (or other runtime code) to queue +outbound messages. + +The message "appears" when some runtime code calls the `send_message()` method of the pallet. +The submitter specifies the lane that he's willing to use and the message itself. If some fee must be +paid for sending the message, it must be paid outside of the pallet. If a message passes all checks +(that include, for example, message size check, disabled lane check, ...), the nonce is assigned and the +message is stored in the module storage. The message is in an "undelivered" state now. We assume that there are external, offchain actors, called relayers, that are submitting module related transactions to both target and source chains. The pallet itself has no assumptions about @@ -94,22 +104,23 @@ to change something in this scheme, get back here for detailed information. The messages module supports instances. Every module instance is supposed to bridge this chain and some bridged chain. To bridge with another chain, using another instance is suggested (this -isn't forced anywhere in the code, though). +isn't forced anywhere in the code, though). Keep in mind, that the pallet may be used to build +virtual channels between multiple chains, as we do in our [Polkadot <> Kusama bridge](../../docs/polkadot-kusama-bridge-overview.md). +There, the pallet actually bridges only two parachains - Kusama Bridge Hub and Polkadot +Bridge Hub. However, other Kusama and Polkadot parachains are able to send (XCM) messages to their +Bridge Hubs. The messages will be delivered to the other side of the bridge and routed to the proper +destination parachain within the bridged chain consensus. Message submitters may track message progress by inspecting module events. When Message is accepted, -the `MessageAccepted` event is emitted in the `send_message()` transaction. The event contains both -message lane identifier and nonce that has been assigned to the message. When a message is delivered -to the target chain, the `MessagesDelivered` event is emitted from the -`receive_messages_delivery_proof()` transaction. The `MessagesDelivered` contains the message lane -identifier, inclusive range of delivered message nonces and their single-bit dispatch results. - -Please note that the meaning of the 'dispatch result' is determined by the message dispatcher at -the target chain. For example, in case of immediate call dispatcher it will be the `true` if call -has been successfully dispatched and `false` if it has only been delivered. This simple mechanism -built into the messages module allows building basic bridge applications, which only care whether -their messages have been successfully dispatched or not. More sophisticated applications may use -their own dispatch result delivery mechanism to deliver something larger than single bit. - +the `MessageAccepted` event is emitted. The event contains both message lane identifier and nonce that +has been assigned to the message. When a message is delivered to the target chain, the `MessagesDelivered` +event is emitted from the `receive_messages_delivery_proof()` transaction. The `MessagesDelivered` contains +the message lane identifier and inclusive range of delivered message nonces. + +The pallet provides no means to get the result of message dispatch at the target chain. If that is +required, it must be done outside of the pallet. For example, XCM messages, when dispatched, have +special instructions to send some data back to the sender. Other dispatchers may use similar +mechanism for that. ### How to plug-in Messages Module to Send Messages to the Bridged Chain? The `pallet_bridge_messages::Config` trait has 3 main associated types that are used to work with @@ -138,16 +149,12 @@ messages and you may charge zero fee for such messages. You may have some rate l sent over the lane#3. Or you may just verify the same rules set for all outbound messages - it is all up to the `pallet_bridge_messages::Config::LaneMessageVerifier` implementation. -The last type is the `pallet_bridge_messages::Config::MessageDeliveryAndDispatchPayment`. When all -checks are made and we have decided to accept the message, we're calling the -`pay_delivery_and_dispatch_fee()` callback, passing the corresponding argument of the `send_message` -function. Later, when message delivery is confirmed, we're calling `pay_relayers_rewards()` -callback, passing accounts of relayers and messages that they have delivered. The simplest -implementation of this trait is in the [`instant_payments.rs`](./src/instant_payments.rs) module and -simply calls `Currency::transfer()` when those callbacks are called. So `Currency` units are -transferred between submitter, 'relayers fund' and relayers accounts. Other implementations may use -more or less sophisticated techniques - the whole relayers incentivization scheme is not a part of -the messages module. +The last type is the `pallet_bridge_messages::Config::DeliveryConfirmationPayments`. When confirmation +transaction is received, we call the `pay_reward()` method, passing the range of delivered messages. +You may use the [`pallet-bridge-relayers`](../relayers/) pallet and its +[`DeliveryConfirmationPaymentsAdapter`](../relayers/src/payment_adapter.rs) adapter as a possible +implementation. It allows you to pay fixed reward for relaying the message and some its portion +for confirming delivery. ### I have a Messages Module in my Runtime, but I Want to Reject all Outbound Messages. What shall I do? @@ -159,7 +166,7 @@ all required traits and will simply reject all transactions, related to outbound The `pallet_bridge_messages::Config` trait has 2 main associated types that are used to work with inbound messages. The `pallet_bridge_messages::Config::SourceHeaderChain` defines how we see the -bridged chain as the source or our inbound messages. When relayer sends us a delivery transaction, +bridged chain as the source of our inbound messages. When relayer sends us a delivery transaction, this implementation must be able to parse and verify the proof of messages wrapped in this transaction. Normally, you would reuse the same (configurable) type on all chains that are sending messages to the same bridged chain. @@ -168,8 +175,7 @@ The `pallet_bridge_messages::Config::MessageDispatch` defines a way on how to di messages. Apart from actually dispatching the message, the implementation must return the correct dispatch weight of the message before dispatch is called. -### I have a Messages Module in my Runtime, but I Want to Reject all Inbound Messages. What -shall I do? +### I have a Messages Module in my Runtime, but I Want to Reject all Inbound Messages. What shall I do? You should be looking at the `bp_messages::target_chain::ForbidInboundMessages` structure from the [`bp_messages::target_chain`](../../primitives/messages/src/target_chain.rs) module. It @@ -177,13 +183,10 @@ implements all required traits and will simply reject all transactions, related ### What about other Constants in the Messages Module Configuration Trait? -Message is being stored in the source chain storage until its delivery will be confirmed. After -that, we may safely remove the message from the storage. Lane messages are removed (pruned) when -someone sends a new message using the same lane. So the message submitter pays for that pruning. To -avoid pruning too many messages in a single transaction, there's -`pallet_bridge_messages::Config::MaxMessagesToPruneAtOnce` configuration parameter. We will never prune -more than this number of messages in the single transaction. That said, the value should not be too -big to avoid waste of resources when there are no messages to prune. +Two setttings that are used to check messages in the `send_message()` function. The +`pallet_bridge_messages::Config::ActiveOutboundLanes` is an array of all message lanes, that +may be used to send messages. All messages sent using other lanes are rejected. All messages that have +size above `pallet_bridge_messages::Config::MaximalOutboundPayloadSize` will also be rejected. To be able to reward the relayer for delivering messages, we store a map of message nonces range => identifier of the relayer that has delivered this range at the target chain runtime storage. If a @@ -219,13 +222,9 @@ large maps, at the same time keeping reserve for future source chain upgrades. ## Non-Essential Functionality -Apart from the message related calls, the module exposes a set of auxiliary calls. They fall in two -groups, described in the next two paragraphs. - There may be a special account in every runtime where the messages module is deployed. This -account, named 'module owner', is like a module-level sudo account - he's able to halt all and -result all module operations without requiring runtime upgrade. The module may have no message -owner, but we suggest to use it at least for initial deployment. To calls that are related to this +account, named 'module owner', is like a module-level sudo account - he's able to halt and +resume all module operations without requiring runtime upgrade. Calls that are related to this account are: - `fn set_owner()`: current module owner may call it to transfer "ownership" to another account; - `fn halt_operations()`: the module owner (or sudo account) may call this function to stop all @@ -235,190 +234,9 @@ account are: - `fn resume_operations()`: module owner may call this function to resume bridge operations. The module will resume its regular operations after this call. -Apart from halting and resuming the bridge, the module owner may also tune module configuration -parameters without runtime upgrades. The set of parameters needs to be designed in advance, though. -The module configuration trait has associated `Parameter` type, which may be e.g. enum and represent -a set of parameters that may be updated by the module owner. For example, if your bridge needs to -convert sums between different tokens, you may define a 'conversion rate' parameter and let the -module owner update this parameter when there are significant changes in the rate. The corresponding -module call is `fn update_pallet_parameter()`. - -## Weights of Module Extrinsics - -The main assumptions behind weight formulas is: -- all possible costs are paid in advance by the message submitter; -- whenever possible, relayer tries to minimize cost of its transactions. So e.g. even though sender - always pays for delivering outbound lane state proof, relayer may not include it in the delivery - transaction (unless messages module on target chain requires that); -- weight formula should incentivize relayer to not to submit any redundant data in the extrinsics - arguments; -- the extrinsic shall never be executing slower (i.e. has larger actual weight) than defined by the - formula. - -### Weight of `send_message` call - -#### Related benchmarks - -| Benchmark | Description | -|-----------------------------------|-----------------------------------------------------| -`send_minimal_message_worst_case` | Sends 0-size message with worst possible conditions | -`send_1_kb_message_worst_case` | Sends 1KB-size message with worst possible conditions | -`send_16_kb_message_worst_case` | Sends 16KB-size message with worst possible conditions | - -#### Weight formula - -The weight formula is: -``` -Weight = BaseWeight + MessageSizeInKilobytes * MessageKiloByteSendWeight -``` - -Where: - -| Component | How it is computed? | Description | -|-----------------------------|------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------| -| `SendMessageOverhead` | `send_minimal_message_worst_case` | Weight of sending minimal (0 bytes) message | -| `MessageKiloByteSendWeight` | `(send_16_kb_message_worst_case - send_1_kb_message_worst_case)/15` | Weight of sending every additional kilobyte of the message | - -### Weight of `receive_messages_proof` call - -#### Related benchmarks - -| Benchmark | Description* | -|---------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------| -| `receive_single_message_proof` | Receives proof of single `EXPECTED_DEFAULT_MESSAGE_LENGTH` message | -| `receive_two_messages_proof` | Receives proof of two identical `EXPECTED_DEFAULT_MESSAGE_LENGTH` messages | -| `receive_single_message_proof_with_outbound_lane_state` | Receives proof of single `EXPECTED_DEFAULT_MESSAGE_LENGTH` message and proof of outbound lane state at the source chain | -| `receive_single_message_proof_1_kb` | Receives proof of single message. The proof has size of approximately 1KB** | -| `receive_single_message_proof_16_kb` | Receives proof of single message. The proof has size of approximately 16KB** | - -*\* - In all benchmarks all received messages are dispatched and their dispatch cost is near to zero* - -*\*\* - Trie leafs are assumed to have minimal values. The proof is derived from the minimal proof -by including more trie nodes. That's because according to our additioal benchmarks, increasing proof -by including more nodes has slightly larger impact on performance than increasing values stored in leafs*. - -#### Weight formula - -The weight formula is: -``` -Weight = BaseWeight + OutboundStateDeliveryWeight - + MessagesCount * MessageDeliveryWeight - + MessagesDispatchWeight - + Max(0, ActualProofSize - ExpectedProofSize) * ProofByteDeliveryWeight -``` - -Where: - -| Component | How it is computed? | Description | -|-------------------------------|------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `BaseWeight` | `2*receive_single_message_proof - receive_two_messages_proof` | Weight of receiving and parsing minimal proof | -| `OutboundStateDeliveryWeight` | `receive_single_message_proof_with_outbound_lane_state - receive_single_message_proof` | Additional weight when proof includes outbound lane state | -| `MessageDeliveryWeight` | `receive_two_messages_proof - receive_single_message_proof` | Weight of of parsing and dispatching (without actual dispatch cost) of every message | -| `MessagesCount` | | Provided by relayer | -| `MessagesDispatchWeight` | | Provided by relayer | -| `ActualProofSize` | | Provided by relayer | -| `ExpectedProofSize` | `EXPECTED_DEFAULT_MESSAGE_LENGTH * MessagesCount + EXTRA_STORAGE_PROOF_SIZE` | Size of proof that we are expecting. This only includes `EXTRA_STORAGE_PROOF_SIZE` once, because we assume that intermediate nodes likely to be included in the proof only once. This may be wrong, but since weight of processing proof with many nodes is almost equal to processing proof with large leafs, additional cost will be covered because we're charging for extra proof bytes anyway | -| `ProofByteDeliveryWeight` | `(receive_single_message_proof_16_kb - receive_single_message_proof_1_kb) / (15 * 1024)` | Weight of processing every additional proof byte over `ExpectedProofSize` limit | - -#### Why for every message sent using `send_message` we will be able to craft `receive_messages_proof` transaction? - -We have following checks in `send_message` transaction on the source chain: -- message size should be less than or equal to `2/3` of maximal extrinsic size on the target chain; -- message dispatch weight should be less than or equal to the `1/2` of maximal extrinsic dispatch - weight on the target chain. - -Delivery transaction is an encoded delivery call and signed extensions. So we have `1/3` of maximal -extrinsic size reserved for: -- storage proof, excluding the message itself. Currently, on our test chains, the overhead is always - within `EXTRA_STORAGE_PROOF_SIZE` limits (1024 bytes); -- signed extras and other call arguments (`relayer_id: SourceChain::AccountId`, `messages_count: - u32`, `dispatch_weight: u64`). - -On Millau chain, maximal extrinsic size is `0.75 * 2MB`, so `1/3` is `512KB` (`524_288` bytes). This -should be enough to cover these extra arguments and signed extensions. - -Let's exclude message dispatch cost from single message delivery transaction weight formula: -``` -Weight = BaseWeight + OutboundStateDeliveryWeight + MessageDeliveryWeight - + Max(0, ActualProofSize - ExpectedProofSize) * ProofByteDeliveryWeight -``` - -So we have `1/2` of maximal extrinsic weight to cover these components. `BaseWeight`, -`OutboundStateDeliveryWeight` and `MessageDeliveryWeight` are determined using benchmarks and are -hardcoded into runtime. Adequate relayer would only include required trie nodes into the proof. So -if message size would be maximal (`2/3` of `MaximalExtrinsicSize`), then the extra proof size would -be `MaximalExtrinsicSize / 3 * 2 - EXPECTED_DEFAULT_MESSAGE_LENGTH`. - -Both conditions are verified by `pallet_bridge_messages::ensure_weights_are_correct` and -`pallet_bridge_messages::ensure_able_to_receive_messages` functions, which must be called from every -runtime's tests. - -#### Post-dispatch weight refunds of the `receive_messages_proof` call - -Weight formula of the `receive_messages_proof` call assumes that the dispatch fee of every message is -paid at the target chain (where call is executed), that every message will be dispatched and that -dispatch weight of the message will be exactly the weight that is returned from the -`MessageDispatch::dispatch_weight` method call. This isn't true for all messages, so the call returns -actual weight used to dispatch messages. - -This actual weight is the weight, returned by the weight formula, minus: -- the weight of undispatched messages, if we have failed to dispatch because of different issues; -- the unspent dispatch weight if the declared weight of some messages is less than their actual post-dispatch weight; -- the pay-dispatch-fee weight for every message that had dispatch fee paid at the source chain. - -The last component is computed as a difference between two benchmarks results - the `receive_single_message_proof` -benchmark (that assumes that the fee is paid during dispatch) and the `receive_single_prepaid_message_proof` -(that assumes that the dispatch fee is already paid). - -### Weight of `receive_messages_delivery_proof` call - -#### Related benchmarks - -| Benchmark | Description | -|-------------------------------------------------------------|------------------------------------------------------------------------------------------| -| `receive_delivery_proof_for_single_message` | Receives proof of single message delivery | -| `receive_delivery_proof_for_two_messages_by_single_relayer` | Receives proof of two messages delivery. Both messages are delivered by the same relayer | -| `receive_delivery_proof_for_two_messages_by_two_relayers` | Receives proof of two messages delivery. Messages are delivered by different relayers | - -#### Weight formula - -The weight formula is: -``` -Weight = BaseWeight + MessagesCount * MessageConfirmationWeight - + RelayersCount * RelayerRewardWeight - + Max(0, ActualProofSize - ExpectedProofSize) * ProofByteDeliveryWeight - + MessagesCount * (DbReadWeight + DbWriteWeight) -``` - -Where: - -| Component | How it is computed? | Description | -|---------------------------|-----------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `BaseWeight` | `2*receive_delivery_proof_for_single_message - receive_delivery_proof_for_two_messages_by_single_relayer` | Weight of receiving and parsing minimal delivery proof | -| `MessageDeliveryWeight` | `receive_delivery_proof_for_two_messages_by_single_relayer - receive_delivery_proof_for_single_message` | Weight of confirming every additional message | -| `MessagesCount` | | Provided by relayer | -| `RelayerRewardWeight` | `receive_delivery_proof_for_two_messages_by_two_relayers - receive_delivery_proof_for_two_messages_by_single_relayer` | Weight of rewarding every additional relayer | -| `RelayersCount` | | Provided by relayer | -| `ActualProofSize` | | Provided by relayer | -| `ExpectedProofSize` | `EXTRA_STORAGE_PROOF_SIZE` | Size of proof that we are expecting | -| `ProofByteDeliveryWeight` | `(receive_single_message_proof_16_kb - receive_single_message_proof_1_kb) / (15 * 1024)` | Weight of processing every additional proof byte over `ExpectedProofSize` limit. We're using the same formula, as for message delivery, because proof mechanism is assumed to be the same in both cases | - -#### Post-dispatch weight refunds of the `receive_messages_delivery_proof` call - -Weight formula of the `receive_messages_delivery_proof` call assumes that all messages in the proof -are actually delivered (so there are no already confirmed messages) and every messages is processed -by the `OnDeliveryConfirmed` callback. This means that for every message, we're adding single db read -weight and single db write weight. If, by some reason, messages are not processed by the -`OnDeliveryConfirmed` callback, or their processing is faster than that additional weight, the -difference is refunded to the submitter. - -#### Why we're always able to craft `receive_messages_delivery_proof` transaction? - -There can be at most `::MaxUnconfirmedMessagesAtInboundLane` -messages and at most -`::MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded -relayers in the single delivery confirmation transaction. - -We're checking that this transaction may be crafted in the -`pallet_bridge_messages::ensure_able_to_receive_confirmation` function, which must be called from every -runtime' tests. +If pallet owner is not defined, the governance may be used to make those calls. + +## Messages Relay + +We have an offchain actor, who is watching for new messages and submits them to the bridged chain. +It is the messages relay - you may look at the [crate level documentation and the code](../../relays/messages/). diff --git a/modules/messages/src/benchmarking.rs b/modules/messages/src/benchmarking.rs index b8360facacb..62dd1d60caf 100644 --- a/modules/messages/src/benchmarking.rs +++ b/modules/messages/src/benchmarking.rs @@ -69,6 +69,8 @@ pub trait Config: crate::Config { } /// Return id of relayer account at the bridged chain. fn bridged_relayer_id() -> Self::InboundRelayer; + /// Returns true if given relayer has been rewarded for some of its actions. + fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool; /// Create given account and give it enough balance for test purposes. fn endow_account(account: &Self::AccountId); /// Prepare messages proof to receive by the module. @@ -256,39 +258,6 @@ benchmarks_instance_pallet! { assert!(T::is_message_dispatched(21)); } - // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: - // * proof does not include outbound lane state proof; - // * inbound lane already has state, so it needs to be read and decoded; - // * message is successfully dispatched; - // * message requires all heavy checks done by dispatcher; - // * message dispatch fee is paid at source (bridged) chain. - // - // This benchmark is used to compute extra weight spent at target chain when fee is paid there. Then we use - // this information in two places: (1) to reduce weight of delivery tx if sender pays fee at the source chain - // and (2) to refund relayer with this weight if fee has been paid at the source chain. - receive_single_prepaid_message_proof { - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - T::endow_account(&relayer_id_on_target); - - // mark messages 1..=20 as delivered - receive_messages::(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=21, - outbound_lane_data: None, - size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), - }); - }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) - verify { - assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - 21, - ); - assert!(T::is_message_dispatched(21)); - } - // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: // * single relayer is rewarded for relaying single message; // * relayer account does not exist (in practice it needs to exist in production environment). @@ -320,6 +289,7 @@ benchmarks_instance_pallet! { }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) verify { assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 1); + assert!(T::is_relayer_rewarded(&relayer_id)); } // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: @@ -358,6 +328,7 @@ benchmarks_instance_pallet! { }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) verify { assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); + assert!(T::is_relayer_rewarded(&relayer_id)); } // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: @@ -401,6 +372,8 @@ benchmarks_instance_pallet! { }: receive_messages_delivery_proof(RawOrigin::Signed(relayer1_id.clone()), proof, relayers_state) verify { assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); + assert!(T::is_relayer_rewarded(&relayer1_id)); + assert!(T::is_relayer_rewarded(&relayer2_id)); } } diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index 0df6c686cc6..680518d9dc8 100644 --- a/modules/messages/src/lib.rs +++ b/modules/messages/src/lib.rs @@ -52,24 +52,21 @@ use crate::{ use bp_messages::{ source_chain::{ - LaneMessageVerifier, MessageDeliveryAndDispatchPayment, RelayersRewards, - SendMessageArtifacts, TargetHeaderChain, + DeliveryConfirmationPayments, LaneMessageVerifier, SendMessageArtifacts, TargetHeaderChain, }, target_chain::{ - DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain, + DeliveryPayments, DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, + SourceHeaderChain, }, total_unrewarded_messages, DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId, MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData, - OutboundMessageDetails, UnrewardedRelayer, UnrewardedRelayersState, + OutboundMessageDetails, UnrewardedRelayersState, }; use bp_runtime::{BasicOperatingMode, ChainId, OwnedBridgeModule, Size}; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get}; use sp_runtime::traits::UniqueSaturatedFrom; -use sp_std::{ - cell::RefCell, collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive, - prelude::*, -}; +use sp_std::{cell::RefCell, marker::PhantomData, prelude::*}; mod inbound_lane; mod outbound_lane; @@ -146,6 +143,8 @@ pub mod pallet { /// Identifier of relayer that deliver messages to this chain. Relayer reward is paid on the /// bridged chain. type InboundRelayer: Parameter + MaxEncodedLen; + /// Delivery payments. + type DeliveryPayments: DeliveryPayments; // Types that are used by outbound_lane (on source chain). @@ -153,11 +152,8 @@ pub mod pallet { type TargetHeaderChain: TargetHeaderChain; /// Message payload verifier. type LaneMessageVerifier: LaneMessageVerifier; - /// Message delivery payment. - type MessageDeliveryAndDispatchPayment: MessageDeliveryAndDispatchPayment< - Self::RuntimeOrigin, - Self::AccountId, - >; + /// Delivery confirmation payments. + type DeliveryConfirmationPayments: DeliveryConfirmationPayments; // Types that are used by inbound_lane (on target chain). @@ -171,10 +167,10 @@ pub mod pallet { } /// Shortcut to messages proof type for Config. - type MessagesProofOf = + pub type MessagesProofOf = <>::SourceHeaderChain as SourceHeaderChain>::MessagesProof; /// Shortcut to messages delivery proof type for Config. - type MessagesDeliveryProofOf = + pub type MessagesDeliveryProofOf = <>::TargetHeaderChain as TargetHeaderChain< >::OutboundPayload, ::AccountId, @@ -230,6 +226,7 @@ pub mod pallet { /// Change `PalletOwner`. /// /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(0)] #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { >::set_owner(origin, new_owner) @@ -238,6 +235,7 @@ pub mod pallet { /// Halt or resume all/some pallet operations. /// /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(1)] #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] pub fn set_operating_mode( origin: OriginFor, @@ -251,6 +249,7 @@ pub mod pallet { /// The weight of the call assumes that the transaction always brings outbound lane /// state update. Because of that, the submitter (relayer) has no benefit of not including /// this data in the transaction, so reward confirmations lags should be minimal. + #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight))] pub fn receive_messages_proof( origin: OriginFor, @@ -361,40 +360,36 @@ pub mod pallet { // losing funds for messages dispatch. But keep in mind that relayer pays base // delivery transaction cost anyway. And base cost covers everything except // dispatch, so we have a balance here. - let (unspent_weight, refund_pay_dispatch_fee) = match &receival_result { + let unspent_weight = match &receival_result { ReceivalResult::Dispatched(dispatch_result) => { valid_messages += 1; - ( - dispatch_result.unspent_weight, - !dispatch_result.dispatch_fee_paid_during_dispatch, - ) + dispatch_result.unspent_weight }, ReceivalResult::InvalidNonce | ReceivalResult::TooManyUnrewardedRelayers | - ReceivalResult::TooManyUnconfirmedMessages => (message_dispatch_weight, true), + ReceivalResult::TooManyUnconfirmedMessages => message_dispatch_weight, }; lane_messages_received_status.push(message.key.nonce, receival_result); let unspent_weight = unspent_weight.min(message_dispatch_weight); dispatch_weight_left -= message_dispatch_weight - unspent_weight; - actual_weight = actual_weight.saturating_sub(unspent_weight).saturating_sub( - // delivery call weight formula assumes that the fee is paid at - // this (target) chain. If the message is prepaid at the source - // chain, let's refund relayer with this extra cost. - if refund_pay_dispatch_fee { - T::WeightInfo::pay_inbound_dispatch_fee_overhead() - } else { - Weight::zero() - }, - ); + actual_weight = actual_weight.saturating_sub(unspent_weight); } messages_received_status.push(lane_messages_received_status); } + // let's now deal with relayer payments + T::DeliveryPayments::pay_reward( + relayer_id_at_this_chain, + total_messages, + valid_messages, + actual_weight, + ); + log::debug!( target: LOG_TARGET, - "Received messages: total={}, valid={}. Weight used: {}/{}", + "Received messages: total={}, valid={}. Weight used: {}/{}.", total_messages, valid_messages, actual_weight, @@ -403,10 +398,11 @@ pub mod pallet { Self::deposit_event(Event::MessagesReceived(messages_received_status)); - Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::No }) } /// Receive messages delivery proof from bridged chain. + #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::receive_messages_delivery_proof_weight( proof, relayers_state, @@ -490,7 +486,7 @@ pub mod pallet { }); // if some new messages have been confirmed, reward relayers - >::MessageDeliveryAndDispatchPayment::pay_relayers_rewards( + T::DeliveryConfirmationPayments::pay_reward( lane_id, lane_data.relayers, &confirmation_relayer, @@ -646,6 +642,11 @@ pub mod pallet { dispatch_weight: T::MessageDispatch::dispatch_weight(&mut dispatch_message), } } + + /// Return inbound lane data. + pub fn inbound_lane_data(lane: LaneId) -> InboundLaneData { + InboundLanes::::get(lane).0 + } } } @@ -737,31 +738,6 @@ fn send_message, I: 'static>( Ok(SendMessageArtifacts { nonce, weight: actual_weight }) } -/// Calculate the number of messages that the relayers have delivered. -pub fn calc_relayers_rewards( - messages_relayers: VecDeque>, - received_range: &RangeInclusive, -) -> RelayersRewards -where - T: frame_system::Config + crate::Config, - I: 'static, -{ - // remember to reward relayers that have delivered messages - // this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain - let mut relayers_rewards = RelayersRewards::new(); - for entry in messages_relayers { - let nonce_begin = sp_std::cmp::max(entry.messages.begin, *received_range.start()); - let nonce_end = sp_std::cmp::min(entry.messages.end, *received_range.end()); - - // loop won't proceed if current entry is ahead of received range (begin > end). - // this loop is bound by `T::MaxUnconfirmedMessagesAtInboundLane` on the bridged chain - if nonce_end >= nonce_begin { - *relayers_rewards.entry(entry.relayer).or_default() += nonce_end - nonce_begin + 1; - } - } - relayers_rewards -} - /// Ensure that the pallet is in normal operational mode. fn ensure_normal_operating_mode, I: 'static>() -> Result<(), Error> { if PalletOperatingMode::::get() == @@ -913,13 +889,13 @@ fn verify_and_decode_messages_proof::get(TEST_LANE_ID).0.last_delivered_nonce(), 1); + + assert!(TestDeliveryPayments::is_reward_paid(1)); }); } @@ -1361,8 +1339,8 @@ mod tests { ..Default::default() }, )); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 1)); - assert!(!TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_B, 1)); + assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); + assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); // this reports delivery of both message 1 and message 2 => reward is paid only to // TEST_RELAYER_B @@ -1387,8 +1365,8 @@ mod tests { ..Default::default() }, )); - assert!(!TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_A, 1)); - assert!(TestMessageDeliveryAndDispatchPayment::is_reward_paid(TEST_RELAYER_B, 1)); + assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); + assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); }); } @@ -1554,11 +1532,9 @@ mod tests { fn submit_with_unspent_weight( nonce: MessageNonce, unspent_weight: u64, - is_prepaid: bool, ) -> (Weight, Weight) { let mut payload = REGULAR_PAYLOAD; *payload.dispatch_result.unspent_weight.ref_time_mut() = unspent_weight; - payload.dispatch_result.dispatch_fee_paid_during_dispatch = !is_prepaid; let proof = Ok(vec![message(nonce, payload)]).into(); let messages_count = 1; let pre_dispatch_weight = @@ -1582,40 +1558,32 @@ mod tests { } // when dispatch is returning `unspent_weight < declared_weight` - let (pre, post) = submit_with_unspent_weight(1, 1, false); + let (pre, post) = submit_with_unspent_weight(1, 1); assert_eq!(post.ref_time(), pre.ref_time() - 1); // when dispatch is returning `unspent_weight = declared_weight` let (pre, post) = - submit_with_unspent_weight(2, REGULAR_PAYLOAD.declared_weight.ref_time(), false); + submit_with_unspent_weight(2, REGULAR_PAYLOAD.declared_weight.ref_time()); assert_eq!( post.ref_time(), pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time() ); // when dispatch is returning `unspent_weight > declared_weight` - let (pre, post) = submit_with_unspent_weight( - 3, - REGULAR_PAYLOAD.declared_weight.ref_time() + 1, - false, - ); + let (pre, post) = + submit_with_unspent_weight(3, REGULAR_PAYLOAD.declared_weight.ref_time() + 1); assert_eq!( post.ref_time(), pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time() ); // when there's no unspent weight - let (pre, post) = submit_with_unspent_weight(4, 0, false); + let (pre, post) = submit_with_unspent_weight(4, 0); assert_eq!(post, pre); - // when dispatch is returning `unspent_weight < declared_weight` AND message is prepaid - let (pre, post) = submit_with_unspent_weight(5, 1, true); - assert_eq!( - post.ref_time(), - pre.ref_time() - - 1 - ::WeightInfo::pay_inbound_dispatch_fee_overhead() - .ref_time() - ); + // when dispatch is returning `unspent_weight < declared_weight` + let (pre, post) = submit_with_unspent_weight(5, 1); + assert_eq!(post.ref_time(), pre.ref_time() - 1); }); } @@ -1927,6 +1895,69 @@ mod tests { }); } + #[test] + fn test_bridge_messages_call_is_correctly_defined() { + let account_id = 1; + let message_proof: TestMessagesProof = Ok(vec![message(1, REGULAR_PAYLOAD)]).into(); + let message_delivery_proof = TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 1, + relayers: vec![UnrewardedRelayer { + relayer: 0, + messages: DeliveredMessages::new(1), + }] + .into_iter() + .collect(), + }, + ))); + let unrewarded_relayer_state = UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + last_delivered_nonce: 1, + ..Default::default() + }; + + let direct_receive_messages_proof_call = Call::::receive_messages_proof { + relayer_id_at_bridged_chain: account_id, + proof: message_proof.clone(), + messages_count: 1, + dispatch_weight: REGULAR_PAYLOAD.declared_weight, + }; + let indirect_receive_messages_proof_call = BridgeMessagesCall::< + AccountId, + TestMessagesProof, + TestMessagesDeliveryProof, + >::receive_messages_proof( + account_id, + message_proof, + 1, + REGULAR_PAYLOAD.declared_weight, + ); + assert_eq!( + direct_receive_messages_proof_call.encode(), + indirect_receive_messages_proof_call.encode() + ); + + let direct_receive_messages_delivery_proof_call = + Call::::receive_messages_delivery_proof { + proof: message_delivery_proof.clone(), + relayers_state: unrewarded_relayer_state.clone(), + }; + let indirect_receive_messages_delivery_proof_call = BridgeMessagesCall::< + AccountId, + TestMessagesProof, + TestMessagesDeliveryProof, + >::receive_messages_delivery_proof( + message_delivery_proof, + unrewarded_relayer_state, + ); + assert_eq!( + direct_receive_messages_delivery_proof_call.encode(), + indirect_receive_messages_delivery_proof_call.encode() + ); + } + generate_owned_bridge_module_tests!( MessagesOperatingMode::Basic(BasicOperatingMode::Normal), MessagesOperatingMode::Basic(BasicOperatingMode::Halted) diff --git a/modules/messages/src/mock.rs b/modules/messages/src/mock.rs index 36c38677e53..c628ab2cbb3 100644 --- a/modules/messages/src/mock.rs +++ b/modules/messages/src/mock.rs @@ -17,13 +17,14 @@ // From construct_runtime macro #![allow(clippy::from_over_into)] -use crate::{calc_relayers_rewards, Config}; +use crate::Config; use bp_messages::{ - source_chain::{LaneMessageVerifier, MessageDeliveryAndDispatchPayment, TargetHeaderChain}, + calc_relayers_rewards, + source_chain::{DeliveryConfirmationPayments, LaneMessageVerifier, TargetHeaderChain}, target_chain::{ - DispatchMessage, DispatchMessageData, MessageDispatch, ProvedLaneMessages, ProvedMessages, - SourceHeaderChain, + DeliveryPayments, DispatchMessage, DispatchMessageData, MessageDispatch, + ProvedLaneMessages, ProvedMessages, SourceHeaderChain, }, DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, UnrewardedRelayer, @@ -32,6 +33,7 @@ use bp_runtime::{messages::MessageDispatchResult, Size}; use codec::{Decode, Encode}; use frame_support::{ parameter_types, + traits::ConstU64, weights::{RuntimeDbWeight, Weight}, }; use scale_info::TypeInfo; @@ -104,7 +106,7 @@ impl frame_system::Config for TestRuntime { type Lookup = IdentityLookup; type Header = SubstrateHeader; type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; + type BlockHashCount = ConstU64<250>; type Version = (); type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; @@ -120,16 +122,12 @@ impl frame_system::Config for TestRuntime { type MaxConsumers = frame_support::traits::ConstU32<16>; } -parameter_types! { - pub const ExistentialDeposit: u64 = 1; -} - impl pallet_balances::Config for TestRuntime { type MaxLocks = (); type Balance = Balance; type DustRemoval = (); type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ExistentialDeposit; + type ExistentialDeposit = ConstU64<1>; type AccountStore = frame_system::Pallet; type WeightInfo = (); type MaxReserves = (); @@ -156,10 +154,11 @@ impl Config for TestRuntime { type InboundPayload = TestPayload; type InboundRelayer = TestRelayer; + type DeliveryPayments = TestDeliveryPayments; type TargetHeaderChain = TestTargetHeaderChain; type LaneMessageVerifier = TestLaneMessageVerifier; - type MessageDeliveryAndDispatchPayment = TestMessageDeliveryAndDispatchPayment; + type DeliveryConfirmationPayments = TestDeliveryConfirmationPayments; type SourceHeaderChain = TestSourceHeaderChain; type MessageDispatch = TestMessageDispatch; @@ -191,13 +190,13 @@ pub const TEST_RELAYER_C: AccountId = 102; pub const TEST_ERROR: &str = "Test error"; /// Lane that we're using in tests. -pub const TEST_LANE_ID: LaneId = [0, 0, 0, 1]; +pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 1]); /// Secondary lane that we're using in tests. -pub const TEST_LANE_ID_2: LaneId = [0, 0, 0, 2]; +pub const TEST_LANE_ID_2: LaneId = LaneId([0, 0, 0, 2]); /// Inactive outbound lane. -pub const TEST_LANE_ID_3: LaneId = [0, 0, 0, 3]; +pub const TEST_LANE_ID_3: LaneId = LaneId([0, 0, 0, 3]); /// Regular message payload. pub const REGULAR_PAYLOAD: TestPayload = message_payload(0, 50); @@ -290,11 +289,38 @@ impl LaneMessageVerifier for TestLaneMessageVerifier } } -/// Message fee payment system that is used in tests. +/// Reward payments at the target chain during delivery transaction. +#[derive(Debug, Default)] +pub struct TestDeliveryPayments; + +impl TestDeliveryPayments { + /// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is + /// cleared after the call. + pub fn is_reward_paid(relayer: AccountId) -> bool { + let key = (b":delivery-relayer-reward:", relayer).encode(); + frame_support::storage::unhashed::take::(&key).is_some() + } +} + +impl DeliveryPayments for TestDeliveryPayments { + type Error = &'static str; + + fn pay_reward( + relayer: AccountId, + _total_messages: MessageNonce, + _valid_messages: MessageNonce, + _actual_weight: Weight, + ) { + let key = (b":delivery-relayer-reward:", relayer).encode(); + frame_support::storage::unhashed::put(&key, &true); + } +} + +/// Reward payments at the source chain during delivery confirmation transaction. #[derive(Debug, Default)] -pub struct TestMessageDeliveryAndDispatchPayment; +pub struct TestDeliveryConfirmationPayments; -impl TestMessageDeliveryAndDispatchPayment { +impl TestDeliveryConfirmationPayments { /// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is /// cleared after the call. pub fn is_reward_paid(relayer: AccountId, fee: TestMessageFee) -> bool { @@ -303,19 +329,16 @@ impl TestMessageDeliveryAndDispatchPayment { } } -impl MessageDeliveryAndDispatchPayment - for TestMessageDeliveryAndDispatchPayment -{ +impl DeliveryConfirmationPayments for TestDeliveryConfirmationPayments { type Error = &'static str; - fn pay_relayers_rewards( + fn pay_reward( _lane_id: LaneId, - message_relayers: VecDeque>, + messages_relayers: VecDeque>, _confirmation_relayer: &AccountId, received_range: &RangeInclusive, ) { - let relayers_rewards = - calc_relayers_rewards::(message_relayers, received_range); + let relayers_rewards = calc_relayers_rewards(messages_relayers, received_range); for (relayer, reward) in &relayers_rewards { let key = (b":relayer-reward:", relayer, reward).encode(); frame_support::storage::unhashed::put(&key, &true); @@ -398,7 +421,6 @@ pub const fn dispatch_result( ) -> MessageDispatchResult { MessageDispatchResult { unspent_weight: Weight::from_ref_time(unspent_weight), - dispatch_fee_paid_during_dispatch: true, dispatch_level_result: (), } } diff --git a/modules/messages/src/weights.rs b/modules/messages/src/weights.rs index 2ae60c17faf..344b9587d68 100644 --- a/modules/messages/src/weights.rs +++ b/modules/messages/src/weights.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-17, STEPS: 50, REPEAT: 20 +//! DATE: 2023-01-12, STEPS: 50, REPEAT: 20 //! LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled //! CHAIN: Some("dev"), DB CACHE: 1024 @@ -55,7 +55,6 @@ pub trait WeightInfo { fn receive_single_message_proof_with_outbound_lane_state() -> Weight; fn receive_single_message_proof_1_kb() -> Weight; fn receive_single_message_proof_16_kb() -> Weight; - fn receive_single_prepaid_message_proof() -> Weight; fn receive_delivery_proof_for_single_message() -> Weight; fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight; fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight; @@ -67,47 +66,42 @@ pub trait WeightInfo { pub struct BridgeWeight(PhantomData); impl WeightInfo for BridgeWeight { fn receive_single_message_proof() -> Weight { - Weight::from_ref_time(50_596_000 as u64) + Weight::from_ref_time(95_401_000 as u64) .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } fn receive_two_messages_proof() -> Weight { - Weight::from_ref_time(77_041_000 as u64) + Weight::from_ref_time(127_794_000 as u64) .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - Weight::from_ref_time(58_331_000 as u64) + Weight::from_ref_time(105_698_000 as u64) .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } fn receive_single_message_proof_1_kb() -> Weight { - Weight::from_ref_time(48_061_000 as u64) + Weight::from_ref_time(92_963_000 as u64) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } fn receive_single_message_proof_16_kb() -> Weight { - Weight::from_ref_time(101_601_000 as u64) + Weight::from_ref_time(158_449_000 as u64) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } - fn receive_single_prepaid_message_proof() -> Weight { - Weight::from_ref_time(49_646_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } fn receive_delivery_proof_for_single_message() -> Weight { - Weight::from_ref_time(55_108_000 as u64) + Weight::from_ref_time(72_085_000 as u64) .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - Weight::from_ref_time(53_917_000 as u64) + Weight::from_ref_time(70_889_000 as u64) .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - Weight::from_ref_time(57_335_000 as u64) + Weight::from_ref_time(78_211_000 as u64) .saturating_add(T::DbWeight::get().reads(5 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } @@ -116,47 +110,42 @@ impl WeightInfo for BridgeWeight { // For backwards compatibility and tests impl WeightInfo for () { fn receive_single_message_proof() -> Weight { - Weight::from_ref_time(50_596_000 as u64) + Weight::from_ref_time(95_401_000 as u64) .saturating_add(RocksDbWeight::get().reads(4 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } fn receive_two_messages_proof() -> Weight { - Weight::from_ref_time(77_041_000 as u64) + Weight::from_ref_time(127_794_000 as u64) .saturating_add(RocksDbWeight::get().reads(4 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - Weight::from_ref_time(58_331_000 as u64) + Weight::from_ref_time(105_698_000 as u64) .saturating_add(RocksDbWeight::get().reads(4 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } fn receive_single_message_proof_1_kb() -> Weight { - Weight::from_ref_time(48_061_000 as u64) + Weight::from_ref_time(92_963_000 as u64) .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } fn receive_single_message_proof_16_kb() -> Weight { - Weight::from_ref_time(101_601_000 as u64) + Weight::from_ref_time(158_449_000 as u64) .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } - fn receive_single_prepaid_message_proof() -> Weight { - Weight::from_ref_time(49_646_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } fn receive_delivery_proof_for_single_message() -> Weight { - Weight::from_ref_time(55_108_000 as u64) + Weight::from_ref_time(72_085_000 as u64) .saturating_add(RocksDbWeight::get().reads(4 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - Weight::from_ref_time(53_917_000 as u64) + Weight::from_ref_time(70_889_000 as u64) .saturating_add(RocksDbWeight::get().reads(4 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - Weight::from_ref_time(57_335_000 as u64) + Weight::from_ref_time(78_211_000 as u64) .saturating_add(RocksDbWeight::get().reads(5 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) } diff --git a/modules/messages/src/weights_ext.rs b/modules/messages/src/weights_ext.rs index 1604832c079..b763ca69024 100644 --- a/modules/messages/src/weights_ext.rs +++ b/modules/messages/src/weights_ext.rs @@ -59,10 +59,7 @@ pub fn ensure_able_to_receive_message( max_incoming_message_proof_size.saturating_add(SIGNED_EXTENSIONS_SIZE); assert!( max_delivery_transaction_size <= max_extrinsic_size, - "Size of maximal message delivery transaction {} + {} is larger than maximal possible transaction size {}", - max_incoming_message_proof_size, - SIGNED_EXTENSIONS_SIZE, - max_extrinsic_size, + "Size of maximal message delivery transaction {max_incoming_message_proof_size} + {SIGNED_EXTENSIONS_SIZE} is larger than maximal possible transaction size {max_extrinsic_size}", ); // verify that we're able to receive proof of maximal-size message with maximal dispatch weight @@ -75,9 +72,7 @@ pub fn ensure_able_to_receive_message( ); assert!( max_delivery_transaction_dispatch_weight.all_lte(max_extrinsic_weight), - "Weight of maximal message delivery transaction + {} is larger than maximal possible transaction weight {}", - max_delivery_transaction_dispatch_weight, - max_extrinsic_weight, + "Weight of maximal message delivery transaction + {max_delivery_transaction_dispatch_weight} is larger than maximal possible transaction weight {max_extrinsic_weight}", ); } @@ -94,10 +89,7 @@ pub fn ensure_able_to_receive_confirmation( max_inbound_lane_data_proof_size_from_peer_chain.saturating_add(SIGNED_EXTENSIONS_SIZE); assert!( max_confirmation_transaction_size <= max_extrinsic_size, - "Size of maximal message delivery confirmation transaction {} + {} is larger than maximal possible transaction size {}", - max_inbound_lane_data_proof_size_from_peer_chain, - SIGNED_EXTENSIONS_SIZE, - max_extrinsic_size, + "Size of maximal message delivery confirmation transaction {max_inbound_lane_data_proof_size_from_peer_chain} + {SIGNED_EXTENSIONS_SIZE} is larger than maximal possible transaction size {max_extrinsic_size}", ); // verify that we're able to reward maximal number of relayers that have delivered maximal @@ -112,9 +104,7 @@ pub fn ensure_able_to_receive_confirmation( ); assert!( max_confirmation_transaction_dispatch_weight.all_lte(max_extrinsic_weight), - "Weight of maximal confirmation transaction {} is larger than maximal possible transaction weight {}", - max_confirmation_transaction_dispatch_weight, - max_extrinsic_weight, + "Weight of maximal confirmation transaction {max_confirmation_transaction_dispatch_weight} is larger than maximal possible transaction weight {max_extrinsic_weight}", ); } @@ -265,15 +255,6 @@ pub trait WeightInfoExt: WeightInfo { (15 * 1024); proof_size_in_bytes * byte_weight } - - /// Returns weight of the pay-dispatch-fee operation for inbound messages. - /// - /// This function may return zero if runtime doesn't support pay-dispatch-fee-at-target-chain - /// option. - fn pay_inbound_dispatch_fee_overhead() -> Weight { - Self::receive_single_message_proof() - .saturating_sub(Self::receive_single_prepaid_message_proof()) - } } impl WeightInfoExt for () { diff --git a/modules/parachains/Cargo.toml b/modules/parachains/Cargo.toml index ce674459eac..3c3ad95fe34 100644 --- a/modules/parachains/Cargo.toml +++ b/modules/parachains/Cargo.toml @@ -30,6 +30,7 @@ sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", [dev-dependencies] bp-header-chain = { path = "../../primitives/header-chain" } bp-test-utils = { path = "../../primitives/test-utils" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } [features] diff --git a/modules/parachains/README.md b/modules/parachains/README.md new file mode 100644 index 00000000000..1bd91a3ba77 --- /dev/null +++ b/modules/parachains/README.md @@ -0,0 +1,90 @@ +# Bridge Parachains Pallet + +The bridge parachains pallet is a light client for one or several parachains of the bridged relay chain. +It serves as a source of finalized parachain headers and is used when you need to build a bridge with +a parachain. + +The pallet requires [bridge GRANDPA pallet](../grandpa/) to be deployed at the same chain - it is used +to verify storage proofs, generated at the bridged relay chain. + +## A Brief Introduction into Parachains Finality + +You can find detailed information on parachains finality in the [Polkadot](https://github.com/paritytech/polkadot) +and [Cumulus](https://github.com/paritytech/cumulus) repositories. This section gives a brief overview of how +the parachain finality works and how to build a light client for a parachain. + +The main thing there is that the parachain generates blocks on its own, but it can't achieve finality without +help of its relay chain. Instead, the parachain collators create a block and hand it over to the relay chain +validators. Validators validate the block and register the new parachain head in the +[`Heads` map](https://github.com/paritytech/polkadot/blob/88013730166ba90745ae7c9eb3e0c1be1513c7cc/runtime/parachains/src/paras/mod.rs#L645) +of the [`paras`](https://github.com/paritytech/polkadot/tree/master/runtime/parachains/src/paras) pallet, +deployed at the relay chain. Keep in mind that this pallet, deployed at a relay chain, is **NOT** a bridge pallet, +even though the names are similar. + +And what the bridge parachains pallet does, is simply verifying storage proofs of parachain heads within that +`Heads` map. It does that using relay chain header, that has been previously imported by the +[bridge GRANDPA pallet](../grandpa/). Once the proof is verified, the pallet knows that the given parachain +header has been finalized by the relay chain. The parachain header fields may then be used to verify storage +proofs, coming from the parachain. This allows the pallet to be used e.g. as a source of finality for the messages +pallet. + +## Pallet Operations + +The main entrypoint of the pallet is the `submit_parachain_heads` call. It has three arguments: + +- storage proof of parachain heads from the `Heads` map; + +- parachain identifiers and hashes of their heads from the storage proof; + +- the relay block, at which the storage proof has been generated. + +The pallet may track multiple parachains. And the parachains may use different primitives - one may use 128-bit block +numbers, other - 32-bit. To avoid extra decode operations, the pallet is using relay chain block number to order +parachain headers. Any finalized descendant of finalized relay block `RB`, which has parachain block `PB` in +its `Heads` map, is guaranteed to have either `PB`, or its descendant. So parachain block number grows with relay +block number. + +The pallet may reject parachain head if it already knows better (or the same) head. In addition, pallet rejects +heads of untracked parachains. + +The pallet doesn't track anything behind parachain heads. So it requires no initialization - it is ready to accept +headers right after deployment. + +## Non-Essential Functionality + +There may be a special account in every runtime where the bridge parachains module is deployed. This +account, named 'module owner', is like a module-level sudo account - he's able to halt and +resume all module operations without requiring runtime upgrade. Calls that are related to this +account are: + +- `fn set_owner()`: current module owner may call it to transfer "ownership" to another account; + +- `fn set_operating_mode()`: the module owner (or sudo account) may call this function to stop all + module operations. After this call, all finality proofs will be rejected until further `set_operating_mode` call'. + This call may be used when something extraordinary happens with the bridge. + +If pallet owner is not defined, the governance may be used to make those calls. + +## Signed Extension to Reject Obsolete Headers + +It'd be better for anyone (for chain and for submitters) to reject all transactions that are submitting +already known parachain heads to the pallet. This way, we leave block space to other useful transactions and +we don't charge concurrent submitters for their honest actions. + +To deal with that, we have a [signed extension](./src/extension.rs) that may be added to the runtime. +It does exactly what is required - rejects all transactions with already known heads. The submitter +pays nothing for such transactions - they're simply removed from the transaction pool, when the block +is built. + +The signed extension, however, is a bit limited - it only works with transactions that provide single +parachain head. So it won't work with multiple parachain heads transactions. This fits our needs +for [Kusama <> Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md). If you need to deal +with other transaction formats, you may implement similar extension for your runtime. + +You may also take a look at the [`generate_bridge_reject_obsolete_headers_and_messages`](../../bin/runtime-common/src/lib.rs) +macro that bundles several similar signed extensions in a single one. + +## Parachains Finality Relay + +We have an offchain actor, who is watching for new parachain heads and submits them to the bridged chain. +It is the parachains relay - you may look at the [crate level documentation and the code](../../relays/parachains/). diff --git a/modules/parachains/src/benchmarking.rs b/modules/parachains/src/benchmarking.rs index aba296dfc1d..b156b724afe 100644 --- a/modules/parachains/src/benchmarking.rs +++ b/modules/parachains/src/benchmarking.rs @@ -32,6 +32,8 @@ pub struct Pallet, I: 'static>(crate::Pallet); /// Trait that must be implemented by runtime to benchmark the parachains finality pallet. pub trait Config: crate::Config { + /// Returns vector of supported parachains. + fn parachains() -> Vec; /// Generate parachain heads proof and prepare environment for verifying this proof. fn prepare_parachain_heads_proof( parachains: &[ParaId], @@ -53,10 +55,16 @@ benchmarks_instance_pallet! { // Benchmark `submit_parachain_heads` extrinsic with different number of parachains. submit_parachain_heads_with_n_parachains { - let p in 1..1024; + let p in 1..(T::parachains().len() + 1) as u32; let sender = account("sender", 0, 0); - let parachains = (1..=p).map(ParaId).collect::>(); + let mut parachains = T::parachains(); + let _ = if p <= parachains.len() as u32 { + parachains.split_off(p as usize) + } else { + Default::default() + }; + log::trace!(target: crate::LOG_TARGET, "=== {:?}", parachains.len()); let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( ¶chains, DEFAULT_PARACHAIN_HEAD_SIZE, @@ -73,7 +81,7 @@ benchmarks_instance_pallet! { // Benchmark `submit_parachain_heads` extrinsic with 1kb proof size. submit_parachain_heads_with_1kb_proof { let sender = account("sender", 0, 0); - let parachains = vec![ParaId(1)]; + let parachains = vec![T::parachains()[0]]; let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( ¶chains, DEFAULT_PARACHAIN_HEAD_SIZE, @@ -90,7 +98,7 @@ benchmarks_instance_pallet! { // Benchmark `submit_parachain_heads` extrinsic with 16kb proof size. submit_parachain_heads_with_16kb_proof { let sender = account("sender", 0, 0); - let parachains = vec![ParaId(1)]; + let parachains = vec![T::parachains()[0]]; let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( ¶chains, DEFAULT_PARACHAIN_HEAD_SIZE, diff --git a/modules/parachains/src/lib.rs b/modules/parachains/src/lib.rs index e6625476a3d..47ab91928c7 100644 --- a/modules/parachains/src/lib.rs +++ b/modules/parachains/src/lib.rs @@ -27,12 +27,10 @@ pub use weights::WeightInfo; pub use weights_ext::WeightInfoExt; use bp_header_chain::HeaderChain; -use bp_parachains::{parachain_head_storage_key_at_source, ParaInfo}; +use bp_parachains::{parachain_head_storage_key_at_source, ParaInfo, ParaStoredHeaderData}; use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; -use bp_runtime::{HashOf, HeaderOf, Parachain, StorageProofError}; -use codec::Decode; -use frame_support::{dispatch::PostDispatchInfo, traits::Contains}; -use sp_runtime::traits::Header as HeaderT; +use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain, StorageProofError}; +use frame_support::dispatch::PostDispatchInfo; use sp_std::{marker::PhantomData, vec::Vec}; // Re-export in crate namespace for `construct_runtime!`. @@ -69,7 +67,10 @@ struct UpdateParachainHeadArtifacts { #[frame_support::pallet] pub mod pallet { use super::*; - use bp_parachains::{BestParaHeadHash, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider}; + use bp_parachains::{ + BestParaHeadHash, ImportedParaHeadsKeyProvider, ParaStoredHeaderDataBuilder, + ParasInfoKeyProvider, + }; use bp_runtime::{ BasicOperatingMode, BoundedStorageValue, OwnedBridgeModule, StorageDoubleMapKeyProvider, StorageMapKeyProvider, @@ -77,9 +78,9 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// Stored parachain head of given parachains pallet. - pub type StoredParaHeadOf = - BoundedStorageValue<>::MaxParaHeadSize, ParaHead>; + /// Stored parachain head data of given parachains pallet. + pub type StoredParaHeadDataOf = + BoundedStorageValue<>::MaxParaHeadDataSize, ParaStoredHeaderData>; /// Weight info of the given parachains pallet. pub type WeightInfoOf = >::WeightInfo; @@ -153,12 +154,18 @@ pub mod pallet { #[pallet::constant] type ParasPalletName: Get<&'static str>; - /// Set of parachains that are tracked by this pallet. + /// Parachain head data builder. + /// + /// We never store parachain heads here, since they may be too big (e.g. because of large + /// digest items). Instead we're using the same approach as `pallet-bridge-grandpa` + /// pallet - we are only storing `bp_messages::StoredHeaderData` (number and state root), + /// which is enough for our applications. However, we work with different parachains here + /// and they can use different primitives (for block numbers and hash). So we can't store + /// it directly. Instead, we're storing `bp_messages::StoredHeaderData` in SCALE-encoded + /// form, wrapping it into `bp_parachains::ParaStoredHeaderData`. /// - /// The set may be extended easily, without requiring any runtime upgrades. Removing tracked - /// parachain requires special handling - pruning existing heads and cleaning related data - /// structures. - type TrackedParachains: Contains; + /// This builder helps to convert from `HeadData` to `bp_parachains::ParaStoredHeaderData`. + type ParaStoredHeaderDataBuilder: ParaStoredHeaderDataBuilder; /// Maximal number of single parachain heads to keep in the storage. /// @@ -170,16 +177,17 @@ pub mod pallet { #[pallet::constant] type HeadsToKeep: Get; - /// Maximal size (in bytes) of the SCALE-encoded parachain head. + /// Maximal size (in bytes) of the SCALE-encoded parachain head data + /// (`bp_parachains::ParaStoredHeaderData`). /// - /// Keep in mind that the size of any tracked parachain header must not exceed this value. - /// So if you're going to track multiple parachains, one of which is storing large digests - /// in its headers, you shall choose this maximal value. + /// Keep in mind that the size of any tracked parachain header data must not exceed this + /// value. So if you're going to track multiple parachains, one of which is using large + /// hashes, you shall choose this maximal value. /// /// There's no mandatory headers in this pallet, so it can't stall if there's some header /// that exceeds this bound. #[pallet::constant] - type MaxParaHeadSize: Get; + type MaxParaHeadDataSize: Get; } /// Optional pallet owner. @@ -212,7 +220,7 @@ pub mod pallet { ::Value, >; - /// Parachain heads which have been imported into the pallet. + /// State roots of parachain heads which have been imported into the pallet. #[pallet::storage] pub type ImportedParaHeads, I: 'static = ()> = StorageDoubleMap< _, @@ -220,7 +228,7 @@ pub mod pallet { ::Key1, ::Hasher2, ::Key2, - StoredParaHeadOf, + StoredParaHeadDataOf, >; /// A ring buffer of imported parachain head hashes. Ordered by the insertion time. @@ -255,6 +263,7 @@ pub mod pallet { /// `polkadot-runtime-parachains::paras` pallet instance, deployed at the bridged chain. /// The proof is supposed to be crafted at the `relay_header_hash` that must already be /// imported by corresponding GRANDPA pallet at this chain. + #[pallet::call_index(0)] #[pallet::weight(WeightInfoOf::::submit_parachain_heads_weight( T::DbWeight::get(), parachain_heads_proof, @@ -267,6 +276,7 @@ pub mod pallet { parachain_heads_proof: ParaHeadsProof, ) -> DispatchResultWithPostInfo { Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + // we'll need relay chain header to verify that parachains heads are always increasing. let (relay_block_number, relay_block_hash) = at_relay_block; let relay_block = pallet_bridge_grandpa::ImportedHeaders::< @@ -275,7 +285,7 @@ pub mod pallet { >::get(relay_block_hash) .ok_or(Error::::UnknownRelayChainBlock)?; ensure!( - *relay_block.number() == relay_block_number, + relay_block.number == relay_block_number, Error::::InvalidRelayChainBlockNumber, ); @@ -291,17 +301,6 @@ pub mod pallet { sp_trie::StorageProof::new(parachain_heads_proof.0), move |storage| { for (parachain, parachain_head_hash) in parachains { - // if we're not tracking this parachain, we'll just ignore its head proof here - if !T::TrackedParachains::contains(¶chain) { - log::trace!( - target: LOG_TARGET, - "The head of parachain {:?} has been provided, but it is not tracked by the pallet", - parachain, - ); - Self::deposit_event(Event::UntrackedParachainRejected { parachain }); - continue; - } - let parachain_head = match Pallet::::read_parachain_head(&storage, parachain) { Ok(Some(parachain_head)) => parachain_head, Ok(None) => { @@ -349,12 +348,26 @@ pub mod pallet { continue; } + // convert from parachain head into stored parachain head data + let parachain_head_data = match T::ParaStoredHeaderDataBuilder::try_build(parachain, ¶chain_head) { + Some(parachain_head_data) => parachain_head_data, + None => { + log::trace!( + target: LOG_TARGET, + "The head of parachain {:?} has been provided, but it is not tracked by the pallet", + parachain, + ); + Self::deposit_event(Event::UntrackedParachainRejected { parachain }); + continue; + }, + }; + let update_result: Result<_, ()> = ParasInfo::::try_mutate(parachain, |stored_best_head| { let artifacts = Pallet::::update_parachain_head( parachain, stored_best_head.take(), relay_block_number, - parachain_head, + parachain_head_data, parachain_head_hash, )?; *stored_best_head = Some(artifacts.best_head); @@ -383,6 +396,7 @@ pub mod pallet { /// Change `PalletOwner`. /// /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(1)] #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { >::set_owner(origin, new_owner) @@ -391,6 +405,7 @@ pub mod pallet { /// Halt or resume all pallet operations. /// /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(2)] #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] pub fn set_operating_mode( origin: OriginFor, @@ -401,14 +416,41 @@ pub mod pallet { } impl, I: 'static> Pallet { - /// Get best finalized header of the given parachain. - pub fn best_parachain_head(parachain: ParaId) -> Option { + /// Get stored parachain info. + pub fn best_parachain_info(parachain: ParaId) -> Option { + ParasInfo::::get(parachain) + } + + /// Get best finalized head data of the given parachain. + pub fn best_parachain_head(parachain: ParaId) -> Option { let best_para_head_hash = ParasInfo::::get(parachain)?.best_head_hash.head_hash; ImportedParaHeads::::get(parachain, best_para_head_hash).map(|h| h.into_inner()) } - /// Get parachain head with given hash. - pub fn parachain_head(parachain: ParaId, hash: ParaHash) -> Option { + /// Get best finalized head hash of the given parachain. + pub fn best_parachain_head_hash(parachain: ParaId) -> Option { + Some(ParasInfo::::get(parachain)?.best_head_hash.head_hash) + } + + /// Get best finalized head id of the given parachain. + pub fn best_parachain_head_id + Parachain>( + ) -> Result>, codec::Error> { + let parachain = ParaId(C::PARACHAIN_ID); + let best_head_hash = match Self::best_parachain_head_hash(parachain) { + Some(best_head_hash) => best_head_hash, + None => return Ok(None), + }; + let encoded_head = match Self::parachain_head(parachain, best_head_hash) { + Some(encoded_head) => encoded_head, + None => return Ok(None), + }; + encoded_head + .decode_parachain_head_data::() + .map(|data| Some(HeaderId(data.number, best_head_hash))) + } + + /// Get parachain head data with given hash. + pub fn parachain_head(parachain: ParaId, hash: ParaHash) -> Option { ImportedParaHeads::::get(parachain, hash).map(|h| h.into_inner()) } @@ -475,7 +517,7 @@ pub mod pallet { parachain: ParaId, stored_best_head: Option, updated_at_relay_block_number: RelayBlockNumber, - updated_head: ParaHead, + updated_head_data: ParaStoredHeaderData, updated_head_hash: ParaHash, ) -> Result { // check if head has been already updated at better relay chain block. Without this @@ -496,28 +538,29 @@ pub mod pallet { return Err(()) } - // verify that the parachain head size is <= `MaxParaHeadSize` - let updated_head = match StoredParaHeadOf::::try_from_inner(updated_head) { - Ok(updated_head) => updated_head, - Err(e) => { - log::trace!( - target: LOG_TARGET, - "{}. The parachain head size for {:?} is {}. It exceeds maximal configured size {}.", - err_log_prefix, - parachain, - e.value_size, - e.maximal_size, - ); - - Self::deposit_event(Event::RejectedLargeParachainHead { - parachain, - parachain_head_hash: updated_head_hash, - parachain_head_size: e.value_size as _, - }); - - return Err(()) - }, - }; + // verify that the parachain head data size is <= `MaxParaHeadDataSize` + let updated_head_data = + match StoredParaHeadDataOf::::try_from_inner(updated_head_data) { + Ok(updated_head_data) => updated_head_data, + Err(e) => { + log::trace!( + target: LOG_TARGET, + "{}. The parachain head data size for {:?} is {}. It exceeds maximal configured size {}.", + err_log_prefix, + parachain, + e.value_size, + e.maximal_size, + ); + + Self::deposit_event(Event::RejectedLargeParachainHead { + parachain, + parachain_head_hash: updated_head_hash, + parachain_head_size: e.value_size as _, + }); + + return Err(()) + }, + }; let next_imported_hash_position = stored_best_head .map_or(0, |stored_best_head| stored_best_head.next_imported_hash_position); @@ -538,7 +581,7 @@ pub mod pallet { next_imported_hash_position, updated_head_hash, ); - ImportedParaHeads::::insert(parachain, updated_head_hash, updated_head); + ImportedParaHeads::::insert(parachain, updated_head_hash, updated_head_data); log::trace!( target: LOG_TARGET, "Updated head of parachain {:?} to {}", @@ -604,9 +647,10 @@ pub struct ParachainHeaders(PhantomData<(T, I, C)>); impl, I: 'static, C: Parachain> HeaderChain for ParachainHeaders { - fn finalized_header(hash: HashOf) -> Option> { + fn finalized_header_state_root(hash: HashOf) -> Option> { Pallet::::parachain_head(ParaId(C::PARACHAIN_ID), hash) - .and_then(|head| Decode::decode(&mut &head.0[..]).ok()) + .and_then(|head| head.decode_parachain_head_data::().ok()) + .map(|h| h.state_root) } } @@ -614,12 +658,15 @@ impl, I: 'static, C: Parachain> HeaderChain mod tests { use super::*; use crate::mock::{ - run_test, test_relay_header, RuntimeEvent as TestEvent, RuntimeOrigin, TestRuntime, - MAXIMAL_PARACHAIN_HEAD_SIZE, PARAS_PALLET_NAME, UNTRACKED_PARACHAIN_ID, + run_test, test_relay_header, BigParachainHeader, RegularParachainHasher, + RegularParachainHeader, RuntimeEvent as TestEvent, RuntimeOrigin, TestRuntime, + PARAS_PALLET_NAME, UNTRACKED_PARACHAIN_ID, }; use codec::Encode; - use bp_parachains::{BestParaHeadHash, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider}; + use bp_parachains::{ + BestParaHeadHash, BridgeParachainCall, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider, + }; use bp_runtime::{ record_all_trie_keys, BasicOperatingMode, OwnedBridgeModuleError, StorageDoubleMapKeyProvider, StorageMapKeyProvider, @@ -635,7 +682,8 @@ mod tests { weights::Weight, }; use frame_system::{EventRecord, Pallet as System, Phase}; - use sp_runtime::DispatchError; + use sp_core::Hasher; + use sp_runtime::{traits::Header as HeaderT, DispatchError}; use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; @@ -710,12 +758,42 @@ mod tests { } fn head_data(parachain: u32, head_number: u32) -> ParaHead { - ParaHead((parachain, head_number).encode()) + ParaHead( + RegularParachainHeader::new( + head_number as _, + Default::default(), + RegularParachainHasher::hash(&(parachain, head_number).encode()), + Default::default(), + Default::default(), + ) + .encode(), + ) + } + + fn stored_head_data(parachain: u32, head_number: u32) -> ParaStoredHeaderData { + ParaStoredHeaderData( + (head_number as u64, RegularParachainHasher::hash(&(parachain, head_number).encode())) + .encode(), + ) } - fn large_head_data(parachain: u32, head_number: u32) -> ParaHead { + fn big_head_data(parachain: u32, head_number: u32) -> ParaHead { ParaHead( - (parachain, head_number, vec![42u8; MAXIMAL_PARACHAIN_HEAD_SIZE as usize]).encode(), + BigParachainHeader::new( + head_number as _, + Default::default(), + RegularParachainHasher::hash(&(parachain, head_number).encode()), + Default::default(), + Default::default(), + ) + .encode(), + ) + } + + fn big_stored_head_data(parachain: u32, head_number: u32) -> ParaStoredHeaderData { + ParaStoredHeaderData( + (head_number as u128, RegularParachainHasher::hash(&(parachain, head_number).encode())) + .encode(), ) } @@ -817,7 +895,7 @@ mod tests { initial_best_head(1).best_head_hash.head_hash ) .map(|h| h.into_inner()), - Some(head_data(1, 0)) + Some(stored_head_data(1, 0)) ); assert_eq!( ImportedParaHeads::::get( @@ -830,7 +908,7 @@ mod tests { assert_eq!( ImportedParaHeads::::get(ParaId(3), head_hash(3, 10)) .map(|h| h.into_inner()), - Some(head_data(3, 10)) + Some(stored_head_data(3, 10)) ); assert_eq!( @@ -880,7 +958,7 @@ mod tests { assert_eq!( ImportedParaHeads::::get(ParaId(1), head_data(1, 5).hash()) .map(|h| h.into_inner()), - Some(head_data(1, 5)) + Some(stored_head_data(1, 5)) ); assert_eq!( ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()) @@ -915,12 +993,12 @@ mod tests { assert_eq!( ImportedParaHeads::::get(ParaId(1), head_data(1, 5).hash()) .map(|h| h.into_inner()), - Some(head_data(1, 5)) + Some(stored_head_data(1, 5)) ); assert_eq!( ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()) .map(|h| h.into_inner()), - Some(head_data(1, 10)) + Some(stored_head_data(1, 10)) ); assert_eq!( System::::events(), @@ -1147,10 +1225,9 @@ mod tests { #[test] fn does_nothing_when_parachain_head_is_too_large() { let (state_root, proof, parachains) = - prepare_parachain_heads_proof(vec![(1, head_data(1, 5)), (2, large_head_data(1, 5))]); + prepare_parachain_heads_proof(vec![(1, head_data(1, 5)), (4, big_head_data(1, 5))]); run_test(|| { - // start with relay block #0 and try to import head#5 of parachain#1 and untracked - // parachain + // start with relay block #0 and try to import head#5 of parachain#1 and big parachain initialize(state_root); let result = Pallet::::submit_parachain_heads( RuntimeOrigin::signed(1), @@ -1169,7 +1246,7 @@ mod tests { next_imported_hash_position: 1, }) ); - assert_eq!(ParasInfo::::get(ParaId(2)), None); + assert_eq!(ParasInfo::::get(ParaId(4)), None); assert_eq!( System::::events(), vec![ @@ -1184,9 +1261,9 @@ mod tests { EventRecord { phase: Phase::Initialization, event: TestEvent::Parachains(Event::RejectedLargeParachainHead { - parachain: ParaId(2), - parachain_head_hash: large_head_data(1, 5).hash(), - parachain_head_size: large_head_data(1, 5).encoded_size() as u32, + parachain: ParaId(4), + parachain_head_hash: big_head_data(1, 5).hash(), + parachain_head_size: big_stored_head_data(1, 5).encoded_size() as u32, }), topics: vec![], }, @@ -1288,7 +1365,7 @@ mod tests { import_parachain_1_head(0, state_root_5, parachains_5, proof_5).expect("ok"); assert_eq!( Pallet::::best_parachain_head(ParaId(1)), - Some(head_data(1, 5)) + Some(stored_head_data(1, 5)) ); // then if someone is pretending to provide updated head#10 of parachain#1 at relay @@ -1304,7 +1381,7 @@ mod tests { ),); assert_eq!( Pallet::::best_parachain_head(ParaId(1)), - Some(head_data(1, 5)) + Some(stored_head_data(1, 5)) ); // then if someone is pretending to provide updated head#10 of parachain#1 at relay @@ -1320,7 +1397,7 @@ mod tests { ),); assert_eq!( Pallet::::best_parachain_head(ParaId(1)), - Some(head_data(1, 10)) + Some(stored_head_data(1, 10)) ); }); } @@ -1399,5 +1476,24 @@ mod tests { }); } + #[test] + fn test_bridge_parachain_call_is_correctly_defined() { + let (state_root, proof, _) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + let parachains = vec![(ParaId(2), Default::default())]; + let relay_header_id = (0, test_relay_header(0, state_root).hash()); + + let direct_submit_parachain_heads_call = Call::::submit_parachain_heads { + at_relay_block: relay_header_id, + parachains: parachains.clone(), + parachain_heads_proof: proof.clone(), + }; + let indirect_submit_parachain_heads_call = + BridgeParachainCall::submit_parachain_heads(relay_header_id, parachains, proof); + assert_eq!( + direct_submit_parachain_heads_call.encode(), + indirect_submit_parachain_heads_call.encode() + ); + } + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); } diff --git a/modules/parachains/src/mock.rs b/modules/parachains/src/mock.rs index 9638472aa98..118a9b1d315 100644 --- a/modules/parachains/src/mock.rs +++ b/modules/parachains/src/mock.rs @@ -15,12 +15,12 @@ // along with Parity Bridges Common. If not, see . use bp_polkadot_core::parachains::ParaId; -use bp_runtime::Chain; -use frame_support::{construct_runtime, parameter_types, traits::IsInVec, weights::Weight}; +use bp_runtime::{Chain, Parachain}; +use frame_support::{construct_runtime, parameter_types, traits::ConstU32, weights::Weight}; use sp_runtime::{ testing::{Header, H256}, traits::{BlakeTwo256, Header as HeaderT, IdentityLookup}, - Perbill, + MultiSignature, Perbill, }; use crate as pallet_bridge_parachains; @@ -36,7 +36,109 @@ type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + +pub struct Parachain1; + +impl Chain for Parachain1 { + type BlockNumber = u64; + type Hash = H256; + type Hasher = RegularParachainHasher; + type Header = RegularParachainHeader; + type AccountId = u64; + type Balance = u64; + type Index = u64; + type Signature = MultiSignature; + + fn max_extrinsic_size() -> u32 { + 0 + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl Parachain for Parachain1 { + const PARACHAIN_ID: u32 = 1; +} + +pub struct Parachain2; + +impl Chain for Parachain2 { + type BlockNumber = u64; + type Hash = H256; + type Hasher = RegularParachainHasher; + type Header = RegularParachainHeader; + type AccountId = u64; + type Balance = u64; + type Index = u64; + type Signature = MultiSignature; + + fn max_extrinsic_size() -> u32 { + 0 + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl Parachain for Parachain2 { + const PARACHAIN_ID: u32 = 2; +} + +pub struct Parachain3; + +impl Chain for Parachain3 { + type BlockNumber = u64; + type Hash = H256; + type Hasher = RegularParachainHasher; + type Header = RegularParachainHeader; + type AccountId = u64; + type Balance = u64; + type Index = u64; + type Signature = MultiSignature; + + fn max_extrinsic_size() -> u32 { + 0 + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl Parachain for Parachain3 { + const PARACHAIN_ID: u32 = 3; +} + +// this parachain is using u128 as block number and stored head data size exceeds limit +pub struct BigParachain; + +impl Chain for BigParachain { + type BlockNumber = u128; + type Hash = H256; + type Hasher = RegularParachainHasher; + type Header = BigParachainHeader; + type AccountId = u64; + type Balance = u64; + type Index = u64; + type Signature = MultiSignature; + + fn max_extrinsic_size() -> u32 { + 0 + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl Parachain for BigParachain { + const PARACHAIN_ID: u32 = 4; +} construct_runtime! { pub enum TestRuntime where @@ -64,7 +166,7 @@ impl frame_system::Config for TestRuntime { type RuntimeCall = RuntimeCall; type BlockNumber = TestNumber; type Hash = H256; - type Hashing = BlakeTwo256; + type Hashing = RegularParachainHasher; type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; @@ -86,27 +188,24 @@ impl frame_system::Config for TestRuntime { } parameter_types! { - pub const MaxRequests: u32 = 2; - pub const HeadersToKeep: u32 = 5; pub const SessionLength: u64 = 5; pub const NumValidators: u32 = 5; + pub const HeadersToKeep: u32 = 5; } impl pallet_bridge_grandpa::Config for TestRuntime { type BridgedChain = TestBridgedChain; - type MaxRequests = MaxRequests; + type MaxRequests = ConstU32<2>; type HeadersToKeep = HeadersToKeep; type MaxBridgedAuthorities = frame_support::traits::ConstU32<5>; - type MaxBridgedHeaderSize = frame_support::traits::ConstU32<512>; type WeightInfo = (); } impl pallet_bridge_grandpa::Config for TestRuntime { type BridgedChain = TestBridgedChain; - type MaxRequests = MaxRequests; + type MaxRequests = ConstU32<2>; type HeadersToKeep = HeadersToKeep; type MaxBridgedAuthorities = frame_support::traits::ConstU32<5>; - type MaxBridgedHeaderSize = frame_support::traits::ConstU32<512>; type WeightInfo = (); } @@ -121,9 +220,9 @@ impl pallet_bridge_parachains::Config for TestRuntime { type WeightInfo = (); type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; type ParasPalletName = ParasPalletName; - type TrackedParachains = IsInVec; + type ParaStoredHeaderDataBuilder = (Parachain1, Parachain2, Parachain3, BigParachain); type HeadsToKeep = HeadsToKeep; - type MaxParaHeadSize = frame_support::traits::ConstU32; + type MaxParaHeadDataSize = frame_support::traits::ConstU32; } #[derive(Debug)] diff --git a/modules/parachains/src/weights.rs b/modules/parachains/src/weights.rs index 5c9206d0ca0..54f468b7636 100644 --- a/modules/parachains/src/weights.rs +++ b/modules/parachains/src/weights.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_bridge_parachains` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-17, STEPS: 50, REPEAT: 20 +//! DATE: 2022-12-21, STEPS: 50, REPEAT: 20 //! LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled //! CHAIN: Some("dev"), DB CACHE: 1024 @@ -60,20 +60,18 @@ pub trait WeightInfo { /// Those weights are test only and must never be used in production. pub struct BridgeWeight(PhantomData); impl WeightInfo for BridgeWeight { - fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { - Weight::from_ref_time(51_173_000 as u64) - .saturating_add(Weight::from_ref_time(24_495_968 as u64).saturating_mul(p as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().reads((2 as u64).saturating_mul(p as u64))) - .saturating_add(T::DbWeight::get().writes((3 as u64).saturating_mul(p as u64))) + fn submit_parachain_heads_with_n_parachains(_p: u32) -> Weight { + Weight::from_ref_time(52_445_014 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) } fn submit_parachain_heads_with_1kb_proof() -> Weight { - Weight::from_ref_time(58_175_000 as u64) + Weight::from_ref_time(55_253_000 as u64) .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } fn submit_parachain_heads_with_16kb_proof() -> Weight { - Weight::from_ref_time(101_796_000 as u64) + Weight::from_ref_time(98_772_000 as u64) .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } @@ -81,20 +79,18 @@ impl WeightInfo for BridgeWeight { // For backwards compatibility and tests impl WeightInfo for () { - fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { - Weight::from_ref_time(51_173_000 as u64) - .saturating_add(Weight::from_ref_time(24_495_968 as u64).saturating_mul(p as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().reads((2 as u64).saturating_mul(p as u64))) - .saturating_add(RocksDbWeight::get().writes((3 as u64).saturating_mul(p as u64))) + fn submit_parachain_heads_with_n_parachains(_p: u32) -> Weight { + Weight::from_ref_time(52_445_014 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) } fn submit_parachain_heads_with_1kb_proof() -> Weight { - Weight::from_ref_time(58_175_000 as u64) + Weight::from_ref_time(55_253_000 as u64) .saturating_add(RocksDbWeight::get().reads(4 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) } fn submit_parachain_heads_with_16kb_proof() -> Weight { - Weight::from_ref_time(101_796_000 as u64) + Weight::from_ref_time(98_772_000 as u64) .saturating_add(RocksDbWeight::get().reads(4 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) } diff --git a/modules/relayers/Cargo.toml b/modules/relayers/Cargo.toml index 00d1bb59120..9c071238363 100644 --- a/modules/relayers/Cargo.toml +++ b/modules/relayers/Cargo.toml @@ -15,7 +15,6 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive" bp-messages = { path = "../../primitives/messages", default-features = false } bp-relayers = { path = "../../primitives/relayers", default-features = false } -pallet-bridge-messages = { path = "../messages", default-features = false } # Substrate Dependencies @@ -23,6 +22,7 @@ frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [dev-dependencies] @@ -41,9 +41,9 @@ std = [ "frame-support/std", "frame-system/std", "log/std", - "pallet-bridge-messages/std", "scale-info/std", "sp-arithmetic/std", + "sp-runtime/std", "sp-std/std", ] runtime-benchmarks = [ diff --git a/modules/relayers/src/benchmarking.rs b/modules/relayers/src/benchmarking.rs index b79c0fb3ff0..7195b316bb6 100644 --- a/modules/relayers/src/benchmarking.rs +++ b/modules/relayers/src/benchmarking.rs @@ -26,12 +26,24 @@ use frame_system::RawOrigin; /// Reward amount that is (hopefully) is larger than existential deposit across all chains. const REWARD_AMOUNT: u32 = u32::MAX; +/// Pallet we're benchmarking here. +pub struct Pallet(crate::Pallet); + +/// Trait that must be implemented by runtime. +pub trait Config: crate::Config { + /// Prepare environment for paying given reward for serving given lane. + fn prepare_environment(lane: LaneId, reward: Self::Reward); +} + benchmarks! { // Benchmark `claim_rewards` call. claim_rewards { - let lane = [0, 0, 0, 0]; + let lane = LaneId([0, 0, 0, 0]); let relayer: T::AccountId = whitelisted_caller(); - RelayerRewards::::insert(&relayer, lane, T::Reward::from(REWARD_AMOUNT)); + let reward = T::Reward::from(REWARD_AMOUNT); + + T::prepare_environment(lane, reward); + RelayerRewards::::insert(&relayer, lane, reward); }: _(RawOrigin::Signed(relayer), lane) verify { // we can't check anything here, because `PaymentProcedure` is responsible for diff --git a/modules/relayers/src/lib.rs b/modules/relayers/src/lib.rs index 921ec0e4d98..b28d17cf5a4 100644 --- a/modules/relayers/src/lib.rs +++ b/modules/relayers/src/lib.rs @@ -28,9 +28,10 @@ use sp_std::marker::PhantomData; use weights::WeightInfo; pub use pallet::*; -pub use payment_adapter::MessageDeliveryAndDispatchPaymentAdapter; +pub use payment_adapter::DeliveryConfirmationPaymentsAdapter; + +pub mod benchmarking; -mod benchmarking; mod mock; mod payment_adapter; @@ -64,6 +65,7 @@ pub mod pallet { #[pallet::call] impl Pallet { /// Claim accumulated rewards. + #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::claim_rewards())] pub fn claim_rewards(origin: OriginFor, lane_id: LaneId) -> DispatchResult { let relayer = ensure_signed(origin)?; @@ -108,8 +110,9 @@ pub mod pallet { log::trace!( target: crate::LOG_TARGET, - "Relayer {:?} can now claim reward: {:?}", + "Relayer {:?} can now claim reward for serving lane {:?}: {:?}", relayer, + lane_id, new_reward, ); }); @@ -140,6 +143,7 @@ pub mod pallet { /// Map of the relayer => accumulated reward. #[pallet::storage] + #[pallet::getter(fn relayer_reward)] pub type RelayerRewards = StorageDoubleMap< _, Blake2_128Concat, @@ -157,7 +161,10 @@ mod tests { use mock::{RuntimeEvent as TestEvent, *}; use crate::Event::RewardPaid; - use frame_support::{assert_noop, assert_ok, traits::fungible::Inspect}; + use frame_support::{ + assert_noop, assert_ok, + traits::fungible::{Inspect, Mutate}, + }; use frame_system::{EventRecord, Pallet as System, Phase}; use sp_runtime::DispatchError; @@ -232,16 +239,31 @@ mod tests { } #[test] - fn mint_reward_payment_procedure_actually_mints_tokens() { + fn pay_lane_reward_from_account_actually_pays_reward() { type Balances = pallet_balances::Pallet; + type PayLaneRewardFromAccount = bp_relayers::PayLaneRewardFromAccount; run_test(|| { + let lane0_rewards_account = + PayLaneRewardFromAccount::lane_rewards_account(LaneId([0, 0, 0, 0])); + let lane1_rewards_account = + PayLaneRewardFromAccount::lane_rewards_account(LaneId([0, 0, 0, 1])); + + Balances::mint_into(&lane0_rewards_account, 100).unwrap(); + Balances::mint_into(&lane1_rewards_account, 100).unwrap(); + assert_eq!(Balances::balance(&lane0_rewards_account), 100); + assert_eq!(Balances::balance(&lane1_rewards_account), 100); assert_eq!(Balances::balance(&1), 0); - assert_eq!(Balances::total_issuance(), 0); - bp_relayers::MintReward::::pay_reward(&1, TEST_LANE_ID, 100) - .unwrap(); + + PayLaneRewardFromAccount::pay_reward(&1, LaneId([0, 0, 0, 0]), 100).unwrap(); + assert_eq!(Balances::balance(&lane0_rewards_account), 0); + assert_eq!(Balances::balance(&lane1_rewards_account), 100); assert_eq!(Balances::balance(&1), 100); - assert_eq!(Balances::total_issuance(), 100); + + PayLaneRewardFromAccount::pay_reward(&1, LaneId([0, 0, 0, 1]), 100).unwrap(); + assert_eq!(Balances::balance(&lane0_rewards_account), 0); + assert_eq!(Balances::balance(&lane1_rewards_account), 0); + assert_eq!(Balances::balance(&1), 200); }); } } diff --git a/modules/relayers/src/mock.rs b/modules/relayers/src/mock.rs index 55db2d575e7..89b3ead0411 100644 --- a/modules/relayers/src/mock.rs +++ b/modules/relayers/src/mock.rs @@ -18,9 +18,7 @@ use crate as pallet_bridge_relayers; -use bp_messages::{ - source_chain::ForbidOutboundMessages, target_chain::ForbidInboundMessages, LaneId, -}; +use bp_messages::LaneId; use bp_relayers::PaymentProcedure; use frame_support::{parameter_types, weights::RuntimeDbWeight}; use sp_core::H256; @@ -43,7 +41,6 @@ frame_support::construct_runtime! { { System: frame_system::{Pallet, Call, Config, Storage, Event}, Balances: pallet_balances::{Pallet, Event}, - Messages: pallet_bridge_messages::{Pallet, Event}, Relayers: pallet_bridge_relayers::{Pallet, Call, Event}, } } @@ -91,34 +88,6 @@ impl pallet_balances::Config for TestRuntime { type ReserveIdentifier = (); } -parameter_types! { - pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; - pub ActiveOutboundLanes: &'static [bp_messages::LaneId] = &[[0, 0, 0, 0]]; -} - -// we're not testing messages pallet here, so values in this config might be crazy -impl pallet_bridge_messages::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type ActiveOutboundLanes = ActiveOutboundLanes; - type MaxUnrewardedRelayerEntriesAtInboundLane = frame_support::traits::ConstU64<8>; - type MaxUnconfirmedMessagesAtInboundLane = frame_support::traits::ConstU64<8>; - - type MaximalOutboundPayloadSize = frame_support::traits::ConstU32<1024>; - type OutboundPayload = (); - - type InboundPayload = (); - type InboundRelayer = AccountId; - - type TargetHeaderChain = ForbidOutboundMessages; - type LaneMessageVerifier = ForbidOutboundMessages; - type MessageDeliveryAndDispatchPayment = (); - - type SourceHeaderChain = ForbidInboundMessages; - type MessageDispatch = ForbidInboundMessages; - type BridgedChainId = TestBridgedChainId; -} - impl pallet_bridge_relayers::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type Reward = Balance; @@ -127,7 +96,7 @@ impl pallet_bridge_relayers::Config for TestRuntime { } /// Message lane that we're using in tests. -pub const TEST_LANE_ID: LaneId = [0, 0, 0, 0]; +pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]); /// Regular relayer that may receive rewards. pub const REGULAR_RELAYER: AccountId = 1; diff --git a/modules/relayers/src/payment_adapter.rs b/modules/relayers/src/payment_adapter.rs index 4939d274c60..946b31cf7af 100644 --- a/modules/relayers/src/payment_adapter.rs +++ b/modules/relayers/src/payment_adapter.rs @@ -14,49 +14,45 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Code that allows relayers pallet to be used as a delivery+dispatch payment mechanism -//! for the messages pallet. +//! Code that allows relayers pallet to be used as a payment mechanism for the messages pallet. use crate::{Config, Pallet}; -use bp_messages::source_chain::{MessageDeliveryAndDispatchPayment, RelayersRewards}; -use frame_support::sp_runtime::SaturatedConversion; +use bp_messages::source_chain::{DeliveryConfirmationPayments, RelayersRewards}; +use frame_support::{sp_runtime::SaturatedConversion, traits::Get}; use sp_arithmetic::traits::{Saturating, UniqueSaturatedFrom, Zero}; use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive}; /// Adapter that allows relayers pallet to be used as a delivery+dispatch payment mechanism /// for the messages pallet. -pub struct MessageDeliveryAndDispatchPaymentAdapter( - PhantomData<(T, MessagesInstance)>, +pub struct DeliveryConfirmationPaymentsAdapter( + PhantomData<(T, DeliveryReward, ConfirmationReward)>, ); -impl MessageDeliveryAndDispatchPayment - for MessageDeliveryAndDispatchPaymentAdapter +impl DeliveryConfirmationPayments + for DeliveryConfirmationPaymentsAdapter where - T: Config + pallet_bridge_messages::Config, - MessagesInstance: 'static, + T: Config, + DeliveryReward: Get, + ConfirmationReward: Get, { type Error = &'static str; - fn pay_relayers_rewards( + fn pay_reward( lane_id: bp_messages::LaneId, messages_relayers: VecDeque>, confirmation_relayer: &T::AccountId, received_range: &RangeInclusive, ) { - let relayers_rewards = pallet_bridge_messages::calc_relayers_rewards::( - messages_relayers, - received_range, - ); + let relayers_rewards = + bp_messages::calc_relayers_rewards::(messages_relayers, received_range); register_relayers_rewards::( confirmation_relayer, relayers_rewards, lane_id, - // TODO (https://github.com/paritytech/parity-bridges-common/issues/1318): this shall be fixed - // in some way. ATM the future of the `register_relayer_reward` is not yet known - 100_000_u32.into(), - 10_000_u32.into(), + DeliveryReward::get(), + ConfirmationReward::get(), ); } } diff --git a/modules/relayers/src/weights.rs b/modules/relayers/src/weights.rs index 7bcd8711a9e..9bbaa343e12 100644 --- a/modules/relayers/src/weights.rs +++ b/modules/relayers/src/weights.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_bridge_relayers` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-17, STEPS: 50, REPEAT: 20 +//! DATE: 2022-12-21, STEPS: 50, REPEAT: 20 //! LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled //! CHAIN: Some("dev"), DB CACHE: 1024 @@ -59,7 +59,7 @@ pub trait WeightInfo { pub struct BridgeWeight(PhantomData); impl WeightInfo for BridgeWeight { fn claim_rewards() -> Weight { - Weight::from_ref_time(59_334_000 as u64) + Weight::from_ref_time(64_832_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } @@ -68,7 +68,7 @@ impl WeightInfo for BridgeWeight { // For backwards compatibility and tests impl WeightInfo for () { fn claim_rewards() -> Weight { - Weight::from_ref_time(59_334_000 as u64) + Weight::from_ref_time(64_832_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } diff --git a/modules/shift-session-manager/src/lib.rs b/modules/shift-session-manager/src/lib.rs index 073013d92d0..dce6005ab54 100644 --- a/modules/shift-session-manager/src/lib.rs +++ b/modules/shift-session-manager/src/lib.rs @@ -152,7 +152,6 @@ mod tests { } parameter_types! { - pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); @@ -169,7 +168,7 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type RuntimeEvent = (); - type BlockHashCount = BlockHashCount; + type BlockHashCount = frame_support::traits::ConstU64<250>; type Version = (); type PalletInfo = PalletInfo; type AccountData = (); diff --git a/primitives/beefy/Cargo.toml b/primitives/beefy/Cargo.toml index 6b169e66113..cc8360c7106 100644 --- a/primitives/beefy/Cargo.toml +++ b/primitives/beefy/Cargo.toml @@ -10,7 +10,6 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "bit-vec"] } scale-info = { version = "2.0.1", default-features = false, features = ["bit-vec", "derive"] } serde = { version = "1.0", optional = true } -static_assertions = "1.1" # Bridge Dependencies @@ -19,20 +18,16 @@ bp-runtime = { path = "../runtime", default-features = false } # Substrate Dependencies beefy-merkle-tree = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -beefy-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [features] default = ["std"] std = [ - "beefy-primitives/std", "bp-runtime/std", "codec/std", "frame-support/std", @@ -40,9 +35,7 @@ std = [ "pallet-mmr/std", "scale-info/std", "serde", - "sp-application-crypto/std", - "sp-core/std", - "sp-io/std", + "sp-beefy/std", "sp-runtime/std", "sp-std/std" ] diff --git a/primitives/beefy/src/lib.rs b/primitives/beefy/src/lib.rs index a0a096bdce1..8b2b05d551f 100644 --- a/primitives/beefy/src/lib.rs +++ b/primitives/beefy/src/lib.rs @@ -20,18 +20,18 @@ #![warn(missing_docs)] pub use beefy_merkle_tree::{merkle_root, Keccak256 as BeefyKeccak256}; -pub use beefy_primitives::{ - crypto::{AuthorityId as EcdsaValidatorId, AuthoritySignature as EcdsaValidatorSignature}, - known_payloads::MMR_ROOT_ID as MMR_ROOT_PAYLOAD_ID, - mmr::{BeefyAuthoritySet, MmrLeafVersion}, - BeefyAuthorityId, BeefyVerify, Commitment, Payload as BeefyPayload, SignedCommitment, - ValidatorSet, ValidatorSetId, BEEFY_ENGINE_ID, -}; pub use pallet_beefy_mmr::BeefyEcdsaToEthereum; pub use pallet_mmr::{ primitives::{DataOrHash as MmrDataOrHash, Proof as MmrProof}, verify_leaves_proof as verify_mmr_leaves_proof, }; +pub use sp_beefy::{ + crypto::{AuthorityId as EcdsaValidatorId, AuthoritySignature as EcdsaValidatorSignature}, + known_payloads::MMR_ROOT_ID as MMR_ROOT_PAYLOAD_ID, + mmr::{BeefyAuthoritySet, MmrLeafVersion}, + BeefyAuthorityId, Commitment, Payload as BeefyPayload, SignedCommitment, ValidatorSet, + ValidatorSetId, BEEFY_ENGINE_ID, +}; use bp_runtime::{BasicOperatingMode, BlockNumberOf, Chain, HashOf}; use codec::{Decode, Encode}; @@ -39,7 +39,7 @@ use frame_support::Parameter; use scale_info::TypeInfo; use sp_runtime::{ traits::{Convert, MaybeSerializeDeserialize}, - RuntimeDebug, + RuntimeAppPublic, RuntimeDebug, }; use sp_std::prelude::*; @@ -72,7 +72,8 @@ pub trait ChainWithBeefy: Chain { + Copy + AsRef<[u8]> + Default - + MaybeSerializeDeserialize; + + MaybeSerializeDeserialize + + PartialOrd; /// The type expected for the MMR leaf extra data. type BeefyMmrLeafExtra: Parameter; @@ -80,12 +81,7 @@ pub trait ChainWithBeefy: Chain { /// A way to identify a BEEFY validator. /// /// Corresponds to the `BeefyId` field of the `pallet-beefy` configuration. - type AuthorityId: BeefyAuthorityId + Parameter; - - /// The signature type used by BEEFY. - /// - /// Corresponds to the `BeefyId` field of the `pallet-beefy` configuration. - type Signature: BeefyVerify + Parameter; + type AuthorityId: BeefyAuthorityId + Parameter; /// A way to convert validator id to its raw representation in the BEEFY merkle tree. /// @@ -99,9 +95,10 @@ pub type BeefyAuthorityIdOf = ::AuthorityId; /// BEEFY validator set, containing both validator identifiers and the numeric set id. pub type BeefyAuthoritySetOf = ValidatorSet>; /// BEEFY authority set, containing both validator identifiers and the numeric set id. -pub type BeefyAuthoritySetInfoOf = beefy_primitives::mmr::BeefyAuthoritySet>; +pub type BeefyAuthoritySetInfoOf = sp_beefy::mmr::BeefyAuthoritySet>; /// BEEFY validator signature used by given Substrate chain. -pub type BeefyValidatorSignatureOf = ::Signature; +pub type BeefyValidatorSignatureOf = + <::AuthorityId as RuntimeAppPublic>::Signature; /// Signed BEEFY commitment used by given Substrate chain. pub type BeefySignedCommitmentOf = SignedCommitment, BeefyValidatorSignatureOf>; @@ -119,12 +116,8 @@ pub type BeefyMmrLeafExtraOf = ::BeefyMmrLeafExtra; /// the given Substrate chain. pub type BeefyAuthorityIdToMerkleLeafOf = ::AuthorityIdToMerkleLeaf; /// Actual type of leafs in the BEEFY MMR. -pub type BeefyMmrLeafOf = beefy_primitives::mmr::MmrLeaf< - BlockNumberOf, - HashOf, - MmrHashOf, - BeefyMmrLeafExtraOf, ->; +pub type BeefyMmrLeafOf = + sp_beefy::mmr::MmrLeaf, HashOf, MmrHashOf, BeefyMmrLeafExtraOf>; /// Data required for initializing the BEEFY pallet. /// diff --git a/primitives/chain-bridge-hub-cumulus/Cargo.toml b/primitives/chain-bridge-hub-cumulus/Cargo.toml new file mode 100644 index 00000000000..8896522951b --- /dev/null +++ b/primitives/chain-bridge-hub-cumulus/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "bp-bridge-hub-cumulus" +description = "Primitives of BridgeHubRococo parachain runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +# Bridge Dependencies + +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } + +# Substrate Based Dependencies + +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +# Polkadot Dependencies +polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-polkadot-core/std", + "bp-messages/std", + "frame-system/std", + "frame-support/std", + "sp-api/std", + "polkadot-primitives/std", +] diff --git a/primitives/chain-bridge-hub-cumulus/src/lib.rs b/primitives/chain-bridge-hub-cumulus/src/lib.rs new file mode 100644 index 00000000000..e33131ff8a2 --- /dev/null +++ b/primitives/chain-bridge-hub-cumulus/src/lib.rs @@ -0,0 +1,116 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_messages::*; +pub use bp_polkadot_core::{ + AccountId, AccountInfoStorageMapKeyProvider, AccountPublic, Balance, BlockNumber, + BridgeSignedExtension, Hash, Hasher, Hashing, Header, Index, Nonce, Perbill, + PolkadotSignedExtension, Signature, SignedBlock, UncheckedExtrinsic, TX_EXTRA_BYTES, +}; +use frame_support::{ + dispatch::DispatchClass, + parameter_types, + sp_runtime::{MultiAddress, MultiSigner}, + weights::constants, +}; +use frame_system::limits; + +/// All cumulus bridge hubs allow normal extrinsics to fill block up to 75 percent. +/// +/// This is a copy-paste from the cumulus repo's `parachains-common` crate. +pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// All cumulus bridge hubs chains allow for 0.5 seconds of compute with a 6-second average block +/// time. +/// +/// This is a copy-paste from the cumulus repo's `parachains-common` crate. +// TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` +const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_ref_time(constants::WEIGHT_REF_TIME_PER_SECOND) + .saturating_div(2) + .set_proof_size(polkadot_primitives::v2::MAX_POV_SIZE as u64); + +/// All cumulus bridge hubs assume that about 5 percent of the block weight is consumed by +/// `on_initialize` handlers. This is used to limit the maximal weight of a single extrinsic. +/// +/// This is a copy-paste from the cumulus repo's `parachains-common` crate. +pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); + +parameter_types! { + pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio( + 5 * 1024 * 1024, + NORMAL_DISPATCH_RATIO, + ); + + pub const BlockExecutionWeight: Weight = Weight::from_ref_time(constants::WEIGHT_REF_TIME_PER_NANOS).saturating_mul(5_000_000); + + pub const ExtrinsicBaseWeight: Weight = Weight::from_ref_time(constants::WEIGHT_REF_TIME_PER_NANOS).saturating_mul(125_000); + + pub BlockWeights: limits::BlockWeights = limits::BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + // Operational transactions have an extra reserved space, so that they + // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT, + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); +} + +/// Public key of the chain account that may be used to verify signatures. +pub type AccountSigner = MultiSigner; + +/// The address format for describing accounts. +pub type Address = MultiAddress; + +// Note about selecting values of two following constants: +// +// Normal transactions have limit of 75% of 1/2 second weight for Cumulus parachains. Let's keep +// some reserve for the rest of stuff there => let's select values that fit in 50% of maximal limit. +// +// Using current constants, the limit would be: +// +// `75% * WEIGHT_REF_TIME_PER_SECOND * 1 / 2 * 50% = 0.75 * 1_000_000_000_000 / 2 * 0.5 = +// 187_500_000_000` +// +// According to (preliminary) weights of messages pallet, cost of additional message is zero and the +// cost of additional relayer is `8_000_000 + db read + db write`. Let's say we want no more than +// 4096 unconfirmed messages (no any scientific justification for that - it just looks large +// enough). And then we can't have more than 4096 relayers. E.g. for 1024 relayers is (using +// `RocksDbWeight`): +// +// `1024 * (8_000_000 + db read + db write) = 1024 * (8_000_000 + 25_000_000 + 100_000_000) = +// 136_192_000_000` +// +// So 1024 looks like good approximation for the number of relayers. If something is wrong in those +// assumptions, or something will change, it shall be caught by the +// `ensure_able_to_receive_confirmation` test. + +/// Maximal number of unrewarded relayer entries at inbound lane for Cumulus-based parachains. +pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; + +/// Maximal number of unconfirmed messages at inbound lane for Cumulus-based parachains. +pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 4096; diff --git a/primitives/chain-bridge-hub-rococo/Cargo.toml b/primitives/chain-bridge-hub-rococo/Cargo.toml index 51bc88baf0e..85c4225ab55 100644 --- a/primitives/chain-bridge-hub-rococo/Cargo.toml +++ b/primitives/chain-bridge-hub-rococo/Cargo.toml @@ -7,11 +7,9 @@ edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -smallvec = "1.10.0" - # Bridge Dependencies -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false } bp-messages = { path = "../../primitives/messages", default-features = false } @@ -21,17 +19,13 @@ frame-support = { git = "https://github.com/paritytech/substrate", branch = "mas sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -# Polkadot Dependencies -polkadot-runtime-constants = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } - [features] default = ["std"] std = [ - "bp-polkadot-core/std", + "bp-bridge-hub-cumulus/std", "bp-messages/std", "bp-runtime/std", "frame-support/std", "sp-api/std", "sp-std/std", - "polkadot-runtime-constants/std", ] diff --git a/primitives/chain-bridge-hub-rococo/src/lib.rs b/primitives/chain-bridge-hub-rococo/src/lib.rs index aa65f378542..633a021b634 100644 --- a/primitives/chain-bridge-hub-rococo/src/lib.rs +++ b/primitives/chain-bridge-hub-rococo/src/lib.rs @@ -19,19 +19,14 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub use bp_bridge_hub_cumulus::*; use bp_messages::*; -pub use bp_polkadot_core::*; use bp_runtime::{ decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, Parachain, }; use frame_support::{ dispatch::DispatchClass, - parameter_types, sp_runtime::{MultiAddress, MultiSigner}, - weights::{ - constants::ExtrinsicBaseWeight, WeightToFeeCoefficient, WeightToFeeCoefficients, - WeightToFeePolynomial, - }, RuntimeDebug, }; use sp_std::prelude::*; @@ -67,26 +62,6 @@ impl Parachain for BridgeHubRococo { const PARACHAIN_ID: u32 = BRIDGE_HUB_ROCOCO_PARACHAIN_ID; } -/// [`WeightToFee`] should reflect cumulus/bridge-hub-rococo-runtime [`WeightToFee`] -pub struct WeightToFee; -impl WeightToFeePolynomial for WeightToFee { - type Balance = Balance; - fn polynomial() -> WeightToFeeCoefficients { - pub const CENTS: Balance = polkadot_runtime_constants::currency::CENTS; - - // in Rococo, extrinsic base weight (smallest non-zero weight) is mapped to 1/10 CENT: - // in BridgeHub, we map to 1/10 of that, or 1/100 CENT - let p = CENTS; - let q = 100 * Balance::from(ExtrinsicBaseWeight::get().ref_time()); - smallvec::smallvec![WeightToFeeCoefficient { - degree: 1, - negative: false, - coeff_frac: Perbill::from_rational(p % q, q), - coeff_integer: p / q, - }] - } -} - /// Public key of the chain account that may be used to verify signatures. pub type AccountSigner = MultiSigner; @@ -99,9 +74,5 @@ pub const BRIDGE_HUB_ROCOCO_PARACHAIN_ID: u32 = 1013; /// Name of the With-BridgeHubRococo messages pallet instance that is deployed at bridged chains. pub const WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME: &str = "BridgeRococoMessages"; -parameter_types! { - pub const SS58Prefix: u16 = 42; -} - decl_bridge_finality_runtime_apis!(bridge_hub_rococo); decl_bridge_messages_runtime_apis!(bridge_hub_rococo); diff --git a/primitives/chain-bridge-hub-wococo/Cargo.toml b/primitives/chain-bridge-hub-wococo/Cargo.toml index 94ac501fbd5..24ecdb7adbc 100644 --- a/primitives/chain-bridge-hub-wococo/Cargo.toml +++ b/primitives/chain-bridge-hub-wococo/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" # Bridge Dependencies -bp-bridge-hub-rococo = { path = "../chain-bridge-hub-rococo", default-features = false } +bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false } bp-messages = { path = "../../primitives/messages", default-features = false } @@ -23,10 +23,10 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", d [features] default = ["std"] std = [ + "bp-bridge-hub-cumulus/std", "bp-runtime/std", "bp-messages/std", "frame-support/std", "sp-api/std", "sp-std/std", - "bp-bridge-hub-rococo/std", ] diff --git a/primitives/chain-bridge-hub-wococo/src/lib.rs b/primitives/chain-bridge-hub-wococo/src/lib.rs index a2c3f2ad37a..e04ca3ff228 100644 --- a/primitives/chain-bridge-hub-wococo/src/lib.rs +++ b/primitives/chain-bridge-hub-wococo/src/lib.rs @@ -16,20 +16,10 @@ //! Module with configuration which reflects BridgeHubWococo runtime setup //! (AccountId, Headers, Hashes...) -//! -//! but actually this is just reexported BridgeHubRococo stuff, because they are supposed to be -//! identical, at least uses the same parachain runtime #![cfg_attr(not(feature = "std"), no_std)] -// Re-export only what is really needed -pub use bp_bridge_hub_rococo::{ - AccountId, AccountInfoStorageMapKeyProvider, AccountPublic, AccountSigner, Address, Balance, - BlockLength, BlockNumber, BlockWeights, Hash, Hasher, Hashing, Header, Index, Nonce, - SS58Prefix, Signature, SignedBlock, SignedExtensions, UncheckedExtrinsic, WeightToFee, - MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, - TX_EXTRA_BYTES, -}; +pub use bp_bridge_hub_cumulus::*; use bp_messages::*; use bp_runtime::{ decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, Parachain, diff --git a/primitives/chain-millau/src/lib.rs b/primitives/chain-millau/src/lib.rs index ceb4be21b81..afe8fbca69c 100644 --- a/primitives/chain-millau/src/lib.rs +++ b/primitives/chain-millau/src/lib.rs @@ -27,7 +27,7 @@ use bp_messages::{ use bp_runtime::{decl_bridge_runtime_apis, Chain}; use frame_support::{ dispatch::DispatchClass, - weights::{constants::WEIGHT_PER_SECOND, IdentityFee, Weight}, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, IdentityFee, Weight}, RuntimeDebug, }; use frame_system::limits; @@ -60,7 +60,9 @@ pub const TX_EXTRA_BYTES: u32 = 103; /// /// This represents 0.5 seconds of compute assuming a target block time of six seconds. // TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` -pub const MAXIMUM_BLOCK_WEIGHT: Weight = WEIGHT_PER_SECOND.set_proof_size(1_000).saturating_div(2); +pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_ref_time(WEIGHT_REF_TIME_PER_SECOND) + .set_proof_size(1_000) + .saturating_div(2); /// Represents the portion of a block that will be used by Normal extrinsics. pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); @@ -81,9 +83,6 @@ pub const SESSION_LENGTH: BlockNumber = 5 * time_units::MINUTES; /// Maximal number of GRANDPA authorities at Millau. pub const MAX_AUTHORITIES_COUNT: u32 = 5; -/// Maximal SCALE-encoded header size (in bytes) at Millau. -pub const MAX_HEADER_SIZE: u32 = 1024; - /// Re-export `time_units` to make usage easier. pub use time_units::*; @@ -163,7 +162,6 @@ impl ChainWithBeefy for Millau { type MmrHash = ::Output; type BeefyMmrLeafExtra = (); type AuthorityId = bp_beefy::EcdsaValidatorId; - type Signature = bp_beefy::EcdsaValidatorSignature; type AuthorityIdToMerkleLeaf = bp_beefy::BeefyEcdsaToEthereum; } diff --git a/primitives/chain-rialto-parachain/src/lib.rs b/primitives/chain-rialto-parachain/src/lib.rs index 82b6a12c182..8a98ffe7061 100644 --- a/primitives/chain-rialto-parachain/src/lib.rs +++ b/primitives/chain-rialto-parachain/src/lib.rs @@ -24,7 +24,7 @@ use bp_messages::{ use bp_runtime::{decl_bridge_runtime_apis, Chain, Parachain}; use frame_support::{ dispatch::DispatchClass, - weights::{constants::WEIGHT_PER_SECOND, IdentityFee, Weight}, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, IdentityFee, Weight}, RuntimeDebug, }; use frame_system::limits; @@ -55,7 +55,9 @@ pub const TX_EXTRA_BYTES: u32 = 104; /// /// This represents two seconds of compute assuming a target block time of six seconds. // TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` -pub const MAXIMUM_BLOCK_WEIGHT: Weight = WEIGHT_PER_SECOND.set_proof_size(1_000).saturating_mul(2); +pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_ref_time(WEIGHT_REF_TIME_PER_SECOND) + .set_proof_size(1_000) + .saturating_mul(2); /// Represents the portion of a block that will be used by Normal extrinsics. pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); diff --git a/primitives/chain-rialto/src/lib.rs b/primitives/chain-rialto/src/lib.rs index dfb727829d9..5cf4f85f54a 100644 --- a/primitives/chain-rialto/src/lib.rs +++ b/primitives/chain-rialto/src/lib.rs @@ -24,7 +24,7 @@ use bp_messages::{ use bp_runtime::{decl_bridge_runtime_apis, Chain}; use frame_support::{ dispatch::DispatchClass, - weights::{constants::WEIGHT_PER_SECOND, IdentityFee, Weight}, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, IdentityFee, Weight}, RuntimeDebug, }; use frame_system::limits; @@ -49,7 +49,9 @@ pub const TX_EXTRA_BYTES: u32 = 104; /// /// This represents two seconds of compute assuming a target block time of six seconds. // TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` -pub const MAXIMUM_BLOCK_WEIGHT: Weight = WEIGHT_PER_SECOND.set_proof_size(1_000).saturating_mul(2); +pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_ref_time(WEIGHT_REF_TIME_PER_SECOND) + .set_proof_size(1_000) + .saturating_mul(2); /// Represents the portion of a block that will be used by Normal extrinsics. pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); @@ -70,11 +72,12 @@ pub const SESSION_LENGTH: BlockNumber = 4; /// Maximal number of GRANDPA authorities at Rialto. pub const MAX_AUTHORITIES_COUNT: u32 = 5; -/// Maximal SCALE-encoded header size (in bytes) at Rialto. -pub const MAX_HEADER_SIZE: u32 = 1024; - -/// Maximal SCALE-encoded size of parachains headers that are stored at Rialto `Paras` pallet. -pub const MAX_NESTED_PARACHAIN_HEAD_SIZE: u32 = MAX_HEADER_SIZE; +/// Maximal size of encoded `bp_parachains::ParaStoredHeaderData` structure among all Rialto +/// parachains. +/// +/// It includes the block number and state root, so it shall be near 40 bytes, but let's have some +/// reserve. +pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; /// Re-export `time_units` to make usage easier. pub use time_units::*; diff --git a/primitives/chain-rococo/src/lib.rs b/primitives/chain-rococo/src/lib.rs index 6fd7228fd75..57a47211e4e 100644 --- a/primitives/chain-rococo/src/lib.rs +++ b/primitives/chain-rococo/src/lib.rs @@ -35,15 +35,12 @@ pub const PARAS_PALLET_NAME: &str = "Paras"; /// Name of the With-Rococo GRANDPA pallet instance that is deployed at bridged chains. pub const WITH_ROCOCO_GRANDPA_PALLET_NAME: &str = "BridgeRococoGrandpa"; -/// Maximal SCALE-encoded header size (in bytes) at Rococo. +/// Maximal size of encoded `bp_parachains::ParaStoredHeaderData` structure among all Rococo +/// parachains. /// -/// Let's assume that the largest header is header that enacts new authorities set with -/// `MAX_AUTHORITES_COUNT`. Every authority means 32-byte key and 8-byte weight. Let's also have -/// some fixed reserve for other things (digest, block hash and number, ...) as well. -pub const MAX_HEADER_SIZE: u32 = 4096 + MAX_AUTHORITIES_COUNT * 40; - -/// Maximal SCALE-encoded size of parachains headers that are stored at Rococo `Paras` pallet. -pub const MAX_NESTED_PARACHAIN_HEAD_SIZE: u32 = MAX_HEADER_SIZE; +/// It includes the block number and state root, so it shall be near 40 bytes, but let's have some +/// reserve. +pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; /// Maximal number of GRANDPA authorities at Rococo. /// diff --git a/primitives/chain-westend/Cargo.toml b/primitives/chain-westend/Cargo.toml index 7d74362b09a..75f6727d764 100644 --- a/primitives/chain-westend/Cargo.toml +++ b/primitives/chain-westend/Cargo.toml @@ -15,6 +15,7 @@ bp-runtime = { path = "../runtime", default-features = false } # Substrate Based Dependencies +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [features] @@ -22,5 +23,6 @@ default = ["std"] std = [ "bp-polkadot-core/std", "bp-runtime/std", + "frame-support/std", "sp-api/std", ] diff --git a/primitives/chain-westend/src/lib.rs b/primitives/chain-westend/src/lib.rs index 9f4a98baedc..8a3794b418e 100644 --- a/primitives/chain-westend/src/lib.rs +++ b/primitives/chain-westend/src/lib.rs @@ -19,11 +19,42 @@ #![allow(clippy::too_many_arguments)] pub use bp_polkadot_core::*; -use bp_runtime::decl_bridge_finality_runtime_apis; +use bp_runtime::{decl_bridge_finality_runtime_apis, Chain, Parachain}; +use frame_support::weights::Weight; /// Westend Chain pub type Westend = PolkadotLike; +/// Westmint parachain definition +#[derive(Debug, Clone, Copy)] +pub struct Westmint; + +// Westmint seems to use the same configuration as all Polkadot-like chains, so we'll use Westend +// primitives here. +impl Chain for Westmint { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Nonce; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + Westend::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + Westend::max_extrinsic_weight() + } +} + +impl Parachain for Westmint { + const PARACHAIN_ID: u32 = WESTMINT_PARACHAIN_ID; +} + /// Name of the parachains pallet at the Westend runtime. pub const PARAS_PALLET_NAME: &str = "Paras"; @@ -37,15 +68,11 @@ pub const WITH_WESTEND_BRIDGE_PARAS_PALLET_NAME: &str = "BridgeWestendParachains /// Corresponds to the `MaxAuthorities` constant value from the Westend runtime configuration. pub const MAX_AUTHORITIES_COUNT: u32 = 100_000; -/// Maximal SCALE-encoded header size (in bytes) at Westend. -/// -/// Let's assume that the largest header is header that enacts new authorities set with -/// `MAX_AUTHORITES_COUNT`. Every authority means 32-byte key and 8-byte weight. Let's also have -/// some fixed reserve for other things (digest, block hash and number, ...) as well. -pub const MAX_HEADER_SIZE: u32 = 4096 + MAX_AUTHORITIES_COUNT * 40; - /// Maximal SCALE-encoded size of parachains headers that are stored at Westend `Paras` pallet. -pub const MAX_NESTED_PARACHAIN_HEAD_SIZE: u32 = MAX_HEADER_SIZE; +/// +/// It includes the block number and state root, so it shall be near 40 bytes, but let's have some +/// reserve. +pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; /// Identifier of Westmint parachain at the Westend relay chain. pub const WESTMINT_PARACHAIN_ID: u32 = 2000; diff --git a/primitives/chain-wococo/src/lib.rs b/primitives/chain-wococo/src/lib.rs index 0436b03c036..1cf666c7f96 100644 --- a/primitives/chain-wococo/src/lib.rs +++ b/primitives/chain-wococo/src/lib.rs @@ -20,8 +20,7 @@ pub use bp_polkadot_core::*; pub use bp_rococo::{ - SS58Prefix, MAX_AUTHORITIES_COUNT, MAX_HEADER_SIZE, MAX_NESTED_PARACHAIN_HEAD_SIZE, - PARAS_PALLET_NAME, + SS58Prefix, MAX_AUTHORITIES_COUNT, MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE, PARAS_PALLET_NAME, }; use bp_runtime::decl_bridge_finality_runtime_apis; diff --git a/primitives/header-chain/src/lib.rs b/primitives/header-chain/src/lib.rs index 2b4e0802a5a..dffa7f7dc6e 100644 --- a/primitives/header-chain/src/lib.rs +++ b/primitives/header-chain/src/lib.rs @@ -20,7 +20,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use bp_runtime::{BasicOperatingMode, Chain, HashOf, HasherOf, HeaderOf, StorageProofChecker}; -use codec::{Codec, Decode, Encode, EncodeLike}; +use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}; use core::{clone::Clone, cmp::Eq, default::Default, fmt::Debug}; use frame_support::PalletError; use scale_info::TypeInfo; @@ -52,20 +52,44 @@ impl From for &'static str { } } +/// Header data that we're storing on-chain. +/// +/// Even though we may store full header, our applications (XCM) only use couple of header +/// fields. Extracting those values makes on-chain storage and PoV smaller, which is good. +#[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] +pub struct StoredHeaderData { + /// Header number. + pub number: Number, + /// Header state root. + pub state_root: Hash, +} + +/// Stored header data builder. +pub trait StoredHeaderDataBuilder { + /// Build header data from self. + fn build(&self) -> StoredHeaderData; +} + +impl StoredHeaderDataBuilder for H { + fn build(&self) -> StoredHeaderData { + StoredHeaderData { number: *self.number(), state_root: *self.state_root() } + } +} + /// Substrate header chain, abstracted from the way it is stored. pub trait HeaderChain { - /// Returns finalized header by its hash. - fn finalized_header(hash: HashOf) -> Option>; + /// Returns state (storage) root of given finalized header. + fn finalized_header_state_root(header_hash: HashOf) -> Option>; /// Parse storage proof using finalized header. fn parse_finalized_storage_proof( - hash: HashOf, + header_hash: HashOf, storage_proof: StorageProof, parse: impl FnOnce(StorageProofChecker>) -> R, ) -> Result { - let header = Self::finalized_header(hash).ok_or(HeaderChainError::UnknownHeader)?; - let storage_proof_checker = - bp_runtime::StorageProofChecker::new(*header.state_root(), storage_proof) - .map_err(|_| HeaderChainError::StorageRootMismatch)?; + let state_root = Self::finalized_header_state_root(header_hash) + .ok_or(HeaderChainError::UnknownHeader)?; + let storage_proof_checker = bp_runtime::StorageProofChecker::new(state_root, storage_proof) + .map_err(|_| HeaderChainError::StorageRootMismatch)?; Ok(parse(storage_proof_checker)) } @@ -145,3 +169,18 @@ impl ConsensusLogReader for GrandpaConsensusLogReader { GrandpaConsensusLogReader::::find_authorities_change(digest).is_some() } } + +/// A minimized version of `pallet-bridge-grandpa::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeGrandpaCall { + /// `pallet-bridge-grandpa::Call::submit_finality_proof` + #[codec(index = 0)] + submit_finality_proof(Box
, justification::GrandpaJustification
), + /// `pallet-bridge-grandpa::Call::initialize` + #[codec(index = 1)] + initialize(InitializationData
), +} + +/// The `BridgeGrandpaCall` used by a chain. +pub type BridgeGrandpaCallOf = BridgeGrandpaCall>; diff --git a/primitives/messages/Cargo.toml b/primitives/messages/Cargo.toml index 4899a1f22da..f54090893a6 100644 --- a/primitives/messages/Cargo.toml +++ b/primitives/messages/Cargo.toml @@ -8,7 +8,6 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive", "bit-vec"] } -impl-trait-for-tuples = "0.2" scale-info = { version = "2.1.1", default-features = false, features = ["bit-vec", "derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } diff --git a/primitives/messages/src/lib.rs b/primitives/messages/src/lib.rs index daaa2800988..61d475cb46c 100644 --- a/primitives/messages/src/lib.rs +++ b/primitives/messages/src/lib.rs @@ -24,7 +24,9 @@ use bp_runtime::{BasicOperatingMode, OperatingMode}; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::RuntimeDebug; use scale_info::TypeInfo; -use sp_std::{collections::vec_deque::VecDeque, prelude::*}; +use source_chain::RelayersRewards; +use sp_core::TypeId; +use sp_std::{collections::vec_deque::VecDeque, ops::RangeInclusive, prelude::*}; pub mod source_chain; pub mod storage_keys; @@ -65,8 +67,27 @@ impl OperatingMode for MessagesOperatingMode { } } -/// Lane identifier. -pub type LaneId = [u8; 4]; +/// Lane id which implements `TypeId`. +#[derive( + Clone, Copy, Decode, Default, Encode, Eq, Ord, PartialOrd, PartialEq, TypeInfo, MaxEncodedLen, +)] +pub struct LaneId(pub [u8; 4]); + +impl core::fmt::Debug for LaneId { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(fmt) + } +} + +impl AsRef<[u8]> for LaneId { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl TypeId for LaneId { + const TYPE_ID: [u8; 4] = *b"blan"; +} /// Message nonce. Valid messages will never have 0 nonce. pub type MessageNonce = u64; @@ -349,6 +370,39 @@ pub fn total_unrewarded_messages( } } +/// Calculate the number of messages that the relayers have delivered. +pub fn calc_relayers_rewards( + messages_relayers: VecDeque>, + received_range: &RangeInclusive, +) -> RelayersRewards +where + AccountId: sp_std::cmp::Ord, +{ + // remember to reward relayers that have delivered messages + // this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain + let mut relayers_rewards = RelayersRewards::new(); + for entry in messages_relayers { + let nonce_begin = sp_std::cmp::max(entry.messages.begin, *received_range.start()); + let nonce_end = sp_std::cmp::min(entry.messages.end, *received_range.end()); + if nonce_end >= nonce_begin { + *relayers_rewards.entry(entry.relayer).or_default() += nonce_end - nonce_begin + 1; + } + } + relayers_rewards +} + +/// A minimized version of `pallet-bridge-messages::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeMessagesCall { + /// `pallet-bridge-messages::Call::receive_messages_proof` + #[codec(index = 2)] + receive_messages_proof(AccountId, MessagesProof, u32, Weight), + /// `pallet-bridge-messages::Call::receive_messages_delivery_proof` + #[codec(index = 3)] + receive_messages_delivery_proof(MessagesDeliveryProof, UnrewardedRelayersState), +} + #[cfg(test)] mod tests { use super::*; @@ -397,11 +451,7 @@ mod tests { let difference = (expected_size.unwrap() as f64 - actual_size as f64).abs(); assert!( difference / (std::cmp::min(actual_size, expected_size.unwrap()) as f64) < 0.1, - "Too large difference between actual ({}) and expected ({:?}) inbound lane data size. Test case: {}+{}", - actual_size, - expected_size, - relayer_entries, - messages_count, + "Too large difference between actual ({actual_size}) and expected ({expected_size:?}) inbound lane data size. Test case: {relayer_entries}+{messages_count}", ); } } @@ -415,4 +465,9 @@ mod tests { assert!(delivered_messages.contains_message(150)); assert!(!delivered_messages.contains_message(151)); } + + #[test] + fn lane_id_debug_format_matches_inner_array_format() { + assert_eq!(format!("{:?}", LaneId([0, 0, 0, 0])), format!("{:?}", [0, 0, 0, 0]),); + } } diff --git a/primitives/messages/src/source_chain.rs b/primitives/messages/src/source_chain.rs index 1cac449ee63..d5d7549099c 100644 --- a/primitives/messages/src/source_chain.rs +++ b/primitives/messages/src/source_chain.rs @@ -88,16 +88,9 @@ pub trait LaneMessageVerifier { ) -> Result<(), Self::Error>; } -/// Message delivery payment. It is called as a part of submit-message transaction. Transaction -/// submitter is paying (in source chain tokens/assets) for: -/// -/// 1) submit-message-transaction-fee itself. This fee is not included in the -/// `delivery_and_dispatch_fee` and is withheld by the regular transaction payment mechanism; -/// 2) message-delivery-transaction-fee. It is submitted to the target node by relayer; -/// 3) message-dispatch fee. It is paid by relayer for processing message by target chain; -/// 4) message-receiving-delivery-transaction-fee. It is submitted to the source node -/// by relayer. -pub trait MessageDeliveryAndDispatchPayment { +/// Manages payments that are happening at the source chain during delivery confirmation +/// transaction. +pub trait DeliveryConfirmationPayments { /// Error type. type Error: Debug + Into<&'static str>; @@ -105,7 +98,7 @@ pub trait MessageDeliveryAndDispatchPayment { /// /// The implementation may also choose to pay reward to the `confirmation_relayer`, which is /// a relayer that has submitted delivery confirmation transaction. - fn pay_relayers_rewards( + fn pay_reward( lane_id: LaneId, messages_relayers: VecDeque>, confirmation_relayer: &AccountId, @@ -113,15 +106,16 @@ pub trait MessageDeliveryAndDispatchPayment { ); } -impl MessageDeliveryAndDispatchPayment for () { +impl DeliveryConfirmationPayments for () { type Error = &'static str; - fn pay_relayers_rewards( + fn pay_reward( _lane_id: LaneId, _messages_relayers: VecDeque>, _confirmation_relayer: &AccountId, _received_range: &RangeInclusive, ) { + // this implementation is not rewarding relayers at all } } @@ -202,12 +196,10 @@ impl LaneMessageVerifier for Forbi } } -impl MessageDeliveryAndDispatchPayment - for ForbidOutboundMessages -{ +impl DeliveryConfirmationPayments for ForbidOutboundMessages { type Error = &'static str; - fn pay_relayers_rewards( + fn pay_reward( _lane_id: LaneId, _messages_relayers: VecDeque>, _confirmation_relayer: &AccountId, diff --git a/primitives/messages/src/storage_keys.rs b/primitives/messages/src/storage_keys.rs index 4db11edec34..4edf9828cfd 100644 --- a/primitives/messages/src/storage_keys.rs +++ b/primitives/messages/src/storage_keys.rs @@ -56,7 +56,7 @@ pub fn outbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey bp_runtime::storage_map_final_key::( pallet_prefix, OUTBOUND_LANES_MAP_NAME, - lane, + &lane.encode(), ) } @@ -65,7 +65,7 @@ pub fn inbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { bp_runtime::storage_map_final_key::( pallet_prefix, INBOUND_LANES_MAP_NAME, - lane, + &lane.encode(), ) } @@ -91,7 +91,7 @@ mod tests { fn storage_message_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted messages proofs. - let storage_key = message_key("BridgeMessages", b"test", 42).0; + let storage_key = message_key("BridgeMessages", &LaneId(*b"test"), 42).0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000").to_vec(), @@ -104,7 +104,7 @@ mod tests { fn outbound_lane_data_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted outbound lane state proofs. - let storage_key = outbound_lane_data_key("BridgeMessages", b"test").0; + let storage_key = outbound_lane_data_key("BridgeMessages", &LaneId(*b"test")).0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1f44a8995dd50b6657a037a7839304535b74657374").to_vec(), @@ -117,7 +117,7 @@ mod tests { fn inbound_lane_data_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted inbound lane state proofs. - let storage_key = inbound_lane_data_key("BridgeMessages", b"test").0; + let storage_key = inbound_lane_data_key("BridgeMessages", &LaneId(*b"test")).0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(), diff --git a/primitives/messages/src/target_chain.rs b/primitives/messages/src/target_chain.rs index 9c6b60e1e15..1b285b065aa 100644 --- a/primitives/messages/src/target_chain.rs +++ b/primitives/messages/src/target_chain.rs @@ -16,7 +16,7 @@ //! Primitives of messages module, that are used on the target chain. -use crate::{LaneId, Message, MessageKey, MessagePayload, OutboundLaneData}; +use crate::{LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData}; use bp_runtime::{messages::MessageDispatchResult, Size}; use codec::{Decode, Encode, Error as CodecError}; @@ -112,6 +112,25 @@ pub trait MessageDispatch { ) -> MessageDispatchResult; } +/// Manages payments that are happening at the target chain during message delivery transaction. +pub trait DeliveryPayments { + /// Error type. + type Error: Debug + Into<&'static str>; + + /// Pay rewards for delivering messages to the given relayer. + /// + /// This method is called during message delivery transaction which has been submitted + /// by the `relayer`. The transaction brings `total_messages` messages but only + /// `valid_messages` have been accepted. The post-dispatch transaction weight is the + /// `actual_weight`. + fn pay_reward( + relayer: AccountId, + total_messages: MessageNonce, + valid_messages: MessageNonce, + actual_weight: Weight, + ); +} + impl Default for ProvedLaneMessages { fn default() -> Self { ProvedLaneMessages { lane_state: None, messages: Vec::new() } @@ -130,6 +149,19 @@ impl From for DispatchMessageData DeliveryPayments for () { + type Error = &'static str; + + fn pay_reward( + _relayer: AccountId, + _total_messages: MessageNonce, + _valid_messages: MessageNonce, + _actual_weight: Weight, + ) { + // this implementation is not rewarding relayer at all + } +} + /// Structure that may be used in place of `SourceHeaderChain` and `MessageDispatch` on chains, /// where inbound messages are forbidden. pub struct ForbidInboundMessages; @@ -162,10 +194,6 @@ impl MessageDispatch for ForbidInboundMessages { _: &AccountId, _: DispatchMessage, ) -> MessageDispatchResult { - MessageDispatchResult { - unspent_weight: Weight::zero(), - dispatch_fee_paid_during_dispatch: false, - dispatch_level_result: (), - } + MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } } } diff --git a/primitives/parachains/Cargo.toml b/primitives/parachains/Cargo.toml index 0aa2adfead1..333f7ad647a 100644 --- a/primitives/parachains/Cargo.toml +++ b/primitives/parachains/Cargo.toml @@ -8,10 +8,12 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } +impl-trait-for-tuples = "0.2" scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } # Bridge dependencies +bp-header-chain = { path = "../header-chain", default-features = false } bp-polkadot-core = { path = "../polkadot-core", default-features = false } bp-runtime = { path = "../runtime", default-features = false } @@ -19,14 +21,19 @@ bp-runtime = { path = "../runtime", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-polkadot-core/std", "bp-runtime/std", "codec/std", "frame-support/std", "scale-info/std", "sp-core/std", + "sp-runtime/std", + "sp-std/std", ] diff --git a/primitives/parachains/src/lib.rs b/primitives/parachains/src/lib.rs index f2edebf8a22..6b38d648d79 100644 --- a/primitives/parachains/src/lib.rs +++ b/primitives/parachains/src/lib.rs @@ -18,15 +18,22 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub use bp_header_chain::StoredHeaderData; + use bp_polkadot_core::{ - parachains::{ParaHash, ParaHead, ParaId}, - BlockNumber as RelayBlockNumber, + parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}, + BlockNumber as RelayBlockNumber, Hash as RelayBlockHash, +}; +use bp_runtime::{ + BlockNumberOf, Chain, HashOf, HeaderOf, Parachain, StorageDoubleMapKeyProvider, + StorageMapKeyProvider, }; -use bp_runtime::{StorageDoubleMapKeyProvider, StorageMapKeyProvider}; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{Blake2_128Concat, RuntimeDebug, Twox64Concat}; use scale_info::TypeInfo; use sp_core::storage::StorageKey; +use sp_runtime::traits::Header as HeaderT; +use sp_std::{marker::PhantomData, prelude::*}; /// Best known parachain head hash. #[derive(Clone, Decode, Encode, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] @@ -86,5 +93,73 @@ impl StorageDoubleMapKeyProvider for ImportedParaHeadsKeyProvider { type Key1 = ParaId; type Hasher2 = Blake2_128Concat; type Key2 = ParaHash; - type Value = ParaHead; + type Value = ParaStoredHeaderData; +} + +/// Stored data of the parachain head. It is encoded version of the +/// `bp_runtime::StoredHeaderData` structure. +/// +/// We do not know exact structure of the parachain head, so we always store encoded version +/// of the `bp_runtime::StoredHeaderData`. It is only decoded when we talk about specific parachain. +#[derive(Clone, Decode, Encode, PartialEq, RuntimeDebug, TypeInfo)] +pub struct ParaStoredHeaderData(pub Vec); + +impl ParaStoredHeaderData { + /// Decode stored parachain head data. + pub fn decode_parachain_head_data( + &self, + ) -> Result, HashOf>, codec::Error> { + StoredHeaderData::, HashOf>::decode(&mut &self.0[..]) + } +} + +/// Stored parachain head data builder. +pub trait ParaStoredHeaderDataBuilder { + /// Try to build head data from self. + fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option; +} + +/// Helper for using single parachain as `ParaStoredHeaderDataBuilder`. +pub struct SingleParaStoredHeaderDataBuilder(PhantomData); + +impl ParaStoredHeaderDataBuilder for SingleParaStoredHeaderDataBuilder { + fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option { + if para_id == ParaId(C::PARACHAIN_ID) { + let header = HeaderOf::::decode(&mut ¶_head.0[..]).ok()?; + return Some(ParaStoredHeaderData( + StoredHeaderData { number: *header.number(), state_root: *header.state_root() } + .encode(), + )) + } + None + } +} + +// Tries to build header data from each tuple member, short-circuiting on first successful one. +#[impl_trait_for_tuples::impl_for_tuples(1, 30)] +#[tuple_types_custom_trait_bound(Parachain)] +impl ParaStoredHeaderDataBuilder for C { + fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option { + for_tuples!( #( + let maybe_para_head = SingleParaStoredHeaderDataBuilder::::try_build(para_id, para_head); + if let Some(maybe_para_head) = maybe_para_head { + return Some(maybe_para_head); + } + )* ); + + None + } +} + +/// A minimized version of `pallet-bridge-parachains::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeParachainCall { + /// `pallet-bridge-parachains::Call::submit_parachain_heads` + #[codec(index = 0)] + submit_parachain_heads( + (RelayBlockNumber, RelayBlockHash), + Vec<(ParaId, ParaHash)>, + ParaHeadsProof, + ), } diff --git a/primitives/polkadot-core/src/lib.rs b/primitives/polkadot-core/src/lib.rs index 9e1f4b0452f..3d4b72fccb9 100644 --- a/primitives/polkadot-core/src/lib.rs +++ b/primitives/polkadot-core/src/lib.rs @@ -18,28 +18,26 @@ use bp_messages::MessageNonce; use bp_runtime::{Chain, EncodedOrDecodedCall, StorageMapKeyProvider}; -use codec::Compact; use frame_support::{ - dispatch::{DispatchClass, Dispatchable}, + dispatch::DispatchClass, parameter_types, weights::{ - constants::{BlockExecutionWeight, WEIGHT_PER_SECOND}, + constants::{BlockExecutionWeight, WEIGHT_REF_TIME_PER_SECOND}, Weight, }, Blake2_128Concat, RuntimeDebug, }; use frame_system::limits; -use scale_info::{StaticTypeInfo, TypeInfo}; use sp_core::{storage::StorageKey, Hasher as HasherT}; use sp_runtime::{ generic, - traits::{BlakeTwo256, DispatchInfoOf, IdentifyAccount, Verify}, - transaction_validity::TransactionValidityError, + traits::{BlakeTwo256, IdentifyAccount, Verify}, MultiAddress, MultiSignature, OpaqueExtrinsic, }; use sp_std::prelude::Vec; // Re-export's to avoid extra substrate dependencies in chain-specific crates. +use bp_runtime::extensions::*; pub use frame_support::{weights::constants::ExtrinsicBaseWeight, Parameter}; pub use sp_runtime::{traits::Convert, Perbill}; @@ -74,7 +72,9 @@ const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); /// /// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. // TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` -pub const MAXIMUM_BLOCK_WEIGHT: Weight = WEIGHT_PER_SECOND.set_proof_size(1_000).saturating_mul(2); +pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_ref_time(WEIGHT_REF_TIME_PER_SECOND) + .set_proof_size(1_000) + .saturating_mul(2); /// All Polkadot-like chains assume that an on-initialize consumes 1 percent of the weight on /// average, hence a single extrinsic will not be allowed to consume more than @@ -118,14 +118,6 @@ parameter_types! { /// Maximal number of messages in single delivery transaction. pub const MAX_MESSAGES_IN_DELIVERY_TRANSACTION: MessageNonce = 128; -/// Maximal number of unrewarded relayer entries at inbound lane. -pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 128; - -// TODO [#438] should be selected keeping in mind: -// finality delay on both chains + reward payout cost + messages throughput. -/// Maximal number of unconfirmed messages at inbound lane. -pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 8192; - /// Maximal number of bytes, included in the signed Polkadot-like transaction apart from the encoded /// call itself. /// @@ -190,55 +182,72 @@ pub type SignedBlock = generic::SignedBlock; pub type Balance = u128; /// Unchecked Extrinsic type. -pub type UncheckedExtrinsic = generic::UncheckedExtrinsic< - AccountAddress, - EncodedOrDecodedCall, - Signature, - SignedExtensions, ->; +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic, Signature, SignedExt>; /// Account address, used by the Polkadot-like chain. pub type Address = MultiAddress; -/// A type of the data encoded as part of the transaction. -pub type SignedExtra = - ((), (), (), (), sp_runtime::generic::Era, Compact, (), Compact); - -/// Parameters which are part of the payload used to produce transaction signature, -/// but don't end up in the transaction itself (i.e. inherent part of the runtime). -pub type AdditionalSigned = ((), u32, u32, Hash, Hash, (), (), ()); - -/// A simplified version of signed extensions meant for producing signed transactions -/// and signed payload in the client code. -#[derive(PartialEq, Eq, Clone, RuntimeDebug, TypeInfo)] -pub struct SignedExtensions { - encode_payload: SignedExtra, - // It may be set to `None` if extensions are decoded. We are never reconstructing transactions - // (and it makes no sense to do that) => decoded version of `SignedExtensions` is only used to - // read fields of `encode_payload`. And when resigning transaction, we're reconstructing - // `SignedExtensions` from the scratch. - additional_signed: Option, - _data: sp_std::marker::PhantomData, -} +/// Polkadot-like chain. +#[derive(RuntimeDebug)] +pub struct PolkadotLike; + +impl Chain for PolkadotLike { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; -impl codec::Encode for SignedExtensions { - fn using_encoded R>(&self, f: F) -> R { - self.encode_payload.using_encoded(f) + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) } -} -impl codec::Decode for SignedExtensions { - fn decode(input: &mut I) -> Result { - SignedExtra::decode(input).map(|encode_payload| SignedExtensions { - encode_payload, - additional_signed: None, - _data: Default::default(), - }) + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) } } -impl SignedExtensions { - pub fn new( +/// Some functionality associated with the default signed extension used by Polkadot and +/// Polkadot-like chains. +pub trait PolkadotSignedExtension { + fn from_params( + spec_version: u32, + transaction_version: u32, + era: bp_runtime::TransactionEraOf, + genesis_hash: Hash, + nonce: Nonce, + tip: Balance, + ) -> Self; + + fn nonce(&self) -> Nonce; + + fn tip(&self) -> Balance; +} + +type DefaultSignedExtra = ( + CheckNonZeroSender, + CheckSpecVersion, + CheckTxVersion, + CheckGenesis, + CheckEra, + CheckNonce, + CheckWeight, + ChargeTransactionPayment, +); + +/// The default signed extension used by Polkadot and Polkadot-like chains. +pub type DefaultSignedExtension = GenericSignedExtension; + +impl PolkadotSignedExtension for DefaultSignedExtension { + fn from_params( spec_version: u32, transaction_version: u32, era: bp_runtime::TransactionEraOf, @@ -246,8 +255,8 @@ impl SignedExtensions { nonce: Nonce, tip: Balance, ) -> Self { - Self { - encode_payload: ( + Self::new( + ( (), // non-zero sender (), // spec version (), // tx version @@ -257,7 +266,7 @@ impl SignedExtensions { (), // Check weight tip.into(), // transaction payment / tip (compact encoding) ), - additional_signed: Some(( + ( (), spec_version, transaction_version, @@ -266,83 +275,79 @@ impl SignedExtensions { (), (), (), - )), - _data: Default::default(), - } + ), + ) } -} -impl SignedExtensions { /// Return signer nonce, used to craft transaction. - pub fn nonce(&self) -> Nonce { - self.encode_payload.5.into() + fn nonce(&self) -> Nonce { + self.payload.5.into() } /// Return transaction tip. - pub fn tip(&self) -> Balance { - self.encode_payload.7.into() + fn tip(&self) -> Balance { + self.payload.7.into() } } -impl sp_runtime::traits::SignedExtension for SignedExtensions -where - Call: codec::Codec + sp_std::fmt::Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, - Call: Dispatchable, -{ - const IDENTIFIER: &'static str = "Not needed."; - - type AccountId = AccountId; - type Call = Call; - type AdditionalSigned = AdditionalSigned; - type Pre = (); - - fn additional_signed( - &self, - ) -> Result { - // we shall not ever see this error in relay, because we are never signing decoded - // transactions. Instead we're constructing and signing new transactions. So the error code - // is kinda random here - self.additional_signed - .ok_or(frame_support::unsigned::TransactionValidityError::Unknown( - frame_support::unsigned::UnknownTransaction::Custom(0xFF), - )) - } - - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result { - Ok(()) +type BridgeSignedExtra = ( + CheckNonZeroSender, + CheckSpecVersion, + CheckTxVersion, + CheckGenesis, + CheckEra, + CheckNonce, + CheckWeight, + ChargeTransactionPayment, + BridgeRejectObsoleteHeadersAndMessages, +); + +/// The default signed extension used by Polkadot and Polkadot-like chains with bridging. +pub type BridgeSignedExtension = GenericSignedExtension; + +impl PolkadotSignedExtension for BridgeSignedExtension { + fn from_params( + spec_version: u32, + transaction_version: u32, + era: bp_runtime::TransactionEraOf, + genesis_hash: Hash, + nonce: Nonce, + tip: Balance, + ) -> Self { + Self::new( + ( + (), // non-zero sender + (), // spec version + (), // tx version + (), // genesis + era.frame_era(), // era + nonce.into(), // nonce (compact encoding) + (), // Check weight + tip.into(), // transaction payment / tip (compact encoding) + (), // bridge reject obsolete headers and msgs + ), + ( + (), + spec_version, + transaction_version, + genesis_hash, + era.signed_payload(genesis_hash), + (), + (), + (), + (), + ), + ) } -} - -/// Polkadot-like chain. -#[derive(RuntimeDebug)] -pub struct PolkadotLike; - -impl Chain for PolkadotLike { - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - type AccountId = AccountId; - type Balance = Balance; - type Index = Index; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) + /// Return signer nonce, used to craft transaction. + fn nonce(&self) -> Nonce { + self.payload.5.into() } - fn max_extrinsic_weight() -> Weight { - BlockWeights::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) + /// Return transaction tip. + fn tip(&self) -> Balance { + self.payload.7.into() } } diff --git a/primitives/polkadot-core/src/parachains.rs b/primitives/polkadot-core/src/parachains.rs index 51fcd59cae1..e8f68dd2a9a 100644 --- a/primitives/polkadot-core/src/parachains.rs +++ b/primitives/polkadot-core/src/parachains.rs @@ -66,9 +66,7 @@ impl From for ParaId { /// /// This is an equivalent of the `polkadot_parachain::HeadData`. /// -/// The parachain head means (at least in Cumulus) a SCALE-encoded parachain header. Keep in mind -/// that in Polkadot it is twice-encoded (so `header.encode().encode()`). We'll also do it to keep -/// it binary-compatible (implies hash-compatibility) with other parachain pallets. +/// The parachain head means (at least in Cumulus) a SCALE-encoded parachain header. #[derive( PartialEq, Eq, Clone, PartialOrd, Ord, Encode, Decode, RuntimeDebug, TypeInfo, Default, )] diff --git a/primitives/relayers/Cargo.toml b/primitives/relayers/Cargo.toml index 4dc09f8d126..4f893f9f83e 100644 --- a/primitives/relayers/Cargo.toml +++ b/primitives/relayers/Cargo.toml @@ -19,6 +19,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [dev-dependencies] +bp-rialto = { path = "../chain-rialto" } hex = "0.4" hex-literal = "0.3" diff --git a/primitives/relayers/src/lib.rs b/primitives/relayers/src/lib.rs index bd456d36310..d00b5f626e4 100644 --- a/primitives/relayers/src/lib.rs +++ b/primitives/relayers/src/lib.rs @@ -20,6 +20,10 @@ #![cfg_attr(not(feature = "std"), no_std)] use bp_messages::LaneId; +use sp_runtime::{ + codec::{Decode, Encode}, + traits::AccountIdConversion, +}; use sp_std::{fmt::Debug, marker::PhantomData}; /// Reward payment procedure. @@ -31,20 +35,56 @@ pub trait PaymentProcedure { fn pay_reward(relayer: &Relayer, lane_id: LaneId, reward: Reward) -> Result<(), Self::Error>; } -/// Reward payment procedure that is simply minting given amount of tokens. -pub struct MintReward(PhantomData<(T, Relayer)>); +/// Reward payment procedure that does `balances::transfer` call from the account, derived from +/// given lane. +pub struct PayLaneRewardFromAccount(PhantomData<(T, Relayer)>); -impl PaymentProcedure for MintReward +impl PayLaneRewardFromAccount where - T: frame_support::traits::fungible::Mutate, + Relayer: Decode + Encode, +{ + /// Return account that pay rewards for serving given lane. + pub fn lane_rewards_account(lane_id: LaneId) -> Relayer { + lane_id.into_sub_account_truncating(b"bridge-lane") + } +} + +impl PaymentProcedure for PayLaneRewardFromAccount +where + T: frame_support::traits::fungible::Transfer, + Relayer: Decode + Encode, { type Error = sp_runtime::DispatchError; fn pay_reward( relayer: &Relayer, - _lane_id: LaneId, + lane_id: LaneId, reward: T::Balance, ) -> Result<(), Self::Error> { - T::mint_into(relayer, reward) + T::transfer(&Self::lane_rewards_account(lane_id), relayer, reward, false).map(drop) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn lanes_are_using_different_accounts() { + assert_eq!( + PayLaneRewardFromAccount::<(), bp_rialto::AccountId>::lane_rewards_account(LaneId([ + 0, 0, 0, 0 + ])), + hex_literal::hex!("626c616e000000006272696467652d6c616e6500000000000000000000000000") + .into(), + ); + + assert_eq!( + PayLaneRewardFromAccount::<(), bp_rialto::AccountId>::lane_rewards_account(LaneId([ + 0, 0, 0, 1 + ])), + hex_literal::hex!("626c616e000000016272696467652d6c616e6500000000000000000000000000") + .into(), + ); } } diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index 79f2b9fe03c..f8d698696de 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -9,6 +9,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } hash-db = { version = "0.15.2", default-features = false } +impl-trait-for-tuples = "0.2.2" num-traits = { version = "0.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } diff --git a/primitives/runtime/src/chain.rs b/primitives/runtime/src/chain.rs index d4a8d5aa020..94b3a193c58 100644 --- a/primitives/runtime/src/chain.rs +++ b/primitives/runtime/src/chain.rs @@ -21,7 +21,7 @@ use num_traits::{AsPrimitive, Bounded, CheckedSub, Saturating, SaturatingAdd, Ze use sp_runtime::{ traits::{ AtLeast32Bit, AtLeast32BitUnsigned, Hash as HashT, Header as HeaderT, MaybeDisplay, - MaybeMallocSizeOf, MaybeSerialize, MaybeSerializeDeserialize, Member, SimpleBitOps, Verify, + MaybeSerialize, MaybeSerializeDeserialize, Member, SimpleBitOps, Verify, }, FixedPointOperand, }; @@ -107,14 +107,10 @@ pub trait Chain: Send + Sync + 'static { + MaybeDisplay + AtLeast32BitUnsigned + FromStr - + MaybeMallocSizeOf + AsPrimitive + Default + Saturating - + MaxEncodedLen - // original `sp_runtime::traits::Header::BlockNumber` doesn't have this trait, but - // `sp_runtime::generic::Era` requires block number -> `u64` conversion. - + Into; + + MaxEncodedLen; /// A type that fulfills the abstract idea of what a Substrate hash is. // Constraits come from the associated Hash type of `sp_runtime::traits::Header` @@ -131,7 +127,6 @@ pub trait Chain: Send + Sync + 'static { + SimpleBitOps + AsRef<[u8]> + AsMut<[u8]> - + MaybeMallocSizeOf + MaxEncodedLen; /// A type that fulfills the abstract idea of what a Substrate hasher (a type @@ -195,12 +190,51 @@ pub trait Chain: Send + Sync + 'static { fn max_extrinsic_weight() -> Weight; } +/// A trait that provides the type of the underlying chain. +pub trait UnderlyingChainProvider { + /// Underlying chain type. + type Chain: Chain; +} + +impl Chain for T +where + T: Send + Sync + 'static + UnderlyingChainProvider, +{ + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hasher = ::Hasher; + type Header = ::Header; + type AccountId = ::AccountId; + type Balance = ::Balance; + type Index = ::Index; + type Signature = ::Signature; + + fn max_extrinsic_size() -> u32 { + ::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + ::max_extrinsic_weight() + } +} + /// Minimal parachain representation that may be used from no_std environment. pub trait Parachain: Chain { /// Parachain identifier. const PARACHAIN_ID: u32; } +impl Parachain for T +where + T: Chain + UnderlyingChainProvider, + ::Chain: Parachain, +{ + const PARACHAIN_ID: u32 = <::Chain as Parachain>::PARACHAIN_ID; +} + +/// Underlying chain type. +pub type UnderlyingChainOf = ::Chain; + /// Block number used by the chain. pub type BlockNumberOf = ::BlockNumber; @@ -272,8 +306,6 @@ macro_rules! decl_bridge_finality_runtime_apis { /// - `ToOutboundLaneApi` /// - `FromInboundLaneApi` /// - constants that are stringified names of runtime API methods: -/// - `TO__ESTIMATE_MESSAGE_FEE_METHOD` -/// - `TO__MESSAGE_DETAILS_METHOD` /// - `FROM__MESSAGE_DETAILS_METHOD`, /// The name of the chain has to be specified in snake case (e.g. `rialto_parachain`). #[macro_export] @@ -283,10 +315,6 @@ macro_rules! decl_bridge_messages_runtime_apis { mod [<$chain _messages_api>] { use super::*; - /// Name of the `ToOutboundLaneApi::estimate_message_delivery_and_dispatch_fee` runtime - /// method. - pub const []: &str = - stringify!([]); /// Name of the `ToOutboundLaneApi::message_details` runtime method. pub const []: &str = stringify!([]); diff --git a/primitives/runtime/src/extensions.rs b/primitives/runtime/src/extensions.rs new file mode 100644 index 00000000000..287f484db4a --- /dev/null +++ b/primitives/runtime/src/extensions.rs @@ -0,0 +1,136 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives that may be used for creating signed extensions for indirect runtimes. + +use crate::{BalanceOf, HashOf}; +use codec::{Compact, Decode, Encode}; +use impl_trait_for_tuples::impl_for_tuples; +use scale_info::{StaticTypeInfo, TypeInfo}; +use sp_runtime::{ + traits::{DispatchInfoOf, SignedExtension}, + transaction_validity::TransactionValidityError, +}; +use sp_std::{fmt::Debug, marker::PhantomData}; + +/// Trait that describes some properties of a `SignedExtension` that are needed in order to send a +/// transaction to the chain. +pub trait SignedExtensionSchema: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo { + /// A type of the data encoded as part of the transaction. + type Payload: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo; + /// Parameters which are part of the payload used to produce transaction signature, + /// but don't end up in the transaction itself (i.e. inherent part of the runtime). + type AdditionalSigned: Encode + Debug + Eq + Clone + StaticTypeInfo; +} + +// An implementation of `SignedExtensionSchema` using generic params. +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo)] +pub struct GenericSignedExtensionSchema(PhantomData<(P, S)>); + +impl SignedExtensionSchema for GenericSignedExtensionSchema +where + P: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo, + S: Encode + Debug + Eq + Clone + StaticTypeInfo, +{ + type Payload = P; + type AdditionalSigned = S; +} + +/// The `SignedExtensionSchema` for `frame_system::CheckNonZeroSender`. +pub type CheckNonZeroSender = GenericSignedExtensionSchema<(), ()>; + +/// The `SignedExtensionSchema` for `frame_system::CheckSpecVersion`. +pub type CheckSpecVersion = GenericSignedExtensionSchema<(), u32>; + +/// The `SignedExtensionSchema` for `frame_system::CheckTxVersion`. +pub type CheckTxVersion = GenericSignedExtensionSchema<(), u32>; + +/// The `SignedExtensionSchema` for `frame_system::CheckGenesis`. +pub type CheckGenesis = GenericSignedExtensionSchema<(), HashOf>; + +/// The `SignedExtensionSchema` for `frame_system::CheckEra`. +pub type CheckEra = GenericSignedExtensionSchema>; + +/// The `SignedExtensionSchema` for `frame_system::CheckNonce`. +pub type CheckNonce = GenericSignedExtensionSchema, ()>; + +/// The `SignedExtensionSchema` for `frame_system::CheckWeight`. +pub type CheckWeight = GenericSignedExtensionSchema<(), ()>; + +/// The `SignedExtensionSchema` for `pallet_transaction_payment::ChargeTransactionPayment`. +pub type ChargeTransactionPayment = GenericSignedExtensionSchema>, ()>; + +/// The `SignedExtensionSchema` for `BridgeRejectObsoleteHeadersAndMessages`. +pub type BridgeRejectObsoleteHeadersAndMessages = GenericSignedExtensionSchema<(), ()>; + +#[impl_for_tuples(1, 12)] +impl SignedExtensionSchema for Tuple { + for_tuples!( type Payload = ( #( Tuple::Payload ),* ); ); + for_tuples!( type AdditionalSigned = ( #( Tuple::AdditionalSigned ),* ); ); +} + +/// A simplified version of signed extensions meant for producing signed transactions +/// and signed payloads in the client code. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub struct GenericSignedExtension { + pub payload: S::Payload, + #[codec(skip)] + // It may be set to `None` if extensions are decoded. We are never reconstructing transactions + // (and it makes no sense to do that) => decoded version of `SignedExtensions` is only used to + // read fields of the `payload`. And when resigning transaction, we're reconstructing + // `SignedExtensions` from the scratch. + additional_signed: Option, +} + +impl GenericSignedExtension { + pub fn new(payload: S::Payload, additional_signed: S::AdditionalSigned) -> Self { + Self { payload, additional_signed: Some(additional_signed) } + } +} + +impl SignedExtension for GenericSignedExtension +where + S: SignedExtensionSchema, + S::Payload: Send + Sync, + S::AdditionalSigned: Send + Sync, +{ + const IDENTIFIER: &'static str = "Not needed."; + type AccountId = (); + type Call = (); + type AdditionalSigned = S::AdditionalSigned; + type Pre = (); + + fn additional_signed(&self) -> Result { + // we shall not ever see this error in relay, because we are never signing decoded + // transactions. Instead we're constructing and signing new transactions. So the error code + // is kinda random here + self.additional_signed.clone().ok_or( + frame_support::unsigned::TransactionValidityError::Unknown( + frame_support::unsigned::UnknownTransaction::Custom(0xFF), + ), + ) + } + + fn pre_dispatch( + self, + _who: &Self::AccountId, + _call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + Ok(()) + } +} diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 75d75c2beff..5eb1de2e3ac 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -27,12 +27,13 @@ use frame_system::RawOrigin; use scale_info::TypeInfo; use sp_core::{hash::H256, storage::StorageKey}; use sp_io::hashing::blake2_256; -use sp_runtime::traits::{BadOrigin, Header as HeaderT}; +use sp_runtime::traits::{BadOrigin, Header as HeaderT, UniqueSaturatedInto}; use sp_std::{convert::TryFrom, fmt::Debug, vec, vec::Vec}; pub use chain::{ AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf, - HasherOf, HeaderOf, IndexOf, Parachain, SignatureOf, TransactionEraOf, + HasherOf, HeaderOf, IndexOf, Parachain, SignatureOf, TransactionEraOf, UnderlyingChainOf, + UnderlyingChainProvider, }; pub use frame_support::storage::storage_prefix as storage_value_final_key; use num_traits::{CheckedSub, One}; @@ -46,6 +47,7 @@ pub use storage_types::BoundedStorageValue; #[cfg(feature = "std")] pub use storage_proof::craft_valid_storage_proof; +pub mod extensions; pub mod messages; mod chain; @@ -96,10 +98,37 @@ pub const ROOT_ACCOUNT_DERIVATION_PREFIX: &[u8] = b"pallet-bridge/account-deriva /// Generic header Id. #[derive( - RuntimeDebug, Default, Clone, Encode, Decode, Copy, Eq, Hash, PartialEq, PartialOrd, Ord, + RuntimeDebug, + Default, + Clone, + Encode, + Decode, + Copy, + Eq, + Hash, + MaxEncodedLen, + PartialEq, + PartialOrd, + Ord, + TypeInfo, )] pub struct HeaderId(pub Number, pub Hash); +impl HeaderId { + /// Return header number. + pub fn number(&self) -> Number { + self.0 + } + + /// Return header hash. + pub fn hash(&self) -> Hash { + self.1 + } +} + +/// Header id used by the chain. +pub type HeaderIdOf = HeaderId, BlockNumberOf>; + /// Generic header id provider. pub trait HeaderIdProvider { // Get the header id. @@ -201,7 +230,9 @@ pub enum TransactionEra { Mortal(HeaderId, u32), } -impl, BlockHash: Copy> TransactionEra { +impl, BlockHash: Copy> + TransactionEra +{ /// Prepare transaction era, based on mortality period and current best block number. pub fn new( best_block_id: HeaderId, @@ -229,8 +260,10 @@ impl, BlockHash: Copy> TransactionEra sp_runtime::generic::Era { match *self { TransactionEra::Immortal => sp_runtime::generic::Era::immortal(), + // `unique_saturated_into` is fine here - mortality `u64::MAX` is not something we + // expect to see on any chain TransactionEra::Mortal(header_id, period) => - sp_runtime::generic::Era::mortal(period as _, header_id.0.into()), + sp_runtime::generic::Era::mortal(period as _, header_id.0.unique_saturated_into()), } } diff --git a/primitives/runtime/src/messages.rs b/primitives/runtime/src/messages.rs index 0bb09090df9..9f7c8ab5ca4 100644 --- a/primitives/runtime/src/messages.rs +++ b/primitives/runtime/src/messages.rs @@ -30,10 +30,6 @@ pub struct MessageDispatchResult { /// the weight, declared by the message sender; /// 2) if message has not been dispatched at all. pub unspent_weight: Weight, - /// Whether the message dispatch fee has been paid during dispatch. This will be true if your - /// configuration supports pay-dispatch-fee-at-target-chain option and message sender has - /// enabled this option. - pub dispatch_fee_paid_during_dispatch: bool, /// Fine-grained result of single message dispatch (for better diagnostic purposes) pub dispatch_level_result: DispatchLevelResult, } diff --git a/primitives/test-utils/src/keyring.rs b/primitives/test-utils/src/keyring.rs index f827c729434..8b7a2b39258 100644 --- a/primitives/test-utils/src/keyring.rs +++ b/primitives/test-utils/src/keyring.rs @@ -90,5 +90,5 @@ pub fn test_keyring() -> Vec<(Account, AuthorityWeight)> { /// Get a list of "unique" accounts. pub fn accounts(len: u16) -> Vec { - (0..len).into_iter().map(Account).collect() + (0..len).map(Account).collect() } diff --git a/primitives/test-utils/src/lib.rs b/primitives/test-utils/src/lib.rs index 186d192014b..e0d06ce9970 100644 --- a/primitives/test-utils/src/lib.rs +++ b/primitives/test-utils/src/lib.rs @@ -308,7 +308,7 @@ mod tests { fn print_xcm(xcm: &VersionedXcm) { println!("-----------------"); - println!("xcm (plain): {:?}", xcm); + println!("xcm (plain): {xcm:?}"); println!("xcm (bytes): {:?}", xcm.encode()); println!("xcm (hex): {:?}", hexdisplay::HexDisplay::from(&xcm.encode())); } diff --git a/relays/bin-substrate/Cargo.toml b/relays/bin-substrate/Cargo.toml index 9ae6a98868e..4be812be86a 100644 --- a/relays/bin-substrate/Cargo.toml +++ b/relays/bin-substrate/Cargo.toml @@ -15,27 +15,23 @@ hex = "0.4" log = "0.4.17" num-format = "0.4" num-traits = "0.2" +rbtag = "0.3" structopt = "0.3" +signal-hook = "0.3.14" +signal-hook-async-std = "0.2.2" strum = { version = "0.21.0", features = ["derive"] } # Bridge dependencies - -bp-bridge-hub-rococo = { path = "../../primitives/chain-bridge-hub-rococo" } -bp-bridge-hub-wococo = { path = "../../primitives/chain-bridge-hub-wococo" } bp-header-chain = { path = "../../primitives/header-chain" } bp-messages = { path = "../../primitives/messages" } +bp-parachains = { path = "../../primitives/parachains" } bp-millau = { path = "../../primitives/chain-millau" } bp-polkadot-core = { path = "../../primitives/polkadot-core" } bp-rialto = { path = "../../primitives/chain-rialto" } bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" } -bp-rococo = { path = "../../primitives/chain-rococo" } bp-runtime = { path = "../../primitives/runtime" } -bp-westend = { path = "../../primitives/chain-westend" } -bp-wococo = { path = "../../primitives/chain-wococo" } bridge-runtime-common = { path = "../../bin/runtime-common" } -messages-relay = { path = "../messages" } millau-runtime = { path = "../../bin/millau/runtime" } -pallet-bridge-messages = { path = "../../modules/messages" } pallet-bridge-parachains = { path = "../../modules/parachains" } parachains-relay = { path = "../parachains" } relay-millau-client = { path = "../client-millau" } @@ -48,7 +44,6 @@ relay-substrate-client = { path = "../client-substrate" } relay-utils = { path = "../utils" } relay-westend-client = { path = "../client-westend" } relay-wococo-client = { path = "../client-wococo" } -rialto-parachain-runtime = { path = "../../bin/rialto-parachain/runtime" } rialto-runtime = { path = "../../bin/rialto/runtime" } substrate-relay-helper = { path = "../lib-substrate-relay" } @@ -57,7 +52,6 @@ substrate-relay-helper = { path = "../lib-substrate-relay" } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-version = { git = "https://github.com/paritytech/substrate", branch = "master" } # Polkadot Dependencies polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "master" } diff --git a/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs index 78ef00f6d62..8422569d009 100644 --- a/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs +++ b/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs @@ -17,7 +17,6 @@ //! BridgeHubRococo-to-BridgeHubWococo messages sync entrypoint. use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge}; -use bp_messages::Weight; use relay_bridge_hub_rococo_client::BridgeHubRococo; use relay_bridge_hub_wococo_client::BridgeHubWococo; use substrate_relay_helper::messages_lane::SubstrateMessageLane; @@ -30,8 +29,6 @@ impl CliBridgeBase for BridgeHubRococoToBridgeHubWococoMessagesCliBridge { } impl MessagesCliBridge for BridgeHubRococoToBridgeHubWococoMessagesCliBridge { - const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = - "TODO: not needed now, used for send_message and estimate_fee CLI"; type MessagesLane = BridgeHubRococoMessagesToBridgeHubWococoMessageLane; } @@ -61,4 +58,7 @@ impl SubstrateMessageLane for BridgeHubRococoMessagesToBridgeHubWococoMessageLan BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesProofCallBuilder; type ReceiveMessagesDeliveryProofCallBuilder = BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesDeliveryProofCallBuilder; + + type SourceBatchCallBuilder = (); + type TargetBatchCallBuilder = (); } diff --git a/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs index 51e1020e749..96df83cd0b2 100644 --- a/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs +++ b/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs @@ -17,7 +17,6 @@ //! BridgeHubWococo-to-BridgeHubRococo messages sync entrypoint. use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge}; -use bp_messages::Weight; use relay_bridge_hub_rococo_client::BridgeHubRococo; use relay_bridge_hub_wococo_client::BridgeHubWococo; use substrate_relay_helper::messages_lane::SubstrateMessageLane; @@ -30,8 +29,6 @@ impl CliBridgeBase for BridgeHubWococoToBridgeHubRococoMessagesCliBridge { } impl MessagesCliBridge for BridgeHubWococoToBridgeHubRococoMessagesCliBridge { - const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = - "TODO: not needed now, used for send_message and estimate_fee CLI"; type MessagesLane = BridgeHubWococoMessagesToBridgeHubRococoMessageLane; } @@ -61,4 +58,7 @@ impl SubstrateMessageLane for BridgeHubWococoMessagesToBridgeHubRococoMessageLan BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesProofCallBuilder; type ReceiveMessagesDeliveryProofCallBuilder = BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesDeliveryProofCallBuilder; + + type SourceBatchCallBuilder = (); + type TargetBatchCallBuilder = (); } diff --git a/relays/bin-substrate/src/chains/millau.rs b/relays/bin-substrate/src/chains/millau.rs index 705755506f7..ca9a32a48a5 100644 --- a/relays/bin-substrate/src/chains/millau.rs +++ b/relays/bin-substrate/src/chains/millau.rs @@ -20,7 +20,7 @@ use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain}; use bp_rialto_parachain::RIALTO_PARACHAIN_ID; use bp_runtime::EncodedOrDecodedCall; use relay_millau_client::Millau; -use sp_version::RuntimeVersion; +use relay_substrate_client::SimpleRuntimeVersion; use xcm::latest::prelude::*; impl CliEncodeMessage for Millau { @@ -53,11 +53,6 @@ impl CliEncodeMessage for Millau { } impl CliChain for Millau { - const RUNTIME_VERSION: Option = Some(millau_runtime::VERSION); - - type KeyPair = sp_core::sr25519::Pair; - - fn ss58_format() -> u16 { - millau_runtime::SS58Prefix::get() as u16 - } + const RUNTIME_VERSION: Option = + Some(SimpleRuntimeVersion::from_runtime_version(&millau_runtime::VERSION)); } diff --git a/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs b/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs index e0dbabd86b4..e136b800b17 100644 --- a/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs +++ b/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs @@ -51,7 +51,5 @@ impl RelayToRelayHeadersCliBridge for MillauToRialtoCliBridge { } impl MessagesCliBridge for MillauToRialtoCliBridge { - const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = - bp_rialto::TO_RIALTO_ESTIMATE_MESSAGE_FEE_METHOD; type MessagesLane = crate::chains::millau_messages_to_rialto::MillauMessagesToRialto; } diff --git a/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs b/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs index cdb3999db6e..6496167cf7c 100644 --- a/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs +++ b/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs @@ -36,10 +36,16 @@ use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, RelayToRelayHeadersCliBridge}; use substrate_relay_helper::finality::{ - engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder, - SubstrateFinalitySyncPipeline, + engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline, }; +substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!( + MillauFinalityToRialtoParachain, + MillauFinalityToRialtoParachainCallBuilder, + relay_rialto_parachain_client::runtime::Call::BridgeMillauGrandpa, + relay_rialto_parachain_client::runtime::BridgeMillauGrandpaCall::submit_finality_proof +); + /// Description of Millau -> Rialto finalized headers bridge. #[derive(Clone, Debug)] pub struct MillauFinalityToRialtoParachain; @@ -49,11 +55,7 @@ impl SubstrateFinalitySyncPipeline for MillauFinalityToRialtoParachain { type TargetChain = relay_rialto_parachain_client::RialtoParachain; type FinalityEngine = GrandpaFinalityEngine; - type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder< - Self, - rialto_parachain_runtime::Runtime, - rialto_parachain_runtime::MillauGrandpaInstance, - >; + type SubmitFinalityProofCallBuilder = MillauFinalityToRialtoParachainCallBuilder; } //// `Millau` to `RialtoParachain` bridge definition. @@ -69,8 +71,6 @@ impl RelayToRelayHeadersCliBridge for MillauToRialtoParachainCliBridge { } impl MessagesCliBridge for MillauToRialtoParachainCliBridge { - const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = - bp_rialto_parachain::TO_RIALTO_PARACHAIN_ESTIMATE_MESSAGE_FEE_METHOD; type MessagesLane = crate::chains::millau_messages_to_rialto_parachain::MillauMessagesToRialtoParachain; } diff --git a/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs b/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs index b9920db53d8..e6a2ef1a856 100644 --- a/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs +++ b/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs @@ -41,4 +41,7 @@ impl SubstrateMessageLane for MillauMessagesToRialto { millau_runtime::Runtime, millau_runtime::WithRialtoMessagesInstance, >; + + type SourceBatchCallBuilder = (); + type TargetBatchCallBuilder = (); } diff --git a/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs b/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs index 70cb887fa35..817d0098d9b 100644 --- a/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs +++ b/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs @@ -19,10 +19,16 @@ use relay_millau_client::Millau; use relay_rialto_parachain_client::RialtoParachain; use substrate_relay_helper::messages_lane::{ - DirectReceiveMessagesDeliveryProofCallBuilder, DirectReceiveMessagesProofCallBuilder, - SubstrateMessageLane, + DirectReceiveMessagesDeliveryProofCallBuilder, SubstrateMessageLane, }; +substrate_relay_helper::generate_mocked_receive_message_proof_call_builder!( + MillauMessagesToRialtoParachain, + MillauMessagesToRialtoParachainReceiveMessagesProofCallBuilder, + relay_rialto_parachain_client::runtime::Call::BridgeMillauMessages, + relay_rialto_parachain_client::runtime::BridgeMillauMessagesCall::receive_messages_proof +); + /// Description of Millau -> RialtoParachain messages bridge. #[derive(Clone, Debug)] pub struct MillauMessagesToRialtoParachain; @@ -31,14 +37,14 @@ impl SubstrateMessageLane for MillauMessagesToRialtoParachain { type SourceChain = Millau; type TargetChain = RialtoParachain; - type ReceiveMessagesProofCallBuilder = DirectReceiveMessagesProofCallBuilder< - Self, - rialto_parachain_runtime::Runtime, - rialto_parachain_runtime::WithMillauMessagesInstance, - >; + type ReceiveMessagesProofCallBuilder = + MillauMessagesToRialtoParachainReceiveMessagesProofCallBuilder; type ReceiveMessagesDeliveryProofCallBuilder = DirectReceiveMessagesDeliveryProofCallBuilder< Self, millau_runtime::Runtime, millau_runtime::WithRialtoParachainMessagesInstance, >; + + type SourceBatchCallBuilder = (); + type TargetBatchCallBuilder = (); } diff --git a/relays/bin-substrate/src/chains/rialto.rs b/relays/bin-substrate/src/chains/rialto.rs index 83bccf626b9..e9e2d0b3eaf 100644 --- a/relays/bin-substrate/src/chains/rialto.rs +++ b/relays/bin-substrate/src/chains/rialto.rs @@ -19,7 +19,7 @@ use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain}; use bp_runtime::EncodedOrDecodedCall; use relay_rialto_client::Rialto; -use sp_version::RuntimeVersion; +use relay_substrate_client::SimpleRuntimeVersion; use xcm::latest::prelude::*; impl CliEncodeMessage for Rialto { @@ -45,11 +45,6 @@ impl CliEncodeMessage for Rialto { } impl CliChain for Rialto { - const RUNTIME_VERSION: Option = Some(rialto_runtime::VERSION); - - type KeyPair = sp_core::sr25519::Pair; - - fn ss58_format() -> u16 { - rialto_runtime::SS58Prefix::get() as u16 - } + const RUNTIME_VERSION: Option = + Some(SimpleRuntimeVersion::from_runtime_version(&rialto_runtime::VERSION)); } diff --git a/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs b/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs index b1ab4a8537b..208b2638af0 100644 --- a/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs +++ b/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs @@ -51,7 +51,5 @@ impl RelayToRelayHeadersCliBridge for RialtoToMillauCliBridge { } impl MessagesCliBridge for RialtoToMillauCliBridge { - const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = - bp_millau::TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD; type MessagesLane = crate::chains::rialto_messages_to_millau::RialtoMessagesToMillau; } diff --git a/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs b/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs index 80b6b9fdbc6..b45239fb9a9 100644 --- a/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs +++ b/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs @@ -41,4 +41,7 @@ impl SubstrateMessageLane for RialtoMessagesToMillau { rialto_runtime::Runtime, rialto_runtime::WithMillauMessagesInstance, >; + + type SourceBatchCallBuilder = (); + type TargetBatchCallBuilder = (); } diff --git a/relays/bin-substrate/src/chains/rialto_parachain.rs b/relays/bin-substrate/src/chains/rialto_parachain.rs index a480dc3eebb..54704e72947 100644 --- a/relays/bin-substrate/src/chains/rialto_parachain.rs +++ b/relays/bin-substrate/src/chains/rialto_parachain.rs @@ -18,8 +18,9 @@ use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain}; use bp_runtime::EncodedOrDecodedCall; +use bridge_runtime_common::CustomNetworkId; use relay_rialto_parachain_client::RialtoParachain; -use sp_version::RuntimeVersion; +use relay_substrate_client::{calls::XcmCall, SimpleRuntimeVersion}; use xcm::latest::prelude::*; impl CliEncodeMessage for RialtoParachain { @@ -29,29 +30,19 @@ impl CliEncodeMessage for RialtoParachain { ) -> anyhow::Result> { let dest = match bridge_instance_index { bridge::RIALTO_PARACHAIN_TO_MILLAU_INDEX => - (Parent, X1(GlobalConsensus(rialto_parachain_runtime::MillauNetwork::get()))), + (Parent, X1(GlobalConsensus(CustomNetworkId::Millau.as_network_id()))), _ => anyhow::bail!( "Unsupported target bridge pallet with instance index: {}", bridge_instance_index ), }; - Ok(rialto_parachain_runtime::RuntimeCall::PolkadotXcm( - rialto_parachain_runtime::XcmCall::send { - dest: Box::new(dest.into()), - message: Box::new(message), - }, - ) - .into()) + let xcm_call = XcmCall::send(Box::new(dest.into()), Box::new(message)); + + Ok(relay_rialto_parachain_client::runtime::Call::PolkadotXcm(xcm_call).into()) } } impl CliChain for RialtoParachain { - const RUNTIME_VERSION: Option = Some(rialto_parachain_runtime::VERSION); - - type KeyPair = sp_core::sr25519::Pair; - - fn ss58_format() -> u16 { - rialto_parachain_runtime::SS58Prefix::get() as u16 - } + const RUNTIME_VERSION: Option = None; } diff --git a/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs b/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs index 5cca26105b8..0000a0a754a 100644 --- a/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs +++ b/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs @@ -18,11 +18,18 @@ use relay_millau_client::Millau; use relay_rialto_parachain_client::RialtoParachain; -use substrate_relay_helper::messages_lane::{ - DirectReceiveMessagesDeliveryProofCallBuilder, DirectReceiveMessagesProofCallBuilder, - SubstrateMessageLane, +use substrate_relay_helper::{ + messages_lane::{DirectReceiveMessagesProofCallBuilder, SubstrateMessageLane}, + UtilityPalletBatchCallBuilder, }; +substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_builder!( + RialtoParachainMessagesToMillau, + RialtoParachainMessagesToMillauReceiveMessagesDeliveryProofCallBuilder, + relay_rialto_parachain_client::runtime::Call::BridgeMillauMessages, + relay_rialto_parachain_client::runtime::BridgeMillauMessagesCall::receive_messages_delivery_proof +); + /// Description of RialtoParachain -> Millau messages bridge. #[derive(Clone, Debug)] pub struct RialtoParachainMessagesToMillau; @@ -36,9 +43,9 @@ impl SubstrateMessageLane for RialtoParachainMessagesToMillau { millau_runtime::Runtime, millau_runtime::WithRialtoParachainMessagesInstance, >; - type ReceiveMessagesDeliveryProofCallBuilder = DirectReceiveMessagesDeliveryProofCallBuilder< - Self, - rialto_parachain_runtime::Runtime, - rialto_parachain_runtime::WithMillauMessagesInstance, - >; + type ReceiveMessagesDeliveryProofCallBuilder = + RialtoParachainMessagesToMillauReceiveMessagesDeliveryProofCallBuilder; + + type SourceBatchCallBuilder = (); + type TargetBatchCallBuilder = UtilityPalletBatchCallBuilder; } diff --git a/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs b/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs index 911d4399389..b3471ad1a38 100644 --- a/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs +++ b/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs @@ -40,8 +40,6 @@ impl SubstrateParachainsPipeline for RialtoParachainsToMillau { type TargetChain = Millau; type SubmitParachainHeadsCallBuilder = RialtoParachainsToMillauSubmitParachainHeadsCallBuilder; - - const SOURCE_PARACHAIN_PARA_ID: u32 = bp_rialto_parachain::RIALTO_PARACHAIN_ID; } /// `submit_parachain_heads` call builder for Rialto-to-Millau parachains sync pipeline. @@ -67,8 +65,6 @@ impl ParachainToRelayHeadersCliBridge for RialtoParachainToMillauCliBridge { } impl MessagesCliBridge for RialtoParachainToMillauCliBridge { - const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = - bp_millau::TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD; type MessagesLane = crate::chains::rialto_parachain_messages_to_millau::RialtoParachainMessagesToMillau; } diff --git a/relays/bin-substrate/src/chains/rococo.rs b/relays/bin-substrate/src/chains/rococo.rs index 6a99eab6cb3..633c7cfb290 100644 --- a/relays/bin-substrate/src/chains/rococo.rs +++ b/relays/bin-substrate/src/chains/rococo.rs @@ -19,24 +19,13 @@ use crate::cli::CliChain; use relay_bridge_hub_rococo_client::BridgeHubRococo; use relay_rococo_client::Rococo; -use sp_version::RuntimeVersion; +use relay_substrate_client::SimpleRuntimeVersion; impl CliChain for Rococo { - const RUNTIME_VERSION: Option = None; - - type KeyPair = sp_core::sr25519::Pair; - - fn ss58_format() -> u16 { - bp_rococo::SS58Prefix::get() as u16 - } + const RUNTIME_VERSION: Option = None; } impl CliChain for BridgeHubRococo { - const RUNTIME_VERSION: Option = None; - - type KeyPair = sp_core::sr25519::Pair; - - fn ss58_format() -> u16 { - relay_bridge_hub_rococo_client::runtime::SS58Prefix::get() - } + const RUNTIME_VERSION: Option = + Some(SimpleRuntimeVersion { spec_version: 9302, transaction_version: 1 }); } diff --git a/relays/bin-substrate/src/chains/rococo_headers_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/chains/rococo_headers_to_bridge_hub_wococo.rs index 2ec13614a34..0de1fd9396a 100644 --- a/relays/bin-substrate/src/chains/rococo_headers_to_bridge_hub_wococo.rs +++ b/relays/bin-substrate/src/chains/rococo_headers_to_bridge_hub_wococo.rs @@ -17,8 +17,12 @@ //! Rococo-to-Wococo bridge hubs headers sync entrypoint. use crate::cli::bridge::{CliBridgeBase, RelayToRelayHeadersCliBridge}; -use substrate_relay_helper::finality::{ - engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline, + +use async_trait::async_trait; +use relay_substrate_client::{AccountKeyPairOf, Client}; +use substrate_relay_helper::{ + finality::{engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline}, + TransactionParams, }; /// Description of Rococo -> Wococo finalized headers bridge. @@ -29,15 +33,30 @@ substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!( RococoFinalityToBridgeHubWococo, RococoFinalityToBridgeHubWococoCallBuilder, relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoGrandpa, - relay_bridge_hub_wococo_client::runtime::BridgeGrandpaRococoCall::submit_finality_proof + relay_bridge_hub_wococo_client::runtime::BridgeRococoGrandpaCall::submit_finality_proof ); +#[async_trait] impl SubstrateFinalitySyncPipeline for RococoFinalityToBridgeHubWococo { type SourceChain = relay_rococo_client::Rococo; type TargetChain = relay_bridge_hub_wococo_client::BridgeHubWococo; type FinalityEngine = GrandpaFinalityEngine; type SubmitFinalityProofCallBuilder = RococoFinalityToBridgeHubWococoCallBuilder; + + async fn start_relay_guards( + target_client: &Client, + _transaction_params: &TransactionParams>, + enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + if enable_version_guard { + relay_substrate_client::guard::abort_on_spec_version_change( + target_client.clone(), + target_client.simple_runtime_version().await?.spec_version, + ); + } + Ok(()) + } } /// `Rococo` to BridgeHub `Wococo` bridge definition. diff --git a/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs index 8b208712db5..b028253d8a4 100644 --- a/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs +++ b/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs @@ -39,8 +39,6 @@ impl SubstrateParachainsPipeline for BridgeHubRococoToBridgeHubWococo { type TargetChain = relay_bridge_hub_wococo_client::BridgeHubWococo; type SubmitParachainHeadsCallBuilder = BridgeHubRococoToBridgeHubWococoCallBuilder; - - const SOURCE_PARACHAIN_PARA_ID: u32 = bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID; } pub struct BridgeHubRococoToBridgeHubWococoCallBuilder; @@ -78,8 +76,6 @@ impl CliBridgeBase for BridgeHubRococoToBridgeHubWococoCliBridge { } impl MessagesCliBridge for BridgeHubRococoToBridgeHubWococoCliBridge { - const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = - bp_bridge_hub_wococo::TO_BRIDGE_HUB_WOCOCO_ESTIMATE_MESSAGE_FEE_METHOD; type MessagesLane = crate::chains::bridge_hub_rococo_messages_to_bridge_hub_wococo::BridgeHubRococoMessagesToBridgeHubWococoMessageLane; } diff --git a/relays/bin-substrate/src/chains/westend.rs b/relays/bin-substrate/src/chains/westend.rs index 1627bc015d3..9cf639e9874 100644 --- a/relays/bin-substrate/src/chains/westend.rs +++ b/relays/bin-substrate/src/chains/westend.rs @@ -17,31 +17,13 @@ //! Westend chain specification for CLI. use crate::cli::CliChain; +use relay_substrate_client::SimpleRuntimeVersion; use relay_westend_client::{Westend, Westmint}; -use sp_version::RuntimeVersion; impl CliChain for Westend { - const RUNTIME_VERSION: Option = None; - - type KeyPair = sp_core::sr25519::Pair; - - fn ss58_format() -> u16 { - sp_core::crypto::Ss58AddressFormat::from( - sp_core::crypto::Ss58AddressFormatRegistry::SubstrateAccount, - ) - .into() - } + const RUNTIME_VERSION: Option = None; } impl CliChain for Westmint { - const RUNTIME_VERSION: Option = None; - - type KeyPair = sp_core::sr25519::Pair; - - fn ss58_format() -> u16 { - sp_core::crypto::Ss58AddressFormat::from( - sp_core::crypto::Ss58AddressFormatRegistry::SubstrateAccount, - ) - .into() - } + const RUNTIME_VERSION: Option = None; } diff --git a/relays/bin-substrate/src/chains/westend_parachains_to_millau.rs b/relays/bin-substrate/src/chains/westend_parachains_to_millau.rs index 73409e65569..f025f48dcb6 100644 --- a/relays/bin-substrate/src/chains/westend_parachains_to_millau.rs +++ b/relays/bin-substrate/src/chains/westend_parachains_to_millau.rs @@ -39,8 +39,6 @@ impl SubstrateParachainsPipeline for WestendParachainsToMillau { type TargetChain = Millau; type SubmitParachainHeadsCallBuilder = WestendParachainsToMillauSubmitParachainHeadsCallBuilder; - - const SOURCE_PARACHAIN_PARA_ID: u32 = bp_westend::WESTMINT_PARACHAIN_ID; } /// `submit_parachain_heads` call builder for Rialto-to-Millau parachains sync pipeline. diff --git a/relays/bin-substrate/src/chains/wococo.rs b/relays/bin-substrate/src/chains/wococo.rs index c44f3ac811f..66a72e11134 100644 --- a/relays/bin-substrate/src/chains/wococo.rs +++ b/relays/bin-substrate/src/chains/wococo.rs @@ -18,25 +18,14 @@ use crate::cli::CliChain; use relay_bridge_hub_wococo_client::BridgeHubWococo; +use relay_substrate_client::SimpleRuntimeVersion; use relay_wococo_client::Wococo; -use sp_version::RuntimeVersion; impl CliChain for Wococo { - const RUNTIME_VERSION: Option = None; - - type KeyPair = sp_core::sr25519::Pair; - - fn ss58_format() -> u16 { - bp_wococo::SS58Prefix::get() as u16 - } + const RUNTIME_VERSION: Option = None; } impl CliChain for BridgeHubWococo { - const RUNTIME_VERSION: Option = None; - - type KeyPair = sp_core::sr25519::Pair; - - fn ss58_format() -> u16 { - relay_bridge_hub_wococo_client::runtime::SS58Prefix::get() - } + const RUNTIME_VERSION: Option = + Some(SimpleRuntimeVersion { spec_version: 9302, transaction_version: 1 }); } diff --git a/relays/bin-substrate/src/chains/wococo_headers_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/chains/wococo_headers_to_bridge_hub_rococo.rs index 30d6af00cd3..e9054fd31e9 100644 --- a/relays/bin-substrate/src/chains/wococo_headers_to_bridge_hub_rococo.rs +++ b/relays/bin-substrate/src/chains/wococo_headers_to_bridge_hub_rococo.rs @@ -17,8 +17,12 @@ //! Wococo-to-Rococo bridge hubs headers sync entrypoint. use crate::cli::bridge::{CliBridgeBase, RelayToRelayHeadersCliBridge}; -use substrate_relay_helper::finality::{ - engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline, + +use async_trait::async_trait; +use relay_substrate_client::{AccountKeyPairOf, Client}; +use substrate_relay_helper::{ + finality::{engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline}, + TransactionParams, }; /// Description of Wococo -> Rococo finalized headers bridge. @@ -32,12 +36,27 @@ substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!( relay_bridge_hub_rococo_client::runtime::BridgeWococoGrandpaCall::submit_finality_proof ); +#[async_trait] impl SubstrateFinalitySyncPipeline for WococoFinalityToBridgeHubRococo { type SourceChain = relay_wococo_client::Wococo; type TargetChain = relay_bridge_hub_rococo_client::BridgeHubRococo; type FinalityEngine = GrandpaFinalityEngine; type SubmitFinalityProofCallBuilder = WococoFinalityToBridgeHubRococoCallBuilder; + + async fn start_relay_guards( + target_client: &Client, + _transaction_params: &TransactionParams>, + enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + if enable_version_guard { + relay_substrate_client::guard::abort_on_spec_version_change( + target_client.clone(), + target_client.simple_runtime_version().await?.spec_version, + ); + } + Ok(()) + } } /// `Wococo` to BridgeHub `Rococo` bridge definition. diff --git a/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs index 453455a7b56..f79979ff832 100644 --- a/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs +++ b/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs @@ -39,8 +39,6 @@ impl SubstrateParachainsPipeline for BridgeHubWococoToBridgeHubRococo { type TargetChain = relay_bridge_hub_rococo_client::BridgeHubRococo; type SubmitParachainHeadsCallBuilder = BridgeHubWococoToBridgeHubRococoCallBuilder; - - const SOURCE_PARACHAIN_PARA_ID: u32 = bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID; } pub struct BridgeHubWococoToBridgeHubRococoCallBuilder; @@ -53,7 +51,7 @@ impl SubmitParachainHeadsCallBuilder parachain_heads_proof: ParaHeadsProof, ) -> CallOf { relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoParachain( - relay_bridge_hub_rococo_client::runtime::BridgeParachainCall::submit_parachain_heads( + bp_parachains::BridgeParachainCall::submit_parachain_heads( (at_relay_block.0, at_relay_block.1), parachains, parachain_heads_proof, @@ -78,8 +76,6 @@ impl CliBridgeBase for BridgeHubWococoToBridgeHubRococoCliBridge { } impl MessagesCliBridge for BridgeHubWococoToBridgeHubRococoCliBridge { - const ESTIMATE_MESSAGE_FEE_METHOD: &'static str = - bp_bridge_hub_rococo::TO_BRIDGE_HUB_ROCOCO_ESTIMATE_MESSAGE_FEE_METHOD; type MessagesLane = crate::chains::bridge_hub_wococo_messages_to_bridge_hub_rococo::BridgeHubWococoMessagesToBridgeHubRococoMessageLane; } diff --git a/relays/bin-substrate/src/cli/bridge.rs b/relays/bin-substrate/src/cli/bridge.rs index ae2a36cd1d5..330b498f19c 100644 --- a/relays/bin-substrate/src/cli/bridge.rs +++ b/relays/bin-substrate/src/cli/bridge.rs @@ -17,7 +17,7 @@ use crate::cli::CliChain; use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use parachains_relay::ParachainsPipeline; -use relay_substrate_client::{AccountKeyPairOf, Chain, ChainWithTransactions, RelayChain}; +use relay_substrate_client::{Chain, ChainWithTransactions, Parachain, RelayChain}; use strum::{EnumString, EnumVariantNames}; use substrate_relay_helper::{ finality::SubstrateFinalitySyncPipeline, messages_lane::SubstrateMessageLane, @@ -61,7 +61,7 @@ pub trait CliBridgeBase: Sized { /// The source chain. type Source: Chain + CliChain; /// The target chain. - type Target: ChainWithTransactions + CliChain>; + type Target: ChainWithTransactions + CliChain; } /// Bridge representation that can be used from the CLI for relaying headers @@ -76,7 +76,10 @@ pub trait RelayToRelayHeadersCliBridge: CliBridgeBase { /// Bridge representation that can be used from the CLI for relaying headers /// from a parachain to a relay chain. -pub trait ParachainToRelayHeadersCliBridge: CliBridgeBase { +pub trait ParachainToRelayHeadersCliBridge: CliBridgeBase +where + Self::Source: Parachain, +{ // The `CliBridgeBase` type represents the parachain in this situation. // We need to add an extra type for the relay chain. type SourceRelay: Chain @@ -97,9 +100,6 @@ pub trait ParachainToRelayHeadersCliBridge: CliBridgeBase { /// Bridge representation that can be used from the CLI for relaying messages. pub trait MessagesCliBridge: CliBridgeBase { - /// Name of the runtime method used to estimate the message dispatch and delivery fee for the - /// defined bridge. - const ESTIMATE_MESSAGE_FEE_METHOD: &'static str; /// The Source -> Destination messages synchronization pipeline. type MessagesLane: SubstrateMessageLane; } diff --git a/relays/bin-substrate/src/cli/chain_schema.rs b/relays/bin-substrate/src/cli/chain_schema.rs index 8023fd9b0f7..bbc95d7dcdb 100644 --- a/relays/bin-substrate/src/cli/chain_schema.rs +++ b/relays/bin-substrate/src/cli/chain_schema.rs @@ -15,12 +15,12 @@ // along with Parity Bridges Common. If not, see . -use sp_core::Pair; +use relay_substrate_client::{AccountKeyPairOf, ChainWithTransactions}; use structopt::StructOpt; use strum::{EnumString, EnumVariantNames}; use crate::cli::CliChain; -pub use relay_substrate_client::ChainRuntimeVersion; +pub use relay_substrate_client::{ChainRuntimeVersion, SimpleRuntimeVersion}; use substrate_relay_helper::TransactionParams; #[doc = "Runtime version params."] @@ -57,25 +57,24 @@ macro_rules! declare_chain_runtime_version_params_cli_schema { /// Converts self into `ChainRuntimeVersion`. pub fn into_runtime_version( self, - bundle_runtime_version: Option, + bundle_runtime_version: Option, ) -> anyhow::Result { Ok(match self.[<$chain_prefix _version_mode>] { RuntimeVersionType::Auto => ChainRuntimeVersion::Auto, RuntimeVersionType::Custom => { - let except_spec_version = self.[<$chain_prefix _spec_version>] + let custom_spec_version = self.[<$chain_prefix _spec_version>] .ok_or_else(|| anyhow::Error::msg(format!("The {}-spec-version is required when choose custom mode", stringify!($chain_prefix))))?; - let except_transaction_version = self.[<$chain_prefix _transaction_version>] + let custom_transaction_version = self.[<$chain_prefix _transaction_version>] .ok_or_else(|| anyhow::Error::msg(format!("The {}-transaction-version is required when choose custom mode", stringify!($chain_prefix))))?; ChainRuntimeVersion::Custom( - except_spec_version, - except_transaction_version + SimpleRuntimeVersion { + spec_version: custom_spec_version, + transaction_version: custom_transaction_version + } ) }, RuntimeVersionType::Bundle => match bundle_runtime_version { - Some(runtime_version) => ChainRuntimeVersion::Custom( - runtime_version.spec_version, - runtime_version.transaction_version - ), + Some(runtime_version) => ChainRuntimeVersion::Custom(runtime_version), None => ChainRuntimeVersion::Auto }, }) @@ -135,16 +134,16 @@ pub trait TransactionParamsProvider { /// Returns `true` if transaction parameters are defined by this provider. fn is_defined(&self) -> bool; /// Returns transaction parameters. - fn transaction_params( + fn transaction_params( &self, - ) -> anyhow::Result>; + ) -> anyhow::Result>>; /// Returns transaction parameters, defined by `self` provider or, if they're not defined, /// defined by `other` provider. - fn transaction_params_or( + fn transaction_params_or( &self, other: &T, - ) -> anyhow::Result> { + ) -> anyhow::Result>> { if self.is_defined() { self.transaction_params::() } else { @@ -202,7 +201,7 @@ macro_rules! declare_chain_signing_params_cli_schema { /// Parse signing params into chain-specific KeyPair. #[allow(dead_code)] - pub fn to_keypair(&self) -> anyhow::Result { + pub fn to_keypair(&self) -> anyhow::Result> { let suri = match (self.[<$chain_prefix _signer>].as_ref(), self.[<$chain_prefix _signer_file>].as_ref()) { (Some(suri), _) => suri.to_owned(), (None, Some(suri_file)) => std::fs::read_to_string(suri_file) @@ -235,7 +234,7 @@ macro_rules! declare_chain_signing_params_cli_schema { use sp_core::crypto::Pair; - Chain::KeyPair::from_string( + AccountKeyPairOf::::from_string( &suri, suri_password.as_deref() ).map_err(|e| anyhow::format_err!("{:?}", e)) @@ -248,7 +247,7 @@ macro_rules! declare_chain_signing_params_cli_schema { self.[<$chain_prefix _signer>].is_some() || self.[<$chain_prefix _signer_file>].is_some() } - fn transaction_params(&self) -> anyhow::Result> { + fn transaction_params(&self) -> anyhow::Result>> { Ok(TransactionParams { mortality: self.transactions_mortality()?, signer: self.to_keypair::()?, @@ -259,40 +258,6 @@ macro_rules! declare_chain_signing_params_cli_schema { }; } -/// Create chain-specific set of messages pallet owner signing parameters. -#[macro_export] -macro_rules! declare_chain_messages_pallet_owner_signing_params_cli_schema { - ($chain:ident, $chain_prefix:ident) => { - bp_runtime::paste::item! { - #[doc = "Parameters required to sign transaction on behalf of owner of the messages pallet at " $chain "."] - #[derive(StructOpt, Debug, PartialEq, Eq)] - pub struct [<$chain MessagesPalletOwnerSigningParams>] { - #[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."] - #[structopt(long)] - pub [<$chain_prefix _messages_pallet_owner>]: Option, - #[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."] - #[structopt(long)] - pub [<$chain_prefix _messages_pallet_owner_password>]: Option, - } - - #[allow(dead_code)] - impl [<$chain MessagesPalletOwnerSigningParams>] { - /// Parse signing params into chain-specific KeyPair. - pub fn to_keypair(&self) -> anyhow::Result> { - let [<$chain_prefix _messages_pallet_owner>] = match self.[<$chain_prefix _messages_pallet_owner>] { - Some(ref messages_pallet_owner) => messages_pallet_owner, - None => return Ok(None), - }; - Chain::KeyPair::from_string( - [<$chain_prefix _messages_pallet_owner>], - self.[<$chain_prefix _messages_pallet_owner_password>].as_deref() - ).map_err(|e| anyhow::format_err!("{:?}", e)).map(Some) - } - } - } - }; -} - /// Create chain-specific set of configuration objects: connection parameters, /// signing parameters and bridge initialization parameters. #[macro_export] @@ -301,10 +266,6 @@ macro_rules! declare_chain_cli_schema { $crate::declare_chain_runtime_version_params_cli_schema!($chain, $chain_prefix); $crate::declare_chain_connection_params_cli_schema!($chain, $chain_prefix); $crate::declare_chain_signing_params_cli_schema!($chain, $chain_prefix); - $crate::declare_chain_messages_pallet_owner_signing_params_cli_schema!( - $chain, - $chain_prefix - ); }; } diff --git a/relays/bin-substrate/src/cli/init_bridge.rs b/relays/bin-substrate/src/cli/init_bridge.rs index 52a3a6fce5a..8f4080b338c 100644 --- a/relays/bin-substrate/src/cli/init_bridge.rs +++ b/relays/bin-substrate/src/cli/init_bridge.rs @@ -29,7 +29,7 @@ use crate::{ cli::{bridge::CliBridgeBase, chain_schema::*}, }; use bp_runtime::Chain as ChainBase; -use relay_substrate_client::{AccountKeyPairOf, Chain, SignParam, UnsignedTransaction}; +use relay_substrate_client::{calls::SudoCall, AccountKeyPairOf, Chain, UnsignedTransaction}; use sp_core::Pair; use structopt::StructOpt; use strum::{EnumString, EnumVariantNames, VariantNames}; @@ -83,17 +83,10 @@ where let target_sign = data.target_sign.to_keypair::()?; let dry_run = data.dry_run; - let (spec_version, transaction_version) = target_client.simple_runtime_version().await?; substrate_relay_helper::finality::initialize::initialize::( source_client, target_client.clone(), - target_sign.public().into(), - SignParam { - spec_version, - transaction_version, - genesis_hash: *target_client.genesis_hash(), - signer: target_sign, - }, + target_sign, move |transaction_nonce, initialization_data| { let call = Self::encode_init_bridge(initialization_data); log::info!( @@ -130,13 +123,13 @@ impl BridgeInitializer for MillauToRialtoParachainCliBridge { fn encode_init_bridge( init_data: >::InitializationData, ) -> ::Call { - let initialize_call = rialto_parachain_runtime::BridgeGrandpaCall::< - rialto_parachain_runtime::Runtime, - rialto_parachain_runtime::MillauGrandpaInstance, - >::initialize { - init_data, - }; - rialto_parachain_runtime::SudoCall::sudo { call: Box::new(initialize_call.into()) }.into() + use relay_rialto_parachain_client::runtime; + + let initialize_call = runtime::Call::BridgeMillauGrandpa( + runtime::BridgeMillauGrandpaCall::initialize(init_data), + ); + let sudo_call = SudoCall::sudo(Box::new(initialize_call)); + runtime::Call::Sudo(sudo_call) } } @@ -183,7 +176,7 @@ impl BridgeInitializer for RococoToBridgeHubWococoCliBridge { init_data: >::InitializationData, ) -> ::Call { relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoGrandpa( - relay_bridge_hub_wococo_client::runtime::BridgeGrandpaRococoCall::initialize(init_data), + relay_bridge_hub_wococo_client::runtime::BridgeRococoGrandpaCall::initialize(init_data), ) } } diff --git a/relays/bin-substrate/src/cli/mod.rs b/relays/bin-substrate/src/cli/mod.rs index 2086008bc95..17d0262b21b 100644 --- a/relays/bin-substrate/src/cli/mod.rs +++ b/relays/bin-substrate/src/cli/mod.rs @@ -18,11 +18,17 @@ use std::convert::TryInto; +use async_std::prelude::*; use codec::{Decode, Encode}; +use futures::{select, FutureExt}; +use rbtag::BuildInfo; +use signal_hook::consts::*; +use signal_hook_async_std::Signals; use structopt::{clap::arg_enum, StructOpt}; use strum::{EnumString, EnumVariantNames}; use bp_messages::LaneId; +use relay_substrate_client::SimpleRuntimeVersion; pub(crate) mod bridge; pub(crate) mod encode_message; @@ -37,6 +43,9 @@ mod relay_messages; mod relay_parachains; mod resubmit_transactions; +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "bridge"; + /// Parse relay CLI args. pub fn parse_args() -> Command { Command::from_args() @@ -100,8 +109,7 @@ impl Command { } /// Run the command. - pub async fn run(self) -> anyhow::Result<()> { - self.init_logger(); + async fn do_run(self) -> anyhow::Result<()> { match self { Self::RelayHeaders(arg) => arg.run().await?, Self::RelayMessages(arg) => arg.run().await?, @@ -114,6 +122,32 @@ impl Command { } Ok(()) } + + /// Run the command. + pub async fn run(self) { + self.init_logger(); + + let exit_signals = match Signals::new([SIGINT, SIGTERM]) { + Ok(signals) => signals, + Err(e) => { + log::error!(target: LOG_TARGET, "Could not register exit signals: {}", e); + return + }, + }; + let run = self.do_run().fuse(); + futures::pin_mut!(exit_signals, run); + + select! { + signal = exit_signals.next().fuse() => { + log::info!(target: LOG_TARGET, "Received exit signal {:?}", signal); + }, + result = run => { + if let Err(e) = result { + log::error!(target: LOG_TARGET, "substrate-relay: {}", e); + } + }, + } + } } arg_enum! { @@ -161,24 +195,16 @@ pub trait CliChain: relay_substrate_client::Chain { /// Current version of the chain runtime, known to relay. /// /// can be `None` if relay is not going to submit transactions to that chain. - const RUNTIME_VERSION: Option; - - /// Crypto KeyPair type used to send messages. - /// - /// In case of chains supporting multiple cryptos, pick one used by the CLI. - type KeyPair: sp_core::crypto::Pair; - - /// Numeric value of SS58 format. - fn ss58_format() -> u16; + const RUNTIME_VERSION: Option; } /// Lane id. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct HexLaneId(pub LaneId); +pub struct HexLaneId(pub [u8; 4]); impl From for LaneId { fn from(lane_id: HexLaneId) -> LaneId { - lane_id.0 + LaneId(lane_id.0) } } @@ -186,7 +212,7 @@ impl std::str::FromStr for HexLaneId { type Err = hex::FromHexError; fn from_str(s: &str) -> Result { - let mut lane_id = LaneId::default(); + let mut lane_id = [0u8; 4]; hex::decode_to_slice(s, &mut lane_id)?; Ok(HexLaneId(lane_id)) } @@ -230,17 +256,30 @@ pub struct PrometheusParams { pub prometheus_port: u16, } -impl From for relay_utils::metrics::MetricsParams { - fn from(cli_params: PrometheusParams) -> relay_utils::metrics::MetricsParams { - if !cli_params.no_prometheus { +/// Struct to get git commit info and build time. +#[derive(BuildInfo)] +struct SubstrateRelayBuildInfo; + +impl PrometheusParams { + /// Tries to convert CLI metrics params into metrics params, used by the relay. + pub fn into_metrics_params(self) -> anyhow::Result { + let metrics_address = if !self.no_prometheus { Some(relay_utils::metrics::MetricsAddress { - host: cli_params.prometheus_host, - port: cli_params.prometheus_port, + host: self.prometheus_host, + port: self.prometheus_port, }) - .into() } else { - None.into() - } + None + }; + + let relay_version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"); + let relay_commit = SubstrateRelayBuildInfo.get_build_commit(); + relay_utils::metrics::MetricsParams::new( + metrics_address, + relay_version.into(), + relay_commit.into(), + ) + .map_err(|e| anyhow::format_err!("{:?}", e)) } } diff --git a/relays/bin-substrate/src/cli/register_parachain.rs b/relays/bin-substrate/src/cli/register_parachain.rs index 4febc4b04a8..1bc9f941ecf 100644 --- a/relays/bin-substrate/src/cli/register_parachain.rs +++ b/relays/bin-substrate/src/cli/register_parachain.rs @@ -26,7 +26,7 @@ use polkadot_runtime_common::{ paras_registrar::Call as ParaRegistrarCall, slots::Call as ParaSlotsCall, }; use polkadot_runtime_parachains::paras::ParaLifecycle; -use relay_substrate_client::{AccountIdOf, CallOf, Chain, Client, SignParam, UnsignedTransaction}; +use relay_substrate_client::{AccountIdOf, CallOf, Chain, Client, UnsignedTransaction}; use relay_utils::{TrackedTransactionStatus, TransactionTracker}; use rialto_runtime::SudoCall; use sp_core::{ @@ -108,28 +108,16 @@ impl RegisterParachain { log::info!(target: "bridge", "Going to reserve parachain id: {:?}", para_id); // step 1: reserve a parachain id - let relay_genesis_hash = *relay_client.genesis_hash(); let relay_sudo_account: AccountIdOf = relay_sign.public().into(); let reserve_parachain_id_call: CallOf = ParaRegistrarCall::reserve {}.into(); - let reserve_parachain_signer = relay_sign.clone(); - let (spec_version, transaction_version) = relay_client.simple_runtime_version().await?; let reserve_result = relay_client - .submit_and_watch_signed_extrinsic( - relay_sudo_account.clone(), - SignParam:: { - spec_version, - transaction_version, - genesis_hash: relay_genesis_hash, - signer: reserve_parachain_signer, - }, - move |_, transaction_nonce| { - Ok(UnsignedTransaction::new( - reserve_parachain_id_call.into(), - transaction_nonce, - )) - }, - ) + .submit_and_watch_signed_extrinsic(&relay_sign, move |_, transaction_nonce| { + Ok(UnsignedTransaction::new( + reserve_parachain_id_call.into(), + transaction_nonce, + )) + }) .await? .wait() .await; @@ -162,23 +150,10 @@ impl RegisterParachain { validation_code: ParaValidationCode(para_code), } .into(); - let register_parathread_signer = relay_sign.clone(); let register_result = relay_client - .submit_and_watch_signed_extrinsic( - relay_sudo_account.clone(), - SignParam:: { - spec_version, - transaction_version, - genesis_hash: relay_genesis_hash, - signer: register_parathread_signer, - }, - move |_, transaction_nonce| { - Ok(UnsignedTransaction::new( - register_parathread_call.into(), - transaction_nonce, - )) - }, - ) + .submit_and_watch_signed_extrinsic(&relay_sign, move |_, transaction_nonce| { + Ok(UnsignedTransaction::new(register_parathread_call.into(), transaction_nonce)) + }) .await? .wait() .await; @@ -227,20 +202,10 @@ impl RegisterParachain { ), } .into(); - let force_lease_signer = relay_sign.clone(); relay_client - .submit_signed_extrinsic( - relay_sudo_account, - SignParam:: { - spec_version, - transaction_version, - genesis_hash: relay_genesis_hash, - signer: force_lease_signer, - }, - move |_, transaction_nonce| { - Ok(UnsignedTransaction::new(force_lease_call.into(), transaction_nonce)) - }, - ) + .submit_signed_extrinsic(&relay_sign, move |_, transaction_nonce| { + Ok(UnsignedTransaction::new(force_lease_call.into(), transaction_nonce)) + }) .await?; log::info!(target: "bridge", "Registered parachain leases: {:?}. Waiting for onboarding", para_id); diff --git a/relays/bin-substrate/src/cli/relay_headers.rs b/relays/bin-substrate/src/cli/relay_headers.rs index 37b805d720b..239ab8d62fd 100644 --- a/relays/bin-substrate/src/cli/relay_headers.rs +++ b/relays/bin-substrate/src/cli/relay_headers.rs @@ -77,7 +77,8 @@ where let target_transactions_mortality = data.target_sign.target_transactions_mortality; let target_sign = data.target_sign.to_keypair::()?; - let metrics_params: relay_utils::metrics::MetricsParams = data.prometheus_params.into(); + let metrics_params: relay_utils::metrics::MetricsParams = + data.prometheus_params.into_metrics_params()?; GlobalMetrics::new()?.register_and_spawn(&metrics_params.registry)?; let target_transactions_params = substrate_relay_helper::TransactionParams { diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs index f84174563e1..ea6d6ad9f51 100644 --- a/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs @@ -59,9 +59,10 @@ use crate::{ declare_chain_cli_schema, }; use bp_messages::LaneId; -use bp_runtime::{BalanceOf, BlockNumberOf}; +use bp_runtime::BalanceOf; use relay_substrate_client::{ AccountIdOf, AccountKeyPairOf, Chain, ChainWithBalances, ChainWithTransactions, Client, + Parachain, }; use relay_utils::metrics::MetricsParams; use sp_core::Pair; @@ -109,7 +110,7 @@ impl, ) -> anyhow::Result { // Create metrics registry. - let metrics_params = shared.prometheus_params.clone().into(); + let metrics_params = shared.prometheus_params.clone().into_metrics_params()?; let metrics_params = relay_utils::relay_metrics(metrics_params).into_params(); Ok(Self { shared, left, right, metrics_params }) @@ -124,8 +125,6 @@ pub struct BridgeEndCommonParams { pub sign: AccountKeyPairOf, /// Transactions mortality. pub transactions_mortality: Option, - /// Account that "owns" messages pallet. - pub messages_pallet_owner: Option>, /// Accounts, which balances are exposed as metrics by the relay process. pub accounts: Vec>>, } @@ -166,8 +165,8 @@ where /// Returns message relay parameters. fn messages_relay_params( &self, - source_to_target_headers_relay: Arc>>, - target_to_source_headers_relay: Arc>>, + source_to_target_headers_relay: Arc>, + target_to_source_headers_relay: Arc>, lane_id: LaneId, ) -> MessagesRelayParams { MessagesRelayParams { @@ -228,9 +227,9 @@ trait Full2WayBridgeBase: Sized + Send + Sync { /// The CLI params for the bridge. type Params; /// The left relay chain. - type Left: ChainWithTransactions + CliChain>; + type Left: ChainWithTransactions + CliChain; /// The right destination chain (it can be a relay or a parachain). - type Right: ChainWithTransactions + CliChain>; + type Right: ChainWithTransactions + CliChain; /// Reference to common relay parameters. fn common(&self) -> &Full2WayBridgeCommonParams; @@ -242,8 +241,8 @@ trait Full2WayBridgeBase: Sized + Send + Sync { async fn start_on_demand_headers_relayers( &mut self, ) -> anyhow::Result<( - Arc>>, - Arc>>, + Arc>, + Arc>, )>; } @@ -260,13 +259,9 @@ where type Base: Full2WayBridgeBase; /// The left relay chain. - type Left: ChainWithTransactions - + ChainWithBalances - + CliChain>; + type Left: ChainWithTransactions + ChainWithBalances + CliChain; /// The right relay chain. - type Right: ChainWithTransactions - + ChainWithBalances - + CliChain>; + type Right: ChainWithTransactions + ChainWithBalances + CliChain; /// Left to Right bridge. type L2R: MessagesCliBridge; @@ -499,8 +494,6 @@ mod tests { "9944", "--millau-signer", "//Charlie", - "--millau-messages-pallet-owner", - "//RialtoMessagesOwner", "--millau-transactions-mortality", "64", "--rialto-host", @@ -509,8 +502,6 @@ mod tests { "9944", "--rialto-signer", "//Charlie", - "--rialto-messages-pallet-owner", - "//MillauMessagesOwner", "--rialto-transactions-mortality", "64", "--lane", @@ -554,10 +545,6 @@ mod tests { millau_signer_password_file: None, millau_transactions_mortality: Some(64), }, - left_messages_pallet_owner: MillauMessagesPalletOwnerSigningParams { - millau_messages_pallet_owner: Some("//RialtoMessagesOwner".into()), - millau_messages_pallet_owner_password: None, - }, left_headers_to_right_sign_override: MillauHeadersToRialtoSigningParams { millau_headers_to_rialto_signer: None, millau_headers_to_rialto_signer_password: None, @@ -582,10 +569,6 @@ mod tests { rialto_signer_password_file: None, rialto_transactions_mortality: Some(64), }, - right_messages_pallet_owner: RialtoMessagesPalletOwnerSigningParams { - rialto_messages_pallet_owner: Some("//MillauMessagesOwner".into()), - rialto_messages_pallet_owner_password: None, - }, right_headers_to_left_sign_override: RialtoHeadersToMillauSigningParams { rialto_headers_to_millau_signer: None, rialto_headers_to_millau_signer_password: None, @@ -611,8 +594,6 @@ mod tests { "//Iden", "--rialto-headers-to-millau-signer", "//Ken", - "--millau-messages-pallet-owner", - "//RialtoParachainMessagesOwner", "--millau-transactions-mortality", "64", "--rialto-parachain-host", @@ -621,8 +602,6 @@ mod tests { "9944", "--rialto-parachain-signer", "//George", - "--rialto-parachain-messages-pallet-owner", - "//MillauMessagesOwner", "--rialto-parachain-transactions-mortality", "64", "--rialto-host", @@ -666,10 +645,6 @@ mod tests { millau_signer_password_file: None, millau_transactions_mortality: Some(64), }, - left_messages_pallet_owner: MillauMessagesPalletOwnerSigningParams { - millau_messages_pallet_owner: Some("//RialtoParachainMessagesOwner".into()), - millau_messages_pallet_owner_password: None, - }, left_headers_to_right_sign_override: MillauHeadersToRialtoParachainSigningParams { millau_headers_to_rialto_parachain_signer: None, @@ -695,12 +670,6 @@ mod tests { rialto_parachain_signer_password_file: None, rialto_parachain_transactions_mortality: Some(64), }, - right_messages_pallet_owner: RialtoParachainMessagesPalletOwnerSigningParams { - rialto_parachain_messages_pallet_owner: Some( - "//MillauMessagesOwner".into() - ), - rialto_parachain_messages_pallet_owner_password: None, - }, right_relay_headers_to_left_sign_override: RialtoHeadersToMillauSigningParams { rialto_headers_to_millau_signer: Some("//Ken".into()), rialto_headers_to_millau_signer_password: None, diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages/parachain_to_parachain.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages/parachain_to_parachain.rs index e7328a837eb..b4c858e566b 100644 --- a/relays/bin-substrate/src/cli/relay_headers_and_messages/parachain_to_parachain.rs +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages/parachain_to_parachain.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -#![allow(unused_macros)] // TODO (https://github.com/paritytech/parity-bridges-common/issues/1629): remove me - use async_trait::async_trait; use std::sync::Arc; @@ -25,9 +23,10 @@ use crate::cli::{ CliChain, }; use bp_polkadot_core::parachains::ParaHash; -use bp_runtime::BlockNumberOf; use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; -use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, Chain, ChainWithTransactions, Client}; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, Chain, ChainWithTransactions, Client, Parachain, +}; use sp_core::Pair; use substrate_relay_helper::{ finality::SubstrateFinalitySyncPipeline, @@ -44,7 +43,10 @@ use substrate_relay_helper::{ pub struct ParachainToParachainBridge< L2R: MessagesCliBridge + ParachainToRelayHeadersCliBridge, R2L: MessagesCliBridge + ParachainToRelayHeadersCliBridge, -> { +> where + ::Source: Parachain, + ::Source: Parachain, +{ /// Parameters that are shared by all bridge types. pub common: Full2WayBridgeCommonParams<::Target, ::Target>, @@ -87,9 +89,6 @@ macro_rules! declare_parachain_to_parachain_bridge_schema { // default signer, which is always used to sign messages relay transactions on the left chain #[structopt(flatten)] left_sign: [<$left_parachain SigningParams>], - // signer used to sign parameter update transactions at the left parachain - #[structopt(flatten)] - left_messages_pallet_owner: [<$left_parachain MessagesPalletOwnerSigningParams>], #[structopt(flatten)] right: [<$right_parachain ConnectionParams>], @@ -99,9 +98,6 @@ macro_rules! declare_parachain_to_parachain_bridge_schema { // default signer, which is always used to sign messages relay transactions on the right chain #[structopt(flatten)] right_sign: [<$right_parachain SigningParams>], - // signer used to sign parameter update transactions at the right parachain - #[structopt(flatten)] - right_messages_pallet_owner: [<$right_parachain MessagesPalletOwnerSigningParams>], // override for right_relay->left-parachain headers signer #[structopt(flatten)] @@ -120,9 +116,9 @@ macro_rules! declare_parachain_to_parachain_bridge_schema { impl [<$left_parachain $right_parachain HeadersAndMessages>] { async fn into_bridge< - Left: ChainWithTransactions + CliChain>, + Left: ChainWithTransactions + CliChain + Parachain, LeftRelay: CliChain, - Right: ChainWithTransactions + CliChain>, + Right: ChainWithTransactions + CliChain + Parachain, RightRelay: CliChain, L2R: CliBridgeBase + MessagesCliBridge @@ -140,14 +136,12 @@ macro_rules! declare_parachain_to_parachain_bridge_schema { client: self.left.into_client::().await?, sign: self.left_sign.to_keypair::()?, transactions_mortality: self.left_sign.transactions_mortality()?, - messages_pallet_owner: self.left_messages_pallet_owner.to_keypair::()?, accounts: vec![], }, BridgeEndCommonParams { client: self.right.into_client::().await?, sign: self.right_sign.to_keypair::()?, transactions_mortality: self.right_sign.transactions_mortality()?, - messages_pallet_owner: self.right_messages_pallet_owner.to_keypair::()?, accounts: vec![], }, )?, @@ -174,10 +168,8 @@ macro_rules! declare_parachain_to_parachain_bridge_schema { #[async_trait] impl< - Left: Chain + ChainWithTransactions + CliChain>, - Right: Chain - + ChainWithTransactions - + CliChain>, + Left: Chain + ChainWithTransactions + CliChain + Parachain, + Right: Chain + ChainWithTransactions + CliChain + Parachain, LeftRelay: Chain + CliChain, RightRelay: Chain @@ -208,8 +200,8 @@ where async fn start_on_demand_headers_relayers( &mut self, ) -> anyhow::Result<( - Arc>>, - Arc>>, + Arc>, + Arc>, )> { self.common.left.accounts.push(TaggedAccount::Headers { id: self.right_headers_to_left_transaction_params.signer.public().into(), @@ -242,31 +234,33 @@ where .await?; let left_relay_to_right_on_demand_headers = - OnDemandHeadersRelay::new::<::RelayFinality>( + OnDemandHeadersRelay::<::RelayFinality>::new( self.left_relay.clone(), self.common.right.client.clone(), self.left_headers_to_right_transaction_params.clone(), self.common.shared.only_mandatory_headers, + Some(self.common.metrics_params.clone()), ); let right_relay_to_left_on_demand_headers = - OnDemandHeadersRelay::new::<::RelayFinality>( + OnDemandHeadersRelay::<::RelayFinality>::new( self.right_relay.clone(), self.common.left.client.clone(), self.right_headers_to_left_transaction_params.clone(), self.common.shared.only_mandatory_headers, + Some(self.common.metrics_params.clone()), ); - let left_to_right_on_demand_parachains = OnDemandParachainsRelay::new::< + let left_to_right_on_demand_parachains = OnDemandParachainsRelay::< ::ParachainFinality, - >( + >::new( self.left_relay.clone(), self.common.right.client.clone(), self.left_parachains_to_right_transaction_params.clone(), Arc::new(left_relay_to_right_on_demand_headers), ); - let right_to_left_on_demand_parachains = OnDemandParachainsRelay::new::< + let right_to_left_on_demand_parachains = OnDemandParachainsRelay::< ::ParachainFinality, - >( + >::new( self.right_relay.clone(), self.common.left.client.clone(), self.right_parachains_to_left_transaction_params.clone(), diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs index 4070783df09..d632cce08e9 100644 --- a/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs @@ -26,9 +26,10 @@ use crate::cli::{ CliChain, }; use bp_polkadot_core::parachains::ParaHash; -use bp_runtime::BlockNumberOf; use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; -use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, Chain, ChainWithTransactions, Client}; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, Chain, ChainWithTransactions, Client, Parachain, +}; use sp_core::Pair; use substrate_relay_helper::{ finality::SubstrateFinalitySyncPipeline, @@ -45,7 +46,9 @@ use substrate_relay_helper::{ pub struct RelayToParachainBridge< L2R: MessagesCliBridge + RelayToRelayHeadersCliBridge, R2L: MessagesCliBridge + ParachainToRelayHeadersCliBridge, -> { +> where + ::Source: Parachain, +{ /// Parameters that are shared by all bridge types. pub common: Full2WayBridgeCommonParams<::Target, ::Target>, @@ -80,9 +83,6 @@ macro_rules! declare_relay_to_parachain_bridge_schema { // default signer, which is always used to sign messages relay transactions on the left chain #[structopt(flatten)] left_sign: [<$left_chain SigningParams>], - // signer used to sign parameter update transactions at the left chain - #[structopt(flatten)] - left_messages_pallet_owner: [<$left_chain MessagesPalletOwnerSigningParams>], #[structopt(flatten)] right: [<$right_parachain ConnectionParams>], @@ -92,10 +92,6 @@ macro_rules! declare_relay_to_parachain_bridge_schema { // default signer, which is always used to sign messages relay transactions on the right chain #[structopt(flatten)] right_sign: [<$right_parachain SigningParams>], - // signer used to sign parameter update transactions at the left chain - #[structopt(flatten)] - right_messages_pallet_owner: [<$right_parachain MessagesPalletOwnerSigningParams>], - // override for right_relay->left headers signer #[structopt(flatten)] @@ -111,8 +107,8 @@ macro_rules! declare_relay_to_parachain_bridge_schema { impl [<$left_chain $right_parachain HeadersAndMessages>] { async fn into_bridge< - Left: ChainWithTransactions + CliChain>, - Right: ChainWithTransactions + CliChain>, + Left: ChainWithTransactions + CliChain, + Right: ChainWithTransactions + CliChain + Parachain, RightRelay: CliChain, L2R: CliBridgeBase + MessagesCliBridge + RelayToRelayHeadersCliBridge, R2L: CliBridgeBase @@ -128,14 +124,12 @@ macro_rules! declare_relay_to_parachain_bridge_schema { client: self.left.into_client::().await?, sign: self.left_sign.to_keypair::()?, transactions_mortality: self.left_sign.transactions_mortality()?, - messages_pallet_owner: self.left_messages_pallet_owner.to_keypair::()?, accounts: vec![], }, BridgeEndCommonParams { client: self.right.into_client::().await?, sign: self.right_sign.to_keypair::()?, transactions_mortality: self.right_sign.transactions_mortality()?, - messages_pallet_owner: self.right_messages_pallet_owner.to_keypair::()?, accounts: vec![], }, )?, @@ -162,10 +156,8 @@ macro_rules! declare_relay_to_parachain_bridge_schema { #[async_trait] impl< - Left: ChainWithTransactions + CliChain>, - Right: Chain - + ChainWithTransactions - + CliChain>, + Left: ChainWithTransactions + CliChain, + Right: Chain + ChainWithTransactions + CliChain + Parachain, RightRelay: Chain + CliChain, L2R: CliBridgeBase @@ -194,8 +186,8 @@ where async fn start_on_demand_headers_relayers( &mut self, ) -> anyhow::Result<( - Arc>>, - Arc>>, + Arc>, + Arc>, )> { self.common.left.accounts.push(TaggedAccount::Headers { id: self.right_headers_to_left_transaction_params.signer.public().into(), @@ -224,22 +216,24 @@ where .await?; let left_to_right_on_demand_headers = - OnDemandHeadersRelay::new::<::Finality>( + OnDemandHeadersRelay::<::Finality>::new( self.common.left.client.clone(), self.common.right.client.clone(), self.left_headers_to_right_transaction_params.clone(), self.common.shared.only_mandatory_headers, + None, ); let right_relay_to_left_on_demand_headers = - OnDemandHeadersRelay::new::<::RelayFinality>( + OnDemandHeadersRelay::<::RelayFinality>::new( self.right_relay.clone(), self.common.left.client.clone(), self.right_headers_to_left_transaction_params.clone(), self.common.shared.only_mandatory_headers, + Some(self.common.metrics_params.clone()), ); - let right_to_left_on_demand_parachains = OnDemandParachainsRelay::new::< + let right_to_left_on_demand_parachains = OnDemandParachainsRelay::< ::ParachainFinality, - >( + >::new( self.right_relay.clone(), self.common.left.client.clone(), self.right_parachains_to_left_transaction_params.clone(), diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs index bda532a2afd..fce80492fc0 100644 --- a/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs @@ -22,7 +22,6 @@ use crate::cli::{ relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams}, CliChain, }; -use bp_runtime::BlockNumberOf; use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, ChainWithTransactions}; use sp_core::Pair; use substrate_relay_helper::{ @@ -65,8 +64,6 @@ macro_rules! declare_relay_to_relay_bridge_schema { right_headers_to_left_sign_override: [<$right_chain HeadersTo $left_chain SigningParams>], #[structopt(flatten)] left_sign: [<$left_chain SigningParams>], - #[structopt(flatten)] - left_messages_pallet_owner: [<$left_chain MessagesPalletOwnerSigningParams>], // default signer, which is always used to sign messages relay transactions on the right chain #[structopt(flatten)] right: [<$right_chain ConnectionParams>], @@ -75,14 +72,12 @@ macro_rules! declare_relay_to_relay_bridge_schema { left_headers_to_right_sign_override: [<$left_chain HeadersTo $right_chain SigningParams>], #[structopt(flatten)] right_sign: [<$right_chain SigningParams>], - #[structopt(flatten)] - right_messages_pallet_owner: [<$right_chain MessagesPalletOwnerSigningParams>], } impl [<$left_chain $right_chain HeadersAndMessages>] { async fn into_bridge< - Left: ChainWithTransactions + CliChain>, - Right: ChainWithTransactions + CliChain>, + Left: ChainWithTransactions + CliChain, + Right: ChainWithTransactions + CliChain, L2R: CliBridgeBase + MessagesCliBridge + RelayToRelayHeadersCliBridge, R2L: CliBridgeBase + MessagesCliBridge + RelayToRelayHeadersCliBridge, >( @@ -95,14 +90,12 @@ macro_rules! declare_relay_to_relay_bridge_schema { client: self.left.into_client::().await?, sign: self.left_sign.to_keypair::()?, transactions_mortality: self.left_sign.transactions_mortality()?, - messages_pallet_owner: self.left_messages_pallet_owner.to_keypair::()?, accounts: vec![], }, BridgeEndCommonParams { client: self.right.into_client::().await?, sign: self.right_sign.to_keypair::()?, transactions_mortality: self.right_sign.transactions_mortality()?, - messages_pallet_owner: self.right_messages_pallet_owner.to_keypair::()?, accounts: vec![], }, )?, @@ -121,8 +114,8 @@ macro_rules! declare_relay_to_relay_bridge_schema { #[async_trait] impl< - Left: ChainWithTransactions + CliChain>, - Right: ChainWithTransactions + CliChain>, + Left: ChainWithTransactions + CliChain, + Right: ChainWithTransactions + CliChain, L2R: CliBridgeBase + MessagesCliBridge + RelayToRelayHeadersCliBridge, @@ -149,8 +142,8 @@ where async fn start_on_demand_headers_relayers( &mut self, ) -> anyhow::Result<( - Arc>>, - Arc>>, + Arc>, + Arc>, )> { self.common.right.accounts.push(TaggedAccount::Headers { id: self.left_to_right_transaction_params.signer.public().into(), @@ -175,18 +168,20 @@ where .await?; let left_to_right_on_demand_headers = - OnDemandHeadersRelay::new::<::Finality>( + OnDemandHeadersRelay::<::Finality>::new( self.common.left.client.clone(), self.common.right.client.clone(), self.left_to_right_transaction_params.clone(), self.common.shared.only_mandatory_headers, + None, ); let right_to_left_on_demand_headers = - OnDemandHeadersRelay::new::<::Finality>( + OnDemandHeadersRelay::<::Finality>::new( self.common.right.client.clone(), self.common.left.client.clone(), self.right_to_left_transaction_params.clone(), self.common.shared.only_mandatory_headers, + None, ); Ok((Arc::new(left_to_right_on_demand_headers), Arc::new(right_to_left_on_demand_headers))) diff --git a/relays/bin-substrate/src/cli/relay_messages.rs b/relays/bin-substrate/src/cli/relay_messages.rs index 7f4dc34ac8b..e0250ef1e47 100644 --- a/relays/bin-substrate/src/cli/relay_messages.rs +++ b/relays/bin-substrate/src/cli/relay_messages.rs @@ -56,7 +56,7 @@ pub struct RelayMessages { #[async_trait] trait MessagesRelayer: MessagesCliBridge where - Self::Source: ChainWithTransactions + CliChain>, + Self::Source: ChainWithTransactions + CliChain, AccountIdOf: From< as Pair>::Public>, AccountIdOf: From< as Pair>::Public>, BalanceOf: TryFrom>, @@ -83,7 +83,7 @@ where source_to_target_headers_relay: None, target_to_source_headers_relay: None, lane_id: data.lane.into(), - metrics_params: data.prometheus_params.into(), + metrics_params: data.prometheus_params.into_metrics_params()?, }) .await .map_err(|e| anyhow::format_err!("{}", e)) diff --git a/relays/bin-substrate/src/cli/relay_parachains.rs b/relays/bin-substrate/src/cli/relay_parachains.rs index 286cecc914b..1234b3a3309 100644 --- a/relays/bin-substrate/src/cli/relay_parachains.rs +++ b/relays/bin-substrate/src/cli/relay_parachains.rs @@ -26,6 +26,7 @@ use bp_polkadot_core::parachains::ParaId; use parachains_relay::parachains_loop::{ AvailableHeader, ParachainSyncParams, SourceClient, TargetClient, }; +use relay_substrate_client::{Parachain, ParachainBase}; use relay_utils::metrics::{GlobalMetrics, StandaloneMetric}; use std::sync::Arc; use structopt::StructOpt; @@ -38,7 +39,11 @@ use substrate_relay_helper::{ TransactionParams, }; -use crate::cli::{bridge::ParachainToRelayHeadersCliBridge, chain_schema::*, PrometheusParams}; +use crate::cli::{ + bridge::{CliBridgeBase, ParachainToRelayHeadersCliBridge}, + chain_schema::*, + PrometheusParams, +}; /// Start parachain heads relayer process. #[derive(StructOpt)] @@ -74,6 +79,7 @@ where SourceClient>, ParachainsTarget: TargetClient>, + ::Source: Parachain, { async fn relay_headers(data: RelayParachains) -> anyhow::Result<()> { let source_client = data.source.into_client::().await?; @@ -92,7 +98,8 @@ where target_transaction_params, ); - let metrics_params: relay_utils::metrics::MetricsParams = data.prometheus_params.into(); + let metrics_params: relay_utils::metrics::MetricsParams = + data.prometheus_params.into_metrics_params()?; GlobalMetrics::new()?.register_and_spawn(&metrics_params.registry)?; parachains_relay::parachains_loop::run( @@ -100,7 +107,7 @@ where target_client, ParachainSyncParams { parachains: vec![ - ParaId(::SOURCE_PARACHAIN_PARA_ID) + ParaId(::SourceParachain::PARACHAIN_ID) ], stall_timeout: std::time::Duration::from_secs(60), strategy: parachains_relay::parachains_loop::ParachainSyncStrategy::Any, diff --git a/relays/bin-substrate/src/cli/resubmit_transactions.rs b/relays/bin-substrate/src/cli/resubmit_transactions.rs index c69f9c8ae4e..2fecd196921 100644 --- a/relays/bin-substrate/src/cli/resubmit_transactions.rs +++ b/relays/bin-substrate/src/cli/resubmit_transactions.rs @@ -403,7 +403,7 @@ async fn update_transaction_tip( })?; let old_tip = unsigned_tx.tip; - let (spec_version, transaction_version) = client.simple_runtime_version().await?; + let runtime_version = client.simple_runtime_version().await?; while current_priority < target_priority { let next_tip = unsigned_tx.tip + tip_step; if next_tip > tip_limit { @@ -425,8 +425,8 @@ async fn update_transaction_tip( at_block.1, C::sign_transaction( SignParam { - spec_version, - transaction_version, + spec_version: runtime_version.spec_version, + transaction_version: runtime_version.transaction_version, genesis_hash: *client.genesis_hash(), signer: transaction_params.signer.clone(), }, @@ -449,8 +449,8 @@ async fn update_transaction_tip( old_tip != unsigned_tx.tip, C::sign_transaction( SignParam { - spec_version, - transaction_version, + spec_version: runtime_version.spec_version, + transaction_version: runtime_version.transaction_version, genesis_hash: *client.genesis_hash(), signer: transaction_params.signer.clone(), }, diff --git a/relays/bin-substrate/src/cli/send_message.rs b/relays/bin-substrate/src/cli/send_message.rs index d492d3422c7..9f14028c4ae 100644 --- a/relays/bin-substrate/src/cli/send_message.rs +++ b/relays/bin-substrate/src/cli/send_message.rs @@ -31,8 +31,7 @@ use crate::{ use async_trait::async_trait; use codec::{Decode, Encode}; use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, Chain, ChainBase, ChainWithTransactions, SignParam, - UnsignedTransaction, + AccountIdOf, AccountKeyPairOf, Chain, ChainBase, ChainWithTransactions, UnsignedTransaction, }; use sp_core::Pair; use sp_runtime::AccountId32; @@ -58,10 +57,7 @@ pub struct SendMessage { #[async_trait] trait MessageSender: MessagesCliBridge where - Self::Source: ChainBase - + ChainWithTransactions - + CliChain> - + CliEncodeMessage, + Self::Source: ChainBase + ChainWithTransactions + CliChain + CliEncodeMessage, ::Balance: Display + From + Into, ::Call: Sync, ::SignedTransaction: Sync, @@ -80,28 +76,17 @@ where data.bridge.bridge_instance_index(), )?; - let source_genesis_hash = *source_client.genesis_hash(); - let (spec_version, transaction_version) = source_client.simple_runtime_version().await?; source_client - .submit_signed_extrinsic( - source_sign.public().into(), - SignParam:: { - spec_version, - transaction_version, - genesis_hash: source_genesis_hash, - signer: source_sign.clone(), - }, - move |_, transaction_nonce| { - let unsigned = UnsignedTransaction::new(send_message_call, transaction_nonce); - log::info!( - target: "bridge", - "Sending message to {}. Size: {}", - Self::Target::NAME, - payload_len, - ); - Ok(unsigned) - }, - ) + .submit_signed_extrinsic(&source_sign, move |_, transaction_nonce| { + let unsigned = UnsignedTransaction::new(send_message_call, transaction_nonce); + log::info!( + target: "bridge", + "Sending message to {}. Size: {}", + Self::Target::NAME, + payload_len, + ); + Ok(unsigned) + }) .await?; Ok(()) diff --git a/relays/bin-substrate/src/main.rs b/relays/bin-substrate/src/main.rs index bc84786ee27..8bd89a41936 100644 --- a/relays/bin-substrate/src/main.rs +++ b/relays/bin-substrate/src/main.rs @@ -24,8 +24,5 @@ mod cli; fn main() { let command = cli::parse_args(); let run = command.run(); - let result = async_std::task::block_on(run); - if let Err(error) = result { - log::error!(target: "bridge", "substrate-relay: {}", error); - } + async_std::task::block_on(run); } diff --git a/relays/client-bridge-hub-rococo/Cargo.toml b/relays/client-bridge-hub-rococo/Cargo.toml index 4a01d81e777..9a9316fa117 100644 --- a/relays/client-bridge-hub-rococo/Cargo.toml +++ b/relays/client-bridge-hub-rococo/Cargo.toml @@ -16,14 +16,12 @@ bp-bridge-hub-rococo = { path = "../../primitives/chain-bridge-hub-rococo" } bp-bridge-hub-wococo = { path = "../../primitives/chain-bridge-hub-wococo" } bp-header-chain = { path = "../../primitives/header-chain" } bp-messages = { path = "../../primitives/messages" } -bp-polkadot-core = { path = "../../primitives/polkadot-core" } -bp-runtime = { path = "../../primitives/runtime" } +bp-parachains = { path = "../../primitives/parachains" } +bp-wococo = { path = "../../primitives/chain-wococo" } bridge-runtime-common = { path = "../../bin/runtime-common" } + # Substrate Dependencies sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } - -[dev-dependencies] -sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-bridge-hub-rococo/src/lib.rs b/relays/client-bridge-hub-rococo/src/lib.rs index 1de0e26d436..8e6e9712925 100644 --- a/relays/client-bridge-hub-rococo/src/lib.rs +++ b/relays/client-bridge-hub-rococo/src/lib.rs @@ -16,11 +16,12 @@ //! Types used to connect to the BridgeHub-Rococo-Substrate parachain. -use bp_messages::{MessageNonce, Weight}; +use bp_bridge_hub_wococo::PolkadotSignedExtension; +use bp_messages::MessageNonce; use codec::Encode; use relay_substrate_client::{ - Chain, ChainBase, ChainWithBalances, ChainWithMessages, ChainWithTransactions, - Error as SubstrateError, SignParam, UnsignedTransaction, + Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, Error as SubstrateError, + SignParam, UnderlyingChainProvider, UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; @@ -34,24 +35,8 @@ pub use runtime_wrapper as runtime; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct BridgeHubRococo; -impl ChainBase for BridgeHubRococo { - type BlockNumber = bp_bridge_hub_rococo::BlockNumber; - type Hash = bp_bridge_hub_rococo::Hash; - type Hasher = bp_bridge_hub_rococo::Hashing; - type Header = bp_bridge_hub_rococo::Header; - - type AccountId = bp_bridge_hub_rococo::AccountId; - type Balance = bp_bridge_hub_rococo::Balance; - type Index = bp_bridge_hub_rococo::Nonce; - type Signature = bp_bridge_hub_rococo::Signature; - - fn max_extrinsic_size() -> u32 { - bp_bridge_hub_rococo::BridgeHubRococo::max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - bp_bridge_hub_rococo::BridgeHubRococo::max_extrinsic_weight() - } +impl UnderlyingChainProvider for BridgeHubRococo { + type Chain = bp_bridge_hub_rococo::BridgeHubRococo; } impl Chain for BridgeHubRococo { @@ -81,7 +66,7 @@ impl ChainWithTransactions for BridgeHubRococo { ) -> Result { let raw_payload = SignedPayload::new( unsigned.call, - bp_bridge_hub_rococo::SignedExtensions::new( + bp_bridge_hub_rococo::BridgeSignedExtension::from_params( param.spec_version, param.transaction_version, unsigned.era, @@ -136,7 +121,6 @@ impl ChainWithMessages for BridgeHubRococo { const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = bp_bridge_hub_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - type WeightToFee = bp_bridge_hub_rococo::WeightToFee; // TODO: fix (https://github.com/paritytech/parity-bridges-common/issues/1640) type WeightInfo = (); } @@ -149,8 +133,10 @@ mod tests { #[test] fn parse_transaction_works() { let unsigned = UnsignedTransaction { - call: runtime::Call::System(runtime::SystemCall::remark(b"Hello world!".to_vec())) - .into(), + call: runtime::Call::System(relay_substrate_client::calls::SystemCall::remark( + b"Hello world!".to_vec(), + )) + .into(), nonce: 777, tip: 888, era: TransactionEra::immortal(), diff --git a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs index 6f83257cf4d..1bb32a4089c 100644 --- a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs +++ b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs @@ -18,202 +18,45 @@ //! Types that are specific to the BridgeHubRococo runtime. -use bp_polkadot_core::PolkadotLike; use codec::{Decode, Encode}; use scale_info::TypeInfo; -pub use bp_bridge_hub_rococo::SS58Prefix; -use bp_messages::UnrewardedRelayersState; -use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; -use bp_runtime::Chain; +use bp_bridge_hub_rococo::BridgeSignedExtension; +pub use bp_header_chain::BridgeGrandpaCallOf; +pub use bp_parachains::BridgeParachainCall; +pub use bridge_runtime_common::messages::BridgeMessagesCallOf; +pub use relay_substrate_client::calls::SystemCall; -// TODO:check-parameter - check SignedExtension /// Unchecked BridgeHubRococo extrinsic. -pub type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic; +pub type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic; -/// Rococo Runtime `Call` enum. +// The indirect pallet call used to sync `Wococo` GRANDPA finality to `BHRococo`. +pub type BridgeWococoGrandpaCall = BridgeGrandpaCallOf; +// The indirect pallet call used to sync `BridgeHubWococo` messages to `BHRococo`. +pub type BridgeWococoMessagesCall = BridgeMessagesCallOf; + +/// `BridgeHubRococo` Runtime `Call` enum. /// -/// The enum represents a subset of possible `Call`s we can send to Rococo chain. +/// The enum represents a subset of possible `Call`s we can send to `BridgeHubRococo` chain. /// Ideally this code would be auto-generated from metadata, because we want to /// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. /// -/// All entries here (like pretty much in the entire file) must be kept in sync with Rococo -/// `construct_runtime`, so that we maintain SCALE-compatibility. -/// -/// // TODO:check-parameter -> change bko-bridge-rococo-wococo when merged to master in cumulus -/// See: [link](https://github.com/paritytech/cumulus/blob/bko-bridge-rococo-wococo/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs) +/// All entries here (like pretty much in the entire file) must be kept in sync with +/// `BridgeHubRococo` `construct_runtime`, so that we maintain SCALE-compatibility. #[allow(clippy::large_enum_variant)] #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] pub enum Call { - /// System pallet. + #[cfg(test)] #[codec(index = 0)] System(SystemCall), + /// Wococo bridge pallet. #[codec(index = 41)] BridgeWococoGrandpa(BridgeWococoGrandpaCall), - /// Rococo bridge pallet. - #[codec(index = 43)] - BridgeRococoGrandpa(BridgeRococoGrandpaCall), - /// Wococo parachain bridge pallet. #[codec(index = 42)] BridgeWococoParachain(BridgeParachainCall), - /// Rococo parachain bridge pallet. - #[codec(index = 44)] - BridgeRococoParachain(BridgeParachainCall), - /// Wococo messages bridge pallet. #[codec(index = 46)] BridgeWococoMessages(BridgeWococoMessagesCall), - /// Rococo messages bridge pallet. - #[codec(index = 45)] - BridgeRococoMessages(BridgeRococoMessagesCall), -} - -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum SystemCall { - #[codec(index = 1)] - remark(Vec), -} - -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeWococoGrandpaCall { - #[codec(index = 0)] - submit_finality_proof( - Box<::Header>, - bp_header_chain::justification::GrandpaJustification<::Header>, - ), - #[codec(index = 1)] - initialize(bp_header_chain::InitializationData<::Header>), -} - -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeRococoGrandpaCall { - #[codec(index = 0)] - submit_finality_proof( - Box<::Header>, - bp_header_chain::justification::GrandpaJustification<::Header>, - ), - #[codec(index = 1)] - initialize(bp_header_chain::InitializationData<::Header>), -} - -pub type RelayBlockHash = bp_polkadot_core::Hash; -pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; - -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeParachainCall { - #[codec(index = 0)] - submit_parachain_heads( - (RelayBlockNumber, RelayBlockHash), - Vec<(ParaId, ParaHash)>, - ParaHeadsProof, - ), -} - -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeWococoMessagesCall { - #[codec(index = 2)] - receive_messages_proof( - relay_substrate_client::AccountIdOf, - bridge_runtime_common::messages::target::FromBridgedChainMessagesProof< - relay_substrate_client::HashOf, - >, - u32, - bp_messages::Weight, - ), - - #[codec(index = 3)] - receive_messages_delivery_proof( - bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof< - relay_substrate_client::HashOf, - >, - UnrewardedRelayersState, - ), -} - -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeRococoMessagesCall { - #[codec(index = 2)] - receive_messages_proof( - relay_substrate_client::AccountIdOf, - bridge_runtime_common::messages::target::FromBridgedChainMessagesProof< - relay_substrate_client::HashOf, - >, - u32, - bp_messages::Weight, - ), - - #[codec(index = 3)] - receive_messages_delivery_proof( - bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof< - relay_substrate_client::HashOf, - >, - UnrewardedRelayersState, - ), -} - -impl sp_runtime::traits::Dispatchable for Call { - type RuntimeOrigin = (); - type Config = (); - type Info = (); - type PostInfo = (); - - fn dispatch( - self, - _origin: Self::RuntimeOrigin, - ) -> sp_runtime::DispatchResultWithInfo { - unimplemented!("The Call is not expected to be dispatched.") - } -} - -#[cfg(test)] -mod tests { - use super::*; - use bp_runtime::BasicOperatingMode; - use sp_core::hexdisplay::HexDisplay; - use sp_finality_grandpa::AuthorityList; - use sp_runtime::traits::Header; - use std::str::FromStr; - - pub type RelayBlockHasher = bp_polkadot_core::Hasher; - pub type RelayBlockHeader = sp_runtime::generic::Header; - - #[test] - fn encode_decode_calls() { - let header = RelayBlockHeader::new( - 75, - bp_polkadot_core::Hash::from_str( - "0xd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d", - ) - .expect("invalid value"), - bp_polkadot_core::Hash::from_str( - "0x92b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141b", - ) - .expect("invalid value"), - bp_polkadot_core::Hash::from_str( - "0xae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d", - ) - .expect("invalid value"), - Default::default(), - ); - let init_data = bp_header_chain::InitializationData { - header: Box::new(header), - authority_list: AuthorityList::default(), - set_id: 6, - operating_mode: BasicOperatingMode::Normal, - }; - let call = BridgeRococoGrandpaCall::initialize(init_data); - let tx = Call::BridgeRococoGrandpa(call); - - // encode call as hex string - let hex_encoded_call = format!("0x{:?}", HexDisplay::from(&Encode::encode(&tx))); - assert_eq!(hex_encoded_call, "0x2b01ae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d2d0192b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141bd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d0000060000000000000000"); - } } diff --git a/relays/client-bridge-hub-wococo/Cargo.toml b/relays/client-bridge-hub-wococo/Cargo.toml index fa28dfde202..7c9b90975d6 100644 --- a/relays/client-bridge-hub-wococo/Cargo.toml +++ b/relays/client-bridge-hub-wococo/Cargo.toml @@ -12,11 +12,21 @@ relay-substrate-client = { path = "../client-substrate" } # Bridge dependencies +bp-bridge-hub-rococo = { path = "../../primitives/chain-bridge-hub-rococo" } bp-bridge-hub-wococo = { path = "../../primitives/chain-bridge-hub-wococo" } +bp-header-chain = { path = "../../primitives/header-chain" } bp-messages = { path = "../../primitives/messages" } -relay-bridge-hub-rococo-client = { path = "../client-bridge-hub-rococo" } +bp-parachains = { path = "../../primitives/parachains" } +bp-rococo = { path = "../../primitives/chain-rococo" } +bp-runtime = { path = "../../primitives/runtime" } + +bridge-runtime-common = { path = "../../bin/runtime-common" } # Substrate Dependencies sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[dev-dependencies] +bp-polkadot-core = { path = "../../primitives/polkadot-core" } +sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-bridge-hub-wococo/src/lib.rs b/relays/client-bridge-hub-wococo/src/lib.rs index 46bac2e826e..3fd8187fa1f 100644 --- a/relays/client-bridge-hub-wococo/src/lib.rs +++ b/relays/client-bridge-hub-wococo/src/lib.rs @@ -16,11 +16,12 @@ //! Types used to connect to the BridgeHub-Wococo-Substrate parachain. -use bp_messages::{MessageNonce, Weight}; +use bp_bridge_hub_wococo::PolkadotSignedExtension; +use bp_messages::MessageNonce; use codec::Encode; use relay_substrate_client::{ - Chain, ChainBase, ChainWithBalances, ChainWithMessages, ChainWithTransactions, - Error as SubstrateError, SignParam, UnsignedTransaction, + Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, Error as SubstrateError, + SignParam, UnderlyingChainProvider, UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; @@ -34,24 +35,8 @@ pub use runtime_wrapper as runtime; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct BridgeHubWococo; -impl ChainBase for BridgeHubWococo { - type BlockNumber = bp_bridge_hub_wococo::BlockNumber; - type Hash = bp_bridge_hub_wococo::Hash; - type Hasher = bp_bridge_hub_wococo::Hashing; - type Header = bp_bridge_hub_wococo::Header; - - type AccountId = bp_bridge_hub_wococo::AccountId; - type Balance = bp_bridge_hub_wococo::Balance; - type Index = bp_bridge_hub_wococo::Nonce; - type Signature = bp_bridge_hub_wococo::Signature; - - fn max_extrinsic_size() -> u32 { - bp_bridge_hub_wococo::BridgeHubWococo::max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - bp_bridge_hub_wococo::BridgeHubWococo::max_extrinsic_weight() - } +impl UnderlyingChainProvider for BridgeHubWococo { + type Chain = bp_bridge_hub_wococo::BridgeHubWococo; } impl Chain for BridgeHubWococo { @@ -81,7 +66,7 @@ impl ChainWithTransactions for BridgeHubWococo { ) -> Result { let raw_payload = SignedPayload::new( unsigned.call, - bp_bridge_hub_wococo::SignedExtensions::new( + bp_bridge_hub_wococo::BridgeSignedExtension::from_params( param.spec_version, param.transaction_version, unsigned.era, @@ -136,7 +121,6 @@ impl ChainWithMessages for BridgeHubWococo { const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = bp_bridge_hub_wococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - type WeightToFee = bp_bridge_hub_wococo::WeightToFee; // TODO: fix (https://github.com/paritytech/parity-bridges-common/issues/1640) type WeightInfo = (); } diff --git a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs index 8d94e7f9026..2158ad0e659 100644 --- a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs +++ b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs @@ -16,13 +16,91 @@ //! Types that are specific to the BridgeHubWococo runtime. -pub use bp_bridge_hub_wococo::SS58Prefix; - -// We reuse everything from rococo runtime wrapper -pub type Call = relay_bridge_hub_rococo_client::runtime::Call; -pub type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic; -pub type BridgeGrandpaRococoCall = relay_bridge_hub_rococo_client::runtime::BridgeRococoGrandpaCall; -pub type BridgeParachainCall = relay_bridge_hub_rococo_client::runtime::BridgeParachainCall; -pub type BridgeRococoMessagesCall = - relay_bridge_hub_rococo_client::runtime::BridgeRococoMessagesCall; -pub type SystemCall = relay_bridge_hub_rococo_client::runtime::SystemCall; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; + +use bp_bridge_hub_wococo::BridgeSignedExtension; +pub use bp_header_chain::BridgeGrandpaCallOf; +pub use bp_parachains::BridgeParachainCall; +pub use bridge_runtime_common::messages::BridgeMessagesCallOf; +pub use relay_substrate_client::calls::SystemCall; + +/// Unchecked BridgeHubWococo extrinsic. +pub type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic; + +// The indirect pallet call used to sync `Rococo` GRANDPA finality to `BHWococo`. +pub type BridgeRococoGrandpaCall = BridgeGrandpaCallOf; +// The indirect pallet call used to sync `BridgeHubRococo` messages to `BridgeHubWococo`. +pub type BridgeRococoMessagesCall = BridgeMessagesCallOf; + +/// `BridgeHubWococo` Runtime `Call` enum. +/// +/// The enum represents a subset of possible `Call`s we can send to `BridgeHubWococo` chain. +/// Ideally this code would be auto-generated from metadata, because we want to +/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. +/// +/// All entries here (like pretty much in the entire file) must be kept in sync with +/// `BridgeHubWococo` `construct_runtime`, so that we maintain SCALE-compatibility. +#[allow(clippy::large_enum_variant)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum Call { + #[cfg(test)] + #[codec(index = 0)] + System(SystemCall), + + /// Rococo bridge pallet. + #[codec(index = 43)] + BridgeRococoGrandpa(BridgeRococoGrandpaCall), + /// Rococo parachain bridge pallet. + #[codec(index = 44)] + BridgeRococoParachain(BridgeParachainCall), + /// Rococo messages bridge pallet. + #[codec(index = 45)] + BridgeRococoMessages(BridgeRococoMessagesCall), +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_runtime::BasicOperatingMode; + use sp_core::hexdisplay::HexDisplay; + use sp_finality_grandpa::AuthorityList; + use sp_runtime::traits::Header; + use std::str::FromStr; + + pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; + pub type RelayBlockHasher = bp_polkadot_core::Hasher; + pub type RelayBlockHeader = sp_runtime::generic::Header; + + #[test] + fn encode_decode_calls() { + let header = RelayBlockHeader::new( + 75, + bp_polkadot_core::Hash::from_str( + "0xd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d", + ) + .expect("invalid value"), + bp_polkadot_core::Hash::from_str( + "0x92b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141b", + ) + .expect("invalid value"), + bp_polkadot_core::Hash::from_str( + "0xae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d", + ) + .expect("invalid value"), + Default::default(), + ); + let init_data = bp_header_chain::InitializationData { + header: Box::new(header), + authority_list: AuthorityList::default(), + set_id: 6, + operating_mode: BasicOperatingMode::Normal, + }; + let call = BridgeRococoGrandpaCall::initialize(init_data); + let tx = Call::BridgeRococoGrandpa(call); + + // encode call as hex string + let hex_encoded_call = format!("0x{:?}", HexDisplay::from(&Encode::encode(&tx))); + assert_eq!(hex_encoded_call, "0x2b01ae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d2d0192b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141bd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d0000060000000000000000"); + } +} diff --git a/relays/client-kusama/Cargo.toml b/relays/client-kusama/Cargo.toml index 2efe62d8ec8..e4d83d88ceb 100644 --- a/relays/client-kusama/Cargo.toml +++ b/relays/client-kusama/Cargo.toml @@ -15,5 +15,4 @@ bp-kusama = { path = "../../primitives/chain-kusama" } # Substrate Dependencies -frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-kusama/src/lib.rs b/relays/client-kusama/src/lib.rs index 25d7bb76e59..cd6e23a4607 100644 --- a/relays/client-kusama/src/lib.rs +++ b/relays/client-kusama/src/lib.rs @@ -17,8 +17,7 @@ //! Types used to connect to the Kusama chain. use bp_kusama::AccountInfoStorageMapKeyProvider; -use frame_support::weights::Weight; -use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, ChainWithGrandpa}; +use relay_substrate_client::{Chain, ChainWithBalances, ChainWithGrandpa, UnderlyingChainProvider}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -29,24 +28,8 @@ pub type HeaderId = relay_utils::HeaderId u32 { - bp_kusama::Kusama::max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - bp_kusama::Kusama::max_extrinsic_weight() - } +impl UnderlyingChainProvider for Kusama { + type Chain = bp_kusama::Kusama; } impl Chain for Kusama { diff --git a/relays/client-millau/src/lib.rs b/relays/client-millau/src/lib.rs index f8b350d3d0c..fb901a4b2de 100644 --- a/relays/client-millau/src/lib.rs +++ b/relays/client-millau/src/lib.rs @@ -18,10 +18,10 @@ use bp_messages::MessageNonce; use codec::{Compact, Decode, Encode}; -use frame_support::weights::Weight; use relay_substrate_client::{ - BalanceOf, Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, - ChainWithTransactions, Error as SubstrateError, IndexOf, SignParam, UnsignedTransaction, + BalanceOf, Chain, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, + ChainWithTransactions, ChainWithUtilityPallet, Error as SubstrateError, + FullRuntimeUtilityPallet, IndexOf, SignParam, UnderlyingChainProvider, UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; @@ -34,24 +34,8 @@ pub type HeaderId = relay_utils::HeaderId u32 { - bp_millau::Millau::max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - bp_millau::Millau::max_extrinsic_weight() - } +impl UnderlyingChainProvider for Millau { + type Chain = bp_millau::Millau; } impl ChainWithGrandpa for Millau { @@ -69,7 +53,6 @@ impl ChainWithMessages for Millau { bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - type WeightToFee = bp_millau::WeightToFee; type WeightInfo = (); } @@ -114,6 +97,7 @@ impl ChainWithTransactions for Millau { frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(unsigned.tip), millau_runtime::BridgeRejectObsoleteHeadersAndMessages, + millau_runtime::BridgeRefundRialtoParachainRelayers::default(), ), ( (), @@ -125,6 +109,7 @@ impl ChainWithTransactions for Millau { (), (), (), + () ), ); let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); @@ -164,6 +149,10 @@ impl ChainWithTransactions for Millau { } } +impl ChainWithUtilityPallet for Millau { + type UtilityPallet = FullRuntimeUtilityPallet; +} + /// Millau signing params. pub type SigningParams = sp_core::sr25519::Pair; diff --git a/relays/client-polkadot/Cargo.toml b/relays/client-polkadot/Cargo.toml index aefbadfdd18..cc16ec1f5c3 100644 --- a/relays/client-polkadot/Cargo.toml +++ b/relays/client-polkadot/Cargo.toml @@ -15,5 +15,4 @@ bp-polkadot = { path = "../../primitives/chain-polkadot" } # Substrate Dependencies -frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-polkadot/src/lib.rs b/relays/client-polkadot/src/lib.rs index 8bba686cfe9..ac67d55ab54 100644 --- a/relays/client-polkadot/src/lib.rs +++ b/relays/client-polkadot/src/lib.rs @@ -17,8 +17,7 @@ //! Types used to connect to the Polkadot chain. use bp_polkadot::AccountInfoStorageMapKeyProvider; -use frame_support::weights::Weight; -use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, ChainWithGrandpa}; +use relay_substrate_client::{Chain, ChainWithBalances, ChainWithGrandpa, UnderlyingChainProvider}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -29,24 +28,8 @@ pub type HeaderId = relay_utils::HeaderId u32 { - bp_polkadot::Polkadot::max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - bp_polkadot::Polkadot::max_extrinsic_weight() - } +impl UnderlyingChainProvider for Polkadot { + type Chain = bp_polkadot::Polkadot; } impl Chain for Polkadot { diff --git a/relays/client-rialto-parachain/Cargo.toml b/relays/client-rialto-parachain/Cargo.toml index 915d0e786cc..f3c4a1d42f8 100644 --- a/relays/client-rialto-parachain/Cargo.toml +++ b/relays/client-rialto-parachain/Cargo.toml @@ -7,19 +7,20 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5" } -relay-substrate-client = { path = "../client-substrate" } -relay-utils = { path = "../utils" } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } # Bridge dependencies +bp-header-chain = { path = "../../primitives/header-chain" } bp-messages = { path = "../../primitives/messages" } +bp-millau = { path = "../../primitives/chain-millau" } +bp-polkadot-core = { path = "../../primitives/polkadot-core" } bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" } -rialto-parachain-runtime = { path = "../../bin/rialto-parachain/runtime" } + +bridge-runtime-common = { path = "../../bin/runtime-common" } +relay-substrate-client = { path = "../client-substrate" } # Substrate Dependencies -frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } -pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-rialto-parachain/src/lib.rs b/relays/client-rialto-parachain/src/lib.rs index 8cf74bdf9b1..ebaac73e036 100644 --- a/relays/client-rialto-parachain/src/lib.rs +++ b/relays/client-rialto-parachain/src/lib.rs @@ -16,43 +16,30 @@ //! Types used to connect to the Rialto-Substrate chain. +pub mod runtime_wrapper; + use bp_messages::MessageNonce; +use bp_polkadot_core::{DefaultSignedExtension, PolkadotSignedExtension}; use codec::Encode; -use frame_support::weights::Weight; use relay_substrate_client::{ - Chain, ChainBase, ChainWithBalances, ChainWithMessages, ChainWithTransactions, - Error as SubstrateError, SignParam, UnsignedTransaction, + Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, Error as SubstrateError, + SignParam, UnderlyingChainProvider, UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; -use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount, MultiAddress}; use std::time::Duration; -/// Rialto header id. -pub type HeaderId = - relay_utils::HeaderId; +pub use runtime_wrapper as runtime; + +/// The address format for describing accounts. +pub type Address = MultiAddress; /// Rialto parachain definition #[derive(Debug, Clone, Copy)] pub struct RialtoParachain; -impl ChainBase for RialtoParachain { - type BlockNumber = rialto_parachain_runtime::BlockNumber; - type Hash = rialto_parachain_runtime::Hash; - type Hasher = rialto_parachain_runtime::Hashing; - type Header = rialto_parachain_runtime::Header; - - type AccountId = rialto_parachain_runtime::AccountId; - type Balance = rialto_parachain_runtime::Balance; - type Index = rialto_parachain_runtime::Index; - type Signature = rialto_parachain_runtime::Signature; - - fn max_extrinsic_size() -> u32 { - bp_rialto_parachain::RialtoParachain::max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - bp_rialto_parachain::RialtoParachain::max_extrinsic_weight() - } +impl UnderlyingChainProvider for RialtoParachain { + type Chain = bp_rialto_parachain::RialtoParachain; } impl Chain for RialtoParachain { @@ -63,18 +50,13 @@ impl Chain for RialtoParachain { bp_rialto_parachain::BEST_FINALIZED_RIALTO_PARACHAIN_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); - type SignedBlock = rialto_parachain_runtime::SignedBlock; - type Call = rialto_parachain_runtime::RuntimeCall; + type SignedBlock = bp_polkadot_core::SignedBlock; + type Call = runtime::Call; } impl ChainWithBalances for RialtoParachain { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { - use frame_support::storage::generator::StorageMap; - StorageKey( - frame_system::Account::::storage_map_final_key( - account_id, - ), - ) + bp_polkadot_core::AccountInfoStorageMapKeyProvider::final_key(account_id) } } @@ -89,51 +71,36 @@ impl ChainWithMessages for RialtoParachain { bp_rialto_parachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = bp_rialto_parachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - type WeightToFee = bp_rialto_parachain::WeightToFee; type WeightInfo = (); } impl ChainWithTransactions for RialtoParachain { type AccountKeyPair = sp_core::sr25519::Pair; - type SignedTransaction = rialto_parachain_runtime::UncheckedExtrinsic; + type SignedTransaction = + bp_polkadot_core::UncheckedExtrinsic; fn sign_transaction( param: SignParam, unsigned: UnsignedTransaction, ) -> Result { - let raw_payload = SignedPayload::from_raw( + let raw_payload = SignedPayload::new( unsigned.call, - ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckEra::::from( - unsigned.era.frame_era(), - ), - frame_system::CheckNonce::::from(unsigned.nonce), - frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::< - rialto_parachain_runtime::Runtime, - >::from(unsigned.tip), - ), - ( - (), + bp_polkadot_core::DefaultSignedExtension::from_params( param.spec_version, param.transaction_version, + unsigned.era, param.genesis_hash, - unsigned.era.signed_payload(param.genesis_hash), - (), - (), - (), + unsigned.nonce, + unsigned.tip, ), - ); + )?; + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); let signer: sp_runtime::MultiSigner = param.signer.public().into(); let (call, extra, _) = raw_payload.deconstruct(); - Ok(rialto_parachain_runtime::UncheckedExtrinsic::new_signed( - call.into_decoded()?, + Ok(Self::SignedTransaction::new_signed( + call, signer.into_account().into(), signature.into(), extra, @@ -147,14 +114,13 @@ impl ChainWithTransactions for RialtoParachain { fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { tx.signature .as_ref() - .map(|(address, _, _)| { - *address == rialto_parachain_runtime::Address::Id(signer.public().into()) - }) + .map(|(address, _, _)| *address == Address::Id(signer.public().into())) .unwrap_or(false) } - fn parse_transaction(_tx: Self::SignedTransaction) -> Option> { - None + fn parse_transaction(tx: Self::SignedTransaction) -> Option> { + let extra = &tx.signature.as_ref()?.2; + Some(UnsignedTransaction::new(tx.function, extra.nonce()).tip(extra.tip())) } } @@ -162,4 +128,4 @@ impl ChainWithTransactions for RialtoParachain { pub type SigningParams = sp_core::sr25519::Pair; /// RialtoParachain header type used in headers sync. -pub type SyncHeader = relay_substrate_client::SyncHeader; +pub type SyncHeader = relay_substrate_client::SyncHeader; diff --git a/relays/client-rialto-parachain/src/runtime_wrapper.rs b/relays/client-rialto-parachain/src/runtime_wrapper.rs new file mode 100644 index 00000000000..b595d145d01 --- /dev/null +++ b/relays/client-rialto-parachain/src/runtime_wrapper.rs @@ -0,0 +1,57 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types that are specific to the `RialtoParachain` runtime. Normally we could use the full +//! `RialtoParachain` runtime here, since it is constructed in this repo and we have access to it. +//! However we use a wrapped runtime instead in order to test the indirect runtime calls +//! functionality. + +use codec::{Decode, Encode}; +use scale_info::TypeInfo; + +use bp_header_chain::BridgeGrandpaCallOf; +use bridge_runtime_common::messages::BridgeMessagesCallOf; +use relay_substrate_client::calls::{SudoCall, XcmCall}; + +// The indirect pallet call used to sync `Millau` GRANDPA finality to `RialtoParachain`. +pub type BridgeMillauGrandpaCall = BridgeGrandpaCallOf; +// The indirect pallet call used to sync `Millau` messages to `RialtoParachain`. +pub type BridgeMillauMessagesCall = BridgeMessagesCallOf; + +/// `RialtoParachain` Runtime `Call` enum. +/// +/// The enum represents a subset of possible `Call`s we can send to `RialtoParachain` chain. +/// +/// All entries here (like pretty much in the entire file) must be kept in sync with +/// `RialtoParachain` `construct_runtime`, so that we maintain SCALE-compatibility. +#[allow(clippy::large_enum_variant)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum Call { + /// `Sudo` pallet. + #[codec(index = 2)] + Sudo(SudoCall), + + /// `Xcm` pallet. + #[codec(index = 51)] + PolkadotXcm(XcmCall), + + /// Millau GRANDPA bridge pallet. + #[codec(index = 55)] + BridgeMillauGrandpa(BridgeMillauGrandpaCall), + /// Millau messages bridge pallet. + #[codec(index = 56)] + BridgeMillauMessages(BridgeMillauMessagesCall), +} diff --git a/relays/client-rialto/src/lib.rs b/relays/client-rialto/src/lib.rs index 23ecbbd47f6..4c3a9d1e18e 100644 --- a/relays/client-rialto/src/lib.rs +++ b/relays/client-rialto/src/lib.rs @@ -18,11 +18,10 @@ use bp_messages::MessageNonce; use codec::{Compact, Decode, Encode}; -use frame_support::weights::Weight; use relay_substrate_client::{ - BalanceOf, Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, + BalanceOf, Chain, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, ChainWithTransactions, Error as SubstrateError, IndexOf, RelayChain, SignParam, - UnsignedTransaction, + UnderlyingChainProvider, UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; @@ -35,24 +34,8 @@ pub type HeaderId = relay_utils::HeaderId u32 { - bp_rialto::Rialto::max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - bp_rialto::Rialto::max_extrinsic_weight() - } +impl UnderlyingChainProvider for Rialto { + type Chain = bp_rialto::Rialto; } impl Chain for Rialto { @@ -88,7 +71,6 @@ impl ChainWithMessages for Rialto { bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - type WeightToFee = bp_rialto::WeightToFee; type WeightInfo = (); } diff --git a/relays/client-rococo/Cargo.toml b/relays/client-rococo/Cargo.toml index 14d3c8ca232..cc197ba8562 100644 --- a/relays/client-rococo/Cargo.toml +++ b/relays/client-rococo/Cargo.toml @@ -15,5 +15,4 @@ bp-rococo = { path = "../../primitives/chain-rococo" } # Substrate Dependencies -frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-rococo/src/lib.rs b/relays/client-rococo/src/lib.rs index 38a95042ff3..a764759906a 100644 --- a/relays/client-rococo/src/lib.rs +++ b/relays/client-rococo/src/lib.rs @@ -16,8 +16,9 @@ //! Types used to connect to the Rococo-Substrate chain. -use frame_support::weights::Weight; -use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, RelayChain}; +use relay_substrate_client::{ + Chain, ChainWithBalances, ChainWithGrandpa, RelayChain, UnderlyingChainProvider, +}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -31,24 +32,8 @@ pub type SyncHeader = relay_substrate_client::SyncHeader; #[derive(Debug, Clone, Copy)] pub struct Rococo; -impl ChainBase for Rococo { - type BlockNumber = bp_rococo::BlockNumber; - type Hash = bp_rococo::Hash; - type Hasher = bp_rococo::Hashing; - type Header = bp_rococo::Header; - - type AccountId = bp_rococo::AccountId; - type Balance = bp_rococo::Balance; - type Index = bp_rococo::Nonce; - type Signature = bp_rococo::Signature; - - fn max_extrinsic_size() -> u32 { - bp_rococo::Rococo::max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - bp_rococo::Rococo::max_extrinsic_weight() - } +impl UnderlyingChainProvider for Rococo { + type Chain = bp_rococo::Rococo; } impl Chain for Rococo { @@ -74,5 +59,5 @@ impl ChainWithBalances for Rococo { impl RelayChain for Rococo { const PARAS_PALLET_NAME: &'static str = bp_rococo::PARAS_PALLET_NAME; - const PARACHAINS_FINALITY_PALLET_NAME: &'static str = "bridgeRococoParachain"; + const PARACHAINS_FINALITY_PALLET_NAME: &'static str = "BridgeRococoParachain"; } diff --git a/relays/client-substrate/Cargo.toml b/relays/client-substrate/Cargo.toml index 60ef1c67a26..229de0cc09d 100644 --- a/relays/client-substrate/Cargo.toml +++ b/relays/client-substrate/Cargo.toml @@ -14,6 +14,7 @@ jsonrpsee = { version = "0.15", features = ["macros", "ws-client"] } log = "0.4.17" num-traits = "0.2" rand = "0.7" +scale-info = { version = "2.1.1", features = ["derive"] } tokio = { version = "1.8", features = ["rt-multi-thread"] } thiserror = "1.0.26" @@ -21,6 +22,7 @@ thiserror = "1.0.26" bp-header-chain = { path = "../../primitives/header-chain" } bp-messages = { path = "../../primitives/messages" } +bp-polkadot-core = { path = "../../primitives/polkadot-core" } bp-runtime = { path = "../../primitives/runtime" } pallet-bridge-messages = { path = "../../modules/messages" } finality-relay = { path = "../finality" } @@ -33,17 +35,21 @@ frame-system = { git = "https://github.com/paritytech/substrate", branch = "mast pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-utility = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-chain-spec = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-rpc-api = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-transaction-pool-api = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-storage = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-version = { git = "https://github.com/paritytech/substrate", branch = "master" } +# Polkadot Dependencies + +xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + [features] default = [] test-helpers = [] diff --git a/relays/client-substrate/src/calls.rs b/relays/client-substrate/src/calls.rs new file mode 100644 index 00000000000..89fc49a209a --- /dev/null +++ b/relays/client-substrate/src/calls.rs @@ -0,0 +1,50 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Basic runtime calls. + +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_std::{boxed::Box, vec::Vec}; + +use xcm::{VersionedMultiLocation, VersionedXcm}; + +/// A minimized version of `frame-system::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum SystemCall { + /// `frame-system::Call::remark` + #[codec(index = 1)] + remark(Vec), +} + +/// A minimized version of `pallet-sudo::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum SudoCall { + /// `pallet-sudo::Call::sudo` + #[codec(index = 0)] + sudo(Box), +} + +/// A minimized version of `pallet-xcm::Call`, that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum XcmCall { + /// `pallet-xcm::Call::send` + #[codec(index = 0)] + send(Box, Box>), +} diff --git a/relays/client-substrate/src/chain.rs b/relays/client-substrate/src/chain.rs index 0e19cf92b77..db08fc1f98c 100644 --- a/relays/client-substrate/src/chain.rs +++ b/relays/client-substrate/src/chain.rs @@ -16,17 +16,17 @@ use bp_messages::MessageNonce; use bp_runtime::{ - Chain as ChainBase, EncodedOrDecodedCall, HashOf, TransactionEra, TransactionEraOf, + Chain as ChainBase, EncodedOrDecodedCall, HashOf, Parachain as ParachainBase, TransactionEra, + TransactionEraOf, UnderlyingChainProvider, }; use codec::{Codec, Encode}; -use frame_support::weights::WeightToFee; use jsonrpsee::core::{DeserializeOwned, Serialize}; use num_traits::Zero; use sc_transaction_pool_api::TransactionStatus; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{ generic::SignedBlock, - traits::{Block as BlockT, Dispatchable, Member}, + traits::{Block as BlockT, Member}, ConsensusEngineId, EncodedJustification, }; use std::{fmt::Debug, time::Duration}; @@ -56,7 +56,7 @@ pub trait Chain: ChainBase + Clone { /// Block type. type SignedBlock: Member + Serialize + DeserializeOwned + BlockWithJustification; /// The aggregated `Call` type. - type Call: Clone + Codec + Dispatchable + Debug + Send; + type Call: Clone + Codec + Debug + Send; } /// Substrate-based relay chain that supports parachains. @@ -73,6 +73,11 @@ pub trait RelayChain: Chain { const PARACHAINS_FINALITY_PALLET_NAME: &'static str; } +/// Substrate-based parachain from minimal relay-client point of view. +pub trait Parachain: Chain + ParachainBase {} + +impl Parachain for T where T: UnderlyingChainProvider + Chain + ParachainBase {} + /// Substrate-based chain that is using direct GRANDPA finality from minimal relay-client point of /// view. /// @@ -111,16 +116,12 @@ pub trait ChainWithMessages: Chain { /// `ChainWithMessages`. const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce; - /// Type that is used by the chain, to convert from weight to fee. - type WeightToFee: WeightToFee; /// Weights of message pallet calls. type WeightInfo: pallet_bridge_messages::WeightInfoExt; } /// Call type used by the chain. pub type CallOf = ::Call; -/// Weight-to-Fee type used by the chain. -pub type WeightToFeeOf = ::WeightToFee; /// Transaction status of the chain. pub type TransactionStatusOf = TransactionStatus, HashOf>; @@ -233,3 +234,31 @@ impl BlockWithJustification for SignedBlock self.justifications.as_ref().and_then(|j| j.get(engine_id)) } } + +/// Trait that provides functionality defined inside `pallet-utility` +pub trait UtilityPallet { + /// Create batch call from given calls vector. + fn build_batch_call(calls: Vec) -> C::Call; +} + +/// Structure that implements `UtilityPalletProvider` based on a full runtime. +pub struct FullRuntimeUtilityPallet { + _phantom: std::marker::PhantomData, +} + +impl UtilityPallet for FullRuntimeUtilityPallet +where + C: Chain, + R: pallet_utility::Config, + ::RuntimeCall: From>, +{ + fn build_batch_call(calls: Vec) -> C::Call { + pallet_utility::Call::batch_all { calls }.into() + } +} + +/// Substrate-based chain that uses `pallet-utility`. +pub trait ChainWithUtilityPallet: Chain { + /// The utility pallet provider. + type UtilityPallet: UtilityPallet; +} diff --git a/relays/client-substrate/src/client.rs b/relays/client-substrate/src/client.rs index 4f783291ee3..62d6a7c599e 100644 --- a/relays/client-substrate/src/client.rs +++ b/relays/client-substrate/src/client.rs @@ -23,8 +23,8 @@ use crate::{ SubstrateFrameSystemClient, SubstrateStateClient, SubstrateSystemClient, SubstrateTransactionPaymentClient, }, - transaction_stall_timeout, ConnectionParams, Error, HashOf, HeaderIdOf, Result, SignParam, - TransactionTracker, UnsignedTransaction, + transaction_stall_timeout, AccountKeyPairOf, ConnectionParams, Error, HashOf, HeaderIdOf, + Result, SignParam, TransactionTracker, UnsignedTransaction, }; use async_std::sync::{Arc, Mutex}; @@ -43,7 +43,7 @@ use pallet_transaction_payment::InclusionFee; use relay_utils::{relay_loop::RECONNECT_DELAY, STALL_TIMEOUT}; use sp_core::{ storage::{StorageData, StorageKey}, - Bytes, Hasher, + Bytes, Hasher, Pair, }; use sp_runtime::{ traits::Header as HeaderT, @@ -57,21 +57,50 @@ const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities"; const SUB_API_TXPOOL_VALIDATE_TRANSACTION: &str = "TaggedTransactionQueue_validate_transaction"; const MAX_SUBSCRIPTION_CAPACITY: usize = 4096; +/// The difference between best block number and number of its ancestor, that is enough +/// for us to consider that ancestor an "ancient" block with dropped state. +/// +/// The relay does not assume that it is connected to the archive node, so it always tries +/// to use the best available chain state. But sometimes it still may use state of some +/// old block. If the state of that block is already dropped, relay will see errors when +/// e.g. it tries to prove something. +/// +/// By default Substrate-based nodes are storing state for last 256 blocks. We'll use +/// half of this value. +pub const ANCIENT_BLOCK_THRESHOLD: u32 = 128; + /// Opaque justifications subscription type. pub struct Subscription(pub(crate) Mutex>>); /// Opaque GRANDPA authorities set. pub type OpaqueGrandpaAuthoritiesSet = Vec; +/// A simple runtime version. It only includes the `spec_version` and `transaction_version`. +#[derive(Copy, Clone, Debug)] +pub struct SimpleRuntimeVersion { + /// Version of the runtime specification. + pub spec_version: u32, + /// All existing dispatches are fully compatible when this number doesn't change. + pub transaction_version: u32, +} + +impl SimpleRuntimeVersion { + /// Create a new instance of `SimpleRuntimeVersion` from a `RuntimeVersion`. + pub const fn from_runtime_version(runtime_version: &RuntimeVersion) -> Self { + Self { + spec_version: runtime_version.spec_version, + transaction_version: runtime_version.transaction_version, + } + } +} + /// Chain runtime version in client #[derive(Clone, Debug)] pub enum ChainRuntimeVersion { /// Auto query from chain. Auto, /// Custom runtime version, defined by user. - /// the first is `spec_version` - /// the second is `transaction_version` - Custom(u32, u32), + Custom(SimpleRuntimeVersion), } /// Substrate client type. @@ -201,16 +230,14 @@ impl Client { impl Client { /// Return simple runtime version, only include `spec_version` and `transaction_version`. - pub async fn simple_runtime_version(&self) -> Result<(u32, u32)> { - let (spec_version, transaction_version) = match self.chain_runtime_version { + pub async fn simple_runtime_version(&self) -> Result { + Ok(match &self.chain_runtime_version { ChainRuntimeVersion::Auto => { let runtime_version = self.runtime_version().await?; - (runtime_version.spec_version, runtime_version.transaction_version) + SimpleRuntimeVersion::from_runtime_version(&runtime_version) }, - ChainRuntimeVersion::Custom(spec_version, transaction_version) => - (spec_version, transaction_version), - }; - Ok((spec_version, transaction_version)) + ChainRuntimeVersion::Custom(version) => *version, + }) } /// Returns true if client is connected to at least one peer and is in synced state. @@ -242,7 +269,7 @@ impl Client { /// Return number of the best finalized block. pub async fn best_finalized_header_number(&self) -> Result { - Ok(*self.header_by_hash(self.best_finalized_header_hash().await?).await?.number()) + Ok(*self.best_finalized_header().await?.number()) } /// Return header of the best finalized block. @@ -414,6 +441,19 @@ impl Client { .await } + async fn build_sign_params(&self, signer: AccountKeyPairOf) -> Result> + where + C: ChainWithTransactions, + { + let runtime_version = self.simple_runtime_version().await?; + Ok(SignParam:: { + spec_version: runtime_version.spec_version, + transaction_version: runtime_version.transaction_version, + genesis_hash: self.genesis_hash, + signer, + }) + } + /// Submit an extrinsic signed by given account. /// /// All calls of this method are synchronized, so there can't be more than one active @@ -423,18 +463,19 @@ impl Client { /// Note: The given transaction needs to be SCALE encoded beforehand. pub async fn submit_signed_extrinsic( &self, - extrinsic_signer: C::AccountId, - signing_data: SignParam, + signer: &AccountKeyPairOf, prepare_extrinsic: impl FnOnce(HeaderIdOf, C::Index) -> Result> + Send + 'static, ) -> Result where C: ChainWithTransactions, + C::AccountId: From<::Public>, { let _guard = self.submit_signed_extrinsic_lock.lock().await; - let transaction_nonce = self.next_account_index(extrinsic_signer).await?; + let transaction_nonce = self.next_account_index(signer.public().into()).await?; let best_header = self.best_header().await?; + let signing_data = self.build_sign_params(signer.clone()).await?; // By using parent of best block here, we are protecing again best-block reorganizations. // E.g. transaction may have been submitted when the best block was `A[num=100]`. Then it @@ -463,18 +504,19 @@ impl Client { /// after submission. pub async fn submit_and_watch_signed_extrinsic( &self, - extrinsic_signer: C::AccountId, - signing_data: SignParam, + signer: &AccountKeyPairOf, prepare_extrinsic: impl FnOnce(HeaderIdOf, C::Index) -> Result> + Send + 'static, ) -> Result> where C: ChainWithTransactions, + C::AccountId: From<::Public>, { let self_clone = self.clone(); + let signing_data = self.build_sign_params(signer.clone()).await?; let _guard = self.submit_signed_extrinsic_lock.lock().await; - let transaction_nonce = self.next_account_index(extrinsic_signer).await?; + let transaction_nonce = self.next_account_index(signer.public().into()).await?; let best_header = self.best_header().await?; let best_header_id = best_header.id(); let (sender, receiver) = futures::channel::mpsc::channel(MAX_SUBSCRIPTION_CAPACITY); diff --git a/relays/client-substrate/src/error.rs b/relays/client-substrate/src/error.rs index 9323b757221..ddea1819fb0 100644 --- a/relays/client-substrate/src/error.rs +++ b/relays/client-substrate/src/error.rs @@ -16,6 +16,7 @@ //! Substrate node RPC errors. +use bp_polkadot_core::parachains::ParaId; use jsonrpsee::core::Error as RpcError; use relay_utils::MaybeConnectionError; use sc_rpc_api::system::Health; @@ -45,6 +46,12 @@ pub enum Error { /// Runtime storage is missing some mandatory value. #[error("Mandatory storage value is missing from the runtime storage.")] MissingMandatoryStorageValue, + /// Required parachain head is not present at the relay chain. + #[error("Parachain {0:?} head {1} is missing from the relay chain storage.")] + MissingRequiredParachainHead(ParaId, u64), + /// Failed to find finality proof for the given header. + #[error("Failed to find finality proof for header {0}.")] + FinalityProofNotFound(u64), /// The client we're connected to is not synced, so we can't rely on its state. #[error("Substrate client is not synced {0}.")] ClientNotSynced(Health), diff --git a/relays/client-substrate/src/lib.rs b/relays/client-substrate/src/lib.rs index 99ff0fbe394..ef75e304246 100644 --- a/relays/client-substrate/src/lib.rs +++ b/relays/client-substrate/src/lib.rs @@ -25,6 +25,7 @@ mod rpc; mod sync_header; mod transaction_tracker; +pub mod calls; pub mod guard; pub mod metrics; pub mod test_chain; @@ -34,23 +35,25 @@ use std::time::Duration; pub use crate::{ chain::{ AccountKeyPairOf, BlockWithJustification, CallOf, Chain, ChainWithBalances, - ChainWithGrandpa, ChainWithMessages, ChainWithTransactions, RelayChain, SignParam, - TransactionStatusOf, UnsignedTransaction, WeightToFeeOf, + ChainWithGrandpa, ChainWithMessages, ChainWithTransactions, ChainWithUtilityPallet, + FullRuntimeUtilityPallet, Parachain, RelayChain, SignParam, TransactionStatusOf, + UnsignedTransaction, UtilityPallet, + }, + client::{ + ChainRuntimeVersion, Client, OpaqueGrandpaAuthoritiesSet, SimpleRuntimeVersion, + Subscription, ANCIENT_BLOCK_THRESHOLD, }, - client::{ChainRuntimeVersion, Client, OpaqueGrandpaAuthoritiesSet, Subscription}, error::{Error, Result}, rpc::{SubstrateBeefyFinalityClient, SubstrateFinalityClient, SubstrateGrandpaFinalityClient}, sync_header::SyncHeader, transaction_tracker::TransactionTracker, }; pub use bp_runtime::{ - AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain as ChainBase, HashOf, HeaderOf, - IndexOf, SignatureOf, TransactionEra, TransactionEraOf, + AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain as ChainBase, HashOf, HeaderIdOf, + HeaderOf, IndexOf, Parachain as ParachainBase, SignatureOf, TransactionEra, TransactionEraOf, + UnderlyingChainProvider, }; -/// Header id used by the chain. -pub type HeaderIdOf = relay_utils::HeaderId, BlockNumberOf>; - /// Substrate-over-websocket connection params. #[derive(Debug, Clone)] pub struct ConnectionParams { diff --git a/relays/client-westend/Cargo.toml b/relays/client-westend/Cargo.toml index 57d2ca3b1e7..c60305868ad 100644 --- a/relays/client-westend/Cargo.toml +++ b/relays/client-westend/Cargo.toml @@ -15,5 +15,4 @@ bp-westend = { path = "../../primitives/chain-westend" } # Substrate Dependencies -frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-westend/src/lib.rs b/relays/client-westend/src/lib.rs index 9132a38710a..6467f431335 100644 --- a/relays/client-westend/src/lib.rs +++ b/relays/client-westend/src/lib.rs @@ -16,8 +16,9 @@ //! Types used to connect to the Westend chain. -use frame_support::weights::Weight; -use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, RelayChain}; +use relay_substrate_client::{ + Chain, ChainWithBalances, ChainWithGrandpa, RelayChain, UnderlyingChainProvider, +}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -31,24 +32,8 @@ pub type SyncHeader = relay_substrate_client::SyncHeader; #[derive(Debug, Clone, Copy)] pub struct Westend; -impl ChainBase for Westend { - type BlockNumber = bp_westend::BlockNumber; - type Hash = bp_westend::Hash; - type Hasher = bp_westend::Hasher; - type Header = bp_westend::Header; - - type AccountId = bp_westend::AccountId; - type Balance = bp_westend::Balance; - type Index = bp_westend::Nonce; - type Signature = bp_westend::Signature; - - fn max_extrinsic_size() -> u32 { - bp_westend::Westend::max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - bp_westend::Westend::max_extrinsic_weight() - } +impl UnderlyingChainProvider for Westend { + type Chain = bp_westend::Westend; } impl Chain for Westend { @@ -83,26 +68,8 @@ impl ChainWithBalances for Westend { #[derive(Debug, Clone, Copy)] pub struct Westmint; -// Westmint seems to use the same configuration as all Polkadot-like chains, so we'll use Westend -// primitives here. -impl ChainBase for Westmint { - type BlockNumber = bp_westend::BlockNumber; - type Hash = bp_westend::Hash; - type Hasher = bp_westend::Hasher; - type Header = bp_westend::Header; - - type AccountId = bp_westend::AccountId; - type Balance = bp_westend::Balance; - type Index = bp_westend::Nonce; - type Signature = bp_westend::Signature; - - fn max_extrinsic_size() -> u32 { - bp_westend::Westend::max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - bp_westend::Westend::max_extrinsic_weight() - } +impl UnderlyingChainProvider for Westmint { + type Chain = bp_westend::Westmint; } // Westmint seems to use the same configuration as all Polkadot-like chains, so we'll use Westend diff --git a/relays/client-wococo/Cargo.toml b/relays/client-wococo/Cargo.toml index 5b97694af1c..19483ab22fd 100644 --- a/relays/client-wococo/Cargo.toml +++ b/relays/client-wococo/Cargo.toml @@ -14,5 +14,4 @@ relay-utils = { path = "../utils" } bp-wococo = { path = "../../primitives/chain-wococo" } # Substrate Dependencies -frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-wococo/src/lib.rs b/relays/client-wococo/src/lib.rs index 6fc3714f667..a3e10729ba0 100644 --- a/relays/client-wococo/src/lib.rs +++ b/relays/client-wococo/src/lib.rs @@ -16,8 +16,9 @@ //! Types used to connect to the Wococo-Substrate chain. -use frame_support::weights::Weight; -use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, RelayChain}; +use relay_substrate_client::{ + Chain, ChainWithBalances, ChainWithGrandpa, RelayChain, UnderlyingChainProvider, +}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -31,24 +32,8 @@ pub type SyncHeader = relay_substrate_client::SyncHeader; #[derive(Debug, Clone, Copy)] pub struct Wococo; -impl ChainBase for Wococo { - type BlockNumber = bp_wococo::BlockNumber; - type Hash = bp_wococo::Hash; - type Hasher = bp_wococo::Hashing; - type Header = bp_wococo::Header; - - type AccountId = bp_wococo::AccountId; - type Balance = bp_wococo::Balance; - type Index = bp_wococo::Nonce; - type Signature = bp_wococo::Signature; - - fn max_extrinsic_size() -> u32 { - bp_wococo::Wococo::max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - bp_wococo::Wococo::max_extrinsic_weight() - } +impl UnderlyingChainProvider for Wococo { + type Chain = bp_wococo::Wococo; } impl Chain for Wococo { @@ -74,5 +59,5 @@ impl ChainWithBalances for Wococo { impl RelayChain for Wococo { const PARAS_PALLET_NAME: &'static str = bp_wococo::PARAS_PALLET_NAME; - const PARACHAINS_FINALITY_PALLET_NAME: &'static str = "bridgeWococoParachain"; + const PARACHAINS_FINALITY_PALLET_NAME: &'static str = "BridgeWococoParachain"; } diff --git a/relays/finality/src/sync_loop_metrics.rs b/relays/finality/src/sync_loop_metrics.rs index ae73bbedc4f..4da1df811f6 100644 --- a/relays/finality/src/sync_loop_metrics.rs +++ b/relays/finality/src/sync_loop_metrics.rs @@ -16,7 +16,10 @@ //! Metrics for headers synchronization relay loop. -use relay_utils::metrics::{metric_name, register, IntGauge, Metric, PrometheusError, Registry}; +use relay_utils::{ + metrics::{metric_name, register, IntGauge, Metric, PrometheusError, Registry}, + UniqueSaturatedInto, +}; /// Headers sync metrics. #[derive(Clone)] @@ -61,13 +64,19 @@ impl SyncLoopMetrics { } /// Update best block number at source. - pub fn update_best_block_at_source>(&self, source_best_number: Number) { - self.best_source_block_number.set(source_best_number.into()); + pub fn update_best_block_at_source>( + &self, + source_best_number: Number, + ) { + self.best_source_block_number.set(source_best_number.unique_saturated_into()); } /// Update best block number at target. - pub fn update_best_block_at_target>(&self, target_best_number: Number) { - self.best_target_block_number.set(target_best_number.into()); + pub fn update_best_block_at_target>( + &self, + target_best_number: Number, + ) { + self.best_target_block_number.set(target_best_number.unique_saturated_into()); } /// Update using-same-fork flag. diff --git a/relays/lib-substrate-relay/src/finality/guards.rs b/relays/lib-substrate-relay/src/finality/guards.rs index 1451b549cc0..188a03733a3 100644 --- a/relays/lib-substrate-relay/src/finality/guards.rs +++ b/relays/lib-substrate-relay/src/finality/guards.rs @@ -36,7 +36,7 @@ where if enable_version_guard { relay_substrate_client::guard::abort_on_spec_version_change( target_client.clone(), - target_client.simple_runtime_version().await?.0, + target_client.simple_runtime_version().await?.spec_version, ); } relay_substrate_client::guard::abort_when_account_balance_decreased( diff --git a/relays/lib-substrate-relay/src/finality/initialize.rs b/relays/lib-substrate-relay/src/finality/initialize.rs index 938f1330b2e..87052cf7aa4 100644 --- a/relays/lib-substrate-relay/src/finality/initialize.rs +++ b/relays/lib-substrate-relay/src/finality/initialize.rs @@ -22,10 +22,14 @@ //! with this header. use crate::{error::Error, finality::engine::Engine}; +use sp_core::Pair; +use bp_runtime::HeaderIdOf; use relay_substrate_client::{ - Chain, ChainWithTransactions, Client, Error as SubstrateError, SignParam, UnsignedTransaction, + AccountKeyPairOf, Chain, ChainWithTransactions, Client, Error as SubstrateError, + UnsignedTransaction, }; +use relay_utils::{TrackedTransactionStatus, TransactionTracker}; use sp_runtime::traits::Header as HeaderT; /// Submit headers-bridge initialization transaction. @@ -37,8 +41,7 @@ pub async fn initialize< >( source_client: Client, target_client: Client, - target_transactions_signer: TargetChain::AccountId, - target_signing_data: SignParam, + target_signer: AccountKeyPairOf, prepare_initialize_transaction: F, dry_run: bool, ) where @@ -48,25 +51,38 @@ pub async fn initialize< ) -> Result, SubstrateError> + Send + 'static, + TargetChain::AccountId: From<::Public>, { let result = do_initialize::( source_client, target_client, - target_transactions_signer, - target_signing_data, + target_signer, prepare_initialize_transaction, dry_run, ) .await; match result { - Ok(Some(tx_hash)) => log::info!( - target: "bridge", - "Successfully submitted {}-headers bridge initialization transaction to {}: {:?}", - SourceChain::NAME, - TargetChain::NAME, - tx_hash, - ), + Ok(Some(tx_status)) => match tx_status { + TrackedTransactionStatus::Lost => { + log::error!( + target: "bridge", + "Failed to execute {}-headers bridge initialization transaction on {}: {:?}.", + SourceChain::NAME, + TargetChain::NAME, + tx_status + ) + }, + TrackedTransactionStatus::Finalized(_) => { + log::info!( + target: "bridge", + "Successfully executed {}-headers bridge initialization transaction on {}: {:?}.", + SourceChain::NAME, + TargetChain::NAME, + tx_status + ) + }, + }, Ok(None) => (), Err(err) => log::error!( target: "bridge", @@ -87,12 +103,11 @@ async fn do_initialize< >( source_client: Client, target_client: Client, - target_transactions_signer: TargetChain::AccountId, - target_signing_data: SignParam, + target_signer: AccountKeyPairOf, prepare_initialize_transaction: F, dry_run: bool, ) -> Result< - Option, + Option>>, Error::Number>, > where @@ -102,6 +117,7 @@ where ) -> Result, SubstrateError> + Send + 'static, + TargetChain::AccountId: From<::Public>, { let is_initialized = E::is_initialized(&target_client) .await @@ -127,23 +143,21 @@ where initialization_data, ); - let initialization_tx_hash = target_client - .submit_signed_extrinsic( - target_transactions_signer, - target_signing_data, - move |_, transaction_nonce| { - let tx = prepare_initialize_transaction(transaction_nonce, initialization_data); - if dry_run { - Err(SubstrateError::Custom( - "Not submitting extrinsic in `dry-run` mode!".to_string(), - )) - } else { - tx - } - }, - ) + let tx_status = target_client + .submit_and_watch_signed_extrinsic(&target_signer, move |_, transaction_nonce| { + let tx = prepare_initialize_transaction(transaction_nonce, initialization_data); + if dry_run { + Err(SubstrateError::Custom( + "Not submitting extrinsic in `dry-run` mode!".to_string(), + )) + } else { + tx + } + }) .await - .map_err(|err| Error::SubmitTransaction(TargetChain::NAME, err))?; + .map_err(|err| Error::SubmitTransaction(TargetChain::NAME, err))? + .wait() + .await; - Ok(Some(initialization_tx_hash)) + Ok(Some(tx_status)) } diff --git a/relays/lib-substrate-relay/src/finality/source.rs b/relays/lib-substrate-relay/src/finality/source.rs index e75862a8227..c8f11d99461 100644 --- a/relays/lib-substrate-relay/src/finality/source.rs +++ b/relays/lib-substrate-relay/src/finality/source.rs @@ -20,13 +20,18 @@ use crate::finality::{engine::Engine, FinalitySyncPipelineAdapter, SubstrateFina use async_std::sync::{Arc, Mutex}; use async_trait::async_trait; +use bp_header_chain::FinalityProof; use codec::Decode; use finality_relay::SourceClient; -use futures::stream::{unfold, Stream, StreamExt}; +use futures::{ + select, + stream::{try_unfold, unfold, Stream, StreamExt, TryStreamExt}, +}; +use num_traits::One; use relay_substrate_client::{ BlockNumberOf, BlockWithJustification, Chain, Client, Error, HeaderOf, }; -use relay_utils::relay_loop::Client as RelayClient; +use relay_utils::{relay_loop::Client as RelayClient, UniqueSaturatedInto}; use std::pin::Pin; /// Shared updatable reference to the maximal header number that we want to sync from the source. @@ -70,6 +75,111 @@ impl SubstrateFinalitySource

{ // target node may be missing proofs that are already available at the source self.client.best_finalized_header_number().await } + + /// Return header and its justification of the given block or its descendant that + /// has a GRANDPA justification. + /// + /// This method is optimized for cases when `block_number` is close to the best finalized + /// chain block. + pub async fn prove_block_finality( + &self, + block_number: BlockNumberOf, + ) -> Result< + (relay_substrate_client::SyncHeader>, SubstrateFinalityProof

), + Error, + > { + // first, subscribe to proofs + let next_persistent_proof = + self.persistent_proofs_stream(block_number + One::one()).await?.fuse(); + let next_ephemeral_proof = self.ephemeral_proofs_stream(block_number).await?.fuse(); + + // in perfect world we'll need to return justfication for the requested `block_number` + let (header, maybe_proof) = self.header_and_finality_proof(block_number).await?; + if let Some(proof) = maybe_proof { + return Ok((header, proof)) + } + + // otherwise we don't care which header to return, so let's select first + futures::pin_mut!(next_persistent_proof, next_ephemeral_proof); + loop { + select! { + maybe_header_and_proof = next_persistent_proof.next() => match maybe_header_and_proof { + Some(header_and_proof) => return header_and_proof, + None => continue, + }, + maybe_header_and_proof = next_ephemeral_proof.next() => match maybe_header_and_proof { + Some(header_and_proof) => return header_and_proof, + None => continue, + }, + complete => return Err(Error::FinalityProofNotFound(block_number.unique_saturated_into())) + } + } + } + + /// Returns stream of headers and their persistent proofs, starting from given block. + async fn persistent_proofs_stream( + &self, + block_number: BlockNumberOf, + ) -> Result< + impl Stream< + Item = Result< + ( + relay_substrate_client::SyncHeader>, + SubstrateFinalityProof

, + ), + Error, + >, + >, + Error, + > { + let client = self.client.clone(); + let best_finalized_block_number = self.client.best_finalized_header_number().await?; + Ok(try_unfold((client, block_number), move |(client, current_block_number)| async move { + // if we've passed the `best_finalized_block_number`, we no longer need persistent + // justifications + if current_block_number > best_finalized_block_number { + return Ok(None) + } + + let (header, maybe_proof) = + header_and_finality_proof::

(&client, current_block_number).await?; + let next_block_number = current_block_number + One::one(); + let next_state = (client, next_block_number); + + Ok(Some((maybe_proof.map(|proof| (header, proof)), next_state))) + }) + .try_filter_map(|maybe_result| async { Ok(maybe_result) })) + } + + /// Returns stream of headers and their ephemeral proofs, starting from given block. + async fn ephemeral_proofs_stream( + &self, + block_number: BlockNumberOf, + ) -> Result< + impl Stream< + Item = Result< + ( + relay_substrate_client::SyncHeader>, + SubstrateFinalityProof

, + ), + Error, + >, + >, + Error, + > { + let client = self.client.clone(); + Ok(self.finality_proofs().await?.map(Ok).try_filter_map(move |proof| { + let client = client.clone(); + async move { + if proof.target_header_number() < block_number { + return Ok(None) + } + + let header = client.header_by_number(proof.target_header_number()).await?; + Ok(Some((header.into(), proof))) + } + })) + } } impl Clone for SubstrateFinalitySource

{ @@ -119,18 +229,7 @@ impl SourceClient { - let header_hash = self.client.block_hash_by_number(number).await?; - let signed_block = self.client.get_block(Some(header_hash)).await?; - - let justification = signed_block - .justification(P::FinalityEngine::ID) - .map(|raw_justification| { - SubstrateFinalityProof::

::decode(&mut raw_justification.as_slice()) - }) - .transpose() - .map_err(Error::ResponseParseFailed)?; - - Ok((signed_block.header().into(), justification)) + header_and_finality_proof::

(&self.client, number).await } async fn finality_proofs(&self) -> Result { @@ -173,3 +272,27 @@ impl SourceClient( + client: &Client, + number: BlockNumberOf, +) -> Result< + ( + relay_substrate_client::SyncHeader>, + Option>, + ), + Error, +> { + let header_hash = client.block_hash_by_number(number).await?; + let signed_block = client.get_block(Some(header_hash)).await?; + + let justification = signed_block + .justification(P::FinalityEngine::ID) + .map(|raw_justification| { + SubstrateFinalityProof::

::decode(&mut raw_justification.as_slice()) + }) + .transpose() + .map_err(Error::ResponseParseFailed)?; + + Ok((signed_block.header().into(), justification)) +} diff --git a/relays/lib-substrate-relay/src/finality/target.rs b/relays/lib-substrate-relay/src/finality/target.rs index 09d9ad15f54..951123ae07c 100644 --- a/relays/lib-substrate-relay/src/finality/target.rs +++ b/relays/lib-substrate-relay/src/finality/target.rs @@ -27,8 +27,8 @@ use crate::{ use async_trait::async_trait; use finality_relay::TargetClient; use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, Chain, Client, Error, HeaderIdOf, HeaderOf, SignParam, - SyncHeader, TransactionEra, TransactionTracker, UnsignedTransaction, + AccountIdOf, AccountKeyPairOf, Client, Error, HeaderIdOf, HeaderOf, SyncHeader, TransactionEra, + TransactionTracker, UnsignedTransaction, }; use relay_utils::relay_loop::Client as RelayClient; use sp_core::Pair; @@ -100,7 +100,6 @@ where Ok(crate::messages_source::read_client_state::( &self.client, None, - P::SourceChain::BEST_FINALIZED_HEADER_ID_METHOD, ) .await? .best_finalized_peer_at_best_self) @@ -111,20 +110,12 @@ where header: SyncHeader>, proof: SubstrateFinalityProof

, ) -> Result { - let genesis_hash = *self.client.genesis_hash(); let transaction_params = self.transaction_params.clone(); let call = P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof); - let (spec_version, transaction_version) = self.client.simple_runtime_version().await?; self.client .submit_and_watch_signed_extrinsic( - self.transaction_params.signer.public().into(), - SignParam:: { - spec_version, - transaction_version, - genesis_hash, - signer: transaction_params.signer.clone(), - }, + &self.transaction_params.signer, move |best_block_id, transaction_nonce| { Ok(UnsignedTransaction::new(call.into(), transaction_nonce) .era(TransactionEra::new(best_block_id, transaction_params.mortality))) diff --git a/relays/lib-substrate-relay/src/lib.rs b/relays/lib-substrate-relay/src/lib.rs index 62ae756e003..28c538b309a 100644 --- a/relays/lib-substrate-relay/src/lib.rs +++ b/relays/lib-substrate-relay/src/lib.rs @@ -18,6 +18,10 @@ #![warn(missing_docs)] +use relay_substrate_client::{Chain, ChainWithUtilityPallet, UtilityPallet}; + +use std::marker::PhantomData; + pub mod error; pub mod finality; pub mod messages_lane; @@ -60,13 +64,6 @@ pub enum TaggedAccount { /// Name of the bridged chain, which sends us messages or delivery confirmations. bridged_chain: String, }, - /// Account, used to sign messages with-bridged-chain pallet parameters update transactions. - MessagesPalletOwner { - /// Account id. - id: AccountId, - /// Name of the chain, bridged using messages pallet at our chain. - bridged_chain: String, - }, } impl TaggedAccount { @@ -76,7 +73,6 @@ impl TaggedAccount { TaggedAccount::Headers { ref id, .. } => id, TaggedAccount::Parachains { ref id, .. } => id, TaggedAccount::Messages { ref id, .. } => id, - TaggedAccount::MessagesPalletOwner { ref id, .. } => id, } } @@ -90,9 +86,46 @@ impl TaggedAccount { TaggedAccount::Messages { ref bridged_chain, .. } => { format!("{bridged_chain}Messages") }, - TaggedAccount::MessagesPalletOwner { ref bridged_chain, .. } => { - format!("{bridged_chain}MessagesPalletOwner") - }, } } } + +/// Batch call builder. +pub trait BatchCallBuilder: Send { + /// Create batch call from given calls vector. + fn build_batch_call(&self, _calls: Vec) -> Call; +} + +/// Batch call builder constructor. +pub trait BatchCallBuilderConstructor { + /// Create a new instance of a batch call builder. + fn new_builder() -> Option>>; +} + +/// Batch call builder based on `pallet-utility`. +pub struct UtilityPalletBatchCallBuilder(PhantomData); + +impl BatchCallBuilder for UtilityPalletBatchCallBuilder +where + C: ChainWithUtilityPallet, +{ + fn build_batch_call(&self, calls: Vec) -> C::Call { + C::UtilityPallet::build_batch_call(calls) + } +} + +impl BatchCallBuilderConstructor for UtilityPalletBatchCallBuilder +where + C: ChainWithUtilityPallet, +{ + fn new_builder() -> Option>> { + Some(Box::new(Self(Default::default()))) + } +} + +/// A `BatchCallBuilderConstructor` that always returns `None`. +impl BatchCallBuilderConstructor for () { + fn new_builder() -> Option>> { + None + } +} diff --git a/relays/lib-substrate-relay/src/messages_lane.rs b/relays/lib-substrate-relay/src/messages_lane.rs index da138a3d125..73d441cfcd3 100644 --- a/relays/lib-substrate-relay/src/messages_lane.rs +++ b/relays/lib-substrate-relay/src/messages_lane.rs @@ -20,22 +20,22 @@ use crate::{ messages_source::{SubstrateMessagesProof, SubstrateMessagesSource}, messages_target::{SubstrateMessagesDeliveryProof, SubstrateMessagesTarget}, on_demand::OnDemandRelay, - TransactionParams, + BatchCallBuilder, BatchCallBuilderConstructor, TransactionParams, }; use async_std::sync::Arc; use bp_messages::{LaneId, MessageNonce}; -use bp_runtime::{AccountIdOf, Chain as _, WeightExtraOps}; +use bp_runtime::{AccountIdOf, Chain as _, HeaderIdOf, WeightExtraOps}; use bridge_runtime_common::messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; use codec::Encode; use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; -use messages_relay::message_lane::MessageLane; +use messages_relay::{message_lane::MessageLane, message_lane_loop::BatchTransaction}; use pallet_bridge_messages::{Call as BridgeMessagesCall, Config as BridgeMessagesConfig}; use relay_substrate_client::{ transaction_stall_timeout, AccountKeyPairOf, BalanceOf, BlockNumberOf, CallOf, Chain, - ChainWithMessages, ChainWithTransactions, Client, HashOf, + ChainWithMessages, ChainWithTransactions, Client, Error as SubstrateError, HashOf, }; use relay_utils::{ metrics::{GlobalMetrics, MetricsParams, StandaloneMetric}, @@ -55,11 +55,16 @@ pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync { type ReceiveMessagesProofCallBuilder: ReceiveMessagesProofCallBuilder; /// How receive messages delivery proof call is built? type ReceiveMessagesDeliveryProofCallBuilder: ReceiveMessagesDeliveryProofCallBuilder; + + /// How batch calls are built at the source chain? + type SourceBatchCallBuilder: BatchCallBuilderConstructor>; + /// How batch calls are built at the target chain? + type TargetBatchCallBuilder: BatchCallBuilderConstructor>; } /// Adapter that allows all `SubstrateMessageLane` to act as `MessageLane`. #[derive(Clone, Debug)] -pub(crate) struct MessageLaneAdapter { +pub struct MessageLaneAdapter { _phantom: PhantomData

, } @@ -90,16 +95,63 @@ pub struct MessagesRelayParams { pub target_transaction_params: TransactionParams>, /// Optional on-demand source to target headers relay. pub source_to_target_headers_relay: - Option>>>, + Option>>, /// Optional on-demand target to source headers relay. pub target_to_source_headers_relay: - Option>>>, + Option>>, /// Identifier of lane that needs to be served. pub lane_id: LaneId, /// Metrics parameters. pub metrics_params: MetricsParams, } +/// Batch transaction that brings headers + and messages delivery/receiving confirmations to the +/// source node. +pub struct BatchProofTransaction>> { + builder: Box>>, + proved_header: HeaderIdOf, + prove_calls: Vec>, + + /// Using `fn() -> B` in order to avoid implementing `Send` for `B`. + _phantom: PhantomData B>, +} + +impl>> + BatchProofTransaction +{ + /// Creates a new instance of `BatchProofTransaction`. + pub async fn new( + relay: Arc>, + block_num: BlockNumberOf, + ) -> Result, SubstrateError> { + if let Some(builder) = B::new_builder() { + let (proved_header, prove_calls) = relay.prove_header(block_num).await?; + return Ok(Some(Self { + builder, + proved_header, + prove_calls, + _phantom: Default::default(), + })) + } + + Ok(None) + } + + /// Return a batch call that includes the provided call. + pub fn append_call_and_build(mut self, call: CallOf) -> CallOf { + self.prove_calls.push(call); + self.builder.build_batch_call(self.prove_calls) + } +} + +impl>> + BatchTransaction> for BatchProofTransaction +{ + fn required_header_id(&self) -> HeaderIdOf { + self.proved_header + } +} + /// Run Substrate-to-Substrate messages sync loop. pub async fn run(params: MessagesRelayParams

) -> anyhow::Result<()> where @@ -278,7 +330,7 @@ macro_rules! generate_mocked_receive_message_proof_call_builder { <$pipeline as $crate::messages_lane::SubstrateMessageLane>::SourceChain >, messages_count: u32, - dispatch_weight: Weight, + dispatch_weight: bp_messages::Weight, _trace_call: bool, ) -> relay_substrate_client::CallOf< <$pipeline as $crate::messages_lane::SubstrateMessageLane>::TargetChain @@ -443,7 +495,7 @@ mod tests { // Any significant change in this values should attract additional attention. // // TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` - (1024, Weight::from_ref_time(216_609_134_667).set_proof_size(217)), + (1024, Weight::from_ref_time(216_600_684_000).set_proof_size(217)), ); } } diff --git a/relays/lib-substrate-relay/src/messages_source.rs b/relays/lib-substrate-relay/src/messages_source.rs index 57f84eb6bb1..e86f7abdd64 100644 --- a/relays/lib-substrate-relay/src/messages_source.rs +++ b/relays/lib-substrate-relay/src/messages_source.rs @@ -20,9 +20,9 @@ use crate::{ messages_lane::{ - MessageLaneAdapter, ReceiveMessagesDeliveryProofCallBuilder, SubstrateMessageLane, + BatchProofTransaction, MessageLaneAdapter, ReceiveMessagesDeliveryProofCallBuilder, + SubstrateMessageLane, }, - messages_target::SubstrateMessagesDeliveryProof, on_demand::OnDemandRelay, TransactionParams, }; @@ -36,7 +36,7 @@ use bp_messages::{ }; use bp_runtime::{BasicOperatingMode, HeaderIdProvider}; use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; -use codec::{Decode, Encode}; +use codec::Encode; use frame_support::weights::Weight; use messages_relay::{ message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, @@ -47,13 +47,12 @@ use messages_relay::{ }; use num_traits::Zero; use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages, Client, - Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam, TransactionEra, - TransactionTracker, UnsignedTransaction, + AccountIdOf, AccountKeyPairOf, BalanceOf, Chain, ChainWithMessages, Client, + Error as SubstrateError, HashOf, HeaderIdOf, TransactionEra, TransactionTracker, + UnsignedTransaction, }; -use relay_utils::{relay_loop::Client as RelayClient, HeaderId}; -use sp_core::{Bytes, Pair}; -use sp_runtime::{traits::Header as HeaderT, DeserializeOwned}; +use relay_utils::relay_loop::Client as RelayClient; +use sp_core::Pair; use std::ops::RangeInclusive; /// Intermediate message proof returned by the source Substrate node. Includes everything @@ -68,7 +67,7 @@ pub struct SubstrateMessagesSource { target_client: Client, lane_id: LaneId, transaction_params: TransactionParams>, - target_to_source_headers_relay: Option>>>, + target_to_source_headers_relay: Option>>, } impl SubstrateMessagesSource

{ @@ -79,7 +78,7 @@ impl SubstrateMessagesSource

{ lane_id: LaneId, transaction_params: TransactionParams>, target_to_source_headers_relay: Option< - Arc>>, + Arc>, >, ) -> Self { SubstrateMessagesSource { @@ -140,6 +139,8 @@ impl SourceClient> for SubstrateM where AccountIdOf: From< as Pair>::Public>, { + type BatchTransaction = + BatchProofTransaction; type TransactionTracker = TransactionTracker>; async fn state(&self) -> Result>, SubstrateError> { @@ -153,12 +154,7 @@ where // we can't relay confirmations if messages pallet at source chain is halted self.ensure_pallet_active().await?; - read_client_state( - &self.source_client, - Some(&self.target_client), - P::TargetChain::BEST_FINALIZED_HEADER_ID_METHOD, - ) - .await + read_client_state(&self.source_client, Some(&self.target_client)).await } async fn latest_generated_nonce( @@ -331,39 +327,47 @@ where async fn submit_messages_receiving_proof( &self, + maybe_batch_tx: Option, _generated_at_block: TargetHeaderIdOf>, proof: as MessageLane>::MessagesReceivingProof, ) -> Result { - let genesis_hash = *self.source_client.genesis_hash(); + let messages_proof_call = + P::ReceiveMessagesDeliveryProofCallBuilder::build_receive_messages_delivery_proof_call( + proof, + maybe_batch_tx.is_none(), + ); + let final_call = match maybe_batch_tx { + Some(batch_tx) => batch_tx.append_call_and_build(messages_proof_call), + None => messages_proof_call, + }; + let transaction_params = self.transaction_params.clone(); - let (spec_version, transaction_version) = - self.source_client.simple_runtime_version().await?; self.source_client .submit_and_watch_signed_extrinsic( - self.transaction_params.signer.public().into(), - SignParam:: { - spec_version, - transaction_version, - genesis_hash, - signer: self.transaction_params.signer.clone(), - }, + &self.transaction_params.signer, move |best_block_id, transaction_nonce| { - make_messages_delivery_proof_transaction::

( - &transaction_params, - best_block_id, - transaction_nonce, - proof, - true, - ) + Ok(UnsignedTransaction::new(final_call.into(), transaction_nonce) + .era(TransactionEra::new(best_block_id, transaction_params.mortality))) }, ) .await } - async fn require_target_header_on_source(&self, id: TargetHeaderIdOf>) { + async fn require_target_header_on_source( + &self, + id: TargetHeaderIdOf>, + ) -> Result, SubstrateError> { if let Some(ref target_to_source_headers_relay) = self.target_to_source_headers_relay { + if let Some(batch_tx) = + BatchProofTransaction::new(target_to_source_headers_relay.clone(), id.0).await? + { + return Ok(Some(batch_tx)) + } + target_to_source_headers_relay.require_more_headers(id.0).await; } + + Ok(None) } } @@ -387,22 +391,6 @@ where } } -/// Make messages delivery proof transaction from given proof. -fn make_messages_delivery_proof_transaction( - source_transaction_params: &TransactionParams>, - source_best_block_id: HeaderIdOf, - transaction_nonce: IndexOf, - proof: SubstrateMessagesDeliveryProof, - trace_call: bool, -) -> Result, SubstrateError> { - let call = - P::ReceiveMessagesDeliveryProofCallBuilder::build_receive_messages_delivery_proof_call( - proof, trace_call, - ); - Ok(UnsignedTransaction::new(call.into(), transaction_nonce) - .era(TransactionEra::new(source_best_block_id, source_transaction_params.mortality))) -} - /// Read best blocks from given client. /// /// This function assumes that the chain that is followed by the `self_client` has @@ -414,31 +402,21 @@ fn make_messages_delivery_proof_transaction( pub async fn read_client_state( self_client: &Client, peer_client: Option<&Client>, - best_finalized_header_id_method_name: &str, ) -> Result, HeaderIdOf>, SubstrateError> where SelfChain: Chain, - SelfChain::Header: DeserializeOwned, - SelfChain::Index: DeserializeOwned, PeerChain: Chain, { // let's read our state first: we need best finalized header hash on **this** chain - let self_best_finalized_header_hash = self_client.best_finalized_header_hash().await?; - let self_best_finalized_header = - self_client.header_by_hash(self_best_finalized_header_hash).await?; - let self_best_finalized_id = self_best_finalized_header.id(); - + let self_best_finalized_id = self_client.best_finalized_header().await?.id(); // now let's read our best header on **this** chain - let self_best_header = self_client.best_header().await?; - let self_best_hash = self_best_header.hash(); - let self_best_id = self_best_header.id(); + let self_best_id = self_client.best_header().await?.id(); // now let's read id of best finalized peer header at our best finalized block let peer_on_self_best_finalized_id = best_finalized_peer_header_at_self::( self_client, - self_best_hash, - best_finalized_header_id_method_name, + self_best_id.hash(), ) .await?; @@ -446,7 +424,7 @@ where let actual_peer_on_self_best_finalized_id = match peer_client { Some(peer_client) => { let actual_peer_on_self_best_finalized = - peer_client.header_by_number(peer_on_self_best_finalized_id.0).await?; + peer_client.header_by_number(peer_on_self_best_finalized_id.number()).await?; actual_peer_on_self_best_finalized.id() }, None => peer_on_self_best_finalized_id, @@ -466,27 +444,20 @@ where pub async fn best_finalized_peer_header_at_self( self_client: &Client, at_self_hash: HashOf, - best_finalized_header_id_method_name: &str, ) -> Result, SubstrateError> where SelfChain: Chain, PeerChain: Chain, { // now let's read id of best finalized peer header at our best finalized block - let encoded_best_finalized_peer_on_self = self_client - .state_call( - best_finalized_header_id_method_name.into(), - Bytes(Vec::new()), + self_client + .typed_state_call::<_, Option<_>>( + PeerChain::BEST_FINALIZED_HEADER_ID_METHOD.into(), + (), Some(at_self_hash), ) - .await?; - - Option::, BlockNumberOf>>::decode( - &mut &encoded_best_finalized_peer_on_self.0[..], - ) - .map_err(SubstrateError::ResponseParseFailed)? - .map(Ok) - .unwrap_or(Err(SubstrateError::BridgePalletIsNotInitialized)) + .await? + .ok_or(SubstrateError::BridgePalletIsNotInitialized) } fn validate_out_msgs_details( @@ -495,8 +466,7 @@ fn validate_out_msgs_details( ) -> Result<(), SubstrateError> { let make_missing_nonce_error = |expected_nonce| { Err(SubstrateError::Custom(format!( - "Missing nonce {} in message_details call result. Expected all nonces from {:?}", - expected_nonce, nonces, + "Missing nonce {expected_nonce} in message_details call result. Expected all nonces from {nonces:?}", ))) }; @@ -660,7 +630,8 @@ mod tests { msgs_to_refine.push((payload, out_msg_details)); } - let maybe_batches = split_msgs_to_refine::([0, 0, 0, 0], msgs_to_refine); + let maybe_batches = + split_msgs_to_refine::(LaneId([0, 0, 0, 0]), msgs_to_refine); match expected_batches { Ok(expected_batches) => { let batches = maybe_batches.unwrap(); diff --git a/relays/lib-substrate-relay/src/messages_target.rs b/relays/lib-substrate-relay/src/messages_target.rs index 22a50acf37e..75119a1de97 100644 --- a/relays/lib-substrate-relay/src/messages_target.rs +++ b/relays/lib-substrate-relay/src/messages_target.rs @@ -19,7 +19,10 @@ //! chain. use crate::{ - messages_lane::{MessageLaneAdapter, ReceiveMessagesProofCallBuilder, SubstrateMessageLane}, + messages_lane::{ + BatchProofTransaction, MessageLaneAdapter, ReceiveMessagesProofCallBuilder, + SubstrateMessageLane, + }, messages_source::{ensure_messages_pallet_active, read_client_state, SubstrateMessagesProof}, on_demand::OnDemandRelay, TransactionParams, @@ -37,9 +40,8 @@ use messages_relay::{ message_lane_loop::{NoncesSubmitArtifacts, TargetClient, TargetClientState}, }; use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages, Client, - Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam, TransactionEra, - TransactionTracker, UnsignedTransaction, + AccountIdOf, AccountKeyPairOf, BalanceOf, CallOf, ChainWithMessages, Client, + Error as SubstrateError, HashOf, TransactionEra, TransactionTracker, UnsignedTransaction, }; use relay_utils::relay_loop::Client as RelayClient; use sp_core::Pair; @@ -56,7 +58,7 @@ pub struct SubstrateMessagesTarget { lane_id: LaneId, relayer_id_at_source: AccountIdOf, transaction_params: TransactionParams>, - source_to_target_headers_relay: Option>>>, + source_to_target_headers_relay: Option>>, } impl SubstrateMessagesTarget

{ @@ -68,7 +70,7 @@ impl SubstrateMessagesTarget

{ relayer_id_at_source: AccountIdOf, transaction_params: TransactionParams>, source_to_target_headers_relay: Option< - Arc>>, + Arc>, >, ) -> Self { SubstrateMessagesTarget { @@ -132,6 +134,8 @@ where AccountIdOf: From< as Pair>::Public>, BalanceOf: TryFrom>, { + type BatchTransaction = + BatchProofTransaction; type TransactionTracker = TransactionTracker>; async fn state(&self) -> Result>, SubstrateError> { @@ -145,12 +149,7 @@ where // we can't relay messages if messages pallet at target chain is halted self.ensure_pallet_active().await?; - read_client_state( - &self.target_client, - Some(&self.source_client), - P::SourceChain::BEST_FINALIZED_HEADER_ID_METHOD, - ) - .await + read_client_state(&self.target_client, Some(&self.source_client)).await } async fn latest_received_nonce( @@ -231,68 +230,68 @@ where async fn submit_messages_proof( &self, + maybe_batch_tx: Option, _generated_at_header: SourceHeaderIdOf>, nonces: RangeInclusive, proof: as MessageLane>::MessagesProof, ) -> Result, SubstrateError> { - let genesis_hash = *self.target_client.genesis_hash(); + let messages_proof_call = make_messages_delivery_call::

( + self.relayer_id_at_source.clone(), + proof.1.nonces_start..=proof.1.nonces_end, + proof, + maybe_batch_tx.is_none(), + ); + let final_call = match maybe_batch_tx { + Some(batch_tx) => batch_tx.append_call_and_build(messages_proof_call), + None => messages_proof_call, + }; + let transaction_params = self.transaction_params.clone(); - let relayer_id_at_source = self.relayer_id_at_source.clone(); - let nonces_clone = nonces.clone(); - let (spec_version, transaction_version) = - self.target_client.simple_runtime_version().await?; let tx_tracker = self .target_client .submit_and_watch_signed_extrinsic( - self.transaction_params.signer.public().into(), - SignParam:: { - spec_version, - transaction_version, - genesis_hash, - signer: self.transaction_params.signer.clone(), - }, + &self.transaction_params.signer, move |best_block_id, transaction_nonce| { - make_messages_delivery_transaction::

( - &transaction_params, - best_block_id, - transaction_nonce, - relayer_id_at_source, - nonces_clone, - proof, - true, - ) + Ok(UnsignedTransaction::new(final_call.into(), transaction_nonce) + .era(TransactionEra::new(best_block_id, transaction_params.mortality))) }, ) .await?; Ok(NoncesSubmitArtifacts { nonces, tx_tracker }) } - async fn require_source_header_on_target(&self, id: SourceHeaderIdOf>) { + async fn require_source_header_on_target( + &self, + id: SourceHeaderIdOf>, + ) -> Result, SubstrateError> { if let Some(ref source_to_target_headers_relay) = self.source_to_target_headers_relay { + if let Some(batch_tx) = + BatchProofTransaction::new(source_to_target_headers_relay.clone(), id.0).await? + { + return Ok(Some(batch_tx)) + } + source_to_target_headers_relay.require_more_headers(id.0).await; } + + Ok(None) } } -/// Make messages delivery transaction from given proof. -fn make_messages_delivery_transaction( - target_transaction_params: &TransactionParams>, - target_best_block_id: HeaderIdOf, - transaction_nonce: IndexOf, +/// Make messages delivery call from given proof. +fn make_messages_delivery_call( relayer_id_at_source: AccountIdOf, nonces: RangeInclusive, proof: SubstrateMessagesProof, trace_call: bool, -) -> Result, SubstrateError> { +) -> CallOf { let messages_count = nonces.end() - nonces.start() + 1; let dispatch_weight = proof.0; - let call = P::ReceiveMessagesProofCallBuilder::build_receive_messages_proof_call( + P::ReceiveMessagesProofCallBuilder::build_receive_messages_proof_call( relayer_id_at_source, proof, messages_count as _, dispatch_weight, trace_call, - ); - Ok(UnsignedTransaction::new(call.into(), transaction_nonce) - .era(TransactionEra::new(target_best_block_id, target_transaction_params.mortality))) + ) } diff --git a/relays/lib-substrate-relay/src/on_demand/headers.rs b/relays/lib-substrate-relay/src/on_demand/headers.rs index c0603cda8cd..75b402aff87 100644 --- a/relays/lib-substrate-relay/src/on_demand/headers.rs +++ b/relays/lib-substrate-relay/src/on_demand/headers.rs @@ -16,15 +16,21 @@ //! On-demand Substrate -> Substrate header finality relay. +use crate::finality::SubmitFinalityProofCallBuilder; + use async_std::sync::{Arc, Mutex}; use async_trait::async_trait; use bp_header_chain::ConsensusLogReader; +use bp_runtime::HeaderIdProvider; use futures::{select, FutureExt}; use num_traits::{One, Zero}; use sp_runtime::traits::Header; use finality_relay::{FinalitySyncParams, TargetClient as FinalityTargetClient}; -use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, Client}; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, Error as SubstrateError, + HeaderIdOf, +}; use relay_utils::{ metrics::MetricsParams, relay_loop::Client as RelayClient, FailedClient, MaybeConnectionError, STALL_TIMEOUT, @@ -47,20 +53,26 @@ use crate::{ /// relay) needs it to continue its regular work. When enough headers are relayed, on-demand stops /// syncing headers. #[derive(Clone)] -pub struct OnDemandHeadersRelay { +pub struct OnDemandHeadersRelay { /// Relay task name. relay_task_name: String, /// Shared reference to maximal required finalized header number. - required_header_number: RequiredHeaderNumberRef, + required_header_number: RequiredHeaderNumberRef, + /// Client of the source chain. + source_client: Client, } -impl OnDemandHeadersRelay { +impl OnDemandHeadersRelay

{ /// Create new on-demand headers relay. - pub fn new>( + /// + /// If `metrics_params` is `Some(_)`, the metrics of the finality relay are registered. + /// Otherwise, all required metrics must be exposed outside of this method. + pub fn new( source_client: Client, target_client: Client, target_transaction_params: TransactionParams>, only_mandatory_headers: bool, + metrics_params: Option, ) -> Self where AccountIdOf: @@ -70,6 +82,7 @@ impl OnDemandHeadersRelay { let this = OnDemandHeadersRelay { relay_task_name: on_demand_headers_relay_name::(), required_header_number: required_header_number.clone(), + source_client: source_client.clone(), }; async_std::task::spawn(async move { background_task::

( @@ -78,6 +91,7 @@ impl OnDemandHeadersRelay { target_transaction_params, only_mandatory_headers, required_header_number, + metrics_params, ) .await; }); @@ -87,23 +101,49 @@ impl OnDemandHeadersRelay { } #[async_trait] -impl OnDemandRelay> - for OnDemandHeadersRelay +impl OnDemandRelay + for OnDemandHeadersRelay

{ - async fn require_more_headers(&self, required_header: BlockNumberOf) { + async fn require_more_headers(&self, required_header: BlockNumberOf) { let mut required_header_number = self.required_header_number.lock().await; if required_header > *required_header_number { log::trace!( target: "bridge", "[{}] More {} headers required. Going to sync up to the {}", self.relay_task_name, - SourceChain::NAME, + P::SourceChain::NAME, required_header, ); *required_header_number = required_header; } } + + async fn prove_header( + &self, + required_header: BlockNumberOf, + ) -> Result<(HeaderIdOf, Vec>), SubstrateError> { + // first find proper header (either `required_header`) or its descendant + let finality_source = SubstrateFinalitySource::

::new(self.source_client.clone(), None); + let (header, proof) = finality_source.prove_block_finality(required_header).await?; + let header_id = header.id(); + + log::debug!( + target: "bridge", + "[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?}", + self.relay_task_name, + P::SourceChain::NAME, + required_header, + P::SourceChain::NAME, + header_id, + ); + + // and then craft the submit-proof call + let call = + P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof); + + Ok((header_id, vec![call])) + } } /// Background task that is responsible for starting headers relay. @@ -113,6 +153,7 @@ async fn background_task( target_transaction_params: TransactionParams>, only_mandatory_headers: bool, required_header_number: RequiredHeaderNumberRef, + metrics_params: Option, ) where AccountIdOf: From< as sp_core::Pair>::Public>, { @@ -275,7 +316,7 @@ async fn background_task( stall_timeout, only_mandatory_headers, }, - MetricsParams::disabled(), + metrics_params.clone().unwrap_or_else(MetricsParams::disabled), futures::future::pending(), ) .fuse(), diff --git a/relays/lib-substrate-relay/src/on_demand/mod.rs b/relays/lib-substrate-relay/src/on_demand/mod.rs index 7a2dfc9c153..eca7d20163d 100644 --- a/relays/lib-substrate-relay/src/on_demand/mod.rs +++ b/relays/lib-substrate-relay/src/on_demand/mod.rs @@ -18,18 +18,28 @@ //! on-demand pipelines. use async_trait::async_trait; +use relay_substrate_client::{BlockNumberOf, CallOf, Chain, Error as SubstrateError, HeaderIdOf}; pub mod headers; pub mod parachains; /// On-demand headers relay that is relaying finalizing headers only when requested. #[async_trait] -pub trait OnDemandRelay: Send + Sync { +pub trait OnDemandRelay: Send + Sync { /// Ask relay to relay source header with given number to the target chain. /// /// Depending on implementation, on-demand relay may also relay `required_header` ancestors /// (e.g. if they're mandatory), or its descendants. The request is considered complete if /// the best avbailable header at the target chain has number that is larger than or equal /// to the `required_header`. - async fn require_more_headers(&self, required_header: SourceHeaderNumber); + async fn require_more_headers(&self, required_header: BlockNumberOf); + + /// Ask relay to prove source `required_header` to the `TargetChain`. + /// + /// Returns number of header that is proved (it may be the `required_header` or one of its + /// descendants) and calls for delivering the proof. + async fn prove_header( + &self, + required_header: BlockNumberOf, + ) -> Result<(HeaderIdOf, Vec>), SubstrateError>; } diff --git a/relays/lib-substrate-relay/src/on_demand/parachains.rs b/relays/lib-substrate-relay/src/on_demand/parachains.rs index 35ef8244ae6..6be3fcb9886 100644 --- a/relays/lib-substrate-relay/src/on_demand/parachains.rs +++ b/relays/lib-substrate-relay/src/on_demand/parachains.rs @@ -21,7 +21,7 @@ use crate::{ on_demand::OnDemandRelay, parachains::{ source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter, - SubstrateParachainsPipeline, + SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, }, TransactionParams, }; @@ -31,17 +31,21 @@ use async_std::{ sync::{Arc, Mutex}, }; use async_trait::async_trait; -use bp_polkadot_core::parachains::ParaHash; +use bp_polkadot_core::parachains::{ParaHash, ParaId}; use bp_runtime::HeaderIdProvider; use futures::{select, FutureExt}; use num_traits::Zero; use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; -use parachains_relay::parachains_loop::{AvailableHeader, ParachainSyncParams, TargetClient}; +use parachains_relay::parachains_loop::{ + AvailableHeader, ParachainSyncParams, SourceClient, TargetClient, +}; use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, + AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, Error as SubstrateError, + HashOf, HeaderIdOf, ParachainBase, ANCIENT_BLOCK_THRESHOLD, }; use relay_utils::{ - metrics::MetricsParams, relay_loop::Client as RelayClient, FailedClient, HeaderId, + metrics::MetricsParams, relay_loop::Client as RelayClient, BlockNumberBase, FailedClient, + HeaderId, UniqueSaturatedInto, }; use std::fmt::Debug; @@ -51,25 +55,32 @@ use std::fmt::Debug; /// (e.g. messages relay) needs it to continue its regular work. When enough parachain headers /// are relayed, on-demand stops syncing headers. #[derive(Clone)] -pub struct OnDemandParachainsRelay { +pub struct OnDemandParachainsRelay { /// Relay task name. relay_task_name: String, /// Channel used to communicate with background task and ask for relay of parachain heads. - required_header_number_sender: Sender>, + required_header_number_sender: Sender>, + /// Source relay chain client. + source_relay_client: Client, + /// Target chain client. + target_client: Client, + /// On-demand relay chain relay. + on_demand_source_relay_to_target_headers: + Arc>, } -impl OnDemandParachainsRelay { +impl OnDemandParachainsRelay

{ /// Create new on-demand parachains relay. /// /// Note that the argument is the source relay chain client, not the parachain client. /// That's because parachain finality is determined by the relay chain and we don't /// need to connect to the parachain itself here. - pub fn new>( + pub fn new( source_relay_client: Client, target_client: Client, target_transaction_params: TransactionParams>, on_demand_source_relay_to_target_headers: Arc< - dyn OnDemandRelay>, + dyn OnDemandRelay, >, ) -> Self where @@ -81,8 +92,13 @@ impl OnDemandParachainsRelay { { let (required_header_number_sender, required_header_number_receiver) = unbounded(); let this = OnDemandParachainsRelay { - relay_task_name: on_demand_parachains_relay_name::(), + relay_task_name: on_demand_parachains_relay_name::( + ), required_header_number_sender, + source_relay_client: source_relay_client.clone(), + target_client: target_client.clone(), + on_demand_source_relay_to_target_headers: on_demand_source_relay_to_target_headers + .clone(), }; async_std::task::spawn(async move { background_task::

( @@ -100,23 +116,109 @@ impl OnDemandParachainsRelay { } #[async_trait] -impl OnDemandRelay> - for OnDemandParachainsRelay +impl OnDemandRelay + for OnDemandParachainsRelay

where - SourceParachain: Chain, + P::SourceParachain: Chain, { - async fn require_more_headers(&self, required_header: BlockNumberOf) { + async fn require_more_headers(&self, required_header: BlockNumberOf) { if let Err(e) = self.required_header_number_sender.send(required_header).await { log::trace!( target: "bridge", "[{}] Failed to request {} header {:?}: {:?}", self.relay_task_name, - SourceParachain::NAME, + P::SourceParachain::NAME, required_header, e, ); } } + + /// Ask relay to prove source `required_header` to the `TargetChain`. + async fn prove_header( + &self, + required_parachain_header: BlockNumberOf, + ) -> Result<(HeaderIdOf, Vec>), SubstrateError> { + // select headers to prove + let parachains_source = ParachainsSource::

::new( + self.source_relay_client.clone(), + Arc::new(Mutex::new(AvailableHeader::Missing)), + ); + let env = (self, ¶chains_source); + let (need_to_prove_relay_block, selected_relay_block, selected_parachain_block) = + select_headers_to_prove(env, required_parachain_header).await?; + + log::debug!( + target: "bridge", + "[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?} and {} head {:?}", + self.relay_task_name, + P::SourceParachain::NAME, + required_parachain_header, + P::SourceParachain::NAME, + selected_parachain_block, + P::SourceRelayChain::NAME, + if need_to_prove_relay_block { + Some(selected_relay_block) + } else { + None + }, + ); + + // now let's prove relay chain block (if needed) + let mut calls = Vec::new(); + let mut proved_relay_block = selected_relay_block; + if need_to_prove_relay_block { + let (relay_block, relay_prove_call) = self + .on_demand_source_relay_to_target_headers + .prove_header(selected_relay_block.number()) + .await?; + proved_relay_block = relay_block; + calls.extend(relay_prove_call); + } + + // despite what we've selected before (in `select_headers_to_prove` call), if headers relay + // have chose the different header (e.g. because there's no GRANDPA jusstification for it), + // we need to prove parachain head available at this header + let para_id = ParaId(P::SourceParachain::PARACHAIN_ID); + let mut proved_parachain_block = selected_parachain_block; + if proved_relay_block != selected_relay_block { + proved_parachain_block = parachains_source + .on_chain_para_head_id(proved_relay_block, para_id) + .await? + // this could happen e.g. if parachain has been offboarded? + .ok_or_else(|| { + SubstrateError::MissingRequiredParachainHead( + para_id, + proved_relay_block.number().unique_saturated_into(), + ) + })?; + + log::debug!( + target: "bridge", + "[{}] Selected to prove {} head {:?} and {} head {:?}. Instead proved {} head {:?} and {} head {:?}", + self.relay_task_name, + P::SourceParachain::NAME, + selected_parachain_block, + P::SourceRelayChain::NAME, + selected_relay_block, + P::SourceParachain::NAME, + proved_parachain_block, + P::SourceRelayChain::NAME, + proved_relay_block, + ); + } + + // and finally - prove parachain head + let (para_proof, para_hashes) = + parachains_source.prove_parachain_heads(proved_relay_block, &[para_id]).await?; + calls.push(P::SubmitParachainHeadsCallBuilder::build_submit_parachain_heads_call( + proved_relay_block, + para_hashes.into_iter().map(|h| (para_id, h)).collect(), + para_proof, + )); + + Ok((proved_parachain_block, calls)) + } } /// Background task that is responsible for starting parachain headers relay. @@ -125,7 +227,7 @@ async fn background_task( target_client: Client, target_transaction_params: TransactionParams>, on_demand_source_relay_to_target_headers: Arc< - dyn OnDemandRelay>, + dyn OnDemandRelay, >, required_parachain_header_number_receiver: Receiver>, ) where @@ -278,7 +380,7 @@ async fn background_task( parachains_source.clone(), parachains_target.clone(), ParachainSyncParams { - parachains: vec![P::SOURCE_PARACHAIN_PARA_ID.into()], + parachains: vec![P::SourceParachain::PARACHAIN_ID.into()], stall_timeout: std::time::Duration::from_secs(60), strategy: parachains_relay::parachains_loop::ParachainSyncStrategy::Any, }, @@ -366,13 +468,11 @@ where }; let best_target_block_hash = target.best_block().await.map_err(map_target_err)?.1; - let para_header_at_target = - best_finalized_peer_header_at_self::( - target.client(), - best_target_block_hash, - P::SourceParachain::BEST_FINALIZED_HEADER_ID_METHOD, - ) - .await; + let para_header_at_target = best_finalized_peer_header_at_self::< + P::TargetChain, + P::SourceParachain, + >(target.client(), best_target_block_hash) + .await; // if there are no parachain heads at the target (`BridgePalletIsNotInitialized`), we'll need // to submit at least one. Otherwise the pallet will be treated as uninitialized and messages // sync will stall. @@ -386,22 +486,23 @@ where source.client().best_finalized_header().await.map_err(map_source_err)?; let best_finalized_relay_block_id = best_finalized_relay_header.id(); let para_header_at_source = source - .on_chain_para_head_id(best_finalized_relay_block_id, P::SOURCE_PARACHAIN_PARA_ID.into()) + .on_chain_para_head_id( + best_finalized_relay_block_id, + P::SourceParachain::PARACHAIN_ID.into(), + ) .await .map_err(map_source_err)?; let relay_header_at_source = best_finalized_relay_block_id.0; - let relay_header_at_target = - best_finalized_peer_header_at_self::( - target.client(), - best_target_block_hash, - P::SourceRelayChain::BEST_FINALIZED_HEADER_ID_METHOD, - ) - .await - .map_err(map_target_err)?; + let relay_header_at_target = best_finalized_peer_header_at_self::< + P::TargetChain, + P::SourceRelayChain, + >(target.client(), best_target_block_hash) + .await + .map_err(map_target_err)?; let para_header_at_relay_header_at_target = source - .on_chain_para_head_id(relay_header_at_target, P::SOURCE_PARACHAIN_PARA_ID.into()) + .on_chain_para_head_id(relay_header_at_target, P::SourceParachain::PARACHAIN_ID.into()) .await .map_err(map_source_err)?; @@ -483,6 +584,124 @@ where RelayState::RelayingParaHeader(para_header_at_source) } +/// Environment for the `select_headers_to_prove` call. +#[async_trait] +trait SelectHeadersToProveEnvironment { + /// Returns associated parachain id. + fn parachain_id(&self) -> ParaId; + /// Returns best finalized relay block. + async fn best_finalized_relay_block_at_source( + &self, + ) -> Result, SubstrateError>; + /// Returns best finalized relay block that is known at `P::TargetChain`. + async fn best_finalized_relay_block_at_target( + &self, + ) -> Result, SubstrateError>; + /// Returns best finalized parachain block at given source relay chain block. + async fn best_finalized_para_block_at_source( + &self, + at_relay_block: HeaderId, + ) -> Result>, SubstrateError>; +} + +#[async_trait] +impl<'a, P: SubstrateParachainsPipeline> + SelectHeadersToProveEnvironment< + BlockNumberOf, + HashOf, + BlockNumberOf, + HashOf, + > for (&'a OnDemandParachainsRelay

, &'a ParachainsSource

) +{ + fn parachain_id(&self) -> ParaId { + ParaId(P::SourceParachain::PARACHAIN_ID) + } + + async fn best_finalized_relay_block_at_source( + &self, + ) -> Result, SubstrateError> { + Ok(self.0.source_relay_client.best_finalized_header().await?.id()) + } + + async fn best_finalized_relay_block_at_target( + &self, + ) -> Result, SubstrateError> { + Ok(crate::messages_source::read_client_state::( + &self.0.target_client, + None, + ) + .await? + .best_finalized_peer_at_best_self) + } + + async fn best_finalized_para_block_at_source( + &self, + at_relay_block: HeaderIdOf, + ) -> Result>, SubstrateError> { + self.1.on_chain_para_head_id(at_relay_block, self.parachain_id()).await + } +} + +/// Given request to prove `required_parachain_header`, select actual headers that need to be +/// proved. +async fn select_headers_to_prove( + env: impl SelectHeadersToProveEnvironment, + required_parachain_header: PBN, +) -> Result<(bool, HeaderId, HeaderId), SubstrateError> +where + RBH: Copy, + RBN: BlockNumberBase, + PBH: Copy, + PBN: BlockNumberBase, +{ + // parachains proof also requires relay header proof. Let's first select relay block + // number that we'll be dealing with + let best_finalized_relay_block_at_source = env.best_finalized_relay_block_at_source().await?; + let best_finalized_relay_block_at_target = env.best_finalized_relay_block_at_target().await?; + + // if we can't prove `required_header` even using `best_finalized_relay_block_at_source`, we + // can't do anything here + // (this shall not actually happen, given current code, because we only require finalized + // headers) + let best_possible_parachain_block = env + .best_finalized_para_block_at_source(best_finalized_relay_block_at_source) + .await? + .filter(|best_possible_parachain_block| { + best_possible_parachain_block.number() >= required_parachain_header + }) + .ok_or(SubstrateError::MissingRequiredParachainHead( + env.parachain_id(), + required_parachain_header.unique_saturated_into(), + ))?; + + // now let's check if `required_header` may be proved using + // `best_finalized_relay_block_at_target` + let selection = env + .best_finalized_para_block_at_source(best_finalized_relay_block_at_target) + .await? + .filter(|best_finalized_para_block_at_target| { + best_finalized_para_block_at_target.number() >= required_parachain_header + }) + .map(|best_finalized_para_block_at_target| { + (false, best_finalized_relay_block_at_target, best_finalized_para_block_at_target) + }) + // we don't require source node to be archive, so we can't craft storage proofs using + // ancient headers. So if the `best_finalized_relay_block_at_target` is too ancient, we + // can't craft storage proofs using it + .filter(|(_, selected_relay_block, _)| { + let difference = best_finalized_relay_block_at_source + .number() + .saturating_sub(selected_relay_block.number()); + difference <= RBN::from(ANCIENT_BLOCK_THRESHOLD) + }); + + Ok(selection.unwrap_or(( + true, + best_finalized_relay_block_at_source, + best_possible_parachain_block, + ))) +} + #[cfg(test)] mod tests { use super::*; @@ -701,4 +920,80 @@ mod tests { RelayState::RelayingRelayHeader(800), ); } + + // tuple is: + // + // - best_finalized_relay_block_at_source + // - best_finalized_relay_block_at_target + // - best_finalized_para_block_at_source at best_finalized_relay_block_at_source + // - best_finalized_para_block_at_source at best_finalized_relay_block_at_target + #[async_trait] + impl SelectHeadersToProveEnvironment for (u32, u32, u32, u32) { + fn parachain_id(&self) -> ParaId { + ParaId(0) + } + + async fn best_finalized_relay_block_at_source( + &self, + ) -> Result, SubstrateError> { + Ok(HeaderId(self.0, self.0)) + } + + async fn best_finalized_relay_block_at_target( + &self, + ) -> Result, SubstrateError> { + Ok(HeaderId(self.1, self.1)) + } + + async fn best_finalized_para_block_at_source( + &self, + at_relay_block: HeaderId, + ) -> Result>, SubstrateError> { + if at_relay_block.0 == self.0 { + Ok(Some(HeaderId(self.2, self.2))) + } else if at_relay_block.0 == self.1 { + Ok(Some(HeaderId(self.3, self.3))) + } else { + Ok(None) + } + } + } + + #[async_std::test] + async fn select_headers_to_prove_returns_err_if_required_para_block_is_missing_at_source() { + assert!(matches!( + select_headers_to_prove((20_u32, 10_u32, 200_u32, 100_u32), 300_u32,).await, + Err(SubstrateError::MissingRequiredParachainHead(ParaId(0), 300_u64)), + )); + } + + #[async_std::test] + async fn select_headers_to_prove_fails_to_use_existing_ancient_relay_block() { + assert_eq!( + select_headers_to_prove((220_u32, 10_u32, 200_u32, 100_u32), 100_u32,) + .await + .map_err(drop), + Ok((true, HeaderId(220, 220), HeaderId(200, 200))), + ); + } + + #[async_std::test] + async fn select_headers_to_prove_is_able_to_use_existing_recent_relay_block() { + assert_eq!( + select_headers_to_prove((40_u32, 10_u32, 200_u32, 100_u32), 100_u32,) + .await + .map_err(drop), + Ok((false, HeaderId(10, 10), HeaderId(100, 100))), + ); + } + + #[async_std::test] + async fn select_headers_to_prove_uses_new_relay_block() { + assert_eq!( + select_headers_to_prove((20_u32, 10_u32, 200_u32, 100_u32), 200_u32,) + .await + .map_err(drop), + Ok((true, HeaderId(20, 20), HeaderId(200, 200))), + ); + } } diff --git a/relays/lib-substrate-relay/src/parachains/mod.rs b/relays/lib-substrate-relay/src/parachains/mod.rs index 1d744a30e4e..9852e512f7e 100644 --- a/relays/lib-substrate-relay/src/parachains/mod.rs +++ b/relays/lib-substrate-relay/src/parachains/mod.rs @@ -24,7 +24,9 @@ use pallet_bridge_parachains::{ RelayBlockHasher, RelayBlockNumber, }; use parachains_relay::ParachainsPipeline; -use relay_substrate_client::{CallOf, Chain, ChainWithTransactions, HeaderIdOf, RelayChain}; +use relay_substrate_client::{ + CallOf, Chain, ChainWithTransactions, HeaderIdOf, Parachain, RelayChain, +}; use std::{fmt::Debug, marker::PhantomData}; pub mod source; @@ -37,7 +39,7 @@ pub mod target; #[async_trait] pub trait SubstrateParachainsPipeline: 'static + Clone + Debug + Send + Sync { /// Headers of this parachain are submitted to the `Self::TargetChain`. - type SourceParachain: Chain; + type SourceParachain: Parachain; /// Relay chain that is storing headers of `Self::SourceParachain`. type SourceRelayChain: RelayChain; /// Target chain where `Self::SourceParachain` headers are submitted. @@ -45,9 +47,6 @@ pub trait SubstrateParachainsPipeline: 'static + Clone + Debug + Send + Sync { /// How submit parachains heads call is built? type SubmitParachainHeadsCallBuilder: SubmitParachainHeadsCallBuilder; - - /// Id of the `Self::SourceParachain`, used for registration in `Self::SourceRelayChain`. - const SOURCE_PARACHAIN_PARA_ID: u32; } /// Adapter that allows all `SubstrateParachainsPipeline` to act as `ParachainsPipeline`. diff --git a/relays/lib-substrate-relay/src/parachains/source.rs b/relays/lib-substrate-relay/src/parachains/source.rs index 2cae7f1a224..afacd18e292 100644 --- a/relays/lib-substrate-relay/src/parachains/source.rs +++ b/relays/lib-substrate-relay/src/parachains/source.rs @@ -29,7 +29,7 @@ use parachains_relay::{ parachains_loop_metrics::ParachainsLoopMetrics, }; use relay_substrate_client::{ - Chain, Client, Error as SubstrateError, HeaderIdOf, HeaderOf, RelayChain, + Chain, Client, Error as SubstrateError, HeaderIdOf, HeaderOf, ParachainBase, RelayChain, }; use relay_utils::relay_loop::Client as RelayClient; @@ -107,11 +107,11 @@ where para_id: ParaId, ) -> Result, Self::Error> { // we don't need to support many parachains now - if para_id.0 != P::SOURCE_PARACHAIN_PARA_ID { + if para_id.0 != P::SourceParachain::PARACHAIN_ID { return Err(SubstrateError::Custom(format!( "Parachain id {} is not matching expected {}", para_id.0, - P::SOURCE_PARACHAIN_PARA_ID, + P::SourceParachain::PARACHAIN_ID, ))) } @@ -144,11 +144,10 @@ where at_block: HeaderIdOf, parachains: &[ParaId], ) -> Result<(ParaHeadsProof, Vec), Self::Error> { - let parachain = ParaId(P::SOURCE_PARACHAIN_PARA_ID); + let parachain = ParaId(P::SourceParachain::PARACHAIN_ID); if parachains != [parachain] { return Err(SubstrateError::Custom(format!( - "Trying to prove unexpected parachains {:?}. Expected {:?}", - parachains, parachain, + "Trying to prove unexpected parachains {parachains:?}. Expected {parachain:?}", ))) } @@ -177,8 +176,7 @@ where .transpose()? .ok_or_else(|| { SubstrateError::Custom(format!( - "Failed to read expected parachain {:?} head at {:?}", - parachain, at_block + "Failed to read expected parachain {parachain:?} head at {at_block:?}" )) })?; let parachain_head_hash = parachain_head.hash(); diff --git a/relays/lib-substrate-relay/src/parachains/target.rs b/relays/lib-substrate-relay/src/parachains/target.rs index 68fd72765aa..5e765f10ff5 100644 --- a/relays/lib-substrate-relay/src/parachains/target.rs +++ b/relays/lib-substrate-relay/src/parachains/target.rs @@ -33,12 +33,10 @@ use parachains_relay::{ }; use relay_substrate_client::{ AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, - HeaderIdOf, HeaderOf, RelayChain, SignParam, TransactionEra, TransactionTracker, - UnsignedTransaction, + HeaderIdOf, RelayChain, TransactionEra, TransactionTracker, UnsignedTransaction, }; use relay_utils::{relay_loop::Client as RelayClient, HeaderId}; use sp_core::{Bytes, Pair}; -use sp_runtime::traits::Header as HeaderT; /// Substrate client as parachain heads source. pub struct ParachainsTarget { @@ -132,7 +130,7 @@ where .map(|para_info| para_info.best_head_hash); if let (Some(metrics), Some(best_para_head_hash)) = (metrics, &best_para_head_hash) { - let imported_para_head = self + let imported_para_head_number = self .client .storage_double_map_value::( P::SourceRelayChain::PARACHAINS_FINALITY_PALLET_NAME, @@ -142,10 +140,11 @@ where ) .await .and_then(|maybe_encoded_head| match maybe_encoded_head { - Some(encoded_head) => - HeaderOf::::decode(&mut &encoded_head.0[..]) - .map(Some) - .map_err(Self::Error::ResponseParseFailed), + Some(encoded_head) => encoded_head + .decode_parachain_head_data::() + .map(|head| head.number) + .map(Some) + .map_err(Self::Error::ResponseParseFailed), None => Ok(None), }) .map_err(|e| { @@ -159,9 +158,8 @@ where e }) .unwrap_or(None); - if let Some(imported_para_head) = imported_para_head { - metrics - .update_best_parachain_block_at_target(para_id, *imported_para_head.number()); + if let Some(imported_para_head_number) = imported_para_head_number { + metrics.update_best_parachain_block_at_target(para_id, imported_para_head_number); } } @@ -174,9 +172,7 @@ where updated_parachains: Vec<(ParaId, ParaHash)>, proof: ParaHeadsProof, ) -> Result { - let genesis_hash = *self.client.genesis_hash(); let transaction_params = self.transaction_params.clone(); - let (spec_version, transaction_version) = self.client.simple_runtime_version().await?; let call = P::SubmitParachainHeadsCallBuilder::build_submit_parachain_heads_call( at_relay_block, updated_parachains, @@ -184,13 +180,7 @@ where ); self.client .submit_and_watch_signed_extrinsic( - self.transaction_params.signer.public().into(), - SignParam:: { - spec_version, - transaction_version, - genesis_hash, - signer: transaction_params.signer, - }, + &transaction_params.signer, move |best_block_id, transaction_nonce| { Ok(UnsignedTransaction::new(call.into(), transaction_nonce) .era(TransactionEra::new(best_block_id, transaction_params.mortality))) diff --git a/relays/messages/Cargo.toml b/relays/messages/Cargo.toml index 02e453b1c32..e144926376c 100644 --- a/relays/messages/Cargo.toml +++ b/relays/messages/Cargo.toml @@ -17,7 +17,6 @@ parking_lot = "0.11.0" # Bridge Dependencies bp-messages = { path = "../../primitives/messages" } -bp-runtime = { path = "../../primitives/runtime" } finality-relay = { path = "../finality" } relay-utils = { path = "../utils" } diff --git a/relays/messages/src/message_lane_loop.rs b/relays/messages/src/message_lane_loop.rs index 6b28dcbaa60..e26849bbb9b 100644 --- a/relays/messages/src/message_lane_loop.rs +++ b/relays/messages/src/message_lane_loop.rs @@ -109,9 +109,19 @@ pub struct NoncesSubmitArtifacts { pub tx_tracker: T, } +/// Batch transaction that already submit some headers and needs to be extended with +/// messages/delivery proof before sending. +pub trait BatchTransaction: Send { + /// Header that was required in the original call and which is bundled within this + /// batch transaction. + fn required_header_id(&self) -> HeaderId; +} + /// Source client trait. #[async_trait] pub trait SourceClient: RelayClient { + /// Type of batch transaction that submits finality and message receiving proof. + type BatchTransaction: BatchTransaction>; /// Transaction tracker to track submitted transactions. type TransactionTracker: TransactionTracker>; @@ -151,17 +161,32 @@ pub trait SourceClient: RelayClient { /// Submit messages receiving proof. async fn submit_messages_receiving_proof( &self, + maybe_batch_tx: Option, generated_at_block: TargetHeaderIdOf

, proof: P::MessagesReceivingProof, ) -> Result; /// We need given finalized target header on source to continue synchronization. - async fn require_target_header_on_source(&self, id: TargetHeaderIdOf

); + /// + /// We assume that the absence of header `id` has already been checked by caller. + /// + /// The client may return `Some(_)`, which means that nothing has happened yet and + /// the caller must generate and append message receiving proof to the batch transaction + /// to actually send it (along with required header) to the node. + /// + /// If function has returned `None`, it means that the caller now must wait for the + /// appearance of the target header `id` at the source client. + async fn require_target_header_on_source( + &self, + id: TargetHeaderIdOf

, + ) -> Result, Self::Error>; } /// Target client trait. #[async_trait] pub trait TargetClient: RelayClient { + /// Type of batch transaction that submits finality and messages proof. + type BatchTransaction: BatchTransaction>; /// Transaction tracker to track submitted transactions. type TransactionTracker: TransactionTracker>; @@ -195,13 +220,24 @@ pub trait TargetClient: RelayClient { /// Submit messages proof. async fn submit_messages_proof( &self, + maybe_batch_tx: Option, generated_at_header: SourceHeaderIdOf

, nonces: RangeInclusive, proof: P::MessagesProof, ) -> Result, Self::Error>; /// We need given finalized source header on target to continue synchronization. - async fn require_source_header_on_target(&self, id: SourceHeaderIdOf

); + /// + /// The client may return `Some(_)`, which means that nothing has happened yet and + /// the caller must generate and append messages proof to the batch transaction + /// to actually send it (along with required header) to the node. + /// + /// If function has returned `None`, it means that the caller now must wait for the + /// appearance of the source header `id` at the target client. + async fn require_source_header_on_target( + &self, + id: SourceHeaderIdOf

, + ) -> Result, Self::Error>; } /// State of the client. @@ -483,6 +519,30 @@ pub(crate) mod tests { type TargetHeaderHash = TestTargetHeaderHash; } + #[derive(Clone, Debug)] + pub struct TestMessagesBatchTransaction { + required_header_id: TestSourceHeaderId, + } + + #[async_trait] + impl BatchTransaction for TestMessagesBatchTransaction { + fn required_header_id(&self) -> TestSourceHeaderId { + self.required_header_id + } + } + + #[derive(Clone, Debug)] + pub struct TestConfirmationBatchTransaction { + required_header_id: TestTargetHeaderId, + } + + #[async_trait] + impl BatchTransaction for TestConfirmationBatchTransaction { + fn required_header_id(&self) -> TestTargetHeaderId { + self.required_header_id + } + } + #[derive(Clone, Debug)] pub struct TestTransactionTracker(TrackedTransactionStatus); @@ -517,8 +577,10 @@ pub(crate) mod tests { target_latest_confirmed_received_nonce: MessageNonce, target_tracked_transaction_status: TrackedTransactionStatus, submitted_messages_proofs: Vec, + target_to_source_batch_transaction: Option, target_to_source_header_required: Option, target_to_source_header_requirements: Vec, + source_to_target_batch_transaction: Option, source_to_target_header_required: Option, source_to_target_header_requirements: Vec, } @@ -546,14 +608,38 @@ pub(crate) mod tests { Default::default(), )), submitted_messages_proofs: Vec::new(), + target_to_source_batch_transaction: None, target_to_source_header_required: None, target_to_source_header_requirements: Vec::new(), + source_to_target_batch_transaction: None, source_to_target_header_required: None, source_to_target_header_requirements: Vec::new(), } } } + impl TestClientData { + fn receive_messages(&mut self, proof: TestMessagesProof) { + self.target_state.best_self = + HeaderId(self.target_state.best_self.0 + 1, self.target_state.best_self.1 + 1); + self.target_state.best_finalized_self = self.target_state.best_self; + self.target_latest_received_nonce = *proof.0.end(); + if let Some(target_latest_confirmed_received_nonce) = proof.1 { + self.target_latest_confirmed_received_nonce = + target_latest_confirmed_received_nonce; + } + self.submitted_messages_proofs.push(proof); + } + + fn receive_messages_delivery_proof(&mut self, proof: TestMessagesReceivingProof) { + self.source_state.best_self = + HeaderId(self.source_state.best_self.0 + 1, self.source_state.best_self.1 + 1); + self.source_state.best_finalized_self = self.source_state.best_self; + self.submitted_messages_receiving_proofs.push(proof); + self.source_latest_confirmed_received_nonce = proof; + } + } + #[derive(Clone)] pub struct TestSourceClient { data: Arc>, @@ -588,6 +674,7 @@ pub(crate) mod tests { #[async_trait] impl SourceClient for TestSourceClient { + type BatchTransaction = TestConfirmationBatchTransaction; type TransactionTracker = TestTransactionTracker; async fn state(&self) -> Result, TestError> { @@ -670,26 +757,31 @@ pub(crate) mod tests { async fn submit_messages_receiving_proof( &self, + _maybe_batch_tx: Option, _generated_at_block: TargetHeaderIdOf, proof: TestMessagesReceivingProof, ) -> Result { let mut data = self.data.lock(); (self.tick)(&mut data); - data.source_state.best_self = - HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.1 + 1); - data.source_state.best_finalized_self = data.source_state.best_self; - data.submitted_messages_receiving_proofs.push(proof); - data.source_latest_confirmed_received_nonce = proof; + data.receive_messages_delivery_proof(proof); (self.post_tick)(&mut data); Ok(TestTransactionTracker(data.source_tracked_transaction_status)) } - async fn require_target_header_on_source(&self, id: TargetHeaderIdOf) { + async fn require_target_header_on_source( + &self, + id: TargetHeaderIdOf, + ) -> Result, Self::Error> { let mut data = self.data.lock(); data.target_to_source_header_required = Some(id); data.target_to_source_header_requirements.push(id); (self.tick)(&mut data); (self.post_tick)(&mut data); + + Ok(data.target_to_source_batch_transaction.take().map(|mut tx| { + tx.required_header_id = id; + tx + })) } } @@ -727,6 +819,7 @@ pub(crate) mod tests { #[async_trait] impl TargetClient for TestTargetClient { + type BatchTransaction = TestMessagesBatchTransaction; type TransactionTracker = TestTransactionTracker; async fn state(&self) -> Result, TestError> { @@ -789,6 +882,7 @@ pub(crate) mod tests { async fn submit_messages_proof( &self, + _maybe_batch_tx: Option, _generated_at_header: SourceHeaderIdOf, nonces: RangeInclusive, proof: TestMessagesProof, @@ -798,15 +892,7 @@ pub(crate) mod tests { if data.is_target_fails { return Err(TestError) } - data.target_state.best_self = - HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1); - data.target_state.best_finalized_self = data.target_state.best_self; - data.target_latest_received_nonce = *proof.0.end(); - if let Some(target_latest_confirmed_received_nonce) = proof.1 { - data.target_latest_confirmed_received_nonce = - target_latest_confirmed_received_nonce; - } - data.submitted_messages_proofs.push(proof); + data.receive_messages(proof); (self.post_tick)(&mut data); Ok(NoncesSubmitArtifacts { nonces, @@ -814,17 +900,25 @@ pub(crate) mod tests { }) } - async fn require_source_header_on_target(&self, id: SourceHeaderIdOf) { + async fn require_source_header_on_target( + &self, + id: SourceHeaderIdOf, + ) -> Result, Self::Error> { let mut data = self.data.lock(); data.source_to_target_header_required = Some(id); data.source_to_target_header_requirements.push(id); (self.tick)(&mut data); (self.post_tick)(&mut data); + + Ok(data.source_to_target_batch_transaction.take().map(|mut tx| { + tx.required_header_id = id; + tx + })) } } fn run_loop_test( - data: TestClientData, + data: Arc>, source_tick: Arc, source_post_tick: Arc, target_tick: Arc, @@ -832,8 +926,6 @@ pub(crate) mod tests { exit_signal: impl Future + 'static + Send, ) -> TestClientData { async_std::task::block_on(async { - let data = Arc::new(Mutex::new(data)); - let source_client = TestSourceClient { data: data.clone(), tick: source_tick, @@ -846,7 +938,7 @@ pub(crate) mod tests { }; let _ = run( Params { - lane: [0, 0, 0, 0], + lane: LaneId([0, 0, 0, 0]), source_tick: Duration::from_millis(100), target_tick: Duration::from_millis(100), reconnect_delay: Duration::from_millis(0), @@ -876,7 +968,7 @@ pub(crate) mod tests { // able to deliver messages. let (exit_sender, exit_receiver) = unbounded(); let result = run_loop_test( - TestClientData { + Arc::new(Mutex::new(TestClientData { is_source_fails: true, source_state: ClientState { best_self: HeaderId(0, 0), @@ -893,7 +985,7 @@ pub(crate) mod tests { }, target_latest_received_nonce: 0, ..Default::default() - }, + })), Arc::new(|data: &mut TestClientData| { if data.is_source_reconnected { data.is_source_fails = false; @@ -929,7 +1021,7 @@ pub(crate) mod tests { let (source_exit_sender, exit_receiver) = unbounded(); let target_exit_sender = source_exit_sender.clone(); let result = run_loop_test( - TestClientData { + Arc::new(Mutex::new(TestClientData { source_state: ClientState { best_self: HeaderId(0, 0), best_finalized_self: HeaderId(0, 0), @@ -947,7 +1039,7 @@ pub(crate) mod tests { target_latest_received_nonce: 0, target_tracked_transaction_status: TrackedTransactionStatus::Lost, ..Default::default() - }, + })), Arc::new(move |data: &mut TestClientData| { if data.is_source_reconnected { data.source_tracked_transaction_status = @@ -980,7 +1072,7 @@ pub(crate) mod tests { // their corresponding nonce won't be udpated => reconnect will happen let (exit_sender, exit_receiver) = unbounded(); let result = run_loop_test( - TestClientData { + Arc::new(Mutex::new(TestClientData { source_state: ClientState { best_self: HeaderId(0, 0), best_finalized_self: HeaderId(0, 0), @@ -996,7 +1088,7 @@ pub(crate) mod tests { }, target_latest_received_nonce: 0, ..Default::default() - }, + })), Arc::new(move |data: &mut TestClientData| { // blocks are produced on every tick data.source_state.best_self = @@ -1054,7 +1146,7 @@ pub(crate) mod tests { fn message_lane_loop_works() { let (exit_sender, exit_receiver) = unbounded(); let result = run_loop_test( - TestClientData { + Arc::new(Mutex::new(TestClientData { source_state: ClientState { best_self: HeaderId(10, 10), best_finalized_self: HeaderId(10, 10), @@ -1070,7 +1162,7 @@ pub(crate) mod tests { }, target_latest_received_nonce: 0, ..Default::default() - }, + })), Arc::new(|data: &mut TestClientData| { // blocks are produced on every tick data.source_state.best_self = @@ -1133,4 +1225,68 @@ pub(crate) mod tests { assert!(!result.target_to_source_header_requirements.is_empty()); assert!(!result.source_to_target_header_requirements.is_empty()); } + + #[test] + fn message_lane_loop_works_with_batch_transactions() { + let (exit_sender, exit_receiver) = unbounded(); + let original_data = Arc::new(Mutex::new(TestClientData { + source_state: ClientState { + best_self: HeaderId(10, 10), + best_finalized_self: HeaderId(10, 10), + best_finalized_peer_at_best_self: HeaderId(0, 0), + actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + }, + source_latest_generated_nonce: 10, + target_state: ClientState { + best_self: HeaderId(0, 0), + best_finalized_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: HeaderId(0, 0), + actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + }, + target_latest_received_nonce: 0, + ..Default::default() + })); + let result = run_loop_test( + original_data, + Arc::new(|_| {}), + Arc::new(move |data: &mut TestClientData| { + if let Some(target_to_source_header_required) = + data.target_to_source_header_required.take() + { + data.target_to_source_batch_transaction = + Some(TestConfirmationBatchTransaction { + required_header_id: target_to_source_header_required, + }) + } + }), + Arc::new(|_| {}), + Arc::new(move |data: &mut TestClientData| { + if let Some(source_to_target_header_required) = + data.source_to_target_header_required.take() + { + data.source_to_target_batch_transaction = Some(TestMessagesBatchTransaction { + required_header_id: source_to_target_header_required, + }) + } + + if data.source_latest_confirmed_received_nonce == 10 { + exit_sender.unbounded_send(()).unwrap(); + } + }), + exit_receiver.into_future().map(|(_, _)| ()), + ); + + // there are no strict restrictions on when reward confirmation should come + // (because `max_unconfirmed_nonces_at_target` is `100` in tests and this confirmation + // depends on the state of both clients) + // => we do not check it here + assert_eq!(result.submitted_messages_proofs[0].0, 1..=4); + assert_eq!(result.submitted_messages_proofs[1].0, 5..=8); + assert_eq!(result.submitted_messages_proofs[2].0, 9..=10); + assert!(!result.submitted_messages_receiving_proofs.is_empty()); + + // check that we have at least once required new source->target or target->source headers + assert!(!result.target_to_source_header_requirements.is_empty()); + assert!(!result.source_to_target_header_requirements.is_empty()); + } } diff --git a/relays/messages/src/message_race_delivery.rs b/relays/messages/src/message_race_delivery.rs index b49a05dac5c..0d957fd8b71 100644 --- a/relays/messages/src/message_race_delivery.rs +++ b/relays/messages/src/message_race_delivery.rs @@ -171,9 +171,13 @@ where { type Error = C::Error; type TargetNoncesData = DeliveryRaceTargetNoncesData; + type BatchTransaction = C::BatchTransaction; type TransactionTracker = C::TransactionTracker; - async fn require_source_header(&self, id: SourceHeaderIdOf

) { + async fn require_source_header( + &self, + id: SourceHeaderIdOf

, + ) -> Result, Self::Error> { self.client.require_source_header_on_target(id).await } @@ -210,11 +214,14 @@ where async fn submit_proof( &self, + maybe_batch_tx: Option, generated_at_block: SourceHeaderIdOf

, nonces: RangeInclusive, proof: P::MessagesProof, ) -> Result, Self::Error> { - self.client.submit_messages_proof(generated_at_block, nonces, proof).await + self.client + .submit_messages_proof(maybe_batch_tx, generated_at_block, nonces, proof) + .await } } @@ -314,16 +321,31 @@ where &self, current_best: &SourceHeaderIdOf

, ) -> Option> { + let has_nonces_to_deliver = !self.strategy.is_empty(); let header_required_for_messages_delivery = self.strategy.required_source_header_at_target(current_best); - let header_required_for_reward_confirmations_delivery = - self.latest_confirmed_nonces_at_source.back().map(|(id, _)| id.clone()); + let header_required_for_reward_confirmations_delivery = self + .latest_confirmed_nonces_at_source + .back() + .filter(|(id, nonce)| *nonce != 0 && id.0 > current_best.0) + .map(|(id, _)| id.clone()); match ( + has_nonces_to_deliver, header_required_for_messages_delivery, header_required_for_reward_confirmations_delivery, ) { - (Some(id1), Some(id2)) => Some(if id1.0 > id2.0 { id1 } else { id2 }), - (a, b) => a.or(b), + // if we need to delver messages and proof-of-delivery-confirmations, then we need to + // select the most recent header to avoid extra roundtrips + (true, Some(id1), Some(id2)) => Some(if id1.0 > id2.0 { id1 } else { id2 }), + // if we only need to deliver messages - fine, let's require some source header + // + // if we need new header for proof-of-delivery-confirmations - let's also ask for that. + // Even though it may require additional header, we'll be sure that we won't block the + // lane (sometimes we can't deliver messages without proof-of-delivery-confirmations) + (true, a, b) => a.or(b), + // we never submit delivery transaction without messages, so if `has_nonces_to_deliver` + // if `false`, we don't need any source headers at target + (false, _, _) => None, } } @@ -590,7 +612,6 @@ mod tests { }, ) }) - .into_iter() .collect(), confirmed_nonce: Some(confirmed_nonce), } @@ -922,38 +943,58 @@ mod tests { // - all messages [20; 23] have been generated at source block#1; let (mut state, mut strategy) = prepare_strategy(); // - // - messages [20; 21] have been delivered, but messages [11; 20] can't be delivered because - // of unrewarded relayers vector capacity; - strategy.max_unconfirmed_nonces_at_target = 2; + // - messages [20; 23] have been delivered assert_eq!( strategy.select_nonces_to_deliver(state.clone()).await, - Some(((20..=21), proof_parameters(false, 2))) + Some(((20..=23), proof_parameters(false, 4))) ); strategy.finalized_target_nonces_updated( TargetClientNonces { - latest_nonce: 21, + latest_nonce: 23, nonces_data: DeliveryRaceTargetNoncesData { confirmed_nonce: 19, unrewarded_relayers: UnrewardedRelayersState { - unrewarded_relayer_entries: 2, - messages_in_oldest_entry: 2, - total_messages: 2, - last_delivered_nonce: 19, + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 4, + total_messages: 4, + last_delivered_nonce: 23, }, }, }, &mut state, ); - assert_eq!(strategy.select_nonces_to_deliver(state).await, None); - // - // - messages [1; 10] receiving confirmation has been delivered at source block#2; - strategy.source_nonces_updated( - header_id(2), - SourceClientNonces { new_nonces: MessageDetailsMap::new(), confirmed_nonce: Some(21) }, - ); + // nothing needs to be delivered now and we don't need any new headers + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); + assert_eq!(strategy.required_source_header_at_target(&header_id(1)), None); + + // now let's generate two more nonces [24; 25] at the soruce; + strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 19, 0)); // - // - so now we'll need to relay source block#11 to be able to accept messages [11; 20]. + // - so now we'll need to relay source block#2 to be able to accept messages [24; 25]. + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); assert_eq!(strategy.required_source_header_at_target(&header_id(1)), Some(header_id(2))); + + // let's relay source block#2 + state.best_finalized_source_header_id_at_source = Some(header_id(2)); + state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); + state.best_target_header_id = Some(header_id(2)); + state.best_finalized_target_header_id = Some(header_id(2)); + + // and ask strategy again => still nothing to deliver, because parallel confirmations + // race need to be pushed further + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); + assert_eq!(strategy.required_source_header_at_target(&header_id(2)), None); + + // let's confirm messages [20; 23] + strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 23, 0)); + + // and ask strategy again => now we have everything required to deliver remaining + // [24; 25] nonces and proof of [20; 23] confirmation + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((24..=25), proof_parameters(true, 2))), + ); + assert_eq!(strategy.required_source_header_at_target(&header_id(2)), None); } #[async_std::test] @@ -981,4 +1022,38 @@ mod tests { Some(((20..=24), proof_parameters(false, 5))) ); } + + #[test] + #[allow(clippy::reversed_empty_ranges)] + fn no_source_headers_required_at_target_if_lanes_are_empty() { + let mut strategy = TestStrategy { + max_unrewarded_relayer_entries_at_target: 4, + max_unconfirmed_nonces_at_target: 4, + max_messages_in_single_batch: 4, + max_messages_weight_in_single_batch: Weight::from_ref_time(4), + max_messages_size_in_single_batch: 4, + latest_confirmed_nonces_at_source: VecDeque::new(), + lane_source_client: TestSourceClient::default(), + lane_target_client: TestTargetClient::default(), + metrics_msg: None, + target_nonces: None, + strategy: BasicStrategy::new(), + }; + + let source_header_id = header_id(10); + strategy.source_nonces_updated( + source_header_id, + // MessageDeliveryRaceSource::nonces returns Some(0), because that's how it is + // represented in memory (there's no Options in OutboundLaneState) + source_nonces(1u64..=0u64, 0, 0), + ); + + // even though `latest_confirmed_nonces_at_source` is not empty, new headers are not + // requested + assert_eq!( + strategy.latest_confirmed_nonces_at_source, + VecDeque::from([(source_header_id, 0)]) + ); + assert_eq!(strategy.required_source_header_at_target(&source_header_id), None); + } } diff --git a/relays/messages/src/message_race_loop.rs b/relays/messages/src/message_race_loop.rs index 4f59b635ae6..deac71e5b6e 100644 --- a/relays/messages/src/message_race_loop.rs +++ b/relays/messages/src/message_race_loop.rs @@ -20,7 +20,7 @@ //! associated data - like messages, lane state, etc) to the target node by //! generating and submitting proof. -use crate::message_lane_loop::{ClientState, NoncesSubmitArtifacts}; +use crate::message_lane_loop::{BatchTransaction, ClientState, NoncesSubmitArtifacts}; use async_trait::async_trait; use bp_messages::MessageNonce; @@ -127,12 +127,24 @@ pub trait TargetClient { type Error: std::fmt::Debug + MaybeConnectionError; /// Type of the additional data from the target client, used by the race. type TargetNoncesData: std::fmt::Debug; + /// Type of batch transaction that submits finality and proof to the target node. + type BatchTransaction: BatchTransaction; /// Transaction tracker to track submitted transactions. type TransactionTracker: TransactionTracker; /// Ask headers relay to relay finalized headers up to (and including) given header /// from race source to race target. - async fn require_source_header(&self, id: P::SourceHeaderId); + /// + /// The client may return `Some(_)`, which means that nothing has happened yet and + /// the caller must generate and append proof to the batch transaction + /// to actually send it (along with required header) to the node. + /// + /// If function has returned `None`, it means that the caller now must wait for the + /// appearance of the required header `id` at the target client. + async fn require_source_header( + &self, + id: P::SourceHeaderId, + ) -> Result, Self::Error>; /// Return nonces that are known to the target client. async fn nonces( @@ -143,6 +155,7 @@ pub trait TargetClient { /// Submit proof to the target client. async fn submit_proof( &self, + maybe_batch_tx: Option, generated_at_block: P::SourceHeaderId, nonces: RangeInclusive, proof: P::Proof, @@ -242,6 +255,7 @@ pub async fn run, TC: TargetClient

>( let mut source_retry_backoff = retry_backoff(); let mut source_client_is_online = true; let mut source_nonces_required = false; + let mut source_required_header = None; let source_nonces = futures::future::Fuse::terminated(); let source_generate_proof = futures::future::Fuse::terminated(); let source_go_offline_future = futures::future::Fuse::terminated(); @@ -250,6 +264,8 @@ pub async fn run, TC: TargetClient

>( let mut target_client_is_online = true; let mut target_best_nonces_required = false; let mut target_finalized_nonces_required = false; + let mut target_batch_transaction = None; + let target_require_source_header = futures::future::Fuse::terminated(); let target_best_nonces = futures::future::Fuse::terminated(); let target_finalized_nonces = futures::future::Fuse::terminated(); let target_submit_proof = futures::future::Fuse::terminated(); @@ -262,6 +278,7 @@ pub async fn run, TC: TargetClient

>( source_generate_proof, source_go_offline_future, race_target_updated, + target_require_source_header, target_best_nonces, target_finalized_nonces, target_submit_proof, @@ -326,13 +343,10 @@ pub async fn run, TC: TargetClient

>( ).fail_if_connection_error(FailedClient::Source)?; // ask for more headers if we have nonces to deliver and required headers are missing - let required_source_header_id = race_state + source_required_header = race_state .best_finalized_source_header_id_at_best_target .as_ref() - .and_then(|best|strategy.required_source_header_at_target(best)); - if let Some(required_source_header_id) = required_source_header_id { - race_target.require_source_header(required_source_header_id).await; - } + .and_then(|best| strategy.required_source_header_at_target(best)); }, nonces = target_best_nonces => { target_best_nonces_required = false; @@ -378,6 +392,28 @@ pub async fn run, TC: TargetClient

>( }, // proof generation and submission + maybe_batch_transaction = target_require_source_header => { + source_required_header = None; + + target_client_is_online = process_future_result( + maybe_batch_transaction, + &mut target_retry_backoff, + |maybe_batch_transaction: Option| { + log::debug!( + target: "bridge", + "Target {} client has been asked for more {} headers. Batch tx: {:?}", + P::target_name(), + P::source_name(), + maybe_batch_transaction.is_some(), + ); + + target_batch_transaction = maybe_batch_transaction; + }, + &mut target_go_offline_future, + async_std::task::sleep, + || format!("Error asking for source headers at {}", P::target_name()), + ).fail_if_connection_error(FailedClient::Target)?; + }, proof = source_generate_proof => { source_client_is_online = process_future_result( proof, @@ -409,6 +445,7 @@ pub async fn run, TC: TargetClient

>( P::target_name(), ); + target_batch_transaction = None; race_state.nonces_to_submit = None; race_state.nonces_submitted = Some(artifacts.nonces); target_tx_tracker.set(artifacts.tx_tracker.wait().fuse()); @@ -479,8 +516,23 @@ pub async fn run, TC: TargetClient

>( if source_client_is_online { source_client_is_online = false; + // if we've started to submit batch transaction, let's prioritize it + let expected_race_state = + if let Some(ref target_batch_transaction) = target_batch_transaction { + // when selecting nonces for the batch transaction, we assume that the required + // source header is already at the target chain + let required_source_header_at_target = + target_batch_transaction.required_header_id(); + let mut expected_race_state = race_state.clone(); + expected_race_state.best_finalized_source_header_id_at_best_target = + Some(required_source_header_at_target); + expected_race_state + } else { + race_state.clone() + }; + let nonces_to_deliver = - select_nonces_to_deliver(race_state.clone(), &mut strategy).await; + select_nonces_to_deliver(expected_race_state, &mut strategy).await; let best_at_source = strategy.best_at_source(); if let Some((at_block, nonces_range, proof_parameters)) = nonces_to_deliver { @@ -491,6 +543,7 @@ pub async fn run, TC: TargetClient

>( nonces_range, at_block, ); + source_generate_proof.set( race_source.generate_proof(at_block, nonces_range, proof_parameters).fuse(), ); @@ -524,11 +577,28 @@ pub async fn run, TC: TargetClient

>( nonces_range, P::target_name(), ); + if let Some(ref target_batch_transaction) = target_batch_transaction { + log::debug!( + target: "bridge", + "This transaction is batched with sending the proof for header {:?}.", + target_batch_transaction.required_header_id(), + ); + } + target_submit_proof.set( race_target - .submit_proof(at_block.clone(), nonces_range.clone(), proof.clone()) + .submit_proof( + target_batch_transaction.take(), + at_block.clone(), + nonces_range.clone(), + proof.clone(), + ) .fuse(), ); + } else if let Some(source_required_header) = source_required_header.clone() { + log::debug!(target: "bridge", "Going to require {} header {:?} at {}", P::source_name(), source_required_header, P::target_name()); + target_require_source_header + .set(race_target.require_source_header(source_required_header).fuse()); } else if target_best_nonces_required { log::debug!(target: "bridge", "Asking {} about best message nonces", P::target_name()); let at_block = race_state diff --git a/relays/messages/src/message_race_receiving.rs b/relays/messages/src/message_race_receiving.rs index c3d65d0e86a..70a3d7c2477 100644 --- a/relays/messages/src/message_race_receiving.rs +++ b/relays/messages/src/message_race_receiving.rs @@ -155,9 +155,13 @@ where { type Error = C::Error; type TargetNoncesData = (); + type BatchTransaction = C::BatchTransaction; type TransactionTracker = C::TransactionTracker; - async fn require_source_header(&self, id: TargetHeaderIdOf

) { + async fn require_source_header( + &self, + id: TargetHeaderIdOf

, + ) -> Result, Self::Error> { self.client.require_target_header_on_source(id).await } @@ -178,12 +182,15 @@ where async fn submit_proof( &self, + maybe_batch_tx: Option, generated_at_block: TargetHeaderIdOf

, nonces: RangeInclusive, proof: P::MessagesReceivingProof, ) -> Result, Self::Error> { - let tx_tracker = - self.client.submit_messages_receiving_proof(generated_at_block, proof).await?; + let tx_tracker = self + .client + .submit_messages_receiving_proof(maybe_batch_tx, generated_at_block, proof) + .await?; Ok(NoncesSubmitArtifacts { nonces, tx_tracker }) } } diff --git a/relays/messages/src/metrics.rs b/relays/messages/src/metrics.rs index 4decb7e092e..ace4264cacc 100644 --- a/relays/messages/src/metrics.rs +++ b/relays/messages/src/metrics.rs @@ -65,10 +65,9 @@ impl MessageLaneLoopMetrics { /// Update source client state metrics. pub fn update_source_state(&self, source_client_state: SourceClientState

) { self.source_to_target_finality_metrics - .update_best_block_at_source(source_client_state.best_self.0.into()); - self.target_to_source_finality_metrics.update_best_block_at_target( - source_client_state.best_finalized_peer_at_best_self.0.into(), - ); + .update_best_block_at_source(source_client_state.best_self.0); + self.target_to_source_finality_metrics + .update_best_block_at_target(source_client_state.best_finalized_peer_at_best_self.0); self.target_to_source_finality_metrics.update_using_same_fork( source_client_state.best_finalized_peer_at_best_self.1 == source_client_state.actual_best_finalized_peer_at_best_self.1, @@ -78,10 +77,9 @@ impl MessageLaneLoopMetrics { /// Update target client state metrics. pub fn update_target_state(&self, target_client_state: TargetClientState

) { self.target_to_source_finality_metrics - .update_best_block_at_source(target_client_state.best_self.0.into()); - self.source_to_target_finality_metrics.update_best_block_at_target( - target_client_state.best_finalized_peer_at_best_self.0.into(), - ); + .update_best_block_at_source(target_client_state.best_self.0); + self.source_to_target_finality_metrics + .update_best_block_at_target(target_client_state.best_finalized_peer_at_best_self.0); self.source_to_target_finality_metrics.update_using_same_fork( target_client_state.best_finalized_peer_at_best_self.1 == target_client_state.actual_best_finalized_peer_at_best_self.1, diff --git a/relays/parachains/src/parachains_loop_metrics.rs b/relays/parachains/src/parachains_loop_metrics.rs index ff8bace2744..5df996b4ddd 100644 --- a/relays/parachains/src/parachains_loop_metrics.rs +++ b/relays/parachains/src/parachains_loop_metrics.rs @@ -15,8 +15,9 @@ // along with Parity Bridges Common. If not, see . use bp_polkadot_core::parachains::ParaId; -use relay_utils::metrics::{ - metric_name, register, GaugeVec, Metric, Opts, PrometheusError, Registry, U64, +use relay_utils::{ + metrics::{metric_name, register, GaugeVec, Metric, Opts, PrometheusError, Registry, U64}, + UniqueSaturatedInto, }; /// Parachains sync metrics. @@ -50,12 +51,12 @@ impl ParachainsLoopMetrics { } /// Update best block number at source. - pub fn update_best_parachain_block_at_source>( + pub fn update_best_parachain_block_at_source>( &self, parachain: ParaId, block_number: Number, ) { - let block_number = block_number.into(); + let block_number = block_number.unique_saturated_into(); let label = parachain_label(¶chain); log::trace!( target: "bridge-metrics", @@ -67,12 +68,12 @@ impl ParachainsLoopMetrics { } /// Update best block number at target. - pub fn update_best_parachain_block_at_target>( + pub fn update_best_parachain_block_at_target>( &self, parachain: ParaId, block_number: Number, ) { - let block_number = block_number.into(); + let block_number = block_number.unique_saturated_into(); let label = parachain_label(¶chain); log::trace!( target: "bridge-metrics", diff --git a/relays/utils/Cargo.toml b/relays/utils/Cargo.toml index ebbfa74c6ff..09cec370900 100644 --- a/relays/utils/Cargo.toml +++ b/relays/utils/Cargo.toml @@ -29,4 +29,5 @@ bp-runtime = { path = "../../primitives/runtime" } # Substrate dependencies +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } substrate-prometheus-endpoint = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/utils/src/lib.rs b/relays/utils/src/lib.rs index 42bf86ebf5b..428ee33494c 100644 --- a/relays/utils/src/lib.rs +++ b/relays/utils/src/lib.rs @@ -19,6 +19,7 @@ pub use bp_runtime::HeaderId; pub use error::Error; pub use relay_loop::{relay_loop, relay_metrics}; +pub use sp_runtime::traits::{UniqueSaturatedFrom, UniqueSaturatedInto}; use async_trait::async_trait; use backoff::{backoff::Backoff, ExponentialBackoff}; @@ -51,7 +52,7 @@ pub mod relay_loop; pub trait BlockNumberBase: 'static + From - + Into + + UniqueSaturatedInto + Ord + Clone + Copy @@ -73,7 +74,7 @@ pub trait BlockNumberBase: impl BlockNumberBase for T where T: 'static + From - + Into + + UniqueSaturatedInto + Ord + Clone + Copy diff --git a/relays/utils/src/metrics.rs b/relays/utils/src/metrics.rs index fa7a79a71c1..2e6c8236da4 100644 --- a/relays/utils/src/metrics.rs +++ b/relays/utils/src/metrics.rs @@ -99,6 +99,39 @@ impl Default for MetricsAddress { } impl MetricsParams { + /// Creates metrics params from metrics address. + pub fn new( + address: Option, + relay_version: String, + relay_commit: String, + ) -> Result { + const BUILD_INFO_METRIC: &str = "substrate_relay_build_info"; + + let registry = Registry::new(); + register( + Gauge::::with_opts( + Opts::new( + BUILD_INFO_METRIC, + "A metric with a constant '1' value labeled by version", + ) + .const_label("version", &relay_version) + .const_label("commit", &relay_commit), + )?, + ®istry, + )? + .set(1); + + log::info!( + target: "bridge", + "Exposed {} metric: version={} commit={}", + BUILD_INFO_METRIC, + relay_version, + relay_commit, + ); + + Ok(MetricsParams { address, registry }) + } + /// Creates metrics params so that metrics are not exposed. pub fn disabled() -> Self { MetricsParams { address: None, registry: Registry::new() } @@ -112,12 +145,6 @@ impl MetricsParams { } } -impl From> for MetricsParams { - fn from(address: Option) -> Self { - MetricsParams { address, registry: Registry::new() } - } -} - /// Returns metric name optionally prefixed with given prefix. pub fn metric_name(prefix: Option<&str>, name: &str) -> String { if let Some(prefix) = prefix { diff --git a/scripts/send-message-from-millau-rialto.sh b/scripts/send-message-from-millau-rialto.sh index 539ca5fc06c..bcfc9df2f57 100755 --- a/scripts/send-message-from-millau-rialto.sh +++ b/scripts/send-message-from-millau-rialto.sh @@ -8,26 +8,11 @@ # TODO: Fix demeo scripts https://github.com/paritytech/parity-bridges-common/issues/1406 -MILLAU_PORT="${RIALTO_PORT:-9945}" +MILLAU_PORT="${MILLAU_PORT:-9945}" -case "$1" in - remark) - RUST_LOG=runtime=trace,substrate-relay=trace,bridge=trace \ - ./target/debug/substrate-relay send-message millau-to-rialto \ - --source-host localhost \ - --source-port $MILLAU_PORT \ - --source-signer //Alice \ - raw 020419ac - ;; - transfer) - RUST_LOG=runtime=trace,substrate-relay=trace,bridge=trace \ - ./target/debug/substrate-relay send-message millau-to-rialto \ - --source-host localhost \ - --source-port $MILLAU_PORT \ - --source-signer //Alice \ - transfer \ - --amount 100000000000000 \ - --recipient 5DZvVvd1udr61vL7Xks17TFQ4fi9NiagYLaBobnbPCP14ewA \ - ;; - *) echo "A message type is require. Supported messages: remark, transfer."; exit 1;; -esac +RUST_LOG=runtime=trace,substrate-relay=trace,bridge=trace \ +./target/debug/substrate-relay send-message millau-to-rialto \ + --source-host localhost \ + --source-port $MILLAU_PORT \ + --source-signer //Alice \ + raw 020419ac diff --git a/scripts/send-message-from-rialto-millau.sh b/scripts/send-message-from-rialto-millau.sh index 923f588ea47..e348c33f2a2 100755 --- a/scripts/send-message-from-rialto-millau.sh +++ b/scripts/send-message-from-rialto-millau.sh @@ -10,24 +10,9 @@ RIALTO_PORT="${RIALTO_PORT:-9944}" -case "$1" in - remark) - RUST_LOG=runtime=trace,substrate-relay=trace,bridge=trace \ - ./target/debug/substrate-relay send-message rialto-to-millau \ - --source-host localhost \ - --source-port $RIALTO_PORT \ - --source-signer //Bob \ - raw 020419ac - ;; - transfer) - RUST_LOG=runtime=trace,substrate-relay=trace,bridge=trace \ - ./target/debug/substrate-relay send-message rialto-to-millau \ - --source-host localhost \ - --source-port $RIALTO_PORT \ - --source-signer //Bob \ - transfer \ - --amount 100000000000000 \ - --recipient 5DZvVvd1udr61vL7Xks17TFQ4fi9NiagYLaBobnbPCP14ewA \ - ;; - *) echo "A message type is require. Supported messages: remark, transfer."; exit 1;; -esac +RUST_LOG=runtime=trace,substrate-relay=trace,bridge=trace \ +./target/debug/substrate-relay send-message rialto-to-millau \ + --source-host localhost \ + --source-port $RIALTO_PORT \ + --source-signer //Bob \ + raw 020419ac From f93bde1f3b51e314c7a6343ce0c48472f395ad08 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 18 Jan 2023 23:52:36 +0100 Subject: [PATCH 176/263] Fixes after merge --- Cargo.lock | 551 +++++++++--------- parachains/runtimes/bridge-hubs/README.md | 2 +- .../bridge-hub-kusama/src/xcm_config.rs | 10 +- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 2 + .../src/bridge_common_config.rs | 27 +- .../src/bridge_hub_rococo_config.rs | 2 +- .../src/bridge_hub_wococo_config.rs | 2 +- .../bridge-hub-rococo/src/constants.rs | 1 + .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 43 +- .../bridge-hub-rococo/src/xcm_config.rs | 17 +- .../src/chain_spec/bridge_hubs.rs | 89 +-- 11 files changed, 338 insertions(+), 408 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1050db69ac..e30f9d65345 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -462,7 +462,7 @@ dependencies = [ [[package]] name = "beefy-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -496,7 +496,7 @@ dependencies = [ [[package]] name = "beefy-gadget-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "beefy-gadget", "futures", @@ -515,7 +515,7 @@ dependencies = [ [[package]] name = "beefy-merkle-tree" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "sp-api", "sp-beefy", @@ -689,25 +689,31 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-application-crypto", "sp-beefy", - "sp-core", - "sp-io", "sp-runtime", "sp-std", - "static_assertions", ] [[package]] -name = "bp-bridge-hub-rococo" +name = "bp-bridge-hub-cumulus" version = "0.1.0" dependencies = [ "bp-messages", "bp-polkadot-core", + "frame-support", + "frame-system", + "polkadot-primitives", + "sp-api", +] + +[[package]] +name = "bp-bridge-hub-rococo" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-messages", "bp-runtime", "frame-support", - "polkadot-runtime-constants", - "smallvec", "sp-api", "sp-std", ] @@ -716,7 +722,7 @@ dependencies = [ name = "bp-bridge-hub-wococo" version = "0.1.0" dependencies = [ - "bp-bridge-hub-rococo", + "bp-bridge-hub-cumulus", "bp-messages", "bp-runtime", "frame-support", @@ -752,7 +758,6 @@ dependencies = [ "frame-support", "hex", "hex-literal", - "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "serde", @@ -788,12 +793,16 @@ dependencies = [ name = "bp-parachains" version = "0.1.0" dependencies = [ + "bp-header-chain", "bp-polkadot-core", "bp-runtime", "frame-support", + "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "sp-core", + "sp-runtime", + "sp-std", ] [[package]] @@ -819,6 +828,7 @@ name = "bp-relayers" version = "0.1.0" dependencies = [ "bp-messages", + "bp-rialto", "frame-support", "hex", "hex-literal", @@ -872,6 +882,7 @@ dependencies = [ "frame-system", "hash-db", "hex-literal", + "impl-trait-for-tuples", "num-traits", "parity-scale-codec", "scale-info", @@ -906,6 +917,7 @@ version = "0.1.0" dependencies = [ "bp-polkadot-core", "bp-runtime", + "frame-support", "sp-api", ] @@ -989,6 +1001,7 @@ dependencies = [ "bp-bridge-hub-rococo", "bp-bridge-hub-wococo", "bp-messages", + "bp-parachains", "bp-rococo", "bp-runtime", "bp-wococo", @@ -1085,7 +1098,9 @@ dependencies = [ "bp-messages", "bp-parachains", "bp-polkadot-core", + "bp-rialto", "bp-runtime", + "bp-test-utils", "frame-support", "frame-system", "hash-db", @@ -1095,6 +1110,9 @@ dependencies = [ "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", + "pallet-bridge-relayers", + "pallet-transaction-payment", + "pallet-utility", "pallet-xcm", "parity-scale-codec", "scale-info", @@ -3480,7 +3498,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", ] @@ -3503,7 +3521,7 @@ checksum = "85dcb89d2b10c5f6133de2efd8c11959ce9dbb46a2f7a4cab208c4eeda6ce1ab" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "frame-system", @@ -3526,7 +3544,7 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "Inflector", "array-bytes 4.2.0", @@ -3573,7 +3591,7 @@ dependencies = [ [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -3584,7 +3602,7 @@ dependencies = [ [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-election-provider-solution-type", "frame-support", @@ -3601,7 +3619,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "frame-system", @@ -3630,7 +3648,7 @@ dependencies = [ [[package]] name = "frame-remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "futures", "log", @@ -3646,7 +3664,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "bitflags", "frame-metadata", @@ -3678,7 +3696,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "Inflector", "cfg-expr", @@ -3692,7 +3710,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", @@ -3704,7 +3722,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "proc-macro2", "quote", @@ -3714,7 +3732,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "log", @@ -3732,7 +3750,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -3747,7 +3765,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "sp-api", @@ -3756,7 +3774,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "parity-scale-codec", @@ -4704,7 +4722,7 @@ checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" [[package]] name = "kusama-runtime" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "bitvec", "frame-benchmarking", @@ -4801,7 +4819,7 @@ dependencies = [ [[package]] name = "kusama-runtime-constants" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "frame-support", "polkadot-primitives", @@ -5593,6 +5611,7 @@ version = "0.1.0" dependencies = [ "bp-messages", "bp-millau", + "bp-parachains", "bp-polkadot-core", "bp-relayers", "bp-rialto", @@ -5607,7 +5626,6 @@ dependencies = [ "frame-system", "frame-system-rpc-runtime-api", "hex-literal", - "log", "pallet-aura", "pallet-balances", "pallet-beefy", @@ -5625,6 +5643,7 @@ dependencies = [ "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", + "pallet-utility", "pallet-xcm", "parity-scale-codec", "scale-info", @@ -5635,7 +5654,6 @@ dependencies = [ "sp-core", "sp-inherents", "sp-io", - "sp-mmr-primitives", "sp-offchain", "sp-runtime", "sp-session", @@ -5689,7 +5707,7 @@ dependencies = [ [[package]] name = "mmr-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "futures", "log", @@ -5708,7 +5726,7 @@ dependencies = [ [[package]] name = "mmr-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "anyhow", "jsonrpsee", @@ -6200,7 +6218,7 @@ dependencies = [ [[package]] name = "pallet-alliance" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "array-bytes 4.2.0", "frame-benchmarking", @@ -6221,7 +6239,7 @@ dependencies = [ [[package]] name = "pallet-asset-tx-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6239,7 +6257,7 @@ dependencies = [ [[package]] name = "pallet-assets" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6254,7 +6272,7 @@ dependencies = [ [[package]] name = "pallet-aura" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "frame-system", @@ -6270,7 +6288,7 @@ dependencies = [ [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "frame-system", @@ -6286,7 +6304,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "frame-system", @@ -6301,7 +6319,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6325,7 +6343,7 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6345,7 +6363,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6360,7 +6378,7 @@ dependencies = [ [[package]] name = "pallet-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "frame-system", @@ -6376,7 +6394,7 @@ dependencies = [ [[package]] name = "pallet-beefy-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "array-bytes 4.2.0", "beefy-merkle-tree", @@ -6399,7 +6417,7 @@ dependencies = [ [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6473,6 +6491,7 @@ dependencies = [ "pallet-bridge-grandpa", "parity-scale-codec", "scale-info", + "sp-core", "sp-io", "sp-runtime", "sp-std", @@ -6491,7 +6510,6 @@ dependencies = [ "frame-system", "log", "pallet-balances", - "pallet-bridge-messages", "parity-scale-codec", "scale-info", "sp-arithmetic", @@ -6504,7 +6522,7 @@ dependencies = [ [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6548,7 +6566,7 @@ dependencies = [ [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6565,7 +6583,7 @@ dependencies = [ [[package]] name = "pallet-contracts" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "bitflags", "frame-benchmarking", @@ -6594,7 +6612,7 @@ dependencies = [ [[package]] name = "pallet-contracts-primitives" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "bitflags", "parity-scale-codec", @@ -6606,7 +6624,7 @@ dependencies = [ [[package]] name = "pallet-contracts-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "proc-macro2", "quote", @@ -6616,7 +6634,7 @@ dependencies = [ [[package]] name = "pallet-conviction-voting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "assert_matches", "frame-benchmarking", @@ -6633,7 +6651,7 @@ dependencies = [ [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6651,7 +6669,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6674,7 +6692,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-support-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6687,7 +6705,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6705,7 +6723,7 @@ dependencies = [ [[package]] name = "pallet-fast-unstake" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6723,7 +6741,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6746,7 +6764,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "enumflags2", "frame-benchmarking", @@ -6762,7 +6780,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6782,7 +6800,7 @@ dependencies = [ [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6799,7 +6817,7 @@ dependencies = [ [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6816,7 +6834,7 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6833,7 +6851,7 @@ dependencies = [ [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6849,7 +6867,7 @@ dependencies = [ [[package]] name = "pallet-nfts" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "enumflags2", "frame-benchmarking", @@ -6866,7 +6884,7 @@ dependencies = [ [[package]] name = "pallet-nis" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6882,7 +6900,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "frame-system", @@ -6899,7 +6917,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-benchmarking" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6919,7 +6937,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "sp-api", @@ -6929,7 +6947,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "frame-system", @@ -6946,7 +6964,7 @@ dependencies = [ [[package]] name = "pallet-offences-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6970,7 +6988,7 @@ dependencies = [ [[package]] name = "pallet-preimage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -6987,7 +7005,7 @@ dependencies = [ [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -7002,7 +7020,7 @@ dependencies = [ [[package]] name = "pallet-randomness-collective-flip" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "frame-system", @@ -7016,7 +7034,7 @@ dependencies = [ [[package]] name = "pallet-ranked-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -7034,7 +7052,7 @@ dependencies = [ [[package]] name = "pallet-recovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -7049,7 +7067,7 @@ dependencies = [ [[package]] name = "pallet-referenda" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "assert_matches", "frame-benchmarking", @@ -7068,7 +7086,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -7085,7 +7103,7 @@ dependencies = [ [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "frame-system", @@ -7106,7 +7124,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -7137,7 +7155,7 @@ dependencies = [ [[package]] name = "pallet-society" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "frame-system", @@ -7151,7 +7169,7 @@ dependencies = [ [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -7174,7 +7192,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7185,7 +7203,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "log", "sp-arithmetic", @@ -7194,7 +7212,7 @@ dependencies = [ [[package]] name = "pallet-state-trie-migration" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -7211,7 +7229,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "frame-system", @@ -7240,7 +7258,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -7258,7 +7276,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -7277,7 +7295,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-support", "frame-system", @@ -7293,7 +7311,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", @@ -7309,7 +7327,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -7321,7 +7339,7 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -7338,7 +7356,7 @@ dependencies = [ [[package]] name = "pallet-uniques" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -7353,7 +7371,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -7369,7 +7387,7 @@ dependencies = [ [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -7384,7 +7402,7 @@ dependencies = [ [[package]] name = "pallet-whitelist" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-benchmarking", "frame-support", @@ -7399,7 +7417,7 @@ dependencies = [ [[package]] name = "pallet-xcm" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "frame-benchmarking", "frame-support", @@ -7419,7 +7437,7 @@ dependencies = [ [[package]] name = "pallet-xcm-benchmarks" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "frame-benchmarking", "frame-support", @@ -7984,7 +8002,7 @@ dependencies = [ [[package]] name = "polkadot-approval-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "futures", "polkadot-node-metrics", @@ -7999,7 +8017,7 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "futures", "polkadot-node-network-protocol", @@ -8013,7 +8031,7 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "derive_more", "fatality", @@ -8036,7 +8054,7 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "fatality", "futures", @@ -8057,7 +8075,7 @@ dependencies = [ [[package]] name = "polkadot-cli" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "clap 4.0.32", "frame-benchmarking-cli", @@ -8084,7 +8102,7 @@ dependencies = [ [[package]] name = "polkadot-client" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "async-trait", "frame-benchmarking", @@ -8127,7 +8145,7 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "always-assert", "bitvec", @@ -8149,7 +8167,7 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "parity-scale-codec", "scale-info", @@ -8161,7 +8179,7 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "derive_more", "fatality", @@ -8186,7 +8204,7 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", @@ -8200,7 +8218,7 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "futures", "futures-timer", @@ -8220,7 +8238,7 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "always-assert", "async-trait", @@ -8244,7 +8262,7 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "futures", "parity-scale-codec", @@ -8262,7 +8280,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "bitvec", "derive_more", @@ -8291,7 +8309,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "bitvec", "futures", @@ -8311,7 +8329,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "bitvec", "fatality", @@ -8330,7 +8348,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "futures", "polkadot-node-subsystem", @@ -8345,7 +8363,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "async-trait", "futures", @@ -8364,7 +8382,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "futures", "polkadot-node-metrics", @@ -8379,7 +8397,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "futures", "futures-timer", @@ -8396,7 +8414,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "fatality", "futures", @@ -8415,7 +8433,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "async-trait", "futures", @@ -8432,7 +8450,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "bitvec", "fatality", @@ -8450,7 +8468,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "always-assert", "assert_matches", @@ -8482,7 +8500,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-checker" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "futures", "polkadot-node-primitives", @@ -8498,7 +8516,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "futures", "lru", @@ -8513,7 +8531,7 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "lazy_static", "log", @@ -8531,7 +8549,7 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "bs58", "futures", @@ -8550,7 +8568,7 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "async-trait", "derive_more", @@ -8573,7 +8591,7 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "bounded-vec", "futures", @@ -8595,7 +8613,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -8605,7 +8623,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-test-helpers" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "async-trait", "futures", @@ -8623,7 +8641,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "async-trait", "derive_more", @@ -8646,7 +8664,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "async-trait", "derive_more", @@ -8679,7 +8697,7 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "async-trait", "futures", @@ -8702,7 +8720,7 @@ dependencies = [ [[package]] name = "polkadot-parachain" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "derive_more", "frame-support", @@ -8799,7 +8817,7 @@ dependencies = [ [[package]] name = "polkadot-performance-test" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "env_logger 0.9.0", "kusama-runtime", @@ -8814,7 +8832,7 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "bitvec", "hex-literal", @@ -8840,7 +8858,7 @@ dependencies = [ [[package]] name = "polkadot-rpc" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", @@ -8872,7 +8890,7 @@ dependencies = [ [[package]] name = "polkadot-runtime" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "bitvec", "frame-benchmarking", @@ -8961,7 +8979,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "bitvec", "frame-benchmarking", @@ -9009,7 +9027,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-constants" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "frame-support", "polkadot-primitives", @@ -9023,7 +9041,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-metrics" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "bs58", "parity-scale-codec", @@ -9035,7 +9053,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "bitflags", "bitvec", @@ -9078,7 +9096,7 @@ dependencies = [ [[package]] name = "polkadot-service" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "async-trait", "beefy-gadget", @@ -9185,7 +9203,7 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "arrayvec 0.5.2", "fatality", @@ -9206,7 +9224,7 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -9216,7 +9234,7 @@ dependencies = [ [[package]] name = "polkadot-test-client" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "parity-scale-codec", "polkadot-node-subsystem", @@ -9241,7 +9259,7 @@ dependencies = [ [[package]] name = "polkadot-test-runtime" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "bitvec", "frame-election-provider-support", @@ -9302,7 +9320,7 @@ dependencies = [ [[package]] name = "polkadot-test-service" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "frame-benchmarking", "frame-system", @@ -10039,7 +10057,7 @@ dependencies = [ [[package]] name = "rococo-runtime" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "beefy-merkle-tree", "frame-benchmarking", @@ -10125,7 +10143,7 @@ dependencies = [ [[package]] name = "rococo-runtime-constants" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "frame-support", "polkadot-primitives", @@ -10349,7 +10367,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "log", "sp-core", @@ -10360,7 +10378,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "futures", @@ -10387,7 +10405,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "futures", "futures-timer", @@ -10410,7 +10428,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -10426,7 +10444,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "memmap2", "sc-chain-spec-derive", @@ -10441,7 +10459,7 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -10452,7 +10470,7 @@ dependencies = [ [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "array-bytes 4.2.0", "chrono", @@ -10492,7 +10510,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "fnv", "futures", @@ -10518,7 +10536,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "hash-db", "kvdb", @@ -10543,7 +10561,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "futures", @@ -10568,7 +10586,7 @@ dependencies = [ [[package]] name = "sc-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "futures", @@ -10597,7 +10615,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "fork-tree", @@ -10635,7 +10653,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "futures", "jsonrpsee", @@ -10657,7 +10675,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "fork-tree", "parity-scale-codec", @@ -10670,7 +10688,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "futures", @@ -10693,7 +10711,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "lru", "parity-scale-codec", @@ -10717,7 +10735,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", @@ -10730,7 +10748,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "log", "sc-allocator", @@ -10743,7 +10761,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "cfg-if", "libc", @@ -10760,7 +10778,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "ahash", "array-bytes 4.2.0", @@ -10800,7 +10818,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "finality-grandpa", "futures", @@ -10820,7 +10838,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "ansi_term", "futures", @@ -10835,7 +10853,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10850,7 +10868,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10892,7 +10910,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "cid", "futures", @@ -10911,7 +10929,7 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "bitflags", @@ -10937,7 +10955,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "ahash", "futures", @@ -10955,7 +10973,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10976,7 +10994,7 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -11008,7 +11026,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "array-bytes 4.2.0", "futures", @@ -11027,7 +11045,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "array-bytes 4.2.0", "bytes", @@ -11057,7 +11075,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "futures", "libp2p", @@ -11070,7 +11088,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -11079,7 +11097,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "futures", "jsonrpsee", @@ -11103,12 +11121,13 @@ dependencies = [ "sp-runtime", "sp-session", "sp-version", + "tokio", ] [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -11127,7 +11146,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "http", "jsonrpsee", @@ -11142,7 +11161,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "array-bytes 4.2.0", "futures", @@ -11168,7 +11187,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "directories", @@ -11233,7 +11252,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "log", "parity-scale-codec", @@ -11244,7 +11263,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -11263,7 +11282,7 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "futures", "libc", @@ -11282,7 +11301,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "chrono", "futures", @@ -11301,7 +11320,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "ansi_term", "atty", @@ -11332,7 +11351,7 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -11343,7 +11362,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "futures", @@ -11370,7 +11389,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "futures", @@ -11384,7 +11403,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "backtrace", "futures", @@ -11813,7 +11832,7 @@ checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" [[package]] name = "slot-range-helper" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "enumn", "parity-scale-codec", @@ -11890,7 +11909,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "hash-db", "log", @@ -11908,7 +11927,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "blake2", "proc-macro-crate", @@ -11920,7 +11939,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "scale-info", @@ -11933,7 +11952,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "integer-sqrt", "num-traits", @@ -11947,7 +11966,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "scale-info", @@ -11960,7 +11979,7 @@ dependencies = [ [[package]] name = "sp-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "parity-scale-codec", @@ -11972,7 +11991,7 @@ dependencies = [ [[package]] name = "sp-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "scale-info", @@ -11989,7 +12008,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "sp-api", @@ -12001,7 +12020,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "futures", "log", @@ -12019,7 +12038,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "futures", @@ -12037,7 +12056,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "parity-scale-codec", @@ -12055,7 +12074,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "merlin", @@ -12078,7 +12097,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "scale-info", @@ -12090,7 +12109,7 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "scale-info", @@ -12103,7 +12122,7 @@ dependencies = [ [[package]] name = "sp-core" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "array-bytes 4.2.0", "base58", @@ -12145,7 +12164,7 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "blake2", "byteorder", @@ -12159,7 +12178,7 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "proc-macro2", "quote", @@ -12170,7 +12189,7 @@ dependencies = [ [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -12179,7 +12198,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "proc-macro2", "quote", @@ -12189,7 +12208,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "environmental", "parity-scale-codec", @@ -12200,7 +12219,7 @@ dependencies = [ [[package]] name = "sp-finality-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "finality-grandpa", "log", @@ -12218,7 +12237,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -12232,7 +12251,7 @@ dependencies = [ [[package]] name = "sp-io" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "bytes", "ed25519", @@ -12257,7 +12276,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "lazy_static", "sp-core", @@ -12268,7 +12287,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "futures", @@ -12285,7 +12304,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "thiserror", "zstd", @@ -12294,7 +12313,7 @@ dependencies = [ [[package]] name = "sp-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "ckb-merkle-mountain-range", "log", @@ -12312,7 +12331,7 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "scale-info", @@ -12326,7 +12345,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "sp-api", "sp-core", @@ -12336,7 +12355,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "backtrace", "lazy_static", @@ -12346,7 +12365,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "rustc-hash", "serde", @@ -12356,7 +12375,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "either", "hash256-std-hasher", @@ -12378,7 +12397,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -12396,7 +12415,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "Inflector", "proc-macro-crate", @@ -12408,7 +12427,7 @@ dependencies = [ [[package]] name = "sp-serializer" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "serde", "serde_json", @@ -12417,7 +12436,7 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "scale-info", @@ -12431,7 +12450,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "scale-info", @@ -12443,7 +12462,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "hash-db", "log", @@ -12463,12 +12482,12 @@ dependencies = [ [[package]] name = "sp-std" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" [[package]] name = "sp-storage" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", @@ -12481,7 +12500,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "futures-timer", @@ -12496,7 +12515,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "sp-std", @@ -12508,7 +12527,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "sp-api", "sp-runtime", @@ -12517,7 +12536,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "log", @@ -12533,7 +12552,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "ahash", "hash-db", @@ -12556,7 +12575,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", @@ -12573,7 +12592,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -12584,7 +12603,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "impl-trait-for-tuples", "log", @@ -12597,7 +12616,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "parity-scale-codec", "scale-info", @@ -12918,7 +12937,7 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "platforms", ] @@ -12926,7 +12945,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "frame-system-rpc-runtime-api", "futures", @@ -12945,7 +12964,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "hyper", "log", @@ -12957,7 +12976,7 @@ dependencies = [ [[package]] name = "substrate-rpc-client" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "async-trait", "jsonrpsee", @@ -12970,7 +12989,7 @@ dependencies = [ [[package]] name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "jsonrpsee", "log", @@ -12989,7 +13008,7 @@ dependencies = [ [[package]] name = "substrate-test-client" version = "2.0.1" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -13015,7 +13034,7 @@ dependencies = [ [[package]] name = "substrate-test-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "futures", "substrate-test-utils-derive", @@ -13025,7 +13044,7 @@ dependencies = [ [[package]] name = "substrate-test-utils-derive" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -13036,7 +13055,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "ansi_term", "build-helper", @@ -13153,7 +13172,7 @@ checksum = "13a4ec180a2de59b57434704ccfad967f789b12737738798fa08798cd5824c16" [[package]] name = "test-runtime-constants" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "frame-support", "polkadot-primitives", @@ -13519,7 +13538,7 @@ dependencies = [ [[package]] name = "tracing-gum" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "polkadot-node-jaeger", "polkadot-primitives", @@ -13530,7 +13549,7 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "expander 0.0.6", "proc-macro-crate", @@ -13660,7 +13679,7 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cfe926282a284f7bdbf7b336eb78e80f51858577" dependencies = [ "clap 4.0.32", "frame-remote-externalities", @@ -14570,7 +14589,7 @@ dependencies = [ [[package]] name = "westend-runtime" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "bitvec", "frame-benchmarking", @@ -14661,7 +14680,7 @@ dependencies = [ [[package]] name = "westend-runtime-constants" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "frame-support", "polkadot-primitives", @@ -15052,7 +15071,7 @@ dependencies = [ [[package]] name = "xcm" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "derivative", "impl-trait-for-tuples", @@ -15068,7 +15087,7 @@ dependencies = [ [[package]] name = "xcm-builder" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "frame-support", "frame-system", @@ -15089,7 +15108,7 @@ dependencies = [ [[package]] name = "xcm-executor" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "environmental", "frame-benchmarking", @@ -15109,7 +15128,7 @@ dependencies = [ [[package]] name = "xcm-procedural" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#37468cac42343dc56e31f83e59da6d8ce12a02a7" dependencies = [ "Inflector", "proc-macro2", diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 27fef83fe87..f1baf91edf5 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -60,7 +60,7 @@ cp target/release/substrate-relay ~/local_bridge_testing/bin/substrate-relay # (Optional) 5. Build polkadot-parachain-mint binary with statemine/westmint for moving assets cd git checkout -b bko-transfer-asset-via-bridge --track origin/bko-transfer-asset-via-bridge -cargo build --release --locked -p polkadot-parachain@0.9.300 +cargo build --release --locked -p polkadot-parachain-bin cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain-mint ``` diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index 29bbed68cb6..c1c19b7c537 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -100,16 +100,16 @@ pub type XcmOriginToTransactDispatchOrigin = ( ); match_types! { - // TODO: map gov2 origins here - after merge https://github.com/paritytech/cumulus/pull/1895 - pub type ParentOrParentsExecutivePlurality: impl Contains = { + pub type ParentOrParentsPlurality: impl Contains = { MultiLocation { parents: 1, interior: Here } | - MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Executive, .. }) } + MultiLocation { parents: 1, interior: X1(Plurality { .. }) } }; pub type ParentOrSiblings: impl Contains = { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(_) } }; } + /// A call filter for the XCM Transact instruction. This is a temporary measure until we properly /// account for proof size weights. /// @@ -163,8 +163,8 @@ pub type Barrier = DenyThenTry< ( // Allow anything to pay for execution. AllowTopLevelPaidExecutionFrom, - // Parent and its exec plurality get free execution. - AllowExplicitUnpaidExecutionFrom, + // Parent and its plurality (i.e. governance bodies) gets free execution. + AllowExplicitUnpaidExecutionFrom, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, ), diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 689e9fa403c..5fe796f1cd0 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -75,6 +75,7 @@ parachains-common = { path = "../../../../parachains/common", default-features = bp-bridge-hub-rococo = { path = "../../../../bridges/primitives/chain-bridge-hub-rococo", default-features = false } bp-bridge-hub-wococo = { path = "../../../../bridges/primitives/chain-bridge-hub-wococo", default-features = false } bp-messages = { path = "../../../../bridges/primitives/messages", default-features = false } +bp-parachains = { path = "../../../../bridges/primitives/parachains", default-features = false } bp-runtime = { path = "../../../../bridges/primitives/runtime", default-features = false } bp-rococo = { path = "../../../../bridges/primitives/chain-rococo", default-features = false } bp-wococo = { path = "../../../../bridges/primitives/chain-wococo", default-features = false } @@ -95,6 +96,7 @@ std = [ "bp-bridge-hub-rococo/std", "bp-bridge-hub-wococo/std", "bp-messages/std", + "bp-parachains/std", "bp-runtime/std", "bp-rococo/std", "bp-wococo/std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index a035e3554c6..0cc7c119e55 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -21,22 +21,15 @@ use bp_messages::{ }; use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, Chain}; use codec::{Decode, Encode}; -use frame_support::{dispatch::Weight, parameter_types, CloneNoBound, EqNoBound, PartialEqNoBound}; +use frame_support::{dispatch::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; use scale_info::TypeInfo; use xcm::latest::prelude::*; -use xcm_builder::{DispatchBlob, DispatchBlobError, HaulBlob}; +use xcm_builder::{DispatchBlob, DispatchBlobError, HaulBlob, HaulBlobError}; // TODO:check-parameter - we could possibly use BridgeMessage from xcm:v3 stuff /// PLain "XCM" payload, which we transfer through bridge pub type XcmAsPlainPayload = sp_std::prelude::Vec; -// TODO:check-parameter -parameter_types! { - pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; - pub const MaxRequests: u32 = 64; - pub const HeadersToKeep: u32 = 1024; -} - #[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] pub enum XcmBlobMessageDispatchResult { InvalidPayload, @@ -87,7 +80,6 @@ impl(sp_std::marker::PhantomData); impl HaulBlob for XcmBlobHaulerAdapter { - fn haul_blob(blob: sp_std::prelude::Vec) { + fn haul_blob(blob: sp_std::prelude::Vec) -> Result<(), HaulBlobError> { let lane = H::xcm_lane(); let result = H::MessageSender::send_message( pallet_xcm::Origin::from(MultiLocation::from(H::message_sender_origin())).into(), lane, blob, ); - let result = result - .map(|artifacts| { - let hash = (lane, artifacts.nonce).using_encoded(sp_io::hashing::blake2_256); - hash - }) - .map_err(|e| e); + let result = result.map(|artifacts| { + let hash = (lane, artifacts.nonce).using_encoded(sp_io::hashing::blake2_256); + hash + }); log::info!(target: crate::LOG_TARGET, "haul_blob result: {:?} on lane: {:?}", result, lane); - result.expect("failed to process: TODO:check-parameter - wait for origin/gav-xcm-v3, there is a comment about handliing errors for HaulBlob"); + result.map(|_| ()).map_err(|_| HaulBlobError::Transport("MessageSenderError")) } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index c7506e6d74a..47131b0d565 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -73,7 +73,7 @@ impl XcmBlobHauler for ToBridgeHubWococoXcmBlobHauler { DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO } } -const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO: LaneId = [0, 0, 0, 1]; +const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO: LaneId = LaneId([0, 0, 0, 1]); /// Messaging Bridge configuration for BridgeHubRococo -> BridgeHubWococo pub struct WithBridgeHubWococoMessageBridge; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 01759671382..a7077734e15 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -73,7 +73,7 @@ impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO } } -const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO: LaneId = [0, 0, 0, 1]; +const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO: LaneId = LaneId([0, 0, 0, 1]); /// Messaging Bridge configuration for BridgeHubWococo -> BridgeHubRococo pub struct WithBridgeHubRococoMessageBridge; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/constants.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/constants.rs index 549e549d38a..b2d57224025 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/constants.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/constants.rs @@ -22,6 +22,7 @@ pub mod currency { pub const UNITS: Balance = constants::currency::UNITS; pub const CENTS: Balance = constants::currency::CENTS; + pub const MILLICENTS: Balance = constants::currency::MILLICENTS; pub const fn deposit(items: u32, bytes: u32) -> Balance { // map to 1/100 of what the rococo relay chain charges diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 4c63a1318c8..12dbd4f869a 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -63,6 +63,7 @@ pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; use xcm_config::{XcmConfig, XcmOriginToTransactDispatchOrigin}; +use bp_parachains::SingleParaStoredHeaderDataBuilder; use bp_runtime::{HeaderId, HeaderIdProvider}; #[cfg(any(feature = "std", test))] @@ -263,7 +264,7 @@ impl pallet_balances::Config for Runtime { parameter_types! { /// Relay Chain `TransactionByteFee` / 10 - pub const TransactionByteFee: Balance = 10 * MICROUNIT; + pub const TransactionByteFee: Balance = 1 * MILLICENTS; } impl pallet_transaction_payment::Config for Runtime { @@ -392,10 +393,9 @@ pub type BridgeGrandpaWococoInstance = pallet_bridge_grandpa::Instance1; impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_wococo::Wococo; type MaxRequests = MaxRequests; - type HeadersToKeep = HeadersToKeep; + type HeadersToKeep = RelayChainHeadersToKeep; type MaxBridgedAuthorities = frame_support::traits::ConstU32<{ bp_wococo::MAX_AUTHORITIES_COUNT }>; - type MaxBridgedHeaderSize = frame_support::traits::ConstU32<{ bp_wococo::MAX_HEADER_SIZE }>; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } @@ -404,19 +404,23 @@ pub type BridgeGrandpaRococoInstance = pallet_bridge_grandpa::Instance2; impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_rococo::Rococo; type MaxRequests = MaxRequests; - type HeadersToKeep = HeadersToKeep; + type HeadersToKeep = RelayChainHeadersToKeep; type MaxBridgedAuthorities = frame_support::traits::ConstU32<{ bp_rococo::MAX_AUTHORITIES_COUNT }>; - type MaxBridgedHeaderSize = frame_support::traits::ConstU32<{ bp_rococo::MAX_HEADER_SIZE }>; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } pub const ROCOCO_BRIDGE_PARA_PALLET_NAME: &str = "Paras"; pub const WOCOCO_BRIDGE_PARA_PALLET_NAME: &str = "Paras"; parameter_types! { + pub const RelayChainHeadersToKeep: u32 = 1024; pub const ParachainHeadsToKeep: u32 = 64; + pub const MaxRequests: u32 = 64; + pub const RococoBridgeParachainPalletName: &'static str = ROCOCO_BRIDGE_PARA_PALLET_NAME; pub const WococoBridgeParachainPalletName: &'static str = WOCOCO_BRIDGE_PARA_PALLET_NAME; + pub const MaxRococoParaHeadDataSize: u32 = bp_rococo::MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE; + pub const MaxWococoParaHeadDataSize: u32 = bp_wococo::MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE; } /// Add parachain bridge pallet to track Wococo bridge hub parachain @@ -426,10 +430,10 @@ impl pallet_bridge_parachains::Config for Runtime type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; type BridgesGrandpaPalletInstance = BridgeGrandpaWococoInstance; type ParasPalletName = WococoBridgeParachainPalletName; - type TrackedParachains = Everything; + type ParaStoredHeaderDataBuilder = + SingleParaStoredHeaderDataBuilder; type HeadsToKeep = ParachainHeadsToKeep; - type MaxParaHeadSize = - frame_support::traits::ConstU32<{ bp_wococo::MAX_NESTED_PARACHAIN_HEAD_SIZE }>; + type MaxParaHeadDataSize = MaxWococoParaHeadDataSize; } /// Add parachain bridge pallet to track Rococo bridge hub parachain @@ -439,10 +443,10 @@ impl pallet_bridge_parachains::Config for Runtime type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; type BridgesGrandpaPalletInstance = BridgeGrandpaRococoInstance; type ParasPalletName = RococoBridgeParachainPalletName; - type TrackedParachains = Everything; + type ParaStoredHeaderDataBuilder = + SingleParaStoredHeaderDataBuilder; type HeadsToKeep = ParachainHeadsToKeep; - type MaxParaHeadSize = - frame_support::traits::ConstU32<{ bp_rococo::MAX_NESTED_PARACHAIN_HEAD_SIZE }>; + type MaxParaHeadDataSize = MaxRococoParaHeadDataSize; } /// Add XCM messages support for BrigdeHubRococo to support Rococo->Wococo XCM messages @@ -463,10 +467,13 @@ impl pallet_bridge_messages::Config for Run type InboundPayload = XcmAsPlainPayload; type InboundRelayer = AccountId; + // TODO:check-parameter - check delivery + type DeliveryPayments = (); type TargetHeaderChain = bridge_hub_rococo_config::BridgeHubWococo; type LaneMessageVerifier = bridge_hub_rococo_config::ToBridgeHubWococoMessageVerifier; - type MessageDeliveryAndDispatchPayment = (); + // TODO:check-parameter - check delivery + type DeliveryConfirmationPayments = (); type SourceHeaderChain = bridge_hub_rococo_config::BridgeHubWococo; type MessageDispatch = XcmBlobMessageDispatch< @@ -494,10 +501,13 @@ impl pallet_bridge_messages::Config for Run type InboundPayload = XcmAsPlainPayload; type InboundRelayer = AccountId; + // TODO:check-parameter - check delivery + type DeliveryPayments = (); type TargetHeaderChain = bridge_hub_wococo_config::BridgeHubRococo; type LaneMessageVerifier = bridge_hub_wococo_config::ToBridgeHubRococoMessageVerifier; - type MessageDeliveryAndDispatchPayment = (); + // TODO:check-parameter - check delivery + type DeliveryConfirmationPayments = (); type SourceHeaderChain = bridge_hub_wococo_config::BridgeHubRococo; type MessageDispatch = XcmBlobMessageDispatch< @@ -541,7 +551,8 @@ construct_runtime!( // Handy utilities. Utility: pallet_utility::{Pallet, Call, Event} = 40, - Multisig: pallet_multisig::{Pallet, Call, Storage, Event} = 41, + // TODO:check-parameter - change back to 41 a align bridge pallets + Multisig: pallet_multisig::{Pallet, Call, Storage, Event} = 36, // Wococo bridge modules BridgeWococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Config} = 41, @@ -698,13 +709,13 @@ impl_runtime_apis! { impl bp_rococo::RococoFinalityApi for Runtime { fn best_finalized() -> Option> { - BridgeRococoGrandpa::best_finalized().map(|header| header.id()) + BridgeRococoGrandpa::best_finalized() } } impl bp_wococo::WococoFinalityApi for Runtime { fn best_finalized() -> Option> { - BridgeWococoGrandpa::best_finalized().map(|header| header.id()) + BridgeWococoGrandpa::best_finalized() } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 64b640a51a2..dcad27b36ed 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -15,8 +15,8 @@ // along with Cumulus. If not, see . use super::{ - AccountId, AllPalletsWithSystem, Balance, Balances, ParachainInfo, ParachainSystem, - PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, }; use crate::{ bridge_hub_rococo_config::ToBridgeHubWococoHaulBlobExporter, @@ -25,9 +25,6 @@ use crate::{ use frame_support::{ match_types, parameter_types, traits::{ConstU32, Contains, Everything, Nothing}, - weights::IdentityFee, - AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, - Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, }; use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; @@ -36,7 +33,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, - CurrencyAdapter, EnsureXcmOrigin, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, + CurrencyAdapter, EnsureXcmOrigin, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, WithComputedOrigin, @@ -50,14 +47,12 @@ use parachains_common::xcm_config::{ ConcreteNativeAssetFrom, DenyReserveTransferToRelayChain, DenyThenTry, }; use polkadot_runtime_common::impls::ToAuthor; -use xcm::latest::prelude::*; -use xcm_executor::traits::WithOriginFilter; parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = - X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); + X2(GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } @@ -192,8 +187,8 @@ pub type Barrier = DenyThenTry< ( // Allow anything to pay for execution. AllowTopLevelPaidExecutionFrom, - // Parent and its exec plurality get free execution. - AllowExplicitUnpaidExecutionFrom, + // Parent and its plurality (i.e. governance bodies) gets free execution. + AllowExplicitUnpaidExecutionFrom, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, ), diff --git a/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/polkadot-parachain/src/chain_spec/bridge_hubs.rs index 2aac0c8da9e..031500fed56 100644 --- a/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -17,7 +17,7 @@ use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed}; use cumulus_primitives_core::ParaId; use parachains_common::Balance as BridgeHubBalance; -use sc_chain_spec::{ChainSpec, ChainType}; +use sc_chain_spec::ChainSpec; use sc_cli::RuntimeVersion; use sp_core::sr25519; use std::{path::PathBuf, str::FromStr}; @@ -115,14 +115,6 @@ impl BridgeHubRuntimeType { Some("Bob".to_string()), |_| (), ))), - BridgeHubRuntimeType::RococoLocal => Ok(Box::new(rococo::local_config( - rococo::BRIDGE_HUB_ROCOCO_LOCAL, - "Rococo BridgeHub Local", - "rococo-local", - ParaId::new(1013), - Some("Bob".to_string()), - |_| (), - ))), BridgeHubRuntimeType::RococoDevelopment => Ok(Box::new(rococo::local_config( rococo::BRIDGE_HUB_ROCOCO_DEVELOPMENT, "Rococo BridgeHub Development", @@ -142,13 +134,6 @@ impl BridgeHubRuntimeType { ParaId::new(1014), Some("Bob".to_string()), ))), - BridgeHubRuntimeType::WococoLocal => Ok(Box::new(wococo::default_config( - wococo::BRIDGE_HUB_WOCOCO_LOCAL, - "Wococo BridgeHub Local", - ChainType::Local, - "wococo-local", - ParaId::new(1014), - ))), } } @@ -204,66 +189,6 @@ pub mod rococo { pub type RuntimeApi = bridge_hub_rococo_runtime::RuntimeApi; - pub fn live_config( - id: &str, - chain_name: &str, - relay_chain: &str, - para_id: ParaId, - modify_props: ModifyProperties, - ) -> BridgeHubChainSpec { - // Rococo defaults - let mut properties = sc_chain_spec::Properties::new(); - properties.insert("ss58Format".into(), 42.into()); - properties.insert("tokenSymbol".into(), "ROC".into()); - properties.insert("tokenDecimals".into(), 12.into()); - modify_props(&mut properties); - - BridgeHubChainSpec::from_genesis( - // Name - chain_name, - // ID - super::ensure_id(id).expect("invalid id"), - ChainType::Live, - move || { - genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - para_id, - None, - ) - }, - Vec::new(), - None, - None, - None, - Some(properties), - Extensions { relay_chain: relay_chain.to_string(), para_id: para_id.into() }, - ) - } - pub fn local_config( id: &str, chain_name: &str, @@ -390,7 +315,6 @@ pub mod rococo { pub mod wococo { use super::ParaId; use crate::chain_spec::bridge_hubs::rococo; - use sc_chain_spec::ChainType; pub(crate) const BRIDGE_HUB_WOCOCO: &str = "bridge-hub-wococo"; pub(crate) const BRIDGE_HUB_WOCOCO_LOCAL: &str = "bridge-hub-wococo-local"; @@ -416,17 +340,6 @@ pub mod wococo { }, ) } - - pub fn live_config( - id: &str, - chain_name: &str, - relay_chain: &str, - para_id: ParaId, - ) -> BridgeHubChainSpec { - rococo::live_config(id, chain_name, relay_chain, para_id, |properties| { - properties.insert("tokenSymbol".into(), "WOOK".into()); - }) - } } /// Sub-module for Kusama setup From 4496728b3e7e78ebdb4442b102a5c5ac6c9d51db Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 19 Jan 2023 00:47:27 +0100 Subject: [PATCH 177/263] Fixes --- .../bridge-hub-rococo/src/bridge_common_config.rs | 1 - parachains/runtimes/bridge-hubs/test-utils/src/lib.rs | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 0cc7c119e55..0fcfdba10da 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -26,7 +26,6 @@ use scale_info::TypeInfo; use xcm::latest::prelude::*; use xcm_builder::{DispatchBlob, DispatchBlobError, HaulBlob, HaulBlobError}; -// TODO:check-parameter - we could possibly use BridgeMessage from xcm:v3 stuff /// PLain "XCM" payload, which we transfer through bridge pub type XcmAsPlainPayload = sp_std::prelude::Vec; diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs index ec438829da3..ff9acd36f68 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs @@ -16,7 +16,7 @@ use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData}, - MessageKey, + LaneId, MessageKey, }; use cumulus_primitives_core::{AbridgedHrmpChannel, ParaId, PersistedValidationData}; use cumulus_primitives_parachain_inherent::ParachainInherentData; @@ -30,7 +30,7 @@ use frame_support::{ use parachains_common::AccountId; use polkadot_parachain::primitives::{HrmpChannelId, RelayChainBlockNumber}; use xcm::{latest::prelude::*, prelude::XcmVersion}; -use xcm_builder::{HaulBlob, HaulBlobExporter}; +use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; use xcm_executor::traits::{validate_export, ExportXcm}; /// Dummy xcm @@ -40,7 +40,7 @@ pub fn dummy_xcm() -> Xcm<()> { pub fn wrap_as_dispatch_message(payload: Vec) -> DispatchMessage> { DispatchMessage { - key: MessageKey { lane_id: [0, 0, 0, 0], nonce: 1 }, + key: MessageKey { lane_id: LaneId([0, 0, 0, 0]), nonce: 1 }, data: DispatchMessageData { payload: Ok(payload) }, } } @@ -59,8 +59,9 @@ macro_rules! grab_haul_blob ( struct $name; impl HaulBlob for $name { - fn haul_blob(blob: Vec) { + fn haul_blob(blob: Vec) -> Result<(), HaulBlobError>{ $grabbed_payload.with(|rm| *rm.borrow_mut() = Some(blob)); + Ok(()) } } } From e845023305c80c911a10ecd1b68de0705e4e9aaf Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Thu, 19 Jan 2023 11:07:38 +0200 Subject: [PATCH 178/263] Use auto runtime version for local runs (#2113) --- parachains/runtimes/bridge-hubs/README.md | 8 ++++++++ scripts/bridges_rococo_wococo.sh | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index f1baf91edf5..a39d6e8dc91 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -104,8 +104,10 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay init-bridge rococo-to-bridge-hub-wococo \ --source-host localhost \ --source-port 9942 \ + --source-version-mode Auto \ --target-host localhost \ --target-port 8945 \ + --target-version-mode Auto \ --target-signer //Bob # Wococo -> Rococo @@ -113,8 +115,10 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay init-bridge wococo-to-bridge-hub-rococo \ --source-host localhost \ --source-port 9945 \ + --source-version-mode Auto \ --target-host localhost \ --target-port 8943 \ + --target-version-mode Auto \ --target-signer //Bob # 2. Relay relay-chain headers, parachain headers and messages** @@ -122,16 +126,20 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay relay-headers-and-messages bridge-hub-rococo-bridge-hub-wococo \ --rococo-host localhost \ --rococo-port 9942 \ + --rococo-version-mode Auto \ --bridge-hub-rococo-host localhost \ --bridge-hub-rococo-port 8943 \ + --bridge-hub-rococo-version-mode Auto \ --bridge-hub-rococo-signer //Charlie \ --wococo-headers-to-bridge-hub-rococo-signer //Bob \ --wococo-parachains-to-bridge-hub-rococo-signer //Bob \ --bridge-hub-rococo-transactions-mortality 4 \ --wococo-host localhost \ --wococo-port 9945 \ + --wococo-version-mode Auto \ --bridge-hub-wococo-host localhost \ --bridge-hub-wococo-port 8945 \ + --bridge-hub-wococo-version-mode Auto \ --bridge-hub-wococo-signer //Charlie \ --rococo-headers-to-bridge-hub-wococo-signer //Bob \ --rococo-parachains-to-bridge-hub-wococo-signer //Bob \ diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh index 1491db61394..c025093d709 100755 --- a/scripts/bridges_rococo_wococo.sh +++ b/scripts/bridges_rococo_wococo.sh @@ -218,8 +218,10 @@ function init_ro_wo() { ~/local_bridge_testing/bin/substrate-relay init-bridge rococo-to-bridge-hub-wococo \ --source-host localhost \ --source-port 9942 \ + --source-version-mode Auto \ --target-host localhost \ --target-port 8945 \ + --target-version-mode Auto \ --target-signer //Bob } @@ -230,8 +232,10 @@ function init_wo_ro() { ~/local_bridge_testing/bin/substrate-relay init-bridge wococo-to-bridge-hub-rococo \ --source-host localhost \ --source-port 9945 \ + --source-version-mode Auto \ --target-host localhost \ --target-port 8943 \ + --target-version-mode Auto \ --target-signer //Bob } @@ -242,16 +246,20 @@ function run_relay() { ~/local_bridge_testing/bin/substrate-relay relay-headers-and-messages bridge-hub-rococo-bridge-hub-wococo \ --rococo-host localhost \ --rococo-port 9942 \ + --rococo-version-mode Auto \ --bridge-hub-rococo-host localhost \ --bridge-hub-rococo-port 8943 \ + --bridge-hub-rococo-version-mode Auto \ --bridge-hub-rococo-signer //Charlie \ --wococo-headers-to-bridge-hub-rococo-signer //Bob \ --wococo-parachains-to-bridge-hub-rococo-signer //Bob \ --bridge-hub-rococo-transactions-mortality 4 \ --wococo-host localhost \ --wococo-port 9945 \ + --wococo-version-mode Auto \ --bridge-hub-wococo-host localhost \ --bridge-hub-wococo-port 8945 \ + --bridge-hub-wococo-version-mode Auto \ --bridge-hub-wococo-signer //Charlie \ --rococo-headers-to-bridge-hub-wococo-signer //Bob \ --rococo-parachains-to-bridge-hub-wococo-signer //Bob \ From 677abb1e8972ff4680e722be9d6d07d93ddfa135 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 19 Jan 2023 11:20:37 +0100 Subject: [PATCH 179/263] Squashed 'bridges/' changes from 984749ba0..fb3c5ef5d fb3c5ef5d Add integrity check for signed extensions (#1780) 3959628ff add try-runtime feature to pallets (#1779) be36ff00c Default impl for some methods in messages benchmarking pallet config (#1777) 68344e329 Relayer reward metric (#1742) 6b455597b Crate-level documentation on finality relays and relayers pallet (#1773) git-subtree-dir: bridges git-subtree-split: fb3c5ef5dae42553522c7eff37678de9bf4f6c67 --- Cargo.lock | 4 + bin/millau/runtime/src/lib.rs | 19 +- bin/rialto-parachain/runtime/Cargo.toml | 3 + bin/rialto-parachain/runtime/src/lib.rs | 26 ++- bin/runtime-common/src/integrity.rs | 13 ++ .../rialto-millau-maintenance-dashboard.json | 218 +++--------------- ...arachain-millau-maintenance-dashboard.json | 204 ++++++---------- docs/high-level-overview.md | 8 +- modules/beefy/Cargo.toml | 4 + modules/grandpa/Cargo.toml | 4 + modules/messages/Cargo.toml | 4 + modules/messages/src/benchmarking.rs | 39 +++- modules/parachains/Cargo.toml | 4 + modules/relayers/Cargo.toml | 6 + modules/relayers/README.md | 14 ++ modules/relayers/src/lib.rs | 17 +- modules/shift-session-manager/Cargo.toml | 4 + modules/shift-session-manager/README.md | 10 + .../chain-bridge-hub-cumulus/src/lib.rs | 8 +- primitives/chain-rialto-parachain/Cargo.toml | 1 + primitives/chain-rialto-parachain/src/lib.rs | 2 + primitives/polkadot-core/src/lib.rs | 8 +- primitives/relayers/Cargo.toml | 2 + primitives/relayers/src/lib.rs | 22 +- primitives/runtime/src/extensions.rs | 4 +- .../src/cli/relay_headers_and_messages/mod.rs | 26 ++- relays/client-bridge-hub-rococo/src/lib.rs | 3 +- .../src/runtime_wrapper.rs | 4 +- relays/client-bridge-hub-wococo/src/lib.rs | 3 +- .../src/runtime_wrapper.rs | 4 +- relays/client-millau/src/lib.rs | 2 + relays/client-rialto-parachain/src/lib.rs | 8 +- relays/client-rialto/src/lib.rs | 2 + relays/client-substrate/src/chain.rs | 10 + relays/finality/README.md | 58 +++++ relays/lib-substrate-relay/Cargo.toml | 2 + .../src/messages_metrics.rs | 59 ++++- relays/parachains/README.md | 49 ++++ 38 files changed, 490 insertions(+), 388 deletions(-) create mode 100644 modules/relayers/README.md create mode 100644 modules/shift-session-manager/README.md create mode 100644 relays/finality/README.md create mode 100644 relays/parachains/README.md diff --git a/Cargo.lock b/Cargo.lock index bdbfa407da9..acc05e76643 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -944,6 +944,7 @@ version = "0.1.0" dependencies = [ "bp-messages", "bp-rialto", + "bp-runtime", "frame-support", "hex", "hex-literal", @@ -970,6 +971,7 @@ name = "bp-rialto-parachain" version = "0.1.0" dependencies = [ "bp-messages", + "bp-polkadot-core", "bp-runtime", "frame-support", "frame-system", @@ -12401,6 +12403,7 @@ dependencies = [ "bp-millau", "bp-parachains", "bp-polkadot-core", + "bp-relayers", "bp-rialto", "bp-rococo", "bp-runtime", @@ -12411,6 +12414,7 @@ dependencies = [ "frame-support", "frame-system", "futures", + "hex", "log", "messages-relay", "num-traits", diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index dbff7f5940c..6c401b9a94b 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -1001,21 +1001,6 @@ impl_runtime_apis! { use rialto_messages::WithRialtoMessageBridge; impl MessagesConfig for Runtime { - fn bridged_relayer_id() -> Self::InboundRelayer { - [0u8; 32].into() - } - - fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { - pallet_bridge_relayers::Pallet::::relayer_reward(relayer, &Self::bench_lane_id()).is_some() - } - - fn endow_account(account: &Self::AccountId) { - pallet_balances::Pallet::::make_free_balance_be( - account, - Balance::MAX / 100, - ); - } - fn prepare_message_proof( params: MessageProofParams, ) -> (rialto_messages::FromRialtoMessagesProof, Weight) { @@ -1032,8 +1017,8 @@ impl_runtime_apis! { ) } - fn is_message_dispatched(_nonce: bp_messages::MessageNonce) -> bool { - true + fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { + pallet_bridge_relayers::Pallet::::relayer_reward(relayer, &Self::bench_lane_id()).is_some() } } diff --git a/bin/rialto-parachain/runtime/Cargo.toml b/bin/rialto-parachain/runtime/Cargo.toml index 2327a864c2c..b91e1cd2751 100644 --- a/bin/rialto-parachain/runtime/Cargo.toml +++ b/bin/rialto-parachain/runtime/Cargo.toml @@ -75,6 +75,9 @@ xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +[dev-dependencies] +bridge-runtime-common = { path = "../../runtime-common", features = ["integrity-test"] } + [features] default = ['std'] runtime-benchmarks = [ diff --git a/bin/rialto-parachain/runtime/src/lib.rs b/bin/rialto-parachain/runtime/src/lib.rs index b9f4c236d86..5ad8506acbf 100644 --- a/bin/rialto-parachain/runtime/src/lib.rs +++ b/bin/rialto-parachain/runtime/src/lib.rs @@ -848,8 +848,11 @@ mod tests { LaneId, MessageKey, }; use bp_runtime::messages::MessageDispatchResult; - use bridge_runtime_common::messages::target::FromBridgedChainMessageDispatch; + use bridge_runtime_common::{ + integrity::check_additional_signed, messages::target::FromBridgedChainMessageDispatch, + }; use codec::Encode; + use sp_runtime::generic::Era; fn new_test_ext() -> sp_io::TestExternalities { sp_io::TestExternalities::new( @@ -909,4 +912,25 @@ mod tests { ); }) } + + #[test] + fn ensure_signed_extension_definition_is_correct() { + let payload: SignedExtra = ( + frame_system::CheckNonZeroSender::new(), + frame_system::CheckSpecVersion::new(), + frame_system::CheckTxVersion::new(), + frame_system::CheckGenesis::new(), + frame_system::CheckEra::from(Era::Immortal), + frame_system::CheckNonce::from(10), + frame_system::CheckWeight::new(), + pallet_transaction_payment::ChargeTransactionPayment::from(10), + ); + let indirect_payload = bp_rialto_parachain::SignedExtension::new( + ((), (), (), (), Era::Immortal, 10.into(), (), 10.into()), + None, + ); + assert_eq!(payload.encode(), indirect_payload.encode()); + + check_additional_signed::(); + } } diff --git a/bin/runtime-common/src/integrity.rs b/bin/runtime-common/src/integrity.rs index 9c4553ad136..e8e3e7f87cc 100644 --- a/bin/runtime-common/src/integrity.rs +++ b/bin/runtime-common/src/integrity.rs @@ -26,6 +26,7 @@ use bp_runtime::{Chain, ChainId}; use codec::Encode; use frame_support::{storage::generator::StorageValue, traits::Get}; use frame_system::limits; +use sp_runtime::traits::SignedExtension; /// Macro that ensures that the runtime configuration and chain primitives crate are sharing /// the same types (index, block number, hash, hasher, account id and header). @@ -319,3 +320,15 @@ pub fn check_message_lane_weights( this_chain_max_unconfirmed_messages, ); } + +/// Check that the `AdditionalSigned` type of a wrapped runtime is the same as the one of the +/// corresponding actual runtime. +/// +/// This method doesn't perform any `assert`. If the condition is not true it will generate a +/// compile-time error. +pub fn check_additional_signed() +where + SignedExt: SignedExtension, + IndirectSignedExt: SignedExtension, +{ +} diff --git a/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json b/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json index ecb06074d7f..4f134914f70 100644 --- a/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json +++ b/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json @@ -8,14 +8,22 @@ "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, "type": "dashboard" } ] }, "editable": true, + "fiscalYearStartMonth": 0, "gnetId": null, "graphTooltip": 0, "links": [], + "liveNow": false, "panels": [ { "alert": { @@ -66,29 +74,6 @@ "type": "last" }, "type": "query" - }, - { - "evaluator": { - "params": [ - 1000 - ], - "type": "lt" - }, - "operator": { - "type": "or" - }, - "query": { - "params": [ - "C", - "5m", - "now" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" } ], "executionErrorState": "alerting", @@ -104,19 +89,13 @@ "dashLength": 10, "dashes": false, "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 8, "w": 12, "x": 0, - "y": 16 + "y": 0 }, "hiddenSeries": false, "id": 8, @@ -132,8 +111,11 @@ "lines": true, "linewidth": 1, "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.3", + "pluginVersion": "8.2.6", "pointradius": 2, "points": false, "renderer": "flot", @@ -161,7 +143,8 @@ "fill": true, "line": true, "op": "lt", - "value": 1000 + "value": 1000, + "visible": true } ], "timeFrom": null, @@ -253,29 +236,6 @@ "type": "last" }, "type": "query" - }, - { - "evaluator": { - "params": [ - 1000 - ], - "type": "lt" - }, - "operator": { - "type": "or" - }, - "query": { - "params": [ - "C", - "5m", - "now" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" } ], "executionErrorState": "alerting", @@ -291,19 +251,13 @@ "dashLength": 10, "dashes": false, "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 8, "w": 12, "x": 12, - "y": 16 + "y": 0 }, "hiddenSeries": false, "id": 9, @@ -319,8 +273,11 @@ "lines": true, "linewidth": 1, "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.3", + "pluginVersion": "8.2.6", "pointradius": 2, "points": false, "renderer": "flot", @@ -348,7 +305,8 @@ "fill": true, "line": true, "op": "lt", - "value": 1000 + "value": 1000, + "visible": true } ], "timeFrom": null, @@ -432,19 +390,13 @@ "dashLength": 10, "dashes": false, "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 8, "w": 12, "x": 0, - "y": 24 + "y": 8 }, "hiddenSeries": false, "id": 11, @@ -460,8 +412,11 @@ "lines": true, "linewidth": 1, "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.3", + "pluginVersion": "8.2.6", "pointradius": 2, "points": false, "renderer": "flot", @@ -483,7 +438,8 @@ "fill": true, "line": true, "op": "gt", - "value": 0 + "value": 0, + "visible": true } ], "timeFrom": null, @@ -567,19 +523,13 @@ "dashLength": 10, "dashes": false, "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 8, "w": 12, "x": 12, - "y": 24 + "y": 8 }, "hiddenSeries": false, "id": 12, @@ -595,8 +545,11 @@ "lines": true, "linewidth": 1, "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.3", + "pluginVersion": "8.2.6", "pointradius": 2, "points": false, "renderer": "flot", @@ -618,7 +571,8 @@ "fill": true, "line": true, "op": "gt", - "value": 0 + "value": 0, + "visible": true } ], "timeFrom": null, @@ -660,108 +614,10 @@ "align": false, "alignLevel": null } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 32 - }, - "hiddenSeries": false, - "id": 14, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "percentage": false, - "pluginVersion": "7.1.3", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "Millau_to_Rialto_MessageLane_00000000_unprofitable_delivery_transactions", - "interval": "", - "legendFormat": "Millau -> Rialto, lane 00000000", - "refId": "A" - }, - { - "expr": "Rialto_to_Millau_MessageLane_00000000_unprofitable_delivery_transactions", - "interval": "", - "legendFormat": "Rialto -> Millau, lane 00000000", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Count of unprofitable message delivery transactions", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } } ], "refresh": "10s", - "schemaVersion": 26, + "schemaVersion": 32, "style": "dark", "tags": [], "templating": { diff --git a/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json index 0da5b8cb4dc..b54bf1b48c5 100644 --- a/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json +++ b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json @@ -8,14 +8,22 @@ "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, "type": "dashboard" } ] }, "editable": true, + "fiscalYearStartMonth": 0, "gnetId": null, "graphTooltip": 0, "links": [], + "liveNow": false, "panels": [ { "alert": { @@ -43,52 +51,6 @@ "type": "last" }, "type": "query" - }, - { - "evaluator": { - "params": [ - 1000 - ], - "type": "lt" - }, - "operator": { - "type": "or" - }, - "query": { - "params": [ - "B", - "5m", - "now" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" - }, - { - "evaluator": { - "params": [ - 1000 - ], - "type": "lt" - }, - "operator": { - "type": "or" - }, - "query": { - "params": [ - "C", - "5m", - "now" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" } ], "executionErrorState": "alerting", @@ -104,19 +66,13 @@ "dashLength": 10, "dashes": false, "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 8, "w": 12, "x": 0, - "y": 16 + "y": 0 }, "hiddenSeries": false, "id": 10, @@ -132,8 +88,11 @@ "lines": true, "linewidth": 1, "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.3", + "pluginVersion": "8.2.6", "pointradius": 2, "points": false, "renderer": "flot", @@ -143,16 +102,29 @@ "steppedLine": false, "targets": [ { - "expr": "at_RialtoParachain_relay_MillauHeaders_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "exemplar": true, + "expr": "at_RialtoParachain_relay_MillauMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "instant": false, "interval": "", - "legendFormat": "With-Millau headers relay account balance", + "legendFormat": "With-Millau relay account balance", "refId": "A" }, { - "expr": "at_RialtoParachain_relay_MillauMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "exemplar": true, + "expr": "at_RialtoParachain_relay_MillauMessages_reward_for_lane_00000000_with_Millau{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "hide": false, + "instant": false, "interval": "", - "legendFormat": "With-Millau messages relay account balance", + "legendFormat": "With-Millau relay account reward", "refId": "B" + }, + { + "exemplar": true, + "expr": "at_RialtoParachain_relay_MillauMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"} + at_RialtoParachain_relay_MillauMessages_reward_for_lane_00000000_with_Millau{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "hide": false, + "interval": "", + "legendFormat": "With-Millau relay account total balance (balance + reward)", + "refId": "C" } ], "thresholds": [ @@ -161,7 +133,8 @@ "fill": true, "line": true, "op": "lt", - "value": 1000 + "value": 1000, + "visible": true } ], "timeFrom": null, @@ -230,52 +203,6 @@ "type": "last" }, "type": "query" - }, - { - "evaluator": { - "params": [ - 1000 - ], - "type": "lt" - }, - "operator": { - "type": "or" - }, - "query": { - "params": [ - "B", - "5m", - "now" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" - }, - { - "evaluator": { - "params": [ - 1000 - ], - "type": "lt" - }, - "operator": { - "type": "or" - }, - "query": { - "params": [ - "C", - "5m", - "now" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" } ], "executionErrorState": "alerting", @@ -291,19 +218,13 @@ "dashLength": 10, "dashes": false, "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 8, "w": 12, "x": 12, - "y": 16 + "y": 0 }, "hiddenSeries": false, "id": 12, @@ -319,8 +240,11 @@ "lines": true, "linewidth": 1, "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.3", + "pluginVersion": "8.2.6", "pointradius": 2, "points": false, "renderer": "flot", @@ -330,21 +254,26 @@ "steppedLine": false, "targets": [ { - "expr": "at_Millau_relay_RialtoHeaders_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "exemplar": true, + "expr": "at_Millau_relay_RialtoParachainMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", "interval": "", - "legendFormat": "With-Rialto headers relay account balance", + "legendFormat": "With-Rialto relay account balance", "refId": "A" }, { - "expr": "at_Millau_relay_RialtoParachainMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "exemplar": true, + "expr": "at_Millau_relay_RialtoParachainMessages_reward_for_lane_00000000_with_RialtoParachain{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "hide": false, "interval": "", - "legendFormat": "With-RialtoParachain messages relay account balance", + "legendFormat": "With-Rialto relay account reward", "refId": "B" }, { - "expr": "at_Millau_relay_RialtoParachains_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "exemplar": true, + "expr": "at_Millau_relay_RialtoParachainMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"} + \nat_Millau_relay_RialtoParachainMessages_reward_for_lane_00000000_with_RialtoParachain{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "hide": false, "interval": "", - "legendFormat": "With-Rialto parachains relay account balance", + "legendFormat": "With-Rialto relay account total balance (balance + reward)", "refId": "C" } ], @@ -354,7 +283,8 @@ "fill": true, "line": true, "op": "lt", - "value": 1000 + "value": 1000, + "visible": true } ], "timeFrom": null, @@ -438,19 +368,13 @@ "dashLength": 10, "dashes": false, "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 8, "w": 12, "x": 0, - "y": 24 + "y": 8 }, "hiddenSeries": false, "id": 14, @@ -466,8 +390,11 @@ "lines": true, "linewidth": 1, "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.3", + "pluginVersion": "8.2.6", "pointradius": 2, "points": false, "renderer": "flot", @@ -490,7 +417,8 @@ "fill": true, "line": true, "op": "gt", - "value": 0 + "value": 0, + "visible": true } ], "timeFrom": null, @@ -574,19 +502,13 @@ "dashLength": 10, "dashes": false, "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "gridPos": { "h": 8, "w": 12, "x": 12, - "y": 24 + "y": 8 }, "hiddenSeries": false, "id": 16, @@ -602,8 +524,11 @@ "lines": true, "linewidth": 1, "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.3", + "pluginVersion": "8.2.6", "pointradius": 2, "points": false, "renderer": "flot", @@ -625,7 +550,8 @@ "fill": true, "line": true, "op": "gt", - "value": 0 + "value": 0, + "visible": true } ], "timeFrom": null, @@ -670,7 +596,7 @@ } ], "refresh": "5s", - "schemaVersion": 26, + "schemaVersion": 32, "style": "dark", "tags": [], "templating": { diff --git a/docs/high-level-overview.md b/docs/high-level-overview.md index 5a76347ce4f..f2806719256 100644 --- a/docs/high-level-overview.md +++ b/docs/high-level-overview.md @@ -41,7 +41,7 @@ proofs are called mandatory in the pallet and relayer pays no fee for such heade The pallet does not require all headers to be imported or provided. The relayer itself chooses which headers he wants to submit (with the exception of mandatory headers). -More: [code](../modules/grandpa/). +More: [pallet level documentation and code](../modules/grandpa/). ### Bridge Parachains Finality Pallet @@ -60,7 +60,7 @@ The pallet may track multiple parachains at once and those parachains may use di parachain header decoding never happens at the pallet level. For maintaining the headers order, the pallet uses relay chain header number. -More: [code](../modules/parachains/). +More: [pallet level documentation and code](../modules/parachains/). ### Bridge Messages Pallet @@ -91,14 +91,14 @@ pallet, in this case, depends on one of the finality pallets. The messages are X XCM executor to dispatch them on receival. You may find more info in [Polkadot <> Kusama Bridge](./polkadot-kusama-bridge-overview.md) document. -More: [code](../modules/messages/). +More: [pallet level documentation and code](../modules/messages/). ### Bridge Relayers Pallet The pallet is quite simple. It just registers relayer rewards and has an entrypoint to collect them. When the rewards are registered and the reward amount is configured outside of the pallet. -More: [code](../modules/relayers/). +More: [pallet level documentation and code](../modules/relayers/). ## Offchain Components diff --git a/modules/beefy/Cargo.toml b/modules/beefy/Cargo.toml index 2dca89d82e5..1908bd50c8a 100644 --- a/modules/beefy/Cargo.toml +++ b/modules/beefy/Cargo.toml @@ -48,3 +48,7 @@ std = [ "sp-runtime/std", "sp-std/std", ] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/modules/grandpa/Cargo.toml b/modules/grandpa/Cargo.toml index 45f314a0bbd..9e260a33537 100644 --- a/modules/grandpa/Cargo.toml +++ b/modules/grandpa/Cargo.toml @@ -57,3 +57,7 @@ runtime-benchmarks = [ "bp-test-utils", "frame-benchmarking/runtime-benchmarks", ] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/modules/messages/Cargo.toml b/modules/messages/Cargo.toml index 0c2fecb0be3..afa8b92b228 100644 --- a/modules/messages/Cargo.toml +++ b/modules/messages/Cargo.toml @@ -50,3 +50,7 @@ std = [ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", ] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/modules/messages/src/benchmarking.rs b/modules/messages/src/benchmarking.rs index 62dd1d60caf..a89cb3a8ef0 100644 --- a/modules/messages/src/benchmarking.rs +++ b/modules/messages/src/benchmarking.rs @@ -27,9 +27,11 @@ use bp_messages::{ UnrewardedRelayersState, }; use bp_runtime::StorageProofSize; +use codec::Decode; use frame_benchmarking::{account, benchmarks_instance_pallet}; use frame_support::weights::Weight; use frame_system::RawOrigin; +use sp_runtime::traits::TrailingZeroInput; use sp_std::{ops::RangeInclusive, prelude::*}; const SEED: u32 = 0; @@ -64,15 +66,26 @@ pub struct MessageDeliveryProofParams { /// Trait that must be implemented by runtime. pub trait Config: crate::Config { /// Lane id to use in benchmarks. + /// + /// By default, lane 00000000 is used. fn bench_lane_id() -> LaneId { - Default::default() + LaneId([0, 0, 0, 0]) } + /// Return id of relayer account at the bridged chain. - fn bridged_relayer_id() -> Self::InboundRelayer; - /// Returns true if given relayer has been rewarded for some of its actions. - fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool; - /// Create given account and give it enough balance for test purposes. - fn endow_account(account: &Self::AccountId); + /// + /// By default, zero account is returned. + fn bridged_relayer_id() -> Self::InboundRelayer { + Self::InboundRelayer::decode(&mut TrailingZeroInput::zeroes()).unwrap() + } + + /// Create given account and give it enough balance for test purposes. Used to create + /// relayer account at the target chain. Is strictly necessary when your rewards scheme + /// assumes that the relayer account must exist. + /// + /// Does nothing by default. + fn endow_account(_account: &Self::AccountId) {} + /// Prepare messages proof to receive by the module. fn prepare_message_proof( params: MessageProofParams, @@ -81,8 +94,20 @@ pub trait Config: crate::Config { fn prepare_message_delivery_proof( params: MessageDeliveryProofParams, ) -> >::MessagesDeliveryProof; + /// Returns true if message has been dispatched (either successfully or not). - fn is_message_dispatched(nonce: MessageNonce) -> bool; + /// + /// We assume that messages have near-zero dispatch weight, so most of times it + /// is hard to determine whether messages has been dispatched or not. For example, + /// XCM message can be a call that leaves entry in `frame_system::Events` vector, + /// but not all XCM messages do that and we don't want to include weight of this + /// action to the base weight of message delivery. Hence, the default `true` return + /// value. + fn is_message_dispatched(_nonce: MessageNonce) -> bool { + true + } + /// Returns true if given relayer has been rewarded for some of its actions. + fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool; } benchmarks_instance_pallet! { diff --git a/modules/parachains/Cargo.toml b/modules/parachains/Cargo.toml index 3c3ad95fe34..c25956248d0 100644 --- a/modules/parachains/Cargo.toml +++ b/modules/parachains/Cargo.toml @@ -54,3 +54,7 @@ std = [ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", ] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/modules/relayers/Cargo.toml b/modules/relayers/Cargo.toml index 9c071238363..c7ea1e544f2 100644 --- a/modules/relayers/Cargo.toml +++ b/modules/relayers/Cargo.toml @@ -15,6 +15,7 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive" bp-messages = { path = "../../primitives/messages", default-features = false } bp-relayers = { path = "../../primitives/relayers", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } # Substrate Dependencies @@ -37,6 +38,7 @@ default = ["std"] std = [ "bp-messages/std", "bp-relayers/std", + "bp-runtime/std", "codec/std", "frame-support/std", "frame-system/std", @@ -49,3 +51,7 @@ std = [ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", ] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/modules/relayers/README.md b/modules/relayers/README.md new file mode 100644 index 00000000000..656200f4486 --- /dev/null +++ b/modules/relayers/README.md @@ -0,0 +1,14 @@ +# Bridge Relayers Pallet + +The pallet serves as a storage for pending bridge relayer rewards. Any runtime component may register reward +to some relayer for doing some useful job at some messages lane. Later, the relayer may claim its rewards +using the `claim_rewards` call. + +The reward payment procedure is abstracted from the pallet code. One of possible implementations, is the +[`PayLaneRewardFromAccount`](../../primitives/relayers/src/lib.rs), which just does a `Currency::transfer` +call to relayer account from the relayer-rewards account, determined by the message lane id. + +We have two examples of how this pallet is used in production. Rewards are registered at the target chain to +compensate fees of message delivery transactions (and linked finality delivery calls). At the source chain, rewards +are registered during delivery confirmation transactions. You may find more information about that in the +[Kusama <> Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md) documentation. diff --git a/modules/relayers/src/lib.rs b/modules/relayers/src/lib.rs index b28d17cf5a4..7132914a4ae 100644 --- a/modules/relayers/src/lib.rs +++ b/modules/relayers/src/lib.rs @@ -21,7 +21,8 @@ #![warn(missing_docs)] use bp_messages::LaneId; -use bp_relayers::PaymentProcedure; +use bp_relayers::{PaymentProcedure, RelayerRewardsKeyProvider}; +use bp_runtime::StorageDoubleMapKeyProvider; use frame_support::sp_runtime::Saturating; use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero}; use sp_std::marker::PhantomData; @@ -46,6 +47,10 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + /// `RelayerRewardsKeyProvider` for given configuration. + type RelayerRewardsKeyProviderOf = + RelayerRewardsKeyProvider<::AccountId, ::Reward>; + #[pallet::config] pub trait Config: frame_system::Config { /// The overarching event type. @@ -146,11 +151,11 @@ pub mod pallet { #[pallet::getter(fn relayer_reward)] pub type RelayerRewards = StorageDoubleMap< _, - Blake2_128Concat, - T::AccountId, - Identity, - LaneId, - T::Reward, + as StorageDoubleMapKeyProvider>::Hasher1, + as StorageDoubleMapKeyProvider>::Key1, + as StorageDoubleMapKeyProvider>::Hasher2, + as StorageDoubleMapKeyProvider>::Key2, + as StorageDoubleMapKeyProvider>::Value, OptionQuery, >; } diff --git a/modules/shift-session-manager/Cargo.toml b/modules/shift-session-manager/Cargo.toml index 5dae3e00fd3..504adfae416 100644 --- a/modules/shift-session-manager/Cargo.toml +++ b/modules/shift-session-manager/Cargo.toml @@ -33,3 +33,7 @@ std = [ "sp-staking/std", "sp-std/std", ] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/modules/shift-session-manager/README.md b/modules/shift-session-manager/README.md new file mode 100644 index 00000000000..8dfbfd416e3 --- /dev/null +++ b/modules/shift-session-manager/README.md @@ -0,0 +1,10 @@ +# Shift Session Manager Pallet + +**THIS PALLET IS NOT INTENDED TO BE USED IN PRODUCTION** + +The pallet does not provide any calls or runtime storage entries. It only provides implementation of the +`pallet_session::SessionManager`. This implementation, starting from session `3` selects two thirds of initial +validators and changes the set on every session. We are using it at our testnets ([Rialto](../../bin/rialto/) and +[Millau](../../bin/millau/)) to be sure that the set changes every session. On well-known production chains +(like Kusama and Polkadot) the alternative is the set of [nPoS](https://research.web3.foundation/en/latest/polkadot/NPoS/index.html) +pallets, which selects validators, based on their nominations. diff --git a/primitives/chain-bridge-hub-cumulus/src/lib.rs b/primitives/chain-bridge-hub-cumulus/src/lib.rs index e33131ff8a2..286fbdbebc3 100644 --- a/primitives/chain-bridge-hub-cumulus/src/lib.rs +++ b/primitives/chain-bridge-hub-cumulus/src/lib.rs @@ -18,9 +18,9 @@ use bp_messages::*; pub use bp_polkadot_core::{ - AccountId, AccountInfoStorageMapKeyProvider, AccountPublic, Balance, BlockNumber, - BridgeSignedExtension, Hash, Hasher, Hashing, Header, Index, Nonce, Perbill, - PolkadotSignedExtension, Signature, SignedBlock, UncheckedExtrinsic, TX_EXTRA_BYTES, + AccountId, AccountInfoStorageMapKeyProvider, AccountPublic, Balance, BlockNumber, Hash, Hasher, + Hashing, Header, Index, Nonce, Perbill, PolkadotSignedExtension, Signature, SignedBlock, + UncheckedExtrinsic, TX_EXTRA_BYTES, }; use frame_support::{ dispatch::DispatchClass, @@ -86,6 +86,8 @@ pub type AccountSigner = MultiSigner; /// The address format for describing accounts. pub type Address = MultiAddress; +pub use bp_polkadot_core::BridgeSignedExtension as SignedExtension; + // Note about selecting values of two following constants: // // Normal transactions have limit of 75% of 1/2 second weight for Cumulus parachains. Let's keep diff --git a/primitives/chain-rialto-parachain/Cargo.toml b/primitives/chain-rialto-parachain/Cargo.toml index a15c4092957..ed7c5c07960 100644 --- a/primitives/chain-rialto-parachain/Cargo.toml +++ b/primitives/chain-rialto-parachain/Cargo.toml @@ -11,6 +11,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" # Bridge Dependencies bp-messages = { path = "../messages", default-features = false } +bp-polkadot-core = { path = "../polkadot-core", default-features = false } bp-runtime = { path = "../runtime", default-features = false } # Substrate Based Dependencies diff --git a/primitives/chain-rialto-parachain/src/lib.rs b/primitives/chain-rialto-parachain/src/lib.rs index 8a98ffe7061..d39fe253783 100644 --- a/primitives/chain-rialto-parachain/src/lib.rs +++ b/primitives/chain-rialto-parachain/src/lib.rs @@ -133,6 +133,8 @@ impl Parachain for RialtoParachain { const PARACHAIN_ID: u32 = RIALTO_PARACHAIN_ID; } +pub use bp_polkadot_core::DefaultSignedExtension as SignedExtension; + frame_support::parameter_types! { pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); diff --git a/primitives/polkadot-core/src/lib.rs b/primitives/polkadot-core/src/lib.rs index 3d4b72fccb9..d5a9b5de817 100644 --- a/primitives/polkadot-core/src/lib.rs +++ b/primitives/polkadot-core/src/lib.rs @@ -266,7 +266,7 @@ impl PolkadotSignedExtension for DefaultSignedExtension { (), // Check weight tip.into(), // transaction payment / tip (compact encoding) ), - ( + Some(( (), spec_version, transaction_version, @@ -275,7 +275,7 @@ impl PolkadotSignedExtension for DefaultSignedExtension { (), (), (), - ), + )), ) } @@ -326,7 +326,7 @@ impl PolkadotSignedExtension for BridgeSignedExtension { tip.into(), // transaction payment / tip (compact encoding) (), // bridge reject obsolete headers and msgs ), - ( + Some(( (), spec_version, transaction_version, @@ -336,7 +336,7 @@ impl PolkadotSignedExtension for BridgeSignedExtension { (), (), (), - ), + )), ) } diff --git a/primitives/relayers/Cargo.toml b/primitives/relayers/Cargo.toml index 4f893f9f83e..acede813995 100644 --- a/primitives/relayers/Cargo.toml +++ b/primitives/relayers/Cargo.toml @@ -11,6 +11,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" # Bridge Dependencies bp-messages = { path = "../messages", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } # Substrate Dependencies @@ -27,6 +28,7 @@ hex-literal = "0.3" default = ["std"] std = [ "bp-messages/std", + "bp-runtime/std", "frame-support/std", "sp-runtime/std", "sp-std/std", diff --git a/primitives/relayers/src/lib.rs b/primitives/relayers/src/lib.rs index d00b5f626e4..207908296cb 100644 --- a/primitives/relayers/src/lib.rs +++ b/primitives/relayers/src/lib.rs @@ -20,8 +20,10 @@ #![cfg_attr(not(feature = "std"), no_std)] use bp_messages::LaneId; +use bp_runtime::StorageDoubleMapKeyProvider; +use frame_support::{Blake2_128Concat, Identity}; use sp_runtime::{ - codec::{Decode, Encode}, + codec::{Codec, Decode, Encode, EncodeLike}, traits::AccountIdConversion, }; use sp_std::{fmt::Debug, marker::PhantomData}; @@ -65,6 +67,24 @@ where } } +/// Can be use to access the runtime storage key within the `RelayerRewards` map of the relayers +/// pallet. +pub struct RelayerRewardsKeyProvider(PhantomData<(AccountId, Reward)>); + +impl StorageDoubleMapKeyProvider for RelayerRewardsKeyProvider +where + AccountId: Codec + EncodeLike, + Reward: Codec + EncodeLike, +{ + const MAP_NAME: &'static str = "RelayerRewards"; + + type Hasher1 = Blake2_128Concat; + type Key1 = AccountId; + type Hasher2 = Identity; + type Key2 = LaneId; + type Value = Reward; +} + #[cfg(test)] mod tests { use super::*; diff --git a/primitives/runtime/src/extensions.rs b/primitives/runtime/src/extensions.rs index 287f484db4a..eefe10f7057 100644 --- a/primitives/runtime/src/extensions.rs +++ b/primitives/runtime/src/extensions.rs @@ -96,8 +96,8 @@ pub struct GenericSignedExtension { } impl GenericSignedExtension { - pub fn new(payload: S::Payload, additional_signed: S::AdditionalSigned) -> Self { - Self { payload, additional_signed: Some(additional_signed) } + pub fn new(payload: S::Payload, additional_signed: Option) -> Self { + Self { payload, additional_signed } } } diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs index ea6d6ad9f51..193632c28b4 100644 --- a/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs @@ -61,8 +61,8 @@ use crate::{ use bp_messages::LaneId; use bp_runtime::BalanceOf; use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, Chain, ChainWithBalances, ChainWithTransactions, Client, - Parachain, + AccountIdOf, AccountKeyPairOf, Chain, ChainWithBalances, ChainWithMessages, + ChainWithTransactions, Client, Parachain, }; use relay_utils::metrics::MetricsParams; use sp_core::Pair; @@ -259,9 +259,9 @@ where type Base: Full2WayBridgeBase; /// The left relay chain. - type Left: ChainWithTransactions + ChainWithBalances + CliChain; + type Left: ChainWithTransactions + ChainWithBalances + ChainWithMessages + CliChain; /// The right relay chain. - type Right: ChainWithTransactions + ChainWithBalances + CliChain; + type Right: ChainWithTransactions + ChainWithBalances + ChainWithMessages + CliChain; /// Left to Right bridge. type L2R: MessagesCliBridge; @@ -317,28 +317,36 @@ where self.mut_base().start_on_demand_headers_relayers().await?; // add balance-related metrics + let lanes = self + .base() + .common() + .shared + .lane + .iter() + .cloned() + .map(Into::into) + .collect::>(); { let common = self.mut_base().mut_common(); - substrate_relay_helper::messages_metrics::add_relay_balances_metrics( + substrate_relay_helper::messages_metrics::add_relay_balances_metrics::<_, Self::Right>( common.left.client.clone(), &mut common.metrics_params, &common.left.accounts, + &lanes, ) .await?; - substrate_relay_helper::messages_metrics::add_relay_balances_metrics( + substrate_relay_helper::messages_metrics::add_relay_balances_metrics::<_, Self::Left>( common.right.client.clone(), &mut common.metrics_params, &common.right.accounts, + &lanes, ) .await?; } - let lanes = self.base().common().shared.lane.clone(); // Need 2x capacity since we consider both directions for each lane let mut message_relays = Vec::with_capacity(lanes.len() * 2); for lane in lanes { - let lane = lane.into(); - let left_to_right_messages = substrate_relay_helper::messages_lane::run::< ::MessagesLane, >(self.left_to_right().messages_relay_params( diff --git a/relays/client-bridge-hub-rococo/src/lib.rs b/relays/client-bridge-hub-rococo/src/lib.rs index 8e6e9712925..b14a9baa61d 100644 --- a/relays/client-bridge-hub-rococo/src/lib.rs +++ b/relays/client-bridge-hub-rococo/src/lib.rs @@ -66,7 +66,7 @@ impl ChainWithTransactions for BridgeHubRococo { ) -> Result { let raw_payload = SignedPayload::new( unsigned.call, - bp_bridge_hub_rococo::BridgeSignedExtension::from_params( + bp_bridge_hub_rococo::SignedExtension::from_params( param.spec_version, param.transaction_version, unsigned.era, @@ -110,6 +110,7 @@ impl ChainWithTransactions for BridgeHubRococo { impl ChainWithMessages for BridgeHubRococo { const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME; + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = None; const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = bp_bridge_hub_rococo::TO_BRIDGE_HUB_ROCOCO_MESSAGE_DETAILS_METHOD; diff --git a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs index 1bb32a4089c..7f526a35aa9 100644 --- a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs +++ b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs @@ -21,14 +21,14 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; -use bp_bridge_hub_rococo::BridgeSignedExtension; +use bp_bridge_hub_rococo::SignedExtension; pub use bp_header_chain::BridgeGrandpaCallOf; pub use bp_parachains::BridgeParachainCall; pub use bridge_runtime_common::messages::BridgeMessagesCallOf; pub use relay_substrate_client::calls::SystemCall; /// Unchecked BridgeHubRococo extrinsic. -pub type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic; +pub type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic; // The indirect pallet call used to sync `Wococo` GRANDPA finality to `BHRococo`. pub type BridgeWococoGrandpaCall = BridgeGrandpaCallOf; diff --git a/relays/client-bridge-hub-wococo/src/lib.rs b/relays/client-bridge-hub-wococo/src/lib.rs index 3fd8187fa1f..abc820ed624 100644 --- a/relays/client-bridge-hub-wococo/src/lib.rs +++ b/relays/client-bridge-hub-wococo/src/lib.rs @@ -66,7 +66,7 @@ impl ChainWithTransactions for BridgeHubWococo { ) -> Result { let raw_payload = SignedPayload::new( unsigned.call, - bp_bridge_hub_wococo::BridgeSignedExtension::from_params( + bp_bridge_hub_wococo::SignedExtension::from_params( param.spec_version, param.transaction_version, unsigned.era, @@ -110,6 +110,7 @@ impl ChainWithTransactions for BridgeHubWococo { impl ChainWithMessages for BridgeHubWococo { const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = bp_bridge_hub_wococo::WITH_BRIDGE_HUB_WOCOCO_MESSAGES_PALLET_NAME; + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = None; const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = bp_bridge_hub_wococo::TO_BRIDGE_HUB_WOCOCO_MESSAGE_DETAILS_METHOD; diff --git a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs index 2158ad0e659..85f77a6377e 100644 --- a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs +++ b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs @@ -19,14 +19,14 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; -use bp_bridge_hub_wococo::BridgeSignedExtension; +use bp_bridge_hub_wococo::SignedExtension; pub use bp_header_chain::BridgeGrandpaCallOf; pub use bp_parachains::BridgeParachainCall; pub use bridge_runtime_common::messages::BridgeMessagesCallOf; pub use relay_substrate_client::calls::SystemCall; /// Unchecked BridgeHubWococo extrinsic. -pub type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic; +pub type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic; // The indirect pallet call used to sync `Rococo` GRANDPA finality to `BHWococo`. pub type BridgeRococoGrandpaCall = BridgeGrandpaCallOf; diff --git a/relays/client-millau/src/lib.rs b/relays/client-millau/src/lib.rs index fb901a4b2de..34bbea92d57 100644 --- a/relays/client-millau/src/lib.rs +++ b/relays/client-millau/src/lib.rs @@ -45,6 +45,8 @@ impl ChainWithGrandpa for Millau { impl ChainWithMessages for Millau { const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME; + // TODO (https://github.com/paritytech/parity-bridges-common/issues/1692): change the name + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = Some("BridgeRelayers"); const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = bp_millau::TO_MILLAU_MESSAGE_DETAILS_METHOD; const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = diff --git a/relays/client-rialto-parachain/src/lib.rs b/relays/client-rialto-parachain/src/lib.rs index ebaac73e036..d6efd6581b2 100644 --- a/relays/client-rialto-parachain/src/lib.rs +++ b/relays/client-rialto-parachain/src/lib.rs @@ -19,7 +19,7 @@ pub mod runtime_wrapper; use bp_messages::MessageNonce; -use bp_polkadot_core::{DefaultSignedExtension, PolkadotSignedExtension}; +use bp_polkadot_core::PolkadotSignedExtension; use codec::Encode; use relay_substrate_client::{ Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, Error as SubstrateError, @@ -63,6 +63,8 @@ impl ChainWithBalances for RialtoParachain { impl ChainWithMessages for RialtoParachain { const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = bp_rialto_parachain::WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME; + // TODO (https://github.com/paritytech/parity-bridges-common/issues/1692): change the name + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = Some("BridgeRelayers"); const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = bp_rialto_parachain::TO_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD; const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = @@ -77,7 +79,7 @@ impl ChainWithMessages for RialtoParachain { impl ChainWithTransactions for RialtoParachain { type AccountKeyPair = sp_core::sr25519::Pair; type SignedTransaction = - bp_polkadot_core::UncheckedExtrinsic; + bp_polkadot_core::UncheckedExtrinsic; fn sign_transaction( param: SignParam, @@ -85,7 +87,7 @@ impl ChainWithTransactions for RialtoParachain { ) -> Result { let raw_payload = SignedPayload::new( unsigned.call, - bp_polkadot_core::DefaultSignedExtension::from_params( + bp_rialto_parachain::SignedExtension::from_params( param.spec_version, param.transaction_version, unsigned.era, diff --git a/relays/client-rialto/src/lib.rs b/relays/client-rialto/src/lib.rs index 4c3a9d1e18e..8ad31de4d58 100644 --- a/relays/client-rialto/src/lib.rs +++ b/relays/client-rialto/src/lib.rs @@ -63,6 +63,8 @@ impl ChainWithGrandpa for Rialto { impl ChainWithMessages for Rialto { const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME; + // TODO (https://github.com/paritytech/parity-bridges-common/issues/1692): change the name + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = Some("BridgeRelayers"); const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = bp_rialto::TO_RIALTO_MESSAGE_DETAILS_METHOD; const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = diff --git a/relays/client-substrate/src/chain.rs b/relays/client-substrate/src/chain.rs index db08fc1f98c..4ec5edfc41b 100644 --- a/relays/client-substrate/src/chain.rs +++ b/relays/client-substrate/src/chain.rs @@ -101,6 +101,16 @@ pub trait ChainWithMessages: Chain { /// the same name. const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str; + // TODO (https://github.com/paritytech/parity-bridges-common/issues/1692): check all the names + // after the issue is fixed - all names must be changed + + /// Name of the bridge relayers pallet (used in `construct_runtime` macro call) that is deployed + /// at some other chain to bridge with this `ChainWithMessages`. + /// + /// We assume that all chains that are bridging with this `ChainWithMessages` are using + /// the same name. + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str>; + /// Name of the `ToOutboundLaneApi::message_details` runtime API method. /// The method is provided by the runtime that is bridged with this `ChainWithMessages`. const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str; diff --git a/relays/finality/README.md b/relays/finality/README.md new file mode 100644 index 00000000000..edfd00192bc --- /dev/null +++ b/relays/finality/README.md @@ -0,0 +1,58 @@ +# GRANDPA Finality Relay + +The finality relay is able to work with different finality engines. In the modern Substrate world they are GRANDPA +and BEEFY. Let's talk about GRANDPA here, because BEEFY relay and bridge BEEFY pallet are in development. + +In general, the relay works as follows: it connects to the source and target chain. The source chain must have the +[GRANDPA gadget](https://github.com/paritytech/finality-grandpa) running (so it can't be a parachain). The target +chain must have the [bridge GRANDPA pallet](../../modules/grandpa/) deployed at its runtime. The relay subscribes +to the GRANDPA finality notifications at the source chain and when the new justification is received, it is submitted +to the pallet at the target chain. + +Apart from that, the relay is watching for every source header that is missing at target. If it finds the missing +mandatory header (header that is changing the current GRANDPA validators set), it submits the justification for +this header. The case when the source node can't return the mandatory justification is considered a fatal error, +because the pallet can't proceed without it. + +More: [GRANDPA Finality Relay Sequence Diagram](../../docs/grandpa-finality-relay.html). + +## How to Use the Finality Relay + +The most important trait is the [`FinalitySyncPipeline`](./src/lib.rs), which defines the basic primitives of the +source chain (like block hash and number) and the type of finality proof (GRANDPA jusitfication or MMR proof). Once +that is defined, there are two other traits - [`SourceClient`](./src/finality_loop.rs) and +[`TarggetClient`](./src/finality_loop.rs). + +The `SourceClient` represents the Substrate node client that connects to the source chain. The client need to +be able to return the best finalized header number, finalized header and its finality proof and the stream of +finality proofs. + +The `TargetClient` implementation must be able to craft finality delivery transaction and submit it to the target +node. The transaction is then tracked by the relay until it is mined and finalized. + +The main entrypoint for the crate is the [`run` function](./src/finality_loop.rs), which takes source and target +clients and [`FinalitySyncParams`](./src/finality_loop.rs) parameters. The most imporant parameter is the +`only_mandatory_headers` - it it is set to `true`, the relay will only submit mandatory headers. Since transactions +with mandatory headers are fee-free, the cost of running such relay is zero (in terms of fees). + +## Finality Relay Metrics + +Finality relay provides several metrics. Metrics names depend on names of source and target chains. The list below +shows metrics names for Rialto (source chain) to Millau (target chain) finality relay. For other chains, simply +change chain names. So the metrics are: + +- `Rialto_to_Millau_Sync_best_source_block_number` - returns best finalized source chain (Rialto) block number, known + to the relay. If relay is running in [on-demand mode](../bin-substrate/src/cli/relay_headers_and_messages/), the + number may not match (it may be far behind) the actual best finalized number; + +- `Rialto_to_Millau_Sync_best_source_at_target_block_number` - returns best finalized source chain (Rialto) block + number that is known to the bridge GRANDPA pallet at the target chain. + +- `Rialto_to_Millau_Sync_is_source_and_source_at_target_using_different_forks` - if this metrics is set to `1`, then + the best source chain header, known to the target chain doesn't match the same-number-header at the source chain. + It means that the GRANDPA validators set has crafted the duplicate justification and it has been submitted to the + target chain. Normally (if majority of validators are honest and if you're running finality relay without large + breaks) this shall not happen and the metric will have `0` value. + +If relay operates properly, you should see that the `Rialto_to_Millau_Sync_best_source_at_target_block_number` +tries to reach the `Rialto_to_Millau_Sync_best_source_block_number`. And the latter one always increases. \ No newline at end of file diff --git a/relays/lib-substrate-relay/Cargo.toml b/relays/lib-substrate-relay/Cargo.toml index bdf49d42eab..e044be0957c 100644 --- a/relays/lib-substrate-relay/Cargo.toml +++ b/relays/lib-substrate-relay/Cargo.toml @@ -12,6 +12,7 @@ async-std = "1.9.0" async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.1.5" } futures = "0.3.12" +hex = "0.4" num-traits = "0.2" log = "0.4.17" @@ -20,6 +21,7 @@ log = "0.4.17" bp-header-chain = { path = "../../primitives/header-chain" } bp-parachains = { path = "../../primitives/parachains" } bp-polkadot-core = { path = "../../primitives/polkadot-core" } +bp-relayers = { path = "../../primitives/relayers" } bridge-runtime-common = { path = "../../bin/runtime-common" } finality-grandpa = { version = "0.16.0" } diff --git a/relays/lib-substrate-relay/src/messages_metrics.rs b/relays/lib-substrate-relay/src/messages_metrics.rs index 37a6d67baae..943f3b7c369 100644 --- a/relays/lib-substrate-relay/src/messages_metrics.rs +++ b/relays/lib-substrate-relay/src/messages_metrics.rs @@ -18,12 +18,15 @@ use crate::TaggedAccount; +use bp_messages::LaneId; +use bp_runtime::StorageDoubleMapKeyProvider; use codec::Decode; use frame_system::AccountInfo; use pallet_balances::AccountData; use relay_substrate_client::{ metrics::{FloatStorageValue, FloatStorageValueMetric}, - AccountIdOf, BalanceOf, Chain, ChainWithBalances, Client, Error as SubstrateError, IndexOf, + AccountIdOf, BalanceOf, Chain, ChainWithBalances, ChainWithMessages, Client, + Error as SubstrateError, IndexOf, }; use relay_utils::metrics::{MetricsParams, StandaloneMetric}; use sp_core::storage::StorageData; @@ -31,10 +34,11 @@ use sp_runtime::{FixedPointNumber, FixedU128}; use std::{convert::TryFrom, fmt::Debug, marker::PhantomData}; /// Add relay accounts balance metrics. -pub async fn add_relay_balances_metrics( +pub async fn add_relay_balances_metrics( client: Client, metrics: &mut MetricsParams, relay_accounts: &Vec>>, + lanes: &[LaneId], ) -> anyhow::Result<()> where BalanceOf: Into + std::fmt::Debug, @@ -68,13 +72,30 @@ where for account in relay_accounts { let relay_account_balance_metric = FloatStorageValueMetric::new( - FreeAccountBalance:: { token_decimals, _phantom: Default::default() }, + AccountBalanceFromAccountInfo:: { token_decimals, _phantom: Default::default() }, client.clone(), C::account_info_storage_key(account.id()), format!("at_{}_relay_{}_balance", C::NAME, account.tag()), format!("Balance of the {} relay account at the {}", account.tag(), C::NAME), )?; relay_account_balance_metric.register_and_spawn(&metrics.registry)?; + + if let Some(relayers_pallet_name) = BC::WITH_CHAIN_RELAYERS_PALLET_NAME { + for lane in lanes { + let relay_account_reward_metric = FloatStorageValueMetric::new( + AccountBalance:: { token_decimals, _phantom: Default::default() }, + client.clone(), + bp_relayers::RelayerRewardsKeyProvider::, BalanceOf>::final_key( + relayers_pallet_name, + account.id(), + lane, + ), + format!("at_{}_relay_{}_reward_for_lane_{}_with_{}", C::NAME, account.tag(), hex::encode(lane.as_ref()), BC::NAME), + format!("Reward of the {} relay account for serving lane {:?} with {} at the {}", account.tag(), lane, BC::NAME, C::NAME), + )?; + relay_account_reward_metric.register_and_spawn(&metrics.registry)?; + } + } } Ok(()) @@ -82,12 +103,12 @@ where /// Adapter for `FloatStorageValueMetric` to decode account free balance. #[derive(Clone, Debug)] -struct FreeAccountBalance { +struct AccountBalanceFromAccountInfo { token_decimals: u32, _phantom: PhantomData, } -impl FloatStorageValue for FreeAccountBalance +impl FloatStorageValue for AccountBalanceFromAccountInfo where C: Chain, BalanceOf: Into, @@ -110,6 +131,34 @@ where } } +/// Adapter for `FloatStorageValueMetric` to decode account free balance. +#[derive(Clone, Debug)] +struct AccountBalance { + token_decimals: u32, + _phantom: PhantomData, +} + +impl FloatStorageValue for AccountBalance +where + C: Chain, + BalanceOf: Into, +{ + type Value = FixedU128; + + fn decode( + &self, + maybe_raw_value: Option, + ) -> Result, SubstrateError> { + maybe_raw_value + .map(|raw_value| { + BalanceOf::::decode(&mut &raw_value.0[..]) + .map_err(SubstrateError::ResponseParseFailed) + .map(|balance| convert_to_token_balance(balance.into(), self.token_decimals)) + }) + .transpose() + } +} + /// Convert from raw `u128` balance (nominated in smallest chain token units) to the float regular /// tokens value. fn convert_to_token_balance(balance: u128, token_decimals: u32) -> FixedU128 { diff --git a/relays/parachains/README.md b/relays/parachains/README.md new file mode 100644 index 00000000000..6cb68f2209f --- /dev/null +++ b/relays/parachains/README.md @@ -0,0 +1,49 @@ +# Parachains Finality Relay + +The parachains finality relay works with two chains - source relay chain and target chain (which may be standalone +chain, relay chain or a parachain). The source chain must have the +[`paras` pallet](https://github.com/paritytech/polkadot/tree/master/runtime/parachains/src/paras) deployed at its +runtime. The target chain must have the [bridge parachains pallet](../../modules/parachains/) deployed at its runtime. + +The relay is configured to submit heads of one or several parachains. It pokes source chain periodically and compares +parachain heads that are known to the source relay chain to heads at the target chain. If there are new heads, +the relay submits them to the target chain. + +More: [Parachains Finality Relay Sequence Diagram](../../docs/parachains-finality-relay.html). + +## How to Use the Parachains Finality Relay + +There are only two traits that need to be implemented. The [`SourceChain`](./src/parachains_loop.rs) implementation +is supposed to connect to the source chain node. It must be able to read parachain heads from the `Heads` map of +the [`paras` pallet](https://github.com/paritytech/polkadot/tree/master/runtime/parachains/src/paras) pallet. +It also must create storage proofs of `Heads` map entries, when required. + +The [`TargetChain`](./src/parachains_loop.rs) implementation connects to the target chain node. It must be able +to return the best known head of given parachain. When required, it must be able to craft and submit parachains +finality delivery transaction to the target node. + +The main entrypoint for the crate is the [`run` function](./src/parachains_loop.rs), which takes source and target +clients and [`ParachainSyncParams`](./src/parachains_loop.rs) parameters. The most imporant parameter is the +`parachains` - it it the set of parachains, which relay tracks and updates. The other important parameter that +may affect the relay operational costs is the `strategy`. If it is set to `Any`, then the finality delivery +transaction is submitted if at least one of tracked parachain heads is updated. The other option is `All`. Then +the relay waits until all tracked parachain heads are updated and submits them all in a single finality delivery +transaction. + +## Parachain Finality Relay Metrics + +Every parachain in Polkadot is identified by the 32-bit number. All metrics, exposed by the parachains finality +relay have the `parachain` label, which is set to the parachain id. And the metrics are prefixed with the prefix, +that depends on the name of the source relay and target chains. The list below shows metrics names for +Rialto (source relay chain) to Millau (target chain) parachains finality relay. For other chains, simply +change chain names. So the metrics are: + +- `Rialto_to_Millau_Parachains_best_parachain_block_number_at_source` - returns best known parachain block + number, registered in the `paras` pallet at the source relay chain (Rialto in our example); + +- `Rialto_to_Millau_Parachains_best_parachain_block_number_at_target` - returns best known parachain block + number, registered in the bridge parachains pallet at the target chain (Millau in our example). + +If relay operates properly, you should see that the `Rialto_to_Millau_Parachains_best_parachain_block_number_at_target` +tries to reach the `Rialto_to_Millau_Parachains_best_parachain_block_number_at_source`. And the latter one +always increases. From 30f286698d71673948086491458a5cceb1bac22e Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 19 Jan 2023 11:40:50 +0100 Subject: [PATCH 180/263] Fixed try-runtime --- Cargo.lock | 2 ++ parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 16890d2bf27..7f43b1b18e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -829,6 +829,7 @@ version = "0.1.0" dependencies = [ "bp-messages", "bp-rialto", + "bp-runtime", "frame-support", "hex", "hex-literal", @@ -855,6 +856,7 @@ name = "bp-rialto-parachain" version = "0.1.0" dependencies = [ "bp-messages", + "bp-polkadot-core", "bp-runtime", "frame-support", "frame-system", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 5fe796f1cd0..4fc2cb1b445 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -174,6 +174,10 @@ runtime-benchmarks = [ ] try-runtime = [ + "pallet-bridge-grandpa/try-runtime", + "pallet-bridge-messages/try-runtime", + "pallet-bridge-parachains/try-runtime", + "pallet-bridge-relayers/try-runtime", "cumulus-pallet-aura-ext/try-runtime", "cumulus-pallet-dmp-queue/try-runtime", "cumulus-pallet-parachain-system/try-runtime", From b743f6cb529f09fe1abca2f65dc370dc6116b929 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 19 Jan 2023 11:54:06 +0100 Subject: [PATCH 181/263] Fix cargo.lock --- Cargo.lock | 622 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 616 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b77dc929db6..053a9c08386 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -677,6 +677,262 @@ dependencies = [ "thiserror", ] +[[package]] +name = "bp-beefy" +version = "0.1.0" +dependencies = [ + "beefy-merkle-tree", + "bp-runtime", + "frame-support", + "pallet-beefy-mmr", + "pallet-mmr", + "parity-scale-codec", + "scale-info", + "serde", + "sp-beefy", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-bridge-hub-cumulus" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-polkadot-core", + "frame-support", + "frame-system", + "polkadot-primitives", + "sp-api", +] + +[[package]] +name = "bp-bridge-hub-rococo" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-messages", + "bp-runtime", + "frame-support", + "sp-api", + "sp-std", +] + +[[package]] +name = "bp-bridge-hub-wococo" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-messages", + "bp-runtime", + "frame-support", + "sp-api", + "sp-std", +] + +[[package]] +name = "bp-header-chain" +version = "0.1.0" +dependencies = [ + "bp-runtime", + "bp-test-utils", + "finality-grandpa", + "frame-support", + "hex", + "hex-literal", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-finality-grandpa", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "bp-messages" +version = "0.1.0" +dependencies = [ + "bp-runtime", + "frame-support", + "hex", + "hex-literal", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-std", +] + +[[package]] +name = "bp-millau" +version = "0.1.0" +dependencies = [ + "bp-beefy", + "bp-messages", + "bp-runtime", + "fixed-hash 0.7.0", + "frame-support", + "frame-system", + "hash256-std-hasher", + "impl-codec", + "impl-serde 0.3.2", + "parity-util-mem", + "scale-info", + "serde", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "bp-parachains" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-polkadot-core" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-runtime", + "frame-support", + "frame-system", + "hex", + "parity-scale-codec", + "parity-util-mem", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-relayers" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-rialto", + "bp-runtime", + "frame-support", + "hex", + "hex-literal", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-rialto" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-runtime", + "frame-support", + "frame-system", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-rialto-parachain" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "frame-system", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "bp-rococo" +version = "0.1.0" +dependencies = [ + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "sp-api", +] + +[[package]] +name = "bp-runtime" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "hash-db", + "hex-literal", + "impl-trait-for-tuples", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "trie-db", +] + +[[package]] +name = "bp-test-utils" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "ed25519-dalek", + "finality-grandpa", + "parity-scale-codec", + "sp-application-crypto", + "sp-finality-grandpa", + "sp-runtime", + "sp-std", + "xcm", +] + +[[package]] +name = "bp-westend" +version = "0.1.0" +dependencies = [ + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "sp-api", +] + +[[package]] +name = "bp-wococo" +version = "0.1.0" +dependencies = [ + "bp-polkadot-core", + "bp-rococo", + "bp-runtime", + "sp-api", +] + [[package]] name = "bridge-hub-kusama-runtime" version = "0.1.0" @@ -744,6 +1000,15 @@ dependencies = [ name = "bridge-hub-rococo-runtime" version = "0.1.0" dependencies = [ + "bp-bridge-hub-rococo", + "bp-bridge-hub-wococo", + "bp-messages", + "bp-parachains", + "bp-rococo", + "bp-runtime", + "bp-wococo", + "bridge-hub-test-utils", + "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -765,10 +1030,13 @@ dependencies = [ "pallet-aura", "pallet-authorship", "pallet-balances", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-bridge-relayers", "pallet-collator-selection", "pallet-multisig", "pallet-session", - "pallet-sudo", "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", @@ -803,6 +1071,65 @@ dependencies = [ "xcm-executor", ] +[[package]] +name = "bridge-hub-test-utils" +version = "0.1.0" +dependencies = [ + "bp-messages", + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-test-relay-sproof-builder", + "frame-support", + "frame-system", + "pallet-xcm", + "pallet-xcm-benchmarks", + "parachain-info", + "parachains-common", + "polkadot-parachain", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "bridge-runtime-common" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-messages", + "bp-parachains", + "bp-polkadot-core", + "bp-rialto", + "bp-runtime", + "bp-test-utils", + "frame-support", + "frame-system", + "hash-db", + "log", + "millau-runtime", + "pallet-balances", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-bridge-relayers", + "pallet-transaction-payment", + "pallet-utility", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-trie", + "static_assertions", + "xcm", + "xcm-builder", + "xcm-executor", +] + [[package]] name = "bs58" version = "0.4.0" @@ -2889,6 +3216,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "atty", + "humantime 2.1.0", + "log", + "regex", + "termcolor", +] + [[package]] name = "env_logger" version = "0.9.0" @@ -2929,6 +3269,33 @@ dependencies = [ "libc", ] +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash 0.8.0", + "impl-rlp", + "impl-serde 0.4.0", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom", + "fixed-hash 0.8.0", + "impl-rlp", + "impl-serde 0.4.0", + "primitive-types", + "uint", +] + [[package]] name = "event-listener" version = "2.5.1" @@ -3072,6 +3439,18 @@ dependencies = [ "scale-info", ] +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fixed-hash" version = "0.8.0" @@ -4011,6 +4390,24 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + [[package]] name = "impl-serde" version = "0.4.0" @@ -5210,6 +5607,68 @@ dependencies = [ "thrift", ] +[[package]] +name = "millau-runtime" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-millau", + "bp-parachains", + "bp-polkadot-core", + "bp-relayers", + "bp-rialto", + "bp-rialto-parachain", + "bp-runtime", + "bp-westend", + "bridge-runtime-common", + "env_logger 0.8.4", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "hex-literal", + "pallet-aura", + "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-bridge-relayers", + "pallet-grandpa", + "pallet-mmr", + "pallet-randomness-collective-flip", + "pallet-session", + "pallet-shift-session-manager", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-utility", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-beefy", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std", + "sp-transaction-pool", + "sp-version", + "static_assertions", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -5975,6 +6434,93 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-bridge-grandpa" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-runtime", + "bp-test-utils", + "finality-grandpa", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-finality-grandpa", + "sp-io", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "pallet-bridge-messages" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-runtime", + "bp-test-utils", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "num-traits", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-bridge-parachains" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-parachains", + "bp-polkadot-core", + "bp-runtime", + "bp-test-utils", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-bridge-grandpa", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-trie", +] + +[[package]] +name = "pallet-bridge-relayers" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-relayers", + "bp-runtime", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" @@ -6593,6 +7139,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-shift-session-manager" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-staking", + "sp-std", +] + [[package]] name = "pallet-society" version = "4.0.0-dev" @@ -7097,6 +7658,35 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" +[[package]] +name = "parity-util-mem" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d32c34f4f5ca7f9196001c0aba5a1f9a5a12382c8944b8b0f90233282d1e8f8" +dependencies = [ + "cfg-if", + "ethereum-types", + "hashbrown", + "impl-trait-for-tuples", + "lru", + "parity-util-mem-derive", + "parking_lot 0.12.1", + "primitive-types", + "smallvec", + "winapi", +] + +[[package]] +name = "parity-util-mem-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" +dependencies = [ + "proc-macro2", + "syn", + "synstructure", +] + [[package]] name = "parity-wasm" version = "0.45.0" @@ -8881,9 +9471,10 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cfd65aea0c5fa0bfcc7c9e7ca828c921ef778f43d325325ec84bda371bfa75a" dependencies = [ - "fixed-hash", + "fixed-hash 0.8.0", "impl-codec", - "impl-serde", + "impl-rlp", + "impl-serde 0.4.0", "scale-info", "uint", ] @@ -9397,6 +9988,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + [[package]] name = "rocksdb" version = "0.19.0" @@ -11534,7 +12135,7 @@ dependencies = [ "futures", "hash-db", "hash256-std-hasher", - "impl-serde", + "impl-serde 0.4.0", "lazy_static", "libsecp256k1", "log", @@ -11890,7 +12491,7 @@ name = "sp-storage" version = "7.0.0" source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ - "impl-serde", + "impl-serde 0.4.0", "parity-scale-codec", "ref-cast", "serde", @@ -11978,7 +12579,7 @@ name = "sp-version" version = "5.0.0" source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ - "impl-serde", + "impl-serde 0.4.0", "parity-scale-codec", "parity-wasm", "scale-info", @@ -12726,6 +13327,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinytemplate" version = "1.2.1" From 7c1752c33b8e6e5ac40d035f18906ff2d3ae9b31 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Thu, 19 Jan 2023 14:01:34 +0200 Subject: [PATCH 182/263] Fix BridgePalletIsNotInitialized (#2114) --- parachains/runtimes/bridge-hubs/README.md | 8 ++++---- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 15 +++++++-------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index a39d6e8dc91..f6adc67dabe 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -126,20 +126,20 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ~/local_bridge_testing/bin/substrate-relay relay-headers-and-messages bridge-hub-rococo-bridge-hub-wococo \ --rococo-host localhost \ --rococo-port 9942 \ - --rococo-version-mode Auto \ + --rococo-version-mode Auto \ --bridge-hub-rococo-host localhost \ --bridge-hub-rococo-port 8943 \ - --bridge-hub-rococo-version-mode Auto \ + --bridge-hub-rococo-version-mode Auto \ --bridge-hub-rococo-signer //Charlie \ --wococo-headers-to-bridge-hub-rococo-signer //Bob \ --wococo-parachains-to-bridge-hub-rococo-signer //Bob \ --bridge-hub-rococo-transactions-mortality 4 \ --wococo-host localhost \ --wococo-port 9945 \ - --wococo-version-mode Auto \ + --wococo-version-mode Auto \ --bridge-hub-wococo-host localhost \ --bridge-hub-wococo-port 8945 \ - --bridge-hub-wococo-version-mode Auto \ + --bridge-hub-wococo-version-mode Auto \ --bridge-hub-wococo-signer //Charlie \ --rococo-headers-to-bridge-hub-wococo-signer //Bob \ --rococo-parachains-to-bridge-hub-wococo-signer //Bob \ diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 6897d86d913..d18f25b4e19 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -30,7 +30,6 @@ mod weights; pub mod xcm_config; use bridge_common_config::*; -use codec::Decode; use constants::currency::*; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use sp_api::impl_runtime_apis; @@ -64,7 +63,7 @@ pub use sp_runtime::{MultiAddress, Perbill, Permill}; use xcm_config::{XcmConfig, XcmOriginToTransactDispatchOrigin}; use bp_parachains::SingleParaStoredHeaderDataBuilder; -use bp_runtime::{HeaderId, HeaderIdProvider}; +use bp_runtime::HeaderId; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -722,17 +721,17 @@ impl_runtime_apis! { impl bp_bridge_hub_rococo::BridgeHubRococoFinalityApi for Runtime { fn best_finalized() -> Option> { - let encoded_head = BridgeRococoParachain::best_parachain_head(bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID.into())?; - let head = bp_bridge_hub_rococo::Header::decode(&mut &encoded_head.0[..]).ok()?; - Some(head.id()) + BridgeRococoParachain::best_parachain_head_id::< + bp_bridge_hub_rococo::BridgeHubRococo + >().unwrap_or(None) } } impl bp_bridge_hub_wococo::BridgeHubWococoFinalityApi for Runtime { fn best_finalized() -> Option> { - let encoded_head = BridgeWococoParachain::best_parachain_head(bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID.into())?; - let head = bp_bridge_hub_wococo::Header::decode(&mut &encoded_head.0[..]).ok()?; - Some(head.id()) + BridgeWococoParachain::best_parachain_head_id::< + bp_bridge_hub_wococo::BridgeHubWococo + >().unwrap_or(None) } } From 2f1438c10a9063e285dfa3964bb685624154c88c Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 19 Jan 2023 20:22:14 +0100 Subject: [PATCH 183/263] Squashed 'bridges/' changes from fb3c5ef5d..e2e9fa7f9 e2e9fa7f9 Expose EXTRA_STORAGE_PROOF_SIZE in bp-bridge-hub-cumulus (#1788) ba85532b1 Removed unecesserry test + substrate/polkadot (#1787) git-subtree-dir: bridges git-subtree-split: e2e9fa7f94d2f105c1816402a9ae4b85bfc34145 --- Cargo.lock | 460 +++++++++--------- .../chain-bridge-hub-cumulus/src/lib.rs | 2 +- primitives/test-utils/Cargo.toml | 3 - primitives/test-utils/src/lib.rs | 44 -- 4 files changed, 231 insertions(+), 278 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index acc05e76643..c263136d4c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -545,7 +545,7 @@ dependencies = [ [[package]] name = "beefy-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -579,7 +579,7 @@ dependencies = [ [[package]] name = "beefy-gadget-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "beefy-gadget", "futures", @@ -598,7 +598,7 @@ dependencies = [ [[package]] name = "beefy-merkle-tree" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "sp-api", "sp-beefy", @@ -1025,7 +1025,6 @@ dependencies = [ "sp-finality-grandpa", "sp-runtime", "sp-std", - "xcm", ] [[package]] @@ -3101,7 +3100,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", ] @@ -3124,7 +3123,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "frame-system", @@ -3147,7 +3146,7 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "Inflector", "array-bytes 4.2.0", @@ -3194,7 +3193,7 @@ dependencies = [ [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.49", @@ -3205,7 +3204,7 @@ dependencies = [ [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-election-provider-solution-type", "frame-support", @@ -3222,7 +3221,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "frame-system", @@ -3250,7 +3249,7 @@ dependencies = [ [[package]] name = "frame-remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "futures", "log", @@ -3266,7 +3265,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "bitflags", "frame-metadata", @@ -3298,7 +3297,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "Inflector", "cfg-expr", @@ -3312,7 +3311,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", @@ -3324,7 +3323,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "proc-macro2 1.0.49", "quote 1.0.23", @@ -3334,7 +3333,7 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "log", @@ -3352,7 +3351,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -3367,7 +3366,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "sp-api", @@ -3376,7 +3375,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "parity-scale-codec", @@ -4501,7 +4500,7 @@ dependencies = [ [[package]] name = "kusama-runtime" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "bitvec", "frame-election-provider-support", @@ -4590,7 +4589,7 @@ dependencies = [ [[package]] name = "kusama-runtime-constants" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "frame-support", "polkadot-primitives", @@ -5560,7 +5559,7 @@ dependencies = [ [[package]] name = "mmr-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "futures", "log", @@ -5579,7 +5578,7 @@ dependencies = [ [[package]] name = "mmr-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "anyhow", "jsonrpsee 0.16.2", @@ -5824,7 +5823,7 @@ dependencies = [ [[package]] name = "node-inspect" version = "0.9.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "clap 4.1.0", "parity-scale-codec", @@ -6108,7 +6107,7 @@ dependencies = [ [[package]] name = "pallet-aura" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "frame-system", @@ -6124,7 +6123,7 @@ dependencies = [ [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "frame-system", @@ -6140,7 +6139,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "frame-system", @@ -6155,7 +6154,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6179,7 +6178,7 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6199,7 +6198,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6214,7 +6213,7 @@ dependencies = [ [[package]] name = "pallet-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "frame-system", @@ -6230,7 +6229,7 @@ dependencies = [ [[package]] name = "pallet-beefy-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "array-bytes 4.2.0", "beefy-merkle-tree", @@ -6253,7 +6252,7 @@ dependencies = [ [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6382,7 +6381,7 @@ dependencies = [ [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6401,7 +6400,7 @@ dependencies = [ [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6418,7 +6417,7 @@ dependencies = [ [[package]] name = "pallet-conviction-voting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "assert_matches", "frame-benchmarking", @@ -6435,7 +6434,7 @@ dependencies = [ [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6453,7 +6452,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6476,7 +6475,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-support-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6489,7 +6488,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6507,7 +6506,7 @@ dependencies = [ [[package]] name = "pallet-fast-unstake" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6525,7 +6524,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6548,7 +6547,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "enumflags2", "frame-benchmarking", @@ -6564,7 +6563,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6584,7 +6583,7 @@ dependencies = [ [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6601,7 +6600,7 @@ dependencies = [ [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6618,7 +6617,7 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6635,7 +6634,7 @@ dependencies = [ [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6651,7 +6650,7 @@ dependencies = [ [[package]] name = "pallet-nis" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6667,7 +6666,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "frame-system", @@ -6684,7 +6683,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "sp-api", @@ -6694,7 +6693,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "frame-system", @@ -6711,7 +6710,7 @@ dependencies = [ [[package]] name = "pallet-preimage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6728,7 +6727,7 @@ dependencies = [ [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6743,7 +6742,7 @@ dependencies = [ [[package]] name = "pallet-randomness-collective-flip" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "frame-system", @@ -6757,7 +6756,7 @@ dependencies = [ [[package]] name = "pallet-ranked-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6775,7 +6774,7 @@ dependencies = [ [[package]] name = "pallet-recovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6790,7 +6789,7 @@ dependencies = [ [[package]] name = "pallet-referenda" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6808,7 +6807,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6825,7 +6824,7 @@ dependencies = [ [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "frame-system", @@ -6861,7 +6860,7 @@ dependencies = [ [[package]] name = "pallet-society" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "frame-system", @@ -6875,7 +6874,7 @@ dependencies = [ [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6897,7 +6896,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.49", @@ -6908,7 +6907,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "log", "sp-arithmetic", @@ -6917,7 +6916,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "frame-system", @@ -6931,7 +6930,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6949,7 +6948,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -6968,7 +6967,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-support", "frame-system", @@ -6984,7 +6983,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "jsonrpsee 0.16.2", "pallet-transaction-payment-rpc-runtime-api", @@ -7000,7 +6999,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -7012,7 +7011,7 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -7029,7 +7028,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -7045,7 +7044,7 @@ dependencies = [ [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -7060,7 +7059,7 @@ dependencies = [ [[package]] name = "pallet-whitelist" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-benchmarking", "frame-support", @@ -7075,7 +7074,7 @@ dependencies = [ [[package]] name = "pallet-xcm" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "frame-benchmarking", "frame-support", @@ -7438,7 +7437,7 @@ checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" [[package]] name = "polkadot-approval-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "futures", "polkadot-node-metrics", @@ -7453,7 +7452,7 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "futures", "polkadot-node-network-protocol", @@ -7467,7 +7466,7 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "derive_more", "fatality", @@ -7490,7 +7489,7 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "fatality", "futures", @@ -7511,7 +7510,7 @@ dependencies = [ [[package]] name = "polkadot-cli" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "clap 4.1.0", "frame-benchmarking-cli", @@ -7538,7 +7537,7 @@ dependencies = [ [[package]] name = "polkadot-client" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "async-trait", "frame-benchmarking", @@ -7580,7 +7579,7 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "always-assert", "bitvec", @@ -7602,7 +7601,7 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "parity-scale-codec", "scale-info", @@ -7614,7 +7613,7 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "derive_more", "fatality", @@ -7639,7 +7638,7 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", @@ -7653,7 +7652,7 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "futures", "futures-timer", @@ -7673,7 +7672,7 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "always-assert", "async-trait", @@ -7697,7 +7696,7 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "futures", "parity-scale-codec", @@ -7715,7 +7714,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "bitvec", "derive_more", @@ -7744,7 +7743,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "bitvec", "futures", @@ -7764,7 +7763,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "bitvec", "fatality", @@ -7783,7 +7782,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "futures", "polkadot-node-subsystem", @@ -7798,7 +7797,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "async-trait", "futures", @@ -7817,7 +7816,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "futures", "polkadot-node-metrics", @@ -7832,7 +7831,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "futures", "futures-timer", @@ -7849,7 +7848,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "fatality", "futures", @@ -7868,7 +7867,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "async-trait", "futures", @@ -7885,7 +7884,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "bitvec", "fatality", @@ -7903,7 +7902,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "always-assert", "assert_matches", @@ -7935,7 +7934,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-checker" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "futures", "polkadot-node-primitives", @@ -7951,7 +7950,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "futures", "lru", @@ -7966,7 +7965,7 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "lazy_static", "log", @@ -7984,7 +7983,7 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "bs58", "futures", @@ -8003,7 +8002,7 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "async-trait", "derive_more", @@ -8026,7 +8025,7 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "bounded-vec", "futures", @@ -8048,7 +8047,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -8058,7 +8057,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "async-trait", "derive_more", @@ -8081,7 +8080,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "async-trait", "derive_more", @@ -8114,7 +8113,7 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "async-trait", "futures", @@ -8137,7 +8136,7 @@ dependencies = [ [[package]] name = "polkadot-parachain" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "derive_more", "frame-support", @@ -8153,7 +8152,7 @@ dependencies = [ [[package]] name = "polkadot-performance-test" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "env_logger 0.9.3", "kusama-runtime", @@ -8168,7 +8167,7 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "bitvec", "hex-literal", @@ -8194,7 +8193,7 @@ dependencies = [ [[package]] name = "polkadot-rpc" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", @@ -8226,7 +8225,7 @@ dependencies = [ [[package]] name = "polkadot-runtime" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "bitvec", "frame-election-provider-support", @@ -8308,7 +8307,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "bitvec", "frame-election-provider-support", @@ -8354,7 +8353,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-constants" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "frame-support", "polkadot-primitives", @@ -8368,7 +8367,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-metrics" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "bs58", "parity-scale-codec", @@ -8380,7 +8379,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "bitflags", "bitvec", @@ -8420,7 +8419,7 @@ dependencies = [ [[package]] name = "polkadot-service" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "async-trait", "beefy-gadget", @@ -8523,7 +8522,7 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "arrayvec 0.5.2", "fatality", @@ -8544,7 +8543,7 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -9845,7 +9844,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "log", "sp-core", @@ -9856,7 +9855,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "futures", @@ -9883,7 +9882,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "futures", "futures-timer", @@ -9906,7 +9905,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -9922,7 +9921,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "memmap2", "sc-chain-spec-derive", @@ -9937,7 +9936,7 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.49", @@ -9948,7 +9947,7 @@ dependencies = [ [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "array-bytes 4.2.0", "chrono", @@ -9988,7 +9987,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "fnv", "futures", @@ -10014,7 +10013,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "hash-db", "kvdb", @@ -10039,7 +10038,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "futures", @@ -10064,7 +10063,7 @@ dependencies = [ [[package]] name = "sc-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "futures", @@ -10093,7 +10092,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "fork-tree", @@ -10131,7 +10130,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "futures", "jsonrpsee 0.16.2", @@ -10153,7 +10152,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "fork-tree", "parity-scale-codec", @@ -10166,7 +10165,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "futures", @@ -10189,7 +10188,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "lru", "parity-scale-codec", @@ -10213,7 +10212,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", @@ -10226,7 +10225,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "log", "sc-allocator", @@ -10239,7 +10238,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "cfg-if 1.0.0", "libc", @@ -10256,7 +10255,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "ahash", "array-bytes 4.2.0", @@ -10296,7 +10295,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "finality-grandpa", "futures", @@ -10316,7 +10315,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "ansi_term", "futures", @@ -10331,7 +10330,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10346,7 +10345,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10388,7 +10387,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "cid", "futures", @@ -10407,7 +10406,7 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "bitflags", @@ -10433,7 +10432,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "ahash", "futures", @@ -10451,7 +10450,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10472,7 +10471,7 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10504,7 +10503,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10523,7 +10522,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "array-bytes 4.2.0", "bytes", @@ -10553,7 +10552,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "futures", "libp2p", @@ -10566,7 +10565,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -10575,7 +10574,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "futures", "jsonrpsee 0.16.2", @@ -10599,12 +10598,13 @@ dependencies = [ "sp-runtime", "sp-session", "sp-version", + "tokio", ] [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "jsonrpsee 0.16.2", "parity-scale-codec", @@ -10623,7 +10623,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "http", "jsonrpsee 0.16.2", @@ -10638,7 +10638,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10664,7 +10664,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "directories", @@ -10729,7 +10729,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "log", "parity-scale-codec", @@ -10740,7 +10740,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "jsonrpsee 0.16.2", "parity-scale-codec", @@ -10759,7 +10759,7 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "futures", "libc", @@ -10778,7 +10778,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "chrono", "futures", @@ -10797,7 +10797,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "ansi_term", "atty", @@ -10828,7 +10828,7 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.49", @@ -10839,7 +10839,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "futures", @@ -10866,7 +10866,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "futures", @@ -10880,7 +10880,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "backtrace", "futures", @@ -11262,7 +11262,7 @@ checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" [[package]] name = "slot-range-helper" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "enumn", "parity-scale-codec", @@ -11350,7 +11350,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "hash-db", "log", @@ -11368,7 +11368,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "blake2", "proc-macro-crate", @@ -11380,7 +11380,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "scale-info", @@ -11393,7 +11393,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "integer-sqrt", "num-traits", @@ -11407,7 +11407,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "scale-info", @@ -11420,7 +11420,7 @@ dependencies = [ [[package]] name = "sp-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "parity-scale-codec", @@ -11432,7 +11432,7 @@ dependencies = [ [[package]] name = "sp-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "scale-info", @@ -11449,7 +11449,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "sp-api", @@ -11461,7 +11461,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "futures", "log", @@ -11479,7 +11479,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "futures", @@ -11497,7 +11497,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "parity-scale-codec", @@ -11515,7 +11515,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "merlin", @@ -11538,7 +11538,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "scale-info", @@ -11550,7 +11550,7 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "scale-info", @@ -11563,7 +11563,7 @@ dependencies = [ [[package]] name = "sp-core" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "array-bytes 4.2.0", "base58", @@ -11605,7 +11605,7 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "blake2", "byteorder", @@ -11619,7 +11619,7 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "proc-macro2 1.0.49", "quote 1.0.23", @@ -11630,7 +11630,7 @@ dependencies = [ [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -11639,7 +11639,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "proc-macro2 1.0.49", "quote 1.0.23", @@ -11649,7 +11649,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "environmental", "parity-scale-codec", @@ -11660,7 +11660,7 @@ dependencies = [ [[package]] name = "sp-finality-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "finality-grandpa", "log", @@ -11678,7 +11678,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -11692,7 +11692,7 @@ dependencies = [ [[package]] name = "sp-io" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "bytes", "ed25519", @@ -11717,7 +11717,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "lazy_static", "sp-core", @@ -11728,7 +11728,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "futures", @@ -11745,7 +11745,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "thiserror", "zstd", @@ -11754,7 +11754,7 @@ dependencies = [ [[package]] name = "sp-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "ckb-merkle-mountain-range 0.5.2", "log", @@ -11772,7 +11772,7 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "scale-info", @@ -11786,7 +11786,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "sp-api", "sp-core", @@ -11796,7 +11796,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "backtrace", "lazy_static", @@ -11806,7 +11806,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "rustc-hash", "serde", @@ -11816,7 +11816,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "either", "hash256-std-hasher", @@ -11838,7 +11838,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -11856,7 +11856,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "Inflector", "proc-macro-crate", @@ -11868,7 +11868,7 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "scale-info", @@ -11882,7 +11882,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "scale-info", @@ -11894,7 +11894,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "hash-db", "log", @@ -11914,12 +11914,12 @@ dependencies = [ [[package]] name = "sp-std" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" [[package]] name = "sp-storage" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", @@ -11932,7 +11932,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "futures-timer", @@ -11947,7 +11947,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "sp-std", @@ -11959,7 +11959,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "sp-api", "sp-runtime", @@ -11968,7 +11968,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "log", @@ -11984,7 +11984,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "ahash", "hash-db", @@ -12007,7 +12007,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", @@ -12024,7 +12024,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "proc-macro2 1.0.49", @@ -12035,7 +12035,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "impl-trait-for-tuples", "log", @@ -12048,7 +12048,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "parity-scale-codec", "scale-info", @@ -12298,7 +12298,7 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "platforms 2.0.0", ] @@ -12306,7 +12306,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "frame-system-rpc-runtime-api", "futures", @@ -12325,7 +12325,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "hyper", "log", @@ -12440,7 +12440,7 @@ dependencies = [ [[package]] name = "substrate-rpc-client" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "async-trait", "jsonrpsee 0.16.2", @@ -12453,7 +12453,7 @@ dependencies = [ [[package]] name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "jsonrpsee 0.16.2", "log", @@ -12472,7 +12472,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "ansi_term", "build-helper", @@ -12961,7 +12961,7 @@ dependencies = [ [[package]] name = "tracing-gum" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "polkadot-node-jaeger", "polkadot-primitives", @@ -12972,7 +12972,7 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "expander 0.0.6", "proc-macro-crate", @@ -13102,7 +13102,7 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#7ff6fa2bfa47f288216fdc6e1585f69756c5ec75" +source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ "clap 4.1.0", "frame-remote-externalities", @@ -14251,7 +14251,7 @@ dependencies = [ [[package]] name = "xcm" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "derivative", "impl-trait-for-tuples", @@ -14267,7 +14267,7 @@ dependencies = [ [[package]] name = "xcm-builder" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "frame-support", "frame-system", @@ -14288,7 +14288,7 @@ dependencies = [ [[package]] name = "xcm-executor" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "environmental", "frame-support", @@ -14307,7 +14307,7 @@ dependencies = [ [[package]] name = "xcm-procedural" version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#2ac18997ab3289cc8f0261f56cd43021fb405ebd" +source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ "Inflector", "proc-macro2 1.0.49", diff --git a/primitives/chain-bridge-hub-cumulus/src/lib.rs b/primitives/chain-bridge-hub-cumulus/src/lib.rs index 286fbdbebc3..6eab5a53a04 100644 --- a/primitives/chain-bridge-hub-cumulus/src/lib.rs +++ b/primitives/chain-bridge-hub-cumulus/src/lib.rs @@ -20,7 +20,7 @@ use bp_messages::*; pub use bp_polkadot_core::{ AccountId, AccountInfoStorageMapKeyProvider, AccountPublic, Balance, BlockNumber, Hash, Hasher, Hashing, Header, Index, Nonce, Perbill, PolkadotSignedExtension, Signature, SignedBlock, - UncheckedExtrinsic, TX_EXTRA_BYTES, + UncheckedExtrinsic, EXTRA_STORAGE_PROOF_SIZE, TX_EXTRA_BYTES, }; use frame_support::{ dispatch::DispatchClass, diff --git a/primitives/test-utils/Cargo.toml b/primitives/test-utils/Cargo.toml index 2bc77e632e5..0a591334577 100644 --- a/primitives/test-utils/Cargo.toml +++ b/primitives/test-utils/Cargo.toml @@ -15,9 +15,6 @@ sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -[dev-dependencies] -xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } - [features] default = ["std"] std = [ diff --git a/primitives/test-utils/src/lib.rs b/primitives/test-utils/src/lib.rs index e0d06ce9970..c1e95ec6fef 100644 --- a/primitives/test-utils/src/lib.rs +++ b/primitives/test-utils/src/lib.rs @@ -299,47 +299,3 @@ macro_rules! generate_owned_bridge_module_tests { } }; } - -#[cfg(test)] -mod tests { - use codec::Encode; - use sp_application_crypto::sp_core::{hexdisplay, hexdisplay::HexDisplay}; - use xcm::VersionedXcm; - - fn print_xcm(xcm: &VersionedXcm) { - println!("-----------------"); - println!("xcm (plain): {xcm:?}"); - println!("xcm (bytes): {:?}", xcm.encode()); - println!("xcm (hex): {:?}", hexdisplay::HexDisplay::from(&xcm.encode())); - } - - fn as_hex(xcm: &VersionedXcm) -> String { - HexDisplay::from(&xcm.encode()).to_string() - } - - pub type RuntimeCall = (); - - #[test] - fn generate_versioned_xcm_message_hex_bytes() { - let xcm: xcm::v2::Xcm = xcm::v2::Xcm(vec![xcm::v2::Instruction::Trap(43)]); - let xcm: VersionedXcm = From::from(xcm); - print_xcm(&xcm); - assert_eq!("020419ac", format!("{}", as_hex(&xcm))); - - let xcm: xcm::v3::Xcm = vec![xcm::v3::Instruction::Trap(43)].into(); - let xcm: VersionedXcm = From::from(xcm); - print_xcm(&xcm); - assert_eq!("030419ac", format!("{}", as_hex(&xcm))); - - let xcm: xcm::v3::Xcm = vec![ - xcm::v3::Instruction::ClearError, - xcm::v3::Instruction::ClearTopic, - xcm::v3::Instruction::ClearTransactStatus, - xcm::v3::Instruction::Trap(43), - ] - .into(); - let xcm: VersionedXcm = From::from(xcm); - print_xcm(&xcm); - assert_eq!("0310172c2319ac", format!("{}", as_hex(&xcm))); - } -} From c15f226d60bd71e2411904857a48d1bae21adddb Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 19 Jan 2023 20:28:14 +0100 Subject: [PATCH 184/263] Fix cargo.toml --- Cargo.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 053a9c08386..36e62202076 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -910,7 +910,6 @@ dependencies = [ "sp-finality-grandpa", "sp-runtime", "sp-std", - "xcm", ] [[package]] From 791cf61fa60628fd287e84abb966356984bd9e1d Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Sat, 21 Jan 2023 21:06:03 +0100 Subject: [PATCH 185/263] Squashed 'bridges/' changes from e2e9fa7f9..d5f95c14a d5f95c14a use wss to connect to the Rialto node in test deployments (#1809) 722d47b06 fix compilation a48732676 Bump sysinfo from 0.15.9 to 0.27.7 9a6e8bb1d Bump env_logger from 0.8.4 to 0.10.0 89c5e7981 Bump async-trait from 0.1.61 to 0.1.62 ddd0a5742 Bump fixed-hash from 0.7.0 to 0.8.0 e8b0b8192 Bump impl-serde from 0.3.2 to 0.4.0 c5227460f Bump jsonpath_lib from 0.2.6 to 0.3.0 9f4771d38 Bump parking_lot from 0.11.2 to 0.12.1 dab5d72ae Bump backoff from 0.2.1 to 0.4.0 d7dd3acce Bump strum from 0.21.0 to 0.24.1 c49799017 Bump tokio from 1.24.1 to 1.24.2 db614681e Bump clap from 4.1.0 to 4.1.1 3c155214d update ignored dependencies for dependabot (#1790) 4c791472c No wildcard patterns in Cargo.toml (#1789) 33632313d Remove chain-specific dependencies from crates that will be used in Cumulus (#1783) git-subtree-dir: bridges git-subtree-split: d5f95c14a2771d7ad51db95670d08a5d0da526a0 --- .github/dependabot.yml | 37 +- Cargo.lock | 207 +++------ Cargo.toml | 56 ++- bin/millau/node/Cargo.toml | 2 +- bin/millau/runtime/Cargo.toml | 2 +- bin/millau/runtime/src/lib.rs | 8 +- bin/millau/runtime/src/rialto_messages.rs | 51 +-- .../runtime/src/rialto_parachain_messages.rs | 64 +-- bin/rialto-parachain/node/Cargo.toml | 2 +- bin/rialto-parachain/runtime/src/lib.rs | 4 +- .../runtime/src/millau_messages.rs | 51 +-- bin/rialto/node/Cargo.toml | 2 +- bin/rialto/runtime/Cargo.toml | 2 +- bin/rialto/runtime/src/lib.rs | 4 +- bin/rialto/runtime/src/millau_messages.rs | 51 +-- bin/runtime-common/Cargo.toml | 5 +- bin/runtime-common/src/integrity.rs | 12 +- bin/runtime-common/src/lib.rs | 1 + bin/runtime-common/src/messages.rs | 311 ++++--------- .../src/messages_benchmarking.rs | 1 - bin/runtime-common/src/messages_extension.rs | 54 ++- bin/runtime-common/src/mock.rs | 422 ++++++++++++++++++ .../src/refund_relayer_extension.rs | 95 ++-- .../bridges/rialto-millau/docker-compose.yml | 2 +- .../relay-millau-rialto-entrypoint.sh | 5 +- deployments/networks/rialto.yml | 2 + fuzz/storage-proof/Cargo.toml | 2 +- modules/relayers/Cargo.toml | 1 + primitives/chain-millau/Cargo.toml | 4 +- primitives/messages/src/target_chain.rs | 18 +- primitives/relayers/Cargo.toml | 1 - primitives/relayers/src/lib.rs | 17 +- relays/bin-substrate/Cargo.toml | 2 +- relays/bin-substrate/src/chains/mod.rs | 6 +- relays/client-substrate/Cargo.toml | 2 +- relays/finality/Cargo.toml | 4 +- relays/messages/Cargo.toml | 2 +- relays/parachains/Cargo.toml | 2 +- relays/utils/Cargo.toml | 10 +- relays/utils/src/metrics/global.rs | 4 +- scripts/verify-partial-repo-build.sh | 80 ++++ 41 files changed, 905 insertions(+), 703 deletions(-) create mode 100644 bin/runtime-common/src/mock.rs create mode 100755 scripts/verify-partial-repo-build.sh diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a06d573703d..f53101109c5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,9 +8,19 @@ updates: timezone: Europe/Berlin open-pull-requests-limit: 20 ignore: + # Substrate (+ Polkadot/Cumulus pallets) dependencies + - dependency-name: beefy-* + versions: + - ">= 0" - dependency-name: frame-* versions: - ">= 0" + - dependency-name: fork-tree + versions: + - ">= 0" + - dependency-name: mmr-* + versions: + - ">= 0" - dependency-name: node-inspect versions: - ">= 0" @@ -26,17 +36,24 @@ updates: - dependency-name: substrate-* versions: - ">= 0" - - dependency-name: vergen + - dependency-name: try-runtime-cli versions: - - 4.0.1 - - 4.0.2 - - 4.1.0 - - 4.2.0 - - dependency-name: jsonrpc-core + - ">= 0" + # Polkadot dependencies + - dependency-name: kusama-* versions: - - 17.0.0 - - dependency-name: finality-grandpa + - ">= 0" + - dependency-name: polkadot-* versions: - - 0.13.0 - - 0.14.0 + - ">= 0" + - dependency-name: xcm-* + versions: + - ">= 0" + # Cumulus dependencies + - dependency-name: cumulus-* + versions: + - ">= 0" + - dependency-name: parachain-info + versions: + - ">= 0" rebase-strategy: disabled diff --git a/Cargo.lock b/Cargo.lock index c263136d4c9..9545c17c715 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -209,12 +209,6 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22f72e9d6fac4bc80778ea470b20197b88d28c292bb7d60c3fb099280003cd19" -[[package]] -name = "array_tool" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271" - [[package]] name = "arrayref" version = "0.3.6" @@ -427,9 +421,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.61" +version = "0.1.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" +checksum = "689894c2db1ea643a50834b999abf1c110887402542955ff5451dab8f861f9ed" dependencies = [ "proc-macro2 1.0.49", "quote 1.0.23", @@ -474,12 +468,13 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backoff" -version = "0.2.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721c249ab59cbc483ad4294c9ee2671835c1e43e9ffc277e6b4ecfef733cfdc5" +checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" dependencies = [ + "getrandom 0.2.8", "instant", - "rand 0.7.3", + "rand 0.8.5", ] [[package]] @@ -878,12 +873,12 @@ dependencies = [ "bp-beefy", "bp-messages", "bp-runtime", - "fixed-hash 0.7.0", + "fixed-hash", "frame-support", "frame-system", "hash256-std-hasher", "impl-codec", - "impl-serde 0.3.2", + "impl-serde", "parity-util-mem", "scale-info", "serde", @@ -943,7 +938,6 @@ name = "bp-relayers" version = "0.1.0" dependencies = [ "bp-messages", - "bp-rialto", "bp-runtime", "frame-support", "hex", @@ -1055,14 +1049,12 @@ dependencies = [ "bp-messages", "bp-parachains", "bp-polkadot-core", - "bp-rialto", "bp-runtime", "bp-test-utils", "frame-support", "frame-system", "hash-db", "log", - "millau-runtime", "pallet-balances", "pallet-bridge-grandpa", "pallet-bridge-messages", @@ -1361,9 +1353,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.0" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa91278560fc226a5d9d736cc21e485ff9aad47d26b8ffe1f54cba868b684b9f" +checksum = "4ec7a4128863c188deefe750ac1d1dfe66c236909f845af04beed823638dc1b2" dependencies = [ "bitflags", "clap_derive", @@ -1424,8 +1416,8 @@ version = "6.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e7b787b0dc42e8111badfdbe4c3059158ccb2db8780352fa1b01e8ccf45cc4d" dependencies = [ - "strum 0.24.1", - "strum_macros 0.24.3", + "strum", + "strum_macros", "unicode-width", ] @@ -1787,7 +1779,7 @@ name = "cumulus-client-cli" version = "0.1.0" source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" dependencies = [ - "clap 4.1.0", + "clap 4.1.1", "parity-scale-codec", "sc-chain-spec", "sc-cli", @@ -2591,12 +2583,6 @@ dependencies = [ "syn 1.0.107", ] -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "downcast" version = "0.11.0" @@ -2771,32 +2757,6 @@ dependencies = [ "syn 1.0.107", ] -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime 1.3.0", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "env_logger" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" -dependencies = [ - "atty", - "humantime 2.1.0", - "log", - "regex", - "termcolor", -] - [[package]] name = "env_logger" version = "0.9.3" @@ -2804,7 +2764,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" dependencies = [ "atty", - "humantime 2.1.0", + "humantime", "log", "regex", "termcolor", @@ -2816,7 +2776,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ - "humantime 2.1.0", + "humantime", "is-terminal", "log", "regex", @@ -2857,9 +2817,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" dependencies = [ "crunchy", - "fixed-hash 0.8.0", + "fixed-hash", "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "tiny-keccak", ] @@ -2870,9 +2830,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ "ethbloom", - "fixed-hash 0.8.0", + "fixed-hash", "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "primitive-types", "uint", ] @@ -3037,22 +2997,10 @@ dependencies = [ "futures", "log", "num-traits", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "relay-utils", ] -[[package]] -name = "fixed-hash" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" -dependencies = [ - "byteorder", - "rand 0.8.5", - "rustc-hex", - "static_assertions", -] - [[package]] name = "fixed-hash" version = "0.8.0" @@ -3151,7 +3099,7 @@ dependencies = [ "Inflector", "array-bytes 4.2.0", "chrono", - "clap 4.1.0", + "clap 4.1.1", "comfy-table", "frame-benchmarking", "frame-support", @@ -3897,15 +3845,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - [[package]] name = "humantime" version = "2.1.0" @@ -4049,15 +3988,6 @@ dependencies = [ "rlp", ] -[[package]] -name = "impl-serde" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" -dependencies = [ - "serde", -] - [[package]] name = "impl-serde" version = "0.4.0" @@ -4246,12 +4176,10 @@ dependencies = [ [[package]] name = "jsonpath_lib" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61352ec23883402b7d30b3313c16cbabefb8907361c4eb669d990cbb87ceee5a" +checksum = "eaa63191d68230cccb81c5aa23abd53ed64d83337cacbb25a7b8c7979523774f" dependencies = [ - "array_tool", - "env_logger 0.7.1", "log", "serde", "serde_json", @@ -5407,7 +5335,7 @@ dependencies = [ "hex", "log", "num-traits", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "relay-utils", "sp-arithmetic", ] @@ -5429,7 +5357,7 @@ version = "0.1.0" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", - "clap 4.1.0", + "clap 4.1.1", "frame-benchmarking", "frame-benchmarking-cli", "jsonrpsee 0.16.2", @@ -5475,7 +5403,7 @@ dependencies = [ "bp-runtime", "bp-westend", "bridge-runtime-common", - "env_logger 0.8.4", + "env_logger 0.10.0", "frame-benchmarking", "frame-executive", "frame-support", @@ -5825,7 +5753,7 @@ name = "node-inspect" version = "0.9.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ - "clap 4.1.0", + "clap 4.1.1", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -5861,9 +5789,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "ntapi" -version = "0.3.7" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc" dependencies = [ "winapi", ] @@ -6469,7 +6397,7 @@ dependencies = [ "sp-npos-elections", "sp-runtime", "sp-std", - "strum 0.24.1", + "strum", ] [[package]] @@ -7512,7 +7440,7 @@ name = "polkadot-cli" version = "0.9.33" source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" dependencies = [ - "clap 4.1.0", + "clap 4.1.1", "frame-benchmarking-cli", "futures", "log", @@ -8017,7 +7945,7 @@ dependencies = [ "sc-authority-discovery", "sc-network", "sc-network-common", - "strum 0.24.1", + "strum", "thiserror", "tracing-gum", ] @@ -8650,10 +8578,10 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" dependencies = [ - "fixed-hash 0.8.0", + "fixed-hash", "impl-codec", "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "scale-info", "uint", ] @@ -9324,7 +9252,7 @@ dependencies = [ "async-trait", "backoff", "bp-runtime", - "env_logger 0.8.4", + "env_logger 0.10.0", "futures", "isahc", "jsonpath_lib", @@ -9393,7 +9321,7 @@ dependencies = [ name = "rialto-bridge-node" version = "0.1.0" dependencies = [ - "clap 4.1.0", + "clap 4.1.1", "frame-benchmarking", "frame-benchmarking-cli", "frame-support", @@ -9420,7 +9348,7 @@ dependencies = [ name = "rialto-parachain-collator" version = "0.1.0" dependencies = [ - "clap 4.1.0", + "clap 4.1.1", "cumulus-client-cli", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", @@ -9535,7 +9463,7 @@ dependencies = [ "bp-rialto", "bp-runtime", "bridge-runtime-common", - "env_logger 0.8.4", + "env_logger 0.10.0", "frame-benchmarking", "frame-executive", "frame-support", @@ -9951,7 +9879,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa52 dependencies = [ "array-bytes 4.2.0", "chrono", - "clap 4.1.0", + "clap 4.1.1", "fdlimit", "futures", "libp2p", @@ -11574,7 +11502,7 @@ dependencies = [ "futures", "hash-db", "hash256-std-hasher", - "impl-serde 0.4.0", + "impl-serde", "lazy_static", "libsecp256k1", "log", @@ -11722,7 +11650,7 @@ dependencies = [ "lazy_static", "sp-core", "sp-runtime", - "strum 0.24.1", + "strum", ] [[package]] @@ -11921,7 +11849,7 @@ name = "sp-storage" version = "7.0.0" source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ - "impl-serde 0.4.0", + "impl-serde", "parity-scale-codec", "ref-cast", "serde", @@ -12009,7 +11937,7 @@ name = "sp-version" version = "5.0.0" source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ - "impl-serde 0.4.0", + "impl-serde", "parity-scale-codec", "parity-wasm", "scale-info", @@ -12174,7 +12102,7 @@ name = "storage-proof-fuzzer" version = "0.1.0" dependencies = [ "bp-runtime", - "env_logger 0.8.4", + "env_logger 0.10.0", "honggfuzz", "log", "sp-core", @@ -12220,34 +12148,13 @@ dependencies = [ "syn 1.0.107", ] -[[package]] -name = "strum" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" -dependencies = [ - "strum_macros 0.21.1", -] - [[package]] name = "strum" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" dependencies = [ - "strum_macros 0.24.3", -] - -[[package]] -name = "strum_macros" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" -dependencies = [ - "heck 0.3.3", - "proc-macro2 1.0.49", - "quote 1.0.23", - "syn 1.0.107", + "strum_macros", ] [[package]] @@ -12385,7 +12292,7 @@ dependencies = [ "sp-keyring", "sp-runtime", "structopt", - "strum 0.21.0", + "strum", "substrate-relay-helper", "tempfile", "xcm", @@ -12479,7 +12386,7 @@ dependencies = [ "cargo_metadata", "filetime", "sp-maybe-compressed-blob", - "strum 0.24.1", + "strum", "tempfile", "toml", "walkdir", @@ -12537,14 +12444,12 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.15.9" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de94457a09609f33fec5e7fceaf907488967c6c7c75d64da6a7ce6ffdb8b5abd" +checksum = "975fe381e0ecba475d4acff52466906d95b153a40324956552e027b2a9eaa89e" dependencies = [ - "cc", "cfg-if 1.0.0", "core-foundation-sys", - "doc-comment", "libc", "ntapi", "once_cell", @@ -12797,9 +12702,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.24.1" +version = "1.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" +checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" dependencies = [ "autocfg", "bytes", @@ -13104,7 +13009,7 @@ name = "try-runtime-cli" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" dependencies = [ - "clap 4.1.0", + "clap 4.1.1", "frame-remote-externalities", "hex", "log", @@ -13160,9 +13065,9 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "digest 0.10.6", - "rand 0.8.5", + "rand 0.7.3", "static_assertions", ] @@ -13460,8 +13365,8 @@ checksum = "b68e8037b4daf711393f4be2056246d12d975651b14d581520ad5d1f19219cec" dependencies = [ "anyhow", "libc", - "strum 0.24.1", - "strum_macros 0.24.3", + "strum", + "strum_macros", "tempfile", "thiserror", "wasm-opt-cxx-sys", diff --git a/Cargo.toml b/Cargo.toml index 1090a0fe5ba..5e06145e079 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,10 +2,54 @@ resolver = "2" members = [ - "bin/*/node", - "bin/*/runtime", - "fuzz/*", - "modules/*", - "primitives/*", - "relays/*", + "bin/millau/node", + "bin/millau/runtime", + "bin/rialto/node", + "bin/rialto/runtime", + "bin/rialto-parachain/node", + "bin/rialto-parachain/runtime", + "bin/runtime-common", + "fuzz/storage-proof", + "modules/beefy", + "modules/grandpa", + "modules/messages", + "modules/parachains", + "modules/relayers", + "modules/shift-session-manager", + "primitives/beefy", + "primitives/chain-bridge-hub-cumulus", + "primitives/chain-bridge-hub-rococo", + "primitives/chain-bridge-hub-wococo", + "primitives/chain-kusama", + "primitives/chain-millau", + "primitives/chain-polkadot", + "primitives/chain-rialto", + "primitives/chain-rialto-parachain", + "primitives/chain-rococo", + "primitives/chain-westend", + "primitives/chain-wococo", + "primitives/header-chain", + "primitives/messages", + "primitives/parachains", + "primitives/polkadot-core", + "primitives/relayers", + "primitives/runtime", + "primitives/test-utils", + "relays/bin-substrate", + "relays/client-bridge-hub-rococo", + "relays/client-bridge-hub-wococo", + "relays/client-kusama", + "relays/client-millau", + "relays/client-polkadot", + "relays/client-rialto", + "relays/client-rialto-parachain", + "relays/client-rococo", + "relays/client-substrate", + "relays/client-westend", + "relays/client-wococo", + "relays/finality", + "relays/lib-substrate-relay", + "relays/messages", + "relays/parachains", + "relays/utils", ] diff --git a/bin/millau/node/Cargo.toml b/bin/millau/node/Cargo.toml index 08661951eed..508f5714a58 100644 --- a/bin/millau/node/Cargo.toml +++ b/bin/millau/node/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.1.1", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } serde_json = "1.0.79" diff --git a/bin/millau/runtime/Cargo.toml b/bin/millau/runtime/Cargo.toml index 0e5868f845e..07e3e135aca 100644 --- a/bin/millau/runtime/Cargo.toml +++ b/bin/millau/runtime/Cargo.toml @@ -71,7 +71,7 @@ xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "maste [dev-dependencies] bridge-runtime-common = { path = "../../runtime-common", features = ["integrity-test"] } -env_logger = "0.8" +env_logger = "0.10" static_assertions = "1.1" [build-dependencies] diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index 6c401b9a94b..5aba458216d 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -473,7 +473,7 @@ impl pallet_bridge_messages::Config for Runtime { type InboundRelayer = bp_rialto::AccountId; type DeliveryPayments = (); - type TargetHeaderChain = crate::rialto_messages::Rialto; + type TargetHeaderChain = crate::rialto_messages::RialtoAsTargetHeaderChain; type LaneMessageVerifier = crate::rialto_messages::ToRialtoMessageVerifier; type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< Runtime, @@ -481,7 +481,7 @@ impl pallet_bridge_messages::Config for Runtime { frame_support::traits::ConstU64<10_000>, >; - type SourceHeaderChain = crate::rialto_messages::Rialto; + type SourceHeaderChain = crate::rialto_messages::RialtoAsSourceHeaderChain; type MessageDispatch = crate::rialto_messages::FromRialtoMessageDispatch; type BridgedChainId = RialtoChainId; } @@ -504,7 +504,7 @@ impl pallet_bridge_messages::Config for Run type InboundRelayer = bp_rialto_parachain::AccountId; type DeliveryPayments = (); - type TargetHeaderChain = crate::rialto_parachain_messages::RialtoParachain; + type TargetHeaderChain = crate::rialto_parachain_messages::RialtoParachainAsTargetHeaderChain; type LaneMessageVerifier = crate::rialto_parachain_messages::ToRialtoParachainMessageVerifier; type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< Runtime, @@ -512,7 +512,7 @@ impl pallet_bridge_messages::Config for Run frame_support::traits::ConstU64<10_000>, >; - type SourceHeaderChain = crate::rialto_parachain_messages::RialtoParachain; + type SourceHeaderChain = crate::rialto_parachain_messages::RialtoParachainAsSourceHeaderChain; type MessageDispatch = crate::rialto_parachain_messages::FromRialtoParachainMessageDispatch; type BridgedChainId = RialtoParachainChainId; } diff --git a/bin/millau/runtime/src/rialto_messages.rs b/bin/millau/runtime/src/rialto_messages.rs index ac203a2f6dd..1bc361d5882 100644 --- a/bin/millau/runtime/src/rialto_messages.rs +++ b/bin/millau/runtime/src/rialto_messages.rs @@ -18,13 +18,11 @@ use crate::{RialtoGrandpaInstance, Runtime, RuntimeCall, RuntimeOrigin}; -use bp_messages::{ - source_chain::TargetHeaderChain, - target_chain::{ProvedMessages, SourceHeaderChain}, - InboundLaneData, LaneId, Message, MessageNonce, -}; +use bp_messages::{LaneId, MessageNonce}; use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; -use bridge_runtime_common::messages::{self, MessageBridge}; +use bridge_runtime_common::messages::{ + self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, +}; use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; /// Default lane that is used to send messages to Rialto. @@ -110,6 +108,10 @@ impl messages::ThisChainWithMessages for Millau { /// Rialto chain from message lane point of view. #[derive(RuntimeDebug, Clone, Copy)] pub struct Rialto; +/// Rialto as source header chain. +pub type RialtoAsSourceHeaderChain = SourceHeaderChainAdapter; +/// Rialto as target header chain. +pub type RialtoAsTargetHeaderChain = TargetHeaderChainAdapter; impl messages::UnderlyingChainProvider for Rialto { type Chain = bp_rialto::Rialto; @@ -121,43 +123,6 @@ impl messages::BridgedChainWithMessages for Rialto { } } -impl TargetHeaderChain for Rialto { - type Error = &'static str; - // The proof is: - // - hash of the header this proof has been created with; - // - the storage proof or one or several keys; - // - id of the lane we prove state of. - type MessagesDeliveryProof = ToRialtoMessagesDeliveryProof; - - fn verify_message(payload: &ToRialtoMessagePayload) -> Result<(), Self::Error> { - messages::source::verify_chain_message::(payload) - } - - fn verify_messages_delivery_proof( - proof: Self::MessagesDeliveryProof, - ) -> Result<(LaneId, InboundLaneData), Self::Error> { - messages::source::verify_messages_delivery_proof::(proof) - } -} - -impl SourceHeaderChain for Rialto { - type Error = &'static str; - // The proof is: - // - hash of the header this proof has been created with; - // - the storage proof or one or several keys; - // - id of the lane we prove messages for; - // - inclusive range of messages nonces that are proved. - type MessagesProof = FromRialtoMessagesProof; - - fn verify_messages_proof( - proof: Self::MessagesProof, - messages_count: u32, - ) -> Result, Self::Error> { - messages::target::verify_messages_proof::(proof, messages_count) - .map_err(Into::into) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/bin/millau/runtime/src/rialto_parachain_messages.rs b/bin/millau/runtime/src/rialto_parachain_messages.rs index 097e2798227..d19e4136408 100644 --- a/bin/millau/runtime/src/rialto_parachain_messages.rs +++ b/bin/millau/runtime/src/rialto_parachain_messages.rs @@ -18,13 +18,11 @@ use crate::{Runtime, RuntimeCall, RuntimeOrigin, WithRialtoParachainsInstance}; -use bp_messages::{ - source_chain::TargetHeaderChain, - target_chain::{ProvedMessages, SourceHeaderChain}, - InboundLaneData, LaneId, Message, MessageNonce, -}; +use bp_messages::{LaneId, MessageNonce}; use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID}; -use bridge_runtime_common::messages::{self, MessageBridge}; +use bridge_runtime_common::messages::{ + self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, +}; use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; /// Default lane that is used to send messages to Rialto parachain. @@ -53,14 +51,6 @@ pub type ToRialtoParachainMessageVerifier = pub type FromRialtoParachainMessagePayload = messages::target::FromBridgedChainMessagePayload; -/// Messages proof for RialtoParachain -> Millau messages. -type FromRialtoParachainMessagesProof = - messages::target::FromBridgedChainMessagesProof; - -/// Messages delivery proof for Millau -> RialtoParachain messages. -type ToRialtoParachainMessagesDeliveryProof = - messages::source::FromBridgedChainMessagesDeliveryProof; - /// Call-dispatch based message dispatch for RialtoParachain -> Millau messages. pub type FromRialtoParachainMessageDispatch = messages::target::FromBridgedChainMessageDispatch< WithRialtoParachainMessageBridge, @@ -115,6 +105,12 @@ impl messages::ThisChainWithMessages for Millau { /// RialtoParachain chain from message lane point of view. #[derive(RuntimeDebug, Clone, Copy)] pub struct RialtoParachain; +/// RialtoParachain as source header chain. +pub type RialtoParachainAsSourceHeaderChain = + SourceHeaderChainAdapter; +/// RialtoParachain as target header chain. +pub type RialtoParachainAsTargetHeaderChain = + TargetHeaderChainAdapter; impl messages::UnderlyingChainProvider for RialtoParachain { type Chain = bp_rialto_parachain::RialtoParachain; @@ -125,43 +121,3 @@ impl messages::BridgedChainWithMessages for RialtoParachain { true } } - -impl TargetHeaderChain for RialtoParachain { - type Error = &'static str; - // The proof is: - // - hash of the header this proof has been created with; - // - the storage proof or one or several keys; - // - id of the lane we prove state of. - type MessagesDeliveryProof = ToRialtoParachainMessagesDeliveryProof; - - fn verify_message(payload: &ToRialtoParachainMessagePayload) -> Result<(), Self::Error> { - messages::source::verify_chain_message::(payload) - } - - fn verify_messages_delivery_proof( - proof: Self::MessagesDeliveryProof, - ) -> Result<(LaneId, InboundLaneData), Self::Error> { - messages::source::verify_messages_delivery_proof::(proof) - } -} - -impl SourceHeaderChain for RialtoParachain { - type Error = &'static str; - // The proof is: - // - hash of the header this proof has been created with; - // - the storage proof or one or several keys; - // - id of the lane we prove messages for; - // - inclusive range of messages nonces that are proved. - type MessagesProof = FromRialtoParachainMessagesProof; - - fn verify_messages_proof( - proof: Self::MessagesProof, - messages_count: u32, - ) -> Result, Self::Error> { - messages::target::verify_messages_proof::( - proof, - messages_count, - ) - .map_err(Into::into) - } -} diff --git a/bin/rialto-parachain/node/Cargo.toml b/bin/rialto-parachain/node/Cargo.toml index 7b81de4fd3a..a6931291091 100644 --- a/bin/rialto-parachain/node/Cargo.toml +++ b/bin/rialto-parachain/node/Cargo.toml @@ -17,7 +17,7 @@ default = [] runtime-benchmarks = ['rialto-parachain-runtime/runtime-benchmarks'] [dependencies] -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.1.1", features = ["derive"] } log = '0.4.17' codec = { package = 'parity-scale-codec', version = '3.1.5' } serde = { version = '1.0', features = ['derive'] } diff --git a/bin/rialto-parachain/runtime/src/lib.rs b/bin/rialto-parachain/runtime/src/lib.rs index 5ad8506acbf..2bc787c4953 100644 --- a/bin/rialto-parachain/runtime/src/lib.rs +++ b/bin/rialto-parachain/runtime/src/lib.rs @@ -581,7 +581,7 @@ impl pallet_bridge_messages::Config for Runtime { type InboundRelayer = bp_millau::AccountId; type DeliveryPayments = (); - type TargetHeaderChain = crate::millau_messages::Millau; + type TargetHeaderChain = crate::millau_messages::MillauAsTargetHeaderChain; type LaneMessageVerifier = crate::millau_messages::ToMillauMessageVerifier; type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< Runtime, @@ -589,7 +589,7 @@ impl pallet_bridge_messages::Config for Runtime { frame_support::traits::ConstU128<100_000>, >; - type SourceHeaderChain = crate::millau_messages::Millau; + type SourceHeaderChain = crate::millau_messages::MillauAsSourceHeaderChain; type MessageDispatch = crate::millau_messages::FromMillauMessageDispatch; type BridgedChainId = BridgedChainId; } diff --git a/bin/rialto-parachain/runtime/src/millau_messages.rs b/bin/rialto-parachain/runtime/src/millau_messages.rs index e21cf76a82c..850ec1da60d 100644 --- a/bin/rialto-parachain/runtime/src/millau_messages.rs +++ b/bin/rialto-parachain/runtime/src/millau_messages.rs @@ -21,13 +21,11 @@ use crate::{MillauGrandpaInstance, Runtime, RuntimeCall, RuntimeOrigin}; -use bp_messages::{ - source_chain::TargetHeaderChain, - target_chain::{ProvedMessages, SourceHeaderChain}, - InboundLaneData, LaneId, Message, MessageNonce, -}; +use bp_messages::{LaneId, MessageNonce}; use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID}; -use bridge_runtime_common::messages::{self, MessageBridge}; +use bridge_runtime_common::messages::{ + self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, +}; use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; /// Default lane that is used to send messages to Millau. @@ -114,6 +112,10 @@ impl messages::ThisChainWithMessages for RialtoParachain { /// Millau chain from message lane point of view. #[derive(RuntimeDebug, Clone, Copy)] pub struct Millau; +/// Millau as source header chain. +pub type MillauAsSourceHeaderChain = SourceHeaderChainAdapter; +/// Millau as target header chain. +pub type MillauAsTargetHeaderChain = TargetHeaderChainAdapter; impl messages::UnderlyingChainProvider for Millau { type Chain = bp_millau::Millau; @@ -124,40 +126,3 @@ impl messages::BridgedChainWithMessages for Millau { true } } - -impl TargetHeaderChain for Millau { - type Error = &'static str; - // The proof is: - // - hash of the header this proof has been created with; - // - the storage proof of one or several keys; - // - id of the lane we prove state of. - type MessagesDeliveryProof = ToMillauMessagesDeliveryProof; - - fn verify_message(payload: &ToMillauMessagePayload) -> Result<(), Self::Error> { - messages::source::verify_chain_message::(payload) - } - - fn verify_messages_delivery_proof( - proof: Self::MessagesDeliveryProof, - ) -> Result<(LaneId, InboundLaneData), Self::Error> { - messages::source::verify_messages_delivery_proof::(proof) - } -} - -impl SourceHeaderChain for Millau { - type Error = &'static str; - // The proof is: - // - hash of the header this proof has been created with; - // - the storage proof of one or several keys; - // - id of the lane we prove messages for; - // - inclusive range of messages nonces that are proved. - type MessagesProof = FromMillauMessagesProof; - - fn verify_messages_proof( - proof: Self::MessagesProof, - messages_count: u32, - ) -> Result, Self::Error> { - messages::target::verify_messages_proof::(proof, messages_count) - .map_err(Into::into) - } -} diff --git a/bin/rialto/node/Cargo.toml b/bin/rialto/node/Cargo.toml index aec1870efd8..5c93a93e0a7 100644 --- a/bin/rialto/node/Cargo.toml +++ b/bin/rialto/node/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.0.9", features = ["derive"] } +clap = { version = "4.1.1", features = ["derive"] } serde_json = "1.0.79" # Bridge dependencies diff --git a/bin/rialto/runtime/Cargo.toml b/bin/rialto/runtime/Cargo.toml index b4d387e3829..d5d018fb864 100644 --- a/bin/rialto/runtime/Cargo.toml +++ b/bin/rialto/runtime/Cargo.toml @@ -69,7 +69,7 @@ xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "maste [dev-dependencies] bridge-runtime-common = { path = "../../runtime-common", features = ["integrity-test"] } -env_logger = "0.8" +env_logger = "0.10" static_assertions = "1.1" [build-dependencies] diff --git a/bin/rialto/runtime/src/lib.rs b/bin/rialto/runtime/src/lib.rs index 123cd477d97..d9b07d582fb 100644 --- a/bin/rialto/runtime/src/lib.rs +++ b/bin/rialto/runtime/src/lib.rs @@ -448,7 +448,7 @@ impl pallet_bridge_messages::Config for Runtime { type InboundRelayer = bp_millau::AccountId; type DeliveryPayments = (); - type TargetHeaderChain = crate::millau_messages::Millau; + type TargetHeaderChain = crate::millau_messages::MillauAsTargetHeaderChain; type LaneMessageVerifier = crate::millau_messages::ToMillauMessageVerifier; type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< Runtime, @@ -456,7 +456,7 @@ impl pallet_bridge_messages::Config for Runtime { frame_support::traits::ConstU128<100_000>, >; - type SourceHeaderChain = crate::millau_messages::Millau; + type SourceHeaderChain = crate::millau_messages::MillauAsSourceHeaderChain; type MessageDispatch = crate::millau_messages::FromMillauMessageDispatch; type BridgedChainId = BridgedChainId; } diff --git a/bin/rialto/runtime/src/millau_messages.rs b/bin/rialto/runtime/src/millau_messages.rs index 90a63a733c3..09fbe46453d 100644 --- a/bin/rialto/runtime/src/millau_messages.rs +++ b/bin/rialto/runtime/src/millau_messages.rs @@ -18,13 +18,11 @@ use crate::{MillauGrandpaInstance, Runtime, RuntimeCall, RuntimeOrigin}; -use bp_messages::{ - source_chain::TargetHeaderChain, - target_chain::{ProvedMessages, SourceHeaderChain}, - InboundLaneData, LaneId, Message, MessageNonce, -}; +use bp_messages::{LaneId, MessageNonce}; use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; -use bridge_runtime_common::messages::{self, MessageBridge}; +use bridge_runtime_common::messages::{ + self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, +}; use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; /// Lane that is used for XCM messages exchange. @@ -110,6 +108,10 @@ impl messages::ThisChainWithMessages for Rialto { /// Millau chain from message lane point of view. #[derive(RuntimeDebug, Clone, Copy)] pub struct Millau; +/// Millau as source header chain. +pub type MillauAsSourceHeaderChain = SourceHeaderChainAdapter; +/// Millau as target header chain. +pub type MillauAsTargetHeaderChain = TargetHeaderChainAdapter; impl messages::UnderlyingChainProvider for Millau { type Chain = bp_millau::Millau; @@ -121,43 +123,6 @@ impl messages::BridgedChainWithMessages for Millau { } } -impl TargetHeaderChain for Millau { - type Error = &'static str; - // The proof is: - // - hash of the header this proof has been created with; - // - the storage proof of one or several keys; - // - id of the lane we prove state of. - type MessagesDeliveryProof = ToMillauMessagesDeliveryProof; - - fn verify_message(payload: &ToMillauMessagePayload) -> Result<(), Self::Error> { - messages::source::verify_chain_message::(payload) - } - - fn verify_messages_delivery_proof( - proof: Self::MessagesDeliveryProof, - ) -> Result<(LaneId, InboundLaneData), Self::Error> { - messages::source::verify_messages_delivery_proof::(proof) - } -} - -impl SourceHeaderChain for Millau { - type Error = &'static str; - // The proof is: - // - hash of the header this proof has been created with; - // - the storage proof of one or several keys; - // - id of the lane we prove messages for; - // - inclusive range of messages nonces that are proved. - type MessagesProof = FromMillauMessagesProof; - - fn verify_messages_proof( - proof: Self::MessagesProof, - messages_count: u32, - ) -> Result, Self::Error> { - messages::target::verify_messages_proof::(proof, messages_count) - .map_err(Into::into) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/bin/runtime-common/Cargo.toml b/bin/runtime-common/Cargo.toml index 9153d7a05dd..9e8e6b29db2 100644 --- a/bin/runtime-common/Cargo.toml +++ b/bin/runtime-common/Cargo.toml @@ -29,7 +29,6 @@ pallet-bridge-relayers = { path = "../../modules/relayers", default-features = f frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-utility = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -46,9 +45,8 @@ xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } [dev-dependencies] -bp-rialto = { path = "../../primitives/chain-rialto" } bp-test-utils = { path = "../../primitives/test-utils" } -millau-runtime = { path = "../millau/runtime" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } [features] default = ["std"] @@ -82,7 +80,6 @@ std = [ "xcm-executor/std", ] runtime-benchmarks = [ - "pallet-balances", "pallet-bridge-grandpa/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks", "xcm-builder/runtime-benchmarks", diff --git a/bin/runtime-common/src/integrity.rs b/bin/runtime-common/src/integrity.rs index e8e3e7f87cc..11c6cb90a83 100644 --- a/bin/runtime-common/src/integrity.rs +++ b/bin/runtime-common/src/integrity.rs @@ -81,8 +81,8 @@ macro_rules! assert_bridge_messages_pallet_types( // configuration is used), or something has broke existing configuration (meaning that all bridged chains // and relays will stop functioning) use $crate::messages::{ - source::FromThisChainMessagePayload, - target::FromBridgedChainMessagePayload, + source::{FromThisChainMessagePayload, TargetHeaderChainAdapter}, + target::{FromBridgedChainMessagePayload, SourceHeaderChainAdapter}, AccountIdOf, BalanceOf, BridgedChain, CallOf, ThisChain, }; use pallet_bridge_messages::Config as MessagesConfig; @@ -93,16 +93,16 @@ macro_rules! assert_bridge_messages_pallet_types( assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundPayload, FromBridgedChainMessagePayload>>); assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundRelayer, AccountIdOf>); - assert_type_eq_all!(<$r as MessagesConfig<$i>>::TargetHeaderChain, BridgedChain<$bridge>); - assert_type_eq_all!(<$r as MessagesConfig<$i>>::SourceHeaderChain, BridgedChain<$bridge>); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::TargetHeaderChain, TargetHeaderChainAdapter<$bridge>); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::SourceHeaderChain, SourceHeaderChainAdapter<$bridge>); } } ); /// Macro that combines four other macro calls - `assert_chain_types`, `assert_bridge_types`, /// `assert_bridge_grandpa_pallet_types` and `assert_bridge_messages_pallet_types`. It may be used -/// at the chain that is implemeting complete standard messages bridge (i.e. with bridge GRANDPA and -/// messages pallets deployed). +/// at the chain that is implementing complete standard messages bridge (i.e. with bridge GRANDPA +/// and messages pallets deployed). #[macro_export] macro_rules! assert_complete_bridge_types( ( diff --git a/bin/runtime-common/src/lib.rs b/bin/runtime-common/src/lib.rs index f8d2e7a039e..9f31450bd28 100644 --- a/bin/runtime-common/src/lib.rs +++ b/bin/runtime-common/src/lib.rs @@ -30,6 +30,7 @@ pub mod parachains_benchmarking; pub mod refund_relayer_extension; mod messages_generation; +mod mock; #[cfg(feature = "integrity-test")] pub mod integrity; diff --git a/bin/runtime-common/src/messages.rs b/bin/runtime-common/src/messages.rs index 3974966daad..8c97198393d 100644 --- a/bin/runtime-common/src/messages.rs +++ b/bin/runtime-common/src/messages.rs @@ -22,8 +22,10 @@ use bp_header_chain::{HeaderChain, HeaderChainError}; use bp_messages::{ - source_chain::LaneMessageVerifier, - target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages}, + source_chain::{LaneMessageVerifier, TargetHeaderChain}, + target_chain::{ + DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain, + }, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, }; use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Size, StorageProofChecker}; @@ -161,13 +163,13 @@ pub mod source { #[derive(RuntimeDebug)] pub struct FromThisChainMessageVerifier(PhantomData); - /// The error message returned from LaneMessageVerifier when outbound lane is disabled. + /// The error message returned from `LaneMessageVerifier` when outbound lane is disabled. pub const MESSAGE_REJECTED_BY_OUTBOUND_LANE: &str = "The outbound message lane has rejected the message."; - /// The error message returned from LaneMessageVerifier when too many pending messages at the + /// The error message returned from `LaneMessageVerifier` when too many pending messages at the /// lane. pub const TOO_MANY_PENDING_MESSAGES: &str = "Too many pending messages at the lane."; - /// The error message returned from LaneMessageVerifier when call origin is mismatch. + /// The error message returned from `LaneMessageVerifier` when call origin is mismatch. pub const BAD_ORIGIN: &str = "Unable to match the source origin to expected target origin."; impl LaneMessageVerifier>, FromThisChainMessagePayload> @@ -212,6 +214,26 @@ pub mod source { ) } + /// `TargetHeaderChain` implementation that is using default types and perform default checks. + pub struct TargetHeaderChainAdapter(PhantomData); + + impl TargetHeaderChain>> + for TargetHeaderChainAdapter + { + type Error = &'static str; + type MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof>>; + + fn verify_message(payload: &FromThisChainMessagePayload) -> Result<(), Self::Error> { + verify_chain_message::(payload) + } + + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData>>), Self::Error> { + verify_messages_delivery_proof::(proof) + } + } + /// Do basic Bridged-chain specific verification of This -> Bridged chain message. /// /// Ok result from this function means that the delivery transaction with this message @@ -549,6 +571,21 @@ pub mod target { maximal_extrinsic_size / 3 * 2 } + /// `SourceHeaderChain` implementation that is using default types and perform default checks. + pub struct SourceHeaderChainAdapter(PhantomData); + + impl SourceHeaderChain for SourceHeaderChainAdapter { + type Error = &'static str; + type MessagesProof = FromBridgedChainMessagesProof>>; + + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result, Self::Error> { + verify_messages_proof::(proof, messages_count).map_err(Into::into) + } + } + /// Verify proof of Bridged -> This chain messages. /// /// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged @@ -702,206 +739,22 @@ pub type BridgeMessagesCallOf = bp_messages::BridgeMessagesCall< #[cfg(test)] mod tests { use super::*; - use crate::messages_generation::{ - encode_all_messages, encode_lane_data, prepare_messages_storage_proof, + use crate::{ + messages_generation::{ + encode_all_messages, encode_lane_data, prepare_messages_storage_proof, + }, + mock::*, }; - use codec::{Decode, Encode}; - use frame_support::weights::Weight; + use bp_header_chain::StoredHeaderDataBuilder; + use bp_runtime::HeaderId; + use codec::Encode; use sp_core::H256; - use sp_runtime::traits::{BlakeTwo256, Header as _}; - use std::cell::RefCell; - - const BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT: usize = 5; - const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: usize = 2048; - const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024; - - /// Bridge that is deployed on ThisChain and allows sending/receiving messages to/from - /// BridgedChain. - #[derive(Debug, PartialEq, Eq)] - struct OnThisChainBridge; - - impl MessageBridge for OnThisChainBridge { - const THIS_CHAIN_ID: ChainId = *b"this"; - const BRIDGED_CHAIN_ID: ChainId = *b"brdg"; - const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; - - type ThisChain = ThisChain; - type BridgedChain = BridgedChain; - type BridgedHeaderChain = BridgedHeaderChain; - } - - /// Bridge that is deployed on BridgedChain and allows sending/receiving messages to/from - /// ThisChain; - #[derive(Debug, PartialEq, Eq)] - struct OnBridgedChainBridge; - - impl MessageBridge for OnBridgedChainBridge { - const THIS_CHAIN_ID: ChainId = *b"brdg"; - const BRIDGED_CHAIN_ID: ChainId = *b"this"; - const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; - - type ThisChain = BridgedChain; - type BridgedChain = ThisChain; - type BridgedHeaderChain = ThisHeaderChain; - } - - #[derive(Clone, Debug)] - struct ThisChainOrigin(Result, ()>); - - impl From - for Result, ThisChainOrigin> - { - fn from( - origin: ThisChainOrigin, - ) -> Result, ThisChainOrigin> { - origin.clone().0.map_err(|_| origin) - } - } - - #[derive(Clone, Debug)] - struct BridgedChainOrigin; - - impl From - for Result, BridgedChainOrigin> - { - fn from( - _origin: BridgedChainOrigin, - ) -> Result, BridgedChainOrigin> { - unreachable!() - } - } - - struct ThisUnderlyingChain; - type ThisChainHeader = sp_runtime::generic::Header; - type ThisChainAccountId = u32; - type ThisChainBalance = u32; - #[derive(Decode, Encode)] - struct ThisChainCall; - - impl Chain for ThisUnderlyingChain { - type BlockNumber = u64; - type Hash = H256; - type Hasher = BlakeTwo256; - type Header = ThisChainHeader; - type AccountId = ThisChainAccountId; - type Balance = ThisChainBalance; - type Index = u32; - type Signature = sp_runtime::MultiSignature; - - fn max_extrinsic_size() -> u32 { - BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE - } - - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } - } - - struct ThisChain; - - impl UnderlyingChainProvider for ThisChain { - type Chain = ThisUnderlyingChain; - } - - impl ThisChainWithMessages for ThisChain { - type RuntimeOrigin = ThisChainOrigin; - type RuntimeCall = ThisChainCall; - - fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool { - lane == TEST_LANE_ID - } - - fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { - MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE - } - } - - impl BridgedChainWithMessages for ThisChain { - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { - unreachable!() - } - } - - struct BridgedUnderlyingChain; - type BridgedChainHeader = sp_runtime::generic::Header; - type BridgedChainAccountId = u128; - type BridgedChainBalance = u128; - #[derive(Decode, Encode)] - struct BridgedChainCall; - - impl Chain for BridgedUnderlyingChain { - type BlockNumber = u64; - type Hash = H256; - type Hasher = BlakeTwo256; - type Header = BridgedChainHeader; - type AccountId = BridgedChainAccountId; - type Balance = BridgedChainBalance; - type Index = u32; - type Signature = sp_runtime::MultiSignature; - - fn max_extrinsic_size() -> u32 { - BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } - } - - struct BridgedChain; - - impl UnderlyingChainProvider for BridgedChain { - type Chain = BridgedUnderlyingChain; - } - - impl ThisChainWithMessages for BridgedChain { - type RuntimeOrigin = BridgedChainOrigin; - type RuntimeCall = BridgedChainCall; - - fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, _lane: &LaneId) -> bool { - unreachable!() - } - - fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { - unreachable!() - } - } - - impl BridgedChainWithMessages for BridgedChain { - fn verify_dispatch_weight(message_payload: &[u8]) -> bool { - message_payload.len() >= BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT && - message_payload.len() <= BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT - } - } - - thread_local! { - static TEST_BRIDGED_HEADER: RefCell> = RefCell::new(None); - } - - struct BridgedHeaderChain; - - impl HeaderChain for BridgedHeaderChain { - fn finalized_header_state_root( - _hash: HashOf, - ) -> Option> { - TEST_BRIDGED_HEADER.with(|h| h.borrow().clone()).map(|h| *h.state_root()) - } - } - - struct ThisHeaderChain; - - impl HeaderChain for ThisHeaderChain { - fn finalized_header_state_root(_hash: HashOf) -> Option> { - unreachable!() - } - } + use sp_runtime::traits::Header as _; fn test_lane_outbound_data() -> OutboundLaneData { OutboundLaneData::default() } - const TEST_LANE_ID: &LaneId = &LaneId(*b"test"); - const MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE: MessageNonce = 32; - fn regular_outbound_message_payload() -> source::FromThisChainMessagePayload { vec![42] } @@ -910,7 +763,7 @@ mod tests { fn message_is_rejected_when_sent_using_disabled_lane() { assert_eq!( source::FromThisChainMessageVerifier::::verify_message( - &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), + &frame_system::RawOrigin::Root.into(), &LaneId(*b"dsbl"), &test_lane_outbound_data(), ®ular_outbound_message_payload(), @@ -923,8 +776,8 @@ mod tests { fn message_is_rejected_when_there_are_too_many_pending_messages_at_outbound_lane() { assert_eq!( source::FromThisChainMessageVerifier::::verify_message( - &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), - TEST_LANE_ID, + &frame_system::RawOrigin::Root.into(), + &TEST_LANE_ID, &OutboundLaneData { latest_received_nonce: 100, latest_generated_nonce: 100 + MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE + 1, @@ -986,7 +839,7 @@ mod tests { test: impl Fn(target::FromBridgedChainMessagesProof) -> R, ) -> R { let (state_root, storage_proof) = prepare_messages_storage_proof::( - *TEST_LANE_ID, + TEST_LANE_ID, 1..=nonces_end, outbound_lane_data, bp_runtime::StorageProofSize::Minimal(0), @@ -995,22 +848,31 @@ mod tests { encode_outbound_lane_data, ); - TEST_BRIDGED_HEADER.with(|h| { - *h.borrow_mut() = Some(BridgedChainHeader::new( + sp_io::TestExternalities::new(Default::default()).execute_with(move || { + let bridged_header = BridgedChainHeader::new( 0, Default::default(), state_root, Default::default(), Default::default(), - )) - }); + ); + let bridged_header_hash = bridged_header.hash(); - test(target::FromBridgedChainMessagesProof { - bridged_header_hash: Default::default(), - storage_proof, - lane: *TEST_LANE_ID, - nonces_start: 1, - nonces_end, + pallet_bridge_grandpa::BestFinalized::::put(HeaderId( + 0, + bridged_header_hash, + )); + pallet_bridge_grandpa::ImportedHeaders::::insert( + bridged_header_hash, + bridged_header.build(), + ); + test(target::FromBridgedChainMessagesProof { + bridged_header_hash, + storage_proof, + lane: TEST_LANE_ID, + nonces_start: 1, + nonces_end, + }) }) } @@ -1038,7 +900,9 @@ mod tests { fn message_proof_is_rejected_if_header_is_missing_from_the_chain() { assert_eq!( using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { - TEST_BRIDGED_HEADER.with(|h| *h.borrow_mut() = None); + let bridged_header_hash = + pallet_bridge_grandpa::BestFinalized::::get().unwrap().1; + pallet_bridge_grandpa::ImportedHeaders::::remove(bridged_header_hash); target::verify_messages_proof::(proof, 10) }), Err(target::MessageProofError::HeaderChain(HeaderChainError::UnknownHeader)), @@ -1049,8 +913,19 @@ mod tests { fn message_proof_is_rejected_if_header_state_root_mismatches() { assert_eq!( using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { - TEST_BRIDGED_HEADER - .with(|h| h.borrow_mut().as_mut().unwrap().state_root = Default::default()); + let bridged_header_hash = + pallet_bridge_grandpa::BestFinalized::::get().unwrap().1; + pallet_bridge_grandpa::ImportedHeaders::::insert( + bridged_header_hash, + BridgedChainHeader::new( + 0, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) + .build(), + ); target::verify_messages_proof::(proof, 10) }), Err(target::MessageProofError::HeaderChain(HeaderChainError::StorageRootMismatch)), @@ -1138,7 +1013,7 @@ mod tests { |proof| target::verify_messages_proof::(proof, 0), ), Ok(vec![( - *TEST_LANE_ID, + TEST_LANE_ID, ProvedLaneMessages { lane_state: Some(OutboundLaneData { oldest_unpruned_nonce: 1, @@ -1168,7 +1043,7 @@ mod tests { |proof| target::verify_messages_proof::(proof, 1), ), Ok(vec![( - *TEST_LANE_ID, + TEST_LANE_ID, ProvedLaneMessages { lane_state: Some(OutboundLaneData { oldest_unpruned_nonce: 1, @@ -1176,7 +1051,7 @@ mod tests { latest_generated_nonce: 1, }), messages: vec![Message { - key: MessageKey { lane_id: *TEST_LANE_ID, nonce: 1 }, + key: MessageKey { lane_id: TEST_LANE_ID, nonce: 1 }, payload: vec![42], }], }, diff --git a/bin/runtime-common/src/messages_benchmarking.rs b/bin/runtime-common/src/messages_benchmarking.rs index 919c03583b8..afe95422d18 100644 --- a/bin/runtime-common/src/messages_benchmarking.rs +++ b/bin/runtime-common/src/messages_benchmarking.rs @@ -48,7 +48,6 @@ pub fn prepare_message_proof( ) -> (FromBridgedChainMessagesProof>>, Weight) where R: frame_system::Config>> - + pallet_balances::Config>> + pallet_bridge_grandpa::Config, R::BridgedChain: bp_runtime::Chain>, Header = BH>, B: MessageBridge, diff --git a/bin/runtime-common/src/messages_extension.rs b/bin/runtime-common/src/messages_extension.rs index 0f680f84e49..1e207dc605b 100644 --- a/bin/runtime-common/src/messages_extension.rs +++ b/bin/runtime-common/src/messages_extension.rs @@ -96,20 +96,17 @@ impl< #[cfg(test)] mod tests { - use bp_messages::UnrewardedRelayersState; - use millau_runtime::{ - bridge_runtime_common::{ - messages::{ - source::FromBridgedChainMessagesDeliveryProof, - target::FromBridgedChainMessagesProof, - }, - BridgeRuntimeFilterCall, + use crate::{ + messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }, - Runtime, RuntimeCall, WithRialtoMessagesInstance, + mock::{TestRuntime, ThisChainRuntimeCall}, + BridgeRuntimeFilterCall, }; + use bp_messages::UnrewardedRelayersState; fn deliver_message_10() { - pallet_bridge_messages::InboundLanes::::insert( + pallet_bridge_messages::InboundLanes::::insert( bp_messages::LaneId([0, 0, 0, 0]), bp_messages::InboundLaneData { relayers: Default::default(), last_confirmed_nonce: 10 }, ); @@ -119,10 +116,10 @@ mod tests { nonces_start: bp_messages::MessageNonce, nonces_end: bp_messages::MessageNonce, ) -> bool { - pallet_bridge_messages::Pallet::::validate( - &RuntimeCall::BridgeRialtoMessages( - pallet_bridge_messages::Call::::receive_messages_proof { - relayer_id_at_bridged_chain: [0u8; 32].into(), + pallet_bridge_messages::Pallet::::validate( + &ThisChainRuntimeCall::BridgeMessages( + pallet_bridge_messages::Call::::receive_messages_proof { + relayer_id_at_bridged_chain: 42, messages_count: (nonces_end - nonces_start + 1) as u32, dispatch_weight: frame_support::weights::Weight::zero(), proof: FromBridgedChainMessagesProof { @@ -169,7 +166,7 @@ mod tests { } fn confirm_message_10() { - pallet_bridge_messages::OutboundLanes::::insert( + pallet_bridge_messages::OutboundLanes::::insert( bp_messages::LaneId([0, 0, 0, 0]), bp_messages::OutboundLaneData { oldest_unpruned_nonce: 0, @@ -180,21 +177,20 @@ mod tests { } fn validate_message_confirmation(last_delivered_nonce: bp_messages::MessageNonce) -> bool { - pallet_bridge_messages::Pallet::::validate( - &RuntimeCall::BridgeRialtoMessages(pallet_bridge_messages::Call::< - Runtime, - WithRialtoMessagesInstance, - >::receive_messages_delivery_proof { - proof: FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: Default::default(), - storage_proof: Vec::new(), - lane: bp_messages::LaneId([0, 0, 0, 0]), - }, - relayers_state: UnrewardedRelayersState { - last_delivered_nonce, - ..Default::default() + pallet_bridge_messages::Pallet::::validate( + &ThisChainRuntimeCall::BridgeMessages( + pallet_bridge_messages::Call::::receive_messages_delivery_proof { + proof: FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: Default::default(), + storage_proof: Vec::new(), + lane: bp_messages::LaneId([0, 0, 0, 0]), + }, + relayers_state: UnrewardedRelayersState { + last_delivered_nonce, + ..Default::default() + }, }, - }), + ), ) .is_ok() } diff --git a/bin/runtime-common/src/mock.rs b/bin/runtime-common/src/mock.rs new file mode 100644 index 00000000000..51b03447c63 --- /dev/null +++ b/bin/runtime-common/src/mock.rs @@ -0,0 +1,422 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! A mock runtime for testing different stuff in the crate. We've been using Millau +//! runtime for that before, but it has two drawbacks: +//! +//! - circular dependencies between this crate and Millau runtime; +//! +//! - we can't use (e.g. as git subtree or by copying) this crate in repo without Millau. + +#![cfg(test)] + +use crate::messages::{ + source::{ + FromThisChainMaximalOutboundPayloadSize, FromThisChainMessagePayload, + FromThisChainMessageVerifier, TargetHeaderChainAdapter, + }, + target::{FromBridgedChainMessagePayload, SourceHeaderChainAdapter}, + BridgedChainWithMessages, HashOf, MessageBridge, ThisChainWithMessages, +}; + +use bp_header_chain::HeaderChain; +use bp_messages::{target_chain::ForbidInboundMessages, LaneId, MessageNonce}; +use bp_parachains::SingleParaStoredHeaderDataBuilder; +use bp_runtime::{Chain, ChainId, Parachain, UnderlyingChainProvider}; +use codec::{Decode, Encode}; +use frame_support::{ + parameter_types, + weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight}, +}; +use pallet_transaction_payment::Multiplier; +use sp_runtime::{ + testing::H256, + traits::{BlakeTwo256, ConstU32, ConstU64, ConstU8, IdentityLookup}, + FixedPointNumber, Perquintill, +}; + +/// Account identifier at `ThisChain`. +pub type ThisChainAccountId = u64; +/// Balance at `ThisChain`. +pub type ThisChainBalance = u64; +/// Block number at `ThisChain`. +pub type ThisChainBlockNumber = u32; +/// Hash at `ThisChain`. +pub type ThisChainHash = H256; +/// Hasher at `ThisChain`. +pub type ThisChainHasher = BlakeTwo256; +/// Runtime call at `ThisChain`. +pub type ThisChainRuntimeCall = RuntimeCall; +/// Runtime call origin at `ThisChain`. +pub type ThisChainCallOrigin = RuntimeOrigin; +/// Header of `ThisChain`. +pub type ThisChainHeader = sp_runtime::generic::Header; +/// Block of `ThisChain`. +pub type ThisChainBlock = frame_system::mocking::MockBlock; +/// Unchecked extrinsic of `ThisChain`. +pub type ThisChainUncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + +/// Account identifier at the `BridgedChain`. +pub type BridgedChainAccountId = u128; +/// Balance at the `BridgedChain`. +pub type BridgedChainBalance = u128; +/// Block number at the `BridgedChain`. +pub type BridgedChainBlockNumber = u32; +/// Hash at the `BridgedChain`. +pub type BridgedChainHash = H256; +/// Hasher at the `BridgedChain`. +pub type BridgedChainHasher = BlakeTwo256; +/// Header of the `BridgedChain`. +pub type BridgedChainHeader = + sp_runtime::generic::Header; + +/// Message lane used in tests. +pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]); +/// Maximal number of queued messages at the test lane. +pub const MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE: MessageNonce = 32; +/// Minimal extrinsic weight at the `BridgedChain`. +pub const BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT: usize = 5; +/// Maximal extrinsic weight at the `BridgedChain`. +pub const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: usize = 2048; +/// Maximal extrinsic size at the `BridgedChain`. +pub const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024; + +frame_support::construct_runtime! { + pub enum TestRuntime where + Block = ThisChainBlock, + NodeBlock = ThisChainBlock, + UncheckedExtrinsic = ThisChainUncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Utility: pallet_utility, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, + BridgeGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, + BridgeParachains: pallet_bridge_parachains::{Pallet, Call, Storage, Event}, + BridgeMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, + } +} + +crate::generate_bridge_reject_obsolete_headers_and_messages! { + ThisChainRuntimeCall, ThisChainAccountId, + BridgeGrandpa, BridgeParachains, BridgeMessages +} + +parameter_types! { + pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID]; + pub const BridgedChainId: ChainId = *b"brdg"; + pub const BridgedParasPalletName: &'static str = "Paras"; + pub const ExistentialDeposit: ThisChainBalance = 500; + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); + pub const TransactionBaseFee: ThisChainBalance = 0; + pub const TransactionByteFee: ThisChainBalance = 1; + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); + pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value(); +} + +impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = ThisChainBlockNumber; + type Hash = ThisChainHash; + type Hashing = ThisChainHasher; + type AccountId = ThisChainAccountId; + type Lookup = IdentityLookup; + type Header = ThisChainHeader; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU32<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type DbWeight = DbWeight; + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_utility::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + +impl pallet_balances::Config for TestRuntime { + type Balance = ThisChainBalance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; +} + +impl pallet_transaction_payment::Config for TestRuntime { + type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = IdentityFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< + TestRuntime, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + MaximumMultiplier, + >; + type RuntimeEvent = RuntimeEvent; +} + +impl pallet_bridge_grandpa::Config for TestRuntime { + type BridgedChain = BridgedUnderlyingChain; + type MaxRequests = ConstU32<50>; + type HeadersToKeep = ConstU32<8>; + type MaxBridgedAuthorities = ConstU32<1024>; + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; +} + +impl pallet_bridge_parachains::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type BridgesGrandpaPalletInstance = (); + type ParasPalletName = BridgedParasPalletName; + type ParaStoredHeaderDataBuilder = + SingleParaStoredHeaderDataBuilder; + type HeadsToKeep = ConstU32<8>; + type MaxParaHeadDataSize = ConstU32<1024>; + type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; +} + +impl pallet_bridge_messages::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + type ActiveOutboundLanes = ActiveOutboundLanes; + type MaxUnrewardedRelayerEntriesAtInboundLane = ConstU64<16>; + type MaxUnconfirmedMessagesAtInboundLane = ConstU64<16>; + + type MaximalOutboundPayloadSize = FromThisChainMaximalOutboundPayloadSize; + type OutboundPayload = FromThisChainMessagePayload; + + type InboundPayload = FromBridgedChainMessagePayload; + type InboundRelayer = BridgedChainAccountId; + type DeliveryPayments = (); + + type TargetHeaderChain = TargetHeaderChainAdapter; + type LaneMessageVerifier = FromThisChainMessageVerifier; + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + TestRuntime, + frame_support::traits::ConstU64<100_000>, + frame_support::traits::ConstU64<10_000>, + >; + + type SourceHeaderChain = SourceHeaderChainAdapter; + type MessageDispatch = + ForbidInboundMessages<(), FromBridgedChainMessagePayload>; + type BridgedChainId = BridgedChainId; +} + +impl pallet_bridge_relayers::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type Reward = ThisChainBalance; + type PaymentProcedure = (); + type WeightInfo = (); +} + +/// Bridge that is deployed on `ThisChain` and allows sending/receiving messages to/from +/// `BridgedChain`. +#[derive(Debug, PartialEq, Eq)] +pub struct OnThisChainBridge; + +impl MessageBridge for OnThisChainBridge { + const THIS_CHAIN_ID: ChainId = *b"this"; + const BRIDGED_CHAIN_ID: ChainId = *b"brdg"; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; + + type ThisChain = ThisChain; + type BridgedChain = BridgedChain; + type BridgedHeaderChain = pallet_bridge_grandpa::GrandpaChainHeaders; +} + +/// Bridge that is deployed on `BridgedChain` and allows sending/receiving messages to/from +/// `ThisChain`. +#[derive(Debug, PartialEq, Eq)] +pub struct OnBridgedChainBridge; + +impl MessageBridge for OnBridgedChainBridge { + const THIS_CHAIN_ID: ChainId = *b"brdg"; + const BRIDGED_CHAIN_ID: ChainId = *b"this"; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; + + type ThisChain = BridgedChain; + type BridgedChain = ThisChain; + type BridgedHeaderChain = ThisHeaderChain; +} + +/// Dummy implementation of `HeaderChain` for `ThisChain` at the `BridgedChain`. +pub struct ThisHeaderChain; + +impl HeaderChain for ThisHeaderChain { + fn finalized_header_state_root(_hash: HashOf) -> Option> { + unreachable!() + } +} + +/// Call origin at `BridgedChain`. +#[derive(Clone, Debug)] +pub struct BridgedChainOrigin; + +impl From + for Result, BridgedChainOrigin> +{ + fn from( + _origin: BridgedChainOrigin, + ) -> Result, BridgedChainOrigin> { + unreachable!() + } +} + +/// Underlying chain of `ThisChain`. +pub struct ThisUnderlyingChain; + +impl Chain for ThisUnderlyingChain { + type BlockNumber = ThisChainBlockNumber; + type Hash = ThisChainHash; + type Hasher = ThisChainHasher; + type Header = ThisChainHeader; + type AccountId = ThisChainAccountId; + type Balance = ThisChainBalance; + type Index = u32; + type Signature = sp_runtime::MultiSignature; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +/// The chain where we are in tests. +pub struct ThisChain; + +impl UnderlyingChainProvider for ThisChain { + type Chain = ThisUnderlyingChain; +} + +impl ThisChainWithMessages for ThisChain { + type RuntimeOrigin = ThisChainCallOrigin; + type RuntimeCall = ThisChainRuntimeCall; + + fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool { + *lane == TEST_LANE_ID + } + + fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { + MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE + } +} + +impl BridgedChainWithMessages for ThisChain { + fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { + unreachable!() + } +} + +/// Underlying chain of `BridgedChain`. +pub struct BridgedUnderlyingChain; +/// Some parachain under `BridgedChain` consensus. +pub struct BridgedUnderlyingParachain; +/// Runtime call of the `BridgedChain`. +#[derive(Decode, Encode)] +pub struct BridgedChainCall; + +impl Chain for BridgedUnderlyingChain { + type BlockNumber = BridgedChainBlockNumber; + type Hash = BridgedChainHash; + type Hasher = BridgedChainHasher; + type Header = BridgedChainHeader; + type AccountId = BridgedChainAccountId; + type Balance = BridgedChainBalance; + type Index = u32; + type Signature = sp_runtime::MultiSignature; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl Chain for BridgedUnderlyingParachain { + type BlockNumber = BridgedChainBlockNumber; + type Hash = BridgedChainHash; + type Hasher = BridgedChainHasher; + type Header = BridgedChainHeader; + type AccountId = BridgedChainAccountId; + type Balance = BridgedChainBalance; + type Index = u32; + type Signature = sp_runtime::MultiSignature; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl Parachain for BridgedUnderlyingParachain { + const PARACHAIN_ID: u32 = 42; +} + +/// The other, bridged chain, used in tests. +pub struct BridgedChain; + +impl UnderlyingChainProvider for BridgedChain { + type Chain = BridgedUnderlyingChain; +} + +impl ThisChainWithMessages for BridgedChain { + type RuntimeOrigin = BridgedChainOrigin; + type RuntimeCall = BridgedChainCall; + + fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, _lane: &LaneId) -> bool { + unreachable!() + } + + fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { + unreachable!() + } +} + +impl BridgedChainWithMessages for BridgedChain { + fn verify_dispatch_weight(message_payload: &[u8]) -> bool { + message_payload.len() >= BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT && + message_payload.len() <= BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT + } +} diff --git a/bin/runtime-common/src/refund_relayer_extension.rs b/bin/runtime-common/src/refund_relayer_extension.rs index 0038af975db..51a7db3b53b 100644 --- a/bin/runtime-common/src/refund_relayer_extension.rs +++ b/bin/runtime-common/src/refund_relayer_extension.rs @@ -19,12 +19,7 @@ //! with calls that are: delivering new messsage and all necessary underlying headers //! (parachain or relay chain). -// hack because we have circular (test-level) dependency between `millau-runtime` -// and `bridge-runtime-common` crates -#[cfg(not(test))] use crate::messages::target::FromBridgedChainMessagesProof; -#[cfg(test)] -use millau_runtime::bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; use bp_messages::{target_chain::SourceHeaderChain, LaneId, MessageNonce}; use bp_polkadot_core::parachains::ParaId; @@ -475,36 +470,37 @@ where #[cfg(test)] mod tests { use super::*; + use crate::{messages::target::FromBridgedChainMessagesProof, mock::*}; use bp_messages::InboundLaneData; use bp_parachains::{BestParaHeadHash, ParaInfo}; use bp_polkadot_core::parachains::ParaHeadsProof; use bp_runtime::HeaderId; use bp_test_utils::make_default_justification; use frame_support::{assert_storage_noop, parameter_types, weights::Weight}; - use millau_runtime::{ - RialtoGrandpaInstance, Runtime, RuntimeCall, WithRialtoParachainMessagesInstance, - WithRialtoParachainsInstance, - }; use sp_runtime::{transaction_validity::InvalidTransaction, DispatchError}; parameter_types! { pub TestParachain: u32 = 1000; - pub TestLaneId: LaneId = LaneId([0, 0, 0, 0]); + pub TestLaneId: LaneId = TEST_LANE_ID; } type TestExtension = RefundRelayerForMessagesFromParachain< - millau_runtime::Runtime, - RialtoGrandpaInstance, - WithRialtoParachainsInstance, - WithRialtoParachainMessagesInstance, - millau_runtime::BridgeRejectObsoleteHeadersAndMessages, + TestRuntime, + (), + (), + (), + BridgeRejectObsoleteHeadersAndMessages, TestParachain, TestLaneId, - millau_runtime::Runtime, + TestRuntime, >; - fn relayer_account() -> millau_runtime::AccountId { - [0u8; 32].into() + fn relayer_account_at_this_chain() -> ThisChainAccountId { + 0 + } + + fn relayer_account_at_bridged_chain() -> BridgedChainAccountId { + 0 } fn initialize_environment( @@ -513,9 +509,7 @@ mod tests { best_delivered_message: MessageNonce, ) { let best_relay_header = HeaderId(best_relay_header_number, RelayBlockHash::default()); - pallet_bridge_grandpa::BestFinalized::::put( - best_relay_header, - ); + pallet_bridge_grandpa::BestFinalized::::put(best_relay_header); let para_id = ParaId(TestParachain::get()); let para_info = ParaInfo { @@ -525,18 +519,16 @@ mod tests { }, next_imported_hash_position: 0, }; - pallet_bridge_parachains::ParasInfo::::insert( - para_id, para_info, - ); + pallet_bridge_parachains::ParasInfo::::insert(para_id, para_info); let lane_id = TestLaneId::get(); let lane_data = InboundLaneData { last_confirmed_nonce: best_delivered_message, ..Default::default() }; - pallet_bridge_messages::InboundLanes::::insert(lane_id, lane_data); + pallet_bridge_messages::InboundLanes::::insert(lane_id, lane_data); } fn submit_relay_header_call(relay_header_number: RelayBlockNumber) -> RuntimeCall { - let relay_header = bp_rialto::Header::new( + let relay_header = BridgedChainHeader::new( relay_header_number, Default::default(), Default::default(), @@ -545,7 +537,7 @@ mod tests { ); let relay_justification = make_default_justification(&relay_header); - RuntimeCall::BridgeRialtoGrandpa(GrandpaCall::submit_finality_proof { + RuntimeCall::BridgeGrandpa(GrandpaCall::submit_finality_proof { finality_target: Box::new(relay_header), justification: relay_justification, }) @@ -554,7 +546,7 @@ mod tests { fn submit_parachain_head_call( parachain_head_at_relay_header_number: RelayBlockNumber, ) -> RuntimeCall { - RuntimeCall::BridgeRialtoParachains(ParachainsCall::submit_parachain_heads { + RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()), parachains: vec![(ParaId(TestParachain::get()), [1u8; 32].into())], parachain_heads_proof: ParaHeadsProof(vec![]), @@ -562,9 +554,9 @@ mod tests { } fn message_delivery_call(best_message: MessageNonce) -> RuntimeCall { - RuntimeCall::BridgeRialtoParachainMessages(MessagesCall::receive_messages_proof { - relayer_id_at_bridged_chain: relayer_account(), - proof: millau_runtime::bridge_runtime_common::messages::target::FromBridgedChainMessagesProof { + RuntimeCall::BridgeMessages(MessagesCall::receive_messages_proof { + relayer_id_at_bridged_chain: relayer_account_at_bridged_chain(), + proof: FromBridgedChainMessagesProof { bridged_header_hash: Default::default(), storage_proof: vec![], lane: TestLaneId::get(), @@ -602,9 +594,9 @@ mod tests { }) } - fn all_finality_pre_dispatch_data() -> PreDispatchData { + fn all_finality_pre_dispatch_data() -> PreDispatchData { PreDispatchData { - relayer: relayer_account(), + relayer: relayer_account_at_this_chain(), call_type: CallType::AllFinalityAndDelivery( ExpectedRelayChainState { best_block_number: 200 }, ExpectedParachainState { at_relay_block_number: 200 }, @@ -613,9 +605,9 @@ mod tests { } } - fn parachain_finality_pre_dispatch_data() -> PreDispatchData { + fn parachain_finality_pre_dispatch_data() -> PreDispatchData { PreDispatchData { - relayer: relayer_account(), + relayer: relayer_account_at_this_chain(), call_type: CallType::ParachainFinalityAndDelivery( ExpectedParachainState { at_relay_block_number: 200 }, MessagesState { best_nonce: 100 }, @@ -623,9 +615,9 @@ mod tests { } } - fn delivery_pre_dispatch_data() -> PreDispatchData { + fn delivery_pre_dispatch_data() -> PreDispatchData { PreDispatchData { - relayer: relayer_account(), + relayer: relayer_account_at_this_chain(), call_type: CallType::Delivery(MessagesState { best_nonce: 100 }), } } @@ -636,14 +628,14 @@ mod tests { fn run_validate(call: RuntimeCall) -> TransactionValidity { let extension: TestExtension = RefundRelayerForMessagesFromParachain(PhantomData); - extension.validate(&relayer_account(), &call, &DispatchInfo::default(), 0) + extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_pre_dispatch( call: RuntimeCall, - ) -> Result>, TransactionValidityError> { + ) -> Result>, TransactionValidityError> { let extension: TestExtension = RefundRelayerForMessagesFromParachain(PhantomData); - extension.pre_dispatch(&relayer_account(), &call, &DispatchInfo::default(), 0) + extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn dispatch_info() -> DispatchInfo { @@ -661,7 +653,7 @@ mod tests { } fn run_post_dispatch( - pre_dispatch_data: Option>, + pre_dispatch_data: Option>, dispatch_result: DispatchResult, ) { let post_dispatch_result = TestExtension::post_dispatch( @@ -674,8 +666,8 @@ mod tests { assert_eq!(post_dispatch_result, Ok(())); } - fn expected_reward() -> millau_runtime::Balance { - pallet_transaction_payment::Pallet::::compute_actual_fee( + fn expected_reward() -> ThisChainBalance { + pallet_transaction_payment::Pallet::::compute_actual_fee( 1024, &dispatch_info(), &post_dispatch_info(), @@ -804,7 +796,7 @@ mod tests { let call = RuntimeCall::Utility(UtilityCall::batch_all { calls: vec![ - RuntimeCall::BridgeRialtoParachains(ParachainsCall::submit_parachain_heads { + RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { at_relay_block: (100, RelayBlockHash::default()), parachains: vec![ (ParaId(TestParachain::get()), [1u8; 32].into()), @@ -892,7 +884,10 @@ mod tests { run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(())); assert_eq!( - RelayersPallet::::relayer_reward(relayer_account(), TestLaneId::get()), + RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + TestLaneId::get() + ), Some(expected_reward()), ); }); @@ -905,7 +900,10 @@ mod tests { run_post_dispatch(Some(parachain_finality_pre_dispatch_data()), Ok(())); assert_eq!( - RelayersPallet::::relayer_reward(relayer_account(), TestLaneId::get()), + RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + TestLaneId::get() + ), Some(expected_reward()), ); }); @@ -918,7 +916,10 @@ mod tests { run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(())); assert_eq!( - RelayersPallet::::relayer_reward(relayer_account(), TestLaneId::get()), + RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + TestLaneId::get() + ), Some(expected_reward()), ); }); diff --git a/deployments/bridges/rialto-millau/docker-compose.yml b/deployments/bridges/rialto-millau/docker-compose.yml index cd77dbbf122..44942e24de9 100644 --- a/deployments/bridges/rialto-millau/docker-compose.yml +++ b/deployments/bridges/rialto-millau/docker-compose.yml @@ -25,7 +25,7 @@ services: - ./bridges/common:/common - ./bridges/rialto-millau/entrypoints:/entrypoints environment: - RUST_LOG: rpc=trace,bridge=trace + RUST_LOG: rpc=trace,bridge=trace,jsonrpsee=trace,jsonrpsee_core=trace,jsonrpsee_ws_client=trace,rustls=trace,soketto=trace ports: - "10016:9616" depends_on: &all-nodes diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh index c6162765351..da809da6acc 100755 --- a/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh +++ b/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh @@ -25,8 +25,9 @@ sleep 6 --millau-port 9944 \ --millau-signer //Rialto.HeadersAndMessagesRelay \ --millau-transactions-mortality=64 \ - --rialto-host rialto-node-alice \ - --rialto-port 9944 \ + --rialto-host wss.rialto.brucke.link \ + --rialto-port 443 \ + --rialto-secure \ --rialto-signer //Millau.HeadersAndMessagesRelay \ --rialto-transactions-mortality=64 \ --lane=00000000 \ diff --git a/deployments/networks/rialto.yml b/deployments/networks/rialto.yml index 1e2bdd3e41b..9d68af965df 100644 --- a/deployments/networks/rialto.yml +++ b/deployments/networks/rialto.yml @@ -56,6 +56,8 @@ services: - --unsafe-rpc-external - --unsafe-ws-external - --prometheus-external + environment: + RUST_LOG: runtime=trace,rpc=debug,txpool=trace,runtime::bridge=trace,beefy=debug,jsonrpsee=trace,jsonrpsee_core=trace,jsonrpsee_server=trace,rustls=trace,soketto=trace ports: - "10133:9933" - "10144:9944" diff --git a/fuzz/storage-proof/Cargo.toml b/fuzz/storage-proof/Cargo.toml index b406054bc6e..61d0e63a878 100644 --- a/fuzz/storage-proof/Cargo.toml +++ b/fuzz/storage-proof/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] honggfuzz = "0.5.54" log = "0.4.0" -env_logger = "0.8.3" +env_logger = "0.10.0" # Bridge Dependencies diff --git a/modules/relayers/Cargo.toml b/modules/relayers/Cargo.toml index c7ea1e544f2..954818a3b50 100644 --- a/modules/relayers/Cargo.toml +++ b/modules/relayers/Cargo.toml @@ -40,6 +40,7 @@ std = [ "bp-relayers/std", "bp-runtime/std", "codec/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", "log/std", diff --git a/primitives/chain-millau/Cargo.toml b/primitives/chain-millau/Cargo.toml index b422e1545d6..abcbd0f5123 100644 --- a/primitives/chain-millau/Cargo.toml +++ b/primitives/chain-millau/Cargo.toml @@ -13,10 +13,10 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" bp-beefy = { path = "../beefy", default-features = false } bp-messages = { path = "../messages", default-features = false } bp-runtime = { path = "../runtime", default-features = false } -fixed-hash = { version = "0.7.0", default-features = false } +fixed-hash = { version = "0.8.0", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } impl-codec = { version = "0.6", default-features = false } -impl-serde = { version = "0.3.1", optional = true } +impl-serde = { version = "0.4.0", optional = true } parity-util-mem = { version = "0.12", default-features = false, features = ["primitive-types"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } diff --git a/primitives/messages/src/target_chain.rs b/primitives/messages/src/target_chain.rs index 1b285b065aa..6604d425cfc 100644 --- a/primitives/messages/src/target_chain.rs +++ b/primitives/messages/src/target_chain.rs @@ -22,7 +22,7 @@ use bp_runtime::{messages::MessageDispatchResult, Size}; use codec::{Decode, Encode, Error as CodecError}; use frame_support::{weights::Weight, Parameter, RuntimeDebug}; use scale_info::TypeInfo; -use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, prelude::*}; +use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData, prelude::*}; /// Proved messages from the source chain. pub type ProvedMessages = BTreeMap>; @@ -164,15 +164,19 @@ impl DeliveryPayments for () { /// Structure that may be used in place of `SourceHeaderChain` and `MessageDispatch` on chains, /// where inbound messages are forbidden. -pub struct ForbidInboundMessages; +pub struct ForbidInboundMessages( + PhantomData<(MessagesProof, DispatchPayload)>, +); /// Error message that is used in `ForbidOutboundMessages` implementation. const ALL_INBOUND_MESSAGES_REJECTED: &str = "This chain is configured to reject all inbound messages"; -impl SourceHeaderChain for ForbidInboundMessages { +impl SourceHeaderChain + for ForbidInboundMessages +{ type Error = &'static str; - type MessagesProof = (); + type MessagesProof = MessagesProof; fn verify_messages_proof( _proof: Self::MessagesProof, @@ -182,8 +186,10 @@ impl SourceHeaderChain for ForbidInboundMessages { } } -impl MessageDispatch for ForbidInboundMessages { - type DispatchPayload = (); +impl MessageDispatch + for ForbidInboundMessages +{ + type DispatchPayload = DispatchPayload; type DispatchLevelResult = (); fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { diff --git a/primitives/relayers/Cargo.toml b/primitives/relayers/Cargo.toml index acede813995..7479811a57d 100644 --- a/primitives/relayers/Cargo.toml +++ b/primitives/relayers/Cargo.toml @@ -20,7 +20,6 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [dev-dependencies] -bp-rialto = { path = "../chain-rialto" } hex = "0.4" hex-literal = "0.3" diff --git a/primitives/relayers/src/lib.rs b/primitives/relayers/src/lib.rs index 207908296cb..70a91ee941a 100644 --- a/primitives/relayers/src/lib.rs +++ b/primitives/relayers/src/lib.rs @@ -37,6 +37,14 @@ pub trait PaymentProcedure { fn pay_reward(relayer: &Relayer, lane_id: LaneId, reward: Reward) -> Result<(), Self::Error>; } +impl PaymentProcedure for () { + type Error = &'static str; + + fn pay_reward(_: &Relayer, _: LaneId, _: Reward) -> Result<(), Self::Error> { + Ok(()) + } +} + /// Reward payment procedure that does `balances::transfer` call from the account, derived from /// given lane. pub struct PayLaneRewardFromAccount(PhantomData<(T, Relayer)>); @@ -88,21 +96,18 @@ where #[cfg(test)] mod tests { use super::*; + use sp_runtime::testing::H256; #[test] fn lanes_are_using_different_accounts() { assert_eq!( - PayLaneRewardFromAccount::<(), bp_rialto::AccountId>::lane_rewards_account(LaneId([ - 0, 0, 0, 0 - ])), + PayLaneRewardFromAccount::<(), H256>::lane_rewards_account(LaneId([0, 0, 0, 0])), hex_literal::hex!("626c616e000000006272696467652d6c616e6500000000000000000000000000") .into(), ); assert_eq!( - PayLaneRewardFromAccount::<(), bp_rialto::AccountId>::lane_rewards_account(LaneId([ - 0, 0, 0, 1 - ])), + PayLaneRewardFromAccount::<(), H256>::lane_rewards_account(LaneId([0, 0, 0, 1])), hex_literal::hex!("626c616e000000016272696467652d6c616e6500000000000000000000000000") .into(), ); diff --git a/relays/bin-substrate/Cargo.toml b/relays/bin-substrate/Cargo.toml index 4be812be86a..050be02e37e 100644 --- a/relays/bin-substrate/Cargo.toml +++ b/relays/bin-substrate/Cargo.toml @@ -19,7 +19,7 @@ rbtag = "0.3" structopt = "0.3" signal-hook = "0.3.14" signal-hook-async-std = "0.2.2" -strum = { version = "0.21.0", features = ["derive"] } +strum = { version = "0.24.1", features = ["derive"] } # Bridge dependencies bp-header-chain = { path = "../../primitives/header-chain" } diff --git a/relays/bin-substrate/src/chains/mod.rs b/relays/bin-substrate/src/chains/mod.rs index 7caba7561dd..7aad5f44e89 100644 --- a/relays/bin-substrate/src/chains/mod.rs +++ b/relays/bin-substrate/src/chains/mod.rs @@ -52,7 +52,7 @@ mod tests { #[test] fn maximal_rialto_to_millau_message_size_is_computed_correctly() { - use rialto_runtime::millau_messages::Millau; + use rialto_runtime::millau_messages::MillauAsTargetHeaderChain; let maximal_message_size = encode_message::compute_maximal_message_size( bp_rialto::Rialto::max_extrinsic_size(), @@ -60,10 +60,10 @@ mod tests { ); let message = vec![42; maximal_message_size as _]; - assert_eq!(Millau::verify_message(&message), Ok(())); + assert_eq!(MillauAsTargetHeaderChain::verify_message(&message), Ok(())); let message = vec![42; (maximal_message_size + 1) as _]; - assert!(Millau::verify_message(&message).is_err()); + assert!(MillauAsTargetHeaderChain::verify_message(&message).is_err()); } #[test] diff --git a/relays/client-substrate/Cargo.toml b/relays/client-substrate/Cargo.toml index 229de0cc09d..a16de01713b 100644 --- a/relays/client-substrate/Cargo.toml +++ b/relays/client-substrate/Cargo.toml @@ -15,7 +15,7 @@ log = "0.4.17" num-traits = "0.2" rand = "0.7" scale-info = { version = "2.1.1", features = ["derive"] } -tokio = { version = "1.8", features = ["rt-multi-thread"] } +tokio = { version = "1.24", features = ["rt-multi-thread"] } thiserror = "1.0.26" # Bridge dependencies diff --git a/relays/finality/Cargo.toml b/relays/finality/Cargo.toml index ae4be5b5524..401a02b2ca1 100644 --- a/relays/finality/Cargo.toml +++ b/relays/finality/Cargo.toml @@ -9,7 +9,7 @@ description = "Finality proofs relay" [dependencies] async-std = "1.6.5" async-trait = "0.1" -backoff = "0.2" +backoff = "0.4" bp-header-chain = { path = "../../primitives/header-chain" } futures = "0.3.5" log = "0.4.17" @@ -17,4 +17,4 @@ num-traits = "0.2" relay-utils = { path = "../utils" } [dev-dependencies] -parking_lot = "0.11.0" +parking_lot = "0.12.1" diff --git a/relays/messages/Cargo.toml b/relays/messages/Cargo.toml index e144926376c..e4564dac15f 100644 --- a/relays/messages/Cargo.toml +++ b/relays/messages/Cargo.toml @@ -12,7 +12,7 @@ futures = "0.3.5" hex = "0.4" log = "0.4.17" num-traits = "0.2" -parking_lot = "0.11.0" +parking_lot = "0.12.1" # Bridge Dependencies diff --git a/relays/parachains/Cargo.toml b/relays/parachains/Cargo.toml index 2af091c7ebb..1909630a6fb 100644 --- a/relays/parachains/Cargo.toml +++ b/relays/parachains/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] async-std = "1.6.5" -async-trait = "0.1.40" +async-trait = "0.1.62" futures = "0.3.5" log = "0.4.17" relay-utils = { path = "../utils" } diff --git a/relays/utils/Cargo.toml b/relays/utils/Cargo.toml index 09cec370900..0ad6168bb84 100644 --- a/relays/utils/Cargo.toml +++ b/relays/utils/Cargo.toml @@ -10,17 +10,17 @@ ansi_term = "0.12" anyhow = "1.0" async-std = "1.6.5" async-trait = "0.1" -backoff = "0.2" +backoff = "0.4" isahc = "1.2" -env_logger = "0.8.2" +env_logger = "0.10.0" futures = "0.3.5" -jsonpath_lib = "0.2" +jsonpath_lib = "0.3" log = "0.4.17" num-traits = "0.2" serde_json = "1.0" -sysinfo = "0.15" +sysinfo = "0.27" time = { version = "0.3", features = ["formatting", "local-offset", "std"] } -tokio = { version = "1.8", features = ["rt"] } +tokio = { version = "1.24", features = ["rt"] } thiserror = "1.0.26" # Bridge dependencies diff --git a/relays/utils/src/metrics/global.rs b/relays/utils/src/metrics/global.rs index df90a2c4823..f7d3e25c964 100644 --- a/relays/utils/src/metrics/global.rs +++ b/relays/utils/src/metrics/global.rs @@ -73,7 +73,7 @@ impl StandaloneMetric for GlobalMetrics { async fn update(&self) { // update system-wide metrics let mut system = self.system.lock().await; - let load = system.get_load_average(); + let load = system.load_average(); self.system_average_load.with_label_values(&["1min"]).set(load.one); self.system_average_load.with_label_values(&["5min"]).set(load.five); self.system_average_load.with_label_values(&["15min"]).set(load.fifteen); @@ -85,7 +85,7 @@ impl StandaloneMetric for GlobalMetrics { qed", ); let is_process_refreshed = system.refresh_process(pid); - match (is_process_refreshed, system.get_process(pid)) { + match (is_process_refreshed, system.process(pid)) { (true, Some(process_info)) => { let cpu_usage = process_info.cpu_usage() as f64; let memory_usage = process_info.memory() * 1024; diff --git a/scripts/verify-partial-repo-build.sh b/scripts/verify-partial-repo-build.sh new file mode 100755 index 00000000000..d7de84d620f --- /dev/null +++ b/scripts/verify-partial-repo-build.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# A script to remove everything from bridges repository/subtree, except: +# +# - modules/grandpa; +# - modules/messages; +# - modules/parachains; +# - modules/relayers; +# - everything required from primitives folder. + +# the script is able to work only on clean git copy +[[ -z "$(git status --porcelain)" ]] || { echo >&2 "The git copy must be clean"; exit 1; } + +# let's avoid any restrictions on where this script can be called for - bridges repo may be +# plugged into any other repo folder. So the script (and other stuff that needs to be removed) +# may be located either in call dir, or one of it subdirs. +BRIDGES_FOLDER="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )/.." + +# let's leave repository/subtree in its original (clean) state if something fails below +function revert_to_clean_state { + echo "Reverting to clean state..." + git reset --hard +} +trap revert_to_clean_state EXIT + +# remove everything we think is not required for our needs +rm -rf $BRIDGES_FOLDER/.config +rm -rf $BRIDGES_FOLDER/.github +rm -rf $BRIDGES_FOLDER/.maintain +rm -rf $BRIDGES_FOLDER/bin/millau +rm -rf $BRIDGES_FOLDER/bin/rialto +rm -rf $BRIDGES_FOLDER/bin/rialto-parachain +rm -rf $BRIDGES_FOLDER/deployments +rm -rf $BRIDGES_FOLDER/fuzz +rm -rf $BRIDGES_FOLDER/modules/beefy +rm -rf $BRIDGES_FOLDER/modules/shift-session-manager +rm -rf $BRIDGES_FOLDER/primitives/beefy +rm -rf $BRIDGES_FOLDER/primitives/chain-millau +rm -rf $BRIDGES_FOLDER/primitives/chain-rialto +rm -rf $BRIDGES_FOLDER/primitives/chain-rialto-parachain +rm -rf $BRIDGES_FOLDER/primitives/chain-westend +rm -rf $BRIDGES_FOLDER/relays +rm -f $BRIDGES_FOLDER/.dockerignore +rm -f $BRIDGES_FOLDER/.gitlab-ci.yml +rm -f $BRIDGES_FOLDER/Cargo.lock +rm -f $BRIDGES_FOLDER/Cargo.toml +rm -f $BRIDGES_FOLDER/ci.Dockerfile +rm -f $BRIDGES_FOLDER/Dockerfile + +# let's fix Cargo.toml a bit (it'll be helpful if we are in the bridges repo) + +cat > $BRIDGES_FOLDER/Cargo.toml <<-CARGO_TOML +[workspace] +resolver = "2" + +members = [ + "bin/runtime-common", + "modules/*", + "primitives/*", +] +CARGO_TOML + +# let's test if everything we need compiles + +cargo check -p pallet-bridge-grandpa +cargo check -p pallet-bridge-grandpa --features runtime-benchmarks +cargo check -p pallet-bridge-grandpa --features try-runtime +cargo check -p pallet-bridge-messages +cargo check -p pallet-bridge-messages --features runtime-benchmarks +cargo check -p pallet-bridge-messages --features try-runtime +cargo check -p pallet-bridge-parachains +cargo check -p pallet-bridge-parachains --features runtime-benchmarks +cargo check -p pallet-bridge-parachains --features try-runtime +cargo check -p pallet-bridge-relayers +cargo check -p pallet-bridge-relayers --features runtime-benchmarks +cargo check -p pallet-bridge-relayers --features try-runtime +cargo check -p bridge-runtime-common +cargo check -p bridge-runtime-common --features runtime-benchmarks + +echo "OK" From 8e24c8caafe3e9ae61bd7864f461d68d2eb81a77 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Sun, 22 Jan 2023 21:27:50 +0100 Subject: [PATCH 186/263] Fix `send-remark-local` script because of new xcm-v3 --- scripts/bridges_rococo_wococo.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh index c025093d709..9fa1e29a653 100755 --- a/scripts/bridges_rococo_wococo.sh +++ b/scripts/bridges_rococo_wococo.sh @@ -79,9 +79,12 @@ function send_xcm_transact_remark_from_statemine() { { "Transact": { "origin_kind": "SovereignAccount", - "require_weight_at_most": 1000000000, + "require_weight_at_most": { + "ref_time": 1000000000, + "proof_size": 0, + }, "call": { - "encoded": [0, 8, 20, 104, 101, 108, 108, 111 ] + "encoded": [0, 7, 20, 72, 101, 108, 108, 111 ] } } } From 1a1a4f502b926d69947cba6ebabb0b5ccf4a60dc Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 23 Jan 2023 13:53:23 +0100 Subject: [PATCH 187/263] Refactor haul/dispatch xcm stuff --- .../src/validate_block/implementation.rs | 8 ++-- .../src/bridge_common_config.rs | 38 +++++++++++-------- .../src/bridge_hub_rococo_config.rs | 7 +++- .../src/bridge_hub_wococo_config.rs | 7 +++- .../bridge-hub-rococo/src/xcm_config.rs | 2 +- 5 files changed, 37 insertions(+), 25 deletions(-) diff --git a/pallets/parachain-system/src/validate_block/implementation.rs b/pallets/parachain-system/src/validate_block/implementation.rs index 537f067e1c6..06018da4229 100644 --- a/pallets/parachain-system/src/validate_block/implementation.rs +++ b/pallets/parachain-system/src/validate_block/implementation.rs @@ -22,18 +22,16 @@ use cumulus_primitives_core::{ }; use cumulus_primitives_parachain_inherent::ParachainInherentData; -use polkadot_parachain::primitives::{ - HeadData, RelayChainBlockNumber, ValidationParams, ValidationResult, -}; +use polkadot_parachain::primitives::{HeadData, RelayChainBlockNumber, ValidationResult}; -use codec::{Decode, Encode}; +use codec::Encode; use frame_support::traits::{ExecuteBlock, ExtrinsicCall, Get, IsSubType}; use sp_core::storage::{ChildInfo, StateVersion}; use sp_externalities::{set_and_run_with_externalities, Externalities}; use sp_io::KillStorageResult; use sp_runtime::traits::{Block as BlockT, Extrinsic, HashFor, Header as HeaderT}; -use sp_std::{mem, prelude::*}; +use sp_std::prelude::*; use sp_trie::MemoryDB; type TrieBackend = sp_state_machine::TrieBackend>, HashFor>; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 0fcfdba10da..708d6020eeb 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -23,12 +23,12 @@ use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, Chain}; use codec::{Decode, Encode}; use frame_support::{dispatch::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; use scale_info::TypeInfo; -use xcm::latest::prelude::*; use xcm_builder::{DispatchBlob, DispatchBlobError, HaulBlob, HaulBlobError}; /// PLain "XCM" payload, which we transfer through bridge pub type XcmAsPlainPayload = sp_std::prelude::Vec; +/// Message dispatch result type for single message #[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] pub enum XcmBlobMessageDispatchResult { InvalidPayload, @@ -62,11 +62,6 @@ impl, message: DispatchMessage, ) -> MessageDispatchResult { - log::warn!( - target: crate::LOG_TARGET, - "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob triggering - message_nonce: {:?}", - message.key.nonce - ); let payload = match message.data.payload { Ok(payload) => payload, Err(e) => { @@ -125,29 +120,42 @@ impl; + type MessageSender: MessagesBridge; + /// Runtime message sender origin, which is used by MessageSender. + type MessageSenderOrigin; /// Our location within the Consensus Universe. - fn message_sender_origin() -> InteriorMultiLocation; + fn message_sender_origin() -> Self::MessageSenderOrigin; /// Return message lane (as "point-to-point link") used to deliver XCM messages. fn xcm_lane() -> LaneId; } pub struct XcmBlobHaulerAdapter(sp_std::marker::PhantomData); -impl HaulBlob for XcmBlobHaulerAdapter { +impl> HaulBlob + for XcmBlobHaulerAdapter +{ fn haul_blob(blob: sp_std::prelude::Vec) -> Result<(), HaulBlobError> { let lane = H::xcm_lane(); - let result = H::MessageSender::send_message( - pallet_xcm::Origin::from(MultiLocation::from(H::message_sender_origin())).into(), - lane, - blob, - ); + let result = H::MessageSender::send_message(H::message_sender_origin(), lane, blob); let result = result.map(|artifacts| { let hash = (lane, artifacts.nonce).using_encoded(sp_io::hashing::blake2_256); hash }); - log::info!(target: crate::LOG_TARGET, "haul_blob result: {:?} on lane: {:?}", result, lane); + match &result { + Ok(result) => log::info!( + target: crate::LOG_TARGET, + "haul_blob result - ok: {:?} on lane: {:?}", + result, + lane + ), + Err(error) => log::error!( + target: crate::LOG_TARGET, + "haul_blob result - error: {:?} on lane: {:?}", + error, + lane + ), + }; result.map(|_| ()).map_err(|_| HaulBlobError::Transport("MessageSenderError")) } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index 47131b0d565..3f85f457b62 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -65,8 +65,11 @@ impl XcmBlobHauler for ToBridgeHubWococoXcmBlobHauler { type MessageSender = pallet_bridge_messages::Pallet; - fn message_sender_origin() -> InteriorMultiLocation { - crate::xcm_config::UniversalLocation::get() + type MessageSenderOrigin = super::RuntimeOrigin; + + fn message_sender_origin() -> Self::MessageSenderOrigin { + pallet_xcm::Origin::from(MultiLocation::new(1, crate::xcm_config::UniversalLocation::get())) + .into() } fn xcm_lane() -> LaneId { diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index a7077734e15..798c8267517 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -65,8 +65,11 @@ impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { type MessageSender = pallet_bridge_messages::Pallet; - fn message_sender_origin() -> InteriorMultiLocation { - crate::xcm_config::UniversalLocation::get() + type MessageSenderOrigin = super::RuntimeOrigin; + + fn message_sender_origin() -> super::RuntimeOrigin { + pallet_xcm::Origin::from(MultiLocation::new(1, crate::xcm_config::UniversalLocation::get())) + .into() } fn xcm_lane() -> LaneId { diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index dcad27b36ed..6bac53894a6 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -51,7 +51,7 @@ use polkadot_runtime_common::impls::ToAuthor; parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorMultiLocation = + pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; From 5ad38a16bd4875a8dff4cb063f8242d3b11e5fa3 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Wed, 25 Jan 2023 13:13:10 +0200 Subject: [PATCH 188/263] Add Rococo <> Wococo integrity tests (#1975) * Remove 1 integrity test In sync with https://github.com/paritytech/parity-bridges-common/pull/1816 * use TargetHeaderChainAdapter and SourceHeaderChainAdapter * Rococo <-> Wococo integrity tests * Add message lane weights tests * Add signed extension integrity test --- Cargo.lock | 45 +++++++- bridges/bin/runtime-common/src/integrity.rs | 1 - .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 3 + .../src/bridge_hub_rococo_config.rs | 106 +++++++++++------- .../src/bridge_hub_wococo_config.rs | 106 +++++++++++------- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 54 ++++++++- 6 files changed, 223 insertions(+), 92 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 42cf68cef9e..9d1b0b71d7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -982,6 +982,7 @@ dependencies = [ "rococo-runtime-constants", "scale-info", "serde", + "serial_test", "smallvec", "sp-api", "sp-block-builder", @@ -995,6 +996,7 @@ dependencies = [ "sp-std", "sp-transaction-pool", "sp-version", + "static_assertions", "substrate-wasm-builder", "xcm", "xcm-builder", @@ -2753,6 +2755,17 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8858831f7781322e539ea39e72449c46b059638250c14344fec8d0aa6e539c" +dependencies = [ + "cfg-if", + "num_cpus", + "parking_lot 0.12.1", +] + [[package]] name = "data-encoding" version = "2.3.2" @@ -5909,11 +5922,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi 0.2.6", "libc", ] @@ -11508,6 +11521,32 @@ dependencies = [ "serde", ] +[[package]] +name = "serial_test" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92761393ee4dc3ff8f4af487bd58f4307c9329bbedea02cac0089ad9c411e153" +dependencies = [ + "dashmap", + "futures", + "lazy_static", + "log", + "parking_lot 0.12.1", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6f5d1c3087fb119617cff2966fe3808a80e5eb59a8c1601d5994d66f4346a5" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sha-1" version = "0.8.2" diff --git a/bridges/bin/runtime-common/src/integrity.rs b/bridges/bin/runtime-common/src/integrity.rs index 11c6cb90a83..51fb50df7e1 100644 --- a/bridges/bin/runtime-common/src/integrity.rs +++ b/bridges/bin/runtime-common/src/integrity.rs @@ -90,7 +90,6 @@ macro_rules! assert_bridge_messages_pallet_types( assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, FromThisChainMessagePayload); - assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundPayload, FromBridgedChainMessagePayload>>); assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundRelayer, AccountIdOf>); assert_type_eq_all!(<$r as MessagesConfig<$i>>::TargetHeaderChain, TargetHeaderChainAdapter<$bridge>); diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 4fc2cb1b445..f6c42988383 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -86,7 +86,10 @@ pallet-bridge-relayers = { path = "../../../../bridges/modules/relayers", defaul bridge-runtime-common = { path = "../../../../bridges/bin/runtime-common", default-features = false } [dev-dependencies] +serial_test = "0.9.0" +static_assertions = "1.1" bridge-hub-test-utils = { path = "../test-utils"} +bridge-runtime-common = { path = "../../../../bridges/bin/runtime-common", features = ["integrity-test"] } [features] default = [ diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index 3f85f457b62..cdaaa8bb49e 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -16,20 +16,13 @@ use crate::{ BridgeParachainWococoInstance, ParachainInfo, Runtime, WithBridgeHubWococoMessagesInstance, - XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, XcmRouter, -}; -use bp_messages::{ - source_chain::TargetHeaderChain, - target_chain::{ProvedMessages, SourceHeaderChain}, - InboundLaneData, LaneId, Message, MessageNonce, + XcmBlobHauler, XcmBlobHaulerAdapter, XcmRouter, }; +use bp_messages::{LaneId, MessageNonce}; use bp_runtime::ChainId; use bridge_runtime_common::{ messages, - messages::{ - target::FromBridgedChainMessagesProof, MessageBridge, ThisChainWithMessages, - UnderlyingChainProvider, - }, + messages::{MessageBridge, ThisChainWithMessages, UnderlyingChainProvider}, }; use frame_support::{parameter_types, RuntimeDebug}; use xcm::{ @@ -110,37 +103,6 @@ impl UnderlyingChainProvider for BridgeHubWococo { type Chain = bp_bridge_hub_wococo::BridgeHubWococo; } -impl SourceHeaderChain for BridgeHubWococo { - type Error = &'static str; - type MessagesProof = FromBridgedChainMessagesProof; - - fn verify_messages_proof( - proof: Self::MessagesProof, - messages_count: u32, - ) -> Result, Self::Error> { - bridge_runtime_common::messages::target::verify_messages_proof::< - WithBridgeHubWococoMessageBridge, - >(proof, messages_count) - .map_err(Into::into) - } -} - -impl TargetHeaderChain for BridgeHubWococo { - type Error = &'static str; - type MessagesDeliveryProof = - messages::source::FromBridgedChainMessagesDeliveryProof; - - fn verify_message(payload: &XcmAsPlainPayload) -> Result<(), Self::Error> { - messages::source::verify_chain_message::(payload) - } - - fn verify_messages_delivery_proof( - proof: Self::MessagesDeliveryProof, - ) -> Result<(LaneId, InboundLaneData), Self::Error> { - messages::source::verify_messages_delivery_proof::(proof) - } -} - impl messages::BridgedChainWithMessages for BridgeHubWococo { fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { true @@ -172,3 +134,65 @@ impl ThisChainWithMessages for BridgeHubRococo { MessageNonce::MAX / 2 } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::BridgeGrandpaWococoInstance; + use bridge_runtime_common::{ + assert_complete_bridge_types, + integrity::{ + assert_complete_bridge_constants, check_message_lane_weights, + AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants, + AssertCompleteBridgeConstants, + }, + }; + + #[test] + fn ensure_bridge_hub_rococo_message_lane_weights_are_correct() { + check_message_lane_weights::( + bp_bridge_hub_wococo::EXTRA_STORAGE_PROOF_SIZE, + bp_bridge_hub_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + bp_bridge_hub_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + ); + } + + #[test] + fn ensure_bridge_integrity() { + assert_complete_bridge_types!( + runtime: Runtime, + with_bridged_chain_grandpa_instance: BridgeGrandpaWococoInstance, + with_bridged_chain_messages_instance: WithBridgeHubWococoMessagesInstance, + bridge: WithBridgeHubWococoMessageBridge, + this_chain: bp_rococo::Rococo, + bridged_chain: bp_wococo::Wococo, + ); + + assert_complete_bridge_constants::< + Runtime, + BridgeGrandpaWococoInstance, + WithBridgeHubWococoMessagesInstance, + WithBridgeHubWococoMessageBridge, + bp_rococo::Rococo, + >(AssertCompleteBridgeConstants { + this_chain_constants: AssertChainConstants { + block_length: bp_bridge_hub_rococo::BlockLength::get(), + block_weights: bp_bridge_hub_rococo::BlockWeights::get(), + }, + messages_pallet_constants: AssertBridgeMessagesPalletConstants { + max_unrewarded_relayers_in_bridged_confirmation_tx: + bp_bridge_hub_wococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + max_unconfirmed_messages_in_bridged_confirmation_tx: + bp_bridge_hub_wococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + bridged_chain_id: bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID, + }, + pallet_names: AssertBridgePalletNames { + with_this_chain_messages_pallet_name: + bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME, + with_bridged_chain_grandpa_pallet_name: bp_wococo::WITH_WOCOCO_GRANDPA_PALLET_NAME, + with_bridged_chain_messages_pallet_name: + bp_bridge_hub_wococo::WITH_BRIDGE_HUB_WOCOCO_MESSAGES_PALLET_NAME, + }, + }); + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 798c8267517..93b85dde9ed 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -16,20 +16,13 @@ use crate::{ BridgeParachainRococoInstance, ParachainInfo, Runtime, WithBridgeHubRococoMessagesInstance, - XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, XcmRouter, -}; -use bp_messages::{ - source_chain::TargetHeaderChain, - target_chain::{ProvedMessages, SourceHeaderChain}, - InboundLaneData, LaneId, Message, MessageNonce, + XcmBlobHauler, XcmBlobHaulerAdapter, XcmRouter, }; +use bp_messages::{LaneId, MessageNonce}; use bp_runtime::ChainId; use bridge_runtime_common::{ messages, - messages::{ - target::FromBridgedChainMessagesProof, MessageBridge, ThisChainWithMessages, - UnderlyingChainProvider, - }, + messages::{MessageBridge, ThisChainWithMessages, UnderlyingChainProvider}, }; use frame_support::{parameter_types, RuntimeDebug}; use xcm::{ @@ -110,37 +103,6 @@ impl UnderlyingChainProvider for BridgeHubRococo { type Chain = bp_bridge_hub_rococo::BridgeHubRococo; } -impl SourceHeaderChain for BridgeHubRococo { - type Error = &'static str; - type MessagesProof = FromBridgedChainMessagesProof; - - fn verify_messages_proof( - proof: Self::MessagesProof, - messages_count: u32, - ) -> Result, Self::Error> { - bridge_runtime_common::messages::target::verify_messages_proof::< - WithBridgeHubRococoMessageBridge, - >(proof, messages_count) - .map_err(Into::into) - } -} - -impl TargetHeaderChain for BridgeHubRococo { - type Error = &'static str; - type MessagesDeliveryProof = - messages::source::FromBridgedChainMessagesDeliveryProof; - - fn verify_message(payload: &XcmAsPlainPayload) -> Result<(), Self::Error> { - messages::source::verify_chain_message::(payload) - } - - fn verify_messages_delivery_proof( - proof: Self::MessagesDeliveryProof, - ) -> Result<(LaneId, InboundLaneData), Self::Error> { - messages::source::verify_messages_delivery_proof::(proof) - } -} - impl messages::BridgedChainWithMessages for BridgeHubRococo { fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { true @@ -172,3 +134,65 @@ impl ThisChainWithMessages for BridgeHubWococo { MessageNonce::MAX / 2 } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::BridgeGrandpaRococoInstance; + use bridge_runtime_common::{ + assert_complete_bridge_types, + integrity::{ + assert_complete_bridge_constants, check_message_lane_weights, + AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants, + AssertCompleteBridgeConstants, + }, + }; + + #[test] + fn ensure_bridge_hub_wococo_message_lane_weights_are_correct() { + check_message_lane_weights::( + bp_bridge_hub_rococo::EXTRA_STORAGE_PROOF_SIZE, + bp_bridge_hub_wococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + bp_bridge_hub_wococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + ); + } + + #[test] + fn ensure_bridge_integrity() { + assert_complete_bridge_types!( + runtime: Runtime, + with_bridged_chain_grandpa_instance: BridgeGrandpaRococoInstance, + with_bridged_chain_messages_instance: WithBridgeHubRococoMessagesInstance, + bridge: WithBridgeHubRococoMessageBridge, + this_chain: bp_wococo::Wococo, + bridged_chain: bp_rococo::Rococo, + ); + + assert_complete_bridge_constants::< + Runtime, + BridgeGrandpaRococoInstance, + WithBridgeHubRococoMessagesInstance, + WithBridgeHubRococoMessageBridge, + bp_wococo::Wococo, + >(AssertCompleteBridgeConstants { + this_chain_constants: AssertChainConstants { + block_length: bp_bridge_hub_wococo::BlockLength::get(), + block_weights: bp_bridge_hub_wococo::BlockWeights::get(), + }, + messages_pallet_constants: AssertBridgeMessagesPalletConstants { + max_unrewarded_relayers_in_bridged_confirmation_tx: + bp_bridge_hub_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + max_unconfirmed_messages_in_bridged_confirmation_tx: + bp_bridge_hub_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + bridged_chain_id: bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID, + }, + pallet_names: AssertBridgePalletNames { + with_this_chain_messages_pallet_name: + bp_bridge_hub_wococo::WITH_BRIDGE_HUB_WOCOCO_MESSAGES_PALLET_NAME, + with_bridged_chain_grandpa_pallet_name: bp_rococo::WITH_ROCOCO_GRANDPA_PALLET_NAME, + with_bridged_chain_messages_pallet_name: + bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME, + }, + }); + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index d18f25b4e19..6fd79614ba0 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -73,10 +73,14 @@ use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use crate::{ - bridge_hub_rococo_config::OnBridgeHubRococoBlobDispatcher, - bridge_hub_wococo_config::OnBridgeHubWococoBlobDispatcher, constants::fee::WeightToFee, + bridge_hub_rococo_config::{OnBridgeHubRococoBlobDispatcher, WithBridgeHubWococoMessageBridge}, + bridge_hub_wococo_config::{OnBridgeHubWococoBlobDispatcher, WithBridgeHubRococoMessageBridge}, + constants::fee::WeightToFee, xcm_config::XcmRouter, }; +use bridge_runtime_common::messages::{ + source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, +}; use parachains_common::{ opaque, AccountId, Balance, BlockNumber, Hash, Header, Index, Signature, AVERAGE_ON_INITIALIZE_RATIO, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, @@ -469,12 +473,12 @@ impl pallet_bridge_messages::Config for Run // TODO:check-parameter - check delivery type DeliveryPayments = (); - type TargetHeaderChain = bridge_hub_rococo_config::BridgeHubWococo; + type TargetHeaderChain = TargetHeaderChainAdapter; type LaneMessageVerifier = bridge_hub_rococo_config::ToBridgeHubWococoMessageVerifier; // TODO:check-parameter - check delivery type DeliveryConfirmationPayments = (); - type SourceHeaderChain = bridge_hub_rococo_config::BridgeHubWococo; + type SourceHeaderChain = SourceHeaderChainAdapter; type MessageDispatch = XcmBlobMessageDispatch< bp_bridge_hub_wococo::BridgeHubWococo, bp_bridge_hub_rococo::BridgeHubRococo, @@ -503,12 +507,12 @@ impl pallet_bridge_messages::Config for Run // TODO:check-parameter - check delivery type DeliveryPayments = (); - type TargetHeaderChain = bridge_hub_wococo_config::BridgeHubRococo; + type TargetHeaderChain = TargetHeaderChainAdapter; type LaneMessageVerifier = bridge_hub_wococo_config::ToBridgeHubRococoMessageVerifier; // TODO:check-parameter - check delivery type DeliveryConfirmationPayments = (); - type SourceHeaderChain = bridge_hub_wococo_config::BridgeHubRococo; + type SourceHeaderChain = SourceHeaderChainAdapter; type MessageDispatch = XcmBlobMessageDispatch< bp_bridge_hub_rococo::BridgeHubRococo, bp_bridge_hub_wococo::BridgeHubWococo, @@ -972,3 +976,41 @@ cumulus_pallet_parachain_system::register_validate_block! { BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, CheckInherents = CheckInherents, } + +#[cfg(test)] +mod tests { + use super::*; + use bridge_runtime_common::integrity::check_additional_signed; + use codec::Encode; + use sp_runtime::generic::Era; + + #[test] + fn ensure_signed_extension_definition_is_correct() { + let payload: SignedExtra = ( + frame_system::CheckNonZeroSender::new(), + frame_system::CheckSpecVersion::new(), + frame_system::CheckTxVersion::new(), + frame_system::CheckGenesis::new(), + frame_system::CheckEra::from(Era::Immortal), + frame_system::CheckNonce::from(10), + frame_system::CheckWeight::new(), + pallet_transaction_payment::ChargeTransactionPayment::from(10), + BridgeRejectObsoleteHeadersAndMessages {}, + ); + + let bhr_indirect_payload = bp_bridge_hub_rococo::SignedExtension::new( + ((), (), (), (), Era::Immortal, 10.into(), (), 10.into(), ()), + None, + ); + assert_eq!(payload.encode(), bhr_indirect_payload.encode()); + + let bhw_indirect_payload = bp_bridge_hub_wococo::SignedExtension::new( + ((), (), (), (), Era::Immortal, 10.into(), (), 10.into(), ()), + None, + ); + assert_eq!(payload.encode(), bhw_indirect_payload.encode()); + + check_additional_signed::(); + check_additional_signed::(); + } +} From 34e8e97c98f9a442ad456344c643d573f0ab5374 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 26 Jan 2023 11:04:36 +0300 Subject: [PATCH 189/263] Bridge benchmarks for bridge hub rococo/wococo (#2107) * fix benchmarks compilation and add bridges benchmarks (prototype) to RBH/WBH * post-merge fixes * remove duplicate "pallet-collator-selection/runtime-benchmarks" * ".git/.scripts/commands/bench/bench.sh" pallet bridge-hub-rococo bridge-hubs pallet_bridge_grandpa * ".git/.scripts/commands/bench/bench.sh" pallet bridge-hub-rococo bridge-hubs pallet_bridge_parachains * remove methods that are no longer required * fixed helpers used in bridge hub messages palelt benchmarks * unused imports * compilation * compilation * benchmarks-ci.sh * ".git/.scripts/commands/bench/bench.sh" pallet bridge-hub-rococo bridge-hubs pallet_bridge_messages * use generated weights in pallets configuration * add mod for new weights * impl WeightInfoExt Co-authored-by: command-bot <> --- Cargo.lock | 1 + bridges/bin/runtime-common/Cargo.toml | 1 + .../src/messages_benchmarking.rs | 201 ++++++++++++++---- bridges/modules/parachains/src/lib.rs | 26 +++ .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 9 +- .../src/bridge_hub_rococo_config.rs | 14 +- .../src/bridge_hub_wococo_config.rs | 14 +- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 147 ++++++++++++- .../bridge-hub-rococo/src/weights/mod.rs | 30 +++ ...et_bridge_grandpa_bridge_rococo_grandpa.rs | 71 +++++++ ...et_bridge_grandpa_bridge_wococo_grandpa.rs | 71 +++++++ ...ith_bridge_hub_rococo_messages_instance.rs | 125 +++++++++++ ...ith_bridge_hub_wococo_messages_instance.rs | 125 +++++++++++ ...untime_bridge_parachain_rococo_instance.rs | 85 ++++++++ ...untime_bridge_parachain_wococo_instance.rs | 87 ++++++++ scripts/benchmarks-ci.sh | 3 + 16 files changed, 954 insertions(+), 56 deletions(-) create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_rococo_grandpa.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_wococo_grandpa.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance.rs diff --git a/Cargo.lock b/Cargo.lock index 9d1b0b71d7e..8430cd5d69e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -934,6 +934,7 @@ dependencies = [ "bp-bridge-hub-wococo", "bp-messages", "bp-parachains", + "bp-polkadot-core", "bp-rococo", "bp-runtime", "bp-wococo", diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml index 9e8e6b29db2..7d538671646 100644 --- a/bridges/bin/runtime-common/Cargo.toml +++ b/bridges/bin/runtime-common/Cargo.toml @@ -82,6 +82,7 @@ std = [ runtime-benchmarks = [ "pallet-bridge-grandpa/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks", + "pallet-bridge-parachains/runtime-benchmarks", "xcm-builder/runtime-benchmarks", ] integrity-test = [ diff --git a/bridges/bin/runtime-common/src/messages_benchmarking.rs b/bridges/bin/runtime-common/src/messages_benchmarking.rs index afe95422d18..e7ca26d952e 100644 --- a/bridges/bin/runtime-common/src/messages_benchmarking.rs +++ b/bridges/bin/runtime-common/src/messages_benchmarking.rs @@ -22,7 +22,7 @@ use crate::{ messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, - AccountIdOf, BalanceOf, BridgedChain, CallOf, HashOf, MessageBridge, ThisChain, + AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, RawStorageProof, ThisChain, }, messages_generation::{ encode_all_messages, encode_lane_data, grow_trie, prepare_messages_storage_proof, @@ -30,52 +30,46 @@ use crate::{ }; use bp_messages::storage_keys; -use bp_runtime::{record_all_trie_keys, StorageProofSize}; +use bp_polkadot_core::parachains::ParaHash; +use bp_runtime::{record_all_trie_keys, Chain, Parachain, StorageProofSize, UnderlyingChainOf}; use codec::Encode; -use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; +use frame_support::weights::Weight; use pallet_bridge_messages::benchmarking::{MessageDeliveryProofParams, MessageProofParams}; -use sp_core::Hasher; -use sp_runtime::traits::{Header, MaybeSerializeDeserialize, Zero}; -use sp_std::{fmt::Debug, prelude::*}; +use sp_runtime::traits::{Header, Zero}; +use sp_std::prelude::*; use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; /// Prepare proof of messages for the `receive_messages_proof` call. /// /// In addition to returning valid messages proof, environment is prepared to verify this message /// proof. -pub fn prepare_message_proof( +/// +/// This method is intended to be used when benchmarking pallet, linked to the chain that +/// uses GRANDPA finality. For parachains, please use the `prepare_message_proof_from_parachain` +/// function. +pub fn prepare_message_proof_from_grandpa_chain( params: MessageProofParams, ) -> (FromBridgedChainMessagesProof>>, Weight) where - R: frame_system::Config>> - + pallet_bridge_grandpa::Config, - R::BridgedChain: bp_runtime::Chain>, Header = BH>, - B: MessageBridge, - BI: 'static, + R: pallet_bridge_grandpa::Config>>, FI: 'static, - BH: Header>>, - BHH: Hasher>>, - AccountIdOf>: PartialEq + sp_std::fmt::Debug, - AccountIdOf>: From<[u8; 32]>, - BalanceOf>: Debug + MaybeSerializeDeserialize, - CallOf>: From> + GetDispatchInfo, - HashOf>: Copy + Default, + B: MessageBridge, { - let message_payload = match params.size { - StorageProofSize::Minimal(ref size) => vec![0u8; *size as _], - _ => vec![], - }; - - // finally - prepare storage proof and update environment + // prepare storage proof let (state_root, storage_proof) = prepare_messages_storage_proof::( params.lane, params.message_nonces.clone(), params.outbound_lane_data, params.size, - message_payload, + match params.size { + StorageProofSize::Minimal(ref size) => vec![0u8; *size as _], + _ => vec![], + }, encode_all_messages, encode_lane_data, ); + + // update runtime storage let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); ( @@ -90,18 +84,115 @@ where ) } +/// Prepare proof of messages for the `receive_messages_proof` call. +/// +/// In addition to returning valid messages proof, environment is prepared to verify this message +/// proof. +/// +/// This method is intended to be used when benchmarking pallet, linked to the chain that +/// uses parachain finality. For GRANDPA chains, please use the +/// `prepare_message_proof_from_grandpa_chain` function. +pub fn prepare_message_proof_from_parachain( + params: MessageProofParams, +) -> (FromBridgedChainMessagesProof>>, Weight) +where + R: pallet_bridge_parachains::Config, + PI: 'static, + B: MessageBridge, + UnderlyingChainOf>: Chain + Parachain, +{ + // prepare storage proof + let (state_root, storage_proof) = prepare_messages_storage_proof::( + params.lane, + params.message_nonces.clone(), + params.outbound_lane_data, + params.size, + match params.size { + StorageProofSize::Minimal(ref size) => vec![0u8; *size as _], + _ => vec![], + }, + encode_all_messages, + encode_lane_data, + ); + + // update runtime storage + let (_, bridged_header_hash) = + insert_header_to_parachains_pallet::>>(state_root); + + ( + FromBridgedChainMessagesProof { + bridged_header_hash, + storage_proof, + lane: params.lane, + nonces_start: *params.message_nonces.start(), + nonces_end: *params.message_nonces.end(), + }, + Weight::zero(), + ) +} + /// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call. -pub fn prepare_message_delivery_proof( +/// +/// This method is intended to be used when benchmarking pallet, linked to the chain that +/// uses GRANDPA finality. For parachains, please use the +/// `prepare_message_delivery_proof_from_parachain` function. +pub fn prepare_message_delivery_proof_from_grandpa_chain( params: MessageDeliveryProofParams>>, ) -> FromBridgedChainMessagesDeliveryProof>> where - R: pallet_bridge_grandpa::Config, - R::BridgedChain: bp_runtime::Chain>, Header = BH>, + R: pallet_bridge_grandpa::Config>>, FI: 'static, B: MessageBridge, - BH: Header>>, - BHH: Hasher>>, - HashOf>: Copy + Default, +{ + // prepare storage proof + let lane = params.lane; + let (state_root, storage_proof) = prepare_message_delivery_proof::(params); + + // update runtime storage + let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); + + FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: bridged_header_hash.into(), + storage_proof, + lane, + } +} + +/// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call. +/// +/// This method is intended to be used when benchmarking pallet, linked to the chain that +/// uses parachain finality. For GRANDPA chains, please use the +/// `prepare_message_delivery_proof_from_grandpa_chain` function. +pub fn prepare_message_delivery_proof_from_parachain( + params: MessageDeliveryProofParams>>, +) -> FromBridgedChainMessagesDeliveryProof>> +where + R: pallet_bridge_parachains::Config, + PI: 'static, + B: MessageBridge, + UnderlyingChainOf>: Chain + Parachain, +{ + // prepare storage proof + let lane = params.lane; + let (state_root, storage_proof) = prepare_message_delivery_proof::(params); + + // update runtime storage + let (_, bridged_header_hash) = + insert_header_to_parachains_pallet::>>(state_root); + + FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: bridged_header_hash.into(), + storage_proof, + lane, + } +} + +/// Prepare in-memory message delivery proof, without inserting anything to the runtime storage. +fn prepare_message_delivery_proof( + params: MessageDeliveryProofParams>>, +) -> (HashOf>, RawStorageProof) +where + B: MessageBridge, { // prepare Bridged chain storage with inbound lane state let storage_key = @@ -109,7 +200,8 @@ where let mut root = Default::default(); let mut mdb = MemoryDB::default(); { - let mut trie = TrieDBMutBuilderV1::::new(&mut mdb, &mut root).build(); + let mut trie = + TrieDBMutBuilderV1::>>::new(&mut mdb, &mut root).build(); trie.insert(&storage_key, ¶ms.inbound_lane_data.encode()) .map_err(|_| "TrieMut::insert has failed") .expect("TrieMut::insert should not fail in benchmarks"); @@ -117,20 +209,17 @@ where root = grow_trie(root, &mut mdb, params.size); // generate storage proof to be delivered to This chain - let mut proof_recorder = Recorder::>::new(); - record_all_trie_keys::, _>(&mdb, &root, &mut proof_recorder) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); + let mut proof_recorder = Recorder::>>>::new(); + record_all_trie_keys::>>, _>( + &mdb, + &root, + &mut proof_recorder, + ) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); - // finally insert header with given state root to our storage - let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(root); - - FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: bridged_header_hash.into(), - storage_proof, - lane: params.lane, - } + (root, storage_proof) } /// Insert header to the bridge GRANDPA pallet. @@ -154,3 +243,25 @@ where pallet_bridge_grandpa::initialize_for_benchmarks::(bridged_header); (bridged_block_number, bridged_header_hash) } + +/// Insert header to the bridge parachains pallet. +pub(crate) fn insert_header_to_parachains_pallet( + state_root: bp_runtime::HashOf, +) -> (bp_runtime::BlockNumberOf, bp_runtime::HashOf) +where + R: pallet_bridge_parachains::Config, + PI: 'static, + PC: Chain + Parachain, +{ + let bridged_block_number = Zero::zero(); + let bridged_header = bp_runtime::HeaderOf::::new( + bridged_block_number, + Default::default(), + state_root, + Default::default(), + Default::default(), + ); + let bridged_header_hash = bridged_header.hash(); + pallet_bridge_parachains::initialize_for_benchmarks::(bridged_header); + (bridged_block_number, bridged_header_hash) +} diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs index 47ab91928c7..06a1f82e7b0 100644 --- a/bridges/modules/parachains/src/lib.rs +++ b/bridges/modules/parachains/src/lib.rs @@ -33,6 +33,13 @@ use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain, StorageProofErr use frame_support::dispatch::PostDispatchInfo; use sp_std::{marker::PhantomData, vec::Vec}; +#[cfg(feature = "runtime-benchmarks")] +use bp_parachains::ParaStoredHeaderDataBuilder; +#[cfg(feature = "runtime-benchmarks")] +use bp_runtime::HeaderOf; +#[cfg(feature = "runtime-benchmarks")] +use codec::Encode; + // Re-export in crate namespace for `construct_runtime!`. pub use pallet::*; @@ -654,6 +661,25 @@ impl, I: 'static, C: Parachain> HeaderChain } } +/// (Re)initialize pallet with given header for using it in `pallet-bridge-messages` benchmarks. +#[cfg(feature = "runtime-benchmarks")] +pub fn initialize_for_benchmarks, I: 'static, PC: Parachain>( + header: HeaderOf, +) { + let parachain = ParaId(PC::PARACHAIN_ID); + let parachain_head = ParaHead(header.encode()); + let updated_head_data = T::ParaStoredHeaderDataBuilder::try_build(parachain, ¶chain_head) + .expect("failed to build stored parachain head in benchmarks"); + Pallet::::update_parachain_head( + parachain, + None, + 0, + updated_head_data, + parachain_head.hash(), + ) + .expect("failed to insert parachain head in benchmarks"); +} + #[cfg(test)] mod tests { use super::*; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index f6c42988383..a7509f30313 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -76,6 +76,7 @@ bp-bridge-hub-rococo = { path = "../../../../bridges/primitives/chain-bridge-hub bp-bridge-hub-wococo = { path = "../../../../bridges/primitives/chain-bridge-hub-wococo", default-features = false } bp-messages = { path = "../../../../bridges/primitives/messages", default-features = false } bp-parachains = { path = "../../../../bridges/primitives/parachains", default-features = false } +bp-polkadot-core = { path = "../../../../bridges/primitives/polkadot-core", default-features = false } bp-runtime = { path = "../../../../bridges/primitives/runtime", default-features = false } bp-rococo = { path = "../../../../bridges/primitives/chain-rococo", default-features = false } bp-wococo = { path = "../../../../bridges/primitives/chain-wococo", default-features = false } @@ -100,6 +101,7 @@ std = [ "bp-bridge-hub-wococo/std", "bp-messages/std", "bp-parachains/std", + "bp-polkadot-core/std", "bp-runtime/std", "bp-rococo/std", "bp-wococo/std", @@ -159,15 +161,20 @@ std = [ ] runtime-benchmarks = [ + "bridge-runtime-common/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", + "pallet-bridge-grandpa/runtime-benchmarks", + "pallet-bridge-messages/runtime-benchmarks", + "pallet-bridge-parachains/runtime-benchmarks", + "pallet-bridge-relayers/runtime-benchmarks", + "pallet-collator-selection/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", - "pallet-collator-selection/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index cdaaa8bb49e..430cc1ffc66 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . +//! Bridge definitions that are used on Rococo to bridge with Wococo. + use crate::{ BridgeParachainWococoInstance, ParachainInfo, Runtime, WithBridgeHubWococoMessagesInstance, XcmBlobHauler, XcmBlobHaulerAdapter, XcmRouter, @@ -22,7 +24,10 @@ use bp_messages::{LaneId, MessageNonce}; use bp_runtime::ChainId; use bridge_runtime_common::{ messages, - messages::{MessageBridge, ThisChainWithMessages, UnderlyingChainProvider}, + messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, + MessageBridge, ThisChainWithMessages, UnderlyingChainProvider, + }, }; use frame_support::{parameter_types, RuntimeDebug}; use xcm::{ @@ -43,6 +48,13 @@ parameter_types! { pub ActiveOutboundLanesToBridgeHubWococo: &'static [bp_messages::LaneId] = &[DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO]; } +/// Proof of messages, coming from Wococo. +pub type FromWococoBridgeHubMessagesProof = + FromBridgedChainMessagesProof; +/// Messages delivery proof for Rococo Bridge Hub -> Wococo Bridge Hub messages. +pub type ToWococoBridgeHubMessagesDeliveryProof = + FromBridgedChainMessagesDeliveryProof; + /// Dispatches received XCM messages from other bridge pub type OnBridgeHubRococoBlobDispatcher = BridgeBlobDispatcher; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 93b85dde9ed..6578136690c 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . +//! Bridge definitions that are used on Wococo to bridge with Rococo. + use crate::{ BridgeParachainRococoInstance, ParachainInfo, Runtime, WithBridgeHubRococoMessagesInstance, XcmBlobHauler, XcmBlobHaulerAdapter, XcmRouter, @@ -22,7 +24,10 @@ use bp_messages::{LaneId, MessageNonce}; use bp_runtime::ChainId; use bridge_runtime_common::{ messages, - messages::{MessageBridge, ThisChainWithMessages, UnderlyingChainProvider}, + messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, + MessageBridge, ThisChainWithMessages, UnderlyingChainProvider, + }, }; use frame_support::{parameter_types, RuntimeDebug}; use xcm::{ @@ -43,6 +48,13 @@ parameter_types! { pub ActiveOutboundLanesToBridgeHubRococo: &'static [bp_messages::LaneId] = &[DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO]; } +/// Proof of messages, coming from Rococo. +pub type FromRococoBridgeHubMessagesProof = + FromBridgedChainMessagesProof; +/// Messages delivery proof for Rococo Bridge Hub -> Wococo Bridge Hub messages. +pub type ToRococoBridgeHubMessagesDeliveryProof = + FromBridgedChainMessagesDeliveryProof; + /// Dispatches received XCM messages from other bridge pub type OnBridgeHubWococoBlobDispatcher = BridgeBlobDispatcher; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 6fd79614ba0..02f211987af 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -399,7 +399,7 @@ impl pallet_bridge_grandpa::Config for Runtime { type HeadersToKeep = RelayChainHeadersToKeep; type MaxBridgedAuthorities = frame_support::traits::ConstU32<{ bp_wococo::MAX_AUTHORITIES_COUNT }>; - type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; + type WeightInfo = weights::pallet_bridge_grandpa_bridge_wococo_grandpa::WeightInfo; } /// Add granda bridge pallet to track Rococo relay chain @@ -410,7 +410,7 @@ impl pallet_bridge_grandpa::Config for Runtime { type HeadersToKeep = RelayChainHeadersToKeep; type MaxBridgedAuthorities = frame_support::traits::ConstU32<{ bp_rococo::MAX_AUTHORITIES_COUNT }>; - type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; + type WeightInfo = weights::pallet_bridge_grandpa_bridge_rococo_grandpa::WeightInfo; } pub const ROCOCO_BRIDGE_PARA_PALLET_NAME: &str = "Paras"; @@ -430,7 +430,7 @@ parameter_types! { pub type BridgeParachainWococoInstance = pallet_bridge_parachains::Instance1; impl pallet_bridge_parachains::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; + type WeightInfo = weights::pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance::WeightInfo; type BridgesGrandpaPalletInstance = BridgeGrandpaWococoInstance; type ParasPalletName = WococoBridgeParachainPalletName; type ParaStoredHeaderDataBuilder = @@ -443,7 +443,7 @@ impl pallet_bridge_parachains::Config for Runtime pub type BridgeParachainRococoInstance = pallet_bridge_parachains::Instance2; impl pallet_bridge_parachains::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; + type WeightInfo = weights::pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance::WeightInfo; type BridgesGrandpaPalletInstance = BridgeGrandpaRococoInstance; type ParasPalletName = RococoBridgeParachainPalletName; type ParaStoredHeaderDataBuilder = @@ -456,7 +456,7 @@ impl pallet_bridge_parachains::Config for Runtime pub type WithBridgeHubWococoMessagesInstance = pallet_bridge_messages::Instance1; impl pallet_bridge_messages::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + type WeightInfo = weights::pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance::WeightInfo; type BridgedChainId = bridge_hub_rococo_config::BridgeHubWococoChainId; type ActiveOutboundLanes = bridge_hub_rococo_config::ActiveOutboundLanesToBridgeHubWococo; type MaxUnrewardedRelayerEntriesAtInboundLane = @@ -490,7 +490,7 @@ impl pallet_bridge_messages::Config for Run pub type WithBridgeHubRococoMessagesInstance = pallet_bridge_messages::Instance2; impl pallet_bridge_messages::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + type WeightInfo = weights::pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance::WeightInfo; type BridgedChainId = bridge_hub_wococo_config::BridgeHubRococoChainId; type ActiveOutboundLanes = bridge_hub_wococo_config::ActiveOutboundLanesToBridgeHubRococo; type MaxUnrewardedRelayerEntriesAtInboundLane = @@ -557,12 +557,16 @@ construct_runtime!( // TODO:check-parameter - change back to 41 a align bridge pallets Multisig: pallet_multisig::{Pallet, Call, Storage, Event} = 36, - // Wococo bridge modules + // Rococo and Wococo Bridge Hubs are sharing the runtime, so this runtime has two sets of + // bridge pallets. Both are deployed at both runtimes, but only one set is actually used + // at particular runtime. + + // With-Wococo bridge modules that are active (used) at Rococo Bridge Hub runtime. BridgeWococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Config} = 41, BridgeWococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage, Event} = 42, BridgeWococoMessages: pallet_bridge_messages::::{Pallet, Call, Storage, Event, Config} = 46, - // Rococo bridge modules + // With-Rococo bridge modules that are active (used) at Wococo Bridge Hub runtime. BridgeRococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Config} = 43, BridgeRococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage, Event} = 44, BridgeRococoMessages: pallet_bridge_messages::::{Pallet, Call, Storage, Event, Config} = 45, @@ -600,6 +604,14 @@ mod benches { // NOTE: Make sure you point to the individual modules below. [pallet_xcm_benchmarks::fungible, XcmBalances] [pallet_xcm_benchmarks::generic, XcmGeneric] + // Bridge pallets at Rococo + [pallet_bridge_grandpa, BridgeWococoGrandpa] + [pallet_bridge_parachains, BridgeParachainsBench::] + [pallet_bridge_messages, BridgeMessagesBench::] + // Bridge pallets at Wococo + [pallet_bridge_grandpa, BridgeRococoGrandpa] + [pallet_bridge_parachains, BridgeParachainsBench::] + [pallet_bridge_messages, BridgeMessagesBench::] ); } @@ -827,6 +839,9 @@ impl_runtime_apis! { type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; type XcmGeneric = pallet_xcm_benchmarks::generic::Pallet::; + use pallet_bridge_parachains::benchmarking::Pallet as BridgeParachainsBench; + use pallet_bridge_messages::benchmarking::Pallet as BridgeMessagesBench; + let mut list = Vec::::new(); list_benchmarks!(list, extra); @@ -926,6 +941,122 @@ impl_runtime_apis! { type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; type XcmGeneric = pallet_xcm_benchmarks::generic::Pallet::; + use bridge_runtime_common::messages_benchmarking::{prepare_message_delivery_proof_from_parachain, prepare_message_proof_from_parachain}; + use pallet_bridge_messages::benchmarking::{ + Config as BridgeMessagesConfig, + Pallet as BridgeMessagesBench, + MessageDeliveryProofParams, + MessageProofParams, + }; + + impl BridgeMessagesConfig for Runtime { + fn is_relayer_rewarded(_: &Self::AccountId) -> bool { + // TODO: implement me properly + true + } + + fn prepare_message_proof( + params: MessageProofParams, + ) -> (bridge_hub_rococo_config::FromWococoBridgeHubMessagesProof, Weight) { + prepare_message_proof_from_parachain::< + Runtime, + BridgeGrandpaWococoInstance, + bridge_hub_rococo_config::WithBridgeHubWococoMessageBridge, + >(params) + } + + fn prepare_message_delivery_proof( + params: MessageDeliveryProofParams, + ) -> bridge_hub_rococo_config::ToWococoBridgeHubMessagesDeliveryProof { + prepare_message_delivery_proof_from_parachain::< + Runtime, + BridgeGrandpaWococoInstance, + bridge_hub_rococo_config::WithBridgeHubWococoMessageBridge, + >(params) + } + } + + impl BridgeMessagesConfig for Runtime { + fn is_relayer_rewarded(_: &Self::AccountId) -> bool { + // TODO: implement me properly + true + } + + fn prepare_message_proof( + params: MessageProofParams, + ) -> (bridge_hub_wococo_config::FromRococoBridgeHubMessagesProof, Weight) { + prepare_message_proof_from_parachain::< + Runtime, + BridgeGrandpaRococoInstance, + bridge_hub_wococo_config::WithBridgeHubRococoMessageBridge, + >(params) + } + + fn prepare_message_delivery_proof( + params: MessageDeliveryProofParams, + ) -> bridge_hub_wococo_config::ToRococoBridgeHubMessagesDeliveryProof { + prepare_message_delivery_proof_from_parachain::< + Runtime, + BridgeGrandpaRococoInstance, + bridge_hub_wococo_config::WithBridgeHubRococoMessageBridge, + >(params) + } + } + + use bridge_runtime_common::parachains_benchmarking::prepare_parachain_heads_proof; + use pallet_bridge_parachains::benchmarking::{ + Config as BridgeParachainsConfig, + Pallet as BridgeParachainsBench, + }; + + impl BridgeParachainsConfig for Runtime { + fn parachains() -> Vec { + use bp_runtime::Parachain; + vec![bp_polkadot_core::parachains::ParaId(bp_bridge_hub_wococo::BridgeHubWococo::PARACHAIN_ID)] + } + + fn prepare_parachain_heads_proof( + parachains: &[bp_polkadot_core::parachains::ParaId], + parachain_head_size: u32, + proof_size: bp_runtime::StorageProofSize, + ) -> ( + pallet_bridge_parachains::RelayBlockNumber, + pallet_bridge_parachains::RelayBlockHash, + bp_polkadot_core::parachains::ParaHeadsProof, + Vec<(bp_polkadot_core::parachains::ParaId, bp_polkadot_core::parachains::ParaHash)>, + ) { + prepare_parachain_heads_proof::( + parachains, + parachain_head_size, + proof_size, + ) + } + } + + impl BridgeParachainsConfig for Runtime { + fn parachains() -> Vec { + use bp_runtime::Parachain; + vec![bp_polkadot_core::parachains::ParaId(bp_bridge_hub_rococo::BridgeHubRococo::PARACHAIN_ID)] + } + + fn prepare_parachain_heads_proof( + parachains: &[bp_polkadot_core::parachains::ParaId], + parachain_head_size: u32, + proof_size: bp_runtime::StorageProofSize, + ) -> ( + pallet_bridge_parachains::RelayBlockNumber, + pallet_bridge_parachains::RelayBlockHash, + bp_polkadot_core::parachains::ParaHeadsProof, + Vec<(bp_polkadot_core::parachains::ParaId, bp_polkadot_core::parachains::ParaHash)>, + ) { + prepare_parachain_heads_proof::( + parachains, + parachain_head_size, + proof_size, + ) + } + } + let whitelist: Vec = vec![ // Block Number hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(), diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs index d5722374def..1eec4e5c18a 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs @@ -22,6 +22,12 @@ pub mod cumulus_pallet_xcmp_queue; pub mod extrinsic_weights; pub mod frame_system; pub mod pallet_balances; +pub mod pallet_bridge_grandpa_bridge_rococo_grandpa; +pub mod pallet_bridge_grandpa_bridge_wococo_grandpa; +pub mod pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance; +pub mod pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance; +pub mod pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance; +pub mod pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance; pub mod pallet_collator_selection; pub mod pallet_multisig; pub mod pallet_session; @@ -36,3 +42,27 @@ pub use block_weights::constants::BlockExecutionWeight; pub use extrinsic_weights::constants::ExtrinsicBaseWeight; pub use paritydb_weights::constants::ParityDbWeight; pub use rocksdb_weights::constants::RocksDbWeight; + +impl pallet_bridge_messages::WeightInfoExt for pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance::WeightInfo { + fn expected_extra_storage_proof_size() -> u32 { + bp_bridge_hub_rococo::EXTRA_STORAGE_PROOF_SIZE + } +} + +impl pallet_bridge_messages::WeightInfoExt for pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance::WeightInfo { + fn expected_extra_storage_proof_size() -> u32 { + bp_bridge_hub_wococo::EXTRA_STORAGE_PROOF_SIZE + } +} + +impl pallet_bridge_parachains::WeightInfoExt for pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance::WeightInfo { + fn expected_extra_storage_proof_size() -> u32 { + bp_bridge_hub_wococo::EXTRA_STORAGE_PROOF_SIZE + } +} + +impl pallet_bridge_parachains::WeightInfoExt for pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance::WeightInfo { + fn expected_extra_storage_proof_size() -> u32 { + bp_bridge_hub_rococo::EXTRA_STORAGE_PROOF_SIZE + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_rococo_grandpa.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_rococo_grandpa.rs new file mode 100644 index 00000000000..9dfa44f562c --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_rococo_grandpa.rs @@ -0,0 +1,71 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `pallet_bridge_grandpa` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-01-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// /home/benchbot/cargo_target_dir/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_bridge_grandpa +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_bridge_grandpa`. +pub struct WeightInfo(PhantomData); +impl pallet_bridge_grandpa::WeightInfo for WeightInfo { + // Storage: BridgeRococoGrandpa PalletOperatingMode (r:1 w:0) + // Storage: BridgeRococoGrandpa RequestCount (r:1 w:1) + // Storage: BridgeRococoGrandpa BestFinalized (r:1 w:1) + // Storage: BridgeRococoGrandpa CurrentAuthoritySet (r:1 w:0) + // Storage: BridgeRococoGrandpa ImportedHashesPointer (r:1 w:1) + // Storage: BridgeRococoGrandpa ImportedHashes (r:1 w:1) + // Storage: BridgeRococoGrandpa ImportedHeaders (r:0 w:2) + /// The range of component `p` is `[51, 102]`. + /// The range of component `v` is `[50, 100]`. + /// The range of component `p` is `[51, 102]`. + /// The range of component `v` is `[50, 100]`. + fn submit_finality_proof(p: u32, v: u32, ) -> Weight { + // Minimum execution time: 2_561_902 nanoseconds. + Weight::from_ref_time(86_419_230) + // Standard Error: 32_880 + .saturating_add(Weight::from_ref_time(46_938_159).saturating_mul(p.into())) + // Standard Error: 33_661 + .saturating_add(Weight::from_ref_time(1_134_973).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(6)) + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_wococo_grandpa.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_wococo_grandpa.rs new file mode 100644 index 00000000000..a22269e2a7c --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_wococo_grandpa.rs @@ -0,0 +1,71 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `pallet_bridge_grandpa` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-01-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// /home/benchbot/cargo_target_dir/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_bridge_grandpa +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_bridge_grandpa`. +pub struct WeightInfo(PhantomData); +impl pallet_bridge_grandpa::WeightInfo for WeightInfo { + // Storage: BridgeWococoGrandpa PalletOperatingMode (r:1 w:0) + // Storage: BridgeWococoGrandpa RequestCount (r:1 w:1) + // Storage: BridgeWococoGrandpa BestFinalized (r:1 w:1) + // Storage: BridgeWococoGrandpa CurrentAuthoritySet (r:1 w:0) + // Storage: BridgeWococoGrandpa ImportedHashesPointer (r:1 w:1) + // Storage: BridgeWococoGrandpa ImportedHashes (r:1 w:1) + // Storage: BridgeWococoGrandpa ImportedHeaders (r:0 w:2) + /// The range of component `p` is `[51, 102]`. + /// The range of component `v` is `[50, 100]`. + /// The range of component `p` is `[51, 102]`. + /// The range of component `v` is `[50, 100]`. + fn submit_finality_proof(p: u32, v: u32, ) -> Weight { + // Minimum execution time: 2_565_169 nanoseconds. + Weight::from_ref_time(67_834_502) + // Standard Error: 35_710 + .saturating_add(Weight::from_ref_time(47_043_118).saturating_mul(p.into())) + // Standard Error: 36_557 + .saturating_add(Weight::from_ref_time(1_301_551).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(6)) + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs new file mode 100644 index 00000000000..65a1858a231 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs @@ -0,0 +1,125 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `pallet_bridge_messages` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-01-25, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `runner-b3zmxxc-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_bridge_messages +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_bridge_messages`. +pub struct WeightInfo(PhantomData); +impl pallet_bridge_messages::WeightInfo for WeightInfo { + // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeRococoMessages InboundLanes (r:1 w:1) + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn receive_single_message_proof() -> Weight { + // Minimum execution time: 48_515 nanoseconds. + Weight::from_ref_time(50_641_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeRococoMessages InboundLanes (r:1 w:1) + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn receive_two_messages_proof() -> Weight { + // Minimum execution time: 62_891 nanoseconds. + Weight::from_ref_time(66_039_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeRococoMessages InboundLanes (r:1 w:1) + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn receive_single_message_proof_with_outbound_lane_state() -> Weight { + // Minimum execution time: 53_010 nanoseconds. + Weight::from_ref_time(53_855_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeRococoMessages InboundLanes (r:1 w:1) + fn receive_single_message_proof_1_kb() -> Weight { + // Minimum execution time: 50_491 nanoseconds. + Weight::from_ref_time(52_570_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeRococoMessages InboundLanes (r:1 w:1) + fn receive_single_message_proof_16_kb() -> Weight { + // Minimum execution time: 116_072 nanoseconds. + Weight::from_ref_time(119_185_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeRococoMessages OutboundLanes (r:1 w:1) + fn receive_delivery_proof_for_single_message() -> Weight { + // Minimum execution time: 31_075 nanoseconds. + Weight::from_ref_time(32_055_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeRococoMessages OutboundLanes (r:1 w:1) + fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { + // Minimum execution time: 31_524 nanoseconds. + Weight::from_ref_time(32_149_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeRococoMessages OutboundLanes (r:1 w:1) + fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { + // Minimum execution time: 31_723 nanoseconds. + Weight::from_ref_time(32_355_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs new file mode 100644 index 00000000000..eaff561d629 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs @@ -0,0 +1,125 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `pallet_bridge_messages` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-01-25, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `runner-b3zmxxc-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_bridge_messages +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_bridge_messages`. +pub struct WeightInfo(PhantomData); +impl pallet_bridge_messages::WeightInfo for WeightInfo { + // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeWococoMessages InboundLanes (r:1 w:1) + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn receive_single_message_proof() -> Weight { + // Minimum execution time: 49_348 nanoseconds. + Weight::from_ref_time(52_687_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeWococoMessages InboundLanes (r:1 w:1) + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn receive_two_messages_proof() -> Weight { + // Minimum execution time: 63_755 nanoseconds. + Weight::from_ref_time(67_615_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeWococoMessages InboundLanes (r:1 w:1) + // Storage: ParachainInfo ParachainId (r:1 w:0) + fn receive_single_message_proof_with_outbound_lane_state() -> Weight { + // Minimum execution time: 54_597 nanoseconds. + Weight::from_ref_time(56_472_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeWococoMessages InboundLanes (r:1 w:1) + fn receive_single_message_proof_1_kb() -> Weight { + // Minimum execution time: 51_363 nanoseconds. + Weight::from_ref_time(54_025_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeWococoMessages InboundLanes (r:1 w:1) + fn receive_single_message_proof_16_kb() -> Weight { + // Minimum execution time: 119_727 nanoseconds. + Weight::from_ref_time(123_138_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeWococoMessages OutboundLanes (r:1 w:1) + fn receive_delivery_proof_for_single_message() -> Weight { + // Minimum execution time: 32_525 nanoseconds. + Weight::from_ref_time(33_410_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeWococoMessages OutboundLanes (r:1 w:1) + fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { + // Minimum execution time: 32_310 nanoseconds. + Weight::from_ref_time(33_208_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + // Storage: BridgeWococoMessages OutboundLanes (r:1 w:1) + fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { + // Minimum execution time: 32_594 nanoseconds. + Weight::from_ref_time(33_449_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance.rs new file mode 100644 index 00000000000..056617988d5 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance.rs @@ -0,0 +1,85 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `pallet_bridge_parachains` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-01-23, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// /home/benchbot/cargo_target_dir/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_bridge_parachains +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_bridge_parachains`. +pub struct WeightInfo(PhantomData); +impl pallet_bridge_parachains::WeightInfo for WeightInfo { + // Storage: BridgeRococoParachain PalletOperatingMode (r:1 w:0) + // Storage: BridgeRococoGrandpa ImportedHeaders (r:1 w:0) + // Storage: BridgeRococoParachain ParasInfo (r:1 w:1) + // Storage: BridgeRococoParachain ImportedParaHashes (r:1 w:1) + // Storage: BridgeRococoParachain ImportedParaHeads (r:0 w:1) + /// The range of component `p` is `[1, 2]`. + /// The range of component `p` is `[1, 2]`. + fn submit_parachain_heads_with_n_parachains(_p: u32, ) -> Weight { + // Minimum execution time: 34_955 nanoseconds. + Weight::from_ref_time(36_400_062) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: BridgeRococoParachain PalletOperatingMode (r:1 w:0) + // Storage: BridgeRococoGrandpa ImportedHeaders (r:1 w:0) + // Storage: BridgeRococoParachain ParasInfo (r:1 w:1) + // Storage: BridgeRococoParachain ImportedParaHashes (r:1 w:1) + // Storage: BridgeRococoParachain ImportedParaHeads (r:0 w:1) + fn submit_parachain_heads_with_1kb_proof() -> Weight { + // Minimum execution time: 44_024 nanoseconds. + Weight::from_ref_time(44_604_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: BridgeRococoParachain PalletOperatingMode (r:1 w:0) + // Storage: BridgeRococoGrandpa ImportedHeaders (r:1 w:0) + // Storage: BridgeRococoParachain ParasInfo (r:1 w:1) + // Storage: BridgeRococoParachain ImportedParaHashes (r:1 w:1) + // Storage: BridgeRococoParachain ImportedParaHeads (r:0 w:1) + fn submit_parachain_heads_with_16kb_proof() -> Weight { + // Minimum execution time: 96_346 nanoseconds. + Weight::from_ref_time(98_207_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance.rs new file mode 100644 index 00000000000..6a4b86a6def --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance.rs @@ -0,0 +1,87 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `pallet_bridge_parachains` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-01-23, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// /home/benchbot/cargo_target_dir/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_bridge_parachains +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_bridge_parachains`. +pub struct WeightInfo(PhantomData); +impl pallet_bridge_parachains::WeightInfo for WeightInfo { + // Storage: BridgeWococoParachain PalletOperatingMode (r:1 w:0) + // Storage: BridgeWococoGrandpa ImportedHeaders (r:1 w:0) + // Storage: BridgeWococoParachain ParasInfo (r:1 w:1) + // Storage: BridgeWococoParachain ImportedParaHashes (r:1 w:1) + // Storage: BridgeWococoParachain ImportedParaHeads (r:0 w:1) + /// The range of component `p` is `[1, 2]`. + /// The range of component `p` is `[1, 2]`. + fn submit_parachain_heads_with_n_parachains(p: u32, ) -> Weight { + // Minimum execution time: 36_411 nanoseconds. + Weight::from_ref_time(37_604_452) + // Standard Error: 57_620 + .saturating_add(Weight::from_ref_time(248_648).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: BridgeWococoParachain PalletOperatingMode (r:1 w:0) + // Storage: BridgeWococoGrandpa ImportedHeaders (r:1 w:0) + // Storage: BridgeWococoParachain ParasInfo (r:1 w:1) + // Storage: BridgeWococoParachain ImportedParaHashes (r:1 w:1) + // Storage: BridgeWococoParachain ImportedParaHeads (r:0 w:1) + fn submit_parachain_heads_with_1kb_proof() -> Weight { + // Minimum execution time: 45_107 nanoseconds. + Weight::from_ref_time(45_916_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: BridgeWococoParachain PalletOperatingMode (r:1 w:0) + // Storage: BridgeWococoGrandpa ImportedHeaders (r:1 w:0) + // Storage: BridgeWococoParachain ParasInfo (r:1 w:1) + // Storage: BridgeWococoParachain ImportedParaHashes (r:1 w:1) + // Storage: BridgeWococoParachain ImportedParaHeads (r:0 w:1) + fn submit_parachain_heads_with_16kb_proof() -> Weight { + // Minimum execution time: 97_738 nanoseconds. + Weight::from_ref_time(100_381_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } +} diff --git a/scripts/benchmarks-ci.sh b/scripts/benchmarks-ci.sh index d4ad99d7bbe..b97373b390c 100755 --- a/scripts/benchmarks-ci.sh +++ b/scripts/benchmarks-ci.sh @@ -51,6 +51,9 @@ elif [[ $runtimeName == "bridge-hub-rococo" ]] || [[ $runtimeName == "bridge-hub cumulus_pallet_xcmp_queue pallet_xcm_benchmarks::generic pallet_xcm_benchmarks::fungible + pallet_bridge_grandpa + pallet_bridge_parachains + pallet_bridge_messages ) else echo "$runtimeName pallet list not found in benchmarks-ci.sh" From 7e4b1caf5d1737831d8b878642dda9ea86660d07 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 26 Jan 2023 15:07:51 +0100 Subject: [PATCH 190/263] Ensure governance can call `initialize` with `xcm:Transact` bridge grandpa pallets --- Cargo.lock | 4 + .../bridge-hub-rococo/src/xcm_config.rs | 17 +++- .../bridge-hub-rococo/tests/tests.rs | 91 ++++++++++++++++++- .../bridge-hubs/test-utils/Cargo.toml | 8 ++ .../bridge-hubs/test-utils/src/lib.rs | 35 +++++++ 5 files changed, 151 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8430cd5d69e..29a6a8ee649 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1008,7 +1008,10 @@ dependencies = [ name = "bridge-hub-test-utils" version = "0.1.0" dependencies = [ + "bp-header-chain", "bp-messages", + "bp-polkadot-core", + "bp-runtime", "cumulus-pallet-parachain-system", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", @@ -1020,6 +1023,7 @@ dependencies = [ "parachain-info", "parachains-common", "polkadot-parachain", + "sp-runtime", "xcm", "xcm-builder", "xcm-executor", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 6bac53894a6..1440753c425 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -15,8 +15,9 @@ // along with Cumulus. If not, see . use super::{ - AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, - Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, Balances, BridgeGrandpaRococoInstance, + BridgeGrandpaWococoInstance, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, + RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, }; use crate::{ bridge_hub_rococo_config::ToBridgeHubWococoHaulBlobExporter, @@ -152,6 +153,18 @@ impl Contains for SafeCallFilter { } match call { + RuntimeCall::BridgeRococoGrandpa(pallet_bridge_grandpa::Call::< + Runtime, + BridgeGrandpaRococoInstance, + >::initialize { + .. + }) | + RuntimeCall::BridgeWococoGrandpa(pallet_bridge_grandpa::Call::< + Runtime, + BridgeGrandpaWococoInstance, + >::initialize { + .. + }) | RuntimeCall::System( frame_system::Call::set_heap_pages { .. } | frame_system::Call::set_code { .. } | diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index 0fda693cc5d..8885641a57e 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -21,9 +21,12 @@ pub use bridge_hub_rococo_runtime::{ xcm_config::{XcmConfig, XcmRouter}, Runtime, *, }; +use codec::Encode; use xcm::latest::prelude::*; use bridge_hub_test_utils::*; +use frame_support::weights::Weight; +use xcm_executor::XcmExecutor; fn execute_on_runtime( with_para_id: u32, @@ -42,7 +45,7 @@ fn execute_on_runtime( } #[test] -fn test_bridge_hub_wococo_dispatch_blob_and_xcm_routing_works() { +fn dispatch_blob_and_xcm_routing_works_on_bridge_hub_wococo() { let universal_source_as_senders = vec![X1(GlobalConsensus(Rococo)), X2(GlobalConsensus(Rococo), Parachain(1000))]; let runtime_para_id = bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID; @@ -111,7 +114,7 @@ fn test_bridge_hub_wococo_dispatch_blob_and_xcm_routing_works() { } #[test] -fn test_bridge_hub_rococo_dispatch_blob_and_xcm_routing_works() { +fn dispatch_blob_and_xcm_routing_works_on_bridge_hub_rococo() { let universal_source_as_senders = vec![X1(GlobalConsensus(Wococo)), X2(GlobalConsensus(Wococo), Parachain(1000))]; let runtime_para_id = bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID; @@ -178,3 +181,87 @@ fn test_bridge_hub_rococo_dispatch_blob_and_xcm_routing_works() { assert_eq!(result.dispatch_level_result, XcmBlobMessageDispatchResult::Dispatched); } } + +#[test] +fn can_govornance_call_xcm_transact_with_initialize_on_bridge_hub_rococo() { + // prepare xcm as govornance will do + let initialize_call: RuntimeCall = + RuntimeCall::BridgeRococoGrandpa(pallet_bridge_grandpa::Call::< + Runtime, + BridgeGrandpaRococoInstance, + >::initialize { + init_data: mock_initialiation_data(), + }); + let xcm = Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + Transact { + origin_kind: OriginKind::Superuser, + require_weight_at_most: Weight::from_ref_time(1000000000), + call: initialize_call.encode().into(), + }, + ]); + // origin as relay chain + let origin = MultiLocation { parents: 1, interior: Here }; + + execute_on_runtime(bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, None, || { + // check mode before + assert_eq!( + pallet_bridge_grandpa::PalletOperatingMode::::try_get(), + Err(()) + ); + + // initialize bridge through governance-like + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + let weight_limit = Weight::from_ref_time(41666666666); + let outcome = XcmExecutor::::execute_xcm(origin, xcm, hash, weight_limit); + + // check mode after + assert_eq!(outcome.ensure_complete(), Ok(())); + assert_eq!( + pallet_bridge_grandpa::PalletOperatingMode::::try_get(), + Ok(bp_runtime::BasicOperatingMode::Normal) + ); + }) +} + +#[test] +fn can_govornance_call_xcm_transact_with_initialize_bridge_on_bridge_hub_wococo() { + // prepare xcm as govornance will do + let initialize_call: RuntimeCall = + RuntimeCall::BridgeWococoGrandpa(pallet_bridge_grandpa::Call::< + Runtime, + BridgeGrandpaWococoInstance, + >::initialize { + init_data: mock_initialiation_data(), + }); + let xcm = Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + Transact { + origin_kind: OriginKind::Superuser, + require_weight_at_most: Weight::from_ref_time(1000000000), + call: initialize_call.encode().into(), + }, + ]); + // origin as relay chain + let origin = MultiLocation { parents: 1, interior: Here }; + + execute_on_runtime(bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, None, || { + // check mode before + assert_eq!( + pallet_bridge_grandpa::PalletOperatingMode::::try_get(), + Err(()) + ); + + // initialize bridge through governance-like + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + let weight_limit = Weight::from_ref_time(41666666666); + let outcome = XcmExecutor::::execute_xcm(origin, xcm, hash, weight_limit); + + // check mode after + assert_eq!(outcome.ensure_complete(), Ok(())); + assert_eq!( + pallet_bridge_grandpa::PalletOperatingMode::::try_get(), + Ok(bp_runtime::BasicOperatingMode::Normal) + ); + }) +} diff --git a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml index 2418111ebb4..fb2126d6192 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -10,6 +10,7 @@ description = "Utils for BridgeHub testing" # Substrate frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } # Cumulus parachains-common = { path = "../../../common", default-features = false } @@ -28,7 +29,10 @@ xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } # Bridges +bp-header-chain = { path = "../../../../bridges/primitives/header-chain", default-features = false } bp-messages = { path = "../../../../bridges/primitives/messages", default-features = false } +bp-polkadot-core = { path = "../../../../bridges/primitives/polkadot-core", default-features = false } +bp-runtime = { path = "../../../../bridges/primitives/runtime", default-features = false } [features] default = [ "std" ] @@ -36,6 +40,9 @@ std = [ "frame-support/std", "frame-system/std", "bp-messages/std", + "bp-polkadot-core/std", + "bp-header-chain/std", + "bp-runtime/std", "parachains-common/std", "parachain-info/std", "cumulus-primitives-core/std", @@ -44,6 +51,7 @@ std = [ "cumulus-test-relay-sproof-builder/std", "polkadot-parachain/std", "pallet-xcm/std", + "sp-runtime/std", "xcm/std", "xcm-builder/std", "xcm-executor/std", diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs index ff9acd36f68..a95b54ea62e 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs @@ -101,6 +101,8 @@ pub fn new_test_ext sp_io::TestExternalities { + frame_support::sp_tracing::try_init_simple(); + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); >::assimilate_storage( &pallet_xcm::GenesisConfig { safe_xcm_version: Some(xcm_version) }, @@ -171,3 +173,36 @@ pub fn mock_open_hrmp_channel< .dispatch_bypass_filter(RawOrigin::None.into()) .expect("dispatch succeeded"); } + +pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; +pub type RelayBlockHasher = bp_polkadot_core::Hasher; +pub type RelayBlockHeader = sp_runtime::generic::Header; + +/// Helper that creates InitializationData mock data, that can be used to initialize bridge GRANDPA pallet +pub fn mock_initialiation_data() -> bp_header_chain::InitializationData { + use sp_runtime::traits::Header; + use std::str::FromStr; + + let header = RelayBlockHeader::new( + 75, + bp_polkadot_core::Hash::from_str( + "0xd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d", + ) + .expect("invalid value"), + bp_polkadot_core::Hash::from_str( + "0x92b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141b", + ) + .expect("invalid value"), + bp_polkadot_core::Hash::from_str( + "0xae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d", + ) + .expect("invalid value"), + Default::default(), + ); + bp_header_chain::InitializationData { + header: Box::new(header), + authority_list: Default::default(), + set_id: 6, + operating_mode: bp_runtime::BasicOperatingMode::Normal, + } +} From ad9d24e871b1c2902dcd291a362b441e72e71d01 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 26 Jan 2023 15:14:24 +0100 Subject: [PATCH 191/263] Just in case changed spec_version --- parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 02f211987af..43a46cee88a 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -142,7 +142,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("bridge-hub-rococo"), impl_name: create_runtime_str!("bridge-hub-rococo"), authoring_version: 1, - spec_version: 9370, + spec_version: 9371, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From f0657dab3439f2b315493b8a044e957618645cc0 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Sat, 21 Jan 2023 21:13:35 +0100 Subject: [PATCH 192/263] Some scripts for enabling asset transfer on local statemine->westmint --- scripts/bridges_rococo_wococo.sh | 337 ++++- scripts/generate_hex_encoded_call/index.js | 112 ++ .../package-lock.json | 1213 +++++++++++++++++ .../generate_hex_encoded_call/package.json | 11 + .../bridge_hub_rococo_local_network.toml | 3 + .../bridge_hub_wococo_local_network.toml | 3 + 6 files changed, 1669 insertions(+), 10 deletions(-) create mode 100644 scripts/generate_hex_encoded_call/index.js create mode 100644 scripts/generate_hex_encoded_call/package-lock.json create mode 100644 scripts/generate_hex_encoded_call/package.json diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh index 9fa1e29a653..5989d98feb8 100755 --- a/scripts/bridges_rococo_wococo.sh +++ b/scripts/bridges_rococo_wococo.sh @@ -41,6 +41,35 @@ function ensure_polkadot_js_api() { echo '' exit 1 fi + generate_hex_encoded_call_data "check" "--" + local retVal=$? + if [ $retVal -ne 0 ]; then + echo "" + echo "" + echo "-------------------" + echo "Installing (nodejs) sub module: ./scripts/generate_hex_encoded_call" + pushd ./scripts/generate_hex_encoded_call + npm install + popd + exit 1 + fi +} + +function generate_hex_encoded_call_data() { + local type=$1 + local endpoint=$2 + local output=$3 + shift + shift + shift + echo "Input params: $@" + + node ./scripts/generate_hex_encoded_call "$type" "$endpoint" "$output" "$@" + + if [ $type != "check" ]; then + local hex_encoded_data=$(cat $output) + echo "Generated hex-encoded bytes to file '$output': $hex_encoded_data" + fi } STATEMINE_ACCOUNT_SEED_FOR_LOCAL="//Alice" @@ -52,12 +81,21 @@ function send_xcm_transact_remark_from_statemine() { local bridge_hub_para_id=$3 local target_network=$4 local target_network_para_id=$5 + local target_network_para_endpoint=$6 echo " calling send_xcm_transact_remark_from_statemine:" echo " url: ${url}" echo " seed: ${seed}" echo " bridge_hub_para_id: ${bridge_hub_para_id}" + echo " target_network: ${target_network}" + echo " target_network_para_id: ${target_network_para_id}" + echo " target_network_para_endpoint: ${target_network_para_endpoint}" echo " params:" + # generate data for Transact on the other side (Westmint) + local tmp_file=$(mktemp) + generate_hex_encoded_call_data "remark-with-event" "${target_network_para_endpoint}" "${tmp_file}" + local hex_encoded_data=$(cat $tmp_file) + local dest=$(jq --null-input \ --arg bridge_hub_para_id "$bridge_hub_para_id" \ '{ "V3": { "parents": 1, "interior": { "X1": { "Parachain": $bridge_hub_para_id } } } }') @@ -65,7 +103,9 @@ function send_xcm_transact_remark_from_statemine() { local message=$(jq --null-input \ --arg target_network "$target_network" \ --arg target_network_para_id "$target_network_para_id" \ - '{ + --argjson hex_encoded_data $hex_encoded_data \ + ' + { "V3": [ { "ExportMessage": { @@ -84,7 +124,7 @@ function send_xcm_transact_remark_from_statemine() { "proof_size": 0, }, "call": { - "encoded": [0, 7, 20, 72, 101, 108, 108, 111 ] + "encoded": $hex_encoded_data } } } @@ -92,7 +132,8 @@ function send_xcm_transact_remark_from_statemine() { } } ] - }') + } + ') echo "" echo " dest:" @@ -130,7 +171,8 @@ function send_xcm_trap_from_statemine() { local message=$(jq --null-input \ --arg target_network "$target_network" \ --arg target_network_para_id "$target_network_para_id" \ - '{ + ' + { "V3": [ { "ExportMessage": { @@ -148,7 +190,8 @@ function send_xcm_trap_from_statemine() { } } ] - }') + } + ') echo "" echo " dest:" @@ -167,6 +210,253 @@ function send_xcm_trap_from_statemine() { "${message}" } +function allow_assets_transfer_from_statemine() { + local relay_url=$1 + local relay_chain_seed=$2 + local statemine_para_id=$3 + local statemine_para_endpoint=$4 + local bridge_hub_para_id=$5 + local statemint_para_network=$6 + local statemint_para_para_id=$7 + echo " calling allow_assets_transfer_from_statemine:" + echo " relay_url: ${relay_url}" + echo " relay_chain_seed: ${relay_chain_seed}" + echo " statemine_para_id: ${statemine_para_id}" + echo " statemine_para_endpoint: ${statemine_para_endpoint}" + echo " bridge_hub_para_id: ${bridge_hub_para_id}" + echo " statemint_para_network: ${statemint_para_network}" + echo " statemint_para_para_id: ${statemint_para_para_id}" + echo " params:" + + # generate data for Transact on Statemine + local bridge_config=$(jq --null-input \ + --arg statemint_para_network "$statemint_para_network" \ + --arg bridge_hub_para_id "$bridge_hub_para_id" \ + --arg statemint_para_network "$statemint_para_network" \ + --arg statemint_para_para_id "$statemint_para_para_id" \ + ' + { + "bridgeLocation": { + "parents": 1, + "interior": { + "X1": { "Parachain": $bridge_hub_para_id } + } + }, + "allowedTargetLocation": { + "parents": 2, + "interior": { + "X2": [ + { + "GlobalConsensus": $statemint_para_network, + }, + { + "Parachain": $statemint_para_para_id + } + ] + } + } + } + ' + ) + local tmp_output_file=$(mktemp) + generate_hex_encoded_call_data "add-bridge-config" "${statemine_para_endpoint}" "${tmp_output_file}" $statemint_para_network "$bridge_config" + local hex_encoded_data=$(cat $tmp_output_file) + + local dest=$(jq --null-input \ + --arg statemine_para_id "$statemine_para_id" \ + '{ "V3": { "parents": 0, "interior": { "X1": { "Parachain": $statemine_para_id } } } }') + + local message=$(jq --null-input \ + --argjson hex_encoded_data $hex_encoded_data \ + ' + { + "V3": [ + { + "UnpaidExecution": { + "weight_limit": "Unlimited" + } + }, + { + "Transact": { + "origin_kind": "Superuser", + "require_weight_at_most": { + "ref_time": 1000000000, + "proof_size": 0, + }, + "call": { + "encoded": $hex_encoded_data + } + } + } + ] + } + ') + + echo "" + echo " dest:" + echo "${dest}" + echo "" + echo " message:" + echo "${message}" + echo "" + echo "--------------------------------------------------" + + polkadot-js-api \ + --ws "${relay_url?}" \ + --seed "${relay_chain_seed?}" \ + --sudo \ + tx.xcmPallet.send \ + "${dest}" \ + "${message}" +} + +function remove_assets_transfer_from_statemine() { + local relay_url=$1 + local relay_chain_seed=$2 + local statemine_para_id=$3 + local statemine_para_endpoint=$4 + local statemint_para_network=$5 + echo " calling remove_assets_transfer_from_statemine:" + echo " relay_url: ${relay_url}" + echo " relay_chain_seed: ${relay_chain_seed}" + echo " statemine_para_id: ${statemine_para_id}" + echo " statemine_para_endpoint: ${statemine_para_endpoint}" + echo " statemint_para_network: ${statemint_para_network}" + echo " params:" + + local tmp_output_file=$(mktemp) + generate_hex_encoded_call_data "remove-bridge-config" "${statemine_para_endpoint}" "${tmp_output_file}" $statemint_para_network + local hex_encoded_data=$(cat $tmp_output_file) + + local dest=$(jq --null-input \ + --arg statemine_para_id "$statemine_para_id" \ + '{ "V3": { "parents": 0, "interior": { "X1": { "Parachain": $statemine_para_id } } } }') + + local message=$(jq --null-input \ + --argjson hex_encoded_data $hex_encoded_data \ + ' + { + "V3": [ + { + "UnpaidExecution": { + "weight_limit": "Unlimited" + } + }, + { + "Transact": { + "origin_kind": "Superuser", + "require_weight_at_most": { + "ref_time": 1000000000, + "proof_size": 0, + }, + "call": { + "encoded": $hex_encoded_data + } + } + } + ] + } + ') + + echo "" + echo " dest:" + echo "${dest}" + echo "" + echo " message:" + echo "${message}" + echo "" + echo "--------------------------------------------------" + + polkadot-js-api \ + --ws "${relay_url?}" \ + --seed "${relay_chain_seed?}" \ + --sudo \ + tx.xcmPallet.send \ + "${dest}" \ + "${message}" +} + +# TODO: we need to fill sovereign account for bridge-hub, because, small ammouts does not match ExistentialDeposit, so no reserve pass +# SA for BH: MultiLocation { parents: 1, interior: X1(Parachain(1013)) } - 5Eg2fntRRwLinojmk3sh5xscp7F3S6Zzm5oDVtoLTALKiypR on Statemine + +function transfer_asset_via_bridge() { + local url=$1 + local seed=$2 + echo " calling transfer_asset_via_bridge:" + echo " url: ${url}" + echo " seed: ${seed}" + echo " params:" + + + local assets=$(jq --null-input \ + ' + { + "V3": [ + { + "id": { + "Concrete": { + "parents": 1, + "interior": "Here" + } + }, + "fun": { + "Fungible": 100000000 + } + } + ] + } + ' + ) + + +## TODO: decode some account to bytes: "id": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" + + local destination=$(jq --null-input \ + ' + { + "V3": { + "parents": 2, + "interior": { + "X3": [ + { + "GlobalConsensus": "Wococo" + }, + { + "Parachain": 1000 + }, + { + "AccountId32": { + "id": [28, 189, 45, 67, 83, 10, 68, 112, 90, 208, 136, 175, 49, 62, 24, 248, 11, 83, 239, 22, 179, 97, 119, 205, 75, 119, 184, 70, 242, 165, 240, 124] + } + } + ] + } + } + } + ' + ) + + echo "" + echo " assets:" + echo "${assets}" + echo "" + echo " destination:" + echo "${destination}" + echo "" + echo "--------------------------------------------------" + +# local tmp_output_file=$(mktemp) +# generate_hex_encoded_call_data "transfer-asset-via-bridge" "${url}" "${tmp_output_file}" "$assets" "$destination" +# local hex_encoded_data=$(cat $tmp_output_file) + + polkadot-js-api \ + --ws "${url?}" \ + --seed "${seed?}" \ + tx.bridgeAssetsTransfer.transferAssetViaBridge \ + "${assets}" \ + "${destination}" +} + function register_parachain() { local PORT=$1 local PARA_ID=$2 @@ -353,16 +643,17 @@ case "$1" in send-remark-local) ensure_polkadot_js_api send_xcm_transact_remark_from_statemine \ - "ws://127.0.0.1:9910#/explorer" \ + "ws://127.0.0.1:9910" \ "${STATEMINE_ACCOUNT_SEED_FOR_LOCAL}" \ 1013 \ "Wococo" \ - 1000 + 1000 \ + "ws://127.0.0.1:9010" ;; send-trap-local) ensure_polkadot_js_api send_xcm_trap_from_statemine \ - "ws://127.0.0.1:9910#/explorer" \ + "ws://127.0.0.1:9910" \ "${STATEMINE_ACCOUNT_SEED_FOR_LOCAL}" \ 1013 \ "Wococo" \ @@ -375,7 +666,8 @@ case "$1" in "${ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO}" \ 1013 \ "Wococo" \ - 1000 + 1000 \ + "wss://ws-wococo-wockmint-collator-node-0.parity-testnet.parity.io" ;; send-trap-rococo) ensure_polkadot_js_api @@ -386,9 +678,34 @@ case "$1" in "Wococo" \ 1000 ;; + allow-transfer-on-statemine-local) + ensure_polkadot_js_api + allow_assets_transfer_from_statemine \ + "ws://127.0.0.1:9942" \ + "//Alice" \ + 1000 \ + "ws://127.0.0.1:9910" \ + 1013 \ + "Wococo" 1000 + ;; + remove-assets-transfer-from-statemine-local) + ensure_polkadot_js_api + remove_assets_transfer_from_statemine \ + "ws://127.0.0.1:9942" \ + "//Alice" \ + 1000 \ + "ws://127.0.0.1:9910" \ + "Wococo" + ;; + transfer-asset) + ensure_polkadot_js_api + transfer_asset_via_bridge \ + "ws://127.0.0.1:9910" \ + "//Alice" + ;; stop) pkill -f polkadot pkill -f parachain ;; - *) echo "A command is require. Supported commands: start-rococo, start-wococo, init-ro-wo, init-wo-ro, run-relay, stop"; exit 1;; + *) echo "A command is require. Supported commands: run-relay, send-trap-rococo/send-trap-local, send-remark-local/send-remark-rococo, allow-transfer-on-statemine-local/remove-assets-transfer-from-statemine-local, transfer-asset"; exit 1;; esac diff --git a/scripts/generate_hex_encoded_call/index.js b/scripts/generate_hex_encoded_call/index.js new file mode 100644 index 00000000000..167a292bb26 --- /dev/null +++ b/scripts/generate_hex_encoded_call/index.js @@ -0,0 +1,112 @@ +const fs = require("fs"); +const { exit } = require("process"); +const { WsProvider, ApiPromise } = require("@polkadot/api"); +const util = require("@polkadot/util"); + +// connect to a substrate chain and return the api object +async function connect(endpoint, types = {}) { + const provider = new WsProvider(endpoint); + const api = await ApiPromise.create({ + provider, + types, + throwOnConnect: false, + }); + return api; +} + +function writeHexEncodedBytesToOutput(method, outputFile) { + console.log("Payload (hex): ", method.toHex()); + console.log("Payload (bytes): ", Array.from(method.toU8a())); + fs.writeFileSync(outputFile, JSON.stringify(Array.from(method.toU8a()))); +} + +function remarkWithEvent(endpoint, outputFile) { + console.log(`Generating remarkWithEvent from RPC endpoint: ${endpoint} to outputFile: ${outputFile}`); + connect(endpoint) + .then((api) => { + const call = api.tx.system.remarkWithEvent("Hello"); + writeHexEncodedBytesToOutput(call.method, outputFile); + exit(0); + }) + .catch((e) => { + console.error(e); + exit(1); + }); +} + +function addBridgeConfig(endpoint, outputFile, bridgedNetwork, bridgeConfig) { + console.log(`Generating addBridgeConfig from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on bridgedNetwork: ${bridgedNetwork}, bridgeConfig: ${bridgeConfig}`); + connect(endpoint) + .then((api) => { + const call = api.tx.bridgeAssetsTransfer.addBridgeConfig(bridgedNetwork, JSON.parse(bridgeConfig)); + writeHexEncodedBytesToOutput(call.method, outputFile); + exit(0); + }) + .catch((e) => { + console.error(e); + exit(1); + }); +} + +function removeBridgeConfig(endpoint, outputFile, bridgedNetwork) { + console.log(`Generating removeBridgeConfig from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on bridgedNetwork: ${bridgedNetwork}`); + connect(endpoint) + .then((api) => { + const call = api.tx.bridgeAssetsTransfer.removeBridgeConfig(bridgedNetwork); + writeHexEncodedBytesToOutput(call.method, outputFile); + exit(0); + }) + .catch((e) => { + console.error(e); + exit(1); + }); +} + +function transferAssetViaBridge(endpoint, outputFile, assets, destination) { + console.log(`Generating transferAssetViaBridge from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on assets: ${assets}, destination: ${destination}`); + connect(endpoint) + .then((api) => { + const call = api.tx.bridgeAssetsTransfer.transferAssetViaBridge(JSON.parse(assets), JSON.parse(destination)); + writeHexEncodedBytesToOutput(call.method, outputFile); + exit(0); + }) + .catch((e) => { + console.error(e); + exit(1); + }); +} + +if (!process.argv[2] || !process.argv[3]) { + console.log("usage: node ./script/generate_hex_encoded_call "); + exit(1); +} + +const type = process.argv[2]; +const rpcEnpoint = process.argv[3]; +const output = process.argv[4]; +const inputArgs = process.argv.slice(5, process.argv.length); +console.log(`Generating hex-encoded call data for:`); +console.log(` type: ${type}`); +console.log(` rpcEnpoint: ${rpcEnpoint}`); +console.log(` output: ${output}`); +console.log(` inputArgs: ${inputArgs}`); + +switch (type) { + case 'remark-with-event': + remarkWithEvent(rpcEnpoint, output); + break; + case 'add-bridge-config': + addBridgeConfig(rpcEnpoint, output, inputArgs[0], inputArgs[1]); + break; + case 'remove-bridge-config': + removeBridgeConfig(rpcEnpoint, output, inputArgs[0], inputArgs[1]); + break; + case 'transfer-asset-via-bridge': + transferAssetViaBridge(rpcEnpoint, output, inputArgs[0], inputArgs[1]); + break; + case 'check': + console.log(`Checking nodejs installation, if you see this everything is ready!`); + break; + default: + console.log(`Sorry, we are out of ${type} - not yet supported!`); +} diff --git a/scripts/generate_hex_encoded_call/package-lock.json b/scripts/generate_hex_encoded_call/package-lock.json new file mode 100644 index 00000000000..3383265e779 --- /dev/null +++ b/scripts/generate_hex_encoded_call/package-lock.json @@ -0,0 +1,1213 @@ +{ + "name": "y", + "version": "y", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "y", + "version": "y", + "license": "MIT", + "dependencies": { + "@polkadot/api": "^6.5.2", + "@polkadot/util": "^7.6.1" + } + }, + "node_modules/@babel/runtime": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", + "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@noble/hashes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.0.0.tgz", + "integrity": "sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg==" + }, + "node_modules/@noble/secp256k1": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.5.5.tgz", + "integrity": "sha512-sZ1W6gQzYnu45wPrWx8D3kwI2/U29VYTx9OjbDAd7jwRItJ0cSTMPRL/C8AWZFn9kWFLQGqEXVEE86w4Z8LpIQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@polkadot/api": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-6.12.1.tgz", + "integrity": "sha512-RVdTiA2WaEvproM3i6E9TKS1bfXpPd9Ly9lUG/kVLaspjKoIot9DJUDTl97TJ+7xr8LXGbXqm448Ud0hsEBV8Q==", + "dependencies": { + "@babel/runtime": "^7.16.3", + "@polkadot/api-derive": "6.12.1", + "@polkadot/keyring": "^8.1.2", + "@polkadot/rpc-core": "6.12.1", + "@polkadot/rpc-provider": "6.12.1", + "@polkadot/types": "6.12.1", + "@polkadot/types-known": "6.12.1", + "@polkadot/util": "^8.1.2", + "@polkadot/util-crypto": "^8.1.2", + "eventemitter3": "^4.0.7", + "rxjs": "^7.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/api-derive": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-6.12.1.tgz", + "integrity": "sha512-5LOVlG5EBCT+ytY6aHmQ4RdEWZovZQqRoc6DLd5BLhkR7BFTHKSkLQW+89so8jd0zEtmSXBVPPnsrXS8joM35Q==", + "dependencies": { + "@babel/runtime": "^7.16.3", + "@polkadot/api": "6.12.1", + "@polkadot/rpc-core": "6.12.1", + "@polkadot/types": "6.12.1", + "@polkadot/util": "^8.1.2", + "@polkadot/util-crypto": "^8.1.2", + "rxjs": "^7.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/api-derive/node_modules/@polkadot/util": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-8.7.1.tgz", + "integrity": "sha512-XjY1bTo7V6OvOCe4yn8H2vifeuBciCy0gq0k5P1tlGUQLI/Yt0hvDmxcA0FEPtqg8CL+rYRG7WXGPVNjkrNvyQ==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-bigint": "8.7.1", + "@polkadot/x-global": "8.7.1", + "@polkadot/x-textdecoder": "8.7.1", + "@polkadot/x-textencoder": "8.7.1", + "@types/bn.js": "^5.1.0", + "bn.js": "^5.2.0", + "ip-regex": "^4.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/api-derive/node_modules/@polkadot/x-textdecoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-8.7.1.tgz", + "integrity": "sha512-ia0Ie2zi4VdQdNVD2GE2FZzBMfX//hEL4w546RMJfZM2LqDS674LofHmcyrsv5zscLnnRyCxZC1+J2dt+6MDIA==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/api-derive/node_modules/@polkadot/x-textencoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-8.7.1.tgz", + "integrity": "sha512-XDO0A27Xy+eJCKSxENroB8Dcnl+UclGG4ZBei+P/BqZ9rsjskUyd2Vsl6peMXAcsxwOE7g0uTvujoGM8jpKOXw==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/api-derive/node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@polkadot/api-derive/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/@polkadot/api/node_modules/@polkadot/util": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-8.7.1.tgz", + "integrity": "sha512-XjY1bTo7V6OvOCe4yn8H2vifeuBciCy0gq0k5P1tlGUQLI/Yt0hvDmxcA0FEPtqg8CL+rYRG7WXGPVNjkrNvyQ==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-bigint": "8.7.1", + "@polkadot/x-global": "8.7.1", + "@polkadot/x-textdecoder": "8.7.1", + "@polkadot/x-textencoder": "8.7.1", + "@types/bn.js": "^5.1.0", + "bn.js": "^5.2.0", + "ip-regex": "^4.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/api/node_modules/@polkadot/x-textdecoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-8.7.1.tgz", + "integrity": "sha512-ia0Ie2zi4VdQdNVD2GE2FZzBMfX//hEL4w546RMJfZM2LqDS674LofHmcyrsv5zscLnnRyCxZC1+J2dt+6MDIA==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/api/node_modules/@polkadot/x-textencoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-8.7.1.tgz", + "integrity": "sha512-XDO0A27Xy+eJCKSxENroB8Dcnl+UclGG4ZBei+P/BqZ9rsjskUyd2Vsl6peMXAcsxwOE7g0uTvujoGM8jpKOXw==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/api/node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@polkadot/api/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/@polkadot/keyring": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-8.7.1.tgz", + "integrity": "sha512-t6ZgQVC+nQT7XwbWtEhkDpiAzxKVJw8Xd/gWdww6xIrawHu7jo3SGB4QNdPgkf8TvDHYAAJiupzVQYAlOIq3GA==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/util": "8.7.1", + "@polkadot/util-crypto": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@polkadot/util": "8.7.1", + "@polkadot/util-crypto": "8.7.1" + } + }, + "node_modules/@polkadot/keyring/node_modules/@polkadot/util": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-8.7.1.tgz", + "integrity": "sha512-XjY1bTo7V6OvOCe4yn8H2vifeuBciCy0gq0k5P1tlGUQLI/Yt0hvDmxcA0FEPtqg8CL+rYRG7WXGPVNjkrNvyQ==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-bigint": "8.7.1", + "@polkadot/x-global": "8.7.1", + "@polkadot/x-textdecoder": "8.7.1", + "@polkadot/x-textencoder": "8.7.1", + "@types/bn.js": "^5.1.0", + "bn.js": "^5.2.0", + "ip-regex": "^4.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/keyring/node_modules/@polkadot/x-textdecoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-8.7.1.tgz", + "integrity": "sha512-ia0Ie2zi4VdQdNVD2GE2FZzBMfX//hEL4w546RMJfZM2LqDS674LofHmcyrsv5zscLnnRyCxZC1+J2dt+6MDIA==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/keyring/node_modules/@polkadot/x-textencoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-8.7.1.tgz", + "integrity": "sha512-XDO0A27Xy+eJCKSxENroB8Dcnl+UclGG4ZBei+P/BqZ9rsjskUyd2Vsl6peMXAcsxwOE7g0uTvujoGM8jpKOXw==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/keyring/node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@polkadot/keyring/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/@polkadot/networks": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-8.7.1.tgz", + "integrity": "sha512-8xAmhDW0ry5EKcEjp6VTuwoTm0DdDo/zHsmx88P6sVL87gupuFsL+B6TrsYLl8GcaqxujwrOlKB+CKTUg7qFKg==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/util": "8.7.1", + "@substrate/ss58-registry": "^1.17.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/networks/node_modules/@polkadot/util": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-8.7.1.tgz", + "integrity": "sha512-XjY1bTo7V6OvOCe4yn8H2vifeuBciCy0gq0k5P1tlGUQLI/Yt0hvDmxcA0FEPtqg8CL+rYRG7WXGPVNjkrNvyQ==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-bigint": "8.7.1", + "@polkadot/x-global": "8.7.1", + "@polkadot/x-textdecoder": "8.7.1", + "@polkadot/x-textencoder": "8.7.1", + "@types/bn.js": "^5.1.0", + "bn.js": "^5.2.0", + "ip-regex": "^4.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/networks/node_modules/@polkadot/x-textdecoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-8.7.1.tgz", + "integrity": "sha512-ia0Ie2zi4VdQdNVD2GE2FZzBMfX//hEL4w546RMJfZM2LqDS674LofHmcyrsv5zscLnnRyCxZC1+J2dt+6MDIA==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/networks/node_modules/@polkadot/x-textencoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-8.7.1.tgz", + "integrity": "sha512-XDO0A27Xy+eJCKSxENroB8Dcnl+UclGG4ZBei+P/BqZ9rsjskUyd2Vsl6peMXAcsxwOE7g0uTvujoGM8jpKOXw==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/networks/node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@polkadot/networks/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/@polkadot/rpc-core": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-6.12.1.tgz", + "integrity": "sha512-Hb08D9zho3SB1UNlUCmG5q0gdgbOx25JKGLDfSYpD/wtD0Y1Sf2X5cfgtMoSYE3USWiRdCu4BxQkXTiRjPjzJg==", + "dependencies": { + "@babel/runtime": "^7.16.3", + "@polkadot/rpc-provider": "6.12.1", + "@polkadot/types": "6.12.1", + "@polkadot/util": "^8.1.2", + "rxjs": "^7.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/rpc-core/node_modules/@polkadot/util": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-8.7.1.tgz", + "integrity": "sha512-XjY1bTo7V6OvOCe4yn8H2vifeuBciCy0gq0k5P1tlGUQLI/Yt0hvDmxcA0FEPtqg8CL+rYRG7WXGPVNjkrNvyQ==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-bigint": "8.7.1", + "@polkadot/x-global": "8.7.1", + "@polkadot/x-textdecoder": "8.7.1", + "@polkadot/x-textencoder": "8.7.1", + "@types/bn.js": "^5.1.0", + "bn.js": "^5.2.0", + "ip-regex": "^4.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/rpc-core/node_modules/@polkadot/x-textdecoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-8.7.1.tgz", + "integrity": "sha512-ia0Ie2zi4VdQdNVD2GE2FZzBMfX//hEL4w546RMJfZM2LqDS674LofHmcyrsv5zscLnnRyCxZC1+J2dt+6MDIA==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/rpc-core/node_modules/@polkadot/x-textencoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-8.7.1.tgz", + "integrity": "sha512-XDO0A27Xy+eJCKSxENroB8Dcnl+UclGG4ZBei+P/BqZ9rsjskUyd2Vsl6peMXAcsxwOE7g0uTvujoGM8jpKOXw==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/rpc-core/node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@polkadot/rpc-core/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/@polkadot/rpc-provider": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-6.12.1.tgz", + "integrity": "sha512-uUHD3fLTOeZYWJoc6DQlhz+MJR33rVelasV+OxFY2nSD9MSNXRwQh+9UKDQBnyxw5B4BZ2QaEGfucDeavXmVDw==", + "dependencies": { + "@babel/runtime": "^7.16.3", + "@polkadot/types": "6.12.1", + "@polkadot/util": "^8.1.2", + "@polkadot/util-crypto": "^8.1.2", + "@polkadot/x-fetch": "^8.1.2", + "@polkadot/x-global": "^8.1.2", + "@polkadot/x-ws": "^8.1.2", + "eventemitter3": "^4.0.7" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/rpc-provider/node_modules/@polkadot/util": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-8.7.1.tgz", + "integrity": "sha512-XjY1bTo7V6OvOCe4yn8H2vifeuBciCy0gq0k5P1tlGUQLI/Yt0hvDmxcA0FEPtqg8CL+rYRG7WXGPVNjkrNvyQ==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-bigint": "8.7.1", + "@polkadot/x-global": "8.7.1", + "@polkadot/x-textdecoder": "8.7.1", + "@polkadot/x-textencoder": "8.7.1", + "@types/bn.js": "^5.1.0", + "bn.js": "^5.2.0", + "ip-regex": "^4.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/rpc-provider/node_modules/@polkadot/x-textdecoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-8.7.1.tgz", + "integrity": "sha512-ia0Ie2zi4VdQdNVD2GE2FZzBMfX//hEL4w546RMJfZM2LqDS674LofHmcyrsv5zscLnnRyCxZC1+J2dt+6MDIA==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/rpc-provider/node_modules/@polkadot/x-textencoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-8.7.1.tgz", + "integrity": "sha512-XDO0A27Xy+eJCKSxENroB8Dcnl+UclGG4ZBei+P/BqZ9rsjskUyd2Vsl6peMXAcsxwOE7g0uTvujoGM8jpKOXw==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/rpc-provider/node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@polkadot/rpc-provider/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/@polkadot/types": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-6.12.1.tgz", + "integrity": "sha512-O37cAGUL0xiXTuO3ySweVh0OuFUD6asrd0TfuzGsEp3jAISWdElEHV5QDiftWq8J9Vf8BMgTcP2QLFbmSusxqA==", + "dependencies": { + "@babel/runtime": "^7.16.3", + "@polkadot/types-known": "6.12.1", + "@polkadot/util": "^8.1.2", + "@polkadot/util-crypto": "^8.1.2", + "rxjs": "^7.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/types-known": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-6.12.1.tgz", + "integrity": "sha512-Z8bHpPQy+mqUm0uR1tai6ra0bQIoPmgRcGFYUM+rJtW1kx/6kZLh10HAICjLpPeA1cwLRzaxHRDqH5MCU6OgXw==", + "dependencies": { + "@babel/runtime": "^7.16.3", + "@polkadot/networks": "^8.1.2", + "@polkadot/types": "6.12.1", + "@polkadot/util": "^8.1.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/types-known/node_modules/@polkadot/util": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-8.7.1.tgz", + "integrity": "sha512-XjY1bTo7V6OvOCe4yn8H2vifeuBciCy0gq0k5P1tlGUQLI/Yt0hvDmxcA0FEPtqg8CL+rYRG7WXGPVNjkrNvyQ==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-bigint": "8.7.1", + "@polkadot/x-global": "8.7.1", + "@polkadot/x-textdecoder": "8.7.1", + "@polkadot/x-textencoder": "8.7.1", + "@types/bn.js": "^5.1.0", + "bn.js": "^5.2.0", + "ip-regex": "^4.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/types-known/node_modules/@polkadot/x-textdecoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-8.7.1.tgz", + "integrity": "sha512-ia0Ie2zi4VdQdNVD2GE2FZzBMfX//hEL4w546RMJfZM2LqDS674LofHmcyrsv5zscLnnRyCxZC1+J2dt+6MDIA==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/types-known/node_modules/@polkadot/x-textencoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-8.7.1.tgz", + "integrity": "sha512-XDO0A27Xy+eJCKSxENroB8Dcnl+UclGG4ZBei+P/BqZ9rsjskUyd2Vsl6peMXAcsxwOE7g0uTvujoGM8jpKOXw==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/types-known/node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@polkadot/types-known/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/@polkadot/types/node_modules/@polkadot/util": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-8.7.1.tgz", + "integrity": "sha512-XjY1bTo7V6OvOCe4yn8H2vifeuBciCy0gq0k5P1tlGUQLI/Yt0hvDmxcA0FEPtqg8CL+rYRG7WXGPVNjkrNvyQ==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-bigint": "8.7.1", + "@polkadot/x-global": "8.7.1", + "@polkadot/x-textdecoder": "8.7.1", + "@polkadot/x-textencoder": "8.7.1", + "@types/bn.js": "^5.1.0", + "bn.js": "^5.2.0", + "ip-regex": "^4.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/types/node_modules/@polkadot/x-textdecoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-8.7.1.tgz", + "integrity": "sha512-ia0Ie2zi4VdQdNVD2GE2FZzBMfX//hEL4w546RMJfZM2LqDS674LofHmcyrsv5zscLnnRyCxZC1+J2dt+6MDIA==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/types/node_modules/@polkadot/x-textencoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-8.7.1.tgz", + "integrity": "sha512-XDO0A27Xy+eJCKSxENroB8Dcnl+UclGG4ZBei+P/BqZ9rsjskUyd2Vsl6peMXAcsxwOE7g0uTvujoGM8jpKOXw==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/types/node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@polkadot/types/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/@polkadot/util": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-7.9.2.tgz", + "integrity": "sha512-6ABY6ErgkCsM4C6+X+AJSY4pBGwbKlHZmUtHftaiTvbaj4XuA4nTo3GU28jw8wY0Jh2cJZJvt6/BJ5GVkm5tBA==", + "dependencies": { + "@babel/runtime": "^7.16.3", + "@polkadot/x-textdecoder": "7.9.2", + "@polkadot/x-textencoder": "7.9.2", + "@types/bn.js": "^4.11.6", + "bn.js": "^4.12.0", + "camelcase": "^6.2.1", + "ip-regex": "^4.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/util-crypto": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-8.7.1.tgz", + "integrity": "sha512-TaSuJ2aNrB5sYK7YXszkEv24nYJKRFqjF2OrggoMg6uYxUAECvTkldFnhtgeizMweRMxJIBu6bMHlSIutbWgjw==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@noble/hashes": "1.0.0", + "@noble/secp256k1": "1.5.5", + "@polkadot/networks": "8.7.1", + "@polkadot/util": "8.7.1", + "@polkadot/wasm-crypto": "^5.1.1", + "@polkadot/x-bigint": "8.7.1", + "@polkadot/x-randomvalues": "8.7.1", + "@scure/base": "1.0.0", + "ed2curve": "^0.3.0", + "tweetnacl": "^1.0.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@polkadot/util": "8.7.1" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/util": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-8.7.1.tgz", + "integrity": "sha512-XjY1bTo7V6OvOCe4yn8H2vifeuBciCy0gq0k5P1tlGUQLI/Yt0hvDmxcA0FEPtqg8CL+rYRG7WXGPVNjkrNvyQ==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-bigint": "8.7.1", + "@polkadot/x-global": "8.7.1", + "@polkadot/x-textdecoder": "8.7.1", + "@polkadot/x-textencoder": "8.7.1", + "@types/bn.js": "^5.1.0", + "bn.js": "^5.2.0", + "ip-regex": "^4.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-textdecoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-8.7.1.tgz", + "integrity": "sha512-ia0Ie2zi4VdQdNVD2GE2FZzBMfX//hEL4w546RMJfZM2LqDS674LofHmcyrsv5zscLnnRyCxZC1+J2dt+6MDIA==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-textencoder": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-8.7.1.tgz", + "integrity": "sha512-XDO0A27Xy+eJCKSxENroB8Dcnl+UclGG4ZBei+P/BqZ9rsjskUyd2Vsl6peMXAcsxwOE7g0uTvujoGM8jpKOXw==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@polkadot/util-crypto/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/@polkadot/wasm-crypto": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-5.1.1.tgz", + "integrity": "sha512-JCcAVfH8DhYuEyd4oX1ouByxhou0TvpErKn8kHjtzt7+tRoFi0nzWlmK4z49vszsV3JJgXxV81i10C0BYlwTcQ==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/wasm-crypto-asmjs": "^5.1.1", + "@polkadot/wasm-crypto-wasm": "^5.1.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-asmjs": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-5.1.1.tgz", + "integrity": "sha512-1WBwc2G3pZMKW1T01uXzKE30Sg22MXmF3RbbZiWWk3H2d/Er4jZQRpjumxO5YGWan+xOb7HQQdwnrUnrPgbDhg==", + "dependencies": { + "@babel/runtime": "^7.17.8" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-wasm": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-5.1.1.tgz", + "integrity": "sha512-F9PZ30J2S8vUNl2oY7Myow5Xsx5z5uNVpnNlJwlmY8IXBvyucvyQ4HSdhJsrbs4W1BfFc0mHghxgp0FbBCnf/Q==", + "dependencies": { + "@babel/runtime": "^7.17.8" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/x-bigint": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-8.7.1.tgz", + "integrity": "sha512-ClkhgdB/KqcAKk3zA6Qw8wBL6Wz67pYTPkrAtImpvoPJmR+l4RARauv+MH34JXMUNlNb3aUwqN6lq2Z1zN+mJg==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/x-fetch": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-8.7.1.tgz", + "integrity": "sha512-ygNparcalYFGbspXtdtZOHvNXZBkNgmNO+um9C0JYq74K5OY9/be93uyfJKJ8JcRJtOqBfVDsJpbiRkuJ1PRfg==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1", + "@types/node-fetch": "^2.6.1", + "node-fetch": "^2.6.7" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/x-global": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-8.7.1.tgz", + "integrity": "sha512-WOgUor16IihgNVdiTVGAWksYLUAlqjmODmIK1cuWrLOZtV1VBomWcb3obkO9sh5P6iWziAvCB/i+L0vnTN9ZCA==", + "dependencies": { + "@babel/runtime": "^7.17.8" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/x-randomvalues": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-8.7.1.tgz", + "integrity": "sha512-njt17MlfN6yNyNEti7fL12lr5qM6A1aSGkWKVuqzc7XwSBesifJuW4km5u6r2gwhXjH2eHDv9SoQ7WXu8vrrkg==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/x-textdecoder": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-7.9.2.tgz", + "integrity": "sha512-wfwbSHXPhrOAl12QvlIOGNkMH/N/h8PId2ytIjvM/8zPPFB5Il6DWSFLtVapOGEpIFjEWbd5t8Td4pHBVXIEbg==", + "dependencies": { + "@babel/runtime": "^7.16.3", + "@polkadot/x-global": "7.9.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/x-textdecoder/node_modules/@polkadot/x-global": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-7.9.2.tgz", + "integrity": "sha512-JX5CrGWckHf1P9xKXq4vQCAuMUbL81l2hOWX7xeP8nv4caHEpmf5T1wD1iMdQBL5PFifo6Pg0V6/oZBB+bts7A==", + "dependencies": { + "@babel/runtime": "^7.16.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/x-textencoder": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-7.9.2.tgz", + "integrity": "sha512-A19wwYINuZwU2dUyQ/mMzB0ISjyfc4cISfL4zCMUAVgj7xVoXMYV2GfjNdMpA8Wsjch3su6pxLbtJ2wU03sRTQ==", + "dependencies": { + "@babel/runtime": "^7.16.3", + "@polkadot/x-global": "7.9.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/x-textencoder/node_modules/@polkadot/x-global": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-7.9.2.tgz", + "integrity": "sha512-JX5CrGWckHf1P9xKXq4vQCAuMUbL81l2hOWX7xeP8nv4caHEpmf5T1wD1iMdQBL5PFifo6Pg0V6/oZBB+bts7A==", + "dependencies": { + "@babel/runtime": "^7.16.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@polkadot/x-ws": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-8.7.1.tgz", + "integrity": "sha512-Mt0tcNzGXyKnN3DQ06alkv+JLtTfXWu6zSypFrrKHSQe3u79xMQ1nSicmpT3gWLhIa8YF+8CYJXMrqaXgCnDhw==", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@polkadot/x-global": "8.7.1", + "@types/websocket": "^1.0.5", + "websocket": "^1.0.34" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@scure/base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.0.0.tgz", + "integrity": "sha512-gIVaYhUsy+9s58m/ETjSJVKHhKTBMmcRb9cEV5/5dwvfDlfORjKrFsDeDHWRrm6RjcPvCLZFwGJjAjLj1gg4HA==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@substrate/ss58-registry": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.38.0.tgz", + "integrity": "sha512-sHiVRWekGMRZAjPukN9/W166NM6D5wtHcK6RVyLy66kg3CHNZ1BXfpXcjOiXSwhbd7guQFDEwnOVaDrbk1XL1g==" + }, + "node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" + }, + "node_modules/@types/node-fetch": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/websocket": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.5.tgz", + "integrity": "sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/bufferutil": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", + "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ed2curve": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ed2curve/-/ed2curve-0.3.0.tgz", + "integrity": "sha512-8w2fmmq3hv9rCrcI7g9hms2pMunQr1JINfcjwR9tAyZqhtyaMN991lF/ZfHfr5tzZQ8c7y7aBgZbjfbd0fjFwQ==", + "dependencies": { + "tweetnacl": "1.x.x" + } + }, + "node_modules/es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "node_modules/node-fetch": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", + "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/websocket": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", + "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "engines": { + "node": ">=0.10.32" + } + } + } +} diff --git a/scripts/generate_hex_encoded_call/package.json b/scripts/generate_hex_encoded_call/package.json new file mode 100644 index 00000000000..1c68924db24 --- /dev/null +++ b/scripts/generate_hex_encoded_call/package.json @@ -0,0 +1,11 @@ +{ + "name": "y", + "version": "y", + "description": "create a scale hex-encoded call values from given message", + "main": "index.js", + "license": "MIT", + "dependencies": { + "@polkadot/api": "^6.5.2", + "@polkadot/util": "^7.6.1" + } +} diff --git a/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml b/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml index 2268abffbe2..529fd638674 100644 --- a/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml +++ b/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml @@ -1,3 +1,6 @@ +[settings] +node_spawn_timeout = 240 + [relaychain] default_command = "{{POLKADOT_BINARY_PATH}}" default_args = [ "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace" ] diff --git a/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml b/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml index c68e64a767e..cf8afd63ef5 100644 --- a/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml +++ b/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml @@ -1,3 +1,6 @@ +[settings] +node_spawn_timeout = 240 + [relaychain] default_command = "{{POLKADOT_BINARY_PATH}}" default_args = [ "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace" ] From f84df6c08ca4f59bdbe63d8b974dacc18c904682 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 8 Feb 2023 12:03:43 +0100 Subject: [PATCH 193/263] script --- parachains/runtimes/bridge-hubs/README.md | 21 ++++++--- scripts/bridges_update_subtree.sh | 53 ++++++++++++++++------- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index a0c674b59e2..ba719e336fe 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -220,14 +220,20 @@ or zombienet-linux --provider native spawn ./zombienet/examples/bridge_hub_polkadot_local_network.toml ``` +## How to test local BridgeHubPolkadot +TODO: from master + ---- ## Git subtree `./bridges` Add Bridges repo as a local remote and synchronize it with latest `master` from bridges repo: + +### How to update `bridges` subtree ``` -git remote add -f bridges git@github.com:paritytech/parity-bridges-common.git -# (ran just only first time, when subtree was initialized) -# git subtree add --prefix=bridges bridges master --squash +cd +./scripts/bridges_update_subtree.sh fetch + + # Synchro bridges repo git fetch bridges --prune @@ -235,7 +241,12 @@ git subtree pull --prefix=bridges bridges master --squash ```` We use `--squash` to avoid adding individual commits and rather squashing them all into one. - Now we use `master` branch, but in future, it could change to some release branch/tag. -Original `./bridges/Cargo.toml` was renamed to `./bridges/Cargo.toml_removed_for_bridges_subtree_feature` to avoid confusion for `Cargo` having multiple workspaces. +### How was first time initialized (dont need anymore) +``` +cd +git remote add -f bridges git@github.com:paritytech/parity-bridges-common.git +# (ran just only first time, when subtree was initialized) +git subtree add --prefix=bridges bridges master --squash +``` diff --git a/scripts/bridges_update_subtree.sh b/scripts/bridges_update_subtree.sh index 0cfa40f36f2..b9eca316330 100755 --- a/scripts/bridges_update_subtree.sh +++ b/scripts/bridges_update_subtree.sh @@ -1,24 +1,47 @@ #!/bin/bash -function fetch() { - # TODO: check if already existing remote bridges - # if not add one: git remote add -f bridges git@github.com:paritytech/parity-bridges-common.git +# A script to udpate bridges repo as subtree to Cumulus +# Usage: +# ./scripts/bridges_update_subtree.sh fetch +# ./scripts/bridges_update_subtree.sh fetch + +set -e - BRIDGES_BRANCH="${BRANCH:-master}" - echo "Syncing with branch: '$BRIDGES_BRANCH'" +BRIDGES_BRANCH="${BRANCH:-master}" +BRIDGES_TARGET_DIR="${TARGET_DIR:-bridges}" - # rm -R bridges - # git add --all - # echo "... check YubiKey" - # git commit -S -m "updating bridges subtree" - # echo "... check YubiKey" - #git subtree add --prefix=bridges bridges $BRIDGES_BRANCH --squash +# the script is able to work only on clean git copy +[[ -z "$(git status --porcelain)" ]] || { + echo >&2 "The git copy must be clean (stash all your changes):"; + git status --porcelain + exit 1; +} - # OR +function fetch() { + local bridges_remote=$(git remote -v | grep "parity-bridges-common.git (fetch)" | head -n1 | awk '{print $1;}') + if [ -z "$bridges_remote" ]; then + echo "" + echo "Adding new remote: 'bridges' repo..." + echo "" + echo "... check your YubiKey ..." + git remote add -f bridges git@github.com:paritytech/parity-bridges-common.git + bridges_remote="bridges" + else + echo "" + echo "Fetching remote: '${bridges_remote}' repo..." + echo "" + echo "... check your YubiKey ..." + git fetch ${bridges_remote} --prune + fi - echo "... check YubiKey" - git fetch bridges --prune - git subtree pull --prefix=bridges bridges $BRIDGES_BRANCH --squash + echo "" + echo "Syncing/updating subtree with remote branch '${bridges_remote}/$BRIDGES_BRANCH' to target directory: '$BRIDGES_TARGET_DIR'" + echo "" + echo "... check your YubiKey ..." + git subtree pull --prefix=$BRIDGES_TARGET_DIR ${bridges_remote} $BRIDGES_BRANCH --squash + echo "" + echo "" + echo "" echo ".. if there are any conflict, please, resolve and then 'git merge --continue'" } From 591a68f6f64dd31afe886fdf21f35da9e13ca9cf Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 8 Feb 2023 12:04:33 +0100 Subject: [PATCH 194/263] Squashed 'bridges/' changes from d5f95c14a..b39cb0dea b39cb0dea MaxValues limit for storage maps in the pallet-bridge-grandpa (#1861) 11b3a611d fixed TODOs for weights v2 (#1860) 5a44f9fea Message delivery transaction is not free!!! (#1859) 59a42bd58 fixed BEEFY genesis (#1858) ab7c7ad0f Use parity-util-mem 0.12.0 (#1856) 8fd346e5a changed some tests for weights v2 (#1855) c438b9f74 Add separate Cargo.lock for `tools/runtime-codegen` (#1854) fc55a97d7 Fix `HeadersToKeep` and `MaxBridgedAuthorities` in Millau benchmarks (#1851) 72e64a3d7 Decrease number of GRANDPA authorities in Polkadot-like chains from 100_000 to 2_048 (#1852) d60a331ed Update Substrate/Polkadot/Cumulus dependencies + weights v2 (#1850) 61b229b65 Bump async-trait from 0.1.63 to 0.1.64 366333108 Bump serde_json from 1.0.91 to 1.0.92 (#1845) 4d917bb3a Bump trie-db from 0.24.0 to 0.25.0 8d919eac9 Bump anyhow from 1.0.68 to 1.0.69 ef9364dd0 Bump proc-macro2 from 1.0.49 to 1.0.51 9ddeebed5 Bump futures from 0.3.25 to 0.3.26 e02eb7573 connect using wss under flag condition (#1843) 99754a07f remove extra tracing on test deployments (#1842) bdb84cea6 Add tool for auto generating runtime code from metadata (#1812) 86662f263 fix bridge-runtime-common build (#1839) f656ac77d Change some macro names (#1837) 50f2980e9 Verify partial repo build on CI (#1832) f70f8231b fix bridge hubs blocks interval (#1836) ddbe5cddf [ci] change runners (#1833) 45a68ad39 Fix on demand parachains relay when no parachain head at target (#1834) 6dbce7258 Use GitLab env vars to get git commit (#1831) b1a8161e8 bump bridge hub versions (#1830) e909595e0 Use specific error for case when para head is missing from the bridge pallet (#1829) d517da8a2 Do not read parachain heads from ancient relay headers (#1827) 217bc72f5 Reconnect source client (#1826) 47bf5f693 Bump tokio from 1.24.2 to 1.25.0 6b307b48a Bump clap from 4.1.3 to 4.1.4 90bc29a17 Use named parameters for indirect calls (#1823) 986eeb556 Fix: typos (#1822) 450823b01 docs: fix broken link and minor nits (#1821) 3ed01ae31 do not call best_finalized_para_block_at_source for ancient block (#1819) 001956290 Functions to benchmark messages pallet with linked to parachain (#1817) e9b0a1c48 Remove InboundPayload check (#1816) 873ea4e40 Bump clap from 4.1.1 to 4.1.3 97eccaa8b also ignore the base xcm crate (#1798) 2d3dcd00b Update bundled runtime version for bridge hub r/wococo (#1814) 7167c0067 Bump bumpalo from 3.10.0 to 3.12.0 in /fuzz/storage-proof 067687520 Bump async-trait from 0.1.62 to 0.1.63 (#1811) git-subtree-dir: bridges git-subtree-split: b39cb0dea5751847ea73ab9946667003625eaf1a --- .github/dependabot.yml | 2 +- .gitlab-ci.yml | 41 +- .maintain/millau-weight-template.hbs | 132 +- Cargo.lock | 3429 +++--- bin/millau/node/Cargo.toml | 4 +- bin/millau/node/src/chain_spec.rs | 2 +- bin/millau/node/src/cli.rs | 1 + bin/millau/node/src/service.rs | 2 +- bin/millau/runtime/src/lib.rs | 43 +- bin/millau/runtime/src/rialto_messages.rs | 4 +- .../runtime/src/rialto_parachain_messages.rs | 4 +- bin/millau/runtime/src/xcm_config.rs | 13 +- bin/rialto-parachain/node/Cargo.toml | 2 +- bin/rialto-parachain/runtime/src/lib.rs | 23 +- .../runtime/src/millau_messages.rs | 4 +- bin/rialto/node/Cargo.toml | 4 +- bin/rialto/node/src/chain_spec.rs | 7 +- bin/rialto/node/src/cli.rs | 1 + bin/rialto/runtime/src/lib.rs | 19 +- bin/rialto/runtime/src/millau_messages.rs | 4 +- bin/rialto/runtime/src/parachains.rs | 43 +- bin/rialto/runtime/src/xcm_config.rs | 13 +- bin/runtime-common/Cargo.toml | 2 + bin/runtime-common/src/integrity.rs | 1 - .../src/messages_benchmarking.rs | 201 +- .../bridges/rialto-millau/docker-compose.yml | 3 +- .../relay-millau-rialto-entrypoint.sh | 9 +- deployments/networks/rialto.yml | 2 - docs/high-level-overview.md | 4 +- docs/polkadot-kusama-bridge-overview.md | 6 +- fuzz/storage-proof/Cargo.lock | 193 +- modules/grandpa/src/benchmarking.rs | 53 +- modules/grandpa/src/lib.rs | 93 +- modules/grandpa/src/weights.rs | 120 +- modules/messages/README.md | 28 +- modules/messages/src/lib.rs | 48 +- modules/messages/src/mock.rs | 2 +- modules/messages/src/weights.rs | 456 +- modules/messages/src/weights_ext.rs | 14 + modules/parachains/src/lib.rs | 33 +- modules/parachains/src/weights.rs | 228 +- modules/relayers/src/weights.rs | 50 +- primitives/beefy/Cargo.toml | 2 +- primitives/beefy/src/lib.rs | 2 +- .../chain-bridge-hub-cumulus/Cargo.toml | 2 + .../chain-bridge-hub-cumulus/src/lib.rs | 16 +- primitives/chain-millau/Cargo.toml | 2 +- primitives/chain-millau/src/lib.rs | 8 +- primitives/chain-rialto-parachain/src/lib.rs | 8 +- primitives/chain-rialto/src/lib.rs | 8 +- primitives/chain-rococo/src/lib.rs | 5 - primitives/chain-westend/src/lib.rs | 5 - primitives/header-chain/src/lib.rs | 7 +- primitives/messages/src/lib.rs | 12 +- primitives/parachains/src/lib.rs | 10 +- primitives/polkadot-core/src/lib.rs | 16 +- primitives/runtime/Cargo.toml | 2 +- relays/bin-substrate/Cargo.toml | 2 +- ...ub_rococo_messages_to_bridge_hub_wococo.rs | 4 +- ...ub_wococo_messages_to_bridge_hub_rococo.rs | 4 +- .../millau_headers_to_rialto_parachain.rs | 6 +- .../millau_messages_to_rialto_parachain.rs | 6 +- .../src/chains/rialto_parachain.rs | 12 +- .../rialto_parachain_messages_to_millau.rs | 6 +- relays/bin-substrate/src/chains/rococo.rs | 2 +- .../rococo_headers_to_bridge_hub_wococo.rs | 2 +- .../rococo_parachains_to_bridge_hub_wococo.rs | 6 +- relays/bin-substrate/src/chains/wococo.rs | 2 +- .../wococo_headers_to_bridge_hub_rococo.rs | 2 +- .../wococo_parachains_to_bridge_hub_rococo.rs | 6 +- relays/bin-substrate/src/cli/init_bridge.rs | 23 +- relays/bin-substrate/src/cli/mod.rs | 20 +- relays/client-bridge-hub-rococo/src/lib.rs | 3 +- relays/client-bridge-hub-wococo/src/lib.rs | 4 +- .../src/runtime_wrapper.rs | 2 +- relays/client-rialto-parachain/Cargo.toml | 2 + .../src/codegen_runtime.rs | 9830 +++++++++++++++++ relays/client-rialto-parachain/src/lib.rs | 14 +- .../src/runtime_wrapper.rs | 57 - relays/client-substrate/Cargo.toml | 4 +- relays/client-substrate/src/client.rs | 7 +- relays/client-substrate/src/error.rs | 3 + relays/client-substrate/src/lib.rs | 4 +- relays/finality/Cargo.toml | 2 +- relays/finality/README.md | 10 +- relays/lib-substrate-relay/Cargo.toml | 4 +- .../lib-substrate-relay/src/finality/mod.rs | 9 +- .../src/finality/target.rs | 3 +- .../lib-substrate-relay/src/messages_lane.rs | 37 +- .../src/messages_source.rs | 22 +- .../src/on_demand/parachains.rs | 133 +- .../src/parachains/source.rs | 11 +- .../src/parachains/target.rs | 7 +- relays/messages/Cargo.toml | 2 +- relays/messages/src/message_lane_loop.rs | 89 +- relays/messages/src/message_race_loop.rs | 2 +- relays/messages/src/metrics.rs | 38 +- relays/parachains/Cargo.toml | 4 +- relays/parachains/README.md | 2 +- relays/parachains/src/parachains_loop.rs | 4 +- relays/utils/Cargo.toml | 4 +- ...-repo-build.sh => verify-pallets-build.sh} | 54 +- tools/runtime-codegen/Cargo.lock | 3888 +++++++ tools/runtime-codegen/Cargo.toml | 20 + tools/runtime-codegen/README.md | 11 + tools/runtime-codegen/src/main.rs | 173 + 106 files changed, 17817 insertions(+), 2193 deletions(-) create mode 100644 relays/client-rialto-parachain/src/codegen_runtime.rs delete mode 100644 relays/client-rialto-parachain/src/runtime_wrapper.rs rename scripts/{verify-partial-repo-build.sh => verify-pallets-build.sh} (76%) create mode 100644 tools/runtime-codegen/Cargo.lock create mode 100644 tools/runtime-codegen/Cargo.toml create mode 100644 tools/runtime-codegen/README.md create mode 100644 tools/runtime-codegen/src/main.rs diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f53101109c5..b3193adadc1 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -46,7 +46,7 @@ updates: - dependency-name: polkadot-* versions: - ">= 0" - - dependency-name: xcm-* + - dependency-name: xcm* versions: - ">= 0" # Cumulus dependencies diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 698dc09e79e..15ba0ca7cb6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,21 +5,24 @@ stages: - build - publish -workflow: - rules: - - if: $CI_COMMIT_TAG - - if: $CI_COMMIT_BRANCH - -variables: &default-vars +variables: GIT_STRATEGY: fetch GIT_DEPTH: 100 CARGO_INCREMENTAL: 0 ARCH: "x86_64" CI_IMAGE: "paritytech/bridges-ci:production" + BUILDAH_IMAGE: "quay.io/buildah/stable:v1.27" RUST_BACKTRACE: full default: cache: {} + interruptible: true + retry: + max: 2 + when: + - runner_system_failure + - unknown_failure + - api_failure .collect-artifacts: &collect-artifacts artifacts: @@ -32,7 +35,6 @@ default: .kubernetes-build: &kubernetes-build tags: - kubernetes-parity-build - interruptible: true .docker-env: &docker-env image: "${CI_IMAGE}" @@ -41,16 +43,8 @@ default: - cargo --version - rustup +nightly show - cargo +nightly --version - - sccache -s - retry: - max: 2 - when: - - runner_system_failure - - unknown_failure - - api_failure - interruptible: true tags: - - linux-docker + - linux-docker-vm-c2 .test-refs: &test-refs rules: @@ -112,7 +106,7 @@ spellcheck: <<: *docker-env <<: *test-refs script: - - cargo spellcheck check --cfg=.config/spellcheck.toml --checkers hunspell -m 1 + - cargo spellcheck check --cfg=.config/spellcheck.toml --checkers hunspell -m 1 $(find . -type f -name '*.rs' ! -path "./target/*" ! -name 'codegen_runtime.rs' ! -name 'weights.rs') #### stage: check @@ -197,6 +191,15 @@ benchmarks-test: # we may live with failing benchmarks, it is just a signal for us allow_failure: true +partial-repo-build-test: + stage: test + <<: *docker-env + <<: *nightly-test + script: + - ./scripts/verify-pallets-build.sh --no-revert + # we may live with failing partial repo build, it is just a signal for us + allow_failure: true + #### stage: build build: @@ -237,7 +240,7 @@ build-nightly: .build-push-image: &build-push-image <<: *kubernetes-build - image: quay.io/buildah/stable:v1.27 + image: $BUILDAH_IMAGE <<: *build-refs variables: &image-variables GIT_STRATEGY: none @@ -248,7 +251,7 @@ build-nightly: needs: - job: build artifacts: true - before_script: &check-versions + before_script: - echo "Starting docker image build/push with name '${IMAGE_NAME}' for '${BRIDGES_PROJECT}' with Dockerfile = '${DOCKERFILE}'" - if [[ "${CI_COMMIT_TAG}" ]]; then VERSION=${CI_COMMIT_TAG}; diff --git a/.maintain/millau-weight-template.hbs b/.maintain/millau-weight-template.hbs index ac5c6b85b91..40a9b17bc13 100644 --- a/.maintain/millau-weight-template.hbs +++ b/.maintain/millau-weight-template.hbs @@ -14,16 +14,16 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Autogenerated weights for `{{pallet}}` +//! 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}} -//! EXECUTION: {{cmd.execution}}, WASM-EXECUTION: {{cmd.wasm_execution}} -//! CHAIN: {{cmd.chain}}, DB CACHE: {{cmd.db_cache}} +//! 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}}` +//! EXECUTION: {{cmd.execution}}, WASM-EXECUTION: {{cmd.wasm_execution}}, CHAIN: {{cmd.chain}}, DB CACHE: {{cmd.db_cache}} // Executed Command: -{{#each args as |arg|~}} +{{#each args as |arg|}} // {{arg}} {{/each}} @@ -35,72 +35,112 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use sp_std::marker::PhantomData; -/// Weight functions needed for `{{pallet}}`. +/// Weight functions needed for {{pallet}}. pub trait WeightInfo { - {{~#each benchmarks as |benchmark|}} + {{#each benchmarks as |benchmark|}} fn {{benchmark.name~}} ( {{~#each benchmark.components as |c| ~}} {{c.name}}: u32, {{/each~}} ) -> Weight; - {{~/each}} + {{/each}} } /// Weights for `{{pallet}}` that are generated using one of the Bridge testnets. /// -/// Those weights are test only and must never be used in production. +/// Those weights are test only and must never be used in production. pub struct BridgeWeight(PhantomData); impl WeightInfo for BridgeWeight { - {{~#each benchmarks as |benchmark|}} + {{#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 { - Weight::from_ref_time({{underscore benchmark.base_weight}} as u64) - {{~#each benchmark.component_weight as |cw|}} - .saturating_add(Weight::from_ref_time({{underscore cw.slope}} as u64).saturating_mul({{cw.name}} as u64)) - {{~/each}} - {{~#if (ne benchmark.base_reads "0")}} - .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}} as u64)) - {{~/if}} - {{~#each benchmark.component_reads as |cr|}} - .saturating_add(T::DbWeight::get().reads(({{cr.slope}} as u64).saturating_mul({{cr.name}} as u64))) - {{~/each}} - {{~#if (ne benchmark.base_writes "0")}} - .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}} as u64)) - {{~/if}} - {{~#each benchmark.component_writes as |cw|}} - .saturating_add(T::DbWeight::get().writes(({{cw.slope}} as u64).saturating_mul({{cw.name}} as u64))) - {{~/each}} + // 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}} nanoseconds. + {{#if (ne benchmark.base_calculated_proof_size "0")}} + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{else}} + Weight::from_ref_time({{underscore benchmark.base_weight}}) + {{/if}} + {{#each benchmark.component_weight as |cw|}} + // Standard Error: {{underscore cw.error}} + .saturating_add(Weight::from_ref_time({{underscore cw.slope}}).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_proof_size({{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} } - {{~/each}} + {{/each}} } // For backwards compatibility and tests impl WeightInfo for () { - {{~#each benchmarks as |benchmark|}} + {{#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 { - Weight::from_ref_time({{underscore benchmark.base_weight}} as u64) - {{~#each benchmark.component_weight as |cw|}} - .saturating_add(Weight::from_ref_time({{underscore cw.slope}} as u64).saturating_mul({{cw.name}} as u64)) - {{~/each}} - {{~#if (ne benchmark.base_reads "0")}} - .saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}} as u64)) - {{~/if}} - {{~#each benchmark.component_reads as |cr|}} - .saturating_add(RocksDbWeight::get().reads(({{cr.slope}} as u64).saturating_mul({{cr.name}} as u64))) - {{~/each}} - {{~#if (ne benchmark.base_writes "0")}} - .saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}} as u64)) - {{~/if}} - {{~#each benchmark.component_writes as |cw|}} - .saturating_add(RocksDbWeight::get().writes(({{cw.slope}} as u64).saturating_mul({{cw.name}} as u64))) - {{~/each}} + // 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}} nanoseconds. + {{#if (ne benchmark.base_calculated_proof_size "0")}} + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{else}} + Weight::from_ref_time({{underscore benchmark.base_weight}}) + {{/if}} + {{#each benchmark.component_weight as |cw|}} + // Standard Error: {{underscore cw.error}} + .saturating_add(Weight::from_ref_time({{underscore cw.slope}}).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_proof_size({{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} } - {{~/each}} + {{/each}} } diff --git a/Cargo.lock b/Cargo.lock index 9545c17c715..3f09e60c479 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,7 +27,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli 0.27.0", + "gimli 0.27.1", ] [[package]] @@ -137,6 +137,18 @@ dependencies = [ "version_check", ] +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if 1.0.0", + "getrandom 0.2.8", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.20" @@ -172,9 +184,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "approx" @@ -187,9 +199,9 @@ dependencies = [ [[package]] name = "arbitrary" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0224938f92e7aef515fac2ff2d18bd1115c1394ddf4a092e0c87e8be9499ee5" +checksum = "3e90af4de65aa7b293ef2d09daff88501eb254f58edde2e1ac02c82d873eadad" [[package]] name = "arc-swap" @@ -265,7 +277,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", "synstructure", @@ -277,7 +289,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", "synstructure", @@ -289,7 +301,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -421,11 +433,11 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.62" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "689894c2db1ea643a50834b999abf1c110887402542955ff5451dab8f861f9ed" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -445,9 +457,9 @@ dependencies = [ [[package]] name = "atomic-waker" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" +checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" [[package]] name = "atty" @@ -488,7 +500,7 @@ dependencies = [ "cfg-if 1.0.0", "libc", "miniz_oxide", - "object 0.30.2", + "object 0.30.3", "rustc-demangle", ] @@ -540,7 +552,7 @@ dependencies = [ [[package]] name = "beefy-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -557,15 +569,15 @@ dependencies = [ "sc-network-gossip", "sc-utils", "sp-api", - "sp-application-crypto", - "sp-arithmetic", + "sp-application-crypto 7.0.0", + "sp-arithmetic 6.0.0", "sp-beefy", "sp-blockchain", "sp-consensus", - "sp-core", - "sp-keystore", + "sp-core 7.0.0", + "sp-keystore 0.13.0", "sp-mmr-primitives", - "sp-runtime", + "sp-runtime 7.0.0", "substrate-prometheus-endpoint", "thiserror", "wasm-timer", @@ -574,7 +586,7 @@ dependencies = [ [[package]] name = "beefy-gadget-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "beefy-gadget", "futures", @@ -585,19 +597,18 @@ dependencies = [ "sc-rpc", "serde", "sp-beefy", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", "thiserror", ] [[package]] -name = "beefy-merkle-tree" +name = "binary-merkle-tree" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "sp-api", - "sp-beefy", - "sp-runtime", + "hash-db", + "log", ] [[package]] @@ -621,7 +632,7 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "regex", "rustc-hash", @@ -760,6 +771,18 @@ dependencies = [ "futures-lite", ] +[[package]] +name = "bounded-collections" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de2aff4807e40f478132150d80b031f2461d88f061851afcab537d7600c24120" +dependencies = [ + "log", + "parity-scale-codec", + "scale-info", + "serde", +] + [[package]] name = "bounded-vec" version = "0.6.0" @@ -773,7 +796,7 @@ dependencies = [ name = "bp-beefy" version = "0.1.0" dependencies = [ - "beefy-merkle-tree", + "binary-merkle-tree", "bp-runtime", "frame-support", "pallet-beefy-mmr", @@ -782,8 +805,8 @@ dependencies = [ "scale-info", "serde", "sp-beefy", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] @@ -796,6 +819,7 @@ dependencies = [ "frame-system", "polkadot-primitives", "sp-api", + "sp-std 5.0.0", ] [[package]] @@ -807,7 +831,7 @@ dependencies = [ "bp-runtime", "frame-support", "sp-api", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -819,7 +843,7 @@ dependencies = [ "bp-runtime", "frame-support", "sp-api", - "sp-std", + "sp-std 5.0.0", ] [[package]] @@ -835,11 +859,11 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", + "sp-core 7.0.0", "sp-finality-grandpa", - "sp-runtime", - "sp-std", - "sp-trie", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "sp-trie 7.0.0", ] [[package]] @@ -862,8 +886,8 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-std", + "sp-core 7.0.0", + "sp-std 5.0.0", ] [[package]] @@ -883,11 +907,11 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "sp-trie", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "sp-trie 7.0.0", ] [[package]] @@ -901,9 +925,9 @@ dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-core", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] @@ -928,9 +952,9 @@ dependencies = [ "parity-util-mem", "scale-info", "serde", - "sp-core", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] @@ -942,8 +966,8 @@ dependencies = [ "frame-support", "hex", "hex-literal", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] @@ -955,9 +979,9 @@ dependencies = [ "frame-support", "frame-system", "sp-api", - "sp-core", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] @@ -970,9 +994,9 @@ dependencies = [ "frame-support", "frame-system", "sp-api", - "sp-core", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] @@ -998,13 +1022,13 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-state-machine", - "sp-std", - "sp-trie", - "trie-db", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "sp-std 5.0.0", + "sp-trie 7.0.0", + "trie-db 0.25.0", ] [[package]] @@ -1015,10 +1039,10 @@ dependencies = [ "ed25519-dalek", "finality-grandpa", "parity-scale-codec", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-finality-grandpa", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] @@ -1066,11 +1090,11 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-api", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "sp-trie", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "sp-trie 7.0.0", "static_assertions", "xcm", "xcm-builder", @@ -1085,9 +1109,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" +checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832" dependencies = [ "memchr", "serde", @@ -1104,9 +1128,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byte-slice-cast" @@ -1120,6 +1144,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytemuck" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" + [[package]] name = "byteorder" version = "1.4.3" @@ -1128,9 +1158,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "bzip2-sys" @@ -1182,9 +1212,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" dependencies = [ "jobserver", ] @@ -1353,9 +1383,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.1" +version = "4.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec7a4128863c188deefe750ac1d1dfe66c236909f845af04beed823638dc1b2" +checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" dependencies = [ "bitflags", "clap_derive", @@ -1372,9 +1402,9 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" dependencies = [ - "heck 0.4.0", + "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -1423,9 +1453,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b" +checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" dependencies = [ "crossbeam-utils", ] @@ -1613,9 +1643,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" dependencies = [ "crc-catalog", ] @@ -1777,22 +1807,22 @@ dependencies = [ [[package]] name = "cumulus-client-cli" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ - "clap 4.1.1", + "clap 4.1.4", "parity-scale-codec", "sc-chain-spec", "sc-cli", "sc-service", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", "url", ] [[package]] name = "cumulus-client-collator" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "cumulus-client-consensus-common", "cumulus-client-network", @@ -1807,15 +1837,15 @@ dependencies = [ "sc-client-api", "sp-api", "sp-consensus", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", "tracing", ] [[package]] name = "cumulus-client-consensus-aura" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "async-trait", "cumulus-client-consensus-common", @@ -1828,15 +1858,15 @@ dependencies = [ "sc-consensus-slots", "sc-telemetry", "sp-api", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-block-builder", "sp-blockchain", "sp-consensus", "sp-consensus-aura", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-keystore", - "sp-runtime", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", "substrate-prometheus-endpoint", "tracing", ] @@ -1844,7 +1874,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-common" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "async-trait", "cumulus-client-pov-recovery", @@ -1859,15 +1889,15 @@ dependencies = [ "sc-consensus", "sp-blockchain", "sp-consensus", - "sp-runtime", - "sp-trie", + "sp-runtime 7.0.0", + "sp-trie 7.0.0", "tracing", ] [[package]] name = "cumulus-client-network" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "async-trait", "cumulus-relay-chain-interface", @@ -1881,16 +1911,16 @@ dependencies = [ "sc-client-api", "sp-blockchain", "sp-consensus", - "sp-core", - "sp-runtime", - "sp-state-machine", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", "tracing", ] [[package]] name = "cumulus-client-pov-recovery" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "cumulus-primitives-core", "cumulus-relay-chain-interface", @@ -1906,14 +1936,14 @@ dependencies = [ "sc-consensus", "sp-consensus", "sp-maybe-compressed-blob", - "sp-runtime", + "sp-runtime 7.0.0", "tracing", ] [[package]] name = "cumulus-client-service" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "cumulus-client-cli", "cumulus-client-collator", @@ -1934,30 +1964,30 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", ] [[package]] name = "cumulus-pallet-aura-ext" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "frame-support", "frame-system", "pallet-aura", "parity-scale-codec", "scale-info", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-consensus-aura", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "cumulus-pallet-dmp-queue" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -1965,16 +1995,16 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-io", - "sp-runtime", - "sp-std", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", "xcm", ] [[package]] name = "cumulus-pallet-parachain-system" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "bytes", "cumulus-pallet-parachain-system-proc-macro", @@ -1988,14 +2018,14 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain", "scale-info", - "sp-core", - "sp-externalities", + "sp-core 7.0.0", + "sp-externalities 0.13.0", "sp-inherents", - "sp-io", - "sp-runtime", - "sp-state-machine", - "sp-std", - "sp-trie", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "sp-std 5.0.0", + "sp-trie 7.0.0", "sp-version", "xcm", ] @@ -2003,10 +2033,10 @@ dependencies = [ [[package]] name = "cumulus-pallet-parachain-system-proc-macro" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -2014,23 +2044,23 @@ dependencies = [ [[package]] name = "cumulus-pallet-xcm" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "cumulus-primitives-core", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-io", - "sp-runtime", - "sp-std", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", "xcm", ] [[package]] name = "cumulus-pallet-xcmp-queue" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -2040,9 +2070,9 @@ dependencies = [ "polkadot-runtime-common", "rand_chacha 0.3.1", "scale-info", - "sp-io", - "sp-runtime", - "sp-std", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", "xcm", "xcm-executor", ] @@ -2050,23 +2080,23 @@ dependencies = [ [[package]] name = "cumulus-primitives-core" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "parity-scale-codec", "polkadot-core-primitives", "polkadot-parachain", "polkadot-primitives", "sp-api", - "sp-runtime", - "sp-std", - "sp-trie", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "sp-trie 7.0.0", "xcm", ] [[package]] name = "cumulus-primitives-parachain-inherent" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2076,33 +2106,33 @@ dependencies = [ "sc-client-api", "scale-info", "sp-api", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-runtime", - "sp-state-machine", - "sp-std", - "sp-storage", - "sp-trie", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "sp-std 5.0.0", + "sp-storage 7.0.0", + "sp-trie 7.0.0", "tracing", ] [[package]] name = "cumulus-primitives-timestamp" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "cumulus-primitives-core", "futures", "parity-scale-codec", "sp-inherents", - "sp-std", + "sp-std 5.0.0", "sp-timestamp", ] [[package]] name = "cumulus-relay-chain-inprocess-interface" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2119,15 +2149,15 @@ dependencies = [ "sc-tracing", "sp-api", "sp-consensus", - "sp-core", - "sp-runtime", - "sp-state-machine", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", ] [[package]] name = "cumulus-relay-chain-interface" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2139,7 +2169,7 @@ dependencies = [ "sc-client-api", "sp-api", "sp-blockchain", - "sp-state-machine", + "sp-state-machine 0.13.0", "thiserror", "tokio", ] @@ -2147,7 +2177,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-minimal-node" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "array-bytes 6.0.0", "async-trait", @@ -2155,7 +2185,7 @@ dependencies = [ "cumulus-relay-chain-interface", "cumulus-relay-chain-rpc-interface", "futures", - "lru", + "lru 0.9.0", "polkadot-core-primitives", "polkadot-network-bridge", "polkadot-node-network-protocol", @@ -2178,7 +2208,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-babe", - "sp-runtime", + "sp-runtime 7.0.0", "tokio", "tracing", "url", @@ -2187,7 +2217,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-rpc-interface" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2195,7 +2225,7 @@ dependencies = [ "futures", "futures-timer", "jsonrpsee 0.16.2", - "lru", + "lru 0.9.0", "parity-scale-codec", "polkadot-service", "sc-client-api", @@ -2205,9 +2235,9 @@ dependencies = [ "sp-api", "sp-authority-discovery", "sp-consensus-babe", - "sp-core", - "sp-state-machine", - "sp-storage", + "sp-core 7.0.0", + "sp-state-machine 0.13.0", + "sp-storage 7.0.0", "tokio", "tracing", "url", @@ -2216,14 +2246,14 @@ dependencies = [ [[package]] name = "cumulus-test-relay-sproof-builder" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "cumulus-primitives-core", "parity-scale-codec", "polkadot-primitives", - "sp-runtime", - "sp-state-machine", - "sp-std", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "sp-std 5.0.0", ] [[package]] @@ -2285,9 +2315,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0-pre.5" +version = "4.0.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67bc65846be335cb20f4e52d49a437b773a2c1fdb42b19fc84e79e6f6771536f" +checksum = "8da00a7a9a4eb92a0a0f8e75660926d48f0d0f3c537e455c457bcdaa1e16b1ac" dependencies = [ "cfg-if 1.0.0", "fiat-crypto", @@ -2299,9 +2329,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" +checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" dependencies = [ "cc", "cxxbridge-flags", @@ -2311,14 +2341,14 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70" +checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "scratch", "syn 1.0.107", @@ -2326,17 +2356,17 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" +checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" [[package]] name = "cxxbridge-macro" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" +checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -2359,7 +2389,7 @@ checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "strsim 0.10.0", "syn 1.0.107", @@ -2447,7 +2477,18 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "derive-syn-parse" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" +dependencies = [ + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -2468,7 +2509,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" dependencies = [ "darling", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -2490,7 +2531,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "rustc_version 0.4.0", "syn 1.0.107", @@ -2578,7 +2619,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -2617,7 +2658,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -2670,7 +2711,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ "curve25519-dalek 3.2.0", - "hashbrown", + "hashbrown 0.12.3", "hex", "rand_core 0.6.4", "sha2 0.9.9", @@ -2679,9 +2720,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" @@ -2707,9 +2748,9 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if 1.0.0", ] @@ -2720,8 +2761,8 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ - "heck 0.4.0", - "proc-macro2 1.0.49", + "heck 0.4.1", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -2741,7 +2782,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -2752,7 +2793,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e88bcb3a067a6555d577aba299e75eff9942da276e6506fc6274327daa026132" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -2860,7 +2901,7 @@ checksum = "a718c0675c555c5f976fff4ea9e2c150fa06cefa201cadef87cfbf9324075881" dependencies = [ "blake3", "fs-err", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", ] @@ -2872,7 +2913,7 @@ checksum = "3774182a5df13c3d1690311ad32fbe913feef26baba609fa2dd5f72042bd2ab6" dependencies = [ "blake2", "fs-err", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -2917,7 +2958,7 @@ dependencies = [ "expander 0.0.4", "indexmap", "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", "thiserror", @@ -3048,7 +3089,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "parity-scale-codec", ] @@ -3071,9 +3112,10 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", + "frame-support-procedural", "frame-system", "linregress", "log", @@ -3082,24 +3124,25 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-application-crypto", - "sp-core", - "sp-io", - "sp-runtime", - "sp-runtime-interface", - "sp-std", - "sp-storage", + "sp-application-crypto 7.0.0", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-runtime-interface 7.0.0", + "sp-std 5.0.0", + "sp-storage 7.0.0", + "static_assertions", ] [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "Inflector", "array-bytes 4.2.0", "chrono", - "clap 4.1.1", + "clap 4.1.4", "comfy-table", "frame-benchmarking", "frame-support", @@ -3124,16 +3167,16 @@ dependencies = [ "serde_json", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-database", - "sp-externalities", + "sp-externalities 0.13.0", "sp-inherents", - "sp-keystore", - "sp-runtime", - "sp-state-machine", - "sp-std", - "sp-storage", - "sp-trie", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "sp-std 5.0.0", + "sp-storage 7.0.0", + "sp-trie 7.0.0", "thiserror", "thousands", ] @@ -3141,10 +3184,10 @@ dependencies = [ [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -3152,34 +3195,34 @@ dependencies = [ [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-election-provider-solution-type", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-arithmetic", - "sp-core", + "sp-arithmetic 6.0.0", + "sp-core 7.0.0", "sp-npos-elections", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "sp-tracing", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "sp-tracing 6.0.0", ] [[package]] @@ -3197,15 +3240,15 @@ dependencies = [ [[package]] name = "frame-remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "futures", "log", "parity-scale-codec", "serde", - "sp-core", - "sp-io", - "sp-runtime", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", "substrate-rpc-client", "tokio", ] @@ -3213,7 +3256,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "bitflags", "frame-metadata", @@ -3228,30 +3271,31 @@ dependencies = [ "serde", "smallvec", "sp-api", - "sp-arithmetic", - "sp-core", + "sp-arithmetic 6.0.0", + "sp-core 7.0.0", "sp-core-hashing-proc-macro", "sp-inherents", - "sp-io", - "sp-runtime", + "sp-io 7.0.0", + "sp-runtime 7.0.0", "sp-staking", - "sp-state-machine", - "sp-std", - "sp-tracing", - "sp-weights", + "sp-state-machine 0.13.0", + "sp-std 5.0.0", + "sp-tracing 6.0.0", + "sp-weights 4.0.0", "tt-call", ] [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "Inflector", "cfg-expr", + "derive-syn-parse", "frame-support-procedural-tools", "itertools", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -3259,11 +3303,11 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -3271,9 +3315,9 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -3281,40 +3325,40 @@ dependencies = [ [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", "log", "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", "sp-version", - "sp-weights", + "sp-weights 4.0.0", ] [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-core", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "parity-scale-codec", "sp-api", @@ -3323,13 +3367,13 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", "parity-scale-codec", "sp-api", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] @@ -3348,12 +3392,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "fs_extra" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" - [[package]] name = "funty" version = "2.0.0" @@ -3362,9 +3400,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" dependencies = [ "futures-channel", "futures-core", @@ -3377,9 +3415,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", "futures-sink", @@ -3387,15 +3425,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" dependencies = [ "futures-core", "futures-task", @@ -3405,9 +3443,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-lite" @@ -3426,11 +3464,11 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -3448,15 +3486,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-timer" @@ -3466,9 +3504,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-channel", "futures-core", @@ -3538,8 +3576,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if 1.0.0", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -3575,9 +3615,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" +checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec" [[package]] name = "glob" @@ -3600,9 +3640,9 @@ dependencies = [ [[package]] name = "gloo-timers" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c4a8d6391675c6b2ee1a6c8d06e8e2d03605c44cec1270675985a4c2a5500b" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" dependencies = [ "futures-channel", "futures-core", @@ -3675,7 +3715,16 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", ] [[package]] @@ -3689,9 +3738,9 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -3711,6 +3760,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" + [[package]] name = "hex" version = "0.4.3" @@ -3853,9 +3908,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" dependencies = [ "bytes", "futures-channel", @@ -3888,6 +3943,7 @@ dependencies = [ "rustls-native-certs", "tokio", "tokio-rustls", + "webpki-roots", ] [[package]] @@ -4003,7 +4059,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -4015,7 +4071,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", "serde", ] @@ -4070,12 +4126,12 @@ checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" [[package]] name = "io-lifetimes" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" dependencies = [ "libc", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -4104,14 +4160,14 @@ checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "is-terminal" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" dependencies = [ - "hermit-abi 0.2.6", - "io-lifetimes 1.0.4", - "rustix 0.36.6", - "windows-sys 0.42.0", + "hermit-abi 0.3.0", + "io-lifetimes 1.0.5", + "rustix 0.36.8", + "windows-sys 0.45.0", ] [[package]] @@ -4167,9 +4223,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -4204,7 +4260,9 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" dependencies = [ + "jsonrpsee-client-transport 0.16.2", "jsonrpsee-core 0.16.2", + "jsonrpsee-http-client", "jsonrpsee-proc-macros 0.16.2", "jsonrpsee-server", "jsonrpsee-types 0.16.2", @@ -4305,6 +4363,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "jsonrpsee-http-client" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls", + "jsonrpsee-core 0.16.2", + "jsonrpsee-types 0.16.2", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "jsonrpsee-proc-macros" version = "0.15.1" @@ -4312,7 +4389,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd67957d4280217247588ac86614ead007b301ca2fa9f19c19f880a536f029e3" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -4323,9 +4400,9 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baa6da1e4199c10d7b1d0a6e5e8bd8e55f351163b6f4b3cbb044672a69bd4c1c" dependencies = [ - "heck 0.4.0", + "heck 0.4.1", "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -4427,8 +4504,8 @@ dependencies = [ [[package]] name = "kusama-runtime" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "bitvec", "frame-election-provider-support", @@ -4490,21 +4567,21 @@ dependencies = [ "serde_derive", "smallvec", "sp-api", - "sp-arithmetic", + "sp-arithmetic 6.0.0", "sp-authority-discovery", "sp-beefy", "sp-block-builder", "sp-consensus-babe", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-io", + "sp-io 7.0.0", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", - "sp-runtime", + "sp-runtime 7.0.0", "sp-session", "sp-staking", - "sp-std", + "sp-std 5.0.0", "sp-transaction-pool", "sp-version", "static_assertions", @@ -4516,16 +4593,16 @@ dependencies = [ [[package]] name = "kusama-runtime-constants" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "frame-support", "polkadot-primitives", "polkadot-runtime-common", "smallvec", - "sp-core", - "sp-runtime", - "sp-weights", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-weights 4.0.0", ] [[package]] @@ -4714,7 +4791,7 @@ dependencies = [ "libp2p-core", "libp2p-swarm", "log", - "lru", + "lru 0.8.1", "prost", "prost-build", "prost-codec", @@ -4909,7 +4986,7 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d527d5827582abd44a6d80c07ff8b50b4ee238a8979e05998474179e79dc400" dependencies = [ - "heck 0.4.0", + "heck 0.4.1", "quote 1.0.23", "syn 1.0.107", ] @@ -5127,12 +5204,11 @@ dependencies = [ [[package]] name = "linregress" -version = "0.4.4" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c601a85f5ecd1aba625247bca0031585fb1c446461b142878a16f8245ddeb8" +checksum = "475015a7f8f017edb28d2e69813be23500ad4b32cfe3421c4148efc97324ee52" dependencies = [ "nalgebra", - "statrs", ] [[package]] @@ -5173,7 +5249,16 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" dependencies = [ - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "lru" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e7d46de488603ffdd5f30afbc64fbba2378214a2c3a2fb83abf3d33126df17" +dependencies = [ + "hashbrown 0.13.2", ] [[package]] @@ -5231,9 +5316,9 @@ dependencies = [ [[package]] name = "matches" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matrixmultiply" @@ -5265,7 +5350,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" dependencies = [ - "rustix 0.36.6", + "rustix 0.36.8", ] [[package]] @@ -5302,7 +5387,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" dependencies = [ "hash-db", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -5337,7 +5422,7 @@ dependencies = [ "num-traits", "parking_lot 0.12.1", "relay-utils", - "sp-arithmetic", + "sp-arithmetic 6.0.0", ] [[package]] @@ -5357,7 +5442,7 @@ version = "0.1.0" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", - "clap 4.1.1", + "clap 4.1.4", "frame-benchmarking", "frame-benchmarking-cli", "jsonrpsee 0.16.2", @@ -5381,9 +5466,9 @@ dependencies = [ "serde_json", "sp-beefy", "sp-consensus-aura", - "sp-core", + "sp-core 7.0.0", "sp-finality-grandpa", - "sp-runtime", + "sp-runtime 7.0.0", "sp-timestamp", "substrate-build-script-utils", "substrate-frame-rpc-system", @@ -5435,13 +5520,13 @@ dependencies = [ "sp-beefy", "sp-block-builder", "sp-consensus-aura", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-io", + "sp-io 7.0.0", "sp-offchain", - "sp-runtime", + "sp-runtime 7.0.0", "sp-session", - "sp-std", + "sp-std 5.0.0", "sp-transaction-pool", "sp-version", "static_assertions", @@ -5487,7 +5572,7 @@ dependencies = [ [[package]] name = "mmr-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "futures", "log", @@ -5498,15 +5583,15 @@ dependencies = [ "sp-beefy", "sp-blockchain", "sp-consensus", - "sp-core", + "sp-core 7.0.0", "sp-mmr-primitives", - "sp-runtime", + "sp-runtime 7.0.0", ] [[package]] name = "mmr-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "anyhow", "jsonrpsee 0.16.2", @@ -5514,9 +5599,9 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-mmr-primitives", - "sp-runtime", + "sp-runtime 7.0.0", ] [[package]] @@ -5541,7 +5626,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "832663583d5fa284ca8810bf7015e46c9fff9622d3cf34bd1eea5003fec06dd0" dependencies = [ "cfg-if 1.0.0", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -5600,7 +5685,7 @@ checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" dependencies = [ "proc-macro-crate", "proc-macro-error", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", "synstructure", @@ -5628,9 +5713,9 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.27.1" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "462fffe4002f4f2e1f6a9dcf12cc1a6fc0e15989014efc02a941d3e0f5dc2120" +checksum = "f6515c882ebfddccaa73ead7320ca28036c4bc84c9bcca3cc0cbba8efe89223a" dependencies = [ "approx", "matrixmultiply", @@ -5638,19 +5723,17 @@ dependencies = [ "num-complex", "num-rational", "num-traits", - "rand 0.8.5", - "rand_distr", "simba", "typenum", ] [[package]] name = "nalgebra-macros" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218" +checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -5698,9 +5781,9 @@ dependencies = [ [[package]] name = "netlink-packet-utils" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25af9cf0dc55498b7bd94a1508af7a78706aa0ab715a73c5169273e03c84845e" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" dependencies = [ "anyhow", "byteorder", @@ -5725,9 +5808,9 @@ dependencies = [ [[package]] name = "netlink-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b654097027250401127914afb37cb1f311df6610a9891ff07a757e94199027" +checksum = "260e21fbb6f3d253a14df90eb0000a6066780a15dd901a7519ce02d77a94985b" dependencies = [ "bytes", "futures", @@ -5748,20 +5831,34 @@ dependencies = [ "memoffset 0.6.5", ] +[[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "libc", + "memoffset 0.7.1", + "pin-utils", + "static_assertions", +] + [[package]] name = "node-inspect" version = "0.9.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "clap 4.1.1", + "clap 4.1.4", "parity-scale-codec", "sc-cli", "sc-client-api", "sc-executor", "sc-service", "sp-blockchain", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", "thiserror", ] @@ -5773,14 +5870,23 @@ checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "nom" -version = "7.1.2" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5507769c4919c998e69e49c839d9dc6e693ede4cc4290d6ad8b41d4f09c548c" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -5809,9 +5915,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" dependencies = [ "num-traits", ] @@ -5855,7 +5961,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", - "libm 0.2.6", ] [[package]] @@ -5884,16 +5989,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ "crc32fast", - "hashbrown", + "hashbrown 0.12.3", "indexmap", "memchr", ] [[package]] name = "object" -version = "0.30.2" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b8c786513eb403643f2a88c244c2aaa270ef2153f55094587d0c48a3cf22a83" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] @@ -5980,7 +6085,7 @@ dependencies = [ "itertools", "petgraph", "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -6035,54 +6140,53 @@ dependencies = [ [[package]] name = "pallet-aura" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", "frame-system", "pallet-timestamp", "parity-scale-codec", "scale-info", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-consensus-aura", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", "frame-system", "pallet-session", "parity-scale-codec", "scale-info", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-authority-discovery", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", "frame-system", "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-authorship", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6093,20 +6197,20 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "scale-info", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-consensus-babe", "sp-consensus-vrf", - "sp-io", - "sp-runtime", + "sp-io 7.0.0", + "sp-runtime 7.0.0", "sp-session", "sp-staking", - "sp-std", + "sp-std 5.0.0", ] [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6116,17 +6220,17 @@ dependencies = [ "pallet-balances", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "sp-tracing", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "sp-tracing 6.0.0", ] [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6134,14 +6238,14 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", "frame-system", @@ -6150,17 +6254,17 @@ dependencies = [ "scale-info", "serde", "sp-beefy", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-beefy-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "array-bytes 4.2.0", - "beefy-merkle-tree", + "binary-merkle-tree", "frame-support", "frame-system", "log", @@ -6170,17 +6274,18 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", + "sp-api", "sp-beefy", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6189,10 +6294,10 @@ dependencies = [ "pallet-treasury", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] @@ -6213,10 +6318,10 @@ dependencies = [ "scale-info", "serde", "sp-beefy", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] @@ -6233,12 +6338,12 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 7.0.0", "sp-finality-grandpa", - "sp-io", - "sp-runtime", - "sp-std", - "sp-trie", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "sp-trie 7.0.0", ] [[package]] @@ -6256,10 +6361,10 @@ dependencies = [ "pallet-balances", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] @@ -6278,11 +6383,11 @@ dependencies = [ "pallet-bridge-grandpa", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "sp-trie", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "sp-trie 7.0.0", ] [[package]] @@ -6299,17 +6404,17 @@ dependencies = [ "pallet-balances", "parity-scale-codec", "scale-info", - "sp-arithmetic", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-arithmetic 6.0.0", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6319,16 +6424,16 @@ dependencies = [ "pallet-treasury", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6336,16 +6441,16 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-conviction-voting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "assert_matches", "frame-benchmarking", @@ -6354,15 +6459,15 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-io", - "sp-runtime", - "sp-std", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6371,16 +6476,16 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6391,32 +6496,32 @@ dependencies = [ "parity-scale-codec", "rand 0.8.5", "scale-info", - "sp-arithmetic", - "sp-core", - "sp-io", + "sp-arithmetic 6.0.0", + "sp-core 7.0.0", + "sp-io 7.0.0", "sp-npos-elections", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", "strum", ] [[package]] name = "pallet-election-provider-support-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-election-provider-support", "frame-system", "parity-scale-codec", "sp-npos-elections", - "sp-runtime", + "sp-runtime 7.0.0", ] [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6424,17 +6529,17 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", + "sp-core 7.0.0", + "sp-io 7.0.0", "sp-npos-elections", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-fast-unstake" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6443,16 +6548,16 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-io", - "sp-runtime", + "sp-io 7.0.0", + "sp-runtime 7.0.0", "sp-staking", - "sp-std", + "sp-std 5.0.0", ] [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6462,20 +6567,20 @@ dependencies = [ "pallet-session", "parity-scale-codec", "scale-info", - "sp-application-crypto", - "sp-core", + "sp-application-crypto 7.0.0", + "sp-core 7.0.0", "sp-finality-grandpa", - "sp-io", - "sp-runtime", + "sp-io 7.0.0", + "sp-runtime 7.0.0", "sp-session", "sp-staking", - "sp-std", + "sp-std 5.0.0", ] [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "enumflags2", "frame-benchmarking", @@ -6483,15 +6588,15 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", - "sp-io", - "sp-runtime", - "sp-std", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6500,35 +6605,35 @@ dependencies = [ "pallet-authorship", "parity-scale-codec", "scale-info", - "sp-application-crypto", - "sp-core", - "sp-io", - "sp-runtime", + "sp-application-crypto 7.0.0", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", "sp-staking", - "sp-std", + "sp-std 5.0.0", ] [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", + "sp-core 7.0.0", + "sp-io 7.0.0", "sp-keyring", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6536,33 +6641,33 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", + "sp-core 7.0.0", + "sp-io 7.0.0", "sp-mmr-primitives", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6570,58 +6675,58 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-io", - "sp-runtime", - "sp-std", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-nis" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-arithmetic", - "sp-core", - "sp-runtime", - "sp-std", + "sp-arithmetic 6.0.0", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-nomination-pools" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", "frame-system", "log", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", - "sp-runtime", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", "sp-staking", - "sp-std", + "sp-std 5.0.0", ] [[package]] name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "parity-scale-codec", "sp-api", - "sp-std", + "sp-std 5.0.0", ] [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", "frame-system", @@ -6630,15 +6735,15 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-runtime", + "sp-runtime 7.0.0", "sp-staking", - "sp-std", + "sp-std 5.0.0", ] [[package]] name = "pallet-preimage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6646,45 +6751,45 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-io", - "sp-runtime", - "sp-std", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-randomness-collective-flip" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", "frame-system", "parity-scale-codec", "safe-mix", "scale-info", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-ranked-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6692,32 +6797,32 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-arithmetic", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-arithmetic 6.0.0", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-recovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-io", - "sp-runtime", - "sp-std", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-referenda" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6726,16 +6831,16 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-arithmetic", - "sp-io", - "sp-runtime", - "sp-std", + "sp-arithmetic 6.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6743,16 +6848,16 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-io", - "sp-runtime", - "sp-std", - "sp-weights", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "sp-weights 4.0.0", ] [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", "frame-system", @@ -6761,13 +6866,13 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", - "sp-runtime", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", "sp-session", "sp-staking", - "sp-std", - "sp-trie", + "sp-std 5.0.0", + "sp-trie 7.0.0", ] [[package]] @@ -6779,30 +6884,30 @@ dependencies = [ "pallet-session", "parity-scale-codec", "scale-info", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", "sp-staking", - "sp-std", + "sp-std 5.0.0", ] [[package]] name = "pallet-society" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", "frame-system", "parity-scale-codec", "rand_chacha 0.2.2", "scale-info", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6814,20 +6919,20 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-application-crypto", - "sp-io", - "sp-runtime", + "sp-application-crypto 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", "sp-staking", - "sp-std", + "sp-std 5.0.0", ] [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -6835,30 +6940,30 @@ dependencies = [ [[package]] name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "log", - "sp-arithmetic", + "sp-arithmetic 6.0.0", ] [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-io", - "sp-runtime", - "sp-std", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6867,16 +6972,16 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-inherents", - "sp-io", - "sp-runtime", - "sp-std", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", "sp-timestamp", ] [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6886,60 +6991,60 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-support", "frame-system", "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "jsonrpsee 0.16.2", "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-rpc", - "sp-runtime", - "sp-weights", + "sp-runtime 7.0.0", + "sp-weights 4.0.0", ] [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", "sp-api", - "sp-runtime", - "sp-weights", + "sp-runtime 7.0.0", + "sp-weights 4.0.0", ] [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6949,30 +7054,30 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6980,14 +7085,14 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-whitelist" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-benchmarking", "frame-support", @@ -6995,14 +7100,14 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-api", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "pallet-xcm" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "frame-benchmarking", "frame-support", @@ -7011,10 +7116,10 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", "xcm", "xcm-executor", ] @@ -7022,7 +7127,7 @@ dependencies = [ [[package]] name = "parachain-info" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#3aae882437f6e3e744cf464e8272ae0fe2f8fc28" +source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -7044,14 +7149,14 @@ dependencies = [ "parity-scale-codec", "relay-substrate-client", "relay-utils", - "sp-core", + "sp-core 7.0.0", ] [[package]] name = "parity-db" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a7511a0bec4a336b5929999d02b560d2439c993cccf98c26481484e811adc43" +checksum = "dd684a725651d9588ef21f140a328b6b4f64e646b2e931f3e6f14f75eedf9980" dependencies = [ "blake2", "crc32fast", @@ -7068,9 +7173,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366e44391a8af4cfd6002ef6ba072bae071a96aafca98d7d448a34c5dca38b6a" +checksum = "c3840933452adf7b3b9145e27086a5a3376c619dca1a21b1e5a5af0d54979bed" dependencies = [ "arrayvec 0.7.2", "bitvec", @@ -7083,12 +7188,12 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.3" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9299338969a3d2f491d65f140b00ddec470858402f888af98e8642fb5e8965cd" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -7107,9 +7212,9 @@ checksum = "0d32c34f4f5ca7f9196001c0aba5a1f9a5a12382c8944b8b0f90233282d1e8f8" dependencies = [ "cfg-if 1.0.0", "ethereum-types", - "hashbrown", + "hashbrown 0.12.3", "impl-trait-for-tuples", - "lru", + "lru 0.8.1", "parity-util-mem-derive", "parking_lot 0.12.1", "primitive-types", @@ -7123,7 +7228,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "syn 1.0.107", "synstructure", ] @@ -7158,7 +7263,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.6", + "parking_lot_core 0.9.7", ] [[package]] @@ -7177,15 +7282,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall", "smallvec", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -7244,9 +7349,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4257b4a04d91f7e9e6290be5d3da4804dd5784fafde3a497d73eb2b4a158c30a" +checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" dependencies = [ "thiserror", "ucd-trie", @@ -7254,9 +7359,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241cda393b0cdd65e62e07e12454f1f25d57017dcc514b1514cd3c4645e3a0a6" +checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea" dependencies = [ "pest", "pest_generator", @@ -7264,22 +7369,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46b53634d8c8196302953c74d5352f33d0c512a9499bd2ce468fc9f4128fa27c" +checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] [[package]] name = "pest_meta" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef4f1332a8d4678b41966bb4cc1d0676880e84183a1ecc3f4b69f03e99c7a51" +checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d" dependencies = [ "once_cell", "pest", @@ -7311,7 +7416,7 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -7364,8 +7469,8 @@ checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" [[package]] name = "polkadot-approval-distribution" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "futures", "polkadot-node-metrics", @@ -7379,8 +7484,8 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "futures", "polkadot-node-network-protocol", @@ -7393,13 +7498,13 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "derive_more", "fatality", "futures", - "lru", + "lru 0.9.0", "parity-scale-codec", "polkadot-erasure-coding", "polkadot-node-network-protocol", @@ -7408,20 +7513,20 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "rand 0.8.5", - "sp-core", - "sp-keystore", + "sp-core 7.0.0", + "sp-keystore 0.13.0", "thiserror", "tracing-gum", ] [[package]] name = "polkadot-availability-recovery" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "fatality", "futures", - "lru", + "lru 0.9.0", "parity-scale-codec", "polkadot-erasure-coding", "polkadot-node-network-protocol", @@ -7437,10 +7542,10 @@ dependencies = [ [[package]] name = "polkadot-cli" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ - "clap 4.1.1", + "clap 4.1.4", "frame-benchmarking-cli", "futures", "log", @@ -7452,10 +7557,11 @@ dependencies = [ "sc-cli", "sc-executor", "sc-service", + "sc-storage-monitor", "sc-sysinfo", "sc-tracing", - "sp-core", - "sp-io", + "sp-core 7.0.0", + "sp-io 7.0.0", "sp-keyring", "substrate-build-script-utils", "thiserror", @@ -7464,8 +7570,8 @@ dependencies = [ [[package]] name = "polkadot-client" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "async-trait", "frame-benchmarking", @@ -7491,23 +7597,23 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-babe", - "sp-core", + "sp-core 7.0.0", "sp-finality-grandpa", "sp-inherents", "sp-keyring", "sp-mmr-primitives", "sp-offchain", - "sp-runtime", + "sp-runtime 7.0.0", "sp-session", - "sp-storage", + "sp-storage 7.0.0", "sp-timestamp", "sp-transaction-pool", ] [[package]] name = "polkadot-collator-protocol" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "always-assert", "bitvec", @@ -7519,36 +7625,36 @@ dependencies = [ "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives", - "sp-core", - "sp-keystore", - "sp-runtime", + "sp-core 7.0.0", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", "thiserror", "tracing-gum", ] [[package]] name = "polkadot-core-primitives" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "parity-scale-codec", "scale-info", - "sp-core", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "polkadot-dispute-distribution" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "derive_more", "fatality", "futures", "futures-timer", "indexmap", - "lru", + "lru 0.9.0", "parity-scale-codec", "polkadot-erasure-coding", "polkadot-node-network-protocol", @@ -7557,30 +7663,30 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "sc-network", - "sp-application-crypto", - "sp-keystore", + "sp-application-crypto 7.0.0", + "sp-keystore 0.13.0", "thiserror", "tracing-gum", ] [[package]] name = "polkadot-erasure-coding" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", "polkadot-primitives", "reed-solomon-novelpoly", - "sp-core", - "sp-trie", + "sp-core 7.0.0", + "sp-trie 7.0.0", "thiserror", ] [[package]] name = "polkadot-gossip-support" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "futures", "futures-timer", @@ -7591,16 +7697,16 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "sc-network", - "sp-application-crypto", - "sp-core", - "sp-keystore", + "sp-application-crypto 7.0.0", + "sp-core 7.0.0", + "sp-keystore 0.13.0", "tracing-gum", ] [[package]] name = "polkadot-network-bridge" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "always-assert", "async-trait", @@ -7623,8 +7729,8 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "futures", "parity-scale-codec", @@ -7633,7 +7739,7 @@ dependencies = [ "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives", - "sp-core", + "sp-core 7.0.0", "sp-maybe-compressed-blob", "thiserror", "tracing-gum", @@ -7641,15 +7747,15 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "bitvec", "derive_more", "futures", "futures-timer", "kvdb", - "lru", + "lru 0.9.0", "merlin", "parity-scale-codec", "polkadot-node-jaeger", @@ -7660,18 +7766,18 @@ dependencies = [ "polkadot-primitives", "sc-keystore", "schnorrkel", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-consensus", "sp-consensus-slots", - "sp-runtime", + "sp-runtime 7.0.0", "thiserror", "tracing-gum", ] [[package]] name = "polkadot-node-core-av-store" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "bitvec", "futures", @@ -7690,8 +7796,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "bitvec", "fatality", @@ -7702,21 +7808,21 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-statement-table", - "sp-keystore", + "sp-keystore 0.13.0", "thiserror", "tracing-gum", ] [[package]] name = "polkadot-node-core-bitfield-signing" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "futures", "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives", - "sp-keystore", + "sp-keystore 0.13.0", "thiserror", "tracing-gum", "wasm-timer", @@ -7724,8 +7830,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "async-trait", "futures", @@ -7743,8 +7849,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "futures", "polkadot-node-metrics", @@ -7758,8 +7864,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "futures", "futures-timer", @@ -7775,13 +7881,13 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "fatality", "futures", "kvdb", - "lru", + "lru 0.9.0", "parity-scale-codec", "polkadot-node-primitives", "polkadot-node-subsystem", @@ -7794,8 +7900,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "async-trait", "futures", @@ -7811,8 +7917,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "bitvec", "fatality", @@ -7829,14 +7935,15 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "always-assert", "assert_matches", "cpu-time", "futures", "futures-timer", + "libc", "parity-scale-codec", "pin-project", "polkadot-core-primitives", @@ -7848,21 +7955,22 @@ dependencies = [ "sc-executor-common", "sc-executor-wasmtime", "slotmap", - "sp-core", - "sp-externalities", - "sp-io", + "sp-core 7.0.0", + "sp-externalities 0.13.0", + "sp-io 7.0.0", "sp-maybe-compressed-blob", - "sp-tracing", - "sp-wasm-interface", + "sp-tracing 6.0.0", + "sp-wasm-interface 7.0.0", "tempfile", + "tikv-jemalloc-ctl", "tokio", "tracing-gum", ] [[package]] name = "polkadot-node-core-pvf-checker" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "futures", "polkadot-node-primitives", @@ -7870,18 +7978,18 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-primitives", - "sp-keystore", + "sp-keystore 0.13.0", "thiserror", "tracing-gum", ] [[package]] name = "polkadot-node-core-runtime-api" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "futures", - "lru", + "lru 0.9.0", "polkadot-node-metrics", "polkadot-node-subsystem", "polkadot-node-subsystem-types", @@ -7892,8 +8000,8 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "lazy_static", "log", @@ -7903,15 +8011,15 @@ dependencies = [ "polkadot-node-primitives", "polkadot-primitives", "sc-network", - "sp-core", + "sp-core 7.0.0", "thiserror", "tokio", ] [[package]] name = "polkadot-node-metrics" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "bs58", "futures", @@ -7929,8 +8037,8 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "async-trait", "derive_more", @@ -7952,8 +8060,8 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "bounded-vec", "futures", @@ -7962,11 +8070,11 @@ dependencies = [ "polkadot-primitives", "schnorrkel", "serde", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-consensus-babe", "sp-consensus-vrf", - "sp-core", - "sp-keystore", + "sp-core 7.0.0", + "sp-keystore 0.13.0", "sp-maybe-compressed-blob", "thiserror", "zstd", @@ -7974,8 +8082,8 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -7984,8 +8092,8 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "async-trait", "derive_more", @@ -8007,8 +8115,8 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "async-trait", "derive_more", @@ -8017,7 +8125,7 @@ dependencies = [ "futures-channel", "itertools", "kvdb", - "lru", + "lru 0.9.0", "parity-db", "parity-scale-codec", "parking_lot 0.11.2", @@ -8031,22 +8139,22 @@ dependencies = [ "polkadot-primitives", "prioritized-metered-channel", "rand 0.8.5", - "sp-application-crypto", - "sp-core", - "sp-keystore", + "sp-application-crypto 7.0.0", + "sp-core 7.0.0", + "sp-keystore 0.13.0", "thiserror", "tracing-gum", ] [[package]] name = "polkadot-overseer" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "async-trait", "futures", "futures-timer", - "lru", + "lru 0.9.0", "orchestra", "parking_lot 0.12.1", "polkadot-node-metrics", @@ -8056,15 +8164,15 @@ dependencies = [ "polkadot-primitives", "sc-client-api", "sp-api", - "sp-core", + "sp-core 7.0.0", "tikv-jemalloc-ctl", "tracing-gum", ] [[package]] name = "polkadot-parachain" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "derive_more", "frame-support", @@ -8072,15 +8180,15 @@ dependencies = [ "polkadot-core-primitives", "scale-info", "serde", - "sp-core", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "polkadot-performance-test" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "env_logger 0.9.3", "kusama-runtime", @@ -8094,8 +8202,8 @@ dependencies = [ [[package]] name = "polkadot-primitives" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "bitvec", "hex-literal", @@ -8105,23 +8213,23 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-application-crypto", - "sp-arithmetic", + "sp-application-crypto 7.0.0", + "sp-arithmetic 6.0.0", "sp-authority-discovery", "sp-consensus-slots", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-io", - "sp-keystore", - "sp-runtime", + "sp-io 7.0.0", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", "sp-staking", - "sp-std", + "sp-std 5.0.0", ] [[package]] name = "polkadot-rpc" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", @@ -8144,16 +8252,16 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-babe", - "sp-keystore", - "sp-runtime", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", "substrate-frame-rpc-system", "substrate-state-trie-migration-rpc", ] [[package]] name = "polkadot-runtime" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "bitvec", "frame-election-provider-support", @@ -8213,16 +8321,16 @@ dependencies = [ "sp-beefy", "sp-block-builder", "sp-consensus-babe", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-io", + "sp-io 7.0.0", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", - "sp-runtime", + "sp-runtime 7.0.0", "sp-session", "sp-staking", - "sp-std", + "sp-std 5.0.0", "sp-transaction-pool", "sp-version", "static_assertions", @@ -8234,8 +8342,8 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "bitvec", "frame-election-provider-support", @@ -8249,6 +8357,7 @@ dependencies = [ "pallet-balances", "pallet-beefy-mmr", "pallet-election-provider-multi-phase", + "pallet-fast-unstake", "pallet-session", "pallet-staking", "pallet-staking-reward-fn", @@ -8266,48 +8375,48 @@ dependencies = [ "slot-range-helper", "sp-api", "sp-beefy", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-io", + "sp-io 7.0.0", "sp-npos-elections", - "sp-runtime", + "sp-runtime 7.0.0", "sp-session", "sp-staking", - "sp-std", + "sp-std 5.0.0", "static_assertions", "xcm", ] [[package]] name = "polkadot-runtime-constants" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "frame-support", "polkadot-primitives", "polkadot-runtime-common", "smallvec", - "sp-core", - "sp-runtime", - "sp-weights", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-weights 4.0.0", ] [[package]] name = "polkadot-runtime-metrics" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "bs58", "parity-scale-codec", "polkadot-primitives", - "sp-std", - "sp-tracing", + "sp-std 5.0.0", + "sp-tracing 6.0.0", ] [[package]] name = "polkadot-runtime-parachains" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "bitflags", "bitvec", @@ -8332,32 +8441,34 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-io", - "sp-keystore", - "sp-runtime", + "sp-io 7.0.0", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", "sp-session", "sp-staking", - "sp-std", + "sp-std 5.0.0", "xcm", "xcm-executor", ] [[package]] name = "polkadot-service" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "async-trait", "beefy-gadget", + "frame-benchmarking-cli", "frame-support", "frame-system-rpc-runtime-api", "futures", "hex-literal", "kvdb", "kvdb-rocksdb", - "lru", + "log", + "lru 0.9.0", "mmr-gadget", "pallet-babe", "pallet-im-online", @@ -8428,20 +8539,20 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-babe", - "sp-core", + "sp-core 7.0.0", "sp-finality-grandpa", "sp-inherents", - "sp-io", - "sp-keystore", + "sp-io 7.0.0", + "sp-keystore 0.13.0", "sp-mmr-primitives", "sp-offchain", - "sp-runtime", + "sp-runtime 7.0.0", "sp-session", - "sp-state-machine", - "sp-storage", + "sp-state-machine 0.13.0", + "sp-storage 7.0.0", "sp-timestamp", "sp-transaction-pool", - "sp-trie", + "sp-trie 7.0.0", "substrate-prometheus-endpoint", "thiserror", "tracing-gum", @@ -8449,8 +8560,8 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "arrayvec 0.5.2", "fatality", @@ -8462,7 +8573,7 @@ dependencies = [ "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives", - "sp-keystore", + "sp-keystore 0.13.0", "sp-staking", "thiserror", "tracing-gum", @@ -8470,12 +8581,12 @@ dependencies = [ [[package]] name = "polkadot-statement-table" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "parity-scale-codec", "polkadot-primitives", - "sp-core", + "sp-core 7.0.0", ] [[package]] @@ -8568,7 +8679,7 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "syn 1.0.107", ] @@ -8604,13 +8715,12 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] @@ -8620,7 +8730,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", "version_check", @@ -8632,7 +8742,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "version_check", ] @@ -8648,9 +8758,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] @@ -8687,7 +8797,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -8709,7 +8819,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f8ad728fb08fe212df3c05169e940fbb6d9d16a877ddde14644a983ba2012e" dependencies = [ "bytes", - "heck 0.4.0", + "heck 0.4.1", "itertools", "lazy_static", "log", @@ -8745,7 +8855,7 @@ checksum = "8bda8c0881ea9f722eb9629376db3d0b903b462477c1aafcb0566610ac28ac5d" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -8819,7 +8929,7 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", ] [[package]] @@ -8890,16 +9000,6 @@ dependencies = [ "getrandom 0.2.8", ] -[[package]] -name = "rand_distr" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" -dependencies = [ - "num-traits", - "rand 0.8.5", -] - [[package]] name = "rand_hc" version = "0.2.0" @@ -8936,9 +9036,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -9039,7 +9139,7 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -9096,8 +9196,8 @@ dependencies = [ "parity-scale-codec", "relay-substrate-client", "scale-info", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", ] [[package]] @@ -9116,9 +9216,9 @@ dependencies = [ "parity-scale-codec", "relay-substrate-client", "scale-info", - "sp-core", + "sp-core 7.0.0", "sp-finality-grandpa", - "sp-runtime", + "sp-runtime 7.0.0", ] [[package]] @@ -9128,7 +9228,7 @@ dependencies = [ "bp-kusama", "relay-substrate-client", "relay-utils", - "sp-core", + "sp-core 7.0.0", ] [[package]] @@ -9144,8 +9244,8 @@ dependencies = [ "parity-scale-codec", "relay-substrate-client", "relay-utils", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", ] [[package]] @@ -9155,7 +9255,7 @@ dependencies = [ "bp-polkadot", "relay-substrate-client", "relay-utils", - "sp-core", + "sp-core 7.0.0", ] [[package]] @@ -9171,8 +9271,8 @@ dependencies = [ "relay-substrate-client", "relay-utils", "rialto-runtime", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", ] [[package]] @@ -9188,8 +9288,10 @@ dependencies = [ "parity-scale-codec", "relay-substrate-client", "scale-info", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-weights 4.0.0", + "subxt", ] [[package]] @@ -9199,7 +9301,7 @@ dependencies = [ "bp-rococo", "relay-substrate-client", "relay-utils", - "sp-core", + "sp-core 7.0.0", ] [[package]] @@ -9231,11 +9333,11 @@ dependencies = [ "sc-rpc-api", "sc-transaction-pool-api", "scale-info", - "sp-core", + "sp-core 7.0.0", "sp-rpc", - "sp-runtime", - "sp-std", - "sp-trie", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "sp-trie 7.0.0", "sp-version", "thiserror", "tokio", @@ -9259,7 +9361,7 @@ dependencies = [ "log", "num-traits", "serde_json", - "sp-runtime", + "sp-runtime 7.0.0", "substrate-prometheus-endpoint", "sysinfo", "thiserror", @@ -9274,7 +9376,7 @@ dependencies = [ "bp-westend", "relay-substrate-client", "relay-utils", - "sp-core", + "sp-core 7.0.0", ] [[package]] @@ -9284,7 +9386,7 @@ dependencies = [ "bp-wococo", "relay-substrate-client", "relay-utils", - "sp-core", + "sp-core 7.0.0", ] [[package]] @@ -9321,7 +9423,7 @@ dependencies = [ name = "rialto-bridge-node" version = "0.1.0" dependencies = [ - "clap 4.1.1", + "clap 4.1.4", "frame-benchmarking", "frame-benchmarking-cli", "frame-support", @@ -9338,9 +9440,9 @@ dependencies = [ "sp-authority-discovery", "sp-beefy", "sp-consensus-babe", - "sp-core", + "sp-core 7.0.0", "sp-finality-grandpa", - "sp-runtime", + "sp-runtime 7.0.0", "substrate-build-script-utils", ] @@ -9348,7 +9450,7 @@ dependencies = [ name = "rialto-parachain-collator" version = "0.1.0" dependencies = [ - "clap 4.1.1", + "clap 4.1.4", "cumulus-client-cli", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", @@ -9384,10 +9486,10 @@ dependencies = [ "sp-api", "sp-block-builder", "sp-consensus-aura", - "sp-core", - "sp-keystore", + "sp-core 7.0.0", + "sp-keystore 0.13.0", "sp-offchain", - "sp-runtime", + "sp-runtime 7.0.0", "sp-session", "sp-timestamp", "sp-transaction-pool", @@ -9438,13 +9540,13 @@ dependencies = [ "sp-api", "sp-block-builder", "sp-consensus-aura", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-io", + "sp-io 7.0.0", "sp-offchain", - "sp-runtime", + "sp-runtime 7.0.0", "sp-session", - "sp-std", + "sp-std 5.0.0", "sp-transaction-pool", "sp-version", "substrate-wasm-builder", @@ -9497,13 +9599,13 @@ dependencies = [ "sp-beefy", "sp-block-builder", "sp-consensus-babe", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-io", + "sp-io 7.0.0", "sp-offchain", - "sp-runtime", + "sp-runtime 7.0.0", "sp-session", - "sp-std", + "sp-std 5.0.0", "sp-transaction-pool", "sp-version", "static_assertions", @@ -9580,7 +9682,7 @@ dependencies = [ "log", "netlink-packet-route", "netlink-proto", - "nix", + "nix 0.24.3", "thiserror", "tokio", ] @@ -9670,16 +9772,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.6" +version = "0.36.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" dependencies = [ "bitflags", "errno", - "io-lifetimes 1.0.4", + "io-lifetimes 1.0.5", "libc", "linux-raw-sys 0.1.4", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -9760,6 +9862,15 @@ dependencies = [ "rustc_version 0.2.3", ] +[[package]] +name = "safe_arch" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" +dependencies = [ + "bytemuck", +] + [[package]] name = "same-file" version = "1.0.6" @@ -9772,18 +9883,18 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "log", - "sp-core", - "sp-wasm-interface", + "sp-core 7.0.0", + "sp-wasm-interface 7.0.0", "thiserror", ] [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "futures", @@ -9800,9 +9911,9 @@ dependencies = [ "sp-api", "sp-authority-discovery", "sp-blockchain", - "sp-core", - "sp-keystore", - "sp-runtime", + "sp-core 7.0.0", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", "substrate-prometheus-endpoint", "thiserror", ] @@ -9810,7 +9921,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "futures", "futures-timer", @@ -9824,32 +9935,32 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-runtime", + "sp-runtime 7.0.0", "substrate-prometheus-endpoint", ] [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "parity-scale-codec", "sc-client-api", "sp-api", "sp-block-builder", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-runtime", - "sp-state-machine", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", ] [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "memmap2", "sc-chain-spec-derive", @@ -9857,17 +9968,17 @@ dependencies = [ "sc-telemetry", "serde", "serde_json", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", ] [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -9875,11 +9986,11 @@ dependencies = [ [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "array-bytes 4.2.0", "chrono", - "clap 4.1.1", + "clap 4.1.4", "fdlimit", "futures", "libp2p", @@ -9901,11 +10012,11 @@ dependencies = [ "serde", "serde_json", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-keyring", - "sp-keystore", - "sp-panic-handler", - "sp-runtime", + "sp-keystore 0.13.0", + "sp-panic-handler 5.0.0", + "sp-runtime 7.0.0", "sp-version", "thiserror", "tiny-bip39", @@ -9915,7 +10026,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "fnv", "futures", @@ -9928,20 +10039,20 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core", + "sp-core 7.0.0", "sp-database", - "sp-externalities", - "sp-keystore", - "sp-runtime", - "sp-state-machine", - "sp-storage", + "sp-externalities 0.13.0", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "sp-storage 7.0.0", "substrate-prometheus-endpoint", ] [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "hash-db", "kvdb", @@ -9954,19 +10065,20 @@ dependencies = [ "parking_lot 0.12.1", "sc-client-api", "sc-state-db", - "sp-arithmetic", + "schnellru", + "sp-arithmetic 6.0.0", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-database", - "sp-runtime", - "sp-state-machine", - "sp-trie", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "sp-trie 7.0.0", ] [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "futures", @@ -9981,9 +10093,9 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core", - "sp-runtime", - "sp-state-machine", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", "substrate-prometheus-endpoint", "thiserror", ] @@ -9991,7 +10103,7 @@ dependencies = [ [[package]] name = "sc-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "futures", @@ -10003,16 +10115,16 @@ dependencies = [ "sc-consensus-slots", "sc-telemetry", "sp-api", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-block-builder", "sp-blockchain", "sp-consensus", "sp-consensus-aura", "sp-consensus-slots", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-keystore", - "sp-runtime", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", "substrate-prometheus-endpoint", "thiserror", ] @@ -10020,7 +10132,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "fork-tree", @@ -10038,19 +10150,20 @@ dependencies = [ "sc-consensus-slots", "sc-keystore", "sc-telemetry", + "scale-info", "schnorrkel", "sp-api", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-block-builder", "sp-blockchain", "sp-consensus", "sp-consensus-babe", "sp-consensus-slots", "sp-consensus-vrf", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-keystore", - "sp-runtime", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", "substrate-prometheus-endpoint", "thiserror", ] @@ -10058,7 +10171,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "futures", "jsonrpsee 0.16.2", @@ -10067,33 +10180,33 @@ dependencies = [ "sc-rpc-api", "serde", "sp-api", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-blockchain", "sp-consensus", "sp-consensus-babe", - "sp-core", - "sp-keystore", - "sp-runtime", + "sp-core 7.0.0", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", "thiserror", ] [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "fork-tree", "parity-scale-codec", "sc-client-api", "sc-consensus", "sp-blockchain", - "sp-runtime", + "sp-runtime 7.0.0", ] [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "futures", @@ -10103,36 +10216,36 @@ dependencies = [ "sc-client-api", "sc-consensus", "sc-telemetry", - "sp-arithmetic", + "sp-arithmetic 6.0.0", "sp-blockchain", "sp-consensus", "sp-consensus-slots", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-runtime", - "sp-state-machine", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", ] [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "lru", + "lru 0.8.1", "parity-scale-codec", "parking_lot 0.12.1", "sc-executor-common", "sc-executor-wasmi", "sc-executor-wasmtime", "sp-api", - "sp-core", - "sp-externalities", - "sp-io", - "sp-panic-handler", - "sp-runtime-interface", - "sp-trie", + "sp-core 7.0.0", + "sp-externalities 0.13.0", + "sp-io 7.0.0", + "sp-panic-handler 5.0.0", + "sp-runtime-interface 7.0.0", + "sp-trie 7.0.0", "sp-version", - "sp-wasm-interface", + "sp-wasm-interface 7.0.0", "tracing", "wasmi", ] @@ -10140,11 +10253,11 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", - "sp-wasm-interface", + "sp-wasm-interface 7.0.0", "thiserror", "wasm-instrument", "wasmi", @@ -10153,20 +10266,20 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "log", "sc-allocator", "sc-executor-common", - "sp-runtime-interface", - "sp-wasm-interface", + "sp-runtime-interface 7.0.0", + "sp-wasm-interface 7.0.0", "wasmi", ] [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "cfg-if 1.0.0", "libc", @@ -10175,17 +10288,17 @@ dependencies = [ "rustix 0.35.13", "sc-allocator", "sc-executor-common", - "sp-runtime-interface", - "sp-wasm-interface", + "sp-runtime-interface 7.0.0", + "sp-wasm-interface 7.0.0", "wasmtime", ] [[package]] name = "sc-finality-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "ahash", + "ahash 0.8.3", "array-bytes 4.2.0", "async-trait", "dyn-clone", @@ -10208,14 +10321,14 @@ dependencies = [ "sc-utils", "serde_json", "sp-api", - "sp-application-crypto", - "sp-arithmetic", + "sp-application-crypto 7.0.0", + "sp-arithmetic 6.0.0", "sp-blockchain", "sp-consensus", - "sp-core", + "sp-core 7.0.0", "sp-finality-grandpa", - "sp-keystore", - "sp-runtime", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", "substrate-prometheus-endpoint", "thiserror", ] @@ -10223,7 +10336,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "finality-grandpa", "futures", @@ -10235,15 +10348,15 @@ dependencies = [ "sc-rpc", "serde", "sp-blockchain", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", "thiserror", ] [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "ansi_term", "futures", @@ -10252,28 +10365,28 @@ dependencies = [ "sc-client-api", "sc-network-common", "sp-blockchain", - "sp-runtime", + "sp-runtime 7.0.0", ] [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "array-bytes 4.2.0", "async-trait", "parking_lot 0.12.1", "serde_json", - "sp-application-crypto", - "sp-core", - "sp-keystore", + "sp-application-crypto 7.0.0", + "sp-core 7.0.0", + "sp-keystore 0.13.0", "thiserror", ] [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10287,7 +10400,7 @@ dependencies = [ "ip_network", "libp2p", "log", - "lru", + "lru 0.8.1", "parity-scale-codec", "parking_lot 0.12.1", "pin-project", @@ -10301,11 +10414,11 @@ dependencies = [ "serde", "serde_json", "smallvec", - "sp-arithmetic", + "sp-arithmetic 6.0.0", "sp-blockchain", "sp-consensus", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", "substrate-prometheus-endpoint", "thiserror", "unsigned-varint", @@ -10315,7 +10428,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "cid", "futures", @@ -10326,7 +10439,7 @@ dependencies = [ "sc-client-api", "sc-network-common", "sp-blockchain", - "sp-runtime", + "sp-runtime 7.0.0", "thiserror", "unsigned-varint", ] @@ -10334,7 +10447,7 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "bitflags", @@ -10352,7 +10465,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-finality-grandpa", - "sp-runtime", + "sp-runtime 7.0.0", "substrate-prometheus-endpoint", "thiserror", ] @@ -10360,17 +10473,17 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "ahash", + "ahash 0.8.3", "futures", "futures-timer", "libp2p", "log", - "lru", + "lru 0.8.1", "sc-network-common", "sc-peerset", - "sp-runtime", + "sp-runtime 7.0.0", "substrate-prometheus-endpoint", "tracing", ] @@ -10378,7 +10491,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10391,15 +10504,15 @@ dependencies = [ "sc-network-common", "sc-peerset", "sp-blockchain", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", "thiserror", ] [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10407,7 +10520,7 @@ dependencies = [ "futures", "libp2p", "log", - "lru", + "lru 0.8.1", "mockall", "parity-scale-codec", "prost", @@ -10418,12 +10531,12 @@ dependencies = [ "sc-peerset", "sc-utils", "smallvec", - "sp-arithmetic", + "sp-arithmetic 6.0.0", "sp-blockchain", "sp-consensus", - "sp-core", + "sp-core 7.0.0", "sp-finality-grandpa", - "sp-runtime", + "sp-runtime 7.0.0", "substrate-prometheus-endpoint", "thiserror", ] @@ -10431,7 +10544,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10443,14 +10556,14 @@ dependencies = [ "sc-peerset", "sc-utils", "sp-consensus", - "sp-runtime", + "sp-runtime 7.0.0", "substrate-prometheus-endpoint", ] [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "array-bytes 4.2.0", "bytes", @@ -10470,9 +10583,9 @@ dependencies = [ "sc-peerset", "sc-utils", "sp-api", - "sp-core", + "sp-core 7.0.0", "sp-offchain", - "sp-runtime", + "sp-runtime 7.0.0", "threadpool", "tracing", ] @@ -10480,7 +10593,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "futures", "libp2p", @@ -10493,7 +10606,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -10502,7 +10615,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "futures", "jsonrpsee 0.16.2", @@ -10519,11 +10632,11 @@ dependencies = [ "serde_json", "sp-api", "sp-blockchain", - "sp-core", - "sp-keystore", + "sp-core 7.0.0", + "sp-keystore 0.13.0", "sp-offchain", "sp-rpc", - "sp-runtime", + "sp-runtime 7.0.0", "sp-session", "sp-version", "tokio", @@ -10532,7 +10645,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "jsonrpsee 0.16.2", "parity-scale-codec", @@ -10541,9 +10654,9 @@ dependencies = [ "scale-info", "serde", "serde_json", - "sp-core", + "sp-core 7.0.0", "sp-rpc", - "sp-runtime", + "sp-runtime 7.0.0", "sp-version", "thiserror", ] @@ -10551,7 +10664,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "http", "jsonrpsee 0.16.2", @@ -10566,7 +10679,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10582,8 +10695,8 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", "sp-version", "thiserror", "tokio-stream", @@ -10592,7 +10705,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "directories", @@ -10623,6 +10736,7 @@ dependencies = [ "sc-rpc", "sc-rpc-server", "sc-rpc-spec-v2", + "sc-storage-monitor", "sc-sysinfo", "sc-telemetry", "sc-tracing", @@ -10634,16 +10748,16 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core", - "sp-externalities", - "sp-keystore", - "sp-runtime", + "sp-core 7.0.0", + "sp-externalities 0.13.0", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", "sp-session", - "sp-state-machine", - "sp-storage", + "sp-state-machine 0.13.0", + "sp-storage 7.0.0", "sp-transaction-pool", "sp-transaction-storage-proof", - "sp-trie", + "sp-trie 7.0.0", "sp-version", "static_init 1.0.3", "substrate-prometheus-endpoint", @@ -10657,18 +10771,34 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "log", "parity-scale-codec", "parking_lot 0.12.1", - "sp-core", + "sp-core 7.0.0", +] + +[[package]] +name = "sc-storage-monitor" +version = "0.1.0" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "clap 4.1.4", + "futures", + "log", + "nix 0.26.2", + "sc-client-db", + "sc-utils", + "sp-core 7.0.0", + "thiserror", + "tokio", ] [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "jsonrpsee 0.16.2", "parity-scale-codec", @@ -10680,14 +10810,14 @@ dependencies = [ "serde", "serde_json", "sp-blockchain", - "sp-runtime", + "sp-runtime 7.0.0", "thiserror", ] [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "futures", "libc", @@ -10698,15 +10828,15 @@ dependencies = [ "sc-telemetry", "serde", "serde_json", - "sp-core", - "sp-io", - "sp-std", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "chrono", "futures", @@ -10725,7 +10855,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "ansi_term", "atty", @@ -10743,10 +10873,10 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core", + "sp-core 7.0.0", "sp-rpc", - "sp-runtime", - "sp-tracing", + "sp-runtime 7.0.0", + "sp-tracing 6.0.0", "thiserror", "tracing", "tracing-log", @@ -10756,10 +10886,10 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -10767,7 +10897,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "futures", @@ -10783,9 +10913,9 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core", - "sp-runtime", - "sp-tracing", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-tracing 6.0.0", "sp-transaction-pool", "substrate-prometheus-endpoint", "thiserror", @@ -10794,21 +10924,21 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "futures", "log", "serde", "sp-blockchain", - "sp-runtime", + "sp-runtime 7.0.0", "thiserror", ] [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "backtrace", "futures", @@ -10819,6 +10949,29 @@ dependencies = [ "prometheus", ] +[[package]] +name = "scale-bits" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd7aca73785181cc41f0bbe017263e682b585ca660540ba569133901d013ecf" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "scale-decode" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d823d4be477fc33321f93d08fb6c2698273d044f01362dc27573a750deb7c233" +dependencies = [ + "parity-scale-codec", + "scale-bits", + "scale-info", + "thiserror", +] + [[package]] name = "scale-info" version = "2.3.1" @@ -10840,11 +10993,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] +[[package]] +name = "scale-value" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a5e7810815bd295da73e4216d1dfbced3c7c7c7054d70fa5f6e4c58123fff4" +dependencies = [ + "either", + "frame-metadata", + "parity-scale-codec", + "scale-bits", + "scale-decode", + "scale-info", + "serde", + "thiserror", + "yap", +] + [[package]] name = "schannel" version = "0.1.21" @@ -10854,6 +11024,17 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "schnellru" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +dependencies = [ + "ahash 0.8.3", + "cfg-if 1.0.0", + "hashbrown 0.13.2", +] + [[package]] name = "schnorrkel" version = "0.9.1" @@ -10959,9 +11140,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -10972,9 +11153,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys", "libc", @@ -11028,16 +11209,16 @@ version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" dependencies = [ "indexmap", "itoa", @@ -11162,14 +11343,15 @@ dependencies = [ [[package]] name = "simba" -version = "0.5.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e82063457853d00243beda9952e910b82593e4b07ae9f721b9278a99a0d3d5c" +checksum = "50582927ed6f77e4ac020c057f37a268fc6aebc29225050365aacbb9deeeddc4" dependencies = [ "approx", "num-complex", "num-traits", "paste", + "wide", ] [[package]] @@ -11189,14 +11371,14 @@ checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" [[package]] name = "slot-range-helper" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "enumn", "parity-scale-codec", "paste", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] @@ -11233,14 +11415,14 @@ checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" [[package]] name = "snow" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774d05a3edae07ce6d68ea6984f3c05e9bba8927e3dd591e3b479e5b03213d0d" +checksum = "12ba5f4d4ff12bdb6a169ed51b7c48c0e0ac4b0b4b31012b2571e97d78d3201d" dependencies = [ "aes-gcm 0.9.4", "blake2", "chacha20poly1305", - "curve25519-dalek 4.0.0-pre.5", + "curve25519-dalek 4.0.0-rc.0", "rand_core 0.6.4", "ring", "rustc_version 0.4.0", @@ -11278,17 +11460,17 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "hash-db", "log", "parity-scale-codec", "sp-api-proc-macro", - "sp-core", - "sp-runtime", - "sp-state-machine", - "sp-std", - "sp-trie", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "sp-std 5.0.0", + "sp-trie 7.0.0", "sp-version", "thiserror", ] @@ -11296,11 +11478,11 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "blake2", "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -11308,116 +11490,135 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-application-crypto" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a72575f160b1b134ee277a2ab46af4361c072a3fe661c48e474255406cb01c97" dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-io", - "sp-std", + "sp-core 11.0.0", + "sp-io 12.0.0", + "sp-std 6.0.0", ] [[package]] name = "sp-arithmetic" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "integer-sqrt", "num-traits", "parity-scale-codec", "scale-info", "serde", - "sp-std", + "sp-std 5.0.0", "static_assertions", ] [[package]] -name = "sp-authority-discovery" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +name = "sp-arithmetic" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2038010f7514d50775dcbd3edb569e17fa9bda63128580a9e172abb1795f2c1d" dependencies = [ + "integer-sqrt", + "num-traits", "parity-scale-codec", "scale-info", - "sp-api", - "sp-application-crypto", - "sp-runtime", - "sp-std", + "serde", + "sp-std 6.0.0", + "static_assertions", ] [[package]] -name = "sp-authorship" +name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "async-trait", "parity-scale-codec", - "sp-inherents", - "sp-runtime", - "sp-std", + "scale-info", + "sp-api", + "sp-application-crypto 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "sp-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ + "lazy_static", "parity-scale-codec", "scale-info", "serde", "sp-api", - "sp-application-crypto", - "sp-core", - "sp-io", + "sp-application-crypto 7.0.0", + "sp-core 7.0.0", + "sp-io 7.0.0", "sp-mmr-primitives", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "strum", ] [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "parity-scale-codec", "sp-api", "sp-inherents", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "futures", "log", - "lru", + "lru 0.8.1", "parity-scale-codec", "parking_lot 0.12.1", "sp-api", "sp-consensus", "sp-database", - "sp-runtime", - "sp-state-machine", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", "thiserror", ] [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "futures", "log", "parity-scale-codec", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-runtime", - "sp-state-machine", - "sp-std", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "sp-std 5.0.0", "sp-version", "thiserror", ] @@ -11425,25 +11626,25 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "parity-scale-codec", "scale-info", "sp-api", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-consensus", "sp-consensus-slots", "sp-inherents", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", "sp-timestamp", ] [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "merlin", @@ -11451,47 +11652,91 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-application-crypto", + "sp-application-crypto 7.0.0", "sp-consensus", "sp-consensus-slots", "sp-consensus-vrf", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-keystore", - "sp-runtime", - "sp-std", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", "sp-timestamp", ] [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-std", + "sp-std 5.0.0", "sp-timestamp", ] [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "parity-scale-codec", "scale-info", "schnorrkel", - "sp-core", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "sp-core" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "array-bytes 4.2.0", + "base58", + "bitflags", + "blake2", + "bounded-collections", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db", + "hash256-std-hasher", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log", + "merlin", + "parity-scale-codec", + "parking_lot 0.12.1", + "primitive-types", + "rand 0.8.5", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing 5.0.0", + "sp-debug-derive 5.0.0", + "sp-externalities 0.13.0", + "sp-runtime-interface 7.0.0", + "sp-std 5.0.0", + "sp-storage 7.0.0", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "zeroize", +] + +[[package]] +name = "sp-core" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d99984f57c9eb858d29fbe0e4cf44092eec484b2ff72176d5afa4ab7bf1dc8b1" dependencies = [ "array-bytes 4.2.0", "base58", @@ -11517,12 +11762,12 @@ dependencies = [ "secp256k1", "secrecy", "serde", - "sp-core-hashing", - "sp-debug-derive", - "sp-externalities", - "sp-runtime-interface", - "sp-std", - "sp-storage", + "sp-core-hashing 6.0.0", + "sp-debug-derive 6.0.0", + "sp-externalities 0.15.0", + "sp-runtime-interface 10.0.0", + "sp-std 6.0.0", + "sp-storage 9.0.0", "ss58-registry", "substrate-bip39", "thiserror", @@ -11533,32 +11778,47 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "blake2", + "byteorder", + "digest 0.10.6", + "sha2 0.10.6", + "sha3", + "sp-std 5.0.0", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc2d1947252b7a4e403b0a260f596920443742791765ec111daa2bbf98eff25" dependencies = [ "blake2", "byteorder", "digest 0.10.6", "sha2 0.10.6", "sha3", - "sp-std", + "sp-std 6.0.0", "twox-hash", ] [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", - "sp-core-hashing", + "sp-core-hashing 5.0.0", "syn 1.0.107", ] [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -11567,9 +11827,20 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "sp-debug-derive" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fb9dc63d54de7d7bed62a505b6e0bd66c122525ea1abb348f6564717c3df2d" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -11577,18 +11848,30 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std 5.0.0", + "sp-storage 7.0.0", +] + +[[package]] +name = "sp-externalities" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc754e1cec66b93df0b48a8986d019c1a2a90431c42cf2614cc35a291597c329" dependencies = [ "environmental", "parity-scale-codec", - "sp-std", - "sp-storage", + "sp-std 6.0.0", + "sp-storage 9.0.0", ] [[package]] name = "sp-finality-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "finality-grandpa", "log", @@ -11596,31 +11879,58 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-application-crypto", - "sp-core", - "sp-keystore", - "sp-runtime", - "sp-std", + "sp-application-crypto 7.0.0", + "sp-core 7.0.0", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "impl-trait-for-tuples", "parity-scale-codec", - "sp-core", - "sp-runtime", - "sp-std", + "scale-info", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", "thiserror", ] [[package]] name = "sp-io" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "bytes", + "ed25519", + "ed25519-dalek", + "futures", + "libsecp256k1", + "log", + "parity-scale-codec", + "secp256k1", + "sp-core 7.0.0", + "sp-externalities 0.13.0", + "sp-keystore 0.13.0", + "sp-runtime-interface 7.0.0", + "sp-state-machine 0.13.0", + "sp-std 5.0.0", + "sp-tracing 6.0.0", + "sp-trie 7.0.0", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-io" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ac917b37c7733e3778e7ffc3958f1181a151ac3b14116d65fb10fe7b08db10e" dependencies = [ "bytes", "ed25519", @@ -11630,14 +11940,14 @@ dependencies = [ "log", "parity-scale-codec", "secp256k1", - "sp-core", - "sp-externalities", - "sp-keystore", - "sp-runtime-interface", - "sp-state-machine", - "sp-std", - "sp-tracing", - "sp-trie", + "sp-core 11.0.0", + "sp-externalities 0.15.0", + "sp-keystore 0.17.0", + "sp-runtime-interface 10.0.0", + "sp-state-machine 0.17.0", + "sp-std 6.0.0", + "sp-tracing 7.0.0", + "sp-trie 11.0.0", "tracing", "tracing-core", ] @@ -11645,18 +11955,18 @@ dependencies = [ [[package]] name = "sp-keyring" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "lazy_static", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", "strum", ] [[package]] name = "sp-keystore" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "futures", @@ -11665,15 +11975,32 @@ dependencies = [ "parking_lot 0.12.1", "schnorrkel", "serde", - "sp-core", - "sp-externalities", + "sp-core 7.0.0", + "sp-externalities 0.13.0", + "thiserror", +] + +[[package]] +name = "sp-keystore" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ada8b7a72404bda58b3353830bc42f027ec764e13a28b4cd4386cf34fb1ae7c" +dependencies = [ + "async-trait", + "futures", + "merlin", + "parity-scale-codec", + "parking_lot 0.12.1", + "schnorrkel", + "sp-core 11.0.0", + "sp-externalities 0.15.0", "thiserror", ] [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "thiserror", "zstd", @@ -11682,7 +12009,7 @@ dependencies = [ [[package]] name = "sp-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "ckb-merkle-mountain-range 0.5.2", "log", @@ -11690,41 +12017,52 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-core", - "sp-debug-derive", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-debug-derive 5.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", "thiserror", ] [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-arithmetic", - "sp-core", - "sp-runtime", - "sp-std", + "sp-arithmetic 6.0.0", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "sp-api", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", ] [[package]] name = "sp-panic-handler" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + +[[package]] +name = "sp-panic-handler" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4abed79c3d5b3622f65ab065676addd9923b9b122cd257df23e2757ce487c6d2" dependencies = [ "backtrace", "lazy_static", @@ -11734,17 +12072,40 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "rustc-hash", "serde", - "sp-core", + "sp-core 7.0.0", ] [[package]] name = "sp-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "paste", + "rand 0.8.5", + "scale-info", + "serde", + "sp-application-crypto 7.0.0", + "sp-arithmetic 6.0.0", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-std 5.0.0", + "sp-weights 4.0.0", +] + +[[package]] +name = "sp-runtime" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86578c67b060a6ecab52af64f1cf18b3892fca7fba5ffc5c49934b2e76b1929" dependencies = [ "either", "hash256-std-hasher", @@ -11755,40 +12116,72 @@ dependencies = [ "rand 0.8.5", "scale-info", "serde", - "sp-application-crypto", - "sp-arithmetic", - "sp-core", - "sp-io", - "sp-std", - "sp-weights", + "sp-application-crypto 12.0.0", + "sp-arithmetic 9.0.0", + "sp-core 11.0.0", + "sp-io 12.0.0", + "sp-std 6.0.0", + "sp-weights 8.0.0", ] [[package]] name = "sp-runtime-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities 0.13.0", + "sp-runtime-interface-proc-macro 6.0.0", + "sp-std 5.0.0", + "sp-storage 7.0.0", + "sp-tracing 6.0.0", + "sp-wasm-interface 7.0.0", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3858935567385728ea45f6d159f9970b0b278eb22a8e77625bbf4a63e43a85a" dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", "primitive-types", - "sp-externalities", - "sp-runtime-interface-proc-macro", - "sp-std", - "sp-storage", - "sp-tracing", - "sp-wasm-interface", + "sp-externalities 0.15.0", + "sp-runtime-interface-proc-macro 7.0.0", + "sp-std 6.0.0", + "sp-storage 9.0.0", + "sp-tracing 7.0.0", + "sp-wasm-interface 8.0.0", "static_assertions", ] [[package]] name = "sp-runtime-interface-proc-macro" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00083a77e938c4f35d0bde7ca0b6e5f616158ebe11af6063795aa664d92a238b" dependencies = [ "Inflector", "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -11796,33 +12189,54 @@ dependencies = [ [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "parity-scale-codec", "scale-info", "sp-api", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", "sp-staking", - "sp-std", + "sp-std 5.0.0", ] [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "parity-scale-codec", "scale-info", - "sp-core", - "sp-runtime", - "sp-std", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", ] [[package]] name = "sp-state-machine" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "smallvec", + "sp-core 7.0.0", + "sp-externalities 0.13.0", + "sp-panic-handler 5.0.0", + "sp-std 5.0.0", + "sp-trie 7.0.0", + "thiserror", + "tracing", +] + +[[package]] +name = "sp-state-machine" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fd4c600df0b5abf26c19b6c61d928b64e0c2b8a709a3dbef872be0507cd1ca" dependencies = [ "hash-db", "log", @@ -11830,11 +12244,11 @@ dependencies = [ "parking_lot 0.12.1", "rand 0.8.5", "smallvec", - "sp-core", - "sp-externalities", - "sp-panic-handler", - "sp-std", - "sp-trie", + "sp-core 11.0.0", + "sp-externalities 0.15.0", + "sp-panic-handler 6.0.0", + "sp-std 6.0.0", + "sp-trie 11.0.0", "thiserror", "tracing", ] @@ -11842,43 +12256,76 @@ dependencies = [ [[package]] name = "sp-std" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" + +[[package]] +name = "sp-std" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af0ee286f98455272f64ac5bb1384ff21ac029fbb669afbaf48477faff12760e" [[package]] name = "sp-storage" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "impl-serde", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive", - "sp-std", + "sp-debug-derive 5.0.0", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-storage" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9acb4059eb0ae4fa8cf9ca9119b0178ad312a592d4c6054bd17b411034f233e9" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive 6.0.0", + "sp-std 6.0.0", ] [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "futures-timer", "log", "parity-scale-codec", "sp-inherents", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", "thiserror", ] [[package]] name = "sp-tracing" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "parity-scale-codec", + "sp-std 5.0.0", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sp-tracing" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b22d28a0bc2365dfb86644d14f2682a79da35891d4656d4896fb09fb05ff1e6c" dependencies = [ "parity-scale-codec", - "sp-std", + "sp-std 6.0.0", "tracing", "tracing-core", "tracing-subscriber", @@ -11887,55 +12334,79 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "sp-api", - "sp-runtime", + "sp-runtime 7.0.0", ] [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "log", "parity-scale-codec", "scale-info", - "sp-core", + "sp-core 7.0.0", "sp-inherents", - "sp-runtime", - "sp-std", - "sp-trie", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "sp-trie 7.0.0", ] [[package]] name = "sp-trie" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "ahash 0.8.3", + "hash-db", + "hashbrown 0.12.3", + "lazy_static", + "memory-db", + "nohash-hasher", + "parity-scale-codec", + "parking_lot 0.12.1", + "scale-info", + "schnellru", + "sp-core 7.0.0", + "sp-std 5.0.0", + "thiserror", + "tracing", + "trie-db 0.24.0", + "trie-root", +] + +[[package]] +name = "sp-trie" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db085f134cb444e52ca569a442d12c84bd17667532613d78dd6f568942632da4" dependencies = [ - "ahash", + "ahash 0.7.6", "hash-db", - "hashbrown", + "hashbrown 0.12.3", "lazy_static", - "lru", + "lru 0.8.1", "memory-db", "nohash-hasher", "parity-scale-codec", "parking_lot 0.12.1", "scale-info", - "sp-core", - "sp-std", + "sp-core 11.0.0", + "sp-std 6.0.0", "thiserror", "tracing", - "trie-db", + "trie-db 0.24.0", "trie-root", ] [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "impl-serde", "parity-scale-codec", @@ -11943,8 +12414,8 @@ dependencies = [ "scale-info", "serde", "sp-core-hashing-proc-macro", - "sp-runtime", - "sp-std", + "sp-runtime 7.0.0", + "sp-std 5.0.0", "sp-version-proc-macro", "thiserror", ] @@ -11952,10 +12423,10 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "parity-scale-codec", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -11963,12 +12434,26 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std 5.0.0", + "wasmi", + "wasmtime", +] + +[[package]] +name = "sp-wasm-interface" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ebc377987d42f8fc20e3f4ec4fd1147dd098fe90bcb4269e1eddb04e920f889" dependencies = [ "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-std", + "sp-std 6.0.0", "wasmi", "wasmtime", ] @@ -11976,16 +12461,32 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic 6.0.0", + "sp-core 7.0.0", + "sp-debug-derive 5.0.0", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-weights" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90eca2a19f48978e9cd605e23905d0602419f7880a9360ac717b03412e9c7916" dependencies = [ "parity-scale-codec", "scale-info", "serde", "smallvec", - "sp-arithmetic", - "sp-core", - "sp-debug-derive", - "sp-std", + "sp-arithmetic 9.0.0", + "sp-core 11.0.0", + "sp-debug-derive 6.0.0", + "sp-std 6.0.0", ] [[package]] @@ -12006,13 +12507,13 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d44528162f980c0e03c71e005d334332c8da0aec9f2b0b4bdc557ed4a9f24776" +checksum = "e40c020d72bc0a9c5660bb71e4a6fdef081493583062c474740a7d59f55f0e7b" dependencies = [ "Inflector", "num-format", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "serde", "serde_json", @@ -12066,7 +12567,7 @@ checksum = "f2261c91034a1edc3fc4d1b80e89d82714faede0515c14a75da10cb941546bbf" dependencies = [ "cfg_aliases", "memchr", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -12079,24 +12580,11 @@ checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" dependencies = [ "cfg_aliases", "memchr", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] -[[package]] -name = "statrs" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05bdbb8e4e78216a85785a85d3ec3183144f98d0097b9281802c019bb07a6f05" -dependencies = [ - "approx", - "lazy_static", - "nalgebra", - "num-traits", - "rand 0.8.5", -] - [[package]] name = "storage-proof-fuzzer" version = "0.1.0" @@ -12105,11 +12593,11 @@ dependencies = [ "env_logger 0.10.0", "honggfuzz", "log", - "sp-core", - "sp-runtime", - "sp-state-machine", - "sp-std", - "sp-trie", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "sp-std 5.0.0", + "sp-trie 7.0.0", ] [[package]] @@ -12143,7 +12631,7 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -12163,8 +12651,8 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck 0.4.0", - "proc-macro2 1.0.49", + "heck 0.4.1", + "proc-macro2 1.0.51", "quote 1.0.23", "rustversion", "syn 1.0.107", @@ -12205,7 +12693,7 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "platforms 2.0.0", ] @@ -12213,7 +12701,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "frame-system-rpc-runtime-api", "futures", @@ -12225,14 +12713,14 @@ dependencies = [ "sp-api", "sp-block-builder", "sp-blockchain", - "sp-core", - "sp-runtime", + "sp-core 7.0.0", + "sp-runtime 7.0.0", ] [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "hyper", "log", @@ -12288,9 +12776,9 @@ dependencies = [ "rialto-runtime", "signal-hook", "signal-hook-async-std", - "sp-core", + "sp-core 7.0.0", "sp-keyring", - "sp-runtime", + "sp-runtime 7.0.0", "structopt", "strum", "substrate-relay-helper", @@ -12307,11 +12795,11 @@ dependencies = [ "async-trait", "bp-header-chain", "bp-messages", - "bp-millau", "bp-parachains", "bp-polkadot-core", "bp-relayers", "bp-rialto", + "bp-rialto-parachain", "bp-rococo", "bp-runtime", "bp-wococo", @@ -12338,29 +12826,29 @@ dependencies = [ "relay-utils", "relay-wococo-client", "rialto-runtime", - "sp-core", + "sp-core 7.0.0", "sp-finality-grandpa", - "sp-runtime", + "sp-runtime 7.0.0", "thiserror", ] [[package]] name = "substrate-rpc-client" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", "jsonrpsee 0.16.2", "log", "sc-rpc-api", "serde", - "sp-runtime", + "sp-runtime 7.0.0", ] [[package]] name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "jsonrpsee 0.16.2", "log", @@ -12369,17 +12857,17 @@ dependencies = [ "sc-rpc-api", "scale-info", "serde", - "sp-core", - "sp-runtime", - "sp-state-machine", - "sp-trie", - "trie-db", + "sp-core 7.0.0", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", + "sp-trie 7.0.0", + "trie-db 0.24.0", ] [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "ansi_term", "build-helper", @@ -12408,6 +12896,80 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +[[package]] +name = "subxt" +version = "0.26.0" +source = "git+https://github.com/paritytech/subxt?branch=master#20adb198e42d1a36d0c919737230423c17ca78e6" +dependencies = [ + "base58", + "blake2", + "derivative", + "frame-metadata", + "futures", + "getrandom 0.2.8", + "hex", + "impl-serde", + "jsonrpsee 0.16.2", + "parity-scale-codec", + "parking_lot 0.12.1", + "primitive-types", + "scale-bits", + "scale-decode", + "scale-info", + "scale-value", + "serde", + "serde_json", + "sp-core 11.0.0", + "sp-core-hashing 6.0.0", + "sp-runtime 12.0.0", + "subxt-macro", + "subxt-metadata", + "thiserror", + "tracing", +] + +[[package]] +name = "subxt-codegen" +version = "0.26.0" +source = "git+https://github.com/paritytech/subxt?branch=master#20adb198e42d1a36d0c919737230423c17ca78e6" +dependencies = [ + "darling", + "frame-metadata", + "heck 0.4.1", + "hex", + "jsonrpsee 0.16.2", + "parity-scale-codec", + "proc-macro-error", + "proc-macro2 1.0.51", + "quote 1.0.23", + "scale-info", + "subxt-metadata", + "syn 1.0.107", + "tokio", +] + +[[package]] +name = "subxt-macro" +version = "0.26.0" +source = "git+https://github.com/paritytech/subxt?branch=master#20adb198e42d1a36d0c919737230423c17ca78e6" +dependencies = [ + "darling", + "proc-macro-error", + "subxt-codegen", + "syn 1.0.107", +] + +[[package]] +name = "subxt-metadata" +version = "0.26.0" +source = "git+https://github.com/paritytech/subxt?branch=master#20adb198e42d1a36d0c919737230423c17ca78e6" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-core-hashing 6.0.0", +] + [[package]] name = "syn" version = "0.15.44" @@ -12425,7 +12987,7 @@ version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "unicode-ident", ] @@ -12436,7 +12998,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", "unicode-xid 0.2.4", @@ -12506,9 +13068,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -12543,7 +13105,7 @@ version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -12598,12 +13160,11 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.5.2+5.3.0-patched" +version = "0.5.3+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec45c14da997d0925c7835883e4d5c181f196fa142f8c19d7643d1e9af2592c3" +checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" dependencies = [ "cc", - "fs_extra", "libc", ] @@ -12696,15 +13257,15 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.24.2" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" dependencies = [ "autocfg", "bytes", @@ -12726,7 +13287,7 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -12771,13 +13332,30 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" + +[[package]] +name = "toml_edit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" +dependencies = [ + "indexmap", + "nom8", + "toml_datetime", +] + [[package]] name = "tower" version = "0.4.13" @@ -12838,7 +13416,7 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -12865,8 +13443,8 @@ dependencies = [ [[package]] name = "tracing-gum" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "polkadot-node-jaeger", "polkadot-primitives", @@ -12876,12 +13454,12 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "expander 0.0.6", "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -12937,7 +13515,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "004e1e8f92535694b4cb1444dc5a8073ecf0815e3357f729638b9f8fc4062908" dependencies = [ "hash-db", - "hashbrown", + "hashbrown 0.12.3", + "log", + "rustc-hex", + "smallvec", +] + +[[package]] +name = "trie-db" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bcd4e7fbb3bcab17b02596b53876e36eb39663ddd87884bfd88c69cdeb2ebd6" +dependencies = [ + "hash-db", + "hashbrown 0.13.2", "log", "rustc-hex", "smallvec", @@ -13007,9 +13598,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#cba59778aa5207b11f7513e5cbb127ea47dd8924" +source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "clap 4.1.1", + "clap 4.1.4", "frame-remote-externalities", "hex", "log", @@ -13020,16 +13611,16 @@ dependencies = [ "serde", "serde_json", "sp-api", - "sp-core", - "sp-debug-derive", - "sp-externalities", - "sp-io", - "sp-keystore", + "sp-core 7.0.0", + "sp-debug-derive 5.0.0", + "sp-externalities 0.13.0", + "sp-io 7.0.0", + "sp-keystore 0.13.0", "sp-rpc", - "sp-runtime", - "sp-state-machine", + "sp-runtime 7.0.0", + "sp-state-machine 0.13.0", "sp-version", - "sp-weights", + "sp-weights 4.0.0", "substrate-rpc-client", "zstd", ] @@ -13067,7 +13658,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if 1.0.0", "digest 0.10.6", - "rand 0.7.3", + "rand 0.8.5", "static_assertions", ] @@ -13097,9 +13688,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" @@ -13118,9 +13709,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" @@ -13181,9 +13772,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" dependencies = [ "getrandom 0.2.8", ] @@ -13284,9 +13875,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -13294,14 +13885,14 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", "wasm-bindgen-shared", @@ -13309,9 +13900,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -13321,9 +13912,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote 1.0.23", "wasm-bindgen-macro-support", @@ -13331,11 +13922,11 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", "wasm-bindgen-backend", @@ -13344,9 +13935,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "wasm-instrument" @@ -13359,9 +13950,9 @@ dependencies = [ [[package]] name = "wasm-opt" -version = "0.110.2" +version = "0.111.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68e8037b4daf711393f4be2056246d12d975651b14d581520ad5d1f19219cec" +checksum = "84a303793cbc01fb96551badfc7367db6007396bba6bac97936b3c8b6f7fdb41" dependencies = [ "anyhow", "libc", @@ -13375,9 +13966,9 @@ dependencies = [ [[package]] name = "wasm-opt-cxx-sys" -version = "0.110.2" +version = "0.111.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91adbad477e97bba3fbd21dd7bfb594e7ad5ceb9169ab1c93ab9cb0ada636b6f" +checksum = "d9c9deb56f8a9f2ec177b3bd642a8205621835944ed5da55f2388ef216aca5a4" dependencies = [ "anyhow", "cxx", @@ -13387,9 +13978,9 @@ dependencies = [ [[package]] name = "wasm-opt-sys" -version = "0.110.2" +version = "0.111.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec4fa5a322a4e6ac22fd141f498d56afbdbf9df5debeac32380d2dcaa3e06941" +checksum = "4432e28b542738a9776cedf92e8a99d8991c7b4667ee2c7ccddfb479dd2856a7" dependencies = [ "anyhow", "cc", @@ -13627,9 +14218,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -13869,7 +14460,7 @@ dependencies = [ "lazy_static", "libc", "log", - "nix", + "nix 0.24.3", "rand 0.8.5", "thiserror", "tokio", @@ -13887,15 +14478,25 @@ dependencies = [ [[package]] name = "which" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", "libc", "once_cell", ] +[[package]] +name = "wide" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feff0a412894d67223777b6cc8d68c0dab06d52d95e9890d5f2d47f10dd9366c" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "widestring" version = "0.5.1" @@ -13974,6 +14575,30 @@ dependencies = [ "windows_x86_64_msvc 0.42.1", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.1" @@ -14155,8 +14780,8 @@ dependencies = [ [[package]] name = "xcm" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "derivative", "impl-trait-for-tuples", @@ -14164,15 +14789,15 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-weights", + "sp-core 7.0.0", + "sp-weights 4.0.0", "xcm-procedural", ] [[package]] name = "xcm-builder" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "frame-support", "frame-system", @@ -14182,40 +14807,40 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain", "scale-info", - "sp-arithmetic", - "sp-io", - "sp-runtime", - "sp-std", + "sp-arithmetic 6.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", "xcm", "xcm-executor", ] [[package]] name = "xcm-executor" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "environmental", "frame-support", "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-arithmetic", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "sp-weights", + "sp-arithmetic 6.0.0", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "sp-weights 4.0.0", "xcm", ] [[package]] name = "xcm-procedural" -version = "0.9.33" -source = "git+https://github.com/paritytech/polkadot?branch=master#a5cd3dd1a26dfb78daf3d743449934cbc7cc4090" +version = "0.9.37" +source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ "Inflector", - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", ] @@ -14234,6 +14859,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "yap" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc77f52dc9e9b10d55d3f4462c3b7fc393c4f17975d641542833ab2d3bc26ef" + [[package]] name = "yasna" version = "0.5.1" @@ -14258,7 +14889,7 @@ version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.51", "quote 1.0.23", "syn 1.0.107", "synstructure", @@ -14285,9 +14916,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.5+zstd.1.5.2" +version = "2.0.6+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc50ffce891ad571e9f9afe5039c4837bede781ac4bb13052ed7ae695518596" +checksum = "68a3f9792c0c3dc6c165840a75f47ae1f4da402c2d006881129579f6597e801b" dependencies = [ "cc", "libc", diff --git a/bin/millau/node/Cargo.toml b/bin/millau/node/Cargo.toml index 508f5714a58..af2c50eb480 100644 --- a/bin/millau/node/Cargo.toml +++ b/bin/millau/node/Cargo.toml @@ -9,9 +9,9 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.1.1", features = ["derive"] } +clap = { version = "4.1.4", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } -serde_json = "1.0.79" +serde_json = "1.0.92" # Bridge dependencies diff --git a/bin/millau/node/src/chain_spec.rs b/bin/millau/node/src/chain_spec.rs index 8b0bc28ed14..d18634eb983 100644 --- a/bin/millau/node/src/chain_spec.rs +++ b/bin/millau/node/src/chain_spec.rs @@ -202,7 +202,7 @@ fn testnet_genesis( balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 50)).collect(), }, aura: AuraConfig { authorities: Vec::new() }, - beefy: BeefyConfig { authorities: Vec::new() }, + beefy: BeefyConfig::default(), grandpa: GrandpaConfig { authorities: Vec::new() }, sudo: SudoConfig { key: Some(root_key) }, session: SessionConfig { diff --git a/bin/millau/node/src/cli.rs b/bin/millau/node/src/cli.rs index 0280254bcad..12499b5718d 100644 --- a/bin/millau/node/src/cli.rs +++ b/bin/millau/node/src/cli.rs @@ -28,6 +28,7 @@ pub struct Cli { /// Possible subcommands of the main binary. #[derive(Debug, Parser)] +#[allow(clippy::large_enum_variant)] pub enum Subcommand { /// Key management CLI utilities #[clap(subcommand)] diff --git a/bin/millau/node/src/service.rs b/bin/millau/node/src/service.rs index 6ad63387da4..8b0bc828957 100644 --- a/bin/millau/node/src/service.rs +++ b/bin/millau/node/src/service.rs @@ -125,7 +125,7 @@ pub fn new_partial( let (grandpa_block_import, grandpa_link) = sc_finality_grandpa::block_import( client.clone(), - &(client.clone() as Arc<_>), + &client, select_chain.clone(), telemetry.as_ref().map(|x| x.handle()), )?; diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index 5aba458216d..2119fa04610 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -61,7 +61,9 @@ pub use frame_support::{ construct_runtime, dispatch::DispatchClass, parameter_types, - traits::{ConstU32, ConstU8, Currency, ExistenceRequirement, Imbalance, KeyOwnerProofSystem}, + traits::{ + ConstU32, ConstU64, ConstU8, Currency, ExistenceRequirement, Imbalance, KeyOwnerProofSystem, + }, weights::{ constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight, @@ -240,6 +242,7 @@ impl pallet_grandpa::Config for Runtime { // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) type WeightInfo = (); type MaxAuthorities = ConstU32<10>; + type MaxSetIdSessionEntries = ConstU64<0>; } /// MMR helper types. @@ -384,30 +387,12 @@ impl pallet_bridge_relayers::Config for Runtime { type WeightInfo = (); } -#[cfg(feature = "runtime-benchmarks")] -parameter_types! { - /// Number of headers to keep in benchmarks. - /// - /// In benchmarks we always populate with full number of `HeadersToKeep` to make sure that - /// pruning is taken into account. - /// - /// Note: This is lower than regular value, to speed up benchmarking setup. - pub const HeadersToKeep: u32 = 1024; - /// Maximal number of authorities at Rialto. - /// - /// In benchmarks we're using sets of up to `1024` authorities to prepare for possible - /// upgrades in the future and see if performance degrades when number of authorities - /// grow. - pub const MaxAuthoritiesAtRialto: u32 = pallet_bridge_grandpa::benchmarking::MAX_VALIDATOR_SET_SIZE; -} - -#[cfg(not(feature = "runtime-benchmarks"))] parameter_types! { /// Number of headers to keep. /// /// Assuming the worst case of every header being finalized, we will keep headers at least for a - /// week. - pub const HeadersToKeep: u32 = 7 * bp_rialto::DAYS; + /// day. + pub const HeadersToKeep: u32 = bp_rialto::DAYS; /// Maximal number of authorities at Rialto. pub const MaxAuthoritiesAtRialto: u32 = bp_rialto::MAX_AUTHORITIES_COUNT; } @@ -752,6 +737,12 @@ impl_runtime_apis! { fn query_fee_details(uxt: ::Extrinsic, len: u32) -> FeeDetails { TransactionPayment::query_fee_details(uxt, len) } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } } impl sp_session::SessionKeys for Runtime { @@ -767,6 +758,10 @@ impl_runtime_apis! { } impl sp_beefy::BeefyApi for Runtime { + fn beefy_genesis() -> Option { + Beefy::genesis_block() + } + fn validator_set() -> Option> { Beefy::validator_set() } @@ -983,7 +978,7 @@ impl_runtime_apis! { let mut batches = Vec::::new(); let params = (&config, &whitelist); - use bridge_runtime_common::messages_benchmarking::{prepare_message_delivery_proof, prepare_message_proof}; + use bridge_runtime_common::messages_benchmarking::{prepare_message_delivery_proof_from_grandpa_chain, prepare_message_proof_from_grandpa_chain}; use pallet_bridge_messages::benchmarking::{ Pallet as MessagesBench, Config as MessagesConfig, @@ -1004,7 +999,7 @@ impl_runtime_apis! { fn prepare_message_proof( params: MessageProofParams, ) -> (rialto_messages::FromRialtoMessagesProof, Weight) { - prepare_message_proof::( + prepare_message_proof_from_grandpa_chain::( params, ) } @@ -1012,7 +1007,7 @@ impl_runtime_apis! { fn prepare_message_delivery_proof( params: MessageDeliveryProofParams, ) -> rialto_messages::ToRialtoMessagesDeliveryProof { - prepare_message_delivery_proof::( + prepare_message_delivery_proof_from_grandpa_chain::( params, ) } diff --git a/bin/millau/runtime/src/rialto_messages.rs b/bin/millau/runtime/src/rialto_messages.rs index 1bc361d5882..62ad919d96c 100644 --- a/bin/millau/runtime/src/rialto_messages.rs +++ b/bin/millau/runtime/src/rialto_messages.rs @@ -30,14 +30,14 @@ pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); /// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge /// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual /// tests, confirming that we don't break encoding somewhere between. -pub const BASE_XCM_WEIGHT_TWICE: u64 = 2 * crate::xcm_config::BASE_XCM_WEIGHT; +pub const BASE_XCM_WEIGHT_TWICE: Weight = crate::xcm_config::BaseXcmWeight::get().saturating_mul(2); parameter_types! { /// Weight credit for our test messages. /// /// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge /// (it is prepended with `UniversalOrigin` instruction). - pub const WeightCredit: Weight = Weight::from_ref_time(BASE_XCM_WEIGHT_TWICE); + pub const WeightCredit: Weight = BASE_XCM_WEIGHT_TWICE; } /// Message payload for Millau -> Rialto messages. diff --git a/bin/millau/runtime/src/rialto_parachain_messages.rs b/bin/millau/runtime/src/rialto_parachain_messages.rs index d19e4136408..1cfdc9c8f49 100644 --- a/bin/millau/runtime/src/rialto_parachain_messages.rs +++ b/bin/millau/runtime/src/rialto_parachain_messages.rs @@ -30,14 +30,14 @@ pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); /// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge /// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual /// tests, confirming that we don't break encoding somewhere between. -pub const BASE_XCM_WEIGHT_TWICE: u64 = 2 * crate::xcm_config::BASE_XCM_WEIGHT; +pub const BASE_XCM_WEIGHT_TWICE: Weight = crate::xcm_config::BaseXcmWeight::get().saturating_mul(2); parameter_types! { /// Weight credit for our test messages. /// /// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge /// (it is prepended with `UniversalOrigin` instruction). - pub const WeightCredit: Weight = Weight::from_ref_time(BASE_XCM_WEIGHT_TWICE); + pub const WeightCredit: Weight = BASE_XCM_WEIGHT_TWICE; } /// Message payload for Millau -> RialtoParachain messages. diff --git a/bin/millau/runtime/src/xcm_config.rs b/bin/millau/runtime/src/xcm_config.rs index 17bdc43da3d..aaaa7bf642d 100644 --- a/bin/millau/runtime/src/xcm_config.rs +++ b/bin/millau/runtime/src/xcm_config.rs @@ -93,13 +93,9 @@ type LocalOriginConverter = ( SignedAccountId32AsNative, ); -/// The amount of weight an XCM operation takes. This is a safe overestimate. -pub const BASE_XCM_WEIGHT: u64 = 1_000_000_000; - parameter_types! { /// The amount of weight an XCM operation takes. This is a safe overestimate. - // TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - check `set_proof_size` 0 or 64*1024 or 1026? - pub const BaseXcmWeight: Weight = Weight::from_parts(BASE_XCM_WEIGHT, 0); + pub const BaseXcmWeight: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); /// Maximum number of instructions in a single XCM fragment. A sanity check against weight /// calculations getting too crazy. pub const MaxInstructions: u32 = 100; @@ -314,17 +310,14 @@ mod tests { }; let dispatch_weight = MessageDispatcher::dispatch_weight(&mut incoming_message); - assert_eq!( - dispatch_weight, - frame_support::weights::Weight::from_ref_time(1_000_000_000) - ); + assert_eq!(dispatch_weight, BaseXcmWeight::get()); let dispatch_result = MessageDispatcher::dispatch(&AccountId::from([0u8; 32]), incoming_message); assert_eq!( dispatch_result, MessageDispatchResult { - unspent_weight: frame_support::weights::Weight::from_ref_time(0), + unspent_weight: frame_support::weights::Weight::zero(), dispatch_level_result: (), } ); diff --git a/bin/rialto-parachain/node/Cargo.toml b/bin/rialto-parachain/node/Cargo.toml index a6931291091..5e1eba1fee5 100644 --- a/bin/rialto-parachain/node/Cargo.toml +++ b/bin/rialto-parachain/node/Cargo.toml @@ -17,7 +17,7 @@ default = [] runtime-benchmarks = ['rialto-parachain-runtime/runtime-benchmarks'] [dependencies] -clap = { version = "4.1.1", features = ["derive"] } +clap = { version = "4.1.4", features = ["derive"] } log = '0.4.17' codec = { package = 'parity-scale-codec', version = '3.1.5' } serde = { version = '1.0', features = ['derive'] } diff --git a/bin/rialto-parachain/runtime/src/lib.rs b/bin/rialto-parachain/runtime/src/lib.rs index 2bc787c4953..ba1aa05463b 100644 --- a/bin/rialto-parachain/runtime/src/lib.rs +++ b/bin/rialto-parachain/runtime/src/lib.rs @@ -368,13 +368,9 @@ pub type XcmOriginToTransactDispatchOrigin = ( // TODO: until https://github.com/paritytech/parity-bridges-common/issues/1417 is fixed (in either way), // the following constant must match the similar constant in the Millau runtime. -/// One XCM operation is `1_000_000_000` weight - almost certainly a conservative estimate. -pub const BASE_XCM_WEIGHT: u64 = 1_000_000_000; - parameter_types! { /// The amount of weight an XCM operation takes. This is a safe overestimate. - // TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - check `set_proof_size` 0 or 64*1024 or 1026? - pub UnitWeightCost: Weight = Weight::from_parts(BASE_XCM_WEIGHT, 0); + pub const UnitWeightCost: Weight = Weight::from_parts(1_000_000, 64 * 1024); // One UNIT buys 1 second of weight. pub const WeightPrice: (MultiLocation, u128) = (MultiLocation::parent(), UNIT); pub const MaxInstructions: u32 = 100; @@ -533,8 +529,8 @@ parameter_types! { /// Number of headers to keep. /// /// Assuming the worst case of every header being finalized, we will keep headers at least for a - /// week. - pub const HeadersToKeep: u32 = 7 * bp_millau::DAYS as u32; + /// day. + pub const HeadersToKeep: u32 = bp_millau::DAYS as u32; /// Maximal number of authorities at Millau. pub const MaxAuthoritiesAtMillau: u32 = bp_millau::MAX_AUTHORITIES_COUNT; @@ -735,6 +731,12 @@ impl_runtime_apis! { ) -> pallet_transaction_payment::FeeDetails { TransactionPayment::query_fee_details(uxt, len) } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } } impl bp_millau::MillauFinalityApi for Runtime { @@ -896,17 +898,14 @@ mod tests { }; let dispatch_weight = MessageDispatcher::dispatch_weight(&mut incoming_message); - assert_eq!( - dispatch_weight, - frame_support::weights::Weight::from_ref_time(1_000_000_000) - ); + assert_eq!(dispatch_weight, UnitWeightCost::get()); let dispatch_result = MessageDispatcher::dispatch(&AccountId::from([0u8; 32]), incoming_message); assert_eq!( dispatch_result, MessageDispatchResult { - unspent_weight: frame_support::weights::Weight::from_ref_time(0), + unspent_weight: frame_support::weights::Weight::zero(), dispatch_level_result: (), } ); diff --git a/bin/rialto-parachain/runtime/src/millau_messages.rs b/bin/rialto-parachain/runtime/src/millau_messages.rs index 850ec1da60d..ee7a089992e 100644 --- a/bin/rialto-parachain/runtime/src/millau_messages.rs +++ b/bin/rialto-parachain/runtime/src/millau_messages.rs @@ -33,14 +33,14 @@ pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); /// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge /// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual /// tests, confirming that we don't break encoding somewhere between. -pub const BASE_XCM_WEIGHT_TWICE: u64 = 2 * crate::BASE_XCM_WEIGHT; +pub const BASE_XCM_WEIGHT_TWICE: Weight = crate::UnitWeightCost::get().saturating_mul(2); parameter_types! { /// Weight credit for our test messages. /// /// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge /// (it is prepended with `UniversalOrigin` instruction). - pub const WeightCredit: Weight = Weight::from_ref_time(BASE_XCM_WEIGHT_TWICE); + pub const WeightCredit: Weight = BASE_XCM_WEIGHT_TWICE; } /// Message payload for RialtoParachain -> Millau messages. diff --git a/bin/rialto/node/Cargo.toml b/bin/rialto/node/Cargo.toml index 5c93a93e0a7..bf8b3484732 100644 --- a/bin/rialto/node/Cargo.toml +++ b/bin/rialto/node/Cargo.toml @@ -9,8 +9,8 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.1.1", features = ["derive"] } -serde_json = "1.0.79" +clap = { version = "4.1.4", features = ["derive"] } +serde_json = "1.0.92" # Bridge dependencies diff --git a/bin/rialto/node/src/chain_spec.rs b/bin/rialto/node/src/chain_spec.rs index 5085c2dc71c..cfc0ed62e10 100644 --- a/bin/rialto/node/src/chain_spec.rs +++ b/bin/rialto/node/src/chain_spec.rs @@ -213,7 +213,7 @@ fn testnet_genesis( authorities: Vec::new(), epoch_config: Some(rialto_runtime::BABE_GENESIS_EPOCH_CONFIG), }, - beefy: BeefyConfig { authorities: Vec::new() }, + beefy: BeefyConfig::default(), grandpa: GrandpaConfig { authorities: Vec::new() }, sudo: SudoConfig { key: Some(root_key) }, session: SessionConfig { @@ -253,7 +253,10 @@ fn testnet_genesis( max_upward_queue_count: 8, max_upward_queue_size: 1024 * 1024, max_downward_message_size: 1024 * 1024, - ump_service_total_weight: Weight::from_ref_time(100_000_000_000), + ump_service_total_weight: Weight::from_parts( + 100_000_000_000, + polkadot_primitives::v2::MAX_POV_SIZE as u64, + ), max_upward_message_size: 50 * 1024, max_upward_message_num_per_candidate: 5, hrmp_sender_deposit: 0, diff --git a/bin/rialto/node/src/cli.rs b/bin/rialto/node/src/cli.rs index 0cdd25417e7..98323c9d9ca 100644 --- a/bin/rialto/node/src/cli.rs +++ b/bin/rialto/node/src/cli.rs @@ -28,6 +28,7 @@ pub struct Cli { /// Possible subcommands of the main binary. #[derive(Debug, Parser)] +#[allow(clippy::large_enum_variant)] pub enum Subcommand { /// Key management CLI utilities #[clap(subcommand)] diff --git a/bin/rialto/runtime/src/lib.rs b/bin/rialto/runtime/src/lib.rs index d9b07d582fb..cb3b6d60486 100644 --- a/bin/rialto/runtime/src/lib.rs +++ b/bin/rialto/runtime/src/lib.rs @@ -57,7 +57,9 @@ pub use frame_support::{ construct_runtime, dispatch::DispatchClass, parameter_types, - traits::{ConstU32, ConstU8, Currency, ExistenceRequirement, Imbalance, KeyOwnerProofSystem}, + traits::{ + ConstU32, ConstU64, ConstU8, Currency, ExistenceRequirement, Imbalance, KeyOwnerProofSystem, + }, weights::{constants::WEIGHT_REF_TIME_PER_SECOND, IdentityFee, RuntimeDbWeight, Weight}, StorageValue, }; @@ -263,6 +265,7 @@ impl pallet_grandpa::Config for Runtime { type HandleEquivocation = (); // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) type WeightInfo = (); + type MaxSetIdSessionEntries = ConstU64<0>; } impl pallet_mmr::Config for Runtime { @@ -398,8 +401,8 @@ parameter_types! { /// Number of headers to keep. /// /// Assuming the worst case of every header being finalized, we will keep headers at least for a - /// week. - pub const HeadersToKeep: u32 = 7 * bp_rialto::DAYS; + /// day. + pub const HeadersToKeep: u32 = bp_rialto::DAYS; /// Maximal number of authorities at Millau. pub const MaxAuthoritiesAtMillau: u32 = bp_millau::MAX_AUTHORITIES_COUNT; @@ -626,6 +629,10 @@ impl_runtime_apis! { } impl sp_beefy::BeefyApi for Runtime { + fn beefy_genesis() -> Option { + Beefy::genesis_block() + } + fn validator_set() -> Option> { Beefy::validator_set() } @@ -864,6 +871,12 @@ impl_runtime_apis! { fn query_fee_details(uxt: ::Extrinsic, len: u32) -> FeeDetails { TransactionPayment::query_fee_details(uxt, len) } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } } impl sp_session::SessionKeys for Runtime { diff --git a/bin/rialto/runtime/src/millau_messages.rs b/bin/rialto/runtime/src/millau_messages.rs index 09fbe46453d..4774d871b78 100644 --- a/bin/rialto/runtime/src/millau_messages.rs +++ b/bin/rialto/runtime/src/millau_messages.rs @@ -30,14 +30,14 @@ pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); /// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge /// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual /// tests, confirming that we don't break encoding somewhere between. -pub const BASE_XCM_WEIGHT_TWICE: u64 = 2 * crate::xcm_config::BASE_XCM_WEIGHT; +pub const BASE_XCM_WEIGHT_TWICE: Weight = crate::xcm_config::BaseXcmWeight::get().saturating_mul(2); parameter_types! { /// Weight credit for our test messages. /// /// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge /// (it is prepended with `UniversalOrigin` instruction). - pub const WeightCredit: Weight = Weight::from_ref_time(BASE_XCM_WEIGHT_TWICE); + pub const WeightCredit: Weight = BASE_XCM_WEIGHT_TWICE; } /// Message payload for Rialto -> Millau messages. diff --git a/bin/rialto/runtime/src/parachains.rs b/bin/rialto/runtime/src/parachains.rs index 1c7280198a4..9a3346eafd8 100644 --- a/bin/rialto/runtime/src/parachains.rs +++ b/bin/rialto/runtime/src/parachains.rs @@ -21,7 +21,7 @@ use crate::{ RuntimeOrigin, ShiftSessionManager, Slots, UncheckedExtrinsic, }; -use frame_support::{parameter_types, traits::KeyOwnerProofSystem, weights::Weight}; +use frame_support::{parameter_types, traits::KeyOwnerProofSystem}; use frame_system::EnsureRoot; use polkadot_primitives::v2::{ValidatorId, ValidatorIndex}; use polkadot_runtime_common::{paras_registrar, paras_sudo_wrapper, slots}; @@ -163,44 +163,3 @@ impl slots::Config for Runtime { } impl paras_sudo_wrapper::Config for Runtime {} - -pub struct ZeroWeights; - -impl polkadot_runtime_common::paras_registrar::WeightInfo for ZeroWeights { - fn reserve() -> Weight { - Weight::from_ref_time(0) - } - fn register() -> Weight { - Weight::from_ref_time(0) - } - fn force_register() -> Weight { - Weight::from_ref_time(0) - } - fn deregister() -> Weight { - Weight::from_ref_time(0) - } - fn swap() -> Weight { - Weight::from_ref_time(0) - } - fn schedule_code_upgrade(_: u32) -> Weight { - Weight::from_ref_time(0) - } - fn set_current_head(_: u32) -> Weight { - Weight::from_ref_time(0) - } -} - -impl polkadot_runtime_common::slots::WeightInfo for ZeroWeights { - fn force_lease() -> Weight { - Weight::from_ref_time(0) - } - fn manage_lease_period_start(_c: u32, _t: u32) -> Weight { - Weight::from_ref_time(0) - } - fn clear_all_leases() -> Weight { - Weight::from_ref_time(0) - } - fn trigger_onboard() -> Weight { - Weight::from_ref_time(0) - } -} diff --git a/bin/rialto/runtime/src/xcm_config.rs b/bin/rialto/runtime/src/xcm_config.rs index a86593f47f9..174ece04725 100644 --- a/bin/rialto/runtime/src/xcm_config.rs +++ b/bin/rialto/runtime/src/xcm_config.rs @@ -87,13 +87,9 @@ type LocalOriginConverter = ( SignedAccountId32AsNative, ); -/// The amount of weight an XCM operation takes. This is a safe overestimate. -pub const BASE_XCM_WEIGHT: u64 = 1_000_000_000; - parameter_types! { /// The amount of weight an XCM operation takes. This is a safe overestimate. - // TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - check `set_proof_size` 0 or 64*1024 or 1026? - pub const BaseXcmWeight: Weight = Weight::from_parts(BASE_XCM_WEIGHT, 0); + pub const BaseXcmWeight: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); /// Maximum number of instructions in a single XCM fragment. A sanity check against weight /// calculations getting too crazy. pub const MaxInstructions: u32 = 100; @@ -273,17 +269,14 @@ mod tests { }; let dispatch_weight = MessageDispatcher::dispatch_weight(&mut incoming_message); - assert_eq!( - dispatch_weight, - frame_support::weights::Weight::from_ref_time(1_000_000_000) - ); + assert_eq!(dispatch_weight, BaseXcmWeight::get()); let dispatch_result = MessageDispatcher::dispatch(&AccountId::from([0u8; 32]), incoming_message); assert_eq!( dispatch_result, MessageDispatchResult { - unspent_weight: frame_support::weights::Weight::from_ref_time(0), + unspent_weight: frame_support::weights::Weight::zero(), dispatch_level_result: (), } ); diff --git a/bin/runtime-common/Cargo.toml b/bin/runtime-common/Cargo.toml index 9e8e6b29db2..27e305751e4 100644 --- a/bin/runtime-common/Cargo.toml +++ b/bin/runtime-common/Cargo.toml @@ -82,6 +82,8 @@ std = [ runtime-benchmarks = [ "pallet-bridge-grandpa/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks", + "pallet-bridge-parachains/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", "xcm-builder/runtime-benchmarks", ] integrity-test = [ diff --git a/bin/runtime-common/src/integrity.rs b/bin/runtime-common/src/integrity.rs index 11c6cb90a83..51fb50df7e1 100644 --- a/bin/runtime-common/src/integrity.rs +++ b/bin/runtime-common/src/integrity.rs @@ -90,7 +90,6 @@ macro_rules! assert_bridge_messages_pallet_types( assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, FromThisChainMessagePayload); - assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundPayload, FromBridgedChainMessagePayload>>); assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundRelayer, AccountIdOf>); assert_type_eq_all!(<$r as MessagesConfig<$i>>::TargetHeaderChain, TargetHeaderChainAdapter<$bridge>); diff --git a/bin/runtime-common/src/messages_benchmarking.rs b/bin/runtime-common/src/messages_benchmarking.rs index afe95422d18..e7ca26d952e 100644 --- a/bin/runtime-common/src/messages_benchmarking.rs +++ b/bin/runtime-common/src/messages_benchmarking.rs @@ -22,7 +22,7 @@ use crate::{ messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, - AccountIdOf, BalanceOf, BridgedChain, CallOf, HashOf, MessageBridge, ThisChain, + AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, RawStorageProof, ThisChain, }, messages_generation::{ encode_all_messages, encode_lane_data, grow_trie, prepare_messages_storage_proof, @@ -30,52 +30,46 @@ use crate::{ }; use bp_messages::storage_keys; -use bp_runtime::{record_all_trie_keys, StorageProofSize}; +use bp_polkadot_core::parachains::ParaHash; +use bp_runtime::{record_all_trie_keys, Chain, Parachain, StorageProofSize, UnderlyingChainOf}; use codec::Encode; -use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; +use frame_support::weights::Weight; use pallet_bridge_messages::benchmarking::{MessageDeliveryProofParams, MessageProofParams}; -use sp_core::Hasher; -use sp_runtime::traits::{Header, MaybeSerializeDeserialize, Zero}; -use sp_std::{fmt::Debug, prelude::*}; +use sp_runtime::traits::{Header, Zero}; +use sp_std::prelude::*; use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; /// Prepare proof of messages for the `receive_messages_proof` call. /// /// In addition to returning valid messages proof, environment is prepared to verify this message /// proof. -pub fn prepare_message_proof( +/// +/// This method is intended to be used when benchmarking pallet, linked to the chain that +/// uses GRANDPA finality. For parachains, please use the `prepare_message_proof_from_parachain` +/// function. +pub fn prepare_message_proof_from_grandpa_chain( params: MessageProofParams, ) -> (FromBridgedChainMessagesProof>>, Weight) where - R: frame_system::Config>> - + pallet_bridge_grandpa::Config, - R::BridgedChain: bp_runtime::Chain>, Header = BH>, - B: MessageBridge, - BI: 'static, + R: pallet_bridge_grandpa::Config>>, FI: 'static, - BH: Header>>, - BHH: Hasher>>, - AccountIdOf>: PartialEq + sp_std::fmt::Debug, - AccountIdOf>: From<[u8; 32]>, - BalanceOf>: Debug + MaybeSerializeDeserialize, - CallOf>: From> + GetDispatchInfo, - HashOf>: Copy + Default, + B: MessageBridge, { - let message_payload = match params.size { - StorageProofSize::Minimal(ref size) => vec![0u8; *size as _], - _ => vec![], - }; - - // finally - prepare storage proof and update environment + // prepare storage proof let (state_root, storage_proof) = prepare_messages_storage_proof::( params.lane, params.message_nonces.clone(), params.outbound_lane_data, params.size, - message_payload, + match params.size { + StorageProofSize::Minimal(ref size) => vec![0u8; *size as _], + _ => vec![], + }, encode_all_messages, encode_lane_data, ); + + // update runtime storage let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); ( @@ -90,18 +84,115 @@ where ) } +/// Prepare proof of messages for the `receive_messages_proof` call. +/// +/// In addition to returning valid messages proof, environment is prepared to verify this message +/// proof. +/// +/// This method is intended to be used when benchmarking pallet, linked to the chain that +/// uses parachain finality. For GRANDPA chains, please use the +/// `prepare_message_proof_from_grandpa_chain` function. +pub fn prepare_message_proof_from_parachain( + params: MessageProofParams, +) -> (FromBridgedChainMessagesProof>>, Weight) +where + R: pallet_bridge_parachains::Config, + PI: 'static, + B: MessageBridge, + UnderlyingChainOf>: Chain + Parachain, +{ + // prepare storage proof + let (state_root, storage_proof) = prepare_messages_storage_proof::( + params.lane, + params.message_nonces.clone(), + params.outbound_lane_data, + params.size, + match params.size { + StorageProofSize::Minimal(ref size) => vec![0u8; *size as _], + _ => vec![], + }, + encode_all_messages, + encode_lane_data, + ); + + // update runtime storage + let (_, bridged_header_hash) = + insert_header_to_parachains_pallet::>>(state_root); + + ( + FromBridgedChainMessagesProof { + bridged_header_hash, + storage_proof, + lane: params.lane, + nonces_start: *params.message_nonces.start(), + nonces_end: *params.message_nonces.end(), + }, + Weight::zero(), + ) +} + /// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call. -pub fn prepare_message_delivery_proof( +/// +/// This method is intended to be used when benchmarking pallet, linked to the chain that +/// uses GRANDPA finality. For parachains, please use the +/// `prepare_message_delivery_proof_from_parachain` function. +pub fn prepare_message_delivery_proof_from_grandpa_chain( params: MessageDeliveryProofParams>>, ) -> FromBridgedChainMessagesDeliveryProof>> where - R: pallet_bridge_grandpa::Config, - R::BridgedChain: bp_runtime::Chain>, Header = BH>, + R: pallet_bridge_grandpa::Config>>, FI: 'static, B: MessageBridge, - BH: Header>>, - BHH: Hasher>>, - HashOf>: Copy + Default, +{ + // prepare storage proof + let lane = params.lane; + let (state_root, storage_proof) = prepare_message_delivery_proof::(params); + + // update runtime storage + let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); + + FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: bridged_header_hash.into(), + storage_proof, + lane, + } +} + +/// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call. +/// +/// This method is intended to be used when benchmarking pallet, linked to the chain that +/// uses parachain finality. For GRANDPA chains, please use the +/// `prepare_message_delivery_proof_from_grandpa_chain` function. +pub fn prepare_message_delivery_proof_from_parachain( + params: MessageDeliveryProofParams>>, +) -> FromBridgedChainMessagesDeliveryProof>> +where + R: pallet_bridge_parachains::Config, + PI: 'static, + B: MessageBridge, + UnderlyingChainOf>: Chain + Parachain, +{ + // prepare storage proof + let lane = params.lane; + let (state_root, storage_proof) = prepare_message_delivery_proof::(params); + + // update runtime storage + let (_, bridged_header_hash) = + insert_header_to_parachains_pallet::>>(state_root); + + FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: bridged_header_hash.into(), + storage_proof, + lane, + } +} + +/// Prepare in-memory message delivery proof, without inserting anything to the runtime storage. +fn prepare_message_delivery_proof( + params: MessageDeliveryProofParams>>, +) -> (HashOf>, RawStorageProof) +where + B: MessageBridge, { // prepare Bridged chain storage with inbound lane state let storage_key = @@ -109,7 +200,8 @@ where let mut root = Default::default(); let mut mdb = MemoryDB::default(); { - let mut trie = TrieDBMutBuilderV1::::new(&mut mdb, &mut root).build(); + let mut trie = + TrieDBMutBuilderV1::>>::new(&mut mdb, &mut root).build(); trie.insert(&storage_key, ¶ms.inbound_lane_data.encode()) .map_err(|_| "TrieMut::insert has failed") .expect("TrieMut::insert should not fail in benchmarks"); @@ -117,20 +209,17 @@ where root = grow_trie(root, &mut mdb, params.size); // generate storage proof to be delivered to This chain - let mut proof_recorder = Recorder::>::new(); - record_all_trie_keys::, _>(&mdb, &root, &mut proof_recorder) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); + let mut proof_recorder = Recorder::>>>::new(); + record_all_trie_keys::>>, _>( + &mdb, + &root, + &mut proof_recorder, + ) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); - // finally insert header with given state root to our storage - let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(root); - - FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: bridged_header_hash.into(), - storage_proof, - lane: params.lane, - } + (root, storage_proof) } /// Insert header to the bridge GRANDPA pallet. @@ -154,3 +243,25 @@ where pallet_bridge_grandpa::initialize_for_benchmarks::(bridged_header); (bridged_block_number, bridged_header_hash) } + +/// Insert header to the bridge parachains pallet. +pub(crate) fn insert_header_to_parachains_pallet( + state_root: bp_runtime::HashOf, +) -> (bp_runtime::BlockNumberOf, bp_runtime::HashOf) +where + R: pallet_bridge_parachains::Config, + PI: 'static, + PC: Chain + Parachain, +{ + let bridged_block_number = Zero::zero(); + let bridged_header = bp_runtime::HeaderOf::::new( + bridged_block_number, + Default::default(), + state_root, + Default::default(), + Default::default(), + ); + let bridged_header_hash = bridged_header.hash(); + pallet_bridge_parachains::initialize_for_benchmarks::(bridged_header); + (bridged_block_number, bridged_header_hash) +} diff --git a/deployments/bridges/rialto-millau/docker-compose.yml b/deployments/bridges/rialto-millau/docker-compose.yml index 44942e24de9..ad61b1d174a 100644 --- a/deployments/bridges/rialto-millau/docker-compose.yml +++ b/deployments/bridges/rialto-millau/docker-compose.yml @@ -25,7 +25,8 @@ services: - ./bridges/common:/common - ./bridges/rialto-millau/entrypoints:/entrypoints environment: - RUST_LOG: rpc=trace,bridge=trace,jsonrpsee=trace,jsonrpsee_core=trace,jsonrpsee_ws_client=trace,rustls=trace,soketto=trace + RUST_LOG: rpc=trace,bridge=trace + GLOBAL_DEPLOYMENTS: $GLOBAL_DEPLOYMENTS ports: - "10016:9616" depends_on: &all-nodes diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh index da809da6acc..af7ffca56d2 100755 --- a/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh +++ b/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh @@ -20,14 +20,17 @@ sleep 15 # Give chain a little bit of time to process initialization transaction sleep 6 +RIALTO_NODE_CONNECTION_PARAMS=$([ -z ${GLOBAL_DEPLOYMENTS} ] && \ + echo "--rialto-host rialto-node-alice --rialto-port 9944" \ + || \ + echo "--rialto-host wss.rialto.brucke.link --rialto-port 443 --rialto-secure" ) + /home/user/substrate-relay relay-headers-and-messages millau-rialto \ --millau-host millau-node-alice \ --millau-port 9944 \ --millau-signer //Rialto.HeadersAndMessagesRelay \ --millau-transactions-mortality=64 \ - --rialto-host wss.rialto.brucke.link \ - --rialto-port 443 \ - --rialto-secure \ + $RIALTO_NODE_CONNECTION_PARAMS \ --rialto-signer //Millau.HeadersAndMessagesRelay \ --rialto-transactions-mortality=64 \ --lane=00000000 \ diff --git a/deployments/networks/rialto.yml b/deployments/networks/rialto.yml index 9d68af965df..1e2bdd3e41b 100644 --- a/deployments/networks/rialto.yml +++ b/deployments/networks/rialto.yml @@ -56,8 +56,6 @@ services: - --unsafe-rpc-external - --unsafe-ws-external - --prometheus-external - environment: - RUST_LOG: runtime=trace,rpc=debug,txpool=trace,runtime::bridge=trace,beefy=debug,jsonrpsee=trace,jsonrpsee_core=trace,jsonrpsee_server=trace,rustls=trace,soketto=trace ports: - "10133:9933" - "10144:9944" diff --git a/docs/high-level-overview.md b/docs/high-level-overview.md index f2806719256..449224124af 100644 --- a/docs/high-level-overview.md +++ b/docs/high-level-overview.md @@ -35,7 +35,7 @@ about the source chain headers which have been finalized. This is useful for hig The pallet tracks current GRANDPA authorities set and only accepts finality proofs (GRANDPA justifications), generated by the current authorities set. The GRANDPA protocol itself requires current authorities set to -generate explicit justificaion for the header that enacts next authorities set. Such headers and their finality +generate explicit justification for the header that enacts next authorities set. Such headers and their finality proofs are called mandatory in the pallet and relayer pays no fee for such headers submission. The pallet does not require all headers to be imported or provided. The relayer itself chooses which headers @@ -123,7 +123,7 @@ The relay connects to the source _relay_ chain and the target chain nodes. It do tracked parachain nodes. The relay looks at the [`Heads`](https://github.com/paritytech/polkadot/blob/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras/mod.rs#L642) map of the [`paras` pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras) in source chain, and compares the value with the best parachain head, stored in the bridge parachains pallet at -the taget chain. If new parachain head appears at the relay chain block `B`, the relay process **waits** +the target chain. If new parachain head appears at the relay chain block `B`, the relay process **waits** until header `B` or one of its ancestors appears at the target chain. Once it is available, the storage proof of the map entry is generated and is submitted to the target chain. diff --git a/docs/polkadot-kusama-bridge-overview.md b/docs/polkadot-kusama-bridge-overview.md index 302196718b9..9f407b6ba00 100644 --- a/docs/polkadot-kusama-bridge-overview.md +++ b/docs/polkadot-kusama-bridge-overview.md @@ -21,8 +21,8 @@ The runtime will have minimal set of non-bridge pallets, so there's not much you ## Connecting Parachains You won't be able to directly use bridge hub transactions to send XCM messages over the bridge. Instead, you'll need -to use other parachains transactions, which will use HRMP to deliver message to the Bridge Hub. The Bridge Hub will -just queue this messages in its outbound lane, which is dedicated to deliver messages between two parachains. +to use other parachains transactions, which will use HRMP to deliver messages to the Bridge Hub. The Bridge Hub will +just queue these messages in its outbound lane, which is dedicated to deliver messages between two parachains. Our first planned bridge will connect the Polkadot' Statemint and Kusama' Statemine. Bridge between those two parachains would allow Statemint accounts to hold wrapped KSM tokens and Statemine accounts to hold wrapped DOT @@ -107,7 +107,7 @@ is not used to cover rewards of bridging with some other Polkadot Parachain. Our goal is to incentivize running honest relayers. But we have no relayers sets, so at any time anyone may submit message delivery transaction, hoping that the cost of this transaction will be compensated. So what if some message is currently queued and two relayers are submitting two identical message delivery transactions at once? Without any -special means, the cost of first included transacton will be compensated and the cost of the other one won't. A honest, +special means, the cost of first included transaction will be compensated and the cost of the other one won't. A honest, but unlucky relayer will lose some money. In addition, we'll waste some portion of block size and weight, which may be used by other useful transactions. diff --git a/fuzz/storage-proof/Cargo.lock b/fuzz/storage-proof/Cargo.lock index 1c40a1a0d3b..6eba720c493 100644 --- a/fuzz/storage-proof/Cargo.lock +++ b/fuzz/storage-proof/Cargo.lock @@ -106,17 +106,6 @@ dependencies = [ "syn", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -239,6 +228,7 @@ dependencies = [ "frame-support", "frame-system", "hash-db", + "impl-trait-for-tuples", "num-traits", "parity-scale-codec", "scale-info", @@ -249,13 +239,14 @@ dependencies = [ "sp-state-machine", "sp-std", "sp-trie", + "trie-db 0.24.0", ] [[package]] name = "bumpalo" -version = "3.10.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byte-slice-cast" @@ -537,12 +528,12 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.8.4" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ - "atty", "humantime", + "is-terminal", "log", "regex", "termcolor", @@ -554,6 +545,27 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b91989ae21441195d7d9b9993a2f9295c7e1a8c96255d8b729accddc124797" +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fake-simd" version = "0.1.2" @@ -864,6 +876,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.2" @@ -956,6 +977,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "io-lifetimes" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +dependencies = [ + "libc", + "windows-sys 0.42.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "windows-sys 0.42.0", +] + [[package]] name = "itoa" version = "0.4.7" @@ -1003,9 +1046,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libsecp256k1" @@ -1055,6 +1098,12 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "lock_api" version = "0.4.7" @@ -1201,7 +1250,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.18", "libc", ] @@ -1234,9 +1283,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-scale-codec" -version = "3.1.2" +version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8b44461635bbb1a0300f100a841e571e7d919c81c73075ef5d152ffdb521066" +checksum = "e7ab01d0f889e957861bc65888d5ccbe82c158d0270136ba46820d43837cdf72" dependencies = [ "arrayvec 0.7.2", "bitvec", @@ -1248,9 +1297,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.2" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c45ed1f39709f5a89338fab50e59816b2e8815f5bb58276e7ddf9afd495f73f8" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1310,7 +1359,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -1578,6 +1627,20 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustix" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.42.0", +] + [[package]] name = "ryu" version = "1.0.5" @@ -2092,7 +2155,7 @@ dependencies = [ "sp-core", "sp-std", "thiserror", - "trie-db", + "trie-db 0.23.1", "trie-root", ] @@ -2390,6 +2453,19 @@ dependencies = [ "smallvec", ] +[[package]] +name = "trie-db" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "004e1e8f92535694b4cb1444dc5a8073ecf0815e3357f729638b9f8fc4062908" +dependencies = [ + "hash-db", + "hashbrown", + "log", + "rustc-hex", + "smallvec", +] + [[package]] name = "trie-root" version = "0.17.0" @@ -2595,43 +2671,100 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + [[package]] name = "wyz" version = "0.5.0" diff --git a/modules/grandpa/src/benchmarking.rs b/modules/grandpa/src/benchmarking.rs index e937f7a0bf4..710a7e0952c 100644 --- a/modules/grandpa/src/benchmarking.rs +++ b/modules/grandpa/src/benchmarking.rs @@ -50,35 +50,30 @@ use frame_benchmarking::{benchmarks_instance_pallet, whitelisted_caller}; use frame_support::traits::Get; use frame_system::RawOrigin; use sp_finality_grandpa::AuthorityId; -use sp_runtime::traits::Zero; +use sp_runtime::traits::{One, Zero}; use sp_std::vec::Vec; -// The maximum number of vote ancestries to include in a justification. -// -// In practice this would be limited by the session length (number of blocks a single authority set -// can produce) of a given chain. +/// The maximum number of vote ancestries to include in a justification. +/// +/// In practice this would be limited by the session length (number of blocks a single authority set +/// can produce) of a given chain. const MAX_VOTE_ANCESTRIES: u32 = 1000; -// The maximum number of pre-commits to include in a justification. In practice this scales with the -// number of validators. -pub const MAX_VALIDATOR_SET_SIZE: u32 = 1024; - -// `1..MAX_VALIDATOR_SET_SIZE` and `1..MAX_VOTE_ANCESTRIES` are too large && benchmarks are -// running for almost 40m (steps=50, repeat=20) on a decent laptop, which is too much. Since -// we're building linear function here, let's just select some limited subrange for benchmarking. -const VALIDATOR_SET_SIZE_RANGE_BEGIN: u32 = MAX_VALIDATOR_SET_SIZE / 20; -const VALIDATOR_SET_SIZE_RANGE_END: u32 = - VALIDATOR_SET_SIZE_RANGE_BEGIN + VALIDATOR_SET_SIZE_RANGE_BEGIN; +// `1..MAX_VOTE_ANCESTRIES` is too large && benchmarks are running for almost 40m (steps=50, +// repeat=20) on a decent laptop, which is too much. Since we're building linear function here, +// let's just select some limited subrange for benchmarking. const MAX_VOTE_ANCESTRIES_RANGE_BEGIN: u32 = MAX_VOTE_ANCESTRIES / 20; const MAX_VOTE_ANCESTRIES_RANGE_END: u32 = MAX_VOTE_ANCESTRIES_RANGE_BEGIN + MAX_VOTE_ANCESTRIES_RANGE_BEGIN; -/// Returns number of first header to be imported. -/// -/// Since we bootstrap the pallet with `HeadersToKeep` already imported headers, -/// this function computes the next expected header number to import. -fn header_number, I: 'static, N: From>() -> N { - (T::HeadersToKeep::get() + 1).into() +// the same with validators - if there are too much validators, let's run benchmarks on subrange +fn validator_set_range_end, I: 'static>() -> u32 { + let max_bridged_authorities = T::MaxBridgedAuthorities::get(); + if max_bridged_authorities > 128 { + sp_std::cmp::max(128, max_bridged_authorities / 5) + } else { + max_bridged_authorities + } } /// Prepare header and its justification to submit using `submit_finality_proof`. @@ -91,16 +86,19 @@ fn prepare_benchmark_data, I: 'static>( .map(|id| (AuthorityId::from(*id), 1)) .collect::>(); + let genesis_header: BridgedHeader = bp_test_utils::test_header(Zero::zero()); + let genesis_hash = genesis_header.hash(); let init_data = InitializationData { - header: Box::new(bp_test_utils::test_header(Zero::zero())), + header: Box::new(genesis_header), authority_list, set_id: TEST_GRANDPA_SET_ID, operating_mode: BasicOperatingMode::Normal, }; bootstrap_bridge::(init_data); + assert!(>::contains_key(genesis_hash)); - let header: BridgedHeader = bp_test_utils::test_header(header_number::()); + let header: BridgedHeader = bp_test_utils::test_header(One::one()); let params = JustificationGeneratorParams { header: header.clone(), round: TEST_GRANDPA_ROUND, @@ -117,16 +115,21 @@ benchmarks_instance_pallet! { // This is the "gold standard" benchmark for this extrinsic, and it's what should be used to // annotate the weight in the pallet. submit_finality_proof { - let p in VALIDATOR_SET_SIZE_RANGE_BEGIN..VALIDATOR_SET_SIZE_RANGE_END; + let p in 1 .. validator_set_range_end::(); let v in MAX_VOTE_ANCESTRIES_RANGE_BEGIN..MAX_VOTE_ANCESTRIES_RANGE_END; let caller: T::AccountId = whitelisted_caller(); let (header, justification) = prepare_benchmark_data::(p, v); }: submit_finality_proof(RawOrigin::Signed(caller), Box::new(header), justification) verify { - let header: BridgedHeader = bp_test_utils::test_header(header_number::()); + let genesis_header: BridgedHeader = bp_test_utils::test_header(Zero::zero()); + let header: BridgedHeader = bp_test_utils::test_header(One::one()); let expected_hash = header.hash(); + // check that the header#1 has been inserted assert_eq!(>::get().unwrap().1, expected_hash); assert!(>::contains_key(expected_hash)); + + // check that the header#0 has been pruned + assert!(!>::contains_key(genesis_header.hash())); } } diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index d3335693adf..6128030d090 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -289,8 +289,14 @@ pub mod pallet { /// A ring buffer of imported hashes. Ordered by the insertion time. #[pallet::storage] - pub(super) type ImportedHashes, I: 'static = ()> = - StorageMap<_, Identity, u32, BridgedBlockHash>; + pub(super) type ImportedHashes, I: 'static = ()> = StorageMap< + Hasher = Identity, + Key = u32, + Value = BridgedBlockHash, + QueryKind = OptionQuery, + OnEmpty = GetDefault, + MaxValues = MaybeHeadersToKeep, + >; /// Current ring buffer position. #[pallet::storage] @@ -299,8 +305,14 @@ pub mod pallet { /// Relevant fields of imported headers. #[pallet::storage] - pub type ImportedHeaders, I: 'static = ()> = - StorageMap<_, Identity, BridgedBlockHash, BridgedStoredHeaderData>; + pub type ImportedHeaders, I: 'static = ()> = StorageMap< + Hasher = Identity, + Key = BridgedBlockHash, + Value = BridgedStoredHeaderData, + QueryKind = OptionQuery, + OnEmpty = GetDefault, + MaxValues = MaybeHeadersToKeep, + >; /// The current GRANDPA Authority set. #[pallet::storage] @@ -518,27 +530,35 @@ pub mod pallet { Ok(()) } + /// Adapter for using `Config::HeadersToKeep` as `MaxValues` bound in our storage maps. + pub struct MaybeHeadersToKeep(PhantomData<(T, I)>); + + // this implementation is required to use the struct as `MaxValues` + impl, I: 'static> Get> for MaybeHeadersToKeep { + fn get() -> Option { + Some(T::HeadersToKeep::get()) + } + } + + /// Initialize pallet so that it is ready for inserting new header. + /// + /// The function makes sure that the new insertion will cause the pruning of some old header. + /// + /// Returns parent header for the new header. #[cfg(feature = "runtime-benchmarks")] pub(crate) fn bootstrap_bridge, I: 'static>( init_params: super::InitializationData>, - ) { - let start_number = *init_params.header.number(); - let end_number = start_number + T::HeadersToKeep::get().into(); + ) -> BridgedHeader { + let start_header = init_params.header.clone(); initialize_bridge::(init_params).expect("benchmarks are correct"); - let mut number = start_number; - while number < end_number { - number = number + sp_runtime::traits::One::one(); - let header = >::new( - number, - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ); - let hash = header.hash(); - insert_header::(header, hash); - } + // the most obvious way to cause pruning during next insertion would be to insert + // `HeadersToKeep` headers. But it'll make our benchmarks slow. So we will just play with + // our pruning ring-buffer. + assert_eq!(ImportedHashesPointer::::get(), 1); + ImportedHashesPointer::::put(0); + + *start_header } } @@ -799,13 +819,9 @@ mod tests { fn succesfully_imports_header_with_valid_finality() { run_test(|| { initialize_substrate_bridge(); - assert_ok!( - submit_finality_proof(1), - PostDispatchInfo { - actual_weight: None, - pays_fee: frame_support::dispatch::Pays::Yes, - }, - ); + let result = submit_finality_proof(1); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); let header = test_header(1); assert_eq!(>::get().unwrap().1, header.hash()); @@ -912,17 +928,13 @@ mod tests { let justification = make_default_justification(&header); // Let's import our test header - assert_ok!( - Pallet::::submit_finality_proof( - RuntimeOrigin::signed(1), - Box::new(header.clone()), - justification - ), - PostDispatchInfo { - actual_weight: None, - pays_fee: frame_support::dispatch::Pays::No, - }, + let result = Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header.clone()), + justification, ); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::No); // Make sure that our header is the best finalized assert_eq!(>::get().unwrap().1, header.hash()); @@ -1178,7 +1190,7 @@ mod tests { let direct_initialize_call = Call::::initialize { init_data: init_data.clone() }; - let indirect_initialize_call = BridgeGrandpaCall::::initialize(init_data); + let indirect_initialize_call = BridgeGrandpaCall::::initialize { init_data }; assert_eq!(direct_initialize_call.encode(), indirect_initialize_call.encode()); let direct_submit_finality_proof_call = Call::::submit_finality_proof { @@ -1186,7 +1198,10 @@ mod tests { justification: justification.clone(), }; let indirect_submit_finality_proof_call = - BridgeGrandpaCall::::submit_finality_proof(Box::new(header), justification); + BridgeGrandpaCall::::submit_finality_proof { + finality_target: Box::new(header), + justification, + }; assert_eq!( direct_submit_finality_proof_call.encode(), indirect_submit_finality_proof_call.encode() diff --git a/modules/grandpa/src/weights.rs b/modules/grandpa/src/weights.rs index d6c3d596d88..44b4ebd37fb 100644 --- a/modules/grandpa/src/weights.rs +++ b/modules/grandpa/src/weights.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Autogenerated weights for `pallet_bridge_grandpa` +//! Autogenerated weights for pallet_bridge_grandpa //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-21, STEPS: 50, REPEAT: 20 -//! LOW RANGE: [], HIGH RANGE: [] -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled -//! CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2023-02-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // target/release/millau-bridge-node @@ -48,7 +48,7 @@ use frame_support::{ }; use sp_std::marker::PhantomData; -/// Weight functions needed for `pallet_bridge_grandpa`. +/// Weight functions needed for pallet_bridge_grandpa. pub trait WeightInfo { fn submit_finality_proof(p: u32, v: u32) -> Weight; } @@ -58,22 +58,110 @@ pub trait WeightInfo { /// Those weights are test only and must never be used in production. pub struct BridgeWeight(PhantomData); impl WeightInfo for BridgeWeight { + /// Storage: BridgeRialtoGrandpa PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa RequestCount (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa RequestCount (max_values: Some(1), max_size: Some(4), added: 499, + /// mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa BestFinalized (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa BestFinalized (max_values: Some(1), max_size: Some(36), added: + /// 531, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa CurrentAuthoritySet (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa CurrentAuthoritySet (max_values: Some(1), max_size: Some(209), + /// added: 704, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHashesPointer (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa ImportedHashesPointer (max_values: Some(1), max_size: Some(4), + /// added: 499, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa ImportedHashes (max_values: Some(14400), max_size: Some(36), + /// added: 2016, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:0 w:2) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// The range of component `p` is `[1, 5]`. + /// + /// The range of component `v` is `[50, 100]`. fn submit_finality_proof(p: u32, v: u32) -> Weight { - Weight::from_ref_time(192_130_822 as u64) - .saturating_add(Weight::from_ref_time(39_781_096 as u64).saturating_mul(p as u64)) - .saturating_add(Weight::from_ref_time(1_365_108 as u64).saturating_mul(v as u64)) - .saturating_add(T::DbWeight::get().reads(6 as u64)) - .saturating_add(T::DbWeight::get().writes(6 as u64)) + // Proof Size summary in bytes: + // Measured: `416 + p * (40 ±0)` + // Estimated: `4745` + // Minimum execution time: 221_703 nanoseconds. + Weight::from_parts(39_358_497, 4745) + // Standard Error: 85_573 + .saturating_add(Weight::from_ref_time(40_593_280).saturating_mul(p.into())) + // Standard Error: 7_808 + .saturating_add(Weight::from_ref_time(1_529_400).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { + /// Storage: BridgeRialtoGrandpa PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa RequestCount (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa RequestCount (max_values: Some(1), max_size: Some(4), added: 499, + /// mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa BestFinalized (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa BestFinalized (max_values: Some(1), max_size: Some(36), added: + /// 531, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa CurrentAuthoritySet (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa CurrentAuthoritySet (max_values: Some(1), max_size: Some(209), + /// added: 704, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHashesPointer (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa ImportedHashesPointer (max_values: Some(1), max_size: Some(4), + /// added: 499, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa ImportedHashes (max_values: Some(14400), max_size: Some(36), + /// added: 2016, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:0 w:2) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// The range of component `p` is `[1, 5]`. + /// + /// The range of component `v` is `[50, 100]`. fn submit_finality_proof(p: u32, v: u32) -> Weight { - Weight::from_ref_time(192_130_822 as u64) - .saturating_add(Weight::from_ref_time(39_781_096 as u64).saturating_mul(p as u64)) - .saturating_add(Weight::from_ref_time(1_365_108 as u64).saturating_mul(v as u64)) - .saturating_add(RocksDbWeight::get().reads(6 as u64)) - .saturating_add(RocksDbWeight::get().writes(6 as u64)) + // Proof Size summary in bytes: + // Measured: `416 + p * (40 ±0)` + // Estimated: `4745` + // Minimum execution time: 221_703 nanoseconds. + Weight::from_parts(39_358_497, 4745) + // Standard Error: 85_573 + .saturating_add(Weight::from_ref_time(40_593_280).saturating_mul(p.into())) + // Standard Error: 7_808 + .saturating_add(Weight::from_ref_time(1_529_400).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } } diff --git a/modules/messages/README.md b/modules/messages/README.md index 0d1ea7fe44a..b717db6ad62 100644 --- a/modules/messages/README.md +++ b/modules/messages/README.md @@ -43,10 +43,10 @@ Instead, it provides runtime-internal method that allows other pallets (or other outbound messages. The message "appears" when some runtime code calls the `send_message()` method of the pallet. -The submitter specifies the lane that he's willing to use and the message itself. If some fee must be -paid for sending the message, it must be paid outside of the pallet. If a message passes all checks -(that include, for example, message size check, disabled lane check, ...), the nonce is assigned and the -message is stored in the module storage. The message is in an "undelivered" state now. +The submitter specifies the lane that they're willing to use and the message itself. If some fee must +be paid for sending the message, it must be paid outside of the pallet. If a message passes all checks +(that include, for example, message size check, disabled lane check, ...), the nonce is assigned and +the message is stored in the module storage. The message is in an "undelivered" state now. We assume that there are external, offchain actors, called relayers, that are submitting module related transactions to both target and source chains. The pallet itself has no assumptions about @@ -57,17 +57,17 @@ for details. Eventually, some relayer would notice this message in the "undelivered" state and it would decide to deliver this message. Relayer then crafts `receive_messages_proof()` transaction (aka delivery transaction) for the messages module instance, deployed at the target chain. Relayer provides -his account id at the source chain, the proof of message (or several messages), the number of +its account id at the source chain, the proof of message (or several messages), the number of messages in the transaction and their cumulative dispatch weight. Once a transaction is mined, the message is considered "delivered". Once a message is delivered, the relayer may want to confirm delivery back to the source chain. -There are two reasons why he would want to do that. The first is that we intentionally limit number +There are two reasons why it would want to do that. The first is that we intentionally limit number of "delivered", but not yet "confirmed" messages at inbound lanes (see [What about other Constants in the Messages Module Configuration Trait](#What-about-other-Constants-in-the-Messages-Module-Configuration-Trait) for explanation). So at some point, the target chain may stop accepting new messages until relayers confirm some of -these. The second is that if the relayer wants to be rewarded for delivery, he must prove the fact -that he has actually delivered the message. And this proof may only be generated after the delivery +these. The second is that if the relayer wants to be rewarded for delivery, it must prove the fact +that it has actually delivered the message. And this proof may only be generated after the delivery transaction is mined. So relayer crafts the `receive_messages_delivery_proof()` transaction (aka confirmation transaction) for the messages module instance, deployed at the source chain. Once this transaction is mined, the message is considered "confirmed". @@ -84,8 +84,8 @@ relayer sometimes includes a nonce of the latest "confirmed" message in the next As it has been said above, the messages module supports both outbound and inbound message lanes. So if we will integrate a module in some runtime, it may act as the source chain runtime for outbound messages and as the target chain runtime for inbound messages. In this section, we'll -sometimes refer to the chain we're currently integrating with, as this chain and the other chain as -bridged chain. +sometimes refer to the chain we're currently integrating with, as "this chain" and the other +chain as "bridged chain". Messages module doesn't simply accept transactions that are claiming that the bridged chain has some updated data for us. Instead of this, the module assumes that the bridged chain is able to @@ -94,9 +94,9 @@ In our Substrate-to-Substrate bridge we're using runtime storage proofs. Other b transaction proofs, Substrate header digests or anything else that may be proved. **IMPORTANT NOTE**: everything below in this chapter describes details of the messages module -configuration. But if you interested in well-probed and relatively easy integration of two +configuration. But if you're interested in well-probed and relatively easy integration of two Substrate-based chains, you may want to look at the -[bridge-runtime-common](../../bin/runtime-common/README.md) crate. This crate is providing a lot of +[bridge-runtime-common](../../bin/runtime-common/) crate. This crate is providing a lot of helpers for integration, which may be directly used from within your runtime. Then if you'll decide to change something in this scheme, get back here for detailed information. @@ -153,7 +153,7 @@ The last type is the `pallet_bridge_messages::Config::DeliveryConfirmationPaymen transaction is received, we call the `pay_reward()` method, passing the range of delivered messages. You may use the [`pallet-bridge-relayers`](../relayers/) pallet and its [`DeliveryConfirmationPaymentsAdapter`](../relayers/src/payment_adapter.rs) adapter as a possible -implementation. It allows you to pay fixed reward for relaying the message and some its portion +implementation. It allows you to pay fixed reward for relaying the message and some of its portion for confirming delivery. ### I have a Messages Module in my Runtime, but I Want to Reject all Outbound Messages. What shall I do? @@ -183,7 +183,7 @@ implements all required traits and will simply reject all transactions, related ### What about other Constants in the Messages Module Configuration Trait? -Two setttings that are used to check messages in the `send_message()` function. The +Two settings that are used to check messages in the `send_message()` function. The `pallet_bridge_messages::Config::ActiveOutboundLanes` is an array of all message lanes, that may be used to send messages. All messages sent using other lanes are rejected. All messages that have size above `pallet_bridge_messages::Config::MaximalOutboundPayloadSize` will also be rejected. diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index 680518d9dc8..eaa681df38f 100644 --- a/modules/messages/src/lib.rs +++ b/modules/messages/src/lib.rs @@ -398,7 +398,7 @@ pub mod pallet { Self::deposit_event(Event::MessagesReceived(messages_received_status)); - Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::No }) + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) } /// Receive messages delivery proof from bridged chain. @@ -899,6 +899,7 @@ mod tests { use bp_test_utils::generate_owned_bridge_module_tests; use frame_support::{ assert_noop, assert_ok, + dispatch::Pays, storage::generator::{StorageMap, StorageValue}, traits::Hooks, weights::Weight, @@ -1270,7 +1271,7 @@ mod tests { TEST_RELAYER_A, Err(()).into(), 1, - Weight::from_ref_time(0), + Weight::zero(), ), Error::::InvalidMessagesProof, ); @@ -1286,7 +1287,7 @@ mod tests { TEST_RELAYER_A, Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), u32::MAX, - Weight::from_ref_time(0), + Weight::zero(), ), Error::::TooManyMessagesInTheProof, ); @@ -1478,8 +1479,8 @@ mod tests { TEST_RELAYER_A, Ok(vec![invalid_message]).into(), 1, - Weight::from_ref_time(0), /* weight may be zero in this case (all messages are - * improperly encoded) */ + Weight::zero(), /* weight may be zero in this case (all messages are + * improperly encoded) */ ),); assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 1,); @@ -1543,16 +1544,19 @@ mod tests { messages_count, REGULAR_PAYLOAD.declared_weight, ); - let post_dispatch_weight = Pallet::::receive_messages_proof( + let result = Pallet::::receive_messages_proof( RuntimeOrigin::signed(1), TEST_RELAYER_A, proof, messages_count, REGULAR_PAYLOAD.declared_weight, ) - .expect("delivery has failed") - .actual_weight - .expect("receive_messages_proof always returns Some"); + .expect("delivery has failed"); + let post_dispatch_weight = + result.actual_weight.expect("receive_messages_proof always returns Some"); + + // message delivery transactions are never free + assert_eq!(result.pays_fee, Pays::Yes); (pre_dispatch_weight, post_dispatch_weight) } @@ -1706,11 +1710,7 @@ mod tests { Pallet::::inbound_message_data( TEST_LANE_ID, REGULAR_PAYLOAD.encode(), - OutboundMessageDetails { - nonce: 0, - dispatch_weight: Weight::from_ref_time(0), - size: 0, - }, + OutboundMessageDetails { nonce: 0, dispatch_weight: Weight::zero(), size: 0 }, ), InboundMessageDetails { dispatch_weight: REGULAR_PAYLOAD.declared_weight }, ); @@ -1928,12 +1928,12 @@ mod tests { AccountId, TestMessagesProof, TestMessagesDeliveryProof, - >::receive_messages_proof( - account_id, - message_proof, - 1, - REGULAR_PAYLOAD.declared_weight, - ); + >::receive_messages_proof { + relayer_id_at_bridged_chain: account_id, + proof: message_proof, + messages_count: 1, + dispatch_weight: REGULAR_PAYLOAD.declared_weight, + }; assert_eq!( direct_receive_messages_proof_call.encode(), indirect_receive_messages_proof_call.encode() @@ -1948,10 +1948,10 @@ mod tests { AccountId, TestMessagesProof, TestMessagesDeliveryProof, - >::receive_messages_delivery_proof( - message_delivery_proof, - unrewarded_relayer_state, - ); + >::receive_messages_delivery_proof { + proof: message_delivery_proof, + relayers_state: unrewarded_relayer_state, + }; assert_eq!( direct_receive_messages_delivery_proof_call.encode(), indirect_receive_messages_delivery_proof_call.encode() diff --git a/modules/messages/src/mock.rs b/modules/messages/src/mock.rs index c628ab2cbb3..bd10de09c7f 100644 --- a/modules/messages/src/mock.rs +++ b/modules/messages/src/mock.rs @@ -374,7 +374,7 @@ impl MessageDispatch for TestMessageDispatch { fn dispatch_weight(message: &mut DispatchMessage) -> Weight { match message.data.payload.as_ref() { Ok(payload) => payload.declared_weight, - Err(_) => Weight::from_ref_time(0), + Err(_) => Weight::zero(), } } diff --git a/modules/messages/src/weights.rs b/modules/messages/src/weights.rs index 344b9587d68..0e258508885 100644 --- a/modules/messages/src/weights.rs +++ b/modules/messages/src/weights.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Autogenerated weights for `pallet_bridge_messages` +//! Autogenerated weights for pallet_bridge_messages //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-12, STEPS: 50, REPEAT: 20 -//! LOW RANGE: [], HIGH RANGE: [] -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled -//! CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2023-02-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // target/release/millau-bridge-node @@ -48,7 +48,7 @@ use frame_support::{ }; use sp_std::marker::PhantomData; -/// Weight functions needed for `pallet_bridge_messages`. +/// Weight functions needed for pallet_bridge_messages. pub trait WeightInfo { fn receive_single_message_proof() -> Weight; fn receive_two_messages_proof() -> Weight; @@ -65,88 +65,436 @@ pub trait WeightInfo { /// Those weights are test only and must never be used in production. pub struct BridgeWeight(PhantomData); impl WeightInfo for BridgeWeight { + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + /// + /// Storage: Balances TotalIssuance (r:1 w:1) + /// + /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: + /// MaxEncodedLen) fn receive_single_message_proof() -> Weight { - Weight::from_ref_time(95_401_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `693` + // Estimated: `55198` + // Minimum execution time: 47_968 nanoseconds. + Weight::from_parts(48_937_000, 55198) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + /// + /// Storage: Balances TotalIssuance (r:1 w:1) + /// + /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: + /// MaxEncodedLen) fn receive_two_messages_proof() -> Weight { - Weight::from_ref_time(127_794_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `693` + // Estimated: `55198` + // Minimum execution time: 63_831 nanoseconds. + Weight::from_parts(85_093_000, 55198) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + /// + /// Storage: Balances TotalIssuance (r:1 w:1) + /// + /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: + /// MaxEncodedLen) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - Weight::from_ref_time(105_698_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `693` + // Estimated: `55198` + // Minimum execution time: 53_775 nanoseconds. + Weight::from_parts(55_113_000, 55198) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) fn receive_single_message_proof_1_kb() -> Weight { - Weight::from_ref_time(92_963_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `54695` + // Minimum execution time: 54_314 nanoseconds. + Weight::from_parts(55_804_000, 54695) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) fn receive_single_message_proof_16_kb() -> Weight { - Weight::from_ref_time(158_449_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `54695` + // Minimum execution time: 103_050 nanoseconds. + Weight::from_parts(106_715_000, 54695) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: None, max_size: Some(44), added: + /// 2519, mode: MaxEncodedLen) + /// + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// mode: MaxEncodedLen) fn receive_delivery_proof_for_single_message() -> Weight { - Weight::from_ref_time(72_085_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `579` + // Estimated: `8094` + // Minimum execution time: 42_111 nanoseconds. + Weight::from_parts(43_168_000, 8094) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: None, max_size: Some(44), added: + /// 2519, mode: MaxEncodedLen) + /// + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - Weight::from_ref_time(70_889_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `596` + // Estimated: `8094` + // Minimum execution time: 40_094 nanoseconds. + Weight::from_parts(41_140_000, 8094) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: None, max_size: Some(44), added: + /// 2519, mode: MaxEncodedLen) + /// + /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - Weight::from_ref_time(78_211_000 as u64) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `596` + // Estimated: `10629` + // Minimum execution time: 42_498 nanoseconds. + Weight::from_parts(43_494_000, 10629) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + /// + /// Storage: Balances TotalIssuance (r:1 w:1) + /// + /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: + /// MaxEncodedLen) fn receive_single_message_proof() -> Weight { - Weight::from_ref_time(95_401_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `693` + // Estimated: `55198` + // Minimum execution time: 47_968 nanoseconds. + Weight::from_parts(48_937_000, 55198) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + /// + /// Storage: Balances TotalIssuance (r:1 w:1) + /// + /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: + /// MaxEncodedLen) fn receive_two_messages_proof() -> Weight { - Weight::from_ref_time(127_794_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `693` + // Estimated: `55198` + // Minimum execution time: 63_831 nanoseconds. + Weight::from_parts(85_093_000, 55198) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + /// + /// Storage: Balances TotalIssuance (r:1 w:1) + /// + /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: + /// MaxEncodedLen) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - Weight::from_ref_time(105_698_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `693` + // Estimated: `55198` + // Minimum execution time: 53_775 nanoseconds. + Weight::from_parts(55_113_000, 55198) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) fn receive_single_message_proof_1_kb() -> Weight { - Weight::from_ref_time(92_963_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `54695` + // Minimum execution time: 54_314 nanoseconds. + Weight::from_parts(55_804_000, 54695) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) fn receive_single_message_proof_16_kb() -> Weight { - Weight::from_ref_time(158_449_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `54695` + // Minimum execution time: 103_050 nanoseconds. + Weight::from_parts(106_715_000, 54695) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: None, max_size: Some(44), added: + /// 2519, mode: MaxEncodedLen) + /// + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// mode: MaxEncodedLen) fn receive_delivery_proof_for_single_message() -> Weight { - Weight::from_ref_time(72_085_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `579` + // Estimated: `8094` + // Minimum execution time: 42_111 nanoseconds. + Weight::from_parts(43_168_000, 8094) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: None, max_size: Some(44), added: + /// 2519, mode: MaxEncodedLen) + /// + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - Weight::from_ref_time(70_889_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `596` + // Estimated: `8094` + // Minimum execution time: 40_094 nanoseconds. + Weight::from_parts(41_140_000, 8094) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: None, max_size: Some(44), added: + /// 2519, mode: MaxEncodedLen) + /// + /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - Weight::from_ref_time(78_211_000 as u64) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `596` + // Estimated: `10629` + // Minimum execution time: 42_498 nanoseconds. + Weight::from_parts(43_494_000, 10629) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } } diff --git a/modules/messages/src/weights_ext.rs b/modules/messages/src/weights_ext.rs index b763ca69024..c764f5b8f65 100644 --- a/modules/messages/src/weights_ext.rs +++ b/modules/messages/src/weights_ext.rs @@ -45,6 +45,20 @@ pub fn ensure_weights_are_correct() { // verify `receive_messages_delivery_proof` weight components assert_ne!(W::receive_messages_delivery_proof_overhead(), Weight::zero()); assert_ne!(W::storage_proof_size_overhead(1), Weight::zero()); + + // verify `receive_message_proof` weight + let receive_messages_proof_weight = + W::receive_messages_proof_weight(&PreComputedSize(1), 10, Weight::zero()); + assert_ne!(receive_messages_proof_weight.ref_time(), 0); + assert_ne!(receive_messages_proof_weight.proof_size(), 0); + + // verify `receive_message_proof` weight + let receive_messages_delivery_proof_weight = W::receive_messages_delivery_proof_weight( + &PreComputedSize(1), + &UnrewardedRelayersState::default(), + ); + assert_ne!(receive_messages_delivery_proof_weight.ref_time(), 0); + assert_ne!(receive_messages_delivery_proof_weight.proof_size(), 0); } /// Ensure that we're able to receive maximal (by-size and by-weight) message from other chain. diff --git a/modules/parachains/src/lib.rs b/modules/parachains/src/lib.rs index 47ab91928c7..e2f2cbf2d49 100644 --- a/modules/parachains/src/lib.rs +++ b/modules/parachains/src/lib.rs @@ -33,6 +33,13 @@ use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain, StorageProofErr use frame_support::dispatch::PostDispatchInfo; use sp_std::{marker::PhantomData, vec::Vec}; +#[cfg(feature = "runtime-benchmarks")] +use bp_parachains::ParaStoredHeaderDataBuilder; +#[cfg(feature = "runtime-benchmarks")] +use bp_runtime::HeaderOf; +#[cfg(feature = "runtime-benchmarks")] +use codec::Encode; + // Re-export in crate namespace for `construct_runtime!`. pub use pallet::*; @@ -654,6 +661,25 @@ impl, I: 'static, C: Parachain> HeaderChain } } +/// (Re)initialize pallet with given header for using it in `pallet-bridge-messages` benchmarks. +#[cfg(feature = "runtime-benchmarks")] +pub fn initialize_for_benchmarks, I: 'static, PC: Parachain>( + header: HeaderOf, +) { + let parachain = ParaId(PC::PARACHAIN_ID); + let parachain_head = ParaHead(header.encode()); + let updated_head_data = T::ParaStoredHeaderDataBuilder::try_build(parachain, ¶chain_head) + .expect("failed to build stored parachain head in benchmarks"); + Pallet::::update_parachain_head( + parachain, + None, + 0, + updated_head_data, + parachain_head.hash(), + ) + .expect("failed to insert parachain head in benchmarks"); +} + #[cfg(test)] mod tests { use super::*; @@ -1487,8 +1513,11 @@ mod tests { parachains: parachains.clone(), parachain_heads_proof: proof.clone(), }; - let indirect_submit_parachain_heads_call = - BridgeParachainCall::submit_parachain_heads(relay_header_id, parachains, proof); + let indirect_submit_parachain_heads_call = BridgeParachainCall::submit_parachain_heads { + at_relay_block: relay_header_id, + parachains, + parachain_heads_proof: proof, + }; assert_eq!( direct_submit_parachain_heads_call.encode(), indirect_submit_parachain_heads_call.encode() diff --git a/modules/parachains/src/weights.rs b/modules/parachains/src/weights.rs index 54f468b7636..f6dc73d40c3 100644 --- a/modules/parachains/src/weights.rs +++ b/modules/parachains/src/weights.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Autogenerated weights for `pallet_bridge_parachains` +//! Autogenerated weights for pallet_bridge_parachains //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-21, STEPS: 50, REPEAT: 20 -//! LOW RANGE: [], HIGH RANGE: [] -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled -//! CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2023-02-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // target/release/millau-bridge-node @@ -48,7 +48,7 @@ use frame_support::{ }; use sp_std::marker::PhantomData; -/// Weight functions needed for `pallet_bridge_parachains`. +/// Weight functions needed for pallet_bridge_parachains. pub trait WeightInfo { fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight; fn submit_parachain_heads_with_1kb_proof() -> Weight; @@ -60,38 +60,214 @@ pub trait WeightInfo { /// Those weights are test only and must never be used in production. pub struct BridgeWeight(PhantomData); impl WeightInfo for BridgeWeight { - fn submit_parachain_heads_with_n_parachains(_p: u32) -> Weight { - Weight::from_ref_time(52_445_014 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + /// Storage: BridgeRialtoParachains PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535, + /// mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64), + /// added: 2539, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196), + /// added: 2671, mode: MaxEncodedLen) + /// + /// The range of component `p` is `[1, 2]`. + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `8113` + // Minimum execution time: 35_348 nanoseconds. + Weight::from_parts(36_906_961, 8113) + // Standard Error: 136_143 + .saturating_add(Weight::from_ref_time(148_169).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } + /// Storage: BridgeRialtoParachains PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535, + /// mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64), + /// added: 2539, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196), + /// added: 2671, mode: MaxEncodedLen) fn submit_parachain_heads_with_1kb_proof() -> Weight { - Weight::from_ref_time(55_253_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `8113` + // Minimum execution time: 43_295 nanoseconds. + Weight::from_parts(48_018_000, 8113) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } + /// Storage: BridgeRialtoParachains PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535, + /// mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64), + /// added: 2539, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196), + /// added: 2671, mode: MaxEncodedLen) fn submit_parachain_heads_with_16kb_proof() -> Weight { - Weight::from_ref_time(98_772_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `8113` + // Minimum execution time: 86_112 nanoseconds. + Weight::from_parts(88_901_000, 8113) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - fn submit_parachain_heads_with_n_parachains(_p: u32) -> Weight { - Weight::from_ref_time(52_445_014 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + /// Storage: BridgeRialtoParachains PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535, + /// mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64), + /// added: 2539, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196), + /// added: 2671, mode: MaxEncodedLen) + /// + /// The range of component `p` is `[1, 2]`. + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `8113` + // Minimum execution time: 35_348 nanoseconds. + Weight::from_parts(36_906_961, 8113) + // Standard Error: 136_143 + .saturating_add(Weight::from_ref_time(148_169).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } + /// Storage: BridgeRialtoParachains PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535, + /// mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64), + /// added: 2539, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196), + /// added: 2671, mode: MaxEncodedLen) fn submit_parachain_heads_with_1kb_proof() -> Weight { - Weight::from_ref_time(55_253_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `8113` + // Minimum execution time: 43_295 nanoseconds. + Weight::from_parts(48_018_000, 8113) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } + /// Storage: BridgeRialtoParachains PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: + /// 2543, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535, + /// mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64), + /// added: 2539, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196), + /// added: 2671, mode: MaxEncodedLen) fn submit_parachain_heads_with_16kb_proof() -> Weight { - Weight::from_ref_time(98_772_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `8113` + // Minimum execution time: 86_112 nanoseconds. + Weight::from_parts(88_901_000, 8113) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } } diff --git a/modules/relayers/src/weights.rs b/modules/relayers/src/weights.rs index 9bbaa343e12..fdaeee4f22a 100644 --- a/modules/relayers/src/weights.rs +++ b/modules/relayers/src/weights.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Autogenerated weights for `pallet_bridge_relayers` +//! Autogenerated weights for pallet_bridge_relayers //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-21, STEPS: 50, REPEAT: 20 -//! LOW RANGE: [], HIGH RANGE: [] -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled -//! CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2023-02-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // target/release/millau-bridge-node @@ -48,7 +48,7 @@ use frame_support::{ }; use sp_std::marker::PhantomData; -/// Weight functions needed for `pallet_bridge_relayers`. +/// Weight functions needed for pallet_bridge_relayers. pub trait WeightInfo { fn claim_rewards() -> Weight; } @@ -58,18 +58,44 @@ pub trait WeightInfo { /// Those weights are test only and must never be used in production. pub struct BridgeWeight(PhantomData); impl WeightInfo for BridgeWeight { + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// mode: MaxEncodedLen) + /// + /// Storage: System Account (r:1 w:1) + /// + /// Proof: System Account (max_values: None, max_size: Some(96), added: 2571, mode: + /// MaxEncodedLen) fn claim_rewards() -> Weight { - Weight::from_ref_time(64_832_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `534` + // Estimated: `5106` + // Minimum execution time: 48_239 nanoseconds. + Weight::from_parts(50_579_000, 5106) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// mode: MaxEncodedLen) + /// + /// Storage: System Account (r:1 w:1) + /// + /// Proof: System Account (max_values: None, max_size: Some(96), added: 2571, mode: + /// MaxEncodedLen) fn claim_rewards() -> Weight { - Weight::from_ref_time(64_832_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `534` + // Estimated: `5106` + // Minimum execution time: 48_239 nanoseconds. + Weight::from_parts(50_579_000, 5106) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } diff --git a/primitives/beefy/Cargo.toml b/primitives/beefy/Cargo.toml index cc8360c7106..58cc268e698 100644 --- a/primitives/beefy/Cargo.toml +++ b/primitives/beefy/Cargo.toml @@ -17,7 +17,7 @@ bp-runtime = { path = "../runtime", default-features = false } # Substrate Dependencies -beefy-merkle-tree = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +binary-merkle-tree = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } diff --git a/primitives/beefy/src/lib.rs b/primitives/beefy/src/lib.rs index 8b2b05d551f..042d1d0e661 100644 --- a/primitives/beefy/src/lib.rs +++ b/primitives/beefy/src/lib.rs @@ -19,7 +19,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs)] -pub use beefy_merkle_tree::{merkle_root, Keccak256 as BeefyKeccak256}; +pub use binary_merkle_tree::merkle_root; pub use pallet_beefy_mmr::BeefyEcdsaToEthereum; pub use pallet_mmr::{ primitives::{DataOrHash as MmrDataOrHash, Proof as MmrProof}, diff --git a/primitives/chain-bridge-hub-cumulus/Cargo.toml b/primitives/chain-bridge-hub-cumulus/Cargo.toml index 8896522951b..cb7f55d3361 100644 --- a/primitives/chain-bridge-hub-cumulus/Cargo.toml +++ b/primitives/chain-bridge-hub-cumulus/Cargo.toml @@ -17,6 +17,7 @@ bp-messages = { path = "../../primitives/messages", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } # Polkadot Dependencies polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } @@ -29,5 +30,6 @@ std = [ "frame-system/std", "frame-support/std", "sp-api/std", + "sp-std/std", "polkadot-primitives/std", ] diff --git a/primitives/chain-bridge-hub-cumulus/src/lib.rs b/primitives/chain-bridge-hub-cumulus/src/lib.rs index 6eab5a53a04..dfa5afe2a5b 100644 --- a/primitives/chain-bridge-hub-cumulus/src/lib.rs +++ b/primitives/chain-bridge-hub-cumulus/src/lib.rs @@ -29,6 +29,12 @@ use frame_support::{ weights::constants, }; use frame_system::limits; +use sp_std::time::Duration; + +/// Average block interval in Cumulus-based parachains. +/// +/// Corresponds to the `MILLISECS_PER_BLOCK` from `parachains_common` crate. +pub const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(12); /// All cumulus bridge hubs allow normal extrinsics to fill block up to 75 percent. /// @@ -39,7 +45,6 @@ pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); /// time. /// /// This is a copy-paste from the cumulus repo's `parachains-common` crate. -// TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_ref_time(constants::WEIGHT_REF_TIME_PER_SECOND) .saturating_div(2) .set_proof_size(polkadot_primitives::v2::MAX_POV_SIZE as u64); @@ -56,9 +61,12 @@ parameter_types! { NORMAL_DISPATCH_RATIO, ); - pub const BlockExecutionWeight: Weight = Weight::from_ref_time(constants::WEIGHT_REF_TIME_PER_NANOS).saturating_mul(5_000_000); - - pub const ExtrinsicBaseWeight: Weight = Weight::from_ref_time(constants::WEIGHT_REF_TIME_PER_NANOS).saturating_mul(125_000); + /// Importing a block with 0 Extrinsics. + pub const BlockExecutionWeight: Weight = Weight::from_ref_time(constants::WEIGHT_REF_TIME_PER_NANOS) + .saturating_mul(5_000_000); + /// Executing a NO-OP `System::remarks` Extrinsic. + pub const ExtrinsicBaseWeight: Weight = Weight::from_ref_time(constants::WEIGHT_REF_TIME_PER_NANOS) + .saturating_mul(125_000); pub BlockWeights: limits::BlockWeights = limits::BlockWeights::builder() .base_block(BlockExecutionWeight::get()) diff --git a/primitives/chain-millau/Cargo.toml b/primitives/chain-millau/Cargo.toml index abcbd0f5123..7600781d891 100644 --- a/primitives/chain-millau/Cargo.toml +++ b/primitives/chain-millau/Cargo.toml @@ -17,7 +17,7 @@ fixed-hash = { version = "0.8.0", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } impl-codec = { version = "0.6", default-features = false } impl-serde = { version = "0.4.0", optional = true } -parity-util-mem = { version = "0.12", default-features = false, features = ["primitive-types"] } +parity-util-mem = { version = "0.12.0", default-features = false, features = ["primitive-types"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } diff --git a/primitives/chain-millau/src/lib.rs b/primitives/chain-millau/src/lib.rs index afe8fbca69c..73515a7f409 100644 --- a/primitives/chain-millau/src/lib.rs +++ b/primitives/chain-millau/src/lib.rs @@ -59,10 +59,10 @@ pub const TX_EXTRA_BYTES: u32 = 103; /// Maximum weight of single Millau block. /// /// This represents 0.5 seconds of compute assuming a target block time of six seconds. -// TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` -pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_ref_time(WEIGHT_REF_TIME_PER_SECOND) - .set_proof_size(1_000) - .saturating_div(2); +/// +/// Max PoV size is set to max value, since it isn't important for relay/standalone chains. +pub const MAXIMUM_BLOCK_WEIGHT: Weight = + Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), u64::MAX); /// Represents the portion of a block that will be used by Normal extrinsics. pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); diff --git a/primitives/chain-rialto-parachain/src/lib.rs b/primitives/chain-rialto-parachain/src/lib.rs index d39fe253783..e49f3d14dc5 100644 --- a/primitives/chain-rialto-parachain/src/lib.rs +++ b/primitives/chain-rialto-parachain/src/lib.rs @@ -54,10 +54,10 @@ pub const TX_EXTRA_BYTES: u32 = 104; /// Maximal weight of single RialtoParachain block. /// /// This represents two seconds of compute assuming a target block time of six seconds. -// TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` -pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_ref_time(WEIGHT_REF_TIME_PER_SECOND) - .set_proof_size(1_000) - .saturating_mul(2); +/// +/// Max PoV size is set to `5Mb` as all Cumulus-based parachains do. +pub const MAXIMUM_BLOCK_WEIGHT: Weight = + Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), 5 * 1024 * 1024); /// Represents the portion of a block that will be used by Normal extrinsics. pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); diff --git a/primitives/chain-rialto/src/lib.rs b/primitives/chain-rialto/src/lib.rs index 5cf4f85f54a..ca0a3dfee88 100644 --- a/primitives/chain-rialto/src/lib.rs +++ b/primitives/chain-rialto/src/lib.rs @@ -48,10 +48,10 @@ pub const TX_EXTRA_BYTES: u32 = 104; /// Maximal weight of single Rialto block. /// /// This represents two seconds of compute assuming a target block time of six seconds. -// TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` -pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_ref_time(WEIGHT_REF_TIME_PER_SECOND) - .set_proof_size(1_000) - .saturating_mul(2); +/// +/// Max PoV size is set to max value, since it isn't important for relay/standalone chains. +pub const MAXIMUM_BLOCK_WEIGHT: Weight = + Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), u64::MAX); /// Represents the portion of a block that will be used by Normal extrinsics. pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); diff --git a/primitives/chain-rococo/src/lib.rs b/primitives/chain-rococo/src/lib.rs index 57a47211e4e..41fc65e0e93 100644 --- a/primitives/chain-rococo/src/lib.rs +++ b/primitives/chain-rococo/src/lib.rs @@ -42,9 +42,4 @@ pub const WITH_ROCOCO_GRANDPA_PALLET_NAME: &str = "BridgeRococoGrandpa"; /// reserve. pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; -/// Maximal number of GRANDPA authorities at Rococo. -/// -/// Corresponds to the `MaxAuthorities` constant value from the Rococo runtime configuration. -pub const MAX_AUTHORITIES_COUNT: u32 = 100_000; - decl_bridge_finality_runtime_apis!(rococo); diff --git a/primitives/chain-westend/src/lib.rs b/primitives/chain-westend/src/lib.rs index 8a3794b418e..910a6c4acbd 100644 --- a/primitives/chain-westend/src/lib.rs +++ b/primitives/chain-westend/src/lib.rs @@ -63,11 +63,6 @@ pub const WITH_WESTEND_GRANDPA_PALLET_NAME: &str = "BridgeWestendGrandpa"; /// Name of the With-Westend parachains bridge pallet instance that is deployed at bridged chains. pub const WITH_WESTEND_BRIDGE_PARAS_PALLET_NAME: &str = "BridgeWestendParachains"; -/// Maximal number of GRANDPA authorities at Westend. -/// -/// Corresponds to the `MaxAuthorities` constant value from the Westend runtime configuration. -pub const MAX_AUTHORITIES_COUNT: u32 = 100_000; - /// Maximal SCALE-encoded size of parachains headers that are stored at Westend `Paras` pallet. /// /// It includes the block number and state root, so it shall be near 40 bytes, but let's have some diff --git a/primitives/header-chain/src/lib.rs b/primitives/header-chain/src/lib.rs index dffa7f7dc6e..14e9395c03c 100644 --- a/primitives/header-chain/src/lib.rs +++ b/primitives/header-chain/src/lib.rs @@ -176,10 +176,13 @@ impl ConsensusLogReader for GrandpaConsensusLogReader { pub enum BridgeGrandpaCall { /// `pallet-bridge-grandpa::Call::submit_finality_proof` #[codec(index = 0)] - submit_finality_proof(Box

, justification::GrandpaJustification
), + submit_finality_proof { + finality_target: Box
, + justification: justification::GrandpaJustification
, + }, /// `pallet-bridge-grandpa::Call::initialize` #[codec(index = 1)] - initialize(InitializationData
), + initialize { init_data: InitializationData
}, } /// The `BridgeGrandpaCall` used by a chain. diff --git a/primitives/messages/src/lib.rs b/primitives/messages/src/lib.rs index 61d475cb46c..754349d634e 100644 --- a/primitives/messages/src/lib.rs +++ b/primitives/messages/src/lib.rs @@ -397,10 +397,18 @@ where pub enum BridgeMessagesCall { /// `pallet-bridge-messages::Call::receive_messages_proof` #[codec(index = 2)] - receive_messages_proof(AccountId, MessagesProof, u32, Weight), + receive_messages_proof { + relayer_id_at_bridged_chain: AccountId, + proof: MessagesProof, + messages_count: u32, + dispatch_weight: Weight, + }, /// `pallet-bridge-messages::Call::receive_messages_delivery_proof` #[codec(index = 3)] - receive_messages_delivery_proof(MessagesDeliveryProof, UnrewardedRelayersState), + receive_messages_delivery_proof { + proof: MessagesDeliveryProof, + relayers_state: UnrewardedRelayersState, + }, } #[cfg(test)] diff --git a/primitives/parachains/src/lib.rs b/primitives/parachains/src/lib.rs index 6b38d648d79..388a995fae6 100644 --- a/primitives/parachains/src/lib.rs +++ b/primitives/parachains/src/lib.rs @@ -157,9 +157,9 @@ impl ParaStoredHeaderDataBuilder for C { pub enum BridgeParachainCall { /// `pallet-bridge-parachains::Call::submit_parachain_heads` #[codec(index = 0)] - submit_parachain_heads( - (RelayBlockNumber, RelayBlockHash), - Vec<(ParaId, ParaHash)>, - ParaHeadsProof, - ), + submit_parachain_heads { + at_relay_block: (RelayBlockNumber, RelayBlockHash), + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + }, } diff --git a/primitives/polkadot-core/src/lib.rs b/primitives/polkadot-core/src/lib.rs index d5a9b5de817..b13ceb5df50 100644 --- a/primitives/polkadot-core/src/lib.rs +++ b/primitives/polkadot-core/src/lib.rs @@ -43,6 +43,16 @@ pub use sp_runtime::{traits::Convert, Perbill}; pub mod parachains; +/// Maximal number of GRANDPA authorities at Polkadot-like chains. +/// +/// Ideally, we would set it to the value of `MaxAuthorities` constant from bridged runtime +/// configurations. But right now it is set to the `100_000`, which makes PoV size for +/// our bridge hub parachains huge. So let's stick to the real-world value here. +/// +/// Right now both Kusama and Polkadot aim to have around 1000 validators. Let's be safe here and +/// take twice as much here. +pub const MAX_AUTHORITIES_COUNT: u32 = 2_048; + /// Number of extra bytes (excluding size of storage value itself) of storage proof, built at /// Polkadot-like chain. This mostly depends on number of entries in the storage trie. /// Some reserve is reserved to account future chain growth. @@ -71,10 +81,8 @@ const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); /// All Polkadot-like chains allow 2 seconds of compute with a 6-second average block time. /// /// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. -// TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` -pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_ref_time(WEIGHT_REF_TIME_PER_SECOND) - .set_proof_size(1_000) - .saturating_mul(2); +pub const MAXIMUM_BLOCK_WEIGHT: Weight = + Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), u64::MAX); /// All Polkadot-like chains assume that an on-initialize consumes 1 percent of the weight on /// average, hence a single extrinsic will not be allowed to consume more than diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index f8d698696de..515d6e1a6c4 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -24,7 +24,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -trie-db = { version = "0.24.0", default-features = false } +trie-db = { version = "0.25.0", default-features = false } [dev-dependencies] hex-literal = "0.3" diff --git a/relays/bin-substrate/Cargo.toml b/relays/bin-substrate/Cargo.toml index 050be02e37e..1a16f3466f3 100644 --- a/relays/bin-substrate/Cargo.toml +++ b/relays/bin-substrate/Cargo.toml @@ -10,7 +10,7 @@ anyhow = "1.0" async-std = "1.9.0" async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.1.5" } -futures = "0.3.12" +futures = "0.3.26" hex = "0.4" log = "0.4.17" num-format = "0.4" diff --git a/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs index 8422569d009..b48ace58987 100644 --- a/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs +++ b/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs @@ -32,14 +32,14 @@ impl MessagesCliBridge for BridgeHubRococoToBridgeHubWococoMessagesCliBridge { type MessagesLane = BridgeHubRococoMessagesToBridgeHubWococoMessageLane; } -substrate_relay_helper::generate_mocked_receive_message_proof_call_builder!( +substrate_relay_helper::generate_receive_message_proof_call_builder!( BridgeHubRococoMessagesToBridgeHubWococoMessageLane, BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesProofCallBuilder, relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoMessages, relay_bridge_hub_wococo_client::runtime::BridgeRococoMessagesCall::receive_messages_proof ); -substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_builder!( +substrate_relay_helper::generate_receive_message_delivery_proof_call_builder!( BridgeHubRococoMessagesToBridgeHubWococoMessageLane, BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesDeliveryProofCallBuilder, relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoMessages, diff --git a/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs index 96df83cd0b2..3bcf62de333 100644 --- a/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs +++ b/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs @@ -32,14 +32,14 @@ impl MessagesCliBridge for BridgeHubWococoToBridgeHubRococoMessagesCliBridge { type MessagesLane = BridgeHubWococoMessagesToBridgeHubRococoMessageLane; } -substrate_relay_helper::generate_mocked_receive_message_proof_call_builder!( +substrate_relay_helper::generate_receive_message_proof_call_builder!( BridgeHubWococoMessagesToBridgeHubRococoMessageLane, BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesProofCallBuilder, relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoMessages, relay_bridge_hub_rococo_client::runtime::BridgeWococoMessagesCall::receive_messages_proof ); -substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_builder!( +substrate_relay_helper::generate_receive_message_delivery_proof_call_builder!( BridgeHubWococoMessagesToBridgeHubRococoMessageLane, BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesDeliveryProofCallBuilder, relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoMessages, diff --git a/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs b/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs index 6496167cf7c..4a99d73147b 100644 --- a/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs +++ b/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs @@ -39,11 +39,11 @@ use substrate_relay_helper::finality::{ engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline, }; -substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!( +substrate_relay_helper::generate_submit_finality_proof_call_builder!( MillauFinalityToRialtoParachain, MillauFinalityToRialtoParachainCallBuilder, - relay_rialto_parachain_client::runtime::Call::BridgeMillauGrandpa, - relay_rialto_parachain_client::runtime::BridgeMillauGrandpaCall::submit_finality_proof + relay_rialto_parachain_client::RuntimeCall::BridgeMillauGrandpa, + relay_rialto_parachain_client::BridgeGrandpaCall::submit_finality_proof ); /// Description of Millau -> Rialto finalized headers bridge. diff --git a/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs b/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs index 817d0098d9b..5dbe1d69d13 100644 --- a/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs +++ b/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs @@ -22,11 +22,11 @@ use substrate_relay_helper::messages_lane::{ DirectReceiveMessagesDeliveryProofCallBuilder, SubstrateMessageLane, }; -substrate_relay_helper::generate_mocked_receive_message_proof_call_builder!( +substrate_relay_helper::generate_receive_message_proof_call_builder!( MillauMessagesToRialtoParachain, MillauMessagesToRialtoParachainReceiveMessagesProofCallBuilder, - relay_rialto_parachain_client::runtime::Call::BridgeMillauMessages, - relay_rialto_parachain_client::runtime::BridgeMillauMessagesCall::receive_messages_proof + relay_rialto_parachain_client::RuntimeCall::BridgeMillauMessages, + relay_rialto_parachain_client::BridgeMessagesCall::receive_messages_proof ); /// Description of Millau -> RialtoParachain messages bridge. diff --git a/relays/bin-substrate/src/chains/rialto_parachain.rs b/relays/bin-substrate/src/chains/rialto_parachain.rs index 54704e72947..1a5ea2e784e 100644 --- a/relays/bin-substrate/src/chains/rialto_parachain.rs +++ b/relays/bin-substrate/src/chains/rialto_parachain.rs @@ -20,7 +20,7 @@ use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain}; use bp_runtime::EncodedOrDecodedCall; use bridge_runtime_common::CustomNetworkId; use relay_rialto_parachain_client::RialtoParachain; -use relay_substrate_client::{calls::XcmCall, SimpleRuntimeVersion}; +use relay_substrate_client::SimpleRuntimeVersion; use xcm::latest::prelude::*; impl CliEncodeMessage for RialtoParachain { @@ -28,6 +28,9 @@ impl CliEncodeMessage for RialtoParachain { message: xcm::VersionedXcm<()>, bridge_instance_index: u8, ) -> anyhow::Result> { + type RuntimeCall = relay_rialto_parachain_client::RuntimeCall; + type XcmCall = relay_rialto_parachain_client::runtime_types::pallet_xcm::pallet::Call; + let dest = match bridge_instance_index { bridge::RIALTO_PARACHAIN_TO_MILLAU_INDEX => (Parent, X1(GlobalConsensus(CustomNetworkId::Millau.as_network_id()))), @@ -37,9 +40,12 @@ impl CliEncodeMessage for RialtoParachain { ), }; - let xcm_call = XcmCall::send(Box::new(dest.into()), Box::new(message)); + let xcm_call = XcmCall::send { + dest: Box::new(unsafe { std::mem::transmute(xcm::VersionedMultiLocation::from(dest)) }), + message: Box::new(unsafe { std::mem::transmute(message) }), + }; - Ok(relay_rialto_parachain_client::runtime::Call::PolkadotXcm(xcm_call).into()) + Ok(RuntimeCall::PolkadotXcm(xcm_call).into()) } } diff --git a/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs b/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs index 0000a0a754a..e19953eac55 100644 --- a/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs +++ b/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs @@ -23,11 +23,11 @@ use substrate_relay_helper::{ UtilityPalletBatchCallBuilder, }; -substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_builder!( +substrate_relay_helper::generate_receive_message_delivery_proof_call_builder!( RialtoParachainMessagesToMillau, RialtoParachainMessagesToMillauReceiveMessagesDeliveryProofCallBuilder, - relay_rialto_parachain_client::runtime::Call::BridgeMillauMessages, - relay_rialto_parachain_client::runtime::BridgeMillauMessagesCall::receive_messages_delivery_proof + relay_rialto_parachain_client::RuntimeCall::BridgeMillauMessages, + relay_rialto_parachain_client::BridgeMessagesCall::receive_messages_delivery_proof ); /// Description of RialtoParachain -> Millau messages bridge. diff --git a/relays/bin-substrate/src/chains/rococo.rs b/relays/bin-substrate/src/chains/rococo.rs index 633c7cfb290..b1e8d1bef85 100644 --- a/relays/bin-substrate/src/chains/rococo.rs +++ b/relays/bin-substrate/src/chains/rococo.rs @@ -27,5 +27,5 @@ impl CliChain for Rococo { impl CliChain for BridgeHubRococo { const RUNTIME_VERSION: Option = - Some(SimpleRuntimeVersion { spec_version: 9302, transaction_version: 1 }); + Some(SimpleRuntimeVersion { spec_version: 9371, transaction_version: 1 }); } diff --git a/relays/bin-substrate/src/chains/rococo_headers_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/chains/rococo_headers_to_bridge_hub_wococo.rs index 0de1fd9396a..2272144311c 100644 --- a/relays/bin-substrate/src/chains/rococo_headers_to_bridge_hub_wococo.rs +++ b/relays/bin-substrate/src/chains/rococo_headers_to_bridge_hub_wococo.rs @@ -29,7 +29,7 @@ use substrate_relay_helper::{ #[derive(Clone, Debug)] pub struct RococoFinalityToBridgeHubWococo; -substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!( +substrate_relay_helper::generate_submit_finality_proof_call_builder!( RococoFinalityToBridgeHubWococo, RococoFinalityToBridgeHubWococoCallBuilder, relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoGrandpa, diff --git a/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs index b028253d8a4..098aed417c6 100644 --- a/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs +++ b/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs @@ -51,11 +51,11 @@ impl SubmitParachainHeadsCallBuilder parachain_heads_proof: ParaHeadsProof, ) -> CallOf { relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoParachain( - relay_bridge_hub_wococo_client::runtime::BridgeParachainCall::submit_parachain_heads( - (at_relay_block.0, at_relay_block.1), + relay_bridge_hub_wococo_client::runtime::BridgeParachainCall::submit_parachain_heads { + at_relay_block: (at_relay_block.0, at_relay_block.1), parachains, parachain_heads_proof, - ), + }, ) } } diff --git a/relays/bin-substrate/src/chains/wococo.rs b/relays/bin-substrate/src/chains/wococo.rs index 66a72e11134..cb8dff620a5 100644 --- a/relays/bin-substrate/src/chains/wococo.rs +++ b/relays/bin-substrate/src/chains/wococo.rs @@ -27,5 +27,5 @@ impl CliChain for Wococo { impl CliChain for BridgeHubWococo { const RUNTIME_VERSION: Option = - Some(SimpleRuntimeVersion { spec_version: 9302, transaction_version: 1 }); + Some(SimpleRuntimeVersion { spec_version: 9371, transaction_version: 1 }); } diff --git a/relays/bin-substrate/src/chains/wococo_headers_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/chains/wococo_headers_to_bridge_hub_rococo.rs index e9054fd31e9..39043009582 100644 --- a/relays/bin-substrate/src/chains/wococo_headers_to_bridge_hub_rococo.rs +++ b/relays/bin-substrate/src/chains/wococo_headers_to_bridge_hub_rococo.rs @@ -29,7 +29,7 @@ use substrate_relay_helper::{ #[derive(Clone, Debug)] pub struct WococoFinalityToBridgeHubRococo; -substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!( +substrate_relay_helper::generate_submit_finality_proof_call_builder!( WococoFinalityToBridgeHubRococo, WococoFinalityToBridgeHubRococoCallBuilder, relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoGrandpa, diff --git a/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs index f79979ff832..683e7705dd6 100644 --- a/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs +++ b/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs @@ -51,11 +51,11 @@ impl SubmitParachainHeadsCallBuilder parachain_heads_proof: ParaHeadsProof, ) -> CallOf { relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoParachain( - bp_parachains::BridgeParachainCall::submit_parachain_heads( - (at_relay_block.0, at_relay_block.1), + bp_parachains::BridgeParachainCall::submit_parachain_heads { + at_relay_block: (at_relay_block.0, at_relay_block.1), parachains, parachain_heads_proof, - ), + }, ) } } diff --git a/relays/bin-substrate/src/cli/init_bridge.rs b/relays/bin-substrate/src/cli/init_bridge.rs index 8f4080b338c..d6df1eaeeaa 100644 --- a/relays/bin-substrate/src/cli/init_bridge.rs +++ b/relays/bin-substrate/src/cli/init_bridge.rs @@ -29,7 +29,7 @@ use crate::{ cli::{bridge::CliBridgeBase, chain_schema::*}, }; use bp_runtime::Chain as ChainBase; -use relay_substrate_client::{calls::SudoCall, AccountKeyPairOf, Chain, UnsignedTransaction}; +use relay_substrate_client::{AccountKeyPairOf, Chain, UnsignedTransaction}; use sp_core::Pair; use structopt::StructOpt; use strum::{EnumString, EnumVariantNames, VariantNames}; @@ -123,13 +123,14 @@ impl BridgeInitializer for MillauToRialtoParachainCliBridge { fn encode_init_bridge( init_data: >::InitializationData, ) -> ::Call { - use relay_rialto_parachain_client::runtime; + type RuntimeCall = relay_rialto_parachain_client::RuntimeCall; + type BridgeGrandpaCall = relay_rialto_parachain_client::BridgeGrandpaCall; + type SudoCall = relay_rialto_parachain_client::SudoCall; - let initialize_call = runtime::Call::BridgeMillauGrandpa( - runtime::BridgeMillauGrandpaCall::initialize(init_data), - ); - let sudo_call = SudoCall::sudo(Box::new(initialize_call)); - runtime::Call::Sudo(sudo_call) + let initialize_call = + RuntimeCall::BridgeMillauGrandpa(BridgeGrandpaCall::initialize { init_data }); + + RuntimeCall::Sudo(SudoCall::sudo { call: Box::new(initialize_call) }) } } @@ -176,7 +177,9 @@ impl BridgeInitializer for RococoToBridgeHubWococoCliBridge { init_data: >::InitializationData, ) -> ::Call { relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoGrandpa( - relay_bridge_hub_wococo_client::runtime::BridgeRococoGrandpaCall::initialize(init_data), + relay_bridge_hub_wococo_client::runtime::BridgeRococoGrandpaCall::initialize { + init_data, + }, ) } } @@ -188,7 +191,9 @@ impl BridgeInitializer for WococoToBridgeHubRococoCliBridge { init_data: >::InitializationData, ) -> ::Call { relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoGrandpa( - relay_bridge_hub_rococo_client::runtime::BridgeWococoGrandpaCall::initialize(init_data), + relay_bridge_hub_rococo_client::runtime::BridgeWococoGrandpaCall::initialize { + init_data, + }, ) } } diff --git a/relays/bin-substrate/src/cli/mod.rs b/relays/bin-substrate/src/cli/mod.rs index 17d0262b21b..a5b90744067 100644 --- a/relays/bin-substrate/src/cli/mod.rs +++ b/relays/bin-substrate/src/cli/mod.rs @@ -260,6 +260,22 @@ pub struct PrometheusParams { #[derive(BuildInfo)] struct SubstrateRelayBuildInfo; +impl SubstrateRelayBuildInfo { + /// Get git commit in form ``. + pub fn get_git_commit() -> String { + // on gitlab we use images without git installed, so we can't use `rbtag` there + // locally we don't have `CI_*` env variables, so we can't rely on them + // => we are using `CI_*` env variables or else `rbtag` + let maybe_sha_from_ci = option_env!("CI_COMMIT_SHORT_SHA"); + maybe_sha_from_ci + .map(|short_sha| { + // we assume that on CI the copy is always clean + format!("{short_sha}-clean") + }) + .unwrap_or_else(|| SubstrateRelayBuildInfo.get_build_commit().into()) + } +} + impl PrometheusParams { /// Tries to convert CLI metrics params into metrics params, used by the relay. pub fn into_metrics_params(self) -> anyhow::Result { @@ -273,11 +289,11 @@ impl PrometheusParams { }; let relay_version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"); - let relay_commit = SubstrateRelayBuildInfo.get_build_commit(); + let relay_commit = SubstrateRelayBuildInfo::get_git_commit(); relay_utils::metrics::MetricsParams::new( metrics_address, relay_version.into(), - relay_commit.into(), + relay_commit, ) .map_err(|e| anyhow::format_err!("{:?}", e)) } diff --git a/relays/client-bridge-hub-rococo/src/lib.rs b/relays/client-bridge-hub-rococo/src/lib.rs index b14a9baa61d..80c075cd0d5 100644 --- a/relays/client-bridge-hub-rococo/src/lib.rs +++ b/relays/client-bridge-hub-rococo/src/lib.rs @@ -16,6 +16,7 @@ //! Types used to connect to the BridgeHub-Rococo-Substrate parachain. +use bp_bridge_hub_rococo::AVERAGE_BLOCK_INTERVAL; use bp_bridge_hub_wococo::PolkadotSignedExtension; use bp_messages::MessageNonce; use codec::Encode; @@ -44,7 +45,7 @@ impl Chain for BridgeHubRococo { const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = bp_bridge_hub_rococo::BEST_FINALIZED_BRIDGE_HUB_ROCOCO_HEADER_METHOD; - const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + const AVERAGE_BLOCK_INTERVAL: Duration = AVERAGE_BLOCK_INTERVAL; type SignedBlock = bp_bridge_hub_rococo::SignedBlock; type Call = runtime::Call; diff --git a/relays/client-bridge-hub-wococo/src/lib.rs b/relays/client-bridge-hub-wococo/src/lib.rs index abc820ed624..7af310bfd41 100644 --- a/relays/client-bridge-hub-wococo/src/lib.rs +++ b/relays/client-bridge-hub-wococo/src/lib.rs @@ -16,7 +16,7 @@ //! Types used to connect to the BridgeHub-Wococo-Substrate parachain. -use bp_bridge_hub_wococo::PolkadotSignedExtension; +use bp_bridge_hub_wococo::{PolkadotSignedExtension, AVERAGE_BLOCK_INTERVAL}; use bp_messages::MessageNonce; use codec::Encode; use relay_substrate_client::{ @@ -44,7 +44,7 @@ impl Chain for BridgeHubWococo { const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = bp_bridge_hub_wococo::BEST_FINALIZED_BRIDGE_HUB_WOCOCO_HEADER_METHOD; - const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + const AVERAGE_BLOCK_INTERVAL: Duration = AVERAGE_BLOCK_INTERVAL; type SignedBlock = bp_bridge_hub_wococo::SignedBlock; type Call = runtime::Call; diff --git a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs index 85f77a6377e..17cc4cbd4e8 100644 --- a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs +++ b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs @@ -96,7 +96,7 @@ mod tests { set_id: 6, operating_mode: BasicOperatingMode::Normal, }; - let call = BridgeRococoGrandpaCall::initialize(init_data); + let call = BridgeRococoGrandpaCall::initialize { init_data }; let tx = Call::BridgeRococoGrandpa(call); // encode call as hex string diff --git a/relays/client-rialto-parachain/Cargo.toml b/relays/client-rialto-parachain/Cargo.toml index f3c4a1d42f8..96929d55cc8 100644 --- a/relays/client-rialto-parachain/Cargo.toml +++ b/relays/client-rialto-parachain/Cargo.toml @@ -8,6 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5" } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +subxt = { git = "https://github.com/paritytech/subxt", branch = "master" } # Bridge dependencies @@ -24,3 +25,4 @@ relay-substrate-client = { path = "../client-substrate" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-weights = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-rialto-parachain/src/codegen_runtime.rs b/relays/client-rialto-parachain/src/codegen_runtime.rs new file mode 100644 index 00000000000..16a539f2843 --- /dev/null +++ b/relays/client-rialto-parachain/src/codegen_runtime.rs @@ -0,0 +1,9830 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Autogenerated runtime API +//! THIS FILE WAS AUTOGENERATED USING parity-bridges-common::runtime-codegen +//! EXECUTED COMMAND: target/debug/runtime-codegen --from-node-url http://localhost:20433 + +#[allow(dead_code, unused_imports, non_camel_case_types)] +#[allow(clippy::all)] +pub mod api { + use super::api as root_mod; + pub static PALLETS: [&str; 17usize] = [ + "System", + "Timestamp", + "Sudo", + "RandomnessCollectiveFlip", + "TransactionPayment", + "ParachainSystem", + "ParachainInfo", + "Balances", + "Aura", + "AuraExt", + "XcmpQueue", + "PolkadotXcm", + "CumulusXcm", + "DmpQueue", + "BridgeRelayers", + "BridgeMillauGrandpa", + "BridgeMillauMessages", + ]; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum Event { + #[codec(index = 0)] + System(system::Event), + #[codec(index = 2)] + Sudo(sudo::Event), + #[codec(index = 4)] + TransactionPayment(transaction_payment::Event), + #[codec(index = 20)] + ParachainSystem(parachain_system::Event), + #[codec(index = 30)] + Balances(balances::Event), + #[codec(index = 50)] + XcmpQueue(xcmp_queue::Event), + #[codec(index = 51)] + PolkadotXcm(polkadot_xcm::Event), + #[codec(index = 52)] + CumulusXcm(cumulus_xcm::Event), + #[codec(index = 53)] + DmpQueue(dmp_queue::Event), + #[codec(index = 54)] + BridgeRelayers(bridge_relayers::Event), + #[codec(index = 56)] + BridgeMillauMessages(bridge_millau_messages::Event), + } + pub mod system { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Remark { + pub remark: ::std::vec::Vec<::core::primitive::u8>, + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct SetHeapPages { + pub pages: ::core::primitive::u64, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetCode { + pub code: ::std::vec::Vec<::core::primitive::u8>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetCodeWithoutChecks { + pub code: ::std::vec::Vec<::core::primitive::u8>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetStorage { + pub items: ::std::vec::Vec<( + ::std::vec::Vec<::core::primitive::u8>, + ::std::vec::Vec<::core::primitive::u8>, + )>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct KillStorage { + pub keys: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct KillPrefix { + pub prefix: ::std::vec::Vec<::core::primitive::u8>, + pub subkeys: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct RemarkWithEvent { + pub remark: ::std::vec::Vec<::core::primitive::u8>, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Make some on-chain remark."] + #[doc = ""] + #[doc = "# "] + #[doc = "- `O(1)`"] + #[doc = "# "] + pub fn remark( + &self, + remark: ::std::vec::Vec<::core::primitive::u8>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "remark", + Remark { remark }, + [ + 101u8, 80u8, 195u8, 226u8, 224u8, 247u8, 60u8, 128u8, 3u8, 101u8, 51u8, + 147u8, 96u8, 126u8, 76u8, 230u8, 194u8, 227u8, 191u8, 73u8, 160u8, + 146u8, 87u8, 147u8, 243u8, 28u8, 228u8, 116u8, 224u8, 181u8, 129u8, + 160u8, + ], + ) + } + #[doc = "Set the number of pages in the WebAssembly environment's heap."] + pub fn set_heap_pages( + &self, + pages: ::core::primitive::u64, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "set_heap_pages", + SetHeapPages { pages }, + [ + 43u8, 103u8, 128u8, 49u8, 156u8, 136u8, 11u8, 204u8, 80u8, 6u8, 244u8, + 86u8, 171u8, 44u8, 140u8, 225u8, 142u8, 198u8, 43u8, 87u8, 26u8, 45u8, + 125u8, 222u8, 165u8, 254u8, 172u8, 158u8, 39u8, 178u8, 86u8, 87u8, + ], + ) + } + #[doc = "Set the new runtime code."] + #[doc = ""] + #[doc = "# "] + #[doc = "- `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code`"] + #[doc = "- 1 call to `can_set_code`: `O(S)` (calls `sp_io::misc::runtime_version` which is"] + #[doc = " expensive)."] + #[doc = "- 1 storage write (codec `O(C)`)."] + #[doc = "- 1 digest item."] + #[doc = "- 1 event."] + #[doc = "The weight of this function is dependent on the runtime, but generally this is very"] + #[doc = "expensive. We will treat this as a full block."] + #[doc = "# "] + pub fn set_code( + &self, + code: ::std::vec::Vec<::core::primitive::u8>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "set_code", + SetCode { code }, + [ + 27u8, 104u8, 244u8, 205u8, 188u8, 254u8, 121u8, 13u8, 106u8, 120u8, + 244u8, 108u8, 97u8, 84u8, 100u8, 68u8, 26u8, 69u8, 93u8, 128u8, 107u8, + 4u8, 3u8, 142u8, 13u8, 134u8, 196u8, 62u8, 113u8, 181u8, 14u8, 40u8, + ], + ) + } + #[doc = "Set the new runtime code without doing any checks of the given `code`."] + #[doc = ""] + #[doc = "# "] + #[doc = "- `O(C)` where `C` length of `code`"] + #[doc = "- 1 storage write (codec `O(C)`)."] + #[doc = "- 1 digest item."] + #[doc = "- 1 event."] + #[doc = "The weight of this function is dependent on the runtime. We will treat this as a full"] + #[doc = "block. # "] + pub fn set_code_without_checks( + &self, + code: ::std::vec::Vec<::core::primitive::u8>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "set_code_without_checks", + SetCodeWithoutChecks { code }, + [ + 102u8, 160u8, 125u8, 235u8, 30u8, 23u8, 45u8, 239u8, 112u8, 148u8, + 159u8, 158u8, 42u8, 93u8, 206u8, 94u8, 80u8, 250u8, 66u8, 195u8, 60u8, + 40u8, 142u8, 169u8, 183u8, 80u8, 80u8, 96u8, 3u8, 231u8, 99u8, 216u8, + ], + ) + } + #[doc = "Set some items of storage."] + pub fn set_storage( + &self, + items: ::std::vec::Vec<( + ::std::vec::Vec<::core::primitive::u8>, + ::std::vec::Vec<::core::primitive::u8>, + )>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "set_storage", + SetStorage { items }, + [ + 74u8, 43u8, 106u8, 255u8, 50u8, 151u8, 192u8, 155u8, 14u8, 90u8, 19u8, + 45u8, 165u8, 16u8, 235u8, 242u8, 21u8, 131u8, 33u8, 172u8, 119u8, 78u8, + 140u8, 10u8, 107u8, 202u8, 122u8, 235u8, 181u8, 191u8, 22u8, 116u8, + ], + ) + } + #[doc = "Kill some items from storage."] + pub fn kill_storage( + &self, + keys: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "kill_storage", + KillStorage { keys }, + [ + 174u8, 174u8, 13u8, 174u8, 75u8, 138u8, 128u8, 235u8, 222u8, 216u8, + 85u8, 18u8, 198u8, 1u8, 138u8, 70u8, 19u8, 108u8, 209u8, 41u8, 228u8, + 67u8, 130u8, 230u8, 160u8, 207u8, 11u8, 180u8, 139u8, 242u8, 41u8, + 15u8, + ], + ) + } + #[doc = "Kill all storage items with a key that starts with the given prefix."] + #[doc = ""] + #[doc = "**NOTE:** We rely on the Root origin to provide us the number of subkeys under"] + #[doc = "the prefix we are removing to accurately calculate the weight of this function."] + pub fn kill_prefix( + &self, + prefix: ::std::vec::Vec<::core::primitive::u8>, + subkeys: ::core::primitive::u32, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "kill_prefix", + KillPrefix { prefix, subkeys }, + [ + 203u8, 116u8, 217u8, 42u8, 154u8, 215u8, 77u8, 217u8, 13u8, 22u8, + 193u8, 2u8, 128u8, 115u8, 179u8, 115u8, 187u8, 218u8, 129u8, 34u8, + 80u8, 4u8, 173u8, 120u8, 92u8, 35u8, 237u8, 112u8, 201u8, 207u8, 200u8, + 48u8, + ], + ) + } + #[doc = "Make some on-chain remark and emit event."] + pub fn remark_with_event( + &self, + remark: ::std::vec::Vec<::core::primitive::u8>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "remark_with_event", + RemarkWithEvent { remark }, + [ + 123u8, 225u8, 180u8, 179u8, 144u8, 74u8, 27u8, 85u8, 101u8, 75u8, + 134u8, 44u8, 181u8, 25u8, 183u8, 158u8, 14u8, 213u8, 56u8, 225u8, + 136u8, 88u8, 26u8, 114u8, 178u8, 43u8, 176u8, 43u8, 240u8, 84u8, 116u8, + 46u8, + ], + ) + } + } + } + #[doc = "Event for the System pallet."] + pub type Event = runtime_types::frame_system::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An extrinsic completed successfully."] + pub struct ExtrinsicSuccess { + pub dispatch_info: runtime_types::frame_support::dispatch::DispatchInfo, + } + impl ::subxt::events::StaticEvent for ExtrinsicSuccess { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "ExtrinsicSuccess"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An extrinsic failed."] + pub struct ExtrinsicFailed { + pub dispatch_error: runtime_types::sp_runtime::DispatchError, + pub dispatch_info: runtime_types::frame_support::dispatch::DispatchInfo, + } + impl ::subxt::events::StaticEvent for ExtrinsicFailed { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "ExtrinsicFailed"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "`:code` was updated."] + pub struct CodeUpdated; + impl ::subxt::events::StaticEvent for CodeUpdated { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "CodeUpdated"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A new account was created."] + pub struct NewAccount { + pub account: ::sp_core::crypto::AccountId32, + } + impl ::subxt::events::StaticEvent for NewAccount { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "NewAccount"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An account was reaped."] + pub struct KilledAccount { + pub account: ::sp_core::crypto::AccountId32, + } + impl ::subxt::events::StaticEvent for KilledAccount { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "KilledAccount"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "On on-chain remark happened."] + pub struct Remarked { + pub sender: ::sp_core::crypto::AccountId32, + pub hash: ::subxt::utils::H256, + } + impl ::subxt::events::StaticEvent for Remarked { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "Remarked"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " The full account information for a particular account ID."] + pub fn account( + &self, + _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::frame_system::AccountInfo< + ::core::primitive::u32, + runtime_types::pallet_balances::AccountData<::core::primitive::u128>, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "Account", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 176u8, 187u8, 21u8, 220u8, 159u8, 204u8, 127u8, 14u8, 21u8, 69u8, 77u8, + 114u8, 230u8, 141u8, 107u8, 79u8, 23u8, 16u8, 174u8, 243u8, 252u8, + 42u8, 65u8, 120u8, 229u8, 38u8, 210u8, 255u8, 22u8, 40u8, 109u8, 223u8, + ], + ) + } + #[doc = " The full account information for a particular account ID."] + pub fn account_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::frame_system::AccountInfo< + ::core::primitive::u32, + runtime_types::pallet_balances::AccountData<::core::primitive::u128>, + >, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "Account", + Vec::new(), + [ + 176u8, 187u8, 21u8, 220u8, 159u8, 204u8, 127u8, 14u8, 21u8, 69u8, 77u8, + 114u8, 230u8, 141u8, 107u8, 79u8, 23u8, 16u8, 174u8, 243u8, 252u8, + 42u8, 65u8, 120u8, 229u8, 38u8, 210u8, 255u8, 22u8, 40u8, 109u8, 223u8, + ], + ) + } + #[doc = " Total extrinsics count for the current block."] + pub fn extrinsic_count( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "ExtrinsicCount", + vec![], + [ + 223u8, 60u8, 201u8, 120u8, 36u8, 44u8, 180u8, 210u8, 242u8, 53u8, + 222u8, 154u8, 123u8, 176u8, 249u8, 8u8, 225u8, 28u8, 232u8, 4u8, 136u8, + 41u8, 151u8, 82u8, 189u8, 149u8, 49u8, 166u8, 139u8, 9u8, 163u8, 231u8, + ], + ) + } + #[doc = " The current weight for the block."] + pub fn block_weight( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::frame_support::dispatch::PerDispatchClass< + ::sp_weights::Weight, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "BlockWeight", + vec![], + [ + 120u8, 67u8, 71u8, 163u8, 36u8, 202u8, 52u8, 106u8, 143u8, 155u8, + 144u8, 87u8, 142u8, 241u8, 232u8, 183u8, 56u8, 235u8, 27u8, 237u8, + 20u8, 202u8, 33u8, 85u8, 189u8, 0u8, 28u8, 52u8, 198u8, 40u8, 219u8, + 54u8, + ], + ) + } + #[doc = " Total length (in bytes) for all extrinsics put together, for the current block."] + pub fn all_extrinsics_len( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "AllExtrinsicsLen", + vec![], + [ + 202u8, 145u8, 209u8, 225u8, 40u8, 220u8, 174u8, 74u8, 93u8, 164u8, + 254u8, 248u8, 254u8, 192u8, 32u8, 117u8, 96u8, 149u8, 53u8, 145u8, + 219u8, 64u8, 234u8, 18u8, 217u8, 200u8, 203u8, 141u8, 145u8, 28u8, + 134u8, 60u8, + ], + ) + } + #[doc = " Map of block numbers to block hashes."] + pub fn block_hash( + &self, + _0: impl ::std::borrow::Borrow<::core::primitive::u32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::subxt::utils::H256>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "BlockHash", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Twox64Concat, + )], + [ + 50u8, 112u8, 176u8, 239u8, 175u8, 18u8, 205u8, 20u8, 241u8, 195u8, + 21u8, 228u8, 186u8, 57u8, 200u8, 25u8, 38u8, 44u8, 106u8, 20u8, 168u8, + 80u8, 76u8, 235u8, 12u8, 51u8, 137u8, 149u8, 200u8, 4u8, 220u8, 237u8, + ], + ) + } + #[doc = " Map of block numbers to block hashes."] + pub fn block_hash_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::subxt::utils::H256>, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "BlockHash", + Vec::new(), + [ + 50u8, 112u8, 176u8, 239u8, 175u8, 18u8, 205u8, 20u8, 241u8, 195u8, + 21u8, 228u8, 186u8, 57u8, 200u8, 25u8, 38u8, 44u8, 106u8, 20u8, 168u8, + 80u8, 76u8, 235u8, 12u8, 51u8, 137u8, 149u8, 200u8, 4u8, 220u8, 237u8, + ], + ) + } + #[doc = " Extrinsics data for the current block (maps an extrinsic's index to its data)."] + pub fn extrinsic_data( + &self, + _0: impl ::std::borrow::Borrow<::core::primitive::u32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "ExtrinsicData", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Twox64Concat, + )], + [ + 210u8, 224u8, 211u8, 186u8, 118u8, 210u8, 185u8, 194u8, 238u8, 211u8, + 254u8, 73u8, 67u8, 184u8, 31u8, 229u8, 168u8, 125u8, 98u8, 23u8, 241u8, + 59u8, 49u8, 86u8, 126u8, 9u8, 114u8, 163u8, 160u8, 62u8, 50u8, 67u8, + ], + ) + } + #[doc = " Extrinsics data for the current block (maps an extrinsic's index to its data)."] + pub fn extrinsic_data_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "ExtrinsicData", + Vec::new(), + [ + 210u8, 224u8, 211u8, 186u8, 118u8, 210u8, 185u8, 194u8, 238u8, 211u8, + 254u8, 73u8, 67u8, 184u8, 31u8, 229u8, 168u8, 125u8, 98u8, 23u8, 241u8, + 59u8, 49u8, 86u8, 126u8, 9u8, 114u8, 163u8, 160u8, 62u8, 50u8, 67u8, + ], + ) + } + #[doc = " The current block number being processed. Set by `execute_block`."] + pub fn number( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "Number", + vec![], + [ + 228u8, 96u8, 102u8, 190u8, 252u8, 130u8, 239u8, 172u8, 126u8, 235u8, + 246u8, 139u8, 208u8, 15u8, 88u8, 245u8, 141u8, 232u8, 43u8, 204u8, + 36u8, 87u8, 211u8, 141u8, 187u8, 68u8, 236u8, 70u8, 193u8, 235u8, + 164u8, 191u8, + ], + ) + } + #[doc = " Hash of the previous block."] + pub fn parent_hash( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::subxt::utils::H256>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "ParentHash", + vec![], + [ + 232u8, 206u8, 177u8, 119u8, 38u8, 57u8, 233u8, 50u8, 225u8, 49u8, + 169u8, 176u8, 210u8, 51u8, 231u8, 176u8, 234u8, 186u8, 188u8, 112u8, + 15u8, 152u8, 195u8, 232u8, 201u8, 97u8, 208u8, 249u8, 9u8, 163u8, 69u8, + 36u8, + ], + ) + } + #[doc = " Digest of the current block, also part of the block header."] + pub fn digest( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::sp_runtime::generic::Digest>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "Digest", + vec![], + [ + 83u8, 141u8, 200u8, 132u8, 182u8, 55u8, 197u8, 122u8, 13u8, 159u8, + 31u8, 42u8, 60u8, 191u8, 89u8, 221u8, 242u8, 47u8, 199u8, 213u8, 48u8, + 216u8, 131u8, 168u8, 245u8, 82u8, 56u8, 190u8, 62u8, 69u8, 96u8, 37u8, + ], + ) + } + #[doc = " Events deposited for the current block."] + #[doc = ""] + #[doc = " NOTE: The item is unbound and should therefore never be read on chain."] + #[doc = " It could otherwise inflate the PoV size of a block."] + #[doc = ""] + #[doc = " Events have a large in-memory size. Box the events to not go out-of-memory"] + #[doc = " just in case someone still reads them from within the runtime."] + pub fn events( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec< + runtime_types::frame_system::EventRecord< + runtime_types::rialto_parachain_runtime::RuntimeEvent, + ::subxt::utils::H256, + >, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "Events", + vec![], + [ + 4u8, 71u8, 59u8, 55u8, 39u8, 106u8, 211u8, 249u8, 109u8, 197u8, 134u8, + 138u8, 5u8, 188u8, 131u8, 120u8, 65u8, 30u8, 151u8, 104u8, 238u8, 32u8, + 251u8, 122u8, 104u8, 218u8, 7u8, 84u8, 253u8, 65u8, 48u8, 185u8, + ], + ) + } + #[doc = " The number of events in the `Events` list."] + pub fn event_count( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "EventCount", + vec![], + [ + 236u8, 93u8, 90u8, 177u8, 250u8, 211u8, 138u8, 187u8, 26u8, 208u8, + 203u8, 113u8, 221u8, 233u8, 227u8, 9u8, 249u8, 25u8, 202u8, 185u8, + 161u8, 144u8, 167u8, 104u8, 127u8, 187u8, 38u8, 18u8, 52u8, 61u8, 66u8, + 112u8, + ], + ) + } + #[doc = " Mapping between a topic (represented by T::Hash) and a vector of indexes"] + #[doc = " of events in the `>` list."] + #[doc = ""] + #[doc = " All topic vectors have deterministic storage locations depending on the topic. This"] + #[doc = " allows light-clients to leverage the changes trie storage tracking mechanism and"] + #[doc = " in case of changes fetch the list of events of interest."] + #[doc = ""] + #[doc = " The value has the type `(T::BlockNumber, EventIndex)` because if we used only just"] + #[doc = " the `EventIndex` then in case if the topic has the same contents on the next block"] + #[doc = " no notification will be triggered thus the event might be lost."] + pub fn event_topics( + &self, + _0: impl ::std::borrow::Borrow<::subxt::utils::H256>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec<(::core::primitive::u32, ::core::primitive::u32)>, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "EventTopics", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 205u8, 90u8, 142u8, 190u8, 176u8, 37u8, 94u8, 82u8, 98u8, 1u8, 129u8, + 63u8, 246u8, 101u8, 130u8, 58u8, 216u8, 16u8, 139u8, 196u8, 154u8, + 111u8, 110u8, 178u8, 24u8, 44u8, 183u8, 176u8, 232u8, 82u8, 223u8, + 38u8, + ], + ) + } + #[doc = " Mapping between a topic (represented by T::Hash) and a vector of indexes"] + #[doc = " of events in the `>` list."] + #[doc = ""] + #[doc = " All topic vectors have deterministic storage locations depending on the topic. This"] + #[doc = " allows light-clients to leverage the changes trie storage tracking mechanism and"] + #[doc = " in case of changes fetch the list of events of interest."] + #[doc = ""] + #[doc = " The value has the type `(T::BlockNumber, EventIndex)` because if we used only just"] + #[doc = " the `EventIndex` then in case if the topic has the same contents on the next block"] + #[doc = " no notification will be triggered thus the event might be lost."] + pub fn event_topics_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec<(::core::primitive::u32, ::core::primitive::u32)>, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "EventTopics", + Vec::new(), + [ + 205u8, 90u8, 142u8, 190u8, 176u8, 37u8, 94u8, 82u8, 98u8, 1u8, 129u8, + 63u8, 246u8, 101u8, 130u8, 58u8, 216u8, 16u8, 139u8, 196u8, 154u8, + 111u8, 110u8, 178u8, 24u8, 44u8, 183u8, 176u8, 232u8, 82u8, 223u8, + 38u8, + ], + ) + } + #[doc = " Stores the `spec_version` and `spec_name` of when the last runtime upgrade happened."] + pub fn last_runtime_upgrade( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::frame_system::LastRuntimeUpgradeInfo, + >, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "LastRuntimeUpgrade", + vec![], + [ + 52u8, 37u8, 117u8, 111u8, 57u8, 130u8, 196u8, 14u8, 99u8, 77u8, 91u8, + 126u8, 178u8, 249u8, 78u8, 34u8, 9u8, 194u8, 92u8, 105u8, 113u8, 81u8, + 185u8, 127u8, 245u8, 184u8, 60u8, 29u8, 234u8, 182u8, 96u8, 196u8, + ], + ) + } + #[doc = " True if we have upgraded so that `type RefCount` is `u32`. False (default) if not."] + pub fn upgraded_to_u32_ref_count( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "UpgradedToU32RefCount", + vec![], + [ + 171u8, 88u8, 244u8, 92u8, 122u8, 67u8, 27u8, 18u8, 59u8, 175u8, 175u8, + 178u8, 20u8, 150u8, 213u8, 59u8, 222u8, 141u8, 32u8, 107u8, 3u8, 114u8, + 83u8, 250u8, 180u8, 233u8, 152u8, 54u8, 187u8, 99u8, 131u8, 204u8, + ], + ) + } + #[doc = " True if we have upgraded so that AccountInfo contains three types of `RefCount`. False"] + #[doc = " (default) if not."] + pub fn upgraded_to_triple_ref_count( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "UpgradedToTripleRefCount", + vec![], + [ + 90u8, 33u8, 56u8, 86u8, 90u8, 101u8, 89u8, 133u8, 203u8, 56u8, 201u8, + 210u8, 244u8, 232u8, 150u8, 18u8, 51u8, 105u8, 14u8, 230u8, 103u8, + 155u8, 246u8, 99u8, 53u8, 207u8, 225u8, 128u8, 186u8, 76u8, 40u8, + 185u8, + ], + ) + } + #[doc = " The execution phase of the block."] + pub fn execution_phase( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "ExecutionPhase", + vec![], + [ + 230u8, 183u8, 221u8, 135u8, 226u8, 223u8, 55u8, 104u8, 138u8, 224u8, + 103u8, 156u8, 222u8, 99u8, 203u8, 199u8, 164u8, 168u8, 193u8, 133u8, + 201u8, 155u8, 63u8, 95u8, 17u8, 206u8, 165u8, 123u8, 161u8, 33u8, + 172u8, 93u8, + ], + ) + } + } + } + pub mod constants { + use super::runtime_types; + pub struct ConstantsApi; + impl ConstantsApi { + #[doc = " Block & extrinsics weights: base values and limits."] + pub fn block_weights( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::frame_system::limits::BlockWeights, + >, + > { + ::subxt::constants::StaticConstantAddress::new( + "System", + "BlockWeights", + [ + 118u8, 253u8, 239u8, 217u8, 145u8, 115u8, 85u8, 86u8, 172u8, 248u8, + 139u8, 32u8, 158u8, 126u8, 172u8, 188u8, 197u8, 105u8, 145u8, 235u8, + 171u8, 50u8, 31u8, 225u8, 167u8, 187u8, 241u8, 87u8, 6u8, 17u8, 234u8, + 185u8, + ], + ) + } + #[doc = " The maximum length of a block (in bytes)."] + pub fn block_length( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::frame_system::limits::BlockLength, + >, + > { + ::subxt::constants::StaticConstantAddress::new( + "System", + "BlockLength", + [ + 116u8, 184u8, 225u8, 228u8, 207u8, 203u8, 4u8, 220u8, 234u8, 198u8, + 150u8, 108u8, 205u8, 87u8, 194u8, 131u8, 229u8, 51u8, 140u8, 4u8, 47u8, + 12u8, 200u8, 144u8, 153u8, 62u8, 51u8, 39u8, 138u8, 205u8, 203u8, + 236u8, + ], + ) + } + #[doc = " Maximum number of block number to block hash mappings to keep (oldest pruned first)."] + pub fn block_hash_count( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "System", + "BlockHashCount", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + #[doc = " The weight of runtime database operations the runtime can invoke."] + pub fn db_weight( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType, + > { + ::subxt::constants::StaticConstantAddress::new( + "System", + "DbWeight", + [ + 124u8, 162u8, 190u8, 149u8, 49u8, 177u8, 162u8, 231u8, 62u8, 167u8, + 199u8, 181u8, 43u8, 232u8, 185u8, 116u8, 195u8, 51u8, 233u8, 223u8, + 20u8, 129u8, 246u8, 13u8, 65u8, 180u8, 64u8, 9u8, 157u8, 59u8, 245u8, + 118u8, + ], + ) + } + #[doc = " Get the chain's current version."] + pub fn version( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType, + > { + ::subxt::constants::StaticConstantAddress::new( + "System", + "Version", + [ + 93u8, 98u8, 57u8, 243u8, 229u8, 8u8, 234u8, 231u8, 72u8, 230u8, 139u8, + 47u8, 63u8, 181u8, 17u8, 2u8, 220u8, 231u8, 104u8, 237u8, 185u8, 143u8, + 165u8, 253u8, 188u8, 76u8, 147u8, 12u8, 170u8, 26u8, 74u8, 200u8, + ], + ) + } + #[doc = " The designated SS58 prefix of this chain."] + #[doc = ""] + #[doc = " This replaces the \"ss58Format\" property declared in the chain spec. Reason is"] + #[doc = " that the runtime should know about the prefix in order to make use of it as"] + #[doc = " an identifier of the chain."] + pub fn ss58_prefix( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u16>, + > { + ::subxt::constants::StaticConstantAddress::new( + "System", + "SS58Prefix", + [ + 116u8, 33u8, 2u8, 170u8, 181u8, 147u8, 171u8, 169u8, 167u8, 227u8, + 41u8, 144u8, 11u8, 236u8, 82u8, 100u8, 74u8, 60u8, 184u8, 72u8, 169u8, + 90u8, 208u8, 135u8, 15u8, 117u8, 10u8, 123u8, 128u8, 193u8, 29u8, 70u8, + ], + ) + } + } + } + } + pub mod timestamp { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Set { + #[codec(compact)] + pub now: ::core::primitive::u64, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Set the current time."] + #[doc = ""] + #[doc = "This call should be invoked exactly once per block. It will panic at the finalization"] + #[doc = "phase, if this call hasn't been invoked by that time."] + #[doc = ""] + #[doc = "The timestamp should be greater than the previous one by the amount specified by"] + #[doc = "`MinimumPeriod`."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be `Inherent`."] + #[doc = ""] + #[doc = "# "] + #[doc = "- `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`)"] + #[doc = "- 1 storage read and 1 storage mutation (codec `O(1)`). (because of `DidUpdate::take` in"] + #[doc = " `on_finalize`)"] + #[doc = "- 1 event handler `on_timestamp_set`. Must be `O(1)`."] + #[doc = "# "] + pub fn set( + &self, + now: ::core::primitive::u64, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Timestamp", + "set", + Set { now }, + [ + 6u8, 97u8, 172u8, 236u8, 118u8, 238u8, 228u8, 114u8, 15u8, 115u8, + 102u8, 85u8, 66u8, 151u8, 16u8, 33u8, 187u8, 17u8, 166u8, 88u8, 127u8, + 214u8, 182u8, 51u8, 168u8, 88u8, 43u8, 101u8, 185u8, 8u8, 1u8, 28u8, + ], + ) + } + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " Current time for the current block."] + pub fn now( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u64>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Timestamp", + "Now", + vec![], + [ + 148u8, 53u8, 50u8, 54u8, 13u8, 161u8, 57u8, 150u8, 16u8, 83u8, 144u8, + 221u8, 59u8, 75u8, 158u8, 130u8, 39u8, 123u8, 106u8, 134u8, 202u8, + 185u8, 83u8, 85u8, 60u8, 41u8, 120u8, 96u8, 210u8, 34u8, 2u8, 250u8, + ], + ) + } + #[doc = " Did the timestamp get updated in this block?"] + pub fn did_update( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Timestamp", + "DidUpdate", + vec![], + [ + 70u8, 13u8, 92u8, 186u8, 80u8, 151u8, 167u8, 90u8, 158u8, 232u8, 175u8, + 13u8, 103u8, 135u8, 2u8, 78u8, 16u8, 6u8, 39u8, 158u8, 167u8, 85u8, + 27u8, 47u8, 122u8, 73u8, 127u8, 26u8, 35u8, 168u8, 72u8, 204u8, + ], + ) + } + } + } + pub mod constants { + use super::runtime_types; + pub struct ConstantsApi; + impl ConstantsApi { + #[doc = " The minimum period between blocks. Beware that this is different to the *expected*"] + #[doc = " period that the block production apparatus provides. Your chosen consensus system will"] + #[doc = " generally work with this to determine a sensible block time. e.g. For Aura, it will be"] + #[doc = " double this period on default settings."] + pub fn minimum_period( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u64>, + > { + ::subxt::constants::StaticConstantAddress::new( + "Timestamp", + "MinimumPeriod", + [ + 128u8, 214u8, 205u8, 242u8, 181u8, 142u8, 124u8, 231u8, 190u8, 146u8, + 59u8, 226u8, 157u8, 101u8, 103u8, 117u8, 249u8, 65u8, 18u8, 191u8, + 103u8, 119u8, 53u8, 85u8, 81u8, 96u8, 220u8, 42u8, 184u8, 239u8, 42u8, + 246u8, + ], + ) + } + } + } + } + pub mod sudo { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Sudo { + pub call: ::std::boxed::Box, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SudoUncheckedWeight { + pub call: ::std::boxed::Box, + pub weight: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetKey { + pub new: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SudoAs { + pub who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + pub call: ::std::boxed::Box, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Authenticates the sudo key and dispatches a function call with `Root` origin."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "# "] + #[doc = "- O(1)."] + #[doc = "- Limited storage reads."] + #[doc = "- One DB write (event)."] + #[doc = "- Weight of derivative `call` execution + 10,000."] + #[doc = "# "] + pub fn sudo( + &self, + call: runtime_types::rialto_parachain_runtime::RuntimeCall, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Sudo", + "sudo", + Sudo { call: ::std::boxed::Box::new(call) }, + [ + 229u8, 162u8, 204u8, 182u8, 225u8, 254u8, 3u8, 66u8, 233u8, 49u8, 60u8, + 73u8, 182u8, 192u8, 90u8, 122u8, 176u8, 63u8, 200u8, 64u8, 111u8, + 155u8, 223u8, 233u8, 51u8, 220u8, 157u8, 192u8, 74u8, 236u8, 150u8, + 222u8, + ], + ) + } + #[doc = "Authenticates the sudo key and dispatches a function call with `Root` origin."] + #[doc = "This function does not check the weight of the call, and instead allows the"] + #[doc = "Sudo user to specify the weight of the call."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "# "] + #[doc = "- O(1)."] + #[doc = "- The weight of this call is defined by the caller."] + #[doc = "# "] + pub fn sudo_unchecked_weight( + &self, + call: runtime_types::rialto_parachain_runtime::RuntimeCall, + weight: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Sudo", + "sudo_unchecked_weight", + SudoUncheckedWeight { call: ::std::boxed::Box::new(call), weight }, + [ + 161u8, 202u8, 77u8, 33u8, 112u8, 211u8, 100u8, 184u8, 205u8, 250u8, + 70u8, 16u8, 39u8, 213u8, 108u8, 2u8, 195u8, 255u8, 15u8, 72u8, 142u8, + 180u8, 26u8, 203u8, 73u8, 81u8, 10u8, 29u8, 34u8, 101u8, 47u8, 99u8, + ], + ) + } + #[doc = "Authenticates the current sudo key and sets the given AccountId (`new`) as the new sudo"] + #[doc = "key."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "# "] + #[doc = "- O(1)."] + #[doc = "- Limited storage reads."] + #[doc = "- One DB change."] + #[doc = "# "] + pub fn set_key( + &self, + new: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Sudo", + "set_key", + SetKey { new }, + [ + 23u8, 224u8, 218u8, 169u8, 8u8, 28u8, 111u8, 199u8, 26u8, 88u8, 225u8, + 105u8, 17u8, 19u8, 87u8, 156u8, 97u8, 67u8, 89u8, 173u8, 70u8, 0u8, + 5u8, 246u8, 198u8, 135u8, 182u8, 180u8, 44u8, 9u8, 212u8, 95u8, + ], + ) + } + #[doc = "Authenticates the sudo key and dispatches a function call with `Signed` origin from"] + #[doc = "a given account."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "# "] + #[doc = "- O(1)."] + #[doc = "- Limited storage reads."] + #[doc = "- One DB write (event)."] + #[doc = "- Weight of derivative `call` execution + 10,000."] + #[doc = "# "] + pub fn sudo_as( + &self, + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + call: runtime_types::rialto_parachain_runtime::RuntimeCall, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Sudo", + "sudo_as", + SudoAs { who, call: ::std::boxed::Box::new(call) }, + [ + 100u8, 104u8, 192u8, 203u8, 123u8, 243u8, 209u8, 131u8, 26u8, 247u8, + 84u8, 157u8, 133u8, 227u8, 210u8, 237u8, 58u8, 23u8, 139u8, 213u8, + 134u8, 19u8, 12u8, 31u8, 219u8, 212u8, 230u8, 37u8, 152u8, 199u8, + 220u8, 163u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::pallet_sudo::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A sudo just took place. \\[result\\]"] + pub struct Sudid { + pub sudo_result: + ::core::result::Result<(), runtime_types::sp_runtime::DispatchError>, + } + impl ::subxt::events::StaticEvent for Sudid { + const PALLET: &'static str = "Sudo"; + const EVENT: &'static str = "Sudid"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "The \\[sudoer\\] just switched identity; the old key is supplied if one existed."] + pub struct KeyChanged { + pub old_sudoer: ::core::option::Option<::sp_core::crypto::AccountId32>, + } + impl ::subxt::events::StaticEvent for KeyChanged { + const PALLET: &'static str = "Sudo"; + const EVENT: &'static str = "KeyChanged"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A sudo just took place. \\[result\\]"] + pub struct SudoAsDone { + pub sudo_result: + ::core::result::Result<(), runtime_types::sp_runtime::DispatchError>, + } + impl ::subxt::events::StaticEvent for SudoAsDone { + const PALLET: &'static str = "Sudo"; + const EVENT: &'static str = "SudoAsDone"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " The `AccountId` of the sudo key."] + pub fn key( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::sp_core::crypto::AccountId32>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Sudo", + "Key", + vec![], + [ + 244u8, 73u8, 188u8, 136u8, 218u8, 163u8, 68u8, 179u8, 122u8, 173u8, + 34u8, 108u8, 137u8, 28u8, 182u8, 16u8, 196u8, 92u8, 138u8, 34u8, 102u8, + 80u8, 199u8, 88u8, 107u8, 207u8, 36u8, 22u8, 168u8, 167u8, 20u8, 142u8, + ], + ) + } + } + } + } + pub mod randomness_collective_flip { + use super::{root_mod, runtime_types}; + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " Series of block headers from the last 81 blocks that acts as random seed material. This"] + #[doc = " is arranged as a ring buffer with `block_number % 81` being the index into the `Vec` of"] + #[doc = " the oldest hash."] + pub fn random_material( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::sp_core::bounded::bounded_vec::BoundedVec< + ::subxt::utils::H256, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "RandomnessCollectiveFlip", + "RandomMaterial", + vec![], + [ + 152u8, 126u8, 73u8, 88u8, 54u8, 147u8, 6u8, 19u8, 214u8, 40u8, 159u8, + 30u8, 236u8, 61u8, 240u8, 65u8, 178u8, 94u8, 146u8, 152u8, 135u8, + 252u8, 160u8, 86u8, 123u8, 114u8, 251u8, 140u8, 98u8, 143u8, 217u8, + 242u8, + ], + ) + } + } + } + } + pub mod transaction_payment { + use super::{root_mod, runtime_types}; + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::pallet_transaction_payment::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A transaction fee `actual_fee`, of which `tip` was added to the minimum inclusion fee,"] + #[doc = "has been paid by `who`."] + pub struct TransactionFeePaid { + pub who: ::sp_core::crypto::AccountId32, + pub actual_fee: ::core::primitive::u128, + pub tip: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for TransactionFeePaid { + const PALLET: &'static str = "TransactionPayment"; + const EVENT: &'static str = "TransactionFeePaid"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + pub fn next_fee_multiplier( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::sp_arithmetic::fixed_point::FixedU128, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "TransactionPayment", + "NextFeeMultiplier", + vec![], + [ + 210u8, 0u8, 206u8, 165u8, 183u8, 10u8, 206u8, 52u8, 14u8, 90u8, 218u8, + 197u8, 189u8, 125u8, 113u8, 216u8, 52u8, 161u8, 45u8, 24u8, 245u8, + 237u8, 121u8, 41u8, 106u8, 29u8, 45u8, 129u8, 250u8, 203u8, 206u8, + 180u8, + ], + ) + } + pub fn storage_version( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::pallet_transaction_payment::Releases, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "TransactionPayment", + "StorageVersion", + vec![], + [ + 219u8, 243u8, 82u8, 176u8, 65u8, 5u8, 132u8, 114u8, 8u8, 82u8, 176u8, + 200u8, 97u8, 150u8, 177u8, 164u8, 166u8, 11u8, 34u8, 12u8, 12u8, 198u8, + 58u8, 191u8, 186u8, 221u8, 221u8, 119u8, 181u8, 253u8, 154u8, 228u8, + ], + ) + } + } + } + pub mod constants { + use super::runtime_types; + pub struct ConstantsApi; + impl ConstantsApi { + #[doc = " A fee mulitplier for `Operational` extrinsics to compute \"virtual tip\" to boost their"] + #[doc = " `priority`"] + #[doc = ""] + #[doc = " This value is multipled by the `final_fee` to obtain a \"virtual tip\" that is later"] + #[doc = " added to a tip component in regular `priority` calculations."] + #[doc = " It means that a `Normal` transaction can front-run a similarly-sized `Operational`"] + #[doc = " extrinsic (with no tip), by including a tip value greater than the virtual tip."] + #[doc = ""] + #[doc = " ```rust,ignore"] + #[doc = " // For `Normal`"] + #[doc = " let priority = priority_calc(tip);"] + #[doc = ""] + #[doc = " // For `Operational`"] + #[doc = " let virtual_tip = (inclusion_fee + tip) * OperationalFeeMultiplier;"] + #[doc = " let priority = priority_calc(tip + virtual_tip);"] + #[doc = " ```"] + #[doc = ""] + #[doc = " Note that since we use `final_fee` the multiplier applies also to the regular `tip`"] + #[doc = " sent with the transaction. So, not only does the transaction get a priority bump based"] + #[doc = " on the `inclusion_fee`, but we also amplify the impact of tips applied to `Operational`"] + #[doc = " transactions."] + pub fn operational_fee_multiplier( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u8>, + > { + ::subxt::constants::StaticConstantAddress::new( + "TransactionPayment", + "OperationalFeeMultiplier", + [ + 141u8, 130u8, 11u8, 35u8, 226u8, 114u8, 92u8, 179u8, 168u8, 110u8, + 28u8, 91u8, 221u8, 64u8, 4u8, 148u8, 201u8, 193u8, 185u8, 66u8, 226u8, + 114u8, 97u8, 79u8, 62u8, 212u8, 202u8, 114u8, 237u8, 228u8, 183u8, + 165u8, + ], + ) + } + } + } + } + pub mod parachain_system { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetValidationData { + pub data: + runtime_types::cumulus_primitives_parachain_inherent::ParachainInherentData, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SudoSendUpwardMessage { + pub message: ::std::vec::Vec<::core::primitive::u8>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct AuthorizeUpgrade { + pub code_hash: ::subxt::utils::H256, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct EnactAuthorizedUpgrade { + pub code: ::std::vec::Vec<::core::primitive::u8>, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Set the current validation data."] + #[doc = ""] + #[doc = "This should be invoked exactly once per block. It will panic at the finalization"] + #[doc = "phase if the call was not invoked."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be `Inherent`"] + #[doc = ""] + #[doc = "As a side effect, this function upgrades the current validation function"] + #[doc = "if the appropriate time has come."] + pub fn set_validation_data( + &self, + data : runtime_types :: cumulus_primitives_parachain_inherent :: ParachainInherentData, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "ParachainSystem", + "set_validation_data", + SetValidationData { data }, + [ + 200u8, 80u8, 163u8, 177u8, 184u8, 117u8, 61u8, 203u8, 244u8, 214u8, + 106u8, 151u8, 128u8, 131u8, 254u8, 120u8, 254u8, 76u8, 104u8, 39u8, + 215u8, 227u8, 233u8, 254u8, 26u8, 62u8, 17u8, 42u8, 19u8, 127u8, 108u8, + 242u8, + ], + ) + } + pub fn sudo_send_upward_message( + &self, + message: ::std::vec::Vec<::core::primitive::u8>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "ParachainSystem", + "sudo_send_upward_message", + SudoSendUpwardMessage { message }, + [ + 127u8, 79u8, 45u8, 183u8, 190u8, 205u8, 184u8, 169u8, 255u8, 191u8, + 86u8, 154u8, 134u8, 25u8, 249u8, 63u8, 47u8, 194u8, 108u8, 62u8, 60u8, + 170u8, 81u8, 240u8, 113u8, 48u8, 181u8, 171u8, 95u8, 63u8, 26u8, 222u8, + ], + ) + } + pub fn authorize_upgrade( + &self, + code_hash: ::subxt::utils::H256, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "ParachainSystem", + "authorize_upgrade", + AuthorizeUpgrade { code_hash }, + [ + 52u8, 152u8, 69u8, 207u8, 143u8, 113u8, 163u8, 11u8, 181u8, 182u8, + 124u8, 101u8, 207u8, 19u8, 59u8, 81u8, 129u8, 29u8, 79u8, 115u8, 90u8, + 83u8, 225u8, 124u8, 21u8, 108u8, 99u8, 194u8, 78u8, 83u8, 252u8, 163u8, + ], + ) + } + pub fn enact_authorized_upgrade( + &self, + code: ::std::vec::Vec<::core::primitive::u8>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "ParachainSystem", + "enact_authorized_upgrade", + EnactAuthorizedUpgrade { code }, + [ + 43u8, 157u8, 1u8, 230u8, 134u8, 72u8, 230u8, 35u8, 159u8, 13u8, 201u8, + 134u8, 184u8, 94u8, 167u8, 13u8, 108u8, 157u8, 145u8, 166u8, 119u8, + 37u8, 51u8, 121u8, 252u8, 255u8, 48u8, 251u8, 126u8, 152u8, 247u8, 5u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::cumulus_pallet_parachain_system::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "The validation function has been scheduled to apply."] + pub struct ValidationFunctionStored; + impl ::subxt::events::StaticEvent for ValidationFunctionStored { + const PALLET: &'static str = "ParachainSystem"; + const EVENT: &'static str = "ValidationFunctionStored"; + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "The validation function was applied as of the contained relay chain block number."] + pub struct ValidationFunctionApplied { + pub relay_chain_block_num: ::core::primitive::u32, + } + impl ::subxt::events::StaticEvent for ValidationFunctionApplied { + const PALLET: &'static str = "ParachainSystem"; + const EVENT: &'static str = "ValidationFunctionApplied"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "The relay-chain aborted the upgrade process."] + pub struct ValidationFunctionDiscarded; + impl ::subxt::events::StaticEvent for ValidationFunctionDiscarded { + const PALLET: &'static str = "ParachainSystem"; + const EVENT: &'static str = "ValidationFunctionDiscarded"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An upgrade has been authorized."] + pub struct UpgradeAuthorized { + pub code_hash: ::subxt::utils::H256, + } + impl ::subxt::events::StaticEvent for UpgradeAuthorized { + const PALLET: &'static str = "ParachainSystem"; + const EVENT: &'static str = "UpgradeAuthorized"; + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Some downward messages have been received and will be processed."] + pub struct DownwardMessagesReceived { + pub count: ::core::primitive::u32, + } + impl ::subxt::events::StaticEvent for DownwardMessagesReceived { + const PALLET: &'static str = "ParachainSystem"; + const EVENT: &'static str = "DownwardMessagesReceived"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward messages were processed using the given weight."] + pub struct DownwardMessagesProcessed { + pub weight_used: ::sp_weights::Weight, + pub dmq_head: ::subxt::utils::H256, + } + impl ::subxt::events::StaticEvent for DownwardMessagesProcessed { + const PALLET: &'static str = "ParachainSystem"; + const EVENT: &'static str = "DownwardMessagesProcessed"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An upward message was sent to the relay chain."] + pub struct UpwardMessageSent { + pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + } + impl ::subxt::events::StaticEvent for UpwardMessageSent { + const PALLET: &'static str = "ParachainSystem"; + const EVENT: &'static str = "UpwardMessageSent"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " In case of a scheduled upgrade, this storage field contains the validation code to be applied."] + #[doc = ""] + #[doc = " As soon as the relay chain gives us the go-ahead signal, we will overwrite the [`:code`][well_known_keys::CODE]"] + #[doc = " which will result the next block process with the new validation code. This concludes the upgrade process."] + #[doc = ""] + #[doc = " [well_known_keys::CODE]: sp_core::storage::well_known_keys::CODE"] + pub fn pending_validation_code( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "PendingValidationCode", + vec![], + [ + 162u8, 35u8, 108u8, 76u8, 160u8, 93u8, 215u8, 84u8, 20u8, 249u8, 57u8, + 187u8, 88u8, 161u8, 15u8, 131u8, 213u8, 89u8, 140u8, 20u8, 227u8, + 204u8, 79u8, 176u8, 114u8, 119u8, 8u8, 7u8, 64u8, 15u8, 90u8, 92u8, + ], + ) + } + #[doc = " Validation code that is set by the parachain and is to be communicated to collator and"] + #[doc = " consequently the relay-chain."] + #[doc = ""] + #[doc = " This will be cleared in `on_initialize` of each new block if no other pallet already set"] + #[doc = " the value."] + pub fn new_validation_code( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "NewValidationCode", + vec![], + [ + 224u8, 174u8, 53u8, 106u8, 240u8, 49u8, 48u8, 79u8, 219u8, 74u8, 142u8, + 166u8, 92u8, 204u8, 244u8, 200u8, 43u8, 169u8, 177u8, 207u8, 190u8, + 106u8, 180u8, 65u8, 245u8, 131u8, 134u8, 4u8, 53u8, 45u8, 76u8, 3u8, + ], + ) + } + #[doc = " The [`PersistedValidationData`] set for this block."] + #[doc = " This value is expected to be set only once per block and it's never stored"] + #[doc = " in the trie."] + pub fn validation_data( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::polkadot_primitives::v2::PersistedValidationData< + ::subxt::utils::H256, + ::core::primitive::u32, + >, + >, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "ValidationData", + vec![], + [ + 112u8, 58u8, 240u8, 81u8, 219u8, 110u8, 244u8, 186u8, 251u8, 90u8, + 195u8, 217u8, 229u8, 102u8, 233u8, 24u8, 109u8, 96u8, 219u8, 72u8, + 139u8, 93u8, 58u8, 140u8, 40u8, 110u8, 167u8, 98u8, 199u8, 12u8, 138u8, + 131u8, + ], + ) + } + #[doc = " Were the validation data set to notify the relay chain?"] + pub fn did_set_validation_code( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "DidSetValidationCode", + vec![], + [ + 89u8, 83u8, 74u8, 174u8, 234u8, 188u8, 149u8, 78u8, 140u8, 17u8, 92u8, + 165u8, 243u8, 87u8, 59u8, 97u8, 135u8, 81u8, 192u8, 86u8, 193u8, 187u8, + 113u8, 22u8, 108u8, 83u8, 242u8, 208u8, 174u8, 40u8, 49u8, 245u8, + ], + ) + } + #[doc = " The relay chain block number associated with the last parachain block."] + pub fn last_relay_chain_block_number( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "LastRelayChainBlockNumber", + vec![], + [ + 68u8, 121u8, 6u8, 159u8, 181u8, 94u8, 151u8, 215u8, 225u8, 244u8, 4u8, + 158u8, 216u8, 85u8, 55u8, 228u8, 197u8, 35u8, 200u8, 33u8, 29u8, 182u8, + 17u8, 83u8, 59u8, 63u8, 25u8, 180u8, 132u8, 23u8, 97u8, 252u8, + ], + ) + } + #[doc = " An option which indicates if the relay-chain restricts signalling a validation code upgrade."] + #[doc = " In other words, if this is `Some` and [`NewValidationCode`] is `Some` then the produced"] + #[doc = " candidate will be invalid."] + #[doc = ""] + #[doc = " This storage item is a mirror of the corresponding value for the current parachain from the"] + #[doc = " relay-chain. This value is ephemeral which means it doesn't hit the storage. This value is"] + #[doc = " set after the inherent."] + pub fn upgrade_restriction_signal( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::core::option::Option< + runtime_types::polkadot_primitives::v2::UpgradeRestriction, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "UpgradeRestrictionSignal", + vec![], + [ + 61u8, 3u8, 26u8, 6u8, 88u8, 114u8, 109u8, 63u8, 7u8, 115u8, 245u8, + 198u8, 73u8, 234u8, 28u8, 228u8, 126u8, 27u8, 151u8, 18u8, 133u8, 54u8, + 144u8, 149u8, 246u8, 43u8, 83u8, 47u8, 77u8, 238u8, 10u8, 196u8, + ], + ) + } + #[doc = " The state proof for the last relay parent block."] + #[doc = ""] + #[doc = " This field is meant to be updated each block with the validation data inherent. Therefore,"] + #[doc = " before processing of the inherent, e.g. in `on_initialize` this data may be stale."] + #[doc = ""] + #[doc = " This data is also absent from the genesis."] + pub fn relay_state_proof( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::sp_trie::storage_proof::StorageProof, + >, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "RelayStateProof", + vec![], + [ + 35u8, 124u8, 167u8, 221u8, 162u8, 145u8, 158u8, 186u8, 57u8, 154u8, + 225u8, 6u8, 176u8, 13u8, 178u8, 195u8, 209u8, 122u8, 221u8, 26u8, + 155u8, 126u8, 153u8, 246u8, 101u8, 221u8, 61u8, 145u8, 211u8, 236u8, + 48u8, 130u8, + ], + ) + } + #[doc = " The snapshot of some state related to messaging relevant to the current parachain as per"] + #[doc = " the relay parent."] + #[doc = ""] + #[doc = " This field is meant to be updated each block with the validation data inherent. Therefore,"] + #[doc = " before processing of the inherent, e.g. in `on_initialize` this data may be stale."] + #[doc = ""] + #[doc = " This data is also absent from the genesis."] pub fn relevant_messaging_state (& self ,) -> :: subxt :: storage :: address :: StaticStorageAddress :: < :: subxt :: metadata :: DecodeStaticType < runtime_types :: cumulus_pallet_parachain_system :: relay_state_snapshot :: MessagingStateSnapshot > , :: subxt :: storage :: address :: Yes , () , () >{ + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "RelevantMessagingState", + vec![], + [ + 68u8, 241u8, 114u8, 83u8, 200u8, 99u8, 8u8, 244u8, 110u8, 134u8, 106u8, + 153u8, 17u8, 90u8, 184u8, 157u8, 100u8, 140u8, 157u8, 83u8, 25u8, + 166u8, 173u8, 31u8, 221u8, 24u8, 236u8, 85u8, 176u8, 223u8, 237u8, + 65u8, + ], + ) + } + #[doc = " The parachain host configuration that was obtained from the relay parent."] + #[doc = ""] + #[doc = " This field is meant to be updated each block with the validation data inherent. Therefore,"] + #[doc = " before processing of the inherent, e.g. in `on_initialize` this data may be stale."] + #[doc = ""] + #[doc = " This data is also absent from the genesis."] + pub fn host_configuration( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::polkadot_primitives::v2::AbridgedHostConfiguration, + >, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "HostConfiguration", + vec![], + [ + 104u8, 200u8, 30u8, 202u8, 119u8, 204u8, 233u8, 20u8, 67u8, 199u8, + 47u8, 166u8, 254u8, 152u8, 10u8, 187u8, 240u8, 255u8, 148u8, 201u8, + 134u8, 41u8, 130u8, 201u8, 112u8, 65u8, 68u8, 103u8, 56u8, 123u8, + 178u8, 113u8, + ], + ) + } + #[doc = " The last downward message queue chain head we have observed."] + #[doc = ""] + #[doc = " This value is loaded before and saved after processing inbound downward messages carried"] + #[doc = " by the system inherent."] + pub fn last_dmq_mqc_head( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::cumulus_primitives_parachain_inherent::MessageQueueChain, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "LastDmqMqcHead", + vec![], + [ + 176u8, 255u8, 246u8, 125u8, 36u8, 120u8, 24u8, 44u8, 26u8, 64u8, 236u8, + 210u8, 189u8, 237u8, 50u8, 78u8, 45u8, 139u8, 58u8, 141u8, 112u8, + 253u8, 178u8, 198u8, 87u8, 71u8, 77u8, 248u8, 21u8, 145u8, 187u8, 52u8, + ], + ) + } + #[doc = " The message queue chain heads we have observed per each channel incoming channel."] + #[doc = ""] + #[doc = " This value is loaded before and saved after processing inbound downward messages carried"] + #[doc = " by the system inherent."] + pub fn last_hrmp_mqc_heads( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::subxt::utils::KeyedVec< + runtime_types::polkadot_parachain::primitives::Id, + runtime_types::cumulus_primitives_parachain_inherent::MessageQueueChain, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "LastHrmpMqcHeads", + vec![], + [ + 55u8, 179u8, 35u8, 16u8, 173u8, 0u8, 122u8, 179u8, 236u8, 98u8, 9u8, + 112u8, 11u8, 219u8, 241u8, 89u8, 131u8, 198u8, 64u8, 139u8, 103u8, + 158u8, 77u8, 107u8, 83u8, 236u8, 255u8, 208u8, 47u8, 61u8, 219u8, + 240u8, + ], + ) + } + #[doc = " Number of downward messages processed in a block."] + #[doc = ""] + #[doc = " This will be cleared in `on_initialize` of each new block."] + pub fn processed_downward_messages( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "ProcessedDownwardMessages", + vec![], + [ + 48u8, 177u8, 84u8, 228u8, 101u8, 235u8, 181u8, 27u8, 66u8, 55u8, 50u8, + 146u8, 245u8, 223u8, 77u8, 132u8, 178u8, 80u8, 74u8, 90u8, 166u8, 81u8, + 109u8, 25u8, 91u8, 69u8, 5u8, 69u8, 123u8, 197u8, 160u8, 146u8, + ], + ) + } + #[doc = " HRMP watermark that was set in a block."] + #[doc = ""] + #[doc = " This will be cleared in `on_initialize` of each new block."] + pub fn hrmp_watermark( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "HrmpWatermark", + vec![], + [ + 189u8, 59u8, 183u8, 195u8, 69u8, 185u8, 241u8, 226u8, 62u8, 204u8, + 230u8, 77u8, 102u8, 75u8, 86u8, 157u8, 249u8, 140u8, 219u8, 72u8, 94u8, + 64u8, 176u8, 72u8, 34u8, 205u8, 114u8, 103u8, 231u8, 233u8, 206u8, + 111u8, + ], + ) + } + #[doc = " HRMP messages that were sent in a block."] + #[doc = ""] + #[doc = " This will be cleared in `on_initialize` of each new block."] + pub fn hrmp_outbound_messages( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec< + runtime_types::polkadot_core_primitives::OutboundHrmpMessage< + runtime_types::polkadot_parachain::primitives::Id, + >, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "HrmpOutboundMessages", + vec![], + [ + 74u8, 86u8, 173u8, 248u8, 90u8, 230u8, 71u8, 225u8, 127u8, 164u8, + 221u8, 62u8, 146u8, 13u8, 73u8, 9u8, 98u8, 168u8, 6u8, 14u8, 97u8, + 166u8, 45u8, 70u8, 62u8, 210u8, 9u8, 32u8, 83u8, 18u8, 4u8, 201u8, + ], + ) + } + #[doc = " Upward messages that were sent in a block."] + #[doc = ""] + #[doc = " This will be cleared in `on_initialize` of each new block."] + pub fn upward_messages( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "UpwardMessages", + vec![], + [ + 129u8, 208u8, 187u8, 36u8, 48u8, 108u8, 135u8, 56u8, 204u8, 60u8, + 100u8, 158u8, 113u8, 238u8, 46u8, 92u8, 228u8, 41u8, 178u8, 177u8, + 208u8, 195u8, 148u8, 149u8, 127u8, 21u8, 93u8, 92u8, 29u8, 115u8, 10u8, + 248u8, + ], + ) + } + #[doc = " Upward messages that are still pending and not yet send to the relay chain."] + pub fn pending_upward_messages( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "PendingUpwardMessages", + vec![], + [ + 223u8, 46u8, 224u8, 227u8, 222u8, 119u8, 225u8, 244u8, 59u8, 87u8, + 127u8, 19u8, 217u8, 237u8, 103u8, 61u8, 6u8, 210u8, 107u8, 201u8, + 117u8, 25u8, 85u8, 248u8, 36u8, 231u8, 28u8, 202u8, 41u8, 140u8, 208u8, + 254u8, + ], + ) + } + #[doc = " The number of HRMP messages we observed in `on_initialize` and thus used that number for"] + #[doc = " announcing the weight of `on_initialize` and `on_finalize`."] + pub fn announced_hrmp_messages_per_candidate( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "AnnouncedHrmpMessagesPerCandidate", + vec![], + [ + 132u8, 61u8, 162u8, 129u8, 251u8, 243u8, 20u8, 144u8, 162u8, 73u8, + 237u8, 51u8, 248u8, 41u8, 127u8, 171u8, 180u8, 79u8, 137u8, 23u8, 66u8, + 134u8, 106u8, 222u8, 182u8, 154u8, 0u8, 145u8, 184u8, 156u8, 36u8, + 97u8, + ], + ) + } + #[doc = " The weight we reserve at the beginning of the block for processing XCMP messages. This"] + #[doc = " overrides the amount set in the Config trait."] + pub fn reserved_xcmp_weight_override( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::sp_weights::Weight>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "ReservedXcmpWeightOverride", + vec![], + [ + 180u8, 90u8, 34u8, 178u8, 1u8, 242u8, 211u8, 97u8, 100u8, 34u8, 39u8, + 42u8, 142u8, 249u8, 236u8, 194u8, 244u8, 164u8, 96u8, 54u8, 98u8, 46u8, + 92u8, 196u8, 185u8, 51u8, 231u8, 234u8, 249u8, 143u8, 244u8, 64u8, + ], + ) + } + #[doc = " The weight we reserve at the beginning of the block for processing DMP messages. This"] + #[doc = " overrides the amount set in the Config trait."] + pub fn reserved_dmp_weight_override( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::sp_weights::Weight>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "ReservedDmpWeightOverride", + vec![], + [ + 90u8, 122u8, 168u8, 240u8, 95u8, 195u8, 160u8, 109u8, 175u8, 170u8, + 227u8, 44u8, 139u8, 176u8, 32u8, 161u8, 57u8, 233u8, 56u8, 55u8, 123u8, + 168u8, 174u8, 96u8, 159u8, 62u8, 186u8, 186u8, 17u8, 70u8, 57u8, 246u8, + ], + ) + } + #[doc = " The next authorized upgrade, if there is one."] + pub fn authorized_upgrade( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::subxt::utils::H256>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "AuthorizedUpgrade", + vec![], + [ + 136u8, 238u8, 241u8, 144u8, 252u8, 61u8, 101u8, 171u8, 234u8, 160u8, + 145u8, 210u8, 69u8, 29u8, 204u8, 166u8, 250u8, 101u8, 254u8, 32u8, + 96u8, 197u8, 222u8, 212u8, 50u8, 189u8, 25u8, 7u8, 48u8, 183u8, 234u8, + 95u8, + ], + ) + } + #[doc = " A custom head data that should be returned as result of `validate_block`."] + #[doc = ""] + #[doc = " See [`Pallet::set_custom_validation_head_data`] for more information."] + pub fn custom_validation_head_data( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "CustomValidationHeadData", + vec![], + [ + 189u8, 150u8, 234u8, 128u8, 111u8, 27u8, 173u8, 92u8, 109u8, 4u8, 98u8, + 103u8, 158u8, 19u8, 16u8, 5u8, 107u8, 135u8, 126u8, 170u8, 62u8, 64u8, + 149u8, 80u8, 33u8, 17u8, 83u8, 22u8, 176u8, 118u8, 26u8, 223u8, + ], + ) + } + } + } + } + pub mod parachain_info { + use super::{root_mod, runtime_types}; + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + pub fn parachain_id( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::polkadot_parachain::primitives::Id, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainInfo", + "ParachainId", + vec![], + [ + 151u8, 191u8, 241u8, 118u8, 192u8, 47u8, 166u8, 151u8, 217u8, 240u8, + 165u8, 232u8, 51u8, 113u8, 243u8, 1u8, 89u8, 240u8, 11u8, 1u8, 77u8, + 104u8, 12u8, 56u8, 17u8, 135u8, 214u8, 19u8, 114u8, 135u8, 66u8, 76u8, + ], + ) + } + } + } + } + pub mod balances { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Transfer { + pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + pub value: ::core::primitive::u128, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetBalance { + pub who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + pub new_free: ::core::primitive::u128, + #[codec(compact)] + pub new_reserved: ::core::primitive::u128, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ForceTransfer { + pub source: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + pub value: ::core::primitive::u128, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct TransferKeepAlive { + pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + pub value: ::core::primitive::u128, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct TransferAll { + pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + pub keep_alive: ::core::primitive::bool, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ForceUnreserve { + pub who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + pub amount: ::core::primitive::u128, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Transfer some liquid free balance to another account."] + #[doc = ""] + #[doc = "`transfer` will set the `FreeBalance` of the sender and receiver."] + #[doc = "If the sender's account is below the existential deposit as a result"] + #[doc = "of the transfer, the account will be reaped."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be `Signed` by the transactor."] + #[doc = ""] + #[doc = "# "] + #[doc = "- Dependent on arguments but not critical, given proper implementations for input config"] + #[doc = " types. See related functions below."] + #[doc = "- It contains a limited number of reads and writes internally and no complex"] + #[doc = " computation."] + #[doc = ""] + #[doc = "Related functions:"] + #[doc = ""] + #[doc = " - `ensure_can_withdraw` is always called internally but has a bounded complexity."] + #[doc = " - Transferring balances to accounts that did not exist before will cause"] + #[doc = " `T::OnNewAccount::on_new_account` to be called."] + #[doc = " - Removing enough funds from an account will trigger `T::DustRemoval::on_unbalanced`."] + #[doc = " - `transfer_keep_alive` works the same way as `transfer`, but has an additional check"] + #[doc = " that the transfer will not kill the origin account."] + #[doc = "---------------------------------"] + #[doc = "- Origin account is already in memory, so no DB operations for them."] + #[doc = "# "] + pub fn transfer( + &self, + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + value: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "transfer", + Transfer { dest, value }, + [ + 111u8, 222u8, 32u8, 56u8, 171u8, 77u8, 252u8, 29u8, 194u8, 155u8, + 200u8, 192u8, 198u8, 81u8, 23u8, 115u8, 236u8, 91u8, 218u8, 114u8, + 107u8, 141u8, 138u8, 100u8, 237u8, 21u8, 58u8, 172u8, 3u8, 20u8, 216u8, + 38u8, + ], + ) + } + #[doc = "Set the balances of a given account."] + #[doc = ""] + #[doc = "This will alter `FreeBalance` and `ReservedBalance` in storage. it will"] + #[doc = "also alter the total issuance of the system (`TotalIssuance`) appropriately."] + #[doc = "If the new free or reserved balance is below the existential deposit,"] + #[doc = "it will reset the account nonce (`frame_system::AccountNonce`)."] + #[doc = ""] + #[doc = "The dispatch origin for this call is `root`."] + pub fn set_balance( + &self, + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + new_free: ::core::primitive::u128, + new_reserved: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "set_balance", + SetBalance { who, new_free, new_reserved }, + [ + 234u8, 215u8, 97u8, 98u8, 243u8, 199u8, 57u8, 76u8, 59u8, 161u8, 118u8, + 207u8, 34u8, 197u8, 198u8, 61u8, 231u8, 210u8, 169u8, 235u8, 150u8, + 137u8, 173u8, 49u8, 28u8, 77u8, 84u8, 149u8, 143u8, 210u8, 139u8, + 193u8, + ], + ) + } + #[doc = "Exactly as `transfer`, except the origin must be root and the source account may be"] + #[doc = "specified."] + #[doc = "# "] + #[doc = "- Same as transfer, but additional read and write because the source account is not"] + #[doc = " assumed to be in the overlay."] + #[doc = "# "] + pub fn force_transfer( + &self, + source: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + value: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "force_transfer", + ForceTransfer { source, dest, value }, + [ + 79u8, 174u8, 212u8, 108u8, 184u8, 33u8, 170u8, 29u8, 232u8, 254u8, + 195u8, 218u8, 221u8, 134u8, 57u8, 99u8, 6u8, 70u8, 181u8, 227u8, 56u8, + 239u8, 243u8, 158u8, 157u8, 245u8, 36u8, 162u8, 11u8, 237u8, 147u8, + 15u8, + ], + ) + } + #[doc = "Same as the [`transfer`] call, but with a check that the transfer will not kill the"] + #[doc = "origin account."] + #[doc = ""] + #[doc = "99% of the time you want [`transfer`] instead."] + #[doc = ""] + #[doc = "[`transfer`]: struct.Pallet.html#method.transfer"] + pub fn transfer_keep_alive( + &self, + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + value: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "transfer_keep_alive", + TransferKeepAlive { dest, value }, + [ + 112u8, 179u8, 75u8, 168u8, 193u8, 221u8, 9u8, 82u8, 190u8, 113u8, + 253u8, 13u8, 130u8, 134u8, 170u8, 216u8, 136u8, 111u8, 242u8, 220u8, + 202u8, 112u8, 47u8, 79u8, 73u8, 244u8, 226u8, 59u8, 240u8, 188u8, + 210u8, 208u8, + ], + ) + } + #[doc = "Transfer the entire transferable balance from the caller account."] + #[doc = ""] + #[doc = "NOTE: This function only attempts to transfer _transferable_ balances. This means that"] + #[doc = "any locked, reserved, or existential deposits (when `keep_alive` is `true`), will not be"] + #[doc = "transferred by this function. To ensure that this function results in a killed account,"] + #[doc = "you might need to prepare the account by removing any reference counters, storage"] + #[doc = "deposits, etc..."] + #[doc = ""] + #[doc = "The dispatch origin of this call must be Signed."] + #[doc = ""] + #[doc = "- `dest`: The recipient of the transfer."] + #[doc = "- `keep_alive`: A boolean to determine if the `transfer_all` operation should send all"] + #[doc = " of the funds the account has, causing the sender account to be killed (false), or"] + #[doc = " transfer everything except at least the existential deposit, which will guarantee to"] + #[doc = " keep the sender account alive (true). # "] + #[doc = "- O(1). Just like transfer, but reading the user's transferable balance first."] + #[doc = " #"] + pub fn transfer_all( + &self, + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + keep_alive: ::core::primitive::bool, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "transfer_all", + TransferAll { dest, keep_alive }, + [ + 46u8, 129u8, 29u8, 177u8, 221u8, 107u8, 245u8, 69u8, 238u8, 126u8, + 145u8, 26u8, 219u8, 208u8, 14u8, 80u8, 149u8, 1u8, 214u8, 63u8, 67u8, + 201u8, 144u8, 45u8, 129u8, 145u8, 174u8, 71u8, 238u8, 113u8, 208u8, + 34u8, + ], + ) + } + #[doc = "Unreserve some balance from a user by force."] + #[doc = ""] + #[doc = "Can only be called by ROOT."] + pub fn force_unreserve( + &self, + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + amount: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "force_unreserve", + ForceUnreserve { who, amount }, + [ + 160u8, 146u8, 137u8, 76u8, 157u8, 187u8, 66u8, 148u8, 207u8, 76u8, + 32u8, 254u8, 82u8, 215u8, 35u8, 161u8, 213u8, 52u8, 32u8, 98u8, 102u8, + 106u8, 234u8, 123u8, 6u8, 175u8, 184u8, 188u8, 174u8, 106u8, 176u8, + 78u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::pallet_balances::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An account was created with some free balance."] + pub struct Endowed { + pub account: ::sp_core::crypto::AccountId32, + pub free_balance: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Endowed { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Endowed"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An account was removed whose balance was non-zero but below ExistentialDeposit,"] + #[doc = "resulting in an outright loss."] + pub struct DustLost { + pub account: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for DustLost { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "DustLost"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Transfer succeeded."] + pub struct Transfer { + pub from: ::sp_core::crypto::AccountId32, + pub to: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Transfer { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Transfer"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A balance was set by root."] + pub struct BalanceSet { + pub who: ::sp_core::crypto::AccountId32, + pub free: ::core::primitive::u128, + pub reserved: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for BalanceSet { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "BalanceSet"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some balance was reserved (moved from free to reserved)."] + pub struct Reserved { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Reserved { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Reserved"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some balance was unreserved (moved from reserved to free)."] + pub struct Unreserved { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Unreserved { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Unreserved"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some balance was moved from the reserve of the first account to the second account."] + #[doc = "Final argument indicates the destination balance type."] + pub struct ReserveRepatriated { + pub from: ::sp_core::crypto::AccountId32, + pub to: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + pub destination_status: + runtime_types::frame_support::traits::tokens::misc::BalanceStatus, + } + impl ::subxt::events::StaticEvent for ReserveRepatriated { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "ReserveRepatriated"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some amount was deposited (e.g. for transaction fees)."] + pub struct Deposit { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Deposit { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Deposit"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some amount was withdrawn from the account (e.g. for transaction fees)."] + pub struct Withdraw { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Withdraw { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Withdraw"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some amount was removed from the account (e.g. for misbehavior)."] + pub struct Slashed { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Slashed { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Slashed"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " The total units issued in the system."] + pub fn total_issuance( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "TotalIssuance", + vec![], + [ + 1u8, 206u8, 252u8, 237u8, 6u8, 30u8, 20u8, 232u8, 164u8, 115u8, 51u8, + 156u8, 156u8, 206u8, 241u8, 187u8, 44u8, 84u8, 25u8, 164u8, 235u8, + 20u8, 86u8, 242u8, 124u8, 23u8, 28u8, 140u8, 26u8, 73u8, 231u8, 51u8, + ], + ) + } + #[doc = " The total units of outstanding deactivated balance in the system."] + pub fn inactive_issuance( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "InactiveIssuance", + vec![], + [ + 74u8, 203u8, 111u8, 142u8, 225u8, 104u8, 173u8, 51u8, 226u8, 12u8, + 85u8, 135u8, 41u8, 206u8, 177u8, 238u8, 94u8, 246u8, 184u8, 250u8, + 140u8, 213u8, 91u8, 118u8, 163u8, 111u8, 211u8, 46u8, 204u8, 160u8, + 154u8, 21u8, + ], + ) + } + #[doc = " The Balances pallet example of storing the balance of an account."] + #[doc = ""] + #[doc = " # Example"] + #[doc = ""] + #[doc = " ```nocompile"] + #[doc = " impl pallet_balances::Config for Runtime {"] + #[doc = " type AccountStore = StorageMapShim, frame_system::Provider, AccountId, Self::AccountData>"] + #[doc = " }"] + #[doc = " ```"] + #[doc = ""] + #[doc = " You can also store the balance of an account in the `System` pallet."] + #[doc = ""] + #[doc = " # Example"] + #[doc = ""] + #[doc = " ```nocompile"] + #[doc = " impl pallet_balances::Config for Runtime {"] + #[doc = " type AccountStore = System"] + #[doc = " }"] + #[doc = " ```"] + #[doc = ""] + #[doc = " But this comes with tradeoffs, storing account balances in the system pallet stores"] + #[doc = " `frame_system` data alongside the account data contrary to storing account balances in the"] + #[doc = " `Balances` pallet, which uses a `StorageMap` to store balances data only."] + #[doc = " NOTE: This is only used in the case that this pallet is used to store balances."] + pub fn account( + &self, + _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::pallet_balances::AccountData<::core::primitive::u128>, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Account", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 246u8, 154u8, 253u8, 71u8, 192u8, 192u8, 192u8, 236u8, 128u8, 80u8, + 40u8, 252u8, 201u8, 43u8, 3u8, 131u8, 19u8, 49u8, 141u8, 240u8, 172u8, + 217u8, 215u8, 109u8, 87u8, 135u8, 248u8, 57u8, 98u8, 185u8, 22u8, 4u8, + ], + ) + } + #[doc = " The Balances pallet example of storing the balance of an account."] + #[doc = ""] + #[doc = " # Example"] + #[doc = ""] + #[doc = " ```nocompile"] + #[doc = " impl pallet_balances::Config for Runtime {"] + #[doc = " type AccountStore = StorageMapShim, frame_system::Provider, AccountId, Self::AccountData>"] + #[doc = " }"] + #[doc = " ```"] + #[doc = ""] + #[doc = " You can also store the balance of an account in the `System` pallet."] + #[doc = ""] + #[doc = " # Example"] + #[doc = ""] + #[doc = " ```nocompile"] + #[doc = " impl pallet_balances::Config for Runtime {"] + #[doc = " type AccountStore = System"] + #[doc = " }"] + #[doc = " ```"] + #[doc = ""] + #[doc = " But this comes with tradeoffs, storing account balances in the system pallet stores"] + #[doc = " `frame_system` data alongside the account data contrary to storing account balances in the"] + #[doc = " `Balances` pallet, which uses a `StorageMap` to store balances data only."] + #[doc = " NOTE: This is only used in the case that this pallet is used to store balances."] + pub fn account_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::pallet_balances::AccountData<::core::primitive::u128>, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Account", + Vec::new(), + [ + 246u8, 154u8, 253u8, 71u8, 192u8, 192u8, 192u8, 236u8, 128u8, 80u8, + 40u8, 252u8, 201u8, 43u8, 3u8, 131u8, 19u8, 49u8, 141u8, 240u8, 172u8, + 217u8, 215u8, 109u8, 87u8, 135u8, 248u8, 57u8, 98u8, 185u8, 22u8, 4u8, + ], + ) + } + #[doc = " Any liquidity locks on some account balances."] + #[doc = " NOTE: Should only be accessed when setting, changing and freeing a lock."] + pub fn locks( + &self, + _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::sp_core::bounded::weak_bounded_vec::WeakBoundedVec< + runtime_types::pallet_balances::BalanceLock<::core::primitive::u128>, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Locks", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 216u8, 253u8, 87u8, 73u8, 24u8, 218u8, 35u8, 0u8, 244u8, 134u8, 195u8, + 58u8, 255u8, 64u8, 153u8, 212u8, 210u8, 232u8, 4u8, 122u8, 90u8, 212u8, + 136u8, 14u8, 127u8, 232u8, 8u8, 192u8, 40u8, 233u8, 18u8, 250u8, + ], + ) + } + #[doc = " Any liquidity locks on some account balances."] + #[doc = " NOTE: Should only be accessed when setting, changing and freeing a lock."] + pub fn locks_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::sp_core::bounded::weak_bounded_vec::WeakBoundedVec< + runtime_types::pallet_balances::BalanceLock<::core::primitive::u128>, + >, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Locks", + Vec::new(), + [ + 216u8, 253u8, 87u8, 73u8, 24u8, 218u8, 35u8, 0u8, 244u8, 134u8, 195u8, + 58u8, 255u8, 64u8, 153u8, 212u8, 210u8, 232u8, 4u8, 122u8, 90u8, 212u8, + 136u8, 14u8, 127u8, 232u8, 8u8, 192u8, 40u8, 233u8, 18u8, 250u8, + ], + ) + } + #[doc = " Named reserves on some account balances."] + pub fn reserves( + &self, + _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::sp_core::bounded::bounded_vec::BoundedVec< + runtime_types::pallet_balances::ReserveData< + [::core::primitive::u8; 8usize], + ::core::primitive::u128, + >, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Reserves", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 17u8, 32u8, 191u8, 46u8, 76u8, 220u8, 101u8, 100u8, 42u8, 250u8, 128u8, + 167u8, 117u8, 44u8, 85u8, 96u8, 105u8, 216u8, 16u8, 147u8, 74u8, 55u8, + 183u8, 94u8, 160u8, 177u8, 26u8, 187u8, 71u8, 197u8, 187u8, 163u8, + ], + ) + } + #[doc = " Named reserves on some account balances."] + pub fn reserves_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::sp_core::bounded::bounded_vec::BoundedVec< + runtime_types::pallet_balances::ReserveData< + [::core::primitive::u8; 8usize], + ::core::primitive::u128, + >, + >, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Reserves", + Vec::new(), + [ + 17u8, 32u8, 191u8, 46u8, 76u8, 220u8, 101u8, 100u8, 42u8, 250u8, 128u8, + 167u8, 117u8, 44u8, 85u8, 96u8, 105u8, 216u8, 16u8, 147u8, 74u8, 55u8, + 183u8, 94u8, 160u8, 177u8, 26u8, 187u8, 71u8, 197u8, 187u8, 163u8, + ], + ) + } + } + } + pub mod constants { + use super::runtime_types; + pub struct ConstantsApi; + impl ConstantsApi { + #[doc = " The minimum amount required to keep an account open."] + pub fn existential_deposit( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, + > { + ::subxt::constants::StaticConstantAddress::new( + "Balances", + "ExistentialDeposit", + [ + 84u8, 157u8, 140u8, 4u8, 93u8, 57u8, 29u8, 133u8, 105u8, 200u8, 214u8, + 27u8, 144u8, 208u8, 218u8, 160u8, 130u8, 109u8, 101u8, 54u8, 210u8, + 136u8, 71u8, 63u8, 49u8, 237u8, 234u8, 15u8, 178u8, 98u8, 148u8, 156u8, + ], + ) + } + #[doc = " The maximum number of locks that should exist on an account."] + #[doc = " Not strictly enforced, but used for weight estimation."] + pub fn max_locks( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "Balances", + "MaxLocks", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + #[doc = " The maximum number of named reserves that can exist on an account."] + pub fn max_reserves( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "Balances", + "MaxReserves", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + } + } + } + pub mod aura { + use super::{root_mod, runtime_types}; + } + pub mod aura_ext { + use super::{root_mod, runtime_types}; + } + pub mod xcmp_queue { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ServiceOverweight { + pub index: ::core::primitive::u64, + pub weight_limit: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SuspendXcmExecution; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ResumeXcmExecution; + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct UpdateSuspendThreshold { + pub new: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct UpdateDropThreshold { + pub new: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct UpdateResumeThreshold { + pub new: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct UpdateThresholdWeight { + pub new: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct UpdateWeightRestrictDecay { + pub new: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct UpdateXcmpMaxIndividualWeight { + pub new: ::sp_weights::Weight, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Services a single overweight XCM."] + #[doc = ""] + #[doc = "- `origin`: Must pass `ExecuteOverweightOrigin`."] + #[doc = "- `index`: The index of the overweight XCM to service"] + #[doc = "- `weight_limit`: The amount of weight that XCM execution may take."] + #[doc = ""] + #[doc = "Errors:"] + #[doc = "- `BadOverweightIndex`: XCM under `index` is not found in the `Overweight` storage map."] + #[doc = "- `BadXcm`: XCM under `index` cannot be properly decoded into a valid XCM format."] + #[doc = "- `WeightOverLimit`: XCM execution may use greater `weight_limit`."] + #[doc = ""] + #[doc = "Events:"] + #[doc = "- `OverweightServiced`: On success."] + pub fn service_overweight( + &self, + index: ::core::primitive::u64, + weight_limit: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "service_overweight", + ServiceOverweight { index, weight_limit }, + [ + 121u8, 236u8, 235u8, 23u8, 210u8, 238u8, 238u8, 122u8, 15u8, 86u8, + 34u8, 119u8, 105u8, 100u8, 214u8, 236u8, 117u8, 39u8, 254u8, 235u8, + 189u8, 15u8, 72u8, 74u8, 225u8, 134u8, 148u8, 126u8, 31u8, 203u8, + 144u8, 106u8, + ], + ) + } + #[doc = "Suspends all XCM executions for the XCMP queue, regardless of the sender's origin."] + #[doc = ""] + #[doc = "- `origin`: Must pass `ControllerOrigin`."] + pub fn suspend_xcm_execution( + &self, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "suspend_xcm_execution", + SuspendXcmExecution {}, + [ + 139u8, 76u8, 166u8, 86u8, 106u8, 144u8, 16u8, 47u8, 105u8, 185u8, 7u8, + 7u8, 63u8, 14u8, 250u8, 236u8, 99u8, 121u8, 101u8, 143u8, 28u8, 175u8, + 108u8, 197u8, 226u8, 43u8, 103u8, 92u8, 186u8, 12u8, 51u8, 153u8, + ], + ) + } + #[doc = "Resumes all XCM executions for the XCMP queue."] + #[doc = ""] + #[doc = "Note that this function doesn't change the status of the in/out bound channels."] + #[doc = ""] + #[doc = "- `origin`: Must pass `ControllerOrigin`."] + pub fn resume_xcm_execution( + &self, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "resume_xcm_execution", + ResumeXcmExecution {}, + [ + 67u8, 111u8, 47u8, 237u8, 79u8, 42u8, 90u8, 56u8, 245u8, 2u8, 20u8, + 23u8, 33u8, 121u8, 135u8, 50u8, 204u8, 147u8, 195u8, 80u8, 177u8, + 202u8, 8u8, 160u8, 164u8, 138u8, 64u8, 252u8, 178u8, 63u8, 102u8, + 245u8, + ], + ) + } + #[doc = "Overwrites the number of pages of messages which must be in the queue for the other side to be told to"] + #[doc = "suspend their sending."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.suspend_value`"] + pub fn update_suspend_threshold( + &self, + new: ::core::primitive::u32, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "update_suspend_threshold", + UpdateSuspendThreshold { new }, + [ + 155u8, 120u8, 9u8, 228u8, 110u8, 62u8, 233u8, 36u8, 57u8, 85u8, 19u8, + 67u8, 246u8, 88u8, 81u8, 116u8, 243u8, 236u8, 174u8, 130u8, 8u8, 246u8, + 254u8, 97u8, 155u8, 207u8, 123u8, 60u8, 164u8, 14u8, 196u8, 97u8, + ], + ) + } + #[doc = "Overwrites the number of pages of messages which must be in the queue after which we drop any further"] + #[doc = "messages from the channel."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.drop_threshold`"] + pub fn update_drop_threshold( + &self, + new: ::core::primitive::u32, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "update_drop_threshold", + UpdateDropThreshold { new }, + [ + 146u8, 177u8, 164u8, 96u8, 247u8, 182u8, 229u8, 175u8, 194u8, 101u8, + 186u8, 168u8, 94u8, 114u8, 172u8, 119u8, 35u8, 222u8, 175u8, 21u8, + 67u8, 61u8, 216u8, 144u8, 194u8, 10u8, 181u8, 62u8, 166u8, 198u8, + 138u8, 243u8, + ], + ) + } + #[doc = "Overwrites the number of pages of messages which the queue must be reduced to before it signals that"] + #[doc = "message sending may recommence after it has been suspended."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.resume_threshold`"] + pub fn update_resume_threshold( + &self, + new: ::core::primitive::u32, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "update_resume_threshold", + UpdateResumeThreshold { new }, + [ + 231u8, 128u8, 80u8, 179u8, 61u8, 50u8, 103u8, 209u8, 103u8, 55u8, + 101u8, 113u8, 150u8, 10u8, 202u8, 7u8, 0u8, 77u8, 58u8, 4u8, 227u8, + 17u8, 225u8, 112u8, 121u8, 203u8, 184u8, 113u8, 231u8, 156u8, 174u8, + 154u8, + ], + ) + } + #[doc = "Overwrites the amount of remaining weight under which we stop processing messages."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.threshold_weight`"] + pub fn update_threshold_weight( + &self, + new: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "update_threshold_weight", + UpdateThresholdWeight { new }, + [ + 14u8, 144u8, 112u8, 207u8, 195u8, 208u8, 184u8, 164u8, 94u8, 41u8, 8u8, + 58u8, 180u8, 80u8, 239u8, 39u8, 210u8, 159u8, 114u8, 169u8, 152u8, + 176u8, 26u8, 161u8, 32u8, 43u8, 250u8, 156u8, 56u8, 21u8, 43u8, 159u8, + ], + ) + } + #[doc = "Overwrites the speed to which the available weight approaches the maximum weight."] + #[doc = "A lower number results in a faster progression. A value of 1 makes the entire weight available initially."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.weight_restrict_decay`."] + pub fn update_weight_restrict_decay( + &self, + new: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "update_weight_restrict_decay", + UpdateWeightRestrictDecay { new }, + [ + 42u8, 53u8, 83u8, 191u8, 51u8, 227u8, 210u8, 193u8, 142u8, 218u8, + 244u8, 177u8, 19u8, 87u8, 148u8, 177u8, 231u8, 197u8, 196u8, 255u8, + 41u8, 130u8, 245u8, 139u8, 107u8, 212u8, 90u8, 161u8, 82u8, 248u8, + 160u8, 223u8, + ], + ) + } + #[doc = "Overwrite the maximum amount of weight any individual message may consume."] + #[doc = "Messages above this weight go into the overweight queue and may only be serviced explicitly."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.xcmp_max_individual_weight`."] + pub fn update_xcmp_max_individual_weight( + &self, + new: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "update_xcmp_max_individual_weight", + UpdateXcmpMaxIndividualWeight { new }, + [ + 148u8, 185u8, 89u8, 36u8, 152u8, 220u8, 248u8, 233u8, 236u8, 82u8, + 170u8, 111u8, 225u8, 142u8, 25u8, 211u8, 72u8, 248u8, 250u8, 14u8, + 45u8, 72u8, 78u8, 95u8, 92u8, 196u8, 245u8, 104u8, 112u8, 128u8, 27u8, + 109u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::cumulus_pallet_xcmp_queue::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some XCM was executed ok."] + pub struct Success { + pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + pub weight: ::sp_weights::Weight, + } + impl ::subxt::events::StaticEvent for Success { + const PALLET: &'static str = "XcmpQueue"; + const EVENT: &'static str = "Success"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some XCM failed."] + pub struct Fail { + pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + pub error: runtime_types::xcm::v3::traits::Error, + pub weight: ::sp_weights::Weight, + } + impl ::subxt::events::StaticEvent for Fail { + const PALLET: &'static str = "XcmpQueue"; + const EVENT: &'static str = "Fail"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Bad XCM version used."] + pub struct BadVersion { + pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + } + impl ::subxt::events::StaticEvent for BadVersion { + const PALLET: &'static str = "XcmpQueue"; + const EVENT: &'static str = "BadVersion"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Bad XCM format used."] + pub struct BadFormat { + pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + } + impl ::subxt::events::StaticEvent for BadFormat { + const PALLET: &'static str = "XcmpQueue"; + const EVENT: &'static str = "BadFormat"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An HRMP message was sent to a sibling parachain."] + pub struct XcmpMessageSent { + pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + } + impl ::subxt::events::StaticEvent for XcmpMessageSent { + const PALLET: &'static str = "XcmpQueue"; + const EVENT: &'static str = "XcmpMessageSent"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An XCM exceeded the individual message weight budget."] + pub struct OverweightEnqueued { + pub sender: runtime_types::polkadot_parachain::primitives::Id, + pub sent_at: ::core::primitive::u32, + pub index: ::core::primitive::u64, + pub required: ::sp_weights::Weight, + } + impl ::subxt::events::StaticEvent for OverweightEnqueued { + const PALLET: &'static str = "XcmpQueue"; + const EVENT: &'static str = "OverweightEnqueued"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An XCM from the overweight queue was executed with the given actual weight used."] + pub struct OverweightServiced { + pub index: ::core::primitive::u64, + pub used: ::sp_weights::Weight, + } + impl ::subxt::events::StaticEvent for OverweightServiced { + const PALLET: &'static str = "XcmpQueue"; + const EVENT: &'static str = "OverweightServiced"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " Status of the inbound XCMP channels."] + pub fn inbound_xcmp_status( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec< + runtime_types::cumulus_pallet_xcmp_queue::InboundChannelDetails, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "InboundXcmpStatus", + vec![], + [ + 183u8, 198u8, 237u8, 153u8, 132u8, 201u8, 87u8, 182u8, 121u8, 164u8, + 129u8, 241u8, 58u8, 192u8, 115u8, 152u8, 7u8, 33u8, 95u8, 51u8, 2u8, + 176u8, 144u8, 12u8, 125u8, 83u8, 92u8, 198u8, 211u8, 101u8, 28u8, 50u8, + ], + ) + } + #[doc = " Inbound aggregate XCMP messages. It can only be one per ParaId/block."] + pub fn inbound_xcmp_messages( + &self, + _0: impl ::std::borrow::Borrow, + _1: impl ::std::borrow::Borrow<::core::primitive::u32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "InboundXcmpMessages", + vec![ + ::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + ), + ::subxt::storage::address::StorageMapKey::new( + _1.borrow(), + ::subxt::storage::address::StorageHasher::Twox64Concat, + ), + ], + [ + 157u8, 232u8, 222u8, 97u8, 218u8, 96u8, 96u8, 90u8, 216u8, 205u8, 39u8, + 130u8, 109u8, 152u8, 127u8, 57u8, 54u8, 63u8, 104u8, 135u8, 33u8, + 175u8, 197u8, 166u8, 238u8, 22u8, 137u8, 162u8, 226u8, 199u8, 87u8, + 25u8, + ], + ) + } + #[doc = " Inbound aggregate XCMP messages. It can only be one per ParaId/block."] + pub fn inbound_xcmp_messages_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "InboundXcmpMessages", + Vec::new(), + [ + 157u8, 232u8, 222u8, 97u8, 218u8, 96u8, 96u8, 90u8, 216u8, 205u8, 39u8, + 130u8, 109u8, 152u8, 127u8, 57u8, 54u8, 63u8, 104u8, 135u8, 33u8, + 175u8, 197u8, 166u8, 238u8, 22u8, 137u8, 162u8, 226u8, 199u8, 87u8, + 25u8, + ], + ) + } + #[doc = " The non-empty XCMP channels in order of becoming non-empty, and the index of the first"] + #[doc = " and last outbound message. If the two indices are equal, then it indicates an empty"] + #[doc = " queue and there must be a non-`Ok` `OutboundStatus`. We assume queues grow no greater"] + #[doc = " than 65535 items. Queue indices for normal messages begin at one; zero is reserved in"] + #[doc = " case of the need to send a high-priority signal message this block."] + #[doc = " The bool is true if there is a signal message waiting to be sent."] + pub fn outbound_xcmp_status( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec< + runtime_types::cumulus_pallet_xcmp_queue::OutboundChannelDetails, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "OutboundXcmpStatus", + vec![], + [ + 238u8, 120u8, 185u8, 141u8, 82u8, 159u8, 41u8, 68u8, 204u8, 15u8, 46u8, + 152u8, 144u8, 74u8, 250u8, 83u8, 71u8, 105u8, 54u8, 53u8, 226u8, 87u8, + 14u8, 202u8, 58u8, 160u8, 54u8, 162u8, 239u8, 248u8, 227u8, 116u8, + ], + ) + } + #[doc = " The messages outbound in a given XCMP channel."] + pub fn outbound_xcmp_messages( + &self, + _0: impl ::std::borrow::Borrow, + _1: impl ::std::borrow::Borrow<::core::primitive::u16>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "OutboundXcmpMessages", + vec![ + ::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + ), + ::subxt::storage::address::StorageMapKey::new( + _1.borrow(), + ::subxt::storage::address::StorageHasher::Twox64Concat, + ), + ], + [ + 50u8, 182u8, 237u8, 191u8, 106u8, 67u8, 54u8, 1u8, 17u8, 107u8, 70u8, + 90u8, 202u8, 8u8, 63u8, 184u8, 171u8, 111u8, 192u8, 196u8, 7u8, 31u8, + 186u8, 68u8, 31u8, 63u8, 71u8, 61u8, 83u8, 223u8, 79u8, 200u8, + ], + ) + } + #[doc = " The messages outbound in a given XCMP channel."] + pub fn outbound_xcmp_messages_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "OutboundXcmpMessages", + Vec::new(), + [ + 50u8, 182u8, 237u8, 191u8, 106u8, 67u8, 54u8, 1u8, 17u8, 107u8, 70u8, + 90u8, 202u8, 8u8, 63u8, 184u8, 171u8, 111u8, 192u8, 196u8, 7u8, 31u8, + 186u8, 68u8, 31u8, 63u8, 71u8, 61u8, 83u8, 223u8, 79u8, 200u8, + ], + ) + } + #[doc = " Any signal messages waiting to be sent."] + pub fn signal_messages( + &self, + _0: impl ::std::borrow::Borrow, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "SignalMessages", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 156u8, 242u8, 186u8, 89u8, 177u8, 195u8, 90u8, 121u8, 94u8, 106u8, + 222u8, 78u8, 19u8, 162u8, 179u8, 96u8, 38u8, 113u8, 209u8, 148u8, 29u8, + 110u8, 106u8, 167u8, 162u8, 96u8, 221u8, 20u8, 33u8, 179u8, 168u8, + 142u8, + ], + ) + } + #[doc = " Any signal messages waiting to be sent."] + pub fn signal_messages_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "SignalMessages", + Vec::new(), + [ + 156u8, 242u8, 186u8, 89u8, 177u8, 195u8, 90u8, 121u8, 94u8, 106u8, + 222u8, 78u8, 19u8, 162u8, 179u8, 96u8, 38u8, 113u8, 209u8, 148u8, 29u8, + 110u8, 106u8, 167u8, 162u8, 96u8, 221u8, 20u8, 33u8, 179u8, 168u8, + 142u8, + ], + ) + } + #[doc = " The configuration which controls the dynamics of the outbound queue."] + pub fn queue_config( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::cumulus_pallet_xcmp_queue::QueueConfigData, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "QueueConfig", + vec![], + [ + 154u8, 172u8, 227u8, 208u8, 130u8, 93u8, 173u8, 129u8, 33u8, 75u8, + 180u8, 100u8, 35u8, 154u8, 40u8, 188u8, 86u8, 53u8, 74u8, 118u8, 131u8, + 159u8, 240u8, 159u8, 185u8, 45u8, 165u8, 6u8, 90u8, 125u8, 77u8, 253u8, + ], + ) + } + #[doc = " The messages that exceeded max individual message weight budget."] + #[doc = ""] + #[doc = " These message stay in this storage map until they are manually dispatched via"] + #[doc = " `service_overweight`."] + pub fn overweight( + &self, + _0: impl ::std::borrow::Borrow<::core::primitive::u64>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<( + runtime_types::polkadot_parachain::primitives::Id, + ::core::primitive::u32, + ::std::vec::Vec<::core::primitive::u8>, + )>, + ::subxt::storage::address::Yes, + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "Overweight", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Twox64Concat, + )], + [ + 222u8, 249u8, 232u8, 110u8, 117u8, 229u8, 165u8, 164u8, 219u8, 219u8, + 149u8, 204u8, 25u8, 78u8, 204u8, 116u8, 111u8, 114u8, 120u8, 222u8, + 56u8, 77u8, 122u8, 147u8, 108u8, 15u8, 94u8, 161u8, 212u8, 50u8, 7u8, + 7u8, + ], + ) + } + #[doc = " The messages that exceeded max individual message weight budget."] + #[doc = ""] + #[doc = " These message stay in this storage map until they are manually dispatched via"] + #[doc = " `service_overweight`."] + pub fn overweight_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<( + runtime_types::polkadot_parachain::primitives::Id, + ::core::primitive::u32, + ::std::vec::Vec<::core::primitive::u8>, + )>, + (), + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "Overweight", + Vec::new(), + [ + 222u8, 249u8, 232u8, 110u8, 117u8, 229u8, 165u8, 164u8, 219u8, 219u8, + 149u8, 204u8, 25u8, 78u8, 204u8, 116u8, 111u8, 114u8, 120u8, 222u8, + 56u8, 77u8, 122u8, 147u8, 108u8, 15u8, 94u8, 161u8, 212u8, 50u8, 7u8, + 7u8, + ], + ) + } + #[doc = "Counter for the related counted storage map"] + pub fn counter_for_overweight( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "CounterForOverweight", + vec![], + [ + 148u8, 226u8, 248u8, 107u8, 165u8, 97u8, 218u8, 160u8, 127u8, 48u8, + 185u8, 251u8, 35u8, 137u8, 119u8, 251u8, 151u8, 167u8, 189u8, 66u8, + 80u8, 74u8, 134u8, 129u8, 222u8, 180u8, 51u8, 182u8, 50u8, 110u8, 10u8, + 43u8, + ], + ) + } + #[doc = " The number of overweight messages ever recorded in `Overweight`. Also doubles as the next"] + #[doc = " available free overweight index."] + pub fn overweight_count( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u64>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "OverweightCount", + vec![], + [ + 102u8, 180u8, 196u8, 148u8, 115u8, 62u8, 46u8, 238u8, 97u8, 116u8, + 117u8, 42u8, 14u8, 5u8, 72u8, 237u8, 230u8, 46u8, 150u8, 126u8, 89u8, + 64u8, 233u8, 166u8, 180u8, 137u8, 52u8, 233u8, 252u8, 255u8, 36u8, + 20u8, + ], + ) + } + #[doc = " Whether or not the XCMP queue is suspended from executing incoming XCMs or not."] + pub fn queue_suspended( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "QueueSuspended", + vec![], + [ + 23u8, 37u8, 48u8, 112u8, 222u8, 17u8, 252u8, 65u8, 160u8, 217u8, 218u8, + 30u8, 2u8, 1u8, 204u8, 0u8, 251u8, 17u8, 138u8, 197u8, 164u8, 50u8, + 122u8, 0u8, 31u8, 238u8, 147u8, 213u8, 30u8, 132u8, 184u8, 215u8, + ], + ) + } + } + } + } + pub mod polkadot_xcm { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Send { + pub dest: ::std::boxed::Box, + pub message: ::std::boxed::Box, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct TeleportAssets { + pub dest: ::std::boxed::Box, + pub beneficiary: ::std::boxed::Box, + pub assets: ::std::boxed::Box, + pub fee_asset_item: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ReserveTransferAssets { + pub dest: ::std::boxed::Box, + pub beneficiary: ::std::boxed::Box, + pub assets: ::std::boxed::Box, + pub fee_asset_item: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Execute { + pub message: ::std::boxed::Box, + pub max_weight: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ForceXcmVersion { + pub location: + ::std::boxed::Box, + pub xcm_version: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ForceDefaultXcmVersion { + pub maybe_xcm_version: ::core::option::Option<::core::primitive::u32>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ForceSubscribeVersionNotify { + pub location: ::std::boxed::Box, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ForceUnsubscribeVersionNotify { + pub location: ::std::boxed::Box, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct LimitedReserveTransferAssets { + pub dest: ::std::boxed::Box, + pub beneficiary: ::std::boxed::Box, + pub assets: ::std::boxed::Box, + pub fee_asset_item: ::core::primitive::u32, + pub weight_limit: runtime_types::xcm::v3::WeightLimit, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct LimitedTeleportAssets { + pub dest: ::std::boxed::Box, + pub beneficiary: ::std::boxed::Box, + pub assets: ::std::boxed::Box, + pub fee_asset_item: ::core::primitive::u32, + pub weight_limit: runtime_types::xcm::v3::WeightLimit, + } + pub struct TransactionApi; + impl TransactionApi { + pub fn send( + &self, + dest: runtime_types::xcm::VersionedMultiLocation, + message: runtime_types::xcm::VersionedXcm, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "send", + Send { + dest: ::std::boxed::Box::new(dest), + message: ::std::boxed::Box::new(message), + }, + [ + 26u8, 89u8, 11u8, 127u8, 105u8, 218u8, 45u8, 49u8, 234u8, 4u8, 72u8, + 86u8, 134u8, 227u8, 122u8, 187u8, 183u8, 63u8, 27u8, 203u8, 123u8, + 200u8, 64u8, 192u8, 64u8, 204u8, 68u8, 98u8, 27u8, 234u8, 176u8, 93u8, + ], + ) + } + #[doc = "Teleport some assets from the local chain to some destination chain."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,"] + #[doc = "with all fees taken as needed from the asset."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the"] + #[doc = " `dest` side. May not be empty."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + pub fn teleport_assets( + &self, + dest: runtime_types::xcm::VersionedMultiLocation, + beneficiary: runtime_types::xcm::VersionedMultiLocation, + assets: runtime_types::xcm::VersionedMultiAssets, + fee_asset_item: ::core::primitive::u32, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "teleport_assets", + TeleportAssets { + dest: ::std::boxed::Box::new(dest), + beneficiary: ::std::boxed::Box::new(beneficiary), + assets: ::std::boxed::Box::new(assets), + fee_asset_item, + }, + [ + 18u8, 130u8, 117u8, 55u8, 235u8, 117u8, 250u8, 246u8, 109u8, 145u8, + 160u8, 184u8, 72u8, 76u8, 61u8, 229u8, 50u8, 94u8, 60u8, 202u8, 25u8, + 180u8, 228u8, 63u8, 171u8, 182u8, 224u8, 146u8, 196u8, 203u8, 243u8, + 70u8, + ], + ) + } + #[doc = "Transfer some assets from the local chain to the sovereign account of a destination"] + #[doc = "chain and forward a notification XCM."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,"] + #[doc = "with all fees taken as needed from the asset."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the"] + #[doc = " `dest` side."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + pub fn reserve_transfer_assets( + &self, + dest: runtime_types::xcm::VersionedMultiLocation, + beneficiary: runtime_types::xcm::VersionedMultiLocation, + assets: runtime_types::xcm::VersionedMultiAssets, + fee_asset_item: ::core::primitive::u32, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "reserve_transfer_assets", + ReserveTransferAssets { + dest: ::std::boxed::Box::new(dest), + beneficiary: ::std::boxed::Box::new(beneficiary), + assets: ::std::boxed::Box::new(assets), + fee_asset_item, + }, + [ + 233u8, 36u8, 82u8, 54u8, 27u8, 201u8, 7u8, 145u8, 212u8, 107u8, 150u8, + 124u8, 125u8, 135u8, 226u8, 60u8, 133u8, 157u8, 202u8, 114u8, 102u8, + 67u8, 170u8, 66u8, 133u8, 228u8, 66u8, 45u8, 55u8, 188u8, 0u8, 19u8, + ], + ) + } + #[doc = "Execute an XCM message from a local, signed, origin."] + #[doc = ""] + #[doc = "An event is deposited indicating whether `msg` could be executed completely or only"] + #[doc = "partially."] + #[doc = ""] + #[doc = "No more than `max_weight` will be used in its attempted execution. If this is less than the"] + #[doc = "maximum amount of weight that the message could take to be executed, then no execution"] + #[doc = "attempt will be made."] + #[doc = ""] + #[doc = "NOTE: A successful return to this does *not* imply that the `msg` was executed successfully"] + #[doc = "to completion; only that *some* of it was executed."] + pub fn execute( + &self, + message: runtime_types::xcm::VersionedXcm, + max_weight: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "execute", + Execute { message: ::std::boxed::Box::new(message), max_weight }, + [ + 144u8, 56u8, 199u8, 18u8, 111u8, 10u8, 128u8, 179u8, 178u8, 216u8, + 119u8, 103u8, 11u8, 218u8, 221u8, 181u8, 129u8, 24u8, 192u8, 66u8, + 219u8, 239u8, 33u8, 161u8, 3u8, 51u8, 25u8, 177u8, 169u8, 188u8, 197u8, + 33u8, + ], + ) + } + #[doc = "Extoll that a particular destination can be communicated with through a particular"] + #[doc = "version of XCM."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `location`: The destination that is being described."] + #[doc = "- `xcm_version`: The latest version of XCM that `location` supports."] + pub fn force_xcm_version( + &self, + location: runtime_types::xcm::v3::multilocation::MultiLocation, + xcm_version: ::core::primitive::u32, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "force_xcm_version", + ForceXcmVersion { location: ::std::boxed::Box::new(location), xcm_version }, + [ + 150u8, 120u8, 133u8, 146u8, 71u8, 43u8, 247u8, 59u8, 124u8, 234u8, + 125u8, 149u8, 216u8, 199u8, 137u8, 17u8, 87u8, 220u8, 110u8, 154u8, + 95u8, 92u8, 251u8, 180u8, 253u8, 1u8, 200u8, 98u8, 36u8, 48u8, 231u8, + 3u8, + ], + ) + } + #[doc = "Set a safe XCM version (the version that XCM should be encoded with if the most recent"] + #[doc = "version a destination can accept is unknown)."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `maybe_xcm_version`: The default XCM encoding version, or `None` to disable."] + pub fn force_default_xcm_version( + &self, + maybe_xcm_version: ::core::option::Option<::core::primitive::u32>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "force_default_xcm_version", + ForceDefaultXcmVersion { maybe_xcm_version }, + [ + 38u8, 36u8, 59u8, 231u8, 18u8, 79u8, 76u8, 9u8, 200u8, 125u8, 214u8, + 166u8, 37u8, 99u8, 111u8, 161u8, 135u8, 2u8, 133u8, 157u8, 165u8, 18u8, + 152u8, 81u8, 209u8, 255u8, 137u8, 237u8, 28u8, 126u8, 224u8, 141u8, + ], + ) + } + #[doc = "Ask a location to notify us regarding their XCM version and any changes to it."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `location`: The location to which we should subscribe for XCM version notifications."] + pub fn force_subscribe_version_notify( + &self, + location: runtime_types::xcm::VersionedMultiLocation, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "force_subscribe_version_notify", + ForceSubscribeVersionNotify { location: ::std::boxed::Box::new(location) }, + [ + 44u8, 16u8, 170u8, 133u8, 130u8, 247u8, 158u8, 196u8, 23u8, 250u8, + 128u8, 106u8, 40u8, 44u8, 245u8, 45u8, 170u8, 223u8, 15u8, 12u8, 204u8, + 159u8, 92u8, 154u8, 186u8, 107u8, 13u8, 70u8, 17u8, 174u8, 192u8, + 111u8, + ], + ) + } + #[doc = "Require that a particular destination should no longer notify us regarding any XCM"] + #[doc = "version changes."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `location`: The location to which we are currently subscribed for XCM version"] + #[doc = " notifications which we no longer desire."] + pub fn force_unsubscribe_version_notify( + &self, + location: runtime_types::xcm::VersionedMultiLocation, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "force_unsubscribe_version_notify", + ForceUnsubscribeVersionNotify { + location: ::std::boxed::Box::new(location), + }, + [ + 77u8, 123u8, 3u8, 212u8, 40u8, 117u8, 46u8, 241u8, 174u8, 0u8, 131u8, + 242u8, 193u8, 177u8, 104u8, 161u8, 42u8, 54u8, 252u8, 70u8, 209u8, + 47u8, 87u8, 181u8, 75u8, 177u8, 157u8, 90u8, 145u8, 110u8, 121u8, + 208u8, + ], + ) + } + #[doc = "Transfer some assets from the local chain to the sovereign account of a destination"] + #[doc = "chain and forward a notification XCM."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight"] + #[doc = "is needed than `weight_limit`, then the operation will fail and the assets send may be"] + #[doc = "at risk."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the"] + #[doc = " `dest` side."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + #[doc = "- `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase."] + pub fn limited_reserve_transfer_assets( + &self, + dest: runtime_types::xcm::VersionedMultiLocation, + beneficiary: runtime_types::xcm::VersionedMultiLocation, + assets: runtime_types::xcm::VersionedMultiAssets, + fee_asset_item: ::core::primitive::u32, + weight_limit: runtime_types::xcm::v3::WeightLimit, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "limited_reserve_transfer_assets", + LimitedReserveTransferAssets { + dest: ::std::boxed::Box::new(dest), + beneficiary: ::std::boxed::Box::new(beneficiary), + assets: ::std::boxed::Box::new(assets), + fee_asset_item, + weight_limit, + }, + [ + 71u8, 241u8, 225u8, 54u8, 199u8, 145u8, 122u8, 171u8, 17u8, 54u8, 20u8, + 80u8, 102u8, 131u8, 241u8, 79u8, 5u8, 3u8, 193u8, 26u8, 22u8, 180u8, + 237u8, 226u8, 109u8, 219u8, 185u8, 161u8, 153u8, 79u8, 102u8, 93u8, + ], + ) + } + #[doc = "Teleport some assets from the local chain to some destination chain."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight"] + #[doc = "is needed than `weight_limit`, then the operation will fail and the assets send may be"] + #[doc = "at risk."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the"] + #[doc = " `dest` side. May not be empty."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + #[doc = "- `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase."] + pub fn limited_teleport_assets( + &self, + dest: runtime_types::xcm::VersionedMultiLocation, + beneficiary: runtime_types::xcm::VersionedMultiLocation, + assets: runtime_types::xcm::VersionedMultiAssets, + fee_asset_item: ::core::primitive::u32, + weight_limit: runtime_types::xcm::v3::WeightLimit, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "limited_teleport_assets", + LimitedTeleportAssets { + dest: ::std::boxed::Box::new(dest), + beneficiary: ::std::boxed::Box::new(beneficiary), + assets: ::std::boxed::Box::new(assets), + fee_asset_item, + weight_limit, + }, + [ + 157u8, 57u8, 28u8, 124u8, 235u8, 202u8, 68u8, 125u8, 203u8, 246u8, + 109u8, 4u8, 199u8, 14u8, 103u8, 38u8, 67u8, 185u8, 81u8, 67u8, 124u8, + 64u8, 197u8, 45u8, 101u8, 199u8, 73u8, 196u8, 162u8, 36u8, 100u8, + 107u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::pallet_xcm::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Execution of an XCM message was attempted."] + #[doc = ""] + #[doc = "\\[ outcome \\]"] + pub struct Attempted(pub runtime_types::xcm::v3::traits::Outcome); + impl ::subxt::events::StaticEvent for Attempted { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "Attempted"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A XCM message was sent."] + #[doc = ""] + #[doc = "\\[ origin, destination, message \\]"] + pub struct Sent( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::v3::Xcm, + ); + impl ::subxt::events::StaticEvent for Sent { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "Sent"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Query response received which does not match a registered query. This may be because a"] + #[doc = "matching query was never registered, it may be because it is a duplicate response, or"] + #[doc = "because the query timed out."] + #[doc = ""] + #[doc = "\\[ origin location, id \\]"] + pub struct UnexpectedResponse( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u64, + ); + impl ::subxt::events::StaticEvent for UnexpectedResponse { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "UnexpectedResponse"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Query response has been received and is ready for taking with `take_response`. There is"] + #[doc = "no registered notification call."] + #[doc = ""] + #[doc = "\\[ id, response \\]"] + pub struct ResponseReady( + pub ::core::primitive::u64, + pub runtime_types::xcm::v3::Response, + ); + impl ::subxt::events::StaticEvent for ResponseReady { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "ResponseReady"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Query response has been received and query is removed. The registered notification has"] + #[doc = "been dispatched and executed successfully."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index \\]"] + pub struct Notified( + pub ::core::primitive::u64, + pub ::core::primitive::u8, + pub ::core::primitive::u8, + ); + impl ::subxt::events::StaticEvent for Notified { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "Notified"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Query response has been received and query is removed. The registered notification could"] + #[doc = "not be dispatched because the dispatch weight is greater than the maximum weight"] + #[doc = "originally budgeted by this runtime for the query result."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index, actual weight, max budgeted weight \\]"] + pub struct NotifyOverweight( + pub ::core::primitive::u64, + pub ::core::primitive::u8, + pub ::core::primitive::u8, + pub ::sp_weights::Weight, + pub ::sp_weights::Weight, + ); + impl ::subxt::events::StaticEvent for NotifyOverweight { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "NotifyOverweight"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Query response has been received and query is removed. There was a general error with"] + #[doc = "dispatching the notification call."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index \\]"] + pub struct NotifyDispatchError( + pub ::core::primitive::u64, + pub ::core::primitive::u8, + pub ::core::primitive::u8, + ); + impl ::subxt::events::StaticEvent for NotifyDispatchError { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "NotifyDispatchError"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Query response has been received and query is removed. The dispatch was unable to be"] + #[doc = "decoded into a `Call`; this might be due to dispatch function having a signature which"] + #[doc = "is not `(origin, QueryId, Response)`."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index \\]"] + pub struct NotifyDecodeFailed( + pub ::core::primitive::u64, + pub ::core::primitive::u8, + pub ::core::primitive::u8, + ); + impl ::subxt::events::StaticEvent for NotifyDecodeFailed { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "NotifyDecodeFailed"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Expected query response has been received but the origin location of the response does"] + #[doc = "not match that expected. The query remains registered for a later, valid, response to"] + #[doc = "be received and acted upon."] + #[doc = ""] + #[doc = "\\[ origin location, id, expected location \\]"] + pub struct InvalidResponder( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u64, + pub ::core::option::Option, + ); + impl ::subxt::events::StaticEvent for InvalidResponder { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "InvalidResponder"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Expected query response has been received but the expected origin location placed in"] + #[doc = "storage by this runtime previously cannot be decoded. The query remains registered."] + #[doc = ""] + #[doc = "This is unexpected (since a location placed in storage in a previously executing"] + #[doc = "runtime should be readable prior to query timeout) and dangerous since the possibly"] + #[doc = "valid response will be dropped. Manual governance intervention is probably going to be"] + #[doc = "needed."] + #[doc = ""] + #[doc = "\\[ origin location, id \\]"] + pub struct InvalidResponderVersion( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u64, + ); + impl ::subxt::events::StaticEvent for InvalidResponderVersion { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "InvalidResponderVersion"; + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Received query response has been read and removed."] + #[doc = ""] + #[doc = "\\[ id \\]"] + pub struct ResponseTaken(pub ::core::primitive::u64); + impl ::subxt::events::StaticEvent for ResponseTaken { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "ResponseTaken"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some assets have been placed in an asset trap."] + #[doc = ""] + #[doc = "\\[ hash, origin, assets \\]"] + pub struct AssetsTrapped( + pub ::subxt::utils::H256, + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::VersionedMultiAssets, + ); + impl ::subxt::events::StaticEvent for AssetsTrapped { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "AssetsTrapped"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An XCM version change notification message has been attempted to be sent."] + #[doc = ""] + #[doc = "The cost of sending it (borne by the chain) is included."] + #[doc = ""] + #[doc = "\\[ destination, result, cost \\]"] + pub struct VersionChangeNotified( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u32, + pub runtime_types::xcm::v3::multiasset::MultiAssets, + ); + impl ::subxt::events::StaticEvent for VersionChangeNotified { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "VersionChangeNotified"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "The supported version of a location has been changed. This might be through an"] + #[doc = "automatic notification or a manual intervention."] + #[doc = ""] + #[doc = "\\[ location, XCM version \\]"] + pub struct SupportedVersionChanged( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u32, + ); + impl ::subxt::events::StaticEvent for SupportedVersionChanged { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "SupportedVersionChanged"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A given location which had a version change subscription was dropped owing to an error"] + #[doc = "sending the notification to it."] + #[doc = ""] + #[doc = "\\[ location, query ID, error \\]"] + pub struct NotifyTargetSendFail( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u64, + pub runtime_types::xcm::v3::traits::Error, + ); + impl ::subxt::events::StaticEvent for NotifyTargetSendFail { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "NotifyTargetSendFail"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A given location which had a version change subscription was dropped owing to an error"] + #[doc = "migrating the location to our new XCM format."] + #[doc = ""] + #[doc = "\\[ location, query ID \\]"] + pub struct NotifyTargetMigrationFail( + pub runtime_types::xcm::VersionedMultiLocation, + pub ::core::primitive::u64, + ); + impl ::subxt::events::StaticEvent for NotifyTargetMigrationFail { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "NotifyTargetMigrationFail"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Expected query response has been received but the expected querier location placed in"] + #[doc = "storage by this runtime previously cannot be decoded. The query remains registered."] + #[doc = ""] + #[doc = "This is unexpected (since a location placed in storage in a previously executing"] + #[doc = "runtime should be readable prior to query timeout) and dangerous since the possibly"] + #[doc = "valid response will be dropped. Manual governance intervention is probably going to be"] + #[doc = "needed."] + #[doc = ""] + #[doc = "\\[ origin location, id \\]"] + pub struct InvalidQuerierVersion( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u64, + ); + impl ::subxt::events::StaticEvent for InvalidQuerierVersion { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "InvalidQuerierVersion"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Expected query response has been received but the querier location of the response does"] + #[doc = "not match the expected. The query remains registered for a later, valid, response to"] + #[doc = "be received and acted upon."] + #[doc = ""] + #[doc = "\\[ origin location, id, expected querier, maybe actual querier \\]"] + pub struct InvalidQuerier( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u64, + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::option::Option, + ); + impl ::subxt::events::StaticEvent for InvalidQuerier { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "InvalidQuerier"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A remote has requested XCM version change notification from us and we have honored it."] + #[doc = "A version information message is sent to them and its cost is included."] + #[doc = ""] + #[doc = "\\[ destination location, cost \\]"] + pub struct VersionNotifyStarted( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::v3::multiasset::MultiAssets, + ); + impl ::subxt::events::StaticEvent for VersionNotifyStarted { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "VersionNotifyStarted"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "We have requested that a remote chain sends us XCM version change notifications."] + #[doc = ""] + #[doc = "\\[ destination location, cost \\]"] + pub struct VersionNotifyRequested( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::v3::multiasset::MultiAssets, + ); + impl ::subxt::events::StaticEvent for VersionNotifyRequested { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "VersionNotifyRequested"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "We have requested that a remote chain stops sending us XCM version change notifications."] + #[doc = ""] + #[doc = "\\[ destination location, cost \\]"] + pub struct VersionNotifyUnrequested( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::v3::multiasset::MultiAssets, + ); + impl ::subxt::events::StaticEvent for VersionNotifyUnrequested { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "VersionNotifyUnrequested"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Fees were paid from a location for an operation (often for using `SendXcm`)."] + #[doc = ""] + #[doc = "\\[ paying location, fees \\]"] + pub struct FeesPaid( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::v3::multiasset::MultiAssets, + ); + impl ::subxt::events::StaticEvent for FeesPaid { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "FeesPaid"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some assets have been claimed from an asset trap"] + #[doc = ""] + #[doc = "\\[ hash, origin, assets \\]"] + pub struct AssetsClaimed( + pub ::subxt::utils::H256, + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::VersionedMultiAssets, + ); + impl ::subxt::events::StaticEvent for AssetsClaimed { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "AssetsClaimed"; + } + } + } + pub mod cumulus_xcm { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + pub struct TransactionApi; + impl TransactionApi {} + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::cumulus_pallet_xcm::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message is invalid XCM."] + #[doc = "\\[ id \\]"] + pub struct InvalidFormat(pub [::core::primitive::u8; 32usize]); + impl ::subxt::events::StaticEvent for InvalidFormat { + const PALLET: &'static str = "CumulusXcm"; + const EVENT: &'static str = "InvalidFormat"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message is unsupported version of XCM."] + #[doc = "\\[ id \\]"] + pub struct UnsupportedVersion(pub [::core::primitive::u8; 32usize]); + impl ::subxt::events::StaticEvent for UnsupportedVersion { + const PALLET: &'static str = "CumulusXcm"; + const EVENT: &'static str = "UnsupportedVersion"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message executed with the given outcome."] + #[doc = "\\[ id, outcome \\]"] + pub struct ExecutedDownward( + pub [::core::primitive::u8; 32usize], + pub runtime_types::xcm::v3::traits::Outcome, + ); + impl ::subxt::events::StaticEvent for ExecutedDownward { + const PALLET: &'static str = "CumulusXcm"; + const EVENT: &'static str = "ExecutedDownward"; + } + } + } + pub mod dmp_queue { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ServiceOverweight { + pub index: ::core::primitive::u64, + pub weight_limit: ::sp_weights::Weight, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Service a single overweight message."] + pub fn service_overweight( + &self, + index: ::core::primitive::u64, + weight_limit: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "DmpQueue", + "service_overweight", + ServiceOverweight { index, weight_limit }, + [ + 121u8, 236u8, 235u8, 23u8, 210u8, 238u8, 238u8, 122u8, 15u8, 86u8, + 34u8, 119u8, 105u8, 100u8, 214u8, 236u8, 117u8, 39u8, 254u8, 235u8, + 189u8, 15u8, 72u8, 74u8, 225u8, 134u8, 148u8, 126u8, 31u8, 203u8, + 144u8, 106u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::cumulus_pallet_dmp_queue::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message is invalid XCM."] + pub struct InvalidFormat { + pub message_id: [::core::primitive::u8; 32usize], + } + impl ::subxt::events::StaticEvent for InvalidFormat { + const PALLET: &'static str = "DmpQueue"; + const EVENT: &'static str = "InvalidFormat"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message is unsupported version of XCM."] + pub struct UnsupportedVersion { + pub message_id: [::core::primitive::u8; 32usize], + } + impl ::subxt::events::StaticEvent for UnsupportedVersion { + const PALLET: &'static str = "DmpQueue"; + const EVENT: &'static str = "UnsupportedVersion"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message executed with the given outcome."] + pub struct ExecutedDownward { + pub message_id: [::core::primitive::u8; 32usize], + pub outcome: runtime_types::xcm::v3::traits::Outcome, + } + impl ::subxt::events::StaticEvent for ExecutedDownward { + const PALLET: &'static str = "DmpQueue"; + const EVENT: &'static str = "ExecutedDownward"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "The weight limit for handling downward messages was reached."] + pub struct WeightExhausted { + pub message_id: [::core::primitive::u8; 32usize], + pub remaining_weight: ::sp_weights::Weight, + pub required_weight: ::sp_weights::Weight, + } + impl ::subxt::events::StaticEvent for WeightExhausted { + const PALLET: &'static str = "DmpQueue"; + const EVENT: &'static str = "WeightExhausted"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message is overweight and was placed in the overweight queue."] + pub struct OverweightEnqueued { + pub message_id: [::core::primitive::u8; 32usize], + pub overweight_index: ::core::primitive::u64, + pub required_weight: ::sp_weights::Weight, + } + impl ::subxt::events::StaticEvent for OverweightEnqueued { + const PALLET: &'static str = "DmpQueue"; + const EVENT: &'static str = "OverweightEnqueued"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message from the overweight queue was executed."] + pub struct OverweightServiced { + pub overweight_index: ::core::primitive::u64, + pub weight_used: ::sp_weights::Weight, + } + impl ::subxt::events::StaticEvent for OverweightServiced { + const PALLET: &'static str = "DmpQueue"; + const EVENT: &'static str = "OverweightServiced"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " The configuration."] + pub fn configuration( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::cumulus_pallet_dmp_queue::ConfigData, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "DmpQueue", + "Configuration", + vec![], + [ + 133u8, 113u8, 115u8, 164u8, 128u8, 145u8, 234u8, 106u8, 150u8, 54u8, + 247u8, 135u8, 181u8, 197u8, 178u8, 30u8, 204u8, 46u8, 6u8, 137u8, 82u8, + 1u8, 75u8, 171u8, 7u8, 157u8, 3u8, 19u8, 92u8, 10u8, 234u8, 66u8, + ], + ) + } + #[doc = " The page index."] + pub fn page_index( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::cumulus_pallet_dmp_queue::PageIndexData, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "DmpQueue", + "PageIndex", + vec![], + [ + 94u8, 132u8, 34u8, 67u8, 10u8, 22u8, 235u8, 96u8, 168u8, 26u8, 57u8, + 200u8, 130u8, 218u8, 37u8, 71u8, 28u8, 119u8, 78u8, 107u8, 209u8, + 120u8, 190u8, 2u8, 101u8, 215u8, 122u8, 187u8, 94u8, 38u8, 255u8, + 234u8, + ], + ) + } + #[doc = " The queue pages."] + pub fn pages( + &self, + _0: impl ::std::borrow::Borrow<::core::primitive::u32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec<( + ::core::primitive::u32, + ::std::vec::Vec<::core::primitive::u8>, + )>, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "DmpQueue", + "Pages", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 228u8, 86u8, 33u8, 107u8, 248u8, 4u8, 223u8, 175u8, 222u8, 25u8, 204u8, + 42u8, 235u8, 21u8, 215u8, 91u8, 167u8, 14u8, 133u8, 151u8, 190u8, 57u8, + 138u8, 208u8, 79u8, 244u8, 132u8, 14u8, 48u8, 247u8, 171u8, 108u8, + ], + ) + } + #[doc = " The queue pages."] + pub fn pages_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec<( + ::core::primitive::u32, + ::std::vec::Vec<::core::primitive::u8>, + )>, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "DmpQueue", + "Pages", + Vec::new(), + [ + 228u8, 86u8, 33u8, 107u8, 248u8, 4u8, 223u8, 175u8, 222u8, 25u8, 204u8, + 42u8, 235u8, 21u8, 215u8, 91u8, 167u8, 14u8, 133u8, 151u8, 190u8, 57u8, + 138u8, 208u8, 79u8, 244u8, 132u8, 14u8, 48u8, 247u8, 171u8, 108u8, + ], + ) + } + #[doc = " The overweight messages."] + pub fn overweight( + &self, + _0: impl ::std::borrow::Borrow<::core::primitive::u64>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<( + ::core::primitive::u32, + ::std::vec::Vec<::core::primitive::u8>, + )>, + ::subxt::storage::address::Yes, + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "DmpQueue", + "Overweight", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 222u8, 85u8, 143u8, 49u8, 42u8, 248u8, 138u8, 163u8, 46u8, 199u8, + 188u8, 61u8, 137u8, 135u8, 127u8, 146u8, 210u8, 254u8, 121u8, 42u8, + 112u8, 114u8, 22u8, 228u8, 207u8, 207u8, 245u8, 175u8, 152u8, 140u8, + 225u8, 237u8, + ], + ) + } + #[doc = " The overweight messages."] + pub fn overweight_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<( + ::core::primitive::u32, + ::std::vec::Vec<::core::primitive::u8>, + )>, + (), + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "DmpQueue", + "Overweight", + Vec::new(), + [ + 222u8, 85u8, 143u8, 49u8, 42u8, 248u8, 138u8, 163u8, 46u8, 199u8, + 188u8, 61u8, 137u8, 135u8, 127u8, 146u8, 210u8, 254u8, 121u8, 42u8, + 112u8, 114u8, 22u8, 228u8, 207u8, 207u8, 245u8, 175u8, 152u8, 140u8, + 225u8, 237u8, + ], + ) + } + #[doc = "Counter for the related counted storage map"] + pub fn counter_for_overweight( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "DmpQueue", + "CounterForOverweight", + vec![], + [ + 148u8, 226u8, 248u8, 107u8, 165u8, 97u8, 218u8, 160u8, 127u8, 48u8, + 185u8, 251u8, 35u8, 137u8, 119u8, 251u8, 151u8, 167u8, 189u8, 66u8, + 80u8, 74u8, 134u8, 129u8, 222u8, 180u8, 51u8, 182u8, 50u8, 110u8, 10u8, + 43u8, + ], + ) + } + } + } + } + pub mod bridge_relayers { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ClaimRewards { + pub lane_id: runtime_types::bp_messages::LaneId, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Claim accumulated rewards."] + pub fn claim_rewards( + &self, + lane_id: runtime_types::bp_messages::LaneId, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeRelayers", + "claim_rewards", + ClaimRewards { lane_id }, + [ + 119u8, 55u8, 12u8, 215u8, 255u8, 17u8, 1u8, 92u8, 187u8, 154u8, 120u8, + 10u8, 51u8, 16u8, 160u8, 18u8, 194u8, 236u8, 95u8, 173u8, 64u8, 75u8, + 251u8, 68u8, 235u8, 116u8, 69u8, 240u8, 186u8, 153u8, 118u8, 17u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::pallet_bridge_relayers::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Reward has been paid to the relayer."] + pub struct RewardPaid { + pub relayer: ::sp_core::crypto::AccountId32, + pub lane_id: runtime_types::bp_messages::LaneId, + pub reward: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for RewardPaid { + const PALLET: &'static str = "BridgeRelayers"; + const EVENT: &'static str = "RewardPaid"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " Map of the relayer => accumulated reward."] + pub fn relayer_rewards( + &self, + _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, + _1: impl ::std::borrow::Borrow, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, + ::subxt::storage::address::Yes, + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeRelayers", + "RelayerRewards", + vec![ + ::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + ), + ::subxt::storage::address::StorageMapKey::new( + _1.borrow(), + ::subxt::storage::address::StorageHasher::Identity, + ), + ], + [ + 79u8, 50u8, 107u8, 107u8, 201u8, 181u8, 235u8, 111u8, 185u8, 125u8, + 108u8, 204u8, 163u8, 11u8, 69u8, 236u8, 90u8, 232u8, 9u8, 225u8, 210u8, + 142u8, 175u8, 166u8, 159u8, 74u8, 112u8, 249u8, 16u8, 115u8, 144u8, + 177u8, + ], + ) + } + #[doc = " Map of the relayer => accumulated reward."] + pub fn relayer_rewards_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, + (), + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeRelayers", + "RelayerRewards", + Vec::new(), + [ + 79u8, 50u8, 107u8, 107u8, 201u8, 181u8, 235u8, 111u8, 185u8, 125u8, + 108u8, 204u8, 163u8, 11u8, 69u8, 236u8, 90u8, 232u8, 9u8, 225u8, 210u8, + 142u8, 175u8, 166u8, 159u8, 74u8, 112u8, 249u8, 16u8, 115u8, 144u8, + 177u8, + ], + ) + } + } + } + } + pub mod bridge_millau_grandpa { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SubmitFinalityProof { + pub finality_target: ::std::boxed::Box< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + pub justification: ::bp_header_chain::justification::GrandpaJustification< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Initialize { + pub init_data: ::bp_header_chain::InitializationData< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetOwner { + pub new_owner: ::core::option::Option<::sp_core::crypto::AccountId32>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetOperatingMode { + pub operating_mode: runtime_types::bp_runtime::BasicOperatingMode, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Verify a target header is finalized according to the given finality proof."] + #[doc = ""] + #[doc = "It will use the underlying storage pallet to fetch information about the current"] + #[doc = "authorities and best finalized header in order to verify that the header is finalized."] + #[doc = ""] + #[doc = "If successful in verification, it will write the target header to the underlying storage"] + #[doc = "pallet."] + pub fn submit_finality_proof( + &self, + finality_target: ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + justification: ::bp_header_chain::justification::GrandpaJustification< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauGrandpa", + "submit_finality_proof", + SubmitFinalityProof { + finality_target: ::std::boxed::Box::new(finality_target), + justification, + }, + [ + 3u8, 161u8, 243u8, 208u8, 245u8, 135u8, 86u8, 233u8, 103u8, 140u8, + 81u8, 3u8, 119u8, 185u8, 68u8, 167u8, 208u8, 155u8, 169u8, 201u8, + 209u8, 248u8, 162u8, 45u8, 155u8, 225u8, 173u8, 62u8, 156u8, 171u8, + 19u8, 190u8, + ], + ) + } + #[doc = "Bootstrap the bridge pallet with an initial header and authority set from which to sync."] + #[doc = ""] + #[doc = "The initial configuration provided does not need to be the genesis header of the bridged"] + #[doc = "chain, it can be any arbitrary header. You can also provide the next scheduled set"] + #[doc = "change if it is already know."] + #[doc = ""] + #[doc = "This function is only allowed to be called from a trusted origin and writes to storage"] + #[doc = "with practically no checks in terms of the validity of the data. It is important that"] + #[doc = "you ensure that valid data is being passed in."] + pub fn initialize( + &self, + init_data: ::bp_header_chain::InitializationData< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauGrandpa", + "initialize", + Initialize { init_data }, + [ + 244u8, 188u8, 202u8, 145u8, 218u8, 91u8, 74u8, 80u8, 41u8, 185u8, + 239u8, 178u8, 231u8, 128u8, 198u8, 90u8, 135u8, 219u8, 200u8, 23u8, + 194u8, 47u8, 61u8, 222u8, 194u8, 84u8, 142u8, 37u8, 64u8, 37u8, 69u8, + 198u8, + ], + ) + } + #[doc = "Change `PalletOwner`."] + #[doc = ""] + #[doc = "May only be called either by root, or by `PalletOwner`."] + pub fn set_owner( + &self, + new_owner: ::core::option::Option<::sp_core::crypto::AccountId32>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauGrandpa", + "set_owner", + SetOwner { new_owner }, + [ + 100u8, 221u8, 84u8, 142u8, 158u8, 5u8, 47u8, 212u8, 9u8, 35u8, 82u8, + 135u8, 108u8, 238u8, 231u8, 197u8, 77u8, 219u8, 176u8, 222u8, 88u8, + 167u8, 152u8, 34u8, 177u8, 244u8, 160u8, 195u8, 211u8, 3u8, 66u8, + 253u8, + ], + ) + } + #[doc = "Halt or resume all pallet operations."] + #[doc = ""] + #[doc = "May only be called either by root, or by `PalletOwner`."] + pub fn set_operating_mode( + &self, + operating_mode: runtime_types::bp_runtime::BasicOperatingMode, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauGrandpa", + "set_operating_mode", + SetOperatingMode { operating_mode }, + [ + 128u8, 25u8, 81u8, 145u8, 111u8, 185u8, 226u8, 152u8, 18u8, 51u8, 89u8, + 236u8, 200u8, 157u8, 157u8, 186u8, 207u8, 208u8, 152u8, 168u8, 12u8, + 39u8, 249u8, 48u8, 195u8, 160u8, 54u8, 73u8, 30u8, 230u8, 25u8, 46u8, + ], + ) + } + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " The current number of requests which have written to storage."] + #[doc = ""] + #[doc = " If the `RequestCount` hits `MaxRequests`, no more calls will be allowed to the pallet until"] + #[doc = " the request capacity is increased."] + #[doc = ""] + #[doc = " The `RequestCount` is decreased by one at the beginning of every block. This is to ensure"] + #[doc = " that the pallet can always make progress."] + pub fn request_count( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "RequestCount", + vec![], + [ + 100u8, 156u8, 98u8, 176u8, 229u8, 85u8, 81u8, 159u8, 120u8, 156u8, + 33u8, 179u8, 224u8, 237u8, 52u8, 198u8, 81u8, 81u8, 10u8, 180u8, 53u8, + 141u8, 96u8, 4u8, 39u8, 217u8, 58u8, 9u8, 57u8, 79u8, 47u8, 201u8, + ], + ) + } + #[doc = " Hash of the header used to bootstrap the pallet."] + pub fn initial_hash( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::bp_millau::MillauHash>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "InitialHash", + vec![], + [ + 167u8, 131u8, 64u8, 215u8, 102u8, 70u8, 21u8, 34u8, 254u8, 233u8, 2u8, + 49u8, 253u8, 67u8, 235u8, 10u8, 21u8, 223u8, 220u8, 198u8, 180u8, + 137u8, 88u8, 251u8, 230u8, 108u8, 9u8, 104u8, 101u8, 105u8, 38u8, + 138u8, + ], + ) + } + #[doc = " Hash of the best finalized header."] + pub fn best_finalized( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_runtime::HeaderId< + ::bp_millau::MillauHash, + ::core::primitive::u64, + >, + >, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "BestFinalized", + vec![], + [ + 61u8, 173u8, 97u8, 223u8, 180u8, 235u8, 85u8, 126u8, 217u8, 228u8, + 87u8, 174u8, 116u8, 156u8, 162u8, 252u8, 100u8, 200u8, 138u8, 14u8, + 102u8, 177u8, 66u8, 164u8, 216u8, 114u8, 18u8, 223u8, 94u8, 78u8, + 107u8, 58u8, + ], + ) + } + #[doc = " A ring buffer of imported hashes. Ordered by the insertion time."] + pub fn imported_hashes( + &self, + _0: impl ::std::borrow::Borrow<::core::primitive::u32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::bp_millau::MillauHash>, + ::subxt::storage::address::Yes, + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "ImportedHashes", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Identity, + )], + [ + 249u8, 185u8, 202u8, 36u8, 40u8, 197u8, 111u8, 168u8, 136u8, 138u8, + 84u8, 135u8, 69u8, 34u8, 181u8, 46u8, 158u8, 16u8, 150u8, 190u8, 81u8, + 53u8, 239u8, 199u8, 83u8, 93u8, 197u8, 205u8, 129u8, 173u8, 141u8, + 43u8, + ], + ) + } + #[doc = " A ring buffer of imported hashes. Ordered by the insertion time."] + pub fn imported_hashes_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::bp_millau::MillauHash>, + (), + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "ImportedHashes", + Vec::new(), + [ + 249u8, 185u8, 202u8, 36u8, 40u8, 197u8, 111u8, 168u8, 136u8, 138u8, + 84u8, 135u8, 69u8, 34u8, 181u8, 46u8, 158u8, 16u8, 150u8, 190u8, 81u8, + 53u8, 239u8, 199u8, 83u8, 93u8, 197u8, 205u8, 129u8, 173u8, 141u8, + 43u8, + ], + ) + } + #[doc = " Current ring buffer position."] + pub fn imported_hashes_pointer( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "ImportedHashesPointer", + vec![], + [ + 159u8, 83u8, 35u8, 45u8, 27u8, 249u8, 155u8, 131u8, 181u8, 196u8, + 224u8, 26u8, 92u8, 132u8, 127u8, 237u8, 13u8, 142u8, 196u8, 147u8, + 221u8, 216u8, 11u8, 78u8, 190u8, 241u8, 201u8, 96u8, 74u8, 185u8, + 208u8, 42u8, + ], + ) + } + #[doc = " Relevant fields of imported headers."] + pub fn imported_headers( + &self, + _0: impl ::std::borrow::Borrow<::bp_millau::MillauHash>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_header_chain::StoredHeaderData< + ::core::primitive::u64, + ::bp_millau::MillauHash, + >, + >, + ::subxt::storage::address::Yes, + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "ImportedHeaders", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Identity, + )], + [ + 109u8, 41u8, 102u8, 223u8, 133u8, 169u8, 46u8, 221u8, 235u8, 67u8, + 28u8, 192u8, 2u8, 242u8, 215u8, 166u8, 227u8, 182u8, 136u8, 217u8, + 61u8, 10u8, 246u8, 70u8, 17u8, 246u8, 223u8, 113u8, 9u8, 136u8, 181u8, + 242u8, + ], + ) + } + #[doc = " Relevant fields of imported headers."] + pub fn imported_headers_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_header_chain::StoredHeaderData< + ::core::primitive::u64, + ::bp_millau::MillauHash, + >, + >, + (), + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "ImportedHeaders", + Vec::new(), + [ + 109u8, 41u8, 102u8, 223u8, 133u8, 169u8, 46u8, 221u8, 235u8, 67u8, + 28u8, 192u8, 2u8, 242u8, 215u8, 166u8, 227u8, 182u8, 136u8, 217u8, + 61u8, 10u8, 246u8, 70u8, 17u8, 246u8, 223u8, 113u8, 9u8, 136u8, 181u8, + 242u8, + ], + ) + } + #[doc = " The current GRANDPA Authority set."] + pub fn current_authority_set( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::pallet_bridge_grandpa::storage_types::StoredAuthoritySet, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "CurrentAuthoritySet", + vec![], + [ + 249u8, 40u8, 229u8, 120u8, 141u8, 219u8, 206u8, 5u8, 54u8, 121u8, + 207u8, 77u8, 8u8, 80u8, 105u8, 107u8, 151u8, 111u8, 82u8, 119u8, 8u8, + 31u8, 104u8, 82u8, 92u8, 156u8, 37u8, 160u8, 235u8, 64u8, 62u8, 94u8, + ], + ) + } + #[doc = " Optional pallet owner."] + #[doc = ""] + #[doc = " Pallet owner has a right to halt all pallet operations and then resume it. If it is"] + #[doc = " `None`, then there are no direct ways to halt/resume pallet operations, but other"] + #[doc = " runtime methods may still be used to do that (i.e. democracy::referendum to update halt"] + #[doc = " flag directly or call the `halt_operations`)."] + pub fn pallet_owner( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::sp_core::crypto::AccountId32>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "PalletOwner", + vec![], + [ + 89u8, 42u8, 74u8, 119u8, 21u8, 164u8, 30u8, 115u8, 207u8, 126u8, 98u8, + 16u8, 162u8, 214u8, 67u8, 172u8, 178u8, 223u8, 139u8, 121u8, 174u8, + 89u8, 215u8, 75u8, 200u8, 161u8, 61u8, 195u8, 65u8, 222u8, 246u8, + 233u8, + ], + ) + } + #[doc = " The current operating mode of the pallet."] + #[doc = ""] + #[doc = " Depending on the mode either all, or no transactions will be allowed."] + pub fn pallet_operating_mode( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_runtime::BasicOperatingMode, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "PalletOperatingMode", + vec![], + [ + 218u8, 66u8, 212u8, 71u8, 38u8, 152u8, 55u8, 129u8, 125u8, 231u8, 91u8, + 216u8, 157u8, 141u8, 173u8, 146u8, 30u8, 40u8, 132u8, 107u8, 97u8, + 39u8, 36u8, 81u8, 231u8, 222u8, 113u8, 136u8, 233u8, 212u8, 225u8, + 75u8, + ], + ) + } + } + } + pub mod constants { + use super::runtime_types; + pub struct ConstantsApi; + impl ConstantsApi { + #[doc = " The upper bound on the number of requests allowed by the pallet."] + #[doc = ""] + #[doc = " A request refers to an action which writes a header to storage."] + #[doc = ""] + #[doc = " Once this bound is reached the pallet will not allow any dispatchables to be called"] + #[doc = " until the request count has decreased."] + pub fn max_requests( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "BridgeMillauGrandpa", + "MaxRequests", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + #[doc = " Maximal number of finalized headers to keep in the storage."] + #[doc = ""] + #[doc = " The setting is there to prevent growing the on-chain state indefinitely. Note"] + #[doc = " the setting does not relate to block numbers - we will simply keep as much items"] + #[doc = " in the storage, so it doesn't guarantee any fixed timeframe for finality headers."] + #[doc = ""] + #[doc = " Incautious change of this constant may lead to orphan entries in the runtime storage."] + pub fn headers_to_keep( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "BridgeMillauGrandpa", + "HeadersToKeep", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + #[doc = " Max number of authorities at the bridged chain."] + pub fn max_bridged_authorities( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "BridgeMillauGrandpa", + "MaxBridgedAuthorities", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + } + } + } + pub mod bridge_millau_messages { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetOwner { + pub new_owner: ::core::option::Option<::sp_core::crypto::AccountId32>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetOperatingMode { + pub operating_mode: runtime_types::bp_messages::MessagesOperatingMode, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ReceiveMessagesProof { + pub relayer_id_at_bridged_chain: ::sp_core::crypto::AccountId32, + pub proof: ::bridge_runtime_common::messages::target::FromBridgedChainMessagesProof< + ::bp_millau::MillauHash, + >, + pub messages_count: ::core::primitive::u32, + pub dispatch_weight: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ReceiveMessagesDeliveryProof { pub proof : :: bridge_runtime_common :: messages :: source :: FromBridgedChainMessagesDeliveryProof < :: bp_millau :: MillauHash > , pub relayers_state : :: bp_messages :: UnrewardedRelayersState , } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Change `PalletOwner`."] + #[doc = ""] + #[doc = "May only be called either by root, or by `PalletOwner`."] + pub fn set_owner( + &self, + new_owner: ::core::option::Option<::sp_core::crypto::AccountId32>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauMessages", + "set_owner", + SetOwner { new_owner }, + [ + 100u8, 221u8, 84u8, 142u8, 158u8, 5u8, 47u8, 212u8, 9u8, 35u8, 82u8, + 135u8, 108u8, 238u8, 231u8, 197u8, 77u8, 219u8, 176u8, 222u8, 88u8, + 167u8, 152u8, 34u8, 177u8, 244u8, 160u8, 195u8, 211u8, 3u8, 66u8, + 253u8, + ], + ) + } + #[doc = "Halt or resume all/some pallet operations."] + #[doc = ""] + #[doc = "May only be called either by root, or by `PalletOwner`."] + pub fn set_operating_mode( + &self, + operating_mode: runtime_types::bp_messages::MessagesOperatingMode, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauMessages", + "set_operating_mode", + SetOperatingMode { operating_mode }, + [ + 236u8, 230u8, 127u8, 17u8, 145u8, 186u8, 102u8, 200u8, 227u8, 208u8, + 230u8, 121u8, 102u8, 199u8, 123u8, 118u8, 199u8, 160u8, 131u8, 116u8, + 102u8, 167u8, 119u8, 144u8, 70u8, 114u8, 0u8, 223u8, 54u8, 197u8, 39u8, + 58u8, + ], + ) + } + #[doc = "Receive messages proof from bridged chain."] + #[doc = ""] + #[doc = "The weight of the call assumes that the transaction always brings outbound lane"] + #[doc = "state update. Because of that, the submitter (relayer) has no benefit of not including"] + #[doc = "this data in the transaction, so reward confirmations lags should be minimal."] + pub fn receive_messages_proof( + &self, + relayer_id_at_bridged_chain: ::sp_core::crypto::AccountId32, + proof: ::bridge_runtime_common::messages::target::FromBridgedChainMessagesProof< + ::bp_millau::MillauHash, + >, + messages_count: ::core::primitive::u32, + dispatch_weight: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauMessages", + "receive_messages_proof", + ReceiveMessagesProof { + relayer_id_at_bridged_chain, + proof, + messages_count, + dispatch_weight, + }, + [ + 14u8, 23u8, 191u8, 242u8, 252u8, 163u8, 87u8, 149u8, 211u8, 25u8, + 112u8, 192u8, 206u8, 49u8, 135u8, 124u8, 79u8, 92u8, 204u8, 39u8, 80u8, + 139u8, 155u8, 124u8, 134u8, 1u8, 66u8, 68u8, 213u8, 85u8, 42u8, 250u8, + ], + ) + } + #[doc = "Receive messages delivery proof from bridged chain."] + pub fn receive_messages_delivery_proof( + &self, + proof : :: bridge_runtime_common :: messages :: source :: FromBridgedChainMessagesDeliveryProof < :: bp_millau :: MillauHash >, + relayers_state: ::bp_messages::UnrewardedRelayersState, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauMessages", + "receive_messages_delivery_proof", + ReceiveMessagesDeliveryProof { proof, relayers_state }, + [ + 216u8, 113u8, 64u8, 20u8, 16u8, 64u8, 144u8, 143u8, 105u8, 30u8, 192u8, + 89u8, 74u8, 34u8, 56u8, 151u8, 20u8, 234u8, 14u8, 211u8, 64u8, 103u8, + 218u8, 164u8, 69u8, 107u8, 6u8, 119u8, 13u8, 90u8, 150u8, 24u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::pallet_bridge_messages::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Message has been accepted and is waiting to be delivered."] + pub struct MessageAccepted { + pub lane_id: runtime_types::bp_messages::LaneId, + pub nonce: ::core::primitive::u64, + } + impl ::subxt::events::StaticEvent for MessageAccepted { + const PALLET: &'static str = "BridgeMillauMessages"; + const EVENT: &'static str = "MessageAccepted"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Messages have been received from the bridged chain."] + pub struct MessagesReceived( + pub ::std::vec::Vec>, + ); + impl ::subxt::events::StaticEvent for MessagesReceived { + const PALLET: &'static str = "BridgeMillauMessages"; + const EVENT: &'static str = "MessagesReceived"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Messages in the inclusive range have been delivered to the bridged chain."] + pub struct MessagesDelivered { + pub lane_id: runtime_types::bp_messages::LaneId, + pub messages: runtime_types::bp_messages::DeliveredMessages, + } + impl ::subxt::events::StaticEvent for MessagesDelivered { + const PALLET: &'static str = "BridgeMillauMessages"; + const EVENT: &'static str = "MessagesDelivered"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " Optional pallet owner."] + #[doc = ""] + #[doc = " Pallet owner has a right to halt all pallet operations and then resume it. If it is"] + #[doc = " `None`, then there are no direct ways to halt/resume pallet operations, but other"] + #[doc = " runtime methods may still be used to do that (i.e. democracy::referendum to update halt"] + #[doc = " flag directly or call the `halt_operations`)."] + pub fn pallet_owner( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::sp_core::crypto::AccountId32>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "PalletOwner", + vec![], + [ + 89u8, 42u8, 74u8, 119u8, 21u8, 164u8, 30u8, 115u8, 207u8, 126u8, 98u8, + 16u8, 162u8, 214u8, 67u8, 172u8, 178u8, 223u8, 139u8, 121u8, 174u8, + 89u8, 215u8, 75u8, 200u8, 161u8, 61u8, 195u8, 65u8, 222u8, 246u8, + 233u8, + ], + ) + } + #[doc = " The current operating mode of the pallet."] + #[doc = ""] + #[doc = " Depending on the mode either all, some, or no transactions will be allowed."] + pub fn pallet_operating_mode( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_messages::MessagesOperatingMode, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "PalletOperatingMode", + vec![], + [ + 215u8, 195u8, 85u8, 231u8, 158u8, 22u8, 160u8, 132u8, 69u8, 206u8, + 238u8, 14u8, 56u8, 100u8, 134u8, 41u8, 58u8, 120u8, 225u8, 164u8, + 173u8, 87u8, 22u8, 123u8, 102u8, 167u8, 68u8, 70u8, 184u8, 131u8, + 232u8, 65u8, + ], + ) + } + #[doc = " Map of lane id => inbound lane data."] + pub fn inbound_lanes( + &self, + _0: impl ::std::borrow::Borrow, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_messages::InboundLaneData<::sp_core::crypto::AccountId32>, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "InboundLanes", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 38u8, 58u8, 110u8, 130u8, 112u8, 76u8, 231u8, 76u8, 56u8, 241u8, 183u8, + 153u8, 112u8, 41u8, 248u8, 208u8, 217u8, 57u8, 102u8, 30u8, 107u8, + 98u8, 59u8, 78u8, 56u8, 119u8, 186u8, 183u8, 213u8, 72u8, 199u8, 90u8, + ], + ) + } + #[doc = " Map of lane id => inbound lane data."] + pub fn inbound_lanes_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_messages::InboundLaneData<::sp_core::crypto::AccountId32>, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "InboundLanes", + Vec::new(), + [ + 38u8, 58u8, 110u8, 130u8, 112u8, 76u8, 231u8, 76u8, 56u8, 241u8, 183u8, + 153u8, 112u8, 41u8, 248u8, 208u8, 217u8, 57u8, 102u8, 30u8, 107u8, + 98u8, 59u8, 78u8, 56u8, 119u8, 186u8, 183u8, 213u8, 72u8, 199u8, 90u8, + ], + ) + } + #[doc = " Map of lane id => outbound lane data."] + pub fn outbound_lanes( + &self, + _0: impl ::std::borrow::Borrow, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_messages::OutboundLaneData, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "OutboundLanes", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 67u8, 155u8, 173u8, 244u8, 80u8, 38u8, 26u8, 71u8, 51u8, 150u8, 86u8, + 146u8, 132u8, 122u8, 70u8, 122u8, 172u8, 246u8, 106u8, 232u8, 149u8, + 227u8, 240u8, 146u8, 51u8, 184u8, 30u8, 182u8, 200u8, 43u8, 190u8, + 38u8, + ], + ) + } + #[doc = " Map of lane id => outbound lane data."] + pub fn outbound_lanes_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_messages::OutboundLaneData, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "OutboundLanes", + Vec::new(), + [ + 67u8, 155u8, 173u8, 244u8, 80u8, 38u8, 26u8, 71u8, 51u8, 150u8, 86u8, + 146u8, 132u8, 122u8, 70u8, 122u8, 172u8, 246u8, 106u8, 232u8, 149u8, + 227u8, 240u8, 146u8, 51u8, 184u8, 30u8, 182u8, 200u8, 43u8, 190u8, + 38u8, + ], + ) + } + #[doc = " All queued outbound messages."] + pub fn outbound_messages( + &self, + _0: impl ::std::borrow::Borrow, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::sp_core::bounded::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + >, + ::subxt::storage::address::Yes, + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "OutboundMessages", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 44u8, 35u8, 2u8, 25u8, 91u8, 101u8, 152u8, 23u8, 48u8, 250u8, 178u8, + 15u8, 194u8, 118u8, 146u8, 1u8, 112u8, 83u8, 243u8, 166u8, 124u8, + 153u8, 48u8, 193u8, 43u8, 31u8, 33u8, 72u8, 228u8, 113u8, 86u8, 217u8, + ], + ) + } + #[doc = " All queued outbound messages."] + pub fn outbound_messages_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::sp_core::bounded::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + >, + (), + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "OutboundMessages", + Vec::new(), + [ + 44u8, 35u8, 2u8, 25u8, 91u8, 101u8, 152u8, 23u8, 48u8, 250u8, 178u8, + 15u8, 194u8, 118u8, 146u8, 1u8, 112u8, 83u8, 243u8, 166u8, 124u8, + 153u8, 48u8, 193u8, 43u8, 31u8, 33u8, 72u8, 228u8, 113u8, 86u8, 217u8, + ], + ) + } + } + } + pub mod constants { + use super::runtime_types; + pub struct ConstantsApi; + impl ConstantsApi { + #[doc = " Gets the chain id value from the instance."] + pub fn bridged_chain_id( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<[::core::primitive::u8; 4usize]>, + > { + ::subxt::constants::StaticConstantAddress::new( + "BridgeMillauMessages", + "BridgedChainId", + [ + 101u8, 157u8, 37u8, 163u8, 190u8, 134u8, 129u8, 212u8, 240u8, 135u8, + 174u8, 76u8, 220u8, 179u8, 252u8, 69u8, 65u8, 253u8, 69u8, 214u8, 61u8, + 249u8, 4u8, 38u8, 181u8, 237u8, 25u8, 131u8, 242u8, 20u8, 17u8, 152u8, + ], + ) + } + #[doc = " Maximal encoded size of the outbound payload."] + pub fn maximal_outbound_payload_size( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "BridgeMillauMessages", + "MaximalOutboundPayloadSize", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + } + } + } + pub mod runtime_types { + use super::runtime_types; + pub mod bp_header_chain { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct StoredHeaderData<_0, _1> { + pub number: _0, + pub state_root: _1, + } + } + pub mod bp_messages { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct DeliveredMessages { + pub begin: ::core::primitive::u64, + pub end: ::core::primitive::u64, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct InboundLaneData<_0> { + pub relayers: ::std::vec::Vec>, + pub last_confirmed_nonce: ::core::primitive::u64, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct LaneId(pub [::core::primitive::u8; 4usize]); + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct MessageKey { + pub lane_id: runtime_types::bp_messages::LaneId, + pub nonce: ::core::primitive::u64, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum MessagesOperatingMode { + #[codec(index = 0)] + Basic(runtime_types::bp_runtime::BasicOperatingMode), + #[codec(index = 1)] + RejectingOutboundMessages, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct OutboundLaneData { + pub oldest_unpruned_nonce: ::core::primitive::u64, + pub latest_received_nonce: ::core::primitive::u64, + pub latest_generated_nonce: ::core::primitive::u64, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum ReceivalResult<_0> { + #[codec(index = 0)] + Dispatched(runtime_types::bp_runtime::messages::MessageDispatchResult<_0>), + #[codec(index = 1)] + InvalidNonce, + #[codec(index = 2)] + TooManyUnrewardedRelayers, + #[codec(index = 3)] + TooManyUnconfirmedMessages, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ReceivedMessages<_0> { + pub lane: runtime_types::bp_messages::LaneId, + pub receive_results: ::std::vec::Vec<( + ::core::primitive::u64, + runtime_types::bp_messages::ReceivalResult<_0>, + )>, + pub skipped_for_not_enough_weight: ::std::vec::Vec<::core::primitive::u64>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct UnrewardedRelayer<_0> { + pub relayer: _0, + pub messages: runtime_types::bp_messages::DeliveredMessages, + } + } + pub mod bp_runtime { + use super::runtime_types; + pub mod messages { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MessageDispatchResult<_0> { + pub unspent_weight: ::sp_weights::Weight, + pub dispatch_level_result: _0, + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum BasicOperatingMode { + #[codec(index = 0)] + Normal, + #[codec(index = 1)] + Halted, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct HeaderId<_0, _1>(pub _1, pub _0); + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum OwnedBridgeModuleError { + #[codec(index = 0)] + Halted, + } + } + pub mod cumulus_pallet_dmp_queue { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Service a single overweight message."] + service_overweight { + index: ::core::primitive::u64, + weight_limit: ::sp_weights::Weight, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "The message index given is unknown."] + Unknown, + #[codec(index = 1)] + #[doc = "The amount of weight given is possibly not enough for executing the message."] + OverLimit, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "Downward message is invalid XCM."] + InvalidFormat { message_id: [::core::primitive::u8; 32usize] }, + #[codec(index = 1)] + #[doc = "Downward message is unsupported version of XCM."] + UnsupportedVersion { message_id: [::core::primitive::u8; 32usize] }, + #[codec(index = 2)] + #[doc = "Downward message executed with the given outcome."] + ExecutedDownward { + message_id: [::core::primitive::u8; 32usize], + outcome: runtime_types::xcm::v3::traits::Outcome, + }, + #[codec(index = 3)] + #[doc = "The weight limit for handling downward messages was reached."] + WeightExhausted { + message_id: [::core::primitive::u8; 32usize], + remaining_weight: ::sp_weights::Weight, + required_weight: ::sp_weights::Weight, + }, + #[codec(index = 4)] + #[doc = "Downward message is overweight and was placed in the overweight queue."] + OverweightEnqueued { + message_id: [::core::primitive::u8; 32usize], + overweight_index: ::core::primitive::u64, + required_weight: ::sp_weights::Weight, + }, + #[codec(index = 5)] + #[doc = "Downward message from the overweight queue was executed."] + OverweightServiced { + overweight_index: ::core::primitive::u64, + weight_used: ::sp_weights::Weight, + }, + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ConfigData { + pub max_individual: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct PageIndexData { + pub begin_used: ::core::primitive::u32, + pub end_used: ::core::primitive::u32, + pub overweight_count: ::core::primitive::u64, + } + } + pub mod cumulus_pallet_parachain_system { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + # [codec (index = 0)] # [doc = "Set the current validation data."] # [doc = ""] # [doc = "This should be invoked exactly once per block. It will panic at the finalization"] # [doc = "phase if the call was not invoked."] # [doc = ""] # [doc = "The dispatch origin for this call must be `Inherent`"] # [doc = ""] # [doc = "As a side effect, this function upgrades the current validation function"] # [doc = "if the appropriate time has come."] set_validation_data { data : runtime_types :: cumulus_primitives_parachain_inherent :: ParachainInherentData , } , # [codec (index = 1)] sudo_send_upward_message { message : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , # [codec (index = 2)] authorize_upgrade { code_hash : :: subxt :: utils :: H256 , } , # [codec (index = 3)] enact_authorized_upgrade { code : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "Attempt to upgrade validation function while existing upgrade pending"] + OverlappingUpgrades, + #[codec(index = 1)] + #[doc = "Polkadot currently prohibits this parachain from upgrading its validation function"] + ProhibitedByPolkadot, + #[codec(index = 2)] + #[doc = "The supplied validation function has compiled into a blob larger than Polkadot is"] + #[doc = "willing to run"] + TooBig, + #[codec(index = 3)] + #[doc = "The inherent which supplies the validation data did not run this block"] + ValidationDataNotAvailable, + #[codec(index = 4)] + #[doc = "The inherent which supplies the host configuration did not run this block"] + HostConfigurationNotAvailable, + #[codec(index = 5)] + #[doc = "No validation function upgrade is currently scheduled."] + NotScheduled, + #[codec(index = 6)] + #[doc = "No code upgrade has been authorized."] + NothingAuthorized, + #[codec(index = 7)] + #[doc = "The given code upgrade has not been authorized."] + Unauthorized, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "The validation function has been scheduled to apply."] + ValidationFunctionStored, + #[codec(index = 1)] + #[doc = "The validation function was applied as of the contained relay chain block number."] + ValidationFunctionApplied { relay_chain_block_num: ::core::primitive::u32 }, + #[codec(index = 2)] + #[doc = "The relay-chain aborted the upgrade process."] + ValidationFunctionDiscarded, + #[codec(index = 3)] + #[doc = "An upgrade has been authorized."] + UpgradeAuthorized { code_hash: ::subxt::utils::H256 }, + #[codec(index = 4)] + #[doc = "Some downward messages have been received and will be processed."] + DownwardMessagesReceived { count: ::core::primitive::u32 }, + #[codec(index = 5)] + #[doc = "Downward messages were processed using the given weight."] + DownwardMessagesProcessed { + weight_used: ::sp_weights::Weight, + dmq_head: ::subxt::utils::H256, + }, + #[codec(index = 6)] + #[doc = "An upward message was sent to the relay chain."] + UpwardMessageSent { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + }, + } + } + pub mod relay_state_snapshot { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MessagingStateSnapshot { + pub dmq_mqc_head: ::subxt::utils::H256, + pub relay_dispatch_queue_size: (::core::primitive::u32, ::core::primitive::u32), + pub ingress_channels: ::std::vec::Vec<( + runtime_types::polkadot_parachain::primitives::Id, + runtime_types::polkadot_primitives::v2::AbridgedHrmpChannel, + )>, + pub egress_channels: ::std::vec::Vec<( + runtime_types::polkadot_parachain::primitives::Id, + runtime_types::polkadot_primitives::v2::AbridgedHrmpChannel, + )>, + } + } + } + pub mod cumulus_pallet_xcm { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call {} + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error {} + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "Downward message is invalid XCM."] + #[doc = "\\[ id \\]"] + InvalidFormat([::core::primitive::u8; 32usize]), + #[codec(index = 1)] + #[doc = "Downward message is unsupported version of XCM."] + #[doc = "\\[ id \\]"] + UnsupportedVersion([::core::primitive::u8; 32usize]), + #[codec(index = 2)] + #[doc = "Downward message executed with the given outcome."] + #[doc = "\\[ id, outcome \\]"] + ExecutedDownward( + [::core::primitive::u8; 32usize], + runtime_types::xcm::v3::traits::Outcome, + ), + } + } + } + pub mod cumulus_pallet_xcmp_queue { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Services a single overweight XCM."] + #[doc = ""] + #[doc = "- `origin`: Must pass `ExecuteOverweightOrigin`."] + #[doc = "- `index`: The index of the overweight XCM to service"] + #[doc = "- `weight_limit`: The amount of weight that XCM execution may take."] + #[doc = ""] + #[doc = "Errors:"] + #[doc = "- `BadOverweightIndex`: XCM under `index` is not found in the `Overweight` storage map."] + #[doc = "- `BadXcm`: XCM under `index` cannot be properly decoded into a valid XCM format."] + #[doc = "- `WeightOverLimit`: XCM execution may use greater `weight_limit`."] + #[doc = ""] + #[doc = "Events:"] + #[doc = "- `OverweightServiced`: On success."] + service_overweight { + index: ::core::primitive::u64, + weight_limit: ::sp_weights::Weight, + }, + #[codec(index = 1)] + #[doc = "Suspends all XCM executions for the XCMP queue, regardless of the sender's origin."] + #[doc = ""] + #[doc = "- `origin`: Must pass `ControllerOrigin`."] + suspend_xcm_execution, + #[codec(index = 2)] + #[doc = "Resumes all XCM executions for the XCMP queue."] + #[doc = ""] + #[doc = "Note that this function doesn't change the status of the in/out bound channels."] + #[doc = ""] + #[doc = "- `origin`: Must pass `ControllerOrigin`."] + resume_xcm_execution, + #[codec(index = 3)] + #[doc = "Overwrites the number of pages of messages which must be in the queue for the other side to be told to"] + #[doc = "suspend their sending."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.suspend_value`"] + update_suspend_threshold { new: ::core::primitive::u32 }, + #[codec(index = 4)] + #[doc = "Overwrites the number of pages of messages which must be in the queue after which we drop any further"] + #[doc = "messages from the channel."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.drop_threshold`"] + update_drop_threshold { new: ::core::primitive::u32 }, + #[codec(index = 5)] + #[doc = "Overwrites the number of pages of messages which the queue must be reduced to before it signals that"] + #[doc = "message sending may recommence after it has been suspended."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.resume_threshold`"] + update_resume_threshold { new: ::core::primitive::u32 }, + #[codec(index = 6)] + #[doc = "Overwrites the amount of remaining weight under which we stop processing messages."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.threshold_weight`"] + update_threshold_weight { new: ::sp_weights::Weight }, + #[codec(index = 7)] + #[doc = "Overwrites the speed to which the available weight approaches the maximum weight."] + #[doc = "A lower number results in a faster progression. A value of 1 makes the entire weight available initially."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.weight_restrict_decay`."] + update_weight_restrict_decay { new: ::sp_weights::Weight }, + #[codec(index = 8)] + #[doc = "Overwrite the maximum amount of weight any individual message may consume."] + #[doc = "Messages above this weight go into the overweight queue and may only be serviced explicitly."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.xcmp_max_individual_weight`."] + update_xcmp_max_individual_weight { new: ::sp_weights::Weight }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "Failed to send XCM message."] + FailedToSend, + #[codec(index = 1)] + #[doc = "Bad XCM origin."] + BadXcmOrigin, + #[codec(index = 2)] + #[doc = "Bad XCM data."] + BadXcm, + #[codec(index = 3)] + #[doc = "Bad overweight index."] + BadOverweightIndex, + #[codec(index = 4)] + #[doc = "Provided weight is possibly not enough to execute the message."] + WeightOverLimit, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "Some XCM was executed ok."] + Success { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + weight: ::sp_weights::Weight, + }, + #[codec(index = 1)] + #[doc = "Some XCM failed."] + Fail { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + error: runtime_types::xcm::v3::traits::Error, + weight: ::sp_weights::Weight, + }, + #[codec(index = 2)] + #[doc = "Bad XCM version used."] + BadVersion { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + }, + #[codec(index = 3)] + #[doc = "Bad XCM format used."] + BadFormat { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + }, + #[codec(index = 4)] + #[doc = "An HRMP message was sent to a sibling parachain."] + XcmpMessageSent { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + }, + #[codec(index = 5)] + #[doc = "An XCM exceeded the individual message weight budget."] + OverweightEnqueued { + sender: runtime_types::polkadot_parachain::primitives::Id, + sent_at: ::core::primitive::u32, + index: ::core::primitive::u64, + required: ::sp_weights::Weight, + }, + #[codec(index = 6)] + #[doc = "An XCM from the overweight queue was executed with the given actual weight used."] + OverweightServiced { index: ::core::primitive::u64, used: ::sp_weights::Weight }, + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct InboundChannelDetails { + pub sender: runtime_types::polkadot_parachain::primitives::Id, + pub state: runtime_types::cumulus_pallet_xcmp_queue::InboundState, + pub message_metadata: ::std::vec::Vec<( + ::core::primitive::u32, + runtime_types::polkadot_parachain::primitives::XcmpMessageFormat, + )>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum InboundState { + #[codec(index = 0)] + Ok, + #[codec(index = 1)] + Suspended, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct OutboundChannelDetails { + pub recipient: runtime_types::polkadot_parachain::primitives::Id, + pub state: runtime_types::cumulus_pallet_xcmp_queue::OutboundState, + pub signals_exist: ::core::primitive::bool, + pub first_index: ::core::primitive::u16, + pub last_index: ::core::primitive::u16, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum OutboundState { + #[codec(index = 0)] + Ok, + #[codec(index = 1)] + Suspended, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct QueueConfigData { + pub suspend_threshold: ::core::primitive::u32, + pub drop_threshold: ::core::primitive::u32, + pub resume_threshold: ::core::primitive::u32, + pub threshold_weight: ::sp_weights::Weight, + pub weight_restrict_decay: ::sp_weights::Weight, + pub xcmp_max_individual_weight: ::sp_weights::Weight, + } + } + pub mod cumulus_primitives_parachain_inherent { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct MessageQueueChain(pub ::subxt::utils::H256); + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ParachainInherentData { + pub validation_data: + runtime_types::polkadot_primitives::v2::PersistedValidationData< + ::subxt::utils::H256, + ::core::primitive::u32, + >, + pub relay_chain_state: runtime_types::sp_trie::storage_proof::StorageProof, + pub downward_messages: ::std::vec::Vec< + runtime_types::polkadot_core_primitives::InboundDownwardMessage< + ::core::primitive::u32, + >, + >, + pub horizontal_messages: ::subxt::utils::KeyedVec< + runtime_types::polkadot_parachain::primitives::Id, + ::std::vec::Vec< + runtime_types::polkadot_core_primitives::InboundHrmpMessage< + ::core::primitive::u32, + >, + >, + >, + } + } + pub mod finality_grandpa { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Commit<_0, _1, _2, _3> { + pub target_hash: _0, + pub target_number: _1, + pub precommits: ::std::vec::Vec< + runtime_types::finality_grandpa::SignedPrecommit<_0, _1, _2, _3>, + >, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Precommit<_0, _1> { + pub target_hash: _0, + pub target_number: _1, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SignedPrecommit<_0, _1, _2, _3> { + pub precommit: runtime_types::finality_grandpa::Precommit<_0, _1>, + pub signature: _2, + pub id: _3, + } + } + pub mod frame_support { + use super::runtime_types; + pub mod dispatch { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum DispatchClass { + #[codec(index = 0)] + Normal, + #[codec(index = 1)] + Operational, + #[codec(index = 2)] + Mandatory, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct DispatchInfo { + pub weight: ::sp_weights::Weight, + pub class: runtime_types::frame_support::dispatch::DispatchClass, + pub pays_fee: runtime_types::frame_support::dispatch::Pays, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Pays { + #[codec(index = 0)] + Yes, + #[codec(index = 1)] + No, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct PerDispatchClass<_0> { + pub normal: _0, + pub operational: _0, + pub mandatory: _0, + } + } + pub mod traits { + use super::runtime_types; + pub mod tokens { + use super::runtime_types; + pub mod misc { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum BalanceStatus { + #[codec(index = 0)] + Free, + #[codec(index = 1)] + Reserved, + } + } + } + } + } + pub mod frame_system { + use super::runtime_types; + pub mod extensions { + use super::runtime_types; + pub mod check_genesis { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct CheckGenesis; + } + pub mod check_mortality { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct CheckMortality(pub ::sp_runtime::generic::Era); + } + pub mod check_non_zero_sender { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct CheckNonZeroSender; + } + pub mod check_nonce { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct CheckNonce(#[codec(compact)] pub ::core::primitive::u32); + } + pub mod check_spec_version { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct CheckSpecVersion; + } + pub mod check_tx_version { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct CheckTxVersion; + } + pub mod check_weight { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct CheckWeight; + } + } + pub mod limits { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct BlockLength { + pub max: runtime_types::frame_support::dispatch::PerDispatchClass< + ::core::primitive::u32, + >, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct BlockWeights { + pub base_block: ::sp_weights::Weight, + pub max_block: ::sp_weights::Weight, + pub per_class: runtime_types::frame_support::dispatch::PerDispatchClass< + runtime_types::frame_system::limits::WeightsPerClass, + >, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct WeightsPerClass { + pub base_extrinsic: ::sp_weights::Weight, + pub max_extrinsic: ::core::option::Option<::sp_weights::Weight>, + pub max_total: ::core::option::Option<::sp_weights::Weight>, + pub reserved: ::core::option::Option<::sp_weights::Weight>, + } + } + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Make some on-chain remark."] + #[doc = ""] + #[doc = "# "] + #[doc = "- `O(1)`"] + #[doc = "# "] + remark { remark: ::std::vec::Vec<::core::primitive::u8> }, + #[codec(index = 1)] + #[doc = "Set the number of pages in the WebAssembly environment's heap."] + set_heap_pages { pages: ::core::primitive::u64 }, + #[codec(index = 2)] + #[doc = "Set the new runtime code."] + #[doc = ""] + #[doc = "# "] + #[doc = "- `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code`"] + #[doc = "- 1 call to `can_set_code`: `O(S)` (calls `sp_io::misc::runtime_version` which is"] + #[doc = " expensive)."] + #[doc = "- 1 storage write (codec `O(C)`)."] + #[doc = "- 1 digest item."] + #[doc = "- 1 event."] + #[doc = "The weight of this function is dependent on the runtime, but generally this is very"] + #[doc = "expensive. We will treat this as a full block."] + #[doc = "# "] + set_code { code: ::std::vec::Vec<::core::primitive::u8> }, + #[codec(index = 3)] + #[doc = "Set the new runtime code without doing any checks of the given `code`."] + #[doc = ""] + #[doc = "# "] + #[doc = "- `O(C)` where `C` length of `code`"] + #[doc = "- 1 storage write (codec `O(C)`)."] + #[doc = "- 1 digest item."] + #[doc = "- 1 event."] + #[doc = "The weight of this function is dependent on the runtime. We will treat this as a full"] + #[doc = "block. # "] + set_code_without_checks { code: ::std::vec::Vec<::core::primitive::u8> }, + #[codec(index = 4)] + #[doc = "Set some items of storage."] + set_storage { + items: ::std::vec::Vec<( + ::std::vec::Vec<::core::primitive::u8>, + ::std::vec::Vec<::core::primitive::u8>, + )>, + }, + #[codec(index = 5)] + #[doc = "Kill some items from storage."] + kill_storage { keys: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>> }, + #[codec(index = 6)] + #[doc = "Kill all storage items with a key that starts with the given prefix."] + #[doc = ""] + #[doc = "**NOTE:** We rely on the Root origin to provide us the number of subkeys under"] + #[doc = "the prefix we are removing to accurately calculate the weight of this function."] + kill_prefix { + prefix: ::std::vec::Vec<::core::primitive::u8>, + subkeys: ::core::primitive::u32, + }, + #[codec(index = 7)] + #[doc = "Make some on-chain remark and emit event."] + remark_with_event { remark: ::std::vec::Vec<::core::primitive::u8> }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Error for the System pallet"] + pub enum Error { + #[codec(index = 0)] + #[doc = "The name of specification does not match between the current runtime"] + #[doc = "and the new runtime."] + InvalidSpecName, + #[codec(index = 1)] + #[doc = "The specification version is not allowed to decrease between the current runtime"] + #[doc = "and the new runtime."] + SpecVersionNeedsToIncrease, + #[codec(index = 2)] + #[doc = "Failed to extract the runtime version from the new runtime."] + #[doc = ""] + #[doc = "Either calling `Core_version` or decoding `RuntimeVersion` failed."] + FailedToExtractRuntimeVersion, + #[codec(index = 3)] + #[doc = "Suicide called when the account has non-default composite data."] + NonDefaultComposite, + #[codec(index = 4)] + #[doc = "There is a non-zero reference count preventing the account from being purged."] + NonZeroRefCount, + #[codec(index = 5)] + #[doc = "The origin filter prevent the call to be dispatched."] + CallFiltered, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Event for the System pallet."] + pub enum Event { + #[codec(index = 0)] + #[doc = "An extrinsic completed successfully."] + ExtrinsicSuccess { + dispatch_info: runtime_types::frame_support::dispatch::DispatchInfo, + }, + #[codec(index = 1)] + #[doc = "An extrinsic failed."] + ExtrinsicFailed { + dispatch_error: runtime_types::sp_runtime::DispatchError, + dispatch_info: runtime_types::frame_support::dispatch::DispatchInfo, + }, + #[codec(index = 2)] + #[doc = "`:code` was updated."] + CodeUpdated, + #[codec(index = 3)] + #[doc = "A new account was created."] + NewAccount { account: ::sp_core::crypto::AccountId32 }, + #[codec(index = 4)] + #[doc = "An account was reaped."] + KilledAccount { account: ::sp_core::crypto::AccountId32 }, + #[codec(index = 5)] + #[doc = "On on-chain remark happened."] + Remarked { sender: ::sp_core::crypto::AccountId32, hash: ::subxt::utils::H256 }, + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct AccountInfo<_0, _1> { + pub nonce: _0, + pub consumers: _0, + pub providers: _0, + pub sufficients: _0, + pub data: _1, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct EventRecord<_0, _1> { + pub phase: runtime_types::frame_system::Phase, + pub event: _0, + pub topics: ::std::vec::Vec<_1>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct LastRuntimeUpgradeInfo { + #[codec(compact)] + pub spec_version: ::core::primitive::u32, + pub spec_name: ::std::string::String, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum Phase { + #[codec(index = 0)] + ApplyExtrinsic(::core::primitive::u32), + #[codec(index = 1)] + Finalization, + #[codec(index = 2)] + Initialization, + } + } + pub mod pallet_balances { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Transfer some liquid free balance to another account."] + #[doc = ""] + #[doc = "`transfer` will set the `FreeBalance` of the sender and receiver."] + #[doc = "If the sender's account is below the existential deposit as a result"] + #[doc = "of the transfer, the account will be reaped."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be `Signed` by the transactor."] + #[doc = ""] + #[doc = "# "] + #[doc = "- Dependent on arguments but not critical, given proper implementations for input config"] + #[doc = " types. See related functions below."] + #[doc = "- It contains a limited number of reads and writes internally and no complex"] + #[doc = " computation."] + #[doc = ""] + #[doc = "Related functions:"] + #[doc = ""] + #[doc = " - `ensure_can_withdraw` is always called internally but has a bounded complexity."] + #[doc = " - Transferring balances to accounts that did not exist before will cause"] + #[doc = " `T::OnNewAccount::on_new_account` to be called."] + #[doc = " - Removing enough funds from an account will trigger `T::DustRemoval::on_unbalanced`."] + #[doc = " - `transfer_keep_alive` works the same way as `transfer`, but has an additional check"] + #[doc = " that the transfer will not kill the origin account."] + #[doc = "---------------------------------"] + #[doc = "- Origin account is already in memory, so no DB operations for them."] + #[doc = "# "] + transfer { + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + value: ::core::primitive::u128, + }, + #[codec(index = 1)] + #[doc = "Set the balances of a given account."] + #[doc = ""] + #[doc = "This will alter `FreeBalance` and `ReservedBalance` in storage. it will"] + #[doc = "also alter the total issuance of the system (`TotalIssuance`) appropriately."] + #[doc = "If the new free or reserved balance is below the existential deposit,"] + #[doc = "it will reset the account nonce (`frame_system::AccountNonce`)."] + #[doc = ""] + #[doc = "The dispatch origin for this call is `root`."] + set_balance { + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + new_free: ::core::primitive::u128, + #[codec(compact)] + new_reserved: ::core::primitive::u128, + }, + #[codec(index = 2)] + #[doc = "Exactly as `transfer`, except the origin must be root and the source account may be"] + #[doc = "specified."] + #[doc = "# "] + #[doc = "- Same as transfer, but additional read and write because the source account is not"] + #[doc = " assumed to be in the overlay."] + #[doc = "# "] + force_transfer { + source: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + value: ::core::primitive::u128, + }, + #[codec(index = 3)] + #[doc = "Same as the [`transfer`] call, but with a check that the transfer will not kill the"] + #[doc = "origin account."] + #[doc = ""] + #[doc = "99% of the time you want [`transfer`] instead."] + #[doc = ""] + #[doc = "[`transfer`]: struct.Pallet.html#method.transfer"] + transfer_keep_alive { + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + value: ::core::primitive::u128, + }, + #[codec(index = 4)] + #[doc = "Transfer the entire transferable balance from the caller account."] + #[doc = ""] + #[doc = "NOTE: This function only attempts to transfer _transferable_ balances. This means that"] + #[doc = "any locked, reserved, or existential deposits (when `keep_alive` is `true`), will not be"] + #[doc = "transferred by this function. To ensure that this function results in a killed account,"] + #[doc = "you might need to prepare the account by removing any reference counters, storage"] + #[doc = "deposits, etc..."] + #[doc = ""] + #[doc = "The dispatch origin of this call must be Signed."] + #[doc = ""] + #[doc = "- `dest`: The recipient of the transfer."] + #[doc = "- `keep_alive`: A boolean to determine if the `transfer_all` operation should send all"] + #[doc = " of the funds the account has, causing the sender account to be killed (false), or"] + #[doc = " transfer everything except at least the existential deposit, which will guarantee to"] + #[doc = " keep the sender account alive (true). # "] + #[doc = "- O(1). Just like transfer, but reading the user's transferable balance first."] + #[doc = " #"] + transfer_all { + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + keep_alive: ::core::primitive::bool, + }, + #[codec(index = 5)] + #[doc = "Unreserve some balance from a user by force."] + #[doc = ""] + #[doc = "Can only be called by ROOT."] + force_unreserve { + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + amount: ::core::primitive::u128, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "Vesting balance too high to send value"] + VestingBalance, + #[codec(index = 1)] + #[doc = "Account liquidity restrictions prevent withdrawal"] + LiquidityRestrictions, + #[codec(index = 2)] + #[doc = "Balance too low to send value."] + InsufficientBalance, + #[codec(index = 3)] + #[doc = "Value too low to create account due to existential deposit"] + ExistentialDeposit, + #[codec(index = 4)] + #[doc = "Transfer/payment would kill account"] + KeepAlive, + #[codec(index = 5)] + #[doc = "A vesting schedule already exists for this account"] + ExistingVestingSchedule, + #[codec(index = 6)] + #[doc = "Beneficiary account must pre-exist"] + DeadAccount, + #[codec(index = 7)] + #[doc = "Number of named reserves exceed MaxReserves"] + TooManyReserves, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "An account was created with some free balance."] + Endowed { + account: ::sp_core::crypto::AccountId32, + free_balance: ::core::primitive::u128, + }, + #[codec(index = 1)] + #[doc = "An account was removed whose balance was non-zero but below ExistentialDeposit,"] + #[doc = "resulting in an outright loss."] + DustLost { + account: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 2)] + #[doc = "Transfer succeeded."] + Transfer { + from: ::sp_core::crypto::AccountId32, + to: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 3)] + #[doc = "A balance was set by root."] + BalanceSet { + who: ::sp_core::crypto::AccountId32, + free: ::core::primitive::u128, + reserved: ::core::primitive::u128, + }, + #[codec(index = 4)] + #[doc = "Some balance was reserved (moved from free to reserved)."] + Reserved { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 5)] + #[doc = "Some balance was unreserved (moved from reserved to free)."] + Unreserved { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 6)] + #[doc = "Some balance was moved from the reserve of the first account to the second account."] + #[doc = "Final argument indicates the destination balance type."] + ReserveRepatriated { + from: ::sp_core::crypto::AccountId32, + to: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + destination_status: + runtime_types::frame_support::traits::tokens::misc::BalanceStatus, + }, + #[codec(index = 7)] + #[doc = "Some amount was deposited (e.g. for transaction fees)."] + Deposit { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, + #[codec(index = 8)] + #[doc = "Some amount was withdrawn from the account (e.g. for transaction fees)."] + Withdraw { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 9)] + #[doc = "Some amount was removed from the account (e.g. for misbehavior)."] + Slashed { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct AccountData<_0> { + pub free: _0, + pub reserved: _0, + pub misc_frozen: _0, + pub fee_frozen: _0, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct BalanceLock<_0> { + pub id: [::core::primitive::u8; 8usize], + pub amount: _0, + pub reasons: runtime_types::pallet_balances::Reasons, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum Reasons { + #[codec(index = 0)] + Fee, + #[codec(index = 1)] + Misc, + #[codec(index = 2)] + All, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ReserveData<_0, _1> { + pub id: _0, + pub amount: _1, + } + } + pub mod pallet_bridge_grandpa { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Verify a target header is finalized according to the given finality proof."] + #[doc = ""] + #[doc = "It will use the underlying storage pallet to fetch information about the current"] + #[doc = "authorities and best finalized header in order to verify that the header is finalized."] + #[doc = ""] + #[doc = "If successful in verification, it will write the target header to the underlying storage"] + #[doc = "pallet."] + submit_finality_proof { + finality_target: ::std::boxed::Box< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + justification: ::bp_header_chain::justification::GrandpaJustification< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + }, + #[codec(index = 1)] + #[doc = "Bootstrap the bridge pallet with an initial header and authority set from which to sync."] + #[doc = ""] + #[doc = "The initial configuration provided does not need to be the genesis header of the bridged"] + #[doc = "chain, it can be any arbitrary header. You can also provide the next scheduled set"] + #[doc = "change if it is already know."] + #[doc = ""] + #[doc = "This function is only allowed to be called from a trusted origin and writes to storage"] + #[doc = "with practically no checks in terms of the validity of the data. It is important that"] + #[doc = "you ensure that valid data is being passed in."] + initialize { + init_data: ::bp_header_chain::InitializationData< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + }, + #[codec(index = 2)] + #[doc = "Change `PalletOwner`."] + #[doc = ""] + #[doc = "May only be called either by root, or by `PalletOwner`."] + set_owner { new_owner: ::core::option::Option<::sp_core::crypto::AccountId32> }, + #[codec(index = 3)] + #[doc = "Halt or resume all pallet operations."] + #[doc = ""] + #[doc = "May only be called either by root, or by `PalletOwner`."] + set_operating_mode { + operating_mode: runtime_types::bp_runtime::BasicOperatingMode, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "The given justification is invalid for the given header."] + InvalidJustification, + #[codec(index = 1)] + #[doc = "The authority set from the underlying header chain is invalid."] + InvalidAuthoritySet, + #[codec(index = 2)] + #[doc = "There are too many requests for the current window to handle."] + TooManyRequests, + #[codec(index = 3)] + #[doc = "The header being imported is older than the best finalized header known to the pallet."] + OldHeader, + #[codec(index = 4)] + #[doc = "The scheduled authority set change found in the header is unsupported by the pallet."] + #[doc = ""] + #[doc = "This is the case for non-standard (e.g forced) authority set changes."] + UnsupportedScheduledChange, + #[codec(index = 5)] + #[doc = "The pallet is not yet initialized."] + NotInitialized, + #[codec(index = 6)] + #[doc = "The pallet has already been initialized."] + AlreadyInitialized, + #[codec(index = 7)] + #[doc = "Too many authorities in the set."] + TooManyAuthoritiesInSet, + #[codec(index = 8)] + #[doc = "Error generated by the `OwnedBridgeModule` trait."] + BridgeModule(runtime_types::bp_runtime::OwnedBridgeModuleError), + } + } + pub mod storage_types { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct StoredAuthoritySet { + pub authorities: runtime_types::sp_core::bounded::bounded_vec::BoundedVec<( + runtime_types::sp_finality_grandpa::app::Public, + ::core::primitive::u64, + )>, + pub set_id: ::core::primitive::u64, + } + } + } + pub mod pallet_bridge_messages { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + # [codec (index = 0)] # [doc = "Change `PalletOwner`."] # [doc = ""] # [doc = "May only be called either by root, or by `PalletOwner`."] set_owner { new_owner : :: core :: option :: Option < :: sp_core :: crypto :: AccountId32 > , } , # [codec (index = 1)] # [doc = "Halt or resume all/some pallet operations."] # [doc = ""] # [doc = "May only be called either by root, or by `PalletOwner`."] set_operating_mode { operating_mode : runtime_types :: bp_messages :: MessagesOperatingMode , } , # [codec (index = 2)] # [doc = "Receive messages proof from bridged chain."] # [doc = ""] # [doc = "The weight of the call assumes that the transaction always brings outbound lane"] # [doc = "state update. Because of that, the submitter (relayer) has no benefit of not including"] # [doc = "this data in the transaction, so reward confirmations lags should be minimal."] receive_messages_proof { relayer_id_at_bridged_chain : :: sp_core :: crypto :: AccountId32 , proof : :: bridge_runtime_common :: messages :: target :: FromBridgedChainMessagesProof < :: bp_millau :: MillauHash > , messages_count : :: core :: primitive :: u32 , dispatch_weight : :: sp_weights :: Weight , } , # [codec (index = 3)] # [doc = "Receive messages delivery proof from bridged chain."] receive_messages_delivery_proof { proof : :: bridge_runtime_common :: messages :: source :: FromBridgedChainMessagesDeliveryProof < :: bp_millau :: MillauHash > , relayers_state : :: bp_messages :: UnrewardedRelayersState , } , } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "Pallet is not in Normal operating mode."] + NotOperatingNormally, + #[codec(index = 1)] + #[doc = "The outbound lane is inactive."] + InactiveOutboundLane, + #[codec(index = 2)] + #[doc = "The message is too large to be sent over the bridge."] + MessageIsTooLarge, + #[codec(index = 3)] + #[doc = "Message has been treated as invalid by chain verifier."] + MessageRejectedByChainVerifier, + #[codec(index = 4)] + #[doc = "Message has been treated as invalid by lane verifier."] + MessageRejectedByLaneVerifier, + #[codec(index = 5)] + #[doc = "Submitter has failed to pay fee for delivering and dispatching messages."] + FailedToWithdrawMessageFee, + #[codec(index = 6)] + #[doc = "The transaction brings too many messages."] + TooManyMessagesInTheProof, + #[codec(index = 7)] + #[doc = "Invalid messages has been submitted."] + InvalidMessagesProof, + #[codec(index = 8)] + #[doc = "Invalid messages delivery proof has been submitted."] + InvalidMessagesDeliveryProof, + #[codec(index = 9)] + #[doc = "The bridged chain has invalid `UnrewardedRelayers` in its storage (fatal for the lane)."] + InvalidUnrewardedRelayers, + #[codec(index = 10)] + #[doc = "The relayer has declared invalid unrewarded relayers state in the"] + #[doc = "`receive_messages_delivery_proof` call."] + InvalidUnrewardedRelayersState, + #[codec(index = 11)] + #[doc = "The message someone is trying to work with (i.e. increase fee) is already-delivered."] + MessageIsAlreadyDelivered, + #[codec(index = 12)] + #[doc = "The message someone is trying to work with (i.e. increase fee) is not yet sent."] + MessageIsNotYetSent, + #[codec(index = 13)] + #[doc = "The number of actually confirmed messages is going to be larger than the number of"] + #[doc = "messages in the proof. This may mean that this or bridged chain storage is corrupted."] + TryingToConfirmMoreMessagesThanExpected, + #[codec(index = 14)] + #[doc = "Error generated by the `OwnedBridgeModule` trait."] + BridgeModule(runtime_types::bp_runtime::OwnedBridgeModuleError), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "Message has been accepted and is waiting to be delivered."] + MessageAccepted { + lane_id: runtime_types::bp_messages::LaneId, + nonce: ::core::primitive::u64, + }, + #[codec(index = 1)] + #[doc = "Messages have been received from the bridged chain."] + MessagesReceived( + ::std::vec::Vec>, + ), + #[codec(index = 2)] + #[doc = "Messages in the inclusive range have been delivered to the bridged chain."] + MessagesDelivered { + lane_id: runtime_types::bp_messages::LaneId, + messages: runtime_types::bp_messages::DeliveredMessages, + }, + } + } + } + pub mod pallet_bridge_relayers { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Claim accumulated rewards."] + claim_rewards { lane_id: runtime_types::bp_messages::LaneId }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "No reward can be claimed by given relayer."] + NoRewardForRelayer, + #[codec(index = 1)] + #[doc = "Reward payment procedure has failed."] + FailedToPayReward, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "Reward has been paid to the relayer."] + RewardPaid { + relayer: ::sp_core::crypto::AccountId32, + lane_id: runtime_types::bp_messages::LaneId, + reward: ::core::primitive::u128, + }, + } + } + } + pub mod pallet_sudo { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Authenticates the sudo key and dispatches a function call with `Root` origin."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "# "] + #[doc = "- O(1)."] + #[doc = "- Limited storage reads."] + #[doc = "- One DB write (event)."] + #[doc = "- Weight of derivative `call` execution + 10,000."] + #[doc = "# "] + sudo { + call: + ::std::boxed::Box, + }, + #[codec(index = 1)] + #[doc = "Authenticates the sudo key and dispatches a function call with `Root` origin."] + #[doc = "This function does not check the weight of the call, and instead allows the"] + #[doc = "Sudo user to specify the weight of the call."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "# "] + #[doc = "- O(1)."] + #[doc = "- The weight of this call is defined by the caller."] + #[doc = "# "] + sudo_unchecked_weight { + call: + ::std::boxed::Box, + weight: ::sp_weights::Weight, + }, + #[codec(index = 2)] + #[doc = "Authenticates the current sudo key and sets the given AccountId (`new`) as the new sudo"] + #[doc = "key."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "# "] + #[doc = "- O(1)."] + #[doc = "- Limited storage reads."] + #[doc = "- One DB change."] + #[doc = "# "] + set_key { + new: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + }, + #[codec(index = 3)] + #[doc = "Authenticates the sudo key and dispatches a function call with `Signed` origin from"] + #[doc = "a given account."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "# "] + #[doc = "- O(1)."] + #[doc = "- Limited storage reads."] + #[doc = "- One DB write (event)."] + #[doc = "- Weight of derivative `call` execution + 10,000."] + #[doc = "# "] + sudo_as { + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + call: + ::std::boxed::Box, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Error for the Sudo pallet"] + pub enum Error { + #[codec(index = 0)] + #[doc = "Sender must be the Sudo account"] + RequireSudo, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "A sudo just took place. \\[result\\]"] + Sudid { + sudo_result: + ::core::result::Result<(), runtime_types::sp_runtime::DispatchError>, + }, + #[codec(index = 1)] + #[doc = "The \\[sudoer\\] just switched identity; the old key is supplied if one existed."] + KeyChanged { + old_sudoer: ::core::option::Option<::sp_core::crypto::AccountId32>, + }, + #[codec(index = 2)] + #[doc = "A sudo just took place. \\[result\\]"] + SudoAsDone { + sudo_result: + ::core::result::Result<(), runtime_types::sp_runtime::DispatchError>, + }, + } + } + } + pub mod pallet_timestamp { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Set the current time."] + #[doc = ""] + #[doc = "This call should be invoked exactly once per block. It will panic at the finalization"] + #[doc = "phase, if this call hasn't been invoked by that time."] + #[doc = ""] + #[doc = "The timestamp should be greater than the previous one by the amount specified by"] + #[doc = "`MinimumPeriod`."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be `Inherent`."] + #[doc = ""] + #[doc = "# "] + #[doc = "- `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`)"] + #[doc = "- 1 storage read and 1 storage mutation (codec `O(1)`). (because of `DidUpdate::take` in"] + #[doc = " `on_finalize`)"] + #[doc = "- 1 event handler `on_timestamp_set`. Must be `O(1)`."] + #[doc = "# "] + set { + #[codec(compact)] + now: ::core::primitive::u64, + }, + } + } + } + pub mod pallet_transaction_payment { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "A transaction fee `actual_fee`, of which `tip` was added to the minimum inclusion fee,"] + #[doc = "has been paid by `who`."] + TransactionFeePaid { + who: ::sp_core::crypto::AccountId32, + actual_fee: ::core::primitive::u128, + tip: ::core::primitive::u128, + }, + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ChargeTransactionPayment(#[codec(compact)] pub ::core::primitive::u128); + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum Releases { + #[codec(index = 0)] + V1Ancient, + #[codec(index = 1)] + V2, + } + } + pub mod pallet_xcm { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + send { + dest: ::std::boxed::Box, + message: ::std::boxed::Box, + }, + #[codec(index = 1)] + #[doc = "Teleport some assets from the local chain to some destination chain."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,"] + #[doc = "with all fees taken as needed from the asset."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the"] + #[doc = " `dest` side. May not be empty."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + teleport_assets { + dest: ::std::boxed::Box, + beneficiary: ::std::boxed::Box, + assets: ::std::boxed::Box, + fee_asset_item: ::core::primitive::u32, + }, + #[codec(index = 2)] + #[doc = "Transfer some assets from the local chain to the sovereign account of a destination"] + #[doc = "chain and forward a notification XCM."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,"] + #[doc = "with all fees taken as needed from the asset."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the"] + #[doc = " `dest` side."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + reserve_transfer_assets { + dest: ::std::boxed::Box, + beneficiary: ::std::boxed::Box, + assets: ::std::boxed::Box, + fee_asset_item: ::core::primitive::u32, + }, + #[codec(index = 3)] + #[doc = "Execute an XCM message from a local, signed, origin."] + #[doc = ""] + #[doc = "An event is deposited indicating whether `msg` could be executed completely or only"] + #[doc = "partially."] + #[doc = ""] + #[doc = "No more than `max_weight` will be used in its attempted execution. If this is less than the"] + #[doc = "maximum amount of weight that the message could take to be executed, then no execution"] + #[doc = "attempt will be made."] + #[doc = ""] + #[doc = "NOTE: A successful return to this does *not* imply that the `msg` was executed successfully"] + #[doc = "to completion; only that *some* of it was executed."] + execute { + message: ::std::boxed::Box, + max_weight: ::sp_weights::Weight, + }, + #[codec(index = 4)] + #[doc = "Extoll that a particular destination can be communicated with through a particular"] + #[doc = "version of XCM."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `location`: The destination that is being described."] + #[doc = "- `xcm_version`: The latest version of XCM that `location` supports."] + force_xcm_version { + location: + ::std::boxed::Box, + xcm_version: ::core::primitive::u32, + }, + #[codec(index = 5)] + #[doc = "Set a safe XCM version (the version that XCM should be encoded with if the most recent"] + #[doc = "version a destination can accept is unknown)."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `maybe_xcm_version`: The default XCM encoding version, or `None` to disable."] + force_default_xcm_version { + maybe_xcm_version: ::core::option::Option<::core::primitive::u32>, + }, + #[codec(index = 6)] + #[doc = "Ask a location to notify us regarding their XCM version and any changes to it."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `location`: The location to which we should subscribe for XCM version notifications."] + force_subscribe_version_notify { + location: ::std::boxed::Box, + }, + #[codec(index = 7)] + #[doc = "Require that a particular destination should no longer notify us regarding any XCM"] + #[doc = "version changes."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `location`: The location to which we are currently subscribed for XCM version"] + #[doc = " notifications which we no longer desire."] + force_unsubscribe_version_notify { + location: ::std::boxed::Box, + }, + #[codec(index = 8)] + #[doc = "Transfer some assets from the local chain to the sovereign account of a destination"] + #[doc = "chain and forward a notification XCM."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight"] + #[doc = "is needed than `weight_limit`, then the operation will fail and the assets send may be"] + #[doc = "at risk."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the"] + #[doc = " `dest` side."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + #[doc = "- `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase."] + limited_reserve_transfer_assets { + dest: ::std::boxed::Box, + beneficiary: ::std::boxed::Box, + assets: ::std::boxed::Box, + fee_asset_item: ::core::primitive::u32, + weight_limit: runtime_types::xcm::v3::WeightLimit, + }, + #[codec(index = 9)] + #[doc = "Teleport some assets from the local chain to some destination chain."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight"] + #[doc = "is needed than `weight_limit`, then the operation will fail and the assets send may be"] + #[doc = "at risk."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the"] + #[doc = " `dest` side. May not be empty."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + #[doc = "- `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase."] + limited_teleport_assets { + dest: ::std::boxed::Box, + beneficiary: ::std::boxed::Box, + assets: ::std::boxed::Box, + fee_asset_item: ::core::primitive::u32, + weight_limit: runtime_types::xcm::v3::WeightLimit, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "The desired destination was unreachable, generally because there is a no way of routing"] + #[doc = "to it."] + Unreachable, + #[codec(index = 1)] + #[doc = "There was some other issue (i.e. not to do with routing) in sending the message. Perhaps"] + #[doc = "a lack of space for buffering the message."] + SendFailure, + #[codec(index = 2)] + #[doc = "The message execution fails the filter."] + Filtered, + #[codec(index = 3)] + #[doc = "The message's weight could not be determined."] + UnweighableMessage, + #[codec(index = 4)] + #[doc = "The destination `MultiLocation` provided cannot be inverted."] + DestinationNotInvertible, + #[codec(index = 5)] + #[doc = "The assets to be sent are empty."] + Empty, + #[codec(index = 6)] + #[doc = "Could not re-anchor the assets to declare the fees for the destination chain."] + CannotReanchor, + #[codec(index = 7)] + #[doc = "Too many assets have been attempted for transfer."] + TooManyAssets, + #[codec(index = 8)] + #[doc = "Origin is invalid for sending."] + InvalidOrigin, + #[codec(index = 9)] + #[doc = "The version of the `Versioned` value used is not able to be interpreted."] + BadVersion, + #[codec(index = 10)] + #[doc = "The given location could not be used (e.g. because it cannot be expressed in the"] + #[doc = "desired version of XCM)."] + BadLocation, + #[codec(index = 11)] + #[doc = "The referenced subscription could not be found."] + NoSubscription, + #[codec(index = 12)] + #[doc = "The location is invalid since it already has a subscription from us."] + AlreadySubscribed, + #[codec(index = 13)] + #[doc = "Invalid asset for the operation."] + InvalidAsset, + #[codec(index = 14)] + #[doc = "The owner does not own (all) of the asset that they wish to do the operation on."] + LowBalance, + #[codec(index = 15)] + #[doc = "The asset owner has too many locks on the asset."] + TooManyLocks, + #[codec(index = 16)] + #[doc = "The given account is not an identifiable sovereign account for any location."] + AccountNotSovereign, + #[codec(index = 17)] + #[doc = "The operation required fees to be paid which the initiator could not meet."] + FeesNotMet, + #[codec(index = 18)] + #[doc = "A remote lock with the corresponding data could not be found."] + LockNotFound, + #[codec(index = 19)] + #[doc = "The unlock operation cannot succeed because there are still users of the lock."] + InUse, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "Execution of an XCM message was attempted."] + #[doc = ""] + #[doc = "\\[ outcome \\]"] + Attempted(runtime_types::xcm::v3::traits::Outcome), + #[codec(index = 1)] + #[doc = "A XCM message was sent."] + #[doc = ""] + #[doc = "\\[ origin, destination, message \\]"] + Sent( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::Xcm, + ), + #[codec(index = 2)] + #[doc = "Query response received which does not match a registered query. This may be because a"] + #[doc = "matching query was never registered, it may be because it is a duplicate response, or"] + #[doc = "because the query timed out."] + #[doc = ""] + #[doc = "\\[ origin location, id \\]"] + UnexpectedResponse( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + ), + #[codec(index = 3)] + #[doc = "Query response has been received and is ready for taking with `take_response`. There is"] + #[doc = "no registered notification call."] + #[doc = ""] + #[doc = "\\[ id, response \\]"] + ResponseReady(::core::primitive::u64, runtime_types::xcm::v3::Response), + #[codec(index = 4)] + #[doc = "Query response has been received and query is removed. The registered notification has"] + #[doc = "been dispatched and executed successfully."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index \\]"] + Notified(::core::primitive::u64, ::core::primitive::u8, ::core::primitive::u8), + #[codec(index = 5)] + #[doc = "Query response has been received and query is removed. The registered notification could"] + #[doc = "not be dispatched because the dispatch weight is greater than the maximum weight"] + #[doc = "originally budgeted by this runtime for the query result."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index, actual weight, max budgeted weight \\]"] + NotifyOverweight( + ::core::primitive::u64, + ::core::primitive::u8, + ::core::primitive::u8, + ::sp_weights::Weight, + ::sp_weights::Weight, + ), + #[codec(index = 6)] + #[doc = "Query response has been received and query is removed. There was a general error with"] + #[doc = "dispatching the notification call."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index \\]"] + NotifyDispatchError( + ::core::primitive::u64, + ::core::primitive::u8, + ::core::primitive::u8, + ), + #[codec(index = 7)] + #[doc = "Query response has been received and query is removed. The dispatch was unable to be"] + #[doc = "decoded into a `Call`; this might be due to dispatch function having a signature which"] + #[doc = "is not `(origin, QueryId, Response)`."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index \\]"] + NotifyDecodeFailed( + ::core::primitive::u64, + ::core::primitive::u8, + ::core::primitive::u8, + ), + #[codec(index = 8)] + #[doc = "Expected query response has been received but the origin location of the response does"] + #[doc = "not match that expected. The query remains registered for a later, valid, response to"] + #[doc = "be received and acted upon."] + #[doc = ""] + #[doc = "\\[ origin location, id, expected location \\]"] + InvalidResponder( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + ), + #[codec(index = 9)] + #[doc = "Expected query response has been received but the expected origin location placed in"] + #[doc = "storage by this runtime previously cannot be decoded. The query remains registered."] + #[doc = ""] + #[doc = "This is unexpected (since a location placed in storage in a previously executing"] + #[doc = "runtime should be readable prior to query timeout) and dangerous since the possibly"] + #[doc = "valid response will be dropped. Manual governance intervention is probably going to be"] + #[doc = "needed."] + #[doc = ""] + #[doc = "\\[ origin location, id \\]"] + InvalidResponderVersion( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + ), + #[codec(index = 10)] + #[doc = "Received query response has been read and removed."] + #[doc = ""] + #[doc = "\\[ id \\]"] + ResponseTaken(::core::primitive::u64), + #[codec(index = 11)] + #[doc = "Some assets have been placed in an asset trap."] + #[doc = ""] + #[doc = "\\[ hash, origin, assets \\]"] + AssetsTrapped( + ::subxt::utils::H256, + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::VersionedMultiAssets, + ), + #[codec(index = 12)] + #[doc = "An XCM version change notification message has been attempted to be sent."] + #[doc = ""] + #[doc = "The cost of sending it (borne by the chain) is included."] + #[doc = ""] + #[doc = "\\[ destination, result, cost \\]"] + VersionChangeNotified( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u32, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 13)] + #[doc = "The supported version of a location has been changed. This might be through an"] + #[doc = "automatic notification or a manual intervention."] + #[doc = ""] + #[doc = "\\[ location, XCM version \\]"] + SupportedVersionChanged( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u32, + ), + #[codec(index = 14)] + #[doc = "A given location which had a version change subscription was dropped owing to an error"] + #[doc = "sending the notification to it."] + #[doc = ""] + #[doc = "\\[ location, query ID, error \\]"] + NotifyTargetSendFail( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + runtime_types::xcm::v3::traits::Error, + ), + #[codec(index = 15)] + #[doc = "A given location which had a version change subscription was dropped owing to an error"] + #[doc = "migrating the location to our new XCM format."] + #[doc = ""] + #[doc = "\\[ location, query ID \\]"] + NotifyTargetMigrationFail( + runtime_types::xcm::VersionedMultiLocation, + ::core::primitive::u64, + ), + #[codec(index = 16)] + #[doc = "Expected query response has been received but the expected querier location placed in"] + #[doc = "storage by this runtime previously cannot be decoded. The query remains registered."] + #[doc = ""] + #[doc = "This is unexpected (since a location placed in storage in a previously executing"] + #[doc = "runtime should be readable prior to query timeout) and dangerous since the possibly"] + #[doc = "valid response will be dropped. Manual governance intervention is probably going to be"] + #[doc = "needed."] + #[doc = ""] + #[doc = "\\[ origin location, id \\]"] + InvalidQuerierVersion( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + ), + #[codec(index = 17)] + #[doc = "Expected query response has been received but the querier location of the response does"] + #[doc = "not match the expected. The query remains registered for a later, valid, response to"] + #[doc = "be received and acted upon."] + #[doc = ""] + #[doc = "\\[ origin location, id, expected querier, maybe actual querier \\]"] + InvalidQuerier( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + ), + #[codec(index = 18)] + #[doc = "A remote has requested XCM version change notification from us and we have honored it."] + #[doc = "A version information message is sent to them and its cost is included."] + #[doc = ""] + #[doc = "\\[ destination location, cost \\]"] + VersionNotifyStarted( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 19)] + #[doc = "We have requested that a remote chain sends us XCM version change notifications."] + #[doc = ""] + #[doc = "\\[ destination location, cost \\]"] + VersionNotifyRequested( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 20)] + #[doc = "We have requested that a remote chain stops sending us XCM version change notifications."] + #[doc = ""] + #[doc = "\\[ destination location, cost \\]"] + VersionNotifyUnrequested( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 21)] + #[doc = "Fees were paid from a location for an operation (often for using `SendXcm`)."] + #[doc = ""] + #[doc = "\\[ paying location, fees \\]"] + FeesPaid( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 22)] + #[doc = "Some assets have been claimed from an asset trap"] + #[doc = ""] + #[doc = "\\[ hash, origin, assets \\]"] + AssetsClaimed( + ::subxt::utils::H256, + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::VersionedMultiAssets, + ), + } + } + } + pub mod polkadot_core_primitives { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct InboundDownwardMessage<_0> { + pub sent_at: _0, + pub msg: ::std::vec::Vec<::core::primitive::u8>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct InboundHrmpMessage<_0> { + pub sent_at: _0, + pub data: ::std::vec::Vec<::core::primitive::u8>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct OutboundHrmpMessage<_0> { + pub recipient: _0, + pub data: ::std::vec::Vec<::core::primitive::u8>, + } + } + pub mod polkadot_parachain { + use super::runtime_types; + pub mod primitives { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct HeadData(pub ::std::vec::Vec<::core::primitive::u8>); + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Id(pub ::core::primitive::u32); + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum XcmpMessageFormat { + #[codec(index = 0)] + ConcatenatedVersionedXcm, + #[codec(index = 1)] + ConcatenatedEncodedBlob, + #[codec(index = 2)] + Signals, + } + } + } + pub mod polkadot_primitives { + use super::runtime_types; + pub mod v2 { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct AbridgedHostConfiguration { + pub max_code_size: ::core::primitive::u32, + pub max_head_data_size: ::core::primitive::u32, + pub max_upward_queue_count: ::core::primitive::u32, + pub max_upward_queue_size: ::core::primitive::u32, + pub max_upward_message_size: ::core::primitive::u32, + pub max_upward_message_num_per_candidate: ::core::primitive::u32, + pub hrmp_max_message_num_per_candidate: ::core::primitive::u32, + pub validation_upgrade_cooldown: ::core::primitive::u32, + pub validation_upgrade_delay: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct AbridgedHrmpChannel { + pub max_capacity: ::core::primitive::u32, + pub max_total_size: ::core::primitive::u32, + pub max_message_size: ::core::primitive::u32, + pub msg_count: ::core::primitive::u32, + pub total_size: ::core::primitive::u32, + pub mqc_head: ::core::option::Option<::subxt::utils::H256>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct PersistedValidationData<_0, _1> { + pub parent_head: runtime_types::polkadot_parachain::primitives::HeadData, + pub relay_parent_number: _1, + pub relay_parent_storage_root: _0, + pub max_pov_size: _1, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum UpgradeRestriction { + #[codec(index = 0)] + Present, + } + } + } + pub mod rialto_parachain_runtime { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Runtime; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum RuntimeCall { + #[codec(index = 0)] + System(runtime_types::frame_system::pallet::Call), + #[codec(index = 1)] + Timestamp(runtime_types::pallet_timestamp::pallet::Call), + #[codec(index = 2)] + Sudo(runtime_types::pallet_sudo::pallet::Call), + #[codec(index = 20)] + ParachainSystem(runtime_types::cumulus_pallet_parachain_system::pallet::Call), + #[codec(index = 30)] + Balances(runtime_types::pallet_balances::pallet::Call), + #[codec(index = 50)] + XcmpQueue(runtime_types::cumulus_pallet_xcmp_queue::pallet::Call), + #[codec(index = 51)] + PolkadotXcm(runtime_types::pallet_xcm::pallet::Call), + #[codec(index = 52)] + CumulusXcm(runtime_types::cumulus_pallet_xcm::pallet::Call), + #[codec(index = 53)] + DmpQueue(runtime_types::cumulus_pallet_dmp_queue::pallet::Call), + #[codec(index = 54)] + BridgeRelayers(runtime_types::pallet_bridge_relayers::pallet::Call), + #[codec(index = 55)] + BridgeMillauGrandpa(runtime_types::pallet_bridge_grandpa::pallet::Call), + #[codec(index = 56)] + BridgeMillauMessages(runtime_types::pallet_bridge_messages::pallet::Call), + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum RuntimeEvent { + #[codec(index = 0)] + System(runtime_types::frame_system::pallet::Event), + #[codec(index = 2)] + Sudo(runtime_types::pallet_sudo::pallet::Event), + #[codec(index = 4)] + TransactionPayment(runtime_types::pallet_transaction_payment::pallet::Event), + #[codec(index = 20)] + ParachainSystem(runtime_types::cumulus_pallet_parachain_system::pallet::Event), + #[codec(index = 30)] + Balances(runtime_types::pallet_balances::pallet::Event), + #[codec(index = 50)] + XcmpQueue(runtime_types::cumulus_pallet_xcmp_queue::pallet::Event), + #[codec(index = 51)] + PolkadotXcm(runtime_types::pallet_xcm::pallet::Event), + #[codec(index = 52)] + CumulusXcm(runtime_types::cumulus_pallet_xcm::pallet::Event), + #[codec(index = 53)] + DmpQueue(runtime_types::cumulus_pallet_dmp_queue::pallet::Event), + #[codec(index = 54)] + BridgeRelayers(runtime_types::pallet_bridge_relayers::pallet::Event), + #[codec(index = 56)] + BridgeMillauMessages(runtime_types::pallet_bridge_messages::pallet::Event), + } + } + pub mod sp_arithmetic { + use super::runtime_types; + pub mod fixed_point { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct FixedU128(pub ::core::primitive::u128); + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum ArithmeticError { + #[codec(index = 0)] + Underflow, + #[codec(index = 1)] + Overflow, + #[codec(index = 2)] + DivisionByZero, + } + } + pub mod sp_core { + use super::runtime_types; + pub mod bounded { + use super::runtime_types; + pub mod bounded_vec { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct BoundedVec<_0>(pub ::std::vec::Vec<_0>); + } + pub mod weak_bounded_vec { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct WeakBoundedVec<_0>(pub ::std::vec::Vec<_0>); + } + } + pub mod ecdsa { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Signature(pub [::core::primitive::u8; 65usize]); + } + pub mod ed25519 { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Public(pub [::core::primitive::u8; 32usize]); + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Signature(pub [::core::primitive::u8; 64usize]); + } + pub mod sr25519 { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Signature(pub [::core::primitive::u8; 64usize]); + } + } + pub mod sp_finality_grandpa { + use super::runtime_types; + pub mod app { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Public(pub runtime_types::sp_core::ed25519::Public); + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Signature(pub runtime_types::sp_core::ed25519::Signature); + } + } + pub mod sp_runtime { + use super::runtime_types; + pub mod generic { + use super::runtime_types; + pub mod digest { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum DigestItem { + #[codec(index = 6)] + PreRuntime( + [::core::primitive::u8; 4usize], + ::std::vec::Vec<::core::primitive::u8>, + ), + #[codec(index = 4)] + Consensus( + [::core::primitive::u8; 4usize], + ::std::vec::Vec<::core::primitive::u8>, + ), + #[codec(index = 5)] + Seal( + [::core::primitive::u8; 4usize], + ::std::vec::Vec<::core::primitive::u8>, + ), + #[codec(index = 0)] + Other(::std::vec::Vec<::core::primitive::u8>), + #[codec(index = 8)] + RuntimeEnvironmentUpdated, + } + } + pub mod unchecked_extrinsic { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct UncheckedExtrinsic<_0, _1, _2, _3>( + pub ::std::vec::Vec<::core::primitive::u8>, + #[codec(skip)] pub ::core::marker::PhantomData<(_1, _0, _2, _3)>, + ); + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum DispatchError { + #[codec(index = 0)] + Other, + #[codec(index = 1)] + CannotLookup, + #[codec(index = 2)] + BadOrigin, + #[codec(index = 3)] + Module(runtime_types::sp_runtime::ModuleError), + #[codec(index = 4)] + ConsumerRemaining, + #[codec(index = 5)] + NoProviders, + #[codec(index = 6)] + TooManyConsumers, + #[codec(index = 7)] + Token(runtime_types::sp_runtime::TokenError), + #[codec(index = 8)] + Arithmetic(runtime_types::sp_arithmetic::ArithmeticError), + #[codec(index = 9)] + Transactional(runtime_types::sp_runtime::TransactionalError), + #[codec(index = 10)] + Exhausted, + #[codec(index = 11)] + Corruption, + #[codec(index = 12)] + Unavailable, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ModuleError { + pub index: ::core::primitive::u8, + pub error: [::core::primitive::u8; 4usize], + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum MultiSignature { + #[codec(index = 0)] + Ed25519(runtime_types::sp_core::ed25519::Signature), + #[codec(index = 1)] + Sr25519(runtime_types::sp_core::sr25519::Signature), + #[codec(index = 2)] + Ecdsa(runtime_types::sp_core::ecdsa::Signature), + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum TokenError { + #[codec(index = 0)] + NoFunds, + #[codec(index = 1)] + WouldDie, + #[codec(index = 2)] + BelowMinimum, + #[codec(index = 3)] + CannotCreate, + #[codec(index = 4)] + UnknownAsset, + #[codec(index = 5)] + Frozen, + #[codec(index = 6)] + Unsupported, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum TransactionalError { + #[codec(index = 0)] + LimitReached, + #[codec(index = 1)] + NoLayer, + } + } + pub mod sp_trie { + use super::runtime_types; + pub mod storage_proof { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct StorageProof { + pub trie_nodes: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, + } + } + } + pub mod sp_version { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct RuntimeVersion { + pub spec_name: ::std::string::String, + pub impl_name: ::std::string::String, + pub authoring_version: ::core::primitive::u32, + pub spec_version: ::core::primitive::u32, + pub impl_version: ::core::primitive::u32, + pub apis: + ::std::vec::Vec<([::core::primitive::u8; 8usize], ::core::primitive::u32)>, + pub transaction_version: ::core::primitive::u32, + pub state_version: ::core::primitive::u8, + } + } + pub mod sp_weights { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct RuntimeDbWeight { + pub read: ::core::primitive::u64, + pub write: ::core::primitive::u64, + } + } + pub mod xcm { + use super::runtime_types; + pub mod double_encoded { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct DoubleEncoded { + pub encoded: ::std::vec::Vec<::core::primitive::u8>, + } + } + pub mod v2 { + use super::runtime_types; + pub mod junction { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Junction { + #[codec(index = 0)] + Parachain(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 1)] + AccountId32 { + network: runtime_types::xcm::v2::NetworkId, + id: [::core::primitive::u8; 32usize], + }, + #[codec(index = 2)] + AccountIndex64 { + network: runtime_types::xcm::v2::NetworkId, + #[codec(compact)] + index: ::core::primitive::u64, + }, + #[codec(index = 3)] + AccountKey20 { + network: runtime_types::xcm::v2::NetworkId, + key: [::core::primitive::u8; 20usize], + }, + #[codec(index = 4)] + PalletInstance(::core::primitive::u8), + #[codec(index = 5)] + GeneralIndex(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 6)] + GeneralKey( + runtime_types::sp_core::bounded::weak_bounded_vec::WeakBoundedVec< + ::core::primitive::u8, + >, + ), + #[codec(index = 7)] + OnlyChild, + #[codec(index = 8)] + Plurality { + id: runtime_types::xcm::v2::BodyId, + part: runtime_types::xcm::v2::BodyPart, + }, + } + } + pub mod multiasset { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum AssetId { + #[codec(index = 0)] + Concrete(runtime_types::xcm::v2::multilocation::MultiLocation), + #[codec(index = 1)] + Abstract(::std::vec::Vec<::core::primitive::u8>), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum AssetInstance { + #[codec(index = 0)] + Undefined, + #[codec(index = 1)] + Index(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 2)] + Array4([::core::primitive::u8; 4usize]), + #[codec(index = 3)] + Array8([::core::primitive::u8; 8usize]), + #[codec(index = 4)] + Array16([::core::primitive::u8; 16usize]), + #[codec(index = 5)] + Array32([::core::primitive::u8; 32usize]), + #[codec(index = 6)] + Blob(::std::vec::Vec<::core::primitive::u8>), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Fungibility { + #[codec(index = 0)] + Fungible(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 1)] + NonFungible(runtime_types::xcm::v2::multiasset::AssetInstance), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MultiAsset { + pub id: runtime_types::xcm::v2::multiasset::AssetId, + pub fun: runtime_types::xcm::v2::multiasset::Fungibility, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum MultiAssetFilter { + #[codec(index = 0)] + Definite(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 1)] + Wild(runtime_types::xcm::v2::multiasset::WildMultiAsset), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MultiAssets( + pub ::std::vec::Vec, + ); + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum WildFungibility { + #[codec(index = 0)] + Fungible, + #[codec(index = 1)] + NonFungible, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum WildMultiAsset { + #[codec(index = 0)] + All, + #[codec(index = 1)] + AllOf { + id: runtime_types::xcm::v2::multiasset::AssetId, + fun: runtime_types::xcm::v2::multiasset::WildFungibility, + }, + } + } + pub mod multilocation { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Junctions { + #[codec(index = 0)] + Here, + #[codec(index = 1)] + X1(runtime_types::xcm::v2::junction::Junction), + #[codec(index = 2)] + X2( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 3)] + X3( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 4)] + X4( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 5)] + X5( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 6)] + X6( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 7)] + X7( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 8)] + X8( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MultiLocation { + pub parents: ::core::primitive::u8, + pub interior: runtime_types::xcm::v2::multilocation::Junctions, + } + } + pub mod traits { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Error { + #[codec(index = 0)] + Overflow, + #[codec(index = 1)] + Unimplemented, + #[codec(index = 2)] + UntrustedReserveLocation, + #[codec(index = 3)] + UntrustedTeleportLocation, + #[codec(index = 4)] + MultiLocationFull, + #[codec(index = 5)] + MultiLocationNotInvertible, + #[codec(index = 6)] + BadOrigin, + #[codec(index = 7)] + InvalidLocation, + #[codec(index = 8)] + AssetNotFound, + #[codec(index = 9)] + FailedToTransactAsset, + #[codec(index = 10)] + NotWithdrawable, + #[codec(index = 11)] + LocationCannotHold, + #[codec(index = 12)] + ExceedsMaxMessageSize, + #[codec(index = 13)] + DestinationUnsupported, + #[codec(index = 14)] + Transport, + #[codec(index = 15)] + Unroutable, + #[codec(index = 16)] + UnknownClaim, + #[codec(index = 17)] + FailedToDecode, + #[codec(index = 18)] + MaxWeightInvalid, + #[codec(index = 19)] + NotHoldingFees, + #[codec(index = 20)] + TooExpensive, + #[codec(index = 21)] + Trap(::core::primitive::u64), + #[codec(index = 22)] + UnhandledXcmVersion, + #[codec(index = 23)] + WeightLimitReached(::core::primitive::u64), + #[codec(index = 24)] + Barrier, + #[codec(index = 25)] + WeightNotComputable, + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum BodyId { + #[codec(index = 0)] + Unit, + #[codec(index = 1)] + Named( + runtime_types::sp_core::bounded::weak_bounded_vec::WeakBoundedVec< + ::core::primitive::u8, + >, + ), + #[codec(index = 2)] + Index(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 3)] + Executive, + #[codec(index = 4)] + Technical, + #[codec(index = 5)] + Legislative, + #[codec(index = 6)] + Judicial, + #[codec(index = 7)] + Defense, + #[codec(index = 8)] + Administration, + #[codec(index = 9)] + Treasury, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum BodyPart { + #[codec(index = 0)] + Voice, + #[codec(index = 1)] + Members { + #[codec(compact)] + count: ::core::primitive::u32, + }, + #[codec(index = 2)] + Fraction { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + #[codec(index = 3)] + AtLeastProportion { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + #[codec(index = 4)] + MoreThanProportion { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Instruction { + #[codec(index = 0)] + WithdrawAsset(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 1)] + ReserveAssetDeposited(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 2)] + ReceiveTeleportedAsset(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 3)] + QueryResponse { + #[codec(compact)] + query_id: ::core::primitive::u64, + response: runtime_types::xcm::v2::Response, + #[codec(compact)] + max_weight: ::core::primitive::u64, + }, + #[codec(index = 4)] + TransferAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssets, + beneficiary: runtime_types::xcm::v2::multilocation::MultiLocation, + }, + #[codec(index = 5)] + TransferReserveAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssets, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + xcm: runtime_types::xcm::v2::Xcm, + }, + #[codec(index = 6)] + Transact { + origin_type: runtime_types::xcm::v2::OriginKind, + #[codec(compact)] + require_weight_at_most: ::core::primitive::u64, + call: runtime_types::xcm::double_encoded::DoubleEncoded, + }, + #[codec(index = 7)] + HrmpNewChannelOpenRequest { + #[codec(compact)] + sender: ::core::primitive::u32, + #[codec(compact)] + max_message_size: ::core::primitive::u32, + #[codec(compact)] + max_capacity: ::core::primitive::u32, + }, + #[codec(index = 8)] + HrmpChannelAccepted { + #[codec(compact)] + recipient: ::core::primitive::u32, + }, + #[codec(index = 9)] + HrmpChannelClosing { + #[codec(compact)] + initiator: ::core::primitive::u32, + #[codec(compact)] + sender: ::core::primitive::u32, + #[codec(compact)] + recipient: ::core::primitive::u32, + }, + #[codec(index = 10)] + ClearOrigin, + #[codec(index = 11)] + DescendOrigin(runtime_types::xcm::v2::multilocation::Junctions), + #[codec(index = 12)] + ReportError { + #[codec(compact)] + query_id: ::core::primitive::u64, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + #[codec(compact)] + max_response_weight: ::core::primitive::u64, + }, + #[codec(index = 13)] + DepositAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + #[codec(compact)] + max_assets: ::core::primitive::u32, + beneficiary: runtime_types::xcm::v2::multilocation::MultiLocation, + }, + #[codec(index = 14)] + DepositReserveAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + #[codec(compact)] + max_assets: ::core::primitive::u32, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + xcm: runtime_types::xcm::v2::Xcm, + }, + #[codec(index = 15)] + ExchangeAsset { + give: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + receive: runtime_types::xcm::v2::multiasset::MultiAssets, + }, + #[codec(index = 16)] + InitiateReserveWithdraw { + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + reserve: runtime_types::xcm::v2::multilocation::MultiLocation, + xcm: runtime_types::xcm::v2::Xcm, + }, + #[codec(index = 17)] + InitiateTeleport { + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + xcm: runtime_types::xcm::v2::Xcm, + }, + #[codec(index = 18)] + QueryHolding { + #[codec(compact)] + query_id: ::core::primitive::u64, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + #[codec(compact)] + max_response_weight: ::core::primitive::u64, + }, + #[codec(index = 19)] + BuyExecution { + fees: runtime_types::xcm::v2::multiasset::MultiAsset, + weight_limit: runtime_types::xcm::v2::WeightLimit, + }, + #[codec(index = 20)] + RefundSurplus, + #[codec(index = 21)] + SetErrorHandler(runtime_types::xcm::v2::Xcm), + #[codec(index = 22)] + SetAppendix(runtime_types::xcm::v2::Xcm), + #[codec(index = 23)] + ClearError, + #[codec(index = 24)] + ClaimAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssets, + ticket: runtime_types::xcm::v2::multilocation::MultiLocation, + }, + #[codec(index = 25)] + Trap(#[codec(compact)] ::core::primitive::u64), + #[codec(index = 26)] + SubscribeVersion { + #[codec(compact)] + query_id: ::core::primitive::u64, + #[codec(compact)] + max_response_weight: ::core::primitive::u64, + }, + #[codec(index = 27)] + UnsubscribeVersion, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum NetworkId { + #[codec(index = 0)] + Any, + #[codec(index = 1)] + Named( + runtime_types::sp_core::bounded::weak_bounded_vec::WeakBoundedVec< + ::core::primitive::u8, + >, + ), + #[codec(index = 2)] + Polkadot, + #[codec(index = 3)] + Kusama, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum OriginKind { + #[codec(index = 0)] + Native, + #[codec(index = 1)] + SovereignAccount, + #[codec(index = 2)] + Superuser, + #[codec(index = 3)] + Xcm, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Response { + #[codec(index = 0)] + Null, + #[codec(index = 1)] + Assets(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 2)] + ExecutionResult( + ::core::option::Option<( + ::core::primitive::u32, + runtime_types::xcm::v2::traits::Error, + )>, + ), + #[codec(index = 3)] + Version(::core::primitive::u32), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum WeightLimit { + #[codec(index = 0)] + Unlimited, + #[codec(index = 1)] + Limited(#[codec(compact)] ::core::primitive::u64), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Xcm(pub ::std::vec::Vec); + } + pub mod v3 { + use super::runtime_types; + pub mod junction { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum BodyId { + #[codec(index = 0)] + Unit, + #[codec(index = 1)] + Moniker([::core::primitive::u8; 4usize]), + #[codec(index = 2)] + Index(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 3)] + Executive, + #[codec(index = 4)] + Technical, + #[codec(index = 5)] + Legislative, + #[codec(index = 6)] + Judicial, + #[codec(index = 7)] + Defense, + #[codec(index = 8)] + Administration, + #[codec(index = 9)] + Treasury, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum BodyPart { + #[codec(index = 0)] + Voice, + #[codec(index = 1)] + Members { + #[codec(compact)] + count: ::core::primitive::u32, + }, + #[codec(index = 2)] + Fraction { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + #[codec(index = 3)] + AtLeastProportion { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + #[codec(index = 4)] + MoreThanProportion { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Junction { + #[codec(index = 0)] + Parachain(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 1)] + AccountId32 { + network: + ::core::option::Option, + id: [::core::primitive::u8; 32usize], + }, + #[codec(index = 2)] + AccountIndex64 { + network: + ::core::option::Option, + #[codec(compact)] + index: ::core::primitive::u64, + }, + #[codec(index = 3)] + AccountKey20 { + network: + ::core::option::Option, + key: [::core::primitive::u8; 20usize], + }, + #[codec(index = 4)] + PalletInstance(::core::primitive::u8), + #[codec(index = 5)] + GeneralIndex(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 6)] + GeneralKey([::core::primitive::u8; 32usize]), + #[codec(index = 7)] + OnlyChild, + #[codec(index = 8)] + Plurality { + id: runtime_types::xcm::v3::junction::BodyId, + part: runtime_types::xcm::v3::junction::BodyPart, + }, + #[codec(index = 9)] + GlobalConsensus(runtime_types::xcm::v3::junction::NetworkId), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum NetworkId { + #[codec(index = 0)] + ByGenesis([::core::primitive::u8; 32usize]), + #[codec(index = 1)] + ByFork { + block_number: ::core::primitive::u64, + block_hash: [::core::primitive::u8; 32usize], + }, + #[codec(index = 2)] + Polkadot, + #[codec(index = 3)] + Kusama, + #[codec(index = 4)] + Westend, + #[codec(index = 5)] + Rococo, + #[codec(index = 6)] + Wococo, + #[codec(index = 7)] + Ethereum { + #[codec(compact)] + chain_id: ::core::primitive::u64, + }, + #[codec(index = 8)] + BitcoinCore, + #[codec(index = 9)] + BitcoinCash, + } + } + pub mod junctions { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Junctions { + #[codec(index = 0)] + Here, + #[codec(index = 1)] + X1(runtime_types::xcm::v3::junction::Junction), + #[codec(index = 2)] + X2( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 3)] + X3( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 4)] + X4( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 5)] + X5( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 6)] + X6( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 7)] + X7( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 8)] + X8( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + } + } + pub mod multiasset { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum AssetId { + #[codec(index = 0)] + Concrete(runtime_types::xcm::v3::multilocation::MultiLocation), + #[codec(index = 1)] + Abstract([::core::primitive::u8; 32usize]), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum AssetInstance { + #[codec(index = 0)] + Undefined, + #[codec(index = 1)] + Index(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 2)] + Array4([::core::primitive::u8; 4usize]), + #[codec(index = 3)] + Array8([::core::primitive::u8; 8usize]), + #[codec(index = 4)] + Array16([::core::primitive::u8; 16usize]), + #[codec(index = 5)] + Array32([::core::primitive::u8; 32usize]), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Fungibility { + #[codec(index = 0)] + Fungible(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 1)] + NonFungible(runtime_types::xcm::v3::multiasset::AssetInstance), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MultiAsset { + pub id: runtime_types::xcm::v3::multiasset::AssetId, + pub fun: runtime_types::xcm::v3::multiasset::Fungibility, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum MultiAssetFilter { + #[codec(index = 0)] + Definite(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 1)] + Wild(runtime_types::xcm::v3::multiasset::WildMultiAsset), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MultiAssets( + pub ::std::vec::Vec, + ); + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum WildFungibility { + #[codec(index = 0)] + Fungible, + #[codec(index = 1)] + NonFungible, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum WildMultiAsset { + #[codec(index = 0)] + All, + #[codec(index = 1)] + AllOf { + id: runtime_types::xcm::v3::multiasset::AssetId, + fun: runtime_types::xcm::v3::multiasset::WildFungibility, + }, + #[codec(index = 2)] + AllCounted(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 3)] + AllOfCounted { + id: runtime_types::xcm::v3::multiasset::AssetId, + fun: runtime_types::xcm::v3::multiasset::WildFungibility, + #[codec(compact)] + count: ::core::primitive::u32, + }, + } + } + pub mod multilocation { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MultiLocation { + pub parents: ::core::primitive::u8, + pub interior: runtime_types::xcm::v3::junctions::Junctions, + } + } + pub mod traits { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Error { + #[codec(index = 0)] + Overflow, + #[codec(index = 1)] + Unimplemented, + #[codec(index = 2)] + UntrustedReserveLocation, + #[codec(index = 3)] + UntrustedTeleportLocation, + #[codec(index = 4)] + LocationFull, + #[codec(index = 5)] + LocationNotInvertible, + #[codec(index = 6)] + BadOrigin, + #[codec(index = 7)] + InvalidLocation, + #[codec(index = 8)] + AssetNotFound, + #[codec(index = 9)] + FailedToTransactAsset, + #[codec(index = 10)] + NotWithdrawable, + #[codec(index = 11)] + LocationCannotHold, + #[codec(index = 12)] + ExceedsMaxMessageSize, + #[codec(index = 13)] + DestinationUnsupported, + #[codec(index = 14)] + Transport, + #[codec(index = 15)] + Unroutable, + #[codec(index = 16)] + UnknownClaim, + #[codec(index = 17)] + FailedToDecode, + #[codec(index = 18)] + MaxWeightInvalid, + #[codec(index = 19)] + NotHoldingFees, + #[codec(index = 20)] + TooExpensive, + #[codec(index = 21)] + Trap(::core::primitive::u64), + #[codec(index = 22)] + ExpectationFalse, + #[codec(index = 23)] + PalletNotFound, + #[codec(index = 24)] + NameMismatch, + #[codec(index = 25)] + VersionIncompatible, + #[codec(index = 26)] + HoldingWouldOverflow, + #[codec(index = 27)] + ExportError, + #[codec(index = 28)] + ReanchorFailed, + #[codec(index = 29)] + NoDeal, + #[codec(index = 30)] + FeesNotMet, + #[codec(index = 31)] + LockError, + #[codec(index = 32)] + NoPermission, + #[codec(index = 33)] + Unanchored, + #[codec(index = 34)] + NotDepositable, + #[codec(index = 35)] + UnhandledXcmVersion, + #[codec(index = 36)] + WeightLimitReached(::sp_weights::Weight), + #[codec(index = 37)] + Barrier, + #[codec(index = 38)] + WeightNotComputable, + #[codec(index = 39)] + ExceedsStackLimit, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Outcome { + #[codec(index = 0)] + Complete(::sp_weights::Weight), + #[codec(index = 1)] + Incomplete(::sp_weights::Weight, runtime_types::xcm::v3::traits::Error), + #[codec(index = 2)] + Error(runtime_types::xcm::v3::traits::Error), + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Instruction { + #[codec(index = 0)] + WithdrawAsset(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 1)] + ReserveAssetDeposited(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 2)] + ReceiveTeleportedAsset(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 3)] + QueryResponse { + #[codec(compact)] + query_id: ::core::primitive::u64, + response: runtime_types::xcm::v3::Response, + max_weight: ::sp_weights::Weight, + querier: ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + }, + #[codec(index = 4)] + TransferAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssets, + beneficiary: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 5)] + TransferReserveAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssets, + dest: runtime_types::xcm::v3::multilocation::MultiLocation, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 6)] + Transact { + origin_kind: runtime_types::xcm::v2::OriginKind, + require_weight_at_most: ::sp_weights::Weight, + call: runtime_types::xcm::double_encoded::DoubleEncoded, + }, + #[codec(index = 7)] + HrmpNewChannelOpenRequest { + #[codec(compact)] + sender: ::core::primitive::u32, + #[codec(compact)] + max_message_size: ::core::primitive::u32, + #[codec(compact)] + max_capacity: ::core::primitive::u32, + }, + #[codec(index = 8)] + HrmpChannelAccepted { + #[codec(compact)] + recipient: ::core::primitive::u32, + }, + #[codec(index = 9)] + HrmpChannelClosing { + #[codec(compact)] + initiator: ::core::primitive::u32, + #[codec(compact)] + sender: ::core::primitive::u32, + #[codec(compact)] + recipient: ::core::primitive::u32, + }, + #[codec(index = 10)] + ClearOrigin, + #[codec(index = 11)] + DescendOrigin(runtime_types::xcm::v3::junctions::Junctions), + #[codec(index = 12)] + ReportError(runtime_types::xcm::v3::QueryResponseInfo), + #[codec(index = 13)] + DepositAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + beneficiary: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 14)] + DepositReserveAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + dest: runtime_types::xcm::v3::multilocation::MultiLocation, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 15)] + ExchangeAsset { + give: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + want: runtime_types::xcm::v3::multiasset::MultiAssets, + maximal: ::core::primitive::bool, + }, + #[codec(index = 16)] + InitiateReserveWithdraw { + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + reserve: runtime_types::xcm::v3::multilocation::MultiLocation, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 17)] + InitiateTeleport { + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + dest: runtime_types::xcm::v3::multilocation::MultiLocation, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 18)] + ReportHolding { + response_info: runtime_types::xcm::v3::QueryResponseInfo, + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + }, + #[codec(index = 19)] + BuyExecution { + fees: runtime_types::xcm::v3::multiasset::MultiAsset, + weight_limit: runtime_types::xcm::v3::WeightLimit, + }, + #[codec(index = 20)] + RefundSurplus, + #[codec(index = 21)] + SetErrorHandler(runtime_types::xcm::v3::Xcm), + #[codec(index = 22)] + SetAppendix(runtime_types::xcm::v3::Xcm), + #[codec(index = 23)] + ClearError, + #[codec(index = 24)] + ClaimAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssets, + ticket: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 25)] + Trap(#[codec(compact)] ::core::primitive::u64), + #[codec(index = 26)] + SubscribeVersion { + #[codec(compact)] + query_id: ::core::primitive::u64, + max_response_weight: ::sp_weights::Weight, + }, + #[codec(index = 27)] + UnsubscribeVersion, + #[codec(index = 28)] + BurnAsset(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 29)] + ExpectAsset(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 30)] + ExpectOrigin( + ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + ), + #[codec(index = 31)] + ExpectError( + ::core::option::Option<( + ::core::primitive::u32, + runtime_types::xcm::v3::traits::Error, + )>, + ), + #[codec(index = 32)] + ExpectTransactStatus(runtime_types::xcm::v3::MaybeErrorCode), + #[codec(index = 33)] + QueryPallet { + module_name: ::std::vec::Vec<::core::primitive::u8>, + response_info: runtime_types::xcm::v3::QueryResponseInfo, + }, + #[codec(index = 34)] + ExpectPallet { + #[codec(compact)] + index: ::core::primitive::u32, + name: ::std::vec::Vec<::core::primitive::u8>, + module_name: ::std::vec::Vec<::core::primitive::u8>, + #[codec(compact)] + crate_major: ::core::primitive::u32, + #[codec(compact)] + min_crate_minor: ::core::primitive::u32, + }, + #[codec(index = 35)] + ReportTransactStatus(runtime_types::xcm::v3::QueryResponseInfo), + #[codec(index = 36)] + ClearTransactStatus, + #[codec(index = 37)] + UniversalOrigin(runtime_types::xcm::v3::junction::Junction), + #[codec(index = 38)] + ExportMessage { + network: runtime_types::xcm::v3::junction::NetworkId, + destination: runtime_types::xcm::v3::junctions::Junctions, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 39)] + LockAsset { + asset: runtime_types::xcm::v3::multiasset::MultiAsset, + unlocker: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 40)] + UnlockAsset { + asset: runtime_types::xcm::v3::multiasset::MultiAsset, + target: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 41)] + NoteUnlockable { + asset: runtime_types::xcm::v3::multiasset::MultiAsset, + owner: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 42)] + RequestUnlock { + asset: runtime_types::xcm::v3::multiasset::MultiAsset, + locker: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 43)] + SetFeesMode { jit_withdraw: ::core::primitive::bool }, + #[codec(index = 44)] + SetTopic([::core::primitive::u8; 32usize]), + #[codec(index = 45)] + ClearTopic, + #[codec(index = 46)] + AliasOrigin(runtime_types::xcm::v3::multilocation::MultiLocation), + #[codec(index = 47)] + UnpaidExecution { + weight_limit: runtime_types::xcm::v3::WeightLimit, + check_origin: ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum MaybeErrorCode { + #[codec(index = 0)] + Success, + #[codec(index = 1)] + Error(::std::vec::Vec<::core::primitive::u8>), + #[codec(index = 2)] + TruncatedError(::std::vec::Vec<::core::primitive::u8>), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct PalletInfo { + #[codec(compact)] + pub index: ::core::primitive::u32, + pub name: ::std::vec::Vec<::core::primitive::u8>, + pub module_name: ::std::vec::Vec<::core::primitive::u8>, + #[codec(compact)] + pub major: ::core::primitive::u32, + #[codec(compact)] + pub minor: ::core::primitive::u32, + #[codec(compact)] + pub patch: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct QueryResponseInfo { + pub destination: runtime_types::xcm::v3::multilocation::MultiLocation, + #[codec(compact)] + pub query_id: ::core::primitive::u64, + pub max_weight: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Response { + #[codec(index = 0)] + Null, + #[codec(index = 1)] + Assets(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 2)] + ExecutionResult( + ::core::option::Option<( + ::core::primitive::u32, + runtime_types::xcm::v3::traits::Error, + )>, + ), + #[codec(index = 3)] + Version(::core::primitive::u32), + #[codec(index = 4)] + PalletsInfo(runtime_types::xcm::v3::VecPalletInfo), + #[codec(index = 5)] + DispatchResult(runtime_types::xcm::v3::MaybeErrorCode), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct VecPalletInfo(pub ::std::vec::Vec); + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum WeightLimit { + #[codec(index = 0)] + Unlimited, + #[codec(index = 1)] + Limited(::sp_weights::Weight), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Xcm(pub ::std::vec::Vec); + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum VersionedMultiAssets { + #[codec(index = 0)] + V2(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 1)] + V3(runtime_types::xcm::v3::multiasset::MultiAssets), + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum VersionedMultiLocation { + #[codec(index = 0)] + V2(runtime_types::xcm::v2::multilocation::MultiLocation), + #[codec(index = 1)] + V3(runtime_types::xcm::v3::multilocation::MultiLocation), + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum VersionedXcm { + #[codec(index = 2)] + V2(runtime_types::xcm::v2::Xcm), + #[codec(index = 3)] + V3(runtime_types::xcm::v3::Xcm), + } + } + } + #[doc = r" The default error type returned when there is a runtime issue,"] + #[doc = r" exposed here for ease of use."] + pub type DispatchError = runtime_types::sp_runtime::DispatchError; + pub fn constants() -> ConstantsApi { + ConstantsApi + } + pub fn storage() -> StorageApi { + StorageApi + } + pub fn tx() -> TransactionApi { + TransactionApi + } + pub struct ConstantsApi; + impl ConstantsApi { + pub fn system(&self) -> system::constants::ConstantsApi { + system::constants::ConstantsApi + } + pub fn timestamp(&self) -> timestamp::constants::ConstantsApi { + timestamp::constants::ConstantsApi + } + pub fn transaction_payment(&self) -> transaction_payment::constants::ConstantsApi { + transaction_payment::constants::ConstantsApi + } + pub fn balances(&self) -> balances::constants::ConstantsApi { + balances::constants::ConstantsApi + } + pub fn bridge_millau_grandpa(&self) -> bridge_millau_grandpa::constants::ConstantsApi { + bridge_millau_grandpa::constants::ConstantsApi + } + pub fn bridge_millau_messages(&self) -> bridge_millau_messages::constants::ConstantsApi { + bridge_millau_messages::constants::ConstantsApi + } + } + pub struct StorageApi; + impl StorageApi { + pub fn system(&self) -> system::storage::StorageApi { + system::storage::StorageApi + } + pub fn timestamp(&self) -> timestamp::storage::StorageApi { + timestamp::storage::StorageApi + } + pub fn sudo(&self) -> sudo::storage::StorageApi { + sudo::storage::StorageApi + } + pub fn randomness_collective_flip( + &self, + ) -> randomness_collective_flip::storage::StorageApi { + randomness_collective_flip::storage::StorageApi + } + pub fn transaction_payment(&self) -> transaction_payment::storage::StorageApi { + transaction_payment::storage::StorageApi + } + pub fn parachain_system(&self) -> parachain_system::storage::StorageApi { + parachain_system::storage::StorageApi + } + pub fn parachain_info(&self) -> parachain_info::storage::StorageApi { + parachain_info::storage::StorageApi + } + pub fn balances(&self) -> balances::storage::StorageApi { + balances::storage::StorageApi + } + pub fn xcmp_queue(&self) -> xcmp_queue::storage::StorageApi { + xcmp_queue::storage::StorageApi + } + pub fn dmp_queue(&self) -> dmp_queue::storage::StorageApi { + dmp_queue::storage::StorageApi + } + pub fn bridge_relayers(&self) -> bridge_relayers::storage::StorageApi { + bridge_relayers::storage::StorageApi + } + pub fn bridge_millau_grandpa(&self) -> bridge_millau_grandpa::storage::StorageApi { + bridge_millau_grandpa::storage::StorageApi + } + pub fn bridge_millau_messages(&self) -> bridge_millau_messages::storage::StorageApi { + bridge_millau_messages::storage::StorageApi + } + } + pub struct TransactionApi; + impl TransactionApi { + pub fn system(&self) -> system::calls::TransactionApi { + system::calls::TransactionApi + } + pub fn timestamp(&self) -> timestamp::calls::TransactionApi { + timestamp::calls::TransactionApi + } + pub fn sudo(&self) -> sudo::calls::TransactionApi { + sudo::calls::TransactionApi + } + pub fn parachain_system(&self) -> parachain_system::calls::TransactionApi { + parachain_system::calls::TransactionApi + } + pub fn balances(&self) -> balances::calls::TransactionApi { + balances::calls::TransactionApi + } + pub fn xcmp_queue(&self) -> xcmp_queue::calls::TransactionApi { + xcmp_queue::calls::TransactionApi + } + pub fn polkadot_xcm(&self) -> polkadot_xcm::calls::TransactionApi { + polkadot_xcm::calls::TransactionApi + } + pub fn cumulus_xcm(&self) -> cumulus_xcm::calls::TransactionApi { + cumulus_xcm::calls::TransactionApi + } + pub fn dmp_queue(&self) -> dmp_queue::calls::TransactionApi { + dmp_queue::calls::TransactionApi + } + pub fn bridge_relayers(&self) -> bridge_relayers::calls::TransactionApi { + bridge_relayers::calls::TransactionApi + } + pub fn bridge_millau_grandpa(&self) -> bridge_millau_grandpa::calls::TransactionApi { + bridge_millau_grandpa::calls::TransactionApi + } + pub fn bridge_millau_messages(&self) -> bridge_millau_messages::calls::TransactionApi { + bridge_millau_messages::calls::TransactionApi + } + } + #[doc = r" check whether the Client you are using is aligned with the statically generated codegen."] + pub fn validate_codegen>( + client: &C, + ) -> Result<(), ::subxt::error::MetadataError> { + let runtime_metadata_hash = client.metadata().metadata_hash(&PALLETS); + if runtime_metadata_hash != + [ + 29u8, 217u8, 238u8, 58u8, 9u8, 201u8, 174u8, 206u8, 19u8, 133u8, 133u8, 32u8, + 139u8, 251u8, 132u8, 7u8, 135u8, 118u8, 231u8, 231u8, 9u8, 150u8, 246u8, 171u8, + 190u8, 93u8, 114u8, 63u8, 86u8, 200u8, 148u8, 126u8, + ] { + Err(::subxt::error::MetadataError::IncompatibleMetadata) + } else { + Ok(()) + } + } +} diff --git a/relays/client-rialto-parachain/src/lib.rs b/relays/client-rialto-parachain/src/lib.rs index d6efd6581b2..2c3792725c3 100644 --- a/relays/client-rialto-parachain/src/lib.rs +++ b/relays/client-rialto-parachain/src/lib.rs @@ -16,7 +16,7 @@ //! Types used to connect to the Rialto-Substrate chain. -pub mod runtime_wrapper; +pub mod codegen_runtime; use bp_messages::MessageNonce; use bp_polkadot_core::PolkadotSignedExtension; @@ -29,7 +29,12 @@ use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount, MultiAddress}; use std::time::Duration; -pub use runtime_wrapper as runtime; +pub use codegen_runtime::api::runtime_types; + +pub type RuntimeCall = runtime_types::rialto_parachain_runtime::RuntimeCall; +pub type SudoCall = runtime_types::pallet_sudo::pallet::Call; +pub type BridgeGrandpaCall = runtime_types::pallet_bridge_grandpa::pallet::Call; +pub type BridgeMessagesCall = runtime_types::pallet_bridge_messages::pallet::Call; /// The address format for describing accounts. pub type Address = MultiAddress; @@ -51,12 +56,13 @@ impl Chain for RialtoParachain { const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); type SignedBlock = bp_polkadot_core::SignedBlock; - type Call = runtime::Call; + type Call = runtime_types::rialto_parachain_runtime::RuntimeCall; } impl ChainWithBalances for RialtoParachain { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { - bp_polkadot_core::AccountInfoStorageMapKeyProvider::final_key(account_id) + let key = codegen_runtime::api::storage().system().account(account_id); + StorageKey(key.to_bytes()) } } diff --git a/relays/client-rialto-parachain/src/runtime_wrapper.rs b/relays/client-rialto-parachain/src/runtime_wrapper.rs deleted file mode 100644 index b595d145d01..00000000000 --- a/relays/client-rialto-parachain/src/runtime_wrapper.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common 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. - -// Parity Bridges Common 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 Parity Bridges Common. If not, see . - -//! Types that are specific to the `RialtoParachain` runtime. Normally we could use the full -//! `RialtoParachain` runtime here, since it is constructed in this repo and we have access to it. -//! However we use a wrapped runtime instead in order to test the indirect runtime calls -//! functionality. - -use codec::{Decode, Encode}; -use scale_info::TypeInfo; - -use bp_header_chain::BridgeGrandpaCallOf; -use bridge_runtime_common::messages::BridgeMessagesCallOf; -use relay_substrate_client::calls::{SudoCall, XcmCall}; - -// The indirect pallet call used to sync `Millau` GRANDPA finality to `RialtoParachain`. -pub type BridgeMillauGrandpaCall = BridgeGrandpaCallOf; -// The indirect pallet call used to sync `Millau` messages to `RialtoParachain`. -pub type BridgeMillauMessagesCall = BridgeMessagesCallOf; - -/// `RialtoParachain` Runtime `Call` enum. -/// -/// The enum represents a subset of possible `Call`s we can send to `RialtoParachain` chain. -/// -/// All entries here (like pretty much in the entire file) must be kept in sync with -/// `RialtoParachain` `construct_runtime`, so that we maintain SCALE-compatibility. -#[allow(clippy::large_enum_variant)] -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub enum Call { - /// `Sudo` pallet. - #[codec(index = 2)] - Sudo(SudoCall), - - /// `Xcm` pallet. - #[codec(index = 51)] - PolkadotXcm(XcmCall), - - /// Millau GRANDPA bridge pallet. - #[codec(index = 55)] - BridgeMillauGrandpa(BridgeMillauGrandpaCall), - /// Millau messages bridge pallet. - #[codec(index = 56)] - BridgeMillauMessages(BridgeMillauMessagesCall), -} diff --git a/relays/client-substrate/Cargo.toml b/relays/client-substrate/Cargo.toml index a16de01713b..49201defdd1 100644 --- a/relays/client-substrate/Cargo.toml +++ b/relays/client-substrate/Cargo.toml @@ -9,13 +9,13 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" async-std = { version = "1.6.5", features = ["attributes"] } async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.1.5" } -futures = "0.3.7" +futures = "0.3.26" jsonrpsee = { version = "0.15", features = ["macros", "ws-client"] } log = "0.4.17" num-traits = "0.2" rand = "0.7" scale-info = { version = "2.1.1", features = ["derive"] } -tokio = { version = "1.24", features = ["rt-multi-thread"] } +tokio = { version = "1.25", features = ["rt-multi-thread"] } thiserror = "1.0.26" # Bridge dependencies diff --git a/relays/client-substrate/src/client.rs b/relays/client-substrate/src/client.rs index 62d6a7c599e..1a4aeb583cd 100644 --- a/relays/client-substrate/src/client.rs +++ b/relays/client-substrate/src/client.rs @@ -37,7 +37,7 @@ use jsonrpsee::{ core::DeserializeOwned, ws_client::{WsClient as RpcClient, WsClientBuilder as RpcClientBuilder}, }; -use num_traits::{Bounded, Zero}; +use num_traits::{Bounded, Saturating, Zero}; use pallet_balances::AccountData; use pallet_transaction_payment::InclusionFee; use relay_utils::{relay_loop::RECONNECT_DELAY, STALL_TIMEOUT}; @@ -69,6 +69,11 @@ const MAX_SUBSCRIPTION_CAPACITY: usize = 4096; /// half of this value. pub const ANCIENT_BLOCK_THRESHOLD: u32 = 128; +/// Returns `true` if we think that the state is already discarded for given block. +pub fn is_ancient_block + PartialOrd + Saturating>(block: N, best: N) -> bool { + best.saturating_sub(block) >= N::from(ANCIENT_BLOCK_THRESHOLD) +} + /// Opaque justifications subscription type. pub struct Subscription(pub(crate) Mutex>>); diff --git a/relays/client-substrate/src/error.rs b/relays/client-substrate/src/error.rs index ddea1819fb0..3b83f917bf8 100644 --- a/relays/client-substrate/src/error.rs +++ b/relays/client-substrate/src/error.rs @@ -61,6 +61,9 @@ pub enum Error { /// The bridge pallet is not yet initialized and all transactions will be rejected. #[error("Bridge pallet is not initialized.")] BridgePalletIsNotInitialized, + /// There's no best head of the parachain at the `pallet-bridge-parachains` at the target side. + #[error("No head of the ParaId({0}) at the bridge parachains pallet at {1}.")] + NoParachainHeadAtTarget(u32, String), /// An error has happened when we have tried to parse storage proof. #[error("Error when parsing storage proof: {0:?}.")] StorageProofError(bp_runtime::StorageProofError), diff --git a/relays/client-substrate/src/lib.rs b/relays/client-substrate/src/lib.rs index ef75e304246..c1a96c487c4 100644 --- a/relays/client-substrate/src/lib.rs +++ b/relays/client-substrate/src/lib.rs @@ -40,8 +40,8 @@ pub use crate::{ UnsignedTransaction, UtilityPallet, }, client::{ - ChainRuntimeVersion, Client, OpaqueGrandpaAuthoritiesSet, SimpleRuntimeVersion, - Subscription, ANCIENT_BLOCK_THRESHOLD, + is_ancient_block, ChainRuntimeVersion, Client, OpaqueGrandpaAuthoritiesSet, + SimpleRuntimeVersion, Subscription, ANCIENT_BLOCK_THRESHOLD, }, error::{Error, Result}, rpc::{SubstrateBeefyFinalityClient, SubstrateFinalityClient, SubstrateGrandpaFinalityClient}, diff --git a/relays/finality/Cargo.toml b/relays/finality/Cargo.toml index 401a02b2ca1..c3e32eb59b2 100644 --- a/relays/finality/Cargo.toml +++ b/relays/finality/Cargo.toml @@ -11,7 +11,7 @@ async-std = "1.6.5" async-trait = "0.1" backoff = "0.4" bp-header-chain = { path = "../../primitives/header-chain" } -futures = "0.3.5" +futures = "0.3.26" log = "0.4.17" num-traits = "0.2" relay-utils = { path = "../utils" } diff --git a/relays/finality/README.md b/relays/finality/README.md index edfd00192bc..effc3db6b12 100644 --- a/relays/finality/README.md +++ b/relays/finality/README.md @@ -19,11 +19,11 @@ More: [GRANDPA Finality Relay Sequence Diagram](../../docs/grandpa-finality-rela ## How to Use the Finality Relay The most important trait is the [`FinalitySyncPipeline`](./src/lib.rs), which defines the basic primitives of the -source chain (like block hash and number) and the type of finality proof (GRANDPA jusitfication or MMR proof). Once +source chain (like block hash and number) and the type of finality proof (GRANDPA justification or MMR proof). Once that is defined, there are two other traits - [`SourceClient`](./src/finality_loop.rs) and [`TarggetClient`](./src/finality_loop.rs). -The `SourceClient` represents the Substrate node client that connects to the source chain. The client need to +The `SourceClient` represents the Substrate node client that connects to the source chain. The client needs to be able to return the best finalized header number, finalized header and its finality proof and the stream of finality proofs. @@ -31,8 +31,8 @@ The `TargetClient` implementation must be able to craft finality delivery transa node. The transaction is then tracked by the relay until it is mined and finalized. The main entrypoint for the crate is the [`run` function](./src/finality_loop.rs), which takes source and target -clients and [`FinalitySyncParams`](./src/finality_loop.rs) parameters. The most imporant parameter is the -`only_mandatory_headers` - it it is set to `true`, the relay will only submit mandatory headers. Since transactions +clients and [`FinalitySyncParams`](./src/finality_loop.rs) parameters. The most important parameter is the +`only_mandatory_headers` - it is set to `true`, the relay will only submit mandatory headers. Since transactions with mandatory headers are fee-free, the cost of running such relay is zero (in terms of fees). ## Finality Relay Metrics @@ -55,4 +55,4 @@ change chain names. So the metrics are: breaks) this shall not happen and the metric will have `0` value. If relay operates properly, you should see that the `Rialto_to_Millau_Sync_best_source_at_target_block_number` -tries to reach the `Rialto_to_Millau_Sync_best_source_block_number`. And the latter one always increases. \ No newline at end of file +tries to reach the `Rialto_to_Millau_Sync_best_source_block_number`. And the latter one always increases. diff --git a/relays/lib-substrate-relay/Cargo.toml b/relays/lib-substrate-relay/Cargo.toml index e044be0957c..96fe9356fa0 100644 --- a/relays/lib-substrate-relay/Cargo.toml +++ b/relays/lib-substrate-relay/Cargo.toml @@ -11,7 +11,7 @@ thiserror = "1.0.26" async-std = "1.9.0" async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.1.5" } -futures = "0.3.12" +futures = "0.3.26" hex = "0.4" num-traits = "0.2" log = "0.4.17" @@ -48,8 +48,8 @@ sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } [dev-dependencies] -bp-millau = { path = "../../primitives/chain-millau" } bp-rialto = { path = "../../primitives/chain-rialto" } +bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" } bp-rococo = { path = "../../primitives/chain-rococo" } bp-wococo = { path = "../../primitives/chain-wococo" } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/lib-substrate-relay/src/finality/mod.rs b/relays/lib-substrate-relay/src/finality/mod.rs index 5d7b52cd1de..b0f0ee4e52c 100644 --- a/relays/lib-substrate-relay/src/finality/mod.rs +++ b/relays/lib-substrate-relay/src/finality/mod.rs @@ -135,7 +135,7 @@ where /// the variant for the `submit_finality_proof` call within that first option. #[rustfmt::skip] #[macro_export] -macro_rules! generate_mocked_submit_finality_proof_call_builder { +macro_rules! generate_submit_finality_proof_call_builder { ($pipeline:ident, $mocked_builder:ident, $bridge_grandpa:path, $submit_finality_proof:path) => { pub struct $mocked_builder; @@ -156,7 +156,12 @@ macro_rules! generate_mocked_submit_finality_proof_call_builder { ) -> relay_substrate_client::CallOf< <$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::TargetChain > { - $bridge_grandpa($submit_finality_proof(Box::new(header.into_inner()), proof)) + bp_runtime::paste::item! { + $bridge_grandpa($submit_finality_proof { + finality_target: Box::new(header.into_inner()), + justification: proof + }) + } } } }; diff --git a/relays/lib-substrate-relay/src/finality/target.rs b/relays/lib-substrate-relay/src/finality/target.rs index 951123ae07c..9c6ec7c3055 100644 --- a/relays/lib-substrate-relay/src/finality/target.rs +++ b/relays/lib-substrate-relay/src/finality/target.rs @@ -102,7 +102,8 @@ where None, ) .await? - .best_finalized_peer_at_best_self) + .best_finalized_peer_at_best_self + .ok_or(Error::BridgePalletIsNotInitialized)?) } async fn submit_finality_proof( diff --git a/relays/lib-substrate-relay/src/messages_lane.rs b/relays/lib-substrate-relay/src/messages_lane.rs index 73d441cfcd3..1a4f65ff51e 100644 --- a/relays/lib-substrate-relay/src/messages_lane.rs +++ b/relays/lib-substrate-relay/src/messages_lane.rs @@ -315,7 +315,7 @@ where /// the variant for the `receive_messages_proof` call within that first option. #[rustfmt::skip] #[macro_export] -macro_rules! generate_mocked_receive_message_proof_call_builder { +macro_rules! generate_receive_message_proof_call_builder { ($pipeline:ident, $mocked_builder:ident, $bridge_messages:path, $receive_messages_proof:path) => { pub struct $mocked_builder; @@ -335,12 +335,14 @@ macro_rules! generate_mocked_receive_message_proof_call_builder { ) -> relay_substrate_client::CallOf< <$pipeline as $crate::messages_lane::SubstrateMessageLane>::TargetChain > { - $bridge_messages($receive_messages_proof( - relayer_id_at_source, - proof.1, - messages_count, - dispatch_weight, - )) + bp_runtime::paste::item! { + $bridge_messages($receive_messages_proof { + relayer_id_at_bridged_chain: relayer_id_at_source, + proof: proof.1, + messages_count: messages_count, + dispatch_weight: dispatch_weight, + }) + } } } }; @@ -409,7 +411,7 @@ where /// the variant for the `receive_messages_delivery_proof` call within that first option. #[rustfmt::skip] #[macro_export] -macro_rules! generate_mocked_receive_message_delivery_proof_call_builder { +macro_rules! generate_receive_message_delivery_proof_call_builder { ($pipeline:ident, $mocked_builder:ident, $bridge_messages:path, $receive_messages_delivery_proof:path) => { pub struct $mocked_builder; @@ -424,7 +426,12 @@ macro_rules! generate_mocked_receive_message_delivery_proof_call_builder { ) -> relay_substrate_client::CallOf< <$pipeline as $crate::messages_lane::SubstrateMessageLane>::SourceChain > { - $bridge_messages($receive_messages_delivery_proof(proof.1, proof.0)) + bp_runtime::paste::item! { + $bridge_messages($receive_messages_delivery_proof { + proof: proof.1, + relayers_state: proof.0 + }) + } } } }; @@ -480,10 +487,14 @@ mod tests { pallet_bridge_messages::weights::BridgeWeight; #[test] - fn select_delivery_transaction_limits_works() { + fn select_delivery_transaction_limits_is_sane() { + // we want to check the `proof_size` component here too. But for Rialto and Millau + // it is set to `u64::MAX` (as for Polkadot and other relay/standalone chains). + // So let's use RialtoParachain limits here - it has `proof_size` limit as all + // Cumulus-based parachains do. let (max_count, max_weight) = select_delivery_transaction_limits::( - bp_millau::Millau::max_extrinsic_weight(), + bp_rialto_parachain::RialtoParachain::max_extrinsic_weight(), bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, ); assert_eq!( @@ -493,9 +504,7 @@ mod tests { // i.e. weight reserved for messages dispatch allows dispatch of non-trivial messages. // // Any significant change in this values should attract additional attention. - // - // TODO: https://github.com/paritytech/parity-bridges-common/issues/1543 - remove `set_proof_size` - (1024, Weight::from_ref_time(216_600_684_000).set_proof_size(217)), + (1024, Weight::from_parts(866_600_106_667, 2_271_915)), ); } } diff --git a/relays/lib-substrate-relay/src/messages_source.rs b/relays/lib-substrate-relay/src/messages_source.rs index e86f7abdd64..8d2ac5874fe 100644 --- a/relays/lib-substrate-relay/src/messages_source.rs +++ b/relays/lib-substrate-relay/src/messages_source.rs @@ -421,14 +421,15 @@ where .await?; // read actual header, matching the `peer_on_self_best_finalized_id` from the peer chain - let actual_peer_on_self_best_finalized_id = match peer_client { - Some(peer_client) => { - let actual_peer_on_self_best_finalized = - peer_client.header_by_number(peer_on_self_best_finalized_id.number()).await?; - actual_peer_on_self_best_finalized.id() - }, - None => peer_on_self_best_finalized_id, - }; + let actual_peer_on_self_best_finalized_id = + match (peer_client, peer_on_self_best_finalized_id.as_ref()) { + (Some(peer_client), Some(peer_on_self_best_finalized_id)) => { + let actual_peer_on_self_best_finalized = + peer_client.header_by_number(peer_on_self_best_finalized_id.number()).await?; + Some(actual_peer_on_self_best_finalized.id()) + }, + _ => peer_on_self_best_finalized_id, + }; Ok(ClientState { best_self: self_best_id, @@ -444,7 +445,7 @@ where pub async fn best_finalized_peer_header_at_self( self_client: &Client, at_self_hash: HashOf, -) -> Result, SubstrateError> +) -> Result>, SubstrateError> where SelfChain: Chain, PeerChain: Chain, @@ -456,8 +457,7 @@ where (), Some(at_self_hash), ) - .await? - .ok_or(SubstrateError::BridgePalletIsNotInitialized) + .await } fn validate_out_msgs_details( diff --git a/relays/lib-substrate-relay/src/on_demand/parachains.rs b/relays/lib-substrate-relay/src/on_demand/parachains.rs index 6be3fcb9886..b1270108c80 100644 --- a/relays/lib-substrate-relay/src/on_demand/parachains.rs +++ b/relays/lib-substrate-relay/src/on_demand/parachains.rs @@ -40,8 +40,8 @@ use parachains_relay::parachains_loop::{ AvailableHeader, ParachainSyncParams, SourceClient, TargetClient, }; use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, Error as SubstrateError, - HashOf, HeaderIdOf, ParachainBase, ANCIENT_BLOCK_THRESHOLD, + is_ancient_block, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, + Error as SubstrateError, HashOf, HeaderIdOf, ParachainBase, }; use relay_utils::{ metrics::MetricsParams, relay_loop::Client as RelayClient, BlockNumberBase, FailedClient, @@ -422,11 +422,14 @@ struct RelayData { pub para_header_at_source: Option>, /// Parachain header, that is available at the source relay chain at `relay_header_at_target` /// block. + /// + /// May be `None` if there's no `relay_header_at_target` yet, or if the + /// `relay_header_at_target` is too old and we think its state has been pruned. pub para_header_at_relay_header_at_target: Option>, /// Relay header number at the source chain. pub relay_header_at_source: RelayNumber, /// Relay header number at the target chain. - pub relay_header_at_target: RelayNumber, + pub relay_header_at_target: Option, } /// Read required data from source and target clients. @@ -473,12 +476,12 @@ where P::SourceParachain, >(target.client(), best_target_block_hash) .await; - // if there are no parachain heads at the target (`BridgePalletIsNotInitialized`), we'll need - // to submit at least one. Otherwise the pallet will be treated as uninitialized and messages + // if there are no parachain heads at the target (`NoParachainHeadAtTarget`), we'll need to + // submit at least one. Otherwise the pallet will be treated as uninitialized and messages // sync will stall. let para_header_at_target = match para_header_at_target { - Ok(para_header_at_target) => Some(para_header_at_target.0), - Err(SubstrateError::BridgePalletIsNotInitialized) => None, + Ok(Some(para_header_at_target)) => Some(para_header_at_target.0), + Ok(None) => None, Err(e) => return Err(map_target_err(e)), }; @@ -501,17 +504,34 @@ where .await .map_err(map_target_err)?; - let para_header_at_relay_header_at_target = source - .on_chain_para_head_id(relay_header_at_target, P::SourceParachain::PARACHAIN_ID.into()) - .await - .map_err(map_source_err)?; + // if relay header at target is too old then its state may already be discarded at the source + // => just use `None` in this case + // + // the same is for case when there's no relay header at target at all + let available_relay_header_at_target = + relay_header_at_target.filter(|relay_header_at_target| { + !is_ancient_block(relay_header_at_target.number(), relay_header_at_source) + }); + let para_header_at_relay_header_at_target = + if let Some(available_relay_header_at_target) = available_relay_header_at_target { + source + .on_chain_para_head_id( + available_relay_header_at_target, + P::SourceParachain::PARACHAIN_ID.into(), + ) + .await + .map_err(map_source_err)? + } else { + None + }; Ok(RelayData { required_para_header: required_header_number, para_header_at_target, para_header_at_source, relay_header_at_source, - relay_header_at_target: relay_header_at_target.0, + relay_header_at_target: relay_header_at_target + .map(|relay_header_at_target| relay_header_at_target.0), para_header_at_relay_header_at_target, }) } @@ -519,25 +539,35 @@ where /// Select relay and parachain headers that need to be relayed. fn select_headers_to_relay( data: &RelayData, - mut state: RelayState, + state: RelayState, ) -> RelayState where ParaHash: Clone, ParaNumber: Copy + PartialOrd + Zero, RelayNumber: Copy + Debug + Ord, { + // we can't do anything until **relay chain** bridge GRANDPA pallet is not initialized at the + // target chain + let relay_header_at_target = match data.relay_header_at_target { + Some(relay_header_at_target) => relay_header_at_target, + None => return RelayState::Idle, + }; + // Process the `RelayingRelayHeader` state. if let &RelayState::RelayingRelayHeader(relay_header_number) = &state { - if data.relay_header_at_target < relay_header_number { + if relay_header_at_target < relay_header_number { // The required relay header hasn't yet been relayed. Ask / wait for it. return state } // We may switch to `RelayingParaHeader` if parachain head is available. - state = data - .para_header_at_relay_header_at_target - .clone() - .map_or(RelayState::Idle, RelayState::RelayingParaHeader); + if let Some(para_header_at_relay_header_at_target) = + data.para_header_at_relay_header_at_target.as_ref() + { + return RelayState::RelayingParaHeader(para_header_at_relay_header_at_target.clone()) + } + + // else use the regular process - e.g. we may require to deliver new relay header first } // Process the `RelayingParaHeader` state. @@ -576,7 +606,7 @@ where // its ancestor // we need relay chain header first - if data.relay_header_at_target < data.relay_header_at_source { + if relay_header_at_target < data.relay_header_at_source { return RelayState::RelayingRelayHeader(data.relay_header_at_source) } @@ -631,7 +661,8 @@ impl<'a, P: SubstrateParachainsPipeline> None, ) .await? - .best_finalized_peer_at_best_self) + .best_finalized_peer_at_best_self + .ok_or(SubstrateError::BridgePalletIsNotInitialized)?) } async fn best_finalized_para_block_at_source( @@ -674,26 +705,28 @@ where required_parachain_header.unique_saturated_into(), ))?; + // we don't require source node to be archive, so we can't craft storage proofs using + // ancient headers. So if the `best_finalized_relay_block_at_target` is too ancient, we + // can't craft storage proofs using it + let may_use_state_at_best_finalized_relay_block_at_target = !is_ancient_block( + best_finalized_relay_block_at_target.number(), + best_finalized_relay_block_at_source.number(), + ); + // now let's check if `required_header` may be proved using // `best_finalized_relay_block_at_target` - let selection = env - .best_finalized_para_block_at_source(best_finalized_relay_block_at_target) - .await? - .filter(|best_finalized_para_block_at_target| { - best_finalized_para_block_at_target.number() >= required_parachain_header - }) - .map(|best_finalized_para_block_at_target| { - (false, best_finalized_relay_block_at_target, best_finalized_para_block_at_target) - }) - // we don't require source node to be archive, so we can't craft storage proofs using - // ancient headers. So if the `best_finalized_relay_block_at_target` is too ancient, we - // can't craft storage proofs using it - .filter(|(_, selected_relay_block, _)| { - let difference = best_finalized_relay_block_at_source - .number() - .saturating_sub(selected_relay_block.number()); - difference <= RBN::from(ANCIENT_BLOCK_THRESHOLD) - }); + let selection = if may_use_state_at_best_finalized_relay_block_at_target { + env.best_finalized_para_block_at_source(best_finalized_relay_block_at_target) + .await? + .filter(|best_finalized_para_block_at_target| { + best_finalized_para_block_at_target.number() >= required_parachain_header + }) + .map(|best_finalized_para_block_at_target| { + (false, best_finalized_relay_block_at_target, best_finalized_para_block_at_target) + }) + } else { + None + }; Ok(selection.unwrap_or(( true, @@ -715,7 +748,7 @@ mod tests { para_header_at_target: Some(50), para_header_at_source: Some(HeaderId(110, 110)), relay_header_at_source: 800, - relay_header_at_target: 700, + relay_header_at_target: Some(700), para_header_at_relay_header_at_target: Some(HeaderId(100, 100)), }, RelayState::RelayingRelayHeader(750), @@ -733,7 +766,7 @@ mod tests { para_header_at_target: Some(50), para_header_at_source: Some(HeaderId(110, 110)), relay_header_at_source: 800, - relay_header_at_target: 750, + relay_header_at_target: Some(750), para_header_at_relay_header_at_target: Some(HeaderId(100, 100)), }, RelayState::RelayingRelayHeader(750), @@ -751,7 +784,7 @@ mod tests { para_header_at_target: Some(50), para_header_at_source: Some(HeaderId(110, 110)), relay_header_at_source: 800, - relay_header_at_target: 780, + relay_header_at_target: Some(780), para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), }, RelayState::RelayingRelayHeader(750), @@ -768,7 +801,7 @@ mod tests { para_header_at_target: Some(50), para_header_at_source: Some(HeaderId(110, 110)), relay_header_at_source: 800, - relay_header_at_target: 780, + relay_header_at_target: Some(780), para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), }, RelayState::RelayingParaHeader(HeaderId(105, 105)), @@ -786,7 +819,7 @@ mod tests { para_header_at_target: Some(105), para_header_at_source: Some(HeaderId(110, 110)), relay_header_at_source: 800, - relay_header_at_target: 780, + relay_header_at_target: Some(780), para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), }, RelayState::Idle, @@ -804,7 +837,7 @@ mod tests { para_header_at_target: Some(105), para_header_at_source: None, relay_header_at_source: 800, - relay_header_at_target: 780, + relay_header_at_target: Some(780), para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), }, RelayState::Idle, @@ -822,7 +855,7 @@ mod tests { para_header_at_target: Some(105), para_header_at_source: Some(HeaderId(110, 110)), relay_header_at_source: 800, - relay_header_at_target: 780, + relay_header_at_target: Some(780), para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), }, RelayState::Idle, @@ -840,7 +873,7 @@ mod tests { para_header_at_target: Some(105), para_header_at_source: Some(HeaderId(125, 125)), relay_header_at_source: 800, - relay_header_at_target: 780, + relay_header_at_target: Some(780), para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), }, RelayState::Idle, @@ -858,7 +891,7 @@ mod tests { para_header_at_target: Some(105), para_header_at_source: Some(HeaderId(125, 125)), relay_header_at_source: 800, - relay_header_at_target: 800, + relay_header_at_target: Some(800), para_header_at_relay_header_at_target: Some(HeaderId(125, 125)), }, RelayState::Idle, @@ -876,7 +909,7 @@ mod tests { para_header_at_target: Some(105), para_header_at_source: None, relay_header_at_source: 800, - relay_header_at_target: 800, + relay_header_at_target: Some(800), para_header_at_relay_header_at_target: None, }, RelayState::RelayingRelayHeader(800), @@ -894,7 +927,7 @@ mod tests { para_header_at_target: None, para_header_at_source: Some(HeaderId(125, 125)), relay_header_at_source: 800, - relay_header_at_target: 800, + relay_header_at_target: Some(800), para_header_at_relay_header_at_target: Some(HeaderId(125, 125)), }, RelayState::Idle, @@ -912,7 +945,7 @@ mod tests { para_header_at_target: None, para_header_at_source: Some(HeaderId(125, 125)), relay_header_at_source: 800, - relay_header_at_target: 700, + relay_header_at_target: Some(700), para_header_at_relay_header_at_target: Some(HeaderId(125, 125)), }, RelayState::Idle, diff --git a/relays/lib-substrate-relay/src/parachains/source.rs b/relays/lib-substrate-relay/src/parachains/source.rs index afacd18e292..90776902dd6 100644 --- a/relays/lib-substrate-relay/src/parachains/source.rs +++ b/relays/lib-substrate-relay/src/parachains/source.rs @@ -29,7 +29,8 @@ use parachains_relay::{ parachains_loop_metrics::ParachainsLoopMetrics, }; use relay_substrate_client::{ - Chain, Client, Error as SubstrateError, HeaderIdOf, HeaderOf, ParachainBase, RelayChain, + is_ancient_block, Chain, Client, Error as SubstrateError, HeaderIdOf, HeaderOf, ParachainBase, + RelayChain, }; use relay_utils::relay_loop::Client as RelayClient; @@ -115,6 +116,14 @@ where ))) } + // if requested relay header is ancient, then we don't even want to try to read the + // parachain head - we simply return `Unavailable` + let best_block_number = self.client.best_finalized_header_number().await?; + if is_ancient_block(at_block.number(), best_block_number) { + return Ok(AvailableHeader::Unavailable) + } + + // else - try to read head from the source client let mut para_head_id = AvailableHeader::Missing; if let Some(on_chain_para_head_id) = self.on_chain_para_head_id(at_block, para_id).await? { // Never return head that is larger than requested. This way we'll never sync diff --git a/relays/lib-substrate-relay/src/parachains/target.rs b/relays/lib-substrate-relay/src/parachains/target.rs index 5e765f10ff5..7f6cdf8f925 100644 --- a/relays/lib-substrate-relay/src/parachains/target.rs +++ b/relays/lib-substrate-relay/src/parachains/target.rs @@ -33,7 +33,7 @@ use parachains_relay::{ }; use relay_substrate_client::{ AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, - HeaderIdOf, RelayChain, TransactionEra, TransactionTracker, UnsignedTransaction, + HeaderIdOf, ParachainBase, RelayChain, TransactionEra, TransactionTracker, UnsignedTransaction, }; use relay_utils::{relay_loop::Client as RelayClient, HeaderId}; use sp_core::{Bytes, Pair}; @@ -110,7 +110,10 @@ where ) .map_err(SubstrateError::ResponseParseFailed)? .map(Ok) - .unwrap_or(Err(SubstrateError::BridgePalletIsNotInitialized)) + .unwrap_or(Err(SubstrateError::NoParachainHeadAtTarget( + P::SourceParachain::PARACHAIN_ID, + P::TargetChain::NAME.into(), + ))) } async fn parachain_head( diff --git a/relays/messages/Cargo.toml b/relays/messages/Cargo.toml index e4564dac15f..c4d2459e5ca 100644 --- a/relays/messages/Cargo.toml +++ b/relays/messages/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] async-std = { version = "1.6.5", features = ["attributes"] } async-trait = "0.1" -futures = "0.3.5" +futures = "0.3.26" hex = "0.4" log = "0.4.17" num-traits = "0.2" diff --git a/relays/messages/src/message_lane_loop.rs b/relays/messages/src/message_lane_loop.rs index e26849bbb9b..5e5085bbd5d 100644 --- a/relays/messages/src/message_lane_loop.rs +++ b/relays/messages/src/message_lane_loop.rs @@ -249,10 +249,13 @@ pub struct ClientState { pub best_finalized_self: SelfHeaderId, /// Best finalized header id of the peer chain read at the best block of this chain (at /// `best_finalized_self`). - pub best_finalized_peer_at_best_self: PeerHeaderId, + /// + /// It may be `None` e,g. if peer is a parachain and we haven't yet relayed any parachain + /// heads. + pub best_finalized_peer_at_best_self: Option, /// Header id of the peer chain with the number, matching the /// `best_finalized_peer_at_best_self`. - pub actual_best_finalized_peer_at_best_self: PeerHeaderId, + pub actual_best_finalized_peer_at_best_self: Option, } /// State of source client in one-way message lane. @@ -973,15 +976,15 @@ pub(crate) mod tests { source_state: ClientState { best_self: HeaderId(0, 0), best_finalized_self: HeaderId(0, 0), - best_finalized_peer_at_best_self: HeaderId(0, 0), - actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), }, source_latest_generated_nonce: 1, target_state: ClientState { best_self: HeaderId(0, 0), best_finalized_self: HeaderId(0, 0), - best_finalized_peer_at_best_self: HeaderId(0, 0), - actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), }, target_latest_received_nonce: 0, ..Default::default() @@ -997,11 +1000,11 @@ pub(crate) mod tests { if data.is_target_reconnected { data.is_target_fails = false; } - if data.target_state.best_finalized_peer_at_best_self.0 < 10 { - data.target_state.best_finalized_peer_at_best_self = HeaderId( - data.target_state.best_finalized_peer_at_best_self.0 + 1, - data.target_state.best_finalized_peer_at_best_self.0 + 1, - ); + if data.target_state.best_finalized_peer_at_best_self.unwrap().0 < 10 { + data.target_state.best_finalized_peer_at_best_self = Some(HeaderId( + data.target_state.best_finalized_peer_at_best_self.unwrap().0 + 1, + data.target_state.best_finalized_peer_at_best_self.unwrap().0 + 1, + )); } if !data.submitted_messages_proofs.is_empty() { exit_sender.unbounded_send(()).unwrap(); @@ -1025,16 +1028,16 @@ pub(crate) mod tests { source_state: ClientState { best_self: HeaderId(0, 0), best_finalized_self: HeaderId(0, 0), - best_finalized_peer_at_best_self: HeaderId(0, 0), - actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), }, source_latest_generated_nonce: 1, source_tracked_transaction_status: TrackedTransactionStatus::Lost, target_state: ClientState { best_self: HeaderId(0, 0), best_finalized_self: HeaderId(0, 0), - best_finalized_peer_at_best_self: HeaderId(0, 0), - actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), }, target_latest_received_nonce: 0, target_tracked_transaction_status: TrackedTransactionStatus::Lost, @@ -1076,15 +1079,15 @@ pub(crate) mod tests { source_state: ClientState { best_self: HeaderId(0, 0), best_finalized_self: HeaderId(0, 0), - best_finalized_peer_at_best_self: HeaderId(0, 0), - actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), }, source_latest_generated_nonce: 1, target_state: ClientState { best_self: HeaderId(0, 0), best_finalized_self: HeaderId(0, 0), - best_finalized_peer_at_best_self: HeaderId(0, 0), - actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), }, target_latest_received_nonce: 0, ..Default::default() @@ -1096,8 +1099,11 @@ pub(crate) mod tests { data.source_state.best_finalized_self = data.source_state.best_self; // syncing target headers -> source chain if let Some(last_requirement) = data.target_to_source_header_requirements.last() { - if *last_requirement != data.source_state.best_finalized_peer_at_best_self { - data.source_state.best_finalized_peer_at_best_self = *last_requirement; + if *last_requirement != + data.source_state.best_finalized_peer_at_best_self.unwrap() + { + data.source_state.best_finalized_peer_at_best_self = + Some(*last_requirement); } } }), @@ -1116,8 +1122,11 @@ pub(crate) mod tests { data.target_state.best_finalized_self = data.target_state.best_self; // syncing source headers -> target chain if let Some(last_requirement) = data.source_to_target_header_requirements.last() { - if *last_requirement != data.target_state.best_finalized_peer_at_best_self { - data.target_state.best_finalized_peer_at_best_self = *last_requirement; + if *last_requirement != + data.target_state.best_finalized_peer_at_best_self.unwrap() + { + data.target_state.best_finalized_peer_at_best_self = + Some(*last_requirement); } } // if source has received all messages receiving confirmations => stop @@ -1150,15 +1159,15 @@ pub(crate) mod tests { source_state: ClientState { best_self: HeaderId(10, 10), best_finalized_self: HeaderId(10, 10), - best_finalized_peer_at_best_self: HeaderId(0, 0), - actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), }, source_latest_generated_nonce: 10, target_state: ClientState { best_self: HeaderId(0, 0), best_finalized_self: HeaderId(0, 0), - best_finalized_peer_at_best_self: HeaderId(0, 0), - actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), }, target_latest_received_nonce: 0, ..Default::default() @@ -1171,15 +1180,18 @@ pub(crate) mod tests { // headers relay must only be started when we need new target headers at source node if data.target_to_source_header_required.is_some() { assert!( - data.source_state.best_finalized_peer_at_best_self.0 < + data.source_state.best_finalized_peer_at_best_self.unwrap().0 < data.target_state.best_self.0 ); data.target_to_source_header_required = None; } // syncing target headers -> source chain if let Some(last_requirement) = data.target_to_source_header_requirements.last() { - if *last_requirement != data.source_state.best_finalized_peer_at_best_self { - data.source_state.best_finalized_peer_at_best_self = *last_requirement; + if *last_requirement != + data.source_state.best_finalized_peer_at_best_self.unwrap() + { + data.source_state.best_finalized_peer_at_best_self = + Some(*last_requirement); } } }), @@ -1192,15 +1204,18 @@ pub(crate) mod tests { // headers relay must only be started when we need new source headers at target node if data.source_to_target_header_required.is_some() { assert!( - data.target_state.best_finalized_peer_at_best_self.0 < + data.target_state.best_finalized_peer_at_best_self.unwrap().0 < data.source_state.best_self.0 ); data.source_to_target_header_required = None; } // syncing source headers -> target chain if let Some(last_requirement) = data.source_to_target_header_requirements.last() { - if *last_requirement != data.target_state.best_finalized_peer_at_best_self { - data.target_state.best_finalized_peer_at_best_self = *last_requirement; + if *last_requirement != + data.target_state.best_finalized_peer_at_best_self.unwrap() + { + data.target_state.best_finalized_peer_at_best_self = + Some(*last_requirement); } } // if source has received all messages receiving confirmations => stop @@ -1233,15 +1248,15 @@ pub(crate) mod tests { source_state: ClientState { best_self: HeaderId(10, 10), best_finalized_self: HeaderId(10, 10), - best_finalized_peer_at_best_self: HeaderId(0, 0), - actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), }, source_latest_generated_nonce: 10, target_state: ClientState { best_self: HeaderId(0, 0), best_finalized_self: HeaderId(0, 0), - best_finalized_peer_at_best_self: HeaderId(0, 0), - actual_best_finalized_peer_at_best_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), }, target_latest_received_nonce: 0, ..Default::default() diff --git a/relays/messages/src/message_race_loop.rs b/relays/messages/src/message_race_loop.rs index deac71e5b6e..2988ab231d9 100644 --- a/relays/messages/src/message_race_loop.rs +++ b/relays/messages/src/message_race_loop.rs @@ -308,7 +308,7 @@ pub async fn run, TC: TargetClient

>( target_best_nonces_required = true; race_state.best_target_header_id = Some(target_state.best_self); race_state.best_finalized_source_header_id_at_best_target - = Some(target_state.best_finalized_peer_at_best_self); + = target_state.best_finalized_peer_at_best_self; } let is_target_finalized_state_updated = race_state.best_finalized_target_header_id.as_ref() diff --git a/relays/messages/src/metrics.rs b/relays/messages/src/metrics.rs index ace4264cacc..20c6986b474 100644 --- a/relays/messages/src/metrics.rs +++ b/relays/messages/src/metrics.rs @@ -66,24 +66,38 @@ impl MessageLaneLoopMetrics { pub fn update_source_state(&self, source_client_state: SourceClientState

) { self.source_to_target_finality_metrics .update_best_block_at_source(source_client_state.best_self.0); - self.target_to_source_finality_metrics - .update_best_block_at_target(source_client_state.best_finalized_peer_at_best_self.0); - self.target_to_source_finality_metrics.update_using_same_fork( - source_client_state.best_finalized_peer_at_best_self.1 == - source_client_state.actual_best_finalized_peer_at_best_self.1, - ); + if let Some(best_finalized_peer_at_best_self) = + source_client_state.best_finalized_peer_at_best_self + { + self.target_to_source_finality_metrics + .update_best_block_at_target(best_finalized_peer_at_best_self.0); + if let Some(actual_best_finalized_peer_at_best_self) = + source_client_state.actual_best_finalized_peer_at_best_self + { + self.target_to_source_finality_metrics.update_using_same_fork( + best_finalized_peer_at_best_self.1 == actual_best_finalized_peer_at_best_self.1, + ); + } + } } /// Update target client state metrics. pub fn update_target_state(&self, target_client_state: TargetClientState

) { self.target_to_source_finality_metrics .update_best_block_at_source(target_client_state.best_self.0); - self.source_to_target_finality_metrics - .update_best_block_at_target(target_client_state.best_finalized_peer_at_best_self.0); - self.source_to_target_finality_metrics.update_using_same_fork( - target_client_state.best_finalized_peer_at_best_self.1 == - target_client_state.actual_best_finalized_peer_at_best_self.1, - ); + if let Some(best_finalized_peer_at_best_self) = + target_client_state.best_finalized_peer_at_best_self + { + self.source_to_target_finality_metrics + .update_best_block_at_target(best_finalized_peer_at_best_self.0); + if let Some(actual_best_finalized_peer_at_best_self) = + target_client_state.actual_best_finalized_peer_at_best_self + { + self.source_to_target_finality_metrics.update_using_same_fork( + best_finalized_peer_at_best_self.1 == actual_best_finalized_peer_at_best_self.1, + ); + } + } } /// Update latest generated nonce at source. diff --git a/relays/parachains/Cargo.toml b/relays/parachains/Cargo.toml index 1909630a6fb..093fc0b94e3 100644 --- a/relays/parachains/Cargo.toml +++ b/relays/parachains/Cargo.toml @@ -7,8 +7,8 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] async-std = "1.6.5" -async-trait = "0.1.62" -futures = "0.3.5" +async-trait = "0.1.64" +futures = "0.3.26" log = "0.4.17" relay-utils = { path = "../utils" } diff --git a/relays/parachains/README.md b/relays/parachains/README.md index 6cb68f2209f..9093dc617dc 100644 --- a/relays/parachains/README.md +++ b/relays/parachains/README.md @@ -24,7 +24,7 @@ finality delivery transaction to the target node. The main entrypoint for the crate is the [`run` function](./src/parachains_loop.rs), which takes source and target clients and [`ParachainSyncParams`](./src/parachains_loop.rs) parameters. The most imporant parameter is the -`parachains` - it it the set of parachains, which relay tracks and updates. The other important parameter that +`parachains` - it is the set of parachains, which relay tracks and updates. The other important parameter that may affect the relay operational costs is the `strategy`. If it is set to `Any`, then the finality delivery transaction is submitted if at least one of tracked parachain heads is updated. The other option is `All`. Then the relay waits until all tracked parachain heads are updated and submits them all in a single finality delivery diff --git a/relays/parachains/src/parachains_loop.rs b/relays/parachains/src/parachains_loop.rs index 3ef9a7f7a73..7b62e72ca29 100644 --- a/relays/parachains/src/parachains_loop.rs +++ b/relays/parachains/src/parachains_loop.rs @@ -243,7 +243,7 @@ where P::SourceChain::NAME, e, ); - return Err(FailedClient::Target) + return Err(FailedClient::Source) }, } @@ -844,7 +844,7 @@ mod tests { None, futures::future::pending(), )), - Err(FailedClient::Target), + Err(FailedClient::Source), ); } diff --git a/relays/utils/Cargo.toml b/relays/utils/Cargo.toml index 0ad6168bb84..57eeb7fbc8c 100644 --- a/relays/utils/Cargo.toml +++ b/relays/utils/Cargo.toml @@ -13,14 +13,14 @@ async-trait = "0.1" backoff = "0.4" isahc = "1.2" env_logger = "0.10.0" -futures = "0.3.5" +futures = "0.3.26" jsonpath_lib = "0.3" log = "0.4.17" num-traits = "0.2" serde_json = "1.0" sysinfo = "0.27" time = { version = "0.3", features = ["formatting", "local-offset", "std"] } -tokio = { version = "1.24", features = ["rt"] } +tokio = { version = "1.25", features = ["rt"] } thiserror = "1.0.26" # Bridge dependencies diff --git a/scripts/verify-partial-repo-build.sh b/scripts/verify-pallets-build.sh similarity index 76% rename from scripts/verify-partial-repo-build.sh rename to scripts/verify-pallets-build.sh index d7de84d620f..e1393688c17 100755 --- a/scripts/verify-partial-repo-build.sh +++ b/scripts/verify-pallets-build.sh @@ -8,6 +8,35 @@ # - modules/relayers; # - everything required from primitives folder. +set -eux + +# show CLI help +function show_help() { + set +x + echo " " + echo Error: $1 + echo "Usage:" + echo " ./scripts/verify-pallets-build.sh Exit with code 0 if pallets code is well decoupled from the other code in the repo" + echo "Options:" + echo " --no-revert Leaves only runtime code on exit" + exit 1 +} + +# parse CLI args +NO_REVERT= +for i in "$@" +do + case $i in + --no-revert) + NO_REVERT=true + shift + ;; + *) + show_help "Unknown option: $i" + ;; + esac +done + # the script is able to work only on clean git copy [[ -z "$(git status --porcelain)" ]] || { echo >&2 "The git copy must be clean"; exit 1; } @@ -18,8 +47,7 @@ BRIDGES_FOLDER="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && p # let's leave repository/subtree in its original (clean) state if something fails below function revert_to_clean_state { - echo "Reverting to clean state..." - git reset --hard + [[ ! -z "${NO_REVERT}" ]] || { echo "Reverting to clean state..."; git checkout .; } } trap revert_to_clean_state EXIT @@ -40,6 +68,7 @@ rm -rf $BRIDGES_FOLDER/primitives/chain-rialto rm -rf $BRIDGES_FOLDER/primitives/chain-rialto-parachain rm -rf $BRIDGES_FOLDER/primitives/chain-westend rm -rf $BRIDGES_FOLDER/relays +rm -rf $BRIDGES_FOLDER/tools rm -f $BRIDGES_FOLDER/.dockerignore rm -f $BRIDGES_FOLDER/.gitlab-ci.yml rm -f $BRIDGES_FOLDER/Cargo.lock @@ -48,17 +77,18 @@ rm -f $BRIDGES_FOLDER/ci.Dockerfile rm -f $BRIDGES_FOLDER/Dockerfile # let's fix Cargo.toml a bit (it'll be helpful if we are in the bridges repo) +if [[ ! -f "Cargo.toml" ]]; then + cat > Cargo.toml <<-CARGO_TOML + [workspace] + resolver = "2" -cat > $BRIDGES_FOLDER/Cargo.toml <<-CARGO_TOML -[workspace] -resolver = "2" - -members = [ - "bin/runtime-common", - "modules/*", - "primitives/*", -] -CARGO_TOML + members = [ + "bin/runtime-common", + "modules/*", + "primitives/*", + ] + CARGO_TOML +fi # let's test if everything we need compiles diff --git a/tools/runtime-codegen/Cargo.lock b/tools/runtime-codegen/Cargo.lock new file mode 100644 index 00000000000..be36a2ccd21 --- /dev/null +++ b/tools/runtime-codegen/Cargo.lock @@ -0,0 +1,3888 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.8", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "async-lock" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" +dependencies = [ + "event-listener", + "futures-lite", +] + +[[package]] +name = "async-trait" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +dependencies = [ + "arrayvec 0.4.12", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "num-integer", + "num-traits", + "winapi", +] + +[[package]] +name = "clap" +version = "4.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.6", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.6", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array 0.14.6", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "cxx" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "darling" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "dyn-clonable" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +dependencies = [ + "dyn-clonable-impl", + "dyn-clone", +] + +[[package]] +name = "dyn-clonable-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dyn-clone" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "environmental" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "eyre" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "frame-metadata" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" +dependencies = [ + "cfg-if", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-executor" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" + +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" + +[[package]] +name = "futures-task" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +dependencies = [ + "gloo-timers", + "send_wrapper", +] + +[[package]] +name = "futures-util" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec" + +[[package]] +name = "gloo-net" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "h2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hash-db" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array 0.14.6", + "hmac 0.8.1", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "webpki-roots", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +dependencies = [ + "hermit-abi 0.3.0", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "jobserver" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpsee" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" +dependencies = [ + "jsonrpsee-client-transport 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpsee-core 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpsee-http-client 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpsee-types 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "jsonrpsee" +version = "0.16.2" +source = "git+https://github.com/paritytech/jsonrpsee#f6f4a0845d7c94dfb9befe625b2a779b3a40a127" +dependencies = [ + "jsonrpsee-client-transport 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-http-client 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-wasm-client", + "jsonrpsee-ws-client", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965de52763f2004bc91ac5bcec504192440f0b568a5d621c59d9dbd6f886c3fb" +dependencies = [ + "futures-util", + "http", + "jsonrpsee-core 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpsee-types 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", + "webpki-roots", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.16.2" +source = "git+https://github.com/paritytech/jsonrpsee#f6f4a0845d7c94dfb9befe625b2a779b3a40a127" +dependencies = [ + "anyhow", + "futures-channel", + "futures-timer", + "futures-util", + "gloo-net", + "http", + "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", + "webpki-roots", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" +dependencies = [ + "anyhow", + "async-lock", + "async-trait", + "beef", + "futures-channel", + "futures-timer", + "futures-util", + "hyper", + "jsonrpsee-types 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.16.2" +source = "git+https://github.com/paritytech/jsonrpsee#f6f4a0845d7c94dfb9befe625b2a779b3a40a127" +dependencies = [ + "anyhow", + "async-lock", + "async-trait", + "beef", + "futures-timer", + "futures-util", + "hyper", + "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "wasm-bindgen-futures", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls", + "jsonrpsee-core 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpsee-types 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.16.2" +source = "git+https://github.com/paritytech/jsonrpsee#f6f4a0845d7c94dfb9befe625b2a779b3a40a127" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls", + "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd522fe1ce3702fd94812965d7bb7a3364b1c9aba743944c5a00529aae80f8c" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.16.2" +source = "git+https://github.com/paritytech/jsonrpsee#f6f4a0845d7c94dfb9befe625b2a779b3a40a127" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "jsonrpsee-wasm-client" +version = "0.16.2" +source = "git+https://github.com/paritytech/jsonrpsee#f6f4a0845d7c94dfb9befe625b2a779b3a40a127" +dependencies = [ + "jsonrpsee-client-transport 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.16.2" +source = "git+https://github.com/paritytech/jsonrpsee#f6f4a0845d7c94dfb9befe625b2a779b3a40a127" +dependencies = [ + "http", + "jsonrpsee-client-transport 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "libm" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memory-db" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6566c70c1016f525ced45d7b7f97730a2bafb037c788211d0c186ef5b2189f0a" +dependencies = [ + "hash-db", + "hashbrown", + "parity-util-mem", +] + +[[package]] +name = "memory_units" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" + +[[package]] +name = "merlin" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.42.0", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec 0.7.2", + "itoa", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "object" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "parity-scale-codec" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3840933452adf7b3b9145e27086a5a3376c619dca1a21b1e5a5af0d54979bed" +dependencies = [ + "arrayvec 0.7.2", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "parity-util-mem" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c32561d248d352148124f036cac253a644685a21dc9fea383eb4907d7bd35a8f" +dependencies = [ + "cfg-if", + "hashbrown", + "impl-trait-for-tuples", + "parity-util-mem-derive", + "parking_lot", + "primitive-types", + "winapi", +] + +[[package]] +name = "parity-util-mem-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" +dependencies = [ + "proc-macro2", + "syn", + "synstructure", +] + +[[package]] +name = "parity-wasm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "paste" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" + +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac 0.8.0", +] + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac 0.11.1", +] + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primitive-types" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-serde", + "scale-info", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.8", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c78fb8c9293bcd48ef6fce7b4ca950ceaf21210de6e105a883ee280c0f7b9ed" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "runtime-codegen" +version = "0.1.0" +dependencies = [ + "clap", + "color-eyre", + "parity-scale-codec", + "proc-macro2", + "subxt-codegen", + "syn", + "wasm-loader", + "wasm-testbed", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustix" +version = "0.36.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64 0.21.0", +] + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "sc-allocator" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "log", + "sp-core", + "sp-wasm-interface", + "thiserror", +] + +[[package]] +name = "sc-executor" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "lazy_static", + "lru", + "parity-scale-codec", + "parking_lot", + "sc-executor-common", + "sc-executor-wasmi", + "sp-api", + "sp-core", + "sp-core-hashing-proc-macro", + "sp-externalities", + "sp-io", + "sp-panic-handler", + "sp-runtime-interface", + "sp-tasks", + "sp-trie", + "sp-version", + "sp-wasm-interface", + "tracing", + "wasmi", +] + +[[package]] +name = "sc-executor-common" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "environmental", + "parity-scale-codec", + "sc-allocator", + "sp-maybe-compressed-blob", + "sp-sandbox", + "sp-serializer", + "sp-wasm-interface", + "thiserror", + "wasm-instrument", + "wasmi", +] + +[[package]] +name = "sc-executor-wasmi" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "log", + "parity-scale-codec", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface", + "sp-sandbox", + "sp-wasm-interface", + "wasmi", +] + +[[package]] +name = "scale-info" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608" +dependencies = [ + "bitvec", + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "serde", +] + +[[package]] +name = "scale-info-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "schnorrkel" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "curve25519-dalek 2.1.3", + "getrandom 0.1.16", + "merlin", + "rand 0.7.3", + "rand_core 0.5.1", + "sha2 0.8.2", + "subtle", + "zeroize", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "secp256k1" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c42e6f1735c5f00f51e43e28d6634141f2bcad10931b2609ddd74a86d751260" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" +dependencies = [ + "cc", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "soketto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +dependencies = [ + "base64 0.13.1", + "bytes", + "futures", + "httparse", + "log", + "rand 0.8.5", + "sha-1", +] + +[[package]] +name = "sp-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "sp-api-proc-macro", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std 4.0.0", + "sp-version", + "thiserror", +] + +[[package]] +name = "sp-api-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "blake2", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-application-crypto" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-std 4.0.0", +] + +[[package]] +name = "sp-arithmetic" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-debug-derive", + "sp-std 4.0.0", + "static_assertions", +] + +[[package]] +name = "sp-core" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "base58", + "bitflags", + "blake2-rfc", + "byteorder", + "dyn-clonable", + "ed25519-dalek", + "futures", + "hash-db", + "hash256-std-hasher", + "hex", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log", + "merlin", + "num-traits", + "parity-scale-codec", + "parity-util-mem", + "parking_lot", + "primitive-types", + "rand 0.7.3", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing 4.0.0", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std 4.0.0", + "sp-storage", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "wasmi", + "zeroize", +] + +[[package]] +name = "sp-core-hashing" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "blake2", + "byteorder", + "digest 0.10.6", + "sha2 0.10.6", + "sha3", + "sp-std 4.0.0", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc2d1947252b7a4e403b0a260f596920443742791765ec111daa2bbf98eff25" +dependencies = [ + "blake2", + "byteorder", + "digest 0.10.6", + "sha2 0.10.6", + "sha3", + "sp-std 6.0.0", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "proc-macro2", + "quote", + "sp-core-hashing 4.0.0", + "syn", +] + +[[package]] +name = "sp-debug-derive" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-externalities" +version = "0.12.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std 4.0.0", + "sp-storage", +] + +[[package]] +name = "sp-io" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "futures", + "hash-db", + "libsecp256k1", + "log", + "parity-scale-codec", + "parking_lot", + "secp256k1", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", + "sp-std 4.0.0", + "sp-tracing", + "sp-trie", + "sp-wasm-interface", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-keystore" +version = "0.12.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "async-trait", + "futures", + "merlin", + "parity-scale-codec", + "parking_lot", + "schnorrkel", + "sp-core", + "sp-externalities", + "thiserror", +] + +[[package]] +name = "sp-maybe-compressed-blob" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "thiserror", + "zstd", +] + +[[package]] +name = "sp-panic-handler" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + +[[package]] +name = "sp-runtime" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "parity-util-mem", + "paste", + "rand 0.7.3", + "scale-info", + "serde", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-std 4.0.0", +] + +[[package]] +name = "sp-runtime-interface" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std 4.0.0", + "sp-storage", + "sp-tracing", + "sp-wasm-interface", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-sandbox" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "log", + "parity-scale-codec", + "sp-core", + "sp-io", + "sp-std 4.0.0", + "sp-wasm-interface", + "wasmi", +] + +[[package]] +name = "sp-serializer" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "sp-state-machine" +version = "0.12.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "hash-db", + "log", + "num-traits", + "parity-scale-codec", + "parking_lot", + "rand 0.7.3", + "smallvec", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-std 4.0.0", + "sp-trie", + "thiserror", + "tracing", + "trie-root", +] + +[[package]] +name = "sp-std" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" + +[[package]] +name = "sp-std" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af0ee286f98455272f64ac5bb1384ff21ac029fbb669afbaf48477faff12760e" + +[[package]] +name = "sp-storage" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive", + "sp-std 4.0.0", +] + +[[package]] +name = "sp-tasks" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "log", + "sp-core", + "sp-externalities", + "sp-io", + "sp-runtime-interface", + "sp-std 4.0.0", +] + +[[package]] +name = "sp-tracing" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "parity-scale-codec", + "sp-std 4.0.0", + "tracing", + "tracing-core", + "tracing-subscriber 0.2.25", +] + +[[package]] +name = "sp-trie" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "hash-db", + "memory-db", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-std 4.0.0", + "thiserror", + "trie-db", + "trie-root", +] + +[[package]] +name = "sp-version" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "parity-wasm", + "scale-info", + "serde", + "sp-core-hashing-proc-macro", + "sp-runtime", + "sp-std 4.0.0", + "sp-version-proc-macro", + "thiserror", +] + +[[package]] +name = "sp-version-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "parity-scale-codec", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-wasm-interface" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +dependencies = [ + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std 4.0.0", + "wasmi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "ss58-registry" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40c020d72bc0a9c5660bb71e4a6fdef081493583062c474740a7d59f55f0e7b" +dependencies = [ + "Inflector", + "num-format", + "proc-macro2", + "quote", + "serde", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "substrate-bip39" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" +dependencies = [ + "hmac 0.11.0", + "pbkdf2 0.8.0", + "schnorrkel", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "substrate-runtime-proposal-hash" +version = "0.18.0" +source = "git+https://github.com/chevdor/subwasm?branch=master#d7e74ab5eb3f83773ad7c78fb0edd42fa33f5356" +dependencies = [ + "blake2", + "hex", + "parity-scale-codec", + "sp-core", + "sp-io", + "sp-runtime", + "sp-wasm-interface", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "subxt-codegen" +version = "0.26.0" +source = "git+https://github.com/paritytech/subxt?branch=master#20adb198e42d1a36d0c919737230423c17ca78e6" +dependencies = [ + "darling", + "frame-metadata", + "heck", + "hex", + "jsonrpsee 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec", + "proc-macro-error", + "proc-macro2", + "quote", + "scale-info", + "subxt-metadata", + "syn", + "tokio", +] + +[[package]] +name = "subxt-metadata" +version = "0.26.0" +source = "git+https://github.com/paritytech/subxt?branch=master#20adb198e42d1a36d0c919737230423c17ca78e6" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-core-hashing 6.0.0", +] + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tiny-bip39" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +dependencies = [ + "anyhow", + "hmac 0.8.1", + "once_cell", + "pbkdf2 0.4.0", + "rand 0.7.3", + "rustc-hash", + "sha2 0.9.9", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.42.0", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml_datetime" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" + +[[package]] +name = "toml_edit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" +dependencies = [ + "indexmap", + "nom8", + "toml_datetime", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber 0.3.16", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "trie-db" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32d034c0d3db64b43c31de38e945f15b40cd4ca6d2dcfc26d4798ce8de4ab83" +dependencies = [ + "hash-db", + "hashbrown", + "log", + "rustc-hex", + "smallvec", +] + +[[package]] +name = "trie-root" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891" +dependencies = [ + "hash-db", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "digest 0.10.6", + "rand 0.8.5", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "wasm-instrument" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "962e5b0401bbb6c887f54e69b8c496ea36f704df65db73e81fd5ff8dc3e63a9f" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasm-loader" +version = "0.18.0" +source = "git+https://github.com/chevdor/subwasm?branch=master#d7e74ab5eb3f83773ad7c78fb0edd42fa33f5356" +dependencies = [ + "hex", + "jsonrpsee 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "log", + "serde", + "sp-maybe-compressed-blob", + "tokio", +] + +[[package]] +name = "wasm-testbed" +version = "0.18.0" +source = "git+https://github.com/chevdor/subwasm?branch=master#d7e74ab5eb3f83773ad7c78fb0edd42fa33f5356" +dependencies = [ + "frame-metadata", + "hex", + "log", + "parity-scale-codec", + "sc-executor", + "sc-executor-common", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-version", + "sp-wasm-interface", + "substrate-runtime-proposal-hash", + "wasm-loader", +] + +[[package]] +name = "wasmi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca00c5147c319a8ec91ec1a0edbec31e566ce2c9cc93b3f9bb86a9efd0eb795d" +dependencies = [ + "downcast-rs", + "libc", + "libm", + "memory_units", + "num-rational", + "num-traits", + "parity-wasm", + "wasmi-validation", +] + +[[package]] +name = "wasmi-validation" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "165343ecd6c018fc09ebcae280752702c9a2ef3e6f8d02f1cfcbdb53ef6d7937" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.6+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a3f9792c0c3dc6c165840a75f47ae1f4da402c2d006881129579f6597e801b" +dependencies = [ + "cc", + "libc", + "pkg-config", +] diff --git a/tools/runtime-codegen/Cargo.toml b/tools/runtime-codegen/Cargo.toml new file mode 100644 index 00000000000..7fcb7bc68a9 --- /dev/null +++ b/tools/runtime-codegen/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "runtime-codegen" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[workspace] + +[dependencies] +clap = { version = "4.0.8", features = ["derive", "cargo"] } +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } +color-eyre = "0.6.1" +proc-macro2 = "1.0.51" +subxt-codegen = { git = "https://github.com/paritytech/subxt", branch = "master" } +syn = "1.0" +wasm-loader = { git = "https://github.com/chevdor/subwasm", branch = "master" } +wasm-testbed = { git = "https://github.com/chevdor/subwasm", branch = "master" } \ No newline at end of file diff --git a/tools/runtime-codegen/README.md b/tools/runtime-codegen/README.md new file mode 100644 index 00000000000..155e90b369c --- /dev/null +++ b/tools/runtime-codegen/README.md @@ -0,0 +1,11 @@ +This is a tool for generating the bridge runtime code from metadata. + +Example commands: + +``` +cargo run --bin runtime-codegen -- --from-node-url "http://localhost:20433" > /tmp/rialto_codegen.rs +``` + +``` +cargo run --bin runtime-codegen -- --from-wasm ~/workplace/bridge-hub-rococo_runtime-v9360.compact.compressed.wasm > /tmp/rococo_bridge_hub_codegen.rs +``` \ No newline at end of file diff --git a/tools/runtime-codegen/src/main.rs b/tools/runtime-codegen/src/main.rs new file mode 100644 index 00000000000..e1aa142e558 --- /dev/null +++ b/tools/runtime-codegen/src/main.rs @@ -0,0 +1,173 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use clap::Parser as ClapParser; +use codec::Encode; +use color_eyre::eyre; +use std::{env, path::PathBuf}; +use subxt_codegen::{ + generate_runtime_api_from_bytes, generate_runtime_api_from_url, utils::Uri, CratePath, + DerivesRegistry, TypeSubstitutes, +}; +use wasm_testbed::WasmTestBed; + +/// Command for generating indirect runtimes code. +#[derive(Debug, ClapParser)] +struct Command { + #[clap(name = "from-node-url", long, value_parser)] + node_url: Option, + #[clap(name = "from-wasm-file", long, value_parser)] + wasm_file: Option, +} + +enum RuntimeMetadataSource { + NodeUrl(Uri), + WasmFile(wasm_loader::Source), +} + +impl RuntimeMetadataSource { + fn from_command(cmd: Command) -> color_eyre::Result { + match (cmd.node_url, cmd.wasm_file) { + (Some(_), Some(_)) => Err(eyre::eyre!( + "Please specify one of `--from-node-url` or `--from-wasm-file` but not both" + )), + (None, None) => + Err(eyre::eyre!("Please specify one of `--from-node-url` or `--from-wasm-file`")), + (Some(node_url), None) => Ok(Self::NodeUrl(node_url)), + (None, Some(source)) => + Ok(Self::WasmFile(wasm_loader::Source::File(PathBuf::from(source)))), + } + } +} + +struct TypeSubstitute { + subxt_type: syn::Path, + substitute: syn::Path, +} + +impl TypeSubstitute { + fn simple(subxt_type: &str) -> Self { + Self { + subxt_type: syn::parse_str::(subxt_type).unwrap(), + substitute: syn::parse_str::(&format!("::{subxt_type}")).unwrap(), + } + } + + fn custom(subxt_type: &str, substitute: &str) -> Self { + Self { + subxt_type: syn::parse_str::(subxt_type).unwrap(), + substitute: syn::parse_str::(substitute).unwrap(), + } + } +} + +fn print_runtime(runtime_api: proc_macro2::TokenStream) { + println!( + "// Copyright 2019-2023 Parity Technologies (UK) Ltd. + // This file is part of Parity Bridges Common. + + // Parity Bridges Common 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. + + // Parity Bridges Common 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 Parity Bridges Common. If not, see . + + //! Autogenerated runtime API + //! THIS FILE WAS AUTOGENERATED USING parity-bridges-common::runtime-codegen + //! EXECUTED COMMAND: {} + + {} + ", + env::args().collect::>().join(" "), + runtime_api + ); +} + +fn main() -> color_eyre::Result<()> { + let args: Command = Command::parse(); + let metadata_source = RuntimeMetadataSource::from_command(args)?; + + // Module under which the API is generated. + let item_mod = syn::parse_quote!( + pub mod api {} + ); + // Default module derivatives. + let mut derives = DerivesRegistry::new(&CratePath::default()); + derives.extend_for_all(vec![syn::parse_quote!(Clone)]); + // Type substitutes + let mut type_substitutes = TypeSubstitutes::new(&CratePath::default()); + type_substitutes.extend( + vec![ + TypeSubstitute::simple("sp_core::crypto::AccountId32"), + TypeSubstitute::custom("bp_millau::millau_hash::MillauHash", "::bp_millau::MillauHash"), + TypeSubstitute::simple("bp_millau::BlakeTwoAndKeccak256"), + TypeSubstitute::custom( + "sp_runtime::generic::digest::Digest", + "::sp_runtime::generic::Digest", + ), + TypeSubstitute::custom("sp_runtime::generic::era::Era", "::sp_runtime::generic::Era"), + TypeSubstitute::custom( + "sp_runtime::generic::header::Header", + "::sp_runtime::generic::Header", + ), + TypeSubstitute::simple("bp_header_chain::justification::GrandpaJustification"), + TypeSubstitute::simple("bp_header_chain::InitializationData"), + TypeSubstitute::simple( + "bridge_runtime_common::messages::target::FromBridgedChainMessagesProof", + ), + TypeSubstitute::custom("sp_weights::weight_v2::Weight", "::sp_weights::Weight"), + TypeSubstitute::simple( + "bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof", + ), + TypeSubstitute::simple("bp_messages::UnrewardedRelayersState"), + ] + .drain(..) + .map(|substitute| (substitute.subxt_type, substitute.substitute.try_into().unwrap())), + ); + + // Generate the Runtime API. + let runtime_api = match metadata_source { + RuntimeMetadataSource::NodeUrl(node_url) => generate_runtime_api_from_url( + item_mod, + &node_url, + derives, + type_substitutes, + CratePath::default(), + ), + RuntimeMetadataSource::WasmFile(source) => { + let testbed = WasmTestBed::new(&source) + .map_err(|e| eyre::eyre!("Error creating WasmTestBed: {:?}", e))?; + generate_runtime_api_from_bytes( + item_mod, + &testbed.runtime_metadata_prefixed().encode(), + derives, + type_substitutes, + CratePath::default(), + ) + }, + }; + + print_runtime(runtime_api); + + Ok(()) +} From 97f2ab29bf650bba3884abafaa480850d64f8278 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 8 Feb 2023 16:26:20 +0100 Subject: [PATCH 195/263] Squashed 'bridges/' changes from b39cb0dea..4c4a7eae1 4c4a7eae1 Small stuff from Cumulus integration (#1865) git-subtree-dir: bridges git-subtree-split: 4c4a7eae1503aa63a84fb65d56d67599d362d645 --- scripts/verify-pallets-build.sh | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/scripts/verify-pallets-build.sh b/scripts/verify-pallets-build.sh index e1393688c17..1f45eb51bd5 100755 --- a/scripts/verify-pallets-build.sh +++ b/scripts/verify-pallets-build.sh @@ -19,11 +19,13 @@ function show_help() { echo " ./scripts/verify-pallets-build.sh Exit with code 0 if pallets code is well decoupled from the other code in the repo" echo "Options:" echo " --no-revert Leaves only runtime code on exit" + echo " --ignore-git-state Ignores git actual state" exit 1 } # parse CLI args NO_REVERT= +IGNORE_GIT_STATE= for i in "$@" do case $i in @@ -31,18 +33,22 @@ do NO_REVERT=true shift ;; + --ignore-git-state) + IGNORE_GIT_STATE=true + shift + ;; *) show_help "Unknown option: $i" ;; esac done -# the script is able to work only on clean git copy -[[ -z "$(git status --porcelain)" ]] || { echo >&2 "The git copy must be clean"; exit 1; } +# the script is able to work only on clean git copy, unless we want to ignore this check +[[ ! -z "${IGNORE_GIT_STATE}" ]] || [[ -z "$(git status --porcelain)" ]] || { echo >&2 "The git copy must be clean"; exit 1; } # let's avoid any restrictions on where this script can be called for - bridges repo may be # plugged into any other repo folder. So the script (and other stuff that needs to be removed) -# may be located either in call dir, or one of it subdirs. +# may be located either in call dir, or one of it subdirs. BRIDGES_FOLDER="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )/.." # let's leave repository/subtree in its original (clean) state if something fails below @@ -58,6 +64,7 @@ rm -rf $BRIDGES_FOLDER/.maintain rm -rf $BRIDGES_FOLDER/bin/millau rm -rf $BRIDGES_FOLDER/bin/rialto rm -rf $BRIDGES_FOLDER/bin/rialto-parachain +rm -rf $BRIDGES_FOLDER/bin/.keep rm -rf $BRIDGES_FOLDER/deployments rm -rf $BRIDGES_FOLDER/fuzz rm -rf $BRIDGES_FOLDER/modules/beefy @@ -68,6 +75,16 @@ rm -rf $BRIDGES_FOLDER/primitives/chain-rialto rm -rf $BRIDGES_FOLDER/primitives/chain-rialto-parachain rm -rf $BRIDGES_FOLDER/primitives/chain-westend rm -rf $BRIDGES_FOLDER/relays +rm -rf $BRIDGES_FOLDER/scripts/add_license.sh +rm -rf $BRIDGES_FOLDER/scripts/build-containers.sh +rm -rf $BRIDGES_FOLDER/scripts/ci-cache.sh +rm -rf $BRIDGES_FOLDER/scripts/dump-logs.sh +rm -rf $BRIDGES_FOLDER/scripts/license_header +rm -rf $BRIDGES_FOLDER/scripts/send-message-from-millau-rialto.sh +rm -rf $BRIDGES_FOLDER/scripts/send-message-from-rialto-millau.sh +rm -rf $BRIDGES_FOLDER/scripts/update-weights.sh +rm -rf $BRIDGES_FOLDER/scripts/update-weights-setup.sh +rm -rf $BRIDGES_FOLDER/scripts/update_substrate.sh rm -rf $BRIDGES_FOLDER/tools rm -f $BRIDGES_FOLDER/.dockerignore rm -f $BRIDGES_FOLDER/.gitlab-ci.yml From be711f15f622a1e358ea43cd814e6a902a5c6bda Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 15 Feb 2023 11:58:53 +0100 Subject: [PATCH 196/263] Squashed 'bridges/' changes from 4c4a7eae1..dcaec27aa dcaec27aa RefundRelayerForMessagesFromParachain improvements (#1879) 5457f0672 clippy fixes (#1880) 29e8a305c MaxValues for OutboundLanes map (#1871) 5219b56f8 More tests for message pallet weights (#1870) c4c0c7a1b Bump signal-hook from 0.3.14 to 0.3.15 0ff597b96 Bump serde_json from 1.0.92 to 1.0.93 1c5132eb1 Bump subxt from `20adb19` to `9e2acff` adb07816b update parachains relay doc (#1874) 972ef3133 Update README.md (#1872) 94648061b MaxValues for maps in parachain maps (#1868) 662267a6f "refund" proof size in GRANDPa pallet (#1863) git-subtree-dir: bridges git-subtree-split: dcaec27aaa6f41070fbdfbfd4fde2029697eb85f --- Cargo.lock | 18 +- bin/millau/node/Cargo.toml | 2 +- bin/millau/runtime/src/lib.rs | 1 - bin/rialto/node/Cargo.toml | 2 +- bin/runtime-common/src/lib.rs | 40 +- ...ages_extension.rs => messages_call_ext.rs} | 155 ++++--- .../src/refund_relayer_extension.rs | 431 +++++++----------- modules/grandpa/README.md | 2 +- modules/grandpa/src/call_ext.rs | 163 +++++++ modules/grandpa/src/extension.rs | 117 ----- modules/grandpa/src/lib.rs | 73 ++- modules/grandpa/src/mock.rs | 2 +- modules/grandpa/src/storage_types.rs | 58 ++- modules/messages/src/lib.rs | 27 +- modules/messages/src/mock.rs | 5 +- modules/messages/src/weights.rs | 186 ++++---- modules/messages/src/weights_ext.rs | 157 ++++++- modules/parachains/README.md | 2 +- modules/parachains/src/call_ext.rs | 243 ++++++++++ modules/parachains/src/extension.rs | 166 ------- modules/parachains/src/lib.rs | 196 ++++---- modules/parachains/src/mock.rs | 2 + modules/parachains/src/weights.rs | 142 +++--- primitives/parachains/src/lib.rs | 17 +- primitives/runtime/src/lib.rs | 7 - relays/bin-substrate/Cargo.toml | 2 +- relays/finality/README.md | 2 +- relays/messages/src/message_race_delivery.rs | 8 +- relays/messages/src/message_race_receiving.rs | 4 +- relays/messages/src/metrics.rs | 11 +- relays/parachains/README.md | 2 +- 31 files changed, 1298 insertions(+), 945 deletions(-) rename bin/runtime-common/src/{messages_extension.rs => messages_call_ext.rs} (57%) create mode 100644 modules/grandpa/src/call_ext.rs delete mode 100644 modules/grandpa/src/extension.rs create mode 100644 modules/parachains/src/call_ext.rs delete mode 100644 modules/parachains/src/extension.rs diff --git a/Cargo.lock b/Cargo.lock index 3f09e60c479..3a7eb6fc545 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11216,9 +11216,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ "indexmap", "itoa", @@ -11302,9 +11302,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "signal-hook" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" dependencies = [ "libc", "signal-hook-registry", @@ -12899,7 +12899,7 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "subxt" version = "0.26.0" -source = "git+https://github.com/paritytech/subxt?branch=master#20adb198e42d1a36d0c919737230423c17ca78e6" +source = "git+https://github.com/paritytech/subxt?branch=master#9e2acff5b2838a59205fac1cedbf2c404f5eb9b7" dependencies = [ "base58", "blake2", @@ -12931,7 +12931,7 @@ dependencies = [ [[package]] name = "subxt-codegen" version = "0.26.0" -source = "git+https://github.com/paritytech/subxt?branch=master#20adb198e42d1a36d0c919737230423c17ca78e6" +source = "git+https://github.com/paritytech/subxt?branch=master#9e2acff5b2838a59205fac1cedbf2c404f5eb9b7" dependencies = [ "darling", "frame-metadata", @@ -12951,7 +12951,7 @@ dependencies = [ [[package]] name = "subxt-macro" version = "0.26.0" -source = "git+https://github.com/paritytech/subxt?branch=master#20adb198e42d1a36d0c919737230423c17ca78e6" +source = "git+https://github.com/paritytech/subxt?branch=master#9e2acff5b2838a59205fac1cedbf2c404f5eb9b7" dependencies = [ "darling", "proc-macro-error", @@ -12962,7 +12962,7 @@ dependencies = [ [[package]] name = "subxt-metadata" version = "0.26.0" -source = "git+https://github.com/paritytech/subxt?branch=master#20adb198e42d1a36d0c919737230423c17ca78e6" +source = "git+https://github.com/paritytech/subxt?branch=master#9e2acff5b2838a59205fac1cedbf2c404f5eb9b7" dependencies = [ "frame-metadata", "parity-scale-codec", @@ -13656,7 +13656,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "digest 0.10.6", "rand 0.8.5", "static_assertions", diff --git a/bin/millau/node/Cargo.toml b/bin/millau/node/Cargo.toml index af2c50eb480..fea96c9b11f 100644 --- a/bin/millau/node/Cargo.toml +++ b/bin/millau/node/Cargo.toml @@ -11,7 +11,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] clap = { version = "4.1.4", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } -serde_json = "1.0.92" +serde_json = "1.0.93" # Bridge dependencies diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index 2119fa04610..8f82e645f9e 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -608,7 +608,6 @@ pub type BridgeRefundRialtoParachainRelayers = RialtoGrandpaInstance, WithRialtoParachainsInstance, WithRialtoParachainMessagesInstance, - BridgeRejectObsoleteHeadersAndMessages, RialtoParachainId, RialtoParachainMessagesLane, Runtime, diff --git a/bin/rialto/node/Cargo.toml b/bin/rialto/node/Cargo.toml index bf8b3484732..d03deaaabef 100644 --- a/bin/rialto/node/Cargo.toml +++ b/bin/rialto/node/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] clap = { version = "4.1.4", features = ["derive"] } -serde_json = "1.0.92" +serde_json = "1.0.93" # Bridge dependencies diff --git a/bin/runtime-common/src/lib.rs b/bin/runtime-common/src/lib.rs index 9f31450bd28..32ea500db3e 100644 --- a/bin/runtime-common/src/lib.rs +++ b/bin/runtime-common/src/lib.rs @@ -18,14 +18,16 @@ #![cfg_attr(not(feature = "std"), no_std)] -use bp_runtime::FilterCall; +use crate::messages_call_ext::MessagesCallSubType; +use pallet_bridge_grandpa::CallSubType as GrandpaCallSubType; +use pallet_bridge_parachains::CallSubType as ParachainsCallSubtype; use sp_runtime::transaction_validity::TransactionValidity; use xcm::v3::NetworkId; pub mod messages; pub mod messages_api; pub mod messages_benchmarking; -pub mod messages_extension; +pub mod messages_call_ext; pub mod parachains_benchmarking; pub mod refund_relayer_extension; @@ -44,21 +46,39 @@ pub trait BridgeRuntimeFilterCall { fn validate(call: &Call) -> TransactionValidity; } -impl BridgeRuntimeFilterCall for pallet_bridge_grandpa::Pallet +impl BridgeRuntimeFilterCall for pallet_bridge_grandpa::Pallet where - pallet_bridge_grandpa::Pallet: FilterCall, + T: pallet_bridge_grandpa::Config, + T::RuntimeCall: GrandpaCallSubType, { - fn validate(call: &Call) -> TransactionValidity { - as FilterCall>::validate(call) + fn validate(call: &T::RuntimeCall) -> TransactionValidity { + GrandpaCallSubType::::check_obsolete_submit_finality_proof(call) } } -impl BridgeRuntimeFilterCall for pallet_bridge_parachains::Pallet +impl BridgeRuntimeFilterCall + for pallet_bridge_parachains::Pallet where - pallet_bridge_parachains::Pallet: FilterCall, + T: pallet_bridge_parachains::Config, + T::RuntimeCall: ParachainsCallSubtype, { - fn validate(call: &Call) -> TransactionValidity { - as FilterCall>::validate(call) + fn validate(call: &T::RuntimeCall) -> TransactionValidity { + ParachainsCallSubtype::::check_obsolete_submit_parachain_heads(call) + } +} + +impl, I: 'static> BridgeRuntimeFilterCall + for pallet_bridge_messages::Pallet +where + T::RuntimeCall: MessagesCallSubType, +{ + /// Validate messages in order to avoid "mining" messages delivery and delivery confirmation + /// transactions, that are delivering outdated messages/confirmations. Without this validation, + /// even honest relayers may lose their funds if there are multiple relays running and + /// submitting the same messages/confirmations. + fn validate(call: &T::RuntimeCall) -> TransactionValidity { + call.check_obsolete_receive_messages_proof()?; + call.check_obsolete_receive_messages_delivery_proof() } } diff --git a/bin/runtime-common/src/messages_extension.rs b/bin/runtime-common/src/messages_call_ext.rs similarity index 57% rename from bin/runtime-common/src/messages_extension.rs rename to bin/runtime-common/src/messages_call_ext.rs index 1e207dc605b..20e604142d9 100644 --- a/bin/runtime-common/src/messages_extension.rs +++ b/bin/runtime-common/src/messages_call_ext.rs @@ -14,20 +14,56 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use crate::{ - messages::{ - source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, - }, - BridgeRuntimeFilterCall, +use crate::messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; +use bp_messages::{LaneId, MessageNonce}; use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; use pallet_bridge_messages::{Config, Pallet}; -use sp_runtime::transaction_validity::TransactionValidity; +use sp_runtime::{transaction_validity::TransactionValidity, RuntimeDebug}; + +/// Info about a `ReceiveMessagesProof` call which tries to update a single lane. +#[derive(Copy, Clone, PartialEq, RuntimeDebug)] +pub struct ReceiveMessagesProofInfo { + pub lane_id: LaneId, + pub best_proof_nonce: MessageNonce, + pub best_stored_nonce: MessageNonce, +} + +/// Helper struct that provides methods for working with the `ReceiveMessagesProof` call. +pub struct ReceiveMessagesProofHelper, I: 'static> { + pub _phantom_data: sp_std::marker::PhantomData<(T, I)>, +} + +impl, I: 'static> ReceiveMessagesProofHelper { + /// Check if the `ReceiveMessagesProof` call delivered at least some of the messages that + /// it contained. + pub fn was_partially_successful(info: &ReceiveMessagesProofInfo) -> bool { + let inbound_lane_data = pallet_bridge_messages::InboundLanes::::get(info.lane_id); + inbound_lane_data.last_delivered_nonce() > info.best_stored_nonce + } +} + +/// Trait representing a call that is a sub type of `pallet_bridge_messages::Call`. +pub trait MessagesCallSubType, I: 'static>: + IsSubType, T>> +{ + /// Create a new instance of `ReceiveMessagesProofInfo` from a `ReceiveMessagesProof` call. + fn receive_messages_proof_info(&self) -> Option; + + /// Create a new instance of `ReceiveMessagesProofInfo` from a `ReceiveMessagesProof` call, + /// if the call is for the provided lane. + fn receive_messages_proof_info_for(&self, lane_id: LaneId) -> Option; + + /// Check that a `ReceiveMessagesProof` call is trying to deliver at least some messages that + /// are better than the ones we know of. + fn check_obsolete_receive_messages_proof(&self) -> TransactionValidity; + + /// Check that a `ReceiveMessagesDeliveryProof` call is trying to deliver at least some message + /// confirmations that are better than the ones we know of. + fn check_obsolete_receive_messages_delivery_proof(&self) -> TransactionValidity; +} -/// Validate messages in order to avoid "mining" messages delivery and delivery confirmation -/// transactions, that are delivering outdated messages/confirmations. Without this validation, -/// even honest relayers may lose their funds if there are multiple relays running and submitting -/// the same messages/confirmations. impl< BridgedHeaderHash, SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain< @@ -42,52 +78,69 @@ impl< T: frame_system::Config + Config, I: 'static, - > BridgeRuntimeFilterCall for Pallet + > MessagesCallSubType for T::RuntimeCall { - fn validate(call: &Call) -> TransactionValidity { - match call.is_sub_type() { - Some(pallet_bridge_messages::Call::::receive_messages_proof { - ref proof, - .. - }) => { - let inbound_lane_data = - pallet_bridge_messages::InboundLanes::::get(proof.lane); - if proof.nonces_end <= inbound_lane_data.last_delivered_nonce() { - log::trace!( - target: pallet_bridge_messages::LOG_TARGET, - "Rejecting obsolete messages delivery transaction: \ + fn receive_messages_proof_info(&self) -> Option { + if let Some(pallet_bridge_messages::Call::::receive_messages_proof { + ref proof, + .. + }) = self.is_sub_type() + { + let inbound_lane_data = pallet_bridge_messages::InboundLanes::::get(proof.lane); + + return Some(ReceiveMessagesProofInfo { + lane_id: proof.lane, + best_proof_nonce: proof.nonces_end, + best_stored_nonce: inbound_lane_data.last_delivered_nonce(), + }) + } + + None + } + + fn receive_messages_proof_info_for(&self, lane_id: LaneId) -> Option { + self.receive_messages_proof_info().filter(|info| info.lane_id == lane_id) + } + + fn check_obsolete_receive_messages_proof(&self) -> TransactionValidity { + if let Some(proof_info) = self.receive_messages_proof_info() { + if proof_info.best_proof_nonce <= proof_info.best_stored_nonce { + log::trace!( + target: pallet_bridge_messages::LOG_TARGET, + "Rejecting obsolete messages delivery transaction: \ lane {:?}, bundled {:?}, best {:?}", - proof.lane, - proof.nonces_end, - inbound_lane_data.last_delivered_nonce(), - ); + proof_info.lane_id, + proof_info.best_proof_nonce, + proof_info.best_stored_nonce, + ); - return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() - } - }, - Some(pallet_bridge_messages::Call::::receive_messages_delivery_proof { - ref proof, - ref relayers_state, - .. - }) => { - let latest_delivered_nonce = relayers_state.last_delivered_nonce; - - let outbound_lane_data = - pallet_bridge_messages::OutboundLanes::::get(proof.lane); - if latest_delivered_nonce <= outbound_lane_data.latest_received_nonce { - log::trace!( - target: pallet_bridge_messages::LOG_TARGET, - "Rejecting obsolete messages confirmation transaction: \ + return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() + } + } + + Ok(sp_runtime::transaction_validity::ValidTransaction::default()) + } + + fn check_obsolete_receive_messages_delivery_proof(&self) -> TransactionValidity { + if let Some(pallet_bridge_messages::Call::::receive_messages_delivery_proof { + ref proof, + ref relayers_state, + .. + }) = self.is_sub_type() + { + let outbound_lane_data = pallet_bridge_messages::OutboundLanes::::get(proof.lane); + if relayers_state.last_delivered_nonce <= outbound_lane_data.latest_received_nonce { + log::trace!( + target: pallet_bridge_messages::LOG_TARGET, + "Rejecting obsolete messages confirmation transaction: \ lane {:?}, bundled {:?}, best {:?}", - proof.lane, - latest_delivered_nonce, - outbound_lane_data.latest_received_nonce, - ); + proof.lane, + relayers_state.last_delivered_nonce, + outbound_lane_data.latest_received_nonce, + ); - return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() - } - }, - _ => {}, + return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() + } } Ok(sp_runtime::transaction_validity::ValidTransaction::default()) diff --git a/bin/runtime-common/src/refund_relayer_extension.rs b/bin/runtime-common/src/refund_relayer_extension.rs index 51a7db3b53b..ac65617483d 100644 --- a/bin/runtime-common/src/refund_relayer_extension.rs +++ b/bin/runtime-common/src/refund_relayer_extension.rs @@ -19,37 +19,32 @@ //! with calls that are: delivering new messsage and all necessary underlying headers //! (parachain or relay chain). -use crate::messages::target::FromBridgedChainMessagesProof; - -use bp_messages::{target_chain::SourceHeaderChain, LaneId, MessageNonce}; -use bp_polkadot_core::parachains::ParaId; -use bp_runtime::{Chain, HashOf}; +use crate::messages_call_ext::{ + MessagesCallSubType, ReceiveMessagesProofHelper, ReceiveMessagesProofInfo, +}; +use bp_messages::LaneId; use codec::{Decode, Encode}; use frame_support::{ dispatch::{CallableCallFor, DispatchInfo, Dispatchable, PostDispatchInfo}, traits::IsSubType, CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; -use pallet_bridge_grandpa::{ - BridgedChain, Call as GrandpaCall, Config as GrandpaConfig, Pallet as GrandpaPallet, -}; -use pallet_bridge_messages::{ - Call as MessagesCall, Config as MessagesConfig, Pallet as MessagesPallet, -}; +use pallet_bridge_grandpa::{CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper}; +use pallet_bridge_messages::Config as MessagesConfig; use pallet_bridge_parachains::{ - Call as ParachainsCall, Config as ParachainsConfig, Pallet as ParachainsPallet, RelayBlockHash, - RelayBlockHasher, RelayBlockNumber, + BoundedBridgeGrandpaConfig, CallSubType as ParachainsCallSubType, Config as ParachainsConfig, + RelayBlockNumber, SubmitParachainHeadsHelper, SubmitParachainHeadsInfo, }; use pallet_bridge_relayers::{Config as RelayersConfig, Pallet as RelayersPallet}; use pallet_transaction_payment::{Config as TransactionPaymentConfig, OnChargeTransaction}; use pallet_utility::{Call as UtilityCall, Config as UtilityConfig, Pallet as UtilityPallet}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Get, Header as HeaderT, PostDispatchInfoOf, SignedExtension, Zero}, + traits::{DispatchInfoOf, Get, PostDispatchInfoOf, SignedExtension, Zero}, transaction_validity::{TransactionValidity, TransactionValidityError, ValidTransaction}, DispatchResult, FixedPointOperand, }; -use sp_std::marker::PhantomData; +use sp_std::{marker::PhantomData, vec, vec::Vec}; // TODO (https://github.com/paritytech/parity-bridges-common/issues/1667): // support multiple bridges in this extension @@ -81,6 +76,7 @@ where pallet_transaction_payment::Pallet::::compute_actual_fee(len as _, info, post_info, tip) } } + /// Signed extension that refunds relayer for new messages coming from the parachain. /// /// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) @@ -99,18 +95,40 @@ where RuntimeDebugNoBound, TypeInfo, )] -#[scale_info(skip_type_params(RT, GI, PI, MI, BE, PID, LID, FEE))] -#[allow(clippy::type_complexity)] // TODO: get rid of that in https://github.com/paritytech/parity-bridges-common/issues/1666 -pub struct RefundRelayerForMessagesFromParachain( - PhantomData<(RT, GI, PI, MI, BE, PID, LID, FEE)>, +#[scale_info(skip_type_params(RT, GI, PI, MI, PID, LID, FEE))] +pub struct RefundRelayerForMessagesFromParachain( + PhantomData<(RT, GI, PI, MI, PID, LID, FEE)>, ); +impl + RefundRelayerForMessagesFromParachain +where + R: UtilityConfig>, + CallOf: IsSubType, R>>, +{ + fn expand_call<'a>(&self, call: &'a CallOf) -> Option>> { + let calls = match call.is_sub_type() { + Some(UtilityCall::::batch_all { ref calls }) => { + if calls.len() > 3 { + return None + } + + calls.iter().collect() + }, + Some(_) => return None, + None => vec![call], + }; + + Some(calls) + } +} + /// Data that is crafted in `pre_dispatch` method and used at `post_dispatch`. #[derive(PartialEq)] #[cfg_attr(test, derive(Debug))] pub struct PreDispatchData { /// Transaction submitter (relayer) account. - pub relayer: AccountId, + relayer: AccountId, /// Type of the call. pub call_type: CallType, } @@ -119,89 +137,51 @@ pub struct PreDispatchData { #[derive(Clone, Copy, PartialEq, RuntimeDebugNoBound)] pub enum CallType { /// Relay chain finality + parachain finality + message delivery calls. - AllFinalityAndDelivery(ExpectedRelayChainState, ExpectedParachainState, MessagesState), + AllFinalityAndDelivery(RelayBlockNumber, SubmitParachainHeadsInfo, ReceiveMessagesProofInfo), /// Parachain finality + message delivery calls. - ParachainFinalityAndDelivery(ExpectedParachainState, MessagesState), + ParachainFinalityAndDelivery(SubmitParachainHeadsInfo, ReceiveMessagesProofInfo), /// Standalone message delivery call. - Delivery(MessagesState), + Delivery(ReceiveMessagesProofInfo), } impl CallType { /// Returns the pre-dispatch messages pallet state. - fn pre_dispatch_messages_state(&self) -> MessagesState { + fn receive_messages_proof_info(&self) -> ReceiveMessagesProofInfo { match *self { - Self::AllFinalityAndDelivery(_, _, messages_state) => messages_state, - Self::ParachainFinalityAndDelivery(_, messages_state) => messages_state, - Self::Delivery(messages_state) => messages_state, + Self::AllFinalityAndDelivery(_, _, info) => info, + Self::ParachainFinalityAndDelivery(_, info) => info, + Self::Delivery(info) => info, } } } -/// Expected post-dispatch state of the relay chain pallet. -#[derive(Clone, Copy, PartialEq, RuntimeDebugNoBound)] -pub struct ExpectedRelayChainState { - /// Best known relay chain block number. - pub best_block_number: RelayBlockNumber, -} - -/// Expected post-dispatch state of the parachain pallet. -#[derive(Clone, Copy, PartialEq, RuntimeDebugNoBound)] -pub struct ExpectedParachainState { - /// At which relay block the parachain head has been updated? - pub at_relay_block_number: RelayBlockNumber, -} - -/// Pre-dispatch state of messages pallet. -/// -/// This struct is for pre-dispatch state of the pallet, not the expected post-dispatch state. -/// That's because message delivery transaction may deliver some of messages that it brings. -/// If this happens, we consider it "helpful" and refund its cost. If transaction fails to -/// deliver at least one message, it is considered wrong and is not refunded. -#[derive(Clone, Copy, PartialEq, RuntimeDebugNoBound)] -pub struct MessagesState { - /// Best delivered message nonce. - pub best_nonce: MessageNonce, -} - // without this typedef rustfmt fails with internal err type BalanceOf = <::OnChargeTransaction as OnChargeTransaction>::Balance; type CallOf = ::RuntimeCall; -impl SignedExtension - for RefundRelayerForMessagesFromParachain +impl SignedExtension + for RefundRelayerForMessagesFromParachain where R: 'static + Send + Sync - + frame_system::Config + UtilityConfig> - + GrandpaConfig + + BoundedBridgeGrandpaConfig + ParachainsConfig + MessagesConfig + RelayersConfig, GI: 'static + Send + Sync, PI: 'static + Send + Sync, MI: 'static + Send + Sync, - BE: 'static - + Send - + Sync - + Default - + SignedExtension>, PID: 'static + Send + Sync + Get, LID: 'static + Send + Sync + Get, FEE: 'static + Send + Sync + TransactionFeeCalculation<::Reward>, - ::RuntimeCall: - Dispatchable, - CallOf: IsSubType, R>> - + IsSubType, R>> - + IsSubType, R>> - + IsSubType, R>>, - >::BridgedChain: - Chain, - >::SourceHeaderChain: SourceHeaderChain< - MessagesProof = FromBridgedChainMessagesProof>>, - >, + CallOf: Dispatchable + + IsSubType, R>> + + GrandpaCallSubType + + ParachainsCallSubType + + MessagesCallSubType, { const IDENTIFIER: &'static str = "RefundRelayerForMessagesFromParachain"; type AccountId = R::AccountId; @@ -215,17 +195,20 @@ where fn validate( &self, - who: &Self::AccountId, + _who: &Self::AccountId, call: &Self::Call, - info: &DispatchInfoOf, - len: usize, + _info: &DispatchInfoOf, + _len: usize, ) -> TransactionValidity { - // reject batch transactions with obsolete headers - if let Some(UtilityCall::::batch_all { ref calls }) = call.is_sub_type() { - for nested_call in calls { - let reject_obsolete_transactions = BE::default(); - reject_obsolete_transactions.pre_dispatch(who, nested_call, info, len)?; - } + let calls = match self.expand_call(call) { + Some(calls) => calls, + None => return Ok(ValidTransaction::default()), + }; + + for call in calls { + call.check_obsolete_submit_finality_proof()?; + call.check_obsolete_submit_parachain_heads()?; + call.check_obsolete_receive_messages_proof()?; } Ok(ValidTransaction::default()) @@ -241,40 +224,37 @@ where // reject batch transactions with obsolete headers self.validate(who, call, info, len).map(drop)?; - // now try to check if tx matches one of types we support + // Try to check if the tx matches one of types we support. let parse_call_type = || { - if let Some(UtilityCall::::batch_all { ref calls }) = call.is_sub_type() { - if calls.len() == 3 { - return Some(CallType::AllFinalityAndDelivery( - extract_expected_relay_chain_state::(&calls[0])?, - extract_expected_parachain_state::(&calls[1])?, - extract_messages_state::(&calls[2])?, - )) - } - if calls.len() == 2 { - return Some(CallType::ParachainFinalityAndDelivery( - extract_expected_parachain_state::(&calls[0])?, - extract_messages_state::(&calls[1])?, - )) - } - return None + let mut calls = self.expand_call(call)?.into_iter(); + match calls.len() { + 3 => Some(CallType::AllFinalityAndDelivery( + calls.next()?.submit_finality_proof_info()?, + calls.next()?.submit_parachain_heads_info_for(PID::get())?, + calls.next()?.receive_messages_proof_info_for(LID::get())?, + )), + 2 => Some(CallType::ParachainFinalityAndDelivery( + calls.next()?.submit_parachain_heads_info_for(PID::get())?, + calls.next()?.receive_messages_proof_info_for(LID::get())?, + )), + 1 => Some(CallType::Delivery( + calls.next()?.receive_messages_proof_info_for(LID::get())?, + )), + _ => None, } - - Some(CallType::Delivery(extract_messages_state::(call)?)) }; - Ok(parse_call_type() - .map(|call_type| { - log::trace!( - target: "runtime::bridge", - "RefundRelayerForMessagesFromParachain from parachain {} via {:?} parsed bridge transaction in pre-dispatch: {:?}", - PID::get(), - LID::get(), - call_type, - ); - PreDispatchData { relayer: who.clone(), call_type } - }) - ) + Ok(parse_call_type().map(|call_type| { + log::trace!( + target: "runtime::bridge", + "RefundRelayerForMessagesFromParachain from parachain {} via {:?} \ + parsed bridge transaction in pre-dispatch: {:?}", + PID::get(), + LID::get(), + call_type, + ); + PreDispatchData { relayer: who.clone(), call_type } + })) } fn post_dispatch( @@ -284,40 +264,37 @@ where len: usize, result: &DispatchResult, ) -> Result<(), TransactionValidityError> { - // we never refund anything if it is not bridge transaction or if it is a bridge - // transaction that we do not support here + // We don't refund anything if the transaction has failed. + if result.is_err() { + return Ok(()) + } + + // We don't refund anything for transactions that we don't support. let (relayer, call_type) = match pre { Some(Some(pre)) => (pre.relayer, pre.call_type), _ => return Ok(()), }; - // we never refund anything if transaction has failed - if result.is_err() { - return Ok(()) - } - // check if relay chain state has been updated - if let CallType::AllFinalityAndDelivery(expected_relay_chain_state, _, _) = call_type { - let actual_relay_chain_state = relay_chain_state::(); - if actual_relay_chain_state != Some(expected_relay_chain_state) { + if let CallType::AllFinalityAndDelivery(relay_block_number, _, _) = call_type { + if !SubmitFinalityProofHelper::::was_successful(relay_block_number) { // we only refund relayer if all calls have updated chain state return Ok(()) } - // there's a conflict between how bridge GRANDPA pallet works and the - // `AllFinalityAndDelivery` transaction. If relay chain header is mandatory, the GRANDPA - // pallet returns `Pays::No`, because such transaction is mandatory for operating the - // bridge. But `utility.batchAll` transaction always requires payment. But in both cases - // we'll refund relayer - either explicitly here, or using `Pays::No` if he's choosing + // there's a conflict between how bridge GRANDPA pallet works and a `utility.batchAll` + // transaction. If relay chain header is mandatory, the GRANDPA pallet returns + // `Pays::No`, because such transaction is mandatory for operating the bridge. But + // `utility.batchAll` transaction always requires payment. But in both cases we'll + // refund relayer - either explicitly here, or using `Pays::No` if he's choosing // to submit dedicated transaction. } // check if parachain state has been updated match call_type { - CallType::AllFinalityAndDelivery(_, expected_parachain_state, _) | - CallType::ParachainFinalityAndDelivery(expected_parachain_state, _) => { - let actual_parachain_state = parachain_state::(); - if actual_parachain_state != Some(expected_parachain_state) { + CallType::AllFinalityAndDelivery(_, parachain_heads_info, _) | + CallType::ParachainFinalityAndDelivery(parachain_heads_info, _) => { + if !SubmitParachainHeadsHelper::::was_successful(¶chain_heads_info) { // we only refund relayer if all calls have updated chain state return Ok(()) } @@ -325,11 +302,10 @@ where _ => (), } - // check if messages have been delivered - let actual_messages_state = messages_state::(); - let pre_dispatch_messages_state = call_type.pre_dispatch_messages_state(); - if actual_messages_state == Some(pre_dispatch_messages_state) { - // we only refund relayer if all calls have updated chain state + // Check if the `ReceiveMessagesProof` call delivered at least some of the messages that + // it contained. If this happens, we consider the transaction "helpful" and refund it. + let messages_proof_info = call_type.receive_messages_proof_info(); + if !ReceiveMessagesProofHelper::::was_partially_successful(&messages_proof_info) { return Ok(()) } @@ -359,125 +335,22 @@ where } } -/// Extracts expected relay chain state from the call. -fn extract_expected_relay_chain_state(call: &CallOf) -> Option -where - R: GrandpaConfig, - GI: 'static, - >::BridgedChain: Chain, - CallOf: IsSubType, R>>, -{ - if let Some(GrandpaCall::::submit_finality_proof { ref finality_target, .. }) = - call.is_sub_type() - { - return Some(ExpectedRelayChainState { best_block_number: *finality_target.number() }) - } - None -} - -/// Extracts expected parachain state from the call. -fn extract_expected_parachain_state( - call: &CallOf, -) -> Option -where - R: GrandpaConfig + ParachainsConfig, - GI: 'static, - PI: 'static, - PID: Get, - >::BridgedChain: - Chain, - CallOf: IsSubType, R>>, -{ - if let Some(ParachainsCall::::submit_parachain_heads { - ref at_relay_block, - ref parachains, - .. - }) = call.is_sub_type() - { - if parachains.len() != 1 || parachains[0].0 != ParaId(PID::get()) { - return None - } - - return Some(ExpectedParachainState { at_relay_block_number: at_relay_block.0 }) - } - None -} - -/// Extracts messages state from the call. -fn extract_messages_state(call: &CallOf) -> Option -where - R: GrandpaConfig + MessagesConfig, - GI: 'static, - MI: 'static, - LID: Get, - CallOf: IsSubType, R>>, - >::SourceHeaderChain: SourceHeaderChain< - MessagesProof = FromBridgedChainMessagesProof>>, - >, -{ - if let Some(MessagesCall::::receive_messages_proof { ref proof, .. }) = - call.is_sub_type() - { - if LID::get() != proof.lane { - return None - } - - return Some(MessagesState { - best_nonce: MessagesPallet::::inbound_lane_data(proof.lane) - .last_delivered_nonce(), - }) - } - None -} - -/// Returns relay chain state that we are interested in. -fn relay_chain_state() -> Option -where - R: GrandpaConfig, - GI: 'static, - >::BridgedChain: Chain, -{ - GrandpaPallet::::best_finalized_number() - .map(|best_block_number| ExpectedRelayChainState { best_block_number }) -} - -/// Returns parachain state that we are interested in. -fn parachain_state() -> Option -where - R: ParachainsConfig, - PI: 'static, - PID: Get, -{ - ParachainsPallet::::best_parachain_info(ParaId(PID::get())).map(|para_info| { - ExpectedParachainState { - at_relay_block_number: para_info.best_head_hash.at_relay_block_number, - } - }) -} - -/// Returns messages state that we are interested in. -fn messages_state() -> Option -where - R: MessagesConfig, - MI: 'static, - LID: Get, -{ - Some(MessagesState { - best_nonce: MessagesPallet::::inbound_lane_data(LID::get()).last_delivered_nonce(), - }) -} - #[cfg(test)] mod tests { use super::*; use crate::{messages::target::FromBridgedChainMessagesProof, mock::*}; - use bp_messages::InboundLaneData; + use bp_messages::{InboundLaneData, MessageNonce}; use bp_parachains::{BestParaHeadHash, ParaInfo}; - use bp_polkadot_core::parachains::ParaHeadsProof; + use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; use bp_runtime::HeaderId; use bp_test_utils::make_default_justification; use frame_support::{assert_storage_noop, parameter_types, weights::Weight}; - use sp_runtime::{transaction_validity::InvalidTransaction, DispatchError}; + use pallet_bridge_grandpa::Call as GrandpaCall; + use pallet_bridge_messages::Call as MessagesCall; + use pallet_bridge_parachains::{Call as ParachainsCall, RelayBlockHash}; + use sp_runtime::{ + traits::Header as HeaderT, transaction_validity::InvalidTransaction, DispatchError, + }; parameter_types! { pub TestParachain: u32 = 1000; @@ -489,7 +362,6 @@ mod tests { (), (), (), - BridgeRejectObsoleteHeadersAndMessages, TestParachain, TestLaneId, TestRuntime, @@ -506,6 +378,7 @@ mod tests { fn initialize_environment( best_relay_header_number: RelayBlockNumber, parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_hash: ParaHash, best_delivered_message: MessageNonce, ) { let best_relay_header = HeaderId(best_relay_header_number, RelayBlockHash::default()); @@ -515,7 +388,7 @@ mod tests { let para_info = ParaInfo { best_head_hash: BestParaHeadHash { at_relay_block_number: parachain_head_at_relay_header_number, - head_hash: Default::default(), + head_hash: parachain_head_hash, }, next_imported_hash_position: 0, }; @@ -598,9 +471,17 @@ mod tests { PreDispatchData { relayer: relayer_account_at_this_chain(), call_type: CallType::AllFinalityAndDelivery( - ExpectedRelayChainState { best_block_number: 200 }, - ExpectedParachainState { at_relay_block_number: 200 }, - MessagesState { best_nonce: 100 }, + 200, + SubmitParachainHeadsInfo { + at_relay_block_number: 200, + para_id: ParaId(TestParachain::get()), + para_head_hash: [1u8; 32].into(), + }, + ReceiveMessagesProofInfo { + lane_id: TEST_LANE_ID, + best_proof_nonce: 200, + best_stored_nonce: 100, + }, ), } } @@ -609,8 +490,16 @@ mod tests { PreDispatchData { relayer: relayer_account_at_this_chain(), call_type: CallType::ParachainFinalityAndDelivery( - ExpectedParachainState { at_relay_block_number: 200 }, - MessagesState { best_nonce: 100 }, + SubmitParachainHeadsInfo { + at_relay_block_number: 200, + para_id: ParaId(TestParachain::get()), + para_head_hash: [1u8; 32].into(), + }, + ReceiveMessagesProofInfo { + lane_id: TEST_LANE_ID, + best_proof_nonce: 200, + best_stored_nonce: 100, + }, ), } } @@ -618,7 +507,11 @@ mod tests { fn delivery_pre_dispatch_data() -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_type: CallType::Delivery(MessagesState { best_nonce: 100 }), + call_type: CallType::Delivery(ReceiveMessagesProofInfo { + lane_id: TEST_LANE_ID, + best_proof_nonce: 200, + best_stored_nonce: 100, + }), } } @@ -678,7 +571,7 @@ mod tests { #[test] fn validate_allows_non_obsolete_transactions() { run_test(|| { - initialize_environment(100, 100, 100); + initialize_environment(100, 100, Default::default(), 100); assert_eq!(run_validate(message_delivery_call(200)), Ok(ValidTransaction::default()),); @@ -697,7 +590,7 @@ mod tests { #[test] fn ext_rejects_batch_with_obsolete_relay_chain_header() { run_test(|| { - initialize_environment(100, 100, 100); + initialize_environment(100, 100, Default::default(), 100); assert_eq!( run_pre_dispatch(all_finality_and_delivery_batch_call(100, 200, 200)), @@ -714,7 +607,7 @@ mod tests { #[test] fn ext_rejects_batch_with_obsolete_parachain_head() { run_test(|| { - initialize_environment(100, 100, 100); + initialize_environment(100, 100, Default::default(), 100); assert_eq!( run_pre_dispatch(all_finality_and_delivery_batch_call(101, 100, 200)), @@ -741,7 +634,7 @@ mod tests { #[test] fn ext_rejects_batch_with_obsolete_messages() { run_test(|| { - initialize_environment(100, 100, 100); + initialize_environment(100, 100, Default::default(), 100); assert_eq!( run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 100)), @@ -768,7 +661,7 @@ mod tests { #[test] fn pre_dispatch_parses_batch_with_relay_chain_and_parachain_headers() { run_test(|| { - initialize_environment(100, 100, 100); + initialize_environment(100, 100, Default::default(), 100); assert_eq!( run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), @@ -780,7 +673,7 @@ mod tests { #[test] fn pre_dispatch_parses_batch_with_parachain_header() { run_test(|| { - initialize_environment(100, 100, 100); + initialize_environment(100, 100, Default::default(), 100); assert_eq!( run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), @@ -792,7 +685,7 @@ mod tests { #[test] fn pre_dispatch_fails_to_parse_batch_with_multiple_parachain_headers() { run_test(|| { - initialize_environment(100, 100, 100); + initialize_environment(100, 100, Default::default(), 100); let call = RuntimeCall::Utility(UtilityCall::batch_all { calls: vec![ @@ -815,7 +708,7 @@ mod tests { #[test] fn pre_dispatch_parses_message_delivery_transaction() { run_test(|| { - initialize_environment(100, 100, 100); + initialize_environment(100, 100, Default::default(), 100); assert_eq!( run_pre_dispatch(message_delivery_call(200)), @@ -844,7 +737,7 @@ mod tests { #[test] fn post_dispatch_ignores_transaction_that_has_not_updated_relay_chain_state() { run_test(|| { - initialize_environment(100, 200, 200); + initialize_environment(100, 200, Default::default(), 200); assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); }); @@ -853,7 +746,7 @@ mod tests { #[test] fn post_dispatch_ignores_transaction_that_has_not_updated_parachain_state() { run_test(|| { - initialize_environment(200, 100, 200); + initialize_environment(200, 100, Default::default(), 200); assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); assert_storage_noop!(run_post_dispatch( @@ -866,7 +759,7 @@ mod tests { #[test] fn post_dispatch_ignores_transaction_that_has_not_delivered_any_messages() { run_test(|| { - initialize_environment(200, 200, 100); + initialize_environment(200, 200, Default::default(), 100); assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); assert_storage_noop!(run_post_dispatch( @@ -880,7 +773,7 @@ mod tests { #[test] fn post_dispatch_refunds_relayer_in_all_finality_batch() { run_test(|| { - initialize_environment(200, 200, 200); + initialize_environment(200, 200, [1u8; 32].into(), 200); run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(())); assert_eq!( @@ -896,7 +789,7 @@ mod tests { #[test] fn post_dispatch_refunds_relayer_in_parachain_finality_batch() { run_test(|| { - initialize_environment(200, 200, 200); + initialize_environment(200, 200, [1u8; 32].into(), 200); run_post_dispatch(Some(parachain_finality_pre_dispatch_data()), Ok(())); assert_eq!( @@ -912,7 +805,7 @@ mod tests { #[test] fn post_dispatch_refunds_relayer_in_message_delivery_transaction() { run_test(|| { - initialize_environment(200, 200, 200); + initialize_environment(200, 200, Default::default(), 200); run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(())); assert_eq!( diff --git a/modules/grandpa/README.md b/modules/grandpa/README.md index eaa62e8b206..27b4d2389c4 100644 --- a/modules/grandpa/README.md +++ b/modules/grandpa/README.md @@ -87,7 +87,7 @@ It'd be better for anyone (for chain and for submitters) to reject all transacti already known headers to the pallet. This way, we leave block space to other useful transactions and we don't charge concurrent submitters for their honest actions. -To deal with that, we have a [signed extension](./src/extension.rs) that may be added to the runtime. +To deal with that, we have a [signed extension](./src/call_ext) that may be added to the runtime. It does exactly what is required - rejects all transactions with already known headers. The submitter pays nothing for such transactions - they're simply removed from the transaction pool, when the block is built. diff --git a/modules/grandpa/src/call_ext.rs b/modules/grandpa/src/call_ext.rs new file mode 100644 index 00000000000..42c276f5f6c --- /dev/null +++ b/modules/grandpa/src/call_ext.rs @@ -0,0 +1,163 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{Config, Error, Pallet}; +use bp_runtime::BlockNumberOf; +use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; +use sp_runtime::{ + traits::Header, + transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, +}; + +/// Helper struct that provides methods for working with the `SubmitFinalityProof` call. +pub struct SubmitFinalityProofHelper, I: 'static> { + pub _phantom_data: sp_std::marker::PhantomData<(T, I)>, +} + +impl, I: 'static> SubmitFinalityProofHelper { + /// Check that the GRANDPA head provided by the `SubmitFinalityProof` is better than the best + /// one we know. + pub fn check_obsolete( + finality_target: BlockNumberOf, + ) -> Result<(), Error> { + let best_finalized = crate::BestFinalized::::get().ok_or_else(|| { + log::trace!( + target: crate::LOG_TARGET, + "Cannot finalize header {:?} because pallet is not yet initialized", + finality_target, + ); + >::NotInitialized + })?; + + if best_finalized.number() >= finality_target { + log::trace!( + target: crate::LOG_TARGET, + "Cannot finalize obsolete header: bundled {:?}, best {:?}", + finality_target, + best_finalized, + ); + + return Err(Error::::OldHeader) + } + + Ok(()) + } + + /// Check if the `SubmitFinalityProof` was successfully executed. + pub fn was_successful(finality_target: BlockNumberOf) -> bool { + match crate::BestFinalized::::get() { + Some(best_finalized) => best_finalized.number() == finality_target, + None => false, + } + } +} + +/// Trait representing a call that is a sub type of this pallet's call. +pub trait CallSubType, I: 'static>: + IsSubType, T>> +{ + /// Extract the finality target from a `SubmitParachainHeads` call. + fn submit_finality_proof_info(&self) -> Option> { + if let Some(crate::Call::::submit_finality_proof { finality_target, .. }) = + self.is_sub_type() + { + return Some(*finality_target.number()) + } + + None + } + + /// Validate Grandpa headers in order to avoid "mining" transactions that provide outdated + /// bridged chain headers. Without this validation, even honest relayers may lose their funds + /// if there are multiple relays running and submitting the same information. + fn check_obsolete_submit_finality_proof(&self) -> TransactionValidity + where + Self: Sized, + { + let finality_target = match self.submit_finality_proof_info() { + Some(finality_proof) => finality_proof, + _ => return Ok(ValidTransaction::default()), + }; + + match SubmitFinalityProofHelper::::check_obsolete(finality_target) { + Ok(_) => Ok(ValidTransaction::default()), + Err(Error::::OldHeader) => InvalidTransaction::Stale.into(), + Err(_) => InvalidTransaction::Call.into(), + } + } +} + +impl, I: 'static> CallSubType for T::RuntimeCall where + T::RuntimeCall: IsSubType, T>> +{ +} + +#[cfg(test)] +mod tests { + use crate::{ + call_ext::CallSubType, + mock::{run_test, test_header, RuntimeCall, TestNumber, TestRuntime}, + BestFinalized, + }; + use bp_runtime::HeaderId; + use bp_test_utils::make_default_justification; + + fn validate_block_submit(num: TestNumber) -> bool { + let bridge_grandpa_call = crate::Call::::submit_finality_proof { + finality_target: Box::new(test_header(num)), + justification: make_default_justification(&test_header(num)), + }; + RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa( + bridge_grandpa_call, + )) + .is_ok() + } + + fn sync_to_header_10() { + let header10_hash = sp_core::H256::default(); + BestFinalized::::put(HeaderId(10, header10_hash)); + } + + #[test] + fn extension_rejects_obsolete_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5 => tx is + // rejected + sync_to_header_10(); + assert!(!validate_block_submit(5)); + }); + } + + #[test] + fn extension_rejects_same_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_header_10(); + assert!(!validate_block_submit(10)); + }); + } + + #[test] + fn extension_accepts_new_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#15 => tx is + // accepted + sync_to_header_10(); + assert!(validate_block_submit(15)); + }); + } +} diff --git a/modules/grandpa/src/extension.rs b/modules/grandpa/src/extension.rs deleted file mode 100644 index 87075ee5d58..00000000000 --- a/modules/grandpa/src/extension.rs +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common 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. - -// Parity Bridges Common 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 Parity Bridges Common. If not, see . - -use crate::{Config, Pallet}; -use bp_runtime::FilterCall; -use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; -use sp_runtime::{ - traits::Header, - transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, -}; - -/// Validate Grandpa headers in order to avoid "mining" transactions that provide outdated -/// bridged chain headers. Without this validation, even honest relayers may lose their funds -/// if there are multiple relays running and submitting the same information. -impl< - Call: IsSubType, T>>, - T: frame_system::Config + Config, - I: 'static, - > FilterCall for Pallet -{ - fn validate(call: &::RuntimeCall) -> TransactionValidity { - let bundled_block_number = match call.is_sub_type() { - Some(crate::Call::::submit_finality_proof { ref finality_target, .. }) => - *finality_target.number(), - _ => return Ok(ValidTransaction::default()), - }; - - let best_finalized = crate::BestFinalized::::get(); - let best_finalized_number = match best_finalized { - Some(best_finalized_id) => best_finalized_id.number(), - None => return InvalidTransaction::Call.into(), - }; - - if best_finalized_number >= bundled_block_number { - log::trace!( - target: crate::LOG_TARGET, - "Rejecting obsolete bridged header: bundled {:?}, best {:?}", - bundled_block_number, - best_finalized_number, - ); - - return InvalidTransaction::Stale.into() - } - - Ok(ValidTransaction::default()) - } -} - -#[cfg(test)] -mod tests { - use super::FilterCall; - use crate::{ - mock::{run_test, test_header, RuntimeCall, TestNumber, TestRuntime}, - BestFinalized, - }; - use bp_runtime::HeaderId; - use bp_test_utils::make_default_justification; - - fn validate_block_submit(num: TestNumber) -> bool { - crate::Pallet::::validate(&RuntimeCall::Grandpa(crate::Call::< - TestRuntime, - (), - >::submit_finality_proof { - finality_target: Box::new(test_header(num)), - justification: make_default_justification(&test_header(num)), - })) - .is_ok() - } - - fn sync_to_header_10() { - let header10_hash = sp_core::H256::default(); - BestFinalized::::put(HeaderId(10, header10_hash)); - } - - #[test] - fn extension_rejects_obsolete_header() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#5 => tx is - // rejected - sync_to_header_10(); - assert!(!validate_block_submit(5)); - }); - } - - #[test] - fn extension_rejects_same_header() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#10 => tx is - // rejected - sync_to_header_10(); - assert!(!validate_block_submit(10)); - }); - } - - #[test] - fn extension_accepts_new_header() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#15 => tx is - // accepted - sync_to_header_10(); - assert!(validate_block_submit(15)); - }); - } -} diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index 6128030d090..c2b79434795 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -44,12 +44,15 @@ use bp_header_chain::{ }; use bp_runtime::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderId, HeaderOf, OwnedBridgeModule}; use finality_grandpa::voter_set::VoterSet; -use frame_support::{ensure, fail}; +use frame_support::{dispatch::PostDispatchInfo, ensure}; use sp_finality_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID}; -use sp_runtime::traits::{Header as HeaderT, Zero}; +use sp_runtime::{ + traits::{Header as HeaderT, Zero}, + SaturatedConversion, +}; use sp_std::{boxed::Box, convert::TryInto}; -mod extension; +mod call_ext; #[cfg(test)] mod mock; mod storage_types; @@ -61,6 +64,7 @@ pub mod weights; pub mod benchmarking; // Re-export in crate namespace for `construct_runtime!` +pub use call_ext::*; pub use pallet::*; pub use weights::WeightInfo; @@ -151,9 +155,9 @@ pub mod pallet { /// If successful in verification, it will write the target header to the underlying storage /// pallet. #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::submit_finality_proof( - justification.commit.precommits.len().try_into().unwrap_or(u32::MAX), - justification.votes_ancestries.len().try_into().unwrap_or(u32::MAX), + #[pallet::weight(::submit_finality_proof( + justification.commit.precommits.len().saturated_into(), + justification.votes_ancestries.len().saturated_into(), ))] pub fn submit_finality_proof( _origin: OriginFor, @@ -171,24 +175,10 @@ pub mod pallet { finality_target ); - let best_finalized_number = match BestFinalized::::get() { - Some(best_finalized_id) => best_finalized_id.number(), - None => { - log::error!( - target: LOG_TARGET, - "Cannot finalize header {:?} because pallet is not yet initialized", - finality_target, - ); - fail!(>::NotInitialized); - }, - }; - - // We do a quick check here to ensure that our header chain is making progress and isn't - // "travelling back in time" (which could be indicative of something bad, e.g a - // hard-fork). - ensure!(best_finalized_number < *number, >::OldHeader); + SubmitFinalityProofHelper::::check_obsolete(*number)?; let authority_set = >::get(); + let unused_proof_size = authority_set.unused_proof_size(); let set_id = authority_set.set_id; verify_justification::(&justification, hash, *number, authority_set.into())?; @@ -210,7 +200,18 @@ pub mod pallet { let is_mandatory_header = is_authorities_change_enacted; let pays_fee = if is_mandatory_header { Pays::No } else { Pays::Yes }; - Ok(pays_fee.into()) + // the proof size component of the call weight assumes that there are + // `MaxBridgedAuthorities` in the `CurrentAuthoritySet` (we use `MaxEncodedLen` + // estimation). But if their number is lower, then we may "refund" some `proof_size`, + // making proof smaller and leaving block space to other useful transactions + let pre_dispatch_weight = T::WeightInfo::submit_finality_proof( + justification.commit.precommits.len().saturated_into(), + justification.votes_ancestries.len().saturated_into(), + ); + let actual_weight = pre_dispatch_weight + .set_proof_size(pre_dispatch_weight.proof_size().saturating_sub(unused_proof_size)); + + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee }) } /// Bootstrap the bridge pallet with an initial header and authority set from which to sync. @@ -647,6 +648,7 @@ mod tests { assert_err, assert_noop, assert_ok, dispatch::PostDispatchInfo, storage::generator::StorageValue, }; + use sp_core::Get; use sp_runtime::{Digest, DigestItem, DispatchError}; fn initialize_substrate_bridge() { @@ -819,9 +821,27 @@ mod tests { fn succesfully_imports_header_with_valid_finality() { run_test(|| { initialize_substrate_bridge(); - let result = submit_finality_proof(1); + + let header_number = 1; + let header = test_header(header_number.into()); + let justification = make_default_justification(&header); + + let pre_dispatch_weight = ::WeightInfo::submit_finality_proof( + justification.commit.precommits.len().try_into().unwrap_or(u32::MAX), + justification.votes_ancestries.len().try_into().unwrap_or(u32::MAX), + ); + + let result = submit_finality_proof(header_number); assert_ok!(result); assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); + // our test config assumes 2048 max authorities and we are just using couple + let pre_dispatch_proof_size = pre_dispatch_weight.proof_size(); + let actual_proof_size = result.unwrap().actual_weight.unwrap().proof_size(); + assert!(actual_proof_size > 0); + assert!( + actual_proof_size < pre_dispatch_proof_size, + "Actual proof size {actual_proof_size} must be less than the pre-dispatch {pre_dispatch_proof_size}", + ); let header = test_header(1); assert_eq!(>::get().unwrap().1, header.hash()); @@ -1209,4 +1229,9 @@ mod tests { } generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); + + #[test] + fn maybe_headers_to_keep_returns_correct_value() { + assert_eq!(MaybeHeadersToKeep::::get(), Some(mock::HeadersToKeep::get())); + } } diff --git a/modules/grandpa/src/mock.rs b/modules/grandpa/src/mock.rs index ba6f176e823..8757093ee68 100644 --- a/modules/grandpa/src/mock.rs +++ b/modules/grandpa/src/mock.rs @@ -33,7 +33,7 @@ pub type TestNumber = crate::BridgedBlockNumber; type Block = frame_system::mocking::MockBlock; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -pub const MAX_BRIDGED_AUTHORITIES: u32 = 2048; +pub const MAX_BRIDGED_AUTHORITIES: u32 = 5; use crate as grandpa; diff --git a/modules/grandpa/src/storage_types.rs b/modules/grandpa/src/storage_types.rs index d930dbadbc6..732e72e2c0d 100644 --- a/modules/grandpa/src/storage_types.rs +++ b/modules/grandpa/src/storage_types.rs @@ -20,7 +20,7 @@ use crate::Config; use bp_header_chain::AuthoritySet; use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::{BoundedVec, RuntimeDebugNoBound}; +use frame_support::{traits::Get, BoundedVec, RuntimeDebugNoBound}; use scale_info::TypeInfo; use sp_finality_grandpa::{AuthorityId, AuthorityList, AuthorityWeight, SetId}; @@ -45,6 +45,24 @@ impl, I: 'static> StoredAuthoritySet { pub fn try_new(authorities: AuthorityList, set_id: SetId) -> Result { Ok(Self { authorities: TryFrom::try_from(authorities).map_err(drop)?, set_id }) } + + /// Returns number of bytes that may be subtracted from the PoV component of + /// `submit_finality_proof` call, because the actual authorities set is smaller than the maximal + /// configured. + /// + /// Maximal authorities set size is configured by the `MaxBridgedAuthorities` constant from + /// the pallet configuration. The PoV of the call includes the size of maximal authorities + /// count. If the actual size is smaller, we may subtract extra bytes from this component. + pub fn unused_proof_size(&self) -> u64 { + // we can only safely estimate bytes that are occupied by the authority data itself. We have + // no means here to compute PoV bytes, occupied by extra trie nodes or extra bytes in the + // whole set encoding + let single_authority_max_encoded_len = + <(AuthorityId, AuthorityWeight)>::max_encoded_len() as u64; + let extra_authorities = + T::MaxBridgedAuthorities::get().saturating_sub(self.authorities.len() as _); + single_authority_max_encoded_len.saturating_mul(extra_authorities as u64) + } } impl, I: 'static> PartialEq for StoredAuthoritySet { @@ -64,3 +82,41 @@ impl, I: 'static> From> for AuthoritySet { AuthoritySet { authorities: t.authorities.into(), set_id: t.set_id } } } + +#[cfg(test)] +mod tests { + use crate::mock::{TestRuntime, MAX_BRIDGED_AUTHORITIES}; + use bp_test_utils::authority_list; + + type StoredAuthoritySet = super::StoredAuthoritySet; + + #[test] + fn unused_proof_size_works() { + let authority_entry = authority_list().pop().unwrap(); + + // when we have exactly `MaxBridgedAuthorities` authorities + assert_eq!( + StoredAuthoritySet::try_new( + vec![authority_entry.clone(); MAX_BRIDGED_AUTHORITIES as usize], + 0, + ) + .unwrap() + .unused_proof_size(), + 0, + ); + + // when we have less than `MaxBridgedAuthorities` authorities + assert_eq!( + StoredAuthoritySet::try_new( + vec![authority_entry; MAX_BRIDGED_AUTHORITIES as usize - 1], + 0, + ) + .unwrap() + .unused_proof_size(), + 40, + ); + + // and we can't have more than `MaxBridgedAuthorities` authorities in the bounded vec, so + // no test for this case + } +} diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index eaa681df38f..70865cf17c5 100644 --- a/modules/messages/src/lib.rs +++ b/modules/messages/src/lib.rs @@ -583,8 +583,14 @@ pub mod pallet { /// Map of lane id => outbound lane data. #[pallet::storage] - pub type OutboundLanes, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, LaneId, OutboundLaneData, ValueQuery>; + pub type OutboundLanes, I: 'static = ()> = StorageMap< + Hasher = Blake2_128Concat, + Key = LaneId, + Value = OutboundLaneData, + QueryKind = ValueQuery, + OnEmpty = GetDefault, + MaxValues = MaybeOutboundLanesCount, + >; /// All queued outbound messages. #[pallet::storage] @@ -648,6 +654,15 @@ pub mod pallet { InboundLanes::::get(lane).0 } } + + /// Get-parameter that returns number of active outbound lanes that the pallet maintains. + pub struct MaybeOutboundLanesCount(PhantomData<(T, I)>); + + impl, I: 'static> Get> for MaybeOutboundLanesCount { + fn get() -> Option { + Some(T::ActiveOutboundLanes::get().len() as u32) + } + } } impl bp_messages::source_chain::MessagesBridge @@ -1962,4 +1977,12 @@ mod tests { MessagesOperatingMode::Basic(BasicOperatingMode::Normal), MessagesOperatingMode::Basic(BasicOperatingMode::Halted) ); + + #[test] + fn maybe_outbound_lanes_count_returns_correct_value() { + assert_eq!( + MaybeOutboundLanesCount::::get(), + Some(mock::ActiveOutboundLanes::get().len() as u32) + ); + } } diff --git a/modules/messages/src/mock.rs b/modules/messages/src/mock.rs index bd10de09c7f..084262c7905 100644 --- a/modules/messages/src/mock.rs +++ b/modules/messages/src/mock.rs @@ -34,7 +34,7 @@ use codec::{Decode, Encode}; use frame_support::{ parameter_types, traits::ConstU64, - weights::{RuntimeDbWeight, Weight}, + weights::{constants::RocksDbWeight, Weight}, }; use scale_info::TypeInfo; use sp_core::H256; @@ -92,9 +92,10 @@ parameter_types! { pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); - pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; } +pub type DbWeight = RocksDbWeight; + impl frame_system::Config for TestRuntime { type RuntimeOrigin = RuntimeOrigin; type Index = u64; diff --git a/modules/messages/src/weights.rs b/modules/messages/src/weights.rs index 0e258508885..f18285f3dfe 100644 --- a/modules/messages/src/weights.rs +++ b/modules/messages/src/weights.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for pallet_bridge_messages //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -72,8 +72,8 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) /// @@ -87,9 +87,9 @@ impl WeightInfo for BridgeWeight { fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: // Measured: `693` - // Estimated: `55198` - // Minimum execution time: 47_968 nanoseconds. - Weight::from_parts(48_937_000, 55198) + // Estimated: `54703` + // Minimum execution time: 50_655 nanoseconds. + Weight::from_parts(60_502_000, 54703) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -100,8 +100,8 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) /// @@ -115,9 +115,9 @@ impl WeightInfo for BridgeWeight { fn receive_two_messages_proof() -> Weight { // Proof Size summary in bytes: // Measured: `693` - // Estimated: `55198` - // Minimum execution time: 63_831 nanoseconds. - Weight::from_parts(85_093_000, 55198) + // Estimated: `54703` + // Minimum execution time: 58_861 nanoseconds. + Weight::from_parts(60_288_000, 54703) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -128,8 +128,8 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) /// @@ -143,9 +143,9 @@ impl WeightInfo for BridgeWeight { fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: // Measured: `693` - // Estimated: `55198` - // Minimum execution time: 53_775 nanoseconds. - Weight::from_parts(55_113_000, 55198) + // Estimated: `54703` + // Minimum execution time: 53_459 nanoseconds. + Weight::from_parts(54_577_000, 54703) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -156,8 +156,8 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) /// @@ -166,9 +166,9 @@ impl WeightInfo for BridgeWeight { fn receive_single_message_proof_1_kb() -> Weight { // Proof Size summary in bytes: // Measured: `618` - // Estimated: `54695` - // Minimum execution time: 54_314 nanoseconds. - Weight::from_parts(55_804_000, 54695) + // Estimated: `54200` + // Minimum execution time: 54_011 nanoseconds. + Weight::from_parts(55_573_000, 54200) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -179,8 +179,8 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) /// @@ -189,9 +189,9 @@ impl WeightInfo for BridgeWeight { fn receive_single_message_proof_16_kb() -> Weight { // Proof Size summary in bytes: // Measured: `618` - // Estimated: `54695` - // Minimum execution time: 103_050 nanoseconds. - Weight::from_parts(106_715_000, 54695) + // Estimated: `54200` + // Minimum execution time: 105_856 nanoseconds. + Weight::from_parts(109_112_000, 54200) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -202,13 +202,13 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) /// - /// Proof: BridgeRialtoMessages OutboundLanes (max_values: None, max_size: Some(44), added: - /// 2519, mode: MaxEncodedLen) + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: + /// 539, mode: MaxEncodedLen) /// /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) /// @@ -217,9 +217,9 @@ impl WeightInfo for BridgeWeight { fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: // Measured: `579` - // Estimated: `8094` - // Minimum execution time: 42_111 nanoseconds. - Weight::from_parts(43_168_000, 8094) + // Estimated: `5619` + // Minimum execution time: 40_894 nanoseconds. + Weight::from_parts(41_766_000, 5619) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -230,13 +230,13 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) /// - /// Proof: BridgeRialtoMessages OutboundLanes (max_values: None, max_size: Some(44), added: - /// 2519, mode: MaxEncodedLen) + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: + /// 539, mode: MaxEncodedLen) /// /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) /// @@ -245,9 +245,9 @@ impl WeightInfo for BridgeWeight { fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: // Measured: `596` - // Estimated: `8094` - // Minimum execution time: 40_094 nanoseconds. - Weight::from_parts(41_140_000, 8094) + // Estimated: `5619` + // Minimum execution time: 39_996 nanoseconds. + Weight::from_parts(41_452_000, 5619) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -258,13 +258,13 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) /// - /// Proof: BridgeRialtoMessages OutboundLanes (max_values: None, max_size: Some(44), added: - /// 2519, mode: MaxEncodedLen) + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: + /// 539, mode: MaxEncodedLen) /// /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) /// @@ -273,9 +273,9 @@ impl WeightInfo for BridgeWeight { fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: // Measured: `596` - // Estimated: `10629` - // Minimum execution time: 42_498 nanoseconds. - Weight::from_parts(43_494_000, 10629) + // Estimated: `8154` + // Minimum execution time: 42_281 nanoseconds. + Weight::from_parts(43_593_000, 8154) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -290,8 +290,8 @@ impl WeightInfo for () { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) /// @@ -305,9 +305,9 @@ impl WeightInfo for () { fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: // Measured: `693` - // Estimated: `55198` - // Minimum execution time: 47_968 nanoseconds. - Weight::from_parts(48_937_000, 55198) + // Estimated: `54703` + // Minimum execution time: 50_655 nanoseconds. + Weight::from_parts(60_502_000, 54703) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -318,8 +318,8 @@ impl WeightInfo for () { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) /// @@ -333,9 +333,9 @@ impl WeightInfo for () { fn receive_two_messages_proof() -> Weight { // Proof Size summary in bytes: // Measured: `693` - // Estimated: `55198` - // Minimum execution time: 63_831 nanoseconds. - Weight::from_parts(85_093_000, 55198) + // Estimated: `54703` + // Minimum execution time: 58_861 nanoseconds. + Weight::from_parts(60_288_000, 54703) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -346,8 +346,8 @@ impl WeightInfo for () { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) /// @@ -361,9 +361,9 @@ impl WeightInfo for () { fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: // Measured: `693` - // Estimated: `55198` - // Minimum execution time: 53_775 nanoseconds. - Weight::from_parts(55_113_000, 55198) + // Estimated: `54703` + // Minimum execution time: 53_459 nanoseconds. + Weight::from_parts(54_577_000, 54703) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -374,8 +374,8 @@ impl WeightInfo for () { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) /// @@ -384,9 +384,9 @@ impl WeightInfo for () { fn receive_single_message_proof_1_kb() -> Weight { // Proof Size summary in bytes: // Measured: `618` - // Estimated: `54695` - // Minimum execution time: 54_314 nanoseconds. - Weight::from_parts(55_804_000, 54695) + // Estimated: `54200` + // Minimum execution time: 54_011 nanoseconds. + Weight::from_parts(55_573_000, 54200) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -397,8 +397,8 @@ impl WeightInfo for () { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) /// @@ -407,9 +407,9 @@ impl WeightInfo for () { fn receive_single_message_proof_16_kb() -> Weight { // Proof Size summary in bytes: // Measured: `618` - // Estimated: `54695` - // Minimum execution time: 103_050 nanoseconds. - Weight::from_parts(106_715_000, 54695) + // Estimated: `54200` + // Minimum execution time: 105_856 nanoseconds. + Weight::from_parts(109_112_000, 54200) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -420,13 +420,13 @@ impl WeightInfo for () { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) /// - /// Proof: BridgeRialtoMessages OutboundLanes (max_values: None, max_size: Some(44), added: - /// 2519, mode: MaxEncodedLen) + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: + /// 539, mode: MaxEncodedLen) /// /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) /// @@ -435,9 +435,9 @@ impl WeightInfo for () { fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: // Measured: `579` - // Estimated: `8094` - // Minimum execution time: 42_111 nanoseconds. - Weight::from_parts(43_168_000, 8094) + // Estimated: `5619` + // Minimum execution time: 40_894 nanoseconds. + Weight::from_parts(41_766_000, 5619) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -448,13 +448,13 @@ impl WeightInfo for () { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) /// - /// Proof: BridgeRialtoMessages OutboundLanes (max_values: None, max_size: Some(44), added: - /// 2519, mode: MaxEncodedLen) + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: + /// 539, mode: MaxEncodedLen) /// /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) /// @@ -463,9 +463,9 @@ impl WeightInfo for () { fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: // Measured: `596` - // Estimated: `8094` - // Minimum execution time: 40_094 nanoseconds. - Weight::from_parts(41_140_000, 8094) + // Estimated: `5619` + // Minimum execution time: 39_996 nanoseconds. + Weight::from_parts(41_452_000, 5619) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -476,13 +476,13 @@ impl WeightInfo for () { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) /// - /// Proof: BridgeRialtoMessages OutboundLanes (max_values: None, max_size: Some(44), added: - /// 2519, mode: MaxEncodedLen) + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: + /// 539, mode: MaxEncodedLen) /// /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) /// @@ -491,9 +491,9 @@ impl WeightInfo for () { fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: // Measured: `596` - // Estimated: `10629` - // Minimum execution time: 42_498 nanoseconds. - Weight::from_parts(43_494_000, 10629) + // Estimated: `8154` + // Minimum execution time: 42_281 nanoseconds. + Weight::from_parts(43_593_000, 8154) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } diff --git a/modules/messages/src/weights_ext.rs b/modules/messages/src/weights_ext.rs index c764f5b8f65..5598706b5db 100644 --- a/modules/messages/src/weights_ext.rs +++ b/modules/messages/src/weights_ext.rs @@ -36,21 +36,43 @@ pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; /// Ensure that weights from `WeightInfoExt` implementation are looking correct. pub fn ensure_weights_are_correct() { + // all components of weight formulae must have zero `proof_size`, because the `proof_size` is + // benchmarked using `MaxEncodedLen` approach and there are no components that cause additional + // db reads + // verify `receive_messages_proof` weight components - assert_ne!(W::receive_messages_proof_overhead(), Weight::zero()); - assert_ne!(W::receive_messages_proof_messages_overhead(1), Weight::zero()); - assert_ne!(W::receive_messages_proof_outbound_lane_state_overhead(), Weight::zero()); - assert_ne!(W::storage_proof_size_overhead(1), Weight::zero()); + assert_ne!(W::receive_messages_proof_overhead().ref_time(), 0); + assert_ne!(W::receive_messages_proof_overhead().proof_size(), 0); + // W::receive_messages_proof_messages_overhead(1).ref_time() may be zero because: + // the message processing code (`InboundLane::receive_message`) is minimal and may not be + // accounted by our benchmarks + assert_eq!(W::receive_messages_proof_messages_overhead(1).proof_size(), 0); + // W::receive_messages_proof_outbound_lane_state_overhead().ref_time() may be zero because: + // the outbound lane state processing code (`InboundLane::receive_state_update`) is minimal and + // may not be accounted by our benchmarks + assert_eq!(W::receive_messages_proof_outbound_lane_state_overhead().proof_size(), 0); + assert_ne!(W::storage_proof_size_overhead(1).ref_time(), 0); + assert_eq!(W::storage_proof_size_overhead(1).proof_size(), 0); // verify `receive_messages_delivery_proof` weight components - assert_ne!(W::receive_messages_delivery_proof_overhead(), Weight::zero()); - assert_ne!(W::storage_proof_size_overhead(1), Weight::zero()); + assert_ne!(W::receive_messages_delivery_proof_overhead().ref_time(), 0); + assert_ne!(W::receive_messages_delivery_proof_overhead().proof_size(), 0); + // W::receive_messages_delivery_proof_messages_overhead(1).ref_time() may be zero because: + // there's no code that iterates over confirmed messages in confirmation transaction + assert_eq!(W::receive_messages_delivery_proof_messages_overhead(1).proof_size(), 0); + assert_ne!(W::receive_messages_delivery_proof_relayers_overhead(1).ref_time(), 0); + // W::receive_messages_delivery_proof_relayers_overhead(1).proof_size() is an exception + // it may or may not cause additional db reads, so proof size may vary + assert_ne!(W::storage_proof_size_overhead(1).ref_time(), 0); + assert_eq!(W::storage_proof_size_overhead(1).proof_size(), 0); // verify `receive_message_proof` weight let receive_messages_proof_weight = W::receive_messages_proof_weight(&PreComputedSize(1), 10, Weight::zero()); assert_ne!(receive_messages_proof_weight.ref_time(), 0); assert_ne!(receive_messages_proof_weight.proof_size(), 0); + messages_proof_size_does_not_affect_proof_size::(); + messages_count_does_not_affect_proof_size::(); // verify `receive_message_proof` weight let receive_messages_delivery_proof_weight = W::receive_messages_delivery_proof_weight( @@ -59,6 +81,8 @@ pub fn ensure_weights_are_correct() { ); assert_ne!(receive_messages_delivery_proof_weight.ref_time(), 0); assert_ne!(receive_messages_delivery_proof_weight.proof_size(), 0); + messages_delivery_proof_size_does_not_affect_proof_size::(); + total_messages_in_delivery_proof_does_not_affect_proof_size::(); } /// Ensure that we're able to receive maximal (by-size and by-weight) message from other chain. @@ -122,6 +146,116 @@ pub fn ensure_able_to_receive_confirmation( ); } +/// Panics if `proof_size` of message delivery call depends on the message proof size. +fn messages_proof_size_does_not_affect_proof_size() { + let dispatch_weight = Weight::zero(); + let weight_when_proof_size_is_8k = + W::receive_messages_proof_weight(&PreComputedSize(8 * 1024), 1, dispatch_weight); + let weight_when_proof_size_is_16k = + W::receive_messages_proof_weight(&PreComputedSize(16 * 1024), 1, dispatch_weight); + + ensure_weight_components_are_not_zero(weight_when_proof_size_is_8k); + ensure_weight_components_are_not_zero(weight_when_proof_size_is_16k); + ensure_proof_size_is_the_same( + weight_when_proof_size_is_8k, + weight_when_proof_size_is_16k, + "Messages proof size does not affect values that we read from our storage", + ); +} + +/// Panics if `proof_size` of message delivery call depends on the messages count. +/// +/// In practice, it will depend on the messages count, because most probably every +/// message will read something from db during dispatch. But this must be accounted +/// by the `dispatch_weight`. +fn messages_count_does_not_affect_proof_size() { + let messages_proof_size = PreComputedSize(8 * 1024); + let dispatch_weight = Weight::zero(); + let weight_of_one_incoming_message = + W::receive_messages_proof_weight(&messages_proof_size, 1, dispatch_weight); + let weight_of_two_incoming_messages = + W::receive_messages_proof_weight(&messages_proof_size, 2, dispatch_weight); + + ensure_weight_components_are_not_zero(weight_of_one_incoming_message); + ensure_weight_components_are_not_zero(weight_of_two_incoming_messages); + ensure_proof_size_is_the_same( + weight_of_one_incoming_message, + weight_of_two_incoming_messages, + "Number of same-lane incoming messages does not affect values that we read from our storage", + ); +} + +/// Panics if `proof_size` of delivery confirmation call depends on the delivery proof size. +fn messages_delivery_proof_size_does_not_affect_proof_size() { + let relayers_state = UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }; + let weight_when_proof_size_is_8k = + W::receive_messages_delivery_proof_weight(&PreComputedSize(8 * 1024), &relayers_state); + let weight_when_proof_size_is_16k = + W::receive_messages_delivery_proof_weight(&PreComputedSize(16 * 1024), &relayers_state); + + ensure_weight_components_are_not_zero(weight_when_proof_size_is_8k); + ensure_weight_components_are_not_zero(weight_when_proof_size_is_16k); + ensure_proof_size_is_the_same( + weight_when_proof_size_is_8k, + weight_when_proof_size_is_16k, + "Messages delivery proof size does not affect values that we read from our storage", + ); +} + +/// Panics if `proof_size` of delivery confirmation call depends on the number of confirmed +/// messages. +fn total_messages_in_delivery_proof_does_not_affect_proof_size() { + let proof_size = PreComputedSize(8 * 1024); + let weight_when_1k_messages_confirmed = W::receive_messages_delivery_proof_weight( + &proof_size, + &UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1024, + last_delivered_nonce: 1, + }, + ); + let weight_when_2k_messages_confirmed = W::receive_messages_delivery_proof_weight( + &proof_size, + &UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 2048, + last_delivered_nonce: 1, + }, + ); + + ensure_weight_components_are_not_zero(weight_when_1k_messages_confirmed); + ensure_weight_components_are_not_zero(weight_when_2k_messages_confirmed); + ensure_proof_size_is_the_same( + weight_when_1k_messages_confirmed, + weight_when_2k_messages_confirmed, + "More messages in delivery proof does not affect values that we read from our storage", + ); +} + +/// Panics if either Weight' `proof_size` or `ref_time` are zero. +fn ensure_weight_components_are_not_zero(weight: Weight) { + assert_ne!(weight.ref_time(), 0); + assert_ne!(weight.proof_size(), 0); +} + +/// Panics if `proof_size` of `weight1` is not equal to `proof_size` of `weight2`. +fn ensure_proof_size_is_the_same(weight1: Weight, weight2: Weight, msg: &str) { + assert_eq!( + weight1.proof_size(), + weight2.proof_size(), + "{msg}: {} must be equal to {}", + weight1.proof_size(), + weight2.proof_size(), + ); +} + /// Extended weight info. pub trait WeightInfoExt: WeightInfo { /// Size of proof that is already included in the single message delivery weight. @@ -282,3 +416,14 @@ impl WeightInfoExt for crate::weights::BridgeWeight EXTRA_STORAGE_PROOF_SIZE } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{mock::TestRuntime, weights::BridgeWeight}; + + #[test] + fn ensure_default_weights_are_correct() { + ensure_weights_are_correct::>(); + } +} diff --git a/modules/parachains/README.md b/modules/parachains/README.md index 1bd91a3ba77..5982c65ad31 100644 --- a/modules/parachains/README.md +++ b/modules/parachains/README.md @@ -71,7 +71,7 @@ It'd be better for anyone (for chain and for submitters) to reject all transacti already known parachain heads to the pallet. This way, we leave block space to other useful transactions and we don't charge concurrent submitters for their honest actions. -To deal with that, we have a [signed extension](./src/extension.rs) that may be added to the runtime. +To deal with that, we have a [signed extension](./src/call_ext) that may be added to the runtime. It does exactly what is required - rejects all transactions with already known heads. The submitter pays nothing for such transactions - they're simply removed from the transaction pool, when the block is built. diff --git a/modules/parachains/src/call_ext.rs b/modules/parachains/src/call_ext.rs new file mode 100644 index 00000000000..41649336579 --- /dev/null +++ b/modules/parachains/src/call_ext.rs @@ -0,0 +1,243 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{Config, Pallet, RelayBlockNumber}; +use bp_parachains::BestParaHeadHash; +use bp_polkadot_core::parachains::{ParaHash, ParaId}; +use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; +use sp_runtime::{ + transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, + RuntimeDebug, +}; + +/// Info about a `SubmitParachainHeads` call which tries to update a single parachain. +#[derive(Copy, Clone, PartialEq, RuntimeDebug)] +pub struct SubmitParachainHeadsInfo { + pub at_relay_block_number: RelayBlockNumber, + pub para_id: ParaId, + pub para_head_hash: ParaHash, +} + +/// Helper struct that provides methods for working with the `SubmitParachainHeads` call. +pub struct SubmitParachainHeadsHelper, I: 'static> { + pub _phantom_data: sp_std::marker::PhantomData<(T, I)>, +} + +impl, I: 'static> SubmitParachainHeadsHelper { + /// Check if the para head provided by the `SubmitParachainHeads` is better than the best one + /// we know. + pub fn is_obsolete(update: &SubmitParachainHeadsInfo) -> bool { + let stored_best_head = match crate::ParasInfo::::get(update.para_id) { + Some(stored_best_head) => stored_best_head, + None => return false, + }; + + if stored_best_head.best_head_hash.at_relay_block_number >= update.at_relay_block_number { + log::trace!( + target: crate::LOG_TARGET, + "The parachain head can't be updated. The parachain head for {:?} \ + was already updated at better relay chain block {} >= {}.", + update.para_id, + stored_best_head.best_head_hash.at_relay_block_number, + update.at_relay_block_number + ); + return true + } + + if stored_best_head.best_head_hash.head_hash == update.para_head_hash { + log::trace!( + target: crate::LOG_TARGET, + "The parachain head can't be updated. The parachain head hash for {:?} \ + was already updated to {} at block {} < {}.", + update.para_id, + update.para_head_hash, + stored_best_head.best_head_hash.at_relay_block_number, + update.at_relay_block_number + ); + return true + } + + false + } + + /// Check if the `SubmitParachainHeads` was successfully executed. + pub fn was_successful(update: &SubmitParachainHeadsInfo) -> bool { + match crate::ParasInfo::::get(update.para_id) { + Some(stored_best_head) => + stored_best_head.best_head_hash == + BestParaHeadHash { + at_relay_block_number: update.at_relay_block_number, + head_hash: update.para_head_hash, + }, + None => false, + } + } +} + +/// Trait representing a call that is a sub type of this pallet's call. +pub trait CallSubType, I: 'static>: + IsSubType, T>> +{ + /// Create a new instance of `SubmitParachainHeadsInfo` from a `SubmitParachainHeads` call with + /// one single parachain entry. + fn one_entry_submit_parachain_heads_info(&self) -> Option { + if let Some(crate::Call::::submit_parachain_heads { + ref at_relay_block, + ref parachains, + .. + }) = self.is_sub_type() + { + if let &[(para_id, para_head_hash)] = parachains.as_slice() { + return Some(SubmitParachainHeadsInfo { + at_relay_block_number: at_relay_block.0, + para_id, + para_head_hash, + }) + } + } + + None + } + + /// Create a new instance of `SubmitParachainHeadsInfo` from a `SubmitParachainHeads` call with + /// one single parachain entry, if the entry is for the provided parachain id. + fn submit_parachain_heads_info_for(&self, para_id: u32) -> Option { + self.one_entry_submit_parachain_heads_info() + .filter(|update| update.para_id.0 == para_id) + } + + /// Validate parachain heads in order to avoid "mining" transactions that provide + /// outdated bridged parachain heads. Without this validation, even honest relayers + /// may lose their funds if there are multiple relays running and submitting the + /// same information. + /// + /// This validation only works with transactions that are updating single parachain + /// head. We can't use unbounded validation - it may take too long and either break + /// block production, or "eat" significant portion of block production time literally + /// for nothing. In addition, the single-parachain-head-per-transaction is how the + /// pallet will be used in our environment. + fn check_obsolete_submit_parachain_heads(&self) -> TransactionValidity + where + Self: Sized, + { + let update = match self.one_entry_submit_parachain_heads_info() { + Some(update) => update, + None => return Ok(ValidTransaction::default()), + }; + + if SubmitParachainHeadsHelper::::is_obsolete(&update) { + return InvalidTransaction::Stale.into() + } + + Ok(ValidTransaction::default()) + } +} + +impl CallSubType for T::RuntimeCall +where + T: Config, + T::RuntimeCall: IsSubType, T>>, +{ +} + +#[cfg(test)] +mod tests { + use crate::{ + mock::{run_test, RuntimeCall, TestRuntime}, + CallSubType, ParaInfo, ParasInfo, RelayBlockNumber, + }; + use bp_parachains::BestParaHeadHash; + use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; + + fn validate_submit_parachain_heads( + num: RelayBlockNumber, + parachains: Vec<(ParaId, ParaHash)>, + ) -> bool { + RuntimeCall::Parachains(crate::Call::::submit_parachain_heads { + at_relay_block: (num, Default::default()), + parachains, + parachain_heads_proof: ParaHeadsProof(Vec::new()), + }) + .check_obsolete_submit_parachain_heads() + .is_ok() + } + + fn sync_to_relay_header_10() { + ParasInfo::::insert( + ParaId(1), + ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 10, + head_hash: [1u8; 32].into(), + }, + next_imported_hash_position: 0, + }, + ); + } + + #[test] + fn extension_rejects_header_from_the_obsolete_relay_block() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(5, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_rejects_header_from_the_same_relay_block() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(10, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_rejects_header_from_new_relay_block_with_same_hash() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(20, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_accepts_new_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#15 => tx is + // accepted + sync_to_relay_header_10(); + assert!(validate_submit_parachain_heads(15, vec![(ParaId(1), [2u8; 32].into())])); + }); + } + + #[test] + fn extension_accepts_if_more_than_one_parachain_is_submitted() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5, but another + // parachain head is also supplied => tx is accepted + sync_to_relay_header_10(); + assert!(validate_submit_parachain_heads( + 5, + vec![(ParaId(1), [1u8; 32].into()), (ParaId(2), [1u8; 32].into())] + )); + }); + } +} diff --git a/modules/parachains/src/extension.rs b/modules/parachains/src/extension.rs deleted file mode 100644 index 5c2f54257ff..00000000000 --- a/modules/parachains/src/extension.rs +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common 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. - -// Parity Bridges Common 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 Parity Bridges Common. If not, see . - -use crate::{Config, Pallet, RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; -use bp_runtime::FilterCall; -use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; -use sp_runtime::transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}; - -/// Validate parachain heads in order to avoid "mining" transactions that provide -/// outdated bridged parachain heads. Without this validation, even honest relayers -/// may lose their funds if there are multiple relays running and submitting the -/// same information. -/// -/// This validation only works with transactions that are updating single parachain -/// head. We can't use unbounded validation - it may take too long and either break -/// block production, or "eat" significant portion of block production time literally -/// for nothing. In addition, the single-parachain-head-per-transaction is how the -/// pallet will be used in our environment. -impl< - Call: IsSubType, T>>, - T: frame_system::Config + Config, - I: 'static, - > FilterCall for Pallet -where - >::BridgedChain: - bp_runtime::Chain< - BlockNumber = RelayBlockNumber, - Hash = RelayBlockHash, - Hasher = RelayBlockHasher, - >, -{ - fn validate(call: &Call) -> TransactionValidity { - let (updated_at_relay_block_number, parachains) = match call.is_sub_type() { - Some(crate::Call::::submit_parachain_heads { - ref at_relay_block, - ref parachains, - .. - }) => (at_relay_block.0, parachains), - _ => return Ok(ValidTransaction::default()), - }; - let (parachain, parachain_head_hash) = match parachains.as_slice() { - &[(parachain, parachain_head_hash)] => (parachain, parachain_head_hash), - _ => return Ok(ValidTransaction::default()), - }; - - let maybe_stored_best_head = crate::ParasInfo::::get(parachain); - let is_valid = Self::validate_updated_parachain_head( - parachain, - &maybe_stored_best_head, - updated_at_relay_block_number, - parachain_head_hash, - "Rejecting obsolete parachain-head transaction", - ); - - if is_valid { - Ok(ValidTransaction::default()) - } else { - InvalidTransaction::Stale.into() - } - } -} - -#[cfg(test)] -mod tests { - use crate::{ - extension::FilterCall, - mock::{run_test, RuntimeCall, TestRuntime}, - ParaInfo, ParasInfo, RelayBlockNumber, - }; - use bp_parachains::BestParaHeadHash; - use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; - - fn validate_submit_parachain_heads( - num: RelayBlockNumber, - parachains: Vec<(ParaId, ParaHash)>, - ) -> bool { - crate::Pallet::::validate(&RuntimeCall::Parachains(crate::Call::< - TestRuntime, - (), - >::submit_parachain_heads { - at_relay_block: (num, Default::default()), - parachains, - parachain_heads_proof: ParaHeadsProof(Vec::new()), - })) - .is_ok() - } - - fn sync_to_relay_header_10() { - ParasInfo::::insert( - ParaId(1), - ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: 10, - head_hash: [1u8; 32].into(), - }, - next_imported_hash_position: 0, - }, - ); - } - - #[test] - fn extension_rejects_header_from_the_obsolete_relay_block() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#5 => tx is - // rejected - sync_to_relay_header_10(); - assert!(!validate_submit_parachain_heads(5, vec![(ParaId(1), [1u8; 32].into())])); - }); - } - - #[test] - fn extension_rejects_header_from_the_same_relay_block() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#10 => tx is - // rejected - sync_to_relay_header_10(); - assert!(!validate_submit_parachain_heads(10, vec![(ParaId(1), [1u8; 32].into())])); - }); - } - - #[test] - fn extension_rejects_header_from_new_relay_block_with_same_hash() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#10 => tx is - // rejected - sync_to_relay_header_10(); - assert!(!validate_submit_parachain_heads(20, vec![(ParaId(1), [1u8; 32].into())])); - }); - } - - #[test] - fn extension_accepts_new_header() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#15 => tx is - // accepted - sync_to_relay_header_10(); - assert!(validate_submit_parachain_heads(15, vec![(ParaId(1), [2u8; 32].into())])); - }); - } - - #[test] - fn extension_accepts_if_more_than_one_parachain_is_submitted() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#5, but another - // parachain head is also supplied => tx is accepted - sync_to_relay_header_10(); - assert!(validate_submit_parachain_heads( - 5, - vec![(ParaId(1), [1u8; 32].into()), (ParaId(2), [1u8; 32].into())] - )); - }); - } -} diff --git a/modules/parachains/src/lib.rs b/modules/parachains/src/lib.rs index e2f2cbf2d49..6875a3690f6 100644 --- a/modules/parachains/src/lib.rs +++ b/modules/parachains/src/lib.rs @@ -41,6 +41,7 @@ use bp_runtime::HeaderOf; use codec::Encode; // Re-export in crate namespace for `construct_runtime!`. +pub use call_ext::*; pub use pallet::*; pub mod weights; @@ -49,7 +50,7 @@ pub mod weights_ext; #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; -mod extension; +mod call_ext; #[cfg(test)] mod mock; @@ -136,10 +137,30 @@ pub mod pallet { BridgeModule(bp_runtime::OwnedBridgeModuleError), } + /// Convenience trait for defining `BridgedChain` bounds. + pub trait BoundedBridgeGrandpaConfig: + pallet_bridge_grandpa::Config + { + type BridgedRelayChain: Chain< + BlockNumber = RelayBlockNumber, + Hash = RelayBlockHash, + Hasher = RelayBlockHasher, + >; + } + + impl BoundedBridgeGrandpaConfig for T + where + T: pallet_bridge_grandpa::Config, + T::BridgedChain: + Chain, + { + type BridgedRelayChain = T::BridgedChain; + } + #[pallet::config] #[pallet::disable_frame_system_supertrait_check] pub trait Config: - pallet_bridge_grandpa::Config + BoundedBridgeGrandpaConfig { /// The overarching event type. type RuntimeEvent: From> @@ -221,27 +242,39 @@ pub mod pallet { /// - the head of the `ImportedParaHashes` ring buffer #[pallet::storage] pub type ParasInfo, I: 'static = ()> = StorageMap< - _, - ::Hasher, - ::Key, - ::Value, + Hasher = ::Hasher, + Key = ::Key, + Value = ::Value, + QueryKind = OptionQuery, + OnEmpty = GetDefault, + MaxValues = MaybeMaxParachains, >; /// State roots of parachain heads which have been imported into the pallet. #[pallet::storage] pub type ImportedParaHeads, I: 'static = ()> = StorageDoubleMap< - _, - ::Hasher1, - ::Key1, - ::Hasher2, - ::Key2, - StoredParaHeadDataOf, + Hasher1 = ::Hasher1, + Key1 = ::Key1, + Hasher2 = ::Hasher2, + Key2 = ::Key2, + Value = StoredParaHeadDataOf, + QueryKind = OptionQuery, + OnEmpty = GetDefault, + MaxValues = MaybeMaxTotalParachainHashes, >; /// A ring buffer of imported parachain head hashes. Ordered by the insertion time. #[pallet::storage] - pub(super) type ImportedParaHashes, I: 'static = ()> = - StorageDoubleMap<_, Blake2_128Concat, ParaId, Twox64Concat, u32, ParaHash>; + pub(super) type ImportedParaHashes, I: 'static = ()> = StorageDoubleMap< + Hasher1 = Blake2_128Concat, + Key1 = ParaId, + Hasher2 = Twox64Concat, + Key2 = u32, + Value = ParaHash, + QueryKind = OptionQuery, + OnEmpty = GetDefault, + MaxValues = MaybeMaxTotalParachainHashes, + >; #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] @@ -255,15 +288,7 @@ pub mod pallet { } #[pallet::call] - impl, I: 'static> Pallet - where - >::BridgedChain: - bp_runtime::Chain< - BlockNumber = RelayBlockNumber, - Hash = RelayBlockHash, - Hasher = RelayBlockHasher, - >, - { + impl, I: 'static> Pallet { /// Submit proof of one or several parachain heads. /// /// The proof is supposed to be proof of some `Heads` entries from the @@ -471,89 +496,38 @@ pub mod pallet { storage.read_and_decode_value(parachain_head_key.0.as_ref()) } - /// Check if para head has been already updated at better relay chain block. - /// Without this check, we may import heads in random order. - /// - /// Returns `true` if the pallet is ready to import given parachain head. - /// Returns `false` if the pallet already knows the same or better parachain head. - #[must_use] - pub fn validate_updated_parachain_head( - parachain: ParaId, - maybe_stored_best_head: &Option, - updated_at_relay_block_number: RelayBlockNumber, - updated_head_hash: ParaHash, - err_log_prefix: &str, - ) -> bool { - let stored_best_head = match maybe_stored_best_head { - Some(stored_best_head) => stored_best_head, - None => return true, - }; - - if stored_best_head.best_head_hash.at_relay_block_number >= - updated_at_relay_block_number - { - log::trace!( - target: LOG_TARGET, - "{}. The parachain head for {:?} was already updated at better relay chain block {} >= {}.", - err_log_prefix, - parachain, - stored_best_head.best_head_hash.at_relay_block_number, - updated_at_relay_block_number - ); - return false - } - - if stored_best_head.best_head_hash.head_hash == updated_head_hash { - log::trace!( - target: LOG_TARGET, - "{}. The parachain head hash for {:?} was already updated to {} at block {} < {}.", - err_log_prefix, - parachain, - updated_head_hash, - stored_best_head.best_head_hash.at_relay_block_number, - updated_at_relay_block_number - ); - return false - } - - true - } - /// Try to update parachain head. pub(super) fn update_parachain_head( parachain: ParaId, stored_best_head: Option, - updated_at_relay_block_number: RelayBlockNumber, - updated_head_data: ParaStoredHeaderData, - updated_head_hash: ParaHash, + new_at_relay_block_number: RelayBlockNumber, + new_head_data: ParaStoredHeaderData, + new_head_hash: ParaHash, ) -> Result { // check if head has been already updated at better relay chain block. Without this // check, we may import heads in random order - let err_log_prefix = "The parachain head can't be updated"; - let is_valid = Self::validate_updated_parachain_head( - parachain, - &stored_best_head, - updated_at_relay_block_number, - updated_head_hash, - err_log_prefix, - ); - if !is_valid { + let update = SubmitParachainHeadsInfo { + at_relay_block_number: new_at_relay_block_number, + para_id: parachain, + para_head_hash: new_head_hash, + }; + if SubmitParachainHeadsHelper::::is_obsolete(&update) { Self::deposit_event(Event::RejectedObsoleteParachainHead { parachain, - parachain_head_hash: updated_head_hash, + parachain_head_hash: new_head_hash, }); return Err(()) } // verify that the parachain head data size is <= `MaxParaHeadDataSize` let updated_head_data = - match StoredParaHeadDataOf::::try_from_inner(updated_head_data) { + match StoredParaHeadDataOf::::try_from_inner(new_head_data) { Ok(updated_head_data) => updated_head_data, Err(e) => { log::trace!( target: LOG_TARGET, - "{}. The parachain head data size for {:?} is {}. It exceeds maximal configured size {}.", - err_log_prefix, + "The parachain head can't be updated. The parachain head data size \ + for {:?} is {}. It exceeds maximal configured size {}.", parachain, e.value_size, e.maximal_size, @@ -561,7 +535,7 @@ pub mod pallet { Self::deposit_event(Event::RejectedLargeParachainHead { parachain, - parachain_head_hash: updated_head_hash, + parachain_head_hash: new_head_hash, parachain_head_size: e.value_size as _, }); @@ -577,8 +551,8 @@ pub mod pallet { ImportedParaHashes::::try_get(parachain, next_imported_hash_position); let updated_best_para_head = ParaInfo { best_head_hash: BestParaHeadHash { - at_relay_block_number: updated_at_relay_block_number, - head_hash: updated_head_hash, + at_relay_block_number: new_at_relay_block_number, + head_hash: new_head_hash, }, next_imported_hash_position: (next_imported_hash_position + 1) % T::HeadsToKeep::get(), @@ -586,14 +560,14 @@ pub mod pallet { ImportedParaHashes::::insert( parachain, next_imported_hash_position, - updated_head_hash, + new_head_hash, ); - ImportedParaHeads::::insert(parachain, updated_head_hash, updated_head_data); + ImportedParaHeads::::insert(parachain, new_head_hash, updated_head_data); log::trace!( target: LOG_TARGET, "Updated head of parachain {:?} to {}", parachain, - updated_head_hash, + new_head_hash, ); // remove old head @@ -609,7 +583,7 @@ pub mod pallet { } Self::deposit_event(Event::UpdatedParachainHead { parachain, - parachain_head_hash: updated_head_hash, + parachain_head_hash: new_head_hash, }); Ok(UpdateParachainHeadArtifacts { best_head: updated_best_para_head, prune_happened }) @@ -646,6 +620,27 @@ pub mod pallet { } } } + + /// Returns maximal number of parachains, supported by the pallet. + pub struct MaybeMaxParachains(PhantomData<(T, I)>); + + impl, I: 'static> Get> for MaybeMaxParachains { + fn get() -> Option { + Some(T::ParaStoredHeaderDataBuilder::supported_parachains()) + } + } + + /// Returns total number of all parachains hashes/heads, stored by the pallet. + pub struct MaybeMaxTotalParachainHashes(PhantomData<(T, I)>); + + impl, I: 'static> Get> for MaybeMaxTotalParachainHashes { + fn get() -> Option { + Some( + T::ParaStoredHeaderDataBuilder::supported_parachains() + .saturating_mul(T::HeadsToKeep::get()), + ) + } + } } /// Single parachain header chain adapter. @@ -1525,4 +1520,17 @@ mod tests { } generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); + + #[test] + fn maybe_max_parachains_returns_correct_value() { + assert_eq!(MaybeMaxParachains::::get(), Some(mock::TOTAL_PARACHAINS)); + } + + #[test] + fn maybe_max_total_parachain_hashes_returns_correct_value() { + assert_eq!( + MaybeMaxTotalParachainHashes::::get(), + Some(mock::TOTAL_PARACHAINS * mock::HeadsToKeep::get()), + ); + } } diff --git a/modules/parachains/src/mock.rs b/modules/parachains/src/mock.rs index 118a9b1d315..8248964263c 100644 --- a/modules/parachains/src/mock.rs +++ b/modules/parachains/src/mock.rs @@ -38,6 +38,8 @@ pub const PARAS_PALLET_NAME: &str = "Paras"; pub const UNTRACKED_PARACHAIN_ID: u32 = 10; // use exact expected encoded size: `vec_len_size + header_number_size + state_root_hash_size` pub const MAXIMAL_PARACHAIN_HEAD_DATA_SIZE: u32 = 1 + 8 + 32; +// total parachains that we use in tests +pub const TOTAL_PARACHAINS: u32 = 4; pub type RegularParachainHeader = sp_runtime::testing::Header; pub type RegularParachainHasher = BlakeTwo256; diff --git a/modules/parachains/src/weights.rs b/modules/parachains/src/weights.rs index f6dc73d40c3..5ee1e4d3621 100644 --- a/modules/parachains/src/weights.rs +++ b/modules/parachains/src/weights.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for pallet_bridge_parachains //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -67,33 +67,33 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535, - /// mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: + /// 555, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64), - /// added: 2539, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size: + /// Some(64), added: 2044, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196), - /// added: 2671, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size: + /// Some(196), added: 2176, mode: MaxEncodedLen) /// /// The range of component `p` is `[1, 2]`. fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { // Proof Size summary in bytes: // Measured: `366` - // Estimated: `8113` - // Minimum execution time: 35_348 nanoseconds. - Weight::from_parts(36_906_961, 8113) - // Standard Error: 136_143 - .saturating_add(Weight::from_ref_time(148_169).saturating_mul(p.into())) + // Estimated: `5143` + // Minimum execution time: 35_160 nanoseconds. + Weight::from_parts(36_951_585, 5143) + // Standard Error: 336_932 + .saturating_add(Weight::from_ref_time(407_557).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -104,29 +104,29 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535, - /// mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: + /// 555, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64), - /// added: 2539, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size: + /// Some(64), added: 2044, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196), - /// added: 2671, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size: + /// Some(196), added: 2176, mode: MaxEncodedLen) fn submit_parachain_heads_with_1kb_proof() -> Weight { // Proof Size summary in bytes: // Measured: `366` - // Estimated: `8113` - // Minimum execution time: 43_295 nanoseconds. - Weight::from_parts(48_018_000, 8113) + // Estimated: `5143` + // Minimum execution time: 42_276 nanoseconds. + Weight::from_parts(43_525_000, 5143) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -137,29 +137,29 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535, - /// mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: + /// 555, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64), - /// added: 2539, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size: + /// Some(64), added: 2044, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196), - /// added: 2671, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size: + /// Some(196), added: 2176, mode: MaxEncodedLen) fn submit_parachain_heads_with_16kb_proof() -> Weight { // Proof Size summary in bytes: // Measured: `366` - // Estimated: `8113` - // Minimum execution time: 86_112 nanoseconds. - Weight::from_parts(88_901_000, 8113) + // Estimated: `5143` + // Minimum execution time: 85_824 nanoseconds. + Weight::from_parts(87_335_000, 5143) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -174,33 +174,33 @@ impl WeightInfo for () { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535, - /// mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: + /// 555, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64), - /// added: 2539, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size: + /// Some(64), added: 2044, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196), - /// added: 2671, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size: + /// Some(196), added: 2176, mode: MaxEncodedLen) /// /// The range of component `p` is `[1, 2]`. fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { // Proof Size summary in bytes: // Measured: `366` - // Estimated: `8113` - // Minimum execution time: 35_348 nanoseconds. - Weight::from_parts(36_906_961, 8113) - // Standard Error: 136_143 - .saturating_add(Weight::from_ref_time(148_169).saturating_mul(p.into())) + // Estimated: `5143` + // Minimum execution time: 35_160 nanoseconds. + Weight::from_parts(36_951_585, 5143) + // Standard Error: 336_932 + .saturating_add(Weight::from_ref_time(407_557).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -211,29 +211,29 @@ impl WeightInfo for () { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535, - /// mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: + /// 555, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64), - /// added: 2539, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size: + /// Some(64), added: 2044, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196), - /// added: 2671, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size: + /// Some(196), added: 2176, mode: MaxEncodedLen) fn submit_parachain_heads_with_1kb_proof() -> Weight { // Proof Size summary in bytes: // Measured: `366` - // Estimated: `8113` - // Minimum execution time: 43_295 nanoseconds. - Weight::from_parts(48_018_000, 8113) + // Estimated: `5143` + // Minimum execution time: 42_276 nanoseconds. + Weight::from_parts(43_525_000, 5143) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -244,29 +244,29 @@ impl WeightInfo for () { /// /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) /// - /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: None, max_size: Some(68), added: - /// 2543, mode: MaxEncodedLen) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ParasInfo (max_values: None, max_size: Some(60), added: 2535, - /// mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: + /// 555, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: None, max_size: Some(64), - /// added: 2539, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size: + /// Some(64), added: 2044, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: None, max_size: Some(196), - /// added: 2671, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size: + /// Some(196), added: 2176, mode: MaxEncodedLen) fn submit_parachain_heads_with_16kb_proof() -> Weight { // Proof Size summary in bytes: // Measured: `366` - // Estimated: `8113` - // Minimum execution time: 86_112 nanoseconds. - Weight::from_parts(88_901_000, 8113) + // Estimated: `5143` + // Minimum execution time: 85_824 nanoseconds. + Weight::from_parts(87_335_000, 5143) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } diff --git a/primitives/parachains/src/lib.rs b/primitives/parachains/src/lib.rs index 388a995fae6..e619fc7b641 100644 --- a/primitives/parachains/src/lib.rs +++ b/primitives/parachains/src/lib.rs @@ -115,7 +115,10 @@ impl ParaStoredHeaderData { /// Stored parachain head data builder. pub trait ParaStoredHeaderDataBuilder { - /// Try to build head data from self. + /// Return number of parachains that are supported by this builder. + fn supported_parachains() -> u32; + + /// Try to build head data from encoded head of parachain with given id. fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option; } @@ -123,6 +126,10 @@ pub trait ParaStoredHeaderDataBuilder { pub struct SingleParaStoredHeaderDataBuilder(PhantomData); impl ParaStoredHeaderDataBuilder for SingleParaStoredHeaderDataBuilder { + fn supported_parachains() -> u32 { + 1 + } + fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option { if para_id == ParaId(C::PARACHAIN_ID) { let header = HeaderOf::::decode(&mut ¶_head.0[..]).ok()?; @@ -139,6 +146,14 @@ impl ParaStoredHeaderDataBuilder for SingleParaStoredHeaderDataBui #[impl_trait_for_tuples::impl_for_tuples(1, 30)] #[tuple_types_custom_trait_bound(Parachain)] impl ParaStoredHeaderDataBuilder for C { + fn supported_parachains() -> u32 { + let mut result = 0; + for_tuples!( #( + result += SingleParaStoredHeaderDataBuilder::::supported_parachains(); + )* ); + result + } + fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option { for_tuples!( #( let maybe_para_head = SingleParaStoredHeaderDataBuilder::::try_build(para_id, para_head); diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 5eb1de2e3ac..83ffd238ab5 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -37,7 +37,6 @@ pub use chain::{ }; pub use frame_support::storage::storage_prefix as storage_value_final_key; use num_traits::{CheckedSub, One}; -use sp_runtime::transaction_validity::TransactionValidity; pub use storage_proof::{ record_all_keys as record_all_trie_keys, Error as StorageProofError, ProofSize as StorageProofSize, StorageProofChecker, @@ -490,12 +489,6 @@ pub trait OwnedBridgeModule { } } -/// A trait for querying whether a runtime call is valid. -pub trait FilterCall { - /// Checks if a runtime call is valid. - fn validate(call: &Call) -> TransactionValidity; -} - /// All extra operations with weights that we need in bridges. pub trait WeightExtraOps { /// Checked division of individual components of two weights. diff --git a/relays/bin-substrate/Cargo.toml b/relays/bin-substrate/Cargo.toml index 1a16f3466f3..0dc20158830 100644 --- a/relays/bin-substrate/Cargo.toml +++ b/relays/bin-substrate/Cargo.toml @@ -17,7 +17,7 @@ num-format = "0.4" num-traits = "0.2" rbtag = "0.3" structopt = "0.3" -signal-hook = "0.3.14" +signal-hook = "0.3.15" signal-hook-async-std = "0.2.2" strum = { version = "0.24.1", features = ["derive"] } diff --git a/relays/finality/README.md b/relays/finality/README.md index effc3db6b12..444056e7563 100644 --- a/relays/finality/README.md +++ b/relays/finality/README.md @@ -21,7 +21,7 @@ More: [GRANDPA Finality Relay Sequence Diagram](../../docs/grandpa-finality-rela The most important trait is the [`FinalitySyncPipeline`](./src/lib.rs), which defines the basic primitives of the source chain (like block hash and number) and the type of finality proof (GRANDPA justification or MMR proof). Once that is defined, there are two other traits - [`SourceClient`](./src/finality_loop.rs) and -[`TarggetClient`](./src/finality_loop.rs). +[`TargetClient`](./src/finality_loop.rs). The `SourceClient` represents the Substrate node client that connects to the source chain. The client needs to be able to return the best finalized header number, finalized header and its finality proof and the stream of diff --git a/relays/messages/src/message_race_delivery.rs b/relays/messages/src/message_race_delivery.rs index 0d957fd8b71..c704a7b5610 100644 --- a/relays/messages/src/message_race_delivery.rs +++ b/relays/messages/src/message_race_delivery.rs @@ -124,8 +124,8 @@ where self.client.latest_confirmed_received_nonce(at_block).await?; if let Some(metrics_msg) = self.metrics_msg.as_ref() { - metrics_msg.update_source_latest_generated_nonce::

(latest_generated_nonce); - metrics_msg.update_source_latest_confirmed_nonce::

(latest_confirmed_nonce); + metrics_msg.update_source_latest_generated_nonce(latest_generated_nonce); + metrics_msg.update_source_latest_confirmed_nonce(latest_confirmed_nonce); } let new_nonces = if latest_generated_nonce > prev_latest_nonce { @@ -195,8 +195,8 @@ where if update_metrics { if let Some(metrics_msg) = self.metrics_msg.as_ref() { - metrics_msg.update_target_latest_received_nonce::

(latest_received_nonce); - metrics_msg.update_target_latest_confirmed_nonce::

(latest_confirmed_nonce); + metrics_msg.update_target_latest_received_nonce(latest_received_nonce); + metrics_msg.update_target_latest_confirmed_nonce(latest_confirmed_nonce); } } diff --git a/relays/messages/src/message_race_receiving.rs b/relays/messages/src/message_race_receiving.rs index 70a3d7c2477..e6497a1b79e 100644 --- a/relays/messages/src/message_race_receiving.rs +++ b/relays/messages/src/message_race_receiving.rs @@ -112,7 +112,7 @@ where ) -> Result<(TargetHeaderIdOf

, SourceClientNonces), Self::Error> { let (at_block, latest_received_nonce) = self.client.latest_received_nonce(at_block).await?; if let Some(metrics_msg) = self.metrics_msg.as_ref() { - metrics_msg.update_target_latest_received_nonce::

(latest_received_nonce); + metrics_msg.update_target_latest_received_nonce(latest_received_nonce); } Ok(( at_block, @@ -174,7 +174,7 @@ where self.client.latest_confirmed_received_nonce(at_block).await?; if update_metrics { if let Some(metrics_msg) = self.metrics_msg.as_ref() { - metrics_msg.update_source_latest_confirmed_nonce::

(latest_confirmed_nonce); + metrics_msg.update_source_latest_confirmed_nonce(latest_confirmed_nonce); } } Ok((at_block, TargetClientNonces { latest_nonce: latest_confirmed_nonce, nonces_data: () })) diff --git a/relays/messages/src/metrics.rs b/relays/messages/src/metrics.rs index 20c6986b474..69d80d178de 100644 --- a/relays/messages/src/metrics.rs +++ b/relays/messages/src/metrics.rs @@ -101,7 +101,7 @@ impl MessageLaneLoopMetrics { } /// Update latest generated nonce at source. - pub fn update_source_latest_generated_nonce( + pub fn update_source_latest_generated_nonce( &self, source_latest_generated_nonce: MessageNonce, ) { @@ -111,7 +111,7 @@ impl MessageLaneLoopMetrics { } /// Update the latest confirmed nonce at source. - pub fn update_source_latest_confirmed_nonce( + pub fn update_source_latest_confirmed_nonce( &self, source_latest_confirmed_nonce: MessageNonce, ) { @@ -121,17 +121,14 @@ impl MessageLaneLoopMetrics { } /// Update the latest received nonce at target. - pub fn update_target_latest_received_nonce( - &self, - target_latest_generated_nonce: MessageNonce, - ) { + pub fn update_target_latest_received_nonce(&self, target_latest_generated_nonce: MessageNonce) { self.lane_state_nonces .with_label_values(&["target_latest_received"]) .set(target_latest_generated_nonce); } /// Update the latest confirmed nonce at target. - pub fn update_target_latest_confirmed_nonce( + pub fn update_target_latest_confirmed_nonce( &self, target_latest_confirmed_nonce: MessageNonce, ) { diff --git a/relays/parachains/README.md b/relays/parachains/README.md index 9093dc617dc..fc5ad03fb34 100644 --- a/relays/parachains/README.md +++ b/relays/parachains/README.md @@ -15,7 +15,7 @@ More: [Parachains Finality Relay Sequence Diagram](../../docs/parachains-finalit There are only two traits that need to be implemented. The [`SourceChain`](./src/parachains_loop.rs) implementation is supposed to connect to the source chain node. It must be able to read parachain heads from the `Heads` map of -the [`paras` pallet](https://github.com/paritytech/polkadot/tree/master/runtime/parachains/src/paras) pallet. +the [`paras` pallet](https://github.com/paritytech/polkadot/tree/master/runtime/parachains/src/paras). It also must create storage proofs of `Heads` map entries, when required. The [`TargetChain`](./src/parachains_loop.rs) implementation connects to the target chain node. It must be able From 1bb5161ff259a280a672be3fe1e13d30eecbde1c Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 16 Feb 2023 10:14:08 +0100 Subject: [PATCH 197/263] Squashed 'bridges/' changes from dcaec27aa..91e66cfb9 91e66cfb9 Fix clippy issues (#1884) 0bd77f457 Reject storage proofs with unused nodes: begin (#1878) 77a3672f9 Refund extra proof bytes in message delivery transaction (#1864) git-subtree-dir: bridges git-subtree-split: 91e66cfb99c1a7b247e435515dd0f62b4058974e --- Cargo.lock | 1 - bin/millau/runtime/src/rialto_messages.rs | 1 - bin/rialto/runtime/src/millau_messages.rs | 1 - bin/runtime-common/src/integrity.rs | 8 +- bin/runtime-common/src/messages.rs | 68 ++++++-- .../src/messages_benchmarking.rs | 20 +-- bin/runtime-common/src/messages_generation.rs | 23 +-- .../src/parachains_benchmarking.rs | 6 +- fuzz/storage-proof/src/main.rs | 9 +- modules/beefy/src/utils.rs | 4 +- modules/grandpa/src/lib.rs | 2 +- modules/messages/src/inbound_lane.rs | 5 + modules/messages/src/lib.rs | 151 +++++++++++++++++- modules/parachains/src/lib.rs | 34 ++-- primitives/header-chain/Cargo.toml | 2 - primitives/header-chain/src/lib.rs | 19 +-- primitives/polkadot-core/src/parachains.rs | 4 +- primitives/runtime/src/lib.rs | 2 +- primitives/runtime/src/storage_proof.rs | 123 ++++++++++++-- 19 files changed, 378 insertions(+), 105 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a7eb6fc545..cad628df103 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -863,7 +863,6 @@ dependencies = [ "sp-finality-grandpa", "sp-runtime 7.0.0", "sp-std 5.0.0", - "sp-trie 7.0.0", ] [[package]] diff --git a/bin/millau/runtime/src/rialto_messages.rs b/bin/millau/runtime/src/rialto_messages.rs index 62ad919d96c..919fac86899 100644 --- a/bin/millau/runtime/src/rialto_messages.rs +++ b/bin/millau/runtime/src/rialto_messages.rs @@ -162,7 +162,6 @@ mod tests { RialtoGrandpaInstance, WithRialtoMessagesInstance, WithRialtoMessageBridge, - bp_millau::Millau, >(AssertCompleteBridgeConstants { this_chain_constants: AssertChainConstants { block_length: bp_millau::BlockLength::get(), diff --git a/bin/rialto/runtime/src/millau_messages.rs b/bin/rialto/runtime/src/millau_messages.rs index 4774d871b78..ff08c08029a 100644 --- a/bin/rialto/runtime/src/millau_messages.rs +++ b/bin/rialto/runtime/src/millau_messages.rs @@ -161,7 +161,6 @@ mod tests { MillauGrandpaInstance, WithMillauMessagesInstance, WithMillauMessageBridge, - bp_rialto::Rialto, >(AssertCompleteBridgeConstants { this_chain_constants: AssertChainConstants { block_length: bp_rialto::BlockLength::get(), diff --git a/bin/runtime-common/src/integrity.rs b/bin/runtime-common/src/integrity.rs index 51fb50df7e1..5a2c796ee43 100644 --- a/bin/runtime-common/src/integrity.rs +++ b/bin/runtime-common/src/integrity.rs @@ -141,10 +141,9 @@ pub struct AssertChainConstants { /// /// 1) block weight limits are matching; /// 2) block size limits are matching. -pub fn assert_chain_constants(params: AssertChainConstants) +pub fn assert_chain_constants(params: AssertChainConstants) where R: frame_system::Config, - C: Chain, { // we don't check runtime version here, because in our case we'll be building relay from one // repo and runtime will live in another repo, along with outdated relay version. To avoid @@ -274,7 +273,7 @@ pub struct AssertCompleteBridgeConstants<'a> { /// All bridge-related constants tests for the complete standard messages bridge (i.e. with bridge /// GRANDPA and messages pallets deployed). -pub fn assert_complete_bridge_constants(params: AssertCompleteBridgeConstants) +pub fn assert_complete_bridge_constants(params: AssertCompleteBridgeConstants) where R: frame_system::Config + pallet_bridge_grandpa::Config @@ -282,9 +281,8 @@ where GI: 'static, MI: 'static, B: MessageBridge, - This: Chain, { - assert_chain_constants::(params.this_chain_constants); + assert_chain_constants::(params.this_chain_constants); assert_bridge_grandpa_pallet_constants::(); assert_bridge_messages_pallet_constants::(params.messages_pallet_constants); assert_bridge_pallet_names::(params.pallet_names); diff --git a/bin/runtime-common/src/messages.rs b/bin/runtime-common/src/messages.rs index 8c97198393d..ceedee12afa 100644 --- a/bin/runtime-common/src/messages.rs +++ b/bin/runtime-common/src/messages.rs @@ -20,6 +20,8 @@ //! pallet is used to dispatch incoming messages. Message identified by a tuple //! of to elements - message lane id and message nonce. +pub use bp_runtime::{UnderlyingChainOf, UnderlyingChainProvider}; + use bp_header_chain::{HeaderChain, HeaderChainError}; use bp_messages::{ source_chain::{LaneMessageVerifier, TargetHeaderChain}, @@ -28,14 +30,15 @@ use bp_messages::{ }, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, }; -use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Size, StorageProofChecker}; -pub use bp_runtime::{UnderlyingChainOf, UnderlyingChainProvider}; +use bp_runtime::{ + messages::MessageDispatchResult, Chain, ChainId, RawStorageProof, Size, StorageProofChecker, + StorageProofError, +}; use codec::{Decode, DecodeLimit, Encode}; use frame_support::{traits::Get, weights::Weight, RuntimeDebug}; use hash_db::Hasher; use scale_info::TypeInfo; use sp_std::{convert::TryFrom, fmt::Debug, marker::PhantomData, vec::Vec}; -use sp_trie::StorageProof; use xcm::latest::prelude::*; /// Bidirectional message bridge. @@ -97,9 +100,6 @@ pub type OriginOf = ::RuntimeOrigin; /// Type of call that is used on this chain. pub type CallOf = ::RuntimeCall; -/// Raw storage proof type (just raw trie nodes). -pub type RawStorageProof = Vec>; - /// Sub-module that is declaring types required for processing This -> Bridged chain messages. pub mod source { use super::*; @@ -274,8 +274,8 @@ pub mod source { proof; B::BridgedHeaderChain::parse_finalized_storage_proof( bridged_header_hash, - StorageProof::new(storage_proof), - |storage| { + storage_proof, + |mut storage| { // Messages delivery proof is just proof of single storage key read => any error // is fatal. let storage_inbound_lane_data_key = @@ -290,6 +290,11 @@ pub mod source { let inbound_lane_data = InboundLaneData::decode(&mut &raw_inbound_lane_data[..]) .map_err(|_| "Failed to decode inbound lane state from the proof")?; + // check that the storage proof doesn't have any untouched trie nodes + storage + .ensure_no_unused_nodes() + .map_err(|_| "Messages delivery proof has unused trie nodes")?; + Ok((lane, inbound_lane_data)) }, ) @@ -608,9 +613,9 @@ pub mod target { B::BridgedHeaderChain::parse_finalized_storage_proof( bridged_header_hash, - StorageProof::new(storage_proof), + storage_proof, |storage| { - let parser = + let mut parser = StorageProofCheckerAdapter::<_, B> { storage, _dummy: Default::default() }; // receiving proofs where end < begin is ok (if proof includes outbound lane state) @@ -661,6 +666,12 @@ pub mod target { return Err(MessageProofError::Empty) } + // check that the storage proof doesn't have any untouched trie nodes + parser + .storage + .ensure_no_unused_nodes() + .map_err(MessageProofError::StorageProof)?; + // We only support single lane messages in this generated_schema let mut proved_messages = ProvedMessages::new(); proved_messages.insert(lane, proved_lane_messages); @@ -686,6 +697,8 @@ pub mod target { FailedToDecodeMessage, /// Failed to decode outbound lane data from the proof. FailedToDecodeOutboundLaneState, + /// Storage proof related error. + StorageProof(StorageProofError), } impl From for &'static str { @@ -700,6 +713,7 @@ pub mod target { "Failed to decode message from the proof", MessageProofError::FailedToDecodeOutboundLaneState => "Failed to decode outbound lane data from the proof", + MessageProofError::StorageProof(_) => "Invalid storage proof", } } } @@ -710,7 +724,7 @@ pub mod target { } impl StorageProofCheckerAdapter { - fn read_raw_outbound_lane_data(&self, lane_id: &LaneId) -> Option> { + fn read_raw_outbound_lane_data(&mut self, lane_id: &LaneId) -> Option> { let storage_outbound_lane_data_key = bp_messages::storage_keys::outbound_lane_data_key( B::BRIDGED_MESSAGES_PALLET_NAME, lane_id, @@ -718,7 +732,7 @@ pub mod target { self.storage.read_value(storage_outbound_lane_data_key.0.as_ref()).ok()? } - fn read_raw_message(&self, message_key: &MessageKey) -> Option> { + fn read_raw_message(&mut self, message_key: &MessageKey) -> Option> { let storage_message_key = bp_messages::storage_keys::message_key( B::BRIDGED_MESSAGES_PALLET_NAME, &message_key.lane_id, @@ -928,7 +942,35 @@ mod tests { ); target::verify_messages_proof::(proof, 10) }), - Err(target::MessageProofError::HeaderChain(HeaderChainError::StorageRootMismatch)), + Err(target::MessageProofError::HeaderChain(HeaderChainError::StorageProof( + StorageProofError::StorageRootMismatch + ))), + ); + } + + #[test] + fn message_proof_is_rejected_if_it_has_duplicate_trie_nodes() { + assert_eq!( + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |mut proof| { + let node = proof.storage_proof.pop().unwrap(); + proof.storage_proof.push(node.clone()); + proof.storage_proof.push(node); + target::verify_messages_proof::(proof, 10) + },), + Err(target::MessageProofError::HeaderChain(HeaderChainError::StorageProof( + StorageProofError::DuplicateNodesInProof + ))), + ); + } + + #[test] + fn message_proof_is_rejected_if_it_has_unused_trie_nodes() { + assert_eq!( + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |mut proof| { + proof.storage_proof.push(vec![42]); + target::verify_messages_proof::(proof, 10) + },), + Err(target::MessageProofError::StorageProof(StorageProofError::UnusedNodesInTheProof)), ); } diff --git a/bin/runtime-common/src/messages_benchmarking.rs b/bin/runtime-common/src/messages_benchmarking.rs index e7ca26d952e..1588c77633e 100644 --- a/bin/runtime-common/src/messages_benchmarking.rs +++ b/bin/runtime-common/src/messages_benchmarking.rs @@ -22,7 +22,7 @@ use crate::{ messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, - AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, RawStorageProof, ThisChain, + AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, ThisChain, }, messages_generation::{ encode_all_messages, encode_lane_data, grow_trie, prepare_messages_storage_proof, @@ -31,13 +31,15 @@ use crate::{ use bp_messages::storage_keys; use bp_polkadot_core::parachains::ParaHash; -use bp_runtime::{record_all_trie_keys, Chain, Parachain, StorageProofSize, UnderlyingChainOf}; +use bp_runtime::{ + record_all_trie_keys, Chain, Parachain, RawStorageProof, StorageProofSize, UnderlyingChainOf, +}; use codec::Encode; use frame_support::weights::Weight; use pallet_bridge_messages::benchmarking::{MessageDeliveryProofParams, MessageProofParams}; use sp_runtime::traits::{Header, Zero}; use sp_std::prelude::*; -use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; +use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; /// Prepare proof of messages for the `receive_messages_proof` call. /// @@ -209,15 +211,9 @@ where root = grow_trie(root, &mut mdb, params.size); // generate storage proof to be delivered to This chain - let mut proof_recorder = Recorder::>>>::new(); - record_all_trie_keys::>>, _>( - &mdb, - &root, - &mut proof_recorder, - ) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); - let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); + let storage_proof = record_all_trie_keys::>>, _>(&mdb, &root) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); (root, storage_proof) } diff --git a/bin/runtime-common/src/messages_generation.rs b/bin/runtime-common/src/messages_generation.rs index 560033d122a..aec97c22800 100644 --- a/bin/runtime-common/src/messages_generation.rs +++ b/bin/runtime-common/src/messages_generation.rs @@ -18,16 +18,16 @@ #![cfg(any(feature = "runtime-benchmarks", test))] -use crate::messages::{BridgedChain, HashOf, HasherOf, MessageBridge, RawStorageProof}; +use crate::messages::{BridgedChain, HashOf, HasherOf, MessageBridge}; use bp_messages::{ storage_keys, LaneId, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, }; -use bp_runtime::{record_all_trie_keys, StorageProofSize}; +use bp_runtime::{record_all_trie_keys, RawStorageProof, StorageProofSize}; use codec::Encode; use sp_core::Hasher; use sp_std::{ops::RangeInclusive, prelude::*}; -use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; +use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; /// Simple and correct message data encode function. pub(crate) fn encode_all_messages(_: MessageNonce, m: &MessagePayload) -> Option> { @@ -97,15 +97,9 @@ where root = grow_trie(root, &mut mdb, size); // generate storage proof to be delivered to This chain - let mut proof_recorder = Recorder::>>>::new(); - record_all_trie_keys::>>, _>( - &mdb, - &root, - &mut proof_recorder, - ) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); - let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); + let storage_proof = record_all_trie_keys::>>, _>(&mdb, &root) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); (root, storage_proof) } @@ -125,11 +119,10 @@ pub fn grow_trie( let mut key_index = 0; loop { // generate storage proof to be delivered to This chain - let mut proof_recorder = Recorder::>::new(); - record_all_trie_keys::, _>(mdb, &root, &mut proof_recorder) + let storage_proof = record_all_trie_keys::, _>(mdb, &root) .map_err(|_| "record_all_trie_keys has failed") .expect("record_all_trie_keys should not fail in benchmarks"); - let size: usize = proof_recorder.drain().into_iter().map(|n| n.data.len()).sum(); + let size: usize = storage_proof.iter().map(|n| n.len()).sum(); if size > minimal_trie_size as _ { return root } diff --git a/bin/runtime-common/src/parachains_benchmarking.rs b/bin/runtime-common/src/parachains_benchmarking.rs index fcd32ea28b8..e549e4f79b9 100644 --- a/bin/runtime-common/src/parachains_benchmarking.rs +++ b/bin/runtime-common/src/parachains_benchmarking.rs @@ -29,7 +29,7 @@ use codec::Encode; use frame_support::traits::Get; use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use sp_std::prelude::*; -use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; +use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; /// Prepare proof of messages for the `receive_messages_proof` call. /// @@ -72,11 +72,9 @@ where state_root = grow_trie(state_root, &mut mdb, size); // generate heads storage proof - let mut proof_recorder = Recorder::>::new(); - record_all_trie_keys::, _>(&mdb, &state_root, &mut proof_recorder) + let proof = record_all_trie_keys::, _>(&mdb, &state_root) .map_err(|_| "record_all_trie_keys has failed") .expect("record_all_trie_keys should not fail in benchmarks"); - let proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); let (relay_block_number, relay_block_hash) = insert_header_to_grandpa_pallet::(state_root); diff --git a/fuzz/storage-proof/src/main.rs b/fuzz/storage-proof/src/main.rs index 26564372cb0..9ceda58d163 100644 --- a/fuzz/storage-proof/src/main.rs +++ b/fuzz/storage-proof/src/main.rs @@ -24,10 +24,11 @@ use honggfuzz::fuzz; use sp_core::{Blake2Hasher, H256}; use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend}; use sp_std::vec::Vec; -use sp_trie::StorageProof; use std::collections::HashMap; -fn craft_known_storage_proof(input_vec: Vec<(Vec, Vec)>) -> (H256, StorageProof) { +fn craft_known_storage_proof( + input_vec: Vec<(Vec, Vec)>, +) -> (H256, bp_runtime::RawStorageProof) { let storage_proof_vec = vec![(None, input_vec.iter().map(|x| (x.0.clone(), Some(x.1.clone()))).collect())]; log::info!("Storage proof vec {:?}", storage_proof_vec); @@ -36,7 +37,7 @@ fn craft_known_storage_proof(input_vec: Vec<(Vec, Vec)>) -> (H256, Stora let root = backend.storage_root(std::iter::empty(), state_version).0; let vector_element_proof = prove_read(backend, input_vec.iter().map(|x| x.0.as_slice())).unwrap(); - (root, vector_element_proof) + (root, vector_element_proof.iter_nodes().cloned().collect()) } fn transform_into_unique(input_vec: Vec<(Vec, Vec)>) -> Vec<(Vec, Vec)> { @@ -58,7 +59,7 @@ fn run_fuzzer() { } let unique_input_vec = transform_into_unique(input_vec); let (root, craft_known_storage_proof) = craft_known_storage_proof(unique_input_vec.clone()); - let checker = + let mut checker = >::new(root, craft_known_storage_proof) .expect("Valid proof passed; qed"); for key_value_pair in unique_input_vec { diff --git a/modules/beefy/src/utils.rs b/modules/beefy/src/utils.rs index 028e61e96b4..fac9a2c92e6 100644 --- a/modules/beefy/src/utils.rs +++ b/modules/beefy/src/utils.rs @@ -52,7 +52,7 @@ fn verify_authority_set, I: 'static>( /// /// We're using 'conservative' approach here, where signatures of `2/3+1` validators are /// required.. -pub(crate) fn signatures_required, I: 'static>(validators_len: usize) -> usize { +pub(crate) fn signatures_required(validators_len: usize) -> usize { validators_len - validators_len.saturating_sub(1) / 3 } @@ -67,7 +67,7 @@ fn verify_signatures, I: 'static>( // Ensure that the commitment was signed by enough authorities. let msg = commitment.commitment.encode(); - let mut missing_signatures = signatures_required::(authority_set.len()); + let mut missing_signatures = signatures_required(authority_set.len()); for (idx, (authority, maybe_sig)) in authority_set.validators().iter().zip(commitment.signatures.iter()).enumerate() { diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index c2b79434795..fca0cb1b758 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -1050,7 +1050,7 @@ mod tests { assert_noop!( Pallet::::parse_finalized_storage_proof( Default::default(), - sp_trie::StorageProof::new(vec![]), + vec![], |_| (), ), bp_header_chain::HeaderChainError::UnknownHeader, diff --git a/modules/messages/src/inbound_lane.rs b/modules/messages/src/inbound_lane.rs index 00c63b5d670..3f64ab765b5 100644 --- a/modules/messages/src/inbound_lane.rs +++ b/modules/messages/src/inbound_lane.rs @@ -117,6 +117,11 @@ impl InboundLane { InboundLane { storage } } + /// Returns storage reference. + pub fn storage(&self) -> &S { + &self.storage + } + /// Receive state of the corresponding outbound lane. pub fn receive_state_update( &mut self, diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index 70865cf17c5..cc9033d1058 100644 --- a/modules/messages/src/lib.rs +++ b/modules/messages/src/lib.rs @@ -303,6 +303,13 @@ pub mod pallet { for (lane_id, lane_data) in messages { let mut lane = inbound_lane::(lane_id); + // subtract extra storage proof bytes from the actual PoV size - there may be + // less unrewarded relayers than the maximal configured value + let lane_extra_proof_size_bytes = lane.storage().extra_proof_size_bytes(); + actual_weight = actual_weight.set_proof_size( + actual_weight.proof_size().saturating_sub(lane_extra_proof_size_bytes), + ); + if let Some(lane_state) = lane_data.lane_state { let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state); if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce { @@ -796,6 +803,25 @@ struct RuntimeInboundLaneStorage, I: 'static = ()> { _phantom: PhantomData, } +impl, I: 'static> RuntimeInboundLaneStorage { + /// Returns number of bytes that may be subtracted from the PoV component of + /// `receive_messages_proof` call, because the actual inbound lane state is smaller than the + /// maximal configured. + /// + /// Maximal inbound lane state set size is configured by the + /// `MaxUnrewardedRelayerEntriesAtInboundLane` constant from the pallet configuration. The PoV + /// of the call includes the maximal size of inbound lane state. If the actual size is smaller, + /// we may subtract extra bytes from this component. + pub fn extra_proof_size_bytes(&self) -> u64 { + let max_encoded_len = StoredInboundLaneData::::max_encoded_len(); + let relayers_count = self.data().relayers.len(); + let actual_encoded_len = + InboundLaneData::::encoded_size_hint(relayers_count) + .unwrap_or(usize::MAX); + max_encoded_len.saturating_sub(actual_encoded_len) as _ + } +} + impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage { type Relayer = T::InboundRelayer; @@ -906,9 +932,9 @@ mod tests { use crate::mock::{ message, message_payload, run_test, unrewarded_relayer, AccountId, DbWeight, RuntimeEvent as TestEvent, RuntimeOrigin, TestDeliveryConfirmationPayments, - TestDeliveryPayments, TestMessagesDeliveryProof, TestMessagesProof, TestRuntime, - MAX_OUTBOUND_PAYLOAD_SIZE, PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, - TEST_LANE_ID_2, TEST_LANE_ID_3, TEST_RELAYER_A, TEST_RELAYER_B, + TestDeliveryPayments, TestMessagesDeliveryProof, TestMessagesProof, TestRelayer, + TestRuntime, MAX_OUTBOUND_PAYLOAD_SIZE, PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, + TEST_LANE_ID, TEST_LANE_ID_2, TEST_LANE_ID_3, TEST_RELAYER_A, TEST_RELAYER_B, }; use bp_messages::{BridgeMessagesCall, UnrewardedRelayer, UnrewardedRelayersState}; use bp_test_utils::generate_owned_bridge_module_tests; @@ -1543,7 +1569,7 @@ mod tests { } #[test] - fn weight_refund_from_receive_messages_proof_works() { + fn ref_time_refund_from_receive_messages_proof_works() { run_test(|| { fn submit_with_unspent_weight( nonce: MessageNonce, @@ -1598,7 +1624,7 @@ mod tests { // when there's no unspent weight let (pre, post) = submit_with_unspent_weight(4, 0); - assert_eq!(post, pre); + assert_eq!(post.ref_time(), pre.ref_time()); // when dispatch is returning `unspent_weight < declared_weight` let (pre, post) = submit_with_unspent_weight(5, 1); @@ -1606,6 +1632,84 @@ mod tests { }); } + #[test] + fn proof_size_refund_from_receive_messages_proof_works() { + run_test(|| { + let max_entries = crate::mock::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize; + + // if there's maximal number of unrewarded relayer entries at the inbound lane, then + // `proof_size` is unchanged in post-dispatch weight + let proof: TestMessagesProof = Ok(vec![message(101, REGULAR_PAYLOAD)]).into(); + let messages_count = 1; + let pre_dispatch_weight = + ::WeightInfo::receive_messages_proof_weight( + &proof, + messages_count, + REGULAR_PAYLOAD.declared_weight, + ); + InboundLanes::::insert( + TEST_LANE_ID, + StoredInboundLaneData(InboundLaneData { + relayers: vec![ + UnrewardedRelayer { + relayer: 42, + messages: DeliveredMessages { begin: 0, end: 100 } + }; + max_entries + ] + .into_iter() + .collect(), + last_confirmed_nonce: 0, + }), + ); + let post_dispatch_weight = Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + proof.clone(), + messages_count, + REGULAR_PAYLOAD.declared_weight, + ) + .unwrap() + .actual_weight + .unwrap(); + assert_eq!(post_dispatch_weight.proof_size(), pre_dispatch_weight.proof_size()); + + // if count of unrewarded relayer entries is less than maximal, then some `proof_size` + // must be refunded + InboundLanes::::insert( + TEST_LANE_ID, + StoredInboundLaneData(InboundLaneData { + relayers: vec![ + UnrewardedRelayer { + relayer: 42, + messages: DeliveredMessages { begin: 0, end: 100 } + }; + max_entries - 1 + ] + .into_iter() + .collect(), + last_confirmed_nonce: 0, + }), + ); + let post_dispatch_weight = Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + proof, + messages_count, + REGULAR_PAYLOAD.declared_weight, + ) + .unwrap() + .actual_weight + .unwrap(); + assert!( + post_dispatch_weight.proof_size() < pre_dispatch_weight.proof_size(), + "Expected post-dispatch PoV {} to be less than pre-dispatch PoV {}", + post_dispatch_weight.proof_size(), + pre_dispatch_weight.proof_size(), + ); + }); + } + #[test] fn messages_delivered_callbacks_are_called() { run_test(|| { @@ -1978,6 +2082,43 @@ mod tests { MessagesOperatingMode::Basic(BasicOperatingMode::Halted) ); + #[test] + fn inbound_storage_extra_proof_size_bytes_works() { + fn relayer_entry() -> UnrewardedRelayer { + UnrewardedRelayer { relayer: 42u64, messages: DeliveredMessages { begin: 0, end: 100 } } + } + + fn storage(relayer_entries: usize) -> RuntimeInboundLaneStorage { + RuntimeInboundLaneStorage { + lane_id: Default::default(), + cached_data: RefCell::new(Some(InboundLaneData { + relayers: vec![relayer_entry(); relayer_entries].into_iter().collect(), + last_confirmed_nonce: 0, + })), + _phantom: Default::default(), + } + } + + let max_entries = crate::mock::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize; + + // when we have exactly `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers + assert_eq!(storage(max_entries).extra_proof_size_bytes(), 0); + + // when we have less than `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers + assert_eq!( + storage(max_entries - 1).extra_proof_size_bytes(), + relayer_entry().encode().len() as u64 + ); + assert_eq!( + storage(max_entries - 2).extra_proof_size_bytes(), + 2 * relayer_entry().encode().len() as u64 + ); + + // when we have more than `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers + // (shall not happen in practice) + assert_eq!(storage(max_entries + 1).extra_proof_size_bytes(), 0); + } + #[test] fn maybe_outbound_lanes_count_returns_correct_value() { assert_eq!( diff --git a/modules/parachains/src/lib.rs b/modules/parachains/src/lib.rs index 6875a3690f6..b9bbaac7ca3 100644 --- a/modules/parachains/src/lib.rs +++ b/modules/parachains/src/lib.rs @@ -330,10 +330,10 @@ pub mod pallet { pallet_bridge_grandpa::Pallet::::parse_finalized_storage_proof( relay_block_hash, - sp_trie::StorageProof::new(parachain_heads_proof.0), - move |storage| { + parachain_heads_proof.0, + move |mut storage| { for (parachain, parachain_head_hash) in parachains { - let parachain_head = match Pallet::::read_parachain_head(&storage, parachain) { + let parachain_head = match Pallet::::read_parachain_head(&mut storage, parachain) { Ok(Some(parachain_head)) => parachain_head, Ok(None) => { log::trace!( @@ -381,7 +381,10 @@ pub mod pallet { } // convert from parachain head into stored parachain head data - let parachain_head_data = match T::ParaStoredHeaderDataBuilder::try_build(parachain, ¶chain_head) { + let parachain_head_data = match T::ParaStoredHeaderDataBuilder::try_build( + parachain, + ¶chain_head, + ) { Some(parachain_head_data) => parachain_head_data, None => { log::trace!( @@ -418,9 +421,20 @@ pub mod pallet { .saturating_sub(WeightInfoOf::::parachain_head_pruning_weight(T::DbWeight::get())); } } + + // even though we may have accepted some parachain heads, we can't allow relayers to submit + // proof with unused trie nodes + // => treat this as an error + // + // (we can throw error here, because now all our calls are transactional) + storage.ensure_no_unused_nodes() }, ) - .map_err(|_| Error::::InvalidStorageProof)?; + .and_then(|r| r.map_err(bp_header_chain::HeaderChainError::StorageProof)) + .map_err(|e| { + log::trace!(target: LOG_TARGET, "Parachain heads storage proof is invalid: {:?}", e); + Error::::InvalidStorageProof + })?; Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) } @@ -488,7 +502,7 @@ pub mod pallet { /// Read parachain head from storage proof. fn read_parachain_head( - storage: &bp_runtime::StorageProofChecker, + storage: &mut bp_runtime::StorageProofChecker, parachain: ParaId, ) -> Result, StorageProofError> { let parachain_head_key = @@ -705,7 +719,7 @@ mod tests { use frame_system::{EventRecord, Pallet as System, Phase}; use sp_core::Hasher; use sp_runtime::{traits::Header as HeaderT, DispatchError}; - use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; + use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; type WeightInfo = ::WeightInfo; @@ -759,11 +773,9 @@ mod tests { } // generate storage proof to be delivered to This chain - let mut proof_recorder = Recorder::>::new(); - record_all_trie_keys::, _>(&mdb, &root, &mut proof_recorder) + let storage_proof = record_all_trie_keys::, _>(&mdb, &root) .map_err(|_| "record_all_trie_keys has failed") .expect("record_all_trie_keys should not fail in benchmarks"); - let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); (root, ParaHeadsProof(storage_proof), parachains) } @@ -1447,7 +1459,7 @@ mod tests { #[test] fn ignores_parachain_head_if_it_is_missing_from_storage_proof() { - let (state_root, proof, _) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + let (state_root, proof, _) = prepare_parachain_heads_proof(vec![]); let parachains = vec![(ParaId(2), Default::default())]; run_test(|| { initialize(state_root); diff --git a/primitives/header-chain/Cargo.toml b/primitives/header-chain/Cargo.toml index 3b7ea58cb96..be87cce460e 100644 --- a/primitives/header-chain/Cargo.toml +++ b/primitives/header-chain/Cargo.toml @@ -23,7 +23,6 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [dev-dependencies] bp-test-utils = { path = "../test-utils" } @@ -43,5 +42,4 @@ std = [ "sp-finality-grandpa/std", "sp-runtime/std", "sp-std/std", - "sp-trie/std", ] diff --git a/primitives/header-chain/src/lib.rs b/primitives/header-chain/src/lib.rs index 14e9395c03c..49d1ae89406 100644 --- a/primitives/header-chain/src/lib.rs +++ b/primitives/header-chain/src/lib.rs @@ -19,35 +19,36 @@ #![cfg_attr(not(feature = "std"), no_std)] -use bp_runtime::{BasicOperatingMode, Chain, HashOf, HasherOf, HeaderOf, StorageProofChecker}; +use bp_runtime::{ + BasicOperatingMode, Chain, HashOf, HasherOf, HeaderOf, RawStorageProof, StorageProofChecker, + StorageProofError, +}; use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}; use core::{clone::Clone, cmp::Eq, default::Default, fmt::Debug}; -use frame_support::PalletError; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_finality_grandpa::{AuthorityList, ConsensusLog, SetId, GRANDPA_ENGINE_ID}; use sp_runtime::{traits::Header as HeaderT, Digest, RuntimeDebug}; use sp_std::boxed::Box; -use sp_trie::StorageProof; pub mod justification; pub mod storage_keys; /// Header chain error. -#[derive(Clone, Copy, Decode, Encode, Eq, PalletError, PartialEq, RuntimeDebug, TypeInfo)] +#[derive(Clone, Eq, PartialEq, RuntimeDebug)] pub enum HeaderChainError { /// Header with given hash is missing from the chain. UnknownHeader, - /// The storage proof doesn't contains storage root. - StorageRootMismatch, + /// Storage proof related error. + StorageProof(StorageProofError), } impl From for &'static str { fn from(err: HeaderChainError) -> &'static str { match err { HeaderChainError::UnknownHeader => "UnknownHeader", - HeaderChainError::StorageRootMismatch => "StorageRootMismatch", + HeaderChainError::StorageProof(e) => e.into(), } } } @@ -83,13 +84,13 @@ pub trait HeaderChain { /// Parse storage proof using finalized header. fn parse_finalized_storage_proof( header_hash: HashOf, - storage_proof: StorageProof, + storage_proof: RawStorageProof, parse: impl FnOnce(StorageProofChecker>) -> R, ) -> Result { let state_root = Self::finalized_header_state_root(header_hash) .ok_or(HeaderChainError::UnknownHeader)?; let storage_proof_checker = bp_runtime::StorageProofChecker::new(state_root, storage_proof) - .map_err(|_| HeaderChainError::StorageRootMismatch)?; + .map_err(HeaderChainError::StorageProof)?; Ok(parse(storage_proof_checker)) } diff --git a/primitives/polkadot-core/src/parachains.rs b/primitives/polkadot-core/src/parachains.rs index e8f68dd2a9a..0b410dff49f 100644 --- a/primitives/polkadot-core/src/parachains.rs +++ b/primitives/polkadot-core/src/parachains.rs @@ -22,7 +22,7 @@ //! chains. Having pallets that are referencing polkadot, would mean that there may //! be two versions of polkadot crates included in the runtime. Which is bad. -use bp_runtime::Size; +use bp_runtime::{RawStorageProof, Size}; use codec::{CompactAs, Decode, Encode, MaxEncodedLen}; use frame_support::RuntimeDebug; use scale_info::TypeInfo; @@ -88,7 +88,7 @@ pub type ParaHasher = crate::Hasher; /// Raw storage proof of parachain heads, stored in polkadot-like chain runtime. #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] -pub struct ParaHeadsProof(pub Vec>); +pub struct ParaHeadsProof(pub RawStorageProof); impl Size for ParaHeadsProof { fn size(&self) -> u32 { diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 83ffd238ab5..75151ccb723 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -39,7 +39,7 @@ pub use frame_support::storage::storage_prefix as storage_value_final_key; use num_traits::{CheckedSub, One}; pub use storage_proof::{ record_all_keys as record_all_trie_keys, Error as StorageProofError, - ProofSize as StorageProofSize, StorageProofChecker, + ProofSize as StorageProofSize, RawStorageProof, StorageProofChecker, }; pub use storage_types::BoundedStorageValue; diff --git a/primitives/runtime/src/storage_proof.rs b/primitives/runtime/src/storage_proof.rs index e1465d2fa16..133a51ce6e7 100644 --- a/primitives/runtime/src/storage_proof.rs +++ b/primitives/runtime/src/storage_proof.rs @@ -16,15 +16,18 @@ //! Logic for checking Substrate storage proofs. -use codec::Decode; +use codec::{Decode, Encode}; use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; use sp_runtime::RuntimeDebug; -use sp_std::{boxed::Box, vec::Vec}; +use sp_std::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec}; use sp_trie::{ read_trie_value, LayoutV1, MemoryDB, Recorder, StorageProof, Trie, TrieConfiguration, TrieDBBuilder, TrieError, TrieHash, }; +/// Raw storage proof type (just raw trie nodes). +pub type RawStorageProof = Vec>; + /// Storage proof size requirements. /// /// This is currently used by benchmarks when generating storage proofs. @@ -48,8 +51,10 @@ pub struct StorageProofChecker where H: Hasher, { + proof_nodes_count: usize, root: H::Out, db: MemoryDB, + recorder: Recorder>, } impl StorageProofChecker @@ -59,28 +64,59 @@ where /// Constructs a new storage proof checker. /// /// This returns an error if the given proof is invalid with respect to the given root. - pub fn new(root: H::Out, proof: StorageProof) -> Result { + pub fn new(root: H::Out, proof: RawStorageProof) -> Result { + // 1. we don't want extra items in the storage proof + // 2. `StorageProof` is storing all trie nodes in the `BTreeSet` + // + // => someone could simply add duplicate items to the proof and we won't be + // able to detect that by just using `StorageProof` + // + // => let's check it when we are converting our "raw proof" into `StorageProof` + let proof_nodes_count = proof.len(); + let proof = StorageProof::new(proof); + if proof_nodes_count != proof.iter_nodes().count() { + return Err(Error::DuplicateNodesInProof) + } + let db = proof.into_memory_db(); if !db.contains(&root, EMPTY_PREFIX) { return Err(Error::StorageRootMismatch) } - let checker = StorageProofChecker { root, db }; + let recorder = Recorder::default(); + let checker = StorageProofChecker { proof_nodes_count, root, db, recorder }; Ok(checker) } + /// Returns error if the proof has some nodes that are left intact by previous `read_value` + /// calls. + pub fn ensure_no_unused_nodes(mut self) -> Result<(), Error> { + let visited_nodes = self + .recorder + .drain() + .into_iter() + .map(|record| record.data) + .collect::>(); + let visited_nodes_count = visited_nodes.len(); + if self.proof_nodes_count == visited_nodes_count { + Ok(()) + } else { + Err(Error::UnusedNodesInTheProof) + } + } + /// Reads a value from the available subset of storage. If the value cannot be read due to an /// incomplete or otherwise invalid proof, this function returns an error. - pub fn read_value(&self, key: &[u8]) -> Result>, Error> { + pub fn read_value(&mut self, key: &[u8]) -> Result>, Error> { // LayoutV1 or LayoutV0 is identical for proof that only read values. - read_trie_value::, _>(&self.db, &self.root, key, None, None) + read_trie_value::, _>(&self.db, &self.root, key, Some(&mut self.recorder), None) .map_err(|_| Error::StorageValueUnavailable) } /// Reads and decodes a value from the available subset of storage. If the value cannot be read /// due to an incomplete or otherwise invalid proof, this function returns an error. If value is /// read, but decoding fails, this function returns an error. - pub fn read_and_decode_value(&self, key: &[u8]) -> Result, Error> { + pub fn read_and_decode_value(&mut self, key: &[u8]) -> Result, Error> { self.read_value(key).and_then(|v| { v.map(|v| T::decode(&mut &v[..]).map_err(Error::StorageValueDecodeFailed)) .transpose() @@ -88,19 +124,39 @@ where } } -#[derive(Eq, RuntimeDebug, PartialEq)] +/// Storage proof related errors. +#[derive(Clone, Eq, PartialEq, RuntimeDebug)] pub enum Error { + /// Duplicate trie nodes are found in the proof. + DuplicateNodesInProof, + /// Unused trie nodes are found in the proof. + UnusedNodesInTheProof, + /// Expected storage root is missing from the proof. StorageRootMismatch, + /// Unable to reach expected storage value using provided trie nodes. StorageValueUnavailable, + /// Failed to decode storage value. StorageValueDecodeFailed(codec::Error), } +impl From for &'static str { + fn from(err: Error) -> &'static str { + match err { + Error::DuplicateNodesInProof => "Storage proof contains duplicate nodes", + Error::UnusedNodesInTheProof => "Storage proof contains unused nodes", + Error::StorageRootMismatch => "Storage root is missing from the storage proof", + Error::StorageValueUnavailable => "Storage value is missing from the storage proof", + Error::StorageValueDecodeFailed(_) => + "Failed to decode storage value from the storage proof", + } + } +} + /// Return valid storage proof and state root. /// /// NOTE: This should only be used for **testing**. #[cfg(feature = "std")] -pub fn craft_valid_storage_proof() -> (sp_core::H256, StorageProof) { - use codec::Encode; +pub fn craft_valid_storage_proof() -> (sp_core::H256, RawStorageProof) { use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend}; let state_version = sp_runtime::StateVersion::default(); @@ -121,25 +177,33 @@ pub fn craft_valid_storage_proof() -> (sp_core::H256, StorageProof) { let proof = prove_read(backend, &[&b"key1"[..], &b"key2"[..], &b"key4"[..], &b"key22"[..]]).unwrap(); - (root, proof) + (root, proof.into_nodes().into_iter().collect()) } /// Record all keys for a given root. pub fn record_all_keys( db: &DB, root: &TrieHash, - recorder: &mut Recorder, -) -> Result<(), Box>> +) -> Result>> where DB: hash_db::HashDBRef, { - let trie = TrieDBBuilder::::new(db, root).with_recorder(recorder).build(); + let mut recorder = Recorder::::new(); + let trie = TrieDBBuilder::::new(db, root).with_recorder(&mut recorder).build(); for x in trie.iter()? { let (key, _) = x?; trie.get(&key)?; } - Ok(()) + // recorder may record the same trie node multiple times and we don't want duplicate nodes + // in our proofs => let's deduplicate it by collecting to the BTreeSet first + Ok(recorder + .drain() + .into_iter() + .map(|n| n.data.to_vec()) + .collect::>() + .into_iter() + .collect()) } #[cfg(test)] @@ -152,7 +216,7 @@ pub mod tests { let (root, proof) = craft_valid_storage_proof(); // check proof in runtime - let checker = + let mut checker = >::new(root, proof.clone()).unwrap(); assert_eq!(checker.read_value(b"key1"), Ok(Some(b"value1".to_vec()))); assert_eq!(checker.read_value(b"key2"), Ok(Some(b"value2".to_vec()))); @@ -171,4 +235,31 @@ pub mod tests { Some(Error::StorageRootMismatch) ); } + + #[test] + fn proof_with_duplicate_items_is_rejected() { + let (root, mut proof) = craft_valid_storage_proof(); + proof.push(proof.first().unwrap().clone()); + + assert_eq!( + StorageProofChecker::::new(root, proof).map(drop), + Err(Error::DuplicateNodesInProof), + ); + } + + #[test] + fn proof_with_unused_items_is_rejected() { + let (root, proof) = craft_valid_storage_proof(); + + let mut checker = + StorageProofChecker::::new(root, proof.clone()).unwrap(); + checker.read_value(b"key1").unwrap(); + checker.read_value(b"key2").unwrap(); + checker.read_value(b"key4").unwrap(); + checker.read_value(b"key22").unwrap(); + assert_eq!(checker.ensure_no_unused_nodes(), Ok(())); + + let checker = StorageProofChecker::::new(root, proof).unwrap(); + assert_eq!(checker.ensure_no_unused_nodes(), Err(Error::UnusedNodesInTheProof)); + } } From 3c155c2616076557e869b99a5781a23cd40dce33 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 16 Feb 2023 10:52:52 +0100 Subject: [PATCH 198/263] Fix tests --- .../bridge-hub-rococo/src/bridge_hub_rococo_config.rs | 1 - .../bridge-hub-rococo/src/bridge_hub_wococo_config.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index 430cc1ffc66..41efbcb031e 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -185,7 +185,6 @@ mod tests { BridgeGrandpaWococoInstance, WithBridgeHubWococoMessagesInstance, WithBridgeHubWococoMessageBridge, - bp_rococo::Rococo, >(AssertCompleteBridgeConstants { this_chain_constants: AssertChainConstants { block_length: bp_bridge_hub_rococo::BlockLength::get(), diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 6578136690c..cc18d510a43 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -185,7 +185,6 @@ mod tests { BridgeGrandpaRococoInstance, WithBridgeHubRococoMessagesInstance, WithBridgeHubRococoMessageBridge, - bp_wococo::Wococo, >(AssertCompleteBridgeConstants { this_chain_constants: AssertChainConstants { block_length: bp_bridge_hub_wococo::BlockLength::get(), From 2f8cce7d9edafc3e4ee80f1930637d4c0831b7e9 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 16 Feb 2023 14:52:03 +0100 Subject: [PATCH 199/263] Squashed 'bridges/' changes from 91e66cfb9..d39f563be d39f563be Make `weights::WeightInfo` pub (#1886) c67d06aa5 ChainWithGrandpa in primitives (#1885) git-subtree-dir: bridges git-subtree-split: d39f563bea57528c16763f458af3036842a0ea5f --- Cargo.lock | 10 ++++++ bin/millau/runtime/src/lib.rs | 27 +++----------- bin/rialto-parachain/runtime/src/lib.rs | 14 +------- bin/rialto/runtime/src/lib.rs | 14 +------- bin/runtime-common/src/mock.rs | 11 ++++-- modules/grandpa/src/benchmarking.rs | 3 +- modules/grandpa/src/lib.rs | 14 +++----- modules/grandpa/src/mock.rs | 18 ++++++++-- modules/grandpa/src/storage_types.rs | 16 +++++++-- modules/parachains/src/mock.rs | 19 ++++++++-- modules/relayers/src/lib.rs | 2 +- primitives/chain-kusama/Cargo.toml | 4 +++ primitives/chain-kusama/src/lib.rs | 36 +++++++++++++++++-- primitives/chain-millau/Cargo.toml | 13 ++++--- primitives/chain-millau/src/lib.rs | 31 ++++++++++++++++ primitives/chain-polkadot/Cargo.toml | 5 +++ primitives/chain-polkadot/src/lib.rs | 36 +++++++++++++++++-- primitives/chain-rialto/Cargo.toml | 2 ++ primitives/chain-rialto/src/lib.rs | 31 ++++++++++++++++ primitives/chain-rococo/Cargo.toml | 6 +++- primitives/chain-rococo/src/lib.rs | 37 +++++++++++++++++-- primitives/chain-westend/Cargo.toml | 2 ++ primitives/chain-westend/src/lib.rs | 33 ++++++++++++++++- primitives/chain-wococo/Cargo.toml | 6 ++++ primitives/chain-wococo/src/lib.rs | 36 +++++++++++++++++-- primitives/header-chain/src/lib.rs | 47 ++++++++++++++++++++++++- primitives/polkadot-core/src/lib.rs | 22 ++++++++++++ relays/client-kusama/src/lib.rs | 6 +--- relays/client-millau/src/lib.rs | 10 ++---- relays/client-polkadot/src/lib.rs | 7 +--- relays/client-rialto/src/lib.rs | 10 ++---- relays/client-rococo/src/lib.rs | 8 +---- relays/client-substrate/src/chain.rs | 19 +++++++--- relays/client-westend/src/lib.rs | 9 +---- relays/client-wococo/src/lib.rs | 8 +---- 35 files changed, 432 insertions(+), 140 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cad628df103..a5298107be1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -869,8 +869,10 @@ dependencies = [ name = "bp-kusama" version = "0.1.0" dependencies = [ + "bp-header-chain", "bp-polkadot-core", "bp-runtime", + "frame-support", "sp-api", ] @@ -894,6 +896,7 @@ name = "bp-millau" version = "0.1.0" dependencies = [ "bp-beefy", + "bp-header-chain", "bp-messages", "bp-runtime", "fixed-hash", @@ -933,8 +936,10 @@ dependencies = [ name = "bp-polkadot" version = "0.1.0" dependencies = [ + "bp-header-chain", "bp-polkadot-core", "bp-runtime", + "frame-support", "sp-api", ] @@ -973,6 +978,7 @@ dependencies = [ name = "bp-rialto" version = "0.1.0" dependencies = [ + "bp-header-chain", "bp-messages", "bp-runtime", "frame-support", @@ -1002,6 +1008,7 @@ dependencies = [ name = "bp-rococo" version = "0.1.0" dependencies = [ + "bp-header-chain", "bp-polkadot-core", "bp-runtime", "frame-support", @@ -1048,6 +1055,7 @@ dependencies = [ name = "bp-westend" version = "0.1.0" dependencies = [ + "bp-header-chain", "bp-polkadot-core", "bp-runtime", "frame-support", @@ -1058,9 +1066,11 @@ dependencies = [ name = "bp-wococo" version = "0.1.0" dependencies = [ + "bp-header-chain", "bp-polkadot-core", "bp-rococo", "bp-runtime", + "frame-support", "sp-api", ] diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index 8f82e645f9e..5a18f8b12c0 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -387,21 +387,6 @@ impl pallet_bridge_relayers::Config for Runtime { type WeightInfo = (); } -parameter_types! { - /// Number of headers to keep. - /// - /// Assuming the worst case of every header being finalized, we will keep headers at least for a - /// day. - pub const HeadersToKeep: u32 = bp_rialto::DAYS; - /// Maximal number of authorities at Rialto. - pub const MaxAuthoritiesAtRialto: u32 = bp_rialto::MAX_AUTHORITIES_COUNT; -} - -parameter_types! { - /// Maximal number of authorities at Westend. - pub const MaxAuthoritiesAtWestend: u32 = bp_westend::MAX_AUTHORITIES_COUNT; -} - pub type RialtoGrandpaInstance = (); impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_rialto::Rialto; @@ -410,9 +395,7 @@ impl pallet_bridge_grandpa::Config for Runtime { // Note that once this is hit the pallet will essentially throttle incoming requests down to one // call per block. type MaxRequests = ConstU32<50>; - type HeadersToKeep = HeadersToKeep; - type MaxBridgedAuthorities = MaxAuthoritiesAtRialto; - + type HeadersToKeep = ConstU32<{ bp_rialto::DAYS }>; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } @@ -420,9 +403,7 @@ pub type WestendGrandpaInstance = pallet_bridge_grandpa::Instance1; impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_westend::Westend; type MaxRequests = ConstU32<50>; - type HeadersToKeep = HeadersToKeep; - type MaxBridgedAuthorities = MaxAuthoritiesAtWestend; - + type HeadersToKeep = ConstU32<{ bp_westend::DAYS }>; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } @@ -521,7 +502,7 @@ impl pallet_bridge_parachains::Config for Runtime type ParasPalletName = RialtoParasPalletName; type ParaStoredHeaderDataBuilder = SingleParaStoredHeaderDataBuilder; - type HeadsToKeep = HeadersToKeep; + type HeadsToKeep = ConstU32<1024>; type MaxParaHeadDataSize = MaxRialtoParaHeadDataSize; } @@ -534,7 +515,7 @@ impl pallet_bridge_parachains::Config for Runtime type BridgesGrandpaPalletInstance = WestendGrandpaInstance; type ParasPalletName = WestendParasPalletName; type ParaStoredHeaderDataBuilder = SingleParaStoredHeaderDataBuilder; - type HeadsToKeep = HeadersToKeep; + type HeadsToKeep = ConstU32<1024>; type MaxParaHeadDataSize = MaxWestendParaHeadDataSize; } diff --git a/bin/rialto-parachain/runtime/src/lib.rs b/bin/rialto-parachain/runtime/src/lib.rs index ba1aa05463b..5eeddf41e52 100644 --- a/bin/rialto-parachain/runtime/src/lib.rs +++ b/bin/rialto-parachain/runtime/src/lib.rs @@ -525,17 +525,6 @@ impl pallet_bridge_relayers::Config for Runtime { type WeightInfo = (); } -parameter_types! { - /// Number of headers to keep. - /// - /// Assuming the worst case of every header being finalized, we will keep headers at least for a - /// day. - pub const HeadersToKeep: u32 = bp_millau::DAYS as u32; - - /// Maximal number of authorities at Millau. - pub const MaxAuthoritiesAtMillau: u32 = bp_millau::MAX_AUTHORITIES_COUNT; -} - pub type MillauGrandpaInstance = (); impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_millau::Millau; @@ -544,8 +533,7 @@ impl pallet_bridge_grandpa::Config for Runtime { /// Note that once this is hit the pallet will essentially throttle incoming requests down to /// one call per block. type MaxRequests = ConstU32<50>; - type HeadersToKeep = HeadersToKeep; - type MaxBridgedAuthorities = MaxAuthoritiesAtMillau; + type HeadersToKeep = ConstU32<{ bp_millau::DAYS as u32 }>; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } diff --git a/bin/rialto/runtime/src/lib.rs b/bin/rialto/runtime/src/lib.rs index cb3b6d60486..3959dbea1aa 100644 --- a/bin/rialto/runtime/src/lib.rs +++ b/bin/rialto/runtime/src/lib.rs @@ -397,17 +397,6 @@ impl pallet_bridge_relayers::Config for Runtime { type WeightInfo = (); } -parameter_types! { - /// Number of headers to keep. - /// - /// Assuming the worst case of every header being finalized, we will keep headers at least for a - /// day. - pub const HeadersToKeep: u32 = bp_rialto::DAYS; - - /// Maximal number of authorities at Millau. - pub const MaxAuthoritiesAtMillau: u32 = bp_millau::MAX_AUTHORITIES_COUNT; -} - pub type MillauGrandpaInstance = (); impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_millau::Millau; @@ -416,8 +405,7 @@ impl pallet_bridge_grandpa::Config for Runtime { /// Note that once this is hit the pallet will essentially throttle incoming requests down to /// one call per block. type MaxRequests = ConstU32<50>; - type HeadersToKeep = HeadersToKeep; - type MaxBridgedAuthorities = MaxAuthoritiesAtMillau; + type HeadersToKeep = ConstU32<{ bp_millau::DAYS as u32 }>; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } diff --git a/bin/runtime-common/src/mock.rs b/bin/runtime-common/src/mock.rs index 51b03447c63..a5ae9131f03 100644 --- a/bin/runtime-common/src/mock.rs +++ b/bin/runtime-common/src/mock.rs @@ -32,7 +32,7 @@ use crate::messages::{ BridgedChainWithMessages, HashOf, MessageBridge, ThisChainWithMessages, }; -use bp_header_chain::HeaderChain; +use bp_header_chain::{ChainWithGrandpa, HeaderChain}; use bp_messages::{target_chain::ForbidInboundMessages, LaneId, MessageNonce}; use bp_parachains::SingleParaStoredHeaderDataBuilder; use bp_runtime::{Chain, ChainId, Parachain, UnderlyingChainProvider}; @@ -195,7 +195,6 @@ impl pallet_bridge_grandpa::Config for TestRuntime { type BridgedChain = BridgedUnderlyingChain; type MaxRequests = ConstU32<50>; type HeadersToKeep = ConstU32<8>; - type MaxBridgedAuthorities = ConstU32<1024>; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } @@ -372,6 +371,14 @@ impl Chain for BridgedUnderlyingChain { } } +impl ChainWithGrandpa for BridgedUnderlyingChain { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; + const MAX_AUTHORITIES_COUNT: u32 = 16; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + const MAX_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64; +} + impl Chain for BridgedUnderlyingParachain { type BlockNumber = BridgedChainBlockNumber; type Hash = BridgedChainHash; diff --git a/modules/grandpa/src/benchmarking.rs b/modules/grandpa/src/benchmarking.rs index 710a7e0952c..337943bee4e 100644 --- a/modules/grandpa/src/benchmarking.rs +++ b/modules/grandpa/src/benchmarking.rs @@ -47,7 +47,6 @@ use bp_test_utils::{ TEST_GRANDPA_SET_ID, }; use frame_benchmarking::{benchmarks_instance_pallet, whitelisted_caller}; -use frame_support::traits::Get; use frame_system::RawOrigin; use sp_finality_grandpa::AuthorityId; use sp_runtime::traits::{One, Zero}; @@ -68,7 +67,7 @@ const MAX_VOTE_ANCESTRIES_RANGE_END: u32 = // the same with validators - if there are too much validators, let's run benchmarks on subrange fn validator_set_range_end, I: 'static>() -> u32 { - let max_bridged_authorities = T::MaxBridgedAuthorities::get(); + let max_bridged_authorities = T::BridgedChain::MAX_AUTHORITIES_COUNT; if max_bridged_authorities > 128 { sp_std::cmp::max(128, max_bridged_authorities / 5) } else { diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index fca0cb1b758..197726dd47a 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -39,10 +39,10 @@ use storage_types::StoredAuthoritySet; use bp_header_chain::{ - justification::GrandpaJustification, HeaderChain, InitializationData, StoredHeaderData, - StoredHeaderDataBuilder, + justification::GrandpaJustification, ChainWithGrandpa, HeaderChain, InitializationData, + StoredHeaderData, StoredHeaderDataBuilder, }; -use bp_runtime::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderId, HeaderOf, OwnedBridgeModule}; +use bp_runtime::{BlockNumberOf, HashOf, HasherOf, HeaderId, HeaderOf, OwnedBridgeModule}; use finality_grandpa::voter_set::VoterSet; use frame_support::{dispatch::PostDispatchInfo, ensure}; use sp_finality_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID}; @@ -97,7 +97,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { /// The chain we are bridging to here. - type BridgedChain: Chain; + type BridgedChain: ChainWithGrandpa; /// The upper bound on the number of requests allowed by the pallet. /// @@ -118,10 +118,6 @@ pub mod pallet { #[pallet::constant] type HeadersToKeep: Get; - /// Max number of authorities at the bridged chain. - #[pallet::constant] - type MaxBridgedAuthorities: Get; - /// Weights gathered through benchmarking. type WeightInfo: WeightInfo; } @@ -513,7 +509,7 @@ pub mod pallet { target: LOG_TARGET, "Failed to initialize bridge. Number of authorities in the set {} is larger than the configured value {}", authority_set_length, - T::MaxBridgedAuthorities::get(), + T::BridgedChain::MAX_AUTHORITIES_COUNT, ); Error::TooManyAuthoritiesInSet diff --git a/modules/grandpa/src/mock.rs b/modules/grandpa/src/mock.rs index 8757093ee68..f0ae2583529 100644 --- a/modules/grandpa/src/mock.rs +++ b/modules/grandpa/src/mock.rs @@ -17,8 +17,13 @@ // From construct_runtime macro #![allow(clippy::from_over_into)] +use bp_header_chain::ChainWithGrandpa; use bp_runtime::Chain; -use frame_support::{construct_runtime, parameter_types, traits::ConstU64, weights::Weight}; +use frame_support::{ + construct_runtime, parameter_types, + traits::{ConstU32, ConstU64}, + weights::Weight, +}; use sp_core::sr25519::Signature; use sp_runtime::{ testing::{Header, H256}, @@ -78,7 +83,7 @@ impl frame_system::Config for TestRuntime { type BlockLength = (); type SS58Prefix = (); type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; + type MaxConsumers = ConstU32<16>; } parameter_types! { @@ -92,7 +97,6 @@ impl grandpa::Config for TestRuntime { type BridgedChain = TestBridgedChain; type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; - type MaxBridgedAuthorities = frame_support::traits::ConstU32; type WeightInfo = (); } @@ -118,6 +122,14 @@ impl Chain for TestBridgedChain { } } +impl ChainWithGrandpa for TestBridgedChain { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; + const MAX_AUTHORITIES_COUNT: u32 = MAX_BRIDGED_AUTHORITIES; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + const MAX_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64; +} + pub fn run_test(test: impl FnOnce() -> T) -> T { sp_io::TestExternalities::new(Default::default()).execute_with(test) } diff --git a/modules/grandpa/src/storage_types.rs b/modules/grandpa/src/storage_types.rs index 732e72e2c0d..918c131289c 100644 --- a/modules/grandpa/src/storage_types.rs +++ b/modules/grandpa/src/storage_types.rs @@ -18,22 +18,32 @@ use crate::Config; -use bp_header_chain::AuthoritySet; +use bp_header_chain::{AuthoritySet, ChainWithGrandpa}; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{traits::Get, BoundedVec, RuntimeDebugNoBound}; use scale_info::TypeInfo; use sp_finality_grandpa::{AuthorityId, AuthorityList, AuthorityWeight, SetId}; +use sp_std::marker::PhantomData; /// A bounded list of Grandpa authorities with associated weights. pub type StoredAuthorityList = BoundedVec<(AuthorityId, AuthorityWeight), MaxBridgedAuthorities>; +/// Adapter for using `T::BridgedChain::MAX_BRIDGED_AUTHORITIES` in `BoundedVec`. +pub struct StoredAuthorityListLimit(PhantomData<(T, I)>); + +impl, I: 'static> Get for StoredAuthorityListLimit { + fn get() -> u32 { + T::BridgedChain::MAX_AUTHORITIES_COUNT + } +} + /// A bounded GRANDPA Authority List and ID. #[derive(Clone, Decode, Encode, Eq, TypeInfo, MaxEncodedLen, RuntimeDebugNoBound)] #[scale_info(skip_type_params(T, I))] pub struct StoredAuthoritySet, I: 'static> { /// List of GRANDPA authorities for the current round. - pub authorities: StoredAuthorityList<>::MaxBridgedAuthorities>, + pub authorities: StoredAuthorityList>, /// Monotonic identifier of the current GRANDPA authority set. pub set_id: SetId, } @@ -60,7 +70,7 @@ impl, I: 'static> StoredAuthoritySet { let single_authority_max_encoded_len = <(AuthorityId, AuthorityWeight)>::max_encoded_len() as u64; let extra_authorities = - T::MaxBridgedAuthorities::get().saturating_sub(self.authorities.len() as _); + T::BridgedChain::MAX_AUTHORITIES_COUNT.saturating_sub(self.authorities.len() as _); single_authority_max_encoded_len.saturating_mul(extra_authorities as u64) } } diff --git a/modules/parachains/src/mock.rs b/modules/parachains/src/mock.rs index 8248964263c..0e8261f6899 100644 --- a/modules/parachains/src/mock.rs +++ b/modules/parachains/src/mock.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . +use bp_header_chain::ChainWithGrandpa; use bp_polkadot_core::parachains::ParaId; use bp_runtime::{Chain, Parachain}; use frame_support::{construct_runtime, parameter_types, traits::ConstU32, weights::Weight}; @@ -199,7 +200,6 @@ impl pallet_bridge_grandpa::Config for TestRun type BridgedChain = TestBridgedChain; type MaxRequests = ConstU32<2>; type HeadersToKeep = HeadersToKeep; - type MaxBridgedAuthorities = frame_support::traits::ConstU32<5>; type WeightInfo = (); } @@ -207,7 +207,6 @@ impl pallet_bridge_grandpa::Config for TestRun type BridgedChain = TestBridgedChain; type MaxRequests = ConstU32<2>; type HeadersToKeep = HeadersToKeep; - type MaxBridgedAuthorities = frame_support::traits::ConstU32<5>; type WeightInfo = (); } @@ -250,6 +249,14 @@ impl Chain for TestBridgedChain { } } +impl ChainWithGrandpa for TestBridgedChain { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; + const MAX_AUTHORITIES_COUNT: u32 = 16; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + const MAX_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64; +} + #[derive(Debug)] pub struct OtherBridgedChain; @@ -273,6 +280,14 @@ impl Chain for OtherBridgedChain { } } +impl ChainWithGrandpa for OtherBridgedChain { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; + const MAX_AUTHORITIES_COUNT: u32 = 16; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + const MAX_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64; +} + pub fn run_test(test: impl FnOnce() -> T) -> T { sp_io::TestExternalities::new(Default::default()).execute_with(|| { System::set_block_number(1); diff --git a/modules/relayers/src/lib.rs b/modules/relayers/src/lib.rs index 7132914a4ae..2ceebd0336e 100644 --- a/modules/relayers/src/lib.rs +++ b/modules/relayers/src/lib.rs @@ -26,10 +26,10 @@ use bp_runtime::StorageDoubleMapKeyProvider; use frame_support::sp_runtime::Saturating; use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero}; use sp_std::marker::PhantomData; -use weights::WeightInfo; pub use pallet::*; pub use payment_adapter::DeliveryConfirmationPaymentsAdapter; +pub use weights::WeightInfo; pub mod benchmarking; diff --git a/primitives/chain-kusama/Cargo.toml b/primitives/chain-kusama/Cargo.toml index 3bca5f4c3f0..7f48ded1a37 100644 --- a/primitives/chain-kusama/Cargo.toml +++ b/primitives/chain-kusama/Cargo.toml @@ -10,17 +10,21 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" # Bridge Dependencies +bp-header-chain = { path = "../header-chain", default-features = false } bp-polkadot-core = { path = "../polkadot-core", default-features = false } bp-runtime = { path = "../runtime", default-features = false } # Substrate Based Dependencies +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-polkadot-core/std", "bp-runtime/std", + "frame-support/std", "sp-api/std", ] diff --git a/primitives/chain-kusama/src/lib.rs b/primitives/chain-kusama/src/lib.rs index 27d5f125ee2..9ada688bf11 100644 --- a/primitives/chain-kusama/src/lib.rs +++ b/primitives/chain-kusama/src/lib.rs @@ -19,10 +19,42 @@ #![allow(clippy::too_many_arguments)] pub use bp_polkadot_core::*; -use bp_runtime::decl_bridge_finality_runtime_apis; + +use bp_header_chain::ChainWithGrandpa; +use bp_runtime::{decl_bridge_finality_runtime_apis, Chain}; +use frame_support::weights::Weight; /// Kusama Chain -pub type Kusama = PolkadotLike; +pub struct Kusama; + +impl Chain for Kusama { + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hasher = ::Hasher; + type Header = ::Header; + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Index = ::Index; + type Signature = ::Signature; + + fn max_extrinsic_size() -> u32 { + PolkadotLike::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + PolkadotLike::max_extrinsic_weight() + } +} + +impl ChainWithGrandpa for Kusama { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_KUSAMA_GRANDPA_PALLET_NAME; + const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; +} /// Name of the With-Kusama GRANDPA pallet instance that is deployed at bridged chains. pub const WITH_KUSAMA_GRANDPA_PALLET_NAME: &str = "BridgeKusamaGrandpa"; diff --git a/primitives/chain-millau/Cargo.toml b/primitives/chain-millau/Cargo.toml index 7600781d891..00d5a02d47c 100644 --- a/primitives/chain-millau/Cargo.toml +++ b/primitives/chain-millau/Cargo.toml @@ -8,11 +8,6 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -# Bridge Dependencies - -bp-beefy = { path = "../beefy", default-features = false } -bp-messages = { path = "../messages", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } fixed-hash = { version = "0.8.0", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } impl-codec = { version = "0.6", default-features = false } @@ -21,6 +16,13 @@ parity-util-mem = { version = "0.12.0", default-features = false, features = ["p scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } +# Bridge Dependencies + +bp-beefy = { path = "../beefy", default-features = false } +bp-header-chain = { path = "../header-chain", default-features = false } +bp-messages = { path = "../messages", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + # Substrate Based Dependencies frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -36,6 +38,7 @@ sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default = ["std"] std = [ "bp-beefy/std", + "bp-header-chain/std", "bp-messages/std", "bp-runtime/std", "fixed-hash/std", diff --git a/primitives/chain-millau/src/lib.rs b/primitives/chain-millau/src/lib.rs index 73515a7f409..81ed5436263 100644 --- a/primitives/chain-millau/src/lib.rs +++ b/primitives/chain-millau/src/lib.rs @@ -21,6 +21,7 @@ mod millau_hash; use bp_beefy::ChainWithBeefy; +use bp_header_chain::ChainWithGrandpa; use bp_messages::{ InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, }; @@ -83,6 +84,27 @@ pub const SESSION_LENGTH: BlockNumber = 5 * time_units::MINUTES; /// Maximal number of GRANDPA authorities at Millau. pub const MAX_AUTHORITIES_COUNT: u32 = 5; +/// Reasonable number of headers in the `votes_ancestries` on Millau chain. +/// +/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + +/// Approximate average header size in `votes_ancestries` field of justification on Millau chain. +/// +/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 256; + +/// Approximate maximal header size on Millau chain. +/// +/// We expect maximal header to have digest item with the new authorities set for every consensus +/// engine (GRANDPA, Babe, BEEFY, ...) - so we multiply it by 3. And also +/// `AVERAGE_HEADER_SIZE_IN_JUSTIFICATION` bytes for other stuff. +/// +/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +pub const MAX_HEADER_SIZE: u32 = MAX_AUTHORITIES_COUNT + .saturating_mul(3) + .saturating_add(AVERAGE_HEADER_SIZE_IN_JUSTIFICATION); + /// Re-export `time_units` to make usage easier. pub use time_units::*; @@ -156,6 +178,15 @@ impl Chain for Millau { } } +impl ChainWithGrandpa for Millau { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_MILLAU_GRANDPA_PALLET_NAME; + const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; +} + impl ChainWithBeefy for Millau { type CommitmentHasher = Keccak256; type MmrHashing = Keccak256; diff --git a/primitives/chain-polkadot/Cargo.toml b/primitives/chain-polkadot/Cargo.toml index b26093a0570..def26bdda1c 100644 --- a/primitives/chain-polkadot/Cargo.toml +++ b/primitives/chain-polkadot/Cargo.toml @@ -7,19 +7,24 @@ edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] + # Bridge Dependencies +bp-header-chain = { path = "../header-chain", default-features = false } bp-polkadot-core = { path = "../polkadot-core", default-features = false } bp-runtime = { path = "../runtime", default-features = false } # Substrate Based Dependencies +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-polkadot-core/std", "bp-runtime/std", + "frame-support/std", "sp-api/std", ] diff --git a/primitives/chain-polkadot/src/lib.rs b/primitives/chain-polkadot/src/lib.rs index 0cada4e49a9..e1600102fcd 100644 --- a/primitives/chain-polkadot/src/lib.rs +++ b/primitives/chain-polkadot/src/lib.rs @@ -19,10 +19,42 @@ #![allow(clippy::too_many_arguments)] pub use bp_polkadot_core::*; -use bp_runtime::decl_bridge_finality_runtime_apis; + +use bp_header_chain::ChainWithGrandpa; +use bp_runtime::{decl_bridge_finality_runtime_apis, Chain}; +use frame_support::weights::Weight; /// Polkadot Chain -pub type Polkadot = PolkadotLike; +pub struct Polkadot; + +impl Chain for Polkadot { + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hasher = ::Hasher; + type Header = ::Header; + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Index = ::Index; + type Signature = ::Signature; + + fn max_extrinsic_size() -> u32 { + PolkadotLike::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + PolkadotLike::max_extrinsic_weight() + } +} + +impl ChainWithGrandpa for Polkadot { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_POLKADOT_GRANDPA_PALLET_NAME; + const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; +} /// Name of the With-Polkadot GRANDPA pallet instance that is deployed at bridged chains. pub const WITH_POLKADOT_GRANDPA_PALLET_NAME: &str = "BridgePolkadotGrandpa"; diff --git a/primitives/chain-rialto/Cargo.toml b/primitives/chain-rialto/Cargo.toml index 663f9076657..0a70e0504c9 100644 --- a/primitives/chain-rialto/Cargo.toml +++ b/primitives/chain-rialto/Cargo.toml @@ -10,6 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" # Bridge Dependencies +bp-header-chain = { path = "../header-chain", default-features = false } bp-messages = { path = "../messages", default-features = false } bp-runtime = { path = "../runtime", default-features = false } @@ -25,6 +26,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", d [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-messages/std", "bp-runtime/std", "frame-support/std", diff --git a/primitives/chain-rialto/src/lib.rs b/primitives/chain-rialto/src/lib.rs index ca0a3dfee88..a1c4a7267a7 100644 --- a/primitives/chain-rialto/src/lib.rs +++ b/primitives/chain-rialto/src/lib.rs @@ -18,6 +18,7 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] +use bp_header_chain::ChainWithGrandpa; use bp_messages::{ InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, }; @@ -72,6 +73,27 @@ pub const SESSION_LENGTH: BlockNumber = 4; /// Maximal number of GRANDPA authorities at Rialto. pub const MAX_AUTHORITIES_COUNT: u32 = 5; +/// Reasonable number of headers in the `votes_ancestries` on Rialto chain. +/// +/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + +/// Approximate average header size in `votes_ancestries` field of justification on Rialto chain. +/// +/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 256; + +/// Approximate maximal header size on Rialto chain. +/// +/// We expect maximal header to have digest item with the new authorities set for every consensus +/// engine (GRANDPA, Babe, BEEFY, ...) - so we multiply it by 3. And also +/// `AVERAGE_HEADER_SIZE_IN_JUSTIFICATION` bytes for other stuff. +/// +/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +pub const MAX_HEADER_SIZE: u32 = MAX_AUTHORITIES_COUNT + .saturating_mul(3) + .saturating_add(AVERAGE_HEADER_SIZE_IN_JUSTIFICATION); + /// Maximal size of encoded `bp_parachains::ParaStoredHeaderData` structure among all Rialto /// parachains. /// @@ -160,6 +182,15 @@ impl Chain for Rialto { } } +impl ChainWithGrandpa for Rialto { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_RIALTO_GRANDPA_PALLET_NAME; + const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; +} + frame_support::parameter_types! { pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); diff --git a/primitives/chain-rococo/Cargo.toml b/primitives/chain-rococo/Cargo.toml index 73a2450cd17..4e21bd38b7a 100644 --- a/primitives/chain-rococo/Cargo.toml +++ b/primitives/chain-rococo/Cargo.toml @@ -9,18 +9,22 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] # Bridge Dependencies + +bp-header-chain = { path = "../header-chain", default-features = false } bp-polkadot-core = { path = "../polkadot-core", default-features = false } bp-runtime = { path = "../runtime", default-features = false } # Substrate Based Dependencies + sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-polkadot-core/std", "bp-runtime/std", - "sp-api/std", "frame-support/std", + "sp-api/std", ] diff --git a/primitives/chain-rococo/src/lib.rs b/primitives/chain-rococo/src/lib.rs index 41fc65e0e93..0cb0b1d41e6 100644 --- a/primitives/chain-rococo/src/lib.rs +++ b/primitives/chain-rococo/src/lib.rs @@ -19,11 +19,42 @@ #![allow(clippy::too_many_arguments)] pub use bp_polkadot_core::*; -use bp_runtime::decl_bridge_finality_runtime_apis; -use frame_support::parameter_types; + +use bp_header_chain::ChainWithGrandpa; +use bp_runtime::{decl_bridge_finality_runtime_apis, Chain}; +use frame_support::{parameter_types, weights::Weight}; /// Rococo Chain -pub type Rococo = PolkadotLike; +pub struct Rococo; + +impl Chain for Rococo { + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hasher = ::Hasher; + type Header = ::Header; + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Index = ::Index; + type Signature = ::Signature; + + fn max_extrinsic_size() -> u32 { + PolkadotLike::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + PolkadotLike::max_extrinsic_weight() + } +} + +impl ChainWithGrandpa for Rococo { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_ROCOCO_GRANDPA_PALLET_NAME; + const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; +} parameter_types! { pub const SS58Prefix: u8 = 42; diff --git a/primitives/chain-westend/Cargo.toml b/primitives/chain-westend/Cargo.toml index 75f6727d764..13a2e597f9d 100644 --- a/primitives/chain-westend/Cargo.toml +++ b/primitives/chain-westend/Cargo.toml @@ -10,6 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" # Bridge Dependencies +bp-header-chain = { path = "../header-chain", default-features = false } bp-polkadot-core = { path = "../polkadot-core", default-features = false } bp-runtime = { path = "../runtime", default-features = false } @@ -21,6 +22,7 @@ sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", d [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-polkadot-core/std", "bp-runtime/std", "frame-support/std", diff --git a/primitives/chain-westend/src/lib.rs b/primitives/chain-westend/src/lib.rs index 910a6c4acbd..b6d41ece286 100644 --- a/primitives/chain-westend/src/lib.rs +++ b/primitives/chain-westend/src/lib.rs @@ -19,11 +19,42 @@ #![allow(clippy::too_many_arguments)] pub use bp_polkadot_core::*; + +use bp_header_chain::ChainWithGrandpa; use bp_runtime::{decl_bridge_finality_runtime_apis, Chain, Parachain}; use frame_support::weights::Weight; /// Westend Chain -pub type Westend = PolkadotLike; +pub struct Westend; + +impl Chain for Westend { + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hasher = ::Hasher; + type Header = ::Header; + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Index = ::Index; + type Signature = ::Signature; + + fn max_extrinsic_size() -> u32 { + PolkadotLike::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + PolkadotLike::max_extrinsic_weight() + } +} + +impl ChainWithGrandpa for Westend { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_WESTEND_GRANDPA_PALLET_NAME; + const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; +} /// Westmint parachain definition #[derive(Debug, Clone, Copy)] diff --git a/primitives/chain-wococo/Cargo.toml b/primitives/chain-wococo/Cargo.toml index 6b741cd4b73..25fd7b9fd94 100644 --- a/primitives/chain-wococo/Cargo.toml +++ b/primitives/chain-wococo/Cargo.toml @@ -9,18 +9,24 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] # Bridge Dependencies + +bp-header-chain = { path = "../header-chain", default-features = false } bp-polkadot-core = { path = "../polkadot-core", default-features = false } bp-runtime = { path = "../runtime", default-features = false } bp-rococo = { path = "../chain-rococo", default-features = false } # Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-polkadot-core/std", "bp-runtime/std", "bp-rococo/std", + "frame-support/std", "sp-api/std", ] diff --git a/primitives/chain-wococo/src/lib.rs b/primitives/chain-wococo/src/lib.rs index 1cf666c7f96..2df019496ab 100644 --- a/primitives/chain-wococo/src/lib.rs +++ b/primitives/chain-wococo/src/lib.rs @@ -22,10 +22,42 @@ pub use bp_polkadot_core::*; pub use bp_rococo::{ SS58Prefix, MAX_AUTHORITIES_COUNT, MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE, PARAS_PALLET_NAME, }; -use bp_runtime::decl_bridge_finality_runtime_apis; + +use bp_header_chain::ChainWithGrandpa; +use bp_runtime::{decl_bridge_finality_runtime_apis, Chain}; +use frame_support::weights::Weight; /// Wococo Chain -pub type Wococo = PolkadotLike; +pub struct Wococo; + +impl Chain for Wococo { + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hasher = ::Hasher; + type Header = ::Header; + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Index = ::Index; + type Signature = ::Signature; + + fn max_extrinsic_size() -> u32 { + PolkadotLike::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + PolkadotLike::max_extrinsic_weight() + } +} + +impl ChainWithGrandpa for Wococo { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_WOCOCO_GRANDPA_PALLET_NAME; + const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; +} /// Name of the With-Wococo GRANDPA pallet instance that is deployed at bridged chains. pub const WITH_WOCOCO_GRANDPA_PALLET_NAME: &str = "BridgeWococoGrandpa"; diff --git a/primitives/header-chain/src/lib.rs b/primitives/header-chain/src/lib.rs index 49d1ae89406..cafb8e7a3c8 100644 --- a/primitives/header-chain/src/lib.rs +++ b/primitives/header-chain/src/lib.rs @@ -119,7 +119,7 @@ impl AuthoritySet { } } -/// Data required for initializing the bridge pallet. +/// Data required for initializing the GRANDPA bridge pallet. /// /// The bridge needs to know where to start its sync from, and this provides that initial context. #[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, Clone, TypeInfo)] @@ -188,3 +188,48 @@ pub enum BridgeGrandpaCall { /// The `BridgeGrandpaCall` used by a chain. pub type BridgeGrandpaCallOf = BridgeGrandpaCall>; + +/// Substrate-based chain that is using direct GRANDPA finality. +/// +/// Keep in mind that parachains are relying on relay chain GRANDPA, so they should not implement +/// this trait. +pub trait ChainWithGrandpa: Chain { + /// Name of the bridge GRANDPA pallet (used in `construct_runtime` macro call) that is deployed + /// at some other chain to bridge with this `ChainWithGrandpa`. + /// + /// We assume that all chains that are bridging with this `ChainWithGrandpa` are using + /// the same name. + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str; + + /// Max number of GRANDPA authorities at the chain. + /// + /// This is a strict constant. If bridged chain will have more authorities than that, + /// the GRANDPA bridge pallet may halt. + const MAX_AUTHORITIES_COUNT: u32; + + /// Max reasonable number of headers in `votes_ancestries` vector of the GRANDPA justification. + /// + /// This isn't a strict limit. The relay may submit justifications with more headers in its + /// ancestry and the pallet will accept such justification. The limit is only used to compute + /// maximal refund amount and submitting justifications which exceed the limit, may be costly + /// to submitter. + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32; + + /// Maximal size of the chain header. The header may be the header that enacts new GRANDPA + /// authorities set (so it has large digest inside). + /// + /// This isn't a strict limit. The relay may submit larger headers and the pallet will accept + /// the call. The limit is only used to compute maximal refund amount and doing calls which + /// exceed the limit, may be costly to submitter. + const MAX_HEADER_SIZE: u32; + + /// Average size of the chain header from justification ancestry. We don't expect to see there + /// headers that change GRANDPA authorities set (GRANDPA will probably be able to finalize at + /// least one additional header per session on non test chains), so this is average size of + /// headers that aren't changing the set. + /// + /// This isn't a strict limit. The relay may submit justifications with larger headers in its + /// ancestry and the pallet will accept the call. The limit is only used to compute maximal + /// refund amount and doing calls which exceed the limit, may be costly to submitter. + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32; +} diff --git a/primitives/polkadot-core/src/lib.rs b/primitives/polkadot-core/src/lib.rs index b13ceb5df50..3b1712042d8 100644 --- a/primitives/polkadot-core/src/lib.rs +++ b/primitives/polkadot-core/src/lib.rs @@ -53,6 +53,28 @@ pub mod parachains; /// take twice as much here. pub const MAX_AUTHORITIES_COUNT: u32 = 2_048; +/// Reasonable number of headers in the `votes_ancestries` on Polkadot-like chains. +/// +/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + +/// Approximate average header size in `votes_ancestries` field of justification on Polkadot-like +/// chains. +/// +/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 256; + +/// Approximate maximal header size on Polkadot-like chains. +/// +/// We expect maximal header to have digest item with the new authorities set for every consensus +/// engine (GRANDPA, Babe, BEEFY, ...) - so we multiply it by 3. And also +/// `AVERAGE_HEADER_SIZE_IN_JUSTIFICATION` bytes for other stuff. +/// +/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +pub const MAX_HEADER_SIZE: u32 = MAX_AUTHORITIES_COUNT + .saturating_mul(3) + .saturating_add(AVERAGE_HEADER_SIZE_IN_JUSTIFICATION); + /// Number of extra bytes (excluding size of storage value itself) of storage proof, built at /// Polkadot-like chain. This mostly depends on number of entries in the storage trie. /// Some reserve is reserved to account future chain growth. diff --git a/relays/client-kusama/src/lib.rs b/relays/client-kusama/src/lib.rs index cd6e23a4607..83f6b30f4cb 100644 --- a/relays/client-kusama/src/lib.rs +++ b/relays/client-kusama/src/lib.rs @@ -17,7 +17,7 @@ //! Types used to connect to the Kusama chain. use bp_kusama::AccountInfoStorageMapKeyProvider; -use relay_substrate_client::{Chain, ChainWithBalances, ChainWithGrandpa, UnderlyingChainProvider}; +use relay_substrate_client::{Chain, ChainWithBalances, UnderlyingChainProvider}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -43,10 +43,6 @@ impl Chain for Kusama { type Call = (); } -impl ChainWithGrandpa for Kusama { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_kusama::WITH_KUSAMA_GRANDPA_PALLET_NAME; -} - impl ChainWithBalances for Kusama { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { AccountInfoStorageMapKeyProvider::final_key(account_id) diff --git a/relays/client-millau/src/lib.rs b/relays/client-millau/src/lib.rs index 34bbea92d57..711b4f32cc6 100644 --- a/relays/client-millau/src/lib.rs +++ b/relays/client-millau/src/lib.rs @@ -19,9 +19,9 @@ use bp_messages::MessageNonce; use codec::{Compact, Decode, Encode}; use relay_substrate_client::{ - BalanceOf, Chain, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, - ChainWithTransactions, ChainWithUtilityPallet, Error as SubstrateError, - FullRuntimeUtilityPallet, IndexOf, SignParam, UnderlyingChainProvider, UnsignedTransaction, + BalanceOf, Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, + ChainWithUtilityPallet, Error as SubstrateError, FullRuntimeUtilityPallet, IndexOf, SignParam, + UnderlyingChainProvider, UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; @@ -38,10 +38,6 @@ impl UnderlyingChainProvider for Millau { type Chain = bp_millau::Millau; } -impl ChainWithGrandpa for Millau { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_GRANDPA_PALLET_NAME; -} - impl ChainWithMessages for Millau { const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME; diff --git a/relays/client-polkadot/src/lib.rs b/relays/client-polkadot/src/lib.rs index ac67d55ab54..19326dd4c73 100644 --- a/relays/client-polkadot/src/lib.rs +++ b/relays/client-polkadot/src/lib.rs @@ -17,7 +17,7 @@ //! Types used to connect to the Polkadot chain. use bp_polkadot::AccountInfoStorageMapKeyProvider; -use relay_substrate_client::{Chain, ChainWithBalances, ChainWithGrandpa, UnderlyingChainProvider}; +use relay_substrate_client::{Chain, ChainWithBalances, UnderlyingChainProvider}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -43,11 +43,6 @@ impl Chain for Polkadot { type Call = (); } -impl ChainWithGrandpa for Polkadot { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = - bp_polkadot::WITH_POLKADOT_GRANDPA_PALLET_NAME; -} - impl ChainWithBalances for Polkadot { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { AccountInfoStorageMapKeyProvider::final_key(account_id) diff --git a/relays/client-rialto/src/lib.rs b/relays/client-rialto/src/lib.rs index 8ad31de4d58..39cc2721ddb 100644 --- a/relays/client-rialto/src/lib.rs +++ b/relays/client-rialto/src/lib.rs @@ -19,9 +19,9 @@ use bp_messages::MessageNonce; use codec::{Compact, Decode, Encode}; use relay_substrate_client::{ - BalanceOf, Chain, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, - ChainWithTransactions, Error as SubstrateError, IndexOf, RelayChain, SignParam, - UnderlyingChainProvider, UnsignedTransaction, + BalanceOf, Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, + Error as SubstrateError, IndexOf, RelayChain, SignParam, UnderlyingChainProvider, + UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; @@ -56,10 +56,6 @@ impl RelayChain for Rialto { bp_rialto::WITH_RIALTO_BRIDGE_PARAS_PALLET_NAME; } -impl ChainWithGrandpa for Rialto { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_rialto::WITH_RIALTO_GRANDPA_PALLET_NAME; -} - impl ChainWithMessages for Rialto { const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME; diff --git a/relays/client-rococo/src/lib.rs b/relays/client-rococo/src/lib.rs index a764759906a..a0730026e04 100644 --- a/relays/client-rococo/src/lib.rs +++ b/relays/client-rococo/src/lib.rs @@ -16,9 +16,7 @@ //! Types used to connect to the Rococo-Substrate chain. -use relay_substrate_client::{ - Chain, ChainWithBalances, ChainWithGrandpa, RelayChain, UnderlyingChainProvider, -}; +use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -47,10 +45,6 @@ impl Chain for Rococo { type Call = (); } -impl ChainWithGrandpa for Rococo { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_rococo::WITH_ROCOCO_GRANDPA_PALLET_NAME; -} - impl ChainWithBalances for Rococo { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { bp_rococo::AccountInfoStorageMapKeyProvider::final_key(account_id) diff --git a/relays/client-substrate/src/chain.rs b/relays/client-substrate/src/chain.rs index 4ec5edfc41b..b9c5793842e 100644 --- a/relays/client-substrate/src/chain.rs +++ b/relays/client-substrate/src/chain.rs @@ -73,11 +73,6 @@ pub trait RelayChain: Chain { const PARACHAINS_FINALITY_PALLET_NAME: &'static str; } -/// Substrate-based parachain from minimal relay-client point of view. -pub trait Parachain: Chain + ParachainBase {} - -impl Parachain for T where T: UnderlyingChainProvider + Chain + ParachainBase {} - /// Substrate-based chain that is using direct GRANDPA finality from minimal relay-client point of /// view. /// @@ -92,6 +87,20 @@ pub trait ChainWithGrandpa: Chain { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str; } +impl ChainWithGrandpa for T +where + T: Chain + UnderlyingChainProvider, + T::Chain: bp_header_chain::ChainWithGrandpa, +{ + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = + ::WITH_CHAIN_GRANDPA_PALLET_NAME; +} + +/// Substrate-based parachain from minimal relay-client point of view. +pub trait Parachain: Chain + ParachainBase {} + +impl Parachain for T where T: UnderlyingChainProvider + Chain + ParachainBase {} + /// Substrate-based chain with messaging support from minimal relay-client point of view. pub trait ChainWithMessages: Chain { /// Name of the bridge messages pallet (used in `construct_runtime` macro call) that is deployed diff --git a/relays/client-westend/src/lib.rs b/relays/client-westend/src/lib.rs index 6467f431335..a8a8df36b8b 100644 --- a/relays/client-westend/src/lib.rs +++ b/relays/client-westend/src/lib.rs @@ -16,9 +16,7 @@ //! Types used to connect to the Westend chain. -use relay_substrate_client::{ - Chain, ChainWithBalances, ChainWithGrandpa, RelayChain, UnderlyingChainProvider, -}; +use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -53,11 +51,6 @@ impl RelayChain for Westend { bp_westend::WITH_WESTEND_BRIDGE_PARAS_PALLET_NAME; } -impl ChainWithGrandpa for Westend { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = - bp_westend::WITH_WESTEND_GRANDPA_PALLET_NAME; -} - impl ChainWithBalances for Westend { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { bp_westend::AccountInfoStorageMapKeyProvider::final_key(account_id) diff --git a/relays/client-wococo/src/lib.rs b/relays/client-wococo/src/lib.rs index a3e10729ba0..8f30572db63 100644 --- a/relays/client-wococo/src/lib.rs +++ b/relays/client-wococo/src/lib.rs @@ -16,9 +16,7 @@ //! Types used to connect to the Wococo-Substrate chain. -use relay_substrate_client::{ - Chain, ChainWithBalances, ChainWithGrandpa, RelayChain, UnderlyingChainProvider, -}; +use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -47,10 +45,6 @@ impl Chain for Wococo { type Call = (); } -impl ChainWithGrandpa for Wococo { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_wococo::WITH_WOCOCO_GRANDPA_PALLET_NAME; -} - impl ChainWithBalances for Wococo { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { bp_wococo::AccountInfoStorageMapKeyProvider::final_key(account_id) From 1cb249da330f2eaafcd77f086ba7ae7ae2f0b9bd Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 17 Feb 2023 09:52:35 +0100 Subject: [PATCH 200/263] Merge fix --- parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 8258380c7ee..8f8dadfd1d4 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -395,8 +395,6 @@ impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_wococo::Wococo; type MaxRequests = MaxRequests; type HeadersToKeep = RelayChainHeadersToKeep; - type MaxBridgedAuthorities = - frame_support::traits::ConstU32<{ bp_wococo::MAX_AUTHORITIES_COUNT }>; type WeightInfo = weights::pallet_bridge_grandpa_bridge_wococo_grandpa::WeightInfo; } @@ -406,8 +404,6 @@ impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_rococo::Rococo; type MaxRequests = MaxRequests; type HeadersToKeep = RelayChainHeadersToKeep; - type MaxBridgedAuthorities = - frame_support::traits::ConstU32<{ bp_rococo::MAX_AUTHORITIES_COUNT }>; type WeightInfo = weights::pallet_bridge_grandpa_bridge_rococo_grandpa::WeightInfo; } From d6149f20e4525c32c9d00972994e4c63d201c125 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 21 Feb 2023 09:33:07 +0100 Subject: [PATCH 201/263] Squashed 'bridges/' changes from d39f563be..78e3357c0 78e3357c0 RefundRelayerForMessagesFromParachain improvements (#1895) 131b17359 optimize justification before submit (#1887) 5bc279ebb use complex transactions on RBH/WBH bridge hubs (#1893) 8f0c09ab9 Bump clap from 4.1.4 to 4.1.6 66429b06a Bump sysinfo from 0.27.7 to 0.28.0 8b329ee8f Bump trie-db from 0.25.0 to 0.25.1 635cfccfd Bump time from 0.3.17 to 0.3.19 git-subtree-dir: bridges git-subtree-split: 78e3357c0387c95317b8c3e5c4d9316f3a9f3ef4 --- Cargo.lock | 36 +- bin/millau/node/Cargo.toml | 2 +- bin/millau/runtime/src/lib.rs | 28 +- bin/rialto-parachain/node/Cargo.toml | 2 +- bin/rialto/node/Cargo.toml | 2 +- bin/runtime-common/src/messages_call_ext.rs | 6 +- .../src/refund_relayer_extension.rs | 347 ++++++++++-------- modules/grandpa/src/lib.rs | 5 + modules/parachains/src/call_ext.rs | 9 +- primitives/header-chain/src/justification.rs | 121 +++++- primitives/header-chain/src/storage_keys.rs | 26 ++ .../header-chain/tests/justification.rs | 86 ++++- primitives/runtime/Cargo.toml | 2 +- primitives/runtime/src/lib.rs | 24 ++ primitives/test-utils/src/lib.rs | 5 +- ...ub_rococo_messages_to_bridge_hub_wococo.rs | 6 +- ...ub_wococo_messages_to_bridge_hub_rococo.rs | 6 +- relays/client-bridge-hub-rococo/src/lib.rs | 9 +- .../src/runtime_wrapper.rs | 11 +- relays/client-bridge-hub-wococo/src/lib.rs | 9 +- .../src/runtime_wrapper.rs | 11 +- relays/client-millau/src/lib.rs | 2 +- relays/client-substrate/src/calls.rs | 9 + relays/client-substrate/src/chain.rs | 17 + relays/client-substrate/src/lib.rs | 4 +- .../src/finality/engine.rs | 51 ++- .../src/finality/target.rs | 4 + relays/utils/Cargo.toml | 2 +- 28 files changed, 628 insertions(+), 214 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5298107be1..da7247b1c52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1034,7 +1034,7 @@ dependencies = [ "sp-state-machine 0.13.0", "sp-std 5.0.0", "sp-trie 7.0.0", - "trie-db 0.25.0", + "trie-db 0.25.1", ] [[package]] @@ -1392,9 +1392,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.4" +version = "4.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" +checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3" dependencies = [ "bitflags", "clap_derive", @@ -1818,7 +1818,7 @@ name = "cumulus-client-cli" version = "0.1.0" source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "parity-scale-codec", "sc-chain-spec", "sc-cli", @@ -3151,7 +3151,7 @@ dependencies = [ "Inflector", "array-bytes 4.2.0", "chrono", - "clap 4.1.4", + "clap 4.1.6", "comfy-table", "frame-benchmarking", "frame-support", @@ -5451,7 +5451,7 @@ version = "0.1.0" dependencies = [ "beefy-gadget", "beefy-gadget-rpc", - "clap 4.1.4", + "clap 4.1.6", "frame-benchmarking", "frame-benchmarking-cli", "jsonrpsee 0.16.2", @@ -5859,7 +5859,7 @@ name = "node-inspect" version = "0.9.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -7554,7 +7554,7 @@ name = "polkadot-cli" version = "0.9.37" source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "frame-benchmarking-cli", "futures", "log", @@ -9432,7 +9432,7 @@ dependencies = [ name = "rialto-bridge-node" version = "0.1.0" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "frame-benchmarking", "frame-benchmarking-cli", "frame-support", @@ -9459,7 +9459,7 @@ dependencies = [ name = "rialto-parachain-collator" version = "0.1.0" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "cumulus-client-cli", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", @@ -9999,7 +9999,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f11 dependencies = [ "array-bytes 4.2.0", "chrono", - "clap 4.1.4", + "clap 4.1.6", "fdlimit", "futures", "libp2p", @@ -10793,7 +10793,7 @@ name = "sc-storage-monitor" version = "0.1.0" source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "futures", "log", "nix 0.26.2", @@ -13015,9 +13015,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.27.7" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975fe381e0ecba475d4acff52466906d95b153a40324956552e027b2a9eaa89e" +checksum = "727220a596b4ca0af040a07091e49f5c105ec8f2592674339a5bf35be592f76e" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys", @@ -13532,9 +13532,9 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bcd4e7fbb3bcab17b02596b53876e36eb39663ddd87884bfd88c69cdeb2ebd6" +checksum = "3390c0409daaa6027d6681393316f4ccd3ff82e1590a1e4725014e3ae2bf1920" dependencies = [ "hash-db", "hashbrown 0.13.2", @@ -13609,7 +13609,7 @@ name = "try-runtime-cli" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "clap 4.1.4", + "clap 4.1.6", "frame-remote-externalities", "hex", "log", @@ -13665,7 +13665,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "digest 0.10.6", "rand 0.8.5", "static_assertions", diff --git a/bin/millau/node/Cargo.toml b/bin/millau/node/Cargo.toml index fea96c9b11f..3d3d238943d 100644 --- a/bin/millau/node/Cargo.toml +++ b/bin/millau/node/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.1.4", features = ["derive"] } +clap = { version = "4.1.6", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } serde_json = "1.0.93" diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index 5a18f8b12c0..369bea2b1fa 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -80,7 +80,13 @@ pub use pallet_sudo::Call as SudoCall; pub use pallet_timestamp::Call as TimestampCall; pub use pallet_xcm::Call as XcmCall; -use bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages; +use bridge_runtime_common::{ + generate_bridge_reject_obsolete_headers_and_messages, + refund_relayer_extension::{ + ActualFeeRefund, RefundBridgedParachainMessages, RefundableMessagesLane, + RefundableParachain, + }, +}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; pub use sp_runtime::{Perbill, Permill}; @@ -582,17 +588,15 @@ generate_bridge_reject_obsolete_headers_and_messages! { BridgeRialtoMessages, BridgeRialtoParachainMessages } +bp_runtime::generate_static_str_provider!(BridgeRefundRialtoPara2000Lane0Msgs); /// Signed extension that refunds relayers that are delivering messages from the Rialto parachain. -pub type BridgeRefundRialtoParachainRelayers = - bridge_runtime_common::refund_relayer_extension::RefundRelayerForMessagesFromParachain< - Runtime, - RialtoGrandpaInstance, - WithRialtoParachainsInstance, - WithRialtoParachainMessagesInstance, - RialtoParachainId, - RialtoParachainMessagesLane, - Runtime, - >; +pub type BridgeRefundRialtoParachainMessages = RefundBridgedParachainMessages< + Runtime, + RefundableParachain, + RefundableMessagesLane, + ActualFeeRefund, + StrBridgeRefundRialtoPara2000Lane0Msgs, +>; /// The address format for describing accounts. pub type Address = AccountId; @@ -615,7 +619,7 @@ pub type SignedExtra = ( frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, BridgeRejectObsoleteHeadersAndMessages, - BridgeRefundRialtoParachainRelayers, + BridgeRefundRialtoParachainMessages, ); /// The payload being signed in transactions. pub type SignedPayload = generic::SignedPayload; diff --git a/bin/rialto-parachain/node/Cargo.toml b/bin/rialto-parachain/node/Cargo.toml index 5e1eba1fee5..c20b7f248cc 100644 --- a/bin/rialto-parachain/node/Cargo.toml +++ b/bin/rialto-parachain/node/Cargo.toml @@ -17,7 +17,7 @@ default = [] runtime-benchmarks = ['rialto-parachain-runtime/runtime-benchmarks'] [dependencies] -clap = { version = "4.1.4", features = ["derive"] } +clap = { version = "4.1.6", features = ["derive"] } log = '0.4.17' codec = { package = 'parity-scale-codec', version = '3.1.5' } serde = { version = '1.0', features = ['derive'] } diff --git a/bin/rialto/node/Cargo.toml b/bin/rialto/node/Cargo.toml index d03deaaabef..556088498f7 100644 --- a/bin/rialto/node/Cargo.toml +++ b/bin/rialto/node/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.1.4", features = ["derive"] } +clap = { version = "4.1.6", features = ["derive"] } serde_json = "1.0.93" # Bridge dependencies diff --git a/bin/runtime-common/src/messages_call_ext.rs b/bin/runtime-common/src/messages_call_ext.rs index 20e604142d9..740d17129c8 100644 --- a/bin/runtime-common/src/messages_call_ext.rs +++ b/bin/runtime-common/src/messages_call_ext.rs @@ -18,12 +18,12 @@ use crate::messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; use bp_messages::{LaneId, MessageNonce}; -use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; +use frame_support::{dispatch::CallableCallFor, traits::IsSubType, RuntimeDebug}; use pallet_bridge_messages::{Config, Pallet}; -use sp_runtime::{transaction_validity::TransactionValidity, RuntimeDebug}; +use sp_runtime::transaction_validity::TransactionValidity; /// Info about a `ReceiveMessagesProof` call which tries to update a single lane. -#[derive(Copy, Clone, PartialEq, RuntimeDebug)] +#[derive(PartialEq, RuntimeDebug)] pub struct ReceiveMessagesProofInfo { pub lane_id: LaneId, pub best_proof_nonce: MessageNonce, diff --git a/bin/runtime-common/src/refund_relayer_extension.rs b/bin/runtime-common/src/refund_relayer_extension.rs index ac65617483d..9efeedf24dd 100644 --- a/bin/runtime-common/src/refund_relayer_extension.rs +++ b/bin/runtime-common/src/refund_relayer_extension.rs @@ -23,6 +23,7 @@ use crate::messages_call_ext::{ MessagesCallSubType, ReceiveMessagesProofHelper, ReceiveMessagesProofInfo, }; use bp_messages::LaneId; +use bp_runtime::StaticStrProvider; use codec::{Decode, Encode}; use frame_support::{ dispatch::{CallableCallFor, DispatchInfo, Dispatchable, PostDispatchInfo}, @@ -46,28 +47,77 @@ use sp_runtime::{ }; use sp_std::{marker::PhantomData, vec, vec::Vec}; -// TODO (https://github.com/paritytech/parity-bridges-common/issues/1667): -// support multiple bridges in this extension +// without this typedef rustfmt fails with internal err +type BalanceOf = + <::OnChargeTransaction as OnChargeTransaction>::Balance; +type CallOf = ::RuntimeCall; + +/// Trait identifying a bridged parachain. A relayer might be refunded for delivering messages +/// coming from this parachain. +trait RefundableParachainId { + /// The instance of the bridge parachains pallet. + type Instance; + /// The parachain Id. + type Id: Get; +} -/// Transaction fee calculation. -pub trait TransactionFeeCalculation { - /// Compute fee that is paid for given transaction. The fee is later refunded to relayer. - fn compute_fee( +/// Default implementation of `RefundableParachainId`. +pub struct RefundableParachain(PhantomData<(Instance, Id)>); + +impl RefundableParachainId for RefundableParachain +where + Id: Get, +{ + type Instance = Instance; + type Id = Id; +} + +/// Trait identifying a bridged messages lane. A relayer might be refunded for delivering messages +/// coming from this lane. +trait RefundableMessagesLaneId { + /// The instance of the bridge messages pallet. + type Instance; + /// The messages lane id. + type Id: Get; +} + +/// Default implementation of `RefundableMessagesLaneId`. +pub struct RefundableMessagesLane(PhantomData<(Instance, Id)>); + +impl RefundableMessagesLaneId for RefundableMessagesLane +where + Id: Get, +{ + type Instance = Instance; + type Id = Id; +} + +/// Refund calculator. +pub trait RefundCalculator { + // The underlying integer type in which the refund is calculated. + type Balance; + + /// Compute refund for given transaction. + fn compute_refund( info: &DispatchInfo, post_info: &PostDispatchInfo, len: usize, - tip: Balance, - ) -> Balance; + tip: Self::Balance, + ) -> Self::Balance; } -impl TransactionFeeCalculation> for R +/// `RefundCalculator` implementation which refunds the actual transaction fee. +pub struct ActualFeeRefund(PhantomData); + +impl RefundCalculator for ActualFeeRefund where R: TransactionPaymentConfig, - ::RuntimeCall: - Dispatchable, + CallOf: Dispatchable, BalanceOf: FixedPointOperand, { - fn compute_fee( + type Balance = BalanceOf; + + fn compute_refund( info: &DispatchInfo, post_info: &PostDispatchInfo, len: usize, @@ -77,7 +127,55 @@ where } } -/// Signed extension that refunds relayer for new messages coming from the parachain. +/// Data that is crafted in `pre_dispatch` method and used at `post_dispatch`. +#[cfg_attr(test, derive(Debug, PartialEq))] +pub struct PreDispatchData { + /// Transaction submitter (relayer) account. + relayer: AccountId, + /// Type of the call. + call_info: CallInfo, +} + +/// Type of the call that the extension recognizes. +#[derive(RuntimeDebugNoBound, PartialEq)] +pub enum CallInfo { + /// Relay chain finality + parachain finality + message delivery calls. + AllFinalityAndDelivery(RelayBlockNumber, SubmitParachainHeadsInfo, ReceiveMessagesProofInfo), + /// Parachain finality + message delivery calls. + ParachainFinalityAndDelivery(SubmitParachainHeadsInfo, ReceiveMessagesProofInfo), + /// Standalone message delivery call. + Delivery(ReceiveMessagesProofInfo), +} + +impl CallInfo { + /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. + fn submit_finality_proof_info(&self) -> Option { + match *self { + Self::AllFinalityAndDelivery(info, _, _) => Some(info), + _ => None, + } + } + + /// Returns the pre-dispatch `SubmitParachainHeadsInfo`. + fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> { + match self { + Self::AllFinalityAndDelivery(_, info, _) => Some(info), + Self::ParachainFinalityAndDelivery(info, _) => Some(info), + _ => None, + } + } + + /// Returns the pre-dispatch `ReceiveMessagesProofInfo`. + fn receive_messages_proof_info(&self) -> &ReceiveMessagesProofInfo { + match self { + Self::AllFinalityAndDelivery(_, _, info) => info, + Self::ParachainFinalityAndDelivery(_, info) => info, + Self::Delivery(info) => info, + } + } +} + +/// Signed extension that refunds a relayer for new messages coming from a parachain. /// /// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) /// with message delivery transaction. Batch may deliver either both relay chain header and @@ -86,29 +184,29 @@ where /// /// Extension does not refund transaction tip due to security reasons. #[derive( + DefaultNoBound, CloneNoBound, Decode, - DefaultNoBound, Encode, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo, )] -#[scale_info(skip_type_params(RT, GI, PI, MI, PID, LID, FEE))] -pub struct RefundRelayerForMessagesFromParachain( - PhantomData<(RT, GI, PI, MI, PID, LID, FEE)>, +#[scale_info(skip_type_params(Runtime, Para, Msgs, Refund, Id))] +pub struct RefundBridgedParachainMessages( + PhantomData<(Runtime, Para, Msgs, Refund, Id)>, ); -impl - RefundRelayerForMessagesFromParachain +impl + RefundBridgedParachainMessages where - R: UtilityConfig>, - CallOf: IsSubType, R>>, + Runtime: UtilityConfig>, + CallOf: IsSubType, Runtime>>, { - fn expand_call<'a>(&self, call: &'a CallOf) -> Option>> { + fn expand_call<'a>(&self, call: &'a CallOf) -> Option>> { let calls = match call.is_sub_type() { - Some(UtilityCall::::batch_all { ref calls }) => { + Some(UtilityCall::::batch_all { ref calls }) => { if calls.len() > 3 { return None } @@ -123,71 +221,30 @@ where } } -/// Data that is crafted in `pre_dispatch` method and used at `post_dispatch`. -#[derive(PartialEq)] -#[cfg_attr(test, derive(Debug))] -pub struct PreDispatchData { - /// Transaction submitter (relayer) account. - relayer: AccountId, - /// Type of the call. - pub call_type: CallType, -} - -/// Type of the call that the extension recognizes. -#[derive(Clone, Copy, PartialEq, RuntimeDebugNoBound)] -pub enum CallType { - /// Relay chain finality + parachain finality + message delivery calls. - AllFinalityAndDelivery(RelayBlockNumber, SubmitParachainHeadsInfo, ReceiveMessagesProofInfo), - /// Parachain finality + message delivery calls. - ParachainFinalityAndDelivery(SubmitParachainHeadsInfo, ReceiveMessagesProofInfo), - /// Standalone message delivery call. - Delivery(ReceiveMessagesProofInfo), -} - -impl CallType { - /// Returns the pre-dispatch messages pallet state. - fn receive_messages_proof_info(&self) -> ReceiveMessagesProofInfo { - match *self { - Self::AllFinalityAndDelivery(_, _, info) => info, - Self::ParachainFinalityAndDelivery(_, info) => info, - Self::Delivery(info) => info, - } - } -} - -// without this typedef rustfmt fails with internal err -type BalanceOf = - <::OnChargeTransaction as OnChargeTransaction>::Balance; -type CallOf = ::RuntimeCall; - -impl SignedExtension - for RefundRelayerForMessagesFromParachain +impl SignedExtension + for RefundBridgedParachainMessages where - R: 'static - + Send - + Sync - + UtilityConfig> - + BoundedBridgeGrandpaConfig - + ParachainsConfig - + MessagesConfig + Self: 'static + Send + Sync, + Runtime: UtilityConfig> + + BoundedBridgeGrandpaConfig + + ParachainsConfig + + MessagesConfig + RelayersConfig, - GI: 'static + Send + Sync, - PI: 'static + Send + Sync, - MI: 'static + Send + Sync, - PID: 'static + Send + Sync + Get, - LID: 'static + Send + Sync + Get, - FEE: 'static + Send + Sync + TransactionFeeCalculation<::Reward>, - CallOf: Dispatchable - + IsSubType, R>> - + GrandpaCallSubType - + ParachainsCallSubType - + MessagesCallSubType, + Para: RefundableParachainId, + Msgs: RefundableMessagesLaneId, + Refund: RefundCalculator, + Id: StaticStrProvider, + CallOf: Dispatchable + + IsSubType, Runtime>> + + GrandpaCallSubType + + ParachainsCallSubType + + MessagesCallSubType, { - const IDENTIFIER: &'static str = "RefundRelayerForMessagesFromParachain"; - type AccountId = R::AccountId; - type Call = CallOf; + const IDENTIFIER: &'static str = Id::STR; + type AccountId = Runtime::AccountId; + type Call = CallOf; type AdditionalSigned = (); - type Pre = Option>; + type Pre = Option>; fn additional_signed(&self) -> Result<(), TransactionValidityError> { Ok(()) @@ -200,15 +257,12 @@ where _info: &DispatchInfoOf, _len: usize, ) -> TransactionValidity { - let calls = match self.expand_call(call) { - Some(calls) => calls, - None => return Ok(ValidTransaction::default()), - }; - - for call in calls { - call.check_obsolete_submit_finality_proof()?; - call.check_obsolete_submit_parachain_heads()?; - call.check_obsolete_receive_messages_proof()?; + if let Some(calls) = self.expand_call(call) { + for nested_call in calls { + nested_call.check_obsolete_submit_finality_proof()?; + nested_call.check_obsolete_submit_parachain_heads()?; + nested_call.check_obsolete_receive_messages_proof()?; + } } Ok(ValidTransaction::default()) @@ -225,35 +279,35 @@ where self.validate(who, call, info, len).map(drop)?; // Try to check if the tx matches one of types we support. - let parse_call_type = || { + let parse_call = || { let mut calls = self.expand_call(call)?.into_iter(); match calls.len() { - 3 => Some(CallType::AllFinalityAndDelivery( + 3 => Some(CallInfo::AllFinalityAndDelivery( calls.next()?.submit_finality_proof_info()?, - calls.next()?.submit_parachain_heads_info_for(PID::get())?, - calls.next()?.receive_messages_proof_info_for(LID::get())?, + calls.next()?.submit_parachain_heads_info_for(Para::Id::get())?, + calls.next()?.receive_messages_proof_info_for(Msgs::Id::get())?, )), - 2 => Some(CallType::ParachainFinalityAndDelivery( - calls.next()?.submit_parachain_heads_info_for(PID::get())?, - calls.next()?.receive_messages_proof_info_for(LID::get())?, + 2 => Some(CallInfo::ParachainFinalityAndDelivery( + calls.next()?.submit_parachain_heads_info_for(Para::Id::get())?, + calls.next()?.receive_messages_proof_info_for(Msgs::Id::get())?, )), - 1 => Some(CallType::Delivery( - calls.next()?.receive_messages_proof_info_for(LID::get())?, + 1 => Some(CallInfo::Delivery( + calls.next()?.receive_messages_proof_info_for(Msgs::Id::get())?, )), _ => None, } }; - Ok(parse_call_type().map(|call_type| { + Ok(parse_call().map(|call_info| { log::trace!( target: "runtime::bridge", - "RefundRelayerForMessagesFromParachain from parachain {} via {:?} \ - parsed bridge transaction in pre-dispatch: {:?}", - PID::get(), - LID::get(), - call_type, + "{} from parachain {} via {:?} parsed bridge transaction in pre-dispatch: {:?}", + Self::IDENTIFIER, + Para::Id::get(), + Msgs::Id::get(), + call_info, ); - PreDispatchData { relayer: who.clone(), call_type } + PreDispatchData { relayer: who.clone(), call_info } })) } @@ -270,14 +324,14 @@ where } // We don't refund anything for transactions that we don't support. - let (relayer, call_type) = match pre { - Some(Some(pre)) => (pre.relayer, pre.call_type), + let (relayer, call_info) = match pre { + Some(Some(pre)) => (pre.relayer, pre.call_info), _ => return Ok(()), }; // check if relay chain state has been updated - if let CallType::AllFinalityAndDelivery(relay_block_number, _, _) = call_type { - if !SubmitFinalityProofHelper::::was_successful(relay_block_number) { + if let Some(relay_block_number) = call_info.submit_finality_proof_info() { + if !SubmitFinalityProofHelper::::was_successful(relay_block_number) { // we only refund relayer if all calls have updated chain state return Ok(()) } @@ -291,44 +345,44 @@ where } // check if parachain state has been updated - match call_type { - CallType::AllFinalityAndDelivery(_, parachain_heads_info, _) | - CallType::ParachainFinalityAndDelivery(parachain_heads_info, _) => { - if !SubmitParachainHeadsHelper::::was_successful(¶chain_heads_info) { - // we only refund relayer if all calls have updated chain state - return Ok(()) - } - }, - _ => (), + if let Some(para_proof_info) = call_info.submit_parachain_heads_info() { + if !SubmitParachainHeadsHelper::::was_successful( + para_proof_info, + ) { + // we only refund relayer if all calls have updated chain state + return Ok(()) + } } // Check if the `ReceiveMessagesProof` call delivered at least some of the messages that // it contained. If this happens, we consider the transaction "helpful" and refund it. - let messages_proof_info = call_type.receive_messages_proof_info(); - if !ReceiveMessagesProofHelper::::was_partially_successful(&messages_proof_info) { + let msgs_proof_info = call_info.receive_messages_proof_info(); + if !ReceiveMessagesProofHelper::::was_partially_successful( + msgs_proof_info, + ) { return Ok(()) } // regarding the tip - refund that happens here (at this side of the bridge) isn't the whole // relayer compensation. He'll receive some amount at the other side of the bridge. It shall - // (in theory) cover the tip here. Otherwise, if we'll be compensating tip here, some + // (in theory) cover the tip there. Otherwise, if we'll be compensating tip here, some // malicious relayer may use huge tips, effectively depleting account that pay rewards. The // cost of this attack is nothing. Hence we use zero as tip here. let tip = Zero::zero(); - // compute the relayer reward - let reward = FEE::compute_fee(info, post_info, len, tip); - - // finally - register reward in relayers pallet - RelayersPallet::::register_relayer_reward(LID::get(), &relayer, reward); + // compute the relayer refund + let refund = Refund::compute_refund(info, post_info, len, tip); + // finally - register refund in relayers pallet + RelayersPallet::::register_relayer_reward(Msgs::Id::get(), &relayer, refund); log::trace!( target: "runtime::bridge", - "RefundRelayerForMessagesFromParachain from parachain {} via {:?} has registered {:?} reward: {:?}", - PID::get(), - LID::get(), + "{} from parachain {} via {:?} has registered reward: {:?} for {:?}", + Self::IDENTIFIER, + Para::Id::get(), + Msgs::Id::get(), + refund, relayer, - reward, ); Ok(()) @@ -353,18 +407,17 @@ mod tests { }; parameter_types! { - pub TestParachain: u32 = 1000; + TestParachain: u32 = 1000; pub TestLaneId: LaneId = TEST_LANE_ID; } - type TestExtension = RefundRelayerForMessagesFromParachain< - TestRuntime, - (), - (), - (), - TestParachain, - TestLaneId, + bp_runtime::generate_static_str_provider!(TestExtension); + type TestExtension = RefundBridgedParachainMessages< TestRuntime, + RefundableParachain<(), TestParachain>, + RefundableMessagesLane<(), TestLaneId>, + ActualFeeRefund, + StrTestExtension, >; fn relayer_account_at_this_chain() -> ThisChainAccountId { @@ -470,7 +523,7 @@ mod tests { fn all_finality_pre_dispatch_data() -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_type: CallType::AllFinalityAndDelivery( + call_info: CallInfo::AllFinalityAndDelivery( 200, SubmitParachainHeadsInfo { at_relay_block_number: 200, @@ -489,7 +542,7 @@ mod tests { fn parachain_finality_pre_dispatch_data() -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_type: CallType::ParachainFinalityAndDelivery( + call_info: CallInfo::ParachainFinalityAndDelivery( SubmitParachainHeadsInfo { at_relay_block_number: 200, para_id: ParaId(TestParachain::get()), @@ -507,7 +560,7 @@ mod tests { fn delivery_pre_dispatch_data() -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_type: CallType::Delivery(ReceiveMessagesProofInfo { + call_info: CallInfo::Delivery(ReceiveMessagesProofInfo { lane_id: TEST_LANE_ID, best_proof_nonce: 200, best_stored_nonce: 100, @@ -520,14 +573,14 @@ mod tests { } fn run_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestExtension = RefundRelayerForMessagesFromParachain(PhantomData); + let extension: TestExtension = RefundBridgedParachainMessages(PhantomData); extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_pre_dispatch( call: RuntimeCall, ) -> Result>, TransactionValidityError> { - let extension: TestExtension = RefundRelayerForMessagesFromParachain(PhantomData); + let extension: TestExtension = RefundBridgedParachainMessages(PhantomData); extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index 197726dd47a..b11da53f7b3 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -1187,6 +1187,11 @@ mod tests { bp_header_chain::storage_keys::pallet_operating_mode_key("Grandpa").0, ); + assert_eq!( + CurrentAuthoritySet::::storage_value_final_key().to_vec(), + bp_header_chain::storage_keys::current_authority_set_key("Grandpa").0, + ); + assert_eq!( BestFinalized::::storage_value_final_key().to_vec(), bp_header_chain::storage_keys::best_finalized_key("Grandpa").0, diff --git a/modules/parachains/src/call_ext.rs b/modules/parachains/src/call_ext.rs index 41649336579..5aed9e80ba0 100644 --- a/modules/parachains/src/call_ext.rs +++ b/modules/parachains/src/call_ext.rs @@ -17,14 +17,11 @@ use crate::{Config, Pallet, RelayBlockNumber}; use bp_parachains::BestParaHeadHash; use bp_polkadot_core::parachains::{ParaHash, ParaId}; -use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; -use sp_runtime::{ - transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, - RuntimeDebug, -}; +use frame_support::{dispatch::CallableCallFor, traits::IsSubType, RuntimeDebug}; +use sp_runtime::transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}; /// Info about a `SubmitParachainHeads` call which tries to update a single parachain. -#[derive(Copy, Clone, PartialEq, RuntimeDebug)] +#[derive(PartialEq, RuntimeDebug)] pub struct SubmitParachainHeadsInfo { pub at_relay_block_number: RelayBlockNumber, pub para_id: ParaId, diff --git a/primitives/header-chain/src/justification.rs b/primitives/header-chain/src/justification.rs index dadd48a4850..43adc801254 100644 --- a/primitives/header-chain/src/justification.rs +++ b/primitives/header-chain/src/justification.rs @@ -71,6 +71,14 @@ pub enum Error { ExtraHeadersInVotesAncestries, } +/// Given GRANDPA authorities set size, return number of valid authorities votes that the +/// justification must have to be valid. +/// +/// This function assumes that all authorities have the same vote weight. +pub fn required_justification_precommits(authorities_set_length: u32) -> u32 { + authorities_set_length - authorities_set_length.saturating_sub(1) / 3 +} + /// Decode justification target. pub fn decode_justification_target( raw_justification: &[u8], @@ -80,6 +88,27 @@ pub fn decode_justification_target( .map_err(|_| Error::JustificationDecode) } +/// Verify and optimize given justification by removing unknown and duplicate votes. +pub fn optimize_justification( + finalized_target: (Header::Hash, Header::Number), + authorities_set_id: SetId, + authorities_set: &VoterSet, + justification: GrandpaJustification

, +) -> Result, Error> +where + Header::Number: finality_grandpa::BlockNumberOps, +{ + let mut optimizer = OptimizationCallbacks(Vec::new()); + verify_justification_with_callbacks( + finalized_target, + authorities_set_id, + authorities_set, + &justification, + &mut optimizer, + )?; + Ok(optimizer.optimize(justification)) +} + /// Verify that justification, that is generated by given authority set, finalizes given header. pub fn verify_justification( finalized_target: (Header::Hash, Header::Number), @@ -87,6 +116,83 @@ pub fn verify_justification( authorities_set: &VoterSet, justification: &GrandpaJustification
, ) -> Result<(), Error> +where + Header::Number: finality_grandpa::BlockNumberOps, +{ + verify_justification_with_callbacks( + finalized_target, + authorities_set_id, + authorities_set, + justification, + &mut (), + ) +} + +/// Verification callbacks. +trait VerificationCallbacks { + /// Called when we see a precommit from unknown authority. + fn on_unkown_authority(&mut self, precommit_idx: usize) -> Result<(), Error>; + /// Called when we see a precommit with duplicate vote from known authority. + fn on_duplicate_authority_vote(&mut self, precommit_idx: usize) -> Result<(), Error>; + /// Called when we see a precommit after we've collected enough votes from authorities. + fn on_redundant_authority_vote(&mut self, precommit_idx: usize) -> Result<(), Error>; +} + +/// Verification callbacks for justification optimization. +struct OptimizationCallbacks(Vec); + +impl OptimizationCallbacks { + fn optimize( + self, + mut justification: GrandpaJustification
, + ) -> GrandpaJustification
{ + for invalid_precommit_idx in self.0.into_iter().rev() { + justification.commit.precommits.remove(invalid_precommit_idx); + } + justification + } +} + +impl VerificationCallbacks for OptimizationCallbacks { + fn on_unkown_authority(&mut self, precommit_idx: usize) -> Result<(), Error> { + self.0.push(precommit_idx); + Ok(()) + } + + fn on_duplicate_authority_vote(&mut self, precommit_idx: usize) -> Result<(), Error> { + self.0.push(precommit_idx); + Ok(()) + } + + fn on_redundant_authority_vote(&mut self, precommit_idx: usize) -> Result<(), Error> { + self.0.push(precommit_idx); + Ok(()) + } +} + +// this implementation will be removed in https://github.com/paritytech/parity-bridges-common/pull/1882 +impl VerificationCallbacks for () { + fn on_unkown_authority(&mut self, _precommit_idx: usize) -> Result<(), Error> { + Ok(()) + } + + fn on_duplicate_authority_vote(&mut self, _precommit_idx: usize) -> Result<(), Error> { + Ok(()) + } + + fn on_redundant_authority_vote(&mut self, _precommit_idx: usize) -> Result<(), Error> { + Ok(()) + } +} + +/// Verify that justification, that is generated by given authority set, finalizes given header. +fn verify_justification_with_callbacks( + finalized_target: (Header::Hash, Header::Number), + authorities_set_id: SetId, + authorities_set: &VoterSet, + justification: &GrandpaJustification
, + callbacks: &mut C, +) -> Result<(), Error> where Header::Number: finality_grandpa::BlockNumberOps, { @@ -95,17 +201,23 @@ where return Err(Error::InvalidJustificationTarget) } + let threshold = authorities_set.threshold().0.into(); let mut chain = AncestryChain::new(&justification.votes_ancestries); let mut signature_buffer = Vec::new(); let mut votes = BTreeSet::new(); let mut cumulative_weight = 0u64; - for signed in &justification.commit.precommits { + for (precommit_idx, signed) in justification.commit.precommits.iter().enumerate() { + // if we have collected enough precommits, we probabably want to fail/remove extra + // precommits + if cumulative_weight > threshold { + callbacks.on_redundant_authority_vote(precommit_idx)?; + } + // authority must be in the set let authority_info = match authorities_set.get(&signed.id) { Some(authority_info) => authority_info, None => { - // just ignore precommit from unknown authority as - // `finality_grandpa::import_precommit` does + callbacks.on_unkown_authority(precommit_idx)?; continue }, }; @@ -116,6 +228,7 @@ where // `finality-grandpa` crate (mostly related to reporting equivocations). But the only thing // that we care about is that only first vote from the authority is accepted if !votes.insert(signed.id.clone()) { + callbacks.on_duplicate_authority_vote(precommit_idx)?; continue } @@ -142,6 +255,7 @@ where thus we'll never overflow the u64::MAX;\ qed", ); + // verify authority signature if !sp_finality_grandpa::check_message_signature_with_buffer( &finality_grandpa::Message::Precommit(signed.precommit.clone()), @@ -162,7 +276,6 @@ where // check that the cumulative weight of validators voted for the justification target (or one // of its descendents) is larger than required threshold. - let threshold = authorities_set.threshold().0.into(); if cumulative_weight >= threshold { Ok(()) } else { diff --git a/primitives/header-chain/src/storage_keys.rs b/primitives/header-chain/src/storage_keys.rs index bb642b1817f..c4dbe53bd9a 100644 --- a/primitives/header-chain/src/storage_keys.rs +++ b/primitives/header-chain/src/storage_keys.rs @@ -20,6 +20,8 @@ pub const PALLET_OPERATING_MODE_VALUE_NAME: &str = "PalletOperatingMode"; /// Name of the `BestFinalized` storage value. pub const BEST_FINALIZED_VALUE_NAME: &str = "BestFinalized"; +/// Name of the `CurrentAuthoritySet` storage value. +pub const CURRENT_AUTHORITY_SET_VALUE_NAME: &str = "CurrentAuthoritySet"; use sp_core::storage::StorageKey; @@ -34,6 +36,17 @@ pub fn pallet_operating_mode_key(pallet_prefix: &str) -> StorageKey { ) } +/// Storage key of the `CurrentAuthoritySet` variable in the runtime storage. +pub fn current_authority_set_key(pallet_prefix: &str) -> StorageKey { + StorageKey( + bp_runtime::storage_value_final_key( + pallet_prefix.as_bytes(), + CURRENT_AUTHORITY_SET_VALUE_NAME.as_bytes(), + ) + .to_vec(), + ) +} + /// Storage key of the best finalized header number and hash value in the runtime storage. pub fn best_finalized_key(pallet_prefix: &str) -> StorageKey { StorageKey( @@ -63,6 +76,19 @@ mod tests { ); } + #[test] + fn current_authority_set_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is breaking + // compatibility with previous pallet. + let storage_key = current_authority_set_key("BridgeGrandpa").0; + assert_eq!( + storage_key, + hex!("0b06f475eddb98cf933a12262e0388de24a7b8b5717ea33346fa595a66ccbcb0").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } + #[test] fn best_finalized_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking diff --git a/primitives/header-chain/tests/justification.rs b/primitives/header-chain/tests/justification.rs index 5b4981a0f69..e18163313c9 100644 --- a/primitives/header-chain/tests/justification.rs +++ b/primitives/header-chain/tests/justification.rs @@ -16,7 +16,7 @@ //! Tests for Grandpa Justification code. -use bp_header_chain::justification::{verify_justification, Error}; +use bp_header_chain::justification::{optimize_justification, verify_justification, Error}; use bp_test_utils::*; type TestHeader = sp_runtime::testing::Header; @@ -190,3 +190,87 @@ fn justification_is_invalid_if_we_dont_meet_threshold() { Err(Error::TooLowCumulativeWeight), ); } + +#[test] +fn optimizer_does_noting_with_minimal_justification() { + let justification = make_default_justification::(&test_header(1)); + + let num_precommits_before = justification.commit.precommits.len(); + let justification = optimize_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + justification, + ) + .unwrap(); + let num_precommits_after = justification.commit.precommits.len(); + + assert_eq!(num_precommits_before, num_precommits_after); +} + +#[test] +fn unknown_authority_votes_are_removed_by_optimizer() { + let mut justification = make_default_justification::(&test_header(1)); + justification.commit.precommits.push(signed_precommit::( + &bp_test_utils::Account(42), + header_id::(1), + justification.round, + TEST_GRANDPA_SET_ID, + )); + + let num_precommits_before = justification.commit.precommits.len(); + let justification = optimize_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + justification, + ) + .unwrap(); + let num_precommits_after = justification.commit.precommits.len(); + + assert_eq!(num_precommits_before - 1, num_precommits_after); +} + +#[test] +fn duplicate_authority_votes_are_removed_by_optimizer() { + let mut justification = make_default_justification::(&test_header(1)); + justification + .commit + .precommits + .push(justification.commit.precommits.first().cloned().unwrap()); + + let num_precommits_before = justification.commit.precommits.len(); + let justification = optimize_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + justification, + ) + .unwrap(); + let num_precommits_after = justification.commit.precommits.len(); + + assert_eq!(num_precommits_before - 1, num_precommits_after); +} + +#[test] +fn redundant_authority_votes_are_removed_by_optimizer() { + let mut justification = make_default_justification::(&test_header(1)); + justification.commit.precommits.push(signed_precommit::( + &EVE, + header_id::(1), + justification.round, + TEST_GRANDPA_SET_ID, + )); + + let num_precommits_before = justification.commit.precommits.len(); + let justification = optimize_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + justification, + ) + .unwrap(); + let num_precommits_after = justification.commit.precommits.len(); + + assert_eq!(num_precommits_before - 1, num_precommits_after); +} diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index 515d6e1a6c4..c8829a508d9 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -24,7 +24,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -trie-db = { version = "0.25.0", default-features = false } +trie-db = { version = "0.25.1", default-features = false } [dev-dependencies] hex-literal = "0.3" diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 75151ccb723..0121b4ab84e 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -507,6 +507,24 @@ impl WeightExtraOps for Weight { } } +/// Trait that provides a static `str`. +pub trait StaticStrProvider { + const STR: &'static str; +} + +#[macro_export] +macro_rules! generate_static_str_provider { + ($str:expr) => { + $crate::paste::item! { + pub struct []; + + impl $crate::StaticStrProvider for [] { + const STR: &'static str = stringify!($str); + } + } + }; +} + #[cfg(test)] mod tests { use super::*; @@ -531,4 +549,10 @@ mod tests { ), ); } + + #[test] + fn generate_static_str_provider_works() { + generate_static_str_provider!(Test); + assert_eq!(StrTest::STR, "Test"); + } } diff --git a/primitives/test-utils/src/lib.rs b/primitives/test-utils/src/lib.rs index c1e95ec6fef..a6bb3d9b8fe 100644 --- a/primitives/test-utils/src/lib.rs +++ b/primitives/test-utils/src/lib.rs @@ -18,7 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use bp_header_chain::justification::GrandpaJustification; +use bp_header_chain::justification::{required_justification_precommits, GrandpaJustification}; use codec::Encode; use sp_finality_grandpa::{AuthorityId, AuthoritySignature, AuthorityWeight, SetId}; use sp_runtime::traits::{Header as HeaderT, One, Zero}; @@ -57,11 +57,12 @@ pub struct JustificationGeneratorParams { impl Default for JustificationGeneratorParams { fn default() -> Self { + let required_signatures = required_justification_precommits(test_keyring().len() as _); Self { header: test_header(One::one()), round: TEST_GRANDPA_ROUND, set_id: TEST_GRANDPA_SET_ID, - authorities: test_keyring(), + authorities: test_keyring().into_iter().take(required_signatures as _).collect(), ancestors: 2, forks: 1, } diff --git a/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs index b48ace58987..9e4b5b15fd6 100644 --- a/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs +++ b/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs @@ -19,7 +19,7 @@ use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge}; use relay_bridge_hub_rococo_client::BridgeHubRococo; use relay_bridge_hub_wococo_client::BridgeHubWococo; -use substrate_relay_helper::messages_lane::SubstrateMessageLane; +use substrate_relay_helper::{messages_lane::SubstrateMessageLane, UtilityPalletBatchCallBuilder}; pub struct BridgeHubRococoToBridgeHubWococoMessagesCliBridge {} @@ -59,6 +59,6 @@ impl SubstrateMessageLane for BridgeHubRococoMessagesToBridgeHubWococoMessageLan type ReceiveMessagesDeliveryProofCallBuilder = BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesDeliveryProofCallBuilder; - type SourceBatchCallBuilder = (); - type TargetBatchCallBuilder = (); + type SourceBatchCallBuilder = UtilityPalletBatchCallBuilder; + type TargetBatchCallBuilder = UtilityPalletBatchCallBuilder; } diff --git a/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs index 3bcf62de333..fb5a81c021e 100644 --- a/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs +++ b/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs @@ -19,7 +19,7 @@ use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge}; use relay_bridge_hub_rococo_client::BridgeHubRococo; use relay_bridge_hub_wococo_client::BridgeHubWococo; -use substrate_relay_helper::messages_lane::SubstrateMessageLane; +use substrate_relay_helper::{messages_lane::SubstrateMessageLane, UtilityPalletBatchCallBuilder}; pub struct BridgeHubWococoToBridgeHubRococoMessagesCliBridge {} @@ -59,6 +59,6 @@ impl SubstrateMessageLane for BridgeHubWococoMessagesToBridgeHubRococoMessageLan type ReceiveMessagesDeliveryProofCallBuilder = BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesDeliveryProofCallBuilder; - type SourceBatchCallBuilder = (); - type TargetBatchCallBuilder = (); + type SourceBatchCallBuilder = UtilityPalletBatchCallBuilder; + type TargetBatchCallBuilder = UtilityPalletBatchCallBuilder; } diff --git a/relays/client-bridge-hub-rococo/src/lib.rs b/relays/client-bridge-hub-rococo/src/lib.rs index 80c075cd0d5..d2e421423a5 100644 --- a/relays/client-bridge-hub-rococo/src/lib.rs +++ b/relays/client-bridge-hub-rococo/src/lib.rs @@ -21,8 +21,9 @@ use bp_bridge_hub_wococo::PolkadotSignedExtension; use bp_messages::MessageNonce; use codec::Encode; use relay_substrate_client::{ - Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, Error as SubstrateError, - SignParam, UnderlyingChainProvider, UnsignedTransaction, + Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, ChainWithUtilityPallet, + Error as SubstrateError, MockedRuntimeUtilityPallet, SignParam, UnderlyingChainProvider, + UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; @@ -57,6 +58,10 @@ impl ChainWithBalances for BridgeHubRococo { } } +impl ChainWithUtilityPallet for BridgeHubRococo { + type UtilityPallet = MockedRuntimeUtilityPallet; +} + impl ChainWithTransactions for BridgeHubRococo { type AccountKeyPair = sp_core::sr25519::Pair; type SignedTransaction = runtime::UncheckedExtrinsic; diff --git a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs index 7f526a35aa9..fc945d8c950 100644 --- a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs +++ b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs @@ -25,7 +25,7 @@ use bp_bridge_hub_rococo::SignedExtension; pub use bp_header_chain::BridgeGrandpaCallOf; pub use bp_parachains::BridgeParachainCall; pub use bridge_runtime_common::messages::BridgeMessagesCallOf; -pub use relay_substrate_client::calls::SystemCall; +pub use relay_substrate_client::calls::{SystemCall, UtilityCall}; /// Unchecked BridgeHubRococo extrinsic. pub type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic; @@ -49,6 +49,9 @@ pub enum Call { #[cfg(test)] #[codec(index = 0)] System(SystemCall), + /// Utility pallet. + #[codec(index = 40)] + Utility(UtilityCall), /// Wococo bridge pallet. #[codec(index = 41)] @@ -60,3 +63,9 @@ pub enum Call { #[codec(index = 46)] BridgeWococoMessages(BridgeWococoMessagesCall), } + +impl From> for Call { + fn from(call: UtilityCall) -> Call { + Call::Utility(call) + } +} diff --git a/relays/client-bridge-hub-wococo/src/lib.rs b/relays/client-bridge-hub-wococo/src/lib.rs index 7af310bfd41..2c211ae86cf 100644 --- a/relays/client-bridge-hub-wococo/src/lib.rs +++ b/relays/client-bridge-hub-wococo/src/lib.rs @@ -20,8 +20,9 @@ use bp_bridge_hub_wococo::{PolkadotSignedExtension, AVERAGE_BLOCK_INTERVAL}; use bp_messages::MessageNonce; use codec::Encode; use relay_substrate_client::{ - Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, Error as SubstrateError, - SignParam, UnderlyingChainProvider, UnsignedTransaction, + Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, ChainWithUtilityPallet, + Error as SubstrateError, MockedRuntimeUtilityPallet, SignParam, UnderlyingChainProvider, + UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; @@ -56,6 +57,10 @@ impl ChainWithBalances for BridgeHubWococo { } } +impl ChainWithUtilityPallet for BridgeHubWococo { + type UtilityPallet = MockedRuntimeUtilityPallet; +} + impl ChainWithTransactions for BridgeHubWococo { type AccountKeyPair = sp_core::sr25519::Pair; type SignedTransaction = runtime::UncheckedExtrinsic; diff --git a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs index 17cc4cbd4e8..c16e7d1a45b 100644 --- a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs +++ b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs @@ -23,7 +23,7 @@ use bp_bridge_hub_wococo::SignedExtension; pub use bp_header_chain::BridgeGrandpaCallOf; pub use bp_parachains::BridgeParachainCall; pub use bridge_runtime_common::messages::BridgeMessagesCallOf; -pub use relay_substrate_client::calls::SystemCall; +pub use relay_substrate_client::calls::{SystemCall, UtilityCall}; /// Unchecked BridgeHubWococo extrinsic. pub type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic; @@ -47,6 +47,9 @@ pub enum Call { #[cfg(test)] #[codec(index = 0)] System(SystemCall), + /// Utility pallet. + #[codec(index = 40)] + Utility(UtilityCall), /// Rococo bridge pallet. #[codec(index = 43)] @@ -59,6 +62,12 @@ pub enum Call { BridgeRococoMessages(BridgeRococoMessagesCall), } +impl From> for Call { + fn from(call: UtilityCall) -> Call { + Call::Utility(call) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/relays/client-millau/src/lib.rs b/relays/client-millau/src/lib.rs index 711b4f32cc6..4c4c1370a6a 100644 --- a/relays/client-millau/src/lib.rs +++ b/relays/client-millau/src/lib.rs @@ -95,7 +95,7 @@ impl ChainWithTransactions for Millau { frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(unsigned.tip), millau_runtime::BridgeRejectObsoleteHeadersAndMessages, - millau_runtime::BridgeRefundRialtoParachainRelayers::default(), + millau_runtime::BridgeRefundRialtoParachainMessages::default(), ), ( (), diff --git a/relays/client-substrate/src/calls.rs b/relays/client-substrate/src/calls.rs index 89fc49a209a..4e0ae9d99d2 100644 --- a/relays/client-substrate/src/calls.rs +++ b/relays/client-substrate/src/calls.rs @@ -31,6 +31,15 @@ pub enum SystemCall { remark(Vec), } +/// A minimized version of `pallet-utility::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum UtilityCall { + /// `pallet-utility::Call::batch_all` + #[codec(index = 2)] + batch_all(Vec), +} + /// A minimized version of `pallet-sudo::Call` that can be used without a runtime. #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] #[allow(non_camel_case_types)] diff --git a/relays/client-substrate/src/chain.rs b/relays/client-substrate/src/chain.rs index b9c5793842e..ebd9e7172d8 100644 --- a/relays/client-substrate/src/chain.rs +++ b/relays/client-substrate/src/chain.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . +use crate::calls::UtilityCall; + use bp_messages::MessageNonce; use bp_runtime::{ Chain as ChainBase, EncodedOrDecodedCall, HashOf, Parachain as ParachainBase, TransactionEra, @@ -276,6 +278,21 @@ where } } +/// Structure that implements `UtilityPalletProvider` based on a call conversion. +pub struct MockedRuntimeUtilityPallet { + _phantom: std::marker::PhantomData, +} + +impl UtilityPallet for MockedRuntimeUtilityPallet +where + C: Chain, + C::Call: From>, +{ + fn build_batch_call(calls: Vec) -> C::Call { + UtilityCall::batch_all(calls).into() + } +} + /// Substrate-based chain that uses `pallet-utility`. pub trait ChainWithUtilityPallet: Chain { /// The utility pallet provider. diff --git a/relays/client-substrate/src/lib.rs b/relays/client-substrate/src/lib.rs index c1a96c487c4..c8d8b6f8129 100644 --- a/relays/client-substrate/src/lib.rs +++ b/relays/client-substrate/src/lib.rs @@ -36,8 +36,8 @@ pub use crate::{ chain::{ AccountKeyPairOf, BlockWithJustification, CallOf, Chain, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, ChainWithTransactions, ChainWithUtilityPallet, - FullRuntimeUtilityPallet, Parachain, RelayChain, SignParam, TransactionStatusOf, - UnsignedTransaction, UtilityPallet, + FullRuntimeUtilityPallet, MockedRuntimeUtilityPallet, Parachain, RelayChain, SignParam, + TransactionStatusOf, UnsignedTransaction, UtilityPallet, }, client::{ is_ancient_block, ChainRuntimeVersion, Client, OpaqueGrandpaAuthoritiesSet, diff --git a/relays/lib-substrate-relay/src/finality/engine.rs b/relays/lib-substrate-relay/src/finality/engine.rs index 4c2da5a5319..f5ac8539a68 100644 --- a/relays/lib-substrate-relay/src/finality/engine.rs +++ b/relays/lib-substrate-relay/src/finality/engine.rs @@ -22,7 +22,7 @@ use bp_header_chain::{ justification::{verify_justification, GrandpaJustification}, ConsensusLogReader, FinalityProof, GrandpaConsensusLogReader, }; -use bp_runtime::{BasicOperatingMode, OperatingMode}; +use bp_runtime::{BasicOperatingMode, HeaderIdProvider, OperatingMode}; use codec::{Decode, Encode}; use finality_grandpa::voter_set::VoterSet; use num_traits::{One, Zero}; @@ -87,6 +87,13 @@ pub trait Engine: Send { client.subscribe_finality_justifications::().await } + /// Optimize finality proof before sending it to the target node. + async fn optimize_proof( + target_client: &Client, + header: &C::Header, + proof: Self::FinalityProof, + ) -> Result; + /// Prepare initialization data for the finality bridge pallet. async fn prepare_initialization_data( client: Client, @@ -139,6 +146,48 @@ impl Engine for Grandpa { bp_header_chain::storage_keys::pallet_operating_mode_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME) } + async fn optimize_proof( + target_client: &Client, + header: &C::Header, + proof: Self::FinalityProof, + ) -> Result { + let current_authority_set_key = bp_header_chain::storage_keys::current_authority_set_key( + C::WITH_CHAIN_GRANDPA_PALLET_NAME, + ); + let (authority_set, authority_set_id): ( + sp_finality_grandpa::AuthorityList, + sp_finality_grandpa::SetId, + ) = target_client + .storage_value(current_authority_set_key, None) + .await? + .map(Ok) + .unwrap_or(Err(SubstrateError::Custom(format!( + "{} `CurrentAuthoritySet` is missing from the {} storage", + C::NAME, + TargetChain::NAME, + ))))?; + let authority_set = + finality_grandpa::voter_set::VoterSet::new(authority_set).expect("TODO"); + // we're risking with race here - we have decided to submit justification some time ago and + // actual authorities set (which we have read now) may have changed, so this + // `optimize_justification` may fail. But if target chain is configured properly, it'll fail + // anyway, after we submit transaction and failing earlier is better. So - it is fine + bp_header_chain::justification::optimize_justification( + (header.hash(), *header.number()), + authority_set_id, + &authority_set, + proof, + ) + .map_err(|e| { + SubstrateError::Custom(format!( + "Failed to optimize {} GRANDPA jutification for header {:?}: {:?}", + C::NAME, + header.id(), + e, + )) + }) + } + /// Prepare initialization data for the GRANDPA verifier pallet. async fn prepare_initialization_data( source_client: Client, diff --git a/relays/lib-substrate-relay/src/finality/target.rs b/relays/lib-substrate-relay/src/finality/target.rs index 9c6ec7c3055..81a22520fa9 100644 --- a/relays/lib-substrate-relay/src/finality/target.rs +++ b/relays/lib-substrate-relay/src/finality/target.rs @@ -111,6 +111,10 @@ where header: SyncHeader>, proof: SubstrateFinalityProof

, ) -> Result { + // runtime module at target chain may require optimized finality proof + let proof = P::FinalityEngine::optimize_proof(&self.client, &header, proof).await?; + + // now we may submit optimized finality proof let transaction_params = self.transaction_params.clone(); let call = P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof); diff --git a/relays/utils/Cargo.toml b/relays/utils/Cargo.toml index 57eeb7fbc8c..1d7422d5a8c 100644 --- a/relays/utils/Cargo.toml +++ b/relays/utils/Cargo.toml @@ -18,7 +18,7 @@ jsonpath_lib = "0.3" log = "0.4.17" num-traits = "0.2" serde_json = "1.0" -sysinfo = "0.27" +sysinfo = "0.28" time = { version = "0.3", features = ["formatting", "local-offset", "std"] } tokio = { version = "1.25", features = ["rt"] } thiserror = "1.0.26" From 1afb13d3da0818381a00bedf89217e202504fadc Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 21 Feb 2023 12:37:19 +0100 Subject: [PATCH 202/263] Squashed 'bridges/' changes from 78e3357c0..5b5627e90 5b5627e90 Rewards refund for relaying BridgeHubRococo/BridgeHubWococo (#1894) git-subtree-dir: bridges git-subtree-split: 5b5627e9081640ed5691eb2891182843563fb99a --- Cargo.lock | 1 + .../chain-bridge-hub-cumulus/Cargo.toml | 2 + .../chain-bridge-hub-cumulus/src/lib.rs | 73 ++++++++++++++++++- primitives/runtime/src/extensions.rs | 9 +++ relays/client-bridge-hub-rococo/src/lib.rs | 13 +++- .../src/runtime_wrapper.rs | 9 ++- relays/client-bridge-hub-wococo/src/lib.rs | 14 +++- .../src/runtime_wrapper.rs | 7 +- 8 files changed, 112 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da7247b1c52..899367d037a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -815,6 +815,7 @@ version = "0.1.0" dependencies = [ "bp-messages", "bp-polkadot-core", + "bp-runtime", "frame-support", "frame-system", "polkadot-primitives", diff --git a/primitives/chain-bridge-hub-cumulus/Cargo.toml b/primitives/chain-bridge-hub-cumulus/Cargo.toml index cb7f55d3361..2bbe3d029a3 100644 --- a/primitives/chain-bridge-hub-cumulus/Cargo.toml +++ b/primitives/chain-bridge-hub-cumulus/Cargo.toml @@ -11,6 +11,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } bp-messages = { path = "../../primitives/messages", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } # Substrate Based Dependencies @@ -27,6 +28,7 @@ default = ["std"] std = [ "bp-polkadot-core/std", "bp-messages/std", + "bp-runtime/std", "frame-system/std", "frame-support/std", "sp-api/std", diff --git a/primitives/chain-bridge-hub-cumulus/src/lib.rs b/primitives/chain-bridge-hub-cumulus/src/lib.rs index dfa5afe2a5b..3a05cb48b3b 100644 --- a/primitives/chain-bridge-hub-cumulus/src/lib.rs +++ b/primitives/chain-bridge-hub-cumulus/src/lib.rs @@ -19,8 +19,8 @@ use bp_messages::*; pub use bp_polkadot_core::{ AccountId, AccountInfoStorageMapKeyProvider, AccountPublic, Balance, BlockNumber, Hash, Hasher, - Hashing, Header, Index, Nonce, Perbill, PolkadotSignedExtension, Signature, SignedBlock, - UncheckedExtrinsic, EXTRA_STORAGE_PROOF_SIZE, TX_EXTRA_BYTES, + Hashing, Header, Index, Nonce, Perbill, Signature, SignedBlock, UncheckedExtrinsic, + EXTRA_STORAGE_PROOF_SIZE, TX_EXTRA_BYTES, }; use frame_support::{ dispatch::DispatchClass, @@ -124,3 +124,72 @@ pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; /// Maximal number of unconfirmed messages at inbound lane for Cumulus-based parachains. pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 4096; + +/// Module with rewarding bridge signed extension support +pub mod rewarding_bridge_signed_extension { + use super::*; + use bp_polkadot_core::PolkadotLike; + use bp_runtime::extensions::*; + + type RewardingBridgeSignedExtra = ( + CheckNonZeroSender, + CheckSpecVersion, + CheckTxVersion, + CheckGenesis, + CheckEra, + CheckNonce, + CheckWeight, + ChargeTransactionPayment, + BridgeRejectObsoleteHeadersAndMessages, + RefundBridgedParachainMessagesSchema, + ); + + /// The signed extension used by Cumulus and Cumulus-like parachain with bridging and rewarding. + pub type RewardingBridgeSignedExtension = GenericSignedExtension; + + pub fn from_params( + spec_version: u32, + transaction_version: u32, + era: bp_runtime::TransactionEraOf, + genesis_hash: Hash, + nonce: Nonce, + tip: Balance, + ) -> RewardingBridgeSignedExtension { + GenericSignedExtension::::new( + ( + (), // non-zero sender + (), // spec version + (), // tx version + (), // genesis + era.frame_era(), // era + nonce.into(), // nonce (compact encoding) + (), // Check weight + tip.into(), // transaction payment / tip (compact encoding) + (), // bridge reject obsolete headers and msgs + (), // bridge register reward to relayer for message passing + ), + Some(( + (), + spec_version, + transaction_version, + genesis_hash, + era.signed_payload(genesis_hash), + (), + (), + (), + (), + (), + )), + ) + } + + /// Return signer nonce, used to craft transaction. + pub fn nonce(sign_ext: &RewardingBridgeSignedExtension) -> Nonce { + sign_ext.payload.5.into() + } + + /// Return transaction tip. + pub fn tip(sign_ext: &RewardingBridgeSignedExtension) -> Balance { + sign_ext.payload.7.into() + } +} diff --git a/primitives/runtime/src/extensions.rs b/primitives/runtime/src/extensions.rs index eefe10f7057..ea51d03741c 100644 --- a/primitives/runtime/src/extensions.rs +++ b/primitives/runtime/src/extensions.rs @@ -76,6 +76,15 @@ pub type ChargeTransactionPayment = GenericSignedExtensionSchema; +/// The `SignedExtensionSchema` for `RefundBridgedParachainMessages`. +/// This schema is dedicated for `RefundBridgedParachainMessages` signed extension as +/// wildcard/placeholder, which relies on the scale encoding for `()` or `((), ())`, or `((), (), +/// ())` is the same. So runtime can contains any kind of tuple: +/// `(BridgeRefundBridgeHubRococoMessages)` +/// `(BridgeRefundBridgeHubRococoMessages, BridgeRefundBridgeHubWococoMessages)` +/// `(BridgeRefundParachainMessages1, ..., BridgeRefundParachainMessagesN)` +pub type RefundBridgedParachainMessagesSchema = GenericSignedExtensionSchema<(), ()>; + #[impl_for_tuples(1, 12)] impl SignedExtensionSchema for Tuple { for_tuples!( type Payload = ( #( Tuple::Payload ),* ); ); diff --git a/relays/client-bridge-hub-rococo/src/lib.rs b/relays/client-bridge-hub-rococo/src/lib.rs index d2e421423a5..b6b844021e0 100644 --- a/relays/client-bridge-hub-rococo/src/lib.rs +++ b/relays/client-bridge-hub-rococo/src/lib.rs @@ -17,7 +17,6 @@ //! Types used to connect to the BridgeHub-Rococo-Substrate parachain. use bp_bridge_hub_rococo::AVERAGE_BLOCK_INTERVAL; -use bp_bridge_hub_wococo::PolkadotSignedExtension; use bp_messages::MessageNonce; use codec::Encode; use relay_substrate_client::{ @@ -72,7 +71,7 @@ impl ChainWithTransactions for BridgeHubRococo { ) -> Result { let raw_payload = SignedPayload::new( unsigned.call, - bp_bridge_hub_rococo::SignedExtension::from_params( + runtime::rewarding_bridge_signed_extension::from_params( param.spec_version, param.transaction_version, unsigned.era, @@ -86,7 +85,7 @@ impl ChainWithTransactions for BridgeHubRococo { let signer: sp_runtime::MultiSigner = param.signer.public().into(); let (call, extra, _) = raw_payload.deconstruct(); - Ok(bp_bridge_hub_rococo::UncheckedExtrinsic::new_signed( + Ok(runtime::UncheckedExtrinsic::new_signed( call, signer.into_account().into(), signature.into(), @@ -109,7 +108,13 @@ impl ChainWithTransactions for BridgeHubRococo { fn parse_transaction(tx: Self::SignedTransaction) -> Option> { let extra = &tx.signature.as_ref()?.2; - Some(UnsignedTransaction::new(tx.function, extra.nonce()).tip(extra.tip())) + Some( + UnsignedTransaction::new( + tx.function, + runtime::rewarding_bridge_signed_extension::nonce(extra), + ) + .tip(runtime::rewarding_bridge_signed_extension::tip(extra)), + ) } } diff --git a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs index fc945d8c950..711306b0127 100644 --- a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs +++ b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs @@ -14,21 +14,22 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -// TODO: join with primitives do we need this here or move to the primitives? - //! Types that are specific to the BridgeHubRococo runtime. use codec::{Decode, Encode}; use scale_info::TypeInfo; -use bp_bridge_hub_rococo::SignedExtension; +pub use bp_bridge_hub_rococo::rewarding_bridge_signed_extension; pub use bp_header_chain::BridgeGrandpaCallOf; pub use bp_parachains::BridgeParachainCall; pub use bridge_runtime_common::messages::BridgeMessagesCallOf; pub use relay_substrate_client::calls::{SystemCall, UtilityCall}; /// Unchecked BridgeHubRococo extrinsic. -pub type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic; +pub type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic< + Call, + rewarding_bridge_signed_extension::RewardingBridgeSignedExtension, +>; // The indirect pallet call used to sync `Wococo` GRANDPA finality to `BHRococo`. pub type BridgeWococoGrandpaCall = BridgeGrandpaCallOf; diff --git a/relays/client-bridge-hub-wococo/src/lib.rs b/relays/client-bridge-hub-wococo/src/lib.rs index 2c211ae86cf..a466df35495 100644 --- a/relays/client-bridge-hub-wococo/src/lib.rs +++ b/relays/client-bridge-hub-wococo/src/lib.rs @@ -16,7 +16,7 @@ //! Types used to connect to the BridgeHub-Wococo-Substrate parachain. -use bp_bridge_hub_wococo::{PolkadotSignedExtension, AVERAGE_BLOCK_INTERVAL}; +use bp_bridge_hub_wococo::AVERAGE_BLOCK_INTERVAL; use bp_messages::MessageNonce; use codec::Encode; use relay_substrate_client::{ @@ -71,7 +71,7 @@ impl ChainWithTransactions for BridgeHubWococo { ) -> Result { let raw_payload = SignedPayload::new( unsigned.call, - bp_bridge_hub_wococo::SignedExtension::from_params( + runtime::rewarding_bridge_signed_extension::from_params( param.spec_version, param.transaction_version, unsigned.era, @@ -85,7 +85,7 @@ impl ChainWithTransactions for BridgeHubWococo { let signer: sp_runtime::MultiSigner = param.signer.public().into(); let (call, extra, _) = raw_payload.deconstruct(); - Ok(bp_bridge_hub_wococo::UncheckedExtrinsic::new_signed( + Ok(runtime::UncheckedExtrinsic::new_signed( call, signer.into_account().into(), signature.into(), @@ -108,7 +108,13 @@ impl ChainWithTransactions for BridgeHubWococo { fn parse_transaction(tx: Self::SignedTransaction) -> Option> { let extra = &tx.signature.as_ref()?.2; - Some(UnsignedTransaction::new(tx.function, extra.nonce()).tip(extra.tip())) + Some( + UnsignedTransaction::new( + tx.function, + runtime::rewarding_bridge_signed_extension::nonce(extra), + ) + .tip(runtime::rewarding_bridge_signed_extension::tip(extra)), + ) } } diff --git a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs index c16e7d1a45b..13fb1a7e6ab 100644 --- a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs +++ b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs @@ -19,14 +19,17 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; -use bp_bridge_hub_wococo::SignedExtension; +pub use bp_bridge_hub_wococo::rewarding_bridge_signed_extension; pub use bp_header_chain::BridgeGrandpaCallOf; pub use bp_parachains::BridgeParachainCall; pub use bridge_runtime_common::messages::BridgeMessagesCallOf; pub use relay_substrate_client::calls::{SystemCall, UtilityCall}; /// Unchecked BridgeHubWococo extrinsic. -pub type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic; +pub type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic< + Call, + rewarding_bridge_signed_extension::RewardingBridgeSignedExtension, +>; // The indirect pallet call used to sync `Rococo` GRANDPA finality to `BHWococo`. pub type BridgeRococoGrandpaCall = BridgeGrandpaCallOf; From 22ad7826ae902a48275521a866584af17aaaf8cd Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 22 Feb 2023 16:10:26 +0100 Subject: [PATCH 203/263] Rewards for relayers setup (#2194) * Rewards for relayers setup * ".git/.scripts/commands/bench/bench.sh" pallet bridge-hub-rococo bridge-hubs pallet_bridge_relayers * Setup weight for relayer * Setup `DeliveryConfirmationPayments` + `RefundRelayerForMessagesFromParachain` * No need to have more than one collator per parachain * Setup multi refund signed extensions * Rewards sign ext test * test * fixes --------- Co-authored-by: command-bot <> --- Cargo.lock | 2 + Cargo.toml | 1 + docs/release.md | 2 +- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 2 + .../src/bridge_hub_rococo_config.rs | 24 +++- .../src/bridge_hub_wococo_config.rs | 24 +++- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 124 +++++++++++++----- .../bridge-hub-rococo/src/weights/mod.rs | 1 + .../src/weights/pallet_bridge_relayers.rs | 65 +++++++++ .../bridge-hub-rococo/tests/tests.rs | 2 + .../bridge-hubs/test-utils/Cargo.toml | 2 + .../bridge-hubs/test-utils/src/lib.rs | 2 + scripts/benchmarks-ci.sh | 1 + 13 files changed, 215 insertions(+), 37 deletions(-) create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs diff --git a/Cargo.lock b/Cargo.lock index 74fb36634eb..60634180d8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1013,6 +1013,7 @@ dependencies = [ "bp-messages", "bp-parachains", "bp-polkadot-core", + "bp-relayers", "bp-rococo", "bp-runtime", "bp-wococo", @@ -1089,6 +1090,7 @@ dependencies = [ "bp-messages", "bp-polkadot-core", "bp-runtime", + "bp-test-utils", "cumulus-pallet-parachain-system", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", diff --git a/Cargo.toml b/Cargo.toml index ed6e6478e90..87050f03b7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "bridges/modules/parachains", "bridges/modules/relayers", "bridges/primitives/messages", + "bridges/primitives/relayers", "bridges/primitives/runtime", "bridges/primitives/chain-bridge-hub-rococo", "bridges/primitives/chain-bridge-hub-wococo", diff --git a/docs/release.md b/docs/release.md index 2b7c79c50be..9c0a6acc0e9 100644 --- a/docs/release.md +++ b/docs/release.md @@ -123,7 +123,7 @@ for `release-parachains-v0.9.270` 5. Clone `it/release--fast-sudo` from Polkadot In case the branch does not exists (it is a manual process): cherry pick paritytech/polkadot@791c8b8 and run `find . -type f -name "*.toml" -print0 | xargs -0 sed -i '' -e 's/polkadot-vX.X.X/polkadot-v/g'` -6. `cargo build --release features --fast-runtime` +6. `cargo build --release --features fast-runtime` 7. Copy `./target/polkadot` into `./bin` (in Cumulus) 8. Run the tests: - Statemint: `yarn zombienet-test -c ./examples/statemint/config.toml -t ./examples/statemint` diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 45e782643ba..7e32cdcf585 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -77,6 +77,7 @@ bp-bridge-hub-wococo = { path = "../../../../bridges/primitives/chain-bridge-hub bp-messages = { path = "../../../../bridges/primitives/messages", default-features = false } bp-parachains = { path = "../../../../bridges/primitives/parachains", default-features = false } bp-polkadot-core = { path = "../../../../bridges/primitives/polkadot-core", default-features = false } +bp-relayers = { path = "../../../../bridges/primitives/relayers", default-features = false } bp-runtime = { path = "../../../../bridges/primitives/runtime", default-features = false } bp-rococo = { path = "../../../../bridges/primitives/chain-rococo", default-features = false } bp-wococo = { path = "../../../../bridges/primitives/chain-wococo", default-features = false } @@ -101,6 +102,7 @@ std = [ "bp-messages/std", "bp-parachains/std", "bp-polkadot-core/std", + "bp-relayers/std", "bp-runtime/std", "bp-rococo/std", "bp-wococo/std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index 41efbcb031e..9df6d5a782d 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -28,6 +28,10 @@ use bridge_runtime_common::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, MessageBridge, ThisChainWithMessages, UnderlyingChainProvider, }, + refund_relayer_extension::{ + ActualFeeRefund, RefundBridgedParachainMessages, RefundableMessagesLane, + RefundableParachain, + }, }; use frame_support::{parameter_types, RuntimeDebug}; use xcm::{ @@ -87,7 +91,7 @@ const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO: LaneId = LaneId([0, 0, 0, 1]); pub struct WithBridgeHubWococoMessageBridge; impl MessageBridge for WithBridgeHubWococoMessageBridge { const THIS_CHAIN_ID: ChainId = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; - const BRIDGED_CHAIN_ID: ChainId = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; + const BRIDGED_CHAIN_ID: ChainId = BridgeHubWococoChainId::get(); const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME; type ThisChain = BridgeHubRococo; @@ -147,6 +151,24 @@ impl ThisChainWithMessages for BridgeHubRococo { } } +/// Signed extension that refunds relayers that are delivering messages from the Wococo parachain. +pub type BridgeRefundBridgeHubWococoMessages = RefundBridgedParachainMessages< + Runtime, + RefundableParachain, + RefundableMessagesLane, + ActualFeeRefund, + StrBridgeRefundBridgeHubWococoMessages, +>; +bp_runtime::generate_static_str_provider!(BridgeRefundBridgeHubWococoMessages); + +parameter_types! { + pub const BridgeHubWococoMessagesLane: bp_messages::LaneId = DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO; + pub const BridgeHubWococoParachainId: u32 = { + use bp_runtime::Parachain; + BridgeHubWococo::PARACHAIN_ID + }; +} + #[cfg(test)] mod tests { use super::*; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index cc18d510a43..3c574c9040f 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -28,6 +28,10 @@ use bridge_runtime_common::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, MessageBridge, ThisChainWithMessages, UnderlyingChainProvider, }, + refund_relayer_extension::{ + ActualFeeRefund, RefundBridgedParachainMessages, RefundableMessagesLane, + RefundableParachain, + }, }; use frame_support::{parameter_types, RuntimeDebug}; use xcm::{ @@ -87,7 +91,7 @@ const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO: LaneId = LaneId([0, 0, 0, 1]); pub struct WithBridgeHubRococoMessageBridge; impl MessageBridge for WithBridgeHubRococoMessageBridge { const THIS_CHAIN_ID: ChainId = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; - const BRIDGED_CHAIN_ID: ChainId = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; + const BRIDGED_CHAIN_ID: ChainId = BridgeHubRococoChainId::get(); const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_bridge_hub_wococo::WITH_BRIDGE_HUB_WOCOCO_MESSAGES_PALLET_NAME; type ThisChain = BridgeHubWococo; @@ -147,6 +151,24 @@ impl ThisChainWithMessages for BridgeHubWococo { } } +/// Signed extension that refunds relayers that are delivering messages from the Rococo parachain. +pub type BridgeRefundBridgeHubRococoMessages = RefundBridgedParachainMessages< + Runtime, + RefundableParachain, + RefundableMessagesLane, + ActualFeeRefund, + StrBridgeRefundBridgeHubRococoMessages, +>; +bp_runtime::generate_static_str_provider!(BridgeRefundBridgeHubRococoMessages); + +parameter_types! { + pub const BridgeHubRococoMessagesLane: bp_messages::LaneId = DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO; + pub const BridgeHubRococoParachainId: u32 = { + use bp_runtime::Parachain; + BridgeHubRococo::PARACHAIN_ID + }; +} + #[cfg(test)] mod tests { use super::*; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 8f8dadfd1d4..c768477c8bb 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -73,8 +73,14 @@ use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use crate::{ - bridge_hub_rococo_config::{OnBridgeHubRococoBlobDispatcher, WithBridgeHubWococoMessageBridge}, - bridge_hub_wococo_config::{OnBridgeHubWococoBlobDispatcher, WithBridgeHubRococoMessageBridge}, + bridge_hub_rococo_config::{ + BridgeRefundBridgeHubWococoMessages, OnBridgeHubRococoBlobDispatcher, + WithBridgeHubWococoMessageBridge, + }, + bridge_hub_wococo_config::{ + BridgeRefundBridgeHubRococoMessages, OnBridgeHubWococoBlobDispatcher, + WithBridgeHubRococoMessageBridge, + }, constants::fee::WeightToFee, xcm_config::XcmRouter, }; @@ -113,6 +119,7 @@ pub type SignedExtra = ( frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, BridgeRejectObsoleteHeadersAndMessages, + (BridgeRefundBridgeHubRococoMessages, BridgeRefundBridgeHubWococoMessages), ); /// Unchecked extrinsic type as expected by this runtime. @@ -398,7 +405,7 @@ impl pallet_bridge_grandpa::Config for Runtime { type WeightInfo = weights::pallet_bridge_grandpa_bridge_wococo_grandpa::WeightInfo; } -/// Add granda bridge pallet to track Rococo relay chain +/// Add granda bridge pallet to track Rococo relay chain on Wococo BridgeHub pub type BridgeGrandpaRococoInstance = pallet_bridge_grandpa::Instance2; impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_rococo::Rococo; @@ -407,17 +414,19 @@ impl pallet_bridge_grandpa::Config for Runtime { type WeightInfo = weights::pallet_bridge_grandpa_bridge_rococo_grandpa::WeightInfo; } -pub const ROCOCO_BRIDGE_PARA_PALLET_NAME: &str = "Paras"; -pub const WOCOCO_BRIDGE_PARA_PALLET_NAME: &str = "Paras"; parameter_types! { pub const RelayChainHeadersToKeep: u32 = 1024; pub const ParachainHeadsToKeep: u32 = 64; pub const MaxRequests: u32 = 64; - pub const RococoBridgeParachainPalletName: &'static str = ROCOCO_BRIDGE_PARA_PALLET_NAME; - pub const WococoBridgeParachainPalletName: &'static str = WOCOCO_BRIDGE_PARA_PALLET_NAME; + pub const RococoBridgeParachainPalletName: &'static str = "Paras"; + pub const WococoBridgeParachainPalletName: &'static str = "Paras"; pub const MaxRococoParaHeadDataSize: u32 = bp_rococo::MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE; pub const MaxWococoParaHeadDataSize: u32 = bp_wococo::MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE; + + // TODO:check-parameter - setup initial values https://github.com/paritytech/parity-bridges-common/issues/1677 + pub storage DeliveryRewardInBalance: u64 = 1_000_000; + pub storage ConfirmationRewardInBalance: u64 = 100_000; } /// Add parachain bridge pallet to track Wococo bridge hub parachain @@ -464,13 +473,15 @@ impl pallet_bridge_messages::Config for Run type InboundPayload = XcmAsPlainPayload; type InboundRelayer = AccountId; - // TODO:check-parameter - check delivery type DeliveryPayments = (); type TargetHeaderChain = TargetHeaderChainAdapter; type LaneMessageVerifier = bridge_hub_rococo_config::ToBridgeHubWococoMessageVerifier; - // TODO:check-parameter - check delivery - type DeliveryConfirmationPayments = (); + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + Runtime, + DeliveryRewardInBalance, + ConfirmationRewardInBalance, + >; type SourceHeaderChain = SourceHeaderChainAdapter; type MessageDispatch = XcmBlobMessageDispatch< @@ -498,13 +509,15 @@ impl pallet_bridge_messages::Config for Run type InboundPayload = XcmAsPlainPayload; type InboundRelayer = AccountId; - // TODO:check-parameter - check delivery type DeliveryPayments = (); type TargetHeaderChain = TargetHeaderChainAdapter; type LaneMessageVerifier = bridge_hub_wococo_config::ToBridgeHubRococoMessageVerifier; - // TODO:check-parameter - check delivery - type DeliveryConfirmationPayments = (); + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + Runtime, + DeliveryRewardInBalance, + ConfirmationRewardInBalance, + >; type SourceHeaderChain = SourceHeaderChainAdapter; type MessageDispatch = XcmBlobMessageDispatch< @@ -514,6 +527,15 @@ impl pallet_bridge_messages::Config for Run >; } +/// Allows collect and claim rewards for relayers +impl pallet_bridge_relayers::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Reward = Balance; + type PaymentProcedure = + bp_relayers::PayLaneRewardFromAccount, AccountId>; + type WeightInfo = weights::pallet_bridge_relayers::WeightInfo; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime where @@ -564,6 +586,8 @@ construct_runtime!( BridgeRococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Config} = 43, BridgeRococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage, Event} = 44, BridgeRococoMessages: pallet_bridge_messages::::{Pallet, Call, Storage, Event, Config} = 45, + + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event} = 47, } ); @@ -606,6 +630,8 @@ mod benches { [pallet_bridge_grandpa, BridgeRococoGrandpa] [pallet_bridge_parachains, BridgeParachainsBench::] [pallet_bridge_messages, BridgeMessagesBench::] + // Bridge relayer pallets + [pallet_bridge_relayers, BridgeRelayersBench::] ); } @@ -841,6 +867,7 @@ impl_runtime_apis! { use pallet_bridge_parachains::benchmarking::Pallet as BridgeParachainsBench; use pallet_bridge_messages::benchmarking::Pallet as BridgeMessagesBench; + use pallet_bridge_relayers::benchmarking::Pallet as BridgeRelayersBench; let mut list = Vec::::new(); list_benchmarks!(list, extra); @@ -950,9 +977,9 @@ impl_runtime_apis! { }; impl BridgeMessagesConfig for Runtime { - fn is_relayer_rewarded(_: &Self::AccountId) -> bool { - // TODO: implement me properly - true + fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { + let bench_lane_id = >::bench_lane_id(); + pallet_bridge_relayers::Pallet::::relayer_reward(relayer, &bench_lane_id).is_some() } fn prepare_message_proof( @@ -977,9 +1004,9 @@ impl_runtime_apis! { } impl BridgeMessagesConfig for Runtime { - fn is_relayer_rewarded(_: &Self::AccountId) -> bool { - // TODO: implement me properly - true + fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { + let bench_lane_id = >::bench_lane_id(); + pallet_bridge_relayers::Pallet::::relayer_reward(relayer, &bench_lane_id).is_some() } fn prepare_message_proof( @@ -1008,6 +1035,10 @@ impl_runtime_apis! { Config as BridgeParachainsConfig, Pallet as BridgeParachainsBench, }; + use pallet_bridge_relayers::benchmarking::{ + Pallet as BridgeRelayersBench, + Config as BridgeRelayersConfig, + }; impl BridgeParachainsConfig for Runtime { fn parachains() -> Vec { @@ -1057,6 +1088,20 @@ impl_runtime_apis! { } } + impl BridgeRelayersConfig for Runtime { + fn prepare_environment( + lane: bp_messages::LaneId, + reward: Balance, + ) { + use frame_support::traits::fungible::Mutate; + let lane_rewards_account = bp_relayers::PayLaneRewardFromAccount::< + Balances, + AccountId + >::lane_rewards_account(lane); + Balances::mint_into(&lane_rewards_account, reward).unwrap(); + } + } + let whitelist: Vec = vec![ // Block Number hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(), @@ -1111,37 +1156,48 @@ cumulus_pallet_parachain_system::register_validate_block! { #[cfg(test)] mod tests { use super::*; - use bridge_runtime_common::integrity::check_additional_signed; + use bp_runtime::TransactionEra; + use bridge_hub_test_utils::test_header; use codec::Encode; - use sp_runtime::generic::Era; #[test] - fn ensure_signed_extension_definition_is_correct() { + fn ensure_signed_extension_definition_is_compatible_with_relay() { let payload: SignedExtra = ( frame_system::CheckNonZeroSender::new(), frame_system::CheckSpecVersion::new(), frame_system::CheckTxVersion::new(), frame_system::CheckGenesis::new(), - frame_system::CheckEra::from(Era::Immortal), + frame_system::CheckEra::from(sp_runtime::generic::Era::Immortal), frame_system::CheckNonce::from(10), frame_system::CheckWeight::new(), pallet_transaction_payment::ChargeTransactionPayment::from(10), BridgeRejectObsoleteHeadersAndMessages {}, + ( + BridgeRefundBridgeHubRococoMessages::default(), + BridgeRefundBridgeHubWococoMessages::default(), + ), ); - let bhr_indirect_payload = bp_bridge_hub_rococo::SignedExtension::new( - ((), (), (), (), Era::Immortal, 10.into(), (), 10.into(), ()), - None, - ); + let bhr_indirect_payload = + bp_bridge_hub_rococo::rewarding_bridge_signed_extension::from_params( + 10, + 10, + TransactionEra::Immortal, + test_header::(1).hash(), + 10, + 10, + ); assert_eq!(payload.encode(), bhr_indirect_payload.encode()); - let bhw_indirect_payload = bp_bridge_hub_wococo::SignedExtension::new( - ((), (), (), (), Era::Immortal, 10.into(), (), 10.into(), ()), - None, - ); + let bhw_indirect_payload = + bp_bridge_hub_wococo::rewarding_bridge_signed_extension::from_params( + 10, + 10, + TransactionEra::Immortal, + test_header::(1).hash(), + 10, + 10, + ); assert_eq!(payload.encode(), bhw_indirect_payload.encode()); - - check_additional_signed::(); - check_additional_signed::(); } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs index 1eec4e5c18a..59f3932ad2b 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs @@ -28,6 +28,7 @@ pub mod pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_roc pub mod pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance; pub mod pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance; pub mod pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance; +pub mod pallet_bridge_relayers; pub mod pallet_collator_selection; pub mod pallet_multisig; pub mod pallet_session; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs new file mode 100644 index 00000000000..c81dfbc14c8 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs @@ -0,0 +1,65 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Autogenerated weights for `pallet_bridge_relayers` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-02-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-osnnfcqu-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/cumulus/.git/.artifacts/bench.json +// --pallet=pallet_bridge_relayers +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +/// Weight functions for `pallet_bridge_relayers`. +pub struct WeightInfo(PhantomData); +impl pallet_bridge_relayers::WeightInfo for WeightInfo { + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn claim_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `271` + // Estimated: `5146` + // Minimum execution time: 37_598 nanoseconds. + Weight::from_ref_time(38_726_000) + .saturating_add(Weight::from_proof_size(5146)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index 8885641a57e..da3e6634434 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -265,3 +265,5 @@ fn can_govornance_call_xcm_transact_with_initialize_bridge_on_bridge_hub_wococo( ); }) } + +// TODO:check-parameter - add test for DeliveryConfirmationPayments when receive_messages_delivery_proof diff --git a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml index fb2126d6192..123bbfb501a 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -33,6 +33,7 @@ bp-header-chain = { path = "../../../../bridges/primitives/header-chain", defaul bp-messages = { path = "../../../../bridges/primitives/messages", default-features = false } bp-polkadot-core = { path = "../../../../bridges/primitives/polkadot-core", default-features = false } bp-runtime = { path = "../../../../bridges/primitives/runtime", default-features = false } +bp-test-utils = { path = "../../../../bridges/primitives/test-utils", default-features = false } [features] default = [ "std" ] @@ -43,6 +44,7 @@ std = [ "bp-polkadot-core/std", "bp-header-chain/std", "bp-runtime/std", + "bp-test-utils/std", "parachains-common/std", "parachain-info/std", "cumulus-primitives-core/std", diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs index a95b54ea62e..0605677c4df 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs @@ -33,6 +33,8 @@ use xcm::{latest::prelude::*, prelude::XcmVersion}; use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; use xcm_executor::traits::{validate_export, ExportXcm}; +pub use bp_test_utils::test_header; + /// Dummy xcm pub fn dummy_xcm() -> Xcm<()> { vec![Trap(42)].into() diff --git a/scripts/benchmarks-ci.sh b/scripts/benchmarks-ci.sh index bf08522c7c1..f283c678345 100755 --- a/scripts/benchmarks-ci.sh +++ b/scripts/benchmarks-ci.sh @@ -67,6 +67,7 @@ elif [[ $runtimeName == "bridge-hub-rococo" ]]; then pallet_bridge_grandpa pallet_bridge_parachains pallet_bridge_messages + pallet_bridge_relayers ) else echo "$runtimeName pallet list not found in benchmarks-ci.sh" From 0fda54d9325708dc3d9d5e254721483fdf2b5638 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 22 Feb 2023 18:08:38 +0100 Subject: [PATCH 204/263] Squashed 'bridges/' changes from 5b5627e90..3c15c3645 3c15c3645 get rid of ChainWithMessages::WeightInfo, because we can't have exact weights for "external chains" (#1899) 8ccaa0213 Wrap confirmation and finality transactions into batch_all in Millau -> RialtoParachain bridge (#1898) 9b7285edb Weight+size limits for bridge GRANDPA pallet calls (#1882) git-subtree-dir: bridges git-subtree-split: 3c15c36455f2ad944df6a492a8d82f7e0aaf7e9f --- .../src/refund_relayer_extension.rs | 98 +++++++++- modules/grandpa/src/call_ext.rs | 172 ++++++++++++++++-- modules/grandpa/src/lib.rs | 79 +++++++- modules/grandpa/src/storage_types.rs | 10 +- modules/messages/src/lib.rs | 1 + modules/parachains/src/call_ext.rs | 5 +- modules/parachains/src/mock.rs | 4 +- primitives/header-chain/src/justification.rs | 88 +++++++-- .../tests/implementation_match.rs | 134 +++++++++++--- .../header-chain/tests/justification.rs | 13 +- primitives/polkadot-core/src/lib.rs | 29 +-- .../millau_messages_to_rialto_parachain.rs | 7 +- relays/client-bridge-hub-rococo/src/lib.rs | 3 - relays/client-bridge-hub-wococo/src/lib.rs | 3 - relays/client-millau/src/lib.rs | 1 - relays/client-rialto-parachain/src/lib.rs | 1 - relays/client-rialto/src/lib.rs | 1 - relays/client-substrate/src/chain.rs | 3 - relays/client-substrate/src/client.rs | 50 +++-- .../lib-substrate-relay/src/messages_lane.rs | 147 ++++++++++----- relays/messages/src/message_race_loop.rs | 13 +- 21 files changed, 670 insertions(+), 192 deletions(-) diff --git a/bin/runtime-common/src/refund_relayer_extension.rs b/bin/runtime-common/src/refund_relayer_extension.rs index 9efeedf24dd..df4eae6f737 100644 --- a/bin/runtime-common/src/refund_relayer_extension.rs +++ b/bin/runtime-common/src/refund_relayer_extension.rs @@ -28,9 +28,12 @@ use codec::{Decode, Encode}; use frame_support::{ dispatch::{CallableCallFor, DispatchInfo, Dispatchable, PostDispatchInfo}, traits::IsSubType, + weights::Weight, CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; -use pallet_bridge_grandpa::{CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper}; +use pallet_bridge_grandpa::{ + CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper, SubmitFinalityProofInfo, +}; use pallet_bridge_messages::Config as MessagesConfig; use pallet_bridge_parachains::{ BoundedBridgeGrandpaConfig, CallSubType as ParachainsCallSubType, Config as ParachainsConfig, @@ -140,7 +143,11 @@ pub struct PreDispatchData { #[derive(RuntimeDebugNoBound, PartialEq)] pub enum CallInfo { /// Relay chain finality + parachain finality + message delivery calls. - AllFinalityAndDelivery(RelayBlockNumber, SubmitParachainHeadsInfo, ReceiveMessagesProofInfo), + AllFinalityAndDelivery( + SubmitFinalityProofInfo, + SubmitParachainHeadsInfo, + ReceiveMessagesProofInfo, + ), /// Parachain finality + message delivery calls. ParachainFinalityAndDelivery(SubmitParachainHeadsInfo, ReceiveMessagesProofInfo), /// Standalone message delivery call. @@ -149,7 +156,7 @@ pub enum CallInfo { impl CallInfo { /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. - fn submit_finality_proof_info(&self) -> Option { + fn submit_finality_proof_info(&self) -> Option> { match *self { Self::AllFinalityAndDelivery(info, _, _) => Some(info), _ => None, @@ -318,6 +325,9 @@ where len: usize, result: &DispatchResult, ) -> Result<(), TransactionValidityError> { + let mut extra_weight = Weight::zero(); + let mut extra_size = 0; + // We don't refund anything if the transaction has failed. if result.is_err() { return Ok(()) @@ -330,8 +340,10 @@ where }; // check if relay chain state has been updated - if let Some(relay_block_number) = call_info.submit_finality_proof_info() { - if !SubmitFinalityProofHelper::::was_successful(relay_block_number) { + if let Some(finality_proof_info) = call_info.submit_finality_proof_info() { + if !SubmitFinalityProofHelper::::was_successful( + finality_proof_info.block_number, + ) { // we only refund relayer if all calls have updated chain state return Ok(()) } @@ -342,6 +354,11 @@ where // `utility.batchAll` transaction always requires payment. But in both cases we'll // refund relayer - either explicitly here, or using `Pays::No` if he's choosing // to submit dedicated transaction. + + // submitter has means to include extra weight/bytes in the `submit_finality_proof` + // call, so let's subtract extra weight/size to avoid refunding for this extra stuff + extra_weight = finality_proof_info.extra_weight; + extra_size = finality_proof_info.extra_size; } // check if parachain state has been updated @@ -370,8 +387,15 @@ where // cost of this attack is nothing. Hence we use zero as tip here. let tip = Zero::zero(); + // decrease post-dispatch weight/size using extra weight/size that we know now + let post_info_len = len.saturating_sub(extra_size as usize); + let mut post_info = *post_info; + post_info.actual_weight = + Some(post_info.actual_weight.unwrap_or(info.weight).saturating_sub(extra_weight)); + // compute the relayer refund - let refund = Refund::compute_refund(info, post_info, len, tip); + let refund = Refund::compute_refund(info, &post_info, post_info_len, tip); + // finally - register refund in relayers pallet RelayersPallet::::register_relayer_reward(Msgs::Id::get(), &relayer, refund); @@ -397,9 +421,9 @@ mod tests { use bp_parachains::{BestParaHeadHash, ParaInfo}; use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; use bp_runtime::HeaderId; - use bp_test_utils::make_default_justification; + use bp_test_utils::{make_default_justification, test_keyring}; use frame_support::{assert_storage_noop, parameter_types, weights::Weight}; - use pallet_bridge_grandpa::Call as GrandpaCall; + use pallet_bridge_grandpa::{Call as GrandpaCall, StoredAuthoritySet}; use pallet_bridge_messages::Call as MessagesCall; use pallet_bridge_parachains::{Call as ParachainsCall, RelayBlockHash}; use sp_runtime::{ @@ -434,7 +458,11 @@ mod tests { parachain_head_hash: ParaHash, best_delivered_message: MessageNonce, ) { + let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect(); let best_relay_header = HeaderId(best_relay_header_number, RelayBlockHash::default()); + pallet_bridge_grandpa::CurrentAuthoritySet::::put( + StoredAuthoritySet::try_new(authorities, 0).unwrap(), + ); pallet_bridge_grandpa::BestFinalized::::put(best_relay_header); let para_id = ParaId(TestParachain::get()); @@ -524,7 +552,11 @@ mod tests { PreDispatchData { relayer: relayer_account_at_this_chain(), call_info: CallInfo::AllFinalityAndDelivery( - 200, + SubmitFinalityProofInfo { + block_number: 200, + extra_weight: Weight::zero(), + extra_size: 0, + }, SubmitParachainHeadsInfo { at_relay_block_number: 200, para_id: ParaId(TestParachain::get()), @@ -823,6 +855,54 @@ mod tests { }); } + #[test] + fn post_dispatch_refunds_relayer_in_all_finality_batch_with_extra_weight() { + run_test(|| { + initialize_environment(200, 200, [1u8; 32].into(), 200); + + let mut dispatch_info = dispatch_info(); + dispatch_info.weight = Weight::from_ref_time( + frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND * 2, + ); + + // without any size/weight refund: we expect regular reward + let pre_dispatch_data = all_finality_pre_dispatch_data(); + let regular_reward = expected_reward(); + run_post_dispatch(Some(pre_dispatch_data), Ok(())); + assert_eq!( + RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + TestLaneId::get() + ), + Some(regular_reward), + ); + + // now repeat the same with size+weight refund: we expect smaller reward + let mut pre_dispatch_data = all_finality_pre_dispatch_data(); + match pre_dispatch_data.call_info { + CallInfo::AllFinalityAndDelivery(ref mut info, ..) => { + info.extra_weight.set_ref_time( + frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, + ); + info.extra_size = 32; + }, + _ => unreachable!(), + } + run_post_dispatch(Some(pre_dispatch_data), Ok(())); + let reward_after_two_calls = RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + TestLaneId::get(), + ) + .unwrap(); + assert!( + reward_after_two_calls < 2 * regular_reward, + "{} must be < 2 * {}", + reward_after_two_calls, + 2 * regular_reward, + ); + }); + } + #[test] fn post_dispatch_refunds_relayer_in_all_finality_batch() { run_test(|| { diff --git a/modules/grandpa/src/call_ext.rs b/modules/grandpa/src/call_ext.rs index 42c276f5f6c..b57aebb1ac1 100644 --- a/modules/grandpa/src/call_ext.rs +++ b/modules/grandpa/src/call_ext.rs @@ -14,17 +14,46 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use crate::{Config, Error, Pallet}; +use crate::{weights::WeightInfo, BridgedBlockNumber, BridgedHeader, Config, Error, Pallet}; +use bp_header_chain::{justification::GrandpaJustification, ChainWithGrandpa}; use bp_runtime::BlockNumberOf; -use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; +use codec::Encode; +use frame_support::{dispatch::CallableCallFor, traits::IsSubType, weights::Weight, RuntimeDebug}; use sp_runtime::{ - traits::Header, + traits::{Header, Zero}, transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, + SaturatedConversion, }; +/// Info about a `SubmitParachainHeads` call which tries to update a single parachain. +#[derive(Copy, Clone, PartialEq, RuntimeDebug)] +pub struct SubmitFinalityProofInfo { + /// Number of the finality target. + pub block_number: N, + /// Extra weight that we assume is included in the call. + /// + /// We have some assumptions about headers and justifications of the bridged chain. + /// We know that if our assumptions are correct, then the call must not have the + /// weight above some limit. The fee paid for weight above that limit, is never refunded. + pub extra_weight: Weight, + /// Extra size (in bytes) that we assume are included in the call. + /// + /// We have some assumptions about headers and justifications of the bridged chain. + /// We know that if our assumptions are correct, then the call must not have the + /// weight above some limit. The fee paid for bytes above that limit, is never refunded. + pub extra_size: u32, +} + +impl SubmitFinalityProofInfo { + /// Returns `true` if call size/weight is below our estimations for regular calls. + pub fn fits_limits(&self) -> bool { + self.extra_weight.is_zero() && self.extra_size.is_zero() + } +} + /// Helper struct that provides methods for working with the `SubmitFinalityProof` call. pub struct SubmitFinalityProofHelper, I: 'static> { - pub _phantom_data: sp_std::marker::PhantomData<(T, I)>, + _phantom_data: sp_std::marker::PhantomData<(T, I)>, } impl, I: 'static> SubmitFinalityProofHelper { @@ -69,12 +98,17 @@ impl, I: 'static> SubmitFinalityProofHelper { pub trait CallSubType, I: 'static>: IsSubType, T>> { - /// Extract the finality target from a `SubmitParachainHeads` call. - fn submit_finality_proof_info(&self) -> Option> { - if let Some(crate::Call::::submit_finality_proof { finality_target, .. }) = + /// Extract finality proof info from a runtime call. + fn submit_finality_proof_info( + &self, + ) -> Option>> { + if let Some(crate::Call::::submit_finality_proof { finality_target, justification }) = self.is_sub_type() { - return Some(*finality_target.number()) + return Some(submit_finality_proof_info_from_args::( + finality_target, + justification, + )) } None @@ -92,7 +126,7 @@ pub trait CallSubType, I: 'static>: _ => return Ok(ValidTransaction::default()), }; - match SubmitFinalityProofHelper::::check_obsolete(finality_target) { + match SubmitFinalityProofHelper::::check_obsolete(finality_target.block_number) { Ok(_) => Ok(ValidTransaction::default()), Err(Error::::OldHeader) => InvalidTransaction::Stale.into(), Err(_) => InvalidTransaction::Call.into(), @@ -105,15 +139,66 @@ impl, I: 'static> CallSubType for T::RuntimeCall where { } +/// Extract finality proof info from the submitted header and justification. +pub(crate) fn submit_finality_proof_info_from_args, I: 'static>( + finality_target: &BridgedHeader, + justification: &GrandpaJustification>, +) -> SubmitFinalityProofInfo> { + let block_number = *finality_target.number(); + + // the `submit_finality_proof` call will reject justifications with invalid, duplicate, + // unknown and extra signatures. It'll also reject justifications with less than necessary + // signatures. So we do not care about extra weight because of additional signatures here. + let precommits_len = justification.commit.precommits.len().saturated_into(); + let required_precommits = precommits_len; + + // We do care about extra weight because of more-than-expected headers in the votes + // ancestries. But we have problems computing extra weight for additional headers (weight of + // additional header is too small, so that our benchmarks aren't detecting that). So if there + // are more than expected headers in votes ancestries, we will treat the whole call weight + // as an extra weight. + let votes_ancestries_len = justification.votes_ancestries.len().saturated_into(); + let extra_weight = + if votes_ancestries_len > T::BridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY { + T::WeightInfo::submit_finality_proof(precommits_len, votes_ancestries_len) + } else { + Weight::zero() + }; + + // we can estimate extra call size easily, without any additional significant overhead + let actual_call_size: u32 = finality_target + .encoded_size() + .saturating_add(justification.encoded_size()) + .saturated_into(); + let max_expected_call_size = max_expected_call_size::(required_precommits); + let extra_size = actual_call_size.saturating_sub(max_expected_call_size); + + SubmitFinalityProofInfo { block_number, extra_weight, extra_size } +} + +/// Returns maximal expected size of `submit_finality_proof` call arguments. +fn max_expected_call_size, I: 'static>(required_precommits: u32) -> u32 { + let max_expected_justification_size = + GrandpaJustification::max_reasonable_size::(required_precommits); + + // call arguments are header and justification + T::BridgedChain::MAX_HEADER_SIZE.saturating_add(max_expected_justification_size) +} + #[cfg(test)] mod tests { use crate::{ call_ext::CallSubType, - mock::{run_test, test_header, RuntimeCall, TestNumber, TestRuntime}, - BestFinalized, + mock::{run_test, test_header, RuntimeCall, TestBridgedChain, TestNumber, TestRuntime}, + BestFinalized, Config, WeightInfo, }; + use bp_header_chain::ChainWithGrandpa; use bp_runtime::HeaderId; - use bp_test_utils::make_default_justification; + use bp_test_utils::{ + make_default_justification, make_justification_for_header, JustificationGeneratorParams, + }; + use frame_support::weights::Weight; + use sp_runtime::{testing::DigestItem, traits::Header as _, SaturatedConversion}; fn validate_block_submit(num: TestNumber) -> bool { let bridge_grandpa_call = crate::Call::::submit_finality_proof { @@ -160,4 +245,67 @@ mod tests { assert!(validate_block_submit(15)); }); } + + #[test] + fn extension_returns_correct_extra_size_if_call_arguments_are_too_large() { + // when call arguments are below our limit => no refund + let small_finality_target = test_header(1); + let justification_params = JustificationGeneratorParams { + header: small_finality_target.clone(), + ..Default::default() + }; + let small_justification = make_justification_for_header(justification_params); + let small_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof { + finality_target: Box::new(small_finality_target), + justification: small_justification, + }); + assert_eq!(small_call.submit_finality_proof_info().unwrap().extra_size, 0); + + // when call arguments are too large => partial refund + let mut large_finality_target = test_header(1); + large_finality_target + .digest_mut() + .push(DigestItem::Other(vec![42u8; 1024 * 1024])); + let justification_params = JustificationGeneratorParams { + header: large_finality_target.clone(), + ..Default::default() + }; + let large_justification = make_justification_for_header(justification_params); + let large_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof { + finality_target: Box::new(large_finality_target), + justification: large_justification, + }); + assert_ne!(large_call.submit_finality_proof_info().unwrap().extra_size, 0); + } + + #[test] + fn extension_returns_correct_extra_weight_if_there_are_too_many_headers_in_votes_ancestry() { + let finality_target = test_header(1); + let mut justification_params = JustificationGeneratorParams { + header: finality_target.clone(), + ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY, + ..Default::default() + }; + + // when there are `REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY` headers => no refund + let justification = make_justification_for_header(justification_params.clone()); + let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof { + finality_target: Box::new(finality_target.clone()), + justification, + }); + assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, Weight::zero()); + + // when there are `REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY + 1` headers => full refund + justification_params.ancestors += 1; + let justification = make_justification_for_header(justification_params); + let call_weight = ::WeightInfo::submit_finality_proof( + justification.commit.precommits.len().saturated_into(), + justification.votes_ancestries.len().saturated_into(), + ); + let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof { + finality_target: Box::new(finality_target), + justification, + }); + assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, call_weight); + } } diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index b11da53f7b3..e94d91a5c16 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -36,7 +36,7 @@ // Runtime-generated enums #![allow(clippy::large_enum_variant)] -use storage_types::StoredAuthoritySet; +pub use storage_types::StoredAuthoritySet; use bp_header_chain::{ justification::GrandpaJustification, ChainWithGrandpa, HeaderChain, InitializationData, @@ -180,6 +180,9 @@ pub mod pallet { let is_authorities_change_enacted = try_enact_authority_change::(&finality_target, set_id)?; + let may_refund_call_fee = is_authorities_change_enacted && + submit_finality_proof_info_from_args::(&*finality_target, &justification) + .fits_limits(); >::mutate(|count| *count += 1); insert_header::(*finality_target, hash); log::info!( @@ -193,8 +196,10 @@ pub mod pallet { // // We don't want to charge extra costs for mandatory operations. So relayer is not // paying fee for mandatory headers import transactions. - let is_mandatory_header = is_authorities_change_enacted; - let pays_fee = if is_mandatory_header { Pays::No } else { Pays::Yes }; + // + // If size/weight of the call is exceeds our estimated limits, the relayer still needs + // to pay for the transaction. + let pays_fee = if may_refund_call_fee { Pays::No } else { Pays::Yes }; // the proof size component of the call weight assumes that there are // `MaxBridgedAuthorities` in the `CurrentAuthoritySet` (we use `MaxEncodedLen` @@ -313,7 +318,7 @@ pub mod pallet { /// The current GRANDPA Authority set. #[pallet::storage] - pub(super) type CurrentAuthoritySet, I: 'static = ()> = + pub type CurrentAuthoritySet, I: 'static = ()> = StorageValue<_, StoredAuthoritySet, ValueQuery>; /// Optional pallet owner. @@ -504,7 +509,7 @@ pub mod pallet { init_params; let authority_set_length = authority_list.len(); let authority_set = StoredAuthoritySet::::try_new(authority_list, set_id) - .map_err(|_| { + .map_err(|e| { log::error!( target: LOG_TARGET, "Failed to initialize bridge. Number of authorities in the set {} is larger than the configured value {}", @@ -512,7 +517,7 @@ pub mod pallet { T::BridgedChain::MAX_AUTHORITIES_COUNT, ); - Error::TooManyAuthoritiesInSet + e })?; let initial_hash = header.hash(); @@ -630,8 +635,8 @@ pub fn initialize_for_benchmarks, I: 'static>(header: BridgedHeader mod tests { use super::*; use crate::mock::{ - run_test, test_header, RuntimeOrigin, TestHeader, TestNumber, TestRuntime, - MAX_BRIDGED_AUTHORITIES, + run_test, test_header, RuntimeOrigin, TestBridgedChain, TestHeader, TestNumber, + TestRuntime, MAX_BRIDGED_AUTHORITIES, }; use bp_header_chain::BridgeGrandpaCall; use bp_runtime::BasicOperatingMode; @@ -965,6 +970,64 @@ mod tests { }) } + #[test] + fn relayer_pays_tx_fee_when_submitting_huge_mandatory_header() { + run_test(|| { + initialize_substrate_bridge(); + + // let's prepare a huge authorities change header, which is definitely above size limits + let mut header = test_header(2); + header.digest = change_log(0); + header.digest.push(DigestItem::Other(vec![42u8; 1024 * 1024])); + let justification = make_default_justification(&header); + + // without large digest item ^^^ the relayer would have paid zero transaction fee + // (`Pays::No`) + let result = Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header.clone()), + justification, + ); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); + + // Make sure that our header is the best finalized + assert_eq!(>::get().unwrap().1, header.hash()); + assert!(>::contains_key(header.hash())); + }) + } + + #[test] + fn relayer_pays_tx_fee_when_submitting_justification_with_long_ancestry_votes() { + run_test(|| { + initialize_substrate_bridge(); + + // let's prepare a huge authorities change header, which is definitely above weight + // limits + let mut header = test_header(2); + header.digest = change_log(0); + let justification = make_justification_for_header(JustificationGeneratorParams { + header: header.clone(), + ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY + 1, + ..Default::default() + }); + + // without many headers in votes ancestries ^^^ the relayer would have paid zero + // transaction fee (`Pays::No`) + let result = Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header.clone()), + justification, + ); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); + + // Make sure that our header is the best finalized + assert_eq!(>::get().unwrap().1, header.hash()); + assert!(>::contains_key(header.hash())); + }) + } + #[test] fn importing_header_rejects_header_with_scheduled_change_delay() { run_test(|| { diff --git a/modules/grandpa/src/storage_types.rs b/modules/grandpa/src/storage_types.rs index 918c131289c..d674f1a7b0a 100644 --- a/modules/grandpa/src/storage_types.rs +++ b/modules/grandpa/src/storage_types.rs @@ -16,7 +16,7 @@ //! Wrappers for public types that are implementing `MaxEncodedLen` -use crate::Config; +use crate::{Config, Error}; use bp_header_chain::{AuthoritySet, ChainWithGrandpa}; use codec::{Decode, Encode, MaxEncodedLen}; @@ -52,8 +52,12 @@ impl, I: 'static> StoredAuthoritySet { /// Try to create a new bounded GRANDPA Authority Set from unbounded list. /// /// Returns error if number of authorities in the provided list is too large. - pub fn try_new(authorities: AuthorityList, set_id: SetId) -> Result { - Ok(Self { authorities: TryFrom::try_from(authorities).map_err(drop)?, set_id }) + pub fn try_new(authorities: AuthorityList, set_id: SetId) -> Result> { + Ok(Self { + authorities: TryFrom::try_from(authorities) + .map_err(|_| Error::TooManyAuthoritiesInSet)?, + set_id, + }) } /// Returns number of bytes that may be subtracted from the PoV component of diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index cc9033d1058..68af3024445 100644 --- a/modules/messages/src/lib.rs +++ b/modules/messages/src/lib.rs @@ -43,6 +43,7 @@ pub use weights::WeightInfo; pub use weights_ext::{ ensure_able_to_receive_confirmation, ensure_able_to_receive_message, ensure_weights_are_correct, WeightInfoExt, EXPECTED_DEFAULT_MESSAGE_LENGTH, + EXTRA_STORAGE_PROOF_SIZE, }; use crate::{ diff --git a/modules/parachains/src/call_ext.rs b/modules/parachains/src/call_ext.rs index 5aed9e80ba0..ee3770146c2 100644 --- a/modules/parachains/src/call_ext.rs +++ b/modules/parachains/src/call_ext.rs @@ -23,14 +23,17 @@ use sp_runtime::transaction_validity::{InvalidTransaction, TransactionValidity, /// Info about a `SubmitParachainHeads` call which tries to update a single parachain. #[derive(PartialEq, RuntimeDebug)] pub struct SubmitParachainHeadsInfo { + /// Number of the finalized relay block that has been used to prove parachain finality. pub at_relay_block_number: RelayBlockNumber, + /// Parachain identifier. pub para_id: ParaId, + /// Hash of the bundled parachain head. pub para_head_hash: ParaHash, } /// Helper struct that provides methods for working with the `SubmitParachainHeads` call. pub struct SubmitParachainHeadsHelper, I: 'static> { - pub _phantom_data: sp_std::marker::PhantomData<(T, I)>, + _phantom_data: sp_std::marker::PhantomData<(T, I)>, } impl, I: 'static> SubmitParachainHeadsHelper { diff --git a/modules/parachains/src/mock.rs b/modules/parachains/src/mock.rs index 0e8261f6899..aabcdcdd364 100644 --- a/modules/parachains/src/mock.rs +++ b/modules/parachains/src/mock.rs @@ -187,7 +187,7 @@ impl frame_system::Config for TestRuntime { type BlockLength = (); type SS58Prefix = (); type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; + type MaxConsumers = ConstU32<16>; } parameter_types! { @@ -223,7 +223,7 @@ impl pallet_bridge_parachains::Config for TestRuntime { type ParasPalletName = ParasPalletName; type ParaStoredHeaderDataBuilder = (Parachain1, Parachain2, Parachain3, BigParachain); type HeadsToKeep = HeadsToKeep; - type MaxParaHeadDataSize = frame_support::traits::ConstU32; + type MaxParaHeadDataSize = ConstU32; } #[derive(Debug)] diff --git a/primitives/header-chain/src/justification.rs b/primitives/header-chain/src/justification.rs index 43adc801254..1e34f365621 100644 --- a/primitives/header-chain/src/justification.rs +++ b/primitives/header-chain/src/justification.rs @@ -19,12 +19,15 @@ //! Adapted copy of substrate/client/finality-grandpa/src/justification.rs. If origin //! will ever be moved to the sp_finality_grandpa, we should reuse that implementation. -use codec::{Decode, Encode}; +use crate::ChainWithGrandpa; + +use bp_runtime::{BlockNumberOf, Chain, HashOf}; +use codec::{Decode, Encode, MaxEncodedLen}; use finality_grandpa::voter_set::VoterSet; use frame_support::RuntimeDebug; use scale_info::TypeInfo; use sp_finality_grandpa::{AuthorityId, AuthoritySignature, SetId}; -use sp_runtime::traits::Header as HeaderT; +use sp_runtime::{traits::Header as HeaderT, SaturatedConversion}; use sp_std::{ collections::{btree_map::BTreeMap, btree_set::BTreeSet}, prelude::*, @@ -46,6 +49,43 @@ pub struct GrandpaJustification { pub votes_ancestries: Vec

, } +impl GrandpaJustification { + /// Returns reasonable size of justification using constants from the provided chain. + /// + /// An imprecise analogue of `MaxEncodedLen` implementation. We don't use it for + /// any precise calculations - that's just an estimation. + pub fn max_reasonable_size(required_precommits: u32) -> u32 + where + C: Chain
+ ChainWithGrandpa, + { + // we don't need precise results here - just estimations, so some details + // are removed from computations (e.g. bytes required to encode vector length) + + // structures in `finality_grandpa` crate are not implementing `MaxEncodedLength`, so + // here's our estimation for the `finality_grandpa::Commit` struct size + // + // precommit is: hash + number + // signed precommit is: precommit + signature (64b) + authority id + // commit is: hash + number + vec of signed precommits + let signed_precommit_size: u32 = BlockNumberOf::::max_encoded_len() + .saturating_add(HashOf::::max_encoded_len().saturated_into()) + .saturating_add(64) + .saturating_add(AuthorityId::max_encoded_len().saturated_into()) + .saturated_into(); + let max_expected_signed_commit_size = signed_precommit_size + .saturating_mul(required_precommits) + .saturating_add(BlockNumberOf::::max_encoded_len().saturated_into()) + .saturating_add(HashOf::::max_encoded_len().saturated_into()); + + // justification is a signed GRANDPA commit, `votes_ancestries` vector and round number + let max_expected_votes_ancestries_size = C::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY + .saturating_mul(C::AVERAGE_HEADER_SIZE_IN_JUSTIFICATION); + + 8u32.saturating_add(max_expected_signed_commit_size) + .saturating_add(max_expected_votes_ancestries_size) + } +} + impl crate::FinalityProof for GrandpaJustification { fn target_header_number(&self) -> H::Number { self.commit.target_number @@ -59,6 +99,12 @@ pub enum Error { JustificationDecode, /// Justification is finalizing unexpected header. InvalidJustificationTarget, + /// Justification contains redundant votes. + RedundantVotesInJustification, + /// Justification contains unknown authority precommit. + UnknownAuthorityVote, + /// Justification contains duplicate authority precommit. + DuplicateAuthorityVote, /// The authority has provided an invalid signature. InvalidAuthoritySignature, /// The justification contains precommit for header that is not a descendant of the commit @@ -124,7 +170,7 @@ where authorities_set_id, authorities_set, justification, - &mut (), + &mut StrictVerificationCallbacks, ) } @@ -138,6 +184,23 @@ trait VerificationCallbacks { fn on_redundant_authority_vote(&mut self, precommit_idx: usize) -> Result<(), Error>; } +/// Verification callbacks that reject all unknown, duplicate or redundant votes. +struct StrictVerificationCallbacks; + +impl VerificationCallbacks for StrictVerificationCallbacks { + fn on_unkown_authority(&mut self, _precommit_idx: usize) -> Result<(), Error> { + Err(Error::UnknownAuthorityVote) + } + + fn on_duplicate_authority_vote(&mut self, _precommit_idx: usize) -> Result<(), Error> { + Err(Error::DuplicateAuthorityVote) + } + + fn on_redundant_authority_vote(&mut self, _precommit_idx: usize) -> Result<(), Error> { + Err(Error::RedundantVotesInJustification) + } +} + /// Verification callbacks for justification optimization. struct OptimizationCallbacks(Vec); @@ -170,21 +233,6 @@ impl VerificationCallbacks for OptimizationCallbacks { } } -// this implementation will be removed in https://github.com/paritytech/parity-bridges-common/pull/1882 -impl VerificationCallbacks for () { - fn on_unkown_authority(&mut self, _precommit_idx: usize) -> Result<(), Error> { - Ok(()) - } - - fn on_duplicate_authority_vote(&mut self, _precommit_idx: usize) -> Result<(), Error> { - Ok(()) - } - - fn on_redundant_authority_vote(&mut self, _precommit_idx: usize) -> Result<(), Error> { - Ok(()) - } -} - /// Verify that justification, that is generated by given authority set, finalizes given header. fn verify_justification_with_callbacks( finalized_target: (Header::Hash, Header::Number), @@ -206,11 +254,13 @@ where let mut signature_buffer = Vec::new(); let mut votes = BTreeSet::new(); let mut cumulative_weight = 0u64; + for (precommit_idx, signed) in justification.commit.precommits.iter().enumerate() { // if we have collected enough precommits, we probabably want to fail/remove extra // precommits - if cumulative_weight > threshold { + if cumulative_weight >= threshold { callbacks.on_redundant_authority_vote(precommit_idx)?; + continue } // authority must be in the set diff --git a/primitives/header-chain/tests/implementation_match.rs b/primitives/header-chain/tests/implementation_match.rs index aaa19d4b918..5d0fa35c376 100644 --- a/primitives/header-chain/tests/implementation_match.rs +++ b/primitives/header-chain/tests/implementation_match.rs @@ -15,7 +15,8 @@ // along with Parity Bridges Common. If not, see . //! Tests inside this module are made to ensure that our custom justification verification -//! implementation works exactly as `fn finality_grandpa::validate_commit`. +//! implementation works similar to the [`finality_grandpa::validate_commit`] and explicitly +//! show where we behave different. //! //! Some of tests in this module may partially duplicate tests from `justification.rs`, //! but their purpose is different. @@ -23,7 +24,7 @@ use bp_header_chain::justification::{verify_justification, Error, GrandpaJustification}; use bp_test_utils::{ header_id, make_justification_for_header, signed_precommit, test_header, Account, - JustificationGeneratorParams, ALICE, BOB, CHARLIE, DAVE, EVE, TEST_GRANDPA_SET_ID, + JustificationGeneratorParams, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, TEST_GRANDPA_SET_ID, }; use finality_grandpa::voter_set::VoterSet; use sp_finality_grandpa::{AuthorityId, AuthorityWeight}; @@ -172,7 +173,42 @@ fn same_result_when_precommit_target_is_not_descendant_of_commit_target() { } #[test] -fn same_result_when_justification_contains_duplicate_vote() { +fn same_result_when_there_are_not_enough_cumulative_weight_to_finalize_commit_target() { + // just remove one authority from the minimal set and we shall not reach the threshold + let mut authorities_set = minimal_accounts_set(); + authorities_set.pop(); + let justification = make_justification_for_header(JustificationGeneratorParams { + header: test_header(1), + authorities: authorities_set, + ..Default::default() + }); + + // our implementation returns an error + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Err(Error::TooLowCumulativeWeight), + ); + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == false`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(!result.is_valid()); +} + +// tests below are our differences with the original implementation + +#[test] +fn different_result_when_justification_contains_duplicate_vote() { let mut justification = make_justification_for_header(JustificationGeneratorParams { header: test_header(1), authorities: minimal_accounts_set(), @@ -181,10 +217,11 @@ fn same_result_when_justification_contains_duplicate_vote() { }); // the justification may contain exactly the same vote (i.e. same precommit and same signature) // multiple times && it isn't treated as an error by original implementation + let last_precommit = justification.commit.precommits.pop().unwrap(); justification.commit.precommits.push(justification.commit.precommits[0].clone()); - justification.commit.precommits.push(justification.commit.precommits[0].clone()); + justification.commit.precommits.push(last_precommit); - // our implementation succeeds + // our implementation fails assert_eq!( verify_justification::( header_id::(1), @@ -192,7 +229,7 @@ fn same_result_when_justification_contains_duplicate_vote() { &full_voter_set(), &justification, ), - Ok(()), + Err(Error::DuplicateAuthorityVote), ); // original implementation returns `Ok(validation_result)` // with `validation_result.is_valid() == true`. @@ -207,7 +244,7 @@ fn same_result_when_justification_contains_duplicate_vote() { } #[test] -fn same_result_when_authority_equivocates_once_in_a_round() { +fn different_results_when_authority_equivocates_once_in_a_round() { let mut justification = make_justification_for_header(JustificationGeneratorParams { header: test_header(1), authorities: minimal_accounts_set(), @@ -216,14 +253,16 @@ fn same_result_when_authority_equivocates_once_in_a_round() { }); // the justification original implementation allows authority to submit two different // votes in a single round, of which only first is 'accepted' + let last_precommit = justification.commit.precommits.pop().unwrap(); justification.commit.precommits.push(signed_precommit::( &ALICE, header_id::(1), justification.round, TEST_GRANDPA_SET_ID, )); + justification.commit.precommits.push(last_precommit); - // our implementation succeeds + // our implementation fails assert_eq!( verify_justification::( header_id::(1), @@ -231,7 +270,7 @@ fn same_result_when_authority_equivocates_once_in_a_round() { &full_voter_set(), &justification, ), - Ok(()), + Err(Error::DuplicateAuthorityVote), ); // original implementation returns `Ok(validation_result)` // with `validation_result.is_valid() == true`. @@ -246,7 +285,7 @@ fn same_result_when_authority_equivocates_once_in_a_round() { } #[test] -fn same_result_when_authority_equivocates_twice_in_a_round() { +fn different_results_when_authority_equivocates_twice_in_a_round() { let mut justification = make_justification_for_header(JustificationGeneratorParams { header: test_header(1), authorities: minimal_accounts_set(), @@ -259,6 +298,8 @@ fn same_result_when_authority_equivocates_twice_in_a_round() { // but there's also a code that prevents this from happening: // https://github.com/paritytech/finality-grandpa/blob/6aeea2d1159d0f418f0b86e70739f2130629ca09/src/round.rs#L287 // => so now we are also just ignoring all votes from the same authority, except the first one + let last_precommit = justification.commit.precommits.pop().unwrap(); + let prev_last_precommit = justification.commit.precommits.pop().unwrap(); justification.commit.precommits.push(signed_precommit::( &ALICE, header_id::(1), @@ -271,8 +312,10 @@ fn same_result_when_authority_equivocates_twice_in_a_round() { justification.round, TEST_GRANDPA_SET_ID, )); + justification.commit.precommits.push(last_precommit); + justification.commit.precommits.push(prev_last_precommit); - // our implementation succeeds + // our implementation fails assert_eq!( verify_justification::( header_id::(1), @@ -280,7 +323,7 @@ fn same_result_when_authority_equivocates_twice_in_a_round() { &full_voter_set(), &justification, ), - Ok(()), + Err(Error::DuplicateAuthorityVote), ); // original implementation returns `Ok(validation_result)` // with `validation_result.is_valid() == true`. @@ -295,17 +338,23 @@ fn same_result_when_authority_equivocates_twice_in_a_round() { } #[test] -fn same_result_when_there_are_not_enough_cumulative_weight_to_finalize_commit_target() { - // just remove one authority from the minimal set and we shall not reach the threshold - let mut authorities_set = minimal_accounts_set(); - authorities_set.pop(); - let justification = make_justification_for_header(JustificationGeneratorParams { +fn different_results_when_there_are_more_than_enough_votes() { + let mut justification = make_justification_for_header(JustificationGeneratorParams { header: test_header(1), - authorities: authorities_set, + authorities: minimal_accounts_set(), + ancestors: 0, ..Default::default() }); + // the reference implementation just keep verifying signatures even if we have + // collected enough votes. We are not + justification.commit.precommits.push(signed_precommit::( + &EVE, + header_id::(1), + justification.round, + TEST_GRANDPA_SET_ID, + )); - // our implementation returns an error + // our implementation fails assert_eq!( verify_justification::( header_id::(1), @@ -313,10 +362,10 @@ fn same_result_when_there_are_not_enough_cumulative_weight_to_finalize_commit_ta &full_voter_set(), &justification, ), - Err(Error::TooLowCumulativeWeight), + Err(Error::RedundantVotesInJustification), ); // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == false`. + // with `validation_result.is_valid() == true`. let result = finality_grandpa::validate_commit( &justification.commit, &full_voter_set(), @@ -324,5 +373,46 @@ fn same_result_when_there_are_not_enough_cumulative_weight_to_finalize_commit_ta ) .unwrap(); - assert!(!result.is_valid()); + assert!(result.is_valid()); +} + +#[test] +fn different_results_when_there_is_a_vote_of_unknown_authority() { + let mut justification = make_justification_for_header(JustificationGeneratorParams { + header: test_header(1), + authorities: minimal_accounts_set(), + ancestors: 0, + ..Default::default() + }); + // the reference implementation just keep verifying signatures even if we have + // collected enough votes. We are not + let last_precommit = justification.commit.precommits.pop().unwrap(); + justification.commit.precommits.push(signed_precommit::( + &FERDIE, + header_id::(1), + justification.round, + TEST_GRANDPA_SET_ID, + )); + justification.commit.precommits.push(last_precommit); + + // our implementation fails + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Err(Error::UnknownAuthorityVote), + ); + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == true`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(result.is_valid()); } diff --git a/primitives/header-chain/tests/justification.rs b/primitives/header-chain/tests/justification.rs index e18163313c9..f01f4ea2f10 100644 --- a/primitives/header-chain/tests/justification.rs +++ b/primitives/header-chain/tests/justification.rs @@ -16,14 +16,16 @@ //! Tests for Grandpa Justification code. -use bp_header_chain::justification::{optimize_justification, verify_justification, Error}; +use bp_header_chain::justification::{ + optimize_justification, required_justification_precommits, verify_justification, Error, +}; use bp_test_utils::*; type TestHeader = sp_runtime::testing::Header; #[test] fn valid_justification_accepted() { - let authorities = vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1), (DAVE, 1)]; + let authorities = vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1)]; let params = JustificationGeneratorParams { header: test_header(1), round: TEST_GRANDPA_ROUND, @@ -54,7 +56,7 @@ fn valid_justification_accepted_with_single_fork() { header: test_header(1), round: TEST_GRANDPA_ROUND, set_id: TEST_GRANDPA_SET_ID, - authorities: vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1), (DAVE, 1), (EVE, 1)], + authorities: vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1)], ancestors: 5, forks: 1, }; @@ -76,15 +78,16 @@ fn valid_justification_accepted_with_arbitrary_number_of_authorities() { use sp_finality_grandpa::AuthorityId; let n = 15; + let required_signatures = required_justification_precommits(n as _); let authorities = accounts(n).iter().map(|k| (*k, 1)).collect::>(); let params = JustificationGeneratorParams { header: test_header(1), round: TEST_GRANDPA_ROUND, set_id: TEST_GRANDPA_SET_ID, - authorities: authorities.clone(), + authorities: authorities.clone().into_iter().take(required_signatures as _).collect(), ancestors: n.into(), - forks: n.into(), + forks: required_signatures, }; let authorities = authorities diff --git a/primitives/polkadot-core/src/lib.rs b/primitives/polkadot-core/src/lib.rs index 3b1712042d8..10da6cd9b35 100644 --- a/primitives/polkadot-core/src/lib.rs +++ b/primitives/polkadot-core/src/lib.rs @@ -50,30 +50,37 @@ pub mod parachains; /// our bridge hub parachains huge. So let's stick to the real-world value here. /// /// Right now both Kusama and Polkadot aim to have around 1000 validators. Let's be safe here and -/// take twice as much here. -pub const MAX_AUTHORITIES_COUNT: u32 = 2_048; +/// take a bit more here. +pub const MAX_AUTHORITIES_COUNT: u32 = 1_256; /// Reasonable number of headers in the `votes_ancestries` on Polkadot-like chains. /// /// See [`bp_header_chain::ChainWithGrandpa`] for more details. -pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; +/// +/// This value comes from recent (February, 2023) Kusama and Polkadot headers. There are no +/// justifications with any additional headers in votes ancestry, so reasonable headers may +/// be set to zero. But we assume that there may be small GRANDPA lags, so we're leaving some +/// reserve here. +pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 2; /// Approximate average header size in `votes_ancestries` field of justification on Polkadot-like /// chains. /// /// See [`bp_header_chain::ChainWithGrandpa`] for more details. -pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 256; +/// +/// This value comes from recent (February, 2023) Kusama headers. Average is `336` there, but some +/// non-mandatory headers has size `40kb` (they contain the BABE epoch descriptor with all +/// authorities - just like our mandatory header). Since we assume `2` headers in justification +/// votes ancestry, let's set average header to `40kb / 2`. +pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 20 * 1024; /// Approximate maximal header size on Polkadot-like chains. /// -/// We expect maximal header to have digest item with the new authorities set for every consensus -/// engine (GRANDPA, Babe, BEEFY, ...) - so we multiply it by 3. And also -/// `AVERAGE_HEADER_SIZE_IN_JUSTIFICATION` bytes for other stuff. -/// /// See [`bp_header_chain::ChainWithGrandpa`] for more details. -pub const MAX_HEADER_SIZE: u32 = MAX_AUTHORITIES_COUNT - .saturating_mul(3) - .saturating_add(AVERAGE_HEADER_SIZE_IN_JUSTIFICATION); +/// +/// This value comes from recent (February, 2023) Kusama headers. Maximal header is a mandatory +/// header. In its SCALE-encoded form it is `80348` bytes. Let's have some reserve here. +pub const MAX_HEADER_SIZE: u32 = 90_000; /// Number of extra bytes (excluding size of storage value itself) of storage proof, built at /// Polkadot-like chain. This mostly depends on number of entries in the storage trie. diff --git a/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs b/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs index 5dbe1d69d13..8fedd22a40a 100644 --- a/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs +++ b/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs @@ -18,8 +18,9 @@ use relay_millau_client::Millau; use relay_rialto_parachain_client::RialtoParachain; -use substrate_relay_helper::messages_lane::{ - DirectReceiveMessagesDeliveryProofCallBuilder, SubstrateMessageLane, +use substrate_relay_helper::{ + messages_lane::{DirectReceiveMessagesDeliveryProofCallBuilder, SubstrateMessageLane}, + UtilityPalletBatchCallBuilder, }; substrate_relay_helper::generate_receive_message_proof_call_builder!( @@ -45,6 +46,6 @@ impl SubstrateMessageLane for MillauMessagesToRialtoParachain { millau_runtime::WithRialtoParachainMessagesInstance, >; - type SourceBatchCallBuilder = (); + type SourceBatchCallBuilder = UtilityPalletBatchCallBuilder; type TargetBatchCallBuilder = (); } diff --git a/relays/client-bridge-hub-rococo/src/lib.rs b/relays/client-bridge-hub-rococo/src/lib.rs index b6b844021e0..c8963f968d0 100644 --- a/relays/client-bridge-hub-rococo/src/lib.rs +++ b/relays/client-bridge-hub-rococo/src/lib.rs @@ -132,9 +132,6 @@ impl ChainWithMessages for BridgeHubRococo { bp_bridge_hub_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = bp_bridge_hub_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - - // TODO: fix (https://github.com/paritytech/parity-bridges-common/issues/1640) - type WeightInfo = (); } #[cfg(test)] diff --git a/relays/client-bridge-hub-wococo/src/lib.rs b/relays/client-bridge-hub-wococo/src/lib.rs index a466df35495..7f23c0d93d2 100644 --- a/relays/client-bridge-hub-wococo/src/lib.rs +++ b/relays/client-bridge-hub-wococo/src/lib.rs @@ -132,9 +132,6 @@ impl ChainWithMessages for BridgeHubWococo { bp_bridge_hub_wococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = bp_bridge_hub_wococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - - // TODO: fix (https://github.com/paritytech/parity-bridges-common/issues/1640) - type WeightInfo = (); } #[cfg(test)] diff --git a/relays/client-millau/src/lib.rs b/relays/client-millau/src/lib.rs index 4c4c1370a6a..9df21b1d503 100644 --- a/relays/client-millau/src/lib.rs +++ b/relays/client-millau/src/lib.rs @@ -51,7 +51,6 @@ impl ChainWithMessages for Millau { bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - type WeightInfo = (); } impl Chain for Millau { diff --git a/relays/client-rialto-parachain/src/lib.rs b/relays/client-rialto-parachain/src/lib.rs index 2c3792725c3..654848da2a7 100644 --- a/relays/client-rialto-parachain/src/lib.rs +++ b/relays/client-rialto-parachain/src/lib.rs @@ -79,7 +79,6 @@ impl ChainWithMessages for RialtoParachain { bp_rialto_parachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = bp_rialto_parachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - type WeightInfo = (); } impl ChainWithTransactions for RialtoParachain { diff --git a/relays/client-rialto/src/lib.rs b/relays/client-rialto/src/lib.rs index 39cc2721ddb..b822dfe7d89 100644 --- a/relays/client-rialto/src/lib.rs +++ b/relays/client-rialto/src/lib.rs @@ -69,7 +69,6 @@ impl ChainWithMessages for Rialto { bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - type WeightInfo = (); } impl ChainWithBalances for Rialto { diff --git a/relays/client-substrate/src/chain.rs b/relays/client-substrate/src/chain.rs index ebd9e7172d8..b55ae71cbf8 100644 --- a/relays/client-substrate/src/chain.rs +++ b/relays/client-substrate/src/chain.rs @@ -136,9 +136,6 @@ pub trait ChainWithMessages: Chain { /// Maximal number of unconfirmed messages in a single confirmation transaction at this /// `ChainWithMessages`. const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce; - - /// Weights of message pallet calls. - type WeightInfo: pallet_bridge_messages::WeightInfoExt; } /// Call type used by the chain. diff --git a/relays/client-substrate/src/client.rs b/relays/client-substrate/src/client.rs index 1a4aeb583cd..bddd53b39f8 100644 --- a/relays/client-substrate/src/client.rs +++ b/relays/client-substrate/src/client.rs @@ -21,7 +21,6 @@ use crate::{ rpc::{ SubstrateAuthorClient, SubstrateChainClient, SubstrateFinalityClient, SubstrateFrameSystemClient, SubstrateStateClient, SubstrateSystemClient, - SubstrateTransactionPaymentClient, }, transaction_stall_timeout, AccountKeyPairOf, ConnectionParams, Error, HashOf, HeaderIdOf, Result, SignParam, TransactionTracker, UnsignedTransaction, @@ -31,15 +30,16 @@ use async_std::sync::{Arc, Mutex}; use async_trait::async_trait; use bp_runtime::{HeaderIdProvider, StorageDoubleMapKeyProvider, StorageMapKeyProvider}; use codec::{Decode, Encode}; +use frame_support::weights::Weight; use frame_system::AccountInfo; use futures::{SinkExt, StreamExt}; use jsonrpsee::{ core::DeserializeOwned, ws_client::{WsClient as RpcClient, WsClientBuilder as RpcClientBuilder}, }; -use num_traits::{Bounded, Saturating, Zero}; +use num_traits::{Saturating, Zero}; use pallet_balances::AccountData; -use pallet_transaction_payment::InclusionFee; +use pallet_transaction_payment::RuntimeDispatchInfo; use relay_utils::{relay_loop::RECONNECT_DELAY, STALL_TIMEOUT}; use sp_core::{ storage::{StorageData, StorageKey}, @@ -51,10 +51,11 @@ use sp_runtime::{ }; use sp_trie::StorageProof; use sp_version::RuntimeVersion; -use std::{convert::TryFrom, future::Future}; +use std::future::Future; const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities"; const SUB_API_TXPOOL_VALIDATE_TRANSACTION: &str = "TaggedTransactionQueue_validate_transaction"; +const SUB_API_TX_PAYMENT_QUERY_INFO: &str = "TransactionPaymentApi_query_info"; const MAX_SUBSCRIPTION_CAPACITY: usize = 4096; /// The difference between best block number and number of its ancestor, that is enough @@ -591,33 +592,24 @@ impl Client { .await } - /// Estimate fee that will be spent on given extrinsic. - pub async fn estimate_extrinsic_fee( + /// Returns weight of the given transaction. + pub async fn extimate_extrinsic_weight( &self, - transaction: Bytes, - ) -> Result> { + transaction: SignedTransaction, + ) -> Result { self.jsonrpsee_execute(move |client| async move { - let fee_details = - SubstrateTransactionPaymentClient::::fee_details(&*client, transaction, None) - .await?; - let inclusion_fee = fee_details - .inclusion_fee - .map(|inclusion_fee| InclusionFee { - base_fee: C::Balance::try_from(inclusion_fee.base_fee.into_u256()) - .unwrap_or_else(|_| C::Balance::max_value()), - len_fee: C::Balance::try_from(inclusion_fee.len_fee.into_u256()) - .unwrap_or_else(|_| C::Balance::max_value()), - adjusted_weight_fee: C::Balance::try_from( - inclusion_fee.adjusted_weight_fee.into_u256(), - ) - .unwrap_or_else(|_| C::Balance::max_value()), - }) - .unwrap_or_else(|| InclusionFee { - base_fee: Zero::zero(), - len_fee: Zero::zero(), - adjusted_weight_fee: Zero::zero(), - }); - Ok(inclusion_fee) + let transaction_len = transaction.encoded_size() as u32; + + let call = SUB_API_TX_PAYMENT_QUERY_INFO.to_string(); + let data = Bytes((transaction, transaction_len).encode()); + + let encoded_response = + SubstrateStateClient::::call(&*client, call, data, None).await?; + let dispatch_info = + RuntimeDispatchInfo::::decode(&mut &encoded_response.0[..]) + .map_err(Error::ResponseParseFailed)?; + + Ok(dispatch_info.weight) }) .await } diff --git a/relays/lib-substrate-relay/src/messages_lane.rs b/relays/lib-substrate-relay/src/messages_lane.rs index 1a4f65ff51e..0a7a3566d20 100644 --- a/relays/lib-substrate-relay/src/messages_lane.rs +++ b/relays/lib-substrate-relay/src/messages_lane.rs @@ -25,7 +25,9 @@ use crate::{ use async_std::sync::Arc; use bp_messages::{LaneId, MessageNonce}; -use bp_runtime::{AccountIdOf, Chain as _, HeaderIdOf, WeightExtraOps}; +use bp_runtime::{ + AccountIdOf, Chain as _, EncodedOrDecodedCall, HeaderIdOf, TransactionEra, WeightExtraOps, +}; use bridge_runtime_common::messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; @@ -35,13 +37,15 @@ use messages_relay::{message_lane::MessageLane, message_lane_loop::BatchTransact use pallet_bridge_messages::{Call as BridgeMessagesCall, Config as BridgeMessagesConfig}; use relay_substrate_client::{ transaction_stall_timeout, AccountKeyPairOf, BalanceOf, BlockNumberOf, CallOf, Chain, - ChainWithMessages, ChainWithTransactions, Client, Error as SubstrateError, HashOf, + ChainWithMessages, ChainWithTransactions, Client, Error as SubstrateError, HashOf, SignParam, + UnsignedTransaction, }; use relay_utils::{ metrics::{GlobalMetrics, MetricsParams, StandaloneMetric}, STALL_TIMEOUT, }; use sp_core::Pair; +use sp_runtime::traits::Zero; use std::{convert::TryFrom, fmt::Debug, marker::PhantomData}; /// Substrate -> Substrate messages synchronization pipeline. @@ -159,25 +163,25 @@ where AccountIdOf: From< as Pair>::Public>, BalanceOf: TryFrom>, { - let source_client = params.source_client; - let target_client = params.target_client; - let relayer_id_at_source: AccountIdOf = - params.source_transaction_params.signer.public().into(); - // 2/3 is reserved for proofs and tx overhead let max_messages_size_in_single_batch = P::TargetChain::max_extrinsic_size() / 3; // we don't know exact weights of the Polkadot runtime. So to guess weights we'll be using // weights from Rialto and then simply dividing it by x2. let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = - crate::messages_lane::select_delivery_transaction_limits::< - ::WeightInfo, - >( + select_delivery_transaction_limits_rpc::

( + ¶ms, P::TargetChain::max_extrinsic_weight(), P::SourceChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, - ); + ) + .await?; let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = (max_messages_in_single_batch / 2, max_messages_weight_in_single_batch / 2); + let source_client = params.source_client; + let target_client = params.target_client; + let relayer_id_at_source: AccountIdOf = + params.source_transaction_params.signer.public().into(); + log::info!( target: "bridge", "Starting {} -> {} messages relay.\n\t\ @@ -437,12 +441,15 @@ macro_rules! generate_receive_message_delivery_proof_call_builder { }; } -/// Returns maximal number of messages and their maximal cumulative dispatch weight, based -/// on given chain parameters. -pub fn select_delivery_transaction_limits( +/// Returns maximal number of messages and their maximal cumulative dispatch weight. +async fn select_delivery_transaction_limits_rpc( + params: &MessagesRelayParams

, max_extrinsic_weight: Weight, max_unconfirmed_messages_at_inbound_lane: MessageNonce, -) -> (MessageNonce, Weight) { +) -> anyhow::Result<(MessageNonce, Weight)> +where + AccountIdOf: From< as Pair>::Public>, +{ // We may try to guess accurate value, based on maximal number of messages and per-message // weight overhead, but the relay loop isn't using this info in a super-accurate way anyway. // So just a rough guess: let's say 1/3 of max tx weight is for tx itself and the rest is @@ -455,13 +462,35 @@ pub fn select_delivery_transaction_limits(params, 0)?; + let delivery_tx_with_zero_messages_weight = params + .target_client + .extimate_extrinsic_weight(delivery_tx_with_zero_messages) + .await + .map_err(|e| { + anyhow::format_err!("Failed to estimate delivery extrinsic weight: {:?}", e) + })?; + + // weight of single message delivery with outbound lane state + let delivery_tx_with_one_message = dummy_messages_delivery_transaction::

(params, 1)?; + let delivery_tx_with_one_message_weight = params + .target_client + .extimate_extrinsic_weight(delivery_tx_with_one_message) + .await + .map_err(|e| { + anyhow::format_err!("Failed to estimate delivery extrinsic weight: {:?}", e) + })?; + + // message overhead is roughly `delivery_tx_with_one_message_weight - + // delivery_tx_with_zero_messages_weight` + let delivery_tx_weight_rest = weight_for_delivery_tx - delivery_tx_with_zero_messages_weight; + let delivery_tx_message_overhead = + delivery_tx_with_one_message_weight.saturating_sub(delivery_tx_with_zero_messages_weight); let max_number_of_messages = std::cmp::min( delivery_tx_weight_rest - .min_components_checked_div(W::receive_messages_proof_messages_overhead(1)) + .min_components_checked_div(delivery_tx_message_overhead) .unwrap_or(u64::MAX), max_unconfirmed_messages_at_inbound_lane, ); @@ -475,36 +504,58 @@ pub fn select_delivery_transaction_limits; - - #[test] - fn select_delivery_transaction_limits_is_sane() { - // we want to check the `proof_size` component here too. But for Rialto and Millau - // it is set to `u64::MAX` (as for Polkadot and other relay/standalone chains). - // So let's use RialtoParachain limits here - it has `proof_size` limit as all - // Cumulus-based parachains do. - let (max_count, max_weight) = - select_delivery_transaction_limits::( - bp_rialto_parachain::RialtoParachain::max_extrinsic_weight(), - bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, - ); - assert_eq!( - (max_count, max_weight), - // We don't actually care about these values, so feel free to update them whenever test - // fails. The only thing to do before that is to ensure that new values looks sane: - // i.e. weight reserved for messages dispatch allows dispatch of non-trivial messages. - // - // Any significant change in this values should attract additional attention. - (1024, Weight::from_parts(866_600_106_667, 2_271_915)), +/// Returns dummy message delivery transaction with zero messages and `1kb` proof. +fn dummy_messages_delivery_transaction( + params: &MessagesRelayParams

, + messages: u32, +) -> anyhow::Result<::SignedTransaction> +where + AccountIdOf: From< as Pair>::Public>, +{ + // we don't care about any call values here, because all that the estimation RPC does + // is calls `GetDispatchInfo::get_dispatch_info` for the wrapped call. So we only are + // interested in values that affect call weight - e.g. number of messages and the + // storage proof size + + let dummy_messages_delivery_call = + P::ReceiveMessagesProofCallBuilder::build_receive_messages_proof_call( + params.source_transaction_params.signer.public().into(), + ( + Weight::zero(), + FromBridgedChainMessagesProof { + bridged_header_hash: Default::default(), + // we may use per-chain `EXTRA_STORAGE_PROOF_SIZE`, but since we don't need + // exact values, this global estimation is fine + storage_proof: vec![vec![ + 42u8; + pallet_bridge_messages::EXTRA_STORAGE_PROOF_SIZE + as usize + ]], + lane: Default::default(), + nonces_start: 1, + nonces_end: messages as u64, + }, + ), + messages, + Weight::zero(), + false, ); - } + P::TargetChain::sign_transaction( + SignParam { + spec_version: 0, + transaction_version: 0, + genesis_hash: Default::default(), + signer: params.target_transaction_params.signer.clone(), + }, + UnsignedTransaction { + call: EncodedOrDecodedCall::Decoded(dummy_messages_delivery_call), + nonce: Zero::zero(), + tip: Zero::zero(), + era: TransactionEra::Immortal, + }, + ) + .map_err(Into::into) } diff --git a/relays/messages/src/message_race_loop.rs b/relays/messages/src/message_race_loop.rs index 2988ab231d9..3d995a0a362 100644 --- a/relays/messages/src/message_race_loop.rs +++ b/relays/messages/src/message_race_loop.rs @@ -573,17 +573,14 @@ pub async fn run, TC: TargetClient

>( if let Some((at_block, nonces_range, proof)) = race_state.nonces_to_submit.as_ref() { log::debug!( target: "bridge", - "Going to submit proof of messages in range {:?} to {} node", + "Going to submit proof of messages in range {:?} to {} node{}", nonces_range, P::target_name(), + target_batch_transaction.as_ref().map(|tx| format!( + ". This transaction is batched with sending the proof for header {:?}.", + tx.required_header_id()) + ).unwrap_or_default(), ); - if let Some(ref target_batch_transaction) = target_batch_transaction { - log::debug!( - target: "bridge", - "This transaction is batched with sending the proof for header {:?}.", - target_batch_transaction.required_header_id(), - ); - } target_submit_proof.set( race_target From 5c29781bfebdb34628ca58aacbd4547e1cfe2c45 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Tue, 28 Feb 2023 15:40:32 +0200 Subject: [PATCH 205/263] Squashed 'bridges/' changes from 3c15c3645..d05a98473 d05a98473 Refund messages confirmation tx (#1904) e2e8a7198 Relayers pallet: extend payment source id (#1907) cccf73b3f fix nightly clippy issues (#1915) a33a91e79 Bump tempfile from 3.3.0 to 3.4.0 1df768a2e Bump time from 0.3.17 to 0.3.20 cf17b424f Bump sysinfo from 0.28.0 to 0.28.1 0b6276b41 Bump jsonrpsee from 0.15.1 to 0.16.2 328dde02b Bump rand from 0.7.3 to 0.8.5 2f302a4b6 Bump trie-db from 0.25.1 to 0.26.0 b5d5d03ab CI add jobs to publish Docker images description to hub.docker.com (#1906) db5168f18 Do not stall on lost transaction (#1903) 2d83d6389 Fix init-bridge (#1900) git-subtree-dir: bridges git-subtree-split: d05a98473dc933cfed9e5f59023efa2ec811f03c --- .gitlab-ci.yml | 47 ++- Cargo.lock | 242 ++++--------- bin/millau/runtime/src/lib.rs | 25 +- bin/rialto-parachain/runtime/src/lib.rs | 4 +- bin/rialto/runtime/src/lib.rs | 4 +- bin/runtime-common/Cargo.toml | 1 + bin/runtime-common/src/lib.rs | 3 +- bin/runtime-common/src/messages_call_ext.rs | 219 +++++++----- bin/runtime-common/src/mock.rs | 12 +- .../src/refund_relayer_extension.rs | 329 ++++++++++++++---- ...arachain-millau-maintenance-dashboard.json | 8 +- docs/dockerhub-bridges-common-relay.README.md | 1 + docs/dockerhub-millau-bridge-node.README.md | 1 + docs/dockerhub-rialto-bridge-node.README.md | 1 + ...kerhub-rialto-parachain-collator.README.md | 1 + docs/dockerhub-substrate-relay.README.md | 1 + modules/grandpa/src/lib.rs | 2 +- modules/relayers/Cargo.toml | 1 + modules/relayers/src/benchmarking.rs | 12 +- modules/relayers/src/lib.rs | 146 +++++--- modules/relayers/src/mock.rs | 7 +- modules/relayers/src/payment_adapter.rs | 96 +++-- modules/relayers/src/weights.rs | 24 +- primitives/header-chain/src/justification.rs | 2 +- .../header-chain/tests/justification.rs | 11 +- primitives/relayers/Cargo.toml | 2 + primitives/relayers/src/lib.rs | 122 +++++-- primitives/runtime/Cargo.toml | 2 +- primitives/runtime/src/lib.rs | 6 + relays/bin-substrate/Cargo.toml | 2 +- .../src/cli/resubmit_transactions.rs | 1 - relays/client-bridge-hub-rococo/Cargo.toml | 1 + relays/client-bridge-hub-rococo/src/lib.rs | 2 + relays/client-bridge-hub-wococo/src/lib.rs | 2 + relays/client-kusama/Cargo.toml | 1 + relays/client-kusama/src/lib.rs | 2 + relays/client-millau/Cargo.toml | 1 + relays/client-millau/src/lib.rs | 2 + relays/client-polkadot/Cargo.toml | 1 + relays/client-polkadot/src/lib.rs | 2 + relays/client-rialto-parachain/Cargo.toml | 1 + relays/client-rialto-parachain/src/lib.rs | 2 + relays/client-rialto/Cargo.toml | 1 + relays/client-rialto/src/lib.rs | 2 + relays/client-rococo/Cargo.toml | 1 + relays/client-rococo/src/lib.rs | 2 + relays/client-substrate/Cargo.toml | 4 +- relays/client-substrate/src/chain.rs | 6 +- relays/client-substrate/src/test_chain.rs | 2 + relays/client-westend/Cargo.toml | 1 + relays/client-westend/src/lib.rs | 3 + relays/client-wococo/Cargo.toml | 1 + relays/client-wococo/src/lib.rs | 2 + .../src/finality/engine.rs | 8 +- .../src/messages_metrics.rs | 24 +- relays/messages/src/message_lane_loop.rs | 53 --- relays/messages/src/message_race_delivery.rs | 19 +- relays/messages/src/message_race_loop.rs | 28 +- relays/messages/src/message_race_strategy.rs | 18 +- 59 files changed, 937 insertions(+), 590 deletions(-) create mode 100644 docs/dockerhub-bridges-common-relay.README.md create mode 100644 docs/dockerhub-millau-bridge-node.README.md create mode 100644 docs/dockerhub-rialto-bridge-node.README.md create mode 100644 docs/dockerhub-rialto-parachain-collator.README.md create mode 100644 docs/dockerhub-substrate-relay.README.md diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 15ba0ca7cb6..b232f610690 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,6 +4,7 @@ stages: - test - build - publish + - publish-docker-description variables: GIT_STRATEGY: fetch @@ -315,4 +316,48 @@ bridges-common-relay: BRIDGES_PROJECT: substrate-relay DOCKER_IMAGE_NAME: bridges-common-relay -# FIXME: publish binaries +# Publish Docker images description to hub.docker.com + +.publish-docker-image-description: &publish-docker-image-description + stage: publish-docker-description + image: paritytech/dockerhub-description + variables: + DOCKER_USERNAME: $Docker_Hub_User_Parity + DOCKER_PASSWORD: $Docker_Hub_Pass_Parity + README_FILEPATH: $CI_PROJECT_DIR/docs/${CI_JOB_NAME}.README.md + rules: + - if: $CI_COMMIT_REF_NAME == "master" + changes: + - docs/${CI_JOB_NAME}.README.md + script: + - export DOCKERHUB_REPOSITORY="paritytech/${CI_JOB_NAME:10}" + - cd / && sh entrypoint.sh + tags: + - kubernetes-parity-build + +dockerhub-rialto-bridge-node: + extends: .publish-docker-image-description + variables: + SHORT_DESCRIPTION: "rialto-bridge-node" + +dockerhub-rialto-parachain-collator: + extends: .publish-docker-image-description + variables: + SHORT_DESCRIPTION: "rialto-parachain-collator" + +dockerhub-millau-bridge-node: + extends: .publish-docker-image-description + variables: + SHORT_DESCRIPTION: "millau-bridge-node" + +dockerhub-substrate-relay: + extends: .publish-docker-image-description + variables: + SHORT_DESCRIPTION: "substrate-relay" + +dockerhub-bridges-common-relay: + extends: .publish-docker-image-description + variables: + SHORT_DESCRIPTION: "bridges-common-relay" + +# FIXME: publish binaries \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 899367d037a..c3c8c9eb97a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -252,7 +252,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.20", ] [[package]] @@ -268,7 +268,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.20", ] [[package]] @@ -590,7 +590,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f11 dependencies = [ "beefy-gadget", "futures", - "jsonrpsee 0.16.2", + "jsonrpsee", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -971,6 +971,8 @@ dependencies = [ "frame-support", "hex", "hex-literal", + "parity-scale-codec", + "scale-info", "sp-runtime 7.0.0", "sp-std 5.0.0", ] @@ -1035,7 +1037,7 @@ dependencies = [ "sp-state-machine 0.13.0", "sp-std 5.0.0", "sp-trie 7.0.0", - "trie-db 0.25.1", + "trie-db 0.26.0", ] [[package]] @@ -1083,6 +1085,7 @@ dependencies = [ "bp-messages", "bp-parachains", "bp-polkadot-core", + "bp-relayers", "bp-runtime", "bp-test-utils", "frame-support", @@ -2172,7 +2175,7 @@ dependencies = [ "async-trait", "cumulus-primitives-core", "futures", - "jsonrpsee-core 0.16.2", + "jsonrpsee-core", "parity-scale-codec", "polkadot-overseer", "polkadot-service", @@ -2234,7 +2237,7 @@ dependencies = [ "cumulus-relay-chain-interface", "futures", "futures-timer", - "jsonrpsee 0.16.2", + "jsonrpsee", "lru 0.9.0", "parity-scale-codec", "polkadot-service", @@ -4251,56 +4254,22 @@ dependencies = [ "serde_json", ] -[[package]] -name = "jsonrpsee" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd0d559d5e679b1ab2f869b486a11182923863b1b3ee8b421763cdd707b783a" -dependencies = [ - "jsonrpsee-core 0.15.1", - "jsonrpsee-proc-macros 0.15.1", - "jsonrpsee-types 0.15.1", - "jsonrpsee-ws-client 0.15.1", - "tracing", -] - [[package]] name = "jsonrpsee" version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" dependencies = [ - "jsonrpsee-client-transport 0.16.2", - "jsonrpsee-core 0.16.2", + "jsonrpsee-client-transport", + "jsonrpsee-core", "jsonrpsee-http-client", - "jsonrpsee-proc-macros 0.16.2", + "jsonrpsee-proc-macros", "jsonrpsee-server", - "jsonrpsee-types 0.16.2", - "jsonrpsee-ws-client 0.16.2", + "jsonrpsee-types", + "jsonrpsee-ws-client", "tracing", ] -[[package]] -name = "jsonrpsee-client-transport" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8752740ecd374bcbf8b69f3e80b0327942df76f793f8d4e60d3355650c31fb74" -dependencies = [ - "futures-util", - "http", - "jsonrpsee-core 0.15.1", - "jsonrpsee-types 0.15.1", - "pin-project", - "rustls-native-certs", - "soketto", - "thiserror", - "tokio", - "tokio-rustls", - "tokio-util", - "tracing", - "webpki-roots", -] - [[package]] name = "jsonrpsee-client-transport" version = "0.16.2" @@ -4309,8 +4278,8 @@ checksum = "965de52763f2004bc91ac5bcec504192440f0b568a5d621c59d9dbd6f886c3fb" dependencies = [ "futures-util", "http", - "jsonrpsee-core 0.16.2", - "jsonrpsee-types 0.16.2", + "jsonrpsee-core", + "jsonrpsee-types", "pin-project", "rustls-native-certs", "soketto", @@ -4322,29 +4291,6 @@ dependencies = [ "webpki-roots", ] -[[package]] -name = "jsonrpsee-core" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3dc3e9cf2ba50b7b1d7d76a667619f82846caa39e8e8daa8a4962d74acaddca" -dependencies = [ - "anyhow", - "async-lock", - "async-trait", - "beef", - "futures-channel", - "futures-timer", - "futures-util", - "jsonrpsee-types 0.15.1", - "rustc-hash", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", - "tracing-futures", -] - [[package]] name = "jsonrpsee-core" version = "0.16.2" @@ -4361,7 +4307,7 @@ dependencies = [ "futures-util", "globset", "hyper", - "jsonrpsee-types 0.16.2", + "jsonrpsee-types", "parking_lot 0.12.1", "rand 0.8.5", "rustc-hash", @@ -4382,8 +4328,8 @@ dependencies = [ "async-trait", "hyper", "hyper-rustls", - "jsonrpsee-core 0.16.2", - "jsonrpsee-types 0.16.2", + "jsonrpsee-core", + "jsonrpsee-types", "rustc-hash", "serde", "serde_json", @@ -4392,18 +4338,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "jsonrpsee-proc-macros" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd67957d4280217247588ac86614ead007b301ca2fa9f19c19f880a536f029e3" -dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.107", -] - [[package]] name = "jsonrpsee-proc-macros" version = "0.16.2" @@ -4427,8 +4361,8 @@ dependencies = [ "futures-util", "http", "hyper", - "jsonrpsee-core 0.16.2", - "jsonrpsee-types 0.16.2", + "jsonrpsee-core", + "jsonrpsee-types", "serde", "serde_json", "soketto", @@ -4439,20 +4373,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "jsonrpsee-types" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e290bba767401b646812f608c099b922d8142603c9e73a50fb192d3ac86f4a0d" -dependencies = [ - "anyhow", - "beef", - "serde", - "serde_json", - "thiserror", - "tracing", -] - [[package]] name = "jsonrpsee-types" version = "0.16.2" @@ -4467,18 +4387,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "jsonrpsee-ws-client" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ee5feddd5188e62ac08fcf0e56478138e581509d4730f3f7be9b57dd402a4ff" -dependencies = [ - "http", - "jsonrpsee-client-transport 0.15.1", - "jsonrpsee-core 0.15.1", - "jsonrpsee-types 0.15.1", -] - [[package]] name = "jsonrpsee-ws-client" version = "0.16.2" @@ -4486,9 +4394,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b83daeecfc6517cfe210df24e570fb06213533dfb990318fae781f4c7119dd9" dependencies = [ "http", - "jsonrpsee-client-transport 0.16.2", - "jsonrpsee-core 0.16.2", - "jsonrpsee-types 0.16.2", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", ] [[package]] @@ -5455,7 +5363,7 @@ dependencies = [ "clap 4.1.6", "frame-benchmarking", "frame-benchmarking-cli", - "jsonrpsee 0.16.2", + "jsonrpsee", "millau-runtime", "mmr-rpc", "node-inspect", @@ -5604,7 +5512,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "anyhow", - "jsonrpsee 0.16.2", + "jsonrpsee", "parity-scale-codec", "serde", "sp-api", @@ -6412,6 +6320,7 @@ dependencies = [ "frame-system", "log", "pallet-balances", + "pallet-bridge-messages", "parity-scale-codec", "scale-info", "sp-arithmetic 6.0.0", @@ -7028,7 +6937,7 @@ name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "jsonrpsee 0.16.2", + "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", "sp-api", @@ -8243,7 +8152,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004 dependencies = [ "beefy-gadget", "beefy-gadget-rpc", - "jsonrpsee 0.16.2", + "jsonrpsee", "mmr-rpc", "pallet-transaction-payment-rpc", "polkadot-primitives", @@ -9084,7 +8993,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.17", + "time 0.3.20", "x509-parser 0.13.2", "yasna", ] @@ -9097,7 +9006,7 @@ checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring", - "time 0.3.17", + "time 0.3.20", "yasna", ] @@ -9201,6 +9110,7 @@ dependencies = [ "bp-header-chain", "bp-messages", "bp-parachains", + "bp-runtime", "bp-wococo", "bridge-runtime-common", "parity-scale-codec", @@ -9236,6 +9146,7 @@ name = "relay-kusama-client" version = "0.1.0" dependencies = [ "bp-kusama", + "bp-runtime", "relay-substrate-client", "relay-utils", "sp-core 7.0.0", @@ -9247,6 +9158,7 @@ version = "0.1.0" dependencies = [ "bp-messages", "bp-millau", + "bp-runtime", "frame-support", "frame-system", "millau-runtime", @@ -9263,6 +9175,7 @@ name = "relay-polkadot-client" version = "0.1.0" dependencies = [ "bp-polkadot", + "bp-runtime", "relay-substrate-client", "relay-utils", "sp-core 7.0.0", @@ -9274,6 +9187,7 @@ version = "0.1.0" dependencies = [ "bp-messages", "bp-rialto", + "bp-runtime", "frame-support", "frame-system", "pallet-transaction-payment", @@ -9294,6 +9208,7 @@ dependencies = [ "bp-millau", "bp-polkadot-core", "bp-rialto-parachain", + "bp-runtime", "bridge-runtime-common", "parity-scale-codec", "relay-substrate-client", @@ -9309,6 +9224,7 @@ name = "relay-rococo-client" version = "0.1.0" dependencies = [ "bp-rococo", + "bp-runtime", "relay-substrate-client", "relay-utils", "sp-core 7.0.0", @@ -9328,7 +9244,7 @@ dependencies = [ "frame-support", "frame-system", "futures", - "jsonrpsee 0.15.1", + "jsonrpsee", "log", "num-traits", "pallet-balances", @@ -9337,7 +9253,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "pallet-utility", "parity-scale-codec", - "rand 0.7.3", + "rand 0.8.5", "relay-utils", "sc-chain-spec", "sc-rpc-api", @@ -9375,7 +9291,7 @@ dependencies = [ "substrate-prometheus-endpoint", "sysinfo", "thiserror", - "time 0.3.17", + "time 0.3.20", "tokio", ] @@ -9383,6 +9299,7 @@ dependencies = [ name = "relay-westend-client" version = "0.1.0" dependencies = [ + "bp-runtime", "bp-westend", "relay-substrate-client", "relay-utils", @@ -9393,21 +9310,13 @@ dependencies = [ name = "relay-wococo-client" version = "0.1.0" dependencies = [ + "bp-runtime", "bp-wococo", "relay-substrate-client", "relay-utils", "sp-core 7.0.0", ] -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - [[package]] name = "resolv-conf" version = "0.7.0" @@ -9471,7 +9380,7 @@ dependencies = [ "cumulus-relay-chain-interface", "frame-benchmarking", "frame-benchmarking-cli", - "jsonrpsee 0.16.2", + "jsonrpsee", "log", "pallet-transaction-payment-rpc", "parity-scale-codec", @@ -10184,7 +10093,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "futures", - "jsonrpsee 0.16.2", + "jsonrpsee", "sc-consensus-babe", "sc-consensus-epochs", "sc-rpc-api", @@ -10350,7 +10259,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f11 dependencies = [ "finality-grandpa", "futures", - "jsonrpsee 0.16.2", + "jsonrpsee", "log", "parity-scale-codec", "sc-client-api", @@ -10628,7 +10537,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "futures", - "jsonrpsee 0.16.2", + "jsonrpsee", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -10657,7 +10566,7 @@ name = "sc-rpc-api" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "jsonrpsee 0.16.2", + "jsonrpsee", "parity-scale-codec", "sc-chain-spec", "sc-transaction-pool-api", @@ -10677,7 +10586,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "http", - "jsonrpsee 0.16.2", + "jsonrpsee", "log", "serde_json", "substrate-prometheus-endpoint", @@ -10695,7 +10604,7 @@ dependencies = [ "futures", "futures-util", "hex", - "jsonrpsee 0.16.2", + "jsonrpsee", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -10722,7 +10631,7 @@ dependencies = [ "exit-future", "futures", "futures-timer", - "jsonrpsee 0.16.2", + "jsonrpsee", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -10810,7 +10719,7 @@ name = "sc-sync-state-rpc" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "jsonrpsee 0.16.2", + "jsonrpsee", "parity-scale-codec", "sc-chain-spec", "sc-client-api", @@ -12715,7 +12624,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f11 dependencies = [ "frame-system-rpc-runtime-api", "futures", - "jsonrpsee 0.16.2", + "jsonrpsee", "log", "parity-scale-codec", "sc-rpc-api", @@ -12848,7 +12757,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ "async-trait", - "jsonrpsee 0.16.2", + "jsonrpsee", "log", "sc-rpc-api", "serde", @@ -12860,7 +12769,7 @@ name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" dependencies = [ - "jsonrpsee 0.16.2", + "jsonrpsee", "log", "parity-scale-codec", "sc-client-api", @@ -12919,7 +12828,7 @@ dependencies = [ "getrandom 0.2.8", "hex", "impl-serde", - "jsonrpsee 0.16.2", + "jsonrpsee", "parity-scale-codec", "parking_lot 0.12.1", "primitive-types", @@ -12947,7 +12856,7 @@ dependencies = [ "frame-metadata", "heck 0.4.1", "hex", - "jsonrpsee 0.16.2", + "jsonrpsee", "parity-scale-codec", "proc-macro-error", "proc-macro2 1.0.51", @@ -13016,9 +12925,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727220a596b4ca0af040a07091e49f5c105ec8f2592674339a5bf35be592f76e" +checksum = "38a81bbc26c485910df47772df6bbcdb417036132caa9e51e29d2e39c4636d4e" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys", @@ -13064,16 +12973,15 @@ checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" [[package]] name = "tempfile" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" dependencies = [ "cfg-if 1.0.0", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix 0.36.8", + "windows-sys 0.42.0", ] [[package]] @@ -13191,9 +13099,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.17" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" dependencies = [ "itoa", "libc", @@ -13211,9 +13119,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" dependencies = [ "time-core", ] @@ -13533,9 +13441,9 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3390c0409daaa6027d6681393316f4ccd3ff82e1590a1e4725014e3ae2bf1920" +checksum = "879380c0061b165ba1f036325b7f3478bc1a947814d9fc36d22c5d8e960b11bd" dependencies = [ "hash-db", "hashbrown 0.13.2", @@ -13666,9 +13574,9 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "digest 0.10.6", - "rand 0.8.5", + "rand 0.7.3", "static_assertions", ] @@ -14291,7 +14199,7 @@ dependencies = [ "sha2 0.10.6", "stun", "thiserror", - "time 0.3.17", + "time 0.3.20", "tokio", "turn", "url", @@ -14767,7 +14675,7 @@ dependencies = [ "ring", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.20", ] [[package]] @@ -14785,7 +14693,7 @@ dependencies = [ "oid-registry 0.6.1", "rusticata-macros", "thiserror", - "time 0.3.17", + "time 0.3.20", ] [[package]] @@ -14881,7 +14789,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aed2e7a52e3744ab4d0c05c20aa065258e84c49fd4226f5191b2ed29712710b4" dependencies = [ - "time 0.3.17", + "time 0.3.20", ] [[package]] diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index 369bea2b1fa..3e46d6ba15a 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -33,6 +33,8 @@ pub mod rialto_parachain_messages; pub mod xcm_config; use bp_parachains::SingleParaStoredHeaderDataBuilder; +#[cfg(feature = "runtime-benchmarks")] +use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::HeaderId; use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, @@ -389,7 +391,7 @@ impl pallet_bridge_relayers::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reward = Balance; type PaymentProcedure = - bp_relayers::PayLaneRewardFromAccount, AccountId>; + bp_relayers::PayRewardFromAccount, AccountId>; type WeightInfo = (); } @@ -449,8 +451,8 @@ impl pallet_bridge_messages::Config for Runtime { type LaneMessageVerifier = crate::rialto_messages::ToRialtoMessageVerifier; type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< Runtime, + WithRialtoMessagesInstance, frame_support::traits::ConstU64<100_000>, - frame_support::traits::ConstU64<10_000>, >; type SourceHeaderChain = crate::rialto_messages::RialtoAsSourceHeaderChain; @@ -480,8 +482,8 @@ impl pallet_bridge_messages::Config for Run type LaneMessageVerifier = crate::rialto_parachain_messages::ToRialtoParachainMessageVerifier; type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< Runtime, + WithRialtoParachainMessagesInstance, frame_support::traits::ConstU64<100_000>, - frame_support::traits::ConstU64<10_000>, >; type SourceHeaderChain = crate::rialto_parachain_messages::RialtoParachainAsSourceHeaderChain; @@ -997,7 +999,14 @@ impl_runtime_apis! { } fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { - pallet_bridge_relayers::Pallet::::relayer_reward(relayer, &Self::bench_lane_id()).is_some() + use bridge_runtime_common::messages::MessageBridge; + + let lane = Self::bench_lane_id(); + let bridged_chain_id = WithRialtoMessageBridge::BRIDGED_CHAIN_ID; + pallet_bridge_relayers::Pallet::::relayer_reward( + relayer, + RewardsAccountParams::new(lane, bridged_chain_id, RewardsAccountOwner::BridgedChain) + ).is_some() } } @@ -1027,15 +1036,15 @@ impl_runtime_apis! { impl RelayersConfig for Runtime { fn prepare_environment( - lane: bp_messages::LaneId, + account_params: RewardsAccountParams, reward: Balance, ) { use frame_support::traits::fungible::Mutate; - let lane_rewards_account = bp_relayers::PayLaneRewardFromAccount::< + let rewards_account = bp_relayers::PayRewardFromAccount::< Balances, AccountId - >::lane_rewards_account(lane); - Balances::mint_into(&lane_rewards_account, reward).unwrap(); + >::rewards_account(account_params); + Balances::mint_into(&rewards_account, reward).unwrap(); } } diff --git a/bin/rialto-parachain/runtime/src/lib.rs b/bin/rialto-parachain/runtime/src/lib.rs index 5eeddf41e52..593c7b21228 100644 --- a/bin/rialto-parachain/runtime/src/lib.rs +++ b/bin/rialto-parachain/runtime/src/lib.rs @@ -521,7 +521,7 @@ impl pallet_bridge_relayers::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reward = Balance; type PaymentProcedure = - bp_relayers::PayLaneRewardFromAccount, AccountId>; + bp_relayers::PayRewardFromAccount, AccountId>; type WeightInfo = (); } @@ -569,7 +569,7 @@ impl pallet_bridge_messages::Config for Runtime { type LaneMessageVerifier = crate::millau_messages::ToMillauMessageVerifier; type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< Runtime, - frame_support::traits::ConstU128<100_000>, + WithMillauMessagesInstance, frame_support::traits::ConstU128<100_000>, >; diff --git a/bin/rialto/runtime/src/lib.rs b/bin/rialto/runtime/src/lib.rs index 3959dbea1aa..9873a6fd855 100644 --- a/bin/rialto/runtime/src/lib.rs +++ b/bin/rialto/runtime/src/lib.rs @@ -393,7 +393,7 @@ impl pallet_bridge_relayers::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reward = Balance; type PaymentProcedure = - bp_relayers::PayLaneRewardFromAccount, AccountId>; + bp_relayers::PayRewardFromAccount, AccountId>; type WeightInfo = (); } @@ -443,7 +443,7 @@ impl pallet_bridge_messages::Config for Runtime { type LaneMessageVerifier = crate::millau_messages::ToMillauMessageVerifier; type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< Runtime, - frame_support::traits::ConstU128<100_000>, + WithMillauMessagesInstance, frame_support::traits::ConstU128<100_000>, >; diff --git a/bin/runtime-common/Cargo.toml b/bin/runtime-common/Cargo.toml index 27e305751e4..dde9e70f576 100644 --- a/bin/runtime-common/Cargo.toml +++ b/bin/runtime-common/Cargo.toml @@ -19,6 +19,7 @@ bp-header-chain = { path = "../../primitives/header-chain", default-features = f bp-messages = { path = "../../primitives/messages", default-features = false } bp-parachains = { path = "../../primitives/parachains", default-features = false } bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-relayers = { path = "../../primitives/relayers", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false } pallet-bridge-grandpa = { path = "../../modules/grandpa", default-features = false } pallet-bridge-messages = { path = "../../modules/messages", default-features = false } diff --git a/bin/runtime-common/src/lib.rs b/bin/runtime-common/src/lib.rs index 32ea500db3e..d9c87049077 100644 --- a/bin/runtime-common/src/lib.rs +++ b/bin/runtime-common/src/lib.rs @@ -77,8 +77,7 @@ where /// even honest relayers may lose their funds if there are multiple relays running and /// submitting the same messages/confirmations. fn validate(call: &T::RuntimeCall) -> TransactionValidity { - call.check_obsolete_receive_messages_proof()?; - call.check_obsolete_receive_messages_delivery_proof() + call.check_obsolete_call() } } diff --git a/bin/runtime-common/src/messages_call_ext.rs b/bin/runtime-common/src/messages_call_ext.rs index 740d17129c8..588afad106d 100644 --- a/bin/runtime-common/src/messages_call_ext.rs +++ b/bin/runtime-common/src/messages_call_ext.rs @@ -22,25 +22,57 @@ use frame_support::{dispatch::CallableCallFor, traits::IsSubType, RuntimeDebug}; use pallet_bridge_messages::{Config, Pallet}; use sp_runtime::transaction_validity::TransactionValidity; -/// Info about a `ReceiveMessagesProof` call which tries to update a single lane. +/// Generic info about a messages delivery/confirmation proof. #[derive(PartialEq, RuntimeDebug)] -pub struct ReceiveMessagesProofInfo { +pub struct BaseMessagesProofInfo { pub lane_id: LaneId, - pub best_proof_nonce: MessageNonce, + pub best_bundled_nonce: MessageNonce, pub best_stored_nonce: MessageNonce, } -/// Helper struct that provides methods for working with the `ReceiveMessagesProof` call. -pub struct ReceiveMessagesProofHelper, I: 'static> { +impl BaseMessagesProofInfo { + fn is_obsolete(&self) -> bool { + self.best_bundled_nonce <= self.best_stored_nonce + } +} + +/// Info about a `ReceiveMessagesProof` call which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub struct ReceiveMessagesProofInfo(pub BaseMessagesProofInfo); + +/// Info about a `ReceiveMessagesDeliveryProof` call which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub struct ReceiveMessagesDeliveryProofInfo(pub BaseMessagesProofInfo); + +/// Info about a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call +/// which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub enum CallInfo { + ReceiveMessagesProof(ReceiveMessagesProofInfo), + ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo), +} + +/// Helper struct that provides methods for working with a call supported by `CallInfo`. +pub struct CallHelper, I: 'static> { pub _phantom_data: sp_std::marker::PhantomData<(T, I)>, } -impl, I: 'static> ReceiveMessagesProofHelper { - /// Check if the `ReceiveMessagesProof` call delivered at least some of the messages that - /// it contained. - pub fn was_partially_successful(info: &ReceiveMessagesProofInfo) -> bool { - let inbound_lane_data = pallet_bridge_messages::InboundLanes::::get(info.lane_id); - inbound_lane_data.last_delivered_nonce() > info.best_stored_nonce +impl, I: 'static> CallHelper { + /// Check if a call delivered proof/confirmation for at least some of the messages that it + /// contained. + pub fn was_partially_successful(info: &CallInfo) -> bool { + match info { + CallInfo::ReceiveMessagesProof(info) => { + let inbound_lane_data = + pallet_bridge_messages::InboundLanes::::get(info.0.lane_id); + inbound_lane_data.last_delivered_nonce() > info.0.best_stored_nonce + }, + CallInfo::ReceiveMessagesDeliveryProof(info) => { + let outbound_lane_data = + pallet_bridge_messages::OutboundLanes::::get(info.0.lane_id); + outbound_lane_data.latest_received_nonce > info.0.best_stored_nonce + }, + } } } @@ -51,17 +83,21 @@ pub trait MessagesCallSubType, I: 'static>: /// Create a new instance of `ReceiveMessagesProofInfo` from a `ReceiveMessagesProof` call. fn receive_messages_proof_info(&self) -> Option; - /// Create a new instance of `ReceiveMessagesProofInfo` from a `ReceiveMessagesProof` call, - /// if the call is for the provided lane. - fn receive_messages_proof_info_for(&self, lane_id: LaneId) -> Option; + /// Create a new instance of `ReceiveMessagesDeliveryProofInfo` from + /// a `ReceiveMessagesDeliveryProof` call. + fn receive_messages_delivery_proof_info(&self) -> Option; + + /// Create a new instance of `CallInfo` from a `ReceiveMessagesProof` + /// or a `ReceiveMessagesDeliveryProof` call. + fn call_info(&self) -> Option; - /// Check that a `ReceiveMessagesProof` call is trying to deliver at least some messages that - /// are better than the ones we know of. - fn check_obsolete_receive_messages_proof(&self) -> TransactionValidity; + /// Create a new instance of `CallInfo` from a `ReceiveMessagesProof` + /// or a `ReceiveMessagesDeliveryProof` call, if the call is for the provided lane. + fn call_info_for(&self, lane_id: LaneId) -> Option; - /// Check that a `ReceiveMessagesDeliveryProof` call is trying to deliver at least some message - /// confirmations that are better than the ones we know of. - fn check_obsolete_receive_messages_delivery_proof(&self) -> TransactionValidity; + /// Check that a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call is trying + /// to deliver/confirm at least some messages that are better than the ones we know of. + fn check_obsolete_call(&self) -> TransactionValidity; } impl< @@ -88,40 +124,17 @@ impl< { let inbound_lane_data = pallet_bridge_messages::InboundLanes::::get(proof.lane); - return Some(ReceiveMessagesProofInfo { + return Some(ReceiveMessagesProofInfo(BaseMessagesProofInfo { lane_id: proof.lane, - best_proof_nonce: proof.nonces_end, + best_bundled_nonce: proof.nonces_end, best_stored_nonce: inbound_lane_data.last_delivered_nonce(), - }) + })) } None } - fn receive_messages_proof_info_for(&self, lane_id: LaneId) -> Option { - self.receive_messages_proof_info().filter(|info| info.lane_id == lane_id) - } - - fn check_obsolete_receive_messages_proof(&self) -> TransactionValidity { - if let Some(proof_info) = self.receive_messages_proof_info() { - if proof_info.best_proof_nonce <= proof_info.best_stored_nonce { - log::trace!( - target: pallet_bridge_messages::LOG_TARGET, - "Rejecting obsolete messages delivery transaction: \ - lane {:?}, bundled {:?}, best {:?}", - proof_info.lane_id, - proof_info.best_proof_nonce, - proof_info.best_stored_nonce, - ); - - return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() - } - } - - Ok(sp_runtime::transaction_validity::ValidTransaction::default()) - } - - fn check_obsolete_receive_messages_delivery_proof(&self) -> TransactionValidity { + fn receive_messages_delivery_proof_info(&self) -> Option { if let Some(pallet_bridge_messages::Call::::receive_messages_delivery_proof { ref proof, ref relayers_state, @@ -129,18 +142,62 @@ impl< }) = self.is_sub_type() { let outbound_lane_data = pallet_bridge_messages::OutboundLanes::::get(proof.lane); - if relayers_state.last_delivered_nonce <= outbound_lane_data.latest_received_nonce { + + return Some(ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { + lane_id: proof.lane, + best_bundled_nonce: relayers_state.last_delivered_nonce, + best_stored_nonce: outbound_lane_data.latest_received_nonce, + })) + } + + None + } + + fn call_info(&self) -> Option { + if let Some(info) = self.receive_messages_proof_info() { + return Some(CallInfo::ReceiveMessagesProof(info)) + } + + if let Some(info) = self.receive_messages_delivery_proof_info() { + return Some(CallInfo::ReceiveMessagesDeliveryProof(info)) + } + + None + } + + fn call_info_for(&self, lane_id: LaneId) -> Option { + self.call_info().filter(|info| { + let actual_lane_id = match info { + CallInfo::ReceiveMessagesProof(info) => info.0.lane_id, + CallInfo::ReceiveMessagesDeliveryProof(info) => info.0.lane_id, + }; + actual_lane_id == lane_id + }) + } + + fn check_obsolete_call(&self) -> TransactionValidity { + match self.call_info() { + Some(CallInfo::ReceiveMessagesProof(proof_info)) if proof_info.0.is_obsolete() => { log::trace!( target: pallet_bridge_messages::LOG_TARGET, - "Rejecting obsolete messages confirmation transaction: \ - lane {:?}, bundled {:?}, best {:?}", - proof.lane, - relayers_state.last_delivered_nonce, - outbound_lane_data.latest_received_nonce, + "Rejecting obsolete messages delivery transaction: {:?}", + proof_info ); return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() - } + }, + Some(CallInfo::ReceiveMessagesDeliveryProof(proof_info)) + if proof_info.0.is_obsolete() => + { + log::trace!( + target: pallet_bridge_messages::LOG_TARGET, + "Rejecting obsolete messages confirmation transaction: {:?}", + proof_info, + ); + + return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() + }, + _ => {}, } Ok(sp_runtime::transaction_validity::ValidTransaction::default()) @@ -153,8 +210,8 @@ mod tests { messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }, + messages_call_ext::MessagesCallSubType, mock::{TestRuntime, ThisChainRuntimeCall}, - BridgeRuntimeFilterCall, }; use bp_messages::UnrewardedRelayersState; @@ -169,22 +226,21 @@ mod tests { nonces_start: bp_messages::MessageNonce, nonces_end: bp_messages::MessageNonce, ) -> bool { - pallet_bridge_messages::Pallet::::validate( - &ThisChainRuntimeCall::BridgeMessages( - pallet_bridge_messages::Call::::receive_messages_proof { - relayer_id_at_bridged_chain: 42, - messages_count: (nonces_end - nonces_start + 1) as u32, - dispatch_weight: frame_support::weights::Weight::zero(), - proof: FromBridgedChainMessagesProof { - bridged_header_hash: Default::default(), - storage_proof: vec![], - lane: bp_messages::LaneId([0, 0, 0, 0]), - nonces_start, - nonces_end, - }, + ThisChainRuntimeCall::BridgeMessages( + pallet_bridge_messages::Call::::receive_messages_proof { + relayer_id_at_bridged_chain: 42, + messages_count: (nonces_end - nonces_start + 1) as u32, + dispatch_weight: frame_support::weights::Weight::zero(), + proof: FromBridgedChainMessagesProof { + bridged_header_hash: Default::default(), + storage_proof: vec![], + lane: bp_messages::LaneId([0, 0, 0, 0]), + nonces_start, + nonces_end, }, - ), + }, ) + .check_obsolete_call() .is_ok() } @@ -230,21 +286,20 @@ mod tests { } fn validate_message_confirmation(last_delivered_nonce: bp_messages::MessageNonce) -> bool { - pallet_bridge_messages::Pallet::::validate( - &ThisChainRuntimeCall::BridgeMessages( - pallet_bridge_messages::Call::::receive_messages_delivery_proof { - proof: FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: Default::default(), - storage_proof: Vec::new(), - lane: bp_messages::LaneId([0, 0, 0, 0]), - }, - relayers_state: UnrewardedRelayersState { - last_delivered_nonce, - ..Default::default() - }, + ThisChainRuntimeCall::BridgeMessages( + pallet_bridge_messages::Call::::receive_messages_delivery_proof { + proof: FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: Default::default(), + storage_proof: Vec::new(), + lane: bp_messages::LaneId([0, 0, 0, 0]), + }, + relayers_state: UnrewardedRelayersState { + last_delivered_nonce, + ..Default::default() }, - ), + }, ) + .check_obsolete_call() .is_ok() } diff --git a/bin/runtime-common/src/mock.rs b/bin/runtime-common/src/mock.rs index a5ae9131f03..c2cd8e9ba83 100644 --- a/bin/runtime-common/src/mock.rs +++ b/bin/runtime-common/src/mock.rs @@ -85,6 +85,8 @@ pub type BridgedChainHeader = /// Message lane used in tests. pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]); +/// Bridged chain id used in tests. +pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg"; /// Maximal number of queued messages at the test lane. pub const MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE: MessageNonce = 32; /// Minimal extrinsic weight at the `BridgedChain`. @@ -118,7 +120,7 @@ crate::generate_bridge_reject_obsolete_headers_and_messages! { parameter_types! { pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID]; - pub const BridgedChainId: ChainId = *b"brdg"; + pub const BridgedChainId: ChainId = TEST_BRIDGED_CHAIN_ID; pub const BridgedParasPalletName: &'static str = "Paras"; pub const ExistentialDeposit: ThisChainBalance = 500; pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; @@ -227,8 +229,8 @@ impl pallet_bridge_messages::Config for TestRuntime { type LaneMessageVerifier = FromThisChainMessageVerifier; type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< TestRuntime, - frame_support::traits::ConstU64<100_000>, - frame_support::traits::ConstU64<10_000>, + (), + ConstU64<100_000>, >; type SourceHeaderChain = SourceHeaderChainAdapter; @@ -251,7 +253,7 @@ pub struct OnThisChainBridge; impl MessageBridge for OnThisChainBridge { const THIS_CHAIN_ID: ChainId = *b"this"; - const BRIDGED_CHAIN_ID: ChainId = *b"brdg"; + const BRIDGED_CHAIN_ID: ChainId = TEST_BRIDGED_CHAIN_ID; const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; type ThisChain = ThisChain; @@ -265,7 +267,7 @@ impl MessageBridge for OnThisChainBridge { pub struct OnBridgedChainBridge; impl MessageBridge for OnBridgedChainBridge { - const THIS_CHAIN_ID: ChainId = *b"brdg"; + const THIS_CHAIN_ID: ChainId = TEST_BRIDGED_CHAIN_ID; const BRIDGED_CHAIN_ID: ChainId = *b"this"; const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; diff --git a/bin/runtime-common/src/refund_relayer_extension.rs b/bin/runtime-common/src/refund_relayer_extension.rs index df4eae6f737..0a61f4fdd02 100644 --- a/bin/runtime-common/src/refund_relayer_extension.rs +++ b/bin/runtime-common/src/refund_relayer_extension.rs @@ -20,9 +20,10 @@ //! (parachain or relay chain). use crate::messages_call_ext::{ - MessagesCallSubType, ReceiveMessagesProofHelper, ReceiveMessagesProofInfo, + CallHelper as MessagesCallHelper, CallInfo as MessagesCallInfo, MessagesCallSubType, }; use bp_messages::LaneId; +use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::StaticStrProvider; use codec::{Decode, Encode}; use frame_support::{ @@ -142,23 +143,23 @@ pub struct PreDispatchData { /// Type of the call that the extension recognizes. #[derive(RuntimeDebugNoBound, PartialEq)] pub enum CallInfo { - /// Relay chain finality + parachain finality + message delivery calls. - AllFinalityAndDelivery( + /// Relay chain finality + parachain finality + message delivery/confirmation calls. + AllFinalityAndMsgs( SubmitFinalityProofInfo, SubmitParachainHeadsInfo, - ReceiveMessagesProofInfo, + MessagesCallInfo, ), - /// Parachain finality + message delivery calls. - ParachainFinalityAndDelivery(SubmitParachainHeadsInfo, ReceiveMessagesProofInfo), - /// Standalone message delivery call. - Delivery(ReceiveMessagesProofInfo), + /// Parachain finality + message delivery/confirmation calls. + ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo), + /// Standalone message delivery/confirmation call. + Msgs(MessagesCallInfo), } impl CallInfo { /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. fn submit_finality_proof_info(&self) -> Option> { match *self { - Self::AllFinalityAndDelivery(info, _, _) => Some(info), + Self::AllFinalityAndMsgs(info, _, _) => Some(info), _ => None, } } @@ -166,18 +167,18 @@ impl CallInfo { /// Returns the pre-dispatch `SubmitParachainHeadsInfo`. fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> { match self { - Self::AllFinalityAndDelivery(_, info, _) => Some(info), - Self::ParachainFinalityAndDelivery(info, _) => Some(info), + Self::AllFinalityAndMsgs(_, info, _) => Some(info), + Self::ParachainFinalityAndMsgs(info, _) => Some(info), _ => None, } } /// Returns the pre-dispatch `ReceiveMessagesProofInfo`. - fn receive_messages_proof_info(&self) -> &ReceiveMessagesProofInfo { + fn messages_call_info(&self) -> &MessagesCallInfo { match self { - Self::AllFinalityAndDelivery(_, _, info) => info, - Self::ParachainFinalityAndDelivery(_, info) => info, - Self::Delivery(info) => info, + Self::AllFinalityAndMsgs(_, _, info) => info, + Self::ParachainFinalityAndMsgs(_, info) => info, + Self::Msgs(info) => info, } } } @@ -268,7 +269,7 @@ where for nested_call in calls { nested_call.check_obsolete_submit_finality_proof()?; nested_call.check_obsolete_submit_parachain_heads()?; - nested_call.check_obsolete_receive_messages_proof()?; + nested_call.check_obsolete_call()?; } } @@ -289,18 +290,16 @@ where let parse_call = || { let mut calls = self.expand_call(call)?.into_iter(); match calls.len() { - 3 => Some(CallInfo::AllFinalityAndDelivery( + 3 => Some(CallInfo::AllFinalityAndMsgs( calls.next()?.submit_finality_proof_info()?, calls.next()?.submit_parachain_heads_info_for(Para::Id::get())?, - calls.next()?.receive_messages_proof_info_for(Msgs::Id::get())?, + calls.next()?.call_info_for(Msgs::Id::get())?, )), - 2 => Some(CallInfo::ParachainFinalityAndDelivery( + 2 => Some(CallInfo::ParachainFinalityAndMsgs( calls.next()?.submit_parachain_heads_info_for(Para::Id::get())?, - calls.next()?.receive_messages_proof_info_for(Msgs::Id::get())?, - )), - 1 => Some(CallInfo::Delivery( - calls.next()?.receive_messages_proof_info_for(Msgs::Id::get())?, + calls.next()?.call_info_for(Msgs::Id::get())?, )), + 1 => Some(CallInfo::Msgs(calls.next()?.call_info_for(Msgs::Id::get())?)), _ => None, } }; @@ -373,10 +372,9 @@ where // Check if the `ReceiveMessagesProof` call delivered at least some of the messages that // it contained. If this happens, we consider the transaction "helpful" and refund it. - let msgs_proof_info = call_info.receive_messages_proof_info(); - if !ReceiveMessagesProofHelper::::was_partially_successful( - msgs_proof_info, - ) { + let msgs_call_info = call_info.messages_call_info(); + if !MessagesCallHelper::::was_partially_successful(msgs_call_info) + { return Ok(()) } @@ -397,7 +395,19 @@ where let refund = Refund::compute_refund(info, &post_info, post_info_len, tip); // finally - register refund in relayers pallet - RelayersPallet::::register_relayer_reward(Msgs::Id::get(), &relayer, refund); + let rewards_account_owner = match msgs_call_info { + MessagesCallInfo::ReceiveMessagesProof(_) => RewardsAccountOwner::ThisChain, + MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => RewardsAccountOwner::BridgedChain, + }; + RelayersPallet::::register_relayer_reward( + RewardsAccountParams::new( + Msgs::Id::get(), + Runtime::BridgedChainId::get(), + rewards_account_owner, + ), + &relayer, + refund, + ); log::trace!( target: "runtime::bridge", @@ -416,8 +426,16 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{messages::target::FromBridgedChainMessagesProof, mock::*}; - use bp_messages::{InboundLaneData, MessageNonce}; + use crate::{ + messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, + }, + messages_call_ext::{ + BaseMessagesProofInfo, ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, + }, + mock::*, + }; + use bp_messages::{InboundLaneData, MessageNonce, OutboundLaneData, UnrewardedRelayersState}; use bp_parachains::{BestParaHeadHash, ParaInfo}; use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; use bp_runtime::HeaderId; @@ -433,6 +451,8 @@ mod tests { parameter_types! { TestParachain: u32 = 1000; pub TestLaneId: LaneId = TEST_LANE_ID; + pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new(TEST_LANE_ID, TEST_BRIDGED_CHAIN_ID, RewardsAccountOwner::ThisChain); + pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new(TEST_LANE_ID, TEST_BRIDGED_CHAIN_ID, RewardsAccountOwner::BridgedChain); } bp_runtime::generate_static_str_provider!(TestExtension); @@ -456,7 +476,7 @@ mod tests { best_relay_header_number: RelayBlockNumber, parachain_head_at_relay_header_number: RelayBlockNumber, parachain_head_hash: ParaHash, - best_delivered_message: MessageNonce, + best_message: MessageNonce, ) { let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect(); let best_relay_header = HeaderId(best_relay_header_number, RelayBlockHash::default()); @@ -476,9 +496,13 @@ mod tests { pallet_bridge_parachains::ParasInfo::::insert(para_id, para_info); let lane_id = TestLaneId::get(); - let lane_data = - InboundLaneData { last_confirmed_nonce: best_delivered_message, ..Default::default() }; - pallet_bridge_messages::InboundLanes::::insert(lane_id, lane_data); + let in_lane_data = + InboundLaneData { last_confirmed_nonce: best_message, ..Default::default() }; + pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); + + let out_lane_data = + OutboundLaneData { latest_received_nonce: best_message, ..Default::default() }; + pallet_bridge_messages::OutboundLanes::::insert(lane_id, out_lane_data); } fn submit_relay_header_call(relay_header_number: RelayBlockNumber) -> RuntimeCall { @@ -522,6 +546,20 @@ mod tests { }) } + fn message_confirmation_call(best_message: MessageNonce) -> RuntimeCall { + RuntimeCall::BridgeMessages(MessagesCall::receive_messages_delivery_proof { + proof: FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: Default::default(), + storage_proof: vec![], + lane: TestLaneId::get(), + }, + relayers_state: UnrewardedRelayersState { + last_delivered_nonce: best_message, + ..Default::default() + }, + }) + } + fn parachain_finality_and_delivery_batch_call( parachain_head_at_relay_header_number: RelayBlockNumber, best_message: MessageNonce, @@ -534,6 +572,18 @@ mod tests { }) } + fn parachain_finality_and_confirmation_batch_call( + parachain_head_at_relay_header_number: RelayBlockNumber, + best_message: MessageNonce, + ) -> RuntimeCall { + RuntimeCall::Utility(UtilityCall::batch_all { + calls: vec![ + submit_parachain_head_call(parachain_head_at_relay_header_number), + message_confirmation_call(best_message), + ], + }) + } + fn all_finality_and_delivery_batch_call( relay_header_number: RelayBlockNumber, parachain_head_at_relay_header_number: RelayBlockNumber, @@ -548,10 +598,24 @@ mod tests { }) } + fn all_finality_and_confirmation_batch_call( + relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: RelayBlockNumber, + best_message: MessageNonce, + ) -> RuntimeCall { + RuntimeCall::Utility(UtilityCall::batch_all { + calls: vec![ + submit_relay_header_call(relay_header_number), + submit_parachain_head_call(parachain_head_at_relay_header_number), + message_confirmation_call(best_message), + ], + }) + } + fn all_finality_pre_dispatch_data() -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::AllFinalityAndDelivery( + call_info: CallInfo::AllFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, extra_weight: Weight::zero(), @@ -562,11 +626,38 @@ mod tests { para_id: ParaId(TestParachain::get()), para_head_hash: [1u8; 32].into(), }, - ReceiveMessagesProofInfo { - lane_id: TEST_LANE_ID, - best_proof_nonce: 200, - best_stored_nonce: 100, + MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo( + BaseMessagesProofInfo { + lane_id: TEST_LANE_ID, + best_bundled_nonce: 200, + best_stored_nonce: 100, + }, + )), + ), + } + } + + fn all_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + PreDispatchData { + relayer: relayer_account_at_this_chain(), + call_info: CallInfo::AllFinalityAndMsgs( + SubmitFinalityProofInfo { + block_number: 200, + extra_weight: Weight::zero(), + extra_size: 0, + }, + SubmitParachainHeadsInfo { + at_relay_block_number: 200, + para_id: ParaId(TestParachain::get()), + para_head_hash: [1u8; 32].into(), }, + MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( + BaseMessagesProofInfo { + lane_id: TEST_LANE_ID, + best_bundled_nonce: 200, + best_stored_nonce: 100, + }, + )), ), } } @@ -574,17 +665,39 @@ mod tests { fn parachain_finality_pre_dispatch_data() -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::ParachainFinalityAndDelivery( + call_info: CallInfo::ParachainFinalityAndMsgs( SubmitParachainHeadsInfo { at_relay_block_number: 200, para_id: ParaId(TestParachain::get()), para_head_hash: [1u8; 32].into(), }, - ReceiveMessagesProofInfo { - lane_id: TEST_LANE_ID, - best_proof_nonce: 200, - best_stored_nonce: 100, + MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo( + BaseMessagesProofInfo { + lane_id: TEST_LANE_ID, + best_bundled_nonce: 200, + best_stored_nonce: 100, + }, + )), + ), + } + } + + fn parachain_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + PreDispatchData { + relayer: relayer_account_at_this_chain(), + call_info: CallInfo::ParachainFinalityAndMsgs( + SubmitParachainHeadsInfo { + at_relay_block_number: 200, + para_id: ParaId(TestParachain::get()), + para_head_hash: [1u8; 32].into(), }, + MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( + BaseMessagesProofInfo { + lane_id: TEST_LANE_ID, + best_bundled_nonce: 200, + best_stored_nonce: 100, + }, + )), ), } } @@ -592,11 +705,26 @@ mod tests { fn delivery_pre_dispatch_data() -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::Delivery(ReceiveMessagesProofInfo { - lane_id: TEST_LANE_ID, - best_proof_nonce: 200, - best_stored_nonce: 100, - }), + call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesProof( + ReceiveMessagesProofInfo(BaseMessagesProofInfo { + lane_id: TEST_LANE_ID, + best_bundled_nonce: 200, + best_stored_nonce: 100, + }), + )), + } + } + + fn confirmation_pre_dispatch_data() -> PreDispatchData { + PreDispatchData { + relayer: relayer_account_at_this_chain(), + call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesDeliveryProof( + ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { + lane_id: TEST_LANE_ID, + best_bundled_nonce: 200, + best_stored_nonce: 100, + }), + )), } } @@ -659,16 +787,28 @@ mod tests { initialize_environment(100, 100, Default::default(), 100); assert_eq!(run_validate(message_delivery_call(200)), Ok(ValidTransaction::default()),); + assert_eq!( + run_validate(message_confirmation_call(200)), + Ok(ValidTransaction::default()), + ); assert_eq!( run_validate(parachain_finality_and_delivery_batch_call(200, 200)), Ok(ValidTransaction::default()), ); + assert_eq!( + run_validate(parachain_finality_and_confirmation_batch_call(200, 200)), + Ok(ValidTransaction::default()), + ); assert_eq!( run_validate(all_finality_and_delivery_batch_call(200, 200, 200)), Ok(ValidTransaction::default()), ); + assert_eq!( + run_validate(all_finality_and_confirmation_batch_call(200, 200, 200)), + Ok(ValidTransaction::default()), + ); }); } @@ -698,17 +838,15 @@ mod tests { run_pre_dispatch(all_finality_and_delivery_batch_call(101, 100, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); - assert_eq!( - run_pre_dispatch(parachain_finality_and_delivery_batch_call(100, 200)), + run_validate(all_finality_and_delivery_batch_call(101, 100, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); assert_eq!( - run_validate(all_finality_and_delivery_batch_call(101, 100, 200)), + run_pre_dispatch(parachain_finality_and_delivery_batch_call(100, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); - assert_eq!( run_validate(parachain_finality_and_delivery_batch_call(100, 200)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), @@ -725,9 +863,8 @@ mod tests { run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 100)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); - assert_eq!( - run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 100)), + run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 100)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); @@ -735,11 +872,28 @@ mod tests { run_validate(all_finality_and_delivery_batch_call(200, 200, 100)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_validate(all_finality_and_confirmation_batch_call(200, 200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + assert_eq!( + run_pre_dispatch(parachain_finality_and_confirmation_batch_call(200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); assert_eq!( run_validate(parachain_finality_and_delivery_batch_call(200, 100)), Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), ); + assert_eq!( + run_validate(parachain_finality_and_confirmation_batch_call(200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); }); } @@ -752,6 +906,10 @@ mod tests { run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), Ok(Some(all_finality_pre_dispatch_data())), ); + assert_eq!( + run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), + Ok(Some(all_finality_confirmation_pre_dispatch_data())), + ); }); } @@ -764,6 +922,10 @@ mod tests { run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), Ok(Some(parachain_finality_pre_dispatch_data())), ); + assert_eq!( + run_pre_dispatch(parachain_finality_and_confirmation_batch_call(200, 200)), + Ok(Some(parachain_finality_confirmation_pre_dispatch_data())), + ); }); } @@ -791,7 +953,7 @@ mod tests { } #[test] - fn pre_dispatch_parses_message_delivery_transaction() { + fn pre_dispatch_parses_message_transaction() { run_test(|| { initialize_environment(100, 100, Default::default(), 100); @@ -799,6 +961,10 @@ mod tests { run_pre_dispatch(message_delivery_call(200)), Ok(Some(delivery_pre_dispatch_data())), ); + assert_eq!( + run_pre_dispatch(message_confirmation_call(200)), + Ok(Some(confirmation_pre_dispatch_data())), + ); }); } @@ -852,6 +1018,16 @@ mod tests { Ok(()) )); assert_storage_noop!(run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(()))); + + assert_storage_noop!(run_post_dispatch( + Some(all_finality_confirmation_pre_dispatch_data()), + Ok(()) + )); + assert_storage_noop!(run_post_dispatch( + Some(parachain_finality_confirmation_pre_dispatch_data()), + Ok(()) + )); + assert_storage_noop!(run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(()))); }); } @@ -872,7 +1048,7 @@ mod tests { assert_eq!( RelayersPallet::::relayer_reward( relayer_account_at_this_chain(), - TestLaneId::get() + MsgProofsRewardsAccount::get() ), Some(regular_reward), ); @@ -880,7 +1056,7 @@ mod tests { // now repeat the same with size+weight refund: we expect smaller reward let mut pre_dispatch_data = all_finality_pre_dispatch_data(); match pre_dispatch_data.call_info { - CallInfo::AllFinalityAndDelivery(ref mut info, ..) => { + CallInfo::AllFinalityAndMsgs(ref mut info, ..) => { info.extra_weight.set_ref_time( frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, ); @@ -891,7 +1067,7 @@ mod tests { run_post_dispatch(Some(pre_dispatch_data), Ok(())); let reward_after_two_calls = RelayersPallet::::relayer_reward( relayer_account_at_this_chain(), - TestLaneId::get(), + MsgProofsRewardsAccount::get(), ) .unwrap(); assert!( @@ -912,7 +1088,16 @@ mod tests { assert_eq!( RelayersPallet::::relayer_reward( relayer_account_at_this_chain(), - TestLaneId::get() + MsgProofsRewardsAccount::get() + ), + Some(expected_reward()), + ); + + run_post_dispatch(Some(all_finality_confirmation_pre_dispatch_data()), Ok(())); + assert_eq!( + RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + MsgDeliveryProofsRewardsAccount::get() ), Some(expected_reward()), ); @@ -928,7 +1113,16 @@ mod tests { assert_eq!( RelayersPallet::::relayer_reward( relayer_account_at_this_chain(), - TestLaneId::get() + MsgProofsRewardsAccount::get() + ), + Some(expected_reward()), + ); + + run_post_dispatch(Some(parachain_finality_confirmation_pre_dispatch_data()), Ok(())); + assert_eq!( + RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + MsgDeliveryProofsRewardsAccount::get() ), Some(expected_reward()), ); @@ -936,7 +1130,7 @@ mod tests { } #[test] - fn post_dispatch_refunds_relayer_in_message_delivery_transaction() { + fn post_dispatch_refunds_relayer_in_message_transaction() { run_test(|| { initialize_environment(200, 200, Default::default(), 200); @@ -944,7 +1138,16 @@ mod tests { assert_eq!( RelayersPallet::::relayer_reward( relayer_account_at_this_chain(), - TestLaneId::get() + MsgProofsRewardsAccount::get() + ), + Some(expected_reward()), + ); + + run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(())); + assert_eq!( + RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + MsgDeliveryProofsRewardsAccount::get() ), Some(expected_reward()), ); diff --git a/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json index b54bf1b48c5..ca22a6dadd0 100644 --- a/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json +++ b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json @@ -111,7 +111,7 @@ }, { "exemplar": true, - "expr": "at_RialtoParachain_relay_MillauMessages_reward_for_lane_00000000_with_Millau{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "expr": "at_RialtoParachain_relay_MillauMessages_reward_for_msgs_from_Millau_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"} + at_RialtoParachain_relay_MillauMessages_reward_for_msgs_to_Millau_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"}", "hide": false, "instant": false, "interval": "", @@ -120,7 +120,7 @@ }, { "exemplar": true, - "expr": "at_RialtoParachain_relay_MillauMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"} + at_RialtoParachain_relay_MillauMessages_reward_for_lane_00000000_with_Millau{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "expr": "at_RialtoParachain_relay_MillauMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"} + at_RialtoParachain_relay_MillauMessages_reward_for_msgs_from_Millau_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"} + at_RialtoParachain_relay_MillauMessages_reward_for_msgs_to_Millau_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"}", "hide": false, "interval": "", "legendFormat": "With-Millau relay account total balance (balance + reward)", @@ -262,7 +262,7 @@ }, { "exemplar": true, - "expr": "at_Millau_relay_RialtoParachainMessages_reward_for_lane_00000000_with_RialtoParachain{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "expr": "at_Millau_relay_RialtoParachainMessages_reward_for_msgs_from_RialtoParachain_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"} + at_Millau_relay_RialtoParachainMessages_reward_for_msgs_to_RialtoParachain_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"}", "hide": false, "interval": "", "legendFormat": "With-Rialto relay account reward", @@ -270,7 +270,7 @@ }, { "exemplar": true, - "expr": "at_Millau_relay_RialtoParachainMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"} + \nat_Millau_relay_RialtoParachainMessages_reward_for_lane_00000000_with_RialtoParachain{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "expr": "at_Millau_relay_RialtoParachainMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"} + at_Millau_relay_RialtoParachainMessages_reward_for_msgs_from_RialtoParachain_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"} + at_Millau_relay_RialtoParachainMessages_reward_for_msgs_to_RialtoParachain_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"}", "hide": false, "interval": "", "legendFormat": "With-Rialto relay account total balance (balance + reward)", diff --git a/docs/dockerhub-bridges-common-relay.README.md b/docs/dockerhub-bridges-common-relay.README.md new file mode 100644 index 00000000000..d199227c9c6 --- /dev/null +++ b/docs/dockerhub-bridges-common-relay.README.md @@ -0,0 +1 @@ +# bridges-common-relay diff --git a/docs/dockerhub-millau-bridge-node.README.md b/docs/dockerhub-millau-bridge-node.README.md new file mode 100644 index 00000000000..481ad46b7c6 --- /dev/null +++ b/docs/dockerhub-millau-bridge-node.README.md @@ -0,0 +1 @@ +# millau-bridge-node diff --git a/docs/dockerhub-rialto-bridge-node.README.md b/docs/dockerhub-rialto-bridge-node.README.md new file mode 100644 index 00000000000..2393e6f8129 --- /dev/null +++ b/docs/dockerhub-rialto-bridge-node.README.md @@ -0,0 +1 @@ +# rialto-bridge-node diff --git a/docs/dockerhub-rialto-parachain-collator.README.md b/docs/dockerhub-rialto-parachain-collator.README.md new file mode 100644 index 00000000000..a09f6b1561b --- /dev/null +++ b/docs/dockerhub-rialto-parachain-collator.README.md @@ -0,0 +1 @@ +# rialto-parachain-collator diff --git a/docs/dockerhub-substrate-relay.README.md b/docs/dockerhub-substrate-relay.README.md new file mode 100644 index 00000000000..1a9f22c425c --- /dev/null +++ b/docs/dockerhub-substrate-relay.README.md @@ -0,0 +1 @@ +# substrate-relay diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index e94d91a5c16..4304e82eeb1 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -181,7 +181,7 @@ pub mod pallet { let is_authorities_change_enacted = try_enact_authority_change::(&finality_target, set_id)?; let may_refund_call_fee = is_authorities_change_enacted && - submit_finality_proof_info_from_args::(&*finality_target, &justification) + submit_finality_proof_info_from_args::(&finality_target, &justification) .fits_limits(); >::mutate(|count| *count += 1); insert_header::(*finality_target, hash); diff --git a/modules/relayers/Cargo.toml b/modules/relayers/Cargo.toml index 954818a3b50..200fbbca309 100644 --- a/modules/relayers/Cargo.toml +++ b/modules/relayers/Cargo.toml @@ -16,6 +16,7 @@ scale-info = { version = "2.1.1", default-features = false, features = ["derive" bp-messages = { path = "../../primitives/messages", default-features = false } bp-relayers = { path = "../../primitives/relayers", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false } +pallet-bridge-messages = { path = "../messages", default-features = false } # Substrate Dependencies diff --git a/modules/relayers/src/benchmarking.rs b/modules/relayers/src/benchmarking.rs index 7195b316bb6..7dcf77eb958 100644 --- a/modules/relayers/src/benchmarking.rs +++ b/modules/relayers/src/benchmarking.rs @@ -20,6 +20,8 @@ use crate::*; +use bp_messages::LaneId; +use bp_relayers::RewardsAccountOwner; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_system::RawOrigin; @@ -32,19 +34,21 @@ pub struct Pallet(crate::Pallet); /// Trait that must be implemented by runtime. pub trait Config: crate::Config { /// Prepare environment for paying given reward for serving given lane. - fn prepare_environment(lane: LaneId, reward: Self::Reward); + fn prepare_environment(account_params: RewardsAccountParams, reward: Self::Reward); } benchmarks! { // Benchmark `claim_rewards` call. claim_rewards { let lane = LaneId([0, 0, 0, 0]); + let account_params = + RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); let relayer: T::AccountId = whitelisted_caller(); let reward = T::Reward::from(REWARD_AMOUNT); - T::prepare_environment(lane, reward); - RelayerRewards::::insert(&relayer, lane, reward); - }: _(RawOrigin::Signed(relayer), lane) + T::prepare_environment(account_params, reward); + RelayerRewards::::insert(&relayer, account_params, reward); + }: _(RawOrigin::Signed(relayer), account_params) verify { // we can't check anything here, because `PaymentProcedure` is responsible for // payment logic, so we assume that if call has succeeded, the procedure has diff --git a/modules/relayers/src/lib.rs b/modules/relayers/src/lib.rs index 2ceebd0336e..3d32e3c7a14 100644 --- a/modules/relayers/src/lib.rs +++ b/modules/relayers/src/lib.rs @@ -20,8 +20,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs)] -use bp_messages::LaneId; -use bp_relayers::{PaymentProcedure, RelayerRewardsKeyProvider}; +use bp_relayers::{PaymentProcedure, RelayerRewardsKeyProvider, RewardsAccountParams}; use bp_runtime::StorageDoubleMapKeyProvider; use frame_support::sp_runtime::Saturating; use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero}; @@ -72,28 +71,32 @@ pub mod pallet { /// Claim accumulated rewards. #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::claim_rewards())] - pub fn claim_rewards(origin: OriginFor, lane_id: LaneId) -> DispatchResult { + pub fn claim_rewards( + origin: OriginFor, + rewards_account_params: RewardsAccountParams, + ) -> DispatchResult { let relayer = ensure_signed(origin)?; RelayerRewards::::try_mutate_exists( &relayer, - lane_id, + rewards_account_params, |maybe_reward| -> DispatchResult { let reward = maybe_reward.take().ok_or(Error::::NoRewardForRelayer)?; - T::PaymentProcedure::pay_reward(&relayer, lane_id, reward).map_err(|e| { - log::trace!( - target: LOG_TARGET, - "Failed to pay {:?} rewards to {:?}: {:?}", - lane_id, - relayer, - e, - ); - Error::::FailedToPayReward - })?; + T::PaymentProcedure::pay_reward(&relayer, rewards_account_params, reward) + .map_err(|e| { + log::trace!( + target: LOG_TARGET, + "Failed to pay {:?} rewards to {:?}: {:?}", + rewards_account_params, + relayer, + e, + ); + Error::::FailedToPayReward + })?; Self::deposit_event(Event::::RewardPaid { relayer: relayer.clone(), - lane_id, + rewards_account_params, reward, }); Ok(()) @@ -104,23 +107,31 @@ pub mod pallet { impl Pallet { /// Register reward for given relayer. - pub fn register_relayer_reward(lane_id: LaneId, relayer: &T::AccountId, reward: T::Reward) { + pub fn register_relayer_reward( + rewards_account_params: RewardsAccountParams, + relayer: &T::AccountId, + reward: T::Reward, + ) { if reward.is_zero() { return } - RelayerRewards::::mutate(relayer, lane_id, |old_reward: &mut Option| { - let new_reward = old_reward.unwrap_or_else(Zero::zero).saturating_add(reward); - *old_reward = Some(new_reward); - - log::trace!( - target: crate::LOG_TARGET, - "Relayer {:?} can now claim reward for serving lane {:?}: {:?}", - relayer, - lane_id, - new_reward, - ); - }); + RelayerRewards::::mutate( + relayer, + rewards_account_params, + |old_reward: &mut Option| { + let new_reward = old_reward.unwrap_or_else(Zero::zero).saturating_add(reward); + *old_reward = Some(new_reward); + + log::trace!( + target: crate::LOG_TARGET, + "Relayer {:?} can now claim reward for serving payer {:?}: {:?}", + relayer, + rewards_account_params, + new_reward, + ); + }, + ); } } @@ -131,8 +142,8 @@ pub mod pallet { RewardPaid { /// Relayer account that has been rewarded. relayer: T::AccountId, - /// Relayer has received reward for serving this lane. - lane_id: LaneId, + /// Relayer has received reward from this account. + rewards_account_params: RewardsAccountParams, /// Reward amount. reward: T::Reward, }, @@ -166,6 +177,8 @@ mod tests { use mock::{RuntimeEvent as TestEvent, *}; use crate::Event::RewardPaid; + use bp_messages::LaneId; + use bp_relayers::RewardsAccountOwner; use frame_support::{ assert_noop, assert_ok, traits::fungible::{Inspect, Mutate}, @@ -182,7 +195,10 @@ mod tests { fn root_cant_claim_anything() { run_test(|| { assert_noop!( - Pallet::::claim_rewards(RuntimeOrigin::root(), TEST_LANE_ID), + Pallet::::claim_rewards( + RuntimeOrigin::root(), + TEST_REWARDS_ACCOUNT_PARAMS + ), DispatchError::BadOrigin, ); }); @@ -194,7 +210,7 @@ mod tests { assert_noop!( Pallet::::claim_rewards( RuntimeOrigin::signed(REGULAR_RELAYER), - TEST_LANE_ID + TEST_REWARDS_ACCOUNT_PARAMS ), Error::::NoRewardForRelayer, ); @@ -204,11 +220,15 @@ mod tests { #[test] fn relayer_cant_claim_if_payment_procedure_fails() { run_test(|| { - RelayerRewards::::insert(FAILING_RELAYER, TEST_LANE_ID, 100); + RelayerRewards::::insert( + FAILING_RELAYER, + TEST_REWARDS_ACCOUNT_PARAMS, + 100, + ); assert_noop!( Pallet::::claim_rewards( RuntimeOrigin::signed(FAILING_RELAYER), - TEST_LANE_ID + TEST_REWARDS_ACCOUNT_PARAMS ), Error::::FailedToPayReward, ); @@ -220,12 +240,19 @@ mod tests { run_test(|| { get_ready_for_events(); - RelayerRewards::::insert(REGULAR_RELAYER, TEST_LANE_ID, 100); + RelayerRewards::::insert( + REGULAR_RELAYER, + TEST_REWARDS_ACCOUNT_PARAMS, + 100, + ); assert_ok!(Pallet::::claim_rewards( RuntimeOrigin::signed(REGULAR_RELAYER), - TEST_LANE_ID + TEST_REWARDS_ACCOUNT_PARAMS )); - assert_eq!(RelayerRewards::::get(REGULAR_RELAYER, TEST_LANE_ID), None); + assert_eq!( + RelayerRewards::::get(REGULAR_RELAYER, TEST_REWARDS_ACCOUNT_PARAMS), + None + ); //Check if the `RewardPaid` event was emitted. assert_eq!( @@ -234,7 +261,7 @@ mod tests { phase: Phase::Initialization, event: TestEvent::Relayers(RewardPaid { relayer: REGULAR_RELAYER, - lane_id: TEST_LANE_ID, + rewards_account_params: TEST_REWARDS_ACCOUNT_PARAMS, reward: 100 }), topics: vec![], @@ -244,30 +271,39 @@ mod tests { } #[test] - fn pay_lane_reward_from_account_actually_pays_reward() { + fn pay_reward_from_account_actually_pays_reward() { type Balances = pallet_balances::Pallet; - type PayLaneRewardFromAccount = bp_relayers::PayLaneRewardFromAccount; + type PayLaneRewardFromAccount = bp_relayers::PayRewardFromAccount; run_test(|| { - let lane0_rewards_account = - PayLaneRewardFromAccount::lane_rewards_account(LaneId([0, 0, 0, 0])); - let lane1_rewards_account = - PayLaneRewardFromAccount::lane_rewards_account(LaneId([0, 0, 0, 1])); - - Balances::mint_into(&lane0_rewards_account, 100).unwrap(); - Balances::mint_into(&lane1_rewards_account, 100).unwrap(); - assert_eq!(Balances::balance(&lane0_rewards_account), 100); - assert_eq!(Balances::balance(&lane1_rewards_account), 100); + let in_lane_0 = RewardsAccountParams::new( + LaneId([0, 0, 0, 0]), + *b"test", + RewardsAccountOwner::ThisChain, + ); + let out_lane_1 = RewardsAccountParams::new( + LaneId([0, 0, 0, 1]), + *b"test", + RewardsAccountOwner::BridgedChain, + ); + + let in_lane0_rewards_account = PayLaneRewardFromAccount::rewards_account(in_lane_0); + let out_lane1_rewards_account = PayLaneRewardFromAccount::rewards_account(out_lane_1); + + Balances::mint_into(&in_lane0_rewards_account, 100).unwrap(); + Balances::mint_into(&out_lane1_rewards_account, 100).unwrap(); + assert_eq!(Balances::balance(&in_lane0_rewards_account), 100); + assert_eq!(Balances::balance(&out_lane1_rewards_account), 100); assert_eq!(Balances::balance(&1), 0); - PayLaneRewardFromAccount::pay_reward(&1, LaneId([0, 0, 0, 0]), 100).unwrap(); - assert_eq!(Balances::balance(&lane0_rewards_account), 0); - assert_eq!(Balances::balance(&lane1_rewards_account), 100); + PayLaneRewardFromAccount::pay_reward(&1, in_lane_0, 100).unwrap(); + assert_eq!(Balances::balance(&in_lane0_rewards_account), 0); + assert_eq!(Balances::balance(&out_lane1_rewards_account), 100); assert_eq!(Balances::balance(&1), 100); - PayLaneRewardFromAccount::pay_reward(&1, LaneId([0, 0, 0, 1]), 100).unwrap(); - assert_eq!(Balances::balance(&lane0_rewards_account), 0); - assert_eq!(Balances::balance(&lane1_rewards_account), 0); + PayLaneRewardFromAccount::pay_reward(&1, out_lane_1, 100).unwrap(); + assert_eq!(Balances::balance(&in_lane0_rewards_account), 0); + assert_eq!(Balances::balance(&out_lane1_rewards_account), 0); assert_eq!(Balances::balance(&1), 200); }); } diff --git a/modules/relayers/src/mock.rs b/modules/relayers/src/mock.rs index 89b3ead0411..c40c86f1db5 100644 --- a/modules/relayers/src/mock.rs +++ b/modules/relayers/src/mock.rs @@ -19,7 +19,7 @@ use crate as pallet_bridge_relayers; use bp_messages::LaneId; -use bp_relayers::PaymentProcedure; +use bp_relayers::{PaymentProcedure, RewardsAccountOwner, RewardsAccountParams}; use frame_support::{parameter_types, weights::RuntimeDbWeight}; use sp_core::H256; use sp_runtime::{ @@ -96,7 +96,8 @@ impl pallet_bridge_relayers::Config for TestRuntime { } /// Message lane that we're using in tests. -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]); +pub const TEST_REWARDS_ACCOUNT_PARAMS: RewardsAccountParams = + RewardsAccountParams::new(LaneId([0, 0, 0, 0]), *b"test", RewardsAccountOwner::ThisChain); /// Regular relayer that may receive rewards. pub const REGULAR_RELAYER: AccountId = 1; @@ -112,7 +113,7 @@ impl PaymentProcedure for TestPaymentProcedure { fn pay_reward( relayer: &AccountId, - _lane_id: LaneId, + _lane_id: RewardsAccountParams, _reward: Balance, ) -> Result<(), Self::Error> { match *relayer { diff --git a/modules/relayers/src/payment_adapter.rs b/modules/relayers/src/payment_adapter.rs index 946b31cf7af..2752044958b 100644 --- a/modules/relayers/src/payment_adapter.rs +++ b/modules/relayers/src/payment_adapter.rs @@ -18,28 +18,32 @@ use crate::{Config, Pallet}; -use bp_messages::source_chain::{DeliveryConfirmationPayments, RelayersRewards}; +use bp_messages::{ + source_chain::{DeliveryConfirmationPayments, RelayersRewards}, + LaneId, +}; +use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use frame_support::{sp_runtime::SaturatedConversion, traits::Get}; -use sp_arithmetic::traits::{Saturating, UniqueSaturatedFrom, Zero}; +use sp_arithmetic::traits::{Saturating, Zero}; use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive}; /// Adapter that allows relayers pallet to be used as a delivery+dispatch payment mechanism /// for the messages pallet. -pub struct DeliveryConfirmationPaymentsAdapter( - PhantomData<(T, DeliveryReward, ConfirmationReward)>, +pub struct DeliveryConfirmationPaymentsAdapter( + PhantomData<(T, MI, DeliveryReward)>, ); -impl DeliveryConfirmationPayments - for DeliveryConfirmationPaymentsAdapter +impl DeliveryConfirmationPayments + for DeliveryConfirmationPaymentsAdapter where - T: Config, + T: Config + pallet_bridge_messages::Config, + MI: 'static, DeliveryReward: Get, - ConfirmationReward: Get, { type Error = &'static str; fn pay_reward( - lane_id: bp_messages::LaneId, + lane_id: LaneId, messages_relayers: VecDeque>, confirmation_relayer: &T::AccountId, received_range: &RangeInclusive, @@ -50,9 +54,12 @@ where register_relayers_rewards::( confirmation_relayer, relayers_rewards, - lane_id, + RewardsAccountParams::new( + lane_id, + T::BridgedChainId::get(), + RewardsAccountOwner::BridgedChain, + ), DeliveryReward::get(), - ConfirmationReward::get(), ); } } @@ -61,34 +68,19 @@ where fn register_relayers_rewards( confirmation_relayer: &T::AccountId, relayers_rewards: RelayersRewards, - lane_id: bp_messages::LaneId, + lane_id: RewardsAccountParams, delivery_fee: T::Reward, - confirmation_fee: T::Reward, ) { // reward every relayer except `confirmation_relayer` let mut confirmation_relayer_reward = T::Reward::zero(); for (relayer, messages) in relayers_rewards { // sane runtime configurations guarantee that the number of messages will be below // `u32::MAX` - let mut relayer_reward = - T::Reward::unique_saturated_from(messages).saturating_mul(delivery_fee); + let relayer_reward = T::Reward::saturated_from(messages).saturating_mul(delivery_fee); if relayer != *confirmation_relayer { - // If delivery confirmation is submitted by other relayer, let's deduct confirmation fee - // from relayer reward. - // - // If confirmation fee has been increased (or if it was the only component of message - // fee), then messages relayer may receive zero reward. - let mut confirmation_reward = - T::Reward::saturated_from(messages).saturating_mul(confirmation_fee); - confirmation_reward = sp_std::cmp::min(confirmation_reward, relayer_reward); - relayer_reward = relayer_reward.saturating_sub(confirmation_reward); - confirmation_relayer_reward = - confirmation_relayer_reward.saturating_add(confirmation_reward); Pallet::::register_relayer_reward(lane_id, &relayer, relayer_reward); } else { - // If delivery confirmation is submitted by this relayer, let's add confirmation fee - // from other relayers to this relayer reward. confirmation_relayer_reward = confirmation_relayer_reward.saturating_add(relayer_reward); } @@ -121,47 +113,43 @@ mod tests { register_relayers_rewards::( &RELAYER_2, relayers_rewards(), - TEST_LANE_ID, + TEST_REWARDS_ACCOUNT_PARAMS, 50, - 10, ); - assert_eq!(RelayerRewards::::get(RELAYER_1, TEST_LANE_ID), Some(80)); - assert_eq!(RelayerRewards::::get(RELAYER_2, TEST_LANE_ID), Some(170)); - }); - } - - #[test] - fn confirmation_relayer_is_rewarded_if_it_has_not_delivered_any_delivered_messages() { - run_test(|| { - register_relayers_rewards::( - &RELAYER_3, - relayers_rewards(), - TEST_LANE_ID, - 50, - 10, + assert_eq!( + RelayerRewards::::get(RELAYER_1, TEST_REWARDS_ACCOUNT_PARAMS), + Some(100) + ); + assert_eq!( + RelayerRewards::::get(RELAYER_2, TEST_REWARDS_ACCOUNT_PARAMS), + Some(150) ); - - assert_eq!(RelayerRewards::::get(RELAYER_1, TEST_LANE_ID), Some(80)); - assert_eq!(RelayerRewards::::get(RELAYER_2, TEST_LANE_ID), Some(120)); - assert_eq!(RelayerRewards::::get(RELAYER_3, TEST_LANE_ID), Some(50)); }); } #[test] - fn only_confirmation_relayer_is_rewarded_if_confirmation_fee_has_significantly_increased() { + fn confirmation_relayer_is_not_rewarded_if_it_has_not_delivered_any_messages() { run_test(|| { register_relayers_rewards::( &RELAYER_3, relayers_rewards(), - TEST_LANE_ID, + TEST_REWARDS_ACCOUNT_PARAMS, 50, - 1000, ); - assert_eq!(RelayerRewards::::get(RELAYER_1, TEST_LANE_ID), None); - assert_eq!(RelayerRewards::::get(RELAYER_2, TEST_LANE_ID), None); - assert_eq!(RelayerRewards::::get(RELAYER_3, TEST_LANE_ID), Some(250)); + assert_eq!( + RelayerRewards::::get(RELAYER_1, TEST_REWARDS_ACCOUNT_PARAMS), + Some(100) + ); + assert_eq!( + RelayerRewards::::get(RELAYER_2, TEST_REWARDS_ACCOUNT_PARAMS), + Some(150) + ); + assert_eq!( + RelayerRewards::::get(RELAYER_3, TEST_REWARDS_ACCOUNT_PARAMS), + None + ); }); } } diff --git a/modules/relayers/src/weights.rs b/modules/relayers/src/weights.rs index fdaeee4f22a..572935f6302 100644 --- a/modules/relayers/src/weights.rs +++ b/modules/relayers/src/weights.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for pallet_bridge_relayers //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-02-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! HOSTNAME: `serban-ROG-Zephyrus`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -60,7 +60,7 @@ pub struct BridgeWeight(PhantomData); impl WeightInfo for BridgeWeight { /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, /// mode: MaxEncodedLen) /// /// Storage: System Account (r:1 w:1) @@ -69,10 +69,10 @@ impl WeightInfo for BridgeWeight { /// MaxEncodedLen) fn claim_rewards() -> Weight { // Proof Size summary in bytes: - // Measured: `534` - // Estimated: `5106` - // Minimum execution time: 48_239 nanoseconds. - Weight::from_parts(50_579_000, 5106) + // Measured: `275` + // Estimated: `5111` + // Minimum execution time: 43_031 nanoseconds. + Weight::from_parts(44_527_000, 5111) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -82,7 +82,7 @@ impl WeightInfo for BridgeWeight { impl WeightInfo for () { /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, /// mode: MaxEncodedLen) /// /// Storage: System Account (r:1 w:1) @@ -91,10 +91,10 @@ impl WeightInfo for () { /// MaxEncodedLen) fn claim_rewards() -> Weight { // Proof Size summary in bytes: - // Measured: `534` - // Estimated: `5106` - // Minimum execution time: 48_239 nanoseconds. - Weight::from_parts(50_579_000, 5106) + // Measured: `275` + // Estimated: `5111` + // Minimum execution time: 43_031 nanoseconds. + Weight::from_parts(44_527_000, 5111) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/primitives/header-chain/src/justification.rs b/primitives/header-chain/src/justification.rs index 1e34f365621..88dac0acf9f 100644 --- a/primitives/header-chain/src/justification.rs +++ b/primitives/header-chain/src/justification.rs @@ -135,7 +135,7 @@ pub fn decode_justification_target( } /// Verify and optimize given justification by removing unknown and duplicate votes. -pub fn optimize_justification( +pub fn verify_and_optimize_justification( finalized_target: (Header::Hash, Header::Number), authorities_set_id: SetId, authorities_set: &VoterSet, diff --git a/primitives/header-chain/tests/justification.rs b/primitives/header-chain/tests/justification.rs index f01f4ea2f10..52fa33ae974 100644 --- a/primitives/header-chain/tests/justification.rs +++ b/primitives/header-chain/tests/justification.rs @@ -17,7 +17,8 @@ //! Tests for Grandpa Justification code. use bp_header_chain::justification::{ - optimize_justification, required_justification_precommits, verify_justification, Error, + required_justification_precommits, verify_and_optimize_justification, verify_justification, + Error, }; use bp_test_utils::*; @@ -199,7 +200,7 @@ fn optimizer_does_noting_with_minimal_justification() { let justification = make_default_justification::(&test_header(1)); let num_precommits_before = justification.commit.precommits.len(); - let justification = optimize_justification::( + let justification = verify_and_optimize_justification::( header_id::(1), TEST_GRANDPA_SET_ID, &voter_set(), @@ -222,7 +223,7 @@ fn unknown_authority_votes_are_removed_by_optimizer() { )); let num_precommits_before = justification.commit.precommits.len(); - let justification = optimize_justification::( + let justification = verify_and_optimize_justification::( header_id::(1), TEST_GRANDPA_SET_ID, &voter_set(), @@ -243,7 +244,7 @@ fn duplicate_authority_votes_are_removed_by_optimizer() { .push(justification.commit.precommits.first().cloned().unwrap()); let num_precommits_before = justification.commit.precommits.len(); - let justification = optimize_justification::( + let justification = verify_and_optimize_justification::( header_id::(1), TEST_GRANDPA_SET_ID, &voter_set(), @@ -266,7 +267,7 @@ fn redundant_authority_votes_are_removed_by_optimizer() { )); let num_precommits_before = justification.commit.precommits.len(); - let justification = optimize_justification::( + let justification = verify_and_optimize_justification::( header_id::(1), TEST_GRANDPA_SET_ID, &voter_set(), diff --git a/primitives/relayers/Cargo.toml b/primitives/relayers/Cargo.toml index 7479811a57d..ee91361a4e2 100644 --- a/primitives/relayers/Cargo.toml +++ b/primitives/relayers/Cargo.toml @@ -7,6 +7,8 @@ edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive", "bit-vec"] } +scale-info = { version = "2.1.1", default-features = false, features = ["bit-vec", "derive"] } # Bridge Dependencies diff --git a/primitives/relayers/src/lib.rs b/primitives/relayers/src/lib.rs index 70a91ee941a..fb350958415 100644 --- a/primitives/relayers/src/lib.rs +++ b/primitives/relayers/src/lib.rs @@ -20,46 +20,95 @@ #![cfg_attr(not(feature = "std"), no_std)] use bp_messages::LaneId; -use bp_runtime::StorageDoubleMapKeyProvider; +use bp_runtime::{ChainId, StorageDoubleMapKeyProvider}; use frame_support::{Blake2_128Concat, Identity}; +use scale_info::TypeInfo; use sp_runtime::{ - codec::{Codec, Decode, Encode, EncodeLike}, + codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}, traits::AccountIdConversion, + TypeId, }; use sp_std::{fmt::Debug, marker::PhantomData}; +/// The owner of the sovereign account that should pay the rewards. +/// +/// Each of the 2 final points connected by a bridge owns a sovereign account at each end of the +/// bridge. So here, at this end of the bridge there can be 2 sovereign accounts that pay rewards. +#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] +pub enum RewardsAccountOwner { + /// The sovereign account of the final chain on this end of the bridge. + ThisChain, + /// The sovereign account of the final chain on the other end of the bridge. + BridgedChain, +} + +/// Structure used to identify the account that pays a reward to the relayer. +/// +/// A bridge connects 2 bridge ends. Each one is located on a separate relay chain. The bridge ends +/// can be the final destinations of the bridge, or they can be intermediary points +/// (e.g. a bridge hub) used to forward messages between pairs of parachains on the bridged relay +/// chains. A pair of such parachains is connected using a bridge lane. Each of the 2 final +/// destinations of a bridge lane must have a sovereign account at each end of the bridge and each +/// of the sovereign accounts will pay rewards for different operations. So we need multiple +/// parameters to identify the account that pays a reward to the relayer. +#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] +pub struct RewardsAccountParams { + lane_id: LaneId, + bridged_chain_id: ChainId, + owner: RewardsAccountOwner, +} + +impl RewardsAccountParams { + /// Create a new instance of `RewardsAccountParams`. + pub const fn new( + lane_id: LaneId, + bridged_chain_id: ChainId, + owner: RewardsAccountOwner, + ) -> Self { + Self { lane_id, bridged_chain_id, owner } + } +} + +impl TypeId for RewardsAccountParams { + const TYPE_ID: [u8; 4] = *b"brap"; +} + /// Reward payment procedure. pub trait PaymentProcedure { /// Error that may be returned by the procedure. type Error: Debug; - /// Pay reward to the relayer for serving given message lane. - fn pay_reward(relayer: &Relayer, lane_id: LaneId, reward: Reward) -> Result<(), Self::Error>; + /// Pay reward to the relayer from the account with provided params. + fn pay_reward( + relayer: &Relayer, + rewards_account_params: RewardsAccountParams, + reward: Reward, + ) -> Result<(), Self::Error>; } impl PaymentProcedure for () { type Error = &'static str; - fn pay_reward(_: &Relayer, _: LaneId, _: Reward) -> Result<(), Self::Error> { + fn pay_reward(_: &Relayer, _: RewardsAccountParams, _: Reward) -> Result<(), Self::Error> { Ok(()) } } /// Reward payment procedure that does `balances::transfer` call from the account, derived from -/// given lane. -pub struct PayLaneRewardFromAccount(PhantomData<(T, Relayer)>); +/// given params. +pub struct PayRewardFromAccount(PhantomData<(T, Relayer)>); -impl PayLaneRewardFromAccount +impl PayRewardFromAccount where Relayer: Decode + Encode, { - /// Return account that pay rewards for serving given lane. - pub fn lane_rewards_account(lane_id: LaneId) -> Relayer { - lane_id.into_sub_account_truncating(b"bridge-lane") + /// Return account that pays rewards based on the provided parameters. + pub fn rewards_account(params: RewardsAccountParams) -> Relayer { + params.into_sub_account_truncating(b"rewards-account") } } -impl PaymentProcedure for PayLaneRewardFromAccount +impl PaymentProcedure for PayRewardFromAccount where T: frame_support::traits::fungible::Transfer, Relayer: Decode + Encode, @@ -68,10 +117,11 @@ where fn pay_reward( relayer: &Relayer, - lane_id: LaneId, + rewards_account_params: RewardsAccountParams, reward: T::Balance, ) -> Result<(), Self::Error> { - T::transfer(&Self::lane_rewards_account(lane_id), relayer, reward, false).map(drop) + T::transfer(&Self::rewards_account(rewards_account_params), relayer, reward, false) + .map(drop) } } @@ -89,26 +139,58 @@ where type Hasher1 = Blake2_128Concat; type Key1 = AccountId; type Hasher2 = Identity; - type Key2 = LaneId; + type Key2 = RewardsAccountParams; type Value = Reward; } #[cfg(test)] mod tests { use super::*; + use bp_messages::LaneId; use sp_runtime::testing::H256; #[test] - fn lanes_are_using_different_accounts() { + fn different_lanes_are_using_different_accounts() { + assert_eq!( + PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( + LaneId([0, 0, 0, 0]), + *b"test", + RewardsAccountOwner::ThisChain + )), + hex_literal::hex!("62726170000000007465737400726577617264732d6163636f756e7400000000") + .into(), + ); + + assert_eq!( + PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( + LaneId([0, 0, 0, 1]), + *b"test", + RewardsAccountOwner::ThisChain + )), + hex_literal::hex!("62726170000000017465737400726577617264732d6163636f756e7400000000") + .into(), + ); + } + + #[test] + fn different_directions_are_using_different_accounts() { assert_eq!( - PayLaneRewardFromAccount::<(), H256>::lane_rewards_account(LaneId([0, 0, 0, 0])), - hex_literal::hex!("626c616e000000006272696467652d6c616e6500000000000000000000000000") + PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( + LaneId([0, 0, 0, 0]), + *b"test", + RewardsAccountOwner::ThisChain + )), + hex_literal::hex!("62726170000000007465737400726577617264732d6163636f756e7400000000") .into(), ); assert_eq!( - PayLaneRewardFromAccount::<(), H256>::lane_rewards_account(LaneId([0, 0, 0, 1])), - hex_literal::hex!("626c616e000000016272696467652d6c616e6500000000000000000000000000") + PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( + LaneId([0, 0, 0, 0]), + *b"test", + RewardsAccountOwner::BridgedChain + )), + hex_literal::hex!("62726170000000007465737401726577617264732d6163636f756e7400000000") .into(), ); } diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index c8829a508d9..3cd2fdd35ca 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -24,7 +24,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -trie-db = { version = "0.25.1", default-features = false } +trie-db = { version = "0.26.0", default-features = false } [dev-dependencies] hex-literal = "0.3" diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 0121b4ab84e..2d29b5aff0a 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -74,6 +74,12 @@ pub const POLKADOT_CHAIN_ID: ChainId = *b"pdot"; /// Bridge-with-Kusama instance id. pub const KUSAMA_CHAIN_ID: ChainId = *b"ksma"; +/// Bridge-with-Westend instance id. +pub const WESTEND_CHAIN_ID: ChainId = *b"wend"; + +/// Bridge-with-Westend instance id. +pub const WESTMINT_CHAIN_ID: ChainId = *b"wmnt"; + /// Bridge-with-Rococo instance id. pub const ROCOCO_CHAIN_ID: ChainId = *b"roco"; diff --git a/relays/bin-substrate/Cargo.toml b/relays/bin-substrate/Cargo.toml index 0dc20158830..9db0e30c52d 100644 --- a/relays/bin-substrate/Cargo.toml +++ b/relays/bin-substrate/Cargo.toml @@ -65,5 +65,5 @@ xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", defau bp-test-utils = { path = "../../primitives/test-utils" } hex-literal = "0.3" sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } -tempfile = "3.2" +tempfile = "3.4" finality-grandpa = { version = "0.16.0" } diff --git a/relays/bin-substrate/src/cli/resubmit_transactions.rs b/relays/bin-substrate/src/cli/resubmit_transactions.rs index 2fecd196921..c5b20e939c1 100644 --- a/relays/bin-substrate/src/cli/resubmit_transactions.rs +++ b/relays/bin-substrate/src/cli/resubmit_transactions.rs @@ -90,7 +90,6 @@ macro_rules! select_bridge { match $bridge { RelayChain::Millau => { type Target = relay_millau_client::Millau; - type TargetSign = relay_millau_client::Millau; $generic }, diff --git a/relays/client-bridge-hub-rococo/Cargo.toml b/relays/client-bridge-hub-rococo/Cargo.toml index 9a9316fa117..78c3533971c 100644 --- a/relays/client-bridge-hub-rococo/Cargo.toml +++ b/relays/client-bridge-hub-rococo/Cargo.toml @@ -17,6 +17,7 @@ bp-bridge-hub-wococo = { path = "../../primitives/chain-bridge-hub-wococo" } bp-header-chain = { path = "../../primitives/header-chain" } bp-messages = { path = "../../primitives/messages" } bp-parachains = { path = "../../primitives/parachains" } +bp-runtime = { path = "../../primitives/runtime" } bp-wococo = { path = "../../primitives/chain-wococo" } bridge-runtime-common = { path = "../../bin/runtime-common" } diff --git a/relays/client-bridge-hub-rococo/src/lib.rs b/relays/client-bridge-hub-rococo/src/lib.rs index c8963f968d0..02c389ffa29 100644 --- a/relays/client-bridge-hub-rococo/src/lib.rs +++ b/relays/client-bridge-hub-rococo/src/lib.rs @@ -18,6 +18,7 @@ use bp_bridge_hub_rococo::AVERAGE_BLOCK_INTERVAL; use bp_messages::MessageNonce; +use bp_runtime::ChainId; use codec::Encode; use relay_substrate_client::{ Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, ChainWithUtilityPallet, @@ -41,6 +42,7 @@ impl UnderlyingChainProvider for BridgeHubRococo { } impl Chain for BridgeHubRococo { + const ID: ChainId = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; const NAME: &'static str = "BridgeHubRococo"; const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = diff --git a/relays/client-bridge-hub-wococo/src/lib.rs b/relays/client-bridge-hub-wococo/src/lib.rs index 7f23c0d93d2..359692b0e3f 100644 --- a/relays/client-bridge-hub-wococo/src/lib.rs +++ b/relays/client-bridge-hub-wococo/src/lib.rs @@ -18,6 +18,7 @@ use bp_bridge_hub_wococo::AVERAGE_BLOCK_INTERVAL; use bp_messages::MessageNonce; +use bp_runtime::ChainId; use codec::Encode; use relay_substrate_client::{ Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, ChainWithUtilityPallet, @@ -41,6 +42,7 @@ impl UnderlyingChainProvider for BridgeHubWococo { } impl Chain for BridgeHubWococo { + const ID: ChainId = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; const NAME: &'static str = "BridgeHubWococo"; const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = diff --git a/relays/client-kusama/Cargo.toml b/relays/client-kusama/Cargo.toml index e4d83d88ceb..302f530eb4e 100644 --- a/relays/client-kusama/Cargo.toml +++ b/relays/client-kusama/Cargo.toml @@ -12,6 +12,7 @@ relay-utils = { path = "../utils" } # Bridge dependencies bp-kusama = { path = "../../primitives/chain-kusama" } +bp-runtime = { path = "../../primitives/runtime" } # Substrate Dependencies diff --git a/relays/client-kusama/src/lib.rs b/relays/client-kusama/src/lib.rs index 83f6b30f4cb..4b631b6052b 100644 --- a/relays/client-kusama/src/lib.rs +++ b/relays/client-kusama/src/lib.rs @@ -17,6 +17,7 @@ //! Types used to connect to the Kusama chain. use bp_kusama::AccountInfoStorageMapKeyProvider; +use bp_runtime::ChainId; use relay_substrate_client::{Chain, ChainWithBalances, UnderlyingChainProvider}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -33,6 +34,7 @@ impl UnderlyingChainProvider for Kusama { } impl Chain for Kusama { + const ID: ChainId = bp_runtime::KUSAMA_CHAIN_ID; const NAME: &'static str = "Kusama"; const TOKEN_ID: Option<&'static str> = Some("kusama"); const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = diff --git a/relays/client-millau/Cargo.toml b/relays/client-millau/Cargo.toml index 66395d5de89..fe5482e5b67 100644 --- a/relays/client-millau/Cargo.toml +++ b/relays/client-millau/Cargo.toml @@ -14,6 +14,7 @@ relay-utils = { path = "../utils" } bp-messages = { path = "../../primitives/messages" } bp-millau = { path = "../../primitives/chain-millau" } +bp-runtime = { path = "../../primitives/runtime" } millau-runtime = { path = "../../bin/millau/runtime" } # Substrate Dependencies diff --git a/relays/client-millau/src/lib.rs b/relays/client-millau/src/lib.rs index 9df21b1d503..1c8a0f984ef 100644 --- a/relays/client-millau/src/lib.rs +++ b/relays/client-millau/src/lib.rs @@ -17,6 +17,7 @@ //! Types used to connect to the Millau-Substrate chain. use bp_messages::MessageNonce; +use bp_runtime::ChainId; use codec::{Compact, Decode, Encode}; use relay_substrate_client::{ BalanceOf, Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, @@ -54,6 +55,7 @@ impl ChainWithMessages for Millau { } impl Chain for Millau { + const ID: ChainId = bp_runtime::MILLAU_CHAIN_ID; const NAME: &'static str = "Millau"; // Rialto token has no value, but we associate it with KSM token const TOKEN_ID: Option<&'static str> = Some("kusama"); diff --git a/relays/client-polkadot/Cargo.toml b/relays/client-polkadot/Cargo.toml index cc16ec1f5c3..0d3a30949cb 100644 --- a/relays/client-polkadot/Cargo.toml +++ b/relays/client-polkadot/Cargo.toml @@ -12,6 +12,7 @@ relay-utils = { path = "../utils" } # Bridge dependencies bp-polkadot = { path = "../../primitives/chain-polkadot" } +bp-runtime = { path = "../../primitives/runtime" } # Substrate Dependencies diff --git a/relays/client-polkadot/src/lib.rs b/relays/client-polkadot/src/lib.rs index 19326dd4c73..08837bdcec2 100644 --- a/relays/client-polkadot/src/lib.rs +++ b/relays/client-polkadot/src/lib.rs @@ -17,6 +17,7 @@ //! Types used to connect to the Polkadot chain. use bp_polkadot::AccountInfoStorageMapKeyProvider; +use bp_runtime::ChainId; use relay_substrate_client::{Chain, ChainWithBalances, UnderlyingChainProvider}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -33,6 +34,7 @@ impl UnderlyingChainProvider for Polkadot { } impl Chain for Polkadot { + const ID: ChainId = bp_runtime::POLKADOT_CHAIN_ID; const NAME: &'static str = "Polkadot"; const TOKEN_ID: Option<&'static str> = Some("polkadot"); const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = diff --git a/relays/client-rialto-parachain/Cargo.toml b/relays/client-rialto-parachain/Cargo.toml index 96929d55cc8..c1a6b5d69aa 100644 --- a/relays/client-rialto-parachain/Cargo.toml +++ b/relays/client-rialto-parachain/Cargo.toml @@ -17,6 +17,7 @@ bp-messages = { path = "../../primitives/messages" } bp-millau = { path = "../../primitives/chain-millau" } bp-polkadot-core = { path = "../../primitives/polkadot-core" } bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" } +bp-runtime = { path = "../../primitives/runtime" } bridge-runtime-common = { path = "../../bin/runtime-common" } relay-substrate-client = { path = "../client-substrate" } diff --git a/relays/client-rialto-parachain/src/lib.rs b/relays/client-rialto-parachain/src/lib.rs index 654848da2a7..27673dc23be 100644 --- a/relays/client-rialto-parachain/src/lib.rs +++ b/relays/client-rialto-parachain/src/lib.rs @@ -20,6 +20,7 @@ pub mod codegen_runtime; use bp_messages::MessageNonce; use bp_polkadot_core::PolkadotSignedExtension; +use bp_runtime::ChainId; use codec::Encode; use relay_substrate_client::{ Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, Error as SubstrateError, @@ -48,6 +49,7 @@ impl UnderlyingChainProvider for RialtoParachain { } impl Chain for RialtoParachain { + const ID: ChainId = bp_runtime::RIALTO_PARACHAIN_CHAIN_ID; const NAME: &'static str = "RialtoParachain"; // RialtoParachain token has no value, but we associate it with DOT token const TOKEN_ID: Option<&'static str> = Some("polkadot"); diff --git a/relays/client-rialto/Cargo.toml b/relays/client-rialto/Cargo.toml index ef339feb8a5..c1ae0dfabe7 100644 --- a/relays/client-rialto/Cargo.toml +++ b/relays/client-rialto/Cargo.toml @@ -14,6 +14,7 @@ relay-utils = { path = "../utils" } bp-messages = { path = "../../primitives/messages" } bp-rialto = { path = "../../primitives/chain-rialto" } +bp-runtime = { path = "../../primitives/runtime" } rialto-runtime = { path = "../../bin/rialto/runtime" } # Substrate Dependencies diff --git a/relays/client-rialto/src/lib.rs b/relays/client-rialto/src/lib.rs index b822dfe7d89..7fe735b4adc 100644 --- a/relays/client-rialto/src/lib.rs +++ b/relays/client-rialto/src/lib.rs @@ -17,6 +17,7 @@ //! Types used to connect to the Rialto-Substrate chain. use bp_messages::MessageNonce; +use bp_runtime::ChainId; use codec::{Compact, Decode, Encode}; use relay_substrate_client::{ BalanceOf, Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, @@ -39,6 +40,7 @@ impl UnderlyingChainProvider for Rialto { } impl Chain for Rialto { + const ID: ChainId = bp_runtime::RIALTO_CHAIN_ID; const NAME: &'static str = "Rialto"; // Rialto token has no value, but we associate it with DOT token const TOKEN_ID: Option<&'static str> = Some("polkadot"); diff --git a/relays/client-rococo/Cargo.toml b/relays/client-rococo/Cargo.toml index cc197ba8562..2a5b556dce2 100644 --- a/relays/client-rococo/Cargo.toml +++ b/relays/client-rococo/Cargo.toml @@ -12,6 +12,7 @@ relay-utils = { path = "../utils" } # Bridge dependencies bp-rococo = { path = "../../primitives/chain-rococo" } +bp-runtime = { path = "../../primitives/runtime" } # Substrate Dependencies diff --git a/relays/client-rococo/src/lib.rs b/relays/client-rococo/src/lib.rs index a0730026e04..5049778a542 100644 --- a/relays/client-rococo/src/lib.rs +++ b/relays/client-rococo/src/lib.rs @@ -16,6 +16,7 @@ //! Types used to connect to the Rococo-Substrate chain. +use bp_runtime::ChainId; use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -35,6 +36,7 @@ impl UnderlyingChainProvider for Rococo { } impl Chain for Rococo { + const ID: ChainId = bp_runtime::ROCOCO_CHAIN_ID; const NAME: &'static str = "Rococo"; const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = diff --git a/relays/client-substrate/Cargo.toml b/relays/client-substrate/Cargo.toml index 49201defdd1..7e7eba5ec2b 100644 --- a/relays/client-substrate/Cargo.toml +++ b/relays/client-substrate/Cargo.toml @@ -10,10 +10,10 @@ async-std = { version = "1.6.5", features = ["attributes"] } async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.1.5" } futures = "0.3.26" -jsonrpsee = { version = "0.15", features = ["macros", "ws-client"] } +jsonrpsee = { version = "0.16", features = ["macros", "ws-client"] } log = "0.4.17" num-traits = "0.2" -rand = "0.7" +rand = "0.8" scale-info = { version = "2.1.1", features = ["derive"] } tokio = { version = "1.25", features = ["rt-multi-thread"] } thiserror = "1.0.26" diff --git a/relays/client-substrate/src/chain.rs b/relays/client-substrate/src/chain.rs index b55ae71cbf8..bc38d1ec932 100644 --- a/relays/client-substrate/src/chain.rs +++ b/relays/client-substrate/src/chain.rs @@ -18,8 +18,8 @@ use crate::calls::UtilityCall; use bp_messages::MessageNonce; use bp_runtime::{ - Chain as ChainBase, EncodedOrDecodedCall, HashOf, Parachain as ParachainBase, TransactionEra, - TransactionEraOf, UnderlyingChainProvider, + Chain as ChainBase, ChainId, EncodedOrDecodedCall, HashOf, Parachain as ParachainBase, + TransactionEra, TransactionEraOf, UnderlyingChainProvider, }; use codec::{Codec, Encode}; use jsonrpsee::core::{DeserializeOwned, Serialize}; @@ -35,6 +35,8 @@ use std::{fmt::Debug, time::Duration}; /// Substrate-based chain from minimal relay-client point of view. pub trait Chain: ChainBase + Clone { + /// Chain id. + const ID: ChainId; /// Chain name. const NAME: &'static str; /// Identifier of the basic token of the chain (if applicable). diff --git a/relays/client-substrate/src/test_chain.rs b/relays/client-substrate/src/test_chain.rs index 4589687d39d..9bc6c5ae15c 100644 --- a/relays/client-substrate/src/test_chain.rs +++ b/relays/client-substrate/src/test_chain.rs @@ -22,6 +22,7 @@ #![cfg(any(feature = "test-helpers", test))] use crate::{Chain, ChainWithBalances}; +use bp_runtime::ChainId; use frame_support::weights::Weight; use std::time::Duration; @@ -50,6 +51,7 @@ impl bp_runtime::Chain for TestChain { } impl Chain for TestChain { + const ID: ChainId = *b"test"; const NAME: &'static str = "Test"; const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "TestMethod"; diff --git a/relays/client-westend/Cargo.toml b/relays/client-westend/Cargo.toml index c60305868ad..351013bd864 100644 --- a/relays/client-westend/Cargo.toml +++ b/relays/client-westend/Cargo.toml @@ -11,6 +11,7 @@ relay-utils = { path = "../utils" } # Bridge dependencies +bp-runtime = { path = "../../primitives/runtime" } bp-westend = { path = "../../primitives/chain-westend" } # Substrate Dependencies diff --git a/relays/client-westend/src/lib.rs b/relays/client-westend/src/lib.rs index a8a8df36b8b..6db8eaf95e4 100644 --- a/relays/client-westend/src/lib.rs +++ b/relays/client-westend/src/lib.rs @@ -16,6 +16,7 @@ //! Types used to connect to the Westend chain. +use bp_runtime::ChainId; use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -35,6 +36,7 @@ impl UnderlyingChainProvider for Westend { } impl Chain for Westend { + const ID: ChainId = bp_runtime::WESTEND_CHAIN_ID; const NAME: &'static str = "Westend"; const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = @@ -68,6 +70,7 @@ impl UnderlyingChainProvider for Westmint { // Westmint seems to use the same configuration as all Polkadot-like chains, so we'll use Westend // primitives here. impl Chain for Westmint { + const ID: ChainId = bp_runtime::WESTMINT_CHAIN_ID; const NAME: &'static str = "Westmint"; const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = diff --git a/relays/client-wococo/Cargo.toml b/relays/client-wococo/Cargo.toml index 19483ab22fd..78a5afe17ca 100644 --- a/relays/client-wococo/Cargo.toml +++ b/relays/client-wococo/Cargo.toml @@ -11,6 +11,7 @@ relay-utils = { path = "../utils" } # Bridge dependencies +bp-runtime = { path = "../../primitives/runtime" } bp-wococo = { path = "../../primitives/chain-wococo" } # Substrate Dependencies diff --git a/relays/client-wococo/src/lib.rs b/relays/client-wococo/src/lib.rs index 8f30572db63..582c34ef853 100644 --- a/relays/client-wococo/src/lib.rs +++ b/relays/client-wococo/src/lib.rs @@ -16,6 +16,7 @@ //! Types used to connect to the Wococo-Substrate chain. +use bp_runtime::ChainId; use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -35,6 +36,7 @@ impl UnderlyingChainProvider for Wococo { } impl Chain for Wococo { + const ID: ChainId = bp_runtime::WOCOCO_CHAIN_ID; const NAME: &'static str = "Wococo"; const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = diff --git a/relays/lib-substrate-relay/src/finality/engine.rs b/relays/lib-substrate-relay/src/finality/engine.rs index f5ac8539a68..4fc503011bb 100644 --- a/relays/lib-substrate-relay/src/finality/engine.rs +++ b/relays/lib-substrate-relay/src/finality/engine.rs @@ -19,7 +19,7 @@ use crate::error::Error; use async_trait::async_trait; use bp_header_chain::{ - justification::{verify_justification, GrandpaJustification}, + justification::{verify_and_optimize_justification, GrandpaJustification}, ConsensusLogReader, FinalityProof, GrandpaConsensusLogReader, }; use bp_runtime::{BasicOperatingMode, HeaderIdProvider, OperatingMode}; @@ -172,7 +172,7 @@ impl Engine for Grandpa { // actual authorities set (which we have read now) may have changed, so this // `optimize_justification` may fail. But if target chain is configured properly, it'll fail // anyway, after we submit transaction and failing earlier is better. So - it is fine - bp_header_chain::justification::optimize_justification( + verify_and_optimize_justification( (header.hash(), *header.number()), authority_set_id, &authority_set, @@ -272,11 +272,11 @@ impl Engine for Grandpa { initial_authorities_set_id, ); - let is_valid_set_id = verify_justification::( + let is_valid_set_id = verify_and_optimize_justification( (initial_header_hash, initial_header_number), initial_authorities_set_id, &authorities_for_verification, - &justification, + justification.clone(), ) .is_ok(); diff --git a/relays/lib-substrate-relay/src/messages_metrics.rs b/relays/lib-substrate-relay/src/messages_metrics.rs index 943f3b7c369..33d855a0263 100644 --- a/relays/lib-substrate-relay/src/messages_metrics.rs +++ b/relays/lib-substrate-relay/src/messages_metrics.rs @@ -19,6 +19,7 @@ use crate::TaggedAccount; use bp_messages::LaneId; +use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::StorageDoubleMapKeyProvider; use codec::Decode; use frame_system::AccountInfo; @@ -82,18 +83,29 @@ where if let Some(relayers_pallet_name) = BC::WITH_CHAIN_RELAYERS_PALLET_NAME { for lane in lanes { - let relay_account_reward_metric = FloatStorageValueMetric::new( + FloatStorageValueMetric::new( AccountBalance:: { token_decimals, _phantom: Default::default() }, client.clone(), bp_relayers::RelayerRewardsKeyProvider::, BalanceOf>::final_key( relayers_pallet_name, account.id(), - lane, + &RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::ThisChain), ), - format!("at_{}_relay_{}_reward_for_lane_{}_with_{}", C::NAME, account.tag(), hex::encode(lane.as_ref()), BC::NAME), - format!("Reward of the {} relay account for serving lane {:?} with {} at the {}", account.tag(), lane, BC::NAME, C::NAME), - )?; - relay_account_reward_metric.register_and_spawn(&metrics.registry)?; + format!("at_{}_relay_{}_reward_for_msgs_from_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, hex::encode(lane.as_ref())), + format!("Reward of the {} relay account at {} for delivering messages from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane), + )?.register_and_spawn(&metrics.registry)?; + + FloatStorageValueMetric::new( + AccountBalance:: { token_decimals, _phantom: Default::default() }, + client.clone(), + bp_relayers::RelayerRewardsKeyProvider::, BalanceOf>::final_key( + relayers_pallet_name, + account.id(), + &RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::BridgedChain), + ), + format!("at_{}_relay_{}_reward_for_msgs_to_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, hex::encode(lane.as_ref())), + format!("Reward of the {} relay account at {} for delivering messages confirmations from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane), + )?.register_and_spawn(&metrics.registry)?; } } } diff --git a/relays/messages/src/message_lane_loop.rs b/relays/messages/src/message_lane_loop.rs index 5e5085bbd5d..2903435dda5 100644 --- a/relays/messages/src/message_lane_loop.rs +++ b/relays/messages/src/message_lane_loop.rs @@ -1017,58 +1017,6 @@ pub(crate) mod tests { assert_eq!(result.submitted_messages_proofs, vec![(1..=1, None)],); } - #[test] - fn message_lane_loop_is_able_to_recover_from_race_stall() { - // with this configuration, both source and target clients will lose their transactions => - // reconnect will happen - let (source_exit_sender, exit_receiver) = unbounded(); - let target_exit_sender = source_exit_sender.clone(); - let result = run_loop_test( - Arc::new(Mutex::new(TestClientData { - source_state: ClientState { - best_self: HeaderId(0, 0), - best_finalized_self: HeaderId(0, 0), - best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), - actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), - }, - source_latest_generated_nonce: 1, - source_tracked_transaction_status: TrackedTransactionStatus::Lost, - target_state: ClientState { - best_self: HeaderId(0, 0), - best_finalized_self: HeaderId(0, 0), - best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), - actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), - }, - target_latest_received_nonce: 0, - target_tracked_transaction_status: TrackedTransactionStatus::Lost, - ..Default::default() - })), - Arc::new(move |data: &mut TestClientData| { - if data.is_source_reconnected { - data.source_tracked_transaction_status = - TrackedTransactionStatus::Finalized(Default::default()); - } - if data.is_source_reconnected && data.is_target_reconnected { - source_exit_sender.unbounded_send(()).unwrap(); - } - }), - Arc::new(|_| {}), - Arc::new(move |data: &mut TestClientData| { - if data.is_target_reconnected { - data.target_tracked_transaction_status = - TrackedTransactionStatus::Finalized(Default::default()); - } - if data.is_source_reconnected && data.is_target_reconnected { - target_exit_sender.unbounded_send(()).unwrap(); - } - }), - Arc::new(|_| {}), - exit_receiver.into_future().map(|(_, _)| ()), - ); - - assert!(result.is_source_reconnected); - } - #[test] fn message_lane_loop_is_able_to_recover_from_unsuccessful_transaction() { // with this configuration, both source and target clients will mine their transactions, but @@ -1146,7 +1094,6 @@ pub(crate) mod tests { exit_receiver.into_future().map(|(_, _)| ()), ); - assert!(result.is_source_reconnected); assert_eq!(result.submitted_messages_proofs.len(), 2); assert_eq!(result.submitted_messages_receiving_proofs.len(), 2); } diff --git a/relays/messages/src/message_race_delivery.rs b/relays/messages/src/message_race_delivery.rs index c704a7b5610..8b8e690ec4a 100644 --- a/relays/messages/src/message_race_delivery.rs +++ b/relays/messages/src/message_race_delivery.rs @@ -292,11 +292,16 @@ impl std::fmt::Debug for MessageDeliveryStrategy MessageDeliveryStrategy { /// Returns total weight of all undelivered messages. - fn total_queued_dispatch_weight(&self) -> Weight { + fn dispatch_weight_for_range(&self, range: &RangeInclusive) -> Weight { self.strategy .source_queue() .iter() - .flat_map(|(_, range)| range.values().map(|details| details.dispatch_weight)) + .flat_map(|(_, subrange)| { + subrange + .iter() + .filter(|(nonce, _)| range.contains(nonce)) + .map(|(_, details)| details.dispatch_weight) + }) .fold(Weight::zero(), |total, weight| total.saturating_add(weight)) } } @@ -424,7 +429,7 @@ where } async fn select_nonces_to_deliver( - &mut self, + &self, race_state: RaceState, TargetHeaderIdOf

, P::MessagesProof>, ) -> Option<(RangeInclusive, Self::ProofParameters)> { let best_finalized_source_header_id_at_best_target = @@ -526,7 +531,6 @@ where let maximal_source_queue_index = self.strategy.maximal_available_source_queue_index(race_state)?; - let previous_total_dispatch_weight = self.total_queued_dispatch_weight(); let source_queue = self.strategy.source_queue(); let reference = RelayMessagesBatchReference { @@ -544,10 +548,7 @@ where let range_begin = source_queue[0].1.begin(); let selected_nonces = range_begin..=range_end; - self.strategy.remove_le_nonces_from_source_queue(range_end); - - let new_total_dispatch_weight = self.total_queued_dispatch_weight(); - let dispatch_weight = previous_total_dispatch_weight - new_total_dispatch_weight; + let dispatch_weight = self.dispatch_weight_for_range(&selected_nonces); Some(( selected_nonces, @@ -707,7 +708,7 @@ mod tests { #[async_std::test] async fn message_delivery_strategy_selects_messages_to_deliver() { - let (state, mut strategy) = prepare_strategy(); + let (state, strategy) = prepare_strategy(); // both sides are ready to relay new messages assert_eq!( diff --git a/relays/messages/src/message_race_loop.rs b/relays/messages/src/message_race_loop.rs index 3d995a0a362..50f71ea050b 100644 --- a/relays/messages/src/message_race_loop.rs +++ b/relays/messages/src/message_race_loop.rs @@ -211,7 +211,7 @@ pub trait RaceStrategy: Debug { /// data) from source to target node. /// Additionally, parameters required to generate proof are returned. async fn select_nonces_to_deliver( - &mut self, + &self, race_state: RaceState, ) -> Option<(RangeInclusive, Self::ProofParameters)>; } @@ -234,6 +234,13 @@ pub struct RaceState { pub nonces_submitted: Option>, } +impl RaceState { + /// Reset `nonces_submitted` to `None`. + fn reset_submitted(&mut self) { + self.nonces_submitted = None; + } +} + /// Run race loop until connection with target or source node is lost. pub async fn run, TC: TargetClient

>( race_source: SC, @@ -460,7 +467,7 @@ pub async fn run, TC: TargetClient

>( (TrackedTransactionStatus::Finalized(at_block), Some(nonces_submitted)) => { // our transaction has been mined, but was it successful or not? let's check the best // nonce at the target node. - race_target.nonces(at_block, false) + let _ = race_target.nonces(at_block, false) .await .map_err(|e| format!("failed to read nonces from target node: {e:?}")) .and_then(|(_, nonces_at_target)| { @@ -477,26 +484,26 @@ pub async fn run, TC: TargetClient

>( .map_err(|e| { log::error!( target: "bridge", - "{} -> {} race has stalled. Transaction failed: {}. Going to restart", + "{} -> {} race transaction failed: {}", P::source_name(), P::target_name(), e, ); - FailedClient::Both - })?; + race_state.reset_submitted(); + }); }, (TrackedTransactionStatus::Lost, _) => { log::warn!( target: "bridge", - "{} -> {} race has stalled. State: {:?}. Strategy: {:?}", + "{} -> {} race transaction has been lost. State: {:?}. Strategy: {:?}", P::source_name(), P::target_name(), race_state, strategy, ); - return Err(FailedClient::Both); + race_state.reset_submitted(); }, _ => (), } @@ -531,8 +538,7 @@ pub async fn run, TC: TargetClient

>( race_state.clone() }; - let nonces_to_deliver = - select_nonces_to_deliver(expected_race_state, &mut strategy).await; + let nonces_to_deliver = select_nonces_to_deliver(expected_race_state, &strategy).await; let best_at_source = strategy.best_at_source(); if let Some((at_block, nonces_range, proof_parameters)) = nonces_to_deliver { @@ -665,7 +671,7 @@ where async fn select_nonces_to_deliver( race_state: RaceState, - strategy: &mut Strategy, + strategy: &Strategy, ) -> Option<(SourceHeaderId, RangeInclusive, Strategy::ProofParameters)> where SourceHeaderId: Clone, @@ -723,7 +729,7 @@ mod tests { // the proof will be generated on source, but using BEST_AT_TARGET block assert_eq!( - select_nonces_to_deliver(race_state, &mut strategy).await, + select_nonces_to_deliver(race_state, &strategy).await, Some((HeaderId(BEST_AT_TARGET, BEST_AT_TARGET), 6..=10, (),)) ); } diff --git a/relays/messages/src/message_race_strategy.rs b/relays/messages/src/message_race_strategy.rs index 9b9091b979f..9a53a487d94 100644 --- a/relays/messages/src/message_race_strategy.rs +++ b/relays/messages/src/message_race_strategy.rs @@ -136,7 +136,7 @@ impl< } /// Remove all nonces that are less than or equal to given nonce from the source queue. - pub fn remove_le_nonces_from_source_queue(&mut self, nonce: MessageNonce) { + fn remove_le_nonces_from_source_queue(&mut self, nonce: MessageNonce) { while let Some((queued_at, queued_range)) = self.source_queue.pop_front() { if let Some(range_to_requeue) = queued_range.greater_than(nonce) { self.source_queue.push_front((queued_at, range_to_requeue)); @@ -168,12 +168,12 @@ impl< SourceNoncesRange, Proof, > where - SourceHeaderHash: Clone + Debug + Send, - SourceHeaderNumber: Clone + Ord + Debug + Send, - SourceNoncesRange: NoncesRange + Debug + Send, - TargetHeaderHash: Debug + Send, - TargetHeaderNumber: Debug + Send, - Proof: Debug + Send, + SourceHeaderHash: Clone + Debug + Send + Sync, + SourceHeaderNumber: Clone + Ord + Debug + Send + Sync, + SourceNoncesRange: NoncesRange + Debug + Send + Sync, + TargetHeaderHash: Debug + Send + Sync, + TargetHeaderNumber: Debug + Send + Sync, + Proof: Debug + Send + Sync, { type SourceNoncesRange = SourceNoncesRange; type ProofParameters = (); @@ -284,6 +284,7 @@ impl< Proof, >, ) { + self.remove_le_nonces_from_source_queue(nonces.latest_nonce); // TODO: does it means that we'll try to submit old nonces in next tx??? self.best_target_nonce = Some(std::cmp::max( self.best_target_nonce.unwrap_or(nonces.latest_nonce), nonces.latest_nonce, @@ -291,7 +292,7 @@ impl< } async fn select_nonces_to_deliver( - &mut self, + &self, race_state: RaceState< HeaderId, HeaderId, @@ -301,7 +302,6 @@ impl< let maximal_source_queue_index = self.maximal_available_source_queue_index(race_state)?; let range_begin = self.source_queue[0].1.begin(); let range_end = self.source_queue[maximal_source_queue_index].1.end(); - self.remove_le_nonces_from_source_queue(range_end); Some((range_begin..=range_end, ())) } } From 6c63ece60399f8e87bfd36005cdf0e6e4c5de117 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Tue, 28 Feb 2023 16:24:38 +0200 Subject: [PATCH 206/263] Rewards adjustments --- .../runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index c768477c8bb..f45d27b2ce9 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -426,7 +426,6 @@ parameter_types! { // TODO:check-parameter - setup initial values https://github.com/paritytech/parity-bridges-common/issues/1677 pub storage DeliveryRewardInBalance: u64 = 1_000_000; - pub storage ConfirmationRewardInBalance: u64 = 100_000; } /// Add parachain bridge pallet to track Wococo bridge hub parachain @@ -479,8 +478,8 @@ impl pallet_bridge_messages::Config for Run type LaneMessageVerifier = bridge_hub_rococo_config::ToBridgeHubWococoMessageVerifier; type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< Runtime, + WithBridgeHubWococoMessagesInstance, DeliveryRewardInBalance, - ConfirmationRewardInBalance, >; type SourceHeaderChain = SourceHeaderChainAdapter; @@ -515,8 +514,8 @@ impl pallet_bridge_messages::Config for Run type LaneMessageVerifier = bridge_hub_wococo_config::ToBridgeHubRococoMessageVerifier; type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< Runtime, + WithBridgeHubRococoMessagesInstance, DeliveryRewardInBalance, - ConfirmationRewardInBalance, >; type SourceHeaderChain = SourceHeaderChainAdapter; @@ -532,7 +531,7 @@ impl pallet_bridge_relayers::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reward = Balance; type PaymentProcedure = - bp_relayers::PayLaneRewardFromAccount, AccountId>; + bp_relayers::PayRewardFromAccount, AccountId>; type WeightInfo = weights::pallet_bridge_relayers::WeightInfo; } From 9c652c394fbc051213764489899bde7080965458 Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Wed, 1 Mar 2023 14:32:04 +0200 Subject: [PATCH 207/263] Update RBH/WBH spec version --- parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index f45d27b2ce9..62a26cccd1c 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -149,7 +149,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("bridge-hub-rococo"), impl_name: create_runtime_str!("bridge-hub-rococo"), authoring_version: 1, - spec_version: 9371, + spec_version: 9372, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From f6a606b1e417589b64d02e5954a3fc886acecb80 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 14 Mar 2023 13:47:47 +0100 Subject: [PATCH 208/263] Squashed 'bridges/' changes from d05a98473..ce7cf9a49 ce7cf9a49 Removed deprecated `#[pallet::generate_store(pub(super) trait Store)]` according to latest Cumulus (#1964) 897b1c0b2 Bump substrate/polkadot/cumulus (#1962) 7b946da2d Backport xcm bridging extensions to the bridge repo (#1813) 88c1114ec Bump futures from 0.3.26 to 0.3.27 8668f73bf Bump serde from 1.0.152 to 1.0.155 3df8823e1 Bump subxt from `a7b45ef` to `d4545de` ef1b1bcd0 Some error improvements (#1956) 434c5e014 optimize justifications before they're included into complex transaction (#1949) 7bac365a6 Actually clone client data by reference when cloning the client (#1941) 764ddd4a8 remove lock file after checks are done (#1942) c18a758f8 Fix invalid messages relay delivery transactions (#1940) 8ad152b06 fix nightly benchmarks test (#1939) d451b4f84 Bump tokio from 1.25.0 to 1.26.0 8019c50aa Bump async-trait from 0.1.64 to 0.1.66 aa055fcee Bump serde_json from 1.0.93 to 1.0.94 ec2ef31c4 Bump subxt from `1c5faf3` to `a7b45ef` 20026366f Bump sysinfo from 0.28.1 to 0.28.2 fe246d1e3 Bump thiserror from 1.0.38 to 1.0.39 c95e0cf02 Fix deploy step in CI (#1931) 15b41c2dd Improve some relay errors readability (#1930) 792deae5e Added deploy Job (#1929) d86c3ce21 Reconnect on-demand clients from MessagesSource::reconnect and MessagesTarget::reconnect (#1927) 4161b51f0 get rid of obsolete weight functions (#1926) 9b3b00e0f cargo update -p clap@4.1.6 (#1925) 13ab28c37 Bump subxt from `9e2acff` to `1c5faf3` bb6171a05 Remove subxt dependency features (#1924) 66d200abb Verify with-parachain message benchmarks on CI (#1923) b6af2116f Update BHR and BHW spec version (#1922) d464e78d9 Fix benchmarks (#1919) 74574d53e fix master compilation (#1920) 1b373dff9 Fix multiple parachain headers submission for single message delivery (#1916) git-subtree-dir: bridges git-subtree-split: ce7cf9a4977fe614d35b6a7a84d5057e2c4ccaf5 --- .config/lingua.dic | 2 +- .gitlab-ci.yml | 51 +- .maintain/millau-weight-template.hbs | 16 +- Cargo.lock | 3282 +++++++---------- bin/millau/node/Cargo.toml | 15 +- bin/millau/node/src/chain_spec.rs | 4 +- bin/millau/node/src/service.rs | 65 +- bin/millau/runtime/Cargo.toml | 8 +- bin/millau/runtime/src/lib.rs | 116 +- bin/rialto-parachain/node/Cargo.toml | 1 + bin/rialto-parachain/node/src/service.rs | 30 +- bin/rialto-parachain/runtime/Cargo.toml | 2 - bin/rialto-parachain/runtime/src/lib.rs | 3 - bin/rialto/node/Cargo.toml | 6 +- bin/rialto/node/src/chain_spec.rs | 4 +- bin/rialto/runtime/Cargo.toml | 7 +- bin/rialto/runtime/src/lib.rs | 61 +- bin/runtime-common/src/lib.rs | 3 + bin/runtime-common/src/messages.rs | 180 +- .../src/messages_benchmarking.rs | 7 +- bin/runtime-common/src/messages_generation.rs | 52 +- .../src/messages_xcm_extension.rs | 181 + .../src/parachains_benchmarking.rs | 13 +- .../src/refund_relayer_extension.rs | 6 +- ...estend-to-millau-parachains-dashboard.json | 6 +- modules/beefy/Cargo.toml | 2 +- modules/beefy/src/lib.rs | 4 +- modules/beefy/src/mock.rs | 6 +- modules/beefy/src/mock_chain.rs | 4 +- modules/beefy/src/utils.rs | 2 +- modules/grandpa/Cargo.toml | 4 +- modules/grandpa/src/benchmarking.rs | 15 +- modules/grandpa/src/lib.rs | 14 +- modules/grandpa/src/mock.rs | 2 +- modules/grandpa/src/storage_types.rs | 2 +- modules/grandpa/src/weights.rs | 34 +- modules/messages/src/benchmarking.rs | 4 +- modules/messages/src/lib.rs | 1 - modules/messages/src/mock.rs | 6 +- modules/messages/src/weights.rs | 98 +- modules/parachains/src/benchmarking.rs | 4 +- modules/parachains/src/lib.rs | 15 +- modules/parachains/src/mock.rs | 2 +- modules/parachains/src/weights.rs | 94 +- modules/relayers/src/lib.rs | 1 - modules/relayers/src/weights.rs | 12 +- modules/shift-session-manager/src/lib.rs | 3 +- primitives/beefy/Cargo.toml | 4 +- primitives/beefy/src/lib.rs | 14 +- .../chain-bridge-hub-cumulus/src/lib.rs | 6 +- primitives/chain-westend/src/lib.rs | 2 +- primitives/header-chain/Cargo.toml | 4 +- primitives/header-chain/src/justification.rs | 6 +- primitives/header-chain/src/lib.rs | 16 +- .../tests/implementation_match.rs | 2 +- .../header-chain/tests/justification.rs | 2 +- primitives/messages/src/source_chain.rs | 2 +- primitives/messages/src/target_chain.rs | 2 +- primitives/runtime/src/lib.rs | 31 + primitives/runtime/src/storage_proof.rs | 49 +- primitives/test-utils/Cargo.toml | 4 +- primitives/test-utils/src/keyring.rs | 2 +- primitives/test-utils/src/lib.rs | 4 +- relays/bin-substrate/Cargo.toml | 2 +- .../src/chains/rialto_parachains_to_millau.rs | 6 - relays/bin-substrate/src/chains/rococo.rs | 2 +- .../rococo_parachains_to_bridge_hub_wococo.rs | 6 - .../chains/westend_parachains_to_millau.rs | 6 - relays/bin-substrate/src/chains/wococo.rs | 2 +- .../wococo_parachains_to_bridge_hub_rococo.rs | 6 - relays/bin-substrate/src/cli/bridge.rs | 9 +- .../bin-substrate/src/cli/relay_parachains.rs | 19 +- relays/client-bridge-hub-wococo/Cargo.toml | 2 +- .../src/runtime_wrapper.rs | 2 +- relays/client-rialto-parachain/Cargo.toml | 2 +- .../src/codegen_runtime.rs | 4 +- relays/client-substrate/Cargo.toml | 6 +- relays/client-substrate/src/chain.rs | 2 +- relays/client-substrate/src/client.rs | 84 +- relays/client-substrate/src/error.rs | 67 +- relays/client-substrate/src/test_chain.rs | 49 + relays/finality/Cargo.toml | 2 +- relays/lib-substrate-relay/Cargo.toml | 6 +- relays/lib-substrate-relay/src/error.rs | 2 +- .../src/finality/engine.rs | 6 +- .../src/messages_source.rs | 16 +- .../src/messages_target.rs | 16 +- .../src/on_demand/headers.rs | 13 + .../lib-substrate-relay/src/on_demand/mod.rs | 3 + .../src/on_demand/parachains.rs | 65 +- .../lib-substrate-relay/src/parachains/mod.rs | 3 +- .../src/parachains/source.rs | 42 +- .../src/parachains/target.rs | 94 +- relays/messages/Cargo.toml | 2 +- relays/messages/src/message_lane_loop.rs | 4 +- relays/messages/src/message_race_delivery.rs | 126 +- relays/messages/src/message_race_limits.rs | 20 +- relays/messages/src/message_race_strategy.rs | 162 +- relays/parachains/Cargo.toml | 5 +- relays/parachains/src/lib.rs | 6 +- relays/parachains/src/parachains_loop.rs | 870 ++--- .../parachains/src/parachains_loop_metrics.rs | 43 +- relays/utils/Cargo.toml | 6 +- scripts/update-weights.sh | 2 +- scripts/verify-pallets-build.sh | 6 +- 105 files changed, 3011 insertions(+), 3385 deletions(-) create mode 100644 bin/runtime-common/src/messages_xcm_extension.rs diff --git a/.config/lingua.dic b/.config/lingua.dic index 3e2d83b02dd..3fd4698c604 100644 --- a/.config/lingua.dic +++ b/.config/lingua.dic @@ -195,7 +195,7 @@ shouldn source_at_target source_latest_confirmed source_latest_generated -sp_finality_grandpa +sp_consensus_grandpa spawner sr25519 src diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b232f610690..f8f29a9eb61 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,6 +5,7 @@ stages: - build - publish - publish-docker-description + - deploy variables: GIT_STRATEGY: fetch @@ -77,6 +78,17 @@ default: # this job runs only on nightly pipeline with the mentioned variable, against `master` branch - if: $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "nightly" +.deploy-refs: &deploy-refs + rules: + - if: $CI_PIPELINE_SOURCE == "pipeline" + when: never + - if: $SCHEDULED_JOB + when: never + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + when: manual + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]{4}-[0-9]{2}-[0-9]{2}.*$/ # i.e. v2021-09-27, v2021-09-27-1 + when: manual + .nightly-test: &nightly-test rules: # 2. another is triggered by scripts repo $CI_PIPELINE_SOURCE == "pipeline" it's for the CI image @@ -140,8 +152,9 @@ test: # RUSTFLAGS: "-D warnings" script: &test-script - time cargo fetch - - time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-runtime\").manifest_path"` - - time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"kusama-runtime\").manifest_path"` + # Enable this, when you see: "`cargo metadata` can not fail on project `Cargo.toml`" + #- time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-runtime\").manifest_path"` + #- time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"kusama-runtime\").manifest_path"` - CARGO_NET_OFFLINE=true SKIP_POLKADOT_RUNTIME_WASM_BUILD=1 SKIP_KUSAMA_RUNTIME_WASM_BUILD=1 SKIP_POLKADOT_TEST_RUNTIME_WASM_BUILD=1 time cargo test --verbose --workspace test-nightly: @@ -185,7 +198,8 @@ benchmarks-test: <<: *docker-env <<: *nightly-test script: - - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_messages --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 + - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=RialtoMessages --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 + - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=RialtoParachainMessages --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_grandpa --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_parachains --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_relayers --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 @@ -211,8 +225,9 @@ build: # master script: &build-script - time cargo fetch - - time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-runtime\").manifest_path"` - - time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"kusama-runtime\").manifest_path"` + # Enable this, when you see: "`cargo metadata` can not fail on project `Cargo.toml`" + #- time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-runtime\").manifest_path"` + #- time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"kusama-runtime\").manifest_path"` - CARGO_NET_OFFLINE=true SKIP_POLKADOT_RUNTIME_WASM_BUILD=1 SKIP_KUSAMA_RUNTIME_WASM_BUILD=1 SKIP_POLKADOT_TEST_RUNTIME_WASM_BUILD=1 time cargo build --release --verbose --workspace after_script: # Prepare artifacts @@ -360,4 +375,28 @@ dockerhub-bridges-common-relay: variables: SHORT_DESCRIPTION: "bridges-common-relay" -# FIXME: publish binaries \ No newline at end of file +# FIXME: publish binaries + +deploy-bridges-common-relay-testnet: + <<: *deploy-refs + <<: *kubernetes-build + needs: + - job: bridges-common-relay + stage: deploy + image: argoproj/argocd:v2.5.5 + environment: parity-testnet + variables: + ARGOCD_OPTS: --grpc-web --grpc-web-root-path /parity-testnet + APP: bridges-common-relay + before_script: + - if [[ "${CI_COMMIT_TAG}" ]]; then + VERSION=${CI_COMMIT_TAG}; + elif [[ "${CI_COMMIT_REF_NAME}" ]]; then + VERSION=$(echo ${CI_COMMIT_REF_NAME} | sed -r 's#/+#-#g'); + fi + script: + - echo "Starting deploy version=${VERSION}" + - argocd app list + - argocd app set $APP --helm-set bridges-common-relay.image.tag=$VERSION + - argocd app sync $APP + - argocd app wait $APP --timeout 120 diff --git a/.maintain/millau-weight-template.hbs b/.maintain/millau-weight-template.hbs index 40a9b17bc13..ff70b55aa2c 100644 --- a/.maintain/millau-weight-template.hbs +++ b/.maintain/millau-weight-template.hbs @@ -69,14 +69,10 @@ impl WeightInfo for BridgeWeight { // 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}} nanoseconds. - {{#if (ne benchmark.base_calculated_proof_size "0")}} Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) - {{else}} - Weight::from_ref_time({{underscore benchmark.base_weight}}) - {{/if}} {{#each benchmark.component_weight as |cw|}} // Standard Error: {{underscore cw.error}} - .saturating_add(Weight::from_ref_time({{underscore cw.slope}}).saturating_mul({{cw.name}}.into())) + .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)) @@ -91,7 +87,7 @@ impl WeightInfo for BridgeWeight { .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_proof_size({{cp.slope}}).saturating_mul({{cp.name}}.into())) + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) {{/each}} } {{/each}} @@ -117,14 +113,10 @@ impl WeightInfo for () { // 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}} nanoseconds. - {{#if (ne benchmark.base_calculated_proof_size "0")}} Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) - {{else}} - Weight::from_ref_time({{underscore benchmark.base_weight}}) - {{/if}} {{#each benchmark.component_weight as |cw|}} // Standard Error: {{underscore cw.error}} - .saturating_add(Weight::from_ref_time({{underscore cw.slope}}).saturating_mul({{cw.name}}.into())) + .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)) @@ -139,7 +131,7 @@ impl WeightInfo for () { .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_proof_size({{cp.slope}}).saturating_mul({{cp.name}}.into())) + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) {{/each}} } {{/each}} diff --git a/Cargo.lock b/Cargo.lock index c3c8c9eb97a..bd01089a33d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -279,7 +279,7 @@ checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", "synstructure", ] @@ -291,7 +291,7 @@ checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", "synstructure", ] @@ -303,7 +303,7 @@ checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -325,7 +325,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" dependencies = [ "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -433,13 +433,13 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.64" +version = "0.1.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -549,63 +549,10 @@ dependencies = [ "serde", ] -[[package]] -name = "beefy-gadget" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" -dependencies = [ - "array-bytes 4.2.0", - "async-trait", - "fnv", - "futures", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "sc-client-api", - "sc-consensus", - "sc-keystore", - "sc-network", - "sc-network-common", - "sc-network-gossip", - "sc-utils", - "sp-api", - "sp-application-crypto 7.0.0", - "sp-arithmetic 6.0.0", - "sp-beefy", - "sp-blockchain", - "sp-consensus", - "sp-core 7.0.0", - "sp-keystore 0.13.0", - "sp-mmr-primitives", - "sp-runtime 7.0.0", - "substrate-prometheus-endpoint", - "thiserror", - "wasm-timer", -] - -[[package]] -name = "beefy-gadget-rpc" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" -dependencies = [ - "beefy-gadget", - "futures", - "jsonrpsee", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "sc-rpc", - "serde", - "sp-beefy", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "thiserror", -] - [[package]] name = "binary-merkle-tree" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "hash-db", "log", @@ -668,13 +615,13 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec 0.7.2", - "constant_time_eq 0.1.5", + "constant_time_eq 0.2.4", ] [[package]] @@ -773,9 +720,9 @@ dependencies = [ [[package]] name = "bounded-collections" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de2aff4807e40f478132150d80b031f2461d88f061851afcab537d7600c24120" +checksum = "a071c348a5ef6da1d3a87166b408170b46002382b1dda83992b5c2208cefb370" dependencies = [ "log", "parity-scale-codec", @@ -785,9 +732,9 @@ dependencies = [ [[package]] name = "bounded-vec" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3372be4090bf9d4da36bd8ba7ce6ca1669503d0cf6e667236c6df7f053153eb6" +checksum = "68534a48cbf63a4b1323c433cf21238c9ec23711e0df13b08c33e5c2082663ce" dependencies = [ "thiserror", ] @@ -804,8 +751,8 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-beefy", - "sp-runtime 7.0.0", + "sp-consensus-beefy", + "sp-runtime", "sp-std 5.0.0", ] @@ -860,9 +807,9 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 7.0.0", - "sp-finality-grandpa", - "sp-runtime 7.0.0", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", "sp-std 5.0.0", ] @@ -888,7 +835,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 7.0.0", + "sp-core", "sp-std 5.0.0", ] @@ -910,11 +857,11 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", - "sp-trie 7.0.0", + "sp-trie", ] [[package]] @@ -928,8 +875,8 @@ dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "sp-std 5.0.0", ] @@ -957,8 +904,8 @@ dependencies = [ "parity-util-mem", "scale-info", "serde", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "sp-std 5.0.0", ] @@ -973,7 +920,7 @@ dependencies = [ "hex-literal", "parity-scale-codec", "scale-info", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] @@ -987,8 +934,8 @@ dependencies = [ "frame-support", "frame-system", "sp-api", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "sp-std 5.0.0", ] @@ -1002,8 +949,8 @@ dependencies = [ "frame-support", "frame-system", "sp-api", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "sp-std 5.0.0", ] @@ -1031,13 +978,13 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", "sp-std 5.0.0", - "sp-trie 7.0.0", - "trie-db 0.26.0", + "sp-trie", + "trie-db", ] [[package]] @@ -1048,9 +995,9 @@ dependencies = [ "ed25519-dalek", "finality-grandpa", "parity-scale-codec", - "sp-application-crypto 7.0.0", - "sp-finality-grandpa", - "sp-runtime 7.0.0", + "sp-application-crypto", + "sp-consensus-grandpa", + "sp-runtime", "sp-std 5.0.0", ] @@ -1103,11 +1050,11 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-api", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", - "sp-trie 7.0.0", + "sp-trie", "static_assertions", "xcm", "xcm-builder", @@ -1206,15 +1153,16 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.14.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" dependencies = [ "camino", "cargo-platform", "semver 1.0.16", "serde", "serde_json", + "thiserror", ] [[package]] @@ -1396,9 +1344,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.6" +version = "4.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3" +checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" dependencies = [ "bitflags", "clap_derive", @@ -1411,15 +1359,15 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.1.0" +version = "4.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" dependencies = [ "heck 0.4.1", "proc-macro-error", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1558,18 +1506,18 @@ checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" [[package]] name = "cranelift-bforest" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52056f6d0584484b57fa6c1a65c1fcb15f3780d8b6a758426d9e3084169b2ddd" +checksum = "a7379abaacee0f14abf3204a7606118f0465785252169d186337bcb75030815a" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fed94c8770dc25d01154c3ffa64ed0b3ba9d583736f305fed7beebe5d9cf74" +checksum = "9489fa336927df749631f1008007ced2871068544f40a202ce6d93fbf2366a7b" dependencies = [ "arrayvec 0.7.2", "bumpalo", @@ -1579,6 +1527,7 @@ dependencies = [ "cranelift-entity", "cranelift-isle", "gimli 0.26.2", + "hashbrown 0.12.3", "log", "regalloc2", "smallvec", @@ -1587,33 +1536,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c451b81faf237d11c7e4f3165eeb6bac61112762c5cfe7b4c0fb7241474358f" +checksum = "05bbb67da91ec721ed57cef2f7c5ef7728e1cd9bde9ffd3ef8601022e73e3239" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c940133198426d26128f08be2b40b0bd117b84771fd36798969c4d712d81fc" +checksum = "418ecb2f36032f6665dc1a5e2060a143dbab41d83b784882e97710e890a7a16d" [[package]] name = "cranelift-entity" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a0f1b2fdc18776956370cf8d9b009ded3f855350c480c1c52142510961f352" +checksum = "7cf583f7b093f291005f9fb1323e2c37f6ee4c7909e39ce016b2e8360d461705" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34897538b36b216cc8dd324e73263596d51b8cf610da6498322838b2546baf8a" +checksum = "0b66bf9e916f57fbbd0f7703ec6286f4624866bf45000111627c70d272c8dda1" dependencies = [ "cranelift-codegen", "log", @@ -1623,15 +1572,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b2629a569fae540f16a76b70afcc87ad7decb38dc28fa6c648ac73b51e78470" +checksum = "649782a39ce99798dd6b4029e2bb318a2fbeaade1b4fa25330763c10c65bc358" [[package]] name = "cranelift-native" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20937dab4e14d3e225c5adfc9c7106bafd4ac669bdb43027b911ff794c6fb318" +checksum = "937e021e089c51f9749d09e7ad1c4f255c2f8686cb8c3df63a34b3ec9921bc41" dependencies = [ "cranelift-codegen", "libc", @@ -1640,9 +1589,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.88.2" +version = "0.93.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80fc2288957a94fd342a015811479de1837850924166d1f1856d8406e6f3609b" +checksum = "d850cf6775477747c9dfda9ae23355dd70512ffebc70cf82b85a5b111ae668b5" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1796,7 +1745,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1820,22 +1769,22 @@ dependencies = [ [[package]] name = "cumulus-client-cli" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ - "clap 4.1.6", + "clap 4.1.8", "parity-scale-codec", "sc-chain-spec", "sc-cli", "sc-service", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "url", ] [[package]] name = "cumulus-client-collator" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "cumulus-client-consensus-common", "cumulus-client-network", @@ -1850,15 +1799,15 @@ dependencies = [ "sc-client-api", "sp-api", "sp-consensus", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "tracing", ] [[package]] name = "cumulus-client-consensus-aura" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "async-trait", "cumulus-client-consensus-common", @@ -1871,15 +1820,15 @@ dependencies = [ "sc-consensus-slots", "sc-telemetry", "sp-api", - "sp-application-crypto 7.0.0", + "sp-application-crypto", "sp-block-builder", "sp-blockchain", "sp-consensus", "sp-consensus-aura", - "sp-core 7.0.0", + "sp-core", "sp-inherents", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", + "sp-keystore", + "sp-runtime", "substrate-prometheus-endpoint", "tracing", ] @@ -1887,7 +1836,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-common" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "async-trait", "cumulus-client-pov-recovery", @@ -1900,17 +1849,18 @@ dependencies = [ "polkadot-primitives", "sc-client-api", "sc-consensus", + "schnellru", "sp-blockchain", "sp-consensus", - "sp-runtime 7.0.0", - "sp-trie 7.0.0", + "sp-runtime", + "sp-trie", "tracing", ] [[package]] name = "cumulus-client-network" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "async-trait", "cumulus-relay-chain-interface", @@ -1924,17 +1874,18 @@ dependencies = [ "sc-client-api", "sp-blockchain", "sp-consensus", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", + "sp-core", + "sp-runtime", + "sp-state-machine", "tracing", ] [[package]] name = "cumulus-client-pov-recovery" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ + "async-trait", "cumulus-primitives-core", "cumulus-relay-chain-interface", "futures", @@ -1949,58 +1900,66 @@ dependencies = [ "sc-consensus", "sp-consensus", "sp-maybe-compressed-blob", - "sp-runtime 7.0.0", + "sp-runtime", "tracing", ] [[package]] name = "cumulus-client-service" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-common", + "cumulus-client-network", "cumulus-client-pov-recovery", "cumulus-primitives-core", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "futures", - "parking_lot 0.12.1", "polkadot-primitives", "sc-client-api", "sc-consensus", + "sc-network", + "sc-network-common", + "sc-network-sync", + "sc-network-transactions", + "sc-rpc", "sc-service", "sc-sysinfo", "sc-telemetry", + "sc-transaction-pool", + "sc-utils", "sp-api", "sp-blockchain", "sp-consensus", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", + "sp-transaction-pool", ] [[package]] name = "cumulus-pallet-aura-ext" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "frame-support", "frame-system", "pallet-aura", "parity-scale-codec", "scale-info", - "sp-application-crypto 7.0.0", + "sp-application-crypto", "sp-consensus-aura", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "cumulus-pallet-dmp-queue" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -2008,8 +1967,8 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-runtime", "sp-std 5.0.0", "xcm", ] @@ -2017,7 +1976,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-parachain-system" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "bytes", "cumulus-pallet-parachain-system-proc-macro", @@ -2031,14 +1990,14 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain", "scale-info", - "sp-core 7.0.0", - "sp-externalities 0.13.0", + "sp-core", + "sp-externalities", "sp-inherents", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", + "sp-io", + "sp-runtime", + "sp-state-machine", "sp-std 5.0.0", - "sp-trie 7.0.0", + "sp-trie", "sp-version", "xcm", ] @@ -2046,26 +2005,26 @@ dependencies = [ [[package]] name = "cumulus-pallet-parachain-system-proc-macro" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "cumulus-pallet-xcm" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "cumulus-primitives-core", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-runtime", "sp-std 5.0.0", "xcm", ] @@ -2073,7 +2032,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-xcmp-queue" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -2083,8 +2042,8 @@ dependencies = [ "polkadot-runtime-common", "rand_chacha 0.3.1", "scale-info", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-runtime", "sp-std 5.0.0", "xcm", "xcm-executor", @@ -2093,23 +2052,23 @@ dependencies = [ [[package]] name = "cumulus-primitives-core" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "parity-scale-codec", "polkadot-core-primitives", "polkadot-parachain", "polkadot-primitives", "sp-api", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", - "sp-trie 7.0.0", + "sp-trie", "xcm", ] [[package]] name = "cumulus-primitives-parachain-inherent" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2119,20 +2078,20 @@ dependencies = [ "sc-client-api", "scale-info", "sp-api", - "sp-core 7.0.0", + "sp-core", "sp-inherents", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", + "sp-runtime", + "sp-state-machine", "sp-std 5.0.0", - "sp-storage 7.0.0", - "sp-trie 7.0.0", + "sp-storage", + "sp-trie", "tracing", ] [[package]] name = "cumulus-primitives-timestamp" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "cumulus-primitives-core", "futures", @@ -2145,7 +2104,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-inprocess-interface" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2162,15 +2121,15 @@ dependencies = [ "sc-tracing", "sp-api", "sp-consensus", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", + "sp-core", + "sp-runtime", + "sp-state-machine", ] [[package]] name = "cumulus-relay-chain-interface" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2178,19 +2137,17 @@ dependencies = [ "jsonrpsee-core", "parity-scale-codec", "polkadot-overseer", - "polkadot-service", "sc-client-api", "sp-api", "sp-blockchain", - "sp-state-machine 0.13.0", + "sp-state-machine", "thiserror", - "tokio", ] [[package]] name = "cumulus-relay-chain-minimal-node" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "array-bytes 6.0.0", "async-trait", @@ -2208,29 +2165,23 @@ dependencies = [ "polkadot-service", "sc-authority-discovery", "sc-client-api", - "sc-consensus", - "sc-keystore", "sc-network", "sc-network-common", "sc-service", - "sc-telemetry", "sc-tracing", - "sc-transaction-pool", - "sc-transaction-pool-api", "sp-api", "sp-blockchain", "sp-consensus", "sp-consensus-babe", - "sp-runtime 7.0.0", + "sp-runtime", "tokio", "tracing", - "url", ] [[package]] name = "cumulus-relay-chain-rpc-interface" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2240,17 +2191,18 @@ dependencies = [ "jsonrpsee", "lru 0.9.0", "parity-scale-codec", - "polkadot-service", + "polkadot-overseer", "sc-client-api", "sc-rpc-api", + "sc-service", "serde", "serde_json", "sp-api", "sp-authority-discovery", "sp-consensus-babe", - "sp-core 7.0.0", - "sp-state-machine 0.13.0", - "sp-storage 7.0.0", + "sp-core", + "sp-state-machine", + "sp-storage", "tokio", "tracing", "url", @@ -2259,13 +2211,13 @@ dependencies = [ [[package]] name = "cumulus-test-relay-sproof-builder" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "cumulus-primitives-core", "parity-scale-codec", "polkadot-primitives", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", + "sp-runtime", + "sp-state-machine", "sp-std 5.0.0", ] @@ -2364,7 +2316,7 @@ dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", "scratch", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2381,14 +2333,14 @@ checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "darling" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8" dependencies = [ "darling_core", "darling_macro", @@ -2396,27 +2348,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" dependencies = [ "fnv", "ident_case", "proc-macro2 1.0.51", "quote 1.0.23", "strsim 0.10.0", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" dependencies = [ "darling_core", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2442,7 +2394,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" dependencies = [ "data-encoding", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2492,7 +2444,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2503,7 +2455,7 @@ checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2524,7 +2476,7 @@ dependencies = [ "darling", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2534,7 +2486,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" dependencies = [ "derive_builder_core", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2546,8 +2498,8 @@ dependencies = [ "convert_case", "proc-macro2 1.0.51", "quote 1.0.23", - "rustc_version 0.4.0", - "syn 1.0.107", + "rustc_version", + "syn 1.0.109", ] [[package]] @@ -2634,7 +2586,7 @@ checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2673,14 +2625,14 @@ checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "dyn-clone" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" [[package]] name = "ecdsa" @@ -2777,7 +2729,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2797,7 +2749,7 @@ checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2808,20 +2760,7 @@ checksum = "e88bcb3a067a6555d577aba299e75eff9942da276e6506fc6274327daa026132" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", -] - -[[package]] -name = "env_logger" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", + "syn 1.0.109", ] [[package]] @@ -2928,7 +2867,7 @@ dependencies = [ "fs-err", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2973,7 +2912,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", "thiserror", ] @@ -3008,7 +2947,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" dependencies = [ - "env_logger 0.10.0", + "env_logger", "log", ] @@ -3102,7 +3041,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "parity-scale-codec", ] @@ -3125,7 +3064,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-support", "frame-support-procedural", @@ -3137,25 +3076,25 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-application-crypto 7.0.0", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-runtime-interface 7.0.0", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", "sp-std 5.0.0", - "sp-storage 7.0.0", + "sp-storage", "static_assertions", ] [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "Inflector", "array-bytes 4.2.0", "chrono", - "clap 4.1.6", + "clap 4.1.8", "comfy-table", "frame-benchmarking", "frame-support", @@ -3180,16 +3119,16 @@ dependencies = [ "serde_json", "sp-api", "sp-blockchain", - "sp-core 7.0.0", + "sp-core", "sp-database", - "sp-externalities 0.13.0", + "sp-externalities", "sp-inherents", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", + "sp-keystore", + "sp-runtime", + "sp-state-machine", "sp-std 5.0.0", - "sp-storage 7.0.0", - "sp-trie 7.0.0", + "sp-storage", + "sp-trie", "thiserror", "thousands", ] @@ -3197,45 +3136,45 @@ dependencies = [ [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-election-provider-solution-type", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-arithmetic 6.0.0", - "sp-core 7.0.0", + "sp-arithmetic", + "sp-core", "sp-npos-elections", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", - "sp-tracing 6.0.0", + "sp-tracing", ] [[package]] @@ -3253,15 +3192,15 @@ dependencies = [ [[package]] name = "frame-remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "futures", "log", "parity-scale-codec", "serde", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "substrate-rpc-client", "tokio", ] @@ -3269,9 +3208,10 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "bitflags", + "environmental", "frame-metadata", "frame-support-procedural", "impl-trait-for-tuples", @@ -3284,24 +3224,24 @@ dependencies = [ "serde", "smallvec", "sp-api", - "sp-arithmetic 6.0.0", - "sp-core 7.0.0", + "sp-arithmetic", + "sp-core", "sp-core-hashing-proc-macro", "sp-inherents", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-runtime", "sp-staking", - "sp-state-machine 0.13.0", + "sp-state-machine", "sp-std 5.0.0", - "sp-tracing 6.0.0", - "sp-weights 4.0.0", + "sp-tracing", + "sp-weights", "tt-call", ] [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "Inflector", "cfg-expr", @@ -3310,68 +3250,68 @@ dependencies = [ "itertools", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-support", "log", "parity-scale-codec", "scale-info", "serde", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", "sp-version", - "sp-weights 4.0.0", + "sp-weights", ] [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "parity-scale-codec", "sp-api", @@ -3380,12 +3320,12 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-support", "parity-scale-codec", "sp-api", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] @@ -3405,6 +3345,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "fs4" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea55201cc351fdb478217c0fb641b59813da9b4efe4c414a9d8f989a657d149" +dependencies = [ + "libc", + "rustix 0.35.13", + "winapi", +] + [[package]] name = "funty" version = "2.0.0" @@ -3413,9 +3364,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" dependencies = [ "futures-channel", "futures-core", @@ -3428,9 +3379,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" dependencies = [ "futures-core", "futures-sink", @@ -3438,15 +3389,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" dependencies = [ "futures-core", "futures-task", @@ -3456,9 +3407,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" [[package]] name = "futures-lite" @@ -3477,13 +3428,13 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -3499,15 +3450,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" [[package]] name = "futures-timer" @@ -3517,9 +3468,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" dependencies = [ "futures-channel", "futures-core", @@ -3859,7 +3810,7 @@ dependencies = [ "arbitrary", "lazy_static", "memmap2", - "rustc_version 0.4.0", + "rustc_version", ] [[package]] @@ -4074,7 +4025,7 @@ checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -4348,7 +4299,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -4420,109 +4371,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "kusama-runtime" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" -dependencies = [ - "bitvec", - "frame-election-provider-support", - "frame-executive", - "frame-support", - "frame-system", - "frame-system-rpc-runtime-api", - "frame-try-runtime", - "kusama-runtime-constants", - "log", - "pallet-authority-discovery", - "pallet-authorship", - "pallet-babe", - "pallet-bags-list", - "pallet-balances", - "pallet-bounties", - "pallet-child-bounties", - "pallet-collective", - "pallet-conviction-voting", - "pallet-democracy", - "pallet-election-provider-multi-phase", - "pallet-elections-phragmen", - "pallet-fast-unstake", - "pallet-grandpa", - "pallet-identity", - "pallet-im-online", - "pallet-indices", - "pallet-membership", - "pallet-multisig", - "pallet-nis", - "pallet-nomination-pools", - "pallet-nomination-pools-runtime-api", - "pallet-offences", - "pallet-preimage", - "pallet-proxy", - "pallet-ranked-collective", - "pallet-recovery", - "pallet-referenda", - "pallet-scheduler", - "pallet-session", - "pallet-society", - "pallet-staking", - "pallet-timestamp", - "pallet-tips", - "pallet-transaction-payment", - "pallet-transaction-payment-rpc-runtime-api", - "pallet-treasury", - "pallet-utility", - "pallet-vesting", - "pallet-whitelist", - "pallet-xcm", - "parity-scale-codec", - "polkadot-primitives", - "polkadot-runtime-common", - "polkadot-runtime-parachains", - "rustc-hex", - "scale-info", - "serde", - "serde_derive", - "smallvec", - "sp-api", - "sp-arithmetic 6.0.0", - "sp-authority-discovery", - "sp-beefy", - "sp-block-builder", - "sp-consensus-babe", - "sp-core 7.0.0", - "sp-inherents", - "sp-io 7.0.0", - "sp-mmr-primitives", - "sp-npos-elections", - "sp-offchain", - "sp-runtime 7.0.0", - "sp-session", - "sp-staking", - "sp-std 5.0.0", - "sp-transaction-pool", - "sp-version", - "static_assertions", - "substrate-wasm-builder", - "xcm", - "xcm-builder", - "xcm-executor", -] - -[[package]] -name = "kusama-runtime-constants" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" -dependencies = [ - "frame-support", - "polkadot-primitives", - "polkadot-runtime-common", - "smallvec", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-weights 4.0.0", -] - [[package]] name = "kv-log-macro" version = "1.0.7" @@ -4906,7 +4754,7 @@ checksum = "9d527d5827582abd44a6d80c07ff8b50b4ee238a8979e05998474179e79dc400" dependencies = [ "heck 0.4.1", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -5340,7 +5188,7 @@ dependencies = [ "num-traits", "parking_lot 0.12.1", "relay-utils", - "sp-arithmetic 6.0.0", + "sp-arithmetic", ] [[package]] @@ -5358,9 +5206,7 @@ dependencies = [ name = "millau-bridge-node" version = "0.1.0" dependencies = [ - "beefy-gadget", - "beefy-gadget-rpc", - "clap 4.1.6", + "clap 4.1.8", "frame-benchmarking", "frame-benchmarking-cli", "jsonrpsee", @@ -5373,20 +5219,23 @@ dependencies = [ "sc-client-api", "sc-consensus", "sc-consensus-aura", + "sc-consensus-beefy", + "sc-consensus-beefy-rpc", + "sc-consensus-grandpa", + "sc-consensus-grandpa-rpc", "sc-executor", - "sc-finality-grandpa", - "sc-finality-grandpa-rpc", "sc-keystore", + "sc-network-common", "sc-rpc", "sc-service", "sc-telemetry", "sc-transaction-pool", "serde_json", - "sp-beefy", "sp-consensus-aura", - "sp-core 7.0.0", - "sp-finality-grandpa", - "sp-runtime 7.0.0", + "sp-consensus-beefy", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", "sp-timestamp", "substrate-build-script-utils", "substrate-frame-rpc-system", @@ -5406,7 +5255,7 @@ dependencies = [ "bp-runtime", "bp-westend", "bridge-runtime-common", - "env_logger 0.10.0", + "env_logger", "frame-benchmarking", "frame-executive", "frame-support", @@ -5423,7 +5272,6 @@ dependencies = [ "pallet-bridge-relayers", "pallet-grandpa", "pallet-mmr", - "pallet-randomness-collective-flip", "pallet-session", "pallet-shift-session-manager", "pallet-sudo", @@ -5435,14 +5283,14 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-api", - "sp-beefy", "sp-block-builder", "sp-consensus-aura", - "sp-core 7.0.0", + "sp-consensus-beefy", + "sp-core", "sp-inherents", - "sp-io 7.0.0", + "sp-io", "sp-offchain", - "sp-runtime 7.0.0", + "sp-runtime", "sp-session", "sp-std 5.0.0", "sp-transaction-pool", @@ -5490,7 +5338,7 @@ dependencies = [ [[package]] name = "mmr-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "futures", "log", @@ -5498,18 +5346,18 @@ dependencies = [ "sc-client-api", "sc-offchain", "sp-api", - "sp-beefy", "sp-blockchain", "sp-consensus", - "sp-core 7.0.0", + "sp-consensus-beefy", + "sp-core", "sp-mmr-primitives", - "sp-runtime 7.0.0", + "sp-runtime", ] [[package]] name = "mmr-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "anyhow", "jsonrpsee", @@ -5517,9 +5365,9 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core 7.0.0", + "sp-core", "sp-mmr-primitives", - "sp-runtime 7.0.0", + "sp-runtime", ] [[package]] @@ -5546,7 +5394,7 @@ dependencies = [ "cfg-if 1.0.0", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -5605,7 +5453,7 @@ dependencies = [ "proc-macro-error", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", "synstructure", ] @@ -5653,7 +5501,7 @@ checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -5749,34 +5597,20 @@ dependencies = [ "memoffset 0.6.5", ] -[[package]] -name = "nix" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" -dependencies = [ - "bitflags", - "cfg-if 1.0.0", - "libc", - "memoffset 0.7.1", - "pin-utils", - "static_assertions", -] - [[package]] name = "node-inspect" version = "0.9.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ - "clap 4.1.6", + "clap 4.1.8", "parity-scale-codec", "sc-cli", "sc-client-api", "sc-executor", "sc-service", "sp-blockchain", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "thiserror", ] @@ -5796,15 +5630,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nom8" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" -dependencies = [ - "memchr", -] - [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -6005,7 +5830,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -6058,53 +5883,53 @@ dependencies = [ [[package]] name = "pallet-aura" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-support", "frame-system", "pallet-timestamp", "parity-scale-codec", "scale-info", - "sp-application-crypto 7.0.0", + "sp-application-crypto", "sp-consensus-aura", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-support", "frame-system", "pallet-session", "parity-scale-codec", "scale-info", - "sp-application-crypto 7.0.0", + "sp-application-crypto", "sp-authority-discovery", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-support", "frame-system", "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6115,11 +5940,11 @@ dependencies = [ "pallet-timestamp", "parity-scale-codec", "scale-info", - "sp-application-crypto 7.0.0", + "sp-application-crypto", "sp-consensus-babe", "sp-consensus-vrf", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-runtime", "sp-session", "sp-staking", "sp-std 5.0.0", @@ -6128,7 +5953,7 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6138,17 +5963,17 @@ dependencies = [ "pallet-balances", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", - "sp-tracing 6.0.0", + "sp-tracing", ] [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6156,30 +5981,33 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-support", "frame-system", + "pallet-authorship", "pallet-session", "parity-scale-codec", "scale-info", "serde", - "sp-beefy", - "sp-runtime 7.0.0", + "sp-consensus-beefy", + "sp-runtime", + "sp-session", + "sp-staking", "sp-std 5.0.0", ] [[package]] name = "pallet-beefy-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "array-bytes 4.2.0", "binary-merkle-tree", @@ -6193,17 +6021,17 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-beefy", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-consensus-beefy", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6212,9 +6040,9 @@ dependencies = [ "pallet-treasury", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] @@ -6235,10 +6063,10 @@ dependencies = [ "rand 0.8.5", "scale-info", "serde", - "sp-beefy", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-consensus-beefy", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] @@ -6256,12 +6084,12 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-finality-grandpa", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-consensus-grandpa", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", - "sp-trie 7.0.0", + "sp-trie", ] [[package]] @@ -6279,9 +6107,9 @@ dependencies = [ "pallet-balances", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] @@ -6301,11 +6129,11 @@ dependencies = [ "pallet-bridge-grandpa", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", - "sp-trie 7.0.0", + "sp-trie", ] [[package]] @@ -6323,17 +6151,17 @@ dependencies = [ "pallet-bridge-messages", "parity-scale-codec", "scale-info", - "sp-arithmetic 6.0.0", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6343,16 +6171,16 @@ dependencies = [ "pallet-treasury", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6360,33 +6188,16 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "pallet-conviction-voting" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" -dependencies = [ - "assert_matches", - "frame-benchmarking", - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "serde", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6395,16 +6206,16 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6415,11 +6226,11 @@ dependencies = [ "parity-scale-codec", "rand 0.8.5", "scale-info", - "sp-arithmetic 6.0.0", - "sp-core 7.0.0", - "sp-io 7.0.0", + "sp-arithmetic", + "sp-core", + "sp-io", "sp-npos-elections", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", "strum", ] @@ -6427,20 +6238,20 @@ dependencies = [ [[package]] name = "pallet-election-provider-support-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-election-provider-support", "frame-system", "parity-scale-codec", "sp-npos-elections", - "sp-runtime 7.0.0", + "sp-runtime", ] [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6448,17 +6259,17 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", + "sp-core", + "sp-io", "sp-npos-elections", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-fast-unstake" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6467,8 +6278,8 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-runtime", "sp-staking", "sp-std 5.0.0", ] @@ -6476,7 +6287,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6486,11 +6297,11 @@ dependencies = [ "pallet-session", "parity-scale-codec", "scale-info", - "sp-application-crypto 7.0.0", - "sp-core 7.0.0", - "sp-finality-grandpa", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-application-crypto", + "sp-consensus-grandpa", + "sp-core", + "sp-io", + "sp-runtime", "sp-session", "sp-staking", "sp-std 5.0.0", @@ -6499,7 +6310,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "enumflags2", "frame-benchmarking", @@ -6507,15 +6318,15 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6524,10 +6335,10 @@ dependencies = [ "pallet-authorship", "parity-scale-codec", "scale-info", - "sp-application-crypto 7.0.0", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", "sp-staking", "sp-std 5.0.0", ] @@ -6535,24 +6346,24 @@ dependencies = [ [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", + "sp-core", + "sp-io", "sp-keyring", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6560,33 +6371,33 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", + "sp-core", + "sp-io", "sp-mmr-primitives", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6594,40 +6405,24 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "pallet-nis" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "sp-arithmetic 6.0.0", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-nomination-pools" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-support", "frame-system", "log", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-staking", "sp-std 5.0.0", ] @@ -6635,8 +6430,9 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ + "pallet-nomination-pools", "parity-scale-codec", "sp-api", "sp-std 5.0.0", @@ -6645,7 +6441,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-support", "frame-system", @@ -6654,7 +6450,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-runtime 7.0.0", + "sp-runtime", "sp-staking", "sp-std 5.0.0", ] @@ -6662,7 +6458,7 @@ dependencies = [ [[package]] name = "pallet-preimage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6670,219 +6466,149 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] [[package]] -name = "pallet-randomness-collective-flip" +name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", + "log", "parity-scale-codec", - "safe-mix", "scale-info", - "sp-runtime 7.0.0", + "sp-io", + "sp-runtime", "sp-std 5.0.0", + "sp-weights", ] [[package]] -name = "pallet-ranked-collective" +name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ - "frame-benchmarking", "frame-support", "frame-system", + "impl-trait-for-tuples", "log", + "pallet-timestamp", "parity-scale-codec", "scale-info", - "sp-arithmetic 6.0.0", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", "sp-std 5.0.0", + "sp-trie", ] [[package]] -name = "pallet-recovery" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +name = "pallet-shift-session-manager" +version = "0.1.0" dependencies = [ - "frame-benchmarking", "frame-support", "frame-system", + "pallet-session", "parity-scale-codec", "scale-info", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", + "sp-staking", "sp-std 5.0.0", ] [[package]] -name = "pallet-referenda" +name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", + "frame-election-provider-support", "frame-support", "frame-system", "log", + "pallet-authorship", + "pallet-session", "parity-scale-codec", "scale-info", "serde", - "sp-arithmetic 6.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-application-crypto", + "sp-io", + "sp-runtime", + "sp-staking", "sp-std 5.0.0", ] [[package]] -name = "pallet-scheduler" +name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "parity-scale-codec", - "scale-info", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", - "sp-weights 4.0.0", + "proc-macro-crate", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] -name = "pallet-session" +name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ - "frame-support", - "frame-system", - "impl-trait-for-tuples", "log", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-session", - "sp-staking", - "sp-std 5.0.0", - "sp-trie 7.0.0", -] - -[[package]] -name = "pallet-shift-session-manager" -version = "0.1.0" -dependencies = [ - "frame-support", - "frame-system", - "pallet-session", - "parity-scale-codec", - "scale-info", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-staking", - "sp-std 5.0.0", -] - -[[package]] -name = "pallet-society" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" -dependencies = [ - "frame-support", - "frame-system", - "parity-scale-codec", - "rand_chacha 0.2.2", - "scale-info", - "sp-runtime 7.0.0", - "sp-std 5.0.0", + "sp-arithmetic", ] [[package]] -name = "pallet-staking" +name = "pallet-staking-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ - "frame-benchmarking", - "frame-election-provider-support", - "frame-support", - "frame-system", - "log", - "pallet-authorship", - "pallet-session", "parity-scale-codec", - "scale-info", - "serde", - "sp-application-crypto 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", - "sp-staking", - "sp-std 5.0.0", -] - -[[package]] -name = "pallet-staking-reward-curve" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" -dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.107", -] - -[[package]] -name = "pallet-staking-reward-fn" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" -dependencies = [ - "log", - "sp-arithmetic 6.0.0", + "sp-api", ] [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6891,8 +6617,8 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-inherents", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-runtime", "sp-std 5.0.0", "sp-timestamp", ] @@ -6900,7 +6626,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6910,60 +6636,60 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-support", "frame-system", "parity-scale-codec", "scale-info", "serde", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", "sp-api", "sp-blockchain", - "sp-core 7.0.0", + "sp-core", "sp-rpc", - "sp-runtime 7.0.0", - "sp-weights 4.0.0", + "sp-runtime", + "sp-weights", ] [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", "sp-api", - "sp-runtime 7.0.0", - "sp-weights 4.0.0", + "sp-runtime", + "sp-weights", ] [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -6973,30 +6699,30 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-benchmarking", "frame-support", @@ -7004,30 +6730,16 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "sp-runtime 7.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "pallet-whitelist" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "sp-api", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "pallet-xcm" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ + "bounded-collections", "frame-benchmarking", "frame-support", "frame-system", @@ -7035,9 +6747,9 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", "xcm", "xcm-executor", @@ -7046,7 +6758,7 @@ dependencies = [ [[package]] name = "parachain-info" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#445f9277ab55b4d930ced4fbbb38d27c617c6658" +source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -7061,21 +6773,20 @@ version = "0.1.0" dependencies = [ "async-std", "async-trait", - "bp-parachains", "bp-polkadot-core", "futures", "log", "parity-scale-codec", "relay-substrate-client", "relay-utils", - "sp-core 7.0.0", + "sp-core", ] [[package]] name = "parity-db" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd684a725651d9588ef21f140a328b6b4f64e646b2e931f3e6f14f75eedf9980" +checksum = "df89dd8311063c54ae4e03d9aeb597b04212a57e82c339344130a9cad9b3e2d9" dependencies = [ "blake2", "crc32fast", @@ -7087,14 +6798,15 @@ dependencies = [ "memmap2", "parking_lot 0.12.1", "rand 0.8.5", + "siphasher", "snap", ] [[package]] name = "parity-scale-codec" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3840933452adf7b3b9145e27086a5a3376c619dca1a21b1e5a5af0d54979bed" +checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" dependencies = [ "arrayvec 0.7.2", "bitvec", @@ -7114,7 +6826,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -7148,7 +6860,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ "proc-macro2 1.0.51", - "syn 1.0.107", + "syn 1.0.109", "synstructure", ] @@ -7296,7 +7008,7 @@ dependencies = [ "pest_meta", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -7337,7 +7049,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -7389,7 +7101,7 @@ checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" [[package]] name = "polkadot-approval-distribution" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "futures", "polkadot-node-metrics", @@ -7404,7 +7116,7 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "futures", "polkadot-node-network-protocol", @@ -7418,7 +7130,7 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "derive_more", "fatality", @@ -7432,8 +7144,8 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "rand 0.8.5", - "sp-core 7.0.0", - "sp-keystore 0.13.0", + "sp-core", + "sp-keystore", "thiserror", "tracing-gum", ] @@ -7441,7 +7153,7 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "fatality", "futures", @@ -7462,16 +7174,15 @@ dependencies = [ [[package]] name = "polkadot-cli" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ - "clap 4.1.6", + "clap 4.1.8", "frame-benchmarking-cli", "futures", "log", "polkadot-client", "polkadot-node-core-pvf", "polkadot-node-metrics", - "polkadot-performance-test", "polkadot-service", "sc-cli", "sc-executor", @@ -7479,8 +7190,8 @@ dependencies = [ "sc-storage-monitor", "sc-sysinfo", "sc-tracing", - "sp-core 7.0.0", - "sp-io 7.0.0", + "sp-core", + "sp-io", "sp-keyring", "substrate-build-script-utils", "thiserror", @@ -7490,7 +7201,7 @@ dependencies = [ [[package]] name = "polkadot-client" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "async-trait", "frame-benchmarking", @@ -7511,20 +7222,20 @@ dependencies = [ "sc-service", "sp-api", "sp-authority-discovery", - "sp-beefy", "sp-block-builder", "sp-blockchain", "sp-consensus", "sp-consensus-babe", - "sp-core 7.0.0", - "sp-finality-grandpa", + "sp-consensus-beefy", + "sp-consensus-grandpa", + "sp-core", "sp-inherents", "sp-keyring", "sp-mmr-primitives", "sp-offchain", - "sp-runtime 7.0.0", + "sp-runtime", "sp-session", - "sp-storage 7.0.0", + "sp-storage", "sp-timestamp", "sp-transaction-pool", ] @@ -7532,7 +7243,7 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "always-assert", "bitvec", @@ -7544,9 +7255,9 @@ dependencies = [ "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives", - "sp-core 7.0.0", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-keystore", + "sp-runtime", "thiserror", "tracing-gum", ] @@ -7554,19 +7265,19 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "polkadot-dispute-distribution" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "derive_more", "fatality", @@ -7582,8 +7293,8 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "sc-network", - "sp-application-crypto 7.0.0", - "sp-keystore 0.13.0", + "sp-application-crypto", + "sp-keystore", "thiserror", "tracing-gum", ] @@ -7591,21 +7302,21 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", "polkadot-primitives", "reed-solomon-novelpoly", - "sp-core 7.0.0", - "sp-trie 7.0.0", + "sp-core", + "sp-trie", "thiserror", ] [[package]] name = "polkadot-gossip-support" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "futures", "futures-timer", @@ -7616,16 +7327,16 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "sc-network", - "sp-application-crypto 7.0.0", - "sp-core 7.0.0", - "sp-keystore 0.13.0", + "sp-application-crypto", + "sp-core", + "sp-keystore", "tracing-gum", ] [[package]] name = "polkadot-network-bridge" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "always-assert", "async-trait", @@ -7649,7 +7360,7 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "futures", "parity-scale-codec", @@ -7658,7 +7369,7 @@ dependencies = [ "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives", - "sp-core 7.0.0", + "sp-core", "sp-maybe-compressed-blob", "thiserror", "tracing-gum", @@ -7667,7 +7378,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "bitvec", "derive_more", @@ -7685,10 +7396,10 @@ dependencies = [ "polkadot-primitives", "sc-keystore", "schnorrkel", - "sp-application-crypto 7.0.0", + "sp-application-crypto", "sp-consensus", "sp-consensus-slots", - "sp-runtime 7.0.0", + "sp-runtime", "thiserror", "tracing-gum", ] @@ -7696,7 +7407,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "bitvec", "futures", @@ -7709,6 +7420,7 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-primitives", + "sp-consensus", "thiserror", "tracing-gum", ] @@ -7716,7 +7428,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "bitvec", "fatality", @@ -7727,7 +7439,7 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-statement-table", - "sp-keystore 0.13.0", + "sp-keystore", "thiserror", "tracing-gum", ] @@ -7735,13 +7447,13 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "futures", "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives", - "sp-keystore 0.13.0", + "sp-keystore", "thiserror", "tracing-gum", "wasm-timer", @@ -7750,7 +7462,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "async-trait", "futures", @@ -7760,6 +7472,7 @@ dependencies = [ "polkadot-node-metrics", "polkadot-node-primitives", "polkadot-node-subsystem", + "polkadot-node-subsystem-util", "polkadot-parachain", "polkadot-primitives", "sp-maybe-compressed-blob", @@ -7769,7 +7482,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "futures", "polkadot-node-metrics", @@ -7784,7 +7497,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "futures", "futures-timer", @@ -7801,7 +7514,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "fatality", "futures", @@ -7820,7 +7533,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "async-trait", "futures", @@ -7837,7 +7550,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "bitvec", "fatality", @@ -7855,7 +7568,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "always-assert", "assert_matches", @@ -7867,19 +7580,21 @@ dependencies = [ "pin-project", "polkadot-core-primitives", "polkadot-node-metrics", + "polkadot-node-primitives", "polkadot-parachain", + "polkadot-primitives", "rand 0.8.5", "rayon", "sc-executor", "sc-executor-common", "sc-executor-wasmtime", "slotmap", - "sp-core 7.0.0", - "sp-externalities 0.13.0", - "sp-io 7.0.0", + "sp-core", + "sp-externalities", + "sp-io", "sp-maybe-compressed-blob", - "sp-tracing 6.0.0", - "sp-wasm-interface 7.0.0", + "sp-tracing", + "sp-wasm-interface", "tempfile", "tikv-jemalloc-ctl", "tokio", @@ -7889,7 +7604,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-checker" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "futures", "polkadot-node-primitives", @@ -7897,7 +7612,7 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-primitives", - "sp-keystore 0.13.0", + "sp-keystore", "thiserror", "tracing-gum", ] @@ -7905,7 +7620,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "futures", "lru 0.9.0", @@ -7920,7 +7635,7 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "lazy_static", "log", @@ -7930,7 +7645,7 @@ dependencies = [ "polkadot-node-primitives", "polkadot-primitives", "sc-network", - "sp-core 7.0.0", + "sp-core", "thiserror", "tokio", ] @@ -7938,7 +7653,7 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "bs58", "futures", @@ -7957,7 +7672,7 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "async-trait", "derive_more", @@ -7980,7 +7695,7 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "bounded-vec", "futures", @@ -7989,12 +7704,13 @@ dependencies = [ "polkadot-primitives", "schnorrkel", "serde", - "sp-application-crypto 7.0.0", + "sp-application-crypto", "sp-consensus-babe", "sp-consensus-vrf", - "sp-core 7.0.0", - "sp-keystore 0.13.0", + "sp-core", + "sp-keystore", "sp-maybe-compressed-blob", + "sp-runtime", "thiserror", "zstd", ] @@ -8002,7 +7718,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -8012,7 +7728,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "async-trait", "derive_more", @@ -8035,7 +7751,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "async-trait", "derive_more", @@ -8058,9 +7774,9 @@ dependencies = [ "polkadot-primitives", "prioritized-metered-channel", "rand 0.8.5", - "sp-application-crypto 7.0.0", - "sp-core 7.0.0", - "sp-keystore 0.13.0", + "sp-application-crypto", + "sp-core", + "sp-keystore", "thiserror", "tracing-gum", ] @@ -8068,7 +7784,7 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "async-trait", "futures", @@ -8083,7 +7799,7 @@ dependencies = [ "polkadot-primitives", "sc-client-api", "sp-api", - "sp-core 7.0.0", + "sp-core", "tikv-jemalloc-ctl", "tracing-gum", ] @@ -8091,38 +7807,24 @@ dependencies = [ [[package]] name = "polkadot-parachain" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ + "bounded-collections", "derive_more", "frame-support", "parity-scale-codec", "polkadot-core-primitives", "scale-info", "serde", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "sp-std 5.0.0", ] -[[package]] -name = "polkadot-performance-test" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" -dependencies = [ - "env_logger 0.9.3", - "kusama-runtime", - "log", - "polkadot-erasure-coding", - "polkadot-node-core-pvf", - "polkadot-node-primitives", - "quote 1.0.23", - "thiserror", -] - [[package]] name = "polkadot-primitives" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "bitvec", "hex-literal", @@ -8132,15 +7834,15 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-application-crypto 7.0.0", - "sp-arithmetic 6.0.0", + "sp-application-crypto", + "sp-arithmetic", "sp-authority-discovery", "sp-consensus-slots", - "sp-core 7.0.0", + "sp-core", "sp-inherents", - "sp-io 7.0.0", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-keystore", + "sp-runtime", "sp-staking", "sp-std 5.0.0", ] @@ -8148,10 +7850,8 @@ dependencies = [ [[package]] name = "polkadot-rpc" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ - "beefy-gadget", - "beefy-gadget-rpc", "jsonrpsee", "mmr-rpc", "pallet-transaction-payment-rpc", @@ -8160,9 +7860,11 @@ dependencies = [ "sc-client-api", "sc-consensus-babe", "sc-consensus-babe-rpc", + "sc-consensus-beefy", + "sc-consensus-beefy-rpc", "sc-consensus-epochs", - "sc-finality-grandpa", - "sc-finality-grandpa-rpc", + "sc-consensus-grandpa", + "sc-consensus-grandpa-rpc", "sc-rpc", "sc-sync-state-rpc", "sc-transaction-pool-api", @@ -8171,8 +7873,8 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-babe", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", + "sp-keystore", + "sp-runtime", "substrate-frame-rpc-system", "substrate-state-trie-migration-rpc", ] @@ -8180,7 +7882,7 @@ dependencies = [ [[package]] name = "polkadot-runtime" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "bitvec", "frame-election-provider-support", @@ -8217,6 +7919,7 @@ dependencies = [ "pallet-session", "pallet-staking", "pallet-staking-reward-curve", + "pallet-staking-runtime-api", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", @@ -8237,16 +7940,16 @@ dependencies = [ "smallvec", "sp-api", "sp-authority-discovery", - "sp-beefy", "sp-block-builder", "sp-consensus-babe", - "sp-core 7.0.0", + "sp-consensus-beefy", + "sp-core", "sp-inherents", - "sp-io 7.0.0", + "sp-io", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", - "sp-runtime 7.0.0", + "sp-runtime", "sp-session", "sp-staking", "sp-std 5.0.0", @@ -8262,7 +7965,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "bitvec", "frame-election-provider-support", @@ -8272,9 +7975,7 @@ dependencies = [ "libsecp256k1", "log", "pallet-authorship", - "pallet-bags-list", "pallet-balances", - "pallet-beefy-mmr", "pallet-election-provider-multi-phase", "pallet-fast-unstake", "pallet-session", @@ -8293,12 +7994,11 @@ dependencies = [ "serde_derive", "slot-range-helper", "sp-api", - "sp-beefy", - "sp-core 7.0.0", + "sp-core", "sp-inherents", - "sp-io 7.0.0", + "sp-io", "sp-npos-elections", - "sp-runtime 7.0.0", + "sp-runtime", "sp-session", "sp-staking", "sp-std 5.0.0", @@ -8309,33 +8009,33 @@ dependencies = [ [[package]] name = "polkadot-runtime-constants" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "frame-support", "polkadot-primitives", "polkadot-runtime-common", "smallvec", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-weights 4.0.0", + "sp-core", + "sp-runtime", + "sp-weights", ] [[package]] name = "polkadot-runtime-metrics" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "bs58", "parity-scale-codec", "polkadot-primitives", "sp-std 5.0.0", - "sp-tracing 6.0.0", + "sp-tracing", ] [[package]] name = "polkadot-runtime-parachains" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "bitflags", "bitvec", @@ -8352,6 +8052,7 @@ dependencies = [ "pallet-timestamp", "pallet-vesting", "parity-scale-codec", + "polkadot-parachain", "polkadot-primitives", "polkadot-runtime-metrics", "rand 0.8.5", @@ -8360,11 +8061,11 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-core 7.0.0", + "sp-core", "sp-inherents", - "sp-io 7.0.0", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", + "sp-io", + "sp-keystore", + "sp-runtime", "sp-session", "sp-staking", "sp-std 5.0.0", @@ -8375,10 +8076,9 @@ dependencies = [ [[package]] name = "polkadot-service" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "async-trait", - "beefy-gadget", "frame-benchmarking-cli", "frame-support", "frame-system-rpc-runtime-api", @@ -8437,12 +8137,14 @@ dependencies = [ "sc-client-db", "sc-consensus", "sc-consensus-babe", + "sc-consensus-beefy", + "sc-consensus-grandpa", "sc-consensus-slots", "sc-executor", - "sc-finality-grandpa", "sc-keystore", "sc-network", "sc-network-common", + "sc-network-sync", "sc-offchain", "sc-service", "sc-sync-state-rpc", @@ -8453,25 +8155,25 @@ dependencies = [ "serde_json", "sp-api", "sp-authority-discovery", - "sp-beefy", "sp-block-builder", "sp-blockchain", "sp-consensus", "sp-consensus-babe", - "sp-core 7.0.0", - "sp-finality-grandpa", + "sp-consensus-beefy", + "sp-consensus-grandpa", + "sp-core", "sp-inherents", - "sp-io 7.0.0", - "sp-keystore 0.13.0", + "sp-io", + "sp-keystore", "sp-mmr-primitives", "sp-offchain", - "sp-runtime 7.0.0", + "sp-runtime", "sp-session", - "sp-state-machine 0.13.0", - "sp-storage 7.0.0", + "sp-state-machine", + "sp-storage", "sp-timestamp", "sp-transaction-pool", - "sp-trie 7.0.0", + "sp-trie", "substrate-prometheus-endpoint", "thiserror", "tracing-gum", @@ -8480,7 +8182,7 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "arrayvec 0.5.2", "fatality", @@ -8492,7 +8194,7 @@ dependencies = [ "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives", - "sp-keystore 0.13.0", + "sp-keystore", "sp-staking", "thiserror", "tracing-gum", @@ -8501,11 +8203,11 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "parity-scale-codec", "polkadot-primitives", - "sp-core 7.0.0", + "sp-core", ] [[package]] @@ -8599,7 +8301,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" dependencies = [ "proc-macro2 1.0.51", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -8634,9 +8336,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", "toml_edit", @@ -8651,7 +8353,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", "version_check", ] @@ -8718,7 +8420,7 @@ checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -8748,7 +8450,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 1.0.107", + "syn 1.0.109", "tempfile", "which", ] @@ -8776,7 +8478,7 @@ dependencies = [ "itertools", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -9060,14 +8762,14 @@ checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "regalloc2" -version = "0.3.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d43a209257d978ef079f3d446331d0f1794f5e0fc19b306a199983857833a779" +checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" dependencies = [ "fxhash", "log", @@ -9101,6 +8803,18 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +[[package]] +name = "region" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi", +] + [[package]] name = "relay-bridge-hub-rococo-client" version = "0.1.0" @@ -9116,8 +8830,8 @@ dependencies = [ "parity-scale-codec", "relay-substrate-client", "scale-info", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", ] [[package]] @@ -9136,9 +8850,9 @@ dependencies = [ "parity-scale-codec", "relay-substrate-client", "scale-info", - "sp-core 7.0.0", - "sp-finality-grandpa", - "sp-runtime 7.0.0", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", ] [[package]] @@ -9149,7 +8863,7 @@ dependencies = [ "bp-runtime", "relay-substrate-client", "relay-utils", - "sp-core 7.0.0", + "sp-core", ] [[package]] @@ -9166,8 +8880,8 @@ dependencies = [ "parity-scale-codec", "relay-substrate-client", "relay-utils", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", ] [[package]] @@ -9178,7 +8892,7 @@ dependencies = [ "bp-runtime", "relay-substrate-client", "relay-utils", - "sp-core 7.0.0", + "sp-core", ] [[package]] @@ -9195,8 +8909,8 @@ dependencies = [ "relay-substrate-client", "relay-utils", "rialto-runtime", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", ] [[package]] @@ -9213,9 +8927,9 @@ dependencies = [ "parity-scale-codec", "relay-substrate-client", "scale-info", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-weights 4.0.0", + "sp-core", + "sp-runtime", + "sp-weights", "subxt", ] @@ -9227,7 +8941,7 @@ dependencies = [ "bp-runtime", "relay-substrate-client", "relay-utils", - "sp-core 7.0.0", + "sp-core", ] [[package]] @@ -9259,11 +8973,11 @@ dependencies = [ "sc-rpc-api", "sc-transaction-pool-api", "scale-info", - "sp-core 7.0.0", + "sp-core", "sp-rpc", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", - "sp-trie 7.0.0", + "sp-trie", "sp-version", "thiserror", "tokio", @@ -9280,14 +8994,14 @@ dependencies = [ "async-trait", "backoff", "bp-runtime", - "env_logger 0.10.0", + "env_logger", "futures", "isahc", "jsonpath_lib", "log", "num-traits", "serde_json", - "sp-runtime 7.0.0", + "sp-runtime", "substrate-prometheus-endpoint", "sysinfo", "thiserror", @@ -9303,7 +9017,7 @@ dependencies = [ "bp-westend", "relay-substrate-client", "relay-utils", - "sp-core 7.0.0", + "sp-core", ] [[package]] @@ -9314,7 +9028,7 @@ dependencies = [ "bp-wococo", "relay-substrate-client", "relay-utils", - "sp-core 7.0.0", + "sp-core", ] [[package]] @@ -9342,7 +9056,7 @@ dependencies = [ name = "rialto-bridge-node" version = "0.1.0" dependencies = [ - "clap 4.1.6", + "clap 4.1.8", "frame-benchmarking", "frame-benchmarking-cli", "frame-support", @@ -9357,11 +9071,11 @@ dependencies = [ "sc-service", "serde_json", "sp-authority-discovery", - "sp-beefy", "sp-consensus-babe", - "sp-core 7.0.0", - "sp-finality-grandpa", - "sp-runtime 7.0.0", + "sp-consensus-beefy", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", "substrate-build-script-utils", ] @@ -9369,7 +9083,7 @@ dependencies = [ name = "rialto-parachain-collator" version = "0.1.0" dependencies = [ - "clap 4.1.6", + "clap 4.1.8", "cumulus-client-cli", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", @@ -9395,6 +9109,7 @@ dependencies = [ "sc-consensus", "sc-executor", "sc-network", + "sc-network-sync", "sc-rpc", "sc-rpc-api", "sc-service", @@ -9405,10 +9120,10 @@ dependencies = [ "sp-api", "sp-block-builder", "sp-consensus-aura", - "sp-core 7.0.0", - "sp-keystore 0.13.0", + "sp-core", + "sp-keystore", "sp-offchain", - "sp-runtime 7.0.0", + "sp-runtime", "sp-session", "sp-timestamp", "sp-transaction-pool", @@ -9446,7 +9161,6 @@ dependencies = [ "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-relayers", - "pallet-randomness-collective-flip", "pallet-sudo", "pallet-timestamp", "pallet-transaction-payment", @@ -9459,11 +9173,11 @@ dependencies = [ "sp-api", "sp-block-builder", "sp-consensus-aura", - "sp-core 7.0.0", + "sp-core", "sp-inherents", - "sp-io 7.0.0", + "sp-io", "sp-offchain", - "sp-runtime 7.0.0", + "sp-runtime", "sp-session", "sp-std 5.0.0", "sp-transaction-pool", @@ -9484,7 +9198,7 @@ dependencies = [ "bp-rialto", "bp-runtime", "bridge-runtime-common", - "env_logger 0.10.0", + "env_logger", "frame-benchmarking", "frame-executive", "frame-support", @@ -9515,14 +9229,14 @@ dependencies = [ "scale-info", "sp-api", "sp-authority-discovery", - "sp-beefy", "sp-block-builder", "sp-consensus-babe", - "sp-core 7.0.0", + "sp-consensus-beefy", + "sp-core", "sp-inherents", - "sp-io 7.0.0", + "sp-io", "sp-offchain", - "sp-runtime 7.0.0", + "sp-runtime", "sp-session", "sp-std 5.0.0", "sp-transaction-pool", @@ -9601,7 +9315,7 @@ dependencies = [ "log", "netlink-packet-route", "netlink-proto", - "nix 0.24.3", + "nix", "thiserror", "tokio", ] @@ -9648,15 +9362,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - [[package]] name = "rustc_version" version = "0.4.0" @@ -9772,15 +9477,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" -[[package]] -name = "safe-mix" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d3d055a2582e6b00ed7a31c1524040aa391092bf636328350813f3a0605215c" -dependencies = [ - "rustc_version 0.2.3", -] - [[package]] name = "safe_arch" version = "0.6.0" @@ -9802,18 +9498,18 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "log", - "sp-core 7.0.0", - "sp-wasm-interface 7.0.0", + "sp-core", + "sp-wasm-interface", "thiserror", ] [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "futures", @@ -9830,9 +9526,9 @@ dependencies = [ "sp-api", "sp-authority-discovery", "sp-blockchain", - "sp-core 7.0.0", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-keystore", + "sp-runtime", "substrate-prometheus-endpoint", "thiserror", ] @@ -9840,7 +9536,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "futures", "futures-timer", @@ -9854,62 +9550,65 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core 7.0.0", + "sp-core", "sp-inherents", - "sp-runtime 7.0.0", + "sp-runtime", "substrate-prometheus-endpoint", ] [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "parity-scale-codec", "sc-client-api", "sp-api", "sp-block-builder", "sp-blockchain", - "sp-core 7.0.0", + "sp-core", "sp-inherents", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", + "sp-runtime", ] [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "memmap2", "sc-chain-spec-derive", + "sc-client-api", + "sc-executor", "sc-network-common", "sc-telemetry", "serde", "serde_json", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-state-machine", ] [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "array-bytes 4.2.0", "chrono", - "clap 4.1.6", + "clap 4.1.8", "fdlimit", "futures", "libp2p", @@ -9931,11 +9630,11 @@ dependencies = [ "serde", "serde_json", "sp-blockchain", - "sp-core 7.0.0", + "sp-core", "sp-keyring", - "sp-keystore 0.13.0", - "sp-panic-handler 5.0.0", - "sp-runtime 7.0.0", + "sp-keystore", + "sp-panic-handler", + "sp-runtime", "sp-version", "thiserror", "tiny-bip39", @@ -9945,7 +9644,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "fnv", "futures", @@ -9958,20 +9657,20 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core 7.0.0", + "sp-core", "sp-database", - "sp-externalities 0.13.0", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", - "sp-storage 7.0.0", + "sp-externalities", + "sp-keystore", + "sp-runtime", + "sp-state-machine", + "sp-storage", "substrate-prometheus-endpoint", ] [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "hash-db", "kvdb", @@ -9985,19 +9684,19 @@ dependencies = [ "sc-client-api", "sc-state-db", "schnellru", - "sp-arithmetic 6.0.0", + "sp-arithmetic", "sp-blockchain", - "sp-core 7.0.0", + "sp-core", "sp-database", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", - "sp-trie 7.0.0", + "sp-runtime", + "sp-state-machine", + "sp-trie", ] [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "futures", @@ -10012,9 +9711,9 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", + "sp-core", + "sp-runtime", + "sp-state-machine", "substrate-prometheus-endpoint", "thiserror", ] @@ -10022,7 +9721,7 @@ dependencies = [ [[package]] name = "sc-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "futures", @@ -10034,16 +9733,16 @@ dependencies = [ "sc-consensus-slots", "sc-telemetry", "sp-api", - "sp-application-crypto 7.0.0", + "sp-application-crypto", "sp-block-builder", "sp-blockchain", "sp-consensus", "sp-consensus-aura", "sp-consensus-slots", - "sp-core 7.0.0", + "sp-core", "sp-inherents", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", + "sp-keystore", + "sp-runtime", "substrate-prometheus-endpoint", "thiserror", ] @@ -10051,7 +9750,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "fork-tree", @@ -10072,17 +9771,17 @@ dependencies = [ "scale-info", "schnorrkel", "sp-api", - "sp-application-crypto 7.0.0", + "sp-application-crypto", "sp-block-builder", "sp-blockchain", "sp-consensus", "sp-consensus-babe", "sp-consensus-slots", "sp-consensus-vrf", - "sp-core 7.0.0", + "sp-core", "sp-inherents", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", + "sp-keystore", + "sp-runtime", "substrate-prometheus-endpoint", "thiserror", ] @@ -10090,7 +9789,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "futures", "jsonrpsee", @@ -10099,33 +9798,147 @@ dependencies = [ "sc-rpc-api", "serde", "sp-api", - "sp-application-crypto 7.0.0", + "sp-application-crypto", "sp-blockchain", "sp-consensus", "sp-consensus-babe", - "sp-core 7.0.0", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-keystore", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-consensus-beefy" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +dependencies = [ + "array-bytes 4.2.0", + "async-trait", + "fnv", + "futures", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-client-api", + "sc-consensus", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-network-gossip", + "sc-network-sync", + "sc-utils", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-beefy", + "sp-core", + "sp-keystore", + "sp-mmr-primitives", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", + "wasm-timer", +] + +[[package]] +name = "sc-consensus-beefy-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +dependencies = [ + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-consensus-beefy", + "sc-rpc", + "serde", + "sp-consensus-beefy", + "sp-core", + "sp-runtime", "thiserror", ] [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +dependencies = [ + "fork-tree", + "parity-scale-codec", + "sc-client-api", + "sc-consensus", + "sp-blockchain", + "sp-runtime", +] + +[[package]] +name = "sc-consensus-grandpa" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ + "ahash 0.8.3", + "array-bytes 4.2.0", + "async-trait", + "dyn-clone", + "finality-grandpa", "fork-tree", + "futures", + "futures-timer", + "log", "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "sc-block-builder", + "sc-chain-spec", "sc-client-api", "sc-consensus", + "sc-network", + "sc-network-common", + "sc-network-gossip", + "sc-telemetry", + "sc-utils", + "serde_json", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-grandpa", + "sp-core", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-consensus-grandpa-rpc" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +dependencies = [ + "finality-grandpa", + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "sc-client-api", + "sc-consensus-grandpa", + "sc-rpc", + "serde", "sp-blockchain", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", + "thiserror", ] [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "futures", @@ -10135,20 +9948,20 @@ dependencies = [ "sc-client-api", "sc-consensus", "sc-telemetry", - "sp-arithmetic 6.0.0", + "sp-arithmetic", "sp-blockchain", "sp-consensus", "sp-consensus-slots", - "sp-core 7.0.0", + "sp-core", "sp-inherents", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", + "sp-runtime", + "sp-state-machine", ] [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "lru 0.8.1", "parity-scale-codec", @@ -10157,14 +9970,14 @@ dependencies = [ "sc-executor-wasmi", "sc-executor-wasmtime", "sp-api", - "sp-core 7.0.0", - "sp-externalities 0.13.0", - "sp-io 7.0.0", - "sp-panic-handler 5.0.0", - "sp-runtime-interface 7.0.0", - "sp-trie 7.0.0", + "sp-core", + "sp-externalities", + "sp-io", + "sp-panic-handler", + "sp-runtime-interface", + "sp-trie", "sp-version", - "sp-wasm-interface 7.0.0", + "sp-wasm-interface", "tracing", "wasmi", ] @@ -10172,11 +9985,11 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", - "sp-wasm-interface 7.0.0", + "sp-wasm-interface", "thiserror", "wasm-instrument", "wasmi", @@ -10185,132 +9998,73 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "log", "sc-allocator", "sc-executor-common", - "sp-runtime-interface 7.0.0", - "sp-wasm-interface 7.0.0", + "sp-runtime-interface", + "sp-wasm-interface", "wasmi", ] [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ + "anyhow", "cfg-if 1.0.0", "libc", "log", "once_cell", - "rustix 0.35.13", + "rustix 0.36.8", "sc-allocator", "sc-executor-common", - "sp-runtime-interface 7.0.0", - "sp-wasm-interface 7.0.0", + "sp-runtime-interface", + "sp-wasm-interface", "wasmtime", ] [[package]] -name = "sc-finality-grandpa" +name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ - "ahash 0.8.3", - "array-bytes 4.2.0", - "async-trait", - "dyn-clone", - "finality-grandpa", - "fork-tree", + "ansi_term", "futures", "futures-timer", "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "rand 0.8.5", - "sc-block-builder", - "sc-chain-spec", "sc-client-api", - "sc-consensus", - "sc-network", "sc-network-common", - "sc-network-gossip", - "sc-telemetry", - "sc-utils", - "serde_json", - "sp-api", - "sp-application-crypto 7.0.0", - "sp-arithmetic 6.0.0", "sp-blockchain", - "sp-consensus", - "sp-core 7.0.0", - "sp-finality-grandpa", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", - "substrate-prometheus-endpoint", - "thiserror", -] - -[[package]] -name = "sc-finality-grandpa-rpc" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" -dependencies = [ - "finality-grandpa", - "futures", - "jsonrpsee", - "log", - "parity-scale-codec", - "sc-client-api", - "sc-finality-grandpa", - "sc-rpc", - "serde", - "sp-blockchain", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "thiserror", -] - -[[package]] -name = "sc-informant" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" -dependencies = [ - "ansi_term", - "futures", - "futures-timer", - "log", - "sc-client-api", - "sc-network-common", - "sp-blockchain", - "sp-runtime 7.0.0", + "sp-runtime", ] [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "array-bytes 4.2.0", "async-trait", "parking_lot 0.12.1", "serde_json", - "sp-application-crypto 7.0.0", - "sp-core 7.0.0", - "sp-keystore 0.13.0", + "sp-application-crypto", + "sp-core", + "sp-keystore", "thiserror", ] [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "array-bytes 4.2.0", + "async-channel", "async-trait", "asynchronous-codec", - "backtrace", "bytes", "either", "fnv", @@ -10320,6 +10074,7 @@ dependencies = [ "libp2p", "log", "lru 0.8.1", + "mockall", "parity-scale-codec", "parking_lot 0.12.1", "pin-project", @@ -10333,11 +10088,11 @@ dependencies = [ "serde", "serde_json", "smallvec", - "sp-arithmetic 6.0.0", + "sp-arithmetic", "sp-blockchain", "sp-consensus", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "substrate-prometheus-endpoint", "thiserror", "unsigned-varint", @@ -10347,7 +10102,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "cid", "futures", @@ -10358,7 +10113,7 @@ dependencies = [ "sc-client-api", "sc-network-common", "sp-blockchain", - "sp-runtime 7.0.0", + "sp-runtime", "thiserror", "unsigned-varint", ] @@ -10366,8 +10121,9 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ + "array-bytes 4.2.0", "async-trait", "bitflags", "bytes", @@ -10379,20 +10135,22 @@ dependencies = [ "prost-build", "sc-consensus", "sc-peerset", + "sc-utils", "serde", "smallvec", "sp-blockchain", "sp-consensus", - "sp-finality-grandpa", - "sp-runtime 7.0.0", + "sp-consensus-grandpa", + "sp-runtime", "substrate-prometheus-endpoint", "thiserror", + "zeroize", ] [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "ahash 0.8.3", "futures", @@ -10402,7 +10160,7 @@ dependencies = [ "lru 0.8.1", "sc-network-common", "sc-peerset", - "sp-runtime 7.0.0", + "sp-runtime", "substrate-prometheus-endpoint", "tracing", ] @@ -10410,7 +10168,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10423,20 +10181,21 @@ dependencies = [ "sc-network-common", "sc-peerset", "sp-blockchain", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "thiserror", ] [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "array-bytes 4.2.0", "async-trait", "fork-tree", "futures", + "futures-timer", "libp2p", "log", "lru 0.8.1", @@ -10450,12 +10209,12 @@ dependencies = [ "sc-peerset", "sc-utils", "smallvec", - "sp-arithmetic 6.0.0", + "sp-arithmetic", "sp-blockchain", "sp-consensus", - "sp-core 7.0.0", - "sp-finality-grandpa", - "sp-runtime 7.0.0", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", "substrate-prometheus-endpoint", "thiserror", ] @@ -10463,7 +10222,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10475,14 +10234,14 @@ dependencies = [ "sc-peerset", "sc-utils", "sp-consensus", - "sp-runtime 7.0.0", + "sp-runtime", "substrate-prometheus-endpoint", ] [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "array-bytes 4.2.0", "bytes", @@ -10502,9 +10261,9 @@ dependencies = [ "sc-peerset", "sc-utils", "sp-api", - "sp-core 7.0.0", + "sp-core", "sp-offchain", - "sp-runtime 7.0.0", + "sp-runtime", "threadpool", "tracing", ] @@ -10512,7 +10271,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "futures", "libp2p", @@ -10525,7 +10284,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -10534,7 +10293,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "futures", "jsonrpsee", @@ -10551,11 +10310,11 @@ dependencies = [ "serde_json", "sp-api", "sp-blockchain", - "sp-core 7.0.0", - "sp-keystore 0.13.0", + "sp-core", + "sp-keystore", "sp-offchain", "sp-rpc", - "sp-runtime 7.0.0", + "sp-runtime", "sp-session", "sp-version", "tokio", @@ -10564,7 +10323,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -10573,9 +10332,9 @@ dependencies = [ "scale-info", "serde", "serde_json", - "sp-core 7.0.0", + "sp-core", "sp-rpc", - "sp-runtime 7.0.0", + "sp-runtime", "sp-version", "thiserror", ] @@ -10583,7 +10342,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "http", "jsonrpsee", @@ -10598,7 +10357,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10614,8 +10373,8 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "sp-version", "thiserror", "tokio-stream", @@ -10624,7 +10383,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "directories", @@ -10667,16 +10426,16 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-consensus", - "sp-core 7.0.0", - "sp-externalities 0.13.0", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime", "sp-session", - "sp-state-machine 0.13.0", - "sp-storage 7.0.0", + "sp-state-machine", + "sp-storage", "sp-transaction-pool", "sp-transaction-storage-proof", - "sp-trie 7.0.0", + "sp-trie", "sp-version", "static_init 1.0.3", "substrate-prometheus-endpoint", @@ -10690,26 +10449,26 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "log", "parity-scale-codec", "parking_lot 0.12.1", - "sp-core 7.0.0", + "sp-core", ] [[package]] name = "sc-storage-monitor" version = "0.1.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ - "clap 4.1.6", + "clap 4.1.8", + "fs4", "futures", "log", - "nix 0.26.2", "sc-client-db", "sc-utils", - "sp-core 7.0.0", + "sp-core", "thiserror", "tokio", ] @@ -10717,7 +10476,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -10725,18 +10484,18 @@ dependencies = [ "sc-client-api", "sc-consensus-babe", "sc-consensus-epochs", - "sc-finality-grandpa", + "sc-consensus-grandpa", "serde", "serde_json", "sp-blockchain", - "sp-runtime 7.0.0", + "sp-runtime", "thiserror", ] [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "futures", "libc", @@ -10747,15 +10506,15 @@ dependencies = [ "sc-telemetry", "serde", "serde_json", - "sp-core 7.0.0", - "sp-io 7.0.0", + "sp-core", + "sp-io", "sp-std 5.0.0", ] [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "chrono", "futures", @@ -10774,7 +10533,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "ansi_term", "atty", @@ -10792,10 +10551,10 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core 7.0.0", + "sp-core", "sp-rpc", - "sp-runtime 7.0.0", - "sp-tracing 6.0.0", + "sp-runtime", + "sp-tracing", "thiserror", "tracing", "tracing-log", @@ -10805,18 +10564,18 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "futures", @@ -10832,9 +10591,9 @@ dependencies = [ "serde", "sp-api", "sp-blockchain", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-tracing 6.0.0", + "sp-core", + "sp-runtime", + "sp-tracing", "sp-transaction-pool", "substrate-prometheus-endpoint", "thiserror", @@ -10843,29 +10602,30 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "futures", "log", "serde", "sp-blockchain", - "sp-runtime 7.0.0", + "sp-runtime", "thiserror", ] [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ - "backtrace", + "async-channel", "futures", "futures-timer", "lazy_static", "log", "parking_lot 0.12.1", "prometheus", + "sp-arithmetic", ] [[package]] @@ -10914,7 +10674,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -11089,15 +10849,6 @@ dependencies = [ "semver-parser", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.16" @@ -11115,29 +10866,29 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "71f2b4817415c6d4210bfe1c7bfcf4801b2d904cb4d0e1a8fdb651013c9e86b8" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "d071a94a3fac4aff69d023a7f411e33f40f3483f8c5190b1953822b6b76d7630" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "indexmap", "itoa", @@ -11273,6 +11024,12 @@ dependencies = [ "wide", ] +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + [[package]] name = "slab" version = "0.4.7" @@ -11291,12 +11048,12 @@ checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" [[package]] name = "slot-range-helper" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "enumn", "parity-scale-codec", "paste", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] @@ -11344,7 +11101,7 @@ dependencies = [ "curve25519-dalek 4.0.0-rc.0", "rand_core 0.6.4", "ring", - "rustc_version 0.4.0", + "rustc_version", "sha2 0.10.6", "subtle", ] @@ -11379,17 +11136,17 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "hash-db", "log", "parity-scale-codec", "sp-api-proc-macro", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", + "sp-core", + "sp-runtime", + "sp-state-machine", "sp-std 5.0.0", - "sp-trie 7.0.0", + "sp-trie", "sp-version", "thiserror", ] @@ -11397,46 +11154,32 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "blake2", "proc-macro-crate", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "sp-application-crypto" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 7.0.0", - "sp-io 7.0.0", + "sp-core", + "sp-io", "sp-std 5.0.0", ] -[[package]] -name = "sp-application-crypto" -version = "12.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a72575f160b1b134ee277a2ab46af4361c072a3fe661c48e474255406cb01c97" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "sp-core 11.0.0", - "sp-io 12.0.0", - "sp-std 6.0.0", -] - [[package]] name = "sp-arithmetic" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "integer-sqrt", "num-traits", @@ -11447,69 +11190,35 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "sp-arithmetic" -version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2038010f7514d50775dcbd3edb569e17fa9bda63128580a9e172abb1795f2c1d" -dependencies = [ - "integer-sqrt", - "num-traits", - "parity-scale-codec", - "scale-info", - "serde", - "sp-std 6.0.0", - "static_assertions", -] - [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" -dependencies = [ - "parity-scale-codec", - "scale-info", - "sp-api", - "sp-application-crypto 7.0.0", - "sp-runtime 7.0.0", - "sp-std 5.0.0", -] - -[[package]] -name = "sp-beefy" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ - "lazy_static", "parity-scale-codec", "scale-info", - "serde", "sp-api", - "sp-application-crypto 7.0.0", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-mmr-primitives", - "sp-runtime 7.0.0", + "sp-application-crypto", + "sp-runtime", "sp-std 5.0.0", - "strum", ] [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "parity-scale-codec", "sp-api", "sp-inherents", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "futures", "log", @@ -11519,43 +11228,40 @@ dependencies = [ "sp-api", "sp-consensus", "sp-database", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", + "sp-runtime", + "sp-state-machine", "thiserror", ] [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "futures", "log", - "parity-scale-codec", - "sp-core 7.0.0", + "sp-core", "sp-inherents", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", - "sp-std 5.0.0", - "sp-version", + "sp-runtime", + "sp-state-machine", "thiserror", ] [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "parity-scale-codec", "scale-info", "sp-api", - "sp-application-crypto 7.0.0", + "sp-application-crypto", "sp-consensus", "sp-consensus-slots", "sp-inherents", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", "sp-timestamp", ] @@ -11563,7 +11269,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "merlin", @@ -11571,22 +11277,59 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-application-crypto 7.0.0", + "sp-application-crypto", "sp-consensus", "sp-consensus-slots", "sp-consensus-vrf", - "sp-core 7.0.0", + "sp-core", "sp-inherents", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", + "sp-keystore", + "sp-runtime", "sp-std 5.0.0", "sp-timestamp", ] +[[package]] +name = "sp-consensus-beefy" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +dependencies = [ + "lazy_static", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-mmr-primitives", + "sp-runtime", + "sp-std 5.0.0", + "strum", +] + +[[package]] +name = "sp-consensus-grandpa" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +dependencies = [ + "finality-grandpa", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "sp-runtime", + "sp-std 5.0.0", +] + [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "parity-scale-codec", "scale-info", @@ -11598,20 +11341,20 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "parity-scale-codec", "scale-info", "schnorrkel", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "sp-core" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "array-bytes 4.2.0", "base58", @@ -11639,54 +11382,11 @@ dependencies = [ "secrecy", "serde", "sp-core-hashing 5.0.0", - "sp-debug-derive 5.0.0", - "sp-externalities 0.13.0", - "sp-runtime-interface 7.0.0", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", "sp-std 5.0.0", - "sp-storage 7.0.0", - "ss58-registry", - "substrate-bip39", - "thiserror", - "tiny-bip39", - "zeroize", -] - -[[package]] -name = "sp-core" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d99984f57c9eb858d29fbe0e4cf44092eec484b2ff72176d5afa4ab7bf1dc8b1" -dependencies = [ - "array-bytes 4.2.0", - "base58", - "bitflags", - "blake2", - "dyn-clonable", - "ed25519-zebra", - "futures", - "hash-db", - "hash256-std-hasher", - "impl-serde", - "lazy_static", - "libsecp256k1", - "log", - "merlin", - "parity-scale-codec", - "parking_lot 0.12.1", - "primitive-types", - "rand 0.8.5", - "regex", - "scale-info", - "schnorrkel", - "secp256k1", - "secrecy", - "serde", - "sp-core-hashing 6.0.0", - "sp-debug-derive 6.0.0", - "sp-externalities 0.15.0", - "sp-runtime-interface 10.0.0", - "sp-std 6.0.0", - "sp-storage 9.0.0", + "sp-storage", "ss58-registry", "substrate-bip39", "thiserror", @@ -11697,9 +11397,9 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ - "blake2", + "blake2b_simd", "byteorder", "digest 0.10.6", "sha2 0.10.6", @@ -11710,34 +11410,34 @@ dependencies = [ [[package]] name = "sp-core-hashing" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc2d1947252b7a4e403b0a260f596920443742791765ec111daa2bbf98eff25" +checksum = "d607f7209b1b9571177fc3722a03312df03606bb65f89317ba686d5fa59d438f" dependencies = [ "blake2", "byteorder", "digest 0.10.6", "sha2 0.10.6", "sha3", - "sp-std 6.0.0", + "sp-std 7.0.0", "twox-hash", ] [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", "sp-core-hashing 5.0.0", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -11746,76 +11446,35 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", -] - -[[package]] -name = "sp-debug-derive" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fb9dc63d54de7d7bed62a505b6e0bd66c122525ea1abb348f6564717c3df2d" -dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "sp-externalities" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" -dependencies = [ - "environmental", - "parity-scale-codec", - "sp-std 5.0.0", - "sp-storage 7.0.0", -] - -[[package]] -name = "sp-externalities" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc754e1cec66b93df0b48a8986d019c1a2a90431c42cf2614cc35a291597c329" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "environmental", "parity-scale-codec", - "sp-std 6.0.0", - "sp-storage 9.0.0", -] - -[[package]] -name = "sp-finality-grandpa" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" -dependencies = [ - "finality-grandpa", - "log", - "parity-scale-codec", - "scale-info", - "serde", - "sp-api", - "sp-application-crypto 7.0.0", - "sp-core 7.0.0", - "sp-keystore 0.13.0", - "sp-runtime 7.0.0", "sp-std 5.0.0", + "sp-storage", ] [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "sp-std 5.0.0", "thiserror", ] @@ -11823,7 +11482,7 @@ dependencies = [ [[package]] name = "sp-io" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "bytes", "ed25519", @@ -11833,40 +11492,14 @@ dependencies = [ "log", "parity-scale-codec", "secp256k1", - "sp-core 7.0.0", - "sp-externalities 0.13.0", - "sp-keystore 0.13.0", - "sp-runtime-interface 7.0.0", - "sp-state-machine 0.13.0", - "sp-std 5.0.0", - "sp-tracing 6.0.0", - "sp-trie 7.0.0", - "tracing", - "tracing-core", -] - -[[package]] -name = "sp-io" -version = "12.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ac917b37c7733e3778e7ffc3958f1181a151ac3b14116d65fb10fe7b08db10e" -dependencies = [ - "bytes", - "ed25519", - "ed25519-dalek", - "futures", - "libsecp256k1", - "log", - "parity-scale-codec", - "secp256k1", - "sp-core 11.0.0", - "sp-externalities 0.15.0", - "sp-keystore 0.17.0", - "sp-runtime-interface 10.0.0", - "sp-state-machine 0.17.0", - "sp-std 6.0.0", - "sp-tracing 7.0.0", - "sp-trie 11.0.0", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", + "sp-std 5.0.0", + "sp-tracing", + "sp-trie", "tracing", "tracing-core", ] @@ -11874,18 +11507,18 @@ dependencies = [ [[package]] name = "sp-keyring" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "lazy_static", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "strum", ] [[package]] name = "sp-keystore" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "futures", @@ -11894,32 +11527,15 @@ dependencies = [ "parking_lot 0.12.1", "schnorrkel", "serde", - "sp-core 7.0.0", - "sp-externalities 0.13.0", - "thiserror", -] - -[[package]] -name = "sp-keystore" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ada8b7a72404bda58b3353830bc42f027ec764e13a28b4cd4386cf34fb1ae7c" -dependencies = [ - "async-trait", - "futures", - "merlin", - "parity-scale-codec", - "parking_lot 0.12.1", - "schnorrkel", - "sp-core 11.0.0", - "sp-externalities 0.15.0", + "sp-core", + "sp-externalities", "thiserror", ] [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "thiserror", "zstd", @@ -11928,7 +11544,7 @@ dependencies = [ [[package]] name = "sp-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "ckb-merkle-mountain-range 0.5.2", "log", @@ -11936,9 +11552,9 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-core 7.0.0", - "sp-debug-derive 5.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-debug-derive", + "sp-runtime", "sp-std 5.0.0", "thiserror", ] @@ -11946,42 +11562,31 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-arithmetic 6.0.0", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-arithmetic", + "sp-core", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "sp-api", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", ] [[package]] name = "sp-panic-handler" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" -dependencies = [ - "backtrace", - "lazy_static", - "regex", -] - -[[package]] -name = "sp-panic-handler" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abed79c3d5b3622f65ab065676addd9923b9b122cd257df23e2757ce487c6d2" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "backtrace", "lazy_static", @@ -11991,17 +11596,17 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "rustc-hash", "serde", - "sp-core 7.0.0", + "sp-core", ] [[package]] name = "sp-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "either", "hash256-std-hasher", @@ -12012,109 +11617,54 @@ dependencies = [ "rand 0.8.5", "scale-info", "serde", - "sp-application-crypto 7.0.0", - "sp-arithmetic 6.0.0", - "sp-core 7.0.0", - "sp-io 7.0.0", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", "sp-std 5.0.0", - "sp-weights 4.0.0", -] - -[[package]] -name = "sp-runtime" -version = "12.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c86578c67b060a6ecab52af64f1cf18b3892fca7fba5ffc5c49934b2e76b1929" -dependencies = [ - "either", - "hash256-std-hasher", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "paste", - "rand 0.8.5", - "scale-info", - "serde", - "sp-application-crypto 12.0.0", - "sp-arithmetic 9.0.0", - "sp-core 11.0.0", - "sp-io 12.0.0", - "sp-std 6.0.0", - "sp-weights 8.0.0", + "sp-weights", ] [[package]] name = "sp-runtime-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", "primitive-types", - "sp-externalities 0.13.0", - "sp-runtime-interface-proc-macro 6.0.0", + "sp-externalities", + "sp-runtime-interface-proc-macro", "sp-std 5.0.0", - "sp-storage 7.0.0", - "sp-tracing 6.0.0", - "sp-wasm-interface 7.0.0", - "static_assertions", -] - -[[package]] -name = "sp-runtime-interface" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3858935567385728ea45f6d159f9970b0b278eb22a8e77625bbf4a63e43a85a" -dependencies = [ - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec", - "primitive-types", - "sp-externalities 0.15.0", - "sp-runtime-interface-proc-macro 7.0.0", - "sp-std 6.0.0", - "sp-storage 9.0.0", - "sp-tracing 7.0.0", - "sp-wasm-interface 8.0.0", + "sp-storage", + "sp-tracing", + "sp-wasm-interface", "static_assertions", ] [[package]] name = "sp-runtime-interface-proc-macro" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "Inflector", "proc-macro-crate", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", -] - -[[package]] -name = "sp-runtime-interface-proc-macro" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00083a77e938c4f35d0bde7ca0b6e5f616158ebe11af6063795aa664d92a238b" -dependencies = [ - "Inflector", - "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "parity-scale-codec", "scale-info", "sp-api", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "sp-staking", "sp-std 5.0.0", ] @@ -12122,19 +11672,19 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "parity-scale-codec", "scale-info", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", "sp-std 5.0.0", ] [[package]] name = "sp-state-machine" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "hash-db", "log", @@ -12142,32 +11692,11 @@ dependencies = [ "parking_lot 0.12.1", "rand 0.8.5", "smallvec", - "sp-core 7.0.0", - "sp-externalities 0.13.0", - "sp-panic-handler 5.0.0", + "sp-core", + "sp-externalities", + "sp-panic-handler", "sp-std 5.0.0", - "sp-trie 7.0.0", - "thiserror", - "tracing", -] - -[[package]] -name = "sp-state-machine" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fd4c600df0b5abf26c19b6c61d928b64e0c2b8a709a3dbef872be0507cd1ca" -dependencies = [ - "hash-db", - "log", - "parity-scale-codec", - "parking_lot 0.12.1", - "rand 0.8.5", - "smallvec", - "sp-core 11.0.0", - "sp-externalities 0.15.0", - "sp-panic-handler 6.0.0", - "sp-std 6.0.0", - "sp-trie 11.0.0", + "sp-trie", "thiserror", "tracing", ] @@ -12175,52 +11704,38 @@ dependencies = [ [[package]] name = "sp-std" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" [[package]] name = "sp-std" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af0ee286f98455272f64ac5bb1384ff21ac029fbb669afbaf48477faff12760e" +checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986" [[package]] name = "sp-storage" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "impl-serde", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 5.0.0", + "sp-debug-derive", "sp-std 5.0.0", ] -[[package]] -name = "sp-storage" -version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9acb4059eb0ae4fa8cf9ca9119b0178ad312a592d4c6054bd17b411034f233e9" -dependencies = [ - "impl-serde", - "parity-scale-codec", - "ref-cast", - "serde", - "sp-debug-derive 6.0.0", - "sp-std 6.0.0", -] - [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "futures-timer", "log", "parity-scale-codec", "sp-inherents", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", "thiserror", ] @@ -12228,7 +11743,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "parity-scale-codec", "sp-std 5.0.0", @@ -12237,48 +11752,35 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "sp-tracing" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b22d28a0bc2365dfb86644d14f2682a79da35891d4656d4896fb09fb05ff1e6c" -dependencies = [ - "parity-scale-codec", - "sp-std 6.0.0", - "tracing", - "tracing-core", - "tracing-subscriber", -] - [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "sp-api", - "sp-runtime 7.0.0", + "sp-runtime", ] [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "log", "parity-scale-codec", "scale-info", - "sp-core 7.0.0", + "sp-core", "sp-inherents", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", - "sp-trie 7.0.0", + "sp-trie", ] [[package]] name = "sp-trie" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "ahash 0.8.3", "hash-db", @@ -12290,42 +11792,18 @@ dependencies = [ "parking_lot 0.12.1", "scale-info", "schnellru", - "sp-core 7.0.0", + "sp-core", "sp-std 5.0.0", "thiserror", "tracing", - "trie-db 0.24.0", - "trie-root", -] - -[[package]] -name = "sp-trie" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db085f134cb444e52ca569a442d12c84bd17667532613d78dd6f568942632da4" -dependencies = [ - "ahash 0.7.6", - "hash-db", - "hashbrown 0.12.3", - "lazy_static", - "lru 0.8.1", - "memory-db", - "nohash-hasher", - "parity-scale-codec", - "parking_lot 0.12.1", - "scale-info", - "sp-core 11.0.0", - "sp-std 6.0.0", - "thiserror", - "tracing", - "trie-db 0.24.0", + "trie-db", "trie-root", ] [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "impl-serde", "parity-scale-codec", @@ -12333,7 +11811,7 @@ dependencies = [ "scale-info", "serde", "sp-core-hashing-proc-macro", - "sp-runtime 7.0.0", + "sp-runtime", "sp-std 5.0.0", "sp-version-proc-macro", "thiserror", @@ -12342,19 +11820,20 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "parity-scale-codec", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "sp-wasm-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ + "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", @@ -12363,51 +11842,21 @@ dependencies = [ "wasmtime", ] -[[package]] -name = "sp-wasm-interface" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ebc377987d42f8fc20e3f4ec4fd1147dd098fe90bcb4269e1eddb04e920f889" -dependencies = [ - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "sp-std 6.0.0", - "wasmi", - "wasmtime", -] - [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "parity-scale-codec", "scale-info", "serde", "smallvec", - "sp-arithmetic 6.0.0", - "sp-core 7.0.0", - "sp-debug-derive 5.0.0", + "sp-arithmetic", + "sp-core", + "sp-debug-derive", "sp-std 5.0.0", ] -[[package]] -name = "sp-weights" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90eca2a19f48978e9cd605e23905d0602419f7880a9360ac717b03412e9c7916" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "smallvec", - "sp-arithmetic 9.0.0", - "sp-core 11.0.0", - "sp-debug-derive 6.0.0", - "sp-std 6.0.0", -] - [[package]] name = "spin" version = "0.5.2" @@ -12488,7 +11937,7 @@ dependencies = [ "memchr", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -12501,7 +11950,7 @@ dependencies = [ "memchr", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -12509,14 +11958,14 @@ name = "storage-proof-fuzzer" version = "0.1.0" dependencies = [ "bp-runtime", - "env_logger 0.10.0", + "env_logger", "honggfuzz", "log", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", + "sp-core", + "sp-runtime", + "sp-state-machine", "sp-std 5.0.0", - "sp-trie 7.0.0", + "sp-trie", ] [[package]] @@ -12552,7 +12001,7 @@ dependencies = [ "proc-macro-error", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -12574,7 +12023,7 @@ dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", "rustversion", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -12612,7 +12061,7 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "platforms 2.0.0", ] @@ -12620,7 +12069,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "frame-system-rpc-runtime-api", "futures", @@ -12632,14 +12081,14 @@ dependencies = [ "sp-api", "sp-block-builder", "sp-blockchain", - "sp-core 7.0.0", - "sp-runtime 7.0.0", + "sp-core", + "sp-runtime", ] [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "hyper", "log", @@ -12695,9 +12144,9 @@ dependencies = [ "rialto-runtime", "signal-hook", "signal-hook-async-std", - "sp-core 7.0.0", + "sp-core", "sp-keyring", - "sp-runtime 7.0.0", + "sp-runtime", "structopt", "strum", "substrate-relay-helper", @@ -12745,29 +12194,29 @@ dependencies = [ "relay-utils", "relay-wococo-client", "rialto-runtime", - "sp-core 7.0.0", - "sp-finality-grandpa", - "sp-runtime 7.0.0", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", "thiserror", ] [[package]] name = "substrate-rpc-client" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "async-trait", "jsonrpsee", "log", "sc-rpc-api", "serde", - "sp-runtime 7.0.0", + "sp-runtime", ] [[package]] name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "jsonrpsee", "log", @@ -12776,17 +12225,17 @@ dependencies = [ "sc-rpc-api", "scale-info", "serde", - "sp-core 7.0.0", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", - "sp-trie 7.0.0", - "trie-db 0.24.0", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-trie", + "trie-db", ] [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ "ansi_term", "build-helper", @@ -12817,8 +12266,8 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "subxt" -version = "0.26.0" -source = "git+https://github.com/paritytech/subxt?branch=master#9e2acff5b2838a59205fac1cedbf2c404f5eb9b7" +version = "0.27.1" +source = "git+https://github.com/paritytech/subxt?branch=master#d4545de8cf426db57a498c14ec28b8e2efc008df" dependencies = [ "base58", "blake2", @@ -12828,7 +12277,6 @@ dependencies = [ "getrandom 0.2.8", "hex", "impl-serde", - "jsonrpsee", "parity-scale-codec", "parking_lot 0.12.1", "primitive-types", @@ -12838,9 +12286,7 @@ dependencies = [ "scale-value", "serde", "serde_json", - "sp-core 11.0.0", - "sp-core-hashing 6.0.0", - "sp-runtime 12.0.0", + "sp-core-hashing 7.0.0", "subxt-macro", "subxt-metadata", "thiserror", @@ -12849,8 +12295,8 @@ dependencies = [ [[package]] name = "subxt-codegen" -version = "0.26.0" -source = "git+https://github.com/paritytech/subxt?branch=master#9e2acff5b2838a59205fac1cedbf2c404f5eb9b7" +version = "0.27.1" +source = "git+https://github.com/paritytech/subxt?branch=master#d4545de8cf426db57a498c14ec28b8e2efc008df" dependencies = [ "darling", "frame-metadata", @@ -12858,35 +12304,35 @@ dependencies = [ "hex", "jsonrpsee", "parity-scale-codec", - "proc-macro-error", "proc-macro2 1.0.51", "quote 1.0.23", "scale-info", "subxt-metadata", - "syn 1.0.107", + "syn 1.0.109", + "thiserror", "tokio", ] [[package]] name = "subxt-macro" -version = "0.26.0" -source = "git+https://github.com/paritytech/subxt?branch=master#9e2acff5b2838a59205fac1cedbf2c404f5eb9b7" +version = "0.27.1" +source = "git+https://github.com/paritytech/subxt?branch=master#d4545de8cf426db57a498c14ec28b8e2efc008df" dependencies = [ "darling", "proc-macro-error", "subxt-codegen", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "subxt-metadata" -version = "0.26.0" -source = "git+https://github.com/paritytech/subxt?branch=master#9e2acff5b2838a59205fac1cedbf2c404f5eb9b7" +version = "0.27.1" +source = "git+https://github.com/paritytech/subxt?branch=master#d4545de8cf426db57a498c14ec28b8e2efc008df" dependencies = [ "frame-metadata", "parity-scale-codec", "scale-info", - "sp-core-hashing 6.0.0", + "sp-core-hashing 7.0.0", ] [[package]] @@ -12902,9 +12348,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", @@ -12919,15 +12365,15 @@ checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", "unicode-xid 0.2.4", ] [[package]] name = "sysinfo" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38a81bbc26c485910df47772df6bbcdb417036132caa9e51e29d2e39c4636d4e" +checksum = "d3e847e2de7a137c8c2cede5095872dbb00f4f9bf34d061347e36b43322acd56" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys", @@ -13010,22 +12456,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -13181,9 +12627,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.25.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" dependencies = [ "autocfg", "bytes", @@ -13196,7 +12642,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -13207,7 +12653,7 @@ checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -13259,19 +12705,19 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" [[package]] name = "toml_edit" -version = "0.18.1" +version = "0.19.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" +checksum = "7082a95d48029677a28f181e5f6422d0c8339ad8396a39d3f33d62a90c1f6c30" dependencies = [ "indexmap", - "nom8", "toml_datetime", + "winnow", ] [[package]] @@ -13336,7 +12782,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -13362,7 +12808,7 @@ dependencies = [ [[package]] name = "tracing-gum" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "polkadot-node-jaeger", "polkadot-primitives", @@ -13373,13 +12819,13 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "expander 0.0.6", "proc-macro-crate", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -13426,19 +12872,6 @@ dependencies = [ "tracing-serde", ] -[[package]] -name = "trie-db" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "004e1e8f92535694b4cb1444dc5a8073ecf0815e3357f729638b9f8fc4062908" -dependencies = [ - "hash-db", - "hashbrown 0.12.3", - "log", - "rustc-hex", - "smallvec", -] - [[package]] name = "trie-db" version = "0.26.0" @@ -13516,9 +12949,10 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#51cf2e790f118f6cfe8d94fb9ca96bc580bd7c98" +source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" dependencies = [ - "clap 4.1.6", + "async-trait", + "clap 4.1.8", "frame-remote-externalities", "hex", "log", @@ -13529,16 +12963,21 @@ dependencies = [ "serde", "serde_json", "sp-api", - "sp-core 7.0.0", - "sp-debug-derive 5.0.0", - "sp-externalities 0.13.0", - "sp-io 7.0.0", - "sp-keystore 0.13.0", + "sp-consensus-aura", + "sp-consensus-babe", + "sp-core", + "sp-debug-derive", + "sp-externalities", + "sp-inherents", + "sp-io", + "sp-keystore", "sp-rpc", - "sp-runtime 7.0.0", - "sp-state-machine 0.13.0", + "sp-runtime", + "sp-state-machine", + "sp-timestamp", + "sp-transaction-storage-proof", "sp-version", - "sp-weights 4.0.0", + "sp-weights", "substrate-rpc-client", "zstd", ] @@ -13574,9 +13013,9 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "digest 0.10.6", - "rand 0.7.3", + "rand 0.8.5", "static_assertions", ] @@ -13812,7 +13251,7 @@ dependencies = [ "once_cell", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -13846,7 +13285,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -13953,22 +13392,24 @@ dependencies = [ "memory_units", "num-rational", "num-traits", + "region", ] [[package]] name = "wasmparser" -version = "0.89.1" +version = "0.100.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5d3e08b13876f96dd55608d03cd4883a0545884932d5adf11925876c96daef" +checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" dependencies = [ "indexmap", + "url", ] [[package]] name = "wasmtime" -version = "1.0.2" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad5af6ba38311282f2a21670d96e78266e8c8e2f38cbcd52c254df6ccbc7731" +checksum = "f6e89f9819523447330ffd70367ef4a18d8c832e24e8150fe054d1d912841632" dependencies = [ "anyhow", "bincode", @@ -13989,23 +13430,23 @@ dependencies = [ "wasmtime-environ", "wasmtime-jit", "wasmtime-runtime", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] name = "wasmtime-asm-macros" -version = "1.0.2" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45de63ddfc8b9223d1adc8f7b2ee5f35d1f6d112833934ad7ea66e4f4339e597" +checksum = "9bd3a5e46c198032da934469f3a6e48649d1f9142438e4fd4617b68a35644b8a" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "wasmtime-cache" -version = "1.0.2" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcd849399d17d2270141cfe47fa0d91ee52d5f8ea9b98cf7ddde0d53e5f79882" +checksum = "b389ae9b678b9c3851091a4804f4182d688d27aff7abc9aa37fa7be37d8ecffa" dependencies = [ "anyhow", "base64 0.13.1", @@ -14013,19 +13454,19 @@ dependencies = [ "directories-next", "file-per-thread-logger", "log", - "rustix 0.35.13", + "rustix 0.36.8", "serde", - "sha2 0.9.9", + "sha2 0.10.6", "toml", - "windows-sys 0.36.1", + "windows-sys 0.42.0", "zstd", ] [[package]] name = "wasmtime-cranelift" -version = "1.0.2" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd91339b742ff20bfed4532a27b73c86b5bcbfedd6bea2dcdf2d64471e1b5c6" +checksum = "59b2c92a08c0db6efffd88fdc97d7aa9c7c63b03edb0971dbca745469f820e8c" dependencies = [ "anyhow", "cranelift-codegen", @@ -14044,9 +13485,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "1.0.2" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebb881c61f4f627b5d45c54e629724974f8a8890d455bcbe634330cc27309644" +checksum = "9a6db9fc52985ba06ca601f2ff0ff1f526c5d724c7ac267b47326304b0c97883" dependencies = [ "anyhow", "cranelift-entity", @@ -14063,9 +13504,9 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "1.0.2" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1985c628011fe26adf5e23a5301bdc79b245e0e338f14bb58b39e4e25e4d8681" +checksum = "b77e3a52cd84d0f7f18554afa8060cfe564ccac61e3b0802d3fd4084772fa5f6" dependencies = [ "addr2line 0.17.0", "anyhow", @@ -14076,32 +13517,42 @@ dependencies = [ "log", "object 0.29.0", "rustc-demangle", - "rustix 0.35.13", "serde", "target-lexicon", - "thiserror", "wasmtime-environ", "wasmtime-jit-debug", + "wasmtime-jit-icache-coherence", "wasmtime-runtime", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] name = "wasmtime-jit-debug" -version = "1.0.2" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f671b588486f5ccec8c5a3dba6b4c07eac2e66ab8c60e6f4e53717c77f709731" +checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" dependencies = [ "object 0.29.0", "once_cell", - "rustix 0.35.13", + "rustix 0.36.8", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67d412e9340ab1c83867051d8d1d7c90aa8c9afc91da086088068e2734e25064" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "windows-sys 0.42.0", ] [[package]] name = "wasmtime-runtime" -version = "1.0.2" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8f92ad4b61736339c29361da85769ebc200f184361959d1792832e592a1afd" +checksum = "d594e791b5fdd4dbaf8cf7ae62f2e4ff85018ce90f483ca6f42947688e48827d" dependencies = [ "anyhow", "cc", @@ -14114,19 +13565,18 @@ dependencies = [ "memoffset 0.6.5", "paste", "rand 0.8.5", - "rustix 0.35.13", - "thiserror", + "rustix 0.36.8", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] name = "wasmtime-types" -version = "1.0.2" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23d61cb4c46e837b431196dd06abb11731541021916d03476a178b54dc07aeb" +checksum = "a6688d6f96d4dbc1f89fab626c56c1778936d122b5f4ae7a57c2eb42b8d982e2" dependencies = [ "cranelift-entity", "serde", @@ -14378,7 +13828,7 @@ dependencies = [ "lazy_static", "libc", "log", - "nix 0.24.3", + "nix", "rand 0.8.5", "thiserror", "tokio", @@ -14465,19 +13915,6 @@ dependencies = [ "windows_x86_64_msvc 0.34.0", ] -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - [[package]] name = "windows-sys" version = "0.42.0" @@ -14529,12 +13966,6 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - [[package]] name = "windows_aarch64_msvc" version = "0.42.1" @@ -14547,12 +13978,6 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - [[package]] name = "windows_i686_gnu" version = "0.42.1" @@ -14565,12 +13990,6 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - [[package]] name = "windows_i686_msvc" version = "0.42.1" @@ -14583,12 +14002,6 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - [[package]] name = "windows_x86_64_gnu" version = "0.42.1" @@ -14609,15 +14022,18 @@ checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] -name = "windows_x86_64_msvc" -version = "0.42.1" +name = "winnow" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" +dependencies = [ + "memchr", +] [[package]] name = "winreg" @@ -14699,23 +14115,23 @@ dependencies = [ [[package]] name = "xcm" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ + "bounded-collections", "derivative", "impl-trait-for-tuples", "log", "parity-scale-codec", "scale-info", "serde", - "sp-core 7.0.0", - "sp-weights 4.0.0", + "sp-weights", "xcm-procedural", ] [[package]] name = "xcm-builder" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "frame-support", "frame-system", @@ -14725,9 +14141,9 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain", "scale-info", - "sp-arithmetic 6.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-arithmetic", + "sp-io", + "sp-runtime", "sp-std 5.0.0", "xcm", "xcm-executor", @@ -14736,31 +14152,31 @@ dependencies = [ [[package]] name = "xcm-executor" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "environmental", "frame-support", "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-arithmetic 6.0.0", - "sp-core 7.0.0", - "sp-io 7.0.0", - "sp-runtime 7.0.0", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", "sp-std 5.0.0", - "sp-weights 4.0.0", + "sp-weights", "xcm", ] [[package]] name = "xcm-procedural" version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#4f331d74c3004d9765b735ec66acc92edea62c7f" +source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" dependencies = [ "Inflector", "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -14809,7 +14225,7 @@ checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ "proc-macro2 1.0.51", "quote 1.0.23", - "syn 1.0.107", + "syn 1.0.109", "synstructure", ] diff --git a/bin/millau/node/Cargo.toml b/bin/millau/node/Cargo.toml index 3d3d238943d..cca94bbe7b1 100644 --- a/bin/millau/node/Cargo.toml +++ b/bin/millau/node/Cargo.toml @@ -11,7 +11,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] clap = { version = "4.1.6", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } -serde_json = "1.0.93" +serde_json = "1.0.94" # Bridge dependencies @@ -19,9 +19,9 @@ millau-runtime = { path = "../runtime" } # Substrate Dependencies -beefy-gadget = { git = "https://github.com/paritytech/substrate", branch = "master" } -beefy-gadget-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-beefy = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-consensus-beefy-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } node-inspect = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -33,8 +33,8 @@ sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "mas sc-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } -sc-finality-grandpa-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-consensus-grandpa-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-service = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -42,10 +42,11 @@ sc-telemetry = { git = "https://github.com/paritytech/substrate", branch = "mast sc-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master" } substrate-frame-rpc-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-network-common = { git = "https://github.com/paritytech/substrate", branch = "master" } [build-dependencies] substrate-build-script-utils = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/bin/millau/node/src/chain_spec.rs b/bin/millau/node/src/chain_spec.rs index d18634eb983..8669ca92cc8 100644 --- a/bin/millau/node/src/chain_spec.rs +++ b/bin/millau/node/src/chain_spec.rs @@ -19,10 +19,10 @@ use millau_runtime::{ BridgeRialtoParachainMessagesConfig, BridgeWestendGrandpaConfig, GenesisConfig, GrandpaConfig, SessionConfig, SessionKeys, Signature, SudoConfig, SystemConfig, WASM_BINARY, }; -use sp_beefy::crypto::AuthorityId as BeefyId; use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_consensus_beefy::crypto::AuthorityId as BeefyId; +use sp_consensus_grandpa::AuthorityId as GrandpaId; use sp_core::{sr25519, Pair, Public}; -use sp_finality_grandpa::AuthorityId as GrandpaId; use sp_runtime::traits::{IdentifyAccount, Verify}; /// "Names" of the authorities accounts at local testnet. diff --git a/bin/millau/node/src/service.rs b/bin/millau/node/src/service.rs index 8b0bc828957..f8e6d34933d 100644 --- a/bin/millau/node/src/service.rs +++ b/bin/millau/node/src/service.rs @@ -20,8 +20,8 @@ use jsonrpsee::RpcModule; use millau_runtime::{self, opaque::Block, RuntimeApi}; use sc_client_api::BlockBackend; use sc_consensus_aura::{CompatibilityMode, ImportQueueParams, SlotProportion, StartAuraParams}; +use sc_consensus_grandpa::SharedVoterState; pub use sc_executor::NativeElseWasmExecutor; -use sc_finality_grandpa::SharedVoterState; use sc_keystore::LocalKeystore; use sc_service::{error::Error as ServiceError, Configuration, TaskManager}; use sc_telemetry::{Telemetry, TelemetryWorker}; @@ -64,15 +64,15 @@ pub fn new_partial( sc_consensus::DefaultImportQueue, sc_transaction_pool::FullPool, ( - sc_finality_grandpa::GrandpaBlockImport< + sc_consensus_grandpa::GrandpaBlockImport< FullBackend, Block, FullClient, FullSelectChain, >, - sc_finality_grandpa::LinkHalf, - beefy_gadget::BeefyVoterLinks, - beefy_gadget::BeefyRPCLinks, + sc_consensus_grandpa::LinkHalf, + sc_consensus_beefy::BeefyVoterLinks, + sc_consensus_beefy::BeefyRPCLinks, Option, ), >, @@ -123,7 +123,7 @@ pub fn new_partial( client.clone(), ); - let (grandpa_block_import, grandpa_link) = sc_finality_grandpa::block_import( + let (grandpa_block_import, grandpa_link) = sc_consensus_grandpa::block_import( client.clone(), &client, select_chain.clone(), @@ -131,10 +131,11 @@ pub fn new_partial( )?; let (beefy_block_import, beefy_voter_links, beefy_rpc_links) = - beefy_gadget::beefy_block_import_and_links( + sc_consensus_beefy::beefy_block_import_and_links( grandpa_block_import.clone(), backend.clone(), client.clone(), + config.prometheus_registry().cloned(), ); let slot_duration = sc_consensus_aura::slot_duration(&*client)?; @@ -183,6 +184,8 @@ fn remote_keystore(_url: &str) -> Result, &'static str> { /// Builds a new service for a full client. pub fn new_full(mut config: Configuration) -> Result { + use sc_network_common::sync::warp::WarpSyncParams; + let sc_service::PartialComponents { client, backend, @@ -209,38 +212,41 @@ pub fn new_full(mut config: Configuration) -> Result // Note: GrandPa is pushed before the Polkadot-specific protocols. This doesn't change // anything in terms of behaviour, but makes the logs more consistent with the other // Substrate nodes. - let grandpa_protocol_name = sc_finality_grandpa::protocol_standard_name( + let grandpa_protocol_name = sc_consensus_grandpa::protocol_standard_name( &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), &config.chain_spec, ); config .network .extra_sets - .push(sc_finality_grandpa::grandpa_peers_set_config(grandpa_protocol_name.clone())); + .push(sc_consensus_grandpa::grandpa_peers_set_config(grandpa_protocol_name.clone())); let beefy_gossip_proto_name = - beefy_gadget::gossip_protocol_name(genesis_hash, config.chain_spec.fork_id()); + sc_consensus_beefy::gossip_protocol_name(genesis_hash, config.chain_spec.fork_id()); // `beefy_on_demand_justifications_handler` is given to `beefy-gadget` task to be run, // while `beefy_req_resp_cfg` is added to `config.network.request_response_protocols`. let (beefy_on_demand_justifications_handler, beefy_req_resp_cfg) = - beefy_gadget::communication::request_response::BeefyJustifsRequestHandler::new( + sc_consensus_beefy::communication::request_response::BeefyJustifsRequestHandler::new( genesis_hash, config.chain_spec.fork_id(), client.clone(), + config.prometheus_registry().cloned(), ); config .network .extra_sets - .push(beefy_gadget::communication::beefy_peers_set_config(beefy_gossip_proto_name.clone())); + .push(sc_consensus_beefy::communication::beefy_peers_set_config( + beefy_gossip_proto_name.clone(), + )); config.network.request_response_protocols.push(beefy_req_resp_cfg); - let warp_sync = Arc::new(sc_finality_grandpa::warp_proof::NetworkProvider::new( + let warp_sync = Arc::new(sc_consensus_grandpa::warp_proof::NetworkProvider::new( backend.clone(), grandpa_link.shared_authority_set().clone(), Vec::default(), )); - let (network, system_rpc_tx, tx_handler_controller, network_starter) = + let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, client: client.clone(), @@ -248,7 +254,7 @@ pub fn new_full(mut config: Configuration) -> Result spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - warp_sync: Some(warp_sync), + warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), })?; if config.offchain_worker.enabled { @@ -269,12 +275,12 @@ pub fn new_full(mut config: Configuration) -> Result let shared_voter_state = SharedVoterState::empty(); let rpc_extensions_builder = { - use sc_finality_grandpa::FinalityProofProvider as GrandpaFinalityProofProvider; + use sc_consensus_grandpa::FinalityProofProvider as GrandpaFinalityProofProvider; - use beefy_gadget_rpc::{Beefy, BeefyApiServer}; use mmr_rpc::{Mmr, MmrApiServer}; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; - use sc_finality_grandpa_rpc::{Grandpa, GrandpaApiServer}; + use sc_consensus_beefy_rpc::{Beefy, BeefyApiServer}; + use sc_consensus_grandpa_rpc::{Grandpa, GrandpaApiServer}; use sc_rpc::DenyUnsafe; use substrate_frame_rpc_system::{System, SystemApiServer}; @@ -329,6 +335,7 @@ pub fn new_full(mut config: Configuration) -> Result keystore: keystore_container.sync_keystore(), task_manager: &mut task_manager, transaction_pool: transaction_pool.clone(), + sync_service: sync_service.clone(), rpc_builder: rpc_extensions_builder, backend: backend.clone(), system_rpc_tx, @@ -369,8 +376,8 @@ pub fn new_full(mut config: Configuration) -> Result force_authoring, backoff_authoring_blocks, keystore: keystore_container.sync_keystore(), - sync_oracle: network.clone(), - justification_sync_link: network.clone(), + sync_oracle: sync_service.clone(), + justification_sync_link: sync_service.clone(), block_proposal_slot_portion: SlotProportion::new(2f32 / 3f32), max_block_proposal_slot_portion: None, telemetry: telemetry.as_ref().map(|x| x.handle()), @@ -391,15 +398,16 @@ pub fn new_full(mut config: Configuration) -> Result if role.is_authority() { Some(keystore_container.sync_keystore()) } else { None }; let justifications_protocol_name = beefy_on_demand_justifications_handler.protocol_name(); - let payload_provider = sp_beefy::mmr::MmrRootProvider::new(client.clone()); - let beefy_params = beefy_gadget::BeefyParams { + let payload_provider = sp_consensus_beefy::mmr::MmrRootProvider::new(client.clone()); + let beefy_params = sc_consensus_beefy::BeefyParams { client: client.clone(), backend, payload_provider, runtime: client, key_store: keystore.clone(), - network_params: beefy_gadget::BeefyNetworkParams { + network_params: sc_consensus_beefy::BeefyNetworkParams { network: network.clone(), + sync: sync_service.clone(), gossip_protocol_name: beefy_gossip_proto_name, justifications_protocol_name, _phantom: core::marker::PhantomData::, @@ -414,10 +422,10 @@ pub fn new_full(mut config: Configuration) -> Result task_manager.spawn_essential_handle().spawn_blocking( "beefy-gadget", None, - beefy_gadget::start_beefy_gadget::<_, _, _, _, _, _>(beefy_params), + sc_consensus_beefy::start_beefy_gadget::<_, _, _, _, _, _, _>(beefy_params), ); - let grandpa_config = sc_finality_grandpa::Config { + let grandpa_config = sc_consensus_grandpa::Config { // FIXME #1578 make this available through chainspec gossip_duration: Duration::from_millis(333), justification_period: 512, @@ -436,11 +444,12 @@ pub fn new_full(mut config: Configuration) -> Result // and vote data availability than the observer. The observer has not // been tested extensively yet and having most nodes in a network run it // could lead to finality stalls. - let grandpa_config = sc_finality_grandpa::GrandpaParams { + let grandpa_config = sc_consensus_grandpa::GrandpaParams { config: grandpa_config, link: grandpa_link, network, - voting_rule: sc_finality_grandpa::VotingRulesBuilder::default().build(), + sync: sync_service, + voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(), prometheus_registry, shared_voter_state, telemetry: telemetry.as_ref().map(|x| x.handle()), @@ -451,7 +460,7 @@ pub fn new_full(mut config: Configuration) -> Result task_manager.spawn_essential_handle().spawn_blocking( "grandpa-voter", None, - sc_finality_grandpa::run_grandpa_voter(grandpa_config)?, + sc_consensus_grandpa::run_grandpa_voter(grandpa_config)?, ); } diff --git a/bin/millau/runtime/Cargo.toml b/bin/millau/runtime/Cargo.toml index 07e3e135aca..2f828393336 100644 --- a/bin/millau/runtime/Cargo.toml +++ b/bin/millau/runtime/Cargo.toml @@ -31,7 +31,7 @@ pallet-shift-session-manager = { path = "../../../modules/shift-session-manager" # Substrate Dependencies -sp-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } frame-executive = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -43,8 +43,7 @@ pallet-beefy = { git = "https://github.com/paritytech/substrate", branch = "mast pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -pallet-randomness-collective-flip = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, features = ["historical"]} pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -80,7 +79,7 @@ substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", bran [features] default = ["std"] std = [ - "sp-beefy/std", + "sp-consensus-beefy/std", "bp-messages/std", "bp-millau/std", "bp-parachains/std", @@ -106,7 +105,6 @@ std = [ "pallet-bridge-relayers/std", "pallet-grandpa/std", "pallet-mmr/std", - "pallet-randomness-collective-flip/std", "pallet-session/std", "pallet-shift-session-manager/std", "pallet-sudo/std", diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index 3e46d6ba15a..21fff0e564e 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -41,9 +41,9 @@ use pallet_grandpa::{ }; use pallet_transaction_payment::{FeeDetails, Multiplier, RuntimeDispatchInfo}; use sp_api::impl_runtime_apis; -use sp_beefy::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_consensus_beefy::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; +use sp_core::OpaqueMetadata; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{Block as BlockT, IdentityLookup, Keccak256, NumberFor, OpaqueKeys}, @@ -223,8 +223,6 @@ impl frame_system::Config for Runtime { type MaxConsumers = frame_support::traits::ConstU32<16>; } -impl pallet_randomness_collective_flip::Config for Runtime {} - impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; type MaxAuthorities = ConstU32<10>; @@ -234,23 +232,21 @@ impl pallet_aura::Config for Runtime { impl pallet_beefy::Config for Runtime { type BeefyId = BeefyId; type MaxAuthorities = ConstU32<10>; + type MaxSetIdSessionEntries = ConstU64<0>; type OnNewValidatorSet = MmrLeaf; + type WeightInfo = (); + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); } impl pallet_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type KeyOwnerProofSystem = (); - type KeyOwnerProof = - >::Proof; - type KeyOwnerIdentification = >::IdentificationTuple; - type HandleEquivocation = (); // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) type WeightInfo = (); type MaxAuthorities = ConstU32<10>; type MaxSetIdSessionEntries = ConstU64<0>; + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); } /// MMR helper types. @@ -292,7 +288,7 @@ parameter_types! { pub struct BeefyDummyDataProvider; -impl sp_beefy::mmr::BeefyDataProvider<()> for BeefyDummyDataProvider { +impl sp_consensus_beefy::mmr::BeefyDataProvider<()> for BeefyDummyDataProvider { fn extra_data() {} } @@ -555,7 +551,6 @@ construct_runtime!( Session: pallet_session::{Pallet, Call, Storage, Event, Config}, Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event}, ShiftSessionManager: pallet_shift_session_manager::{Pallet}, - RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage}, // BEEFY Bridges support. Beefy: pallet_beefy::{Pallet, Storage, Config}, @@ -743,7 +738,7 @@ impl_runtime_apis! { } } - impl sp_beefy::BeefyApi for Runtime { + impl sp_consensus_beefy::BeefyApi for Runtime { fn beefy_genesis() -> Option { Beefy::genesis_block() } @@ -751,6 +746,20 @@ impl_runtime_apis! { fn validator_set() -> Option> { Beefy::validator_set() } + + fn submit_report_equivocation_unsigned_extrinsic( + _equivocation_proof: sp_consensus_beefy::EquivocationProof< + NumberFor, + sp_consensus_beefy::crypto::AuthorityId, + sp_consensus_beefy::crypto::Signature + >, + _key_owner_proof: sp_consensus_beefy::OpaqueKeyOwnershipProof, + ) -> Option<()> { None } + + fn generate_key_ownership_proof( + _set_id: sp_consensus_beefy::ValidatorSetId, + _authority_id: sp_consensus_beefy::crypto::AuthorityId, + ) -> Option { None } } impl pallet_mmr::primitives::MmrApi< @@ -933,9 +942,10 @@ impl_runtime_apis! { let mut list = Vec::::new(); - list_benchmark!(list, extra, pallet_bridge_messages, MessagesBench::); + list_benchmark!(list, extra, RialtoParachainMessages, MessagesBench::); + list_benchmark!(list, extra, RialtoMessages, MessagesBench::); list_benchmark!(list, extra, pallet_bridge_grandpa, BridgeRialtoGrandpa); - list_benchmark!(list, extra, pallet_bridge_parachains, ParachainsBench::); + list_benchmark!(list, extra, pallet_bridge_parachains, ParachainsBench::); list_benchmark!(list, extra, pallet_bridge_relayers, RelayersBench::); let storage_info = AllPalletsWithSystem::storage_info(); @@ -964,7 +974,12 @@ impl_runtime_apis! { let mut batches = Vec::::new(); let params = (&config, &whitelist); - use bridge_runtime_common::messages_benchmarking::{prepare_message_delivery_proof_from_grandpa_chain, prepare_message_proof_from_grandpa_chain}; + use bridge_runtime_common::messages_benchmarking::{ + prepare_message_delivery_proof_from_grandpa_chain, + prepare_message_delivery_proof_from_parachain, + prepare_message_proof_from_grandpa_chain, + prepare_message_proof_from_parachain, + }; use pallet_bridge_messages::benchmarking::{ Pallet as MessagesBench, Config as MessagesConfig, @@ -980,28 +995,66 @@ impl_runtime_apis! { Config as RelayersConfig, }; use rialto_messages::WithRialtoMessageBridge; + use rialto_parachain_messages::WithRialtoParachainMessageBridge; + + impl MessagesConfig for Runtime { + fn prepare_message_proof( + params: MessageProofParams, + ) -> (rialto_messages::FromRialtoMessagesProof, Weight) { + prepare_message_proof_from_parachain::< + Runtime, + WithRialtoParachainsInstance, + WithRialtoParachainMessageBridge, + >(params) + } + + fn prepare_message_delivery_proof( + params: MessageDeliveryProofParams, + ) -> rialto_messages::ToRialtoMessagesDeliveryProof { + prepare_message_delivery_proof_from_parachain::< + Runtime, + WithRialtoParachainsInstance, + WithRialtoParachainMessageBridge, + >(params) + } + + fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { + use bridge_runtime_common::messages::MessageBridge; + + let lane = >::bench_lane_id(); + let bridged_chain_id = WithRialtoParachainMessageBridge::BRIDGED_CHAIN_ID; + pallet_bridge_relayers::Pallet::::relayer_reward( + relayer, + RewardsAccountParams::new(lane, bridged_chain_id, RewardsAccountOwner::BridgedChain) + ).is_some() + } + } impl MessagesConfig for Runtime { fn prepare_message_proof( params: MessageProofParams, ) -> (rialto_messages::FromRialtoMessagesProof, Weight) { - prepare_message_proof_from_grandpa_chain::( - params, - ) + prepare_message_proof_from_grandpa_chain::< + Runtime, + RialtoGrandpaInstance, + WithRialtoMessageBridge, + >(params) } fn prepare_message_delivery_proof( params: MessageDeliveryProofParams, ) -> rialto_messages::ToRialtoMessagesDeliveryProof { - prepare_message_delivery_proof_from_grandpa_chain::( - params, - ) + prepare_message_delivery_proof_from_grandpa_chain::< + Runtime, + RialtoGrandpaInstance, + WithRialtoMessageBridge, + >(params) } fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { use bridge_runtime_common::messages::MessageBridge; - let lane = Self::bench_lane_id(); + let lane = >::bench_lane_id(); let bridged_chain_id = WithRialtoMessageBridge::BRIDGED_CHAIN_ID; pallet_bridge_relayers::Pallet::::relayer_reward( relayer, @@ -1026,7 +1079,10 @@ impl_runtime_apis! { bp_polkadot_core::parachains::ParaHeadsProof, Vec<(bp_polkadot_core::parachains::ParaId, bp_polkadot_core::parachains::ParaHash)>, ) { - bridge_runtime_common::parachains_benchmarking::prepare_parachain_heads_proof::( + bridge_runtime_common::parachains_benchmarking::prepare_parachain_heads_proof::< + Runtime, + WithRialtoParachainsInstance, + >( parachains, parachain_head_size, proof_size, @@ -1051,7 +1107,13 @@ impl_runtime_apis! { add_benchmark!( params, batches, - pallet_bridge_messages, + RialtoParachainMessages, + MessagesBench:: + ); + add_benchmark!( + params, + batches, + RialtoMessages, MessagesBench:: ); add_benchmark!(params, batches, pallet_bridge_grandpa, BridgeRialtoGrandpa); diff --git a/bin/rialto-parachain/node/Cargo.toml b/bin/rialto-parachain/node/Cargo.toml index c20b7f248cc..4cdaa93bad0 100644 --- a/bin/rialto-parachain/node/Cargo.toml +++ b/bin/rialto-parachain/node/Cargo.toml @@ -45,6 +45,7 @@ sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "mas sc-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-network = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-network-sync = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-rpc-api = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-service = { git = "https://github.com/paritytech/substrate", branch = "master"} diff --git a/bin/rialto-parachain/node/src/service.rs b/bin/rialto-parachain/node/src/service.rs index 254340a8ad4..7e7fa2ed09b 100644 --- a/bin/rialto-parachain/node/src/service.rs +++ b/bin/rialto-parachain/node/src/service.rs @@ -38,11 +38,12 @@ use cumulus_client_service::{ prepare_node_config, start_collator, start_full_node, StartCollatorParams, StartFullNodeParams, }; use cumulus_primitives_core::ParaId; -use cumulus_relay_chain_interface::{RelayChainError, RelayChainInterface}; +use cumulus_relay_chain_interface::RelayChainInterface; use sc_consensus::ImportQueue; // Substrate Imports use sc_executor::{NativeElseWasmExecutor, NativeExecutionDispatch}; -use sc_network::{NetworkBlock, NetworkService}; +use sc_network::NetworkBlock; +use sc_network_sync::SyncingService; use sc_service::{Configuration, PartialComponents, TFullBackend, TFullClient, TaskManager}; use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle}; use sp_api::ConstructRuntimeApi; @@ -54,7 +55,6 @@ use substrate_prometheus_endpoint::Registry; type BlockNumber = u32; type Header = sp_runtime::generic::Header; pub type Block = sp_runtime::generic::Block; -type Hash = sp_core::H256; type ParachainClient = TFullClient>; @@ -236,7 +236,7 @@ where &TaskManager, Arc, Arc>>, - Arc>, + Arc>, SyncCryptoStorePtr, bool, ) -> Result>, sc_service::Error>, @@ -257,10 +257,7 @@ where None, ) .await - .map_err(|e| match e { - RelayChainError::ServiceError(polkadot_service::Error::Sub(x)) => x, - s => s.to_string().into(), - })?; + .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; let client = params.client.clone(); let backend = params.backend.clone(); @@ -272,7 +269,7 @@ where let transaction_pool = params.transaction_pool.clone(); let import_queue_service = params.import_queue.service(); - let (network, system_rpc_tx, tx_handler_controller, start_network) = + let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: ¶chain_config, client: client.clone(), @@ -282,7 +279,7 @@ where block_announce_validator_builder: Some(Box::new(|_| { Box::new(block_announce_validator) })), - warp_sync: None, + warp_sync_params: None, })?; let rpc_client = client.clone(); @@ -300,18 +297,23 @@ where keystore: params.keystore_container.sync_keystore(), backend: backend.clone(), network: network.clone(), + sync_service: sync_service.clone(), system_rpc_tx, tx_handler_controller, telemetry: telemetry.as_mut(), })?; let announce_block = { - let network = network.clone(); - Arc::new(move |hash, data| network.announce_block(hash, data)) + let sync_service = sync_service.clone(); + Arc::new(move |hash, data| sync_service.announce_block(hash, data)) }; let relay_chain_slot_duration = Duration::from_secs(6); + let overseer_handle = relay_chain_interface + .overseer_handle() + .map_err(|e| sc_service::Error::Application(Box::new(e)))?; + if validator { let parachain_consensus = build_consensus( client.clone(), @@ -321,7 +323,7 @@ where &task_manager, relay_chain_interface.clone(), transaction_pool, - network, + sync_service, params.keystore_container.sync_keystore(), force_authoring, )?; @@ -340,6 +342,7 @@ where import_queue: import_queue_service, collator_key: collator_key.expect("Command line arguments do not allow this. qed"), relay_chain_slot_duration, + recovery_handle: Box::new(overseer_handle), }; start_collator(params).await?; @@ -352,6 +355,7 @@ where relay_chain_interface, relay_chain_slot_duration, import_queue: import_queue_service, + recovery_handle: Box::new(overseer_handle), }; start_full_node(params)?; diff --git a/bin/rialto-parachain/runtime/Cargo.toml b/bin/rialto-parachain/runtime/Cargo.toml index b91e1cd2751..ec8955e7fd1 100644 --- a/bin/rialto-parachain/runtime/Cargo.toml +++ b/bin/rialto-parachain/runtime/Cargo.toml @@ -52,7 +52,6 @@ frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate" ## Substrate Pallet Dependencies pallet-aura = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -pallet-randomness-collective-flip = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -116,7 +115,6 @@ std = [ "pallet-bridge-grandpa/std", "pallet-bridge-messages/std", "pallet-bridge-relayers/std", - "pallet-randomness-collective-flip/std", "pallet-timestamp/std", "pallet-sudo/std", "pallet-transaction-payment/std", diff --git a/bin/rialto-parachain/runtime/src/lib.rs b/bin/rialto-parachain/runtime/src/lib.rs index 593c7b21228..d8e087c5a6a 100644 --- a/bin/rialto-parachain/runtime/src/lib.rs +++ b/bin/rialto-parachain/runtime/src/lib.rs @@ -302,8 +302,6 @@ impl parachain_info::Config for Runtime {} impl cumulus_pallet_aura_ext::Config for Runtime {} -impl pallet_randomness_collective_flip::Config for Runtime {} - parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); pub const RelayNetwork: NetworkId = CustomNetworkId::Rialto.as_network_id(); @@ -588,7 +586,6 @@ construct_runtime!( System: frame_system::{Pallet, Call, Storage, Config, Event}, Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, Sudo: pallet_sudo::{Pallet, Call, Storage, Config, Event}, - RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage}, TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Storage, Inherent, Event} = 20, diff --git a/bin/rialto/node/Cargo.toml b/bin/rialto/node/Cargo.toml index 556088498f7..be83e02424a 100644 --- a/bin/rialto/node/Cargo.toml +++ b/bin/rialto/node/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] clap = { version = "4.1.6", features = ["derive"] } -serde_json = "1.0.93" +serde_json = "1.0.94" # Bridge dependencies @@ -18,7 +18,7 @@ rialto-runtime = { path = "../runtime" } # Substrate Dependencies -sp-beefy = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -29,7 +29,7 @@ sc-service = { git = "https://github.com/paritytech/substrate", branch = "master sp-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } # Polkadot Dependencies diff --git a/bin/rialto/node/src/chain_spec.rs b/bin/rialto/node/src/chain_spec.rs index cfc0ed62e10..c51ca1197f6 100644 --- a/bin/rialto/node/src/chain_spec.rs +++ b/bin/rialto/node/src/chain_spec.rs @@ -23,10 +23,10 @@ use rialto_runtime::{ }; use serde_json::json; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; -use sp_beefy::crypto::AuthorityId as BeefyId; use sp_consensus_babe::AuthorityId as BabeId; +use sp_consensus_beefy::crypto::AuthorityId as BeefyId; +use sp_consensus_grandpa::AuthorityId as GrandpaId; use sp_core::{sr25519, Pair, Public}; -use sp_finality_grandpa::AuthorityId as GrandpaId; use sp_runtime::traits::{IdentifyAccount, Verify}; /// "Names" of the authorities accounts at local testnet. diff --git a/bin/rialto/runtime/Cargo.toml b/bin/rialto/runtime/Cargo.toml index d5d018fb864..4f0f64cbc95 100644 --- a/bin/rialto/runtime/Cargo.toml +++ b/bin/rialto/runtime/Cargo.toml @@ -26,7 +26,7 @@ pallet-shift-session-manager = { path = "../../../modules/shift-session-manager" # Substrate Dependencies -sp-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +sp-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } frame-executive = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -39,7 +39,7 @@ pallet-beefy = { git = "https://github.com/paritytech/substrate", branch = "mast pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, features = ["historical"]} pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -78,7 +78,7 @@ substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", bran [features] default = ["std"] std = [ - "sp-beefy/std", + "sp-consensus-beefy/std", "bp-messages/std", "bp-millau/std", "bp-relayers/std", @@ -103,6 +103,7 @@ std = [ "pallet-grandpa/std", "pallet-mmr/std", "pallet-xcm/std", + "pallet-session/std", "pallet-shift-session-manager/std", "pallet-sudo/std", "pallet-timestamp/std", diff --git a/bin/rialto/runtime/src/lib.rs b/bin/rialto/runtime/src/lib.rs index 9873a6fd855..9db181e2840 100644 --- a/bin/rialto/runtime/src/lib.rs +++ b/bin/rialto/runtime/src/lib.rs @@ -39,8 +39,8 @@ use pallet_grandpa::{ use pallet_transaction_payment::{FeeDetails, Multiplier, RuntimeDispatchInfo}; use sp_api::impl_runtime_apis; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; -use sp_beefy::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_consensus_beefy::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; +use sp_core::OpaqueMetadata; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{AccountIdLookup, Block as BlockT, Keccak256, NumberFor, OpaqueKeys}, @@ -225,56 +225,47 @@ parameter_types! { impl pallet_babe::Config for Runtime { type EpochDuration = EpochDuration; type ExpectedBlockTime = ExpectedBlockTime; - type MaxAuthorities = ConstU32<10>; - // session module is the trigger type EpochChangeTrigger = pallet_babe::ExternalTrigger; - // equivocation related configuration - we don't expect any equivocations in our testnets - type KeyOwnerProofSystem = (); - type KeyOwnerProof = >::Proof; - type KeyOwnerIdentification = >::IdentificationTuple; - type HandleEquivocation = (); - type DisabledValidators = (); + type WeightInfo = (); + + type MaxAuthorities = ConstU32<10>; + + // equivocation related configuration - we don't expect any equivocations in our testnets + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); } impl pallet_beefy::Config for Runtime { type BeefyId = BeefyId; type MaxAuthorities = ConstU32<10>; + type MaxSetIdSessionEntries = ConstU64<0>; type OnNewValidatorSet = MmrLeaf; + type WeightInfo = (); + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); } impl pallet_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type MaxAuthorities = ConstU32<10>; - type KeyOwnerProofSystem = (); - type KeyOwnerProof = - >::Proof; - type KeyOwnerIdentification = >::IdentificationTuple; - type HandleEquivocation = (); // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) type WeightInfo = (); + type MaxAuthorities = ConstU32<10>; type MaxSetIdSessionEntries = ConstU64<0>; + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); } impl pallet_mmr::Config for Runtime { const INDEXING_PREFIX: &'static [u8] = b"mmr"; type Hashing = Keccak256; type Hash = ::Output; + type LeafData = pallet_beefy_mmr::Pallet; type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest; type WeightInfo = (); - type LeafData = pallet_beefy_mmr::Pallet; } parameter_types! { @@ -296,7 +287,7 @@ parameter_types! { pub struct BeefyDummyDataProvider; -impl sp_beefy::mmr::BeefyDataProvider<()> for BeefyDummyDataProvider { +impl sp_consensus_beefy::mmr::BeefyDataProvider<()> for BeefyDummyDataProvider { fn extra_data() {} } @@ -616,7 +607,7 @@ impl_runtime_apis! { } } - impl sp_beefy::BeefyApi for Runtime { + impl sp_consensus_beefy::BeefyApi for Runtime { fn beefy_genesis() -> Option { Beefy::genesis_block() } @@ -624,6 +615,20 @@ impl_runtime_apis! { fn validator_set() -> Option> { Beefy::validator_set() } + + fn submit_report_equivocation_unsigned_extrinsic( + _equivocation_proof: sp_consensus_beefy::EquivocationProof< + NumberFor, + sp_consensus_beefy::crypto::AuthorityId, + sp_consensus_beefy::crypto::Signature + >, + _key_owner_proof: sp_consensus_beefy::OpaqueKeyOwnershipProof, + ) -> Option<()> { None } + + fn generate_key_ownership_proof( + _set_id: sp_consensus_beefy::ValidatorSetId, + _authority_id: sp_consensus_beefy::crypto::AuthorityId, + ) -> Option { None } } impl pallet_mmr::primitives::MmrApi< diff --git a/bin/runtime-common/src/lib.rs b/bin/runtime-common/src/lib.rs index d9c87049077..ed486c04abc 100644 --- a/bin/runtime-common/src/lib.rs +++ b/bin/runtime-common/src/lib.rs @@ -28,6 +28,7 @@ pub mod messages; pub mod messages_api; pub mod messages_benchmarking; pub mod messages_call_ext; +pub mod messages_xcm_extension; pub mod parachains_benchmarking; pub mod refund_relayer_extension; @@ -37,6 +38,8 @@ mod mock; #[cfg(feature = "integrity-test")] pub mod integrity; +const LOG_TARGET_BRIDGE_DISPATCH: &str = "runtime::bridge-dispatch"; + /// A duplication of the `FilterCall` trait. /// /// We need this trait in order to be able to implement it for the messages pallet, diff --git a/bin/runtime-common/src/messages.rs b/bin/runtime-common/src/messages.rs index ceedee12afa..4493ac66742 100644 --- a/bin/runtime-common/src/messages.rs +++ b/bin/runtime-common/src/messages.rs @@ -100,6 +100,29 @@ pub type OriginOf = ::RuntimeOrigin; /// Type of call that is used on this chain. pub type CallOf = ::RuntimeCall; +/// Error that happens during message verification. +#[derive(Debug, PartialEq, Eq)] +pub enum Error { + /// The message proof is empty. + EmptyMessageProof, + /// Error returned by the bridged header chain. + HeaderChain(HeaderChainError), + /// Error returned while reading/decoding inbound lane data from the storage proof. + InboundLaneStorage(StorageProofError), + /// The declared message weight is incorrect. + InvalidMessageWeight, + /// Declared messages count doesn't match actual value. + MessagesCountMismatch, + /// Error returned while reading/decoding message data from the storage proof. + MessageStorage(StorageProofError), + /// The message is too large. + MessageTooLarge, + /// Error returned while reading/decoding outbound lane data from the storage proof. + OutboundLaneStorage(StorageProofError), + /// Storage proof related error. + StorageProof(StorageProofError), +} + /// Sub-module that is declaring types required for processing This -> Bridged chain messages. pub mod source { use super::*; @@ -169,8 +192,6 @@ pub mod source { /// The error message returned from `LaneMessageVerifier` when too many pending messages at the /// lane. pub const TOO_MANY_PENDING_MESSAGES: &str = "Too many pending messages at the lane."; - /// The error message returned from `LaneMessageVerifier` when call origin is mismatch. - pub const BAD_ORIGIN: &str = "Unable to match the source origin to expected target origin."; impl LaneMessageVerifier>, FromThisChainMessagePayload> for FromThisChainMessageVerifier @@ -220,7 +241,7 @@ pub mod source { impl TargetHeaderChain>> for TargetHeaderChainAdapter { - type Error = &'static str; + type Error = Error; type MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof>>; fn verify_message(payload: &FromThisChainMessagePayload) -> Result<(), Self::Error> { @@ -241,9 +262,9 @@ pub mod source { /// check) that would reject message (see `FromThisChainMessageVerifier`). pub fn verify_chain_message( payload: &FromThisChainMessagePayload, - ) -> Result<(), &'static str> { + ) -> Result<(), Error> { if !BridgedChain::::verify_dispatch_weight(payload) { - return Err("Incorrect message weight declared") + return Err(Error::InvalidMessageWeight) } // The maximal size of extrinsic at Substrate-based chain depends on the @@ -257,7 +278,7 @@ pub mod source { // transaction also contains signatures and signed extensions. Because of this, we reserve // 1/3 of the the maximal extrinsic weight for this data. if payload.len() > maximal_message_size::() as usize { - return Err("The message is too large to be sent over the lane") + return Err(Error::MessageTooLarge) } Ok(()) @@ -269,7 +290,7 @@ pub mod source { /// parachains, please use the `verify_messages_delivery_proof_from_parachain`. pub fn verify_messages_delivery_proof( proof: FromBridgedChainMessagesDeliveryProof>>, - ) -> Result, &'static str> { + ) -> Result, Error> { let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } = proof; B::BridgedHeaderChain::parse_finalized_storage_proof( @@ -283,22 +304,17 @@ pub mod source { B::BRIDGED_MESSAGES_PALLET_NAME, &lane, ); - let raw_inbound_lane_data = storage - .read_value(storage_inbound_lane_data_key.0.as_ref()) - .map_err(|_| "Failed to read inbound lane state from storage proof")? - .ok_or("Inbound lane state is missing from the messages proof")?; - let inbound_lane_data = InboundLaneData::decode(&mut &raw_inbound_lane_data[..]) - .map_err(|_| "Failed to decode inbound lane state from the proof")?; + let inbound_lane_data = storage + .read_and_decode_mandatory_value(storage_inbound_lane_data_key.0.as_ref()) + .map_err(Error::InboundLaneStorage)?; // check that the storage proof doesn't have any untouched trie nodes - storage - .ensure_no_unused_nodes() - .map_err(|_| "Messages delivery proof has unused trie nodes")?; + storage.ensure_no_unused_nodes().map_err(Error::StorageProof)?; Ok((lane, inbound_lane_data)) }, ) - .map_err(<&'static str>::from)? + .map_err(Error::HeaderChain)? } /// XCM bridge. @@ -481,7 +497,7 @@ pub mod target { let weight = XcmWeigher::weight(&mut payload.xcm.1); let weight = weight.unwrap_or_else(|e| { log::debug!( - target: "runtime::bridge-dispatch", + target: crate::LOG_TARGET_BRIDGE_DISPATCH, "Failed to compute dispatch weight of incoming XCM message {:?}/{}: {:?}", message.key.lane_id, message.key.nonce, @@ -509,7 +525,7 @@ pub mod target { let FromBridgedChainMessagePayload { xcm: (location, xcm), weight: weight_limit } = message.data.payload?; log::trace!( - target: "runtime::bridge-dispatch", + target: crate::LOG_TARGET_BRIDGE_DISPATCH, "Going to execute message {:?} (weight limit: {:?}): {:?} {:?}", message_id, weight_limit, @@ -535,7 +551,7 @@ pub mod target { match xcm_outcome { Ok(outcome) => { log::trace!( - target: "runtime::bridge-dispatch", + target: crate::LOG_TARGET_BRIDGE_DISPATCH, "Incoming message {:?} dispatched with result: {:?}", message_id, outcome, @@ -544,7 +560,7 @@ pub mod target { Ok(_weight) => (), Err(e) => { log::error!( - target: "runtime::bridge-dispatch", + target: crate::LOG_TARGET_BRIDGE_DISPATCH, "Incoming message {:?} was not dispatched, error: {:?}", message_id, e, @@ -554,7 +570,7 @@ pub mod target { }, Err(e) => { log::error!( - target: "runtime::bridge-dispatch", + target: crate::LOG_TARGET_BRIDGE_DISPATCH, "Incoming message {:?} was not dispatched, codec error: {:?}", message_id, e, @@ -580,14 +596,14 @@ pub mod target { pub struct SourceHeaderChainAdapter(PhantomData); impl SourceHeaderChain for SourceHeaderChainAdapter { - type Error = &'static str; + type Error = Error; type MessagesProof = FromBridgedChainMessagesProof>>; fn verify_messages_proof( proof: Self::MessagesProof, messages_count: u32, ) -> Result, Self::Error> { - verify_messages_proof::(proof, messages_count).map_err(Into::into) + verify_messages_proof::(proof, messages_count) } } @@ -602,7 +618,7 @@ pub mod target { pub fn verify_messages_proof( proof: FromBridgedChainMessagesProof>>, messages_count: u32, - ) -> Result, MessageProofError> { + ) -> Result, Error> { let FromBridgedChainMessagesProof { bridged_header_hash, storage_proof, @@ -625,7 +641,7 @@ pub mod target { // (this bounds maximal capacity of messages vec below) let messages_in_the_proof = nonces_difference.saturating_add(1); if messages_in_the_proof != MessageNonce::from(messages_count) { - return Err(MessageProofError::MessagesCountMismatch) + return Err(Error::MessagesCountMismatch) } messages_in_the_proof @@ -640,37 +656,26 @@ pub mod target { let mut messages = Vec::with_capacity(messages_in_the_proof as _); for nonce in nonces_start..=nonces_end { let message_key = MessageKey { lane_id: lane, nonce }; - let raw_message_data = parser - .read_raw_message(&message_key) - .ok_or(MessageProofError::MissingRequiredMessage)?; - let payload = MessagePayload::decode(&mut &raw_message_data[..]) - .map_err(|_| MessageProofError::FailedToDecodeMessage)?; - messages.push(Message { key: message_key, payload }); + let message_payload = parser.read_and_decode_message_payload(&message_key)?; + messages.push(Message { key: message_key, payload: message_payload }); } // Now let's check if proof contains outbound lane state proof. It is optional, so // we simply ignore `read_value` errors and missing value. - let mut proved_lane_messages = ProvedLaneMessages { lane_state: None, messages }; - let raw_outbound_lane_data = parser.read_raw_outbound_lane_data(&lane); - if let Some(raw_outbound_lane_data) = raw_outbound_lane_data { - proved_lane_messages.lane_state = Some( - OutboundLaneData::decode(&mut &raw_outbound_lane_data[..]) - .map_err(|_| MessageProofError::FailedToDecodeOutboundLaneState)?, - ); - } + let proved_lane_messages = ProvedLaneMessages { + lane_state: parser.read_and_decode_outbound_lane_data(&lane)?, + messages, + }; // Now we may actually check if the proof is empty or not. if proved_lane_messages.lane_state.is_none() && proved_lane_messages.messages.is_empty() { - return Err(MessageProofError::Empty) + return Err(Error::EmptyMessageProof) } // check that the storage proof doesn't have any untouched trie nodes - parser - .storage - .ensure_no_unused_nodes() - .map_err(MessageProofError::StorageProof)?; + parser.storage.ensure_no_unused_nodes().map_err(Error::StorageProof)?; // We only support single lane messages in this generated_schema let mut proved_messages = ProvedMessages::new(); @@ -679,43 +684,7 @@ pub mod target { Ok(proved_messages) }, ) - .map_err(MessageProofError::HeaderChain)? - } - - /// Error that happens during message proof verification. - #[derive(Debug, PartialEq, Eq)] - pub enum MessageProofError { - /// Error returned by the bridged header chain. - HeaderChain(HeaderChainError), - /// The message proof is empty. - Empty, - /// Declared messages count doesn't match actual value. - MessagesCountMismatch, - /// Message is missing from the proof. - MissingRequiredMessage, - /// Failed to decode message from the proof. - FailedToDecodeMessage, - /// Failed to decode outbound lane data from the proof. - FailedToDecodeOutboundLaneState, - /// Storage proof related error. - StorageProof(StorageProofError), - } - - impl From for &'static str { - fn from(err: MessageProofError) -> &'static str { - match err { - MessageProofError::HeaderChain(err) => err.into(), - MessageProofError::Empty => "Messages proof is empty", - MessageProofError::MessagesCountMismatch => - "Declared messages count doesn't match actual value", - MessageProofError::MissingRequiredMessage => "Message is missing from the proof", - MessageProofError::FailedToDecodeMessage => - "Failed to decode message from the proof", - MessageProofError::FailedToDecodeOutboundLaneState => - "Failed to decode outbound lane data from the proof", - MessageProofError::StorageProof(_) => "Invalid storage proof", - } - } + .map_err(Error::HeaderChain)? } struct StorageProofCheckerAdapter { @@ -724,21 +693,32 @@ pub mod target { } impl StorageProofCheckerAdapter { - fn read_raw_outbound_lane_data(&mut self, lane_id: &LaneId) -> Option> { + fn read_and_decode_outbound_lane_data( + &mut self, + lane_id: &LaneId, + ) -> Result, Error> { let storage_outbound_lane_data_key = bp_messages::storage_keys::outbound_lane_data_key( B::BRIDGED_MESSAGES_PALLET_NAME, lane_id, ); - self.storage.read_value(storage_outbound_lane_data_key.0.as_ref()).ok()? + + self.storage + .read_and_decode_opt_value(storage_outbound_lane_data_key.0.as_ref()) + .map_err(Error::OutboundLaneStorage) } - fn read_raw_message(&mut self, message_key: &MessageKey) -> Option> { + fn read_and_decode_message_payload( + &mut self, + message_key: &MessageKey, + ) -> Result { let storage_message_key = bp_messages::storage_keys::message_key( B::BRIDGED_MESSAGES_PALLET_NAME, &message_key.lane_id, message_key.nonce, ); - self.storage.read_value(storage_message_key.0.as_ref()).ok()? + self.storage + .read_and_decode_mandatory_value(storage_message_key.0.as_ref()) + .map_err(Error::MessageStorage) } } } @@ -896,7 +876,7 @@ mod tests { using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { target::verify_messages_proof::(proof, 5) }), - Err(target::MessageProofError::MessagesCountMismatch), + Err(Error::MessagesCountMismatch), ); } @@ -906,7 +886,7 @@ mod tests { using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { target::verify_messages_proof::(proof, 15) }), - Err(target::MessageProofError::MessagesCountMismatch), + Err(Error::MessagesCountMismatch), ); } @@ -919,7 +899,7 @@ mod tests { pallet_bridge_grandpa::ImportedHeaders::::remove(bridged_header_hash); target::verify_messages_proof::(proof, 10) }), - Err(target::MessageProofError::HeaderChain(HeaderChainError::UnknownHeader)), + Err(Error::HeaderChain(HeaderChainError::UnknownHeader)), ); } @@ -942,7 +922,7 @@ mod tests { ); target::verify_messages_proof::(proof, 10) }), - Err(target::MessageProofError::HeaderChain(HeaderChainError::StorageProof( + Err(Error::HeaderChain(HeaderChainError::StorageProof( StorageProofError::StorageRootMismatch ))), ); @@ -957,7 +937,7 @@ mod tests { proof.storage_proof.push(node); target::verify_messages_proof::(proof, 10) },), - Err(target::MessageProofError::HeaderChain(HeaderChainError::StorageProof( + Err(Error::HeaderChain(HeaderChainError::StorageProof( StorageProofError::DuplicateNodesInProof ))), ); @@ -970,13 +950,13 @@ mod tests { proof.storage_proof.push(vec![42]); target::verify_messages_proof::(proof, 10) },), - Err(target::MessageProofError::StorageProof(StorageProofError::UnusedNodesInTheProof)), + Err(Error::StorageProof(StorageProofError::UnusedNodesInTheProof)), ); } #[test] fn message_proof_is_rejected_if_required_message_is_missing() { - assert_eq!( + matches!( using_messages_proof( 10, None, @@ -984,13 +964,13 @@ mod tests { encode_lane_data, |proof| target::verify_messages_proof::(proof, 10) ), - Err(target::MessageProofError::MissingRequiredMessage), + Err(Error::MessageStorage(StorageProofError::StorageValueEmpty)), ); } #[test] fn message_proof_is_rejected_if_message_decode_fails() { - assert_eq!( + matches!( using_messages_proof( 10, None, @@ -1004,13 +984,13 @@ mod tests { encode_lane_data, |proof| target::verify_messages_proof::(proof, 10), ), - Err(target::MessageProofError::FailedToDecodeMessage), + Err(Error::MessageStorage(StorageProofError::StorageValueDecodeFailed(_))), ); } #[test] fn message_proof_is_rejected_if_outbound_lane_state_decode_fails() { - assert_eq!( + matches!( using_messages_proof( 10, Some(OutboundLaneData { @@ -1026,7 +1006,7 @@ mod tests { }, |proof| target::verify_messages_proof::(proof, 10), ), - Err(target::MessageProofError::FailedToDecodeOutboundLaneState), + Err(Error::OutboundLaneStorage(StorageProofError::StorageValueDecodeFailed(_))), ); } @@ -1036,7 +1016,7 @@ mod tests { using_messages_proof(0, None, encode_all_messages, encode_lane_data, |proof| { target::verify_messages_proof::(proof, 0) },), - Err(target::MessageProofError::Empty), + Err(Error::EmptyMessageProof), ); } @@ -1110,7 +1090,7 @@ mod tests { proof.nonces_end = u64::MAX; target::verify_messages_proof::(proof, u32::MAX) },), - Err(target::MessageProofError::MessagesCountMismatch), + Err(Error::MessagesCountMismatch), ); } } diff --git a/bin/runtime-common/src/messages_benchmarking.rs b/bin/runtime-common/src/messages_benchmarking.rs index 1588c77633e..028172e7ab4 100644 --- a/bin/runtime-common/src/messages_benchmarking.rs +++ b/bin/runtime-common/src/messages_benchmarking.rs @@ -25,7 +25,7 @@ use crate::{ AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, ThisChain, }, messages_generation::{ - encode_all_messages, encode_lane_data, grow_trie, prepare_messages_storage_proof, + encode_all_messages, encode_lane_data, grow_trie_leaf_value, prepare_messages_storage_proof, }, }; @@ -204,11 +204,12 @@ where { let mut trie = TrieDBMutBuilderV1::>>::new(&mut mdb, &mut root).build(); - trie.insert(&storage_key, ¶ms.inbound_lane_data.encode()) + let inbound_lane_data = + grow_trie_leaf_value(params.inbound_lane_data.encode(), params.size); + trie.insert(&storage_key, &inbound_lane_data) .map_err(|_| "TrieMut::insert has failed") .expect("TrieMut::insert should not fail in benchmarks"); } - root = grow_trie(root, &mut mdb, params.size); // generate storage proof to be delivered to This chain let storage_proof = record_all_trie_keys::>>, _>(&mdb, &root) diff --git a/bin/runtime-common/src/messages_generation.rs b/bin/runtime-common/src/messages_generation.rs index aec97c22800..29a869a5c87 100644 --- a/bin/runtime-common/src/messages_generation.rs +++ b/bin/runtime-common/src/messages_generation.rs @@ -25,7 +25,6 @@ use bp_messages::{ }; use bp_runtime::{record_all_trie_keys, RawStorageProof, StorageProofSize}; use codec::Encode; -use sp_core::Hasher; use sp_std::{ops::RangeInclusive, prelude::*}; use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; @@ -65,10 +64,15 @@ where TrieDBMutBuilderV1::>>::new(&mut mdb, &mut root).build(); // insert messages - for nonce in message_nonces { + for (i, nonce) in message_nonces.into_iter().enumerate() { let message_key = MessageKey { lane_id: lane, nonce }; let message_payload = match encode_message(nonce, &message_payload) { - Some(message_payload) => message_payload, + Some(message_payload) => + if i == 0 { + grow_trie_leaf_value(message_payload, size) + } else { + message_payload + }, None => continue, }; let storage_key = storage_keys::message_key( @@ -94,46 +98,22 @@ where storage_keys.push(storage_key); } } - root = grow_trie(root, &mut mdb, size); // generate storage proof to be delivered to This chain let storage_proof = record_all_trie_keys::>>, _>(&mdb, &root) .map_err(|_| "record_all_trie_keys has failed") .expect("record_all_trie_keys should not fail in benchmarks"); - (root, storage_proof) } -/// Populate trie with dummy keys+values until trie has at least given size. -pub fn grow_trie( - mut root: H::Out, - mdb: &mut MemoryDB, - trie_size: StorageProofSize, -) -> H::Out { - let (iterations, leaf_size, minimal_trie_size) = match trie_size { - StorageProofSize::Minimal(_) => return root, - StorageProofSize::HasLargeLeaf(size) => (1, size, size), - StorageProofSize::HasExtraNodes(size) => (8, 1, size), - }; - - let mut key_index = 0; - loop { - // generate storage proof to be delivered to This chain - let storage_proof = record_all_trie_keys::, _>(mdb, &root) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); - let size: usize = storage_proof.iter().map(|n| n.len()).sum(); - if size > minimal_trie_size as _ { - return root - } - - let mut trie = TrieDBMutBuilderV1::::from_existing(mdb, &mut root).build(); - for _ in 0..iterations { - trie.insert(&key_index.encode(), &vec![42u8; leaf_size as _]) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in benchmarks"); - key_index += 1; - } - trie.commit(); +/// Add extra data to the trie leaf value so that it'll be of given size. +pub fn grow_trie_leaf_value(mut value: Vec, size: StorageProofSize) -> Vec { + match size { + StorageProofSize::Minimal(_) => (), + StorageProofSize::HasLargeLeaf(size) if size as usize > value.len() => { + value.extend(sp_std::iter::repeat(42u8).take(size as usize - value.len())); + }, + StorageProofSize::HasLargeLeaf(_) => (), } + value } diff --git a/bin/runtime-common/src/messages_xcm_extension.rs b/bin/runtime-common/src/messages_xcm_extension.rs new file mode 100644 index 00000000000..7163d8a5357 --- /dev/null +++ b/bin/runtime-common/src/messages_xcm_extension.rs @@ -0,0 +1,181 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Module provides utilities for easier XCM handling, e.g: +//! [`XcmExecutor`] -> [`MessageSender`] -> +//! | +//! +//! | +//! [`XcmRouter`] <- [`MessageDispatch`] <- + +use bp_messages::{ + source_chain::MessagesBridge, + target_chain::{DispatchMessage, MessageDispatch}, + LaneId, +}; +use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, Chain}; +use codec::{Decode, Encode}; +use frame_support::{dispatch::Weight, traits::Get, CloneNoBound, EqNoBound, PartialEqNoBound}; +use scale_info::TypeInfo; +use xcm_builder::{DispatchBlob, DispatchBlobError, HaulBlob, HaulBlobError}; + +/// Plain "XCM" payload, which we transfer through bridge +pub type XcmAsPlainPayload = sp_std::prelude::Vec; + +/// Message dispatch result type for single message +#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] +pub enum XcmBlobMessageDispatchResult { + InvalidPayload, + Dispatched, + NotDispatched(#[codec(skip)] &'static str), +} + +/// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages +pub struct XcmBlobMessageDispatch< + SourceBridgeHubChain, + TargetBridgeHubChain, + DispatchBlob, + DispatchBlobWeigher, +> { + _marker: sp_std::marker::PhantomData<( + SourceBridgeHubChain, + TargetBridgeHubChain, + DispatchBlob, + DispatchBlobWeigher, + )>, +} + +impl< + SourceBridgeHubChain: Chain, + TargetBridgeHubChain: Chain, + BlobDispatcher: DispatchBlob, + DispatchBlobWeigher: Get, + > MessageDispatch> + for XcmBlobMessageDispatch< + SourceBridgeHubChain, + TargetBridgeHubChain, + BlobDispatcher, + DispatchBlobWeigher, + > +{ + type DispatchPayload = XcmAsPlainPayload; + type DispatchLevelResult = XcmBlobMessageDispatchResult; + + fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { + DispatchBlobWeigher::get() + } + + fn dispatch( + _relayer_account: &AccountIdOf, + message: DispatchMessage, + ) -> MessageDispatchResult { + let payload = match message.data.payload { + Ok(payload) => payload, + Err(e) => { + log::error!( + target: crate::LOG_TARGET_BRIDGE_DISPATCH, + "[XcmBlobMessageDispatch] payload error: {:?} - message_nonce: {:?}", + e, + message.key.nonce + ); + return MessageDispatchResult { + // TODO:check-parameter - setup uspent_weight? https://github.com/paritytech/polkadot/issues/6629 + unspent_weight: Weight::zero(), + dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, + } + }, + }; + let dispatch_level_result = match BlobDispatcher::dispatch_blob(payload) { + Ok(_) => { + log::debug!( + target: crate::LOG_TARGET_BRIDGE_DISPATCH, + "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}", + message.key.nonce + ); + XcmBlobMessageDispatchResult::Dispatched + }, + Err(e) => { + let e = match e { + DispatchBlobError::Unbridgable => "DispatchBlobError::Unbridgable", + DispatchBlobError::InvalidEncoding => "DispatchBlobError::InvalidEncoding", + DispatchBlobError::UnsupportedLocationVersion => + "DispatchBlobError::UnsupportedLocationVersion", + DispatchBlobError::UnsupportedXcmVersion => + "DispatchBlobError::UnsupportedXcmVersion", + DispatchBlobError::RoutingError => "DispatchBlobError::RoutingError", + DispatchBlobError::NonUniversalDestination => + "DispatchBlobError::NonUniversalDestination", + DispatchBlobError::WrongGlobal => "DispatchBlobError::WrongGlobal", + }; + log::error!( + target: crate::LOG_TARGET_BRIDGE_DISPATCH, + "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?} - message_nonce: {:?}", + e, message.key.nonce + ); + XcmBlobMessageDispatchResult::NotDispatched(e) + }, + }; + MessageDispatchResult { + // TODO:check-parameter - setup uspent_weight? https://github.com/paritytech/polkadot/issues/6629 + unspent_weight: Weight::zero(), + dispatch_level_result, + } + } +} + +/// [`XcmBlobHauler`] is responsible for sending messages to the bridge "point-to-point link" from +/// one side, where on the other it can be dispatched by [`XcmBlobMessageDispatch`]. +pub trait XcmBlobHauler { + /// Runtime message sender adapter. + type MessageSender: MessagesBridge; + + /// Runtime message sender origin, which is used by [`MessageSender`]. + type MessageSenderOrigin; + /// Our location within the Consensus Universe. + fn message_sender_origin() -> Self::MessageSenderOrigin; + + /// Return message lane (as "point-to-point link") used to deliver XCM messages. + fn xcm_lane() -> LaneId; +} + +/// XCM bridge adapter which connects [`XcmBlobHauler`] with [`MessageSender`] and makes sure that +/// XCM blob is sent to the [`pallet_bridge_messages`] queue to be relayed. +pub struct XcmBlobHaulerAdapter(sp_std::marker::PhantomData); +impl> HaulBlob + for XcmBlobHaulerAdapter +{ + fn haul_blob(blob: sp_std::prelude::Vec) -> Result<(), HaulBlobError> { + let lane = H::xcm_lane(); + let result = H::MessageSender::send_message(H::message_sender_origin(), lane, blob); + let result = result + .map(|artifacts| (lane, artifacts.nonce).using_encoded(sp_io::hashing::blake2_256)); + match &result { + Ok(result) => log::info!( + target: crate::LOG_TARGET_BRIDGE_DISPATCH, + "haul_blob result - ok: {:?} on lane: {:?}", + result, + lane + ), + Err(error) => log::error!( + target: crate::LOG_TARGET_BRIDGE_DISPATCH, + "haul_blob result - error: {:?} on lane: {:?}", + error, + lane + ), + }; + result.map(|_| ()).map_err(|_| HaulBlobError::Transport("MessageSenderError")) + } +} diff --git a/bin/runtime-common/src/parachains_benchmarking.rs b/bin/runtime-common/src/parachains_benchmarking.rs index e549e4f79b9..aad53673c3a 100644 --- a/bin/runtime-common/src/parachains_benchmarking.rs +++ b/bin/runtime-common/src/parachains_benchmarking.rs @@ -19,7 +19,8 @@ #![cfg(feature = "runtime-benchmarks")] use crate::{ - messages_benchmarking::insert_header_to_grandpa_pallet, messages_generation::grow_trie, + messages_benchmarking::insert_header_to_grandpa_pallet, + messages_generation::grow_trie_leaf_value, }; use bp_parachains::parachain_head_storage_key_at_source; @@ -59,17 +60,21 @@ where TrieDBMutBuilderV1::::new(&mut mdb, &mut state_root).build(); // insert parachain heads - for parachain in parachains { + for (i, parachain) in parachains.into_iter().enumerate() { let storage_key = parachain_head_storage_key_at_source(R::ParasPalletName::get(), *parachain); - trie.insert(&storage_key.0, ¶chain_head.encode()) + let leaf_data = if i == 0 { + grow_trie_leaf_value(parachain_head.encode(), size) + } else { + parachain_head.encode() + }; + trie.insert(&storage_key.0, &leaf_data) .map_err(|_| "TrieMut::insert has failed") .expect("TrieMut::insert should not fail in benchmarks"); storage_keys.push(storage_key); parachain_heads.push((*parachain, parachain_head.hash())) } } - state_root = grow_trie(state_root, &mut mdb, size); // generate heads storage proof let proof = record_all_trie_keys::, _>(&mdb, &state_root) diff --git a/bin/runtime-common/src/refund_relayer_extension.rs b/bin/runtime-common/src/refund_relayer_extension.rs index 0a61f4fdd02..7be97f19ad2 100644 --- a/bin/runtime-common/src/refund_relayer_extension.rs +++ b/bin/runtime-common/src/refund_relayer_extension.rs @@ -746,8 +746,9 @@ mod tests { fn dispatch_info() -> DispatchInfo { DispatchInfo { - weight: Weight::from_ref_time( + weight: Weight::from_parts( frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, + 0, ), class: frame_support::dispatch::DispatchClass::Normal, pays_fee: frame_support::dispatch::Pays::Yes, @@ -1037,8 +1038,9 @@ mod tests { initialize_environment(200, 200, [1u8; 32].into(), 200); let mut dispatch_info = dispatch_info(); - dispatch_info.weight = Weight::from_ref_time( + dispatch_info.weight = Weight::from_parts( frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND * 2, + 0, ); // without any size/weight refund: we expect regular reward diff --git a/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-parachains-dashboard.json b/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-parachains-dashboard.json index 007b6ef9325..31a7cca5229 100644 --- a/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-parachains-dashboard.json +++ b/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-parachains-dashboard.json @@ -98,19 +98,19 @@ "steppedLine": false, "targets": [ { - "expr": "Westend_to_Millau_Parachains_best_parachain_block_number_at_source{parachain='para_2000'}", + "expr": "Westend_to_Millau_Parachains_1000_best_parachain_block_number_at_source", "interval": "", "legendFormat": "At Westend", "refId": "A" }, { - "expr": "Westend_to_Millau_Parachains_best_parachain_block_number_at_target{parachain='para_2000'}", + "expr": "Westend_to_Millau_Parachains_1000_best_parachain_block_number_at_target", "interval": "", "legendFormat": "At Millau", "refId": "B" }, { - "expr": "Westend_to_Millau_Parachains_best_parachain_block_number_at_source{parachain='para_2000'} - Westend_to_Millau_Parachains_best_parachain_block_number_at_target{parachain='para_2000'}", + "expr": "Westend_to_Millau_Parachains_1000_best_parachain_block_number_at_source - Westend_to_Millau_Parachains_1000_best_parachain_block_number_at_target", "hide": true, "interval": "", "legendFormat": "Missing Westmint headers at Millau", diff --git a/modules/beefy/Cargo.toml b/modules/beefy/Cargo.toml index 1908bd50c8a..b50ac723db2 100644 --- a/modules/beefy/Cargo.toml +++ b/modules/beefy/Cargo.toml @@ -25,7 +25,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [dev-dependencies] -sp-beefy = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "master" } mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.3.2" } pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/modules/beefy/src/lib.rs b/modules/beefy/src/lib.rs index e05a4119463..a3a68928bc5 100644 --- a/modules/beefy/src/lib.rs +++ b/modules/beefy/src/lib.rs @@ -133,7 +133,7 @@ pub mod pallet { fn on_initialize(_n: T::BlockNumber) -> frame_support::weights::Weight { >::mutate(|count| *count = count.saturating_sub(1)); - Weight::from_ref_time(0) + Weight::from_parts(0, 0) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -419,7 +419,7 @@ mod tests { use frame_support::{assert_noop, assert_ok, traits::Get}; use mock::*; use mock_chain::*; - use sp_beefy::mmr::BeefyAuthoritySet; + use sp_consensus_beefy::mmr::BeefyAuthoritySet; use sp_runtime::DispatchError; fn next_block() { diff --git a/modules/beefy/src/mock.rs b/modules/beefy/src/mock.rs index 212c6c5d434..1ffccc5dfbc 100644 --- a/modules/beefy/src/mock.rs +++ b/modules/beefy/src/mock.rs @@ -32,7 +32,7 @@ use sp_runtime::{ Perbill, }; -pub use sp_beefy::crypto::{AuthorityId as BeefyId, Pair as BeefyPair}; +pub use sp_consensus_beefy::crypto::{AuthorityId as BeefyId, Pair as BeefyPair}; use sp_core::crypto::Wraps; use sp_runtime::traits::Keccak256; @@ -49,7 +49,7 @@ pub type TestBridgedMmrHashing = BridgedMmrHashing; pub type TestBridgedMmrHash = BridgedMmrHash; pub type TestBridgedBeefyMmrLeafExtra = BridgedBeefyMmrLeafExtra; pub type TestBridgedMmrProof = BridgedMmrProof; -pub type TestBridgedRawMmrLeaf = sp_beefy::mmr::MmrLeaf< +pub type TestBridgedRawMmrLeaf = sp_consensus_beefy::mmr::MmrLeaf< TestBridgedBlockNumber, TestBridgedBlockHash, TestBridgedMmrHash, @@ -72,7 +72,7 @@ construct_runtime! { } parameter_types! { - pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); + pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 0); pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); } diff --git a/modules/beefy/src/mock_chain.rs b/modules/beefy/src/mock_chain.rs index ca7e84b121a..c736aed0742 100644 --- a/modules/beefy/src/mock_chain.rs +++ b/modules/beefy/src/mock_chain.rs @@ -30,7 +30,7 @@ use bp_beefy::{BeefyPayload, Commitment, ValidatorSetId, MMR_ROOT_PAYLOAD_ID}; use codec::Encode; use pallet_mmr::NodeIndex; use rand::Rng; -use sp_beefy::mmr::{BeefyNextAuthoritySet, MmrLeafVersion}; +use sp_consensus_beefy::mmr::{BeefyNextAuthoritySet, MmrLeafVersion}; use sp_core::Pair; use sp_runtime::traits::{Hash, Header as HeaderT}; use std::collections::HashMap; @@ -188,7 +188,7 @@ impl HeaderBuilder { next_validator_keys.iter().map(|pair| pair.public()).collect::>(); let next_validators_mmr_root = get_authorities_mmr_root::(next_validators.iter()); - let leaf = sp_beefy::mmr::MmrLeaf { + let leaf = sp_consensus_beefy::mmr::MmrLeaf { version: MmrLeafVersion::new(1, 0), parent_number_and_hash: (header.number().saturating_sub(1), *header.parent_hash()), beefy_next_authority_set: BeefyNextAuthoritySet { diff --git a/modules/beefy/src/utils.rs b/modules/beefy/src/utils.rs index fac9a2c92e6..45e14ecbfe0 100644 --- a/modules/beefy/src/utils.rs +++ b/modules/beefy/src/utils.rs @@ -163,7 +163,7 @@ mod tests { use crate::{mock::*, mock_chain::*, *}; use bp_beefy::{BeefyPayload, MMR_ROOT_PAYLOAD_ID}; use frame_support::{assert_noop, assert_ok}; - use sp_beefy::ValidatorSet; + use sp_consensus_beefy::ValidatorSet; #[test] fn submit_commitment_checks_metadata() { diff --git a/modules/grandpa/Cargo.toml b/modules/grandpa/Cargo.toml index 9e260a33537..095c1d9f4e4 100644 --- a/modules/grandpa/Cargo.toml +++ b/modules/grandpa/Cargo.toml @@ -22,7 +22,7 @@ bp-header-chain = { path = "../../primitives/header-chain", default-features = f frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -48,7 +48,7 @@ std = [ "frame-benchmarking/std", "log/std", "scale-info/std", - "sp-finality-grandpa/std", + "sp-consensus-grandpa/std", "sp-runtime/std", "sp-std/std", "sp-trie/std", diff --git a/modules/grandpa/src/benchmarking.rs b/modules/grandpa/src/benchmarking.rs index 337943bee4e..5b9faea2a99 100644 --- a/modules/grandpa/src/benchmarking.rs +++ b/modules/grandpa/src/benchmarking.rs @@ -41,6 +41,7 @@ use crate::*; +use bp_header_chain::justification::required_justification_precommits; use bp_runtime::BasicOperatingMode; use bp_test_utils::{ accounts, make_justification_for_header, JustificationGeneratorParams, TEST_GRANDPA_ROUND, @@ -48,7 +49,7 @@ use bp_test_utils::{ }; use frame_benchmarking::{benchmarks_instance_pallet, whitelisted_caller}; use frame_system::RawOrigin; -use sp_finality_grandpa::AuthorityId; +use sp_consensus_grandpa::AuthorityId; use sp_runtime::traits::{One, Zero}; use sp_std::vec::Vec; @@ -66,13 +67,14 @@ const MAX_VOTE_ANCESTRIES_RANGE_END: u32 = MAX_VOTE_ANCESTRIES_RANGE_BEGIN + MAX_VOTE_ANCESTRIES_RANGE_BEGIN; // the same with validators - if there are too much validators, let's run benchmarks on subrange -fn validator_set_range_end, I: 'static>() -> u32 { +fn precommits_range_end, I: 'static>() -> u32 { let max_bridged_authorities = T::BridgedChain::MAX_AUTHORITIES_COUNT; if max_bridged_authorities > 128 { sp_std::cmp::max(128, max_bridged_authorities / 5) } else { max_bridged_authorities - } + }; + required_justification_precommits(max_bridged_authorities) } /// Prepare header and its justification to submit using `submit_finality_proof`. @@ -80,7 +82,10 @@ fn prepare_benchmark_data, I: 'static>( precommits: u32, ancestors: u32, ) -> (BridgedHeader, GrandpaJustification>) { - let authority_list = accounts(precommits as u16) + // going from precommits to total authorities count + let total_authorities_count = (3 * precommits - 1) / 2; + + let authority_list = accounts(total_authorities_count as u16) .iter() .map(|id| (AuthorityId::from(*id), 1)) .collect::>(); @@ -114,7 +119,7 @@ benchmarks_instance_pallet! { // This is the "gold standard" benchmark for this extrinsic, and it's what should be used to // annotate the weight in the pallet. submit_finality_proof { - let p in 1 .. validator_set_range_end::(); + let p in 1 .. precommits_range_end::(); let v in MAX_VOTE_ANCESTRIES_RANGE_BEGIN..MAX_VOTE_ANCESTRIES_RANGE_END; let caller: T::AccountId = whitelisted_caller(); let (header, justification) = prepare_benchmark_data::(p, v); diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index 4304e82eeb1..2b9e49d4c95 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -45,7 +45,7 @@ use bp_header_chain::{ use bp_runtime::{BlockNumberOf, HashOf, HasherOf, HeaderId, HeaderOf, OwnedBridgeModule}; use finality_grandpa::voter_set::VoterSet; use frame_support::{dispatch::PostDispatchInfo, ensure}; -use sp_finality_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID}; +use sp_consensus_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID}; use sp_runtime::{ traits::{Header as HeaderT, Zero}, SaturatedConversion, @@ -403,7 +403,7 @@ pub mod pallet { /// Returned value will indicate if a change was enacted or not. pub(crate) fn try_enact_authority_change, I: 'static>( header: &BridgedHeader, - current_set_id: sp_finality_grandpa::SetId, + current_set_id: sp_consensus_grandpa::SetId, ) -> Result { let mut change_enacted = false; @@ -584,7 +584,7 @@ impl, I: 'static> HeaderChain> for GrandpaChainH pub(crate) fn find_scheduled_change( header: &H, -) -> Option> { +) -> Option> { use sp_runtime::generic::OpaqueDigestItemId; let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); @@ -603,7 +603,7 @@ pub(crate) fn find_scheduled_change( /// extracts it. pub(crate) fn find_forced_change( header: &H, -) -> Option<(H::Number, sp_finality_grandpa::ScheduledChange)> { +) -> Option<(H::Number, sp_consensus_grandpa::ScheduledChange)> { use sp_runtime::generic::OpaqueDigestItemId; let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); @@ -695,7 +695,7 @@ mod tests { fn change_log(delay: u64) -> Digest { let consensus_log = - ConsensusLog::::ScheduledChange(sp_finality_grandpa::ScheduledChange { + ConsensusLog::::ScheduledChange(sp_consensus_grandpa::ScheduledChange { next_authorities: vec![(ALICE.into(), 1), (BOB.into(), 1)], delay, }); @@ -706,7 +706,7 @@ mod tests { fn forced_change_log(delay: u64) -> Digest { let consensus_log = ConsensusLog::::ForcedChange( delay, - sp_finality_grandpa::ScheduledChange { + sp_consensus_grandpa::ScheduledChange { next_authorities: vec![(ALICE.into(), 1), (BOB.into(), 1)], delay, }, @@ -717,7 +717,7 @@ mod tests { fn many_authorities_log() -> Digest { let consensus_log = - ConsensusLog::::ScheduledChange(sp_finality_grandpa::ScheduledChange { + ConsensusLog::::ScheduledChange(sp_consensus_grandpa::ScheduledChange { next_authorities: std::iter::repeat((ALICE.into(), 1)) .take(MAX_BRIDGED_AUTHORITIES as usize + 1) .collect(), diff --git a/modules/grandpa/src/mock.rs b/modules/grandpa/src/mock.rs index f0ae2583529..acedfc3582c 100644 --- a/modules/grandpa/src/mock.rs +++ b/modules/grandpa/src/mock.rs @@ -54,7 +54,7 @@ construct_runtime! { } parameter_types! { - pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); + pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 0); pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); } diff --git a/modules/grandpa/src/storage_types.rs b/modules/grandpa/src/storage_types.rs index d674f1a7b0a..70448286335 100644 --- a/modules/grandpa/src/storage_types.rs +++ b/modules/grandpa/src/storage_types.rs @@ -22,7 +22,7 @@ use bp_header_chain::{AuthoritySet, ChainWithGrandpa}; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{traits::Get, BoundedVec, RuntimeDebugNoBound}; use scale_info::TypeInfo; -use sp_finality_grandpa::{AuthorityId, AuthorityList, AuthorityWeight, SetId}; +use sp_consensus_grandpa::{AuthorityId, AuthorityList, AuthorityWeight, SetId}; use sp_std::marker::PhantomData; /// A bounded list of Grandpa authorities with associated weights. diff --git a/modules/grandpa/src/weights.rs b/modules/grandpa/src/weights.rs index 44b4ebd37fb..089aee8b569 100644 --- a/modules/grandpa/src/weights.rs +++ b/modules/grandpa/src/weights.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for pallet_bridge_grandpa //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -93,19 +93,19 @@ impl WeightInfo for BridgeWeight { /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), /// added: 2048, mode: MaxEncodedLen) /// - /// The range of component `p` is `[1, 5]`. + /// The range of component `p` is `[1, 4]`. /// /// The range of component `v` is `[50, 100]`. fn submit_finality_proof(p: u32, v: u32) -> Weight { // Proof Size summary in bytes: - // Measured: `416 + p * (40 ±0)` + // Measured: `394 + p * (60 ±0)` // Estimated: `4745` - // Minimum execution time: 221_703 nanoseconds. - Weight::from_parts(39_358_497, 4745) - // Standard Error: 85_573 - .saturating_add(Weight::from_ref_time(40_593_280).saturating_mul(p.into())) - // Standard Error: 7_808 - .saturating_add(Weight::from_ref_time(1_529_400).saturating_mul(v.into())) + // Minimum execution time: 228_072 nanoseconds. + Weight::from_parts(57_853_228, 4745) + // Standard Error: 149_421 + .saturating_add(Weight::from_parts(36_708_702, 0).saturating_mul(p.into())) + // Standard Error: 10_625 + .saturating_add(Weight::from_parts(1_469_032, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -148,19 +148,19 @@ impl WeightInfo for () { /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), /// added: 2048, mode: MaxEncodedLen) /// - /// The range of component `p` is `[1, 5]`. + /// The range of component `p` is `[1, 4]`. /// /// The range of component `v` is `[50, 100]`. fn submit_finality_proof(p: u32, v: u32) -> Weight { // Proof Size summary in bytes: - // Measured: `416 + p * (40 ±0)` + // Measured: `394 + p * (60 ±0)` // Estimated: `4745` - // Minimum execution time: 221_703 nanoseconds. - Weight::from_parts(39_358_497, 4745) - // Standard Error: 85_573 - .saturating_add(Weight::from_ref_time(40_593_280).saturating_mul(p.into())) - // Standard Error: 7_808 - .saturating_add(Weight::from_ref_time(1_529_400).saturating_mul(v.into())) + // Minimum execution time: 228_072 nanoseconds. + Weight::from_parts(57_853_228, 4745) + // Standard Error: 149_421 + .saturating_add(Weight::from_parts(36_708_702, 0).saturating_mul(p.into())) + // Standard Error: 10_625 + .saturating_add(Weight::from_parts(1_469_032, 0).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } diff --git a/modules/messages/src/benchmarking.rs b/modules/messages/src/benchmarking.rs index a89cb3a8ef0..955dd3da2c5 100644 --- a/modules/messages/src/benchmarking.rs +++ b/modules/messages/src/benchmarking.rs @@ -238,7 +238,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: StorageProofSize::HasExtraNodes(1024), + size: StorageProofSize::HasLargeLeaf(1024), }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) verify { @@ -272,7 +272,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: StorageProofSize::HasExtraNodes(16 * 1024), + size: StorageProofSize::HasLargeLeaf(16 * 1024), }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) verify { diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index 68af3024445..a9a25fb9622 100644 --- a/modules/messages/src/lib.rs +++ b/modules/messages/src/lib.rs @@ -178,7 +178,6 @@ pub mod pallet { >>::MessagesDeliveryProof; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData<(T, I)>); impl, I: 'static> OwnedBridgeModule for Pallet { diff --git a/modules/messages/src/mock.rs b/modules/messages/src/mock.rs index 084262c7905..a31ee2b9401 100644 --- a/modules/messages/src/mock.rs +++ b/modules/messages/src/mock.rs @@ -89,7 +89,7 @@ frame_support::construct_runtime! { parameter_types! { pub const BlockHashCount: u64 = 250; - pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); + pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 0); pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); } @@ -410,7 +410,7 @@ pub const fn message_payload(id: u64, declared_weight: u64) -> TestPayload { TestPayload { id, reject_by_lane_verifier: false, - declared_weight: Weight::from_ref_time(declared_weight), + declared_weight: Weight::from_parts(declared_weight, 0), dispatch_result: dispatch_result(0), extra: Vec::new(), } @@ -421,7 +421,7 @@ pub const fn dispatch_result( unspent_weight: u64, ) -> MessageDispatchResult { MessageDispatchResult { - unspent_weight: Weight::from_ref_time(unspent_weight), + unspent_weight: Weight::from_parts(unspent_weight, 0), dispatch_level_result: (), } } diff --git a/modules/messages/src/weights.rs b/modules/messages/src/weights.rs index f18285f3dfe..4b2e5b48ee2 100644 --- a/modules/messages/src/weights.rs +++ b/modules/messages/src/weights.rs @@ -14,10 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Autogenerated weights for pallet_bridge_messages +//! Autogenerated weights for RialtoMessages //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -29,7 +29,7 @@ // --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_bridge_messages +// --pallet=RialtoMessages // --extrinsic=* // --execution=wasm // --wasm-execution=Compiled @@ -48,7 +48,7 @@ use frame_support::{ }; use sp_std::marker::PhantomData; -/// Weight functions needed for pallet_bridge_messages. +/// Weight functions needed for RialtoMessages. pub trait WeightInfo { fn receive_single_message_proof() -> Weight; fn receive_two_messages_proof() -> Weight; @@ -60,7 +60,7 @@ pub trait WeightInfo { fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight; } -/// Weights for `pallet_bridge_messages` that are generated using one of the Bridge testnets. +/// Weights for `RialtoMessages` that are generated using one of the Bridge testnets. /// /// Those weights are test only and must never be used in production. pub struct BridgeWeight(PhantomData); @@ -88,8 +88,8 @@ impl WeightInfo for BridgeWeight { // Proof Size summary in bytes: // Measured: `693` // Estimated: `54703` - // Minimum execution time: 50_655 nanoseconds. - Weight::from_parts(60_502_000, 54703) + // Minimum execution time: 48_058 nanoseconds. + Weight::from_parts(50_422_000, 54703) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -116,8 +116,8 @@ impl WeightInfo for BridgeWeight { // Proof Size summary in bytes: // Measured: `693` // Estimated: `54703` - // Minimum execution time: 58_861 nanoseconds. - Weight::from_parts(60_288_000, 54703) + // Minimum execution time: 59_371 nanoseconds. + Weight::from_parts(61_726_000, 54703) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -144,8 +144,8 @@ impl WeightInfo for BridgeWeight { // Proof Size summary in bytes: // Measured: `693` // Estimated: `54703` - // Minimum execution time: 53_459 nanoseconds. - Weight::from_parts(54_577_000, 54703) + // Minimum execution time: 53_398 nanoseconds. + Weight::from_parts(54_351_000, 54703) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -167,8 +167,8 @@ impl WeightInfo for BridgeWeight { // Proof Size summary in bytes: // Measured: `618` // Estimated: `54200` - // Minimum execution time: 54_011 nanoseconds. - Weight::from_parts(55_573_000, 54200) + // Minimum execution time: 50_064 nanoseconds. + Weight::from_parts(51_306_000, 54200) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -190,8 +190,8 @@ impl WeightInfo for BridgeWeight { // Proof Size summary in bytes: // Measured: `618` // Estimated: `54200` - // Minimum execution time: 105_856 nanoseconds. - Weight::from_parts(109_112_000, 54200) + // Minimum execution time: 75_403 nanoseconds. + Weight::from_parts(77_006_000, 54200) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -212,14 +212,14 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, /// mode: MaxEncodedLen) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: // Measured: `579` - // Estimated: `5619` - // Minimum execution time: 40_894 nanoseconds. - Weight::from_parts(41_766_000, 5619) + // Estimated: `5624` + // Minimum execution time: 41_670 nanoseconds. + Weight::from_parts(42_863_000, 5624) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -240,14 +240,14 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, /// mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: // Measured: `596` - // Estimated: `5619` - // Minimum execution time: 39_996 nanoseconds. - Weight::from_parts(41_452_000, 5619) + // Estimated: `5624` + // Minimum execution time: 40_928 nanoseconds. + Weight::from_parts(42_165_000, 5624) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -268,14 +268,14 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, /// mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: // Measured: `596` - // Estimated: `8154` - // Minimum execution time: 42_281 nanoseconds. - Weight::from_parts(43_593_000, 8154) + // Estimated: `8164` + // Minimum execution time: 44_022 nanoseconds. + Weight::from_parts(44_657_000, 8164) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -306,8 +306,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `693` // Estimated: `54703` - // Minimum execution time: 50_655 nanoseconds. - Weight::from_parts(60_502_000, 54703) + // Minimum execution time: 48_058 nanoseconds. + Weight::from_parts(50_422_000, 54703) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -334,8 +334,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `693` // Estimated: `54703` - // Minimum execution time: 58_861 nanoseconds. - Weight::from_parts(60_288_000, 54703) + // Minimum execution time: 59_371 nanoseconds. + Weight::from_parts(61_726_000, 54703) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -362,8 +362,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `693` // Estimated: `54703` - // Minimum execution time: 53_459 nanoseconds. - Weight::from_parts(54_577_000, 54703) + // Minimum execution time: 53_398 nanoseconds. + Weight::from_parts(54_351_000, 54703) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -385,8 +385,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `618` // Estimated: `54200` - // Minimum execution time: 54_011 nanoseconds. - Weight::from_parts(55_573_000, 54200) + // Minimum execution time: 50_064 nanoseconds. + Weight::from_parts(51_306_000, 54200) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -408,8 +408,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `618` // Estimated: `54200` - // Minimum execution time: 105_856 nanoseconds. - Weight::from_parts(109_112_000, 54200) + // Minimum execution time: 75_403 nanoseconds. + Weight::from_parts(77_006_000, 54200) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -430,14 +430,14 @@ impl WeightInfo for () { /// /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, /// mode: MaxEncodedLen) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: // Measured: `579` - // Estimated: `5619` - // Minimum execution time: 40_894 nanoseconds. - Weight::from_parts(41_766_000, 5619) + // Estimated: `5624` + // Minimum execution time: 41_670 nanoseconds. + Weight::from_parts(42_863_000, 5624) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -458,14 +458,14 @@ impl WeightInfo for () { /// /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, /// mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: // Measured: `596` - // Estimated: `5619` - // Minimum execution time: 39_996 nanoseconds. - Weight::from_parts(41_452_000, 5619) + // Estimated: `5624` + // Minimum execution time: 40_928 nanoseconds. + Weight::from_parts(42_165_000, 5624) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -486,14 +486,14 @@ impl WeightInfo for () { /// /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(60), added: 2535, + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, /// mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: // Measured: `596` - // Estimated: `8154` - // Minimum execution time: 42_281 nanoseconds. - Weight::from_parts(43_593_000, 8154) + // Estimated: `8164` + // Minimum execution time: 44_022 nanoseconds. + Weight::from_parts(44_657_000, 8164) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } diff --git a/modules/parachains/src/benchmarking.rs b/modules/parachains/src/benchmarking.rs index b156b724afe..83cfba0b8c6 100644 --- a/modules/parachains/src/benchmarking.rs +++ b/modules/parachains/src/benchmarking.rs @@ -85,7 +85,7 @@ benchmarks_instance_pallet! { let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( ¶chains, DEFAULT_PARACHAIN_HEAD_SIZE, - StorageProofSize::HasExtraNodes(1024), + StorageProofSize::HasLargeLeaf(1024), ); let at_relay_block = (relay_block_number, relay_block_hash); }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) @@ -102,7 +102,7 @@ benchmarks_instance_pallet! { let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( ¶chains, DEFAULT_PARACHAIN_HEAD_SIZE, - StorageProofSize::HasExtraNodes(16 * 1024), + StorageProofSize::HasLargeLeaf(16 * 1024), ); let at_relay_block = (relay_block_number, relay_block_hash); }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) diff --git a/modules/parachains/src/lib.rs b/modules/parachains/src/lib.rs index b9bbaac7ca3..20a9ef9b95b 100644 --- a/modules/parachains/src/lib.rs +++ b/modules/parachains/src/lib.rs @@ -26,7 +26,7 @@ pub use weights::WeightInfo; pub use weights_ext::WeightInfoExt; -use bp_header_chain::HeaderChain; +use bp_header_chain::{HeaderChain, HeaderChainError}; use bp_parachains::{parachain_head_storage_key_at_source, ParaInfo, ParaStoredHeaderData}; use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain, StorageProofError}; @@ -125,8 +125,8 @@ pub mod pallet { UnknownRelayChainBlock, /// The number of stored relay block is different from what the relayer has provided. InvalidRelayChainBlockNumber, - /// Invalid storage proof has been passed. - InvalidStorageProof, + /// Error generated by a method defined in `bp-header-chain`. + HeaderChain(HeaderChainError), /// Given parachain head is unknown. UnknownParaHead, /// The storage proof doesn't contains storage root. So it is invalid for given header. @@ -277,7 +277,6 @@ pub mod pallet { >; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData<(T, I)>); impl, I: 'static> OwnedBridgeModule for Pallet { @@ -430,10 +429,10 @@ pub mod pallet { storage.ensure_no_unused_nodes() }, ) - .and_then(|r| r.map_err(bp_header_chain::HeaderChainError::StorageProof)) + .and_then(|r| r.map_err(HeaderChainError::StorageProof)) .map_err(|e| { log::trace!(target: LOG_TARGET, "Parachain heads storage proof is invalid: {:?}", e); - Error::::InvalidStorageProof + Error::::HeaderChain(e) })?; Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) @@ -1379,7 +1378,9 @@ mod tests { // try to import head#5 of parachain#1 at relay chain block #0 assert_noop!( import_parachain_1_head(0, Default::default(), parachains, proof), - Error::::InvalidStorageProof + Error::::HeaderChain(HeaderChainError::StorageProof( + StorageProofError::StorageRootMismatch + )) ); }); } diff --git a/modules/parachains/src/mock.rs b/modules/parachains/src/mock.rs index aabcdcdd364..83d347018e4 100644 --- a/modules/parachains/src/mock.rs +++ b/modules/parachains/src/mock.rs @@ -158,7 +158,7 @@ construct_runtime! { parameter_types! { pub const BlockHashCount: TestNumber = 250; - pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); + pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 0); pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); } diff --git a/modules/parachains/src/weights.rs b/modules/parachains/src/weights.rs index 5ee1e4d3621..54a835cbc87 100644 --- a/modules/parachains/src/weights.rs +++ b/modules/parachains/src/weights.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for pallet_bridge_parachains //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -77,23 +77,23 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size: - /// Some(64), added: 2044, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(1024), max_size: + /// Some(64), added: 1549, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size: - /// Some(196), added: 2176, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: + /// Some(196), added: 1681, mode: MaxEncodedLen) /// /// The range of component `p` is `[1, 2]`. fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { // Proof Size summary in bytes: // Measured: `366` - // Estimated: `5143` - // Minimum execution time: 35_160 nanoseconds. - Weight::from_parts(36_951_585, 5143) - // Standard Error: 336_932 - .saturating_add(Weight::from_ref_time(407_557).saturating_mul(p.into())) + // Estimated: `4648` + // Minimum execution time: 36_701 nanoseconds. + Weight::from_parts(38_597_828, 4648) + // Standard Error: 190_859 + .saturating_add(Weight::from_parts(60_685, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -114,19 +114,19 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size: - /// Some(64), added: 2044, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(1024), max_size: + /// Some(64), added: 1549, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size: - /// Some(196), added: 2176, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: + /// Some(196), added: 1681, mode: MaxEncodedLen) fn submit_parachain_heads_with_1kb_proof() -> Weight { // Proof Size summary in bytes: // Measured: `366` - // Estimated: `5143` - // Minimum execution time: 42_276 nanoseconds. - Weight::from_parts(43_525_000, 5143) + // Estimated: `4648` + // Minimum execution time: 38_189 nanoseconds. + Weight::from_parts(39_252_000, 4648) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -147,19 +147,19 @@ impl WeightInfo for BridgeWeight { /// /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size: - /// Some(64), added: 2044, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(1024), max_size: + /// Some(64), added: 1549, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size: - /// Some(196), added: 2176, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: + /// Some(196), added: 1681, mode: MaxEncodedLen) fn submit_parachain_heads_with_16kb_proof() -> Weight { // Proof Size summary in bytes: // Measured: `366` - // Estimated: `5143` - // Minimum execution time: 85_824 nanoseconds. - Weight::from_parts(87_335_000, 5143) + // Estimated: `4648` + // Minimum execution time: 62_868 nanoseconds. + Weight::from_parts(63_581_000, 4648) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -184,23 +184,23 @@ impl WeightInfo for () { /// /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size: - /// Some(64), added: 2044, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(1024), max_size: + /// Some(64), added: 1549, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size: - /// Some(196), added: 2176, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: + /// Some(196), added: 1681, mode: MaxEncodedLen) /// /// The range of component `p` is `[1, 2]`. fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { // Proof Size summary in bytes: // Measured: `366` - // Estimated: `5143` - // Minimum execution time: 35_160 nanoseconds. - Weight::from_parts(36_951_585, 5143) - // Standard Error: 336_932 - .saturating_add(Weight::from_ref_time(407_557).saturating_mul(p.into())) + // Estimated: `4648` + // Minimum execution time: 36_701 nanoseconds. + Weight::from_parts(38_597_828, 4648) + // Standard Error: 190_859 + .saturating_add(Weight::from_parts(60_685, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -221,19 +221,19 @@ impl WeightInfo for () { /// /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size: - /// Some(64), added: 2044, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(1024), max_size: + /// Some(64), added: 1549, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size: - /// Some(196), added: 2176, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: + /// Some(196), added: 1681, mode: MaxEncodedLen) fn submit_parachain_heads_with_1kb_proof() -> Weight { // Proof Size summary in bytes: // Measured: `366` - // Estimated: `5143` - // Minimum execution time: 42_276 nanoseconds. - Weight::from_parts(43_525_000, 5143) + // Estimated: `4648` + // Minimum execution time: 38_189 nanoseconds. + Weight::from_parts(39_252_000, 4648) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -254,19 +254,19 @@ impl WeightInfo for () { /// /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(14400), max_size: - /// Some(64), added: 2044, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(1024), max_size: + /// Some(64), added: 1549, mode: MaxEncodedLen) /// /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) /// - /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(14400), max_size: - /// Some(196), added: 2176, mode: MaxEncodedLen) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: + /// Some(196), added: 1681, mode: MaxEncodedLen) fn submit_parachain_heads_with_16kb_proof() -> Weight { // Proof Size summary in bytes: // Measured: `366` - // Estimated: `5143` - // Minimum execution time: 85_824 nanoseconds. - Weight::from_parts(87_335_000, 5143) + // Estimated: `4648` + // Minimum execution time: 62_868 nanoseconds. + Weight::from_parts(63_581_000, 4648) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } diff --git a/modules/relayers/src/lib.rs b/modules/relayers/src/lib.rs index 3d32e3c7a14..bd33b811b30 100644 --- a/modules/relayers/src/lib.rs +++ b/modules/relayers/src/lib.rs @@ -63,7 +63,6 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData); #[pallet::call] diff --git a/modules/relayers/src/weights.rs b/modules/relayers/src/weights.rs index 572935f6302..1f111aaf136 100644 --- a/modules/relayers/src/weights.rs +++ b/modules/relayers/src/weights.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for pallet_bridge_relayers //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `serban-ROG-Zephyrus`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -71,8 +71,8 @@ impl WeightInfo for BridgeWeight { // Proof Size summary in bytes: // Measured: `275` // Estimated: `5111` - // Minimum execution time: 43_031 nanoseconds. - Weight::from_parts(44_527_000, 5111) + // Minimum execution time: 48_639 nanoseconds. + Weight::from_parts(49_600_000, 5111) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -93,8 +93,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `275` // Estimated: `5111` - // Minimum execution time: 43_031 nanoseconds. - Weight::from_parts(44_527_000, 5111) + // Minimum execution time: 48_639 nanoseconds. + Weight::from_parts(49_600_000, 5111) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } diff --git a/modules/shift-session-manager/src/lib.rs b/modules/shift-session-manager/src/lib.rs index dce6005ab54..1c38a52b0c2 100644 --- a/modules/shift-session-manager/src/lib.rs +++ b/modules/shift-session-manager/src/lib.rs @@ -35,7 +35,6 @@ pub mod pallet { pub trait Config: pallet_session::Config {} #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] #[pallet::without_storage_info] pub struct Pallet(PhantomData); @@ -152,7 +151,7 @@ mod tests { } parameter_types! { - pub const MaximumBlockWeight: Weight = Weight::from_ref_time(1024); + pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 0); pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); } diff --git a/primitives/beefy/Cargo.toml b/primitives/beefy/Cargo.toml index 58cc268e698..4b4044252ca 100644 --- a/primitives/beefy/Cargo.toml +++ b/primitives/beefy/Cargo.toml @@ -18,7 +18,7 @@ bp-runtime = { path = "../runtime", default-features = false } # Substrate Dependencies binary-merkle-tree = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -35,7 +35,7 @@ std = [ "pallet-mmr/std", "scale-info/std", "serde", - "sp-beefy/std", + "sp-consensus-beefy/std", "sp-runtime/std", "sp-std/std" ] diff --git a/primitives/beefy/src/lib.rs b/primitives/beefy/src/lib.rs index 042d1d0e661..de260d45eca 100644 --- a/primitives/beefy/src/lib.rs +++ b/primitives/beefy/src/lib.rs @@ -25,7 +25,7 @@ pub use pallet_mmr::{ primitives::{DataOrHash as MmrDataOrHash, Proof as MmrProof}, verify_leaves_proof as verify_mmr_leaves_proof, }; -pub use sp_beefy::{ +pub use sp_consensus_beefy::{ crypto::{AuthorityId as EcdsaValidatorId, AuthoritySignature as EcdsaValidatorSignature}, known_payloads::MMR_ROOT_ID as MMR_ROOT_PAYLOAD_ID, mmr::{BeefyAuthoritySet, MmrLeafVersion}, @@ -51,7 +51,7 @@ use sp_std::prelude::*; pub trait ChainWithBeefy: Chain { /// The hashing algorithm used to compute the digest of the BEEFY commitment. /// - /// Corresponds to the hashing algorithm, used by `beefy_gadget::BeefyKeystore`. + /// Corresponds to the hashing algorithm, used by `sc_consensus_beefy::BeefyKeystore`. type CommitmentHasher: sp_runtime::traits::Hash; /// The hashing algorithm used to build the MMR. @@ -95,7 +95,7 @@ pub type BeefyAuthorityIdOf = ::AuthorityId; /// BEEFY validator set, containing both validator identifiers and the numeric set id. pub type BeefyAuthoritySetOf = ValidatorSet>; /// BEEFY authority set, containing both validator identifiers and the numeric set id. -pub type BeefyAuthoritySetInfoOf = sp_beefy::mmr::BeefyAuthoritySet>; +pub type BeefyAuthoritySetInfoOf = sp_consensus_beefy::mmr::BeefyAuthoritySet>; /// BEEFY validator signature used by given Substrate chain. pub type BeefyValidatorSignatureOf = <::AuthorityId as RuntimeAppPublic>::Signature; @@ -116,8 +116,12 @@ pub type BeefyMmrLeafExtraOf = ::BeefyMmrLeafExtra; /// the given Substrate chain. pub type BeefyAuthorityIdToMerkleLeafOf = ::AuthorityIdToMerkleLeaf; /// Actual type of leafs in the BEEFY MMR. -pub type BeefyMmrLeafOf = - sp_beefy::mmr::MmrLeaf, HashOf, MmrHashOf, BeefyMmrLeafExtraOf>; +pub type BeefyMmrLeafOf = sp_consensus_beefy::mmr::MmrLeaf< + BlockNumberOf, + HashOf, + MmrHashOf, + BeefyMmrLeafExtraOf, +>; /// Data required for initializing the BEEFY pallet. /// diff --git a/primitives/chain-bridge-hub-cumulus/src/lib.rs b/primitives/chain-bridge-hub-cumulus/src/lib.rs index 3a05cb48b3b..cbdd36c2e4a 100644 --- a/primitives/chain-bridge-hub-cumulus/src/lib.rs +++ b/primitives/chain-bridge-hub-cumulus/src/lib.rs @@ -45,7 +45,7 @@ pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); /// time. /// /// This is a copy-paste from the cumulus repo's `parachains-common` crate. -const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_ref_time(constants::WEIGHT_REF_TIME_PER_SECOND) +const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts(constants::WEIGHT_REF_TIME_PER_SECOND, 0) .saturating_div(2) .set_proof_size(polkadot_primitives::v2::MAX_POV_SIZE as u64); @@ -62,10 +62,10 @@ parameter_types! { ); /// Importing a block with 0 Extrinsics. - pub const BlockExecutionWeight: Weight = Weight::from_ref_time(constants::WEIGHT_REF_TIME_PER_NANOS) + pub const BlockExecutionWeight: Weight = Weight::from_parts(constants::WEIGHT_REF_TIME_PER_NANOS, 0) .saturating_mul(5_000_000); /// Executing a NO-OP `System::remarks` Extrinsic. - pub const ExtrinsicBaseWeight: Weight = Weight::from_ref_time(constants::WEIGHT_REF_TIME_PER_NANOS) + pub const ExtrinsicBaseWeight: Weight = Weight::from_parts(constants::WEIGHT_REF_TIME_PER_NANOS, 0) .saturating_mul(125_000); pub BlockWeights: limits::BlockWeights = limits::BlockWeights::builder() diff --git a/primitives/chain-westend/src/lib.rs b/primitives/chain-westend/src/lib.rs index b6d41ece286..74e8895aff9 100644 --- a/primitives/chain-westend/src/lib.rs +++ b/primitives/chain-westend/src/lib.rs @@ -101,7 +101,7 @@ pub const WITH_WESTEND_BRIDGE_PARAS_PALLET_NAME: &str = "BridgeWestendParachains pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; /// Identifier of Westmint parachain at the Westend relay chain. -pub const WESTMINT_PARACHAIN_ID: u32 = 2000; +pub const WESTMINT_PARACHAIN_ID: u32 = 1000; decl_bridge_finality_runtime_apis!(westend); diff --git a/primitives/header-chain/Cargo.toml b/primitives/header-chain/Cargo.toml index be87cce460e..0335836e328 100644 --- a/primitives/header-chain/Cargo.toml +++ b/primitives/header-chain/Cargo.toml @@ -20,7 +20,7 @@ bp-runtime = { path = "../runtime", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -39,7 +39,7 @@ std = [ "frame-support/std", "scale-info/std", "sp-core/std", - "sp-finality-grandpa/std", + "sp-consensus-grandpa/std", "sp-runtime/std", "sp-std/std", ] diff --git a/primitives/header-chain/src/justification.rs b/primitives/header-chain/src/justification.rs index 88dac0acf9f..06ed782763d 100644 --- a/primitives/header-chain/src/justification.rs +++ b/primitives/header-chain/src/justification.rs @@ -17,7 +17,7 @@ //! Pallet for checking GRANDPA Finality Proofs. //! //! Adapted copy of substrate/client/finality-grandpa/src/justification.rs. If origin -//! will ever be moved to the sp_finality_grandpa, we should reuse that implementation. +//! will ever be moved to the sp_consensus_grandpa, we should reuse that implementation. use crate::ChainWithGrandpa; @@ -26,7 +26,7 @@ use codec::{Decode, Encode, MaxEncodedLen}; use finality_grandpa::voter_set::VoterSet; use frame_support::RuntimeDebug; use scale_info::TypeInfo; -use sp_finality_grandpa::{AuthorityId, AuthoritySignature, SetId}; +use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, SetId}; use sp_runtime::{traits::Header as HeaderT, SaturatedConversion}; use sp_std::{ collections::{btree_map::BTreeMap, btree_set::BTreeSet}, @@ -307,7 +307,7 @@ where ); // verify authority signature - if !sp_finality_grandpa::check_message_signature_with_buffer( + if !sp_consensus_grandpa::check_message_signature_with_buffer( &finality_grandpa::Message::Precommit(signed.precommit.clone()), &signed.id, &signed.signature, diff --git a/primitives/header-chain/src/lib.rs b/primitives/header-chain/src/lib.rs index cafb8e7a3c8..5e2bbad242e 100644 --- a/primitives/header-chain/src/lib.rs +++ b/primitives/header-chain/src/lib.rs @@ -25,10 +25,11 @@ use bp_runtime::{ }; use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}; use core::{clone::Clone, cmp::Eq, default::Default, fmt::Debug}; +use frame_support::PalletError; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; -use sp_finality_grandpa::{AuthorityList, ConsensusLog, SetId, GRANDPA_ENGINE_ID}; +use sp_consensus_grandpa::{AuthorityList, ConsensusLog, SetId, GRANDPA_ENGINE_ID}; use sp_runtime::{traits::Header as HeaderT, Digest, RuntimeDebug}; use sp_std::boxed::Box; @@ -36,7 +37,7 @@ pub mod justification; pub mod storage_keys; /// Header chain error. -#[derive(Clone, Eq, PartialEq, RuntimeDebug)] +#[derive(Clone, Decode, Encode, Eq, PartialEq, PalletError, Debug, TypeInfo)] pub enum HeaderChainError { /// Header with given hash is missing from the chain. UnknownHeader, @@ -44,15 +45,6 @@ pub enum HeaderChainError { StorageProof(StorageProofError), } -impl From for &'static str { - fn from(err: HeaderChainError) -> &'static str { - match err { - HeaderChainError::UnknownHeader => "UnknownHeader", - HeaderChainError::StorageProof(e) => e.into(), - } - } -} - /// Header data that we're storing on-chain. /// /// Even though we may store full header, our applications (XCM) only use couple of header @@ -153,7 +145,7 @@ pub struct GrandpaConsensusLogReader(sp_std::marker::PhantomData impl GrandpaConsensusLogReader { pub fn find_authorities_change( digest: &Digest, - ) -> Option> { + ) -> Option> { // find the first consensus digest with the right ID which converts to // the right kind of consensus log. digest diff --git a/primitives/header-chain/tests/implementation_match.rs b/primitives/header-chain/tests/implementation_match.rs index 5d0fa35c376..c70683b173d 100644 --- a/primitives/header-chain/tests/implementation_match.rs +++ b/primitives/header-chain/tests/implementation_match.rs @@ -27,7 +27,7 @@ use bp_test_utils::{ JustificationGeneratorParams, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, TEST_GRANDPA_SET_ID, }; use finality_grandpa::voter_set::VoterSet; -use sp_finality_grandpa::{AuthorityId, AuthorityWeight}; +use sp_consensus_grandpa::{AuthorityId, AuthorityWeight}; use sp_runtime::traits::Header as HeaderT; type TestHeader = sp_runtime::testing::Header; diff --git a/primitives/header-chain/tests/justification.rs b/primitives/header-chain/tests/justification.rs index 52fa33ae974..3cd63b935d0 100644 --- a/primitives/header-chain/tests/justification.rs +++ b/primitives/header-chain/tests/justification.rs @@ -76,7 +76,7 @@ fn valid_justification_accepted_with_single_fork() { #[test] fn valid_justification_accepted_with_arbitrary_number_of_authorities() { use finality_grandpa::voter_set::VoterSet; - use sp_finality_grandpa::AuthorityId; + use sp_consensus_grandpa::AuthorityId; let n = 15; let required_signatures = required_justification_precommits(n as _); diff --git a/primitives/messages/src/source_chain.rs b/primitives/messages/src/source_chain.rs index d5d7549099c..881bd92f5f9 100644 --- a/primitives/messages/src/source_chain.rs +++ b/primitives/messages/src/source_chain.rs @@ -41,7 +41,7 @@ pub type RelayersRewards = BTreeMap; /// type used by the source chain. pub trait TargetHeaderChain { /// Error type. - type Error: Debug + Into<&'static str>; + type Error: Debug; /// Proof that messages have been received by target chain. type MessagesDeliveryProof: Parameter + Size; diff --git a/primitives/messages/src/target_chain.rs b/primitives/messages/src/target_chain.rs index 6604d425cfc..8496b90214c 100644 --- a/primitives/messages/src/target_chain.rs +++ b/primitives/messages/src/target_chain.rs @@ -59,7 +59,7 @@ pub struct DispatchMessage { /// that's stuck) and/or processing messages without paying fees. pub trait SourceHeaderChain { /// Error type. - type Error: Debug + Into<&'static str>; + type Error: Debug; /// Proof that messages are sent from source chain. This may also include proof /// of corresponding outbound lane states. diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 2d29b5aff0a..ece925d7952 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -531,6 +531,37 @@ macro_rules! generate_static_str_provider { }; } +#[derive(Encode, Decode, Clone, Eq, PartialEq, PalletError, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct StrippableError { + _phantom_data: sp_std::marker::PhantomData, + #[codec(skip)] + #[cfg(feature = "std")] + message: String, +} + +impl From for StrippableError { + fn from(err: T) -> Self { + Self { + _phantom_data: Default::default(), + #[cfg(feature = "std")] + message: format!("{:?}", err), + } + } +} + +impl Debug for StrippableError { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + f.write_str(&self.message) + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + f.write_str("Stripped error") + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/primitives/runtime/src/storage_proof.rs b/primitives/runtime/src/storage_proof.rs index 133a51ce6e7..09641376666 100644 --- a/primitives/runtime/src/storage_proof.rs +++ b/primitives/runtime/src/storage_proof.rs @@ -16,9 +16,11 @@ //! Logic for checking Substrate storage proofs. +use crate::StrippableError; use codec::{Decode, Encode}; +use frame_support::PalletError; use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; -use sp_runtime::RuntimeDebug; +use scale_info::TypeInfo; use sp_std::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec}; use sp_trie::{ read_trie_value, LayoutV1, MemoryDB, Recorder, StorageProof, Trie, TrieConfiguration, @@ -36,9 +38,6 @@ pub enum ProofSize { /// The proof is expected to be minimal. If value size may be changed, then it is expected to /// have given size. Minimal(u32), - /// The proof is expected to have at least given size and grow by increasing number of trie - /// nodes included in the proof. - HasExtraNodes(u32), /// The proof is expected to have at least given size and grow by increasing value that is /// stored in the trie. HasLargeLeaf(u32), @@ -46,7 +45,8 @@ pub enum ProofSize { /// This struct is used to read storage values from a subset of a Merklized database. The "proof" /// is a subset of the nodes in the Merkle structure of the database, so that it provides -/// authentication against a known Merkle root as well as the values in the database themselves. +/// authentication against a known Merkle root as well as the values in the +/// database themselves. pub struct StorageProofChecker where H: Hasher, @@ -118,14 +118,32 @@ where /// read, but decoding fails, this function returns an error. pub fn read_and_decode_value(&mut self, key: &[u8]) -> Result, Error> { self.read_value(key).and_then(|v| { - v.map(|v| T::decode(&mut &v[..]).map_err(Error::StorageValueDecodeFailed)) + v.map(|v| T::decode(&mut &v[..]).map_err(|e| Error::StorageValueDecodeFailed(e.into()))) .transpose() }) } + + /// Reads and decodes a value from the available subset of storage. If the value cannot be read + /// due to an incomplete or otherwise invalid proof, or if the value is `None`, this function + /// returns an error. If value is read, but decoding fails, this function returns an error. + pub fn read_and_decode_mandatory_value(&mut self, key: &[u8]) -> Result { + self.read_and_decode_value(key)?.ok_or(Error::StorageValueEmpty) + } + + /// Reads and decodes a value from the available subset of storage. If the value cannot be read + /// due to an incomplete or otherwise invalid proof, this function returns `Ok(None)`. + /// If value is read, but decoding fails, this function returns an error. + pub fn read_and_decode_opt_value(&mut self, key: &[u8]) -> Result, Error> { + match self.read_and_decode_value(key) { + Ok(outbound_lane_data) => Ok(outbound_lane_data), + Err(Error::StorageValueUnavailable) => Ok(None), + Err(e) => Err(e), + } + } } /// Storage proof related errors. -#[derive(Clone, Eq, PartialEq, RuntimeDebug)] +#[derive(Encode, Decode, Clone, Eq, PartialEq, PalletError, Debug, TypeInfo)] pub enum Error { /// Duplicate trie nodes are found in the proof. DuplicateNodesInProof, @@ -135,21 +153,10 @@ pub enum Error { StorageRootMismatch, /// Unable to reach expected storage value using provided trie nodes. StorageValueUnavailable, + /// The storage value is `None`. + StorageValueEmpty, /// Failed to decode storage value. - StorageValueDecodeFailed(codec::Error), -} - -impl From for &'static str { - fn from(err: Error) -> &'static str { - match err { - Error::DuplicateNodesInProof => "Storage proof contains duplicate nodes", - Error::UnusedNodesInTheProof => "Storage proof contains unused nodes", - Error::StorageRootMismatch => "Storage root is missing from the storage proof", - Error::StorageValueUnavailable => "Storage value is missing from the storage proof", - Error::StorageValueDecodeFailed(_) => - "Failed to decode storage value from the storage proof", - } - } + StorageValueDecodeFailed(StrippableError), } /// Return valid storage proof and state root. diff --git a/primitives/test-utils/Cargo.toml b/primitives/test-utils/Cargo.toml index 0a591334577..8307534093f 100644 --- a/primitives/test-utils/Cargo.toml +++ b/primitives/test-utils/Cargo.toml @@ -11,7 +11,7 @@ codec = { package = "parity-scale-codec", version = "3.1.5", default-features = ed25519-dalek = { version = "1.0", default-features = false, features = ["u64_backend"] } finality-grandpa = { version = "0.16.0", default-features = false } sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -23,7 +23,7 @@ std = [ "ed25519-dalek/std", "finality-grandpa/std", "sp-application-crypto/std", - "sp-finality-grandpa/std", + "sp-consensus-grandpa/std", "sp-runtime/std", "sp-std/std", ] diff --git a/primitives/test-utils/src/keyring.rs b/primitives/test-utils/src/keyring.rs index 8b7a2b39258..b1782109668 100644 --- a/primitives/test-utils/src/keyring.rs +++ b/primitives/test-utils/src/keyring.rs @@ -19,7 +19,7 @@ use codec::Encode; use ed25519_dalek::{Keypair, PublicKey, SecretKey, Signature}; use finality_grandpa::voter_set::VoterSet; -use sp_finality_grandpa::{AuthorityId, AuthorityList, AuthorityWeight}; +use sp_consensus_grandpa::{AuthorityId, AuthorityList, AuthorityWeight}; use sp_runtime::RuntimeDebug; use sp_std::prelude::*; diff --git a/primitives/test-utils/src/lib.rs b/primitives/test-utils/src/lib.rs index a6bb3d9b8fe..6bb4adbf450 100644 --- a/primitives/test-utils/src/lib.rs +++ b/primitives/test-utils/src/lib.rs @@ -20,7 +20,7 @@ use bp_header_chain::justification::{required_justification_precommits, GrandpaJustification}; use codec::Encode; -use sp_finality_grandpa::{AuthorityId, AuthoritySignature, AuthorityWeight, SetId}; +use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, AuthorityWeight, SetId}; use sp_runtime::traits::{Header as HeaderT, One, Zero}; use sp_std::prelude::*; @@ -170,7 +170,7 @@ pub fn signed_precommit( ) -> finality_grandpa::SignedPrecommit { let precommit = finality_grandpa::Precommit { target_hash: target.0, target_number: target.1 }; - let encoded = sp_finality_grandpa::localized_payload( + let encoded = sp_consensus_grandpa::localized_payload( round, set_id, &finality_grandpa::Message::Precommit(precommit.clone()), diff --git a/relays/bin-substrate/Cargo.toml b/relays/bin-substrate/Cargo.toml index 9db0e30c52d..86974b282d9 100644 --- a/relays/bin-substrate/Cargo.toml +++ b/relays/bin-substrate/Cargo.toml @@ -10,7 +10,7 @@ anyhow = "1.0" async-std = "1.9.0" async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.1.5" } -futures = "0.3.26" +futures = "0.3.27" hex = "0.4" log = "0.4.17" num-format = "0.4" diff --git a/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs b/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs index b3471ad1a38..37eb848fe5f 100644 --- a/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs +++ b/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs @@ -17,7 +17,6 @@ //! Rialto-to-Millau parachains sync entrypoint. use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}; -use parachains_relay::ParachainsPipeline; use relay_millau_client::Millau; use relay_rialto_client::Rialto; use relay_rialto_parachain_client::RialtoParachain; @@ -29,11 +28,6 @@ use substrate_relay_helper::parachains::{ #[derive(Clone, Debug)] pub struct RialtoParachainsToMillau; -impl ParachainsPipeline for RialtoParachainsToMillau { - type SourceChain = Rialto; - type TargetChain = Millau; -} - impl SubstrateParachainsPipeline for RialtoParachainsToMillau { type SourceParachain = RialtoParachain; type SourceRelayChain = Rialto; diff --git a/relays/bin-substrate/src/chains/rococo.rs b/relays/bin-substrate/src/chains/rococo.rs index b1e8d1bef85..166e6edbad3 100644 --- a/relays/bin-substrate/src/chains/rococo.rs +++ b/relays/bin-substrate/src/chains/rococo.rs @@ -27,5 +27,5 @@ impl CliChain for Rococo { impl CliChain for BridgeHubRococo { const RUNTIME_VERSION: Option = - Some(SimpleRuntimeVersion { spec_version: 9371, transaction_version: 1 }); + Some(SimpleRuntimeVersion { spec_version: 9372, transaction_version: 1 }); } diff --git a/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs index 098aed417c6..ba0c10beae4 100644 --- a/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs +++ b/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs @@ -18,7 +18,6 @@ use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}; use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; -use parachains_relay::ParachainsPipeline; use relay_substrate_client::{CallOf, HeaderIdOf}; use substrate_relay_helper::parachains::{ SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, @@ -28,11 +27,6 @@ use substrate_relay_helper::parachains::{ #[derive(Clone, Debug)] pub struct BridgeHubRococoToBridgeHubWococo; -impl ParachainsPipeline for BridgeHubRococoToBridgeHubWococo { - type SourceChain = relay_rococo_client::Rococo; - type TargetChain = relay_bridge_hub_wococo_client::BridgeHubWococo; -} - impl SubstrateParachainsPipeline for BridgeHubRococoToBridgeHubWococo { type SourceParachain = relay_bridge_hub_rococo_client::BridgeHubRococo; type SourceRelayChain = relay_rococo_client::Rococo; diff --git a/relays/bin-substrate/src/chains/westend_parachains_to_millau.rs b/relays/bin-substrate/src/chains/westend_parachains_to_millau.rs index f025f48dcb6..abd9e6137bb 100644 --- a/relays/bin-substrate/src/chains/westend_parachains_to_millau.rs +++ b/relays/bin-substrate/src/chains/westend_parachains_to_millau.rs @@ -17,7 +17,6 @@ //! Westend-to-Millau parachains sync entrypoint. use crate::cli::bridge::{CliBridgeBase, ParachainToRelayHeadersCliBridge}; -use parachains_relay::ParachainsPipeline; use relay_millau_client::Millau; use relay_westend_client::{Westend, Westmint}; use substrate_relay_helper::parachains::{ @@ -28,11 +27,6 @@ use substrate_relay_helper::parachains::{ #[derive(Clone, Debug)] pub struct WestendParachainsToMillau; -impl ParachainsPipeline for WestendParachainsToMillau { - type SourceChain = Westend; - type TargetChain = Millau; -} - impl SubstrateParachainsPipeline for WestendParachainsToMillau { type SourceParachain = Westmint; type SourceRelayChain = Westend; diff --git a/relays/bin-substrate/src/chains/wococo.rs b/relays/bin-substrate/src/chains/wococo.rs index cb8dff620a5..b6eceb8614e 100644 --- a/relays/bin-substrate/src/chains/wococo.rs +++ b/relays/bin-substrate/src/chains/wococo.rs @@ -27,5 +27,5 @@ impl CliChain for Wococo { impl CliChain for BridgeHubWococo { const RUNTIME_VERSION: Option = - Some(SimpleRuntimeVersion { spec_version: 9371, transaction_version: 1 }); + Some(SimpleRuntimeVersion { spec_version: 9372, transaction_version: 1 }); } diff --git a/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs index 683e7705dd6..028d8c9e17d 100644 --- a/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs +++ b/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs @@ -18,7 +18,6 @@ use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}; use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; -use parachains_relay::ParachainsPipeline; use relay_substrate_client::{CallOf, HeaderIdOf}; use substrate_relay_helper::parachains::{ SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, @@ -28,11 +27,6 @@ use substrate_relay_helper::parachains::{ #[derive(Clone, Debug)] pub struct BridgeHubWococoToBridgeHubRococo; -impl ParachainsPipeline for BridgeHubWococoToBridgeHubRococo { - type SourceChain = relay_wococo_client::Wococo; - type TargetChain = relay_bridge_hub_rococo_client::BridgeHubRococo; -} - impl SubstrateParachainsPipeline for BridgeHubWococoToBridgeHubRococo { type SourceParachain = relay_bridge_hub_wococo_client::BridgeHubWococo; type SourceRelayChain = relay_wococo_client::Wococo; diff --git a/relays/bin-substrate/src/cli/bridge.rs b/relays/bin-substrate/src/cli/bridge.rs index 330b498f19c..208f67c527b 100644 --- a/relays/bin-substrate/src/cli/bridge.rs +++ b/relays/bin-substrate/src/cli/bridge.rs @@ -16,7 +16,6 @@ use crate::cli::CliChain; use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; -use parachains_relay::ParachainsPipeline; use relay_substrate_client::{Chain, ChainWithTransactions, Parachain, RelayChain}; use strum::{EnumString, EnumVariantNames}; use substrate_relay_helper::{ @@ -87,10 +86,10 @@ where + RelayChain; /// Finality proofs synchronization pipeline (source parachain -> target). type ParachainFinality: SubstrateParachainsPipeline< - SourceRelayChain = Self::SourceRelay, - SourceParachain = Self::Source, - TargetChain = Self::Target, - > + ParachainsPipeline; + SourceRelayChain = Self::SourceRelay, + SourceParachain = Self::Source, + TargetChain = Self::Target, + >; /// Finality proofs synchronization pipeline (source relay chain -> target). type RelayFinality: SubstrateFinalitySyncPipeline< SourceChain = Self::SourceRelay, diff --git a/relays/bin-substrate/src/cli/relay_parachains.rs b/relays/bin-substrate/src/cli/relay_parachains.rs index 1234b3a3309..4e59dc14733 100644 --- a/relays/bin-substrate/src/cli/relay_parachains.rs +++ b/relays/bin-substrate/src/cli/relay_parachains.rs @@ -22,20 +22,14 @@ use crate::chains::{ }; use async_std::sync::Mutex; use async_trait::async_trait; -use bp_polkadot_core::parachains::ParaId; -use parachains_relay::parachains_loop::{ - AvailableHeader, ParachainSyncParams, SourceClient, TargetClient, -}; -use relay_substrate_client::{Parachain, ParachainBase}; +use parachains_relay::parachains_loop::{AvailableHeader, SourceClient, TargetClient}; +use relay_substrate_client::Parachain; use relay_utils::metrics::{GlobalMetrics, StandaloneMetric}; use std::sync::Arc; use structopt::StructOpt; use strum::{EnumString, EnumVariantNames, VariantNames}; use substrate_relay_helper::{ - parachains::{ - source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter, - SubstrateParachainsPipeline, - }, + parachains::{source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter}, TransactionParams, }; @@ -105,13 +99,6 @@ where parachains_relay::parachains_loop::run( source_client, target_client, - ParachainSyncParams { - parachains: vec![ - ParaId(::SourceParachain::PARACHAIN_ID) - ], - stall_timeout: std::time::Duration::from_secs(60), - strategy: parachains_relay::parachains_loop::ParachainSyncStrategy::Any, - }, metrics_params, futures::future::pending(), ) diff --git a/relays/client-bridge-hub-wococo/Cargo.toml b/relays/client-bridge-hub-wococo/Cargo.toml index 7c9b90975d6..8164ae5afed 100644 --- a/relays/client-bridge-hub-wococo/Cargo.toml +++ b/relays/client-bridge-hub-wococo/Cargo.toml @@ -29,4 +29,4 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master [dev-dependencies] bp-polkadot-core = { path = "../../primitives/polkadot-core" } -sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs index 13fb1a7e6ab..374dd5b44ce 100644 --- a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs +++ b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs @@ -75,8 +75,8 @@ impl From> for Call { mod tests { use super::*; use bp_runtime::BasicOperatingMode; + use sp_consensus_grandpa::AuthorityList; use sp_core::hexdisplay::HexDisplay; - use sp_finality_grandpa::AuthorityList; use sp_runtime::traits::Header; use std::str::FromStr; diff --git a/relays/client-rialto-parachain/Cargo.toml b/relays/client-rialto-parachain/Cargo.toml index c1a6b5d69aa..807da290d24 100644 --- a/relays/client-rialto-parachain/Cargo.toml +++ b/relays/client-rialto-parachain/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5" } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -subxt = { git = "https://github.com/paritytech/subxt", branch = "master" } +subxt = { git = "https://github.com/paritytech/subxt", branch = "master", default-features = false, features = [] } # Bridge dependencies diff --git a/relays/client-rialto-parachain/src/codegen_runtime.rs b/relays/client-rialto-parachain/src/codegen_runtime.rs index 16a539f2843..d725aafd415 100644 --- a/relays/client-rialto-parachain/src/codegen_runtime.rs +++ b/relays/client-rialto-parachain/src/codegen_runtime.rs @@ -7034,7 +7034,7 @@ pub mod api { )] pub struct StoredAuthoritySet { pub authorities: runtime_types::sp_core::bounded::bounded_vec::BoundedVec<( - runtime_types::sp_finality_grandpa::app::Public, + runtime_types::sp_consensus_grandpa::app::Public, ::core::primitive::u64, )>, pub set_id: ::core::primitive::u64, @@ -8115,7 +8115,7 @@ pub mod api { pub struct Signature(pub [::core::primitive::u8; 64usize]); } } - pub mod sp_finality_grandpa { + pub mod sp_consensus_grandpa { use super::runtime_types; pub mod app { use super::runtime_types; diff --git a/relays/client-substrate/Cargo.toml b/relays/client-substrate/Cargo.toml index 7e7eba5ec2b..1a80a54f965 100644 --- a/relays/client-substrate/Cargo.toml +++ b/relays/client-substrate/Cargo.toml @@ -9,14 +9,14 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" async-std = { version = "1.6.5", features = ["attributes"] } async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.1.5" } -futures = "0.3.26" +futures = "0.3.27" jsonrpsee = { version = "0.16", features = ["macros", "ws-client"] } log = "0.4.17" num-traits = "0.2" rand = "0.8" scale-info = { version = "2.1.1", features = ["derive"] } -tokio = { version = "1.25", features = ["rt-multi-thread"] } -thiserror = "1.0.26" +tokio = { version = "1.26", features = ["rt-multi-thread"] } +thiserror = "1.0.39" # Bridge dependencies diff --git a/relays/client-substrate/src/chain.rs b/relays/client-substrate/src/chain.rs index bc38d1ec932..6ab353921ef 100644 --- a/relays/client-substrate/src/chain.rs +++ b/relays/client-substrate/src/chain.rs @@ -72,7 +72,7 @@ pub trait RelayChain: Chain { /// Name of the bridge parachains pallet (used in `construct_runtime` macro call) that is /// deployed at the **bridged** chain. /// - /// We assume that all chains that are bridging with this `ChainWithGrandpa` are using + /// We assume that all chains that are bridging with this `RelayChain` are using /// the same name. const PARACHAINS_FINALITY_PALLET_NAME: &'static str; } diff --git a/relays/client-substrate/src/client.rs b/relays/client-substrate/src/client.rs index bddd53b39f8..6a381c4e977 100644 --- a/relays/client-substrate/src/client.rs +++ b/relays/client-substrate/src/client.rs @@ -26,7 +26,7 @@ use crate::{ Result, SignParam, TransactionTracker, UnsignedTransaction, }; -use async_std::sync::{Arc, Mutex}; +use async_std::sync::{Arc, Mutex, RwLock}; use async_trait::async_trait; use bp_runtime::{HeaderIdProvider, StorageDoubleMapKeyProvider, StorageMapKeyProvider}; use codec::{Decode, Encode}; @@ -111,23 +111,31 @@ pub enum ChainRuntimeVersion { /// Substrate client type. /// -/// Cloning `Client` is a cheap operation. +/// Cloning `Client` is a cheap operation that only clones internal references. Different +/// clones of the same client are guaranteed to use the same references. pub struct Client { - /// Tokio runtime handle. - tokio: Arc, + // Lock order: `submit_signed_extrinsic_lock`, `data` /// Client connection params. params: Arc, - /// Substrate RPC client. - client: Arc, - /// Genesis block hash. - genesis_hash: HashOf, + /// Saved chain runtime version. + chain_runtime_version: ChainRuntimeVersion, /// If several tasks are submitting their transactions simultaneously using /// `submit_signed_extrinsic` method, they may get the same transaction nonce. So one of /// transactions will be rejected from the pool. This lock is here to prevent situations like /// that. submit_signed_extrinsic_lock: Arc>, - /// Saved chain runtime version - chain_runtime_version: ChainRuntimeVersion, + /// Genesis block hash. + genesis_hash: HashOf, + /// Shared dynamic data. + data: Arc>, +} + +/// Client data, shared by all `Client` clones. +struct ClientData { + /// Tokio runtime handle. + tokio: Arc, + /// Substrate RPC client. + client: Arc, } #[async_trait] @@ -135,9 +143,10 @@ impl relay_utils::relay_loop::Client for Client { type Error = Error; async fn reconnect(&mut self) -> Result<()> { + let mut data = self.data.write().await; let (tokio, client) = Self::build_client(&self.params).await?; - self.tokio = tokio; - self.client = client; + data.tokio = tokio; + data.client = client; Ok(()) } } @@ -145,12 +154,11 @@ impl relay_utils::relay_loop::Client for Client { impl Clone for Client { fn clone(&self) -> Self { Client { - tokio: self.tokio.clone(), params: self.params.clone(), - client: self.client.clone(), - genesis_hash: self.genesis_hash, - submit_signed_extrinsic_lock: self.submit_signed_extrinsic_lock.clone(), chain_runtime_version: self.chain_runtime_version.clone(), + submit_signed_extrinsic_lock: self.submit_signed_extrinsic_lock.clone(), + genesis_hash: self.genesis_hash, + data: self.data.clone(), } } } @@ -199,12 +207,11 @@ impl Client { let chain_runtime_version = params.chain_runtime_version.clone(); Ok(Self { - tokio, params, - client, - genesis_hash, - submit_signed_extrinsic_lock: Arc::new(Mutex::new(())), chain_runtime_version, + submit_signed_extrinsic_lock: Arc::new(Mutex::new(())), + genesis_hash, + data: Arc::new(RwLock::new(ClientData { tokio, client })), }) } @@ -271,6 +278,10 @@ impl Client { Ok(SubstrateChainClient::::finalized_head(&*client).await?) }) .await + .map_err(|e| Error::FailedToReadBestFinalizedHeaderHash { + chain: C::NAME.into(), + error: e.boxed(), + }) } /// Return number of the best finalized block. @@ -292,6 +303,7 @@ impl Client { Ok(SubstrateChainClient::::header(&*client, None).await?) }) .await + .map_err(|e| Error::FailedToReadBestHeader { chain: C::NAME.into(), error: e.boxed() }) } /// Get a Substrate block from its hash. @@ -311,6 +323,11 @@ impl Client { Ok(SubstrateChainClient::::header(&*client, Some(block_hash)).await?) }) .await + .map_err(|e| Error::FailedToReadHeaderByHash { + chain: C::NAME.into(), + hash: format!("{block_hash}"), + error: e.boxed(), + }) } /// Get a Substrate block hash by its number. @@ -394,10 +411,17 @@ impl Client { storage_key: StorageKey, block_hash: Option, ) -> Result> { + let cloned_storage_key = storage_key.clone(); self.jsonrpsee_execute(move |client| async move { - Ok(SubstrateStateClient::::storage(&*client, storage_key, block_hash).await?) + Ok(SubstrateStateClient::::storage(&*client, storage_key.clone(), block_hash) + .await?) }) .await + .map_err(|e| Error::FailedToReadRuntimeStorageValue { + chain: C::NAME.into(), + key: cloned_storage_key, + error: e.boxed(), + }) } /// Return native tokens balance of the account. @@ -555,7 +579,7 @@ impl Client { Ok((tracker, subscription)) }) .await?; - self.tokio.spawn(Subscription::background_worker( + self.data.read().await.tokio.spawn(Subscription::background_worker( C::NAME.into(), "extrinsic".into(), subscription, @@ -640,7 +664,14 @@ impl Client { input: Input, at_block: Option, ) -> Result { - let encoded_output = self.state_call(method_name, Bytes(input.encode()), at_block).await?; + let encoded_output = self + .state_call(method_name.clone(), Bytes(input.encode()), at_block) + .await + .map_err(|e| Error::ErrorExecutingRuntimeCall { + chain: C::NAME.into(), + method: method_name, + error: e.boxed(), + })?; Output::decode(&mut &encoded_output.0[..]).map_err(Error::ResponseParseFailed) } @@ -695,7 +726,7 @@ impl Client { }) .await?; let (sender, receiver) = futures::channel::mpsc::channel(MAX_SUBSCRIPTION_CAPACITY); - self.tokio.spawn(Subscription::background_worker( + self.data.read().await.tokio.spawn(Subscription::background_worker( C::NAME.into(), "justification".into(), subscription, @@ -711,8 +742,9 @@ impl Client { F: Future> + Send, T: Send + 'static, { - let client = self.client.clone(); - self.tokio.spawn(async move { make_jsonrpsee_future(client).await }).await? + let data = self.data.read().await; + let client = data.client.clone(); + data.tokio.spawn(async move { make_jsonrpsee_future(client).await }).await? } /// Returns `true` if version guard can be started. diff --git a/relays/client-substrate/src/error.rs b/relays/client-substrate/src/error.rs index 3b83f917bf8..54247c5dc17 100644 --- a/relays/client-substrate/src/error.rs +++ b/relays/client-substrate/src/error.rs @@ -20,6 +20,7 @@ use bp_polkadot_core::parachains::ParaId; use jsonrpsee::core::Error as RpcError; use relay_utils::MaybeConnectionError; use sc_rpc_api::system::Health; +use sp_core::storage::StorageKey; use sp_runtime::transaction_validity::TransactionValidityError; use thiserror::Error; @@ -55,6 +56,52 @@ pub enum Error { /// The client we're connected to is not synced, so we can't rely on its state. #[error("Substrate client is not synced {0}.")] ClientNotSynced(Health), + /// Failed to read best finalized header hash from given chain. + #[error("Failed to read best finalized header hash of {chain}: {error:?}.")] + FailedToReadBestFinalizedHeaderHash { + /// Name of the chain where the error has happened. + chain: String, + /// Underlying error. + error: Box, + }, + /// Failed to read best finalized header from given chain. + #[error("Failed to read best header of {chain}: {error:?}.")] + FailedToReadBestHeader { + /// Name of the chain where the error has happened. + chain: String, + /// Underlying error. + error: Box, + }, + /// Failed to read header by hash from given chain. + #[error("Failed to read header {hash} of {chain}: {error:?}.")] + FailedToReadHeaderByHash { + /// Name of the chain where the error has happened. + chain: String, + /// Hash of the header we've tried to read. + hash: String, + /// Underlying error. + error: Box, + }, + /// Failed to execute runtime call at given chain. + #[error("Failed to execute runtime call {method} at {chain}: {error:?}.")] + ErrorExecutingRuntimeCall { + /// Name of the chain where the error has happened. + chain: String, + /// Runtime method name. + method: String, + /// Underlying error. + error: Box, + }, + /// Failed to read sotrage value at given chain. + #[error("Failed to read storage value {key:?} at {chain}: {error:?}.")] + FailedToReadRuntimeStorageValue { + /// Name of the chain where the error has happened. + chain: String, + /// Runtime storage key + key: StorageKey, + /// Underlying error. + error: Box, + }, /// The bridge pallet is halted and all transactions will be rejected. #[error("Bridge pallet is halted.")] BridgePalletIsHalted, @@ -81,16 +128,28 @@ impl From for Error { } } +impl Error { + /// Box the error. + pub fn boxed(self) -> Box { + Box::new(self) + } +} + impl MaybeConnectionError for Error { fn is_connection_error(&self) -> bool { - matches!( - *self, + match *self { Error::RpcError(RpcError::Transport(_)) // right now if connection to the ws server is dropped (after it is already established), // we're getting this error | Error::RpcError(RpcError::Internal(_)) | Error::RpcError(RpcError::RestartNeeded(_)) - | Error::ClientNotSynced(_), - ) + | Error::ClientNotSynced(_) => true, + Error::FailedToReadBestFinalizedHeaderHash { ref error, .. } => error.is_connection_error(), + Error::FailedToReadBestHeader { ref error, .. } => error.is_connection_error(), + Error::FailedToReadHeaderByHash { ref error, .. } => error.is_connection_error(), + Error::ErrorExecutingRuntimeCall { ref error, .. } => error.is_connection_error(), + Error::FailedToReadRuntimeStorageValue { ref error, .. } => error.is_connection_error(), + _ => false, + } } } diff --git a/relays/client-substrate/src/test_chain.rs b/relays/client-substrate/src/test_chain.rs index 9bc6c5ae15c..d33cb1de681 100644 --- a/relays/client-substrate/src/test_chain.rs +++ b/relays/client-substrate/src/test_chain.rs @@ -68,3 +68,52 @@ impl ChainWithBalances for TestChain { unreachable!() } } + +/// Primitives-level parachain that may be used in tests. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TestParachainBase; + +impl bp_runtime::Chain for TestParachainBase { + type BlockNumber = u32; + type Hash = sp_core::H256; + type Hasher = sp_runtime::traits::BlakeTwo256; + type Header = sp_runtime::generic::Header; + + type AccountId = u32; + type Balance = u32; + type Index = u32; + type Signature = sp_runtime::testing::TestSignature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + + fn max_extrinsic_weight() -> Weight { + unreachable!() + } +} + +impl bp_runtime::Parachain for TestParachainBase { + const PARACHAIN_ID: u32 = 1000; +} + +/// Parachain that may be used in tests. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TestParachain; + +impl bp_runtime::UnderlyingChainProvider for TestParachain { + type Chain = TestParachainBase; +} + +impl Chain for TestParachain { + const ID: ChainId = *b"test"; + const NAME: &'static str = "TestParachain"; + const TOKEN_ID: Option<&'static str> = None; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "TestParachainMethod"; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(0); + + type SignedBlock = sp_runtime::generic::SignedBlock< + sp_runtime::generic::Block, + >; + type Call = (); +} diff --git a/relays/finality/Cargo.toml b/relays/finality/Cargo.toml index c3e32eb59b2..287798431fe 100644 --- a/relays/finality/Cargo.toml +++ b/relays/finality/Cargo.toml @@ -11,7 +11,7 @@ async-std = "1.6.5" async-trait = "0.1" backoff = "0.4" bp-header-chain = { path = "../../primitives/header-chain" } -futures = "0.3.26" +futures = "0.3.27" log = "0.4.17" num-traits = "0.2" relay-utils = { path = "../utils" } diff --git a/relays/lib-substrate-relay/Cargo.toml b/relays/lib-substrate-relay/Cargo.toml index 96fe9356fa0..9c336056dc6 100644 --- a/relays/lib-substrate-relay/Cargo.toml +++ b/relays/lib-substrate-relay/Cargo.toml @@ -7,11 +7,11 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] anyhow = "1.0" -thiserror = "1.0.26" +thiserror = "1.0.39" async-std = "1.9.0" async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.1.5" } -futures = "0.3.26" +futures = "0.3.27" hex = "0.4" num-traits = "0.2" log = "0.4.17" @@ -44,7 +44,7 @@ frame-support = { git = "https://github.com/paritytech/substrate", branch = "mas frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } [dev-dependencies] diff --git a/relays/lib-substrate-relay/src/error.rs b/relays/lib-substrate-relay/src/error.rs index b41870a181d..2ebd9130f39 100644 --- a/relays/lib-substrate-relay/src/error.rs +++ b/relays/lib-substrate-relay/src/error.rs @@ -17,7 +17,7 @@ //! Relay errors. use relay_substrate_client as client; -use sp_finality_grandpa::AuthorityList; +use sp_consensus_grandpa::AuthorityList; use sp_runtime::traits::MaybeDisplay; use std::fmt::Debug; use thiserror::Error; diff --git a/relays/lib-substrate-relay/src/finality/engine.rs b/relays/lib-substrate-relay/src/finality/engine.rs index 4fc503011bb..f2ddd32db86 100644 --- a/relays/lib-substrate-relay/src/finality/engine.rs +++ b/relays/lib-substrate-relay/src/finality/engine.rs @@ -30,8 +30,8 @@ use relay_substrate_client::{ BlockNumberOf, Chain, ChainWithGrandpa, Client, Error as SubstrateError, HashOf, HeaderOf, Subscription, SubstrateFinalityClient, SubstrateGrandpaFinalityClient, }; +use sp_consensus_grandpa::{AuthorityList as GrandpaAuthoritiesSet, GRANDPA_ENGINE_ID}; use sp_core::{storage::StorageKey, Bytes}; -use sp_finality_grandpa::{AuthorityList as GrandpaAuthoritiesSet, GRANDPA_ENGINE_ID}; use sp_runtime::{traits::Header, ConsensusEngineId}; use std::marker::PhantomData; @@ -155,8 +155,8 @@ impl Engine for Grandpa { C::WITH_CHAIN_GRANDPA_PALLET_NAME, ); let (authority_set, authority_set_id): ( - sp_finality_grandpa::AuthorityList, - sp_finality_grandpa::SetId, + sp_consensus_grandpa::AuthorityList, + sp_consensus_grandpa::SetId, ) = target_client .storage_value(current_authority_set_key, None) .await? diff --git a/relays/lib-substrate-relay/src/messages_source.rs b/relays/lib-substrate-relay/src/messages_source.rs index 8d2ac5874fe..a151af8ee82 100644 --- a/relays/lib-substrate-relay/src/messages_source.rs +++ b/relays/lib-substrate-relay/src/messages_source.rs @@ -129,8 +129,22 @@ impl RelayClient for SubstrateMessagesSource

{ type Error = SubstrateError; async fn reconnect(&mut self) -> Result<(), SubstrateError> { + // since the client calls RPC methods on both sides, we need to reconnect both self.source_client.reconnect().await?; - self.target_client.reconnect().await + self.target_client.reconnect().await?; + + // call reconnect on on-demand headers relay, because we may use different chains there + // and the error that has lead to reconnect may have came from those other chains + // (see `require_target_header_on_source`) + // + // this may lead to multiple reconnects to the same node during the same call and it + // needs to be addressed in the future + // TODO: https://github.com/paritytech/parity-bridges-common/issues/1928 + if let Some(ref mut target_to_source_headers_relay) = self.target_to_source_headers_relay { + target_to_source_headers_relay.reconnect().await?; + } + + Ok(()) } } diff --git a/relays/lib-substrate-relay/src/messages_target.rs b/relays/lib-substrate-relay/src/messages_target.rs index 75119a1de97..7b0d9d63fb7 100644 --- a/relays/lib-substrate-relay/src/messages_target.rs +++ b/relays/lib-substrate-relay/src/messages_target.rs @@ -123,8 +123,22 @@ impl RelayClient for SubstrateMessagesTarget

{ type Error = SubstrateError; async fn reconnect(&mut self) -> Result<(), SubstrateError> { + // since the client calls RPC methods on both sides, we need to reconnect both self.target_client.reconnect().await?; - self.source_client.reconnect().await + self.source_client.reconnect().await?; + + // call reconnect on on-demand headers relay, because we may use different chains there + // and the error that has lead to reconnect may have came from those other chains + // (see `require_source_header_on_target`) + // + // this may lead to multiple reconnects to the same node during the same call and it + // needs to be addressed in the future + // TODO: https://github.com/paritytech/parity-bridges-common/issues/1928 + if let Some(ref mut source_to_target_headers_relay) = self.source_to_target_headers_relay { + source_to_target_headers_relay.reconnect().await?; + } + + Ok(()) } } diff --git a/relays/lib-substrate-relay/src/on_demand/headers.rs b/relays/lib-substrate-relay/src/on_demand/headers.rs index 75b402aff87..aded79a857a 100644 --- a/relays/lib-substrate-relay/src/on_demand/headers.rs +++ b/relays/lib-substrate-relay/src/on_demand/headers.rs @@ -60,6 +60,8 @@ pub struct OnDemandHeadersRelay { required_header_number: RequiredHeaderNumberRef, /// Client of the source chain. source_client: Client, + /// Client of the target chain. + target_client: Client, } impl OnDemandHeadersRelay

{ @@ -83,6 +85,7 @@ impl OnDemandHeadersRelay

{ relay_task_name: on_demand_headers_relay_name::(), required_header_number: required_header_number.clone(), source_client: source_client.clone(), + target_client: target_client.clone(), }; async_std::task::spawn(async move { background_task::

( @@ -104,6 +107,13 @@ impl OnDemandHeadersRelay

{ impl OnDemandRelay for OnDemandHeadersRelay

{ + async fn reconnect(&self) -> Result<(), SubstrateError> { + // using clone is fine here (to avoid mut requirement), because clone on Client clones + // internal references + self.source_client.clone().reconnect().await?; + self.target_client.clone().reconnect().await + } + async fn require_more_headers(&self, required_header: BlockNumberOf) { let mut required_header_number = self.required_header_number.lock().await; if required_header > *required_header_number { @@ -128,6 +138,9 @@ impl OnDemandRelay: Send + Sync { + /// Reconnect to source and target nodes. + async fn reconnect(&self) -> Result<(), SubstrateError>; + /// Ask relay to relay source header with given number to the target chain. /// /// Depending on implementation, on-demand relay may also relay `required_header` ancestors diff --git a/relays/lib-substrate-relay/src/on_demand/parachains.rs b/relays/lib-substrate-relay/src/on_demand/parachains.rs index b1270108c80..4c205770d4a 100644 --- a/relays/lib-substrate-relay/src/on_demand/parachains.rs +++ b/relays/lib-substrate-relay/src/on_demand/parachains.rs @@ -36,9 +36,7 @@ use bp_runtime::HeaderIdProvider; use futures::{select, FutureExt}; use num_traits::Zero; use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; -use parachains_relay::parachains_loop::{ - AvailableHeader, ParachainSyncParams, SourceClient, TargetClient, -}; +use parachains_relay::parachains_loop::{AvailableHeader, SourceClient, TargetClient}; use relay_substrate_client::{ is_ancient_block, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, Error as SubstrateError, HashOf, HeaderIdOf, ParachainBase, @@ -121,6 +119,15 @@ impl OnDemandRelay, { + async fn reconnect(&self) -> Result<(), SubstrateError> { + // using clone is fine here (to avoid mut requirement), because clone on Client clones + // internal references + self.source_relay_client.clone().reconnect().await?; + self.target_client.clone().reconnect().await?; + // we'll probably need to reconnect relay chain relayer clients also + self.on_demand_source_relay_to_target_headers.reconnect().await + } + async fn require_more_headers(&self, required_header: BlockNumberOf) { if let Err(e) = self.required_header_number_sender.send(required_header).await { log::trace!( @@ -183,7 +190,7 @@ where let mut proved_parachain_block = selected_parachain_block; if proved_relay_block != selected_relay_block { proved_parachain_block = parachains_source - .on_chain_para_head_id(proved_relay_block, para_id) + .on_chain_para_head_id(proved_relay_block) .await? // this could happen e.g. if parachain has been offboarded? .ok_or_else(|| { @@ -209,11 +216,11 @@ where } // and finally - prove parachain head - let (para_proof, para_hashes) = - parachains_source.prove_parachain_heads(proved_relay_block, &[para_id]).await?; + let (para_proof, para_hash) = + parachains_source.prove_parachain_head(proved_relay_block).await?; calls.push(P::SubmitParachainHeadsCallBuilder::build_submit_parachain_heads_call( proved_relay_block, - para_hashes.into_iter().map(|h| (para_id, h)).collect(), + vec![(para_id, para_hash)], para_proof, )); @@ -241,16 +248,14 @@ async fn background_task( let mut relay_state = RelayState::Idle; let mut required_parachain_header_number = Zero::zero(); - let required_para_header_number_ref = Arc::new(Mutex::new(AvailableHeader::Unavailable)); + let required_para_header_ref = Arc::new(Mutex::new(AvailableHeader::Unavailable)); let mut restart_relay = true; let parachains_relay_task = futures::future::Fuse::terminated(); futures::pin_mut!(parachains_relay_task); - let mut parachains_source = ParachainsSource::

::new( - source_relay_client.clone(), - required_para_header_number_ref.clone(), - ); + let mut parachains_source = + ParachainsSource::

::new(source_relay_client.clone(), required_para_header_ref.clone()); let mut parachains_target = ParachainsTarget::

::new(target_client.clone(), target_transaction_params.clone()); @@ -271,13 +276,20 @@ async fn background_task( }, }; - // keep in mind that we are not updating `required_para_header_number_ref` here, because + // keep in mind that we are not updating `required_para_header_ref` here, because // then we'll be submitting all previous headers as well (while required relay headers are // delivered) and we want to avoid that (to reduce cost) - required_parachain_header_number = std::cmp::max( - required_parachain_header_number, - new_required_parachain_header_number, - ); + if new_required_parachain_header_number > required_parachain_header_number { + log::trace!( + target: "bridge", + "[{}] More {} headers required. Going to sync up to the {}", + relay_task_name, + P::SourceParachain::NAME, + new_required_parachain_header_number, + ); + + required_parachain_header_number = new_required_parachain_header_number; + } }, _ = async_std::task::sleep(P::TargetChain::AVERAGE_BLOCK_INTERVAL).fuse() => {}, _ = parachains_relay_task => { @@ -351,7 +363,7 @@ async fn background_task( .await; }, RelayState::RelayingParaHeader(required_para_header) => { - *required_para_header_number_ref.lock().await = + *required_para_header_ref.lock().await = AvailableHeader::Available(required_para_header); }, } @@ -379,11 +391,6 @@ async fn background_task( parachains_relay::parachains_loop::run( parachains_source.clone(), parachains_target.clone(), - ParachainSyncParams { - parachains: vec![P::SourceParachain::PARACHAIN_ID.into()], - stall_timeout: std::time::Duration::from_secs(60), - strategy: parachains_relay::parachains_loop::ParachainSyncStrategy::Any, - }, MetricsParams::disabled(), futures::future::pending(), ) @@ -489,10 +496,7 @@ where source.client().best_finalized_header().await.map_err(map_source_err)?; let best_finalized_relay_block_id = best_finalized_relay_header.id(); let para_header_at_source = source - .on_chain_para_head_id( - best_finalized_relay_block_id, - P::SourceParachain::PARACHAIN_ID.into(), - ) + .on_chain_para_head_id(best_finalized_relay_block_id) .await .map_err(map_source_err)?; @@ -515,10 +519,7 @@ where let para_header_at_relay_header_at_target = if let Some(available_relay_header_at_target) = available_relay_header_at_target { source - .on_chain_para_head_id( - available_relay_header_at_target, - P::SourceParachain::PARACHAIN_ID.into(), - ) + .on_chain_para_head_id(available_relay_header_at_target) .await .map_err(map_source_err)? } else { @@ -669,7 +670,7 @@ impl<'a, P: SubstrateParachainsPipeline> &self, at_relay_block: HeaderIdOf, ) -> Result>, SubstrateError> { - self.1.on_chain_para_head_id(at_relay_block, self.parachain_id()).await + self.1.on_chain_para_head_id(at_relay_block).await } } diff --git a/relays/lib-substrate-relay/src/parachains/mod.rs b/relays/lib-substrate-relay/src/parachains/mod.rs index 9852e512f7e..722f9b61f9f 100644 --- a/relays/lib-substrate-relay/src/parachains/mod.rs +++ b/relays/lib-substrate-relay/src/parachains/mod.rs @@ -56,7 +56,8 @@ pub struct ParachainsPipelineAdapter { } impl ParachainsPipeline for ParachainsPipelineAdapter

{ - type SourceChain = P::SourceRelayChain; + type SourceParachain = P::SourceParachain; + type SourceRelayChain = P::SourceRelayChain; type TargetChain = P::TargetChain; } diff --git a/relays/lib-substrate-relay/src/parachains/source.rs b/relays/lib-substrate-relay/src/parachains/source.rs index 90776902dd6..146c5840cd5 100644 --- a/relays/lib-substrate-relay/src/parachains/source.rs +++ b/relays/lib-substrate-relay/src/parachains/source.rs @@ -24,10 +24,7 @@ use bp_parachains::parachain_head_storage_key_at_source; use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; use bp_runtime::HeaderIdProvider; use codec::Decode; -use parachains_relay::{ - parachains_loop::{AvailableHeader, SourceClient}, - parachains_loop_metrics::ParachainsLoopMetrics, -}; +use parachains_relay::parachains_loop::{AvailableHeader, SourceClient}; use relay_substrate_client::{ is_ancient_block, Chain, Client, Error as SubstrateError, HeaderIdOf, HeaderOf, ParachainBase, RelayChain, @@ -63,8 +60,8 @@ impl ParachainsSource

{ pub async fn on_chain_para_head_id( &self, at_block: HeaderIdOf, - para_id: ParaId, ) -> Result>, SubstrateError> { + let para_id = ParaId(P::SourceParachain::PARACHAIN_ID); let storage_key = parachain_head_storage_key_at_source(P::SourceRelayChain::PARAS_PALLET_NAME, para_id); let para_head = self.client.raw_storage_value(storage_key, Some(at_block.1)).await?; @@ -104,18 +101,7 @@ where async fn parachain_head( &self, at_block: HeaderIdOf, - metrics: Option<&ParachainsLoopMetrics>, - para_id: ParaId, - ) -> Result, Self::Error> { - // we don't need to support many parachains now - if para_id.0 != P::SourceParachain::PARACHAIN_ID { - return Err(SubstrateError::Custom(format!( - "Parachain id {} is not matching expected {}", - para_id.0, - P::SourceParachain::PARACHAIN_ID, - ))) - } - + ) -> Result>, Self::Error> { // if requested relay header is ancient, then we don't even want to try to read the // parachain head - we simply return `Unavailable` let best_block_number = self.client.best_finalized_header_number().await?; @@ -125,7 +111,7 @@ where // else - try to read head from the source client let mut para_head_id = AvailableHeader::Missing; - if let Some(on_chain_para_head_id) = self.on_chain_para_head_id(at_block, para_id).await? { + if let Some(on_chain_para_head_id) = self.on_chain_para_head_id(at_block).await? { // Never return head that is larger than requested. This way we'll never sync // headers past `max_header_id`. para_head_id = match *self.max_head_id.lock().await { @@ -141,26 +127,14 @@ where } } - if let (Some(metrics), AvailableHeader::Available(para_head_id)) = (metrics, para_head_id) { - metrics.update_best_parachain_block_at_source(para_id, para_head_id.0); - } - - Ok(para_head_id.map(|para_head_id| para_head_id.1)) + Ok(para_head_id) } - async fn prove_parachain_heads( + async fn prove_parachain_head( &self, at_block: HeaderIdOf, - parachains: &[ParaId], - ) -> Result<(ParaHeadsProof, Vec), Self::Error> { + ) -> Result<(ParaHeadsProof, ParaHash), Self::Error> { let parachain = ParaId(P::SourceParachain::PARACHAIN_ID); - if parachains != [parachain] { - return Err(SubstrateError::Custom(format!( - "Trying to prove unexpected parachains {parachains:?}. Expected {parachain:?}", - ))) - } - - let parachain = parachains[0]; let storage_key = parachain_head_storage_key_at_source(P::SourceRelayChain::PARAS_PALLET_NAME, parachain); let parachain_heads_proof = self @@ -190,6 +164,6 @@ where })?; let parachain_head_hash = parachain_head.hash(); - Ok((ParaHeadsProof(parachain_heads_proof), vec![parachain_head_hash])) + Ok((ParaHeadsProof(parachain_heads_proof), parachain_head_hash)) } } diff --git a/relays/lib-substrate-relay/src/parachains/target.rs b/relays/lib-substrate-relay/src/parachains/target.rs index 7f6cdf8f925..6df7bc0a742 100644 --- a/relays/lib-substrate-relay/src/parachains/target.rs +++ b/relays/lib-substrate-relay/src/parachains/target.rs @@ -24,18 +24,15 @@ use crate::{ }; use async_trait::async_trait; -use bp_parachains::{BestParaHeadHash, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider}; use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; use bp_runtime::HeaderIdProvider; use codec::Decode; -use parachains_relay::{ - parachains_loop::TargetClient, parachains_loop_metrics::ParachainsLoopMetrics, -}; +use parachains_relay::parachains_loop::TargetClient; use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, - HeaderIdOf, ParachainBase, RelayChain, TransactionEra, TransactionTracker, UnsignedTransaction, + AccountIdOf, AccountKeyPairOf, Chain, Client, Error as SubstrateError, HeaderIdOf, + ParachainBase, TransactionEra, TransactionTracker, UnsignedTransaction, }; -use relay_utils::{relay_loop::Client as RelayClient, HeaderId}; +use relay_utils::relay_loop::Client as RelayClient; use sp_core::{Bytes, Pair}; /// Substrate client as parachain heads source. @@ -92,93 +89,50 @@ where Ok(best_id) } - async fn best_finalized_source_block( + async fn best_finalized_source_relay_chain_block( &self, at_block: &HeaderIdOf, ) -> Result, Self::Error> { - let encoded_best_finalized_source_block = self - .client - .state_call( + self.client + .typed_state_call::<_, Option>>( P::SourceRelayChain::BEST_FINALIZED_HEADER_ID_METHOD.into(), - Bytes(Vec::new()), + (), Some(at_block.1), ) - .await?; - - Option::, BlockNumberOf>>::decode( - &mut &encoded_best_finalized_source_block.0[..], - ) - .map_err(SubstrateError::ResponseParseFailed)? - .map(Ok) - .unwrap_or(Err(SubstrateError::NoParachainHeadAtTarget( - P::SourceParachain::PARACHAIN_ID, - P::TargetChain::NAME.into(), - ))) + .await? + .map(Ok) + .unwrap_or(Err(SubstrateError::BridgePalletIsNotInitialized)) } async fn parachain_head( &self, at_block: HeaderIdOf, - metrics: Option<&ParachainsLoopMetrics>, - para_id: ParaId, - ) -> Result, Self::Error> { - let best_para_head_hash: Option = self + ) -> Result>, Self::Error> { + let encoded_best_finalized_source_para_block = self .client - .storage_map_value::( - P::SourceRelayChain::PARACHAINS_FINALITY_PALLET_NAME, - ¶_id, + .state_call( + P::SourceParachain::BEST_FINALIZED_HEADER_ID_METHOD.into(), + Bytes(Vec::new()), Some(at_block.1), ) - .await? - .map(|para_info| para_info.best_head_hash); - - if let (Some(metrics), Some(best_para_head_hash)) = (metrics, &best_para_head_hash) { - let imported_para_head_number = self - .client - .storage_double_map_value::( - P::SourceRelayChain::PARACHAINS_FINALITY_PALLET_NAME, - ¶_id, - &best_para_head_hash.head_hash, - Some(at_block.1), - ) - .await - .and_then(|maybe_encoded_head| match maybe_encoded_head { - Some(encoded_head) => encoded_head - .decode_parachain_head_data::() - .map(|head| head.number) - .map(Some) - .map_err(Self::Error::ResponseParseFailed), - None => Ok(None), - }) - .map_err(|e| { - log::error!( - target: "bridge-metrics", - "Failed to read or decode {} parachain header at {}: {:?}. Metric will have obsolete value", - P::SourceParachain::NAME, - P::TargetChain::NAME, - e, - ); - e - }) - .unwrap_or(None); - if let Some(imported_para_head_number) = imported_para_head_number { - metrics.update_best_parachain_block_at_target(para_id, imported_para_head_number); - } - } + .await?; - Ok(best_para_head_hash) + Ok(Option::>::decode( + &mut &encoded_best_finalized_source_para_block.0[..], + ) + .map_err(SubstrateError::ResponseParseFailed)?) } - async fn submit_parachain_heads_proof( + async fn submit_parachain_head_proof( &self, at_relay_block: HeaderIdOf, - updated_parachains: Vec<(ParaId, ParaHash)>, + updated_head_hash: ParaHash, proof: ParaHeadsProof, ) -> Result { let transaction_params = self.transaction_params.clone(); let call = P::SubmitParachainHeadsCallBuilder::build_submit_parachain_heads_call( at_relay_block, - updated_parachains, + vec![(ParaId(P::SourceParachain::PARACHAIN_ID), updated_head_hash)], proof, ); self.client diff --git a/relays/messages/Cargo.toml b/relays/messages/Cargo.toml index c4d2459e5ca..efb6c6c8585 100644 --- a/relays/messages/Cargo.toml +++ b/relays/messages/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] async-std = { version = "1.6.5", features = ["attributes"] } async-trait = "0.1" -futures = "0.3.26" +futures = "0.3.27" hex = "0.4" log = "0.4.17" num-traits = "0.2" diff --git a/relays/messages/src/message_lane_loop.rs b/relays/messages/src/message_lane_loop.rs index 2903435dda5..0533e51d5db 100644 --- a/relays/messages/src/message_lane_loop.rs +++ b/relays/messages/src/message_lane_loop.rs @@ -723,7 +723,7 @@ pub(crate) mod tests { ( nonce, MessageDetails { - dispatch_weight: Weight::from_ref_time(1), + dispatch_weight: Weight::from_parts(1, 0), size: 1, reward: 1, }, @@ -949,7 +949,7 @@ pub(crate) mod tests { max_unrewarded_relayer_entries_at_target: 4, max_unconfirmed_nonces_at_target: 4, max_messages_in_single_batch: 4, - max_messages_weight_in_single_batch: Weight::from_ref_time(4), + max_messages_weight_in_single_batch: Weight::from_parts(4, 0), max_messages_size_in_single_batch: 4, }, }, diff --git a/relays/messages/src/message_race_delivery.rs b/relays/messages/src/message_race_delivery.rs index 8b8e690ec4a..b50e6c0841e 100644 --- a/relays/messages/src/message_race_delivery.rs +++ b/relays/messages/src/message_race_delivery.rs @@ -255,7 +255,7 @@ struct MessageDeliveryStrategy { /// Latest confirmed nonces at the source client + the header id where we have first met this /// nonce. latest_confirmed_nonces_at_source: VecDeque<(SourceHeaderIdOf

, MessageNonce)>, - /// Target nonces from the source client. + /// Target nonces available at the **best** block of the target chain. target_nonces: Option>, /// Basic delivery strategy. strategy: MessageDeliveryStrategyBase

, @@ -387,13 +387,11 @@ where race_state: &mut RaceState, TargetHeaderIdOf

, P::MessagesProof>, ) { // best target nonces must always be ge than finalized target nonces - let mut target_nonces = self.target_nonces.take().unwrap_or_else(|| nonces.clone()); - target_nonces.nonces_data = nonces.nonces_data.clone(); - target_nonces.latest_nonce = std::cmp::max(target_nonces.latest_nonce, nonces.latest_nonce); - self.target_nonces = Some(target_nonces); + let latest_nonce = nonces.latest_nonce; + self.target_nonces = Some(nonces); self.strategy.best_target_nonces_updated( - TargetClientNonces { latest_nonce: nonces.latest_nonce, nonces_data: () }, + TargetClientNonces { latest_nonce, nonces_data: () }, race_state, ) } @@ -432,6 +430,7 @@ where &self, race_state: RaceState, TargetHeaderIdOf

, P::MessagesProof>, ) -> Option<(RangeInclusive, Self::ProofParameters)> { + let best_target_nonce = self.strategy.best_at_target()?; let best_finalized_source_header_id_at_best_target = race_state.best_finalized_source_header_id_at_best_target.clone()?; let latest_confirmed_nonce_at_source = self @@ -439,7 +438,8 @@ where .iter() .take_while(|(id, _)| id.0 <= best_finalized_source_header_id_at_best_target.0) .last() - .map(|(_, nonce)| *nonce)?; + .map(|(_, nonce)| *nonce) + .unwrap_or(best_target_nonce); let target_nonces = self.target_nonces.as_ref()?; // There's additional condition in the message delivery race: target would reject messages @@ -529,8 +529,8 @@ where let lane_source_client = self.lane_source_client.clone(); let lane_target_client = self.lane_target_client.clone(); - let maximal_source_queue_index = - self.strategy.maximal_available_source_queue_index(race_state)?; + let available_source_queue_indices = + self.strategy.available_source_queue_indices(race_state)?; let source_queue = self.strategy.source_queue(); let reference = RelayMessagesBatchReference { @@ -539,15 +539,13 @@ where max_messages_size_in_single_batch, lane_source_client: lane_source_client.clone(), lane_target_client: lane_target_client.clone(), + best_target_nonce, nonces_queue: source_queue.clone(), - nonces_queue_range: 0..maximal_source_queue_index + 1, + nonces_queue_range: available_source_queue_indices, metrics: self.metrics_msg.clone(), }; - let range_end = MessageRaceLimits::decide(reference).await?; - - let range_begin = source_queue[0].1.begin(); - let selected_nonces = range_begin..=range_end; + let selected_nonces = MessageRaceLimits::decide(reference).await?; let dispatch_weight = self.dispatch_weight_for_range(&selected_nonces); Some(( @@ -588,7 +586,7 @@ mod tests { use super::*; - const DEFAULT_DISPATCH_WEIGHT: Weight = Weight::from_ref_time(1); + const DEFAULT_DISPATCH_WEIGHT: Weight = Weight::from_parts(1, 0); const DEFAULT_SIZE: u32 = 1; type TestRaceState = RaceState; @@ -632,7 +630,7 @@ mod tests { max_unrewarded_relayer_entries_at_target: 4, max_unconfirmed_nonces_at_target: 4, max_messages_in_single_batch: 4, - max_messages_weight_in_single_batch: Weight::from_ref_time(4), + max_messages_weight_in_single_batch: Weight::from_parts(4, 0), max_messages_size_in_single_batch: 4, latest_confirmed_nonces_at_source: vec![(header_id(1), 19)].into_iter().collect(), lane_source_client: TestSourceClient::default(), @@ -671,7 +669,7 @@ mod tests { fn proof_parameters(state_required: bool, weight: u32) -> MessageProofParameters { MessageProofParameters { outbound_state_proof_required: state_required, - dispatch_weight: Weight::from_ref_time(weight as u64), + dispatch_weight: Weight::from_parts(weight as u64, 0), } } @@ -685,7 +683,7 @@ mod tests { ( idx, MessageDetails { - dispatch_weight: Weight::from_ref_time(idx), + dispatch_weight: Weight::from_parts(idx, 0), size: idx as _, reward: idx as _, }, @@ -813,7 +811,7 @@ mod tests { let (state, mut strategy) = prepare_strategy(); // not all queued messages may fit in the batch, because batch has max weight - strategy.max_messages_weight_in_single_batch = Weight::from_ref_time(3); + strategy.max_messages_weight_in_single_batch = Weight::from_parts(3, 0); assert_eq!( strategy.select_nonces_to_deliver(state).await, Some(((20..=22), proof_parameters(false, 3))) @@ -828,7 +826,7 @@ mod tests { // first message doesn't fit in the batch, because it has weight (10) that overflows max // weight (4) strategy.strategy.source_queue_mut()[0].1.get_mut(&20).unwrap().dispatch_weight = - Weight::from_ref_time(10); + Weight::from_parts(10, 0); assert_eq!( strategy.select_nonces_to_deliver(state).await, Some(((20..=20), proof_parameters(false, 10))) @@ -1014,7 +1012,7 @@ mod tests { strategy.max_unrewarded_relayer_entries_at_target = 100; strategy.max_unconfirmed_nonces_at_target = 100; strategy.max_messages_in_single_batch = 5; - strategy.max_messages_weight_in_single_batch = Weight::from_ref_time(100); + strategy.max_messages_weight_in_single_batch = Weight::from_parts(100, 0); strategy.max_messages_size_in_single_batch = 100; state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); @@ -1031,7 +1029,7 @@ mod tests { max_unrewarded_relayer_entries_at_target: 4, max_unconfirmed_nonces_at_target: 4, max_messages_in_single_batch: 4, - max_messages_weight_in_single_batch: Weight::from_ref_time(4), + max_messages_weight_in_single_batch: Weight::from_parts(4, 0), max_messages_size_in_single_batch: 4, latest_confirmed_nonces_at_source: VecDeque::new(), lane_source_client: TestSourceClient::default(), @@ -1057,4 +1055,88 @@ mod tests { ); assert_eq!(strategy.required_source_header_at_target(&source_header_id), None); } + + #[async_std::test] + async fn previous_nonces_are_selected_if_reorg_happens_at_target_chain() { + // this is the copy of the similar test in the `mesage_race_strategy.rs`, but it also tests + // that the `MessageDeliveryStrategy` acts properly in the similar scenario + + // tune parameters to allow 5 nonces per delivery transaction + let (mut state, mut strategy) = prepare_strategy(); + strategy.max_unrewarded_relayer_entries_at_target = 5; + strategy.max_unconfirmed_nonces_at_target = 5; + strategy.max_messages_in_single_batch = 5; + strategy.max_messages_weight_in_single_batch = Weight::from_parts(5, 0); + strategy.max_messages_size_in_single_batch = 5; + + // in this state we have 4 available nonces for delivery + assert_eq!( + strategy.select_nonces_to_deliver(state.clone()).await, + Some(( + 20..=23, + MessageProofParameters { + outbound_state_proof_required: false, + dispatch_weight: Weight::from_parts(4, 0), + } + )), + ); + + // let's say we have submitted 20..=23 + state.nonces_submitted = Some(20..=23); + + // then new nonce 24 appear at the source block 2 + let new_nonce_24 = vec![( + 24, + MessageDetails { dispatch_weight: Weight::from_parts(1, 0), size: 0, reward: 0 }, + )] + .into_iter() + .collect(); + let source_header_2 = header_id(2); + state.best_finalized_source_header_id_at_source = Some(source_header_2); + strategy.source_nonces_updated( + source_header_2, + SourceClientNonces { new_nonces: new_nonce_24, confirmed_nonce: None }, + ); + // and nonce 23 appear at the best block of the target node (best finalized still has 0 + // nonces) + let target_nonces_data = DeliveryRaceTargetNoncesData { + confirmed_nonce: 19, + unrewarded_relayers: UnrewardedRelayersState::default(), + }; + let target_header_2 = header_id(2); + state.best_target_header_id = Some(target_header_2); + strategy.best_target_nonces_updated( + TargetClientNonces { latest_nonce: 23, nonces_data: target_nonces_data.clone() }, + &mut state, + ); + + // then best target header is retracted + strategy.best_target_nonces_updated( + TargetClientNonces { latest_nonce: 19, nonces_data: target_nonces_data.clone() }, + &mut state, + ); + + // ... and some fork with 19 delivered nonces is finalized + let target_header_2_fork = header_id(2_1); + state.best_finalized_source_header_id_at_source = Some(source_header_2); + state.best_finalized_source_header_id_at_best_target = Some(source_header_2); + state.best_target_header_id = Some(target_header_2_fork); + state.best_finalized_target_header_id = Some(target_header_2_fork); + strategy.finalized_target_nonces_updated( + TargetClientNonces { latest_nonce: 19, nonces_data: target_nonces_data.clone() }, + &mut state, + ); + + // now we have to select nonces 20..=23 for delivery again + assert_eq!( + strategy.select_nonces_to_deliver(state.clone()).await, + Some(( + 20..=24, + MessageProofParameters { + outbound_state_proof_required: false, + dispatch_weight: Weight::from_parts(5, 0), + } + )), + ); + } } diff --git a/relays/messages/src/message_race_limits.rs b/relays/messages/src/message_race_limits.rs index a28d9ba63da..873bb6aad04 100644 --- a/relays/messages/src/message_race_limits.rs +++ b/relays/messages/src/message_race_limits.rs @@ -17,7 +17,7 @@ //! enforcement strategy use num_traits::Zero; -use std::ops::Range; +use std::ops::RangeInclusive; use bp_messages::{MessageNonce, Weight}; @@ -76,14 +76,17 @@ pub struct RelayMessagesBatchReference< pub lane_target_client: TargetClient, /// Metrics reference. pub metrics: Option, + /// Best available nonce at the **best** target block. We do not want to deliver nonces + /// less than this nonce, even though the block may be retracted. + pub best_target_nonce: MessageNonce, /// Source queue. pub nonces_queue: SourceRangesQueue< P::SourceHeaderHash, P::SourceHeaderNumber, MessageDetailsMap, >, - /// Source queue range - pub nonces_queue_range: Range, + /// Range of indices within the `nonces_queue` that are available for selection. + pub nonces_queue_range: RangeInclusive, } /// Limits of the message race transactions. @@ -97,14 +100,16 @@ impl MessageRaceLimits { TargetClient: MessageLaneTargetClient

, >( reference: RelayMessagesBatchReference, - ) -> Option { + ) -> Option> { let mut hard_selected_count = 0; let mut selected_weight = Weight::zero(); let mut selected_count: MessageNonce = 0; - let hard_selected_begin_nonce = - reference.nonces_queue[reference.nonces_queue_range.start].1.begin(); + let hard_selected_begin_nonce = std::cmp::max( + reference.best_target_nonce + 1, + reference.nonces_queue[*reference.nonces_queue_range.start()].1.begin(), + ); // relay reference let mut relay_reference = RelayReference { @@ -129,6 +134,7 @@ impl MessageRaceLimits { .nonces_queue .range(reference.nonces_queue_range.clone()) .flat_map(|(_, ready_nonces)| ready_nonces.iter()) + .filter(|(nonce, _)| **nonce >= hard_selected_begin_nonce) .enumerate(); for (index, (nonce, details)) in all_ready_nonces { relay_reference.index = index; @@ -192,7 +198,7 @@ impl MessageRaceLimits { if hard_selected_count != 0 { let selected_max_nonce = hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1; - Some(selected_max_nonce) + Some(hard_selected_begin_nonce..=selected_max_nonce) } else { None } diff --git a/relays/messages/src/message_race_strategy.rs b/relays/messages/src/message_race_strategy.rs index 9a53a487d94..479ffe51329 100644 --- a/relays/messages/src/message_race_strategy.rs +++ b/relays/messages/src/message_race_strategy.rs @@ -41,8 +41,11 @@ pub struct BasicStrategy< Proof, > { /// All queued nonces. + /// + /// The queue may contain already delivered nonces. We only remove entries from this + /// queue after corresponding nonces are finalized by the target chain. source_queue: SourceRangesQueue, - /// The best nonce known to target node (at its best block). `None` if it has not been received + /// The best nonce known to target node at its best block. `None` if it has not been received /// yet. best_target_nonce: Option, /// Unused generic types dump. @@ -93,21 +96,26 @@ impl< &mut self.source_queue } - /// Returns index of the latest source queue entry, that may be delivered to the target node. + /// Returns indices of source queue entries, which may be delivered to the target node. + /// + /// The function may skip some nonces from the queue front if nonces from this entry are + /// already available at the **best** target block. After this block is finalized, the entry + /// will be removed from the queue. /// - /// Returns `None` if no entries may be delivered. All entries before and including the - /// `Some(_)` index are guaranteed to be witnessed at source blocks that are known to be - /// finalized at the target node. - pub fn maximal_available_source_queue_index( + /// All entries before and including the range end index, are guaranteed to be witnessed + /// at source blocks that are known to be finalized at the target node. + /// + /// Returns `None` if no entries may be delivered. + pub fn available_source_queue_indices( &self, race_state: RaceState< HeaderId, HeaderId, Proof, >, - ) -> Option { + ) -> Option> { // if we do not know best nonce at target node, we can't select anything - let _ = self.best_target_nonce?; + let best_target_nonce = self.best_target_nonce?; // if we have already selected nonces that we want to submit, do nothing if race_state.nonces_to_submit.is_some() { @@ -119,6 +127,15 @@ impl< return None } + // find first entry that may be delivered to the target node + let begin_index = self + .source_queue + .iter() + .enumerate() + .skip_while(|(_, (_, nonces))| nonces.end() <= best_target_nonce) + .map(|(index, _)| index) + .next()?; + // 1) we want to deliver all nonces, starting from `target_nonce + 1` // 2) we can't deliver new nonce until header, that has emitted this nonce, is finalized // by target client @@ -127,12 +144,16 @@ impl< // => let's first select range of entries inside deque that are already finalized at // the target client and pass this range to the selector let best_header_at_target = race_state.best_finalized_source_header_id_at_best_target?; - self.source_queue + let end_index = self + .source_queue .iter() .enumerate() + .skip(begin_index) .take_while(|(_, (queued_at, _))| queued_at.0 <= best_header_at_target.0) .map(|(index, _)| index) - .last() + .last()?; + + Some(begin_index..=end_index) } /// Remove all nonces that are less than or equal to given nonce from the source queue. @@ -237,22 +258,6 @@ impl< ) { let nonce = nonces.latest_nonce; - if let Some(best_target_nonce) = self.best_target_nonce { - if nonce < best_target_nonce { - return - } - } - - while let Some(true) = self.source_queue.front().map(|(_, range)| range.begin() <= nonce) { - let maybe_subrange = self.source_queue.pop_front().and_then(|(at_block, range)| { - range.greater_than(nonce).map(|subrange| (at_block, subrange)) - }); - if let Some((at_block, subrange)) = maybe_subrange { - self.source_queue.push_front((at_block, subrange)); - break - } - } - let need_to_select_new_nonces = race_state .nonces_to_submit .as_ref() @@ -271,8 +276,7 @@ impl< race_state.nonces_submitted = None; } - self.best_target_nonce = - Some(std::cmp::max(self.best_target_nonce.unwrap_or(nonces.latest_nonce), nonce)); + self.best_target_nonce = Some(nonce); } fn finalized_target_nonces_updated( @@ -284,7 +288,7 @@ impl< Proof, >, ) { - self.remove_le_nonces_from_source_queue(nonces.latest_nonce); // TODO: does it means that we'll try to submit old nonces in next tx??? + self.remove_le_nonces_from_source_queue(nonces.latest_nonce); self.best_target_nonce = Some(std::cmp::max( self.best_target_nonce.unwrap_or(nonces.latest_nonce), nonces.latest_nonce, @@ -299,9 +303,12 @@ impl< Proof, >, ) -> Option<(RangeInclusive, Self::ProofParameters)> { - let maximal_source_queue_index = self.maximal_available_source_queue_index(race_state)?; - let range_begin = self.source_queue[0].1.begin(); - let range_end = self.source_queue[maximal_source_queue_index].1.end(); + let available_indices = self.available_source_queue_indices(race_state)?; + let range_begin = std::cmp::max( + self.best_target_nonce? + 1, + self.source_queue[*available_indices.start()].1.begin(), + ); + let range_end = self.source_queue[*available_indices.end()].1.end(); Some((range_begin..=range_end, ())) } } @@ -351,7 +358,7 @@ mod tests { strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); assert_eq!(strategy.best_at_source(), None); strategy.best_target_nonces_updated(target_nonces(10), &mut Default::default()); - assert_eq!(strategy.source_queue, vec![]); + assert_eq!(strategy.source_queue, vec![(header_id(1), 1..=5)]); assert_eq!(strategy.best_at_source(), Some(10)); } @@ -372,16 +379,6 @@ mod tests { assert_eq!(strategy.source_queue, vec![(header_id(1), 1..=5)]); } - #[test] - fn target_nonce_is_never_lower_than_latest_known_target_nonce() { - let mut strategy = BasicStrategy::::new(); - assert_eq!(strategy.best_target_nonce, None); - strategy.best_target_nonces_updated(target_nonces(10), &mut Default::default()); - assert_eq!(strategy.best_target_nonce, Some(10)); - strategy.best_target_nonces_updated(target_nonces(5), &mut Default::default()); - assert_eq!(strategy.best_target_nonce, Some(10)); - } - #[test] fn updated_target_nonce_removes_queued_entries() { let mut strategy = BasicStrategy::::new(); @@ -389,9 +386,9 @@ mod tests { strategy.source_nonces_updated(header_id(2), source_nonces(6..=10)); strategy.source_nonces_updated(header_id(3), source_nonces(11..=15)); strategy.source_nonces_updated(header_id(4), source_nonces(16..=20)); - strategy.best_target_nonces_updated(target_nonces(15), &mut Default::default()); + strategy.finalized_target_nonces_updated(target_nonces(15), &mut Default::default()); assert_eq!(strategy.source_queue, vec![(header_id(4), 16..=20)]); - strategy.best_target_nonces_updated(target_nonces(17), &mut Default::default()); + strategy.finalized_target_nonces_updated(target_nonces(17), &mut Default::default()); assert_eq!(strategy.source_queue, vec![(header_id(4), 18..=20)]); } @@ -459,7 +456,7 @@ mod tests { } #[test] - fn maximal_available_source_queue_index_works() { + fn available_source_queue_indices_works() { let mut state = RaceState::<_, _, TestMessagesProof>::default(); let mut strategy = BasicStrategy::::new(); strategy.best_target_nonces_updated(target_nonces(0), &mut state); @@ -468,19 +465,19 @@ mod tests { strategy.source_nonces_updated(header_id(3), source_nonces(7..=9)); state.best_finalized_source_header_id_at_best_target = Some(header_id(0)); - assert_eq!(strategy.maximal_available_source_queue_index(state.clone()), None); + assert_eq!(strategy.available_source_queue_indices(state.clone()), None); state.best_finalized_source_header_id_at_best_target = Some(header_id(1)); - assert_eq!(strategy.maximal_available_source_queue_index(state.clone()), Some(0)); + assert_eq!(strategy.available_source_queue_indices(state.clone()), Some(0..=0)); state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); - assert_eq!(strategy.maximal_available_source_queue_index(state.clone()), Some(1)); + assert_eq!(strategy.available_source_queue_indices(state.clone()), Some(0..=1)); state.best_finalized_source_header_id_at_best_target = Some(header_id(3)); - assert_eq!(strategy.maximal_available_source_queue_index(state.clone()), Some(2)); + assert_eq!(strategy.available_source_queue_indices(state.clone()), Some(0..=2)); state.best_finalized_source_header_id_at_best_target = Some(header_id(4)); - assert_eq!(strategy.maximal_available_source_queue_index(state), Some(2)); + assert_eq!(strategy.available_source_queue_indices(state), Some(0..=2)); } #[test] @@ -514,4 +511,67 @@ mod tests { strategy.remove_le_nonces_from_source_queue(100); assert_eq!(source_queue_nonces(&strategy.source_queue), Vec::::new(),); } + + #[async_std::test] + async fn previous_nonces_are_selected_if_reorg_happens_at_target_chain() { + let source_header_1 = header_id(1); + let target_header_1 = header_id(1); + + // we start in perfec sync state - all headers are synced and finalized on both ends + let mut state = RaceState::<_, _, TestMessagesProof> { + best_finalized_source_header_id_at_source: Some(source_header_1), + best_finalized_source_header_id_at_best_target: Some(source_header_1), + best_target_header_id: Some(target_header_1), + best_finalized_target_header_id: Some(target_header_1), + nonces_to_submit: None, + nonces_submitted: None, + }; + + // in this state we have 1 available nonce for delivery + let mut strategy = BasicStrategy:: { + source_queue: vec![(header_id(1), 1..=1)].into_iter().collect(), + best_target_nonce: Some(0), + _phantom: PhantomData, + }; + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((1..=1, ())),); + + // let's say we have submitted 1..=1 + state.nonces_submitted = Some(1..=1); + + // then new nonce 2 appear at the source block 2 + let source_header_2 = header_id(2); + state.best_finalized_source_header_id_at_source = Some(source_header_2); + strategy.source_nonces_updated( + source_header_2, + SourceClientNonces { new_nonces: 2..=2, confirmed_nonce: None }, + ); + // and nonce 1 appear at the best block of the target node (best finalized still has 0 + // nonces) + let target_header_2 = header_id(2); + state.best_target_header_id = Some(target_header_2); + strategy.best_target_nonces_updated( + TargetClientNonces { latest_nonce: 1, nonces_data: () }, + &mut state, + ); + + // then best target header is retracted + strategy.best_target_nonces_updated( + TargetClientNonces { latest_nonce: 0, nonces_data: () }, + &mut state, + ); + + // ... and some fork with zero delivered nonces is finalized + let target_header_2_fork = header_id(2_1); + state.best_finalized_source_header_id_at_source = Some(source_header_2); + state.best_finalized_source_header_id_at_best_target = Some(source_header_2); + state.best_target_header_id = Some(target_header_2_fork); + state.best_finalized_target_header_id = Some(target_header_2_fork); + strategy.finalized_target_nonces_updated( + TargetClientNonces { latest_nonce: 0, nonces_data: () }, + &mut state, + ); + + // now we have to select nonce 1 for delivery again + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((1..=2, ())),); + } } diff --git a/relays/parachains/Cargo.toml b/relays/parachains/Cargo.toml index 093fc0b94e3..5481ce7edf8 100644 --- a/relays/parachains/Cargo.toml +++ b/relays/parachains/Cargo.toml @@ -7,14 +7,13 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] async-std = "1.6.5" -async-trait = "0.1.64" -futures = "0.3.26" +async-trait = "0.1.66" +futures = "0.3.27" log = "0.4.17" relay-utils = { path = "../utils" } # Bridge dependencies -bp-parachains = { path = "../../primitives/parachains" } bp-polkadot-core = { path = "../../primitives/polkadot-core" } relay-substrate-client = { path = "../client-substrate" } diff --git a/relays/parachains/src/lib.rs b/relays/parachains/src/lib.rs index 94b3ce3ec76..81ea983a6f7 100644 --- a/relays/parachains/src/lib.rs +++ b/relays/parachains/src/lib.rs @@ -16,7 +16,7 @@ use std::fmt::Debug; -use relay_substrate_client::Chain; +use relay_substrate_client::{Chain, Parachain}; pub mod parachains_loop; pub mod parachains_loop_metrics; @@ -24,7 +24,9 @@ pub mod parachains_loop_metrics; /// Finality proofs synchronization pipeline. pub trait ParachainsPipeline: 'static + Clone + Debug + Send + Sync { /// Relay chain which is storing parachain heads in its `paras` module. - type SourceChain: Chain; + type SourceRelayChain: Chain; + /// Parachain which headers we are syncing here. + type SourceParachain: Parachain; /// Target chain (either relay or para) which wants to know about new parachain heads. type TargetChain: Chain; } diff --git a/relays/parachains/src/parachains_loop.rs b/relays/parachains/src/parachains_loop.rs index 7b62e72ca29..9b9038fd976 100644 --- a/relays/parachains/src/parachains_loop.rs +++ b/relays/parachains/src/parachains_loop.rs @@ -17,7 +17,6 @@ use crate::{parachains_loop_metrics::ParachainsLoopMetrics, ParachainsPipeline}; use async_trait::async_trait; -use bp_parachains::BestParaHeadHash; use bp_polkadot_core::{ parachains::{ParaHash, ParaHeadsProof, ParaId}, BlockNumber as RelayBlockNumber, @@ -26,44 +25,17 @@ use futures::{ future::{FutureExt, Shared}, poll, select_biased, }; -use relay_substrate_client::{BlockNumberOf, Chain, HeaderIdOf}; +use relay_substrate_client::{Chain, HeaderIdOf, ParachainBase}; use relay_utils::{ metrics::MetricsParams, relay_loop::Client as RelayClient, FailedClient, TrackedTransactionStatus, TransactionTracker, }; -use std::{ - collections::{BTreeMap, BTreeSet}, - future::Future, - pin::Pin, - task::Poll, - time::Duration, -}; - -/// Parachain heads synchronization params. -#[derive(Clone, Debug)] -pub struct ParachainSyncParams { - /// Parachains that we're relaying here. - pub parachains: Vec, - /// Parachain heads update strategy. - pub strategy: ParachainSyncStrategy, - /// Stall timeout. If we have submitted transaction and we see no state updates for this - /// period, we consider our transaction lost. - pub stall_timeout: Duration, -} - -/// Parachain heads update strategy. -#[derive(Clone, Copy, Debug)] -pub enum ParachainSyncStrategy { - /// Update whenever any parachain head is updated. - Any, - /// Wait till all parachain heads are updated. - All, -} +use std::{future::Future, pin::Pin, task::Poll}; /// Parachain header availability at a certain chain. #[derive(Clone, Copy, Debug)] pub enum AvailableHeader { - /// The client refuses to report parachain head at this moment. + /// The client can not report actual parachain head at this moment. /// /// It is a "mild" error, which may appear when e.g. on-demand parachains relay is used. /// This variant must be treated as "we don't want to update parachain head value at the @@ -78,15 +50,20 @@ pub enum AvailableHeader { } impl AvailableHeader { - /// Transform contained value. - pub fn map(self, f: F) -> AvailableHeader - where - F: FnOnce(T) -> U, - { - match self { - AvailableHeader::Unavailable => AvailableHeader::Unavailable, - AvailableHeader::Missing => AvailableHeader::Missing, - AvailableHeader::Available(val) => AvailableHeader::Available(f(val)), + /// Return available header. + pub fn as_available(&self) -> Option<&T> { + match *self { + AvailableHeader::Available(ref header) => Some(header), + _ => None, + } + } +} + +impl From> for AvailableHeader { + fn from(maybe_header: Option) -> AvailableHeader { + match maybe_header { + Some(header) => AvailableHeader::Available(header), + None => AvailableHeader::Missing, } } } @@ -97,27 +74,17 @@ pub trait SourceClient: RelayClient { /// Returns `Ok(true)` if client is in synced state. async fn ensure_synced(&self) -> Result; - /// Get parachain head hash at given block. - /// - /// The implementation may call `ParachainsLoopMetrics::update_best_parachain_block_at_source` - /// on provided `metrics` object to update corresponding metric value. + /// Get parachain head id at given block. async fn parachain_head( &self, - at_block: HeaderIdOf, - metrics: Option<&ParachainsLoopMetrics>, - para_id: ParaId, - ) -> Result, Self::Error>; + at_block: HeaderIdOf, + ) -> Result>, Self::Error>; - /// Get parachain heads proof. - /// - /// The number and order of entries in the resulting parachain head hashes vector must match the - /// number and order of parachains in the `parachains` vector. The incorrect implementation will - /// result in panic. - async fn prove_parachain_heads( + /// Get parachain head proof at given block. + async fn prove_parachain_head( &self, - at_block: HeaderIdOf, - parachains: &[ParaId], - ) -> Result<(ParaHeadsProof, Vec), Self::Error>; + at_block: HeaderIdOf, + ) -> Result<(ParaHeadsProof, ParaHash), Self::Error>; } /// Target client used in parachain heads synchronization loop. @@ -129,28 +96,23 @@ pub trait TargetClient: RelayClient { /// Get best block id. async fn best_block(&self) -> Result, Self::Error>; - /// Get best finalized source block id. - async fn best_finalized_source_block( + /// Get best finalized source relay chain block id. + async fn best_finalized_source_relay_chain_block( &self, at_block: &HeaderIdOf, - ) -> Result, Self::Error>; + ) -> Result, Self::Error>; - /// Get parachain head hash at given block. - /// - /// The implementation may call `ParachainsLoopMetrics::update_best_parachain_block_at_target` - /// on provided `metrics` object to update corresponding metric value. + /// Get parachain head id at given block. async fn parachain_head( &self, at_block: HeaderIdOf, - metrics: Option<&ParachainsLoopMetrics>, - para_id: ParaId, - ) -> Result, Self::Error>; + ) -> Result>, Self::Error>; /// Submit parachain heads proof. - async fn submit_parachain_heads_proof( + async fn submit_parachain_head_proof( &self, - at_source_block: HeaderIdOf, - updated_parachains: Vec<(ParaId, ParaHash)>, + at_source_block: HeaderIdOf, + para_head_hash: ParaHash, proof: ParaHeadsProof, ) -> Result; } @@ -158,19 +120,23 @@ pub trait TargetClient: RelayClient { /// Return prefix that will be used by default to expose Prometheus metrics of the parachains /// sync loop. pub fn metrics_prefix() -> String { - format!("{}_to_{}_Parachains", P::SourceChain::NAME, P::TargetChain::NAME) + format!( + "{}_to_{}_Parachains_{}", + P::SourceRelayChain::NAME, + P::TargetChain::NAME, + P::SourceParachain::PARACHAIN_ID + ) } /// Run parachain heads synchronization. pub async fn run( source_client: impl SourceClient

, target_client: impl TargetClient

, - sync_params: ParachainSyncParams, metrics_params: MetricsParams, exit_signal: impl Future + 'static + Send, ) -> Result<(), relay_utils::Error> where - P::SourceChain: Chain, + P::SourceRelayChain: Chain, { let exit_signal = exit_signal.shared(); relay_utils::relay_loop(source_client, target_client) @@ -179,13 +145,7 @@ where .expose() .await? .run(metrics_prefix::

(), move |source_client, target_client, metrics| { - run_until_connection_lost( - source_client, - target_client, - sync_params.clone(), - metrics, - exit_signal.clone(), - ) + run_until_connection_lost(source_client, target_client, metrics, exit_signal.clone()) }) .await } @@ -194,16 +154,15 @@ where async fn run_until_connection_lost( source_client: impl SourceClient

, target_client: impl TargetClient

, - sync_params: ParachainSyncParams, metrics: Option, exit_signal: impl Future + Send, ) -> Result<(), FailedClient> where - P::SourceChain: Chain, + P::SourceRelayChain: Chain, { let exit_signal = exit_signal.fuse(); let min_block_interval = std::cmp::min( - P::SourceChain::AVERAGE_BLOCK_INTERVAL, + P::SourceRelayChain::AVERAGE_BLOCK_INTERVAL, P::TargetChain::AVERAGE_BLOCK_INTERVAL, ); @@ -232,7 +191,7 @@ where log::warn!( target: "bridge", "{} client is syncing. Won't do anything until it is synced", - P::SourceChain::NAME, + P::SourceRelayChain::NAME, ); continue }, @@ -240,7 +199,7 @@ where log::warn!( target: "bridge", "{} client has failed to return its sync status: {:?}", - P::SourceChain::NAME, + P::SourceRelayChain::NAME, e, ); return Err(FailedClient::Source) @@ -249,33 +208,28 @@ where // if we have active transaction, we'll need to wait until it is mined or dropped let best_target_block = target_client.best_block().await.map_err(|e| { - log::warn!(target: "bridge", "Failed to read best {} block: {:?}", P::SourceChain::NAME, e); + log::warn!(target: "bridge", "Failed to read best {} block: {:?}", P::SourceRelayChain::NAME, e); FailedClient::Target })?; - let heads_at_target = read_heads_at_target( - &target_client, - metrics.as_ref(), - &best_target_block, - &sync_params.parachains, - ) - .await?; + let head_at_target = + read_head_at_target(&target_client, metrics.as_ref(), &best_target_block).await?; // check if our transaction has been mined if let Some(tracker) = submitted_heads_tracker.take() { - match tracker.update(&best_target_block, &heads_at_target).await { - SubmittedHeadsStatus::Waiting(tracker) => { + match tracker.update(&best_target_block, &head_at_target).await { + SubmittedHeadStatus::Waiting(tracker) => { // no news about our transaction and we shall keep waiting submitted_heads_tracker = Some(tracker); continue }, - SubmittedHeadsStatus::Final(TrackedTransactionStatus::Finalized(_)) => { + SubmittedHeadStatus::Final(TrackedTransactionStatus::Finalized(_)) => { // all heads have been updated, we don't need this tracker anymore }, - SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost) => { + SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost) => { log::warn!( target: "bridge", "Parachains synchronization from {} to {} has stalled. Going to restart", - P::SourceChain::NAME, + P::SourceRelayChain::NAME, P::TargetChain::NAME, ); @@ -287,247 +241,201 @@ where // we have no active transaction and may need to update heads, but do we have something for // update? let best_finalized_relay_block = target_client - .best_finalized_source_block(&best_target_block) + .best_finalized_source_relay_chain_block(&best_target_block) .await .map_err(|e| { log::warn!( target: "bridge", "Failed to read best finalized {} block from {}: {:?}", - P::SourceChain::NAME, + P::SourceRelayChain::NAME, P::TargetChain::NAME, e, ); FailedClient::Target })?; - let heads_at_source = read_heads_at_source( - &source_client, - metrics.as_ref(), - &best_finalized_relay_block, - &sync_params.parachains, - ) - .await?; - let updated_ids = select_parachains_to_update::

( - heads_at_source, - heads_at_target, - best_finalized_relay_block, - ); - let is_update_required = is_update_required(&sync_params, &updated_ids); - - log::info!( - target: "bridge", - "Total {} parachains: {}. Up-to-date at {}: {}. Needs update at {}: {}.", - P::SourceChain::NAME, - sync_params.parachains.len(), - P::TargetChain::NAME, - sync_params.parachains.len() - updated_ids.len(), - P::TargetChain::NAME, - updated_ids.len(), - ); + let head_at_source = + read_head_at_source(&source_client, metrics.as_ref(), &best_finalized_relay_block) + .await?; + let is_update_required = is_update_required::

(head_at_source, head_at_target); if is_update_required { - let (heads_proofs, head_hashes) = source_client - .prove_parachain_heads(best_finalized_relay_block, &updated_ids) + let (head_proof, head_hash) = source_client + .prove_parachain_head(best_finalized_relay_block) .await .map_err(|e| { log::warn!( target: "bridge", - "Failed to prove {} parachain heads: {:?}", - P::SourceChain::NAME, + "Failed to prove {} parachain ParaId({}) heads: {:?}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, e, ); FailedClient::Source })?; log::info!( target: "bridge", - "Submitting {} parachain heads update transaction to {}", - P::SourceChain::NAME, + "Submitting {} parachain ParaId({}) head update transaction to {}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, P::TargetChain::NAME, ); - assert_eq!( - head_hashes.len(), - updated_ids.len(), - "Incorrect parachains SourceClient implementation" - ); - let transaction_tracker = target_client - .submit_parachain_heads_proof( - best_finalized_relay_block, - updated_ids.iter().cloned().zip(head_hashes).collect(), - heads_proofs, - ) + .submit_parachain_head_proof(best_finalized_relay_block, head_hash, head_proof) .await .map_err(|e| { log::warn!( target: "bridge", - "Failed to submit {} parachain heads proof to {}: {:?}", - P::SourceChain::NAME, + "Failed to submit {} parachain ParaId({}) heads proof to {}: {:?}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, P::TargetChain::NAME, e, ); FailedClient::Target })?; - submitted_heads_tracker = Some(SubmittedHeadsTracker::

::new( - updated_ids, - best_finalized_relay_block.0, - transaction_tracker, - )); + submitted_heads_tracker = + Some(SubmittedHeadsTracker::

::new(head_at_source, transaction_tracker)); } } } -/// Given heads at source and target clients, returns set of heads that are out of sync. -fn select_parachains_to_update( - heads_at_source: BTreeMap>, - heads_at_target: BTreeMap>, - best_finalized_relay_block: HeaderIdOf, -) -> Vec +/// Returns `true` if we need to submit parachain-head-update transaction. +fn is_update_required( + head_at_source: AvailableHeader>, + head_at_target: Option>, +) -> bool where - P::SourceChain: Chain, + P::SourceRelayChain: Chain, { log::trace!( target: "bridge", - "Selecting {} parachains to update at {} (relay block: {:?}):\n\t\ + "Checking if {} parachain ParaId({}) needs update at {}:\n\t\ At {}: {:?}\n\t\ At {}: {:?}", - P::SourceChain::NAME, + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, P::TargetChain::NAME, - best_finalized_relay_block, - P::SourceChain::NAME, - heads_at_source, + P::SourceRelayChain::NAME, + head_at_source, P::TargetChain::NAME, - heads_at_target, + head_at_target, ); - heads_at_source - .into_iter() - .zip(heads_at_target.into_iter()) - .filter(|((para, head_at_source), (_, head_at_target))| { - let needs_update = match (head_at_source, head_at_target) { - (AvailableHeader::Unavailable, _) => { - // source client has politely asked us not to update current parachain head - // at the target chain - false - }, - (AvailableHeader::Available(head_at_source), Some(head_at_target)) - if head_at_target.at_relay_block_number < best_finalized_relay_block.0 && - head_at_target.head_hash != *head_at_source => - { - // source client knows head that is better than the head known to the target - // client - true - }, - (AvailableHeader::Available(_), Some(_)) => { - // this is normal case when relay has recently updated heads, when parachain is - // not progressing, or when our source client is still syncing - false - }, - (AvailableHeader::Available(_), None) => { - // parachain is not yet known to the target client. This is true when parachain - // or bridge has been just onboarded/started - true - }, - (AvailableHeader::Missing, Some(_)) => { - // parachain/parathread has been offboarded removed from the system. It needs to - // be propageted to the target client - true - }, - (AvailableHeader::Missing, None) => { - // all's good - parachain is unknown to both clients - false - }, - }; - if needs_update { - log::trace!( - target: "bridge", - "{} parachain {:?} needs update at {}: {:?} vs {:?}", - P::SourceChain::NAME, - para, - P::TargetChain::NAME, - head_at_source, - head_at_target, - ); - } - - needs_update - }) - .map(|((para, _), _)| para) - .collect() -} + let needs_update = match (head_at_source, head_at_target) { + (AvailableHeader::Unavailable, _) => { + // source client has politely asked us not to update current parachain head + // at the target chain + false + }, + (AvailableHeader::Available(head_at_source), Some(head_at_target)) + if head_at_source.number() > head_at_target.number() => + { + // source client knows head that is better than the head known to the target + // client + true + }, + (AvailableHeader::Available(_), Some(_)) => { + // this is normal case when relay has recently updated heads, when parachain is + // not progressing, or when our source client is still syncing + false + }, + (AvailableHeader::Available(_), None) => { + // parachain is not yet known to the target client. This is true when parachain + // or bridge has been just onboarded/started + true + }, + (AvailableHeader::Missing, Some(_)) => { + // parachain/parathread has been offboarded removed from the system. It needs to + // be propageted to the target client + true + }, + (AvailableHeader::Missing, None) => { + // all's good - parachain is unknown to both clients + false + }, + }; -/// Returns true if we need to submit update transactions to the target node. -fn is_update_required(sync_params: &ParachainSyncParams, updated_ids: &[ParaId]) -> bool { - match sync_params.strategy { - ParachainSyncStrategy::All => updated_ids.len() == sync_params.parachains.len(), - ParachainSyncStrategy::Any => !updated_ids.is_empty(), + if needs_update { + log::trace!( + target: "bridge", + "{} parachain ParaId({}) needs update at {}: {:?} vs {:?}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, + P::TargetChain::NAME, + head_at_source, + head_at_target, + ); } + + needs_update } -/// Reads given parachains heads from the source client. -/// -/// Guarantees that the returning map will have an entry for every parachain from `parachains`. -async fn read_heads_at_source( +/// Reads parachain head from the source client. +async fn read_head_at_source( source_client: &impl SourceClient

, metrics: Option<&ParachainsLoopMetrics>, - at_relay_block: &HeaderIdOf, - parachains: &[ParaId], -) -> Result>, FailedClient> { - let mut para_head_hashes = BTreeMap::new(); - for para in parachains { - let para_head = source_client.parachain_head(*at_relay_block, metrics, *para).await; - match para_head { - Ok(para_head) => { - para_head_hashes.insert(*para, para_head); - }, - Err(e) => { - log::warn!( - target: "bridge", - "Failed to read head of {} parachain {:?}: {:?}", - P::SourceChain::NAME, - para, - e, + at_relay_block: &HeaderIdOf, +) -> Result>, FailedClient> { + let para_head = source_client.parachain_head(*at_relay_block).await; + match para_head { + Ok(AvailableHeader::Available(para_head)) => { + if let Some(metrics) = metrics { + metrics.update_best_parachain_block_at_source( + ParaId(P::SourceParachain::PARACHAIN_ID), + para_head.number(), ); - return Err(FailedClient::Source) - }, - } + } + Ok(AvailableHeader::Available(para_head)) + }, + Ok(r) => Ok(r), + Err(e) => { + log::warn!( + target: "bridge", + "Failed to read head of {} parachain ParaId({:?}): {:?}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, + e, + ); + Err(FailedClient::Source) + }, } - Ok(para_head_hashes) } -/// Reads given parachains heads from the source client. -/// -/// Guarantees that the returning map will have an entry for every parachain from `parachains`. -async fn read_heads_at_target( +/// Reads parachain head from the target client. +async fn read_head_at_target( target_client: &impl TargetClient

, metrics: Option<&ParachainsLoopMetrics>, at_block: &HeaderIdOf, - parachains: &[ParaId], -) -> Result>, FailedClient> { - let mut para_best_head_hashes = BTreeMap::new(); - for para in parachains { - let para_best_head = target_client.parachain_head(*at_block, metrics, *para).await; - match para_best_head { - Ok(para_best_head) => { - para_best_head_hashes.insert(*para, para_best_head); - }, - Err(e) => { - log::warn!( - target: "bridge", - "Failed to read head of {} parachain {:?} at {}: {:?}", - P::SourceChain::NAME, - para, - P::TargetChain::NAME, - e, +) -> Result>, FailedClient> { + let para_head_id = target_client.parachain_head(*at_block).await; + match para_head_id { + Ok(Some(para_head_id)) => { + if let Some(metrics) = metrics { + metrics.update_best_parachain_block_at_target( + ParaId(P::SourceParachain::PARACHAIN_ID), + para_head_id.number(), ); - return Err(FailedClient::Target) - }, - } + } + Ok(Some(para_head_id)) + }, + Ok(None) => Ok(None), + Err(e) => { + log::warn!( + target: "bridge", + "Failed to read head of {} parachain ParaId({}) at {}: {:?}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, + P::TargetChain::NAME, + e, + ); + Err(FailedClient::Target) + }, } - Ok(para_best_head_hashes) } /// Submitted heads status. -enum SubmittedHeadsStatus { +enum SubmittedHeadStatus { /// Heads are not yet updated. Waiting(SubmittedHeadsTracker

), /// Heads transaction has either been finalized or lost (i.e. received its "final" status). @@ -551,66 +459,50 @@ type SharedTransactionTracker

= Shared< /// Submitted parachain heads transaction. struct SubmittedHeadsTracker { - /// Ids of parachains which heads were updated in the tracked transaction. - awaiting_update: BTreeSet, - /// Number of relay chain block that has been used to craft parachain heads proof. - relay_block_number: BlockNumberOf, + /// Parachain header id that we have submitted. + submitted_head: AvailableHeader>, /// Future that waits for submitted transaction finality or loss. /// /// It needs to be shared because of `poll` macro and our consuming `update` method. transaction_tracker: SharedTransactionTracker

, } -impl SubmittedHeadsTracker

-where - P::SourceChain: Chain, -{ +impl SubmittedHeadsTracker

{ /// Creates new parachain heads transaction tracker. pub fn new( - awaiting_update: impl IntoIterator, - relay_block_number: BlockNumberOf, + submitted_head: AvailableHeader>, transaction_tracker: impl TransactionTracker> + 'static, ) -> Self { SubmittedHeadsTracker { - awaiting_update: awaiting_update.into_iter().collect(), - relay_block_number, + submitted_head, transaction_tracker: transaction_tracker.wait().fuse().boxed().shared(), } } /// Returns `None` if all submitted parachain heads have been updated. pub async fn update( - mut self, + self, at_target_block: &HeaderIdOf, - heads_at_target: &BTreeMap>, - ) -> SubmittedHeadsStatus

{ - // remove all pending heads that were synced - for (para, best_para_head) in heads_at_target { - if best_para_head - .as_ref() - .map(|best_para_head| { - best_para_head.at_relay_block_number >= self.relay_block_number - }) - .unwrap_or(false) - { - self.awaiting_update.remove(para); - - log::trace!( - target: "bridge", - "Head of parachain {:?} has been updated at {}: {:?}. Outdated parachains remaining: {}", - para, - P::TargetChain::NAME, - best_para_head, - self.awaiting_update.len(), - ); - } - } + head_at_target: &Option>, + ) -> SubmittedHeadStatus

{ + // check if our head has been updated + let is_head_updated = match (self.submitted_head, head_at_target) { + (AvailableHeader::Available(submitted_head), Some(head_at_target)) + if head_at_target.number() >= submitted_head.number() => + true, + (AvailableHeader::Missing, None) => true, + _ => false, + }; + if is_head_updated { + log::trace!( + target: "bridge", + "Head of parachain ParaId({}) has been updated at {}: {:?}", + P::SourceParachain::PARACHAIN_ID, + P::TargetChain::NAME, + head_at_target, + ); - // if we have synced all required heads, we are done - if self.awaiting_update.is_empty() { - return SubmittedHeadsStatus::Final(TrackedTransactionStatus::Finalized( - *at_target_block, - )) + return SubmittedHeadStatus::Final(TrackedTransactionStatus::Finalized(*at_target_block)) } // if underlying transaction tracker has reported that the transaction is lost, we may @@ -618,16 +510,16 @@ where let transaction_tracker = self.transaction_tracker.clone(); match poll!(transaction_tracker) { Poll::Ready(TrackedTransactionStatus::Lost) => - return SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost), + return SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost), Poll::Ready(TrackedTransactionStatus::Finalized(_)) => { // so we are here and our transaction is mined+finalized, but some of heads were not // updated => we're considering our loop as stalled - return SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost) + return SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost) }, _ => (), } - SubmittedHeadsStatus::Waiting(self) + SubmittedHeadStatus::Waiting(self) } } @@ -637,18 +529,16 @@ mod tests { use async_std::sync::{Arc, Mutex}; use codec::Encode; use futures::{SinkExt, StreamExt}; - use relay_substrate_client::test_chain::TestChain; + use relay_substrate_client::test_chain::{TestChain, TestParachain}; use relay_utils::{HeaderId, MaybeConnectionError}; use sp_core::H256; - const PARA_ID: u32 = 0; - const PARA_0_HASH: ParaHash = H256([1u8; 32]); - const PARA_1_HASH: ParaHash = H256([2u8; 32]); + const PARA_10_HASH: ParaHash = H256([10u8; 32]); + const PARA_20_HASH: ParaHash = H256([20u8; 32]); #[derive(Clone, Debug)] enum TestError { Error, - MissingParachainHeadProof, } impl MaybeConnectionError for TestError { @@ -661,7 +551,8 @@ mod tests { struct TestParachainsPipeline; impl ParachainsPipeline for TestParachainsPipeline { - type SourceChain = TestChain; + type SourceRelayChain = TestChain; + type SourceParachain = TestParachain; type TargetChain = TestChain; } @@ -688,12 +579,12 @@ mod tests { #[derive(Clone, Debug)] struct TestClientData { source_sync_status: Result, - source_heads: BTreeMap, TestError>>, - source_proofs: BTreeMap, TestError>>, + source_head: Result>, TestError>, + source_proof: Result<(), TestError>, target_best_block: Result, TestError>, target_best_finalized_source_block: Result, TestError>, - target_heads: BTreeMap>, + target_head: Result>, TestError>, target_submit_result: Result<(), TestError>, exit_signal_sender: Option>>, @@ -703,14 +594,12 @@ mod tests { pub fn minimal() -> Self { TestClientData { source_sync_status: Ok(true), - source_heads: vec![(PARA_ID, Ok(AvailableHeader::Available(PARA_0_HASH)))] - .into_iter() - .collect(), - source_proofs: vec![(PARA_ID, Ok(PARA_0_HASH.encode()))].into_iter().collect(), + source_head: Ok(AvailableHeader::Available(HeaderId(0, PARA_20_HASH))), + source_proof: Ok(()), target_best_block: Ok(HeaderId(0, Default::default())), target_best_finalized_source_block: Ok(HeaderId(0, Default::default())), - target_heads: BTreeMap::new(), + target_head: Ok(None), target_submit_result: Ok(()), exit_signal_sender: None, @@ -750,34 +639,17 @@ mod tests { async fn parachain_head( &self, _at_block: HeaderIdOf, - _metrics: Option<&ParachainsLoopMetrics>, - para_id: ParaId, - ) -> Result, TestError> { - match self.data.lock().await.source_heads.get(¶_id.0).cloned() { - Some(result) => result, - None => Ok(AvailableHeader::Missing), - } + ) -> Result>, TestError> { + self.data.lock().await.source_head.clone() } - async fn prove_parachain_heads( + async fn prove_parachain_head( &self, _at_block: HeaderIdOf, - parachains: &[ParaId], - ) -> Result<(ParaHeadsProof, Vec), TestError> { - let mut proofs = Vec::new(); - for para_id in parachains { - proofs.push( - self.data - .lock() - .await - .source_proofs - .get(¶_id.0) - .cloned() - .transpose()? - .ok_or(TestError::MissingParachainHeadProof)?, - ); - } - Ok((ParaHeadsProof(proofs), vec![Default::default(); parachains.len()])) + ) -> Result<(ParaHeadsProof, ParaHash), TestError> { + let head = *self.data.lock().await.source_head.clone()?.as_available().unwrap(); + let proof = (ParaHeadsProof(vec![head.hash().encode()]), head.hash()); + self.data.lock().await.source_proof.clone().map(|_| proof) } } @@ -789,7 +661,7 @@ mod tests { self.data.lock().await.target_best_block.clone() } - async fn best_finalized_source_block( + async fn best_finalized_source_relay_chain_block( &self, _at_block: &HeaderIdOf, ) -> Result, TestError> { @@ -799,16 +671,14 @@ mod tests { async fn parachain_head( &self, _at_block: HeaderIdOf, - _metrics: Option<&ParachainsLoopMetrics>, - para_id: ParaId, - ) -> Result, TestError> { - self.data.lock().await.target_heads.get(¶_id.0).cloned().transpose() + ) -> Result>, TestError> { + self.data.lock().await.target_head.clone() } - async fn submit_parachain_heads_proof( + async fn submit_parachain_head_proof( &self, _at_source_block: HeaderIdOf, - _updated_parachains: Vec<(ParaId, ParaHash)>, + _updated_parachain_head: ParaHash, _proof: ParaHeadsProof, ) -> Result { let mut data = self.data.lock().await; @@ -823,14 +693,6 @@ mod tests { } } - fn default_sync_params() -> ParachainSyncParams { - ParachainSyncParams { - parachains: vec![ParaId(PARA_ID)], - strategy: ParachainSyncStrategy::Any, - stall_timeout: Duration::from_secs(60), - } - } - #[test] fn when_source_client_fails_to_return_sync_state() { let mut test_source_client = TestClientData::minimal(); @@ -840,7 +702,6 @@ mod tests { async_std::task::block_on(run_until_connection_lost( TestClient::from(test_source_client), TestClient::from(TestClientData::minimal()), - default_sync_params(), None, futures::future::pending(), )), @@ -857,7 +718,6 @@ mod tests { async_std::task::block_on(run_until_connection_lost( TestClient::from(TestClientData::minimal()), TestClient::from(test_target_client), - default_sync_params(), None, futures::future::pending(), )), @@ -868,13 +728,12 @@ mod tests { #[test] fn when_target_client_fails_to_read_heads() { let mut test_target_client = TestClientData::minimal(); - test_target_client.target_heads.insert(PARA_ID, Err(TestError::Error)); + test_target_client.target_head = Err(TestError::Error); assert_eq!( async_std::task::block_on(run_until_connection_lost( TestClient::from(TestClientData::minimal()), TestClient::from(test_target_client), - default_sync_params(), None, futures::future::pending(), )), @@ -891,7 +750,6 @@ mod tests { async_std::task::block_on(run_until_connection_lost( TestClient::from(TestClientData::minimal()), TestClient::from(test_target_client), - default_sync_params(), None, futures::future::pending(), )), @@ -902,13 +760,12 @@ mod tests { #[test] fn when_source_client_fails_to_read_heads() { let mut test_source_client = TestClientData::minimal(); - test_source_client.source_heads.insert(PARA_ID, Err(TestError::Error)); + test_source_client.source_head = Err(TestError::Error); assert_eq!( async_std::task::block_on(run_until_connection_lost( TestClient::from(test_source_client), TestClient::from(TestClientData::minimal()), - default_sync_params(), None, futures::future::pending(), )), @@ -919,13 +776,12 @@ mod tests { #[test] fn when_source_client_fails_to_prove_heads() { let mut test_source_client = TestClientData::minimal(); - test_source_client.source_proofs.insert(PARA_ID, Err(TestError::Error)); + test_source_client.source_proof = Err(TestError::Error); assert_eq!( async_std::task::block_on(run_until_connection_lost( TestClient::from(test_source_client), TestClient::from(TestClientData::minimal()), - default_sync_params(), None, futures::future::pending(), )), @@ -942,7 +798,6 @@ mod tests { async_std::task::block_on(run_until_connection_lost( TestClient::from(TestClientData::minimal()), TestClient::from(test_target_client), - default_sync_params(), None, futures::future::pending(), )), @@ -957,7 +812,6 @@ mod tests { async_std::task::block_on(run_until_connection_lost( TestClient::from(TestClientData::minimal()), TestClient::from(TestClientData::with_exit_signal_sender(exit_signal_sender)), - default_sync_params(), None, exit_signal.into_future().map(|(_, _)| ()), )), @@ -965,111 +819,62 @@ mod tests { ); } - const PARA_1_ID: u32 = PARA_ID + 1; - const SOURCE_BLOCK_NUMBER: u32 = 100; - fn test_tx_tracker() -> SubmittedHeadsTracker { SubmittedHeadsTracker::new( - vec![ParaId(PARA_ID), ParaId(PARA_1_ID)], - SOURCE_BLOCK_NUMBER, + AvailableHeader::Available(HeaderId(20, PARA_20_HASH)), TestTransactionTracker(None), ) } - fn all_expected_tracker_heads() -> BTreeMap> { - vec![ - ( - ParaId(PARA_ID), - Some(BestParaHeadHash { - at_relay_block_number: SOURCE_BLOCK_NUMBER, - head_hash: PARA_0_HASH, - }), - ), - ( - ParaId(PARA_1_ID), - Some(BestParaHeadHash { - at_relay_block_number: SOURCE_BLOCK_NUMBER, - head_hash: PARA_0_HASH, - }), - ), - ] - .into_iter() - .collect() - } - - impl From> for Option> { - fn from(status: SubmittedHeadsStatus) -> Option> { + impl From> for Option<()> { + fn from(status: SubmittedHeadStatus) -> Option<()> { match status { - SubmittedHeadsStatus::Waiting(tracker) => Some(tracker.awaiting_update), + SubmittedHeadStatus::Waiting(_) => Some(()), _ => None, } } } #[async_std::test] - async fn tx_tracker_update_when_nothing_is_updated() { + async fn tx_tracker_update_when_head_at_target_has_none_value() { assert_eq!( - Some(test_tx_tracker().awaiting_update), + Some(()), test_tx_tracker() - .update(&HeaderId(0, Default::default()), &vec![].into_iter().collect()) + .update(&HeaderId(0, Default::default()), &Some(HeaderId(10, PARA_10_HASH))) .await .into(), ); } #[async_std::test] - async fn tx_tracker_update_when_one_of_heads_is_updated_to_previous_value() { + async fn tx_tracker_update_when_head_at_target_has_old_value() { assert_eq!( - Some(test_tx_tracker().awaiting_update), + Some(()), test_tx_tracker() - .update( - &HeaderId(0, Default::default()), - &vec![( - ParaId(PARA_ID), - Some(BestParaHeadHash { - at_relay_block_number: SOURCE_BLOCK_NUMBER - 1, - head_hash: PARA_0_HASH, - }) - )] - .into_iter() - .collect() - ) + .update(&HeaderId(0, Default::default()), &Some(HeaderId(10, PARA_10_HASH))) .await .into(), ); } #[async_std::test] - async fn tx_tracker_update_when_one_of_heads_is_updated() { - assert_eq!( - Some(vec![ParaId(PARA_1_ID)].into_iter().collect::>()), + async fn tx_tracker_update_when_head_at_target_has_same_value() { + assert!(matches!( test_tx_tracker() - .update( - &HeaderId(0, Default::default()), - &vec![( - ParaId(PARA_ID), - Some(BestParaHeadHash { - at_relay_block_number: SOURCE_BLOCK_NUMBER, - head_hash: PARA_0_HASH, - }) - )] - .into_iter() - .collect() - ) - .await - .into(), - ); + .update(&HeaderId(0, Default::default()), &Some(HeaderId(20, PARA_20_HASH))) + .await, + SubmittedHeadStatus::Final(TrackedTransactionStatus::Finalized(_)), + )); } #[async_std::test] - async fn tx_tracker_update_when_all_heads_are_updated() { - assert_eq!( - Option::>::None, + async fn tx_tracker_update_when_head_at_target_has_better_value() { + assert!(matches!( test_tx_tracker() - .update(&HeaderId(0, Default::default()), &all_expected_tracker_heads()) - .await - .into(), - ); + .update(&HeaderId(0, Default::default()), &Some(HeaderId(30, PARA_20_HASH))) + .await, + SubmittedHeadStatus::Final(TrackedTransactionStatus::Finalized(_)), + )); } #[async_std::test] @@ -1079,9 +884,9 @@ mod tests { futures::future::ready(TrackedTransactionStatus::Lost).boxed().shared(); assert!(matches!( tx_tracker - .update(&HeaderId(0, Default::default()), &vec![].into_iter().collect()) + .update(&HeaderId(0, Default::default()), &Some(HeaderId(10, PARA_10_HASH))) .await, - SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost), + SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost), )); } @@ -1094,162 +899,55 @@ mod tests { .shared(); assert!(matches!( tx_tracker - .update(&HeaderId(0, Default::default()), &vec![].into_iter().collect()) + .update(&HeaderId(0, Default::default()), &Some(HeaderId(10, PARA_10_HASH))) .await, - SubmittedHeadsStatus::Final(TrackedTransactionStatus::Lost), + SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost), )); } - #[async_std::test] - async fn tx_tracker_update_when_tx_is_finalized_and_heads_are_updated() { - let mut tx_tracker = test_tx_tracker(); - tx_tracker.transaction_tracker = - futures::future::ready(TrackedTransactionStatus::Finalized(Default::default())) - .boxed() - .shared(); - assert!(matches!( - tx_tracker - .update(&HeaderId(0, Default::default()), &all_expected_tracker_heads()) - .await, - SubmittedHeadsStatus::Final(TrackedTransactionStatus::Finalized(_)), + #[test] + fn parachain_is_not_updated_if_it_is_unavailable() { + assert!(!is_update_required::(AvailableHeader::Unavailable, None)); + assert!(!is_update_required::( + AvailableHeader::Unavailable, + Some(HeaderId(10, PARA_10_HASH)) )); } #[test] fn parachain_is_not_updated_if_it_is_unknown_to_both_clients() { - assert_eq!( - select_parachains_to_update::( - vec![(ParaId(PARA_ID), AvailableHeader::Missing)].into_iter().collect(), - vec![(ParaId(PARA_ID), None)].into_iter().collect(), - HeaderId(10, Default::default()), - ), - Vec::::new(), - ); - } - - #[test] - fn parachain_is_not_updated_if_it_has_been_updated_at_better_relay_block() { - assert_eq!( - select_parachains_to_update::( - vec![(ParaId(PARA_ID), AvailableHeader::Available(PARA_0_HASH))] - .into_iter() - .collect(), - vec![( - ParaId(PARA_ID), - Some(BestParaHeadHash { at_relay_block_number: 20, head_hash: PARA_1_HASH }) - )] - .into_iter() - .collect(), - HeaderId(10, Default::default()), - ), - Vec::::new(), - ); + assert!(!is_update_required::(AvailableHeader::Missing, None),); } #[test] - fn parachain_is_not_updated_if_hash_is_the_same_at_next_relay_block() { - assert_eq!( - select_parachains_to_update::( - vec![(ParaId(PARA_ID), AvailableHeader::Available(PARA_0_HASH))] - .into_iter() - .collect(), - vec![( - ParaId(PARA_ID), - Some(BestParaHeadHash { at_relay_block_number: 0, head_hash: PARA_0_HASH }) - )] - .into_iter() - .collect(), - HeaderId(10, Default::default()), - ), - Vec::::new(), - ); + fn parachain_is_not_updated_if_target_has_better_head() { + assert!(!is_update_required::( + AvailableHeader::Available(HeaderId(10, Default::default())), + Some(HeaderId(20, Default::default())), + ),); } #[test] fn parachain_is_updated_after_offboarding() { - assert_eq!( - select_parachains_to_update::( - vec![(ParaId(PARA_ID), AvailableHeader::Missing)].into_iter().collect(), - vec![( - ParaId(PARA_ID), - Some(BestParaHeadHash { - at_relay_block_number: 0, - head_hash: Default::default(), - }) - )] - .into_iter() - .collect(), - HeaderId(10, Default::default()), - ), - vec![ParaId(PARA_ID)], - ); + assert!(is_update_required::( + AvailableHeader::Missing, + Some(HeaderId(20, Default::default())), + ),); } #[test] fn parachain_is_updated_after_onboarding() { - assert_eq!( - select_parachains_to_update::( - vec![(ParaId(PARA_ID), AvailableHeader::Available(PARA_0_HASH))] - .into_iter() - .collect(), - vec![(ParaId(PARA_ID), None)].into_iter().collect(), - HeaderId(10, Default::default()), - ), - vec![ParaId(PARA_ID)], - ); + assert!(is_update_required::( + AvailableHeader::Available(HeaderId(30, Default::default())), + None, + ),); } #[test] fn parachain_is_updated_if_newer_head_is_known() { - assert_eq!( - select_parachains_to_update::( - vec![(ParaId(PARA_ID), AvailableHeader::Available(PARA_1_HASH))] - .into_iter() - .collect(), - vec![( - ParaId(PARA_ID), - Some(BestParaHeadHash { at_relay_block_number: 0, head_hash: PARA_0_HASH }) - )] - .into_iter() - .collect(), - HeaderId(10, Default::default()), - ), - vec![ParaId(PARA_ID)], - ); - } - - #[test] - fn parachain_is_not_updated_if_source_head_is_unavailable() { - assert_eq!( - select_parachains_to_update::( - vec![(ParaId(PARA_ID), AvailableHeader::Unavailable)].into_iter().collect(), - vec![( - ParaId(PARA_ID), - Some(BestParaHeadHash { at_relay_block_number: 0, head_hash: PARA_0_HASH }) - )] - .into_iter() - .collect(), - HeaderId(10, Default::default()), - ), - vec![], - ); - } - - #[test] - fn is_update_required_works() { - let mut sync_params = ParachainSyncParams { - parachains: vec![ParaId(PARA_ID), ParaId(PARA_1_ID)], - strategy: ParachainSyncStrategy::Any, - stall_timeout: Duration::from_secs(60), - }; - - assert!(!is_update_required(&sync_params, &[])); - assert!(is_update_required(&sync_params, &[ParaId(PARA_ID)])); - assert!(is_update_required(&sync_params, &[ParaId(PARA_ID), ParaId(PARA_1_ID)])); - - sync_params.strategy = ParachainSyncStrategy::All; - assert!(!is_update_required(&sync_params, &[])); - assert!(!is_update_required(&sync_params, &[ParaId(PARA_ID)])); - assert!(is_update_required(&sync_params, &[ParaId(PARA_ID), ParaId(PARA_1_ID)])); + assert!(is_update_required::( + AvailableHeader::Available(HeaderId(40, Default::default())), + Some(HeaderId(30, Default::default())), + ),); } } diff --git a/relays/parachains/src/parachains_loop_metrics.rs b/relays/parachains/src/parachains_loop_metrics.rs index 5df996b4ddd..8138a43b3b3 100644 --- a/relays/parachains/src/parachains_loop_metrics.rs +++ b/relays/parachains/src/parachains_loop_metrics.rs @@ -16,7 +16,7 @@ use bp_polkadot_core::parachains::ParaId; use relay_utils::{ - metrics::{metric_name, register, GaugeVec, Metric, Opts, PrometheusError, Registry, U64}, + metrics::{metric_name, register, Gauge, Metric, PrometheusError, Registry, U64}, UniqueSaturatedInto, }; @@ -24,28 +24,22 @@ use relay_utils::{ #[derive(Clone)] pub struct ParachainsLoopMetrics { /// Best parachains header numbers at the source. - best_source_block_numbers: GaugeVec, + best_source_block_numbers: Gauge, /// Best parachains header numbers at the target. - best_target_block_numbers: GaugeVec, + best_target_block_numbers: Gauge, } impl ParachainsLoopMetrics { /// Create and register parachains loop metrics. pub fn new(prefix: Option<&str>) -> Result { Ok(ParachainsLoopMetrics { - best_source_block_numbers: GaugeVec::new( - Opts::new( - metric_name(prefix, "best_parachain_block_number_at_source"), - "Best parachain block numbers at the source relay chain".to_string(), - ), - &["parachain"], + best_source_block_numbers: Gauge::new( + metric_name(prefix, "best_parachain_block_number_at_source"), + "Best parachain block numbers at the source relay chain".to_string(), )?, - best_target_block_numbers: GaugeVec::new( - Opts::new( - metric_name(prefix, "best_parachain_block_number_at_target"), - "Best parachain block numbers at the target chain".to_string(), - ), - &["parachain"], + best_target_block_numbers: Gauge::new( + metric_name(prefix, "best_parachain_block_number_at_target"), + "Best parachain block numbers at the target chain".to_string(), )?, }) } @@ -57,14 +51,13 @@ impl ParachainsLoopMetrics { block_number: Number, ) { let block_number = block_number.unique_saturated_into(); - let label = parachain_label(¶chain); log::trace!( target: "bridge-metrics", - "Updated value of metric 'best_parachain_block_number_at_source[{}]': {:?}", - label, + "Updated value of metric 'best_parachain_block_number_at_source[{:?}]': {:?}", + parachain, block_number, ); - self.best_source_block_numbers.with_label_values(&[&label]).set(block_number); + self.best_source_block_numbers.set(block_number); } /// Update best block number at target. @@ -74,14 +67,13 @@ impl ParachainsLoopMetrics { block_number: Number, ) { let block_number = block_number.unique_saturated_into(); - let label = parachain_label(¶chain); log::trace!( target: "bridge-metrics", - "Updated value of metric 'best_parachain_block_number_at_target[{}]': {:?}", - label, + "Updated value of metric 'best_parachain_block_number_at_target[{:?}]': {:?}", + parachain, block_number, ); - self.best_target_block_numbers.with_label_values(&[&label]).set(block_number); + self.best_target_block_numbers.set(block_number); } } @@ -92,8 +84,3 @@ impl Metric for ParachainsLoopMetrics { Ok(()) } } - -/// Return metric label for the parachain. -fn parachain_label(parachain: &ParaId) -> String { - format!("para_{}", parachain.0) -} diff --git a/relays/utils/Cargo.toml b/relays/utils/Cargo.toml index 1d7422d5a8c..d8b66db2abc 100644 --- a/relays/utils/Cargo.toml +++ b/relays/utils/Cargo.toml @@ -13,15 +13,15 @@ async-trait = "0.1" backoff = "0.4" isahc = "1.2" env_logger = "0.10.0" -futures = "0.3.26" +futures = "0.3.27" jsonpath_lib = "0.3" log = "0.4.17" num-traits = "0.2" serde_json = "1.0" sysinfo = "0.28" time = { version = "0.3", features = ["formatting", "local-offset", "std"] } -tokio = { version = "1.25", features = ["rt"] } -thiserror = "1.0.26" +tokio = { version = "1.26", features = ["rt"] } +thiserror = "1.0.39" # Bridge dependencies diff --git a/scripts/update-weights.sh b/scripts/update-weights.sh index ac24da62b3c..84ddf0cdaae 100755 --- a/scripts/update-weights.sh +++ b/scripts/update-weights.sh @@ -10,7 +10,7 @@ time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- --chain=dev \ --steps=50 \ --repeat=20 \ - --pallet=pallet_bridge_messages \ + --pallet=RialtoMessages \ --extrinsic=* \ --execution=wasm \ --wasm-execution=Compiled \ diff --git a/scripts/verify-pallets-build.sh b/scripts/verify-pallets-build.sh index 1f45eb51bd5..98dbe02b4b0 100755 --- a/scripts/verify-pallets-build.sh +++ b/scripts/verify-pallets-build.sh @@ -66,6 +66,7 @@ rm -rf $BRIDGES_FOLDER/bin/rialto rm -rf $BRIDGES_FOLDER/bin/rialto-parachain rm -rf $BRIDGES_FOLDER/bin/.keep rm -rf $BRIDGES_FOLDER/deployments +rm -f $BRIDGES_FOLDER/docs/dockerhub-* rm -rf $BRIDGES_FOLDER/fuzz rm -rf $BRIDGES_FOLDER/modules/beefy rm -rf $BRIDGES_FOLDER/modules/shift-session-manager @@ -88,7 +89,6 @@ rm -rf $BRIDGES_FOLDER/scripts/update_substrate.sh rm -rf $BRIDGES_FOLDER/tools rm -f $BRIDGES_FOLDER/.dockerignore rm -f $BRIDGES_FOLDER/.gitlab-ci.yml -rm -f $BRIDGES_FOLDER/Cargo.lock rm -f $BRIDGES_FOLDER/Cargo.toml rm -f $BRIDGES_FOLDER/ci.Dockerfile rm -f $BRIDGES_FOLDER/Dockerfile @@ -124,4 +124,8 @@ cargo check -p pallet-bridge-relayers --features try-runtime cargo check -p bridge-runtime-common cargo check -p bridge-runtime-common --features runtime-benchmarks +# we're removing lock file after all chechs are done. Otherwise we may use different +# Substrate/Polkadot/Cumulus commits and our checks will fail +rm -f $BRIDGES_FOLDER/Cargo.lock + echo "OK" From 1392b9df9eac34ea75d04b766206834d319591cf Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 15 Mar 2023 12:29:45 +0300 Subject: [PATCH 209/263] fixed npm install call (#2323) --- scripts/bridges_rococo_wococo.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh index 5989d98feb8..a9375b8150d 100755 --- a/scripts/bridges_rococo_wococo.sh +++ b/scripts/bridges_rococo_wococo.sh @@ -51,7 +51,6 @@ function ensure_polkadot_js_api() { pushd ./scripts/generate_hex_encoded_call npm install popd - exit 1 fi } @@ -65,11 +64,14 @@ function generate_hex_encoded_call_data() { echo "Input params: $@" node ./scripts/generate_hex_encoded_call "$type" "$endpoint" "$output" "$@" + local retVal=$? if [ $type != "check" ]; then local hex_encoded_data=$(cat $output) echo "Generated hex-encoded bytes to file '$output': $hex_encoded_data" fi + + return $retVal } STATEMINE_ACCOUNT_SEED_FOR_LOCAL="//Alice" From 9e81c9fd64101e4a1a304313677645b1607c6b98 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 15 Mar 2023 15:04:43 +0100 Subject: [PATCH 210/263] New weights (#2315) * New weights * Fix compile benchmarks * Fix import * Fix all weights --- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 30 ++- ...et_bridge_grandpa_bridge_rococo_grandpa.rs | 60 +++--- ...et_bridge_grandpa_bridge_wococo_grandpa.rs | 60 +++--- ...ith_bridge_hub_rococo_messages_instance.rs | 186 ++++++++++++------ ...ith_bridge_hub_wococo_messages_instance.rs | 186 ++++++++++++------ ...untime_bridge_parachain_rococo_instance.rs | 88 ++++++--- ...untime_bridge_parachain_wococo_instance.rs | 88 ++++++--- .../src/weights/pallet_bridge_relayers.rs | 28 +-- .../bridge-hub-rococo/src/xcm_config.rs | 8 +- .../bridge-hub-rococo/tests/tests.rs | 8 +- 10 files changed, 494 insertions(+), 248 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index fd57eba27bd..bcff038b0ec 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -977,8 +977,17 @@ impl_runtime_apis! { impl BridgeMessagesConfig for Runtime { fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { + use bridge_runtime_common::messages::MessageBridge; let bench_lane_id = >::bench_lane_id(); - pallet_bridge_relayers::Pallet::::relayer_reward(relayer, &bench_lane_id).is_some() + let bridged_chain_id = bridge_hub_rococo_config::WithBridgeHubWococoMessageBridge::BRIDGED_CHAIN_ID; + pallet_bridge_relayers::Pallet::::relayer_reward( + relayer, + bp_relayers::RewardsAccountParams::new( + bench_lane_id, + bridged_chain_id, + bp_relayers::RewardsAccountOwner::BridgedChain + ) + ).is_some() } fn prepare_message_proof( @@ -1004,8 +1013,17 @@ impl_runtime_apis! { impl BridgeMessagesConfig for Runtime { fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { + use bridge_runtime_common::messages::MessageBridge; let bench_lane_id = >::bench_lane_id(); - pallet_bridge_relayers::Pallet::::relayer_reward(relayer, &bench_lane_id).is_some() + let bridged_chain_id = bridge_hub_wococo_config::WithBridgeHubRococoMessageBridge::BRIDGED_CHAIN_ID; + pallet_bridge_relayers::Pallet::::relayer_reward( + relayer, + bp_relayers::RewardsAccountParams::new( + bench_lane_id, + bridged_chain_id, + bp_relayers::RewardsAccountOwner::BridgedChain + ) + ).is_some() } fn prepare_message_proof( @@ -1089,15 +1107,15 @@ impl_runtime_apis! { impl BridgeRelayersConfig for Runtime { fn prepare_environment( - lane: bp_messages::LaneId, + account_params: bp_relayers::RewardsAccountParams, reward: Balance, ) { use frame_support::traits::fungible::Mutate; - let lane_rewards_account = bp_relayers::PayLaneRewardFromAccount::< + let rewards_account = bp_relayers::PayRewardFromAccount::< Balances, AccountId - >::lane_rewards_account(lane); - Balances::mint_into(&lane_rewards_account, reward).unwrap(); + >::rewards_account(account_params); + Balances::mint_into(&rewards_account, reward).unwrap(); } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_rococo_grandpa.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_rococo_grandpa.rs index 9dfa44f562c..88e9c24bc58 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_rococo_grandpa.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_rococo_grandpa.rs @@ -17,25 +17,26 @@ //! Autogenerated weights for `pallet_bridge_grandpa` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-03-15, STEPS: `10`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// /home/benchbot/cargo_target_dir/production/polkadot-parachain +// ./target/production/polkadot-parachain // benchmark // pallet -// --steps=50 -// --repeat=20 +// --steps=10 +// --repeat=1 // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json -// --pallet=pallet_bridge_grandpa -// --chain=bridge-hub-rococo-dev +// --json-file=./bench.json // --header=./file_header.txt -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ +// --chain=bridge-hub-rococo-dev +// --pallet=pallet_bridge_grandpa +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,24 +48,35 @@ use sp_std::marker::PhantomData; /// Weight functions for `pallet_bridge_grandpa`. pub struct WeightInfo(PhantomData); impl pallet_bridge_grandpa::WeightInfo for WeightInfo { - // Storage: BridgeRococoGrandpa PalletOperatingMode (r:1 w:0) - // Storage: BridgeRococoGrandpa RequestCount (r:1 w:1) - // Storage: BridgeRococoGrandpa BestFinalized (r:1 w:1) - // Storage: BridgeRococoGrandpa CurrentAuthoritySet (r:1 w:0) - // Storage: BridgeRococoGrandpa ImportedHashesPointer (r:1 w:1) - // Storage: BridgeRococoGrandpa ImportedHashes (r:1 w:1) - // Storage: BridgeRococoGrandpa ImportedHeaders (r:0 w:2) - /// The range of component `p` is `[51, 102]`. + /// Storage: BridgeRococoGrandpa PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRococoGrandpa PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: BridgeRococoGrandpa RequestCount (r:1 w:1) + /// Proof: BridgeRococoGrandpa RequestCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: BridgeRococoGrandpa BestFinalized (r:1 w:1) + /// Proof: BridgeRococoGrandpa BestFinalized (max_values: Some(1), max_size: Some(36), added: 531, mode: MaxEncodedLen) + /// Storage: BridgeRococoGrandpa CurrentAuthoritySet (r:1 w:0) + /// Proof: BridgeRococoGrandpa CurrentAuthoritySet (max_values: Some(1), max_size: Some(50250), added: 50745, mode: MaxEncodedLen) + /// Storage: BridgeRococoGrandpa ImportedHashesPointer (r:1 w:1) + /// Proof: BridgeRococoGrandpa ImportedHashesPointer (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: BridgeRococoGrandpa ImportedHashes (r:1 w:1) + /// Proof: BridgeRococoGrandpa ImportedHashes (max_values: Some(1024), max_size: Some(36), added: 1521, mode: MaxEncodedLen) + /// Storage: BridgeRococoGrandpa ImportedHeaders (r:0 w:2) + /// Proof: BridgeRococoGrandpa ImportedHeaders (max_values: Some(1024), max_size: Some(68), added: 1553, mode: MaxEncodedLen) + /// The range of component `p` is `[1, 838]`. /// The range of component `v` is `[50, 100]`. - /// The range of component `p` is `[51, 102]`. + /// The range of component `p` is `[1, 838]`. /// The range of component `v` is `[50, 100]`. fn submit_finality_proof(p: u32, v: u32, ) -> Weight { - // Minimum execution time: 2_561_902 nanoseconds. - Weight::from_ref_time(86_419_230) - // Standard Error: 32_880 - .saturating_add(Weight::from_ref_time(46_938_159).saturating_mul(p.into())) - // Standard Error: 33_661 - .saturating_add(Weight::from_ref_time(1_134_973).saturating_mul(v.into())) + // Proof Size summary in bytes: + // Measured: `295 + p * (60 ±0)` + // Estimated: `60231` + // Minimum execution time: 616_404_000 picoseconds. + Weight::from_parts(616_404_000, 0) + .saturating_add(Weight::from_parts(0, 60231)) + // Standard Error: 10_615_697 + .saturating_add(Weight::from_parts(107_372_374, 0).saturating_mul(p.into())) + // Standard Error: 81_776_733 + .saturating_add(Weight::from_parts(97_333_232, 0).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(6)) } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_wococo_grandpa.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_wococo_grandpa.rs index a22269e2a7c..68265a8c489 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_wococo_grandpa.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_wococo_grandpa.rs @@ -17,25 +17,26 @@ //! Autogenerated weights for `pallet_bridge_grandpa` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-03-15, STEPS: `10`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// /home/benchbot/cargo_target_dir/production/polkadot-parachain +// ./target/production/polkadot-parachain // benchmark // pallet -// --steps=50 -// --repeat=20 +// --steps=10 +// --repeat=1 // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json -// --pallet=pallet_bridge_grandpa -// --chain=bridge-hub-rococo-dev +// --json-file=./bench.json // --header=./file_header.txt -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ +// --chain=bridge-hub-rococo-dev +// --pallet=pallet_bridge_grandpa +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,24 +48,33 @@ use sp_std::marker::PhantomData; /// Weight functions for `pallet_bridge_grandpa`. pub struct WeightInfo(PhantomData); impl pallet_bridge_grandpa::WeightInfo for WeightInfo { - // Storage: BridgeWococoGrandpa PalletOperatingMode (r:1 w:0) - // Storage: BridgeWococoGrandpa RequestCount (r:1 w:1) - // Storage: BridgeWococoGrandpa BestFinalized (r:1 w:1) - // Storage: BridgeWococoGrandpa CurrentAuthoritySet (r:1 w:0) - // Storage: BridgeWococoGrandpa ImportedHashesPointer (r:1 w:1) - // Storage: BridgeWococoGrandpa ImportedHashes (r:1 w:1) - // Storage: BridgeWococoGrandpa ImportedHeaders (r:0 w:2) - /// The range of component `p` is `[51, 102]`. + /// Storage: BridgeWococoGrandpa PalletOperatingMode (r:1 w:0) + /// Proof: BridgeWococoGrandpa PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: BridgeWococoGrandpa RequestCount (r:1 w:1) + /// Proof: BridgeWococoGrandpa RequestCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: BridgeWococoGrandpa BestFinalized (r:1 w:1) + /// Proof: BridgeWococoGrandpa BestFinalized (max_values: Some(1), max_size: Some(36), added: 531, mode: MaxEncodedLen) + /// Storage: BridgeWococoGrandpa CurrentAuthoritySet (r:1 w:0) + /// Proof: BridgeWococoGrandpa CurrentAuthoritySet (max_values: Some(1), max_size: Some(50250), added: 50745, mode: MaxEncodedLen) + /// Storage: BridgeWococoGrandpa ImportedHashesPointer (r:1 w:1) + /// Proof: BridgeWococoGrandpa ImportedHashesPointer (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: BridgeWococoGrandpa ImportedHashes (r:1 w:1) + /// Proof: BridgeWococoGrandpa ImportedHashes (max_values: Some(1024), max_size: Some(36), added: 1521, mode: MaxEncodedLen) + /// Storage: BridgeWococoGrandpa ImportedHeaders (r:0 w:2) + /// Proof: BridgeWococoGrandpa ImportedHeaders (max_values: Some(1024), max_size: Some(68), added: 1553, mode: MaxEncodedLen) + /// The range of component `p` is `[1, 838]`. /// The range of component `v` is `[50, 100]`. - /// The range of component `p` is `[51, 102]`. + /// The range of component `p` is `[1, 838]`. /// The range of component `v` is `[50, 100]`. - fn submit_finality_proof(p: u32, v: u32, ) -> Weight { - // Minimum execution time: 2_565_169 nanoseconds. - Weight::from_ref_time(67_834_502) - // Standard Error: 35_710 - .saturating_add(Weight::from_ref_time(47_043_118).saturating_mul(p.into())) - // Standard Error: 36_557 - .saturating_add(Weight::from_ref_time(1_301_551).saturating_mul(v.into())) + fn submit_finality_proof(p: u32, _v: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `332 + p * (60 ±0)` + // Estimated: `60231` + // Minimum execution time: 672_054_000 picoseconds. + Weight::from_parts(47_309_240_121, 0) + .saturating_add(Weight::from_parts(0, 60231)) + // Standard Error: 16_649_480 + .saturating_add(Weight::from_parts(98_202_871, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(6)) } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs index 65a1858a231..14f7e85227b 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs @@ -17,25 +17,26 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `runner-b3zmxxc-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// target/production/polkadot-parachain +// ./target/production/polkadot-parachain // benchmark // pallet // --steps=50 -// --repeat=20 +// --repeat=2 // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/cumulus/.git/.artifacts/bench.json -// --pallet=pallet_bridge_messages -// --chain=bridge-hub-rococo-dev +// --json-file=./bench.json // --header=./file_header.txt -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ +// --chain=bridge-hub-rococo-dev +// --pallet=pallet_bridge_messages +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,79 +48,150 @@ use sp_std::marker::PhantomData; /// Weight functions for `pallet_bridge_messages`. pub struct WeightInfo(PhantomData); impl pallet_bridge_messages::WeightInfo for WeightInfo { - // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeRococoMessages InboundLanes (r:1 w:1) - // Storage: ParachainInfo ParachainId (r:1 w:0) + /// Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeRococoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn receive_single_message_proof() -> Weight { - // Minimum execution time: 48_515 nanoseconds. - Weight::from_ref_time(50_641_000) + // Proof Size summary in bytes: + // Measured: `430` + // Estimated: `57797` + // Minimum execution time: 43_647_000 picoseconds. + Weight::from_parts(78_914_000, 0) + .saturating_add(Weight::from_parts(0, 57797)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeRococoMessages InboundLanes (r:1 w:1) - // Storage: ParachainInfo ParachainId (r:1 w:0) + /// Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeRococoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn receive_two_messages_proof() -> Weight { - // Minimum execution time: 62_891 nanoseconds. - Weight::from_ref_time(66_039_000) + // Proof Size summary in bytes: + // Measured: `430` + // Estimated: `57797` + // Minimum execution time: 62_322_000 picoseconds. + Weight::from_parts(75_283_000, 0) + .saturating_add(Weight::from_parts(0, 57797)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeRococoMessages InboundLanes (r:1 w:1) - // Storage: ParachainInfo ParachainId (r:1 w:0) + /// Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeRococoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - // Minimum execution time: 53_010 nanoseconds. - Weight::from_ref_time(53_855_000) + // Proof Size summary in bytes: + // Measured: `430` + // Estimated: `57797` + // Minimum execution time: 47_917_000 picoseconds. + Weight::from_parts(53_246_000, 0) + .saturating_add(Weight::from_parts(0, 57797)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeRococoMessages InboundLanes (r:1 w:1) + /// Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeRococoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) fn receive_single_message_proof_1_kb() -> Weight { - // Minimum execution time: 50_491 nanoseconds. - Weight::from_ref_time(52_570_000) + // Proof Size summary in bytes: + // Measured: `398` + // Estimated: `56308` + // Minimum execution time: 42_265_000 picoseconds. + Weight::from_parts(50_221_000, 0) + .saturating_add(Weight::from_parts(0, 56308)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeRococoMessages InboundLanes (r:1 w:1) + /// Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeRococoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) fn receive_single_message_proof_16_kb() -> Weight { - // Minimum execution time: 116_072 nanoseconds. - Weight::from_ref_time(119_185_000) + // Proof Size summary in bytes: + // Measured: `398` + // Estimated: `56308` + // Minimum execution time: 70_236_000 picoseconds. + Weight::from_parts(107_444_000, 0) + .saturating_add(Weight::from_parts(0, 56308)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeRococoMessages OutboundLanes (r:1 w:1) + /// Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeRococoMessages OutboundLanes (r:1 w:1) + /// Proof: BridgeRococoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: 539, mode: MaxEncodedLen) + /// Storage: unknown `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) + /// Proof Skipped: unknown `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn receive_delivery_proof_for_single_message() -> Weight { - // Minimum execution time: 31_075 nanoseconds. - Weight::from_ref_time(32_055_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `370` + // Estimated: `12565` + // Minimum execution time: 34_072_000 picoseconds. + Weight::from_parts(54_240_000, 0) + .saturating_add(Weight::from_parts(0, 12565)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeRococoMessages OutboundLanes (r:1 w:1) + /// Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeRococoMessages OutboundLanes (r:1 w:1) + /// Proof: BridgeRococoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: 539, mode: MaxEncodedLen) + /// Storage: unknown `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) + /// Proof Skipped: unknown `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - // Minimum execution time: 31_524 nanoseconds. - Weight::from_ref_time(32_149_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `370` + // Estimated: `12565` + // Minimum execution time: 34_013_000 picoseconds. + Weight::from_parts(50_732_000, 0) + .saturating_add(Weight::from_parts(0, 12565)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeRococoMessages OutboundLanes (r:1 w:1) + /// Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeRococoMessages OutboundLanes (r:1 w:1) + /// Proof: BridgeRococoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: 539, mode: MaxEncodedLen) + /// Storage: unknown `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) + /// Proof Skipped: unknown `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) + /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - // Minimum execution time: 31_723 nanoseconds. - Weight::from_ref_time(32_355_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `370` + // Estimated: `15113` + // Minimum execution time: 36_242_000 picoseconds. + Weight::from_parts(57_344_000, 0) + .saturating_add(Weight::from_parts(0, 15113)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(3)) } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs index eaff561d629..23d74f6feb7 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs @@ -17,25 +17,26 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-25, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `runner-b3zmxxc-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// target/production/polkadot-parachain +// ./target/production/polkadot-parachain // benchmark // pallet // --steps=50 -// --repeat=20 +// --repeat=2 // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/cumulus/.git/.artifacts/bench.json -// --pallet=pallet_bridge_messages -// --chain=bridge-hub-rococo-dev +// --json-file=./bench.json // --header=./file_header.txt -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ +// --chain=bridge-hub-rococo-dev +// --pallet=pallet_bridge_messages +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,79 +48,150 @@ use sp_std::marker::PhantomData; /// Weight functions for `pallet_bridge_messages`. pub struct WeightInfo(PhantomData); impl pallet_bridge_messages::WeightInfo for WeightInfo { - // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeWococoMessages InboundLanes (r:1 w:1) - // Storage: ParachainInfo ParachainId (r:1 w:0) + /// Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeWococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeWococoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeWococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn receive_single_message_proof() -> Weight { - // Minimum execution time: 49_348 nanoseconds. - Weight::from_ref_time(52_687_000) + // Proof Size summary in bytes: + // Measured: `467` + // Estimated: `57797` + // Minimum execution time: 46_035_000 picoseconds. + Weight::from_parts(87_082_000, 0) + .saturating_add(Weight::from_parts(0, 57797)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeWococoMessages InboundLanes (r:1 w:1) - // Storage: ParachainInfo ParachainId (r:1 w:0) + /// Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeWococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeWococoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeWococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn receive_two_messages_proof() -> Weight { - // Minimum execution time: 63_755 nanoseconds. - Weight::from_ref_time(67_615_000) + // Proof Size summary in bytes: + // Measured: `467` + // Estimated: `57797` + // Minimum execution time: 69_379_000 picoseconds. + Weight::from_parts(80_326_000, 0) + .saturating_add(Weight::from_parts(0, 57797)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeWococoMessages InboundLanes (r:1 w:1) - // Storage: ParachainInfo ParachainId (r:1 w:0) + /// Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeWococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeWococoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeWococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - // Minimum execution time: 54_597 nanoseconds. - Weight::from_ref_time(56_472_000) + // Proof Size summary in bytes: + // Measured: `467` + // Estimated: `57797` + // Minimum execution time: 51_877_000 picoseconds. + Weight::from_parts(61_053_000, 0) + .saturating_add(Weight::from_parts(0, 57797)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeWococoMessages InboundLanes (r:1 w:1) + /// Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeWococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeWococoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeWococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) fn receive_single_message_proof_1_kb() -> Weight { - // Minimum execution time: 51_363 nanoseconds. - Weight::from_ref_time(54_025_000) + // Proof Size summary in bytes: + // Measured: `435` + // Estimated: `56308` + // Minimum execution time: 44_882_000 picoseconds. + Weight::from_parts(53_124_000, 0) + .saturating_add(Weight::from_parts(0, 56308)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeWococoMessages InboundLanes (r:1 w:1) + /// Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeWococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeWococoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeWococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) fn receive_single_message_proof_16_kb() -> Weight { - // Minimum execution time: 119_727 nanoseconds. - Weight::from_ref_time(123_138_000) + // Proof Size summary in bytes: + // Measured: `435` + // Estimated: `56308` + // Minimum execution time: 74_426_000 picoseconds. + Weight::from_parts(111_901_000, 0) + .saturating_add(Weight::from_parts(0, 56308)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeWococoMessages OutboundLanes (r:1 w:1) + /// Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeWococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeWococoMessages OutboundLanes (r:1 w:1) + /// Proof: BridgeWococoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: 539, mode: MaxEncodedLen) + /// Storage: unknown `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) + /// Proof Skipped: unknown `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn receive_delivery_proof_for_single_message() -> Weight { - // Minimum execution time: 32_525 nanoseconds. - Weight::from_ref_time(33_410_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `407` + // Estimated: `12602` + // Minimum execution time: 35_466_000 picoseconds. + Weight::from_parts(59_081_000, 0) + .saturating_add(Weight::from_parts(0, 12602)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeWococoMessages OutboundLanes (r:1 w:1) + /// Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeWococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeWococoMessages OutboundLanes (r:1 w:1) + /// Proof: BridgeWococoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: 539, mode: MaxEncodedLen) + /// Storage: unknown `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) + /// Proof Skipped: unknown `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - // Minimum execution time: 32_310 nanoseconds. - Weight::from_ref_time(33_208_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `407` + // Estimated: `12602` + // Minimum execution time: 35_889_000 picoseconds. + Weight::from_parts(48_281_000, 0) + .saturating_add(Weight::from_parts(0, 12602)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) } - // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) - // Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) - // Storage: BridgeWococoMessages OutboundLanes (r:1 w:1) + /// Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeWococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeWococoMessages OutboundLanes (r:1 w:1) + /// Proof: BridgeWococoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: 539, mode: MaxEncodedLen) + /// Storage: unknown `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) + /// Proof Skipped: unknown `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) + /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - // Minimum execution time: 32_594 nanoseconds. - Weight::from_ref_time(33_449_000) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) + // Proof Size summary in bytes: + // Measured: `407` + // Estimated: `15150` + // Minimum execution time: 37_430_000 picoseconds. + Weight::from_parts(54_994_000, 0) + .saturating_add(Weight::from_parts(0, 15150)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(3)) } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance.rs index 056617988d5..410bd211af9 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance.rs @@ -17,12 +17,13 @@ //! Autogenerated weights for `pallet_bridge_parachains` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-23, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// /home/benchbot/cargo_target_dir/production/polkadot-parachain +// ./target/production/polkadot-parachain // benchmark // pallet // --steps=50 @@ -31,11 +32,11 @@ // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json -// --pallet=pallet_bridge_parachains -// --chain=bridge-hub-rococo-dev +// --json-file=./bench.json // --header=./file_header.txt -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ +// --chain=bridge-hub-rococo-dev +// --pallet=pallet_bridge_parachains +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,38 +48,67 @@ use sp_std::marker::PhantomData; /// Weight functions for `pallet_bridge_parachains`. pub struct WeightInfo(PhantomData); impl pallet_bridge_parachains::WeightInfo for WeightInfo { - // Storage: BridgeRococoParachain PalletOperatingMode (r:1 w:0) - // Storage: BridgeRococoGrandpa ImportedHeaders (r:1 w:0) - // Storage: BridgeRococoParachain ParasInfo (r:1 w:1) - // Storage: BridgeRococoParachain ImportedParaHashes (r:1 w:1) - // Storage: BridgeRococoParachain ImportedParaHeads (r:0 w:1) + /// Storage: BridgeRococoParachain PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRococoParachain PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: BridgeRococoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeRococoGrandpa ImportedHeaders (max_values: Some(1024), max_size: Some(68), added: 1553, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ParasInfo (r:1 w:1) + /// Proof: BridgeRococoParachain ParasInfo (max_values: Some(1), max_size: Some(60), added: 555, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHashes (r:1 w:1) + /// Proof: BridgeRococoParachain ImportedParaHashes (max_values: Some(64), max_size: Some(64), added: 1054, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHeads (r:0 w:1) + /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) /// The range of component `p` is `[1, 2]`. /// The range of component `p` is `[1, 2]`. - fn submit_parachain_heads_with_n_parachains(_p: u32, ) -> Weight { - // Minimum execution time: 34_955 nanoseconds. - Weight::from_ref_time(36_400_062) + fn submit_parachain_heads_with_n_parachains(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `326` + // Estimated: `7618` + // Minimum execution time: 43_640_000 picoseconds. + Weight::from_parts(70_049_694, 0) + .saturating_add(Weight::from_parts(0, 7618)) + // Standard Error: 651_207 + .saturating_add(Weight::from_parts(7_744_677, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: BridgeRococoParachain PalletOperatingMode (r:1 w:0) - // Storage: BridgeRococoGrandpa ImportedHeaders (r:1 w:0) - // Storage: BridgeRococoParachain ParasInfo (r:1 w:1) - // Storage: BridgeRococoParachain ImportedParaHashes (r:1 w:1) - // Storage: BridgeRococoParachain ImportedParaHeads (r:0 w:1) + /// Storage: BridgeRococoParachain PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRococoParachain PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: BridgeRococoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeRococoGrandpa ImportedHeaders (max_values: Some(1024), max_size: Some(68), added: 1553, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ParasInfo (r:1 w:1) + /// Proof: BridgeRococoParachain ParasInfo (max_values: Some(1), max_size: Some(60), added: 555, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHashes (r:1 w:1) + /// Proof: BridgeRococoParachain ImportedParaHashes (max_values: Some(64), max_size: Some(64), added: 1054, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHeads (r:0 w:1) + /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) fn submit_parachain_heads_with_1kb_proof() -> Weight { - // Minimum execution time: 44_024 nanoseconds. - Weight::from_ref_time(44_604_000) + // Proof Size summary in bytes: + // Measured: `326` + // Estimated: `7618` + // Minimum execution time: 69_524_000 picoseconds. + Weight::from_parts(83_696_000, 0) + .saturating_add(Weight::from_parts(0, 7618)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: BridgeRococoParachain PalletOperatingMode (r:1 w:0) - // Storage: BridgeRococoGrandpa ImportedHeaders (r:1 w:0) - // Storage: BridgeRococoParachain ParasInfo (r:1 w:1) - // Storage: BridgeRococoParachain ImportedParaHashes (r:1 w:1) - // Storage: BridgeRococoParachain ImportedParaHeads (r:0 w:1) + /// Storage: BridgeRococoParachain PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRococoParachain PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: BridgeRococoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeRococoGrandpa ImportedHeaders (max_values: Some(1024), max_size: Some(68), added: 1553, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ParasInfo (r:1 w:1) + /// Proof: BridgeRococoParachain ParasInfo (max_values: Some(1), max_size: Some(60), added: 555, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHashes (r:1 w:1) + /// Proof: BridgeRococoParachain ImportedParaHashes (max_values: Some(64), max_size: Some(64), added: 1054, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHeads (r:0 w:1) + /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) fn submit_parachain_heads_with_16kb_proof() -> Weight { - // Minimum execution time: 96_346 nanoseconds. - Weight::from_ref_time(98_207_000) + // Proof Size summary in bytes: + // Measured: `326` + // Estimated: `7618` + // Minimum execution time: 126_315_000 picoseconds. + Weight::from_parts(147_026_000, 0) + .saturating_add(Weight::from_parts(0, 7618)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance.rs index 6a4b86a6def..b02641ab918 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance.rs @@ -17,12 +17,13 @@ //! Autogenerated weights for `pallet_bridge_parachains` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-01-23, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// /home/benchbot/cargo_target_dir/production/polkadot-parachain +// ./target/production/polkadot-parachain // benchmark // pallet // --steps=50 @@ -31,11 +32,11 @@ // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json -// --pallet=pallet_bridge_parachains -// --chain=bridge-hub-rococo-dev +// --json-file=./bench.json // --header=./file_header.txt -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ +// --chain=bridge-hub-rococo-dev +// --pallet=pallet_bridge_parachains +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -47,40 +48,67 @@ use sp_std::marker::PhantomData; /// Weight functions for `pallet_bridge_parachains`. pub struct WeightInfo(PhantomData); impl pallet_bridge_parachains::WeightInfo for WeightInfo { - // Storage: BridgeWococoParachain PalletOperatingMode (r:1 w:0) - // Storage: BridgeWococoGrandpa ImportedHeaders (r:1 w:0) - // Storage: BridgeWococoParachain ParasInfo (r:1 w:1) - // Storage: BridgeWococoParachain ImportedParaHashes (r:1 w:1) - // Storage: BridgeWococoParachain ImportedParaHeads (r:0 w:1) + /// Storage: BridgeWococoParachain PalletOperatingMode (r:1 w:0) + /// Proof: BridgeWococoParachain PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: BridgeWococoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeWococoGrandpa ImportedHeaders (max_values: Some(1024), max_size: Some(68), added: 1553, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ParasInfo (r:1 w:1) + /// Proof: BridgeWococoParachain ParasInfo (max_values: Some(1), max_size: Some(60), added: 555, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHashes (r:1 w:1) + /// Proof: BridgeWococoParachain ImportedParaHashes (max_values: Some(64), max_size: Some(64), added: 1054, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHeads (r:0 w:1) + /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) /// The range of component `p` is `[1, 2]`. /// The range of component `p` is `[1, 2]`. fn submit_parachain_heads_with_n_parachains(p: u32, ) -> Weight { - // Minimum execution time: 36_411 nanoseconds. - Weight::from_ref_time(37_604_452) - // Standard Error: 57_620 - .saturating_add(Weight::from_ref_time(248_648).saturating_mul(p.into())) + // Proof Size summary in bytes: + // Measured: `399` + // Estimated: `7618` + // Minimum execution time: 47_436_000 picoseconds. + Weight::from_parts(75_670_977, 0) + .saturating_add(Weight::from_parts(0, 7618)) + // Standard Error: 724_179 + .saturating_add(Weight::from_parts(5_669_961, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: BridgeWococoParachain PalletOperatingMode (r:1 w:0) - // Storage: BridgeWococoGrandpa ImportedHeaders (r:1 w:0) - // Storage: BridgeWococoParachain ParasInfo (r:1 w:1) - // Storage: BridgeWococoParachain ImportedParaHashes (r:1 w:1) - // Storage: BridgeWococoParachain ImportedParaHeads (r:0 w:1) + /// Storage: BridgeWococoParachain PalletOperatingMode (r:1 w:0) + /// Proof: BridgeWococoParachain PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: BridgeWococoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeWococoGrandpa ImportedHeaders (max_values: Some(1024), max_size: Some(68), added: 1553, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ParasInfo (r:1 w:1) + /// Proof: BridgeWococoParachain ParasInfo (max_values: Some(1), max_size: Some(60), added: 555, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHashes (r:1 w:1) + /// Proof: BridgeWococoParachain ImportedParaHashes (max_values: Some(64), max_size: Some(64), added: 1054, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHeads (r:0 w:1) + /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) fn submit_parachain_heads_with_1kb_proof() -> Weight { - // Minimum execution time: 45_107 nanoseconds. - Weight::from_ref_time(45_916_000) + // Proof Size summary in bytes: + // Measured: `399` + // Estimated: `7618` + // Minimum execution time: 70_961_000 picoseconds. + Weight::from_parts(83_138_000, 0) + .saturating_add(Weight::from_parts(0, 7618)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } - // Storage: BridgeWococoParachain PalletOperatingMode (r:1 w:0) - // Storage: BridgeWococoGrandpa ImportedHeaders (r:1 w:0) - // Storage: BridgeWococoParachain ParasInfo (r:1 w:1) - // Storage: BridgeWococoParachain ImportedParaHashes (r:1 w:1) - // Storage: BridgeWococoParachain ImportedParaHeads (r:0 w:1) + /// Storage: BridgeWococoParachain PalletOperatingMode (r:1 w:0) + /// Proof: BridgeWococoParachain PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: BridgeWococoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeWococoGrandpa ImportedHeaders (max_values: Some(1024), max_size: Some(68), added: 1553, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ParasInfo (r:1 w:1) + /// Proof: BridgeWococoParachain ParasInfo (max_values: Some(1), max_size: Some(60), added: 555, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHashes (r:1 w:1) + /// Proof: BridgeWococoParachain ImportedParaHashes (max_values: Some(64), max_size: Some(64), added: 1054, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHeads (r:0 w:1) + /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) fn submit_parachain_heads_with_16kb_proof() -> Weight { - // Minimum execution time: 97_738 nanoseconds. - Weight::from_ref_time(100_381_000) + // Proof Size summary in bytes: + // Measured: `399` + // Estimated: `7618` + // Minimum execution time: 122_639_000 picoseconds. + Weight::from_parts(142_091_000, 0) + .saturating_add(Weight::from_parts(0, 7618)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs index c81dfbc14c8..3959e424c26 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs @@ -17,26 +17,26 @@ //! Autogenerated weights for `pallet_bridge_relayers` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-osnnfcqu-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// target/production/polkadot-parachain +// ./target/production/polkadot-parachain // benchmark // pallet // --steps=50 -// --repeat=20 +// --repeat=2 // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/cumulus/.git/.artifacts/bench.json -// --pallet=pallet_bridge_relayers -// --chain=bridge-hub-rococo-dev +// --json-file=./bench.json // --header=./file_header.txt -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ +// --chain=bridge-hub-rococo-dev +// --pallet=pallet_bridge_relayers +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -49,16 +49,16 @@ use sp_std::marker::PhantomData; pub struct WeightInfo(PhantomData); impl pallet_bridge_relayers::WeightInfo for WeightInfo { /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn claim_rewards() -> Weight { // Proof Size summary in bytes: - // Measured: `271` - // Estimated: `5146` - // Minimum execution time: 37_598 nanoseconds. - Weight::from_ref_time(38_726_000) - .saturating_add(Weight::from_proof_size(5146)) + // Measured: `239` + // Estimated: `7131` + // Minimum execution time: 37_638_000 picoseconds. + Weight::from_parts(43_222_000, 0) + .saturating_add(Weight::from_parts(0, 7131)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 646fcbfb3c7..571e2075cb8 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -176,11 +176,15 @@ impl Contains for SafeCallFilter { RuntimeCall::BridgeRococoGrandpa(pallet_bridge_grandpa::Call::< Runtime, BridgeGrandpaRococoInstance, - >::initialize { .. }) | + >::initialize { + .. + }) | RuntimeCall::BridgeWococoGrandpa(pallet_bridge_grandpa::Call::< Runtime, BridgeGrandpaWococoInstance, - >::initialize { .. }) => true, + >::initialize { + .. + }) => true, _ => false, } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index da3e6634434..a759c905df8 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -196,7 +196,7 @@ fn can_govornance_call_xcm_transact_with_initialize_on_bridge_hub_rococo() { UnpaidExecution { weight_limit: Unlimited, check_origin: None }, Transact { origin_kind: OriginKind::Superuser, - require_weight_at_most: Weight::from_ref_time(1000000000), + require_weight_at_most: Weight::from_parts(1000000000, 0), call: initialize_call.encode().into(), }, ]); @@ -212,7 +212,7 @@ fn can_govornance_call_xcm_transact_with_initialize_on_bridge_hub_rococo() { // initialize bridge through governance-like let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - let weight_limit = Weight::from_ref_time(41666666666); + let weight_limit = Weight::from_parts(41666666666, 0); let outcome = XcmExecutor::::execute_xcm(origin, xcm, hash, weight_limit); // check mode after @@ -238,7 +238,7 @@ fn can_govornance_call_xcm_transact_with_initialize_bridge_on_bridge_hub_wococo( UnpaidExecution { weight_limit: Unlimited, check_origin: None }, Transact { origin_kind: OriginKind::Superuser, - require_weight_at_most: Weight::from_ref_time(1000000000), + require_weight_at_most: Weight::from_parts(1000000000, 0), call: initialize_call.encode().into(), }, ]); @@ -254,7 +254,7 @@ fn can_govornance_call_xcm_transact_with_initialize_bridge_on_bridge_hub_wococo( // initialize bridge through governance-like let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - let weight_limit = Weight::from_ref_time(41666666666); + let weight_limit = Weight::from_parts(41666666666, 0); let outcome = XcmExecutor::::execute_xcm(origin, xcm, hash, weight_limit); // check mode after From bf98c88db08e6bc807e9a86b5674b46f462f40fe Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 15 Mar 2023 15:48:59 +0100 Subject: [PATCH 211/263] Remove bridge_common_config replaced by bridges impl --- .../src/bridge_common_config.rs | 161 ------------------ .../src/bridge_hub_rococo_config.rs | 3 +- .../src/bridge_hub_wococo_config.rs | 3 +- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 11 +- .../bridge-hub-rococo/src/xcm_config.rs | 21 +++ .../bridge-hub-rococo/tests/tests.rs | 2 +- 6 files changed, 32 insertions(+), 169 deletions(-) delete mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs deleted file mode 100644 index 708d6020eeb..00000000000 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2022 Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus 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. - -// Cumulus 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 Cumulus. If not, see . - -use bp_messages::{ - source_chain::MessagesBridge, - target_chain::{DispatchMessage, MessageDispatch}, - LaneId, -}; -use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, Chain}; -use codec::{Decode, Encode}; -use frame_support::{dispatch::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; -use scale_info::TypeInfo; -use xcm_builder::{DispatchBlob, DispatchBlobError, HaulBlob, HaulBlobError}; - -/// PLain "XCM" payload, which we transfer through bridge -pub type XcmAsPlainPayload = sp_std::prelude::Vec; - -/// Message dispatch result type for single message -#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] -pub enum XcmBlobMessageDispatchResult { - InvalidPayload, - Dispatched, - NotDispatched(#[codec(skip)] &'static str), -} - -/// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages from other BridgeHub -pub struct XcmBlobMessageDispatch { - _marker: - sp_std::marker::PhantomData<(SourceBridgeHubChain, TargetBridgeHubChain, DispatchBlob)>, -} - -impl - MessageDispatch> - for XcmBlobMessageDispatch -{ - type DispatchPayload = XcmAsPlainPayload; - type DispatchLevelResult = XcmBlobMessageDispatchResult; - - fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { - log::error!( - target: crate::LOG_TARGET, - "[XcmBlobMessageDispatch] TODO: change here to XCMv3 dispatch_weight with XcmExecutor - message: ?...?", - ); - // TODO:check-parameter - setup weight? - Weight::zero() - } - - fn dispatch( - _relayer_account: &AccountIdOf, - message: DispatchMessage, - ) -> MessageDispatchResult { - let payload = match message.data.payload { - Ok(payload) => payload, - Err(e) => { - log::error!( - target: crate::LOG_TARGET, - "[XcmBlobMessageDispatch] payload error: {:?} - message_nonce: {:?}", - e, - message.key.nonce - ); - return MessageDispatchResult { - // TODO:check-parameter - setup uspent_weight? - unspent_weight: Weight::zero(), - dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, - } - }, - }; - let dispatch_level_result = match BlobDispatcher::dispatch_blob(payload) { - Ok(_) => { - log::debug!( - target: crate::LOG_TARGET, - "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}", - message.key.nonce - ); - XcmBlobMessageDispatchResult::Dispatched - }, - Err(e) => { - let e = match e { - DispatchBlobError::Unbridgable => "DispatchBlobError::Unbridgable", - DispatchBlobError::InvalidEncoding => "DispatchBlobError::InvalidEncoding", - DispatchBlobError::UnsupportedLocationVersion => - "DispatchBlobError::UnsupportedLocationVersion", - DispatchBlobError::UnsupportedXcmVersion => - "DispatchBlobError::UnsupportedXcmVersion", - DispatchBlobError::RoutingError => "DispatchBlobError::RoutingError", - DispatchBlobError::NonUniversalDestination => - "DispatchBlobError::NonUniversalDestination", - DispatchBlobError::WrongGlobal => "DispatchBlobError::WrongGlobal", - }; - log::error!( - target: crate::LOG_TARGET, - "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?} - message_nonce: {:?}", - e, message.key.nonce - ); - XcmBlobMessageDispatchResult::NotDispatched(e) - }, - }; - MessageDispatchResult { - // TODO:check-parameter - setup uspent_weight? - unspent_weight: Weight::zero(), - dispatch_level_result, - } - } -} - -/// [`XcmBlobHauler`] is responsible for sending messages to the bridge "point-to-point link" from one side, -/// where on the other it can be dispatched by [`XcmBlobMessageDispatch`]. -pub trait XcmBlobHauler { - /// Runtime message sender adapter. - type MessageSender: MessagesBridge; - - /// Runtime message sender origin, which is used by MessageSender. - type MessageSenderOrigin; - /// Our location within the Consensus Universe. - fn message_sender_origin() -> Self::MessageSenderOrigin; - - /// Return message lane (as "point-to-point link") used to deliver XCM messages. - fn xcm_lane() -> LaneId; -} - -pub struct XcmBlobHaulerAdapter(sp_std::marker::PhantomData); -impl> HaulBlob - for XcmBlobHaulerAdapter -{ - fn haul_blob(blob: sp_std::prelude::Vec) -> Result<(), HaulBlobError> { - let lane = H::xcm_lane(); - let result = H::MessageSender::send_message(H::message_sender_origin(), lane, blob); - let result = result.map(|artifacts| { - let hash = (lane, artifacts.nonce).using_encoded(sp_io::hashing::blake2_256); - hash - }); - match &result { - Ok(result) => log::info!( - target: crate::LOG_TARGET, - "haul_blob result - ok: {:?} on lane: {:?}", - result, - lane - ), - Err(error) => log::error!( - target: crate::LOG_TARGET, - "haul_blob result - error: {:?} on lane: {:?}", - error, - lane - ), - }; - result.map(|_| ()).map_err(|_| HaulBlobError::Transport("MessageSenderError")) - } -} diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index 9df6d5a782d..97368ebfad0 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -18,7 +18,7 @@ use crate::{ BridgeParachainWococoInstance, ParachainInfo, Runtime, WithBridgeHubWococoMessagesInstance, - XcmBlobHauler, XcmBlobHaulerAdapter, XcmRouter, + XcmRouter, }; use bp_messages::{LaneId, MessageNonce}; use bp_runtime::ChainId; @@ -28,6 +28,7 @@ use bridge_runtime_common::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, MessageBridge, ThisChainWithMessages, UnderlyingChainProvider, }, + messages_xcm_extension::{XcmBlobHauler, XcmBlobHaulerAdapter}, refund_relayer_extension::{ ActualFeeRefund, RefundBridgedParachainMessages, RefundableMessagesLane, RefundableParachain, diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 3c574c9040f..d5c21243169 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -18,7 +18,7 @@ use crate::{ BridgeParachainRococoInstance, ParachainInfo, Runtime, WithBridgeHubRococoMessagesInstance, - XcmBlobHauler, XcmBlobHaulerAdapter, XcmRouter, + XcmRouter, }; use bp_messages::{LaneId, MessageNonce}; use bp_runtime::ChainId; @@ -28,6 +28,7 @@ use bridge_runtime_common::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, MessageBridge, ThisChainWithMessages, UnderlyingChainProvider, }, + messages_xcm_extension::{XcmBlobHauler, XcmBlobHaulerAdapter}, refund_relayer_extension::{ ActualFeeRefund, RefundBridgedParachainMessages, RefundableMessagesLane, RefundableParachain, diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index bcff038b0ec..fb95c47b76c 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -22,14 +22,12 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -pub mod bridge_common_config; pub mod bridge_hub_rococo_config; pub mod bridge_hub_wococo_config; pub mod constants; mod weights; pub mod xcm_config; -use bridge_common_config::*; use constants::currency::*; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; use sp_api::impl_runtime_apis; @@ -82,10 +80,11 @@ use crate::{ WithBridgeHubRococoMessageBridge, }, constants::fee::WeightToFee, - xcm_config::XcmRouter, + xcm_config::{XcmRouter, XcmRouterWeigher}, }; -use bridge_runtime_common::messages::{ - source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, +use bridge_runtime_common::{ + messages::{source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter}, + messages_xcm_extension::{XcmAsPlainPayload, XcmBlobMessageDispatch}, }; use parachains_common::{ opaque, AccountId, Balance, BlockNumber, Hash, Header, Index, Signature, @@ -487,6 +486,7 @@ impl pallet_bridge_messages::Config for Run bp_bridge_hub_wococo::BridgeHubWococo, bp_bridge_hub_rococo::BridgeHubRococo, OnBridgeHubRococoBlobDispatcher, + XcmRouterWeigher, >; } @@ -523,6 +523,7 @@ impl pallet_bridge_messages::Config for Run bp_bridge_hub_rococo::BridgeHubRococo, bp_bridge_hub_wococo::BridgeHubWococo, OnBridgeHubWococoBlobDispatcher, + XcmRouterWeigher, >; } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 571e2075cb8..bae5aea0a84 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -26,6 +26,7 @@ use crate::{ use frame_support::{ match_types, parameter_types, traits::{ConstU32, Contains, Everything, Nothing}, + weights::Weight, }; use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; @@ -44,6 +45,7 @@ use xcm_executor::{ XcmExecutor, }; +use crate::weights::RocksDbWeight; use parachains_common::xcm_config::{ ConcreteNativeAssetFrom, DenyReserveTransferToRelayChain, DenyThenTry, }; @@ -262,6 +264,25 @@ pub type XcmRouter = ( XcmpQueue, ); +/// Simple weigher for xcm dispatch: +/// +/// `ParentAsUmp` does `>::append(` means one write +/// `XcmpQueue` does: +/// - `wrap_version` - 1 read +/// - `send_fragment`: +/// - `get_channel_max` - 1 read +/// - `>::get()` - 1 read +/// - `>::mutate(` - 1 write +/// or +/// - `>::insert(` - 1 write +/// - `>::put - 1 write` - 1 write +pub struct XcmRouterWeigher; +impl Get for XcmRouterWeigher { + fn get() -> Weight { + RocksDbWeight::get().reads_writes(1, 2) + } +} + #[cfg(feature = "runtime-benchmarks")] parameter_types! { pub ReachableDest: Option = Some(Parent.into()); diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index a759c905df8..e0b1f47fba6 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -16,7 +16,6 @@ use bp_messages::target_chain::MessageDispatch; use bp_runtime::messages::MessageDispatchResult; -use bridge_hub_rococo_runtime::bridge_common_config::XcmBlobMessageDispatchResult; pub use bridge_hub_rococo_runtime::{ xcm_config::{XcmConfig, XcmRouter}, Runtime, *, @@ -25,6 +24,7 @@ use codec::Encode; use xcm::latest::prelude::*; use bridge_hub_test_utils::*; +use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult; use frame_support::weights::Weight; use xcm_executor::XcmExecutor; From 3dae8ee42587d5be581a1cdcf503ca943800de11 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 15 Mar 2023 15:51:47 +0100 Subject: [PATCH 212/263] Cargo.lock --- Cargo.lock | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8fbc45468c3..81740617a01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -502,7 +502,7 @@ name = "binary-merkle-tree" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#9eafc96a62d160cd97f8150b2742d7048e394bba" dependencies = [ - "hash-db", + "hash-db 0.16.0", "log", ] @@ -811,7 +811,7 @@ version = "0.1.0" dependencies = [ "frame-support", "frame-system", - "hash-db", + "hash-db 0.15.2", "hex-literal", "impl-trait-for-tuples", "num-traits", @@ -824,7 +824,7 @@ dependencies = [ "sp-state-machine", "sp-std", "sp-trie", - "trie-db", + "trie-db 0.26.0", ] [[package]] @@ -1096,7 +1096,7 @@ dependencies = [ "bp-test-utils", "frame-support", "frame-system", - "hash-db", + "hash-db 0.15.2", "log", "pallet-balances", "pallet-bridge-grandpa", @@ -4129,6 +4129,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "hash-db" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" + [[package]] name = "hash-db" version = "0.16.0" @@ -5620,7 +5626,7 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" dependencies = [ - "hash-db", + "hash-db 0.16.0", ] [[package]] @@ -10551,7 +10557,7 @@ name = "sc-client-db" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#9eafc96a62d160cd97f8150b2742d7048e394bba" dependencies = [ - "hash-db", + "hash-db 0.16.0", "kvdb", "kvdb-memorydb", "kvdb-rocksdb", @@ -12038,7 +12044,7 @@ name = "sp-api" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#9eafc96a62d160cd97f8150b2742d7048e394bba" dependencies = [ - "hash-db", + "hash-db 0.16.0", "log", "parity-scale-codec", "sp-api-proc-macro", @@ -12266,7 +12272,7 @@ dependencies = [ "dyn-clonable", "ed25519-zebra", "futures", - "hash-db", + "hash-db 0.16.0", "hash256-std-hasher", "impl-serde", "lazy_static", @@ -12582,7 +12588,7 @@ name = "sp-state-machine" version = "0.13.0" source = "git+https://github.com/paritytech/substrate?branch=master#9eafc96a62d160cd97f8150b2742d7048e394bba" dependencies = [ - "hash-db", + "hash-db 0.16.0", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -12673,7 +12679,7 @@ version = "7.0.0" source = "git+https://github.com/paritytech/substrate?branch=master#9eafc96a62d160cd97f8150b2742d7048e394bba" dependencies = [ "ahash 0.8.2", - "hash-db", + "hash-db 0.16.0", "hashbrown 0.12.3", "lazy_static", "memory-db", @@ -12686,7 +12692,7 @@ dependencies = [ "sp-std", "thiserror", "tracing", - "trie-db", + "trie-db 0.27.0", "trie-root", ] @@ -13110,7 +13116,7 @@ dependencies = [ "sp-runtime", "sp-state-machine", "sp-trie", - "trie-db", + "trie-db 0.27.0", ] [[package]] @@ -13743,13 +13749,26 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "trie-db" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879380c0061b165ba1f036325b7f3478bc1a947814d9fc36d22c5d8e960b11bd" +dependencies = [ + "hash-db 0.15.2", + "hashbrown 0.13.2", + "log", + "rustc-hex", + "smallvec", +] + [[package]] name = "trie-db" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634d75c77ea43f2ad8ea9d9c58de49dfc9c3995bdef32b503df7883ff054e7f1" dependencies = [ - "hash-db", + "hash-db 0.16.0", "hashbrown 0.13.2", "log", "rustc-hex", @@ -13762,7 +13781,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" dependencies = [ - "hash-db", + "hash-db 0.16.0", ] [[package]] From 5b3ed05e6468366f88dd5d6863106b59b44cfb8d Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 16 Mar 2023 13:49:15 +0300 Subject: [PATCH 213/263] fixed bridge pallets compilation --- bridges/bin/runtime-common/Cargo.toml | 2 +- bridges/primitives/runtime/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml index dde9e70f576..58ca8a31b5b 100644 --- a/bridges/bin/runtime-common/Cargo.toml +++ b/bridges/bin/runtime-common/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } -hash-db = { version = "0.15.2", default-features = false } +hash-db = { version = "0.16", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } static_assertions = { version = "1.1", optional = true } diff --git a/bridges/primitives/runtime/Cargo.toml b/bridges/primitives/runtime/Cargo.toml index 3cd2fdd35ca..a470571cf44 100644 --- a/bridges/primitives/runtime/Cargo.toml +++ b/bridges/primitives/runtime/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } -hash-db = { version = "0.15.2", default-features = false } +hash-db = { version = "0.16", default-features = false } impl-trait-for-tuples = "0.2.2" num-traits = { version = "0.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } From c94162a686ecc21061ae8261d9e9cbd6611d1d9c Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 16 Mar 2023 12:10:20 +0100 Subject: [PATCH 214/263] Cargo.lock --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 81740617a01..69075d99c6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -811,7 +811,7 @@ version = "0.1.0" dependencies = [ "frame-support", "frame-system", - "hash-db 0.15.2", + "hash-db 0.16.0", "hex-literal", "impl-trait-for-tuples", "num-traits", @@ -1096,7 +1096,7 @@ dependencies = [ "bp-test-utils", "frame-support", "frame-system", - "hash-db 0.15.2", + "hash-db 0.16.0", "log", "pallet-balances", "pallet-bridge-grandpa", From 07a5b10da2cc8b7255b0ea0c0e5e52662a8f5c08 Mon Sep 17 00:00:00 2001 From: acatangiu Date: Thu, 23 Mar 2023 19:18:28 +0200 Subject: [PATCH 215/263] fix bridge pallets compilation after substrate+polkadot bump --- Cargo.lock | 132 +++++++++--------- .../chain-bridge-hub-cumulus/src/lib.rs | 7 +- bridges/primitives/relayers/src/lib.rs | 13 +- 3 files changed, 79 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 940c73aa8c1..4b5940bbc32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4874,7 +4874,7 @@ checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" [[package]] name = "kusama-runtime" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "frame-benchmarking", @@ -4972,7 +4972,7 @@ dependencies = [ [[package]] name = "kusama-runtime-constants" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "frame-support", "polkadot-primitives", @@ -7534,7 +7534,7 @@ dependencies = [ [[package]] name = "pallet-xcm" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bounded-collections", "frame-benchmarking", @@ -7555,7 +7555,7 @@ dependencies = [ [[package]] name = "pallet-xcm-benchmarks" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "frame-benchmarking", "frame-support", @@ -8126,7 +8126,7 @@ dependencies = [ [[package]] name = "polkadot-approval-distribution" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "polkadot-node-metrics", @@ -8141,7 +8141,7 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "polkadot-node-network-protocol", @@ -8155,7 +8155,7 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "derive_more", "fatality", @@ -8178,7 +8178,7 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "fatality", "futures", @@ -8199,7 +8199,7 @@ dependencies = [ [[package]] name = "polkadot-cli" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "clap 4.1.11", "frame-benchmarking-cli", @@ -8227,7 +8227,7 @@ dependencies = [ [[package]] name = "polkadot-client" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "frame-benchmarking", @@ -8270,7 +8270,7 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "always-assert", "bitvec", @@ -8292,7 +8292,7 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "parity-scale-codec", "scale-info", @@ -8304,7 +8304,7 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "derive_more", "fatality", @@ -8329,7 +8329,7 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", @@ -8343,7 +8343,7 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "futures-timer", @@ -8363,7 +8363,7 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "always-assert", "async-trait", @@ -8386,7 +8386,7 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "parity-scale-codec", @@ -8404,7 +8404,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "derive_more", @@ -8433,7 +8433,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "futures", @@ -8454,7 +8454,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "fatality", @@ -8473,7 +8473,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "polkadot-node-subsystem", @@ -8488,7 +8488,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "futures", @@ -8508,7 +8508,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "polkadot-node-metrics", @@ -8523,7 +8523,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "futures-timer", @@ -8540,7 +8540,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "fatality", "futures", @@ -8559,7 +8559,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "futures", @@ -8576,7 +8576,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "fatality", @@ -8594,7 +8594,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "always-assert", "assert_matches", @@ -8630,7 +8630,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-checker" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "polkadot-node-primitives", @@ -8646,7 +8646,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "lru 0.9.0", @@ -8661,7 +8661,7 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "lazy_static", "log", @@ -8679,7 +8679,7 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bs58", "futures", @@ -8698,7 +8698,7 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "derive_more", @@ -8720,7 +8720,7 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bounded-vec", "futures", @@ -8743,7 +8743,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -8753,7 +8753,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-test-helpers" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "futures", @@ -8771,7 +8771,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "derive_more", @@ -8794,7 +8794,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "derive_more", @@ -8827,7 +8827,7 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "futures", @@ -8850,7 +8850,7 @@ dependencies = [ [[package]] name = "polkadot-parachain" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bounded-collections", "derive_more", @@ -8947,7 +8947,7 @@ dependencies = [ [[package]] name = "polkadot-performance-test" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "env_logger 0.9.0", "kusama-runtime", @@ -8963,7 +8963,7 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "hex-literal", @@ -8989,7 +8989,7 @@ dependencies = [ [[package]] name = "polkadot-rpc" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "jsonrpsee", "mmr-rpc", @@ -9021,7 +9021,7 @@ dependencies = [ [[package]] name = "polkadot-runtime" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "frame-benchmarking", @@ -9115,7 +9115,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "frame-benchmarking", @@ -9161,7 +9161,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-constants" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "frame-support", "polkadot-primitives", @@ -9175,7 +9175,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-metrics" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bs58", "parity-scale-codec", @@ -9187,7 +9187,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitflags 1.3.2", "bitvec", @@ -9231,7 +9231,7 @@ dependencies = [ [[package]] name = "polkadot-service" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "frame-benchmarking-cli", @@ -9341,7 +9341,7 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "arrayvec 0.5.2", "fatality", @@ -9362,7 +9362,7 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -9372,7 +9372,7 @@ dependencies = [ [[package]] name = "polkadot-test-client" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "parity-scale-codec", "polkadot-node-subsystem", @@ -9397,7 +9397,7 @@ dependencies = [ [[package]] name = "polkadot-test-runtime" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "frame-election-provider-support", @@ -9458,7 +9458,7 @@ dependencies = [ [[package]] name = "polkadot-test-service" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "frame-benchmarking", "frame-system", @@ -10205,7 +10205,7 @@ dependencies = [ [[package]] name = "rococo-runtime" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "binary-merkle-tree", "frame-benchmarking", @@ -10291,7 +10291,7 @@ dependencies = [ [[package]] name = "rococo-runtime-constants" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "frame-support", "polkadot-primitives", @@ -12131,7 +12131,7 @@ checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" [[package]] name = "slot-range-helper" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "enumn", "parity-scale-codec", @@ -13464,7 +13464,7 @@ checksum = "13a4ec180a2de59b57434704ccfad967f789b12737738798fa08798cd5824c16" [[package]] name = "test-runtime-constants" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "frame-support", "polkadot-primitives", @@ -13864,7 +13864,7 @@ dependencies = [ [[package]] name = "tracing-gum" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "polkadot-node-jaeger", "polkadot-primitives", @@ -13875,7 +13875,7 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "expander 0.0.6", "proc-macro-crate", @@ -14946,7 +14946,7 @@ dependencies = [ [[package]] name = "westend-runtime" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "frame-benchmarking", @@ -15038,7 +15038,7 @@ dependencies = [ [[package]] name = "westend-runtime-constants" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "frame-support", "polkadot-primitives", @@ -15474,7 +15474,7 @@ dependencies = [ [[package]] name = "xcm" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bounded-collections", "derivative", @@ -15490,7 +15490,7 @@ dependencies = [ [[package]] name = "xcm-builder" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "frame-support", "frame-system", @@ -15511,7 +15511,7 @@ dependencies = [ [[package]] name = "xcm-executor" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "environmental", "frame-benchmarking", @@ -15531,7 +15531,7 @@ dependencies = [ [[package]] name = "xcm-procedural" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#d8e6ca5e27aa5e0836853898bcce124648b3850e" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "Inflector", "proc-macro2", diff --git a/bridges/primitives/chain-bridge-hub-cumulus/src/lib.rs b/bridges/primitives/chain-bridge-hub-cumulus/src/lib.rs index cbdd36c2e4a..55b22453558 100644 --- a/bridges/primitives/chain-bridge-hub-cumulus/src/lib.rs +++ b/bridges/primitives/chain-bridge-hub-cumulus/src/lib.rs @@ -45,9 +45,10 @@ pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); /// time. /// /// This is a copy-paste from the cumulus repo's `parachains-common` crate. -const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts(constants::WEIGHT_REF_TIME_PER_SECOND, 0) - .saturating_div(2) - .set_proof_size(polkadot_primitives::v2::MAX_POV_SIZE as u64); +const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + constants::WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), + polkadot_primitives::MAX_POV_SIZE as u64, +); /// All cumulus bridge hubs assume that about 5 percent of the block weight is consumed by /// `on_initialize` handlers. This is used to limit the maximal weight of a single extrinsic. diff --git a/bridges/primitives/relayers/src/lib.rs b/bridges/primitives/relayers/src/lib.rs index fb350958415..f14b841fa9e 100644 --- a/bridges/primitives/relayers/src/lib.rs +++ b/bridges/primitives/relayers/src/lib.rs @@ -21,7 +21,7 @@ use bp_messages::LaneId; use bp_runtime::{ChainId, StorageDoubleMapKeyProvider}; -use frame_support::{Blake2_128Concat, Identity}; +use frame_support::{traits::tokens::Preservation, Blake2_128Concat, Identity}; use scale_info::TypeInfo; use sp_runtime::{ codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}, @@ -110,7 +110,7 @@ where impl PaymentProcedure for PayRewardFromAccount where - T: frame_support::traits::fungible::Transfer, + T: frame_support::traits::fungible::Mutate, Relayer: Decode + Encode, { type Error = sp_runtime::DispatchError; @@ -120,8 +120,13 @@ where rewards_account_params: RewardsAccountParams, reward: T::Balance, ) -> Result<(), Self::Error> { - T::transfer(&Self::rewards_account(rewards_account_params), relayer, reward, false) - .map(drop) + T::transfer( + &Self::rewards_account(rewards_account_params), + relayer, + reward, + Preservation::Expendable, + ) + .map(drop) } } From fa78c96cfc9aa85b1a301d632cf1b53f40ddcf61 Mon Sep 17 00:00:00 2001 From: acatangiu Date: Mon, 20 Mar 2023 20:14:16 +0200 Subject: [PATCH 216/263] BridgeHubs: XCM ExportMessage benchmark - just Rococo now --- parachains/runtimes/assets/statemine/src/lib.rs | 5 +++++ parachains/runtimes/assets/statemint/src/lib.rs | 5 +++++ parachains/runtimes/assets/westmint/src/lib.rs | 5 +++++ .../runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs | 6 ++++++ .../runtimes/bridge-hubs/bridge-hub-polkadot/src/lib.rs | 6 ++++++ .../runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 5 +++++ .../bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs | 7 +------ .../src/weights/xcm/pallet_xcm_benchmarks_generic.rs | 3 +++ 8 files changed, 36 insertions(+), 6 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/lib.rs b/parachains/runtimes/assets/statemine/src/lib.rs index 4f03b049efa..e90863e1d3b 100644 --- a/parachains/runtimes/assets/statemine/src/lib.rs +++ b/parachains/runtimes/assets/statemine/src/lib.rs @@ -1135,6 +1135,11 @@ impl_runtime_apis! { fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { Err(BenchmarkError::Skip) } + + fn export_message_origin_and_destination( + ) -> Result<(MultiLocation, NetworkId, InteriorMultiLocation), BenchmarkError> { + Err(BenchmarkError::Skip) + } } type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; diff --git a/parachains/runtimes/assets/statemint/src/lib.rs b/parachains/runtimes/assets/statemint/src/lib.rs index 21bdf7bc038..74115b4e959 100644 --- a/parachains/runtimes/assets/statemint/src/lib.rs +++ b/parachains/runtimes/assets/statemint/src/lib.rs @@ -1032,6 +1032,11 @@ impl_runtime_apis! { fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { Err(BenchmarkError::Skip) } + + fn export_message_origin_and_destination( + ) -> Result<(MultiLocation, NetworkId, InteriorMultiLocation), BenchmarkError> { + Err(BenchmarkError::Skip) + } } type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; diff --git a/parachains/runtimes/assets/westmint/src/lib.rs b/parachains/runtimes/assets/westmint/src/lib.rs index 8fca37c35bf..5994fdcabb5 100644 --- a/parachains/runtimes/assets/westmint/src/lib.rs +++ b/parachains/runtimes/assets/westmint/src/lib.rs @@ -1146,6 +1146,11 @@ impl_runtime_apis! { fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { Err(BenchmarkError::Skip) } + + fn export_message_origin_and_destination( + ) -> Result<(MultiLocation, NetworkId, InteriorMultiLocation), BenchmarkError> { + Err(BenchmarkError::Skip) + } } type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs index f62dbe9ea4c..19595c41d37 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs @@ -704,6 +704,12 @@ impl_runtime_apis! { fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { Err(BenchmarkError::Skip) } + + fn export_message_origin_and_destination( + ) -> Result<(MultiLocation, NetworkId, InteriorMultiLocation), BenchmarkError> { + // TODO - don't skip + Err(BenchmarkError::Skip) + } } type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/lib.rs index 37ab7a0afba..91d9fbd2591 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/lib.rs @@ -709,6 +709,12 @@ impl_runtime_apis! { fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { Err(BenchmarkError::Skip) } + + fn export_message_origin_and_destination( + ) -> Result<(MultiLocation, NetworkId, InteriorMultiLocation), BenchmarkError> { + // TODO - don't skip + Err(BenchmarkError::Skip) + } } type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 591ebaf46c0..9c9da6ac068 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -973,6 +973,11 @@ impl_runtime_apis! { fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { Err(BenchmarkError::Skip) } + + fn export_message_origin_and_destination( + ) -> Result<(MultiLocation, NetworkId, InteriorMultiLocation), BenchmarkError> { + Ok((RelayLocation::get(), NetworkId::Wococo, X1(Parachain(100)))) + } } type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs index 1ae78a807f5..84efa21f80b 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs @@ -218,12 +218,7 @@ impl XcmWeightInfo for BridgeHubRococoXcmWeight { Weight::MAX } fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { - log::error!( - target: crate::LOG_TARGET, - "TODO: Calling weight: export_message -> triggers unpaid_execution -> need fix here!" - ); - // TODO:check-parameter - add correct weight also add it to the polkadot gav-xcm-v3 - XcmGeneric::::unpaid_execution() + XcmGeneric::::export_message() } fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { Weight::MAX diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index d3df54c32e5..ad2289f80de 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -338,6 +338,9 @@ impl WeightInfo { // Minimum execution time: 2_901_000 picoseconds. Weight::from_parts(2_977_000, 0) } + pub(crate) fn export_message() -> Weight { + Weight::from_parts(3_394_000 as u64, 0) + } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` From b99b10321a3ea9067b4bf7d8069ecec0f0fcbd05 Mon Sep 17 00:00:00 2001 From: acatangiu Date: Tue, 21 Mar 2023 14:27:02 +0200 Subject: [PATCH 217/263] bench export_message() --- .../src/weights/xcm/pallet_xcm_benchmarks_generic.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index ad2289f80de..7b6309a567a 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -338,8 +338,18 @@ impl WeightInfo { // Minimum execution time: 2_901_000 picoseconds. Weight::from_parts(2_977_000, 0) } + // Storage: ParachainInfo ParachainId (r:1 w:0) + // Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + // Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + // Proof: BridgeWococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + // Storage: BridgeWococoMessages OutboundLanes (r:1 w:1) + // Proof: BridgeWococoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: 539, mode: MaxEncodedLen) + // Storage: BridgeWococoMessages OutboundMessages (r:0 w:1) + // Proof: BridgeWococoMessages OutboundMessages (max_values: None, max_size: Some(2621472), added: 2623947, mode: MaxEncodedLen) pub(crate) fn export_message() -> Weight { - Weight::from_parts(3_394_000 as u64, 0) + Weight::from_parts(30_496_000 as u64, 0) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: From 5b5ac53b102a4a3922354397f2503f4726de1a9c Mon Sep 17 00:00:00 2001 From: acatangiu Date: Wed, 22 Mar 2023 15:21:12 +0200 Subject: [PATCH 218/263] include Bridge::haul_blob() weight in ExportMessage weight --- .../bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs | 6 ++++-- .../src/weights/xcm/pallet_xcm_benchmarks_generic.rs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs index 84efa21f80b..0e938644dc0 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs @@ -21,6 +21,7 @@ use crate::{xcm_config::MaxAssetsIntoHolding, Runtime}; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; +use parity_scale_codec::Encode; use sp_std::prelude::*; use xcm::{latest::prelude::*, DoubleEncoded}; @@ -217,8 +218,9 @@ impl XcmWeightInfo for BridgeHubRococoXcmWeight { fn universal_origin(_: &Junction) -> Weight { Weight::MAX } - fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight { - XcmGeneric::::export_message() + fn export_message(_: &NetworkId, _: &Junctions, inner: &Xcm<()>) -> Weight { + let inner_encoded_len = inner.encode().len() as u32; + XcmGeneric::::export_message(inner_encoded_len) } fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight { Weight::MAX diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 7b6309a567a..ce577474182 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -346,8 +346,10 @@ impl WeightInfo { // Proof: BridgeWococoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: 539, mode: MaxEncodedLen) // Storage: BridgeWococoMessages OutboundMessages (r:0 w:1) // Proof: BridgeWococoMessages OutboundMessages (max_values: None, max_size: Some(2621472), added: 2623947, mode: MaxEncodedLen) - pub(crate) fn export_message() -> Weight { - Weight::from_parts(30_496_000 as u64, 0) + pub(crate) fn export_message(x: u32, ) -> Weight { + Weight::from_parts(31_677_716 as u64, 0) + // Standard Error: 4_158 + .saturating_add(Weight::from_parts(123_901, 0).saturating_mul(x as u64)) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } From 44e9280ae4d9f8cf6fb0ae3ace7a9e940535a6f4 Mon Sep 17 00:00:00 2001 From: acatangiu Date: Wed, 22 Mar 2023 15:38:56 +0200 Subject: [PATCH 219/263] fix import --- .../bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs index 0e938644dc0..a0aaac56f5a 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/mod.rs @@ -18,10 +18,10 @@ mod pallet_xcm_benchmarks_fungible; mod pallet_xcm_benchmarks_generic; use crate::{xcm_config::MaxAssetsIntoHolding, Runtime}; +use codec::Encode; use frame_support::weights::Weight; use pallet_xcm_benchmarks_fungible::WeightInfo as XcmFungibleWeight; use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric; -use parity_scale_codec::Encode; use sp_std::prelude::*; use xcm::{latest::prelude::*, DoubleEncoded}; From c876914fa46bd5d6b2f1e4970107a0a4af5846cc Mon Sep 17 00:00:00 2001 From: acatangiu Date: Fri, 24 Mar 2023 00:01:43 +0200 Subject: [PATCH 220/263] more build fixes --- bridges/modules/messages/src/mock.rs | 6 +++++- bridges/modules/relayers/src/mock.rs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/bridges/modules/messages/src/mock.rs b/bridges/modules/messages/src/mock.rs index a31ee2b9401..807721ba866 100644 --- a/bridges/modules/messages/src/mock.rs +++ b/bridges/modules/messages/src/mock.rs @@ -40,7 +40,7 @@ use scale_info::TypeInfo; use sp_core::H256; use sp_runtime::{ testing::Header as SubstrateHeader, - traits::{BlakeTwo256, IdentityLookup}, + traits::{BlakeTwo256, ConstU32, IdentityLookup}, Perbill, }; use std::{ @@ -133,6 +133,10 @@ impl pallet_balances::Config for TestRuntime { type WeightInfo = (); type MaxReserves = (); type ReserveIdentifier = (); + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; } parameter_types! { diff --git a/bridges/modules/relayers/src/mock.rs b/bridges/modules/relayers/src/mock.rs index c40c86f1db5..5e97e81ae33 100644 --- a/bridges/modules/relayers/src/mock.rs +++ b/bridges/modules/relayers/src/mock.rs @@ -24,7 +24,7 @@ use frame_support::{parameter_types, weights::RuntimeDbWeight}; use sp_core::H256; use sp_runtime::{ testing::Header as SubstrateHeader, - traits::{BlakeTwo256, IdentityLookup}, + traits::{BlakeTwo256, ConstU32, IdentityLookup}, }; pub type AccountId = u64; @@ -86,6 +86,10 @@ impl pallet_balances::Config for TestRuntime { type WeightInfo = (); type MaxReserves = (); type ReserveIdentifier = (); + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; } impl pallet_bridge_relayers::Config for TestRuntime { From b120660a689acb25af0cd701322e172d01b5f215 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 24 Mar 2023 14:39:55 +0100 Subject: [PATCH 221/263] Squashed 'bridges/' changes from ce7cf9a49..6343a7d37 6343a7d37 bump substrate+polkadot refs and fix builds (#1989) 8efc2b3cc Added receive_single_message_proof_with_dispatch benchmark (#1990) 6540f74dc Remove deprecated code from bridge-runtime-common (#1983) c4f368be3 minor cosmetic updates (#1985) bef11ac43 remove invalid weight, returned by send_message (#1984) 28cf5c957 Kusama <> Polkadot relay prototype (#1982) b195223d1 Bump serde from 1.0.156 to 1.0.157 70caa75d7 ignore binary-merkle-tree (#1980) 3dc640d30 Bump thiserror from 1.0.39 to 1.0.40 8a2729101 Bump subxt from `d4545de` to `ae63d3d` 40937e8a3 Bump clap from 4.1.8 to 4.1.11 d72394c4e Bump finality-grandpa from 0.16.1 to 0.16.2 54147603d Bump serde from 1.0.155 to 1.0.156 b513193e6 Bump anyhow from 1.0.69 to 1.0.70 20867abd9 Bump sysinfo from 0.28.2 to 0.28.3 4d9a45305 Bump async-trait from 0.1.66 to 0.1.67 8a88a7536 Bump trie-db from 0.26.0 to 0.27.1 0add06edd move signed extension stuff from prolkadot-core primitives to bridge-hub-cumulus-primitives (#1968) 7481ce6eb added UpdatedBestFinalizedHeader event to pallet-bridge-grandpa (#1967) 6787cd0cb RBH <> WBH dashboards and alerts (#1966) 036f7be76 enable relayer rewards metrics at bridge hubs (#1965) a3f07d5dd Fix invalid batch transaction (#1957) git-subtree-dir: bridges git-subtree-split: 6343a7d37c32191413be91afb537b8bc6c770285 --- .config/lingua.dic | 2 + .github/dependabot.yml | 3 + Cargo.lock | 2317 ++++++++++------- Cargo.toml | 4 + README.md | 9 +- bin/millau/node/Cargo.toml | 2 +- bin/millau/node/src/service.rs | 31 +- bin/millau/runtime/src/lib.rs | 30 +- bin/millau/runtime/src/rialto_messages.rs | 68 +- .../runtime/src/rialto_parachain_messages.rs | 70 +- bin/millau/runtime/src/xcm_config.rs | 295 ++- bin/rialto-parachain/node/Cargo.toml | 2 +- bin/rialto-parachain/node/src/command.rs | 2 +- bin/rialto-parachain/node/src/service.rs | 8 +- bin/rialto-parachain/runtime/Cargo.toml | 2 + bin/rialto-parachain/runtime/src/lib.rs | 237 +- .../runtime/src/millau_messages.rs | 64 +- bin/rialto/node/Cargo.toml | 2 +- bin/rialto/node/src/chain_spec.rs | 8 +- bin/rialto/runtime/src/lib.rs | 109 +- bin/rialto/runtime/src/millau_messages.rs | 68 +- bin/rialto/runtime/src/parachains.rs | 2 +- bin/rialto/runtime/src/xcm_config.rs | 173 +- bin/runtime-common/Cargo.toml | 2 +- bin/runtime-common/src/integrity.rs | 2 +- bin/runtime-common/src/messages.rs | 355 +-- .../src/messages_benchmarking.rs | 53 +- .../src/messages_xcm_extension.rs | 77 +- bin/runtime-common/src/mock.rs | 53 +- .../bridges/common/generate_messages.sh | 3 +- .../bridges/rialto-millau/docker-compose.yml | 23 - ...messages-to-millau-generator-entrypoint.sh | 5 +- ...messages-to-rialto-generator-entrypoint.sh | 5 +- .../relay-millau-rialto-entrypoint.sh | 1 - ...messages-to-millau-generator-entrypoint.sh | 5 +- ...o-rialto-parachain-generator-entrypoint.sh | 5 +- deployments/bridges/rococo-wococo/README.md | 10 + ...y-rococo-to-wococo-messages-dashboard.json | 941 +++++++ ...y-wococo-to-rococo-messages-dashboard.json | 941 +++++++ .../rococo-wococo-maintenance-dashboard.json | 853 ++++++ deployments/networks/millau.yml | 2 +- deployments/networks/rialto-parachain.yml | 2 +- deployments/networks/rialto.yml | 2 +- modules/grandpa/Cargo.toml | 2 +- modules/grandpa/src/lib.rs | 42 +- modules/grandpa/src/mock.rs | 5 +- modules/messages/src/benchmarking.rs | 93 +- modules/messages/src/lib.rs | 23 +- modules/messages/src/mock.rs | 6 +- modules/messages/src/weights.rs | 219 +- modules/messages/src/weights_ext.rs | 15 + modules/parachains/src/lib.rs | 72 +- modules/parachains/src/mock.rs | 6 +- modules/relayers/src/mock.rs | 6 +- .../chain-bridge-hub-cumulus/src/lib.rs | 91 +- primitives/chain-bridge-hub-kusama/Cargo.toml | 31 + primitives/chain-bridge-hub-kusama/src/lib.rs | 84 + .../chain-bridge-hub-polkadot/Cargo.toml | 32 + .../chain-bridge-hub-polkadot/src/lib.rs | 75 + primitives/chain-bridge-hub-rococo/src/lib.rs | 4 + primitives/chain-bridge-hub-wococo/src/lib.rs | 4 + primitives/chain-kusama/src/lib.rs | 3 + primitives/chain-polkadot/src/lib.rs | 3 + primitives/chain-rialto-parachain/Cargo.toml | 2 + primitives/chain-rialto-parachain/src/lib.rs | 4 +- primitives/header-chain/Cargo.toml | 2 +- primitives/messages/src/source_chain.rs | 6 +- primitives/polkadot-core/src/lib.rs | 137 - primitives/relayers/src/lib.rs | 13 +- primitives/runtime/Cargo.toml | 4 +- primitives/runtime/src/extensions.rs | 9 +- primitives/runtime/src/lib.rs | 75 +- primitives/test-utils/Cargo.toml | 2 +- relays/bin-substrate/Cargo.toml | 6 +- ..._kusama_messages_to_bridge_hub_polkadot.rs | 65 + ..._polkadot_messages_to_bridge_hub_kusama.rs | 65 + .../kusama_headers_to_bridge_hub_polkadot.rs | 72 + ...usama_parachains_to_bridge_hub_polkadot.rs | 75 + .../src/bridges/kusama_polkadot/mod.rs | 24 + .../polkadot_headers_to_bridge_hub_kusama.rs | 72 + ...olkadot_parachains_to_bridge_hub_kusama.rs | 75 + relays/bin-substrate/src/bridges/mod.rs | 23 + .../millau_headers_to_rialto.rs | 3 +- .../millau_messages_to_rialto.rs | 0 .../src/bridges/rialto_millau/mod.rs | 22 + .../rialto_headers_to_millau.rs | 3 +- .../rialto_messages_to_millau.rs | 0 .../millau_headers_to_rialto_parachain.rs | 2 +- .../millau_messages_to_rialto_parachain.rs | 0 .../bridges/rialto_parachain_millau/mod.rs | 22 + .../rialto_parachain_messages_to_millau.rs | 0 .../rialto_parachains_to_millau.rs | 5 +- ...ub_rococo_messages_to_bridge_hub_wococo.rs | 0 ...ub_wococo_messages_to_bridge_hub_rococo.rs | 0 .../src/bridges/rococo_wococo/mod.rs | 24 + .../rococo_headers_to_bridge_hub_wococo.rs | 0 .../rococo_parachains_to_bridge_hub_wococo.rs | 4 +- .../wococo_headers_to_bridge_hub_rococo.rs | 0 .../wococo_parachains_to_bridge_hub_rococo.rs | 4 +- .../src/bridges/westend_millau/mod.rs | 20 + .../westend_headers_to_millau.rs | 0 .../westend_parachains_to_millau.rs | 3 +- relays/bin-substrate/src/chains/kusama.rs | 32 + relays/bin-substrate/src/chains/millau.rs | 29 +- relays/bin-substrate/src/chains/mod.rs | 19 +- relays/bin-substrate/src/chains/polkadot.rs | 32 + relays/bin-substrate/src/chains/rialto.rs | 21 +- .../src/chains/rialto_parachain.rs | 22 +- relays/bin-substrate/src/cli/bridge.rs | 21 +- .../bin-substrate/src/cli/encode_message.rs | 18 +- relays/bin-substrate/src/cli/init_bridge.rs | 18 +- .../src/cli/register_parachain.rs | 4 +- relays/bin-substrate/src/cli/relay_headers.rs | 30 +- .../src/cli/relay_headers_and_messages/mod.rs | 20 +- .../bin-substrate/src/cli/relay_messages.rs | 30 +- .../bin-substrate/src/cli/relay_parachains.rs | 46 +- relays/bin-substrate/src/cli/send_message.rs | 27 +- relays/bin-substrate/src/main.rs | 1 + relays/client-bridge-hub-kusama/Cargo.toml | 28 + relays/client-bridge-hub-kusama/src/lib.rs | 162 ++ .../src/runtime_wrapper.rs | 74 + relays/client-bridge-hub-polkadot/Cargo.toml | 32 + relays/client-bridge-hub-polkadot/src/lib.rs | 160 ++ .../src/runtime_wrapper.rs | 119 + relays/client-bridge-hub-rococo/src/lib.rs | 16 +- .../src/runtime_wrapper.rs | 7 +- relays/client-bridge-hub-wococo/src/lib.rs | 16 +- .../src/runtime_wrapper.rs | 7 +- relays/client-kusama/src/lib.rs | 13 +- relays/client-millau/src/lib.rs | 2 - relays/client-polkadot/src/lib.rs | 13 +- relays/client-rialto-parachain/Cargo.toml | 3 +- .../src/codegen_runtime.rs | 1342 ++++++---- relays/client-rialto-parachain/src/lib.rs | 4 +- relays/client-rialto/src/lib.rs | 2 - relays/client-rococo/src/lib.rs | 1 - relays/client-substrate/Cargo.toml | 2 +- relays/client-substrate/src/chain.rs | 5 - relays/client-substrate/src/client.rs | 4 +- relays/client-substrate/src/test_chain.rs | 2 - relays/client-westend/src/lib.rs | 2 - relays/client-wococo/src/lib.rs | 1 - relays/lib-substrate-relay/Cargo.toml | 4 +- relays/lib-substrate-relay/src/lib.rs | 28 +- .../lib-substrate-relay/src/messages_lane.rs | 13 +- relays/messages/src/message_lane_loop.rs | 13 +- relays/messages/src/message_race_delivery.rs | 66 +- relays/messages/src/message_race_loop.rs | 176 +- relays/messages/src/message_race_strategy.rs | 103 +- relays/parachains/Cargo.toml | 2 +- relays/utils/Cargo.toml | 2 +- scripts/send-message-from-millau-rialto.sh | 2 +- scripts/send-message-from-rialto-millau.sh | 2 +- tools/runtime-codegen/Cargo.lock | 1644 ++++++++---- tools/runtime-codegen/Cargo.toml | 2 +- 155 files changed, 9265 insertions(+), 3686 deletions(-) create mode 100644 deployments/bridges/rococo-wococo/README.md create mode 100644 deployments/bridges/rococo-wococo/dashboard/grafana/relay-rococo-to-wococo-messages-dashboard.json create mode 100644 deployments/bridges/rococo-wococo/dashboard/grafana/relay-wococo-to-rococo-messages-dashboard.json create mode 100644 deployments/bridges/rococo-wococo/dashboard/grafana/rococo-wococo-maintenance-dashboard.json create mode 100644 primitives/chain-bridge-hub-kusama/Cargo.toml create mode 100644 primitives/chain-bridge-hub-kusama/src/lib.rs create mode 100644 primitives/chain-bridge-hub-polkadot/Cargo.toml create mode 100644 primitives/chain-bridge-hub-polkadot/src/lib.rs create mode 100644 relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_kusama_messages_to_bridge_hub_polkadot.rs create mode 100644 relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_polkadot_messages_to_bridge_hub_kusama.rs create mode 100644 relays/bin-substrate/src/bridges/kusama_polkadot/kusama_headers_to_bridge_hub_polkadot.rs create mode 100644 relays/bin-substrate/src/bridges/kusama_polkadot/kusama_parachains_to_bridge_hub_polkadot.rs create mode 100644 relays/bin-substrate/src/bridges/kusama_polkadot/mod.rs create mode 100644 relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_headers_to_bridge_hub_kusama.rs create mode 100644 relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_parachains_to_bridge_hub_kusama.rs create mode 100644 relays/bin-substrate/src/bridges/mod.rs rename relays/bin-substrate/src/{chains => bridges/rialto_millau}/millau_headers_to_rialto.rs (94%) rename relays/bin-substrate/src/{chains => bridges/rialto_millau}/millau_messages_to_rialto.rs (100%) create mode 100644 relays/bin-substrate/src/bridges/rialto_millau/mod.rs rename relays/bin-substrate/src/{chains => bridges/rialto_millau}/rialto_headers_to_millau.rs (94%) rename relays/bin-substrate/src/{chains => bridges/rialto_millau}/rialto_messages_to_millau.rs (100%) rename relays/bin-substrate/src/{chains => bridges/rialto_parachain_millau}/millau_headers_to_rialto_parachain.rs (96%) rename relays/bin-substrate/src/{chains => bridges/rialto_parachain_millau}/millau_messages_to_rialto_parachain.rs (100%) create mode 100644 relays/bin-substrate/src/bridges/rialto_parachain_millau/mod.rs rename relays/bin-substrate/src/{chains => bridges/rialto_parachain_millau}/rialto_parachain_messages_to_millau.rs (100%) rename relays/bin-substrate/src/{chains => bridges/rialto_parachain_millau}/rialto_parachains_to_millau.rs (91%) rename relays/bin-substrate/src/{chains => bridges/rococo_wococo}/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs (100%) rename relays/bin-substrate/src/{chains => bridges/rococo_wococo}/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs (100%) create mode 100644 relays/bin-substrate/src/bridges/rococo_wococo/mod.rs rename relays/bin-substrate/src/{chains => bridges/rococo_wococo}/rococo_headers_to_bridge_hub_wococo.rs (100%) rename relays/bin-substrate/src/{chains => bridges/rococo_wococo}/rococo_parachains_to_bridge_hub_wococo.rs (92%) rename relays/bin-substrate/src/{chains => bridges/rococo_wococo}/wococo_headers_to_bridge_hub_rococo.rs (100%) rename relays/bin-substrate/src/{chains => bridges/rococo_wococo}/wococo_parachains_to_bridge_hub_rococo.rs (92%) create mode 100644 relays/bin-substrate/src/bridges/westend_millau/mod.rs rename relays/bin-substrate/src/{chains => bridges/westend_millau}/westend_headers_to_millau.rs (100%) rename relays/bin-substrate/src/{chains => bridges/westend_millau}/westend_parachains_to_millau.rs (95%) create mode 100644 relays/bin-substrate/src/chains/kusama.rs create mode 100644 relays/bin-substrate/src/chains/polkadot.rs create mode 100644 relays/client-bridge-hub-kusama/Cargo.toml create mode 100644 relays/client-bridge-hub-kusama/src/lib.rs create mode 100644 relays/client-bridge-hub-kusama/src/runtime_wrapper.rs create mode 100644 relays/client-bridge-hub-polkadot/Cargo.toml create mode 100644 relays/client-bridge-hub-polkadot/src/lib.rs create mode 100644 relays/client-bridge-hub-polkadot/src/runtime_wrapper.rs diff --git a/.config/lingua.dic b/.config/lingua.dic index 3fd4698c604..9b622aad77d 100644 --- a/.config/lingua.dic +++ b/.config/lingua.dic @@ -19,6 +19,8 @@ BridgeStorage BridgeHub BridgeHubRococo BridgeHubWococo +BridgeHubKusama +BridgeHubPolkadot CLI/MS Chain1 Chain2 diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b3193adadc1..c0c8ea64802 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -39,6 +39,9 @@ updates: - dependency-name: try-runtime-cli versions: - ">= 0" + - dependency-name: binary-merkle-tree + versions: + - ">= 0" # Polkadot dependencies - dependency-name: kusama-* versions: diff --git a/Cargo.lock b/Cargo.lock index bd01089a33d..0ac84c43688 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,7 +27,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli 0.27.1", + "gimli 0.27.2", ] [[package]] @@ -55,6 +55,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "aead" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8" +dependencies = [ + "crypto-common", + "generic-array 0.14.6", +] + [[package]] name = "aes" version = "0.6.0" @@ -79,17 +89,14 @@ dependencies = [ ] [[package]] -name = "aes-gcm" -version = "0.8.0" +name = "aes" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" +checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" dependencies = [ - "aead 0.3.2", - "aes 0.6.0", - "cipher 0.2.5", - "ctr 0.6.0", - "ghash 0.3.1", - "subtle", + "cfg-if 1.0.0", + "cipher 0.4.4", + "cpufeatures", ] [[package]] @@ -106,6 +113,20 @@ dependencies = [ "subtle", ] +[[package]] +name = "aes-gcm" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +dependencies = [ + "aead 0.5.1", + "aes 0.8.2", + "cipher 0.4.4", + "ctr 0.9.2", + "ghash 0.5.0", + "subtle", +] + [[package]] name = "aes-soft" version = "0.6.4" @@ -184,9 +205,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "approx" @@ -199,9 +220,9 @@ dependencies = [ [[package]] name = "arbitrary" -version = "1.2.3" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e90af4de65aa7b293ef2d09daff88501eb254f58edde2e1ac02c82d873eadad" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" [[package]] name = "arc-swap" @@ -223,9 +244,9 @@ checksum = "22f72e9d6fac4bc80778ea470b20197b88d28c292bb7d60c3fb099280003cd19" [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -257,9 +278,9 @@ dependencies = [ [[package]] name = "asn1-rs" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf6690c370453db30743b373a60ba498fc0d6d83b11f4abfd87a84a075db5dd4" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" dependencies = [ "asn1-rs-derive 0.4.0", "asn1-rs-impl", @@ -277,8 +298,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", "synstructure", ] @@ -289,8 +310,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", "synstructure", ] @@ -301,8 +322,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -324,7 +345,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" dependencies = [ - "quote 1.0.23", + "quote 1.0.26", "syn 1.0.109", ] @@ -370,32 +391,31 @@ dependencies = [ [[package]] name = "async-io" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock", "autocfg", + "cfg-if 1.0.0", "concurrent-queue", "futures-lite", - "libc", "log", "parking", "polling", + "rustix 0.37.3", "slab", "socket2", "waker-fn", - "windows-sys 0.42.0", ] [[package]] name = "async-lock" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" dependencies = [ "event-listener", - "futures-lite", ] [[package]] @@ -433,13 +453,13 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.66" +version = "0.1.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc" +checksum = "86ea188f25f0255d8f92797797c97ebf5631fa88178beb1a46fdf5622c9a00e4" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 2.0.8", ] [[package]] @@ -516,6 +536,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base58" version = "0.2.0" @@ -536,9 +562,9 @@ checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "base64ct" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "beef" @@ -552,7 +578,7 @@ dependencies = [ [[package]] name = "binary-merkle-tree" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "hash-db", "log", @@ -569,21 +595,22 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.60.1" +version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", "lazycell", "peeking_take_while", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "regex", "rustc-hash", "shlex", + "syn 1.0.109", ] [[package]] @@ -592,6 +619,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" + [[package]] name = "bitvec" version = "1.0.1" @@ -621,18 +654,18 @@ checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec 0.7.2", - "constant_time_eq 0.2.4", + "constant_time_eq", ] [[package]] name = "blake2s_simd" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" +checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" dependencies = [ "arrayref", "arrayvec 0.7.2", - "constant_time_eq 0.1.5", + "constant_time_eq", ] [[package]] @@ -645,7 +678,7 @@ dependencies = [ "arrayvec 0.7.2", "cc", "cfg-if 1.0.0", - "constant_time_eq 0.2.4", + "constant_time_eq", "digest 0.10.6", ] @@ -672,9 +705,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array 0.14.6", ] @@ -770,6 +803,30 @@ dependencies = [ "sp-std 5.0.0", ] +[[package]] +name = "bp-bridge-hub-kusama" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-messages", + "bp-runtime", + "frame-support", + "sp-api", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-bridge-hub-polkadot" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-messages", + "bp-runtime", + "frame-support", + "sp-api", + "sp-std 5.0.0", +] + [[package]] name = "bp-bridge-hub-rococo" version = "0.1.0" @@ -943,6 +1000,7 @@ dependencies = [ name = "bp-rialto-parachain" version = "0.1.0" dependencies = [ + "bp-bridge-hub-cumulus", "bp-messages", "bp-polkadot-core", "bp-runtime", @@ -1069,9 +1127,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bstr" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832" +checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" dependencies = [ "memchr", "serde", @@ -1106,9 +1164,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" [[package]] name = "byteorder" @@ -1135,9 +1193,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055" +checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" dependencies = [ "serde", ] @@ -1159,7 +1217,7 @@ checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" dependencies = [ "camino", "cargo-platform", - "semver 1.0.16", + "semver 1.0.17", "serde", "serde_json", "thiserror", @@ -1254,9 +1312,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "iana-time-zone", "js-sys", @@ -1275,7 +1333,7 @@ checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" dependencies = [ "core2", "multibase", - "multihash", + "multihash 0.16.3", "serde", "unsigned-varint", ] @@ -1298,6 +1356,16 @@ dependencies = [ "generic-array 0.14.6", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "ckb-merkle-mountain-range" version = "0.3.2" @@ -1318,9 +1386,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" dependencies = [ "glob", "libc", @@ -1335,7 +1403,7 @@ checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", - "bitflags", + "bitflags 1.3.2", "strsim 0.8.0", "textwrap", "unicode-width", @@ -1344,11 +1412,11 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.8" +version = "4.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +checksum = "42dfd32784433290c51d92c438bb72ea5063797fc3cc9a21a8c4346bebbb2098" dependencies = [ - "bitflags", + "bitflags 2.0.2", "clap_derive", "clap_lex", "is-terminal", @@ -1359,31 +1427,31 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.1.8" +version = "4.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" +checksum = "fddf67631444a3a3e3e5ac51c36a5e01335302de677bd78759eaa90ab1f46644" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "clap_lex" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" +checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" dependencies = [ "os_str_bytes", ] [[package]] name = "coarsetime" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "454038500439e141804c655b4cd1bc6a70bcb95cd2bc9463af5661b6956f0e46" +checksum = "a90d114103adbc625300f346d4d09dfb4ab1c4a8df6868435dd903392ecf4354" dependencies = [ "libc", "once_cell", @@ -1423,21 +1491,15 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b" - -[[package]] -name = "constant_time_eq" -version = "0.1.5" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" [[package]] name = "constant_time_eq" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" +checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" [[package]] name = "convert_case" @@ -1498,12 +1560,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cpuid-bool" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" - [[package]] name = "cranelift-bforest" version = "0.93.1" @@ -1629,9 +1685,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -1639,9 +1695,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", @@ -1650,14 +1706,14 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ "autocfg", "cfg-if 1.0.0", "crossbeam-utils", - "memoffset 0.7.1", + "memoffset 0.8.0", "scopeguard", ] @@ -1673,9 +1729,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if 1.0.0", ] @@ -1699,30 +1755,33 @@ dependencies = [ ] [[package]] -name = "crypto-common" -version = "0.1.6" +name = "crypto-bigint" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" dependencies = [ "generic-array 0.14.6", - "typenum", + "rand_core 0.6.4", + "subtle", + "zeroize", ] [[package]] -name = "crypto-mac" -version = "0.8.0" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array 0.14.6", - "subtle", + "rand_core 0.6.4", + "typenum", ] [[package]] name = "crypto-mac" -version = "0.10.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ "generic-array 0.14.6", "subtle", @@ -1744,34 +1803,34 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ - "quote 1.0.23", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "ctr" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" dependencies = [ - "cipher 0.2.5", + "cipher 0.3.0", ] [[package]] name = "ctr" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher 0.3.0", + "cipher 0.4.4", ] [[package]] name = "cumulus-client-cli" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ - "clap 4.1.8", + "clap 4.1.11", "parity-scale-codec", "sc-chain-spec", "sc-cli", @@ -1784,7 +1843,7 @@ dependencies = [ [[package]] name = "cumulus-client-collator" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "cumulus-client-consensus-common", "cumulus-client-network", @@ -1807,7 +1866,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-aura" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "async-trait", "cumulus-client-consensus-common", @@ -1836,7 +1895,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-common" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "async-trait", "cumulus-client-pov-recovery", @@ -1860,7 +1919,7 @@ dependencies = [ [[package]] name = "cumulus-client-network" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "async-trait", "cumulus-relay-chain-interface", @@ -1883,7 +1942,7 @@ dependencies = [ [[package]] name = "cumulus-client-pov-recovery" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -1907,7 +1966,7 @@ dependencies = [ [[package]] name = "cumulus-client-service" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "cumulus-client-cli", "cumulus-client-collator", @@ -1923,7 +1982,6 @@ dependencies = [ "sc-client-api", "sc-consensus", "sc-network", - "sc-network-common", "sc-network-sync", "sc-network-transactions", "sc-rpc", @@ -1943,7 +2001,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-aura-ext" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "frame-support", "frame-system", @@ -1959,7 +2017,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-dmp-queue" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -1976,7 +2034,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-parachain-system" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "bytes", "cumulus-pallet-parachain-system-proc-macro", @@ -2005,18 +2063,18 @@ dependencies = [ [[package]] name = "cumulus-pallet-parachain-system-proc-macro" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "cumulus-pallet-xcm" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -2032,7 +2090,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-xcmp-queue" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -2052,7 +2110,7 @@ dependencies = [ [[package]] name = "cumulus-primitives-core" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "parity-scale-codec", "polkadot-core-primitives", @@ -2068,7 +2126,7 @@ dependencies = [ [[package]] name = "cumulus-primitives-parachain-inherent" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2091,7 +2149,7 @@ dependencies = [ [[package]] name = "cumulus-primitives-timestamp" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "cumulus-primitives-core", "futures", @@ -2104,7 +2162,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-inprocess-interface" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2129,7 +2187,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-interface" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2147,7 +2205,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-minimal-node" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "array-bytes 6.0.0", "async-trait", @@ -2181,7 +2239,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-rpc-interface" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2211,7 +2269,7 @@ dependencies = [ [[package]] name = "cumulus-test-relay-sproof-builder" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "cumulus-primitives-core", "parity-scale-codec", @@ -2238,9 +2296,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.59+curl-7.86.0" +version = "0.4.61+curl-8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cfce34829f448b08f55b7db6d0009e23e2e86a34e8c2b366269bf5799b4a407" +checksum = "14d05c10f541ae6f3bc5b3d923c20001f47db7d5f0b2bc6ad16490133842db79" dependencies = [ "cc", "libc", @@ -2280,9 +2338,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0-rc.0" +version = "4.0.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da00a7a9a4eb92a0a0f8e75660926d48f0d0f3c537e455c457bcdaa1e16b1ac" +checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16" dependencies = [ "cfg-if 1.0.0", "fiat-crypto", @@ -2294,9 +2352,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" +checksum = "a9c00419335c41018365ddf7e4d5f1c12ee3659ddcf3e01974650ba1de73d038" dependencies = [ "cc", "cxxbridge-flags", @@ -2306,41 +2364,41 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" +checksum = "fb8307ad413a98fff033c8545ecf133e3257747b3bae935e7602aab8aa92d4ca" dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "scratch", - "syn 1.0.109", + "syn 2.0.8", ] [[package]] name = "cxxbridge-flags" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" +checksum = "edc52e2eb08915cb12596d29d55f0b5384f00d697a646dbd269b6ecb0fbd9d31" [[package]] name = "cxxbridge-macro" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" +checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 2.0.8", ] [[package]] name = "darling" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ "darling_core", "darling_macro", @@ -2348,26 +2406,26 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "strsim 0.10.0", "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core", - "quote 1.0.23", + "quote 1.0.26", "syn 1.0.109", ] @@ -2408,6 +2466,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc906908ea6458456e5eaa160a9c08543ec3d1e6f71e2235cedd660cb65f9df0" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "der-parser" version = "7.0.0" @@ -2424,11 +2492,11 @@ dependencies = [ [[package]] name = "der-parser" -version = "8.1.0" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" dependencies = [ - "asn1-rs 0.5.1", + "asn1-rs 0.5.2", "displaydoc", "nom", "num-bigint", @@ -2442,8 +2510,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -2453,8 +2521,8 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -2474,8 +2542,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" dependencies = [ "darling", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -2496,8 +2564,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "rustc_version", "syn 1.0.109", ] @@ -2532,7 +2600,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.4", "crypto-common", "subtle", ] @@ -2584,8 +2652,8 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -2603,9 +2671,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "dtoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00704156a7de8df8da0911424e30c2049957b0a714542a44e05fe693dd85313" +checksum = "65d09067bfacaa79114679b279d7f5885b53295b1e2cfb4e79c8e4bd3d633169" [[package]] name = "dyn-clonable" @@ -2623,8 +2691,8 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -2640,10 +2708,22 @@ version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1b0a1222f8072619e8a6b667a854020a03d363738303203c09468b3424a420a" +dependencies = [ + "der 0.7.1", + "elliptic-curve 0.13.2", + "rfc6979 0.4.0", + "signature 2.0.0", ] [[package]] @@ -2652,7 +2732,7 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "signature", + "signature 1.6.4", ] [[package]] @@ -2695,18 +2775,37 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", - "der", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", "digest 0.10.6", - "ff", + "ff 0.12.1", "generic-array 0.14.6", - "group", + "group 0.12.1", "hkdf", "pem-rfc7468", - "pkcs8", + "pkcs8 0.9.0", "rand_core 0.6.4", - "sec1", + "sec1 0.3.0", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea5a92946e8614bb585254898bb7dd1ddad241ace60c52149e3765e34cc039d" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.1", + "digest 0.10.6", + "ff 0.13.0", + "generic-array 0.14.6", + "group 0.13.0", + "pkcs8 0.10.1", + "rand_core 0.6.4", + "sec1 0.7.1", "subtle", "zeroize", ] @@ -2727,8 +2826,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -2747,20 +2846,20 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "enumn" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88bcb3a067a6555d577aba299e75eff9942da276e6506fc6274327daa026132" +checksum = "48016319042fb7c87b78d2993084a831793a897a5cd1a2a67cab9d1eeb4b7d76" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 2.0.8", ] [[package]] @@ -2793,6 +2892,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "errno" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "errno-dragonfly" version = "0.1.2" @@ -2853,8 +2963,8 @@ checksum = "a718c0675c555c5f976fff4ea9e2c150fa06cefa201cadef87cfbf9324075881" dependencies = [ "blake3", "fs-err", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", ] [[package]] @@ -2865,8 +2975,21 @@ checksum = "3774182a5df13c3d1690311ad32fbe913feef26baba609fa2dd5f72042bd2ab6" dependencies = [ "blake2", "fs-err", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "expander" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -2884,9 +3007,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -2910,8 +3033,8 @@ dependencies = [ "expander 0.0.4", "indexmap", "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", "thiserror", ] @@ -2935,11 +3058,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "fiat-crypto" -version = "0.1.17" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a214f5bb88731d436478f3ae1f8a277b62124089ba9fb67f4f93fb100ef73c90" +checksum = "93ace6ec7cc19c8ed33a32eaa9ea692d7faea05006b5356b9e2b668ec4bc3955" [[package]] name = "file-per-thread-logger" @@ -2953,21 +3086,21 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" +checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "finality-grandpa" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24e6c429951433ccb7c87fd528c60084834dcd14763182c1f83291bcde24c34" +checksum = "36530797b9bf31cd4ff126dcfee8170f86b00cfdcea3269d73133cc0415945c3" dependencies = [ "either", "futures", @@ -3041,7 +3174,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", ] @@ -3064,7 +3197,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-support", "frame-support-procedural", @@ -3089,12 +3222,12 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "Inflector", "array-bytes 4.2.0", "chrono", - "clap 4.1.8", + "clap 4.1.11", "comfy-table", "frame-benchmarking", "frame-support", @@ -3136,18 +3269,18 @@ dependencies = [ [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-election-provider-solution-type", "frame-support", @@ -3164,7 +3297,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-support", "frame-system", @@ -3192,7 +3325,7 @@ dependencies = [ [[package]] name = "frame-remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "futures", "log", @@ -3208,9 +3341,9 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ - "bitflags", + "bitflags 1.3.2", "environmental", "frame-metadata", "frame-support-procedural", @@ -3241,44 +3374,44 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "Inflector", "cfg-expr", "derive-syn-parse", "frame-support-procedural-tools", "itertools", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-support", "log", @@ -3296,7 +3429,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -3311,7 +3444,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", "sp-api", @@ -3320,7 +3453,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-support", "parity-scale-codec", @@ -3432,8 +3565,8 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -3510,6 +3643,7 @@ checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -3548,22 +3682,22 @@ dependencies = [ [[package]] name = "ghash" -version = "0.3.1" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" dependencies = [ "opaque-debug 0.3.0", - "polyval 0.4.5", + "polyval 0.5.3", ] [[package]] name = "ghash" -version = "0.4.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" dependencies = [ "opaque-debug 0.3.0", - "polyval 0.5.3", + "polyval 0.6.0", ] [[package]] @@ -3579,9 +3713,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" [[package]] name = "glob" @@ -3620,16 +3754,27 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", "rand_core 0.6.4", "subtle", ] [[package]] name = "h2" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" dependencies = [ "bytes", "fnv", @@ -3660,9 +3805,9 @@ dependencies = [ [[package]] name = "hash-db" -version = "0.15.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" +checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" [[package]] name = "hash256-std-hasher" @@ -3726,9 +3871,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "hex" @@ -3761,16 +3906,6 @@ dependencies = [ "digest 0.9.0", ] -[[package]] -name = "hmac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" -dependencies = [ - "crypto-mac 0.10.1", - "digest 0.9.0", -] - [[package]] name = "hmac" version = "0.11.0" @@ -3826,9 +3961,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -3872,9 +4007,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" dependencies = [ "bytes", "futures-channel", @@ -3912,16 +4047,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows 0.46.0", ] [[package]] @@ -3987,7 +4122,7 @@ dependencies = [ "rtnetlink", "system-configuration", "tokio", - "windows", + "windows 0.34.0", ] [[package]] @@ -4023,8 +4158,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -4039,6 +4174,15 @@ dependencies = [ "serde", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.6", +] + [[package]] name = "instant" version = "0.1.12" @@ -4090,10 +4234,11 @@ checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" [[package]] name = "io-lifetimes" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" dependencies = [ + "hermit-abi 0.3.1", "libc", "windows-sys 0.45.0", ] @@ -4124,13 +4269,13 @@ checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "is-terminal" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" dependencies = [ - "hermit-abi 0.3.0", - "io-lifetimes 1.0.5", - "rustix 0.36.8", + "hermit-abi 0.3.1", + "io-lifetimes 1.0.9", + "rustix 0.36.11", "windows-sys 0.45.0", ] @@ -4172,15 +4317,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] @@ -4297,8 +4442,8 @@ checksum = "baa6da1e4199c10d7b1d0a6e5e8bd8e55f351163b6f4b3cbb044672a69bd4c1c" dependencies = [ "heck 0.4.1", "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -4352,13 +4497,14 @@ dependencies = [ [[package]] name = "k256" -version = "0.11.6" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +checksum = "955890845095ccf31ef83ad41a05aabb4d8cc23dc3cac5a9f5c89cf26dd0da75" dependencies = [ "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.1", + "elliptic-curve 0.13.2", + "once_cell", "sha2 0.10.6", ] @@ -4427,9 +4573,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "libloading" @@ -4465,16 +4611,16 @@ dependencies = [ [[package]] name = "libp2p" -version = "0.50.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e0a0d2f693675f49ded13c5d510c48b78069e23cbd9108d7ccd59f6dc568819" +checksum = "9c7b0104790be871edcf97db9bd2356604984e623a08d825c3f27852290266b8" dependencies = [ "bytes", "futures", "futures-timer", "getrandom 0.2.8", "instant", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-dns", "libp2p-identify", "libp2p-kad", @@ -4491,7 +4637,7 @@ dependencies = [ "libp2p-webrtc", "libp2p-websocket", "libp2p-yamux", - "multiaddr", + "multiaddr 0.16.0", "parking_lot 0.12.1", "pin-project", "smallvec", @@ -4512,8 +4658,8 @@ dependencies = [ "futures-timer", "instant", "log", - "multiaddr", - "multihash", + "multiaddr 0.16.0", + "multihash 0.16.3", "multistream-select", "once_cell", "parking_lot 0.12.1", @@ -4522,7 +4668,7 @@ dependencies = [ "prost-build", "rand 0.8.5", "rw-stream-sink", - "sec1", + "sec1 0.3.0", "sha2 0.10.6", "smallvec", "thiserror", @@ -4531,6 +4677,34 @@ dependencies = [ "zeroize", ] +[[package]] +name = "libp2p-core" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7f8b7d65c070a5a1b5f8f0510648189da08f787b8963f8e21219e0710733af" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-identity", + "log", + "multiaddr 0.17.1", + "multihash 0.17.0", + "multistream-select", + "once_cell", + "parking_lot 0.12.1", + "pin-project", + "quick-protobuf", + "rand 0.8.5", + "rw-stream-sink", + "smallvec", + "thiserror", + "unsigned-varint", + "void", +] + [[package]] name = "libp2p-dns" version = "0.38.0" @@ -4538,7 +4712,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e42a271c1b49f789b92f7fc87749fa79ce5c7bdc88cbdfacb818a4bca47fec5" dependencies = [ "futures", - "libp2p-core", + "libp2p-core 0.38.0", "log", "parking_lot 0.12.1", "smallvec", @@ -4554,7 +4728,7 @@ dependencies = [ "asynchronous-codec", "futures", "futures-timer", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-swarm", "log", "lru 0.8.1", @@ -4566,6 +4740,24 @@ dependencies = [ "void", ] +[[package]] +name = "libp2p-identity" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a8ea433ae0cea7e3315354305237b9897afe45278b2118a7a57ca744e70fd27" +dependencies = [ + "bs58", + "ed25519-dalek", + "log", + "multiaddr 0.17.1", + "multihash 0.17.0", + "prost", + "quick-protobuf", + "rand 0.8.5", + "thiserror", + "zeroize", +] + [[package]] name = "libp2p-kad" version = "0.42.1" @@ -4580,7 +4772,7 @@ dependencies = [ "futures", "futures-timer", "instant", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-swarm", "log", "prost", @@ -4603,7 +4795,7 @@ dependencies = [ "data-encoding", "futures", "if-watch", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-swarm", "log", "rand 0.8.5", @@ -4620,7 +4812,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ad8a64f29da86005c86a4d2728b8a0719e9b192f4092b609fd8790acb9dec55" dependencies = [ - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-identify", "libp2p-kad", "libp2p-ping", @@ -4637,7 +4829,7 @@ dependencies = [ "asynchronous-codec", "bytes", "futures", - "libp2p-core", + "libp2p-core 0.38.0", "log", "nohash-hasher", "parking_lot 0.12.1", @@ -4655,7 +4847,7 @@ dependencies = [ "bytes", "curve25519-dalek 3.2.0", "futures", - "libp2p-core", + "libp2p-core 0.38.0", "log", "once_cell", "prost", @@ -4678,7 +4870,7 @@ dependencies = [ "futures", "futures-timer", "instant", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-swarm", "log", "rand 0.8.5", @@ -4695,7 +4887,7 @@ dependencies = [ "futures", "futures-timer", "if-watch", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-tls", "log", "parking_lot 0.12.1", @@ -4716,7 +4908,7 @@ dependencies = [ "bytes", "futures", "instant", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-swarm", "log", "rand 0.8.5", @@ -4735,7 +4927,7 @@ dependencies = [ "futures", "futures-timer", "instant", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-swarm-derive", "log", "pin-project", @@ -4753,7 +4945,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d527d5827582abd44a6d80c07ff8b50b4ee238a8979e05998474179e79dc400" dependencies = [ "heck 0.4.1", - "quote 1.0.23", + "quote 1.0.26", "syn 1.0.109", ] @@ -4767,7 +4959,7 @@ dependencies = [ "futures-timer", "if-watch", "libc", - "libp2p-core", + "libp2p-core 0.38.0", "log", "socket2", "tokio", @@ -4775,13 +4967,14 @@ dependencies = [ [[package]] name = "libp2p-tls" -version = "0.1.0-alpha" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7905ce0d040576634e8a3229a7587cc8beab83f79db6023800f1792895defa8" +checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781" dependencies = [ "futures", "futures-rustls", - "libp2p-core", + "libp2p-core 0.39.1", + "libp2p-identity", "rcgen 0.10.0", "ring", "rustls 0.20.8", @@ -4799,7 +4992,7 @@ checksum = "1bb1a35299860e0d4b3c02a3e74e3b293ad35ae0cee8a056363b0c862d082069" dependencies = [ "futures", "js-sys", - "libp2p-core", + "libp2p-core 0.38.0", "parity-send-wrapper", "wasm-bindgen", "wasm-bindgen-futures", @@ -4818,10 +5011,10 @@ dependencies = [ "futures-timer", "hex", "if-watch", - "libp2p-core", + "libp2p-core 0.38.0", "libp2p-noise", "log", - "multihash", + "multihash 0.16.3", "prost", "prost-build", "prost-codec", @@ -4845,7 +5038,7 @@ dependencies = [ "either", "futures", "futures-rustls", - "libp2p-core", + "libp2p-core 0.38.0", "log", "parking_lot 0.12.1", "quicksink", @@ -4862,7 +5055,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f63594a0aa818642d9d4915c791945053877253f08a3626f13416b5cd928a29" dependencies = [ "futures", - "libp2p-core", + "libp2p-core 0.38.0", "log", "parking_lot 0.12.1", "thiserror", @@ -4871,9 +5064,9 @@ dependencies = [ [[package]] name = "librocksdb-sys" -version = "0.8.0+7.4.4" +version = "0.8.3+7.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611804e4666a25136fcc5f8cf425ab4d26c7f74ea245ffe92ea23b85b6420b5d" +checksum = "557b255ff04123fcc176162f56ed0c9cd42d8f357cf55b3fabeb60f7413741b3" dependencies = [ "bindgen", "bzip2-sys", @@ -4989,6 +5182,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "linux-raw-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd550e73688e6d578f0ac2119e32b797a327631a42f9433e59d02e139c8df60d" + [[package]] name = "lock_api" version = "0.4.9" @@ -5116,14 +5315,14 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" dependencies = [ - "rustix 0.36.8", + "rustix 0.36.11", ] [[package]] name = "memmap2" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ "libc", ] @@ -5139,21 +5338,20 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ "autocfg", ] [[package]] name = "memory-db" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" +checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" dependencies = [ "hash-db", - "hashbrown 0.12.3", ] [[package]] @@ -5206,7 +5404,7 @@ dependencies = [ name = "millau-bridge-node" version = "0.1.0" dependencies = [ - "clap 4.1.8", + "clap 4.1.11", "frame-benchmarking", "frame-benchmarking-cli", "jsonrpsee", @@ -5304,9 +5502,9 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minimal-lexical" @@ -5325,20 +5523,20 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "mmr-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "futures", "log", @@ -5357,7 +5555,7 @@ dependencies = [ [[package]] name = "mmr-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "anyhow", "jsonrpsee", @@ -5392,8 +5590,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "832663583d5fa284ca8810bf7015e46c9fff9622d3cf34bd1eea5003fec06dd0" dependencies = [ "cfg-if 1.0.0", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -5407,7 +5605,26 @@ dependencies = [ "byteorder", "data-encoding", "multibase", - "multihash", + "multihash 0.16.3", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multiaddr" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "log", + "multibase", + "multihash 0.17.0", "percent-encoding", "serde", "static_assertions", @@ -5443,6 +5660,19 @@ dependencies = [ "unsigned-varint", ] +[[package]] +name = "multihash" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" +dependencies = [ + "core2", + "digest 0.10.6", + "multihash-derive", + "sha2 0.10.6", + "unsigned-varint", +] + [[package]] name = "multihash-derive" version = "0.8.0" @@ -5451,8 +5681,8 @@ checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" dependencies = [ "proc-macro-crate", "proc-macro-error", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", "synstructure", ] @@ -5479,9 +5709,9 @@ dependencies = [ [[package]] name = "nalgebra" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6515c882ebfddccaa73ead7320ca28036c4bc84c9bcca3cc0cbba8efe89223a" +checksum = "d68d47bba83f9e2006d117a9a33af1524e655516b8919caac694427a6fb1e511" dependencies = [ "approx", "matrixmultiply", @@ -5499,8 +5729,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -5538,7 +5768,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" dependencies = [ "anyhow", - "bitflags", + "bitflags 1.3.2", "byteorder", "libc", "netlink-packet-core", @@ -5574,9 +5804,9 @@ dependencies = [ [[package]] name = "netlink-sys" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260e21fbb6f3d253a14df90eb0000a6066780a15dd901a7519ce02d77a94985b" +checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" dependencies = [ "bytes", "futures", @@ -5591,7 +5821,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if 1.0.0", "libc", "memoffset 0.6.5", @@ -5600,9 +5830,9 @@ dependencies = [ [[package]] name = "node-inspect" version = "0.9.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ - "clap 4.1.8", + "clap 4.1.11", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -5761,14 +5991,14 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" dependencies = [ - "asn1-rs 0.5.1", + "asn1-rs 0.5.2", ] [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "opaque-debug" @@ -5790,9 +6020,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.80" +version = "0.9.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +checksum = "a95792af3c4e0153c3914df2261bedd30a98476f94dc892b67dfe1d89d433a04" dependencies = [ "autocfg", "cc", @@ -5828,8 +6058,8 @@ dependencies = [ "itertools", "petgraph", "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -5844,9 +6074,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "p256" @@ -5854,8 +6084,8 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ - "ecdsa", - "elliptic-curve", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", "sha2 0.10.6", ] @@ -5865,8 +6095,8 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" dependencies = [ - "ecdsa", - "elliptic-curve", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", "sha2 0.10.6", ] @@ -5883,7 +6113,7 @@ dependencies = [ [[package]] name = "pallet-aura" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-support", "frame-system", @@ -5899,7 +6129,7 @@ dependencies = [ [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-support", "frame-system", @@ -5915,7 +6145,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-support", "frame-system", @@ -5929,7 +6159,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -5953,7 +6183,7 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -5973,7 +6203,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -5988,7 +6218,7 @@ dependencies = [ [[package]] name = "pallet-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-support", "frame-system", @@ -6007,7 +6237,7 @@ dependencies = [ [[package]] name = "pallet-beefy-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "array-bytes 4.2.0", "binary-merkle-tree", @@ -6031,7 +6261,7 @@ dependencies = [ [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6161,7 +6391,7 @@ dependencies = [ [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6180,7 +6410,7 @@ dependencies = [ [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6194,10 +6424,27 @@ dependencies = [ "sp-std 5.0.0", ] +[[package]] +name = "pallet-conviction-voting" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +dependencies = [ + "assert_matches", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6215,7 +6462,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6238,7 +6485,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-support-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6251,7 +6498,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6269,7 +6516,7 @@ dependencies = [ [[package]] name = "pallet-fast-unstake" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6287,7 +6534,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6310,7 +6557,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "enumflags2", "frame-benchmarking", @@ -6326,7 +6573,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6346,7 +6593,7 @@ dependencies = [ [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6363,7 +6610,7 @@ dependencies = [ [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6380,7 +6627,7 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6397,7 +6644,7 @@ dependencies = [ [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6413,7 +6660,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-support", "frame-system", @@ -6430,7 +6677,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "pallet-nomination-pools", "parity-scale-codec", @@ -6441,7 +6688,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-support", "frame-system", @@ -6458,7 +6705,7 @@ dependencies = [ [[package]] name = "pallet-preimage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6473,15 +6720,33 @@ dependencies = [ ] [[package]] -name = "pallet-proxy" +name = "pallet-proxy" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-referenda" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "log", "parity-scale-codec", "scale-info", + "serde", + "sp-arithmetic", "sp-io", "sp-runtime", "sp-std 5.0.0", @@ -6490,7 +6755,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6507,7 +6772,7 @@ dependencies = [ [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-support", "frame-system", @@ -6543,7 +6808,7 @@ dependencies = [ [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6565,18 +6830,18 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "log", "sp-arithmetic", @@ -6585,7 +6850,7 @@ dependencies = [ [[package]] name = "pallet-staking-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", "sp-api", @@ -6594,7 +6859,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-support", "frame-system", @@ -6608,7 +6873,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6626,7 +6891,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6645,7 +6910,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-support", "frame-system", @@ -6661,7 +6926,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", @@ -6677,7 +6942,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -6689,7 +6954,7 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6706,7 +6971,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6722,7 +6987,7 @@ dependencies = [ [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-benchmarking", "frame-support", @@ -6734,10 +6999,25 @@ dependencies = [ "sp-std 5.0.0", ] +[[package]] +name = "pallet-whitelist" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-runtime", + "sp-std 5.0.0", +] + [[package]] name = "pallet-xcm" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bounded-collections", "frame-benchmarking", @@ -6758,7 +7038,7 @@ dependencies = [ [[package]] name = "parachain-info" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#836f13e24c74fcc0fe70eeeffd88214c88bf2316" +source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -6784,9 +7064,9 @@ dependencies = [ [[package]] name = "parity-db" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df89dd8311063c54ae4e03d9aeb597b04212a57e82c339344130a9cad9b3e2d9" +checksum = "00bfb81cf5c90a222db2fb7b3a7cbf8cc7f38dfb6647aca4d98edf8281f56ed5" dependencies = [ "blake2", "crc32fast", @@ -6824,8 +7104,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -6859,7 +7139,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "proc-macro2 1.0.51", + "proc-macro2 1.0.53", "syn 1.0.109", "synstructure", ] @@ -6926,9 +7206,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[package]] name = "pbkdf2" @@ -6980,9 +7260,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.4" +version = "2.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" +checksum = "8cbd939b234e95d72bc393d51788aec68aeeb5d51e748ca08ff3aad58cb722f7" dependencies = [ "thiserror", "ucd-trie", @@ -6990,9 +7270,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.4" +version = "2.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea" +checksum = "a81186863f3d0a27340815be8f2078dd8050b14cd71913db9fbda795e5f707d7" dependencies = [ "pest", "pest_generator", @@ -7000,22 +7280,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.4" +version = "2.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f" +checksum = "75a1ef20bf3193c15ac345acb32e26b3dc3223aff4d77ae4fc5359567683796b" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "pest_meta" -version = "2.5.4" +version = "2.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d" +checksum = "5e3b284b1f13a20dc5ebc90aff59a51b8d7137c221131b52a7260c08cbc1cc80" dependencies = [ "once_cell", "pest", @@ -7024,9 +7304,9 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", "indexmap", @@ -7047,8 +7327,8 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -7076,8 +7356,18 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ - "der", - "spki", + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d2820d87d2b008616e5c27212dd9e0e694fb4c6b522de06094106813328cb49" +dependencies = [ + "der 0.7.1", + "spki 0.7.0", ] [[package]] @@ -7100,8 +7390,8 @@ checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" [[package]] name = "polkadot-approval-distribution" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "polkadot-node-metrics", @@ -7115,8 +7405,8 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "polkadot-node-network-protocol", @@ -7129,8 +7419,8 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "derive_more", "fatality", @@ -7152,8 +7442,8 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "fatality", "futures", @@ -7173,10 +7463,10 @@ dependencies = [ [[package]] name = "polkadot-cli" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ - "clap 4.1.8", + "clap 4.1.11", "frame-benchmarking-cli", "futures", "log", @@ -7200,8 +7490,8 @@ dependencies = [ [[package]] name = "polkadot-client" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "frame-benchmarking", @@ -7242,8 +7532,8 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "always-assert", "bitvec", @@ -7264,8 +7554,8 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "parity-scale-codec", "scale-info", @@ -7276,8 +7566,8 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "derive_more", "fatality", @@ -7301,8 +7591,8 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", @@ -7315,8 +7605,8 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "futures-timer", @@ -7335,8 +7625,8 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "always-assert", "async-trait", @@ -7351,7 +7641,6 @@ dependencies = [ "polkadot-overseer", "polkadot-primitives", "sc-network", - "sc-network-common", "sp-consensus", "thiserror", "tracing-gum", @@ -7359,8 +7648,8 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "parity-scale-codec", @@ -7377,8 +7666,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "derive_more", @@ -7406,8 +7695,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "futures", @@ -7427,8 +7716,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "fatality", @@ -7446,8 +7735,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "polkadot-node-subsystem", @@ -7461,8 +7750,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "futures", @@ -7481,8 +7770,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "polkadot-node-metrics", @@ -7496,8 +7785,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "futures-timer", @@ -7513,8 +7802,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "fatality", "futures", @@ -7532,8 +7821,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "futures", @@ -7549,8 +7838,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "fatality", @@ -7567,8 +7856,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "always-assert", "assert_matches", @@ -7603,8 +7892,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-checker" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "polkadot-node-primitives", @@ -7619,8 +7908,8 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "futures", "lru 0.9.0", @@ -7634,8 +7923,8 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "lazy_static", "log", @@ -7652,8 +7941,8 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bs58", "futures", @@ -7671,8 +7960,8 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "derive_more", @@ -7686,7 +7975,6 @@ dependencies = [ "rand 0.8.5", "sc-authority-discovery", "sc-network", - "sc-network-common", "strum", "thiserror", "tracing-gum", @@ -7694,8 +7982,8 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bounded-vec", "futures", @@ -7717,8 +8005,8 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -7727,8 +8015,8 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "derive_more", @@ -7750,8 +8038,8 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "derive_more", @@ -7783,8 +8071,8 @@ dependencies = [ [[package]] name = "polkadot-overseer" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "futures", @@ -7806,8 +8094,8 @@ dependencies = [ [[package]] name = "polkadot-parachain" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bounded-collections", "derive_more", @@ -7823,8 +8111,8 @@ dependencies = [ [[package]] name = "polkadot-primitives" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "hex-literal", @@ -7849,8 +8137,8 @@ dependencies = [ [[package]] name = "polkadot-rpc" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "jsonrpsee", "mmr-rpc", @@ -7881,8 +8169,8 @@ dependencies = [ [[package]] name = "polkadot-runtime" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "frame-election-provider-support", @@ -7900,6 +8188,7 @@ dependencies = [ "pallet-bounties", "pallet-child-bounties", "pallet-collective", + "pallet-conviction-voting", "pallet-democracy", "pallet-election-provider-multi-phase", "pallet-elections-phragmen", @@ -7915,6 +8204,7 @@ dependencies = [ "pallet-offences", "pallet-preimage", "pallet-proxy", + "pallet-referenda", "pallet-scheduler", "pallet-session", "pallet-staking", @@ -7927,6 +8217,7 @@ dependencies = [ "pallet-treasury", "pallet-utility", "pallet-vesting", + "pallet-whitelist", "pallet-xcm", "parity-scale-codec", "polkadot-primitives", @@ -7939,6 +8230,7 @@ dependencies = [ "serde_derive", "smallvec", "sp-api", + "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-babe", @@ -7964,8 +8256,8 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", "frame-election-provider-support", @@ -8008,8 +8300,8 @@ dependencies = [ [[package]] name = "polkadot-runtime-constants" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "frame-support", "polkadot-primitives", @@ -8022,8 +8314,8 @@ dependencies = [ [[package]] name = "polkadot-runtime-metrics" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bs58", "parity-scale-codec", @@ -8034,10 +8326,10 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ - "bitflags", + "bitflags 1.3.2", "bitvec", "derive_more", "frame-support", @@ -8075,8 +8367,8 @@ dependencies = [ [[package]] name = "polkadot-service" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "async-trait", "frame-benchmarking-cli", @@ -8181,8 +8473,8 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "arrayvec 0.5.2", "fatality", @@ -8202,8 +8494,8 @@ dependencies = [ [[package]] name = "polkadot-statement-table" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -8212,16 +8504,18 @@ dependencies = [ [[package]] name = "polling" -version = "2.5.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" +checksum = "7e1f879b2998099c2d69ab9605d145d5b661195627eccc680002c4918a7fb6fa" dependencies = [ "autocfg", + "bitflags 1.3.2", "cfg-if 1.0.0", + "concurrent-queue", "libc", "log", - "wepoll-ffi", - "windows-sys 0.42.0", + "pin-project-lite 0.2.9", + "windows-sys 0.45.0", ] [[package]] @@ -8232,30 +8526,31 @@ checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" dependencies = [ "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.4.1", ] [[package]] name = "polyval" -version = "0.4.5" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" dependencies = [ - "cpuid-bool", + "cfg-if 1.0.0", + "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.4.1", ] [[package]] name = "polyval" -version = "0.5.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" dependencies = [ "cfg-if 1.0.0", "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.5.0", ] [[package]] @@ -8280,15 +8575,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ "predicates-core", "termtree", @@ -8296,11 +8591,11 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.1.23" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ - "proc-macro2 1.0.51", + "proc-macro2 1.0.53", "syn 1.0.109", ] @@ -8351,8 +8646,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", "version_check", ] @@ -8363,8 +8658,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "version_check", ] @@ -8379,9 +8674,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" dependencies = [ "unicode-ident", ] @@ -8418,16 +8713,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "prost" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dc42e00223fc37204bd4aa177e69420c604ca4a183209a8f9de30c6d934698" +checksum = "e48e50df39172a3e7eb17e14642445da64996989bc212b583015435d39a58537" dependencies = [ "bytes", "prost-derive", @@ -8435,9 +8730,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f8ad728fb08fe212df3c05169e940fbb6d9d16a877ddde14644a983ba2012e" +checksum = "2c828f93f5ca4826f97fedcbd3f9a536c16b12cff3dbbb4a007f932bbad95b12" dependencies = [ "bytes", "heck 0.4.1", @@ -8470,24 +8765,23 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda8c0881ea9f722eb9629376db3d0b903b462477c1aafcb0566610ac28ac5d" +checksum = "4ea9b0f8cbe5e15a8a042d030bd96668db28ecb567ec37d691971ff5731d2b1b" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "prost-types" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e0526209433e96d83d750dd81a99118edbc55739e7e61a46764fd2ad537788" +checksum = "379119666929a1afd7a043aa6cf96fa67a6dce9af60c88095a4686dbce4c9c88" dependencies = [ - "bytes", "prost", ] @@ -8506,6 +8800,15 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-protobuf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder", +] + [[package]] name = "quicksink" version = "0.1.2" @@ -8546,11 +8849,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ - "proc-macro2 1.0.51", + "proc-macro2 1.0.53", ] [[package]] @@ -8647,9 +8950,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", @@ -8657,9 +8960,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.2" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -8718,7 +9021,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -8747,22 +9050,22 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c78fb8c9293bcd48ef6fce7b4ca950ceaf21210de6e105a883ee280c0f7b9ed" +checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f" +checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 2.0.8", ] [[package]] @@ -8779,9 +9082,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" dependencies = [ "aho-corasick", "memchr", @@ -8799,9 +9102,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "region" @@ -8809,12 +9112,52 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "mach", "winapi", ] +[[package]] +name = "relay-bridge-hub-kusama-client" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-kusama", + "bp-bridge-hub-polkadot", + "bp-header-chain", + "bp-messages", + "bp-parachains", + "bp-polkadot", + "bp-runtime", + "bridge-runtime-common", + "parity-scale-codec", + "relay-substrate-client", + "scale-info", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "relay-bridge-hub-polkadot-client" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-kusama", + "bp-bridge-hub-polkadot", + "bp-header-chain", + "bp-kusama", + "bp-messages", + "bp-parachains", + "bp-polkadot-core", + "bp-runtime", + "bridge-runtime-common", + "parity-scale-codec", + "relay-substrate-client", + "scale-info", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", +] + [[package]] name = "relay-bridge-hub-rococo-client" version = "0.1.0" @@ -8917,6 +9260,7 @@ dependencies = [ name = "relay-rialto-parachain-client" version = "0.1.0" dependencies = [ + "bp-bridge-hub-cumulus", "bp-header-chain", "bp-messages", "bp-millau", @@ -9047,16 +9391,26 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.4.9", "hmac 0.12.1", "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + [[package]] name = "rialto-bridge-node" version = "0.1.0" dependencies = [ - "clap 4.1.8", + "clap 4.1.11", "frame-benchmarking", "frame-benchmarking-cli", "frame-support", @@ -9083,7 +9437,7 @@ dependencies = [ name = "rialto-parachain-collator" version = "0.1.0" dependencies = [ - "clap 4.1.8", + "clap 4.1.11", "cumulus-client-cli", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", @@ -9346,9 +9700,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" [[package]] name = "rustc-hash" @@ -9368,7 +9722,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.16", + "semver 1.0.17", ] [[package]] @@ -9386,8 +9740,8 @@ version = "0.35.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" dependencies = [ - "bitflags", - "errno", + "bitflags 1.3.2", + "errno 0.2.8", "io-lifetimes 0.7.5", "libc", "linux-raw-sys 0.0.46", @@ -9396,18 +9750,32 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.8" +version = "0.36.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" dependencies = [ - "bitflags", - "errno", - "io-lifetimes 1.0.5", + "bitflags 1.3.2", + "errno 0.2.8", + "io-lifetimes 1.0.9", "libc", "linux-raw-sys 0.1.4", "windows-sys 0.45.0", ] +[[package]] +name = "rustix" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2" +dependencies = [ + "bitflags 1.3.2", + "errno 0.3.0", + "io-lifetimes 1.0.9", + "libc", + "linux-raw-sys 0.3.0", + "windows-sys 0.45.0", +] + [[package]] name = "rustls" version = "0.19.1" @@ -9456,9 +9824,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" [[package]] name = "rw-stream-sink" @@ -9473,9 +9841,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "safe_arch" @@ -9498,7 +9866,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "log", "sp-core", @@ -9509,7 +9877,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "futures", @@ -9522,6 +9890,7 @@ dependencies = [ "prost-build", "rand 0.8.5", "sc-client-api", + "sc-network", "sc-network-common", "sp-api", "sp-authority-discovery", @@ -9536,7 +9905,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "futures", "futures-timer", @@ -9559,7 +9928,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -9574,13 +9943,13 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "memmap2", "sc-chain-spec-derive", "sc-client-api", "sc-executor", - "sc-network-common", + "sc-network", "sc-telemetry", "serde", "serde_json", @@ -9593,22 +9962,22 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "array-bytes 4.2.0", "chrono", - "clap 4.1.8", + "clap 4.1.11", "fdlimit", "futures", "libp2p", @@ -9644,7 +10013,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "fnv", "futures", @@ -9670,7 +10039,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "hash-db", "kvdb", @@ -9696,7 +10065,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "futures", @@ -9721,7 +10090,7 @@ dependencies = [ [[package]] name = "sc-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "futures", @@ -9750,7 +10119,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "fork-tree", @@ -9789,7 +10158,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "futures", "jsonrpsee", @@ -9811,7 +10180,7 @@ dependencies = [ [[package]] name = "sc-consensus-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -9846,7 +10215,7 @@ dependencies = [ [[package]] name = "sc-consensus-beefy-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "futures", "jsonrpsee", @@ -9865,7 +10234,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "fork-tree", "parity-scale-codec", @@ -9878,7 +10247,7 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "ahash 0.8.3", "array-bytes 4.2.0", @@ -9918,7 +10287,7 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "finality-grandpa", "futures", @@ -9938,7 +10307,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "futures", @@ -9961,7 +10330,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "lru 0.8.1", "parity-scale-codec", @@ -9985,7 +10354,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", @@ -9998,7 +10367,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "log", "sc-allocator", @@ -10011,14 +10380,14 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "anyhow", "cfg-if 1.0.0", "libc", "log", "once_cell", - "rustix 0.36.8", + "rustix 0.36.11", "sc-allocator", "sc-executor-common", "sp-runtime-interface", @@ -10029,13 +10398,14 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "ansi_term", "futures", "futures-timer", "log", "sc-client-api", + "sc-network", "sc-network-common", "sp-blockchain", "sp-runtime", @@ -10044,7 +10414,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10059,7 +10429,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "array-bytes 4.2.0", "async-channel", @@ -10072,6 +10442,7 @@ dependencies = [ "futures-timer", "ip_network", "libp2p", + "linked_hash_set", "log", "lru 0.8.1", "mockall", @@ -10102,7 +10473,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "cid", "futures", @@ -10111,6 +10482,7 @@ dependencies = [ "prost", "prost-build", "sc-client-api", + "sc-network", "sc-network-common", "sp-blockchain", "sp-runtime", @@ -10121,16 +10493,15 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "array-bytes 4.2.0", "async-trait", - "bitflags", + "bitflags 1.3.2", "bytes", "futures", "futures-timer", "libp2p", - "linked_hash_set", "parity-scale-codec", "prost-build", "sc-consensus", @@ -10150,7 +10521,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "ahash 0.8.3", "futures", @@ -10158,6 +10529,7 @@ dependencies = [ "libp2p", "log", "lru 0.8.1", + "sc-network", "sc-network-common", "sc-peerset", "sp-runtime", @@ -10168,7 +10540,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10178,6 +10550,7 @@ dependencies = [ "prost", "prost-build", "sc-client-api", + "sc-network", "sc-network-common", "sc-peerset", "sp-blockchain", @@ -10189,7 +10562,7 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10205,6 +10578,7 @@ dependencies = [ "prost-build", "sc-client-api", "sc-consensus", + "sc-network", "sc-network-common", "sc-peerset", "sc-utils", @@ -10222,7 +10596,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10230,6 +10604,7 @@ dependencies = [ "log", "parity-scale-codec", "pin-project", + "sc-network", "sc-network-common", "sc-peerset", "sc-utils", @@ -10241,7 +10616,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "array-bytes 4.2.0", "bytes", @@ -10257,6 +10632,7 @@ dependencies = [ "parking_lot 0.12.1", "rand 0.8.5", "sc-client-api", + "sc-network", "sc-network-common", "sc-peerset", "sc-utils", @@ -10271,7 +10647,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "futures", "libp2p", @@ -10284,7 +10660,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -10293,7 +10669,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "futures", "jsonrpsee", @@ -10323,7 +10699,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -10342,7 +10718,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "http", "jsonrpsee", @@ -10357,7 +10733,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10383,7 +10759,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "directories", @@ -10449,7 +10825,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "log", "parity-scale-codec", @@ -10460,9 +10836,9 @@ dependencies = [ [[package]] name = "sc-storage-monitor" version = "0.1.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ - "clap 4.1.8", + "clap 4.1.11", "fs4", "futures", "log", @@ -10476,7 +10852,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -10495,7 +10871,7 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "futures", "libc", @@ -10514,7 +10890,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "chrono", "futures", @@ -10533,7 +10909,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "ansi_term", "atty", @@ -10564,18 +10940,18 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "futures", @@ -10602,7 +10978,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "futures", @@ -10616,7 +10992,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-channel", "futures", @@ -10653,9 +11029,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608" +checksum = "61471dff9096de1d8b2319efed7162081e96793f5ebb147e50db10d50d648a4d" dependencies = [ "bitvec", "cfg-if 1.0.0", @@ -10667,13 +11043,13 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" +checksum = "219580e803a66b3f05761fd06f1f879a872444e49ce23f73694d26e5a954c7e6" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -10740,9 +11116,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scratch" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "sct" @@ -10782,10 +11158,24 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct", - "der", + "base16ct 0.1.1", + "der 0.6.1", + "generic-array 0.14.6", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.1", "generic-array 0.14.6", - "pkcs8", + "pkcs8 0.10.1", "subtle", "zeroize", ] @@ -10823,7 +11213,7 @@ version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -10851,9 +11241,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" dependencies = [ "serde", ] @@ -10866,22 +11256,22 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.155" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71f2b4817415c6d4210bfe1c7bfcf4801b2d904cb4d0e1a8fdb651013c9e86b8" +checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.155" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d071a94a3fac4aff69d023a7f411e33f40f3483f8c5190b1953822b6b76d7630" +checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 2.0.8", ] [[package]] @@ -10909,6 +11299,17 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.6", +] + [[package]] name = "sha2" version = "0.8.2" @@ -10994,9 +11395,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -11011,6 +11412,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "signature" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.4", +] + [[package]] name = "simba" version = "0.8.0" @@ -11032,9 +11443,9 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -11047,8 +11458,8 @@ checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" [[package]] name = "slot-range-helper" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "enumn", "parity-scale-codec", @@ -11091,14 +11502,14 @@ checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" [[package]] name = "snow" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ba5f4d4ff12bdb6a169ed51b7c48c0e0ac4b0b4b31012b2571e97d78d3201d" +checksum = "5ccba027ba85743e09d15c03296797cad56395089b832b48b5a5217880f57733" dependencies = [ "aes-gcm 0.9.4", "blake2", "chacha20poly1305", - "curve25519-dalek 4.0.0-rc.0", + "curve25519-dalek 4.0.0-rc.1", "rand_core 0.6.4", "ring", "rustc_version", @@ -11108,9 +11519,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -11136,7 +11547,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "hash-db", "log", @@ -11154,19 +11565,21 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ + "Inflector", "blake2", + "expander 1.0.0", "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "sp-application-crypto" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", "scale-info", @@ -11179,7 +11592,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "integer-sqrt", "num-traits", @@ -11193,7 +11606,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", "scale-info", @@ -11206,7 +11619,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", "sp-api", @@ -11218,7 +11631,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "futures", "log", @@ -11236,7 +11649,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "futures", @@ -11251,7 +11664,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "parity-scale-codec", @@ -11269,7 +11682,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "merlin", @@ -11292,7 +11705,7 @@ dependencies = [ [[package]] name = "sp-consensus-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "lazy_static", "parity-scale-codec", @@ -11311,7 +11724,7 @@ dependencies = [ [[package]] name = "sp-consensus-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "finality-grandpa", "log", @@ -11329,7 +11742,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", "scale-info", @@ -11341,7 +11754,7 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", "scale-info", @@ -11354,11 +11767,11 @@ dependencies = [ [[package]] name = "sp-core" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "array-bytes 4.2.0", "base58", - "bitflags", + "bitflags 1.3.2", "blake2", "bounded-collections", "dyn-clonable", @@ -11397,7 +11810,7 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "blake2b_simd", "byteorder", @@ -11410,26 +11823,26 @@ dependencies = [ [[package]] name = "sp-core-hashing" -version = "7.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d607f7209b1b9571177fc3722a03312df03606bb65f89317ba686d5fa59d438f" +checksum = "cbc2d1947252b7a4e403b0a260f596920443742791765ec111daa2bbf98eff25" dependencies = [ "blake2", "byteorder", "digest 0.10.6", "sha2 0.10.6", "sha3", - "sp-std 7.0.0", + "sp-std 6.0.0", "twox-hash", ] [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "sp-core-hashing 5.0.0", "syn 1.0.109", ] @@ -11437,7 +11850,7 @@ dependencies = [ [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -11446,17 +11859,17 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "sp-externalities" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "environmental", "parity-scale-codec", @@ -11467,7 +11880,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -11482,7 +11895,7 @@ dependencies = [ [[package]] name = "sp-io" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "bytes", "ed25519", @@ -11507,7 +11920,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "lazy_static", "sp-core", @@ -11518,9 +11931,8 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ - "async-trait", "futures", "merlin", "parity-scale-codec", @@ -11535,7 +11947,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "thiserror", "zstd", @@ -11544,7 +11956,7 @@ dependencies = [ [[package]] name = "sp-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "ckb-merkle-mountain-range 0.5.2", "log", @@ -11562,7 +11974,7 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", "scale-info", @@ -11576,7 +11988,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "sp-api", "sp-core", @@ -11586,7 +11998,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "backtrace", "lazy_static", @@ -11596,7 +12008,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "rustc-hash", "serde", @@ -11606,7 +12018,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "either", "hash256-std-hasher", @@ -11628,7 +12040,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -11646,19 +12058,19 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "Inflector", "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", "scale-info", @@ -11672,7 +12084,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", "scale-info", @@ -11684,7 +12096,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "hash-db", "log", @@ -11704,18 +12116,18 @@ dependencies = [ [[package]] name = "sp-std" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" [[package]] name = "sp-std" -version = "7.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986" +checksum = "af0ee286f98455272f64ac5bb1384ff21ac029fbb669afbaf48477faff12760e" [[package]] name = "sp-storage" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "impl-serde", "parity-scale-codec", @@ -11728,7 +12140,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "futures-timer", @@ -11743,7 +12155,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", "sp-std 5.0.0", @@ -11755,7 +12167,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "sp-api", "sp-runtime", @@ -11764,7 +12176,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "log", @@ -11780,11 +12192,11 @@ dependencies = [ [[package]] name = "sp-trie" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "ahash 0.8.3", "hash-db", - "hashbrown 0.12.3", + "hashbrown 0.13.2", "lazy_static", "memory-db", "nohash-hasher", @@ -11803,7 +12215,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "impl-serde", "parity-scale-codec", @@ -11820,18 +12232,18 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "sp-wasm-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -11845,7 +12257,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "parity-scale-codec", "scale-info", @@ -11870,19 +12282,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0445c905640145c7ea8c1993555957f65e7c46d0535b91ba501bc9bfc85522f" +dependencies = [ + "base64ct", + "der 0.7.1", ] [[package]] name = "ss58-registry" -version = "1.38.0" +version = "1.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e40c020d72bc0a9c5660bb71e4a6fdef081493583062c474740a7d59f55f0e7b" +checksum = "ecf0bd63593ef78eca595a7fc25e9a443ca46fe69fd472f8f09f5245cdcd769d" dependencies = [ "Inflector", "num-format", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "serde", "serde_json", "unicode-xid 0.2.4", @@ -11918,7 +12340,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg_aliases", "libc", "parking_lot 0.11.2", @@ -11935,8 +12357,8 @@ checksum = "f2261c91034a1edc3fc4d1b80e89d82714faede0515c14a75da10cb941546bbf" dependencies = [ "cfg_aliases", "memchr", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -11948,8 +12370,8 @@ checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" dependencies = [ "cfg_aliases", "memchr", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -11999,8 +12421,8 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -12020,8 +12442,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "rustversion", "syn 1.0.109", ] @@ -12061,7 +12483,7 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "platforms 2.0.0", ] @@ -12069,7 +12491,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "frame-system-rpc-runtime-api", "futures", @@ -12088,7 +12510,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "hyper", "log", @@ -12131,9 +12553,13 @@ dependencies = [ "polkadot-runtime-common", "polkadot-runtime-parachains", "rbtag", + "relay-bridge-hub-kusama-client", + "relay-bridge-hub-polkadot-client", "relay-bridge-hub-rococo-client", "relay-bridge-hub-wococo-client", + "relay-kusama-client", "relay-millau-client", + "relay-polkadot-client", "relay-rialto-client", "relay-rialto-parachain-client", "relay-rococo-client", @@ -12203,7 +12629,7 @@ dependencies = [ [[package]] name = "substrate-rpc-client" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", "jsonrpsee", @@ -12216,7 +12642,7 @@ dependencies = [ [[package]] name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "jsonrpsee", "log", @@ -12235,7 +12661,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "ansi_term", "build-helper", @@ -12267,7 +12693,8 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "subxt" version = "0.27.1" -source = "git+https://github.com/paritytech/subxt?branch=master#d4545de8cf426db57a498c14ec28b8e2efc008df" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54639dba6a113584083968b6a8f457dedae612abe1bd214762101ca29f12e332" dependencies = [ "base58", "blake2", @@ -12286,7 +12713,7 @@ dependencies = [ "scale-value", "serde", "serde_json", - "sp-core-hashing 7.0.0", + "sp-core-hashing 6.0.0", "subxt-macro", "subxt-metadata", "thiserror", @@ -12296,7 +12723,8 @@ dependencies = [ [[package]] name = "subxt-codegen" version = "0.27.1" -source = "git+https://github.com/paritytech/subxt?branch=master#d4545de8cf426db57a498c14ec28b8e2efc008df" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e86cb719003f1cedf2710a6e55ca4c37aba4c989bbd3b81dd1c52af9e4827e" dependencies = [ "darling", "frame-metadata", @@ -12304,19 +12732,20 @@ dependencies = [ "hex", "jsonrpsee", "parity-scale-codec", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro-error", + "proc-macro2 1.0.53", + "quote 1.0.26", "scale-info", "subxt-metadata", "syn 1.0.109", - "thiserror", "tokio", ] [[package]] name = "subxt-macro" version = "0.27.1" -source = "git+https://github.com/paritytech/subxt?branch=master#d4545de8cf426db57a498c14ec28b8e2efc008df" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c08de402a78c4c06c3ee3702c80e519efdcb65911348e018b6998d04404916" dependencies = [ "darling", "proc-macro-error", @@ -12327,12 +12756,13 @@ dependencies = [ [[package]] name = "subxt-metadata" version = "0.27.1" -source = "git+https://github.com/paritytech/subxt?branch=master#d4545de8cf426db57a498c14ec28b8e2efc008df" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2593ab5f53435e6352675af4f9851342607f37785d84c7a3fb3139550d3c35f0" dependencies = [ "frame-metadata", "parity-scale-codec", "scale-info", - "sp-core-hashing 7.0.0", + "sp-core-hashing 6.0.0", ] [[package]] @@ -12352,8 +12782,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +dependencies = [ + "proc-macro2 1.0.53", + "quote 1.0.26", "unicode-ident", ] @@ -12363,17 +12804,17 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", "unicode-xid 0.2.4", ] [[package]] name = "sysinfo" -version = "0.28.2" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e847e2de7a137c8c2cede5095872dbb00f4f9bf34d061347e36b43322acd56" +checksum = "f69e0d827cce279e61c2f3399eb789271a8f136d8245edef70f06e3c9601a670" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys", @@ -12390,7 +12831,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "system-configuration-sys", ] @@ -12413,9 +12854,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" +checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" [[package]] name = "tempfile" @@ -12426,7 +12867,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall", - "rustix 0.36.8", + "rustix 0.36.11", "windows-sys 0.42.0", ] @@ -12441,9 +12882,9 @@ dependencies = [ [[package]] name = "termtree" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "textwrap" @@ -12456,22 +12897,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 2.0.8", ] [[package]] @@ -12482,10 +12923,11 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if 1.0.0", "once_cell", ] @@ -12651,8 +13093,8 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -12669,9 +13111,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" dependencies = [ "futures-core", "pin-project-lite 0.2.9", @@ -12681,9 +13123,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -12711,9 +13153,9 @@ checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" [[package]] name = "toml_edit" -version = "0.19.5" +version = "0.19.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7082a95d48029677a28f181e5f6422d0c8339ad8396a39d3f33d62a90c1f6c30" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" dependencies = [ "indexmap", "toml_datetime", @@ -12737,7 +13179,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ - "bitflags", + "bitflags 1.3.2", "bytes", "futures-core", "futures-util", @@ -12780,8 +13222,8 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -12807,8 +13249,8 @@ dependencies = [ [[package]] name = "tracing-gum" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "polkadot-node-jaeger", "polkadot-primitives", @@ -12818,13 +13260,13 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "expander 0.0.6", "proc-macro-crate", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -12874,9 +13316,9 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.26.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879380c0061b165ba1f036325b7f3478bc1a947814d9fc36d22c5d8e960b11bd" +checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" dependencies = [ "hash-db", "hashbrown 0.13.2", @@ -12887,9 +13329,9 @@ dependencies = [ [[package]] name = "trie-root" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891" +checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" dependencies = [ "hash-db", ] @@ -12949,10 +13391,10 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#f5c2cecd7efe6dcbc828906794f3c4c41f23c791" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", - "clap 4.1.8", + "clap 4.1.11", "frame-remote-externalities", "hex", "log", @@ -13045,15 +13487,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -13098,6 +13540,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "universal-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "unsigned-varint" version = "0.7.1" @@ -13193,12 +13645,11 @@ checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -13249,8 +13700,8 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", "wasm-bindgen-shared", ] @@ -13273,7 +13724,7 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ - "quote 1.0.23", + "quote 1.0.26", "wasm-bindgen-macro-support", ] @@ -13283,8 +13734,8 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -13454,7 +13905,7 @@ dependencies = [ "directories-next", "file-per-thread-logger", "log", - "rustix 0.36.8", + "rustix 0.36.11", "serde", "sha2 0.10.6", "toml", @@ -13534,7 +13985,7 @@ checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" dependencies = [ "object 0.29.0", "once_cell", - "rustix 0.36.8", + "rustix 0.36.11", ] [[package]] @@ -13565,7 +14016,7 @@ dependencies = [ "memoffset 0.6.5", "paste", "rand 0.8.5", - "rustix 0.36.8", + "rustix 0.36.11", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", @@ -13681,22 +14132,22 @@ dependencies = [ [[package]] name = "webrtc-dtls" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7021987ae0a2ed6c8cd33f68e98e49bb6e74ffe9543310267b48a1bbe3900e5f" +checksum = "942be5bd85f072c3128396f6e5a9bfb93ca8c1939ded735d177b7bcba9a13d05" dependencies = [ "aes 0.6.0", - "aes-gcm 0.8.0", + "aes-gcm 0.10.1", "async-trait", "bincode", "block-modes", "byteorder", "ccm", "curve25519-dalek 3.2.0", - "der-parser 8.1.0", - "elliptic-curve", + "der-parser 8.2.0", + "elliptic-curve 0.12.3", "hkdf", - "hmac 0.10.1", + "hmac 0.12.1", "log", "oid-registry 0.6.1", "p256", @@ -13706,11 +14157,11 @@ dependencies = [ "rcgen 0.9.3", "ring", "rustls 0.19.1", - "sec1", + "sec1 0.3.0", "serde", - "sha-1", - "sha2 0.9.9", - "signature", + "sha1", + "sha2 0.10.6", + "signature 1.6.4", "subtle", "thiserror", "tokio", @@ -13722,9 +14173,9 @@ dependencies = [ [[package]] name = "webrtc-ice" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494483fbb2f5492620871fdc78b084aed8807377f6e3fe88b2e49f0a9c9c41d7" +checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80" dependencies = [ "arc-swap", "async-trait", @@ -13821,7 +14272,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" dependencies = [ "async-trait", - "bitflags", + "bitflags 1.3.2", "bytes", "cc", "ipnet", @@ -13835,15 +14286,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "wepoll-ffi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" -dependencies = [ - "cc", -] - [[package]] name = "which" version = "4.4.0" @@ -13857,9 +14299,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feff0a412894d67223777b6cc8d68c0dab06d52d95e9890d5f2d47f10dd9366c" +checksum = "b689b6c49d6549434bf944e6b0f39238cf63693cb7a147e9d887507fffa3b223" dependencies = [ "bytemuck", "safe_arch", @@ -13915,6 +14357,15 @@ dependencies = [ "windows_x86_64_msvc 0.34.0", ] +[[package]] +name = "windows" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.42.0" @@ -13922,12 +14373,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.1", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -13941,24 +14392,24 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.1", + "windows_x86_64_msvc 0.42.2", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_msvc" @@ -13968,9 +14419,9 @@ checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_i686_gnu" @@ -13980,9 +14431,9 @@ checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_msvc" @@ -13992,9 +14443,9 @@ checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_x86_64_gnu" @@ -14004,15 +14455,15 @@ checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_msvc" @@ -14022,15 +14473,15 @@ checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "winnow" -version = "0.3.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" +checksum = "deac0939bd6e4f24ab5919fbf751c97a8cfc8543bb083a305ed5c0c10bb241d1" dependencies = [ "memchr", ] @@ -14100,10 +14551,10 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" dependencies = [ - "asn1-rs 0.5.1", + "asn1-rs 0.5.2", "base64 0.13.1", "data-encoding", - "der-parser 8.1.0", + "der-parser 8.2.0", "lazy_static", "nom", "oid-registry 0.6.1", @@ -14114,8 +14565,8 @@ dependencies = [ [[package]] name = "xcm" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bounded-collections", "derivative", @@ -14130,8 +14581,8 @@ dependencies = [ [[package]] name = "xcm-builder" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "frame-support", "frame-system", @@ -14151,8 +14602,8 @@ dependencies = [ [[package]] name = "xcm-executor" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "environmental", "frame-support", @@ -14170,12 +14621,12 @@ dependencies = [ [[package]] name = "xcm-procedural" -version = "0.9.37" -source = "git+https://github.com/paritytech/polkadot?branch=master#c02f9adb6e31357a21fb0d07005441db71ddc9d6" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "Inflector", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", ] @@ -14223,8 +14674,8 @@ version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.53", + "quote 1.0.26", "syn 1.0.109", "synstructure", ] @@ -14250,9 +14701,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.6+zstd.1.5.2" +version = "2.0.7+zstd.1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a3f9792c0c3dc6c165840a75f47ae1f4da402c2d006881129579f6597e801b" +checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" dependencies = [ "cc", "libc", diff --git a/Cargo.toml b/Cargo.toml index 5e06145e079..8048b9fcdcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,8 @@ members = [ "modules/shift-session-manager", "primitives/beefy", "primitives/chain-bridge-hub-cumulus", + "primitives/chain-bridge-hub-kusama", + "primitives/chain-bridge-hub-polkadot", "primitives/chain-bridge-hub-rococo", "primitives/chain-bridge-hub-wococo", "primitives/chain-kusama", @@ -36,6 +38,8 @@ members = [ "primitives/runtime", "primitives/test-utils", "relays/bin-substrate", + "relays/client-bridge-hub-kusama", + "relays/client-bridge-hub-polkadot", "relays/client-bridge-hub-rococo", "relays/client-bridge-hub-wococo", "relays/client-kusama", diff --git a/README.md b/README.md index b5885026426..aab6007d2cd 100644 --- a/README.md +++ b/README.md @@ -215,18 +215,19 @@ In this section we'll show you how to quickly send a bridge message. The message After sending a message you will see the following logs showing a message was successfully sent: ``` -INFO bridge Sending message to Rialto. Size: 5. +INFO bridge Sending message to Rialto. Size: 11. TRACE bridge Sent transaction to Millau node: 0x5e68... ``` And at the Rialto node logs you'll something like this: ``` -... runtime::bridge-dispatch: Going to execute message ([0, 0, 0, 0], 1) (...), Trap(43)]) -... runtime::bridge-dispatch: Incoming message ([0, 0, 0, 0], 1) dispatched with result: Incomplete(2000000000, Trap(43)) +... runtime::bridge-messages: Received messages: total=1, valid=1. Weight used: Weight(ref_time: 1215065371, proof_size: 48559)/Weight(ref_time: 1215065371, proof_size: 54703). ``` -It means that the message has been delivered and successfully dispatched. +It means that the message has been delivered and dispatched. Message may be dispatched with an +error, though - the goal of our test bridge is to ensure that messages are successfully delivered +and all involved components are working. ## Full Network Docker Compose Setup diff --git a/bin/millau/node/Cargo.toml b/bin/millau/node/Cargo.toml index cca94bbe7b1..063fedc5e64 100644 --- a/bin/millau/node/Cargo.toml +++ b/bin/millau/node/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.1.6", features = ["derive"] } +clap = { version = "4.1.11", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } serde_json = "1.0.94" diff --git a/bin/millau/node/src/service.rs b/bin/millau/node/src/service.rs index f8e6d34933d..7230accef8e 100644 --- a/bin/millau/node/src/service.rs +++ b/bin/millau/node/src/service.rs @@ -22,7 +22,6 @@ use sc_client_api::BlockBackend; use sc_consensus_aura::{CompatibilityMode, ImportQueueParams, SlotProportion, StartAuraParams}; use sc_consensus_grandpa::SharedVoterState; pub use sc_executor::NativeElseWasmExecutor; -use sc_keystore::LocalKeystore; use sc_service::{error::Error as ServiceError, Configuration, TaskManager}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; @@ -78,10 +77,6 @@ pub fn new_partial( >, ServiceError, > { - if config.keystore_remote.is_some() { - return Err(ServiceError::Other("Remote Keystores are not supported.".into())) - } - let telemetry = config .telemetry_endpoints .clone() @@ -175,13 +170,6 @@ pub fn new_partial( }) } -fn remote_keystore(_url: &str) -> Result, &'static str> { - // FIXME: here would the concrete keystore be built, - // must return a concrete type (NOT `LocalKeystore`) that - // implements `CryptoStore` and `SyncCryptoStore` - Err("Remote Keystore not supported.") -} - /// Builds a new service for a full client. pub fn new_full(mut config: Configuration) -> Result { use sc_network_common::sync::warp::WarpSyncParams; @@ -191,22 +179,12 @@ pub fn new_full(mut config: Configuration) -> Result backend, mut task_manager, import_queue, - mut keystore_container, + keystore_container, select_chain, transaction_pool, other: (block_import, grandpa_link, beefy_voter_links, beefy_rpc_links, mut telemetry), } = new_partial(&config)?; - if let Some(url) = &config.keystore_remote { - match remote_keystore(url) { - Ok(k) => keystore_container.set_remote_keystore(k), - Err(e) => - return Err(ServiceError::Other(format!( - "Error hooking up remote keystore for {url}: {e}" - ))), - }; - } - let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"); // Note: GrandPa is pushed before the Polkadot-specific protocols. This doesn't change @@ -332,7 +310,7 @@ pub fn new_full(mut config: Configuration) -> Result let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { network: network.clone(), client: client.clone(), - keystore: keystore_container.sync_keystore(), + keystore: keystore_container.keystore(), task_manager: &mut task_manager, transaction_pool: transaction_pool.clone(), sync_service: sync_service.clone(), @@ -375,7 +353,7 @@ pub fn new_full(mut config: Configuration) -> Result }, force_authoring, backoff_authoring_blocks, - keystore: keystore_container.sync_keystore(), + keystore: keystore_container.keystore(), sync_oracle: sync_service.clone(), justification_sync_link: sync_service.clone(), block_proposal_slot_portion: SlotProportion::new(2f32 / 3f32), @@ -394,8 +372,7 @@ pub fn new_full(mut config: Configuration) -> Result // if the node isn't actively participating in consensus then it doesn't // need a keystore, regardless of which protocol we use below. - let keystore = - if role.is_authority() { Some(keystore_container.sync_keystore()) } else { None }; + let keystore = if role.is_authority() { Some(keystore_container.keystore()) } else { None }; let justifications_protocol_name = beefy_on_demand_justifications_handler.protocol_name(); let payload_provider = sp_consensus_beefy::mmr::MmrRootProvider::new(client.clone()); diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index 21fff0e564e..b0c6b2875fe 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -331,6 +331,10 @@ impl pallet_balances::Config for Runtime { type MaxLocks = ConstU32<50>; type MaxReserves = ConstU32<50>; type ReserveIdentifier = [u8; 8]; + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; } parameter_types! { @@ -393,6 +397,7 @@ impl pallet_bridge_relayers::Config for Runtime { pub type RialtoGrandpaInstance = (); impl pallet_bridge_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; type BridgedChain = bp_rialto::Rialto; // This is a pretty unscientific cap. // @@ -405,6 +410,7 @@ impl pallet_bridge_grandpa::Config for Runtime { pub type WestendGrandpaInstance = pallet_bridge_grandpa::Instance1; impl pallet_bridge_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; type BridgedChain = bp_westend::Westend; type MaxRequests = ConstU32<50>; type HeadersToKeep = ConstU32<{ bp_westend::DAYS }>; @@ -559,11 +565,11 @@ construct_runtime!( // Rialto bridge modules. BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, - BridgeRialtoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, + BridgeRialtoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event}, BridgeRialtoMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, // Westend bridge modules. - BridgeWestendGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Config, Storage}, + BridgeWestendGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Config, Storage, Event}, BridgeWestendParachains: pallet_bridge_parachains::::{Pallet, Call, Storage, Event}, // RialtoParachain bridge modules. @@ -653,6 +659,14 @@ impl_runtime_apis! { fn metadata() -> OpaqueMetadata { OpaqueMetadata::new(Runtime::metadata().into()) } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> sp_std::vec::Vec { + Runtime::metadata_versions() + } } impl sp_block_builder::BlockBuilder for Runtime { @@ -1005,7 +1019,7 @@ impl_runtime_apis! { Runtime, WithRialtoParachainsInstance, WithRialtoParachainMessageBridge, - >(params) + >(params, xcm::v3::Junctions::Here) } fn prepare_message_delivery_proof( @@ -1019,10 +1033,8 @@ impl_runtime_apis! { } fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { - use bridge_runtime_common::messages::MessageBridge; - let lane = >::bench_lane_id(); - let bridged_chain_id = WithRialtoParachainMessageBridge::BRIDGED_CHAIN_ID; + let bridged_chain_id = bp_runtime::RIALTO_PARACHAIN_CHAIN_ID; pallet_bridge_relayers::Pallet::::relayer_reward( relayer, RewardsAccountParams::new(lane, bridged_chain_id, RewardsAccountOwner::BridgedChain) @@ -1038,7 +1050,7 @@ impl_runtime_apis! { Runtime, RialtoGrandpaInstance, WithRialtoMessageBridge, - >(params) + >(params, xcm::v3::Junctions::Here) } fn prepare_message_delivery_proof( @@ -1052,10 +1064,8 @@ impl_runtime_apis! { } fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { - use bridge_runtime_common::messages::MessageBridge; - let lane = >::bench_lane_id(); - let bridged_chain_id = WithRialtoMessageBridge::BRIDGED_CHAIN_ID; + let bridged_chain_id = bp_runtime::RIALTO_CHAIN_ID; pallet_bridge_relayers::Pallet::::relayer_reward( relayer, RewardsAccountParams::new(lane, bridged_chain_id, RewardsAccountOwner::BridgedChain) diff --git a/bin/millau/runtime/src/rialto_messages.rs b/bin/millau/runtime/src/rialto_messages.rs index 919fac86899..84535283d48 100644 --- a/bin/millau/runtime/src/rialto_messages.rs +++ b/bin/millau/runtime/src/rialto_messages.rs @@ -16,14 +16,18 @@ //! Everything required to serve Millau <-> Rialto messages. -use crate::{RialtoGrandpaInstance, Runtime, RuntimeCall, RuntimeOrigin}; - -use bp_messages::{LaneId, MessageNonce}; -use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; -use bridge_runtime_common::messages::{ - self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, +use crate::{RialtoGrandpaInstance, Runtime, RuntimeOrigin, WithRialtoMessagesInstance}; + +use bp_messages::LaneId; +use bridge_runtime_common::{ + messages::{ + self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, + }, + messages_xcm_extension::{XcmBlobHauler, XcmBlobHaulerAdapter}, }; use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; +use xcm::latest::prelude::*; +use xcm_builder::HaulBlobExporter; /// Default lane that is used to send messages to Rialto. pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); @@ -48,7 +52,7 @@ pub type ToRialtoMessageVerifier = messages::source::FromThisChainMessageVerifier; /// Message payload for Rialto -> Millau messages. -pub type FromRialtoMessagePayload = messages::target::FromBridgedChainMessagePayload; +pub type FromRialtoMessagePayload = messages::target::FromBridgedChainMessagePayload; /// Messages proof for Rialto -> Millau messages. pub type FromRialtoMessagesProof = messages::target::FromBridgedChainMessagesProof; @@ -58,12 +62,13 @@ pub type ToRialtoMessagesDeliveryProof = messages::source::FromBridgedChainMessagesDeliveryProof; /// Call-dispatch based message dispatch for Rialto -> Millau messages. -pub type FromRialtoMessageDispatch = messages::target::FromBridgedChainMessageDispatch< - WithRialtoMessageBridge, - xcm_executor::XcmExecutor, - crate::xcm_config::XcmWeigher, - WeightCredit, ->; +pub type FromRialtoMessageDispatch = + bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatch< + bp_millau::Millau, + bp_rialto::Rialto, + crate::xcm_config::OnMillauBlobDispatcher, + (), + >; /// Maximal outbound payload size of Millau -> Rialto messages. pub type ToRialtoMaximalOutboundPayloadSize = @@ -74,8 +79,6 @@ pub type ToRialtoMaximalOutboundPayloadSize = pub struct WithRialtoMessageBridge; impl MessageBridge for WithRialtoMessageBridge { - const THIS_CHAIN_ID: ChainId = MILLAU_CHAIN_ID; - const BRIDGED_CHAIN_ID: ChainId = RIALTO_CHAIN_ID; const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME; type ThisChain = Millau; @@ -94,15 +97,6 @@ impl messages::UnderlyingChainProvider for Millau { impl messages::ThisChainWithMessages for Millau { type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - - fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, _lane: &LaneId) -> bool { - true - } - - fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { - MessageNonce::MAX - } } /// Rialto chain from message lane point of view. @@ -117,9 +111,29 @@ impl messages::UnderlyingChainProvider for Rialto { type Chain = bp_rialto::Rialto; } -impl messages::BridgedChainWithMessages for Rialto { - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { - true +impl messages::BridgedChainWithMessages for Rialto {} + +/// Export XCM messages to be relayed to Rialto. +pub type ToRialtoBlobExporter = HaulBlobExporter< + XcmBlobHaulerAdapter, + crate::xcm_config::RialtoNetwork, + (), +>; + +/// To-Rialto XCM hauler. +pub struct ToRialtoXcmBlobHauler; + +impl XcmBlobHauler for ToRialtoXcmBlobHauler { + type MessageSender = pallet_bridge_messages::Pallet; + type MessageSenderOrigin = RuntimeOrigin; + + fn message_sender_origin() -> RuntimeOrigin { + pallet_xcm::Origin::from(MultiLocation::new(1, crate::xcm_config::UniversalLocation::get())) + .into() + } + + fn xcm_lane() -> LaneId { + XCM_LANE } } diff --git a/bin/millau/runtime/src/rialto_parachain_messages.rs b/bin/millau/runtime/src/rialto_parachain_messages.rs index 1cfdc9c8f49..b3d33c1cefd 100644 --- a/bin/millau/runtime/src/rialto_parachain_messages.rs +++ b/bin/millau/runtime/src/rialto_parachain_messages.rs @@ -16,14 +16,20 @@ //! Everything required to serve Millau <-> RialtoParachain messages. -use crate::{Runtime, RuntimeCall, RuntimeOrigin, WithRialtoParachainsInstance}; +use crate::{ + Runtime, RuntimeOrigin, WithRialtoParachainMessagesInstance, WithRialtoParachainsInstance, +}; -use bp_messages::{LaneId, MessageNonce}; -use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID}; -use bridge_runtime_common::messages::{ - self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, +use bp_messages::LaneId; +use bridge_runtime_common::{ + messages::{ + self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, + }, + messages_xcm_extension::{XcmBlobHauler, XcmBlobHaulerAdapter}, }; use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; +use xcm::latest::prelude::*; +use xcm_builder::HaulBlobExporter; /// Default lane that is used to send messages to Rialto parachain. pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); @@ -48,16 +54,16 @@ pub type ToRialtoParachainMessageVerifier = messages::source::FromThisChainMessageVerifier; /// Message payload for RialtoParachain -> Millau messages. -pub type FromRialtoParachainMessagePayload = - messages::target::FromBridgedChainMessagePayload; +pub type FromRialtoParachainMessagePayload = messages::target::FromBridgedChainMessagePayload; /// Call-dispatch based message dispatch for RialtoParachain -> Millau messages. -pub type FromRialtoParachainMessageDispatch = messages::target::FromBridgedChainMessageDispatch< - WithRialtoParachainMessageBridge, - xcm_executor::XcmExecutor, - crate::xcm_config::XcmWeigher, - WeightCredit, ->; +pub type FromRialtoParachainMessageDispatch = + bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatch< + bp_millau::Millau, + bp_rialto::Rialto, + crate::xcm_config::OnMillauBlobDispatcher, + (), + >; /// Maximal outbound payload size of Millau -> RialtoParachain messages. pub type ToRialtoParachainMaximalOutboundPayloadSize = @@ -68,8 +74,6 @@ pub type ToRialtoParachainMaximalOutboundPayloadSize = pub struct WithRialtoParachainMessageBridge; impl MessageBridge for WithRialtoParachainMessageBridge { - const THIS_CHAIN_ID: ChainId = MILLAU_CHAIN_ID; - const BRIDGED_CHAIN_ID: ChainId = RIALTO_PARACHAIN_CHAIN_ID; const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME; type ThisChain = Millau; @@ -90,16 +94,7 @@ impl messages::UnderlyingChainProvider for Millau { } impl messages::ThisChainWithMessages for Millau { - type RuntimeCall = RuntimeCall; type RuntimeOrigin = RuntimeOrigin; - - fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, _lane: &LaneId) -> bool { - true - } - - fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { - MessageNonce::MAX - } } /// RialtoParachain chain from message lane point of view. @@ -116,8 +111,29 @@ impl messages::UnderlyingChainProvider for RialtoParachain { type Chain = bp_rialto_parachain::RialtoParachain; } -impl messages::BridgedChainWithMessages for RialtoParachain { - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { - true +impl messages::BridgedChainWithMessages for RialtoParachain {} + +/// Export XCM messages to be relayed to Rialto. +pub type ToRialtoParachainBlobExporter = HaulBlobExporter< + XcmBlobHaulerAdapter, + crate::xcm_config::RialtoParachainNetwork, + (), +>; + +/// To-RialtoParachain XCM hauler. +pub struct ToRialtoParachainXcmBlobHauler; + +impl XcmBlobHauler for ToRialtoParachainXcmBlobHauler { + type MessageSender = + pallet_bridge_messages::Pallet; + type MessageSenderOrigin = RuntimeOrigin; + + fn message_sender_origin() -> RuntimeOrigin { + pallet_xcm::Origin::from(MultiLocation::new(1, crate::xcm_config::UniversalLocation::get())) + .into() + } + + fn xcm_lane() -> LaneId { + XCM_LANE } } diff --git a/bin/millau/runtime/src/xcm_config.rs b/bin/millau/runtime/src/xcm_config.rs index aaaa7bf642d..4aaec83771b 100644 --- a/bin/millau/runtime/src/xcm_config.rs +++ b/bin/millau/runtime/src/xcm_config.rs @@ -17,29 +17,25 @@ //! XCM configurations for the Millau runtime. use super::{ - rialto_messages::{WithRialtoMessageBridge, XCM_LANE}, - rialto_parachain_messages::{WithRialtoParachainMessageBridge, XCM_LANE as XCM_LANE_PARACHAIN}, - AccountId, AllPalletsWithSystem, Balances, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - WithRialtoMessagesInstance, WithRialtoParachainMessagesInstance, XcmPallet, + rialto_messages::ToRialtoBlobExporter, + rialto_parachain_messages::ToRialtoParachainBlobExporter, AccountId, AllPalletsWithSystem, + Balances, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, XcmPallet, }; -use bp_messages::LaneId; use bp_millau::WeightToFee; -use bp_rialto_parachain::RIALTO_PARACHAIN_ID; -use bridge_runtime_common::{ - messages::source::{XcmBridge, XcmBridgeAdapter}, - CustomNetworkId, -}; +use bridge_runtime_common::CustomNetworkId; use frame_support::{ parameter_types, traits::{ConstU32, Everything, Nothing}, weights::Weight, }; +use frame_system::EnsureRoot; use xcm::latest::prelude::*; use xcm_builder::{ - AccountId32Aliases, AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, - CurrencyAdapter as XcmCurrencyAdapter, IsConcrete, MintLocation, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + AccountId32Aliases, CurrencyAdapter as XcmCurrencyAdapter, IsConcrete, MintLocation, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + UsingComponents, }; +use xcm_executor::traits::ExportXcm; parameter_types! { /// The location of the `MLAU` token, from the context of this chain. Since this token is native to this @@ -101,32 +97,28 @@ parameter_types! { pub const MaxInstructions: u32 = 100; } -/// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our -/// individual routers. -pub type XcmRouter = ( - // Router to send messages to Rialto. - XcmBridgeAdapter, - // Router to send messages to RialtoParachains. - XcmBridgeAdapter, -); +/// The XCM router. We are not sending messages to sibling/parent/child chains here. +pub type XcmRouter = (); /// The barriers one of which must be passed for an XCM message to be executed. pub type Barrier = ( // Weight that is paid for may be consumed. TakeWeightCredit, - // If the message is one that immediately attemps to pay for execution, then allow it. - AllowTopLevelPaidExecutionFrom, - // Expected responses are OK. - AllowKnownQueryResponses, ); +/// Dispatches received XCM messages from other chain. +pub type OnMillauBlobDispatcher = xcm_builder::BridgeBlobDispatcher< + crate::xcm_config::XcmRouter, + crate::xcm_config::UniversalLocation, +>; + /// XCM weigher type. pub type XcmWeigher = xcm_builder::FixedWeightBounds; pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; + type XcmSender = (); type AssetTransactor = LocalAssetTransactor; type OriginConverter = LocalOriginConverter; type IsReserve = (); @@ -145,7 +137,7 @@ impl xcm_executor::Config for XcmConfig { type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = ConstU32<64>; type FeeManager = (); - type MessageExporter = (); + type MessageExporter = ToRialtoOrRialtoParachainSwitchExporter; type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; @@ -160,7 +152,7 @@ pub type LocalOriginToLocation = ( #[cfg(feature = "runtime-benchmarks")] parameter_types! { - pub ReachableDest: Option = todo!("We dont use benchmarks for pallet_xcm, so if you hit this message, you need to remove this and define value instead"); + pub ReachableDest: Option = None; } impl pallet_xcm::Config for Runtime { @@ -170,7 +162,7 @@ impl pallet_xcm::Config for Runtime { // the DOT to send from the Relay-chain). But it's useless until we bring in XCM v3 which will // make `DescendOrigin` a bit more useful. type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; - type XcmRouter = XcmRouter; + type XcmRouter = (); // Anyone can execute XCM messages locally. type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; type XcmExecuteFilter = Everything; @@ -195,72 +187,65 @@ impl pallet_xcm::Config for Runtime { type WeightInfo = pallet_xcm::TestWeightInfo; #[cfg(feature = "runtime-benchmarks")] type ReachableDest = ReachableDest; + type AdminOrigin = EnsureRoot; } -/// With-Rialto bridge. -pub struct ToRialtoBridge; - -impl XcmBridge for ToRialtoBridge { - type MessageBridge = WithRialtoMessageBridge; - type MessageSender = pallet_bridge_messages::Pallet; - - fn universal_location() -> InteriorMultiLocation { - UniversalLocation::get() - } - - fn verify_destination(dest: &MultiLocation) -> bool { - matches!(*dest, MultiLocation { parents: 1, interior: X1(GlobalConsensus(r)) } if r == RialtoNetwork::get()) - } - - fn build_destination() -> MultiLocation { - let dest: InteriorMultiLocation = RialtoNetwork::get().into(); - let here = UniversalLocation::get(); - dest.relative_to(&here) - } - - fn xcm_lane() -> LaneId { - XCM_LANE - } -} - -/// With-RialtoParachain bridge. -pub struct ToRialtoParachainBridge; - -impl XcmBridge for ToRialtoParachainBridge { - type MessageBridge = WithRialtoParachainMessageBridge; - type MessageSender = - pallet_bridge_messages::Pallet; - - fn universal_location() -> InteriorMultiLocation { - UniversalLocation::get() +pub struct ToRialtoOrRialtoParachainSwitchExporter; + +impl ExportXcm for ToRialtoOrRialtoParachainSwitchExporter { + type Ticket = (NetworkId, (sp_std::prelude::Vec, XcmHash)); + + fn validate( + network: NetworkId, + channel: u32, + universal_source: &mut Option, + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { + if network == RialtoNetwork::get() { + ToRialtoBlobExporter::validate(network, channel, universal_source, destination, message) + .map(|result| ((RialtoNetwork::get(), result.0), result.1)) + } else if network == RialtoParachainNetwork::get() { + ToRialtoParachainBlobExporter::validate( + network, + channel, + universal_source, + destination, + message, + ) + .map(|result| ((RialtoParachainNetwork::get(), result.0), result.1)) + } else { + Err(SendError::Unroutable) + } } - fn verify_destination(dest: &MultiLocation) -> bool { - matches!(*dest, MultiLocation { parents: 1, interior: X2(GlobalConsensus(r), Parachain(RIALTO_PARACHAIN_ID)) } if r == RialtoNetwork::get()) - } - - fn build_destination() -> MultiLocation { - let dest: InteriorMultiLocation = RialtoParachainNetwork::get().into(); - let here = UniversalLocation::get(); - dest.relative_to(&here) - } - - fn xcm_lane() -> LaneId { - XCM_LANE_PARACHAIN + fn deliver(ticket: Self::Ticket) -> Result { + let (network, ticket) = ticket; + if network == RialtoNetwork::get() { + ToRialtoBlobExporter::deliver(ticket) + } else if network == RialtoParachainNetwork::get() { + ToRialtoParachainBlobExporter::deliver(ticket) + } else { + Err(SendError::Unroutable) + } } } #[cfg(test)] mod tests { use super::*; - use crate::rialto_messages::WeightCredit; + use crate::{ + rialto_messages::FromRialtoMessageDispatch, WithRialtoMessagesInstance, + WithRialtoParachainMessagesInstance, + }; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - MessageKey, + LaneId, MessageKey, }; - use bp_runtime::messages::MessageDispatchResult; - use bridge_runtime_common::messages::target::FromBridgedChainMessageDispatch; + use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult; use codec::Encode; + use pallet_bridge_messages::OutboundLanes; + use xcm_executor::XcmExecutor; fn new_test_ext() -> sp_io::TestExternalities { sp_io::TestExternalities::new( @@ -268,59 +253,121 @@ mod tests { ) } + fn prepare_outbound_xcm_message(destination: NetworkId) -> Xcm { + vec![ExportMessage { + network: destination, + destination: destination.into(), + xcm: vec![Instruction::Trap(42)].into(), + }] + .into() + } + #[test] - fn xcm_messages_are_sent_using_bridge_router() { + fn xcm_messages_to_rialto_are_sent_using_bridge_exporter() { new_test_ext().execute_with(|| { - let xcm: Xcm<()> = vec![Instruction::Trap(42)].into(); - let expected_fee = MultiAssets::from((Here, 1_000_000_u128)); - let expected_hash = - ([0u8, 0u8, 0u8, 0u8], 1u64).using_encoded(sp_io::hashing::blake2_256); - - // message 1 to Rialto - let dest = (Parent, X1(GlobalConsensus(RialtoNetwork::get()))); - let send_result = send_xcm::(dest.into(), xcm.clone()); - assert_eq!(send_result, Ok((expected_hash, expected_fee.clone()))); - - // message 2 to RialtoParachain (expected hash is the same, since other lane is used) - let dest = - (Parent, X2(GlobalConsensus(RialtoNetwork::get()), Parachain(RIALTO_PARACHAIN_ID))); - let send_result = send_xcm::(dest.into(), xcm); - assert_eq!(send_result, Ok((expected_hash, expected_fee))); + // ensure that the there are no messages queued + assert_eq!( + OutboundLanes::::get( + crate::rialto_messages::XCM_LANE + ) + .latest_generated_nonce, + 0, + ); + + // export message instruction "sends" message to Rialto + XcmExecutor::::execute_xcm_in_credit( + Here, + prepare_outbound_xcm_message(RialtoNetwork::get()), + Default::default(), + Weight::MAX, + Weight::MAX, + ) + .ensure_complete() + .expect("runtime configuration must be correct"); + + // ensure that the message has been queued + assert_eq!( + OutboundLanes::::get( + crate::rialto_messages::XCM_LANE + ) + .latest_generated_nonce, + 1, + ); }) } #[test] - fn xcm_messages_from_rialto_are_dispatched() { - type XcmExecutor = xcm_executor::XcmExecutor; - type MessageDispatcher = FromBridgedChainMessageDispatch< - WithRialtoMessageBridge, - XcmExecutor, - XcmWeigher, - WeightCredit, - >; - + fn xcm_messages_to_rialto_parachain_are_sent_using_bridge_exporter() { new_test_ext().execute_with(|| { - let location: MultiLocation = - (Parent, X1(GlobalConsensus(RialtoNetwork::get()))).into(); - let xcm: Xcm = vec![Instruction::Trap(42)].into(); - - let mut incoming_message = DispatchMessage { - key: MessageKey { lane_id: LaneId([0, 0, 0, 0]), nonce: 1 }, - data: DispatchMessageData { payload: Ok((location, xcm).into()) }, - }; - - let dispatch_weight = MessageDispatcher::dispatch_weight(&mut incoming_message); - assert_eq!(dispatch_weight, BaseXcmWeight::get()); + // ensure that the there are no messages queued + assert_eq!( + OutboundLanes::::get( + crate::rialto_parachain_messages::XCM_LANE + ) + .latest_generated_nonce, + 0, + ); - let dispatch_result = - MessageDispatcher::dispatch(&AccountId::from([0u8; 32]), incoming_message); + // export message instruction "sends" message to Rialto + XcmExecutor::::execute_xcm_in_credit( + Here, + prepare_outbound_xcm_message(RialtoParachainNetwork::get()), + Default::default(), + Weight::MAX, + Weight::MAX, + ) + .ensure_complete() + .expect("runtime configuration must be correct"); + + // ensure that the message has been queued assert_eq!( - dispatch_result, - MessageDispatchResult { - unspent_weight: frame_support::weights::Weight::zero(), - dispatch_level_result: (), - } + OutboundLanes::::get( + crate::rialto_parachain_messages::XCM_LANE + ) + .latest_generated_nonce, + 1, ); }) } + + fn prepare_inbound_bridge_message() -> DispatchMessage> { + let xcm = xcm::VersionedXcm::::V3(vec![Instruction::Trap(42)].into()); + let location = + xcm::VersionedInteriorMultiLocation::V3(X1(GlobalConsensus(ThisNetwork::get()))); + // this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor + // or public fields, so just tuple + let bridge_message = (location, xcm).encode(); + DispatchMessage { + key: MessageKey { lane_id: LaneId([0, 0, 0, 0]), nonce: 1 }, + data: DispatchMessageData { payload: Ok(bridge_message) }, + } + } + + #[test] + fn xcm_messages_from_rialto_are_dispatched() { + let incoming_message = prepare_inbound_bridge_message(); + + // we care only about handing message to the XCM dispatcher, so we don't care about its + // actual dispatch + let dispatch_result = + FromRialtoMessageDispatch::dispatch(&AccountId::from([0u8; 32]), incoming_message); + assert!(matches!( + dispatch_result.dispatch_level_result, + XcmBlobMessageDispatchResult::NotDispatched(_), + )); + } + + #[test] + fn xcm_messages_from_rialto_parachain_are_dispatched() { + let incoming_message = prepare_inbound_bridge_message(); + + // we care only about handing message to the XCM dispatcher, so we don't care about its + // actual dispatch + let dispatch_result = + FromRialtoMessageDispatch::dispatch(&AccountId::from([0u8; 32]), incoming_message); + assert!(matches!( + dispatch_result.dispatch_level_result, + XcmBlobMessageDispatchResult::NotDispatched(_), + )); + } } diff --git a/bin/rialto-parachain/node/Cargo.toml b/bin/rialto-parachain/node/Cargo.toml index 4cdaa93bad0..84e804f484e 100644 --- a/bin/rialto-parachain/node/Cargo.toml +++ b/bin/rialto-parachain/node/Cargo.toml @@ -17,7 +17,7 @@ default = [] runtime-benchmarks = ['rialto-parachain-runtime/runtime-benchmarks'] [dependencies] -clap = { version = "4.1.6", features = ["derive"] } +clap = { version = "4.1.11", features = ["derive"] } log = '0.4.17' codec = { package = 'parity-scale-codec', version = '3.1.5' } serde = { version = '1.0', features = ['derive'] } diff --git a/bin/rialto-parachain/node/src/command.rs b/bin/rialto-parachain/node/src/command.rs index 2cb08ec4ea2..7393b014732 100644 --- a/bin/rialto-parachain/node/src/command.rs +++ b/bin/rialto-parachain/node/src/command.rs @@ -286,7 +286,7 @@ pub fn run() -> Result<()> { let id = ParaId::from(cli.parachain_id.or(para_id).expect("Missing ParaId")); let parachain_account = - AccountIdConversion::::into_account_truncating(&id); + AccountIdConversion::::into_account_truncating(&id); let state_version = RelayChainCli::native_runtime_version(&config.chain_spec).state_version(); diff --git a/bin/rialto-parachain/node/src/service.rs b/bin/rialto-parachain/node/src/service.rs index 7e7fa2ed09b..6662b2e9625 100644 --- a/bin/rialto-parachain/node/src/service.rs +++ b/bin/rialto-parachain/node/src/service.rs @@ -47,7 +47,7 @@ use sc_network_sync::SyncingService; use sc_service::{Configuration, PartialComponents, TFullBackend, TFullClient, TaskManager}; use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle}; use sp_api::ConstructRuntimeApi; -use sp_keystore::SyncCryptoStorePtr; +use sp_keystore::KeystorePtr; use sp_runtime::traits::BlakeTwo256; use substrate_prometheus_endpoint::Registry; @@ -237,7 +237,7 @@ where Arc, Arc>>, Arc>, - SyncCryptoStorePtr, + KeystorePtr, bool, ) -> Result>, sc_service::Error>, { @@ -294,7 +294,7 @@ where transaction_pool: transaction_pool.clone(), task_manager: &mut task_manager, config: parachain_config, - keystore: params.keystore_container.sync_keystore(), + keystore: params.keystore_container.keystore(), backend: backend.clone(), network: network.clone(), sync_service: sync_service.clone(), @@ -324,7 +324,7 @@ where relay_chain_interface.clone(), transaction_pool, sync_service, - params.keystore_container.sync_keystore(), + params.keystore_container.keystore(), force_authoring, )?; diff --git a/bin/rialto-parachain/runtime/Cargo.toml b/bin/rialto-parachain/runtime/Cargo.toml index ec8955e7fd1..ed193b4e681 100644 --- a/bin/rialto-parachain/runtime/Cargo.toml +++ b/bin/rialto-parachain/runtime/Cargo.toml @@ -111,6 +111,7 @@ std = [ "frame-support/std", "frame-executive/std", "frame-system/std", + "frame-system-rpc-runtime-api/std", "pallet-balances/std", "pallet-bridge-grandpa/std", "pallet-bridge-messages/std", @@ -118,6 +119,7 @@ std = [ "pallet-timestamp/std", "pallet-sudo/std", "pallet-transaction-payment/std", + "pallet-transaction-payment-rpc-runtime-api/std", "pallet-xcm/std", "parachain-info/std", "polkadot-parachain/std", diff --git a/bin/rialto-parachain/runtime/src/lib.rs b/bin/rialto-parachain/runtime/src/lib.rs index d8e087c5a6a..4e4fcffcd00 100644 --- a/bin/rialto-parachain/runtime/src/lib.rs +++ b/bin/rialto-parachain/runtime/src/lib.rs @@ -26,16 +26,16 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -use crate::millau_messages::{WithMillauMessageBridge, XCM_LANE}; - -use bridge_runtime_common::messages::source::{XcmBridge, XcmBridgeAdapter}; +use bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages; +use codec::{Decode, Encode}; use cumulus_pallet_parachain_system::AnyRelayNumber; +use scale_info::TypeInfo; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{AccountIdLookup, Block as BlockT}, - transaction_validity::{TransactionSource, TransactionValidity}, + traits::{AccountIdLookup, Block as BlockT, DispatchInfoOf, SignedExtension}, + transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError}, ApplyExtrinsicResult, }; @@ -83,16 +83,53 @@ use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use xcm::latest::prelude::*; use xcm_builder::{ - AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, - EnsureXcmOrigin, FixedWeightBounds, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - UsingComponents, + AccountId32Aliases, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, + NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{Config, XcmExecutor}; pub mod millau_messages; +// generate signed extension that rejects obsolete bridge transactions +generate_bridge_reject_obsolete_headers_and_messages! { + RuntimeCall, AccountId, + // Grandpa + BridgeMillauGrandpa, + // Messages + BridgeMillauMessages +} + +/// Dummy signed extension that does nothing. +/// +/// We're using it to have the same set of signed extensions on all parachains with bridge pallets +/// deployed (bridge hubs and rialto parachain). +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub struct DummyBridgeRefundMillauMessages; + +impl SignedExtension for DummyBridgeRefundMillauMessages { + const IDENTIFIER: &'static str = "DummyBridgeRefundMillauMessages"; + type AccountId = AccountId; + type Call = RuntimeCall; + type AdditionalSigned = (); + type Pre = (); + + fn additional_signed(&self) -> Result { + Ok(()) + } + + fn pre_dispatch( + self, + _who: &Self::AccountId, + _call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + Ok(()) + } +} + /// The address format for describing accounts. pub type Address = MultiAddress; /// Block type as expected by this runtime. @@ -111,6 +148,8 @@ pub type SignedExtra = ( frame_system::CheckNonce, frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, + BridgeRejectObsoleteHeadersAndMessages, + DummyBridgeRefundMillauMessages, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = @@ -265,6 +304,10 @@ impl pallet_balances::Config for Runtime { type MaxLocks = ConstU32<50>; type MaxReserves = ConstU32<50>; type ReserveIdentifier = [u8; 8]; + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; } impl pallet_transaction_payment::Config for Runtime { @@ -306,7 +349,7 @@ parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); pub const RelayNetwork: NetworkId = CustomNetworkId::Rialto.as_network_id(); pub RelayOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorMultiLocation = X1(Parachain(ParachainInfo::parachain_id().into())); + pub UniversalLocation: InteriorMultiLocation = ThisNetwork::get().into(); /// The Millau network ID. pub const MillauNetwork: NetworkId = CustomNetworkId::Millau.as_network_id(); /// The RialtoParachain network ID. @@ -367,8 +410,8 @@ pub type XcmOriginToTransactDispatchOrigin = ( // the following constant must match the similar constant in the Millau runtime. parameter_types! { - /// The amount of weight an XCM operation takes. This is a safe overestimate. - pub const UnitWeightCost: Weight = Weight::from_parts(1_000_000, 64 * 1024); + /// The amount of weight an XCM operation takes. We don't care much about those values as we're on testnet. + pub const UnitWeightCost: Weight = Weight::from_parts(1_000_000, 1024); // One UNIT buys 1 second of weight. pub const WeightPrice: (MultiLocation, u128) = (MultiLocation::parent(), UNIT); pub const MaxInstructions: u32 = 100; @@ -383,12 +426,11 @@ match_types! { }; } -pub type Barrier = ( - TakeWeightCredit, - AllowTopLevelPaidExecutionFrom, - AllowUnpaidExecutionFrom, - // ^^^ Parent & its unit plurality gets free execution -); +pub type Barrier = TakeWeightCredit; + +/// Dispatches received XCM messages from other chain. +pub type OnRialtoParachainBlobDispatcher = + xcm_builder::BridgeBlobDispatcher; /// XCM weigher type. pub type XcmWeigher = FixedWeightBounds; @@ -396,7 +438,7 @@ pub type XcmWeigher = FixedWeightBounds; -/// The means for routing XCM messages which are not for local execution into the right message -/// queues. -pub type XcmRouter = ( - // Bridge is used to communicate with other relay chain (Millau). - XcmBridgeAdapter, -); - -/// With-Millau bridge. -pub struct ToMillauBridge; - -impl XcmBridge for ToMillauBridge { - type MessageBridge = WithMillauMessageBridge; - type MessageSender = pallet_bridge_messages::Pallet; - - fn universal_location() -> InteriorMultiLocation { - UniversalLocation::get() - } - - fn verify_destination(dest: &MultiLocation) -> bool { - matches!(*dest, MultiLocation { parents: 1, interior: X1(GlobalConsensus(r)) } if r == MillauNetwork::get()) - } - - fn build_destination() -> MultiLocation { - let dest: InteriorMultiLocation = MillauNetwork::get().into(); - let here = UniversalLocation::get(); - dest.relative_to(&here) - } - - fn xcm_lane() -> bp_messages::LaneId { - XCM_LANE - } -} +/// The XCM router. We are not sending messages to sibling/parent/child chains here. +pub type XcmRouter = (); #[cfg(feature = "runtime-benchmarks")] parameter_types! { - pub ReachableDest: Option = todo!("We dont use benchmarks for pallet_xcm, so if you hit this message, you need to remove this and define value instead"); + pub ReachableDest: Option = None; } impl pallet_xcm::Config for Runtime { @@ -484,6 +496,7 @@ impl pallet_xcm::Config for Runtime { type WeightInfo = pallet_xcm::TestWeightInfo; #[cfg(feature = "runtime-benchmarks")] type ReachableDest = ReachableDest; + type AdminOrigin = frame_system::EnsureRoot; } impl cumulus_pallet_xcm::Config for Runtime { @@ -525,6 +538,7 @@ impl pallet_bridge_relayers::Config for Runtime { pub type MillauGrandpaInstance = (); impl pallet_bridge_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; type BridgedChain = bp_millau::Millau; /// This is a pretty unscientific cap. /// @@ -604,7 +618,7 @@ construct_runtime!( // Millau bridge modules. BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, - BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, + BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event}, BridgeMillauMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, } ); @@ -628,6 +642,14 @@ impl_runtime_apis! { fn metadata() -> OpaqueMetadata { OpaqueMetadata::new(Runtime::metadata().into()) } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> sp_std::vec::Vec { + Runtime::metadata_versions() + } } impl sp_block_builder::BlockBuilder for Runtime { @@ -829,17 +851,18 @@ cumulus_pallet_parachain_system::register_validate_block!( #[cfg(test)] mod tests { use super::*; - use crate::millau_messages::WeightCredit; + use crate::millau_messages::{FromMillauMessageDispatch, XCM_LANE}; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, LaneId, MessageKey, }; - use bp_runtime::messages::MessageDispatchResult; use bridge_runtime_common::{ - integrity::check_additional_signed, messages::target::FromBridgedChainMessageDispatch, + integrity::check_additional_signed, messages_xcm_extension::XcmBlobMessageDispatchResult, }; use codec::Encode; + use pallet_bridge_messages::OutboundLanes; use sp_runtime::generic::Era; + use xcm_executor::XcmExecutor; fn new_test_ext() -> sp_io::TestExternalities { sp_io::TestExternalities::new( @@ -847,54 +870,72 @@ mod tests { ) } + fn prepare_outbound_xcm_message(destination: NetworkId) -> Xcm { + vec![ExportMessage { + network: destination, + destination: destination.into(), + xcm: vec![Instruction::Trap(42)].into(), + }] + .into() + } + #[test] - fn xcm_messages_to_millau_are_sent() { + fn xcm_messages_to_millau_are_sent_using_bridge_exporter() { new_test_ext().execute_with(|| { - // the encoded message (origin ++ xcm) is 0x010109020419A8 - let dest = (Parent, X1(GlobalConsensus(MillauNetwork::get()))); - let xcm: Xcm<()> = vec![Instruction::Trap(42)].into(); - - let send_result = send_xcm::(dest.into(), xcm); - let expected_fee = MultiAssets::from((Here, Fungibility::Fungible(1_000_000_u128))); - let expected_hash = - ([0u8, 0u8, 0u8, 0u8], 1u64).using_encoded(sp_io::hashing::blake2_256); - assert_eq!(send_result, Ok((expected_hash, expected_fee)),); + // ensure that the there are no messages queued + assert_eq!( + OutboundLanes::::get(XCM_LANE) + .latest_generated_nonce, + 0, + ); + + // export message instruction "sends" message to Rialto + XcmExecutor::::execute_xcm_in_credit( + Here, + prepare_outbound_xcm_message(MillauNetwork::get()), + Default::default(), + Weight::MAX, + Weight::MAX, + ) + .ensure_complete() + .expect("runtime configuration must be correct"); + + // ensure that the message has been queued + assert_eq!( + OutboundLanes::::get(XCM_LANE) + .latest_generated_nonce, + 1, + ); }) } + fn prepare_inbound_bridge_message() -> DispatchMessage> { + let xcm = xcm::VersionedXcm::::V3(vec![Instruction::Trap(42)].into()); + let location = + xcm::VersionedInteriorMultiLocation::V3(X1(GlobalConsensus(ThisNetwork::get()))); + // this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor + // or public fields, so just tuple + let bridge_message = (location, xcm).encode(); + DispatchMessage { + key: MessageKey { lane_id: LaneId([0, 0, 0, 0]), nonce: 1 }, + data: DispatchMessageData { payload: Ok(bridge_message) }, + } + } + #[test] fn xcm_messages_from_millau_are_dispatched() { - type XcmExecutor = xcm_executor::XcmExecutor; - type MessageDispatcher = FromBridgedChainMessageDispatch< - WithMillauMessageBridge, - XcmExecutor, - XcmWeigher, - WeightCredit, - >; - new_test_ext().execute_with(|| { - let location: MultiLocation = - (Parent, X1(GlobalConsensus(MillauNetwork::get()))).into(); - let xcm: Xcm = vec![Instruction::Trap(42)].into(); - - let mut incoming_message = DispatchMessage { - key: MessageKey { lane_id: LaneId([0, 0, 0, 0]), nonce: 1 }, - data: DispatchMessageData { payload: Ok((location, xcm).into()) }, - }; - - let dispatch_weight = MessageDispatcher::dispatch_weight(&mut incoming_message); - assert_eq!(dispatch_weight, UnitWeightCost::get()); + let incoming_message = prepare_inbound_bridge_message(); + // we care only about handing message to the XCM dispatcher, so we don't care about its + // actual dispatch let dispatch_result = - MessageDispatcher::dispatch(&AccountId::from([0u8; 32]), incoming_message); - assert_eq!( - dispatch_result, - MessageDispatchResult { - unspent_weight: frame_support::weights::Weight::zero(), - dispatch_level_result: (), - } - ); - }) + FromMillauMessageDispatch::dispatch(&AccountId::from([0u8; 32]), incoming_message); + assert!(matches!( + dispatch_result.dispatch_level_result, + XcmBlobMessageDispatchResult::NotDispatched(_), + )); + }); } #[test] @@ -908,9 +949,11 @@ mod tests { frame_system::CheckNonce::from(10), frame_system::CheckWeight::new(), pallet_transaction_payment::ChargeTransactionPayment::from(10), + BridgeRejectObsoleteHeadersAndMessages, + DummyBridgeRefundMillauMessages, ); let indirect_payload = bp_rialto_parachain::SignedExtension::new( - ((), (), (), (), Era::Immortal, 10.into(), (), 10.into()), + ((), (), (), (), Era::Immortal, 10.into(), (), 10.into(), (), ()), None, ); assert_eq!(payload.encode(), indirect_payload.encode()); diff --git a/bin/rialto-parachain/runtime/src/millau_messages.rs b/bin/rialto-parachain/runtime/src/millau_messages.rs index ee7a089992e..5d4a92b5091 100644 --- a/bin/rialto-parachain/runtime/src/millau_messages.rs +++ b/bin/rialto-parachain/runtime/src/millau_messages.rs @@ -19,14 +19,18 @@ // TODO: this is almost exact copy of `millau_messages.rs` from Rialto runtime. // Should be extracted to a separate crate and reused here. -use crate::{MillauGrandpaInstance, Runtime, RuntimeCall, RuntimeOrigin}; - -use bp_messages::{LaneId, MessageNonce}; -use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_PARACHAIN_CHAIN_ID}; -use bridge_runtime_common::messages::{ - self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, +use crate::{MillauGrandpaInstance, Runtime, RuntimeOrigin, WithMillauMessagesInstance}; + +use bp_messages::LaneId; +use bridge_runtime_common::{ + messages::{ + self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, + }, + messages_xcm_extension::{XcmBlobHauler, XcmBlobHaulerAdapter}, }; use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; +use xcm::latest::prelude::*; +use xcm_builder::HaulBlobExporter; /// Default lane that is used to send messages to Millau. pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); @@ -51,15 +55,16 @@ pub type ToMillauMessageVerifier = messages::source::FromThisChainMessageVerifier; /// Message payload for Millau -> RialtoParachain messages. -pub type FromMillauMessagePayload = messages::target::FromBridgedChainMessagePayload; +pub type FromMillauMessagePayload = messages::target::FromBridgedChainMessagePayload; /// Call-dispatch based message dispatch for Millau -> RialtoParachain messages. -pub type FromMillauMessageDispatch = messages::target::FromBridgedChainMessageDispatch< - WithMillauMessageBridge, - xcm_executor::XcmExecutor, - crate::XcmWeigher, - WeightCredit, ->; +pub type FromMillauMessageDispatch = + bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatch< + bp_rialto_parachain::RialtoParachain, + bp_millau::Millau, + crate::OnRialtoParachainBlobDispatcher, + (), + >; /// Messages proof for Millau -> RialtoParachain messages. pub type FromMillauMessagesProof = messages::target::FromBridgedChainMessagesProof; @@ -77,8 +82,6 @@ pub type ToMillauMaximalOutboundPayloadSize = pub struct WithMillauMessageBridge; impl MessageBridge for WithMillauMessageBridge { - const THIS_CHAIN_ID: ChainId = RIALTO_PARACHAIN_CHAIN_ID; - const BRIDGED_CHAIN_ID: ChainId = MILLAU_CHAIN_ID; const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_rialto_parachain::WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME; @@ -97,16 +100,7 @@ impl messages::UnderlyingChainProvider for RialtoParachain { } impl messages::ThisChainWithMessages for RialtoParachain { - type RuntimeCall = RuntimeCall; type RuntimeOrigin = RuntimeOrigin; - - fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, _lane: &LaneId) -> bool { - true - } - - fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { - MessageNonce::MAX - } } /// Millau chain from message lane point of view. @@ -121,8 +115,24 @@ impl messages::UnderlyingChainProvider for Millau { type Chain = bp_millau::Millau; } -impl messages::BridgedChainWithMessages for Millau { - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { - true +impl messages::BridgedChainWithMessages for Millau {} + +/// Export XCM messages to be relayed to Millau. +pub type ToMillauBlobExporter = + HaulBlobExporter, crate::MillauNetwork, ()>; + +/// To-Millau XCM hauler. +pub struct ToMillauXcmBlobHauler; + +impl XcmBlobHauler for ToMillauXcmBlobHauler { + type MessageSender = pallet_bridge_messages::Pallet; + type MessageSenderOrigin = RuntimeOrigin; + + fn message_sender_origin() -> RuntimeOrigin { + pallet_xcm::Origin::from(MultiLocation::new(1, crate::UniversalLocation::get())).into() + } + + fn xcm_lane() -> LaneId { + XCM_LANE } } diff --git a/bin/rialto/node/Cargo.toml b/bin/rialto/node/Cargo.toml index be83e02424a..8658dd95ec5 100644 --- a/bin/rialto/node/Cargo.toml +++ b/bin/rialto/node/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.1.6", features = ["derive"] } +clap = { version = "4.1.11", features = ["derive"] } serde_json = "1.0.94" # Bridge dependencies diff --git a/bin/rialto/node/src/chain_spec.rs b/bin/rialto/node/src/chain_spec.rs index c51ca1197f6..ceb82d45b38 100644 --- a/bin/rialto/node/src/chain_spec.rs +++ b/bin/rialto/node/src/chain_spec.rs @@ -15,7 +15,7 @@ // along with Parity Bridges Common. If not, see . use frame_support::weights::Weight; -use polkadot_primitives::v2::{AssignmentId, ValidatorId}; +use polkadot_primitives::v4::{AssignmentId, ValidatorId}; use rialto_runtime::{ AccountId, BabeConfig, BalancesConfig, BeefyConfig, BridgeMillauMessagesConfig, ConfigurationConfig, GenesisConfig, GrandpaConfig, SessionConfig, SessionKeys, Signature, @@ -244,8 +244,8 @@ fn testnet_genesis( validation_upgrade_cooldown: 2u32, validation_upgrade_delay: 2, code_retention_period: 1200, - max_code_size: polkadot_primitives::v2::MAX_CODE_SIZE, - max_pov_size: polkadot_primitives::v2::MAX_POV_SIZE, + max_code_size: polkadot_primitives::v4::MAX_CODE_SIZE, + max_pov_size: polkadot_primitives::v4::MAX_POV_SIZE, max_head_data_size: 32 * 1024, group_rotation_frequency: 20, chain_availability_period: 4, @@ -255,7 +255,7 @@ fn testnet_genesis( max_downward_message_size: 1024 * 1024, ump_service_total_weight: Weight::from_parts( 100_000_000_000, - polkadot_primitives::v2::MAX_POV_SIZE as u64, + polkadot_primitives::v4::MAX_POV_SIZE as u64, ), max_upward_message_size: 50 * 1024, max_upward_message_num_per_candidate: 5, diff --git a/bin/rialto/runtime/src/lib.rs b/bin/rialto/runtime/src/lib.rs index 9db181e2840..1a6f0f1e540 100644 --- a/bin/rialto/runtime/src/lib.rs +++ b/bin/rialto/runtime/src/lib.rs @@ -330,6 +330,10 @@ impl pallet_balances::Config for Runtime { type MaxLocks = ConstU32<50>; type MaxReserves = ConstU32<50>; type ReserveIdentifier = [u8; 8]; + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; } parameter_types! { @@ -390,6 +394,7 @@ impl pallet_bridge_relayers::Config for Runtime { pub type MillauGrandpaInstance = (); impl pallet_bridge_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; type BridgedChain = bp_millau::Millau; /// This is a pretty unscientific cap. /// @@ -479,7 +484,7 @@ construct_runtime!( // Millau bridge modules. BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, - BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, + BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event}, BridgeMillauMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, // Millau bridge modules (BEEFY based). @@ -578,6 +583,14 @@ impl_runtime_apis! { fn metadata() -> OpaqueMetadata { OpaqueMetadata::new(Runtime::metadata().into()) } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> sp_std::vec::Vec { + Runtime::metadata_versions() + } } impl sp_block_builder::BlockBuilder for Runtime { @@ -753,55 +766,55 @@ impl_runtime_apis! { } impl polkadot_primitives::runtime_api::ParachainHost for Runtime { - fn validators() -> Vec { - polkadot_runtime_parachains::runtime_api_impl::v2::validators::() + fn validators() -> Vec { + polkadot_runtime_parachains::runtime_api_impl::v4::validators::() } - fn validator_groups() -> (Vec>, polkadot_primitives::v2::GroupRotationInfo) { - polkadot_runtime_parachains::runtime_api_impl::v2::validator_groups::() + fn validator_groups() -> (Vec>, polkadot_primitives::v4::GroupRotationInfo) { + polkadot_runtime_parachains::runtime_api_impl::v4::validator_groups::() } - fn availability_cores() -> Vec> { - polkadot_runtime_parachains::runtime_api_impl::v2::availability_cores::() + fn availability_cores() -> Vec> { + polkadot_runtime_parachains::runtime_api_impl::v4::availability_cores::() } - fn persisted_validation_data(para_id: polkadot_primitives::v2::Id, assumption: polkadot_primitives::v2::OccupiedCoreAssumption) - -> Option> { - polkadot_runtime_parachains::runtime_api_impl::v2::persisted_validation_data::(para_id, assumption) + fn persisted_validation_data(para_id: polkadot_primitives::v4::Id, assumption: polkadot_primitives::v4::OccupiedCoreAssumption) + -> Option> { + polkadot_runtime_parachains::runtime_api_impl::v4::persisted_validation_data::(para_id, assumption) } fn assumed_validation_data( - para_id: polkadot_primitives::v2::Id, + para_id: polkadot_primitives::v4::Id, expected_persisted_validation_data_hash: Hash, - ) -> Option<(polkadot_primitives::v2::PersistedValidationData, polkadot_primitives::v2::ValidationCodeHash)> { - polkadot_runtime_parachains::runtime_api_impl::v2::assumed_validation_data::( + ) -> Option<(polkadot_primitives::v4::PersistedValidationData, polkadot_primitives::v4::ValidationCodeHash)> { + polkadot_runtime_parachains::runtime_api_impl::v4::assumed_validation_data::( para_id, expected_persisted_validation_data_hash, ) } fn check_validation_outputs( - para_id: polkadot_primitives::v2::Id, - outputs: polkadot_primitives::v2::CandidateCommitments, + para_id: polkadot_primitives::v4::Id, + outputs: polkadot_primitives::v4::CandidateCommitments, ) -> bool { - polkadot_runtime_parachains::runtime_api_impl::v2::check_validation_outputs::(para_id, outputs) + polkadot_runtime_parachains::runtime_api_impl::v4::check_validation_outputs::(para_id, outputs) } - fn session_index_for_child() -> polkadot_primitives::v2::SessionIndex { - polkadot_runtime_parachains::runtime_api_impl::v2::session_index_for_child::() + fn session_index_for_child() -> polkadot_primitives::v4::SessionIndex { + polkadot_runtime_parachains::runtime_api_impl::v4::session_index_for_child::() } - fn validation_code(para_id: polkadot_primitives::v2::Id, assumption: polkadot_primitives::v2::OccupiedCoreAssumption) - -> Option { - polkadot_runtime_parachains::runtime_api_impl::v2::validation_code::(para_id, assumption) + fn validation_code(para_id: polkadot_primitives::v4::Id, assumption: polkadot_primitives::v4::OccupiedCoreAssumption) + -> Option { + polkadot_runtime_parachains::runtime_api_impl::v4::validation_code::(para_id, assumption) } - fn candidate_pending_availability(para_id: polkadot_primitives::v2::Id) -> Option> { - polkadot_runtime_parachains::runtime_api_impl::v2::candidate_pending_availability::(para_id) + fn candidate_pending_availability(para_id: polkadot_primitives::v4::Id) -> Option> { + polkadot_runtime_parachains::runtime_api_impl::v4::candidate_pending_availability::(para_id) } - fn candidate_events() -> Vec> { - polkadot_runtime_parachains::runtime_api_impl::v2::candidate_events::(|ev| { + fn candidate_events() -> Vec> { + polkadot_runtime_parachains::runtime_api_impl::v4::candidate_events::(|ev| { match ev { RuntimeEvent::Inclusion(ev) => { Some(ev) @@ -811,46 +824,54 @@ impl_runtime_apis! { }) } - fn session_info(index: polkadot_primitives::v2::SessionIndex) -> Option { - polkadot_runtime_parachains::runtime_api_impl::v2::session_info::(index) + fn session_info(index: polkadot_primitives::v4::SessionIndex) -> Option { + polkadot_runtime_parachains::runtime_api_impl::v4::session_info::(index) } - fn dmq_contents(recipient: polkadot_primitives::v2::Id) -> Vec> { - polkadot_runtime_parachains::runtime_api_impl::v2::dmq_contents::(recipient) + fn dmq_contents(recipient: polkadot_primitives::v4::Id) -> Vec> { + polkadot_runtime_parachains::runtime_api_impl::v4::dmq_contents::(recipient) } fn inbound_hrmp_channels_contents( - recipient: polkadot_primitives::v2::Id - ) -> BTreeMap>> { - polkadot_runtime_parachains::runtime_api_impl::v2::inbound_hrmp_channels_contents::(recipient) + recipient: polkadot_primitives::v4::Id + ) -> BTreeMap>> { + polkadot_runtime_parachains::runtime_api_impl::v4::inbound_hrmp_channels_contents::(recipient) } - fn validation_code_by_hash(hash: polkadot_primitives::v2::ValidationCodeHash) -> Option { - polkadot_runtime_parachains::runtime_api_impl::v2::validation_code_by_hash::(hash) + fn validation_code_by_hash(hash: polkadot_primitives::v4::ValidationCodeHash) -> Option { + polkadot_runtime_parachains::runtime_api_impl::v4::validation_code_by_hash::(hash) } - fn on_chain_votes() -> Option> { - polkadot_runtime_parachains::runtime_api_impl::v2::on_chain_votes::() + fn on_chain_votes() -> Option> { + polkadot_runtime_parachains::runtime_api_impl::v4::on_chain_votes::() } - fn submit_pvf_check_statement(stmt: polkadot_primitives::v2::PvfCheckStatement, signature: polkadot_primitives::v2::ValidatorSignature) { - polkadot_runtime_parachains::runtime_api_impl::v2::submit_pvf_check_statement::(stmt, signature) + fn submit_pvf_check_statement(stmt: polkadot_primitives::v4::PvfCheckStatement, signature: polkadot_primitives::v4::ValidatorSignature) { + polkadot_runtime_parachains::runtime_api_impl::v4::submit_pvf_check_statement::(stmt, signature) } - fn pvfs_require_precheck() -> Vec { - polkadot_runtime_parachains::runtime_api_impl::v2::pvfs_require_precheck::() + fn pvfs_require_precheck() -> Vec { + polkadot_runtime_parachains::runtime_api_impl::v4::pvfs_require_precheck::() } - fn validation_code_hash(para_id: polkadot_primitives::v2::Id, assumption: polkadot_primitives::v2::OccupiedCoreAssumption) - -> Option + fn validation_code_hash(para_id: polkadot_primitives::v4::Id, assumption: polkadot_primitives::v4::OccupiedCoreAssumption) + -> Option { - polkadot_runtime_parachains::runtime_api_impl::v2::validation_code_hash::(para_id, assumption) + polkadot_runtime_parachains::runtime_api_impl::v4::validation_code_hash::(para_id, assumption) + } + + fn disputes() -> Vec<(polkadot_primitives::v4::SessionIndex, polkadot_primitives::v4::CandidateHash, polkadot_primitives::v4::DisputeState)> { + polkadot_runtime_parachains::runtime_api_impl::v4::get_session_disputes::() + } + + fn session_executor_params(session_index: polkadot_primitives::v4::SessionIndex) -> Option { + polkadot_runtime_parachains::runtime_api_impl::v4::session_executor_params::(session_index) } } impl sp_authority_discovery::AuthorityDiscoveryApi for Runtime { fn authorities() -> Vec { - polkadot_runtime_parachains::runtime_api_impl::v2::relevant_authority_ids::() + polkadot_runtime_parachains::runtime_api_impl::v4::relevant_authority_ids::() } } diff --git a/bin/rialto/runtime/src/millau_messages.rs b/bin/rialto/runtime/src/millau_messages.rs index ff08c08029a..ab4b7dd521d 100644 --- a/bin/rialto/runtime/src/millau_messages.rs +++ b/bin/rialto/runtime/src/millau_messages.rs @@ -16,14 +16,18 @@ //! Everything required to serve Millau <-> Rialto messages. -use crate::{MillauGrandpaInstance, Runtime, RuntimeCall, RuntimeOrigin}; - -use bp_messages::{LaneId, MessageNonce}; -use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; -use bridge_runtime_common::messages::{ - self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, +use crate::{MillauGrandpaInstance, Runtime, RuntimeOrigin, WithMillauMessagesInstance}; + +use bp_messages::LaneId; +use bridge_runtime_common::{ + messages::{ + self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, + }, + messages_xcm_extension::{XcmBlobHauler, XcmBlobHaulerAdapter}, }; use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; +use xcm::latest::prelude::*; +use xcm_builder::HaulBlobExporter; /// Lane that is used for XCM messages exchange. pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); @@ -48,15 +52,16 @@ pub type ToMillauMessageVerifier = messages::source::FromThisChainMessageVerifier; /// Message payload for Millau -> Rialto messages. -pub type FromMillauMessagePayload = messages::target::FromBridgedChainMessagePayload; +pub type FromMillauMessagePayload = messages::target::FromBridgedChainMessagePayload; /// Call-dispatch based message dispatch for Millau -> Rialto messages. -pub type FromMillauMessageDispatch = messages::target::FromBridgedChainMessageDispatch< - WithMillauMessageBridge, - xcm_executor::XcmExecutor, - crate::xcm_config::XcmWeigher, - WeightCredit, ->; +pub type FromMillauMessageDispatch = + bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatch< + bp_rialto::Rialto, + bp_millau::Millau, + crate::xcm_config::OnRialtoBlobDispatcher, + (), + >; /// Messages proof for Millau -> Rialto messages. pub type FromMillauMessagesProof = messages::target::FromBridgedChainMessagesProof; @@ -74,8 +79,6 @@ pub type ToMillauMaximalOutboundPayloadSize = pub struct WithMillauMessageBridge; impl MessageBridge for WithMillauMessageBridge { - const THIS_CHAIN_ID: ChainId = RIALTO_CHAIN_ID; - const BRIDGED_CHAIN_ID: ChainId = MILLAU_CHAIN_ID; const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME; type ThisChain = Rialto; @@ -94,15 +97,6 @@ impl messages::UnderlyingChainProvider for Rialto { impl messages::ThisChainWithMessages for Rialto { type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - - fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, _lane: &LaneId) -> bool { - true - } - - fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { - MessageNonce::MAX - } } /// Millau chain from message lane point of view. @@ -117,9 +111,29 @@ impl messages::UnderlyingChainProvider for Millau { type Chain = bp_millau::Millau; } -impl messages::BridgedChainWithMessages for Millau { - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { - true +impl messages::BridgedChainWithMessages for Millau {} + +/// Export XCM messages to be relayed to Millau. +pub type ToMillauBlobExporter = HaulBlobExporter< + XcmBlobHaulerAdapter, + crate::xcm_config::MillauNetwork, + (), +>; + +/// To-Millau XCM hauler. +pub struct ToMillauXcmBlobHauler; + +impl XcmBlobHauler for ToMillauXcmBlobHauler { + type MessageSender = pallet_bridge_messages::Pallet; + type MessageSenderOrigin = RuntimeOrigin; + + fn message_sender_origin() -> RuntimeOrigin { + pallet_xcm::Origin::from(MultiLocation::new(1, crate::xcm_config::UniversalLocation::get())) + .into() + } + + fn xcm_lane() -> LaneId { + XCM_LANE } } diff --git a/bin/rialto/runtime/src/parachains.rs b/bin/rialto/runtime/src/parachains.rs index 9a3346eafd8..8ceb27140f0 100644 --- a/bin/rialto/runtime/src/parachains.rs +++ b/bin/rialto/runtime/src/parachains.rs @@ -23,7 +23,7 @@ use crate::{ use frame_support::{parameter_types, traits::KeyOwnerProofSystem}; use frame_system::EnsureRoot; -use polkadot_primitives::v2::{ValidatorId, ValidatorIndex}; +use polkadot_primitives::v4::{ValidatorId, ValidatorIndex}; use polkadot_runtime_common::{paras_registrar, paras_sudo_wrapper, slots}; use polkadot_runtime_parachains::{ configuration as parachains_configuration, disputes as parachains_disputes, diff --git a/bin/rialto/runtime/src/xcm_config.rs b/bin/rialto/runtime/src/xcm_config.rs index 174ece04725..9f6488b4c4d 100644 --- a/bin/rialto/runtime/src/xcm_config.rs +++ b/bin/rialto/runtime/src/xcm_config.rs @@ -17,24 +17,22 @@ //! XCM configurations for the Rialto runtime. use super::{ - millau_messages::WithMillauMessageBridge, AccountId, AllPalletsWithSystem, Balances, Runtime, - RuntimeCall, RuntimeEvent, RuntimeOrigin, WithMillauMessagesInstance, XcmPallet, + millau_messages::ToMillauBlobExporter, AccountId, AllPalletsWithSystem, Balances, Runtime, + RuntimeCall, RuntimeEvent, RuntimeOrigin, XcmPallet, }; use bp_rialto::WeightToFee; -use bridge_runtime_common::{ - messages::source::{XcmBridge, XcmBridgeAdapter}, - CustomNetworkId, -}; +use bridge_runtime_common::CustomNetworkId; use frame_support::{ parameter_types, traits::{ConstU32, Everything, Nothing}, weights::Weight, }; +use frame_system::EnsureRoot; use xcm::latest::prelude::*; use xcm_builder::{ - AccountId32Aliases, AllowKnownQueryResponses, AllowTopLevelPaidExecutionFrom, - CurrencyAdapter as XcmCurrencyAdapter, IsConcrete, MintLocation, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + AccountId32Aliases, CurrencyAdapter as XcmCurrencyAdapter, IsConcrete, MintLocation, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + UsingComponents, }; parameter_types! { @@ -95,30 +93,28 @@ parameter_types! { pub const MaxInstructions: u32 = 100; } -/// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our -/// individual routers. -pub type XcmRouter = ( - // Router to send messages to Millau. - XcmBridgeAdapter, -); +/// The XCM router. We are not sending messages to sibling/parent/child chains here. +pub type XcmRouter = (); /// The barriers one of which must be passed for an XCM message to be executed. pub type Barrier = ( // Weight that is paid for may be consumed. TakeWeightCredit, - // If the message is one that immediately attemps to pay for execution, then allow it. - AllowTopLevelPaidExecutionFrom, - // Expected responses are OK. - AllowKnownQueryResponses, ); +/// Dispatches received XCM messages from other chain. +pub type OnRialtoBlobDispatcher = xcm_builder::BridgeBlobDispatcher< + crate::xcm_config::XcmRouter, + crate::xcm_config::UniversalLocation, +>; + /// Incoming XCM weigher type. pub type XcmWeigher = xcm_builder::FixedWeightBounds; pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; + type XcmSender = (); type AssetTransactor = LocalAssetTransactor; type OriginConverter = LocalOriginConverter; type IsReserve = (); @@ -137,7 +133,7 @@ impl xcm_executor::Config for XcmConfig { type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = ConstU32<64>; type FeeManager = (); - type MessageExporter = (); + type MessageExporter = ToMillauBlobExporter; type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; @@ -152,7 +148,7 @@ pub type LocalOriginToLocation = ( #[cfg(feature = "runtime-benchmarks")] parameter_types! { - pub ReachableDest: Option = todo!("We dont use benchmarks for pallet_xcm, so if you hit this message, you need to remove this and define value instead"); + pub ReachableDest: Option = None; } impl pallet_xcm::Config for Runtime { @@ -187,45 +183,24 @@ impl pallet_xcm::Config for Runtime { type WeightInfo = pallet_xcm::TestWeightInfo; #[cfg(feature = "runtime-benchmarks")] type ReachableDest = ReachableDest; -} - -/// With-Millau bridge. -pub struct ToMillauBridge; - -impl XcmBridge for ToMillauBridge { - type MessageBridge = WithMillauMessageBridge; - type MessageSender = pallet_bridge_messages::Pallet; - - fn universal_location() -> InteriorMultiLocation { - UniversalLocation::get() - } - - fn verify_destination(dest: &MultiLocation) -> bool { - matches!(*dest, MultiLocation { parents: 1, interior: X1(GlobalConsensus(r)) } if r == MillauNetwork::get()) - } - - fn build_destination() -> MultiLocation { - let dest: InteriorMultiLocation = MillauNetwork::get().into(); - let here = UniversalLocation::get(); - dest.relative_to(&here) - } - - fn xcm_lane() -> bp_messages::LaneId { - bp_messages::LaneId([0, 0, 0, 0]) - } + type AdminOrigin = EnsureRoot; } #[cfg(test)] mod tests { use super::*; - use crate::millau_messages::WeightCredit; + use crate::{ + millau_messages::{FromMillauMessageDispatch, XCM_LANE}, + WithMillauMessagesInstance, + }; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, LaneId, MessageKey, }; - use bp_runtime::messages::MessageDispatchResult; - use bridge_runtime_common::messages::target::FromBridgedChainMessageDispatch; + use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult; use codec::Encode; + use pallet_bridge_messages::OutboundLanes; + use xcm_executor::XcmExecutor; fn new_test_ext() -> sp_io::TestExternalities { sp_io::TestExternalities::new( @@ -233,53 +208,69 @@ mod tests { ) } - #[test] - fn xcm_messages_to_millau_are_sent() { - new_test_ext().execute_with(|| { - // the encoded message (origin ++ xcm) is 0x010109030419A8 - let dest = (Parent, X1(GlobalConsensus(MillauNetwork::get()))); - let xcm: Xcm<()> = vec![Instruction::Trap(42)].into(); - - let send_result = send_xcm::(dest.into(), xcm); - let expected_fee = MultiAssets::from((Here, 1_000_000_u128)); - let expected_hash = - ([0u8, 0u8, 0u8, 0u8], 1u64).using_encoded(sp_io::hashing::blake2_256); - assert_eq!(send_result, Ok((expected_hash, expected_fee)),); - }) + fn prepare_outbound_xcm_message(destination: NetworkId) -> Xcm { + vec![ExportMessage { + network: destination, + destination: destination.into(), + xcm: vec![Instruction::Trap(42)].into(), + }] + .into() } #[test] - fn xcm_messages_from_millau_are_dispatched() { - type XcmExecutor = xcm_executor::XcmExecutor; - type MessageDispatcher = FromBridgedChainMessageDispatch< - WithMillauMessageBridge, - XcmExecutor, - XcmWeigher, - WeightCredit, - >; - + fn xcm_messages_to_millau_are_sent_using_bridge_exporter() { new_test_ext().execute_with(|| { - let location: MultiLocation = - (Parent, X1(GlobalConsensus(MillauNetwork::get()))).into(); - let xcm: Xcm = vec![Instruction::Trap(42)].into(); - - let mut incoming_message = DispatchMessage { - key: MessageKey { lane_id: LaneId([0, 0, 0, 0]), nonce: 1 }, - data: DispatchMessageData { payload: Ok((location, xcm).into()) }, - }; - - let dispatch_weight = MessageDispatcher::dispatch_weight(&mut incoming_message); - assert_eq!(dispatch_weight, BaseXcmWeight::get()); + // ensure that the there are no messages queued + assert_eq!( + OutboundLanes::::get(XCM_LANE) + .latest_generated_nonce, + 0, + ); - let dispatch_result = - MessageDispatcher::dispatch(&AccountId::from([0u8; 32]), incoming_message); + // export message instruction "sends" message to Rialto + XcmExecutor::::execute_xcm_in_credit( + Here, + prepare_outbound_xcm_message(MillauNetwork::get()), + Default::default(), + Weight::MAX, + Weight::MAX, + ) + .ensure_complete() + .expect("runtime configuration must be correct"); + + // ensure that the message has been queued assert_eq!( - dispatch_result, - MessageDispatchResult { - unspent_weight: frame_support::weights::Weight::zero(), - dispatch_level_result: (), - } + OutboundLanes::::get(XCM_LANE) + .latest_generated_nonce, + 1, ); }) } + + fn prepare_inbound_bridge_message() -> DispatchMessage> { + let xcm = xcm::VersionedXcm::::V3(vec![Instruction::Trap(42)].into()); + let location = + xcm::VersionedInteriorMultiLocation::V3(X1(GlobalConsensus(ThisNetwork::get()))); + // this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor + // or public fields, so just tuple + let bridge_message = (location, xcm).encode(); + DispatchMessage { + key: MessageKey { lane_id: LaneId([0, 0, 0, 0]), nonce: 1 }, + data: DispatchMessageData { payload: Ok(bridge_message) }, + } + } + + #[test] + fn xcm_messages_from_millau_are_dispatched() { + let incoming_message = prepare_inbound_bridge_message(); + + // we care only about handing message to the XCM dispatcher, so we don't care about its + // actual dispatch + let dispatch_result = + FromMillauMessageDispatch::dispatch(&AccountId::from([0u8; 32]), incoming_message); + assert!(matches!( + dispatch_result.dispatch_level_result, + XcmBlobMessageDispatchResult::NotDispatched(_), + )); + } } diff --git a/bin/runtime-common/Cargo.toml b/bin/runtime-common/Cargo.toml index dde9e70f576..e4a5d7ccce1 100644 --- a/bin/runtime-common/Cargo.toml +++ b/bin/runtime-common/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } -hash-db = { version = "0.15.2", default-features = false } +hash-db = { version = "0.16.0", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } static_assertions = { version = "1.1", optional = true } diff --git a/bin/runtime-common/src/integrity.rs b/bin/runtime-common/src/integrity.rs index 5a2c796ee43..96716fe851c 100644 --- a/bin/runtime-common/src/integrity.rs +++ b/bin/runtime-common/src/integrity.rs @@ -83,7 +83,7 @@ macro_rules! assert_bridge_messages_pallet_types( use $crate::messages::{ source::{FromThisChainMessagePayload, TargetHeaderChainAdapter}, target::{FromBridgedChainMessagePayload, SourceHeaderChainAdapter}, - AccountIdOf, BalanceOf, BridgedChain, CallOf, ThisChain, + AccountIdOf, BalanceOf, BridgedChain, ThisChain, }; use pallet_bridge_messages::Config as MessagesConfig; use static_assertions::assert_type_eq_all; diff --git a/bin/runtime-common/src/messages.rs b/bin/runtime-common/src/messages.rs index 4493ac66742..9d2e5811380 100644 --- a/bin/runtime-common/src/messages.rs +++ b/bin/runtime-common/src/messages.rs @@ -25,28 +25,18 @@ pub use bp_runtime::{UnderlyingChainOf, UnderlyingChainProvider}; use bp_header_chain::{HeaderChain, HeaderChainError}; use bp_messages::{ source_chain::{LaneMessageVerifier, TargetHeaderChain}, - target_chain::{ - DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain, - }, + target_chain::{ProvedLaneMessages, ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, }; -use bp_runtime::{ - messages::MessageDispatchResult, Chain, ChainId, RawStorageProof, Size, StorageProofChecker, - StorageProofError, -}; -use codec::{Decode, DecodeLimit, Encode}; +use bp_runtime::{Chain, RawStorageProof, Size, StorageProofChecker, StorageProofError}; +use codec::{Decode, Encode}; use frame_support::{traits::Get, weights::Weight, RuntimeDebug}; use hash_db::Hasher; use scale_info::TypeInfo; use sp_std::{convert::TryFrom, fmt::Debug, marker::PhantomData, vec::Vec}; -use xcm::latest::prelude::*; /// Bidirectional message bridge. pub trait MessageBridge { - /// Identifier of this chain. - const THIS_CHAIN_ID: ChainId; - /// Identifier of the Bridged chain. - const BRIDGED_CHAIN_ID: ChainId; /// Name of the paired messages pallet instance at the Bridged chain. /// /// Should be the name that is used in the `construct_runtime!()` macro. @@ -64,24 +54,10 @@ pub trait MessageBridge { pub trait ThisChainWithMessages: UnderlyingChainProvider { /// Call origin on the chain. type RuntimeOrigin; - /// Call type on the chain. - type RuntimeCall: Encode + Decode; - - /// Do we accept message sent by given origin to given lane? - fn is_message_accepted(origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool; - - /// Maximal number of pending (not yet delivered) messages at This chain. - /// - /// Any messages over this limit, will be rejected. - fn maximal_pending_messages_at_outbound_lane() -> MessageNonce; } /// Bridged chain that has `pallet-bridge-messages` module. -pub trait BridgedChainWithMessages: UnderlyingChainProvider { - /// Returns `true` if message dispatch weight is withing expected limits. `false` means - /// that the message is too heavy to be sent over the bridge and shall be rejected. - fn verify_dispatch_weight(message_payload: &[u8]) -> bool; -} +pub trait BridgedChainWithMessages: UnderlyingChainProvider {} /// This chain in context of message bridge. pub type ThisChain = ::ThisChain; @@ -97,8 +73,6 @@ pub type AccountIdOf = bp_runtime::AccountIdOf>; pub type BalanceOf = bp_runtime::BalanceOf>; /// Type of origin that is used on the chain. pub type OriginOf = ::RuntimeOrigin; -/// Type of call that is used on this chain. -pub type CallOf = ::RuntimeCall; /// Error that happens during message verification. #[derive(Debug, PartialEq, Eq)] @@ -128,7 +102,7 @@ pub mod source { use super::*; /// Message payload for This -> Bridged chain messages. - pub type FromThisChainMessagePayload = Vec; + pub type FromThisChainMessagePayload = crate::messages_xcm_extension::XcmAsPlainPayload; /// Maximal size of outbound message payload. pub struct FromThisChainMaximalOutboundPayloadSize(PhantomData); @@ -186,13 +160,6 @@ pub mod source { #[derive(RuntimeDebug)] pub struct FromThisChainMessageVerifier(PhantomData); - /// The error message returned from `LaneMessageVerifier` when outbound lane is disabled. - pub const MESSAGE_REJECTED_BY_OUTBOUND_LANE: &str = - "The outbound message lane has rejected the message."; - /// The error message returned from `LaneMessageVerifier` when too many pending messages at the - /// lane. - pub const TOO_MANY_PENDING_MESSAGES: &str = "Too many pending messages at the lane."; - impl LaneMessageVerifier>, FromThisChainMessagePayload> for FromThisChainMessageVerifier where @@ -205,24 +172,16 @@ pub mod source { type Error = &'static str; fn verify_message( - submitter: &OriginOf>, - lane: &LaneId, - lane_outbound_data: &OutboundLaneData, + _submitter: &OriginOf>, + _lane: &LaneId, + _lane_outbound_data: &OutboundLaneData, _payload: &FromThisChainMessagePayload, ) -> Result<(), Self::Error> { - // reject message if lane is blocked - if !ThisChain::::is_message_accepted(submitter, lane) { - return Err(MESSAGE_REJECTED_BY_OUTBOUND_LANE) - } - - // reject message if there are too many pending messages at this lane - let max_pending_messages = ThisChain::::maximal_pending_messages_at_outbound_lane(); - let pending_messages = lane_outbound_data - .latest_generated_nonce - .saturating_sub(lane_outbound_data.latest_received_nonce); - if pending_messages > max_pending_messages { - return Err(TOO_MANY_PENDING_MESSAGES) - } + // IMPORTANT: any error that is returned here is fatal for the bridge, because + // this code is executed at the bridge hub and message sender actually lives + // at some sibling parachain. So we are failing **after** the message has been + // sent and we can't report it back to sender (unless error report mechanism is + // embedded into message and its dispatcher). Ok(()) } @@ -263,9 +222,15 @@ pub mod source { pub fn verify_chain_message( payload: &FromThisChainMessagePayload, ) -> Result<(), Error> { - if !BridgedChain::::verify_dispatch_weight(payload) { - return Err(Error::InvalidMessageWeight) - } + // IMPORTANT: any error that is returned here is fatal for the bridge, because + // this code is executed at the bridge hub and message sender actually lives + // at some sibling parachain. So we are failing **after** the message has been + // sent and we can't report it back to sender (unless error report mechanism is + // embedded into message and its dispatcher). + + // apart from maximal message size check (see below), we should also check the message + // dispatch weight here. But we assume that the bridged chain will just push the message + // to some queue (XCMP, UMP, DMP), so the weight is constant and fits the block. // The maximal size of extrinsic at Substrate-based chain depends on the // `frame_system::Config::MaximumBlockLength` and @@ -316,92 +281,6 @@ pub mod source { ) .map_err(Error::HeaderChain)? } - - /// XCM bridge. - pub trait XcmBridge { - /// Runtime message bridge configuration. - type MessageBridge: MessageBridge; - /// Runtime message sender adapter. - type MessageSender: bp_messages::source_chain::MessagesBridge< - OriginOf>, - FromThisChainMessagePayload, - >; - - /// Our location within the Consensus Universe. - fn universal_location() -> InteriorMultiLocation; - /// Verify that the adapter is responsible for handling given XCM destination. - fn verify_destination(dest: &MultiLocation) -> bool; - /// Build route from this chain to the XCM destination. - fn build_destination() -> MultiLocation; - /// Return message lane used to deliver XCM messages. - fn xcm_lane() -> LaneId; - } - - /// XCM bridge adapter for `bridge-messages` pallet. - pub struct XcmBridgeAdapter(PhantomData); - - impl SendXcm for XcmBridgeAdapter - where - BalanceOf>: Into, - OriginOf>: From, - { - type Ticket = FromThisChainMessagePayload; - - fn validate( - dest: &mut Option, - msg: &mut Option>, - ) -> SendResult { - let d = dest.take().ok_or(SendError::MissingArgument)?; - if !T::verify_destination(&d) { - *dest = Some(d); - return Err(SendError::NotApplicable) - } - - let route = T::build_destination(); - let msg = (route, msg.take().ok_or(SendError::MissingArgument)?).encode(); - - // let's just take fixed (out of thin air) fee per message in our test bridges - // (this code won't be used in production anyway) - let fee_assets = MultiAssets::from((Here, 1_000_000_u128)); - - Ok((msg, fee_assets)) - } - - fn deliver(ticket: Self::Ticket) -> Result { - use bp_messages::source_chain::MessagesBridge; - - let lane = T::xcm_lane(); - let msg = ticket; - let result = T::MessageSender::send_message( - pallet_xcm::Origin::from(MultiLocation::from(T::universal_location())).into(), - lane, - msg, - ); - result - .map(|artifacts| { - let hash = (lane, artifacts.nonce).using_encoded(sp_io::hashing::blake2_256); - log::debug!( - target: "runtime::bridge", - "Sent XCM message {:?}/{} to {:?}: {:?}", - lane, - artifacts.nonce, - T::MessageBridge::BRIDGED_CHAIN_ID, - hash, - ); - hash - }) - .map_err(|e| { - log::debug!( - target: "runtime::bridge", - "Failed to send XCM message over lane {:?} to {:?}: {:?}", - lane, - T::MessageBridge::BRIDGED_CHAIN_ID, - e, - ); - SendError::Transport("Bridge has rejected the message") - }) - } - } } /// Sub-module that is declaring types required for processing Bridged -> This chain messages. @@ -409,35 +288,7 @@ pub mod target { use super::*; /// Decoded Bridged -> This message payload. - #[derive(RuntimeDebug, PartialEq, Eq)] - pub struct FromBridgedChainMessagePayload { - /// Data that is actually sent over the wire. - pub xcm: (xcm::v3::MultiLocation, xcm::v3::Xcm), - /// Weight of the message, computed by the weigher. Unknown initially. - pub weight: Option, - } - - impl Decode for FromBridgedChainMessagePayload { - fn decode(input: &mut I) -> Result { - let _: codec::Compact = Decode::decode(input)?; - type XcmPairType = (xcm::v3::MultiLocation, xcm::v3::Xcm); - Ok(FromBridgedChainMessagePayload { - xcm: XcmPairType::::decode_with_depth_limit( - sp_api::MAX_EXTRINSIC_DEPTH, - input, - )?, - weight: None, - }) - } - } - - impl From<(xcm::v3::MultiLocation, xcm::v3::Xcm)> - for FromBridgedChainMessagePayload - { - fn from(xcm: (xcm::v3::MultiLocation, xcm::v3::Xcm)) -> Self { - FromBridgedChainMessagePayload { xcm, weight: None } - } - } + pub type FromBridgedChainMessagePayload = crate::messages_xcm_extension::XcmAsPlainPayload; /// Messages proof from bridged chain: /// @@ -470,118 +321,6 @@ pub mod target { } } - /// Dispatching Bridged -> This chain messages. - #[derive(RuntimeDebug, Clone, Copy)] - pub struct FromBridgedChainMessageDispatch { - _marker: PhantomData<(B, XcmExecutor, XcmWeigher, WeightCredit)>, - } - - impl - MessageDispatch>> - for FromBridgedChainMessageDispatch - where - XcmExecutor: xcm::v3::ExecuteXcm>>, - XcmWeigher: xcm_executor::traits::WeightBounds>>, - WeightCredit: Get, - { - type DispatchPayload = FromBridgedChainMessagePayload>>; - type DispatchLevelResult = (); - - fn dispatch_weight( - message: &mut DispatchMessage, - ) -> frame_support::weights::Weight { - match message.data.payload { - Ok(ref mut payload) => { - // I have no idea why this method takes `&mut` reference and there's nothing - // about that in documentation. Hope it'll only mutate iff error is returned. - let weight = XcmWeigher::weight(&mut payload.xcm.1); - let weight = weight.unwrap_or_else(|e| { - log::debug!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Failed to compute dispatch weight of incoming XCM message {:?}/{}: {:?}", - message.key.lane_id, - message.key.nonce, - e, - ); - - // we shall return 0 and then the XCM executor will fail to execute XCM - // if we'll return something else (e.g. maximal value), the lane may stuck - Weight::zero() - }); - - payload.weight = Some(weight); - weight - }, - _ => Weight::zero(), - } - } - - fn dispatch( - _relayer_account: &AccountIdOf>, - message: DispatchMessage, - ) -> MessageDispatchResult { - let message_id = (message.key.lane_id, message.key.nonce); - let do_dispatch = move || -> sp_std::result::Result { - let FromBridgedChainMessagePayload { xcm: (location, xcm), weight: weight_limit } = - message.data.payload?; - log::trace!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Going to execute message {:?} (weight limit: {:?}): {:?} {:?}", - message_id, - weight_limit, - location, - xcm, - ); - let hash = message_id.using_encoded(sp_io::hashing::blake2_256); - - // if this cod will end up in production, this most likely needs to be set to zero - let weight_credit = WeightCredit::get(); - - let xcm_outcome = XcmExecutor::execute_xcm_in_credit( - location, - xcm, - hash, - weight_limit.unwrap_or_else(Weight::zero), - weight_credit, - ); - Ok(xcm_outcome) - }; - - let xcm_outcome = do_dispatch(); - match xcm_outcome { - Ok(outcome) => { - log::trace!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Incoming message {:?} dispatched with result: {:?}", - message_id, - outcome, - ); - match outcome.ensure_execution() { - Ok(_weight) => (), - Err(e) => { - log::error!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Incoming message {:?} was not dispatched, error: {:?}", - message_id, - e, - ); - }, - } - }, - Err(e) => { - log::error!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Incoming message {:?} was not dispatched, codec error: {:?}", - message_id, - e, - ); - }, - } - - MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } - } - } - /// Return maximal dispatch weight of the message we're able to receive. pub fn maximal_incoming_message_dispatch_weight(maximal_extrinsic_weight: Weight) -> Weight { maximal_extrinsic_weight / 2 @@ -745,54 +484,6 @@ mod tests { use sp_core::H256; use sp_runtime::traits::Header as _; - fn test_lane_outbound_data() -> OutboundLaneData { - OutboundLaneData::default() - } - - fn regular_outbound_message_payload() -> source::FromThisChainMessagePayload { - vec![42] - } - - #[test] - fn message_is_rejected_when_sent_using_disabled_lane() { - assert_eq!( - source::FromThisChainMessageVerifier::::verify_message( - &frame_system::RawOrigin::Root.into(), - &LaneId(*b"dsbl"), - &test_lane_outbound_data(), - ®ular_outbound_message_payload(), - ), - Err(source::MESSAGE_REJECTED_BY_OUTBOUND_LANE) - ); - } - - #[test] - fn message_is_rejected_when_there_are_too_many_pending_messages_at_outbound_lane() { - assert_eq!( - source::FromThisChainMessageVerifier::::verify_message( - &frame_system::RawOrigin::Root.into(), - &TEST_LANE_ID, - &OutboundLaneData { - latest_received_nonce: 100, - latest_generated_nonce: 100 + MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE + 1, - ..Default::default() - }, - ®ular_outbound_message_payload(), - ), - Err(source::TOO_MANY_PENDING_MESSAGES) - ); - } - - #[test] - fn verify_chain_message_rejects_message_with_too_small_declared_weight() { - assert!(source::verify_chain_message::(&vec![ - 42; - BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT - - 1 - ]) - .is_err()); - } - #[test] fn verify_chain_message_rejects_message_with_too_large_declared_weight() { assert!(source::verify_chain_message::(&vec![ diff --git a/bin/runtime-common/src/messages_benchmarking.rs b/bin/runtime-common/src/messages_benchmarking.rs index 028172e7ab4..b067523c305 100644 --- a/bin/runtime-common/src/messages_benchmarking.rs +++ b/bin/runtime-common/src/messages_benchmarking.rs @@ -40,6 +40,39 @@ use pallet_bridge_messages::benchmarking::{MessageDeliveryProofParams, MessagePr use sp_runtime::traits::{Header, Zero}; use sp_std::prelude::*; use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; +use xcm::v3::prelude::*; + +/// Prepare inbound bridge message according to given message proof parameters. +fn prepare_inbound_message( + params: &MessageProofParams, + destination: InteriorMultiLocation, +) -> Vec { + // we only care about **this** message size when message proof needs to be `Minimal` + let expected_size = match params.size { + StorageProofSize::Minimal(size) => size as usize, + _ => 0, + }; + + // if we don't need a correct message, then we may just return some random blob + if !params.is_successful_dispatch_expected { + return vec![0u8; expected_size] + } + + // else let's prepare successful message. For XCM bridge hubs, it is the message that + // will be pushed further to some XCM queue (XCMP/UMP) + let location = xcm::VersionedInteriorMultiLocation::V3(destination); + let location_encoded_size = location.encoded_size(); + + // we don't need to be super-precise with `expected_size` here + let xcm_size = expected_size.saturating_sub(location_encoded_size); + let xcm = xcm::VersionedXcm::<()>::V3(vec![Instruction::ClearOrigin; xcm_size].into()); + + // this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor + // or public fields, so just tuple + // (double encoding, because `.encode()` is called on original Xcm BLOB when it is pushed + // to the storage) + (location, xcm).encode().encode() +} /// Prepare proof of messages for the `receive_messages_proof` call. /// @@ -51,6 +84,7 @@ use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; /// function. pub fn prepare_message_proof_from_grandpa_chain( params: MessageProofParams, + message_destination: InteriorMultiLocation, ) -> (FromBridgedChainMessagesProof>>, Weight) where R: pallet_bridge_grandpa::Config>>, @@ -61,12 +95,9 @@ where let (state_root, storage_proof) = prepare_messages_storage_proof::( params.lane, params.message_nonces.clone(), - params.outbound_lane_data, + params.outbound_lane_data.clone(), params.size, - match params.size { - StorageProofSize::Minimal(ref size) => vec![0u8; *size as _], - _ => vec![], - }, + prepare_inbound_message(¶ms, message_destination), encode_all_messages, encode_lane_data, ); @@ -82,7 +113,7 @@ where nonces_start: *params.message_nonces.start(), nonces_end: *params.message_nonces.end(), }, - Weight::zero(), + Weight::MAX / 1000, ) } @@ -96,6 +127,7 @@ where /// `prepare_message_proof_from_grandpa_chain` function. pub fn prepare_message_proof_from_parachain( params: MessageProofParams, + message_destination: InteriorMultiLocation, ) -> (FromBridgedChainMessagesProof>>, Weight) where R: pallet_bridge_parachains::Config, @@ -107,12 +139,9 @@ where let (state_root, storage_proof) = prepare_messages_storage_proof::( params.lane, params.message_nonces.clone(), - params.outbound_lane_data, + params.outbound_lane_data.clone(), params.size, - match params.size { - StorageProofSize::Minimal(ref size) => vec![0u8; *size as _], - _ => vec![], - }, + prepare_inbound_message(¶ms, message_destination), encode_all_messages, encode_lane_data, ); @@ -129,7 +158,7 @@ where nonces_start: *params.message_nonces.start(), nonces_end: *params.message_nonces.end(), }, - Weight::zero(), + Weight::MAX / 1000, ) } diff --git a/bin/runtime-common/src/messages_xcm_extension.rs b/bin/runtime-common/src/messages_xcm_extension.rs index 7163d8a5357..d4bf2bc1bed 100644 --- a/bin/runtime-common/src/messages_xcm_extension.rs +++ b/bin/runtime-common/src/messages_xcm_extension.rs @@ -28,8 +28,10 @@ use bp_messages::{ }; use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, Chain}; use codec::{Decode, Encode}; -use frame_support::{dispatch::Weight, traits::Get, CloneNoBound, EqNoBound, PartialEqNoBound}; +use frame_support::{dispatch::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; +use pallet_bridge_messages::WeightInfoExt as MessagesPalletWeights; use scale_info::TypeInfo; +use sp_runtime::SaturatedConversion; use xcm_builder::{DispatchBlob, DispatchBlobError, HaulBlob, HaulBlobError}; /// Plain "XCM" payload, which we transfer through bridge @@ -44,17 +46,13 @@ pub enum XcmBlobMessageDispatchResult { } /// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages -pub struct XcmBlobMessageDispatch< - SourceBridgeHubChain, - TargetBridgeHubChain, - DispatchBlob, - DispatchBlobWeigher, -> { +pub struct XcmBlobMessageDispatch +{ _marker: sp_std::marker::PhantomData<( SourceBridgeHubChain, TargetBridgeHubChain, DispatchBlob, - DispatchBlobWeigher, + Weights, )>, } @@ -62,20 +60,21 @@ impl< SourceBridgeHubChain: Chain, TargetBridgeHubChain: Chain, BlobDispatcher: DispatchBlob, - DispatchBlobWeigher: Get, + Weights: MessagesPalletWeights, > MessageDispatch> - for XcmBlobMessageDispatch< - SourceBridgeHubChain, - TargetBridgeHubChain, - BlobDispatcher, - DispatchBlobWeigher, - > + for XcmBlobMessageDispatch { type DispatchPayload = XcmAsPlainPayload; type DispatchLevelResult = XcmBlobMessageDispatchResult; - fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { - DispatchBlobWeigher::get() + fn dispatch_weight(message: &mut DispatchMessage) -> Weight { + match message.data.payload { + Ok(ref payload) => { + let payload_size = payload.encoded_size().saturated_into(); + Weights::message_dispatch_weight(payload_size) + }, + Err(_) => Weight::zero(), + } } fn dispatch( @@ -92,7 +91,6 @@ impl< message.key.nonce ); return MessageDispatchResult { - // TODO:check-parameter - setup uspent_weight? https://github.com/paritytech/polkadot/issues/6629 unspent_weight: Weight::zero(), dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, } @@ -128,11 +126,7 @@ impl< XcmBlobMessageDispatchResult::NotDispatched(e) }, }; - MessageDispatchResult { - // TODO:check-parameter - setup uspent_weight? https://github.com/paritytech/polkadot/issues/6629 - unspent_weight: Weight::zero(), - dispatch_level_result, - } + MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result } } } @@ -159,23 +153,24 @@ impl> HaulBlo { fn haul_blob(blob: sp_std::prelude::Vec) -> Result<(), HaulBlobError> { let lane = H::xcm_lane(); - let result = H::MessageSender::send_message(H::message_sender_origin(), lane, blob); - let result = result - .map(|artifacts| (lane, artifacts.nonce).using_encoded(sp_io::hashing::blake2_256)); - match &result { - Ok(result) => log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "haul_blob result - ok: {:?} on lane: {:?}", - result, - lane - ), - Err(error) => log::error!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "haul_blob result - error: {:?} on lane: {:?}", - error, - lane - ), - }; - result.map(|_| ()).map_err(|_| HaulBlobError::Transport("MessageSenderError")) + H::MessageSender::send_message(H::message_sender_origin(), lane, blob) + .map(|artifacts| (lane, artifacts.nonce).using_encoded(sp_io::hashing::blake2_256)) + .map(|result| { + log::info!( + target: crate::LOG_TARGET_BRIDGE_DISPATCH, + "haul_blob result - ok: {:?} on lane: {:?}", + result, + lane + ) + }) + .map_err(|error| { + log::error!( + target: crate::LOG_TARGET_BRIDGE_DISPATCH, + "haul_blob result - error: {:?} on lane: {:?}", + error, + lane + ); + HaulBlobError::Transport("MessageSenderError") + }) } } diff --git a/bin/runtime-common/src/mock.rs b/bin/runtime-common/src/mock.rs index c2cd8e9ba83..ac6cf122b0f 100644 --- a/bin/runtime-common/src/mock.rs +++ b/bin/runtime-common/src/mock.rs @@ -33,7 +33,7 @@ use crate::messages::{ }; use bp_header_chain::{ChainWithGrandpa, HeaderChain}; -use bp_messages::{target_chain::ForbidInboundMessages, LaneId, MessageNonce}; +use bp_messages::{target_chain::ForbidInboundMessages, LaneId}; use bp_parachains::SingleParaStoredHeaderDataBuilder; use bp_runtime::{Chain, ChainId, Parachain, UnderlyingChainProvider}; use codec::{Decode, Encode}; @@ -87,10 +87,6 @@ pub type BridgedChainHeader = pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]); /// Bridged chain id used in tests. pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg"; -/// Maximal number of queued messages at the test lane. -pub const MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE: MessageNonce = 32; -/// Minimal extrinsic weight at the `BridgedChain`. -pub const BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT: usize = 5; /// Maximal extrinsic weight at the `BridgedChain`. pub const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: usize = 2048; /// Maximal extrinsic size at the `BridgedChain`. @@ -107,7 +103,7 @@ frame_support::construct_runtime! { Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, - BridgeGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, + BridgeGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event}, BridgeParachains: pallet_bridge_parachains::{Pallet, Call, Storage, Event}, BridgeMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, } @@ -176,6 +172,10 @@ impl pallet_balances::Config for TestRuntime { type MaxLocks = ConstU32<50>; type MaxReserves = ConstU32<50>; type ReserveIdentifier = [u8; 8]; + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; } impl pallet_transaction_payment::Config for TestRuntime { @@ -194,6 +194,7 @@ impl pallet_transaction_payment::Config for TestRuntime { } impl pallet_bridge_grandpa::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; type BridgedChain = BridgedUnderlyingChain; type MaxRequests = ConstU32<50>; type HeadersToKeep = ConstU32<8>; @@ -221,7 +222,7 @@ impl pallet_bridge_messages::Config for TestRuntime { type MaximalOutboundPayloadSize = FromThisChainMaximalOutboundPayloadSize; type OutboundPayload = FromThisChainMessagePayload; - type InboundPayload = FromBridgedChainMessagePayload; + type InboundPayload = FromBridgedChainMessagePayload; type InboundRelayer = BridgedChainAccountId; type DeliveryPayments = (); @@ -234,8 +235,7 @@ impl pallet_bridge_messages::Config for TestRuntime { >; type SourceHeaderChain = SourceHeaderChainAdapter; - type MessageDispatch = - ForbidInboundMessages<(), FromBridgedChainMessagePayload>; + type MessageDispatch = ForbidInboundMessages<(), FromBridgedChainMessagePayload>; type BridgedChainId = BridgedChainId; } @@ -252,8 +252,6 @@ impl pallet_bridge_relayers::Config for TestRuntime { pub struct OnThisChainBridge; impl MessageBridge for OnThisChainBridge { - const THIS_CHAIN_ID: ChainId = *b"this"; - const BRIDGED_CHAIN_ID: ChainId = TEST_BRIDGED_CHAIN_ID; const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; type ThisChain = ThisChain; @@ -267,8 +265,6 @@ impl MessageBridge for OnThisChainBridge { pub struct OnBridgedChainBridge; impl MessageBridge for OnBridgedChainBridge { - const THIS_CHAIN_ID: ChainId = TEST_BRIDGED_CHAIN_ID; - const BRIDGED_CHAIN_ID: ChainId = *b"this"; const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; type ThisChain = BridgedChain; @@ -330,22 +326,9 @@ impl UnderlyingChainProvider for ThisChain { impl ThisChainWithMessages for ThisChain { type RuntimeOrigin = ThisChainCallOrigin; - type RuntimeCall = ThisChainRuntimeCall; - - fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool { - *lane == TEST_LANE_ID - } - - fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { - MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE - } } -impl BridgedChainWithMessages for ThisChain { - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { - unreachable!() - } -} +impl BridgedChainWithMessages for ThisChain {} /// Underlying chain of `BridgedChain`. pub struct BridgedUnderlyingChain; @@ -412,20 +395,6 @@ impl UnderlyingChainProvider for BridgedChain { impl ThisChainWithMessages for BridgedChain { type RuntimeOrigin = BridgedChainOrigin; - type RuntimeCall = BridgedChainCall; - - fn is_message_accepted(_send_origin: &Self::RuntimeOrigin, _lane: &LaneId) -> bool { - unreachable!() - } - - fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { - unreachable!() - } } -impl BridgedChainWithMessages for BridgedChain { - fn verify_dispatch_weight(message_payload: &[u8]) -> bool { - message_payload.len() >= BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT && - message_payload.len() <= BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT - } -} +impl BridgedChainWithMessages for BridgedChain {} diff --git a/deployments/bridges/common/generate_messages.sh b/deployments/bridges/common/generate_messages.sh index 156fb5b5d2b..0987ff8987e 100755 --- a/deployments/bridges/common/generate_messages.sh +++ b/deployments/bridges/common/generate_messages.sh @@ -12,7 +12,6 @@ # SECONDARY_EXTRA_ARGS - optional, for example "--use-xcm-pallet" # EXTRA_ARGS - for example "--use-xcm-pallet" # REGULAR_PAYLOAD -# BATCH_PAYLOAD # MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE SECONDARY_EXTRA_ARGS=${SECONDARY_EXTRA_ARGS:-""} @@ -59,7 +58,7 @@ do do $SEND_MESSAGE \ $EXTRA_ARGS \ - raw $BATCH_PAYLOAD + raw $REGULAR_PAYLOAD done fi diff --git a/deployments/bridges/rialto-millau/docker-compose.yml b/deployments/bridges/rialto-millau/docker-compose.yml index ad61b1d174a..5d15a0398b1 100644 --- a/deployments/bridges/rialto-millau/docker-compose.yml +++ b/deployments/bridges/rialto-millau/docker-compose.yml @@ -41,21 +41,10 @@ services: - rialto-node-dave - rialto-node-eve - relay-messages-millau-to-rialto-lane-00000001: - <<: *sub-bridge-relay - environment: - MSG_EXCHANGE_GEN_LANE: "00000001" - entrypoint: /entrypoints/relay-messages-millau-to-rialto-entrypoint.sh - ports: - - "10116:9616" - depends_on: - - relay-millau-rialto - relay-messages-millau-to-rialto-generator: <<: *sub-bridge-relay environment: RUST_LOG: bridge=trace - MSG_EXCHANGE_GEN_SECONDARY_LANE: "00000001" entrypoint: /entrypoints/relay-messages-to-rialto-generator-entrypoint.sh ports: - "10216:9616" @@ -72,22 +61,10 @@ services: depends_on: - relay-messages-millau-to-rialto-generator - relay-messages-rialto-to-millau-lane-00000001: - <<: *sub-bridge-relay - environment: - RUST_LOG: bridge=trace - MSG_EXCHANGE_GEN_LANE: "00000001" - entrypoint: /entrypoints/relay-messages-rialto-to-millau-entrypoint.sh - ports: - - "10416:9616" - depends_on: - - relay-millau-rialto - relay-messages-rialto-to-millau-generator: <<: *sub-bridge-relay environment: RUST_LOG: bridge=trace - MSG_EXCHANGE_GEN_SECONDARY_LANE: "00000001" entrypoint: /entrypoints/relay-messages-to-millau-generator-entrypoint.sh ports: - "10516:9616" diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh index 790e9c82110..a36538ea515 100755 --- a/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh +++ b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh @@ -20,7 +20,8 @@ SEND_MESSAGE="$SHARED_CMD $SHARED_HOST $SOURCE_SIGNER" SOURCE_CHAIN="Rialto" TARGET_CHAIN="Millau" EXTRA_ARGS="" -REGULAR_PAYLOAD="020419ac" -BATCH_PAYLOAD="020419ac" +# It is the encoded `xcm::VersionedXcm::V3(prepare_outbound_xcm_message(MillauNetwork::get())` +# from the `xcm_messages_to_millau_are_sent_using_bridge_exporter` test in the `rialto-runtime` +REGULAR_PAYLOAD="030426030109030419a8" source /common/generate_messages.sh diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh index 3f3105a4d0e..dacb5615229 100755 --- a/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh +++ b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh @@ -20,7 +20,8 @@ SEND_MESSAGE="$SHARED_CMD $SHARED_HOST $SOURCE_SIGNER" SOURCE_CHAIN="Millau" TARGET_CHAIN="Rialto" EXTRA_ARGS="" -REGULAR_PAYLOAD="020419ac" -BATCH_PAYLOAD="020419ac" +# It is the encoded `xcm::VersionedXcm::V3(prepare_outbound_xcm_message(RialtoNetwork::get())` +# from the `xcm_messages_to_rialto_are_sent_using_bridge_exporter` test in the `millau-runtime` +REGULAR_PAYLOAD="030426020109020419a8" source /common/generate_messages.sh diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh index af7ffca56d2..a1306984bca 100755 --- a/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh +++ b/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh @@ -34,5 +34,4 @@ RIALTO_NODE_CONNECTION_PARAMS=$([ -z ${GLOBAL_DEPLOYMENTS} ] && \ --rialto-signer //Millau.HeadersAndMessagesRelay \ --rialto-transactions-mortality=64 \ --lane=00000000 \ - --lane=73776170 \ --prometheus-host=0.0.0.0 diff --git a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh index cf0c910a05b..c09116a1614 100755 --- a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh +++ b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh @@ -21,7 +21,8 @@ SEND_MESSAGE="$SHARED_CMD $SHARED_HOST $SOURCE_SIGNER" SOURCE_CHAIN="RialtoParachain" TARGET_CHAIN="Millau" EXTRA_ARGS="" -REGULAR_PAYLOAD="020419ac" -BATCH_PAYLOAD="010109020419A8" +# It is the encoded `xcm::VersionedXcm::V3(prepare_outbound_xcm_message(MillauNetwork::get())` +# from the `xcm_messages_to_millau_are_sent_using_bridge_exporter` test in the `rialto-parachain-runtime` +REGULAR_PAYLOAD="030426030109030419a8" source /common/generate_messages.sh diff --git a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-generator-entrypoint.sh b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-generator-entrypoint.sh index 7917f7739d5..dbf14ef18bc 100755 --- a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-generator-entrypoint.sh +++ b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-generator-entrypoint.sh @@ -20,7 +20,8 @@ SEND_MESSAGE="$SHARED_CMD $SHARED_HOST $SOURCE_SIGNER" SOURCE_CHAIN="Millau" TARGET_CHAIN="RialtoParachain" EXTRA_ARGS="" -REGULAR_PAYLOAD="020419ac" -BATCH_PAYLOAD="010109020419A8" +# It is the encoded `xcm::VersionedXcm::V3(prepare_outbound_xcm_message(RialtoParachainNetwork::get())` +# from the `xcm_messages_to_rialto_parachain_are_sent_using_bridge_exporter` test in the `millau-runtime` +REGULAR_PAYLOAD="030426040109040419a8" source /common/generate_messages.sh diff --git a/deployments/bridges/rococo-wococo/README.md b/deployments/bridges/rococo-wococo/README.md new file mode 100644 index 00000000000..1c2985a3357 --- /dev/null +++ b/deployments/bridges/rococo-wococo/README.md @@ -0,0 +1,10 @@ +# Rococo Bridge Hub <> Wococo Bridge Hub deployments + +This folder contains some information and useful stuff from our other test deployment - between Rococo and Wococo +bridge hubs. The bridge overview and other helpful information can be found in +[this readme](https://github.com/paritytech/cumulus/tree/bridge-hub-rococo-wococo/parachains/runtimes/bridge-hubs). + +## Grafana Alerts and Dashboards + +JSON model for Grafana alerts and dashobards that we use, may be found in the [dasboard/grafana](./dashboard/grafana/) +folder. diff --git a/deployments/bridges/rococo-wococo/dashboard/grafana/relay-rococo-to-wococo-messages-dashboard.json b/deployments/bridges/rococo-wococo/dashboard/grafana/relay-rococo-to-wococo-messages-dashboard.json new file mode 100644 index 00000000000..d1ecba2acfd --- /dev/null +++ b/deployments/bridges/rococo-wococo/dashboard/grafana/relay-rococo-to-wococo-messages-dashboard.json @@ -0,0 +1,941 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 141, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", + "legendFormat": "At Rococo", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At BridgeHubWococo", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized Rococo headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", + "legendFormat": "At Wococo", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At BridgeHubRococo", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized Wococo headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": true, + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_source_block_number{domain=\"parity-testnet\"}", + "interval": "", + "legendFormat": "At RococoBridgeHub", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized RococoBridgeHub headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": true, + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_target_block_number{domain=\"parity-testnet\"}", + "interval": "", + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_target_at_source_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At RococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized WococoBridgeHub headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "label_replace(label_replace(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\", type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from BridgeHubRococo\", \"type\", \"source_latest_generated\"), \"type\", \"Latest BridgeHubRococo message received by BridgeHubWococo\", \"type\", \"target_latest_received\")", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + } + ], + "title": "Delivery race (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "label_replace(label_replace(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest delivery confirmation from BridgeHubWococo to BridgeHubRococo\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest BridgeHubRococo message received by BridgeHubWococo\", \"type\", \"target_latest_received\")", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + } + ], + "title": "Confirmations race (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 16 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "legendFormat": "Undelivered messages", + "range": true, + "refId": "A" + } + ], + "title": "Delivery race lags (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 16 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "legendFormat": "Unconfirmed messages", + "range": true, + "refId": "A" + } + ], + "title": "Confirmations race lags (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 16 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", + "legendFormat": "Unconfirmed rewards", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "(scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "hide": true, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "Reward lags (00000001)", + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BridgeHubRococo to BridgeHubWococo (00000001)", + "uid": "tkgc6_bnk", + "version": 42, + "weekStart": "" + } \ No newline at end of file diff --git a/deployments/bridges/rococo-wococo/dashboard/grafana/relay-wococo-to-rococo-messages-dashboard.json b/deployments/bridges/rococo-wococo/dashboard/grafana/relay-wococo-to-rococo-messages-dashboard.json new file mode 100644 index 00000000000..be33f0dcecc --- /dev/null +++ b/deployments/bridges/rococo-wococo/dashboard/grafana/relay-wococo-to-rococo-messages-dashboard.json @@ -0,0 +1,941 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 142, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", + "legendFormat": "At Wococo", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At BridgeHubRococo", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized Wococo headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", + "legendFormat": "At Rococo", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized Rococo headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": true, + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_source_block_number{domain=\"parity-testnet\"}", + "interval": "", + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At RococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized WococoBridgeHub headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": true, + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_target_block_number{domain=\"parity-testnet\"}", + "interval": "", + "legendFormat": "At RococoBridgeHub", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_target_at_source_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized RococoBridgeHub headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "label_replace(label_replace(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from BridgeHubWococo\", \"type\", \"source_latest_generated\"), \"type\", \"Latest BridgeHubWococo message received by BridgeHubRococo\", \"type\", \"target_latest_received\")", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + } + ], + "title": "Delivery race (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "label_replace(label_replace(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest delivery confirmation from BridgeHubRococo to BridgeHubWococo\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest BridgeHubWococo message received by BridgeHubRococo\", \"type\", \"target_latest_received\")", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + } + ], + "title": "Confirmations race (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 16 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "legendFormat": "Undelivered messages", + "range": true, + "refId": "A" + } + ], + "title": "Delivery race lags (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 16 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "legendFormat": "Unconfirmed messages", + "range": true, + "refId": "A" + } + ], + "title": "Confirmations race lags (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 16 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", + "legendFormat": "Unconfirmed rewards", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "(scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "hide": true, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "Reward lags (00000001)", + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BridgeHubWococo to BridgeHubRococo (00000001)", + "uid": "zqjpgXxnk", + "version": 30, + "weekStart": "" + } \ No newline at end of file diff --git a/deployments/bridges/rococo-wococo/dashboard/grafana/rococo-wococo-maintenance-dashboard.json b/deployments/bridges/rococo-wococo/dashboard/grafana/rococo-wococo-maintenance-dashboard.json new file mode 100644 index 00000000000..46dc6c43c49 --- /dev/null +++ b/deployments/bridges/rococo-wococo/dashboard/grafana/rococo-wococo-maintenance-dashboard.json @@ -0,0 +1,853 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 284, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 0 + }, + "id": 8, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "name" + }, + "pluginVersion": "9.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": false, + "expr": "substrate_relay_build_info{domain=\"parity-testnet\"}", + "instant": true, + "legendFormat": "{{commit}}", + "range": false, + "refId": "A" + } + ], + "title": "Relay build commit", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 5, + "y": 0 + }, + "id": 9, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^1\\.0\\.1$/", + "values": false + }, + "text": {}, + "textMode": "name" + }, + "pluginVersion": "9.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": false, + "expr": "substrate_relay_build_info{domain=\"parity-testnet\"}", + "instant": true, + "legendFormat": "{{version}}", + "range": false, + "refId": "A" + } + ], + "title": "Relay build version", + "type": "stat" + }, + { + "datasource": { + "type": "loki", + "uid": "P03E52D76DFE188C3" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 10, + "y": 0 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "P03E52D76DFE188C3" + }, + "editorMode": "code", + "expr": "count_over_time({container=\"bridges-common-relay\"} |~ `(?i)(warn|error|fail)` [1m])", + "legendFormat": "Errors per minute", + "queryType": "range", + "refId": "A" + } + ], + "title": "Relay errors/warnings per minute", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 9, + "x": 0, + "y": 5 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "range": true, + "refId": "A" + } + ], + "title": "Rococo headers mismatch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 9, + "x": 9, + "y": 5 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "range": true, + "refId": "A" + } + ], + "title": "Wococo headers mismatch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 9, + "x": 0, + "y": 12 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "range": true, + "refId": "A" + } + ], + "title": "BridgeHubRococo headers mismatch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 9, + "x": 9, + "y": 12 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "range": true, + "refId": "A" + } + ], + "title": "BridgeHubWococo headers mismatch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 0, + "y": 19 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "at_BridgeHubRococo_relay_BridgeHubWococoMessages_balance{domain=\"parity-testnet\"}", + "legendFormat": "Messages Relay Balance", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "at_BridgeHubRococo_relay_WococoHeaders_reward_for_msgs_to_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"} + at_BridgeHubRococo_relay_BridgeHubWococoMessages_reward_for_msgs_to_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "Pending reward", + "range": true, + "refId": "B" + } + ], + "title": "Relay balances at RococoBridgeHub", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 9, + "y": 19 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "at_BridgeHubWococo_relay_BridgeHubRococoMessages_balance{domain=\"parity-testnet\"}", + "legendFormat": "Messages Relay Balance", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "at_BridgeHubRococo_relay_BridgeHubWococoMessages_reward_for_msgs_from_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"} + at_BridgeHubRococo_relay_BridgeHubWococoMessages_reward_for_msgs_to_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "Pending reward", + "range": true, + "refId": "B" + } + ], + "title": "Relay balances at WococoBridgeHub", + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BridgeHubRococo <> BridgeHubWococo maintenance (00000001)", + "uid": "UFsgbJtVz", + "version": 23, + "weekStart": "" + } \ No newline at end of file diff --git a/deployments/networks/millau.yml b/deployments/networks/millau.yml index 6c57a3863ea..0f5846571aa 100644 --- a/deployments/networks/millau.yml +++ b/deployments/networks/millau.yml @@ -21,7 +21,7 @@ services: - --unsafe-rpc-external - --unsafe-ws-external environment: - RUST_LOG: runtime=trace,rpc=debug,txpool=trace,runtime::bridge=trace,sc_basic_authorship=trace,beefy=debug + RUST_LOG: runtime=trace,rpc=debug,txpool=trace,runtime::bridge=trace,sc_basic_authorship=trace,beefy=debug,xcm=trace ports: - "19933:9933" - "19944:9944" diff --git a/deployments/networks/rialto-parachain.yml b/deployments/networks/rialto-parachain.yml index c6dab73ff72..89724183148 100644 --- a/deployments/networks/rialto-parachain.yml +++ b/deployments/networks/rialto-parachain.yml @@ -25,7 +25,7 @@ services: volumes: - rialto-share:/rialto-share:z environment: - RUST_LOG: runtime=trace,rpc=trace,txpool=trace,parachain=trace,parity_ws=trace,sc_basic_authorship=trace + RUST_LOG: runtime=trace,rpc=trace,txpool=trace,parachain=trace,parity_ws=trace,sc_basic_authorship=trace,xcm=trace depends_on: - rialto-chainspec-exporter ports: diff --git a/deployments/networks/rialto.yml b/deployments/networks/rialto.yml index 1e2bdd3e41b..5a8ed64e067 100644 --- a/deployments/networks/rialto.yml +++ b/deployments/networks/rialto.yml @@ -21,7 +21,7 @@ services: - --unsafe-rpc-external - --unsafe-ws-external environment: - RUST_LOG: runtime=trace,rpc=debug,txpool=trace,runtime::bridge=trace,beefy=debug + RUST_LOG: runtime=trace,rpc=debug,txpool=trace,runtime::bridge=trace,beefy=debug,xcm=trace ports: - "9933:9933" - "9944:9944" diff --git a/modules/grandpa/Cargo.toml b/modules/grandpa/Cargo.toml index 095c1d9f4e4..0a737ef633a 100644 --- a/modules/grandpa/Cargo.toml +++ b/modules/grandpa/Cargo.toml @@ -9,7 +9,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } -finality-grandpa = { version = "0.16.0", default-features = false } +finality-grandpa = { version = "0.16.2", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index 2b9e49d4c95..2d81f3106fc 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -96,6 +96,10 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + /// The chain we are bridging to here. type BridgedChain: ChainWithGrandpa; @@ -164,19 +168,19 @@ pub mod pallet { ensure!(Self::request_count() < T::MaxRequests::get(), >::TooManyRequests); - let (hash, number) = (finality_target.hash(), finality_target.number()); + let (hash, number) = (finality_target.hash(), *finality_target.number()); log::trace!( target: LOG_TARGET, "Going to try and finalize header {:?}", finality_target ); - SubmitFinalityProofHelper::::check_obsolete(*number)?; + SubmitFinalityProofHelper::::check_obsolete(number)?; let authority_set = >::get(); let unused_proof_size = authority_set.unused_proof_size(); let set_id = authority_set.set_id; - verify_justification::(&justification, hash, *number, authority_set.into())?; + verify_justification::(&justification, hash, number, authority_set.into())?; let is_authorities_change_enacted = try_enact_authority_change::(&finality_target, set_id)?; @@ -212,6 +216,8 @@ pub mod pallet { let actual_weight = pre_dispatch_weight .set_proof_size(pre_dispatch_weight.proof_size().saturating_sub(unused_proof_size)); + Self::deposit_event(Event::UpdatedBestFinalizedHeader { number, hash }); + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee }) } @@ -370,6 +376,16 @@ pub mod pallet { } } + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// Best finalized chain header has been updated to the header with given number and hash. + UpdatedBestFinalizedHeader { + number: BridgedBlockNumber, + hash: BridgedBlockHash, + }, + } + #[pallet::error] pub enum Error { /// The given justification is invalid for the given header. @@ -635,8 +651,8 @@ pub fn initialize_for_benchmarks, I: 'static>(header: BridgedHeader mod tests { use super::*; use crate::mock::{ - run_test, test_header, RuntimeOrigin, TestBridgedChain, TestHeader, TestNumber, - TestRuntime, MAX_BRIDGED_AUTHORITIES, + run_test, test_header, RuntimeEvent as TestEvent, RuntimeOrigin, System, TestBridgedChain, + TestHeader, TestNumber, TestRuntime, MAX_BRIDGED_AUTHORITIES, }; use bp_header_chain::BridgeGrandpaCall; use bp_runtime::BasicOperatingMode; @@ -649,10 +665,14 @@ mod tests { assert_err, assert_noop, assert_ok, dispatch::PostDispatchInfo, storage::generator::StorageValue, }; + use frame_system::{EventRecord, Phase}; use sp_core::Get; use sp_runtime::{Digest, DigestItem, DispatchError}; fn initialize_substrate_bridge() { + System::set_block_number(1); + System::reset_events(); + assert_ok!(init_with_origin(RuntimeOrigin::root())); } @@ -847,6 +867,18 @@ mod tests { let header = test_header(1); assert_eq!(>::get().unwrap().1, header.hash()); assert!(>::contains_key(header.hash())); + + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Grandpa(Event::UpdatedBestFinalizedHeader { + number: *header.number(), + hash: header.hash(), + }), + topics: vec![], + }], + ); }) } diff --git a/modules/grandpa/src/mock.rs b/modules/grandpa/src/mock.rs index acedfc3582c..b10f5d86c3d 100644 --- a/modules/grandpa/src/mock.rs +++ b/modules/grandpa/src/mock.rs @@ -49,7 +49,7 @@ construct_runtime! { UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system::{Pallet, Call, Config, Storage, Event}, - Grandpa: grandpa::{Pallet, Call}, + Grandpa: grandpa::{Pallet, Call, Event}, } } @@ -69,7 +69,7 @@ impl frame_system::Config for TestRuntime { type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; - type RuntimeEvent = (); + type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU64<250>; type Version = (); type PalletInfo = PalletInfo; @@ -94,6 +94,7 @@ parameter_types! { } impl grandpa::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; type BridgedChain = TestBridgedChain; type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; diff --git a/modules/messages/src/benchmarking.rs b/modules/messages/src/benchmarking.rs index 955dd3da2c5..bc9c1f75257 100644 --- a/modules/messages/src/benchmarking.rs +++ b/modules/messages/src/benchmarking.rs @@ -48,6 +48,11 @@ pub struct MessageProofParams { pub message_nonces: RangeInclusive, /// If `Some`, the proof needs to include this outbound lane data. pub outbound_lane_data: Option, + /// If `true`, the caller expects that the proof will contain correct messages that will + /// be successfully dispatched. This is only called from the "optional" + /// `receive_single_message_proof_with_dispatch` benchmark. If you don't need it, just + /// return `true` from the `is_message_successfully_dispatched`. + pub is_successful_dispatch_expected: bool, /// Proof size requirements. pub size: StorageProofSize, } @@ -95,32 +100,25 @@ pub trait Config: crate::Config { params: MessageDeliveryProofParams, ) -> >::MessagesDeliveryProof; - /// Returns true if message has been dispatched (either successfully or not). - /// - /// We assume that messages have near-zero dispatch weight, so most of times it - /// is hard to determine whether messages has been dispatched or not. For example, - /// XCM message can be a call that leaves entry in `frame_system::Events` vector, - /// but not all XCM messages do that and we don't want to include weight of this - /// action to the base weight of message delivery. Hence, the default `true` return - /// value. - fn is_message_dispatched(_nonce: MessageNonce) -> bool { + /// Returns true if message has been successfully dispatched or not. + fn is_message_successfully_dispatched(_nonce: MessageNonce) -> bool { true } + /// Returns true if given relayer has been rewarded for some of its actions. fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool; } benchmarks_instance_pallet! { // - // Benchmarks that are used directly by the runtime. + // Benchmarks that are used directly by the runtime calls weight formulae. // // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: // * proof does not include outbound lane state proof; // * inbound lane already has state, so it needs to be read and decoded; - // * message is successfully dispatched; - // * message requires all heavy checks done by dispatcher; - // * message dispatch fee is paid at target (this) chain. + // * message is dispatched (reminder: dispatch weight should be minimal); + // * message requires all heavy checks done by dispatcher. // // This is base benchmark for all other message delivery benchmarks. receive_single_message_proof { @@ -135,6 +133,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, + is_successful_dispatch_expected: false, size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -143,15 +142,13 @@ benchmarks_instance_pallet! { crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), 21, ); - assert!(T::is_message_dispatched(21)); } // Benchmark `receive_messages_proof` extrinsic with two minimal-weight messages and following conditions: // * proof does not include outbound lane state proof; // * inbound lane already has state, so it needs to be read and decoded; - // * message is successfully dispatched; - // * message requires all heavy checks done by dispatcher; - // * message dispatch fee is paid at target (this) chain. + // * message is dispatched (reminder: dispatch weight should be minimal); + // * message requires all heavy checks done by dispatcher. // // The weight of single message delivery could be approximated as // `weight(receive_two_messages_proof) - weight(receive_single_message_proof)`. @@ -169,6 +166,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=22, outbound_lane_data: None, + is_successful_dispatch_expected: false, size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 2, dispatch_weight) @@ -177,15 +175,13 @@ benchmarks_instance_pallet! { crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), 22, ); - assert!(T::is_message_dispatched(22)); } // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: // * proof includes outbound lane state proof; // * inbound lane already has state, so it needs to be read and decoded; - // * message is successfully dispatched; - // * message requires all heavy checks done by dispatcher; - // * message dispatch fee is paid at target (this) chain. + // * message is successfully dispatched (reminder: dispatch weight should be minimal); + // * message requires all heavy checks done by dispatcher. // // The weight of outbound lane state delivery would be // `weight(receive_single_message_proof_with_outbound_lane_state) - weight(receive_single_message_proof)`. @@ -207,6 +203,7 @@ benchmarks_instance_pallet! { latest_received_nonce: 20, latest_generated_nonce: 21, }), + is_successful_dispatch_expected: false, size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -214,14 +211,13 @@ benchmarks_instance_pallet! { let lane_state = crate::InboundLanes::::get(&T::bench_lane_id()); assert_eq!(lane_state.last_delivered_nonce(), 21); assert_eq!(lane_state.last_confirmed_nonce, 20); - assert!(T::is_message_dispatched(21)); } // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: - // * the proof has many redundand trie nodes with total size of approximately 1KB; + // * the proof has large leaf with total size of approximately 1KB; // * proof does not include outbound lane state proof; // * inbound lane already has state, so it needs to be read and decoded; - // * message is successfully dispatched; + // * message is dispatched (reminder: dispatch weight should be minimal); // * message requires all heavy checks done by dispatcher. // // With single KB of messages proof, the weight of the call is increased (roughly) by @@ -238,6 +234,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, + is_successful_dispatch_expected: false, size: StorageProofSize::HasLargeLeaf(1024), }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -246,14 +243,13 @@ benchmarks_instance_pallet! { crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), 21, ); - assert!(T::is_message_dispatched(21)); } // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: - // * the proof has many redundand trie nodes with total size of approximately 16KB; + // * the proof has large leaf with total size of approximately 16KB; // * proof does not include outbound lane state proof; // * inbound lane already has state, so it needs to be read and decoded; - // * message is successfully dispatched; + // * message is dispatched (reminder: dispatch weight should be minimal); // * message requires all heavy checks done by dispatcher. // // Size of proof grows because it contains extra trie nodes in it. @@ -272,6 +268,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, + is_successful_dispatch_expected: false, size: StorageProofSize::HasLargeLeaf(16 * 1024), }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -280,7 +277,6 @@ benchmarks_instance_pallet! { crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), 21, ); - assert!(T::is_message_dispatched(21)); } // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: @@ -400,6 +396,47 @@ benchmarks_instance_pallet! { assert!(T::is_relayer_rewarded(&relayer1_id)); assert!(T::is_relayer_rewarded(&relayer2_id)); } + + // + // Benchmarks that the runtime developers may use for proper pallet configuration. + // + + // This benchmark is optional and may be used when runtime developer need a way to compute + // message dispatch weight. In this case, he needs to provide messages that can go the whole + // dispatch + // + // Benchmark `receive_messages_proof` extrinsic with single message and following conditions: + // + // * proof does not include outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is **SUCCESSFULLY** dispatched; + // * message requires all heavy checks done by dispatcher. + receive_single_message_proof_with_dispatch { + // maybe dispatch weight relies on the message size too? + let i in EXPECTED_DEFAULT_MESSAGE_LENGTH .. EXPECTED_DEFAULT_MESSAGE_LENGTH * 16; + + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + T::endow_account(&relayer_id_on_target); + + // mark messages 1..=20 as delivered + receive_messages::(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: T::bench_lane_id(), + message_nonces: 21..=21, + outbound_lane_data: None, + is_successful_dispatch_expected: true, + size: StorageProofSize::Minimal(i), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) + verify { + assert_eq!( + crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), + 21, + ); + assert!(T::is_message_successfully_dispatched(21)); + } } fn send_regular_message, I: 'static>() { diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index a9a25fb9622..3faf00b9e75 100644 --- a/modules/messages/src/lib.rs +++ b/modules/messages/src/lib.rs @@ -749,15 +749,7 @@ fn send_message, I: 'static>( Pallet::::deposit_event(Event::MessageAccepted { lane_id, nonce }); - // we may introduce benchmarks for that, but no heavy ops planned here apart from - // db reads and writes. There are currently 2 db reads and 2 db writes: - // - one db read for operation mode check (`ensure_normal_operating_mode`); - // - one db read for outbound lane state (`outbound_lane`); - // - one db write for outbound lane state (`send_message`); - // - one db write for the message (`send_message`); - let actual_weight = T::DbWeight::get().reads_writes(2, 2); - - Ok(SendMessageArtifacts { nonce, weight: actual_weight }) + Ok(SendMessageArtifacts { nonce }) } /// Ensure that the pallet is in normal operational mode. @@ -970,18 +962,13 @@ mod tests { } } - fn send_regular_message() -> Weight { + fn send_regular_message() { get_ready_for_events(); let message_nonce = outbound_lane::(TEST_LANE_ID).data().latest_generated_nonce + 1; - let weight = send_message::( - RuntimeOrigin::signed(1), - TEST_LANE_ID, - REGULAR_PAYLOAD, - ) - .expect("send_message has failed") - .weight; + send_message::(RuntimeOrigin::signed(1), TEST_LANE_ID, REGULAR_PAYLOAD) + .expect("send_message has failed"); // check event with assigned nonce assert_eq!( @@ -995,8 +982,6 @@ mod tests { topics: vec![], }], ); - - weight } fn receive_messages_delivery_proof() { diff --git a/modules/messages/src/mock.rs b/modules/messages/src/mock.rs index a31ee2b9401..807721ba866 100644 --- a/modules/messages/src/mock.rs +++ b/modules/messages/src/mock.rs @@ -40,7 +40,7 @@ use scale_info::TypeInfo; use sp_core::H256; use sp_runtime::{ testing::Header as SubstrateHeader, - traits::{BlakeTwo256, IdentityLookup}, + traits::{BlakeTwo256, ConstU32, IdentityLookup}, Perbill, }; use std::{ @@ -133,6 +133,10 @@ impl pallet_balances::Config for TestRuntime { type WeightInfo = (); type MaxReserves = (); type ReserveIdentifier = (); + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; } parameter_types! { diff --git a/modules/messages/src/weights.rs b/modules/messages/src/weights.rs index 4b2e5b48ee2..baaef317241 100644 --- a/modules/messages/src/weights.rs +++ b/modules/messages/src/weights.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for RialtoMessages //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -58,6 +58,7 @@ pub trait WeightInfo { fn receive_delivery_proof_for_single_message() -> Weight; fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight; fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight; + fn receive_single_message_proof_with_dispatch(i: u32) -> Weight; } /// Weights for `RialtoMessages` that are generated using one of the Bridge testnets. @@ -79,19 +80,14 @@ impl WeightInfo for BridgeWeight { /// /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: /// 51655, mode: MaxEncodedLen) - /// - /// Storage: Balances TotalIssuance (r:1 w:1) - /// - /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: - /// MaxEncodedLen) fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `693` - // Estimated: `54703` - // Minimum execution time: 48_058 nanoseconds. - Weight::from_parts(50_422_000, 54703) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 52_321 nanoseconds. + Weight::from_parts(54_478_000, 57170) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) /// @@ -107,19 +103,14 @@ impl WeightInfo for BridgeWeight { /// /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: /// 51655, mode: MaxEncodedLen) - /// - /// Storage: Balances TotalIssuance (r:1 w:1) - /// - /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: - /// MaxEncodedLen) fn receive_two_messages_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `693` - // Estimated: `54703` - // Minimum execution time: 59_371 nanoseconds. - Weight::from_parts(61_726_000, 54703) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 64_597 nanoseconds. + Weight::from_parts(69_267_000, 57170) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) /// @@ -135,19 +126,14 @@ impl WeightInfo for BridgeWeight { /// /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: /// 51655, mode: MaxEncodedLen) - /// - /// Storage: Balances TotalIssuance (r:1 w:1) - /// - /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: - /// MaxEncodedLen) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: - // Measured: `693` - // Estimated: `54703` - // Minimum execution time: 53_398 nanoseconds. - Weight::from_parts(54_351_000, 54703) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 64_079 nanoseconds. + Weight::from_parts(65_905_000, 57170) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) /// @@ -166,9 +152,9 @@ impl WeightInfo for BridgeWeight { fn receive_single_message_proof_1_kb() -> Weight { // Proof Size summary in bytes: // Measured: `618` - // Estimated: `54200` - // Minimum execution time: 50_064 nanoseconds. - Weight::from_parts(51_306_000, 54200) + // Estimated: `57170` + // Minimum execution time: 50_588 nanoseconds. + Weight::from_parts(53_544_000, 57170) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -189,9 +175,9 @@ impl WeightInfo for BridgeWeight { fn receive_single_message_proof_16_kb() -> Weight { // Proof Size summary in bytes: // Measured: `618` - // Estimated: `54200` - // Minimum execution time: 75_403 nanoseconds. - Weight::from_parts(77_006_000, 54200) + // Estimated: `57170` + // Minimum execution time: 78_269 nanoseconds. + Weight::from_parts(81_748_000, 57170) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -217,9 +203,9 @@ impl WeightInfo for BridgeWeight { fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: // Measured: `579` - // Estimated: `5624` - // Minimum execution time: 41_670 nanoseconds. - Weight::from_parts(42_863_000, 5624) + // Estimated: `9584` + // Minimum execution time: 45_786 nanoseconds. + Weight::from_parts(47_382_000, 9584) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -245,9 +231,9 @@ impl WeightInfo for BridgeWeight { fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: // Measured: `596` - // Estimated: `5624` - // Minimum execution time: 40_928 nanoseconds. - Weight::from_parts(42_165_000, 5624) + // Estimated: `9584` + // Minimum execution time: 44_544 nanoseconds. + Weight::from_parts(45_451_000, 9584) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -273,12 +259,39 @@ impl WeightInfo for BridgeWeight { fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: // Measured: `596` - // Estimated: `8164` - // Minimum execution time: 44_022 nanoseconds. - Weight::from_parts(44_657_000, 8164) + // Estimated: `12124` + // Minimum execution time: 47_344 nanoseconds. + Weight::from_parts(48_311_000, 12124) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + /// + /// The range of component `i` is `[128, 2048]`. + fn receive_single_message_proof_with_dispatch(i: u32) -> Weight { + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 52_385 nanoseconds. + Weight::from_parts(54_919_468, 57170) + // Standard Error: 108 + .saturating_add(Weight::from_parts(3_286, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } // For backwards compatibility and tests @@ -297,19 +310,14 @@ impl WeightInfo for () { /// /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: /// 51655, mode: MaxEncodedLen) - /// - /// Storage: Balances TotalIssuance (r:1 w:1) - /// - /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: - /// MaxEncodedLen) fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `693` - // Estimated: `54703` - // Minimum execution time: 48_058 nanoseconds. - Weight::from_parts(50_422_000, 54703) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 52_321 nanoseconds. + Weight::from_parts(54_478_000, 57170) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) /// @@ -325,19 +333,14 @@ impl WeightInfo for () { /// /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: /// 51655, mode: MaxEncodedLen) - /// - /// Storage: Balances TotalIssuance (r:1 w:1) - /// - /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: - /// MaxEncodedLen) fn receive_two_messages_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `693` - // Estimated: `54703` - // Minimum execution time: 59_371 nanoseconds. - Weight::from_parts(61_726_000, 54703) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 64_597 nanoseconds. + Weight::from_parts(69_267_000, 57170) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) /// @@ -353,19 +356,14 @@ impl WeightInfo for () { /// /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: /// 51655, mode: MaxEncodedLen) - /// - /// Storage: Balances TotalIssuance (r:1 w:1) - /// - /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: - /// MaxEncodedLen) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: - // Measured: `693` - // Estimated: `54703` - // Minimum execution time: 53_398 nanoseconds. - Weight::from_parts(54_351_000, 54703) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 64_079 nanoseconds. + Weight::from_parts(65_905_000, 57170) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) /// @@ -384,9 +382,9 @@ impl WeightInfo for () { fn receive_single_message_proof_1_kb() -> Weight { // Proof Size summary in bytes: // Measured: `618` - // Estimated: `54200` - // Minimum execution time: 50_064 nanoseconds. - Weight::from_parts(51_306_000, 54200) + // Estimated: `57170` + // Minimum execution time: 50_588 nanoseconds. + Weight::from_parts(53_544_000, 57170) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -407,9 +405,9 @@ impl WeightInfo for () { fn receive_single_message_proof_16_kb() -> Weight { // Proof Size summary in bytes: // Measured: `618` - // Estimated: `54200` - // Minimum execution time: 75_403 nanoseconds. - Weight::from_parts(77_006_000, 54200) + // Estimated: `57170` + // Minimum execution time: 78_269 nanoseconds. + Weight::from_parts(81_748_000, 57170) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -435,9 +433,9 @@ impl WeightInfo for () { fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: // Measured: `579` - // Estimated: `5624` - // Minimum execution time: 41_670 nanoseconds. - Weight::from_parts(42_863_000, 5624) + // Estimated: `9584` + // Minimum execution time: 45_786 nanoseconds. + Weight::from_parts(47_382_000, 9584) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -463,9 +461,9 @@ impl WeightInfo for () { fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: // Measured: `596` - // Estimated: `5624` - // Minimum execution time: 40_928 nanoseconds. - Weight::from_parts(42_165_000, 5624) + // Estimated: `9584` + // Minimum execution time: 44_544 nanoseconds. + Weight::from_parts(45_451_000, 9584) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -491,10 +489,37 @@ impl WeightInfo for () { fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: // Measured: `596` - // Estimated: `8164` - // Minimum execution time: 44_022 nanoseconds. - Weight::from_parts(44_657_000, 8164) + // Estimated: `12124` + // Minimum execution time: 47_344 nanoseconds. + Weight::from_parts(48_311_000, 12124) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + /// + /// The range of component `i` is `[128, 2048]`. + fn receive_single_message_proof_with_dispatch(i: u32) -> Weight { + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 52_385 nanoseconds. + Weight::from_parts(54_919_468, 57170) + // Standard Error: 108 + .saturating_add(Weight::from_parts(3_286, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } diff --git a/modules/messages/src/weights_ext.rs b/modules/messages/src/weights_ext.rs index 5598706b5db..090c03390ba 100644 --- a/modules/messages/src/weights_ext.rs +++ b/modules/messages/src/weights_ext.rs @@ -403,6 +403,21 @@ pub trait WeightInfoExt: WeightInfo { (15 * 1024); proof_size_in_bytes * byte_weight } + + // Functions that may be used by runtime developers. + + /// Returns dispatch weight of message of given size. + /// + /// This function would return correct value only if your runtime is configured to run + /// `receive_single_message_proof_with_dispatch` benchmark. See its requirements for + /// details. + fn message_dispatch_weight(message_size: u32) -> Weight { + // There may be a tiny overweight/underweight here, because we don't account how message + // size affects all steps before dispatch. But the effect should be small enough and we + // may ignore it. + Self::receive_single_message_proof_with_dispatch(message_size) + .saturating_sub(Self::receive_single_message_proof()) + } } impl WeightInfoExt for () { diff --git a/modules/parachains/src/lib.rs b/modules/parachains/src/lib.rs index 20a9ef9b95b..e72e2aec8c2 100644 --- a/modules/parachains/src/lib.rs +++ b/modules/parachains/src/lib.rs @@ -735,14 +735,18 @@ mod tests { }, ) .unwrap(); + + System::::set_block_number(1); + System::::reset_events(); } - fn proceed(num: RelayBlockNumber, state_root: RelayBlockHash) { + fn proceed(num: RelayBlockNumber, state_root: RelayBlockHash) -> ParaHash { pallet_bridge_grandpa::Pallet::::on_initialize( 0, ); let header = test_relay_header(num, state_root); + let hash = header.hash(); let justification = make_default_justification(&header); assert_ok!( pallet_bridge_grandpa::Pallet::::submit_finality_proof( @@ -751,6 +755,8 @@ mod tests { justification, ) ); + + hash } fn prepare_parachain_heads_proof( @@ -1010,7 +1016,7 @@ mod tests { ); // import head#10 of parachain#1 at relay block #1 - proceed(1, state_root_10); + let relay_1_hash = proceed(1, state_root_10); assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10)); assert_eq!( ParasInfo::::get(ParaId(1)), @@ -1043,6 +1049,16 @@ mod tests { }), topics: vec![], }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Grandpa1( + pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { + number: 1, + hash: relay_1_hash, + } + ), + topics: vec![], + }, EventRecord { phase: Phase::Initialization, event: TestEvent::Parachains(Event::UpdatedParachainHead { @@ -1155,7 +1171,7 @@ mod tests { // try to import head#0 of parachain#1 at relay block#1 // => call succeeds, but nothing is changed - proceed(1, state_root); + let relay_1_hash = proceed(1, state_root); assert_ok!(import_parachain_1_head(1, state_root, parachains, proof)); assert_eq!(ParasInfo::::get(ParaId(1)), Some(initial_best_head(1))); assert_eq!( @@ -1169,6 +1185,16 @@ mod tests { }), topics: vec![], }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Grandpa1( + pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { + number: 1, + hash: relay_1_hash, + } + ), + topics: vec![], + }, EventRecord { phase: Phase::Initialization, event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead { @@ -1193,7 +1219,7 @@ mod tests { initialize(state_root_5); // head#10 of parachain#1 at relay block#1 - proceed(1, state_root_10); + let relay_1_hash = proceed(1, state_root_10); assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10)); assert_eq!( ParasInfo::::get(ParaId(1)), @@ -1207,14 +1233,26 @@ mod tests { ); assert_eq!( System::::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::UpdatedParachainHead { - parachain: ParaId(1), - parachain_head_hash: head_data(1, 10).hash(), - }), - topics: vec![], - }], + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Grandpa1( + pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { + number: 1, + hash: relay_1_hash, + } + ), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 10).hash(), + }), + topics: vec![], + } + ], ); // now try to import head#5 at relay block#0 @@ -1233,6 +1271,16 @@ mod tests { assert_eq!( System::::events(), vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Grandpa1( + pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { + number: 1, + hash: relay_1_hash, + } + ), + topics: vec![], + }, EventRecord { phase: Phase::Initialization, event: TestEvent::Parachains(Event::UpdatedParachainHead { diff --git a/modules/parachains/src/mock.rs b/modules/parachains/src/mock.rs index 83d347018e4..c19ce98eba2 100644 --- a/modules/parachains/src/mock.rs +++ b/modules/parachains/src/mock.rs @@ -150,8 +150,8 @@ construct_runtime! { UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system::{Pallet, Call, Config, Storage, Event}, - Grandpa1: pallet_bridge_grandpa::::{Pallet}, - Grandpa2: pallet_bridge_grandpa::::{Pallet}, + Grandpa1: pallet_bridge_grandpa::::{Pallet, Event}, + Grandpa2: pallet_bridge_grandpa::::{Pallet, Event}, Parachains: pallet_bridge_parachains::{Call, Pallet, Event}, } } @@ -197,6 +197,7 @@ parameter_types! { } impl pallet_bridge_grandpa::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; type BridgedChain = TestBridgedChain; type MaxRequests = ConstU32<2>; type HeadersToKeep = HeadersToKeep; @@ -204,6 +205,7 @@ impl pallet_bridge_grandpa::Config for TestRun } impl pallet_bridge_grandpa::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; type BridgedChain = TestBridgedChain; type MaxRequests = ConstU32<2>; type HeadersToKeep = HeadersToKeep; diff --git a/modules/relayers/src/mock.rs b/modules/relayers/src/mock.rs index c40c86f1db5..5e97e81ae33 100644 --- a/modules/relayers/src/mock.rs +++ b/modules/relayers/src/mock.rs @@ -24,7 +24,7 @@ use frame_support::{parameter_types, weights::RuntimeDbWeight}; use sp_core::H256; use sp_runtime::{ testing::Header as SubstrateHeader, - traits::{BlakeTwo256, IdentityLookup}, + traits::{BlakeTwo256, ConstU32, IdentityLookup}, }; pub type AccountId = u64; @@ -86,6 +86,10 @@ impl pallet_balances::Config for TestRuntime { type WeightInfo = (); type MaxReserves = (); type ReserveIdentifier = (); + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; } impl pallet_bridge_relayers::Config for TestRuntime { diff --git a/primitives/chain-bridge-hub-cumulus/src/lib.rs b/primitives/chain-bridge-hub-cumulus/src/lib.rs index cbdd36c2e4a..4c9f9e20468 100644 --- a/primitives/chain-bridge-hub-cumulus/src/lib.rs +++ b/primitives/chain-bridge-hub-cumulus/src/lib.rs @@ -16,12 +16,18 @@ #![cfg_attr(not(feature = "std"), no_std)] -use bp_messages::*; pub use bp_polkadot_core::{ AccountId, AccountInfoStorageMapKeyProvider, AccountPublic, Balance, BlockNumber, Hash, Hasher, Hashing, Header, Index, Nonce, Perbill, Signature, SignedBlock, UncheckedExtrinsic, EXTRA_STORAGE_PROOF_SIZE, TX_EXTRA_BYTES, }; + +use bp_messages::*; +use bp_runtime::extensions::{ + BridgeRejectObsoleteHeadersAndMessages, ChargeTransactionPayment, CheckEra, CheckGenesis, + CheckNonZeroSender, CheckNonce, CheckSpecVersion, CheckTxVersion, CheckWeight, + GenericSignedExtension, RefundBridgedParachainMessagesSchema, +}; use frame_support::{ dispatch::DispatchClass, parameter_types, @@ -47,7 +53,7 @@ pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); /// This is a copy-paste from the cumulus repo's `parachains-common` crate. const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts(constants::WEIGHT_REF_TIME_PER_SECOND, 0) .saturating_div(2) - .set_proof_size(polkadot_primitives::v2::MAX_POV_SIZE as u64); + .set_proof_size(polkadot_primitives::v4::MAX_POV_SIZE as u64); /// All cumulus bridge hubs assume that about 5 percent of the block weight is consumed by /// `on_initialize` handlers. This is used to limit the maximal weight of a single extrinsic. @@ -94,8 +100,6 @@ pub type AccountSigner = MultiSigner; /// The address format for describing accounts. pub type Address = MultiAddress; -pub use bp_polkadot_core::BridgeSignedExtension as SignedExtension; - // Note about selecting values of two following constants: // // Normal transactions have limit of 75% of 1/2 second weight for Cumulus parachains. Let's keep @@ -125,37 +129,54 @@ pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; /// Maximal number of unconfirmed messages at inbound lane for Cumulus-based parachains. pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 4096; -/// Module with rewarding bridge signed extension support -pub mod rewarding_bridge_signed_extension { - use super::*; - use bp_polkadot_core::PolkadotLike; - use bp_runtime::extensions::*; - - type RewardingBridgeSignedExtra = ( - CheckNonZeroSender, - CheckSpecVersion, - CheckTxVersion, - CheckGenesis, - CheckEra, - CheckNonce, - CheckWeight, - ChargeTransactionPayment, - BridgeRejectObsoleteHeadersAndMessages, - RefundBridgedParachainMessagesSchema, - ); +/// Extra signed extension data that is used by all bridge hubs. +pub type SignedExtra = ( + CheckNonZeroSender, + CheckSpecVersion, + CheckTxVersion, + CheckGenesis, + CheckEra, + CheckNonce, + CheckWeight, + ChargeTransactionPayment, + BridgeRejectObsoleteHeadersAndMessages, + RefundBridgedParachainMessagesSchema, +); + +/// Signed extension that is used by all bridge hubs. +pub type SignedExtension = GenericSignedExtension; + +/// Helper trait to define some extra methods on bridge hubs signed extension (and +/// overcome Rust limitations). +pub trait BridgeHubSignedExtension { + /// Create signed extension from its components. + fn from_params( + spec_version: u32, + transaction_version: u32, + era: bp_runtime::TransactionEra, + genesis_hash: Hash, + nonce: Index, + tip: Balance, + ) -> Self; - /// The signed extension used by Cumulus and Cumulus-like parachain with bridging and rewarding. - pub type RewardingBridgeSignedExtension = GenericSignedExtension; + /// Return transaction nonce. + fn nonce(&self) -> Index; + + /// Return transaction tip. + fn tip(&self) -> Balance; +} - pub fn from_params( +impl BridgeHubSignedExtension for SignedExtension { + /// Create signed extension from its components. + fn from_params( spec_version: u32, transaction_version: u32, - era: bp_runtime::TransactionEraOf, + era: bp_runtime::TransactionEra, genesis_hash: Hash, - nonce: Nonce, + nonce: Index, tip: Balance, - ) -> RewardingBridgeSignedExtension { - GenericSignedExtension::::new( + ) -> Self { + GenericSignedExtension::new( ( (), // non-zero sender (), // spec version @@ -166,7 +187,7 @@ pub mod rewarding_bridge_signed_extension { (), // Check weight tip.into(), // transaction payment / tip (compact encoding) (), // bridge reject obsolete headers and msgs - (), // bridge register reward to relayer for message passing + (), // bridge reward to relayer for message passing ), Some(( (), @@ -183,13 +204,13 @@ pub mod rewarding_bridge_signed_extension { ) } - /// Return signer nonce, used to craft transaction. - pub fn nonce(sign_ext: &RewardingBridgeSignedExtension) -> Nonce { - sign_ext.payload.5.into() + /// Return transaction nonce. + fn nonce(&self) -> Index { + self.payload.5 .0 } /// Return transaction tip. - pub fn tip(sign_ext: &RewardingBridgeSignedExtension) -> Balance { - sign_ext.payload.7.into() + fn tip(&self) -> Balance { + self.payload.7 .0 } } diff --git a/primitives/chain-bridge-hub-kusama/Cargo.toml b/primitives/chain-bridge-hub-kusama/Cargo.toml new file mode 100644 index 00000000000..6d4334eaa57 --- /dev/null +++ b/primitives/chain-bridge-hub-kusama/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "bp-bridge-hub-kusama" +description = "Primitives of BridgeHubRococo parachain runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +# Bridge Dependencies + +bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-bridge-hub-cumulus/std", + "bp-messages/std", + "bp-runtime/std", + "frame-support/std", + "sp-api/std", + "sp-std/std", +] diff --git a/primitives/chain-bridge-hub-kusama/src/lib.rs b/primitives/chain-bridge-hub-kusama/src/lib.rs new file mode 100644 index 00000000000..6ca2cd047fb --- /dev/null +++ b/primitives/chain-bridge-hub-kusama/src/lib.rs @@ -0,0 +1,84 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Module with configuration which reflects BridgeHubKusama runtime setup (AccountId, Headers, +//! Hashes...) + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use bp_bridge_hub_cumulus::*; +use bp_messages::*; +use bp_runtime::{ + decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, Parachain, +}; +use frame_support::{ + dispatch::DispatchClass, + sp_runtime::{MultiAddress, MultiSigner}, + RuntimeDebug, +}; +use sp_std::prelude::*; + +/// BridgeHubKusama parachain. +#[derive(RuntimeDebug)] +pub struct BridgeHubKusama; + +impl Chain for BridgeHubKusama { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +impl Parachain for BridgeHubKusama { + const PARACHAIN_ID: u32 = BRIDGE_HUB_KUSAMA_PARACHAIN_ID; +} + +/// Public key of the chain account that may be used to verify signatures. +pub type AccountSigner = MultiSigner; + +/// The address format for describing accounts. +pub type Address = MultiAddress; + +/// Identifier of BridgeHubKusama in the Kusama relay chain. +pub const BRIDGE_HUB_KUSAMA_PARACHAIN_ID: u32 = 1002; + +/// Name of the With-BridgeHubKusama messages pallet instance that is deployed at bridged chains. +// TODO: check me (https://github.com/paritytech/parity-bridges-common/issues/1945) +pub const WITH_BRIDGE_HUB_KUSAMA_MESSAGES_PALLET_NAME: &str = "BridgeKusamaMessages"; + +/// Name of the With-BridgeHubKusama bridge-relayers pallet instance that is deployed at bridged +/// chains. +// TODO: check me (https://github.com/paritytech/parity-bridges-common/issues/1945) +pub const WITH_BRIDGE_HUB_KUSAMA_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; + +decl_bridge_finality_runtime_apis!(bridge_hub_kusama); +decl_bridge_messages_runtime_apis!(bridge_hub_kusama); diff --git a/primitives/chain-bridge-hub-polkadot/Cargo.toml b/primitives/chain-bridge-hub-polkadot/Cargo.toml new file mode 100644 index 00000000000..2a0ab3213c8 --- /dev/null +++ b/primitives/chain-bridge-hub-polkadot/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "bp-bridge-hub-polkadot" +description = "Primitives of BridgeHubWococo parachain runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-bridge-hub-cumulus/std", + "bp-runtime/std", + "bp-messages/std", + "frame-support/std", + "sp-api/std", + "sp-std/std", +] diff --git a/primitives/chain-bridge-hub-polkadot/src/lib.rs b/primitives/chain-bridge-hub-polkadot/src/lib.rs new file mode 100644 index 00000000000..646fb0a6e41 --- /dev/null +++ b/primitives/chain-bridge-hub-polkadot/src/lib.rs @@ -0,0 +1,75 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Module with configuration which reflects BridgeHubPolkadot runtime setup +//! (AccountId, Headers, Hashes...) + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use bp_bridge_hub_cumulus::*; +use bp_messages::*; +use bp_runtime::{ + decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, Parachain, +}; +use frame_support::{dispatch::DispatchClass, RuntimeDebug}; +use sp_std::prelude::*; + +/// BridgeHubPolkadot parachain. +#[derive(RuntimeDebug)] +pub struct BridgeHubPolkadot; + +impl Chain for BridgeHubPolkadot { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +impl Parachain for BridgeHubPolkadot { + const PARACHAIN_ID: u32 = BRIDGE_HUB_POLKADOT_PARACHAIN_ID; +} + +/// Identifier of BridgeHubPolkadot in the Polkadot relay chain. +// TODO: check me (https://github.com/paritytech/parity-bridges-common/issues/1945) +pub const BRIDGE_HUB_POLKADOT_PARACHAIN_ID: u32 = 1002; + +/// Name of the With-BridgeHubPolkadot messages pallet instance that is deployed at bridged chains. +// TODO: check me (https://github.com/paritytech/parity-bridges-common/issues/1945) +pub const WITH_BRIDGE_HUB_POLKADOT_MESSAGES_PALLET_NAME: &str = "BridgePolkadotMessages"; + +/// Name of the With-BridgeHubPolkadot bridge-relayers pallet instance that is deployed at bridged +/// chains. +// TODO: check me (https://github.com/paritytech/parity-bridges-common/issues/1945) +pub const WITH_BRIDGE_HUB_POLKADOT_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; + +decl_bridge_finality_runtime_apis!(bridge_hub_polkadot); +decl_bridge_messages_runtime_apis!(bridge_hub_polkadot); diff --git a/primitives/chain-bridge-hub-rococo/src/lib.rs b/primitives/chain-bridge-hub-rococo/src/lib.rs index 633a021b634..936e4d1beb7 100644 --- a/primitives/chain-bridge-hub-rococo/src/lib.rs +++ b/primitives/chain-bridge-hub-rococo/src/lib.rs @@ -74,5 +74,9 @@ pub const BRIDGE_HUB_ROCOCO_PARACHAIN_ID: u32 = 1013; /// Name of the With-BridgeHubRococo messages pallet instance that is deployed at bridged chains. pub const WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME: &str = "BridgeRococoMessages"; +/// Name of the With-BridgeHubRococo bridge-relayers pallet instance that is deployed at bridged +/// chains. +pub const WITH_BRIDGE_HUB_ROCOCO_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; + decl_bridge_finality_runtime_apis!(bridge_hub_rococo); decl_bridge_messages_runtime_apis!(bridge_hub_rococo); diff --git a/primitives/chain-bridge-hub-wococo/src/lib.rs b/primitives/chain-bridge-hub-wococo/src/lib.rs index e04ca3ff228..00704995c5e 100644 --- a/primitives/chain-bridge-hub-wococo/src/lib.rs +++ b/primitives/chain-bridge-hub-wococo/src/lib.rs @@ -64,5 +64,9 @@ pub const BRIDGE_HUB_WOCOCO_PARACHAIN_ID: u32 = 1014; /// Name of the With-BridgeHubWococo messages pallet instance that is deployed at bridged chains. pub const WITH_BRIDGE_HUB_WOCOCO_MESSAGES_PALLET_NAME: &str = "BridgeWococoMessages"; +/// Name of the With-BridgeHubWococo bridge-relayers pallet instance that is deployed at bridged +/// chains. +pub const WITH_BRIDGE_HUB_WOCOCO_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; + decl_bridge_finality_runtime_apis!(bridge_hub_wococo); decl_bridge_messages_runtime_apis!(bridge_hub_wococo); diff --git a/primitives/chain-kusama/src/lib.rs b/primitives/chain-kusama/src/lib.rs index 9ada688bf11..8e5aec8afda 100644 --- a/primitives/chain-kusama/src/lib.rs +++ b/primitives/chain-kusama/src/lib.rs @@ -56,6 +56,9 @@ impl ChainWithGrandpa for Kusama { const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; } +/// Name of the parachains pallet in the Kusama runtime. +pub const PARAS_PALLET_NAME: &str = "Paras"; + /// Name of the With-Kusama GRANDPA pallet instance that is deployed at bridged chains. pub const WITH_KUSAMA_GRANDPA_PALLET_NAME: &str = "BridgeKusamaGrandpa"; diff --git a/primitives/chain-polkadot/src/lib.rs b/primitives/chain-polkadot/src/lib.rs index e1600102fcd..92995601698 100644 --- a/primitives/chain-polkadot/src/lib.rs +++ b/primitives/chain-polkadot/src/lib.rs @@ -56,6 +56,9 @@ impl ChainWithGrandpa for Polkadot { const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; } +/// Name of the parachains pallet in the Polkadot runtime. +pub const PARAS_PALLET_NAME: &str = "Paras"; + /// Name of the With-Polkadot GRANDPA pallet instance that is deployed at bridged chains. pub const WITH_POLKADOT_GRANDPA_PALLET_NAME: &str = "BridgePolkadotGrandpa"; diff --git a/primitives/chain-rialto-parachain/Cargo.toml b/primitives/chain-rialto-parachain/Cargo.toml index ed7c5c07960..3335c6bcc41 100644 --- a/primitives/chain-rialto-parachain/Cargo.toml +++ b/primitives/chain-rialto-parachain/Cargo.toml @@ -10,6 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" # Bridge Dependencies +bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } bp-messages = { path = "../messages", default-features = false } bp-polkadot-core = { path = "../polkadot-core", default-features = false } bp-runtime = { path = "../runtime", default-features = false } @@ -26,6 +27,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", d [features] default = ["std"] std = [ + "bp-bridge-hub-cumulus/std", "bp-messages/std", "bp-runtime/std", "frame-support/std", diff --git a/primitives/chain-rialto-parachain/src/lib.rs b/primitives/chain-rialto-parachain/src/lib.rs index e49f3d14dc5..67b1a135f34 100644 --- a/primitives/chain-rialto-parachain/src/lib.rs +++ b/primitives/chain-rialto-parachain/src/lib.rs @@ -133,7 +133,9 @@ impl Parachain for RialtoParachain { const PARACHAIN_ID: u32 = RIALTO_PARACHAIN_ID; } -pub use bp_polkadot_core::DefaultSignedExtension as SignedExtension; +// Technically this is incorrect, because rialto-parachain isn't a bridge hub, but we're +// trying to keep it close to the bridge hubs code (at least in this aspect). +pub use bp_bridge_hub_cumulus::SignedExtension; frame_support::parameter_types! { pub BlockLength: limits::BlockLength = diff --git a/primitives/header-chain/Cargo.toml b/primitives/header-chain/Cargo.toml index 0335836e328..ca2f4ad88d3 100644 --- a/primitives/header-chain/Cargo.toml +++ b/primitives/header-chain/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } -finality-grandpa = { version = "0.16.0", default-features = false } +finality-grandpa = { version = "0.16.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true } diff --git a/primitives/messages/src/source_chain.rs b/primitives/messages/src/source_chain.rs index 881bd92f5f9..83f27843d63 100644 --- a/primitives/messages/src/source_chain.rs +++ b/primitives/messages/src/source_chain.rs @@ -20,7 +20,7 @@ use crate::{InboundLaneData, LaneId, MessageNonce, OutboundLaneData}; use crate::UnrewardedRelayer; use bp_runtime::Size; -use frame_support::{weights::Weight, Parameter, RuntimeDebug}; +use frame_support::{Parameter, RuntimeDebug}; use sp_std::{ collections::{btree_map::BTreeMap, vec_deque::VecDeque}, fmt::Debug, @@ -124,8 +124,6 @@ impl DeliveryConfirmationPayments for () { pub struct SendMessageArtifacts { /// Nonce of the message. pub nonce: MessageNonce, - /// Actual weight of send message call. - pub weight: Weight, } /// Messages bridge API to be used from other pallets. @@ -155,7 +153,7 @@ impl MessagesBridge for NoopMessag _lane: LaneId, _message: Payload, ) -> Result { - Ok(SendMessageArtifacts { nonce: 0, weight: Weight::zero() }) + Ok(SendMessageArtifacts { nonce: 0 }) } } diff --git a/primitives/polkadot-core/src/lib.rs b/primitives/polkadot-core/src/lib.rs index 10da6cd9b35..eae2d49a6fd 100644 --- a/primitives/polkadot-core/src/lib.rs +++ b/primitives/polkadot-core/src/lib.rs @@ -37,7 +37,6 @@ use sp_runtime::{ use sp_std::prelude::Vec; // Re-export's to avoid extra substrate dependencies in chain-specific crates. -use bp_runtime::extensions::*; pub use frame_support::{weights::constants::ExtrinsicBaseWeight, Parameter}; pub use sp_runtime::{traits::Convert, Perbill}; @@ -252,142 +251,6 @@ impl Chain for PolkadotLike { } } -/// Some functionality associated with the default signed extension used by Polkadot and -/// Polkadot-like chains. -pub trait PolkadotSignedExtension { - fn from_params( - spec_version: u32, - transaction_version: u32, - era: bp_runtime::TransactionEraOf, - genesis_hash: Hash, - nonce: Nonce, - tip: Balance, - ) -> Self; - - fn nonce(&self) -> Nonce; - - fn tip(&self) -> Balance; -} - -type DefaultSignedExtra = ( - CheckNonZeroSender, - CheckSpecVersion, - CheckTxVersion, - CheckGenesis, - CheckEra, - CheckNonce, - CheckWeight, - ChargeTransactionPayment, -); - -/// The default signed extension used by Polkadot and Polkadot-like chains. -pub type DefaultSignedExtension = GenericSignedExtension; - -impl PolkadotSignedExtension for DefaultSignedExtension { - fn from_params( - spec_version: u32, - transaction_version: u32, - era: bp_runtime::TransactionEraOf, - genesis_hash: Hash, - nonce: Nonce, - tip: Balance, - ) -> Self { - Self::new( - ( - (), // non-zero sender - (), // spec version - (), // tx version - (), // genesis - era.frame_era(), // era - nonce.into(), // nonce (compact encoding) - (), // Check weight - tip.into(), // transaction payment / tip (compact encoding) - ), - Some(( - (), - spec_version, - transaction_version, - genesis_hash, - era.signed_payload(genesis_hash), - (), - (), - (), - )), - ) - } - - /// Return signer nonce, used to craft transaction. - fn nonce(&self) -> Nonce { - self.payload.5.into() - } - - /// Return transaction tip. - fn tip(&self) -> Balance { - self.payload.7.into() - } -} - -type BridgeSignedExtra = ( - CheckNonZeroSender, - CheckSpecVersion, - CheckTxVersion, - CheckGenesis, - CheckEra, - CheckNonce, - CheckWeight, - ChargeTransactionPayment, - BridgeRejectObsoleteHeadersAndMessages, -); - -/// The default signed extension used by Polkadot and Polkadot-like chains with bridging. -pub type BridgeSignedExtension = GenericSignedExtension; - -impl PolkadotSignedExtension for BridgeSignedExtension { - fn from_params( - spec_version: u32, - transaction_version: u32, - era: bp_runtime::TransactionEraOf, - genesis_hash: Hash, - nonce: Nonce, - tip: Balance, - ) -> Self { - Self::new( - ( - (), // non-zero sender - (), // spec version - (), // tx version - (), // genesis - era.frame_era(), // era - nonce.into(), // nonce (compact encoding) - (), // Check weight - tip.into(), // transaction payment / tip (compact encoding) - (), // bridge reject obsolete headers and msgs - ), - Some(( - (), - spec_version, - transaction_version, - genesis_hash, - era.signed_payload(genesis_hash), - (), - (), - (), - (), - )), - ) - } - - /// Return signer nonce, used to craft transaction. - fn nonce(&self) -> Nonce { - self.payload.5.into() - } - - /// Return transaction tip. - fn tip(&self) -> Balance { - self.payload.7.into() - } -} - /// Provides a storage key for account data. /// /// We need to use this approach when we don't have access to the runtime. diff --git a/primitives/relayers/src/lib.rs b/primitives/relayers/src/lib.rs index fb350958415..f14b841fa9e 100644 --- a/primitives/relayers/src/lib.rs +++ b/primitives/relayers/src/lib.rs @@ -21,7 +21,7 @@ use bp_messages::LaneId; use bp_runtime::{ChainId, StorageDoubleMapKeyProvider}; -use frame_support::{Blake2_128Concat, Identity}; +use frame_support::{traits::tokens::Preservation, Blake2_128Concat, Identity}; use scale_info::TypeInfo; use sp_runtime::{ codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}, @@ -110,7 +110,7 @@ where impl PaymentProcedure for PayRewardFromAccount where - T: frame_support::traits::fungible::Transfer, + T: frame_support::traits::fungible::Mutate, Relayer: Decode + Encode, { type Error = sp_runtime::DispatchError; @@ -120,8 +120,13 @@ where rewards_account_params: RewardsAccountParams, reward: T::Balance, ) -> Result<(), Self::Error> { - T::transfer(&Self::rewards_account(rewards_account_params), relayer, reward, false) - .map(drop) + T::transfer( + &Self::rewards_account(rewards_account_params), + relayer, + reward, + Preservation::Expendable, + ) + .map(drop) } } diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index 3cd2fdd35ca..802e58e1af1 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } -hash-db = { version = "0.15.2", default-features = false } +hash-db = { version = "0.16.0", default-features = false } impl-trait-for-tuples = "0.2.2" num-traits = { version = "0.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } @@ -24,7 +24,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -trie-db = { version = "0.26.0", default-features = false } +trie-db = { version = "0.27.1", default-features = false } [dev-dependencies] hex-literal = "0.3" diff --git a/primitives/runtime/src/extensions.rs b/primitives/runtime/src/extensions.rs index ea51d03741c..96ee9d1e6ec 100644 --- a/primitives/runtime/src/extensions.rs +++ b/primitives/runtime/src/extensions.rs @@ -16,7 +16,6 @@ //! Primitives that may be used for creating signed extensions for indirect runtimes. -use crate::{BalanceOf, HashOf}; use codec::{Compact, Decode, Encode}; use impl_trait_for_tuples::impl_for_tuples; use scale_info::{StaticTypeInfo, TypeInfo}; @@ -59,19 +58,19 @@ pub type CheckSpecVersion = GenericSignedExtensionSchema<(), u32>; pub type CheckTxVersion = GenericSignedExtensionSchema<(), u32>; /// The `SignedExtensionSchema` for `frame_system::CheckGenesis`. -pub type CheckGenesis = GenericSignedExtensionSchema<(), HashOf>; +pub type CheckGenesis = GenericSignedExtensionSchema<(), Hash>; /// The `SignedExtensionSchema` for `frame_system::CheckEra`. -pub type CheckEra = GenericSignedExtensionSchema>; +pub type CheckEra = GenericSignedExtensionSchema; /// The `SignedExtensionSchema` for `frame_system::CheckNonce`. -pub type CheckNonce = GenericSignedExtensionSchema, ()>; +pub type CheckNonce = GenericSignedExtensionSchema, ()>; /// The `SignedExtensionSchema` for `frame_system::CheckWeight`. pub type CheckWeight = GenericSignedExtensionSchema<(), ()>; /// The `SignedExtensionSchema` for `pallet_transaction_payment::ChargeTransactionPayment`. -pub type ChargeTransactionPayment = GenericSignedExtensionSchema>, ()>; +pub type ChargeTransactionPayment = GenericSignedExtensionSchema, ()>; /// The `SignedExtensionSchema` for `BridgeRejectObsoleteHeadersAndMessages`. pub type BridgeRejectObsoleteHeadersAndMessages = GenericSignedExtensionSchema<(), ()>; diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index ece925d7952..d4f551ce57a 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -25,8 +25,7 @@ use frame_support::{ }; use frame_system::RawOrigin; use scale_info::TypeInfo; -use sp_core::{hash::H256, storage::StorageKey}; -use sp_io::hashing::blake2_256; +use sp_core::storage::StorageKey; use sp_runtime::traits::{BadOrigin, Header as HeaderT, UniqueSaturatedInto}; use sp_std::{convert::TryFrom, fmt::Debug, vec, vec::Vec}; @@ -59,47 +58,44 @@ pub use sp_runtime::paste; /// Use this when something must be shared among all instances. pub const NO_INSTANCE_ID: ChainId = [0, 0, 0, 0]; -/// Bridge-with-Rialto instance id. +/// Rialto chain id. pub const RIALTO_CHAIN_ID: ChainId = *b"rlto"; -/// Bridge-with-RialtoParachain instance id. +/// RialtoParachain chain id. pub const RIALTO_PARACHAIN_CHAIN_ID: ChainId = *b"rlpa"; -/// Bridge-with-Millau instance id. +/// Millau chain id. pub const MILLAU_CHAIN_ID: ChainId = *b"mlau"; -/// Bridge-with-Polkadot instance id. +/// Polkadot chain id. pub const POLKADOT_CHAIN_ID: ChainId = *b"pdot"; -/// Bridge-with-Kusama instance id. +/// Kusama chain id. pub const KUSAMA_CHAIN_ID: ChainId = *b"ksma"; -/// Bridge-with-Westend instance id. +/// Westend chain id. pub const WESTEND_CHAIN_ID: ChainId = *b"wend"; -/// Bridge-with-Westend instance id. +/// Westend chain id. pub const WESTMINT_CHAIN_ID: ChainId = *b"wmnt"; -/// Bridge-with-Rococo instance id. +/// Rococo chain id. pub const ROCOCO_CHAIN_ID: ChainId = *b"roco"; -/// Bridge-with-Wococo instance id. +/// Wococo chain id. pub const WOCOCO_CHAIN_ID: ChainId = *b"woco"; -/// Bridge-with-BridgeHubRococo instance id. +/// BridgeHubRococo chain id. pub const BRIDGE_HUB_ROCOCO_CHAIN_ID: ChainId = *b"bhro"; -/// Bridge-with-BridgeHubWococo instance id. +/// BridgeHubWococo chain id. pub const BRIDGE_HUB_WOCOCO_CHAIN_ID: ChainId = *b"bhwo"; -/// Call-dispatch module prefix. -pub const CALL_DISPATCH_MODULE_PREFIX: &[u8] = b"pallet-bridge/dispatch"; +/// BridgeHubKusama chain id. +pub const BRIDGE_HUB_KUSAMA_CHAIN_ID: ChainId = *b"bhks"; -/// A unique prefix for entropy when generating cross-chain account IDs. -pub const ACCOUNT_DERIVATION_PREFIX: &[u8] = b"pallet-bridge/account-derivation/account"; - -/// A unique prefix for entropy when generating a cross-chain account ID for the Root account. -pub const ROOT_ACCOUNT_DERIVATION_PREFIX: &[u8] = b"pallet-bridge/account-derivation/root"; +/// BridgeHubPolkadot chain id. +pub const BRIDGE_HUB_POLKADOT_CHAIN_ID: ChainId = *b"bhwo"; /// Generic header Id. #[derive( @@ -164,41 +160,6 @@ impl HeaderIdProvider

for Header { /// used for that. pub type ChainId = [u8; 4]; -/// Type of accounts on the source chain. -pub enum SourceAccount { - /// An account that belongs to Root (privileged origin). - Root, - /// A non-privileged account. - /// - /// The embedded account ID may or may not have a private key depending on the "owner" of the - /// account (private key, pallet, proxy, etc.). - Account(T), -} - -/// Derive an account ID from a foreign account ID. -/// -/// This function returns an encoded Blake2 hash. It is the responsibility of the caller to ensure -/// this can be successfully decoded into an AccountId. -/// -/// The `bridge_id` is used to provide extra entropy when producing account IDs. This helps prevent -/// AccountId collisions between different bridges on a single target chain. -/// -/// Note: If the same `bridge_id` is used across different chains (for example, if one source chain -/// is bridged to multiple target chains), then all the derived accounts would be the same across -/// the different chains. This could negatively impact users' privacy across chains. -pub fn derive_account_id(bridge_id: ChainId, id: SourceAccount) -> H256 -where - AccountId: Encode, -{ - match id { - SourceAccount::Root => - (ROOT_ACCOUNT_DERIVATION_PREFIX, bridge_id).using_encoded(blake2_256), - SourceAccount::Account(id) => - (ACCOUNT_DERIVATION_PREFIX, bridge_id, id).using_encoded(blake2_256), - } - .into() -} - /// Anything that has size. pub trait Size { /// Return size of this object (in bytes). @@ -541,11 +502,11 @@ pub struct StrippableError { } impl From for StrippableError { - fn from(err: T) -> Self { + fn from(_err: T) -> Self { Self { _phantom_data: Default::default(), #[cfg(feature = "std")] - message: format!("{:?}", err), + message: format!("{:?}", _err), } } } diff --git a/primitives/test-utils/Cargo.toml b/primitives/test-utils/Cargo.toml index 8307534093f..5ed835857d1 100644 --- a/primitives/test-utils/Cargo.toml +++ b/primitives/test-utils/Cargo.toml @@ -9,7 +9,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" bp-header-chain = { path = "../header-chain", default-features = false } codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } ed25519-dalek = { version = "1.0", default-features = false, features = ["u64_backend"] } -finality-grandpa = { version = "0.16.0", default-features = false } +finality-grandpa = { version = "0.16.2", default-features = false } sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } diff --git a/relays/bin-substrate/Cargo.toml b/relays/bin-substrate/Cargo.toml index 86974b282d9..6984cfed00c 100644 --- a/relays/bin-substrate/Cargo.toml +++ b/relays/bin-substrate/Cargo.toml @@ -37,8 +37,12 @@ parachains-relay = { path = "../parachains" } relay-millau-client = { path = "../client-millau" } relay-rialto-client = { path = "../client-rialto" } relay-rialto-parachain-client = { path = "../client-rialto-parachain" } +relay-bridge-hub-kusama-client = { path = "../client-bridge-hub-kusama" } +relay-bridge-hub-polkadot-client = { path = "../client-bridge-hub-polkadot" } relay-bridge-hub-rococo-client = { path = "../client-bridge-hub-rococo" } relay-bridge-hub-wococo-client = { path = "../client-bridge-hub-wococo" } +relay-kusama-client = { path = "../client-kusama" } +relay-polkadot-client = { path = "../client-polkadot" } relay-rococo-client = { path = "../client-rococo" } relay-substrate-client = { path = "../client-substrate" } relay-utils = { path = "../utils" } @@ -66,4 +70,4 @@ bp-test-utils = { path = "../../primitives/test-utils" } hex-literal = "0.3" sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } tempfile = "3.4" -finality-grandpa = { version = "0.16.0" } +finality-grandpa = { version = "0.16.2" } diff --git a/relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_kusama_messages_to_bridge_hub_polkadot.rs b/relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_kusama_messages_to_bridge_hub_polkadot.rs new file mode 100644 index 00000000000..9abec22b981 --- /dev/null +++ b/relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_kusama_messages_to_bridge_hub_polkadot.rs @@ -0,0 +1,65 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! BridgeHubKusama-to-BridgeHubPolkadot messages sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge}; +use relay_bridge_hub_kusama_client::BridgeHubKusama; +use relay_bridge_hub_polkadot_client::BridgeHubPolkadot; +use substrate_relay_helper::{messages_lane::SubstrateMessageLane, UtilityPalletBatchCallBuilder}; + +/// BridgeHubKusama-to-BridgeHubPolkadot messages bridge. +pub struct BridgeHubKusamaToBridgeHubPolkadotMessagesCliBridge {} + +impl CliBridgeBase for BridgeHubKusamaToBridgeHubPolkadotMessagesCliBridge { + type Source = BridgeHubKusama; + type Target = BridgeHubPolkadot; +} + +impl MessagesCliBridge for BridgeHubKusamaToBridgeHubPolkadotMessagesCliBridge { + type MessagesLane = BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLane; +} + +substrate_relay_helper::generate_receive_message_proof_call_builder!( + BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLane, + BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLaneReceiveMessagesProofCallBuilder, + relay_bridge_hub_polkadot_client::runtime::Call::BridgeKusamaMessages, + relay_bridge_hub_polkadot_client::runtime::BridgeKusamaMessagesCall::receive_messages_proof +); + +substrate_relay_helper::generate_receive_message_delivery_proof_call_builder!( + BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLane, + BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLaneReceiveMessagesDeliveryProofCallBuilder, + relay_bridge_hub_kusama_client::runtime::Call::BridgePolkadotMessages, + relay_bridge_hub_kusama_client::runtime::BridgePolkadotMessagesCall::receive_messages_delivery_proof +); + +/// BridgeHubKusama-to-BridgeHubPolkadot messages lane. +#[derive(Clone, Debug)] +pub struct BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLane; + +impl SubstrateMessageLane for BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLane { + type SourceChain = BridgeHubKusama; + type TargetChain = BridgeHubPolkadot; + + type ReceiveMessagesProofCallBuilder = + BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLaneReceiveMessagesProofCallBuilder; + type ReceiveMessagesDeliveryProofCallBuilder = + BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLaneReceiveMessagesDeliveryProofCallBuilder; + + type SourceBatchCallBuilder = UtilityPalletBatchCallBuilder; + type TargetBatchCallBuilder = UtilityPalletBatchCallBuilder; +} diff --git a/relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_polkadot_messages_to_bridge_hub_kusama.rs b/relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_polkadot_messages_to_bridge_hub_kusama.rs new file mode 100644 index 00000000000..191a84b27c2 --- /dev/null +++ b/relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_polkadot_messages_to_bridge_hub_kusama.rs @@ -0,0 +1,65 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! BridgeHubPolkadot-to-BridgeHubKusama messages sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge}; +use relay_bridge_hub_kusama_client::BridgeHubKusama; +use relay_bridge_hub_polkadot_client::BridgeHubPolkadot; +use substrate_relay_helper::{messages_lane::SubstrateMessageLane, UtilityPalletBatchCallBuilder}; + +/// BridgeHubPolkadot-to-BridgeHubKusama messages bridge. +pub struct BridgeHubPolkadotToBridgeHubKusamaMessagesCliBridge {} + +impl CliBridgeBase for BridgeHubPolkadotToBridgeHubKusamaMessagesCliBridge { + type Source = BridgeHubPolkadot; + type Target = BridgeHubKusama; +} + +impl MessagesCliBridge for BridgeHubPolkadotToBridgeHubKusamaMessagesCliBridge { + type MessagesLane = BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLane; +} + +substrate_relay_helper::generate_receive_message_proof_call_builder!( + BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLane, + BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLaneReceiveMessagesProofCallBuilder, + relay_bridge_hub_kusama_client::runtime::Call::BridgePolkadotMessages, + relay_bridge_hub_kusama_client::runtime::BridgePolkadotMessagesCall::receive_messages_proof +); + +substrate_relay_helper::generate_receive_message_delivery_proof_call_builder!( + BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLane, + BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLaneReceiveMessagesDeliveryProofCallBuilder, + relay_bridge_hub_polkadot_client::runtime::Call::BridgeKusamaMessages, + relay_bridge_hub_polkadot_client::runtime::BridgeKusamaMessagesCall::receive_messages_delivery_proof +); + +/// BridgeHubPolkadot-to-BridgeHubKusama messages lane. +#[derive(Clone, Debug)] +pub struct BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLane; + +impl SubstrateMessageLane for BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLane { + type SourceChain = BridgeHubPolkadot; + type TargetChain = BridgeHubKusama; + + type ReceiveMessagesProofCallBuilder = + BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLaneReceiveMessagesProofCallBuilder; + type ReceiveMessagesDeliveryProofCallBuilder = + BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLaneReceiveMessagesDeliveryProofCallBuilder; + + type SourceBatchCallBuilder = UtilityPalletBatchCallBuilder; + type TargetBatchCallBuilder = UtilityPalletBatchCallBuilder; +} diff --git a/relays/bin-substrate/src/bridges/kusama_polkadot/kusama_headers_to_bridge_hub_polkadot.rs b/relays/bin-substrate/src/bridges/kusama_polkadot/kusama_headers_to_bridge_hub_polkadot.rs new file mode 100644 index 00000000000..1cfaf922692 --- /dev/null +++ b/relays/bin-substrate/src/bridges/kusama_polkadot/kusama_headers_to_bridge_hub_polkadot.rs @@ -0,0 +1,72 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Kusama-to-BridgeHubPolkadot headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, RelayToRelayHeadersCliBridge}; + +use async_trait::async_trait; +use relay_substrate_client::{AccountKeyPairOf, Client}; +use substrate_relay_helper::{ + finality::{engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline}, + TransactionParams, +}; + +/// Description of Kusama -> PolkadotBridgeHub finalized headers bridge. +#[derive(Clone, Debug)] +pub struct KusamaFinalityToBridgeHubPolkadot; + +substrate_relay_helper::generate_submit_finality_proof_call_builder!( + KusamaFinalityToBridgeHubPolkadot, + KusamaFinalityToBridgeHubPolkadotCallBuilder, + relay_bridge_hub_polkadot_client::runtime::Call::BridgeKusamaGrandpa, + relay_bridge_hub_polkadot_client::runtime::BridgeKusamaGrandpaCall::submit_finality_proof +); + +#[async_trait] +impl SubstrateFinalitySyncPipeline for KusamaFinalityToBridgeHubPolkadot { + type SourceChain = relay_kusama_client::Kusama; + type TargetChain = relay_bridge_hub_polkadot_client::BridgeHubPolkadot; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = KusamaFinalityToBridgeHubPolkadotCallBuilder; + + async fn start_relay_guards( + target_client: &Client, + _transaction_params: &TransactionParams>, + enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + if enable_version_guard { + relay_substrate_client::guard::abort_on_spec_version_change( + target_client.clone(), + target_client.simple_runtime_version().await?.spec_version, + ); + } + Ok(()) + } +} + +/// `Kusama` to BridgeHub `Polkadot` bridge definition. +pub struct KusamaToBridgeHubPolkadotCliBridge {} + +impl CliBridgeBase for KusamaToBridgeHubPolkadotCliBridge { + type Source = relay_kusama_client::Kusama; + type Target = relay_bridge_hub_polkadot_client::BridgeHubPolkadot; +} + +impl RelayToRelayHeadersCliBridge for KusamaToBridgeHubPolkadotCliBridge { + type Finality = KusamaFinalityToBridgeHubPolkadot; +} diff --git a/relays/bin-substrate/src/bridges/kusama_polkadot/kusama_parachains_to_bridge_hub_polkadot.rs b/relays/bin-substrate/src/bridges/kusama_polkadot/kusama_parachains_to_bridge_hub_polkadot.rs new file mode 100644 index 00000000000..e5936640cb3 --- /dev/null +++ b/relays/bin-substrate/src/bridges/kusama_polkadot/kusama_parachains_to_bridge_hub_polkadot.rs @@ -0,0 +1,75 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Kusama-to-BridgeHubPolkadot parachains sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}; +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use relay_substrate_client::{CallOf, HeaderIdOf}; +use substrate_relay_helper::parachains::{ + SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, +}; + +/// Kusama-to-BridgeHubPolkadot parachain sync description. +#[derive(Clone, Debug)] +pub struct BridgeHubKusamaToBridgeHubPolkadot; + +impl SubstrateParachainsPipeline for BridgeHubKusamaToBridgeHubPolkadot { + type SourceParachain = relay_bridge_hub_kusama_client::BridgeHubKusama; + type SourceRelayChain = relay_kusama_client::Kusama; + type TargetChain = relay_bridge_hub_polkadot_client::BridgeHubPolkadot; + + type SubmitParachainHeadsCallBuilder = BridgeHubKusamaToBridgeHubPolkadotCallBuilder; +} + +pub struct BridgeHubKusamaToBridgeHubPolkadotCallBuilder; +impl SubmitParachainHeadsCallBuilder + for BridgeHubKusamaToBridgeHubPolkadotCallBuilder +{ + fn build_submit_parachain_heads_call( + at_relay_block: HeaderIdOf, + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> CallOf { + relay_bridge_hub_polkadot_client::runtime::Call::BridgeKusamaParachain( + relay_bridge_hub_polkadot_client::runtime::BridgeParachainCall::submit_parachain_heads { + at_relay_block: (at_relay_block.0, at_relay_block.1), + parachains, + parachain_heads_proof, + }, + ) + } +} + +/// Kusama-to-BridgeHubPolkadot parachain sync description for the CLI. +pub struct BridgeHubKusamaToBridgeHubPolkadotCliBridge {} + +impl ParachainToRelayHeadersCliBridge for BridgeHubKusamaToBridgeHubPolkadotCliBridge { + type SourceRelay = relay_kusama_client::Kusama; + type ParachainFinality = BridgeHubKusamaToBridgeHubPolkadot; + type RelayFinality = + crate::bridges::kusama_polkadot::kusama_headers_to_bridge_hub_polkadot::KusamaFinalityToBridgeHubPolkadot; +} + +impl CliBridgeBase for BridgeHubKusamaToBridgeHubPolkadotCliBridge { + type Source = relay_bridge_hub_kusama_client::BridgeHubKusama; + type Target = relay_bridge_hub_polkadot_client::BridgeHubPolkadot; +} + +impl MessagesCliBridge for BridgeHubKusamaToBridgeHubPolkadotCliBridge { + type MessagesLane = + crate::bridges::kusama_polkadot::bridge_hub_kusama_messages_to_bridge_hub_polkadot::BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLane; +} diff --git a/relays/bin-substrate/src/bridges/kusama_polkadot/mod.rs b/relays/bin-substrate/src/bridges/kusama_polkadot/mod.rs new file mode 100644 index 00000000000..65cd8d9ded6 --- /dev/null +++ b/relays/bin-substrate/src/bridges/kusama_polkadot/mod.rs @@ -0,0 +1,24 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Declaration of all bridges between Kusama Bridge Hub and Polkadot Bridge Hub. + +pub mod bridge_hub_kusama_messages_to_bridge_hub_polkadot; +pub mod bridge_hub_polkadot_messages_to_bridge_hub_kusama; +pub mod kusama_headers_to_bridge_hub_polkadot; +pub mod kusama_parachains_to_bridge_hub_polkadot; +pub mod polkadot_headers_to_bridge_hub_kusama; +pub mod polkadot_parachains_to_bridge_hub_kusama; diff --git a/relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_headers_to_bridge_hub_kusama.rs b/relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_headers_to_bridge_hub_kusama.rs new file mode 100644 index 00000000000..6827c24768c --- /dev/null +++ b/relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_headers_to_bridge_hub_kusama.rs @@ -0,0 +1,72 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Polkadot-to-KusamaBridgeHub headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, RelayToRelayHeadersCliBridge}; + +use async_trait::async_trait; +use relay_substrate_client::{AccountKeyPairOf, Client}; +use substrate_relay_helper::{ + finality::{engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline}, + TransactionParams, +}; + +/// Description of Polkadot -> KusamaBridgeHub finalized headers bridge. +#[derive(Clone, Debug)] +pub struct PolkadotFinalityToBridgeHubKusama; + +substrate_relay_helper::generate_submit_finality_proof_call_builder!( + PolkadotFinalityToBridgeHubKusama, + PolkadotFinalityToBridgeHubKusamaCallBuilder, + relay_bridge_hub_kusama_client::runtime::Call::BridgePolkadotGrandpa, + relay_bridge_hub_kusama_client::runtime::BridgePolkadotGrandpaCall::submit_finality_proof +); + +#[async_trait] +impl SubstrateFinalitySyncPipeline for PolkadotFinalityToBridgeHubKusama { + type SourceChain = relay_polkadot_client::Polkadot; + type TargetChain = relay_bridge_hub_kusama_client::BridgeHubKusama; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = PolkadotFinalityToBridgeHubKusamaCallBuilder; + + async fn start_relay_guards( + target_client: &Client, + _transaction_params: &TransactionParams>, + enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + if enable_version_guard { + relay_substrate_client::guard::abort_on_spec_version_change( + target_client.clone(), + target_client.simple_runtime_version().await?.spec_version, + ); + } + Ok(()) + } +} + +/// `Polkadot` to BridgeHub `Kusama` bridge definition. +pub struct PolkadotToBridgeHubKusamaCliBridge {} + +impl CliBridgeBase for PolkadotToBridgeHubKusamaCliBridge { + type Source = relay_polkadot_client::Polkadot; + type Target = relay_bridge_hub_kusama_client::BridgeHubKusama; +} + +impl RelayToRelayHeadersCliBridge for PolkadotToBridgeHubKusamaCliBridge { + type Finality = PolkadotFinalityToBridgeHubKusama; +} diff --git a/relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_parachains_to_bridge_hub_kusama.rs b/relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_parachains_to_bridge_hub_kusama.rs new file mode 100644 index 00000000000..f2a7f7309cf --- /dev/null +++ b/relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_parachains_to_bridge_hub_kusama.rs @@ -0,0 +1,75 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Polkadot-to-BridgeHubKusama parachains sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}; +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use relay_substrate_client::{CallOf, HeaderIdOf}; +use substrate_relay_helper::parachains::{ + SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, +}; + +/// Polkadot-to-BridgeHubKusama parachain sync description. +#[derive(Clone, Debug)] +pub struct BridgeHubPolkadotToBridgeHubKusama; + +impl SubstrateParachainsPipeline for BridgeHubPolkadotToBridgeHubKusama { + type SourceParachain = relay_bridge_hub_polkadot_client::BridgeHubPolkadot; + type SourceRelayChain = relay_polkadot_client::Polkadot; + type TargetChain = relay_bridge_hub_kusama_client::BridgeHubKusama; + + type SubmitParachainHeadsCallBuilder = BridgeHubPolkadotToBridgeHubKusamaCallBuilder; +} + +pub struct BridgeHubPolkadotToBridgeHubKusamaCallBuilder; +impl SubmitParachainHeadsCallBuilder + for BridgeHubPolkadotToBridgeHubKusamaCallBuilder +{ + fn build_submit_parachain_heads_call( + at_relay_block: HeaderIdOf, + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> CallOf { + relay_bridge_hub_kusama_client::runtime::Call::BridgePolkadotParachain( + bp_parachains::BridgeParachainCall::submit_parachain_heads { + at_relay_block: (at_relay_block.0, at_relay_block.1), + parachains, + parachain_heads_proof, + }, + ) + } +} + +/// Polkadot-to-BridgeHubKusama parachain sync description for the CLI. +pub struct BridgeHubPolkadotToBridgeHubKusamaCliBridge {} + +impl ParachainToRelayHeadersCliBridge for BridgeHubPolkadotToBridgeHubKusamaCliBridge { + type SourceRelay = relay_polkadot_client::Polkadot; + type ParachainFinality = BridgeHubPolkadotToBridgeHubKusama; + type RelayFinality = + crate::bridges::kusama_polkadot::polkadot_headers_to_bridge_hub_kusama::PolkadotFinalityToBridgeHubKusama; +} + +impl CliBridgeBase for BridgeHubPolkadotToBridgeHubKusamaCliBridge { + type Source = relay_bridge_hub_polkadot_client::BridgeHubPolkadot; + type Target = relay_bridge_hub_kusama_client::BridgeHubKusama; +} + +impl MessagesCliBridge for BridgeHubPolkadotToBridgeHubKusamaCliBridge { + type MessagesLane = + crate::bridges::kusama_polkadot::bridge_hub_polkadot_messages_to_bridge_hub_kusama::BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLane; +} diff --git a/relays/bin-substrate/src/bridges/mod.rs b/relays/bin-substrate/src/bridges/mod.rs new file mode 100644 index 00000000000..62e69cc0e5f --- /dev/null +++ b/relays/bin-substrate/src/bridges/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Declaration of all bridges that the relay is able to serve. + +pub mod kusama_polkadot; +pub mod rialto_millau; +pub mod rialto_parachain_millau; +pub mod rococo_wococo; +pub mod westend_millau; diff --git a/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs b/relays/bin-substrate/src/bridges/rialto_millau/millau_headers_to_rialto.rs similarity index 94% rename from relays/bin-substrate/src/chains/millau_headers_to_rialto.rs rename to relays/bin-substrate/src/bridges/rialto_millau/millau_headers_to_rialto.rs index e136b800b17..f805b29c6a9 100644 --- a/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs +++ b/relays/bin-substrate/src/bridges/rialto_millau/millau_headers_to_rialto.rs @@ -51,5 +51,6 @@ impl RelayToRelayHeadersCliBridge for MillauToRialtoCliBridge { } impl MessagesCliBridge for MillauToRialtoCliBridge { - type MessagesLane = crate::chains::millau_messages_to_rialto::MillauMessagesToRialto; + type MessagesLane = + crate::bridges::rialto_millau::millau_messages_to_rialto::MillauMessagesToRialto; } diff --git a/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs b/relays/bin-substrate/src/bridges/rialto_millau/millau_messages_to_rialto.rs similarity index 100% rename from relays/bin-substrate/src/chains/millau_messages_to_rialto.rs rename to relays/bin-substrate/src/bridges/rialto_millau/millau_messages_to_rialto.rs diff --git a/relays/bin-substrate/src/bridges/rialto_millau/mod.rs b/relays/bin-substrate/src/bridges/rialto_millau/mod.rs new file mode 100644 index 00000000000..2353b58ce61 --- /dev/null +++ b/relays/bin-substrate/src/bridges/rialto_millau/mod.rs @@ -0,0 +1,22 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Declaration of all bridges between Rialto and Millau. + +pub mod millau_headers_to_rialto; +pub mod millau_messages_to_rialto; +pub mod rialto_headers_to_millau; +pub mod rialto_messages_to_millau; diff --git a/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs b/relays/bin-substrate/src/bridges/rialto_millau/rialto_headers_to_millau.rs similarity index 94% rename from relays/bin-substrate/src/chains/rialto_headers_to_millau.rs rename to relays/bin-substrate/src/bridges/rialto_millau/rialto_headers_to_millau.rs index 208b2638af0..7c979f52795 100644 --- a/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs +++ b/relays/bin-substrate/src/bridges/rialto_millau/rialto_headers_to_millau.rs @@ -51,5 +51,6 @@ impl RelayToRelayHeadersCliBridge for RialtoToMillauCliBridge { } impl MessagesCliBridge for RialtoToMillauCliBridge { - type MessagesLane = crate::chains::rialto_messages_to_millau::RialtoMessagesToMillau; + type MessagesLane = + crate::bridges::rialto_millau::rialto_messages_to_millau::RialtoMessagesToMillau; } diff --git a/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs b/relays/bin-substrate/src/bridges/rialto_millau/rialto_messages_to_millau.rs similarity index 100% rename from relays/bin-substrate/src/chains/rialto_messages_to_millau.rs rename to relays/bin-substrate/src/bridges/rialto_millau/rialto_messages_to_millau.rs diff --git a/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs b/relays/bin-substrate/src/bridges/rialto_parachain_millau/millau_headers_to_rialto_parachain.rs similarity index 96% rename from relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs rename to relays/bin-substrate/src/bridges/rialto_parachain_millau/millau_headers_to_rialto_parachain.rs index 4a99d73147b..d1c090c0797 100644 --- a/relays/bin-substrate/src/chains/millau_headers_to_rialto_parachain.rs +++ b/relays/bin-substrate/src/bridges/rialto_parachain_millau/millau_headers_to_rialto_parachain.rs @@ -72,5 +72,5 @@ impl RelayToRelayHeadersCliBridge for MillauToRialtoParachainCliBridge { impl MessagesCliBridge for MillauToRialtoParachainCliBridge { type MessagesLane = - crate::chains::millau_messages_to_rialto_parachain::MillauMessagesToRialtoParachain; + crate::bridges::rialto_parachain_millau::millau_messages_to_rialto_parachain::MillauMessagesToRialtoParachain; } diff --git a/relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs b/relays/bin-substrate/src/bridges/rialto_parachain_millau/millau_messages_to_rialto_parachain.rs similarity index 100% rename from relays/bin-substrate/src/chains/millau_messages_to_rialto_parachain.rs rename to relays/bin-substrate/src/bridges/rialto_parachain_millau/millau_messages_to_rialto_parachain.rs diff --git a/relays/bin-substrate/src/bridges/rialto_parachain_millau/mod.rs b/relays/bin-substrate/src/bridges/rialto_parachain_millau/mod.rs new file mode 100644 index 00000000000..f0613d1511e --- /dev/null +++ b/relays/bin-substrate/src/bridges/rialto_parachain_millau/mod.rs @@ -0,0 +1,22 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Declaration of all bridges between Rialto Parachain and Millau. + +pub mod millau_headers_to_rialto_parachain; +pub mod millau_messages_to_rialto_parachain; +pub mod rialto_parachain_messages_to_millau; +pub mod rialto_parachains_to_millau; diff --git a/relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs b/relays/bin-substrate/src/bridges/rialto_parachain_millau/rialto_parachain_messages_to_millau.rs similarity index 100% rename from relays/bin-substrate/src/chains/rialto_parachain_messages_to_millau.rs rename to relays/bin-substrate/src/bridges/rialto_parachain_millau/rialto_parachain_messages_to_millau.rs diff --git a/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs b/relays/bin-substrate/src/bridges/rialto_parachain_millau/rialto_parachains_to_millau.rs similarity index 91% rename from relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs rename to relays/bin-substrate/src/bridges/rialto_parachain_millau/rialto_parachains_to_millau.rs index 37eb848fe5f..04f2b5aa7c7 100644 --- a/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs +++ b/relays/bin-substrate/src/bridges/rialto_parachain_millau/rialto_parachains_to_millau.rs @@ -55,10 +55,11 @@ impl CliBridgeBase for RialtoParachainToMillauCliBridge { impl ParachainToRelayHeadersCliBridge for RialtoParachainToMillauCliBridge { type SourceRelay = Rialto; type ParachainFinality = RialtoParachainsToMillau; - type RelayFinality = crate::chains::rialto_headers_to_millau::RialtoFinalityToMillau; + type RelayFinality = + crate::bridges::rialto_millau::rialto_headers_to_millau::RialtoFinalityToMillau; } impl MessagesCliBridge for RialtoParachainToMillauCliBridge { type MessagesLane = - crate::chains::rialto_parachain_messages_to_millau::RialtoParachainMessagesToMillau; + crate::bridges::rialto_parachain_millau::rialto_parachain_messages_to_millau::RialtoParachainMessagesToMillau; } diff --git a/relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs similarity index 100% rename from relays/bin-substrate/src/chains/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs rename to relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs diff --git a/relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs similarity index 100% rename from relays/bin-substrate/src/chains/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs rename to relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs diff --git a/relays/bin-substrate/src/bridges/rococo_wococo/mod.rs b/relays/bin-substrate/src/bridges/rococo_wococo/mod.rs new file mode 100644 index 00000000000..64330a92252 --- /dev/null +++ b/relays/bin-substrate/src/bridges/rococo_wococo/mod.rs @@ -0,0 +1,24 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Declaration of all bridges between Rococo Bridge Hub and Wococo Bridge Hub. + +pub mod bridge_hub_rococo_messages_to_bridge_hub_wococo; +pub mod bridge_hub_wococo_messages_to_bridge_hub_rococo; +pub mod rococo_headers_to_bridge_hub_wococo; +pub mod rococo_parachains_to_bridge_hub_wococo; +pub mod wococo_headers_to_bridge_hub_rococo; +pub mod wococo_parachains_to_bridge_hub_rococo; diff --git a/relays/bin-substrate/src/chains/rococo_headers_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/rococo_headers_to_bridge_hub_wococo.rs similarity index 100% rename from relays/bin-substrate/src/chains/rococo_headers_to_bridge_hub_wococo.rs rename to relays/bin-substrate/src/bridges/rococo_wococo/rococo_headers_to_bridge_hub_wococo.rs diff --git a/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/rococo_parachains_to_bridge_hub_wococo.rs similarity index 92% rename from relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs rename to relays/bin-substrate/src/bridges/rococo_wococo/rococo_parachains_to_bridge_hub_wococo.rs index ba0c10beae4..78b4ff35a7d 100644 --- a/relays/bin-substrate/src/chains/rococo_parachains_to_bridge_hub_wococo.rs +++ b/relays/bin-substrate/src/bridges/rococo_wococo/rococo_parachains_to_bridge_hub_wococo.rs @@ -61,7 +61,7 @@ impl ParachainToRelayHeadersCliBridge for BridgeHubRococoToBridgeHubWococoCliBri type SourceRelay = relay_rococo_client::Rococo; type ParachainFinality = BridgeHubRococoToBridgeHubWococo; type RelayFinality = - crate::chains::rococo_headers_to_bridge_hub_wococo::RococoFinalityToBridgeHubWococo; + crate::bridges::rococo_wococo::rococo_headers_to_bridge_hub_wococo::RococoFinalityToBridgeHubWococo; } impl CliBridgeBase for BridgeHubRococoToBridgeHubWococoCliBridge { @@ -71,5 +71,5 @@ impl CliBridgeBase for BridgeHubRococoToBridgeHubWococoCliBridge { impl MessagesCliBridge for BridgeHubRococoToBridgeHubWococoCliBridge { type MessagesLane = - crate::chains::bridge_hub_rococo_messages_to_bridge_hub_wococo::BridgeHubRococoMessagesToBridgeHubWococoMessageLane; + crate::bridges::rococo_wococo::bridge_hub_rococo_messages_to_bridge_hub_wococo::BridgeHubRococoMessagesToBridgeHubWococoMessageLane; } diff --git a/relays/bin-substrate/src/chains/wococo_headers_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/wococo_headers_to_bridge_hub_rococo.rs similarity index 100% rename from relays/bin-substrate/src/chains/wococo_headers_to_bridge_hub_rococo.rs rename to relays/bin-substrate/src/bridges/rococo_wococo/wococo_headers_to_bridge_hub_rococo.rs diff --git a/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/wococo_parachains_to_bridge_hub_rococo.rs similarity index 92% rename from relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs rename to relays/bin-substrate/src/bridges/rococo_wococo/wococo_parachains_to_bridge_hub_rococo.rs index 028d8c9e17d..bc76e377c92 100644 --- a/relays/bin-substrate/src/chains/wococo_parachains_to_bridge_hub_rococo.rs +++ b/relays/bin-substrate/src/bridges/rococo_wococo/wococo_parachains_to_bridge_hub_rococo.rs @@ -61,7 +61,7 @@ impl ParachainToRelayHeadersCliBridge for BridgeHubWococoToBridgeHubRococoCliBri type SourceRelay = relay_wococo_client::Wococo; type ParachainFinality = BridgeHubWococoToBridgeHubRococo; type RelayFinality = - crate::chains::wococo_headers_to_bridge_hub_rococo::WococoFinalityToBridgeHubRococo; + crate::bridges::rococo_wococo::wococo_headers_to_bridge_hub_rococo::WococoFinalityToBridgeHubRococo; } impl CliBridgeBase for BridgeHubWococoToBridgeHubRococoCliBridge { @@ -71,5 +71,5 @@ impl CliBridgeBase for BridgeHubWococoToBridgeHubRococoCliBridge { impl MessagesCliBridge for BridgeHubWococoToBridgeHubRococoCliBridge { type MessagesLane = - crate::chains::bridge_hub_wococo_messages_to_bridge_hub_rococo::BridgeHubWococoMessagesToBridgeHubRococoMessageLane; + crate::bridges::rococo_wococo::bridge_hub_wococo_messages_to_bridge_hub_rococo::BridgeHubWococoMessagesToBridgeHubRococoMessageLane; } diff --git a/relays/bin-substrate/src/bridges/westend_millau/mod.rs b/relays/bin-substrate/src/bridges/westend_millau/mod.rs new file mode 100644 index 00000000000..10bc19241ce --- /dev/null +++ b/relays/bin-substrate/src/bridges/westend_millau/mod.rs @@ -0,0 +1,20 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Declaration of all bridges between Westend and Millau. + +pub mod westend_headers_to_millau; +pub mod westend_parachains_to_millau; diff --git a/relays/bin-substrate/src/chains/westend_headers_to_millau.rs b/relays/bin-substrate/src/bridges/westend_millau/westend_headers_to_millau.rs similarity index 100% rename from relays/bin-substrate/src/chains/westend_headers_to_millau.rs rename to relays/bin-substrate/src/bridges/westend_millau/westend_headers_to_millau.rs diff --git a/relays/bin-substrate/src/chains/westend_parachains_to_millau.rs b/relays/bin-substrate/src/bridges/westend_millau/westend_parachains_to_millau.rs similarity index 95% rename from relays/bin-substrate/src/chains/westend_parachains_to_millau.rs rename to relays/bin-substrate/src/bridges/westend_millau/westend_parachains_to_millau.rs index abd9e6137bb..0556ecab368 100644 --- a/relays/bin-substrate/src/chains/westend_parachains_to_millau.rs +++ b/relays/bin-substrate/src/bridges/westend_millau/westend_parachains_to_millau.rs @@ -49,7 +49,8 @@ pub struct WestmintToMillauCliBridge {} impl ParachainToRelayHeadersCliBridge for WestmintToMillauCliBridge { type SourceRelay = Westend; type ParachainFinality = WestendParachainsToMillau; - type RelayFinality = crate::chains::westend_headers_to_millau::WestendFinalityToMillau; + type RelayFinality = + crate::bridges::westend_millau::westend_headers_to_millau::WestendFinalityToMillau; } impl CliBridgeBase for WestmintToMillauCliBridge { diff --git a/relays/bin-substrate/src/chains/kusama.rs b/relays/bin-substrate/src/chains/kusama.rs new file mode 100644 index 00000000000..6d5a4764f91 --- /dev/null +++ b/relays/bin-substrate/src/chains/kusama.rs @@ -0,0 +1,32 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Kusama + Kusama parachains specification for CLI. + +use crate::cli::CliChain; +use relay_bridge_hub_kusama_client::BridgeHubKusama; +use relay_kusama_client::Kusama; +use relay_substrate_client::SimpleRuntimeVersion; + +impl CliChain for Kusama { + const RUNTIME_VERSION: Option = None; +} + +impl CliChain for BridgeHubKusama { + // TODO: fix me (https://github.com/paritytech/parity-bridges-common/issues/1945) + const RUNTIME_VERSION: Option = + Some(SimpleRuntimeVersion { spec_version: 4242, transaction_version: 42 }); +} diff --git a/relays/bin-substrate/src/chains/millau.rs b/relays/bin-substrate/src/chains/millau.rs index ca9a32a48a5..44416195c6a 100644 --- a/relays/bin-substrate/src/chains/millau.rs +++ b/relays/bin-substrate/src/chains/millau.rs @@ -16,37 +16,18 @@ //! Millau chain specification for CLI. -use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain}; -use bp_rialto_parachain::RIALTO_PARACHAIN_ID; +use crate::cli::{encode_message::CliEncodeMessage, CliChain}; use bp_runtime::EncodedOrDecodedCall; use relay_millau_client::Millau; use relay_substrate_client::SimpleRuntimeVersion; -use xcm::latest::prelude::*; impl CliEncodeMessage for Millau { - fn encode_send_xcm( - message: xcm::VersionedXcm<()>, - bridge_instance_index: u8, + fn encode_execute_xcm( + message: xcm::VersionedXcm, ) -> anyhow::Result> { - let dest = match bridge_instance_index { - bridge::MILLAU_TO_RIALTO_INDEX => - (Parent, X1(GlobalConsensus(millau_runtime::xcm_config::RialtoNetwork::get()))), - bridge::MILLAU_TO_RIALTO_PARACHAIN_INDEX => ( - Parent, - X2( - GlobalConsensus(millau_runtime::xcm_config::RialtoNetwork::get()), - Parachain(RIALTO_PARACHAIN_ID), - ), - ), - _ => anyhow::bail!( - "Unsupported target bridge pallet with instance index: {}", - bridge_instance_index - ), - }; - - Ok(millau_runtime::RuntimeCall::XcmPallet(millau_runtime::XcmCall::send { - dest: Box::new(dest.into()), + Ok(millau_runtime::RuntimeCall::XcmPallet(millau_runtime::XcmCall::execute { message: Box::new(message), + max_weight: Self::estimate_execute_xcm_weight(), }) .into()) } diff --git a/relays/bin-substrate/src/chains/mod.rs b/relays/bin-substrate/src/chains/mod.rs index 7aad5f44e89..b1a91ed1e85 100644 --- a/relays/bin-substrate/src/chains/mod.rs +++ b/relays/bin-substrate/src/chains/mod.rs @@ -16,24 +16,9 @@ //! Chain-specific relayer configuration. -pub mod bridge_hub_rococo_messages_to_bridge_hub_wococo; -pub mod bridge_hub_wococo_messages_to_bridge_hub_rococo; -pub mod millau_headers_to_rialto; -pub mod millau_headers_to_rialto_parachain; -pub mod millau_messages_to_rialto; -pub mod millau_messages_to_rialto_parachain; -pub mod rialto_headers_to_millau; -pub mod rialto_messages_to_millau; -pub mod rialto_parachain_messages_to_millau; -pub mod rialto_parachains_to_millau; -pub mod rococo_headers_to_bridge_hub_wococo; -pub mod rococo_parachains_to_bridge_hub_wococo; -pub mod westend_headers_to_millau; -pub mod westend_parachains_to_millau; -pub mod wococo_headers_to_bridge_hub_rococo; -pub mod wococo_parachains_to_bridge_hub_rococo; - +mod kusama; mod millau; +mod polkadot; mod rialto; mod rialto_parachain; mod rococo; diff --git a/relays/bin-substrate/src/chains/polkadot.rs b/relays/bin-substrate/src/chains/polkadot.rs new file mode 100644 index 00000000000..4fe5a48398b --- /dev/null +++ b/relays/bin-substrate/src/chains/polkadot.rs @@ -0,0 +1,32 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Polkadot + Polkadot parachains specification for CLI. + +use crate::cli::CliChain; +use relay_bridge_hub_polkadot_client::BridgeHubPolkadot; +use relay_polkadot_client::Polkadot; +use relay_substrate_client::SimpleRuntimeVersion; + +impl CliChain for Polkadot { + const RUNTIME_VERSION: Option = None; +} + +impl CliChain for BridgeHubPolkadot { + // TODO: fix me (https://github.com/paritytech/parity-bridges-common/issues/1945) + const RUNTIME_VERSION: Option = + Some(SimpleRuntimeVersion { spec_version: 4242, transaction_version: 42 }); +} diff --git a/relays/bin-substrate/src/chains/rialto.rs b/relays/bin-substrate/src/chains/rialto.rs index e9e2d0b3eaf..34a448ae4cb 100644 --- a/relays/bin-substrate/src/chains/rialto.rs +++ b/relays/bin-substrate/src/chains/rialto.rs @@ -16,29 +16,18 @@ //! Rialto chain specification for CLI. -use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain}; +use crate::cli::{encode_message::CliEncodeMessage, CliChain}; use bp_runtime::EncodedOrDecodedCall; use relay_rialto_client::Rialto; use relay_substrate_client::SimpleRuntimeVersion; -use xcm::latest::prelude::*; impl CliEncodeMessage for Rialto { - fn encode_send_xcm( - message: xcm::VersionedXcm<()>, - bridge_instance_index: u8, + fn encode_execute_xcm( + message: xcm::VersionedXcm, ) -> anyhow::Result> { - let dest = match bridge_instance_index { - bridge::RIALTO_TO_MILLAU_INDEX => - (Parent, X1(GlobalConsensus(rialto_runtime::xcm_config::MillauNetwork::get()))), - _ => anyhow::bail!( - "Unsupported target bridge pallet with instance index: {}", - bridge_instance_index - ), - }; - - Ok(rialto_runtime::RuntimeCall::XcmPallet(rialto_runtime::XcmCall::send { - dest: Box::new(dest.into()), + Ok(rialto_runtime::RuntimeCall::XcmPallet(rialto_runtime::XcmCall::execute { message: Box::new(message), + max_weight: Self::estimate_execute_xcm_weight(), }) .into()) } diff --git a/relays/bin-substrate/src/chains/rialto_parachain.rs b/relays/bin-substrate/src/chains/rialto_parachain.rs index 1a5ea2e784e..8ea2c1ffd43 100644 --- a/relays/bin-substrate/src/chains/rialto_parachain.rs +++ b/relays/bin-substrate/src/chains/rialto_parachain.rs @@ -16,33 +16,21 @@ //! Rialto parachain specification for CLI. -use crate::cli::{bridge, encode_message::CliEncodeMessage, CliChain}; +use crate::cli::{encode_message::CliEncodeMessage, CliChain}; use bp_runtime::EncodedOrDecodedCall; -use bridge_runtime_common::CustomNetworkId; use relay_rialto_parachain_client::RialtoParachain; use relay_substrate_client::SimpleRuntimeVersion; -use xcm::latest::prelude::*; impl CliEncodeMessage for RialtoParachain { - fn encode_send_xcm( - message: xcm::VersionedXcm<()>, - bridge_instance_index: u8, + fn encode_execute_xcm( + message: xcm::VersionedXcm, ) -> anyhow::Result> { type RuntimeCall = relay_rialto_parachain_client::RuntimeCall; type XcmCall = relay_rialto_parachain_client::runtime_types::pallet_xcm::pallet::Call; - let dest = match bridge_instance_index { - bridge::RIALTO_PARACHAIN_TO_MILLAU_INDEX => - (Parent, X1(GlobalConsensus(CustomNetworkId::Millau.as_network_id()))), - _ => anyhow::bail!( - "Unsupported target bridge pallet with instance index: {}", - bridge_instance_index - ), - }; - - let xcm_call = XcmCall::send { - dest: Box::new(unsafe { std::mem::transmute(xcm::VersionedMultiLocation::from(dest)) }), + let xcm_call = XcmCall::execute { message: Box::new(unsafe { std::mem::transmute(message) }), + max_weight: Self::estimate_execute_xcm_weight(), }; Ok(RuntimeCall::PolkadotXcm(xcm_call).into()) diff --git a/relays/bin-substrate/src/cli/bridge.rs b/relays/bin-substrate/src/cli/bridge.rs index 208f67c527b..62c71183a48 100644 --- a/relays/bin-substrate/src/cli/bridge.rs +++ b/relays/bin-substrate/src/cli/bridge.rs @@ -33,27 +33,10 @@ pub enum FullBridge { RialtoParachainToMillau, BridgeHubRococoToBridgeHubWococo, BridgeHubWococoToBridgeHubRococo, + BridgeHubKusamaToBridgeHubPolkadot, + BridgeHubPolkadotToBridgeHubKusama, } -impl FullBridge { - /// Return instance index of the bridge pallet in source runtime. - pub fn bridge_instance_index(&self) -> u8 { - match self { - Self::MillauToRialto => MILLAU_TO_RIALTO_INDEX, - Self::RialtoToMillau => RIALTO_TO_MILLAU_INDEX, - Self::MillauToRialtoParachain => MILLAU_TO_RIALTO_PARACHAIN_INDEX, - Self::RialtoParachainToMillau => RIALTO_PARACHAIN_TO_MILLAU_INDEX, - Self::BridgeHubRococoToBridgeHubWococo | Self::BridgeHubWococoToBridgeHubRococo => - unimplemented!("Relay doesn't support send-message subcommand on bridge hubs"), - } - } -} - -pub const RIALTO_TO_MILLAU_INDEX: u8 = 0; -pub const MILLAU_TO_RIALTO_INDEX: u8 = 0; -pub const MILLAU_TO_RIALTO_PARACHAIN_INDEX: u8 = 1; -pub const RIALTO_PARACHAIN_TO_MILLAU_INDEX: u8 = 0; - /// Minimal bridge representation that can be used from the CLI. /// It connects a source chain to a target chain. pub trait CliBridgeBase: Sized { diff --git a/relays/bin-substrate/src/cli/encode_message.rs b/relays/bin-substrate/src/cli/encode_message.rs index c7ca5d51f79..9abf8b2df6d 100644 --- a/relays/bin-substrate/src/cli/encode_message.rs +++ b/relays/bin-substrate/src/cli/encode_message.rs @@ -17,6 +17,7 @@ use crate::cli::{ExplicitOrMaximal, HexBytes}; use bp_runtime::EncodedOrDecodedCall; use codec::Encode; +use frame_support::weights::Weight; use relay_substrate_client::Chain; use structopt::StructOpt; @@ -42,11 +43,16 @@ pub enum Message { pub type RawMessage = Vec; pub trait CliEncodeMessage: Chain { - /// Encode a send XCM call of the XCM pallet. - fn encode_send_xcm( - message: xcm::VersionedXcm<()>, - bridge_instance_index: u8, + /// Encode an `execute` XCM call of the XCM pallet. + fn encode_execute_xcm( + message: xcm::VersionedXcm, ) -> anyhow::Result>; + + /// Estimate value of `max_weight` argument for the `execute` XCM call of the XCM pallet. + fn estimate_execute_xcm_weight() -> Weight { + // we are only executing XCM on our testnets and 1/100 of max extrinsic weight is ok + Self::max_extrinsic_weight() / 100 + } } /// Encode message payload passed through CLI flags. @@ -125,7 +131,7 @@ mod tests { .unwrap(); assert_eq!(msg.len(), 100); // check that it decodes to valid xcm - let _ = decode_xcm(msg).unwrap(); + let _ = decode_xcm::<()>(msg).unwrap(); } #[test] @@ -140,6 +146,6 @@ mod tests { .unwrap(); assert_eq!(msg.len(), maximal_size as usize); // check that it decodes to valid xcm - let _ = decode_xcm(msg).unwrap(); + let _ = decode_xcm::<()>(msg).unwrap(); } } diff --git a/relays/bin-substrate/src/cli/init_bridge.rs b/relays/bin-substrate/src/cli/init_bridge.rs index d6df1eaeeaa..95cd02f6c6f 100644 --- a/relays/bin-substrate/src/cli/init_bridge.rs +++ b/relays/bin-substrate/src/cli/init_bridge.rs @@ -18,13 +18,17 @@ use async_trait::async_trait; use codec::Encode; use crate::{ - chains::{ - millau_headers_to_rialto::MillauToRialtoCliBridge, - millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, - rialto_headers_to_millau::RialtoToMillauCliBridge, - rococo_headers_to_bridge_hub_wococo::RococoToBridgeHubWococoCliBridge, - westend_headers_to_millau::WestendToMillauCliBridge, - wococo_headers_to_bridge_hub_rococo::WococoToBridgeHubRococoCliBridge, + bridges::{ + rialto_millau::{ + millau_headers_to_rialto::MillauToRialtoCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + }, + rialto_parachain_millau::millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rococo_wococo::{ + rococo_headers_to_bridge_hub_wococo::RococoToBridgeHubWococoCliBridge, + wococo_headers_to_bridge_hub_rococo::WococoToBridgeHubRococoCliBridge, + }, + westend_millau::westend_headers_to_millau::WestendToMillauCliBridge, }, cli::{bridge::CliBridgeBase, chain_schema::*}, }; diff --git a/relays/bin-substrate/src/cli/register_parachain.rs b/relays/bin-substrate/src/cli/register_parachain.rs index 1bc9f941ecf..320277590bc 100644 --- a/relays/bin-substrate/src/cli/register_parachain.rs +++ b/relays/bin-substrate/src/cli/register_parachain.rs @@ -103,8 +103,8 @@ impl RegisterParachain { let para_id: ParaId = relay_client .storage_value(StorageKey(para_id_key.to_vec()), None) .await? - .unwrap_or(polkadot_primitives::v2::LOWEST_PUBLIC_ID) - .max(polkadot_primitives::v2::LOWEST_PUBLIC_ID); + .unwrap_or(polkadot_primitives::v4::LOWEST_PUBLIC_ID) + .max(polkadot_primitives::v4::LOWEST_PUBLIC_ID); log::info!(target: "bridge", "Going to reserve parachain id: {:?}", para_id); // step 1: reserve a parachain id diff --git a/relays/bin-substrate/src/cli/relay_headers.rs b/relays/bin-substrate/src/cli/relay_headers.rs index 239ab8d62fd..fbe360d3cd7 100644 --- a/relays/bin-substrate/src/cli/relay_headers.rs +++ b/relays/bin-substrate/src/cli/relay_headers.rs @@ -20,13 +20,21 @@ use sp_core::Pair; use structopt::StructOpt; use strum::{EnumString, EnumVariantNames, VariantNames}; -use crate::chains::{ - millau_headers_to_rialto::MillauToRialtoCliBridge, - millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, - rialto_headers_to_millau::RialtoToMillauCliBridge, - rococo_headers_to_bridge_hub_wococo::RococoToBridgeHubWococoCliBridge, - westend_headers_to_millau::WestendToMillauCliBridge, - wococo_headers_to_bridge_hub_rococo::WococoToBridgeHubRococoCliBridge, +use crate::bridges::{ + kusama_polkadot::{ + kusama_headers_to_bridge_hub_polkadot::KusamaToBridgeHubPolkadotCliBridge, + polkadot_headers_to_bridge_hub_kusama::PolkadotToBridgeHubKusamaCliBridge, + }, + rialto_millau::{ + millau_headers_to_rialto::MillauToRialtoCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + }, + rialto_parachain_millau::millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rococo_wococo::{ + rococo_headers_to_bridge_hub_wococo::RococoToBridgeHubWococoCliBridge, + wococo_headers_to_bridge_hub_rococo::WococoToBridgeHubRococoCliBridge, + }, + westend_millau::westend_headers_to_millau::WestendToMillauCliBridge, }; use relay_utils::metrics::{GlobalMetrics, StandaloneMetric}; use substrate_relay_helper::finality::SubstrateFinalitySyncPipeline; @@ -63,6 +71,8 @@ pub enum RelayHeadersBridge { MillauToRialtoParachain, RococoToBridgeHubWococo, WococoToBridgeHubRococo, + KusamaToBridgeHubPolkadot, + PolkadotToBridgeHubKusama, } #[async_trait] @@ -109,6 +119,8 @@ impl HeadersRelayer for WestendToMillauCliBridge {} impl HeadersRelayer for MillauToRialtoParachainCliBridge {} impl HeadersRelayer for RococoToBridgeHubWococoCliBridge {} impl HeadersRelayer for WococoToBridgeHubRococoCliBridge {} +impl HeadersRelayer for KusamaToBridgeHubPolkadotCliBridge {} +impl HeadersRelayer for PolkadotToBridgeHubKusamaCliBridge {} impl RelayHeaders { /// Run the command. @@ -123,6 +135,10 @@ impl RelayHeaders { RococoToBridgeHubWococoCliBridge::relay_headers(self), RelayHeadersBridge::WococoToBridgeHubRococo => WococoToBridgeHubRococoCliBridge::relay_headers(self), + RelayHeadersBridge::KusamaToBridgeHubPolkadot => + KusamaToBridgeHubPolkadotCliBridge::relay_headers(self), + RelayHeadersBridge::PolkadotToBridgeHubKusama => + PolkadotToBridgeHubKusamaCliBridge::relay_headers(self), } .await } diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs index 193632c28b4..5384c8ff9e9 100644 --- a/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs @@ -39,13 +39,19 @@ use relay_to_parachain::*; use relay_to_relay::*; use crate::{ - chains::{ - millau_headers_to_rialto::MillauToRialtoCliBridge, - millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, - rialto_headers_to_millau::RialtoToMillauCliBridge, - rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, - rococo_parachains_to_bridge_hub_wococo::BridgeHubRococoToBridgeHubWococoCliBridge, - wococo_parachains_to_bridge_hub_rococo::BridgeHubWococoToBridgeHubRococoCliBridge, + bridges::{ + rialto_millau::{ + millau_headers_to_rialto::MillauToRialtoCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + }, + rialto_parachain_millau::{ + millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, + }, + rococo_wococo::{ + rococo_parachains_to_bridge_hub_wococo::BridgeHubRococoToBridgeHubWococoCliBridge, + wococo_parachains_to_bridge_hub_rococo::BridgeHubWococoToBridgeHubRococoCliBridge, + }, }, cli::{ bridge::{ diff --git a/relays/bin-substrate/src/cli/relay_messages.rs b/relays/bin-substrate/src/cli/relay_messages.rs index e0250ef1e47..1624524e4a5 100644 --- a/relays/bin-substrate/src/cli/relay_messages.rs +++ b/relays/bin-substrate/src/cli/relay_messages.rs @@ -19,13 +19,23 @@ use sp_core::Pair; use structopt::StructOpt; use strum::VariantNames; -use crate::chains::{ - bridge_hub_rococo_messages_to_bridge_hub_wococo::BridgeHubRococoToBridgeHubWococoMessagesCliBridge, - bridge_hub_wococo_messages_to_bridge_hub_rococo::BridgeHubWococoToBridgeHubRococoMessagesCliBridge, - millau_headers_to_rialto::MillauToRialtoCliBridge, - millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, - rialto_headers_to_millau::RialtoToMillauCliBridge, - rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, +use crate::bridges::{ + kusama_polkadot::{ + bridge_hub_kusama_messages_to_bridge_hub_polkadot::BridgeHubKusamaToBridgeHubPolkadotMessagesCliBridge, + bridge_hub_polkadot_messages_to_bridge_hub_kusama::BridgeHubPolkadotToBridgeHubKusamaMessagesCliBridge, + }, + rialto_millau::{ + millau_headers_to_rialto::MillauToRialtoCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + }, + rialto_parachain_millau::{ + millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, + }, + rococo_wococo::{ + bridge_hub_rococo_messages_to_bridge_hub_wococo::BridgeHubRococoToBridgeHubWococoMessagesCliBridge, + bridge_hub_wococo_messages_to_bridge_hub_rococo::BridgeHubWococoToBridgeHubRococoMessagesCliBridge, + }, }; use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, BalanceOf, ChainWithTransactions}; use substrate_relay_helper::{messages_lane::MessagesRelayParams, TransactionParams}; @@ -96,6 +106,8 @@ impl MessagesRelayer for MillauToRialtoParachainCliBridge {} impl MessagesRelayer for RialtoParachainToMillauCliBridge {} impl MessagesRelayer for BridgeHubRococoToBridgeHubWococoMessagesCliBridge {} impl MessagesRelayer for BridgeHubWococoToBridgeHubRococoMessagesCliBridge {} +impl MessagesRelayer for BridgeHubKusamaToBridgeHubPolkadotMessagesCliBridge {} +impl MessagesRelayer for BridgeHubPolkadotToBridgeHubKusamaMessagesCliBridge {} impl RelayMessages { /// Run the command. @@ -111,6 +123,10 @@ impl RelayMessages { BridgeHubRococoToBridgeHubWococoMessagesCliBridge::relay_messages(self), FullBridge::BridgeHubWococoToBridgeHubRococo => BridgeHubWococoToBridgeHubRococoMessagesCliBridge::relay_messages(self), + FullBridge::BridgeHubKusamaToBridgeHubPolkadot => + BridgeHubKusamaToBridgeHubPolkadotMessagesCliBridge::relay_messages(self), + FullBridge::BridgeHubPolkadotToBridgeHubKusama => + BridgeHubPolkadotToBridgeHubKusamaMessagesCliBridge::relay_messages(self), } .await } diff --git a/relays/bin-substrate/src/cli/relay_parachains.rs b/relays/bin-substrate/src/cli/relay_parachains.rs index 4e59dc14733..8c2aa1cc8c1 100644 --- a/relays/bin-substrate/src/cli/relay_parachains.rs +++ b/relays/bin-substrate/src/cli/relay_parachains.rs @@ -14,11 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use crate::chains::{ - rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, - rococo_parachains_to_bridge_hub_wococo::BridgeHubRococoToBridgeHubWococoCliBridge, - westend_parachains_to_millau::WestmintToMillauCliBridge, - wococo_parachains_to_bridge_hub_rococo::BridgeHubWococoToBridgeHubRococoCliBridge, +use crate::bridges::{ + kusama_polkadot::{ + kusama_parachains_to_bridge_hub_polkadot::BridgeHubKusamaToBridgeHubPolkadotCliBridge, + polkadot_parachains_to_bridge_hub_kusama::BridgeHubPolkadotToBridgeHubKusamaCliBridge, + }, + rialto_parachain_millau::rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, + rococo_wococo::{ + rococo_parachains_to_bridge_hub_wococo::BridgeHubRococoToBridgeHubWococoCliBridge, + wococo_parachains_to_bridge_hub_rococo::BridgeHubWococoToBridgeHubRococoCliBridge, + }, + westend_millau::westend_parachains_to_millau::WestmintToMillauCliBridge, }; use async_std::sync::Mutex; use async_trait::async_trait; @@ -60,10 +66,11 @@ pub struct RelayParachains { #[strum(serialize_all = "kebab_case")] pub enum RelayParachainsBridge { RialtoToMillau, - // TODO:check-parameter - rename to WestmintToMillau? WestendToMillau, - BridgeHubRococoToBridgeHubWococo, - BridgeHubWococoToBridgeHubRococo, + RococoToBridgeHubWococo, + WococoToBridgeHubRococo, + KusamaToBridgeHubPolkadot, + PolkadotToBridgeHubKusama, } #[async_trait] @@ -75,7 +82,7 @@ where TargetClient>, ::Source: Parachain, { - async fn relay_headers(data: RelayParachains) -> anyhow::Result<()> { + async fn relay_parachains(data: RelayParachains) -> anyhow::Result<()> { let source_client = data.source.into_client::().await?; let source_client = ParachainsSource::::new( source_client, @@ -108,25 +115,28 @@ where } impl ParachainsRelayer for RialtoParachainToMillauCliBridge {} - impl ParachainsRelayer for WestmintToMillauCliBridge {} - impl ParachainsRelayer for BridgeHubRococoToBridgeHubWococoCliBridge {} - impl ParachainsRelayer for BridgeHubWococoToBridgeHubRococoCliBridge {} +impl ParachainsRelayer for BridgeHubKusamaToBridgeHubPolkadotCliBridge {} +impl ParachainsRelayer for BridgeHubPolkadotToBridgeHubKusamaCliBridge {} impl RelayParachains { /// Run the command. pub async fn run(self) -> anyhow::Result<()> { match self.bridge { RelayParachainsBridge::RialtoToMillau => - RialtoParachainToMillauCliBridge::relay_headers(self), + RialtoParachainToMillauCliBridge::relay_parachains(self), RelayParachainsBridge::WestendToMillau => - WestmintToMillauCliBridge::relay_headers(self), - RelayParachainsBridge::BridgeHubRococoToBridgeHubWococo => - BridgeHubRococoToBridgeHubWococoCliBridge::relay_headers(self), - RelayParachainsBridge::BridgeHubWococoToBridgeHubRococo => - BridgeHubWococoToBridgeHubRococoCliBridge::relay_headers(self), + WestmintToMillauCliBridge::relay_parachains(self), + RelayParachainsBridge::RococoToBridgeHubWococo => + BridgeHubRococoToBridgeHubWococoCliBridge::relay_parachains(self), + RelayParachainsBridge::WococoToBridgeHubRococo => + BridgeHubWococoToBridgeHubRococoCliBridge::relay_parachains(self), + RelayParachainsBridge::KusamaToBridgeHubPolkadot => + BridgeHubKusamaToBridgeHubPolkadotCliBridge::relay_parachains(self), + RelayParachainsBridge::PolkadotToBridgeHubKusama => + BridgeHubPolkadotToBridgeHubKusamaCliBridge::relay_parachains(self), } .await } diff --git a/relays/bin-substrate/src/cli/send_message.rs b/relays/bin-substrate/src/cli/send_message.rs index 9f14028c4ae..8f76e501f69 100644 --- a/relays/bin-substrate/src/cli/send_message.rs +++ b/relays/bin-substrate/src/cli/send_message.rs @@ -15,11 +15,15 @@ // along with Parity Bridges Common. If not, see . use crate::{ - chains::{ - millau_headers_to_rialto::MillauToRialtoCliBridge, - millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, - rialto_headers_to_millau::RialtoToMillauCliBridge, - rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, + bridges::{ + rialto_millau::{ + millau_headers_to_rialto::MillauToRialtoCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + }, + rialto_parachain_millau::{ + millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, + }, }, cli::{ bridge::{FullBridge, MessagesCliBridge}, @@ -71,10 +75,7 @@ where let source_sign = data.source_sign.to_keypair::()?; let payload_len = payload.encoded_size(); - let send_message_call = Self::Source::encode_send_xcm( - decode_xcm(payload)?, - data.bridge.bridge_instance_index(), - )?; + let send_message_call = Self::Source::encode_execute_xcm(decode_xcm(payload)?)?; source_client .submit_signed_extrinsic(&source_sign, move |_, transaction_nonce| { @@ -114,13 +115,19 @@ impl SendMessage { FullBridge::BridgeHubWococoToBridgeHubRococo => unimplemented!( "Sending message from BridgeHubWococo to BridgeHubRococo is not supported" ), + FullBridge::BridgeHubKusamaToBridgeHubPolkadot => unimplemented!( + "Sending message from BridgeHubKusama to BridgeHubPolkadot is not supported" + ), + FullBridge::BridgeHubPolkadotToBridgeHubKusama => unimplemented!( + "Sending message from BridgeHubPolkadot to BridgeHubKusama is not supported" + ), } .await } } /// Decode SCALE encoded raw XCM message. -pub(crate) fn decode_xcm(message: RawMessage) -> anyhow::Result> { +pub(crate) fn decode_xcm(message: RawMessage) -> anyhow::Result> { Decode::decode(&mut &message[..]) .map_err(|e| anyhow::format_err!("Failed to decode XCM program: {:?}", e)) } diff --git a/relays/bin-substrate/src/main.rs b/relays/bin-substrate/src/main.rs index 8bd89a41936..33a423b0766 100644 --- a/relays/bin-substrate/src/main.rs +++ b/relays/bin-substrate/src/main.rs @@ -18,6 +18,7 @@ #![warn(missing_docs)] +mod bridges; mod chains; mod cli; diff --git a/relays/client-bridge-hub-kusama/Cargo.toml b/relays/client-bridge-hub-kusama/Cargo.toml new file mode 100644 index 00000000000..3485323ca6c --- /dev/null +++ b/relays/client-bridge-hub-kusama/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "relay-bridge-hub-kusama-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +relay-substrate-client = { path = "../client-substrate" } + +# Bridge dependencies + +bp-bridge-hub-kusama = { path = "../../primitives/chain-bridge-hub-kusama" } +bp-bridge-hub-polkadot = { path = "../../primitives/chain-bridge-hub-polkadot" } +bp-header-chain = { path = "../../primitives/header-chain" } +bp-messages = { path = "../../primitives/messages" } +bp-parachains = { path = "../../primitives/parachains" } +bp-runtime = { path = "../../primitives/runtime" } +bp-polkadot = { path = "../../primitives/chain-polkadot" } + +bridge-runtime-common = { path = "../../bin/runtime-common" } + +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-bridge-hub-kusama/src/lib.rs b/relays/client-bridge-hub-kusama/src/lib.rs new file mode 100644 index 00000000000..6549a32ca79 --- /dev/null +++ b/relays/client-bridge-hub-kusama/src/lib.rs @@ -0,0 +1,162 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the BridgeHub-Kusama-Substrate parachain. + +use bp_bridge_hub_kusama::{BridgeHubSignedExtension, AVERAGE_BLOCK_INTERVAL}; +use bp_messages::MessageNonce; +use bp_runtime::ChainId; +use codec::Encode; +use relay_substrate_client::{ + Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, ChainWithUtilityPallet, + Error as SubstrateError, MockedRuntimeUtilityPallet, SignParam, UnderlyingChainProvider, + UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; +use std::time::Duration; + +/// Re-export runtime wrapper +pub mod runtime_wrapper; +pub use runtime_wrapper as runtime; + +/// Kusama chain definition +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct BridgeHubKusama; + +impl UnderlyingChainProvider for BridgeHubKusama { + type Chain = bp_bridge_hub_kusama::BridgeHubKusama; +} + +impl Chain for BridgeHubKusama { + const ID: ChainId = bp_runtime::BRIDGE_HUB_KUSAMA_CHAIN_ID; + const NAME: &'static str = "BridgeHubKusama"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_bridge_hub_kusama::BEST_FINALIZED_BRIDGE_HUB_KUSAMA_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = AVERAGE_BLOCK_INTERVAL; + + type SignedBlock = bp_bridge_hub_kusama::SignedBlock; + type Call = runtime::Call; +} + +impl ChainWithBalances for BridgeHubKusama { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + bp_bridge_hub_kusama::AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +impl ChainWithUtilityPallet for BridgeHubKusama { + type UtilityPallet = MockedRuntimeUtilityPallet; +} + +impl ChainWithTransactions for BridgeHubKusama { + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = runtime::UncheckedExtrinsic; + + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result { + let raw_payload = SignedPayload::new( + unsigned.call, + runtime::SignedExtension::from_params( + param.spec_version, + param.transaction_version, + unsigned.era, + param.genesis_hash, + unsigned.nonce, + unsigned.tip, + ), + )?; + + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(runtime::UncheckedExtrinsic::new_signed( + call, + signer.into_account().into(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| { + *address == bp_bridge_hub_kusama::Address::Id(signer.public().into()) + }) + .unwrap_or(false) + } + + fn parse_transaction(tx: Self::SignedTransaction) -> Option> { + let extra = &tx.signature.as_ref()?.2; + Some(UnsignedTransaction::new(tx.function, extra.nonce()).tip(extra.tip())) + } +} + +impl ChainWithMessages for BridgeHubKusama { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_bridge_hub_kusama::WITH_BRIDGE_HUB_KUSAMA_MESSAGES_PALLET_NAME; + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = + Some(bp_bridge_hub_kusama::WITH_BRIDGE_HUB_KUSAMA_RELAYERS_PALLET_NAME); + + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_kusama::TO_BRIDGE_HUB_KUSAMA_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_kusama::FROM_BRIDGE_HUB_KUSAMA_MESSAGE_DETAILS_METHOD; + + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_kusama::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_kusama::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; +} + +#[cfg(test)] +mod tests { + use super::*; + use relay_substrate_client::TransactionEra; + + #[test] + fn parse_transaction_works() { + let unsigned = UnsignedTransaction { + call: runtime::Call::System(relay_substrate_client::calls::SystemCall::remark( + b"Hello world!".to_vec(), + )) + .into(), + nonce: 777, + tip: 888, + era: TransactionEra::immortal(), + }; + let signed_transaction = BridgeHubKusama::sign_transaction( + SignParam { + spec_version: 42, + transaction_version: 50000, + genesis_hash: [42u8; 32].into(), + signer: sp_core::sr25519::Pair::from_seed_slice(&[1u8; 32]).unwrap(), + }, + unsigned.clone(), + ) + .unwrap(); + let parsed_transaction = BridgeHubKusama::parse_transaction(signed_transaction).unwrap(); + assert_eq!(parsed_transaction, unsigned); + } +} diff --git a/relays/client-bridge-hub-kusama/src/runtime_wrapper.rs b/relays/client-bridge-hub-kusama/src/runtime_wrapper.rs new file mode 100644 index 00000000000..18bdafc0f04 --- /dev/null +++ b/relays/client-bridge-hub-kusama/src/runtime_wrapper.rs @@ -0,0 +1,74 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types that are specific to the BridgeHubKusama runtime. +// TODO: regenerate me using `runtime-codegen` tool? (https://github.com/paritytech/parity-bridges-common/issues/1945) + +use codec::{Decode, Encode}; +use scale_info::TypeInfo; + +pub use bp_bridge_hub_kusama::SignedExtension; +pub use bp_header_chain::BridgeGrandpaCallOf; +pub use bp_parachains::BridgeParachainCall; +pub use bridge_runtime_common::messages::BridgeMessagesCallOf; +pub use relay_substrate_client::calls::{SystemCall, UtilityCall}; + +/// Unchecked BridgeHubKusama extrinsic. +pub type UncheckedExtrinsic = bp_bridge_hub_kusama::UncheckedExtrinsic; + +// The indirect pallet call used to sync `Polkadot` GRANDPA finality to `BHKusama`. +pub type BridgePolkadotGrandpaCall = BridgeGrandpaCallOf; +// The indirect pallet call used to sync `BridgeHubPolkadot` messages to `BHKusama`. +pub type BridgePolkadotMessagesCall = + BridgeMessagesCallOf; + +/// `BridgeHubKusama` Runtime `Call` enum. +/// +/// The enum represents a subset of possible `Call`s we can send to `BridgeHubKusama` chain. +/// Ideally this code would be auto-generated from metadata, because we want to +/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. +/// +/// All entries here (like pretty much in the entire file) must be kept in sync with +/// `BridgeHubKusama` `construct_runtime`, so that we maintain SCALE-compatibility. +#[allow(clippy::large_enum_variant)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum Call { + #[cfg(test)] + #[codec(index = 0)] + System(SystemCall), + /// Utility pallet. + #[codec(index = 40)] + Utility(UtilityCall), + + /// Polkadot bridge pallet. + // TODO: the index is wrong (https://github.com/paritytech/parity-bridges-common/issues/1945) + #[codec(index = 41)] + BridgePolkadotGrandpa(BridgePolkadotGrandpaCall), + /// Polkadot parachain bridge pallet. + // TODO: the index is wrong (https://github.com/paritytech/parity-bridges-common/issues/1945) + #[codec(index = 42)] + BridgePolkadotParachain(BridgeParachainCall), + /// Polkadot messages bridge pallet. + // TODO: the index is wrong (https://github.com/paritytech/parity-bridges-common/issues/1945) + #[codec(index = 46)] + BridgePolkadotMessages(BridgePolkadotMessagesCall), +} + +impl From> for Call { + fn from(call: UtilityCall) -> Call { + Call::Utility(call) + } +} diff --git a/relays/client-bridge-hub-polkadot/Cargo.toml b/relays/client-bridge-hub-polkadot/Cargo.toml new file mode 100644 index 00000000000..dee3147eb35 --- /dev/null +++ b/relays/client-bridge-hub-polkadot/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "relay-bridge-hub-polkadot-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +relay-substrate-client = { path = "../client-substrate" } + +# Bridge dependencies + +bp-bridge-hub-kusama = { path = "../../primitives/chain-bridge-hub-kusama" } +bp-bridge-hub-polkadot = { path = "../../primitives/chain-bridge-hub-polkadot" } +bp-header-chain = { path = "../../primitives/header-chain" } +bp-messages = { path = "../../primitives/messages" } +bp-parachains = { path = "../../primitives/parachains" } +bp-kusama = { path = "../../primitives/chain-kusama" } +bp-runtime = { path = "../../primitives/runtime" } + +bridge-runtime-common = { path = "../../bin/runtime-common" } + +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[dev-dependencies] +bp-polkadot-core = { path = "../../primitives/polkadot-core" } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-bridge-hub-polkadot/src/lib.rs b/relays/client-bridge-hub-polkadot/src/lib.rs new file mode 100644 index 00000000000..6428b055dd5 --- /dev/null +++ b/relays/client-bridge-hub-polkadot/src/lib.rs @@ -0,0 +1,160 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the BridgeHub-Polkadot-Substrate parachain. + +use bp_bridge_hub_polkadot::{BridgeHubSignedExtension, AVERAGE_BLOCK_INTERVAL}; +use bp_messages::MessageNonce; +use bp_runtime::ChainId; +use codec::Encode; +use relay_substrate_client::{ + Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, ChainWithUtilityPallet, + Error as SubstrateError, MockedRuntimeUtilityPallet, SignParam, UnderlyingChainProvider, + UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; +use std::time::Duration; + +/// Re-export runtime wrapper +pub mod runtime_wrapper; +pub use runtime_wrapper as runtime; + +/// Polkadot chain definition +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct BridgeHubPolkadot; + +impl UnderlyingChainProvider for BridgeHubPolkadot { + type Chain = bp_bridge_hub_polkadot::BridgeHubPolkadot; +} + +impl Chain for BridgeHubPolkadot { + const ID: ChainId = bp_runtime::BRIDGE_HUB_POLKADOT_CHAIN_ID; + const NAME: &'static str = "BridgeHubPolkadot"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_bridge_hub_polkadot::BEST_FINALIZED_BRIDGE_HUB_POLKADOT_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = AVERAGE_BLOCK_INTERVAL; + + type SignedBlock = bp_bridge_hub_polkadot::SignedBlock; + type Call = runtime::Call; +} + +impl ChainWithBalances for BridgeHubPolkadot { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + bp_bridge_hub_polkadot::AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +impl ChainWithUtilityPallet for BridgeHubPolkadot { + type UtilityPallet = MockedRuntimeUtilityPallet; +} + +impl ChainWithTransactions for BridgeHubPolkadot { + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = runtime::UncheckedExtrinsic; + + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result { + let raw_payload = SignedPayload::new( + unsigned.call, + runtime::SignedExtension::from_params( + param.spec_version, + param.transaction_version, + unsigned.era, + param.genesis_hash, + unsigned.nonce, + unsigned.tip, + ), + )?; + + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(runtime::UncheckedExtrinsic::new_signed( + call, + signer.into_account().into(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| { + *address == bp_bridge_hub_polkadot::Address::Id(signer.public().into()) + }) + .unwrap_or(false) + } + + fn parse_transaction(tx: Self::SignedTransaction) -> Option> { + let extra = &tx.signature.as_ref()?.2; + Some(UnsignedTransaction::new(tx.function, extra.nonce()).tip(extra.tip())) + } +} + +impl ChainWithMessages for BridgeHubPolkadot { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_bridge_hub_polkadot::WITH_BRIDGE_HUB_POLKADOT_MESSAGES_PALLET_NAME; + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = + Some(bp_bridge_hub_polkadot::WITH_BRIDGE_HUB_POLKADOT_RELAYERS_PALLET_NAME); + + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_polkadot::TO_BRIDGE_HUB_POLKADOT_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_polkadot::FROM_BRIDGE_HUB_POLKADOT_MESSAGE_DETAILS_METHOD; + + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_polkadot::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_polkadot::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; +} + +#[cfg(test)] +mod tests { + use super::*; + use relay_substrate_client::TransactionEra; + + #[test] + fn parse_transaction_works() { + let unsigned = UnsignedTransaction { + call: runtime::Call::System(runtime::SystemCall::remark(b"Hello world!".to_vec())) + .into(), + nonce: 777, + tip: 888, + era: TransactionEra::immortal(), + }; + let signed_transaction = BridgeHubPolkadot::sign_transaction( + SignParam { + spec_version: 42, + transaction_version: 50000, + genesis_hash: [42u8; 32].into(), + signer: sp_core::sr25519::Pair::from_seed_slice(&[1u8; 32]).unwrap(), + }, + unsigned.clone(), + ) + .unwrap(); + let parsed_transaction = BridgeHubPolkadot::parse_transaction(signed_transaction).unwrap(); + assert_eq!(parsed_transaction, unsigned); + } +} diff --git a/relays/client-bridge-hub-polkadot/src/runtime_wrapper.rs b/relays/client-bridge-hub-polkadot/src/runtime_wrapper.rs new file mode 100644 index 00000000000..5afac61dbb3 --- /dev/null +++ b/relays/client-bridge-hub-polkadot/src/runtime_wrapper.rs @@ -0,0 +1,119 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types that are specific to the BridgeHubPolkadot runtime. +// TODO: regenerate me using `runtime-codegen` tool? (https://github.com/paritytech/parity-bridges-common/issues/1945) + +use codec::{Decode, Encode}; +use scale_info::TypeInfo; + +pub use bp_bridge_hub_polkadot::SignedExtension; +pub use bp_header_chain::BridgeGrandpaCallOf; +pub use bp_parachains::BridgeParachainCall; +pub use bridge_runtime_common::messages::BridgeMessagesCallOf; +pub use relay_substrate_client::calls::{SystemCall, UtilityCall}; + +/// Unchecked BridgeHubPolkadot extrinsic. +pub type UncheckedExtrinsic = bp_bridge_hub_polkadot::UncheckedExtrinsic; + +// The indirect pallet call used to sync `Kusama` GRANDPA finality to `BHPolkadot`. +pub type BridgeKusamaGrandpaCall = BridgeGrandpaCallOf; +// The indirect pallet call used to sync `BridgeHubKusama` messages to `BridgeHubPolkadot`. +pub type BridgeKusamaMessagesCall = BridgeMessagesCallOf; + +/// `BridgeHubPolkadot` Runtime `Call` enum. +/// +/// The enum represents a subset of possible `Call`s we can send to `BridgeHubPolkadot` chain. +/// Ideally this code would be auto-generated from metadata, because we want to +/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. +/// +/// All entries here (like pretty much in the entire file) must be kept in sync with +/// `BridgeHubPolkadot` `construct_runtime`, so that we maintain SCALE-compatibility. +#[allow(clippy::large_enum_variant)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum Call { + #[cfg(test)] + #[codec(index = 0)] + System(SystemCall), + /// Utility pallet. + #[codec(index = 40)] + Utility(UtilityCall), + + /// Kusama bridge pallet. + // TODO: the index is wrong (https://github.com/paritytech/parity-bridges-common/issues/1945) + #[codec(index = 43)] + BridgeKusamaGrandpa(BridgeKusamaGrandpaCall), + /// Kusama parachain bridge pallet. + // TODO: the index is wrong (https://github.com/paritytech/parity-bridges-common/issues/1945) + #[codec(index = 44)] + BridgeKusamaParachain(BridgeParachainCall), + /// Kusama messages bridge pallet. + // TODO: the index is wrong (https://github.com/paritytech/parity-bridges-common/issues/1945) + #[codec(index = 45)] + BridgeKusamaMessages(BridgeKusamaMessagesCall), +} + +impl From> for Call { + fn from(call: UtilityCall) -> Call { + Call::Utility(call) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_runtime::BasicOperatingMode; + use sp_consensus_grandpa::AuthorityList; + use sp_core::hexdisplay::HexDisplay; + use sp_runtime::traits::Header; + use std::str::FromStr; + + pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; + pub type RelayBlockHasher = bp_polkadot_core::Hasher; + pub type RelayBlockHeader = sp_runtime::generic::Header; + + #[test] + fn encode_decode_calls() { + let header = RelayBlockHeader::new( + 75, + bp_polkadot_core::Hash::from_str( + "0xd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d", + ) + .expect("invalid value"), + bp_polkadot_core::Hash::from_str( + "0x92b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141b", + ) + .expect("invalid value"), + bp_polkadot_core::Hash::from_str( + "0xae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d", + ) + .expect("invalid value"), + Default::default(), + ); + let init_data = bp_header_chain::InitializationData { + header: Box::new(header), + authority_list: AuthorityList::default(), + set_id: 6, + operating_mode: BasicOperatingMode::Normal, + }; + let call = BridgeKusamaGrandpaCall::initialize { init_data }; + let tx = Call::BridgeKusamaGrandpa(call); + + // encode call as hex string + let hex_encoded_call = format!("0x{:?}", HexDisplay::from(&Encode::encode(&tx))); + assert_eq!(hex_encoded_call, "0x2b01ae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d2d0192b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141bd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d0000060000000000000000"); + } +} diff --git a/relays/client-bridge-hub-rococo/src/lib.rs b/relays/client-bridge-hub-rococo/src/lib.rs index 02c389ffa29..bc2ec3aca41 100644 --- a/relays/client-bridge-hub-rococo/src/lib.rs +++ b/relays/client-bridge-hub-rococo/src/lib.rs @@ -16,7 +16,7 @@ //! Types used to connect to the BridgeHub-Rococo-Substrate parachain. -use bp_bridge_hub_rococo::AVERAGE_BLOCK_INTERVAL; +use bp_bridge_hub_rococo::{BridgeHubSignedExtension, AVERAGE_BLOCK_INTERVAL}; use bp_messages::MessageNonce; use bp_runtime::ChainId; use codec::Encode; @@ -44,7 +44,6 @@ impl UnderlyingChainProvider for BridgeHubRococo { impl Chain for BridgeHubRococo { const ID: ChainId = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; const NAME: &'static str = "BridgeHubRococo"; - const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = bp_bridge_hub_rococo::BEST_FINALIZED_BRIDGE_HUB_ROCOCO_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = AVERAGE_BLOCK_INTERVAL; @@ -73,7 +72,7 @@ impl ChainWithTransactions for BridgeHubRococo { ) -> Result { let raw_payload = SignedPayload::new( unsigned.call, - runtime::rewarding_bridge_signed_extension::from_params( + runtime::SignedExtension::from_params( param.spec_version, param.transaction_version, unsigned.era, @@ -110,20 +109,15 @@ impl ChainWithTransactions for BridgeHubRococo { fn parse_transaction(tx: Self::SignedTransaction) -> Option> { let extra = &tx.signature.as_ref()?.2; - Some( - UnsignedTransaction::new( - tx.function, - runtime::rewarding_bridge_signed_extension::nonce(extra), - ) - .tip(runtime::rewarding_bridge_signed_extension::tip(extra)), - ) + Some(UnsignedTransaction::new(tx.function, extra.nonce()).tip(extra.tip())) } } impl ChainWithMessages for BridgeHubRococo { const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME; - const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = None; + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = + Some(bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_RELAYERS_PALLET_NAME); const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = bp_bridge_hub_rococo::TO_BRIDGE_HUB_ROCOCO_MESSAGE_DETAILS_METHOD; diff --git a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs index 711306b0127..26e0bd9da5e 100644 --- a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs +++ b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs @@ -19,17 +19,14 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; -pub use bp_bridge_hub_rococo::rewarding_bridge_signed_extension; +pub use bp_bridge_hub_rococo::SignedExtension; pub use bp_header_chain::BridgeGrandpaCallOf; pub use bp_parachains::BridgeParachainCall; pub use bridge_runtime_common::messages::BridgeMessagesCallOf; pub use relay_substrate_client::calls::{SystemCall, UtilityCall}; /// Unchecked BridgeHubRococo extrinsic. -pub type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic< - Call, - rewarding_bridge_signed_extension::RewardingBridgeSignedExtension, ->; +pub type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic; // The indirect pallet call used to sync `Wococo` GRANDPA finality to `BHRococo`. pub type BridgeWococoGrandpaCall = BridgeGrandpaCallOf; diff --git a/relays/client-bridge-hub-wococo/src/lib.rs b/relays/client-bridge-hub-wococo/src/lib.rs index 359692b0e3f..c9acc47dd95 100644 --- a/relays/client-bridge-hub-wococo/src/lib.rs +++ b/relays/client-bridge-hub-wococo/src/lib.rs @@ -16,7 +16,7 @@ //! Types used to connect to the BridgeHub-Wococo-Substrate parachain. -use bp_bridge_hub_wococo::AVERAGE_BLOCK_INTERVAL; +use bp_bridge_hub_wococo::{BridgeHubSignedExtension, AVERAGE_BLOCK_INTERVAL}; use bp_messages::MessageNonce; use bp_runtime::ChainId; use codec::Encode; @@ -44,7 +44,6 @@ impl UnderlyingChainProvider for BridgeHubWococo { impl Chain for BridgeHubWococo { const ID: ChainId = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; const NAME: &'static str = "BridgeHubWococo"; - const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = bp_bridge_hub_wococo::BEST_FINALIZED_BRIDGE_HUB_WOCOCO_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = AVERAGE_BLOCK_INTERVAL; @@ -73,7 +72,7 @@ impl ChainWithTransactions for BridgeHubWococo { ) -> Result { let raw_payload = SignedPayload::new( unsigned.call, - runtime::rewarding_bridge_signed_extension::from_params( + runtime::SignedExtension::from_params( param.spec_version, param.transaction_version, unsigned.era, @@ -110,20 +109,15 @@ impl ChainWithTransactions for BridgeHubWococo { fn parse_transaction(tx: Self::SignedTransaction) -> Option> { let extra = &tx.signature.as_ref()?.2; - Some( - UnsignedTransaction::new( - tx.function, - runtime::rewarding_bridge_signed_extension::nonce(extra), - ) - .tip(runtime::rewarding_bridge_signed_extension::tip(extra)), - ) + Some(UnsignedTransaction::new(tx.function, extra.nonce()).tip(extra.tip())) } } impl ChainWithMessages for BridgeHubWococo { const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = bp_bridge_hub_wococo::WITH_BRIDGE_HUB_WOCOCO_MESSAGES_PALLET_NAME; - const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = None; + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = + Some(bp_bridge_hub_wococo::WITH_BRIDGE_HUB_WOCOCO_RELAYERS_PALLET_NAME); const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = bp_bridge_hub_wococo::TO_BRIDGE_HUB_WOCOCO_MESSAGE_DETAILS_METHOD; diff --git a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs index 374dd5b44ce..b9803955922 100644 --- a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs +++ b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs @@ -19,17 +19,14 @@ use codec::{Decode, Encode}; use scale_info::TypeInfo; -pub use bp_bridge_hub_wococo::rewarding_bridge_signed_extension; +pub use bp_bridge_hub_wococo::SignedExtension; pub use bp_header_chain::BridgeGrandpaCallOf; pub use bp_parachains::BridgeParachainCall; pub use bridge_runtime_common::messages::BridgeMessagesCallOf; pub use relay_substrate_client::calls::{SystemCall, UtilityCall}; /// Unchecked BridgeHubWococo extrinsic. -pub type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic< - Call, - rewarding_bridge_signed_extension::RewardingBridgeSignedExtension, ->; +pub type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic; // The indirect pallet call used to sync `Rococo` GRANDPA finality to `BHWococo`. pub type BridgeRococoGrandpaCall = BridgeGrandpaCallOf; diff --git a/relays/client-kusama/src/lib.rs b/relays/client-kusama/src/lib.rs index 4b631b6052b..99a52b4baf4 100644 --- a/relays/client-kusama/src/lib.rs +++ b/relays/client-kusama/src/lib.rs @@ -18,13 +18,16 @@ use bp_kusama::AccountInfoStorageMapKeyProvider; use bp_runtime::ChainId; -use relay_substrate_client::{Chain, ChainWithBalances, UnderlyingChainProvider}; +use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider}; use sp_core::storage::StorageKey; use std::time::Duration; /// Kusama header id. pub type HeaderId = relay_utils::HeaderId; +/// Kusama header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + /// Kusama chain definition #[derive(Debug, Clone, Copy)] pub struct Kusama; @@ -36,7 +39,6 @@ impl UnderlyingChainProvider for Kusama { impl Chain for Kusama { const ID: ChainId = bp_runtime::KUSAMA_CHAIN_ID; const NAME: &'static str = "Kusama"; - const TOKEN_ID: Option<&'static str> = Some("kusama"); const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); @@ -51,5 +53,8 @@ impl ChainWithBalances for Kusama { } } -/// Kusama header type used in headers sync. -pub type SyncHeader = relay_substrate_client::SyncHeader; +impl RelayChain for Kusama { + const PARAS_PALLET_NAME: &'static str = bp_kusama::PARAS_PALLET_NAME; + // TODO: check me (https://github.com/paritytech/parity-bridges-common/issues/1945) + const PARACHAINS_FINALITY_PALLET_NAME: &'static str = "BridgeKusamaParachain"; +} diff --git a/relays/client-millau/src/lib.rs b/relays/client-millau/src/lib.rs index 1c8a0f984ef..ce42d004bb8 100644 --- a/relays/client-millau/src/lib.rs +++ b/relays/client-millau/src/lib.rs @@ -57,8 +57,6 @@ impl ChainWithMessages for Millau { impl Chain for Millau { const ID: ChainId = bp_runtime::MILLAU_CHAIN_ID; const NAME: &'static str = "Millau"; - // Rialto token has no value, but we associate it with KSM token - const TOKEN_ID: Option<&'static str> = Some("kusama"); const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); diff --git a/relays/client-polkadot/src/lib.rs b/relays/client-polkadot/src/lib.rs index 08837bdcec2..7f8615e0cd0 100644 --- a/relays/client-polkadot/src/lib.rs +++ b/relays/client-polkadot/src/lib.rs @@ -18,13 +18,16 @@ use bp_polkadot::AccountInfoStorageMapKeyProvider; use bp_runtime::ChainId; -use relay_substrate_client::{Chain, ChainWithBalances, UnderlyingChainProvider}; +use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider}; use sp_core::storage::StorageKey; use std::time::Duration; /// Polkadot header id. pub type HeaderId = relay_utils::HeaderId; +/// Polkadot header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + /// Polkadot chain definition #[derive(Debug, Clone, Copy)] pub struct Polkadot; @@ -36,7 +39,6 @@ impl UnderlyingChainProvider for Polkadot { impl Chain for Polkadot { const ID: ChainId = bp_runtime::POLKADOT_CHAIN_ID; const NAME: &'static str = "Polkadot"; - const TOKEN_ID: Option<&'static str> = Some("polkadot"); const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); @@ -51,5 +53,8 @@ impl ChainWithBalances for Polkadot { } } -/// Polkadot header type used in headers sync. -pub type SyncHeader = relay_substrate_client::SyncHeader; +impl RelayChain for Polkadot { + const PARAS_PALLET_NAME: &'static str = bp_polkadot::PARAS_PALLET_NAME; + // TODO: check me (https://github.com/paritytech/parity-bridges-common/issues/1945) + const PARACHAINS_FINALITY_PALLET_NAME: &'static str = "BridgePolkadotParachain"; +} diff --git a/relays/client-rialto-parachain/Cargo.toml b/relays/client-rialto-parachain/Cargo.toml index 807da290d24..987ac532ca6 100644 --- a/relays/client-rialto-parachain/Cargo.toml +++ b/relays/client-rialto-parachain/Cargo.toml @@ -8,10 +8,11 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5" } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -subxt = { git = "https://github.com/paritytech/subxt", branch = "master", default-features = false, features = [] } +subxt = { version = "0.27.1", default-features = false, features = [] } # Bridge dependencies +bp-bridge-hub-cumulus = { path = "../../primitives/chain-bridge-hub-cumulus" } bp-header-chain = { path = "../../primitives/header-chain" } bp-messages = { path = "../../primitives/messages" } bp-millau = { path = "../../primitives/chain-millau" } diff --git a/relays/client-rialto-parachain/src/codegen_runtime.rs b/relays/client-rialto-parachain/src/codegen_runtime.rs index d725aafd415..893bca1e27e 100644 --- a/relays/client-rialto-parachain/src/codegen_runtime.rs +++ b/relays/client-rialto-parachain/src/codegen_runtime.rs @@ -22,11 +22,10 @@ #[allow(clippy::all)] pub mod api { use super::api as root_mod; - pub static PALLETS: [&str; 17usize] = [ + pub static PALLETS: [&str; 16usize] = [ "System", "Timestamp", "Sudo", - "RandomnessCollectiveFlip", "TransactionPayment", "ParachainSystem", "ParachainInfo", @@ -49,7 +48,7 @@ pub mod api { System(system::Event), #[codec(index = 2)] Sudo(sudo::Event), - #[codec(index = 4)] + #[codec(index = 3)] TransactionPayment(transaction_payment::Event), #[codec(index = 20)] ParachainSystem(parachain_system::Event), @@ -65,6 +64,8 @@ pub mod api { DmpQueue(dmp_queue::Event), #[codec(index = 54)] BridgeRelayers(bridge_relayers::Event), + #[codec(index = 55)] + BridgeMillauGrandpa(bridge_millau_grandpa::Event), #[codec(index = 56)] BridgeMillauMessages(bridge_millau_messages::Event), } @@ -134,9 +135,8 @@ pub mod api { impl TransactionApi { #[doc = "Make some on-chain remark."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- `O(1)`"] - #[doc = "# "] pub fn remark( &self, remark: ::std::vec::Vec<::core::primitive::u8>, @@ -171,16 +171,8 @@ pub mod api { } #[doc = "Set the new runtime code."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code`"] - #[doc = "- 1 call to `can_set_code`: `O(S)` (calls `sp_io::misc::runtime_version` which is"] - #[doc = " expensive)."] - #[doc = "- 1 storage write (codec `O(C)`)."] - #[doc = "- 1 digest item."] - #[doc = "- 1 event."] - #[doc = "The weight of this function is dependent on the runtime, but generally this is very"] - #[doc = "expensive. We will treat this as a full block."] - #[doc = "# "] pub fn set_code( &self, code: ::std::vec::Vec<::core::primitive::u8>, @@ -198,13 +190,8 @@ pub mod api { } #[doc = "Set the new runtime code without doing any checks of the given `code`."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- `O(C)` where `C` length of `code`"] - #[doc = "- 1 storage write (codec `O(C)`)."] - #[doc = "- 1 digest item."] - #[doc = "- 1 event."] - #[doc = "The weight of this function is dependent on the runtime. We will treat this as a full"] - #[doc = "block. # "] pub fn set_code_without_checks( &self, code: ::std::vec::Vec<::core::primitive::u8>, @@ -379,7 +366,9 @@ pub mod api { ::subxt::metadata::DecodeStaticType< runtime_types::frame_system::AccountInfo< ::core::primitive::u32, - runtime_types::pallet_balances::AccountData<::core::primitive::u128>, + runtime_types::pallet_balances::types::AccountData< + ::core::primitive::u128, + >, >, >, ::subxt::storage::address::Yes, @@ -394,9 +383,10 @@ pub mod api { ::subxt::storage::address::StorageHasher::Blake2_128Concat, )], [ - 176u8, 187u8, 21u8, 220u8, 159u8, 204u8, 127u8, 14u8, 21u8, 69u8, 77u8, - 114u8, 230u8, 141u8, 107u8, 79u8, 23u8, 16u8, 174u8, 243u8, 252u8, - 42u8, 65u8, 120u8, 229u8, 38u8, 210u8, 255u8, 22u8, 40u8, 109u8, 223u8, + 248u8, 178u8, 160u8, 222u8, 45u8, 231u8, 115u8, 164u8, 98u8, 184u8, + 174u8, 206u8, 149u8, 190u8, 175u8, 34u8, 202u8, 230u8, 69u8, 218u8, + 83u8, 43u8, 170u8, 41u8, 106u8, 77u8, 233u8, 97u8, 114u8, 14u8, 155u8, + 131u8, ], ) } @@ -407,7 +397,9 @@ pub mod api { ::subxt::metadata::DecodeStaticType< runtime_types::frame_system::AccountInfo< ::core::primitive::u32, - runtime_types::pallet_balances::AccountData<::core::primitive::u128>, + runtime_types::pallet_balances::types::AccountData< + ::core::primitive::u128, + >, >, >, (), @@ -419,9 +411,10 @@ pub mod api { "Account", Vec::new(), [ - 176u8, 187u8, 21u8, 220u8, 159u8, 204u8, 127u8, 14u8, 21u8, 69u8, 77u8, - 114u8, 230u8, 141u8, 107u8, 79u8, 23u8, 16u8, 174u8, 243u8, 252u8, - 42u8, 65u8, 120u8, 229u8, 38u8, 210u8, 255u8, 22u8, 40u8, 109u8, 223u8, + 248u8, 178u8, 160u8, 222u8, 45u8, 231u8, 115u8, 164u8, 98u8, 184u8, + 174u8, 206u8, 149u8, 190u8, 175u8, 34u8, 202u8, 230u8, 69u8, 218u8, + 83u8, 43u8, 170u8, 41u8, 106u8, 77u8, 233u8, 97u8, 114u8, 14u8, 155u8, + 131u8, ], ) } @@ -668,9 +661,9 @@ pub mod api { "Events", vec![], [ - 4u8, 71u8, 59u8, 55u8, 39u8, 106u8, 211u8, 249u8, 109u8, 197u8, 134u8, - 138u8, 5u8, 188u8, 131u8, 120u8, 65u8, 30u8, 151u8, 104u8, 238u8, 32u8, - 251u8, 122u8, 104u8, 218u8, 7u8, 84u8, 253u8, 65u8, 48u8, 185u8, + 76u8, 19u8, 44u8, 121u8, 59u8, 168u8, 101u8, 101u8, 248u8, 3u8, 172u8, + 27u8, 249u8, 200u8, 147u8, 17u8, 4u8, 102u8, 186u8, 6u8, 152u8, 62u8, + 76u8, 195u8, 45u8, 188u8, 191u8, 30u8, 134u8, 78u8, 199u8, 93u8, ], ) } @@ -990,12 +983,11 @@ pub mod api { #[doc = ""] #[doc = "The dispatch origin for this call must be `Inherent`."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`)"] #[doc = "- 1 storage read and 1 storage mutation (codec `O(1)`). (because of `DidUpdate::take` in"] #[doc = " `on_finalize`)"] #[doc = "- 1 event handler `on_timestamp_set`. Must be `O(1)`."] - #[doc = "# "] pub fn set( &self, now: ::core::primitive::u64, @@ -1124,12 +1116,8 @@ pub mod api { #[doc = ""] #[doc = "The dispatch origin for this call must be _Signed_."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- O(1)."] - #[doc = "- Limited storage reads."] - #[doc = "- One DB write (event)."] - #[doc = "- Weight of derivative `call` execution + 10,000."] - #[doc = "# "] pub fn sudo( &self, call: runtime_types::rialto_parachain_runtime::RuntimeCall, @@ -1139,10 +1127,9 @@ pub mod api { "sudo", Sudo { call: ::std::boxed::Box::new(call) }, [ - 229u8, 162u8, 204u8, 182u8, 225u8, 254u8, 3u8, 66u8, 233u8, 49u8, 60u8, - 73u8, 182u8, 192u8, 90u8, 122u8, 176u8, 63u8, 200u8, 64u8, 111u8, - 155u8, 223u8, 233u8, 51u8, 220u8, 157u8, 192u8, 74u8, 236u8, 150u8, - 222u8, + 19u8, 178u8, 172u8, 30u8, 28u8, 209u8, 160u8, 61u8, 76u8, 239u8, 71u8, + 124u8, 21u8, 34u8, 233u8, 176u8, 100u8, 90u8, 198u8, 118u8, 117u8, 2u8, + 147u8, 7u8, 109u8, 1u8, 32u8, 35u8, 99u8, 0u8, 107u8, 145u8, ], ) } @@ -1152,10 +1139,8 @@ pub mod api { #[doc = ""] #[doc = "The dispatch origin for this call must be _Signed_."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- O(1)."] - #[doc = "- The weight of this call is defined by the caller."] - #[doc = "# "] pub fn sudo_unchecked_weight( &self, call: runtime_types::rialto_parachain_runtime::RuntimeCall, @@ -1166,9 +1151,10 @@ pub mod api { "sudo_unchecked_weight", SudoUncheckedWeight { call: ::std::boxed::Box::new(call), weight }, [ - 161u8, 202u8, 77u8, 33u8, 112u8, 211u8, 100u8, 184u8, 205u8, 250u8, - 70u8, 16u8, 39u8, 213u8, 108u8, 2u8, 195u8, 255u8, 15u8, 72u8, 142u8, - 180u8, 26u8, 203u8, 73u8, 81u8, 10u8, 29u8, 34u8, 101u8, 47u8, 99u8, + 145u8, 160u8, 12u8, 105u8, 228u8, 240u8, 115u8, 105u8, 220u8, 99u8, + 215u8, 228u8, 115u8, 71u8, 109u8, 28u8, 149u8, 247u8, 159u8, 216u8, + 76u8, 71u8, 68u8, 87u8, 254u8, 146u8, 185u8, 174u8, 251u8, 209u8, 72u8, + 122u8, ], ) } @@ -1177,11 +1163,8 @@ pub mod api { #[doc = ""] #[doc = "The dispatch origin for this call must be _Signed_."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- O(1)."] - #[doc = "- Limited storage reads."] - #[doc = "- One DB change."] - #[doc = "# "] pub fn set_key( &self, new: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, @@ -1202,12 +1185,8 @@ pub mod api { #[doc = ""] #[doc = "The dispatch origin for this call must be _Signed_."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- O(1)."] - #[doc = "- Limited storage reads."] - #[doc = "- One DB write (event)."] - #[doc = "- Weight of derivative `call` execution + 10,000."] - #[doc = "# "] pub fn sudo_as( &self, who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, @@ -1218,10 +1197,10 @@ pub mod api { "sudo_as", SudoAs { who, call: ::std::boxed::Box::new(call) }, [ - 100u8, 104u8, 192u8, 203u8, 123u8, 243u8, 209u8, 131u8, 26u8, 247u8, - 84u8, 157u8, 133u8, 227u8, 210u8, 237u8, 58u8, 23u8, 139u8, 213u8, - 134u8, 19u8, 12u8, 31u8, 219u8, 212u8, 230u8, 37u8, 152u8, 199u8, - 220u8, 163u8, + 229u8, 45u8, 129u8, 96u8, 54u8, 29u8, 159u8, 77u8, 210u8, 144u8, 29u8, + 97u8, 127u8, 133u8, 122u8, 110u8, 152u8, 194u8, 211u8, 246u8, 97u8, + 197u8, 187u8, 203u8, 46u8, 12u8, 104u8, 125u8, 15u8, 226u8, 28u8, + 183u8, ], ) } @@ -1294,42 +1273,6 @@ pub mod api { } } } - pub mod randomness_collective_flip { - use super::{root_mod, runtime_types}; - pub mod storage { - use super::runtime_types; - pub struct StorageApi; - impl StorageApi { - #[doc = " Series of block headers from the last 81 blocks that acts as random seed material. This"] - #[doc = " is arranged as a ring buffer with `block_number % 81` being the index into the `Vec` of"] - #[doc = " the oldest hash."] - pub fn random_material( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::sp_core::bounded::bounded_vec::BoundedVec< - ::subxt::utils::H256, - >, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "RandomnessCollectiveFlip", - "RandomMaterial", - vec![], - [ - 152u8, 126u8, 73u8, 88u8, 54u8, 147u8, 6u8, 19u8, 214u8, 40u8, 159u8, - 30u8, 236u8, 61u8, 240u8, 65u8, 178u8, 94u8, 146u8, 152u8, 135u8, - 252u8, 160u8, 86u8, 123u8, 114u8, 251u8, 140u8, 98u8, 143u8, 217u8, - 242u8, - ], - ) - } - } - } - } pub mod transaction_payment { use super::{root_mod, runtime_types}; #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] @@ -1468,6 +1411,7 @@ pub mod api { )] pub struct AuthorizeUpgrade { pub code_hash: ::subxt::utils::H256, + pub check_version: ::core::primitive::bool, } #[derive( :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, @@ -1517,21 +1461,40 @@ pub mod api { ], ) } + #[doc = "Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied"] + #[doc = "later."] + #[doc = ""] + #[doc = "The `check_version` parameter sets a boolean flag for whether or not the runtime's spec"] + #[doc = "version and name should be verified on upgrade. Since the authorization only has a hash,"] + #[doc = "it cannot actually perform the verification."] + #[doc = ""] + #[doc = "This call requires Root origin."] pub fn authorize_upgrade( &self, code_hash: ::subxt::utils::H256, + check_version: ::core::primitive::bool, ) -> ::subxt::tx::StaticTxPayload { ::subxt::tx::StaticTxPayload::new( "ParachainSystem", "authorize_upgrade", - AuthorizeUpgrade { code_hash }, + AuthorizeUpgrade { code_hash, check_version }, [ - 52u8, 152u8, 69u8, 207u8, 143u8, 113u8, 163u8, 11u8, 181u8, 182u8, - 124u8, 101u8, 207u8, 19u8, 59u8, 81u8, 129u8, 29u8, 79u8, 115u8, 90u8, - 83u8, 225u8, 124u8, 21u8, 108u8, 99u8, 194u8, 78u8, 83u8, 252u8, 163u8, + 208u8, 115u8, 62u8, 35u8, 70u8, 223u8, 65u8, 57u8, 216u8, 44u8, 169u8, + 249u8, 90u8, 112u8, 17u8, 208u8, 30u8, 131u8, 102u8, 131u8, 240u8, + 217u8, 230u8, 214u8, 145u8, 198u8, 55u8, 13u8, 217u8, 51u8, 178u8, + 141u8, ], ) } + #[doc = "Provide the preimage (runtime binary) `code` for an upgrade that has been authorized."] + #[doc = ""] + #[doc = "If the authorization required a version check, this call will ensure the spec name"] + #[doc = "remains unchanged and that the spec version has increased."] + #[doc = ""] + #[doc = "Note that this function will not apply the new `code`, but only attempt to schedule the"] + #[doc = "upgrade with the Relay Chain."] + #[doc = ""] + #[doc = "All origins are allowed."] pub fn enact_authorized_upgrade( &self, code: ::std::vec::Vec<::core::primitive::u8>, @@ -1696,7 +1659,7 @@ pub mod api { &self, ) -> ::subxt::storage::address::StaticStorageAddress< ::subxt::metadata::DecodeStaticType< - runtime_types::polkadot_primitives::v2::PersistedValidationData< + runtime_types::polkadot_primitives::v4::PersistedValidationData< ::subxt::utils::H256, ::core::primitive::u32, >, @@ -1769,7 +1732,7 @@ pub mod api { ) -> ::subxt::storage::address::StaticStorageAddress< ::subxt::metadata::DecodeStaticType< ::core::option::Option< - runtime_types::polkadot_primitives::v2::UpgradeRestriction, + runtime_types::polkadot_primitives::v4::UpgradeRestriction, >, >, ::subxt::storage::address::Yes, @@ -1844,7 +1807,7 @@ pub mod api { &self, ) -> ::subxt::storage::address::StaticStorageAddress< ::subxt::metadata::DecodeStaticType< - runtime_types::polkadot_primitives::v2::AbridgedHostConfiguration, + runtime_types::polkadot_primitives::v4::AbridgedHostConfiguration, >, ::subxt::storage::address::Yes, (), @@ -2105,7 +2068,9 @@ pub mod api { pub fn authorized_upgrade( &self, ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::subxt::utils::H256>, + ::subxt::metadata::DecodeStaticType< + runtime_types::cumulus_pallet_parachain_system::CodeUpgradeAuthorization, + >, ::subxt::storage::address::Yes, (), (), @@ -2115,10 +2080,9 @@ pub mod api { "AuthorizedUpgrade", vec![], [ - 136u8, 238u8, 241u8, 144u8, 252u8, 61u8, 101u8, 171u8, 234u8, 160u8, - 145u8, 210u8, 69u8, 29u8, 204u8, 166u8, 250u8, 101u8, 254u8, 32u8, - 96u8, 197u8, 222u8, 212u8, 50u8, 189u8, 25u8, 7u8, 48u8, 183u8, 234u8, - 95u8, + 12u8, 212u8, 71u8, 191u8, 89u8, 101u8, 195u8, 3u8, 23u8, 180u8, 233u8, + 52u8, 53u8, 133u8, 207u8, 94u8, 58u8, 43u8, 221u8, 236u8, 161u8, 41u8, + 30u8, 194u8, 125u8, 2u8, 118u8, 152u8, 197u8, 49u8, 34u8, 33u8, ], ) } @@ -2186,7 +2150,7 @@ pub mod api { #[derive( :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, )] - pub struct Transfer { + pub struct TransferAllowDeath { pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, #[codec(compact)] pub value: ::core::primitive::u128, @@ -2194,12 +2158,12 @@ pub mod api { #[derive( :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, )] - pub struct SetBalance { + pub struct SetBalanceDeprecated { pub who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, #[codec(compact)] pub new_free: ::core::primitive::u128, #[codec(compact)] - pub new_reserved: ::core::primitive::u128, + pub old_reserved: ::core::primitive::u128, } #[derive( :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, @@ -2232,82 +2196,79 @@ pub mod api { pub who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, pub amount: ::core::primitive::u128, } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct UpgradeAccounts { + pub who: ::std::vec::Vec<::sp_core::crypto::AccountId32>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Transfer { + pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + pub value: ::core::primitive::u128, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ForceSetBalance { + pub who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + pub new_free: ::core::primitive::u128, + } pub struct TransactionApi; impl TransactionApi { #[doc = "Transfer some liquid free balance to another account."] #[doc = ""] - #[doc = "`transfer` will set the `FreeBalance` of the sender and receiver."] + #[doc = "`transfer_allow_death` will set the `FreeBalance` of the sender and receiver."] #[doc = "If the sender's account is below the existential deposit as a result"] #[doc = "of the transfer, the account will be reaped."] #[doc = ""] #[doc = "The dispatch origin for this call must be `Signed` by the transactor."] - #[doc = ""] - #[doc = "# "] - #[doc = "- Dependent on arguments but not critical, given proper implementations for input config"] - #[doc = " types. See related functions below."] - #[doc = "- It contains a limited number of reads and writes internally and no complex"] - #[doc = " computation."] - #[doc = ""] - #[doc = "Related functions:"] - #[doc = ""] - #[doc = " - `ensure_can_withdraw` is always called internally but has a bounded complexity."] - #[doc = " - Transferring balances to accounts that did not exist before will cause"] - #[doc = " `T::OnNewAccount::on_new_account` to be called."] - #[doc = " - Removing enough funds from an account will trigger `T::DustRemoval::on_unbalanced`."] - #[doc = " - `transfer_keep_alive` works the same way as `transfer`, but has an additional check"] - #[doc = " that the transfer will not kill the origin account."] - #[doc = "---------------------------------"] - #[doc = "- Origin account is already in memory, so no DB operations for them."] - #[doc = "# "] - pub fn transfer( + pub fn transfer_allow_death( &self, dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, value: ::core::primitive::u128, - ) -> ::subxt::tx::StaticTxPayload { + ) -> ::subxt::tx::StaticTxPayload { ::subxt::tx::StaticTxPayload::new( "Balances", - "transfer", - Transfer { dest, value }, + "transfer_allow_death", + TransferAllowDeath { dest, value }, [ - 111u8, 222u8, 32u8, 56u8, 171u8, 77u8, 252u8, 29u8, 194u8, 155u8, - 200u8, 192u8, 198u8, 81u8, 23u8, 115u8, 236u8, 91u8, 218u8, 114u8, - 107u8, 141u8, 138u8, 100u8, 237u8, 21u8, 58u8, 172u8, 3u8, 20u8, 216u8, - 38u8, + 234u8, 130u8, 149u8, 36u8, 235u8, 112u8, 159u8, 189u8, 104u8, 148u8, + 108u8, 230u8, 25u8, 198u8, 71u8, 158u8, 112u8, 3u8, 162u8, 25u8, 145u8, + 252u8, 44u8, 63u8, 47u8, 34u8, 47u8, 158u8, 61u8, 14u8, 120u8, 255u8, ], ) } - #[doc = "Set the balances of a given account."] - #[doc = ""] - #[doc = "This will alter `FreeBalance` and `ReservedBalance` in storage. it will"] - #[doc = "also alter the total issuance of the system (`TotalIssuance`) appropriately."] - #[doc = "If the new free or reserved balance is below the existential deposit,"] - #[doc = "it will reset the account nonce (`frame_system::AccountNonce`)."] + #[doc = "Set the regular balance of a given account; it also takes a reserved balance but this"] + #[doc = "must be the same as the account's current reserved balance."] #[doc = ""] #[doc = "The dispatch origin for this call is `root`."] - pub fn set_balance( + #[doc = ""] + #[doc = "WARNING: This call is DEPRECATED! Use `force_set_balance` instead."] + pub fn set_balance_deprecated( &self, who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, new_free: ::core::primitive::u128, - new_reserved: ::core::primitive::u128, - ) -> ::subxt::tx::StaticTxPayload { + old_reserved: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { ::subxt::tx::StaticTxPayload::new( "Balances", - "set_balance", - SetBalance { who, new_free, new_reserved }, + "set_balance_deprecated", + SetBalanceDeprecated { who, new_free, old_reserved }, [ - 234u8, 215u8, 97u8, 98u8, 243u8, 199u8, 57u8, 76u8, 59u8, 161u8, 118u8, - 207u8, 34u8, 197u8, 198u8, 61u8, 231u8, 210u8, 169u8, 235u8, 150u8, - 137u8, 173u8, 49u8, 28u8, 77u8, 84u8, 149u8, 143u8, 210u8, 139u8, - 193u8, + 240u8, 107u8, 184u8, 206u8, 78u8, 106u8, 115u8, 152u8, 130u8, 56u8, + 156u8, 176u8, 105u8, 27u8, 176u8, 187u8, 49u8, 171u8, 229u8, 79u8, + 254u8, 248u8, 8u8, 162u8, 134u8, 12u8, 89u8, 100u8, 137u8, 102u8, + 132u8, 158u8, ], ) } - #[doc = "Exactly as `transfer`, except the origin must be root and the source account may be"] - #[doc = "specified."] - #[doc = "# "] - #[doc = "- Same as transfer, but additional read and write because the source account is not"] - #[doc = " assumed to be in the overlay."] - #[doc = "# "] + #[doc = "Exactly as `transfer_allow_death`, except the origin must be root and the source account"] + #[doc = "may be specified."] pub fn force_transfer( &self, source: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, @@ -2326,12 +2287,12 @@ pub mod api { ], ) } - #[doc = "Same as the [`transfer`] call, but with a check that the transfer will not kill the"] - #[doc = "origin account."] + #[doc = "Same as the [`transfer_allow_death`] call, but with a check that the transfer will not"] + #[doc = "kill the origin account."] #[doc = ""] - #[doc = "99% of the time you want [`transfer`] instead."] + #[doc = "99% of the time you want [`transfer_allow_death`] instead."] #[doc = ""] - #[doc = "[`transfer`]: struct.Pallet.html#method.transfer"] + #[doc = "[`transfer_allow_death`]: struct.Pallet.html#method.transfer"] pub fn transfer_keep_alive( &self, dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, @@ -2363,9 +2324,7 @@ pub mod api { #[doc = "- `keep_alive`: A boolean to determine if the `transfer_all` operation should send all"] #[doc = " of the funds the account has, causing the sender account to be killed (false), or"] #[doc = " transfer everything except at least the existential deposit, which will guarantee to"] - #[doc = " keep the sender account alive (true). # "] - #[doc = "- O(1). Just like transfer, but reading the user's transferable balance first."] - #[doc = " #"] + #[doc = " keep the sender account alive (true)."] pub fn transfer_all( &self, dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, @@ -2403,6 +2362,68 @@ pub mod api { ], ) } + #[doc = "Upgrade a specified account."] + #[doc = ""] + #[doc = "- `origin`: Must be `Signed`."] + #[doc = "- `who`: The account to be upgraded."] + #[doc = ""] + #[doc = "This will waive the transaction fee if at least all but 10% of the accounts needed to"] + #[doc = "be upgraded. (We let some not have to be upgraded just in order to allow for the"] + #[doc = "possibililty of churn)."] + pub fn upgrade_accounts( + &self, + who: ::std::vec::Vec<::sp_core::crypto::AccountId32>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "upgrade_accounts", + UpgradeAccounts { who }, + [ + 164u8, 61u8, 119u8, 24u8, 165u8, 46u8, 197u8, 59u8, 39u8, 198u8, 228u8, + 96u8, 228u8, 45u8, 85u8, 51u8, 37u8, 5u8, 75u8, 40u8, 241u8, 163u8, + 86u8, 228u8, 151u8, 217u8, 47u8, 105u8, 203u8, 103u8, 207u8, 4u8, + ], + ) + } + #[doc = "Alias for `transfer_allow_death`, provided only for name-wise compatibility."] + #[doc = ""] + #[doc = "WARNING: DEPRECATED! Will be released in approximately 3 months."] + pub fn transfer( + &self, + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + value: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "transfer", + Transfer { dest, value }, + [ + 111u8, 222u8, 32u8, 56u8, 171u8, 77u8, 252u8, 29u8, 194u8, 155u8, + 200u8, 192u8, 198u8, 81u8, 23u8, 115u8, 236u8, 91u8, 218u8, 114u8, + 107u8, 141u8, 138u8, 100u8, 237u8, 21u8, 58u8, 172u8, 3u8, 20u8, 216u8, + 38u8, + ], + ) + } + #[doc = "Set the regular balance of a given account."] + #[doc = ""] + #[doc = "The dispatch origin for this call is `root`."] + pub fn force_set_balance( + &self, + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + new_free: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "force_set_balance", + ForceSetBalance { who, new_free }, + [ + 237u8, 4u8, 41u8, 58u8, 62u8, 179u8, 160u8, 4u8, 50u8, 71u8, 178u8, + 36u8, 130u8, 130u8, 92u8, 229u8, 16u8, 245u8, 169u8, 109u8, 165u8, + 72u8, 94u8, 70u8, 196u8, 136u8, 37u8, 94u8, 140u8, 215u8, 125u8, 125u8, + ], + ) + } } } #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] @@ -2454,7 +2475,6 @@ pub mod api { pub struct BalanceSet { pub who: ::sp_core::crypto::AccountId32, pub free: ::core::primitive::u128, - pub reserved: ::core::primitive::u128, } impl ::subxt::events::StaticEvent for BalanceSet { const PALLET: &'static str = "Balances"; @@ -2536,6 +2556,95 @@ pub mod api { const PALLET: &'static str = "Balances"; const EVENT: &'static str = "Slashed"; } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some amount was minted into an account."] + pub struct Minted { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Minted { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Minted"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some amount was burned from an account."] + pub struct Burned { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Burned { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Burned"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some amount was suspended from an account (it can be restored later)."] + pub struct Suspended { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Suspended { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Suspended"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some amount was restored into an account."] + pub struct Restored { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Restored { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Restored"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An account was upgraded."] + pub struct Upgraded { + pub who: ::sp_core::crypto::AccountId32, + } + impl ::subxt::events::StaticEvent for Upgraded { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Upgraded"; + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Total issuance was increased by `amount`, creating a credit to be balanced."] + pub struct Issued { + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Issued { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Issued"; + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Total issuance was decreased by `amount`, creating a debt to be balanced."] + pub struct Rescinded { + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Rescinded { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Rescinded"; + } } pub mod storage { use super::runtime_types; @@ -2611,7 +2720,7 @@ pub mod api { _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, ) -> ::subxt::storage::address::StaticStorageAddress< ::subxt::metadata::DecodeStaticType< - runtime_types::pallet_balances::AccountData<::core::primitive::u128>, + runtime_types::pallet_balances::types::AccountData<::core::primitive::u128>, >, ::subxt::storage::address::Yes, ::subxt::storage::address::Yes, @@ -2625,9 +2734,10 @@ pub mod api { ::subxt::storage::address::StorageHasher::Blake2_128Concat, )], [ - 246u8, 154u8, 253u8, 71u8, 192u8, 192u8, 192u8, 236u8, 128u8, 80u8, - 40u8, 252u8, 201u8, 43u8, 3u8, 131u8, 19u8, 49u8, 141u8, 240u8, 172u8, - 217u8, 215u8, 109u8, 87u8, 135u8, 248u8, 57u8, 98u8, 185u8, 22u8, 4u8, + 109u8, 250u8, 18u8, 96u8, 139u8, 232u8, 4u8, 139u8, 133u8, 239u8, 30u8, + 237u8, 73u8, 209u8, 143u8, 160u8, 94u8, 248u8, 124u8, 43u8, 224u8, + 165u8, 11u8, 6u8, 176u8, 144u8, 189u8, 161u8, 174u8, 210u8, 56u8, + 225u8, ], ) } @@ -2659,7 +2769,7 @@ pub mod api { &self, ) -> ::subxt::storage::address::StaticStorageAddress< ::subxt::metadata::DecodeStaticType< - runtime_types::pallet_balances::AccountData<::core::primitive::u128>, + runtime_types::pallet_balances::types::AccountData<::core::primitive::u128>, >, (), ::subxt::storage::address::Yes, @@ -2670,9 +2780,10 @@ pub mod api { "Account", Vec::new(), [ - 246u8, 154u8, 253u8, 71u8, 192u8, 192u8, 192u8, 236u8, 128u8, 80u8, - 40u8, 252u8, 201u8, 43u8, 3u8, 131u8, 19u8, 49u8, 141u8, 240u8, 172u8, - 217u8, 215u8, 109u8, 87u8, 135u8, 248u8, 57u8, 98u8, 185u8, 22u8, 4u8, + 109u8, 250u8, 18u8, 96u8, 139u8, 232u8, 4u8, 139u8, 133u8, 239u8, 30u8, + 237u8, 73u8, 209u8, 143u8, 160u8, 94u8, 248u8, 124u8, 43u8, 224u8, + 165u8, 11u8, 6u8, 176u8, 144u8, 189u8, 161u8, 174u8, 210u8, 56u8, + 225u8, ], ) } @@ -2683,8 +2794,10 @@ pub mod api { _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, ) -> ::subxt::storage::address::StaticStorageAddress< ::subxt::metadata::DecodeStaticType< - runtime_types::sp_core::bounded::weak_bounded_vec::WeakBoundedVec< - runtime_types::pallet_balances::BalanceLock<::core::primitive::u128>, + runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< + runtime_types::pallet_balances::types::BalanceLock< + ::core::primitive::u128, + >, >, >, ::subxt::storage::address::Yes, @@ -2711,8 +2824,10 @@ pub mod api { &self, ) -> ::subxt::storage::address::StaticStorageAddress< ::subxt::metadata::DecodeStaticType< - runtime_types::sp_core::bounded::weak_bounded_vec::WeakBoundedVec< - runtime_types::pallet_balances::BalanceLock<::core::primitive::u128>, + runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< + runtime_types::pallet_balances::types::BalanceLock< + ::core::primitive::u128, + >, >, >, (), @@ -2736,8 +2851,8 @@ pub mod api { _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, ) -> ::subxt::storage::address::StaticStorageAddress< ::subxt::metadata::DecodeStaticType< - runtime_types::sp_core::bounded::bounded_vec::BoundedVec< - runtime_types::pallet_balances::ReserveData< + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::pallet_balances::types::ReserveData< [::core::primitive::u8; 8usize], ::core::primitive::u128, >, @@ -2766,8 +2881,8 @@ pub mod api { &self, ) -> ::subxt::storage::address::StaticStorageAddress< ::subxt::metadata::DecodeStaticType< - runtime_types::sp_core::bounded::bounded_vec::BoundedVec< - runtime_types::pallet_balances::ReserveData< + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::pallet_balances::types::ReserveData< [::core::primitive::u8; 8usize], ::core::primitive::u128, >, @@ -2788,6 +2903,122 @@ pub mod api { ], ) } + #[doc = " Holds on account balances."] + pub fn holds( + &self, + _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::pallet_balances::types::IdAmount< + (), + ::core::primitive::u128, + >, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Holds", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 247u8, 81u8, 4u8, 220u8, 77u8, 205u8, 28u8, 131u8, 215u8, 74u8, 197u8, + 137u8, 113u8, 214u8, 249u8, 91u8, 81u8, 216u8, 8u8, 5u8, 233u8, 39u8, + 104u8, 250u8, 3u8, 228u8, 148u8, 78u8, 4u8, 34u8, 45u8, 143u8, + ], + ) + } + #[doc = " Holds on account balances."] + pub fn holds_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::pallet_balances::types::IdAmount< + (), + ::core::primitive::u128, + >, + >, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Holds", + Vec::new(), + [ + 247u8, 81u8, 4u8, 220u8, 77u8, 205u8, 28u8, 131u8, 215u8, 74u8, 197u8, + 137u8, 113u8, 214u8, 249u8, 91u8, 81u8, 216u8, 8u8, 5u8, 233u8, 39u8, + 104u8, 250u8, 3u8, 228u8, 148u8, 78u8, 4u8, 34u8, 45u8, 143u8, + ], + ) + } + #[doc = " Freeze locks on account balances."] + pub fn freezes( + &self, + _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::pallet_balances::types::IdAmount< + (), + ::core::primitive::u128, + >, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Freezes", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 211u8, 24u8, 237u8, 217u8, 47u8, 230u8, 147u8, 39u8, 112u8, 209u8, + 193u8, 47u8, 242u8, 13u8, 241u8, 0u8, 100u8, 45u8, 116u8, 130u8, 246u8, + 196u8, 50u8, 134u8, 135u8, 112u8, 206u8, 1u8, 12u8, 53u8, 106u8, 131u8, + ], + ) + } + #[doc = " Freeze locks on account balances."] + pub fn freezes_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::pallet_balances::types::IdAmount< + (), + ::core::primitive::u128, + >, + >, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Freezes", + Vec::new(), + [ + 211u8, 24u8, 237u8, 217u8, 47u8, 230u8, 147u8, 39u8, 112u8, 209u8, + 193u8, 47u8, 242u8, 13u8, 241u8, 0u8, 100u8, 45u8, 116u8, 130u8, 246u8, + 196u8, 50u8, 134u8, 135u8, 112u8, 206u8, 1u8, 12u8, 53u8, 106u8, 131u8, + ], + ) + } } } pub mod constants { @@ -2845,6 +3076,40 @@ pub mod api { ], ) } + #[doc = " The maximum number of holds that can exist on an account at any time."] + pub fn max_holds( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "Balances", + "MaxHolds", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + #[doc = " The maximum number of individual freeze locks that can exist on an account at any time."] + pub fn max_freezes( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "Balances", + "MaxFreezes", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } } } } @@ -3660,9 +3925,9 @@ pub mod api { message: ::std::boxed::Box::new(message), }, [ - 26u8, 89u8, 11u8, 127u8, 105u8, 218u8, 45u8, 49u8, 234u8, 4u8, 72u8, - 86u8, 134u8, 227u8, 122u8, 187u8, 183u8, 63u8, 27u8, 203u8, 123u8, - 200u8, 64u8, 192u8, 64u8, 204u8, 68u8, 98u8, 27u8, 234u8, 176u8, 93u8, + 246u8, 35u8, 227u8, 112u8, 223u8, 7u8, 44u8, 186u8, 60u8, 225u8, 153u8, + 249u8, 104u8, 51u8, 123u8, 227u8, 143u8, 65u8, 232u8, 209u8, 178u8, + 104u8, 70u8, 56u8, 230u8, 14u8, 75u8, 83u8, 250u8, 160u8, 9u8, 39u8, ], ) } @@ -3698,10 +3963,9 @@ pub mod api { fee_asset_item, }, [ - 18u8, 130u8, 117u8, 55u8, 235u8, 117u8, 250u8, 246u8, 109u8, 145u8, - 160u8, 184u8, 72u8, 76u8, 61u8, 229u8, 50u8, 94u8, 60u8, 202u8, 25u8, - 180u8, 228u8, 63u8, 171u8, 182u8, 224u8, 146u8, 196u8, 203u8, 243u8, - 70u8, + 187u8, 42u8, 2u8, 96u8, 105u8, 125u8, 74u8, 53u8, 2u8, 21u8, 31u8, + 160u8, 201u8, 197u8, 157u8, 190u8, 40u8, 145u8, 5u8, 99u8, 194u8, 41u8, + 114u8, 60u8, 165u8, 186u8, 15u8, 226u8, 85u8, 113u8, 159u8, 136u8, ], ) } @@ -3738,9 +4002,9 @@ pub mod api { fee_asset_item, }, [ - 233u8, 36u8, 82u8, 54u8, 27u8, 201u8, 7u8, 145u8, 212u8, 107u8, 150u8, - 124u8, 125u8, 135u8, 226u8, 60u8, 133u8, 157u8, 202u8, 114u8, 102u8, - 67u8, 170u8, 66u8, 133u8, 228u8, 66u8, 45u8, 55u8, 188u8, 0u8, 19u8, + 249u8, 177u8, 76u8, 204u8, 186u8, 165u8, 16u8, 186u8, 129u8, 239u8, + 65u8, 252u8, 9u8, 132u8, 32u8, 164u8, 117u8, 177u8, 40u8, 21u8, 196u8, + 246u8, 147u8, 2u8, 95u8, 110u8, 68u8, 162u8, 148u8, 9u8, 59u8, 170u8, ], ) } @@ -3765,10 +4029,9 @@ pub mod api { "execute", Execute { message: ::std::boxed::Box::new(message), max_weight }, [ - 144u8, 56u8, 199u8, 18u8, 111u8, 10u8, 128u8, 179u8, 178u8, 216u8, - 119u8, 103u8, 11u8, 218u8, 221u8, 181u8, 129u8, 24u8, 192u8, 66u8, - 219u8, 239u8, 33u8, 161u8, 3u8, 51u8, 25u8, 177u8, 169u8, 188u8, 197u8, - 33u8, + 102u8, 41u8, 146u8, 29u8, 241u8, 205u8, 95u8, 153u8, 228u8, 141u8, + 11u8, 228u8, 13u8, 44u8, 75u8, 204u8, 174u8, 35u8, 155u8, 104u8, 204u8, + 82u8, 239u8, 98u8, 249u8, 187u8, 193u8, 1u8, 122u8, 88u8, 162u8, 200u8, ], ) } @@ -3788,10 +4051,9 @@ pub mod api { "force_xcm_version", ForceXcmVersion { location: ::std::boxed::Box::new(location), xcm_version }, [ - 150u8, 120u8, 133u8, 146u8, 71u8, 43u8, 247u8, 59u8, 124u8, 234u8, - 125u8, 149u8, 216u8, 199u8, 137u8, 17u8, 87u8, 220u8, 110u8, 154u8, - 95u8, 92u8, 251u8, 180u8, 253u8, 1u8, 200u8, 98u8, 36u8, 48u8, 231u8, - 3u8, + 68u8, 48u8, 95u8, 61u8, 152u8, 95u8, 213u8, 126u8, 209u8, 176u8, 230u8, + 160u8, 164u8, 42u8, 128u8, 62u8, 175u8, 3u8, 161u8, 170u8, 20u8, 31u8, + 216u8, 122u8, 31u8, 77u8, 64u8, 182u8, 121u8, 41u8, 23u8, 80u8, ], ) } @@ -3828,10 +4090,9 @@ pub mod api { "force_subscribe_version_notify", ForceSubscribeVersionNotify { location: ::std::boxed::Box::new(location) }, [ - 44u8, 16u8, 170u8, 133u8, 130u8, 247u8, 158u8, 196u8, 23u8, 250u8, - 128u8, 106u8, 40u8, 44u8, 245u8, 45u8, 170u8, 223u8, 15u8, 12u8, 204u8, - 159u8, 92u8, 154u8, 186u8, 107u8, 13u8, 70u8, 17u8, 174u8, 192u8, - 111u8, + 236u8, 37u8, 153u8, 26u8, 174u8, 187u8, 154u8, 38u8, 179u8, 223u8, + 130u8, 32u8, 128u8, 30u8, 148u8, 229u8, 7u8, 185u8, 174u8, 9u8, 96u8, + 215u8, 189u8, 178u8, 148u8, 141u8, 249u8, 118u8, 7u8, 238u8, 1u8, 49u8, ], ) } @@ -3852,10 +4113,9 @@ pub mod api { location: ::std::boxed::Box::new(location), }, [ - 77u8, 123u8, 3u8, 212u8, 40u8, 117u8, 46u8, 241u8, 174u8, 0u8, 131u8, - 242u8, 193u8, 177u8, 104u8, 161u8, 42u8, 54u8, 252u8, 70u8, 209u8, - 47u8, 87u8, 181u8, 75u8, 177u8, 157u8, 90u8, 145u8, 110u8, 121u8, - 208u8, + 154u8, 169u8, 145u8, 211u8, 185u8, 71u8, 9u8, 63u8, 3u8, 158u8, 187u8, + 173u8, 115u8, 166u8, 100u8, 66u8, 12u8, 40u8, 198u8, 40u8, 213u8, + 104u8, 95u8, 183u8, 215u8, 53u8, 94u8, 158u8, 106u8, 56u8, 149u8, 52u8, ], ) } @@ -3896,9 +4156,9 @@ pub mod api { weight_limit, }, [ - 71u8, 241u8, 225u8, 54u8, 199u8, 145u8, 122u8, 171u8, 17u8, 54u8, 20u8, - 80u8, 102u8, 131u8, 241u8, 79u8, 5u8, 3u8, 193u8, 26u8, 22u8, 180u8, - 237u8, 226u8, 109u8, 219u8, 185u8, 161u8, 153u8, 79u8, 102u8, 93u8, + 131u8, 191u8, 89u8, 27u8, 236u8, 142u8, 130u8, 129u8, 245u8, 95u8, + 159u8, 96u8, 252u8, 80u8, 28u8, 40u8, 128u8, 55u8, 41u8, 123u8, 22u8, + 18u8, 0u8, 236u8, 77u8, 68u8, 135u8, 181u8, 40u8, 47u8, 92u8, 240u8, ], ) } @@ -3938,10 +4198,10 @@ pub mod api { weight_limit, }, [ - 157u8, 57u8, 28u8, 124u8, 235u8, 202u8, 68u8, 125u8, 203u8, 246u8, - 109u8, 4u8, 199u8, 14u8, 103u8, 38u8, 67u8, 185u8, 81u8, 67u8, 124u8, - 64u8, 197u8, 45u8, 101u8, 199u8, 73u8, 196u8, 162u8, 36u8, 100u8, - 107u8, + 234u8, 19u8, 104u8, 174u8, 98u8, 159u8, 205u8, 110u8, 240u8, 78u8, + 186u8, 138u8, 236u8, 116u8, 104u8, 215u8, 57u8, 178u8, 166u8, 208u8, + 197u8, 113u8, 101u8, 56u8, 23u8, 56u8, 84u8, 14u8, 173u8, 70u8, 211u8, + 201u8, ], ) } @@ -4477,6 +4737,17 @@ pub mod api { const PALLET: &'static str = "DmpQueue"; const EVENT: &'static str = "OverweightServiced"; } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "The maximum number of downward messages was."] + pub struct MaxMessagesExhausted { + pub message_id: [::core::primitive::u8; 32usize], + } + impl ::subxt::events::StaticEvent for MaxMessagesExhausted { + const PALLET: &'static str = "DmpQueue"; + const EVENT: &'static str = "MaxMessagesExhausted"; + } } pub mod storage { use super::runtime_types; @@ -4667,23 +4938,24 @@ pub mod api { :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, )] pub struct ClaimRewards { - pub lane_id: runtime_types::bp_messages::LaneId, + pub rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, } pub struct TransactionApi; impl TransactionApi { #[doc = "Claim accumulated rewards."] pub fn claim_rewards( &self, - lane_id: runtime_types::bp_messages::LaneId, + rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, ) -> ::subxt::tx::StaticTxPayload { ::subxt::tx::StaticTxPayload::new( "BridgeRelayers", "claim_rewards", - ClaimRewards { lane_id }, + ClaimRewards { rewards_account_params }, [ - 119u8, 55u8, 12u8, 215u8, 255u8, 17u8, 1u8, 92u8, 187u8, 154u8, 120u8, - 10u8, 51u8, 16u8, 160u8, 18u8, 194u8, 236u8, 95u8, 173u8, 64u8, 75u8, - 251u8, 68u8, 235u8, 116u8, 69u8, 240u8, 186u8, 153u8, 118u8, 17u8, + 141u8, 52u8, 193u8, 42u8, 145u8, 26u8, 147u8, 35u8, 227u8, 221u8, + 221u8, 188u8, 104u8, 186u8, 123u8, 46u8, 190u8, 236u8, 120u8, 19u8, + 230u8, 219u8, 238u8, 227u8, 75u8, 35u8, 36u8, 177u8, 227u8, 130u8, + 103u8, 128u8, ], ) } @@ -4699,7 +4971,7 @@ pub mod api { #[doc = "Reward has been paid to the relayer."] pub struct RewardPaid { pub relayer: ::sp_core::crypto::AccountId32, - pub lane_id: runtime_types::bp_messages::LaneId, + pub rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, pub reward: ::core::primitive::u128, } impl ::subxt::events::StaticEvent for RewardPaid { @@ -4715,7 +4987,7 @@ pub mod api { pub fn relayer_rewards( &self, _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, - _1: impl ::std::borrow::Borrow, + _1: impl ::std::borrow::Borrow, ) -> ::subxt::storage::address::StaticStorageAddress< ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, ::subxt::storage::address::Yes, @@ -4736,10 +5008,10 @@ pub mod api { ), ], [ - 79u8, 50u8, 107u8, 107u8, 201u8, 181u8, 235u8, 111u8, 185u8, 125u8, - 108u8, 204u8, 163u8, 11u8, 69u8, 236u8, 90u8, 232u8, 9u8, 225u8, 210u8, - 142u8, 175u8, 166u8, 159u8, 74u8, 112u8, 249u8, 16u8, 115u8, 144u8, - 177u8, + 116u8, 81u8, 48u8, 55u8, 199u8, 26u8, 100u8, 7u8, 177u8, 230u8, 132u8, + 248u8, 221u8, 90u8, 33u8, 155u8, 198u8, 216u8, 43u8, 149u8, 92u8, + 100u8, 199u8, 183u8, 150u8, 214u8, 199u8, 222u8, 224u8, 228u8, 238u8, + 108u8, ], ) } @@ -4757,10 +5029,10 @@ pub mod api { "RelayerRewards", Vec::new(), [ - 79u8, 50u8, 107u8, 107u8, 201u8, 181u8, 235u8, 111u8, 185u8, 125u8, - 108u8, 204u8, 163u8, 11u8, 69u8, 236u8, 90u8, 232u8, 9u8, 225u8, 210u8, - 142u8, 175u8, 166u8, 159u8, 74u8, 112u8, 249u8, 16u8, 115u8, 144u8, - 177u8, + 116u8, 81u8, 48u8, 55u8, 199u8, 26u8, 100u8, 7u8, 177u8, 230u8, 132u8, + 248u8, 221u8, 90u8, 33u8, 155u8, 198u8, 216u8, 43u8, 149u8, 92u8, + 100u8, 199u8, 183u8, 150u8, 214u8, 199u8, 222u8, 224u8, 228u8, 238u8, + 108u8, ], ) } @@ -4919,6 +5191,23 @@ pub mod api { } } } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::pallet_bridge_grandpa::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Best finalized chain header has been updated to the header with given number and hash."] + pub struct UpdatedBestFinalizedHeader { + pub number: ::core::primitive::u64, + pub hash: ::bp_millau::MillauHash, + } + impl ::subxt::events::StaticEvent for UpdatedBestFinalizedHeader { + const PALLET: &'static str = "BridgeMillauGrandpa"; + const EVENT: &'static str = "UpdatedBestFinalizedHeader"; + } + } pub mod storage { use super::runtime_types; pub struct StorageApi; @@ -5243,23 +5532,6 @@ pub mod api { ], ) } - #[doc = " Max number of authorities at the bridged chain."] - pub fn max_bridged_authorities( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - > { - ::subxt::constants::StaticConstantAddress::new( - "BridgeMillauGrandpa", - "MaxBridgedAuthorities", - [ - 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, - 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, - 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, - 145u8, - ], - ) - } } } } @@ -5405,9 +5677,7 @@ pub mod api { :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, )] #[doc = "Messages have been received from the bridged chain."] - pub struct MessagesReceived( - pub ::std::vec::Vec>, - ); + pub struct MessagesReceived (pub :: std :: vec :: Vec < runtime_types :: bp_messages :: ReceivedMessages < runtime_types :: bridge_runtime_common :: messages_xcm_extension :: XcmBlobMessageDispatchResult > > ,) ; impl ::subxt::events::StaticEvent for MessagesReceived { const PALLET: &'static str = "BridgeMillauMessages"; const EVENT: &'static str = "MessagesReceived"; @@ -5584,7 +5854,7 @@ pub mod api { _0: impl ::std::borrow::Borrow, ) -> ::subxt::storage::address::StaticStorageAddress< ::subxt::metadata::DecodeStaticType< - runtime_types::sp_core::bounded::bounded_vec::BoundedVec< + runtime_types::bounded_collections::bounded_vec::BoundedVec< ::core::primitive::u8, >, >, @@ -5611,7 +5881,7 @@ pub mod api { &self, ) -> ::subxt::storage::address::StaticStorageAddress< ::subxt::metadata::DecodeStaticType< - runtime_types::sp_core::bounded::bounded_vec::BoundedVec< + runtime_types::bounded_collections::bounded_vec::BoundedVec< ::core::primitive::u8, >, >, @@ -5671,9 +5941,32 @@ pub mod api { } } } - } - pub mod runtime_types { - use super::runtime_types; + } + pub mod runtime_types { + use super::runtime_types; + pub mod bounded_collections { + use super::runtime_types; + pub mod bounded_vec { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct BoundedVec<_0>(pub ::std::vec::Vec<_0>); + } + pub mod weak_bounded_vec { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct WeakBoundedVec<_0>(pub ::std::vec::Vec<_0>); + } + } pub mod bp_header_chain { use super::runtime_types; #[derive( @@ -5760,6 +6053,26 @@ pub mod api { pub messages: runtime_types::bp_messages::DeliveredMessages, } } + pub mod bp_relayers { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum RewardsAccountOwner { + #[codec(index = 0)] + ThisChain, + #[codec(index = 1)] + BridgedChain, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct RewardsAccountParams { + pub lane_id: runtime_types::bp_messages::LaneId, + pub bridged_chain_id: [::core::primitive::u8; 4usize], + pub owner: runtime_types::bp_relayers::RewardsAccountOwner, + } + } pub mod bp_runtime { use super::runtime_types; pub mod messages { @@ -5796,6 +6109,26 @@ pub mod api { Halted, } } + pub mod bridge_runtime_common { + use super::runtime_types; + pub mod messages_xcm_extension { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum XcmBlobMessageDispatchResult { + #[codec(index = 0)] + InvalidPayload, + #[codec(index = 1)] + Dispatched, + #[codec(index = 2)] + NotDispatched, + } + } + } pub mod cumulus_pallet_dmp_queue { use super::runtime_types; pub mod pallet { @@ -5870,6 +6203,9 @@ pub mod api { overweight_index: ::core::primitive::u64, weight_used: ::sp_weights::Weight, }, + #[codec(index = 6)] + #[doc = "The maximum number of downward messages was."] + MaxMessagesExhausted { message_id: [::core::primitive::u8; 32usize] }, } } #[derive( @@ -5899,7 +6235,7 @@ pub mod api { )] #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] pub enum Call { - # [codec (index = 0)] # [doc = "Set the current validation data."] # [doc = ""] # [doc = "This should be invoked exactly once per block. It will panic at the finalization"] # [doc = "phase if the call was not invoked."] # [doc = ""] # [doc = "The dispatch origin for this call must be `Inherent`"] # [doc = ""] # [doc = "As a side effect, this function upgrades the current validation function"] # [doc = "if the appropriate time has come."] set_validation_data { data : runtime_types :: cumulus_primitives_parachain_inherent :: ParachainInherentData , } , # [codec (index = 1)] sudo_send_upward_message { message : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , # [codec (index = 2)] authorize_upgrade { code_hash : :: subxt :: utils :: H256 , } , # [codec (index = 3)] enact_authorized_upgrade { code : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , } + # [codec (index = 0)] # [doc = "Set the current validation data."] # [doc = ""] # [doc = "This should be invoked exactly once per block. It will panic at the finalization"] # [doc = "phase if the call was not invoked."] # [doc = ""] # [doc = "The dispatch origin for this call must be `Inherent`"] # [doc = ""] # [doc = "As a side effect, this function upgrades the current validation function"] # [doc = "if the appropriate time has come."] set_validation_data { data : runtime_types :: cumulus_primitives_parachain_inherent :: ParachainInherentData , } , # [codec (index = 1)] sudo_send_upward_message { message : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , # [codec (index = 2)] # [doc = "Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied"] # [doc = "later."] # [doc = ""] # [doc = "The `check_version` parameter sets a boolean flag for whether or not the runtime's spec"] # [doc = "version and name should be verified on upgrade. Since the authorization only has a hash,"] # [doc = "it cannot actually perform the verification."] # [doc = ""] # [doc = "This call requires Root origin."] authorize_upgrade { code_hash : :: subxt :: utils :: H256 , check_version : :: core :: primitive :: bool , } , # [codec (index = 3)] # [doc = "Provide the preimage (runtime binary) `code` for an upgrade that has been authorized."] # [doc = ""] # [doc = "If the authorization required a version check, this call will ensure the spec name"] # [doc = "remains unchanged and that the spec version has increased."] # [doc = ""] # [doc = "Note that this function will not apply the new `code`, but only attempt to schedule the"] # [doc = "upgrade with the Relay Chain."] # [doc = ""] # [doc = "All origins are allowed."] enact_authorized_upgrade { code : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , } #[derive( :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, @@ -5909,20 +6245,20 @@ pub mod api { #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] pub enum Error { #[codec(index = 0)] - #[doc = "Attempt to upgrade validation function while existing upgrade pending"] + #[doc = "Attempt to upgrade validation function while existing upgrade pending."] OverlappingUpgrades, #[codec(index = 1)] - #[doc = "Polkadot currently prohibits this parachain from upgrading its validation function"] + #[doc = "Polkadot currently prohibits this parachain from upgrading its validation function."] ProhibitedByPolkadot, #[codec(index = 2)] #[doc = "The supplied validation function has compiled into a blob larger than Polkadot is"] - #[doc = "willing to run"] + #[doc = "willing to run."] TooBig, #[codec(index = 3)] - #[doc = "The inherent which supplies the validation data did not run this block"] + #[doc = "The inherent which supplies the validation data did not run this block."] ValidationDataNotAvailable, #[codec(index = 4)] - #[doc = "The inherent which supplies the host configuration did not run this block"] + #[doc = "The inherent which supplies the host configuration did not run this block."] HostConfigurationNotAvailable, #[codec(index = 5)] #[doc = "No validation function upgrade is currently scheduled."] @@ -5983,14 +6319,21 @@ pub mod api { pub relay_dispatch_queue_size: (::core::primitive::u32, ::core::primitive::u32), pub ingress_channels: ::std::vec::Vec<( runtime_types::polkadot_parachain::primitives::Id, - runtime_types::polkadot_primitives::v2::AbridgedHrmpChannel, + runtime_types::polkadot_primitives::v4::AbridgedHrmpChannel, )>, pub egress_channels: ::std::vec::Vec<( runtime_types::polkadot_parachain::primitives::Id, - runtime_types::polkadot_primitives::v2::AbridgedHrmpChannel, + runtime_types::polkadot_primitives::v4::AbridgedHrmpChannel, )>, } } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct CodeUpgradeAuthorization { + pub code_hash: ::subxt::utils::H256, + pub check_version: ::core::primitive::bool, + } } pub mod cumulus_pallet_xcm { use super::runtime_types; @@ -6257,7 +6600,7 @@ pub mod api { )] pub struct ParachainInherentData { pub validation_data: - runtime_types::polkadot_primitives::v2::PersistedValidationData< + runtime_types::polkadot_primitives::v4::PersistedValidationData< ::subxt::utils::H256, ::core::primitive::u32, >, @@ -6507,9 +6850,8 @@ pub mod api { #[codec(index = 0)] #[doc = "Make some on-chain remark."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- `O(1)`"] - #[doc = "# "] remark { remark: ::std::vec::Vec<::core::primitive::u8> }, #[codec(index = 1)] #[doc = "Set the number of pages in the WebAssembly environment's heap."] @@ -6517,27 +6859,14 @@ pub mod api { #[codec(index = 2)] #[doc = "Set the new runtime code."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code`"] - #[doc = "- 1 call to `can_set_code`: `O(S)` (calls `sp_io::misc::runtime_version` which is"] - #[doc = " expensive)."] - #[doc = "- 1 storage write (codec `O(C)`)."] - #[doc = "- 1 digest item."] - #[doc = "- 1 event."] - #[doc = "The weight of this function is dependent on the runtime, but generally this is very"] - #[doc = "expensive. We will treat this as a full block."] - #[doc = "# "] set_code { code: ::std::vec::Vec<::core::primitive::u8> }, #[codec(index = 3)] #[doc = "Set the new runtime code without doing any checks of the given `code`."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- `O(C)` where `C` length of `code`"] - #[doc = "- 1 storage write (codec `O(C)`)."] - #[doc = "- 1 digest item."] - #[doc = "- 1 event."] - #[doc = "The weight of this function is dependent on the runtime. We will treat this as a full"] - #[doc = "block. # "] set_code_without_checks { code: ::std::vec::Vec<::core::primitive::u8> }, #[codec(index = 4)] #[doc = "Set some items of storage."] @@ -6680,57 +7009,33 @@ pub mod api { #[codec(index = 0)] #[doc = "Transfer some liquid free balance to another account."] #[doc = ""] - #[doc = "`transfer` will set the `FreeBalance` of the sender and receiver."] + #[doc = "`transfer_allow_death` will set the `FreeBalance` of the sender and receiver."] #[doc = "If the sender's account is below the existential deposit as a result"] #[doc = "of the transfer, the account will be reaped."] #[doc = ""] #[doc = "The dispatch origin for this call must be `Signed` by the transactor."] - #[doc = ""] - #[doc = "# "] - #[doc = "- Dependent on arguments but not critical, given proper implementations for input config"] - #[doc = " types. See related functions below."] - #[doc = "- It contains a limited number of reads and writes internally and no complex"] - #[doc = " computation."] - #[doc = ""] - #[doc = "Related functions:"] - #[doc = ""] - #[doc = " - `ensure_can_withdraw` is always called internally but has a bounded complexity."] - #[doc = " - Transferring balances to accounts that did not exist before will cause"] - #[doc = " `T::OnNewAccount::on_new_account` to be called."] - #[doc = " - Removing enough funds from an account will trigger `T::DustRemoval::on_unbalanced`."] - #[doc = " - `transfer_keep_alive` works the same way as `transfer`, but has an additional check"] - #[doc = " that the transfer will not kill the origin account."] - #[doc = "---------------------------------"] - #[doc = "- Origin account is already in memory, so no DB operations for them."] - #[doc = "# "] - transfer { + transfer_allow_death { dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, #[codec(compact)] value: ::core::primitive::u128, }, #[codec(index = 1)] - #[doc = "Set the balances of a given account."] - #[doc = ""] - #[doc = "This will alter `FreeBalance` and `ReservedBalance` in storage. it will"] - #[doc = "also alter the total issuance of the system (`TotalIssuance`) appropriately."] - #[doc = "If the new free or reserved balance is below the existential deposit,"] - #[doc = "it will reset the account nonce (`frame_system::AccountNonce`)."] + #[doc = "Set the regular balance of a given account; it also takes a reserved balance but this"] + #[doc = "must be the same as the account's current reserved balance."] #[doc = ""] #[doc = "The dispatch origin for this call is `root`."] - set_balance { + #[doc = ""] + #[doc = "WARNING: This call is DEPRECATED! Use `force_set_balance` instead."] + set_balance_deprecated { who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, #[codec(compact)] new_free: ::core::primitive::u128, #[codec(compact)] - new_reserved: ::core::primitive::u128, + old_reserved: ::core::primitive::u128, }, #[codec(index = 2)] - #[doc = "Exactly as `transfer`, except the origin must be root and the source account may be"] - #[doc = "specified."] - #[doc = "# "] - #[doc = "- Same as transfer, but additional read and write because the source account is not"] - #[doc = " assumed to be in the overlay."] - #[doc = "# "] + #[doc = "Exactly as `transfer_allow_death`, except the origin must be root and the source account"] + #[doc = "may be specified."] force_transfer { source: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, @@ -6738,12 +7043,12 @@ pub mod api { value: ::core::primitive::u128, }, #[codec(index = 3)] - #[doc = "Same as the [`transfer`] call, but with a check that the transfer will not kill the"] - #[doc = "origin account."] + #[doc = "Same as the [`transfer_allow_death`] call, but with a check that the transfer will not"] + #[doc = "kill the origin account."] #[doc = ""] - #[doc = "99% of the time you want [`transfer`] instead."] + #[doc = "99% of the time you want [`transfer_allow_death`] instead."] #[doc = ""] - #[doc = "[`transfer`]: struct.Pallet.html#method.transfer"] + #[doc = "[`transfer_allow_death`]: struct.Pallet.html#method.transfer"] transfer_keep_alive { dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, #[codec(compact)] @@ -6764,9 +7069,7 @@ pub mod api { #[doc = "- `keep_alive`: A boolean to determine if the `transfer_all` operation should send all"] #[doc = " of the funds the account has, causing the sender account to be killed (false), or"] #[doc = " transfer everything except at least the existential deposit, which will guarantee to"] - #[doc = " keep the sender account alive (true). # "] - #[doc = "- O(1). Just like transfer, but reading the user's transferable balance first."] - #[doc = " #"] + #[doc = " keep the sender account alive (true)."] transfer_all { dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, keep_alive: ::core::primitive::bool, @@ -6779,6 +7082,34 @@ pub mod api { who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, amount: ::core::primitive::u128, }, + #[codec(index = 6)] + #[doc = "Upgrade a specified account."] + #[doc = ""] + #[doc = "- `origin`: Must be `Signed`."] + #[doc = "- `who`: The account to be upgraded."] + #[doc = ""] + #[doc = "This will waive the transaction fee if at least all but 10% of the accounts needed to"] + #[doc = "be upgraded. (We let some not have to be upgraded just in order to allow for the"] + #[doc = "possibililty of churn)."] + upgrade_accounts { who: ::std::vec::Vec<::sp_core::crypto::AccountId32> }, + #[codec(index = 7)] + #[doc = "Alias for `transfer_allow_death`, provided only for name-wise compatibility."] + #[doc = ""] + #[doc = "WARNING: DEPRECATED! Will be released in approximately 3 months."] + transfer { + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + value: ::core::primitive::u128, + }, + #[codec(index = 8)] + #[doc = "Set the regular balance of a given account."] + #[doc = ""] + #[doc = "The dispatch origin for this call is `root`."] + force_set_balance { + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + new_free: ::core::primitive::u128, + }, } #[derive( :: subxt :: ext :: codec :: Decode, @@ -6789,29 +7120,35 @@ pub mod api { #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] pub enum Error { #[codec(index = 0)] - #[doc = "Vesting balance too high to send value"] + #[doc = "Vesting balance too high to send value."] VestingBalance, #[codec(index = 1)] - #[doc = "Account liquidity restrictions prevent withdrawal"] + #[doc = "Account liquidity restrictions prevent withdrawal."] LiquidityRestrictions, #[codec(index = 2)] #[doc = "Balance too low to send value."] InsufficientBalance, #[codec(index = 3)] - #[doc = "Value too low to create account due to existential deposit"] + #[doc = "Value too low to create account due to existential deposit."] ExistentialDeposit, #[codec(index = 4)] - #[doc = "Transfer/payment would kill account"] - KeepAlive, + #[doc = "Transfer/payment would kill account."] + Expendability, #[codec(index = 5)] - #[doc = "A vesting schedule already exists for this account"] + #[doc = "A vesting schedule already exists for this account."] ExistingVestingSchedule, #[codec(index = 6)] - #[doc = "Beneficiary account must pre-exist"] + #[doc = "Beneficiary account must pre-exist."] DeadAccount, #[codec(index = 7)] - #[doc = "Number of named reserves exceed MaxReserves"] + #[doc = "Number of named reserves exceed `MaxReserves`."] TooManyReserves, + #[codec(index = 8)] + #[doc = "Number of holds exceed `MaxHolds`."] + TooManyHolds, + #[codec(index = 9)] + #[doc = "Number of freezes exceed `MaxFreezes`."] + TooManyFreezes, } #[derive( :: subxt :: ext :: codec :: Decode, @@ -6846,7 +7183,6 @@ pub mod api { BalanceSet { who: ::sp_core::crypto::AccountId32, free: ::core::primitive::u128, - reserved: ::core::primitive::u128, }, #[codec(index = 4)] #[doc = "Some balance was reserved (moved from free to reserved)."] @@ -6882,42 +7218,102 @@ pub mod api { #[codec(index = 9)] #[doc = "Some amount was removed from the account (e.g. for misbehavior)."] Slashed { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, + #[codec(index = 10)] + #[doc = "Some amount was minted into an account."] + Minted { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, + #[codec(index = 11)] + #[doc = "Some amount was burned from an account."] + Burned { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, + #[codec(index = 12)] + #[doc = "Some amount was suspended from an account (it can be restored later)."] + Suspended { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 13)] + #[doc = "Some amount was restored into an account."] + Restored { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 14)] + #[doc = "An account was upgraded."] + Upgraded { who: ::sp_core::crypto::AccountId32 }, + #[codec(index = 15)] + #[doc = "Total issuance was increased by `amount`, creating a credit to be balanced."] + Issued { amount: ::core::primitive::u128 }, + #[codec(index = 16)] + #[doc = "Total issuance was decreased by `amount`, creating a debt to be balanced."] + Rescinded { amount: ::core::primitive::u128 }, } } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct AccountData<_0> { - pub free: _0, - pub reserved: _0, - pub misc_frozen: _0, - pub fee_frozen: _0, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct BalanceLock<_0> { - pub id: [::core::primitive::u8; 8usize], - pub amount: _0, - pub reasons: runtime_types::pallet_balances::Reasons, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub enum Reasons { - #[codec(index = 0)] - Fee, - #[codec(index = 1)] - Misc, - #[codec(index = 2)] - All, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ReserveData<_0, _1> { - pub id: _0, - pub amount: _1, + pub mod types { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct AccountData<_0> { + pub free: _0, + pub reserved: _0, + pub frozen: _0, + pub flags: runtime_types::pallet_balances::types::ExtraFlags, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct BalanceLock<_0> { + pub id: [::core::primitive::u8; 8usize], + pub amount: _0, + pub reasons: runtime_types::pallet_balances::types::Reasons, + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct ExtraFlags(pub ::core::primitive::u128); + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct IdAmount<_0, _1> { + pub id: _0, + pub amount: _1, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Reasons { + #[codec(index = 0)] + Fee, + #[codec(index = 1)] + Misc, + #[codec(index = 2)] + All, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct ReserveData<_0, _1> { + pub id: _0, + pub amount: _1, + } } } pub mod pallet_bridge_grandpa { @@ -7023,6 +7419,21 @@ pub mod api { #[doc = "Error generated by the `OwnedBridgeModule` trait."] BridgeModule(runtime_types::bp_runtime::OwnedBridgeModuleError), } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "Best finalized chain header has been updated to the header with given number and hash."] + UpdatedBestFinalizedHeader { + number: ::core::primitive::u64, + hash: ::bp_millau::MillauHash, + }, + } } pub mod storage_types { use super::runtime_types; @@ -7033,7 +7444,7 @@ pub mod api { Debug, )] pub struct StoredAuthoritySet { - pub authorities: runtime_types::sp_core::bounded::bounded_vec::BoundedVec<( + pub authorities: runtime_types::bounded_collections::bounded_vec::BoundedVec<( runtime_types::sp_consensus_grandpa::app::Public, ::core::primitive::u64, )>, @@ -7118,24 +7529,7 @@ pub mod api { )] #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] pub enum Event { - #[codec(index = 0)] - #[doc = "Message has been accepted and is waiting to be delivered."] - MessageAccepted { - lane_id: runtime_types::bp_messages::LaneId, - nonce: ::core::primitive::u64, - }, - #[codec(index = 1)] - #[doc = "Messages have been received from the bridged chain."] - MessagesReceived( - ::std::vec::Vec>, - ), - #[codec(index = 2)] - #[doc = "Messages in the inclusive range have been delivered to the bridged chain."] - MessagesDelivered { - lane_id: runtime_types::bp_messages::LaneId, - messages: runtime_types::bp_messages::DeliveredMessages, - }, - } + # [codec (index = 0)] # [doc = "Message has been accepted and is waiting to be delivered."] MessageAccepted { lane_id : runtime_types :: bp_messages :: LaneId , nonce : :: core :: primitive :: u64 , } , # [codec (index = 1)] # [doc = "Messages have been received from the bridged chain."] MessagesReceived (:: std :: vec :: Vec < runtime_types :: bp_messages :: ReceivedMessages < runtime_types :: bridge_runtime_common :: messages_xcm_extension :: XcmBlobMessageDispatchResult > > ,) , # [codec (index = 2)] # [doc = "Messages in the inclusive range have been delivered to the bridged chain."] MessagesDelivered { lane_id : runtime_types :: bp_messages :: LaneId , messages : runtime_types :: bp_messages :: DeliveredMessages , } , } } } pub mod pallet_bridge_relayers { @@ -7152,7 +7546,9 @@ pub mod api { pub enum Call { #[codec(index = 0)] #[doc = "Claim accumulated rewards."] - claim_rewards { lane_id: runtime_types::bp_messages::LaneId }, + claim_rewards { + rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, + }, } #[derive( :: subxt :: ext :: codec :: Decode, @@ -7181,7 +7577,7 @@ pub mod api { #[doc = "Reward has been paid to the relayer."] RewardPaid { relayer: ::sp_core::crypto::AccountId32, - lane_id: runtime_types::bp_messages::LaneId, + rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, reward: ::core::primitive::u128, }, } @@ -7204,12 +7600,8 @@ pub mod api { #[doc = ""] #[doc = "The dispatch origin for this call must be _Signed_."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- O(1)."] - #[doc = "- Limited storage reads."] - #[doc = "- One DB write (event)."] - #[doc = "- Weight of derivative `call` execution + 10,000."] - #[doc = "# "] sudo { call: ::std::boxed::Box, @@ -7221,10 +7613,8 @@ pub mod api { #[doc = ""] #[doc = "The dispatch origin for this call must be _Signed_."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- O(1)."] - #[doc = "- The weight of this call is defined by the caller."] - #[doc = "# "] sudo_unchecked_weight { call: ::std::boxed::Box, @@ -7236,11 +7626,8 @@ pub mod api { #[doc = ""] #[doc = "The dispatch origin for this call must be _Signed_."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- O(1)."] - #[doc = "- Limited storage reads."] - #[doc = "- One DB change."] - #[doc = "# "] set_key { new: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, }, @@ -7250,12 +7637,8 @@ pub mod api { #[doc = ""] #[doc = "The dispatch origin for this call must be _Signed_."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- O(1)."] - #[doc = "- Limited storage reads."] - #[doc = "- One DB write (event)."] - #[doc = "- Weight of derivative `call` execution + 10,000."] - #[doc = "# "] sudo_as { who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, call: @@ -7325,12 +7708,11 @@ pub mod api { #[doc = ""] #[doc = "The dispatch origin for this call must be `Inherent`."] #[doc = ""] - #[doc = "# "] + #[doc = "## Complexity"] #[doc = "- `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`)"] #[doc = "- 1 storage read and 1 storage mutation (codec `O(1)`). (because of `DidUpdate::take` in"] #[doc = " `on_finalize`)"] #[doc = "- 1 event handler `on_timestamp_set`. Must be `O(1)`."] - #[doc = "# "] set { #[codec(compact)] now: ::core::primitive::u64, @@ -7907,7 +8289,7 @@ pub mod api { } pub mod polkadot_primitives { use super::runtime_types; - pub mod v2 { + pub mod v4 { use super::runtime_types; #[derive( :: subxt :: ext :: codec :: Decode, @@ -7969,6 +8351,14 @@ pub mod api { #[derive( :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, )] + pub struct BridgeRejectObsoleteHeadersAndMessages; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct DummyBridgeRefundMillauMessages; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] pub struct Runtime; #[derive( :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, @@ -8007,7 +8397,7 @@ pub mod api { System(runtime_types::frame_system::pallet::Event), #[codec(index = 2)] Sudo(runtime_types::pallet_sudo::pallet::Event), - #[codec(index = 4)] + #[codec(index = 3)] TransactionPayment(runtime_types::pallet_transaction_payment::pallet::Event), #[codec(index = 20)] ParachainSystem(runtime_types::cumulus_pallet_parachain_system::pallet::Event), @@ -8023,6 +8413,8 @@ pub mod api { DmpQueue(runtime_types::cumulus_pallet_dmp_queue::pallet::Event), #[codec(index = 54)] BridgeRelayers(runtime_types::pallet_bridge_relayers::pallet::Event), + #[codec(index = 55)] + BridgeMillauGrandpa(runtime_types::pallet_bridge_grandpa::pallet::Event), #[codec(index = 56)] BridgeMillauMessages(runtime_types::pallet_bridge_messages::pallet::Event), } @@ -8052,32 +8444,9 @@ pub mod api { DivisionByZero, } } - pub mod sp_core { + pub mod sp_consensus_grandpa { use super::runtime_types; - pub mod bounded { - use super::runtime_types; - pub mod bounded_vec { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - pub struct BoundedVec<_0>(pub ::std::vec::Vec<_0>); - } - pub mod weak_bounded_vec { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - pub struct WeakBoundedVec<_0>(pub ::std::vec::Vec<_0>); - } - } - pub mod ecdsa { + pub mod app { use super::runtime_types; #[derive( :: subxt :: ext :: codec :: Decode, @@ -8085,26 +8454,29 @@ pub mod api { Clone, Debug, )] - pub struct Signature(pub [::core::primitive::u8; 65usize]); - } - pub mod ed25519 { - use super::runtime_types; + pub struct Public(pub runtime_types::sp_core::ed25519::Public); #[derive( :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, )] - pub struct Public(pub [::core::primitive::u8; 32usize]); + pub struct Signature(pub runtime_types::sp_core::ed25519::Signature); + } + } + pub mod sp_core { + use super::runtime_types; + pub mod ecdsa { + use super::runtime_types; #[derive( :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, )] - pub struct Signature(pub [::core::primitive::u8; 64usize]); + pub struct Signature(pub [::core::primitive::u8; 65usize]); } - pub mod sr25519 { + pub mod ed25519 { use super::runtime_types; #[derive( :: subxt :: ext :: codec :: Decode, @@ -8112,27 +8484,24 @@ pub mod api { Clone, Debug, )] - pub struct Signature(pub [::core::primitive::u8; 64usize]); - } - } - pub mod sp_consensus_grandpa { - use super::runtime_types; - pub mod app { - use super::runtime_types; + pub struct Public(pub [::core::primitive::u8; 32usize]); #[derive( :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, )] - pub struct Public(pub runtime_types::sp_core::ed25519::Public); + pub struct Signature(pub [::core::primitive::u8; 64usize]); + } + pub mod sr25519 { + use super::runtime_types; #[derive( :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, )] - pub struct Signature(pub runtime_types::sp_core::ed25519::Signature); + pub struct Signature(pub [::core::primitive::u8; 64usize]); } } pub mod sp_runtime { @@ -8237,9 +8606,9 @@ pub mod api { )] pub enum TokenError { #[codec(index = 0)] - NoFunds, + FundsUnavailable, #[codec(index = 1)] - WouldDie, + OnlyProvider, #[codec(index = 2)] BelowMinimum, #[codec(index = 3)] @@ -8250,6 +8619,10 @@ pub mod api { Frozen, #[codec(index = 6)] Unsupported, + #[codec(index = 7)] + CannotCreateHold, + #[codec(index = 8)] + NotExpendable, } #[derive( :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, @@ -8352,7 +8725,7 @@ pub mod api { GeneralIndex(#[codec(compact)] ::core::primitive::u128), #[codec(index = 6)] GeneralKey( - runtime_types::sp_core::bounded::weak_bounded_vec::WeakBoundedVec< + runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< ::core::primitive::u8, >, ), @@ -8627,7 +9000,7 @@ pub mod api { Unit, #[codec(index = 1)] Named( - runtime_types::sp_core::bounded::weak_bounded_vec::WeakBoundedVec< + runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< ::core::primitive::u8, >, ), @@ -8840,7 +9213,7 @@ pub mod api { Any, #[codec(index = 1)] Named( - runtime_types::sp_core::bounded::weak_bounded_vec::WeakBoundedVec< + runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< ::core::primitive::u8, >, ), @@ -9007,7 +9380,10 @@ pub mod api { #[codec(index = 5)] GeneralIndex(#[codec(compact)] ::core::primitive::u128), #[codec(index = 6)] - GeneralKey([::core::primitive::u8; 32usize]), + GeneralKey { + length: ::core::primitive::u8, + data: [::core::primitive::u8; 32usize], + }, #[codec(index = 7)] OnlyChild, #[codec(index = 8)] @@ -9580,9 +9956,17 @@ pub mod api { #[codec(index = 0)] Success, #[codec(index = 1)] - Error(::std::vec::Vec<::core::primitive::u8>), + Error( + runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + ), #[codec(index = 2)] - TruncatedError(::std::vec::Vec<::core::primitive::u8>), + TruncatedError( + runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + ), } #[derive( :: subxt :: ext :: codec :: Decode, @@ -9593,8 +9977,12 @@ pub mod api { pub struct PalletInfo { #[codec(compact)] pub index: ::core::primitive::u32, - pub name: ::std::vec::Vec<::core::primitive::u8>, - pub module_name: ::std::vec::Vec<::core::primitive::u8>, + pub name: runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + pub module_name: runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, #[codec(compact)] pub major: ::core::primitive::u32, #[codec(compact)] @@ -9635,7 +10023,11 @@ pub mod api { #[codec(index = 3)] Version(::core::primitive::u32), #[codec(index = 4)] - PalletsInfo(runtime_types::xcm::v3::VecPalletInfo), + PalletsInfo( + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::xcm::v3::PalletInfo, + >, + ), #[codec(index = 5)] DispatchResult(runtime_types::xcm::v3::MaybeErrorCode), } @@ -9645,13 +10037,6 @@ pub mod api { Clone, Debug, )] - pub struct VecPalletInfo(pub ::std::vec::Vec); - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] pub enum WeightLimit { #[codec(index = 0)] Unlimited, @@ -9670,18 +10055,18 @@ pub mod api { :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, )] pub enum VersionedMultiAssets { - #[codec(index = 0)] - V2(runtime_types::xcm::v2::multiasset::MultiAssets), #[codec(index = 1)] + V2(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 3)] V3(runtime_types::xcm::v3::multiasset::MultiAssets), } #[derive( :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, )] pub enum VersionedMultiLocation { - #[codec(index = 0)] - V2(runtime_types::xcm::v2::multilocation::MultiLocation), #[codec(index = 1)] + V2(runtime_types::xcm::v2::multilocation::MultiLocation), + #[codec(index = 3)] V3(runtime_types::xcm::v3::multilocation::MultiLocation), } #[derive( @@ -9739,11 +10124,6 @@ pub mod api { pub fn sudo(&self) -> sudo::storage::StorageApi { sudo::storage::StorageApi } - pub fn randomness_collective_flip( - &self, - ) -> randomness_collective_flip::storage::StorageApi { - randomness_collective_flip::storage::StorageApi - } pub fn transaction_payment(&self) -> transaction_payment::storage::StorageApi { transaction_payment::storage::StorageApi } @@ -9818,9 +10198,9 @@ pub mod api { let runtime_metadata_hash = client.metadata().metadata_hash(&PALLETS); if runtime_metadata_hash != [ - 29u8, 217u8, 238u8, 58u8, 9u8, 201u8, 174u8, 206u8, 19u8, 133u8, 133u8, 32u8, - 139u8, 251u8, 132u8, 7u8, 135u8, 118u8, 231u8, 231u8, 9u8, 150u8, 246u8, 171u8, - 190u8, 93u8, 114u8, 63u8, 86u8, 200u8, 148u8, 126u8, + 244u8, 26u8, 245u8, 96u8, 241u8, 56u8, 22u8, 163u8, 219u8, 209u8, 103u8, 161u8, + 138u8, 242u8, 33u8, 114u8, 162u8, 107u8, 1u8, 216u8, 115u8, 116u8, 164u8, 126u8, + 81u8, 51u8, 88u8, 36u8, 180u8, 113u8, 18u8, 58u8, ] { Err(::subxt::error::MetadataError::IncompatibleMetadata) } else { diff --git a/relays/client-rialto-parachain/src/lib.rs b/relays/client-rialto-parachain/src/lib.rs index 27673dc23be..ddb54fbefc0 100644 --- a/relays/client-rialto-parachain/src/lib.rs +++ b/relays/client-rialto-parachain/src/lib.rs @@ -18,8 +18,8 @@ pub mod codegen_runtime; +use bp_bridge_hub_cumulus::BridgeHubSignedExtension; use bp_messages::MessageNonce; -use bp_polkadot_core::PolkadotSignedExtension; use bp_runtime::ChainId; use codec::Encode; use relay_substrate_client::{ @@ -51,8 +51,6 @@ impl UnderlyingChainProvider for RialtoParachain { impl Chain for RialtoParachain { const ID: ChainId = bp_runtime::RIALTO_PARACHAIN_CHAIN_ID; const NAME: &'static str = "RialtoParachain"; - // RialtoParachain token has no value, but we associate it with DOT token - const TOKEN_ID: Option<&'static str> = Some("polkadot"); const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = bp_rialto_parachain::BEST_FINALIZED_RIALTO_PARACHAIN_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); diff --git a/relays/client-rialto/src/lib.rs b/relays/client-rialto/src/lib.rs index 7fe735b4adc..f8f696068fe 100644 --- a/relays/client-rialto/src/lib.rs +++ b/relays/client-rialto/src/lib.rs @@ -42,8 +42,6 @@ impl UnderlyingChainProvider for Rialto { impl Chain for Rialto { const ID: ChainId = bp_runtime::RIALTO_CHAIN_ID; const NAME: &'static str = "Rialto"; - // Rialto token has no value, but we associate it with DOT token - const TOKEN_ID: Option<&'static str> = Some("polkadot"); const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); diff --git a/relays/client-rococo/src/lib.rs b/relays/client-rococo/src/lib.rs index 5049778a542..ede1bf1ded4 100644 --- a/relays/client-rococo/src/lib.rs +++ b/relays/client-rococo/src/lib.rs @@ -38,7 +38,6 @@ impl UnderlyingChainProvider for Rococo { impl Chain for Rococo { const ID: ChainId = bp_runtime::ROCOCO_CHAIN_ID; const NAME: &'static str = "Rococo"; - const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); diff --git a/relays/client-substrate/Cargo.toml b/relays/client-substrate/Cargo.toml index 1a80a54f965..ce83817a995 100644 --- a/relays/client-substrate/Cargo.toml +++ b/relays/client-substrate/Cargo.toml @@ -16,7 +16,7 @@ num-traits = "0.2" rand = "0.8" scale-info = { version = "2.1.1", features = ["derive"] } tokio = { version = "1.26", features = ["rt-multi-thread"] } -thiserror = "1.0.39" +thiserror = "1.0.40" # Bridge dependencies diff --git a/relays/client-substrate/src/chain.rs b/relays/client-substrate/src/chain.rs index 6ab353921ef..8c7dc00aa67 100644 --- a/relays/client-substrate/src/chain.rs +++ b/relays/client-substrate/src/chain.rs @@ -39,11 +39,6 @@ pub trait Chain: ChainBase + Clone { const ID: ChainId; /// Chain name. const NAME: &'static str; - /// Identifier of the basic token of the chain (if applicable). - /// - /// This identifier is used to fetch token price. In case of testnets, you may either - /// set it to `None`, or associate testnet with one of the existing tokens. - const TOKEN_ID: Option<&'static str>; /// Name of the runtime API method that is returning best known finalized header number /// and hash (as tuple). /// diff --git a/relays/client-substrate/src/client.rs b/relays/client-substrate/src/client.rs index 6a381c4e977..27af7d7d77f 100644 --- a/relays/client-substrate/src/client.rs +++ b/relays/client-substrate/src/client.rs @@ -739,12 +739,12 @@ impl Client { async fn jsonrpsee_execute(&self, make_jsonrpsee_future: MF) -> Result where MF: FnOnce(Arc) -> F + Send + 'static, - F: Future> + Send, + F: Future> + Send + 'static, T: Send + 'static, { let data = self.data.read().await; let client = data.client.clone(); - data.tokio.spawn(async move { make_jsonrpsee_future(client).await }).await? + data.tokio.spawn(make_jsonrpsee_future(client)).await? } /// Returns `true` if version guard can be started. diff --git a/relays/client-substrate/src/test_chain.rs b/relays/client-substrate/src/test_chain.rs index d33cb1de681..64c7590ee52 100644 --- a/relays/client-substrate/src/test_chain.rs +++ b/relays/client-substrate/src/test_chain.rs @@ -53,7 +53,6 @@ impl bp_runtime::Chain for TestChain { impl Chain for TestChain { const ID: ChainId = *b"test"; const NAME: &'static str = "Test"; - const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "TestMethod"; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(0); @@ -108,7 +107,6 @@ impl bp_runtime::UnderlyingChainProvider for TestParachain { impl Chain for TestParachain { const ID: ChainId = *b"test"; const NAME: &'static str = "TestParachain"; - const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "TestParachainMethod"; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(0); diff --git a/relays/client-westend/src/lib.rs b/relays/client-westend/src/lib.rs index 6db8eaf95e4..6013bfad128 100644 --- a/relays/client-westend/src/lib.rs +++ b/relays/client-westend/src/lib.rs @@ -38,7 +38,6 @@ impl UnderlyingChainProvider for Westend { impl Chain for Westend { const ID: ChainId = bp_runtime::WESTEND_CHAIN_ID; const NAME: &'static str = "Westend"; - const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = bp_westend::BEST_FINALIZED_WESTEND_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); @@ -72,7 +71,6 @@ impl UnderlyingChainProvider for Westmint { impl Chain for Westmint { const ID: ChainId = bp_runtime::WESTMINT_CHAIN_ID; const NAME: &'static str = "Westmint"; - const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = bp_westend::BEST_FINALIZED_WESTMINT_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); diff --git a/relays/client-wococo/src/lib.rs b/relays/client-wococo/src/lib.rs index 582c34ef853..2f0d6b22f92 100644 --- a/relays/client-wococo/src/lib.rs +++ b/relays/client-wococo/src/lib.rs @@ -38,7 +38,6 @@ impl UnderlyingChainProvider for Wococo { impl Chain for Wococo { const ID: ChainId = bp_runtime::WOCOCO_CHAIN_ID; const NAME: &'static str = "Wococo"; - const TOKEN_ID: Option<&'static str> = None; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); diff --git a/relays/lib-substrate-relay/Cargo.toml b/relays/lib-substrate-relay/Cargo.toml index 9c336056dc6..17c75220f6d 100644 --- a/relays/lib-substrate-relay/Cargo.toml +++ b/relays/lib-substrate-relay/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] anyhow = "1.0" -thiserror = "1.0.39" +thiserror = "1.0.40" async-std = "1.9.0" async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.1.5" } @@ -24,7 +24,7 @@ bp-polkadot-core = { path = "../../primitives/polkadot-core" } bp-relayers = { path = "../../primitives/relayers" } bridge-runtime-common = { path = "../../bin/runtime-common" } -finality-grandpa = { version = "0.16.0" } +finality-grandpa = { version = "0.16.2" } finality-relay = { path = "../finality" } parachains-relay = { path = "../parachains" } relay-utils = { path = "../utils" } diff --git a/relays/lib-substrate-relay/src/lib.rs b/relays/lib-substrate-relay/src/lib.rs index 28c538b309a..37a4d602e59 100644 --- a/relays/lib-substrate-relay/src/lib.rs +++ b/relays/lib-substrate-relay/src/lib.rs @@ -91,18 +91,21 @@ impl TaggedAccount { } /// Batch call builder. -pub trait BatchCallBuilder: Send { +pub trait BatchCallBuilder: Clone + Send { /// Create batch call from given calls vector. fn build_batch_call(&self, _calls: Vec) -> Call; } /// Batch call builder constructor. -pub trait BatchCallBuilderConstructor { +pub trait BatchCallBuilderConstructor: Clone { + /// Call builder, used by this constructor. + type CallBuilder: BatchCallBuilder; /// Create a new instance of a batch call builder. - fn new_builder() -> Option>>; + fn new_builder() -> Option; } /// Batch call builder based on `pallet-utility`. +#[derive(Clone)] pub struct UtilityPalletBatchCallBuilder(PhantomData); impl BatchCallBuilder for UtilityPalletBatchCallBuilder @@ -118,14 +121,25 @@ impl BatchCallBuilderConstructor for UtilityPalletBatchCallBu where C: ChainWithUtilityPallet, { - fn new_builder() -> Option>> { - Some(Box::new(Self(Default::default()))) + type CallBuilder = Self; + + fn new_builder() -> Option { + Some(Self(Default::default())) } } -/// A `BatchCallBuilderConstructor` that always returns `None`. +// A `BatchCallBuilderConstructor` that always returns `None`. impl BatchCallBuilderConstructor for () { - fn new_builder() -> Option>> { + type CallBuilder = (); + fn new_builder() -> Option { None } } + +// Dummy `BatchCallBuilder` implementation that must never be used outside +// of the `impl BatchCallBuilderConstructor for ()` code. +impl BatchCallBuilder for () { + fn build_batch_call(&self, _calls: Vec) -> Call { + unreachable!("never called, because ()::new_builder() returns None; qed") + } +} diff --git a/relays/lib-substrate-relay/src/messages_lane.rs b/relays/lib-substrate-relay/src/messages_lane.rs index 0a7a3566d20..b86a2629b07 100644 --- a/relays/lib-substrate-relay/src/messages_lane.rs +++ b/relays/lib-substrate-relay/src/messages_lane.rs @@ -111,8 +111,9 @@ pub struct MessagesRelayParams { /// Batch transaction that brings headers + and messages delivery/receiving confirmations to the /// source node. +#[derive(Clone)] pub struct BatchProofTransaction>> { - builder: Box>>, + builder: B::CallBuilder, proved_header: HeaderIdOf, prove_calls: Vec>, @@ -120,6 +121,16 @@ pub struct BatchProofTransaction B>, } +impl>> std::fmt::Debug + for BatchProofTransaction +{ + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.debug_struct("BatchProofTransaction") + .field("proved_header", &self.proved_header) + .finish() + } +} + impl>> BatchProofTransaction { diff --git a/relays/messages/src/message_lane_loop.rs b/relays/messages/src/message_lane_loop.rs index 0533e51d5db..ba86f05ffd3 100644 --- a/relays/messages/src/message_lane_loop.rs +++ b/relays/messages/src/message_lane_loop.rs @@ -111,7 +111,7 @@ pub struct NoncesSubmitArtifacts { /// Batch transaction that already submit some headers and needs to be extended with /// messages/delivery proof before sending. -pub trait BatchTransaction: Send { +pub trait BatchTransaction: Debug + Send { /// Header that was required in the original call and which is bundled within this /// batch transaction. fn required_header_id(&self) -> HeaderId; @@ -121,7 +121,7 @@ pub trait BatchTransaction: Send { #[async_trait] pub trait SourceClient: RelayClient { /// Type of batch transaction that submits finality and message receiving proof. - type BatchTransaction: BatchTransaction>; + type BatchTransaction: BatchTransaction> + Clone; /// Transaction tracker to track submitted transactions. type TransactionTracker: TransactionTracker>; @@ -186,7 +186,7 @@ pub trait SourceClient: RelayClient { #[async_trait] pub trait TargetClient: RelayClient { /// Type of batch transaction that submits finality and messages proof. - type BatchTransaction: BatchTransaction>; + type BatchTransaction: BatchTransaction> + Clone; /// Transaction tracker to track submitted transactions. type TransactionTracker: TransactionTracker>; @@ -1212,6 +1212,9 @@ pub(crate) mod tests { original_data, Arc::new(|_| {}), Arc::new(move |data: &mut TestClientData| { + data.source_state.best_self = + HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.1 + 1); + data.source_state.best_finalized_self = data.source_state.best_self; if let Some(target_to_source_header_required) = data.target_to_source_header_required.take() { @@ -1223,6 +1226,10 @@ pub(crate) mod tests { }), Arc::new(|_| {}), Arc::new(move |data: &mut TestClientData| { + data.target_state.best_self = + HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1); + data.target_state.best_finalized_self = data.target_state.best_self; + if let Some(source_to_target_header_required) = data.source_to_target_header_required.take() { diff --git a/relays/messages/src/message_race_delivery.rs b/relays/messages/src/message_race_delivery.rs index b50e6c0841e..7a245858b32 100644 --- a/relays/messages/src/message_race_delivery.rs +++ b/relays/messages/src/message_race_delivery.rs @@ -322,13 +322,19 @@ where self.strategy.is_empty() } - fn required_source_header_at_target( + fn required_source_header_at_target, TargetHeaderIdOf

>>( &self, current_best: &SourceHeaderIdOf

, + race_state: RS, ) -> Option> { + // we have already submitted something - let's wait until it is mined + if race_state.nonces_submitted().is_some() { + return None + } + let has_nonces_to_deliver = !self.strategy.is_empty(); let header_required_for_messages_delivery = - self.strategy.required_source_header_at_target(current_best); + self.strategy.required_source_header_at_target(current_best, race_state); let header_required_for_reward_confirmations_delivery = self .latest_confirmed_nonces_at_source .back() @@ -381,10 +387,10 @@ where self.strategy.source_nonces_updated(at_block, nonces) } - fn best_target_nonces_updated( + fn best_target_nonces_updated, TargetHeaderIdOf

>>( &mut self, nonces: TargetClientNonces, - race_state: &mut RaceState, TargetHeaderIdOf

, P::MessagesProof>, + race_state: &mut RS, ) { // best target nonces must always be ge than finalized target nonces let latest_nonce = nonces.latest_nonce; @@ -396,13 +402,13 @@ where ) } - fn finalized_target_nonces_updated( + fn finalized_target_nonces_updated, TargetHeaderIdOf

>>( &mut self, nonces: TargetClientNonces, - race_state: &mut RaceState, TargetHeaderIdOf

, P::MessagesProof>, + race_state: &mut RS, ) { if let Some(ref best_finalized_source_header_id_at_best_target) = - race_state.best_finalized_source_header_id_at_best_target + race_state.best_finalized_source_header_id_at_best_target() { let oldest_header_number_to_keep = best_finalized_source_header_id_at_best_target.0; while self @@ -426,13 +432,13 @@ where ) } - async fn select_nonces_to_deliver( + async fn select_nonces_to_deliver, TargetHeaderIdOf

>>( &self, - race_state: RaceState, TargetHeaderIdOf

, P::MessagesProof>, + race_state: RS, ) -> Option<(RangeInclusive, Self::ProofParameters)> { let best_target_nonce = self.strategy.best_at_target()?; let best_finalized_source_header_id_at_best_target = - race_state.best_finalized_source_header_id_at_best_target.clone()?; + race_state.best_finalized_source_header_id_at_best_target()?; let latest_confirmed_nonce_at_source = self .latest_confirmed_nonces_at_source .iter() @@ -576,12 +582,16 @@ impl NoncesRange for MessageDetailsMap; + type TestRaceState = RaceStateImpl< + TestSourceHeaderId, + TestTargetHeaderId, + TestMessagesProof, + TestMessagesBatchTransaction, + >; type TestStrategy = MessageDeliveryStrategy; @@ -617,12 +632,13 @@ mod tests { } fn prepare_strategy() -> (TestRaceState, TestStrategy) { - let mut race_state = RaceState { + let mut race_state = RaceStateImpl { best_finalized_source_header_id_at_source: Some(header_id(1)), best_finalized_source_header_id_at_best_target: Some(header_id(1)), best_target_header_id: Some(header_id(1)), best_finalized_target_header_id: Some(header_id(1)), nonces_to_submit: None, + nonces_to_submit_batch: None, nonces_submitted: None, }; @@ -964,14 +980,17 @@ mod tests { ); // nothing needs to be delivered now and we don't need any new headers assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); - assert_eq!(strategy.required_source_header_at_target(&header_id(1)), None); + assert_eq!(strategy.required_source_header_at_target(&header_id(1), state.clone()), None); // now let's generate two more nonces [24; 25] at the soruce; strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 19, 0)); // // - so now we'll need to relay source block#2 to be able to accept messages [24; 25]. assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); - assert_eq!(strategy.required_source_header_at_target(&header_id(1)), Some(header_id(2))); + assert_eq!( + strategy.required_source_header_at_target(&header_id(1), state.clone()), + Some(header_id(2)) + ); // let's relay source block#2 state.best_finalized_source_header_id_at_source = Some(header_id(2)); @@ -982,7 +1001,7 @@ mod tests { // and ask strategy again => still nothing to deliver, because parallel confirmations // race need to be pushed further assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); - assert_eq!(strategy.required_source_header_at_target(&header_id(2)), None); + assert_eq!(strategy.required_source_header_at_target(&header_id(2), state.clone()), None); // let's confirm messages [20; 23] strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 23, 0)); @@ -990,10 +1009,10 @@ mod tests { // and ask strategy again => now we have everything required to deliver remaining // [24; 25] nonces and proof of [20; 23] confirmation assert_eq!( - strategy.select_nonces_to_deliver(state).await, + strategy.select_nonces_to_deliver(state.clone()).await, Some(((24..=25), proof_parameters(true, 2))), ); - assert_eq!(strategy.required_source_header_at_target(&header_id(2)), None); + assert_eq!(strategy.required_source_header_at_target(&header_id(2), state), None); } #[async_std::test] @@ -1025,6 +1044,7 @@ mod tests { #[test] #[allow(clippy::reversed_empty_ranges)] fn no_source_headers_required_at_target_if_lanes_are_empty() { + let (state, _) = prepare_strategy(); let mut strategy = TestStrategy { max_unrewarded_relayer_entries_at_target: 4, max_unconfirmed_nonces_at_target: 4, @@ -1053,7 +1073,7 @@ mod tests { strategy.latest_confirmed_nonces_at_source, VecDeque::from([(source_header_id, 0)]) ); - assert_eq!(strategy.required_source_header_at_target(&source_header_id), None); + assert_eq!(strategy.required_source_header_at_target(&source_header_id, state), None); } #[async_std::test] diff --git a/relays/messages/src/message_race_loop.rs b/relays/messages/src/message_race_loop.rs index 50f71ea050b..7e3f84dd5d1 100644 --- a/relays/messages/src/message_race_loop.rs +++ b/relays/messages/src/message_race_loop.rs @@ -25,7 +25,7 @@ use crate::message_lane_loop::{BatchTransaction, ClientState, NoncesSubmitArtifa use async_trait::async_trait; use bp_messages::MessageNonce; use futures::{ - future::FutureExt, + future::{FutureExt, TryFutureExt}, stream::{FusedStream, StreamExt}, }; use relay_utils::{ @@ -41,14 +41,14 @@ use std::{ /// One of races within lane. pub trait MessageRace { /// Header id of the race source. - type SourceHeaderId: Debug + Clone + PartialEq; + type SourceHeaderId: Debug + Clone + PartialEq + Send; /// Header id of the race source. - type TargetHeaderId: Debug + Clone + PartialEq; + type TargetHeaderId: Debug + Clone + PartialEq + Send; /// Message nonce used in the race. type MessageNonce: Debug + Clone; /// Proof that is generated and delivered in this race. - type Proof: Debug + Clone; + type Proof: Debug + Clone + Send; /// Name of the race source. fn source_name() -> String; @@ -128,7 +128,7 @@ pub trait TargetClient { /// Type of the additional data from the target client, used by the race. type TargetNoncesData: std::fmt::Debug; /// Type of batch transaction that submits finality and proof to the target node. - type BatchTransaction: BatchTransaction; + type BatchTransaction: BatchTransaction + Clone; /// Transaction tracker to track submitted transactions. type TransactionTracker: TransactionTracker; @@ -175,9 +175,10 @@ pub trait RaceStrategy: Debug { /// Should return true if nothing has to be synced. fn is_empty(&self) -> bool; /// Return id of source header that is required to be on target to continue synchronization. - fn required_source_header_at_target( + fn required_source_header_at_target>( &self, current_best: &SourceHeaderId, + race_state: RS, ) -> Option; /// Return the best nonce at source node. /// @@ -196,29 +197,53 @@ pub trait RaceStrategy: Debug { nonces: SourceClientNonces, ); /// Called when best nonces are updated at target node of the race. - fn best_target_nonces_updated( + fn best_target_nonces_updated>( &mut self, nonces: TargetClientNonces, - race_state: &mut RaceState, + race_state: &mut RS, ); /// Called when finalized nonces are updated at target node of the race. - fn finalized_target_nonces_updated( + fn finalized_target_nonces_updated>( &mut self, nonces: TargetClientNonces, - race_state: &mut RaceState, + race_state: &mut RS, ); /// Should return `Some(nonces)` if we need to deliver proof of `nonces` (and associated /// data) from source to target node. /// Additionally, parameters required to generate proof are returned. - async fn select_nonces_to_deliver( + async fn select_nonces_to_deliver>( &self, - race_state: RaceState, + race_state: RS, ) -> Option<(RangeInclusive, Self::ProofParameters)>; } /// State of the race. +pub trait RaceState: Send { + /// Best finalized source header id at the source client. + fn best_finalized_source_header_id_at_source(&self) -> Option; + /// Best finalized source header id at the best block on the target + /// client (at the `best_finalized_source_header_id_at_best_target`). + fn best_finalized_source_header_id_at_best_target(&self) -> Option; + /// The best header id at the target client. + fn best_target_header_id(&self) -> Option; + /// Best finalized header id at the target client. + fn best_finalized_target_header_id(&self) -> Option; + + /// Returns `true` if we have selected nonces to submit to the target node. + fn nonces_to_submit(&self) -> Option>; + /// Reset our nonces selection. + fn reset_nonces_to_submit(&mut self); + + /// Returns `true` if we have submitted some nonces to the target node and are + /// waiting for them to appear there. + fn nonces_submitted(&self) -> Option>; + /// Reset our nonces submission. + fn reset_nonces_submitted(&mut self); +} + +/// State of the race and prepared batch transaction (if available). #[derive(Debug, Clone)] -pub struct RaceState { +pub(crate) struct RaceStateImpl { /// Best finalized source header id at the source client. pub best_finalized_source_header_id_at_source: Option, /// Best finalized source header id at the best block on the target @@ -230,13 +255,67 @@ pub struct RaceState { pub best_finalized_target_header_id: Option, /// Range of nonces that we have selected to submit. pub nonces_to_submit: Option<(SourceHeaderId, RangeInclusive, Proof)>, + /// Batch transaction ready to include and deliver selected `nonces_to_submit` from the + /// `state`. + pub nonces_to_submit_batch: Option, /// Range of nonces that is currently submitted. pub nonces_submitted: Option>, } -impl RaceState { - /// Reset `nonces_submitted` to `None`. - fn reset_submitted(&mut self) { +impl Default + for RaceStateImpl +{ + fn default() -> Self { + RaceStateImpl { + best_finalized_source_header_id_at_source: None, + best_finalized_source_header_id_at_best_target: None, + best_target_header_id: None, + best_finalized_target_header_id: None, + nonces_to_submit: None, + nonces_to_submit_batch: None, + nonces_submitted: None, + } + } +} + +impl RaceState + for RaceStateImpl +where + SourceHeaderId: Clone + Send, + TargetHeaderId: Clone + Send, + Proof: Clone + Send, + BatchTx: Clone + Send, +{ + fn best_finalized_source_header_id_at_source(&self) -> Option { + self.best_finalized_source_header_id_at_source.clone() + } + + fn best_finalized_source_header_id_at_best_target(&self) -> Option { + self.best_finalized_source_header_id_at_best_target.clone() + } + + fn best_target_header_id(&self) -> Option { + self.best_target_header_id.clone() + } + + fn best_finalized_target_header_id(&self) -> Option { + self.best_finalized_target_header_id.clone() + } + + fn nonces_to_submit(&self) -> Option> { + self.nonces_to_submit.as_ref().map(|(_, nonces, _)| nonces.clone()) + } + + fn reset_nonces_to_submit(&mut self) { + self.nonces_to_submit = None; + self.nonces_to_submit_batch = None; + } + + fn nonces_submitted(&self) -> Option> { + self.nonces_submitted.clone() + } + + fn reset_nonces_submitted(&mut self) { self.nonces_submitted = None; } } @@ -257,7 +336,7 @@ pub async fn run, TC: TargetClient

>( >, ) -> Result<(), FailedClient> { let mut progress_context = Instant::now(); - let mut race_state = RaceState::default(); + let mut race_state = RaceStateImpl::default(); let mut source_retry_backoff = retry_backoff(); let mut source_client_is_online = true; @@ -302,7 +381,8 @@ pub async fn run, TC: TargetClient

>( != Some(&source_state.best_finalized_self); if is_source_state_updated { source_nonces_required = true; - race_state.best_finalized_source_header_id_at_source = Some(source_state.best_finalized_self); + race_state.best_finalized_source_header_id_at_source + = Some(source_state.best_finalized_self); } } }, @@ -353,7 +433,7 @@ pub async fn run, TC: TargetClient

>( source_required_header = race_state .best_finalized_source_header_id_at_best_target .as_ref() - .and_then(|best| strategy.required_source_header_at_target(best)); + .and_then(|best| strategy.required_source_header_at_target(best, race_state.clone())); }, nonces = target_best_nonces => { target_best_nonces_required = false; @@ -408,10 +488,13 @@ pub async fn run, TC: TargetClient

>( |maybe_batch_transaction: Option| { log::debug!( target: "bridge", - "Target {} client has been asked for more {} headers. Batch tx: {:?}", + "Target {} client has been asked for more {} headers. Batch tx: {}", P::target_name(), P::source_name(), - maybe_batch_transaction.is_some(), + maybe_batch_transaction + .as_ref() + .map(|bt| format!("yes ({:?})", bt.required_header_id())) + .unwrap_or_else(|| "no".into()), ); target_batch_transaction = maybe_batch_transaction; @@ -425,7 +508,7 @@ pub async fn run, TC: TargetClient

>( source_client_is_online = process_future_result( proof, &mut source_retry_backoff, - |(at_block, nonces_range, proof)| { + |(at_block, nonces_range, proof, batch_transaction)| { log::debug!( target: "bridge", "Received proof for nonces in range {:?} from {}", @@ -434,6 +517,7 @@ pub async fn run, TC: TargetClient

>( ); race_state.nonces_to_submit = Some((at_block, nonces_range, proof)); + race_state.nonces_to_submit_batch = batch_transaction; }, &mut source_go_offline_future, async_std::task::sleep, @@ -452,8 +536,7 @@ pub async fn run, TC: TargetClient

>( P::target_name(), ); - target_batch_transaction = None; - race_state.nonces_to_submit = None; + race_state.reset_nonces_to_submit(); race_state.nonces_submitted = Some(artifacts.nonces); target_tx_tracker.set(artifacts.tx_tracker.wait().fuse()); }, @@ -490,7 +573,7 @@ pub async fn run, TC: TargetClient

>( e, ); - race_state.reset_submitted(); + race_state.reset_nonces_submitted(); }); }, (TrackedTransactionStatus::Lost, _) => { @@ -503,7 +586,7 @@ pub async fn run, TC: TargetClient

>( strategy, ); - race_state.reset_submitted(); + race_state.reset_nonces_submitted(); }, _ => (), } @@ -524,6 +607,12 @@ pub async fn run, TC: TargetClient

>( source_client_is_online = false; // if we've started to submit batch transaction, let's prioritize it + // + // we're using `take` here, because we don't need batch transaction (i.e. some + // underlying finality proof) anymore for our future calls - we were unable to + // use it for our current state, so why would we need to keep an obsolete proof + // for the future? + let target_batch_transaction = target_batch_transaction.take(); let expected_race_state = if let Some(ref target_batch_transaction) = target_batch_transaction { // when selecting nonces for the batch transaction, we assume that the required @@ -551,7 +640,12 @@ pub async fn run, TC: TargetClient

>( ); source_generate_proof.set( - race_source.generate_proof(at_block, nonces_range, proof_parameters).fuse(), + race_source + .generate_proof(at_block, nonces_range, proof_parameters) + .and_then(|(at_source_block, nonces, proof)| async { + Ok((at_source_block, nonces, proof, target_batch_transaction)) + }) + .fuse(), ); } else if source_nonces_required && best_at_source.is_some() { log::debug!(target: "bridge", "Asking {} about message nonces", P::source_name()); @@ -582,7 +676,7 @@ pub async fn run, TC: TargetClient

>( "Going to submit proof of messages in range {:?} to {} node{}", nonces_range, P::target_name(), - target_batch_transaction.as_ref().map(|tx| format!( + race_state.nonces_to_submit_batch.as_ref().map(|tx| format!( ". This transaction is batched with sending the proof for header {:?}.", tx.required_header_id()) ).unwrap_or_default(), @@ -591,7 +685,7 @@ pub async fn run, TC: TargetClient

>( target_submit_proof.set( race_target .submit_proof( - target_batch_transaction.take(), + race_state.nonces_to_submit_batch.clone(), at_block.clone(), nonces_range.clone(), proof.clone(), @@ -628,21 +722,6 @@ pub async fn run, TC: TargetClient

>( } } -impl Default - for RaceState -{ - fn default() -> Self { - RaceState { - best_finalized_source_header_id_at_source: None, - best_finalized_source_header_id_at_best_target: None, - best_target_header_id: None, - best_finalized_target_header_id: None, - nonces_to_submit: None, - nonces_submitted: None, - } - } -} - /// Print race progress. fn print_race_progress(prev_time: Instant, strategy: &S) -> Instant where @@ -670,7 +749,7 @@ where } async fn select_nonces_to_deliver( - race_state: RaceState, + race_state: impl RaceState, strategy: &Strategy, ) -> Option<(SourceHeaderId, RangeInclusive, Strategy::ProofParameters)> where @@ -678,7 +757,7 @@ where Strategy: RaceStrategy, { let best_finalized_source_header_id_at_best_target = - race_state.best_finalized_source_header_id_at_best_target.clone()?; + race_state.best_finalized_source_header_id_at_best_target()?; strategy .select_nonces_to_deliver(race_state) .await @@ -701,7 +780,7 @@ mod tests { // target node only knows about source' BEST_AT_TARGET block // source node has BEST_AT_SOURCE > BEST_AT_TARGET block - let mut race_state = RaceState::<_, _, ()> { + let mut race_state = RaceStateImpl::<_, _, (), ()> { best_finalized_source_header_id_at_source: Some(HeaderId( BEST_AT_SOURCE, BEST_AT_SOURCE, @@ -713,11 +792,12 @@ mod tests { best_target_header_id: Some(HeaderId(0, 0)), best_finalized_target_header_id: Some(HeaderId(0, 0)), nonces_to_submit: None, + nonces_to_submit_batch: None, nonces_submitted: None, }; // we have some nonces to deliver and they're generated at GENERATED_AT < BEST_AT_SOURCE - let mut strategy = BasicStrategy::new(); + let mut strategy = BasicStrategy::<_, _, _, _, _, ()>::new(); strategy.source_nonces_updated( HeaderId(GENERATED_AT, GENERATED_AT), SourceClientNonces { new_nonces: 0..=10, confirmed_nonce: None }, diff --git a/relays/messages/src/message_race_strategy.rs b/relays/messages/src/message_race_strategy.rs index 479ffe51329..e6016448c95 100644 --- a/relays/messages/src/message_race_strategy.rs +++ b/relays/messages/src/message_race_strategy.rs @@ -106,24 +106,25 @@ impl< /// at source blocks that are known to be finalized at the target node. /// /// Returns `None` if no entries may be delivered. - pub fn available_source_queue_indices( - &self, - race_state: RaceState< + pub fn available_source_queue_indices< + RS: RaceState< HeaderId, HeaderId, - Proof, >, + >( + &self, + race_state: RS, ) -> Option> { // if we do not know best nonce at target node, we can't select anything let best_target_nonce = self.best_target_nonce?; // if we have already selected nonces that we want to submit, do nothing - if race_state.nonces_to_submit.is_some() { + if race_state.nonces_to_submit().is_some() { return None } // if we already submitted some nonces, do nothing - if race_state.nonces_submitted.is_some() { + if race_state.nonces_submitted().is_some() { return None } @@ -143,7 +144,7 @@ impl< // // => let's first select range of entries inside deque that are already finalized at // the target client and pass this range to the selector - let best_header_at_target = race_state.best_finalized_source_header_id_at_best_target?; + let best_header_at_target = race_state.best_finalized_source_header_id_at_best_target()?; let end_index = self .source_queue .iter() @@ -204,9 +205,15 @@ impl< self.source_queue.is_empty() } - fn required_source_header_at_target( + fn required_source_header_at_target< + RS: RaceState< + HeaderId, + HeaderId, + >, + >( &self, current_best: &HeaderId, + _race_state: RS, ) -> Option> { self.source_queue .back() @@ -247,46 +254,46 @@ impl< ) } - fn best_target_nonces_updated( - &mut self, - nonces: TargetClientNonces<()>, - race_state: &mut RaceState< + fn best_target_nonces_updated< + RS: RaceState< HeaderId, HeaderId, - Proof, >, + >( + &mut self, + nonces: TargetClientNonces<()>, + race_state: &mut RS, ) { let nonce = nonces.latest_nonce; let need_to_select_new_nonces = race_state - .nonces_to_submit - .as_ref() - .map(|(_, nonces, _)| *nonces.end() <= nonce) + .nonces_to_submit() + .map(|nonces| *nonces.end() <= nonce) .unwrap_or(false); if need_to_select_new_nonces { - race_state.nonces_to_submit = None; + race_state.reset_nonces_to_submit(); } let need_new_nonces_to_submit = race_state - .nonces_submitted - .as_ref() + .nonces_submitted() .map(|nonces| *nonces.end() <= nonce) .unwrap_or(false); if need_new_nonces_to_submit { - race_state.nonces_submitted = None; + race_state.reset_nonces_submitted(); } self.best_target_nonce = Some(nonce); } - fn finalized_target_nonces_updated( - &mut self, - nonces: TargetClientNonces<()>, - _race_state: &mut RaceState< + fn finalized_target_nonces_updated< + RS: RaceState< HeaderId, HeaderId, - Proof, >, + >( + &mut self, + nonces: TargetClientNonces<()>, + _race_state: &mut RS, ) { self.remove_le_nonces_from_source_queue(nonces.latest_nonce); self.best_target_nonce = Some(std::cmp::max( @@ -295,13 +302,14 @@ impl< )); } - async fn select_nonces_to_deliver( - &self, - race_state: RaceState< + async fn select_nonces_to_deliver< + RS: RaceState< HeaderId, HeaderId, - Proof, >, + >( + &self, + race_state: RS, ) -> Option<(RangeInclusive, Self::ProofParameters)> { let available_indices = self.available_source_queue_indices(race_state)?; let range_begin = std::cmp::max( @@ -317,15 +325,23 @@ impl< mod tests { use super::*; use crate::{ - message_lane::MessageLane, + message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, message_lane_loop::tests::{ header_id, TestMessageLane, TestMessagesProof, TestSourceHeaderHash, TestSourceHeaderNumber, }, + message_race_loop::RaceStateImpl, }; type SourceNoncesRange = RangeInclusive; + type TestRaceStateImpl = RaceStateImpl< + SourceHeaderIdOf, + TargetHeaderIdOf, + TestMessagesProof, + (), + >; + type BasicStrategy

= super::BasicStrategy<

::SourceHeaderNumber,

::SourceHeaderHash, @@ -357,7 +373,7 @@ mod tests { assert_eq!(strategy.best_at_source(), None); strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); assert_eq!(strategy.best_at_source(), None); - strategy.best_target_nonces_updated(target_nonces(10), &mut Default::default()); + strategy.best_target_nonces_updated(target_nonces(10), &mut TestRaceStateImpl::default()); assert_eq!(strategy.source_queue, vec![(header_id(1), 1..=5)]); assert_eq!(strategy.best_at_source(), Some(10)); } @@ -365,7 +381,7 @@ mod tests { #[test] fn source_nonce_is_never_lower_than_known_target_nonce() { let mut strategy = BasicStrategy::::new(); - strategy.best_target_nonces_updated(target_nonces(10), &mut Default::default()); + strategy.best_target_nonces_updated(target_nonces(10), &mut TestRaceStateImpl::default()); strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); assert_eq!(strategy.source_queue, vec![]); } @@ -386,15 +402,17 @@ mod tests { strategy.source_nonces_updated(header_id(2), source_nonces(6..=10)); strategy.source_nonces_updated(header_id(3), source_nonces(11..=15)); strategy.source_nonces_updated(header_id(4), source_nonces(16..=20)); - strategy.finalized_target_nonces_updated(target_nonces(15), &mut Default::default()); + strategy + .finalized_target_nonces_updated(target_nonces(15), &mut TestRaceStateImpl::default()); assert_eq!(strategy.source_queue, vec![(header_id(4), 16..=20)]); - strategy.finalized_target_nonces_updated(target_nonces(17), &mut Default::default()); + strategy + .finalized_target_nonces_updated(target_nonces(17), &mut TestRaceStateImpl::default()); assert_eq!(strategy.source_queue, vec![(header_id(4), 18..=20)]); } #[test] fn selected_nonces_are_dropped_on_target_nonce_update() { - let mut state = RaceState::default(); + let mut state = TestRaceStateImpl::default(); let mut strategy = BasicStrategy::::new(); state.nonces_to_submit = Some((header_id(1), 5..=10, (5..=10, None))); strategy.best_target_nonces_updated(target_nonces(7), &mut state); @@ -405,7 +423,7 @@ mod tests { #[test] fn submitted_nonces_are_dropped_on_target_nonce_update() { - let mut state = RaceState::default(); + let mut state = TestRaceStateImpl::default(); let mut strategy = BasicStrategy::::new(); state.nonces_submitted = Some(5..=10); strategy.best_target_nonces_updated(target_nonces(7), &mut state); @@ -416,7 +434,7 @@ mod tests { #[async_std::test] async fn nothing_is_selected_if_something_is_already_selected() { - let mut state = RaceState::default(); + let mut state = TestRaceStateImpl::default(); let mut strategy = BasicStrategy::::new(); state.nonces_to_submit = Some((header_id(1), 1..=10, (1..=10, None))); strategy.best_target_nonces_updated(target_nonces(0), &mut state); @@ -426,7 +444,7 @@ mod tests { #[async_std::test] async fn nothing_is_selected_if_something_is_already_submitted() { - let mut state = RaceState::default(); + let mut state = TestRaceStateImpl::default(); let mut strategy = BasicStrategy::::new(); state.nonces_submitted = Some(1..=10); strategy.best_target_nonces_updated(target_nonces(0), &mut state); @@ -436,7 +454,7 @@ mod tests { #[async_std::test] async fn select_nonces_to_deliver_works() { - let mut state = RaceState::<_, _, TestMessagesProof>::default(); + let mut state = TestRaceStateImpl::default(); let mut strategy = BasicStrategy::::new(); strategy.best_target_nonces_updated(target_nonces(0), &mut state); strategy.source_nonces_updated(header_id(1), source_nonces(1..=1)); @@ -457,7 +475,7 @@ mod tests { #[test] fn available_source_queue_indices_works() { - let mut state = RaceState::<_, _, TestMessagesProof>::default(); + let mut state = TestRaceStateImpl::default(); let mut strategy = BasicStrategy::::new(); strategy.best_target_nonces_updated(target_nonces(0), &mut state); strategy.source_nonces_updated(header_id(1), source_nonces(1..=3)); @@ -482,7 +500,7 @@ mod tests { #[test] fn remove_le_nonces_from_source_queue_works() { - let mut state = RaceState::<_, _, TestMessagesProof>::default(); + let mut state = TestRaceStateImpl::default(); let mut strategy = BasicStrategy::::new(); strategy.best_target_nonces_updated(target_nonces(0), &mut state); strategy.source_nonces_updated(header_id(1), source_nonces(1..=3)); @@ -518,12 +536,13 @@ mod tests { let target_header_1 = header_id(1); // we start in perfec sync state - all headers are synced and finalized on both ends - let mut state = RaceState::<_, _, TestMessagesProof> { + let mut state = TestRaceStateImpl { best_finalized_source_header_id_at_source: Some(source_header_1), best_finalized_source_header_id_at_best_target: Some(source_header_1), best_target_header_id: Some(target_header_1), best_finalized_target_header_id: Some(target_header_1), nonces_to_submit: None, + nonces_to_submit_batch: None, nonces_submitted: None, }; diff --git a/relays/parachains/Cargo.toml b/relays/parachains/Cargo.toml index 5481ce7edf8..d683c131b6e 100644 --- a/relays/parachains/Cargo.toml +++ b/relays/parachains/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] async-std = "1.6.5" -async-trait = "0.1.66" +async-trait = "0.1.67" futures = "0.3.27" log = "0.4.17" relay-utils = { path = "../utils" } diff --git a/relays/utils/Cargo.toml b/relays/utils/Cargo.toml index d8b66db2abc..91bd9e20d65 100644 --- a/relays/utils/Cargo.toml +++ b/relays/utils/Cargo.toml @@ -21,7 +21,7 @@ serde_json = "1.0" sysinfo = "0.28" time = { version = "0.3", features = ["formatting", "local-offset", "std"] } tokio = { version = "1.26", features = ["rt"] } -thiserror = "1.0.39" +thiserror = "1.0.40" # Bridge dependencies diff --git a/scripts/send-message-from-millau-rialto.sh b/scripts/send-message-from-millau-rialto.sh index bcfc9df2f57..1a736b4f8bd 100755 --- a/scripts/send-message-from-millau-rialto.sh +++ b/scripts/send-message-from-millau-rialto.sh @@ -15,4 +15,4 @@ RUST_LOG=runtime=trace,substrate-relay=trace,bridge=trace \ --source-host localhost \ --source-port $MILLAU_PORT \ --source-signer //Alice \ - raw 020419ac + raw 030426020109020419a8 diff --git a/scripts/send-message-from-rialto-millau.sh b/scripts/send-message-from-rialto-millau.sh index e348c33f2a2..8133978f6ce 100755 --- a/scripts/send-message-from-rialto-millau.sh +++ b/scripts/send-message-from-rialto-millau.sh @@ -15,4 +15,4 @@ RUST_LOG=runtime=trace,substrate-relay=trace,bridge=trace \ --source-host localhost \ --source-port $RIALTO_PORT \ --source-signer //Bob \ - raw 020419ac + raw 030426030109030419a8 diff --git a/tools/runtime-codegen/Cargo.lock b/tools/runtime-codegen/Cargo.lock index be36a2ccd21..85777e90283 100644 --- a/tools/runtime-codegen/Cargo.lock +++ b/tools/runtime-codegen/Cargo.lock @@ -12,13 +12,22 @@ dependencies = [ "regex", ] +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli 0.26.2", +] + [[package]] name = "addr2line" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ - "gimli", + "gimli 0.27.2", ] [[package]] @@ -38,6 +47,18 @@ dependencies = [ "version_check", ] +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "getrandom 0.2.8", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.20" @@ -67,24 +88,21 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] -name = "arrayref" -version = "0.3.6" +name = "array-bytes" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" [[package]] -name = "arrayvec" -version = "0.4.12" +name = "arrayref" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -dependencies = [ - "nodrop", -] +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -100,23 +118,22 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "async-lock" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" dependencies = [ "event-listener", - "futures-lite", ] [[package]] name = "async-trait" -version = "0.1.64" +version = "0.1.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +checksum = "86ea188f25f0255d8f92797797c97ebf5631fa88178beb1a46fdf5622c9a00e4" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.8", ] [[package]] @@ -131,12 +148,12 @@ version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ - "addr2line", + "addr2line 0.19.0", "cc", "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.30.3", "rustc-demangle", ] @@ -167,6 +184,15 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -194,16 +220,6 @@ dependencies = [ "digest 0.10.6", ] -[[package]] -name = "blake2-rfc" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -dependencies = [ - "arrayvec 0.4.12", - "constant_time_eq", -] - [[package]] name = "block-buffer" version = "0.7.3" @@ -227,9 +243,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array 0.14.6", ] @@ -243,6 +259,18 @@ dependencies = [ "byte-tools", ] +[[package]] +name = "bounded-collections" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a071c348a5ef6da1d3a87166b408170b46002382b1dda83992b5c2208cefb370" +dependencies = [ + "log", + "parity-scale-codec", + "scale-info", + "serde", +] + [[package]] name = "bumpalo" version = "3.12.0" @@ -290,9 +318,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "iana-time-zone", "num-integer", @@ -302,9 +330,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.4" +version = "4.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" +checksum = "3c911b090850d79fc64fe9ea01e28e465f65e821e08813ced95bced72f7a8a9b" dependencies = [ "bitflags", "clap_derive", @@ -317,22 +345,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.1.0" +version = "4.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +checksum = "9a932373bab67b984c790ddf2c9ca295d8e3af3b7ef92de5a5bacdccdee4b09b" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.8", ] [[package]] name = "clap_lex" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" +checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" dependencies = [ "os_str_bytes", ] @@ -374,12 +401,6 @@ dependencies = [ "tracing-error", ] -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "core-foundation" version = "0.9.3" @@ -396,6 +417,15 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpufeatures" version = "0.2.5" @@ -405,6 +435,157 @@ dependencies = [ "libc", ] +[[package]] +name = "cranelift-bforest" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7379abaacee0f14abf3204a7606118f0465785252169d186337bcb75030815a" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9489fa336927df749631f1008007ced2871068544f40a202ce6d93fbf2366a7b" +dependencies = [ + "arrayvec 0.7.2", + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "cranelift-isle", + "gimli 0.26.2", + "hashbrown 0.12.3", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05bbb67da91ec721ed57cef2f7c5ef7728e1cd9bde9ffd3ef8601022e73e3239" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418ecb2f36032f6665dc1a5e2060a143dbab41d83b784882e97710e890a7a16d" + +[[package]] +name = "cranelift-entity" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cf583f7b093f291005f9fb1323e2c37f6ee4c7909e39ce016b2e8360d461705" +dependencies = [ + "serde", +] + +[[package]] +name = "cranelift-frontend" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b66bf9e916f57fbbd0f7703ec6286f4624866bf45000111627c70d272c8dda1" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "649782a39ce99798dd6b4029e2bb318a2fbeaade1b4fa25330763c10c65bc358" + +[[package]] +name = "cranelift-native" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "937e021e089c51f9749d09e7ad1c4f255c2f8686cb8c3df63a34b3ec9921bc41" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d850cf6775477747c9dfda9ae23355dd70512ffebc70cf82b85a5b111ae668b5" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools", + "log", + "smallvec", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.8.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + [[package]] name = "crunchy" version = "0.2.2" @@ -469,9 +650,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" +checksum = "a9c00419335c41018365ddf7e4d5f1c12ee3659ddcf3e01974650ba1de73d038" dependencies = [ "cc", "cxxbridge-flags", @@ -481,9 +662,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" +checksum = "fb8307ad413a98fff033c8545ecf133e3257747b3bae935e7602aab8aa92d4ca" dependencies = [ "cc", "codespan-reporting", @@ -491,31 +672,31 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 2.0.8", ] [[package]] name = "cxxbridge-flags" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" +checksum = "edc52e2eb08915cb12596d29d55f0b5384f00d697a646dbd269b6ecb0fbd9d31" [[package]] name = "cxxbridge-macro" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" +checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.8", ] [[package]] name = "darling" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ "darling_core", "darling_macro", @@ -523,27 +704,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn", + "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -554,7 +735,7 @@ checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -581,11 +762,32 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.4", "crypto-common", "subtle", ] +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "downcast-rs" version = "1.2.0" @@ -610,14 +812,14 @@ checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "dyn-clone" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" [[package]] name = "ed25519" @@ -636,8 +838,20 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek 3.2.0", "ed25519", - "rand 0.7.3", - "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek 3.2.0", + "hashbrown 0.12.3", + "hex", + "rand_core 0.6.4", "sha2 0.9.9", "zeroize", ] @@ -648,6 +862,19 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + [[package]] name = "environmental" version = "1.1.4" @@ -698,19 +925,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] -name = "fastrand" -version = "1.8.0" +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "file-per-thread-logger" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" dependencies = [ - "instant", + "env_logger", + "log", ] [[package]] name = "fixed-hash" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", "rand 0.8.5", @@ -724,6 +958,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + [[package]] name = "frame-metadata" version = "15.0.0" @@ -744,9 +987,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" dependencies = [ "futures-channel", "futures-core", @@ -759,9 +1002,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" dependencies = [ "futures-core", "futures-sink", @@ -769,15 +1012,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" dependencies = [ "futures-core", "futures-task", @@ -787,47 +1030,32 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" - -[[package]] -name = "futures-lite" -version = "1.12.0" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] +checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" [[package]] name = "futures-timer" @@ -841,9 +1069,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" dependencies = [ "futures-channel", "futures-core", @@ -857,6 +1085,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.12.4" @@ -883,10 +1120,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.9.0+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] @@ -902,9 +1137,20 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" [[package]] name = "gloo-net" @@ -953,9 +1199,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" dependencies = [ "bytes", "fnv", @@ -991,7 +1237,16 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", ] [[package]] @@ -1011,9 +1266,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "hex" @@ -1041,6 +1296,15 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + [[package]] name = "hmac-drbg" version = "0.3.0" @@ -1054,9 +1318,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -1086,11 +1350,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" dependencies = [ "bytes", "futures-channel", @@ -1128,16 +1398,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows", ] [[package]] @@ -1157,7 +1427,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "impl-codec" +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-codec" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" @@ -1167,9 +1447,9 @@ dependencies = [ [[package]] name = "impl-serde" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" dependencies = [ "serde", ] @@ -1182,7 +1462,7 @@ checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1198,16 +1478,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", + "hashbrown 0.12.3", + "serde", ] [[package]] @@ -1221,37 +1493,47 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" dependencies = [ + "hermit-abi 0.3.1", "libc", "windows-sys 0.45.0", ] [[package]] name = "is-terminal" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" dependencies = [ - "hermit-abi 0.3.0", + "hermit-abi 0.3.1", "io-lifetimes", "rustix", "windows-sys 0.45.0", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] @@ -1280,7 +1562,7 @@ dependencies = [ [[package]] name = "jsonrpsee" version = "0.16.2" -source = "git+https://github.com/paritytech/jsonrpsee#f6f4a0845d7c94dfb9befe625b2a779b3a40a127" +source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" dependencies = [ "jsonrpsee-client-transport 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", @@ -1314,16 +1596,13 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" version = "0.16.2" -source = "git+https://github.com/paritytech/jsonrpsee#f6f4a0845d7c94dfb9befe625b2a779b3a40a127" +source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" dependencies = [ - "anyhow", "futures-channel", - "futures-timer", "futures-util", "gloo-net", "http", "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", - "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", "pin-project", "rustls-native-certs", "soketto", @@ -1361,7 +1640,7 @@ dependencies = [ [[package]] name = "jsonrpsee-core" version = "0.16.2" -source = "git+https://github.com/paritytech/jsonrpsee#f6f4a0845d7c94dfb9befe625b2a779b3a40a127" +source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" dependencies = [ "anyhow", "async-lock", @@ -1403,14 +1682,13 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" version = "0.16.2" -source = "git+https://github.com/paritytech/jsonrpsee#f6f4a0845d7c94dfb9befe625b2a779b3a40a127" +source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" dependencies = [ "async-trait", "hyper", "hyper-rustls", "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", - "rustc-hash", "serde", "serde_json", "thiserror", @@ -1436,7 +1714,7 @@ dependencies = [ [[package]] name = "jsonrpsee-types" version = "0.16.2" -source = "git+https://github.com/paritytech/jsonrpsee#f6f4a0845d7c94dfb9befe625b2a779b3a40a127" +source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" dependencies = [ "anyhow", "beef", @@ -1449,7 +1727,7 @@ dependencies = [ [[package]] name = "jsonrpsee-wasm-client" version = "0.16.2" -source = "git+https://github.com/paritytech/jsonrpsee#f6f4a0845d7c94dfb9befe625b2a779b3a40a127" +source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" dependencies = [ "jsonrpsee-client-transport 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", @@ -1459,7 +1737,7 @@ dependencies = [ [[package]] name = "jsonrpsee-ws-client" version = "0.16.2" -source = "git+https://github.com/paritytech/jsonrpsee#f6f4a0845d7c94dfb9befe625b2a779b3a40a127" +source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" dependencies = [ "http", "jsonrpsee-client-transport 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", @@ -1484,9 +1762,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "libm" @@ -1578,11 +1856,20 @@ dependencies = [ [[package]] name = "lru" -version = "0.7.8" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" +dependencies = [ + "hashbrown 0.12.3", +] + +[[package]] +name = "mach" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" dependencies = [ - "hashbrown", + "libc", ] [[package]] @@ -1600,22 +1887,48 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memfd" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" +dependencies = [ + "rustix", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + [[package]] name = "memory-db" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6566c70c1016f525ced45d7b7f97730a2bafb037c788211d0c186ef5b2189f0a" +checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" dependencies = [ "hash-db", - "hashbrown", - "parity-util-mem", + "hashbrown 0.12.3", ] [[package]] name = "memory_units" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" [[package]] name = "merlin" @@ -1640,36 +1953,27 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "nom8" +name = "nohash-hasher" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" -dependencies = [ - "memchr", -] +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "num-bigint" -version = "0.2.6" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ "autocfg", "num-integer", @@ -1698,9 +2002,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.2.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", "num-bigint", @@ -1727,6 +2031,18 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "crc32fast", + "hashbrown 0.12.3", + "indexmap", + "memchr", +] + [[package]] name = "object" version = "0.30.3" @@ -1738,9 +2054,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "opaque-debug" @@ -1762,9 +2078,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "owo-colors" @@ -1774,13 +2090,14 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "parity-scale-codec" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3840933452adf7b3b9145e27086a5a3376c619dca1a21b1e5a5af0d54979bed" +checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" dependencies = [ "arrayvec 0.7.2", "bitvec", "byte-slice-cast", + "bytes", "impl-trait-for-tuples", "parity-scale-codec-derive", "serde", @@ -1795,46 +2112,14 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", -] - -[[package]] -name = "parity-util-mem" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c32561d248d352148124f036cac253a644685a21dc9fea383eb4907d7bd35a8f" -dependencies = [ - "cfg-if", - "hashbrown", - "impl-trait-for-tuples", - "parity-util-mem-derive", - "parking_lot", - "primitive-types", - "winapi", -] - -[[package]] -name = "parity-util-mem-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" -dependencies = [ - "proc-macro2", - "syn", - "synstructure", + "syn 1.0.109", ] [[package]] name = "parity-wasm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" - -[[package]] -name = "parking" -version = "2.0.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "parking_lot" @@ -1861,28 +2146,34 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[package]] name = "pbkdf2" -version = "0.4.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" dependencies = [ - "crypto-mac 0.8.0", + "crypto-mac 0.11.1", ] [[package]] name = "pbkdf2" -version = "0.8.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "crypto-mac 0.11.1", + "digest 0.10.6", ] +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + [[package]] name = "pin-project" version = "1.0.12" @@ -1900,7 +2191,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1929,9 +2220,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "primitive-types" -version = "0.11.1" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" dependencies = [ "fixed-hash", "impl-codec", @@ -1942,9 +2233,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", "toml_edit", @@ -1959,7 +2250,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -1976,18 +2267,27 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" dependencies = [ "unicode-ident", ] +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -2009,7 +2309,6 @@ dependencies = [ "rand_chacha 0.2.2", "rand_core 0.5.1", "rand_hc", - "rand_pcg", ] [[package]] @@ -2071,12 +2370,25 @@ dependencies = [ ] [[package]] -name = "rand_pcg" -version = "0.2.1" +name = "rayon" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ - "rand_core 0.5.1", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", ] [[package]] @@ -2088,31 +2400,54 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.8", + "redox_syscall", + "thiserror", +] + [[package]] name = "ref-cast" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c78fb8c9293bcd48ef6fce7b4ca950ceaf21210de6e105a883ee280c0f7b9ed" +checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f" +checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.8", +] + +[[package]] +name = "regalloc2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" +dependencies = [ + "fxhash", + "log", + "slice-group-by", + "smallvec", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" dependencies = [ "aho-corasick", "memchr", @@ -2130,9 +2465,21 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "region" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi", +] [[package]] name = "ring" @@ -2158,16 +2505,16 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "subxt-codegen", - "syn", + "syn 1.0.109", "wasm-loader", "wasm-testbed", ] [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" [[package]] name = "rustc-hash" @@ -2183,9 +2530,9 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustix" -version = "0.36.8" +version = "0.36.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" dependencies = [ "bitflags", "errno", @@ -2230,14 +2577,14 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "log", "sp-core", @@ -2248,22 +2595,20 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ - "lazy_static", "lru", "parity-scale-codec", "parking_lot", "sc-executor-common", "sc-executor-wasmi", + "sc-executor-wasmtime", "sp-api", "sp-core", - "sp-core-hashing-proc-macro", "sp-externalities", "sp-io", "sp-panic-handler", "sp-runtime-interface", - "sp-tasks", "sp-trie", "sp-version", "sp-wasm-interface", @@ -2274,14 +2619,10 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ - "environmental", - "parity-scale-codec", "sc-allocator", "sp-maybe-compressed-blob", - "sp-sandbox", - "sp-serializer", "sp-wasm-interface", "thiserror", "wasm-instrument", @@ -2291,23 +2632,39 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "log", - "parity-scale-codec", "sc-allocator", "sc-executor-common", "sp-runtime-interface", - "sp-sandbox", "sp-wasm-interface", "wasmi", ] +[[package]] +name = "sc-executor-wasmtime" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "anyhow", + "cfg-if", + "libc", + "log", + "once_cell", + "rustix", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface", + "sp-wasm-interface", + "wasmtime", +] + [[package]] name = "scale-info" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608" +checksum = "61471dff9096de1d8b2319efed7162081e96793f5ebb147e50db10d50d648a4d" dependencies = [ "bitvec", "cfg-if", @@ -2319,14 +2676,14 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" +checksum = "219580e803a66b3f05761fd06f1f879a872444e49ce23f73694d26e5a954c7e6" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -2338,6 +2695,17 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "schnellru" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +dependencies = [ + "ahash 0.8.3", + "cfg-if", + "hashbrown 0.13.2", +] + [[package]] name = "schnorrkel" version = "0.9.1" @@ -2364,9 +2732,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scratch" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "sct" @@ -2380,18 +2748,18 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.21.3" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c42e6f1735c5f00f51e43e28d6634141f2bcad10931b2609ddd74a86d751260" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" dependencies = [ "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.4.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" +checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" dependencies = [ "cc", ] @@ -2436,29 +2804,29 @@ checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.8", ] [[package]] name = "serde_json" -version = "1.0.92" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "itoa", "ryu", @@ -2535,9 +2903,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -2550,13 +2918,19 @@ checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] +[[package]] +name = "slice-group-by" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" + [[package]] name = "smallvec" version = "1.10.0" @@ -2565,9 +2939,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -2591,7 +2965,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "hash-db", "log", @@ -2600,7 +2974,8 @@ dependencies = [ "sp-core", "sp-runtime", "sp-state-machine", - "sp-std 4.0.0", + "sp-std 5.0.0", + "sp-trie", "sp-version", "thiserror", ] @@ -2608,100 +2983,96 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "blake2", "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "sp-application-crypto" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "parity-scale-codec", "scale-info", "serde", "sp-core", "sp-io", - "sp-std 4.0.0", + "sp-std 5.0.0", ] [[package]] name = "sp-arithmetic" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "integer-sqrt", "num-traits", "parity-scale-codec", "scale-info", "serde", - "sp-debug-derive", - "sp-std 4.0.0", + "sp-std 5.0.0", "static_assertions", ] [[package]] name = "sp-core" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ + "array-bytes", "base58", "bitflags", - "blake2-rfc", - "byteorder", + "blake2", + "bounded-collections", "dyn-clonable", - "ed25519-dalek", + "ed25519-zebra", "futures", "hash-db", "hash256-std-hasher", - "hex", "impl-serde", "lazy_static", "libsecp256k1", "log", "merlin", - "num-traits", "parity-scale-codec", - "parity-util-mem", "parking_lot", "primitive-types", - "rand 0.7.3", + "rand 0.8.5", "regex", "scale-info", "schnorrkel", "secp256k1", "secrecy", "serde", - "sp-core-hashing 4.0.0", + "sp-core-hashing 5.0.0", "sp-debug-derive", "sp-externalities", "sp-runtime-interface", - "sp-std 4.0.0", + "sp-std 5.0.0", "sp-storage", "ss58-registry", "substrate-bip39", "thiserror", "tiny-bip39", - "wasmi", "zeroize", ] [[package]] name = "sp-core-hashing" -version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "blake2", "byteorder", "digest 0.10.6", "sha2 0.10.6", "sha3", - "sp-std 4.0.0", + "sp-std 5.0.0", "twox-hash", ] @@ -2723,64 +3094,64 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "proc-macro2", "quote", - "sp-core-hashing 4.0.0", - "syn", + "sp-core-hashing 5.0.0", + "syn 1.0.109", ] [[package]] name = "sp-debug-derive" -version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "sp-externalities" -version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "environmental", "parity-scale-codec", - "sp-std 4.0.0", + "sp-std 5.0.0", "sp-storage", ] [[package]] name = "sp-io" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ + "bytes", + "ed25519", + "ed25519-dalek", "futures", - "hash-db", "libsecp256k1", "log", "parity-scale-codec", - "parking_lot", "secp256k1", "sp-core", "sp-externalities", "sp-keystore", "sp-runtime-interface", "sp-state-machine", - "sp-std 4.0.0", + "sp-std 5.0.0", "sp-tracing", "sp-trie", - "sp-wasm-interface", "tracing", "tracing-core", ] [[package]] name = "sp-keystore" -version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "async-trait", "futures", @@ -2796,7 +3167,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "thiserror", "zstd", @@ -2804,8 +3175,8 @@ dependencies = [ [[package]] name = "sp-panic-handler" -version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "backtrace", "lazy_static", @@ -2814,37 +3185,38 @@ dependencies = [ [[package]] name = "sp-runtime" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "either", "hash256-std-hasher", "impl-trait-for-tuples", "log", "parity-scale-codec", - "parity-util-mem", "paste", - "rand 0.7.3", + "rand 0.8.5", "scale-info", "serde", "sp-application-crypto", "sp-arithmetic", "sp-core", "sp-io", - "sp-std 4.0.0", + "sp-std 5.0.0", + "sp-weights", ] [[package]] name = "sp-runtime-interface" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ + "bytes", "impl-trait-for-tuples", "parity-scale-codec", "primitive-types", "sp-externalities", "sp-runtime-interface-proc-macro", - "sp-std 4.0.0", + "sp-std 5.0.0", "sp-storage", "sp-tracing", "sp-wasm-interface", @@ -2853,65 +3225,40 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "Inflector", "proc-macro-crate", "proc-macro2", "quote", - "syn", -] - -[[package]] -name = "sp-sandbox" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" -dependencies = [ - "log", - "parity-scale-codec", - "sp-core", - "sp-io", - "sp-std 4.0.0", - "sp-wasm-interface", - "wasmi", -] - -[[package]] -name = "sp-serializer" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" -dependencies = [ - "serde", - "serde_json", + "syn 1.0.109", ] [[package]] name = "sp-state-machine" -version = "0.12.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "hash-db", "log", - "num-traits", "parity-scale-codec", "parking_lot", - "rand 0.7.3", + "rand 0.8.5", "smallvec", "sp-core", "sp-externalities", "sp-panic-handler", - "sp-std 4.0.0", + "sp-std 5.0.0", "sp-trie", "thiserror", "tracing", - "trie-root", ] [[package]] name = "sp-std" -version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" [[package]] name = "sp-std" @@ -2921,37 +3268,24 @@ checksum = "af0ee286f98455272f64ac5bb1384ff21ac029fbb669afbaf48477faff12760e" [[package]] name = "sp-storage" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "impl-serde", "parity-scale-codec", "ref-cast", "serde", "sp-debug-derive", - "sp-std 4.0.0", -] - -[[package]] -name = "sp-tasks" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" -dependencies = [ - "log", - "sp-core", - "sp-externalities", - "sp-io", - "sp-runtime-interface", - "sp-std 4.0.0", + "sp-std 5.0.0", ] [[package]] name = "sp-tracing" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "parity-scale-codec", - "sp-std 4.0.0", + "sp-std 5.0.0", "tracing", "tracing-core", "tracing-subscriber 0.2.25", @@ -2959,16 +3293,23 @@ dependencies = [ [[package]] name = "sp-trie" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ + "ahash 0.8.3", "hash-db", + "hashbrown 0.12.3", + "lazy_static", "memory-db", + "nohash-hasher", "parity-scale-codec", + "parking_lot", "scale-info", + "schnellru", "sp-core", - "sp-std 4.0.0", + "sp-std 5.0.0", "thiserror", + "tracing", "trie-db", "trie-root", ] @@ -2976,7 +3317,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "impl-serde", "parity-scale-codec", @@ -2985,7 +3326,7 @@ dependencies = [ "serde", "sp-core-hashing-proc-macro", "sp-runtime", - "sp-std 4.0.0", + "sp-std 5.0.0", "sp-version-proc-macro", "thiserror", ] @@ -2993,24 +3334,41 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "sp-wasm-interface" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2022-07#279593d87f103fa5e10c9751a97b1584f3ad79d6" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" dependencies = [ + "anyhow", "impl-trait-for-tuples", "log", "parity-scale-codec", - "sp-std 4.0.0", + "sp-std 5.0.0", "wasmi", + "wasmtime", +] + +[[package]] +name = "sp-weights" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic", + "sp-core", + "sp-debug-derive", + "sp-std 5.0.0", ] [[package]] @@ -3021,9 +3379,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "ss58-registry" -version = "1.38.0" +version = "1.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e40c020d72bc0a9c5660bb71e4a6fdef081493583062c474740a7d59f55f0e7b" +checksum = "ecf0bd63593ef78eca595a7fc25e9a443ca46fe69fd472f8f09f5245cdcd769d" dependencies = [ "Inflector", "num-format", @@ -3034,6 +3392,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -3061,8 +3425,8 @@ dependencies = [ [[package]] name = "substrate-runtime-proposal-hash" -version = "0.18.0" -source = "git+https://github.com/chevdor/subwasm?branch=master#d7e74ab5eb3f83773ad7c78fb0edd42fa33f5356" +version = "0.19.0" +source = "git+https://github.com/chevdor/subwasm?branch=master#fd047d76d52beab8c062eb94141f8d14ddc0f59d" dependencies = [ "blake2", "hex", @@ -3081,8 +3445,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "subxt-codegen" -version = "0.26.0" -source = "git+https://github.com/paritytech/subxt?branch=master#20adb198e42d1a36d0c919737230423c17ca78e6" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e86cb719003f1cedf2710a6e55ca4c37aba4c989bbd3b81dd1c52af9e4827e" dependencies = [ "darling", "frame-metadata", @@ -3095,14 +3460,15 @@ dependencies = [ "quote", "scale-info", "subxt-metadata", - "syn", + "syn 1.0.109", "tokio", ] [[package]] name = "subxt-metadata" -version = "0.26.0" -source = "git+https://github.com/paritytech/subxt?branch=master#20adb198e42d1a36d0c919737230423c17ca78e6" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2593ab5f53435e6352675af4f9851342607f37785d84c7a3fb3139550d3c35f0" dependencies = [ "frame-metadata", "parity-scale-codec", @@ -3112,9 +3478,20 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" dependencies = [ "proc-macro2", "quote", @@ -3129,7 +3506,7 @@ checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "unicode-xid", ] @@ -3139,6 +3516,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "target-lexicon" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" + [[package]] name = "termcolor" version = "1.2.0" @@ -3150,46 +3533,47 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.8", ] [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] [[package]] name = "tiny-bip39" -version = "0.8.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" dependencies = [ "anyhow", - "hmac 0.8.1", + "hmac 0.12.1", "once_cell", - "pbkdf2 0.4.0", - "rand 0.7.3", + "pbkdf2 0.11.0", + "rand 0.8.5", "rustc-hash", - "sha2 0.9.9", + "sha2 0.10.6", "thiserror", "unicode-normalization", "wasm-bindgen", @@ -3213,9 +3597,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.25.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" dependencies = [ "autocfg", "bytes", @@ -3228,7 +3612,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -3239,7 +3623,7 @@ checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -3255,9 +3639,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" dependencies = [ "futures-core", "pin-project-lite", @@ -3266,9 +3650,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -3279,21 +3663,30 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "toml_datetime" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" [[package]] name = "toml_edit" -version = "0.18.1" +version = "0.19.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" dependencies = [ "indexmap", - "nom8", "toml_datetime", + "winnow", ] [[package]] @@ -3344,7 +3737,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -3423,12 +3816,12 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.23.1" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32d034c0d3db64b43c31de38e945f15b40cd4ca6d2dcfc26d4798ce8de4ab83" +checksum = "3390c0409daaa6027d6681393316f4ccd3ff82e1590a1e4725014e3ae2bf1920" dependencies = [ "hash-db", - "hashbrown", + "hashbrown 0.13.2", "log", "rustc-hex", "smallvec", @@ -3479,11 +3872,17 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -3512,6 +3911,17 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "valuable" version = "0.1.0" @@ -3524,12 +3934,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - [[package]] name = "want" version = "0.3.0" @@ -3573,7 +3977,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -3607,7 +4011,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3620,17 +4024,17 @@ checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "wasm-instrument" -version = "0.1.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "962e5b0401bbb6c887f54e69b8c496ea36f704df65db73e81fd5ff8dc3e63a9f" +checksum = "aa1dafb3e60065305741e83db35c6c2584bb3725b692b5b66148a38d72ace6cd" dependencies = [ "parity-wasm", ] [[package]] name = "wasm-loader" -version = "0.18.0" -source = "git+https://github.com/chevdor/subwasm?branch=master#d7e74ab5eb3f83773ad7c78fb0edd42fa33f5356" +version = "0.19.0" +source = "git+https://github.com/chevdor/subwasm?branch=master#fd047d76d52beab8c062eb94141f8d14ddc0f59d" dependencies = [ "hex", "jsonrpsee 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", @@ -3642,8 +4046,8 @@ dependencies = [ [[package]] name = "wasm-testbed" -version = "0.18.0" -source = "git+https://github.com/chevdor/subwasm?branch=master#d7e74ab5eb3f83773ad7c78fb0edd42fa33f5356" +version = "0.19.0" +source = "git+https://github.com/chevdor/subwasm?branch=master#fd047d76d52beab8c062eb94141f8d14ddc0f59d" dependencies = [ "frame-metadata", "hex", @@ -3663,27 +4067,225 @@ dependencies = [ [[package]] name = "wasmi" -version = "0.9.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca00c5147c319a8ec91ec1a0edbec31e566ce2c9cc93b3f9bb86a9efd0eb795d" +checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" +dependencies = [ + "parity-wasm", + "wasmi-validation", + "wasmi_core", +] + +[[package]] +name = "wasmi-validation" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasmi_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" dependencies = [ "downcast-rs", - "libc", "libm", "memory_units", "num-rational", "num-traits", - "parity-wasm", - "wasmi-validation", + "region", ] [[package]] -name = "wasmi-validation" -version = "0.4.1" +name = "wasmparser" +version = "0.100.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "165343ecd6c018fc09ebcae280752702c9a2ef3e6f8d02f1cfcbdb53ef6d7937" +checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" dependencies = [ - "parity-wasm", + "indexmap", + "url", +] + +[[package]] +name = "wasmtime" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e89f9819523447330ffd70367ef4a18d8c832e24e8150fe054d1d912841632" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "indexmap", + "libc", + "log", + "object 0.29.0", + "once_cell", + "paste", + "psm", + "rayon", + "serde", + "target-lexicon", + "wasmparser", + "wasmtime-cache", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd3a5e46c198032da934469f3a6e48649d1f9142438e4fd4617b68a35644b8a" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-cache" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b389ae9b678b9c3851091a4804f4182d688d27aff7abc9aa37fa7be37d8ecffa" +dependencies = [ + "anyhow", + "base64 0.13.1", + "bincode", + "directories-next", + "file-per-thread-logger", + "log", + "rustix", + "serde", + "sha2 0.10.6", + "toml", + "windows-sys 0.42.0", + "zstd", +] + +[[package]] +name = "wasmtime-cranelift" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b2c92a08c0db6efffd88fdc97d7aa9c7c63b03edb0971dbca745469f820e8c" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli 0.26.2", + "log", + "object 0.29.0", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a6db9fc52985ba06ca601f2ff0ff1f526c5d724c7ac267b47326304b0c97883" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli 0.26.2", + "indexmap", + "log", + "object 0.29.0", + "serde", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-jit" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77e3a52cd84d0f7f18554afa8060cfe564ccac61e3b0802d3fd4084772fa5f6" +dependencies = [ + "addr2line 0.17.0", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli 0.26.2", + "log", + "object 0.29.0", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-debug", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" +dependencies = [ + "object 0.29.0", + "once_cell", + "rustix", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67d412e9340ab1c83867051d8d1d7c90aa8c9afc91da086088068e2734e25064" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d594e791b5fdd4dbaf8cf7ae62f2e4ff85018ce90f483ca6f42947688e48827d" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap", + "libc", + "log", + "mach", + "memfd", + "memoffset 0.6.5", + "paste", + "rand 0.8.5", + "rustix", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-types" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6688d6f96d4dbc1f89fab626c56c1778936d122b5f4ae7a57c2eb42b8d982e2" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror", + "wasmparser", ] [[package]] @@ -3746,6 +4348,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.42.0" @@ -3772,9 +4383,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -3787,45 +4398,54 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "winnow" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deac0939bd6e4f24ab5919fbf751c97a8cfc8543bb083a305ed5c0c10bb241d1" +dependencies = [ + "memchr", +] [[package]] name = "wyz" @@ -3853,7 +4473,7 @@ checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "synstructure", ] @@ -3878,9 +4498,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.6+zstd.1.5.2" +version = "2.0.7+zstd.1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a3f9792c0c3dc6c165840a75f47ae1f4da402c2d006881129579f6597e801b" +checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" dependencies = [ "cc", "libc", diff --git a/tools/runtime-codegen/Cargo.toml b/tools/runtime-codegen/Cargo.toml index 7fcb7bc68a9..5eea4cdbc88 100644 --- a/tools/runtime-codegen/Cargo.toml +++ b/tools/runtime-codegen/Cargo.toml @@ -14,7 +14,7 @@ clap = { version = "4.0.8", features = ["derive", "cargo"] } codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } color-eyre = "0.6.1" proc-macro2 = "1.0.51" -subxt-codegen = { git = "https://github.com/paritytech/subxt", branch = "master" } +subxt-codegen = "0.27.1" syn = "1.0" wasm-loader = { git = "https://github.com/chevdor/subwasm", branch = "master" } wasm-testbed = { git = "https://github.com/chevdor/subwasm", branch = "master" } \ No newline at end of file From 5b61c2d3104b61c886a98bc2aa9bd5efe0ac1214 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 24 Mar 2023 20:53:04 +0100 Subject: [PATCH 222/263] dispatch message weight for bridge messages (#2378) Co-authored-by: Svyatoslav Nikolsky --- .../src/messages_benchmarking.rs | 3 +- .../src/messages_xcm_extension.rs | 1 + pallets/parachain-system/src/lib.rs | 24 ++++ .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 1 + .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 26 ++++- ...ith_bridge_hub_rococo_messages_instance.rs | 107 ++++++++++++------ ...ith_bridge_hub_wococo_messages_instance.rs | 107 ++++++++++++------ .../bridge-hub-rococo/src/xcm_config.rs | 21 ---- 8 files changed, 189 insertions(+), 101 deletions(-) diff --git a/bridges/bin/runtime-common/src/messages_benchmarking.rs b/bridges/bin/runtime-common/src/messages_benchmarking.rs index b067523c305..5d0ab6df71f 100644 --- a/bridges/bin/runtime-common/src/messages_benchmarking.rs +++ b/bridges/bin/runtime-common/src/messages_benchmarking.rs @@ -21,7 +21,8 @@ use crate::{ messages::{ - source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, + source::FromBridgedChainMessagesDeliveryProof, + target::FromBridgedChainMessagesProof, AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, ThisChain, }, messages_generation::{ diff --git a/bridges/bin/runtime-common/src/messages_xcm_extension.rs b/bridges/bin/runtime-common/src/messages_xcm_extension.rs index d4bf2bc1bed..353f777d6ca 100644 --- a/bridges/bin/runtime-common/src/messages_xcm_extension.rs +++ b/bridges/bin/runtime-common/src/messages_xcm_extension.rs @@ -57,6 +57,7 @@ pub struct XcmBlobMessageDispatch Pallet { pub fn set_custom_validation_head_data(head_data: Vec) { CustomValidationHeadData::::put(head_data); } + + /// Open HRMP channel for using it in benchmarks. + /// + /// The caller assumes that the pallet will accept regular outbound message to the sibling + /// `target_parachain` after this call. No other assumptions are made. + #[cfg(feature = "runtime-benchmarks")] + pub fn open_outbound_hrmp_channel_for_benchmarks(target_parachain: ParaId) { + RelevantMessagingState::::put(MessagingStateSnapshot { + dmq_mqc_head: Default::default(), + relay_dispatch_queue_size: Default::default(), + ingress_channels: Default::default(), + egress_channels: vec![( + target_parachain, + cumulus_primitives_core::AbridgedHrmpChannel { + max_capacity: 10, + max_total_size: 10_000_000_u32, + max_message_size: 10_000_000_u32, + msg_count: 5, + total_size: 5_000_000_u32, + mqc_head: None, + }, + )], + }) + } } pub struct ParachainSetCode(sp_std::marker::PhantomData); diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 18f9eddecc0..9dcc30e3e1a 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -175,6 +175,7 @@ runtime-benchmarks = [ "pallet-bridge-relayers/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", + "cumulus-pallet-parachain-system/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 9c9da6ac068..ff3af4b7434 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -80,7 +80,7 @@ use crate::{ WithBridgeHubRococoMessageBridge, }, constants::fee::WeightToFee, - xcm_config::{XcmRouter, XcmRouterWeigher}, + xcm_config::XcmRouter, }; use bridge_runtime_common::{ messages::{source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter}, @@ -488,7 +488,7 @@ impl pallet_bridge_messages::Config for Run bp_bridge_hub_wococo::BridgeHubWococo, bp_bridge_hub_rococo::BridgeHubRococo, OnBridgeHubRococoBlobDispatcher, - XcmRouterWeigher, + Self::WeightInfo, >; } @@ -525,7 +525,7 @@ impl pallet_bridge_messages::Config for Run bp_bridge_hub_rococo::BridgeHubRococo, bp_bridge_hub_wococo::BridgeHubWococo, OnBridgeHubWococoBlobDispatcher, - XcmRouterWeigher, + Self::WeightInfo, >; } @@ -1009,11 +1009,14 @@ impl_runtime_apis! { fn prepare_message_proof( params: MessageProofParams, ) -> (bridge_hub_rococo_config::FromWococoBridgeHubMessagesProof, Weight) { + use cumulus_primitives_core::XcmpMessageSource; + assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty()); + ParachainSystem::open_outbound_hrmp_channel_for_benchmarks(42.into()); prepare_message_proof_from_parachain::< Runtime, BridgeGrandpaWococoInstance, bridge_hub_rococo_config::WithBridgeHubWococoMessageBridge, - >(params) + >(params, X2(GlobalConsensus(Rococo), Parachain(42))) } fn prepare_message_delivery_proof( @@ -1025,6 +1028,11 @@ impl_runtime_apis! { bridge_hub_rococo_config::WithBridgeHubWococoMessageBridge, >(params) } + + fn is_message_successfully_dispatched(_nonce: bp_messages::MessageNonce) -> bool { + use cumulus_primitives_core::XcmpMessageSource; + !XcmpQueue::take_outbound_messages(usize::MAX).is_empty() + } } impl BridgeMessagesConfig for Runtime { @@ -1045,11 +1053,14 @@ impl_runtime_apis! { fn prepare_message_proof( params: MessageProofParams, ) -> (bridge_hub_wococo_config::FromRococoBridgeHubMessagesProof, Weight) { + use cumulus_primitives_core::XcmpMessageSource; + assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty()); + ParachainSystem::open_outbound_hrmp_channel_for_benchmarks(42.into()); prepare_message_proof_from_parachain::< Runtime, BridgeGrandpaRococoInstance, bridge_hub_wococo_config::WithBridgeHubRococoMessageBridge, - >(params) + >(params, X2(GlobalConsensus(Wococo), Parachain(42))) } fn prepare_message_delivery_proof( @@ -1061,6 +1072,11 @@ impl_runtime_apis! { bridge_hub_wococo_config::WithBridgeHubRococoMessageBridge, >(params) } + + fn is_message_successfully_dispatched(_nonce: bp_messages::MessageNonce) -> bool { + use cumulus_primitives_core::XcmpMessageSource; + !XcmpQueue::take_outbound_messages(usize::MAX).is_empty() + } } use bridge_runtime_common::parachains_benchmarking::prepare_parachain_heads_proof; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs index 14f7e85227b..7f61b0d6a11 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs @@ -17,25 +17,24 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/debug/polkadot-parachain // benchmark // pallet // --steps=50 -// --repeat=2 +// --repeat=20 // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=./bench.json -// --header=./file_header.txt -// --chain=bridge-hub-rococo-dev // --pallet=pallet_bridge_messages +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt // --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights #![cfg_attr(rustfmt, rustfmt_skip)] @@ -58,10 +57,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `430` + // Measured: `367` // Estimated: `57797` - // Minimum execution time: 43_647_000 picoseconds. - Weight::from_parts(78_914_000, 0) + // Minimum execution time: 1_052_304_000 picoseconds. + Weight::from_parts(1_067_318_000, 0) .saturating_add(Weight::from_parts(0, 57797)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) @@ -76,10 +75,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn receive_two_messages_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `430` + // Measured: `367` // Estimated: `57797` - // Minimum execution time: 62_322_000 picoseconds. - Weight::from_parts(75_283_000, 0) + // Minimum execution time: 1_358_159_000 picoseconds. + Weight::from_parts(1_371_112_000, 0) .saturating_add(Weight::from_parts(0, 57797)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) @@ -94,10 +93,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: - // Measured: `430` + // Measured: `367` // Estimated: `57797` - // Minimum execution time: 47_917_000 picoseconds. - Weight::from_parts(53_246_000, 0) + // Minimum execution time: 1_248_139_000 picoseconds. + Weight::from_parts(1_262_958_000, 0) .saturating_add(Weight::from_parts(0, 57797)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) @@ -110,10 +109,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: BridgeRococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) fn receive_single_message_proof_1_kb() -> Weight { // Proof Size summary in bytes: - // Measured: `398` + // Measured: `335` // Estimated: `56308` - // Minimum execution time: 42_265_000 picoseconds. - Weight::from_parts(50_221_000, 0) + // Minimum execution time: 1_006_936_000 picoseconds. + Weight::from_parts(1_017_299_000, 0) .saturating_add(Weight::from_parts(0, 56308)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -126,10 +125,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: BridgeRococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) fn receive_single_message_proof_16_kb() -> Weight { // Proof Size summary in bytes: - // Measured: `398` + // Measured: `335` // Estimated: `56308` - // Minimum execution time: 70_236_000 picoseconds. - Weight::from_parts(107_444_000, 0) + // Minimum execution time: 1_734_842_000 picoseconds. + Weight::from_parts(1_750_451_000, 0) .saturating_add(Weight::from_parts(0, 56308)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -146,11 +145,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: - // Measured: `370` - // Estimated: `12565` - // Minimum execution time: 34_072_000 picoseconds. - Weight::from_parts(54_240_000, 0) - .saturating_add(Weight::from_parts(0, 12565)) + // Measured: `339` + // Estimated: `12534` + // Minimum execution time: 983_917_000 picoseconds. + Weight::from_parts(991_532_000, 0) + .saturating_add(Weight::from_parts(0, 12534)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -166,11 +165,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: - // Measured: `370` - // Estimated: `12565` - // Minimum execution time: 34_013_000 picoseconds. - Weight::from_parts(50_732_000, 0) - .saturating_add(Weight::from_parts(0, 12565)) + // Measured: `339` + // Estimated: `12534` + // Minimum execution time: 983_370_000 picoseconds. + Weight::from_parts(992_178_000, 0) + .saturating_add(Weight::from_parts(0, 12534)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -186,12 +185,46 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: - // Measured: `370` - // Estimated: `15113` - // Minimum execution time: 36_242_000 picoseconds. - Weight::from_parts(57_344_000, 0) - .saturating_add(Weight::from_parts(0, 15113)) + // Measured: `339` + // Estimated: `15082` + // Minimum execution time: 1_118_509_000 picoseconds. + Weight::from_parts(1_136_181_000, 0) + .saturating_add(Weight::from_parts(0, 15082)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } + /// Storage: BridgeRococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeRococoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) + /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainSystem RelevantMessagingState (r:1 w:0) + /// Proof Skipped: ParachainSystem RelevantMessagingState (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: XcmpQueue OutboundXcmpStatus (r:1 w:1) + /// Proof Skipped: XcmpQueue OutboundXcmpStatus (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: XcmpQueue OutboundXcmpMessages (r:0 w:1) + /// Proof Skipped: XcmpQueue OutboundXcmpMessages (max_values: None, max_size: None, mode: Measured) + /// The range of component `i` is `[128, 2048]`. + /// The range of component `i` is `[128, 2048]`. + fn receive_single_message_proof_with_dispatch(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `635` + // Estimated: `71012` + // Minimum execution time: 1_887_056_000 picoseconds. + Weight::from_parts(1_896_414_726, 0) + .saturating_add(Weight::from_parts(0, 71012)) + // Standard Error: 2_462 + .saturating_add(Weight::from_parts(557_738, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs index 23d74f6feb7..811e483a779 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs @@ -17,25 +17,24 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-03-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/debug/polkadot-parachain // benchmark // pallet // --steps=50 -// --repeat=2 +// --repeat=20 // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=./bench.json -// --header=./file_header.txt -// --chain=bridge-hub-rococo-dev // --pallet=pallet_bridge_messages +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt // --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights #![cfg_attr(rustfmt, rustfmt_skip)] @@ -58,10 +57,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `467` + // Measured: `404` // Estimated: `57797` - // Minimum execution time: 46_035_000 picoseconds. - Weight::from_parts(87_082_000, 0) + // Minimum execution time: 1_063_700_000 picoseconds. + Weight::from_parts(1_073_859_000, 0) .saturating_add(Weight::from_parts(0, 57797)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) @@ -76,10 +75,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn receive_two_messages_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `467` + // Measured: `404` // Estimated: `57797` - // Minimum execution time: 69_379_000 picoseconds. - Weight::from_parts(80_326_000, 0) + // Minimum execution time: 1_363_828_000 picoseconds. + Weight::from_parts(1_383_808_000, 0) .saturating_add(Weight::from_parts(0, 57797)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) @@ -94,10 +93,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: - // Measured: `467` + // Measured: `404` // Estimated: `57797` - // Minimum execution time: 51_877_000 picoseconds. - Weight::from_parts(61_053_000, 0) + // Minimum execution time: 1_258_314_000 picoseconds. + Weight::from_parts(1_276_600_000, 0) .saturating_add(Weight::from_parts(0, 57797)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) @@ -110,10 +109,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: BridgeWococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) fn receive_single_message_proof_1_kb() -> Weight { // Proof Size summary in bytes: - // Measured: `435` + // Measured: `372` // Estimated: `56308` - // Minimum execution time: 44_882_000 picoseconds. - Weight::from_parts(53_124_000, 0) + // Minimum execution time: 1_019_420_000 picoseconds. + Weight::from_parts(1_031_056_000, 0) .saturating_add(Weight::from_parts(0, 56308)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -126,10 +125,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: BridgeWococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) fn receive_single_message_proof_16_kb() -> Weight { // Proof Size summary in bytes: - // Measured: `435` + // Measured: `372` // Estimated: `56308` - // Minimum execution time: 74_426_000 picoseconds. - Weight::from_parts(111_901_000, 0) + // Minimum execution time: 1_745_271_000 picoseconds. + Weight::from_parts(1_759_460_000, 0) .saturating_add(Weight::from_parts(0, 56308)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -146,11 +145,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: - // Measured: `407` - // Estimated: `12602` - // Minimum execution time: 35_466_000 picoseconds. - Weight::from_parts(59_081_000, 0) - .saturating_add(Weight::from_parts(0, 12602)) + // Measured: `376` + // Estimated: `12571` + // Minimum execution time: 993_132_000 picoseconds. + Weight::from_parts(1_005_111_000, 0) + .saturating_add(Weight::from_parts(0, 12571)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -166,11 +165,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: - // Measured: `407` - // Estimated: `12602` - // Minimum execution time: 35_889_000 picoseconds. - Weight::from_parts(48_281_000, 0) - .saturating_add(Weight::from_parts(0, 12602)) + // Measured: `376` + // Estimated: `12571` + // Minimum execution time: 995_201_000 picoseconds. + Weight::from_parts(1_003_630_000, 0) + .saturating_add(Weight::from_parts(0, 12571)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -186,12 +185,46 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: - // Measured: `407` - // Estimated: `15150` - // Minimum execution time: 37_430_000 picoseconds. - Weight::from_parts(54_994_000, 0) - .saturating_add(Weight::from_parts(0, 15150)) + // Measured: `376` + // Estimated: `15119` + // Minimum execution time: 1_126_518_000 picoseconds. + Weight::from_parts(1_143_524_000, 0) + .saturating_add(Weight::from_parts(0, 15119)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } + /// Storage: BridgeWococoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeWococoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeWococoParachain ImportedParaHeads (r:1 w:0) + /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) + /// Storage: BridgeWococoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeWococoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: PolkadotXcm SupportedVersion (r:1 w:0) + /// Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + /// Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + /// Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + /// Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: ParachainSystem RelevantMessagingState (r:1 w:0) + /// Proof Skipped: ParachainSystem RelevantMessagingState (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: XcmpQueue OutboundXcmpStatus (r:1 w:1) + /// Proof Skipped: XcmpQueue OutboundXcmpStatus (max_values: Some(1), max_size: None, mode: Measured) + /// Storage: XcmpQueue OutboundXcmpMessages (r:0 w:1) + /// Proof Skipped: XcmpQueue OutboundXcmpMessages (max_values: None, max_size: None, mode: Measured) + /// The range of component `i` is `[128, 2048]`. + /// The range of component `i` is `[128, 2048]`. + fn receive_single_message_proof_with_dispatch(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `672` + // Estimated: `71234` + // Minimum execution time: 1_893_906_000 picoseconds. + Weight::from_parts(1_907_046_615, 0) + .saturating_add(Weight::from_parts(0, 71234)) + // Standard Error: 2_494 + .saturating_add(Weight::from_parts(561_329, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(4)) + } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index dfb5f38cdb5..af2b8ba7868 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -26,7 +26,6 @@ use crate::{ use frame_support::{ match_types, parameter_types, traits::{ConstU32, Contains, Everything, Nothing}, - weights::Weight, }; use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; @@ -46,7 +45,6 @@ use xcm_executor::{ XcmExecutor, }; -use crate::weights::RocksDbWeight; use parachains_common::xcm_config::{ ConcreteNativeAssetFrom, DenyReserveTransferToRelayChain, DenyThenTry, }; @@ -265,25 +263,6 @@ pub type XcmRouter = ( XcmpQueue, ); -/// Simple weigher for xcm dispatch: -/// -/// `ParentAsUmp` does `>::append(` means one write -/// `XcmpQueue` does: -/// - `wrap_version` - 1 read -/// - `send_fragment`: -/// - `get_channel_max` - 1 read -/// - `>::get()` - 1 read -/// - `>::mutate(` - 1 write -/// or -/// - `>::insert(` - 1 write -/// - `>::put - 1 write` - 1 write -pub struct XcmRouterWeigher; -impl Get for XcmRouterWeigher { - fn get() -> Weight { - RocksDbWeight::get().reads_writes(1, 2) - } -} - #[cfg(feature = "runtime-benchmarks")] parameter_types! { pub ReachableDest: Option = Some(Parent.into()); From b0ac4fe0fa90f9a289aea9688fdd63da570ca664 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 24 Mar 2023 20:51:02 +0100 Subject: [PATCH 223/263] Fixes --- .../src/messages_benchmarking.rs | 3 +-- .../src/bridge_hub_rococo_config.rs | 22 +------------------ .../src/bridge_hub_wococo_config.rs | 22 +------------------ .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 6 +++-- 4 files changed, 7 insertions(+), 46 deletions(-) diff --git a/bridges/bin/runtime-common/src/messages_benchmarking.rs b/bridges/bin/runtime-common/src/messages_benchmarking.rs index 5d0ab6df71f..b067523c305 100644 --- a/bridges/bin/runtime-common/src/messages_benchmarking.rs +++ b/bridges/bin/runtime-common/src/messages_benchmarking.rs @@ -21,8 +21,7 @@ use crate::{ messages::{ - source::FromBridgedChainMessagesDeliveryProof, - target::FromBridgedChainMessagesProof, + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, ThisChain, }, messages_generation::{ diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index 97368ebfad0..4b0c3d1aecc 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -91,8 +91,6 @@ const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO: LaneId = LaneId([0, 0, 0, 1]); /// Messaging Bridge configuration for BridgeHubRococo -> BridgeHubWococo pub struct WithBridgeHubWococoMessageBridge; impl MessageBridge for WithBridgeHubWococoMessageBridge { - const THIS_CHAIN_ID: ChainId = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; - const BRIDGED_CHAIN_ID: ChainId = BridgeHubWococoChainId::get(); const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME; type ThisChain = BridgeHubRococo; @@ -120,11 +118,7 @@ impl UnderlyingChainProvider for BridgeHubWococo { type Chain = bp_bridge_hub_wococo::BridgeHubWococo; } -impl messages::BridgedChainWithMessages for BridgeHubWococo { - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { - true - } -} +impl messages::BridgedChainWithMessages for BridgeHubWococo {} /// BridgeHubRococo chain from message lane point of view. #[derive(RuntimeDebug, Clone, Copy)] @@ -136,20 +130,6 @@ impl UnderlyingChainProvider for BridgeHubRococo { impl ThisChainWithMessages for BridgeHubRococo { type RuntimeOrigin = crate::RuntimeOrigin; - type RuntimeCall = crate::RuntimeCall; - - fn is_message_accepted(origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool { - log::info!(target: crate::LOG_TARGET, "[BridgeHubRococo::ThisChainWithMessages] is_message_accepted - origin: {:?}, lane: {:?}", origin, lane); - lane == &DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO - } - - fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { - log::info!( - target: crate::LOG_TARGET, - "[BridgeHubRococo::ThisChainWithMessages] maximal_pending_messages_at_outbound_lane" - ); - MessageNonce::MAX / 2 - } } /// Signed extension that refunds relayers that are delivering messages from the Wococo parachain. diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index d5c21243169..2e010a0ab79 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -91,8 +91,6 @@ const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO: LaneId = LaneId([0, 0, 0, 1]); /// Messaging Bridge configuration for BridgeHubWococo -> BridgeHubRococo pub struct WithBridgeHubRococoMessageBridge; impl MessageBridge for WithBridgeHubRococoMessageBridge { - const THIS_CHAIN_ID: ChainId = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; - const BRIDGED_CHAIN_ID: ChainId = BridgeHubRococoChainId::get(); const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_bridge_hub_wococo::WITH_BRIDGE_HUB_WOCOCO_MESSAGES_PALLET_NAME; type ThisChain = BridgeHubWococo; @@ -120,11 +118,7 @@ impl UnderlyingChainProvider for BridgeHubRococo { type Chain = bp_bridge_hub_rococo::BridgeHubRococo; } -impl messages::BridgedChainWithMessages for BridgeHubRococo { - fn verify_dispatch_weight(_message_payload: &[u8]) -> bool { - true - } -} +impl messages::BridgedChainWithMessages for BridgeHubRococo {} /// BridgeHubWococo chain from message lane point of view. #[derive(RuntimeDebug, Clone, Copy)] @@ -136,20 +130,6 @@ impl UnderlyingChainProvider for BridgeHubWococo { impl ThisChainWithMessages for BridgeHubWococo { type RuntimeOrigin = crate::RuntimeOrigin; - type RuntimeCall = crate::RuntimeCall; - - fn is_message_accepted(origin: &Self::RuntimeOrigin, lane: &LaneId) -> bool { - log::info!(target: crate::LOG_TARGET, "[BridgeHubWococo::ThisChainWithMessages] is_message_accepted - origin: {:?}, lane: {:?}", origin, lane); - lane == &DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO - } - - fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { - log::info!( - target: crate::LOG_TARGET, - "[BridgeHubWococo::ThisChainWithMessages] maximal_pending_messages_at_outbound_lane" - ); - MessageNonce::MAX / 2 - } } /// Signed extension that refunds relayers that are delivering messages from the Rococo parachain. diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index ff3af4b7434..47df1e475be 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -400,6 +400,7 @@ impl pallet_utility::Config for Runtime { /// Add granda bridge pallet to track Wococo relay chain on Rococo BridgeHub pub type BridgeGrandpaWococoInstance = pallet_bridge_grandpa::Instance1; impl pallet_bridge_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; type BridgedChain = bp_wococo::Wococo; type MaxRequests = MaxRequests; type HeadersToKeep = RelayChainHeadersToKeep; @@ -409,6 +410,7 @@ impl pallet_bridge_grandpa::Config for Runtime { /// Add granda bridge pallet to track Rococo relay chain on Wococo BridgeHub pub type BridgeGrandpaRococoInstance = pallet_bridge_grandpa::Instance2; impl pallet_bridge_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; type BridgedChain = bp_rococo::Rococo; type MaxRequests = MaxRequests; type HeadersToKeep = RelayChainHeadersToKeep; @@ -580,12 +582,12 @@ construct_runtime!( // at particular runtime. // With-Wococo bridge modules that are active (used) at Rococo Bridge Hub runtime. - BridgeWococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Config} = 41, + BridgeWococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Event, Config} = 41, BridgeWococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage, Event} = 42, BridgeWococoMessages: pallet_bridge_messages::::{Pallet, Call, Storage, Event, Config} = 46, // With-Rococo bridge modules that are active (used) at Wococo Bridge Hub runtime. - BridgeRococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Config} = 43, + BridgeRococoGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Event, Config} = 43, BridgeRococoParachain: pallet_bridge_parachains::::{Pallet, Call, Storage, Event} = 44, BridgeRococoMessages: pallet_bridge_messages::::{Pallet, Call, Storage, Event, Config} = 45, From 1c733cfda8ceab052ad6e9e7edb0a5c4d29aab66 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 24 Mar 2023 20:57:45 +0100 Subject: [PATCH 224/263] Squashed 'bridges/' changes from 6343a7d37..c1d5990e8 c1d5990e8 Try check-rustdoc pipeline (#1782) git-subtree-dir: bridges git-subtree-split: c1d5990e840b8ee4981beb61a8099271ee629ae5 --- .gitlab-ci.yml | 12 +++++++++++- bin/rialto-parachain/runtime/src/lib.rs | 2 +- bin/runtime-common/src/messages_xcm_extension.rs | 12 ++++++------ primitives/chain-millau/src/lib.rs | 6 +++--- primitives/chain-rialto/src/lib.rs | 6 +++--- primitives/polkadot-core/src/lib.rs | 6 +++--- relays/lib-substrate-relay/src/messages_source.rs | 2 +- relays/lib-substrate-relay/src/messages_target.rs | 2 +- 8 files changed, 29 insertions(+), 19 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f8f29a9eb61..8545cb58761 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -206,7 +206,17 @@ benchmarks-test: # we may live with failing benchmarks, it is just a signal for us allow_failure: true -partial-repo-build-test: +check-rustdoc: + stage: test + <<: *docker-env + <<: *test-refs + variables: + SKIP_WASM_BUILD: 1 + RUSTDOCFLAGS: "-Dwarnings" + script: + - time cargo +nightly doc --workspace --verbose --no-deps --exclude relay-rialto-parachain-client + +partial-repo-pallets-build-test: stage: test <<: *docker-env <<: *nightly-test diff --git a/bin/rialto-parachain/runtime/src/lib.rs b/bin/rialto-parachain/runtime/src/lib.rs index 4e4fcffcd00..b5419e2c25e 100644 --- a/bin/rialto-parachain/runtime/src/lib.rs +++ b/bin/rialto-parachain/runtime/src/lib.rs @@ -16,7 +16,7 @@ //! The Rialto parachain runtime. This can be compiled with `#[no_std]`, ready for Wasm. //! -//! Originally a copy of runtime from https://github.com/substrate-developer-hub/substrate-parachain-template. +//! Originally a copy of runtime from . #![cfg_attr(not(feature = "std"), no_std)] // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. diff --git a/bin/runtime-common/src/messages_xcm_extension.rs b/bin/runtime-common/src/messages_xcm_extension.rs index d4bf2bc1bed..3d802d12fad 100644 --- a/bin/runtime-common/src/messages_xcm_extension.rs +++ b/bin/runtime-common/src/messages_xcm_extension.rs @@ -15,11 +15,11 @@ // along with Parity Bridges Common. If not, see . //! Module provides utilities for easier XCM handling, e.g: -//! [`XcmExecutor`] -> [`MessageSender`] -> +//! `XcmExecutor` -> `MessageSender` -> `OutboundMessageQueue` //! | -//! +//! `Relayer` //! | -//! [`XcmRouter`] <- [`MessageDispatch`] <- +//! `XcmRouter` <- `MessageDispatch` <- `InboundMessageQueue` use bp_messages::{ source_chain::MessagesBridge, @@ -136,7 +136,7 @@ pub trait XcmBlobHauler { /// Runtime message sender adapter. type MessageSender: MessagesBridge; - /// Runtime message sender origin, which is used by [`MessageSender`]. + /// Runtime message sender origin, which is used by [`Self::MessageSender`]. type MessageSenderOrigin; /// Our location within the Consensus Universe. fn message_sender_origin() -> Self::MessageSenderOrigin; @@ -145,8 +145,8 @@ pub trait XcmBlobHauler { fn xcm_lane() -> LaneId; } -/// XCM bridge adapter which connects [`XcmBlobHauler`] with [`MessageSender`] and makes sure that -/// XCM blob is sent to the [`pallet_bridge_messages`] queue to be relayed. +/// XCM bridge adapter which connects [`XcmBlobHauler`] with [`XcmBlobHauler::MessageSender`] and +/// makes sure that XCM blob is sent to the [`pallet_bridge_messages`] queue to be relayed. pub struct XcmBlobHaulerAdapter(sp_std::marker::PhantomData); impl> HaulBlob for XcmBlobHaulerAdapter diff --git a/primitives/chain-millau/src/lib.rs b/primitives/chain-millau/src/lib.rs index 81ed5436263..b7f67ca07e2 100644 --- a/primitives/chain-millau/src/lib.rs +++ b/primitives/chain-millau/src/lib.rs @@ -86,12 +86,12 @@ pub const MAX_AUTHORITIES_COUNT: u32 = 5; /// Reasonable number of headers in the `votes_ancestries` on Millau chain. /// -/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; /// Approximate average header size in `votes_ancestries` field of justification on Millau chain. /// -/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 256; /// Approximate maximal header size on Millau chain. @@ -100,7 +100,7 @@ pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 256; /// engine (GRANDPA, Babe, BEEFY, ...) - so we multiply it by 3. And also /// `AVERAGE_HEADER_SIZE_IN_JUSTIFICATION` bytes for other stuff. /// -/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. pub const MAX_HEADER_SIZE: u32 = MAX_AUTHORITIES_COUNT .saturating_mul(3) .saturating_add(AVERAGE_HEADER_SIZE_IN_JUSTIFICATION); diff --git a/primitives/chain-rialto/src/lib.rs b/primitives/chain-rialto/src/lib.rs index a1c4a7267a7..01349f131be 100644 --- a/primitives/chain-rialto/src/lib.rs +++ b/primitives/chain-rialto/src/lib.rs @@ -75,12 +75,12 @@ pub const MAX_AUTHORITIES_COUNT: u32 = 5; /// Reasonable number of headers in the `votes_ancestries` on Rialto chain. /// -/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; /// Approximate average header size in `votes_ancestries` field of justification on Rialto chain. /// -/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 256; /// Approximate maximal header size on Rialto chain. @@ -89,7 +89,7 @@ pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 256; /// engine (GRANDPA, Babe, BEEFY, ...) - so we multiply it by 3. And also /// `AVERAGE_HEADER_SIZE_IN_JUSTIFICATION` bytes for other stuff. /// -/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. pub const MAX_HEADER_SIZE: u32 = MAX_AUTHORITIES_COUNT .saturating_mul(3) .saturating_add(AVERAGE_HEADER_SIZE_IN_JUSTIFICATION); diff --git a/primitives/polkadot-core/src/lib.rs b/primitives/polkadot-core/src/lib.rs index eae2d49a6fd..3774d283fc4 100644 --- a/primitives/polkadot-core/src/lib.rs +++ b/primitives/polkadot-core/src/lib.rs @@ -54,7 +54,7 @@ pub const MAX_AUTHORITIES_COUNT: u32 = 1_256; /// Reasonable number of headers in the `votes_ancestries` on Polkadot-like chains. /// -/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. /// /// This value comes from recent (February, 2023) Kusama and Polkadot headers. There are no /// justifications with any additional headers in votes ancestry, so reasonable headers may @@ -65,7 +65,7 @@ pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 2; /// Approximate average header size in `votes_ancestries` field of justification on Polkadot-like /// chains. /// -/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. /// /// This value comes from recent (February, 2023) Kusama headers. Average is `336` there, but some /// non-mandatory headers has size `40kb` (they contain the BABE epoch descriptor with all @@ -75,7 +75,7 @@ pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 20 * 1024; /// Approximate maximal header size on Polkadot-like chains. /// -/// See [`bp_header_chain::ChainWithGrandpa`] for more details. +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. /// /// This value comes from recent (February, 2023) Kusama headers. Maximal header is a mandatory /// header. In its SCALE-encoded form it is `80348` bytes. Let's have some reserve here. diff --git a/relays/lib-substrate-relay/src/messages_source.rs b/relays/lib-substrate-relay/src/messages_source.rs index a151af8ee82..de795beb787 100644 --- a/relays/lib-substrate-relay/src/messages_source.rs +++ b/relays/lib-substrate-relay/src/messages_source.rs @@ -16,7 +16,7 @@ //! Substrate client as Substrate messages source. The chain we connect to should have //! runtime that implements `HeaderApi` to allow bridging with -//! chain. +//! `` chain. use crate::{ messages_lane::{ diff --git a/relays/lib-substrate-relay/src/messages_target.rs b/relays/lib-substrate-relay/src/messages_target.rs index 7b0d9d63fb7..68b68b19936 100644 --- a/relays/lib-substrate-relay/src/messages_target.rs +++ b/relays/lib-substrate-relay/src/messages_target.rs @@ -16,7 +16,7 @@ //! Substrate client as Substrate messages target. The chain we connect to should have //! runtime that implements `HeaderApi` to allow bridging with -//! chain. +//! `` chain. use crate::{ messages_lane::{ From 6283a066f7a8d21cac44ac74d0f59aa64fff7bb7 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 24 Mar 2023 21:03:00 +0100 Subject: [PATCH 225/263] Removed imports --- .../bridge-hub-rococo/src/bridge_hub_rococo_config.rs | 3 +-- .../bridge-hub-rococo/src/bridge_hub_wococo_config.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index 4b0c3d1aecc..1dd47bdb17f 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -20,8 +20,7 @@ use crate::{ BridgeParachainWococoInstance, ParachainInfo, Runtime, WithBridgeHubWococoMessagesInstance, XcmRouter, }; -use bp_messages::{LaneId, MessageNonce}; -use bp_runtime::ChainId; +use bp_messages::LaneId; use bridge_runtime_common::{ messages, messages::{ diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 2e010a0ab79..7f756abee99 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -20,8 +20,7 @@ use crate::{ BridgeParachainRococoInstance, ParachainInfo, Runtime, WithBridgeHubRococoMessagesInstance, XcmRouter, }; -use bp_messages::{LaneId, MessageNonce}; -use bp_runtime::ChainId; +use bp_messages::LaneId; use bridge_runtime_common::{ messages, messages::{ From 0cdf9af30d51ca8bd1f89e8a767ccfe87bbfd64e Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 24 Mar 2023 21:39:15 +0100 Subject: [PATCH 226/263] Fix compile --- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 47df1e475be..ba5db0007f2 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -1229,8 +1229,9 @@ mod tests { ), ); - let bhr_indirect_payload = - bp_bridge_hub_rococo::rewarding_bridge_signed_extension::from_params( + { + use bp_bridge_hub_rococo::BridgeHubSignedExtension; + let bhr_indirect_payload = bp_bridge_hub_rococo::SignedExtension::from_params( 10, 10, TransactionEra::Immortal, @@ -1238,10 +1239,12 @@ mod tests { 10, 10, ); - assert_eq!(payload.encode(), bhr_indirect_payload.encode()); + assert_eq!(payload.encode(), bhr_indirect_payload.encode()); + } - let bhw_indirect_payload = - bp_bridge_hub_wococo::rewarding_bridge_signed_extension::from_params( + { + use bp_bridge_hub_wococo::BridgeHubSignedExtension; + let bhw_indirect_payload = bp_bridge_hub_wococo::SignedExtension::from_params( 10, 10, TransactionEra::Immortal, @@ -1249,6 +1252,7 @@ mod tests { 10, 10, ); - assert_eq!(payload.encode(), bhw_indirect_payload.encode()); + assert_eq!(payload.encode(), bhw_indirect_payload.encode()); + } } } From ff63023aafa90ded03193f4d7c510960341b3cda Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 27 Mar 2023 10:33:55 +0300 Subject: [PATCH 227/263] fixed benchmarks compilation --- .../runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index ba5db0007f2..f465378bb5b 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -995,9 +995,8 @@ impl_runtime_apis! { impl BridgeMessagesConfig for Runtime { fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { - use bridge_runtime_common::messages::MessageBridge; let bench_lane_id = >::bench_lane_id(); - let bridged_chain_id = bridge_hub_rococo_config::WithBridgeHubWococoMessageBridge::BRIDGED_CHAIN_ID; + let bridged_chain_id = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; pallet_bridge_relayers::Pallet::::relayer_reward( relayer, bp_relayers::RewardsAccountParams::new( @@ -1039,9 +1038,8 @@ impl_runtime_apis! { impl BridgeMessagesConfig for Runtime { fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { - use bridge_runtime_common::messages::MessageBridge; let bench_lane_id = >::bench_lane_id(); - let bridged_chain_id = bridge_hub_wococo_config::WithBridgeHubRococoMessageBridge::BRIDGED_CHAIN_ID; + let bridged_chain_id = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; pallet_bridge_relayers::Pallet::::relayer_reward( relayer, bp_relayers::RewardsAccountParams::new( From 8c48e4d6b139ea9545dc74f0298a978b1269da10 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 27 Mar 2023 10:49:53 +0300 Subject: [PATCH 228/263] fix rustdoc --- bridges/modules/grandpa/src/benchmarking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bridges/modules/grandpa/src/benchmarking.rs b/bridges/modules/grandpa/src/benchmarking.rs index 5b9faea2a99..78c7fc362a2 100644 --- a/bridges/modules/grandpa/src/benchmarking.rs +++ b/bridges/modules/grandpa/src/benchmarking.rs @@ -30,8 +30,8 @@ //! //! Consider the following: //! -//! / [B'] <- [C'] -//! [A] <- [B] <- [C] +//! / B <- C' +//! A <- B <- C //! //! The common ancestor of both forks is block A, so this is what GRANDPA will finalize. In order to //! verify this we will have vote ancestries of `[B, C, B', C']` and pre-commits `[C, C']`. From 245daafbffeb4f6cd8dcabf4a7c57fd387f761fb Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 27 Mar 2023 10:24:48 +0200 Subject: [PATCH 229/263] Squashed 'bridges/' changes from c1d5990e8..ecddd4a31 ecddd4a31 Rust cargo doc for all features (#1995) e0997c14d Fix gitlab-check (#1994) 5284850ef Bump clap from 4.1.11 to 4.1.13 743cd60df Bump sysinfo from 0.28.3 to 0.28.4 dc322bae2 Bump async-trait from 0.1.67 to 0.1.68 git-subtree-dir: bridges git-subtree-split: ecddd4a315470d85135aafbdb96753af9b07b854 --- .gitlab-ci.yml | 2 +- Cargo.lock | 598 +++++++++++++++++++++--- bin/millau/node/Cargo.toml | 2 +- bin/rialto-parachain/node/Cargo.toml | 2 +- bin/rialto-parachain/runtime/Cargo.toml | 4 +- bin/rialto/node/Cargo.toml | 3 +- modules/grandpa/src/benchmarking.rs | 4 +- relays/parachains/Cargo.toml | 2 +- 8 files changed, 554 insertions(+), 63 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8545cb58761..f3ba2243c7f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -214,7 +214,7 @@ check-rustdoc: SKIP_WASM_BUILD: 1 RUSTDOCFLAGS: "-Dwarnings" script: - - time cargo +nightly doc --workspace --verbose --no-deps --exclude relay-rialto-parachain-client + - time cargo +nightly doc --workspace --verbose --no-deps --all-features --exclude relay-rialto-parachain-client partial-repo-pallets-build-test: stage: test diff --git a/Cargo.lock b/Cargo.lock index 0ac84c43688..20d18112ad1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -453,13 +453,13 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.67" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ea188f25f0255d8f92797797c97ebf5631fa88178beb1a46fdf5622c9a00e4" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2 1.0.53", "quote 1.0.26", - "syn 2.0.8", + "syn 2.0.10", ] [[package]] @@ -599,7 +599,7 @@ version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" dependencies = [ - "bitflags 1.3.2", + "bitflags", "cexpr", "clang-sys", "lazy_static", @@ -619,12 +619,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bitflags" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" - [[package]] name = "bitvec" version = "1.0.1" @@ -1403,7 +1397,7 @@ checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", - "bitflags 1.3.2", + "bitflags", "strsim 0.8.0", "textwrap", "unicode-width", @@ -1412,11 +1406,11 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.11" +version = "4.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42dfd32784433290c51d92c438bb72ea5063797fc3cc9a21a8c4346bebbb2098" +checksum = "3c911b090850d79fc64fe9ea01e28e465f65e821e08813ced95bced72f7a8a9b" dependencies = [ - "bitflags 2.0.2", + "bitflags", "clap_derive", "clap_lex", "is-terminal", @@ -1427,15 +1421,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.1.9" +version = "4.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fddf67631444a3a3e3e5ac51c36a5e01335302de677bd78759eaa90ab1f46644" +checksum = "9a932373bab67b984c790ddf2c9ca295d8e3af3b7ef92de5a5bacdccdee4b09b" dependencies = [ "heck 0.4.1", - "proc-macro-error", "proc-macro2 1.0.53", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.10", ] [[package]] @@ -1830,7 +1823,7 @@ name = "cumulus-client-cli" version = "0.1.0" source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" dependencies = [ - "clap 4.1.11", + "clap 4.1.13", "parity-scale-codec", "sc-chain-spec", "sc-cli", @@ -2374,7 +2367,7 @@ dependencies = [ "proc-macro2 1.0.53", "quote 1.0.26", "scratch", - "syn 2.0.8", + "syn 2.0.10", ] [[package]] @@ -2391,7 +2384,7 @@ checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f" dependencies = [ "proc-macro2 1.0.53", "quote 1.0.26", - "syn 2.0.8", + "syn 2.0.10", ] [[package]] @@ -2859,7 +2852,7 @@ checksum = "48016319042fb7c87b78d2993084a831793a897a5cd1a2a67cab9d1eeb4b7d76" dependencies = [ "proc-macro2 1.0.53", "quote 1.0.26", - "syn 2.0.8", + "syn 2.0.10", ] [[package]] @@ -3227,7 +3220,7 @@ dependencies = [ "Inflector", "array-bytes 4.2.0", "chrono", - "clap 4.1.11", + "clap 4.1.13", "comfy-table", "frame-benchmarking", "frame-support", @@ -3343,7 +3336,7 @@ name = "frame-support" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ - "bitflags 1.3.2", + "bitflags", "environmental", "frame-metadata", "frame-support-procedural", @@ -4517,6 +4510,118 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "kusama-runtime" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +dependencies = [ + "bitvec", + "frame-benchmarking", + "frame-election-provider-support", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal", + "kusama-runtime-constants", + "log", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-bags-list", + "pallet-balances", + "pallet-bounties", + "pallet-child-bounties", + "pallet-collective", + "pallet-conviction-voting", + "pallet-democracy", + "pallet-election-provider-multi-phase", + "pallet-election-provider-support-benchmarking", + "pallet-elections-phragmen", + "pallet-fast-unstake", + "pallet-grandpa", + "pallet-identity", + "pallet-im-online", + "pallet-indices", + "pallet-membership", + "pallet-multisig", + "pallet-nis", + "pallet-nomination-pools", + "pallet-nomination-pools-benchmarking", + "pallet-nomination-pools-runtime-api", + "pallet-offences", + "pallet-offences-benchmarking", + "pallet-preimage", + "pallet-proxy", + "pallet-ranked-collective", + "pallet-recovery", + "pallet-referenda", + "pallet-scheduler", + "pallet-session", + "pallet-session-benchmarking", + "pallet-society", + "pallet-staking", + "pallet-staking-runtime-api", + "pallet-timestamp", + "pallet-tips", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-vesting", + "pallet-whitelist", + "pallet-xcm", + "pallet-xcm-benchmarks", + "parity-scale-codec", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "rustc-hex", + "scale-info", + "serde", + "serde_derive", + "smallvec", + "sp-api", + "sp-arithmetic", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-core", + "sp-inherents", + "sp-io", + "sp-mmr-primitives", + "sp-npos-elections", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", + "sp-transaction-pool", + "sp-version", + "static_assertions", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "kusama-runtime-constants" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +dependencies = [ + "frame-support", + "polkadot-primitives", + "polkadot-runtime-common", + "smallvec", + "sp-core", + "sp-runtime", + "sp-weights", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -5404,7 +5509,7 @@ dependencies = [ name = "millau-bridge-node" version = "0.1.0" dependencies = [ - "clap 4.1.11", + "clap 4.1.13", "frame-benchmarking", "frame-benchmarking-cli", "jsonrpsee", @@ -5768,7 +5873,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" dependencies = [ "anyhow", - "bitflags 1.3.2", + "bitflags", "byteorder", "libc", "netlink-packet-core", @@ -5821,7 +5926,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ - "bitflags 1.3.2", + "bitflags", "cfg-if 1.0.0", "libc", "memoffset 0.6.5", @@ -5832,7 +5937,7 @@ name = "node-inspect" version = "0.9.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ - "clap 4.1.11", + "clap 4.1.13", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -6657,6 +6762,22 @@ dependencies = [ "sp-std 5.0.0", ] +[[package]] +name = "pallet-nis" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + [[package]] name = "pallet-nomination-pools" version = "1.0.0" @@ -6674,6 +6795,26 @@ dependencies = [ "sp-std 5.0.0", ] +[[package]] +name = "pallet-nomination-pools-benchmarking" +version = "1.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "pallet-bags-list", + "pallet-nomination-pools", + "pallet-staking", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-runtime-interface", + "sp-staking", + "sp-std 5.0.0", +] + [[package]] name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" @@ -6702,6 +6843,30 @@ dependencies = [ "sp-std 5.0.0", ] +[[package]] +name = "pallet-offences-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-babe", + "pallet-balances", + "pallet-grandpa", + "pallet-im-online", + "pallet-offences", + "pallet-session", + "pallet-staking", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", +] + [[package]] name = "pallet-preimage" version = "4.0.0-dev" @@ -6734,11 +6899,45 @@ dependencies = [ "sp-std 5.0.0", ] +[[package]] +name = "pallet-ranked-collective" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-recovery" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + [[package]] name = "pallet-referenda" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ + "assert_matches", "frame-benchmarking", "frame-support", "frame-system", @@ -6790,6 +6989,22 @@ dependencies = [ "sp-trie", ] +[[package]] +name = "pallet-session-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-session", + "pallet-staking", + "rand 0.8.5", + "sp-runtime", + "sp-session", + "sp-std 5.0.0", +] + [[package]] name = "pallet-shift-session-manager" version = "0.1.0" @@ -6805,6 +7020,20 @@ dependencies = [ "sp-std 5.0.0", ] +[[package]] +name = "pallet-society" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "rand_chacha 0.2.2", + "scale-info", + "sp-runtime", + "sp-std 5.0.0", +] + [[package]] name = "pallet-staking" version = "4.0.0-dev" @@ -6818,6 +7047,7 @@ dependencies = [ "pallet-authorship", "pallet-session", "parity-scale-codec", + "rand_chacha 0.2.2", "scale-info", "serde", "sp-application-crypto", @@ -6856,6 +7086,23 @@ dependencies = [ "sp-api", ] +[[package]] +name = "pallet-state-trie-migration" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + [[package]] name = "pallet-sudo" version = "4.0.0-dev" @@ -7035,6 +7282,25 @@ dependencies = [ "xcm-executor", ] +[[package]] +name = "pallet-xcm-benchmarks" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "xcm", + "xcm-builder", + "xcm-executor", +] + [[package]] name = "parachain-info" version = "0.1.0" @@ -7466,7 +7732,7 @@ name = "polkadot-cli" version = "0.9.39" source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ - "clap 4.1.11", + "clap 4.1.13", "frame-benchmarking-cli", "futures", "log", @@ -8173,12 +8439,15 @@ version = "0.9.39" source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", + "frame-benchmarking", "frame-election-provider-support", "frame-executive", "frame-support", "frame-system", + "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", + "hex-literal", "log", "pallet-authority-discovery", "pallet-authorship", @@ -8191,6 +8460,7 @@ dependencies = [ "pallet-conviction-voting", "pallet-democracy", "pallet-election-provider-multi-phase", + "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-grandpa", @@ -8200,13 +8470,16 @@ dependencies = [ "pallet-membership", "pallet-multisig", "pallet-nomination-pools", + "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", + "pallet-offences-benchmarking", "pallet-preimage", "pallet-proxy", "pallet-referenda", "pallet-scheduler", "pallet-session", + "pallet-session-benchmarking", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-runtime-api", @@ -8260,6 +8533,7 @@ version = "0.9.39" source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "bitvec", + "frame-benchmarking", "frame-election-provider-support", "frame-support", "frame-system", @@ -8267,6 +8541,7 @@ dependencies = [ "libsecp256k1", "log", "pallet-authorship", + "pallet-babe", "pallet-balances", "pallet-election-provider-multi-phase", "pallet-fast-unstake", @@ -8329,9 +8604,10 @@ name = "polkadot-runtime-parachains" version = "0.9.39" source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ - "bitflags 1.3.2", + "bitflags", "bitvec", "derive_more", + "frame-benchmarking", "frame-support", "frame-system", "log", @@ -8353,6 +8629,7 @@ dependencies = [ "scale-info", "serde", "sp-api", + "sp-application-crypto", "sp-core", "sp-inherents", "sp-io", @@ -8361,6 +8638,7 @@ dependencies = [ "sp-session", "sp-staking", "sp-std 5.0.0", + "static_assertions", "xcm", "xcm-executor", ] @@ -8376,6 +8654,7 @@ dependencies = [ "frame-system-rpc-runtime-api", "futures", "hex-literal", + "kusama-runtime", "kvdb", "kvdb-rocksdb", "log", @@ -8421,6 +8700,7 @@ dependencies = [ "polkadot-runtime-constants", "polkadot-runtime-parachains", "polkadot-statement-distribution", + "rococo-runtime", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", @@ -8469,6 +8749,7 @@ dependencies = [ "substrate-prometheus-endpoint", "thiserror", "tracing-gum", + "westend-runtime", ] [[package]] @@ -8509,7 +8790,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e1f879b2998099c2d69ab9605d145d5b661195627eccc680002c4918a7fb6fa" dependencies = [ "autocfg", - "bitflags 1.3.2", + "bitflags", "cfg-if 1.0.0", "concurrent-queue", "libc", @@ -9021,7 +9302,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] @@ -9065,7 +9346,7 @@ checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" dependencies = [ "proc-macro2 1.0.53", "quote 1.0.26", - "syn 2.0.8", + "syn 2.0.10", ] [[package]] @@ -9112,7 +9393,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" dependencies = [ - "bitflags 1.3.2", + "bitflags", "libc", "mach", "winapi", @@ -9410,7 +9691,7 @@ dependencies = [ name = "rialto-bridge-node" version = "0.1.0" dependencies = [ - "clap 4.1.11", + "clap 4.1.13", "frame-benchmarking", "frame-benchmarking-cli", "frame-support", @@ -9437,7 +9718,7 @@ dependencies = [ name = "rialto-parachain-collator" version = "0.1.0" dependencies = [ - "clap 4.1.11", + "clap 4.1.13", "cumulus-client-cli", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", @@ -9637,6 +9918,106 @@ dependencies = [ "librocksdb-sys", ] +[[package]] +name = "rococo-runtime" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +dependencies = [ + "binary-merkle-tree", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal", + "log", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", + "pallet-bounties", + "pallet-child-bounties", + "pallet-collective", + "pallet-democracy", + "pallet-elections-phragmen", + "pallet-grandpa", + "pallet-identity", + "pallet-im-online", + "pallet-indices", + "pallet-membership", + "pallet-mmr", + "pallet-multisig", + "pallet-nis", + "pallet-offences", + "pallet-preimage", + "pallet-proxy", + "pallet-recovery", + "pallet-scheduler", + "pallet-session", + "pallet-society", + "pallet-staking", + "pallet-state-trie-migration", + "pallet-sudo", + "pallet-timestamp", + "pallet-tips", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-vesting", + "pallet-xcm", + "pallet-xcm-benchmarks", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "rococo-runtime-constants", + "scale-info", + "serde", + "serde_derive", + "smallvec", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-core", + "sp-inherents", + "sp-io", + "sp-mmr-primitives", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", + "sp-transaction-pool", + "sp-version", + "static_assertions", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "rococo-runtime-constants" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +dependencies = [ + "frame-support", + "polkadot-primitives", + "polkadot-runtime-common", + "smallvec", + "sp-core", + "sp-runtime", + "sp-weights", +] + [[package]] name = "rpassword" version = "7.2.0" @@ -9740,7 +10121,7 @@ version = "0.35.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" dependencies = [ - "bitflags 1.3.2", + "bitflags", "errno 0.2.8", "io-lifetimes 0.7.5", "libc", @@ -9754,7 +10135,7 @@ version = "0.36.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" dependencies = [ - "bitflags 1.3.2", + "bitflags", "errno 0.2.8", "io-lifetimes 1.0.9", "libc", @@ -9768,7 +10149,7 @@ version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2" dependencies = [ - "bitflags 1.3.2", + "bitflags", "errno 0.3.0", "io-lifetimes 1.0.9", "libc", @@ -9977,7 +10358,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d dependencies = [ "array-bytes 4.2.0", "chrono", - "clap 4.1.11", + "clap 4.1.13", "fdlimit", "futures", "libp2p", @@ -10497,7 +10878,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d dependencies = [ "array-bytes 4.2.0", "async-trait", - "bitflags 1.3.2", + "bitflags", "bytes", "futures", "futures-timer", @@ -10838,7 +11219,7 @@ name = "sc-storage-monitor" version = "0.1.0" source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ - "clap 4.1.11", + "clap 4.1.13", "fs4", "futures", "log", @@ -11213,7 +11594,7 @@ version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ - "bitflags 1.3.2", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -11271,7 +11652,7 @@ checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" dependencies = [ "proc-macro2 1.0.53", "quote 1.0.26", - "syn 2.0.8", + "syn 2.0.10", ] [[package]] @@ -11771,7 +12152,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d dependencies = [ "array-bytes 4.2.0", "base58", - "bitflags 1.3.2", + "bitflags", "blake2", "bounded-collections", "dyn-clonable", @@ -12340,7 +12721,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6" dependencies = [ - "bitflags 1.3.2", + "bitflags", "cfg_aliases", "libc", "parking_lot 0.11.2", @@ -12789,9 +13170,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.8" +version = "2.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +checksum = "5aad1363ed6d37b84299588d62d3a7d95b5a5c2d9aad5c85609fda12afaa1f40" dependencies = [ "proc-macro2 1.0.53", "quote 1.0.26", @@ -12812,9 +13193,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.28.3" +version = "0.28.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f69e0d827cce279e61c2f3399eb789271a8f136d8245edef70f06e3c9601a670" +checksum = "b4c2f3ca6693feb29a89724516f016488e9aafc7f37264f898593ee4b942f31b" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys", @@ -12831,7 +13212,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" dependencies = [ - "bitflags 1.3.2", + "bitflags", "core-foundation", "system-configuration-sys", ] @@ -12912,7 +13293,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2 1.0.53", "quote 1.0.26", - "syn 2.0.8", + "syn 2.0.10", ] [[package]] @@ -13179,7 +13560,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ - "bitflags 1.3.2", + "bitflags", "bytes", "futures-core", "futures-util", @@ -13394,7 +13775,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ "async-trait", - "clap 4.1.11", + "clap 4.1.13", "frame-remote-externalities", "hex", "log", @@ -14272,7 +14653,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" dependencies = [ "async-trait", - "bitflags 1.3.2", + "bitflags", "bytes", "cc", "ipnet", @@ -14286,6 +14667,112 @@ dependencies = [ "winapi", ] +[[package]] +name = "westend-runtime" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +dependencies = [ + "bitvec", + "frame-benchmarking", + "frame-election-provider-support", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal", + "log", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-bags-list", + "pallet-balances", + "pallet-collective", + "pallet-democracy", + "pallet-election-provider-multi-phase", + "pallet-election-provider-support-benchmarking", + "pallet-elections-phragmen", + "pallet-fast-unstake", + "pallet-grandpa", + "pallet-identity", + "pallet-im-online", + "pallet-indices", + "pallet-membership", + "pallet-multisig", + "pallet-nomination-pools", + "pallet-nomination-pools-benchmarking", + "pallet-nomination-pools-runtime-api", + "pallet-offences", + "pallet-offences-benchmarking", + "pallet-preimage", + "pallet-proxy", + "pallet-recovery", + "pallet-scheduler", + "pallet-session", + "pallet-session-benchmarking", + "pallet-society", + "pallet-staking", + "pallet-staking-reward-curve", + "pallet-staking-runtime-api", + "pallet-state-trie-migration", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-vesting", + "pallet-xcm", + "pallet-xcm-benchmarks", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "rustc-hex", + "scale-info", + "serde", + "serde_derive", + "smallvec", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-core", + "sp-inherents", + "sp-io", + "sp-mmr-primitives", + "sp-npos-elections", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", + "sp-transaction-pool", + "sp-version", + "substrate-wasm-builder", + "westend-runtime-constants", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "westend-runtime-constants" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +dependencies = [ + "frame-support", + "polkadot-primitives", + "polkadot-runtime-common", + "smallvec", + "sp-core", + "sp-runtime", + "sp-weights", +] + [[package]] name = "which" version = "4.4.0" @@ -14606,6 +15093,7 @@ version = "0.9.39" source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" dependencies = [ "environmental", + "frame-benchmarking", "frame-support", "impl-trait-for-tuples", "log", diff --git a/bin/millau/node/Cargo.toml b/bin/millau/node/Cargo.toml index 063fedc5e64..cfe6e921f4b 100644 --- a/bin/millau/node/Cargo.toml +++ b/bin/millau/node/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.1.11", features = ["derive"] } +clap = { version = "4.1.13", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } serde_json = "1.0.94" diff --git a/bin/rialto-parachain/node/Cargo.toml b/bin/rialto-parachain/node/Cargo.toml index 84e804f484e..c60bad8386a 100644 --- a/bin/rialto-parachain/node/Cargo.toml +++ b/bin/rialto-parachain/node/Cargo.toml @@ -17,7 +17,7 @@ default = [] runtime-benchmarks = ['rialto-parachain-runtime/runtime-benchmarks'] [dependencies] -clap = { version = "4.1.11", features = ["derive"] } +clap = { version = "4.1.13", features = ["derive"] } log = '0.4.17' codec = { package = 'parity-scale-codec', version = '3.1.5' } serde = { version = '1.0', features = ['derive'] } diff --git a/bin/rialto-parachain/runtime/Cargo.toml b/bin/rialto-parachain/runtime/Cargo.toml index ed193b4e681..0018c8f3905 100644 --- a/bin/rialto-parachain/runtime/Cargo.toml +++ b/bin/rialto-parachain/runtime/Cargo.toml @@ -83,10 +83,12 @@ runtime-benchmarks = [ 'sp-runtime/runtime-benchmarks', 'frame-benchmarking', 'frame-support/runtime-benchmarks', - 'frame-system-benchmarking', + 'frame-system-benchmarking/runtime-benchmarks', 'frame-system/runtime-benchmarks', 'pallet-balances/runtime-benchmarks', 'pallet-timestamp/runtime-benchmarks', + 'pallet-xcm/runtime-benchmarks', + 'xcm-builder/runtime-benchmarks', ] std = [ "bp-messages/std", diff --git a/bin/rialto/node/Cargo.toml b/bin/rialto/node/Cargo.toml index 8658dd95ec5..bc8841c4f87 100644 --- a/bin/rialto/node/Cargo.toml +++ b/bin/rialto/node/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.1.11", features = ["derive"] } +clap = { version = "4.1.13", features = ["derive"] } serde_json = "1.0.94" # Bridge dependencies @@ -45,5 +45,6 @@ frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", bran [features] default = [] runtime-benchmarks = [ + "polkadot-service/runtime-benchmarks", "rialto-runtime/runtime-benchmarks", ] diff --git a/modules/grandpa/src/benchmarking.rs b/modules/grandpa/src/benchmarking.rs index 5b9faea2a99..78c7fc362a2 100644 --- a/modules/grandpa/src/benchmarking.rs +++ b/modules/grandpa/src/benchmarking.rs @@ -30,8 +30,8 @@ //! //! Consider the following: //! -//! / [B'] <- [C'] -//! [A] <- [B] <- [C] +//! / B <- C' +//! A <- B <- C //! //! The common ancestor of both forks is block A, so this is what GRANDPA will finalize. In order to //! verify this we will have vote ancestries of `[B, C, B', C']` and pre-commits `[C, C']`. diff --git a/relays/parachains/Cargo.toml b/relays/parachains/Cargo.toml index d683c131b6e..80df2311bc9 100644 --- a/relays/parachains/Cargo.toml +++ b/relays/parachains/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] async-std = "1.6.5" -async-trait = "0.1.67" +async-trait = "0.1.68" futures = "0.3.27" log = "0.4.17" relay-utils = { path = "../utils" } From 2b4467cf6c20a5ce62ec908167f8463d34225eda Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 27 Mar 2023 11:49:26 +0200 Subject: [PATCH 230/263] Updated scripts for transfer assets --- parachains/runtimes/bridge-hubs/README.md | 17 + scripts/bridges_rococo_wococo.sh | 531 +++++++++------------ scripts/generate_hex_encoded_call/index.js | 64 ++- 3 files changed, 291 insertions(+), 321 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index c7f8378ad04..788e767f0fc 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -169,6 +169,23 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ### Send messages (Rococo, Wococo) +#### Transfer assets via bridge + +1. allow bridge transfer on statemine/westmint (governance-like): + ``` + ./scripts/bridges_rococo_wococo.sh allow-transfers-local + ``` + +2. do transfer from statemine to westmint + ``` + ./scripts/bridges_rococo_wococo.sh transfer-asset-from-statemine-local + ``` + +#### Ping via bridge +``` +./scripts/bridges_rococo_wococo.sh allow-transfers-local +./scripts/bridges_rococo_wococo.sh ping-via-bridge-from-statemine-local +``` #### Local Rococo:Statemine -> Wococo:Westmint - check that relayers are up and running (see above) diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh index a9375b8150d..e5c19d3004e 100755 --- a/scripts/bridges_rococo_wococo.sh +++ b/scripts/bridges_rococo_wococo.sh @@ -74,65 +74,70 @@ function generate_hex_encoded_call_data() { return $retVal } -STATEMINE_ACCOUNT_SEED_FOR_LOCAL="//Alice" -ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO="scatter feed race company oxygen trip extra elbow slot bundle auto canoe" - -function send_xcm_transact_remark_from_statemine() { - local url=$1 +function transfer_balance() { + local runtime_para_endpoint=$1 local seed=$2 - local bridge_hub_para_id=$3 - local target_network=$4 - local target_network_para_id=$5 - local target_network_para_endpoint=$6 - echo " calling send_xcm_transact_remark_from_statemine:" - echo " url: ${url}" + local target_account=$3 + local amount=$4 + echo " calling transfer_balance:" + echo " runtime_para_endpoint: ${runtime_para_endpoint}" echo " seed: ${seed}" - echo " bridge_hub_para_id: ${bridge_hub_para_id}" - echo " target_network: ${target_network}" - echo " target_network_para_id: ${target_network_para_id}" - echo " target_network_para_endpoint: ${target_network_para_endpoint}" - echo " params:" + echo " target_account: ${target_account}" + echo " amount: ${amount}" + echo "--------------------------------------------------" + + polkadot-js-api \ + --ws "${runtime_para_endpoint}" \ + --seed "${seed?}" \ + tx.balances.transfer \ + "${target_account}" \ + "${amount}" +} - # generate data for Transact on the other side (Westmint) - local tmp_file=$(mktemp) - generate_hex_encoded_call_data "remark-with-event" "${target_network_para_endpoint}" "${tmp_file}" - local hex_encoded_data=$(cat $tmp_file) +function send_governance_transact() { + local relay_url=$1 + local relay_chain_seed=$2 + local para_id=$3 + local hex_encoded_data=$4 + local require_weight_at_most_ref_time=$5 + local require_weight_at_most_proof_size=$6 + echo " calling send_governance_transact:" + echo " relay_url: ${relay_url}" + echo " relay_chain_seed: ${relay_chain_seed}" + echo " para_id: ${para_id}" + echo " hex_encoded_data: ${hex_encoded_data}" + echo " require_weight_at_most_ref_time: ${require_weight_at_most_ref_time}" + echo " require_weight_at_most_proof_size: ${require_weight_at_most_proof_size}" + echo " params:" local dest=$(jq --null-input \ - --arg bridge_hub_para_id "$bridge_hub_para_id" \ - '{ "V3": { "parents": 1, "interior": { "X1": { "Parachain": $bridge_hub_para_id } } } }') + --arg para_id "$para_id" \ + '{ "V3": { "parents": 0, "interior": { "X1": { "Parachain": $para_id } } } }') local message=$(jq --null-input \ - --arg target_network "$target_network" \ - --arg target_network_para_id "$target_network_para_id" \ --argjson hex_encoded_data $hex_encoded_data \ + --arg require_weight_at_most_ref_time "$require_weight_at_most_ref_time" \ + --arg require_weight_at_most_proof_size "$require_weight_at_most_proof_size" \ ' { "V3": [ - { - "ExportMessage": { - "network": $target_network, - "destination": { - "X1": { - "Parachain": $target_network_para_id - } - }, - "xcm": [ + { + "UnpaidExecution": { + "weight_limit": "Unlimited" + } + }, { "Transact": { - "origin_kind": "SovereignAccount", + "origin_kind": "Superuser", "require_weight_at_most": { - "ref_time": 1000000000, - "proof_size": 0, + "ref_time": $require_weight_at_most_ref_time, + "proof_size": $require_weight_at_most_proof_size, }, "call": { "encoded": $hex_encoded_data } } } - ] - } - } ] } ') @@ -147,13 +152,17 @@ function send_xcm_transact_remark_from_statemine() { echo "--------------------------------------------------" polkadot-js-api \ - --ws "${url?}" \ - --seed "${seed?}" \ - tx.polkadotXcm.send \ + --ws "${relay_url?}" \ + --seed "${relay_chain_seed?}" \ + --sudo \ + tx.xcmPallet.send \ "${dest}" \ "${message}" } +STATEMINE_ACCOUNT_SEED_FOR_LOCAL="//Alice" +ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO="scatter feed race company oxygen trip extra elbow slot bundle auto canoe" + function send_xcm_trap_from_statemine() { local url=$1 local seed=$2 @@ -212,30 +221,29 @@ function send_xcm_trap_from_statemine() { "${message}" } -function allow_assets_transfer_from_statemine() { +function allow_assets_transfer_send() { local relay_url=$1 local relay_chain_seed=$2 - local statemine_para_id=$3 - local statemine_para_endpoint=$4 + local runtime_para_id=$3 + local runtime_para_endpoint=$4 local bridge_hub_para_id=$5 - local statemint_para_network=$6 - local statemint_para_para_id=$7 - echo " calling allow_assets_transfer_from_statemine:" + local bridged_para_network=$6 + local bridged_para_para_id=$7 + echo " calling allow_assets_transfer_send:" echo " relay_url: ${relay_url}" echo " relay_chain_seed: ${relay_chain_seed}" - echo " statemine_para_id: ${statemine_para_id}" - echo " statemine_para_endpoint: ${statemine_para_endpoint}" + echo " runtime_para_id: ${runtime_para_id}" + echo " runtime_para_endpoint: ${runtime_para_endpoint}" echo " bridge_hub_para_id: ${bridge_hub_para_id}" - echo " statemint_para_network: ${statemint_para_network}" - echo " statemint_para_para_id: ${statemint_para_para_id}" + echo " bridged_para_network: ${bridged_para_network}" + echo " bridged_para_para_id: ${bridged_para_para_id}" echo " params:" - # generate data for Transact on Statemine + # 1. generate data for Transact (add_exporter_config) local bridge_config=$(jq --null-input \ - --arg statemint_para_network "$statemint_para_network" \ --arg bridge_hub_para_id "$bridge_hub_para_id" \ - --arg statemint_para_network "$statemint_para_network" \ - --arg statemint_para_para_id "$statemint_para_para_id" \ + --arg bridged_para_network "$bridged_para_network" \ + --arg bridged_para_para_id "$bridged_para_para_id" \ ' { "bridgeLocation": { @@ -249,133 +257,149 @@ function allow_assets_transfer_from_statemine() { "interior": { "X2": [ { - "GlobalConsensus": $statemint_para_network, + "GlobalConsensus": $bridged_para_network, }, { - "Parachain": $statemint_para_para_id + "Parachain": $bridged_para_para_id } ] } + }, + "targetLocationFee": { + "id": { + "Concrete": { + "parents": 1, + "interior": "Here" + } + }, + "fun": { + "Fungible": 50000000000 + } } } ' ) local tmp_output_file=$(mktemp) - generate_hex_encoded_call_data "add-bridge-config" "${statemine_para_endpoint}" "${tmp_output_file}" $statemint_para_network "$bridge_config" + generate_hex_encoded_call_data "add-exporter-config" "${runtime_para_endpoint}" "${tmp_output_file}" $bridged_para_network "$bridge_config" local hex_encoded_data=$(cat $tmp_output_file) - local dest=$(jq --null-input \ - --arg statemine_para_id "$statemine_para_id" \ - '{ "V3": { "parents": 0, "interior": { "X1": { "Parachain": $statemine_para_id } } } }') + send_governance_transact "${relay_url}" "${relay_chain_seed}" "${runtime_para_id}" "${hex_encoded_data}" 200000000 12000 +} - local message=$(jq --null-input \ - --argjson hex_encoded_data $hex_encoded_data \ - ' - { - "V3": [ - { - "UnpaidExecution": { - "weight_limit": "Unlimited" - } - }, - { - "Transact": { - "origin_kind": "Superuser", - "require_weight_at_most": { - "ref_time": 1000000000, - "proof_size": 0, - }, - "call": { - "encoded": $hex_encoded_data - } - } - } - ] - } - ') +function force_create_foreign_asset() { + local relay_url=$1 + local relay_chain_seed=$2 + local runtime_para_id=$3 + local runtime_para_endpoint=$4 + local global_consensus=$5 + local asset_owner_account_id=$6 + echo " calling force_create_foreign_asset:" + echo " relay_url: ${relay_url}" + echo " relay_chain_seed: ${relay_chain_seed}" + echo " runtime_para_id: ${runtime_para_id}" + echo " runtime_para_endpoint: ${runtime_para_endpoint}" + echo " global_consensus: ${global_consensus}" + echo " asset_owner_account_id: ${asset_owner_account_id}" + echo " params:" - echo "" - echo " dest:" - echo "${dest}" - echo "" - echo " message:" - echo "${message}" - echo "" - echo "--------------------------------------------------" + # 1. generate data for Transact (ForeignAssets::force_create) + local asset_id=$(jq --null-input \ + --arg global_consensus "$global_consensus" \ + ' + { + "parents": 2, + "interior": { + "X1": { + "GlobalConsensus": $global_consensus, + } + } + } + ' + ) + local tmp_output_file=$(mktemp) + generate_hex_encoded_call_data "force-create-asset" "${runtime_para_endpoint}" "${tmp_output_file}" "$asset_id" "$asset_owner_account_id" false "1000" + local hex_encoded_data=$(cat $tmp_output_file) - polkadot-js-api \ - --ws "${relay_url?}" \ - --seed "${relay_chain_seed?}" \ - --sudo \ - tx.xcmPallet.send \ - "${dest}" \ - "${message}" + send_governance_transact "${relay_url}" "${relay_chain_seed}" "${runtime_para_id}" "${hex_encoded_data}" 200000000 12000 } -function remove_assets_transfer_from_statemine() { +function allow_assets_transfer_receive() { local relay_url=$1 local relay_chain_seed=$2 - local statemine_para_id=$3 - local statemine_para_endpoint=$4 - local statemint_para_network=$5 - echo " calling remove_assets_transfer_from_statemine:" + local runtime_para_id=$3 + local runtime_para_endpoint=$4 + local bridge_hub_para_id=$5 + local bridged_network=$6 + local bridged_para_id=$7 + echo " calling allow_assets_transfer_receive:" echo " relay_url: ${relay_url}" echo " relay_chain_seed: ${relay_chain_seed}" - echo " statemine_para_id: ${statemine_para_id}" - echo " statemine_para_endpoint: ${statemine_para_endpoint}" - echo " statemint_para_network: ${statemint_para_network}" + echo " runtime_para_id: ${runtime_para_id}" + echo " runtime_para_endpoint: ${runtime_para_endpoint}" + echo " bridge_hub_para_id: ${bridge_hub_para_id}" + echo " bridged_network: ${bridged_network}" + echo " bridged_para_id: ${bridged_para_id}" echo " params:" + # 1. generate data for Transact (add_universal_alias) + local location=$(jq --null-input \ + --arg bridge_hub_para_id "$bridge_hub_para_id" \ + '{ "V3": { "parents": 1, "interior": { "X1": { "Parachain": $bridge_hub_para_id } } } }') + + local junction=$(jq --null-input \ + --arg bridged_network "$bridged_network" \ + '{ "GlobalConsensus": $bridged_network } ') + local tmp_output_file=$(mktemp) - generate_hex_encoded_call_data "remove-bridge-config" "${statemine_para_endpoint}" "${tmp_output_file}" $statemint_para_network + generate_hex_encoded_call_data "add-universal-alias" "${runtime_para_endpoint}" "${tmp_output_file}" "$location" "$junction" local hex_encoded_data=$(cat $tmp_output_file) - local dest=$(jq --null-input \ - --arg statemine_para_id "$statemine_para_id" \ - '{ "V3": { "parents": 0, "interior": { "X1": { "Parachain": $statemine_para_id } } } }') - - local message=$(jq --null-input \ - --argjson hex_encoded_data $hex_encoded_data \ - ' - { - "V3": [ - { - "UnpaidExecution": { - "weight_limit": "Unlimited" - } - }, - { - "Transact": { - "origin_kind": "Superuser", - "require_weight_at_most": { - "ref_time": 1000000000, - "proof_size": 0, - }, - "call": { - "encoded": $hex_encoded_data - } + send_governance_transact "${relay_url}" "${relay_chain_seed}" "${runtime_para_id}" "${hex_encoded_data}" 200000000 12000 + + # 2. generate data for Transact (add_reserve_location) + local reserve_location=$(jq --null-input \ + --arg bridged_network "$bridged_network" \ + --arg bridged_para_id "$bridged_para_id" \ + '{ "V3": { + "parents": 2, + "interior": { + "X2": [ + { + "GlobalConsensus": $bridged_network, + }, + { + "Parachain": $bridged_para_id } - } - ] - } - ') + ] + } + } }') - echo "" - echo " dest:" - echo "${dest}" - echo "" - echo " message:" - echo "${message}" - echo "" - echo "--------------------------------------------------" + local tmp_output_file=$(mktemp) + generate_hex_encoded_call_data "add-reserve-location" "${runtime_para_endpoint}" "${tmp_output_file}" "$reserve_location" + local hex_encoded_data=$(cat $tmp_output_file) - polkadot-js-api \ - --ws "${relay_url?}" \ - --seed "${relay_chain_seed?}" \ - --sudo \ - tx.xcmPallet.send \ - "${dest}" \ - "${message}" + send_governance_transact "${relay_url}" "${relay_chain_seed}" "${runtime_para_id}" "${hex_encoded_data}" 200000000 12000 +} + +function remove_assets_transfer_send() { + local relay_url=$1 + local relay_chain_seed=$2 + local runtime_para_id=$3 + local runtime_para_endpoint=$4 + local bridged_network=$5 + echo " calling remove_assets_transfer_send:" + echo " relay_url: ${relay_url}" + echo " relay_chain_seed: ${relay_chain_seed}" + echo " runtime_para_id: ${runtime_para_id}" + echo " runtime_para_endpoint: ${runtime_para_endpoint}" + echo " bridged_network: ${bridged_network}" + echo " params:" + + local tmp_output_file=$(mktemp) + generate_hex_encoded_call_data "remove-exporter-config" "${runtime_para_endpoint}" "${tmp_output_file}" $bridged_network + local hex_encoded_data=$(cat $tmp_output_file) + + send_governance_transact "${relay_url}" "${relay_chain_seed}" "${runtime_para_id}" "${hex_encoded_data}" 200000000 12000 } # TODO: we need to fill sovereign account for bridge-hub, because, small ammouts does not match ExistentialDeposit, so no reserve pass @@ -411,7 +435,9 @@ function transfer_asset_via_bridge() { ) -## TODO: decode some account to bytes: "id": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" +## // TODO:check-parameter - find dynamic way to decode some account to bytes: "id": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" +## AccountId32::from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY").unwrap().0` -> [u8; 32] +## [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] local destination=$(jq --null-input \ ' @@ -428,7 +454,7 @@ function transfer_asset_via_bridge() { }, { "AccountId32": { - "id": [28, 189, 45, 67, 83, 10, 68, 112, 90, 208, 136, 175, 49, 62, 24, 248, 11, 83, 239, 22, 179, 97, 119, 205, 75, 119, 184, 70, 242, 165, 240, 124] + "id": [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] } } ] @@ -447,65 +473,14 @@ function transfer_asset_via_bridge() { echo "" echo "--------------------------------------------------" -# local tmp_output_file=$(mktemp) -# generate_hex_encoded_call_data "transfer-asset-via-bridge" "${url}" "${tmp_output_file}" "$assets" "$destination" -# local hex_encoded_data=$(cat $tmp_output_file) - polkadot-js-api \ --ws "${url?}" \ --seed "${seed?}" \ - tx.bridgeAssetsTransfer.transferAssetViaBridge \ + tx.bridgeTransfer.transferAssetViaBridge \ "${assets}" \ "${destination}" } -function register_parachain() { - local PORT=$1 - local PARA_ID=$2 - local GENESIS_FILE=$3 - local GENESIS_WASM_FILE=$4 - - echo "" - echo "" - echo " Please, register parachain: https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A${PORT}#/sudo" - echo " parasSudoWrapper.sudoScheduleParaInitialize:" - echo " ParaId: $PARA_ID" - echo " Genesis (file): $GENESIS_FILE" - echo " Genesis wasm (file): $GENESIS_WASM_FILE" - echo " Parachain: Yes" - echo "" - echo "" - - # TODO: find the way to do it automatically -} - -function show_node_log_file() { - local NAME=$1 - local WS_PORT=$2 - local FILE_PATH=$3 - - echo "" - echo "" - echo " Node(${NAME}): ws-port: ${WS_PORT}" - echo " Logs: ${FILE_PATH}" - echo "" - echo "" -} - -function check_parachain_collator() { - local PORT=$1 - local PALLET=$2 - - echo "" - echo "" - echo " Once relayers work, check parachain: https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A${PORT}#/chainstate" - echo " Pallet: ${PALLET}" - echo " Keys:" - echo " bestFinalized()" - echo "" - echo "" -} - function init_ro_wo() { ensure_relayer @@ -563,95 +538,11 @@ function run_relay() { } case "$1" in - start-rococo) - ensure_binaries - - # TODO: change to generate: ./bin/polkadot key generate-node-key - VALIDATOR_KEY_ALICE=(12D3KooWRD6kEQuKDn7BCsMfW71U8AEFDYTc7N2dFQthRsjSR8J5 aa5dc9a97f82f9af38d1f16fbc9c72e915577c3ca654f7d33601645ca5b9a8a5) - VALIDATOR_KEY_BOB=(12D3KooWELR4gpc8unmH8WiauK133v397KoMfkkjESWJrzg7Y3Pq 556a36beaeeb95225eb84ae2633bd8135e5ace22f03291853654f07bc5da20ae) - VALIDATOR_KEY_CHARLIE=(12D3KooWRatYNHCRpyzhjPqfSPHYsUTi1zX4452kxSWg8ek99Vun 56e6593340685021d27509c0f48836c1dcb84c5aeba730ada68f54a9ae242e67) - COLLATOR_KEY_ALICE=(12D3KooWAU51GKCfPBfhaKK8gr6XmHNVXd4e96BYL9v2QkWtDrAm 15642c2e078ddfaacd1e68afcc8bab6dd8085b72b3038b799ddfb46bec18696c) - COLLATOR_KEY_BOB=(12D3KooWPg8SEatSi9HvdPJVYBNxeNAvyPTo3Rd4WPdzLRcM8gaS ac651cd6b9a2c7768bca92369ecd4807b54059c33775257e6ba5f72ae91a136d) - - # Start Rococo relay - ~/local_bridge_testing/bin/polkadot build-spec --chain rococo-local --disable-default-bootnode --raw > ~/local_bridge_testing/rococo-local-cfde.json - ~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --alice --tmp --port 30332 --rpc-port 9932 --ws-port 9942 --no-mdns --node-key ${VALIDATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30333/p2p/${VALIDATOR_KEY_BOB[0]} &> ~/local_bridge_testing/logs/rococo_relay_alice.log & - ~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --bob --tmp --port 30333 --rpc-port 9933 --ws-port 9943 --no-mdns --node-key ${VALIDATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/30332/p2p/${VALIDATOR_KEY_ALICE[0]} &> ~/local_bridge_testing/logs/rococo_relay_bob.log & - ~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/rococo-local-cfde.json --charlie --tmp --port 30334 --rpc-port 9934 --ws-port 9944 --no-mdns ${VALIDATOR_KEY_CHARLIE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30332/p2p/${VALIDATOR_KEY_ALICE[0]} &> ~/local_bridge_testing/logs/rococo_relay_charlie.log & - sleep 2 - - # Prepare Rococo parachain - rm ~/local_bridge_testing/bridge-hub-rococo-local-raw.json - ~/local_bridge_testing/bin/polkadot-parachain build-spec --chain bridge-hub-rococo-local --raw --disable-default-bootnode > ~/local_bridge_testing/bridge-hub-rococo-local-raw.json - ~/local_bridge_testing/bin/polkadot-parachain export-genesis-state --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json > ~/local_bridge_testing/bridge-hub-rococo-local-genesis - ~/local_bridge_testing/bin/polkadot-parachain export-genesis-wasm --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json > ~/local_bridge_testing/bridge-hub-rococo-local-genesis-wasm - - # Rococo - ~/local_bridge_testing/bin/polkadot-parachain --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json --collator --alice --force-authoring --tmp --port 40333 --rpc-port 8933 --ws-port 8943 --no-mdns --node-key ${COLLATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/40334/p2p/${COLLATOR_KEY_BOB[0]} -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 41333 --rpc-port 48933 --ws-port 48943 --no-mdns --node-key ${COLLATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30332/p2p/${VALIDATOR_KEY_ALICE[0]} &> ~/local_bridge_testing/logs/rococo_para_alice.log & - show_node_log_file alice 8943 ~/local_bridge_testing/logs/rococo_para_alice.log - ~/local_bridge_testing/bin/polkadot-parachain --chain ~/local_bridge_testing/bridge-hub-rococo-local-raw.json --collator --bob --force-authoring --tmp --port 40334 --rpc-port 8934 --ws-port 8944 --no-mdns --node-key ${COLLATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/40333/p2p/${COLLATOR_KEY_ALICE[0]} -- --execution wasm --chain ~/local_bridge_testing/rococo-local-cfde.json --port 41334 --rpc-port 48934 --ws-port 48944 --no-mdns --node-key ${COLLATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/30333/p2p/${VALIDATOR_KEY_BOB[0]} &> ~/local_bridge_testing/logs/rococo_para_bob.log & - show_node_log_file bob 8944 ~/local_bridge_testing/logs/rococo_para_bob.log - - register_parachain 9942 1013 ~/local_bridge_testing/bridge-hub-rococo-local-genesis ~/local_bridge_testing/bridge-hub-rococo-local-genesis-wasm - check_parachain_collator 8943 bridgeWococoGrandpa - ;; - start-wococo) - ensure_binaries - - # TODO: change to generate: ./bin/polkadot key generate-node-key - VALIDATOR_KEY_ALICE=(12D3KooWKB4SqNJAttmDHXEKtETLPr8Nixso8gUNzNKDS9b6HtYj 8c87694d3a3b3a0e201caceaae095aac66a76ba37545c439f7e0d986670da8e6) - VALIDATOR_KEY_BOB=(12D3KooWJq6xkfyV3LFxoNgsCskAwLv6VfLhL3cjHfW4UxDNwroF 9ebcd7371b427d63c087762fe3dc5a1d4b01875cb8611c263feda21364d4a329) - VALIDATOR_KEY_CHARLIE=(12D3KooW9yQQXz6xUJY2YgizKTFLS5fPaPi4KznQdMHEW4Hzt3NL 8bb1c50421a73082b8275ead6e3f150a774a0f8d6ad4a786df5d5b7688115cb1) - VALIDATOR_KEY_DAVE=(12D3KooWJP1R7XxqEuSryXSKYRVz9wdMRvd5LSqjRpdK4AvjnB12 e160d8b0ef4d66e02abf6663417b024f27f6cb2f826b746f3d267c58a32e9c05) - COLLATOR_KEY_ALICE=(12D3KooWNsQbEdW1eyC6TXAJCXxZ8qzkCJPWFqhWjco1SYYdaKPU 93ac8257255961b3d3ec0038747f0f9c7ddaa5dd352297daa352e098285965b7) - COLLATOR_KEY_BOB=(12D3KooWQ9yxdcf7kavoaKNWmt92tkCGQ8C4sq2JNBgCjvGtvvFg 7e396f01a8a74ecf2010ab2d57ca9fc6c5d673914d97ad6a066fe724e60c3412) - - # Start Wococo relay - ~/local_bridge_testing/bin/polkadot build-spec --chain wococo-local --disable-default-bootnode --raw > ~/local_bridge_testing/wococo-local-cfde.json - ~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --alice --tmp --port 30335 --rpc-port 9935 --ws-port 9945 --no-mdns --node-key ${VALIDATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30336/p2p/${VALIDATOR_KEY_BOB[0]} &> ~/local_bridge_testing/logs/wococo_relay_alice.log & - ~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --bob --tmp --port 30336 --rpc-port 9936 --ws-port 9946 --no-mdns --node-key ${VALIDATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/30335/p2p/${VALIDATOR_KEY_ALICE[0]} &> ~/local_bridge_testing/logs/wococo_relay_bob.log & - ~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --charlie --tmp --port 30337 --rpc-port 9937 --ws-port 9947 --no-mdns --node-key ${VALIDATOR_KEY_CHARLIE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30335/p2p/${VALIDATOR_KEY_ALICE[0]} &> ~/local_bridge_testing/logs/wococo_relay_charlie.log & - ~/local_bridge_testing/bin/polkadot --chain ~/local_bridge_testing/wococo-local-cfde.json --dave --tmp --port 30338 --rpc-port 9938 --ws-port 9948 --no-mdns --node-key ${VALIDATOR_KEY_DAVE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30336/p2p/${VALIDATOR_KEY_BOB[0]} &> ~/local_bridge_testing/logs/wococo_relay_dave.log & - sleep 2 - - # Prepare Wococo parachain - rm ~/local_bridge_testing/bridge-hub-wococo-local-raw.json - ~/local_bridge_testing/bin/polkadot-parachain build-spec --chain bridge-hub-wococo-local --raw --disable-default-bootnode > ~/local_bridge_testing/bridge-hub-wococo-local-raw.json - ~/local_bridge_testing/bin/polkadot-parachain export-genesis-state --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json > ~/local_bridge_testing/bridge-hub-wococo-local-genesis - ~/local_bridge_testing/bin/polkadot-parachain export-genesis-wasm --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json > ~/local_bridge_testing/bridge-hub-wococo-local-genesis-wasm - - # Wococo - ~/local_bridge_testing/bin/polkadot-parachain --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json --collator --alice --force-authoring --tmp --port 40335 --rpc-port 8935 --ws-port 8945 --no-mdns --node-key ${COLLATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/40336/p2p/${COLLATOR_KEY_BOB[0]} -- --execution wasm --chain ~/local_bridge_testing/wococo-local-cfde.json --port 41335 --rpc-port 48935 --ws-port 48945 --no-mdns --node-key ${COLLATOR_KEY_ALICE[1]} --bootnodes=/ip4/127.0.0.1/tcp/30335/p2p/${VALIDATOR_KEY_ALICE[0]} &> ~/local_bridge_testing/logs/wococo_para_alice.log & - show_node_log_file alice 8945 ~/local_bridge_testing/logs/wococo_para_alice.log - ~/local_bridge_testing/bin/polkadot-parachain --chain ~/local_bridge_testing/bridge-hub-wococo-local-raw.json --collator --bob --force-authoring --tmp --port 40336 --rpc-port 8936 --ws-port 8946 --no-mdns --node-key ${COLLATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/40335/p2p/${COLLATOR_KEY_ALICE[0]} -- --execution wasm --chain ~/local_bridge_testing/wococo-local-cfde.json --port 41336 --rpc-port 48936 --ws-port 48946 --no-mdns --node-key ${COLLATOR_KEY_BOB[1]} --bootnodes=/ip4/127.0.0.1/tcp/30336/p2p/${VALIDATOR_KEY_BOB[0]} &> ~/local_bridge_testing/logs/wococo_para_bob.log & - show_node_log_file bob 8946 ~/local_bridge_testing/logs/wococo_para_bob.log - - register_parachain 9945 1013 ~/local_bridge_testing/bridge-hub-wococo-local-genesis ~/local_bridge_testing/bridge-hub-wococo-local-genesis-wasm - check_parachain_collator 8945 bridgeRococoGrandpa - ;; - init-ro-wo) - # Init bridge Rococo->Wococo - init_ro_wo - ;; - init-wo-ro) - # Init bridge Wococo->Rococo - init_wo_ro - ;; run-relay) init_ro_wo init_wo_ro run_relay ;; - send-remark-local) - ensure_polkadot_js_api - send_xcm_transact_remark_from_statemine \ - "ws://127.0.0.1:9910" \ - "${STATEMINE_ACCOUNT_SEED_FOR_LOCAL}" \ - 1013 \ - "Wococo" \ - 1000 \ - "ws://127.0.0.1:9010" - ;; send-trap-local) ensure_polkadot_js_api send_xcm_trap_from_statemine \ @@ -661,16 +552,6 @@ case "$1" in "Wococo" \ 1000 ;; - send-remark-rococo) - ensure_polkadot_js_api - send_xcm_transact_remark_from_statemine \ - "wss://ws-rococo-rockmine2-collator-node-0.parity-testnet.parity.io" \ - "${ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO}" \ - 1013 \ - "Wococo" \ - 1000 \ - "wss://ws-wococo-wockmint-collator-node-0.parity-testnet.parity.io" - ;; send-trap-rococo) ensure_polkadot_js_api send_xcm_trap_from_statemine \ @@ -680,9 +561,15 @@ case "$1" in "Wococo" \ 1000 ;; + allow-transfers-local) + # this allows send transfers on statemine (by governance-like) + ./$0 "allow-transfer-on-statemine-local" + # this allows receive transfers on westmint (by governance-like) + ./$0 "allow-transfer-on-westmint-local" + ;; allow-transfer-on-statemine-local) ensure_polkadot_js_api - allow_assets_transfer_from_statemine \ + allow_assets_transfer_send \ "ws://127.0.0.1:9942" \ "//Alice" \ 1000 \ @@ -690,24 +577,56 @@ case "$1" in 1013 \ "Wococo" 1000 ;; + allow-transfer-on-westmint-local) + ensure_polkadot_js_api + allow_assets_transfer_receive \ + "ws://127.0.0.1:9945" \ + "//Alice" \ + 1000 \ + "ws://127.0.0.1:9010" \ + 1014 \ + "Rococo" \ + 1000 + # drip SovereignAccount for `MultiLocation { parents: 2, interior: X2(GlobalConsensus(Rococo), Parachain(1000)) }` + transfer_balance \ + "ws://127.0.0.1:9010" \ + "//Alice" \ + "5DHZvp523gmJWxg9UcLVbofyu5nZkPvATeP1ciYncpFpXtiG" \ + $((1000000000 + 50000000000 * 20)) # ExistentialDeposit + targetLocationFee * 20 + # create foreign assets for native Statemine token (yes, Kusama, because we are using Statemine runtime on rococo) + force_create_foreign_asset \ + "ws://127.0.0.1:9945" \ + "//Alice" \ + 1000 \ + "ws://127.0.0.1:9010" \ + "Kusama" \ + "5DHZvp523gmJWxg9UcLVbofyu5nZkPvATeP1ciYncpFpXtiG" + ;; remove-assets-transfer-from-statemine-local) ensure_polkadot_js_api - remove_assets_transfer_from_statemine \ + remove_assets_transfer_send \ "ws://127.0.0.1:9942" \ "//Alice" \ 1000 \ "ws://127.0.0.1:9910" \ "Wococo" ;; - transfer-asset) + transfer-asset-from-statemine-local) ensure_polkadot_js_api transfer_asset_via_bridge \ "ws://127.0.0.1:9910" \ "//Alice" ;; + drip) + transfer_balance \ + "ws://127.0.0.1:9010" \ + "//Alice" \ + "5DHZvp523gmJWxg9UcLVbofyu5nZkPvATeP1ciYncpFpXtiG" \ + $((1000000000 + 50000000000 * 20)) + ;; stop) pkill -f polkadot pkill -f parachain ;; - *) echo "A command is require. Supported commands: run-relay, send-trap-rococo/send-trap-local, send-remark-local/send-remark-rococo, allow-transfer-on-statemine-local/remove-assets-transfer-from-statemine-local, transfer-asset"; exit 1;; + *) echo "A command is require. Supported commands: run-relay, send-trap-rococo/send-trap-local, send-remark-local/send-remark-rococo, allow-transfers-local/allow-transfer-on-statemine-local/remove-assets-transfer-from-statemine-local, allow-transfer-on-westmint-local, transfer-asset-from-statemine-local"; exit 1;; esac diff --git a/scripts/generate_hex_encoded_call/index.js b/scripts/generate_hex_encoded_call/index.js index 167a292bb26..b135622c335 100644 --- a/scripts/generate_hex_encoded_call/index.js +++ b/scripts/generate_hex_encoded_call/index.js @@ -34,11 +34,11 @@ function remarkWithEvent(endpoint, outputFile) { }); } -function addBridgeConfig(endpoint, outputFile, bridgedNetwork, bridgeConfig) { - console.log(`Generating addBridgeConfig from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on bridgedNetwork: ${bridgedNetwork}, bridgeConfig: ${bridgeConfig}`); +function addExporterConfig(endpoint, outputFile, bridgedNetwork, bridgeConfig) { + console.log(`Generating addExporterConfig from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on bridgedNetwork: ${bridgedNetwork}, bridgeConfig: ${bridgeConfig}`); connect(endpoint) .then((api) => { - const call = api.tx.bridgeAssetsTransfer.addBridgeConfig(bridgedNetwork, JSON.parse(bridgeConfig)); + const call = api.tx.bridgeTransfer.addExporterConfig(bridgedNetwork, JSON.parse(bridgeConfig)); writeHexEncodedBytesToOutput(call.method, outputFile); exit(0); }) @@ -48,11 +48,11 @@ function addBridgeConfig(endpoint, outputFile, bridgedNetwork, bridgeConfig) { }); } -function removeBridgeConfig(endpoint, outputFile, bridgedNetwork) { - console.log(`Generating removeBridgeConfig from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on bridgedNetwork: ${bridgedNetwork}`); +function addUniversalAlias(endpoint, outputFile, location, junction) { + console.log(`Generating addUniversalAlias from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on location: ${location}, junction: ${junction}`); connect(endpoint) .then((api) => { - const call = api.tx.bridgeAssetsTransfer.removeBridgeConfig(bridgedNetwork); + const call = api.tx.bridgeTransfer.addUniversalAlias(JSON.parse(location), JSON.parse(junction)); writeHexEncodedBytesToOutput(call.method, outputFile); exit(0); }) @@ -62,11 +62,39 @@ function removeBridgeConfig(endpoint, outputFile, bridgedNetwork) { }); } -function transferAssetViaBridge(endpoint, outputFile, assets, destination) { - console.log(`Generating transferAssetViaBridge from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on assets: ${assets}, destination: ${destination}`); +function addReserveLocation(endpoint, outputFile, reserve_location) { + console.log(`Generating addReserveLocation from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on reserve_location: ${reserve_location}`); connect(endpoint) .then((api) => { - const call = api.tx.bridgeAssetsTransfer.transferAssetViaBridge(JSON.parse(assets), JSON.parse(destination)); + const call = api.tx.bridgeTransfer.addReserveLocation(JSON.parse(reserve_location)); + writeHexEncodedBytesToOutput(call.method, outputFile); + exit(0); + }) + .catch((e) => { + console.error(e); + exit(1); + }); +} + +function removeExporterConfig(endpoint, outputFile, bridgedNetwork) { + console.log(`Generating removeExporterConfig from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on bridgedNetwork: ${bridgedNetwork}`); + connect(endpoint) + .then((api) => { + const call = api.tx.bridgeTransfer.removeExporterConfig(bridgedNetwork); + writeHexEncodedBytesToOutput(call.method, outputFile); + exit(0); + }) + .catch((e) => { + console.error(e); + exit(1); + }); +} + +function forceCreateAsset(endpoint, outputFile, assetId, assetOwnerAccountId, isSufficient, minBalance) { + console.log(`Generating forceCreateAsset from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on assetId: ${assetId}, assetOwnerAccountId: ${assetOwnerAccountId}, isSufficient: ${isSufficient}, minBalance: ${minBalance}`); + connect(endpoint) + .then((api) => { + const call = api.tx.foreignAssets.forceCreate(JSON.parse(assetId), assetOwnerAccountId, isSufficient, minBalance); writeHexEncodedBytesToOutput(call.method, outputFile); exit(0); }) @@ -95,14 +123,20 @@ switch (type) { case 'remark-with-event': remarkWithEvent(rpcEnpoint, output); break; - case 'add-bridge-config': - addBridgeConfig(rpcEnpoint, output, inputArgs[0], inputArgs[1]); + case 'add-exporter-config': + addExporterConfig(rpcEnpoint, output, inputArgs[0], inputArgs[1]); + break; + case 'remove-exporter-config': + removeExporterConfig(rpcEnpoint, output, inputArgs[0], inputArgs[1]); + break; + case 'add-universal-alias': + addUniversalAlias(rpcEnpoint, output, inputArgs[0], inputArgs[1]); break; - case 'remove-bridge-config': - removeBridgeConfig(rpcEnpoint, output, inputArgs[0], inputArgs[1]); + case 'add-reserve-location': + addReserveLocation(rpcEnpoint, output, inputArgs[0]); break; - case 'transfer-asset-via-bridge': - transferAssetViaBridge(rpcEnpoint, output, inputArgs[0], inputArgs[1]); + case 'force-create-asset': + forceCreateAsset(rpcEnpoint, output, inputArgs[0], inputArgs[1], inputArgs[2], inputArgs[3]); break; case 'check': console.log(`Checking nodejs installation, if you see this everything is ready!`); From 25b7b445745fb78e6aebe1df80d9e13f500cb307 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 5 Apr 2023 17:14:51 +0200 Subject: [PATCH 231/263] Cargo.lock --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3245303d462..d1a96657fc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -743,7 +743,7 @@ dependencies = [ "finality-grandpa", "frame-support", "hex", - "hex-literal", + "hex-literal 0.3.4", "parity-scale-codec", "scale-info", "serde", @@ -760,7 +760,7 @@ dependencies = [ "bp-runtime", "frame-support", "hex", - "hex-literal", + "hex-literal 0.3.4", "parity-scale-codec", "scale-info", "serde", @@ -810,7 +810,7 @@ dependencies = [ "bp-runtime", "frame-support", "hex", - "hex-literal", + "hex-literal 0.3.4", "parity-scale-codec", "scale-info", "sp-runtime", @@ -835,7 +835,7 @@ dependencies = [ "frame-support", "frame-system", "hash-db", - "hex-literal", + "hex-literal 0.3.4", "impl-trait-for-tuples", "num-traits", "parity-scale-codec", From dcedafb847478939e8170939b4e3da39d5b89a3c Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 11 Apr 2023 10:47:51 +0200 Subject: [PATCH 232/263] Script updates for `ping-via-bridge-from-statemine-rococo` --- parachains/runtimes/bridge-hubs/README.md | 30 ++--- scripts/bridges_rococo_wococo.sh | 144 ++++++++++------------ 2 files changed, 76 insertions(+), 98 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 788e767f0fc..27df65c5ac5 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -176,32 +176,21 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ./scripts/bridges_rococo_wococo.sh allow-transfers-local ``` -2. do transfer from statemine to westmint +2. do (asset) transfer from statemine to westmint ``` ./scripts/bridges_rococo_wococo.sh transfer-asset-from-statemine-local ``` -#### Ping via bridge -``` -./scripts/bridges_rococo_wococo.sh allow-transfers-local -./scripts/bridges_rococo_wococo.sh ping-via-bridge-from-statemine-local -``` - -#### Local Rococo:Statemine -> Wococo:Westmint -- check that relayers are up and running (see above) -- uses account seed `//Alice` - ``` - cd +3. do (ping) transfer from statemine to westmint + ``` + ./scripts/bridges_rococo_wococo.sh ping-via-bridge-from-statemine-local + ``` - ./scripts/bridges_rococo_wococo.sh send-remark-local - or - ./scripts/bridges_rococo_wococo.sh send-trap-local - ``` - open explorers: (see zombienets) - Statemine (see `polkadotXcm.Sent`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9910#/explorer - BridgeHubRococo (see `bridgeWococoMessages.MessageAccepted`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8943#/explorer - BridgeHubWococo (see `bridgeRococoMessages.MessagesReceived`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8945#/explorer - - Westmint (see `xcmpQueue.Success` for `remark` and `xcmpQueue.Fail` for `trap`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9010#/explorer + - Westmint (see `xcmpQueue.Success` for `transfer-asset` and `xcmpQueue.Fail` for `ping-via-bridge`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9010#/explorer - BridgeHubRococo (see `bridgeWococoMessages.MessagesDelivered`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8943#/explorer #### Live Rococo:Rockmine2 -> Wococo:Wockmint @@ -209,15 +198,16 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ``` cd - ./scripts/bridges_rococo_wococo.sh send-remark-rococo + ./scripts/bridges_rococo_wococo.sh transfer-asset-from-statemine-rococo or - ./scripts/bridges_rococo_wococo.sh send-trap-rococo + ./scripts/bridges_rococo_wococo.sh ping-via-bridge-from-statemine-rococo ``` + - open explorers: (see https://github.com/paritytech/parity-bridges-common/issues/1671) - Rockmine2 (see `polkadotXcm.Sent`) - BridgeHubRococo (see `bridgeWococoMessages.MessageAccepted`) - BridgeHubWococo (see `bridgeRococoMessages.MessagesReceived`) - - Wockmint (see `xcmpQueue.Success` for `remark` and `xcmpQueue.Fail` for `trap`) + - Wockmint (see `xcmpQueue.Success` for `transfer-asset` and `xcmpQueue.Fail` for `ping-via-bridge`) - BridgeHubRococo (see `bridgeWococoMessages.MessagesDelivered`) ## How to test local BridgeHubKusama diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh index e5c19d3004e..d707dcfb3f3 100755 --- a/scripts/bridges_rococo_wococo.sh +++ b/scripts/bridges_rococo_wococo.sh @@ -161,66 +161,9 @@ function send_governance_transact() { } STATEMINE_ACCOUNT_SEED_FOR_LOCAL="//Alice" +# Address: GegTpZJMyzkntLN7NJhRfHDk4GWukLbGSsag6PHrLSrCK4h ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO="scatter feed race company oxygen trip extra elbow slot bundle auto canoe" -function send_xcm_trap_from_statemine() { - local url=$1 - local seed=$2 - local bridge_hub_para_id=$3 - local target_network=$4 - local target_network_para_id=$5 - echo " calling send_xcm_trap_from_statemine:" - echo " url: ${url}" - echo " seed: ${seed}" - echo " bridge_hub_para_id: ${bridge_hub_para_id}" - echo " params:" - - local dest=$(jq --null-input \ - --arg bridge_hub_para_id "$bridge_hub_para_id" \ - '{ "V3": { "parents": 1, "interior": { "X1": { "Parachain": $bridge_hub_para_id } } } }') - - local message=$(jq --null-input \ - --arg target_network "$target_network" \ - --arg target_network_para_id "$target_network_para_id" \ - ' - { - "V3": [ - { - "ExportMessage": { - "network": $target_network, - "destination": { - "X1": { - "Parachain": $target_network_para_id - } - }, - "xcm": [ - { - "Trap": 12345 - } - ] - } - } - ] - } - ') - - echo "" - echo " dest:" - echo "${dest}" - echo "" - echo " message:" - echo "${message}" - echo "" - echo "--------------------------------------------------" - - polkadot-js-api \ - --ws "${url?}" \ - --seed "${seed?}" \ - tx.polkadotXcm.send \ - "${dest}" \ - "${message}" -} - function allow_assets_transfer_send() { local relay_url=$1 local relay_chain_seed=$2 @@ -481,6 +424,56 @@ function transfer_asset_via_bridge() { "${destination}" } +function ping_via_bridge() { + local url=$1 + local seed=$2 + echo " calling transfer_asset_via_bridge:" + echo " url: ${url}" + echo " seed: ${seed}" + echo " params:" + +## // TODO:check-parameter - find dynamic way to decode some account to bytes: "id": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" +## AccountId32::from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY").unwrap().0` -> [u8; 32] +## [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] + + local destination=$(jq --null-input \ + ' + { + "V3": { + "parents": 2, + "interior": { + "X3": [ + { + "GlobalConsensus": "Wococo" + }, + { + "Parachain": 1000 + }, + { + "AccountId32": { + "id": [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] + } + } + ] + } + } + } + ' + ) + + echo "" + echo " destination:" + echo "${destination}" + echo "" + echo "--------------------------------------------------" + + polkadot-js-api \ + --ws "${url?}" \ + --seed "${seed?}" \ + tx.bridgeTransfer.pingViaBridge \ + "${destination}" +} + function init_ro_wo() { ensure_relayer @@ -543,24 +536,6 @@ case "$1" in init_wo_ro run_relay ;; - send-trap-local) - ensure_polkadot_js_api - send_xcm_trap_from_statemine \ - "ws://127.0.0.1:9910" \ - "${STATEMINE_ACCOUNT_SEED_FOR_LOCAL}" \ - 1013 \ - "Wococo" \ - 1000 - ;; - send-trap-rococo) - ensure_polkadot_js_api - send_xcm_trap_from_statemine \ - "wss://ws-rococo-rockmine2-collator-node-0.parity-testnet.parity.io" \ - "${ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO}" \ - 1013 \ - "Wococo" \ - 1000 - ;; allow-transfers-local) # this allows send transfers on statemine (by governance-like) ./$0 "allow-transfer-on-statemine-local" @@ -587,7 +562,8 @@ case "$1" in 1014 \ "Rococo" \ 1000 - # drip SovereignAccount for `MultiLocation { parents: 2, interior: X2(GlobalConsensus(Rococo), Parachain(1000)) }` + # drip SovereignAccount for `MultiLocation { parents: 2, interior: X2(GlobalConsensus(Rococo), Parachain(1000)) }` => 5DHZvp523gmJWxg9UcLVbofyu5nZkPvATeP1ciYncpFpXtiG + # drip SovereignAccount for `MultiLocation { parents: 2, interior: X2(GlobalConsensus(Rococo), Parachain(1015)) }` => 5FS75NFUdEYhWHuV3y3ncjSG4PFdHfC5X7V6SEzc3rnCciwb transfer_balance \ "ws://127.0.0.1:9010" \ "//Alice" \ @@ -617,6 +593,18 @@ case "$1" in "ws://127.0.0.1:9910" \ "//Alice" ;; + ping-via-bridge-from-statemine-local) + ensure_polkadot_js_api + ping_via_bridge \ + "ws://127.0.0.1:9910" \ + "${STATEMINE_ACCOUNT_SEED_FOR_LOCAL}" + ;; + ping-via-bridge-from-statemine-rococo) + ensure_polkadot_js_api + ping_via_bridge \ + "wss://ws-rococo-rockmine2-collator-node-0.parity-testnet.parity.io" \ + "${ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO}" + ;; drip) transfer_balance \ "ws://127.0.0.1:9010" \ @@ -628,5 +616,5 @@ case "$1" in pkill -f polkadot pkill -f parachain ;; - *) echo "A command is require. Supported commands: run-relay, send-trap-rococo/send-trap-local, send-remark-local/send-remark-rococo, allow-transfers-local/allow-transfer-on-statemine-local/remove-assets-transfer-from-statemine-local, allow-transfer-on-westmint-local, transfer-asset-from-statemine-local"; exit 1;; + *) echo "A command is require. Supported commands: run-relay, allow-transfers-local/allow-transfer-on-statemine-local/remove-assets-transfer-from-statemine-local, allow-transfer-on-westmint-local, transfer-asset-from-statemine-local/TODO:, ping-via-bridge-from-statemine-local/ping-via-bridge-from-statemine-rococo"; exit 1;; esac From a9a6c30b82aeb9e8f00c527a2eb26b0172bab8c4 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 12 Apr 2023 10:55:29 +0200 Subject: [PATCH 233/263] Added `transfer-asset-from-statemine-rococo` --- parachains/runtimes/bridge-hubs/README.md | 2 +- scripts/bridges_rococo_wococo.sh | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 27df65c5ac5..de00813506d 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -187,7 +187,7 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ``` - open explorers: (see zombienets) - - Statemine (see `polkadotXcm.Sent`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9910#/explorer + - Statemine (see events `xcmpQueue.XcmpMessageSent`, `bridgeTransfer.ReserveAssetsDeposited`, `bridgeTransfer.TransferInitiated`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9910#/explorer - BridgeHubRococo (see `bridgeWococoMessages.MessageAccepted`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8943#/explorer - BridgeHubWococo (see `bridgeRococoMessages.MessagesReceived`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8945#/explorer - Westmint (see `xcmpQueue.Success` for `transfer-asset` and `xcmpQueue.Fail` for `ping-via-bridge`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9010#/explorer diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh index d707dcfb3f3..fb411603b1d 100755 --- a/scripts/bridges_rococo_wococo.sh +++ b/scripts/bridges_rococo_wococo.sh @@ -593,6 +593,12 @@ case "$1" in "ws://127.0.0.1:9910" \ "//Alice" ;; + transfer-asset-from-statemine-rococo) + ensure_polkadot_js_api + transfer_asset_via_bridge \ + "wss://ws-rococo-rockmine2-collator-node-0.parity-testnet.parity.io" \ + "${ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO}" + ;; ping-via-bridge-from-statemine-local) ensure_polkadot_js_api ping_via_bridge \ @@ -616,5 +622,19 @@ case "$1" in pkill -f polkadot pkill -f parachain ;; - *) echo "A command is require. Supported commands: run-relay, allow-transfers-local/allow-transfer-on-statemine-local/remove-assets-transfer-from-statemine-local, allow-transfer-on-westmint-local, transfer-asset-from-statemine-local/TODO:, ping-via-bridge-from-statemine-local/ping-via-bridge-from-statemine-rococo"; exit 1;; + *) + echo "A command is require. Supported commands for: + Local (zombienet) run: + - run-relay + - allow-transfers-local + - allow-transfer-on-statemine-local + - allow-transfer-on-westmint-local + - remove-assets-transfer-from-statemine-local + - transfer-asset-from-statemine-local + - ping-via-bridge-from-statemine-local + Live Rococo/Wococo run: + - transfer-asset-from-statemine-rococo + - ping-via-bridge-from-statemine-rococo"; + exit 1 + ;; esac From 8252d87105ff32a253a6f34fe221696e121b2ef9 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 12 Apr 2023 16:22:05 +0200 Subject: [PATCH 234/263] Finished scripts --- parachains/runtimes/bridge-hubs/README.md | 12 ++-- scripts/bridges_rococo_wococo.sh | 71 +++++++++++++++++------ 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index de00813506d..3c9f86295f0 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -169,7 +169,7 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ### Send messages (Rococo, Wococo) -#### Transfer assets via bridge +#### Local (zombienet) run 1. allow bridge transfer on statemine/westmint (governance-like): ``` @@ -203,11 +203,11 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ ./scripts/bridges_rococo_wococo.sh ping-via-bridge-from-statemine-rococo ``` -- open explorers: (see https://github.com/paritytech/parity-bridges-common/issues/1671) - - Rockmine2 (see `polkadotXcm.Sent`) - - BridgeHubRococo (see `bridgeWococoMessages.MessageAccepted`) - - BridgeHubWococo (see `bridgeRococoMessages.MessagesReceived`) - - Wockmint (see `xcmpQueue.Success` for `transfer-asset` and `xcmpQueue.Fail` for `ping-via-bridge`) +- open explorers: + - Rockmine2 (see events `xcmpQueue.XcmpMessageSent`, `bridgeTransfer.ReserveAssetsDeposited`, `bridgeTransfer.TransferInitiated`) https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fws-rococo-rockmine2-collator-node-0.parity-testnet.parity.io#/explorer + - BridgeHubRococo (see `bridgeWococoMessages.MessageAccepted`) https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frococo-bridge-hub-rpc.polkadot.io#/explorer + - BridgeHubWococo (see `bridgeRococoMessages.MessagesReceived`) https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fwococo-bridge-hub-rpc.polkadot.io#/explorer + - Wockmint (see `xcmpQueue.Success` for `transfer-asset` and `xcmpQueue.Fail` for `ping-via-bridge`) https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fws-wococo-wockmint-collator-node-0.parity-testnet.parity.io#/explorer - BridgeHubRococo (see `bridgeWococoMessages.MessagesDelivered`) ## How to test local BridgeHubKusama diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh index fb411603b1d..55edd8dee24 100755 --- a/scripts/bridges_rococo_wococo.sh +++ b/scripts/bridges_rococo_wococo.sh @@ -1,5 +1,34 @@ #!/bin/bash +# Address: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY +# AccountId: [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] +STATEMINE_ACCOUNT_SEED_FOR_LOCAL="//Alice" +# Address: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY +# AccountId: [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] +WOCKMINT_ACCOUNT_ADDRESS_FOR_LOCAL="5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" + +# Address: GegTpZJMyzkntLN7NJhRfHDk4GWukLbGSsag6PHrLSrCK4h +ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO="scatter feed race company oxygen trip extra elbow slot bundle auto canoe" + +# Adress: 5Ge7YcbctWCP1CccugzxWDn9hFnTxvTh3bL6PNy4ubNJmp7Y / H9jCvwVWsDJkrS4gPp1QB99qr4hmbGsVyAqn3F2PPaoWyU3 +# AccountId: [202, 107, 198, 135, 15, 25, 193, 165, 172, 73, 137, 218, 115, 177, 204, 0, 5, 155, 215, 86, 208, 51, 50, 130, 190, 110, 184, 143, 124, 50, 160, 20] +WOCKMINT_ACCOUNT_ADDRESS_FOR_ROCOCO="5Ge7YcbctWCP1CccugzxWDn9hFnTxvTh3bL6PNy4ubNJmp7Y" +WOCKMINT_ACCOUNT_SEED_FOR_WOCOCO="tone spirit magnet sunset cannon poverty forget lock river east blouse random" + +function address_to_account_id_bytes() { + local address=$1 + local output=$2 + echo "address_to_account_id_bytes - address: $address, output: $output" + if [ $address == "$WOCKMINT_ACCOUNT_ADDRESS_FOR_LOCAL" ]; then + jq --null-input '[212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125]' > $output + elif [ $address == "$WOCKMINT_ACCOUNT_ADDRESS_FOR_ROCOCO" ]; then + jq --null-input '[202, 107, 198, 135, 15, 25, 193, 165, 172, 73, 137, 218, 115, 177, 204, 0, 5, 155, 215, 86, 208, 51, 50, 130, 190, 110, 184, 143, 124, 50, 160, 20]' > $output + else + echo -n "Sorry, unknown address: $address - please, add bytes here or function for that!" + exit 1 + fi +} + function ensure_binaries() { if [[ ! -f ~/local_bridge_testing/bin/polkadot ]]; then echo " Required polkadot binary '~/local_bridge_testing/bin/polkadot' does not exist!" @@ -160,10 +189,6 @@ function send_governance_transact() { "${message}" } -STATEMINE_ACCOUNT_SEED_FOR_LOCAL="//Alice" -# Address: GegTpZJMyzkntLN7NJhRfHDk4GWukLbGSsag6PHrLSrCK4h -ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO="scatter feed race company oxygen trip extra elbow slot bundle auto canoe" - function allow_assets_transfer_send() { local relay_url=$1 local relay_chain_seed=$2 @@ -351,12 +376,13 @@ function remove_assets_transfer_send() { function transfer_asset_via_bridge() { local url=$1 local seed=$2 + local target_account=$3 echo " calling transfer_asset_via_bridge:" echo " url: ${url}" echo " seed: ${seed}" + echo " target_account: ${target_account}" echo " params:" - local assets=$(jq --null-input \ ' { @@ -377,12 +403,12 @@ function transfer_asset_via_bridge() { ' ) - -## // TODO:check-parameter - find dynamic way to decode some account to bytes: "id": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" -## AccountId32::from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY").unwrap().0` -> [u8; 32] -## [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] + local tmp_output_file=$(mktemp) + address_to_account_id_bytes "$target_account" "${tmp_output_file}" + local hex_encoded_data=$(cat $tmp_output_file) local destination=$(jq --null-input \ + --argjson hex_encoded_data "$hex_encoded_data" \ ' { "V3": { @@ -397,7 +423,7 @@ function transfer_asset_via_bridge() { }, { "AccountId32": { - "id": [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] + "id": $hex_encoded_data } } ] @@ -427,16 +453,19 @@ function transfer_asset_via_bridge() { function ping_via_bridge() { local url=$1 local seed=$2 - echo " calling transfer_asset_via_bridge:" + local target_account=$3 + echo " calling ping_via_bridge:" echo " url: ${url}" echo " seed: ${seed}" + echo " target_account: ${target_account}" echo " params:" -## // TODO:check-parameter - find dynamic way to decode some account to bytes: "id": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" -## AccountId32::from_str("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY").unwrap().0` -> [u8; 32] -## [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] + local tmp_output_file=$(mktemp) + address_to_account_id_bytes "$target_account" "${tmp_output_file}" + local hex_encoded_data=$(cat $tmp_output_file) local destination=$(jq --null-input \ + --argjson hex_encoded_data "$hex_encoded_data" \ ' { "V3": { @@ -451,7 +480,7 @@ function ping_via_bridge() { }, { "AccountId32": { - "id": [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] + "id": $hex_encoded_data } } ] @@ -591,25 +620,29 @@ case "$1" in ensure_polkadot_js_api transfer_asset_via_bridge \ "ws://127.0.0.1:9910" \ - "//Alice" + "$STATEMINE_ACCOUNT_SEED_FOR_LOCAL" \ + "$WOCKMINT_ACCOUNT_ADDRESS_FOR_LOCAL" ;; transfer-asset-from-statemine-rococo) ensure_polkadot_js_api transfer_asset_via_bridge \ "wss://ws-rococo-rockmine2-collator-node-0.parity-testnet.parity.io" \ - "${ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO}" + "$ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO" \ + "$WOCKMINT_ACCOUNT_ADDRESS_FOR_ROCOCO" ;; ping-via-bridge-from-statemine-local) ensure_polkadot_js_api ping_via_bridge \ "ws://127.0.0.1:9910" \ - "${STATEMINE_ACCOUNT_SEED_FOR_LOCAL}" + "$STATEMINE_ACCOUNT_SEED_FOR_LOCAL" \ + "$WOCKMINT_ACCOUNT_ADDRESS_FOR_LOCAL" ;; ping-via-bridge-from-statemine-rococo) ensure_polkadot_js_api ping_via_bridge \ "wss://ws-rococo-rockmine2-collator-node-0.parity-testnet.parity.io" \ - "${ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO}" + "${ROCKMINE2_ACCOUNT_SEED_FOR_ROCOCO}" \ + "$WOCKMINT_ACCOUNT_ADDRESS_FOR_ROCOCO" ;; drip) transfer_balance \ From c70f503eab10190013ff28975b8e80b3137d45e3 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 12 Apr 2023 16:59:01 +0200 Subject: [PATCH 235/263] README.md --- parachains/runtimes/bridge-hubs/README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 3c9f86295f0..a76fc7df519 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -5,11 +5,14 @@ + [Run relayers (Rococo, Wococo)](#run-relayers--rococo--wococo-) - [Run with script (alternative 1)](#run-with-script--alternative-1-) - [Run with binary (alternative 2)](#run-with-binary--alternative-2-) - + [Send messages (Rococo, Wococo)](#send-messages--rococo--wococo-) - - [Local Rococo:Statemine -> Wococo:Westmint](#local-rococo-statemine----wococo-westmint) - - [Live Rococo:Rockmine2 -> Wococo:Wockmint](#live-rococo-rockmine2----wococo-wockmint) + + [Send messages](#send-messages) + - [Local zombienet run](#local-zombienet-run) + - [Live Rockmine2 to Wockmint](#live-rockmine2-to-wockmint) * [How to test local BridgeHubKusama](#how-to-test-local-bridgehubkusama) + * [How to test local BridgeHubPolkadot](#how-to-test-local-bridgehubpolkadot) * [Git subtree `./bridges`](#git-subtree---bridges-) + + [How to update `bridges` subtree](#how-to-update--bridges--subtree) + + [How was first time initialized (dont need anymore)](#how-was-first-time-initialized--dont-need-anymore-) # Bridge-hub Parachains @@ -167,9 +170,9 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - Pallet: **bridgeRococoParachain** - Keys: **bestParaHeads()** -### Send messages (Rococo, Wococo) +### Send messages -#### Local (zombienet) run +#### Local zombienet run 1. allow bridge transfer on statemine/westmint (governance-like): ``` @@ -193,7 +196,7 @@ RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - Westmint (see `xcmpQueue.Success` for `transfer-asset` and `xcmpQueue.Fail` for `ping-via-bridge`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9010#/explorer - BridgeHubRococo (see `bridgeWococoMessages.MessagesDelivered`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8943#/explorer -#### Live Rococo:Rockmine2 -> Wococo:Wockmint +#### Live Rockmine2 to Wockmint - uses account seed on Live Rococo:Rockmine2 ``` cd From 6a1272401b0cb7988438cdc2b605f13730620733 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 14 Apr 2023 15:44:06 +0200 Subject: [PATCH 236/263] Compile fix + log xcm trace all --- bridges/primitives/messages/src/lib.rs | 4 ++-- .../bridge-hubs/bridge_hub_rococo_local_network.toml | 10 +++++----- .../bridge-hubs/bridge_hub_wococo_local_network.toml | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bridges/primitives/messages/src/lib.rs b/bridges/primitives/messages/src/lib.rs index 754349d634e..cb6fa36661a 100644 --- a/bridges/primitives/messages/src/lib.rs +++ b/bridges/primitives/messages/src/lib.rs @@ -194,7 +194,7 @@ impl InboundLaneData { } /// Outbound message details, returned by runtime APIs. -#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq)] +#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct OutboundMessageDetails { /// Nonce assigned to the message. pub nonce: MessageNonce, @@ -208,7 +208,7 @@ pub struct OutboundMessageDetails { } /// Inbound message details, returned by runtime APIs. -#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq)] +#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct InboundMessageDetails { /// Computed message dispatch weight. /// diff --git a/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml b/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml index 529fd638674..e5a8459a76a 100644 --- a/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml +++ b/zombienet/bridge-hubs/bridge_hub_rococo_local_network.toml @@ -3,7 +3,7 @@ node_spawn_timeout = 240 [relaychain] default_command = "{{POLKADOT_BINARY_PATH}}" -default_args = [ "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace" ] +default_args = [ "-lparachain=debug,xcm=trace" ] chain = "rococo-local" [[relaychain.nodes]] @@ -40,7 +40,7 @@ cumulus_based = true rpc_port = 8933 ws_port = 8943 args = [ - "-lparachain=debug,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace", + "-lparachain=debug,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace", ] extra_args = [ "--force-authoring", "--no-mdns", "--bootnodes {{'bob-collator'|zombie('multiAddress')}}", @@ -55,7 +55,7 @@ cumulus_based = true rpc_port = 8934 ws_port = 8944 args = [ - "-lparachain=trace,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace", + "-lparachain=trace,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace", ] extra_args = [ "--force-authoring", "--no-mdns", "--bootnodes {{'alice-collator'|zombie('multiAddress')}}", @@ -73,7 +73,7 @@ cumulus_based = true ws_port = 9910 command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ROCKMINE}}" args = [ - "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,runtime::bridge-assets-transfer=trace,xcm::should_execute=trace", + "-lparachain=debug,xcm=trace", ] extra_args = [ "--no-mdns", "--bootnodes {{'rockmine-collator2'|zombie('multiAddress')}}", @@ -84,7 +84,7 @@ cumulus_based = true name = "rockmine-collator2" command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_ROCKMINE}}" args = [ - "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,runtime::bridge-assets-transfer=trace,xcm::should_execute=trace", + "-lparachain=debug,xcm=trace", ] extra_args = [ "--no-mdns", "--bootnodes {{'rockmine-collator1'|zombie('multiAddress')}}", diff --git a/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml b/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml index cf8afd63ef5..55a95eb85f7 100644 --- a/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml +++ b/zombienet/bridge-hubs/bridge_hub_wococo_local_network.toml @@ -3,7 +3,7 @@ node_spawn_timeout = 240 [relaychain] default_command = "{{POLKADOT_BINARY_PATH}}" -default_args = [ "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace" ] +default_args = [ "-lparachain=debug,xcm=trace" ] chain = "wococo-local" [[relaychain.nodes]] @@ -40,7 +40,7 @@ cumulus_based = true rpc_port = 8935 ws_port = 8945 args = [ - "-lparachain=debug,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace", + "-lparachain=debug,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace", ] extra_args = [ "--force-authoring", "--no-mdns", "--bootnodes {{'bob-collator-wo'|zombie('multiAddress')}}", @@ -55,7 +55,7 @@ cumulus_based = true rpc_port = 8936 ws_port = 8946 args = [ - "-lparachain=trace,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace", + "-lparachain=trace,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace", ] extra_args = [ "--force-authoring", "--no-mdns", "--bootnodes {{'alice-collator-wo'|zombie('multiAddress')}}", @@ -73,7 +73,7 @@ cumulus_based = true ws_port = 9010 command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_WOCKMINT}}" args = [ - "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace", + "-lparachain=debug,xcm=trace", ] extra_args = [ "--no-mdns", "--bootnodes {{'wockmint-collator2'|zombie('multiAddress')}}", @@ -84,7 +84,7 @@ cumulus_based = true name = "wockmint-collator2" command = "{{POLKADOT_PARACHAIN_BINARY_PATH_FOR_WOCKMINT}}" args = [ - "-lparachain=debug,xcm::weight=trace,xcm::filter_asset_location=trace,xcm::send_xcm=trace,xcm::barriers=trace,xcm::barrier=trace,xcm::execute_xcm=trace,xcm::contains=trace,xcm::execute_xcm_in_credit,xcm::process_instruction=trace,xcm::currency_adapter=trace,xcm::origin_conversion=trace,xcm::fungibles_adapter=trace,xcm::process=trace,xcm::execute=trace,xcm::should_execute=trace", + "-lparachain=debug,xcm=trace", ] extra_args = [ "--no-mdns", "--bootnodes {{'wockmint-collator1'|zombie('multiAddress')}}", From 795fe4edd78ebef496fbb80880864eb61466566f Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 14 Apr 2023 16:05:02 +0200 Subject: [PATCH 237/263] Initial version of bridges pallet as subtree of https://github.com/paritytech/parity-bridges-common Added `Bridges subtree files` pr review rule --- .github/pr-custom-review.yml | 10 ++- Cargo.toml | 5 ++ parachains/runtimes/bridge-hubs/README.md | 38 ++++++++++ scripts/bridges_update_subtree.sh | 85 +++++++++++++++++++++++ 4 files changed, 137 insertions(+), 1 deletion(-) create mode 100755 scripts/bridges_update_subtree.sh diff --git a/.github/pr-custom-review.yml b/.github/pr-custom-review.yml index f8c887c5f4a..b833d3a01aa 100644 --- a/.github/pr-custom-review.yml +++ b/.github/pr-custom-review.yml @@ -19,12 +19,20 @@ rules: check_type: changed_files condition: include: .* - # excluding files from 'Runtime files' and 'CI files' rules + # excluding files from 'Runtime files' and 'CI files' rules and `Bridges subtree files` exclude: ^parachains/runtimes/assets/(statemine|statemint)/src/[^/]+\.rs$|^parachains/runtimes/bridge-hubs/(bridge-hub-kusama|bridge-hub-polkadot)/src/[^/]+\.rs$|^parachains/runtimes/collectives/collectives-polkadot/src/[^/]+\.rs$|^parachains/common/src/[^/]+\.rs$|^\.gitlab-ci\.yml|^scripts/ci/.*|^\.github/.* min_approvals: 2 teams: - core-devs + # if there are any changes in the bridges subtree (in case of backport changes back to bridges repo) + - name: Bridges subtree files + check_type: changed_files + condition: ^bridges/.* + min_approvals: 1 + teams: + - bridges-core + - name: CI files check_type: changed_files condition: diff --git a/Cargo.toml b/Cargo.toml index cdcf4730f72..4ceb8d5c04a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,11 @@ resolver = "2" members = [ + "bridges/bin/runtime-common", + "bridges/modules/grandpa", + "bridges/modules/messages", + "bridges/modules/parachains", + "bridges/modules/relayers", "client/cli", "client/consensus/aura", "client/consensus/common", diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 79e82f3497a..7a6cb35aaf7 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -35,3 +35,41 @@ or # BridgeHubPolkadot zombienet-linux --provider native spawn ./zombienet/examples/bridge_hub_polkadot_local_network.toml ``` + +---- +## Git subtree `./bridges` + +Add Bridges repo as a local remote and synchronize it with latest `master` from bridges repo: + +### How to update `bridges` subtree +``` +cd +# this will update new git branches from bridges repo +# there could be unresolved conflicts, but dont worry, +# lots of them are caused because of removed unneeded files with patch step :) +# so before solving conflicts just run patch +./scripts/bridges_update_subtree.sh fetch +# this will remove unneeded files and checks if subtree modules compiles +./scripts/bridges_update_subtree.sh patch +# if there are conflicts, this could help, removes locally deleted files at least +# (but you can also do this manually) +./scripts/bridges_update_subtree.sh merge +# when conflicts resolved, you can check build again - should pass +# also important: this updates global Cargo.lock +./scripts/bridges_update_subtree.sh patch +```` +We use `--squash` to avoid adding individual commits and rather squashing them +all into one. +Now we use `master` branch, but in future, it could change to some release branch/tag. + +### How was first time initialized (does not need anymore) +``` +cd +git remote add -f bridges git@github.com:paritytech/parity-bridges-common.git +# (ran just only first time, when subtree was initialized) +git subtree add --prefix=bridges bridges master --squash +# remove unnecessery files +./scripts/bridges_update_subtree.sh patch +./scripts/bridges_update_subtree.sh merge +git commit --amend +``` diff --git a/scripts/bridges_update_subtree.sh b/scripts/bridges_update_subtree.sh new file mode 100755 index 00000000000..bd9161b601f --- /dev/null +++ b/scripts/bridges_update_subtree.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# A script to udpate bridges repo as subtree to Cumulus +# Usage: +# ./scripts/bridges_update_subtree.sh fetch +# ./scripts/bridges_update_subtree.sh patch +# ./scripts/bridges_update_subtree.sh merge + +set -e + +BRIDGES_BRANCH="${BRANCH:-master}" +BRIDGES_TARGET_DIR="${TARGET_DIR:-bridges}" + +function fetch() { + # the script is able to work only on clean git copy + [[ -z "$(git status --porcelain)" ]] || { + echo >&2 "The git copy must be clean (stash all your changes):"; + git status --porcelain + exit 1; + } + + local bridges_remote=$(git remote -v | grep "parity-bridges-common.git (fetch)" | head -n1 | awk '{print $1;}') + if [ -z "$bridges_remote" ]; then + echo "" + echo "Adding new remote: 'bridges' repo..." + echo "" + echo "... check your YubiKey ..." + git remote add -f bridges git@github.com:paritytech/parity-bridges-common.git + bridges_remote="bridges" + else + echo "" + echo "Fetching remote: '${bridges_remote}' repo..." + echo "" + echo "... check your YubiKey ..." + git fetch ${bridges_remote} --prune + fi + + echo "" + echo "Syncing/updating subtree with remote branch '${bridges_remote}/$BRIDGES_BRANCH' to target directory: '$BRIDGES_TARGET_DIR'" + echo "" + echo "... check your YubiKey ..." + git subtree pull --prefix=$BRIDGES_TARGET_DIR ${bridges_remote} $BRIDGES_BRANCH --squash +} + +function patch() { + echo "" + echo "Patching/removing unneeded stuff from subtree in target directory: '$BRIDGES_TARGET_DIR'" + $BRIDGES_TARGET_DIR/scripts/verify-pallets-build.sh --ignore-git-state --no-revert +} + +function merge() { + echo "" + echo "Merging stuff from subtree in target directory: '$BRIDGES_TARGET_DIR'" + + # stage all removed by patch: DU, MD, D, AD - only from subtree directory + git status -s | awk '$1 == "DU" || $1 == "D" || $1 == "MD" || $1 == "AD" {print $2}' | grep "^$BRIDGES_TARGET_DIR/" | xargs git rm -q --ignore-unmatch + + echo "" + echo "When all conflicts are resolved, do 'git merge --continue'" +} + +function amend() { + echo "" + echo "Amend stuff from subtree in target directory: '$BRIDGES_TARGET_DIR'" + git commit --amend -S -m "updating bridges subtree + remove extra folders" +} + +case "$1" in + fetch) + fetch + ;; + patch) + patch + ;; + merge) + merge + ;; + amend) + amend + ;; + all) + fetch + patch + ;; +esac \ No newline at end of file From 3a8b6ea5290368cb0dbf39e76555d15d380fc194 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 17 Apr 2023 12:02:39 +0200 Subject: [PATCH 238/263] Squashed 'bridges/' changes from ecddd4a31..d30927c08 d30927c08 Revert dispatch-results (#2048) fa454c3b4 Remove unneeded files (#2044) 956a2c687 Bump clap from 4.2.1 to 4.2.2 91951583a Bump serde_json from 1.0.95 to 1.0.96 fcf462051 Bump h2 from 0.3.16 to 0.3.17 in /tools/runtime-codegen b751fb24f Bump h2 from 0.3.16 to 0.3.17 0bf31ab78 update refs (#2041) a490ecbd3 Fix CI build (#2039) 01139ebbc Define `RangeInclusiveExt` (#2037) 2db2f3fe3 Impl review suggestions from #2021 (#2036) 36292760f fix build step on CI (#2034) 3a2311b7a refund extra weight in receive_messages_delivery_proof call (#2031) 77f1641d1 Boost message delivery transaction priority (#2023) c23c4e441 Reject delivery transactions with at least one obsolete message (#2021) 68ba699b7 Reintroduce msg dispatch status reporting (#2027) d1e852cc3 Bump hex-literal from 0.4.0 to 0.4.1 16f25d613 Relay node down alert (#2002) 4bb1a6406 only refund if all bundled messages have been delivered (#2019) b9acf52bc fail with InsufficientDispatchWeight if dispatch_weight doesn't cover weight of all bundled messages (#2018) e10097fe2 Remove unneeded error debug strings (#2017) f5e38f057 enable metrics on all validator nodes (#2016) c35f1a187 Bump scale-info from 2.4.0 to 2.5.0 04c56977c Bump clap from 4.1.13 to 4.2.1 481371f3c Bump hex-literal from 0.3.4 to 0.4.0 6b9c1400d Bump serde from 1.0.158 to 1.0.159 e71877a2e Bump futures from 0.3.27 to 0.3.28 c019f4faa Bump tempfile from 3.4.0 to 3.5.0 2e6e79ef6 Bump serde_json from 1.0.94 to 1.0.95 0698b1ff9 Bump tokio from 1.26.0 to 1.27.0 35b149830 fix test step on CI (#2003) 0c3acc858 cleanup removed lane traces (#2001) 8bf81749e bump BridgeHubRococo/BridgeHubWococo versions (#2000) e53bb7f36 MaxRequests -> MaxFreeMandatoryHeadersPerBlock in pallet-bridge-grandpa (#1997) dfcc09043 Run tests for `runtime-benchmarks` feature only (#1998) efcc8db17 Run benchmarks for mock runtimes (#1996) git-subtree-dir: bridges git-subtree-split: d30927c089bd9e73092d1ec1a62895603cb277a3 --- .gitlab-ci.yml | 4 +- Cargo.lock | 1168 ++++++---- bin/millau/node/Cargo.toml | 4 +- bin/millau/node/src/service.rs | 17 +- bin/millau/runtime/Cargo.toml | 6 +- bin/millau/runtime/src/lib.rs | 10 +- .../runtime/src/rialto_parachain_messages.rs | 70 + bin/rialto-parachain/node/Cargo.toml | 2 +- bin/rialto-parachain/node/src/service.rs | 24 +- bin/rialto-parachain/runtime/Cargo.toml | 4 +- bin/rialto-parachain/runtime/src/lib.rs | 6 +- bin/rialto/node/Cargo.toml | 4 +- bin/rialto/node/src/command.rs | 4 +- bin/rialto/runtime/Cargo.toml | 2 +- bin/rialto/runtime/src/lib.rs | 6 +- bin/runtime-common/Cargo.toml | 2 +- bin/runtime-common/src/integrity.rs | 6 +- bin/runtime-common/src/lib.rs | 1 + bin/runtime-common/src/messages_call_ext.rs | 371 ++- .../src/messages_xcm_extension.rs | 16 +- bin/runtime-common/src/mock.rs | 10 +- bin/runtime-common/src/priority_calculator.rs | 201 ++ .../src/refund_relayer_extension.rs | 357 ++- .../dashboard/prometheus/targets.yml | 2 - .../dashboard/grafana/bridges-alerts.json | 1994 +++++++++++++++++ .../rococo-wococo-maintenance-dashboard.json | 1736 +++++++------- .../dashboard/grafana/beefy-dashboard.json | 347 ++- .../dashboard/prometheus/millau-targets.yml | 4 + .../dashboard/prometheus/rialto-targets.yml | 4 + deployments/networks/millau.yml | 16 +- deployments/networks/rialto.yml | 16 +- deployments/types-millau.json | 3 +- deployments/types-rialto.json | 3 +- deployments/types/common.json | 3 +- modules/beefy/Cargo.toml | 2 +- modules/beefy/src/lib.rs | 2 +- modules/grandpa/Cargo.toml | 2 +- modules/grandpa/src/benchmarking.rs | 2 + modules/grandpa/src/lib.rs | 200 +- modules/grandpa/src/mock.rs | 18 +- modules/messages/Cargo.toml | 6 +- modules/messages/src/benchmarking.rs | 4 +- modules/messages/src/lib.rs | 188 +- modules/messages/src/mock.rs | 60 +- modules/parachains/Cargo.toml | 2 +- modules/parachains/src/benchmarking.rs | 4 +- modules/parachains/src/lib.rs | 20 +- modules/parachains/src/mock.rs | 43 +- modules/relayers/Cargo.toml | 2 +- modules/relayers/src/benchmarking.rs | 2 + modules/relayers/src/mock.rs | 22 +- modules/relayers/src/payment_adapter.rs | 7 +- modules/shift-session-manager/Cargo.toml | 2 +- primitives/beefy/Cargo.toml | 2 +- primitives/chain-millau/Cargo.toml | 2 +- primitives/header-chain/Cargo.toml | 4 +- primitives/messages/Cargo.toml | 4 +- primitives/messages/src/lib.rs | 20 +- primitives/messages/src/source_chain.rs | 10 +- primitives/parachains/Cargo.toml | 2 +- primitives/polkadot-core/Cargo.toml | 2 +- primitives/relayers/Cargo.toml | 4 +- primitives/runtime/Cargo.toml | 4 +- primitives/runtime/src/lib.rs | 21 +- relays/bin-substrate/Cargo.toml | 6 +- relays/bin-substrate/src/chains/rococo.rs | 2 +- relays/bin-substrate/src/chains/wococo.rs | 2 +- relays/client-bridge-hub-kusama/Cargo.toml | 2 +- relays/client-bridge-hub-polkadot/Cargo.toml | 2 +- relays/client-bridge-hub-rococo/Cargo.toml | 2 +- relays/client-bridge-hub-wococo/Cargo.toml | 2 +- relays/client-rialto-parachain/Cargo.toml | 2 +- .../src/codegen_runtime.rs | 6 +- relays/client-substrate/Cargo.toml | 6 +- relays/finality/Cargo.toml | 2 +- relays/lib-substrate-relay/Cargo.toml | 2 +- relays/messages/Cargo.toml | 2 +- relays/parachains/Cargo.toml | 2 +- relays/utils/Cargo.toml | 4 +- scripts/dump-logs.sh | 2 - scripts/verify-pallets-build.sh | 2 + tools/runtime-codegen/Cargo.lock | 4 +- 82 files changed, 5270 insertions(+), 1868 deletions(-) create mode 100644 bin/runtime-common/src/priority_calculator.rs create mode 100644 deployments/bridges/rococo-wococo/dashboard/grafana/bridges-alerts.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f3ba2243c7f..eb88001b50c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -155,7 +155,7 @@ test: # Enable this, when you see: "`cargo metadata` can not fail on project `Cargo.toml`" #- time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-runtime\").manifest_path"` #- time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"kusama-runtime\").manifest_path"` - - CARGO_NET_OFFLINE=true SKIP_POLKADOT_RUNTIME_WASM_BUILD=1 SKIP_KUSAMA_RUNTIME_WASM_BUILD=1 SKIP_POLKADOT_TEST_RUNTIME_WASM_BUILD=1 time cargo test --verbose --workspace + - CARGO_NET_OFFLINE=true SKIP_WASM_BUILD=1 time cargo test --verbose --workspace --features runtime-benchmarks test-nightly: stage: test @@ -238,7 +238,7 @@ build: # Enable this, when you see: "`cargo metadata` can not fail on project `Cargo.toml`" #- time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-runtime\").manifest_path"` #- time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"kusama-runtime\").manifest_path"` - - CARGO_NET_OFFLINE=true SKIP_POLKADOT_RUNTIME_WASM_BUILD=1 SKIP_KUSAMA_RUNTIME_WASM_BUILD=1 SKIP_POLKADOT_TEST_RUNTIME_WASM_BUILD=1 time cargo build --release --verbose --workspace + - CARGO_NET_OFFLINE=true time cargo build --release --verbose --workspace after_script: # Prepare artifacts - mkdir -p ./artifacts diff --git a/Cargo.lock b/Cargo.lock index 20d18112ad1..f4b78e93bca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -203,6 +203,55 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "anyhow" version = "1.0.70" @@ -298,7 +347,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "synstructure", @@ -310,7 +359,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "synstructure", @@ -322,7 +371,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -457,9 +506,9 @@ version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 2.0.10", + "syn 2.0.14", ] [[package]] @@ -578,7 +627,7 @@ dependencies = [ [[package]] name = "binary-merkle-tree" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "hash-db", "log", @@ -605,7 +654,7 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "regex", "rustc-hash", @@ -854,7 +903,7 @@ dependencies = [ "finality-grandpa", "frame-support", "hex", - "hex-literal", + "hex-literal 0.4.1", "parity-scale-codec", "scale-info", "serde", @@ -882,7 +931,7 @@ dependencies = [ "bp-runtime", "frame-support", "hex", - "hex-literal", + "hex-literal 0.4.1", "parity-scale-codec", "scale-info", "serde", @@ -968,7 +1017,7 @@ dependencies = [ "bp-runtime", "frame-support", "hex", - "hex-literal", + "hex-literal 0.4.1", "parity-scale-codec", "scale-info", "sp-runtime", @@ -1024,7 +1073,7 @@ dependencies = [ "frame-support", "frame-system", "hash-db", - "hex-literal", + "hex-literal 0.4.1", "impl-trait-for-tuples", "num-traits", "parity-scale-codec", @@ -1406,39 +1455,45 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.13" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c911b090850d79fc64fe9ea01e28e465f65e821e08813ced95bced72f7a8a9b" +checksum = "9b802d85aaf3a1cdb02b224ba472ebdea62014fccfcb269b95a4d76443b5ee5a" dependencies = [ - "bitflags", + "clap_builder", "clap_derive", - "clap_lex", - "is-terminal", "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14a1a858f532119338887a4b8e1af9c60de8249cd7bafd68036a489e261e37b6" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex", "strsim 0.10.0", - "termcolor", ] [[package]] name = "clap_derive" -version = "4.1.12" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a932373bab67b984c790ddf2c9ca295d8e3af3b7ef92de5a5bacdccdee4b09b" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 2.0.10", + "syn 2.0.14", ] [[package]] name = "clap_lex" -version = "0.3.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" -dependencies = [ - "os_str_bytes", -] +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] name = "coarsetime" @@ -1462,6 +1517,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "comfy-table" version = "6.1.4" @@ -1821,9 +1882,9 @@ dependencies = [ [[package]] name = "cumulus-client-cli" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ - "clap 4.1.13", + "clap 4.2.2", "parity-scale-codec", "sc-chain-spec", "sc-cli", @@ -1836,7 +1897,7 @@ dependencies = [ [[package]] name = "cumulus-client-collator" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "cumulus-client-consensus-common", "cumulus-client-network", @@ -1859,7 +1920,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-aura" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "async-trait", "cumulus-client-consensus-common", @@ -1888,7 +1949,7 @@ dependencies = [ [[package]] name = "cumulus-client-consensus-common" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "async-trait", "cumulus-client-pov-recovery", @@ -1912,7 +1973,7 @@ dependencies = [ [[package]] name = "cumulus-client-network" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "async-trait", "cumulus-relay-chain-interface", @@ -1935,7 +1996,7 @@ dependencies = [ [[package]] name = "cumulus-client-pov-recovery" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -1959,7 +2020,7 @@ dependencies = [ [[package]] name = "cumulus-client-service" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "cumulus-client-cli", "cumulus-client-collator", @@ -1994,7 +2055,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-aura-ext" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "frame-support", "frame-system", @@ -2010,7 +2071,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-dmp-queue" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -2027,7 +2088,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-parachain-system" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "bytes", "cumulus-pallet-parachain-system-proc-macro", @@ -2056,18 +2117,18 @@ dependencies = [ [[package]] name = "cumulus-pallet-parachain-system-proc-macro" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] name = "cumulus-pallet-xcm" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -2083,7 +2144,7 @@ dependencies = [ [[package]] name = "cumulus-pallet-xcmp-queue" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -2103,12 +2164,13 @@ dependencies = [ [[package]] name = "cumulus-primitives-core" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "parity-scale-codec", "polkadot-core-primitives", "polkadot-parachain", "polkadot-primitives", + "scale-info", "sp-api", "sp-runtime", "sp-std 5.0.0", @@ -2119,7 +2181,7 @@ dependencies = [ [[package]] name = "cumulus-primitives-parachain-inherent" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2142,7 +2204,7 @@ dependencies = [ [[package]] name = "cumulus-primitives-timestamp" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "cumulus-primitives-core", "futures", @@ -2155,7 +2217,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-inprocess-interface" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2180,7 +2242,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-interface" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2198,7 +2260,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-minimal-node" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "array-bytes 6.0.0", "async-trait", @@ -2207,19 +2269,23 @@ dependencies = [ "cumulus-relay-chain-rpc-interface", "futures", "lru 0.9.0", + "polkadot-availability-recovery", + "polkadot-collator-protocol", "polkadot-core-primitives", "polkadot-network-bridge", + "polkadot-node-collation-generation", + "polkadot-node-core-runtime-api", "polkadot-node-network-protocol", "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-primitives", - "polkadot-service", "sc-authority-discovery", "sc-client-api", "sc-network", "sc-network-common", "sc-service", "sc-tracing", + "sc-utils", "sp-api", "sp-blockchain", "sp-consensus", @@ -2232,7 +2298,7 @@ dependencies = [ [[package]] name = "cumulus-relay-chain-rpc-interface" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "async-trait", "cumulus-primitives-core", @@ -2262,7 +2328,7 @@ dependencies = [ [[package]] name = "cumulus-test-relay-sproof-builder" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "cumulus-primitives-core", "parity-scale-codec", @@ -2364,10 +2430,10 @@ dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "scratch", - "syn 2.0.10", + "syn 2.0.14", ] [[package]] @@ -2382,9 +2448,9 @@ version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 2.0.10", + "syn 2.0.14", ] [[package]] @@ -2405,7 +2471,7 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "strsim 0.10.0", "syn 1.0.109", @@ -2503,7 +2569,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -2514,7 +2580,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -2535,7 +2601,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" dependencies = [ "darling", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -2557,7 +2623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "rustc_version", "syn 1.0.109", @@ -2645,7 +2711,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -2684,7 +2750,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -2819,7 +2885,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -2839,7 +2905,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -2850,9 +2916,9 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48016319042fb7c87b78d2993084a831793a897a5cd1a2a67cab9d1eeb4b7d76" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 2.0.10", + "syn 2.0.14", ] [[package]] @@ -2956,7 +3022,7 @@ checksum = "a718c0675c555c5f976fff4ea9e2c150fa06cefa201cadef87cfbf9324075881" dependencies = [ "blake3", "fs-err", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", ] @@ -2968,9 +3034,8 @@ checksum = "3774182a5df13c3d1690311ad32fbe913feef26baba609fa2dd5f72042bd2ab6" dependencies = [ "blake2", "fs-err", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", ] [[package]] @@ -2981,11 +3046,24 @@ checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" dependencies = [ "blake2", "fs-err", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] +[[package]] +name = "expander" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f86a749cf851891866c10515ef6c299b5c69661465e9c3bbe7e07a2b77fb0f7" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + [[package]] name = "fake-simd" version = "0.1.2" @@ -3026,7 +3104,7 @@ dependencies = [ "expander 0.0.4", "indexmap", "proc-macro-crate", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "thiserror", @@ -3085,7 +3163,7 @@ checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "windows-sys 0.45.0", ] @@ -3167,7 +3245,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", ] @@ -3190,7 +3268,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "frame-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support", "frame-support-procedural", @@ -3215,12 +3293,12 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "Inflector", "array-bytes 4.2.0", "chrono", - "clap 4.1.13", + "clap 4.2.2", "comfy-table", "frame-benchmarking", "frame-support", @@ -3262,18 +3340,18 @@ dependencies = [ [[package]] name = "frame-election-provider-solution-type" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] name = "frame-election-provider-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-election-provider-solution-type", "frame-support", @@ -3290,7 +3368,7 @@ dependencies = [ [[package]] name = "frame-executive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support", "frame-system", @@ -3305,9 +3383,9 @@ dependencies = [ [[package]] name = "frame-metadata" -version = "15.0.0" +version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" +checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" dependencies = [ "cfg-if 1.0.0", "parity-scale-codec", @@ -3318,7 +3396,7 @@ dependencies = [ [[package]] name = "frame-remote-externalities" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "futures", "log", @@ -3334,7 +3412,7 @@ dependencies = [ [[package]] name = "frame-support" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "bitflags", "environmental", @@ -3367,44 +3445,45 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "Inflector", "cfg-expr", "derive-syn-parse", "frame-support-procedural-tools", "itertools", - "proc-macro2 1.0.53", + "proc-macro-warning", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] name = "frame-support-procedural-tools" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] name = "frame-support-procedural-tools-derive" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] name = "frame-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support", "log", @@ -3422,7 +3501,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -3437,7 +3516,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", "sp-api", @@ -3446,7 +3525,7 @@ dependencies = [ [[package]] name = "frame-try-runtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support", "parity-scale-codec", @@ -3490,9 +3569,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -3505,9 +3584,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -3515,15 +3594,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -3533,9 +3612,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-lite" @@ -3554,13 +3633,13 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -3576,15 +3655,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-timer" @@ -3594,9 +3673,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -3765,9 +3844,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" dependencies = [ "bytes", "fnv", @@ -3880,6 +3959,12 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hkdf" version = "0.12.3" @@ -4151,7 +4236,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -4435,7 +4520,7 @@ checksum = "baa6da1e4199c10d7b1d0a6e5e8bd8e55f351163b6f4b3cbb044672a69bd4c1c" dependencies = [ "heck 0.4.1", "proc-macro-crate", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -4513,7 +4598,7 @@ dependencies = [ [[package]] name = "kusama-runtime" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bitvec", "frame-benchmarking", @@ -4524,7 +4609,7 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", - "hex-literal", + "hex-literal 0.3.4", "kusama-runtime-constants", "log", "pallet-authority-discovery", @@ -4611,7 +4696,7 @@ dependencies = [ [[package]] name = "kusama-runtime-constants" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "frame-support", "polkadot-primitives", @@ -5509,7 +5594,7 @@ dependencies = [ name = "millau-bridge-node" version = "0.1.0" dependencies = [ - "clap 4.1.13", + "clap 4.2.2", "frame-benchmarking", "frame-benchmarking-cli", "jsonrpsee", @@ -5564,7 +5649,7 @@ dependencies = [ "frame-support", "frame-system", "frame-system-rpc-runtime-api", - "hex-literal", + "hex-literal 0.4.1", "pallet-aura", "pallet-balances", "pallet-beefy", @@ -5641,7 +5726,7 @@ dependencies = [ [[package]] name = "mmr-gadget" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "futures", "log", @@ -5660,7 +5745,7 @@ dependencies = [ [[package]] name = "mmr-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "anyhow", "jsonrpsee", @@ -5695,7 +5780,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "832663583d5fa284ca8810bf7015e46c9fff9622d3cf34bd1eea5003fec06dd0" dependencies = [ "cfg-if 1.0.0", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -5786,7 +5871,7 @@ checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" dependencies = [ "proc-macro-crate", "proc-macro-error", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "synstructure", @@ -5834,7 +5919,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -5937,7 +6022,7 @@ name = "node-inspect" version = "0.9.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ - "clap 4.1.13", + "clap 4.2.2", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -6138,9 +6223,9 @@ dependencies = [ [[package]] name = "orchestra" -version = "0.0.4" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17e7d5b6bb115db09390bed8842c94180893dd83df3dfce7354f2a2aa090a4ee" +checksum = "227585216d05ba65c7ab0a0450a3cf2cbd81a98862a54c4df8e14d5ac6adb015" dependencies = [ "async-trait", "dyn-clonable", @@ -6155,15 +6240,15 @@ dependencies = [ [[package]] name = "orchestra-proc-macro" -version = "0.0.4" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2af4dabb2286b0be0e9711d2d24e25f6217048b71210cffd3daddc3b5c84e1f" +checksum = "2871aadd82a2c216ee68a69837a526dfe788ecbe74c4c5038a6acdbff6653066" dependencies = [ "expander 0.0.6", "itertools", "petgraph", "proc-macro-crate", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -6177,12 +6262,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "os_str_bytes" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - [[package]] name = "p256" version = "0.11.1" @@ -6218,7 +6297,7 @@ dependencies = [ [[package]] name = "pallet-aura" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support", "frame-system", @@ -6234,7 +6313,7 @@ dependencies = [ [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support", "frame-system", @@ -6250,7 +6329,7 @@ dependencies = [ [[package]] name = "pallet-authorship" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support", "frame-system", @@ -6264,7 +6343,7 @@ dependencies = [ [[package]] name = "pallet-babe" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6288,7 +6367,7 @@ dependencies = [ [[package]] name = "pallet-bags-list" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6308,7 +6387,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6323,7 +6402,7 @@ dependencies = [ [[package]] name = "pallet-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support", "frame-system", @@ -6342,7 +6421,7 @@ dependencies = [ [[package]] name = "pallet-beefy-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "array-bytes 4.2.0", "binary-merkle-tree", @@ -6366,7 +6445,7 @@ dependencies = [ [[package]] name = "pallet-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6496,7 +6575,7 @@ dependencies = [ [[package]] name = "pallet-child-bounties" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6515,7 +6594,7 @@ dependencies = [ [[package]] name = "pallet-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6532,7 +6611,7 @@ dependencies = [ [[package]] name = "pallet-conviction-voting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "assert_matches", "frame-benchmarking", @@ -6549,7 +6628,7 @@ dependencies = [ [[package]] name = "pallet-democracy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6567,7 +6646,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6590,7 +6669,7 @@ dependencies = [ [[package]] name = "pallet-election-provider-support-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6603,7 +6682,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6621,7 +6700,7 @@ dependencies = [ [[package]] name = "pallet-fast-unstake" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6639,7 +6718,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6662,7 +6741,7 @@ dependencies = [ [[package]] name = "pallet-identity" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "enumflags2", "frame-benchmarking", @@ -6678,7 +6757,7 @@ dependencies = [ [[package]] name = "pallet-im-online" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6698,7 +6777,7 @@ dependencies = [ [[package]] name = "pallet-indices" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6715,7 +6794,7 @@ dependencies = [ [[package]] name = "pallet-membership" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6732,7 +6811,7 @@ dependencies = [ [[package]] name = "pallet-mmr" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6749,7 +6828,7 @@ dependencies = [ [[package]] name = "pallet-multisig" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6765,7 +6844,7 @@ dependencies = [ [[package]] name = "pallet-nis" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6781,7 +6860,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support", "frame-system", @@ -6798,7 +6877,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-benchmarking" version = "1.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6818,7 +6897,7 @@ dependencies = [ [[package]] name = "pallet-nomination-pools-runtime-api" version = "1.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "pallet-nomination-pools", "parity-scale-codec", @@ -6829,7 +6908,7 @@ dependencies = [ [[package]] name = "pallet-offences" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support", "frame-system", @@ -6846,7 +6925,7 @@ dependencies = [ [[package]] name = "pallet-offences-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -6870,7 +6949,7 @@ dependencies = [ [[package]] name = "pallet-preimage" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6887,7 +6966,7 @@ dependencies = [ [[package]] name = "pallet-proxy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6902,7 +6981,7 @@ dependencies = [ [[package]] name = "pallet-ranked-collective" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6920,7 +6999,7 @@ dependencies = [ [[package]] name = "pallet-recovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6935,7 +7014,7 @@ dependencies = [ [[package]] name = "pallet-referenda" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "assert_matches", "frame-benchmarking", @@ -6954,7 +7033,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -6971,7 +7050,7 @@ dependencies = [ [[package]] name = "pallet-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support", "frame-system", @@ -6992,7 +7071,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -7023,7 +7102,7 @@ dependencies = [ [[package]] name = "pallet-society" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support", "frame-system", @@ -7037,7 +7116,7 @@ dependencies = [ [[package]] name = "pallet-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-election-provider-support", @@ -7060,18 +7139,18 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] name = "pallet-staking-reward-fn" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "log", "sp-arithmetic", @@ -7080,7 +7159,7 @@ dependencies = [ [[package]] name = "pallet-staking-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", "sp-api", @@ -7089,7 +7168,7 @@ dependencies = [ [[package]] name = "pallet-state-trie-migration" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#83c81985521b9f956890539d8b62371c40093c60" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -7106,7 +7185,7 @@ dependencies = [ [[package]] name = "pallet-sudo" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support", "frame-system", @@ -7120,7 +7199,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -7138,7 +7217,7 @@ dependencies = [ [[package]] name = "pallet-tips" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -7157,7 +7236,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-support", "frame-system", @@ -7173,7 +7252,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "jsonrpsee", "pallet-transaction-payment-rpc-runtime-api", @@ -7189,7 +7268,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -7201,7 +7280,7 @@ dependencies = [ [[package]] name = "pallet-treasury" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -7218,7 +7297,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -7234,7 +7313,7 @@ dependencies = [ [[package]] name = "pallet-vesting" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -7249,7 +7328,7 @@ dependencies = [ [[package]] name = "pallet-whitelist" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-benchmarking", "frame-support", @@ -7264,7 +7343,7 @@ dependencies = [ [[package]] name = "pallet-xcm" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bounded-collections", "frame-benchmarking", @@ -7285,7 +7364,7 @@ dependencies = [ [[package]] name = "pallet-xcm-benchmarks" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "frame-benchmarking", "frame-support", @@ -7304,7 +7383,7 @@ dependencies = [ [[package]] name = "parachain-info" version = "0.1.0" -source = "git+https://github.com/paritytech/cumulus?branch=master#020a024ab56a75812f6209842c369b631339609e" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ "cumulus-primitives-core", "frame-support", @@ -7370,7 +7449,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -7405,7 +7484,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "syn 1.0.109", "synstructure", ] @@ -7452,7 +7531,7 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi", ] @@ -7465,7 +7544,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] @@ -7552,7 +7631,7 @@ checksum = "75a1ef20bf3193c15ac345acb32e26b3dc3223aff4d77ae4fc5359567683796b" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -7593,7 +7672,7 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -7657,9 +7736,10 @@ checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" [[package]] name = "polkadot-approval-distribution" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "futures", + "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", @@ -7672,7 +7752,7 @@ dependencies = [ [[package]] name = "polkadot-availability-bitfield-distribution" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "futures", "polkadot-node-network-protocol", @@ -7686,7 +7766,7 @@ dependencies = [ [[package]] name = "polkadot-availability-distribution" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "derive_more", "fatality", @@ -7709,7 +7789,7 @@ dependencies = [ [[package]] name = "polkadot-availability-recovery" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "fatality", "futures", @@ -7730,9 +7810,9 @@ dependencies = [ [[package]] name = "polkadot-cli" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ - "clap 4.1.13", + "clap 4.2.2", "frame-benchmarking-cli", "futures", "log", @@ -7757,7 +7837,7 @@ dependencies = [ [[package]] name = "polkadot-client" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "async-trait", "frame-benchmarking", @@ -7799,7 +7879,7 @@ dependencies = [ [[package]] name = "polkadot-collator-protocol" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "always-assert", "bitvec", @@ -7821,7 +7901,7 @@ dependencies = [ [[package]] name = "polkadot-core-primitives" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "parity-scale-codec", "scale-info", @@ -7833,7 +7913,7 @@ dependencies = [ [[package]] name = "polkadot-dispute-distribution" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "derive_more", "fatality", @@ -7858,7 +7938,7 @@ dependencies = [ [[package]] name = "polkadot-erasure-coding" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "parity-scale-codec", "polkadot-node-primitives", @@ -7872,7 +7952,7 @@ dependencies = [ [[package]] name = "polkadot-gossip-support" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "futures", "futures-timer", @@ -7892,7 +7972,7 @@ dependencies = [ [[package]] name = "polkadot-network-bridge" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "always-assert", "async-trait", @@ -7915,7 +7995,7 @@ dependencies = [ [[package]] name = "polkadot-node-collation-generation" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "futures", "parity-scale-codec", @@ -7933,7 +8013,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-approval-voting" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bitvec", "derive_more", @@ -7962,7 +8042,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-av-store" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bitvec", "futures", @@ -7983,7 +8063,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-backing" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bitvec", "fatality", @@ -8002,7 +8082,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-bitfield-signing" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "futures", "polkadot-node-subsystem", @@ -8017,7 +8097,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-candidate-validation" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "async-trait", "futures", @@ -8037,7 +8117,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-api" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "futures", "polkadot-node-metrics", @@ -8052,7 +8132,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-chain-selection" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "futures", "futures-timer", @@ -8069,7 +8149,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-dispute-coordinator" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "fatality", "futures", @@ -8088,7 +8168,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-parachains-inherent" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "async-trait", "futures", @@ -8105,7 +8185,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-provisioner" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bitvec", "fatality", @@ -8123,7 +8203,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "always-assert", "assert_matches", @@ -8150,6 +8230,7 @@ dependencies = [ "sp-maybe-compressed-blob", "sp-tracing", "sp-wasm-interface", + "substrate-build-script-utils", "tempfile", "tikv-jemalloc-ctl", "tokio", @@ -8159,7 +8240,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-pvf-checker" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "futures", "polkadot-node-primitives", @@ -8175,7 +8256,7 @@ dependencies = [ [[package]] name = "polkadot-node-core-runtime-api" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "futures", "lru 0.9.0", @@ -8190,7 +8271,7 @@ dependencies = [ [[package]] name = "polkadot-node-jaeger" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "lazy_static", "log", @@ -8208,7 +8289,7 @@ dependencies = [ [[package]] name = "polkadot-node-metrics" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bs58", "futures", @@ -8227,7 +8308,7 @@ dependencies = [ [[package]] name = "polkadot-node-network-protocol" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "async-trait", "derive_more", @@ -8249,7 +8330,7 @@ dependencies = [ [[package]] name = "polkadot-node-primitives" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bounded-vec", "futures", @@ -8266,13 +8347,13 @@ dependencies = [ "sp-maybe-compressed-blob", "sp-runtime", "thiserror", - "zstd", + "zstd 0.11.2+zstd.1.5.2", ] [[package]] name = "polkadot-node-subsystem" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "polkadot-node-jaeger", "polkadot-node-subsystem-types", @@ -8282,7 +8363,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-types" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "async-trait", "derive_more", @@ -8305,7 +8386,7 @@ dependencies = [ [[package]] name = "polkadot-node-subsystem-util" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "async-trait", "derive_more", @@ -8338,7 +8419,7 @@ dependencies = [ [[package]] name = "polkadot-overseer" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "async-trait", "futures", @@ -8361,7 +8442,7 @@ dependencies = [ [[package]] name = "polkadot-parachain" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bounded-collections", "derive_more", @@ -8378,10 +8459,10 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bitvec", - "hex-literal", + "hex-literal 0.3.4", "parity-scale-codec", "polkadot-core-primitives", "polkadot-parachain", @@ -8404,7 +8485,7 @@ dependencies = [ [[package]] name = "polkadot-rpc" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "jsonrpsee", "mmr-rpc", @@ -8436,7 +8517,7 @@ dependencies = [ [[package]] name = "polkadot-runtime" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bitvec", "frame-benchmarking", @@ -8447,7 +8528,7 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", - "hex-literal", + "hex-literal 0.3.4", "log", "pallet-authority-discovery", "pallet-authorship", @@ -8530,7 +8611,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-common" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bitvec", "frame-benchmarking", @@ -8576,7 +8657,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-constants" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "frame-support", "polkadot-primitives", @@ -8590,7 +8671,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-metrics" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bs58", "parity-scale-codec", @@ -8602,7 +8683,7 @@ dependencies = [ [[package]] name = "polkadot-runtime-parachains" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bitflags", "bitvec", @@ -8646,14 +8727,14 @@ dependencies = [ [[package]] name = "polkadot-service" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "async-trait", "frame-benchmarking-cli", "frame-support", "frame-system-rpc-runtime-api", "futures", - "hex-literal", + "hex-literal 0.3.4", "kusama-runtime", "kvdb", "kvdb-rocksdb", @@ -8755,7 +8836,7 @@ dependencies = [ [[package]] name = "polkadot-statement-distribution" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "arrayvec 0.5.2", "fatality", @@ -8776,7 +8857,7 @@ dependencies = [ [[package]] name = "polkadot-statement-table" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "parity-scale-codec", "polkadot-primitives", @@ -8876,7 +8957,7 @@ version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "syn 1.0.109", ] @@ -8927,7 +9008,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "version_check", @@ -8939,11 +9020,22 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "version_check", ] +[[package]] +name = "proc-macro-warning" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d4f284d87b9cedc2ff57223cbc4e3937cd6063c01e92c8e2a8c080df0013933" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + [[package]] name = "proc-macro2" version = "0.4.30" @@ -8955,9 +9047,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -8994,7 +9086,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -9052,7 +9144,7 @@ checksum = "4ea9b0f8cbe5e15a8a042d030bd96668db28ecb567ec37d691971ff5731d2b1b" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -9134,7 +9226,7 @@ version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", ] [[package]] @@ -9305,6 +9397,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -9312,7 +9413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom 0.2.8", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] @@ -9344,9 +9445,9 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 2.0.10", + "syn 2.0.14", ] [[package]] @@ -9691,7 +9792,7 @@ dependencies = [ name = "rialto-bridge-node" version = "0.1.0" dependencies = [ - "clap 4.1.13", + "clap 4.2.2", "frame-benchmarking", "frame-benchmarking-cli", "frame-support", @@ -9718,7 +9819,7 @@ dependencies = [ name = "rialto-parachain-collator" version = "0.1.0" dependencies = [ - "clap 4.1.13", + "clap 4.2.2", "cumulus-client-cli", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", @@ -9790,7 +9891,7 @@ dependencies = [ "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", - "hex-literal", + "hex-literal 0.4.1", "pallet-aura", "pallet-balances", "pallet-bridge-grandpa", @@ -9921,7 +10022,7 @@ dependencies = [ [[package]] name = "rococo-runtime" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "binary-merkle-tree", "frame-benchmarking", @@ -9931,7 +10032,7 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", - "hex-literal", + "hex-literal 0.3.4", "log", "pallet-authority-discovery", "pallet-authorship", @@ -10007,7 +10108,7 @@ dependencies = [ [[package]] name = "rococo-runtime-constants" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "frame-support", "polkadot-primitives", @@ -10247,7 +10348,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "log", "sp-core", @@ -10258,7 +10359,7 @@ dependencies = [ [[package]] name = "sc-authority-discovery" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "futures", @@ -10286,7 +10387,7 @@ dependencies = [ [[package]] name = "sc-basic-authorship" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "futures", "futures-timer", @@ -10309,7 +10410,7 @@ dependencies = [ [[package]] name = "sc-block-builder" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -10324,7 +10425,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "memmap2", "sc-chain-spec-derive", @@ -10343,22 +10444,22 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] name = "sc-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "array-bytes 4.2.0", "chrono", - "clap 4.1.13", + "clap 4.2.2", "fdlimit", "futures", "libp2p", @@ -10394,7 +10495,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "fnv", "futures", @@ -10420,7 +10521,7 @@ dependencies = [ [[package]] name = "sc-client-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "hash-db", "kvdb", @@ -10446,7 +10547,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "futures", @@ -10471,7 +10572,7 @@ dependencies = [ [[package]] name = "sc-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "futures", @@ -10500,7 +10601,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "fork-tree", @@ -10539,7 +10640,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "futures", "jsonrpsee", @@ -10561,7 +10662,7 @@ dependencies = [ [[package]] name = "sc-consensus-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10596,7 +10697,7 @@ dependencies = [ [[package]] name = "sc-consensus-beefy-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "futures", "jsonrpsee", @@ -10615,7 +10716,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "fork-tree", "parity-scale-codec", @@ -10628,7 +10729,7 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "ahash 0.8.3", "array-bytes 4.2.0", @@ -10668,7 +10769,7 @@ dependencies = [ [[package]] name = "sc-consensus-grandpa-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "finality-grandpa", "futures", @@ -10688,7 +10789,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "futures", @@ -10711,7 +10812,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "lru 0.8.1", "parity-scale-codec", @@ -10735,7 +10836,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", @@ -10748,7 +10849,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "log", "sc-allocator", @@ -10761,7 +10862,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "anyhow", "cfg-if 1.0.0", @@ -10779,7 +10880,7 @@ dependencies = [ [[package]] name = "sc-informant" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "ansi_term", "futures", @@ -10795,7 +10896,7 @@ dependencies = [ [[package]] name = "sc-keystore" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10810,7 +10911,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "array-bytes 4.2.0", "async-channel", @@ -10840,6 +10941,7 @@ dependencies = [ "serde", "serde_json", "smallvec", + "snow", "sp-arithmetic", "sp-blockchain", "sp-consensus", @@ -10854,7 +10956,7 @@ dependencies = [ [[package]] name = "sc-network-bitswap" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "cid", "futures", @@ -10874,7 +10976,7 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10902,7 +11004,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "ahash 0.8.3", "futures", @@ -10921,7 +11023,7 @@ dependencies = [ [[package]] name = "sc-network-light" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10943,7 +11045,7 @@ dependencies = [ [[package]] name = "sc-network-sync" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "array-bytes 4.2.0", "async-trait", @@ -10977,7 +11079,7 @@ dependencies = [ [[package]] name = "sc-network-transactions" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "array-bytes 4.2.0", "futures", @@ -10997,7 +11099,7 @@ dependencies = [ [[package]] name = "sc-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "array-bytes 4.2.0", "bytes", @@ -11028,7 +11130,7 @@ dependencies = [ [[package]] name = "sc-peerset" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "futures", "libp2p", @@ -11041,7 +11143,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -11050,7 +11152,7 @@ dependencies = [ [[package]] name = "sc-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "futures", "jsonrpsee", @@ -11080,7 +11182,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -11099,7 +11201,7 @@ dependencies = [ [[package]] name = "sc-rpc-server" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "http", "jsonrpsee", @@ -11114,7 +11216,7 @@ dependencies = [ [[package]] name = "sc-rpc-spec-v2" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "array-bytes 4.2.0", "futures", @@ -11140,7 +11242,7 @@ dependencies = [ [[package]] name = "sc-service" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "directories", @@ -11206,7 +11308,7 @@ dependencies = [ [[package]] name = "sc-state-db" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "log", "parity-scale-codec", @@ -11217,9 +11319,9 @@ dependencies = [ [[package]] name = "sc-storage-monitor" version = "0.1.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ - "clap 4.1.13", + "clap 4.2.2", "fs4", "futures", "log", @@ -11233,7 +11335,7 @@ dependencies = [ [[package]] name = "sc-sync-state-rpc" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "jsonrpsee", "parity-scale-codec", @@ -11252,7 +11354,7 @@ dependencies = [ [[package]] name = "sc-sysinfo" version = "6.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "futures", "libc", @@ -11271,7 +11373,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "chrono", "futures", @@ -11290,7 +11392,7 @@ dependencies = [ [[package]] name = "sc-tracing" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "ansi_term", "atty", @@ -11321,18 +11423,18 @@ dependencies = [ [[package]] name = "sc-tracing-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] name = "sc-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "futures", @@ -11359,7 +11461,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "futures", @@ -11373,7 +11475,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-channel", "futures", @@ -11410,9 +11512,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61471dff9096de1d8b2319efed7162081e96793f5ebb147e50db10d50d648a4d" +checksum = "0cfdffd972d76b22f3d7f81c8be34b2296afd3a25e0a547bd9abe340a4dbbe97" dependencies = [ "bitvec", "cfg-if 1.0.0", @@ -11424,12 +11526,12 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219580e803a66b3f05761fd06f1f879a872444e49ce23f73694d26e5a954c7e6" +checksum = "61fa974aea2d63dd18a4ec3a49d59af9f34178c73a4f56d2f18205628d00681e" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -11637,29 +11739,29 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.158" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.158" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 2.0.10", + "syn 2.0.14", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "indexmap", "itoa", @@ -11667,6 +11769,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde", +] + [[package]] name = "sha-1" version = "0.9.8" @@ -11840,7 +11951,7 @@ checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" [[package]] name = "slot-range-helper" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "enumn", "parity-scale-codec", @@ -11928,13 +12039,15 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "hash-db", "log", "parity-scale-codec", + "scale-info", "sp-api-proc-macro", "sp-core", + "sp-metadata-ir", "sp-runtime", "sp-state-machine", "sp-std 5.0.0", @@ -11946,21 +12059,21 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "Inflector", "blake2", "expander 1.0.0", "proc-macro-crate", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] name = "sp-application-crypto" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", "scale-info", @@ -11973,7 +12086,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "integer-sqrt", "num-traits", @@ -11987,7 +12100,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", "scale-info", @@ -12000,7 +12113,7 @@ dependencies = [ [[package]] name = "sp-block-builder" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", "sp-api", @@ -12012,7 +12125,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "futures", "log", @@ -12030,7 +12143,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "futures", @@ -12045,7 +12158,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "parity-scale-codec", @@ -12063,7 +12176,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "merlin", @@ -12086,7 +12199,7 @@ dependencies = [ [[package]] name = "sp-consensus-beefy" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "lazy_static", "parity-scale-codec", @@ -12105,7 +12218,7 @@ dependencies = [ [[package]] name = "sp-consensus-grandpa" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "finality-grandpa", "log", @@ -12123,7 +12236,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", "scale-info", @@ -12135,7 +12248,7 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", "scale-info", @@ -12148,13 +12261,13 @@ dependencies = [ [[package]] name = "sp-core" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "array-bytes 4.2.0", - "base58", "bitflags", "blake2", "bounded-collections", + "bs58", "dyn-clonable", "ed25519-zebra", "futures", @@ -12191,7 +12304,7 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "blake2b_simd", "byteorder", @@ -12220,18 +12333,18 @@ dependencies = [ [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "sp-core-hashing 5.0.0", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] name = "sp-database" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -12240,17 +12353,17 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] name = "sp-externalities" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "environmental", "parity-scale-codec", @@ -12261,7 +12374,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -12276,7 +12389,7 @@ dependencies = [ [[package]] name = "sp-io" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "bytes", "ed25519", @@ -12285,6 +12398,7 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", + "rustversion", "secp256k1", "sp-core", "sp-externalities", @@ -12301,7 +12415,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "lazy_static", "sp-core", @@ -12312,7 +12426,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "futures", "merlin", @@ -12328,16 +12442,27 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "thiserror", - "zstd", + "zstd 0.12.3+zstd.1.5.2", +] + +[[package]] +name = "sp-metadata-ir" +version = "0.1.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-std 5.0.0", ] [[package]] name = "sp-mmr-primitives" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "ckb-merkle-mountain-range 0.5.2", "log", @@ -12355,7 +12480,7 @@ dependencies = [ [[package]] name = "sp-npos-elections" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", "scale-info", @@ -12369,7 +12494,7 @@ dependencies = [ [[package]] name = "sp-offchain" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "sp-api", "sp-core", @@ -12379,7 +12504,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "backtrace", "lazy_static", @@ -12389,7 +12514,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "rustc-hash", "serde", @@ -12399,7 +12524,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "either", "hash256-std-hasher", @@ -12421,7 +12546,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -12439,19 +12564,19 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "Inflector", "proc-macro-crate", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] name = "sp-session" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", "scale-info", @@ -12465,7 +12590,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", "scale-info", @@ -12477,7 +12602,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "hash-db", "log", @@ -12497,7 +12622,7 @@ dependencies = [ [[package]] name = "sp-std" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" [[package]] name = "sp-std" @@ -12508,7 +12633,7 @@ checksum = "af0ee286f98455272f64ac5bb1384ff21ac029fbb669afbaf48477faff12760e" [[package]] name = "sp-storage" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "impl-serde", "parity-scale-codec", @@ -12521,7 +12646,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "futures-timer", @@ -12536,7 +12661,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", "sp-std 5.0.0", @@ -12548,7 +12673,7 @@ dependencies = [ [[package]] name = "sp-transaction-pool" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "sp-api", "sp-runtime", @@ -12557,7 +12682,7 @@ dependencies = [ [[package]] name = "sp-transaction-storage-proof" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "log", @@ -12573,7 +12698,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "ahash 0.8.3", "hash-db", @@ -12596,7 +12721,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "impl-serde", "parity-scale-codec", @@ -12613,18 +12738,18 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] name = "sp-wasm-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -12638,7 +12763,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "parity-scale-codec", "scale-info", @@ -12684,7 +12809,7 @@ checksum = "ecf0bd63593ef78eca595a7fc25e9a443ca46fe69fd472f8f09f5245cdcd769d" dependencies = [ "Inflector", "num-format", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "serde", "serde_json", @@ -12738,7 +12863,7 @@ checksum = "f2261c91034a1edc3fc4d1b80e89d82714faede0515c14a75da10cb941546bbf" dependencies = [ "cfg_aliases", "memchr", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -12751,7 +12876,7 @@ checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" dependencies = [ "cfg_aliases", "memchr", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -12802,7 +12927,7 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -12823,7 +12948,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "rustversion", "syn 1.0.109", @@ -12864,7 +12989,7 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "platforms 2.0.0", ] @@ -12872,7 +12997,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "frame-system-rpc-runtime-api", "futures", @@ -12891,7 +13016,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "hyper", "log", @@ -12921,7 +13046,7 @@ dependencies = [ "frame-support", "futures", "hex", - "hex-literal", + "hex-literal 0.4.1", "log", "millau-runtime", "num-format", @@ -13010,7 +13135,7 @@ dependencies = [ [[package]] name = "substrate-rpc-client" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", "jsonrpsee", @@ -13023,7 +13148,7 @@ dependencies = [ [[package]] name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "jsonrpsee", "log", @@ -13042,7 +13167,7 @@ dependencies = [ [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "ansi_term", "build-helper", @@ -13051,7 +13176,7 @@ dependencies = [ "sp-maybe-compressed-blob", "strum", "tempfile", - "toml", + "toml 0.7.3", "walkdir", "wasm-opt", ] @@ -13114,7 +13239,7 @@ dependencies = [ "jsonrpsee", "parity-scale-codec", "proc-macro-error", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "scale-info", "subxt-metadata", @@ -13163,18 +13288,18 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.10" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aad1363ed6d37b84299588d62d3a7d95b5a5c2d9aad5c85609fda12afaa1f40" +checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "unicode-ident", ] @@ -13185,7 +13310,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "unicode-xid 0.2.4", @@ -13241,15 +13366,15 @@ checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" [[package]] name = "tempfile" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if 1.0.0", "fastrand", - "redox_syscall", - "rustix 0.36.11", - "windows-sys 0.42.0", + "redox_syscall 0.3.5", + "rustix 0.37.3", + "windows-sys 0.45.0", ] [[package]] @@ -13291,9 +13416,9 @@ version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 2.0.10", + "syn 2.0.14", ] [[package]] @@ -13450,14 +13575,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.26.0" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot 0.12.1", @@ -13470,13 +13594,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -13526,11 +13650,26 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -13539,6 +13678,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -13603,7 +13744,7 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -13631,7 +13772,7 @@ dependencies = [ [[package]] name = "tracing-gum" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "polkadot-node-jaeger", "polkadot-primitives", @@ -13642,13 +13783,13 @@ dependencies = [ [[package]] name = "tracing-gum-proc-macro" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ - "expander 0.0.6", + "expander 2.0.0", "proc-macro-crate", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -13772,10 +13913,10 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "try-runtime-cli" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", - "clap 4.1.13", + "clap 4.2.2", "frame-remote-externalities", "hex", "log", @@ -13802,7 +13943,7 @@ dependencies = [ "sp-version", "sp-weights", "substrate-rpc-client", - "zstd", + "zstd 0.12.3+zstd.1.5.2", ] [[package]] @@ -13960,6 +14101,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "1.3.0" @@ -14081,7 +14228,7 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "wasm-bindgen-shared", @@ -14115,7 +14262,7 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "wasm-bindgen-backend", @@ -14289,9 +14436,9 @@ dependencies = [ "rustix 0.36.11", "serde", "sha2 0.10.6", - "toml", + "toml 0.5.11", "windows-sys 0.42.0", - "zstd", + "zstd 0.11.2+zstd.1.5.2", ] [[package]] @@ -14670,7 +14817,7 @@ dependencies = [ [[package]] name = "westend-runtime" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bitvec", "frame-benchmarking", @@ -14681,7 +14828,7 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", - "hex-literal", + "hex-literal 0.3.4", "log", "pallet-authority-discovery", "pallet-authorship", @@ -14762,7 +14909,7 @@ dependencies = [ [[package]] name = "westend-runtime-constants" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "frame-support", "polkadot-primitives", @@ -14850,7 +14997,7 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", ] [[package]] @@ -14859,12 +15006,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.2", "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.2", "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.2", ] @@ -14874,7 +15021,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -14883,21 +15039,42 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.2", "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.2", "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.34.0" @@ -14910,6 +15087,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.34.0" @@ -14922,6 +15105,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.34.0" @@ -14934,6 +15123,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.34.0" @@ -14946,12 +15141,24 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.34.0" @@ -14964,6 +15171,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winnow" version = "0.4.0" @@ -15053,7 +15266,7 @@ dependencies = [ [[package]] name = "xcm" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "bounded-collections", "derivative", @@ -15069,7 +15282,7 @@ dependencies = [ [[package]] name = "xcm-builder" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "frame-support", "frame-system", @@ -15090,7 +15303,7 @@ dependencies = [ [[package]] name = "xcm-executor" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "environmental", "frame-benchmarking", @@ -15110,12 +15323,12 @@ dependencies = [ [[package]] name = "xcm-procedural" version = "0.9.39" -source = "git+https://github.com/paritytech/polkadot?branch=master#2bbef460d24f44aeba9d4014062bb8c631327496" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ "Inflector", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -15162,7 +15375,7 @@ version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "synstructure", @@ -15174,7 +15387,16 @@ version = "0.11.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" dependencies = [ - "zstd-safe", + "zstd-safe 5.0.2+zstd.1.5.2", +] + +[[package]] +name = "zstd" +version = "0.12.3+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +dependencies = [ + "zstd-safe 6.0.5+zstd.1.5.4", ] [[package]] @@ -15187,6 +15409,16 @@ dependencies = [ "zstd-sys", ] +[[package]] +name = "zstd-safe" +version = "6.0.5+zstd.1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +dependencies = [ + "libc", + "zstd-sys", +] + [[package]] name = "zstd-sys" version = "2.0.7+zstd.1.5.4" diff --git a/bin/millau/node/Cargo.toml b/bin/millau/node/Cargo.toml index cfe6e921f4b..21b5cd02559 100644 --- a/bin/millau/node/Cargo.toml +++ b/bin/millau/node/Cargo.toml @@ -9,9 +9,9 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.1.13", features = ["derive"] } +clap = { version = "4.2.2", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } -serde_json = "1.0.94" +serde_json = "1.0.96" # Bridge dependencies diff --git a/bin/millau/node/src/service.rs b/bin/millau/node/src/service.rs index 7230accef8e..07cd29a0d6d 100644 --- a/bin/millau/node/src/service.rs +++ b/bin/millau/node/src/service.rs @@ -22,6 +22,7 @@ use sc_client_api::BlockBackend; use sc_consensus_aura::{CompatibilityMode, ImportQueueParams, SlotProportion, StartAuraParams}; use sc_consensus_grandpa::SharedVoterState; pub use sc_executor::NativeElseWasmExecutor; +use sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY}; use sc_service::{error::Error as ServiceError, Configuration, TaskManager}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; @@ -88,11 +89,17 @@ pub fn new_partial( }) .transpose()?; - let executor = NativeElseWasmExecutor::::new( - config.wasm_method, - config.default_heap_pages, - config.max_runtime_instances, - config.runtime_cache_size, + let heap_pages = config + .default_heap_pages + .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static { extra_pages: h as _ }); + let executor = NativeElseWasmExecutor::::new_with_wasm_executor( + WasmExecutor::builder() + .with_execution_method(config.wasm_method) + .with_onchain_heap_alloc_strategy(heap_pages) + .with_offchain_heap_alloc_strategy(heap_pages) + .with_max_runtime_instances(config.max_runtime_instances) + .with_runtime_cache_size(config.runtime_cache_size) + .build(), ); let (client, backend, keystore_container, task_manager) = diff --git a/bin/millau/runtime/Cargo.toml b/bin/millau/runtime/Cargo.toml index 2f828393336..e1a55ea6f24 100644 --- a/bin/millau/runtime/Cargo.toml +++ b/bin/millau/runtime/Cargo.toml @@ -7,9 +7,9 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -hex-literal = "0.3" +hex-literal = "0.4" codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } # Bridge dependencies @@ -69,7 +69,7 @@ xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } [dev-dependencies] -bridge-runtime-common = { path = "../../runtime-common", features = ["integrity-test"] } +bridge-runtime-common = { path = "../../runtime-common", features = ["integrity-test", "std"] } env_logger = "0.10" static_assertions = "1.1" diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index b0c6b2875fe..4e6f1e43e8c 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -399,11 +399,7 @@ pub type RialtoGrandpaInstance = (); impl pallet_bridge_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; type BridgedChain = bp_rialto::Rialto; - // This is a pretty unscientific cap. - // - // Note that once this is hit the pallet will essentially throttle incoming requests down to one - // call per block. - type MaxRequests = ConstU32<50>; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; type HeadersToKeep = ConstU32<{ bp_rialto::DAYS }>; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } @@ -412,7 +408,7 @@ pub type WestendGrandpaInstance = pallet_bridge_grandpa::Instance1; impl pallet_bridge_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; type BridgedChain = bp_westend::Westend; - type MaxRequests = ConstU32<50>; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; type HeadersToKeep = ConstU32<{ bp_westend::DAYS }>; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } @@ -593,11 +589,13 @@ generate_bridge_reject_obsolete_headers_and_messages! { bp_runtime::generate_static_str_provider!(BridgeRefundRialtoPara2000Lane0Msgs); /// Signed extension that refunds relayers that are delivering messages from the Rialto parachain. +pub type PriorityBoostPerMessage = ConstU64<921_900_294>; pub type BridgeRefundRialtoParachainMessages = RefundBridgedParachainMessages< Runtime, RefundableParachain, RefundableMessagesLane, ActualFeeRefund, + PriorityBoostPerMessage, StrBridgeRefundRialtoPara2000Lane0Msgs, >; diff --git a/bin/millau/runtime/src/rialto_parachain_messages.rs b/bin/millau/runtime/src/rialto_parachain_messages.rs index b3d33c1cefd..bef8a281188 100644 --- a/bin/millau/runtime/src/rialto_parachain_messages.rs +++ b/bin/millau/runtime/src/rialto_parachain_messages.rs @@ -137,3 +137,73 @@ impl XcmBlobHauler for ToRialtoParachainXcmBlobHauler { XCM_LANE } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + PriorityBoostPerMessage, RialtoGrandpaInstance, Runtime, + WithRialtoParachainMessagesInstance, + }; + + use bridge_runtime_common::{ + assert_complete_bridge_types, + integrity::{ + assert_complete_bridge_constants, check_message_lane_weights, + AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants, + AssertCompleteBridgeConstants, + }, + }; + + #[test] + fn ensure_millau_message_lane_weights_are_correct() { + check_message_lane_weights::( + bp_rialto_parachain::EXTRA_STORAGE_PROOF_SIZE, + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + ); + } + + #[test] + fn ensure_bridge_integrity() { + assert_complete_bridge_types!( + runtime: Runtime, + with_bridged_chain_grandpa_instance: RialtoGrandpaInstance, + with_bridged_chain_messages_instance: WithRialtoParachainMessagesInstance, + bridge: WithRialtoParachainMessageBridge, + this_chain: bp_millau::Millau, + bridged_chain: bp_rialto::Rialto, + ); + + assert_complete_bridge_constants::< + Runtime, + RialtoGrandpaInstance, + WithRialtoParachainMessagesInstance, + WithRialtoParachainMessageBridge, + >(AssertCompleteBridgeConstants { + this_chain_constants: AssertChainConstants { + block_length: bp_millau::BlockLength::get(), + block_weights: bp_millau::BlockWeights::get(), + }, + messages_pallet_constants: AssertBridgeMessagesPalletConstants { + max_unrewarded_relayers_in_bridged_confirmation_tx: + bp_rialto_parachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + max_unconfirmed_messages_in_bridged_confirmation_tx: + bp_rialto_parachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + bridged_chain_id: bp_runtime::RIALTO_PARACHAIN_CHAIN_ID, + }, + pallet_names: AssertBridgePalletNames { + with_this_chain_messages_pallet_name: bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME, + with_bridged_chain_grandpa_pallet_name: bp_rialto::WITH_RIALTO_GRANDPA_PALLET_NAME, + with_bridged_chain_messages_pallet_name: + bp_rialto_parachain::WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME, + }, + }); + + bridge_runtime_common::priority_calculator::ensure_priority_boost_is_sane::< + Runtime, + WithRialtoParachainMessagesInstance, + PriorityBoostPerMessage, + >(1_000_000); + } +} diff --git a/bin/rialto-parachain/node/Cargo.toml b/bin/rialto-parachain/node/Cargo.toml index c60bad8386a..2315eb94a11 100644 --- a/bin/rialto-parachain/node/Cargo.toml +++ b/bin/rialto-parachain/node/Cargo.toml @@ -17,7 +17,7 @@ default = [] runtime-benchmarks = ['rialto-parachain-runtime/runtime-benchmarks'] [dependencies] -clap = { version = "4.1.13", features = ["derive"] } +clap = { version = "4.2.2", features = ["derive"] } log = '0.4.17' codec = { package = 'parity-scale-codec', version = '3.1.5' } serde = { version = '1.0', features = ['derive'] } diff --git a/bin/rialto-parachain/node/src/service.rs b/bin/rialto-parachain/node/src/service.rs index 6662b2e9625..24eb94e655d 100644 --- a/bin/rialto-parachain/node/src/service.rs +++ b/bin/rialto-parachain/node/src/service.rs @@ -41,7 +41,10 @@ use cumulus_primitives_core::ParaId; use cumulus_relay_chain_interface::RelayChainInterface; use sc_consensus::ImportQueue; // Substrate Imports -use sc_executor::{NativeElseWasmExecutor, NativeExecutionDispatch}; +use sc_executor::{ + HeapAllocStrategy, NativeElseWasmExecutor, NativeExecutionDispatch, WasmExecutor, + DEFAULT_HEAP_ALLOC_STRATEGY, +}; use sc_network::NetworkBlock; use sc_network_sync::SyncingService; use sc_service::{Configuration, PartialComponents, TFullBackend, TFullClient, TaskManager}; @@ -131,12 +134,19 @@ where }) .transpose()?; - let executor = sc_executor::NativeElseWasmExecutor::::new( - config.wasm_method, - config.default_heap_pages, - config.max_runtime_instances, - config.runtime_cache_size, - ); + let heap_pages = config + .default_heap_pages + .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static { extra_pages: h as _ }); + let executor = + sc_executor::NativeElseWasmExecutor::::new_with_wasm_executor( + WasmExecutor::builder() + .with_execution_method(config.wasm_method) + .with_onchain_heap_alloc_strategy(heap_pages) + .with_offchain_heap_alloc_strategy(heap_pages) + .with_max_runtime_instances(config.max_runtime_instances) + .with_runtime_cache_size(config.runtime_cache_size) + .build(), + ); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( diff --git a/bin/rialto-parachain/runtime/Cargo.toml b/bin/rialto-parachain/runtime/Cargo.toml index 0018c8f3905..53f57e02619 100644 --- a/bin/rialto-parachain/runtime/Cargo.toml +++ b/bin/rialto-parachain/runtime/Cargo.toml @@ -11,8 +11,8 @@ substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", bran [dependencies] codec = { package = 'parity-scale-codec', version = '3.1.5', default-features = false, features = ['derive']} -hex-literal = "0.3" -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +hex-literal = "0.4" +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } # Bridge depedencies diff --git a/bin/rialto-parachain/runtime/src/lib.rs b/bin/rialto-parachain/runtime/src/lib.rs index b5419e2c25e..cd4e256f420 100644 --- a/bin/rialto-parachain/runtime/src/lib.rs +++ b/bin/rialto-parachain/runtime/src/lib.rs @@ -540,11 +540,7 @@ pub type MillauGrandpaInstance = (); impl pallet_bridge_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; type BridgedChain = bp_millau::Millau; - /// This is a pretty unscientific cap. - /// - /// Note that once this is hit the pallet will essentially throttle incoming requests down to - /// one call per block. - type MaxRequests = ConstU32<50>; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; type HeadersToKeep = ConstU32<{ bp_millau::DAYS as u32 }>; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } diff --git a/bin/rialto/node/Cargo.toml b/bin/rialto/node/Cargo.toml index bc8841c4f87..23c7d3315e7 100644 --- a/bin/rialto/node/Cargo.toml +++ b/bin/rialto/node/Cargo.toml @@ -9,8 +9,8 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.1.13", features = ["derive"] } -serde_json = "1.0.94" +clap = { version = "4.2.2", features = ["derive"] } +serde_json = "1.0.96" # Bridge dependencies diff --git a/bin/rialto/node/src/command.rs b/bin/rialto/node/src/command.rs index 8286444d3ea..0bff2e523b4 100644 --- a/bin/rialto/node/src/command.rs +++ b/bin/rialto/node/src/command.rs @@ -164,7 +164,7 @@ pub fn run() -> sc_cli::Result<()> { builder.with_colors(false); let _ = builder.init(); - polkadot_node_core_pvf::prepare_worker_entrypoint(&cmd.socket_path); + polkadot_node_core_pvf::prepare_worker_entrypoint(&cmd.socket_path, None); Ok(()) }, Some(crate::cli::Subcommand::PvfExecuteWorker(cmd)) => { @@ -172,7 +172,7 @@ pub fn run() -> sc_cli::Result<()> { builder.with_colors(false); let _ = builder.init(); - polkadot_node_core_pvf::execute_worker_entrypoint(&cmd.socket_path); + polkadot_node_core_pvf::execute_worker_entrypoint(&cmd.socket_path, None); Ok(()) }, None => { diff --git a/bin/rialto/runtime/Cargo.toml b/bin/rialto/runtime/Cargo.toml index 4f0f64cbc95..2b68e72d6f8 100644 --- a/bin/rialto/runtime/Cargo.toml +++ b/bin/rialto/runtime/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } # Bridge dependencies diff --git a/bin/rialto/runtime/src/lib.rs b/bin/rialto/runtime/src/lib.rs index 1a6f0f1e540..aeb0a3a7c57 100644 --- a/bin/rialto/runtime/src/lib.rs +++ b/bin/rialto/runtime/src/lib.rs @@ -396,11 +396,7 @@ pub type MillauGrandpaInstance = (); impl pallet_bridge_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; type BridgedChain = bp_millau::Millau; - /// This is a pretty unscientific cap. - /// - /// Note that once this is hit the pallet will essentially throttle incoming requests down to - /// one call per block. - type MaxRequests = ConstU32<50>; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; type HeadersToKeep = ConstU32<{ bp_millau::DAYS as u32 }>; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } diff --git a/bin/runtime-common/Cargo.toml b/bin/runtime-common/Cargo.toml index e4a5d7ccce1..3db4ae9abca 100644 --- a/bin/runtime-common/Cargo.toml +++ b/bin/runtime-common/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } hash-db = { version = "0.16.0", default-features = false } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } static_assertions = { version = "1.1", optional = true } # Bridge dependencies diff --git a/bin/runtime-common/src/integrity.rs b/bin/runtime-common/src/integrity.rs index 96716fe851c..5820dd99b91 100644 --- a/bin/runtime-common/src/integrity.rs +++ b/bin/runtime-common/src/integrity.rs @@ -178,9 +178,9 @@ where GI: 'static, { assert!( - R::MaxRequests::get() > 0, - "MaxRequests ({}) must be larger than zero", - R::MaxRequests::get(), + R::HeadersToKeep::get() > 0, + "HeadersToKeep ({}) must be larger than zero", + R::HeadersToKeep::get(), ); } diff --git a/bin/runtime-common/src/lib.rs b/bin/runtime-common/src/lib.rs index ed486c04abc..e8a2d2470fa 100644 --- a/bin/runtime-common/src/lib.rs +++ b/bin/runtime-common/src/lib.rs @@ -30,6 +30,7 @@ pub mod messages_benchmarking; pub mod messages_call_ext; pub mod messages_xcm_extension; pub mod parachains_benchmarking; +pub mod priority_calculator; pub mod refund_relayer_extension; mod messages_generation; diff --git a/bin/runtime-common/src/messages_call_ext.rs b/bin/runtime-common/src/messages_call_ext.rs index 588afad106d..f3665a8d93b 100644 --- a/bin/runtime-common/src/messages_call_ext.rs +++ b/bin/runtime-common/src/messages_call_ext.rs @@ -17,33 +17,96 @@ use crate::messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; -use bp_messages::{LaneId, MessageNonce}; -use frame_support::{dispatch::CallableCallFor, traits::IsSubType, RuntimeDebug}; +use bp_messages::{InboundLaneData, LaneId, MessageNonce}; +use frame_support::{ + dispatch::CallableCallFor, + traits::{Get, IsSubType}, + RuntimeDebug, +}; use pallet_bridge_messages::{Config, Pallet}; use sp_runtime::transaction_validity::TransactionValidity; +use sp_std::ops::RangeInclusive; /// Generic info about a messages delivery/confirmation proof. #[derive(PartialEq, RuntimeDebug)] pub struct BaseMessagesProofInfo { + /// Message lane, used by the call. pub lane_id: LaneId, - pub best_bundled_nonce: MessageNonce, + /// Nonces of messages, included in the call. + /// + /// For delivery transaction, it is nonces of bundled messages. For confirmation + /// transaction, it is nonces that are to be confirmed during the call. + pub bundled_range: RangeInclusive, + /// Nonce of the best message, stored by this chain before the call is dispatched. + /// + /// For delivery transaction, it is the nonce of best delivered message before the call. + /// For confirmation transaction, it is the nonce of best confirmed message before the call. pub best_stored_nonce: MessageNonce, } impl BaseMessagesProofInfo { - fn is_obsolete(&self) -> bool { - self.best_bundled_nonce <= self.best_stored_nonce + /// Returns true if `bundled_range` continues the `0..=best_stored_nonce` range. + fn appends_to_stored_nonce(&self) -> bool { + *self.bundled_range.start() == self.best_stored_nonce + 1 } } +/// Occupation state of the unrewarded relayers vector. +#[derive(PartialEq, RuntimeDebug)] +#[cfg_attr(test, derive(Default))] +pub struct UnrewardedRelayerOccupation { + /// The number of remaining unoccupied entries for new relayers. + pub free_relayer_slots: MessageNonce, + /// The number of messages that we are ready to accept. + pub free_message_slots: MessageNonce, +} + /// Info about a `ReceiveMessagesProof` call which tries to update a single lane. #[derive(PartialEq, RuntimeDebug)] -pub struct ReceiveMessagesProofInfo(pub BaseMessagesProofInfo); +pub struct ReceiveMessagesProofInfo { + /// Base messages proof info + pub base: BaseMessagesProofInfo, + /// State of unrewarded relayers vector. + pub unrewarded_relayers: UnrewardedRelayerOccupation, +} + +impl ReceiveMessagesProofInfo { + /// Returns true if: + /// + /// - either inbound lane is ready to accept bundled messages; + /// + /// - or there are no bundled messages, but the inbound lane is blocked by too many unconfirmed + /// messages and/or unrewarded relayers. + fn is_obsolete(&self) -> bool { + // transactions with zero bundled nonces are not allowed, unless they're message + // delivery transactions, which brings reward confirmations required to unblock + // the lane + if self.base.bundled_range.is_empty() { + let empty_transactions_allowed = + // we allow empty transactions when we can't accept delivery from new relayers + self.unrewarded_relayers.free_relayer_slots == 0 || + // or if we can't accept new messages at all + self.unrewarded_relayers.free_message_slots == 0; + + return !empty_transactions_allowed + } + + // otherwise we require bundled messages to continue stored range + !self.base.appends_to_stored_nonce() + } +} /// Info about a `ReceiveMessagesDeliveryProof` call which tries to update a single lane. #[derive(PartialEq, RuntimeDebug)] pub struct ReceiveMessagesDeliveryProofInfo(pub BaseMessagesProofInfo); +impl ReceiveMessagesDeliveryProofInfo { + /// Returns true if outbound lane is ready to accept confirmations of bundled messages. + fn is_obsolete(&self) -> bool { + self.0.bundled_range.is_empty() || !self.0.appends_to_stored_nonce() + } +} + /// Info about a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call /// which tries to update a single lane. #[derive(PartialEq, RuntimeDebug)] @@ -58,19 +121,34 @@ pub struct CallHelper, I: 'static> { } impl, I: 'static> CallHelper { - /// Check if a call delivered proof/confirmation for at least some of the messages that it - /// contained. - pub fn was_partially_successful(info: &CallInfo) -> bool { + /// Returns true if: + /// + /// - call is `receive_messages_proof` and all messages have been delivered; + /// + /// - call is `receive_messages_delivery_proof` and all messages confirmations have been + /// received. + pub fn was_successful(info: &CallInfo) -> bool { match info { CallInfo::ReceiveMessagesProof(info) => { let inbound_lane_data = - pallet_bridge_messages::InboundLanes::::get(info.0.lane_id); - inbound_lane_data.last_delivered_nonce() > info.0.best_stored_nonce + pallet_bridge_messages::InboundLanes::::get(info.base.lane_id); + if info.base.bundled_range.is_empty() { + let post_occupation = + unrewarded_relayers_occupation::(&inbound_lane_data); + // we don't care about `free_relayer_slots` here - it is checked in + // `is_obsolete` and every relayer has delivered at least one message, + // so if relayer slots are released, then message slots are also + // released + return post_occupation.free_message_slots > + info.unrewarded_relayers.free_message_slots + } + + inbound_lane_data.last_delivered_nonce() == *info.base.bundled_range.end() }, CallInfo::ReceiveMessagesDeliveryProof(info) => { let outbound_lane_data = pallet_bridge_messages::OutboundLanes::::get(info.0.lane_id); - outbound_lane_data.latest_received_nonce > info.0.best_stored_nonce + outbound_lane_data.latest_received_nonce == *info.0.bundled_range.end() }, } } @@ -124,11 +202,16 @@ impl< { let inbound_lane_data = pallet_bridge_messages::InboundLanes::::get(proof.lane); - return Some(ReceiveMessagesProofInfo(BaseMessagesProofInfo { - lane_id: proof.lane, - best_bundled_nonce: proof.nonces_end, - best_stored_nonce: inbound_lane_data.last_delivered_nonce(), - })) + return Some(ReceiveMessagesProofInfo { + base: BaseMessagesProofInfo { + lane_id: proof.lane, + // we want all messages in this range to be new for us. Otherwise transaction + // will be considered obsolete. + bundled_range: proof.nonces_start..=proof.nonces_end, + best_stored_nonce: inbound_lane_data.last_delivered_nonce(), + }, + unrewarded_relayers: unrewarded_relayers_occupation::(&inbound_lane_data), + }) } None @@ -145,7 +228,12 @@ impl< return Some(ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { lane_id: proof.lane, - best_bundled_nonce: relayers_state.last_delivered_nonce, + // there's a time frame between message delivery, message confirmation and reward + // confirmation. Because of that, we can't assume that our state has been confirmed + // to the bridged chain. So we are accepting any proof that brings new + // confirmations. + bundled_range: outbound_lane_data.latest_received_nonce + 1..= + relayers_state.last_delivered_nonce, best_stored_nonce: outbound_lane_data.latest_received_nonce, })) } @@ -168,7 +256,7 @@ impl< fn call_info_for(&self, lane_id: LaneId) -> Option { self.call_info().filter(|info| { let actual_lane_id = match info { - CallInfo::ReceiveMessagesProof(info) => info.0.lane_id, + CallInfo::ReceiveMessagesProof(info) => info.base.lane_id, CallInfo::ReceiveMessagesDeliveryProof(info) => info.0.lane_id, }; actual_lane_id == lane_id @@ -177,7 +265,7 @@ impl< fn check_obsolete_call(&self) -> TransactionValidity { match self.call_info() { - Some(CallInfo::ReceiveMessagesProof(proof_info)) if proof_info.0.is_obsolete() => { + Some(CallInfo::ReceiveMessagesProof(proof_info)) if proof_info.is_obsolete() => { log::trace!( target: pallet_bridge_messages::LOG_TARGET, "Rejecting obsolete messages delivery transaction: {:?}", @@ -187,7 +275,7 @@ impl< return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() }, Some(CallInfo::ReceiveMessagesDeliveryProof(proof_info)) - if proof_info.0.is_obsolete() => + if proof_info.is_obsolete() => { log::trace!( target: pallet_bridge_messages::LOG_TARGET, @@ -204,20 +292,72 @@ impl< } } +/// Returns occupation state of unrewarded relayers vector. +fn unrewarded_relayers_occupation, I: 'static>( + inbound_lane_data: &InboundLaneData, +) -> UnrewardedRelayerOccupation { + UnrewardedRelayerOccupation { + free_relayer_slots: T::MaxUnrewardedRelayerEntriesAtInboundLane::get() + .saturating_sub(inbound_lane_data.relayers.len() as MessageNonce), + free_message_slots: { + let unconfirmed_messages = inbound_lane_data + .last_delivered_nonce() + .saturating_sub(inbound_lane_data.last_confirmed_nonce); + T::MaxUnconfirmedMessagesAtInboundLane::get().saturating_sub(unconfirmed_messages) + }, + } +} + #[cfg(test)] mod tests { + use super::*; use crate::{ messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }, messages_call_ext::MessagesCallSubType, - mock::{TestRuntime, ThisChainRuntimeCall}, + mock::{ + MaxUnconfirmedMessagesAtInboundLane, MaxUnrewardedRelayerEntriesAtInboundLane, + TestRuntime, ThisChainRuntimeCall, + }, }; - use bp_messages::UnrewardedRelayersState; + use bp_messages::{DeliveredMessages, UnrewardedRelayer, UnrewardedRelayersState}; + use sp_std::ops::RangeInclusive; + + fn fill_unrewarded_relayers() { + let mut inbound_lane_state = + pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); + for n in 0..MaxUnrewardedRelayerEntriesAtInboundLane::get() { + inbound_lane_state.relayers.push_back(UnrewardedRelayer { + relayer: Default::default(), + messages: DeliveredMessages { begin: n + 1, end: n + 1 }, + }); + } + pallet_bridge_messages::InboundLanes::::insert( + LaneId([0, 0, 0, 0]), + inbound_lane_state, + ); + } + + fn fill_unrewarded_messages() { + let mut inbound_lane_state = + pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); + inbound_lane_state.relayers.push_back(UnrewardedRelayer { + relayer: Default::default(), + messages: DeliveredMessages { + begin: 1, + end: MaxUnconfirmedMessagesAtInboundLane::get(), + }, + }); + pallet_bridge_messages::InboundLanes::::insert( + LaneId([0, 0, 0, 0]), + inbound_lane_state, + ); + } fn deliver_message_10() { pallet_bridge_messages::InboundLanes::::insert( - bp_messages::LaneId([0, 0, 0, 0]), + LaneId([0, 0, 0, 0]), bp_messages::InboundLaneData { relayers: Default::default(), last_confirmed_nonce: 10 }, ); } @@ -229,12 +369,13 @@ mod tests { ThisChainRuntimeCall::BridgeMessages( pallet_bridge_messages::Call::::receive_messages_proof { relayer_id_at_bridged_chain: 42, - messages_count: (nonces_end - nonces_start + 1) as u32, + messages_count: nonces_end.checked_sub(nonces_start).map(|x| x + 1).unwrap_or(0) + as u32, dispatch_weight: frame_support::weights::Weight::zero(), proof: FromBridgedChainMessagesProof { bridged_header_hash: Default::default(), storage_proof: vec![], - lane: bp_messages::LaneId([0, 0, 0, 0]), + lane: LaneId([0, 0, 0, 0]), nonces_start, nonces_end, }, @@ -247,8 +388,8 @@ mod tests { #[test] fn extension_rejects_obsolete_messages() { sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to deliver message#5 => tx - // is rejected + // when current best delivered is message#10 and we're trying to deliver messages 8..=9 + // => tx is rejected deliver_message_10(); assert!(!validate_message_delivery(8, 9)); }); @@ -257,26 +398,77 @@ mod tests { #[test] fn extension_rejects_same_message() { sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to import message#10 => tx - // is rejected + // when current best delivered is message#10 and we're trying to import messages 10..=10 + // => tx is rejected deliver_message_10(); assert!(!validate_message_delivery(8, 10)); }); } #[test] - fn extension_accepts_new_message() { + fn extension_rejects_call_with_some_obsolete_messages() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best delivered is message#10 and we're trying to deliver messages + // 10..=15 => tx is rejected + deliver_message_10(); + assert!(!validate_message_delivery(10, 15)); + }); + } + + #[test] + fn extension_rejects_call_with_future_messages() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best delivered is message#10 and we're trying to deliver messages + // 13..=15 => tx is rejected + deliver_message_10(); + assert!(!validate_message_delivery(13, 15)); + }); + } + + #[test] + fn extension_rejects_empty_delivery_with_rewards_confirmations_if_there_are_free_relayer_and_message_slots( + ) { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + deliver_message_10(); + assert!(!validate_message_delivery(10, 9)); + }); + } + + #[test] + fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_relayer_slots( + ) { sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to deliver message#15 => - // tx is accepted deliver_message_10(); - assert!(validate_message_delivery(10, 15)); + fill_unrewarded_relayers(); + assert!(validate_message_delivery(10, 9)); + }); + } + + #[test] + fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_message_slots( + ) { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + fill_unrewarded_messages(); + assert!(validate_message_delivery( + MaxUnconfirmedMessagesAtInboundLane::get(), + MaxUnconfirmedMessagesAtInboundLane::get() - 1 + )); + }); + } + + #[test] + fn extension_accepts_new_messages() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best delivered is message#10 and we're trying to deliver message 11..=15 + // => tx is accepted + deliver_message_10(); + assert!(validate_message_delivery(11, 15)); }); } fn confirm_message_10() { pallet_bridge_messages::OutboundLanes::::insert( - bp_messages::LaneId([0, 0, 0, 0]), + LaneId([0, 0, 0, 0]), bp_messages::OutboundLaneData { oldest_unpruned_nonce: 0, latest_received_nonce: 10, @@ -291,7 +483,7 @@ mod tests { proof: FromBridgedChainMessagesDeliveryProof { bridged_header_hash: Default::default(), storage_proof: Vec::new(), - lane: bp_messages::LaneId([0, 0, 0, 0]), + lane: LaneId([0, 0, 0, 0]), }, relayers_state: UnrewardedRelayersState { last_delivered_nonce, @@ -323,6 +515,15 @@ mod tests { }); } + #[test] + fn extension_rejects_empty_confirmation_even_if_there_are_no_free_unrewarded_entries() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + confirm_message_10(); + fill_unrewarded_relayers(); + assert!(!validate_message_confirmation(10)); + }); + } + #[test] fn extension_accepts_new_confirmation() { sp_io::TestExternalities::new(Default::default()).execute_with(|| { @@ -332,4 +533,102 @@ mod tests { assert!(validate_message_confirmation(15)); }); } + + fn was_message_delivery_successful( + bundled_range: RangeInclusive, + is_empty: bool, + ) -> bool { + CallHelper::::was_successful(&CallInfo::ReceiveMessagesProof( + ReceiveMessagesProofInfo { + base: BaseMessagesProofInfo { + lane_id: LaneId([0, 0, 0, 0]), + bundled_range, + best_stored_nonce: 0, // doesn't matter for `was_successful` + }, + unrewarded_relayers: UnrewardedRelayerOccupation { + free_relayer_slots: 0, // doesn't matter for `was_successful` + free_message_slots: if is_empty { + 0 + } else { + MaxUnconfirmedMessagesAtInboundLane::get() + }, + }, + }, + )) + } + + #[test] + #[allow(clippy::reversed_empty_ranges)] + fn was_successful_returns_false_for_failed_reward_confirmation_transaction() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + fill_unrewarded_messages(); + assert!(!was_message_delivery_successful(10..=9, true)); + }); + } + + #[test] + #[allow(clippy::reversed_empty_ranges)] + fn was_successful_returns_true_for_successful_reward_confirmation_transaction() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + assert!(was_message_delivery_successful(10..=9, true)); + }); + } + + #[test] + fn was_successful_returns_false_for_failed_delivery() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + deliver_message_10(); + assert!(!was_message_delivery_successful(10..=12, false)); + }); + } + + #[test] + fn was_successful_returns_false_for_partially_successful_delivery() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + deliver_message_10(); + assert!(!was_message_delivery_successful(9..=12, false)); + }); + } + + #[test] + fn was_successful_returns_true_for_successful_delivery() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + deliver_message_10(); + assert!(was_message_delivery_successful(9..=10, false)); + }); + } + + fn was_message_confirmation_successful(bundled_range: RangeInclusive) -> bool { + CallHelper::::was_successful(&CallInfo::ReceiveMessagesDeliveryProof( + ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { + lane_id: LaneId([0, 0, 0, 0]), + bundled_range, + best_stored_nonce: 0, // doesn't matter for `was_successful` + }), + )) + } + + #[test] + fn was_successful_returns_false_for_failed_confirmation() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + confirm_message_10(); + assert!(!was_message_confirmation_successful(10..=12)); + }); + } + + #[test] + fn was_successful_returns_false_for_partially_successful_confirmation() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + confirm_message_10(); + assert!(!was_message_confirmation_successful(9..=12)); + }); + } + + #[test] + fn was_successful_returns_true_for_successful_confirmation() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + confirm_message_10(); + assert!(was_message_confirmation_successful(9..=10)); + }); + } } diff --git a/bin/runtime-common/src/messages_xcm_extension.rs b/bin/runtime-common/src/messages_xcm_extension.rs index 3d802d12fad..4ccdd7a4b4d 100644 --- a/bin/runtime-common/src/messages_xcm_extension.rs +++ b/bin/runtime-common/src/messages_xcm_extension.rs @@ -42,7 +42,7 @@ pub type XcmAsPlainPayload = sp_std::prelude::Vec; pub enum XcmBlobMessageDispatchResult { InvalidPayload, Dispatched, - NotDispatched(#[codec(skip)] &'static str), + NotDispatched(#[codec(skip)] Option), } /// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages @@ -106,24 +106,12 @@ impl< XcmBlobMessageDispatchResult::Dispatched }, Err(e) => { - let e = match e { - DispatchBlobError::Unbridgable => "DispatchBlobError::Unbridgable", - DispatchBlobError::InvalidEncoding => "DispatchBlobError::InvalidEncoding", - DispatchBlobError::UnsupportedLocationVersion => - "DispatchBlobError::UnsupportedLocationVersion", - DispatchBlobError::UnsupportedXcmVersion => - "DispatchBlobError::UnsupportedXcmVersion", - DispatchBlobError::RoutingError => "DispatchBlobError::RoutingError", - DispatchBlobError::NonUniversalDestination => - "DispatchBlobError::NonUniversalDestination", - DispatchBlobError::WrongGlobal => "DispatchBlobError::WrongGlobal", - }; log::error!( target: crate::LOG_TARGET_BRIDGE_DISPATCH, "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?} - message_nonce: {:?}", e, message.key.nonce ); - XcmBlobMessageDispatchResult::NotDispatched(e) + XcmBlobMessageDispatchResult::NotDispatched(Some(e)) }, }; MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result } diff --git a/bin/runtime-common/src/mock.rs b/bin/runtime-common/src/mock.rs index ac6cf122b0f..036813f6fd5 100644 --- a/bin/runtime-common/src/mock.rs +++ b/bin/runtime-common/src/mock.rs @@ -33,7 +33,7 @@ use crate::messages::{ }; use bp_header_chain::{ChainWithGrandpa, HeaderChain}; -use bp_messages::{target_chain::ForbidInboundMessages, LaneId}; +use bp_messages::{target_chain::ForbidInboundMessages, LaneId, MessageNonce}; use bp_parachains::SingleParaStoredHeaderDataBuilder; use bp_runtime::{Chain, ChainId, Parachain, UnderlyingChainProvider}; use codec::{Decode, Encode}; @@ -126,6 +126,8 @@ parameter_types! { pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value(); + pub const MaxUnrewardedRelayerEntriesAtInboundLane: MessageNonce = 16; + pub const MaxUnconfirmedMessagesAtInboundLane: MessageNonce = 1_000; } impl frame_system::Config for TestRuntime { @@ -196,7 +198,7 @@ impl pallet_transaction_payment::Config for TestRuntime { impl pallet_bridge_grandpa::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type BridgedChain = BridgedUnderlyingChain; - type MaxRequests = ConstU32<50>; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; type HeadersToKeep = ConstU32<8>; type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } @@ -216,8 +218,8 @@ impl pallet_bridge_messages::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; type ActiveOutboundLanes = ActiveOutboundLanes; - type MaxUnrewardedRelayerEntriesAtInboundLane = ConstU64<16>; - type MaxUnconfirmedMessagesAtInboundLane = ConstU64<16>; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; type MaximalOutboundPayloadSize = FromThisChainMaximalOutboundPayloadSize; type OutboundPayload = FromThisChainMessagePayload; diff --git a/bin/runtime-common/src/priority_calculator.rs b/bin/runtime-common/src/priority_calculator.rs new file mode 100644 index 00000000000..590de05fb1c --- /dev/null +++ b/bin/runtime-common/src/priority_calculator.rs @@ -0,0 +1,201 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Bridge transaction priority calculator. +//! +//! We want to prioritize message delivery transactions with more messages over +//! transactions with less messages. That's because we reject delivery transactions +//! if it contains already delivered message. And if some transaction delivers +//! single message with nonce `N`, then the transaction with nonces `N..=N+100` will +//! be rejected. This can lower bridge throughput down to one message per block. + +use bp_messages::MessageNonce; +use frame_support::traits::Get; +use sp_runtime::transaction_validity::TransactionPriority; + +// reexport everything from `integrity_tests` module +pub use integrity_tests::*; + +/// Compute priority boost for message delivery transaction that delivers +/// given number of messages. +pub fn compute_priority_boost( + messages: MessageNonce, +) -> TransactionPriority +where + PriorityBoostPerMessage: Get, +{ + // we don't want any boost for transaction with single message => minus one + PriorityBoostPerMessage::get().saturating_mul(messages - 1) +} + +#[cfg(not(feature = "integrity-test"))] +mod integrity_tests {} + +#[cfg(feature = "integrity-test")] +mod integrity_tests { + use super::compute_priority_boost; + + use bp_messages::MessageNonce; + use bp_runtime::PreComputedSize; + use frame_support::{ + dispatch::{DispatchClass, DispatchInfo, Dispatchable, Pays, PostDispatchInfo}, + traits::Get, + }; + use pallet_bridge_messages::WeightInfoExt; + use pallet_transaction_payment::OnChargeTransaction; + use sp_runtime::{ + traits::{UniqueSaturatedInto, Zero}, + transaction_validity::TransactionPriority, + FixedPointOperand, SaturatedConversion, Saturating, + }; + + type BalanceOf = + <::OnChargeTransaction as OnChargeTransaction< + T, + >>::Balance; + + /// Ensures that the value of `PriorityBoostPerMessage` matches the value of + /// `tip_boost_per_message`. + /// + /// We want two transactions, `TX1` with `N` messages and `TX2` with `N+1` messages, have almost + /// the same priority if we'll add `tip_boost_per_message` tip to the `TX1`. We want to be sure + /// that if we add plain `PriorityBoostPerMessage` priority to `TX1`, the priority will be close + /// to `TX2` as well. + pub fn ensure_priority_boost_is_sane( + tip_boost_per_message: BalanceOf, + ) where + Runtime: + pallet_transaction_payment::Config + pallet_bridge_messages::Config, + MessagesInstance: 'static, + PriorityBoostPerMessage: Get, + Runtime::RuntimeCall: Dispatchable, + BalanceOf: Send + Sync + FixedPointOperand, + { + let priority_boost_per_message = PriorityBoostPerMessage::get(); + let maximal_messages_in_delivery_transaction = + Runtime::MaxUnconfirmedMessagesAtInboundLane::get(); + for messages in 1..=maximal_messages_in_delivery_transaction { + let base_priority = estimate_message_delivery_transaction_priority::< + Runtime, + MessagesInstance, + >(messages, Zero::zero()); + let priority_boost = compute_priority_boost::(messages); + let priority_with_boost = base_priority + priority_boost; + + let tip = tip_boost_per_message.saturating_mul((messages - 1).unique_saturated_into()); + let priority_with_tip = + estimate_message_delivery_transaction_priority::(1, tip); + + const ERROR_MARGIN: TransactionPriority = 5; // 5% + if priority_with_boost.abs_diff(priority_with_tip).saturating_mul(100) / + priority_with_tip > + ERROR_MARGIN + { + panic!( + "The PriorityBoostPerMessage value ({}) must be fixed to: {}", + priority_boost_per_message, + compute_priority_boost_per_message::( + tip_boost_per_message + ), + ); + } + } + } + + /// Compute priority boost that we give to message delivery transaction for additional message. + #[cfg(feature = "integrity-test")] + fn compute_priority_boost_per_message( + tip_boost_per_message: BalanceOf, + ) -> TransactionPriority + where + Runtime: + pallet_transaction_payment::Config + pallet_bridge_messages::Config, + MessagesInstance: 'static, + Runtime::RuntimeCall: Dispatchable, + BalanceOf: Send + Sync + FixedPointOperand, + { + // esimate priority of transaction that delivers one message and has large tip + let maximal_messages_in_delivery_transaction = + Runtime::MaxUnconfirmedMessagesAtInboundLane::get(); + let small_with_tip_priority = + estimate_message_delivery_transaction_priority::( + 1, + tip_boost_per_message + .saturating_mul(maximal_messages_in_delivery_transaction.saturated_into()), + ); + // estimate priority of transaction that delivers maximal number of messages, but has no tip + let large_without_tip_priority = estimate_message_delivery_transaction_priority::< + Runtime, + MessagesInstance, + >(maximal_messages_in_delivery_transaction, Zero::zero()); + + small_with_tip_priority + .saturating_sub(large_without_tip_priority) + .saturating_div(maximal_messages_in_delivery_transaction - 1) + } + + /// Estimate message delivery transaction priority. + #[cfg(feature = "integrity-test")] + fn estimate_message_delivery_transaction_priority( + messages: MessageNonce, + tip: BalanceOf, + ) -> TransactionPriority + where + Runtime: + pallet_transaction_payment::Config + pallet_bridge_messages::Config, + MessagesInstance: 'static, + Runtime::RuntimeCall: Dispatchable, + BalanceOf: Send + Sync + FixedPointOperand, + { + // just an estimation of extra transaction bytes that are added to every transaction + // (including signature, signed extensions extra and etc + in our case it includes + // all call arguments extept the proof itself) + let base_tx_size = 512; + // let's say we are relaying similar small messages and for every message we add more trie + // nodes to the proof (x0.5 because we expect some nodes to be reused) + let estimated_message_size = 512; + // let's say all our messages have the same dispatch weight + let estimated_message_dispatch_weight = + Runtime::WeightInfo::message_dispatch_weight(estimated_message_size); + // messages proof argument size is (for every message) messages size + some additional + // trie nodes. Some of them are reused by different messages, so let's take 2/3 of default + // "overhead" constant + let messages_proof_size = Runtime::WeightInfo::expected_extra_storage_proof_size() + .saturating_mul(2) + .saturating_div(3) + .saturating_add(estimated_message_size) + .saturating_mul(messages as _); + + // finally we are able to estimate transaction size and weight + let transaction_size = base_tx_size.saturating_add(messages_proof_size); + let transaction_weight = Runtime::WeightInfo::receive_messages_proof_weight( + &PreComputedSize(transaction_size as _), + messages as _, + estimated_message_dispatch_weight.saturating_mul(messages), + ); + + pallet_transaction_payment::ChargeTransactionPayment::::get_priority( + &DispatchInfo { + weight: transaction_weight, + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }, + transaction_size as _, + tip, + Zero::zero(), + ) + } +} diff --git a/bin/runtime-common/src/refund_relayer_extension.rs b/bin/runtime-common/src/refund_relayer_extension.rs index 7be97f19ad2..925fea2a743 100644 --- a/bin/runtime-common/src/refund_relayer_extension.rs +++ b/bin/runtime-common/src/refund_relayer_extension.rs @@ -24,7 +24,7 @@ use crate::messages_call_ext::{ }; use bp_messages::LaneId; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; -use bp_runtime::StaticStrProvider; +use bp_runtime::{RangeInclusiveExt, StaticStrProvider}; use codec::{Decode, Encode}; use frame_support::{ dispatch::{CallableCallFor, DispatchInfo, Dispatchable, PostDispatchInfo}, @@ -46,7 +46,9 @@ use pallet_utility::{Call as UtilityCall, Config as UtilityConfig, Pallet as Uti use scale_info::TypeInfo; use sp_runtime::{ traits::{DispatchInfoOf, Get, PostDispatchInfoOf, SignedExtension, Zero}, - transaction_validity::{TransactionValidity, TransactionValidityError, ValidTransaction}, + transaction_validity::{ + TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransactionBuilder, + }, DispatchResult, FixedPointOperand, }; use sp_std::{marker::PhantomData, vec, vec::Vec}; @@ -58,7 +60,7 @@ type CallOf = ::RuntimeCall; /// Trait identifying a bridged parachain. A relayer might be refunded for delivering messages /// coming from this parachain. -trait RefundableParachainId { +pub trait RefundableParachainId { /// The instance of the bridge parachains pallet. type Instance; /// The parachain Id. @@ -78,7 +80,7 @@ where /// Trait identifying a bridged messages lane. A relayer might be refunded for delivering messages /// coming from this lane. -trait RefundableMessagesLaneId { +pub trait RefundableMessagesLaneId { /// The instance of the bridge messages pallet. type Instance; /// The messages lane id. @@ -201,36 +203,75 @@ impl CallInfo { RuntimeDebugNoBound, TypeInfo, )] -#[scale_info(skip_type_params(Runtime, Para, Msgs, Refund, Id))] -pub struct RefundBridgedParachainMessages( - PhantomData<(Runtime, Para, Msgs, Refund, Id)>, +#[scale_info(skip_type_params(Runtime, Para, Msgs, Refund, Priority, Id))] +pub struct RefundBridgedParachainMessages( + PhantomData<(Runtime, Para, Msgs, Refund, Priority, Id)>, ); -impl - RefundBridgedParachainMessages +impl + RefundBridgedParachainMessages where - Runtime: UtilityConfig>, - CallOf: IsSubType, Runtime>>, + Self: 'static + Send + Sync, + Runtime: UtilityConfig> + + BoundedBridgeGrandpaConfig + + ParachainsConfig + + MessagesConfig, + Para: RefundableParachainId, + Msgs: RefundableMessagesLaneId, + CallOf: Dispatchable + + IsSubType, Runtime>> + + GrandpaCallSubType + + ParachainsCallSubType + + MessagesCallSubType, { - fn expand_call<'a>(&self, call: &'a CallOf) -> Option>> { - let calls = match call.is_sub_type() { - Some(UtilityCall::::batch_all { ref calls }) => { - if calls.len() > 3 { - return None - } - - calls.iter().collect() - }, - Some(_) => return None, + fn expand_call<'a>(&self, call: &'a CallOf) -> Vec<&'a CallOf> { + match call.is_sub_type() { + Some(UtilityCall::::batch_all { ref calls }) if calls.len() <= 3 => + calls.iter().collect(), + Some(_) => vec![], None => vec![call], - }; + } + } + + fn parse_and_check_for_obsolete_call( + &self, + call: &CallOf, + ) -> Result, TransactionValidityError> { + let calls = self.expand_call(call); + let total_calls = calls.len(); + let mut calls = calls.into_iter().map(Self::check_obsolete_call).rev(); + + let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info_for(Msgs::Id::get())); + let para_finality_call = calls + .next() + .transpose()? + .and_then(|c| c.submit_parachain_heads_info_for(Para::Id::get())); + let relay_finality_call = + calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); + + Ok(match (total_calls, relay_finality_call, para_finality_call, msgs_call) { + (3, Some(relay_finality_call), Some(para_finality_call), Some(msgs_call)) => Some( + CallInfo::AllFinalityAndMsgs(relay_finality_call, para_finality_call, msgs_call), + ), + (2, None, Some(para_finality_call), Some(msgs_call)) => + Some(CallInfo::ParachainFinalityAndMsgs(para_finality_call, msgs_call)), + (1, None, None, Some(msgs_call)) => Some(CallInfo::Msgs(msgs_call)), + _ => None, + }) + } - Some(calls) + fn check_obsolete_call( + call: &CallOf, + ) -> Result<&CallOf, TransactionValidityError> { + call.check_obsolete_submit_finality_proof()?; + call.check_obsolete_submit_parachain_heads()?; + call.check_obsolete_call()?; + Ok(call) } } -impl SignedExtension - for RefundBridgedParachainMessages +impl SignedExtension + for RefundBridgedParachainMessages where Self: 'static + Send + Sync, Runtime: UtilityConfig> @@ -241,6 +282,7 @@ where Para: RefundableParachainId, Msgs: RefundableMessagesLaneId, Refund: RefundCalculator, + Priority: Get, Id: StaticStrProvider, CallOf: Dispatchable + IsSubType, Runtime>> @@ -265,46 +307,46 @@ where _info: &DispatchInfoOf, _len: usize, ) -> TransactionValidity { - if let Some(calls) = self.expand_call(call) { - for nested_call in calls { - nested_call.check_obsolete_submit_finality_proof()?; - nested_call.check_obsolete_submit_parachain_heads()?; - nested_call.check_obsolete_call()?; + // this is the only relevant line of code for the `pre_dispatch` + // + // we're not calling `validato` from `pre_dispatch` directly because of performance + // reasons, so if you're adding some code that may fail here, please check if it needs + // to be added to the `pre_dispatch` as well + let parsed_call = self.parse_and_check_for_obsolete_call(call)?; + + // the following code just plays with transaction priority and never returns an error + let mut valid_transaction = ValidTransactionBuilder::default(); + if let Some(parsed_call) = parsed_call { + // we give delivery transactions some boost, that depends on number of messages inside + let messages_call_info = parsed_call.messages_call_info(); + if let MessagesCallInfo::ReceiveMessagesProof(info) = messages_call_info { + // compute total number of messages in transaction + let bundled_messages = info.base.bundled_range.checked_len().unwrap_or(0); + + // a quick check to avoid invalid high-priority transactions + if bundled_messages <= Runtime::MaxUnconfirmedMessagesAtInboundLane::get() { + let priority_boost = crate::priority_calculator::compute_priority_boost::< + Priority, + >(bundled_messages); + valid_transaction = valid_transaction.priority(priority_boost); + } } } - Ok(ValidTransaction::default()) + valid_transaction.build() } fn pre_dispatch( self, who: &Self::AccountId, call: &Self::Call, - info: &DispatchInfoOf, - len: usize, + _info: &DispatchInfoOf, + _len: usize, ) -> Result { - // reject batch transactions with obsolete headers - self.validate(who, call, info, len).map(drop)?; - - // Try to check if the tx matches one of types we support. - let parse_call = || { - let mut calls = self.expand_call(call)?.into_iter(); - match calls.len() { - 3 => Some(CallInfo::AllFinalityAndMsgs( - calls.next()?.submit_finality_proof_info()?, - calls.next()?.submit_parachain_heads_info_for(Para::Id::get())?, - calls.next()?.call_info_for(Msgs::Id::get())?, - )), - 2 => Some(CallInfo::ParachainFinalityAndMsgs( - calls.next()?.submit_parachain_heads_info_for(Para::Id::get())?, - calls.next()?.call_info_for(Msgs::Id::get())?, - )), - 1 => Some(CallInfo::Msgs(calls.next()?.call_info_for(Msgs::Id::get())?)), - _ => None, - } - }; + // this is a relevant piece of `validate` that we need here (in `pre_dispatch`) + let parsed_call = self.parse_and_check_for_obsolete_call(call)?; - Ok(parse_call().map(|call_info| { + Ok(parsed_call.map(|call_info| { log::trace!( target: "runtime::bridge", "{} from parachain {} via {:?} parsed bridge transaction in pre-dispatch: {:?}", @@ -344,6 +386,16 @@ where finality_proof_info.block_number, ) { // we only refund relayer if all calls have updated chain state + log::trace!( + target: "runtime::bridge", + "{} from parachain {} via {:?}: failed to refund relayer {:?}, because \ + relay chain finality proof has not been accepted", + Self::IDENTIFIER, + Para::Id::get(), + Msgs::Id::get(), + relayer, + ); + return Ok(()) } @@ -366,15 +418,34 @@ where para_proof_info, ) { // we only refund relayer if all calls have updated chain state + log::trace!( + target: "runtime::bridge", + "{} from parachain {} via {:?}: failed to refund relayer {:?}, because \ + parachain finality proof has not been accepted", + Self::IDENTIFIER, + Para::Id::get(), + Msgs::Id::get(), + relayer, + ); + return Ok(()) } } - // Check if the `ReceiveMessagesProof` call delivered at least some of the messages that + // Check if the `ReceiveMessagesProof` call delivered all the messages that // it contained. If this happens, we consider the transaction "helpful" and refund it. let msgs_call_info = call_info.messages_call_info(); - if !MessagesCallHelper::::was_partially_successful(msgs_call_info) - { + if !MessagesCallHelper::::was_successful(msgs_call_info) { + log::trace!( + target: "runtime::bridge", + "{} from parachain {} via {:?}: failed to refund relayer {:?}, because \ + some of messages have not been accepted", + Self::IDENTIFIER, + Para::Id::get(), + Msgs::Id::get(), + relayer, + ); + return Ok(()) } @@ -432,6 +503,7 @@ mod tests { }, messages_call_ext::{ BaseMessagesProofInfo, ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, + UnrewardedRelayerOccupation, }, mock::*, }; @@ -445,14 +517,24 @@ mod tests { use pallet_bridge_messages::Call as MessagesCall; use pallet_bridge_parachains::{Call as ParachainsCall, RelayBlockHash}; use sp_runtime::{ - traits::Header as HeaderT, transaction_validity::InvalidTransaction, DispatchError, + traits::{ConstU64, Header as HeaderT}, + transaction_validity::{InvalidTransaction, ValidTransaction}, + DispatchError, }; parameter_types! { TestParachain: u32 = 1000; pub TestLaneId: LaneId = TEST_LANE_ID; - pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new(TEST_LANE_ID, TEST_BRIDGED_CHAIN_ID, RewardsAccountOwner::ThisChain); - pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new(TEST_LANE_ID, TEST_BRIDGED_CHAIN_ID, RewardsAccountOwner::BridgedChain); + pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( + TEST_LANE_ID, + TEST_BRIDGED_CHAIN_ID, + RewardsAccountOwner::ThisChain, + ); + pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( + TEST_LANE_ID, + TEST_BRIDGED_CHAIN_ID, + RewardsAccountOwner::BridgedChain, + ); } bp_runtime::generate_static_str_provider!(TestExtension); @@ -461,6 +543,7 @@ mod tests { RefundableParachain<(), TestParachain>, RefundableMessagesLane<(), TestLaneId>, ActualFeeRefund, + ConstU64<1>, StrTestExtension, >; @@ -538,7 +621,11 @@ mod tests { bridged_header_hash: Default::default(), storage_proof: vec![], lane: TestLaneId::get(), - nonces_start: best_message, + nonces_start: pallet_bridge_messages::InboundLanes::::get( + TEST_LANE_ID, + ) + .last_delivered_nonce() + + 1, nonces_end: best_message, }, messages_count: 1, @@ -626,13 +713,17 @@ mod tests { para_id: ParaId(TestParachain::get()), para_head_hash: [1u8; 32].into(), }, - MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo( - BaseMessagesProofInfo { + MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { + base: BaseMessagesProofInfo { lane_id: TEST_LANE_ID, - best_bundled_nonce: 200, + bundled_range: 101..=200, best_stored_nonce: 100, }, - )), + unrewarded_relayers: UnrewardedRelayerOccupation { + free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(), + free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(), + }, + }), ), } } @@ -654,7 +745,7 @@ mod tests { MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( BaseMessagesProofInfo { lane_id: TEST_LANE_ID, - best_bundled_nonce: 200, + bundled_range: 101..=200, best_stored_nonce: 100, }, )), @@ -671,13 +762,17 @@ mod tests { para_id: ParaId(TestParachain::get()), para_head_hash: [1u8; 32].into(), }, - MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo( - BaseMessagesProofInfo { + MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { + base: BaseMessagesProofInfo { lane_id: TEST_LANE_ID, - best_bundled_nonce: 200, + bundled_range: 101..=200, best_stored_nonce: 100, }, - )), + unrewarded_relayers: UnrewardedRelayerOccupation { + free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(), + free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(), + }, + }), ), } } @@ -694,7 +789,7 @@ mod tests { MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( BaseMessagesProofInfo { lane_id: TEST_LANE_ID, - best_bundled_nonce: 200, + bundled_range: 101..=200, best_stored_nonce: 100, }, )), @@ -706,11 +801,17 @@ mod tests { PreDispatchData { relayer: relayer_account_at_this_chain(), call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesProof( - ReceiveMessagesProofInfo(BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - best_bundled_nonce: 200, - best_stored_nonce: 100, - }), + ReceiveMessagesProofInfo { + base: BaseMessagesProofInfo { + lane_id: TEST_LANE_ID, + bundled_range: 101..=200, + best_stored_nonce: 100, + }, + unrewarded_relayers: UnrewardedRelayerOccupation { + free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(), + free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(), + }, + }, )), } } @@ -721,7 +822,7 @@ mod tests { call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesDeliveryProof( ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { lane_id: TEST_LANE_ID, - best_bundled_nonce: 200, + bundled_range: 101..=200, best_stored_nonce: 100, }), )), @@ -782,32 +883,98 @@ mod tests { ) } + #[test] + fn validate_boosts_priority_of_message_delivery_transactons() { + run_test(|| { + initialize_environment(100, 100, Default::default(), 100); + + let priority_of_100_messages_delivery = + run_validate(message_delivery_call(200)).unwrap().priority; + let priority_of_200_messages_delivery = + run_validate(message_delivery_call(300)).unwrap().priority; + assert!( + priority_of_200_messages_delivery > priority_of_100_messages_delivery, + "Invalid priorities: {} for 200 messages vs {} for 100 messages", + priority_of_200_messages_delivery, + priority_of_100_messages_delivery, + ); + + let priority_of_100_messages_confirmation = + run_validate(message_confirmation_call(200)).unwrap().priority; + let priority_of_200_messages_confirmation = + run_validate(message_confirmation_call(300)).unwrap().priority; + assert_eq!( + priority_of_100_messages_confirmation, + priority_of_200_messages_confirmation + ); + }); + } + + #[test] + fn validate_does_not_boost_priority_of_message_delivery_transactons_with_too_many_messages() { + run_test(|| { + initialize_environment(100, 100, Default::default(), 100); + + let priority_of_max_messages_delivery = run_validate(message_delivery_call( + 100 + MaxUnconfirmedMessagesAtInboundLane::get(), + )) + .unwrap() + .priority; + let priority_of_more_than_max_messages_delivery = run_validate(message_delivery_call( + 100 + MaxUnconfirmedMessagesAtInboundLane::get() + 1, + )) + .unwrap() + .priority; + + assert!( + priority_of_max_messages_delivery > priority_of_more_than_max_messages_delivery, + "Invalid priorities: {} for MAX messages vs {} for MAX+1 messages", + priority_of_max_messages_delivery, + priority_of_more_than_max_messages_delivery, + ); + }); + } + #[test] fn validate_allows_non_obsolete_transactions() { run_test(|| { initialize_environment(100, 100, Default::default(), 100); - assert_eq!(run_validate(message_delivery_call(200)), Ok(ValidTransaction::default()),); + fn run_validate_ignore_priority(call: RuntimeCall) -> TransactionValidity { + run_validate(call).map(|mut tx| { + tx.priority = 0; + tx + }) + } + assert_eq!( - run_validate(message_confirmation_call(200)), + run_validate_ignore_priority(message_delivery_call(200)), + Ok(ValidTransaction::default()), + ); + assert_eq!( + run_validate_ignore_priority(message_confirmation_call(200)), Ok(ValidTransaction::default()), ); assert_eq!( - run_validate(parachain_finality_and_delivery_batch_call(200, 200)), + run_validate_ignore_priority(parachain_finality_and_delivery_batch_call(200, 200)), Ok(ValidTransaction::default()), ); assert_eq!( - run_validate(parachain_finality_and_confirmation_batch_call(200, 200)), + run_validate_ignore_priority(parachain_finality_and_confirmation_batch_call( + 200, 200 + )), Ok(ValidTransaction::default()), ); assert_eq!( - run_validate(all_finality_and_delivery_batch_call(200, 200, 200)), + run_validate_ignore_priority(all_finality_and_delivery_batch_call(200, 200, 200)), Ok(ValidTransaction::default()), ); assert_eq!( - run_validate(all_finality_and_confirmation_batch_call(200, 200, 200)), + run_validate_ignore_priority(all_finality_and_confirmation_batch_call( + 200, 200, 200 + )), Ok(ValidTransaction::default()), ); }); @@ -1032,6 +1199,30 @@ mod tests { }); } + #[test] + fn post_dispatch_ignores_transaction_that_has_not_delivered_all_messages() { + run_test(|| { + initialize_environment(200, 200, Default::default(), 150); + + assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); + assert_storage_noop!(run_post_dispatch( + Some(parachain_finality_pre_dispatch_data()), + Ok(()) + )); + assert_storage_noop!(run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(()))); + + assert_storage_noop!(run_post_dispatch( + Some(all_finality_confirmation_pre_dispatch_data()), + Ok(()) + )); + assert_storage_noop!(run_post_dispatch( + Some(parachain_finality_confirmation_pre_dispatch_data()), + Ok(()) + )); + assert_storage_noop!(run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(()))); + }); + } + #[test] fn post_dispatch_refunds_relayer_in_all_finality_batch_with_extra_weight() { run_test(|| { diff --git a/deployments/bridges/rialto-millau/dashboard/prometheus/targets.yml b/deployments/bridges/rialto-millau/dashboard/prometheus/targets.yml index 16b798b5a25..75c3e0c2aa9 100644 --- a/deployments/bridges/rialto-millau/dashboard/prometheus/targets.yml +++ b/deployments/bridges/rialto-millau/dashboard/prometheus/targets.yml @@ -1,4 +1,2 @@ - targets: - relay-millau-rialto:9616 - - relay-messages-millau-to-rialto-lane-00000001:9616 - - relay-messages-rialto-to-millau-lane-00000001:9616 diff --git a/deployments/bridges/rococo-wococo/dashboard/grafana/bridges-alerts.json b/deployments/bridges/rococo-wococo/dashboard/grafana/bridges-alerts.json new file mode 100644 index 00000000000..6432f79deaf --- /dev/null +++ b/deployments/bridges/rococo-wococo/dashboard/grafana/bridges-alerts.json @@ -0,0 +1,1994 @@ +{ + "Bridges": [ + { + "name": "Bridges", + "interval": "1m", + "rules": [ + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "tkgc6_bnk", + "__panelId__": "12", + "summary": "Messages from RococoBridgeHub to WococoBridgeHub (00000001) are either not delivered, or are delivered with lags" + }, + "grafana_alert": { + "id": 51, + "orgId": 1, + "title": "RococoBridgeHub -> WococoBridgeHub delivery lags (00000001)", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Undelivered messages", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 90, + "uid": "r41otJp4k", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "zqjpgXxnk", + "__panelId__": "14", + "summary": "Messages from WococoBridgeHub to RococoBridgeHub (00000001) are either not delivered, or are delivered with lags" + }, + "grafana_alert": { + "id": 55, + "orgId": 1, + "title": "WococoBridgeHub -> RococoBridgeHub delivery lags (00000001)", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Undelivered messages", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 88, + "uid": "wqmPtJpVz", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "tkgc6_bnk", + "__panelId__": "14", + "summary": "Messages from RococoBridgeHub to WococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" + }, + "grafana_alert": { + "id": 56, + "orgId": 1, + "title": "RococoBridgeHub -> WococoBridgeHub confirmation lags (00000001)", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Unconfirmed messages", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 50 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 84, + "uid": "z4h3pJtVz", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "zqjpgXxnk", + "__panelId__": "16", + "summary": "Messages from WococoBridgeHub to RococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" + }, + "grafana_alert": { + "id": 57, + "orgId": 1, + "title": "WococoBridgeHub -> RococoBridgeHub confirmation lags (00000001)", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Unconfirmed messages", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 50 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 83, + "uid": "Kj_z21t4k", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "zqjpgXxnk", + "__panelId__": "18", + "summary": "Rewards for messages from WococoBridgeHub to RococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" + }, + "grafana_alert": { + "id": 58, + "orgId": 1, + "title": "WococoBridgeHub -> RococoBridgeHub reward lags (00000001)", + "condition": "C", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Unconfirmed rewards", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "(scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "hide": true, + "interval": "", + "intervalMs": 30000, + "legendFormat": "__auto", + "maxDataPoints": 43200, + "range": true, + "refId": "B" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "C", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 78, + "uid": "hw_a21pVk", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "tkgc6_bnk", + "__panelId__": "15", + "summary": "Rewards for messages from RococoBridgeHub to WococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" + }, + "grafana_alert": { + "id": 59, + "orgId": 1, + "title": "RococoBridgeHub -> WococoBridgeHub reward lags (00000001)", + "condition": "C", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Unconfirmed rewards", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "(scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "hide": true, + "interval": "", + "intervalMs": 30000, + "legendFormat": "__auto", + "maxDataPoints": 43200, + "range": true, + "refId": "B" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "C", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 77, + "uid": "daN62Jt4z", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "2", + "summary": "Best BridgeHubRococo header at BridgeHubWococo (00000001) doesn't match the same header at BridgeHubRococo" + }, + "grafana_alert": { + "id": 60, + "orgId": 1, + "title": "BridgeHubRococo header mismatch (00000001)", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 74, + "uid": "BzBDb1pVz", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "3", + "summary": "Best BridgeHubWococo header at BridgeHubRococo (00000001) doesn't match the same header at BridgeHubWococo" + }, + "grafana_alert": { + "id": 61, + "orgId": 1, + "title": "BridgeHubWococo header mismatch (00000001)", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 73, + "uid": "1W6lb1p4z", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "5", + "summary": "With-WococoBridgeHub messages relay balance at RococoBridgeHub (00000001) is too low" + }, + "grafana_alert": { + "id": 62, + "orgId": 1, + "title": "With-WococoBridgeHub messages relay balance at RococoBridgeHub (00000001) is too low", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "last_over_time(at_BridgeHubRococo_relay_BridgeHubWococoMessages_balance{domain=\"parity-testnet\"}[1h])", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Messages Relay Balance", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 72, + "uid": "Y5Dm-1tVz", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "6", + "summary": "With-RococoBridgeHub messages relay balance at WococoBridgeHub (00000001) is too low" + }, + "grafana_alert": { + "id": 63, + "orgId": 1, + "title": "With-RococoBridgeHub messages relay balance at WococoBridgeHub (00000001) is too low", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "last_over_time(at_BridgeHubWococo_relay_BridgeHubRococoMessages_balance{domain=\"parity-testnet\"}[1h])", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Messages Relay Balance", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 71, + "uid": "yUl4a1tVz", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "5m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "tkgc6_bnk", + "__panelId__": "6", + "summary": "Less than 500 Rococo headers have been synced to WococoBridgeHub in last 120 minutes. Relay is not running?" + }, + "grafana_alert": { + "id": 65, + "orgId": 1, + "title": "Rococo -> WococoBridgeHub finality sync lags (00000001)", + "condition": "D", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "max(increase(Rococo_to_BridgeHubWococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}[120m]))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "At Rococo", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "reducer": "last", + "refId": "C", + "type": "reduce" + } + }, + { + "refId": "D", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 500 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "D" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "C", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "D", + "type": "threshold" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 40, + "uid": "R6GKwNA4z", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "5m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "zqjpgXxnk", + "__panelId__": "2", + "summary": "Less than 500 Wococo headers have been synced to RococoBridgeHub in last 120 minutes. Relay is not running?" + }, + "grafana_alert": { + "id": 66, + "orgId": 1, + "title": "Wococo -> RococoBridgeHub finality sync lags (00000001)", + "condition": "D", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "max(increase(Wococo_to_BridgeHubRococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}[120m]))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "At Wococo", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "reducer": "last", + "refId": "C", + "type": "reduce" + } + }, + { + "refId": "D", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 500 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "D" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "C", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "D", + "type": "threshold" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 38, + "uid": "rAM1QHAVk", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "0s", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "11", + "summary": "The RococoBridgeHub <> WococoBridgeHub relay (00000001) has been aborted by version guard - i.e. one of chains has been upgraded and relay wasn't redeployed" + }, + "grafana_alert": { + "id": 67, + "orgId": 1, + "title": "Version guard has aborted RococoBridgeHub <> WococoBridgeHub relay (00000001)", + "condition": "B", + "data": [ + { + "refId": "B", + "queryType": "range", + "relativeTimeRange": { + "from": 600, + "to": 0 + }, + "datasourceUid": "P03E52D76DFE188C3", + "model": { + "datasource": { + "type": "loki", + "uid": "P03E52D76DFE188C3" + }, + "editorMode": "code", + "expr": "count_over_time({container=\"bridges-common-relay\"} |= `Aborting relay` [1m])", + "hide": false, + "intervalMs": 1000, + "legendFormat": "Aborts per minute", + "maxDataPoints": 43200, + "queryType": "range", + "refId": "B" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "B", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "reducer": "max", + "refId": "C", + "type": "reduce" + } + }, + { + "refId": "D", + "queryType": "", + "relativeTimeRange": { + "from": 600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "D" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "C", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "D", + "type": "threshold" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 37, + "uid": "TwWPeN04z", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "12", + "summary": "Best Rococo header at BridgeHubWococo (00000001) doesn't match the same header at Rococo" + }, + "grafana_alert": { + "id": 69, + "orgId": 1, + "title": "Rococo headers mismatch", + "condition": "C", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 0, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "reducer": "last", + "refId": "B", + "type": "reduce" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 0, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "B", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "C", + "type": "threshold" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 32, + "uid": "08-5gv04k", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "13", + "summary": "Best Wococo header at BridgeHubRococo (00000001) doesn't match the same header at Wococo" + }, + "grafana_alert": { + "id": 70, + "orgId": 1, + "title": "Wococo headers mismatch", + "condition": "C", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 0, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "reducer": "last", + "refId": "B", + "type": "reduce" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 0, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "B", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "C", + "type": "threshold" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 31, + "uid": "Esj2gD0Vk", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "5m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "tkgc6_bnk", + "__panelId__": "9", + "summary": "Test messages from RococoBridgeHub to WococoBridgeHub are not generated. Our cronjob has died?" + }, + "grafana_alert": { + "id": 73, + "orgId": 1, + "title": "Test messages from RococoBridgeHub to WococoBridgeHub are not generated.", + "condition": "D", + "data": [ + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "increase(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\", type=~\"source_latest_generated\"}[24h])", + "hide": true, + "interval": "", + "intervalMs": 30000, + "legendFormat": "Messages generated in last 24h", + "maxDataPoints": 43200, + "range": true, + "refId": "B" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "B", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "reducer": "max", + "refId": "C", + "type": "reduce" + } + }, + { + "refId": "D", + "queryType": "", + "relativeTimeRange": { + "from": 600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "D" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "C", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "D", + "type": "threshold" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 3, + "uid": "ry1K5SB4k", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "5m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "16", + "summary": "RococoBridgeHub <> WococoBridgeHub relay (00000001) node is down" + }, + "grafana_alert": { + "id": 74, + "orgId": 1, + "title": "RococoBridgeHub <> WococoBridgeHub relay (00000001) node is down", + "condition": "C", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 900, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": false, + "expr": "up{domain=\"parity-testnet\",container=\"bridges-common-relay\"}", + "instant": false, + "interval": "", + "intervalMs": 30000, + "legendFormat": "Is relay running", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 0, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "reducer": "max", + "refId": "B", + "type": "reduce" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 0, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "B", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "C", + "type": "threshold" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 1, + "uid": "9YAdEUB4z", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + } + ] + } + ] + } diff --git a/deployments/bridges/rococo-wococo/dashboard/grafana/rococo-wococo-maintenance-dashboard.json b/deployments/bridges/rococo-wococo/dashboard/grafana/rococo-wococo-maintenance-dashboard.json index 46dc6c43c49..6158c3e73a9 100644 --- a/deployments/bridges/rococo-wococo/dashboard/grafana/rococo-wococo-maintenance-dashboard.json +++ b/deployments/bridges/rococo-wococo/dashboard/grafana/rococo-wococo-maintenance-dashboard.json @@ -1,853 +1,1031 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 284, - "links": [], - "liveNow": false, - "panels": [ + "annotations": { + "list": [ { + "builtIn": 1, "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "type": "grafana", + "uid": "-- Grafana --" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" }, - "gridPos": { - "h": 5, - "w": 5, - "x": 0, - "y": 0 + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 284, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } }, - "id": 8, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "name" + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 0 + }, + "id": 8, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false }, - "pluginVersion": "9.3.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "exemplar": false, - "expr": "substrate_relay_build_info{domain=\"parity-testnet\"}", - "instant": true, - "legendFormat": "{{commit}}", - "range": false, - "refId": "A" + "text": {}, + "textMode": "name" + }, + "pluginVersion": "9.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": false, + "expr": "substrate_relay_build_info{domain=\"parity-testnet\"}", + "instant": true, + "legendFormat": "{{commit}}", + "range": false, + "refId": "A" + } + ], + "title": "Relay build commit", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } - ], - "title": "Relay build commit", - "type": "stat" + }, + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 5, + "w": 4, + "x": 5, + "y": 0 + }, + "id": 9, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^1\\.0\\.1$/", + "values": false }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { + "text": {}, + "textMode": "name" + }, + "pluginVersion": "9.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": false, + "expr": "substrate_relay_build_info{domain=\"parity-testnet\"}", + "instant": true, + "legendFormat": "{{version}}", + "range": false, + "refId": "A" + } + ], + "title": "Relay build version", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { "color": "red", - "value": 80 + "index": 1, + "text": "No" + }, + "1": { + "color": "green", + "index": 0, + "text": "Yes" } - ] + }, + "type": "value" } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 5, - "x": 5, - "y": 0 - }, - "id": 9, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "/^1\\.0\\.1$/", - "values": false - }, - "text": {}, - "textMode": "name" - }, - "pluginVersion": "9.3.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "exemplar": false, - "expr": "substrate_relay_build_info{domain=\"parity-testnet\"}", - "instant": true, - "legendFormat": "{{version}}", - "range": false, - "refId": "A" + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } - ], - "title": "Relay build version", - "type": "stat" + }, + "overrides": [] }, - { - "datasource": { - "type": "loki", - "uid": "P03E52D76DFE188C3" + "gridPos": { + "h": 5, + "w": 4, + "x": 9, + "y": 0 + }, + "id": 15, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "textMode": "auto" + }, + "pluginVersion": "9.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": false, + "expr": "up{domain=\"parity-testnet\",container=\"bridges-common-relay\"}", + "instant": false, + "legendFormat": "Is relay running", + "range": true, + "refId": "A" + } + ], + "title": "Is relay running?", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 8, - "x": 10, - "y": 0 - }, - "id": 11, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "loki", - "uid": "P03E52D76DFE188C3" - }, - "editorMode": "code", - "expr": "count_over_time({container=\"bridges-common-relay\"} |~ `(?i)(warn|error|fail)` [1m])", - "legendFormat": "Errors per minute", - "queryType": "range", - "refId": "A" - } - ], - "title": "Relay errors/warnings per minute", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 5, + "w": 5, + "x": 13, + "y": 0 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": false, + "expr": "up{domain=\"parity-testnet\",container=\"bridges-common-relay\"}", + "instant": false, + "legendFormat": "Is relay running", + "range": true, + "refId": "A" + } + ], + "title": "Is relay running? (for alert)", + "type": "timeseries" + }, + { + "datasource": { + "type": "loki", + "uid": "P03E52D76DFE188C3" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 9, - "x": 0, - "y": 5 - }, - "id": 12, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "Rococo_to_BridgeHubWococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", - "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", - "range": true, - "refId": "A" - } - ], - "title": "Rococo headers mismatch", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 9, + "w": 18, + "x": 0, + "y": 5 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "P03E52D76DFE188C3" + }, + "editorMode": "code", + "expr": "count_over_time({container=\"bridges-common-relay\"} |~ `(?i)(warn|error|fail)` [1m])", + "legendFormat": "Errors per minute", + "queryType": "range", + "refId": "A" + } + ], + "title": "Relay errors/warnings per minute", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 9, - "x": 9, - "y": 5 - }, - "id": 13, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "Wococo_to_BridgeHubRococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", - "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", - "range": true, - "refId": "A" - } - ], - "title": "Wococo headers mismatch", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 7, + "w": 9, + "x": 0, + "y": 14 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "range": true, + "refId": "A" + } + ], + "title": "Rococo headers mismatch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 9, - "x": 0, - "y": 12 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", - "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", - "range": true, - "refId": "A" - } - ], - "title": "BridgeHubRococo headers mismatch", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 7, + "w": 9, + "x": 9, + "y": 14 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "range": true, + "refId": "A" + } + ], + "title": "Wococo headers mismatch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 9, - "x": 9, - "y": 12 - }, - "id": 3, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", - "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", - "range": true, - "refId": "A" - } - ], - "title": "BridgeHubWococo headers mismatch", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 7, + "w": 9, + "x": 0, + "y": 21 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "range": true, + "refId": "A" + } + ], + "title": "BridgeHubRococo headers mismatch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 9, - "x": 0, - "y": 19 - }, - "id": 5, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "at_BridgeHubRococo_relay_BridgeHubWococoMessages_balance{domain=\"parity-testnet\"}", - "legendFormat": "Messages Relay Balance", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "at_BridgeHubRococo_relay_WococoHeaders_reward_for_msgs_to_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"} + at_BridgeHubRococo_relay_BridgeHubWococoMessages_reward_for_msgs_to_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"}", - "hide": false, - "legendFormat": "Pending reward", - "range": true, - "refId": "B" - } - ], - "title": "Relay balances at RococoBridgeHub", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 7, + "w": 9, + "x": 9, + "y": 21 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "range": true, + "refId": "A" + } + ], + "title": "BridgeHubWococo headers mismatch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } }, - "gridPos": { - "h": 8, - "w": 9, - "x": 9, - "y": 19 + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 0, + "y": 28 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "id": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "at_BridgeHubRococo_relay_BridgeHubWococoMessages_balance{domain=\"parity-testnet\"}", + "legendFormat": "Messages Relay Balance", + "range": true, + "refId": "A" }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "at_BridgeHubRococo_relay_WococoHeaders_reward_for_msgs_to_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"} + at_BridgeHubRococo_relay_BridgeHubWococoMessages_reward_for_msgs_to_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "Pending reward", + "range": true, + "refId": "B" + } + ], + "title": "Relay balances at RococoBridgeHub", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" }, - "editorMode": "code", - "expr": "at_BridgeHubWococo_relay_BridgeHubRococoMessages_balance{domain=\"parity-testnet\"}", - "legendFormat": "Messages Relay Balance", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" }, - "editorMode": "code", - "expr": "at_BridgeHubRococo_relay_BridgeHubWococoMessages_reward_for_msgs_from_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"} + at_BridgeHubRococo_relay_BridgeHubWococoMessages_reward_for_msgs_to_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"}", - "hide": false, - "legendFormat": "Pending reward", - "range": true, - "refId": "B" + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } - ], - "title": "Relay balances at WococoBridgeHub", - "type": "timeseries" - } - ], - "refresh": "5s", - "schemaVersion": 37, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "BridgeHubRococo <> BridgeHubWococo maintenance (00000001)", - "uid": "UFsgbJtVz", - "version": 23, - "weekStart": "" - } \ No newline at end of file + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 9, + "y": 28 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "at_BridgeHubWococo_relay_BridgeHubRococoMessages_balance{domain=\"parity-testnet\"}", + "legendFormat": "Messages Relay Balance", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "at_BridgeHubRococo_relay_BridgeHubWococoMessages_reward_for_msgs_from_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"} + at_BridgeHubRococo_relay_BridgeHubWococoMessages_reward_for_msgs_to_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "Pending reward", + "range": true, + "refId": "B" + } + ], + "title": "Relay balances at WococoBridgeHub", + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BridgeHubRococo <> BridgeHubWococo maintenance (00000001)", + "uid": "UFsgbJtVz", + "version": 26, + "weekStart": "" +} diff --git a/deployments/networks/dashboard/grafana/beefy-dashboard.json b/deployments/networks/dashboard/grafana/beefy-dashboard.json index a5df3449e67..2e1e177641a 100644 --- a/deployments/networks/dashboard/grafana/beefy-dashboard.json +++ b/deployments/networks/dashboard/grafana/beefy-dashboard.json @@ -8,14 +8,22 @@ "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, "type": "dashboard" } ] }, "editable": true, + "fiscalYearStartMonth": 0, "gnetId": null, "graphTooltip": 0, "links": [], + "liveNow": false, "panels": [ { "alert": { @@ -81,28 +89,6 @@ "dashLength": 10, "dashes": false, "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": { - "align": null - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "gridPos": { @@ -125,8 +111,11 @@ "lines": true, "linewidth": 1, "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.3", + "pluginVersion": "8.2.6", "pointradius": 2, "points": false, "renderer": "flot", @@ -136,24 +125,33 @@ "steppedLine": false, "targets": [ { + "exemplar": true, "expr": "substrate_beefy_best_block{chain=\"rialto_local\"}", - "legendFormat": "Rialto(Charlie)", + "interval": "", + "legendFormat": "", "refId": "A" }, { + "exemplar": true, "expr": "substrate_beefy_best_block{chain=\"millau_local\"}", - "legendFormat": "Millau(Charlie)", + "hide": false, + "interval": "", + "legendFormat": "", "refId": "B" }, { - "expr": "max_over_time(substrate_beefy_best_block{chain=\"millau_local\"}[5m]) - min_over_time(substrate_beefy_best_block{chain=\"millau_local\"}[5m])", + "exemplar": true, + "expr": "increase(substrate_beefy_best_block{chain=\"millau_local\"}[5m])", "hide": true, + "interval": "", "legendFormat": "Millau Best Beefy blocks count in last 5 minutes", "refId": "C" }, { - "expr": "max_over_time(substrate_beefy_best_block{chain=\"rialto_local\"}[5m]) - min_over_time(substrate_beefy_best_block{chain=\"rialto_local\"}[5m])", + "exemplar": true, + "expr": "increase(substrate_beefy_best_block{chain=\"rialto_local\"}[5m])", "hide": true, + "interval": "", "legendFormat": "Rialto Best Beefy blocks count in last 5 minutes", "refId": "D" } @@ -164,7 +162,8 @@ "fill": true, "line": true, "op": "lt", - "value": 1 + "value": 1, + "visible": true } ], "timeFrom": null, @@ -208,98 +207,79 @@ } }, { - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {}, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "yellow", - "value": null - }, - { - "color": "yellow", - "value": null - } - ] + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" } - }, - "overrides": [] - }, - "gridPos": { - "h": 14, - "w": 11, - "x": 12, - "y": 0 - }, - "id": 4, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "textMode": "auto" + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Beefy Lagging Sessions alert", + "noDataState": "no_data", + "notifications": [] }, - "pluginVersion": "7.1.3", - "targets": [ - { - "expr": "substrate_beefy_should_vote_on{chain=\"rialto_local\"}", - "legendFormat": "Rialto(Charlie) Should-Vote-On", - "refId": "C" - }, - { - "expr": "substrate_beefy_round_concluded{chain=\"rialto_local\"}", - "legendFormat": "Rialto(Charlie) Round-Concluded", - "refId": "A" - }, - { - "expr": "substrate_beefy_should_vote_on{chain=\"millau_local\"}", - "legendFormat": "Millau(Charlie) Should-Vote-On", - "refId": "D" - }, - { - "expr": "substrate_beefy_round_concluded{chain=\"millau_local\"}", - "legendFormat": "Millau(Charlie) Round-Concluded", - "refId": "B" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Beefy Voting Rounds", - "type": "stat" - }, - { "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "gridPos": { - "h": 8, - "w": 18, - "x": 0, - "y": 14 + "h": 14, + "w": 6, + "x": 12, + "y": 0 }, "hiddenSeries": false, - "id": 6, + "id": 8, "legend": { "avg": false, "current": false, @@ -312,8 +292,11 @@ "lines": true, "linewidth": 1, "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.3", + "pluginVersion": "8.2.6", "pointradius": 2, "points": false, "renderer": "flot", @@ -323,21 +306,34 @@ "steppedLine": false, "targets": [ { - "expr": "substrate_beefy_votes_sent{chain=\"rialto_local\"}", - "legendFormat": "Rialto (node Charlie)", + "exemplar": true, + "expr": "substrate_beefy_lagging_sessions{chain=\"rialto_local\"}", + "interval": "", + "legendFormat": "", "refId": "A" }, { - "expr": "substrate_beefy_votes_sent{chain=\"millau_local\"}", - "legendFormat": "Millau (node Charlie)", + "exemplar": true, + "expr": "substrate_beefy_lagging_sessions{chain=\"millau_local\"}", + "interval": "", + "legendFormat": "", "refId": "B" } ], - "thresholds": [], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0, + "visible": true + } + ], "timeFrom": null, "timeRegions": [], "timeShift": null, - "title": "Beefy Votes Sent", + "title": "Beefy Lagging Sessions", "tooltip": { "shared": true, "sort": 0, @@ -375,101 +371,21 @@ } }, { - "alert": { - "alertRuleTags": {}, - "conditions": [ - { - "evaluator": { - "params": [ - 0 - ], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "A", - "5m", - "now" - ] - }, - "reducer": { - "params": [], - "type": "max" - }, - "type": "query" - }, - { - "evaluator": { - "params": [ - 0 - ], - "type": "gt" - }, - "operator": { - "type": "or" - }, - "query": { - "params": [ - "B", - "5m", - "now" - ] - }, - "reducer": { - "params": [], - "type": "max" - }, - "type": "query" - } - ], - "executionErrorState": "alerting", - "for": "5m", - "frequency": "1m", - "handler": 1, - "name": "Beefy Lagging Sessions alert", - "noDataState": "no_data", - "notifications": [] - }, "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "custom": { - "align": null - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 1 - } - ] - } - }, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "gridPos": { - "h": 8, - "w": 5, - "x": 18, + "h": 12, + "w": 18, + "x": 0, "y": 14 }, "hiddenSeries": false, - "id": 8, + "id": 6, "legend": { "avg": false, "current": false, @@ -482,8 +398,11 @@ "lines": true, "linewidth": 1, "nullPointMode": "null", + "options": { + "alertThreshold": true + }, "percentage": false, - "pluginVersion": "7.1.3", + "pluginVersion": "8.2.6", "pointradius": 2, "points": false, "renderer": "flot", @@ -493,29 +412,25 @@ "steppedLine": false, "targets": [ { - "expr": "substrate_beefy_lagging_sessions{chain=\"rialto_local\"}", - "legendFormat": "Rialto(Charlie)", + "exemplar": true, + "expr": "substrate_beefy_votes_sent{chain=\"rialto_local\"}", + "interval": "", + "legendFormat": "", "refId": "A" }, { - "expr": "substrate_beefy_lagging_sessions{chain=\"millau_local\"}", - "legendFormat": "Millau(Charlie)", + "exemplar": true, + "expr": "substrate_beefy_votes_sent{chain=\"millau_local\"}", + "interval": "", + "legendFormat": "", "refId": "B" } ], - "thresholds": [ - { - "colorMode": "critical", - "fill": true, - "line": true, - "op": "gt", - "value": 0 - } - ], + "thresholds": [], "timeFrom": null, "timeRegions": [], "timeShift": null, - "title": "Beefy Lagging Sessions", + "title": "Beefy Votes Sent", "tooltip": { "shared": true, "sort": 0, @@ -554,14 +469,14 @@ } ], "refresh": "5s", - "schemaVersion": 26, + "schemaVersion": 32, "style": "dark", "tags": [], "templating": { "list": [] }, "time": { - "from": "now-5m", + "from": "now-30m", "to": "now" }, "timepicker": { diff --git a/deployments/networks/dashboard/prometheus/millau-targets.yml b/deployments/networks/dashboard/prometheus/millau-targets.yml index c7a06509276..5890c8fb3fd 100644 --- a/deployments/networks/dashboard/prometheus/millau-targets.yml +++ b/deployments/networks/dashboard/prometheus/millau-targets.yml @@ -1,2 +1,6 @@ - targets: + - millau-node-alice:9615 + - millau-node-bob:9615 - millau-node-charlie:9615 + - millau-node-dave:9615 + - millau-node-eve:9615 diff --git a/deployments/networks/dashboard/prometheus/rialto-targets.yml b/deployments/networks/dashboard/prometheus/rialto-targets.yml index 9de26b9a2d7..0c89926e8c3 100644 --- a/deployments/networks/dashboard/prometheus/rialto-targets.yml +++ b/deployments/networks/dashboard/prometheus/rialto-targets.yml @@ -1,2 +1,6 @@ - targets: + - rialto-node-alice:9615 + - rialto-node-bob:9615 - rialto-node-charlie:9615 + - rialto-node-dave:9615 + - rialto-node-eve:9615 diff --git a/deployments/networks/millau.yml b/deployments/networks/millau.yml index 0f5846571aa..d91c5d83286 100644 --- a/deployments/networks/millau.yml +++ b/deployments/networks/millau.yml @@ -20,11 +20,13 @@ services: - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external + - --prometheus-external environment: - RUST_LOG: runtime=trace,rpc=debug,txpool=trace,runtime::bridge=trace,sc_basic_authorship=trace,beefy=debug,xcm=trace + RUST_LOG: runtime=trace,rpc=debug,txpool=trace,runtime::bridge=trace,sc_basic_authorship=trace,beefy=trace,xcm=trace ports: - "19933:9933" - "19944:9944" + - "19615:9615" millau-node-bob: <<: *millau-bridge-node @@ -39,9 +41,11 @@ services: - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external + - --prometheus-external ports: - "20033:9933" - "20044:9944" + - "20015:9615" millau-node-charlie: <<: *millau-bridge-node @@ -59,7 +63,7 @@ services: ports: - "20133:9933" - "20144:9944" - - "20615:9615" + - "20115:9615" millau-node-dave: <<: *millau-bridge-node @@ -73,9 +77,11 @@ services: - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external + - --prometheus-external ports: - "20233:9933" - "20244:9944" + - "20215:9615" millau-node-eve: <<: *millau-bridge-node @@ -89,13 +95,19 @@ services: - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external + - --prometheus-external ports: - "20333:9933" - "20344:9944" + - "20315:9615" # Note: These are being overridden from the top level `monitoring` compose file. prometheus-metrics: volumes: - ./networks/dashboard/prometheus/millau-targets.yml:/etc/prometheus/targets-millau-nodes.yml depends_on: + - millau-node-alice + - millau-node-bob - millau-node-charlie + - millau-node-dave + - millau-node-eve diff --git a/deployments/networks/rialto.yml b/deployments/networks/rialto.yml index 5a8ed64e067..fab85b89c04 100644 --- a/deployments/networks/rialto.yml +++ b/deployments/networks/rialto.yml @@ -20,11 +20,13 @@ services: - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external + - --prometheus-external environment: - RUST_LOG: runtime=trace,rpc=debug,txpool=trace,runtime::bridge=trace,beefy=debug,xcm=trace + RUST_LOG: runtime=trace,rpc=debug,txpool=trace,runtime::bridge=trace,beefy=trace,xcm=trace ports: - "9933:9933" - "9944:9944" + - "9915:9615" rialto-node-bob: <<: *rialto-bridge-node @@ -39,9 +41,11 @@ services: - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external + - --prometheus-external ports: - "10033:9933" - "10044:9944" + - "10015:9615" rialto-node-charlie: <<: *rialto-bridge-node @@ -59,7 +63,7 @@ services: ports: - "10133:9933" - "10144:9944" - - "10615:9615" + - "10115:9615" rialto-node-dave: <<: *rialto-bridge-node @@ -73,9 +77,11 @@ services: - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external + - --prometheus-external ports: - "10233:9933" - "10244:9944" + - "10215:9615" rialto-node-eve: <<: *rialto-bridge-node @@ -89,9 +95,11 @@ services: - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external + - --prometheus-external ports: - "10333:9933" - "10344:9944" + - "10315:9615" rialto-chainspec-exporter: image: ${RIALTO_BRIDGE_NODE_IMAGE:-paritytech/rialto-bridge-node} @@ -105,7 +113,11 @@ services: volumes: - ./networks/dashboard/prometheus/rialto-targets.yml:/etc/prometheus/targets-rialto-nodes.yml depends_on: + - rialto-node-alice + - rialto-node-bob - rialto-node-charlie + - rialto-node-dave + - rialto-node-eve # we're using `/rialto-share` to expose Rialto chain spec to those who are interested. Right # now it is Rialto Parachain collator nodes. Local + tmpfs combination allows sharing writable diff --git a/deployments/types-millau.json b/deployments/types-millau.json index 6d651b4c7cf..88b67f70b05 100644 --- a/deployments/types-millau.json +++ b/deployments/types-millau.json @@ -88,8 +88,7 @@ }, "DeliveredMessages": { "begin": "MessageNonce", - "end": "MessageNonce", - "dispatch_results": "BitVec" + "end": "MessageNonce" }, "OutboundLaneData": { "oldest_unpruned_nonce": "MessageNonce", diff --git a/deployments/types-rialto.json b/deployments/types-rialto.json index a574e117893..02ceaf676d6 100644 --- a/deployments/types-rialto.json +++ b/deployments/types-rialto.json @@ -88,8 +88,7 @@ }, "DeliveredMessages": { "begin": "MessageNonce", - "end": "MessageNonce", - "dispatch_results": "BitVec" + "end": "MessageNonce" }, "OutboundLaneData": { "oldest_unpruned_nonce": "MessageNonce", diff --git a/deployments/types/common.json b/deployments/types/common.json index 4e129f7132b..7247e546bec 100644 --- a/deployments/types/common.json +++ b/deployments/types/common.json @@ -34,8 +34,7 @@ }, "DeliveredMessages": { "begin": "MessageNonce", - "end": "MessageNonce", - "dispatch_results": "BitVec" + "end": "MessageNonce" }, "OutboundLaneData": { "oldest_unpruned_nonce": "MessageNonce", diff --git a/modules/beefy/Cargo.toml b/modules/beefy/Cargo.toml index b50ac723db2..8aff2c169b4 100644 --- a/modules/beefy/Cargo.toml +++ b/modules/beefy/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } log = { version = "0.4.14", default-features = false } -scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true } # Bridge Dependencies diff --git a/modules/beefy/src/lib.rs b/modules/beefy/src/lib.rs index a3a68928bc5..79795dd0770 100644 --- a/modules/beefy/src/lib.rs +++ b/modules/beefy/src/lib.rs @@ -94,7 +94,7 @@ pub struct ImportedCommitmentsInfoData { next_block_number_index: u32, } -#[frame_support::pallet] +#[frame_support::pallet(dev_mode)] pub mod pallet { use super::*; use bp_runtime::{BasicOperatingMode, OwnedBridgeModule}; diff --git a/modules/grandpa/Cargo.toml b/modules/grandpa/Cargo.toml index 0a737ef633a..07d2593b914 100644 --- a/modules/grandpa/Cargo.toml +++ b/modules/grandpa/Cargo.toml @@ -11,7 +11,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } finality-grandpa = { version = "0.16.2", default-features = false } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } # Bridge Dependencies diff --git a/modules/grandpa/src/benchmarking.rs b/modules/grandpa/src/benchmarking.rs index 78c7fc362a2..aa222d6e4de 100644 --- a/modules/grandpa/src/benchmarking.rs +++ b/modules/grandpa/src/benchmarking.rs @@ -136,4 +136,6 @@ benchmarks_instance_pallet! { // check that the header#0 has been pruned assert!(!>::contains_key(genesis_header.hash())); } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) } diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index 2d81f3106fc..9d38c9723d7 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -103,14 +103,17 @@ pub mod pallet { /// The chain we are bridging to here. type BridgedChain: ChainWithGrandpa; - /// The upper bound on the number of requests allowed by the pallet. + /// Maximal number of "free" mandatory header transactions per block. /// - /// A request refers to an action which writes a header to storage. - /// - /// Once this bound is reached the pallet will not allow any dispatchables to be called - /// until the request count has decreased. + /// To be able to track the bridged chain, the pallet requires all headers that are + /// changing GRANDPA authorities set at the bridged chain (we call them mandatory). + /// So it is a common good deed to submit mandatory headers to the pallet. However, if the + /// bridged chain gets compromised, its validators may generate as many mandatory headers + /// as they want. And they may fill the whole block (at this chain) for free. This constants + /// limits number of calls that we may refund in a single block. All calls above this + /// limit are accepted, but are not refunded. #[pallet::constant] - type MaxRequests: Get; + type MaxFreeMandatoryHeadersPerBlock: Get; /// Maximal number of finalized headers to keep in the storage. /// @@ -131,10 +134,13 @@ pub mod pallet { #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { - fn on_initialize(_n: T::BlockNumber) -> frame_support::weights::Weight { - >::mutate(|count| *count = count.saturating_sub(1)); + fn on_initialize(_n: BlockNumberFor) -> Weight { + FreeMandatoryHeadersRemaining::::put(T::MaxFreeMandatoryHeadersPerBlock::get()); + Weight::zero() + } - T::DbWeight::get().reads_writes(1, 1) + fn on_finalize(_n: BlockNumberFor) { + FreeMandatoryHeadersRemaining::::kill(); } } @@ -154,6 +160,16 @@ pub mod pallet { /// /// If successful in verification, it will write the target header to the underlying storage /// pallet. + /// + /// The call fails if: + /// + /// - the pallet is halted; + /// + /// - the pallet knows better header than the `finality_target`; + /// + /// - verification is not optimized or invalid; + /// + /// - header contains forced authorities set change or change with non-zero delay. #[pallet::call_index(0)] #[pallet::weight(::submit_finality_proof( justification.commit.precommits.len().saturated_into(), @@ -166,8 +182,6 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { Self::ensure_not_halted().map_err(Error::::BridgeModule)?; - ensure!(Self::request_count() < T::MaxRequests::get(), >::TooManyRequests); - let (hash, number) = (finality_target.hash(), *finality_target.number()); log::trace!( target: LOG_TARGET, @@ -185,9 +199,16 @@ pub mod pallet { let is_authorities_change_enacted = try_enact_authority_change::(&finality_target, set_id)?; let may_refund_call_fee = is_authorities_change_enacted && + // if we have seen too many mandatory headers in this block, we don't want to refund + Self::free_mandatory_headers_remaining() > 0 && + // if arguments out of expected bounds, we don't want to refund submit_finality_proof_info_from_args::(&finality_target, &justification) .fits_limits(); - >::mutate(|count| *count += 1); + if may_refund_call_fee { + FreeMandatoryHeadersRemaining::::mutate(|count| { + *count = count.saturating_sub(1) + }); + } insert_header::(*finality_target, hash); log::info!( target: LOG_TARGET, @@ -273,16 +294,20 @@ pub mod pallet { } } - /// The current number of requests which have written to storage. + /// Number mandatory headers that we may accept in the current block for free (returning + /// `Pays::No`). /// - /// If the `RequestCount` hits `MaxRequests`, no more calls will be allowed to the pallet until - /// the request capacity is increased. + /// If the `FreeMandatoryHeadersRemaining` hits zero, all following mandatory headers in the + /// current block are accepted with fee (`Pays::Yes` is returned). /// - /// The `RequestCount` is decreased by one at the beginning of every block. This is to ensure - /// that the pallet can always make progress. + /// The `FreeMandatoryHeadersRemaining` is an ephemeral value that is set to + /// `MaxFreeMandatoryHeadersPerBlock` at each block initialization and is killed on block + /// finalization. So it never ends up in the storage trie. #[pallet::storage] - #[pallet::getter(fn request_count)] - pub(super) type RequestCount, I: 'static = ()> = StorageValue<_, u32, ValueQuery>; + #[pallet::whitelist_storage] + #[pallet::getter(fn free_mandatory_headers_remaining)] + pub(super) type FreeMandatoryHeadersRemaining, I: 'static = ()> = + StorageValue<_, u32, ValueQuery>; /// Hash of the header used to bootstrap the pallet. #[pallet::storage] @@ -392,8 +417,6 @@ pub mod pallet { InvalidJustification, /// The authority set from the underlying header chain is invalid. InvalidAuthoritySet, - /// There are too many requests for the current window to handle. - TooManyRequests, /// The header being imported is older than the best finalized header known to the pallet. OldHeader, /// The scheduled authority set change found in the header is unsupported by the pallet. @@ -662,7 +685,8 @@ mod tests { }; use codec::Encode; use frame_support::{ - assert_err, assert_noop, assert_ok, dispatch::PostDispatchInfo, + assert_err, assert_noop, assert_ok, + dispatch::{Pays, PostDispatchInfo}, storage::generator::StorageValue, }; use frame_system::{EventRecord, Phase}; @@ -705,6 +729,51 @@ mod tests { ) } + fn submit_finality_proof_with_set_id( + header: u8, + set_id: u64, + ) -> frame_support::dispatch::DispatchResultWithPostInfo { + let header = test_header(header.into()); + let justification = make_justification_for_header(JustificationGeneratorParams { + header: header.clone(), + set_id, + ..Default::default() + }); + + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification, + ) + } + + fn submit_mandatory_finality_proof( + number: u8, + set_id: u64, + ) -> frame_support::dispatch::DispatchResultWithPostInfo { + let mut header = test_header(number.into()); + // to ease tests that are using `submit_mandatory_finality_proof`, we'll be using the + // same set for all sessions + let consensus_log = + ConsensusLog::::ScheduledChange(sp_consensus_grandpa::ScheduledChange { + next_authorities: authority_list(), + delay: 0, + }); + header.digest = + Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] }; + let justification = make_justification_for_header(JustificationGeneratorParams { + header: header.clone(), + set_id, + ..Default::default() + }); + + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification, + ) + } + fn next_block() { use frame_support::traits::OnInitialize; @@ -1169,13 +1238,18 @@ mod tests { } #[test] - fn rate_limiter_disallows_imports_once_limit_is_hit_in_single_block() { + fn rate_limiter_disallows_free_imports_once_limit_is_hit_in_single_block() { run_test(|| { initialize_substrate_bridge(); - assert_ok!(submit_finality_proof(1)); - assert_ok!(submit_finality_proof(2)); - assert_err!(submit_finality_proof(3), >::TooManyRequests); + let result = submit_mandatory_finality_proof(1, 1); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(2, 2); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(3, 3); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); }) } @@ -1183,7 +1257,8 @@ mod tests { fn rate_limiter_invalid_requests_do_not_count_towards_request_count() { run_test(|| { let submit_invalid_request = || { - let header = test_header(1); + let mut header = test_header(1); + header.digest = change_log(0); let mut invalid_justification = make_default_justification(&header); invalid_justification.round = 42; @@ -1196,15 +1271,19 @@ mod tests { initialize_substrate_bridge(); - for _ in 0..::MaxRequests::get() + 1 { - // Notice that the error here *isn't* `TooManyRequests` + for _ in 0..::MaxFreeMandatoryHeadersPerBlock::get() + 1 { assert_err!(submit_invalid_request(), >::InvalidJustification); } - // Can still submit `MaxRequests` requests afterwards - assert_ok!(submit_finality_proof(1)); - assert_ok!(submit_finality_proof(2)); - assert_err!(submit_finality_proof(3), >::TooManyRequests); + // Can still submit free mandatory headers afterwards + let result = submit_mandatory_finality_proof(1, 1); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(2, 2); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(3, 3); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); }) } @@ -1212,40 +1291,51 @@ mod tests { fn rate_limiter_allows_request_after_new_block_has_started() { run_test(|| { initialize_substrate_bridge(); - assert_ok!(submit_finality_proof(1)); - assert_ok!(submit_finality_proof(2)); - next_block(); - assert_ok!(submit_finality_proof(3)); - }) - } + let result = submit_mandatory_finality_proof(1, 1); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); - #[test] - fn rate_limiter_disallows_imports_once_limit_is_hit_across_different_blocks() { - run_test(|| { - initialize_substrate_bridge(); - assert_ok!(submit_finality_proof(1)); - assert_ok!(submit_finality_proof(2)); + let result = submit_mandatory_finality_proof(2, 2); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(3, 3); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); next_block(); - assert_ok!(submit_finality_proof(3)); - assert_err!(submit_finality_proof(4), >::TooManyRequests); + + let result = submit_mandatory_finality_proof(4, 4); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(5, 5); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(6, 6); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); }) } #[test] - fn rate_limiter_allows_max_requests_after_long_time_with_no_activity() { + fn rate_limiter_ignores_non_mandatory_headers() { run_test(|| { initialize_substrate_bridge(); - assert_ok!(submit_finality_proof(1)); - assert_ok!(submit_finality_proof(2)); - next_block(); - next_block(); + let result = submit_finality_proof(1); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - next_block(); - assert_ok!(submit_finality_proof(5)); - assert_ok!(submit_finality_proof(7)); + let result = submit_mandatory_finality_proof(2, 1); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_finality_proof_with_set_id(3, 2); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); + + let result = submit_mandatory_finality_proof(4, 2); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_finality_proof_with_set_id(5, 3); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); + + let result = submit_mandatory_finality_proof(6, 3); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); }) } diff --git a/modules/grandpa/src/mock.rs b/modules/grandpa/src/mock.rs index b10f5d86c3d..0ebbc0bccbb 100644 --- a/modules/grandpa/src/mock.rs +++ b/modules/grandpa/src/mock.rs @@ -21,7 +21,7 @@ use bp_header_chain::ChainWithGrandpa; use bp_runtime::Chain; use frame_support::{ construct_runtime, parameter_types, - traits::{ConstU32, ConstU64}, + traits::{ConstU32, ConstU64, Hooks}, weights::Weight, }; use sp_core::sr25519::Signature; @@ -87,7 +87,7 @@ impl frame_system::Config for TestRuntime { } parameter_types! { - pub const MaxRequests: u32 = 2; + pub const MaxFreeMandatoryHeadersPerBlock: u32 = 2; pub const HeadersToKeep: u32 = 5; pub const SessionLength: u64 = 5; pub const NumValidators: u32 = 5; @@ -96,7 +96,7 @@ parameter_types! { impl grandpa::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type BridgedChain = TestBridgedChain; - type MaxRequests = MaxRequests; + type MaxFreeMandatoryHeadersPerBlock = MaxFreeMandatoryHeadersPerBlock; type HeadersToKeep = HeadersToKeep; type WeightInfo = (); } @@ -131,10 +131,20 @@ impl ChainWithGrandpa for TestBridgedChain { const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64; } +/// Return test externalities to use in tests. +pub fn new_test_ext() -> sp_io::TestExternalities { + sp_io::TestExternalities::new(Default::default()) +} + +/// Return test within default test externalities context. pub fn run_test(test: impl FnOnce() -> T) -> T { - sp_io::TestExternalities::new(Default::default()).execute_with(test) + new_test_ext().execute_with(|| { + let _ = Grandpa::on_initialize(0); + test() + }) } +/// Return test header with given number. pub fn test_header(num: TestNumber) -> TestHeader { // We wrap the call to avoid explicit type annotations in our tests bp_test_utils::test_header(num) diff --git a/modules/messages/Cargo.toml b/modules/messages/Cargo.toml index afa8b92b228..f733d62bf64 100644 --- a/modules/messages/Cargo.toml +++ b/modules/messages/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } log = { version = "0.4.17", default-features = false } num-traits = { version = "0.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } # Bridge dependencies @@ -27,9 +27,9 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [dev-dependencies] -sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } -pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } bp-test-utils = { path = "../../primitives/test-utils" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } [features] default = ["std"] diff --git a/modules/messages/src/benchmarking.rs b/modules/messages/src/benchmarking.rs index bc9c1f75257..aab8855a729 100644 --- a/modules/messages/src/benchmarking.rs +++ b/modules/messages/src/benchmarking.rs @@ -37,7 +37,7 @@ use sp_std::{ops::RangeInclusive, prelude::*}; const SEED: u32 = 0; /// Pallet we're benchmarking here. -pub struct Pallet, I: 'static>(crate::Pallet); +pub struct Pallet, I: 'static = ()>(crate::Pallet); /// Benchmark-specific message proof parameters. #[derive(Debug)] @@ -437,6 +437,8 @@ benchmarks_instance_pallet! { ); assert!(T::is_message_successfully_dispatched(21)); } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) } fn send_regular_message, I: 'static>() { diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index 3faf00b9e75..c94f5ffa752 100644 --- a/modules/messages/src/lib.rs +++ b/modules/messages/src/lib.rs @@ -63,7 +63,7 @@ use bp_messages::{ MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, UnrewardedRelayersState, }; -use bp_runtime::{BasicOperatingMode, ChainId, OwnedBridgeModule, Size}; +use bp_runtime::{BasicOperatingMode, ChainId, OwnedBridgeModule, PreComputedSize, Size}; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get}; use sp_runtime::traits::UniqueSaturatedFrom; @@ -90,6 +90,7 @@ pub const LOG_TARGET: &str = "runtime::bridge-messages"; pub mod pallet { use super::*; use bp_messages::{ReceivalResult, ReceivedMessages}; + use bp_runtime::RangeInclusiveExt; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -249,6 +250,22 @@ pub mod pallet { /// The weight of the call assumes that the transaction always brings outbound lane /// state update. Because of that, the submitter (relayer) has no benefit of not including /// this data in the transaction, so reward confirmations lags should be minimal. + /// + /// The call fails if: + /// + /// - the pallet is halted; + /// + /// - the call origin is not `Signed(_)`; + /// + /// - there are too many messages in the proof; + /// + /// - the proof verification procedure returns an error - e.g. because header used to craft + /// proof is not imported by the associated finality pallet; + /// + /// - the `dispatch_weight` argument is not sufficient to dispatch all bundled messages. + /// + /// The call may succeed, but some messages may not be delivered e.g. if they are not fit + /// into the unrewarded relayers vector. #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight))] pub fn receive_messages_proof( @@ -324,18 +341,10 @@ pub mod pallet { let mut lane_messages_received_status = ReceivedMessages::new(lane_id, Vec::with_capacity(lane_data.messages.len())); - let mut is_lane_processing_stopped_no_weight_left = false; - for mut message in lane_data.messages { debug_assert_eq!(message.key.lane_id, lane_id); total_messages += 1; - if is_lane_processing_stopped_no_weight_left { - lane_messages_received_status - .push_skipped_for_not_enough_weight(message.key.nonce); - continue - } - // ensure that relayer has declared enough weight for dispatching next message // on this lane. We can't dispatch lane messages out-of-order, so if declared // weight is not enough, let's move to next lane @@ -348,10 +357,8 @@ pub mod pallet { message_dispatch_weight, dispatch_weight_left, ); - lane_messages_received_status - .push_skipped_for_not_enough_weight(message.key.nonce); - is_lane_processing_stopped_no_weight_left = true; - continue + + fail!(Error::::InsufficientDispatchWeight); } let receival_result = lane.receive_message::( @@ -417,10 +424,11 @@ pub mod pallet { pub fn receive_messages_delivery_proof( origin: OriginFor, proof: MessagesDeliveryProofOf, - relayers_state: UnrewardedRelayersState, - ) -> DispatchResult { + mut relayers_state: UnrewardedRelayersState, + ) -> DispatchResultWithPostInfo { Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + let proof_size = proof.size(); let confirmation_relayer = ensure_signed(origin)?; let (lane_id, lane_data) = T::TargetHeaderChain::verify_messages_delivery_proof(proof) .map_err(|err| { @@ -493,13 +501,23 @@ pub mod pallet { }); // if some new messages have been confirmed, reward relayers - T::DeliveryConfirmationPayments::pay_reward( + let actually_rewarded_relayers = T::DeliveryConfirmationPayments::pay_reward( lane_id, lane_data.relayers, &confirmation_relayer, &received_range, ); - } + + // update relayers state with actual numbers to compute actual weight below + relayers_state.unrewarded_relayer_entries = sp_std::cmp::min( + relayers_state.unrewarded_relayer_entries, + actually_rewarded_relayers, + ); + relayers_state.total_messages = sp_std::cmp::min( + relayers_state.total_messages, + received_range.checked_len().unwrap_or(MessageNonce::MAX), + ); + }; log::trace!( target: LOG_TARGET, @@ -508,7 +526,15 @@ pub mod pallet { lane_id, ); - Ok(()) + // because of lags, the inbound lane state (`lane_data`) may have entries for + // already rewarded relayers and messages (if all entries are duplicated, then + // this transaction must be filtered out by our signed extension) + let actual_weight = T::WeightInfo::receive_messages_delivery_proof_weight( + &PreComputedSize(proof_size as usize), + &relayers_state, + ); + + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) } } @@ -554,8 +580,9 @@ pub mod pallet { /// The relayer has declared invalid unrewarded relayers state in the /// `receive_messages_delivery_proof` call. InvalidUnrewardedRelayersState, - /// The message someone is trying to work with (i.e. increase fee) is already-delivered. - MessageIsAlreadyDelivered, + /// The cumulative dispatch weight, passed by relayer is not enough to cover dispatch + /// of all bundled messages. + InsufficientDispatchWeight, /// The message someone is trying to work with (i.e. increase fee) is not yet sent. MessageIsNotYetSent, /// The number of actually confirmed messages is going to be larger than the number of @@ -925,8 +952,9 @@ mod tests { message, message_payload, run_test, unrewarded_relayer, AccountId, DbWeight, RuntimeEvent as TestEvent, RuntimeOrigin, TestDeliveryConfirmationPayments, TestDeliveryPayments, TestMessagesDeliveryProof, TestMessagesProof, TestRelayer, - TestRuntime, MAX_OUTBOUND_PAYLOAD_SIZE, PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, - TEST_LANE_ID, TEST_LANE_ID_2, TEST_LANE_ID_3, TEST_RELAYER_A, TEST_RELAYER_B, + TestRuntime, TestWeightInfo, MAX_OUTBOUND_PAYLOAD_SIZE, PAYLOAD_REJECTED_BY_TARGET_CHAIN, + REGULAR_PAYLOAD, TEST_LANE_ID, TEST_LANE_ID_2, TEST_LANE_ID_3, TEST_RELAYER_A, + TEST_RELAYER_B, }; use bp_messages::{BridgeMessagesCall, UnrewardedRelayer, UnrewardedRelayersState}; use bp_test_utils::generate_owned_bridge_module_tests; @@ -1277,13 +1305,16 @@ mod tests { run_test(|| { let mut declared_weight = REGULAR_PAYLOAD.declared_weight; *declared_weight.ref_time_mut() -= 1; - assert_ok!(Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), - 1, - declared_weight, - )); + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), + 1, + declared_weight, + ), + Error::::InsufficientDispatchWeight + ); assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); }); } @@ -1348,50 +1379,78 @@ mod tests { )); // this reports delivery of message 1 => reward is paid to TEST_RELAYER_A - assert_ok!(Pallet::::receive_messages_delivery_proof( + let single_message_delivery_proof = TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into_iter().collect(), + ..Default::default() + }, + ))); + let single_message_delivery_proof_size = single_message_delivery_proof.size(); + let result = Pallet::::receive_messages_delivery_proof( RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] - .into_iter() - .collect(), - ..Default::default() - } - ))), + single_message_delivery_proof, UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, last_delivered_nonce: 1, ..Default::default() }, - )); + ); + assert_ok!(result); + assert_eq!( + result.unwrap().actual_weight.unwrap(), + TestWeightInfo::receive_messages_delivery_proof_weight( + &PreComputedSize(single_message_delivery_proof_size as _), + &UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + ..Default::default() + }, + ) + ); assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); // this reports delivery of both message 1 and message 2 => reward is paid only to // TEST_RELAYER_B - assert_ok!(Pallet::::receive_messages_delivery_proof( + let two_messages_delivery_proof = TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![ + unrewarded_relayer(1, 1, TEST_RELAYER_A), + unrewarded_relayer(2, 2, TEST_RELAYER_B), + ] + .into_iter() + .collect(), + ..Default::default() + }, + ))); + let two_messages_delivery_proof_size = two_messages_delivery_proof.size(); + let result = Pallet::::receive_messages_delivery_proof( RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - relayers: vec![ - unrewarded_relayer(1, 1, TEST_RELAYER_A), - unrewarded_relayer(2, 2, TEST_RELAYER_B) - ] - .into_iter() - .collect(), - ..Default::default() - } - ))), + two_messages_delivery_proof, UnrewardedRelayersState { unrewarded_relayer_entries: 2, total_messages: 2, last_delivered_nonce: 2, ..Default::default() }, - )); + ); + assert_ok!(result); + // even though the pre-dispatch weight was for two messages, the actual weight is + // for single message only + assert_eq!( + result.unwrap().actual_weight.unwrap(), + TestWeightInfo::receive_messages_delivery_proof_weight( + &PreComputedSize(two_messages_delivery_proof_size as _), + &UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + ..Default::default() + }, + ) + ); assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); }); @@ -1541,15 +1600,18 @@ mod tests { let message2 = message(2, message_payload(0, u64::MAX / 2)); let message3 = message(3, message_payload(0, u64::MAX / 2)); - assert_ok!(Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - // this may cause overflow if source chain storage is invalid - Ok(vec![message1, message2, message3]).into(), - 3, - Weight::MAX, - )); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 2); + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + // this may cause overflow if source chain storage is invalid + Ok(vec![message1, message2, message3]).into(), + 3, + Weight::MAX, + ), + Error::::InsufficientDispatchWeight + ); + assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); }); } diff --git a/modules/messages/src/mock.rs b/modules/messages/src/mock.rs index 807721ba866..75f05b4820a 100644 --- a/modules/messages/src/mock.rs +++ b/modules/messages/src/mock.rs @@ -147,9 +147,12 @@ parameter_types! { pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID, TEST_LANE_ID_2]; } +/// weights of messages pallet calls we use in tests. +pub type TestWeightInfo = (); + impl Config for TestRuntime { type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); + type WeightInfo = TestWeightInfo; type ActiveOutboundLanes = ActiveOutboundLanes; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; @@ -170,6 +173,44 @@ impl Config for TestRuntime { type BridgedChainId = TestBridgedChainId; } +#[cfg(feature = "runtime-benchmarks")] +impl crate::benchmarking::Config<()> for TestRuntime { + fn bench_lane_id() -> LaneId { + TEST_LANE_ID + } + + fn prepare_message_proof( + params: crate::benchmarking::MessageProofParams, + ) -> (TestMessagesProof, Weight) { + // in mock run we only care about benchmarks correctness, not the benchmark results + // => ignore size related arguments + let (messages, total_dispatch_weight) = + params.message_nonces.into_iter().map(|n| message(n, REGULAR_PAYLOAD)).fold( + (Vec::new(), Weight::zero()), + |(mut messages, total_dispatch_weight), message| { + let weight = REGULAR_PAYLOAD.declared_weight; + messages.push(message); + (messages, total_dispatch_weight.saturating_add(weight)) + }, + ); + let mut proof: TestMessagesProof = Ok(messages).into(); + proof.result.as_mut().unwrap().get_mut(0).unwrap().1.lane_state = params.outbound_lane_data; + (proof, total_dispatch_weight) + } + + fn prepare_message_delivery_proof( + params: crate::benchmarking::MessageDeliveryProofParams, + ) -> TestMessagesDeliveryProof { + // in mock run we only care about benchmarks correctness, not the benchmark results + // => ignore size related arguments + TestMessagesDeliveryProof(Ok((params.lane, params.inbound_lane_data))) + } + + fn is_relayer_rewarded(_relayer: &AccountId) -> bool { + true + } +} + impl Size for TestPayload { fn size(&self) -> u32 { 16 + self.extra.len() as u32 @@ -342,12 +383,15 @@ impl DeliveryConfirmationPayments for TestDeliveryConfirmationPayment messages_relayers: VecDeque>, _confirmation_relayer: &AccountId, received_range: &RangeInclusive, - ) { + ) -> MessageNonce { let relayers_rewards = calc_relayers_rewards(messages_relayers, received_range); + let rewarded_relayers = relayers_rewards.len(); for (relayer, reward) in &relayers_rewards { let key = (b":relayer-reward:", relayer, reward).encode(); frame_support::storage::unhashed::put(&key, &true); } + + rewarded_relayers as _ } } @@ -439,12 +483,16 @@ pub fn unrewarded_relayer( UnrewardedRelayer { relayer, messages: DeliveredMessages { begin, end } } } -/// Run pallet test. -pub fn run_test(test: impl FnOnce() -> T) -> T { +/// Return test externalities to use in tests. +pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { balances: vec![(ENDOWED_ACCOUNT, 1_000_000)] } .assimilate_storage(&mut t) .unwrap(); - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(test) + sp_io::TestExternalities::new(t) +} + +/// Run pallet test. +pub fn run_test(test: impl FnOnce() -> T) -> T { + new_test_ext().execute_with(test) } diff --git a/modules/parachains/Cargo.toml b/modules/parachains/Cargo.toml index c25956248d0..39c3ba626aa 100644 --- a/modules/parachains/Cargo.toml +++ b/modules/parachains/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } # Bridge Dependencies diff --git a/modules/parachains/src/benchmarking.rs b/modules/parachains/src/benchmarking.rs index 83cfba0b8c6..59c4642cde9 100644 --- a/modules/parachains/src/benchmarking.rs +++ b/modules/parachains/src/benchmarking.rs @@ -28,7 +28,7 @@ use frame_system::RawOrigin; use sp_std::prelude::*; /// Pallet we're benchmarking here. -pub struct Pallet, I: 'static>(crate::Pallet); +pub struct Pallet, I: 'static = ()>(crate::Pallet); /// Trait that must be implemented by runtime to benchmark the parachains finality pallet. pub trait Config: crate::Config { @@ -111,4 +111,6 @@ benchmarks_instance_pallet! { assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); } } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) } diff --git a/modules/parachains/src/lib.rs b/modules/parachains/src/lib.rs index e72e2aec8c2..6c89b09513c 100644 --- a/modules/parachains/src/lib.rs +++ b/modules/parachains/src/lib.rs @@ -294,6 +294,16 @@ pub mod pallet { /// `polkadot-runtime-parachains::paras` pallet instance, deployed at the bridged chain. /// The proof is supposed to be crafted at the `relay_header_hash` that must already be /// imported by corresponding GRANDPA pallet at this chain. + /// + /// The call fails if: + /// + /// - the pallet is halted; + /// + /// - the relay chain block `at_relay_block` is not imported by the associated bridge + /// GRANDPA pallet. + /// + /// The call may succeed, but some heads may not be updated e.g. because pallet knows + /// better head or it isn't tracked by the pallet. #[pallet::call_index(0)] #[pallet::weight(WeightInfoOf::::submit_parachain_heads_weight( T::DbWeight::get(), @@ -689,7 +699,7 @@ pub fn initialize_for_benchmarks, I: 'static, PC: Parachain::WeightInfo; type DbWeight = ::DbWeight; - fn initialize(state_root: RelayBlockHash) { + pub(crate) fn initialize(state_root: RelayBlockHash) -> RelayBlockHash { pallet_bridge_grandpa::Pallet::::initialize( RuntimeOrigin::root(), bp_header_chain::InitializationData { @@ -738,6 +748,8 @@ mod tests { System::::set_block_number(1); System::::reset_events(); + + test_relay_header(0, state_root).hash() } fn proceed(num: RelayBlockNumber, state_root: RelayBlockHash) -> ParaHash { @@ -759,7 +771,7 @@ mod tests { hash } - fn prepare_parachain_heads_proof( + pub(crate) fn prepare_parachain_heads_proof( heads: Vec<(u32, ParaHead)>, ) -> (RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) { let mut parachains = Vec::with_capacity(heads.len()); @@ -795,7 +807,7 @@ mod tests { } } - fn head_data(parachain: u32, head_number: u32) -> ParaHead { + pub(crate) fn head_data(parachain: u32, head_number: u32) -> ParaHead { ParaHead( RegularParachainHeader::new( head_number as _, diff --git a/modules/parachains/src/mock.rs b/modules/parachains/src/mock.rs index c19ce98eba2..3086adc1cc2 100644 --- a/modules/parachains/src/mock.rs +++ b/modules/parachains/src/mock.rs @@ -199,7 +199,7 @@ parameter_types! { impl pallet_bridge_grandpa::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type BridgedChain = TestBridgedChain; - type MaxRequests = ConstU32<2>; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<2>; type HeadersToKeep = HeadersToKeep; type WeightInfo = (); } @@ -207,7 +207,7 @@ impl pallet_bridge_grandpa::Config for TestRun impl pallet_bridge_grandpa::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type BridgedChain = TestBridgedChain; - type MaxRequests = ConstU32<2>; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<2>; type HeadersToKeep = HeadersToKeep; type WeightInfo = (); } @@ -228,6 +228,36 @@ impl pallet_bridge_parachains::Config for TestRuntime { type MaxParaHeadDataSize = ConstU32; } +#[cfg(feature = "runtime-benchmarks")] +impl pallet_bridge_parachains::benchmarking::Config<()> for TestRuntime { + fn parachains() -> Vec { + vec![ + ParaId(Parachain1::PARACHAIN_ID), + ParaId(Parachain2::PARACHAIN_ID), + ParaId(Parachain3::PARACHAIN_ID), + ] + } + + fn prepare_parachain_heads_proof( + parachains: &[ParaId], + _parachain_head_size: u32, + _proof_size: bp_runtime::StorageProofSize, + ) -> ( + crate::RelayBlockNumber, + crate::RelayBlockHash, + bp_polkadot_core::parachains::ParaHeadsProof, + Vec<(ParaId, bp_polkadot_core::parachains::ParaHash)>, + ) { + // in mock run we only care about benchmarks correctness, not the benchmark results + // => ignore size related arguments + let (state_root, proof, parachains) = crate::tests::prepare_parachain_heads_proof( + parachains.iter().map(|p| (p.0, crate::tests::head_data(p.0, 1))).collect(), + ); + let relay_genesis_hash = crate::tests::initialize(state_root); + (0, relay_genesis_hash, proof, parachains) + } +} + #[derive(Debug)] pub struct TestBridgedChain; @@ -290,14 +320,21 @@ impl ChainWithGrandpa for OtherBridgedChain { const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64; } +/// Return test externalities to use in tests. +pub fn new_test_ext() -> sp_io::TestExternalities { + sp_io::TestExternalities::new(Default::default()) +} + +/// Run pallet test. pub fn run_test(test: impl FnOnce() -> T) -> T { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + new_test_ext().execute_with(|| { System::set_block_number(1); System::reset_events(); test() }) } +/// Return test relay chain header with given number. pub fn test_relay_header( num: crate::RelayBlockNumber, state_root: crate::RelayBlockHash, diff --git a/modules/relayers/Cargo.toml b/modules/relayers/Cargo.toml index 200fbbca309..c654c60d02b 100644 --- a/modules/relayers/Cargo.toml +++ b/modules/relayers/Cargo.toml @@ -9,7 +9,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } # Bridge dependencies diff --git a/modules/relayers/src/benchmarking.rs b/modules/relayers/src/benchmarking.rs index 7dcf77eb958..a762a5693c2 100644 --- a/modules/relayers/src/benchmarking.rs +++ b/modules/relayers/src/benchmarking.rs @@ -54,4 +54,6 @@ benchmarks! { // payment logic, so we assume that if call has succeeded, the procedure has // also completed successfully } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) } diff --git a/modules/relayers/src/mock.rs b/modules/relayers/src/mock.rs index 5e97e81ae33..fe8c586eecc 100644 --- a/modules/relayers/src/mock.rs +++ b/modules/relayers/src/mock.rs @@ -99,6 +99,18 @@ impl pallet_bridge_relayers::Config for TestRuntime { type WeightInfo = (); } +#[cfg(feature = "runtime-benchmarks")] +impl pallet_bridge_relayers::benchmarking::Config for TestRuntime { + fn prepare_environment(account_params: RewardsAccountParams, reward: Balance) { + use frame_support::traits::fungible::Mutate; + let rewards_account = + bp_relayers::PayRewardFromAccount::::rewards_account( + account_params, + ); + Balances::mint_into(&rewards_account, reward).unwrap(); + } +} + /// Message lane that we're using in tests. pub const TEST_REWARDS_ACCOUNT_PARAMS: RewardsAccountParams = RewardsAccountParams::new(LaneId([0, 0, 0, 0]), *b"test", RewardsAccountOwner::ThisChain); @@ -127,9 +139,13 @@ impl PaymentProcedure for TestPaymentProcedure { } } +/// Return test externalities to use in tests. +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + sp_io::TestExternalities::new(t) +} + /// Run pallet test. pub fn run_test(test: impl FnOnce() -> T) -> T { - let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(test) + new_test_ext().execute_with(test) } diff --git a/modules/relayers/src/payment_adapter.rs b/modules/relayers/src/payment_adapter.rs index 2752044958b..a9536cfc027 100644 --- a/modules/relayers/src/payment_adapter.rs +++ b/modules/relayers/src/payment_adapter.rs @@ -20,7 +20,7 @@ use crate::{Config, Pallet}; use bp_messages::{ source_chain::{DeliveryConfirmationPayments, RelayersRewards}, - LaneId, + LaneId, MessageNonce, }; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use frame_support::{sp_runtime::SaturatedConversion, traits::Get}; @@ -47,9 +47,10 @@ where messages_relayers: VecDeque>, confirmation_relayer: &T::AccountId, received_range: &RangeInclusive, - ) { + ) -> MessageNonce { let relayers_rewards = bp_messages::calc_relayers_rewards::(messages_relayers, received_range); + let rewarded_relayers = relayers_rewards.len(); register_relayers_rewards::( confirmation_relayer, @@ -61,6 +62,8 @@ where ), DeliveryReward::get(), ); + + rewarded_relayers as _ } } diff --git a/modules/shift-session-manager/Cargo.toml b/modules/shift-session-manager/Cargo.toml index 504adfae416..2d7dc272a6f 100644 --- a/modules/shift-session-manager/Cargo.toml +++ b/modules/shift-session-manager/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } # Substrate Dependencies diff --git a/primitives/beefy/Cargo.toml b/primitives/beefy/Cargo.toml index 4b4044252ca..9039064fbf4 100644 --- a/primitives/beefy/Cargo.toml +++ b/primitives/beefy/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "bit-vec"] } -scale-info = { version = "2.0.1", default-features = false, features = ["bit-vec", "derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["bit-vec", "derive"] } serde = { version = "1.0", optional = true } # Bridge Dependencies diff --git a/primitives/chain-millau/Cargo.toml b/primitives/chain-millau/Cargo.toml index 00d5a02d47c..d1e2e0edd96 100644 --- a/primitives/chain-millau/Cargo.toml +++ b/primitives/chain-millau/Cargo.toml @@ -13,7 +13,7 @@ hash256-std-hasher = { version = "0.15.2", default-features = false } impl-codec = { version = "0.6", default-features = false } impl-serde = { version = "0.4.0", optional = true } parity-util-mem = { version = "0.12.0", default-features = false, features = ["primitive-types"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } # Bridge Dependencies diff --git a/primitives/header-chain/Cargo.toml b/primitives/header-chain/Cargo.toml index ca2f4ad88d3..5b9f87614a8 100644 --- a/primitives/header-chain/Cargo.toml +++ b/primitives/header-chain/Cargo.toml @@ -9,7 +9,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } finality-grandpa = { version = "0.16.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true } # Bridge dependencies @@ -27,7 +27,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", d [dev-dependencies] bp-test-utils = { path = "../test-utils" } hex = "0.4" -hex-literal = "0.3" +hex-literal = "0.4" [features] default = ["std"] diff --git a/primitives/messages/Cargo.toml b/primitives/messages/Cargo.toml index f54090893a6..32d7c65ebcb 100644 --- a/primitives/messages/Cargo.toml +++ b/primitives/messages/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive", "bit-vec"] } -scale-info = { version = "2.1.1", default-features = false, features = ["bit-vec", "derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["bit-vec", "derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } # Bridge dependencies @@ -23,7 +23,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", d [dev-dependencies] hex = "0.4" -hex-literal = "0.3" +hex-literal = "0.4" [features] default = ["std"] diff --git a/primitives/messages/src/lib.rs b/primitives/messages/src/lib.rs index 754349d634e..3910837a442 100644 --- a/primitives/messages/src/lib.rs +++ b/primitives/messages/src/lib.rs @@ -20,7 +20,7 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] -use bp_runtime::{BasicOperatingMode, OperatingMode}; +use bp_runtime::{BasicOperatingMode, OperatingMode, RangeInclusiveExt}; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::RuntimeDebug; use scale_info::TypeInfo; @@ -194,7 +194,7 @@ impl InboundLaneData { } /// Outbound message details, returned by runtime APIs. -#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq)] +#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct OutboundMessageDetails { /// Nonce assigned to the message. pub nonce: MessageNonce, @@ -208,7 +208,7 @@ pub struct OutboundMessageDetails { } /// Inbound message details, returned by runtime APIs. -#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq)] +#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] pub struct InboundMessageDetails { /// Computed message dispatch weight. /// @@ -238,8 +238,6 @@ pub struct ReceivedMessages { pub lane: LaneId, /// Result of messages which we tried to dispatch pub receive_results: Vec<(MessageNonce, ReceivalResult)>, - /// Messages which were skipped and never dispatched - pub skipped_for_not_enough_weight: Vec, } impl ReceivedMessages { @@ -247,16 +245,12 @@ impl ReceivedMessages { lane: LaneId, receive_results: Vec<(MessageNonce, ReceivalResult)>, ) -> Self { - ReceivedMessages { lane, receive_results, skipped_for_not_enough_weight: Vec::new() } + ReceivedMessages { lane, receive_results } } pub fn push(&mut self, message: MessageNonce, result: ReceivalResult) { self.receive_results.push((message, result)); } - - pub fn push_skipped_for_not_enough_weight(&mut self, message: MessageNonce) { - self.skipped_for_not_enough_weight.push(message); - } } /// Result of single message receival. @@ -293,11 +287,7 @@ impl DeliveredMessages { /// Return total count of delivered messages. pub fn total_messages(&self) -> MessageNonce { - if self.end >= self.begin { - self.end - self.begin + 1 - } else { - 0 - } + (self.begin..=self.end).checked_len().unwrap_or(0) } /// Note new dispatched message. diff --git a/primitives/messages/src/source_chain.rs b/primitives/messages/src/source_chain.rs index 83f27843d63..394a934171f 100644 --- a/primitives/messages/src/source_chain.rs +++ b/primitives/messages/src/source_chain.rs @@ -98,12 +98,14 @@ pub trait DeliveryConfirmationPayments { /// /// The implementation may also choose to pay reward to the `confirmation_relayer`, which is /// a relayer that has submitted delivery confirmation transaction. + /// + /// Returns number of actually rewarded relayers. fn pay_reward( lane_id: LaneId, messages_relayers: VecDeque>, confirmation_relayer: &AccountId, received_range: &RangeInclusive, - ); + ) -> MessageNonce; } impl DeliveryConfirmationPayments for () { @@ -114,8 +116,9 @@ impl DeliveryConfirmationPayments for () { _messages_relayers: VecDeque>, _confirmation_relayer: &AccountId, _received_range: &RangeInclusive, - ) { + ) -> MessageNonce { // this implementation is not rewarding relayers at all + 0 } } @@ -202,6 +205,7 @@ impl DeliveryConfirmationPayments for ForbidOutboundMessag _messages_relayers: VecDeque>, _confirmation_relayer: &AccountId, _received_range: &RangeInclusive, - ) { + ) -> MessageNonce { + 0 } } diff --git a/primitives/parachains/Cargo.toml b/primitives/parachains/Cargo.toml index 333f7ad647a..e47b8c5e68c 100644 --- a/primitives/parachains/Cargo.toml +++ b/primitives/parachains/Cargo.toml @@ -9,7 +9,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2" -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } # Bridge dependencies diff --git a/primitives/polkadot-core/Cargo.toml b/primitives/polkadot-core/Cargo.toml index a9db53a8bf0..daae99ec71c 100644 --- a/primitives/polkadot-core/Cargo.toml +++ b/primitives/polkadot-core/Cargo.toml @@ -9,7 +9,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } parity-util-mem = { version = "0.12.0", optional = true } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } # Bridge Dependencies diff --git a/primitives/relayers/Cargo.toml b/primitives/relayers/Cargo.toml index ee91361a4e2..8ac31258488 100644 --- a/primitives/relayers/Cargo.toml +++ b/primitives/relayers/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive", "bit-vec"] } -scale-info = { version = "2.1.1", default-features = false, features = ["bit-vec", "derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["bit-vec", "derive"] } # Bridge Dependencies @@ -23,7 +23,7 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", d [dev-dependencies] hex = "0.4" -hex-literal = "0.3" +hex-literal = "0.4" [features] default = ["std"] diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index 802e58e1af1..4d48ad61894 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -11,7 +11,7 @@ codec = { package = "parity-scale-codec", version = "3.1.5", default-features = hash-db = { version = "0.16.0", default-features = false } impl-trait-for-tuples = "0.2.2" num-traits = { version = "0.2", default-features = false } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } # Substrate Dependencies @@ -27,7 +27,7 @@ sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", trie-db = { version = "0.27.1", default-features = false } [dev-dependencies] -hex-literal = "0.3" +hex-literal = "0.4" [features] default = ["std"] diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index d4f551ce57a..df77745bc02 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -27,7 +27,7 @@ use frame_system::RawOrigin; use scale_info::TypeInfo; use sp_core::storage::StorageKey; use sp_runtime::traits::{BadOrigin, Header as HeaderT, UniqueSaturatedInto}; -use sp_std::{convert::TryFrom, fmt::Debug, vec, vec::Vec}; +use sp_std::{convert::TryFrom, fmt::Debug, ops::RangeInclusive, vec, vec::Vec}; pub use chain::{ AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf, @@ -35,7 +35,7 @@ pub use chain::{ UnderlyingChainProvider, }; pub use frame_support::storage::storage_prefix as storage_value_final_key; -use num_traits::{CheckedSub, One}; +use num_traits::{CheckedAdd, CheckedSub, One}; pub use storage_proof::{ record_all_keys as record_all_trie_keys, Error as StorageProofError, ProofSize as StorageProofSize, RawStorageProof, StorageProofChecker, @@ -523,6 +523,23 @@ impl Debug for StrippableError { } } +/// A trait defining helper methods for `RangeInclusive` (start..=end) +pub trait RangeInclusiveExt { + /// Computes the length of the `RangeInclusive`, checking for underflow and overflow. + fn checked_len(&self) -> Option; +} + +impl RangeInclusiveExt for RangeInclusive +where + Idx: CheckedSub + CheckedAdd + One, +{ + fn checked_len(&self) -> Option { + self.end() + .checked_sub(self.start()) + .and_then(|len| len.checked_add(&Idx::one())) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/relays/bin-substrate/Cargo.toml b/relays/bin-substrate/Cargo.toml index 6984cfed00c..7853b9cb599 100644 --- a/relays/bin-substrate/Cargo.toml +++ b/relays/bin-substrate/Cargo.toml @@ -10,7 +10,7 @@ anyhow = "1.0" async-std = "1.9.0" async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.1.5" } -futures = "0.3.27" +futures = "0.3.28" hex = "0.4" log = "0.4.17" num-format = "0.4" @@ -67,7 +67,7 @@ xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", defau [dev-dependencies] bp-test-utils = { path = "../../primitives/test-utils" } -hex-literal = "0.3" +hex-literal = "0.4" sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } -tempfile = "3.4" +tempfile = "3.5" finality-grandpa = { version = "0.16.2" } diff --git a/relays/bin-substrate/src/chains/rococo.rs b/relays/bin-substrate/src/chains/rococo.rs index 166e6edbad3..c0041c53ddb 100644 --- a/relays/bin-substrate/src/chains/rococo.rs +++ b/relays/bin-substrate/src/chains/rococo.rs @@ -27,5 +27,5 @@ impl CliChain for Rococo { impl CliChain for BridgeHubRococo { const RUNTIME_VERSION: Option = - Some(SimpleRuntimeVersion { spec_version: 9372, transaction_version: 1 }); + Some(SimpleRuntimeVersion { spec_version: 9381, transaction_version: 2 }); } diff --git a/relays/bin-substrate/src/chains/wococo.rs b/relays/bin-substrate/src/chains/wococo.rs index b6eceb8614e..73b3884c289 100644 --- a/relays/bin-substrate/src/chains/wococo.rs +++ b/relays/bin-substrate/src/chains/wococo.rs @@ -27,5 +27,5 @@ impl CliChain for Wococo { impl CliChain for BridgeHubWococo { const RUNTIME_VERSION: Option = - Some(SimpleRuntimeVersion { spec_version: 9372, transaction_version: 1 }); + Some(SimpleRuntimeVersion { spec_version: 9381, transaction_version: 2 }); } diff --git a/relays/client-bridge-hub-kusama/Cargo.toml b/relays/client-bridge-hub-kusama/Cargo.toml index 3485323ca6c..96650710f67 100644 --- a/relays/client-bridge-hub-kusama/Cargo.toml +++ b/relays/client-bridge-hub-kusama/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } relay-substrate-client = { path = "../client-substrate" } # Bridge dependencies diff --git a/relays/client-bridge-hub-polkadot/Cargo.toml b/relays/client-bridge-hub-polkadot/Cargo.toml index dee3147eb35..6126a8f2b3f 100644 --- a/relays/client-bridge-hub-polkadot/Cargo.toml +++ b/relays/client-bridge-hub-polkadot/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } relay-substrate-client = { path = "../client-substrate" } # Bridge dependencies diff --git a/relays/client-bridge-hub-rococo/Cargo.toml b/relays/client-bridge-hub-rococo/Cargo.toml index 78c3533971c..48df2e56cf0 100644 --- a/relays/client-bridge-hub-rococo/Cargo.toml +++ b/relays/client-bridge-hub-rococo/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } relay-substrate-client = { path = "../client-substrate" } # Bridge dependencies diff --git a/relays/client-bridge-hub-wococo/Cargo.toml b/relays/client-bridge-hub-wococo/Cargo.toml index 8164ae5afed..d4578fcd488 100644 --- a/relays/client-bridge-hub-wococo/Cargo.toml +++ b/relays/client-bridge-hub-wococo/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } relay-substrate-client = { path = "../client-substrate" } # Bridge dependencies diff --git a/relays/client-rialto-parachain/Cargo.toml b/relays/client-rialto-parachain/Cargo.toml index 987ac532ca6..4450dee3711 100644 --- a/relays/client-rialto-parachain/Cargo.toml +++ b/relays/client-rialto-parachain/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5" } -scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } subxt = { version = "0.27.1", default-features = false, features = [] } # Bridge dependencies diff --git a/relays/client-rialto-parachain/src/codegen_runtime.rs b/relays/client-rialto-parachain/src/codegen_runtime.rs index 893bca1e27e..3ea4a0ac2b5 100644 --- a/relays/client-rialto-parachain/src/codegen_runtime.rs +++ b/relays/client-rialto-parachain/src/codegen_runtime.rs @@ -6043,7 +6043,6 @@ pub mod api { ::core::primitive::u64, runtime_types::bp_messages::ReceivalResult<_0>, )>, - pub skipped_for_not_enough_weight: ::std::vec::Vec<::core::primitive::u64>, } #[derive( :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, @@ -7508,8 +7507,9 @@ pub mod api { #[doc = "`receive_messages_delivery_proof` call."] InvalidUnrewardedRelayersState, #[codec(index = 11)] - #[doc = "The message someone is trying to work with (i.e. increase fee) is already-delivered."] - MessageIsAlreadyDelivered, + #[doc = "The cumulative dispatch weight, passed by relayer is not enough to cover dispatch"] + #[doc = "of all bundled messages."] + InsufficientDispatchWeight, #[codec(index = 12)] #[doc = "The message someone is trying to work with (i.e. increase fee) is not yet sent."] MessageIsNotYetSent, diff --git a/relays/client-substrate/Cargo.toml b/relays/client-substrate/Cargo.toml index ce83817a995..2a79832bf9d 100644 --- a/relays/client-substrate/Cargo.toml +++ b/relays/client-substrate/Cargo.toml @@ -9,13 +9,13 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" async-std = { version = "1.6.5", features = ["attributes"] } async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.1.5" } -futures = "0.3.27" +futures = "0.3.28" jsonrpsee = { version = "0.16", features = ["macros", "ws-client"] } log = "0.4.17" num-traits = "0.2" rand = "0.8" -scale-info = { version = "2.1.1", features = ["derive"] } -tokio = { version = "1.26", features = ["rt-multi-thread"] } +scale-info = { version = "2.5.0", features = ["derive"] } +tokio = { version = "1.27", features = ["rt-multi-thread"] } thiserror = "1.0.40" # Bridge dependencies diff --git a/relays/finality/Cargo.toml b/relays/finality/Cargo.toml index 287798431fe..ab75533b023 100644 --- a/relays/finality/Cargo.toml +++ b/relays/finality/Cargo.toml @@ -11,7 +11,7 @@ async-std = "1.6.5" async-trait = "0.1" backoff = "0.4" bp-header-chain = { path = "../../primitives/header-chain" } -futures = "0.3.27" +futures = "0.3.28" log = "0.4.17" num-traits = "0.2" relay-utils = { path = "../utils" } diff --git a/relays/lib-substrate-relay/Cargo.toml b/relays/lib-substrate-relay/Cargo.toml index 17c75220f6d..d07aa936b57 100644 --- a/relays/lib-substrate-relay/Cargo.toml +++ b/relays/lib-substrate-relay/Cargo.toml @@ -11,7 +11,7 @@ thiserror = "1.0.40" async-std = "1.9.0" async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.1.5" } -futures = "0.3.27" +futures = "0.3.28" hex = "0.4" num-traits = "0.2" log = "0.4.17" diff --git a/relays/messages/Cargo.toml b/relays/messages/Cargo.toml index efb6c6c8585..8c4b8257d5a 100644 --- a/relays/messages/Cargo.toml +++ b/relays/messages/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] async-std = { version = "1.6.5", features = ["attributes"] } async-trait = "0.1" -futures = "0.3.27" +futures = "0.3.28" hex = "0.4" log = "0.4.17" num-traits = "0.2" diff --git a/relays/parachains/Cargo.toml b/relays/parachains/Cargo.toml index 80df2311bc9..0cecf063922 100644 --- a/relays/parachains/Cargo.toml +++ b/relays/parachains/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] async-std = "1.6.5" async-trait = "0.1.68" -futures = "0.3.27" +futures = "0.3.28" log = "0.4.17" relay-utils = { path = "../utils" } diff --git a/relays/utils/Cargo.toml b/relays/utils/Cargo.toml index 91bd9e20d65..aa14e2ae2e3 100644 --- a/relays/utils/Cargo.toml +++ b/relays/utils/Cargo.toml @@ -13,14 +13,14 @@ async-trait = "0.1" backoff = "0.4" isahc = "1.2" env_logger = "0.10.0" -futures = "0.3.27" +futures = "0.3.28" jsonpath_lib = "0.3" log = "0.4.17" num-traits = "0.2" serde_json = "1.0" sysinfo = "0.28" time = { version = "0.3", features = ["formatting", "local-offset", "std"] } -tokio = { version = "1.26", features = ["rt"] } +tokio = { version = "1.27", features = ["rt"] } thiserror = "1.0.40" # Bridge dependencies diff --git a/scripts/dump-logs.sh b/scripts/dump-logs.sh index 51e8518872a..709a5065887 100755 --- a/scripts/dump-logs.sh +++ b/scripts/dump-logs.sh @@ -15,8 +15,6 @@ cd $LOGS_DIR SERVICES=(\ deployments_relay-messages-millau-to-rialto-generator_1 \ deployments_relay-messages-rialto-to-millau-generator_1 \ - deployments_relay-messages-millau-to-rialto-lane-00000001_1 \ - deployments_relay-messages-rialto-to-millau-lane-00000001_1 \ deployments_relay-millau-rialto_1 \ deployments_relay-headers-westend-to-millau-1_1 \ deployments_relay-headers-westend-to-millau-2_1 \ diff --git a/scripts/verify-pallets-build.sh b/scripts/verify-pallets-build.sh index 98dbe02b4b0..ad69c5df008 100755 --- a/scripts/verify-pallets-build.sh +++ b/scripts/verify-pallets-build.sh @@ -88,7 +88,9 @@ rm -rf $BRIDGES_FOLDER/scripts/update-weights-setup.sh rm -rf $BRIDGES_FOLDER/scripts/update_substrate.sh rm -rf $BRIDGES_FOLDER/tools rm -f $BRIDGES_FOLDER/.dockerignore +rm -f $BRIDGES_FOLDER/deny.toml rm -f $BRIDGES_FOLDER/.gitlab-ci.yml +rm -f $BRIDGES_FOLDER/.editorconfig rm -f $BRIDGES_FOLDER/Cargo.toml rm -f $BRIDGES_FOLDER/ci.Dockerfile rm -f $BRIDGES_FOLDER/Dockerfile diff --git a/tools/runtime-codegen/Cargo.lock b/tools/runtime-codegen/Cargo.lock index 85777e90283..46093b70727 100644 --- a/tools/runtime-codegen/Cargo.lock +++ b/tools/runtime-codegen/Cargo.lock @@ -1199,9 +1199,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" dependencies = [ "bytes", "fnv", From 20f7a557a2e30e695b56881512152419d6347e0a Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 17 Apr 2023 12:29:29 +0200 Subject: [PATCH 239/263] Fixes --- .../bridge-hub-rococo/src/bridge_hub_rococo_config.rs | 2 ++ .../bridge-hub-rococo/src/bridge_hub_wococo_config.rs | 2 ++ parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 4 ++-- .../runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs | 5 +++-- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index 1dd47bdb17f..b2f4a7af81b 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -50,6 +50,7 @@ parameter_types! { pub BridgeHubRococoUniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(Rococo), Parachain(ParachainInfo::parachain_id().into())); pub WococoGlobalConsensusNetwork: NetworkId = NetworkId::Wococo; pub ActiveOutboundLanesToBridgeHubWococo: &'static [bp_messages::LaneId] = &[DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO]; + pub PriorityBoostPerMessage: u64 = 921_900_294; } /// Proof of messages, coming from Wococo. @@ -137,6 +138,7 @@ pub type BridgeRefundBridgeHubWococoMessages = RefundBridgedParachainMessages< RefundableParachain, RefundableMessagesLane, ActualFeeRefund, + PriorityBoostPerMessage, StrBridgeRefundBridgeHubWococoMessages, >; bp_runtime::generate_static_str_provider!(BridgeRefundBridgeHubWococoMessages); diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 7f756abee99..02d95c0646a 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -50,6 +50,7 @@ parameter_types! { pub BridgeHubWococoUniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(Wococo), Parachain(ParachainInfo::parachain_id().into())); pub RococoGlobalConsensusNetwork: NetworkId = NetworkId::Rococo; pub ActiveOutboundLanesToBridgeHubRococo: &'static [bp_messages::LaneId] = &[DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO]; + pub PriorityBoostPerMessage: u64 = 921_900_294; } /// Proof of messages, coming from Rococo. @@ -137,6 +138,7 @@ pub type BridgeRefundBridgeHubRococoMessages = RefundBridgedParachainMessages< RefundableParachain, RefundableMessagesLane, ActualFeeRefund, + PriorityBoostPerMessage, StrBridgeRefundBridgeHubRococoMessages, >; bp_runtime::generate_static_str_provider!(BridgeRefundBridgeHubRococoMessages); diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 22b6b7d13e0..ee0c7a660d6 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -402,7 +402,7 @@ pub type BridgeGrandpaWococoInstance = pallet_bridge_grandpa::Instance1; impl pallet_bridge_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; type BridgedChain = bp_wococo::Wococo; - type MaxRequests = MaxRequests; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; type HeadersToKeep = RelayChainHeadersToKeep; type WeightInfo = weights::pallet_bridge_grandpa_bridge_wococo_grandpa::WeightInfo; } @@ -412,7 +412,7 @@ pub type BridgeGrandpaRococoInstance = pallet_bridge_grandpa::Instance2; impl pallet_bridge_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; type BridgedChain = bp_rococo::Rococo; - type MaxRequests = MaxRequests; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; type HeadersToKeep = RelayChainHeadersToKeep; type WeightInfo = weights::pallet_bridge_grandpa_bridge_rococo_grandpa::WeightInfo; } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index e0b1f47fba6..84cdb3ecb4c 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -26,6 +26,7 @@ use xcm::latest::prelude::*; use bridge_hub_test_utils::*; use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult; use frame_support::weights::Weight; +use xcm_builder::DispatchBlobError; use xcm_executor::XcmExecutor; fn execute_on_runtime( @@ -95,7 +96,7 @@ fn dispatch_blob_and_xcm_routing_works_on_bridge_hub_wococo() { ); assert_eq!( result.dispatch_level_result, - XcmBlobMessageDispatchResult::NotDispatched("DispatchBlobError::RoutingError") + XcmBlobMessageDispatchResult::NotDispatched(Some(DispatchBlobError::RoutingError)) ); // 2.1. WITH hrmp channel -> Ok @@ -164,7 +165,7 @@ fn dispatch_blob_and_xcm_routing_works_on_bridge_hub_rococo() { ); assert_eq!( result.dispatch_level_result, - XcmBlobMessageDispatchResult::NotDispatched("DispatchBlobError::RoutingError") + XcmBlobMessageDispatchResult::NotDispatched(Some(DispatchBlobError::RoutingError)) ); // 2.1. WITH hrmp channel -> Ok From a9e27730296e3269b82cd315aed9d635610f076c Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 17 Apr 2023 12:31:10 +0200 Subject: [PATCH 240/263] Squashed 'bridges/' content from commit d30927c08 git-subtree-dir: bridges git-subtree-split: d30927c089bd9e73092d1ec1a62895603cb277a3 --- .config/lingua.dic | 244 + .config/spellcheck.toml | 13 + .dockerignore | 1 + .editorconfig | 19 + .github/dependabot.yml | 62 + .gitignore | 26 + .gitlab-ci.yml | 412 + .maintain/millau-weight-template.hbs | 138 + CODEOWNERS | 21 + CODE_OF_CONDUCT.md | 80 + Cargo.lock | 15431 ++++++++++++++++ Cargo.toml | 59 + Dockerfile | 72 + LICENSE | 675 + README.md | 259 + SECURITY.md | 14 + bin/.keep | 0 bin/millau/node/Cargo.toml | 59 + bin/millau/node/build.rs | 23 + bin/millau/node/src/chain_spec.rs | 235 + bin/millau/node/src/cli.rs | 73 + bin/millau/node/src/command.rs | 159 + bin/millau/node/src/lib.rs | 32 + bin/millau/node/src/main.rs | 30 + bin/millau/node/src/service.rs | 453 + bin/millau/runtime/Cargo.toml | 144 + bin/millau/runtime/build.rs | 25 + bin/millau/runtime/src/lib.rs | 1159 ++ bin/millau/runtime/src/rialto_messages.rs | 199 + .../runtime/src/rialto_parachain_messages.rs | 209 + bin/millau/runtime/src/xcm_config.rs | 373 + bin/rialto-parachain/node/Cargo.toml | 81 + bin/rialto-parachain/node/build.rs | 22 + bin/rialto-parachain/node/src/chain_spec.rs | 198 + bin/rialto-parachain/node/src/cli.rs | 142 + bin/rialto-parachain/node/src/command.rs | 445 + bin/rialto-parachain/node/src/lib.rs | 18 + bin/rialto-parachain/node/src/main.rs | 29 + bin/rialto-parachain/node/src/service.rs | 510 + bin/rialto-parachain/runtime/Cargo.toml | 139 + bin/rialto-parachain/runtime/build.rs | 25 + bin/rialto-parachain/runtime/src/lib.rs | 959 + .../runtime/src/millau_messages.rs | 138 + bin/rialto/node/Cargo.toml | 50 + bin/rialto/node/build.rs | 23 + bin/rialto/node/src/chain_spec.rs | 289 + bin/rialto/node/src/cli.rs | 88 + bin/rialto/node/src/command.rs | 222 + bin/rialto/node/src/main.rs | 28 + bin/rialto/runtime/Cargo.toml | 142 + bin/rialto/runtime/build.rs | 25 + bin/rialto/runtime/src/lib.rs | 986 + bin/rialto/runtime/src/millau_messages.rs | 198 + bin/rialto/runtime/src/parachains.rs | 165 + bin/rialto/runtime/src/xcm_config.rs | 276 + bin/runtime-common/Cargo.toml | 92 + bin/runtime-common/src/integrity.rs | 331 + bin/runtime-common/src/lib.rs | 245 + bin/runtime-common/src/messages.rs | 787 + bin/runtime-common/src/messages_api.rs | 66 + .../src/messages_benchmarking.rs | 293 + bin/runtime-common/src/messages_call_ext.rs | 634 + bin/runtime-common/src/messages_generation.rs | 119 + .../src/messages_xcm_extension.rs | 164 + bin/runtime-common/src/mock.rs | 402 + .../src/parachains_benchmarking.rs | 88 + bin/runtime-common/src/priority_calculator.rs | 201 + .../src/refund_relayer_extension.rs | 1349 ++ ci.Dockerfile | 53 + deny.toml | 202 + deployments/README.md | 270 + .../bridges/common/generate_messages.sh | 65 + ...y-millau-to-rialto-messages-dashboard.json | 1153 ++ ...y-rialto-to-millau-messages-dashboard.json | 1145 ++ .../rialto-millau-maintenance-dashboard.json | 635 + .../dashboard/prometheus/targets.yml | 2 + .../bridges/rialto-millau/docker-compose.yml | 88 + ...ay-messages-millau-to-rialto-entrypoint.sh | 16 + ...ay-messages-rialto-to-millau-entrypoint.sh | 16 + ...messages-to-millau-generator-entrypoint.sh | 27 + ...messages-to-rialto-generator-entrypoint.sh | 27 + ...ssages-to-rialto-resubmitter-entrypoint.sh | 25 + .../relay-millau-rialto-entrypoint.sh | 37 + ...o-rialto-parachain-messages-dashboard.json | 910 + ...arachain-to-millau-messages-dashboard.json | 908 + ...arachain-millau-maintenance-dashboard.json | 627 + .../dashboard/prometheus/targets.yml | 3 + .../docker-compose.yml | 90 + ...messages-to-millau-generator-entrypoint.sh | 28 + ...o-rialto-parachain-generator-entrypoint.sh | 27 + ...rialto-parachain-resubmitter-entrypoint.sh | 25 + ...elay-millau-rialto-parachain-entrypoint.sh | 39 + deployments/bridges/rococo-wococo/README.md | 10 + .../dashboard/grafana/bridges-alerts.json | 1994 ++ ...y-rococo-to-wococo-messages-dashboard.json | 941 + ...y-wococo-to-rococo-messages-dashboard.json | 941 + .../rococo-wococo-maintenance-dashboard.json | 1031 ++ ...y-westend-to-millau-headers-dashboard.json | 781 + ...estend-to-millau-parachains-dashboard.json | 200 + .../dashboard/prometheus/targets.yml | 3 + .../bridges/westend-millau/docker-compose.yml | 72 + ...ay-headers-westend-to-millau-entrypoint.sh | 26 + ...parachains-westend-to-millau-entrypoint.sh | 16 + .../local-scripts/bridge-entrypoint.sh | 7 + .../relay-messages-millau-to-rialto.sh | 20 + .../relay-messages-rialto-to-millau.sh | 20 + .../local-scripts/relay-millau-to-rialto.sh | 29 + .../local-scripts/relay-rialto-to-millau.sh | 27 + deployments/local-scripts/run-millau-node.sh | 11 + deployments/local-scripts/run-rialto-node.sh | 11 + deployments/local-scripts/run-westend-node.sh | 14 + .../monitoring/GrafanaMatrix.Dockerfile | 15 + deployments/monitoring/disabled.yml | 15 + deployments/monitoring/docker-compose.yml | 36 + .../monitoring/grafana-matrix/config.yml | 47 + .../monitoring/grafana/dashboards/nodes.json | 200 + .../dashboards/grafana-dashboard.yaml | 6 + .../datasources/grafana-datasource.yaml | 16 + .../notifiers/grafana-notifier.yaml | 15 + .../monitoring/prometheus/prometheus.yml | 7 + .../dashboard/grafana/beefy-dashboard.json | 500 + .../dashboard/prometheus/millau-targets.yml | 6 + .../dashboard/prometheus/rialto-targets.yml | 6 + .../rialto-chainspec-exporter-entrypoint.sh | 16 + .../rialto-parachain-registrar-entrypoint.sh | 11 + deployments/networks/millau.yml | 113 + deployments/networks/rialto-parachain.yml | 90 + deployments/networks/rialto.yml | 130 + deployments/reverse-proxy/README.md | 15 + deployments/reverse-proxy/docker-compose.yml | 45 + deployments/run.sh | 217 + deployments/types-millau.json | 191 + deployments/types-rialto.json | 191 + deployments/types/build.sh | 20 + deployments/types/common.json | 123 + deployments/types/millau.json | 17 + deployments/types/rialto-millau.json | 56 + deployments/types/rialto.json | 17 + deployments/ui/README.md | 23 + deployments/ui/docker-compose.yml | 14 + docs/complex-relay.html | 85 + docs/dockerhub-bridges-common-relay.README.md | 1 + docs/dockerhub-millau-bridge-node.README.md | 1 + docs/dockerhub-rialto-bridge-node.README.md | 1 + ...kerhub-rialto-parachain-collator.README.md | 1 + docs/dockerhub-substrate-relay.README.md | 1 + docs/grandpa-finality-relay.html | 47 + docs/high-level-overview.md | 181 + docs/messages-relay.html | 78 + docs/parachains-finality-relay.html | 55 + docs/polkadot-kusama-bridge-overview.md | 132 + docs/polkadot-kusama-bridge.html | 67 + fuzz/storage-proof/Cargo.lock | 2796 +++ fuzz/storage-proof/Cargo.toml | 25 + fuzz/storage-proof/README.md | 34 + fuzz/storage-proof/src/main.rs | 78 + modules/beefy/Cargo.toml | 54 + modules/beefy/src/lib.rs | 648 + modules/beefy/src/mock.rs | 224 + modules/beefy/src/mock_chain.rs | 299 + modules/beefy/src/utils.rs | 361 + modules/grandpa/Cargo.toml | 63 + modules/grandpa/README.md | 101 + modules/grandpa/src/benchmarking.rs | 141 + modules/grandpa/src/call_ext.rs | 311 + modules/grandpa/src/lib.rs | 1423 ++ modules/grandpa/src/mock.rs | 151 + modules/grandpa/src/storage_types.rs | 136 + modules/grandpa/src/weights.rs | 167 + modules/messages/Cargo.toml | 56 + modules/messages/README.md | 242 + modules/messages/src/benchmarking.rs | 460 + modules/messages/src/inbound_lane.rs | 550 + modules/messages/src/lib.rs | 2176 +++ modules/messages/src/mock.rs | 498 + modules/messages/src/outbound_lane.rs | 436 + modules/messages/src/weights.rs | 525 + modules/messages/src/weights_ext.rs | 444 + modules/parachains/Cargo.toml | 60 + modules/parachains/README.md | 90 + modules/parachains/src/benchmarking.rs | 116 + modules/parachains/src/call_ext.rs | 243 + modules/parachains/src/lib.rs | 1609 ++ modules/parachains/src/mock.rs | 349 + modules/parachains/src/weights.rs | 273 + modules/parachains/src/weights_ext.rs | 107 + modules/relayers/Cargo.toml | 59 + modules/relayers/README.md | 14 + modules/relayers/src/benchmarking.rs | 59 + modules/relayers/src/lib.rs | 309 + modules/relayers/src/mock.rs | 151 + modules/relayers/src/payment_adapter.rs | 158 + modules/relayers/src/weights.rs | 101 + modules/shift-session-manager/Cargo.toml | 39 + modules/shift-session-manager/README.md | 10 + modules/shift-session-manager/src/lib.rs | 266 + primitives/beefy/Cargo.toml | 41 + primitives/beefy/src/lib.rs | 149 + .../chain-bridge-hub-cumulus/Cargo.toml | 37 + .../chain-bridge-hub-cumulus/src/lib.rs | 216 + primitives/chain-bridge-hub-kusama/Cargo.toml | 31 + primitives/chain-bridge-hub-kusama/src/lib.rs | 84 + .../chain-bridge-hub-polkadot/Cargo.toml | 32 + .../chain-bridge-hub-polkadot/src/lib.rs | 75 + primitives/chain-bridge-hub-rococo/Cargo.toml | 31 + primitives/chain-bridge-hub-rococo/src/lib.rs | 82 + primitives/chain-bridge-hub-wococo/Cargo.toml | 32 + primitives/chain-bridge-hub-wococo/src/lib.rs | 72 + primitives/chain-kusama/Cargo.toml | 30 + primitives/chain-kusama/src/lib.rs | 65 + primitives/chain-millau/Cargo.toml | 59 + primitives/chain-millau/src/lib.rs | 249 + primitives/chain-millau/src/millau_hash.rs | 58 + primitives/chain-polkadot/Cargo.toml | 30 + primitives/chain-polkadot/src/lib.rs | 65 + primitives/chain-rialto-parachain/Cargo.toml | 39 + primitives/chain-rialto-parachain/src/lib.rs | 152 + primitives/chain-rialto/Cargo.toml | 38 + primitives/chain-rialto/src/lib.rs | 214 + primitives/chain-rococo/Cargo.toml | 30 + primitives/chain-rococo/src/lib.rs | 76 + primitives/chain-westend/Cargo.toml | 30 + primitives/chain-westend/src/lib.rs | 108 + primitives/chain-wococo/Cargo.toml | 32 + primitives/chain-wococo/src/lib.rs | 65 + primitives/header-chain/Cargo.toml | 45 + primitives/header-chain/src/justification.rs | 390 + primitives/header-chain/src/lib.rs | 227 + primitives/header-chain/src/storage_keys.rs | 104 + .../tests/implementation_match.rs | 418 + .../header-chain/tests/justification.rs | 280 + primitives/messages/Cargo.toml | 38 + primitives/messages/src/lib.rs | 471 + primitives/messages/src/source_chain.rs | 211 + primitives/messages/src/storage_keys.rs | 128 + primitives/messages/src/target_chain.rs | 205 + primitives/parachains/Cargo.toml | 39 + primitives/parachains/src/lib.rs | 180 + primitives/polkadot-core/Cargo.toml | 45 + primitives/polkadot-core/src/lib.rs | 292 + primitives/polkadot-core/src/parachains.rs | 98 + primitives/relayers/Cargo.toml | 36 + primitives/relayers/src/lib.rs | 202 + primitives/runtime/Cargo.toml | 49 + primitives/runtime/src/chain.rs | 375 + primitives/runtime/src/extensions.rs | 144 + primitives/runtime/src/lib.rs | 573 + primitives/runtime/src/messages.rs | 35 + primitives/runtime/src/storage_proof.rs | 272 + primitives/runtime/src/storage_types.rs | 90 + primitives/test-utils/Cargo.toml | 29 + primitives/test-utils/src/keyring.rs | 94 + primitives/test-utils/src/lib.rs | 302 + relays/bin-substrate/Cargo.toml | 73 + ..._kusama_messages_to_bridge_hub_polkadot.rs | 65 + ..._polkadot_messages_to_bridge_hub_kusama.rs | 65 + .../kusama_headers_to_bridge_hub_polkadot.rs | 72 + ...usama_parachains_to_bridge_hub_polkadot.rs | 75 + .../src/bridges/kusama_polkadot/mod.rs | 24 + .../polkadot_headers_to_bridge_hub_kusama.rs | 72 + ...olkadot_parachains_to_bridge_hub_kusama.rs | 75 + relays/bin-substrate/src/bridges/mod.rs | 23 + .../rialto_millau/millau_headers_to_rialto.rs | 56 + .../millau_messages_to_rialto.rs | 47 + .../src/bridges/rialto_millau/mod.rs | 22 + .../rialto_millau/rialto_headers_to_millau.rs | 56 + .../rialto_messages_to_millau.rs | 47 + .../millau_headers_to_rialto_parachain.rs | 76 + .../millau_messages_to_rialto_parachain.rs | 51 + .../bridges/rialto_parachain_millau/mod.rs | 22 + .../rialto_parachain_messages_to_millau.rs | 51 + .../rialto_parachains_to_millau.rs | 65 + ...ub_rococo_messages_to_bridge_hub_wococo.rs | 64 + ...ub_wococo_messages_to_bridge_hub_rococo.rs | 64 + .../src/bridges/rococo_wococo/mod.rs | 24 + .../rococo_headers_to_bridge_hub_wococo.rs | 72 + .../rococo_parachains_to_bridge_hub_wococo.rs | 75 + .../wococo_headers_to_bridge_hub_rococo.rs | 72 + .../wococo_parachains_to_bridge_hub_rococo.rs | 75 + .../src/bridges/westend_millau/mod.rs | 20 + .../westend_headers_to_millau.rs | 51 + .../westend_parachains_to_millau.rs | 59 + relays/bin-substrate/src/chains/kusama.rs | 32 + relays/bin-substrate/src/chains/millau.rs | 39 + relays/bin-substrate/src/chains/mod.rs | 110 + relays/bin-substrate/src/chains/polkadot.rs | 32 + relays/bin-substrate/src/chains/rialto.rs | 39 + .../src/chains/rialto_parachain.rs | 42 + relays/bin-substrate/src/chains/rococo.rs | 31 + relays/bin-substrate/src/chains/westend.rs | 29 + relays/bin-substrate/src/chains/wococo.rs | 31 + relays/bin-substrate/src/cli/bridge.rs | 87 + relays/bin-substrate/src/cli/chain_schema.rs | 370 + .../bin-substrate/src/cli/encode_message.rs | 151 + relays/bin-substrate/src/cli/init_bridge.rs | 221 + relays/bin-substrate/src/cli/mod.rs | 355 + .../src/cli/register_parachain.rs | 324 + relays/bin-substrate/src/cli/relay_headers.rs | 145 + .../src/cli/relay_headers_and_messages/mod.rs | 715 + .../parachain_to_parachain.rs | 275 + .../relay_to_parachain.rs | 248 + .../relay_to_relay.rs | 189 + .../bin-substrate/src/cli/relay_messages.rs | 133 + .../bin-substrate/src/cli/relay_parachains.rs | 143 + .../src/cli/resubmit_transactions.rs | 560 + relays/bin-substrate/src/cli/send_message.rs | 187 + relays/bin-substrate/src/main.rs | 29 + relays/client-bridge-hub-kusama/Cargo.toml | 28 + relays/client-bridge-hub-kusama/src/lib.rs | 162 + .../src/runtime_wrapper.rs | 74 + relays/client-bridge-hub-polkadot/Cargo.toml | 32 + relays/client-bridge-hub-polkadot/src/lib.rs | 160 + .../src/runtime_wrapper.rs | 119 + relays/client-bridge-hub-rococo/Cargo.toml | 28 + relays/client-bridge-hub-rococo/src/lib.rs | 162 + .../src/runtime_wrapper.rs | 69 + relays/client-bridge-hub-wococo/Cargo.toml | 32 + relays/client-bridge-hub-wococo/src/lib.rs | 160 + .../src/runtime_wrapper.rs | 115 + relays/client-kusama/Cargo.toml | 19 + relays/client-kusama/src/lib.rs | 60 + relays/client-millau/Cargo.toml | 26 + relays/client-millau/src/lib.rs | 188 + relays/client-polkadot/Cargo.toml | 19 + relays/client-polkadot/src/lib.rs | 60 + relays/client-rialto-parachain/Cargo.toml | 30 + .../src/codegen_runtime.rs | 10210 ++++++++++ relays/client-rialto-parachain/src/lib.rs | 138 + relays/client-rialto/Cargo.toml | 26 + relays/client-rialto/src/lib.rs | 184 + relays/client-rococo/Cargo.toml | 19 + relays/client-rococo/src/lib.rs | 58 + relays/client-substrate/Cargo.toml | 55 + relays/client-substrate/src/calls.rs | 59 + relays/client-substrate/src/chain.rs | 294 + relays/client-substrate/src/client.rs | 813 + relays/client-substrate/src/error.rs | 155 + relays/client-substrate/src/guard.rs | 373 + relays/client-substrate/src/lib.rs | 94 + .../src/metrics/float_storage_value.rs | 133 + relays/client-substrate/src/metrics/mod.rs | 21 + relays/client-substrate/src/rpc.rs | 170 + relays/client-substrate/src/sync_header.rs | 61 + relays/client-substrate/src/test_chain.rs | 117 + .../src/transaction_tracker.rs | 447 + relays/client-westend/Cargo.toml | 19 + relays/client-westend/src/lib.rs | 80 + relays/client-wococo/Cargo.toml | 18 + relays/client-wococo/src/lib.rs | 58 + relays/finality/Cargo.toml | 20 + relays/finality/README.md | 58 + relays/finality/src/finality_loop.rs | 761 + relays/finality/src/finality_loop_tests.rs | 598 + relays/finality/src/lib.rs | 61 + relays/finality/src/sync_loop_metrics.rs | 95 + relays/lib-substrate-relay/Cargo.toml | 59 + relays/lib-substrate-relay/src/error.rs | 63 + .../src/finality/engine.rs | 308 + .../src/finality/guards.rs | 48 + .../src/finality/initialize.rs | 163 + .../lib-substrate-relay/src/finality/mod.rs | 209 + .../src/finality/source.rs | 298 + .../src/finality/target.rs | 131 + relays/lib-substrate-relay/src/lib.rs | 145 + .../lib-substrate-relay/src/messages_lane.rs | 572 + .../src/messages_metrics.rs | 190 + .../src/messages_source.rs | 721 + .../src/messages_target.rs | 311 + .../src/on_demand/headers.rs | 511 + .../lib-substrate-relay/src/on_demand/mod.rs | 48 + .../src/on_demand/parachains.rs | 1033 ++ .../lib-substrate-relay/src/parachains/mod.rs | 108 + .../src/parachains/source.rs | 169 + .../src/parachains/target.rs | 148 + relays/messages/Cargo.toml | 23 + relays/messages/src/lib.rs | 37 + relays/messages/src/message_lane.rs | 71 + relays/messages/src/message_lane_loop.rs | 1261 ++ relays/messages/src/message_race_delivery.rs | 1162 ++ relays/messages/src/message_race_limits.rs | 206 + relays/messages/src/message_race_loop.rs | 816 + relays/messages/src/message_race_receiving.rs | 235 + relays/messages/src/message_race_strategy.rs | 596 + relays/messages/src/metrics.rs | 148 + relays/parachains/Cargo.toml | 23 + relays/parachains/README.md | 49 + relays/parachains/src/lib.rs | 32 + relays/parachains/src/parachains_loop.rs | 953 + .../parachains/src/parachains_loop_metrics.rs | 86 + relays/utils/Cargo.toml | 33 + relays/utils/src/error.rs | 46 + relays/utils/src/initialize.rs | 136 + relays/utils/src/lib.rs | 313 + relays/utils/src/metrics.rs | 192 + relays/utils/src/metrics/float_json_value.rs | 147 + relays/utils/src/metrics/global.rs | 118 + relays/utils/src/relay_loop.rs | 269 + rustfmt.toml | 24 + scripts/add_license.sh | 22 + scripts/build-containers.sh | 7 + scripts/ci-cache.sh | 19 + scripts/dump-logs.sh | 44 + scripts/license_header | 16 + scripts/send-message-from-millau-rialto.sh | 18 + scripts/send-message-from-rialto-millau.sh | 18 + scripts/update-weights-setup.sh | 33 + scripts/update-weights.sh | 55 + scripts/update_substrate.sh | 10 + scripts/verify-pallets-build.sh | 133 + tools/runtime-codegen/Cargo.lock | 4508 +++++ tools/runtime-codegen/Cargo.toml | 20 + tools/runtime-codegen/README.md | 11 + tools/runtime-codegen/src/main.rs | 173 + 413 files changed, 110220 insertions(+) create mode 100644 .config/lingua.dic create mode 100644 .config/spellcheck.toml create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .github/dependabot.yml create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 .maintain/millau-weight-template.hbs create mode 100644 CODEOWNERS create mode 100644 CODE_OF_CONDUCT.md create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 SECURITY.md create mode 100644 bin/.keep create mode 100644 bin/millau/node/Cargo.toml create mode 100644 bin/millau/node/build.rs create mode 100644 bin/millau/node/src/chain_spec.rs create mode 100644 bin/millau/node/src/cli.rs create mode 100644 bin/millau/node/src/command.rs create mode 100644 bin/millau/node/src/lib.rs create mode 100644 bin/millau/node/src/main.rs create mode 100644 bin/millau/node/src/service.rs create mode 100644 bin/millau/runtime/Cargo.toml create mode 100644 bin/millau/runtime/build.rs create mode 100644 bin/millau/runtime/src/lib.rs create mode 100644 bin/millau/runtime/src/rialto_messages.rs create mode 100644 bin/millau/runtime/src/rialto_parachain_messages.rs create mode 100644 bin/millau/runtime/src/xcm_config.rs create mode 100644 bin/rialto-parachain/node/Cargo.toml create mode 100644 bin/rialto-parachain/node/build.rs create mode 100644 bin/rialto-parachain/node/src/chain_spec.rs create mode 100644 bin/rialto-parachain/node/src/cli.rs create mode 100644 bin/rialto-parachain/node/src/command.rs create mode 100644 bin/rialto-parachain/node/src/lib.rs create mode 100644 bin/rialto-parachain/node/src/main.rs create mode 100644 bin/rialto-parachain/node/src/service.rs create mode 100644 bin/rialto-parachain/runtime/Cargo.toml create mode 100644 bin/rialto-parachain/runtime/build.rs create mode 100644 bin/rialto-parachain/runtime/src/lib.rs create mode 100644 bin/rialto-parachain/runtime/src/millau_messages.rs create mode 100644 bin/rialto/node/Cargo.toml create mode 100644 bin/rialto/node/build.rs create mode 100644 bin/rialto/node/src/chain_spec.rs create mode 100644 bin/rialto/node/src/cli.rs create mode 100644 bin/rialto/node/src/command.rs create mode 100644 bin/rialto/node/src/main.rs create mode 100644 bin/rialto/runtime/Cargo.toml create mode 100644 bin/rialto/runtime/build.rs create mode 100644 bin/rialto/runtime/src/lib.rs create mode 100644 bin/rialto/runtime/src/millau_messages.rs create mode 100644 bin/rialto/runtime/src/parachains.rs create mode 100644 bin/rialto/runtime/src/xcm_config.rs create mode 100644 bin/runtime-common/Cargo.toml create mode 100644 bin/runtime-common/src/integrity.rs create mode 100644 bin/runtime-common/src/lib.rs create mode 100644 bin/runtime-common/src/messages.rs create mode 100644 bin/runtime-common/src/messages_api.rs create mode 100644 bin/runtime-common/src/messages_benchmarking.rs create mode 100644 bin/runtime-common/src/messages_call_ext.rs create mode 100644 bin/runtime-common/src/messages_generation.rs create mode 100644 bin/runtime-common/src/messages_xcm_extension.rs create mode 100644 bin/runtime-common/src/mock.rs create mode 100644 bin/runtime-common/src/parachains_benchmarking.rs create mode 100644 bin/runtime-common/src/priority_calculator.rs create mode 100644 bin/runtime-common/src/refund_relayer_extension.rs create mode 100644 ci.Dockerfile create mode 100644 deny.toml create mode 100644 deployments/README.md create mode 100755 deployments/bridges/common/generate_messages.sh create mode 100644 deployments/bridges/rialto-millau/dashboard/grafana/relay-millau-to-rialto-messages-dashboard.json create mode 100644 deployments/bridges/rialto-millau/dashboard/grafana/relay-rialto-to-millau-messages-dashboard.json create mode 100644 deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json create mode 100644 deployments/bridges/rialto-millau/dashboard/prometheus/targets.yml create mode 100644 deployments/bridges/rialto-millau/docker-compose.yml create mode 100755 deployments/bridges/rialto-millau/entrypoints/relay-messages-millau-to-rialto-entrypoint.sh create mode 100755 deployments/bridges/rialto-millau/entrypoints/relay-messages-rialto-to-millau-entrypoint.sh create mode 100755 deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh create mode 100755 deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh create mode 100755 deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-resubmitter-entrypoint.sh create mode 100755 deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh create mode 100644 deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-millau-to-rialto-parachain-messages-dashboard.json create mode 100644 deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-rialto-parachain-to-millau-messages-dashboard.json create mode 100644 deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json create mode 100644 deployments/bridges/rialto-parachain-millau/dashboard/prometheus/targets.yml create mode 100644 deployments/bridges/rialto-parachain-millau/docker-compose.yml create mode 100755 deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh create mode 100755 deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-generator-entrypoint.sh create mode 100755 deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-resubmitter-entrypoint.sh create mode 100755 deployments/bridges/rialto-parachain-millau/entrypoints/relay-millau-rialto-parachain-entrypoint.sh create mode 100644 deployments/bridges/rococo-wococo/README.md create mode 100644 deployments/bridges/rococo-wococo/dashboard/grafana/bridges-alerts.json create mode 100644 deployments/bridges/rococo-wococo/dashboard/grafana/relay-rococo-to-wococo-messages-dashboard.json create mode 100644 deployments/bridges/rococo-wococo/dashboard/grafana/relay-wococo-to-rococo-messages-dashboard.json create mode 100644 deployments/bridges/rococo-wococo/dashboard/grafana/rococo-wococo-maintenance-dashboard.json create mode 100644 deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-headers-dashboard.json create mode 100644 deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-parachains-dashboard.json create mode 100644 deployments/bridges/westend-millau/dashboard/prometheus/targets.yml create mode 100644 deployments/bridges/westend-millau/docker-compose.yml create mode 100755 deployments/bridges/westend-millau/entrypoints/relay-headers-westend-to-millau-entrypoint.sh create mode 100755 deployments/bridges/westend-millau/entrypoints/relay-parachains-westend-to-millau-entrypoint.sh create mode 100755 deployments/local-scripts/bridge-entrypoint.sh create mode 100755 deployments/local-scripts/relay-messages-millau-to-rialto.sh create mode 100755 deployments/local-scripts/relay-messages-rialto-to-millau.sh create mode 100755 deployments/local-scripts/relay-millau-to-rialto.sh create mode 100755 deployments/local-scripts/relay-rialto-to-millau.sh create mode 100755 deployments/local-scripts/run-millau-node.sh create mode 100755 deployments/local-scripts/run-rialto-node.sh create mode 100755 deployments/local-scripts/run-westend-node.sh create mode 100644 deployments/monitoring/GrafanaMatrix.Dockerfile create mode 100644 deployments/monitoring/disabled.yml create mode 100644 deployments/monitoring/docker-compose.yml create mode 100644 deployments/monitoring/grafana-matrix/config.yml create mode 100644 deployments/monitoring/grafana/dashboards/nodes.json create mode 100644 deployments/monitoring/grafana/provisioning/dashboards/grafana-dashboard.yaml create mode 100644 deployments/monitoring/grafana/provisioning/datasources/grafana-datasource.yaml create mode 100644 deployments/monitoring/grafana/provisioning/notifiers/grafana-notifier.yaml create mode 100644 deployments/monitoring/prometheus/prometheus.yml create mode 100644 deployments/networks/dashboard/grafana/beefy-dashboard.json create mode 100644 deployments/networks/dashboard/prometheus/millau-targets.yml create mode 100644 deployments/networks/dashboard/prometheus/rialto-targets.yml create mode 100755 deployments/networks/entrypoints/rialto-chainspec-exporter-entrypoint.sh create mode 100755 deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh create mode 100644 deployments/networks/millau.yml create mode 100644 deployments/networks/rialto-parachain.yml create mode 100644 deployments/networks/rialto.yml create mode 100644 deployments/reverse-proxy/README.md create mode 100644 deployments/reverse-proxy/docker-compose.yml create mode 100755 deployments/run.sh create mode 100644 deployments/types-millau.json create mode 100644 deployments/types-rialto.json create mode 100755 deployments/types/build.sh create mode 100644 deployments/types/common.json create mode 100644 deployments/types/millau.json create mode 100644 deployments/types/rialto-millau.json create mode 100644 deployments/types/rialto.json create mode 100644 deployments/ui/README.md create mode 100644 deployments/ui/docker-compose.yml create mode 100644 docs/complex-relay.html create mode 100644 docs/dockerhub-bridges-common-relay.README.md create mode 100644 docs/dockerhub-millau-bridge-node.README.md create mode 100644 docs/dockerhub-rialto-bridge-node.README.md create mode 100644 docs/dockerhub-rialto-parachain-collator.README.md create mode 100644 docs/dockerhub-substrate-relay.README.md create mode 100644 docs/grandpa-finality-relay.html create mode 100644 docs/high-level-overview.md create mode 100644 docs/messages-relay.html create mode 100644 docs/parachains-finality-relay.html create mode 100644 docs/polkadot-kusama-bridge-overview.md create mode 100644 docs/polkadot-kusama-bridge.html create mode 100644 fuzz/storage-proof/Cargo.lock create mode 100644 fuzz/storage-proof/Cargo.toml create mode 100644 fuzz/storage-proof/README.md create mode 100644 fuzz/storage-proof/src/main.rs create mode 100644 modules/beefy/Cargo.toml create mode 100644 modules/beefy/src/lib.rs create mode 100644 modules/beefy/src/mock.rs create mode 100644 modules/beefy/src/mock_chain.rs create mode 100644 modules/beefy/src/utils.rs create mode 100644 modules/grandpa/Cargo.toml create mode 100644 modules/grandpa/README.md create mode 100644 modules/grandpa/src/benchmarking.rs create mode 100644 modules/grandpa/src/call_ext.rs create mode 100644 modules/grandpa/src/lib.rs create mode 100644 modules/grandpa/src/mock.rs create mode 100644 modules/grandpa/src/storage_types.rs create mode 100644 modules/grandpa/src/weights.rs create mode 100644 modules/messages/Cargo.toml create mode 100644 modules/messages/README.md create mode 100644 modules/messages/src/benchmarking.rs create mode 100644 modules/messages/src/inbound_lane.rs create mode 100644 modules/messages/src/lib.rs create mode 100644 modules/messages/src/mock.rs create mode 100644 modules/messages/src/outbound_lane.rs create mode 100644 modules/messages/src/weights.rs create mode 100644 modules/messages/src/weights_ext.rs create mode 100644 modules/parachains/Cargo.toml create mode 100644 modules/parachains/README.md create mode 100644 modules/parachains/src/benchmarking.rs create mode 100644 modules/parachains/src/call_ext.rs create mode 100644 modules/parachains/src/lib.rs create mode 100644 modules/parachains/src/mock.rs create mode 100644 modules/parachains/src/weights.rs create mode 100644 modules/parachains/src/weights_ext.rs create mode 100644 modules/relayers/Cargo.toml create mode 100644 modules/relayers/README.md create mode 100644 modules/relayers/src/benchmarking.rs create mode 100644 modules/relayers/src/lib.rs create mode 100644 modules/relayers/src/mock.rs create mode 100644 modules/relayers/src/payment_adapter.rs create mode 100644 modules/relayers/src/weights.rs create mode 100644 modules/shift-session-manager/Cargo.toml create mode 100644 modules/shift-session-manager/README.md create mode 100644 modules/shift-session-manager/src/lib.rs create mode 100644 primitives/beefy/Cargo.toml create mode 100644 primitives/beefy/src/lib.rs create mode 100644 primitives/chain-bridge-hub-cumulus/Cargo.toml create mode 100644 primitives/chain-bridge-hub-cumulus/src/lib.rs create mode 100644 primitives/chain-bridge-hub-kusama/Cargo.toml create mode 100644 primitives/chain-bridge-hub-kusama/src/lib.rs create mode 100644 primitives/chain-bridge-hub-polkadot/Cargo.toml create mode 100644 primitives/chain-bridge-hub-polkadot/src/lib.rs create mode 100644 primitives/chain-bridge-hub-rococo/Cargo.toml create mode 100644 primitives/chain-bridge-hub-rococo/src/lib.rs create mode 100644 primitives/chain-bridge-hub-wococo/Cargo.toml create mode 100644 primitives/chain-bridge-hub-wococo/src/lib.rs create mode 100644 primitives/chain-kusama/Cargo.toml create mode 100644 primitives/chain-kusama/src/lib.rs create mode 100644 primitives/chain-millau/Cargo.toml create mode 100644 primitives/chain-millau/src/lib.rs create mode 100644 primitives/chain-millau/src/millau_hash.rs create mode 100644 primitives/chain-polkadot/Cargo.toml create mode 100644 primitives/chain-polkadot/src/lib.rs create mode 100644 primitives/chain-rialto-parachain/Cargo.toml create mode 100644 primitives/chain-rialto-parachain/src/lib.rs create mode 100644 primitives/chain-rialto/Cargo.toml create mode 100644 primitives/chain-rialto/src/lib.rs create mode 100644 primitives/chain-rococo/Cargo.toml create mode 100644 primitives/chain-rococo/src/lib.rs create mode 100644 primitives/chain-westend/Cargo.toml create mode 100644 primitives/chain-westend/src/lib.rs create mode 100644 primitives/chain-wococo/Cargo.toml create mode 100644 primitives/chain-wococo/src/lib.rs create mode 100644 primitives/header-chain/Cargo.toml create mode 100644 primitives/header-chain/src/justification.rs create mode 100644 primitives/header-chain/src/lib.rs create mode 100644 primitives/header-chain/src/storage_keys.rs create mode 100644 primitives/header-chain/tests/implementation_match.rs create mode 100644 primitives/header-chain/tests/justification.rs create mode 100644 primitives/messages/Cargo.toml create mode 100644 primitives/messages/src/lib.rs create mode 100644 primitives/messages/src/source_chain.rs create mode 100644 primitives/messages/src/storage_keys.rs create mode 100644 primitives/messages/src/target_chain.rs create mode 100644 primitives/parachains/Cargo.toml create mode 100644 primitives/parachains/src/lib.rs create mode 100644 primitives/polkadot-core/Cargo.toml create mode 100644 primitives/polkadot-core/src/lib.rs create mode 100644 primitives/polkadot-core/src/parachains.rs create mode 100644 primitives/relayers/Cargo.toml create mode 100644 primitives/relayers/src/lib.rs create mode 100644 primitives/runtime/Cargo.toml create mode 100644 primitives/runtime/src/chain.rs create mode 100644 primitives/runtime/src/extensions.rs create mode 100644 primitives/runtime/src/lib.rs create mode 100644 primitives/runtime/src/messages.rs create mode 100644 primitives/runtime/src/storage_proof.rs create mode 100644 primitives/runtime/src/storage_types.rs create mode 100644 primitives/test-utils/Cargo.toml create mode 100644 primitives/test-utils/src/keyring.rs create mode 100644 primitives/test-utils/src/lib.rs create mode 100644 relays/bin-substrate/Cargo.toml create mode 100644 relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_kusama_messages_to_bridge_hub_polkadot.rs create mode 100644 relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_polkadot_messages_to_bridge_hub_kusama.rs create mode 100644 relays/bin-substrate/src/bridges/kusama_polkadot/kusama_headers_to_bridge_hub_polkadot.rs create mode 100644 relays/bin-substrate/src/bridges/kusama_polkadot/kusama_parachains_to_bridge_hub_polkadot.rs create mode 100644 relays/bin-substrate/src/bridges/kusama_polkadot/mod.rs create mode 100644 relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_headers_to_bridge_hub_kusama.rs create mode 100644 relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_parachains_to_bridge_hub_kusama.rs create mode 100644 relays/bin-substrate/src/bridges/mod.rs create mode 100644 relays/bin-substrate/src/bridges/rialto_millau/millau_headers_to_rialto.rs create mode 100644 relays/bin-substrate/src/bridges/rialto_millau/millau_messages_to_rialto.rs create mode 100644 relays/bin-substrate/src/bridges/rialto_millau/mod.rs create mode 100644 relays/bin-substrate/src/bridges/rialto_millau/rialto_headers_to_millau.rs create mode 100644 relays/bin-substrate/src/bridges/rialto_millau/rialto_messages_to_millau.rs create mode 100644 relays/bin-substrate/src/bridges/rialto_parachain_millau/millau_headers_to_rialto_parachain.rs create mode 100644 relays/bin-substrate/src/bridges/rialto_parachain_millau/millau_messages_to_rialto_parachain.rs create mode 100644 relays/bin-substrate/src/bridges/rialto_parachain_millau/mod.rs create mode 100644 relays/bin-substrate/src/bridges/rialto_parachain_millau/rialto_parachain_messages_to_millau.rs create mode 100644 relays/bin-substrate/src/bridges/rialto_parachain_millau/rialto_parachains_to_millau.rs create mode 100644 relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs create mode 100644 relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs create mode 100644 relays/bin-substrate/src/bridges/rococo_wococo/mod.rs create mode 100644 relays/bin-substrate/src/bridges/rococo_wococo/rococo_headers_to_bridge_hub_wococo.rs create mode 100644 relays/bin-substrate/src/bridges/rococo_wococo/rococo_parachains_to_bridge_hub_wococo.rs create mode 100644 relays/bin-substrate/src/bridges/rococo_wococo/wococo_headers_to_bridge_hub_rococo.rs create mode 100644 relays/bin-substrate/src/bridges/rococo_wococo/wococo_parachains_to_bridge_hub_rococo.rs create mode 100644 relays/bin-substrate/src/bridges/westend_millau/mod.rs create mode 100644 relays/bin-substrate/src/bridges/westend_millau/westend_headers_to_millau.rs create mode 100644 relays/bin-substrate/src/bridges/westend_millau/westend_parachains_to_millau.rs create mode 100644 relays/bin-substrate/src/chains/kusama.rs create mode 100644 relays/bin-substrate/src/chains/millau.rs create mode 100644 relays/bin-substrate/src/chains/mod.rs create mode 100644 relays/bin-substrate/src/chains/polkadot.rs create mode 100644 relays/bin-substrate/src/chains/rialto.rs create mode 100644 relays/bin-substrate/src/chains/rialto_parachain.rs create mode 100644 relays/bin-substrate/src/chains/rococo.rs create mode 100644 relays/bin-substrate/src/chains/westend.rs create mode 100644 relays/bin-substrate/src/chains/wococo.rs create mode 100644 relays/bin-substrate/src/cli/bridge.rs create mode 100644 relays/bin-substrate/src/cli/chain_schema.rs create mode 100644 relays/bin-substrate/src/cli/encode_message.rs create mode 100644 relays/bin-substrate/src/cli/init_bridge.rs create mode 100644 relays/bin-substrate/src/cli/mod.rs create mode 100644 relays/bin-substrate/src/cli/register_parachain.rs create mode 100644 relays/bin-substrate/src/cli/relay_headers.rs create mode 100644 relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs create mode 100644 relays/bin-substrate/src/cli/relay_headers_and_messages/parachain_to_parachain.rs create mode 100644 relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs create mode 100644 relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs create mode 100644 relays/bin-substrate/src/cli/relay_messages.rs create mode 100644 relays/bin-substrate/src/cli/relay_parachains.rs create mode 100644 relays/bin-substrate/src/cli/resubmit_transactions.rs create mode 100644 relays/bin-substrate/src/cli/send_message.rs create mode 100644 relays/bin-substrate/src/main.rs create mode 100644 relays/client-bridge-hub-kusama/Cargo.toml create mode 100644 relays/client-bridge-hub-kusama/src/lib.rs create mode 100644 relays/client-bridge-hub-kusama/src/runtime_wrapper.rs create mode 100644 relays/client-bridge-hub-polkadot/Cargo.toml create mode 100644 relays/client-bridge-hub-polkadot/src/lib.rs create mode 100644 relays/client-bridge-hub-polkadot/src/runtime_wrapper.rs create mode 100644 relays/client-bridge-hub-rococo/Cargo.toml create mode 100644 relays/client-bridge-hub-rococo/src/lib.rs create mode 100644 relays/client-bridge-hub-rococo/src/runtime_wrapper.rs create mode 100644 relays/client-bridge-hub-wococo/Cargo.toml create mode 100644 relays/client-bridge-hub-wococo/src/lib.rs create mode 100644 relays/client-bridge-hub-wococo/src/runtime_wrapper.rs create mode 100644 relays/client-kusama/Cargo.toml create mode 100644 relays/client-kusama/src/lib.rs create mode 100644 relays/client-millau/Cargo.toml create mode 100644 relays/client-millau/src/lib.rs create mode 100644 relays/client-polkadot/Cargo.toml create mode 100644 relays/client-polkadot/src/lib.rs create mode 100644 relays/client-rialto-parachain/Cargo.toml create mode 100644 relays/client-rialto-parachain/src/codegen_runtime.rs create mode 100644 relays/client-rialto-parachain/src/lib.rs create mode 100644 relays/client-rialto/Cargo.toml create mode 100644 relays/client-rialto/src/lib.rs create mode 100644 relays/client-rococo/Cargo.toml create mode 100644 relays/client-rococo/src/lib.rs create mode 100644 relays/client-substrate/Cargo.toml create mode 100644 relays/client-substrate/src/calls.rs create mode 100644 relays/client-substrate/src/chain.rs create mode 100644 relays/client-substrate/src/client.rs create mode 100644 relays/client-substrate/src/error.rs create mode 100644 relays/client-substrate/src/guard.rs create mode 100644 relays/client-substrate/src/lib.rs create mode 100644 relays/client-substrate/src/metrics/float_storage_value.rs create mode 100644 relays/client-substrate/src/metrics/mod.rs create mode 100644 relays/client-substrate/src/rpc.rs create mode 100644 relays/client-substrate/src/sync_header.rs create mode 100644 relays/client-substrate/src/test_chain.rs create mode 100644 relays/client-substrate/src/transaction_tracker.rs create mode 100644 relays/client-westend/Cargo.toml create mode 100644 relays/client-westend/src/lib.rs create mode 100644 relays/client-wococo/Cargo.toml create mode 100644 relays/client-wococo/src/lib.rs create mode 100644 relays/finality/Cargo.toml create mode 100644 relays/finality/README.md create mode 100644 relays/finality/src/finality_loop.rs create mode 100644 relays/finality/src/finality_loop_tests.rs create mode 100644 relays/finality/src/lib.rs create mode 100644 relays/finality/src/sync_loop_metrics.rs create mode 100644 relays/lib-substrate-relay/Cargo.toml create mode 100644 relays/lib-substrate-relay/src/error.rs create mode 100644 relays/lib-substrate-relay/src/finality/engine.rs create mode 100644 relays/lib-substrate-relay/src/finality/guards.rs create mode 100644 relays/lib-substrate-relay/src/finality/initialize.rs create mode 100644 relays/lib-substrate-relay/src/finality/mod.rs create mode 100644 relays/lib-substrate-relay/src/finality/source.rs create mode 100644 relays/lib-substrate-relay/src/finality/target.rs create mode 100644 relays/lib-substrate-relay/src/lib.rs create mode 100644 relays/lib-substrate-relay/src/messages_lane.rs create mode 100644 relays/lib-substrate-relay/src/messages_metrics.rs create mode 100644 relays/lib-substrate-relay/src/messages_source.rs create mode 100644 relays/lib-substrate-relay/src/messages_target.rs create mode 100644 relays/lib-substrate-relay/src/on_demand/headers.rs create mode 100644 relays/lib-substrate-relay/src/on_demand/mod.rs create mode 100644 relays/lib-substrate-relay/src/on_demand/parachains.rs create mode 100644 relays/lib-substrate-relay/src/parachains/mod.rs create mode 100644 relays/lib-substrate-relay/src/parachains/source.rs create mode 100644 relays/lib-substrate-relay/src/parachains/target.rs create mode 100644 relays/messages/Cargo.toml create mode 100644 relays/messages/src/lib.rs create mode 100644 relays/messages/src/message_lane.rs create mode 100644 relays/messages/src/message_lane_loop.rs create mode 100644 relays/messages/src/message_race_delivery.rs create mode 100644 relays/messages/src/message_race_limits.rs create mode 100644 relays/messages/src/message_race_loop.rs create mode 100644 relays/messages/src/message_race_receiving.rs create mode 100644 relays/messages/src/message_race_strategy.rs create mode 100644 relays/messages/src/metrics.rs create mode 100644 relays/parachains/Cargo.toml create mode 100644 relays/parachains/README.md create mode 100644 relays/parachains/src/lib.rs create mode 100644 relays/parachains/src/parachains_loop.rs create mode 100644 relays/parachains/src/parachains_loop_metrics.rs create mode 100644 relays/utils/Cargo.toml create mode 100644 relays/utils/src/error.rs create mode 100644 relays/utils/src/initialize.rs create mode 100644 relays/utils/src/lib.rs create mode 100644 relays/utils/src/metrics.rs create mode 100644 relays/utils/src/metrics/float_json_value.rs create mode 100644 relays/utils/src/metrics/global.rs create mode 100644 relays/utils/src/relay_loop.rs create mode 100644 rustfmt.toml create mode 100755 scripts/add_license.sh create mode 100755 scripts/build-containers.sh create mode 100755 scripts/ci-cache.sh create mode 100755 scripts/dump-logs.sh create mode 100644 scripts/license_header create mode 100755 scripts/send-message-from-millau-rialto.sh create mode 100755 scripts/send-message-from-rialto-millau.sh create mode 100644 scripts/update-weights-setup.sh create mode 100755 scripts/update-weights.sh create mode 100755 scripts/update_substrate.sh create mode 100755 scripts/verify-pallets-build.sh create mode 100644 tools/runtime-codegen/Cargo.lock create mode 100644 tools/runtime-codegen/Cargo.toml create mode 100644 tools/runtime-codegen/README.md create mode 100644 tools/runtime-codegen/src/main.rs diff --git a/.config/lingua.dic b/.config/lingua.dic new file mode 100644 index 00000000000..9b622aad77d --- /dev/null +++ b/.config/lingua.dic @@ -0,0 +1,244 @@ +90 + +&& +1KB +1MB +5MB += +API/SM +APIs +AccountId/MS +Apache-2.0/M +Autogenerated +BFT/M +BTC/S +Best/MS +BlockId +BlockNumber +BridgeStorage +BridgeHub +BridgeHubRococo +BridgeHubWococo +BridgeHubKusama +BridgeHubPolkadot +CLI/MS +Chain1 +Chain2 +ChainSpec +ChainTime +DOT/S +ERC-20 +Ethereum +FN +FinalizationError +GPL/M +GPLv3/M +GiB/S +Handler/MS +Hasher +HeaderA +HeaderId +InitiateChange +Instance1 +Instance2 +Instance42 +KSM/S +KYC/M +KeyPair +Kovan +Lane1 +Lane2 +Lane3 +LaneId +MIN_SIZE +MIT/M +MMR +MaxUnrewardedRelayerEntriesAtInboundLane +MaybeExtra +MaybeOrphan +Merklized +MessageNonce +MessageNonces +MessagePayload +MetricsParams +Millau/MS +OldHeader +OutboundMessages +PoA +PoV/MS +Pre +RLP +RPC/MS +Rialto/MS +RialtoParachain/MS +Relayer/MS +Runtime1 +Runtime2 +SIZE_FACTOR +SS58 +SS58Prefix +STALL_SYNC_TIMEOUT +SURI +ServiceFactory/MS +SignedExtension +Stringified +Submitter1 +S|N +TCP +ThisChain +TODO +U256 +Unparsed +Vec +WND/S +Westend/MS +Wococo/MS +XCM/S +XCMP/M +annualised/MS +api/SM +aren +arg +args +async +auth +auths/SM +backoff +benchmarking/MS +best_substrate_header +bitfield/MS +blake2/MS +blockchain/MS +borked +chain_getBlock +choosen +config/MS +crypto/MS +customizable/B +debian/M +decodable/MS +delivery_and_dispatch_fee +dev +dispatchable +dispatchables +doesn +ed25519 +enum/MS +entrypoint/MS +ethereum/MS +externality/MS +extrinsic/MS +extrinsics +fedora/M +functor +fuzzer +hasher +hardcoded +https +implementers +include/BG +inherent/MS +initialize/RG +instantiate/B +intrinsic/MS +invariant/MS +invariants +io +isn +isolate/BG +js +jsonrpsee +keccak +keccak256/M +keyring +keystore/MS +kusama/S +lane +malus +max_value +merkle/MS +metadata +millau +misbehavior/SM +misbehaviors +multivalidator/SM +natively +no_std +nonces +number +ok +oneshot/MS +others' +pallet_bridge_grandpa +pallet_bridge_messages +pallet_message_lane +parablock/MS +parachain/MS +param/MS +parameterize/D +plancks +polkadot/MS +pov-block/MS +precommit +promethius +promethius' +provisioner/MS +probabilistically +prune_depth +prune_end +receival +reconnection +redhat/M +repo/MS +runtime/MS +rustc/MS +relayer/MS +shouldn +source_at_target +source_latest_confirmed +source_latest_generated +sp_consensus_grandpa +spawner +sr25519 +src +stringified +struct/MS +submitters/MS +subsystem/MS +subsystems' +subcommand/MS +synchronizer +target_at_source +target_latest_confirmed +target_latest_received +taskmanager/MS +teleport/RG +teleportation/SM +teleporter/SM +teleporters +testnet/MS +timeframe +tokio +timestamp +trie/MS +trustless/Y +tuple +u32 +ubuntu/M +undeliverable +unfinalized +union/MSG +unpruned +unservable/B +unsynced +updatable +validator/SM +ve +vec +verifier +w3f/MS +wakeup +wasm/M +websocket +x2 +~ diff --git a/.config/spellcheck.toml b/.config/spellcheck.toml new file mode 100644 index 00000000000..e061c29ac22 --- /dev/null +++ b/.config/spellcheck.toml @@ -0,0 +1,13 @@ +[hunspell] +lang = "en_US" +search_dirs = ["."] +extra_dictionaries = ["lingua.dic"] +skip_os_lookups = true +use_builtin = true + +[hunspell.quirks] +# `Type`'s +# 5x +transform_regex = ["^'([^\\s])'$", "^[0-9]+(?:\\.[0-9]*)?x$", "^'s$", "^\\+$", "[><+-]"] +allow_concatenation = true +allow_dashes = true diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000000..f4ceea78560 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +**/target/ diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..e2375881ea0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +root = true +[*] +indent_style=tab +indent_size=tab +tab_width=4 +end_of_line=lf +charset=utf-8 +trim_trailing_whitespace=true +max_line_length=100 +insert_final_newline=true + +[*.{yml,md,yaml,sh}] +indent_style=space +indent_size=2 +tab_width=8 +end_of_line=lf + +[*.md] +max_line_length=80 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..c0c8ea64802 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,62 @@ +version: 2 +updates: +- package-ecosystem: cargo + directory: "/" + schedule: + interval: weekly + time: "03:00" + timezone: Europe/Berlin + open-pull-requests-limit: 20 + ignore: + # Substrate (+ Polkadot/Cumulus pallets) dependencies + - dependency-name: beefy-* + versions: + - ">= 0" + - dependency-name: frame-* + versions: + - ">= 0" + - dependency-name: fork-tree + versions: + - ">= 0" + - dependency-name: mmr-* + versions: + - ">= 0" + - dependency-name: node-inspect + versions: + - ">= 0" + - dependency-name: pallet-* + versions: + - ">= 0" + - dependency-name: sc-* + versions: + - ">= 0" + - dependency-name: sp-* + versions: + - ">= 0" + - dependency-name: substrate-* + versions: + - ">= 0" + - dependency-name: try-runtime-cli + versions: + - ">= 0" + - dependency-name: binary-merkle-tree + versions: + - ">= 0" + # Polkadot dependencies + - dependency-name: kusama-* + versions: + - ">= 0" + - dependency-name: polkadot-* + versions: + - ">= 0" + - dependency-name: xcm* + versions: + - ">= 0" + # Cumulus dependencies + - dependency-name: cumulus-* + versions: + - ">= 0" + - dependency-name: parachain-info + versions: + - ">= 0" + rebase-strategy: disabled diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..5d10cfa41a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +**/target/ +**/.env +**/.env2 +**/rust-toolchain +hfuzz_target +hfuzz_workspace +**/Cargo.lock + +**/*.rs.bk + +*.o +*.so +*.rlib +*.dll +.gdb_history + +*.exe + +.DS_Store + +.cargo +.idea +.vscode +*.iml +*.swp +*.swo diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000000..eb88001b50c --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,412 @@ +stages: + - lint + - check + - test + - build + - publish + - publish-docker-description + - deploy + +variables: + GIT_STRATEGY: fetch + GIT_DEPTH: 100 + CARGO_INCREMENTAL: 0 + ARCH: "x86_64" + CI_IMAGE: "paritytech/bridges-ci:production" + BUILDAH_IMAGE: "quay.io/buildah/stable:v1.27" + RUST_BACKTRACE: full + +default: + cache: {} + interruptible: true + retry: + max: 2 + when: + - runner_system_failure + - unknown_failure + - api_failure + +.collect-artifacts: &collect-artifacts + artifacts: + name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" + when: on_success + expire_in: 7 days + paths: + - artifacts/ + +.kubernetes-build: &kubernetes-build + tags: + - kubernetes-parity-build + +.docker-env: &docker-env + image: "${CI_IMAGE}" + before_script: + - rustup show + - cargo --version + - rustup +nightly show + - cargo +nightly --version + tags: + - linux-docker-vm-c2 + +.test-refs: &test-refs + rules: + # FIXME: This is the cause why pipelines wouldn't start. The problem might be in our custom + # mirroring. This should be investigated further, but for now let's have the working + # pipeline. + # - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH + # changes: + # - '**.md' + # - diagrams/* + # - docs/* + # when: never + - if: $CI_PIPELINE_SOURCE == "pipeline" + - if: $CI_PIPELINE_SOURCE == "web" + - if: $CI_PIPELINE_SOURCE == "schedule" + - if: $CI_COMMIT_REF_NAME == "master" + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + +.build-refs: &build-refs + rules: + # won't run on the CI image update pipeline + - if: $CI_PIPELINE_SOURCE == "pipeline" + when: never + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]{4}-[0-9]{2}-[0-9]{2}.*$/ # i.e. v2021-09-27, v2021-09-27-1 + # there are two types of nightly pipelines: + # 1. this one is triggered by the schedule with $PIPELINE == "nightly", it's for releasing. + # this job runs only on nightly pipeline with the mentioned variable, against `master` branch + - if: $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "nightly" + +.deploy-refs: &deploy-refs + rules: + - if: $CI_PIPELINE_SOURCE == "pipeline" + when: never + - if: $SCHEDULED_JOB + when: never + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + when: manual + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]{4}-[0-9]{2}-[0-9]{2}.*$/ # i.e. v2021-09-27, v2021-09-27-1 + when: manual + +.nightly-test: &nightly-test + rules: + # 2. another is triggered by scripts repo $CI_PIPELINE_SOURCE == "pipeline" it's for the CI image + # update, it also runs all the nightly checks. + - if: $CI_PIPELINE_SOURCE == "pipeline" + +#### stage: lint + +clippy-nightly: + stage: lint + <<: *docker-env + <<: *test-refs + variables: + RUSTFLAGS: "-D warnings" + script: + # clippy currently is raising `derive-partial-eq-without-eq` warning even if `Eq` is actually derived + - SKIP_WASM_BUILD=1 cargo +nightly clippy --all-targets -- -A clippy::redundant_closure -A clippy::derive-partial-eq-without-eq -A clippy::or_fun_call + +fmt: + stage: lint + <<: *docker-env + <<: *test-refs + script: + - cargo +nightly fmt --all -- --check + +spellcheck: + stage: lint + <<: *docker-env + <<: *test-refs + script: + - cargo spellcheck check --cfg=.config/spellcheck.toml --checkers hunspell -m 1 $(find . -type f -name '*.rs' ! -path "./target/*" ! -name 'codegen_runtime.rs' ! -name 'weights.rs') + +#### stage: check + +check: + stage: check + <<: *docker-env + <<: *test-refs + script: &check-script + - SKIP_WASM_BUILD=1 time cargo check --locked --verbose --workspace + # Check Rialto benchmarks runtime + - SKIP_WASM_BUILD=1 time cargo check -p rialto-runtime --locked --features runtime-benchmarks --verbose + # Check Millau benchmarks runtime + - SKIP_WASM_BUILD=1 time cargo check -p millau-runtime --locked --features runtime-benchmarks --verbose + +check-nightly: + stage: check + <<: *docker-env + <<: *nightly-test + script: + - rustup default nightly + - *check-script + +#### stage: test + +test: + stage: test + <<: *docker-env + <<: *test-refs +# variables: +# RUSTFLAGS: "-D warnings" + script: &test-script + - time cargo fetch + # Enable this, when you see: "`cargo metadata` can not fail on project `Cargo.toml`" + #- time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-runtime\").manifest_path"` + #- time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"kusama-runtime\").manifest_path"` + - CARGO_NET_OFFLINE=true SKIP_WASM_BUILD=1 time cargo test --verbose --workspace --features runtime-benchmarks + +test-nightly: + stage: test + <<: *docker-env + <<: *nightly-test + script: + - rustup default nightly + - *test-script + +deny: + stage: test + <<: *docker-env + <<: *nightly-test + <<: *collect-artifacts + script: + - cargo deny check advisories --hide-inclusion-graph + - cargo deny check bans sources --hide-inclusion-graph + after_script: + - mkdir -p ./artifacts + - echo "___Complete logs can be found in the artifacts___" + - cargo deny check advisories 2> advisories.log + - cargo deny check bans sources 2> bans_sources.log + # this job is allowed to fail, only licenses check is important + allow_failure: true + +deny-licenses: + stage: test + <<: *docker-env + <<: *test-refs + <<: *collect-artifacts + script: + - cargo deny check licenses --hide-inclusion-graph + after_script: + - mkdir -p ./artifacts + - echo "___Complete logs can be found in the artifacts___" + - cargo deny check licenses 2> licenses.log + +benchmarks-test: + stage: test + <<: *docker-env + <<: *nightly-test + script: + - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=RialtoMessages --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 + - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=RialtoParachainMessages --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 + - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_grandpa --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 + - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_parachains --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 + - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_relayers --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 + # we may live with failing benchmarks, it is just a signal for us + allow_failure: true + +check-rustdoc: + stage: test + <<: *docker-env + <<: *test-refs + variables: + SKIP_WASM_BUILD: 1 + RUSTDOCFLAGS: "-Dwarnings" + script: + - time cargo +nightly doc --workspace --verbose --no-deps --all-features --exclude relay-rialto-parachain-client + +partial-repo-pallets-build-test: + stage: test + <<: *docker-env + <<: *nightly-test + script: + - ./scripts/verify-pallets-build.sh --no-revert + # we may live with failing partial repo build, it is just a signal for us + allow_failure: true + +#### stage: build + +build: + stage: build + <<: *docker-env + <<: *build-refs + <<: *collect-artifacts + # master + script: &build-script + - time cargo fetch + # Enable this, when you see: "`cargo metadata` can not fail on project `Cargo.toml`" + #- time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-runtime\").manifest_path"` + #- time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"kusama-runtime\").manifest_path"` + - CARGO_NET_OFFLINE=true time cargo build --release --verbose --workspace + after_script: + # Prepare artifacts + - mkdir -p ./artifacts + - strip ./target/release/rialto-bridge-node + - mv -v ./target/release/rialto-bridge-node ./artifacts/ + - strip ./target/release/rialto-parachain-collator + - mv -v ./target/release/rialto-parachain-collator ./artifacts/ + - strip ./target/release/millau-bridge-node + - mv -v ./target/release/millau-bridge-node ./artifacts/ + - strip ./target/release/substrate-relay + - mv -v ./target/release/substrate-relay ./artifacts/ + - mv -v ./deployments/local-scripts/bridge-entrypoint.sh ./artifacts/ + - mv -v ./ci.Dockerfile ./artifacts/ + +build-nightly: + stage: build + <<: *docker-env + <<: *collect-artifacts + <<: *nightly-test + script: + - rustup default nightly + - *build-script + +#### stage: publish + +.build-push-image: &build-push-image + <<: *kubernetes-build + image: $BUILDAH_IMAGE + <<: *build-refs + variables: &image-variables + GIT_STRATEGY: none + DOCKERFILE: ci.Dockerfile + BRIDGES_PROJECT: "${CI_JOB_NAME}" + DOCKER_IMAGE_NAME: "${CI_JOB_NAME}" + IMAGE_NAME: docker.io/paritytech/$DOCKER_IMAGE_NAME + needs: + - job: build + artifacts: true + before_script: + - echo "Starting docker image build/push with name '${IMAGE_NAME}' for '${BRIDGES_PROJECT}' with Dockerfile = '${DOCKERFILE}'" + - if [[ "${CI_COMMIT_TAG}" ]]; then + VERSION=${CI_COMMIT_TAG}; + elif [[ "${CI_COMMIT_REF_NAME}" ]]; then + VERSION=$(echo ${CI_COMMIT_REF_NAME} | sed -r 's#/+#-#g'); + fi + # When building from version tags (v1.0, v2.1rc1, ...) we'll use "production" to tag + # docker image. In all other cases, it'll be "latest". + - if [[ $CI_COMMIT_REF_NAME =~ ^v[0-9]+\.[0-9]+.*$ ]]; then + FLOATING_TAG="production"; + else + FLOATING_TAG="latest"; + fi + - echo "Effective tags = ${VERSION} sha-${CI_COMMIT_SHORT_SHA} ${FLOATING_TAG}" + - echo "Full docker image name = ${IMAGE_NAME}" + script: + - test "${Docker_Hub_User_Parity}" -a "${Docker_Hub_Pass_Parity}" || + ( echo "no docker credentials provided"; exit 1 ) + - cd ./artifacts + - buildah bud + --format=docker + --build-arg VCS_REF="${CI_COMMIT_SHORT_SHA}" + --build-arg BUILD_DATE="$(date +%d-%m-%Y)" + --build-arg PROJECT="${BRIDGES_PROJECT}" + --build-arg VERSION="${VERSION}" + --tag "${IMAGE_NAME}:${VERSION}" + --tag "${IMAGE_NAME}:sha-${CI_COMMIT_SHORT_SHA}" + --tag "${IMAGE_NAME}:${FLOATING_TAG}" + --file "${DOCKERFILE}" . + # The job will success only on the protected branch + - echo "${Docker_Hub_Pass_Parity}" | + buildah login --username "${Docker_Hub_User_Parity}" --password-stdin docker.io + - buildah info + - buildah push --format=v2s2 "${IMAGE_NAME}:${VERSION}" + - buildah push --format=v2s2 "${IMAGE_NAME}:sha-${CI_COMMIT_SHORT_SHA}" + - buildah push --format=v2s2 "${IMAGE_NAME}:${FLOATING_TAG}" + after_script: + - env REGISTRY_AUTH_FILE= buildah logout --all + +rialto-bridge-node: + stage: publish + <<: *build-push-image + +rialto-parachain-collator: + stage: publish + <<: *build-push-image + +millau-bridge-node: + stage: publish + <<: *build-push-image + +substrate-relay: + stage: publish + <<: *build-push-image + +bridges-common-relay: + stage: publish + <<: *build-push-image + variables: + <<: *image-variables + BRIDGES_PROJECT: substrate-relay + DOCKER_IMAGE_NAME: bridges-common-relay + +# Publish Docker images description to hub.docker.com + +.publish-docker-image-description: &publish-docker-image-description + stage: publish-docker-description + image: paritytech/dockerhub-description + variables: + DOCKER_USERNAME: $Docker_Hub_User_Parity + DOCKER_PASSWORD: $Docker_Hub_Pass_Parity + README_FILEPATH: $CI_PROJECT_DIR/docs/${CI_JOB_NAME}.README.md + rules: + - if: $CI_COMMIT_REF_NAME == "master" + changes: + - docs/${CI_JOB_NAME}.README.md + script: + - export DOCKERHUB_REPOSITORY="paritytech/${CI_JOB_NAME:10}" + - cd / && sh entrypoint.sh + tags: + - kubernetes-parity-build + +dockerhub-rialto-bridge-node: + extends: .publish-docker-image-description + variables: + SHORT_DESCRIPTION: "rialto-bridge-node" + +dockerhub-rialto-parachain-collator: + extends: .publish-docker-image-description + variables: + SHORT_DESCRIPTION: "rialto-parachain-collator" + +dockerhub-millau-bridge-node: + extends: .publish-docker-image-description + variables: + SHORT_DESCRIPTION: "millau-bridge-node" + +dockerhub-substrate-relay: + extends: .publish-docker-image-description + variables: + SHORT_DESCRIPTION: "substrate-relay" + +dockerhub-bridges-common-relay: + extends: .publish-docker-image-description + variables: + SHORT_DESCRIPTION: "bridges-common-relay" + +# FIXME: publish binaries + +deploy-bridges-common-relay-testnet: + <<: *deploy-refs + <<: *kubernetes-build + needs: + - job: bridges-common-relay + stage: deploy + image: argoproj/argocd:v2.5.5 + environment: parity-testnet + variables: + ARGOCD_OPTS: --grpc-web --grpc-web-root-path /parity-testnet + APP: bridges-common-relay + before_script: + - if [[ "${CI_COMMIT_TAG}" ]]; then + VERSION=${CI_COMMIT_TAG}; + elif [[ "${CI_COMMIT_REF_NAME}" ]]; then + VERSION=$(echo ${CI_COMMIT_REF_NAME} | sed -r 's#/+#-#g'); + fi + script: + - echo "Starting deploy version=${VERSION}" + - argocd app list + - argocd app set $APP --helm-set bridges-common-relay.image.tag=$VERSION + - argocd app sync $APP + - argocd app wait $APP --timeout 120 diff --git a/.maintain/millau-weight-template.hbs b/.maintain/millau-weight-template.hbs new file mode 100644 index 00000000000..ff70b55aa2c --- /dev/null +++ b/.maintain/millau-weight-template.hbs @@ -0,0 +1,138 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! 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}}` +//! EXECUTION: {{cmd.execution}}, WASM-EXECUTION: {{cmd.wasm_execution}}, CHAIN: {{cmd.chain}}, DB CACHE: {{cmd.db_cache}} + +// Executed Command: +{{#each args as |arg|}} +// {{arg}} +{{/each}} + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// 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}}` that are generated using one of the Bridge testnets. +/// +/// Those weights are test only and must never be used in production. +pub struct BridgeWeight(PhantomData); +impl WeightInfo for BridgeWeight { + {{#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}} nanoseconds. + 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}} nanoseconds. + 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/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000000..3941ba8451a --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,21 @@ +# Lists some code owners. +# +# A codeowner just oversees some part of the codebase. If an owned file is changed then the +# corresponding codeowner receives a review request. An approval of the codeowner might be +# required for merging a PR (depends on repository settings). +# +# For details about syntax, see: +# https://help.github.com/en/articles/about-code-owners +# But here are some important notes: +# +# - Glob syntax is git-like, e.g. `/core` means the core directory in the root, unlike `core` +# which can be everywhere. +# - Multiple owners are supported. +# - Either handle (e.g, @github_user or @github_org/team) or email can be used. Keep in mind, +# that handles might work better because they are more recognizable on GitHub, +# eyou can use them for mentioning unlike an email. +# - The latest matching rule, if multiple, takes precedence. + +# CI +/.github/ @paritytech/ci +/.gitlab-ci.yml @paritytech/ci diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..70541fb72fa --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,80 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers +pledge to making participation in our project and our community a harassment-free experience for +everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity +and expression, level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit + permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +### Facilitation, Not Strongarming + +We recognise that this software is merely a tool for users to create and maintain their blockchain +of preference. We see that blockchains are naturally community platforms with users being the +ultimate decision makers. We assert that good software will maximise user agency by facilitate +user-expression on the network. As such: + +- This project will strive to give users as much choice as is both reasonable and possible over what + protocol they adhere to; but +- use of the project's technical forums, commenting systems, pull requests and issue trackers as a + means to express individual protocol preferences is forbidden. + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are +expected to take appropriate and fair corrective action in response to any instances of unacceptable +behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, +code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or +to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is +representing the project or its community. Examples of representing a project or community include +using an official project e-mail address, posting via an official social media account, or acting as +an appointed representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting +the project team at admin@parity.io. All complaints will be reviewed and investigated and will +result in a response that is deemed necessary and appropriate to the circumstances. The project team +is obligated to maintain confidentiality with regard to the reporter of an incident. Further +details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face +temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at +https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000000..f4b78e93bca --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,15431 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli 0.26.2", +] + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.2", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aead" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array 0.14.6", + "rand_core 0.6.4", +] + +[[package]] +name = "aead" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8" +dependencies = [ + "crypto-common", + "generic-array 0.14.6", +] + +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher 0.2.5", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if 1.0.0", + "cipher 0.3.0", + "cpufeatures", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aes" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +dependencies = [ + "cfg-if 1.0.0", + "cipher 0.4.4", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +dependencies = [ + "aead 0.4.3", + "aes 0.7.5", + "cipher 0.3.0", + "ctr 0.8.0", + "ghash 0.4.4", + "subtle", +] + +[[package]] +name = "aes-gcm" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +dependencies = [ + "aead 0.5.1", + "aes 0.8.2", + "cipher 0.4.4", + "ctr 0.9.2", + "ghash 0.5.0", + "subtle", +] + +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher 0.2.5", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher 0.2.5", + "opaque-debug 0.3.0", +] + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.8", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if 1.0.0", + "getrandom 0.2.8", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "always-assert" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf688625d06217d5b1bb0ea9d9c44a1635fd0ee3534466388d18203174f4d11" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anstream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "arbitrary" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" + +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + +[[package]] +name = "array-bytes" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" + +[[package]] +name = "array-bytes" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f72e9d6fac4bc80778ea470b20197b88d28c292bb7d60c3fb099280003cd19" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "asn1-rs" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ff05a702273012438132f449575dbc804e27b2f3cbe3069aa237d26c98fa33" +dependencies = [ + "asn1-rs-derive 0.1.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time 0.3.20", +] + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive 0.4.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time 0.3.20", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "asn1_der" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22d1f4b888c298a027c99dc9048015fac177587de20fc30232a057dfbe24a21" + +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" +dependencies = [ + "async-lock", + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg", + "cfg-if 1.0.0", + "concurrent-queue", + "futures-lite", + "log", + "parking", + "polling", + "rustix 0.37.3", + "slab", + "socket2", + "waker-fn", +] + +[[package]] +name = "async-lock" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-attributes", + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite 0.2.9", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" + +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "asynchronous-codec" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite 0.2.9", +] + +[[package]] +name = "atomic-waker" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backoff" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +dependencies = [ + "getrandom 0.2.8", + "instant", + "rand 0.8.5", +] + +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line 0.19.0", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object 0.30.3", + "rustc-demangle", +] + +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] + +[[package]] +name = "binary-merkle-tree" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "hash-db", + "log", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.64.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2 1.0.56", + "quote 1.0.26", + "regex", + "rustc-hash", + "shlex", + "syn 1.0.109", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "blake2b_simd" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "constant_time_eq", +] + +[[package]] +name = "blake2s_simd" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "constant_time_eq", +] + +[[package]] +name = "blake3" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "cc", + "cfg-if 1.0.0", + "constant_time_eq", + "digest 0.10.6", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding 0.1.5", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "block-modes" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" +dependencies = [ + "block-padding 0.2.1", + "cipher 0.2.5", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "blocking" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c67b173a56acffd6d2326fb7ab938ba0b00a71480e14902b2591c87bc5741e8" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", +] + +[[package]] +name = "bounded-collections" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a071c348a5ef6da1d3a87166b408170b46002382b1dda83992b5c2208cefb370" +dependencies = [ + "log", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "bounded-vec" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68534a48cbf63a4b1323c433cf21238c9ec23711e0df13b08c33e5c2082663ce" +dependencies = [ + "thiserror", +] + +[[package]] +name = "bp-beefy" +version = "0.1.0" +dependencies = [ + "binary-merkle-tree", + "bp-runtime", + "frame-support", + "pallet-beefy-mmr", + "pallet-mmr", + "parity-scale-codec", + "scale-info", + "serde", + "sp-consensus-beefy", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-bridge-hub-cumulus" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "frame-system", + "polkadot-primitives", + "sp-api", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-bridge-hub-kusama" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-messages", + "bp-runtime", + "frame-support", + "sp-api", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-bridge-hub-polkadot" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-messages", + "bp-runtime", + "frame-support", + "sp-api", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-bridge-hub-rococo" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-messages", + "bp-runtime", + "frame-support", + "sp-api", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-bridge-hub-wococo" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-messages", + "bp-runtime", + "frame-support", + "sp-api", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-header-chain" +version = "0.1.0" +dependencies = [ + "bp-runtime", + "bp-test-utils", + "finality-grandpa", + "frame-support", + "hex", + "hex-literal 0.4.1", + "parity-scale-codec", + "scale-info", + "serde", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-kusama" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "sp-api", +] + +[[package]] +name = "bp-messages" +version = "0.1.0" +dependencies = [ + "bp-runtime", + "frame-support", + "hex", + "hex-literal 0.4.1", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-millau" +version = "0.1.0" +dependencies = [ + "bp-beefy", + "bp-header-chain", + "bp-messages", + "bp-runtime", + "fixed-hash", + "frame-support", + "frame-system", + "hash256-std-hasher", + "impl-codec", + "impl-serde", + "parity-util-mem", + "scale-info", + "serde", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "sp-trie", +] + +[[package]] +name = "bp-parachains" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-polkadot" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "sp-api", +] + +[[package]] +name = "bp-polkadot-core" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-runtime", + "frame-support", + "frame-system", + "hex", + "parity-scale-codec", + "parity-util-mem", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-relayers" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-runtime", + "frame-support", + "hex", + "hex-literal 0.4.1", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-rialto" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-messages", + "bp-runtime", + "frame-support", + "frame-system", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-rialto-parachain" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-messages", + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "frame-system", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-rococo" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "sp-api", +] + +[[package]] +name = "bp-runtime" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "hash-db", + "hex-literal 0.4.1", + "impl-trait-for-tuples", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", + "sp-trie", + "trie-db", +] + +[[package]] +name = "bp-test-utils" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "ed25519-dalek", + "finality-grandpa", + "parity-scale-codec", + "sp-application-crypto", + "sp-consensus-grandpa", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "bp-westend" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-polkadot-core", + "bp-runtime", + "frame-support", + "sp-api", +] + +[[package]] +name = "bp-wococo" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-polkadot-core", + "bp-rococo", + "bp-runtime", + "frame-support", + "sp-api", +] + +[[package]] +name = "bridge-runtime-common" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-messages", + "bp-parachains", + "bp-polkadot-core", + "bp-relayers", + "bp-runtime", + "bp-test-utils", + "frame-support", + "frame-system", + "hash-db", + "log", + "pallet-balances", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-bridge-relayers", + "pallet-transaction-payment", + "pallet-utility", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "sp-trie", + "static_assertions", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bstr" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "build-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdce191bf3fa4995ce948c8c83b4640a1745457a149e73c6db75b4ffe36aad5f" +dependencies = [ + "semver 0.6.0", +] + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "camino" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.17", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "castaway" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "ccm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca1a8fbc20b50ac9673ff014abfb2b5f4085ee1a850d408f14a159c5853ac7" +dependencies = [ + "aead 0.3.2", + "cipher 0.2.5", + "subtle", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-expr" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chacha20" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" +dependencies = [ + "cfg-if 1.0.0", + "cipher 0.3.0", + "cpufeatures", + "zeroize", +] + +[[package]] +name = "chacha20poly1305" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +dependencies = [ + "aead 0.4.3", + "chacha20", + "cipher 0.3.0", + "poly1305", + "zeroize", +] + +[[package]] +name = "chrono" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time 0.1.45", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "cid" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" +dependencies = [ + "core2", + "multibase", + "multihash 0.16.3", + "serde", + "unsigned-varint", +] + +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "ckb-merkle-mountain-range" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f061f97d64fd1822664bdfb722f7ae5469a97b77567390f7442be5b5dc82a5b" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "ckb-merkle-mountain-range" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ccb671c5921be8a84686e6212ca184cb1d7c51cadcdbfcbd1cc3f042f5dfb8" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "clang-sys" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim 0.8.0", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "clap" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b802d85aaf3a1cdb02b224ba472ebdea62014fccfcb269b95a4d76443b5ee5a" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14a1a858f532119338887a4b8e1af9c60de8249cd7bafd68036a489e261e37b6" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex", + "strsim 0.10.0", +] + +[[package]] +name = "clap_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "clap_lex" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" + +[[package]] +name = "coarsetime" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90d114103adbc625300f346d4d09dfb4ab1c4a8df6868435dd903392ecf4354" +dependencies = [ + "libc", + "once_cell", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "comfy-table" +version = "6.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e7b787b0dc42e8111badfdbe4c3059158ccb2db8780352fa1b01e8ccf45cc4d" +dependencies = [ + "strum", + "strum_macros", + "unicode-width", +] + +[[package]] +name = "concurrent-queue" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + +[[package]] +name = "constant_time_eq" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "cpu-time" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e393a7668fe1fad3075085b86c781883000b4ede868f43627b34a87c8b7ded" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7379abaacee0f14abf3204a7606118f0465785252169d186337bcb75030815a" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9489fa336927df749631f1008007ced2871068544f40a202ce6d93fbf2366a7b" +dependencies = [ + "arrayvec 0.7.2", + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "cranelift-isle", + "gimli 0.26.2", + "hashbrown 0.12.3", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05bbb67da91ec721ed57cef2f7c5ef7728e1cd9bde9ffd3ef8601022e73e3239" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418ecb2f36032f6665dc1a5e2060a143dbab41d83b784882e97710e890a7a16d" + +[[package]] +name = "cranelift-entity" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cf583f7b093f291005f9fb1323e2c37f6ee4c7909e39ce016b2e8360d461705" +dependencies = [ + "serde", +] + +[[package]] +name = "cranelift-frontend" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b66bf9e916f57fbbd0f7703ec6286f4624866bf45000111627c70d272c8dda1" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "649782a39ce99798dd6b4029e2bb318a2fbeaade1b4fa25330763c10c65bc358" + +[[package]] +name = "cranelift-native" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "937e021e089c51f9749d09e7ad1c4f255c2f8686cb8c3df63a34b3ec9921bc41" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d850cf6775477747c9dfda9ae23355dd70512ffebc70cf82b85a5b111ae668b5" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools", + "log", + "smallvec", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "memoffset 0.8.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.6", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" +dependencies = [ + "generic-array 0.14.6", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.6", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.6", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array 0.14.6", + "subtle", +] + +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher 0.3.0", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher 0.4.4", +] + +[[package]] +name = "cumulus-client-cli" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "clap 4.2.2", + "parity-scale-codec", + "sc-chain-spec", + "sc-cli", + "sc-service", + "sp-core", + "sp-runtime", + "url", +] + +[[package]] +name = "cumulus-client-collator" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "cumulus-client-consensus-common", + "cumulus-client-network", + "cumulus-primitives-core", + "futures", + "parity-scale-codec", + "parking_lot 0.12.1", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-overseer", + "polkadot-primitives", + "sc-client-api", + "sp-api", + "sp-consensus", + "sp-core", + "sp-runtime", + "tracing", +] + +[[package]] +name = "cumulus-client-consensus-aura" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "async-trait", + "cumulus-client-consensus-common", + "cumulus-primitives-core", + "futures", + "parity-scale-codec", + "sc-client-api", + "sc-consensus", + "sc-consensus-aura", + "sc-consensus-slots", + "sc-telemetry", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "tracing", +] + +[[package]] +name = "cumulus-client-consensus-common" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "async-trait", + "cumulus-client-pov-recovery", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "dyn-clone", + "futures", + "log", + "parity-scale-codec", + "polkadot-primitives", + "sc-client-api", + "sc-consensus", + "schnellru", + "sp-blockchain", + "sp-consensus", + "sp-runtime", + "sp-trie", + "tracing", +] + +[[package]] +name = "cumulus-client-network" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "async-trait", + "cumulus-relay-chain-interface", + "futures", + "futures-timer", + "parity-scale-codec", + "parking_lot 0.12.1", + "polkadot-node-primitives", + "polkadot-parachain", + "polkadot-primitives", + "sc-client-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-state-machine", + "tracing", +] + +[[package]] +name = "cumulus-client-pov-recovery" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "futures", + "futures-timer", + "parity-scale-codec", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-overseer", + "polkadot-primitives", + "rand 0.8.5", + "sc-client-api", + "sc-consensus", + "sp-consensus", + "sp-maybe-compressed-blob", + "sp-runtime", + "tracing", +] + +[[package]] +name = "cumulus-client-service" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "cumulus-client-cli", + "cumulus-client-collator", + "cumulus-client-consensus-common", + "cumulus-client-network", + "cumulus-client-pov-recovery", + "cumulus-primitives-core", + "cumulus-relay-chain-inprocess-interface", + "cumulus-relay-chain-interface", + "cumulus-relay-chain-minimal-node", + "futures", + "polkadot-primitives", + "sc-client-api", + "sc-consensus", + "sc-network", + "sc-network-sync", + "sc-network-transactions", + "sc-rpc", + "sc-service", + "sc-sysinfo", + "sc-telemetry", + "sc-transaction-pool", + "sc-utils", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-transaction-pool", +] + +[[package]] +name = "cumulus-pallet-aura-ext" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "frame-support", + "frame-system", + "pallet-aura", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-aura", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "cumulus-pallet-dmp-queue" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "xcm", +] + +[[package]] +name = "cumulus-pallet-parachain-system" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "bytes", + "cumulus-pallet-parachain-system-proc-macro", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "environmental", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "polkadot-parachain", + "scale-info", + "sp-core", + "sp-externalities", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", + "sp-trie", + "sp-version", + "xcm", +] + +[[package]] +name = "cumulus-pallet-parachain-system-proc-macro" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "cumulus-pallet-xcm" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "xcm", +] + +[[package]] +name = "cumulus-pallet-xcmp-queue" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "polkadot-runtime-common", + "rand_chacha 0.3.1", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "xcm", + "xcm-executor", +] + +[[package]] +name = "cumulus-primitives-core" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain", + "polkadot-primitives", + "scale-info", + "sp-api", + "sp-runtime", + "sp-std 5.0.0", + "sp-trie", + "xcm", +] + +[[package]] +name = "cumulus-primitives-parachain-inherent" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "cumulus-test-relay-sproof-builder", + "parity-scale-codec", + "sc-client-api", + "scale-info", + "sp-api", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", + "sp-storage", + "sp-trie", + "tracing", +] + +[[package]] +name = "cumulus-primitives-timestamp" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "cumulus-primitives-core", + "futures", + "parity-scale-codec", + "sp-inherents", + "sp-std 5.0.0", + "sp-timestamp", +] + +[[package]] +name = "cumulus-relay-chain-inprocess-interface" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "futures", + "futures-timer", + "polkadot-cli", + "polkadot-client", + "polkadot-service", + "sc-cli", + "sc-client-api", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sp-api", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-state-machine", +] + +[[package]] +name = "cumulus-relay-chain-interface" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "futures", + "jsonrpsee-core", + "parity-scale-codec", + "polkadot-overseer", + "sc-client-api", + "sp-api", + "sp-blockchain", + "sp-state-machine", + "thiserror", +] + +[[package]] +name = "cumulus-relay-chain-minimal-node" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "array-bytes 6.0.0", + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "cumulus-relay-chain-rpc-interface", + "futures", + "lru 0.9.0", + "polkadot-availability-recovery", + "polkadot-collator-protocol", + "polkadot-core-primitives", + "polkadot-network-bridge", + "polkadot-node-collation-generation", + "polkadot-node-core-runtime-api", + "polkadot-node-network-protocol", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-primitives", + "sc-authority-discovery", + "sc-client-api", + "sc-network", + "sc-network-common", + "sc-service", + "sc-tracing", + "sc-utils", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-runtime", + "tokio", + "tracing", +] + +[[package]] +name = "cumulus-relay-chain-rpc-interface" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "futures", + "futures-timer", + "jsonrpsee", + "lru 0.9.0", + "parity-scale-codec", + "polkadot-overseer", + "sc-client-api", + "sc-rpc-api", + "sc-service", + "serde", + "serde_json", + "sp-api", + "sp-authority-discovery", + "sp-consensus-babe", + "sp-core", + "sp-state-machine", + "sp-storage", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "cumulus-test-relay-sproof-builder" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "cumulus-primitives-core", + "parity-scale-codec", + "polkadot-primitives", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", +] + +[[package]] +name = "curl" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2", + "winapi", +] + +[[package]] +name = "curl-sys" +version = "0.4.61+curl-8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14d05c10f541ae6f3bc5b3d923c20001f47db7d5f0b2bc6ad16490133842db79" +dependencies = [ + "cc", + "libc", + "libnghttp2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "winapi", +] + +[[package]] +name = "curve25519-dalek" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "4.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16" +dependencies = [ + "cfg-if 1.0.0", + "fiat-crypto", + "packed_simd_2", + "platforms 3.0.2", + "subtle", + "zeroize", +] + +[[package]] +name = "cxx" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c00419335c41018365ddf7e4d5f1c12ee3659ddcf3e01974650ba1de73d038" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8307ad413a98fff033c8545ecf133e3257747b3bae935e7602aab8aa92d4ca" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2 1.0.56", + "quote 1.0.26", + "scratch", + "syn 2.0.14", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc52e2eb08915cb12596d29d55f0b5384f00d697a646dbd269b6ecb0fbd9d31" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.56", + "quote 1.0.26", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "data-encoding" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" + +[[package]] +name = "data-encoding-macro" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86927b7cd2fe88fa698b87404b287ab98d1a0063a34071d92e575b72d3029aca" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" +dependencies = [ + "data-encoding", + "syn 1.0.109", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc906908ea6458456e5eaa160a9c08543ec3d1e6f71e2235cedd660cb65f9df0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe398ac75057914d7d07307bf67dc7f3f574a26783b4fc7805a20ffa9f506e82" +dependencies = [ + "asn1-rs 0.3.1", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs 0.5.2", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "derive-syn-parse" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" +dependencies = [ + "darling", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" +dependencies = [ + "derive_builder_core", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2 1.0.56", + "quote 1.0.26", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "directories" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "displaydoc" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "dtoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65d09067bfacaa79114679b279d7f5885b53295b1e2cfb4e79c8e4bd3d633169" + +[[package]] +name = "dyn-clonable" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +dependencies = [ + "dyn-clonable-impl", + "dyn-clone", +] + +[[package]] +name = "dyn-clonable-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "dyn-clone" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1b0a1222f8072619e8a6b667a854020a03d363738303203c09468b3424a420a" +dependencies = [ + "der 0.7.1", + "elliptic-curve 0.13.2", + "rfc6979 0.4.0", + "signature 2.0.0", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature 1.6.4", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek 3.2.0", + "hashbrown 0.12.3", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.6", + "ff 0.12.1", + "generic-array 0.14.6", + "group 0.12.1", + "hkdf", + "pem-rfc7468", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea5a92946e8614bb585254898bb7dd1ddad241ace60c52149e3765e34cc039d" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.1", + "digest 0.10.6", + "ff 0.13.0", + "generic-array 0.14.6", + "group 0.13.0", + "pkcs8 0.10.1", + "rand_core 0.6.4", + "sec1 0.7.1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "enum-as-inner" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "enumflags2" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb" +dependencies = [ + "enumflags2_derive", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "enumn" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48016319042fb7c87b78d2993084a831793a897a5cd1a2a67cab9d1eeb4b7d76" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "environmental" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-rlp", + "impl-serde", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-rlp", + "impl-serde", + "primitive-types", + "uint", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "exit-future" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" +dependencies = [ + "futures", +] + +[[package]] +name = "expander" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a718c0675c555c5f976fff4ea9e2c150fa06cefa201cadef87cfbf9324075881" +dependencies = [ + "blake3", + "fs-err", + "proc-macro2 1.0.56", + "quote 1.0.26", +] + +[[package]] +name = "expander" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3774182a5df13c3d1690311ad32fbe913feef26baba609fa2dd5f72042bd2ab6" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2 1.0.56", + "quote 1.0.26", +] + +[[package]] +name = "expander" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "expander" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f86a749cf851891866c10515ef6c299b5c69661465e9c3bbe7e07a2b77fb0f7" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fatality" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad875162843b0d046276327afe0136e9ed3a23d5a754210fb6f1f33610d39ab" +dependencies = [ + "fatality-proc-macro", + "thiserror", +] + +[[package]] +name = "fatality-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5aa1e3ae159e592ad222dc90c5acbad632b527779ba88486abe92782ab268bd" +dependencies = [ + "expander 0.0.4", + "indexmap", + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", + "thiserror", +] + +[[package]] +name = "fdlimit" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4c9e43643f5a3be4ca5b67d26b98031ff9db6806c3440ae32e02e3ceac3f1b" +dependencies = [ + "libc", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ace6ec7cc19c8ed33a32eaa9ea692d7faea05006b5356b9e2b668ec4bc3955" + +[[package]] +name = "file-per-thread-logger" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "filetime" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.2.16", + "windows-sys 0.45.0", +] + +[[package]] +name = "finality-grandpa" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36530797b9bf31cd4ff126dcfee8170f86b00cfdcea3269d73133cc0415945c3" +dependencies = [ + "either", + "futures", + "futures-timer", + "log", + "num-traits", + "parity-scale-codec", + "parking_lot 0.12.1", + "scale-info", +] + +[[package]] +name = "finality-relay" +version = "0.1.0" +dependencies = [ + "async-std", + "async-trait", + "backoff", + "bp-header-chain", + "futures", + "log", + "num-traits", + "parking_lot 0.12.1", + "relay-utils", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "libz-sys", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fork-tree" +version = "3.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + +[[package]] +name = "frame-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support", + "frame-support-procedural", + "frame-system", + "linregress", + "log", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std 5.0.0", + "sp-storage", + "static_assertions", +] + +[[package]] +name = "frame-benchmarking-cli" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "Inflector", + "array-bytes 4.2.0", + "chrono", + "clap 4.2.2", + "comfy-table", + "frame-benchmarking", + "frame-support", + "frame-system", + "gethostname", + "handlebars", + "itertools", + "lazy_static", + "linked-hash-map", + "log", + "parity-scale-codec", + "rand 0.8.5", + "rand_pcg", + "sc-block-builder", + "sc-cli", + "sc-client-api", + "sc-client-db", + "sc-executor", + "sc-service", + "sc-sysinfo", + "serde", + "serde_json", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-database", + "sp-externalities", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", + "sp-storage", + "sp-trie", + "thiserror", + "thousands", +] + +[[package]] +name = "frame-election-provider-solution-type" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "frame-election-provider-support" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-election-provider-solution-type", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-npos-elections", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "frame-executive" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "sp-tracing", +] + +[[package]] +name = "frame-metadata" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" +dependencies = [ + "cfg-if 1.0.0", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "frame-remote-externalities" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "futures", + "log", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "substrate-rpc-client", + "tokio", +] + +[[package]] +name = "frame-support" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "bitflags", + "environmental", + "frame-metadata", + "frame-support-procedural", + "impl-trait-for-tuples", + "k256", + "log", + "once_cell", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "smallvec", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-core-hashing-proc-macro", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-state-machine", + "sp-std 5.0.0", + "sp-tracing", + "sp-weights", + "tt-call", +] + +[[package]] +name = "frame-support-procedural" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "Inflector", + "cfg-expr", + "derive-syn-parse", + "frame-support-procedural-tools", + "itertools", + "proc-macro-warning", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "frame-support-procedural-tools" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support-procedural-tools-derive", + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "frame-support-procedural-tools-derive" +version = "3.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "frame-system" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "sp-version", + "sp-weights", +] + +[[package]] +name = "frame-system-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "frame-system-rpc-runtime-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", + "sp-api", +] + +[[package]] +name = "frame-try-runtime" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "fs-err" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fs4" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea55201cc351fdb478217c0fb641b59813da9b4efe4c414a9d8f989a657d149" +dependencies = [ + "libc", + "rustix 0.35.13", + "winapi", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite 0.2.9", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "futures-rustls" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" +dependencies = [ + "futures-io", + "rustls 0.20.8", + "webpki 0.22.0", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite 0.2.9", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug 0.3.0", + "polyval 0.5.3", +] + +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug 0.3.0", + "polyval 0.6.0", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "handlebars" +version = "4.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "hash-db" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array 0.14.6", + "hmac 0.8.1", +] + +[[package]] +name = "honggfuzz" +version = "0.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "848e9c511092e0daa0a35a63e8e6e475a3e8f870741448b9f6028d69b142f18e" +dependencies = [ + "arbitrary", + "lazy_static", + "memmap2", + "rustc_version", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite 0.2.9", +] + +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite 0.2.9", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "log", + "rustls 0.20.8", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "webpki-roots", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows 0.46.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "if-addrs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "if-watch" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba7abdbb86e485125dad06c2691e1e393bf3b08c7b743b43aa162a00fd39062e" +dependencies = [ + "async-io", + "core-foundation", + "fnv", + "futures", + "if-addrs", + "ipnet", + "log", + "rtnetlink", + "system-configuration", + "tokio", + "windows 0.34.0", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "integer-encoding" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "interceptor" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8a11ae2da61704edada656798b61c94b35ecac2c58eb955156987d5e6be90b" +dependencies = [ + "async-trait", + "bytes", + "log", + "rand 0.8.5", + "rtcp", + "rtp", + "thiserror", + "tokio", + "waitgroup", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "io-lifetimes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" + +[[package]] +name = "io-lifetimes" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "ip_network" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1" + +[[package]] +name = "ipconfig" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd302af1b90f2463a98fa5ad469fc212c8e3175a41c3068601bfa2727591c5be" +dependencies = [ + "socket2", + "widestring", + "winapi", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + +[[package]] +name = "is-terminal" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes 1.0.9", + "rustix 0.36.11", + "windows-sys 0.45.0", +] + +[[package]] +name = "isahc" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" +dependencies = [ + "async-channel", + "castaway", + "crossbeam-utils", + "curl", + "curl-sys", + "encoding_rs", + "event-listener", + "futures-lite", + "http", + "log", + "mime", + "once_cell", + "polling", + "slab", + "sluice", + "tracing", + "tracing-futures", + "url", + "waker-fn", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonpath_lib" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaa63191d68230cccb81c5aa23abd53ed64d83337cacbb25a7b8c7979523774f" +dependencies = [ + "log", + "serde", + "serde_json", +] + +[[package]] +name = "jsonrpsee" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", + "jsonrpsee-ws-client", + "tracing", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965de52763f2004bc91ac5bcec504192440f0b568a5d621c59d9dbd6f886c3fb" +dependencies = [ + "futures-util", + "http", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", + "webpki-roots", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" +dependencies = [ + "anyhow", + "arrayvec 0.7.2", + "async-lock", + "async-trait", + "beef", + "futures-channel", + "futures-timer", + "futures-util", + "globset", + "hyper", + "jsonrpsee-types", + "parking_lot 0.12.1", + "rand 0.8.5", + "rustc-hash", + "serde", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls", + "jsonrpsee-core", + "jsonrpsee-types", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa6da1e4199c10d7b1d0a6e5e8bd8e55f351163b6f4b3cbb044672a69bd4c1c" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb69dad85df79527c019659a992498d03f8495390496da2f07e6c24c2b356fc" +dependencies = [ + "futures-channel", + "futures-util", + "http", + "hyper", + "jsonrpsee-core", + "jsonrpsee-types", + "serde", + "serde_json", + "soketto", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd522fe1ce3702fd94812965d7bb7a3364b1c9aba743944c5a00529aae80f8c" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b83daeecfc6517cfe210df24e570fb06213533dfb990318fae781f4c7119dd9" +dependencies = [ + "http", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", +] + +[[package]] +name = "k256" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955890845095ccf31ef83ad41a05aabb4d8cc23dc3cac5a9f5c89cf26dd0da75" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa 0.16.1", + "elliptic-curve 0.13.2", + "once_cell", + "sha2 0.10.6", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "kusama-runtime" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bitvec", + "frame-benchmarking", + "frame-election-provider-support", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal 0.3.4", + "kusama-runtime-constants", + "log", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-bags-list", + "pallet-balances", + "pallet-bounties", + "pallet-child-bounties", + "pallet-collective", + "pallet-conviction-voting", + "pallet-democracy", + "pallet-election-provider-multi-phase", + "pallet-election-provider-support-benchmarking", + "pallet-elections-phragmen", + "pallet-fast-unstake", + "pallet-grandpa", + "pallet-identity", + "pallet-im-online", + "pallet-indices", + "pallet-membership", + "pallet-multisig", + "pallet-nis", + "pallet-nomination-pools", + "pallet-nomination-pools-benchmarking", + "pallet-nomination-pools-runtime-api", + "pallet-offences", + "pallet-offences-benchmarking", + "pallet-preimage", + "pallet-proxy", + "pallet-ranked-collective", + "pallet-recovery", + "pallet-referenda", + "pallet-scheduler", + "pallet-session", + "pallet-session-benchmarking", + "pallet-society", + "pallet-staking", + "pallet-staking-runtime-api", + "pallet-timestamp", + "pallet-tips", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-vesting", + "pallet-whitelist", + "pallet-xcm", + "pallet-xcm-benchmarks", + "parity-scale-codec", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "rustc-hex", + "scale-info", + "serde", + "serde_derive", + "smallvec", + "sp-api", + "sp-arithmetic", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-core", + "sp-inherents", + "sp-io", + "sp-mmr-primitives", + "sp-npos-elections", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", + "sp-transaction-pool", + "sp-version", + "static_assertions", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "kusama-runtime-constants" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "frame-support", + "polkadot-primitives", + "polkadot-runtime-common", + "smallvec", + "sp-core", + "sp-runtime", + "sp-weights", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "kvdb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d770dcb02bf6835887c3a979b5107a04ff4bbde97a5f0928d27404a155add9" +dependencies = [ + "smallvec", +] + +[[package]] +name = "kvdb-memorydb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7a85fe66f9ff9cd74e169fdd2c94c6e1e74c412c99a73b4df3200b5d3760b2" +dependencies = [ + "kvdb", + "parking_lot 0.12.1", +] + +[[package]] +name = "kvdb-rocksdb" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2182b8219fee6bd83aacaab7344e840179ae079d5216aa4e249b4d704646a844" +dependencies = [ + "kvdb", + "num_cpus", + "parking_lot 0.12.1", + "regex", + "rocksdb", + "smallvec", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + +[[package]] +name = "libm" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" + +[[package]] +name = "libnghttp2-sys" +version = "0.1.7+1.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ed28aba195b38d5ff02b9170cbff627e336a20925e43b4945390401c5dc93f" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "libp2p" +version = "0.50.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7b0104790be871edcf97db9bd2356604984e623a08d825c3f27852290266b8" +dependencies = [ + "bytes", + "futures", + "futures-timer", + "getrandom 0.2.8", + "instant", + "libp2p-core 0.38.0", + "libp2p-dns", + "libp2p-identify", + "libp2p-kad", + "libp2p-mdns", + "libp2p-metrics", + "libp2p-mplex", + "libp2p-noise", + "libp2p-ping", + "libp2p-quic", + "libp2p-request-response", + "libp2p-swarm", + "libp2p-tcp", + "libp2p-wasm-ext", + "libp2p-webrtc", + "libp2p-websocket", + "libp2p-yamux", + "multiaddr 0.16.0", + "parking_lot 0.12.1", + "pin-project", + "smallvec", +] + +[[package]] +name = "libp2p-core" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a8fcd392ff67af6cc3f03b1426c41f7f26b6b9aff2dc632c1c56dd649e571f" +dependencies = [ + "asn1_der", + "bs58", + "ed25519-dalek", + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "log", + "multiaddr 0.16.0", + "multihash 0.16.3", + "multistream-select", + "once_cell", + "parking_lot 0.12.1", + "pin-project", + "prost", + "prost-build", + "rand 0.8.5", + "rw-stream-sink", + "sec1 0.3.0", + "sha2 0.10.6", + "smallvec", + "thiserror", + "unsigned-varint", + "void", + "zeroize", +] + +[[package]] +name = "libp2p-core" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7f8b7d65c070a5a1b5f8f0510648189da08f787b8963f8e21219e0710733af" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-identity", + "log", + "multiaddr 0.17.1", + "multihash 0.17.0", + "multistream-select", + "once_cell", + "parking_lot 0.12.1", + "pin-project", + "quick-protobuf", + "rand 0.8.5", + "rw-stream-sink", + "smallvec", + "thiserror", + "unsigned-varint", + "void", +] + +[[package]] +name = "libp2p-dns" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e42a271c1b49f789b92f7fc87749fa79ce5c7bdc88cbdfacb818a4bca47fec5" +dependencies = [ + "futures", + "libp2p-core 0.38.0", + "log", + "parking_lot 0.12.1", + "smallvec", + "trust-dns-resolver", +] + +[[package]] +name = "libp2p-identify" +version = "0.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c052d0026f4817b44869bfb6810f4e1112f43aec8553f2cb38881c524b563abf" +dependencies = [ + "asynchronous-codec", + "futures", + "futures-timer", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log", + "lru 0.8.1", + "prost", + "prost-build", + "prost-codec", + "smallvec", + "thiserror", + "void", +] + +[[package]] +name = "libp2p-identity" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a8ea433ae0cea7e3315354305237b9897afe45278b2118a7a57ca744e70fd27" +dependencies = [ + "bs58", + "ed25519-dalek", + "log", + "multiaddr 0.17.1", + "multihash 0.17.0", + "prost", + "quick-protobuf", + "rand 0.8.5", + "thiserror", + "zeroize", +] + +[[package]] +name = "libp2p-kad" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2766dcd2be8c87d5e1f35487deb22d765f49c6ae1251b3633efe3b25698bd3d2" +dependencies = [ + "arrayvec 0.7.2", + "asynchronous-codec", + "bytes", + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log", + "prost", + "prost-build", + "rand 0.8.5", + "sha2 0.10.6", + "smallvec", + "thiserror", + "uint", + "unsigned-varint", + "void", +] + +[[package]] +name = "libp2p-mdns" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f378264aade9872d6ccd315c0accc18be3a35d15fc1b9c36e5b6f983b62b5b" +dependencies = [ + "data-encoding", + "futures", + "if-watch", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log", + "rand 0.8.5", + "smallvec", + "socket2", + "tokio", + "trust-dns-proto", + "void", +] + +[[package]] +name = "libp2p-metrics" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad8a64f29da86005c86a4d2728b8a0719e9b192f4092b609fd8790acb9dec55" +dependencies = [ + "libp2p-core 0.38.0", + "libp2p-identify", + "libp2p-kad", + "libp2p-ping", + "libp2p-swarm", + "prometheus-client", +] + +[[package]] +name = "libp2p-mplex" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03805b44107aa013e7cbbfa5627b31c36cbedfdfb00603c0311998882bc4bace" +dependencies = [ + "asynchronous-codec", + "bytes", + "futures", + "libp2p-core 0.38.0", + "log", + "nohash-hasher", + "parking_lot 0.12.1", + "rand 0.8.5", + "smallvec", + "unsigned-varint", +] + +[[package]] +name = "libp2p-noise" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978cb57efe82e892ec6f348a536bfbd9fee677adbe5689d7a93ad3a9bffbf2e" +dependencies = [ + "bytes", + "curve25519-dalek 3.2.0", + "futures", + "libp2p-core 0.38.0", + "log", + "once_cell", + "prost", + "prost-build", + "rand 0.8.5", + "sha2 0.10.6", + "snow", + "static_assertions", + "thiserror", + "x25519-dalek 1.1.1", + "zeroize", +] + +[[package]] +name = "libp2p-ping" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "929fcace45a112536e22b3dcfd4db538723ef9c3cb79f672b98be2cc8e25f37f" +dependencies = [ + "futures", + "futures-timer", + "instant", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log", + "rand 0.8.5", + "void", +] + +[[package]] +name = "libp2p-quic" +version = "0.7.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01e7c867e95c8130667b24409d236d37598270e6da69b3baf54213ba31ffca59" +dependencies = [ + "bytes", + "futures", + "futures-timer", + "if-watch", + "libp2p-core 0.38.0", + "libp2p-tls", + "log", + "parking_lot 0.12.1", + "quinn-proto", + "rand 0.8.5", + "rustls 0.20.8", + "thiserror", + "tokio", +] + +[[package]] +name = "libp2p-request-response" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3236168796727bfcf4927f766393415361e2c644b08bedb6a6b13d957c9a4884" +dependencies = [ + "async-trait", + "bytes", + "futures", + "instant", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log", + "rand 0.8.5", + "smallvec", + "unsigned-varint", +] + +[[package]] +name = "libp2p-swarm" +version = "0.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a35472fe3276b3855c00f1c032ea8413615e030256429ad5349cdf67c6e1a0" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-core 0.38.0", + "libp2p-swarm-derive", + "log", + "pin-project", + "rand 0.8.5", + "smallvec", + "thiserror", + "tokio", + "void", +] + +[[package]] +name = "libp2p-swarm-derive" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d527d5827582abd44a6d80c07ff8b50b4ee238a8979e05998474179e79dc400" +dependencies = [ + "heck 0.4.1", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "libp2p-tcp" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b257baf6df8f2df39678b86c578961d48cc8b68642a12f0f763f56c8e5858d" +dependencies = [ + "futures", + "futures-timer", + "if-watch", + "libc", + "libp2p-core 0.38.0", + "log", + "socket2", + "tokio", +] + +[[package]] +name = "libp2p-tls" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781" +dependencies = [ + "futures", + "futures-rustls", + "libp2p-core 0.39.1", + "libp2p-identity", + "rcgen 0.10.0", + "ring", + "rustls 0.20.8", + "thiserror", + "webpki 0.22.0", + "x509-parser 0.14.0", + "yasna", +] + +[[package]] +name = "libp2p-wasm-ext" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb1a35299860e0d4b3c02a3e74e3b293ad35ae0cee8a056363b0c862d082069" +dependencies = [ + "futures", + "js-sys", + "libp2p-core 0.38.0", + "parity-send-wrapper", + "wasm-bindgen", + "wasm-bindgen-futures", +] + +[[package]] +name = "libp2p-webrtc" +version = "0.4.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb6cd86dd68cba72308ea05de1cebf3ba0ae6e187c40548167955d4e3970f6a" +dependencies = [ + "async-trait", + "asynchronous-codec", + "bytes", + "futures", + "futures-timer", + "hex", + "if-watch", + "libp2p-core 0.38.0", + "libp2p-noise", + "log", + "multihash 0.16.3", + "prost", + "prost-build", + "prost-codec", + "rand 0.8.5", + "rcgen 0.9.3", + "serde", + "stun", + "thiserror", + "tinytemplate", + "tokio", + "tokio-util", + "webrtc", +] + +[[package]] +name = "libp2p-websocket" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d705506030d5c0aaf2882437c70dab437605f21c5f9811978f694e6917a3b54" +dependencies = [ + "either", + "futures", + "futures-rustls", + "libp2p-core 0.38.0", + "log", + "parking_lot 0.12.1", + "quicksink", + "rw-stream-sink", + "soketto", + "url", + "webpki-roots", +] + +[[package]] +name = "libp2p-yamux" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f63594a0aa818642d9d4915c791945053877253f08a3626f13416b5cd928a29" +dependencies = [ + "futures", + "libp2p-core 0.38.0", + "log", + "parking_lot 0.12.1", + "thiserror", + "yamux", +] + +[[package]] +name = "librocksdb-sys" +version = "0.8.3+7.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "557b255ff04123fcc176162f56ed0c9cd42d8f357cf55b3fabeb60f7413741b3" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "glob", + "libc", + "libz-sys", + "tikv-jemalloc-sys", +] + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libz-sys" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linked_hash_set" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "linregress" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "475015a7f8f017edb28d2e69813be23500ad4b32cfe3421c4148efc97324ee52" +dependencies = [ + "nalgebra", +] + +[[package]] +name = "linux-raw-sys" +version = "0.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "linux-raw-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd550e73688e6d578f0ac2119e32b797a327631a42f9433e59d02e139c8df60d" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if 1.0.0", + "value-bag", +] + +[[package]] +name = "lru" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" +dependencies = [ + "hashbrown 0.12.3", +] + +[[package]] +name = "lru" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e7d46de488603ffdd5f30afbc64fbba2378214a2c3a2fb83abf3d33126df17" +dependencies = [ + "hashbrown 0.13.2", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "lz4" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" +dependencies = [ + "libc", + "lz4-sys", +] + +[[package]] +name = "lz4-sys" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matrixmultiply" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add85d4dd35074e6fedc608f8c8f513a3548619a9024b751949ef0e8e45a4d84" +dependencies = [ + "rawpointer", +] + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memfd" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" +dependencies = [ + "rustix 0.36.11", +] + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memory-db" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" +dependencies = [ + "hash-db", +] + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "merlin" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "messages-relay" +version = "0.1.0" +dependencies = [ + "async-std", + "async-trait", + "bp-messages", + "finality-relay", + "futures", + "hex", + "log", + "num-traits", + "parking_lot 0.12.1", + "relay-utils", + "sp-arithmetic", +] + +[[package]] +name = "mick-jaeger" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69672161530e8aeca1d1400fbf3f1a1747ff60ea604265a4e906c2442df20532" +dependencies = [ + "futures", + "rand 0.8.5", + "thrift", +] + +[[package]] +name = "millau-bridge-node" +version = "0.1.0" +dependencies = [ + "clap 4.2.2", + "frame-benchmarking", + "frame-benchmarking-cli", + "jsonrpsee", + "millau-runtime", + "mmr-rpc", + "node-inspect", + "pallet-transaction-payment-rpc", + "sc-basic-authorship", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-consensus-aura", + "sc-consensus-beefy", + "sc-consensus-beefy-rpc", + "sc-consensus-grandpa", + "sc-consensus-grandpa-rpc", + "sc-executor", + "sc-keystore", + "sc-network-common", + "sc-rpc", + "sc-service", + "sc-telemetry", + "sc-transaction-pool", + "serde_json", + "sp-consensus-aura", + "sp-consensus-beefy", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", + "sp-timestamp", + "substrate-build-script-utils", + "substrate-frame-rpc-system", +] + +[[package]] +name = "millau-runtime" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-millau", + "bp-parachains", + "bp-polkadot-core", + "bp-relayers", + "bp-rialto", + "bp-rialto-parachain", + "bp-runtime", + "bp-westend", + "bridge-runtime-common", + "env_logger", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "hex-literal 0.4.1", + "pallet-aura", + "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-bridge-relayers", + "pallet-grandpa", + "pallet-mmr", + "pallet-session", + "pallet-shift-session-manager", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-utility", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-consensus-beefy", + "sp-core", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std 5.0.0", + "sp-transaction-pool", + "sp-version", + "static_assertions", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.45.0", +] + +[[package]] +name = "mmr-gadget" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "futures", + "log", + "parity-scale-codec", + "sc-client-api", + "sc-offchain", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-consensus-beefy", + "sp-core", + "sp-mmr-primitives", + "sp-runtime", +] + +[[package]] +name = "mmr-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "anyhow", + "jsonrpsee", + "parity-scale-codec", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-mmr-primitives", + "sp-runtime", +] + +[[package]] +name = "mockall" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e4a1c770583dac7ab5e2f6c139153b783a53a1bbee9729613f193e59828326" +dependencies = [ + "cfg-if 1.0.0", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832663583d5fa284ca8810bf7015e46c9fff9622d3cf34bd1eea5003fec06dd0" +dependencies = [ + "cfg-if 1.0.0", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "multiaddr" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aebdb21e90f81d13ed01dc84123320838e53963c2ca94b60b305d3fa64f31e" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "multibase", + "multihash 0.16.3", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multiaddr" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "log", + "multibase", + "multihash 0.17.0", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +dependencies = [ + "blake2b_simd", + "blake2s_simd", + "blake3", + "core2", + "digest 0.10.6", + "multihash-derive", + "sha2 0.10.6", + "sha3", + "unsigned-varint", +] + +[[package]] +name = "multihash" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" +dependencies = [ + "core2", + "digest 0.10.6", + "multihash-derive", + "sha2 0.10.6", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "multistream-select" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8552ab875c1313b97b8d20cb857b9fd63e2d1d6a0a1b53ce9821e575405f27a" +dependencies = [ + "bytes", + "futures", + "log", + "pin-project", + "smallvec", + "unsigned-varint", +] + +[[package]] +name = "nalgebra" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68d47bba83f9e2006d117a9a33af1524e655516b8919caac694427a6fb1e511" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d232c68884c0c99810a5a4d333ef7e47689cfd0edc85efc9e54e1e6bf5212766" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "names" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d66043b25d4a6cccb23619d10c19c25304b355a7dccd4a8e11423dd2382146" +dependencies = [ + "rand 0.8.5", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" + +[[package]] +name = "netlink-packet-core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" +dependencies = [ + "anyhow", + "byteorder", + "libc", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" +dependencies = [ + "anyhow", + "bitflags", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror", +] + +[[package]] +name = "netlink-proto" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" +dependencies = [ + "bytes", + "futures", + "log", + "netlink-packet-core", + "netlink-sys", + "thiserror", + "tokio", +] + +[[package]] +name = "netlink-sys" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" +dependencies = [ + "bytes", + "futures", + "libc", + "log", + "tokio", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "node-inspect" +version = "0.9.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" +dependencies = [ + "clap 4.2.2", + "parity-scale-codec", + "sc-cli", + "sc-client-api", + "sc-executor", + "sc-service", + "sp-blockchain", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "ntapi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec 0.7.2", + "itoa", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "crc32fast", + "hashbrown 0.12.3", + "indexmap", + "memchr", +] + +[[package]] +name = "object" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "memchr", +] + +[[package]] +name = "oid-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e20717fa0541f39bd146692035c37bedfa532b3e5071b35761082407546b2a" +dependencies = [ + "asn1-rs 0.3.1", +] + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs 0.5.2", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a95792af3c4e0153c3914df2261bedd30a98476f94dc892b67dfe1d89d433a04" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "orchestra" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "227585216d05ba65c7ab0a0450a3cf2cbd81a98862a54c4df8e14d5ac6adb015" +dependencies = [ + "async-trait", + "dyn-clonable", + "futures", + "futures-timer", + "orchestra-proc-macro", + "pin-project", + "prioritized-metered-channel", + "thiserror", + "tracing", +] + +[[package]] +name = "orchestra-proc-macro" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2871aadd82a2c216ee68a69837a526dfe788ecbe74c4c5038a6acdbff6653066" +dependencies = [ + "expander 0.0.6", + "itertools", + "petgraph", + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "ordered-float" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" +dependencies = [ + "num-traits", +] + +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.6", +] + +[[package]] +name = "p384" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" +dependencies = [ + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.6", +] + +[[package]] +name = "packed_simd_2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" +dependencies = [ + "cfg-if 1.0.0", + "libm 0.1.4", +] + +[[package]] +name = "pallet-aura" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support", + "frame-system", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-aura", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-authority-discovery" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support", + "frame-system", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-authority-discovery", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-authorship" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-babe" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-babe", + "sp-consensus-vrf", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-bags-list" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "sp-tracing", +] + +[[package]] +name = "pallet-balances" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-beefy" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support", + "frame-system", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "scale-info", + "serde", + "sp-consensus-beefy", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-beefy-mmr" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "array-bytes 4.2.0", + "binary-merkle-tree", + "frame-support", + "frame-system", + "log", + "pallet-beefy", + "pallet-mmr", + "pallet-session", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-consensus-beefy", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-bounties" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-treasury", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-bridge-beefy" +version = "0.1.0" +dependencies = [ + "bp-beefy", + "bp-runtime", + "bp-test-utils", + "ckb-merkle-mountain-range 0.3.2", + "frame-support", + "frame-system", + "log", + "pallet-beefy-mmr", + "pallet-mmr", + "parity-scale-codec", + "rand 0.8.5", + "scale-info", + "serde", + "sp-consensus-beefy", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-bridge-grandpa" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-runtime", + "bp-test-utils", + "finality-grandpa", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-consensus-grandpa", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "sp-trie", +] + +[[package]] +name = "pallet-bridge-messages" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-runtime", + "bp-test-utils", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "num-traits", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-bridge-parachains" +version = "0.1.0" +dependencies = [ + "bp-header-chain", + "bp-parachains", + "bp-polkadot-core", + "bp-runtime", + "bp-test-utils", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-bridge-grandpa", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "sp-trie", +] + +[[package]] +name = "pallet-bridge-relayers" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-relayers", + "bp-runtime", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "pallet-bridge-messages", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-child-bounties" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-bounties", + "pallet-treasury", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-collective" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-conviction-voting" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "assert_matches", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-democracy" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-election-provider-multi-phase" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-election-provider-support-benchmarking", + "parity-scale-codec", + "rand 0.8.5", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-std 5.0.0", + "strum", +] + +[[package]] +name = "pallet-election-provider-support-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-system", + "parity-scale-codec", + "sp-npos-elections", + "sp-runtime", +] + +[[package]] +name = "pallet-elections-phragmen" +version = "5.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-fast-unstake" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-grandpa" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-grandpa", + "sp-core", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-identity" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "enumflags2", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-im-online" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-indices" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-membership" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-mmr" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-mmr-primitives", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-multisig" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-nis" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-nomination-pools" +version = "1.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-nomination-pools-benchmarking" +version = "1.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "pallet-bags-list", + "pallet-nomination-pools", + "pallet-staking", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-runtime-interface", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-nomination-pools-runtime-api" +version = "1.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "pallet-nomination-pools", + "parity-scale-codec", + "sp-api", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-offences" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support", + "frame-system", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-offences-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-babe", + "pallet-balances", + "pallet-grandpa", + "pallet-im-online", + "pallet-offences", + "pallet-session", + "pallet-staking", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-preimage" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-proxy" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-ranked-collective" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-recovery" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-referenda" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "assert_matches", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-arithmetic", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-scheduler" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "sp-weights", +] + +[[package]] +name = "pallet-session" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", + "sp-trie", +] + +[[package]] +name = "pallet-session-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-session", + "pallet-staking", + "rand 0.8.5", + "sp-runtime", + "sp-session", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-shift-session-manager" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-society" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "rand_chacha 0.2.2", + "scale-info", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-staking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "rand_chacha 0.2.2", + "scale-info", + "serde", + "sp-application-crypto", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-staking-reward-curve" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "pallet-staking-reward-fn" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "log", + "sp-arithmetic", +] + +[[package]] +name = "pallet-staking-runtime-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", + "sp-api", +] + +[[package]] +name = "pallet-state-trie-migration" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-sudo" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-timestamp" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "sp-timestamp", +] + +[[package]] +name = "pallet-tips" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-treasury", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-transaction-payment" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-transaction-payment-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "jsonrpsee", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-weights", +] + +[[package]] +name = "pallet-transaction-payment-rpc-runtime-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "pallet-transaction-payment", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-weights", +] + +[[package]] +name = "pallet-treasury" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-utility" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-vesting" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-whitelist" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-xcm" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bounded-collections", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "xcm", + "xcm-executor", +] + +[[package]] +name = "pallet-xcm-benchmarks" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "parachain-info" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "parachains-relay" +version = "0.1.0" +dependencies = [ + "async-std", + "async-trait", + "bp-polkadot-core", + "futures", + "log", + "parity-scale-codec", + "relay-substrate-client", + "relay-utils", + "sp-core", +] + +[[package]] +name = "parity-db" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bfb81cf5c90a222db2fb7b3a7cbf8cc7f38dfb6647aca4d98edf8281f56ed5" +dependencies = [ + "blake2", + "crc32fast", + "fs2", + "hex", + "libc", + "log", + "lz4", + "memmap2", + "parking_lot 0.12.1", + "rand 0.8.5", + "siphasher", + "snap", +] + +[[package]] +name = "parity-scale-codec" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" +dependencies = [ + "arrayvec 0.7.2", + "bitvec", + "byte-slice-cast", + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +dependencies = [ + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "parity-send-wrapper" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" + +[[package]] +name = "parity-util-mem" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d32c34f4f5ca7f9196001c0aba5a1f9a5a12382c8944b8b0f90233282d1e8f8" +dependencies = [ + "cfg-if 1.0.0", + "ethereum-types", + "hashbrown 0.12.3", + "impl-trait-for-tuples", + "lru 0.8.1", + "parity-util-mem-derive", + "parking_lot 0.12.1", + "primitive-types", + "smallvec", + "winapi", +] + +[[package]] +name = "parity-util-mem-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" +dependencies = [ + "proc-macro2 1.0.56", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "parity-wasm" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.7", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac 0.11.1", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "pem-rfc7468" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pest" +version = "2.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cbd939b234e95d72bc393d51788aec68aeeb5d51e748ca08ff3aad58cb722f7" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a81186863f3d0a27340815be8f2078dd8050b14cd71913db9fbda795e5f707d7" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a1ef20bf3193c15ac345acb32e26b3dc3223aff4d77ae4fc5359567683796b" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "pest_meta" +version = "2.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3b284b1f13a20dc5ebc90aff59a51b8d7137c221131b52a7260c08cbc1cc80" +dependencies = [ + "once_cell", + "pest", + "sha2 0.10.6", +] + +[[package]] +name = "petgraph" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d2820d87d2b008616e5c27212dd9e0e694fb4c6b522de06094106813328cb49" +dependencies = [ + "der 0.7.1", + "spki 0.7.0", +] + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "platforms" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d0eef3571242013a0d5dc84861c3ae4a652e56e12adf8bdc26ff5f8cb34c94" + +[[package]] +name = "platforms" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" + +[[package]] +name = "polkadot-approval-distribution" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "futures", + "polkadot-node-jaeger", + "polkadot-node-metrics", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-primitives", + "rand 0.8.5", + "tracing-gum", +] + +[[package]] +name = "polkadot-availability-bitfield-distribution" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "futures", + "polkadot-node-network-protocol", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand 0.8.5", + "tracing-gum", +] + +[[package]] +name = "polkadot-availability-distribution" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "derive_more", + "fatality", + "futures", + "lru 0.9.0", + "parity-scale-codec", + "polkadot-erasure-coding", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand 0.8.5", + "sp-core", + "sp-keystore", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-availability-recovery" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "fatality", + "futures", + "lru 0.9.0", + "parity-scale-codec", + "polkadot-erasure-coding", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand 0.8.5", + "sc-network", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-cli" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "clap 4.2.2", + "frame-benchmarking-cli", + "futures", + "log", + "polkadot-client", + "polkadot-node-core-pvf", + "polkadot-node-metrics", + "polkadot-service", + "sc-cli", + "sc-executor", + "sc-service", + "sc-storage-monitor", + "sc-sysinfo", + "sc-tracing", + "sp-core", + "sp-io", + "sp-keyring", + "substrate-build-script-utils", + "thiserror", + "try-runtime-cli", +] + +[[package]] +name = "polkadot-client" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "async-trait", + "frame-benchmarking", + "frame-benchmarking-cli", + "frame-system", + "frame-system-rpc-runtime-api", + "futures", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "polkadot-core-primitives", + "polkadot-node-core-parachains-inherent", + "polkadot-primitives", + "polkadot-runtime", + "polkadot-runtime-common", + "sc-client-api", + "sc-consensus", + "sc-executor", + "sc-service", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-consensus-grandpa", + "sp-core", + "sp-inherents", + "sp-keyring", + "sp-mmr-primitives", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-storage", + "sp-timestamp", + "sp-transaction-pool", +] + +[[package]] +name = "polkadot-collator-protocol" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "always-assert", + "bitvec", + "fatality", + "futures", + "futures-timer", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sp-core", + "sp-keystore", + "sp-runtime", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-core-primitives" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "polkadot-dispute-distribution" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "derive_more", + "fatality", + "futures", + "futures-timer", + "indexmap", + "lru 0.9.0", + "parity-scale-codec", + "polkadot-erasure-coding", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sc-network", + "sp-application-crypto", + "sp-keystore", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-erasure-coding" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "parity-scale-codec", + "polkadot-node-primitives", + "polkadot-primitives", + "reed-solomon-novelpoly", + "sp-core", + "sp-trie", + "thiserror", +] + +[[package]] +name = "polkadot-gossip-support" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "futures", + "futures-timer", + "polkadot-node-network-protocol", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand 0.8.5", + "rand_chacha 0.3.1", + "sc-network", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "tracing-gum", +] + +[[package]] +name = "polkadot-network-bridge" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "always-assert", + "async-trait", + "bytes", + "fatality", + "futures", + "parity-scale-codec", + "parking_lot 0.12.1", + "polkadot-node-metrics", + "polkadot-node-network-protocol", + "polkadot-node-subsystem", + "polkadot-overseer", + "polkadot-primitives", + "sc-network", + "sp-consensus", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-collation-generation" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "futures", + "parity-scale-codec", + "polkadot-erasure-coding", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sp-core", + "sp-maybe-compressed-blob", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-approval-voting" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bitvec", + "derive_more", + "futures", + "futures-timer", + "kvdb", + "lru 0.9.0", + "merlin", + "parity-scale-codec", + "polkadot-node-jaeger", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-primitives", + "sc-keystore", + "schnorrkel", + "sp-application-crypto", + "sp-consensus", + "sp-consensus-slots", + "sp-runtime", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-av-store" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bitvec", + "futures", + "futures-timer", + "kvdb", + "parity-scale-codec", + "polkadot-erasure-coding", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-primitives", + "sp-consensus", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-backing" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bitvec", + "fatality", + "futures", + "polkadot-erasure-coding", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "polkadot-statement-table", + "sp-keystore", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-bitfield-signing" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "futures", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sp-keystore", + "thiserror", + "tracing-gum", + "wasm-timer", +] + +[[package]] +name = "polkadot-node-core-candidate-validation" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "parity-scale-codec", + "polkadot-node-core-pvf", + "polkadot-node-metrics", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-parachain", + "polkadot-primitives", + "sp-maybe-compressed-blob", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-chain-api" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "futures", + "polkadot-node-metrics", + "polkadot-node-subsystem", + "polkadot-primitives", + "sc-client-api", + "sc-consensus-babe", + "sp-blockchain", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-chain-selection" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "futures", + "futures-timer", + "kvdb", + "parity-scale-codec", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-dispute-coordinator" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "fatality", + "futures", + "kvdb", + "lru 0.9.0", + "parity-scale-codec", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sc-keystore", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-parachains-inherent" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "polkadot-node-subsystem", + "polkadot-overseer", + "polkadot-primitives", + "sp-blockchain", + "sp-inherents", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-provisioner" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bitvec", + "fatality", + "futures", + "futures-timer", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "rand 0.8.5", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-pvf" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "always-assert", + "assert_matches", + "cpu-time", + "futures", + "futures-timer", + "libc", + "parity-scale-codec", + "pin-project", + "polkadot-core-primitives", + "polkadot-node-metrics", + "polkadot-node-primitives", + "polkadot-parachain", + "polkadot-primitives", + "rand 0.8.5", + "rayon", + "sc-executor", + "sc-executor-common", + "sc-executor-wasmtime", + "slotmap", + "sp-core", + "sp-externalities", + "sp-io", + "sp-maybe-compressed-blob", + "sp-tracing", + "sp-wasm-interface", + "substrate-build-script-utils", + "tempfile", + "tikv-jemalloc-ctl", + "tokio", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-pvf-checker" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "futures", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-primitives", + "sp-keystore", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-core-runtime-api" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "futures", + "lru 0.9.0", + "polkadot-node-metrics", + "polkadot-node-subsystem", + "polkadot-node-subsystem-types", + "polkadot-primitives", + "sp-consensus-babe", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-jaeger" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "lazy_static", + "log", + "mick-jaeger", + "parity-scale-codec", + "parking_lot 0.12.1", + "polkadot-node-primitives", + "polkadot-primitives", + "sc-network", + "sp-core", + "thiserror", + "tokio", +] + +[[package]] +name = "polkadot-node-metrics" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bs58", + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "polkadot-primitives", + "prioritized-metered-channel", + "sc-cli", + "sc-service", + "sc-tracing", + "substrate-prometheus-endpoint", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-network-protocol" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "async-trait", + "derive_more", + "fatality", + "futures", + "hex", + "parity-scale-codec", + "polkadot-node-jaeger", + "polkadot-node-primitives", + "polkadot-primitives", + "rand 0.8.5", + "sc-authority-discovery", + "sc-network", + "strum", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-primitives" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bounded-vec", + "futures", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "schnorrkel", + "serde", + "sp-application-crypto", + "sp-consensus-babe", + "sp-consensus-vrf", + "sp-core", + "sp-keystore", + "sp-maybe-compressed-blob", + "sp-runtime", + "thiserror", + "zstd 0.11.2+zstd.1.5.2", +] + +[[package]] +name = "polkadot-node-subsystem" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "polkadot-node-jaeger", + "polkadot-node-subsystem-types", + "polkadot-overseer", +] + +[[package]] +name = "polkadot-node-subsystem-types" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "async-trait", + "derive_more", + "futures", + "orchestra", + "polkadot-node-jaeger", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-primitives", + "polkadot-statement-table", + "sc-network", + "smallvec", + "sp-api", + "sp-authority-discovery", + "sp-consensus-babe", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "polkadot-node-subsystem-util" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "async-trait", + "derive_more", + "fatality", + "futures", + "futures-channel", + "itertools", + "kvdb", + "lru 0.9.0", + "parity-db", + "parity-scale-codec", + "parking_lot 0.11.2", + "pin-project", + "polkadot-node-jaeger", + "polkadot-node-metrics", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-overseer", + "polkadot-primitives", + "prioritized-metered-channel", + "rand 0.8.5", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-overseer" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "lru 0.9.0", + "orchestra", + "parking_lot 0.12.1", + "polkadot-node-metrics", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem-types", + "polkadot-primitives", + "sc-client-api", + "sp-api", + "sp-core", + "tikv-jemalloc-ctl", + "tracing-gum", +] + +[[package]] +name = "polkadot-parachain" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bounded-collections", + "derive_more", + "frame-support", + "parity-scale-codec", + "polkadot-core-primitives", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "polkadot-primitives" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bitvec", + "hex-literal 0.3.4", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-authority-discovery", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "polkadot-rpc" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "jsonrpsee", + "mmr-rpc", + "pallet-transaction-payment-rpc", + "polkadot-primitives", + "sc-chain-spec", + "sc-client-api", + "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-rpc", + "sc-sync-state-rpc", + "sc-transaction-pool-api", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-keystore", + "sp-runtime", + "substrate-frame-rpc-system", + "substrate-state-trie-migration-rpc", +] + +[[package]] +name = "polkadot-runtime" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bitvec", + "frame-benchmarking", + "frame-election-provider-support", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal 0.3.4", + "log", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-bags-list", + "pallet-balances", + "pallet-bounties", + "pallet-child-bounties", + "pallet-collective", + "pallet-conviction-voting", + "pallet-democracy", + "pallet-election-provider-multi-phase", + "pallet-election-provider-support-benchmarking", + "pallet-elections-phragmen", + "pallet-fast-unstake", + "pallet-grandpa", + "pallet-identity", + "pallet-im-online", + "pallet-indices", + "pallet-membership", + "pallet-multisig", + "pallet-nomination-pools", + "pallet-nomination-pools-benchmarking", + "pallet-nomination-pools-runtime-api", + "pallet-offences", + "pallet-offences-benchmarking", + "pallet-preimage", + "pallet-proxy", + "pallet-referenda", + "pallet-scheduler", + "pallet-session", + "pallet-session-benchmarking", + "pallet-staking", + "pallet-staking-reward-curve", + "pallet-staking-runtime-api", + "pallet-timestamp", + "pallet-tips", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-vesting", + "pallet-whitelist", + "pallet-xcm", + "parity-scale-codec", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-constants", + "polkadot-runtime-parachains", + "rustc-hex", + "scale-info", + "serde", + "serde_derive", + "smallvec", + "sp-api", + "sp-arithmetic", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-core", + "sp-inherents", + "sp-io", + "sp-mmr-primitives", + "sp-npos-elections", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", + "sp-transaction-pool", + "sp-version", + "static_assertions", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "polkadot-runtime-common" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bitvec", + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "libsecp256k1", + "log", + "pallet-authorship", + "pallet-babe", + "pallet-balances", + "pallet-election-provider-multi-phase", + "pallet-fast-unstake", + "pallet-session", + "pallet-staking", + "pallet-staking-reward-fn", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-treasury", + "pallet-vesting", + "parity-scale-codec", + "polkadot-primitives", + "polkadot-runtime-parachains", + "rustc-hex", + "scale-info", + "serde", + "serde_derive", + "slot-range-helper", + "sp-api", + "sp-core", + "sp-inherents", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", + "static_assertions", + "xcm", +] + +[[package]] +name = "polkadot-runtime-constants" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "frame-support", + "polkadot-primitives", + "polkadot-runtime-common", + "smallvec", + "sp-core", + "sp-runtime", + "sp-weights", +] + +[[package]] +name = "polkadot-runtime-metrics" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bs58", + "parity-scale-codec", + "polkadot-primitives", + "sp-std 5.0.0", + "sp-tracing", +] + +[[package]] +name = "polkadot-runtime-parachains" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bitflags", + "bitvec", + "derive_more", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-balances", + "pallet-session", + "pallet-staking", + "pallet-timestamp", + "pallet-vesting", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "polkadot-runtime-metrics", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rustc-hex", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", + "static_assertions", + "xcm", + "xcm-executor", +] + +[[package]] +name = "polkadot-service" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "async-trait", + "frame-benchmarking-cli", + "frame-support", + "frame-system-rpc-runtime-api", + "futures", + "hex-literal 0.3.4", + "kusama-runtime", + "kvdb", + "kvdb-rocksdb", + "log", + "lru 0.9.0", + "mmr-gadget", + "pallet-babe", + "pallet-im-online", + "pallet-staking", + "pallet-transaction-payment-rpc-runtime-api", + "parity-db", + "polkadot-approval-distribution", + "polkadot-availability-bitfield-distribution", + "polkadot-availability-distribution", + "polkadot-availability-recovery", + "polkadot-client", + "polkadot-collator-protocol", + "polkadot-dispute-distribution", + "polkadot-gossip-support", + "polkadot-network-bridge", + "polkadot-node-collation-generation", + "polkadot-node-core-approval-voting", + "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-provisioner", + "polkadot-node-core-pvf-checker", + "polkadot-node-core-runtime-api", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-types", + "polkadot-node-subsystem-util", + "polkadot-overseer", + "polkadot-parachain", + "polkadot-primitives", + "polkadot-rpc", + "polkadot-runtime", + "polkadot-runtime-constants", + "polkadot-runtime-parachains", + "polkadot-statement-distribution", + "rococo-runtime", + "sc-authority-discovery", + "sc-basic-authorship", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-client-db", + "sc-consensus", + "sc-consensus-babe", + "sc-consensus-beefy", + "sc-consensus-grandpa", + "sc-consensus-slots", + "sc-executor", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-network-sync", + "sc-offchain", + "sc-service", + "sc-sync-state-rpc", + "sc-sysinfo", + "sc-telemetry", + "sc-transaction-pool", + "serde", + "serde_json", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-consensus-grandpa", + "sp-core", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-mmr-primitives", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-state-machine", + "sp-storage", + "sp-timestamp", + "sp-transaction-pool", + "sp-trie", + "substrate-prometheus-endpoint", + "thiserror", + "tracing-gum", + "westend-runtime", +] + +[[package]] +name = "polkadot-statement-distribution" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "arrayvec 0.5.2", + "fatality", + "futures", + "indexmap", + "parity-scale-codec", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem", + "polkadot-node-subsystem-util", + "polkadot-primitives", + "sp-keystore", + "sp-staking", + "thiserror", + "tracing-gum", +] + +[[package]] +name = "polkadot-statement-table" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "parity-scale-codec", + "polkadot-primitives", + "sp-core", +] + +[[package]] +name = "polling" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e1f879b2998099c2d69ab9605d145d5b661195627eccc680002c4918a7fb6fa" +dependencies = [ + "autocfg", + "bitflags", + "cfg-if 1.0.0", + "concurrent-queue", + "libc", + "log", + "pin-project-lite 0.2.9", + "windows-sys 0.45.0", +] + +[[package]] +name = "poly1305" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +dependencies = [ + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash 0.4.1", +] + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash 0.4.1", +] + +[[package]] +name = "polyval" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash 0.5.0", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "predicates" +version = "2.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +dependencies = [ + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2 1.0.56", + "syn 1.0.109", +] + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "uint", +] + +[[package]] +name = "prioritized-metered-channel" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382698e48a268c832d0b181ed438374a6bb708a82a8ca273bb0f61c74cf209c4" +dependencies = [ + "coarsetime", + "crossbeam-queue", + "derive_more", + "futures", + "futures-timer", + "nanorand", + "thiserror", + "tracing", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "version_check", +] + +[[package]] +name = "proc-macro-warning" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d4f284d87b9cedc2ff57223cbc4e3937cd6063c01e92c8e2a8c080df0013933" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prometheus" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +dependencies = [ + "cfg-if 1.0.0", + "fnv", + "lazy_static", + "memchr", + "parking_lot 0.12.1", + "thiserror", +] + +[[package]] +name = "prometheus-client" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cd1b99916654a69008fd66b4f9397fbe08e6e51dfe23d4417acf5d3b8cb87c" +dependencies = [ + "dtoa", + "itoa", + "parking_lot 0.12.1", + "prometheus-client-derive-text-encode", +] + +[[package]] +name = "prometheus-client-derive-text-encode" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "prost" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48e50df39172a3e7eb17e14642445da64996989bc212b583015435d39a58537" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c828f93f5ca4826f97fedcbd3f9a536c16b12cff3dbbb4a007f932bbad95b12" +dependencies = [ + "bytes", + "heck 0.4.1", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 1.0.109", + "tempfile", + "which", +] + +[[package]] +name = "prost-codec" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc34979ff898b6e141106178981ce2596c387ea6e62533facfc61a37fc879c0" +dependencies = [ + "asynchronous-codec", + "bytes", + "prost", + "thiserror", + "unsigned-varint", +] + +[[package]] +name = "prost-derive" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea9b0f8cbe5e15a8a042d030bd96668db28ecb567ec37d691971ff5731d2b1b" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "prost-types" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "379119666929a1afd7a043aa6cf96fa67a6dce9af60c88095a4686dbce4c9c88" +dependencies = [ + "prost", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-protobuf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder", +] + +[[package]] +name = "quicksink" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77de3c815e5a160b1539c6592796801df2043ae35e123b46d73380cfa57af858" +dependencies = [ + "futures-core", + "futures-sink", + "pin-project-lite 0.1.12", +] + +[[package]] +name = "quinn-proto" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4ced82a24bb281af338b9e8f94429b6eca01b4e66d899f40031f074e74c9" +dependencies = [ + "bytes", + "rand 0.8.5", + "ring", + "rustc-hash", + "rustls 0.20.8", + "slab", + "thiserror", + "tinyvec", + "tracing", + "webpki 0.22.0", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2 1.0.56", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.8", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "rbtag" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c64936fcc0b811890a9d90020f3df5cec9c604efde88af7db6a35d365132a3" +dependencies = [ + "rbtag_derive", +] + +[[package]] +name = "rbtag_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75511b710ccca8adbb211e04763bd8c78fed585b0ec188a20ed9b0dd95567c4" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + +[[package]] +name = "rcgen" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" +dependencies = [ + "pem", + "ring", + "time 0.3.20", + "x509-parser 0.13.2", + "yasna", +] + +[[package]] +name = "rcgen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +dependencies = [ + "pem", + "ring", + "time 0.3.20", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.8", + "redox_syscall 0.2.16", + "thiserror", +] + +[[package]] +name = "reed-solomon-novelpoly" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd8f48b2066e9f69ab192797d66da804d1935bf22763204ed3675740cb0f221" +dependencies = [ + "derive_more", + "fs-err", + "itertools", + "static_init 0.5.2", + "thiserror", +] + +[[package]] +name = "ref-cast" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "regalloc2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" +dependencies = [ + "fxhash", + "log", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "region" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi", +] + +[[package]] +name = "relay-bridge-hub-kusama-client" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-kusama", + "bp-bridge-hub-polkadot", + "bp-header-chain", + "bp-messages", + "bp-parachains", + "bp-polkadot", + "bp-runtime", + "bridge-runtime-common", + "parity-scale-codec", + "relay-substrate-client", + "scale-info", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "relay-bridge-hub-polkadot-client" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-kusama", + "bp-bridge-hub-polkadot", + "bp-header-chain", + "bp-kusama", + "bp-messages", + "bp-parachains", + "bp-polkadot-core", + "bp-runtime", + "bridge-runtime-common", + "parity-scale-codec", + "relay-substrate-client", + "scale-info", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "relay-bridge-hub-rococo-client" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-rococo", + "bp-bridge-hub-wococo", + "bp-header-chain", + "bp-messages", + "bp-parachains", + "bp-runtime", + "bp-wococo", + "bridge-runtime-common", + "parity-scale-codec", + "relay-substrate-client", + "scale-info", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "relay-bridge-hub-wococo-client" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-rococo", + "bp-bridge-hub-wococo", + "bp-header-chain", + "bp-messages", + "bp-parachains", + "bp-polkadot-core", + "bp-rococo", + "bp-runtime", + "bridge-runtime-common", + "parity-scale-codec", + "relay-substrate-client", + "scale-info", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "relay-kusama-client" +version = "0.1.0" +dependencies = [ + "bp-kusama", + "bp-runtime", + "relay-substrate-client", + "relay-utils", + "sp-core", +] + +[[package]] +name = "relay-millau-client" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-millau", + "bp-runtime", + "frame-support", + "frame-system", + "millau-runtime", + "pallet-transaction-payment", + "parity-scale-codec", + "relay-substrate-client", + "relay-utils", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "relay-polkadot-client" +version = "0.1.0" +dependencies = [ + "bp-polkadot", + "bp-runtime", + "relay-substrate-client", + "relay-utils", + "sp-core", +] + +[[package]] +name = "relay-rialto-client" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-rialto", + "bp-runtime", + "frame-support", + "frame-system", + "pallet-transaction-payment", + "parity-scale-codec", + "relay-substrate-client", + "relay-utils", + "rialto-runtime", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "relay-rialto-parachain-client" +version = "0.1.0" +dependencies = [ + "bp-bridge-hub-cumulus", + "bp-header-chain", + "bp-messages", + "bp-millau", + "bp-polkadot-core", + "bp-rialto-parachain", + "bp-runtime", + "bridge-runtime-common", + "parity-scale-codec", + "relay-substrate-client", + "scale-info", + "sp-core", + "sp-runtime", + "sp-weights", + "subxt", +] + +[[package]] +name = "relay-rococo-client" +version = "0.1.0" +dependencies = [ + "bp-rococo", + "bp-runtime", + "relay-substrate-client", + "relay-utils", + "sp-core", +] + +[[package]] +name = "relay-substrate-client" +version = "0.1.0" +dependencies = [ + "async-std", + "async-trait", + "bp-header-chain", + "bp-messages", + "bp-polkadot-core", + "bp-runtime", + "finality-relay", + "frame-support", + "frame-system", + "futures", + "jsonrpsee", + "log", + "num-traits", + "pallet-balances", + "pallet-bridge-messages", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-utility", + "parity-scale-codec", + "rand 0.8.5", + "relay-utils", + "sc-chain-spec", + "sc-rpc-api", + "sc-transaction-pool-api", + "scale-info", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-std 5.0.0", + "sp-trie", + "sp-version", + "thiserror", + "tokio", + "xcm", +] + +[[package]] +name = "relay-utils" +version = "0.1.0" +dependencies = [ + "ansi_term", + "anyhow", + "async-std", + "async-trait", + "backoff", + "bp-runtime", + "env_logger", + "futures", + "isahc", + "jsonpath_lib", + "log", + "num-traits", + "serde_json", + "sp-runtime", + "substrate-prometheus-endpoint", + "sysinfo", + "thiserror", + "time 0.3.20", + "tokio", +] + +[[package]] +name = "relay-westend-client" +version = "0.1.0" +dependencies = [ + "bp-runtime", + "bp-westend", + "relay-substrate-client", + "relay-utils", + "sp-core", +] + +[[package]] +name = "relay-wococo-client" +version = "0.1.0" +dependencies = [ + "bp-runtime", + "bp-wococo", + "relay-substrate-client", + "relay-utils", + "sp-core", +] + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac 0.12.1", + "zeroize", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + +[[package]] +name = "rialto-bridge-node" +version = "0.1.0" +dependencies = [ + "clap 4.2.2", + "frame-benchmarking", + "frame-benchmarking-cli", + "frame-support", + "node-inspect", + "polkadot-node-core-pvf", + "polkadot-primitives", + "polkadot-runtime-parachains", + "polkadot-service", + "rialto-runtime", + "sc-cli", + "sc-executor", + "sc-service", + "serde_json", + "sp-authority-discovery", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", + "substrate-build-script-utils", +] + +[[package]] +name = "rialto-parachain-collator" +version = "0.1.0" +dependencies = [ + "clap 4.2.2", + "cumulus-client-cli", + "cumulus-client-consensus-aura", + "cumulus-client-consensus-common", + "cumulus-client-network", + "cumulus-client-service", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-relay-chain-interface", + "frame-benchmarking", + "frame-benchmarking-cli", + "jsonrpsee", + "log", + "pallet-transaction-payment-rpc", + "parity-scale-codec", + "polkadot-cli", + "polkadot-primitives", + "polkadot-service", + "rialto-parachain-runtime", + "sc-basic-authorship", + "sc-chain-spec", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-executor", + "sc-network", + "sc-network-sync", + "sc-rpc", + "sc-rpc-api", + "sc-service", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "serde", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-keystore", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-timestamp", + "sp-transaction-pool", + "substrate-build-script-utils", + "substrate-frame-rpc-system", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "rialto-parachain-runtime" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-millau", + "bp-relayers", + "bp-rialto-parachain", + "bp-runtime", + "bridge-runtime-common", + "cumulus-pallet-aura-ext", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "hex-literal 0.4.1", + "pallet-aura", + "pallet-balances", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-relayers", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-xcm", + "parachain-info", + "parity-scale-codec", + "polkadot-parachain", + "scale-info", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std 5.0.0", + "sp-transaction-pool", + "sp-version", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "rialto-runtime" +version = "0.1.0" +dependencies = [ + "bp-messages", + "bp-millau", + "bp-relayers", + "bp-rialto", + "bp-runtime", + "bridge-runtime-common", + "env_logger", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-rpc-runtime-api", + "pallet-authority-discovery", + "pallet-babe", + "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", + "pallet-bridge-beefy", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-relayers", + "pallet-grandpa", + "pallet-mmr", + "pallet-session", + "pallet-shift-session-manager", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-xcm", + "parity-scale-codec", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "scale-info", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-core", + "sp-inherents", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std 5.0.0", + "sp-transaction-pool", + "sp-version", + "static_assertions", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rocksdb" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9562ea1d70c0cc63a34a22d977753b50cca91cc6b6527750463bd5dd8697bc" +dependencies = [ + "libc", + "librocksdb-sys", +] + +[[package]] +name = "rococo-runtime" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "binary-merkle-tree", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal 0.3.4", + "log", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-balances", + "pallet-beefy", + "pallet-beefy-mmr", + "pallet-bounties", + "pallet-child-bounties", + "pallet-collective", + "pallet-democracy", + "pallet-elections-phragmen", + "pallet-grandpa", + "pallet-identity", + "pallet-im-online", + "pallet-indices", + "pallet-membership", + "pallet-mmr", + "pallet-multisig", + "pallet-nis", + "pallet-offences", + "pallet-preimage", + "pallet-proxy", + "pallet-recovery", + "pallet-scheduler", + "pallet-session", + "pallet-society", + "pallet-staking", + "pallet-state-trie-migration", + "pallet-sudo", + "pallet-timestamp", + "pallet-tips", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-vesting", + "pallet-xcm", + "pallet-xcm-benchmarks", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "rococo-runtime-constants", + "scale-info", + "serde", + "serde_derive", + "smallvec", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-core", + "sp-inherents", + "sp-io", + "sp-mmr-primitives", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", + "sp-transaction-pool", + "sp-version", + "static_assertions", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "rococo-runtime-constants" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "frame-support", + "polkadot-primitives", + "polkadot-runtime-common", + "smallvec", + "sp-core", + "sp-runtime", + "sp-weights", +] + +[[package]] +name = "rpassword" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" +dependencies = [ + "libc", + "rtoolbox", + "winapi", +] + +[[package]] +name = "rtcp" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1919efd6d4a6a85d13388f9487549bb8e359f17198cc03ffd72f79b553873691" +dependencies = [ + "bytes", + "thiserror", + "webrtc-util", +] + +[[package]] +name = "rtnetlink" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" +dependencies = [ + "futures", + "log", + "netlink-packet-route", + "netlink-proto", + "nix", + "thiserror", + "tokio", +] + +[[package]] +name = "rtoolbox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "rtp" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a095411ff00eed7b12e4c6a118ba984d113e1079582570d56a5ee723f11f80" +dependencies = [ + "async-trait", + "bytes", + "rand 0.8.5", + "serde", + "thiserror", + "webrtc-util", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.17", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.35.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" +dependencies = [ + "bitflags", + "errno 0.2.8", + "io-lifetimes 0.7.5", + "libc", + "linux-raw-sys 0.0.46", + "windows-sys 0.42.0", +] + +[[package]] +name = "rustix" +version = "0.36.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +dependencies = [ + "bitflags", + "errno 0.2.8", + "io-lifetimes 1.0.9", + "libc", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustix" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2" +dependencies = [ + "bitflags", + "errno 0.3.0", + "io-lifetimes 1.0.9", + "libc", + "linux-raw-sys 0.3.0", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64 0.13.1", + "log", + "ring", + "sct 0.6.1", + "webpki 0.21.4", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct 0.7.0", + "webpki 0.22.0", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64 0.21.0", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + +[[package]] +name = "rw-stream-sink" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" +dependencies = [ + "futures", + "pin-project", + "static_assertions", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "safe_arch" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "sc-allocator" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "log", + "sp-core", + "sp-wasm-interface", + "thiserror", +] + +[[package]] +name = "sc-authority-discovery" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "ip_network", + "libp2p", + "log", + "parity-scale-codec", + "prost", + "prost-build", + "rand 0.8.5", + "sc-client-api", + "sc-network", + "sc-network-common", + "sp-api", + "sp-authority-discovery", + "sp-blockchain", + "sp-core", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-basic-authorship" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "sc-block-builder", + "sc-client-api", + "sc-proposer-metrics", + "sc-telemetry", + "sc-transaction-pool-api", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-inherents", + "sp-runtime", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "sc-block-builder" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", + "sc-client-api", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-core", + "sp-inherents", + "sp-runtime", +] + +[[package]] +name = "sc-chain-spec" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "memmap2", + "sc-chain-spec-derive", + "sc-client-api", + "sc-executor", + "sc-network", + "sc-telemetry", + "serde", + "serde_json", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-state-machine", +] + +[[package]] +name = "sc-chain-spec-derive" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "sc-cli" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "array-bytes 4.2.0", + "chrono", + "clap 4.2.2", + "fdlimit", + "futures", + "libp2p", + "log", + "names", + "parity-scale-codec", + "rand 0.8.5", + "regex", + "rpassword", + "sc-client-api", + "sc-client-db", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-service", + "sc-telemetry", + "sc-tracing", + "sc-utils", + "serde", + "serde_json", + "sp-blockchain", + "sp-core", + "sp-keyring", + "sp-keystore", + "sp-panic-handler", + "sp-runtime", + "sp-version", + "thiserror", + "tiny-bip39", + "tokio", +] + +[[package]] +name = "sc-client-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "fnv", + "futures", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-executor", + "sc-transaction-pool-api", + "sc-utils", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-database", + "sp-externalities", + "sp-keystore", + "sp-runtime", + "sp-state-machine", + "sp-storage", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "sc-client-db" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "hash-db", + "kvdb", + "kvdb-memorydb", + "kvdb-rocksdb", + "linked-hash-map", + "log", + "parity-db", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-client-api", + "sc-state-db", + "schnellru", + "sp-arithmetic", + "sp-blockchain", + "sp-core", + "sp-database", + "sp-runtime", + "sp-state-machine", + "sp-trie", +] + +[[package]] +name = "sc-consensus" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "libp2p", + "log", + "mockall", + "parking_lot 0.12.1", + "sc-client-api", + "sc-utils", + "serde", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-state-machine", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-consensus-aura" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "futures", + "log", + "parity-scale-codec", + "sc-block-builder", + "sc-client-api", + "sc-consensus", + "sc-consensus-slots", + "sc-telemetry", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-aura", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-consensus-babe" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "fork-tree", + "futures", + "log", + "merlin", + "num-bigint", + "num-rational", + "num-traits", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-client-api", + "sc-consensus", + "sc-consensus-epochs", + "sc-consensus-slots", + "sc-keystore", + "sc-telemetry", + "scale-info", + "schnorrkel", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-consensus-slots", + "sp-consensus-vrf", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-consensus-babe-rpc" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "futures", + "jsonrpsee", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-rpc-api", + "serde", + "sp-api", + "sp-application-crypto", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-core", + "sp-keystore", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-consensus-beefy" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "array-bytes 4.2.0", + "async-trait", + "fnv", + "futures", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-client-api", + "sc-consensus", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-network-gossip", + "sc-network-sync", + "sc-utils", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-beefy", + "sp-core", + "sp-keystore", + "sp-mmr-primitives", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", + "wasm-timer", +] + +[[package]] +name = "sc-consensus-beefy-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-consensus-beefy", + "sc-rpc", + "serde", + "sp-consensus-beefy", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-consensus-epochs" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "fork-tree", + "parity-scale-codec", + "sc-client-api", + "sc-consensus", + "sp-blockchain", + "sp-runtime", +] + +[[package]] +name = "sc-consensus-grandpa" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "ahash 0.8.3", + "array-bytes 4.2.0", + "async-trait", + "dyn-clone", + "finality-grandpa", + "fork-tree", + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-consensus", + "sc-network", + "sc-network-common", + "sc-network-gossip", + "sc-telemetry", + "sc-utils", + "serde_json", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-grandpa", + "sp-core", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-consensus-grandpa-rpc" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "finality-grandpa", + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "sc-client-api", + "sc-consensus-grandpa", + "sc-rpc", + "serde", + "sp-blockchain", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-consensus-slots" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "log", + "parity-scale-codec", + "sc-client-api", + "sc-consensus", + "sc-telemetry", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", +] + +[[package]] +name = "sc-executor" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "lru 0.8.1", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-executor-common", + "sc-executor-wasmi", + "sc-executor-wasmtime", + "sp-api", + "sp-core", + "sp-externalities", + "sp-io", + "sp-panic-handler", + "sp-runtime-interface", + "sp-trie", + "sp-version", + "sp-wasm-interface", + "tracing", + "wasmi", +] + +[[package]] +name = "sc-executor-common" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "sc-allocator", + "sp-maybe-compressed-blob", + "sp-wasm-interface", + "thiserror", + "wasm-instrument", + "wasmi", +] + +[[package]] +name = "sc-executor-wasmi" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "log", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface", + "sp-wasm-interface", + "wasmi", +] + +[[package]] +name = "sc-executor-wasmtime" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "anyhow", + "cfg-if 1.0.0", + "libc", + "log", + "once_cell", + "rustix 0.36.11", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface", + "sp-wasm-interface", + "wasmtime", +] + +[[package]] +name = "sc-informant" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "ansi_term", + "futures", + "futures-timer", + "log", + "sc-client-api", + "sc-network", + "sc-network-common", + "sp-blockchain", + "sp-runtime", +] + +[[package]] +name = "sc-keystore" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "array-bytes 4.2.0", + "async-trait", + "parking_lot 0.12.1", + "serde_json", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "thiserror", +] + +[[package]] +name = "sc-network" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "array-bytes 4.2.0", + "async-channel", + "async-trait", + "asynchronous-codec", + "bytes", + "either", + "fnv", + "futures", + "futures-timer", + "ip_network", + "libp2p", + "linked_hash_set", + "log", + "lru 0.8.1", + "mockall", + "parity-scale-codec", + "parking_lot 0.12.1", + "pin-project", + "rand 0.8.5", + "sc-block-builder", + "sc-client-api", + "sc-consensus", + "sc-network-common", + "sc-peerset", + "sc-utils", + "serde", + "serde_json", + "smallvec", + "snow", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", + "unsigned-varint", + "zeroize", +] + +[[package]] +name = "sc-network-bitswap" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "cid", + "futures", + "libp2p", + "log", + "prost", + "prost-build", + "sc-client-api", + "sc-network", + "sc-network-common", + "sp-blockchain", + "sp-runtime", + "thiserror", + "unsigned-varint", +] + +[[package]] +name = "sc-network-common" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "array-bytes 4.2.0", + "async-trait", + "bitflags", + "bytes", + "futures", + "futures-timer", + "libp2p", + "parity-scale-codec", + "prost-build", + "sc-consensus", + "sc-peerset", + "sc-utils", + "serde", + "smallvec", + "sp-blockchain", + "sp-consensus", + "sp-consensus-grandpa", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", + "zeroize", +] + +[[package]] +name = "sc-network-gossip" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "ahash 0.8.3", + "futures", + "futures-timer", + "libp2p", + "log", + "lru 0.8.1", + "sc-network", + "sc-network-common", + "sc-peerset", + "sp-runtime", + "substrate-prometheus-endpoint", + "tracing", +] + +[[package]] +name = "sc-network-light" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "array-bytes 4.2.0", + "futures", + "libp2p", + "log", + "parity-scale-codec", + "prost", + "prost-build", + "sc-client-api", + "sc-network", + "sc-network-common", + "sc-peerset", + "sp-blockchain", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-network-sync" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "array-bytes 4.2.0", + "async-trait", + "fork-tree", + "futures", + "futures-timer", + "libp2p", + "log", + "lru 0.8.1", + "mockall", + "parity-scale-codec", + "prost", + "prost-build", + "sc-client-api", + "sc-consensus", + "sc-network", + "sc-network-common", + "sc-peerset", + "sc-utils", + "smallvec", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-network-transactions" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "array-bytes 4.2.0", + "futures", + "libp2p", + "log", + "parity-scale-codec", + "pin-project", + "sc-network", + "sc-network-common", + "sc-peerset", + "sc-utils", + "sp-consensus", + "sp-runtime", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "sc-offchain" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "array-bytes 4.2.0", + "bytes", + "fnv", + "futures", + "futures-timer", + "hyper", + "hyper-rustls", + "libp2p", + "num_cpus", + "once_cell", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "sc-client-api", + "sc-network", + "sc-network-common", + "sc-peerset", + "sc-utils", + "sp-api", + "sp-core", + "sp-offchain", + "sp-runtime", + "threadpool", + "tracing", +] + +[[package]] +name = "sc-peerset" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "futures", + "libp2p", + "log", + "sc-utils", + "serde_json", + "wasm-timer", +] + +[[package]] +name = "sc-proposer-metrics" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "log", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "sc-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-rpc-api", + "sc-tracing", + "sc-transaction-pool-api", + "sc-utils", + "serde_json", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-keystore", + "sp-offchain", + "sp-rpc", + "sp-runtime", + "sp-session", + "sp-version", + "tokio", +] + +[[package]] +name = "sc-rpc-api" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "jsonrpsee", + "parity-scale-codec", + "sc-chain-spec", + "sc-transaction-pool-api", + "scale-info", + "serde", + "serde_json", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-version", + "thiserror", +] + +[[package]] +name = "sc-rpc-server" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "http", + "jsonrpsee", + "log", + "serde_json", + "substrate-prometheus-endpoint", + "tokio", + "tower", + "tower-http", +] + +[[package]] +name = "sc-rpc-spec-v2" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "array-bytes 4.2.0", + "futures", + "futures-util", + "hex", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-chain-spec", + "sc-client-api", + "sc-transaction-pool-api", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-version", + "thiserror", + "tokio-stream", +] + +[[package]] +name = "sc-service" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "directories", + "exit-future", + "futures", + "futures-timer", + "jsonrpsee", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "pin-project", + "rand 0.8.5", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-client-db", + "sc-consensus", + "sc-executor", + "sc-informant", + "sc-keystore", + "sc-network", + "sc-network-bitswap", + "sc-network-common", + "sc-network-light", + "sc-network-sync", + "sc-network-transactions", + "sc-offchain", + "sc-rpc", + "sc-rpc-server", + "sc-rpc-spec-v2", + "sc-storage-monitor", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "sc-transaction-pool-api", + "sc-utils", + "serde", + "serde_json", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime", + "sp-session", + "sp-state-machine", + "sp-storage", + "sp-transaction-pool", + "sp-transaction-storage-proof", + "sp-trie", + "sp-version", + "static_init 1.0.3", + "substrate-prometheus-endpoint", + "tempfile", + "thiserror", + "tokio", + "tracing", + "tracing-futures", +] + +[[package]] +name = "sc-state-db" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "sp-core", +] + +[[package]] +name = "sc-storage-monitor" +version = "0.1.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "clap 4.2.2", + "fs4", + "futures", + "log", + "sc-client-db", + "sc-utils", + "sp-core", + "thiserror", + "tokio", +] + +[[package]] +name = "sc-sync-state-rpc" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "jsonrpsee", + "parity-scale-codec", + "sc-chain-spec", + "sc-client-api", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-consensus-grandpa", + "serde", + "serde_json", + "sp-blockchain", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-sysinfo" +version = "6.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "futures", + "libc", + "log", + "rand 0.8.5", + "rand_pcg", + "regex", + "sc-telemetry", + "serde", + "serde_json", + "sp-core", + "sp-io", + "sp-std 5.0.0", +] + +[[package]] +name = "sc-telemetry" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "chrono", + "futures", + "libp2p", + "log", + "parking_lot 0.12.1", + "pin-project", + "rand 0.8.5", + "sc-utils", + "serde", + "serde_json", + "thiserror", + "wasm-timer", +] + +[[package]] +name = "sc-tracing" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "ansi_term", + "atty", + "chrono", + "lazy_static", + "libc", + "log", + "once_cell", + "parking_lot 0.12.1", + "regex", + "rustc-hash", + "sc-client-api", + "sc-rpc-server", + "sc-tracing-proc-macro", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-tracing", + "thiserror", + "tracing", + "tracing-log", + "tracing-subscriber", +] + +[[package]] +name = "sc-tracing-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "sc-transaction-pool" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "futures", + "futures-timer", + "linked-hash-map", + "log", + "num-traits", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-client-api", + "sc-transaction-pool-api", + "sc-utils", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-tracing", + "sp-transaction-pool", + "substrate-prometheus-endpoint", + "thiserror", +] + +[[package]] +name = "sc-transaction-pool-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "futures", + "log", + "serde", + "sp-blockchain", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "sc-utils" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-channel", + "futures", + "futures-timer", + "lazy_static", + "log", + "parking_lot 0.12.1", + "prometheus", + "sp-arithmetic", +] + +[[package]] +name = "scale-bits" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd7aca73785181cc41f0bbe017263e682b585ca660540ba569133901d013ecf" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "scale-decode" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d823d4be477fc33321f93d08fb6c2698273d044f01362dc27573a750deb7c233" +dependencies = [ + "parity-scale-codec", + "scale-bits", + "scale-info", + "thiserror", +] + +[[package]] +name = "scale-info" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cfdffd972d76b22f3d7f81c8be34b2296afd3a25e0a547bd9abe340a4dbbe97" +dependencies = [ + "bitvec", + "cfg-if 1.0.0", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "serde", +] + +[[package]] +name = "scale-info-derive" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61fa974aea2d63dd18a4ec3a49d59af9f34178c73a4f56d2f18205628d00681e" +dependencies = [ + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "scale-value" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a5e7810815bd295da73e4216d1dfbced3c7c7c7054d70fa5f6e4c58123fff4" +dependencies = [ + "either", + "frame-metadata", + "parity-scale-codec", + "scale-bits", + "scale-decode", + "scale-info", + "serde", + "thiserror", + "yap", +] + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "schnellru" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +dependencies = [ + "ahash 0.8.3", + "cfg-if 1.0.0", + "hashbrown 0.13.2", +] + +[[package]] +name = "schnorrkel" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "curve25519-dalek 2.1.3", + "getrandom 0.1.16", + "merlin", + "rand 0.7.3", + "rand_core 0.5.1", + "sha2 0.8.2", + "subtle", + "zeroize", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sdp" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d22a5ef407871893fd72b4562ee15e4742269b173959db4b8df6f538c414e13" +dependencies = [ + "rand 0.8.5", + "substring", + "thiserror", + "url", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array 0.14.6", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.1", + "generic-array 0.14.6", + "pkcs8 0.10.1", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +dependencies = [ + "cc", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +dependencies = [ + "serde", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "signal-hook" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-async-std" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4aa94397e2023af5b7cff5b8d4785e935cfb77f0e4aab0cae3b26258ace556" +dependencies = [ + "async-io", + "futures-lite", + "libc", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.4", +] + +[[package]] +name = "signature" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.4", +] + +[[package]] +name = "simba" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50582927ed6f77e4ac020c057f37a268fc6aebc29225050365aacbb9deeeddc4" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slice-group-by" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" + +[[package]] +name = "slot-range-helper" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "enumn", + "parity-scale-codec", + "paste", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "slotmap" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +dependencies = [ + "version_check", +] + +[[package]] +name = "sluice" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" +dependencies = [ + "async-channel", + "futures-core", + "futures-io", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "snap" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" + +[[package]] +name = "snow" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ccba027ba85743e09d15c03296797cad56395089b832b48b5a5217880f57733" +dependencies = [ + "aes-gcm 0.9.4", + "blake2", + "chacha20poly1305", + "curve25519-dalek 4.0.0-rc.1", + "rand_core 0.6.4", + "ring", + "rustc_version", + "sha2 0.10.6", + "subtle", +] + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "soketto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +dependencies = [ + "base64 0.13.1", + "bytes", + "flate2", + "futures", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha-1", +] + +[[package]] +name = "sp-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "scale-info", + "sp-api-proc-macro", + "sp-core", + "sp-metadata-ir", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", + "sp-trie", + "sp-version", + "thiserror", +] + +[[package]] +name = "sp-api-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "Inflector", + "blake2", + "expander 1.0.0", + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "sp-application-crypto" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-arithmetic" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-std 5.0.0", + "static_assertions", +] + +[[package]] +name = "sp-authority-discovery" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-block-builder" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", + "sp-api", + "sp-inherents", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-blockchain" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "futures", + "log", + "lru 0.8.1", + "parity-scale-codec", + "parking_lot 0.12.1", + "sp-api", + "sp-consensus", + "sp-database", + "sp-runtime", + "sp-state-machine", + "thiserror", +] + +[[package]] +name = "sp-consensus" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "futures", + "log", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", + "thiserror", +] + +[[package]] +name = "sp-consensus-aura" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-consensus", + "sp-consensus-slots", + "sp-inherents", + "sp-runtime", + "sp-std 5.0.0", + "sp-timestamp", +] + +[[package]] +name = "sp-consensus-babe" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "merlin", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-consensus", + "sp-consensus-slots", + "sp-consensus-vrf", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "sp-std 5.0.0", + "sp-timestamp", +] + +[[package]] +name = "sp-consensus-beefy" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "lazy_static", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-mmr-primitives", + "sp-runtime", + "sp-std 5.0.0", + "strum", +] + +[[package]] +name = "sp-consensus-grandpa" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "finality-grandpa", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-consensus-slots" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-std 5.0.0", + "sp-timestamp", +] + +[[package]] +name = "sp-consensus-vrf" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", + "scale-info", + "schnorrkel", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-core" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "array-bytes 4.2.0", + "bitflags", + "blake2", + "bounded-collections", + "bs58", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db", + "hash256-std-hasher", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log", + "merlin", + "parity-scale-codec", + "parking_lot 0.12.1", + "primitive-types", + "rand 0.8.5", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing 5.0.0", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std 5.0.0", + "sp-storage", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "zeroize", +] + +[[package]] +name = "sp-core-hashing" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "blake2b_simd", + "byteorder", + "digest 0.10.6", + "sha2 0.10.6", + "sha3", + "sp-std 5.0.0", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc2d1947252b7a4e403b0a260f596920443742791765ec111daa2bbf98eff25" +dependencies = [ + "blake2", + "byteorder", + "digest 0.10.6", + "sha2 0.10.6", + "sha3", + "sp-std 6.0.0", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "sp-core-hashing 5.0.0", + "syn 2.0.14", +] + +[[package]] +name = "sp-database" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "kvdb", + "parking_lot 0.12.1", +] + +[[package]] +name = "sp-debug-derive" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "sp-externalities" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std 5.0.0", + "sp-storage", +] + +[[package]] +name = "sp-inherents" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", + "thiserror", +] + +[[package]] +name = "sp-io" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "bytes", + "ed25519", + "ed25519-dalek", + "futures", + "libsecp256k1", + "log", + "parity-scale-codec", + "rustversion", + "secp256k1", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", + "sp-std 5.0.0", + "sp-tracing", + "sp-trie", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-keyring" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "lazy_static", + "sp-core", + "sp-runtime", + "strum", +] + +[[package]] +name = "sp-keystore" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "futures", + "merlin", + "parity-scale-codec", + "parking_lot 0.12.1", + "schnorrkel", + "serde", + "sp-core", + "sp-externalities", + "thiserror", +] + +[[package]] +name = "sp-maybe-compressed-blob" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "thiserror", + "zstd 0.12.3+zstd.1.5.2", +] + +[[package]] +name = "sp-metadata-ir" +version = "0.1.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-mmr-primitives" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "ckb-merkle-mountain-range 0.5.2", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-api", + "sp-core", + "sp-debug-derive", + "sp-runtime", + "sp-std 5.0.0", + "thiserror", +] + +[[package]] +name = "sp-npos-elections" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-arithmetic", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-offchain" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "sp-api", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "sp-panic-handler" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + +[[package]] +name = "sp-rpc" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "rustc-hash", + "serde", + "sp-core", +] + +[[package]] +name = "sp-runtime" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "paste", + "rand 0.8.5", + "scale-info", + "serde", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-std 5.0.0", + "sp-weights", +] + +[[package]] +name = "sp-runtime-interface" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std 5.0.0", + "sp-storage", + "sp-tracing", + "sp-wasm-interface", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "sp-session" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-staking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-state-machine" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "smallvec", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-std 5.0.0", + "sp-trie", + "thiserror", + "tracing", +] + +[[package]] +name = "sp-std" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" + +[[package]] +name = "sp-std" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af0ee286f98455272f64ac5bb1384ff21ac029fbb669afbaf48477faff12760e" + +[[package]] +name = "sp-storage" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-timestamp" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "futures-timer", + "log", + "parity-scale-codec", + "sp-inherents", + "sp-runtime", + "sp-std 5.0.0", + "thiserror", +] + +[[package]] +name = "sp-tracing" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", + "sp-std 5.0.0", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sp-transaction-pool" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "sp-api", + "sp-runtime", +] + +[[package]] +name = "sp-transaction-storage-proof" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-std 5.0.0", + "sp-trie", +] + +[[package]] +name = "sp-trie" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "ahash 0.8.3", + "hash-db", + "hashbrown 0.13.2", + "lazy_static", + "memory-db", + "nohash-hasher", + "parity-scale-codec", + "parking_lot 0.12.1", + "scale-info", + "schnellru", + "sp-core", + "sp-std 5.0.0", + "thiserror", + "tracing", + "trie-db", + "trie-root", +] + +[[package]] +name = "sp-version" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "parity-wasm", + "scale-info", + "serde", + "sp-core-hashing-proc-macro", + "sp-runtime", + "sp-std 5.0.0", + "sp-version-proc-macro", + "thiserror", +] + +[[package]] +name = "sp-version-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "sp-wasm-interface" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "anyhow", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std 5.0.0", + "wasmi", + "wasmtime", +] + +[[package]] +name = "sp-weights" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic", + "sp-core", + "sp-debug-derive", + "sp-std 5.0.0", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0445c905640145c7ea8c1993555957f65e7c46d0535b91ba501bc9bfc85522f" +dependencies = [ + "base64ct", + "der 0.7.1", +] + +[[package]] +name = "ss58-registry" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecf0bd63593ef78eca595a7fc25e9a443ca46fe69fd472f8f09f5245cdcd769d" +dependencies = [ + "Inflector", + "num-format", + "proc-macro2 1.0.56", + "quote 1.0.26", + "serde", + "serde_json", + "unicode-xid 0.2.4", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "static_init" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11b73400442027c4adedda20a9f9b7945234a5bd8d5f7e86da22bd5d0622369c" +dependencies = [ + "cfg_aliases", + "libc", + "parking_lot 0.11.2", + "static_init_macro 0.5.0", +] + +[[package]] +name = "static_init" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6" +dependencies = [ + "bitflags", + "cfg_aliases", + "libc", + "parking_lot 0.11.2", + "parking_lot_core 0.8.6", + "static_init_macro 1.0.2", + "winapi", +] + +[[package]] +name = "static_init_macro" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2261c91034a1edc3fc4d1b80e89d82714faede0515c14a75da10cb941546bbf" +dependencies = [ + "cfg_aliases", + "memchr", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "static_init_macro" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" +dependencies = [ + "cfg_aliases", + "memchr", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "storage-proof-fuzzer" +version = "0.1.0" +dependencies = [ + "bp-runtime", + "env_logger", + "honggfuzz", + "log", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", + "sp-trie", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap 2.34.0", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck 0.3.3", + "proc-macro-error", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.56", + "quote 1.0.26", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "stun" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25" +dependencies = [ + "base64 0.13.1", + "crc", + "lazy_static", + "md-5", + "rand 0.8.5", + "ring", + "subtle", + "thiserror", + "tokio", + "url", + "webrtc-util", +] + +[[package]] +name = "substrate-bip39" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" +dependencies = [ + "hmac 0.11.0", + "pbkdf2 0.8.0", + "schnorrkel", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "substrate-build-script-utils" +version = "3.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "platforms 2.0.0", +] + +[[package]] +name = "substrate-frame-rpc-system" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "frame-system-rpc-runtime-api", + "futures", + "jsonrpsee", + "log", + "parity-scale-codec", + "sc-rpc-api", + "sc-transaction-pool-api", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "substrate-prometheus-endpoint" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "hyper", + "log", + "prometheus", + "thiserror", + "tokio", +] + +[[package]] +name = "substrate-relay" +version = "1.0.1" +dependencies = [ + "anyhow", + "async-std", + "async-trait", + "bp-header-chain", + "bp-messages", + "bp-millau", + "bp-parachains", + "bp-polkadot-core", + "bp-rialto", + "bp-rialto-parachain", + "bp-runtime", + "bp-test-utils", + "bridge-runtime-common", + "finality-grandpa", + "frame-support", + "futures", + "hex", + "hex-literal 0.4.1", + "log", + "millau-runtime", + "num-format", + "num-traits", + "pallet-bridge-parachains", + "parachains-relay", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "rbtag", + "relay-bridge-hub-kusama-client", + "relay-bridge-hub-polkadot-client", + "relay-bridge-hub-rococo-client", + "relay-bridge-hub-wococo-client", + "relay-kusama-client", + "relay-millau-client", + "relay-polkadot-client", + "relay-rialto-client", + "relay-rialto-parachain-client", + "relay-rococo-client", + "relay-substrate-client", + "relay-utils", + "relay-westend-client", + "relay-wococo-client", + "rialto-runtime", + "signal-hook", + "signal-hook-async-std", + "sp-core", + "sp-keyring", + "sp-runtime", + "structopt", + "strum", + "substrate-relay-helper", + "tempfile", + "xcm", +] + +[[package]] +name = "substrate-relay-helper" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-std", + "async-trait", + "bp-header-chain", + "bp-messages", + "bp-parachains", + "bp-polkadot-core", + "bp-relayers", + "bp-rialto", + "bp-rialto-parachain", + "bp-rococo", + "bp-runtime", + "bp-wococo", + "bridge-runtime-common", + "finality-grandpa", + "finality-relay", + "frame-support", + "frame-system", + "futures", + "hex", + "log", + "messages-relay", + "num-traits", + "pallet-balances", + "pallet-bridge-grandpa", + "pallet-bridge-messages", + "pallet-bridge-parachains", + "pallet-transaction-payment", + "parachains-relay", + "parity-scale-codec", + "relay-rialto-client", + "relay-rococo-client", + "relay-substrate-client", + "relay-utils", + "relay-wococo-client", + "rialto-runtime", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "substrate-rpc-client" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "jsonrpsee", + "log", + "sc-rpc-api", + "serde", + "sp-runtime", +] + +[[package]] +name = "substrate-state-trie-migration-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "jsonrpsee", + "log", + "parity-scale-codec", + "sc-client-api", + "sc-rpc-api", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-trie", + "trie-db", +] + +[[package]] +name = "substrate-wasm-builder" +version = "5.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "ansi_term", + "build-helper", + "cargo_metadata", + "filetime", + "sp-maybe-compressed-blob", + "strum", + "tempfile", + "toml 0.7.3", + "walkdir", + "wasm-opt", +] + +[[package]] +name = "substring" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ee6433ecef213b2e72f587ef64a2f5943e7cd16fbd82dbe8bc07486c534c86" +dependencies = [ + "autocfg", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "subxt" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54639dba6a113584083968b6a8f457dedae612abe1bd214762101ca29f12e332" +dependencies = [ + "base58", + "blake2", + "derivative", + "frame-metadata", + "futures", + "getrandom 0.2.8", + "hex", + "impl-serde", + "parity-scale-codec", + "parking_lot 0.12.1", + "primitive-types", + "scale-bits", + "scale-decode", + "scale-info", + "scale-value", + "serde", + "serde_json", + "sp-core-hashing 6.0.0", + "subxt-macro", + "subxt-metadata", + "thiserror", + "tracing", +] + +[[package]] +name = "subxt-codegen" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e86cb719003f1cedf2710a6e55ca4c37aba4c989bbd3b81dd1c52af9e4827e" +dependencies = [ + "darling", + "frame-metadata", + "heck 0.4.1", + "hex", + "jsonrpsee", + "parity-scale-codec", + "proc-macro-error", + "proc-macro2 1.0.56", + "quote 1.0.26", + "scale-info", + "subxt-metadata", + "syn 1.0.109", + "tokio", +] + +[[package]] +name = "subxt-macro" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c08de402a78c4c06c3ee3702c80e519efdcb65911348e018b6998d04404916" +dependencies = [ + "darling", + "proc-macro-error", + "subxt-codegen", + "syn 1.0.109", +] + +[[package]] +name = "subxt-metadata" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2593ab5f53435e6352675af4f9851342607f37785d84c7a3fb3139550d3c35f0" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-core-hashing 6.0.0", +] + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", + "unicode-xid 0.2.4", +] + +[[package]] +name = "sysinfo" +version = "0.28.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2f3ca6693feb29a89724516f016488e9aafc7f37264f898593ee4b942f31b" +dependencies = [ + "cfg-if 1.0.0", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "winapi", +] + +[[package]] +name = "system-configuration" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" + +[[package]] +name = "tempfile" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "redox_syscall 0.3.5", + "rustix 0.37.3", + "windows-sys 0.45.0", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "thousands" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "thrift" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b82ca8f46f95b3ce96081fe3dd89160fdea970c254bb72925255d1b62aae692e" +dependencies = [ + "byteorder", + "integer-encoding", + "log", + "ordered-float", + "threadpool", +] + +[[package]] +name = "tikv-jemalloc-ctl" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37706572f4b151dff7a0146e040804e9c26fe3a3118591112f05cf12a4216c1" +dependencies = [ + "libc", + "paste", + "tikv-jemalloc-sys", +] + +[[package]] +name = "tikv-jemalloc-sys" +version = "0.5.3+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +dependencies = [ + "itoa", + "libc", + "num_threads", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +dependencies = [ + "time-core", +] + +[[package]] +name = "tiny-bip39" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" +dependencies = [ + "anyhow", + "hmac 0.12.1", + "once_cell", + "pbkdf2 0.11.0", + "rand 0.8.5", + "rustc-hash", + "sha2 0.10.6", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot 0.12.1", + "pin-project-lite 0.2.9", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.45.0", +] + +[[package]] +name = "tokio-macros" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls 0.20.8", + "tokio", + "webpki 0.22.0", +] + +[[package]] +name = "tokio-stream" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +dependencies = [ + "futures-core", + "pin-project-lite 0.2.9", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite 0.2.9", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite 0.2.9", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if 1.0.0", + "log", + "pin-project-lite 0.2.9", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-gum" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "polkadot-node-jaeger", + "polkadot-primitives", + "tracing", + "tracing-gum-proc-macro", +] + +[[package]] +name = "tracing-gum-proc-macro" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "expander 2.0.0", + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "parking_lot 0.11.2", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "trie-db" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" +dependencies = [ + "hash-db", + "hashbrown 0.13.2", + "log", + "rustc-hex", + "smallvec", +] + +[[package]] +name = "trie-root" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" +dependencies = [ + "hash-db", +] + +[[package]] +name = "trust-dns-proto" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" +dependencies = [ + "async-trait", + "cfg-if 1.0.0", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.2.3", + "ipnet", + "lazy_static", + "rand 0.8.5", + "smallvec", + "socket2", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe" +dependencies = [ + "cfg-if 1.0.0", + "futures-util", + "ipconfig", + "lazy_static", + "lru-cache", + "parking_lot 0.12.1", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tracing", + "trust-dns-proto", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "try-runtime-cli" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" +dependencies = [ + "async-trait", + "clap 4.2.2", + "frame-remote-externalities", + "hex", + "log", + "parity-scale-codec", + "sc-cli", + "sc-executor", + "sc-service", + "serde", + "serde_json", + "sp-api", + "sp-consensus-aura", + "sp-consensus-babe", + "sp-core", + "sp-debug-derive", + "sp-externalities", + "sp-inherents", + "sp-io", + "sp-keystore", + "sp-rpc", + "sp-runtime", + "sp-state-machine", + "sp-timestamp", + "sp-transaction-storage-proof", + "sp-version", + "sp-weights", + "substrate-rpc-client", + "zstd 0.12.3+zstd.1.5.2", +] + +[[package]] +name = "tt-call" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f195fd851901624eee5a58c4bb2b4f06399148fcd0ed336e6f1cb60a9881df" + +[[package]] +name = "turn" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" +dependencies = [ + "async-trait", + "base64 0.13.1", + "futures", + "log", + "md-5", + "rand 0.8.5", + "ring", + "stun", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if 1.0.0", + "digest 0.10.6", + "rand 0.8.5", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array 0.14.6", + "subtle", +] + +[[package]] +name = "universal-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "unsigned-varint" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" +dependencies = [ + "asynchronous-codec", + "bytes", + "futures-io", + "futures-util", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna 0.3.0", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" +dependencies = [ + "getrandom 0.2.8", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "value-bag" +version = "1.0.0-alpha.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" +dependencies = [ + "ctor", + "version_check", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "waitgroup" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1f50000a783467e6c0200f9d10642f4bc424e39efc1b770203e88b488f79292" +dependencies = [ + "atomic-waker", +] + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote 1.0.26", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "wasm-instrument" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa1dafb3e60065305741e83db35c6c2584bb3725b692b5b66148a38d72ace6cd" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasm-opt" +version = "0.111.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a303793cbc01fb96551badfc7367db6007396bba6bac97936b3c8b6f7fdb41" +dependencies = [ + "anyhow", + "libc", + "strum", + "strum_macros", + "tempfile", + "thiserror", + "wasm-opt-cxx-sys", + "wasm-opt-sys", +] + +[[package]] +name = "wasm-opt-cxx-sys" +version = "0.111.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c9deb56f8a9f2ec177b3bd642a8205621835944ed5da55f2388ef216aca5a4" +dependencies = [ + "anyhow", + "cxx", + "cxx-build", + "wasm-opt-sys", +] + +[[package]] +name = "wasm-opt-sys" +version = "0.111.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4432e28b542738a9776cedf92e8a99d8991c7b4667ee2c7ccddfb479dd2856a7" +dependencies = [ + "anyhow", + "cc", + "cxx", + "cxx-build", + "regex", +] + +[[package]] +name = "wasm-timer" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.11.2", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmi" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" +dependencies = [ + "parity-wasm", + "wasmi-validation", + "wasmi_core", +] + +[[package]] +name = "wasmi-validation" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasmi_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +dependencies = [ + "downcast-rs", + "libm 0.2.6", + "memory_units", + "num-rational", + "num-traits", + "region", +] + +[[package]] +name = "wasmparser" +version = "0.100.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" +dependencies = [ + "indexmap", + "url", +] + +[[package]] +name = "wasmtime" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e89f9819523447330ffd70367ef4a18d8c832e24e8150fe054d1d912841632" +dependencies = [ + "anyhow", + "bincode", + "cfg-if 1.0.0", + "indexmap", + "libc", + "log", + "object 0.29.0", + "once_cell", + "paste", + "psm", + "rayon", + "serde", + "target-lexicon", + "wasmparser", + "wasmtime-cache", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd3a5e46c198032da934469f3a6e48649d1f9142438e4fd4617b68a35644b8a" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "wasmtime-cache" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b389ae9b678b9c3851091a4804f4182d688d27aff7abc9aa37fa7be37d8ecffa" +dependencies = [ + "anyhow", + "base64 0.13.1", + "bincode", + "directories-next", + "file-per-thread-logger", + "log", + "rustix 0.36.11", + "serde", + "sha2 0.10.6", + "toml 0.5.11", + "windows-sys 0.42.0", + "zstd 0.11.2+zstd.1.5.2", +] + +[[package]] +name = "wasmtime-cranelift" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b2c92a08c0db6efffd88fdc97d7aa9c7c63b03edb0971dbca745469f820e8c" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli 0.26.2", + "log", + "object 0.29.0", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a6db9fc52985ba06ca601f2ff0ff1f526c5d724c7ac267b47326304b0c97883" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli 0.26.2", + "indexmap", + "log", + "object 0.29.0", + "serde", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-jit" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77e3a52cd84d0f7f18554afa8060cfe564ccac61e3b0802d3fd4084772fa5f6" +dependencies = [ + "addr2line 0.17.0", + "anyhow", + "bincode", + "cfg-if 1.0.0", + "cpp_demangle", + "gimli 0.26.2", + "log", + "object 0.29.0", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-debug", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" +dependencies = [ + "object 0.29.0", + "once_cell", + "rustix 0.36.11", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67d412e9340ab1c83867051d8d1d7c90aa8c9afc91da086088068e2734e25064" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d594e791b5fdd4dbaf8cf7ae62f2e4ff85018ce90f483ca6f42947688e48827d" +dependencies = [ + "anyhow", + "cc", + "cfg-if 1.0.0", + "indexmap", + "libc", + "log", + "mach", + "memfd", + "memoffset 0.6.5", + "paste", + "rand 0.8.5", + "rustix 0.36.11", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-types" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6688d6f96d4dbc1f89fab626c56c1778936d122b5f4ae7a57c2eb42b8d982e2" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror", + "wasmparser", +] + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki 0.22.0", +] + +[[package]] +name = "webrtc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3bc9049bdb2cea52f5fd4f6f728184225bdb867ed0dc2410eab6df5bdd67bb" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "hex", + "interceptor", + "lazy_static", + "log", + "rand 0.8.5", + "rcgen 0.9.3", + "regex", + "ring", + "rtcp", + "rtp", + "rustls 0.19.1", + "sdp", + "serde", + "serde_json", + "sha2 0.10.6", + "stun", + "thiserror", + "time 0.3.20", + "tokio", + "turn", + "url", + "waitgroup", + "webrtc-data", + "webrtc-dtls", + "webrtc-ice", + "webrtc-mdns", + "webrtc-media", + "webrtc-sctp", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "webrtc-data" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef36a4d12baa6e842582fe9ec16a57184ba35e1a09308307b67d43ec8883100" +dependencies = [ + "bytes", + "derive_builder", + "log", + "thiserror", + "tokio", + "webrtc-sctp", + "webrtc-util", +] + +[[package]] +name = "webrtc-dtls" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942be5bd85f072c3128396f6e5a9bfb93ca8c1939ded735d177b7bcba9a13d05" +dependencies = [ + "aes 0.6.0", + "aes-gcm 0.10.1", + "async-trait", + "bincode", + "block-modes", + "byteorder", + "ccm", + "curve25519-dalek 3.2.0", + "der-parser 8.2.0", + "elliptic-curve 0.12.3", + "hkdf", + "hmac 0.12.1", + "log", + "oid-registry 0.6.1", + "p256", + "p384", + "rand 0.8.5", + "rand_core 0.6.4", + "rcgen 0.9.3", + "ring", + "rustls 0.19.1", + "sec1 0.3.0", + "serde", + "sha1", + "sha2 0.10.6", + "signature 1.6.4", + "subtle", + "thiserror", + "tokio", + "webpki 0.21.4", + "webrtc-util", + "x25519-dalek 2.0.0-pre.1", + "x509-parser 0.13.2", +] + +[[package]] +name = "webrtc-ice" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80" +dependencies = [ + "arc-swap", + "async-trait", + "crc", + "log", + "rand 0.8.5", + "serde", + "serde_json", + "stun", + "thiserror", + "tokio", + "turn", + "url", + "uuid", + "waitgroup", + "webrtc-mdns", + "webrtc-util", +] + +[[package]] +name = "webrtc-mdns" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f08dfd7a6e3987e255c4dbe710dde5d94d0f0574f8a21afa95d171376c143106" +dependencies = [ + "log", + "socket2", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-media" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2a3c157a040324e5049bcbd644ffc9079e6738fa2cfab2bcff64e5cc4c00d7" +dependencies = [ + "byteorder", + "bytes", + "derive_builder", + "displaydoc", + "rand 0.8.5", + "rtp", + "thiserror", + "webrtc-util", +] + +[[package]] +name = "webrtc-sctp" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d47adcd9427eb3ede33d5a7f3424038f63c965491beafcc20bc650a2f6679c0" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "crc", + "log", + "rand 0.8.5", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-srtp" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6183edc4c1c6c0175f8812eefdce84dfa0aea9c3ece71c2bf6ddd3c964de3da5" +dependencies = [ + "aead 0.4.3", + "aes 0.7.5", + "aes-gcm 0.9.4", + "async-trait", + "byteorder", + "bytes", + "ctr 0.8.0", + "hmac 0.11.0", + "log", + "rtcp", + "rtp", + "sha-1", + "subtle", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-util" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" +dependencies = [ + "async-trait", + "bitflags", + "bytes", + "cc", + "ipnet", + "lazy_static", + "libc", + "log", + "nix", + "rand 0.8.5", + "thiserror", + "tokio", + "winapi", +] + +[[package]] +name = "westend-runtime" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bitvec", + "frame-benchmarking", + "frame-election-provider-support", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal 0.3.4", + "log", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-bags-list", + "pallet-balances", + "pallet-collective", + "pallet-democracy", + "pallet-election-provider-multi-phase", + "pallet-election-provider-support-benchmarking", + "pallet-elections-phragmen", + "pallet-fast-unstake", + "pallet-grandpa", + "pallet-identity", + "pallet-im-online", + "pallet-indices", + "pallet-membership", + "pallet-multisig", + "pallet-nomination-pools", + "pallet-nomination-pools-benchmarking", + "pallet-nomination-pools-runtime-api", + "pallet-offences", + "pallet-offences-benchmarking", + "pallet-preimage", + "pallet-proxy", + "pallet-recovery", + "pallet-scheduler", + "pallet-session", + "pallet-session-benchmarking", + "pallet-society", + "pallet-staking", + "pallet-staking-reward-curve", + "pallet-staking-runtime-api", + "pallet-state-trie-migration", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-vesting", + "pallet-xcm", + "pallet-xcm-benchmarks", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "polkadot-runtime-common", + "polkadot-runtime-parachains", + "rustc-hex", + "scale-info", + "serde", + "serde_derive", + "smallvec", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-core", + "sp-inherents", + "sp-io", + "sp-mmr-primitives", + "sp-npos-elections", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", + "sp-transaction-pool", + "sp-version", + "substrate-wasm-builder", + "westend-runtime-constants", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "westend-runtime-constants" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "frame-support", + "polkadot-primitives", + "polkadot-runtime-common", + "smallvec", + "sp-core", + "sp-runtime", + "sp-weights", +] + +[[package]] +name = "which" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell", +] + +[[package]] +name = "wide" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b689b6c49d6549434bf944e6b0f39238cf63693cb7a147e9d887507fffa3b223" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "widestring" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f" +dependencies = [ + "windows_aarch64_msvc 0.34.0", + "windows_i686_gnu 0.34.0", + "windows_i686_msvc 0.34.0", + "windows_x86_64_gnu 0.34.0", + "windows_x86_64_msvc 0.34.0", +] + +[[package]] +name = "windows" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deac0939bd6e4f24ab5919fbf751c97a8cfc8543bb083a305ed5c0c10bb241d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x25519-dalek" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" +dependencies = [ + "curve25519-dalek 3.2.0", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "x25519-dalek" +version = "2.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5da623d8af10a62342bcbbb230e33e58a63255a58012f8653c578e54bab48df" +dependencies = [ + "curve25519-dalek 3.2.0", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9bace5b5589ffead1afb76e43e34cff39cd0f3ce7e170ae0c29e53b88eb1c" +dependencies = [ + "asn1-rs 0.3.1", + "base64 0.13.1", + "data-encoding", + "der-parser 7.0.0", + "lazy_static", + "nom", + "oid-registry 0.4.0", + "ring", + "rusticata-macros", + "thiserror", + "time 0.3.20", +] + +[[package]] +name = "x509-parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" +dependencies = [ + "asn1-rs 0.5.2", + "base64 0.13.1", + "data-encoding", + "der-parser 8.2.0", + "lazy_static", + "nom", + "oid-registry 0.6.1", + "rusticata-macros", + "thiserror", + "time 0.3.20", +] + +[[package]] +name = "xcm" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "bounded-collections", + "derivative", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-weights", + "xcm-procedural", +] + +[[package]] +name = "xcm-builder" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log", + "pallet-transaction-payment", + "parity-scale-codec", + "polkadot-parachain", + "scale-info", + "sp-arithmetic", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "xcm", + "xcm-executor", +] + +[[package]] +name = "xcm-executor" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "environmental", + "frame-benchmarking", + "frame-support", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 5.0.0", + "sp-weights", + "xcm", +] + +[[package]] +name = "xcm-procedural" +version = "0.9.39" +source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" +dependencies = [ + "Inflector", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 2.0.14", +] + +[[package]] +name = "yamux" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d9ba232399af1783a58d8eb26f6b5006fbefe2dc9ef36bd283324792d03ea5" +dependencies = [ + "futures", + "log", + "nohash-hasher", + "parking_lot 0.12.1", + "rand 0.8.5", + "static_assertions", +] + +[[package]] +name = "yap" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc77f52dc9e9b10d55d3f4462c3b7fc393c4f17975d641542833ab2d3bc26ef" + +[[package]] +name = "yasna" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aed2e7a52e3744ab4d0c05c20aa065258e84c49fd4226f5191b2ed29712710b4" +dependencies = [ + "time 0.3.20", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +dependencies = [ + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe 5.0.2+zstd.1.5.2", +] + +[[package]] +name = "zstd" +version = "0.12.3+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +dependencies = [ + "zstd-safe 6.0.5+zstd.1.5.4", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-safe" +version = "6.0.5+zstd.1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.7+zstd.1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" +dependencies = [ + "cc", + "libc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000000..8048b9fcdcd --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,59 @@ +[workspace] +resolver = "2" + +members = [ + "bin/millau/node", + "bin/millau/runtime", + "bin/rialto/node", + "bin/rialto/runtime", + "bin/rialto-parachain/node", + "bin/rialto-parachain/runtime", + "bin/runtime-common", + "fuzz/storage-proof", + "modules/beefy", + "modules/grandpa", + "modules/messages", + "modules/parachains", + "modules/relayers", + "modules/shift-session-manager", + "primitives/beefy", + "primitives/chain-bridge-hub-cumulus", + "primitives/chain-bridge-hub-kusama", + "primitives/chain-bridge-hub-polkadot", + "primitives/chain-bridge-hub-rococo", + "primitives/chain-bridge-hub-wococo", + "primitives/chain-kusama", + "primitives/chain-millau", + "primitives/chain-polkadot", + "primitives/chain-rialto", + "primitives/chain-rialto-parachain", + "primitives/chain-rococo", + "primitives/chain-westend", + "primitives/chain-wococo", + "primitives/header-chain", + "primitives/messages", + "primitives/parachains", + "primitives/polkadot-core", + "primitives/relayers", + "primitives/runtime", + "primitives/test-utils", + "relays/bin-substrate", + "relays/client-bridge-hub-kusama", + "relays/client-bridge-hub-polkadot", + "relays/client-bridge-hub-rococo", + "relays/client-bridge-hub-wococo", + "relays/client-kusama", + "relays/client-millau", + "relays/client-polkadot", + "relays/client-rialto", + "relays/client-rialto-parachain", + "relays/client-rococo", + "relays/client-substrate", + "relays/client-westend", + "relays/client-wococo", + "relays/finality", + "relays/lib-substrate-relay", + "relays/messages", + "relays/parachains", + "relays/utils", +] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000000..a64b59e7081 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,72 @@ +# Builds images used by the bridge. +# +# In particular, it can be used to build Substrate nodes and bridge relayers. The binary that gets +# built can be specified with the `PROJECT` build-arg. For example, to build the `substrate-relay` +# you would do the following: +# +# `docker build . -t local/substrate-relay --build-arg=PROJECT=substrate-relay` +# +# See the `deployments/README.md` for all the available `PROJECT` values. + +FROM docker.io/paritytech/bridges-ci:production as builder +USER root +WORKDIR /parity-bridges-common + +COPY . . + +ARG PROJECT=substrate-relay +RUN cargo build --release --verbose -p ${PROJECT} && \ + strip ./target/release/${PROJECT} + +# In this final stage we copy over the final binary and do some checks +# to make sure that everything looks good. +FROM docker.io/library/ubuntu:20.04 as runtime + +# show backtraces +ENV RUST_BACKTRACE 1 +ENV DEBIAN_FRONTEND=noninteractive + +RUN set -eux; \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + curl ca-certificates libssl-dev && \ + update-ca-certificates && \ + groupadd -g 1000 user && \ + useradd -u 1000 -g user -s /bin/sh -m user && \ + # apt clean up + apt-get autoremove -y && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# switch to non-root user +USER user + +WORKDIR /home/user + +ARG PROJECT=substrate-relay + +COPY --chown=user:user --from=builder /parity-bridges-common/target/release/${PROJECT} ./ +COPY --chown=user:user --from=builder /parity-bridges-common/deployments/local-scripts/bridge-entrypoint.sh ./ + +# check if executable works in this container +RUN ./${PROJECT} --version + +ENV PROJECT=$PROJECT +ENTRYPOINT ["/home/user/bridge-entrypoint.sh"] + +# metadata +ARG VCS_REF=master +ARG BUILD_DATE="" +ARG VERSION="" + +LABEL org.opencontainers.image.title="${PROJECT}" \ + org.opencontainers.image.description="${PROJECT} - component of Parity Bridges Common" \ + org.opencontainers.image.source="https://github.com/paritytech/parity-bridges-common/blob/${VCS_REF}/Dockerfile" \ + org.opencontainers.image.url="https://github.com/paritytech/parity-bridges-common/blob/${VCS_REF}/Dockerfile" \ + org.opencontainers.image.documentation="https://github.com/paritytech/parity-bridges-common/blob/${VCS_REF}/README.md" \ + org.opencontainers.image.created="${BUILD_DATE}" \ + org.opencontainers.image.version="${VERSION}" \ + org.opencontainers.image.revision="${VCS_REF}" \ + org.opencontainers.image.authors="devops-team@parity.io" \ + org.opencontainers.image.vendor="Parity Technologies" \ + org.opencontainers.image.licenses="GPL-3.0 License" diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000000..733c072369c --- /dev/null +++ b/LICENSE @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/README.md b/README.md new file mode 100644 index 00000000000..aab6007d2cd --- /dev/null +++ b/README.md @@ -0,0 +1,259 @@ +# Parity Bridges Common + +This is a collection of components for building bridges. + +These components include Substrate pallets for syncing headers, passing arbitrary messages, as well +as libraries for building relayers to provide cross-chain communication capabilities. + +Three bridge nodes are also available. The nodes can be used to run test networks which bridge other +Substrate chains. + +🚧 The bridges are currently under construction - a hardhat is recommended beyond this point 🚧 + +## Contents + +- [Installation](#installation) +- [High-Level Architecture](#high-level-architecture) +- [Project Layout](#project-layout) +- [Running the Bridge](#running-the-bridge) +- [How to send a message](#how-to-send-a-message) +- [Community](#community) + +## Installation + +To get up and running you need both stable and nightly Rust. Rust nightly is used to build the Web +Assembly (WASM) runtime for the node. You can configure the WASM support as so: + +```bash +rustup install nightly +rustup target add wasm32-unknown-unknown --toolchain nightly +``` + +Once this is configured you can build and test the repo as follows: + +``` +git clone https://github.com/paritytech/parity-bridges-common.git +cd parity-bridges-common +cargo build --all +cargo test --all +``` + +Also you can build the repo with +[Parity CI Docker image](https://github.com/paritytech/scripts/tree/master/dockerfiles/bridges-ci): + +```bash +docker pull paritytech/bridges-ci:production +mkdir ~/cache +chown 1000:1000 ~/cache #processes in the container runs as "nonroot" user with UID 1000 +docker run --rm -it -w /shellhere/parity-bridges-common \ + -v /home/$(whoami)/cache/:/cache/ \ + -v "$(pwd)":/shellhere/parity-bridges-common \ + -e CARGO_HOME=/cache/cargo/ \ + -e SCCACHE_DIR=/cache/sccache/ \ + -e CARGO_TARGET_DIR=/cache/target/ paritytech/bridges-ci:production cargo build --all +#artifacts can be found in ~/cache/target +``` + +If you want to reproduce other steps of CI process you can use the following +[guide](https://github.com/paritytech/scripts#reproduce-ci-locally). + +If you need more information about setting up your development environment [Substrate's +Installation page](https://docs.substrate.io/main-docs/install/) is a good +resource. + +## High-Level Architecture + +This repo has support for bridging foreign chains together using a combination of Substrate pallets +and external processes called relayers. A bridge chain is one that is able to follow the consensus +of a foreign chain independently. For example, consider the case below where we want to bridge two +Substrate based chains. + +``` ++---------------+ +---------------+ +| | | | +| Rialto | | Millau | +| | | | ++-------+-------+ +-------+-------+ + ^ ^ + | +---------------+ | + | | | | + +-----> | Bridge Relay | <-------+ + | | + +---------------+ +``` + +The Millau chain must be able to accept Rialto headers and verify their integrity. It does this by +using a runtime module designed to track GRANDPA finality. Since two blockchains can't interact +directly they need an external service, called a relayer, to communicate. The relayer will subscribe +to new Rialto headers via RPC and submit them to the Millau chain for verification. + +Take a look at [Bridge High Level Documentation](./docs/high-level-overview.md) for more in-depth +description of the bridge interaction. + +## Project Layout + +Here's an overview of how the project is laid out. The main bits are the `bin`, which is the actual +"blockchain", the `modules` which are used to build the blockchain's logic (a.k.a the runtime) and +the `relays` which are used to pass messages between chains. + +``` +├── bin // Node and Runtime for the various Substrate chains +│ └── ... +├── deployments // Useful tools for deploying test networks +│ └── ... +├── modules // Substrate Runtime Modules (a.k.a Pallets) +│ ├── beefy // On-Chain BEEFY Light Client (in progress) +│ ├── grandpa // On-Chain GRANDPA Light Client +│ ├── messages // Cross Chain Message Passing +│ ├── parachains // On-Chain Parachains Light Client +│ ├── relayers // Relayer rewards registry +│ └── ... +├── primitives // Code shared between modules, runtimes, and relays +│ └── ... +├── relays // Application for sending finality proofs and messages between chains +│ └── ... +└── scripts // Useful development and maintenance scripts +``` + +## Running the Bridge + +To run the Bridge you need to be able to connect the bridge relay node to the RPC interface of nodes +on each side of the bridge (source and target chain). + +There are 2 ways to run the bridge, described below: + +- building & running from source: with this option, you'll be able to run the bridge between two standalone +chains that are running GRANDPA finality gadget to achieve finality; + +- running a Docker Compose setup: this is a recommended option, where you'll see bridges with parachains, +complex relays and more. + +### Using the Source + +First you'll need to build the bridge nodes and relay. This can be done as follows: + +```bash +# In `parity-bridges-common` folder +cargo build -p rialto-bridge-node +cargo build -p millau-bridge-node +cargo build -p substrate-relay +``` + +### Running a Dev network + +We will launch a dev network to demonstrate how to relay a message between two Substrate based +chains (named Rialto and Millau). + +To do this we will need two nodes, two relayers which will relay headers, and two relayers which +will relay messages. + +#### Running from local scripts + +To run a simple dev network you can use the scripts located in the +[`deployments/local-scripts` folder](./deployments/local-scripts). + +First, we must run the two Substrate nodes. + +```bash +# In `parity-bridges-common` folder +./deployments/local-scripts/run-rialto-node.sh +./deployments/local-scripts/run-millau-node.sh +``` + +After the nodes are up we can run the header relayers. + +```bash +./deployments/local-scripts/relay-millau-to-rialto.sh +./deployments/local-scripts/relay-rialto-to-millau.sh +``` + +At this point you should see the relayer submitting headers from the Millau Substrate chain to the +Rialto Substrate chain. + +``` +# Header Relayer Logs +[Millau_to_Rialto_Sync] [date] DEBUG bridge Going to submit finality proof of Millau header #147 to Rialto +[...] [date] INFO bridge Synced 147 of 147 headers +[...] [date] DEBUG bridge Going to submit finality proof of Millau header #148 to Rialto +[...] [date] INFO bridge Synced 148 of 149 headers +``` + +Finally, we can run the message relayers. + +```bash +./deployments/local-scripts/relay-messages-millau-to-rialto.sh +./deployments/local-scripts/relay-messages-rialto-to-millau.sh +``` + +You will also see the message lane relayers listening for new messages. + +``` +# Message Relayer Logs +[Millau_to_Rialto_MessageLane_00000000] [date] DEBUG bridge Asking Millau::ReceivingConfirmationsDelivery about best message nonces +[...] [date] INFO bridge Synced Some(2) of Some(3) nonces in Millau::MessagesDelivery -> Rialto::MessagesDelivery race +[...] [date] DEBUG bridge Asking Millau::MessagesDelivery about message nonces +[...] [date] DEBUG bridge Received best nonces from Millau::ReceivingConfirmationsDelivery: TargetClientNonces { latest_nonce: 0, nonces_data: () } +[...] [date] DEBUG bridge Asking Millau::ReceivingConfirmationsDelivery about finalized message nonces +[...] [date] DEBUG bridge Received finalized nonces from Millau::ReceivingConfirmationsDelivery: TargetClientNonces { latest_nonce: 0, nonces_data: () } +[...] [date] DEBUG bridge Received nonces from Millau::MessagesDelivery: SourceClientNonces { new_nonces: {}, confirmed_nonce: Some(0) } +[...] [date] DEBUG bridge Asking Millau node about its state +[...] [date] DEBUG bridge Received state from Millau node: ClientState { best_self: HeaderId(1593, 0xacac***), best_finalized_self: HeaderId(1590, 0x0be81d...), best_finalized_peer_at_best_self: HeaderId(0, 0xdcdd89...) } +``` + +To send a message see the ["How to send a message" section](#how-to-send-a-message). + +### How to send a message + +In this section we'll show you how to quickly send a bridge message. The message is just an encoded XCM +`Trap(43)` message. + +```bash +# In `parity-bridges-common` folder +./scripts/send-message-from-millau-rialto.sh +``` + +After sending a message you will see the following logs showing a message was successfully sent: + +``` +INFO bridge Sending message to Rialto. Size: 11. +TRACE bridge Sent transaction to Millau node: 0x5e68... +``` + +And at the Rialto node logs you'll something like this: + +``` +... runtime::bridge-messages: Received messages: total=1, valid=1. Weight used: Weight(ref_time: 1215065371, proof_size: 48559)/Weight(ref_time: 1215065371, proof_size: 54703). +``` + +It means that the message has been delivered and dispatched. Message may be dispatched with an +error, though - the goal of our test bridge is to ensure that messages are successfully delivered +and all involved components are working. + +## Full Network Docker Compose Setup + +For a more sophisticated deployment which includes bidirectional header sync, message passing, +monitoring dashboards, etc. see the [Deployments README](./deployments/README.md). + +You should note that you can find images for all the bridge components published on +[Docker Hub](https://hub.docker.com/u/paritytech). + +To run a Rialto node for example, you can use the following command: + +```bash +docker run -p 30333:30333 -p 9933:9933 -p 9944:9944 \ + -it paritytech/rialto-bridge-node --dev --tmp \ + --rpc-cors=all --unsafe-rpc-external --unsafe-ws-external +``` + +## Community + +Main hangout for the community is [Element](https://element.io/) (formerly Riot). Element is a chat +server like, for example, Discord. Most discussions around Polkadot and Substrate happen +in various Element "rooms" (channels). So, joining Element might be a good idea, anyway. + +If you are interested in information exchange and development of Polkadot related bridges please +feel free to join the [Polkadot Bridges](https://app.element.io/#/room/#bridges:web3.foundation) +Element channel. + +The [Substrate Technical](https://app.element.io/#/room/#substrate-technical:matrix.org) Element +channel is most suited for discussions regarding Substrate itself. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..65f2f3bff05 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,14 @@ +# Security Policy + +Thanks for helping make the Parity ecosystem more secure. Security is one of our first priorities. + +## Reporting a vulnerability + +If you find something that can be treated as a security vulnerability, please do not use the issue tracker or discuss it in the public forum as it can cause more damage, rather than giving real help to the ecosystem. + +Security vulnerabilities should be reported by the [contact form](https://security-submission.parity.io/). + +If you think that your report might be eligible for the Bug Bounty Program, please mark this during the submission. Please check up-to-date [Parity Bug Bounty Program rules](https://www.parity.io/bug-bounty) to find out the information about our Bug Bounty Program. + +**Warning**: This is an unified SECURITY.md file for Paritytech GitHub Organization. The presence of this file does not mean that this repository is covered by the Bug Bounty program. Please always check the Bug Bounty Program scope for information. + diff --git a/bin/.keep b/bin/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/bin/millau/node/Cargo.toml b/bin/millau/node/Cargo.toml new file mode 100644 index 00000000000..21b5cd02559 --- /dev/null +++ b/bin/millau/node/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "millau-bridge-node" +description = "Substrate node compatible with Millau runtime" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +build = "build.rs" +repository = "https://github.com/paritytech/parity-bridges-common/" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +clap = { version = "4.2.2", features = ["derive"] } +jsonrpsee = { version = "0.16.2", features = ["server"] } +serde_json = "1.0.96" + +# Bridge dependencies + +millau-runtime = { path = "../runtime" } + +# Substrate Dependencies + +sc-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-consensus-beefy-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } +node-inspect = { git = "https://github.com/paritytech/substrate", branch = "master" } +mmr-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-basic-authorship = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master"} +sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-consensus-grandpa-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-service = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-telemetry = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master" } +substrate-frame-rpc-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-network-common = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[build-dependencies] +substrate-build-script-utils = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = [] +runtime-benchmarks = [ + "millau-runtime/runtime-benchmarks", +] diff --git a/bin/millau/node/build.rs b/bin/millau/node/build.rs new file mode 100644 index 00000000000..d9b50049e26 --- /dev/null +++ b/bin/millau/node/build.rs @@ -0,0 +1,23 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; + +fn main() { + generate_cargo_keys(); + + rerun_if_git_head_changed(); +} diff --git a/bin/millau/node/src/chain_spec.rs b/bin/millau/node/src/chain_spec.rs new file mode 100644 index 00000000000..8669ca92cc8 --- /dev/null +++ b/bin/millau/node/src/chain_spec.rs @@ -0,0 +1,235 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use millau_runtime::{ + AccountId, AuraConfig, BalancesConfig, BeefyConfig, BridgeRialtoMessagesConfig, + BridgeRialtoParachainMessagesConfig, BridgeWestendGrandpaConfig, GenesisConfig, GrandpaConfig, + SessionConfig, SessionKeys, Signature, SudoConfig, SystemConfig, WASM_BINARY, +}; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_consensus_beefy::crypto::AuthorityId as BeefyId; +use sp_consensus_grandpa::AuthorityId as GrandpaId; +use sp_core::{sr25519, Pair, Public}; +use sp_runtime::traits::{IdentifyAccount, Verify}; + +/// "Names" of the authorities accounts at local testnet. +const LOCAL_AUTHORITIES_ACCOUNTS: [&str; 5] = ["Alice", "Bob", "Charlie", "Dave", "Eve"]; +/// "Names" of the authorities accounts at development testnet. +const DEV_AUTHORITIES_ACCOUNTS: [&str; 1] = [LOCAL_AUTHORITIES_ACCOUNTS[0]]; +/// "Names" of all possible authorities accounts. +const ALL_AUTHORITIES_ACCOUNTS: [&str; 5] = LOCAL_AUTHORITIES_ACCOUNTS; +/// "Name" of the `sudo` account. +const SUDO_ACCOUNT: &str = "Sudo"; +/// "Name" of the account, which owns the with-Westend GRANDPA pallet. +const WESTEND_GRANDPA_PALLET_OWNER: &str = "Westend.GrandpaOwner"; +/// "Name" of the account, which owns the with-Rialto messages pallet. +const RIALTO_MESSAGES_PALLET_OWNER: &str = "Rialto.MessagesOwner"; +/// "Name" of the account, which owns the with-RialtoParachain messages pallet. +const RIALTO_PARACHAIN_MESSAGES_PALLET_OWNER: &str = "RialtoParachain.MessagesOwner"; + +/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. +pub type ChainSpec = sc_service::GenericChainSpec; + +/// The chain specification option. This is expected to come in from the CLI and +/// is little more than one of a number of alternatives which can easily be converted +/// from a string (`--chain=...`) into a `ChainSpec`. +#[derive(Clone, Debug)] +pub enum Alternative { + /// Whatever the current runtime is, with just Alice as an auth. + Development, + /// Whatever the current runtime is, with simple Alice/Bob/Charlie/Dave/Eve auths. + LocalTestnet, +} + +/// Helper function to generate a crypto pair from seed +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{seed}"), None) + .expect("static values are valid; qed") + .public() +} + +type AccountPublic = ::Signer; + +/// Helper function to generate an account ID from seed +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// Helper function to generate an authority key for Aura +pub fn get_authority_keys_from_seed(s: &str) -> (AccountId, AuraId, BeefyId, GrandpaId) { + ( + get_account_id_from_seed::(s), + get_from_seed::(s), + get_from_seed::(s), + get_from_seed::(s), + ) +} + +impl Alternative { + /// Get an actual chain config from one of the alternatives. + pub(crate) fn load(self) -> ChainSpec { + let properties = Some( + serde_json::json!({ + "tokenDecimals": 9, + "tokenSymbol": "MLAU" + }) + .as_object() + .expect("Map given; qed") + .clone(), + ); + match self { + Alternative::Development => ChainSpec::from_genesis( + "Millau Development", + "millau_dev", + sc_service::ChainType::Development, + || { + testnet_genesis( + DEV_AUTHORITIES_ACCOUNTS + .into_iter() + .map(get_authority_keys_from_seed) + .collect(), + get_account_id_from_seed::(SUDO_ACCOUNT), + endowed_accounts(), + true, + ) + }, + vec![], + None, + None, + None, + properties, + None, + ), + Alternative::LocalTestnet => ChainSpec::from_genesis( + "Millau Local", + "millau_local", + sc_service::ChainType::Local, + || { + testnet_genesis( + LOCAL_AUTHORITIES_ACCOUNTS + .into_iter() + .map(get_authority_keys_from_seed) + .collect(), + get_account_id_from_seed::(SUDO_ACCOUNT), + endowed_accounts(), + true, + ) + }, + vec![], + None, + None, + None, + properties, + None, + ), + } + } +} + +/// We're using the same set of endowed accounts on all Millau chains (dev/local) to make +/// sure that all accounts, required for bridge to be functional (e.g. relayers fund account, +/// accounts used by relayers in our test deployments, accounts used for demonstration +/// purposes), are all available on these chains. +fn endowed_accounts() -> Vec { + let all_authorities = ALL_AUTHORITIES_ACCOUNTS.iter().flat_map(|x| { + [ + get_account_id_from_seed::(x), + get_account_id_from_seed::(&format!("{x}//stash")), + ] + }); + vec![ + // Sudo account + get_account_id_from_seed::(SUDO_ACCOUNT), + // Regular (unused) accounts + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Ferdie//stash"), + // Accounts, used by Westend<>Millau bridge + get_account_id_from_seed::(WESTEND_GRANDPA_PALLET_OWNER), + get_account_id_from_seed::("Westend.HeadersRelay1"), + get_account_id_from_seed::("Westend.HeadersRelay2"), + get_account_id_from_seed::("Westend.WestmintHeaders1"), + get_account_id_from_seed::("Westend.WestmintHeaders2"), + // Accounts, used by Rialto<>Millau bridge + get_account_id_from_seed::(RIALTO_MESSAGES_PALLET_OWNER), + get_account_id_from_seed::("Rialto.HeadersAndMessagesRelay"), + get_account_id_from_seed::("Rialto.OutboundMessagesRelay.Lane00000001"), + get_account_id_from_seed::("Rialto.InboundMessagesRelay.Lane00000001"), + get_account_id_from_seed::("Rialto.MessagesSender"), + // Accounts, used by RialtoParachain<>Millau bridge + get_account_id_from_seed::(RIALTO_PARACHAIN_MESSAGES_PALLET_OWNER), + get_account_id_from_seed::("RialtoParachain.HeadersAndMessagesRelay1"), + get_account_id_from_seed::("RialtoParachain.HeadersAndMessagesRelay2"), + get_account_id_from_seed::("RialtoParachain.RialtoHeadersRelay1"), + get_account_id_from_seed::("RialtoParachain.RialtoHeadersRelay2"), + get_account_id_from_seed::("RialtoParachain.MessagesSender"), + ] + .into_iter() + .chain(all_authorities) + .collect() +} + +fn session_keys(aura: AuraId, beefy: BeefyId, grandpa: GrandpaId) -> SessionKeys { + SessionKeys { aura, beefy, grandpa } +} + +fn testnet_genesis( + initial_authorities: Vec<(AccountId, AuraId, BeefyId, GrandpaId)>, + root_key: AccountId, + endowed_accounts: Vec, + _enable_println: bool, +) -> GenesisConfig { + GenesisConfig { + system: SystemConfig { + code: WASM_BINARY.expect("Millau development WASM not available").to_vec(), + }, + balances: BalancesConfig { + balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 50)).collect(), + }, + aura: AuraConfig { authorities: Vec::new() }, + beefy: BeefyConfig::default(), + grandpa: GrandpaConfig { authorities: Vec::new() }, + sudo: SudoConfig { key: Some(root_key) }, + session: SessionConfig { + keys: initial_authorities + .iter() + .map(|x| { + (x.0.clone(), x.0.clone(), session_keys(x.1.clone(), x.2.clone(), x.3.clone())) + }) + .collect::>(), + }, + bridge_westend_grandpa: BridgeWestendGrandpaConfig { + // for our deployments to avoid multiple same-nonces transactions: + // //Alice is already used to initialize Rialto<->Millau bridge + // => let's use //Westend.GrandpaOwner to initialize Westend->Millau bridge + owner: Some(get_account_id_from_seed::(WESTEND_GRANDPA_PALLET_OWNER)), + ..Default::default() + }, + bridge_rialto_messages: BridgeRialtoMessagesConfig { + owner: Some(get_account_id_from_seed::(RIALTO_MESSAGES_PALLET_OWNER)), + ..Default::default() + }, + bridge_rialto_parachain_messages: BridgeRialtoParachainMessagesConfig { + owner: Some(get_account_id_from_seed::( + RIALTO_PARACHAIN_MESSAGES_PALLET_OWNER, + )), + ..Default::default() + }, + xcm_pallet: Default::default(), + } +} diff --git a/bin/millau/node/src/cli.rs b/bin/millau/node/src/cli.rs new file mode 100644 index 00000000000..12499b5718d --- /dev/null +++ b/bin/millau/node/src/cli.rs @@ -0,0 +1,73 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use clap::Parser; +use sc_cli::RunCmd; + +#[derive(Debug, Parser)] +pub struct Cli { + #[structopt(subcommand)] + pub subcommand: Option, + + #[structopt(flatten)] + pub run: RunCmd, +} + +/// Possible subcommands of the main binary. +#[derive(Debug, Parser)] +#[allow(clippy::large_enum_variant)] +pub enum Subcommand { + /// Key management CLI utilities + #[clap(subcommand)] + Key(sc_cli::KeySubcommand), + + /// Verify a signature for a message, provided on `STDIN`, with a given (public or secret) key. + Verify(sc_cli::VerifyCmd), + + /// Generate a seed that provides a vanity address. + Vanity(sc_cli::VanityCmd), + + /// Sign a message, with a given (secret) key. + Sign(sc_cli::SignCmd), + + /// Build a chain specification. + BuildSpec(sc_cli::BuildSpecCmd), + + /// Validate blocks. + CheckBlock(sc_cli::CheckBlockCmd), + + /// Export blocks. + ExportBlocks(sc_cli::ExportBlocksCmd), + + /// Export the state of a given block into a chain spec. + ExportState(sc_cli::ExportStateCmd), + + /// Import blocks. + ImportBlocks(sc_cli::ImportBlocksCmd), + + /// Remove the whole chain. + PurgeChain(sc_cli::PurgeChainCmd), + + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), + + /// Inspect blocks or extrinsics. + Inspect(node_inspect::cli::InspectCmd), + + /// Benchmark runtime pallets. + #[clap(subcommand)] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), +} diff --git a/bin/millau/node/src/command.rs b/bin/millau/node/src/command.rs new file mode 100644 index 00000000000..b8dff87f8f2 --- /dev/null +++ b/bin/millau/node/src/command.rs @@ -0,0 +1,159 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{ + cli::{Cli, Subcommand}, + service, + service::new_partial, +}; +use frame_benchmarking_cli::BenchmarkCmd; +use millau_runtime::{Block, RuntimeApi}; +use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli}; +use sc_service::PartialComponents; + +impl SubstrateCli for Cli { + fn impl_name() -> String { + "Millau Bridge Node".into() + } + + fn impl_version() -> String { + env!("CARGO_PKG_VERSION").into() + } + + fn description() -> String { + "Millau Bridge Node".into() + } + + fn author() -> String { + "Parity Technologies".into() + } + + fn support_url() -> String { + "https://github.com/paritytech/parity-bridges-common/".into() + } + + fn copyright_start_year() -> i32 { + 2019 + } + + fn executable_name() -> String { + "millau-bridge-node".into() + } + + fn native_runtime_version(_: &Box) -> &'static RuntimeVersion { + &millau_runtime::VERSION + } + + fn load_spec(&self, id: &str) -> Result, String> { + Ok(Box::new( + match id { + "" | "dev" => crate::chain_spec::Alternative::Development, + "local" => crate::chain_spec::Alternative::LocalTestnet, + _ => return Err(format!("Unsupported chain specification: {id}")), + } + .load(), + )) + } +} + +/// Parse and run command line arguments +pub fn run() -> sc_cli::Result<()> { + let cli = Cli::from_args(); + // make sure to set correct crypto version. + sp_core::crypto::set_default_ss58_version(sp_core::crypto::Ss58AddressFormat::custom( + millau_runtime::SS58Prefix::get() as u16, + )); + + match &cli.subcommand { + Some(Subcommand::Benchmark(cmd)) => { + let runner = cli.create_runner(cmd)?; + match cmd { + BenchmarkCmd::Pallet(cmd) => + if cfg!(feature = "runtime-benchmarks") { + runner + .sync_run(|config| cmd.run::(config)) + } else { + println!( + "Benchmarking wasn't enabled when building the node. \ + You can enable it with `--features runtime-benchmarks`." + ); + Ok(()) + }, + _ => Err("Unsupported benchmarking subcommand".into()), + } + }, + Some(Subcommand::Key(cmd)) => cmd.run(&cli), + Some(Subcommand::Sign(cmd)) => cmd.run(), + Some(Subcommand::Verify(cmd)) => cmd.run(), + Some(Subcommand::Vanity(cmd)) => cmd.run(), + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) + }, + Some(Subcommand::CheckBlock(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, import_queue, .. } = + new_partial(&config)?; + Ok((cmd.run(client, import_queue), task_manager)) + }) + }, + Some(Subcommand::ExportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, .. } = new_partial(&config)?; + Ok((cmd.run(client, config.database), task_manager)) + }) + }, + Some(Subcommand::ExportState(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, .. } = new_partial(&config)?; + Ok((cmd.run(client, config.chain_spec), task_manager)) + }) + }, + Some(Subcommand::ImportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, import_queue, .. } = + new_partial(&config)?; + Ok((cmd.run(client, import_queue), task_manager)) + }) + }, + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.database)) + }, + Some(Subcommand::Revert(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, backend, .. } = new_partial(&config)?; + Ok((cmd.run(client, backend, None), task_manager)) + }) + }, + Some(Subcommand::Inspect(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner + .sync_run(|config| cmd.run::(config)) + }, + None => { + let runner = cli.create_runner(&cli.run)?; + runner.run_node_until_exit(|config| async move { + service::new_full(config).map_err(sc_cli::Error::Service) + }) + }, + } +} diff --git a/bin/millau/node/src/lib.rs b/bin/millau/node/src/lib.rs new file mode 100644 index 00000000000..382d1c2d7fb --- /dev/null +++ b/bin/millau/node/src/lib.rs @@ -0,0 +1,32 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate Node Template CLI library. +#![warn(missing_docs)] + +mod chain_spec; +#[macro_use] +mod service; +mod cli; +mod command; + +/// Node run result. +pub type Result = sc_cli::Result<()>; + +/// Run node. +pub fn run() -> Result { + command::run() +} diff --git a/bin/millau/node/src/main.rs b/bin/millau/node/src/main.rs new file mode 100644 index 00000000000..cf6dd9f733a --- /dev/null +++ b/bin/millau/node/src/main.rs @@ -0,0 +1,30 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Millau bridge node. + +#![warn(missing_docs)] + +mod chain_spec; +#[macro_use] +mod service; +mod cli; +mod command; + +/// Run the Millau Node +fn main() -> sc_cli::Result<()> { + command::run() +} diff --git a/bin/millau/node/src/service.rs b/bin/millau/node/src/service.rs new file mode 100644 index 00000000000..07cd29a0d6d --- /dev/null +++ b/bin/millau/node/src/service.rs @@ -0,0 +1,453 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Service and ServiceFactory implementation. Specialized wrapper over substrate service. + +use jsonrpsee::RpcModule; +use millau_runtime::{self, opaque::Block, RuntimeApi}; +use sc_client_api::BlockBackend; +use sc_consensus_aura::{CompatibilityMode, ImportQueueParams, SlotProportion, StartAuraParams}; +use sc_consensus_grandpa::SharedVoterState; +pub use sc_executor::NativeElseWasmExecutor; +use sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY}; +use sc_service::{error::Error as ServiceError, Configuration, TaskManager}; +use sc_telemetry::{Telemetry, TelemetryWorker}; +use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; +use std::{sync::Arc, time::Duration}; + +// Our native executor instance. +pub struct ExecutorDispatch; + +impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { + /// Only enable the benchmarking host functions when we actually want to benchmark. + #[cfg(feature = "runtime-benchmarks")] + type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; + /// Otherwise we only use the default Substrate host functions. + #[cfg(not(feature = "runtime-benchmarks"))] + type ExtendHostFunctions = (); + + fn dispatch(method: &str, data: &[u8]) -> Option> { + millau_runtime::api::dispatch(method, data) + } + + fn native_version() -> sc_executor::NativeVersion { + millau_runtime::native_version() + } +} + +type FullClient = + sc_service::TFullClient>; +type FullBackend = sc_service::TFullBackend; +type FullSelectChain = sc_consensus::LongestChain; + +#[allow(clippy::type_complexity)] +pub fn new_partial( + config: &Configuration, +) -> Result< + sc_service::PartialComponents< + FullClient, + FullBackend, + FullSelectChain, + sc_consensus::DefaultImportQueue, + sc_transaction_pool::FullPool, + ( + sc_consensus_grandpa::GrandpaBlockImport< + FullBackend, + Block, + FullClient, + FullSelectChain, + >, + sc_consensus_grandpa::LinkHalf, + sc_consensus_beefy::BeefyVoterLinks, + sc_consensus_beefy::BeefyRPCLinks, + Option, + ), + >, + ServiceError, +> { + let telemetry = config + .telemetry_endpoints + .clone() + .filter(|x| !x.is_empty()) + .map(|endpoints| -> Result<_, sc_telemetry::Error> { + let worker = TelemetryWorker::new(16)?; + let telemetry = worker.handle().new_telemetry(endpoints); + Ok((worker, telemetry)) + }) + .transpose()?; + + let heap_pages = config + .default_heap_pages + .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static { extra_pages: h as _ }); + let executor = NativeElseWasmExecutor::::new_with_wasm_executor( + WasmExecutor::builder() + .with_execution_method(config.wasm_method) + .with_onchain_heap_alloc_strategy(heap_pages) + .with_offchain_heap_alloc_strategy(heap_pages) + .with_max_runtime_instances(config.max_runtime_instances) + .with_runtime_cache_size(config.runtime_cache_size) + .build(), + ); + + let (client, backend, keystore_container, task_manager) = + sc_service::new_full_parts::( + config, + telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), + executor, + )?; + let client = Arc::new(client); + + let telemetry = telemetry.map(|(worker, telemetry)| { + task_manager.spawn_handle().spawn("telemetry", None, worker.run()); + telemetry + }); + + let select_chain = sc_consensus::LongestChain::new(backend.clone()); + + let transaction_pool = sc_transaction_pool::BasicPool::new_full( + config.transaction_pool.clone(), + config.role.is_authority().into(), + config.prometheus_registry(), + task_manager.spawn_essential_handle(), + client.clone(), + ); + + let (grandpa_block_import, grandpa_link) = sc_consensus_grandpa::block_import( + client.clone(), + &client, + select_chain.clone(), + telemetry.as_ref().map(|x| x.handle()), + )?; + + let (beefy_block_import, beefy_voter_links, beefy_rpc_links) = + sc_consensus_beefy::beefy_block_import_and_links( + grandpa_block_import.clone(), + backend.clone(), + client.clone(), + config.prometheus_registry().cloned(), + ); + + let slot_duration = sc_consensus_aura::slot_duration(&*client)?; + + let import_queue = + sc_consensus_aura::import_queue::(ImportQueueParams { + block_import: beefy_block_import, + justification_import: Some(Box::new(grandpa_block_import.clone())), + client: client.clone(), + create_inherent_data_providers: move |_, ()| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + Ok((slot, timestamp)) + }, + spawner: &task_manager.spawn_essential_handle(), + registry: config.prometheus_registry(), + check_for_equivocation: Default::default(), + telemetry: telemetry.as_ref().map(|x| x.handle()), + compatibility_mode: CompatibilityMode::None, + })?; + + Ok(sc_service::PartialComponents { + client, + backend, + task_manager, + import_queue, + keystore_container, + select_chain, + transaction_pool, + other: (grandpa_block_import, grandpa_link, beefy_voter_links, beefy_rpc_links, telemetry), + }) +} + +/// Builds a new service for a full client. +pub fn new_full(mut config: Configuration) -> Result { + use sc_network_common::sync::warp::WarpSyncParams; + + let sc_service::PartialComponents { + client, + backend, + mut task_manager, + import_queue, + keystore_container, + select_chain, + transaction_pool, + other: (block_import, grandpa_link, beefy_voter_links, beefy_rpc_links, mut telemetry), + } = new_partial(&config)?; + + let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"); + + // Note: GrandPa is pushed before the Polkadot-specific protocols. This doesn't change + // anything in terms of behaviour, but makes the logs more consistent with the other + // Substrate nodes. + let grandpa_protocol_name = sc_consensus_grandpa::protocol_standard_name( + &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), + &config.chain_spec, + ); + config + .network + .extra_sets + .push(sc_consensus_grandpa::grandpa_peers_set_config(grandpa_protocol_name.clone())); + + let beefy_gossip_proto_name = + sc_consensus_beefy::gossip_protocol_name(genesis_hash, config.chain_spec.fork_id()); + // `beefy_on_demand_justifications_handler` is given to `beefy-gadget` task to be run, + // while `beefy_req_resp_cfg` is added to `config.network.request_response_protocols`. + let (beefy_on_demand_justifications_handler, beefy_req_resp_cfg) = + sc_consensus_beefy::communication::request_response::BeefyJustifsRequestHandler::new( + genesis_hash, + config.chain_spec.fork_id(), + client.clone(), + config.prometheus_registry().cloned(), + ); + config + .network + .extra_sets + .push(sc_consensus_beefy::communication::beefy_peers_set_config( + beefy_gossip_proto_name.clone(), + )); + config.network.request_response_protocols.push(beefy_req_resp_cfg); + + let warp_sync = Arc::new(sc_consensus_grandpa::warp_proof::NetworkProvider::new( + backend.clone(), + grandpa_link.shared_authority_set().clone(), + Vec::default(), + )); + + let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = + sc_service::build_network(sc_service::BuildNetworkParams { + config: &config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + spawn_handle: task_manager.spawn_handle(), + import_queue, + block_announce_validator_builder: None, + warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), + })?; + + if config.offchain_worker.enabled { + sc_service::build_offchain_workers( + &config, + task_manager.spawn_handle(), + client.clone(), + network.clone(), + ); + } + + let role = config.role.clone(); + let force_authoring = config.force_authoring; + let backoff_authoring_blocks: Option<()> = None; + let name = config.network.node_name.clone(); + let enable_grandpa = !config.disable_grandpa; + let prometheus_registry = config.prometheus_registry().cloned(); + let shared_voter_state = SharedVoterState::empty(); + + let rpc_extensions_builder = { + use sc_consensus_grandpa::FinalityProofProvider as GrandpaFinalityProofProvider; + + use mmr_rpc::{Mmr, MmrApiServer}; + use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; + use sc_consensus_beefy_rpc::{Beefy, BeefyApiServer}; + use sc_consensus_grandpa_rpc::{Grandpa, GrandpaApiServer}; + use sc_rpc::DenyUnsafe; + use substrate_frame_rpc_system::{System, SystemApiServer}; + + let backend = backend.clone(); + let client = client.clone(); + let pool = transaction_pool.clone(); + + let justification_stream = grandpa_link.justification_stream(); + let shared_authority_set = grandpa_link.shared_authority_set().clone(); + let shared_voter_state = shared_voter_state.clone(); + + let finality_proof_provider = GrandpaFinalityProofProvider::new_for_service( + backend, + Some(shared_authority_set.clone()), + ); + + Box::new(move |_, subscription_executor: sc_rpc::SubscriptionTaskExecutor| { + let mut io = RpcModule::new(()); + let map_err = |e| sc_service::Error::Other(format!("{e}")); + io.merge(System::new(client.clone(), pool.clone(), DenyUnsafe::No).into_rpc()) + .map_err(map_err)?; + io.merge(TransactionPayment::new(client.clone()).into_rpc()).map_err(map_err)?; + io.merge( + Grandpa::new( + subscription_executor.clone(), + shared_authority_set.clone(), + shared_voter_state.clone(), + justification_stream.clone(), + finality_proof_provider.clone(), + ) + .into_rpc(), + ) + .map_err(map_err)?; + io.merge( + Beefy::::new( + beefy_rpc_links.from_voter_justif_stream.clone(), + beefy_rpc_links.from_voter_best_beefy_stream.clone(), + subscription_executor, + ) + .map_err(|e| sc_service::Error::Other(format!("{e}")))? + .into_rpc(), + ) + .map_err(map_err)?; + io.merge(Mmr::new(client.clone()).into_rpc()).map_err(map_err)?; + Ok(io) + }) + }; + + let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { + network: network.clone(), + client: client.clone(), + keystore: keystore_container.keystore(), + task_manager: &mut task_manager, + transaction_pool: transaction_pool.clone(), + sync_service: sync_service.clone(), + rpc_builder: rpc_extensions_builder, + backend: backend.clone(), + system_rpc_tx, + config, + tx_handler_controller, + telemetry: telemetry.as_mut(), + })?; + + if role.is_authority() { + let proposer_factory = sc_basic_authorship::ProposerFactory::new( + task_manager.spawn_handle(), + client.clone(), + transaction_pool, + prometheus_registry.as_ref(), + telemetry.as_ref().map(|x| x.handle()), + ); + + let slot_duration = sc_consensus_aura::slot_duration(&*client)?; + + let aura = sc_consensus_aura::start_aura::( + StartAuraParams { + slot_duration, + client: client.clone(), + select_chain, + block_import, + proposer_factory, + create_inherent_data_providers: move |_, ()| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + Ok((slot, timestamp)) + }, + force_authoring, + backoff_authoring_blocks, + keystore: keystore_container.keystore(), + sync_oracle: sync_service.clone(), + justification_sync_link: sync_service.clone(), + block_proposal_slot_portion: SlotProportion::new(2f32 / 3f32), + max_block_proposal_slot_portion: None, + telemetry: telemetry.as_ref().map(|x| x.handle()), + compatibility_mode: CompatibilityMode::None, + }, + )?; + + // the AURA authoring task is considered essential, i.e. if it + // fails we take down the service with it. + task_manager + .spawn_essential_handle() + .spawn_blocking("aura", Some("block-authoring"), aura); + } + + // if the node isn't actively participating in consensus then it doesn't + // need a keystore, regardless of which protocol we use below. + let keystore = if role.is_authority() { Some(keystore_container.keystore()) } else { None }; + + let justifications_protocol_name = beefy_on_demand_justifications_handler.protocol_name(); + let payload_provider = sp_consensus_beefy::mmr::MmrRootProvider::new(client.clone()); + let beefy_params = sc_consensus_beefy::BeefyParams { + client: client.clone(), + backend, + payload_provider, + runtime: client, + key_store: keystore.clone(), + network_params: sc_consensus_beefy::BeefyNetworkParams { + network: network.clone(), + sync: sync_service.clone(), + gossip_protocol_name: beefy_gossip_proto_name, + justifications_protocol_name, + _phantom: core::marker::PhantomData::, + }, + min_block_delta: 2, + prometheus_registry: prometheus_registry.clone(), + links: beefy_voter_links, + on_demand_justifications_handler: beefy_on_demand_justifications_handler, + }; + + // Start the BEEFY bridge gadget. + task_manager.spawn_essential_handle().spawn_blocking( + "beefy-gadget", + None, + sc_consensus_beefy::start_beefy_gadget::<_, _, _, _, _, _, _>(beefy_params), + ); + + let grandpa_config = sc_consensus_grandpa::Config { + // FIXME #1578 make this available through chainspec + gossip_duration: Duration::from_millis(333), + justification_period: 512, + name: Some(name), + observer_enabled: false, + keystore, + local_role: role, + telemetry: telemetry.as_ref().map(|x| x.handle()), + protocol_name: grandpa_protocol_name, + }; + + if enable_grandpa { + // start the full GRANDPA voter + // NOTE: non-authorities could run the GRANDPA observer protocol, but at + // this point the full voter should provide better guarantees of block + // and vote data availability than the observer. The observer has not + // been tested extensively yet and having most nodes in a network run it + // could lead to finality stalls. + let grandpa_config = sc_consensus_grandpa::GrandpaParams { + config: grandpa_config, + link: grandpa_link, + network, + sync: sync_service, + voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(), + prometheus_registry, + shared_voter_state, + telemetry: telemetry.as_ref().map(|x| x.handle()), + }; + + // the GRANDPA voter task is considered infallible, i.e. + // if it fails we take down the service with it. + task_manager.spawn_essential_handle().spawn_blocking( + "grandpa-voter", + None, + sc_consensus_grandpa::run_grandpa_voter(grandpa_config)?, + ); + } + + network_starter.start_network(); + Ok(task_manager) +} diff --git a/bin/millau/runtime/Cargo.toml b/bin/millau/runtime/Cargo.toml new file mode 100644 index 00000000000..e1a55ea6f24 --- /dev/null +++ b/bin/millau/runtime/Cargo.toml @@ -0,0 +1,144 @@ +[package] +name = "millau-runtime" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +repository = "https://github.com/paritytech/parity-bridges-common/" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +hex-literal = "0.4" +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } + +# Bridge dependencies + +bp-messages = { path = "../../../primitives/messages", default-features = false } +bp-millau = { path = "../../../primitives/chain-millau", default-features = false } +bp-parachains = { path = "../../../primitives/parachains", default-features = false } +bp-polkadot-core = { path = "../../../primitives/polkadot-core", default-features = false } +bp-relayers = { path = "../../../primitives/relayers", default-features = false } +bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false } +bp-rialto-parachain = { path = "../../../primitives/chain-rialto-parachain", default-features = false } +bp-runtime = { path = "../../../primitives/runtime", default-features = false } +bp-westend = { path = "../../../primitives/chain-westend", default-features = false } +bridge-runtime-common = { path = "../../runtime-common", default-features = false } +pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false } +pallet-bridge-messages = { path = "../../../modules/messages", default-features = false } +pallet-bridge-parachains = { path = "../../../modules/parachains", default-features = false } +pallet-bridge-relayers = { path = "../../../modules/relayers", default-features = false } +pallet-shift-session-manager = { path = "../../../modules/shift-session-manager", default-features = false } + +# Substrate Dependencies + +sp-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-executive = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-aura = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, features = ["historical"]} +pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-utility = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +# Polkadot Dependencies +pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + +[dev-dependencies] +bridge-runtime-common = { path = "../../runtime-common", features = ["integrity-test", "std"] } +env_logger = "0.10" +static_assertions = "1.1" + +[build-dependencies] +substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "sp-consensus-beefy/std", + "bp-messages/std", + "bp-millau/std", + "bp-parachains/std", + "bp-polkadot-core/std", + "bp-relayers/std", + "bp-rialto/std", + "bp-rialto-parachain/std", + "bp-runtime/std", + "bp-westend/std", + "bridge-runtime-common/std", + "codec/std", + "frame-executive/std", + "frame-support/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "pallet-aura/std", + "pallet-balances/std", + "pallet-beefy/std", + "pallet-beefy-mmr/std", + "pallet-bridge-grandpa/std", + "pallet-bridge-messages/std", + "pallet-bridge-parachains/std", + "pallet-bridge-relayers/std", + "pallet-grandpa/std", + "pallet-mmr/std", + "pallet-session/std", + "pallet-shift-session-manager/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "pallet-utility/std", + "pallet-xcm/std", + "scale-info/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-core/std", + "sp-inherents/std", + "sp-io/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-version/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", +] +runtime-benchmarks = [ + "bridge-runtime-common/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-bridge-messages/runtime-benchmarks", + "pallet-bridge-parachains/runtime-benchmarks", + "pallet-bridge-relayers/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", +] diff --git a/bin/millau/runtime/build.rs b/bin/millau/runtime/build.rs new file mode 100644 index 00000000000..cc865704327 --- /dev/null +++ b/bin/millau/runtime/build.rs @@ -0,0 +1,25 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .import_memory() + .export_heap_base() + .build() +} diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs new file mode 100644 index 00000000000..4e6f1e43e8c --- /dev/null +++ b/bin/millau/runtime/src/lib.rs @@ -0,0 +1,1159 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! The Millau runtime. This can be compiled with `#[no_std]`, ready for Wasm. + +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] +// Runtime-generated enums +#![allow(clippy::large_enum_variant)] +// From construct_runtime macro +#![allow(clippy::from_over_into)] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +pub mod rialto_messages; +pub mod rialto_parachain_messages; +pub mod xcm_config; + +use bp_parachains::SingleParaStoredHeaderDataBuilder; +#[cfg(feature = "runtime-benchmarks")] +use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; +use bp_runtime::HeaderId; +use pallet_grandpa::{ + fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, +}; +use pallet_transaction_payment::{FeeDetails, Multiplier, RuntimeDispatchInfo}; +use sp_api::impl_runtime_apis; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_consensus_beefy::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; +use sp_core::OpaqueMetadata; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{Block as BlockT, IdentityLookup, Keccak256, NumberFor, OpaqueKeys}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, FixedPointNumber, Perquintill, +}; +use sp_std::prelude::*; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +// to be able to use Millau runtime in `bridge-runtime-common` tests +pub use bridge_runtime_common; + +// A few exports that help ease life for downstream crates. +pub use frame_support::{ + construct_runtime, + dispatch::DispatchClass, + parameter_types, + traits::{ + ConstU32, ConstU64, ConstU8, Currency, ExistenceRequirement, Imbalance, KeyOwnerProofSystem, + }, + weights::{ + constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, IdentityFee, RuntimeDbWeight, + Weight, + }, + RuntimeDebug, StorageValue, +}; + +pub use frame_system::Call as SystemCall; +pub use pallet_balances::Call as BalancesCall; +pub use pallet_bridge_grandpa::Call as BridgeGrandpaCall; +pub use pallet_bridge_messages::Call as MessagesCall; +pub use pallet_bridge_parachains::Call as BridgeParachainsCall; +pub use pallet_sudo::Call as SudoCall; +pub use pallet_timestamp::Call as TimestampCall; +pub use pallet_xcm::Call as XcmCall; + +use bridge_runtime_common::{ + generate_bridge_reject_obsolete_headers_and_messages, + refund_relayer_extension::{ + ActualFeeRefund, RefundBridgedParachainMessages, RefundableMessagesLane, + RefundableParachain, + }, +}; +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; +pub use sp_runtime::{Perbill, Permill}; + +/// An index to a block. +pub type BlockNumber = bp_millau::BlockNumber; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = bp_millau::Signature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = bp_millau::AccountId; + +/// The type for looking up accounts. We don't expect more than 4 billion of them, but you +/// never know... +pub type AccountIndex = u32; + +/// Balance of an account. +pub type Balance = bp_millau::Balance; + +/// Index of a transaction in the chain. +pub type Index = bp_millau::Index; + +/// A hash of some data used by the chain. +pub type Hash = bp_millau::Hash; + +/// Hashing algorithm used by the chain. +pub type Hashing = bp_millau::Hasher; + +/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know +/// the specifics of the runtime. They can then be made to be agnostic over specific formats +/// of data like extrinsics, allowing for them to continue syncing the network through upgrades +/// to even the core data structures. +pub mod opaque { + use super::*; + + pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; + + /// Opaque block header type. + pub type Header = generic::Header; + /// Opaque block type. + pub type Block = generic::Block; + /// Opaque block identifier type. + pub type BlockId = generic::BlockId; +} + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + pub beefy: Beefy, + pub grandpa: Grandpa, + } +} + +/// This runtime version. +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("millau-runtime"), + impl_name: create_runtime_str!("millau-runtime"), + authoring_version: 1, + spec_version: 1, + impl_version: 1, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 0, +}; + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } +} + +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub const Version: RuntimeVersion = VERSION; + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { + read: 60_000_000, // ~0.06 ms = ~60 µs + write: 200_000_000, // ~0.2 ms = 200 µs + }; + pub const SS58Prefix: u8 = 60; +} + +impl frame_system::Config for Runtime { + /// The basic call filter to use in dispatchable. + type BaseCallFilter = frame_support::traits::Everything; + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type RuntimeCall = RuntimeCall; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = IdentityLookup; + /// The index type for storing how many extrinsics an account has signed. + type Index = Index; + /// The index type for blocks. + type BlockNumber = BlockNumber; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = Hashing; + /// The header type. + type Header = generic::Header; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// Version of the runtime. + type Version = Version; + /// Provides information about the pallet setup in the runtime. + type PalletInfo = PalletInfo; + /// What to do if a new account is created. + type OnNewAccount = (); + /// What to do if an account is fully reaped from the system. + type OnKilledAccount = (); + /// The data to be stored in an account. + type AccountData = pallet_balances::AccountData; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + /// Weight information for the extrinsics of this pallet. + type SystemWeightInfo = (); + /// Block and extrinsics weights: base values and limits. + type BlockWeights = bp_millau::BlockWeights; + /// The maximum length of a block (in bytes). + type BlockLength = bp_millau::BlockLength; + /// The weight of database operations that the runtime can invoke. + type DbWeight = DbWeight; + /// The designated SS58 prefix of this chain. + type SS58Prefix = SS58Prefix; + /// The set code logic, just the default since we're not a parachain. + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type MaxAuthorities = ConstU32<10>; + type DisabledValidators = (); +} + +impl pallet_beefy::Config for Runtime { + type BeefyId = BeefyId; + type MaxAuthorities = ConstU32<10>; + type MaxSetIdSessionEntries = ConstU64<0>; + type OnNewValidatorSet = MmrLeaf; + type WeightInfo = (); + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); +} + +impl pallet_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); + type MaxAuthorities = ConstU32<10>; + type MaxSetIdSessionEntries = ConstU64<0>; + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); +} + +/// MMR helper types. +mod mmr { + use super::Runtime; + pub use pallet_mmr::primitives::*; + use sp_runtime::traits::Keccak256; + + pub type Leaf = <::LeafData as LeafDataProvider>::LeafData; + pub type Hash = ::Output; + pub type Hashing = ::Hashing; +} + +impl pallet_mmr::Config for Runtime { + const INDEXING_PREFIX: &'static [u8] = b"mmr"; + type Hashing = Keccak256; + type Hash = mmr::Hash; + type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest; + type WeightInfo = (); + type LeafData = pallet_beefy_mmr::Pallet; +} + +parameter_types! { + /// Version of the produced MMR leaf. + /// + /// The version consists of two parts; + /// - `major` (3 bits) + /// - `minor` (5 bits) + /// + /// `major` should be updated only if decoding the previous MMR Leaf format from the payload + /// is not possible (i.e. backward incompatible change). + /// `minor` should be updated if fields are added to the previous MMR Leaf, which given SCALE + /// encoding does not prevent old leafs from being decoded. + /// + /// Hence we expect `major` to be changed really rarely (think never). + /// See [`MmrLeafVersion`] type documentation for more details. + pub LeafVersion: MmrLeafVersion = MmrLeafVersion::new(0, 0); +} + +pub struct BeefyDummyDataProvider; + +impl sp_consensus_beefy::mmr::BeefyDataProvider<()> for BeefyDummyDataProvider { + fn extra_data() {} +} + +impl pallet_beefy_mmr::Config for Runtime { + type LeafVersion = LeafVersion; + type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; + type LeafExtra = (); + type BeefyDataProvider = BeefyDummyDataProvider; +} + +parameter_types! { + pub const MinimumPeriod: u64 = bp_millau::SLOT_DURATION / 2; +} + +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the UNIX epoch. + type Moment = u64; + type OnTimestampSet = Aura; + type MinimumPeriod = MinimumPeriod; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); +} + +parameter_types! { + pub const ExistentialDeposit: bp_millau::Balance = 500; +} + +impl pallet_balances::Config for Runtime { + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); + // For weight estimation, we assume that the most locks on an individual account will be 50. + // This number may need to be adjusted in the future if this assumption no longer holds true. + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; +} + +parameter_types! { + pub const TransactionBaseFee: Balance = 0; + pub const TransactionByteFee: Balance = 1; + // values for following parameters are copied from polkadot repo, but it is fine + // not to sync them - we're not going to make Rialto a full copy of one of Polkadot-like chains + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); + pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value(); +} + +impl pallet_transaction_payment::Config for Runtime { + type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = bp_millau::WeightToFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< + Runtime, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + MaximumMultiplier, + >; + type RuntimeEvent = RuntimeEvent; +} + +impl pallet_sudo::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; +} + +parameter_types! { + /// Authorities are changing every 5 minutes. + pub const Period: BlockNumber = bp_millau::SESSION_LENGTH; + pub const Offset: BlockNumber = 0; +} + +impl pallet_session::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ValidatorId = ::AccountId; + type ValidatorIdOf = (); + type ShouldEndSession = pallet_session::PeriodicSessions; + type NextSessionRotation = pallet_session::PeriodicSessions; + type SessionManager = pallet_shift_session_manager::Pallet; + type SessionHandler = ::KeyTypeIdProviders; + type Keys = SessionKeys; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); +} + +impl pallet_bridge_relayers::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Reward = Balance; + type PaymentProcedure = + bp_relayers::PayRewardFromAccount, AccountId>; + type WeightInfo = (); +} + +pub type RialtoGrandpaInstance = (); +impl pallet_bridge_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type BridgedChain = bp_rialto::Rialto; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; + type HeadersToKeep = ConstU32<{ bp_rialto::DAYS }>; + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; +} + +pub type WestendGrandpaInstance = pallet_bridge_grandpa::Instance1; +impl pallet_bridge_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type BridgedChain = bp_westend::Westend; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; + type HeadersToKeep = ConstU32<{ bp_westend::DAYS }>; + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; +} + +impl pallet_shift_session_manager::Config for Runtime {} + +parameter_types! { + pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; + pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = + bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + pub const RootAccountForPayments: Option = None; + pub const RialtoChainId: bp_runtime::ChainId = bp_runtime::RIALTO_CHAIN_ID; + pub const RialtoParachainChainId: bp_runtime::ChainId = bp_runtime::RIALTO_PARACHAIN_CHAIN_ID; + pub RialtoActiveOutboundLanes: &'static [bp_messages::LaneId] = &[rialto_messages::XCM_LANE]; + pub RialtoParachainActiveOutboundLanes: &'static [bp_messages::LaneId] = &[rialto_parachain_messages::XCM_LANE]; +} + +/// Instance of the messages pallet used to relay messages to/from Rialto chain. +pub type WithRialtoMessagesInstance = (); + +impl pallet_bridge_messages::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + type ActiveOutboundLanes = RialtoActiveOutboundLanes; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type MaximalOutboundPayloadSize = crate::rialto_messages::ToRialtoMaximalOutboundPayloadSize; + type OutboundPayload = crate::rialto_messages::ToRialtoMessagePayload; + + type InboundPayload = crate::rialto_messages::FromRialtoMessagePayload; + type InboundRelayer = bp_rialto::AccountId; + type DeliveryPayments = (); + + type TargetHeaderChain = crate::rialto_messages::RialtoAsTargetHeaderChain; + type LaneMessageVerifier = crate::rialto_messages::ToRialtoMessageVerifier; + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + Runtime, + WithRialtoMessagesInstance, + frame_support::traits::ConstU64<100_000>, + >; + + type SourceHeaderChain = crate::rialto_messages::RialtoAsSourceHeaderChain; + type MessageDispatch = crate::rialto_messages::FromRialtoMessageDispatch; + type BridgedChainId = RialtoChainId; +} + +/// Instance of the messages pallet used to relay messages to/from RialtoParachain chain. +pub type WithRialtoParachainMessagesInstance = pallet_bridge_messages::Instance1; + +impl pallet_bridge_messages::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + type ActiveOutboundLanes = RialtoParachainActiveOutboundLanes; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type MaximalOutboundPayloadSize = + crate::rialto_parachain_messages::ToRialtoParachainMaximalOutboundPayloadSize; + type OutboundPayload = crate::rialto_parachain_messages::ToRialtoParachainMessagePayload; + + type InboundPayload = crate::rialto_parachain_messages::FromRialtoParachainMessagePayload; + type InboundRelayer = bp_rialto_parachain::AccountId; + type DeliveryPayments = (); + + type TargetHeaderChain = crate::rialto_parachain_messages::RialtoParachainAsTargetHeaderChain; + type LaneMessageVerifier = crate::rialto_parachain_messages::ToRialtoParachainMessageVerifier; + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + Runtime, + WithRialtoParachainMessagesInstance, + frame_support::traits::ConstU64<100_000>, + >; + + type SourceHeaderChain = crate::rialto_parachain_messages::RialtoParachainAsSourceHeaderChain; + type MessageDispatch = crate::rialto_parachain_messages::FromRialtoParachainMessageDispatch; + type BridgedChainId = RialtoParachainChainId; +} + +parameter_types! { + pub const RialtoParachainMessagesLane: bp_messages::LaneId = rialto_parachain_messages::XCM_LANE; + pub const RialtoParachainId: u32 = bp_rialto_parachain::RIALTO_PARACHAIN_ID; + pub const RialtoParasPalletName: &'static str = bp_rialto::PARAS_PALLET_NAME; + pub const WestendParasPalletName: &'static str = bp_westend::PARAS_PALLET_NAME; + pub const MaxRialtoParaHeadDataSize: u32 = bp_rialto::MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE; + pub const MaxWestendParaHeadDataSize: u32 = bp_westend::MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE; +} + +/// Instance of the with-Rialto parachains pallet. +pub type WithRialtoParachainsInstance = (); + +impl pallet_bridge_parachains::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; + type BridgesGrandpaPalletInstance = RialtoGrandpaInstance; + type ParasPalletName = RialtoParasPalletName; + type ParaStoredHeaderDataBuilder = + SingleParaStoredHeaderDataBuilder; + type HeadsToKeep = ConstU32<1024>; + type MaxParaHeadDataSize = MaxRialtoParaHeadDataSize; +} + +/// Instance of the with-Westend parachains pallet. +pub type WithWestendParachainsInstance = pallet_bridge_parachains::Instance1; + +impl pallet_bridge_parachains::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; + type BridgesGrandpaPalletInstance = WestendGrandpaInstance; + type ParasPalletName = WestendParasPalletName; + type ParaStoredHeaderDataBuilder = SingleParaStoredHeaderDataBuilder; + type HeadsToKeep = ConstU32<1024>; + type MaxParaHeadDataSize = MaxWestendParaHeadDataSize; +} + +impl pallet_utility::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = opaque::Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Sudo: pallet_sudo::{Pallet, Call, Config, Storage, Event}, + Utility: pallet_utility, + + // Must be before session. + Aura: pallet_aura::{Pallet, Config}, + + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + + // Consensus support. + Session: pallet_session::{Pallet, Call, Storage, Event, Config}, + Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event}, + ShiftSessionManager: pallet_shift_session_manager::{Pallet}, + + // BEEFY Bridges support. + Beefy: pallet_beefy::{Pallet, Storage, Config}, + Mmr: pallet_mmr::{Pallet, Storage}, + MmrLeaf: pallet_beefy_mmr::{Pallet, Storage}, + + // Rialto bridge modules. + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, + BridgeRialtoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event}, + BridgeRialtoMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, + + // Westend bridge modules. + BridgeWestendGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Config, Storage, Event}, + BridgeWestendParachains: pallet_bridge_parachains::::{Pallet, Call, Storage, Event}, + + // RialtoParachain bridge modules. + BridgeRialtoParachains: pallet_bridge_parachains::{Pallet, Call, Storage, Event}, + BridgeRialtoParachainMessages: pallet_bridge_messages::::{Pallet, Call, Storage, Event, Config}, + + // Pallet for sending XCM. + XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin, Config} = 99, + } +); + +generate_bridge_reject_obsolete_headers_and_messages! { + RuntimeCall, AccountId, + // Grandpa + BridgeRialtoGrandpa, BridgeWestendGrandpa, + // Parachains + BridgeRialtoParachains, + //Messages + BridgeRialtoMessages, BridgeRialtoParachainMessages +} + +bp_runtime::generate_static_str_provider!(BridgeRefundRialtoPara2000Lane0Msgs); +/// Signed extension that refunds relayers that are delivering messages from the Rialto parachain. +pub type PriorityBoostPerMessage = ConstU64<921_900_294>; +pub type BridgeRefundRialtoParachainMessages = RefundBridgedParachainMessages< + Runtime, + RefundableParachain, + RefundableMessagesLane, + ActualFeeRefund, + PriorityBoostPerMessage, + StrBridgeRefundRialtoPara2000Lane0Msgs, +>; + +/// The address format for describing accounts. +pub type Address = AccountId; +/// Block header type as expected by this runtime. +pub type Header = generic::Header; +/// Block type as expected by this runtime. +pub type Block = generic::Block; +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, + BridgeRejectObsoleteHeadersAndMessages, + BridgeRefundRialtoParachainMessages, +); +/// The payload being signed in transactions. +pub type SignedPayload = generic::SignedPayload; +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = generic::CheckedExtrinsic; +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block); + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> sp_std::vec::Vec { + Runtime::metadata_versions() + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + } + + fn authorities() -> Vec { + Aura::authorities().to_vec() + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi< + Block, + Balance, + > for Runtime { + fn query_info(uxt: ::Extrinsic, len: u32) -> RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details(uxt: ::Extrinsic, len: u32) -> FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, sp_core::crypto::KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl sp_consensus_beefy::BeefyApi for Runtime { + fn beefy_genesis() -> Option { + Beefy::genesis_block() + } + + fn validator_set() -> Option> { + Beefy::validator_set() + } + + fn submit_report_equivocation_unsigned_extrinsic( + _equivocation_proof: sp_consensus_beefy::EquivocationProof< + NumberFor, + sp_consensus_beefy::crypto::AuthorityId, + sp_consensus_beefy::crypto::Signature + >, + _key_owner_proof: sp_consensus_beefy::OpaqueKeyOwnershipProof, + ) -> Option<()> { None } + + fn generate_key_ownership_proof( + _set_id: sp_consensus_beefy::ValidatorSetId, + _authority_id: sp_consensus_beefy::crypto::AuthorityId, + ) -> Option { None } + } + + impl pallet_mmr::primitives::MmrApi< + Block, + mmr::Hash, + BlockNumber, + > for Runtime { + fn mmr_root() -> Result { + Ok(Mmr::mmr_root()) + } + + fn mmr_leaf_count() -> Result { + Ok(Mmr::mmr_leaves()) + } + + fn generate_proof( + block_numbers: Vec, + best_known_block_number: Option, + ) -> Result<(Vec, mmr::Proof), mmr::Error> { + Mmr::generate_proof(block_numbers, best_known_block_number).map( + |(leaves, proof)| { + ( + leaves + .into_iter() + .map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf)) + .collect(), + proof, + ) + }, + ) + } + + fn verify_proof(leaves: Vec, proof: mmr::Proof) + -> Result<(), mmr::Error> + { + let leaves = leaves.into_iter().map(|leaf| + leaf.into_opaque_leaf() + .try_decode() + .ok_or(mmr::Error::Verify)).collect::, mmr::Error>>()?; + Mmr::verify_leaves(leaves, proof) + } + + fn verify_proof_stateless( + root: mmr::Hash, + leaves: Vec, + proof: mmr::Proof + ) -> Result<(), mmr::Error> { + let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect(); + pallet_mmr::verify_leaves_proof::(root, nodes, proof) + } + } + + impl fg_primitives::GrandpaApi for Runtime { + fn current_set_id() -> fg_primitives::SetId { + Grandpa::current_set_id() + } + + fn grandpa_authorities() -> GrandpaAuthorityList { + Grandpa::grandpa_authorities() + } + + fn submit_report_equivocation_unsigned_extrinsic( + equivocation_proof: fg_primitives::EquivocationProof< + ::Hash, + NumberFor, + >, + key_owner_proof: fg_primitives::OpaqueKeyOwnershipProof, + ) -> Option<()> { + let key_owner_proof = key_owner_proof.decode()?; + + Grandpa::submit_unsigned_equivocation_report( + equivocation_proof, + key_owner_proof, + ) + } + + fn generate_key_ownership_proof( + _set_id: fg_primitives::SetId, + _authority_id: GrandpaId, + ) -> Option { + // NOTE: this is the only implementation possible since we've + // defined our key owner proof type as a bottom type (i.e. a type + // with no values). + None + } + } + + impl bp_rialto::RialtoFinalityApi for Runtime { + fn best_finalized() -> Option> { + BridgeRialtoGrandpa::best_finalized() + } + } + + impl bp_westend::WestendFinalityApi for Runtime { + fn best_finalized() -> Option> { + BridgeWestendGrandpa::best_finalized() + } + } + + impl bp_westend::WestmintFinalityApi for Runtime { + fn best_finalized() -> Option> { + pallet_bridge_parachains::Pallet::< + Runtime, + WithWestendParachainsInstance, + >::best_parachain_head_id::().unwrap_or(None) + } + } + + impl bp_rialto_parachain::RialtoParachainFinalityApi for Runtime { + fn best_finalized() -> Option> { + pallet_bridge_parachains::Pallet::< + Runtime, + WithRialtoParachainsInstance, + >::best_parachain_head_id::().unwrap_or(None) + } + } + + impl bp_rialto::ToRialtoOutboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + begin: bp_messages::MessageNonce, + end: bp_messages::MessageNonce, + ) -> Vec { + bridge_runtime_common::messages_api::outbound_message_details::< + Runtime, + WithRialtoMessagesInstance, + >(lane, begin, end) + } + } + + impl bp_rialto::FromRialtoInboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + ) -> Vec { + bridge_runtime_common::messages_api::inbound_message_details::< + Runtime, + WithRialtoMessagesInstance, + >(lane, messages) + } + } + + impl bp_rialto_parachain::ToRialtoParachainOutboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + begin: bp_messages::MessageNonce, + end: bp_messages::MessageNonce, + ) -> Vec { + bridge_runtime_common::messages_api::outbound_message_details::< + Runtime, + WithRialtoParachainMessagesInstance, + >(lane, begin, end) + } + } + + impl bp_rialto_parachain::FromRialtoParachainInboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + ) -> Vec { + bridge_runtime_common::messages_api::inbound_message_details::< + Runtime, + WithRialtoParachainMessagesInstance, + >(lane, messages) + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata(extra: bool) -> ( + Vec, + Vec, + ) { + use frame_benchmarking::{list_benchmark, Benchmarking, BenchmarkList}; + use frame_support::traits::StorageInfoTrait; + + use pallet_bridge_messages::benchmarking::Pallet as MessagesBench; + use pallet_bridge_parachains::benchmarking::Pallet as ParachainsBench; + use pallet_bridge_relayers::benchmarking::Pallet as RelayersBench; + + let mut list = Vec::::new(); + + list_benchmark!(list, extra, RialtoParachainMessages, MessagesBench::); + list_benchmark!(list, extra, RialtoMessages, MessagesBench::); + list_benchmark!(list, extra, pallet_bridge_grandpa, BridgeRialtoGrandpa); + list_benchmark!(list, extra, pallet_bridge_parachains, ParachainsBench::); + list_benchmark!(list, extra, pallet_bridge_relayers, RelayersBench::); + + let storage_info = AllPalletsWithSystem::storage_info(); + + return (list, storage_info) + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig, + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{Benchmarking, BenchmarkBatch, TrackedStorageKey, add_benchmark}; + + let whitelist: Vec = vec![ + // Block Number + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(), + // Execution Phase + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(), + // Event Count + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(), + // System Events + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(), + // Caller 0 Account + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da946c154ffd9992e395af90b5b13cc6f295c77033fce8a9045824a6690bbf99c6db269502f0a8d1d2a008542d5690a0749").to_vec().into(), + ]; + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + + use bridge_runtime_common::messages_benchmarking::{ + prepare_message_delivery_proof_from_grandpa_chain, + prepare_message_delivery_proof_from_parachain, + prepare_message_proof_from_grandpa_chain, + prepare_message_proof_from_parachain, + }; + use pallet_bridge_messages::benchmarking::{ + Pallet as MessagesBench, + Config as MessagesConfig, + MessageDeliveryProofParams, + MessageProofParams, + }; + use pallet_bridge_parachains::benchmarking::{ + Pallet as ParachainsBench, + Config as ParachainsConfig, + }; + use pallet_bridge_relayers::benchmarking::{ + Pallet as RelayersBench, + Config as RelayersConfig, + }; + use rialto_messages::WithRialtoMessageBridge; + use rialto_parachain_messages::WithRialtoParachainMessageBridge; + + impl MessagesConfig for Runtime { + fn prepare_message_proof( + params: MessageProofParams, + ) -> (rialto_messages::FromRialtoMessagesProof, Weight) { + prepare_message_proof_from_parachain::< + Runtime, + WithRialtoParachainsInstance, + WithRialtoParachainMessageBridge, + >(params, xcm::v3::Junctions::Here) + } + + fn prepare_message_delivery_proof( + params: MessageDeliveryProofParams, + ) -> rialto_messages::ToRialtoMessagesDeliveryProof { + prepare_message_delivery_proof_from_parachain::< + Runtime, + WithRialtoParachainsInstance, + WithRialtoParachainMessageBridge, + >(params) + } + + fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { + let lane = >::bench_lane_id(); + let bridged_chain_id = bp_runtime::RIALTO_PARACHAIN_CHAIN_ID; + pallet_bridge_relayers::Pallet::::relayer_reward( + relayer, + RewardsAccountParams::new(lane, bridged_chain_id, RewardsAccountOwner::BridgedChain) + ).is_some() + } + } + + impl MessagesConfig for Runtime { + fn prepare_message_proof( + params: MessageProofParams, + ) -> (rialto_messages::FromRialtoMessagesProof, Weight) { + prepare_message_proof_from_grandpa_chain::< + Runtime, + RialtoGrandpaInstance, + WithRialtoMessageBridge, + >(params, xcm::v3::Junctions::Here) + } + + fn prepare_message_delivery_proof( + params: MessageDeliveryProofParams, + ) -> rialto_messages::ToRialtoMessagesDeliveryProof { + prepare_message_delivery_proof_from_grandpa_chain::< + Runtime, + RialtoGrandpaInstance, + WithRialtoMessageBridge, + >(params) + } + + fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { + let lane = >::bench_lane_id(); + let bridged_chain_id = bp_runtime::RIALTO_CHAIN_ID; + pallet_bridge_relayers::Pallet::::relayer_reward( + relayer, + RewardsAccountParams::new(lane, bridged_chain_id, RewardsAccountOwner::BridgedChain) + ).is_some() + } + } + + impl ParachainsConfig for Runtime { + fn parachains() -> Vec { + use bp_runtime::Parachain; + vec![bp_polkadot_core::parachains::ParaId(bp_rialto_parachain::RialtoParachain::PARACHAIN_ID)] + } + + fn prepare_parachain_heads_proof( + parachains: &[bp_polkadot_core::parachains::ParaId], + parachain_head_size: u32, + proof_size: bp_runtime::StorageProofSize, + ) -> ( + pallet_bridge_parachains::RelayBlockNumber, + pallet_bridge_parachains::RelayBlockHash, + bp_polkadot_core::parachains::ParaHeadsProof, + Vec<(bp_polkadot_core::parachains::ParaId, bp_polkadot_core::parachains::ParaHash)>, + ) { + bridge_runtime_common::parachains_benchmarking::prepare_parachain_heads_proof::< + Runtime, + WithRialtoParachainsInstance, + >( + parachains, + parachain_head_size, + proof_size, + ) + } + } + + impl RelayersConfig for Runtime { + fn prepare_environment( + account_params: RewardsAccountParams, + reward: Balance, + ) { + use frame_support::traits::fungible::Mutate; + let rewards_account = bp_relayers::PayRewardFromAccount::< + Balances, + AccountId + >::rewards_account(account_params); + Balances::mint_into(&rewards_account, reward).unwrap(); + } + } + + add_benchmark!( + params, + batches, + RialtoParachainMessages, + MessagesBench:: + ); + add_benchmark!( + params, + batches, + RialtoMessages, + MessagesBench:: + ); + add_benchmark!(params, batches, pallet_bridge_grandpa, BridgeRialtoGrandpa); + add_benchmark!( + params, + batches, + pallet_bridge_parachains, + ParachainsBench:: + ); + add_benchmark!(params, batches, pallet_bridge_relayers, RelayersBench::); + + Ok(batches) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn call_size() { + const BRIDGES_PALLETS_MAX_CALL_SIZE: usize = 200; + assert!( + core::mem::size_of::>() <= + BRIDGES_PALLETS_MAX_CALL_SIZE + ); + assert!( + core::mem::size_of::>() <= + BRIDGES_PALLETS_MAX_CALL_SIZE + ); + const MAX_CALL_SIZE: usize = 230; // value from polkadot-runtime tests + assert!(core::mem::size_of::() <= MAX_CALL_SIZE); + } +} diff --git a/bin/millau/runtime/src/rialto_messages.rs b/bin/millau/runtime/src/rialto_messages.rs new file mode 100644 index 00000000000..84535283d48 --- /dev/null +++ b/bin/millau/runtime/src/rialto_messages.rs @@ -0,0 +1,199 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything required to serve Millau <-> Rialto messages. + +use crate::{RialtoGrandpaInstance, Runtime, RuntimeOrigin, WithRialtoMessagesInstance}; + +use bp_messages::LaneId; +use bridge_runtime_common::{ + messages::{ + self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, + }, + messages_xcm_extension::{XcmBlobHauler, XcmBlobHaulerAdapter}, +}; +use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; +use xcm::latest::prelude::*; +use xcm_builder::HaulBlobExporter; + +/// Default lane that is used to send messages to Rialto. +pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); +/// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge +/// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual +/// tests, confirming that we don't break encoding somewhere between. +pub const BASE_XCM_WEIGHT_TWICE: Weight = crate::xcm_config::BaseXcmWeight::get().saturating_mul(2); + +parameter_types! { + /// Weight credit for our test messages. + /// + /// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge + /// (it is prepended with `UniversalOrigin` instruction). + pub const WeightCredit: Weight = BASE_XCM_WEIGHT_TWICE; +} + +/// Message payload for Millau -> Rialto messages. +pub type ToRialtoMessagePayload = messages::source::FromThisChainMessagePayload; + +/// Message verifier for Millau -> Rialto messages. +pub type ToRialtoMessageVerifier = + messages::source::FromThisChainMessageVerifier; + +/// Message payload for Rialto -> Millau messages. +pub type FromRialtoMessagePayload = messages::target::FromBridgedChainMessagePayload; + +/// Messages proof for Rialto -> Millau messages. +pub type FromRialtoMessagesProof = messages::target::FromBridgedChainMessagesProof; + +/// Messages delivery proof for Millau -> Rialto messages. +pub type ToRialtoMessagesDeliveryProof = + messages::source::FromBridgedChainMessagesDeliveryProof; + +/// Call-dispatch based message dispatch for Rialto -> Millau messages. +pub type FromRialtoMessageDispatch = + bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatch< + bp_millau::Millau, + bp_rialto::Rialto, + crate::xcm_config::OnMillauBlobDispatcher, + (), + >; + +/// Maximal outbound payload size of Millau -> Rialto messages. +pub type ToRialtoMaximalOutboundPayloadSize = + messages::source::FromThisChainMaximalOutboundPayloadSize; + +/// Millau <-> Rialto message bridge. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct WithRialtoMessageBridge; + +impl MessageBridge for WithRialtoMessageBridge { + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME; + + type ThisChain = Millau; + type BridgedChain = Rialto; + type BridgedHeaderChain = + pallet_bridge_grandpa::GrandpaChainHeaders; +} + +/// Millau chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Millau; + +impl messages::UnderlyingChainProvider for Millau { + type Chain = bp_millau::Millau; +} + +impl messages::ThisChainWithMessages for Millau { + type RuntimeOrigin = RuntimeOrigin; +} + +/// Rialto chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Rialto; +/// Rialto as source header chain. +pub type RialtoAsSourceHeaderChain = SourceHeaderChainAdapter; +/// Rialto as target header chain. +pub type RialtoAsTargetHeaderChain = TargetHeaderChainAdapter; + +impl messages::UnderlyingChainProvider for Rialto { + type Chain = bp_rialto::Rialto; +} + +impl messages::BridgedChainWithMessages for Rialto {} + +/// Export XCM messages to be relayed to Rialto. +pub type ToRialtoBlobExporter = HaulBlobExporter< + XcmBlobHaulerAdapter, + crate::xcm_config::RialtoNetwork, + (), +>; + +/// To-Rialto XCM hauler. +pub struct ToRialtoXcmBlobHauler; + +impl XcmBlobHauler for ToRialtoXcmBlobHauler { + type MessageSender = pallet_bridge_messages::Pallet; + type MessageSenderOrigin = RuntimeOrigin; + + fn message_sender_origin() -> RuntimeOrigin { + pallet_xcm::Origin::from(MultiLocation::new(1, crate::xcm_config::UniversalLocation::get())) + .into() + } + + fn xcm_lane() -> LaneId { + XCM_LANE + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Runtime, WithRialtoMessagesInstance}; + + use bridge_runtime_common::{ + assert_complete_bridge_types, + integrity::{ + assert_complete_bridge_constants, check_message_lane_weights, + AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants, + AssertCompleteBridgeConstants, + }, + }; + + #[test] + fn ensure_millau_message_lane_weights_are_correct() { + check_message_lane_weights::( + bp_rialto::EXTRA_STORAGE_PROOF_SIZE, + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + ); + } + + #[test] + fn ensure_bridge_integrity() { + assert_complete_bridge_types!( + runtime: Runtime, + with_bridged_chain_grandpa_instance: RialtoGrandpaInstance, + with_bridged_chain_messages_instance: WithRialtoMessagesInstance, + bridge: WithRialtoMessageBridge, + this_chain: bp_millau::Millau, + bridged_chain: bp_rialto::Rialto, + ); + + assert_complete_bridge_constants::< + Runtime, + RialtoGrandpaInstance, + WithRialtoMessagesInstance, + WithRialtoMessageBridge, + >(AssertCompleteBridgeConstants { + this_chain_constants: AssertChainConstants { + block_length: bp_millau::BlockLength::get(), + block_weights: bp_millau::BlockWeights::get(), + }, + messages_pallet_constants: AssertBridgeMessagesPalletConstants { + max_unrewarded_relayers_in_bridged_confirmation_tx: + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + max_unconfirmed_messages_in_bridged_confirmation_tx: + bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + bridged_chain_id: bp_runtime::RIALTO_CHAIN_ID, + }, + pallet_names: AssertBridgePalletNames { + with_this_chain_messages_pallet_name: bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME, + with_bridged_chain_grandpa_pallet_name: bp_rialto::WITH_RIALTO_GRANDPA_PALLET_NAME, + with_bridged_chain_messages_pallet_name: + bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME, + }, + }); + } +} diff --git a/bin/millau/runtime/src/rialto_parachain_messages.rs b/bin/millau/runtime/src/rialto_parachain_messages.rs new file mode 100644 index 00000000000..bef8a281188 --- /dev/null +++ b/bin/millau/runtime/src/rialto_parachain_messages.rs @@ -0,0 +1,209 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything required to serve Millau <-> RialtoParachain messages. + +use crate::{ + Runtime, RuntimeOrigin, WithRialtoParachainMessagesInstance, WithRialtoParachainsInstance, +}; + +use bp_messages::LaneId; +use bridge_runtime_common::{ + messages::{ + self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, + }, + messages_xcm_extension::{XcmBlobHauler, XcmBlobHaulerAdapter}, +}; +use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; +use xcm::latest::prelude::*; +use xcm_builder::HaulBlobExporter; + +/// Default lane that is used to send messages to Rialto parachain. +pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); +/// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge +/// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual +/// tests, confirming that we don't break encoding somewhere between. +pub const BASE_XCM_WEIGHT_TWICE: Weight = crate::xcm_config::BaseXcmWeight::get().saturating_mul(2); + +parameter_types! { + /// Weight credit for our test messages. + /// + /// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge + /// (it is prepended with `UniversalOrigin` instruction). + pub const WeightCredit: Weight = BASE_XCM_WEIGHT_TWICE; +} + +/// Message payload for Millau -> RialtoParachain messages. +pub type ToRialtoParachainMessagePayload = messages::source::FromThisChainMessagePayload; + +/// Message verifier for Millau -> RialtoParachain messages. +pub type ToRialtoParachainMessageVerifier = + messages::source::FromThisChainMessageVerifier; + +/// Message payload for RialtoParachain -> Millau messages. +pub type FromRialtoParachainMessagePayload = messages::target::FromBridgedChainMessagePayload; + +/// Call-dispatch based message dispatch for RialtoParachain -> Millau messages. +pub type FromRialtoParachainMessageDispatch = + bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatch< + bp_millau::Millau, + bp_rialto::Rialto, + crate::xcm_config::OnMillauBlobDispatcher, + (), + >; + +/// Maximal outbound payload size of Millau -> RialtoParachain messages. +pub type ToRialtoParachainMaximalOutboundPayloadSize = + messages::source::FromThisChainMaximalOutboundPayloadSize; + +/// Millau <-> RialtoParachain message bridge. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct WithRialtoParachainMessageBridge; + +impl MessageBridge for WithRialtoParachainMessageBridge { + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME; + + type ThisChain = Millau; + type BridgedChain = RialtoParachain; + type BridgedHeaderChain = pallet_bridge_parachains::ParachainHeaders< + Runtime, + WithRialtoParachainsInstance, + bp_rialto_parachain::RialtoParachain, + >; +} + +/// Millau chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Millau; + +impl messages::UnderlyingChainProvider for Millau { + type Chain = bp_millau::Millau; +} + +impl messages::ThisChainWithMessages for Millau { + type RuntimeOrigin = RuntimeOrigin; +} + +/// RialtoParachain chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct RialtoParachain; +/// RialtoParachain as source header chain. +pub type RialtoParachainAsSourceHeaderChain = + SourceHeaderChainAdapter; +/// RialtoParachain as target header chain. +pub type RialtoParachainAsTargetHeaderChain = + TargetHeaderChainAdapter; + +impl messages::UnderlyingChainProvider for RialtoParachain { + type Chain = bp_rialto_parachain::RialtoParachain; +} + +impl messages::BridgedChainWithMessages for RialtoParachain {} + +/// Export XCM messages to be relayed to Rialto. +pub type ToRialtoParachainBlobExporter = HaulBlobExporter< + XcmBlobHaulerAdapter, + crate::xcm_config::RialtoParachainNetwork, + (), +>; + +/// To-RialtoParachain XCM hauler. +pub struct ToRialtoParachainXcmBlobHauler; + +impl XcmBlobHauler for ToRialtoParachainXcmBlobHauler { + type MessageSender = + pallet_bridge_messages::Pallet; + type MessageSenderOrigin = RuntimeOrigin; + + fn message_sender_origin() -> RuntimeOrigin { + pallet_xcm::Origin::from(MultiLocation::new(1, crate::xcm_config::UniversalLocation::get())) + .into() + } + + fn xcm_lane() -> LaneId { + XCM_LANE + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + PriorityBoostPerMessage, RialtoGrandpaInstance, Runtime, + WithRialtoParachainMessagesInstance, + }; + + use bridge_runtime_common::{ + assert_complete_bridge_types, + integrity::{ + assert_complete_bridge_constants, check_message_lane_weights, + AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants, + AssertCompleteBridgeConstants, + }, + }; + + #[test] + fn ensure_millau_message_lane_weights_are_correct() { + check_message_lane_weights::( + bp_rialto_parachain::EXTRA_STORAGE_PROOF_SIZE, + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + ); + } + + #[test] + fn ensure_bridge_integrity() { + assert_complete_bridge_types!( + runtime: Runtime, + with_bridged_chain_grandpa_instance: RialtoGrandpaInstance, + with_bridged_chain_messages_instance: WithRialtoParachainMessagesInstance, + bridge: WithRialtoParachainMessageBridge, + this_chain: bp_millau::Millau, + bridged_chain: bp_rialto::Rialto, + ); + + assert_complete_bridge_constants::< + Runtime, + RialtoGrandpaInstance, + WithRialtoParachainMessagesInstance, + WithRialtoParachainMessageBridge, + >(AssertCompleteBridgeConstants { + this_chain_constants: AssertChainConstants { + block_length: bp_millau::BlockLength::get(), + block_weights: bp_millau::BlockWeights::get(), + }, + messages_pallet_constants: AssertBridgeMessagesPalletConstants { + max_unrewarded_relayers_in_bridged_confirmation_tx: + bp_rialto_parachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + max_unconfirmed_messages_in_bridged_confirmation_tx: + bp_rialto_parachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + bridged_chain_id: bp_runtime::RIALTO_PARACHAIN_CHAIN_ID, + }, + pallet_names: AssertBridgePalletNames { + with_this_chain_messages_pallet_name: bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME, + with_bridged_chain_grandpa_pallet_name: bp_rialto::WITH_RIALTO_GRANDPA_PALLET_NAME, + with_bridged_chain_messages_pallet_name: + bp_rialto_parachain::WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME, + }, + }); + + bridge_runtime_common::priority_calculator::ensure_priority_boost_is_sane::< + Runtime, + WithRialtoParachainMessagesInstance, + PriorityBoostPerMessage, + >(1_000_000); + } +} diff --git a/bin/millau/runtime/src/xcm_config.rs b/bin/millau/runtime/src/xcm_config.rs new file mode 100644 index 00000000000..4aaec83771b --- /dev/null +++ b/bin/millau/runtime/src/xcm_config.rs @@ -0,0 +1,373 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! XCM configurations for the Millau runtime. + +use super::{ + rialto_messages::ToRialtoBlobExporter, + rialto_parachain_messages::ToRialtoParachainBlobExporter, AccountId, AllPalletsWithSystem, + Balances, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, XcmPallet, +}; +use bp_millau::WeightToFee; +use bridge_runtime_common::CustomNetworkId; +use frame_support::{ + parameter_types, + traits::{ConstU32, Everything, Nothing}, + weights::Weight, +}; +use frame_system::EnsureRoot; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, CurrencyAdapter as XcmCurrencyAdapter, IsConcrete, MintLocation, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + UsingComponents, +}; +use xcm_executor::traits::ExportXcm; + +parameter_types! { + /// The location of the `MLAU` token, from the context of this chain. Since this token is native to this + /// chain, we make it synonymous with it and thus it is the `Here` location, which means "equivalent to + /// the context". + pub const TokenLocation: MultiLocation = Here.into_location(); + /// The Millau network ID. + pub const ThisNetwork: NetworkId = CustomNetworkId::Millau.as_network_id(); + /// The Rialto network ID. + pub const RialtoNetwork: NetworkId = CustomNetworkId::Rialto.as_network_id(); + /// The RialtoParachain network ID. + pub const RialtoParachainNetwork: NetworkId = CustomNetworkId::RialtoParachain.as_network_id(); + + /// Our XCM location ancestry - i.e. our location within the Consensus Universe. + /// + /// Since Kusama is a top-level relay-chain with its own consensus, it's just our network ID. + pub UniversalLocation: InteriorMultiLocation = ThisNetwork::get().into(); + /// The check account, which holds any native assets that have been teleported out and not back in (yet). + pub CheckAccount: (AccountId, MintLocation) = (XcmPallet::check_account(), MintLocation::Local); +} + +/// The canonical means of converting a `MultiLocation` into an `AccountId`, used when we want to +/// determine the sovereign account controlled by a location. +pub type SovereignAccountOf = ( + // We can directly alias an `AccountId32` into a local account. + AccountId32Aliases, +); + +/// Our asset transactor. This is what allows us to interest with the runtime facilities from the +/// point of view of XCM-only concepts like `MultiLocation` and `MultiAsset`. +/// +/// Ours is only aware of the Balances pallet, which is mapped to `TokenLocation`. +pub type LocalAssetTransactor = XcmCurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // We can convert the MultiLocations with our converter above: + SovereignAccountOf, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We track our teleports in/out to keep total issuance correct. + CheckAccount, +>; + +/// The means that we convert the XCM message origin location into a local dispatch origin. +type LocalOriginConverter = ( + // A `Signed` origin of the sovereign account that the original location controls. + SovereignSignedViaLocation, + // The AccountId32 location type can be expressed natively as a `Signed` origin. + SignedAccountId32AsNative, +); + +parameter_types! { + /// The amount of weight an XCM operation takes. This is a safe overestimate. + pub const BaseXcmWeight: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); + /// Maximum number of instructions in a single XCM fragment. A sanity check against weight + /// calculations getting too crazy. + pub const MaxInstructions: u32 = 100; +} + +/// The XCM router. We are not sending messages to sibling/parent/child chains here. +pub type XcmRouter = (); + +/// The barriers one of which must be passed for an XCM message to be executed. +pub type Barrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, +); + +/// Dispatches received XCM messages from other chain. +pub type OnMillauBlobDispatcher = xcm_builder::BridgeBlobDispatcher< + crate::xcm_config::XcmRouter, + crate::xcm_config::UniversalLocation, +>; + +/// XCM weigher type. +pub type XcmWeigher = xcm_builder::FixedWeightBounds; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = (); + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = XcmWeigher; + // The weight trader piggybacks on the existing transaction-fee conversion logic. + type Trader = UsingComponents; + type ResponseHandler = XcmPallet; + type AssetTrap = XcmPallet; + type AssetLocker = (); + type AssetExchanger = (); + type AssetClaims = XcmPallet; + type SubscriptionService = XcmPallet; + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = ConstU32<64>; + type FeeManager = (); + type MessageExporter = ToRialtoOrRialtoParachainSwitchExporter; + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; +} + +/// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior +/// location of this chain. +pub type LocalOriginToLocation = ( + // Usual Signed origin to be used in XCM as a corresponding AccountId32 + SignedToAccountId32, +); + +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = None; +} + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + // We don't allow any messages to be sent via the transaction yet. This is basically safe to + // enable, (safe the possibility of someone spamming the parachain if they're willing to pay + // the DOT to send from the Relay-chain). But it's useless until we bring in XCM v3 which will + // make `DescendOrigin` a bit more useful. + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = (); + // Anyone can execute XCM messages locally. + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = xcm_executor::XcmExecutor; + // Anyone is able to use teleportation regardless of who they are and what they want to + // teleport. + type XcmTeleportFilter = Everything; + // Anyone is able to use reserve transfers regardless of who they are and what they want to + // transfer. + type XcmReserveTransferFilter = Everything; + type Weigher = XcmWeigher; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = SovereignAccountOf; + type MaxLockers = frame_support::traits::ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; + type AdminOrigin = EnsureRoot; +} + +pub struct ToRialtoOrRialtoParachainSwitchExporter; + +impl ExportXcm for ToRialtoOrRialtoParachainSwitchExporter { + type Ticket = (NetworkId, (sp_std::prelude::Vec, XcmHash)); + + fn validate( + network: NetworkId, + channel: u32, + universal_source: &mut Option, + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { + if network == RialtoNetwork::get() { + ToRialtoBlobExporter::validate(network, channel, universal_source, destination, message) + .map(|result| ((RialtoNetwork::get(), result.0), result.1)) + } else if network == RialtoParachainNetwork::get() { + ToRialtoParachainBlobExporter::validate( + network, + channel, + universal_source, + destination, + message, + ) + .map(|result| ((RialtoParachainNetwork::get(), result.0), result.1)) + } else { + Err(SendError::Unroutable) + } + } + + fn deliver(ticket: Self::Ticket) -> Result { + let (network, ticket) = ticket; + if network == RialtoNetwork::get() { + ToRialtoBlobExporter::deliver(ticket) + } else if network == RialtoParachainNetwork::get() { + ToRialtoParachainBlobExporter::deliver(ticket) + } else { + Err(SendError::Unroutable) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + rialto_messages::FromRialtoMessageDispatch, WithRialtoMessagesInstance, + WithRialtoParachainMessagesInstance, + }; + use bp_messages::{ + target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, + LaneId, MessageKey, + }; + use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult; + use codec::Encode; + use pallet_bridge_messages::OutboundLanes; + use xcm_executor::XcmExecutor; + + fn new_test_ext() -> sp_io::TestExternalities { + sp_io::TestExternalities::new( + frame_system::GenesisConfig::default().build_storage::().unwrap(), + ) + } + + fn prepare_outbound_xcm_message(destination: NetworkId) -> Xcm { + vec![ExportMessage { + network: destination, + destination: destination.into(), + xcm: vec![Instruction::Trap(42)].into(), + }] + .into() + } + + #[test] + fn xcm_messages_to_rialto_are_sent_using_bridge_exporter() { + new_test_ext().execute_with(|| { + // ensure that the there are no messages queued + assert_eq!( + OutboundLanes::::get( + crate::rialto_messages::XCM_LANE + ) + .latest_generated_nonce, + 0, + ); + + // export message instruction "sends" message to Rialto + XcmExecutor::::execute_xcm_in_credit( + Here, + prepare_outbound_xcm_message(RialtoNetwork::get()), + Default::default(), + Weight::MAX, + Weight::MAX, + ) + .ensure_complete() + .expect("runtime configuration must be correct"); + + // ensure that the message has been queued + assert_eq!( + OutboundLanes::::get( + crate::rialto_messages::XCM_LANE + ) + .latest_generated_nonce, + 1, + ); + }) + } + + #[test] + fn xcm_messages_to_rialto_parachain_are_sent_using_bridge_exporter() { + new_test_ext().execute_with(|| { + // ensure that the there are no messages queued + assert_eq!( + OutboundLanes::::get( + crate::rialto_parachain_messages::XCM_LANE + ) + .latest_generated_nonce, + 0, + ); + + // export message instruction "sends" message to Rialto + XcmExecutor::::execute_xcm_in_credit( + Here, + prepare_outbound_xcm_message(RialtoParachainNetwork::get()), + Default::default(), + Weight::MAX, + Weight::MAX, + ) + .ensure_complete() + .expect("runtime configuration must be correct"); + + // ensure that the message has been queued + assert_eq!( + OutboundLanes::::get( + crate::rialto_parachain_messages::XCM_LANE + ) + .latest_generated_nonce, + 1, + ); + }) + } + + fn prepare_inbound_bridge_message() -> DispatchMessage> { + let xcm = xcm::VersionedXcm::::V3(vec![Instruction::Trap(42)].into()); + let location = + xcm::VersionedInteriorMultiLocation::V3(X1(GlobalConsensus(ThisNetwork::get()))); + // this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor + // or public fields, so just tuple + let bridge_message = (location, xcm).encode(); + DispatchMessage { + key: MessageKey { lane_id: LaneId([0, 0, 0, 0]), nonce: 1 }, + data: DispatchMessageData { payload: Ok(bridge_message) }, + } + } + + #[test] + fn xcm_messages_from_rialto_are_dispatched() { + let incoming_message = prepare_inbound_bridge_message(); + + // we care only about handing message to the XCM dispatcher, so we don't care about its + // actual dispatch + let dispatch_result = + FromRialtoMessageDispatch::dispatch(&AccountId::from([0u8; 32]), incoming_message); + assert!(matches!( + dispatch_result.dispatch_level_result, + XcmBlobMessageDispatchResult::NotDispatched(_), + )); + } + + #[test] + fn xcm_messages_from_rialto_parachain_are_dispatched() { + let incoming_message = prepare_inbound_bridge_message(); + + // we care only about handing message to the XCM dispatcher, so we don't care about its + // actual dispatch + let dispatch_result = + FromRialtoMessageDispatch::dispatch(&AccountId::from([0u8; 32]), incoming_message); + assert!(matches!( + dispatch_result.dispatch_level_result, + XcmBlobMessageDispatchResult::NotDispatched(_), + )); + } +} diff --git a/bin/rialto-parachain/node/Cargo.toml b/bin/rialto-parachain/node/Cargo.toml new file mode 100644 index 00000000000..2315eb94a11 --- /dev/null +++ b/bin/rialto-parachain/node/Cargo.toml @@ -0,0 +1,81 @@ +[package] +name = "rialto-parachain-collator" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +repository = "https://github.com/paritytech/parity-bridges-common/" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[build-dependencies] +substrate-build-script-utils = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[[bin]] +name = 'rialto-parachain-collator' + +[features] +default = [] +runtime-benchmarks = ['rialto-parachain-runtime/runtime-benchmarks'] + +[dependencies] +clap = { version = "4.2.2", features = ["derive"] } +log = '0.4.17' +codec = { package = 'parity-scale-codec', version = '3.1.5' } +serde = { version = '1.0', features = ['derive'] } + +# RPC related Dependencies +jsonrpsee = { version = "0.16.2", features = ["server"] } + +# Local Dependencies +rialto-parachain-runtime = { path = '../runtime' } + +# Substrate Dependencies +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } + +pallet-transaction-payment-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } + +substrate-frame-rpc-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +substrate-prometheus-endpoint = { git = "https://github.com/paritytech/substrate", branch = "master" } + +## Substrate Client Dependencies +sc-basic-authorship = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-chain-spec = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-client-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-consensus = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-network = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-network-sync = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-rpc-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-service = { git = "https://github.com/paritytech/substrate", branch = "master"} +sc-telemetry = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-tracing = { git = "https://github.com/paritytech/substrate", branch = "master" } + +## Substrate Primitive Dependencies +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-session = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master" } + +# Cumulus dependencies +cumulus-client-consensus-aura = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-client-consensus-common = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-client-cli = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-client-network = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-client-service = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-primitives-parachain-inherent = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-relay-chain-interface = { git = "https://github.com/paritytech/cumulus", branch = "master" } + +# Polkadot dependencies +polkadot-cli = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "master" } diff --git a/bin/rialto-parachain/node/build.rs b/bin/rialto-parachain/node/build.rs new file mode 100644 index 00000000000..8ba8a31e9a7 --- /dev/null +++ b/bin/rialto-parachain/node/build.rs @@ -0,0 +1,22 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; + +fn main() { + generate_cargo_keys(); + rerun_if_git_head_changed(); +} diff --git a/bin/rialto-parachain/node/src/chain_spec.rs b/bin/rialto-parachain/node/src/chain_spec.rs new file mode 100644 index 00000000000..bfce4f717c6 --- /dev/null +++ b/bin/rialto-parachain/node/src/chain_spec.rs @@ -0,0 +1,198 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use cumulus_primitives_core::ParaId; +use rialto_parachain_runtime::{AccountId, AuraId, BridgeMillauMessagesConfig, Signature}; +use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; +use sc_service::ChainType; +use serde::{Deserialize, Serialize}; +use sp_core::{sr25519, Pair, Public}; +use sp_runtime::traits::{IdentifyAccount, Verify}; + +/// "Names" of the authorities accounts at local testnet. +const LOCAL_AUTHORITIES_ACCOUNTS: [&str; 2] = ["Alice", "Bob"]; +/// "Names" of the authorities accounts at development testnet. +const DEV_AUTHORITIES_ACCOUNTS: [&str; 2] = LOCAL_AUTHORITIES_ACCOUNTS; +/// "Names" of all possible authorities accounts. +const ALL_AUTHORITIES_ACCOUNTS: [&str; 2] = LOCAL_AUTHORITIES_ACCOUNTS; +/// "Name" of the `sudo` account. +const SUDO_ACCOUNT: &str = "Sudo"; +/// "Name" of the account, which owns the with-Millau messages pallet. +const MILLAU_MESSAGES_PALLET_OWNER: &str = "Millau.MessagesOwner"; + +/// Specialized `ChainSpec` for the normal parachain runtime. +pub type ChainSpec = + sc_service::GenericChainSpec; + +/// Helper function to generate a crypto pair from seed +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{seed}"), None) + .expect("static values are valid; qed") + .public() +} + +/// The extensions for the [`ChainSpec`]. +#[derive( + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension, +)] +#[serde(deny_unknown_fields)] +pub struct Extensions { + /// The relay chain of the Parachain. + pub relay_chain: String, + /// The id of the Parachain. + pub para_id: u32, +} + +impl Extensions { + /// Try to get the extension from the given `ChainSpec`. + pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { + sc_chain_spec::get_extension(chain_spec.extensions()) + } +} + +type AccountPublic = ::Signer; + +/// Helper function to generate an account ID from seed +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// We're using the same set of endowed accounts on all RialtoParachain chains (dev/local) to make +/// sure that all accounts, required for bridge to be functional (e.g. relayers fund account, +/// accounts used by relayers in our test deployments, accounts used for demonstration +/// purposes), are all available on these chains. +fn endowed_accounts() -> Vec { + let all_authorities = ALL_AUTHORITIES_ACCOUNTS.iter().flat_map(|x| { + [ + get_account_id_from_seed::(x), + get_account_id_from_seed::(&format!("{x}//stash")), + ] + }); + vec![ + // Sudo account + get_account_id_from_seed::(SUDO_ACCOUNT), + // Regular (unused) accounts + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + // Accounts, used by RialtoParachain<>Millau bridge + get_account_id_from_seed::(MILLAU_MESSAGES_PALLET_OWNER), + get_account_id_from_seed::("Millau.HeadersAndMessagesRelay1"), + get_account_id_from_seed::("Millau.HeadersAndMessagesRelay2"), + get_account_id_from_seed::("Millau.MessagesSender"), + ] + .into_iter() + .chain(all_authorities) + .collect() +} + +pub fn development_config(id: ParaId) -> ChainSpec { + // Give your base currency a unit name and decimal places + let mut properties = sc_chain_spec::Properties::new(); + properties.insert("tokenSymbol".into(), "UNIT".into()); + properties.insert("tokenDecimals".into(), 12.into()); + + ChainSpec::from_genesis( + // Name + "Development", + // ID + "dev", + ChainType::Local, + move || { + testnet_genesis( + get_account_id_from_seed::(SUDO_ACCOUNT), + DEV_AUTHORITIES_ACCOUNTS.into_iter().map(get_from_seed::).collect(), + endowed_accounts(), + id, + ) + }, + vec![], + None, + None, + None, + None, + Extensions { + relay_chain: "rococo-local".into(), // You MUST set this to the correct network! + para_id: id.into(), + }, + ) +} + +pub fn local_testnet_config(id: ParaId) -> ChainSpec { + // Give your base currency a unit name and decimal places + let mut properties = sc_chain_spec::Properties::new(); + properties.insert("tokenSymbol".into(), "UNIT".into()); + properties.insert("tokenDecimals".into(), 12.into()); + + ChainSpec::from_genesis( + // Name + "Local Testnet", + // ID + "local_testnet", + ChainType::Local, + move || { + testnet_genesis( + get_account_id_from_seed::(SUDO_ACCOUNT), + LOCAL_AUTHORITIES_ACCOUNTS.into_iter().map(get_from_seed::).collect(), + endowed_accounts(), + id, + ) + }, + Vec::new(), + None, + None, + None, + None, + Extensions { + relay_chain: "rococo-local".into(), // You MUST set this to the correct network! + para_id: id.into(), + }, + ) +} + +fn testnet_genesis( + root_key: AccountId, + initial_authorities: Vec, + endowed_accounts: Vec, + id: ParaId, +) -> rialto_parachain_runtime::GenesisConfig { + rialto_parachain_runtime::GenesisConfig { + system: rialto_parachain_runtime::SystemConfig { + code: rialto_parachain_runtime::WASM_BINARY + .expect("WASM binary was not build, please build it!") + .to_vec(), + }, + balances: rialto_parachain_runtime::BalancesConfig { + balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), + }, + sudo: rialto_parachain_runtime::SudoConfig { key: Some(root_key) }, + parachain_info: rialto_parachain_runtime::ParachainInfoConfig { parachain_id: id }, + aura: rialto_parachain_runtime::AuraConfig { authorities: initial_authorities }, + aura_ext: Default::default(), + bridge_millau_messages: BridgeMillauMessagesConfig { + owner: Some(get_account_id_from_seed::(MILLAU_MESSAGES_PALLET_OWNER)), + ..Default::default() + }, + } +} diff --git a/bin/rialto-parachain/node/src/cli.rs b/bin/rialto-parachain/node/src/cli.rs new file mode 100644 index 00000000000..a003c91113c --- /dev/null +++ b/bin/rialto-parachain/node/src/cli.rs @@ -0,0 +1,142 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![allow(clippy::large_enum_variant)] + +use crate::chain_spec; +use clap::Parser; +use std::path::PathBuf; + +/// Sub-commands supported by the collator. +#[derive(Debug, Parser)] +pub enum Subcommand { + /// Export the genesis state of the parachain. + #[clap(name = "export-genesis-state")] + ExportGenesisState(ExportGenesisStateCommand), + + /// Export the genesis wasm of the parachain. + #[clap(name = "export-genesis-wasm")] + ExportGenesisWasm(ExportGenesisWasmCommand), + + /// Build a chain specification. + BuildSpec(sc_cli::BuildSpecCmd), + + /// Validate blocks. + CheckBlock(sc_cli::CheckBlockCmd), + + /// Export blocks. + ExportBlocks(sc_cli::ExportBlocksCmd), + + /// Export the state of a given block into a chain spec. + ExportState(sc_cli::ExportStateCmd), + + /// Import blocks. + ImportBlocks(sc_cli::ImportBlocksCmd), + + /// Remove the whole chain. + PurgeChain(cumulus_client_cli::PurgeChainCmd), + + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), + + /// The custom benchmark subcommand benchmarking runtime pallets. + #[clap(subcommand)] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), +} + +/// Command for exporting the genesis state of the parachain +#[derive(Debug, Parser)] +pub struct ExportGenesisStateCommand { + /// Output file name or stdout if unspecified. + #[clap(action)] + pub output: Option, + + /// Id of the parachain this state is for. + /// + /// Default: 100 + #[clap(long, conflicts_with = "chain")] + pub parachain_id: Option, + + /// Write output in binary. Default is to write in hex. + #[clap(short, long)] + pub raw: bool, + + /// The name of the chain for that the genesis state should be exported. + #[clap(long, conflicts_with = "parachain-id")] + pub chain: Option, +} + +/// Command for exporting the genesis wasm file. +#[derive(Debug, Parser)] +pub struct ExportGenesisWasmCommand { + /// Output file name or stdout if unspecified. + #[clap(action)] + pub output: Option, + + /// Write output in binary. Default is to write in hex. + #[clap(short, long)] + pub raw: bool, + + /// The name of the chain for that the genesis wasm file should be exported. + #[clap(long)] + pub chain: Option, +} + +#[derive(Debug, Parser)] +#[clap( + propagate_version = true, + args_conflicts_with_subcommands = true, + subcommand_negates_reqs = true +)] +pub struct Cli { + #[clap(subcommand)] + pub subcommand: Option, + + #[clap(long)] + pub parachain_id: Option, + + #[clap(flatten)] + pub run: cumulus_client_cli::RunCmd, + + /// Relaychain arguments + #[clap(raw = true)] + pub relaychain_args: Vec, +} + +#[derive(Debug)] +pub struct RelayChainCli { + /// The actual relay chain CLI object. + pub base: polkadot_cli::RunCmd, + + /// Optional chain id that should be passed to the relay chain. + pub chain_id: Option, + + /// The base path that should be used by the relay chain. + pub base_path: Option, +} + +impl RelayChainCli { + /// Parse the relay chain CLI parameters using the para chain `Configuration`. + pub fn new<'a>( + para_config: &sc_service::Configuration, + relay_chain_args: impl Iterator, + ) -> Self { + let extension = chain_spec::Extensions::try_get(&*para_config.chain_spec); + let chain_id = extension.map(|e| e.relay_chain.clone()); + let base_path = para_config.base_path.as_ref().map(|x| x.path().join("rialto-bridge-node")); + Self { base_path, chain_id, base: polkadot_cli::RunCmd::parse_from(relay_chain_args) } + } +} diff --git a/bin/rialto-parachain/node/src/command.rs b/bin/rialto-parachain/node/src/command.rs new file mode 100644 index 00000000000..7393b014732 --- /dev/null +++ b/bin/rialto-parachain/node/src/command.rs @@ -0,0 +1,445 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{ + chain_spec, + cli::{Cli, RelayChainCli, Subcommand}, + service::{new_partial, ParachainRuntimeExecutor}, +}; +use codec::Encode; +use cumulus_client_cli::generate_genesis_block; +use cumulus_primitives_core::ParaId; +use frame_benchmarking_cli::BenchmarkCmd; +use log::info; +use rialto_parachain_runtime::{Block, RuntimeApi}; +use sc_cli::{ + ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, + NetworkParams, Result, RuntimeVersion, SharedParams, SubstrateCli, +}; +use sc_service::config::{BasePath, PrometheusConfig}; +use sp_core::hexdisplay::HexDisplay; +use sp_runtime::traits::{AccountIdConversion, Block as BlockT}; +use std::{io::Write, net::SocketAddr}; + +fn load_spec( + id: &str, + para_id: ParaId, +) -> std::result::Result, String> { + Ok(match id { + "dev" => Box::new(chain_spec::development_config(para_id)), + "" | "local" => Box::new(chain_spec::local_testnet_config(para_id)), + path => Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?), + }) +} + +impl SubstrateCli for Cli { + fn impl_name() -> String { + "Parachain Collator Template".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + format!( + "Parachain Collator Template\n\nThe command-line arguments provided first will be \ + passed to the parachain node, while the arguments provided after -- will be passed \ + to the relaychain node.\n\n\ + {} [parachain-args] -- [relaychain-args]", + Self::executable_name() + ) + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/substrate-developer-hub/substrate-parachain-template/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2017 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + load_spec(id, self.parachain_id.unwrap_or(2000).into()) + } + + fn native_runtime_version(_: &Box) -> &'static RuntimeVersion { + &rialto_parachain_runtime::VERSION + } +} + +impl SubstrateCli for RelayChainCli { + fn impl_name() -> String { + "Parachain Collator Template".into() + } + + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn description() -> String { + "Parachain Collator Template\n\nThe command-line arguments provided first will be \ + passed to the parachain node, while the arguments provided after -- will be passed \ + to the relaychain node.\n\n\ + parachain-collator [parachain-args] -- [relaychain-args]" + .into() + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/substrate-developer-hub/substrate-parachain-template/issues/new".into() + } + + fn copyright_start_year() -> i32 { + 2017 + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id) + } + + fn native_runtime_version(chain_spec: &Box) -> &'static RuntimeVersion { + polkadot_cli::Cli::native_runtime_version(chain_spec) + } +} + +fn extract_genesis_wasm(chain_spec: &dyn sc_service::ChainSpec) -> Result> { + let mut storage = chain_spec.build_storage()?; + + storage + .top + .remove(sp_core::storage::well_known_keys::CODE) + .ok_or_else(|| "Could not find wasm file in genesis state!".into()) +} + +macro_rules! construct_async_run { + (|$components:ident, $cli:ident, $cmd:ident, $config:ident| $( $code:tt )* ) => {{ + let runner = $cli.create_runner($cmd)?; + runner.async_run(|$config| { + let $components = new_partial::< + RuntimeApi, + _ + >( + &$config, + crate::service::parachain_build_import_queue, + )?; + let task_manager = $components.task_manager; + { $( $code )* }.map(|v| (v, task_manager)) + }) + }} +} + +/// Parse command line arguments into service configuration. +pub fn run() -> Result<()> { + let cli = Cli::from_args(); + sp_core::crypto::set_default_ss58_version(sp_core::crypto::Ss58AddressFormat::custom( + rialto_parachain_runtime::SS58Prefix::get() as u16, + )); + + match &cli.subcommand { + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) + }, + Some(Subcommand::CheckBlock(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, components.import_queue)) + }) + }, + Some(Subcommand::ExportBlocks(cmd)) => { + construct_async_run!(|components, cli, cmd, config| Ok( + cmd.run(components.client, config.database) + )) + }, + Some(Subcommand::ExportState(cmd)) => { + construct_async_run!(|components, cli, cmd, config| Ok( + cmd.run(components.client, config.chain_spec) + )) + }, + Some(Subcommand::ImportBlocks(cmd)) => { + construct_async_run!(|components, cli, cmd, config| { + Ok(cmd.run(components.client, components.import_queue)) + }) + }, + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + + runner.sync_run(|config| { + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()].iter().chain(cli.relaychain_args.iter()), + ); + + let polkadot_config = SubstrateCli::create_configuration( + &polkadot_cli, + &polkadot_cli, + config.tokio_handle.clone(), + ) + .map_err(|err| format!("Relay chain argument error: {err}"))?; + + cmd.run(config, polkadot_config) + }) + }, + Some(Subcommand::Revert(cmd)) => { + construct_async_run!(|components, cli, cmd, config| Ok(cmd.run( + components.client, + components.backend, + None + ))) + }, + Some(Subcommand::ExportGenesisState(params)) => { + let mut builder = sc_cli::LoggerBuilder::new(""); + builder.with_profiling(sc_tracing::TracingReceiver::Log, ""); + let _ = builder.init(); + + let spec = load_spec( + ¶ms.chain.clone().unwrap_or_default(), + params.parachain_id.expect("Missing ParaId").into(), + )?; + let state_version = Cli::native_runtime_version(&spec).state_version(); + let block: Block = generate_genesis_block(&*spec, state_version)?; + let raw_header = block.header().encode(); + let output_buf = if params.raw { + raw_header + } else { + format!("0x{:?}", HexDisplay::from(&block.header().encode())).into_bytes() + }; + + if let Some(output) = ¶ms.output { + std::fs::write(output, output_buf)?; + } else { + std::io::stdout().write_all(&output_buf)?; + } + + Ok(()) + }, + Some(Subcommand::ExportGenesisWasm(params)) => { + let mut builder = sc_cli::LoggerBuilder::new(""); + builder.with_profiling(sc_tracing::TracingReceiver::Log, ""); + let _ = builder.init(); + + let raw_wasm_blob = + extract_genesis_wasm(&*cli.load_spec(¶ms.chain.clone().unwrap_or_default())?)?; + let output_buf = if params.raw { + raw_wasm_blob + } else { + format!("0x{:?}", HexDisplay::from(&raw_wasm_blob)).into_bytes() + }; + + if let Some(output) = ¶ms.output { + std::fs::write(output, output_buf)?; + } else { + std::io::stdout().write_all(&output_buf)?; + } + + Ok(()) + }, + Some(Subcommand::Benchmark(cmd)) => { + let runner = cli.create_runner(cmd)?; + match cmd { + BenchmarkCmd::Pallet(cmd) => + if cfg!(feature = "runtime-benchmarks") { + runner.sync_run(|config| cmd.run::(config)) + } else { + println!( + "Benchmarking wasn't enabled when building the node. \ + You can enable it with `--features runtime-benchmarks`." + ); + Ok(()) + }, + _ => Err("Unsupported benchmarking subcommand".into()), + } + }, + None => { + let runner = cli.create_runner(&cli.run.normalize())?; + let collator_options = cli.run.collator_options(); + + runner.run_node_until_exit(|config| async move { + let para_id = + chain_spec::Extensions::try_get(&*config.chain_spec).map(|e| e.para_id); + + let polkadot_cli = RelayChainCli::new( + &config, + [RelayChainCli::executable_name()].iter().chain(cli.relaychain_args.iter()), + ); + + let id = ParaId::from(cli.parachain_id.or(para_id).expect("Missing ParaId")); + + let parachain_account = + AccountIdConversion::::into_account_truncating(&id); + + let state_version = + RelayChainCli::native_runtime_version(&config.chain_spec).state_version(); + let block: Block = generate_genesis_block(&*config.chain_spec, state_version) + .map_err(|e| format!("{e:?}"))?; + let genesis_state = format!("0x{:?}", HexDisplay::from(&block.header().encode())); + + let polkadot_config = SubstrateCli::create_configuration( + &polkadot_cli, + &polkadot_cli, + config.tokio_handle.clone(), + ) + .map_err(|err| format!("Relay chain argument error: {err}"))?; + + info!("Parachain id: {:?}", id); + info!("Parachain Account: {}", parachain_account); + info!("Parachain genesis state: {}", genesis_state); + info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" }); + + crate::service::start_node(config, polkadot_config, collator_options, id) + .await + .map(|r| r.0) + .map_err(Into::into) + }) + }, + } +} + +impl DefaultConfigurationValues for RelayChainCli { + fn p2p_listen_port() -> u16 { + 30334 + } + + fn rpc_ws_listen_port() -> u16 { + 9945 + } + + fn rpc_http_listen_port() -> u16 { + 9934 + } + + fn prometheus_listen_port() -> u16 { + 9616 + } +} + +impl CliConfiguration for RelayChainCli { + fn shared_params(&self) -> &SharedParams { + self.base.base.shared_params() + } + + fn import_params(&self) -> Option<&ImportParams> { + self.base.base.import_params() + } + + fn network_params(&self) -> Option<&NetworkParams> { + self.base.base.network_params() + } + + fn keystore_params(&self) -> Option<&KeystoreParams> { + self.base.base.keystore_params() + } + + fn base_path(&self) -> Result> { + Ok(self + .shared_params() + .base_path()? + .or_else(|| self.base_path.clone().map(Into::into))) + } + + fn rpc_http(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_http(default_listen_port) + } + + fn rpc_ipc(&self) -> Result> { + self.base.base.rpc_ipc() + } + + fn rpc_ws(&self, default_listen_port: u16) -> Result> { + self.base.base.rpc_ws(default_listen_port) + } + + fn prometheus_config( + &self, + default_listen_port: u16, + chain_spec: &Box, + ) -> Result> { + self.base.base.prometheus_config(default_listen_port, chain_spec) + } + + fn init( + &self, + _support_url: &String, + _impl_version: &String, + _logger_hook: F, + _config: &sc_service::Configuration, + ) -> Result<()> + where + F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), + { + unreachable!("PolkadotCli is never initialized; qed"); + } + + fn chain_id(&self, is_dev: bool) -> Result { + let chain_id = self.base.base.chain_id(is_dev)?; + + Ok(if chain_id.is_empty() { self.chain_id.clone().unwrap_or_default() } else { chain_id }) + } + + fn role(&self, is_dev: bool) -> Result { + self.base.base.role(is_dev) + } + + fn transaction_pool(&self, is_dev: bool) -> Result { + self.base.base.transaction_pool(is_dev) + } + + fn rpc_methods(&self) -> Result { + self.base.base.rpc_methods() + } + + fn rpc_ws_max_connections(&self) -> Result> { + self.base.base.rpc_ws_max_connections() + } + + fn rpc_cors(&self, is_dev: bool) -> Result>> { + self.base.base.rpc_cors(is_dev) + } + + fn default_heap_pages(&self) -> Result> { + self.base.base.default_heap_pages() + } + + fn force_authoring(&self) -> Result { + self.base.base.force_authoring() + } + + fn disable_grandpa(&self) -> Result { + self.base.base.disable_grandpa() + } + + fn max_runtime_instances(&self) -> Result> { + self.base.base.max_runtime_instances() + } + + fn announce_block(&self) -> Result { + self.base.base.announce_block() + } + + fn telemetry_endpoints( + &self, + chain_spec: &Box, + ) -> Result> { + self.base.base.telemetry_endpoints(chain_spec) + } +} diff --git a/bin/rialto-parachain/node/src/lib.rs b/bin/rialto-parachain/node/src/lib.rs new file mode 100644 index 00000000000..3ec291596b7 --- /dev/null +++ b/bin/rialto-parachain/node/src/lib.rs @@ -0,0 +1,18 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +pub mod chain_spec; +pub mod service; diff --git a/bin/rialto-parachain/node/src/main.rs b/bin/rialto-parachain/node/src/main.rs new file mode 100644 index 00000000000..2b4e0b438d1 --- /dev/null +++ b/bin/rialto-parachain/node/src/main.rs @@ -0,0 +1,29 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate Parachain Node Template CLI + +#![warn(missing_docs)] + +mod chain_spec; +#[macro_use] +mod service; +mod cli; +mod command; + +fn main() -> sc_cli::Result<()> { + command::run() +} diff --git a/bin/rialto-parachain/node/src/service.rs b/bin/rialto-parachain/node/src/service.rs new file mode 100644 index 00000000000..24eb94e655d --- /dev/null +++ b/bin/rialto-parachain/node/src/service.rs @@ -0,0 +1,510 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rialto parachain node service. +//! +//! The code is mostly copy of `polkadot-parachains/src/service.rs` file from Cumulus +//! repository with some parts removed. We have added two RPC extensions to the original +//! service: `pallet_transaction_payment_rpc::TransactionPaymentApi` and +//! `substrate_frame_rpc_system::SystemApi`. + +// std +use std::{sync::Arc, time::Duration}; + +// Local Runtime Types +use rialto_parachain_runtime::RuntimeApi; + +// Cumulus Imports +use cumulus_client_cli::CollatorOptions; +use cumulus_client_consensus_aura::{AuraConsensus, BuildAuraConsensusParams, SlotProportion}; +use cumulus_client_consensus_common::{ + ParachainBlockImport as TParachainBlockImport, ParachainConsensus, +}; +use cumulus_client_network::BlockAnnounceValidator; +use cumulus_client_service::{ + prepare_node_config, start_collator, start_full_node, StartCollatorParams, StartFullNodeParams, +}; +use cumulus_primitives_core::ParaId; +use cumulus_relay_chain_interface::RelayChainInterface; +use sc_consensus::ImportQueue; +// Substrate Imports +use sc_executor::{ + HeapAllocStrategy, NativeElseWasmExecutor, NativeExecutionDispatch, WasmExecutor, + DEFAULT_HEAP_ALLOC_STRATEGY, +}; +use sc_network::NetworkBlock; +use sc_network_sync::SyncingService; +use sc_service::{Configuration, PartialComponents, TFullBackend, TFullClient, TaskManager}; +use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle}; +use sp_api::ConstructRuntimeApi; +use sp_keystore::KeystorePtr; +use sp_runtime::traits::BlakeTwo256; +use substrate_prometheus_endpoint::Registry; + +// Runtime type overrides +type BlockNumber = u32; +type Header = sp_runtime::generic::Header; +pub type Block = sp_runtime::generic::Block; + +type ParachainClient = + TFullClient>; +type ParachainBackend = TFullBackend; +type ParachainBlockImport = + TParachainBlockImport>, ParachainBackend>; + +pub type ParachainRuntimeExecutor = ExecutorDispatch; + +// Our native executor instance. +pub struct ExecutorDispatch; + +impl NativeExecutionDispatch for ExecutorDispatch { + type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; + + fn dispatch(method: &str, data: &[u8]) -> Option> { + rialto_parachain_runtime::api::dispatch(method, data) + } + + fn native_version() -> sc_executor::NativeVersion { + rialto_parachain_runtime::native_version() + } +} + +/// Starts a `ServiceBuilder` for a full service. +/// +/// Use this macro if you don't actually need the full service, but just the builder in order to +/// be able to perform chain operations. +#[allow(clippy::type_complexity)] +pub fn new_partial( + config: &Configuration, + build_import_queue: BIQ, +) -> Result< + PartialComponents< + ParachainClient, + ParachainBackend, + (), + sc_consensus::DefaultImportQueue>, + sc_transaction_pool::FullPool>, + (ParachainBlockImport, Option, Option), + >, + sc_service::Error, +> +where + RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, + RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue + + sp_api::Metadata + + sp_session::SessionKeys + + sp_api::ApiExt< + Block, + StateBackend = sc_client_api::StateBackendFor, + > + sp_offchain::OffchainWorkerApi + + sp_block_builder::BlockBuilder, + sc_client_api::StateBackendFor: sp_api::StateBackend, + BIQ: FnOnce( + Arc>, + ParachainBlockImport, + &Configuration, + Option, + &TaskManager, + ) -> Result< + sc_consensus::DefaultImportQueue>, + sc_service::Error, + >, +{ + let telemetry = config + .telemetry_endpoints + .clone() + .filter(|x| !x.is_empty()) + .map(|endpoints| -> Result<_, sc_telemetry::Error> { + let worker = TelemetryWorker::new(16)?; + let telemetry = worker.handle().new_telemetry(endpoints); + Ok((worker, telemetry)) + }) + .transpose()?; + + let heap_pages = config + .default_heap_pages + .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static { extra_pages: h as _ }); + let executor = + sc_executor::NativeElseWasmExecutor::::new_with_wasm_executor( + WasmExecutor::builder() + .with_execution_method(config.wasm_method) + .with_onchain_heap_alloc_strategy(heap_pages) + .with_offchain_heap_alloc_strategy(heap_pages) + .with_max_runtime_instances(config.max_runtime_instances) + .with_runtime_cache_size(config.runtime_cache_size) + .build(), + ); + + let (client, backend, keystore_container, task_manager) = + sc_service::new_full_parts::( + config, + telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), + executor, + )?; + let client = Arc::new(client); + + let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle()); + + let telemetry = telemetry.map(|(worker, telemetry)| { + task_manager.spawn_handle().spawn("telemetry", None, worker.run()); + telemetry + }); + + let transaction_pool = sc_transaction_pool::BasicPool::new_full( + config.transaction_pool.clone(), + config.role.is_authority().into(), + config.prometheus_registry(), + task_manager.spawn_essential_handle(), + client.clone(), + ); + + let block_import = ParachainBlockImport::new(client.clone(), backend.clone()); + + let import_queue = build_import_queue( + client.clone(), + block_import.clone(), + config, + telemetry.as_ref().map(|telemetry| telemetry.handle()), + &task_manager, + )?; + + let params = PartialComponents { + backend, + client, + import_queue, + keystore_container, + task_manager, + transaction_pool, + select_chain: (), + other: (block_import, telemetry, telemetry_worker_handle), + }; + + Ok(params) +} + +/// Start a node with the given parachain `Configuration` and relay chain `Configuration`. +/// +/// This is the actual implementation that is abstract over the executor and the runtime api. +#[sc_tracing::logging::prefix_logs_with("Parachain")] +async fn start_node_impl( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + id: ParaId, + rpc_ext_builder: RB, + build_import_queue: BIQ, + build_consensus: BIC, +) -> sc_service::error::Result<(TaskManager, Arc>)> +where + RuntimeApi: ConstructRuntimeApi> + Send + Sync + 'static, + RuntimeApi::RuntimeApi: sp_transaction_pool::runtime_api::TaggedTransactionQueue + + sp_api::Metadata + + sp_session::SessionKeys + + sp_api::ApiExt< + Block, + StateBackend = sc_client_api::StateBackendFor, + > + sp_offchain::OffchainWorkerApi + + sp_block_builder::BlockBuilder + + cumulus_primitives_core::CollectCollationInfo, + sc_client_api::StateBackendFor: sp_api::StateBackend, + RB: Fn( + sc_rpc_api::DenyUnsafe, + Arc>, + Arc>>, + ) -> Result, sc_service::Error> + + Send + + Clone + + 'static, + BIQ: FnOnce( + Arc>, + ParachainBlockImport, + &Configuration, + Option, + &TaskManager, + ) -> Result< + sc_consensus::DefaultImportQueue>, + sc_service::Error, + >, + BIC: FnOnce( + Arc>, + ParachainBlockImport, + Option<&Registry>, + Option, + &TaskManager, + Arc, + Arc>>, + Arc>, + KeystorePtr, + bool, + ) -> Result>, sc_service::Error>, +{ + let parachain_config = prepare_node_config(parachain_config); + + let params = new_partial::(¶chain_config, build_import_queue)?; + let (block_import, mut telemetry, telemetry_worker_handle) = params.other; + + let mut task_manager = params.task_manager; + let (relay_chain_interface, collator_key) = + cumulus_client_service::build_relay_chain_interface( + polkadot_config, + ¶chain_config, + telemetry_worker_handle, + &mut task_manager, + collator_options, + None, + ) + .await + .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; + + let client = params.client.clone(); + let backend = params.backend.clone(); + let block_announce_validator = BlockAnnounceValidator::new(relay_chain_interface.clone(), id); + + let force_authoring = parachain_config.force_authoring; + let validator = parachain_config.role.is_authority(); + let prometheus_registry = parachain_config.prometheus_registry().cloned(); + let transaction_pool = params.transaction_pool.clone(); + let import_queue_service = params.import_queue.service(); + + let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = + sc_service::build_network(sc_service::BuildNetworkParams { + config: ¶chain_config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + spawn_handle: task_manager.spawn_handle(), + import_queue: params.import_queue, + block_announce_validator_builder: Some(Box::new(|_| { + Box::new(block_announce_validator) + })), + warp_sync_params: None, + })?; + + let rpc_client = client.clone(); + let rpc_transaction_pool = transaction_pool.clone(); + let rpc_extensions_builder = Box::new(move |deny_unsafe, _| { + rpc_ext_builder(deny_unsafe, rpc_client.clone(), rpc_transaction_pool.clone()) + }); + + sc_service::spawn_tasks(sc_service::SpawnTasksParams { + rpc_builder: rpc_extensions_builder.clone(), + client: client.clone(), + transaction_pool: transaction_pool.clone(), + task_manager: &mut task_manager, + config: parachain_config, + keystore: params.keystore_container.keystore(), + backend: backend.clone(), + network: network.clone(), + sync_service: sync_service.clone(), + system_rpc_tx, + tx_handler_controller, + telemetry: telemetry.as_mut(), + })?; + + let announce_block = { + let sync_service = sync_service.clone(); + Arc::new(move |hash, data| sync_service.announce_block(hash, data)) + }; + + let relay_chain_slot_duration = Duration::from_secs(6); + + let overseer_handle = relay_chain_interface + .overseer_handle() + .map_err(|e| sc_service::Error::Application(Box::new(e)))?; + + if validator { + let parachain_consensus = build_consensus( + client.clone(), + block_import, + prometheus_registry.as_ref(), + telemetry.as_ref().map(|t| t.handle()), + &task_manager, + relay_chain_interface.clone(), + transaction_pool, + sync_service, + params.keystore_container.keystore(), + force_authoring, + )?; + + let spawner = task_manager.spawn_handle(); + + let params = StartCollatorParams { + para_id: id, + block_status: client.clone(), + announce_block, + client: client.clone(), + task_manager: &mut task_manager, + relay_chain_interface, + spawner, + parachain_consensus, + import_queue: import_queue_service, + collator_key: collator_key.expect("Command line arguments do not allow this. qed"), + relay_chain_slot_duration, + recovery_handle: Box::new(overseer_handle), + }; + + start_collator(params).await?; + } else { + let params = StartFullNodeParams { + client: client.clone(), + announce_block, + task_manager: &mut task_manager, + para_id: id, + relay_chain_interface, + relay_chain_slot_duration, + import_queue: import_queue_service, + recovery_handle: Box::new(overseer_handle), + }; + + start_full_node(params)?; + } + + start_network.start_network(); + + Ok((task_manager, client)) +} + +/// Build the import queue for the the parachain runtime. +#[allow(clippy::type_complexity)] +pub fn parachain_build_import_queue( + client: Arc>, + block_import: ParachainBlockImport, + config: &Configuration, + telemetry: Option, + task_manager: &TaskManager, +) -> Result>, sc_service::Error> +{ + let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; + + cumulus_client_consensus_aura::import_queue::< + sp_consensus_aura::sr25519::AuthorityPair, + _, + _, + _, + _, + _, + >(cumulus_client_consensus_aura::ImportQueueParams { + block_import, + client, + create_inherent_data_providers: move |_, _| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + Ok((slot, timestamp)) + }, + registry: config.prometheus_registry(), + spawner: &task_manager.spawn_essential_handle(), + telemetry, + }) + .map_err(Into::into) +} + +/// Start a normal parachain node. +pub async fn start_node( + parachain_config: Configuration, + polkadot_config: Configuration, + collator_options: CollatorOptions, + id: ParaId, +) -> sc_service::error::Result<(TaskManager, Arc>)> { + start_node_impl::( + parachain_config, + polkadot_config, + collator_options, + id, + |_deny_unsafe, client, pool| { + use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; + use sc_rpc::DenyUnsafe; + use substrate_frame_rpc_system::{System, SystemApiServer}; + + let mut io = jsonrpsee::RpcModule::new(()); + let map_err = |e| sc_service::Error::Other(format!("{e}")); + io.merge(System::new(client.clone(), pool, DenyUnsafe::No).into_rpc()) + .map_err(map_err)?; + io.merge(TransactionPayment::new(client).into_rpc()).map_err(map_err)?; + Ok(io) + }, + parachain_build_import_queue, + |client, + block_import, + prometheus_registry, + telemetry, + task_manager, + relay_chain_interface, + transaction_pool, + sync_oracle, + keystore, + force_authoring| { + let client2 = client.clone(); + let block_import2 = block_import; + let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?; + + let proposer_factory = sc_basic_authorship::ProposerFactory::with_proof_recording( + task_manager.spawn_handle(), + client, + transaction_pool, + prometheus_registry, + telemetry.clone(), + ); + + Ok(AuraConsensus::build::( + BuildAuraConsensusParams { + proposer_factory, + create_inherent_data_providers: move |_, (relay_parent, validation_data)| { + let relay_chain_interface = relay_chain_interface.clone(); + async move { + let parachain_inherent = + cumulus_primitives_parachain_inherent::ParachainInherentData::create_at( + relay_parent, + &relay_chain_interface, + &validation_data, + id, + ).await; + let time = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *time, + slot_duration, + ); + + let parachain_inherent = parachain_inherent.ok_or_else(|| { + Box::::from( + "Failed to create parachain inherent", + ) + })?; + Ok((slot, time, parachain_inherent)) + } + }, + block_import: block_import2, + para_client: client2, + backoff_authoring_blocks: Option::<()>::None, + sync_oracle, + keystore, + force_authoring, + slot_duration, + // We got around 500ms for proposing + block_proposal_slot_portion: SlotProportion::new(1f32 / 24f32), + telemetry, + max_block_proposal_slot_portion: None, + }, + )) + }, + ) + .await +} diff --git a/bin/rialto-parachain/runtime/Cargo.toml b/bin/rialto-parachain/runtime/Cargo.toml new file mode 100644 index 00000000000..53f57e02619 --- /dev/null +++ b/bin/rialto-parachain/runtime/Cargo.toml @@ -0,0 +1,139 @@ +[package] +name = "rialto-parachain-runtime" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +repository = "https://github.com/paritytech/parity-bridges-common/" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[build-dependencies] +substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[dependencies] +codec = { package = 'parity-scale-codec', version = '3.1.5', default-features = false, features = ['derive']} +hex-literal = "0.4" +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } + +# Bridge depedencies + +bp-messages = { path = "../../../primitives/messages", default-features = false } +bp-millau = { path = "../../../primitives/chain-millau", default-features = false } +bp-relayers = { path = "../../../primitives/relayers", default-features = false } +bp-runtime = { path = "../../../primitives/runtime", default-features = false } +bp-rialto-parachain = { path = "../../../primitives/chain-rialto-parachain", default-features = false } +bridge-runtime-common = { path = "../../runtime-common", default-features = false } +pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false } +pallet-bridge-messages = { path = "../../../modules/messages", default-features = false } +pallet-bridge-relayers = { path = "../../../modules/relayers", default-features = false } + +# Substrate Dependencies +## Substrate Primitive Dependencies +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +## Substrate FRAME Dependencies +frame-executive = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +## Substrate Pallet Dependencies +pallet-aura = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +# Cumulus Dependencies +cumulus-pallet-aura-ext = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } +cumulus-pallet-parachain-system = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } +cumulus-pallet-dmp-queue = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } +cumulus-pallet-xcm = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } +cumulus-pallet-xcmp-queue = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } +cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } +cumulus-primitives-timestamp = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } +parachain-info = { git = "https://github.com/paritytech/cumulus", branch = "master", default-features = false } + +# Polkadot Dependencies +polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + +[dev-dependencies] +bridge-runtime-common = { path = "../../runtime-common", features = ["integrity-test"] } + +[features] +default = ['std'] +runtime-benchmarks = [ + 'sp-runtime/runtime-benchmarks', + 'frame-benchmarking', + 'frame-support/runtime-benchmarks', + 'frame-system-benchmarking/runtime-benchmarks', + 'frame-system/runtime-benchmarks', + 'pallet-balances/runtime-benchmarks', + 'pallet-timestamp/runtime-benchmarks', + 'pallet-xcm/runtime-benchmarks', + 'xcm-builder/runtime-benchmarks', +] +std = [ + "bp-messages/std", + "bp-millau/std", + "bp-relayers/std", + "bp-runtime/std", + "bp-rialto-parachain/std", + "bridge-runtime-common/std", + "codec/std", + "scale-info/std", + "sp-api/std", + "sp-std/std", + "sp-io/std", + "sp-core/std", + "sp-runtime/std", + "sp-version/std", + "sp-offchain/std", + "sp-session/std", + "sp-block-builder/std", + "sp-transaction-pool/std", + "sp-inherents/std", + "frame-support/std", + "frame-executive/std", + "frame-system/std", + "frame-system-rpc-runtime-api/std", + "pallet-balances/std", + "pallet-bridge-grandpa/std", + "pallet-bridge-messages/std", + "pallet-bridge-relayers/std", + "pallet-timestamp/std", + "pallet-sudo/std", + "pallet-transaction-payment/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-xcm/std", + "parachain-info/std", + "polkadot-parachain/std", + "cumulus-pallet-aura-ext/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-pallet-xcm/std", + "cumulus-primitives-core/std", + "cumulus-primitives-timestamp/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", + "pallet-aura/std", + "sp-consensus-aura/std", +] diff --git a/bin/rialto-parachain/runtime/build.rs b/bin/rialto-parachain/runtime/build.rs new file mode 100644 index 00000000000..65095bd1b7e --- /dev/null +++ b/bin/rialto-parachain/runtime/build.rs @@ -0,0 +1,25 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .export_heap_base() + .import_memory() + .build() +} diff --git a/bin/rialto-parachain/runtime/src/lib.rs b/bin/rialto-parachain/runtime/src/lib.rs new file mode 100644 index 00000000000..cd4e256f420 --- /dev/null +++ b/bin/rialto-parachain/runtime/src/lib.rs @@ -0,0 +1,959 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! The Rialto parachain runtime. This can be compiled with `#[no_std]`, ready for Wasm. +//! +//! Originally a copy of runtime from . + +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +use bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages; +use codec::{Decode, Encode}; +use cumulus_pallet_parachain_system::AnyRelayNumber; +use scale_info::TypeInfo; +use sp_api::impl_runtime_apis; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{AccountIdLookup, Block as BlockT, DispatchInfoOf, SignedExtension}, + transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError}, + ApplyExtrinsicResult, +}; + +use sp_std::prelude::*; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +// A few exports that help ease life for downstream crates. +use bp_runtime::HeaderId; +pub use frame_support::{ + construct_runtime, + dispatch::DispatchClass, + match_types, parameter_types, + traits::{ConstU32, Everything, IsInVec, Nothing, Randomness}, + weights::{ + constants::{ + BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, + }, + IdentityFee, Weight, + }, + StorageValue, +}; +pub use frame_system::{Call as SystemCall, EnsureRoot}; +pub use pallet_balances::Call as BalancesCall; +pub use pallet_sudo::Call as SudoCall; +pub use pallet_timestamp::Call as TimestampCall; +pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; +pub use sp_runtime::{MultiAddress, Perbill, Permill}; + +pub use bp_rialto_parachain::{ + AccountId, Balance, BlockLength, BlockNumber, BlockWeights, Hash, Hasher as Hashing, Header, + Index, Signature, MAXIMUM_BLOCK_WEIGHT, +}; + +pub use pallet_bridge_grandpa::Call as BridgeGrandpaCall; +pub use pallet_bridge_messages::Call as MessagesCall; +pub use pallet_xcm::Call as XcmCall; + +// Polkadot & XCM imports +use bridge_runtime_common::CustomNetworkId; +use pallet_xcm::XcmPassthrough; +use polkadot_parachain::primitives::Sibling; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, + NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, +}; +use xcm_executor::{Config, XcmExecutor}; + +pub mod millau_messages; + +// generate signed extension that rejects obsolete bridge transactions +generate_bridge_reject_obsolete_headers_and_messages! { + RuntimeCall, AccountId, + // Grandpa + BridgeMillauGrandpa, + // Messages + BridgeMillauMessages +} + +/// Dummy signed extension that does nothing. +/// +/// We're using it to have the same set of signed extensions on all parachains with bridge pallets +/// deployed (bridge hubs and rialto parachain). +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub struct DummyBridgeRefundMillauMessages; + +impl SignedExtension for DummyBridgeRefundMillauMessages { + const IDENTIFIER: &'static str = "DummyBridgeRefundMillauMessages"; + type AccountId = AccountId; + type Call = RuntimeCall; + type AdditionalSigned = (); + type Pre = (); + + fn additional_signed(&self) -> Result { + Ok(()) + } + + fn pre_dispatch( + self, + _who: &Self::AccountId, + _call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + Ok(()) + } +} + +/// The address format for describing accounts. +pub type Address = MultiAddress; +/// Block type as expected by this runtime. +pub type Block = generic::Block; +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, + BridgeRejectObsoleteHeadersAndMessages, + DummyBridgeRefundMillauMessages, +); +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = generic::CheckedExtrinsic; +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + } +} + +/// This runtime version. +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("template-parachain"), + impl_name: create_runtime_str!("template-parachain"), + authoring_version: 1, + spec_version: 1, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 0, +}; + +/// This determines the average expected block time that we are targeting. +/// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. +/// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked +/// up by `pallet_aura` to implement `fn slot_duration()`. +/// +/// Change this to adjust the block time. +pub const MILLISECS_PER_BLOCK: u64 = 12000; + +pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + +pub const EPOCH_DURATION_IN_BLOCKS: u32 = 10 * MINUTES; + +// Time is measured by number of blocks. +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; +pub const DAYS: BlockNumber = HOURS * 24; + +// Unit = the base number of indivisible units for balances +pub const UNIT: Balance = 1_000_000_000_000; +pub const MILLIUNIT: Balance = 1_000_000_000; +pub const MICROUNIT: Balance = 1_000_000; + +// 1 in 4 blocks (on average, not counting collisions) will be primary babe blocks. +pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4); + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } +} + +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub const Version: RuntimeVersion = VERSION; + pub const SS58Prefix: u8 = 48; +} + +// Configure FRAME pallets to include in runtime. + +impl frame_system::Config for Runtime { + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type RuntimeCall = RuntimeCall; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = AccountIdLookup; + /// The index type for storing how many extrinsics an account has signed. + type Index = Index; + /// The index type for blocks. + type BlockNumber = BlockNumber; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = Hashing; + /// The header type. + type Header = generic::Header; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// Runtime version. + type Version = Version; + /// Converts a module to an index of this module in the runtime. + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + /// What to do if a new account is created. + type OnNewAccount = (); + /// What to do if an account is fully reaped from the system. + type OnKilledAccount = (); + /// The weight of database operations that the runtime can invoke. + type DbWeight = (); + /// The basic call filter to use in dispatchable. + type BaseCallFilter = Everything; + /// Weight information for the extrinsics of this pallet. + type SystemWeightInfo = (); + /// Block & extrinsics weights: base values and limits. + type BlockWeights = BlockWeights; + /// The maximum length of a block (in bytes). + type BlockLength = BlockLength; + /// This is used as an identifier of the chain. 42 is the generic substrate prefix. + type SS58Prefix = SS58Prefix; + /// The action to take on a Runtime Upgrade + type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const MinimumPeriod: u64 = SLOT_DURATION / 2; +} + +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the Unix epoch. + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +parameter_types! { + pub const ExistentialDeposit: u128 = MILLIUNIT; + pub const TransferFee: u128 = MILLIUNIT; + pub const CreationFee: u128 = MILLIUNIT; + pub const TransactionByteFee: u128 = MICROUNIT; + pub const OperationalFeeMultiplier: u8 = 5; +} + +impl pallet_balances::Config for Runtime { + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = pallet_balances::weights::SubstrateWeight; + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; +} + +impl pallet_transaction_payment::Config for Runtime { + type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; + type OperationalFeeMultiplier = OperationalFeeMultiplier; + type WeightToFee = IdentityFee; + type LengthToFee = IdentityFee; + type FeeMultiplierUpdate = (); + type RuntimeEvent = RuntimeEvent; +} + +impl pallet_sudo::Config for Runtime { + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnSystemEvent = (); + type SelfParaId = parachain_info::Pallet; + type OutboundXcmpMessageSource = XcmpQueue; + type DmpMessageHandler = DmpQueue; + type ReservedDmpWeight = ReservedDmpWeight; + type XcmpMessageHandler = XcmpQueue; + type ReservedXcmpWeight = ReservedXcmpWeight; + type CheckAssociatedRelayNumber = AnyRelayNumber; +} + +impl parachain_info::Config for Runtime {} + +impl cumulus_pallet_aura_ext::Config for Runtime {} + +parameter_types! { + pub const RelayLocation: MultiLocation = MultiLocation::parent(); + pub const RelayNetwork: NetworkId = CustomNetworkId::Rialto.as_network_id(); + pub RelayOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + pub UniversalLocation: InteriorMultiLocation = ThisNetwork::get().into(); + /// The Millau network ID. + pub const MillauNetwork: NetworkId = CustomNetworkId::Millau.as_network_id(); + /// The RialtoParachain network ID. + pub const ThisNetwork: NetworkId = CustomNetworkId::RialtoParachain.as_network_id(); +} + +/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, +); + +/// Means for transacting assets on this chain. +pub type LocalAssetTransactor = CurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We don't track any teleports. + (), +>; + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with XCM `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a + // transaction from the Root origin. + ParentAsSuperuser, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `Origin::Signed` origin of the same 32-byte value. + SignedAccountId32AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + XcmPassthrough, +); + +// TODO: until https://github.com/paritytech/parity-bridges-common/issues/1417 is fixed (in either way), +// the following constant must match the similar constant in the Millau runtime. + +parameter_types! { + /// The amount of weight an XCM operation takes. We don't care much about those values as we're on testnet. + pub const UnitWeightCost: Weight = Weight::from_parts(1_000_000, 1024); + // One UNIT buys 1 second of weight. + pub const WeightPrice: (MultiLocation, u128) = (MultiLocation::parent(), UNIT); + pub const MaxInstructions: u32 = 100; + pub const MaxAuthorities: u32 = 100_000; + pub MaxAssetsIntoHolding: u32 = 64; +} + +match_types! { + pub type ParentOrParentsUnitPlurality: impl Contains = { + MultiLocation { parents: 1, interior: Here } | + MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Unit, .. }) } + }; +} + +pub type Barrier = TakeWeightCredit; + +/// Dispatches received XCM messages from other chain. +pub type OnRialtoParachainBlobDispatcher = + xcm_builder::BridgeBlobDispatcher; + +/// XCM weigher type. +pub type XcmWeigher = FixedWeightBounds; + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = (); + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = NativeAsset; + type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of UNIT + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = XcmWeigher; + type Trader = UsingComponents, RelayLocation, AccountId, Balances, ()>; + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = millau_messages::ToMillauBlobExporter; + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; +} + +/// No local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = SignedToAccountId32; + +/// The XCM router. We are not sending messages to sibling/parent/child chains here. +pub type XcmRouter = (); + +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = None; +} + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = XcmWeigher; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = (); + type MaxLockers = frame_support::traits::ConstU32<8>; + type UniversalLocation = UniversalLocation; + type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; + type AdminOrigin = frame_system::EnsureRoot; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; + type ChannelInfo = ParachainSystem; + type VersionWrapper = (); + type ExecuteOverweightOrigin = EnsureRoot; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type WeightInfo = (); + type PriceForSiblingDelivery = (); +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; + type ExecuteOverweightOrigin = frame_system::EnsureRoot; +} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = MaxAuthorities; +} + +impl pallet_bridge_relayers::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Reward = Balance; + type PaymentProcedure = + bp_relayers::PayRewardFromAccount, AccountId>; + type WeightInfo = (); +} + +pub type MillauGrandpaInstance = (); +impl pallet_bridge_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type BridgedChain = bp_millau::Millau; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; + type HeadersToKeep = ConstU32<{ bp_millau::DAYS as u32 }>; + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; +} + +parameter_types! { + pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; + pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + pub const RootAccountForPayments: Option = None; + pub const BridgedChainId: bp_runtime::ChainId = bp_runtime::MILLAU_CHAIN_ID; + pub ActiveOutboundLanes: &'static [bp_messages::LaneId] = &[millau_messages::XCM_LANE]; +} + +/// Instance of the messages pallet used to relay messages to/from Millau chain. +pub type WithMillauMessagesInstance = (); + +impl pallet_bridge_messages::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + type ActiveOutboundLanes = ActiveOutboundLanes; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type MaximalOutboundPayloadSize = crate::millau_messages::ToMillauMaximalOutboundPayloadSize; + type OutboundPayload = crate::millau_messages::ToMillauMessagePayload; + + type InboundPayload = crate::millau_messages::FromMillauMessagePayload; + type InboundRelayer = bp_millau::AccountId; + type DeliveryPayments = (); + + type TargetHeaderChain = crate::millau_messages::MillauAsTargetHeaderChain; + type LaneMessageVerifier = crate::millau_messages::ToMillauMessageVerifier; + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + Runtime, + WithMillauMessagesInstance, + frame_support::traits::ConstU128<100_000>, + >; + + type SourceHeaderChain = crate::millau_messages::MillauAsSourceHeaderChain; + type MessageDispatch = crate::millau_messages::FromMillauMessageDispatch; + type BridgedChainId = BridgedChainId; +} + +// Create the runtime by composing the FRAME pallets that were previously configured. +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = generic::Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Sudo: pallet_sudo::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + + ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Storage, Inherent, Event} = 20, + ParachainInfo: parachain_info::{Pallet, Storage, Config} = 21, + + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event} = 30, + + Aura: pallet_aura::{Pallet, Config}, + AuraExt: cumulus_pallet_aura_ext::{Pallet, Config}, + + // XCM helpers. + XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event} = 50, + PolkadotXcm: pallet_xcm::{Pallet, Call, Event, Origin} = 51, + CumulusXcm: cumulus_pallet_xcm::{Pallet, Call, Event, Origin} = 52, + DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 53, + + // Millau bridge modules. + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, + BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event}, + BridgeMillauMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, + } +); + +impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> sp_std::vec::Vec { + Runtime::metadata_versions() + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic( + extrinsic: ::Extrinsic, + ) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_session::SessionKeys for Runtime { + fn decode_session_keys( + encoded: Vec, + ) -> Option, KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + } + + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + } + + fn authorities() -> Vec { + Aura::authorities().to_vec() + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { + fn query_info( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details( + uxt: ::Extrinsic, + len: u32, + ) -> pallet_transaction_payment::FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl bp_millau::MillauFinalityApi for Runtime { + fn best_finalized() -> Option> { + BridgeMillauGrandpa::best_finalized() + } + } + + impl bp_millau::ToMillauOutboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + begin: bp_messages::MessageNonce, + end: bp_messages::MessageNonce, + ) -> Vec { + bridge_runtime_common::messages_api::outbound_message_details::< + Runtime, + WithMillauMessagesInstance, + >(lane, begin, end) + } + } + + impl bp_millau::FromMillauInboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + ) -> Vec { + bridge_runtime_common::messages_api::inbound_message_details::< + Runtime, + WithMillauMessagesInstance, + >(lane, messages) + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata(_extra: bool) -> ( + Vec, + Vec, + ) { + todo!("TODO: fix or remove") + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey}; + + use frame_system_benchmarking::Pallet as SystemBench; + impl frame_system_benchmarking::Config for Runtime {} + + let whitelist: Vec = vec![ + // Block Number + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(), + // Total Issuance + hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(), + // Execution Phase + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(), + // Event Count + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(), + // System Events + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(), + ]; + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + + add_benchmark!(params, batches, frame_system, SystemBench::); + add_benchmark!(params, batches, pallet_balances, Balances); + add_benchmark!(params, batches, pallet_timestamp, Timestamp); + + Ok(batches) + } + } +} + +struct CheckInherents; + +impl cumulus_pallet_parachain_system::CheckInherents for CheckInherents { + fn check_inherents( + block: &Block, + relay_state_proof: &cumulus_pallet_parachain_system::RelayChainStateProof, + ) -> sp_inherents::CheckInherentsResult { + let relay_chain_slot = relay_state_proof + .read_slot() + .expect("Could not read the relay chain slot from the proof"); + + let inherent_data = + cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration( + relay_chain_slot, + sp_std::time::Duration::from_secs(6), + ) + .create_inherent_data() + .expect("Could not create the timestamp inherent data"); + + inherent_data.check_extrinsics(block) + } +} + +cumulus_pallet_parachain_system::register_validate_block!( + Runtime = Runtime, + BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, + CheckInherents = CheckInherents, +); + +#[cfg(test)] +mod tests { + use super::*; + use crate::millau_messages::{FromMillauMessageDispatch, XCM_LANE}; + use bp_messages::{ + target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, + LaneId, MessageKey, + }; + use bridge_runtime_common::{ + integrity::check_additional_signed, messages_xcm_extension::XcmBlobMessageDispatchResult, + }; + use codec::Encode; + use pallet_bridge_messages::OutboundLanes; + use sp_runtime::generic::Era; + use xcm_executor::XcmExecutor; + + fn new_test_ext() -> sp_io::TestExternalities { + sp_io::TestExternalities::new( + frame_system::GenesisConfig::default().build_storage::().unwrap(), + ) + } + + fn prepare_outbound_xcm_message(destination: NetworkId) -> Xcm { + vec![ExportMessage { + network: destination, + destination: destination.into(), + xcm: vec![Instruction::Trap(42)].into(), + }] + .into() + } + + #[test] + fn xcm_messages_to_millau_are_sent_using_bridge_exporter() { + new_test_ext().execute_with(|| { + // ensure that the there are no messages queued + assert_eq!( + OutboundLanes::::get(XCM_LANE) + .latest_generated_nonce, + 0, + ); + + // export message instruction "sends" message to Rialto + XcmExecutor::::execute_xcm_in_credit( + Here, + prepare_outbound_xcm_message(MillauNetwork::get()), + Default::default(), + Weight::MAX, + Weight::MAX, + ) + .ensure_complete() + .expect("runtime configuration must be correct"); + + // ensure that the message has been queued + assert_eq!( + OutboundLanes::::get(XCM_LANE) + .latest_generated_nonce, + 1, + ); + }) + } + + fn prepare_inbound_bridge_message() -> DispatchMessage> { + let xcm = xcm::VersionedXcm::::V3(vec![Instruction::Trap(42)].into()); + let location = + xcm::VersionedInteriorMultiLocation::V3(X1(GlobalConsensus(ThisNetwork::get()))); + // this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor + // or public fields, so just tuple + let bridge_message = (location, xcm).encode(); + DispatchMessage { + key: MessageKey { lane_id: LaneId([0, 0, 0, 0]), nonce: 1 }, + data: DispatchMessageData { payload: Ok(bridge_message) }, + } + } + + #[test] + fn xcm_messages_from_millau_are_dispatched() { + new_test_ext().execute_with(|| { + let incoming_message = prepare_inbound_bridge_message(); + + // we care only about handing message to the XCM dispatcher, so we don't care about its + // actual dispatch + let dispatch_result = + FromMillauMessageDispatch::dispatch(&AccountId::from([0u8; 32]), incoming_message); + assert!(matches!( + dispatch_result.dispatch_level_result, + XcmBlobMessageDispatchResult::NotDispatched(_), + )); + }); + } + + #[test] + fn ensure_signed_extension_definition_is_correct() { + let payload: SignedExtra = ( + frame_system::CheckNonZeroSender::new(), + frame_system::CheckSpecVersion::new(), + frame_system::CheckTxVersion::new(), + frame_system::CheckGenesis::new(), + frame_system::CheckEra::from(Era::Immortal), + frame_system::CheckNonce::from(10), + frame_system::CheckWeight::new(), + pallet_transaction_payment::ChargeTransactionPayment::from(10), + BridgeRejectObsoleteHeadersAndMessages, + DummyBridgeRefundMillauMessages, + ); + let indirect_payload = bp_rialto_parachain::SignedExtension::new( + ((), (), (), (), Era::Immortal, 10.into(), (), 10.into(), (), ()), + None, + ); + assert_eq!(payload.encode(), indirect_payload.encode()); + + check_additional_signed::(); + } +} diff --git a/bin/rialto-parachain/runtime/src/millau_messages.rs b/bin/rialto-parachain/runtime/src/millau_messages.rs new file mode 100644 index 00000000000..5d4a92b5091 --- /dev/null +++ b/bin/rialto-parachain/runtime/src/millau_messages.rs @@ -0,0 +1,138 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything required to serve Millau <-> RialtoParachain messages. + +// TODO: this is almost exact copy of `millau_messages.rs` from Rialto runtime. +// Should be extracted to a separate crate and reused here. + +use crate::{MillauGrandpaInstance, Runtime, RuntimeOrigin, WithMillauMessagesInstance}; + +use bp_messages::LaneId; +use bridge_runtime_common::{ + messages::{ + self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, + }, + messages_xcm_extension::{XcmBlobHauler, XcmBlobHaulerAdapter}, +}; +use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; +use xcm::latest::prelude::*; +use xcm_builder::HaulBlobExporter; + +/// Default lane that is used to send messages to Millau. +pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); +/// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge +/// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual +/// tests, confirming that we don't break encoding somewhere between. +pub const BASE_XCM_WEIGHT_TWICE: Weight = crate::UnitWeightCost::get().saturating_mul(2); + +parameter_types! { + /// Weight credit for our test messages. + /// + /// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge + /// (it is prepended with `UniversalOrigin` instruction). + pub const WeightCredit: Weight = BASE_XCM_WEIGHT_TWICE; +} + +/// Message payload for RialtoParachain -> Millau messages. +pub type ToMillauMessagePayload = messages::source::FromThisChainMessagePayload; + +/// Message verifier for RialtoParachain -> Millau messages. +pub type ToMillauMessageVerifier = + messages::source::FromThisChainMessageVerifier; + +/// Message payload for Millau -> RialtoParachain messages. +pub type FromMillauMessagePayload = messages::target::FromBridgedChainMessagePayload; + +/// Call-dispatch based message dispatch for Millau -> RialtoParachain messages. +pub type FromMillauMessageDispatch = + bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatch< + bp_rialto_parachain::RialtoParachain, + bp_millau::Millau, + crate::OnRialtoParachainBlobDispatcher, + (), + >; + +/// Messages proof for Millau -> RialtoParachain messages. +pub type FromMillauMessagesProof = messages::target::FromBridgedChainMessagesProof; + +/// Messages delivery proof for RialtoParachain -> Millau messages. +pub type ToMillauMessagesDeliveryProof = + messages::source::FromBridgedChainMessagesDeliveryProof; + +/// Maximal outbound payload size of Rialto -> Millau messages. +pub type ToMillauMaximalOutboundPayloadSize = + messages::source::FromThisChainMaximalOutboundPayloadSize; + +/// Millau <-> RialtoParachain message bridge. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct WithMillauMessageBridge; + +impl MessageBridge for WithMillauMessageBridge { + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = + bp_rialto_parachain::WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME; + + type ThisChain = RialtoParachain; + type BridgedChain = Millau; + type BridgedHeaderChain = + pallet_bridge_grandpa::GrandpaChainHeaders; +} + +/// RialtoParachain chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct RialtoParachain; + +impl messages::UnderlyingChainProvider for RialtoParachain { + type Chain = bp_rialto_parachain::RialtoParachain; +} + +impl messages::ThisChainWithMessages for RialtoParachain { + type RuntimeOrigin = RuntimeOrigin; +} + +/// Millau chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Millau; +/// Millau as source header chain. +pub type MillauAsSourceHeaderChain = SourceHeaderChainAdapter; +/// Millau as target header chain. +pub type MillauAsTargetHeaderChain = TargetHeaderChainAdapter; + +impl messages::UnderlyingChainProvider for Millau { + type Chain = bp_millau::Millau; +} + +impl messages::BridgedChainWithMessages for Millau {} + +/// Export XCM messages to be relayed to Millau. +pub type ToMillauBlobExporter = + HaulBlobExporter, crate::MillauNetwork, ()>; + +/// To-Millau XCM hauler. +pub struct ToMillauXcmBlobHauler; + +impl XcmBlobHauler for ToMillauXcmBlobHauler { + type MessageSender = pallet_bridge_messages::Pallet; + type MessageSenderOrigin = RuntimeOrigin; + + fn message_sender_origin() -> RuntimeOrigin { + pallet_xcm::Origin::from(MultiLocation::new(1, crate::UniversalLocation::get())).into() + } + + fn xcm_lane() -> LaneId { + XCM_LANE + } +} diff --git a/bin/rialto/node/Cargo.toml b/bin/rialto/node/Cargo.toml new file mode 100644 index 00000000000..23c7d3315e7 --- /dev/null +++ b/bin/rialto/node/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "rialto-bridge-node" +description = "Substrate node compatible with Rialto runtime" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +build = "build.rs" +repository = "https://github.com/paritytech/parity-bridges-common/" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +clap = { version = "4.2.2", features = ["derive"] } +serde_json = "1.0.96" + +# Bridge dependencies + +rialto-runtime = { path = "../runtime" } + +# Substrate Dependencies + +sp-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +node-inspect = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-cli = { git = "https://github.com/paritytech/substrate", branch = "master"} +sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-service = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } + +# Polkadot Dependencies +polkadot-node-core-pvf = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false, features = [ "full-node", "polkadot-native" ] } + +[build-dependencies] +substrate-build-script-utils = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-benchmarking-cli = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = [] +runtime-benchmarks = [ + "polkadot-service/runtime-benchmarks", + "rialto-runtime/runtime-benchmarks", +] diff --git a/bin/rialto/node/build.rs b/bin/rialto/node/build.rs new file mode 100644 index 00000000000..d9b50049e26 --- /dev/null +++ b/bin/rialto/node/build.rs @@ -0,0 +1,23 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; + +fn main() { + generate_cargo_keys(); + + rerun_if_git_head_changed(); +} diff --git a/bin/rialto/node/src/chain_spec.rs b/bin/rialto/node/src/chain_spec.rs new file mode 100644 index 00000000000..ceb82d45b38 --- /dev/null +++ b/bin/rialto/node/src/chain_spec.rs @@ -0,0 +1,289 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use frame_support::weights::Weight; +use polkadot_primitives::v4::{AssignmentId, ValidatorId}; +use rialto_runtime::{ + AccountId, BabeConfig, BalancesConfig, BeefyConfig, BridgeMillauMessagesConfig, + ConfigurationConfig, GenesisConfig, GrandpaConfig, SessionConfig, SessionKeys, Signature, + SudoConfig, SystemConfig, WASM_BINARY, +}; +use serde_json::json; +use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; +use sp_consensus_babe::AuthorityId as BabeId; +use sp_consensus_beefy::crypto::AuthorityId as BeefyId; +use sp_consensus_grandpa::AuthorityId as GrandpaId; +use sp_core::{sr25519, Pair, Public}; +use sp_runtime::traits::{IdentifyAccount, Verify}; + +/// "Names" of the authorities accounts at local testnet. +const LOCAL_AUTHORITIES_ACCOUNTS: [&str; 5] = ["Alice", "Bob", "Charlie", "Dave", "Eve"]; +/// "Names" of the authorities accounts at development testnet. +const DEV_AUTHORITIES_ACCOUNTS: [&str; 1] = [LOCAL_AUTHORITIES_ACCOUNTS[0]]; +/// "Names" of all possible authorities accounts. +const ALL_AUTHORITIES_ACCOUNTS: [&str; 5] = LOCAL_AUTHORITIES_ACCOUNTS; +/// "Name" of the `sudo` account. +const SUDO_ACCOUNT: &str = "Sudo"; +/// "Name" of the account, which owns the with-Millau messages pallet. +const MILLAU_MESSAGES_PALLET_OWNER: &str = "Millau.MessagesOwner"; + +/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. +pub type ChainSpec = + sc_service::GenericChainSpec; + +/// The chain specification option. This is expected to come in from the CLI and +/// is little more than one of a number of alternatives which can easily be converted +/// from a string (`--chain=...`) into a `ChainSpec`. +#[derive(Clone, Debug)] +pub enum Alternative { + /// Whatever the current runtime is, with just Alice as an auth. + Development, + /// Whatever the current runtime is, with simple Alice/Bob/Charlie/Dave/Eve auths. + LocalTestnet, +} + +/// Helper function to generate a crypto pair from seed +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{seed}"), None) + .expect("static values are valid; qed") + .public() +} + +type AccountPublic = ::Signer; + +/// Helper function to generate an account ID from seed +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// Helper function to generate authority keys. +pub fn get_authority_keys_from_seed( + s: &str, +) -> (AccountId, BabeId, BeefyId, GrandpaId, ValidatorId, AssignmentId, AuthorityDiscoveryId) { + ( + get_account_id_from_seed::(s), + get_from_seed::(s), + get_from_seed::(s), + get_from_seed::(s), + get_from_seed::(s), + get_from_seed::(s), + get_from_seed::(s), + ) +} + +impl Alternative { + /// Get an actual chain config from one of the alternatives. + pub(crate) fn load(self) -> ChainSpec { + let properties = Some( + json!({ + "tokenDecimals": 9, + "tokenSymbol": "RLT" + }) + .as_object() + .expect("Map given; qed") + .clone(), + ); + match self { + Alternative::Development => ChainSpec::from_genesis( + "Rialto Development", + "rialto_dev", + sc_service::ChainType::Development, + || { + testnet_genesis( + DEV_AUTHORITIES_ACCOUNTS + .into_iter() + .map(get_authority_keys_from_seed) + .collect(), + get_account_id_from_seed::(SUDO_ACCOUNT), + endowed_accounts(), + true, + ) + }, + vec![], + None, + None, + None, + properties, + Default::default(), + ), + Alternative::LocalTestnet => ChainSpec::from_genesis( + "Rialto Local", + "rialto_local", + sc_service::ChainType::Local, + || { + testnet_genesis( + LOCAL_AUTHORITIES_ACCOUNTS + .into_iter() + .map(get_authority_keys_from_seed) + .collect(), + get_account_id_from_seed::(SUDO_ACCOUNT), + endowed_accounts(), + true, + ) + }, + vec![], + None, + None, + None, + properties, + Default::default(), + ), + } + } +} + +/// We're using the same set of endowed accounts on all Millau chains (dev/local) to make +/// sure that all accounts, required for bridge to be functional (e.g. relayers fund account, +/// accounts used by relayers in our test deployments, accounts used for demonstration +/// purposes), are all available on these chains. +fn endowed_accounts() -> Vec { + let all_authorities = ALL_AUTHORITIES_ACCOUNTS.iter().flat_map(|x| { + [ + get_account_id_from_seed::(x), + get_account_id_from_seed::(&format!("{x}//stash")), + ] + }); + vec![ + // Sudo account + get_account_id_from_seed::(SUDO_ACCOUNT), + // Regular (unused) accounts + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Ferdie//stash"), + // Accounts, used by Rialto<>Millau bridge + get_account_id_from_seed::(MILLAU_MESSAGES_PALLET_OWNER), + get_account_id_from_seed::("Millau.HeadersAndMessagesRelay"), + get_account_id_from_seed::("Millau.OutboundMessagesRelay.Lane00000001"), + get_account_id_from_seed::("Millau.InboundMessagesRelay.Lane00000001"), + get_account_id_from_seed::("Millau.MessagesSender"), + ] + .into_iter() + .chain(all_authorities) + .collect() +} + +fn session_keys( + babe: BabeId, + beefy: BeefyId, + grandpa: GrandpaId, + para_validator: ValidatorId, + para_assignment: AssignmentId, + authority_discovery: AuthorityDiscoveryId, +) -> SessionKeys { + SessionKeys { babe, beefy, grandpa, para_validator, para_assignment, authority_discovery } +} + +fn testnet_genesis( + initial_authorities: Vec<( + AccountId, + BabeId, + BeefyId, + GrandpaId, + ValidatorId, + AssignmentId, + AuthorityDiscoveryId, + )>, + root_key: AccountId, + endowed_accounts: Vec, + _enable_println: bool, +) -> GenesisConfig { + GenesisConfig { + system: SystemConfig { + code: WASM_BINARY.expect("Rialto development WASM not available").to_vec(), + }, + balances: BalancesConfig { + balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 50)).collect(), + }, + babe: BabeConfig { + authorities: Vec::new(), + epoch_config: Some(rialto_runtime::BABE_GENESIS_EPOCH_CONFIG), + }, + beefy: BeefyConfig::default(), + grandpa: GrandpaConfig { authorities: Vec::new() }, + sudo: SudoConfig { key: Some(root_key) }, + session: SessionConfig { + keys: initial_authorities + .iter() + .map(|x| { + ( + x.0.clone(), + x.0.clone(), + session_keys( + x.1.clone(), + x.2.clone(), + x.3.clone(), + x.4.clone(), + x.5.clone(), + x.6.clone(), + ), + ) + }) + .collect::>(), + }, + authority_discovery: Default::default(), + hrmp: Default::default(), + // this configuration is exact copy of configuration from Polkadot repo + // (see /node/service/src/chain_spec.rs:default_parachains_host_configuration) + configuration: ConfigurationConfig { + config: polkadot_runtime_parachains::configuration::HostConfiguration { + validation_upgrade_cooldown: 2u32, + validation_upgrade_delay: 2, + code_retention_period: 1200, + max_code_size: polkadot_primitives::v4::MAX_CODE_SIZE, + max_pov_size: polkadot_primitives::v4::MAX_POV_SIZE, + max_head_data_size: 32 * 1024, + group_rotation_frequency: 20, + chain_availability_period: 4, + thread_availability_period: 4, + max_upward_queue_count: 8, + max_upward_queue_size: 1024 * 1024, + max_downward_message_size: 1024 * 1024, + ump_service_total_weight: Weight::from_parts( + 100_000_000_000, + polkadot_primitives::v4::MAX_POV_SIZE as u64, + ), + max_upward_message_size: 50 * 1024, + max_upward_message_num_per_candidate: 5, + hrmp_sender_deposit: 0, + hrmp_recipient_deposit: 0, + hrmp_channel_max_capacity: 8, + hrmp_channel_max_total_size: 8 * 1024, + hrmp_max_parachain_inbound_channels: 4, + hrmp_max_parathread_inbound_channels: 4, + hrmp_channel_max_message_size: 1024 * 1024, + hrmp_max_parachain_outbound_channels: 4, + hrmp_max_parathread_outbound_channels: 4, + hrmp_max_message_num_per_candidate: 5, + dispute_period: 6, + no_show_slots: 2, + n_delay_tranches: 25, + needed_approvals: 2, + relay_vrf_modulo_samples: 2, + zeroth_delay_tranche_width: 0, + minimum_validation_upgrade_delay: 5, + ..Default::default() + }, + }, + paras: Default::default(), + bridge_millau_messages: BridgeMillauMessagesConfig { + owner: Some(get_account_id_from_seed::(MILLAU_MESSAGES_PALLET_OWNER)), + ..Default::default() + }, + xcm_pallet: Default::default(), + } +} diff --git a/bin/rialto/node/src/cli.rs b/bin/rialto/node/src/cli.rs new file mode 100644 index 00000000000..98323c9d9ca --- /dev/null +++ b/bin/rialto/node/src/cli.rs @@ -0,0 +1,88 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use clap::Parser; +use sc_cli::RunCmd; + +#[derive(Debug, Parser)] +pub struct Cli { + #[structopt(subcommand)] + pub subcommand: Option, + + #[structopt(flatten)] + pub run: RunCmd, +} + +/// Possible subcommands of the main binary. +#[derive(Debug, Parser)] +#[allow(clippy::large_enum_variant)] +pub enum Subcommand { + /// Key management CLI utilities + #[clap(subcommand)] + Key(sc_cli::KeySubcommand), + + /// Verify a signature for a message, provided on `STDIN`, with a given (public or secret) key. + Verify(sc_cli::VerifyCmd), + + /// Generate a seed that provides a vanity address. + Vanity(sc_cli::VanityCmd), + + /// Sign a message, with a given (secret) key. + Sign(sc_cli::SignCmd), + + /// Build a chain specification. + BuildSpec(sc_cli::BuildSpecCmd), + + /// Validate blocks. + CheckBlock(sc_cli::CheckBlockCmd), + + /// Export blocks. + ExportBlocks(sc_cli::ExportBlocksCmd), + + /// Export the state of a given block into a chain spec. + ExportState(sc_cli::ExportStateCmd), + + /// Import blocks. + ImportBlocks(sc_cli::ImportBlocksCmd), + + /// Remove the whole chain. + PurgeChain(sc_cli::PurgeChainCmd), + + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), + + /// Inspect blocks or extrinsics. + Inspect(node_inspect::cli::InspectCmd), + + /// Benchmark runtime pallets. + #[clap(subcommand)] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), + + /// FOR INTERNAL USE: analog of the "prepare-worker" command of the polkadot binary. + #[clap(name = "prepare-worker", hide = true)] + PvfPrepareWorker(ValidationWorkerCommand), + + /// FOR INTERNAL USE: analog of the "execute-worker" command of the polkadot binary. + #[clap(name = "execute-worker", hide = true)] + PvfExecuteWorker(ValidationWorkerCommand), +} + +/// Validation worker command. +#[derive(Debug, Parser)] +pub struct ValidationWorkerCommand { + /// The path to the validation host's socket. + pub socket_path: String, +} diff --git a/bin/rialto/node/src/command.rs b/bin/rialto/node/src/command.rs new file mode 100644 index 00000000000..0bff2e523b4 --- /dev/null +++ b/bin/rialto/node/src/command.rs @@ -0,0 +1,222 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::cli::{Cli, Subcommand}; +use frame_benchmarking_cli::BenchmarkCmd; +use rialto_runtime::{Block, RuntimeApi}; +use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli}; + +impl SubstrateCli for Cli { + fn impl_name() -> String { + "Rialto Bridge Node".into() + } + + fn impl_version() -> String { + env!("CARGO_PKG_VERSION").into() + } + + fn description() -> String { + "Rialto Bridge Node".into() + } + + fn author() -> String { + "Parity Technologies".into() + } + + fn support_url() -> String { + "https://github.com/paritytech/parity-bridges-common/".into() + } + + fn copyright_start_year() -> i32 { + 2019 + } + + fn executable_name() -> String { + "rialto-bridge-node".into() + } + + fn native_runtime_version(_: &Box) -> &'static RuntimeVersion { + &rialto_runtime::VERSION + } + + fn load_spec(&self, id: &str) -> Result, String> { + Ok(Box::new( + match id { + "" | "dev" => crate::chain_spec::Alternative::Development, + "local" => crate::chain_spec::Alternative::LocalTestnet, + _ => return Err(format!("Unsupported chain specification: {id}")), + } + .load(), + )) + } +} + +// Rialto native executor instance. +pub struct ExecutorDispatch; + +impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { + type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; + + fn dispatch(method: &str, data: &[u8]) -> Option> { + rialto_runtime::api::dispatch(method, data) + } + + fn native_version() -> sc_executor::NativeVersion { + rialto_runtime::native_version() + } +} + +/// Parse and run command line arguments +pub fn run() -> sc_cli::Result<()> { + let cli = Cli::from_args(); + sp_core::crypto::set_default_ss58_version(sp_core::crypto::Ss58AddressFormat::custom( + rialto_runtime::SS58Prefix::get() as u16, + )); + + match &cli.subcommand { + Some(Subcommand::Benchmark(cmd)) => { + let runner = cli.create_runner(cmd)?; + match cmd { + BenchmarkCmd::Pallet(cmd) => + if cfg!(feature = "runtime-benchmarks") { + runner.sync_run(|config| cmd.run::(config)) + } else { + println!( + "Benchmarking wasn't enabled when building the node. \ + You can enable it with `--features runtime-benchmarks`." + ); + Ok(()) + }, + _ => Err("Unsupported benchmarking subcommand".into()), + } + }, + Some(Subcommand::Key(cmd)) => cmd.run(&cli), + Some(Subcommand::Sign(cmd)) => cmd.run(), + Some(Subcommand::Verify(cmd)) => cmd.run(), + Some(Subcommand::Vanity(cmd)) => cmd.run(), + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) + }, + Some(Subcommand::CheckBlock(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, _, import_queue, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; + Ok((cmd.run(client, import_queue), task_manager)) + }) + }, + Some(Subcommand::ExportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, _, _, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; + Ok((cmd.run(client, config.database), task_manager)) + }) + }, + Some(Subcommand::ExportState(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, _, _, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; + Ok((cmd.run(client, config.chain_spec), task_manager)) + }) + }, + Some(Subcommand::ImportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, _, import_queue, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; + Ok((cmd.run(client, import_queue), task_manager)) + }) + }, + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.database)) + }, + Some(Subcommand::Revert(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|mut config| { + let (client, backend, _, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; + Ok((cmd.run(client, backend, None), task_manager)) + }) + }, + Some(Subcommand::Inspect(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run::(config)) + }, + Some(Subcommand::PvfPrepareWorker(cmd)) => { + let mut builder = sc_cli::LoggerBuilder::new(""); + builder.with_colors(false); + let _ = builder.init(); + + polkadot_node_core_pvf::prepare_worker_entrypoint(&cmd.socket_path, None); + Ok(()) + }, + Some(crate::cli::Subcommand::PvfExecuteWorker(cmd)) => { + let mut builder = sc_cli::LoggerBuilder::new(""); + builder.with_colors(false); + let _ = builder.init(); + + polkadot_node_core_pvf::execute_worker_entrypoint(&cmd.socket_path, None); + Ok(()) + }, + None => { + let runner = cli.create_runner(&cli.run)?; + + // some parameters that are used by polkadot nodes, but that are not used by our binary + // let jaeger_agent = None; + // let grandpa_pause = None; + // let no_beefy = true; + // let telemetry_worker_handler = None; + // let is_collator = crate::service::IsCollator::No; + let overseer_gen = polkadot_service::overseer::RealOverseerGen; + runner.run_node_until_exit(|config| async move { + let is_collator = polkadot_service::IsCollator::No; + let grandpa_pause = None; + let enable_beefy = true; + let jaeger_agent = None; + let telemetry_worker_handle = None; + let program_path = None; + let overseer_enable_anyways = false; + + polkadot_service::new_full::( + config, + is_collator, + grandpa_pause, + enable_beefy, + jaeger_agent, + telemetry_worker_handle, + program_path, + overseer_enable_anyways, + overseer_gen, + None, + None, + None, + ) + .map(|full| full.task_manager) + .map_err(service_error) + }) + }, + } +} + +// We don't want to change 'service.rs' too much to ease future updates => it'll keep using +// its own error enum like original polkadot service does. +fn service_error(err: polkadot_service::Error) -> sc_cli::Error { + sc_cli::Error::Application(Box::new(err)) +} diff --git a/bin/rialto/node/src/main.rs b/bin/rialto/node/src/main.rs new file mode 100644 index 00000000000..6dea84a309b --- /dev/null +++ b/bin/rialto/node/src/main.rs @@ -0,0 +1,28 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rialto bridge node. + +#![warn(missing_docs)] + +mod chain_spec; +mod cli; +mod command; + +/// Run the Rialto Node +fn main() -> sc_cli::Result<()> { + command::run() +} diff --git a/bin/rialto/runtime/Cargo.toml b/bin/rialto/runtime/Cargo.toml new file mode 100644 index 00000000000..2b68e72d6f8 --- /dev/null +++ b/bin/rialto/runtime/Cargo.toml @@ -0,0 +1,142 @@ +[package] +name = "rialto-runtime" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +repository = "https://github.com/paritytech/parity-bridges-common/" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } + +# Bridge dependencies + +bp-messages = { path = "../../../primitives/messages", default-features = false } +bp-millau = { path = "../../../primitives/chain-millau", default-features = false } +bp-relayers = { path = "../../../primitives/relayers", default-features = false } +bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false } +bp-runtime = { path = "../../../primitives/runtime", default-features = false } +bridge-runtime-common = { path = "../../runtime-common", default-features = false } +pallet-bridge-beefy = { path = "../../../modules/beefy", default-features = false } +pallet-bridge-grandpa = { path = "../../../modules/grandpa", default-features = false } +pallet-bridge-messages = { path = "../../../modules/messages", default-features = false } +pallet-bridge-relayers = { path = "../../../modules/relayers", default-features = false } +pallet-shift-session-manager = { path = "../../../modules/shift-session-manager", default-features = false } + +# Substrate Dependencies + +sp-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-executive = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-babe = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, features = ["historical"]} +pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-authority-discovery = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-block-builder = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-babe = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-inherents = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-offchain = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +# Polkadot (parachain) Dependencies +pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + +[dev-dependencies] +bridge-runtime-common = { path = "../../runtime-common", features = ["integrity-test"] } +env_logger = "0.10" +static_assertions = "1.1" + +[build-dependencies] +substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "sp-consensus-beefy/std", + "bp-messages/std", + "bp-millau/std", + "bp-relayers/std", + "bp-rialto/std", + "bp-runtime/std", + "bridge-runtime-common/std", + "codec/std", + "frame-benchmarking/std", + "frame-executive/std", + "frame-support/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "pallet-authority-discovery/std", + "pallet-babe/std", + "pallet-balances/std", + "pallet-beefy/std", + "pallet-beefy-mmr/std", + "pallet-bridge-beefy/std", + "pallet-bridge-grandpa/std", + "pallet-bridge-messages/std", + "pallet-bridge-relayers/std", + "pallet-grandpa/std", + "pallet-mmr/std", + "pallet-xcm/std", + "pallet-session/std", + "pallet-shift-session-manager/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "polkadot-primitives/std", + "polkadot-runtime-common/std", + "polkadot-runtime-parachains/std", + "scale-info/std", + "sp-api/std", + "sp-authority-discovery/std", + "sp-block-builder/std", + "sp-consensus-babe/std", + "sp-core/std", + "sp-inherents/std", + "sp-io/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-transaction-pool/std", + "sp-version/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", +] +runtime-benchmarks = [ + "bridge-runtime-common/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-bridge-messages/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", +] diff --git a/bin/rialto/runtime/build.rs b/bin/rialto/runtime/build.rs new file mode 100644 index 00000000000..cc865704327 --- /dev/null +++ b/bin/rialto/runtime/build.rs @@ -0,0 +1,25 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use substrate_wasm_builder::WasmBuilder; + +fn main() { + WasmBuilder::new() + .with_current_project() + .import_memory() + .export_heap_base() + .build() +} diff --git a/bin/rialto/runtime/src/lib.rs b/bin/rialto/runtime/src/lib.rs new file mode 100644 index 00000000000..aeb0a3a7c57 --- /dev/null +++ b/bin/rialto/runtime/src/lib.rs @@ -0,0 +1,986 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! The Rialto runtime. This can be compiled with `#[no_std]`, ready for Wasm. + +#![cfg_attr(not(feature = "std"), no_std)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] +// Runtime-generated enums +#![allow(clippy::large_enum_variant)] +// From construct_runtime macro +#![allow(clippy::from_over_into)] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +pub mod millau_messages; +pub mod parachains; +pub mod xcm_config; + +use bp_runtime::HeaderId; +use pallet_grandpa::{ + fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, +}; +use pallet_transaction_payment::{FeeDetails, Multiplier, RuntimeDispatchInfo}; +use sp_api::impl_runtime_apis; +use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; +use sp_consensus_beefy::{crypto::AuthorityId as BeefyId, mmr::MmrLeafVersion, ValidatorSet}; +use sp_core::OpaqueMetadata; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{AccountIdLookup, Block as BlockT, Keccak256, NumberFor, OpaqueKeys}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, FixedPointNumber, Perquintill, +}; +use sp_std::{collections::btree_map::BTreeMap, prelude::*}; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +// A few exports that help ease life for downstream crates. +pub use frame_support::{ + construct_runtime, + dispatch::DispatchClass, + parameter_types, + traits::{ + ConstU32, ConstU64, ConstU8, Currency, ExistenceRequirement, Imbalance, KeyOwnerProofSystem, + }, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, IdentityFee, RuntimeDbWeight, Weight}, + StorageValue, +}; + +pub use frame_system::Call as SystemCall; +pub use pallet_balances::Call as BalancesCall; +pub use pallet_bridge_beefy::Call as BridgeBeefyCall; +pub use pallet_bridge_grandpa::Call as BridgeGrandpaCall; +pub use pallet_bridge_messages::Call as MessagesCall; +pub use pallet_sudo::Call as SudoCall; +pub use pallet_timestamp::Call as TimestampCall; +pub use pallet_xcm::Call as XcmCall; + +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; +pub use sp_runtime::{Perbill, Permill}; + +/// An index to a block. +pub type BlockNumber = bp_rialto::BlockNumber; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = bp_rialto::Signature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = bp_rialto::AccountId; + +/// The type for looking up accounts. We don't expect more than 4 billion of them, but you +/// never know... +pub type AccountIndex = u32; + +/// Balance of an account. +pub type Balance = bp_rialto::Balance; + +/// Index of a transaction in the chain. +pub type Index = bp_rialto::Index; + +/// A hash of some data used by the chain. +pub type Hash = bp_rialto::Hash; + +/// Hashing algorithm used by the chain. +pub type Hashing = bp_rialto::Hasher; + +/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know +/// the specifics of the runtime. They can then be made to be agnostic over specific formats +/// of data like extrinsics, allowing for them to continue syncing the network through upgrades +/// to even the core data structures. +pub mod opaque { + use super::*; + + pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; + + /// Opaque block header type. + pub type Header = generic::Header; + /// Opaque block type. + pub type Block = generic::Block; + /// Opaque block identifier type. + pub type BlockId = generic::BlockId; +} + +impl_opaque_keys! { + pub struct SessionKeys { + pub babe: Babe, + pub grandpa: Grandpa, + pub beefy: Beefy, + pub para_validator: Initializer, + pub para_assignment: SessionInfo, + pub authority_discovery: AuthorityDiscovery, + } +} + +/// This runtime version. +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("rialto-runtime"), + impl_name: create_runtime_str!("rialto-runtime"), + authoring_version: 1, + spec_version: 1, + impl_version: 1, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 1, +}; + +/// The version information used to identify this runtime when compiled natively. +#[cfg(feature = "std")] +pub fn native_version() -> NativeVersion { + NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } +} + +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub const Version: RuntimeVersion = VERSION; + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { + read: 60_000_000, // ~0.06 ms = ~60 µs + write: 200_000_000, // ~0.2 ms = 200 µs + }; + pub const SS58Prefix: u8 = 48; +} + +impl frame_system::Config for Runtime { + /// The basic call filter to use in dispatchable. + type BaseCallFilter = frame_support::traits::Everything; + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type RuntimeCall = RuntimeCall; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = AccountIdLookup; + /// The index type for storing how many extrinsics an account has signed. + type Index = Index; + /// The index type for blocks. + type BlockNumber = BlockNumber; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = Hashing; + /// The header type. + type Header = generic::Header; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// Version of the runtime. + type Version = Version; + /// Provides information about the pallet setup in the runtime. + type PalletInfo = PalletInfo; + /// What to do if a new account is created. + type OnNewAccount = (); + /// What to do if an account is fully reaped from the system. + type OnKilledAccount = (); + /// The data to be stored in an account. + type AccountData = pallet_balances::AccountData; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + /// Weight information for the extrinsics of this pallet. + type SystemWeightInfo = (); + /// Block and extrinsics weights: base values and limits. + type BlockWeights = bp_rialto::BlockWeights; + /// The maximum length of a block (in bytes). + type BlockLength = bp_rialto::BlockLength; + /// The weight of database operations that the runtime can invoke. + type DbWeight = DbWeight; + /// The designated SS58 prefix of this chain. + type SS58Prefix = SS58Prefix; + /// The set code logic, just the default since we're not a parachain. + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +/// The BABE epoch configuration at genesis. +pub const BABE_GENESIS_EPOCH_CONFIG: sp_consensus_babe::BabeEpochConfiguration = + sp_consensus_babe::BabeEpochConfiguration { + c: bp_rialto::time_units::PRIMARY_PROBABILITY, + allowed_slots: sp_consensus_babe::AllowedSlots::PrimaryAndSecondaryVRFSlots, + }; + +parameter_types! { + pub const EpochDuration: u64 = bp_rialto::EPOCH_DURATION_IN_SLOTS as u64; + pub const ExpectedBlockTime: bp_rialto::Moment = bp_rialto::time_units::MILLISECS_PER_BLOCK; +} + +impl pallet_babe::Config for Runtime { + type EpochDuration = EpochDuration; + type ExpectedBlockTime = ExpectedBlockTime; + // session module is the trigger + type EpochChangeTrigger = pallet_babe::ExternalTrigger; + + type DisabledValidators = (); + + type WeightInfo = (); + + type MaxAuthorities = ConstU32<10>; + + // equivocation related configuration - we don't expect any equivocations in our testnets + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); +} + +impl pallet_beefy::Config for Runtime { + type BeefyId = BeefyId; + type MaxAuthorities = ConstU32<10>; + type MaxSetIdSessionEntries = ConstU64<0>; + type OnNewValidatorSet = MmrLeaf; + type WeightInfo = (); + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); +} + +impl pallet_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); + type MaxAuthorities = ConstU32<10>; + type MaxSetIdSessionEntries = ConstU64<0>; + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); +} + +impl pallet_mmr::Config for Runtime { + const INDEXING_PREFIX: &'static [u8] = b"mmr"; + type Hashing = Keccak256; + type Hash = ::Output; + type LeafData = pallet_beefy_mmr::Pallet; + type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest; + type WeightInfo = (); +} + +parameter_types! { + /// Version of the produced MMR leaf. + /// + /// The version consists of two parts; + /// - `major` (3 bits) + /// - `minor` (5 bits) + /// + /// `major` should be updated only if decoding the previous MMR Leaf format from the payload + /// is not possible (i.e. backward incompatible change). + /// `minor` should be updated if fields are added to the previous MMR Leaf, which given SCALE + /// encoding does not prevent old leafs from being decoded. + /// + /// Hence we expect `major` to be changed really rarely (think never). + /// See [`MmrLeafVersion`] type documentation for more details. + pub LeafVersion: MmrLeafVersion = MmrLeafVersion::new(0, 0); +} + +pub struct BeefyDummyDataProvider; + +impl sp_consensus_beefy::mmr::BeefyDataProvider<()> for BeefyDummyDataProvider { + fn extra_data() {} +} + +impl pallet_beefy_mmr::Config for Runtime { + type LeafVersion = LeafVersion; + type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; + type LeafExtra = (); + type BeefyDataProvider = BeefyDummyDataProvider; +} + +parameter_types! { + pub const MinimumPeriod: u64 = bp_rialto::SLOT_DURATION / 2; +} + +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the UNIX epoch. + type Moment = bp_rialto::Moment; + type OnTimestampSet = Babe; + type MinimumPeriod = MinimumPeriod; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); +} + +parameter_types! { + pub const ExistentialDeposit: bp_rialto::Balance = 500; +} + +impl pallet_balances::Config for Runtime { + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); + // For weight estimation, we assume that the most locks on an individual account will be 50. + // This number may need to be adjusted in the future if this assumption no longer holds true. + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; +} + +parameter_types! { + pub const TransactionBaseFee: Balance = 0; + pub const TransactionByteFee: Balance = 1; + // values for following parameters are copied from polkadot repo, but it is fine + // not to sync them - we're not going to make Rialto a full copy of one of Polkadot-like chains + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); + pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value(); +} + +impl pallet_transaction_payment::Config for Runtime { + type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = bp_rialto::WeightToFee; + type LengthToFee = bp_rialto::WeightToFee; + type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< + Runtime, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + MaximumMultiplier, + >; + type RuntimeEvent = RuntimeEvent; +} + +impl pallet_sudo::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; +} + +impl pallet_session::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ValidatorId = ::AccountId; + type ValidatorIdOf = (); + type ShouldEndSession = Babe; + type NextSessionRotation = Babe; + type SessionManager = pallet_shift_session_manager::Pallet; + type SessionHandler = ::KeyTypeIdProviders; + type Keys = SessionKeys; + // TODO: update me (https://github.com/paritytech/parity-bridges-common/issues/78) + type WeightInfo = (); +} + +impl pallet_authority_discovery::Config for Runtime { + type MaxAuthorities = ConstU32<10>; +} + +impl pallet_bridge_relayers::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Reward = Balance; + type PaymentProcedure = + bp_relayers::PayRewardFromAccount, AccountId>; + type WeightInfo = (); +} + +pub type MillauGrandpaInstance = (); +impl pallet_bridge_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type BridgedChain = bp_millau::Millau; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; + type HeadersToKeep = ConstU32<{ bp_millau::DAYS as u32 }>; + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; +} + +impl pallet_shift_session_manager::Config for Runtime {} + +parameter_types! { + pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; + pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + pub const RootAccountForPayments: Option = None; + pub const BridgedChainId: bp_runtime::ChainId = bp_runtime::MILLAU_CHAIN_ID; + pub ActiveOutboundLanes: &'static [bp_messages::LaneId] = &[millau_messages::XCM_LANE]; +} + +/// Instance of the messages pallet used to relay messages to/from Millau chain. +pub type WithMillauMessagesInstance = (); + +impl pallet_bridge_messages::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + type ActiveOutboundLanes = ActiveOutboundLanes; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type MaximalOutboundPayloadSize = crate::millau_messages::ToMillauMaximalOutboundPayloadSize; + type OutboundPayload = crate::millau_messages::ToMillauMessagePayload; + + type InboundPayload = crate::millau_messages::FromMillauMessagePayload; + type InboundRelayer = bp_millau::AccountId; + type DeliveryPayments = (); + + type TargetHeaderChain = crate::millau_messages::MillauAsTargetHeaderChain; + type LaneMessageVerifier = crate::millau_messages::ToMillauMessageVerifier; + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + Runtime, + WithMillauMessagesInstance, + frame_support::traits::ConstU128<100_000>, + >; + + type SourceHeaderChain = crate::millau_messages::MillauAsSourceHeaderChain; + type MessageDispatch = crate::millau_messages::FromMillauMessageDispatch; + type BridgedChainId = BridgedChainId; +} + +pub type MillauBeefyInstance = (); +impl pallet_bridge_beefy::Config for Runtime { + type MaxRequests = frame_support::traits::ConstU32<16>; + type CommitmentsToKeep = frame_support::traits::ConstU32<8>; + type BridgedChain = bp_millau::Millau; +} + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = opaque::Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Sudo: pallet_sudo::{Pallet, Call, Config, Storage, Event}, + + // Must be before session. + Babe: pallet_babe::{Pallet, Call, Storage, Config, ValidateUnsigned}, + + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + + // Consensus support. + AuthorityDiscovery: pallet_authority_discovery::{Pallet, Config}, + Session: pallet_session::{Pallet, Call, Storage, Event, Config}, + Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event}, + ShiftSessionManager: pallet_shift_session_manager::{Pallet}, + + // BEEFY Bridges support. + Beefy: pallet_beefy::{Pallet, Storage, Config}, + Mmr: pallet_mmr::{Pallet, Storage}, + MmrLeaf: pallet_beefy_mmr::{Pallet, Storage}, + + // Millau bridge modules. + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, + BridgeMillauGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event}, + BridgeMillauMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, + + // Millau bridge modules (BEEFY based). + BridgeMillauBeefy: pallet_bridge_beefy::{Pallet, Call, Storage}, + + // Parachain modules. + ParachainsOrigin: polkadot_runtime_parachains::origin::{Pallet, Origin}, + Configuration: polkadot_runtime_parachains::configuration::{Pallet, Call, Storage, Config}, + Shared: polkadot_runtime_parachains::shared::{Pallet, Call, Storage}, + Inclusion: polkadot_runtime_parachains::inclusion::{Pallet, Call, Storage, Event}, + ParasInherent: polkadot_runtime_parachains::paras_inherent::{Pallet, Call, Storage, Inherent}, + Scheduler: polkadot_runtime_parachains::scheduler::{Pallet, Storage}, + Paras: polkadot_runtime_parachains::paras::{Pallet, Call, Storage, Event, Config}, + Initializer: polkadot_runtime_parachains::initializer::{Pallet, Call, Storage}, + Dmp: polkadot_runtime_parachains::dmp::{Pallet, Call, Storage}, + Ump: polkadot_runtime_parachains::ump::{Pallet, Call, Storage, Event}, + Hrmp: polkadot_runtime_parachains::hrmp::{Pallet, Call, Storage, Event, Config}, + SessionInfo: polkadot_runtime_parachains::session_info::{Pallet, Storage}, + ParaSessionInfo: polkadot_runtime_parachains::session_info::{Pallet, Storage}, + ParasDisputes: polkadot_runtime_parachains::disputes::{Pallet, Call, Storage, Event}, + ParasSlashing: polkadot_runtime_parachains::disputes::slashing::{Pallet, Call, Storage, ValidateUnsigned}, + + // Parachain Onboarding Pallets + Registrar: polkadot_runtime_common::paras_registrar::{Pallet, Call, Storage, Event}, + Slots: polkadot_runtime_common::slots::{Pallet, Call, Storage, Event}, + ParasSudoWrapper: polkadot_runtime_common::paras_sudo_wrapper::{Pallet, Call}, + + // Pallet for sending XCM. + XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin, Config} = 99, + } +); + +/// The address format for describing accounts. +pub type Address = sp_runtime::MultiAddress; +/// Block header type as expected by this runtime. +pub type Header = generic::Header; +/// Block type as expected by this runtime. +pub type Block = generic::Block; +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; +/// BlockId type as expected by this runtime. +pub type BlockId = generic::BlockId; +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); +/// The payload being signed in transactions. +pub type SignedPayload = generic::SignedPayload; +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = generic::CheckedExtrinsic; +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +/// MMR helper types. +mod mmr { + use super::Runtime; + pub use pallet_mmr::primitives::*; + + pub type Leaf = <::LeafData as LeafDataProvider>::LeafData; + pub type Hash = ::Hash; + pub type Hashing = ::Hashing; +} + +impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block); + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> sp_std::vec::Vec { + Runtime::metadata_versions() + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult { + Executive::apply_extrinsic(extrinsic) + } + + fn finalize_block() -> ::Header { + Executive::finalize_block() + } + + fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { + data.create_extrinsics() + } + + fn check_inherents( + block: Block, + data: sp_inherents::InherentData, + ) -> sp_inherents::CheckInherentsResult { + data.check_extrinsics(&block) + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) + } + } + + impl sp_consensus_beefy::BeefyApi for Runtime { + fn beefy_genesis() -> Option { + Beefy::genesis_block() + } + + fn validator_set() -> Option> { + Beefy::validator_set() + } + + fn submit_report_equivocation_unsigned_extrinsic( + _equivocation_proof: sp_consensus_beefy::EquivocationProof< + NumberFor, + sp_consensus_beefy::crypto::AuthorityId, + sp_consensus_beefy::crypto::Signature + >, + _key_owner_proof: sp_consensus_beefy::OpaqueKeyOwnershipProof, + ) -> Option<()> { None } + + fn generate_key_ownership_proof( + _set_id: sp_consensus_beefy::ValidatorSetId, + _authority_id: sp_consensus_beefy::crypto::AuthorityId, + ) -> Option { None } + } + + impl pallet_mmr::primitives::MmrApi< + Block, + mmr::Hash, + BlockNumber, + > for Runtime { + fn mmr_root() -> Result { + Ok(Mmr::mmr_root()) + } + + fn mmr_leaf_count() -> Result { + Ok(Mmr::mmr_leaves()) + } + + fn generate_proof( + block_numbers: Vec, + best_known_block_number: Option, + ) -> Result<(Vec, mmr::Proof), mmr::Error> { + Mmr::generate_proof(block_numbers, best_known_block_number).map( + |(leaves, proof)| { + ( + leaves + .into_iter() + .map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf)) + .collect(), + proof, + ) + }, + ) + } + + fn verify_proof(leaves: Vec, proof: mmr::Proof) + -> Result<(), mmr::Error> + { + let leaves = leaves.into_iter().map(|leaf| + leaf.into_opaque_leaf() + .try_decode() + .ok_or(mmr::Error::Verify)).collect::, mmr::Error>>()?; + Mmr::verify_leaves(leaves, proof) + } + + fn verify_proof_stateless( + root: mmr::Hash, + leaves: Vec, + proof: mmr::Proof + ) -> Result<(), mmr::Error> { + let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect(); + pallet_mmr::verify_leaves_proof::(root, nodes, proof) + } + } + + impl bp_millau::MillauFinalityApi for Runtime { + fn best_finalized() -> Option> { + BridgeMillauGrandpa::best_finalized() + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + source: TransactionSource, + tx: ::Extrinsic, + block_hash: ::Hash, + ) -> TransactionValidity { + Executive::validate_transaction(source, tx, block_hash) + } + } + + impl sp_offchain::OffchainWorkerApi for Runtime { + fn offchain_worker(header: &::Header) { + Executive::offchain_worker(header) + } + } + + impl sp_consensus_babe::BabeApi for Runtime { + fn configuration() -> sp_consensus_babe::BabeConfiguration { + // The choice of `c` parameter (where `1 - c` represents the + // probability of a slot being empty), is done in accordance to the + // slot duration and expected target block time, for safely + // resisting network delays of maximum two seconds. + // + sp_consensus_babe::BabeConfiguration { + slot_duration: Babe::slot_duration(), + epoch_length: EpochDuration::get(), + c: BABE_GENESIS_EPOCH_CONFIG.c, + authorities: Babe::authorities().to_vec(), + randomness: Babe::randomness(), + allowed_slots: BABE_GENESIS_EPOCH_CONFIG.allowed_slots, + } + } + + fn current_epoch_start() -> sp_consensus_babe::Slot { + Babe::current_epoch_start() + } + + fn current_epoch() -> sp_consensus_babe::Epoch { + Babe::current_epoch() + } + + fn next_epoch() -> sp_consensus_babe::Epoch { + Babe::next_epoch() + } + + fn generate_key_ownership_proof( + _slot: sp_consensus_babe::Slot, + _authority_id: sp_consensus_babe::AuthorityId, + ) -> Option { + None + } + + fn submit_report_equivocation_unsigned_extrinsic( + equivocation_proof: sp_consensus_babe::EquivocationProof<::Header>, + key_owner_proof: sp_consensus_babe::OpaqueKeyOwnershipProof, + ) -> Option<()> { + let key_owner_proof = key_owner_proof.decode()?; + + Babe::submit_unsigned_equivocation_report( + equivocation_proof, + key_owner_proof, + ) + } + } + + impl polkadot_primitives::runtime_api::ParachainHost for Runtime { + fn validators() -> Vec { + polkadot_runtime_parachains::runtime_api_impl::v4::validators::() + } + + fn validator_groups() -> (Vec>, polkadot_primitives::v4::GroupRotationInfo) { + polkadot_runtime_parachains::runtime_api_impl::v4::validator_groups::() + } + + fn availability_cores() -> Vec> { + polkadot_runtime_parachains::runtime_api_impl::v4::availability_cores::() + } + + fn persisted_validation_data(para_id: polkadot_primitives::v4::Id, assumption: polkadot_primitives::v4::OccupiedCoreAssumption) + -> Option> { + polkadot_runtime_parachains::runtime_api_impl::v4::persisted_validation_data::(para_id, assumption) + } + + fn assumed_validation_data( + para_id: polkadot_primitives::v4::Id, + expected_persisted_validation_data_hash: Hash, + ) -> Option<(polkadot_primitives::v4::PersistedValidationData, polkadot_primitives::v4::ValidationCodeHash)> { + polkadot_runtime_parachains::runtime_api_impl::v4::assumed_validation_data::( + para_id, + expected_persisted_validation_data_hash, + ) + } + + fn check_validation_outputs( + para_id: polkadot_primitives::v4::Id, + outputs: polkadot_primitives::v4::CandidateCommitments, + ) -> bool { + polkadot_runtime_parachains::runtime_api_impl::v4::check_validation_outputs::(para_id, outputs) + } + + fn session_index_for_child() -> polkadot_primitives::v4::SessionIndex { + polkadot_runtime_parachains::runtime_api_impl::v4::session_index_for_child::() + } + + fn validation_code(para_id: polkadot_primitives::v4::Id, assumption: polkadot_primitives::v4::OccupiedCoreAssumption) + -> Option { + polkadot_runtime_parachains::runtime_api_impl::v4::validation_code::(para_id, assumption) + } + + fn candidate_pending_availability(para_id: polkadot_primitives::v4::Id) -> Option> { + polkadot_runtime_parachains::runtime_api_impl::v4::candidate_pending_availability::(para_id) + } + + fn candidate_events() -> Vec> { + polkadot_runtime_parachains::runtime_api_impl::v4::candidate_events::(|ev| { + match ev { + RuntimeEvent::Inclusion(ev) => { + Some(ev) + } + _ => None, + } + }) + } + + fn session_info(index: polkadot_primitives::v4::SessionIndex) -> Option { + polkadot_runtime_parachains::runtime_api_impl::v4::session_info::(index) + } + + fn dmq_contents(recipient: polkadot_primitives::v4::Id) -> Vec> { + polkadot_runtime_parachains::runtime_api_impl::v4::dmq_contents::(recipient) + } + + fn inbound_hrmp_channels_contents( + recipient: polkadot_primitives::v4::Id + ) -> BTreeMap>> { + polkadot_runtime_parachains::runtime_api_impl::v4::inbound_hrmp_channels_contents::(recipient) + } + + fn validation_code_by_hash(hash: polkadot_primitives::v4::ValidationCodeHash) -> Option { + polkadot_runtime_parachains::runtime_api_impl::v4::validation_code_by_hash::(hash) + } + + fn on_chain_votes() -> Option> { + polkadot_runtime_parachains::runtime_api_impl::v4::on_chain_votes::() + } + + fn submit_pvf_check_statement(stmt: polkadot_primitives::v4::PvfCheckStatement, signature: polkadot_primitives::v4::ValidatorSignature) { + polkadot_runtime_parachains::runtime_api_impl::v4::submit_pvf_check_statement::(stmt, signature) + } + + fn pvfs_require_precheck() -> Vec { + polkadot_runtime_parachains::runtime_api_impl::v4::pvfs_require_precheck::() + } + + fn validation_code_hash(para_id: polkadot_primitives::v4::Id, assumption: polkadot_primitives::v4::OccupiedCoreAssumption) + -> Option + { + polkadot_runtime_parachains::runtime_api_impl::v4::validation_code_hash::(para_id, assumption) + } + + fn disputes() -> Vec<(polkadot_primitives::v4::SessionIndex, polkadot_primitives::v4::CandidateHash, polkadot_primitives::v4::DisputeState)> { + polkadot_runtime_parachains::runtime_api_impl::v4::get_session_disputes::() + } + + fn session_executor_params(session_index: polkadot_primitives::v4::SessionIndex) -> Option { + polkadot_runtime_parachains::runtime_api_impl::v4::session_executor_params::(session_index) + } + } + + impl sp_authority_discovery::AuthorityDiscoveryApi for Runtime { + fn authorities() -> Vec { + polkadot_runtime_parachains::runtime_api_impl::v4::relevant_authority_ids::() + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi< + Block, + Balance, + > for Runtime { + fn query_info(uxt: ::Extrinsic, len: u32) -> RuntimeDispatchInfo { + TransactionPayment::query_info(uxt, len) + } + fn query_fee_details(uxt: ::Extrinsic, len: u32) -> FeeDetails { + TransactionPayment::query_fee_details(uxt, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + SessionKeys::generate(seed) + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, sp_core::crypto::KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl fg_primitives::GrandpaApi for Runtime { + fn current_set_id() -> fg_primitives::SetId { + Grandpa::current_set_id() + } + + fn grandpa_authorities() -> GrandpaAuthorityList { + Grandpa::grandpa_authorities() + } + + fn submit_report_equivocation_unsigned_extrinsic( + equivocation_proof: fg_primitives::EquivocationProof< + ::Hash, + NumberFor, + >, + key_owner_proof: fg_primitives::OpaqueKeyOwnershipProof, + ) -> Option<()> { + let key_owner_proof = key_owner_proof.decode()?; + + Grandpa::submit_unsigned_equivocation_report( + equivocation_proof, + key_owner_proof, + ) + } + + fn generate_key_ownership_proof( + _set_id: fg_primitives::SetId, + _authority_id: GrandpaId, + ) -> Option { + // NOTE: this is the only implementation possible since we've + // defined our key owner proof type as a bottom type (i.e. a type + // with no values). + None + } + } + + impl bp_millau::ToMillauOutboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + begin: bp_messages::MessageNonce, + end: bp_messages::MessageNonce, + ) -> Vec { + bridge_runtime_common::messages_api::outbound_message_details::< + Runtime, + WithMillauMessagesInstance, + >(lane, begin, end) + } + } + + impl bp_millau::FromMillauInboundLaneApi for Runtime { + fn message_details( + lane: bp_messages::LaneId, + messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, + ) -> Vec { + bridge_runtime_common::messages_api::inbound_message_details::< + Runtime, + WithMillauMessagesInstance, + >(lane, messages) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn call_size() { + const BRIDGES_PALLETS_MAX_CALL_SIZE: usize = 200; + assert!( + core::mem::size_of::>() <= + BRIDGES_PALLETS_MAX_CALL_SIZE + ); + assert!( + core::mem::size_of::>() <= + BRIDGES_PALLETS_MAX_CALL_SIZE + ); + // Largest inner Call is `pallet_session::Call` with a size of 224 bytes. This size is a + // result of large `SessionKeys` struct. + // Total size of Rialto runtime Call is 232. + const MAX_CALL_SIZE: usize = 232; + assert!(core::mem::size_of::() <= MAX_CALL_SIZE); + } +} diff --git a/bin/rialto/runtime/src/millau_messages.rs b/bin/rialto/runtime/src/millau_messages.rs new file mode 100644 index 00000000000..ab4b7dd521d --- /dev/null +++ b/bin/rialto/runtime/src/millau_messages.rs @@ -0,0 +1,198 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything required to serve Millau <-> Rialto messages. + +use crate::{MillauGrandpaInstance, Runtime, RuntimeOrigin, WithMillauMessagesInstance}; + +use bp_messages::LaneId; +use bridge_runtime_common::{ + messages::{ + self, source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, MessageBridge, + }, + messages_xcm_extension::{XcmBlobHauler, XcmBlobHaulerAdapter}, +}; +use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; +use xcm::latest::prelude::*; +use xcm_builder::HaulBlobExporter; + +/// Lane that is used for XCM messages exchange. +pub const XCM_LANE: LaneId = LaneId([0, 0, 0, 0]); +/// Weight of 2 XCM instructions is for simple `Trap(42)` program, coming through bridge +/// (it is prepended with `UniversalOrigin` instruction). It is used just for simplest manual +/// tests, confirming that we don't break encoding somewhere between. +pub const BASE_XCM_WEIGHT_TWICE: Weight = crate::xcm_config::BaseXcmWeight::get().saturating_mul(2); + +parameter_types! { + /// Weight credit for our test messages. + /// + /// 2 XCM instructions is for simple `Trap(42)` program, coming through bridge + /// (it is prepended with `UniversalOrigin` instruction). + pub const WeightCredit: Weight = BASE_XCM_WEIGHT_TWICE; +} + +/// Message payload for Rialto -> Millau messages. +pub type ToMillauMessagePayload = messages::source::FromThisChainMessagePayload; + +/// Message verifier for Rialto -> Millau messages. +pub type ToMillauMessageVerifier = + messages::source::FromThisChainMessageVerifier; + +/// Message payload for Millau -> Rialto messages. +pub type FromMillauMessagePayload = messages::target::FromBridgedChainMessagePayload; + +/// Call-dispatch based message dispatch for Millau -> Rialto messages. +pub type FromMillauMessageDispatch = + bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatch< + bp_rialto::Rialto, + bp_millau::Millau, + crate::xcm_config::OnRialtoBlobDispatcher, + (), + >; + +/// Messages proof for Millau -> Rialto messages. +pub type FromMillauMessagesProof = messages::target::FromBridgedChainMessagesProof; + +/// Messages delivery proof for Rialto -> Millau messages. +pub type ToMillauMessagesDeliveryProof = + messages::source::FromBridgedChainMessagesDeliveryProof; + +/// Maximal outbound payload size of Rialto -> Millau messages. +pub type ToMillauMaximalOutboundPayloadSize = + messages::source::FromThisChainMaximalOutboundPayloadSize; + +/// Millau <-> Rialto message bridge. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct WithMillauMessageBridge; + +impl MessageBridge for WithMillauMessageBridge { + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME; + + type ThisChain = Rialto; + type BridgedChain = Millau; + type BridgedHeaderChain = + pallet_bridge_grandpa::GrandpaChainHeaders; +} + +/// Rialto chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Rialto; + +impl messages::UnderlyingChainProvider for Rialto { + type Chain = bp_rialto::Rialto; +} + +impl messages::ThisChainWithMessages for Rialto { + type RuntimeOrigin = RuntimeOrigin; +} + +/// Millau chain from message lane point of view. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Millau; +/// Millau as source header chain. +pub type MillauAsSourceHeaderChain = SourceHeaderChainAdapter; +/// Millau as target header chain. +pub type MillauAsTargetHeaderChain = TargetHeaderChainAdapter; + +impl messages::UnderlyingChainProvider for Millau { + type Chain = bp_millau::Millau; +} + +impl messages::BridgedChainWithMessages for Millau {} + +/// Export XCM messages to be relayed to Millau. +pub type ToMillauBlobExporter = HaulBlobExporter< + XcmBlobHaulerAdapter, + crate::xcm_config::MillauNetwork, + (), +>; + +/// To-Millau XCM hauler. +pub struct ToMillauXcmBlobHauler; + +impl XcmBlobHauler for ToMillauXcmBlobHauler { + type MessageSender = pallet_bridge_messages::Pallet; + type MessageSenderOrigin = RuntimeOrigin; + + fn message_sender_origin() -> RuntimeOrigin { + pallet_xcm::Origin::from(MultiLocation::new(1, crate::xcm_config::UniversalLocation::get())) + .into() + } + + fn xcm_lane() -> LaneId { + XCM_LANE + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{MillauGrandpaInstance, Runtime, WithMillauMessagesInstance}; + use bridge_runtime_common::{ + assert_complete_bridge_types, + integrity::{ + assert_complete_bridge_constants, check_message_lane_weights, + AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants, + AssertCompleteBridgeConstants, + }, + }; + + #[test] + fn ensure_rialto_message_lane_weights_are_correct() { + check_message_lane_weights::( + bp_millau::EXTRA_STORAGE_PROOF_SIZE, + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + ); + } + + #[test] + fn ensure_bridge_integrity() { + assert_complete_bridge_types!( + runtime: Runtime, + with_bridged_chain_grandpa_instance: MillauGrandpaInstance, + with_bridged_chain_messages_instance: WithMillauMessagesInstance, + bridge: WithMillauMessageBridge, + this_chain: bp_rialto::Rialto, + bridged_chain: bp_millau::Millau, + ); + + assert_complete_bridge_constants::< + Runtime, + MillauGrandpaInstance, + WithMillauMessagesInstance, + WithMillauMessageBridge, + >(AssertCompleteBridgeConstants { + this_chain_constants: AssertChainConstants { + block_length: bp_rialto::BlockLength::get(), + block_weights: bp_rialto::BlockWeights::get(), + }, + messages_pallet_constants: AssertBridgeMessagesPalletConstants { + max_unrewarded_relayers_in_bridged_confirmation_tx: + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + max_unconfirmed_messages_in_bridged_confirmation_tx: + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + bridged_chain_id: bp_runtime::MILLAU_CHAIN_ID, + }, + pallet_names: AssertBridgePalletNames { + with_this_chain_messages_pallet_name: bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME, + with_bridged_chain_grandpa_pallet_name: bp_millau::WITH_MILLAU_GRANDPA_PALLET_NAME, + with_bridged_chain_messages_pallet_name: + bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME, + }, + }); + } +} diff --git a/bin/rialto/runtime/src/parachains.rs b/bin/rialto/runtime/src/parachains.rs new file mode 100644 index 00000000000..8ceb27140f0 --- /dev/null +++ b/bin/rialto/runtime/src/parachains.rs @@ -0,0 +1,165 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Parachains support in Rialto runtime. + +use crate::{ + AccountId, Babe, Balance, Balances, BlockNumber, Registrar, Runtime, RuntimeCall, RuntimeEvent, + RuntimeOrigin, ShiftSessionManager, Slots, UncheckedExtrinsic, +}; + +use frame_support::{parameter_types, traits::KeyOwnerProofSystem}; +use frame_system::EnsureRoot; +use polkadot_primitives::v4::{ValidatorId, ValidatorIndex}; +use polkadot_runtime_common::{paras_registrar, paras_sudo_wrapper, slots}; +use polkadot_runtime_parachains::{ + configuration as parachains_configuration, disputes as parachains_disputes, + disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, + inclusion as parachains_inclusion, initializer as parachains_initializer, + origin as parachains_origin, paras as parachains_paras, + paras_inherent as parachains_paras_inherent, scheduler as parachains_scheduler, + session_info as parachains_session_info, shared as parachains_shared, ump as parachains_ump, +}; +use sp_core::crypto::KeyTypeId; +use sp_runtime::transaction_validity::TransactionPriority; + +impl frame_system::offchain::SendTransactionTypes for Runtime +where + RuntimeCall: From, +{ + type Extrinsic = UncheckedExtrinsic; + type OverarchingCall = RuntimeCall; +} + +/// Special `RewardValidators` that does nothing ;) +pub struct RewardValidators; +impl polkadot_runtime_parachains::inclusion::RewardValidators for RewardValidators { + fn reward_backing(_: impl IntoIterator) {} + fn reward_bitfields(_: impl IntoIterator) {} +} + +// all required parachain modules from `polkadot-runtime-parachains` crate + +impl parachains_configuration::Config for Runtime { + type WeightInfo = parachains_configuration::TestWeightInfo; +} + +impl parachains_dmp::Config for Runtime {} + +impl parachains_hrmp::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type Currency = Balances; + type WeightInfo = parachains_hrmp::TestWeightInfo; +} + +impl parachains_inclusion::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RewardValidators = RewardValidators; + type DisputesHandler = (); +} + +impl parachains_initializer::Config for Runtime { + type Randomness = pallet_babe::RandomnessFromOneEpochAgo; + type ForceOrigin = EnsureRoot; + type WeightInfo = (); +} + +impl parachains_disputes::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RewardValidators = (); + type SlashingHandler = (); + type WeightInfo = parachains_disputes::TestWeightInfo; +} + +impl parachains_slashing::Config for Runtime { + type KeyOwnerProofSystem = (); + type KeyOwnerProof = + >::Proof; + type KeyOwnerIdentification = >::IdentificationTuple; + type HandleReports = (); + type WeightInfo = parachains_slashing::TestWeightInfo; + type BenchmarkingConfig = parachains_slashing::BenchConfig<200>; +} + +impl parachains_origin::Config for Runtime {} + +parameter_types! { + pub const ParasUnsignedPriority: TransactionPriority = TransactionPriority::max_value(); +} + +impl parachains_paras::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = parachains_paras::TestWeightInfo; + type UnsignedPriority = ParasUnsignedPriority; + type NextSessionRotation = Babe; +} + +impl parachains_paras_inherent::Config for Runtime { + type WeightInfo = parachains_paras_inherent::TestWeightInfo; +} + +impl parachains_scheduler::Config for Runtime {} + +impl parachains_session_info::Config for Runtime { + type ValidatorSet = ShiftSessionManager; +} + +impl parachains_shared::Config for Runtime {} + +impl parachains_ump::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UmpSink = (); + type FirstMessageFactorPercent = frame_support::traits::ConstU64<100>; + type ExecuteOverweightOrigin = EnsureRoot; + type WeightInfo = parachains_ump::TestWeightInfo; +} + +// required onboarding pallets. We're not going to use auctions or crowdloans, so they're missing + +parameter_types! { + pub const ParaDeposit: Balance = 0; + pub const DataDepositPerByte: Balance = 0; +} + +impl paras_registrar::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type Currency = Balances; + type OnSwap = Slots; + type ParaDeposit = ParaDeposit; + type DataDepositPerByte = DataDepositPerByte; + type WeightInfo = paras_registrar::TestWeightInfo; +} + +parameter_types! { + pub const LeasePeriod: BlockNumber = 10 * bp_rialto::MINUTES; +} + +impl slots::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type Registrar = Registrar; + type LeasePeriod = LeasePeriod; + type WeightInfo = slots::TestWeightInfo; + type LeaseOffset = (); + type ForceOrigin = EnsureRoot; +} + +impl paras_sudo_wrapper::Config for Runtime {} diff --git a/bin/rialto/runtime/src/xcm_config.rs b/bin/rialto/runtime/src/xcm_config.rs new file mode 100644 index 00000000000..9f6488b4c4d --- /dev/null +++ b/bin/rialto/runtime/src/xcm_config.rs @@ -0,0 +1,276 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! XCM configurations for the Rialto runtime. + +use super::{ + millau_messages::ToMillauBlobExporter, AccountId, AllPalletsWithSystem, Balances, Runtime, + RuntimeCall, RuntimeEvent, RuntimeOrigin, XcmPallet, +}; +use bp_rialto::WeightToFee; +use bridge_runtime_common::CustomNetworkId; +use frame_support::{ + parameter_types, + traits::{ConstU32, Everything, Nothing}, + weights::Weight, +}; +use frame_system::EnsureRoot; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, CurrencyAdapter as XcmCurrencyAdapter, IsConcrete, MintLocation, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + UsingComponents, +}; + +parameter_types! { + /// The location of the `MLAU` token, from the context of this chain. Since this token is native to this + /// chain, we make it synonymous with it and thus it is the `Here` location, which means "equivalent to + /// the context". + pub const TokenLocation: MultiLocation = Here.into_location(); + /// The Rialto network ID. + pub const ThisNetwork: NetworkId = CustomNetworkId::Rialto.as_network_id(); + /// The Millau network ID. + pub const MillauNetwork: NetworkId = CustomNetworkId::Millau.as_network_id(); + + /// Our XCM location ancestry - i.e. our location within the Consensus Universe. + /// + /// Since Polkadot is a top-level relay-chain with its own consensus, it's just our network ID. + pub UniversalLocation: InteriorMultiLocation = ThisNetwork::get().into(); + /// The check account, which holds any native assets that have been teleported out and not back in (yet). + pub CheckAccount: (AccountId, MintLocation) = (XcmPallet::check_account(), MintLocation::Local); +} + +/// The canonical means of converting a `MultiLocation` into an `AccountId`, used when we want to +/// determine the sovereign account controlled by a location. +pub type SovereignAccountOf = ( + // We can directly alias an `AccountId32` into a local account. + AccountId32Aliases, +); + +/// Our asset transactor. This is what allows us to interest with the runtime facilities from the +/// point of view of XCM-only concepts like `MultiLocation` and `MultiAsset`. +/// +/// Ours is only aware of the Balances pallet, which is mapped to `TokenLocation`. +pub type LocalAssetTransactor = XcmCurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // We can convert the MultiLocations with our converter above: + SovereignAccountOf, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We track our teleports in/out to keep total issuance correct. + CheckAccount, +>; + +/// The means that we convert the XCM message origin location into a local dispatch origin. +type LocalOriginConverter = ( + // A `Signed` origin of the sovereign account that the original location controls. + SovereignSignedViaLocation, + // The AccountId32 location type can be expressed natively as a `Signed` origin. + SignedAccountId32AsNative, +); + +parameter_types! { + /// The amount of weight an XCM operation takes. This is a safe overestimate. + pub const BaseXcmWeight: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); + /// Maximum number of instructions in a single XCM fragment. A sanity check against weight + /// calculations getting too crazy. + pub const MaxInstructions: u32 = 100; +} + +/// The XCM router. We are not sending messages to sibling/parent/child chains here. +pub type XcmRouter = (); + +/// The barriers one of which must be passed for an XCM message to be executed. +pub type Barrier = ( + // Weight that is paid for may be consumed. + TakeWeightCredit, +); + +/// Dispatches received XCM messages from other chain. +pub type OnRialtoBlobDispatcher = xcm_builder::BridgeBlobDispatcher< + crate::xcm_config::XcmRouter, + crate::xcm_config::UniversalLocation, +>; + +/// Incoming XCM weigher type. +pub type XcmWeigher = xcm_builder::FixedWeightBounds; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = (); + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = XcmWeigher; + // The weight trader piggybacks on the existing transaction-fee conversion logic. + type Trader = UsingComponents; + type ResponseHandler = XcmPallet; + type AssetTrap = XcmPallet; + type AssetLocker = (); + type AssetExchanger = (); + type AssetClaims = XcmPallet; + type SubscriptionService = XcmPallet; + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = ConstU32<64>; + type FeeManager = (); + type MessageExporter = ToMillauBlobExporter; + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; +} + +/// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior +/// location of this chain. +pub type LocalOriginToLocation = ( + // Usual Signed origin to be used in XCM as a corresponding AccountId32 + SignedToAccountId32, +); + +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = None; +} + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + // We don't allow any messages to be sent via the transaction yet. This is basically safe to + // enable, (safe the possibility of someone spamming the parachain if they're willing to pay + // the DOT to send from the Relay-chain). But it's useless until we bring in XCM v3 which will + // make `DescendOrigin` a bit more useful. + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = XcmRouter; + // Anyone can execute XCM messages locally. + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = xcm_executor::XcmExecutor; + // Anyone is able to use teleportation regardless of who they are and what they want to + // teleport. + type XcmTeleportFilter = Everything; + // Anyone is able to use reserve transfers regardless of who they are and what they want to + // transfer. + type XcmReserveTransferFilter = Everything; + type Weigher = XcmWeigher; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = SovereignAccountOf; + type MaxLockers = frame_support::traits::ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; + type AdminOrigin = EnsureRoot; +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + millau_messages::{FromMillauMessageDispatch, XCM_LANE}, + WithMillauMessagesInstance, + }; + use bp_messages::{ + target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, + LaneId, MessageKey, + }; + use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult; + use codec::Encode; + use pallet_bridge_messages::OutboundLanes; + use xcm_executor::XcmExecutor; + + fn new_test_ext() -> sp_io::TestExternalities { + sp_io::TestExternalities::new( + frame_system::GenesisConfig::default().build_storage::().unwrap(), + ) + } + + fn prepare_outbound_xcm_message(destination: NetworkId) -> Xcm { + vec![ExportMessage { + network: destination, + destination: destination.into(), + xcm: vec![Instruction::Trap(42)].into(), + }] + .into() + } + + #[test] + fn xcm_messages_to_millau_are_sent_using_bridge_exporter() { + new_test_ext().execute_with(|| { + // ensure that the there are no messages queued + assert_eq!( + OutboundLanes::::get(XCM_LANE) + .latest_generated_nonce, + 0, + ); + + // export message instruction "sends" message to Rialto + XcmExecutor::::execute_xcm_in_credit( + Here, + prepare_outbound_xcm_message(MillauNetwork::get()), + Default::default(), + Weight::MAX, + Weight::MAX, + ) + .ensure_complete() + .expect("runtime configuration must be correct"); + + // ensure that the message has been queued + assert_eq!( + OutboundLanes::::get(XCM_LANE) + .latest_generated_nonce, + 1, + ); + }) + } + + fn prepare_inbound_bridge_message() -> DispatchMessage> { + let xcm = xcm::VersionedXcm::::V3(vec![Instruction::Trap(42)].into()); + let location = + xcm::VersionedInteriorMultiLocation::V3(X1(GlobalConsensus(ThisNetwork::get()))); + // this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor + // or public fields, so just tuple + let bridge_message = (location, xcm).encode(); + DispatchMessage { + key: MessageKey { lane_id: LaneId([0, 0, 0, 0]), nonce: 1 }, + data: DispatchMessageData { payload: Ok(bridge_message) }, + } + } + + #[test] + fn xcm_messages_from_millau_are_dispatched() { + let incoming_message = prepare_inbound_bridge_message(); + + // we care only about handing message to the XCM dispatcher, so we don't care about its + // actual dispatch + let dispatch_result = + FromMillauMessageDispatch::dispatch(&AccountId::from([0u8; 32]), incoming_message); + assert!(matches!( + dispatch_result.dispatch_level_result, + XcmBlobMessageDispatchResult::NotDispatched(_), + )); + } +} diff --git a/bin/runtime-common/Cargo.toml b/bin/runtime-common/Cargo.toml new file mode 100644 index 00000000000..3db4ae9abca --- /dev/null +++ b/bin/runtime-common/Cargo.toml @@ -0,0 +1,92 @@ +[package] +name = "bridge-runtime-common" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +repository = "https://github.com/paritytech/parity-bridges-common/" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } +hash-db = { version = "0.16.0", default-features = false } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +static_assertions = { version = "1.1", optional = true } + +# Bridge dependencies + +bp-header-chain = { path = "../../primitives/header-chain", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } +bp-parachains = { path = "../../primitives/parachains", default-features = false } +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-relayers = { path = "../../primitives/relayers", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +pallet-bridge-grandpa = { path = "../../modules/grandpa", default-features = false } +pallet-bridge-messages = { path = "../../modules/messages", default-features = false } +pallet-bridge-parachains = { path = "../../modules/parachains", default-features = false } +pallet-bridge-relayers = { path = "../../modules/relayers", default-features = false } + +# Substrate dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-utility = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +# Polkadot dependencies +pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + +[dev-dependencies] +bp-test-utils = { path = "../../primitives/test-utils" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "bp-messages/std", + "bp-parachains/std", + "bp-polkadot-core/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "frame-system/std", + "hash-db/std", + "log/std", + "pallet-bridge-grandpa/std", + "pallet-bridge-messages/std", + "pallet-bridge-parachains/std", + "pallet-bridge-relayers/std", + "pallet-transaction-payment/std", + "pallet-utility/std", + "pallet-xcm/std", + "scale-info/std", + "sp-api/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", +] +runtime-benchmarks = [ + "pallet-bridge-grandpa/runtime-benchmarks", + "pallet-bridge-messages/runtime-benchmarks", + "pallet-bridge-parachains/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", +] +integrity-test = [ + "static_assertions", +] diff --git a/bin/runtime-common/src/integrity.rs b/bin/runtime-common/src/integrity.rs new file mode 100644 index 00000000000..5820dd99b91 --- /dev/null +++ b/bin/runtime-common/src/integrity.rs @@ -0,0 +1,331 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Integrity tests for chain constants and pallets configuration. +//! +//! Most of the tests in this module assume that the bridge is using standard (see `crate::messages` +//! module for details) configuration. + +use crate::{messages, messages::MessageBridge}; + +use bp_messages::{InboundLaneData, MessageNonce}; +use bp_runtime::{Chain, ChainId}; +use codec::Encode; +use frame_support::{storage::generator::StorageValue, traits::Get}; +use frame_system::limits; +use sp_runtime::traits::SignedExtension; + +/// Macro that ensures that the runtime configuration and chain primitives crate are sharing +/// the same types (index, block number, hash, hasher, account id and header). +#[macro_export] +macro_rules! assert_chain_types( + ( runtime: $r:path, this_chain: $this:path ) => { + { + // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard + // configuration is used), or something has broke existing configuration (meaning that all bridged chains + // and relays will stop functioning) + use frame_system::Config as SystemConfig; + use static_assertions::assert_type_eq_all; + + assert_type_eq_all!(<$r as SystemConfig>::Index, bp_runtime::IndexOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::BlockNumber, bp_runtime::BlockNumberOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::Hash, bp_runtime::HashOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::Hashing, bp_runtime::HasherOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::AccountId, bp_runtime::AccountIdOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::Header, bp_runtime::HeaderOf<$this>); + } + } +); + +/// Macro that ensures that the bridge GRANDPA pallet is configured properly to bridge with given +/// chain. +#[macro_export] +macro_rules! assert_bridge_grandpa_pallet_types( + ( runtime: $r:path, with_bridged_chain_grandpa_instance: $i:path, bridged_chain: $bridged:path ) => { + { + // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard + // configuration is used), or something has broke existing configuration (meaning that all bridged chains + // and relays will stop functioning) + use pallet_bridge_grandpa::Config as GrandpaConfig; + use static_assertions::assert_type_eq_all; + + assert_type_eq_all!(<$r as GrandpaConfig<$i>>::BridgedChain, $bridged); + } + } +); + +/// Macro that ensures that the bridge messages pallet is configured properly to bridge using given +/// configuration. +#[macro_export] +macro_rules! assert_bridge_messages_pallet_types( + ( + runtime: $r:path, + with_bridged_chain_messages_instance: $i:path, + bridge: $bridge:path + ) => { + { + // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard + // configuration is used), or something has broke existing configuration (meaning that all bridged chains + // and relays will stop functioning) + use $crate::messages::{ + source::{FromThisChainMessagePayload, TargetHeaderChainAdapter}, + target::{FromBridgedChainMessagePayload, SourceHeaderChainAdapter}, + AccountIdOf, BalanceOf, BridgedChain, ThisChain, + }; + use pallet_bridge_messages::Config as MessagesConfig; + use static_assertions::assert_type_eq_all; + + assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, FromThisChainMessagePayload); + + assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundRelayer, AccountIdOf>); + + assert_type_eq_all!(<$r as MessagesConfig<$i>>::TargetHeaderChain, TargetHeaderChainAdapter<$bridge>); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::SourceHeaderChain, SourceHeaderChainAdapter<$bridge>); + } + } +); + +/// Macro that combines four other macro calls - `assert_chain_types`, `assert_bridge_types`, +/// `assert_bridge_grandpa_pallet_types` and `assert_bridge_messages_pallet_types`. It may be used +/// at the chain that is implementing complete standard messages bridge (i.e. with bridge GRANDPA +/// and messages pallets deployed). +#[macro_export] +macro_rules! assert_complete_bridge_types( + ( + runtime: $r:path, + with_bridged_chain_grandpa_instance: $gi:path, + with_bridged_chain_messages_instance: $mi:path, + bridge: $bridge:path, + this_chain: $this:path, + bridged_chain: $bridged:path, + ) => { + $crate::assert_chain_types!(runtime: $r, this_chain: $this); + $crate::assert_bridge_grandpa_pallet_types!( + runtime: $r, + with_bridged_chain_grandpa_instance: $gi, + bridged_chain: $bridged + ); + $crate::assert_bridge_messages_pallet_types!( + runtime: $r, + with_bridged_chain_messages_instance: $mi, + bridge: $bridge + ); + } +); + +/// Parameters for asserting chain-related constants. +#[derive(Debug)] +pub struct AssertChainConstants { + /// Block length limits of the chain. + pub block_length: limits::BlockLength, + /// Block weight limits of the chain. + pub block_weights: limits::BlockWeights, +} + +/// Test that our hardcoded, chain-related constants, are matching chain runtime configuration. +/// +/// In particular, this test ensures that: +/// +/// 1) block weight limits are matching; +/// 2) block size limits are matching. +pub fn assert_chain_constants(params: AssertChainConstants) +where + R: frame_system::Config, +{ + // we don't check runtime version here, because in our case we'll be building relay from one + // repo and runtime will live in another repo, along with outdated relay version. To avoid + // unneeded commits, let's not raise an error in case of version mismatch. + + // if one of following assert fails, it means that we may need to upgrade bridged chain and + // relay to use updated constants. If constants are now smaller than before, it may lead to + // undeliverable messages. + + // `BlockLength` struct is not implementing `PartialEq`, so we compare encoded values here. + assert_eq!( + R::BlockLength::get().encode(), + params.block_length.encode(), + "BlockLength from runtime ({:?}) differ from hardcoded: {:?}", + R::BlockLength::get(), + params.block_length, + ); + // `BlockWeights` struct is not implementing `PartialEq`, so we compare encoded values here + assert_eq!( + R::BlockWeights::get().encode(), + params.block_weights.encode(), + "BlockWeights from runtime ({:?}) differ from hardcoded: {:?}", + R::BlockWeights::get(), + params.block_weights, + ); +} + +/// Test that the constants, used in GRANDPA pallet configuration are valid. +pub fn assert_bridge_grandpa_pallet_constants() +where + R: pallet_bridge_grandpa::Config, + GI: 'static, +{ + assert!( + R::HeadersToKeep::get() > 0, + "HeadersToKeep ({}) must be larger than zero", + R::HeadersToKeep::get(), + ); +} + +/// Parameters for asserting messages pallet constants. +#[derive(Debug)] +pub struct AssertBridgeMessagesPalletConstants { + /// Maximal number of unrewarded relayer entries in a confirmation transaction at the bridged + /// chain. + pub max_unrewarded_relayers_in_bridged_confirmation_tx: MessageNonce, + /// Maximal number of unconfirmed messages in a confirmation transaction at the bridged chain. + pub max_unconfirmed_messages_in_bridged_confirmation_tx: MessageNonce, + /// Identifier of the bridged chain. + pub bridged_chain_id: ChainId, +} + +/// Test that the constants, used in messages pallet configuration are valid. +pub fn assert_bridge_messages_pallet_constants(params: AssertBridgeMessagesPalletConstants) +where + R: pallet_bridge_messages::Config, + MI: 'static, +{ + assert!( + !R::ActiveOutboundLanes::get().is_empty(), + "ActiveOutboundLanes ({:?}) must not be empty", + R::ActiveOutboundLanes::get(), + ); + assert!( + R::MaxUnrewardedRelayerEntriesAtInboundLane::get() <= params.max_unrewarded_relayers_in_bridged_confirmation_tx, + "MaxUnrewardedRelayerEntriesAtInboundLane ({}) must be <= than the hardcoded value for bridged chain: {}", + R::MaxUnrewardedRelayerEntriesAtInboundLane::get(), + params.max_unrewarded_relayers_in_bridged_confirmation_tx, + ); + assert!( + R::MaxUnconfirmedMessagesAtInboundLane::get() <= params.max_unconfirmed_messages_in_bridged_confirmation_tx, + "MaxUnrewardedRelayerEntriesAtInboundLane ({}) must be <= than the hardcoded value for bridged chain: {}", + R::MaxUnconfirmedMessagesAtInboundLane::get(), + params.max_unconfirmed_messages_in_bridged_confirmation_tx, + ); + assert_eq!(R::BridgedChainId::get(), params.bridged_chain_id); +} + +/// Parameters for asserting bridge pallet names. +#[derive(Debug)] +pub struct AssertBridgePalletNames<'a> { + /// Name of the messages pallet, deployed at the bridged chain and used to bridge with this + /// chain. + pub with_this_chain_messages_pallet_name: &'a str, + /// Name of the GRANDPA pallet, deployed at this chain and used to bridge with the bridged + /// chain. + pub with_bridged_chain_grandpa_pallet_name: &'a str, + /// Name of the messages pallet, deployed at this chain and used to bridge with the bridged + /// chain. + pub with_bridged_chain_messages_pallet_name: &'a str, +} + +/// Tests that bridge pallet names used in `construct_runtime!()` macro call are matching constants +/// from chain primitives crates. +pub fn assert_bridge_pallet_names(params: AssertBridgePalletNames) +where + B: MessageBridge, + R: pallet_bridge_grandpa::Config + pallet_bridge_messages::Config, + GI: 'static, + MI: 'static, +{ + assert_eq!(B::BRIDGED_MESSAGES_PALLET_NAME, params.with_this_chain_messages_pallet_name); + assert_eq!( + pallet_bridge_grandpa::PalletOwner::::storage_value_final_key().to_vec(), + bp_runtime::storage_value_key(params.with_bridged_chain_grandpa_pallet_name, "PalletOwner",).0, + ); + assert_eq!( + pallet_bridge_messages::PalletOwner::::storage_value_final_key().to_vec(), + bp_runtime::storage_value_key( + params.with_bridged_chain_messages_pallet_name, + "PalletOwner", + ) + .0, + ); +} + +/// Parameters for asserting complete standard messages bridge. +#[derive(Debug)] +pub struct AssertCompleteBridgeConstants<'a> { + /// Parameters to assert this chain constants. + pub this_chain_constants: AssertChainConstants, + /// Parameters to assert messages pallet constants. + pub messages_pallet_constants: AssertBridgeMessagesPalletConstants, + /// Parameters to assert pallet names constants. + pub pallet_names: AssertBridgePalletNames<'a>, +} + +/// All bridge-related constants tests for the complete standard messages bridge (i.e. with bridge +/// GRANDPA and messages pallets deployed). +pub fn assert_complete_bridge_constants(params: AssertCompleteBridgeConstants) +where + R: frame_system::Config + + pallet_bridge_grandpa::Config + + pallet_bridge_messages::Config, + GI: 'static, + MI: 'static, + B: MessageBridge, +{ + assert_chain_constants::(params.this_chain_constants); + assert_bridge_grandpa_pallet_constants::(); + assert_bridge_messages_pallet_constants::(params.messages_pallet_constants); + assert_bridge_pallet_names::(params.pallet_names); +} + +/// Check that the message lane weights are correct. +pub fn check_message_lane_weights( + bridged_chain_extra_storage_proof_size: u32, + this_chain_max_unrewarded_relayers: MessageNonce, + this_chain_max_unconfirmed_messages: MessageNonce, +) { + type Weights = pallet_bridge_messages::weights::BridgeWeight; + + pallet_bridge_messages::ensure_weights_are_correct::>(); + + let max_incoming_message_proof_size = bridged_chain_extra_storage_proof_size + .saturating_add(messages::target::maximal_incoming_message_size(C::max_extrinsic_size())); + pallet_bridge_messages::ensure_able_to_receive_message::>( + C::max_extrinsic_size(), + C::max_extrinsic_weight(), + max_incoming_message_proof_size, + messages::target::maximal_incoming_message_dispatch_weight(C::max_extrinsic_weight()), + ); + + let max_incoming_inbound_lane_data_proof_size = + InboundLaneData::<()>::encoded_size_hint_u32(this_chain_max_unrewarded_relayers as _); + pallet_bridge_messages::ensure_able_to_receive_confirmation::>( + C::max_extrinsic_size(), + C::max_extrinsic_weight(), + max_incoming_inbound_lane_data_proof_size, + this_chain_max_unrewarded_relayers, + this_chain_max_unconfirmed_messages, + ); +} + +/// Check that the `AdditionalSigned` type of a wrapped runtime is the same as the one of the +/// corresponding actual runtime. +/// +/// This method doesn't perform any `assert`. If the condition is not true it will generate a +/// compile-time error. +pub fn check_additional_signed() +where + SignedExt: SignedExtension, + IndirectSignedExt: SignedExtension, +{ +} diff --git a/bin/runtime-common/src/lib.rs b/bin/runtime-common/src/lib.rs new file mode 100644 index 00000000000..e8a2d2470fa --- /dev/null +++ b/bin/runtime-common/src/lib.rs @@ -0,0 +1,245 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Common types/functions that may be used by runtimes of all bridged chains. + +#![cfg_attr(not(feature = "std"), no_std)] + +use crate::messages_call_ext::MessagesCallSubType; +use pallet_bridge_grandpa::CallSubType as GrandpaCallSubType; +use pallet_bridge_parachains::CallSubType as ParachainsCallSubtype; +use sp_runtime::transaction_validity::TransactionValidity; +use xcm::v3::NetworkId; + +pub mod messages; +pub mod messages_api; +pub mod messages_benchmarking; +pub mod messages_call_ext; +pub mod messages_xcm_extension; +pub mod parachains_benchmarking; +pub mod priority_calculator; +pub mod refund_relayer_extension; + +mod messages_generation; +mod mock; + +#[cfg(feature = "integrity-test")] +pub mod integrity; + +const LOG_TARGET_BRIDGE_DISPATCH: &str = "runtime::bridge-dispatch"; + +/// A duplication of the `FilterCall` trait. +/// +/// We need this trait in order to be able to implement it for the messages pallet, +/// since the implementation is done outside of the pallet crate. +pub trait BridgeRuntimeFilterCall { + /// Checks if a runtime call is valid. + fn validate(call: &Call) -> TransactionValidity; +} + +impl BridgeRuntimeFilterCall for pallet_bridge_grandpa::Pallet +where + T: pallet_bridge_grandpa::Config, + T::RuntimeCall: GrandpaCallSubType, +{ + fn validate(call: &T::RuntimeCall) -> TransactionValidity { + GrandpaCallSubType::::check_obsolete_submit_finality_proof(call) + } +} + +impl BridgeRuntimeFilterCall + for pallet_bridge_parachains::Pallet +where + T: pallet_bridge_parachains::Config, + T::RuntimeCall: ParachainsCallSubtype, +{ + fn validate(call: &T::RuntimeCall) -> TransactionValidity { + ParachainsCallSubtype::::check_obsolete_submit_parachain_heads(call) + } +} + +impl, I: 'static> BridgeRuntimeFilterCall + for pallet_bridge_messages::Pallet +where + T::RuntimeCall: MessagesCallSubType, +{ + /// Validate messages in order to avoid "mining" messages delivery and delivery confirmation + /// transactions, that are delivering outdated messages/confirmations. Without this validation, + /// even honest relayers may lose their funds if there are multiple relays running and + /// submitting the same messages/confirmations. + fn validate(call: &T::RuntimeCall) -> TransactionValidity { + call.check_obsolete_call() + } +} + +/// Declares a runtime-specific `BridgeRejectObsoleteHeadersAndMessages` signed extension. +/// +/// ## Example +/// +/// ```nocompile +/// generate_bridge_reject_obsolete_headers_and_messages!{ +/// Call, AccountId +/// BridgeRialtoGrandpa, BridgeWestendGrandpa, +/// BridgeRialtoParachains +/// } +/// ``` +/// +/// The goal of this extension is to avoid "mining" transactions that provide outdated bridged +/// headers and messages. Without that extension, even honest relayers may lose their funds if +/// there are multiple relays running and submitting the same information. +#[macro_export] +macro_rules! generate_bridge_reject_obsolete_headers_and_messages { + ($call:ty, $account_id:ty, $($filter_call:ty),*) => { + #[derive(Clone, codec::Decode, Default, codec::Encode, Eq, PartialEq, frame_support::RuntimeDebug, scale_info::TypeInfo)] + pub struct BridgeRejectObsoleteHeadersAndMessages; + impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteHeadersAndMessages { + const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages"; + type AccountId = $account_id; + type Call = $call; + type AdditionalSigned = (); + type Pre = (); + + fn additional_signed(&self) -> sp_std::result::Result< + (), + sp_runtime::transaction_validity::TransactionValidityError, + > { + Ok(()) + } + + fn validate( + &self, + _who: &Self::AccountId, + call: &Self::Call, + _info: &sp_runtime::traits::DispatchInfoOf, + _len: usize, + ) -> sp_runtime::transaction_validity::TransactionValidity { + let valid = sp_runtime::transaction_validity::ValidTransaction::default(); + $( + let valid = valid + .combine_with(<$filter_call as $crate::BridgeRuntimeFilterCall<$call>>::validate(call)?); + )* + Ok(valid) + } + + fn pre_dispatch( + self, + who: &Self::AccountId, + call: &Self::Call, + info: &sp_runtime::traits::DispatchInfoOf, + len: usize, + ) -> Result { + self.validate(who, call, info, len).map(drop) + } + } + }; +} + +/// A mapping over `NetworkId`. +/// Since `NetworkId` doesn't include `Millau`, `Rialto` and `RialtoParachain`, we create some +/// synthetic associations between these chains and `NetworkId` chains. +pub enum CustomNetworkId { + /// The Millau network ID, associated with Kusama. + Millau, + /// The Rialto network ID, associated with Polkadot. + Rialto, + /// The RialtoParachain network ID, associated with Westend. + RialtoParachain, +} + +impl CustomNetworkId { + pub const fn as_network_id(&self) -> NetworkId { + match *self { + CustomNetworkId::Millau => NetworkId::Kusama, + CustomNetworkId::Rialto => NetworkId::Polkadot, + CustomNetworkId::RialtoParachain => NetworkId::Westend, + } + } +} + +#[cfg(test)] +mod tests { + use crate::BridgeRuntimeFilterCall; + use frame_support::{assert_err, assert_ok}; + use sp_runtime::{ + traits::SignedExtension, + transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, + }; + + pub struct MockCall { + data: u32, + } + + impl sp_runtime::traits::Dispatchable for MockCall { + type RuntimeOrigin = (); + type Config = (); + type Info = (); + type PostInfo = (); + + fn dispatch( + self, + _origin: Self::RuntimeOrigin, + ) -> sp_runtime::DispatchResultWithInfo { + unimplemented!() + } + } + + struct FirstFilterCall; + impl BridgeRuntimeFilterCall for FirstFilterCall { + fn validate(call: &MockCall) -> TransactionValidity { + if call.data <= 1 { + return InvalidTransaction::Custom(1).into() + } + + Ok(ValidTransaction { priority: 1, ..Default::default() }) + } + } + + struct SecondFilterCall; + impl BridgeRuntimeFilterCall for SecondFilterCall { + fn validate(call: &MockCall) -> TransactionValidity { + if call.data <= 2 { + return InvalidTransaction::Custom(2).into() + } + + Ok(ValidTransaction { priority: 2, ..Default::default() }) + } + } + + #[test] + fn test() { + generate_bridge_reject_obsolete_headers_and_messages!( + MockCall, + (), + FirstFilterCall, + SecondFilterCall + ); + + assert_err!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 1 }, &(), 0), + InvalidTransaction::Custom(1) + ); + + assert_err!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 2 }, &(), 0), + InvalidTransaction::Custom(2) + ); + + assert_ok!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 3 }, &(), 0), + ValidTransaction { priority: 3, ..Default::default() } + ) + } +} diff --git a/bin/runtime-common/src/messages.rs b/bin/runtime-common/src/messages.rs new file mode 100644 index 00000000000..9d2e5811380 --- /dev/null +++ b/bin/runtime-common/src/messages.rs @@ -0,0 +1,787 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types that allow runtime to act as a source/target endpoint of message lanes. +//! +//! Messages are assumed to be encoded `Call`s of the target chain. Call-dispatch +//! pallet is used to dispatch incoming messages. Message identified by a tuple +//! of to elements - message lane id and message nonce. + +pub use bp_runtime::{UnderlyingChainOf, UnderlyingChainProvider}; + +use bp_header_chain::{HeaderChain, HeaderChainError}; +use bp_messages::{ + source_chain::{LaneMessageVerifier, TargetHeaderChain}, + target_chain::{ProvedLaneMessages, ProvedMessages, SourceHeaderChain}, + InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, +}; +use bp_runtime::{Chain, RawStorageProof, Size, StorageProofChecker, StorageProofError}; +use codec::{Decode, Encode}; +use frame_support::{traits::Get, weights::Weight, RuntimeDebug}; +use hash_db::Hasher; +use scale_info::TypeInfo; +use sp_std::{convert::TryFrom, fmt::Debug, marker::PhantomData, vec::Vec}; + +/// Bidirectional message bridge. +pub trait MessageBridge { + /// Name of the paired messages pallet instance at the Bridged chain. + /// + /// Should be the name that is used in the `construct_runtime!()` macro. + const BRIDGED_MESSAGES_PALLET_NAME: &'static str; + + /// This chain in context of message bridge. + type ThisChain: ThisChainWithMessages; + /// Bridged chain in context of message bridge. + type BridgedChain: BridgedChainWithMessages; + /// Bridged header chain. + type BridgedHeaderChain: HeaderChain>; +} + +/// This chain that has `pallet-bridge-messages` module. +pub trait ThisChainWithMessages: UnderlyingChainProvider { + /// Call origin on the chain. + type RuntimeOrigin; +} + +/// Bridged chain that has `pallet-bridge-messages` module. +pub trait BridgedChainWithMessages: UnderlyingChainProvider {} + +/// This chain in context of message bridge. +pub type ThisChain = ::ThisChain; +/// Bridged chain in context of message bridge. +pub type BridgedChain = ::BridgedChain; +/// Hash used on the chain. +pub type HashOf = bp_runtime::HashOf<::Chain>; +/// Hasher used on the chain. +pub type HasherOf = bp_runtime::HasherOf>; +/// Account id used on the chain. +pub type AccountIdOf = bp_runtime::AccountIdOf>; +/// Type of balances that is used on the chain. +pub type BalanceOf = bp_runtime::BalanceOf>; +/// Type of origin that is used on the chain. +pub type OriginOf = ::RuntimeOrigin; + +/// Error that happens during message verification. +#[derive(Debug, PartialEq, Eq)] +pub enum Error { + /// The message proof is empty. + EmptyMessageProof, + /// Error returned by the bridged header chain. + HeaderChain(HeaderChainError), + /// Error returned while reading/decoding inbound lane data from the storage proof. + InboundLaneStorage(StorageProofError), + /// The declared message weight is incorrect. + InvalidMessageWeight, + /// Declared messages count doesn't match actual value. + MessagesCountMismatch, + /// Error returned while reading/decoding message data from the storage proof. + MessageStorage(StorageProofError), + /// The message is too large. + MessageTooLarge, + /// Error returned while reading/decoding outbound lane data from the storage proof. + OutboundLaneStorage(StorageProofError), + /// Storage proof related error. + StorageProof(StorageProofError), +} + +/// Sub-module that is declaring types required for processing This -> Bridged chain messages. +pub mod source { + use super::*; + + /// Message payload for This -> Bridged chain messages. + pub type FromThisChainMessagePayload = crate::messages_xcm_extension::XcmAsPlainPayload; + + /// Maximal size of outbound message payload. + pub struct FromThisChainMaximalOutboundPayloadSize(PhantomData); + + impl Get for FromThisChainMaximalOutboundPayloadSize { + fn get() -> u32 { + maximal_message_size::() + } + } + + /// Messages delivery proof from bridged chain: + /// + /// - hash of finalized header; + /// - storage proof of inbound lane state; + /// - lane id. + #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] + pub struct FromBridgedChainMessagesDeliveryProof { + /// Hash of the bridge header the proof is for. + pub bridged_header_hash: BridgedHeaderHash, + /// Storage trie proof generated for [`Self::bridged_header_hash`]. + pub storage_proof: RawStorageProof, + /// Lane id of which messages were delivered and the proof is for. + pub lane: LaneId, + } + + impl Size for FromBridgedChainMessagesDeliveryProof { + fn size(&self) -> u32 { + u32::try_from( + self.storage_proof + .iter() + .fold(0usize, |sum, node| sum.saturating_add(node.len())), + ) + .unwrap_or(u32::MAX) + } + } + + /// 'Parsed' message delivery proof - inbound lane id and its state. + pub type ParsedMessagesDeliveryProofFromBridgedChain = + (LaneId, InboundLaneData>>); + + /// Message verifier that is doing all basic checks. + /// + /// This verifier assumes following: + /// + /// - all message lanes are equivalent, so all checks are the same; + /// + /// Following checks are made: + /// + /// - message is rejected if its lane is currently blocked; + /// - message is rejected if there are too many pending (undelivered) messages at the outbound + /// lane; + /// - check that the sender has rights to dispatch the call on target chain using provided + /// dispatch origin; + /// - check that the sender has paid enough funds for both message delivery and dispatch. + #[derive(RuntimeDebug)] + pub struct FromThisChainMessageVerifier(PhantomData); + + impl LaneMessageVerifier>, FromThisChainMessagePayload> + for FromThisChainMessageVerifier + where + B: MessageBridge, + // matches requirements from the `frame_system::Config::Origin` + OriginOf>: Clone + + Into>>, OriginOf>>>, + AccountIdOf>: PartialEq + Clone, + { + type Error = &'static str; + + fn verify_message( + _submitter: &OriginOf>, + _lane: &LaneId, + _lane_outbound_data: &OutboundLaneData, + _payload: &FromThisChainMessagePayload, + ) -> Result<(), Self::Error> { + // IMPORTANT: any error that is returned here is fatal for the bridge, because + // this code is executed at the bridge hub and message sender actually lives + // at some sibling parachain. So we are failing **after** the message has been + // sent and we can't report it back to sender (unless error report mechanism is + // embedded into message and its dispatcher). + + Ok(()) + } + } + + /// Return maximal message size of This -> Bridged chain message. + pub fn maximal_message_size() -> u32 { + super::target::maximal_incoming_message_size( + UnderlyingChainOf::>::max_extrinsic_size(), + ) + } + + /// `TargetHeaderChain` implementation that is using default types and perform default checks. + pub struct TargetHeaderChainAdapter(PhantomData); + + impl TargetHeaderChain>> + for TargetHeaderChainAdapter + { + type Error = Error; + type MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof>>; + + fn verify_message(payload: &FromThisChainMessagePayload) -> Result<(), Self::Error> { + verify_chain_message::(payload) + } + + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData>>), Self::Error> { + verify_messages_delivery_proof::(proof) + } + } + + /// Do basic Bridged-chain specific verification of This -> Bridged chain message. + /// + /// Ok result from this function means that the delivery transaction with this message + /// may be 'mined' by the target chain. But the lane may have its own checks (e.g. fee + /// check) that would reject message (see `FromThisChainMessageVerifier`). + pub fn verify_chain_message( + payload: &FromThisChainMessagePayload, + ) -> Result<(), Error> { + // IMPORTANT: any error that is returned here is fatal for the bridge, because + // this code is executed at the bridge hub and message sender actually lives + // at some sibling parachain. So we are failing **after** the message has been + // sent and we can't report it back to sender (unless error report mechanism is + // embedded into message and its dispatcher). + + // apart from maximal message size check (see below), we should also check the message + // dispatch weight here. But we assume that the bridged chain will just push the message + // to some queue (XCMP, UMP, DMP), so the weight is constant and fits the block. + + // The maximal size of extrinsic at Substrate-based chain depends on the + // `frame_system::Config::MaximumBlockLength` and + // `frame_system::Config::AvailableBlockRatio` constants. This check is here to be sure that + // the lane won't stuck because message is too large to fit into delivery transaction. + // + // **IMPORTANT NOTE**: the delivery transaction contains storage proof of the message, not + // the message itself. The proof is always larger than the message. But unless chain state + // is enormously large, it should be several dozens/hundreds of bytes. The delivery + // transaction also contains signatures and signed extensions. Because of this, we reserve + // 1/3 of the the maximal extrinsic weight for this data. + if payload.len() > maximal_message_size::() as usize { + return Err(Error::MessageTooLarge) + } + + Ok(()) + } + + /// Verify proof of This -> Bridged chain messages delivery. + /// + /// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged + /// parachains, please use the `verify_messages_delivery_proof_from_parachain`. + pub fn verify_messages_delivery_proof( + proof: FromBridgedChainMessagesDeliveryProof>>, + ) -> Result, Error> { + let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } = + proof; + B::BridgedHeaderChain::parse_finalized_storage_proof( + bridged_header_hash, + storage_proof, + |mut storage| { + // Messages delivery proof is just proof of single storage key read => any error + // is fatal. + let storage_inbound_lane_data_key = + bp_messages::storage_keys::inbound_lane_data_key( + B::BRIDGED_MESSAGES_PALLET_NAME, + &lane, + ); + let inbound_lane_data = storage + .read_and_decode_mandatory_value(storage_inbound_lane_data_key.0.as_ref()) + .map_err(Error::InboundLaneStorage)?; + + // check that the storage proof doesn't have any untouched trie nodes + storage.ensure_no_unused_nodes().map_err(Error::StorageProof)?; + + Ok((lane, inbound_lane_data)) + }, + ) + .map_err(Error::HeaderChain)? + } +} + +/// Sub-module that is declaring types required for processing Bridged -> This chain messages. +pub mod target { + use super::*; + + /// Decoded Bridged -> This message payload. + pub type FromBridgedChainMessagePayload = crate::messages_xcm_extension::XcmAsPlainPayload; + + /// Messages proof from bridged chain: + /// + /// - hash of finalized header; + /// - storage proof of messages and (optionally) outbound lane state; + /// - lane id; + /// - nonces (inclusive range) of messages which are included in this proof. + #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] + pub struct FromBridgedChainMessagesProof { + /// Hash of the finalized bridged header the proof is for. + pub bridged_header_hash: BridgedHeaderHash, + /// A storage trie proof of messages being delivered. + pub storage_proof: RawStorageProof, + /// Messages in this proof are sent over this lane. + pub lane: LaneId, + /// Nonce of the first message being delivered. + pub nonces_start: MessageNonce, + /// Nonce of the last message being delivered. + pub nonces_end: MessageNonce, + } + + impl Size for FromBridgedChainMessagesProof { + fn size(&self) -> u32 { + u32::try_from( + self.storage_proof + .iter() + .fold(0usize, |sum, node| sum.saturating_add(node.len())), + ) + .unwrap_or(u32::MAX) + } + } + + /// Return maximal dispatch weight of the message we're able to receive. + pub fn maximal_incoming_message_dispatch_weight(maximal_extrinsic_weight: Weight) -> Weight { + maximal_extrinsic_weight / 2 + } + + /// Return maximal message size given maximal extrinsic size. + pub fn maximal_incoming_message_size(maximal_extrinsic_size: u32) -> u32 { + maximal_extrinsic_size / 3 * 2 + } + + /// `SourceHeaderChain` implementation that is using default types and perform default checks. + pub struct SourceHeaderChainAdapter(PhantomData); + + impl SourceHeaderChain for SourceHeaderChainAdapter { + type Error = Error; + type MessagesProof = FromBridgedChainMessagesProof>>; + + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result, Self::Error> { + verify_messages_proof::(proof, messages_count) + } + } + + /// Verify proof of Bridged -> This chain messages. + /// + /// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged + /// parachains, please use the `verify_messages_proof_from_parachain`. + /// + /// The `messages_count` argument verification (sane limits) is supposed to be made + /// outside of this function. This function only verifies that the proof declares exactly + /// `messages_count` messages. + pub fn verify_messages_proof( + proof: FromBridgedChainMessagesProof>>, + messages_count: u32, + ) -> Result, Error> { + let FromBridgedChainMessagesProof { + bridged_header_hash, + storage_proof, + lane, + nonces_start, + nonces_end, + } = proof; + + B::BridgedHeaderChain::parse_finalized_storage_proof( + bridged_header_hash, + storage_proof, + |storage| { + let mut parser = + StorageProofCheckerAdapter::<_, B> { storage, _dummy: Default::default() }; + + // receiving proofs where end < begin is ok (if proof includes outbound lane state) + let messages_in_the_proof = + if let Some(nonces_difference) = nonces_end.checked_sub(nonces_start) { + // let's check that the user (relayer) has passed correct `messages_count` + // (this bounds maximal capacity of messages vec below) + let messages_in_the_proof = nonces_difference.saturating_add(1); + if messages_in_the_proof != MessageNonce::from(messages_count) { + return Err(Error::MessagesCountMismatch) + } + + messages_in_the_proof + } else { + 0 + }; + + // Read messages first. All messages that are claimed to be in the proof must + // be in the proof. So any error in `read_value`, or even missing value is fatal. + // + // Mind that we allow proofs with no messages if outbound lane state is proved. + let mut messages = Vec::with_capacity(messages_in_the_proof as _); + for nonce in nonces_start..=nonces_end { + let message_key = MessageKey { lane_id: lane, nonce }; + let message_payload = parser.read_and_decode_message_payload(&message_key)?; + messages.push(Message { key: message_key, payload: message_payload }); + } + + // Now let's check if proof contains outbound lane state proof. It is optional, so + // we simply ignore `read_value` errors and missing value. + let proved_lane_messages = ProvedLaneMessages { + lane_state: parser.read_and_decode_outbound_lane_data(&lane)?, + messages, + }; + + // Now we may actually check if the proof is empty or not. + if proved_lane_messages.lane_state.is_none() && + proved_lane_messages.messages.is_empty() + { + return Err(Error::EmptyMessageProof) + } + + // check that the storage proof doesn't have any untouched trie nodes + parser.storage.ensure_no_unused_nodes().map_err(Error::StorageProof)?; + + // We only support single lane messages in this generated_schema + let mut proved_messages = ProvedMessages::new(); + proved_messages.insert(lane, proved_lane_messages); + + Ok(proved_messages) + }, + ) + .map_err(Error::HeaderChain)? + } + + struct StorageProofCheckerAdapter { + storage: StorageProofChecker, + _dummy: sp_std::marker::PhantomData, + } + + impl StorageProofCheckerAdapter { + fn read_and_decode_outbound_lane_data( + &mut self, + lane_id: &LaneId, + ) -> Result, Error> { + let storage_outbound_lane_data_key = bp_messages::storage_keys::outbound_lane_data_key( + B::BRIDGED_MESSAGES_PALLET_NAME, + lane_id, + ); + + self.storage + .read_and_decode_opt_value(storage_outbound_lane_data_key.0.as_ref()) + .map_err(Error::OutboundLaneStorage) + } + + fn read_and_decode_message_payload( + &mut self, + message_key: &MessageKey, + ) -> Result { + let storage_message_key = bp_messages::storage_keys::message_key( + B::BRIDGED_MESSAGES_PALLET_NAME, + &message_key.lane_id, + message_key.nonce, + ); + self.storage + .read_and_decode_mandatory_value(storage_message_key.0.as_ref()) + .map_err(Error::MessageStorage) + } + } +} + +/// The `BridgeMessagesCall` used by a chain. +pub type BridgeMessagesCallOf = bp_messages::BridgeMessagesCall< + bp_runtime::AccountIdOf, + target::FromBridgedChainMessagesProof>, + source::FromBridgedChainMessagesDeliveryProof>, +>; + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + messages_generation::{ + encode_all_messages, encode_lane_data, prepare_messages_storage_proof, + }, + mock::*, + }; + use bp_header_chain::StoredHeaderDataBuilder; + use bp_runtime::HeaderId; + use codec::Encode; + use sp_core::H256; + use sp_runtime::traits::Header as _; + + #[test] + fn verify_chain_message_rejects_message_with_too_large_declared_weight() { + assert!(source::verify_chain_message::(&vec![ + 42; + BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT - + 1 + ]) + .is_err()); + } + + #[test] + fn verify_chain_message_rejects_message_too_large_message() { + assert!(source::verify_chain_message::(&vec![ + 0; + source::maximal_message_size::() + as usize + 1 + ],) + .is_err()); + } + + #[test] + fn verify_chain_message_accepts_maximal_message() { + assert_eq!( + source::verify_chain_message::(&vec![ + 0; + source::maximal_message_size::() + as _ + ],), + Ok(()), + ); + } + + fn using_messages_proof( + nonces_end: MessageNonce, + outbound_lane_data: Option, + encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option>, + encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec, + test: impl Fn(target::FromBridgedChainMessagesProof) -> R, + ) -> R { + let (state_root, storage_proof) = prepare_messages_storage_proof::( + TEST_LANE_ID, + 1..=nonces_end, + outbound_lane_data, + bp_runtime::StorageProofSize::Minimal(0), + vec![42], + encode_message, + encode_outbound_lane_data, + ); + + sp_io::TestExternalities::new(Default::default()).execute_with(move || { + let bridged_header = BridgedChainHeader::new( + 0, + Default::default(), + state_root, + Default::default(), + Default::default(), + ); + let bridged_header_hash = bridged_header.hash(); + + pallet_bridge_grandpa::BestFinalized::::put(HeaderId( + 0, + bridged_header_hash, + )); + pallet_bridge_grandpa::ImportedHeaders::::insert( + bridged_header_hash, + bridged_header.build(), + ); + test(target::FromBridgedChainMessagesProof { + bridged_header_hash, + storage_proof, + lane: TEST_LANE_ID, + nonces_start: 1, + nonces_end, + }) + }) + } + + #[test] + fn messages_proof_is_rejected_if_declared_less_than_actual_number_of_messages() { + assert_eq!( + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { + target::verify_messages_proof::(proof, 5) + }), + Err(Error::MessagesCountMismatch), + ); + } + + #[test] + fn messages_proof_is_rejected_if_declared_more_than_actual_number_of_messages() { + assert_eq!( + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { + target::verify_messages_proof::(proof, 15) + }), + Err(Error::MessagesCountMismatch), + ); + } + + #[test] + fn message_proof_is_rejected_if_header_is_missing_from_the_chain() { + assert_eq!( + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { + let bridged_header_hash = + pallet_bridge_grandpa::BestFinalized::::get().unwrap().1; + pallet_bridge_grandpa::ImportedHeaders::::remove(bridged_header_hash); + target::verify_messages_proof::(proof, 10) + }), + Err(Error::HeaderChain(HeaderChainError::UnknownHeader)), + ); + } + + #[test] + fn message_proof_is_rejected_if_header_state_root_mismatches() { + assert_eq!( + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { + let bridged_header_hash = + pallet_bridge_grandpa::BestFinalized::::get().unwrap().1; + pallet_bridge_grandpa::ImportedHeaders::::insert( + bridged_header_hash, + BridgedChainHeader::new( + 0, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ) + .build(), + ); + target::verify_messages_proof::(proof, 10) + }), + Err(Error::HeaderChain(HeaderChainError::StorageProof( + StorageProofError::StorageRootMismatch + ))), + ); + } + + #[test] + fn message_proof_is_rejected_if_it_has_duplicate_trie_nodes() { + assert_eq!( + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |mut proof| { + let node = proof.storage_proof.pop().unwrap(); + proof.storage_proof.push(node.clone()); + proof.storage_proof.push(node); + target::verify_messages_proof::(proof, 10) + },), + Err(Error::HeaderChain(HeaderChainError::StorageProof( + StorageProofError::DuplicateNodesInProof + ))), + ); + } + + #[test] + fn message_proof_is_rejected_if_it_has_unused_trie_nodes() { + assert_eq!( + using_messages_proof(10, None, encode_all_messages, encode_lane_data, |mut proof| { + proof.storage_proof.push(vec![42]); + target::verify_messages_proof::(proof, 10) + },), + Err(Error::StorageProof(StorageProofError::UnusedNodesInTheProof)), + ); + } + + #[test] + fn message_proof_is_rejected_if_required_message_is_missing() { + matches!( + using_messages_proof( + 10, + None, + |n, m| if n != 5 { Some(m.encode()) } else { None }, + encode_lane_data, + |proof| target::verify_messages_proof::(proof, 10) + ), + Err(Error::MessageStorage(StorageProofError::StorageValueEmpty)), + ); + } + + #[test] + fn message_proof_is_rejected_if_message_decode_fails() { + matches!( + using_messages_proof( + 10, + None, + |n, m| { + let mut m = m.encode(); + if n == 5 { + m = vec![42] + } + Some(m) + }, + encode_lane_data, + |proof| target::verify_messages_proof::(proof, 10), + ), + Err(Error::MessageStorage(StorageProofError::StorageValueDecodeFailed(_))), + ); + } + + #[test] + fn message_proof_is_rejected_if_outbound_lane_state_decode_fails() { + matches!( + using_messages_proof( + 10, + Some(OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 1, + latest_generated_nonce: 1, + }), + encode_all_messages, + |d| { + let mut d = d.encode(); + d.truncate(1); + d + }, + |proof| target::verify_messages_proof::(proof, 10), + ), + Err(Error::OutboundLaneStorage(StorageProofError::StorageValueDecodeFailed(_))), + ); + } + + #[test] + fn message_proof_is_rejected_if_it_is_empty() { + assert_eq!( + using_messages_proof(0, None, encode_all_messages, encode_lane_data, |proof| { + target::verify_messages_proof::(proof, 0) + },), + Err(Error::EmptyMessageProof), + ); + } + + #[test] + fn non_empty_message_proof_without_messages_is_accepted() { + assert_eq!( + using_messages_proof( + 0, + Some(OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 1, + latest_generated_nonce: 1, + }), + encode_all_messages, + encode_lane_data, + |proof| target::verify_messages_proof::(proof, 0), + ), + Ok(vec![( + TEST_LANE_ID, + ProvedLaneMessages { + lane_state: Some(OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 1, + latest_generated_nonce: 1, + }), + messages: Vec::new(), + }, + )] + .into_iter() + .collect()), + ); + } + + #[test] + fn non_empty_message_proof_is_accepted() { + assert_eq!( + using_messages_proof( + 1, + Some(OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 1, + latest_generated_nonce: 1, + }), + encode_all_messages, + encode_lane_data, + |proof| target::verify_messages_proof::(proof, 1), + ), + Ok(vec![( + TEST_LANE_ID, + ProvedLaneMessages { + lane_state: Some(OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 1, + latest_generated_nonce: 1, + }), + messages: vec![Message { + key: MessageKey { lane_id: TEST_LANE_ID, nonce: 1 }, + payload: vec![42], + }], + }, + )] + .into_iter() + .collect()), + ); + } + + #[test] + fn verify_messages_proof_does_not_panic_if_messages_count_mismatches() { + assert_eq!( + using_messages_proof(1, None, encode_all_messages, encode_lane_data, |mut proof| { + proof.nonces_end = u64::MAX; + target::verify_messages_proof::(proof, u32::MAX) + },), + Err(Error::MessagesCountMismatch), + ); + } +} diff --git a/bin/runtime-common/src/messages_api.rs b/bin/runtime-common/src/messages_api.rs new file mode 100644 index 00000000000..199e062fe98 --- /dev/null +++ b/bin/runtime-common/src/messages_api.rs @@ -0,0 +1,66 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Helpers for implementing various message-related runtime API mthods. + +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; +use sp_std::vec::Vec; + +/// Implementation of the `To*OutboundLaneApi::message_details`. +pub fn outbound_message_details( + lane: LaneId, + begin: MessageNonce, + end: MessageNonce, +) -> Vec +where + Runtime: pallet_bridge_messages::Config, + MessagesPalletInstance: 'static, +{ + (begin..=end) + .filter_map(|nonce| { + let message_data = + pallet_bridge_messages::Pallet::::outbound_message_data(lane, nonce)?; + Some(OutboundMessageDetails { + nonce, + // dispatch message weight is always zero at the source chain, since we're paying for + // dispatch at the target chain + dispatch_weight: frame_support::weights::Weight::zero(), + size: message_data.len() as _, + }) + }) + .collect() +} + +/// Implementation of the `To*InboundLaneApi::message_details`. +pub fn inbound_message_details( + lane: LaneId, + messages: Vec<(MessagePayload, OutboundMessageDetails)>, +) -> Vec +where + Runtime: pallet_bridge_messages::Config, + MessagesPalletInstance: 'static, +{ + messages + .into_iter() + .map(|(payload, details)| { + pallet_bridge_messages::Pallet::::inbound_message_data( + lane, payload, details, + ) + }) + .collect() +} diff --git a/bin/runtime-common/src/messages_benchmarking.rs b/bin/runtime-common/src/messages_benchmarking.rs new file mode 100644 index 00000000000..b067523c305 --- /dev/null +++ b/bin/runtime-common/src/messages_benchmarking.rs @@ -0,0 +1,293 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything required to run benchmarks of messages module, based on +//! `bridge_runtime_common::messages` implementation. + +#![cfg(feature = "runtime-benchmarks")] + +use crate::{ + messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, + AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, ThisChain, + }, + messages_generation::{ + encode_all_messages, encode_lane_data, grow_trie_leaf_value, prepare_messages_storage_proof, + }, +}; + +use bp_messages::storage_keys; +use bp_polkadot_core::parachains::ParaHash; +use bp_runtime::{ + record_all_trie_keys, Chain, Parachain, RawStorageProof, StorageProofSize, UnderlyingChainOf, +}; +use codec::Encode; +use frame_support::weights::Weight; +use pallet_bridge_messages::benchmarking::{MessageDeliveryProofParams, MessageProofParams}; +use sp_runtime::traits::{Header, Zero}; +use sp_std::prelude::*; +use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; +use xcm::v3::prelude::*; + +/// Prepare inbound bridge message according to given message proof parameters. +fn prepare_inbound_message( + params: &MessageProofParams, + destination: InteriorMultiLocation, +) -> Vec { + // we only care about **this** message size when message proof needs to be `Minimal` + let expected_size = match params.size { + StorageProofSize::Minimal(size) => size as usize, + _ => 0, + }; + + // if we don't need a correct message, then we may just return some random blob + if !params.is_successful_dispatch_expected { + return vec![0u8; expected_size] + } + + // else let's prepare successful message. For XCM bridge hubs, it is the message that + // will be pushed further to some XCM queue (XCMP/UMP) + let location = xcm::VersionedInteriorMultiLocation::V3(destination); + let location_encoded_size = location.encoded_size(); + + // we don't need to be super-precise with `expected_size` here + let xcm_size = expected_size.saturating_sub(location_encoded_size); + let xcm = xcm::VersionedXcm::<()>::V3(vec![Instruction::ClearOrigin; xcm_size].into()); + + // this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor + // or public fields, so just tuple + // (double encoding, because `.encode()` is called on original Xcm BLOB when it is pushed + // to the storage) + (location, xcm).encode().encode() +} + +/// Prepare proof of messages for the `receive_messages_proof` call. +/// +/// In addition to returning valid messages proof, environment is prepared to verify this message +/// proof. +/// +/// This method is intended to be used when benchmarking pallet, linked to the chain that +/// uses GRANDPA finality. For parachains, please use the `prepare_message_proof_from_parachain` +/// function. +pub fn prepare_message_proof_from_grandpa_chain( + params: MessageProofParams, + message_destination: InteriorMultiLocation, +) -> (FromBridgedChainMessagesProof>>, Weight) +where + R: pallet_bridge_grandpa::Config>>, + FI: 'static, + B: MessageBridge, +{ + // prepare storage proof + let (state_root, storage_proof) = prepare_messages_storage_proof::( + params.lane, + params.message_nonces.clone(), + params.outbound_lane_data.clone(), + params.size, + prepare_inbound_message(¶ms, message_destination), + encode_all_messages, + encode_lane_data, + ); + + // update runtime storage + let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); + + ( + FromBridgedChainMessagesProof { + bridged_header_hash, + storage_proof, + lane: params.lane, + nonces_start: *params.message_nonces.start(), + nonces_end: *params.message_nonces.end(), + }, + Weight::MAX / 1000, + ) +} + +/// Prepare proof of messages for the `receive_messages_proof` call. +/// +/// In addition to returning valid messages proof, environment is prepared to verify this message +/// proof. +/// +/// This method is intended to be used when benchmarking pallet, linked to the chain that +/// uses parachain finality. For GRANDPA chains, please use the +/// `prepare_message_proof_from_grandpa_chain` function. +pub fn prepare_message_proof_from_parachain( + params: MessageProofParams, + message_destination: InteriorMultiLocation, +) -> (FromBridgedChainMessagesProof>>, Weight) +where + R: pallet_bridge_parachains::Config, + PI: 'static, + B: MessageBridge, + UnderlyingChainOf>: Chain + Parachain, +{ + // prepare storage proof + let (state_root, storage_proof) = prepare_messages_storage_proof::( + params.lane, + params.message_nonces.clone(), + params.outbound_lane_data.clone(), + params.size, + prepare_inbound_message(¶ms, message_destination), + encode_all_messages, + encode_lane_data, + ); + + // update runtime storage + let (_, bridged_header_hash) = + insert_header_to_parachains_pallet::>>(state_root); + + ( + FromBridgedChainMessagesProof { + bridged_header_hash, + storage_proof, + lane: params.lane, + nonces_start: *params.message_nonces.start(), + nonces_end: *params.message_nonces.end(), + }, + Weight::MAX / 1000, + ) +} + +/// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call. +/// +/// This method is intended to be used when benchmarking pallet, linked to the chain that +/// uses GRANDPA finality. For parachains, please use the +/// `prepare_message_delivery_proof_from_parachain` function. +pub fn prepare_message_delivery_proof_from_grandpa_chain( + params: MessageDeliveryProofParams>>, +) -> FromBridgedChainMessagesDeliveryProof>> +where + R: pallet_bridge_grandpa::Config>>, + FI: 'static, + B: MessageBridge, +{ + // prepare storage proof + let lane = params.lane; + let (state_root, storage_proof) = prepare_message_delivery_proof::(params); + + // update runtime storage + let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); + + FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: bridged_header_hash.into(), + storage_proof, + lane, + } +} + +/// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call. +/// +/// This method is intended to be used when benchmarking pallet, linked to the chain that +/// uses parachain finality. For GRANDPA chains, please use the +/// `prepare_message_delivery_proof_from_grandpa_chain` function. +pub fn prepare_message_delivery_proof_from_parachain( + params: MessageDeliveryProofParams>>, +) -> FromBridgedChainMessagesDeliveryProof>> +where + R: pallet_bridge_parachains::Config, + PI: 'static, + B: MessageBridge, + UnderlyingChainOf>: Chain + Parachain, +{ + // prepare storage proof + let lane = params.lane; + let (state_root, storage_proof) = prepare_message_delivery_proof::(params); + + // update runtime storage + let (_, bridged_header_hash) = + insert_header_to_parachains_pallet::>>(state_root); + + FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: bridged_header_hash.into(), + storage_proof, + lane, + } +} + +/// Prepare in-memory message delivery proof, without inserting anything to the runtime storage. +fn prepare_message_delivery_proof( + params: MessageDeliveryProofParams>>, +) -> (HashOf>, RawStorageProof) +where + B: MessageBridge, +{ + // prepare Bridged chain storage with inbound lane state + let storage_key = + storage_keys::inbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, ¶ms.lane).0; + let mut root = Default::default(); + let mut mdb = MemoryDB::default(); + { + let mut trie = + TrieDBMutBuilderV1::>>::new(&mut mdb, &mut root).build(); + let inbound_lane_data = + grow_trie_leaf_value(params.inbound_lane_data.encode(), params.size); + trie.insert(&storage_key, &inbound_lane_data) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + } + + // generate storage proof to be delivered to This chain + let storage_proof = record_all_trie_keys::>>, _>(&mdb, &root) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); + + (root, storage_proof) +} + +/// Insert header to the bridge GRANDPA pallet. +pub(crate) fn insert_header_to_grandpa_pallet( + state_root: bp_runtime::HashOf, +) -> (bp_runtime::BlockNumberOf, bp_runtime::HashOf) +where + R: pallet_bridge_grandpa::Config, + GI: 'static, + R::BridgedChain: bp_runtime::Chain, +{ + let bridged_block_number = Zero::zero(); + let bridged_header = bp_runtime::HeaderOf::::new( + bridged_block_number, + Default::default(), + state_root, + Default::default(), + Default::default(), + ); + let bridged_header_hash = bridged_header.hash(); + pallet_bridge_grandpa::initialize_for_benchmarks::(bridged_header); + (bridged_block_number, bridged_header_hash) +} + +/// Insert header to the bridge parachains pallet. +pub(crate) fn insert_header_to_parachains_pallet( + state_root: bp_runtime::HashOf, +) -> (bp_runtime::BlockNumberOf, bp_runtime::HashOf) +where + R: pallet_bridge_parachains::Config, + PI: 'static, + PC: Chain + Parachain, +{ + let bridged_block_number = Zero::zero(); + let bridged_header = bp_runtime::HeaderOf::::new( + bridged_block_number, + Default::default(), + state_root, + Default::default(), + Default::default(), + ); + let bridged_header_hash = bridged_header.hash(); + pallet_bridge_parachains::initialize_for_benchmarks::(bridged_header); + (bridged_block_number, bridged_header_hash) +} diff --git a/bin/runtime-common/src/messages_call_ext.rs b/bin/runtime-common/src/messages_call_ext.rs new file mode 100644 index 00000000000..f3665a8d93b --- /dev/null +++ b/bin/runtime-common/src/messages_call_ext.rs @@ -0,0 +1,634 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, +}; +use bp_messages::{InboundLaneData, LaneId, MessageNonce}; +use frame_support::{ + dispatch::CallableCallFor, + traits::{Get, IsSubType}, + RuntimeDebug, +}; +use pallet_bridge_messages::{Config, Pallet}; +use sp_runtime::transaction_validity::TransactionValidity; +use sp_std::ops::RangeInclusive; + +/// Generic info about a messages delivery/confirmation proof. +#[derive(PartialEq, RuntimeDebug)] +pub struct BaseMessagesProofInfo { + /// Message lane, used by the call. + pub lane_id: LaneId, + /// Nonces of messages, included in the call. + /// + /// For delivery transaction, it is nonces of bundled messages. For confirmation + /// transaction, it is nonces that are to be confirmed during the call. + pub bundled_range: RangeInclusive, + /// Nonce of the best message, stored by this chain before the call is dispatched. + /// + /// For delivery transaction, it is the nonce of best delivered message before the call. + /// For confirmation transaction, it is the nonce of best confirmed message before the call. + pub best_stored_nonce: MessageNonce, +} + +impl BaseMessagesProofInfo { + /// Returns true if `bundled_range` continues the `0..=best_stored_nonce` range. + fn appends_to_stored_nonce(&self) -> bool { + *self.bundled_range.start() == self.best_stored_nonce + 1 + } +} + +/// Occupation state of the unrewarded relayers vector. +#[derive(PartialEq, RuntimeDebug)] +#[cfg_attr(test, derive(Default))] +pub struct UnrewardedRelayerOccupation { + /// The number of remaining unoccupied entries for new relayers. + pub free_relayer_slots: MessageNonce, + /// The number of messages that we are ready to accept. + pub free_message_slots: MessageNonce, +} + +/// Info about a `ReceiveMessagesProof` call which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub struct ReceiveMessagesProofInfo { + /// Base messages proof info + pub base: BaseMessagesProofInfo, + /// State of unrewarded relayers vector. + pub unrewarded_relayers: UnrewardedRelayerOccupation, +} + +impl ReceiveMessagesProofInfo { + /// Returns true if: + /// + /// - either inbound lane is ready to accept bundled messages; + /// + /// - or there are no bundled messages, but the inbound lane is blocked by too many unconfirmed + /// messages and/or unrewarded relayers. + fn is_obsolete(&self) -> bool { + // transactions with zero bundled nonces are not allowed, unless they're message + // delivery transactions, which brings reward confirmations required to unblock + // the lane + if self.base.bundled_range.is_empty() { + let empty_transactions_allowed = + // we allow empty transactions when we can't accept delivery from new relayers + self.unrewarded_relayers.free_relayer_slots == 0 || + // or if we can't accept new messages at all + self.unrewarded_relayers.free_message_slots == 0; + + return !empty_transactions_allowed + } + + // otherwise we require bundled messages to continue stored range + !self.base.appends_to_stored_nonce() + } +} + +/// Info about a `ReceiveMessagesDeliveryProof` call which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub struct ReceiveMessagesDeliveryProofInfo(pub BaseMessagesProofInfo); + +impl ReceiveMessagesDeliveryProofInfo { + /// Returns true if outbound lane is ready to accept confirmations of bundled messages. + fn is_obsolete(&self) -> bool { + self.0.bundled_range.is_empty() || !self.0.appends_to_stored_nonce() + } +} + +/// Info about a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call +/// which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub enum CallInfo { + ReceiveMessagesProof(ReceiveMessagesProofInfo), + ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo), +} + +/// Helper struct that provides methods for working with a call supported by `CallInfo`. +pub struct CallHelper, I: 'static> { + pub _phantom_data: sp_std::marker::PhantomData<(T, I)>, +} + +impl, I: 'static> CallHelper { + /// Returns true if: + /// + /// - call is `receive_messages_proof` and all messages have been delivered; + /// + /// - call is `receive_messages_delivery_proof` and all messages confirmations have been + /// received. + pub fn was_successful(info: &CallInfo) -> bool { + match info { + CallInfo::ReceiveMessagesProof(info) => { + let inbound_lane_data = + pallet_bridge_messages::InboundLanes::::get(info.base.lane_id); + if info.base.bundled_range.is_empty() { + let post_occupation = + unrewarded_relayers_occupation::(&inbound_lane_data); + // we don't care about `free_relayer_slots` here - it is checked in + // `is_obsolete` and every relayer has delivered at least one message, + // so if relayer slots are released, then message slots are also + // released + return post_occupation.free_message_slots > + info.unrewarded_relayers.free_message_slots + } + + inbound_lane_data.last_delivered_nonce() == *info.base.bundled_range.end() + }, + CallInfo::ReceiveMessagesDeliveryProof(info) => { + let outbound_lane_data = + pallet_bridge_messages::OutboundLanes::::get(info.0.lane_id); + outbound_lane_data.latest_received_nonce == *info.0.bundled_range.end() + }, + } + } +} + +/// Trait representing a call that is a sub type of `pallet_bridge_messages::Call`. +pub trait MessagesCallSubType, I: 'static>: + IsSubType, T>> +{ + /// Create a new instance of `ReceiveMessagesProofInfo` from a `ReceiveMessagesProof` call. + fn receive_messages_proof_info(&self) -> Option; + + /// Create a new instance of `ReceiveMessagesDeliveryProofInfo` from + /// a `ReceiveMessagesDeliveryProof` call. + fn receive_messages_delivery_proof_info(&self) -> Option; + + /// Create a new instance of `CallInfo` from a `ReceiveMessagesProof` + /// or a `ReceiveMessagesDeliveryProof` call. + fn call_info(&self) -> Option; + + /// Create a new instance of `CallInfo` from a `ReceiveMessagesProof` + /// or a `ReceiveMessagesDeliveryProof` call, if the call is for the provided lane. + fn call_info_for(&self, lane_id: LaneId) -> Option; + + /// Check that a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call is trying + /// to deliver/confirm at least some messages that are better than the ones we know of. + fn check_obsolete_call(&self) -> TransactionValidity; +} + +impl< + BridgedHeaderHash, + SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain< + MessagesProof = FromBridgedChainMessagesProof, + >, + TargetHeaderChain: bp_messages::source_chain::TargetHeaderChain< + >::OutboundPayload, + ::AccountId, + MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof, + >, + Call: IsSubType, T>>, + T: frame_system::Config + + Config, + I: 'static, + > MessagesCallSubType for T::RuntimeCall +{ + fn receive_messages_proof_info(&self) -> Option { + if let Some(pallet_bridge_messages::Call::::receive_messages_proof { + ref proof, + .. + }) = self.is_sub_type() + { + let inbound_lane_data = pallet_bridge_messages::InboundLanes::::get(proof.lane); + + return Some(ReceiveMessagesProofInfo { + base: BaseMessagesProofInfo { + lane_id: proof.lane, + // we want all messages in this range to be new for us. Otherwise transaction + // will be considered obsolete. + bundled_range: proof.nonces_start..=proof.nonces_end, + best_stored_nonce: inbound_lane_data.last_delivered_nonce(), + }, + unrewarded_relayers: unrewarded_relayers_occupation::(&inbound_lane_data), + }) + } + + None + } + + fn receive_messages_delivery_proof_info(&self) -> Option { + if let Some(pallet_bridge_messages::Call::::receive_messages_delivery_proof { + ref proof, + ref relayers_state, + .. + }) = self.is_sub_type() + { + let outbound_lane_data = pallet_bridge_messages::OutboundLanes::::get(proof.lane); + + return Some(ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { + lane_id: proof.lane, + // there's a time frame between message delivery, message confirmation and reward + // confirmation. Because of that, we can't assume that our state has been confirmed + // to the bridged chain. So we are accepting any proof that brings new + // confirmations. + bundled_range: outbound_lane_data.latest_received_nonce + 1..= + relayers_state.last_delivered_nonce, + best_stored_nonce: outbound_lane_data.latest_received_nonce, + })) + } + + None + } + + fn call_info(&self) -> Option { + if let Some(info) = self.receive_messages_proof_info() { + return Some(CallInfo::ReceiveMessagesProof(info)) + } + + if let Some(info) = self.receive_messages_delivery_proof_info() { + return Some(CallInfo::ReceiveMessagesDeliveryProof(info)) + } + + None + } + + fn call_info_for(&self, lane_id: LaneId) -> Option { + self.call_info().filter(|info| { + let actual_lane_id = match info { + CallInfo::ReceiveMessagesProof(info) => info.base.lane_id, + CallInfo::ReceiveMessagesDeliveryProof(info) => info.0.lane_id, + }; + actual_lane_id == lane_id + }) + } + + fn check_obsolete_call(&self) -> TransactionValidity { + match self.call_info() { + Some(CallInfo::ReceiveMessagesProof(proof_info)) if proof_info.is_obsolete() => { + log::trace!( + target: pallet_bridge_messages::LOG_TARGET, + "Rejecting obsolete messages delivery transaction: {:?}", + proof_info + ); + + return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() + }, + Some(CallInfo::ReceiveMessagesDeliveryProof(proof_info)) + if proof_info.is_obsolete() => + { + log::trace!( + target: pallet_bridge_messages::LOG_TARGET, + "Rejecting obsolete messages confirmation transaction: {:?}", + proof_info, + ); + + return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() + }, + _ => {}, + } + + Ok(sp_runtime::transaction_validity::ValidTransaction::default()) + } +} + +/// Returns occupation state of unrewarded relayers vector. +fn unrewarded_relayers_occupation, I: 'static>( + inbound_lane_data: &InboundLaneData, +) -> UnrewardedRelayerOccupation { + UnrewardedRelayerOccupation { + free_relayer_slots: T::MaxUnrewardedRelayerEntriesAtInboundLane::get() + .saturating_sub(inbound_lane_data.relayers.len() as MessageNonce), + free_message_slots: { + let unconfirmed_messages = inbound_lane_data + .last_delivered_nonce() + .saturating_sub(inbound_lane_data.last_confirmed_nonce); + T::MaxUnconfirmedMessagesAtInboundLane::get().saturating_sub(unconfirmed_messages) + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, + }, + messages_call_ext::MessagesCallSubType, + mock::{ + MaxUnconfirmedMessagesAtInboundLane, MaxUnrewardedRelayerEntriesAtInboundLane, + TestRuntime, ThisChainRuntimeCall, + }, + }; + use bp_messages::{DeliveredMessages, UnrewardedRelayer, UnrewardedRelayersState}; + use sp_std::ops::RangeInclusive; + + fn fill_unrewarded_relayers() { + let mut inbound_lane_state = + pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); + for n in 0..MaxUnrewardedRelayerEntriesAtInboundLane::get() { + inbound_lane_state.relayers.push_back(UnrewardedRelayer { + relayer: Default::default(), + messages: DeliveredMessages { begin: n + 1, end: n + 1 }, + }); + } + pallet_bridge_messages::InboundLanes::::insert( + LaneId([0, 0, 0, 0]), + inbound_lane_state, + ); + } + + fn fill_unrewarded_messages() { + let mut inbound_lane_state = + pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); + inbound_lane_state.relayers.push_back(UnrewardedRelayer { + relayer: Default::default(), + messages: DeliveredMessages { + begin: 1, + end: MaxUnconfirmedMessagesAtInboundLane::get(), + }, + }); + pallet_bridge_messages::InboundLanes::::insert( + LaneId([0, 0, 0, 0]), + inbound_lane_state, + ); + } + + fn deliver_message_10() { + pallet_bridge_messages::InboundLanes::::insert( + LaneId([0, 0, 0, 0]), + bp_messages::InboundLaneData { relayers: Default::default(), last_confirmed_nonce: 10 }, + ); + } + + fn validate_message_delivery( + nonces_start: bp_messages::MessageNonce, + nonces_end: bp_messages::MessageNonce, + ) -> bool { + ThisChainRuntimeCall::BridgeMessages( + pallet_bridge_messages::Call::::receive_messages_proof { + relayer_id_at_bridged_chain: 42, + messages_count: nonces_end.checked_sub(nonces_start).map(|x| x + 1).unwrap_or(0) + as u32, + dispatch_weight: frame_support::weights::Weight::zero(), + proof: FromBridgedChainMessagesProof { + bridged_header_hash: Default::default(), + storage_proof: vec![], + lane: LaneId([0, 0, 0, 0]), + nonces_start, + nonces_end, + }, + }, + ) + .check_obsolete_call() + .is_ok() + } + + #[test] + fn extension_rejects_obsolete_messages() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best delivered is message#10 and we're trying to deliver messages 8..=9 + // => tx is rejected + deliver_message_10(); + assert!(!validate_message_delivery(8, 9)); + }); + } + + #[test] + fn extension_rejects_same_message() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best delivered is message#10 and we're trying to import messages 10..=10 + // => tx is rejected + deliver_message_10(); + assert!(!validate_message_delivery(8, 10)); + }); + } + + #[test] + fn extension_rejects_call_with_some_obsolete_messages() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best delivered is message#10 and we're trying to deliver messages + // 10..=15 => tx is rejected + deliver_message_10(); + assert!(!validate_message_delivery(10, 15)); + }); + } + + #[test] + fn extension_rejects_call_with_future_messages() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best delivered is message#10 and we're trying to deliver messages + // 13..=15 => tx is rejected + deliver_message_10(); + assert!(!validate_message_delivery(13, 15)); + }); + } + + #[test] + fn extension_rejects_empty_delivery_with_rewards_confirmations_if_there_are_free_relayer_and_message_slots( + ) { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + deliver_message_10(); + assert!(!validate_message_delivery(10, 9)); + }); + } + + #[test] + fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_relayer_slots( + ) { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + deliver_message_10(); + fill_unrewarded_relayers(); + assert!(validate_message_delivery(10, 9)); + }); + } + + #[test] + fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_message_slots( + ) { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + fill_unrewarded_messages(); + assert!(validate_message_delivery( + MaxUnconfirmedMessagesAtInboundLane::get(), + MaxUnconfirmedMessagesAtInboundLane::get() - 1 + )); + }); + } + + #[test] + fn extension_accepts_new_messages() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best delivered is message#10 and we're trying to deliver message 11..=15 + // => tx is accepted + deliver_message_10(); + assert!(validate_message_delivery(11, 15)); + }); + } + + fn confirm_message_10() { + pallet_bridge_messages::OutboundLanes::::insert( + LaneId([0, 0, 0, 0]), + bp_messages::OutboundLaneData { + oldest_unpruned_nonce: 0, + latest_received_nonce: 10, + latest_generated_nonce: 10, + }, + ); + } + + fn validate_message_confirmation(last_delivered_nonce: bp_messages::MessageNonce) -> bool { + ThisChainRuntimeCall::BridgeMessages( + pallet_bridge_messages::Call::::receive_messages_delivery_proof { + proof: FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: Default::default(), + storage_proof: Vec::new(), + lane: LaneId([0, 0, 0, 0]), + }, + relayers_state: UnrewardedRelayersState { + last_delivered_nonce, + ..Default::default() + }, + }, + ) + .check_obsolete_call() + .is_ok() + } + + #[test] + fn extension_rejects_obsolete_confirmations() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best confirmed is message#10 and we're trying to confirm message#5 => tx + // is rejected + confirm_message_10(); + assert!(!validate_message_confirmation(5)); + }); + } + + #[test] + fn extension_rejects_same_confirmation() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best confirmed is message#10 and we're trying to confirm message#10 => + // tx is rejected + confirm_message_10(); + assert!(!validate_message_confirmation(10)); + }); + } + + #[test] + fn extension_rejects_empty_confirmation_even_if_there_are_no_free_unrewarded_entries() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + confirm_message_10(); + fill_unrewarded_relayers(); + assert!(!validate_message_confirmation(10)); + }); + } + + #[test] + fn extension_accepts_new_confirmation() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + // when current best confirmed is message#10 and we're trying to confirm message#15 => + // tx is accepted + confirm_message_10(); + assert!(validate_message_confirmation(15)); + }); + } + + fn was_message_delivery_successful( + bundled_range: RangeInclusive, + is_empty: bool, + ) -> bool { + CallHelper::::was_successful(&CallInfo::ReceiveMessagesProof( + ReceiveMessagesProofInfo { + base: BaseMessagesProofInfo { + lane_id: LaneId([0, 0, 0, 0]), + bundled_range, + best_stored_nonce: 0, // doesn't matter for `was_successful` + }, + unrewarded_relayers: UnrewardedRelayerOccupation { + free_relayer_slots: 0, // doesn't matter for `was_successful` + free_message_slots: if is_empty { + 0 + } else { + MaxUnconfirmedMessagesAtInboundLane::get() + }, + }, + }, + )) + } + + #[test] + #[allow(clippy::reversed_empty_ranges)] + fn was_successful_returns_false_for_failed_reward_confirmation_transaction() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + fill_unrewarded_messages(); + assert!(!was_message_delivery_successful(10..=9, true)); + }); + } + + #[test] + #[allow(clippy::reversed_empty_ranges)] + fn was_successful_returns_true_for_successful_reward_confirmation_transaction() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + assert!(was_message_delivery_successful(10..=9, true)); + }); + } + + #[test] + fn was_successful_returns_false_for_failed_delivery() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + deliver_message_10(); + assert!(!was_message_delivery_successful(10..=12, false)); + }); + } + + #[test] + fn was_successful_returns_false_for_partially_successful_delivery() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + deliver_message_10(); + assert!(!was_message_delivery_successful(9..=12, false)); + }); + } + + #[test] + fn was_successful_returns_true_for_successful_delivery() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + deliver_message_10(); + assert!(was_message_delivery_successful(9..=10, false)); + }); + } + + fn was_message_confirmation_successful(bundled_range: RangeInclusive) -> bool { + CallHelper::::was_successful(&CallInfo::ReceiveMessagesDeliveryProof( + ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { + lane_id: LaneId([0, 0, 0, 0]), + bundled_range, + best_stored_nonce: 0, // doesn't matter for `was_successful` + }), + )) + } + + #[test] + fn was_successful_returns_false_for_failed_confirmation() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + confirm_message_10(); + assert!(!was_message_confirmation_successful(10..=12)); + }); + } + + #[test] + fn was_successful_returns_false_for_partially_successful_confirmation() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + confirm_message_10(); + assert!(!was_message_confirmation_successful(9..=12)); + }); + } + + #[test] + fn was_successful_returns_true_for_successful_confirmation() { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + confirm_message_10(); + assert!(was_message_confirmation_successful(9..=10)); + }); + } +} diff --git a/bin/runtime-common/src/messages_generation.rs b/bin/runtime-common/src/messages_generation.rs new file mode 100644 index 00000000000..29a869a5c87 --- /dev/null +++ b/bin/runtime-common/src/messages_generation.rs @@ -0,0 +1,119 @@ +// Copyright 2019-2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Helpers for generating message storage proofs, that are used by tests and by benchmarks. + +#![cfg(any(feature = "runtime-benchmarks", test))] + +use crate::messages::{BridgedChain, HashOf, HasherOf, MessageBridge}; + +use bp_messages::{ + storage_keys, LaneId, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, +}; +use bp_runtime::{record_all_trie_keys, RawStorageProof, StorageProofSize}; +use codec::Encode; +use sp_std::{ops::RangeInclusive, prelude::*}; +use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; + +/// Simple and correct message data encode function. +pub(crate) fn encode_all_messages(_: MessageNonce, m: &MessagePayload) -> Option> { + Some(m.encode()) +} + +/// Simple and correct outbound lane data encode function. +pub(crate) fn encode_lane_data(d: &OutboundLaneData) -> Vec { + d.encode() +} + +/// Prepare storage proof of given messages. +/// +/// Returns state trie root and nodes with prepared messages. +pub(crate) fn prepare_messages_storage_proof( + lane: LaneId, + message_nonces: RangeInclusive, + outbound_lane_data: Option, + size: StorageProofSize, + message_payload: MessagePayload, + encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option>, + encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec, +) -> (HashOf>, RawStorageProof) +where + B: MessageBridge, + HashOf>: Copy + Default, +{ + // prepare Bridged chain storage with messages and (optionally) outbound lane state + let message_count = message_nonces.end().saturating_sub(*message_nonces.start()) + 1; + let mut storage_keys = Vec::with_capacity(message_count as usize + 1); + let mut root = Default::default(); + let mut mdb = MemoryDB::default(); + { + let mut trie = + TrieDBMutBuilderV1::>>::new(&mut mdb, &mut root).build(); + + // insert messages + for (i, nonce) in message_nonces.into_iter().enumerate() { + let message_key = MessageKey { lane_id: lane, nonce }; + let message_payload = match encode_message(nonce, &message_payload) { + Some(message_payload) => + if i == 0 { + grow_trie_leaf_value(message_payload, size) + } else { + message_payload + }, + None => continue, + }; + let storage_key = storage_keys::message_key( + B::BRIDGED_MESSAGES_PALLET_NAME, + &message_key.lane_id, + message_key.nonce, + ) + .0; + trie.insert(&storage_key, &message_payload) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + storage_keys.push(storage_key); + } + + // insert outbound lane state + if let Some(outbound_lane_data) = outbound_lane_data.as_ref().map(encode_outbound_lane_data) + { + let storage_key = + storage_keys::outbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane).0; + trie.insert(&storage_key, &outbound_lane_data) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + storage_keys.push(storage_key); + } + } + + // generate storage proof to be delivered to This chain + let storage_proof = record_all_trie_keys::>>, _>(&mdb, &root) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); + (root, storage_proof) +} + +/// Add extra data to the trie leaf value so that it'll be of given size. +pub fn grow_trie_leaf_value(mut value: Vec, size: StorageProofSize) -> Vec { + match size { + StorageProofSize::Minimal(_) => (), + StorageProofSize::HasLargeLeaf(size) if size as usize > value.len() => { + value.extend(sp_std::iter::repeat(42u8).take(size as usize - value.len())); + }, + StorageProofSize::HasLargeLeaf(_) => (), + } + value +} diff --git a/bin/runtime-common/src/messages_xcm_extension.rs b/bin/runtime-common/src/messages_xcm_extension.rs new file mode 100644 index 00000000000..4ccdd7a4b4d --- /dev/null +++ b/bin/runtime-common/src/messages_xcm_extension.rs @@ -0,0 +1,164 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Module provides utilities for easier XCM handling, e.g: +//! `XcmExecutor` -> `MessageSender` -> `OutboundMessageQueue` +//! | +//! `Relayer` +//! | +//! `XcmRouter` <- `MessageDispatch` <- `InboundMessageQueue` + +use bp_messages::{ + source_chain::MessagesBridge, + target_chain::{DispatchMessage, MessageDispatch}, + LaneId, +}; +use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, Chain}; +use codec::{Decode, Encode}; +use frame_support::{dispatch::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; +use pallet_bridge_messages::WeightInfoExt as MessagesPalletWeights; +use scale_info::TypeInfo; +use sp_runtime::SaturatedConversion; +use xcm_builder::{DispatchBlob, DispatchBlobError, HaulBlob, HaulBlobError}; + +/// Plain "XCM" payload, which we transfer through bridge +pub type XcmAsPlainPayload = sp_std::prelude::Vec; + +/// Message dispatch result type for single message +#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] +pub enum XcmBlobMessageDispatchResult { + InvalidPayload, + Dispatched, + NotDispatched(#[codec(skip)] Option), +} + +/// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages +pub struct XcmBlobMessageDispatch +{ + _marker: sp_std::marker::PhantomData<( + SourceBridgeHubChain, + TargetBridgeHubChain, + DispatchBlob, + Weights, + )>, +} + +impl< + SourceBridgeHubChain: Chain, + TargetBridgeHubChain: Chain, + BlobDispatcher: DispatchBlob, + Weights: MessagesPalletWeights, + > MessageDispatch> + for XcmBlobMessageDispatch +{ + type DispatchPayload = XcmAsPlainPayload; + type DispatchLevelResult = XcmBlobMessageDispatchResult; + + fn dispatch_weight(message: &mut DispatchMessage) -> Weight { + match message.data.payload { + Ok(ref payload) => { + let payload_size = payload.encoded_size().saturated_into(); + Weights::message_dispatch_weight(payload_size) + }, + Err(_) => Weight::zero(), + } + } + + fn dispatch( + _relayer_account: &AccountIdOf, + message: DispatchMessage, + ) -> MessageDispatchResult { + let payload = match message.data.payload { + Ok(payload) => payload, + Err(e) => { + log::error!( + target: crate::LOG_TARGET_BRIDGE_DISPATCH, + "[XcmBlobMessageDispatch] payload error: {:?} - message_nonce: {:?}", + e, + message.key.nonce + ); + return MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, + } + }, + }; + let dispatch_level_result = match BlobDispatcher::dispatch_blob(payload) { + Ok(_) => { + log::debug!( + target: crate::LOG_TARGET_BRIDGE_DISPATCH, + "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}", + message.key.nonce + ); + XcmBlobMessageDispatchResult::Dispatched + }, + Err(e) => { + log::error!( + target: crate::LOG_TARGET_BRIDGE_DISPATCH, + "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?} - message_nonce: {:?}", + e, message.key.nonce + ); + XcmBlobMessageDispatchResult::NotDispatched(Some(e)) + }, + }; + MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result } + } +} + +/// [`XcmBlobHauler`] is responsible for sending messages to the bridge "point-to-point link" from +/// one side, where on the other it can be dispatched by [`XcmBlobMessageDispatch`]. +pub trait XcmBlobHauler { + /// Runtime message sender adapter. + type MessageSender: MessagesBridge; + + /// Runtime message sender origin, which is used by [`Self::MessageSender`]. + type MessageSenderOrigin; + /// Our location within the Consensus Universe. + fn message_sender_origin() -> Self::MessageSenderOrigin; + + /// Return message lane (as "point-to-point link") used to deliver XCM messages. + fn xcm_lane() -> LaneId; +} + +/// XCM bridge adapter which connects [`XcmBlobHauler`] with [`XcmBlobHauler::MessageSender`] and +/// makes sure that XCM blob is sent to the [`pallet_bridge_messages`] queue to be relayed. +pub struct XcmBlobHaulerAdapter(sp_std::marker::PhantomData); +impl> HaulBlob + for XcmBlobHaulerAdapter +{ + fn haul_blob(blob: sp_std::prelude::Vec) -> Result<(), HaulBlobError> { + let lane = H::xcm_lane(); + H::MessageSender::send_message(H::message_sender_origin(), lane, blob) + .map(|artifacts| (lane, artifacts.nonce).using_encoded(sp_io::hashing::blake2_256)) + .map(|result| { + log::info!( + target: crate::LOG_TARGET_BRIDGE_DISPATCH, + "haul_blob result - ok: {:?} on lane: {:?}", + result, + lane + ) + }) + .map_err(|error| { + log::error!( + target: crate::LOG_TARGET_BRIDGE_DISPATCH, + "haul_blob result - error: {:?} on lane: {:?}", + error, + lane + ); + HaulBlobError::Transport("MessageSenderError") + }) + } +} diff --git a/bin/runtime-common/src/mock.rs b/bin/runtime-common/src/mock.rs new file mode 100644 index 00000000000..036813f6fd5 --- /dev/null +++ b/bin/runtime-common/src/mock.rs @@ -0,0 +1,402 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! A mock runtime for testing different stuff in the crate. We've been using Millau +//! runtime for that before, but it has two drawbacks: +//! +//! - circular dependencies between this crate and Millau runtime; +//! +//! - we can't use (e.g. as git subtree or by copying) this crate in repo without Millau. + +#![cfg(test)] + +use crate::messages::{ + source::{ + FromThisChainMaximalOutboundPayloadSize, FromThisChainMessagePayload, + FromThisChainMessageVerifier, TargetHeaderChainAdapter, + }, + target::{FromBridgedChainMessagePayload, SourceHeaderChainAdapter}, + BridgedChainWithMessages, HashOf, MessageBridge, ThisChainWithMessages, +}; + +use bp_header_chain::{ChainWithGrandpa, HeaderChain}; +use bp_messages::{target_chain::ForbidInboundMessages, LaneId, MessageNonce}; +use bp_parachains::SingleParaStoredHeaderDataBuilder; +use bp_runtime::{Chain, ChainId, Parachain, UnderlyingChainProvider}; +use codec::{Decode, Encode}; +use frame_support::{ + parameter_types, + weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight}, +}; +use pallet_transaction_payment::Multiplier; +use sp_runtime::{ + testing::H256, + traits::{BlakeTwo256, ConstU32, ConstU64, ConstU8, IdentityLookup}, + FixedPointNumber, Perquintill, +}; + +/// Account identifier at `ThisChain`. +pub type ThisChainAccountId = u64; +/// Balance at `ThisChain`. +pub type ThisChainBalance = u64; +/// Block number at `ThisChain`. +pub type ThisChainBlockNumber = u32; +/// Hash at `ThisChain`. +pub type ThisChainHash = H256; +/// Hasher at `ThisChain`. +pub type ThisChainHasher = BlakeTwo256; +/// Runtime call at `ThisChain`. +pub type ThisChainRuntimeCall = RuntimeCall; +/// Runtime call origin at `ThisChain`. +pub type ThisChainCallOrigin = RuntimeOrigin; +/// Header of `ThisChain`. +pub type ThisChainHeader = sp_runtime::generic::Header; +/// Block of `ThisChain`. +pub type ThisChainBlock = frame_system::mocking::MockBlock; +/// Unchecked extrinsic of `ThisChain`. +pub type ThisChainUncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + +/// Account identifier at the `BridgedChain`. +pub type BridgedChainAccountId = u128; +/// Balance at the `BridgedChain`. +pub type BridgedChainBalance = u128; +/// Block number at the `BridgedChain`. +pub type BridgedChainBlockNumber = u32; +/// Hash at the `BridgedChain`. +pub type BridgedChainHash = H256; +/// Hasher at the `BridgedChain`. +pub type BridgedChainHasher = BlakeTwo256; +/// Header of the `BridgedChain`. +pub type BridgedChainHeader = + sp_runtime::generic::Header; + +/// Message lane used in tests. +pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]); +/// Bridged chain id used in tests. +pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg"; +/// Maximal extrinsic weight at the `BridgedChain`. +pub const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: usize = 2048; +/// Maximal extrinsic size at the `BridgedChain`. +pub const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024; + +frame_support::construct_runtime! { + pub enum TestRuntime where + Block = ThisChainBlock, + NodeBlock = ThisChainBlock, + UncheckedExtrinsic = ThisChainUncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Utility: pallet_utility, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, + BridgeGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event}, + BridgeParachains: pallet_bridge_parachains::{Pallet, Call, Storage, Event}, + BridgeMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, + } +} + +crate::generate_bridge_reject_obsolete_headers_and_messages! { + ThisChainRuntimeCall, ThisChainAccountId, + BridgeGrandpa, BridgeParachains, BridgeMessages +} + +parameter_types! { + pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID]; + pub const BridgedChainId: ChainId = TEST_BRIDGED_CHAIN_ID; + pub const BridgedParasPalletName: &'static str = "Paras"; + pub const ExistentialDeposit: ThisChainBalance = 500; + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); + pub const TransactionBaseFee: ThisChainBalance = 0; + pub const TransactionByteFee: ThisChainBalance = 1; + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); + pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value(); + pub const MaxUnrewardedRelayerEntriesAtInboundLane: MessageNonce = 16; + pub const MaxUnconfirmedMessagesAtInboundLane: MessageNonce = 1_000; +} + +impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = ThisChainBlockNumber; + type Hash = ThisChainHash; + type Hashing = ThisChainHasher; + type AccountId = ThisChainAccountId; + type Lookup = IdentityLookup; + type Header = ThisChainHeader; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU32<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type DbWeight = DbWeight; + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_utility::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + +impl pallet_balances::Config for TestRuntime { + type Balance = ThisChainBalance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = ConstU32<50>; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; +} + +impl pallet_transaction_payment::Config for TestRuntime { + type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = IdentityFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< + TestRuntime, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + MaximumMultiplier, + >; + type RuntimeEvent = RuntimeEvent; +} + +impl pallet_bridge_grandpa::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type BridgedChain = BridgedUnderlyingChain; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; + type HeadersToKeep = ConstU32<8>; + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; +} + +impl pallet_bridge_parachains::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type BridgesGrandpaPalletInstance = (); + type ParasPalletName = BridgedParasPalletName; + type ParaStoredHeaderDataBuilder = + SingleParaStoredHeaderDataBuilder; + type HeadsToKeep = ConstU32<8>; + type MaxParaHeadDataSize = ConstU32<1024>; + type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; +} + +impl pallet_bridge_messages::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + type ActiveOutboundLanes = ActiveOutboundLanes; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type MaximalOutboundPayloadSize = FromThisChainMaximalOutboundPayloadSize; + type OutboundPayload = FromThisChainMessagePayload; + + type InboundPayload = FromBridgedChainMessagePayload; + type InboundRelayer = BridgedChainAccountId; + type DeliveryPayments = (); + + type TargetHeaderChain = TargetHeaderChainAdapter; + type LaneMessageVerifier = FromThisChainMessageVerifier; + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + TestRuntime, + (), + ConstU64<100_000>, + >; + + type SourceHeaderChain = SourceHeaderChainAdapter; + type MessageDispatch = ForbidInboundMessages<(), FromBridgedChainMessagePayload>; + type BridgedChainId = BridgedChainId; +} + +impl pallet_bridge_relayers::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type Reward = ThisChainBalance; + type PaymentProcedure = (); + type WeightInfo = (); +} + +/// Bridge that is deployed on `ThisChain` and allows sending/receiving messages to/from +/// `BridgedChain`. +#[derive(Debug, PartialEq, Eq)] +pub struct OnThisChainBridge; + +impl MessageBridge for OnThisChainBridge { + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; + + type ThisChain = ThisChain; + type BridgedChain = BridgedChain; + type BridgedHeaderChain = pallet_bridge_grandpa::GrandpaChainHeaders; +} + +/// Bridge that is deployed on `BridgedChain` and allows sending/receiving messages to/from +/// `ThisChain`. +#[derive(Debug, PartialEq, Eq)] +pub struct OnBridgedChainBridge; + +impl MessageBridge for OnBridgedChainBridge { + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; + + type ThisChain = BridgedChain; + type BridgedChain = ThisChain; + type BridgedHeaderChain = ThisHeaderChain; +} + +/// Dummy implementation of `HeaderChain` for `ThisChain` at the `BridgedChain`. +pub struct ThisHeaderChain; + +impl HeaderChain for ThisHeaderChain { + fn finalized_header_state_root(_hash: HashOf) -> Option> { + unreachable!() + } +} + +/// Call origin at `BridgedChain`. +#[derive(Clone, Debug)] +pub struct BridgedChainOrigin; + +impl From + for Result, BridgedChainOrigin> +{ + fn from( + _origin: BridgedChainOrigin, + ) -> Result, BridgedChainOrigin> { + unreachable!() + } +} + +/// Underlying chain of `ThisChain`. +pub struct ThisUnderlyingChain; + +impl Chain for ThisUnderlyingChain { + type BlockNumber = ThisChainBlockNumber; + type Hash = ThisChainHash; + type Hasher = ThisChainHasher; + type Header = ThisChainHeader; + type AccountId = ThisChainAccountId; + type Balance = ThisChainBalance; + type Index = u32; + type Signature = sp_runtime::MultiSignature; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +/// The chain where we are in tests. +pub struct ThisChain; + +impl UnderlyingChainProvider for ThisChain { + type Chain = ThisUnderlyingChain; +} + +impl ThisChainWithMessages for ThisChain { + type RuntimeOrigin = ThisChainCallOrigin; +} + +impl BridgedChainWithMessages for ThisChain {} + +/// Underlying chain of `BridgedChain`. +pub struct BridgedUnderlyingChain; +/// Some parachain under `BridgedChain` consensus. +pub struct BridgedUnderlyingParachain; +/// Runtime call of the `BridgedChain`. +#[derive(Decode, Encode)] +pub struct BridgedChainCall; + +impl Chain for BridgedUnderlyingChain { + type BlockNumber = BridgedChainBlockNumber; + type Hash = BridgedChainHash; + type Hasher = BridgedChainHasher; + type Header = BridgedChainHeader; + type AccountId = BridgedChainAccountId; + type Balance = BridgedChainBalance; + type Index = u32; + type Signature = sp_runtime::MultiSignature; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl ChainWithGrandpa for BridgedUnderlyingChain { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; + const MAX_AUTHORITIES_COUNT: u32 = 16; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + const MAX_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64; +} + +impl Chain for BridgedUnderlyingParachain { + type BlockNumber = BridgedChainBlockNumber; + type Hash = BridgedChainHash; + type Hasher = BridgedChainHasher; + type Header = BridgedChainHeader; + type AccountId = BridgedChainAccountId; + type Balance = BridgedChainBalance; + type Index = u32; + type Signature = sp_runtime::MultiSignature; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl Parachain for BridgedUnderlyingParachain { + const PARACHAIN_ID: u32 = 42; +} + +/// The other, bridged chain, used in tests. +pub struct BridgedChain; + +impl UnderlyingChainProvider for BridgedChain { + type Chain = BridgedUnderlyingChain; +} + +impl ThisChainWithMessages for BridgedChain { + type RuntimeOrigin = BridgedChainOrigin; +} + +impl BridgedChainWithMessages for BridgedChain {} diff --git a/bin/runtime-common/src/parachains_benchmarking.rs b/bin/runtime-common/src/parachains_benchmarking.rs new file mode 100644 index 00000000000..aad53673c3a --- /dev/null +++ b/bin/runtime-common/src/parachains_benchmarking.rs @@ -0,0 +1,88 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything required to run benchmarks of parachains finality module. + +#![cfg(feature = "runtime-benchmarks")] + +use crate::{ + messages_benchmarking::insert_header_to_grandpa_pallet, + messages_generation::grow_trie_leaf_value, +}; + +use bp_parachains::parachain_head_storage_key_at_source; +use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; +use bp_runtime::{record_all_trie_keys, StorageProofSize}; +use codec::Encode; +use frame_support::traits::Get; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use sp_std::prelude::*; +use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; + +/// Prepare proof of messages for the `receive_messages_proof` call. +/// +/// In addition to returning valid messages proof, environment is prepared to verify this message +/// proof. +pub fn prepare_parachain_heads_proof( + parachains: &[ParaId], + parachain_head_size: u32, + size: StorageProofSize, +) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) +where + R: pallet_bridge_parachains::Config + + pallet_bridge_grandpa::Config, + PI: 'static, + >::BridgedChain: + bp_runtime::Chain, +{ + let parachain_head = ParaHead(vec![0u8; parachain_head_size as usize]); + + // insert all heads to the trie + let mut parachain_heads = Vec::with_capacity(parachains.len()); + let mut storage_keys = Vec::with_capacity(parachains.len()); + let mut state_root = Default::default(); + let mut mdb = MemoryDB::default(); + { + let mut trie = + TrieDBMutBuilderV1::::new(&mut mdb, &mut state_root).build(); + + // insert parachain heads + for (i, parachain) in parachains.into_iter().enumerate() { + let storage_key = + parachain_head_storage_key_at_source(R::ParasPalletName::get(), *parachain); + let leaf_data = if i == 0 { + grow_trie_leaf_value(parachain_head.encode(), size) + } else { + parachain_head.encode() + }; + trie.insert(&storage_key.0, &leaf_data) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + storage_keys.push(storage_key); + parachain_heads.push((*parachain, parachain_head.hash())) + } + } + + // generate heads storage proof + let proof = record_all_trie_keys::, _>(&mdb, &state_root) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); + + let (relay_block_number, relay_block_hash) = + insert_header_to_grandpa_pallet::(state_root); + + (relay_block_number, relay_block_hash, ParaHeadsProof(proof), parachain_heads) +} diff --git a/bin/runtime-common/src/priority_calculator.rs b/bin/runtime-common/src/priority_calculator.rs new file mode 100644 index 00000000000..590de05fb1c --- /dev/null +++ b/bin/runtime-common/src/priority_calculator.rs @@ -0,0 +1,201 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Bridge transaction priority calculator. +//! +//! We want to prioritize message delivery transactions with more messages over +//! transactions with less messages. That's because we reject delivery transactions +//! if it contains already delivered message. And if some transaction delivers +//! single message with nonce `N`, then the transaction with nonces `N..=N+100` will +//! be rejected. This can lower bridge throughput down to one message per block. + +use bp_messages::MessageNonce; +use frame_support::traits::Get; +use sp_runtime::transaction_validity::TransactionPriority; + +// reexport everything from `integrity_tests` module +pub use integrity_tests::*; + +/// Compute priority boost for message delivery transaction that delivers +/// given number of messages. +pub fn compute_priority_boost( + messages: MessageNonce, +) -> TransactionPriority +where + PriorityBoostPerMessage: Get, +{ + // we don't want any boost for transaction with single message => minus one + PriorityBoostPerMessage::get().saturating_mul(messages - 1) +} + +#[cfg(not(feature = "integrity-test"))] +mod integrity_tests {} + +#[cfg(feature = "integrity-test")] +mod integrity_tests { + use super::compute_priority_boost; + + use bp_messages::MessageNonce; + use bp_runtime::PreComputedSize; + use frame_support::{ + dispatch::{DispatchClass, DispatchInfo, Dispatchable, Pays, PostDispatchInfo}, + traits::Get, + }; + use pallet_bridge_messages::WeightInfoExt; + use pallet_transaction_payment::OnChargeTransaction; + use sp_runtime::{ + traits::{UniqueSaturatedInto, Zero}, + transaction_validity::TransactionPriority, + FixedPointOperand, SaturatedConversion, Saturating, + }; + + type BalanceOf = + <::OnChargeTransaction as OnChargeTransaction< + T, + >>::Balance; + + /// Ensures that the value of `PriorityBoostPerMessage` matches the value of + /// `tip_boost_per_message`. + /// + /// We want two transactions, `TX1` with `N` messages and `TX2` with `N+1` messages, have almost + /// the same priority if we'll add `tip_boost_per_message` tip to the `TX1`. We want to be sure + /// that if we add plain `PriorityBoostPerMessage` priority to `TX1`, the priority will be close + /// to `TX2` as well. + pub fn ensure_priority_boost_is_sane( + tip_boost_per_message: BalanceOf, + ) where + Runtime: + pallet_transaction_payment::Config + pallet_bridge_messages::Config, + MessagesInstance: 'static, + PriorityBoostPerMessage: Get, + Runtime::RuntimeCall: Dispatchable, + BalanceOf: Send + Sync + FixedPointOperand, + { + let priority_boost_per_message = PriorityBoostPerMessage::get(); + let maximal_messages_in_delivery_transaction = + Runtime::MaxUnconfirmedMessagesAtInboundLane::get(); + for messages in 1..=maximal_messages_in_delivery_transaction { + let base_priority = estimate_message_delivery_transaction_priority::< + Runtime, + MessagesInstance, + >(messages, Zero::zero()); + let priority_boost = compute_priority_boost::(messages); + let priority_with_boost = base_priority + priority_boost; + + let tip = tip_boost_per_message.saturating_mul((messages - 1).unique_saturated_into()); + let priority_with_tip = + estimate_message_delivery_transaction_priority::(1, tip); + + const ERROR_MARGIN: TransactionPriority = 5; // 5% + if priority_with_boost.abs_diff(priority_with_tip).saturating_mul(100) / + priority_with_tip > + ERROR_MARGIN + { + panic!( + "The PriorityBoostPerMessage value ({}) must be fixed to: {}", + priority_boost_per_message, + compute_priority_boost_per_message::( + tip_boost_per_message + ), + ); + } + } + } + + /// Compute priority boost that we give to message delivery transaction for additional message. + #[cfg(feature = "integrity-test")] + fn compute_priority_boost_per_message( + tip_boost_per_message: BalanceOf, + ) -> TransactionPriority + where + Runtime: + pallet_transaction_payment::Config + pallet_bridge_messages::Config, + MessagesInstance: 'static, + Runtime::RuntimeCall: Dispatchable, + BalanceOf: Send + Sync + FixedPointOperand, + { + // esimate priority of transaction that delivers one message and has large tip + let maximal_messages_in_delivery_transaction = + Runtime::MaxUnconfirmedMessagesAtInboundLane::get(); + let small_with_tip_priority = + estimate_message_delivery_transaction_priority::( + 1, + tip_boost_per_message + .saturating_mul(maximal_messages_in_delivery_transaction.saturated_into()), + ); + // estimate priority of transaction that delivers maximal number of messages, but has no tip + let large_without_tip_priority = estimate_message_delivery_transaction_priority::< + Runtime, + MessagesInstance, + >(maximal_messages_in_delivery_transaction, Zero::zero()); + + small_with_tip_priority + .saturating_sub(large_without_tip_priority) + .saturating_div(maximal_messages_in_delivery_transaction - 1) + } + + /// Estimate message delivery transaction priority. + #[cfg(feature = "integrity-test")] + fn estimate_message_delivery_transaction_priority( + messages: MessageNonce, + tip: BalanceOf, + ) -> TransactionPriority + where + Runtime: + pallet_transaction_payment::Config + pallet_bridge_messages::Config, + MessagesInstance: 'static, + Runtime::RuntimeCall: Dispatchable, + BalanceOf: Send + Sync + FixedPointOperand, + { + // just an estimation of extra transaction bytes that are added to every transaction + // (including signature, signed extensions extra and etc + in our case it includes + // all call arguments extept the proof itself) + let base_tx_size = 512; + // let's say we are relaying similar small messages and for every message we add more trie + // nodes to the proof (x0.5 because we expect some nodes to be reused) + let estimated_message_size = 512; + // let's say all our messages have the same dispatch weight + let estimated_message_dispatch_weight = + Runtime::WeightInfo::message_dispatch_weight(estimated_message_size); + // messages proof argument size is (for every message) messages size + some additional + // trie nodes. Some of them are reused by different messages, so let's take 2/3 of default + // "overhead" constant + let messages_proof_size = Runtime::WeightInfo::expected_extra_storage_proof_size() + .saturating_mul(2) + .saturating_div(3) + .saturating_add(estimated_message_size) + .saturating_mul(messages as _); + + // finally we are able to estimate transaction size and weight + let transaction_size = base_tx_size.saturating_add(messages_proof_size); + let transaction_weight = Runtime::WeightInfo::receive_messages_proof_weight( + &PreComputedSize(transaction_size as _), + messages as _, + estimated_message_dispatch_weight.saturating_mul(messages), + ); + + pallet_transaction_payment::ChargeTransactionPayment::::get_priority( + &DispatchInfo { + weight: transaction_weight, + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }, + transaction_size as _, + tip, + Zero::zero(), + ) + } +} diff --git a/bin/runtime-common/src/refund_relayer_extension.rs b/bin/runtime-common/src/refund_relayer_extension.rs new file mode 100644 index 00000000000..925fea2a743 --- /dev/null +++ b/bin/runtime-common/src/refund_relayer_extension.rs @@ -0,0 +1,1349 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Signed extension that refunds relayer if he has delivered some new messages. +//! It also refunds transaction cost if the transaction is an `utility.batchAll()` +//! with calls that are: delivering new messsage and all necessary underlying headers +//! (parachain or relay chain). + +use crate::messages_call_ext::{ + CallHelper as MessagesCallHelper, CallInfo as MessagesCallInfo, MessagesCallSubType, +}; +use bp_messages::LaneId; +use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; +use bp_runtime::{RangeInclusiveExt, StaticStrProvider}; +use codec::{Decode, Encode}; +use frame_support::{ + dispatch::{CallableCallFor, DispatchInfo, Dispatchable, PostDispatchInfo}, + traits::IsSubType, + weights::Weight, + CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, +}; +use pallet_bridge_grandpa::{ + CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper, SubmitFinalityProofInfo, +}; +use pallet_bridge_messages::Config as MessagesConfig; +use pallet_bridge_parachains::{ + BoundedBridgeGrandpaConfig, CallSubType as ParachainsCallSubType, Config as ParachainsConfig, + RelayBlockNumber, SubmitParachainHeadsHelper, SubmitParachainHeadsInfo, +}; +use pallet_bridge_relayers::{Config as RelayersConfig, Pallet as RelayersPallet}; +use pallet_transaction_payment::{Config as TransactionPaymentConfig, OnChargeTransaction}; +use pallet_utility::{Call as UtilityCall, Config as UtilityConfig, Pallet as UtilityPallet}; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{DispatchInfoOf, Get, PostDispatchInfoOf, SignedExtension, Zero}, + transaction_validity::{ + TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransactionBuilder, + }, + DispatchResult, FixedPointOperand, +}; +use sp_std::{marker::PhantomData, vec, vec::Vec}; + +// without this typedef rustfmt fails with internal err +type BalanceOf = + <::OnChargeTransaction as OnChargeTransaction>::Balance; +type CallOf = ::RuntimeCall; + +/// Trait identifying a bridged parachain. A relayer might be refunded for delivering messages +/// coming from this parachain. +pub trait RefundableParachainId { + /// The instance of the bridge parachains pallet. + type Instance; + /// The parachain Id. + type Id: Get; +} + +/// Default implementation of `RefundableParachainId`. +pub struct RefundableParachain(PhantomData<(Instance, Id)>); + +impl RefundableParachainId for RefundableParachain +where + Id: Get, +{ + type Instance = Instance; + type Id = Id; +} + +/// Trait identifying a bridged messages lane. A relayer might be refunded for delivering messages +/// coming from this lane. +pub trait RefundableMessagesLaneId { + /// The instance of the bridge messages pallet. + type Instance; + /// The messages lane id. + type Id: Get; +} + +/// Default implementation of `RefundableMessagesLaneId`. +pub struct RefundableMessagesLane(PhantomData<(Instance, Id)>); + +impl RefundableMessagesLaneId for RefundableMessagesLane +where + Id: Get, +{ + type Instance = Instance; + type Id = Id; +} + +/// Refund calculator. +pub trait RefundCalculator { + // The underlying integer type in which the refund is calculated. + type Balance; + + /// Compute refund for given transaction. + fn compute_refund( + info: &DispatchInfo, + post_info: &PostDispatchInfo, + len: usize, + tip: Self::Balance, + ) -> Self::Balance; +} + +/// `RefundCalculator` implementation which refunds the actual transaction fee. +pub struct ActualFeeRefund(PhantomData); + +impl RefundCalculator for ActualFeeRefund +where + R: TransactionPaymentConfig, + CallOf: Dispatchable, + BalanceOf: FixedPointOperand, +{ + type Balance = BalanceOf; + + fn compute_refund( + info: &DispatchInfo, + post_info: &PostDispatchInfo, + len: usize, + tip: BalanceOf, + ) -> BalanceOf { + pallet_transaction_payment::Pallet::::compute_actual_fee(len as _, info, post_info, tip) + } +} + +/// Data that is crafted in `pre_dispatch` method and used at `post_dispatch`. +#[cfg_attr(test, derive(Debug, PartialEq))] +pub struct PreDispatchData { + /// Transaction submitter (relayer) account. + relayer: AccountId, + /// Type of the call. + call_info: CallInfo, +} + +/// Type of the call that the extension recognizes. +#[derive(RuntimeDebugNoBound, PartialEq)] +pub enum CallInfo { + /// Relay chain finality + parachain finality + message delivery/confirmation calls. + AllFinalityAndMsgs( + SubmitFinalityProofInfo, + SubmitParachainHeadsInfo, + MessagesCallInfo, + ), + /// Parachain finality + message delivery/confirmation calls. + ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo), + /// Standalone message delivery/confirmation call. + Msgs(MessagesCallInfo), +} + +impl CallInfo { + /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. + fn submit_finality_proof_info(&self) -> Option> { + match *self { + Self::AllFinalityAndMsgs(info, _, _) => Some(info), + _ => None, + } + } + + /// Returns the pre-dispatch `SubmitParachainHeadsInfo`. + fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> { + match self { + Self::AllFinalityAndMsgs(_, info, _) => Some(info), + Self::ParachainFinalityAndMsgs(info, _) => Some(info), + _ => None, + } + } + + /// Returns the pre-dispatch `ReceiveMessagesProofInfo`. + fn messages_call_info(&self) -> &MessagesCallInfo { + match self { + Self::AllFinalityAndMsgs(_, _, info) => info, + Self::ParachainFinalityAndMsgs(_, info) => info, + Self::Msgs(info) => info, + } + } +} + +/// Signed extension that refunds a relayer for new messages coming from a parachain. +/// +/// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) +/// with message delivery transaction. Batch may deliver either both relay chain header and +/// parachain head, or just parachain head. Corresponding headers must be used in messages +/// proof verification. +/// +/// Extension does not refund transaction tip due to security reasons. +#[derive( + DefaultNoBound, + CloneNoBound, + Decode, + Encode, + EqNoBound, + PartialEqNoBound, + RuntimeDebugNoBound, + TypeInfo, +)] +#[scale_info(skip_type_params(Runtime, Para, Msgs, Refund, Priority, Id))] +pub struct RefundBridgedParachainMessages( + PhantomData<(Runtime, Para, Msgs, Refund, Priority, Id)>, +); + +impl + RefundBridgedParachainMessages +where + Self: 'static + Send + Sync, + Runtime: UtilityConfig> + + BoundedBridgeGrandpaConfig + + ParachainsConfig + + MessagesConfig, + Para: RefundableParachainId, + Msgs: RefundableMessagesLaneId, + CallOf: Dispatchable + + IsSubType, Runtime>> + + GrandpaCallSubType + + ParachainsCallSubType + + MessagesCallSubType, +{ + fn expand_call<'a>(&self, call: &'a CallOf) -> Vec<&'a CallOf> { + match call.is_sub_type() { + Some(UtilityCall::::batch_all { ref calls }) if calls.len() <= 3 => + calls.iter().collect(), + Some(_) => vec![], + None => vec![call], + } + } + + fn parse_and_check_for_obsolete_call( + &self, + call: &CallOf, + ) -> Result, TransactionValidityError> { + let calls = self.expand_call(call); + let total_calls = calls.len(); + let mut calls = calls.into_iter().map(Self::check_obsolete_call).rev(); + + let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info_for(Msgs::Id::get())); + let para_finality_call = calls + .next() + .transpose()? + .and_then(|c| c.submit_parachain_heads_info_for(Para::Id::get())); + let relay_finality_call = + calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); + + Ok(match (total_calls, relay_finality_call, para_finality_call, msgs_call) { + (3, Some(relay_finality_call), Some(para_finality_call), Some(msgs_call)) => Some( + CallInfo::AllFinalityAndMsgs(relay_finality_call, para_finality_call, msgs_call), + ), + (2, None, Some(para_finality_call), Some(msgs_call)) => + Some(CallInfo::ParachainFinalityAndMsgs(para_finality_call, msgs_call)), + (1, None, None, Some(msgs_call)) => Some(CallInfo::Msgs(msgs_call)), + _ => None, + }) + } + + fn check_obsolete_call( + call: &CallOf, + ) -> Result<&CallOf, TransactionValidityError> { + call.check_obsolete_submit_finality_proof()?; + call.check_obsolete_submit_parachain_heads()?; + call.check_obsolete_call()?; + Ok(call) + } +} + +impl SignedExtension + for RefundBridgedParachainMessages +where + Self: 'static + Send + Sync, + Runtime: UtilityConfig> + + BoundedBridgeGrandpaConfig + + ParachainsConfig + + MessagesConfig + + RelayersConfig, + Para: RefundableParachainId, + Msgs: RefundableMessagesLaneId, + Refund: RefundCalculator, + Priority: Get, + Id: StaticStrProvider, + CallOf: Dispatchable + + IsSubType, Runtime>> + + GrandpaCallSubType + + ParachainsCallSubType + + MessagesCallSubType, +{ + const IDENTIFIER: &'static str = Id::STR; + type AccountId = Runtime::AccountId; + type Call = CallOf; + type AdditionalSigned = (); + type Pre = Option>; + + fn additional_signed(&self) -> Result<(), TransactionValidityError> { + Ok(()) + } + + fn validate( + &self, + _who: &Self::AccountId, + call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> TransactionValidity { + // this is the only relevant line of code for the `pre_dispatch` + // + // we're not calling `validato` from `pre_dispatch` directly because of performance + // reasons, so if you're adding some code that may fail here, please check if it needs + // to be added to the `pre_dispatch` as well + let parsed_call = self.parse_and_check_for_obsolete_call(call)?; + + // the following code just plays with transaction priority and never returns an error + let mut valid_transaction = ValidTransactionBuilder::default(); + if let Some(parsed_call) = parsed_call { + // we give delivery transactions some boost, that depends on number of messages inside + let messages_call_info = parsed_call.messages_call_info(); + if let MessagesCallInfo::ReceiveMessagesProof(info) = messages_call_info { + // compute total number of messages in transaction + let bundled_messages = info.base.bundled_range.checked_len().unwrap_or(0); + + // a quick check to avoid invalid high-priority transactions + if bundled_messages <= Runtime::MaxUnconfirmedMessagesAtInboundLane::get() { + let priority_boost = crate::priority_calculator::compute_priority_boost::< + Priority, + >(bundled_messages); + valid_transaction = valid_transaction.priority(priority_boost); + } + } + } + + valid_transaction.build() + } + + fn pre_dispatch( + self, + who: &Self::AccountId, + call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + // this is a relevant piece of `validate` that we need here (in `pre_dispatch`) + let parsed_call = self.parse_and_check_for_obsolete_call(call)?; + + Ok(parsed_call.map(|call_info| { + log::trace!( + target: "runtime::bridge", + "{} from parachain {} via {:?} parsed bridge transaction in pre-dispatch: {:?}", + Self::IDENTIFIER, + Para::Id::get(), + Msgs::Id::get(), + call_info, + ); + PreDispatchData { relayer: who.clone(), call_info } + })) + } + + fn post_dispatch( + pre: Option, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + let mut extra_weight = Weight::zero(); + let mut extra_size = 0; + + // We don't refund anything if the transaction has failed. + if result.is_err() { + return Ok(()) + } + + // We don't refund anything for transactions that we don't support. + let (relayer, call_info) = match pre { + Some(Some(pre)) => (pre.relayer, pre.call_info), + _ => return Ok(()), + }; + + // check if relay chain state has been updated + if let Some(finality_proof_info) = call_info.submit_finality_proof_info() { + if !SubmitFinalityProofHelper::::was_successful( + finality_proof_info.block_number, + ) { + // we only refund relayer if all calls have updated chain state + log::trace!( + target: "runtime::bridge", + "{} from parachain {} via {:?}: failed to refund relayer {:?}, because \ + relay chain finality proof has not been accepted", + Self::IDENTIFIER, + Para::Id::get(), + Msgs::Id::get(), + relayer, + ); + + return Ok(()) + } + + // there's a conflict between how bridge GRANDPA pallet works and a `utility.batchAll` + // transaction. If relay chain header is mandatory, the GRANDPA pallet returns + // `Pays::No`, because such transaction is mandatory for operating the bridge. But + // `utility.batchAll` transaction always requires payment. But in both cases we'll + // refund relayer - either explicitly here, or using `Pays::No` if he's choosing + // to submit dedicated transaction. + + // submitter has means to include extra weight/bytes in the `submit_finality_proof` + // call, so let's subtract extra weight/size to avoid refunding for this extra stuff + extra_weight = finality_proof_info.extra_weight; + extra_size = finality_proof_info.extra_size; + } + + // check if parachain state has been updated + if let Some(para_proof_info) = call_info.submit_parachain_heads_info() { + if !SubmitParachainHeadsHelper::::was_successful( + para_proof_info, + ) { + // we only refund relayer if all calls have updated chain state + log::trace!( + target: "runtime::bridge", + "{} from parachain {} via {:?}: failed to refund relayer {:?}, because \ + parachain finality proof has not been accepted", + Self::IDENTIFIER, + Para::Id::get(), + Msgs::Id::get(), + relayer, + ); + + return Ok(()) + } + } + + // Check if the `ReceiveMessagesProof` call delivered all the messages that + // it contained. If this happens, we consider the transaction "helpful" and refund it. + let msgs_call_info = call_info.messages_call_info(); + if !MessagesCallHelper::::was_successful(msgs_call_info) { + log::trace!( + target: "runtime::bridge", + "{} from parachain {} via {:?}: failed to refund relayer {:?}, because \ + some of messages have not been accepted", + Self::IDENTIFIER, + Para::Id::get(), + Msgs::Id::get(), + relayer, + ); + + return Ok(()) + } + + // regarding the tip - refund that happens here (at this side of the bridge) isn't the whole + // relayer compensation. He'll receive some amount at the other side of the bridge. It shall + // (in theory) cover the tip there. Otherwise, if we'll be compensating tip here, some + // malicious relayer may use huge tips, effectively depleting account that pay rewards. The + // cost of this attack is nothing. Hence we use zero as tip here. + let tip = Zero::zero(); + + // decrease post-dispatch weight/size using extra weight/size that we know now + let post_info_len = len.saturating_sub(extra_size as usize); + let mut post_info = *post_info; + post_info.actual_weight = + Some(post_info.actual_weight.unwrap_or(info.weight).saturating_sub(extra_weight)); + + // compute the relayer refund + let refund = Refund::compute_refund(info, &post_info, post_info_len, tip); + + // finally - register refund in relayers pallet + let rewards_account_owner = match msgs_call_info { + MessagesCallInfo::ReceiveMessagesProof(_) => RewardsAccountOwner::ThisChain, + MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => RewardsAccountOwner::BridgedChain, + }; + RelayersPallet::::register_relayer_reward( + RewardsAccountParams::new( + Msgs::Id::get(), + Runtime::BridgedChainId::get(), + rewards_account_owner, + ), + &relayer, + refund, + ); + + log::trace!( + target: "runtime::bridge", + "{} from parachain {} via {:?} has registered reward: {:?} for {:?}", + Self::IDENTIFIER, + Para::Id::get(), + Msgs::Id::get(), + refund, + relayer, + ); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, + }, + messages_call_ext::{ + BaseMessagesProofInfo, ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, + UnrewardedRelayerOccupation, + }, + mock::*, + }; + use bp_messages::{InboundLaneData, MessageNonce, OutboundLaneData, UnrewardedRelayersState}; + use bp_parachains::{BestParaHeadHash, ParaInfo}; + use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; + use bp_runtime::HeaderId; + use bp_test_utils::{make_default_justification, test_keyring}; + use frame_support::{assert_storage_noop, parameter_types, weights::Weight}; + use pallet_bridge_grandpa::{Call as GrandpaCall, StoredAuthoritySet}; + use pallet_bridge_messages::Call as MessagesCall; + use pallet_bridge_parachains::{Call as ParachainsCall, RelayBlockHash}; + use sp_runtime::{ + traits::{ConstU64, Header as HeaderT}, + transaction_validity::{InvalidTransaction, ValidTransaction}, + DispatchError, + }; + + parameter_types! { + TestParachain: u32 = 1000; + pub TestLaneId: LaneId = TEST_LANE_ID; + pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( + TEST_LANE_ID, + TEST_BRIDGED_CHAIN_ID, + RewardsAccountOwner::ThisChain, + ); + pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( + TEST_LANE_ID, + TEST_BRIDGED_CHAIN_ID, + RewardsAccountOwner::BridgedChain, + ); + } + + bp_runtime::generate_static_str_provider!(TestExtension); + type TestExtension = RefundBridgedParachainMessages< + TestRuntime, + RefundableParachain<(), TestParachain>, + RefundableMessagesLane<(), TestLaneId>, + ActualFeeRefund, + ConstU64<1>, + StrTestExtension, + >; + + fn relayer_account_at_this_chain() -> ThisChainAccountId { + 0 + } + + fn relayer_account_at_bridged_chain() -> BridgedChainAccountId { + 0 + } + + fn initialize_environment( + best_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_hash: ParaHash, + best_message: MessageNonce, + ) { + let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect(); + let best_relay_header = HeaderId(best_relay_header_number, RelayBlockHash::default()); + pallet_bridge_grandpa::CurrentAuthoritySet::::put( + StoredAuthoritySet::try_new(authorities, 0).unwrap(), + ); + pallet_bridge_grandpa::BestFinalized::::put(best_relay_header); + + let para_id = ParaId(TestParachain::get()); + let para_info = ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: parachain_head_at_relay_header_number, + head_hash: parachain_head_hash, + }, + next_imported_hash_position: 0, + }; + pallet_bridge_parachains::ParasInfo::::insert(para_id, para_info); + + let lane_id = TestLaneId::get(); + let in_lane_data = + InboundLaneData { last_confirmed_nonce: best_message, ..Default::default() }; + pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); + + let out_lane_data = + OutboundLaneData { latest_received_nonce: best_message, ..Default::default() }; + pallet_bridge_messages::OutboundLanes::::insert(lane_id, out_lane_data); + } + + fn submit_relay_header_call(relay_header_number: RelayBlockNumber) -> RuntimeCall { + let relay_header = BridgedChainHeader::new( + relay_header_number, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ); + let relay_justification = make_default_justification(&relay_header); + + RuntimeCall::BridgeGrandpa(GrandpaCall::submit_finality_proof { + finality_target: Box::new(relay_header), + justification: relay_justification, + }) + } + + fn submit_parachain_head_call( + parachain_head_at_relay_header_number: RelayBlockNumber, + ) -> RuntimeCall { + RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { + at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()), + parachains: vec![(ParaId(TestParachain::get()), [1u8; 32].into())], + parachain_heads_proof: ParaHeadsProof(vec![]), + }) + } + + fn message_delivery_call(best_message: MessageNonce) -> RuntimeCall { + RuntimeCall::BridgeMessages(MessagesCall::receive_messages_proof { + relayer_id_at_bridged_chain: relayer_account_at_bridged_chain(), + proof: FromBridgedChainMessagesProof { + bridged_header_hash: Default::default(), + storage_proof: vec![], + lane: TestLaneId::get(), + nonces_start: pallet_bridge_messages::InboundLanes::::get( + TEST_LANE_ID, + ) + .last_delivered_nonce() + + 1, + nonces_end: best_message, + }, + messages_count: 1, + dispatch_weight: Weight::zero(), + }) + } + + fn message_confirmation_call(best_message: MessageNonce) -> RuntimeCall { + RuntimeCall::BridgeMessages(MessagesCall::receive_messages_delivery_proof { + proof: FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: Default::default(), + storage_proof: vec![], + lane: TestLaneId::get(), + }, + relayers_state: UnrewardedRelayersState { + last_delivered_nonce: best_message, + ..Default::default() + }, + }) + } + + fn parachain_finality_and_delivery_batch_call( + parachain_head_at_relay_header_number: RelayBlockNumber, + best_message: MessageNonce, + ) -> RuntimeCall { + RuntimeCall::Utility(UtilityCall::batch_all { + calls: vec![ + submit_parachain_head_call(parachain_head_at_relay_header_number), + message_delivery_call(best_message), + ], + }) + } + + fn parachain_finality_and_confirmation_batch_call( + parachain_head_at_relay_header_number: RelayBlockNumber, + best_message: MessageNonce, + ) -> RuntimeCall { + RuntimeCall::Utility(UtilityCall::batch_all { + calls: vec![ + submit_parachain_head_call(parachain_head_at_relay_header_number), + message_confirmation_call(best_message), + ], + }) + } + + fn all_finality_and_delivery_batch_call( + relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: RelayBlockNumber, + best_message: MessageNonce, + ) -> RuntimeCall { + RuntimeCall::Utility(UtilityCall::batch_all { + calls: vec![ + submit_relay_header_call(relay_header_number), + submit_parachain_head_call(parachain_head_at_relay_header_number), + message_delivery_call(best_message), + ], + }) + } + + fn all_finality_and_confirmation_batch_call( + relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: RelayBlockNumber, + best_message: MessageNonce, + ) -> RuntimeCall { + RuntimeCall::Utility(UtilityCall::batch_all { + calls: vec![ + submit_relay_header_call(relay_header_number), + submit_parachain_head_call(parachain_head_at_relay_header_number), + message_confirmation_call(best_message), + ], + }) + } + + fn all_finality_pre_dispatch_data() -> PreDispatchData { + PreDispatchData { + relayer: relayer_account_at_this_chain(), + call_info: CallInfo::AllFinalityAndMsgs( + SubmitFinalityProofInfo { + block_number: 200, + extra_weight: Weight::zero(), + extra_size: 0, + }, + SubmitParachainHeadsInfo { + at_relay_block_number: 200, + para_id: ParaId(TestParachain::get()), + para_head_hash: [1u8; 32].into(), + }, + MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { + base: BaseMessagesProofInfo { + lane_id: TEST_LANE_ID, + bundled_range: 101..=200, + best_stored_nonce: 100, + }, + unrewarded_relayers: UnrewardedRelayerOccupation { + free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(), + free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(), + }, + }), + ), + } + } + + fn all_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + PreDispatchData { + relayer: relayer_account_at_this_chain(), + call_info: CallInfo::AllFinalityAndMsgs( + SubmitFinalityProofInfo { + block_number: 200, + extra_weight: Weight::zero(), + extra_size: 0, + }, + SubmitParachainHeadsInfo { + at_relay_block_number: 200, + para_id: ParaId(TestParachain::get()), + para_head_hash: [1u8; 32].into(), + }, + MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( + BaseMessagesProofInfo { + lane_id: TEST_LANE_ID, + bundled_range: 101..=200, + best_stored_nonce: 100, + }, + )), + ), + } + } + + fn parachain_finality_pre_dispatch_data() -> PreDispatchData { + PreDispatchData { + relayer: relayer_account_at_this_chain(), + call_info: CallInfo::ParachainFinalityAndMsgs( + SubmitParachainHeadsInfo { + at_relay_block_number: 200, + para_id: ParaId(TestParachain::get()), + para_head_hash: [1u8; 32].into(), + }, + MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { + base: BaseMessagesProofInfo { + lane_id: TEST_LANE_ID, + bundled_range: 101..=200, + best_stored_nonce: 100, + }, + unrewarded_relayers: UnrewardedRelayerOccupation { + free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(), + free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(), + }, + }), + ), + } + } + + fn parachain_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + PreDispatchData { + relayer: relayer_account_at_this_chain(), + call_info: CallInfo::ParachainFinalityAndMsgs( + SubmitParachainHeadsInfo { + at_relay_block_number: 200, + para_id: ParaId(TestParachain::get()), + para_head_hash: [1u8; 32].into(), + }, + MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( + BaseMessagesProofInfo { + lane_id: TEST_LANE_ID, + bundled_range: 101..=200, + best_stored_nonce: 100, + }, + )), + ), + } + } + + fn delivery_pre_dispatch_data() -> PreDispatchData { + PreDispatchData { + relayer: relayer_account_at_this_chain(), + call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesProof( + ReceiveMessagesProofInfo { + base: BaseMessagesProofInfo { + lane_id: TEST_LANE_ID, + bundled_range: 101..=200, + best_stored_nonce: 100, + }, + unrewarded_relayers: UnrewardedRelayerOccupation { + free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(), + free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(), + }, + }, + )), + } + } + + fn confirmation_pre_dispatch_data() -> PreDispatchData { + PreDispatchData { + relayer: relayer_account_at_this_chain(), + call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesDeliveryProof( + ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { + lane_id: TEST_LANE_ID, + bundled_range: 101..=200, + best_stored_nonce: 100, + }), + )), + } + } + + fn run_test(test: impl FnOnce()) { + sp_io::TestExternalities::new(Default::default()).execute_with(test) + } + + fn run_validate(call: RuntimeCall) -> TransactionValidity { + let extension: TestExtension = RefundBridgedParachainMessages(PhantomData); + extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) + } + + fn run_pre_dispatch( + call: RuntimeCall, + ) -> Result>, TransactionValidityError> { + let extension: TestExtension = RefundBridgedParachainMessages(PhantomData); + extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) + } + + fn dispatch_info() -> DispatchInfo { + DispatchInfo { + weight: Weight::from_parts( + frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, + 0, + ), + class: frame_support::dispatch::DispatchClass::Normal, + pays_fee: frame_support::dispatch::Pays::Yes, + } + } + + fn post_dispatch_info() -> PostDispatchInfo { + PostDispatchInfo { actual_weight: None, pays_fee: frame_support::dispatch::Pays::Yes } + } + + fn run_post_dispatch( + pre_dispatch_data: Option>, + dispatch_result: DispatchResult, + ) { + let post_dispatch_result = TestExtension::post_dispatch( + Some(pre_dispatch_data), + &dispatch_info(), + &post_dispatch_info(), + 1024, + &dispatch_result, + ); + assert_eq!(post_dispatch_result, Ok(())); + } + + fn expected_reward() -> ThisChainBalance { + pallet_transaction_payment::Pallet::::compute_actual_fee( + 1024, + &dispatch_info(), + &post_dispatch_info(), + Zero::zero(), + ) + } + + #[test] + fn validate_boosts_priority_of_message_delivery_transactons() { + run_test(|| { + initialize_environment(100, 100, Default::default(), 100); + + let priority_of_100_messages_delivery = + run_validate(message_delivery_call(200)).unwrap().priority; + let priority_of_200_messages_delivery = + run_validate(message_delivery_call(300)).unwrap().priority; + assert!( + priority_of_200_messages_delivery > priority_of_100_messages_delivery, + "Invalid priorities: {} for 200 messages vs {} for 100 messages", + priority_of_200_messages_delivery, + priority_of_100_messages_delivery, + ); + + let priority_of_100_messages_confirmation = + run_validate(message_confirmation_call(200)).unwrap().priority; + let priority_of_200_messages_confirmation = + run_validate(message_confirmation_call(300)).unwrap().priority; + assert_eq!( + priority_of_100_messages_confirmation, + priority_of_200_messages_confirmation + ); + }); + } + + #[test] + fn validate_does_not_boost_priority_of_message_delivery_transactons_with_too_many_messages() { + run_test(|| { + initialize_environment(100, 100, Default::default(), 100); + + let priority_of_max_messages_delivery = run_validate(message_delivery_call( + 100 + MaxUnconfirmedMessagesAtInboundLane::get(), + )) + .unwrap() + .priority; + let priority_of_more_than_max_messages_delivery = run_validate(message_delivery_call( + 100 + MaxUnconfirmedMessagesAtInboundLane::get() + 1, + )) + .unwrap() + .priority; + + assert!( + priority_of_max_messages_delivery > priority_of_more_than_max_messages_delivery, + "Invalid priorities: {} for MAX messages vs {} for MAX+1 messages", + priority_of_max_messages_delivery, + priority_of_more_than_max_messages_delivery, + ); + }); + } + + #[test] + fn validate_allows_non_obsolete_transactions() { + run_test(|| { + initialize_environment(100, 100, Default::default(), 100); + + fn run_validate_ignore_priority(call: RuntimeCall) -> TransactionValidity { + run_validate(call).map(|mut tx| { + tx.priority = 0; + tx + }) + } + + assert_eq!( + run_validate_ignore_priority(message_delivery_call(200)), + Ok(ValidTransaction::default()), + ); + assert_eq!( + run_validate_ignore_priority(message_confirmation_call(200)), + Ok(ValidTransaction::default()), + ); + + assert_eq!( + run_validate_ignore_priority(parachain_finality_and_delivery_batch_call(200, 200)), + Ok(ValidTransaction::default()), + ); + assert_eq!( + run_validate_ignore_priority(parachain_finality_and_confirmation_batch_call( + 200, 200 + )), + Ok(ValidTransaction::default()), + ); + + assert_eq!( + run_validate_ignore_priority(all_finality_and_delivery_batch_call(200, 200, 200)), + Ok(ValidTransaction::default()), + ); + assert_eq!( + run_validate_ignore_priority(all_finality_and_confirmation_batch_call( + 200, 200, 200 + )), + Ok(ValidTransaction::default()), + ); + }); + } + + #[test] + fn ext_rejects_batch_with_obsolete_relay_chain_header() { + run_test(|| { + initialize_environment(100, 100, Default::default(), 100); + + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call(100, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_validate(all_finality_and_delivery_batch_call(100, 200, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + }); + } + + #[test] + fn ext_rejects_batch_with_obsolete_parachain_head() { + run_test(|| { + initialize_environment(100, 100, Default::default(), 100); + + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call(101, 100, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + assert_eq!( + run_validate(all_finality_and_delivery_batch_call(101, 100, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_pre_dispatch(parachain_finality_and_delivery_batch_call(100, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + assert_eq!( + run_validate(parachain_finality_and_delivery_batch_call(100, 200)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + }); + } + + #[test] + fn ext_rejects_batch_with_obsolete_messages() { + run_test(|| { + initialize_environment(100, 100, Default::default(), 100); + + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + assert_eq!( + run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_validate(all_finality_and_delivery_batch_call(200, 200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + assert_eq!( + run_validate(all_finality_and_confirmation_batch_call(200, 200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + assert_eq!( + run_pre_dispatch(parachain_finality_and_confirmation_batch_call(200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_validate(parachain_finality_and_delivery_batch_call(200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + assert_eq!( + run_validate(parachain_finality_and_confirmation_batch_call(200, 100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + }); + } + + #[test] + fn pre_dispatch_parses_batch_with_relay_chain_and_parachain_headers() { + run_test(|| { + initialize_environment(100, 100, Default::default(), 100); + + assert_eq!( + run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), + Ok(Some(all_finality_pre_dispatch_data())), + ); + assert_eq!( + run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), + Ok(Some(all_finality_confirmation_pre_dispatch_data())), + ); + }); + } + + #[test] + fn pre_dispatch_parses_batch_with_parachain_header() { + run_test(|| { + initialize_environment(100, 100, Default::default(), 100); + + assert_eq!( + run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), + Ok(Some(parachain_finality_pre_dispatch_data())), + ); + assert_eq!( + run_pre_dispatch(parachain_finality_and_confirmation_batch_call(200, 200)), + Ok(Some(parachain_finality_confirmation_pre_dispatch_data())), + ); + }); + } + + #[test] + fn pre_dispatch_fails_to_parse_batch_with_multiple_parachain_headers() { + run_test(|| { + initialize_environment(100, 100, Default::default(), 100); + + let call = RuntimeCall::Utility(UtilityCall::batch_all { + calls: vec![ + RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { + at_relay_block: (100, RelayBlockHash::default()), + parachains: vec![ + (ParaId(TestParachain::get()), [1u8; 32].into()), + (ParaId(TestParachain::get() + 1), [1u8; 32].into()), + ], + parachain_heads_proof: ParaHeadsProof(vec![]), + }), + message_delivery_call(200), + ], + }); + + assert_eq!(run_pre_dispatch(call), Ok(None),); + }); + } + + #[test] + fn pre_dispatch_parses_message_transaction() { + run_test(|| { + initialize_environment(100, 100, Default::default(), 100); + + assert_eq!( + run_pre_dispatch(message_delivery_call(200)), + Ok(Some(delivery_pre_dispatch_data())), + ); + assert_eq!( + run_pre_dispatch(message_confirmation_call(200)), + Ok(Some(confirmation_pre_dispatch_data())), + ); + }); + } + + #[test] + fn post_dispatch_ignores_unknown_transaction() { + run_test(|| { + assert_storage_noop!(run_post_dispatch(None, Ok(()))); + }); + } + + #[test] + fn post_dispatch_ignores_failed_transaction() { + run_test(|| { + assert_storage_noop!(run_post_dispatch( + Some(all_finality_pre_dispatch_data()), + Err(DispatchError::BadOrigin) + )); + }); + } + + #[test] + fn post_dispatch_ignores_transaction_that_has_not_updated_relay_chain_state() { + run_test(|| { + initialize_environment(100, 200, Default::default(), 200); + + assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); + }); + } + + #[test] + fn post_dispatch_ignores_transaction_that_has_not_updated_parachain_state() { + run_test(|| { + initialize_environment(200, 100, Default::default(), 200); + + assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); + assert_storage_noop!(run_post_dispatch( + Some(parachain_finality_pre_dispatch_data()), + Ok(()) + )); + }); + } + + #[test] + fn post_dispatch_ignores_transaction_that_has_not_delivered_any_messages() { + run_test(|| { + initialize_environment(200, 200, Default::default(), 100); + + assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); + assert_storage_noop!(run_post_dispatch( + Some(parachain_finality_pre_dispatch_data()), + Ok(()) + )); + assert_storage_noop!(run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(()))); + + assert_storage_noop!(run_post_dispatch( + Some(all_finality_confirmation_pre_dispatch_data()), + Ok(()) + )); + assert_storage_noop!(run_post_dispatch( + Some(parachain_finality_confirmation_pre_dispatch_data()), + Ok(()) + )); + assert_storage_noop!(run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(()))); + }); + } + + #[test] + fn post_dispatch_ignores_transaction_that_has_not_delivered_all_messages() { + run_test(|| { + initialize_environment(200, 200, Default::default(), 150); + + assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); + assert_storage_noop!(run_post_dispatch( + Some(parachain_finality_pre_dispatch_data()), + Ok(()) + )); + assert_storage_noop!(run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(()))); + + assert_storage_noop!(run_post_dispatch( + Some(all_finality_confirmation_pre_dispatch_data()), + Ok(()) + )); + assert_storage_noop!(run_post_dispatch( + Some(parachain_finality_confirmation_pre_dispatch_data()), + Ok(()) + )); + assert_storage_noop!(run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(()))); + }); + } + + #[test] + fn post_dispatch_refunds_relayer_in_all_finality_batch_with_extra_weight() { + run_test(|| { + initialize_environment(200, 200, [1u8; 32].into(), 200); + + let mut dispatch_info = dispatch_info(); + dispatch_info.weight = Weight::from_parts( + frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND * 2, + 0, + ); + + // without any size/weight refund: we expect regular reward + let pre_dispatch_data = all_finality_pre_dispatch_data(); + let regular_reward = expected_reward(); + run_post_dispatch(Some(pre_dispatch_data), Ok(())); + assert_eq!( + RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + MsgProofsRewardsAccount::get() + ), + Some(regular_reward), + ); + + // now repeat the same with size+weight refund: we expect smaller reward + let mut pre_dispatch_data = all_finality_pre_dispatch_data(); + match pre_dispatch_data.call_info { + CallInfo::AllFinalityAndMsgs(ref mut info, ..) => { + info.extra_weight.set_ref_time( + frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, + ); + info.extra_size = 32; + }, + _ => unreachable!(), + } + run_post_dispatch(Some(pre_dispatch_data), Ok(())); + let reward_after_two_calls = RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + MsgProofsRewardsAccount::get(), + ) + .unwrap(); + assert!( + reward_after_two_calls < 2 * regular_reward, + "{} must be < 2 * {}", + reward_after_two_calls, + 2 * regular_reward, + ); + }); + } + + #[test] + fn post_dispatch_refunds_relayer_in_all_finality_batch() { + run_test(|| { + initialize_environment(200, 200, [1u8; 32].into(), 200); + + run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(())); + assert_eq!( + RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + MsgProofsRewardsAccount::get() + ), + Some(expected_reward()), + ); + + run_post_dispatch(Some(all_finality_confirmation_pre_dispatch_data()), Ok(())); + assert_eq!( + RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + MsgDeliveryProofsRewardsAccount::get() + ), + Some(expected_reward()), + ); + }); + } + + #[test] + fn post_dispatch_refunds_relayer_in_parachain_finality_batch() { + run_test(|| { + initialize_environment(200, 200, [1u8; 32].into(), 200); + + run_post_dispatch(Some(parachain_finality_pre_dispatch_data()), Ok(())); + assert_eq!( + RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + MsgProofsRewardsAccount::get() + ), + Some(expected_reward()), + ); + + run_post_dispatch(Some(parachain_finality_confirmation_pre_dispatch_data()), Ok(())); + assert_eq!( + RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + MsgDeliveryProofsRewardsAccount::get() + ), + Some(expected_reward()), + ); + }); + } + + #[test] + fn post_dispatch_refunds_relayer_in_message_transaction() { + run_test(|| { + initialize_environment(200, 200, Default::default(), 200); + + run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(())); + assert_eq!( + RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + MsgProofsRewardsAccount::get() + ), + Some(expected_reward()), + ); + + run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(())); + assert_eq!( + RelayersPallet::::relayer_reward( + relayer_account_at_this_chain(), + MsgDeliveryProofsRewardsAccount::get() + ), + Some(expected_reward()), + ); + }); + } +} diff --git a/ci.Dockerfile b/ci.Dockerfile new file mode 100644 index 00000000000..b419f6be54d --- /dev/null +++ b/ci.Dockerfile @@ -0,0 +1,53 @@ +# This file is a "runtime" part from a builder-pattern in Dockerfile, it's used in CI. +# The only different part is that the compilation happens externally, +# so COPY has a different source. +FROM docker.io/library/ubuntu:20.04 + +# show backtraces +ENV RUST_BACKTRACE 1 +ENV DEBIAN_FRONTEND=noninteractive + +RUN set -eux; \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + curl ca-certificates libssl-dev && \ + update-ca-certificates && \ + groupadd -g 1000 user && \ + useradd -u 1000 -g user -s /bin/sh -m user && \ + # apt clean up + apt-get autoremove -y && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# switch to non-root user +USER user + +WORKDIR /home/user + +ARG PROJECT=substrate-relay + +COPY --chown=user:user ./${PROJECT} ./ +COPY --chown=user:user ./bridge-entrypoint.sh ./ + +# check if executable works in this container +RUN ./${PROJECT} --version + +ENV PROJECT=$PROJECT +ENTRYPOINT ["/home/user/bridge-entrypoint.sh"] + +# metadata +ARG VCS_REF=master +ARG BUILD_DATE="" +ARG VERSION="" + +LABEL org.opencontainers.image.title="${PROJECT}" \ + org.opencontainers.image.description="${PROJECT} - component of Parity Bridges Common" \ + org.opencontainers.image.source="https://github.com/paritytech/parity-bridges-common/blob/${VCS_REF}/ci.Dockerfile" \ + org.opencontainers.image.url="https://github.com/paritytech/parity-bridges-common/blob/${VCS_REF}/ci.Dockerfile" \ + org.opencontainers.image.documentation="https://github.com/paritytech/parity-bridges-common/blob/${VCS_REF}/README.md" \ + org.opencontainers.image.created="${BUILD_DATE}" \ + org.opencontainers.image.version="${VERSION}" \ + org.opencontainers.image.revision="${VCS_REF}" \ + org.opencontainers.image.authors="devops-team@parity.io" \ + org.opencontainers.image.vendor="Parity Technologies" \ + org.opencontainers.image.licenses="GPL-3.0 License" diff --git a/deny.toml b/deny.toml new file mode 100644 index 00000000000..264a37bd989 --- /dev/null +++ b/deny.toml @@ -0,0 +1,202 @@ +# This template contains all of the possible sections and their default values + +# Note that all fields that take a lint level have these possible values: +# * deny - An error will be produced and the check will fail +# * warn - A warning will be produced, but the check will not fail +# * allow - No warning or error will be produced, though in some cases a note +# will be + +# The values provided in this template are the default values that will be used +# when any section or field is not specified in your own configuration + +# If 1 or more target triples (and optionally, target_features) are specified, +# only the specified targets will be checked when running `cargo deny check`. +# This means, if a particular package is only ever used as a target specific +# dependency, such as, for example, the `nix` crate only being used via the +# `target_family = "unix"` configuration, that only having windows targets in +# this list would mean the nix crate, as well as any of its exclusive +# dependencies not shared by any other crates, would be ignored, as the target +# list here is effectively saying which targets you are building for. +targets = [ + # The triple can be any string, but only the target triples built in to + # rustc (as of 1.40) can be checked against actual config expressions + #{ triple = "x86_64-unknown-linux-musl" }, + # You can also specify which target_features you promise are enabled for a + # particular target. target_features are currently not validated against + # the actual valid features supported by the target architecture. + #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, +] + +# This section is considered when running `cargo deny check advisories` +# More documentation for the advisories section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html +[advisories] +# The path where the advisory database is cloned/fetched into +db-path = "~/.cargo/advisory-db" +# The url of the advisory database to use +db-urls = ["https://github.com/rustsec/advisory-db"] +# The lint level for security vulnerabilities +vulnerability = "deny" +# The lint level for unmaintained crates +unmaintained = "warn" +# The lint level for crates that have been yanked from their source registry +yanked = "warn" +# The lint level for crates with security notices. Note that as of +# 2019-12-17 there are no security notice advisories in +# https://github.com/rustsec/advisory-db +notice = "warn" +# A list of advisory IDs to ignore. Note that ignored advisories will still +# output a note when they are encountered. +ignore = [ + # time (origin: Substrate RPC + benchmarking crates) + "RUSTSEC-2020-0071", + # ansi_term (The maintainer has adviced that this crate is deprecated and will not receive any maintenance. + # Once other crates will move to some alternative, we'll do that too) + "RUSTSEC-2021-0139", + # deprecated parity-wasm (origin: Substrate) + "RUSTSEC-2022-0061", + # atty (origin: Substrate, clap) + "RUSTSEC-2021-0145", + # wasmtime (origin: Substrate) + "RUSTSEC-2022-0076", +] +# Threshold for security vulnerabilities, any vulnerability with a CVSS score +# lower than the range specified will be ignored. Note that ignored advisories +# will still output a note when they are encountered. +# * None - CVSS Score 0.0 +# * Low - CVSS Score 0.1 - 3.9 +# * Medium - CVSS Score 4.0 - 6.9 +# * High - CVSS Score 7.0 - 8.9 +# * Critical - CVSS Score 9.0 - 10.0 +#severity-threshold = + +# This section is considered when running `cargo deny check licenses` +# More documentation for the licenses section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html +[licenses] +# The lint level for crates which do not have a detectable license +unlicensed = "allow" +# List of explictly allowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.7 short identifier (+ optional exception)]. +allow = [ + "BlueOak-1.0.0" +] +# List of explictly disallowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.7 short identifier (+ optional exception)]. +deny = [ + #"Nokia", +] +# Lint level for licenses considered copyleft +copyleft = "allow" +# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses +# * both - The license will be approved if it is both OSI-approved *AND* FSF +# * either - The license will be approved if it is either OSI-approved *OR* FSF +# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF +# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved +# * neither - This predicate is ignored and the default lint level is used +allow-osi-fsf-free = "either" +# Lint level used when no other predicates are matched +# 1. License isn't in the allow or deny lists +# 2. License isn't copyleft +# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" +default = "deny" +# The confidence threshold for detecting a license from license text. +# The higher the value, the more closely the license text must be to the +# canonical license text of a valid SPDX license file. +# [possible values: any between 0.0 and 1.0]. +confidence-threshold = 0.9 +# Allow 1 or more licenses on a per-crate basis, so that particular licenses +# aren't accepted for every possible crate as with the normal allow list +exceptions = [ + # Each entry is the crate and version constraint, and its specific allow + # list + #{ allow = ["Zlib"], name = "adler32", version = "*" }, +] + +# Some crates don't have (easily) machine readable licensing information, +# adding a clarification entry for it allows you to manually specify the +# licensing information +[[licenses.clarify]] +# The name of the crate the clarification applies to +name = "ring" +# THe optional version constraint for the crate +#version = "*" +# The SPDX expression for the license requirements of the crate +expression = "OpenSSL" +# One or more files in the crate's source used as the "source of truth" for +# the license expression. If the contents match, the clarification will be used +# when running the license check, otherwise the clarification will be ignored +# and the crate will be checked normally, which may produce warnings or errors +# depending on the rest of your configuration +license-files = [ + # Each entry is a crate relative path, and the (opaque) hash of its contents + { path = "LICENSE", hash = 0xbd0eed23 } +] + +[[licenses.clarify]] +name = "webpki" +expression = "ISC" +license-files = [{ path = "LICENSE", hash = 0x001c7e6c }] + +[licenses.private] +# If true, ignores workspace crates that aren't published, or are only +# published to private registries +ignore = false +# One or more private registries that you might publish crates to, if a crate +# is only published to private registries, and ignore is true, the crate will +# not have its license(s) checked +registries = [ + #"https://sekretz.com/registry +] + +# This section is considered when running `cargo deny check bans`. +# More documentation about the 'bans' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html +[bans] +# Lint level for when multiple versions of the same crate are detected +multiple-versions = "warn" +# The graph highlighting used when creating dotgraphs for crates +# with multiple versions +# * lowest-version - The path to the lowest versioned duplicate is highlighted +# * simplest-path - The path to the version with the fewest edges is highlighted +# * all - Both lowest-version and simplest-path are used +highlight = "lowest-version" +# List of crates that are allowed. Use with care! +allow = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +# List of crates to deny +deny = [ + { name = "parity-util-mem", version = "<0.6" } + # Each entry the name of a crate and a version range. If version is + # not specified, all versions will be matched. +] +# Certain crates/versions that will be skipped when doing duplicate detection. +skip = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +# Similarly to `skip` allows you to skip certain crates during duplicate +# detection. Unlike skip, it also includes the entire tree of transitive +# dependencies starting at the specified crate, up to a certain depth, which is +# by default infinite +skip-tree = [ + #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, +] + +# This section is considered when running `cargo deny check sources`. +# More documentation about the 'sources' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html +[sources] +# Lint level for what to happen when a crate from a crate registry that is not +# in the allow list is encountered +unknown-registry = "deny" +# Lint level for what to happen when a crate from a git repository that is not +# in the allow list is encountered +unknown-git = "allow" +# List of URLs for allowed crate registries. Defaults to the crates.io index +# if not specified. If it is specified but empty, no registries are allowed. +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +# List of URLs for allowed Git repositories +allow-git = [] diff --git a/deployments/README.md b/deployments/README.md new file mode 100644 index 00000000000..e478f7df84a --- /dev/null +++ b/deployments/README.md @@ -0,0 +1,270 @@ +# Bridge Deployments + +## Requirements + +Make sure to install `docker` and `docker-compose` to be able to run and test bridge deployments. If +for whatever reason you can't or don't want to use Docker, you can find some scripts for running the +bridge [here](./local-scripts/). + +## Networks + +One of the building blocks we use for our deployments are _networks_. A network is a collection of +homogenous blockchain nodes. We have Docker Compose files for each network that we want to bridge. +Each of the compose files found in the `./networks` folder is able to independently spin up a +network like so: + +```bash +docker-compose -f ./networks/rialto.yml up +``` + +After running this command we would have a network of several nodes producing blocks. + +## Bridges + +A _bridge_ is a way for several _networks_ to connect to one another. Bridge deployments have their +own Docker Compose files which can be found in the `./bridges` folder. These Compose files typically +contain bridge relayers, which are services external to blockchain nodes, and other components such +as testing infrastructure, or user interfaces. + +Unlike the network Compose files, these *cannot* be deployed on their own. They must be combined +with different networks. + +In general, we can deploy the bridge using `docker-compose up` in the following way: + +```bash +docker-compose -f .yml \ + -f .yml \ + -f .yml \ + -f .yml up +``` + +If you want to see how the Compose commands are actually run, check out the source code of the +[`./run.sh`](./run.sh). + +One thing worth noting is that we have a _monitoring_ Compose file. This adds support for Prometheus +and Grafana. We cover these in more details in the [Monitoring](#monitoring) section. At the moment +the monitoring Compose file is _not_ optional, and must be included for bridge deployments. + +### Running and Updating Deployments + +We currently support three bridge deployments +1. Rialto Substrate to Millau Substrate +2. Rialto Parachain Substrate to Millau Substrate +2. Westend Substrate to Millau Substrate (only finality bridge) + +These bridges can be deployed using our [`./run.sh`](./run.sh) script. + +The first argument it takes is the name of the bridge you want to run. Right now we only support three +bridges: `rialto-millau`, `rialto-parachain-millau` and `westend-millau`. + +```bash +./run.sh rialto-millau +``` + +If you add a second `update` argument to the script it will pull the latest images from Docker Hub +and restart the deployment. + +```bash +./run.sh rialto-millau update +``` + +You can also bring down a deployment using the script with the `stop` argument. + +```bash +./run.sh rialto-millau stop +``` + +### Adding Deployments + +We need two main things when adding a new deployment. First, the new network which we want to +bridge. A compose file for the network should be added in the `/networks/` folder. Secondly we'll +need a new bridge Compose file in `./bridges/`. This should configure the bridge relayer nodes +correctly for the two networks, and add any additional components needed for the deployment. If you +want you can also add support in the `./run.sh` script for the new deployment. While recommended it's +not strictly required. + +## General Notes + +Rialto authorities are named: `Alice`, `Bob`, `Charlie`, `Dave`, `Eve`. +Millau authorities are named: `Alice`, `Bob`, `Charlie`, `Dave`, `Eve`. +RialtoParachain authorities are named: `Alice`, `Bob`. + +`Sudo` is a sudo account on all chains. + +Both authorities and following accounts have enough funds (for test purposes) on corresponding Substrate chains: + +- on Rialto: `Ferdie`. +- on Millau: `Ferdie`. +- on RialtoParachain: `Charlie`, `Dave`, `Eve`, `Ferdie`. + +Names of accounts on Substrate (Rialto and Millau) chains may be prefixed with `//` and used as +seeds for the `sr25519` keys. This seed may also be used in the signer argument in Substrate relays. +Example: + +```bash +./substrate-relay relay-headers rialto-to-millau \ + --source-host rialto-node-alice \ + --source-port 9944 \ + --target-host millau-node-alice \ + --target-port 9944 \ + --source-signer //Ferdie \ + --prometheus-host=0.0.0.0 +``` + +Some accounts are used by bridge components. Using these accounts to sign other transactions +is not recommended, because this may lead to nonces conflict. + +Following accounts are used when `rialto-millau` bridge is running: + +- Millau's `Rialto.HeadersAndMessagesRelay1` signs complex headers+messages relay transactions on Millau chain; +- Rialto's `Millau.HeadersAndMessagesRelay1` signs complex headers+messages relay transactions on Rialto chain; +- Millau's `Rialto.MessagesSender` signs Millau transactions which contain messages for Rialto; +- Rialto's `Millau.MessagesSender` signs Rialto transactions which contain messages for Millau; +- Millau's `Rialto.OutboundMessagesRelay.Lane00000001` signs relay transactions with message delivery confirmations (lane 00000001) from Rialto to Millau; +- Rialto's `Millau.InboundMessagesRelay.Lane00000001` signs relay transactions with messages (lane 00000001) from Millau to Rialto; +- Millau's `Millau.OutboundMessagesRelay.Lane00000001` signs relay transactions with messages (lane 00000001) from Rialto to Millau; +- Rialto's `Rialto.InboundMessagesRelay.Lane00000001` signs relay transactions with message delivery confirmations (lane 00000001) from Millau to Rialto; +- Millau's `Rialto.MessagesOwner` signs relay transactions with updated Rialto -> Millau conversion rate; +- Rialto's `Millau.MessagesOwner` signs relay transactions with updated Millau -> Rialto conversion rate. + +Following accounts are used when `westend-millau` bridge is running: + +- Millau's `Westend.GrandpaOwner` is signing with-Westend GRANDPA pallet initialization transaction. +- Millau's `Westend.HeadersRelay1` and `Westend.HeadersRelay2` are signing transactions with new Westend headers. +- Millau's `Westend.WestmintHeaders1` and `Westend.WestmintHeaders2` is signing transactions with new Westming headers. + +Following accounts are used when `rialto-parachain-millau` bridge is running: + +- RialtoParachain's `Millau.MessagesSender` signs RialtoParachain transactions which contain messages for Millau; +- Millau's `RialtoParachain.MessagesSender` signs Millau transactions which contain messages for RialtoParachain; +- Millau's `RialtoParachain.HeadersAndMessagesRelay` signs complex headers+parachains+messages relay transactions on Millau chain; +- RialtoParachain's `Millau.HeadersAndMessagesRelay` signs complex headers+messages relay transactions on RialtoParachain chain. + +### Docker Usage + +When the network is running you can query logs from individual nodes using: + +```bash +docker logs rialto_millau-node-charlie_1 -f +``` + +You may use the [dump-logs.sh](../scripts/dump-logs.sh) to dump logs of most of running containers. + +To kill all leftover containers and start the network from scratch next time: +```bash +docker ps -a --format "{{.ID}}" | xargs docker rm # This removes all containers! +``` + +### Docker Compose Usage + +If you're not familiar with how to use `docker-compose` here are some useful commands you'll need +when interacting with the bridge deployments: + +```bash +docker-compose pull # Get the latest images from the Docker Hub +docker-compose build # This is going to build images +docker-compose up # Start all the nodes +docker-compose up -d # Start the nodes in detached mode. +docker-compose down # Stop the network. +``` + +Note that you'll also need to add the appropriate `-f` arguments that were mentioned in the +[Bridges](#bridges) section. You can read more about using multiple Compose files +[here](https://docs.docker.com/compose/extends/#multiple-compose-files). One thing worth noting is +that the _order_ the compose files are specified in matters. A different order will result in a +different configuration. + +You can sanity check the final config like so: + +```bash +docker-compose -f docker-compose.yml -f docker-compose.override.yml config > docker-compose.merged.yml +``` + +## Docker and Git Deployment + +It is also possible to avoid using images from the Docker Hub and instead build +containers from Git. There are two ways to build the images this way. + +### Git Repo + +If you have cloned the bridges repo you can build local Docker images by running the following +command at the top level of the repo: + +```bash +docker build . -t local/ --build-arg=PROJECT= +``` + +This will build a local image of a particular component with a tag of +`local/`. This tag can be used in Docker Compose files. + +You can configure the build using Docker +[build arguments](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg). +Here are the arguments currently supported: + - `PROJECT`: Project to build withing bridges repo. Can be one of: + - `rialto-bridge-node` + - `millau-bridge-node` + - `rialto-parachain-collator` + - `substrate-relay` + +You may use the [build-containers.sh](../scripts/build-containers.sh) script to build all available +containers. + +### GitHub Actions + +We have a nightly job which runs and publishes Docker images for the different nodes and relayers to +the [ParityTech Docker Hub](https://hub.docker.com/u/paritytech) organization. These images are used +for our ephemeral (temporary) test networks. Additionally, any time a tag in the form of `v*` is +pushed to GitHub the publishing job is run. This will build all the components (nodes, relayers) and +publish them. + +With images built using either method, all you have to do to use them in a deployment is change the +`image` field in the existing Docker Compose files to point to the tag of the image you want to use. + +### Monitoring + +[Prometheus](https://prometheus.io/) is used by the bridge relay to monitor information such as system +resource use, and block data (e.g the best blocks it knows about). In order to visualize this data +a [Grafana](https://grafana.com/) dashboard can be used. + +As part of the Rialto `docker-compose` setup we spin up a Prometheus server and Grafana dashboard. The +Prometheus server connects to the Prometheus data endpoint exposed by the bridge relay. The Grafana +dashboard uses the Prometheus server as its data source. + +The default port for the bridge relay's Prometheus data is `9616`. The host and port can be +configured though the `--prometheus-host` and `--prometheus-port` flags. The Prometheus server's +dashboard can be accessed at `http://localhost:9090`. The Grafana dashboard can be accessed at +`http://localhost:3000`. Note that the default log-in credentials for Grafana are `admin:admin`. + +### Environment Variables + +Here is an example `.env` file which is used for production deployments and network updates. For +security reasons it is not kept as part of version control. When deploying a network this +file should be correctly populated and kept in the appropriate [`bridges`](`./bridges`) deployment +folder. + +The `UI_SUBSTRATE_PROVIDER` variable lets you define the url of the Substrate node that the user +interface will connect to. `UI_ETHEREUM_PROVIDER` is used only as a guidance for users to connect +Metamask to the right Ethereum network. `UI_EXPECTED_ETHEREUM_NETWORK_ID` is used by +the user interface as a fail safe to prevent users from connecting their Metamask extension to an +unexpected network. + +```bash +GRAFANA_ADMIN_PASS=admin_pass +GRAFANA_SERVER_ROOT_URL=%(protocol)s://%(domain)s:%(http_port)s/ +GRAFANA_SERVER_DOMAIN=server.domain.io +MATRIX_ACCESS_TOKEN="access-token" +WITH_PROXY=1 # Optional +UI_SUBSTRATE_PROVIDER=ws://localhost:9944 +UI_ETHEREUM_PROVIDER=http://localhost:8545 +UI_EXPECTED_ETHEREUM_NETWORK_ID=105 +``` + +### UI + +Use [wss://wss.rialto.brucke.link](https://polkadot.js.org/apps/) as a custom endpoint for +[https://polkadot.js.org/apps/](https://polkadot.js.org/apps/). + +## Scripts + +There are some bash scripts in `scripts` folder that allow testing `Relay` +without running the entire network within docker. Use if needed for development. diff --git a/deployments/bridges/common/generate_messages.sh b/deployments/bridges/common/generate_messages.sh new file mode 100755 index 00000000000..0987ff8987e --- /dev/null +++ b/deployments/bridges/common/generate_messages.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +# Script for generating messages from a source chain to a target chain. +# Prerequisites: mounting the common folder in the docker container (Adding the following volume entry): +# - ./bridges/common:/common +# It can be used by executing `source /common/generate_messages.sh` in a different script, +# after setting the following variables: +# SOURCE_CHAIN +# TARGET_CHAIN +# MAX_SUBMIT_DELAY_S +# SEND_MESSAGE - the command that is executed to send a message +# SECONDARY_EXTRA_ARGS - optional, for example "--use-xcm-pallet" +# EXTRA_ARGS - for example "--use-xcm-pallet" +# REGULAR_PAYLOAD +# MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE + +SECONDARY_EXTRA_ARGS=${SECONDARY_EXTRA_ARGS:-""} + +trap "echo Exiting... TERM; exit $?" TERM + +# Sleep a bit between messages +rand_sleep() { + SUBMIT_DELAY_S=`shuf -i 0-$MAX_SUBMIT_DELAY_S -n 1` + echo "Sleeping $SUBMIT_DELAY_S seconds..." + sleep $SUBMIT_DELAY_S & wait $! + NOW=`date "+%Y-%m-%d %H:%M:%S"` + echo "Woke up at $NOW" +} + +# start sending large messages immediately +LARGE_MESSAGES_TIME=0 +# start sending message packs in a hour +BUNCH_OF_MESSAGES_TIME=3600 + +while true +do + rand_sleep + + # send regular message + echo "Sending Message from $SOURCE_CHAIN to $TARGET_CHAIN" + $SEND_MESSAGE $EXTRA_ARGS raw $REGULAR_PAYLOAD + + # every other hour we're sending 3 large (size, weight, size+weight) messages + if [ $SECONDS -ge $LARGE_MESSAGES_TIME ]; then + LARGE_MESSAGES_TIME=$((SECONDS + 7200)) + + rand_sleep + echo "Sending Maximal Size Message from $SOURCE_CHAIN to $TARGET_CHAIN" + $SEND_MESSAGE \ + sized max + fi + + # every other hour we're sending a bunch of small messages + if [ $SECONDS -ge $BUNCH_OF_MESSAGES_TIME ]; then + BUNCH_OF_MESSAGES_TIME=$((SECONDS + 7200)) + + for i in $(seq 0 $MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE); + do + $SEND_MESSAGE \ + $EXTRA_ARGS \ + raw $REGULAR_PAYLOAD + done + + fi +done diff --git a/deployments/bridges/rialto-millau/dashboard/grafana/relay-millau-to-rialto-messages-dashboard.json b/deployments/bridges/rialto-millau/dashboard/grafana/relay-millau-to-rialto-messages-dashboard.json new file mode 100644 index 00000000000..af8749325de --- /dev/null +++ b/deployments/bridges/rialto-millau/dashboard/grafana/relay-millau-to-rialto-messages-dashboard.json @@ -0,0 +1,1153 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Millau_to_Rialto_MessageLane_00000000_best_target_block_number", + "instant": false, + "interval": "", + "legendFormat": "At Rialto", + "refId": "A" + }, + { + "expr": "Millau_to_Rialto_MessageLane_00000000_best_target_at_source_block_number", + "instant": false, + "interval": "", + "legendFormat": "At Millau", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized Rialto headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Millau_to_Rialto_MessageLane_00000000_best_source_block_number", + "interval": "", + "legendFormat": "At Millau", + "refId": "A" + }, + { + "expr": "Millau_to_Rialto_MessageLane_00000000_best_source_at_target_block_number", + "interval": "", + "legendFormat": "At Rialto", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized Millau headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Millau -> Rialto messages are not detected by relay", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from Rialto\", \"type\", \"source_latest_generated\"), \"type\", \"Latest Rialto message received by Millau\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + }, + { + "expr": "increase(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"source_latest_generated\"}[10m]) OR on() vector(0)", + "hide": true, + "interval": "", + "legendFormat": "Messages generated in last 5 minutes (Millau -> Rialto, 00000000)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest message confirmed by Rialto to Millau\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest Rialto message received by Millau\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "sum" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "7m", + "frequency": "1m", + "handler": 1, + "name": "Messages from Millau to Rialto are not being delivered", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 20 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"source_latest_generated\"}[2m])) - scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]))", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "Undelivered messages at Rialto", + "refId": "A" + }, + { + "expr": "increase(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[5m]) OR on() vector(0)", + "interval": "", + "legendFormat": "Millau Messages delivered to Rialto in last 5m", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 50 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Too many unconfirmed messages (Millau -> Rialto)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 20 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "legendFormat": "Unconfirmed messages at Millau", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Rewards are not being confirmed (Millau -> Rialto messages)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 20 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"source_latest_confirmed\"}[2m])) - scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_confirmed\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at Rialto", + "refId": "A" + }, + { + "expr": "(scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]) > bool min_over_time(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at Millau->Rialto (zero if messages are not being delivered to Rialto)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Reward lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 38 + }, + "id": 16, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "7.1.3", + "targets": [ + { + "expr": "avg_over_time(process_cpu_usage_percentage{instance='relay-millau-rialto:9616'}[1m])", + "instant": true, + "interval": "", + "legendFormat": "1 CPU = 100", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Relay process CPU usage (1 CPU = 100)", + "type": "gauge" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 38 + }, + "hiddenSeries": false, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "system_average_load{instance='relay-millau-rialto:9616'}", + "interval": "", + "legendFormat": "Average system load in last {{over}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "System load average", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 38 + }, + "hiddenSeries": false, + "id": 20, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "process_memory_usage_bytes{instance='relay-millau-rialto:9616'} / 1024 / 1024", + "interval": "", + "legendFormat": "Process memory, MB", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory used by relay process", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Millau to Rialto Message Sync Dashboard", + "uid": "relay-millau-to-rialto-messages", + "version": 1 +} diff --git a/deployments/bridges/rialto-millau/dashboard/grafana/relay-rialto-to-millau-messages-dashboard.json b/deployments/bridges/rialto-millau/dashboard/grafana/relay-rialto-to-millau-messages-dashboard.json new file mode 100644 index 00000000000..efee749c5c1 --- /dev/null +++ b/deployments/bridges/rialto-millau/dashboard/grafana/relay-rialto-to-millau-messages-dashboard.json @@ -0,0 +1,1145 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 4, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Rialto_to_Millau_MessageLane_00000000_best_target_block_number", + "instant": false, + "interval": "", + "legendFormat": "At Millau", + "refId": "A" + }, + { + "expr": "Rialto_to_Millau_MessageLane_00000000_best_target_at_source_block_number", + "instant": false, + "interval": "", + "legendFormat": "At Rialto", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized Millau headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Rialto_to_Millau_MessageLane_00000000_best_source_block_number", + "interval": "", + "legendFormat": "At Rialto", + "refId": "A" + }, + { + "expr": "Rialto_to_Millau_MessageLane_00000000_best_source_at_target_block_number", + "interval": "", + "legendFormat": "At Millau", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized Rialto headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Rialto -> Millau messages are not detected by relay", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from Millau\", \"type\", \"source_latest_generated\"), \"type\", \"Latest message received by Rialto\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + }, + { + "expr": "increase(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"source_latest_generated\"}[10m]) OR on() vector(0)", + "hide": true, + "interval": "", + "legendFormat": "Messages generated in last 5 minutes (Rialto -> Millau, 00000000)", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest message confirmed by Millau to Rialto\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest message received by Rialto\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "sum" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "7m", + "frequency": "1m", + "handler": 1, + "name": "Messages from Rialto to Millau are not being delivered", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 20 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"source_latest_generated\"}[2m])) - scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]))", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "Undelivered messages at Millau", + "refId": "A" + }, + { + "expr": "increase(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[5m]) OR on() vector(0)", + "interval": "", + "legendFormat": "Rialto Messages delivered to Millau in last 5m", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 50 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Too many unconfirmed messages (Rialto -> Millau)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 20 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "legendFormat": "Unconfirmed messages at Rialto", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Rewards are not being confirmed (Rialto -> Millau messages)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 20 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"source_latest_confirmed\"}[2m])) - scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_confirmed\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at Millau", + "refId": "A" + }, + { + "expr": "(scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]) > bool min_over_time(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at Millau (zero if messages are not being delivered to Millau)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Reward lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 38 + }, + "id": 16, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "7.1.3", + "targets": [ + { + "expr": "avg_over_time(process_cpu_usage_percentage{instance='relay-millau-rialto:9616'}[1m])", + "instant": true, + "interval": "", + "legendFormat": "1 CPU = 100", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Relay process CPU usage (1 CPU = 100)", + "type": "gauge" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 38 + }, + "hiddenSeries": false, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "system_average_load{instance='relay-millau-rialto:9616'}", + "interval": "", + "legendFormat": "Average system load in last {{over}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "System load average", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 38 + }, + "hiddenSeries": false, + "id": 20, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "process_memory_usage_bytes{instance='relay-millau-rialto:9616'} / 1024 / 1024", + "interval": "", + "legendFormat": "Process memory, MB", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory used by relay process", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Rialto to Millau Message Sync Dashboard", + "uid": "relay-rialto-to-millau-messages", + "version": 2 +} diff --git a/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json b/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json new file mode 100644 index 00000000000..4f134914f70 --- /dev/null +++ b/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json @@ -0,0 +1,635 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "liveNow": false, + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "At-Rialto relay balances are too low", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.2.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "at_Rialto_relay_MillauHeaders_balance", + "interval": "", + "legendFormat": "With-Millau headers relay account balance", + "refId": "A" + }, + { + "expr": "at_Rialto_relay_MillauMessages_balance", + "interval": "", + "legendFormat": "With-Millau messages relay account balance", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1000, + "visible": true + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Rialto relay balances", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "At-Millau relay balances are too low", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 9, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.2.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "at_Millau_relay_RialtoHeaders_balance{instance=\"relay-millau-rialto:9616\"}", + "interval": "", + "legendFormat": "With-Rialto headers relay account balance", + "refId": "A" + }, + { + "expr": "at_Millau_relay_RialtoMessages_balance", + "interval": "", + "legendFormat": "With-Rialto messages relay account balance", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1000, + "visible": true + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Millau relay balances", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Whether with-Rialto-grandpa-pallet and Rialto itself are on different forks alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "hiddenSeries": false, + "id": 11, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.2.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Rialto_to_Millau_MessageLane_00000000_is_source_and_source_at_target_using_different_forks OR on() vector(0)", + "interval": "", + "legendFormat": "On different forks?", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0, + "visible": true + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Whether with-Rialto-grandpa-pallet and Rialto itself are on different forks", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Whether with-Rialto-grandpa-pallet and Rialto itself are on different forks alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.2.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Millau_to_Rialto_MessageLane_00000000_is_source_and_source_at_target_using_different_forks OR on() vector(0)", + "interval": "", + "legendFormat": "On different forks?", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0, + "visible": true + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Whether with-Millau-grandpa-pallet and Millau itself are on different forks", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "10s", + "schemaVersion": 32, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Rialto+Millau maintenance dashboard", + "uid": "7AuyrjlMz", + "version": 1 +} diff --git a/deployments/bridges/rialto-millau/dashboard/prometheus/targets.yml b/deployments/bridges/rialto-millau/dashboard/prometheus/targets.yml new file mode 100644 index 00000000000..75c3e0c2aa9 --- /dev/null +++ b/deployments/bridges/rialto-millau/dashboard/prometheus/targets.yml @@ -0,0 +1,2 @@ +- targets: + - relay-millau-rialto:9616 diff --git a/deployments/bridges/rialto-millau/docker-compose.yml b/deployments/bridges/rialto-millau/docker-compose.yml new file mode 100644 index 00000000000..5d15a0398b1 --- /dev/null +++ b/deployments/bridges/rialto-millau/docker-compose.yml @@ -0,0 +1,88 @@ +# Exposed ports: 10016, 10116, 10216, 10316, 10416, 10516, 10716 + +version: '3.5' +services: + # We provide overrides for these particular nodes since they are public facing + # nodes which we use to connect from things like Polkadot JS Apps. + rialto-node-charlie: + environment: + VIRTUAL_HOST: wss.rialto.brucke.link + VIRTUAL_PORT: 9944 + LETSENCRYPT_HOST: wss.rialto.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + + millau-node-charlie: + environment: + VIRTUAL_HOST: wss.millau.brucke.link + VIRTUAL_PORT: 9944 + LETSENCRYPT_HOST: wss.millau.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + + relay-millau-rialto: &sub-bridge-relay + image: ${SUBSTRATE_RELAY_IMAGE:-paritytech/substrate-relay} + entrypoint: /entrypoints/relay-millau-rialto-entrypoint.sh + volumes: + - ./bridges/common:/common + - ./bridges/rialto-millau/entrypoints:/entrypoints + environment: + RUST_LOG: rpc=trace,bridge=trace + GLOBAL_DEPLOYMENTS: $GLOBAL_DEPLOYMENTS + ports: + - "10016:9616" + depends_on: &all-nodes + - millau-node-alice + - millau-node-bob + - millau-node-charlie + - millau-node-dave + - millau-node-eve + - rialto-node-alice + - rialto-node-bob + - rialto-node-charlie + - rialto-node-dave + - rialto-node-eve + + relay-messages-millau-to-rialto-generator: + <<: *sub-bridge-relay + environment: + RUST_LOG: bridge=trace + entrypoint: /entrypoints/relay-messages-to-rialto-generator-entrypoint.sh + ports: + - "10216:9616" + depends_on: + - relay-millau-rialto + + relay-messages-millau-to-rialto-resubmitter: + <<: *sub-bridge-relay + environment: + RUST_LOG: bridge=trace + entrypoint: /entrypoints/relay-messages-to-rialto-resubmitter-entrypoint.sh + ports: + - "10316:9616" + depends_on: + - relay-messages-millau-to-rialto-generator + + relay-messages-rialto-to-millau-generator: + <<: *sub-bridge-relay + environment: + RUST_LOG: bridge=trace + entrypoint: /entrypoints/relay-messages-to-millau-generator-entrypoint.sh + ports: + - "10516:9616" + depends_on: + - relay-millau-rialto + + # Note: These are being overridden from the top level `monitoring` compose file. + grafana-dashboard: + environment: + VIRTUAL_HOST: grafana.millau.brucke.link,grafana.rialto.brucke.link + VIRTUAL_PORT: 3000 + LETSENCRYPT_HOST: grafana.millau.brucke.link,grafana.rialto.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + volumes: + - ./bridges/rialto-millau/dashboard/grafana:/etc/grafana/dashboards/rialto-millau:ro + - ./networks/dashboard/grafana/beefy-dashboard.json:/etc/grafana/dashboards/beefy.json + + prometheus-metrics: + volumes: + - ./bridges/rialto-millau/dashboard/prometheus/targets.yml:/etc/prometheus/targets-rialto-millau.yml + depends_on: *all-nodes diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-messages-millau-to-rialto-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-messages-millau-to-rialto-entrypoint.sh new file mode 100755 index 00000000000..3a55cc782ca --- /dev/null +++ b/deployments/bridges/rialto-millau/entrypoints/relay-messages-millau-to-rialto-entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -xeu + +sleep 15 + +MESSAGE_LANE=${MSG_EXCHANGE_GEN_LANE:-00000000} + +/home/user/substrate-relay relay-messages millau-to-rialto \ + --lane $MESSAGE_LANE \ + --source-host millau-node-bob \ + --source-port 9944 \ + --source-signer //Rialto.OutboundMessagesRelay.Lane00000001 \ + --target-host rialto-node-bob \ + --target-port 9944 \ + --target-signer //Millau.InboundMessagesRelay.Lane00000001 \ + --prometheus-host=0.0.0.0 diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-messages-rialto-to-millau-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-messages-rialto-to-millau-entrypoint.sh new file mode 100755 index 00000000000..145cd20100d --- /dev/null +++ b/deployments/bridges/rialto-millau/entrypoints/relay-messages-rialto-to-millau-entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -xeu + +sleep 15 + +MESSAGE_LANE=${MSG_EXCHANGE_GEN_LANE:-00000000} + +/home/user/substrate-relay relay-messages rialto-to-millau \ + --lane $MESSAGE_LANE \ + --source-host rialto-node-bob \ + --source-port 9944 \ + --source-signer //Millau.OutboundMessagesRelay.Lane00000001 \ + --target-host millau-node-bob \ + --target-port 9944 \ + --target-signer //Rialto.InboundMessagesRelay.Lane00000001 \ + --prometheus-host=0.0.0.0 diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh new file mode 100755 index 00000000000..a36538ea515 --- /dev/null +++ b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# THIS SCRIPT IS NOT INTENDED FOR USE IN PRODUCTION ENVIRONMENT +# +# This scripts periodically calls the Substrate relay binary to generate messages. These messages +# are sent from the Rialto network to the Millau network. + +set -eu + +# Max delay before submitting transactions (s) +MAX_SUBMIT_DELAY_S=${MSG_EXCHANGE_GEN_MAX_SUBMIT_DELAY_S:-30} +MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE=1024 + +SHARED_CMD="/home/user/substrate-relay send-message rialto-to-millau" +SHARED_HOST="--source-host rialto-node-bob --source-port 9944" +SOURCE_SIGNER="--source-signer //Millau.MessagesSender" + +SEND_MESSAGE="$SHARED_CMD $SHARED_HOST $SOURCE_SIGNER" + +SOURCE_CHAIN="Rialto" +TARGET_CHAIN="Millau" +EXTRA_ARGS="" +# It is the encoded `xcm::VersionedXcm::V3(prepare_outbound_xcm_message(MillauNetwork::get())` +# from the `xcm_messages_to_millau_are_sent_using_bridge_exporter` test in the `rialto-runtime` +REGULAR_PAYLOAD="030426030109030419a8" + +source /common/generate_messages.sh diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh new file mode 100755 index 00000000000..dacb5615229 --- /dev/null +++ b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# THIS SCRIPT IS NOT INTENDED FOR USE IN PRODUCTION ENVIRONMENT +# +# This scripts periodically calls the Substrate relay binary to generate messages. These messages +# are sent from the Millau network to the Rialto network. + +set -eu + +# Max delay before submitting transactions (s) +MAX_SUBMIT_DELAY_S=${MSG_EXCHANGE_GEN_MAX_SUBMIT_DELAY_S:-30} +MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE=128 + +SHARED_CMD=" /home/user/substrate-relay send-message millau-to-rialto" +SHARED_HOST="--source-host millau-node-bob --source-port 9944" +SOURCE_SIGNER="--source-signer //Rialto.MessagesSender" + +SEND_MESSAGE="$SHARED_CMD $SHARED_HOST $SOURCE_SIGNER" + +SOURCE_CHAIN="Millau" +TARGET_CHAIN="Rialto" +EXTRA_ARGS="" +# It is the encoded `xcm::VersionedXcm::V3(prepare_outbound_xcm_message(RialtoNetwork::get())` +# from the `xcm_messages_to_rialto_are_sent_using_bridge_exporter` test in the `millau-runtime` +REGULAR_PAYLOAD="030426020109020419a8" + +source /common/generate_messages.sh diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-resubmitter-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-resubmitter-entrypoint.sh new file mode 100755 index 00000000000..bd9bf800ad4 --- /dev/null +++ b/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-resubmitter-entrypoint.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -xeu + +sleep 15 + +# //Rialto.MessagesSender is signing Millau -> Rialto message-send transactions, which are causing problems. +# +# When large message is being sent from Millau to Rialto AND other transactions are +# blocking it from being mined, we'll see something like this in logs: +# +# Millau transaction priority with tip=0: 17800827994. Target priority: +# 526186677695 +# +# So since fee multiplier in Millau is `1` and `WeightToFee` is `IdentityFee`, then +# we need tip around `526186677695 - 17800827994 = 508_385_849_701`. Let's round it +# up to `1_000_000_000_000`. + +/home/user/substrate-relay resubmit-transactions millau \ + --target-host millau-node-alice \ + --target-port 9944 \ + --target-signer //Rialto.MessagesSender \ + --stalled-blocks 5 \ + --tip-limit 1000000000000 \ + --tip-step 1000000000 \ + make-it-best-transaction diff --git a/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh b/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh new file mode 100755 index 00000000000..a1306984bca --- /dev/null +++ b/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -xeu + +sleep 15 + +/home/user/substrate-relay init-bridge millau-to-rialto \ + --source-host millau-node-alice \ + --source-port 9944 \ + --target-host rialto-node-alice \ + --target-port 9944 \ + --target-signer //Sudo + +/home/user/substrate-relay init-bridge rialto-to-millau \ + --source-host rialto-node-alice \ + --source-port 9944 \ + --target-host millau-node-alice \ + --target-port 9944 \ + --target-signer //Sudo + +# Give chain a little bit of time to process initialization transaction +sleep 6 + +RIALTO_NODE_CONNECTION_PARAMS=$([ -z ${GLOBAL_DEPLOYMENTS} ] && \ + echo "--rialto-host rialto-node-alice --rialto-port 9944" \ + || \ + echo "--rialto-host wss.rialto.brucke.link --rialto-port 443 --rialto-secure" ) + +/home/user/substrate-relay relay-headers-and-messages millau-rialto \ + --millau-host millau-node-alice \ + --millau-port 9944 \ + --millau-signer //Rialto.HeadersAndMessagesRelay \ + --millau-transactions-mortality=64 \ + $RIALTO_NODE_CONNECTION_PARAMS \ + --rialto-signer //Millau.HeadersAndMessagesRelay \ + --rialto-transactions-mortality=64 \ + --lane=00000000 \ + --prometheus-host=0.0.0.0 diff --git a/deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-millau-to-rialto-parachain-messages-dashboard.json b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-millau-to-rialto-parachain-messages-dashboard.json new file mode 100644 index 00000000000..b20348cc513 --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-millau-to-rialto-parachain-messages-dashboard.json @@ -0,0 +1,910 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Millau_to_RialtoParachain_MessageLane_00000000_best_target_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "instant": false, + "interval": "", + "legendFormat": "At RialtoParachain", + "refId": "A" + }, + { + "expr": "Millau_to_RialtoParachain_MessageLane_00000000_best_target_at_source_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "instant": false, + "interval": "", + "legendFormat": "At Millau", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized RialtoParachain headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Millau_to_RialtoParachain_MessageLane_00000000_best_source_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "At Millau", + "refId": "A" + }, + { + "expr": "Millau_to_RialtoParachain_MessageLane_00000000_best_source_at_target_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "At RialtoParachain", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized Millau headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Millau -> RialtoParachain messages are not detected by relay", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from RialtoParachain\", \"type\", \"source_latest_generated\"), \"type\", \"Latest RialtoParachain message received by Millau\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + }, + { + "expr": "increase(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_generated\"}[10m]) OR on() vector(0)", + "hide": true, + "interval": "", + "legendFormat": "Messages generated in last 5 minutes (Millau -> RialtoParachain, 00000000)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest message confirmed by RialtoParachain to Millau\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest RialtoParachain message received by Millau\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "sum" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "7m", + "frequency": "1m", + "handler": 1, + "name": "Messages from Millau to RialtoParachain are not being delivered", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 20 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_generated\"}[2m])) - scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]))", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "Undelivered messages at RialtoParachain", + "refId": "A" + }, + { + "expr": "increase(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[5m]) OR on() vector(0)", + "interval": "", + "legendFormat": "Millau Messages delivered to RialtoParachain in last 5m", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 50 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Too many unconfirmed messages (Millau -> RialtoParachain)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 20 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "legendFormat": "Unconfirmed messages at Millau", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Rewards are not being confirmed (Millau -> RialtoParachain messages)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 20 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_confirmed\"}[2m])) - scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_confirmed\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at RialtoParachain", + "refId": "A" + }, + { + "expr": "(scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]) > bool min_over_time(Millau_to_RialtoParachain_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at Millau->RaltoParachain (zero if messages are not being delivered to RialtoParachain)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Reward lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Millau to RialtoParachain Message Sync Dashboard", + "uid": "C61e-797z", + "version": 1 +} diff --git a/deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-rialto-parachain-to-millau-messages-dashboard.json b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-rialto-parachain-to-millau-messages-dashboard.json new file mode 100644 index 00000000000..064436145de --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/relay-rialto-parachain-to-millau-messages-dashboard.json @@ -0,0 +1,908 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "RialtoParachain_to_Millau_MessageLane_00000000_best_target_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "instant": false, + "interval": "", + "legendFormat": "At Millau", + "refId": "A" + }, + { + "expr": "RialtoParachain_to_Millau_MessageLane_00000000_best_target_at_source_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "instant": false, + "interval": "", + "legendFormat": "At RialtoParachain", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized Millau headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "RialtoParachain_to_Millau_MessageLane_00000000_best_source_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "At RialtoParachain", + "refId": "A" + }, + { + "expr": "RialtoParachain_to_Millau_MessageLane_00000000_best_source_at_target_block_number{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "At Millau", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Best finalized RialtoParachain headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "RialtoParachain -> Millau messages are not detected by relay", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from Millau\", \"type\", \"source_latest_generated\"), \"type\", \"Latest Millau message received by RialtoParachain\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + }, + { + "expr": "increase(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_generated\"}[10m]) OR on() vector(0)", + "hide": true, + "interval": "", + "legendFormat": "Messages generated in last 5 minutes (RialtoParachain -> Millau, 00000000)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "label_replace(label_replace(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest message confirmed by Millau to RialtoParachain\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest Millau message received by RialtoParachain\", \"type\", \"target_latest_received\")", + "interval": "", + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "1m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "sum" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "7m", + "frequency": "1m", + "handler": 1, + "name": "Messages from RialtoParachain to Millau are not being delivered", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 20 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_generated\"}[2m])) - scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]))", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "Undelivered messages at Millau", + "refId": "A" + }, + { + "expr": "increase(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[5m]) OR on() vector(0)", + "interval": "", + "legendFormat": "RialtoParachain Messages delivered to Millau in last 5m", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Delivery race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 50 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Too many unconfirmed messages (RialtoParachain -> Millau)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 20 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "legendFormat": "Unconfirmed messages at RialtoParachain", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Confirmations race lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "20m", + "frequency": "1m", + "handler": 1, + "name": "Rewards are not being confirmed (RialtoParachain -> Millau messages)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 20 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_confirmed\"}[2m])) - scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_confirmed\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at Millau", + "refId": "A" + }, + { + "expr": "(scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]) > bool min_over_time(RialtoParachain_to_Millau_MessageLane_00000000_lane_state_nonces{instance=\"relay-millau-rialto-parachain-1:9616\",type=\"target_latest_received\"}[2m]))", + "interval": "", + "legendFormat": "Unconfirmed rewards at Millau (zero if messages are not being delivered to Millau)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 10 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Reward lags (00000000)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "RialtoParachain to Millau Message Sync Dashboard", + "uid": "-l61a7r7k", + "version": 1 +} diff --git a/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json new file mode 100644 index 00000000000..ca22a6dadd0 --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/dashboard/grafana/rialto-parachain-millau-maintenance-dashboard.json @@ -0,0 +1,627 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "liveNow": false, + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "At-Rialto relay balances are too low", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.2.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "at_RialtoParachain_relay_MillauMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "instant": false, + "interval": "", + "legendFormat": "With-Millau relay account balance", + "refId": "A" + }, + { + "exemplar": true, + "expr": "at_RialtoParachain_relay_MillauMessages_reward_for_msgs_from_Millau_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"} + at_RialtoParachain_relay_MillauMessages_reward_for_msgs_to_Millau_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "With-Millau relay account reward", + "refId": "B" + }, + { + "exemplar": true, + "expr": "at_RialtoParachain_relay_MillauMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"} + at_RialtoParachain_relay_MillauMessages_reward_for_msgs_from_Millau_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"} + at_RialtoParachain_relay_MillauMessages_reward_for_msgs_to_Millau_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "hide": false, + "interval": "", + "legendFormat": "With-Millau relay account total balance (balance + reward)", + "refId": "C" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1000, + "visible": true + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "RialtoParachain relay balances", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "At-Millau relay balances are too low", + "noDataState": "ok", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.2.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "at_Millau_relay_RialtoParachainMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "interval": "", + "legendFormat": "With-Rialto relay account balance", + "refId": "A" + }, + { + "exemplar": true, + "expr": "at_Millau_relay_RialtoParachainMessages_reward_for_msgs_from_RialtoParachain_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"} + at_Millau_relay_RialtoParachainMessages_reward_for_msgs_to_RialtoParachain_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "hide": false, + "interval": "", + "legendFormat": "With-Rialto relay account reward", + "refId": "B" + }, + { + "exemplar": true, + "expr": "at_Millau_relay_RialtoParachainMessages_balance{instance=\"relay-millau-rialto-parachain-1:9616\"} + at_Millau_relay_RialtoParachainMessages_reward_for_msgs_from_RialtoParachain_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"} + at_Millau_relay_RialtoParachainMessages_reward_for_msgs_to_RialtoParachain_on_lane_00000000{instance=\"relay-millau-rialto-parachain-1:9616\"}", + "hide": false, + "interval": "", + "legendFormat": "With-Rialto relay account total balance (balance + reward)", + "refId": "C" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1000, + "visible": true + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Millau relay balances", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Whether with-RialtoParachain-parachains-pallet and Rialto itself are on different forks alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.2.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "RialtoParachain_to_Millau_MessageLane_00000000_is_source_and_source_at_target_using_different_forks{instance=\"relay-millau-rialto-parachain-1:9616\"} OR on() vector(0)", + "instant": false, + "interval": "", + "legendFormat": "On different forks?", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0, + "visible": true + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Whether with-RialtoParachain-parachains-pallet and RialtoParachain itself are on different forks", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Whether with-Millau-grandpa-pallet and Millau itself are on different forks", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "hiddenSeries": false, + "id": 16, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.2.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Millau_to_RialtoParachain_MessageLane_00000000_is_source_and_source_at_target_using_different_forks{instance=\"relay-millau-rialto-parachain-1:9616\"} OR on() vector(0)", + "interval": "", + "legendFormat": "On different forks?", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0, + "visible": true + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Whether with-Millau-grandpa-pallet and Millau itself are on different forks", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 32, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "RialtoParachain+Millau maintenance dashboard", + "uid": "WALc3ajnk", + "version": 1 +} diff --git a/deployments/bridges/rialto-parachain-millau/dashboard/prometheus/targets.yml b/deployments/bridges/rialto-parachain-millau/dashboard/prometheus/targets.yml new file mode 100644 index 00000000000..ba683e3479a --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/dashboard/prometheus/targets.yml @@ -0,0 +1,3 @@ +- targets: + - relay-millau-rialto-parachain-1:9616 + - relay-millau-rialto-parachain-2:9616 diff --git a/deployments/bridges/rialto-parachain-millau/docker-compose.yml b/deployments/bridges/rialto-parachain-millau/docker-compose.yml new file mode 100644 index 00000000000..a62bda52eb3 --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/docker-compose.yml @@ -0,0 +1,90 @@ +# Exposed ports: 10816, 10916, 11016, 11017, 11018 + +version: '3.5' +services: + # We provide overrides for these particular nodes since they are public facing + # nodes which we use to connect from things like Polkadot JS Apps. + rialto-parachain-collator-charlie: + environment: + VIRTUAL_HOST: wss.rialto-parachain.brucke.link + VIRTUAL_PORT: 9944 + LETSENCRYPT_HOST: wss.rialto-parachain.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + + millau-node-charlie: + environment: + VIRTUAL_HOST: wss.millau.brucke.link + VIRTUAL_PORT: 9944 + LETSENCRYPT_HOST: wss.millau.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + + relay-millau-rialto-parachain-1: &sub-bridge-relay + image: ${SUBSTRATE_RELAY_IMAGE:-paritytech/substrate-relay} + entrypoint: /entrypoints/relay-millau-rialto-parachain-entrypoint.sh + volumes: + - ./bridges/common:/common + - ./bridges/rialto-parachain-millau/entrypoints:/entrypoints + environment: + RUST_LOG: rpc=trace,bridge=trace + ports: + - "10816:9616" + depends_on: &all-nodes + - millau-node-alice + - millau-node-bob + - millau-node-charlie + - millau-node-dave + - millau-node-eve + - rialto-parachain-collator-alice + - rialto-parachain-collator-bob + - rialto-parachain-collator-charlie + + relay-millau-rialto-parachain-2: + <<: *sub-bridge-relay + environment: + RUST_LOG: rpc=trace,bridge=trace + EXT_MILLAU_RELAY_ACCOUNT: //RialtoParachain.HeadersAndMessagesRelay2 + EXT_MILLAU_RELAY_ACCOUNT_HEADERS_OVERRIDE: //RialtoParachain.RialtoHeadersRelay2 + EXT_RIALTO_PARACHAIN_RELAY_ACCOUNT: //Millau.HeadersAndMessagesRelay2 + ports: + - "10916:9616" + relay-messages-millau-to-rialto-parachain-generator: + <<: *sub-bridge-relay + ports: + - "11016:9616" + entrypoint: /entrypoints/relay-messages-to-rialto-parachain-generator-entrypoint.sh + depends_on: + - relay-millau-rialto-parachain-1 + + relay-messages-rialto-parachain-to-millau-generator: + <<: *sub-bridge-relay + entrypoint: /entrypoints/relay-messages-to-millau-generator-entrypoint.sh + ports: + - "11017:9616" + depends_on: + - relay-millau-rialto-parachain-1 + + relay-messages-millau-to-rialto-parachain-resubmitter: + <<: *sub-bridge-relay + environment: + RUST_LOG: bridge=trace + entrypoint: /entrypoints/relay-messages-to-rialto-parachain-resubmitter-entrypoint.sh + ports: + - "11018:9616" + depends_on: + - relay-messages-millau-to-rialto-parachain-generator + + # Note: These are being overridden from the top level `monitoring` compose file. + grafana-dashboard: + environment: + VIRTUAL_HOST: grafana.millau.brucke.link,grafana.rialto.brucke.link + VIRTUAL_PORT: 3000 + LETSENCRYPT_HOST: grafana.millau.brucke.link,grafana.rialto.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + volumes: + - ./bridges/rialto-parachain-millau/dashboard/grafana:/etc/grafana/dashboards/rialto-parachain-millau:ro + - ./networks/dashboard/grafana/beefy-dashboard.json:/etc/grafana/dashboards/beefy.json + + prometheus-metrics: + volumes: + - ./bridges/rialto-parachain-millau/dashboard/prometheus/targets.yml:/etc/prometheus/targets-rialto-parachain-millau.yml + depends_on: *all-nodes diff --git a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh new file mode 100755 index 00000000000..c09116a1614 --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# THIS SCRIPT IS NOT INTENDED FOR USE IN PRODUCTION ENVIRONMENT +# +# This scripts periodically calls the Substrate relay binary to generate messages. These messages +# are sent from the Rialto network to the Millau network. + +set -eu + +# Max delay before submitting transactions (s) +MAX_SUBMIT_DELAY_S=${MSG_EXCHANGE_GEN_MAX_SUBMIT_DELAY_S:-30} +MESSAGE_LANE=${MSG_EXCHANGE_GEN_LANE:-00000000} +MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE=1024 + +SHARED_CMD="/home/user/substrate-relay send-message rialto-parachain-to-millau" +SHARED_HOST="--source-host rialto-parachain-collator-bob --source-port 9944" +SOURCE_SIGNER="--source-signer //Millau.MessagesSender" + +SEND_MESSAGE="$SHARED_CMD $SHARED_HOST $SOURCE_SIGNER" + +SOURCE_CHAIN="RialtoParachain" +TARGET_CHAIN="Millau" +EXTRA_ARGS="" +# It is the encoded `xcm::VersionedXcm::V3(prepare_outbound_xcm_message(MillauNetwork::get())` +# from the `xcm_messages_to_millau_are_sent_using_bridge_exporter` test in the `rialto-parachain-runtime` +REGULAR_PAYLOAD="030426030109030419a8" + +source /common/generate_messages.sh diff --git a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-generator-entrypoint.sh b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-generator-entrypoint.sh new file mode 100755 index 00000000000..dbf14ef18bc --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-generator-entrypoint.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# THIS SCRIPT IS NOT INTENDED FOR USE IN PRODUCTION ENVIRONMENT +# +# This scripts periodically calls the Substrate relay binary to generate messages. These messages +# are sent from the Millau network to the Rialto network. + +set -eu + +# Max delay before submitting transactions (s) +MAX_SUBMIT_DELAY_S=${MSG_EXCHANGE_GEN_MAX_SUBMIT_DELAY_S:-30} +MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE=1024 + +SHARED_CMD=" /home/user/substrate-relay send-message millau-to-rialto-parachain" +SHARED_HOST="--source-host millau-node-bob --source-port 9944" +SOURCE_SIGNER="--source-signer //RialtoParachain.MessagesSender" + +SEND_MESSAGE="$SHARED_CMD $SHARED_HOST $SOURCE_SIGNER" + +SOURCE_CHAIN="Millau" +TARGET_CHAIN="RialtoParachain" +EXTRA_ARGS="" +# It is the encoded `xcm::VersionedXcm::V3(prepare_outbound_xcm_message(RialtoParachainNetwork::get())` +# from the `xcm_messages_to_rialto_parachain_are_sent_using_bridge_exporter` test in the `millau-runtime` +REGULAR_PAYLOAD="030426040109040419a8" + +source /common/generate_messages.sh diff --git a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-resubmitter-entrypoint.sh b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-resubmitter-entrypoint.sh new file mode 100755 index 00000000000..4653ae396c6 --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-messages-to-rialto-parachain-resubmitter-entrypoint.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -xeu + +sleep 15 + +# //RialtoParachain.MessagesSender is signing Millau -> RialtoParachain message-send transactions, which are causing problems. +# +# When large message is being sent from Millau to RialtoParachain AND other transactions are +# blocking it from being mined, we'll see something like this in logs: +# +# Millau transaction priority with tip=0: 17800827994. Target priority: +# 526186677695 +# +# So since fee multiplier in Millau is `1` and `WeightToFee` is `IdentityFee`, then +# we need tip around `526186677695 - 17800827994 = 508_385_849_701`. Let's round it +# up to `1_000_000_000_000`. + +exec /home/user/substrate-relay resubmit-transactions millau \ + --target-host millau-node-alice \ + --target-port 9944 \ + --target-signer //RialtoParachain.MessagesSender \ + --stalled-blocks 7 \ + --tip-limit 1000000000000 \ + --tip-step 1000000000 \ + make-it-best-transaction diff --git a/deployments/bridges/rialto-parachain-millau/entrypoints/relay-millau-rialto-parachain-entrypoint.sh b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-millau-rialto-parachain-entrypoint.sh new file mode 100755 index 00000000000..7685f5f5ec2 --- /dev/null +++ b/deployments/bridges/rialto-parachain-millau/entrypoints/relay-millau-rialto-parachain-entrypoint.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -xeu + +sleep 15 + +MILLAU_RELAY_ACCOUNT=${EXT_MILLAU_RELAY_ACCOUNT:-//RialtoParachain.HeadersAndMessagesRelay1} +MILLAU_RELAY_ACCOUNT_HEADERS_OVERRIDE=${EXT_MILLAU_RELAY_ACCOUNT_HEADERS_OVERRIDE:-//RialtoParachain.RialtoHeadersRelay1} +RIALTO_PARACHAIN_RELAY_ACCOUNT=${EXT_RIALTO_PARACHAIN_RELAY_ACCOUNT:-//Millau.HeadersAndMessagesRelay1} + +/home/user/substrate-relay init-bridge millau-to-rialto-parachain \ + --source-host millau-node-alice \ + --source-port 9944 \ + --target-host rialto-parachain-collator-alice \ + --target-port 9944 \ + --target-signer //Sudo + +/home/user/substrate-relay init-bridge rialto-to-millau \ + --source-host rialto-node-alice \ + --source-port 9944 \ + --target-host millau-node-alice \ + --target-port 9944 \ + --target-signer //Sudo + +# Give chain a little bit of time to process initialization transaction +sleep 6 + +exec /home/user/substrate-relay relay-headers-and-messages millau-rialto-parachain \ + --millau-host millau-node-alice \ + --millau-port 9944 \ + --millau-signer $MILLAU_RELAY_ACCOUNT \ + --millau-transactions-mortality=64 \ + --rialto-parachain-host rialto-parachain-collator-charlie \ + --rialto-parachain-port 9944 \ + --rialto-parachain-signer $RIALTO_PARACHAIN_RELAY_ACCOUNT \ + --rialto-parachain-transactions-mortality=64 \ + --rialto-host rialto-node-alice \ + --rialto-port 9944 \ + --lane=00000000 \ + --prometheus-host=0.0.0.0 diff --git a/deployments/bridges/rococo-wococo/README.md b/deployments/bridges/rococo-wococo/README.md new file mode 100644 index 00000000000..1c2985a3357 --- /dev/null +++ b/deployments/bridges/rococo-wococo/README.md @@ -0,0 +1,10 @@ +# Rococo Bridge Hub <> Wococo Bridge Hub deployments + +This folder contains some information and useful stuff from our other test deployment - between Rococo and Wococo +bridge hubs. The bridge overview and other helpful information can be found in +[this readme](https://github.com/paritytech/cumulus/tree/bridge-hub-rococo-wococo/parachains/runtimes/bridge-hubs). + +## Grafana Alerts and Dashboards + +JSON model for Grafana alerts and dashobards that we use, may be found in the [dasboard/grafana](./dashboard/grafana/) +folder. diff --git a/deployments/bridges/rococo-wococo/dashboard/grafana/bridges-alerts.json b/deployments/bridges/rococo-wococo/dashboard/grafana/bridges-alerts.json new file mode 100644 index 00000000000..6432f79deaf --- /dev/null +++ b/deployments/bridges/rococo-wococo/dashboard/grafana/bridges-alerts.json @@ -0,0 +1,1994 @@ +{ + "Bridges": [ + { + "name": "Bridges", + "interval": "1m", + "rules": [ + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "tkgc6_bnk", + "__panelId__": "12", + "summary": "Messages from RococoBridgeHub to WococoBridgeHub (00000001) are either not delivered, or are delivered with lags" + }, + "grafana_alert": { + "id": 51, + "orgId": 1, + "title": "RococoBridgeHub -> WococoBridgeHub delivery lags (00000001)", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Undelivered messages", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 90, + "uid": "r41otJp4k", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "zqjpgXxnk", + "__panelId__": "14", + "summary": "Messages from WococoBridgeHub to RococoBridgeHub (00000001) are either not delivered, or are delivered with lags" + }, + "grafana_alert": { + "id": 55, + "orgId": 1, + "title": "WococoBridgeHub -> RococoBridgeHub delivery lags (00000001)", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Undelivered messages", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 88, + "uid": "wqmPtJpVz", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "tkgc6_bnk", + "__panelId__": "14", + "summary": "Messages from RococoBridgeHub to WococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" + }, + "grafana_alert": { + "id": 56, + "orgId": 1, + "title": "RococoBridgeHub -> WococoBridgeHub confirmation lags (00000001)", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Unconfirmed messages", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 50 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 84, + "uid": "z4h3pJtVz", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "zqjpgXxnk", + "__panelId__": "16", + "summary": "Messages from WococoBridgeHub to RococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" + }, + "grafana_alert": { + "id": 57, + "orgId": 1, + "title": "WococoBridgeHub -> RococoBridgeHub confirmation lags (00000001)", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Unconfirmed messages", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 50 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 83, + "uid": "Kj_z21t4k", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "zqjpgXxnk", + "__panelId__": "18", + "summary": "Rewards for messages from WococoBridgeHub to RococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" + }, + "grafana_alert": { + "id": 58, + "orgId": 1, + "title": "WococoBridgeHub -> RococoBridgeHub reward lags (00000001)", + "condition": "C", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Unconfirmed rewards", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "(scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "hide": true, + "interval": "", + "intervalMs": 30000, + "legendFormat": "__auto", + "maxDataPoints": 43200, + "range": true, + "refId": "B" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "C", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 78, + "uid": "hw_a21pVk", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "tkgc6_bnk", + "__panelId__": "15", + "summary": "Rewards for messages from RococoBridgeHub to WococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" + }, + "grafana_alert": { + "id": 59, + "orgId": 1, + "title": "RococoBridgeHub -> WococoBridgeHub reward lags (00000001)", + "condition": "C", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Unconfirmed rewards", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "(scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "hide": true, + "interval": "", + "intervalMs": 30000, + "legendFormat": "__auto", + "maxDataPoints": 43200, + "range": true, + "refId": "B" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "C", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 77, + "uid": "daN62Jt4z", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "2", + "summary": "Best BridgeHubRococo header at BridgeHubWococo (00000001) doesn't match the same header at BridgeHubRococo" + }, + "grafana_alert": { + "id": 60, + "orgId": 1, + "title": "BridgeHubRococo header mismatch (00000001)", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 74, + "uid": "BzBDb1pVz", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "3", + "summary": "Best BridgeHubWococo header at BridgeHubRococo (00000001) doesn't match the same header at BridgeHubWococo" + }, + "grafana_alert": { + "id": 61, + "orgId": 1, + "title": "BridgeHubWococo header mismatch (00000001)", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 73, + "uid": "1W6lb1p4z", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "5", + "summary": "With-WococoBridgeHub messages relay balance at RococoBridgeHub (00000001) is too low" + }, + "grafana_alert": { + "id": 62, + "orgId": 1, + "title": "With-WococoBridgeHub messages relay balance at RococoBridgeHub (00000001) is too low", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "last_over_time(at_BridgeHubRococo_relay_BridgeHubWococoMessages_balance{domain=\"parity-testnet\"}[1h])", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Messages Relay Balance", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 72, + "uid": "Y5Dm-1tVz", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "6", + "summary": "With-RococoBridgeHub messages relay balance at WococoBridgeHub (00000001) is too low" + }, + "grafana_alert": { + "id": 63, + "orgId": 1, + "title": "With-RococoBridgeHub messages relay balance at WococoBridgeHub (00000001) is too low", + "condition": "B", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "last_over_time(at_BridgeHubWococo_relay_BridgeHubRococoMessages_balance{domain=\"parity-testnet\"}[1h])", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Messages Relay Balance", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 300, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 10 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "B", + "type": "classic_conditions" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 71, + "uid": "yUl4a1tVz", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "5m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "tkgc6_bnk", + "__panelId__": "6", + "summary": "Less than 500 Rococo headers have been synced to WococoBridgeHub in last 120 minutes. Relay is not running?" + }, + "grafana_alert": { + "id": 65, + "orgId": 1, + "title": "Rococo -> WococoBridgeHub finality sync lags (00000001)", + "condition": "D", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "max(increase(Rococo_to_BridgeHubWococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}[120m]))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "At Rococo", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "reducer": "last", + "refId": "C", + "type": "reduce" + } + }, + { + "refId": "D", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 500 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "D" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "C", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "D", + "type": "threshold" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 40, + "uid": "R6GKwNA4z", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "5m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "zqjpgXxnk", + "__panelId__": "2", + "summary": "Less than 500 Wococo headers have been synced to RococoBridgeHub in last 120 minutes. Relay is not running?" + }, + "grafana_alert": { + "id": 66, + "orgId": 1, + "title": "Wococo -> RococoBridgeHub finality sync lags (00000001)", + "condition": "D", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "max(increase(Wococo_to_BridgeHubRococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}[120m]))", + "interval": "", + "intervalMs": 30000, + "legendFormat": "At Wococo", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "reducer": "last", + "refId": "C", + "type": "reduce" + } + }, + { + "refId": "D", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 500 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "D" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "C", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "D", + "type": "threshold" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 38, + "uid": "rAM1QHAVk", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "0s", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "11", + "summary": "The RococoBridgeHub <> WococoBridgeHub relay (00000001) has been aborted by version guard - i.e. one of chains has been upgraded and relay wasn't redeployed" + }, + "grafana_alert": { + "id": 67, + "orgId": 1, + "title": "Version guard has aborted RococoBridgeHub <> WococoBridgeHub relay (00000001)", + "condition": "B", + "data": [ + { + "refId": "B", + "queryType": "range", + "relativeTimeRange": { + "from": 600, + "to": 0 + }, + "datasourceUid": "P03E52D76DFE188C3", + "model": { + "datasource": { + "type": "loki", + "uid": "P03E52D76DFE188C3" + }, + "editorMode": "code", + "expr": "count_over_time({container=\"bridges-common-relay\"} |= `Aborting relay` [1m])", + "hide": false, + "intervalMs": 1000, + "legendFormat": "Aborts per minute", + "maxDataPoints": 43200, + "queryType": "range", + "refId": "B" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "B", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "reducer": "max", + "refId": "C", + "type": "reduce" + } + }, + { + "refId": "D", + "queryType": "", + "relativeTimeRange": { + "from": 600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "D" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "C", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "D", + "type": "threshold" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 37, + "uid": "TwWPeN04z", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "12", + "summary": "Best Rococo header at BridgeHubWococo (00000001) doesn't match the same header at Rococo" + }, + "grafana_alert": { + "id": 69, + "orgId": 1, + "title": "Rococo headers mismatch", + "condition": "C", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 0, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "reducer": "last", + "refId": "B", + "type": "reduce" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 0, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "B", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "C", + "type": "threshold" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 32, + "uid": "08-5gv04k", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "10m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "13", + "summary": "Best Wococo header at BridgeHubRococo (00000001) doesn't match the same header at Wococo" + }, + "grafana_alert": { + "id": 70, + "orgId": 1, + "title": "Wococo headers mismatch", + "condition": "C", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "interval": "", + "intervalMs": 30000, + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 0, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "reducer": "last", + "refId": "B", + "type": "reduce" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 0, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "B", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "C", + "type": "threshold" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 31, + "uid": "Esj2gD0Vk", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "5m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "tkgc6_bnk", + "__panelId__": "9", + "summary": "Test messages from RococoBridgeHub to WococoBridgeHub are not generated. Our cronjob has died?" + }, + "grafana_alert": { + "id": 73, + "orgId": 1, + "title": "Test messages from RococoBridgeHub to WococoBridgeHub are not generated.", + "condition": "D", + "data": [ + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 21600, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "increase(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\", type=~\"source_latest_generated\"}[24h])", + "hide": true, + "interval": "", + "intervalMs": 30000, + "legendFormat": "Messages generated in last 24h", + "maxDataPoints": 43200, + "range": true, + "refId": "B" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "B", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "reducer": "max", + "refId": "C", + "type": "reduce" + } + }, + { + "refId": "D", + "queryType": "", + "relativeTimeRange": { + "from": 600, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "D" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "C", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "D", + "type": "threshold" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 3, + "uid": "ry1K5SB4k", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + }, + { + "expr": "", + "for": "5m", + "labels": { + "matrix_room": "XyVkmRJgIkjcvIBPsP" + }, + "annotations": { + "__dashboardUid__": "UFsgbJtVz", + "__panelId__": "16", + "summary": "RococoBridgeHub <> WococoBridgeHub relay (00000001) node is down" + }, + "grafana_alert": { + "id": 74, + "orgId": 1, + "title": "RococoBridgeHub <> WococoBridgeHub relay (00000001) node is down", + "condition": "C", + "data": [ + { + "refId": "A", + "queryType": "", + "relativeTimeRange": { + "from": 900, + "to": 0 + }, + "datasourceUid": "PC96415006F908B67", + "model": { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": false, + "expr": "up{domain=\"parity-testnet\",container=\"bridges-common-relay\"}", + "instant": false, + "interval": "", + "intervalMs": 30000, + "legendFormat": "Is relay running", + "maxDataPoints": 43200, + "range": true, + "refId": "A" + } + }, + { + "refId": "B", + "queryType": "", + "relativeTimeRange": { + "from": 0, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "A", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "reducer": "max", + "refId": "B", + "type": "reduce" + } + }, + { + "refId": "C", + "queryType": "", + "relativeTimeRange": { + "from": 0, + "to": 0 + }, + "datasourceUid": "-100", + "model": { + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "datasource": { + "type": "__expr__", + "uid": "-100" + }, + "expression": "B", + "hide": false, + "intervalMs": 1000, + "maxDataPoints": 43200, + "refId": "C", + "type": "threshold" + } + } + ], + "updated": "2023-03-29T05:39:46Z", + "intervalSeconds": 60, + "version": 1, + "uid": "9YAdEUB4z", + "namespace_uid": "eblDiw17z", + "namespace_id": 140, + "rule_group": "Bridges", + "no_data_state": "OK", + "exec_err_state": "OK" + } + } + ] + } + ] + } diff --git a/deployments/bridges/rococo-wococo/dashboard/grafana/relay-rococo-to-wococo-messages-dashboard.json b/deployments/bridges/rococo-wococo/dashboard/grafana/relay-rococo-to-wococo-messages-dashboard.json new file mode 100644 index 00000000000..d1ecba2acfd --- /dev/null +++ b/deployments/bridges/rococo-wococo/dashboard/grafana/relay-rococo-to-wococo-messages-dashboard.json @@ -0,0 +1,941 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 141, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", + "legendFormat": "At Rococo", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At BridgeHubWococo", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized Rococo headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", + "legendFormat": "At Wococo", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At BridgeHubRococo", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized Wococo headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": true, + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_source_block_number{domain=\"parity-testnet\"}", + "interval": "", + "legendFormat": "At RococoBridgeHub", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized RococoBridgeHub headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": true, + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_target_block_number{domain=\"parity-testnet\"}", + "interval": "", + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_target_at_source_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At RococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized WococoBridgeHub headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "label_replace(label_replace(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\", type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from BridgeHubRococo\", \"type\", \"source_latest_generated\"), \"type\", \"Latest BridgeHubRococo message received by BridgeHubWococo\", \"type\", \"target_latest_received\")", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + } + ], + "title": "Delivery race (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "label_replace(label_replace(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest delivery confirmation from BridgeHubWococo to BridgeHubRococo\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest BridgeHubRococo message received by BridgeHubWococo\", \"type\", \"target_latest_received\")", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + } + ], + "title": "Confirmations race (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 16 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "legendFormat": "Undelivered messages", + "range": true, + "refId": "A" + } + ], + "title": "Delivery race lags (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 16 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "legendFormat": "Unconfirmed messages", + "range": true, + "refId": "A" + } + ], + "title": "Confirmations race lags (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 16 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", + "legendFormat": "Unconfirmed rewards", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "(scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "hide": true, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "Reward lags (00000001)", + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BridgeHubRococo to BridgeHubWococo (00000001)", + "uid": "tkgc6_bnk", + "version": 42, + "weekStart": "" + } \ No newline at end of file diff --git a/deployments/bridges/rococo-wococo/dashboard/grafana/relay-wococo-to-rococo-messages-dashboard.json b/deployments/bridges/rococo-wococo/dashboard/grafana/relay-wococo-to-rococo-messages-dashboard.json new file mode 100644 index 00000000000..be33f0dcecc --- /dev/null +++ b/deployments/bridges/rococo-wococo/dashboard/grafana/relay-wococo-to-rococo-messages-dashboard.json @@ -0,0 +1,941 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 142, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", + "legendFormat": "At Wococo", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At BridgeHubRococo", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized Wococo headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", + "legendFormat": "At Rococo", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized Rococo headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": true, + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_source_block_number{domain=\"parity-testnet\"}", + "interval": "", + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At RococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized WococoBridgeHub headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": true, + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_target_block_number{domain=\"parity-testnet\"}", + "interval": "", + "legendFormat": "At RococoBridgeHub", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_target_at_source_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized RococoBridgeHub headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "label_replace(label_replace(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from BridgeHubWococo\", \"type\", \"source_latest_generated\"), \"type\", \"Latest BridgeHubWococo message received by BridgeHubRococo\", \"type\", \"target_latest_received\")", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + } + ], + "title": "Delivery race (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "label_replace(label_replace(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest delivery confirmation from BridgeHubRococo to BridgeHubWococo\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest BridgeHubWococo message received by BridgeHubRococo\", \"type\", \"target_latest_received\")", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + } + ], + "title": "Confirmations race (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 16 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "legendFormat": "Undelivered messages", + "range": true, + "refId": "A" + } + ], + "title": "Delivery race lags (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 16 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "legendFormat": "Unconfirmed messages", + "range": true, + "refId": "A" + } + ], + "title": "Confirmations race lags (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 16 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", + "legendFormat": "Unconfirmed rewards", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "(scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "hide": true, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "Reward lags (00000001)", + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BridgeHubWococo to BridgeHubRococo (00000001)", + "uid": "zqjpgXxnk", + "version": 30, + "weekStart": "" + } \ No newline at end of file diff --git a/deployments/bridges/rococo-wococo/dashboard/grafana/rococo-wococo-maintenance-dashboard.json b/deployments/bridges/rococo-wococo/dashboard/grafana/rococo-wococo-maintenance-dashboard.json new file mode 100644 index 00000000000..6158c3e73a9 --- /dev/null +++ b/deployments/bridges/rococo-wococo/dashboard/grafana/rococo-wococo-maintenance-dashboard.json @@ -0,0 +1,1031 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 284, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 0 + }, + "id": 8, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "name" + }, + "pluginVersion": "9.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": false, + "expr": "substrate_relay_build_info{domain=\"parity-testnet\"}", + "instant": true, + "legendFormat": "{{commit}}", + "range": false, + "refId": "A" + } + ], + "title": "Relay build commit", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 5, + "y": 0 + }, + "id": 9, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^1\\.0\\.1$/", + "values": false + }, + "text": {}, + "textMode": "name" + }, + "pluginVersion": "9.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": false, + "expr": "substrate_relay_build_info{domain=\"parity-testnet\"}", + "instant": true, + "legendFormat": "{{version}}", + "range": false, + "refId": "A" + } + ], + "title": "Relay build version", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "index": 1, + "text": "No" + }, + "1": { + "color": "green", + "index": 0, + "text": "Yes" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 9, + "y": 0 + }, + "id": 15, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": false, + "expr": "up{domain=\"parity-testnet\",container=\"bridges-common-relay\"}", + "instant": false, + "legendFormat": "Is relay running", + "range": true, + "refId": "A" + } + ], + "title": "Is relay running?", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 13, + "y": 0 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": false, + "expr": "up{domain=\"parity-testnet\",container=\"bridges-common-relay\"}", + "instant": false, + "legendFormat": "Is relay running", + "range": true, + "refId": "A" + } + ], + "title": "Is relay running? (for alert)", + "type": "timeseries" + }, + { + "datasource": { + "type": "loki", + "uid": "P03E52D76DFE188C3" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 18, + "x": 0, + "y": 5 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "P03E52D76DFE188C3" + }, + "editorMode": "code", + "expr": "count_over_time({container=\"bridges-common-relay\"} |~ `(?i)(warn|error|fail)` [1m])", + "legendFormat": "Errors per minute", + "queryType": "range", + "refId": "A" + } + ], + "title": "Relay errors/warnings per minute", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 9, + "x": 0, + "y": 14 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "range": true, + "refId": "A" + } + ], + "title": "Rococo headers mismatch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 9, + "x": 9, + "y": 14 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "range": true, + "refId": "A" + } + ], + "title": "Wococo headers mismatch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 9, + "x": 0, + "y": 21 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "range": true, + "refId": "A" + } + ], + "title": "BridgeHubRococo headers mismatch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 9, + "x": 9, + "y": 21 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "range": true, + "refId": "A" + } + ], + "title": "BridgeHubWococo headers mismatch", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 0, + "y": 28 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "at_BridgeHubRococo_relay_BridgeHubWococoMessages_balance{domain=\"parity-testnet\"}", + "legendFormat": "Messages Relay Balance", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "at_BridgeHubRococo_relay_WococoHeaders_reward_for_msgs_to_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"} + at_BridgeHubRococo_relay_BridgeHubWococoMessages_reward_for_msgs_to_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "Pending reward", + "range": true, + "refId": "B" + } + ], + "title": "Relay balances at RococoBridgeHub", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 9, + "y": 28 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "at_BridgeHubWococo_relay_BridgeHubRococoMessages_balance{domain=\"parity-testnet\"}", + "legendFormat": "Messages Relay Balance", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "at_BridgeHubRococo_relay_BridgeHubWococoMessages_reward_for_msgs_from_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"} + at_BridgeHubRococo_relay_BridgeHubWococoMessages_reward_for_msgs_to_BridgeHubWococo_on_lane_00000001{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "Pending reward", + "range": true, + "refId": "B" + } + ], + "title": "Relay balances at WococoBridgeHub", + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BridgeHubRococo <> BridgeHubWococo maintenance (00000001)", + "uid": "UFsgbJtVz", + "version": 26, + "weekStart": "" +} diff --git a/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-headers-dashboard.json b/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-headers-dashboard.json new file mode 100644 index 00000000000..8f740d2ba5e --- /dev/null +++ b/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-headers-dashboard.json @@ -0,0 +1,781 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 32 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "60m", + "frequency": "5m", + "handler": 1, + "message": "", + "name": "Synced Header Difference is Over 32 (Westend to Millau)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "Shows how many headers behind the target chain is from the source chain.", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "max(Westend_to_Millau_Sync_best_source_block_number) - max(Westend_to_Millau_Sync_best_source_at_target_block_number)", + "format": "table", + "instant": false, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 5 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Difference Between Source and Target Headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 32 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "2m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "60m", + "frequency": "5m", + "handler": 1, + "name": "No New Headers (Westend to Millau)", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "How many headers has the relay synced from the source node in the last 2 mins?", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 16, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "max_over_time(Westend_to_Millau_Sync_best_source_block_number[10m])-min_over_time(Westend_to_Millau_Sync_best_source_block_number[10m])", + "interval": "", + "legendFormat": "Number of new Headers on Westend (Last 10 Mins)", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 5 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Headers Synced on Millau (Last 2 Mins)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": { + "align": null + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 2, + "interval": "5s", + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.1.3", + "targets": [ + { + "expr": "Westend_to_Millau_Sync_best_source_block_number", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "Best Known Westend Header at Westend", + "refId": "A" + }, + { + "expr": "Westend_to_Millau_Sync_best_source_at_target_block_number", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "Best Known Westend Header at Millau", + "refId": "B" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Best Blocks according to Relay", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 8 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "system_average_load{instance='relay-headers-westend-to-millau-1:9616'}", + "interval": "", + "legendFormat": "Average system load in last {{over}}", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": null + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Average System Load", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 8 + }, + "id": 12, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "7.1.3", + "targets": [ + { + "expr": "avg_over_time(process_cpu_usage_percentage{instance='relay-headers-westend-to-millau-1:9616'}[1m])", + "instant": true, + "interval": "", + "legendFormat": "1 CPU = 100", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Relay Process CPU Usage ", + "type": "gauge" + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Whether with-Westend-grandpa-pallet and Westend itself are on different forks alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 14 + }, + "hiddenSeries": false, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Westend_to_Millau_Sync_is_source_and_source_at_target_using_different_forks", + "interval": "", + "legendFormat": "On different forks?", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Whether with-Westend-grandpa-pallet and Westend itself are on different forks", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "process_memory_usage_bytes{instance='relay-headers-westend-to-millau-1:9616'} / 1024 / 1024", + "interval": "", + "legendFormat": "Process memory, MB", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Usage for Relay Process", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Westend to Millau Header Sync Dashboard", + "uid": "relay-westend-to-millau-headers", + "version": 1 +} diff --git a/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-parachains-dashboard.json b/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-parachains-dashboard.json new file mode 100644 index 00000000000..31a7cca5229 --- /dev/null +++ b/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-parachains-dashboard.json @@ -0,0 +1,200 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 9, + "links": [], + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 32 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Too many Westmint headers are missing at Millau", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Westend_to_Millau_Parachains_1000_best_parachain_block_number_at_source", + "interval": "", + "legendFormat": "At Westend", + "refId": "A" + }, + { + "expr": "Westend_to_Millau_Parachains_1000_best_parachain_block_number_at_target", + "interval": "", + "legendFormat": "At Millau", + "refId": "B" + }, + { + "expr": "Westend_to_Millau_Parachains_1000_best_parachain_block_number_at_source - Westend_to_Millau_Parachains_1000_best_parachain_block_number_at_target", + "hide": true, + "interval": "", + "legendFormat": "Missing Westmint headers at Millau", + "refId": "C" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 32 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Westmint headers", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Westend parachains at Millau", + "uid": "vUMhOlq7k", + "version": 1 + } + \ No newline at end of file diff --git a/deployments/bridges/westend-millau/dashboard/prometheus/targets.yml b/deployments/bridges/westend-millau/dashboard/prometheus/targets.yml new file mode 100644 index 00000000000..aed2d494582 --- /dev/null +++ b/deployments/bridges/westend-millau/dashboard/prometheus/targets.yml @@ -0,0 +1,3 @@ +- targets: + - relay-headers-westend-to-millau-1:9616 + - relay-parachains-westend-to-millau-1:9616 \ No newline at end of file diff --git a/deployments/bridges/westend-millau/docker-compose.yml b/deployments/bridges/westend-millau/docker-compose.yml new file mode 100644 index 00000000000..dfb6ebb0c64 --- /dev/null +++ b/deployments/bridges/westend-millau/docker-compose.yml @@ -0,0 +1,72 @@ +# Exposed ports: 10616, 10617, 10618, 10619 + +version: '3.5' +services: + relay-headers-westend-to-millau-1: + image: ${SUBSTRATE_RELAY_IMAGE:-paritytech/substrate-relay} + entrypoint: /entrypoints/relay-headers-westend-to-millau-entrypoint.sh + volumes: + - ./bridges/westend-millau/entrypoints:/entrypoints + environment: + RUST_LOG: rpc=trace,bridge=trace + ports: + - "10616:9616" + depends_on: + - millau-node-alice + + relay-headers-westend-to-millau-2: + image: ${SUBSTRATE_RELAY_IMAGE:-paritytech/substrate-relay} + entrypoint: /entrypoints/relay-headers-westend-to-millau-entrypoint.sh + volumes: + - ./bridges/westend-millau/entrypoints:/entrypoints + environment: + RUST_LOG: rpc=trace,bridge=trace + EXT_RELAY_ACCOUNT: //Westend.HeadersRelay2 + ports: + - "10617:9616" + depends_on: + - millau-node-alice + + relay-parachains-westend-to-millau-1: + image: ${SUBSTRATE_RELAY_IMAGE:-paritytech/substrate-relay} + entrypoint: /entrypoints/relay-parachains-westend-to-millau-entrypoint.sh + volumes: + - ./bridges/westend-millau/entrypoints:/entrypoints + environment: + RUST_LOG: rpc=trace,bridge=trace + ports: + - "10618:9616" + depends_on: + - millau-node-alice + + relay-parachains-westend-to-millau-2: + image: ${SUBSTRATE_RELAY_IMAGE:-paritytech/substrate-relay} + entrypoint: /entrypoints/relay-parachains-westend-to-millau-entrypoint.sh + volumes: + - ./bridges/westend-millau/entrypoints:/entrypoints + environment: + RUST_LOG: rpc=trace,bridge=trace + EXT_RELAY_ACCOUNT: //Westend.WestmintHeaders2 + ports: + - "10619:9616" + depends_on: + - millau-node-alice + + # Note: These are being overridden from the top level `monitoring` compose file. + grafana-dashboard: + environment: + VIRTUAL_HOST: grafana.millau.brucke.link,grafana.rialto.brucke.link + VIRTUAL_PORT: 3000 + LETSENCRYPT_HOST: grafana.millau.brucke.link,grafana.rialto.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + volumes: + - ./bridges/westend-millau/dashboard/grafana:/etc/grafana/dashboards/westend-millau:ro + + prometheus-metrics: + volumes: + - ./bridges/westend-millau/dashboard/prometheus/targets.yml:/etc/prometheus/targets-westend-millau.yml + depends_on: + - relay-headers-westend-to-millau-1 + - relay-headers-westend-to-millau-2 + - relay-parachains-westend-to-millau-1 + - relay-parachains-westend-to-millau-2 diff --git a/deployments/bridges/westend-millau/entrypoints/relay-headers-westend-to-millau-entrypoint.sh b/deployments/bridges/westend-millau/entrypoints/relay-headers-westend-to-millau-entrypoint.sh new file mode 100755 index 00000000000..8548e9f5a41 --- /dev/null +++ b/deployments/bridges/westend-millau/entrypoints/relay-headers-westend-to-millau-entrypoint.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -xeu + +sleep 15 + +RELAY_ACCOUNT=${EXT_RELAY_ACCOUNT:-//Westend.HeadersRelay1} + +/home/user/substrate-relay init-bridge westend-to-millau \ + --source-host westend-rpc.polkadot.io \ + --source-port 443 \ + --source-secure \ + --target-host millau-node-alice \ + --target-port 9944 \ + --target-signer //Westend.GrandpaOwner + +# Give chain a little bit of time to process initialization transaction +sleep 6 +/home/user/substrate-relay relay-headers westend-to-millau \ + --source-host westend-rpc.polkadot.io \ + --source-port 443 \ + --source-secure \ + --target-host millau-node-alice \ + --target-port 9944 \ + --target-signer $RELAY_ACCOUNT \ + --target-transactions-mortality=4\ + --prometheus-host=0.0.0.0 diff --git a/deployments/bridges/westend-millau/entrypoints/relay-parachains-westend-to-millau-entrypoint.sh b/deployments/bridges/westend-millau/entrypoints/relay-parachains-westend-to-millau-entrypoint.sh new file mode 100755 index 00000000000..fc7ccb3584f --- /dev/null +++ b/deployments/bridges/westend-millau/entrypoints/relay-parachains-westend-to-millau-entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -xeu + +sleep 15 + +RELAY_ACCOUNT=${EXT_RELAY_ACCOUNT:-//Westend.WestmintHeaders1} + +/home/user/substrate-relay relay-parachains westend-to-millau \ + --source-host westend-rpc.polkadot.io \ + --source-port 443 \ + --source-secure \ + --target-host millau-node-alice \ + --target-port 9944 \ + --target-signer $RELAY_ACCOUNT \ + --target-transactions-mortality=4\ + --prometheus-host=0.0.0.0 diff --git a/deployments/local-scripts/bridge-entrypoint.sh b/deployments/local-scripts/bridge-entrypoint.sh new file mode 100755 index 00000000000..5c1b6e90ec2 --- /dev/null +++ b/deployments/local-scripts/bridge-entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -xeu + +# This will allow us to run whichever binary the user wanted +# with arguments passed through `docker run` +# e.g `docker run -it rialto-bridge-node-dev --dev --tmp` +/home/user/$PROJECT $@ diff --git a/deployments/local-scripts/relay-messages-millau-to-rialto.sh b/deployments/local-scripts/relay-messages-millau-to-rialto.sh new file mode 100755 index 00000000000..d420dc56c26 --- /dev/null +++ b/deployments/local-scripts/relay-messages-millau-to-rialto.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# A script for relaying Millau messages to the Rialto chain. +# +# Will not work unless both the Rialto and Millau are running (see `run-rialto-node.sh` +# and `run-millau-node.sh). +set -xeu + +MILLAU_PORT="${MILLAU_PORT:-9945}" +RIALTO_PORT="${RIALTO_PORT:-9944}" + +RUST_LOG=bridge=debug \ +./target/debug/substrate-relay relay-messages millau-to-rialto \ + --lane 00000000 \ + --source-host localhost \ + --source-port $MILLAU_PORT \ + --source-signer //Bob \ + --target-host localhost \ + --target-port $RIALTO_PORT \ + --target-signer //Bob \ + --prometheus-host=0.0.0.0 diff --git a/deployments/local-scripts/relay-messages-rialto-to-millau.sh b/deployments/local-scripts/relay-messages-rialto-to-millau.sh new file mode 100755 index 00000000000..0cd73c00454 --- /dev/null +++ b/deployments/local-scripts/relay-messages-rialto-to-millau.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# A script for relaying Rialto messages to the Millau chain. +# +# Will not work unless both the Rialto and Millau are running (see `run-rialto-node.sh` +# and `run-millau-node.sh). +set -xeu + +MILLAU_PORT="${MILLAU_PORT:-9945}" +RIALTO_PORT="${RIALTO_PORT:-9944}" + +RUST_LOG=bridge=debug \ +./target/debug/substrate-relay relay-messages rialto-to-millau \ + --lane 00000000 \ + --source-host localhost \ + --source-port $RIALTO_PORT \ + --source-signer //Bob \ + --target-host localhost \ + --target-port $MILLAU_PORT \ + --target-signer //Bob \ + --prometheus-host=0.0.0.0 diff --git a/deployments/local-scripts/relay-millau-to-rialto.sh b/deployments/local-scripts/relay-millau-to-rialto.sh new file mode 100755 index 00000000000..4758173f72f --- /dev/null +++ b/deployments/local-scripts/relay-millau-to-rialto.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# A script for relaying Millau headers to the Rialto chain. +# +# Will not work unless both the Rialto and Millau are running (see `run-rialto-node.sh` +# and `run-millau-node.sh). + +MILLAU_PORT="${MILLAU_PORT:-9945}" +RIALTO_PORT="${RIALTO_PORT:-9944}" + +RUST_LOG=bridge=debug \ +./target/debug/substrate-relay init-bridge millau-to-rialto \ + --source-host localhost \ + --source-port $MILLAU_PORT \ + --target-host localhost \ + --target-port $RIALTO_PORT \ + --target-signer //Sudo \ + --source-version-mode Bundle \ + --target-version-mode Bundle + +sleep 5 +RUST_LOG=bridge=debug \ +./target/debug/substrate-relay relay-headers millau-to-rialto \ + --source-host localhost \ + --source-port $MILLAU_PORT \ + --target-host localhost \ + --target-port $RIALTO_PORT \ + --target-signer //Alice \ + --prometheus-host=0.0.0.0 diff --git a/deployments/local-scripts/relay-rialto-to-millau.sh b/deployments/local-scripts/relay-rialto-to-millau.sh new file mode 100755 index 00000000000..5fd27840214 --- /dev/null +++ b/deployments/local-scripts/relay-rialto-to-millau.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# A script for relaying Rialto headers to the Millau chain. +# +# Will not work unless both the Rialto and Millau are running (see `run-rialto-node.sh` +# and `run-millau-node.sh). + +MILLAU_PORT="${MILLAU_PORT:-9945}" +RIALTO_PORT="${RIALTO_PORT:-9944}" + +RUST_LOG=bridge=debug \ +./target/debug/substrate-relay init-bridge rialto-to-millau \ + --target-host localhost \ + --target-port $MILLAU_PORT \ + --source-host localhost \ + --source-port $RIALTO_PORT \ + --target-signer //Sudo \ + +sleep 5 +RUST_LOG=bridge=debug \ +./target/debug/substrate-relay relay-headers rialto-to-millau \ + --target-host localhost \ + --target-port $MILLAU_PORT \ + --source-host localhost \ + --source-port $RIALTO_PORT \ + --target-signer //Alice \ + --prometheus-host=0.0.0.0 diff --git a/deployments/local-scripts/run-millau-node.sh b/deployments/local-scripts/run-millau-node.sh new file mode 100755 index 00000000000..916f876c536 --- /dev/null +++ b/deployments/local-scripts/run-millau-node.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Run a development instance of the Millau Substrate bridge node. +# To override the default port just export MILLAU_PORT=9945 + +MILLAU_PORT="${MILLAU_PORT:-9945}" + +RUST_LOG=runtime=trace \ +./target/debug/millau-bridge-node --dev --tmp \ + --rpc-cors=all --unsafe-rpc-external --unsafe-ws-external \ + --port 33044 --rpc-port 9934 --ws-port $MILLAU_PORT \ diff --git a/deployments/local-scripts/run-rialto-node.sh b/deployments/local-scripts/run-rialto-node.sh new file mode 100755 index 00000000000..e7987e2af36 --- /dev/null +++ b/deployments/local-scripts/run-rialto-node.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Run a development instance of the Rialto Substrate bridge node. +# To override the default port just export RIALTO_PORT=9944 + +RIALTO_PORT="${RIALTO_PORT:-9944}" + +RUST_LOG=runtime=trace \ + ./target/debug/rialto-bridge-node --dev --tmp \ + --rpc-cors=all --unsafe-rpc-external --unsafe-ws-external \ + --port 33033 --rpc-port 9933 --ws-port $RIALTO_PORT \ diff --git a/deployments/local-scripts/run-westend-node.sh b/deployments/local-scripts/run-westend-node.sh new file mode 100755 index 00000000000..1bb490fc1a8 --- /dev/null +++ b/deployments/local-scripts/run-westend-node.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Run a development instance of the Westend Substrate bridge node. +# To override the default port just export WESTEND_PORT=9945 +# +# Note: This script will not work out of the box with the bridges +# repo since it relies on a Polkadot binary. + +WESTEND_PORT="${WESTEND_PORT:-9944}" + +RUST_LOG=runtime=trace,runtime::bridge=trace \ +./target/debug/polkadot --chain=westend-dev --alice --tmp \ + --rpc-cors=all --unsafe-rpc-external --unsafe-ws-external \ + --port 33033 --rpc-port 9933 --ws-port $WESTEND_PORT \ diff --git a/deployments/monitoring/GrafanaMatrix.Dockerfile b/deployments/monitoring/GrafanaMatrix.Dockerfile new file mode 100644 index 00000000000..be1c80d4059 --- /dev/null +++ b/deployments/monitoring/GrafanaMatrix.Dockerfile @@ -0,0 +1,15 @@ +FROM ruby:alpine3.13 + +RUN apk add --no-cache git + +ENV APP_HOME /app +ENV RACK_ENV production +RUN mkdir $APP_HOME +WORKDIR $APP_HOME + +RUN git clone https://github.com/ananace/ruby-grafana-matrix.git $APP_HOME +RUN bundle install --without development + +RUN mkdir /config && touch /config/config.yml && ln -s /config/config.yml ./config.yml + +CMD ["bundle", "exec", "rackup", "-p4567"] diff --git a/deployments/monitoring/disabled.yml b/deployments/monitoring/disabled.yml new file mode 100644 index 00000000000..a0b4ed3aad0 --- /dev/null +++ b/deployments/monitoring/disabled.yml @@ -0,0 +1,15 @@ +# A disabled version of monitoring. +# +# We replace each service with a no-op container. We can't simply not include this file, +# cause the bridge-specific compose files might have overrides. +version: '3.5' +services: + prometheus-metrics: + image: alpine + + grafana-dashboard: + image: alpine + + grafana-matrix-notifier: + image: alpine + diff --git a/deployments/monitoring/docker-compose.yml b/deployments/monitoring/docker-compose.yml new file mode 100644 index 00000000000..4f7d958da4a --- /dev/null +++ b/deployments/monitoring/docker-compose.yml @@ -0,0 +1,36 @@ +version: '3.5' +services: + prometheus-metrics: + image: prom/prometheus:v2.38.0 + volumes: + - ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml + ports: + - "9090:9090" + + grafana-dashboard: + image: grafana/grafana:8.2.6 + environment: + GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASS:-admin} + GF_SERVER_ROOT_URL: ${GRAFANA_SERVER_ROOT_URL} + GF_SERVER_DOMAIN: ${GRAFANA_SERVER_DOMAIN} + volumes: + - ./monitoring/grafana/provisioning/:/etc/grafana/provisioning/:ro + - ./monitoring/grafana/dashboards/:/etc/grafana/dashboards/common:ro + ports: + - "3000:3000" + depends_on: + - prometheus-metrics + # SIGTERM won't work because of our custom entrypoint. Should be ok to use SIGKILL. + stop_signal: SIGKILL + entrypoint: sh -c "${NO_GRAFANA_STARTUP_DELAY:-echo 'sleeping for 10m' && sleep 600} && /run.sh" + + grafana-matrix-notifier: + build: + context: . + dockerfile: ./monitoring/GrafanaMatrix.Dockerfile + volumes: + - ./monitoring/grafana-matrix:/config + ports: + - "4567:4567" + depends_on: + - grafana-dashboard diff --git a/deployments/monitoring/grafana-matrix/config.yml b/deployments/monitoring/grafana-matrix/config.yml new file mode 100644 index 00000000000..123cf76a80b --- /dev/null +++ b/deployments/monitoring/grafana-matrix/config.yml @@ -0,0 +1,47 @@ +--- +# Webhook server configuration +# Or use the launch options `-o '::' -p 4567` + +# Set up your HS connections +matrix: +- name: matrix-parity-io + url: https://matrix.parity.io + # Create a user - log that user in using a post request + # curl -XPOST -d '{"type": "m.login.password", + # "user":"grafana", + # "password":"dummy-password"}' + # "https://my-matrix-server/_matrix/client/r0/login" + # Fill that access token in here + access_token: "" + #device_id: # Optional + +# The default message type for messages, should be either m.text or m.notice, +# defaults to m.text +msgtype: m.text + +# Set up notification ingress rules +rules: +- name: bridge # Name of the rule + room: "#bridges-rialto-millau-alerts:matrix.parity.io" # Room or ID + matrix: matrix-parity-io # The Matrix HS to use - defaults to first one + msgtype: m.notice + # The following values are optional: + image: true # Attach image to the notification? + embed_image: true # Upload and embed the image into the message? + #templates: + # Templates to use when rendering the notification, available placeholders: + # %TEMPLATES% - lib/grafana_matrix/templates + # $ - Environment variables + #html: "%TEMPLATES%/html.erb" # Path to HTML template + #plain: "%TEMPLATES%/plain.erb" # Path to plaintext template + #auth: + #user: example + #pass: any HTTP encodable string +#- name: other-hq +# room: "#hq:private.matrix.org +# matrix: matrix-priv + +# To use the webhook, you need to configure it into Grafana as: +# +# Url: http://:/hook?rule= +# Http Method: POST diff --git a/deployments/monitoring/grafana/dashboards/nodes.json b/deployments/monitoring/grafana/dashboards/nodes.json new file mode 100644 index 00000000000..db14c3cf1fa --- /dev/null +++ b/deployments/monitoring/grafana/dashboards/nodes.json @@ -0,0 +1,200 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "Running nodes", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "liveNow": false, + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "15m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "min" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "3m", + "frequency": "1m", + "handler": 1, + "name": "Nodes are not running", + "noDataState": "alerting", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "description": "", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "interval": null, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "maxDataPoints": null, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.2.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "up", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1, + "visible": true + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Running nodes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:111", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:112", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "", + "schemaVersion": 32, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Running nodes", + "uid": "DHl-xSW4z", + "version": 1 +} diff --git a/deployments/monitoring/grafana/provisioning/dashboards/grafana-dashboard.yaml b/deployments/monitoring/grafana/provisioning/dashboards/grafana-dashboard.yaml new file mode 100644 index 00000000000..d14ed2637d5 --- /dev/null +++ b/deployments/monitoring/grafana/provisioning/dashboards/grafana-dashboard.yaml @@ -0,0 +1,6 @@ +- name: 'default' + orgId: 1 + folder: '' + type: file + options: + path: '/etc/grafana/dashboards' \ No newline at end of file diff --git a/deployments/monitoring/grafana/provisioning/datasources/grafana-datasource.yaml b/deployments/monitoring/grafana/provisioning/datasources/grafana-datasource.yaml new file mode 100644 index 00000000000..b85cf06e2bd --- /dev/null +++ b/deployments/monitoring/grafana/provisioning/datasources/grafana-datasource.yaml @@ -0,0 +1,16 @@ +# list of datasources to insert/update depending +# whats available in the database +datasources: + # name of the datasource. Required +- name: Prometheus + # datasource type. Required + type: prometheus + # access mode. direct or proxy. Required + access: proxy + # org id. will default to orgId 1 if not specified + orgId: 1 + # url + url: http://prometheus-metrics:9090 + # mark as default datasource. Max one per org + isDefault: true + version: 1 diff --git a/deployments/monitoring/grafana/provisioning/notifiers/grafana-notifier.yaml b/deployments/monitoring/grafana/provisioning/notifiers/grafana-notifier.yaml new file mode 100644 index 00000000000..4eb6ea3863e --- /dev/null +++ b/deployments/monitoring/grafana/provisioning/notifiers/grafana-notifier.yaml @@ -0,0 +1,15 @@ +notifiers: + - name: Matrix + type: webhook + uid: notifier1 + is_default: true + send_reminder: true + frequency: 1h + disable_resolve_message: false + settings: + url: http://grafana-matrix-notifier:4567/hook?rule=bridge + http_method: POST + +delete_notifiers: + - name: Matrix + uid: notifier1 diff --git a/deployments/monitoring/prometheus/prometheus.yml b/deployments/monitoring/prometheus/prometheus.yml new file mode 100644 index 00000000000..7092bd27314 --- /dev/null +++ b/deployments/monitoring/prometheus/prometheus.yml @@ -0,0 +1,7 @@ +global: + scrape_interval: 15s +scrape_configs: + - job_name: dummy + file_sd_configs: + - files: + - /etc/prometheus/targets-*.yml diff --git a/deployments/networks/dashboard/grafana/beefy-dashboard.json b/deployments/networks/dashboard/grafana/beefy-dashboard.json new file mode 100644 index 00000000000..2e1e177641a --- /dev/null +++ b/deployments/networks/dashboard/grafana/beefy-dashboard.json @@ -0,0 +1,500 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "liveNow": false, + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "D", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Beefy best blocks not advancing", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 14, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.2.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "substrate_beefy_best_block{chain=\"rialto_local\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "exemplar": true, + "expr": "substrate_beefy_best_block{chain=\"millau_local\"}", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + }, + { + "exemplar": true, + "expr": "increase(substrate_beefy_best_block{chain=\"millau_local\"}[5m])", + "hide": true, + "interval": "", + "legendFormat": "Millau Best Beefy blocks count in last 5 minutes", + "refId": "C" + }, + { + "exemplar": true, + "expr": "increase(substrate_beefy_best_block{chain=\"rialto_local\"}[5m])", + "hide": true, + "interval": "", + "legendFormat": "Rialto Best Beefy blocks count in last 5 minutes", + "refId": "D" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1, + "visible": true + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Beefy Best block", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Beefy Lagging Sessions alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 14, + "w": 6, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.2.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "substrate_beefy_lagging_sessions{chain=\"rialto_local\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "exemplar": true, + "expr": "substrate_beefy_lagging_sessions{chain=\"millau_local\"}", + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0, + "visible": true + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Beefy Lagging Sessions", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 12, + "w": 18, + "x": 0, + "y": 14 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "8.2.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "substrate_beefy_votes_sent{chain=\"rialto_local\"}", + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "exemplar": true, + "expr": "substrate_beefy_votes_sent{chain=\"millau_local\"}", + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Beefy Votes Sent", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 32, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Beefy", + "uid": "j6cRDRh7z", + "version": 1 +} diff --git a/deployments/networks/dashboard/prometheus/millau-targets.yml b/deployments/networks/dashboard/prometheus/millau-targets.yml new file mode 100644 index 00000000000..5890c8fb3fd --- /dev/null +++ b/deployments/networks/dashboard/prometheus/millau-targets.yml @@ -0,0 +1,6 @@ +- targets: + - millau-node-alice:9615 + - millau-node-bob:9615 + - millau-node-charlie:9615 + - millau-node-dave:9615 + - millau-node-eve:9615 diff --git a/deployments/networks/dashboard/prometheus/rialto-targets.yml b/deployments/networks/dashboard/prometheus/rialto-targets.yml new file mode 100644 index 00000000000..0c89926e8c3 --- /dev/null +++ b/deployments/networks/dashboard/prometheus/rialto-targets.yml @@ -0,0 +1,6 @@ +- targets: + - rialto-node-alice:9615 + - rialto-node-bob:9615 + - rialto-node-charlie:9615 + - rialto-node-dave:9615 + - rialto-node-eve:9615 diff --git a/deployments/networks/entrypoints/rialto-chainspec-exporter-entrypoint.sh b/deployments/networks/entrypoints/rialto-chainspec-exporter-entrypoint.sh new file mode 100755 index 00000000000..eac2a80de87 --- /dev/null +++ b/deployments/networks/entrypoints/rialto-chainspec-exporter-entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -xeu + +trap "echo Exiting... TERM; exit $?" TERM + +/home/user/rialto-bridge-node build-spec \ + --chain local \ + --raw \ + --disable-default-bootnode \ + > /rialto-share/rialto-relaychain-spec-raw.json + +# we're using local driver + tmpfs for shared `/rialto-share` volume, which is populated +# by the container running this script. If this script ends, the volume will be detached +# and our chain spec will be lost when it'll go online again. Hence the never-ending +# script which keeps volume online until container is stopped. +tail -f /dev/null & wait $! diff --git a/deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh b/deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh new file mode 100755 index 00000000000..1c33dd00841 --- /dev/null +++ b/deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -xeu + +sleep 15 + +exec /home/user/substrate-relay register-parachain rialto-parachain \ + --parachain-host rialto-parachain-collator-alice \ + --parachain-port 9944 \ + --relaychain-host rialto-node-alice \ + --relaychain-port 9944 \ + --relaychain-signer //Sudo diff --git a/deployments/networks/millau.yml b/deployments/networks/millau.yml new file mode 100644 index 00000000000..d91c5d83286 --- /dev/null +++ b/deployments/networks/millau.yml @@ -0,0 +1,113 @@ +# Compose file for quickly spinning up a local instance of the Millau Substrate network. +# +# Note that the Millau network is only used for testing, so the configuration settings you see here +# are *not* recommended for a production environment. +# +# For example, do *not* keep your `node-key` in version control, and unless you're _really_ sure you +# want to provide public access to your nodes do *not* publicly expose RPC methods. +version: '3.5' +services: + millau-node-alice: &millau-bridge-node + image: ${MILLAU_BRIDGE_NODE_IMAGE:-paritytech/millau-bridge-node} + entrypoint: + - /home/user/millau-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/millau-node-bob/tcp/30333/p2p/12D3KooWM5LFR5ne4yTQ4sBSXJ75M4bDo2MAhAW2GhL3i8fe5aRb + - --alice + - --node-key=0f900c89f4e626f4a217302ab8c7d213737d00627115f318ad6fb169717ac8e0 + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + - --prometheus-external + environment: + RUST_LOG: runtime=trace,rpc=debug,txpool=trace,runtime::bridge=trace,sc_basic_authorship=trace,beefy=trace,xcm=trace + ports: + - "19933:9933" + - "19944:9944" + - "19615:9615" + + millau-node-bob: + <<: *millau-bridge-node + entrypoint: + - /home/user/millau-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H + - --bob + - --node-key=db383639ff2905d79f8e936fd5dc4416ef46b514b2f83823ec3c42753d7557bb + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + - --prometheus-external + ports: + - "20033:9933" + - "20044:9944" + - "20015:9615" + + millau-node-charlie: + <<: *millau-bridge-node + entrypoint: + - /home/user/millau-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H + - --charlie + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + - --prometheus-external + ports: + - "20133:9933" + - "20144:9944" + - "20115:9615" + + millau-node-dave: + <<: *millau-bridge-node + entrypoint: + - /home/user/millau-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H + - --dave + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + - --prometheus-external + ports: + - "20233:9933" + - "20244:9944" + - "20215:9615" + + millau-node-eve: + <<: *millau-bridge-node + entrypoint: + - /home/user/millau-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H + - --eve + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + - --prometheus-external + ports: + - "20333:9933" + - "20344:9944" + - "20315:9615" + + # Note: These are being overridden from the top level `monitoring` compose file. + prometheus-metrics: + volumes: + - ./networks/dashboard/prometheus/millau-targets.yml:/etc/prometheus/targets-millau-nodes.yml + depends_on: + - millau-node-alice + - millau-node-bob + - millau-node-charlie + - millau-node-dave + - millau-node-eve diff --git a/deployments/networks/rialto-parachain.yml b/deployments/networks/rialto-parachain.yml new file mode 100644 index 00000000000..89724183148 --- /dev/null +++ b/deployments/networks/rialto-parachain.yml @@ -0,0 +1,90 @@ +# Compose file for quickly spinning up a local instance of the Rialto Parachain network. +# +# Since Rialto Parachain is unusable without Rialto, this file depends on some Rialto +# network nodes. +version: '3.5' +services: + rialto-parachain-collator-alice: &rialto-parachain-collator + image: ${RIALTO_PARACHAIN_COLLATOR_IMAGE:-paritytech/rialto-parachain-collator} + entrypoint: > + /home/user/rialto-parachain-collator + --alice + --collator + --force-authoring + --parachain-id 2000 + --rpc-port 9933 + --ws-port 9944 + --rpc-cors=all + --unsafe-rpc-external + --unsafe-ws-external + -- + --execution wasm + --chain /rialto-share/rialto-relaychain-spec-raw.json + --rpc-port 9934 + --ws-port 9945 + volumes: + - rialto-share:/rialto-share:z + environment: + RUST_LOG: runtime=trace,rpc=trace,txpool=trace,parachain=trace,parity_ws=trace,sc_basic_authorship=trace,xcm=trace + depends_on: + - rialto-chainspec-exporter + ports: + - "20433:9933" + - "20444:9944" + + rialto-parachain-collator-bob: + <<: *rialto-parachain-collator + entrypoint: > + /home/user/rialto-parachain-collator + --bob + --collator + --force-authoring + --parachain-id 2000 + --rpc-port 9933 + --ws-port 9944 + --rpc-cors=all + --unsafe-rpc-external + --unsafe-ws-external + -- + --execution wasm + --chain /rialto-share/rialto-relaychain-spec-raw.json + --rpc-port 9934 + --ws-port 9945 + ports: + - "20533:9933" + - "20544:9944" + + rialto-parachain-collator-charlie: + <<: *rialto-parachain-collator + entrypoint: > + /home/user/rialto-parachain-collator + --charlie + --collator + --force-authoring + --parachain-id 2000 + --rpc-port 9933 + --ws-port 9944 + --rpc-cors=all + --unsafe-rpc-external + --unsafe-ws-external + -- + --execution wasm + --chain /rialto-share/rialto-relaychain-spec-raw.json + --rpc-port 9934 + --ws-port 9945 + ports: + - "20633:9933" + - "20644:9944" + + rialto-parachain-registrar: + image: ${SUBSTRATE_RELAY_IMAGE:-paritytech/substrate-relay} + entrypoint: /entrypoints/rialto-parachain-registrar-entrypoint.sh + volumes: + - ./networks/entrypoints:/entrypoints + - rialto-share:/rialto-share:z + environment: + RUST_LOG: bridge=trace + depends_on: + - rialto-node-alice + - rialto-parachain-collator-alice + diff --git a/deployments/networks/rialto.yml b/deployments/networks/rialto.yml new file mode 100644 index 00000000000..fab85b89c04 --- /dev/null +++ b/deployments/networks/rialto.yml @@ -0,0 +1,130 @@ +# Compose file for quickly spinning up a local instance of the Rialto Substrate network. +# +# Note that the Rialto network is only used for testing, so the configuration settings you see here +# are *not* recommended for a production environment. +# +# For example, do *not* keep your `node-key` in version control, and unless you're _really_ sure you +# want to provide public access to your nodes do *not* publicly expose RPC methods. +version: '3.5' +services: + rialto-node-alice: &rialto-bridge-node + image: ${RIALTO_BRIDGE_NODE_IMAGE:-paritytech/rialto-bridge-node} + entrypoint: + - /home/user/rialto-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/rialto-node-bob/tcp/30333/p2p/12D3KooWSEpHJj29HEzgPFcRYVc5X3sEuP3KgiUoqJNCet51NiMX + - --alice + - --node-key=79cf382988364291a7968ae7825c01f68c50d679796a8983237d07fe0ccf363b + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + - --prometheus-external + environment: + RUST_LOG: runtime=trace,rpc=debug,txpool=trace,runtime::bridge=trace,beefy=trace,xcm=trace + ports: + - "9933:9933" + - "9944:9944" + - "9915:9615" + + rialto-node-bob: + <<: *rialto-bridge-node + entrypoint: + - /home/user/rialto-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE + - --bob + - --node-key=4f9d0146dd9b7b3bf5a8089e3880023d1df92057f89e96e07bb4d8c2ead75bbd + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + - --prometheus-external + ports: + - "10033:9933" + - "10044:9944" + - "10015:9615" + + rialto-node-charlie: + <<: *rialto-bridge-node + entrypoint: + - /home/user/rialto-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE + - --charlie + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + - --prometheus-external + ports: + - "10133:9933" + - "10144:9944" + - "10115:9615" + + rialto-node-dave: + <<: *rialto-bridge-node + entrypoint: + - /home/user/rialto-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE + - --dave + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + - --prometheus-external + ports: + - "10233:9933" + - "10244:9944" + - "10215:9615" + + rialto-node-eve: + <<: *rialto-bridge-node + entrypoint: + - /home/user/rialto-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE + - --eve + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + - --prometheus-external + ports: + - "10333:9933" + - "10344:9944" + - "10315:9615" + + rialto-chainspec-exporter: + image: ${RIALTO_BRIDGE_NODE_IMAGE:-paritytech/rialto-bridge-node} + entrypoint: /entrypoints/rialto-chainspec-exporter-entrypoint.sh + volumes: + - ./networks/entrypoints:/entrypoints + - rialto-share:/rialto-share:z + + # Note: These are being overridden from the top level `monitoring` compose file. + prometheus-metrics: + volumes: + - ./networks/dashboard/prometheus/rialto-targets.yml:/etc/prometheus/targets-rialto-nodes.yml + depends_on: + - rialto-node-alice + - rialto-node-bob + - rialto-node-charlie + - rialto-node-dave + - rialto-node-eve + +# we're using `/rialto-share` to expose Rialto chain spec to those who are interested. Right +# now it is Rialto Parachain collator nodes. Local + tmpfs combination allows sharing writable +# in-memory volumes, which are dropped when containers are stopped. +volumes: + rialto-share: + driver: local + driver_opts: + type: "tmpfs" + device: "tmpfs" diff --git a/deployments/reverse-proxy/README.md b/deployments/reverse-proxy/README.md new file mode 100644 index 00000000000..ded81f80a1b --- /dev/null +++ b/deployments/reverse-proxy/README.md @@ -0,0 +1,15 @@ +# nginx-proxy + +This is a nginx reverse proxy configuration with Let's encrypt companion. +Main purpose is to be able to use `https://polkadot.js.org/apps` to connect to +a running network. + +## How to? + +In current directory: +```bash +docker-compose up -d +``` + +Then start `rialto` network with the same command (one folder up). `nginx` should +pick up new containers being created and automatically create a proxy setup for `Charlie`. diff --git a/deployments/reverse-proxy/docker-compose.yml b/deployments/reverse-proxy/docker-compose.yml new file mode 100644 index 00000000000..ee49e96afdd --- /dev/null +++ b/deployments/reverse-proxy/docker-compose.yml @@ -0,0 +1,45 @@ +version: '2' +services: + nginx-proxy: + image: jwilder/nginx-proxy + container_name: nginx-proxy + networks: + - nginx-proxy + - deployments_default + ports: + - "80:80" + - "443:443" + volumes: + - conf:/etc/nginx/conf.d + - vhost:/etc/nginx/vhost.d + - html:/usr/share/nginx/html + - dhparam:/etc/nginx/dhparam + - certs:/etc/nginx/certs:ro + - /var/run/docker.sock:/tmp/docker.sock:ro + - acme:/etc/acme.sh + + letsencrypt: + image: jrcs/letsencrypt-nginx-proxy-companion + container_name: nginx-proxy-le + networks: + - nginx-proxy + volumes_from: + - nginx-proxy + volumes: + - certs:/etc/nginx/certs:rw + - /var/run/docker.sock:/var/run/docker.sock:ro + - acme:/etc/acme.sh + +volumes: + conf: + vhost: + html: + dhparam: + certs: + acme: + +networks: + nginx-proxy: + driver: bridge + deployments_default: + external: true diff --git a/deployments/run.sh b/deployments/run.sh new file mode 100755 index 00000000000..a2a1900c1f0 --- /dev/null +++ b/deployments/run.sh @@ -0,0 +1,217 @@ +#!/bin/bash + +# Script used for running and updating bridge deployments. +# +# To deploy a network you can run this script with the name of the bridge (or multiple bridges) you want to run. +# +# `./run.sh westend-millau rialto-millau` +# +# To update a deployment to use the latest images available from the Docker Hub add the `update` +# argument after the bridge name. +# +# `./run.sh rialto-millau update` +# +# Once you've stopped having fun with your deployment you can take it down with: +# +# `./run.sh rialto-millau stop` +# +# Stopping the bridge will also bring down all networks that it uses. So if you have started multiple bridges +# that are using the same network (like Millau in rialto-millau and westend-millau bridges), then stopping one +# of these bridges will cause the other bridge to break. + +set -xeu + +# Since the Compose commands are using relative paths we need to `cd` into the `deployments` folder. +cd "$( dirname "${BASH_SOURCE[0]}" )" + +function show_help () { + set +x + echo " " + echo Error: $1 + echo " " + echo "Usage:" + echo " ./run.sh rialto-millau [stop|update] Run Rialto <> Millau Networks & Bridge" + echo " ./run.sh rialto-parachain-millau [stop|update] Run RialtoParachain <> Millau Networks & Bridge" + echo " ./run.sh westend-millau [stop|update] Run Westend -> Millau Networks & Bridge" + echo " ./run.sh everything|all [stop|update] Run all available Networks & Bridges" + echo " " + echo "Options:" + echo " --no-monitoring Disable monitoring" + echo " --no-ui Disable UI" + echo " --local Use prebuilt local images when starting relay and nodes" + echo " --local-substrate-relay Use prebuilt local/substrate-realy image when starting relay" + echo " --local-rialto Use prebuilt local/rialto-bridge-node image when starting nodes" + echo " --local-rialto-parachain Use prebuilt local/rialto-parachain-collator image when starting nodes" + echo " --local-millau Use prebuilt local/millau-bridge-node image when starting nodes" + echo " --no-grafana-startup-delay Start Grafana without any delay (you may see some false alerts during startup)" + echo " " + echo "You can start multiple bridges at once by passing several bridge names:" + echo " ./run.sh rialto-millau rialto-parachain-millau westend-millau [stop|update]" + exit 1 +} + +RIALTO=' -f ./networks/rialto.yml' +RIALTO_PARACHAIN=' -f ./networks/rialto-parachain.yml' +MILLAU=' -f ./networks/millau.yml' + +RIALTO_MILLAU='rialto-millau' +RIALTO_PARACHAIN_MILLAU='rialto-parachain-millau' +WESTEND_MILLAU='westend-millau' + +MONITORING=' -f ./monitoring/docker-compose.yml' +UI=' -f ./ui/docker-compose.yml' + +BRIDGES=() +NETWORKS='' +SUB_COMMAND='start' +for i in "$@" +do + case $i in + --no-monitoring) + MONITORING=" -f ./monitoring/disabled.yml" + shift + continue + ;; + --no-ui) + UI="" + shift + continue + ;; + --local) + export SUBSTRATE_RELAY_IMAGE=local/substrate-relay + export RIALTO_BRIDGE_NODE_IMAGE=local/rialto-bridge-node + export RIALTO_PARACHAIN_COLLATOR_IMAGE=local/rialto-parachain-collator + export MILLAU_BRIDGE_NODE_IMAGE=local/millau-bridge-node + export IMMEDIATE_ + shift + continue + ;; + --local-substrate-relay) + export SUBSTRATE_RELAY_IMAGE=local/substrate-relay + shift + continue + ;; + --local-rialto) + export RIALTO_BRIDGE_NODE_IMAGE=local/rialto-bridge-node + shift + continue + ;; + --local-rialto-parachain) + export RIALTO_PARACHAIN_COLLATOR_IMAGE=local/rialto-parachain-collator + shift + continue + ;; + --local-millau) + export MILLAU_BRIDGE_NODE_IMAGE=local/millau-bridge-node + shift + continue + ;; + --no-grafana-startup-delay) + export NO_GRAFANA_STARTUP_DELAY="echo 'No Grafana startup delay'" + shift + continue + ;; + everything|all) + BRIDGES=(${RIALTO_MILLAU:-} ${RIALTO_PARACHAIN_MILLAU:-} ${WESTEND_MILLAU:-}) + NETWORKS="${RIALTO:-} ${RIALTO_PARACHAIN:-} ${MILLAU:-}" + unset RIALTO RIALTO_PARACHAIN MILLAU RIALTO_MILLAU RIALTO_PARACHAIN_MILLAU WESTEND_MILLAU + shift + ;; + rialto-millau) + BRIDGES+=(${RIALTO_MILLAU:-}) + NETWORKS+="${RIALTO:-} ${MILLAU:-}" + unset RIALTO MILLAU RIALTO_MILLAU + shift + ;; + rialto-parachain-millau) + BRIDGES+=(${RIALTO_PARACHAIN_MILLAU:-}) + NETWORKS+="${RIALTO:-} ${RIALTO_PARACHAIN:-} ${MILLAU:-}" + unset RIALTO RIALTO_PARACHAIN MILLAU RIALTO_PARACHAIN_MILLAU + shift + ;; + westend-millau) + BRIDGES+=(${WESTEND_MILLAU:-}) + NETWORKS+=${MILLAU:-} + unset MILLAU WESTEND_MILLAU + shift + ;; + start|stop|update) + SUB_COMMAND=$i + shift + ;; + *) + show_help "Unknown option: $i" + ;; + esac +done + +if [ ${#BRIDGES[@]} -eq 0 ]; then + show_help "Missing bridge name." +fi + +COMPOSE_FILES=$NETWORKS$MONITORING$UI + +# Compose looks for .env files in the the current directory by default, we don't want that +COMPOSE_ARGS="--project-directory ." +# Path to env file that we want to use. Compose only accepts single `--env-file` argument, +# so we'll be using the last .env file we'll found. +COMPOSE_ENV_FILE='' + +for BRIDGE in "${BRIDGES[@]}" +do + BRIDGE_PATH="./bridges/$BRIDGE" + BRIDGE=" -f $BRIDGE_PATH/docker-compose.yml" + COMPOSE_FILES=$BRIDGE$COMPOSE_FILES + + # Remember .env file to use in docker-compose call + if [[ -f "$BRIDGE_PATH/.env" ]]; then + COMPOSE_ENV_FILE=" --env-file $BRIDGE_PATH/.env" + fi + + # Read and source variables from .env file so we can use them here + grep -e MATRIX_ACCESS_TOKEN -e WITH_PROXY $BRIDGE_PATH/.env > .env2 && . ./.env2 && rm .env2 + if [ ! -z ${MATRIX_ACCESS_TOKEN+x} ]; then + sed -i "s/access_token.*/access_token: \"$MATRIX_ACCESS_TOKEN\"/" ./monitoring/grafana-matrix/config.yml + fi +done + +# Final COMPOSE_ARGS +COMPOSE_ARGS="$COMPOSE_ARGS $COMPOSE_ENV_FILE" + +# Check the sub-command, perhaps we just mean to stop the network instead of starting it. +if [ "$SUB_COMMAND" == "stop" ]; then + + if [ ! -z ${WITH_PROXY+x} ]; then + cd ./reverse-proxy + docker-compose down + cd - + fi + + docker-compose $COMPOSE_ARGS $COMPOSE_FILES down + + exit 0 +fi + +# See if we want to update the docker images before starting the network. +if [ "$SUB_COMMAND" == "update" ]; then + + # Stop the proxy cause otherwise the network can't be stopped + if [ ! -z ${WITH_PROXY+x} ]; then + cd ./reverse-proxy + docker-compose down + cd - + fi + + + docker-compose $COMPOSE_ARGS $COMPOSE_FILES pull + docker-compose $COMPOSE_ARGS $COMPOSE_FILES down + docker-compose $COMPOSE_ARGS $COMPOSE_FILES build +fi + +docker-compose $COMPOSE_ARGS $COMPOSE_FILES up -d + +# Start the proxy if needed +if [ ! -z ${WITH_PROXY+x} ]; then + cd ./reverse-proxy + docker-compose up -d +fi diff --git a/deployments/types-millau.json b/deployments/types-millau.json new file mode 100644 index 00000000000..88b67f70b05 --- /dev/null +++ b/deployments/types-millau.json @@ -0,0 +1,191 @@ +{ + "--1": "Millau Types", + "MillauAddress": "AccountId", + "MillauLookupSource": "AccountId", + "MillauBalance": "u64", + "MillauBlockHash": "H512", + "MillauBlockNumber": "u64", + "MillauHeader": { + "parent_Hash": "MillauBlockHash", + "number": "Compact", + "state_root": "MillauBlockHash", + "extrinsics_root": "MillauBlockHash", + "digest": "MillauDigest" + }, + "MillauDigest": { + "logs": "Vec" + }, + "MillauDigestItem": { + "_enum": { + "Other": "Vec", + "AuthoritiesChange": "Vec", + "ChangesTrieRoot": "MillauBlockHash", + "SealV0": "SealV0", + "Consensus": "Consensus", + "Seal": "Seal", + "PreRuntime": "PreRuntime" + } + }, + "--2": "Rialto Types", + "RialtoAddress": "MultiAddress", + "RialtoLookupSource": "MultiAddress", + "RialtoBalance": "u128", + "RialtoBlockHash": "H256", + "RialtoBlockNumber": "u32", + "RialtoHeader": { + "parent_Hash": "RialtoBlockHash", + "number": "Compact", + "state_root": "RialtoBlockHash", + "extrinsics_root": "RialtoBlockHash", + "digest": "RialtoDigest" + }, + "RialtoDigest": { + "logs": "Vec" + }, + "RialtoDigestItem": { + "_enum": { + "Other": "Vec", + "AuthoritiesChange": "Vec", + "ChangesTrieRoot": "RialtoBlockHash", + "SealV0": "SealV0", + "Consensus": "Consensus", + "Seal": "Seal", + "PreRuntime": "PreRuntime" + } + }, + "--3": "Common types", + "AccountSigner": "MultiSigner", + "SpecVersion": "u32", + "RelayerId": "AccountId", + "SourceAccountId": "AccountId", + "ImportedHeader": { + "header": "BridgedHeader", + "requires_justification": "bool", + "is_finalized": "bool", + "signal_hash": "Option" + }, + "AuthoritySet": { + "authorities": "AuthorityList", + "set_id": "SetId" + }, + "Id": "[u8; 4]", + "ChainId": "Id", + "LaneId": "Id", + "MessageNonce": "u64", + "BridgeMessageId": "(Id, u64)", + "MessageKey": { + "lane_id": "LaneId", + "nonce:": "MessageNonce" + }, + "InboundRelayer": "AccountId", + "InboundLaneData": { + "relayers": "Vec", + "last_confirmed_nonce": "MessageNonce" + }, + "UnrewardedRelayer": { + "relayer": "RelayerId", + "messages": "DeliveredMessages" + }, + "DeliveredMessages": { + "begin": "MessageNonce", + "end": "MessageNonce" + }, + "OutboundLaneData": { + "oldest_unpruned_nonce": "MessageNonce", + "latest_received_nonce": "MessageNonce", + "latest_generated_nonce": "MessageNonce" + }, + "MessageData": { + "payload": "MessagePayload", + "fee": "Fee" + }, + "MessagePayload": "Vec", + "BridgedOpaqueCall": "Vec", + "OutboundMessageFee": "Fee", + "OutboundPayload": { + "spec_version": "SpecVersion", + "weight": "Weight", + "origin": "CallOrigin", + "dispatch_fee_payment": "DispatchFeePayment", + "call": "BridgedOpaqueCall" + }, + "CallOrigin": { + "_enum": { + "SourceRoot": "()", + "TargetAccount": "(SourceAccountId, MultiSigner, MultiSignature)", + "SourceAccount": "SourceAccountId" + } + }, + "DispatchFeePayment": { + "_enum": { + "AtSourceChain": "()", + "AtTargetChain": "()" + } + }, + "MultiSigner": { + "_enum": { + "Ed25519": "H256", + "Sr25519": "H256", + "Ecdsa": "[u8;33]" + } + }, + "MessagesProofOf": { + "bridged_header_hash": "BridgedBlockHash", + "storage_proof": "Vec", + "lane": "LaneId", + "nonces_start": "MessageNonce", + "nonces_end": "MessageNonce" + }, + "StorageProofItem": "Vec", + "MessagesDeliveryProofOf": { + "bridged_header_hash": "BridgedBlockHash", + "storage_proof": "Vec", + "lane": "LaneId" + }, + "UnrewardedRelayersState": { + "unrewarded_relayer_entries": "MessageNonce", + "messages_in_oldest_entry": "MessageNonce", + "total_messages": "MessageNonce" + }, + "AncestryProof": "()", + "MessageFeeData": { + "lane_id": "LaneId", + "payload": "OutboundPayload" + }, + "Precommit": { + "target_hash": "BridgedBlockHash", + "target_number": "BridgedBlockNumber" + }, + "AuthoritySignature": "[u8;64]", + "AuthorityId": "[u8;32]", + "SignedPrecommit": { + "precommit": "Precommit", + "signature": "AuthoritySignature", + "id": "AuthorityId" + }, + "Commit": { + "target_hash": "BridgedBlockHash", + "target_number": "BridgedBlockNumber", + "precommits": "Vec" + }, + "GrandpaJustification": { + "round": "u64", + "commit": "Commit", + "votes_ancestries": "Vec" + }, + "Address": "MillauAddress", + "LookupSource": "MillauLookupSource", + "Fee": "MillauBalance", + "Balance": "MillauBalance", + "Hash": "MillauBlockHash", + "BlockHash": "MillauBlockHash", + "BlockNumber": "MillauBlockNumber", + "BridgedBlockHash": "RialtoBlockHash", + "BridgedBlockNumber": "RialtoBlockNumber", + "BridgedHeader": "RialtoHeader", + "Parameter": { + "_enum": { + "MillauToRialtoConversionRate": "u128" + } + } +} diff --git a/deployments/types-rialto.json b/deployments/types-rialto.json new file mode 100644 index 00000000000..02ceaf676d6 --- /dev/null +++ b/deployments/types-rialto.json @@ -0,0 +1,191 @@ +{ + "--1": "Millau Types", + "MillauAddress": "AccountId", + "MillauLookupSource": "AccountId", + "MillauBalance": "u64", + "MillauBlockHash": "H512", + "MillauBlockNumber": "u64", + "MillauHeader": { + "parent_Hash": "MillauBlockHash", + "number": "Compact", + "state_root": "MillauBlockHash", + "extrinsics_root": "MillauBlockHash", + "digest": "MillauDigest" + }, + "MillauDigest": { + "logs": "Vec" + }, + "MillauDigestItem": { + "_enum": { + "Other": "Vec", + "AuthoritiesChange": "Vec", + "ChangesTrieRoot": "MillauBlockHash", + "SealV0": "SealV0", + "Consensus": "Consensus", + "Seal": "Seal", + "PreRuntime": "PreRuntime" + } + }, + "--2": "Rialto Types", + "RialtoAddress": "MultiAddress", + "RialtoLookupSource": "MultiAddress", + "RialtoBalance": "u128", + "RialtoBlockHash": "H256", + "RialtoBlockNumber": "u32", + "RialtoHeader": { + "parent_Hash": "RialtoBlockHash", + "number": "Compact", + "state_root": "RialtoBlockHash", + "extrinsics_root": "RialtoBlockHash", + "digest": "RialtoDigest" + }, + "RialtoDigest": { + "logs": "Vec" + }, + "RialtoDigestItem": { + "_enum": { + "Other": "Vec", + "AuthoritiesChange": "Vec", + "ChangesTrieRoot": "RialtoBlockHash", + "SealV0": "SealV0", + "Consensus": "Consensus", + "Seal": "Seal", + "PreRuntime": "PreRuntime" + } + }, + "--3": "Common types", + "AccountSigner": "MultiSigner", + "SpecVersion": "u32", + "RelayerId": "AccountId", + "SourceAccountId": "AccountId", + "ImportedHeader": { + "header": "BridgedHeader", + "requires_justification": "bool", + "is_finalized": "bool", + "signal_hash": "Option" + }, + "AuthoritySet": { + "authorities": "AuthorityList", + "set_id": "SetId" + }, + "Id": "[u8; 4]", + "ChainId": "Id", + "LaneId": "Id", + "MessageNonce": "u64", + "BridgeMessageId": "(Id, u64)", + "MessageKey": { + "lane_id": "LaneId", + "nonce:": "MessageNonce" + }, + "InboundRelayer": "AccountId", + "InboundLaneData": { + "relayers": "Vec", + "last_confirmed_nonce": "MessageNonce" + }, + "UnrewardedRelayer": { + "relayer": "RelayerId", + "messages": "DeliveredMessages" + }, + "DeliveredMessages": { + "begin": "MessageNonce", + "end": "MessageNonce" + }, + "OutboundLaneData": { + "oldest_unpruned_nonce": "MessageNonce", + "latest_received_nonce": "MessageNonce", + "latest_generated_nonce": "MessageNonce" + }, + "MessageData": { + "payload": "MessagePayload", + "fee": "Fee" + }, + "MessagePayload": "Vec", + "BridgedOpaqueCall": "Vec", + "OutboundMessageFee": "Fee", + "OutboundPayload": { + "spec_version": "SpecVersion", + "weight": "Weight", + "origin": "CallOrigin", + "dispatch_fee_payment": "DispatchFeePayment", + "call": "BridgedOpaqueCall" + }, + "CallOrigin": { + "_enum": { + "SourceRoot": "()", + "TargetAccount": "(SourceAccountId, MultiSigner, MultiSignature)", + "SourceAccount": "SourceAccountId" + } + }, + "DispatchFeePayment": { + "_enum": { + "AtSourceChain": "()", + "AtTargetChain": "()" + } + }, + "MultiSigner": { + "_enum": { + "Ed25519": "H256", + "Sr25519": "H256", + "Ecdsa": "[u8;33]" + } + }, + "MessagesProofOf": { + "bridged_header_hash": "BridgedBlockHash", + "storage_proof": "Vec", + "lane": "LaneId", + "nonces_start": "MessageNonce", + "nonces_end": "MessageNonce" + }, + "StorageProofItem": "Vec", + "MessagesDeliveryProofOf": { + "bridged_header_hash": "BridgedBlockHash", + "storage_proof": "Vec", + "lane": "LaneId" + }, + "UnrewardedRelayersState": { + "unrewarded_relayer_entries": "MessageNonce", + "messages_in_oldest_entry": "MessageNonce", + "total_messages": "MessageNonce" + }, + "AncestryProof": "()", + "MessageFeeData": { + "lane_id": "LaneId", + "payload": "OutboundPayload" + }, + "Precommit": { + "target_hash": "BridgedBlockHash", + "target_number": "BridgedBlockNumber" + }, + "AuthoritySignature": "[u8;64]", + "AuthorityId": "[u8;32]", + "SignedPrecommit": { + "precommit": "Precommit", + "signature": "AuthoritySignature", + "id": "AuthorityId" + }, + "Commit": { + "target_hash": "BridgedBlockHash", + "target_number": "BridgedBlockNumber", + "precommits": "Vec" + }, + "GrandpaJustification": { + "round": "u64", + "commit": "Commit", + "votes_ancestries": "Vec" + }, + "Address": "RialtoAddress", + "LookupSource": "RialtoLookupSource", + "Fee": "RialtoBalance", + "Balance": "RialtoBalance", + "BlockHash": "RialtoBlockHash", + "BlockNumber": "RialtoBlockNumber", + "BridgedBlockHash": "MillauBlockHash", + "BridgedBlockNumber": "MillauBlockNumber", + "BridgedHeader": "MillauHeader", + "Parameter": { + "_enum": { + "RialtoToMillauConversionRate": "u128" + } + }, + "ValidationCodeHash": "H256" +} diff --git a/deployments/types/build.sh b/deployments/types/build.sh new file mode 100755 index 00000000000..be96459a6da --- /dev/null +++ b/deployments/types/build.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# The script generates JSON type definition files in `./deployment` directory to be used for +# JS clients. +# +# It works by creating definitions for each side of the different bridge pairs we support +# (Rialto<>Millau at the moment). +# +# To avoid duplication each bridge pair has a JSON file with common definitions, as well as a +# general JSON file with common definitions regardless of the bridge pair. These files are then +# merged with chain-specific type definitions. + +set -eux + +# Make sure we are in the right dir. +cd $(dirname $(realpath $0)) + +# Create types for our supported bridge pairs (Rialto<>Millau) +jq -s '.[0] * .[1] * .[2]' rialto-millau.json common.json rialto.json > ../types-rialto.json +jq -s '.[0] * .[1] * .[2]' rialto-millau.json common.json millau.json > ../types-millau.json diff --git a/deployments/types/common.json b/deployments/types/common.json new file mode 100644 index 00000000000..7247e546bec --- /dev/null +++ b/deployments/types/common.json @@ -0,0 +1,123 @@ +{ + "--3": "Common types", + "AccountSigner": "MultiSigner", + "SpecVersion": "u32", + "RelayerId": "AccountId", + "SourceAccountId": "AccountId", + "ImportedHeader": { + "header": "BridgedHeader", + "requires_justification": "bool", + "is_finalized": "bool", + "signal_hash": "Option" + }, + "AuthoritySet": { + "authorities": "AuthorityList", + "set_id": "SetId" + }, + "Id": "[u8; 4]", + "ChainId": "Id", + "LaneId": "Id", + "MessageNonce": "u64", + "BridgeMessageId": "(Id, u64)", + "MessageKey": { + "lane_id": "LaneId", + "nonce:": "MessageNonce" + }, + "InboundRelayer": "AccountId", + "InboundLaneData": { + "relayers": "Vec", + "last_confirmed_nonce": "MessageNonce" + }, + "UnrewardedRelayer": { + "relayer": "RelayerId", + "messages": "DeliveredMessages" + }, + "DeliveredMessages": { + "begin": "MessageNonce", + "end": "MessageNonce" + }, + "OutboundLaneData": { + "oldest_unpruned_nonce": "MessageNonce", + "latest_received_nonce": "MessageNonce", + "latest_generated_nonce": "MessageNonce" + + }, + "MessageData": { + "payload": "MessagePayload", + "fee": "Fee" + }, + "MessagePayload": "Vec", + "BridgedOpaqueCall": "Vec", + "OutboundMessageFee": "Fee", + "OutboundPayload": { + "spec_version": "SpecVersion", + "weight": "Weight", + "origin": "CallOrigin", + "dispatch_fee_payment": "DispatchFeePayment", + "call": "BridgedOpaqueCall" + }, + "CallOrigin": { + "_enum": { + "SourceRoot": "()", + "TargetAccount": "(SourceAccountId, MultiSigner, MultiSignature)", + "SourceAccount": "SourceAccountId" + } + }, + "DispatchFeePayment": { + "_enum": { + "AtSourceChain": "()", + "AtTargetChain": "()" + } + }, + "MultiSigner": { + "_enum": { + "Ed25519": "H256", + "Sr25519": "H256", + "Ecdsa": "[u8;33]" + } + }, + "MessagesProofOf": { + "bridged_header_hash": "BridgedBlockHash", + "storage_proof": "Vec", + "lane": "LaneId", + "nonces_start": "MessageNonce", + "nonces_end": "MessageNonce" + }, + "StorageProofItem": "Vec", + "MessagesDeliveryProofOf": { + "bridged_header_hash": "BridgedBlockHash", + "storage_proof": "Vec", + "lane": "LaneId" + }, + "UnrewardedRelayersState": { + "unrewarded_relayer_entries": "MessageNonce", + "messages_in_oldest_entry": "MessageNonce", + "total_messages": "MessageNonce" + }, + "AncestryProof": "()", + "MessageFeeData": { + "lane_id": "LaneId", + "payload": "OutboundPayload" + }, + "Precommit": { + "target_hash": "BridgedBlockHash", + "target_number": "BridgedBlockNumber" + }, + "AuthoritySignature": "[u8;64]", + "AuthorityId": "[u8;32]", + "SignedPrecommit": { + "precommit": "Precommit", + "signature": "AuthoritySignature", + "id": "AuthorityId" + }, + "Commit": { + "target_hash": "BridgedBlockHash", + "target_number": "BridgedBlockNumber", + "precommits": "Vec" + }, + "GrandpaJustification": { + "round": "u64", + "commit": "Commit", + "votes_ancestries": "Vec" + } +} diff --git a/deployments/types/millau.json b/deployments/types/millau.json new file mode 100644 index 00000000000..589d5619df4 --- /dev/null +++ b/deployments/types/millau.json @@ -0,0 +1,17 @@ +{ + "Address": "MillauAddress", + "LookupSource": "MillauLookupSource", + "Fee": "MillauBalance", + "Balance": "MillauBalance", + "Hash": "MillauBlockHash", + "BlockHash": "MillauBlockHash", + "BlockNumber": "MillauBlockNumber", + "BridgedBlockHash": "RialtoBlockHash", + "BridgedBlockNumber": "RialtoBlockNumber", + "BridgedHeader": "RialtoHeader", + "Parameter": { + "_enum": { + "MillauToRialtoConversionRate": "u128" + } + } +} diff --git a/deployments/types/rialto-millau.json b/deployments/types/rialto-millau.json new file mode 100644 index 00000000000..971cf666d47 --- /dev/null +++ b/deployments/types/rialto-millau.json @@ -0,0 +1,56 @@ +{ + "--1": "Millau Types", + "MillauAddress": "AccountId", + "MillauLookupSource": "AccountId", + "MillauBalance": "u64", + "MillauBlockHash": "H512", + "MillauBlockNumber": "u64", + "MillauHeader": { + "parent_Hash": "MillauBlockHash", + "number": "Compact", + "state_root": "MillauBlockHash", + "extrinsics_root": "MillauBlockHash", + "digest": "MillauDigest" + }, + "MillauDigest": { + "logs": "Vec" + }, + "MillauDigestItem": { + "_enum": { + "Other": "Vec", + "AuthoritiesChange": "Vec", + "ChangesTrieRoot": "MillauBlockHash", + "SealV0": "SealV0", + "Consensus": "Consensus", + "Seal": "Seal", + "PreRuntime": "PreRuntime" + } + }, + "--2": "Rialto Types", + "RialtoAddress": "MultiAddress", + "RialtoLookupSource": "MultiAddress", + "RialtoBalance": "u128", + "RialtoBlockHash": "H256", + "RialtoBlockNumber": "u32", + "RialtoHeader": { + "parent_Hash": "RialtoBlockHash", + "number": "Compact", + "state_root": "RialtoBlockHash", + "extrinsics_root": "RialtoBlockHash", + "digest": "RialtoDigest" + }, + "RialtoDigest": { + "logs": "Vec" + }, + "RialtoDigestItem": { + "_enum": { + "Other": "Vec", + "AuthoritiesChange": "Vec", + "ChangesTrieRoot": "RialtoBlockHash", + "SealV0": "SealV0", + "Consensus": "Consensus", + "Seal": "Seal", + "PreRuntime": "PreRuntime" + } + } +} diff --git a/deployments/types/rialto.json b/deployments/types/rialto.json new file mode 100644 index 00000000000..77c30b7cc2d --- /dev/null +++ b/deployments/types/rialto.json @@ -0,0 +1,17 @@ +{ + "Address": "RialtoAddress", + "LookupSource": "RialtoLookupSource", + "Fee": "RialtoBalance", + "Balance": "RialtoBalance", + "BlockHash": "RialtoBlockHash", + "BlockNumber": "RialtoBlockNumber", + "BridgedBlockHash": "MillauBlockHash", + "BridgedBlockNumber": "MillauBlockNumber", + "BridgedHeader": "MillauHeader", + "Parameter": { + "_enum": { + "RialtoToMillauConversionRate": "u128" + } + }, + "ValidationCodeHash": "H256" +} diff --git a/deployments/ui/README.md b/deployments/ui/README.md new file mode 100644 index 00000000000..ad946fc699b --- /dev/null +++ b/deployments/ui/README.md @@ -0,0 +1,23 @@ +# bridges-ui + +This is a Bridges UI docker configuration file. The source of the Bridges UI code +can be found in [the repository](https://github.com/paritytech/parity-bridges-ui). +The CI should create and publish a docker image that is used by this configuration +file, so that the code is always using the latest version. +The UI is configured to point to local Rialto and Millau nodes to retrieve the require +data. + +This image can be used together with `nginx-proxy` to expose the UI externally. See +`VIRTUAL_*` and `LETSENCRYPT_*` environment variables. + +After start the UI is available at `http://localhost:8080` + +## How to? + +In current directory: +```bash +docker-compose up -d +``` + +Then start `rialto` & `millau` networks with the same command (one folder up) or +run the full setup by using `../run.sh` script. diff --git a/deployments/ui/docker-compose.yml b/deployments/ui/docker-compose.yml new file mode 100644 index 00000000000..2d400609c44 --- /dev/null +++ b/deployments/ui/docker-compose.yml @@ -0,0 +1,14 @@ +version: '3.5' +services: + bridges-ui: + image: paritytech/parity-bridges-ui + environment: + VIRTUAL_HOST: ui.brucke.link + VIRTUAL_PORT: 80 + LETSENCRYPT_HOST: ui.brucke.link + LETSENCRYPT_EMAIL: admin@parity.io + CHAIN_1_SUBSTRATE_PROVIDER: ${UI_CHAIN_1:-ws://localhost:9944} + CHAIN_2_SUBSTRATE_PROVIDER: ${UI_CHAIN_2:-ws://localhost:19944} + stop_signal: SIGKILL + ports: + - "8080:80" diff --git a/docs/complex-relay.html b/docs/complex-relay.html new file mode 100644 index 00000000000..21524bfd049 --- /dev/null +++ b/docs/complex-relay.html @@ -0,0 +1,85 @@ + + + + + + Complex Relay + + +

Complex Relay

+

+ Both Source Chain and Target Chains have Bridge Messages pallets deployed. They also have required + finality pallets deployed - we don't care about finality type here - they can be either Bridge GRANDPA, + or Bridge Parachains finality pallets, or any combination of those.
+

+

+ There are 4-6 relayer subprocesses inside the Complex Relayer. They include two message relayers, + serving the lane in both directions and 2-4 Complex Relayers (depending on the finality type of Source + and Target Chains).
+

+

+ The following diagram shows the way the complex relayer serves the lane in single direction. Everything + below may be applied to the opposite direction if you'll swap the Source and Target Chains. +

+
+ sequenceDiagram + participant Source Chain + participant Complex Relayer + participant Target Chain + + Note right of Source Chain: Finalized: 480, Target Finalized: 50, Sent Messages: 42, Confirmed Messages: 42 + Note left of Target Chain: Finalized: 60, Source Finalized: 420, Received Messages: 42 + + Source Chain ->> Source Chain: someone Sends Message 43 + Source Chain ->> Source Chain: Import and Finalize Block 481 + + Source Chain ->> Complex Relayer: notes new outbound message 43 at Source Chain Block 481 + Note right of Complex Relayer: can't deliver message 43, Source Chain Block 481 is not relayed + Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Source Chain Block 481 + + Source Chain ->> Complex Relayer: Read Finality Proof of Block 481 + Complex Relayer ->> Target Chain: Submit Finality Proof of Block 481 + Target Chain ->> Target Chain: Import and Finalize Block 61 + Note left of Target Chain: Finalized: 61, Source Finalized: 481, Received Messages: 42 + + Source Chain ->> Complex Relayer: Read Proof of Message 43 at Block 481 + Complex Relayer ->> Target Chain: Submit Proof of Message 43 at Block 481 + Target Chain ->> Target Chain: Import and Finalize Block 62 + Note left of Target Chain: Finalized: 62, Source Finalized: 481, Received Messages: { rewarded: 42, messages-relayer-account: [43] } + + Target Chain ->> Complex Relayer: notes new unrewarded relayer at Target Chain Block 62 + Note right of Complex Relayer: can't relay delivery confirmations because Target Chain Block 62 is not relayed + Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Target Chain Block 62 + + Target Chain ->> Complex Relayer: Read Finality Proof of Block 62 + Complex Relayer ->> Source Chain: Submit Finality Proof of Block 62 + Source Chain ->> Source Chain: Import and Finalize Block 482 + Note right of Source Chain: Finalized: 482, Target Finalized: 62, Confirmed Messages: 42 + + Target Chain ->> Complex Relayer: Read Proof of Message 43 Delivery at Block 62 + Complex Relayer ->> Source Chain: Submit Proof of Message 43 Delivery at Block 612 + Source Chain ->> Source Chain: rewards messages-relayer-account for delivering message [43] + Source Chain ->> Source Chain: prune delivered message 43 from runtime storage + Note right of Source Chain: Finalized: 482, Target Finalized: 61, Confirmed Messages: 43 + + Source Chain ->> Source Chain: someone Sends Message 44 + Source Chain ->> Source Chain: Import and Finalize Block 483 + + Source Chain ->> Complex Relayer: notes new outbound message 44 at Source Chain Block 483 and new confirmed message 43 + Note right of Complex Relayer: can't deliver message 44, Source Chain Block 483 is not relayed + Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Source Chain Block 483 + + Source Chain ->> Complex Relayer: Read Finality Proof of Block 483 + Complex Relayer ->> Target Chain: Submit Finality Proof of Block 483 + Target Chain ->> Target Chain: Import and Finalize Block 63 + Note left of Target Chain: Finalized: 63, Source Finalized: 483, Received Messages: { rewarded: 42, messages-relayer-account: [43] } + + Source Chain ->> Complex Relayer: Read Proof of Message 44 and Proof of Message 43 reward at Block 483 + Complex Relayer ->> Target Chain: Submit Proof of Message 44 and Proof of Message 43 reward at Block 483 + Target Chain ->> Target Chain: Import and Finalize Block 64 + Note left of Target Chain: Finalized: 64, Source Finalized: 483, Received Messages: { rewarded: 43, messages-relayer-account: [44] }--> +
+ + + + diff --git a/docs/dockerhub-bridges-common-relay.README.md b/docs/dockerhub-bridges-common-relay.README.md new file mode 100644 index 00000000000..d199227c9c6 --- /dev/null +++ b/docs/dockerhub-bridges-common-relay.README.md @@ -0,0 +1 @@ +# bridges-common-relay diff --git a/docs/dockerhub-millau-bridge-node.README.md b/docs/dockerhub-millau-bridge-node.README.md new file mode 100644 index 00000000000..481ad46b7c6 --- /dev/null +++ b/docs/dockerhub-millau-bridge-node.README.md @@ -0,0 +1 @@ +# millau-bridge-node diff --git a/docs/dockerhub-rialto-bridge-node.README.md b/docs/dockerhub-rialto-bridge-node.README.md new file mode 100644 index 00000000000..2393e6f8129 --- /dev/null +++ b/docs/dockerhub-rialto-bridge-node.README.md @@ -0,0 +1 @@ +# rialto-bridge-node diff --git a/docs/dockerhub-rialto-parachain-collator.README.md b/docs/dockerhub-rialto-parachain-collator.README.md new file mode 100644 index 00000000000..a09f6b1561b --- /dev/null +++ b/docs/dockerhub-rialto-parachain-collator.README.md @@ -0,0 +1 @@ +# rialto-parachain-collator diff --git a/docs/dockerhub-substrate-relay.README.md b/docs/dockerhub-substrate-relay.README.md new file mode 100644 index 00000000000..1a9f22c425c --- /dev/null +++ b/docs/dockerhub-substrate-relay.README.md @@ -0,0 +1 @@ +# substrate-relay diff --git a/docs/grandpa-finality-relay.html b/docs/grandpa-finality-relay.html new file mode 100644 index 00000000000..4136621b1a4 --- /dev/null +++ b/docs/grandpa-finality-relay.html @@ -0,0 +1,47 @@ + + + + + + GRANDPA Finality Relay + + +

GRANDPA Finality Relay

+

+ Source Chain is running GRANDPA Finality Gadget. Bridge GRANDPA finality pallet is deployed at + Target Chain runtime. Relayer is configured to relay Source Chain finality to Target Chain. +

+
+ sequenceDiagram + participant Source Chain + participant Relayer + participant Target Chain + Note left of Source Chain: Best: 500, Finalized: 480, Authorities Set Index: 42 + Note right of Target Chain: Uninitialized + + Source Chain ->> Relayer: Read Initialization Data + Relayer ->> Target Chain: Initialize Bridge GRANDPA Finality Pallet + Note right of Target Chain: Finalized: 480, Authorities Set Index: 42 + + Source Chain ->> Source Chain: Import Block 501 + Source Chain ->> Source Chain: Import Block 502 + Source Chain ->> Source Chain: Finalize Block 495 + Source Chain ->> Relayer: Read Finality Proof of Block 495 + Relayer ->> Target Chain: Finality Proof of Block 495 + Note right of Target Chain: Finalized: 495, Authorities Set Index: 42 + + Source Chain ->> Source Chain: Import Block 503 that changes Authorities Set to 43 + Source Chain ->> Source Chain: Finalize Block 500 + Note left of Relayer: Relayer Misses Finality Notification for Block 500 + + Source Chain ->> Source Chain: Import Block 504 + Source Chain ->> Source Chain: Finalize Mandatory Block 503 + Source Chain ->> Source Chain: Finalize Block 504 + Source Chain ->> Relayer: Read Finality Proof of Mandatory Block 503 + Relayer ->> Target Chain: Finality Proof of Block 503 + Note right of Target Chain: Finalized: 503, Authorities Set Index: 43 +
+ + + + diff --git a/docs/high-level-overview.md b/docs/high-level-overview.md new file mode 100644 index 00000000000..449224124af --- /dev/null +++ b/docs/high-level-overview.md @@ -0,0 +1,181 @@ +# High-Level Bridge Documentation + +This document gives a brief, abstract description of main components that may be found in this repository. +If you want to see how we're using them to build Rococo <> Wococo (Kusama <> Polkadot) bridge, please +refer to the [Polkadot <> Kusama Bridge](./polkadot-kusama-bridge-overview.md). + +## Purpose + +This repo contains all components required to build a trustless connection between standalone Substrate chains, +that are using GRANDPA finality, their parachains or any combination of those. On top of this connection, we +offer a messaging pallet that provides means to organize messages exchange. + +On top of that layered infrastructure, anyone may build their own bridge applications - e.g. [XCM messaging](./polkadot-kusama-bridge-overview.md), +[encoded calls messaging](https://github.com/paritytech/parity-bridges-common/releases/tag/encoded-calls-messaging) and so on. + +## Terminology + +Even though we support (and require) two-way bridging, the documentation will generally talk about +a one-sided interaction. That's to say, we will only talk about syncing finality proofs and messages +from a _source_ chain to a _target_ chain. This is because the two-sided interaction is really just the +one-sided interaction with the source and target chains switched. + +The bridge has both on-chain (pallets) and offchain (relayers) components. + +## On-chain components + +On-chain bridge components are pallets that are deployed at the chain runtime. Finality pallets require +deployment at the target chain, while messages pallet needs to be deployed at both, source +and target chains. + +### Bridge GRANDPA Finality Pallet + +A GRANDPA light client of the source chain built into the target chain's runtime. It provides a "source of truth" +about the source chain headers which have been finalized. This is useful for higher level applications. + +The pallet tracks current GRANDPA authorities set and only accepts finality proofs (GRANDPA justifications), +generated by the current authorities set. The GRANDPA protocol itself requires current authorities set to +generate explicit justification for the header that enacts next authorities set. Such headers and their finality +proofs are called mandatory in the pallet and relayer pays no fee for such headers submission. + +The pallet does not require all headers to be imported or provided. The relayer itself chooses which headers +he wants to submit (with the exception of mandatory headers). + +More: [pallet level documentation and code](../modules/grandpa/). + +### Bridge Parachains Finality Pallet + +Parachains are not supposed to have their own finality, so we can't use bridge GRANDPA pallet to verify their +finality proofs. Instead, they rely on their relay chain finality. The parachain header is considered final, +when it is accepted by the [`paras` pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras) +at its relay chain. Obviously, the relay chain block, where it is accepted, must also be finalized by the relay +chain GRANDPA gadget. + +That said, the bridge parachains pallet accepts storage proof of one or several parachain heads, inserted to the +[`Heads`](https://github.com/paritytech/polkadot/blob/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras/mod.rs#L642) +map of the [`paras` pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras). +To verify this storage proof, the pallet uses relay chain header, imported earlier by the bridge GRANDPA pallet. + +The pallet may track multiple parachains at once and those parachains may use different primitives. So the +parachain header decoding never happens at the pallet level. For maintaining the headers order, the pallet +uses relay chain header number. + +More: [pallet level documentation and code](../modules/parachains/). + +### Bridge Messages Pallet + +The pallet is responsible for queuing messages at the source chain and receiving the messages proofs at the +target chain. The messages are sent to the particular _lane_, where they are guaranteed to be received in the +same order they are sent. The pallet supports many lanes. + +The lane has two ends. Outbound lane end is storing number of messages that have been sent and the number of +messages that have been received. Inbound lane end stores the number of messages that have been received and +also a map that maps messages to relayers that have delivered those messages to the target chain. + +The pallet has three main entrypoints: +- the `send_message` may be used by the other runtime pallets to send the messages; +- the `receive_messages_proof` is responsible for parsing the messages proof and handing messages over to the +dispatch code; +- the `receive_messages_delivery_proof` is responsible for parsing the messages delivery proof and rewarding +relayers that have delivered the message. + +Many things are abstracted by the pallet: +- the message itself may mean anything, the pallet doesn't care about its content; +- the message dispatch happens during delivery, but it is decoupled from the pallet code; +- the messages proof and messages delivery proof are verified outside of the pallet; +- the relayers incentivization scheme is defined outside of the pallet. + +Outside of the messaging pallet, we have a set of adapters, where messages and delivery proofs are regular +storage proofs. The proofs are generated at the bridged chain and require bridged chain finality. So messages +pallet, in this case, depends on one of the finality pallets. The messages are XCM messages and we are using +XCM executor to dispatch them on receival. You may find more info in [Polkadot <> Kusama Bridge](./polkadot-kusama-bridge-overview.md) +document. + +More: [pallet level documentation and code](../modules/messages/). + +### Bridge Relayers Pallet + +The pallet is quite simple. It just registers relayer rewards and has an entrypoint to collect them. When +the rewards are registered and the reward amount is configured outside of the pallet. + +More: [pallet level documentation and code](../modules/relayers/). + +## Offchain Components + +Offchain bridge components are separate processes, called relayers. Relayers are connected both to the +source chain and target chain nodes. Relayers are reading state of the source chain, compare it to the +state of the target chain and, if state at target chain needs to be updated, submits target chain +transaction. + +### GRANDPA Finality Relay + +The task of relay is to submit source chain GRANDPA justifications and their corresponding headers to +the Bridge GRANDPA Finality Pallet, deployed at the target chain. For that, the relay subscribes to +the source chain GRANDPA justifications stream and submits every new justification it sees to the +target chain GRANDPA light client. In addition, relay is searching for mandatory headers and +submits their justifications - without that the pallet will be unable to move forward. + +More: [GRANDPA Finality Relay Sequence Diagram](./grandpa-finality-relay.html), [pallet level documentation and code](../relays/finality/). + +### Parachains Finality Relay + +The relay connects to the source _relay_ chain and the target chain nodes. It doesn't need to connect to the +tracked parachain nodes. The relay looks at the [`Heads`](https://github.com/paritytech/polkadot/blob/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras/mod.rs#L642) +map of the [`paras` pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras) +in source chain, and compares the value with the best parachain head, stored in the bridge parachains pallet at +the target chain. If new parachain head appears at the relay chain block `B`, the relay process **waits** +until header `B` or one of its ancestors appears at the target chain. Once it is available, the storage +proof of the map entry is generated and is submitted to the target chain. + +As its on-chain component (which requires bridge GRANDPA pallet to be deployed nearby), the parachains +finality relay requires GRANDPA finality relay to be running in parallel. Without it, the header `B` or +any of its children's finality at source won't be relayed at target, and target chain +won't be able to verify generated storage proof. + +More: [Parachains Finality Relay Sequence Diagram](./parachains-finality-relay.html), [code](../relays/parachains/). + +### Messages Relay + +Messages relay is actually two relays that are running in a single process: messages delivery relay and +delivery confirmation relay. Even though they are more complex and have many caveats, the overall algorithm +is the same as in other relays. + +Message delivery relay connects to the source chain and looks at the outbound lane end, waiting until new +messages are queued there. Once they appear at the source block `B`, the relay start waiting for the block +`B` or its descendant appear at the target chain. Then the messages storage proof is generated and submitted +to the bridge messages pallet at the target chain. In addition, the transaction may include the storage proof +of the outbound lane state - that proves that relayer rewards have been paid and this data (map of relay +accounts to the delivered messages) may be pruned from the inbound lane state at the target chain. + +Delivery confirmation relay connects to the target chain and starts watching the inbound lane end. When new +messages are delivered to the target chain, the corresponding _source chain account_ is inserted to the +map in the inbound lane data. Relay detects that, say, at the target chain block `B` and waits until that +block or its descendant appears at the source chain. Once that happens, the relay crafts a storage proof of +that data and sends it to the messages pallet, deployed at the source chain. + +As you can see, the messages relay also requires finality relay to be operating in parallel. Since messages +relay submits transactions to both source and target chains, it requires both _source-to-target_ and +_target-to-source_ finality relays. They can be GRANDPA finality relays or GRANDPA+parachains finality relays, +depending on the type of connected chain. + +More: [Messages Relay Sequence Diagram](./messages-relay.html), [pallet level documentation and code](../relays/messages/). + +### Complex Relay + +Every relay transaction has its cost. The only transaction, that is "free" to relayer is when the mandatory +GRANDPA header is submitted. The relay that feeds the bridge with every relay chain and/or parachain head it +sees, will have to pay a (quite large) cost. And if no messages are sent through the bridge, that is just +waste of money. + +We have a special relay mode, called _complex relay_, where relay mostly sleeps and only submits transactions +that are required for the messages/confirmations delivery. This mode starts two message relays (in both +directions). All required finality relays are also started in a special _on-demand_ mode. In this mode they +do not submit any headers without special request. As always, the only exception is when GRANDPA finality +relay sees the mandatory header - it is submitted without such request. + +The message relays are watching their lanes and when, at some block `B`, they see new messages/confirmations +to be delivered, they are asking on-demand relays to relay this block `B`. On-demand relays does that and +then message relay may perform its job. If on-demand relay is a parachain finality relay, it also runs its +own on-demand GRANDPA relay, which is used to relay required relay chain headers. + +More: [Complex Relay Sequence Diagram](./complex-relay.html), [code](../relays/bin-substrate/src/cli/relay_headers_and_messages/). diff --git a/docs/messages-relay.html b/docs/messages-relay.html new file mode 100644 index 00000000000..c4dab9901e0 --- /dev/null +++ b/docs/messages-relay.html @@ -0,0 +1,78 @@ + + + + + + Messages Relay + + +

Messages Relay

+

+ Both Source Chain and Target Chains have Bridge Messages pallets deployed. They also have required + finality pallets deployed - we don't care about finality type here - they can be either Bridge GRANDPA, + or Bridge Parachains finality pallets, or any combination of those. +

+

+ Finality Relayer represents two actual relayers - one relays Source Chain Finality to Target Chain. + And another one relays Target Chain Finality to Source Chain. +

+
+ sequenceDiagram + participant Source Chain + participant Finality Relayer + participant Messages Relayer + participant Target Chain + + Note right of Source Chain: Finalized: 480, Target Finalized: 50, Sent Messages: 42, Confirmed Messages: 42 + Note left of Target Chain: Finalized: 60, Source Finalized: 420, Received Messages: 42 + + Source Chain ->> Source Chain: someone Sends Message 43 + Source Chain ->> Source Chain: Import and Finalize Block 481 + + Source Chain ->> Messages Relayer: notes new outbound message 43 at Source Chain Block 481 + Note right of Messages Relayer: can't deliver message 43, Source Chain Block 481 is not relayed + + Source Chain ->> Finality Relayer: Read Finality Proof of Block 481 + Finality Relayer ->> Target Chain: Submit Finality Proof of Block 481 + Target Chain ->> Target Chain: Import and Finalize Block 61 + Note left of Target Chain: Finalized: 61, Source Finalized: 481, Received Messages: 42 + + Source Chain ->> Messages Relayer: Read Proof of Message 43 at Block 481 + Messages Relayer ->> Target Chain: Submit Proof of Message 43 at Block 481 + Target Chain ->> Target Chain: Import and Finalize Block 62 + Note left of Target Chain: Finalized: 62, Source Finalized: 481, Received Messages: { rewarded: 42, messages-relayer-account: [43] } + + Target Chain ->> Messages Relayer: notes new unrewarded relayer at Target Chain Block 62 + Note right of Messages Relayer: can't relay delivery confirmations because Target Chain Block 62 is not relayed + + Target Chain ->> Finality Relayer: Read Finality Proof of Block 62 + Finality Relayer ->> Source Chain: Submit Finality Proof of Block 62 + Source Chain ->> Source Chain: Import and Finalize Block 482 + Note right of Source Chain: Finalized: 482, Target Finalized: 62, Confirmed Messages: 42 + + Target Chain ->> Messages Relayer: Read Proof of Message 43 Delivery at Block 62 + Messages Relayer ->> Source Chain: Submit Proof of Message 43 Delivery at Block 612 + Source Chain ->> Source Chain: rewards messages-relayer-account for delivering message [43] + Source Chain ->> Source Chain: prune delivered message 43 from runtime storage + Note right of Source Chain: Finalized: 482, Target Finalized: 61, Confirmed Messages: 43 + + Source Chain ->> Source Chain: someone Sends Message 44 + Source Chain ->> Source Chain: Import and Finalize Block 483 + + Source Chain ->> Messages Relayer: notes new outbound message 44 at Source Chain Block 483 and new confirmed message 43 + Note right of Messages Relayer: can't deliver message 44, Source Chain Block 483 is not relayed + + Source Chain ->> Finality Relayer: Read Finality Proof of Block 483 + Finality Relayer ->> Target Chain: Submit Finality Proof of Block 483 + Target Chain ->> Target Chain: Import and Finalize Block 63 + Note left of Target Chain: Finalized: 63, Source Finalized: 483, Received Messages: { rewarded: 42, messages-relayer-account: [43] } + + Source Chain ->> Messages Relayer: Read Proof of Message 44 and Proof of Message 43 reward at Block 483 + Messages Relayer ->> Target Chain: Submit Proof of Message 44 and Proof of Message 43 reward at Block 483 + Target Chain ->> Target Chain: Import and Finalize Block 64 + Note left of Target Chain: Finalized: 64, Source Finalized: 483, Received Messages: { rewarded: 43, messages-relayer-account: [44] } +
+ + + + diff --git a/docs/parachains-finality-relay.html b/docs/parachains-finality-relay.html new file mode 100644 index 00000000000..4fc1392b87d --- /dev/null +++ b/docs/parachains-finality-relay.html @@ -0,0 +1,55 @@ + + + + + + Parachains Finality Relay + + +

Parachains Finality Relay

+

+ Source Relay Chain is running GRANDPA Finality Gadget. Source Parachain is a parachain of the Source + Relay Chain. Bridge GRANDPA finality pallet is deployed at Target Chain runtime and is "connected" + to the Source Relay Chain. Bridge Parachains finality pallet is deployed at Target Chain and is + configured to track the Source Parachain. GRANDPA Relayer is configured to relay Source Relay Chain + finality to Target Chain. Parachains Relayer is configured to relay Source Parachain headers finality + to Target Chain. +

+
+ sequenceDiagram + participant Source Parachain + participant Source Relay Chain + participant GRANDPA Relayer + participant Parachains Relayer + participant Target Chain + + Note left of Source Parachain: Best: 125 + Note left of Source Relay Chain: Finalized: 500, Best Parachain at Finalized: 120 + Note right of Target Chain: Best Relay: 480, Best Parachain: 110 + + Source Parachain ->> Source Parachain: Import Block 126 + Source Parachain ->> Source Relay Chain: Receives the Parachain block 126 + + Source Relay Chain ->> Source Relay Chain: Import block 501 + Source Relay Chain ->> Source Relay Chain: Finalize block 501 + Note left of Source Relay Chain: Finalized: 501, Best Parachain at Finalized: 126 + + Source Relay Chain ->> Parachains Relayer: notes new Source Parachain Block 126 + Note left of Parachains Relayer: can't relay Source Parachain Block 126, because it requires at least Source Relay Block 501 at Target Chain + + Source Relay Chain ->> Source Relay Chain: Import block 502 + Source Relay Chain ->> Source Relay Chain: Finalize block 502 + + Source Relay Chain ->> GRANDPA Relayer: read GRANDPA Finality Proof of Block 502 + GRANDPA Relayer ->> Target Chain: submit GRANDPA Finality Proof of Block 502 + Note right of Target Chain: Best Relay: 502, Best Parachain: 110 + + Target Chain ->> Parachains Relayer: notes finalized Source Relay Block 502 at Target Chain + Source Relay Chain ->> Parachains Relayer: read Parachain Finality Proof at Relay Block 502 + Parachains Relayer ->> Target Chain: submit Parachain Finality Proof at Relay Block 502 + Note right of Target Chain: Best Relay: 502, Best Parachain: 126 +
+ + + + diff --git a/docs/polkadot-kusama-bridge-overview.md b/docs/polkadot-kusama-bridge-overview.md new file mode 100644 index 00000000000..9f407b6ba00 --- /dev/null +++ b/docs/polkadot-kusama-bridge-overview.md @@ -0,0 +1,132 @@ +# Polkadot <> Kusama Bridge Overview + +This document describes how we use all components, described in the [High-Level Bridge Documentation](./high-level-overview.md), +to build the XCM bridge between Kusama and Polkadot. In this case, our components merely work as a XCM transport +(like XCMP/UMP/HRMP), between chains that are not a part of the same consensus system. + +The overall architecture may be seen in [this diagram](./polkadot-kusama-bridge.html). + +## Bridge Hubs + +All operations at relay chain are expensive. Ideally all non-mandatory transactions must happen on parachains. +That's why we are planning to have two parachains - Polkadot Bridge Hub under Polkadot consensus and Kusama +Bridge Hub under Kusama consensus. + +The Bridge Hub will have all required bridge pallets in its runtime. We hope that later, other teams will be able to +use our bridge hubs too and have their pallets there. + +The Bridge Hub will use the base token of the ecosystem - KSM at Kusama Bridge Hub and DOT at Polkadot Bridge Hub. +The runtime will have minimal set of non-bridge pallets, so there's not much you can do directly on bridge hubs. + +## Connecting Parachains + +You won't be able to directly use bridge hub transactions to send XCM messages over the bridge. Instead, you'll need +to use other parachains transactions, which will use HRMP to deliver messages to the Bridge Hub. The Bridge Hub will +just queue these messages in its outbound lane, which is dedicated to deliver messages between two parachains. + +Our first planned bridge will connect the Polkadot' Statemint and Kusama' Statemine. Bridge between those two +parachains would allow Statemint accounts to hold wrapped KSM tokens and Statemine accounts to hold wrapped DOT +tokens. + +For that bridge (pair of parachains under different consensus systems) we'll be using the lane 00000000. Later, +when other parachains will join the bridge, they will be using other lanes for their messages. + +## Running Relayers + +We are planning to run our own complex relayer for the lane 00000000. The relayer will relay Kusama/Polkadot GRANDPA +justifications to the bridge hubs at the other side. It'll also relay finalized Kusama Bridge Hub and Polkadot Bridge +Hub heads. This will only happen when messages will be queued at hubs. So most of time relayer will be idle. + +There's no any active relayer sets, or something like that. Anyone may start its own relayer and relay queued messages. +We are not against that and, as always, appreciate any community efforts. Of course, running relayer has the cost. +Apart from paying for the CPU and network, the relayer pays for transactions at both sides of the bridge. We have +a mechanism for rewarding relayers. + +### Compensating the Cost of Message Delivery Transactions + +One part of our rewarding scheme is that the cost of message delivery, for honest relayer, is zero. The honest relayer +is the relayer, which is following our rules: + +- we do not reward relayers for submitting GRANDPA finality transactions. The only exception is submitting mandatory + headers (headers which are changing the GRANDPA authorities set) - the cost of such transaction is zero. The relayer + will pay the full cost for submitting all other headers; + +- we do not reward relayers for submitting parachain finality transactions. The relayer will pay the full cost for + submitting parachain finality transactions; + +- we compensate the cost of message delivery transactions that have actually delivered the messages. So if your + transaction has claimed to deliver messages `[42, 43, 44]`, but, because of some reasons, has actually delivered + messages `[42, 43]`, the transaction will be free for relayer. If it has not delivered any messages, then + the relayer pays the full cost of the transaction; + +- we compensate the cost of message delivery and all required finality calls, if they are part of the same + [`frame_utility::batch_all`](https://github.com/paritytech/substrate/blob/891d6a5c870ab88521183facafc811a203bb6541/frame/utility/src/lib.rs#L326) + transaction. Of course, the calls inside the batch must be linked - e.g. the submitted parachain head must be used + to prove messages. Relay header must be used to prove parachain head finality. If one of calls fails, or if they + are not linked together, the relayer pays the full transaction cost. + +Please keep in mind that the fee of "zero-cost" transactions is still withdrawn from the relayer account. But the +compensation is registered in the `pallet_bridge_relayers::RelayerRewards` map at the target bridge hub. The relayer +may later claim all its rewards later, using the `pallet_bridge_relayers::claim_rewards` call. + +*A side note*: why we don't simply set the cost of useful transactions to zero? That's because the bridge has its cost. +If we won't take any fees, it would mean that the sender is not obliged to pay for its messages. And Bridge Hub +collators (and, maybe, "treasury") are not receiving any payment for including transactions. More about this later, +in the [Who is Rewarding Relayers](#who-is-rewarding-relayers) section. + +### Message Delivery Confirmation Rewards + +In addition to the "zero-cost" message delivery transactions, the relayer is also rewarded for: + +- delivering every message. The reward is registered during delivery confirmation transaction at the Source Bridge + Hub.; + +- submitting delivery confirmation transaction. The relayer may submit delivery confirmation that e.g. confirms + delivery of four messages, of which the only one (or zero) messages is actually delivered by this relayer. It + receives some fee for confirming messages, delivered by other relayers. + +Both rewards may be claimed using the `pallet_bridge_relayers::claim_rewards` call at the Source Bridge Hub. + +### Who is Rewarding Relayers + +Obviously, there should be someone who is paying relayer rewards. We want bridge transactions to have a cost, so we +can't use fees for rewards. Instead, the parachains using the bridge, use sovereign accounts on both sides +of the bridge to cover relayer rewards. + +Bridged Parachains will have sovereign accounts at bridge hubs. For example, the Statemine (Kusama Parachain) will +have an account at the Polkadot Bridge Hub. The Statemint (Polkadot Parachain) will have an account at the Kusama +Bridge Hub. The sovereign accounts are used as a source of funds when the relayer is calling the +`pallet_bridge_relayers::claim_rewards`. + +Since messages lane is only used by the pair of parachains, there's no collision between different bridges. E.g. +Statemine will only reward relayers that are delivering messages from Statemine. The Statemine sovereign account +is not used to cover rewards of bridging with some other Polkadot Parachain. + +### Multiple Relayers and Rewards + +Our goal is to incentivize running honest relayers. But we have no relayers sets, so at any time anyone may submit +message delivery transaction, hoping that the cost of this transaction will be compensated. So what if some message is +currently queued and two relayers are submitting two identical message delivery transactions at once? Without any +special means, the cost of first included transaction will be compensated and the cost of the other one won't. A honest, +but unlucky relayer will lose some money. In addition, we'll waste some portion of block size and weight, which +may be used by other useful transactions. + +To solve the problem, we have two signed extensions ([generate_bridge_reject_obsolete_headers_and_messages! {}](../bin/runtime-common/src/lib.rs) +and [RefundRelayerForMessagesFromParachain](../bin/runtime-common/src/refund_relayer_extension.rs)), that are +preventing bridge transactions with obsolete data from including into the block. We are rejecting following +transactions: + +- transactions, that are submitting the GRANDPA justification for the best finalized header, or one of its ancestors; + +- transactions, that are submitting the proof of the current best parachain head, or one of its ancestors; + +- transactions, that are delivering already delivered messages. If at least one of messages is not yet delivered, + the transaction is not rejected; + +- transactions, that are confirming delivery of already confirmed messages. If at least one of confirmations is new, + the transaction is not rejected; + +- [`frame_utility::batch_all`](https://github.com/paritytech/substrate/blob/891d6a5c870ab88521183facafc811a203bb6541/frame/utility/src/lib.rs#L326) + transactions, that have both finality and message delivery calls. All restrictions from the + [Compensating the Cost of Message Delivery Transactions](#compensating-the-cost-of-message-delivery-transactions) + are applied. diff --git a/docs/polkadot-kusama-bridge.html b/docs/polkadot-kusama-bridge.html new file mode 100644 index 00000000000..dcbae0e7b17 --- /dev/null +++ b/docs/polkadot-kusama-bridge.html @@ -0,0 +1,67 @@ + + + + + + Polkadot <> Kusama Bridge + + +

Polkadot <> Kusama Bridge

+

+ Our bridge connects two parachains - Kusama Bridge Hub and Polkadot Bridge Hub. Messages that + are sent over bridge have XCM format and we are using existing architecture to dispatch them. + Since both Polkadot, Kusama and their parachains already have means to exchange XCM messages + within the same consensus system (HRMP, VMP, ...), it means that we are able to connect all those + chains with our bridge. +

+

+ In our architecture, the lane that is used to relay messages over the bridge is determined by + the XCM source and destinations. So e.g. bridge between Statemint and Statemine (and opposite direction) + will use the lane 00000000, bridge between some other Polkadot Parachain and some other Kusama Parachain + will use the lane 00000001 and so on. +

+
+ flowchart LR + subgraph Polkadot Consensus + polkadot(((Polkadot))) + statemint(((Statemint))) + polkadot_bh(((Polkadot Bridge Hub))) + + polkadot---statemint + polkadot---polkadot_bh + + statemint-->|Send Message Using HRMP|polkadot_bh + + polkadot_bh-->|Send Message Using HRMP|statemint + statemint-->|Dispatch the Message|statemint + end + subgraph Kusama Consensus + kusama_bh(((Kusama Bridge Hub))) + statemine(((Statemine))) + kusama(((Kusama))) + + kusama---statemine + kusama---kusama_bh + + kusama_bh-->|Send Message Using HRMP|statemine + statemine-->|Dispatch the Message|statemine + + statemine-->|Send Message Using HRMP|kusama_bh + end + + polkadot_bh<===>|Message is relayed to the Bridged Chain using lane 00000000|kusama_bh + + linkStyle 2 stroke:red + linkStyle 7 stroke:red + linkStyle 8 stroke:red + + linkStyle 3 stroke:green + linkStyle 4 stroke:green + linkStyle 9 stroke:green +
+ + + \ No newline at end of file diff --git a/fuzz/storage-proof/Cargo.lock b/fuzz/storage-proof/Cargo.lock new file mode 100644 index 00000000000..6eba720c493 --- /dev/null +++ b/fuzz/storage-proof/Cargo.lock @@ -0,0 +1,2796 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.7", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" + +[[package]] +name = "arbitrary" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c38b6b6b79f671c25e1a3e785b7b82d7562ffc9cd3efdc98627e5668a2472490" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "async-trait" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1489fcb93a5bb47da0462ca93ad252ad6af2145cce58d10d46a83931ba9f016b" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388" +dependencies = [ + "digest 0.10.3", +] + +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +dependencies = [ + "arrayvec 0.4.12", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "bp-runtime" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "hash-db", + "impl-trait-for-tuples", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", + "trie-db 0.24.0", +] + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byte-slice-cast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c5fdd0166095e1d463fc6cc01aa8ce547ad77a4e84d42eb6762b084e28067e" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "winapi", +] + +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array 0.14.4", + "rand_core 0.6.1", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ccfd8c0ee4cce11e45b3fd6f9d5e69e0cc62912aa6a0cb1bf4617b0eba5a12f" +dependencies = [ + "generic-array 0.14.4", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434e1720189a637d44fe464f4df1e6eb900b4835255b14354497c78af37d9bb8" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f627126b946c25a4638eec0ea634fc52506dea98db118aae985118ce7c3d723f" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid", +] + +[[package]] +name = "derive_more" +version = "0.99.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "dyn-clonable" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +dependencies = [ + "dyn-clonable-impl", + "dyn-clone", +] + +[[package]] +name = "dyn-clonable-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dyn-clone" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" + +[[package]] +name = "ecdsa" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c66a534cbb46ab4ea03477eae19d5c22c01da8258030280b7bd9d8433fb6ef" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.0.2", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "elliptic-curve" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "ff", + "generic-array 0.14.4", + "group", + "rand_core 0.6.1", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "environmental" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b91989ae21441195d7d9b9993a2f9295c7e1a8c96255d8b729accddc124797" + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "ff" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924" +dependencies = [ + "rand_core 0.6.1", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.2", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "frame-metadata" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" +dependencies = [ + "cfg-if", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "frame-support" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "bitflags", + "frame-metadata", + "frame-support-procedural", + "impl-trait-for-tuples", + "k256", + "log", + "once_cell", + "parity-scale-codec", + "paste", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic", + "sp-core", + "sp-core-hashing-proc-macro", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-state-machine", + "sp-std", + "sp-tracing", + "tt-call", +] + +[[package]] +name = "frame-support-procedural" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "Inflector", + "frame-support-procedural-tools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-support-procedural-tools" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "frame-support-procedural-tools-derive", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-support-procedural-tools-derive" +version = "3.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "frame-system" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "frame-support", + "log", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-version", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-executor" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" + +[[package]] +name = "futures-macro" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" + +[[package]] +name = "group" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" +dependencies = [ + "ff", + "rand_core 0.6.1", + "subtle", +] + +[[package]] +name = "hash-db" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" +dependencies = [ + "ahash", +] + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array 0.14.4", + "hmac 0.8.1", +] + +[[package]] +name = "honggfuzz" +version = "0.5.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea09577d948a98a5f59b7c891e274c4fb35ad52f67782b3d0cb53b9c05301f1" +dependencies = [ + "arbitrary", + "lazy_static", + "memmap", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-serde" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b47ca4d2b6931707a55fce5cf66aff80e2178c8b63bbb4ecb5695cbc870ddf6f" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +dependencies = [ + "libc", + "windows-sys 0.42.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "windows-sys 0.42.0", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + +[[package]] +name = "js-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sec1", +] + +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "libsecp256k1" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0452aac8bab02242429380e9b2f94ea20cea2b37e2c1777a1358799bbe97f37" +dependencies = [ + "arrayref", + "base64", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.2", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "memory-db" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6566c70c1016f525ced45d7b7f97730a2bafb037c788211d0c186ef5b2189f0a" +dependencies = [ + "hash-db", + "hashbrown", + "parity-util-mem", +] + +[[package]] +name = "memory_units" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" + +[[package]] +name = "merlin" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "miniz_oxide" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +dependencies = [ + "adler", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-format" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465" +dependencies = [ + "arrayvec 0.4.12", + "itoa 0.4.7", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi 0.1.18", + "libc", +] + +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parity-scale-codec" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ab01d0f889e957861bc65888d5ccbe82c158d0270136ba46820d43837cdf72" +dependencies = [ + "arrayvec 0.7.2", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "parity-util-mem" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c32561d248d352148124f036cac253a644685a21dc9fea383eb4907d7bd35a8f" +dependencies = [ + "cfg-if", + "hashbrown", + "impl-trait-for-tuples", + "parity-util-mem-derive", + "parking_lot", + "primitive-types", + "winapi", +] + +[[package]] +name = "parity-util-mem-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" +dependencies = [ + "proc-macro2", + "syn", + "synstructure", +] + +[[package]] +name = "parity-wasm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.36.1", +] + +[[package]] +name = "paste" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" + +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac 0.8.0", +] + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac 0.11.1", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "primitive-types" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-serde", + "scale-info", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18519b42a40024d661e1714153e9ad0c3de27cd495760ceb09710920f1098b1e" +dependencies = [ + "libc", + "rand_chacha 0.3.0", + "rand_core 0.6.1", + "rand_hc 0.3.1", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.1", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" +dependencies = [ + "getrandom 0.2.7", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core 0.6.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300f2a835d808734ee295d45007adacb9ebb29dd3ae2424acfa17930cae541da" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c38e3aecd2b21cb3959637b883bb3714bc7e43f0268b9a29d3743ee3e55cdd2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" +dependencies = [ + "byteorder", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "rfc6979" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +dependencies = [ + "crypto-bigint", + "hmac 0.11.0", + "zeroize", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustix" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.42.0", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "scale-info" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c46be926081c9f4dd5dd9b6f1d3e3229f2360bc6502dd8836f84a93b7c75e99a" +dependencies = [ + "bitvec", + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "serde", +] + +[[package]] +name = "scale-info-derive" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e334bb10a245e28e5fd755cabcafd96cfcd167c99ae63a46924ca8d8703a3c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "schnorrkel" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "curve25519-dalek 2.1.2", + "getrandom 0.1.16", + "merlin", + "rand 0.7.3", + "rand_core 0.5.1", + "sha2 0.8.2", + "subtle", + "zeroize", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sec1" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +dependencies = [ + "der", + "generic-array 0.14.4", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c42e6f1735c5f00f51e43e28d6634141f2bcad10931b2609ddd74a86d751260" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" +dependencies = [ + "cc", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "serde" +version = "1.0.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +dependencies = [ + "itoa 1.0.2", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.3", +] + +[[package]] +name = "sha3" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881bf8156c87b6301fc5ca6b27f11eeb2761224c7081e69b409d5a1951a70c86" +dependencies = [ + "digest 0.10.3", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79c719719ee05df97490f80a45acfc99e5a30ce98a1e4fb67aee422745ae14e3" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signature" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" +dependencies = [ + "digest 0.9.0", + "rand_core 0.6.1", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "sp-application-crypto" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-std", +] + +[[package]] +name = "sp-arithmetic" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-debug-derive", + "sp-std", + "static_assertions", +] + +[[package]] +name = "sp-core" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "base58", + "bitflags", + "blake2-rfc", + "byteorder", + "dyn-clonable", + "ed25519-dalek", + "futures", + "hash-db", + "hash256-std-hasher", + "hex", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log", + "merlin", + "num-traits", + "parity-scale-codec", + "parity-util-mem", + "parking_lot", + "primitive-types", + "rand 0.7.3", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "wasmi", + "zeroize", +] + +[[package]] +name = "sp-core-hashing" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "blake2", + "byteorder", + "digest 0.10.3", + "sha2 0.10.2", + "sha3", + "sp-std", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "proc-macro2", + "quote", + "sp-core-hashing", + "syn", +] + +[[package]] +name = "sp-debug-derive" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-externalities" +version = "0.12.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std", + "sp-storage", +] + +[[package]] +name = "sp-inherents" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "async-trait", + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std", + "thiserror", +] + +[[package]] +name = "sp-io" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "futures", + "hash-db", + "libsecp256k1", + "log", + "parity-scale-codec", + "parking_lot", + "secp256k1", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-trie", + "sp-wasm-interface", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-keystore" +version = "0.12.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "async-trait", + "futures", + "merlin", + "parity-scale-codec", + "parking_lot", + "schnorrkel", + "sp-core", + "sp-externalities", + "thiserror", +] + +[[package]] +name = "sp-panic-handler" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + +[[package]] +name = "sp-runtime" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "parity-util-mem", + "paste", + "rand 0.7.3", + "scale-info", + "serde", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-std", +] + +[[package]] +name = "sp-runtime-interface" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std", + "sp-storage", + "sp-tracing", + "sp-wasm-interface", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-staking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-state-machine" +version = "0.12.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "hash-db", + "log", + "num-traits", + "parity-scale-codec", + "parking_lot", + "rand 0.7.3", + "smallvec", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-std", + "sp-trie", + "thiserror", + "tracing", + "trie-root", +] + +[[package]] +name = "sp-std" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" + +[[package]] +name = "sp-storage" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive", + "sp-std", +] + +[[package]] +name = "sp-tracing" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "parity-scale-codec", + "sp-std", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sp-trie" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "hash-db", + "memory-db", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-std", + "thiserror", + "trie-db 0.23.1", + "trie-root", +] + +[[package]] +name = "sp-version" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "parity-wasm", + "scale-info", + "serde", + "sp-core-hashing-proc-macro", + "sp-runtime", + "sp-std", + "sp-version-proc-macro", + "thiserror", +] + +[[package]] +name = "sp-version-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "parity-scale-codec", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sp-wasm-interface" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?branch=master#367dab0d4bd7fd7b6c222dd15c753169c057dd42" +dependencies = [ + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std", + "wasmi", +] + +[[package]] +name = "ss58-registry" +version = "1.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ef98aedad3dc52e10995e7ed15f1279e11d4da35795f5dac7305742d0feb66" +dependencies = [ + "Inflector", + "num-format", + "proc-macro2", + "quote", + "serde", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "storage-proof-fuzzer" +version = "0.1.0" +dependencies = [ + "bp-runtime", + "env_logger", + "honggfuzz", + "log", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-trie", +] + +[[package]] +name = "substrate-bip39" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" +dependencies = [ + "hmac 0.11.0", + "pbkdf2 0.8.0", + "schnorrkel", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "subtle" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tiny-bip39" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +dependencies = [ + "anyhow", + "hmac 0.8.1", + "once_cell", + "pbkdf2 0.4.0", + "rand 0.7.3", + "rustc-hash", + "sha2 0.9.9", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tinyvec" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "trie-db" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32d034c0d3db64b43c31de38e945f15b40cd4ca6d2dcfc26d4798ce8de4ab83" +dependencies = [ + "hash-db", + "hashbrown", + "log", + "rustc-hex", + "smallvec", +] + +[[package]] +name = "trie-db" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "004e1e8f92535694b4cb1444dc5a8073ecf0815e3357f729638b9f8fc4062908" +dependencies = [ + "hash-db", + "hashbrown", + "log", + "rustc-hex", + "smallvec", +] + +[[package]] +name = "trie-root" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891" +dependencies = [ + "hash-db", +] + +[[package]] +name = "tt-call" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e66dcbec4290c69dd03c57e76c2469ea5c7ce109c6dd4351c13055cf71ea055" + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "digest 0.10.3", + "rand 0.8.2", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "uint" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11fe9a9348741cf134085ad57c249508345fe16411b3d7fb4ff2da2f1d6382e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-ident" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" + +[[package]] +name = "unicode-normalization" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" + +[[package]] +name = "wasmi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca00c5147c319a8ec91ec1a0edbec31e566ce2c9cc93b3f9bb86a9efd0eb795d" +dependencies = [ + "downcast-rs", + "libc", + "memory_units", + "num-rational", + "num-traits", + "parity-wasm", + "wasmi-validation", +] + +[[package]] +name = "wasmi-validation" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "165343ecd6c018fc09ebcae280752702c9a2ef3e6f8d02f1cfcbdb53ef6d7937" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "wyz" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" +dependencies = [ + "tap", +] + +[[package]] +name = "zeroize" +version = "1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20b578acffd8516a6c3f2a1bdefc1ec37e547bb4e0fb8b6b01a4cafc886b4442" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/fuzz/storage-proof/Cargo.toml b/fuzz/storage-proof/Cargo.toml new file mode 100644 index 00000000000..61d0e63a878 --- /dev/null +++ b/fuzz/storage-proof/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "storage-proof-fuzzer" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +honggfuzz = "0.5.54" +log = "0.4.0" +env_logger = "0.10.0" + +# Bridge Dependencies + +bp-runtime = { path = "../../primitives/runtime" } + +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/fuzz/storage-proof/README.md b/fuzz/storage-proof/README.md new file mode 100644 index 00000000000..1eeec7562a9 --- /dev/null +++ b/fuzz/storage-proof/README.md @@ -0,0 +1,34 @@ +# Storage Proof Fuzzer + +## How to run? + +Install dependencies: +``` +$ sudo apt install build-essential binutils-dev libunwind-dev +``` +or on nix: +``` +$ nix-shell -p honggfuzz +``` + +Install `cargo hfuzz` plugin: +``` +$ cargo install honggfuzz +``` + +Run: +``` +$ cargo hfuzz run storage-proof-fuzzer +``` + +Use `HFUZZ_RUN_ARGS` to customize execution: +``` +# 1 second of timeout +# use 12 fuzzing thread +# be verbose +# stop after 1000000 fuzzing iteration +# exit upon crash +HFUZZ_RUN_ARGS="-t 1 -n 12 -v -N 1000000 --exit_upon_crash" cargo hfuzz run example +``` + +More details in the [official documentation](https://docs.rs/honggfuzz/0.5.52/honggfuzz/#about-honggfuzz). diff --git a/fuzz/storage-proof/src/main.rs b/fuzz/storage-proof/src/main.rs new file mode 100644 index 00000000000..9ceda58d163 --- /dev/null +++ b/fuzz/storage-proof/src/main.rs @@ -0,0 +1,78 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Storage Proof Checker fuzzer. + +#![warn(missing_docs)] + +use honggfuzz::fuzz; +// Logic for checking Substrate storage proofs. + +use sp_core::{Blake2Hasher, H256}; +use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend}; +use sp_std::vec::Vec; +use std::collections::HashMap; + +fn craft_known_storage_proof( + input_vec: Vec<(Vec, Vec)>, +) -> (H256, bp_runtime::RawStorageProof) { + let storage_proof_vec = + vec![(None, input_vec.iter().map(|x| (x.0.clone(), Some(x.1.clone()))).collect())]; + log::info!("Storage proof vec {:?}", storage_proof_vec); + let state_version = sp_runtime::StateVersion::default(); + let backend = >::from((storage_proof_vec, state_version)); + let root = backend.storage_root(std::iter::empty(), state_version).0; + let vector_element_proof = + prove_read(backend, input_vec.iter().map(|x| x.0.as_slice())).unwrap(); + (root, vector_element_proof.iter_nodes().cloned().collect()) +} + +fn transform_into_unique(input_vec: Vec<(Vec, Vec)>) -> Vec<(Vec, Vec)> { + let mut output_hashmap = HashMap::new(); + let mut output_vec = Vec::new(); + for key_value_pair in input_vec { + output_hashmap.insert(key_value_pair.0, key_value_pair.1); //Only 1 value per key + } + for (key, val) in output_hashmap.iter() { + output_vec.push((key.clone(), val.clone())); + } + output_vec +} + +fn run_fuzzer() { + fuzz!(|input_vec: Vec<(Vec, Vec)>| { + if input_vec.is_empty() { + return + } + let unique_input_vec = transform_into_unique(input_vec); + let (root, craft_known_storage_proof) = craft_known_storage_proof(unique_input_vec.clone()); + let mut checker = + >::new(root, craft_known_storage_proof) + .expect("Valid proof passed; qed"); + for key_value_pair in unique_input_vec { + log::info!("Reading value for pair {:?}", key_value_pair); + assert_eq!(checker.read_value(&key_value_pair.0), Ok(Some(key_value_pair.1.clone()))); + } + }) +} + +fn main() { + env_logger::init(); + + loop { + run_fuzzer(); + } +} diff --git a/modules/beefy/Cargo.toml b/modules/beefy/Cargo.toml new file mode 100644 index 00000000000..8aff2c169b4 --- /dev/null +++ b/modules/beefy/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "pallet-bridge-beefy" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +log = { version = "0.4.14", default-features = false } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true } + +# Bridge Dependencies + +bp-beefy = { path = "../../primitives/beefy", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +sp-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "master" } +mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.3.2" } +pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master" } +rand = "0.8" +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } +bp-test-utils = { path = "../../primitives/test-utils" } + +[features] +default = ["std"] +std = [ + "bp-beefy/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "serde", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/modules/beefy/src/lib.rs b/modules/beefy/src/lib.rs new file mode 100644 index 00000000000..79795dd0770 --- /dev/null +++ b/modules/beefy/src/lib.rs @@ -0,0 +1,648 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! BEEFY bridge pallet. +//! +//! This pallet is an on-chain BEEFY light client for Substrate-based chains that are using the +//! following pallets bundle: `pallet-mmr`, `pallet-beefy` and `pallet-beefy-mmr`. +//! +//! The pallet is able to verify MMR leaf proofs and BEEFY commitments, so it has access +//! to the following data of the bridged chain: +//! +//! - header hashes +//! - changes of BEEFY authorities +//! - extra data of MMR leafs +//! +//! Given the header hash, other pallets are able to verify header-based proofs +//! (e.g. storage proofs, transaction inclusion proofs, etc.). + +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_beefy::{ChainWithBeefy, InitializationData}; +use sp_std::{boxed::Box, prelude::*}; + +// Re-export in crate namespace for `construct_runtime!` +pub use pallet::*; + +mod utils; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod mock_chain; + +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-beefy"; + +/// Configured bridged chain. +pub type BridgedChain = >::BridgedChain; +/// Block number, used by configured bridged chain. +pub type BridgedBlockNumber = bp_runtime::BlockNumberOf>; +/// Block hash, used by configured bridged chain. +pub type BridgedBlockHash = bp_runtime::HashOf>; + +/// Pallet initialization data. +pub type InitializationDataOf = + InitializationData, bp_beefy::MmrHashOf>>; +/// BEEFY commitment hasher, used by configured bridged chain. +pub type BridgedBeefyCommitmentHasher = bp_beefy::BeefyCommitmentHasher>; +/// BEEFY validator id, used by configured bridged chain. +pub type BridgedBeefyAuthorityId = bp_beefy::BeefyAuthorityIdOf>; +/// BEEFY validator set, used by configured bridged chain. +pub type BridgedBeefyAuthoritySet = bp_beefy::BeefyAuthoritySetOf>; +/// BEEFY authority set, used by configured bridged chain. +pub type BridgedBeefyAuthoritySetInfo = bp_beefy::BeefyAuthoritySetInfoOf>; +/// BEEFY signed commitment, used by configured bridged chain. +pub type BridgedBeefySignedCommitment = bp_beefy::BeefySignedCommitmentOf>; +/// MMR hashing algorithm, used by configured bridged chain. +pub type BridgedMmrHashing = bp_beefy::MmrHashingOf>; +/// MMR hashing output type of `BridgedMmrHashing`. +pub type BridgedMmrHash = bp_beefy::MmrHashOf>; +/// The type of the MMR leaf extra data used by the configured bridged chain. +pub type BridgedBeefyMmrLeafExtra = bp_beefy::BeefyMmrLeafExtraOf>; +/// BEEFY MMR proof type used by the pallet +pub type BridgedMmrProof = bp_beefy::MmrProofOf>; +/// MMR leaf type, used by configured bridged chain. +pub type BridgedBeefyMmrLeaf = bp_beefy::BeefyMmrLeafOf>; +/// Imported commitment data, stored by the pallet. +pub type ImportedCommitment = bp_beefy::ImportedCommitment< + BridgedBlockNumber, + BridgedBlockHash, + BridgedMmrHash, +>; + +/// Some high level info about the imported commitments. +#[derive(codec::Encode, codec::Decode, scale_info::TypeInfo)] +pub struct ImportedCommitmentsInfoData { + /// Best known block number, provided in a BEEFY commitment. However this is not + /// the best proven block. The best proven block is this block's parent. + best_block_number: BlockNumber, + /// The head of the `ImportedBlockNumbers` ring buffer. + next_block_number_index: u32, +} + +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use super::*; + use bp_runtime::{BasicOperatingMode, OwnedBridgeModule}; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The upper bound on the number of requests allowed by the pallet. + /// + /// A request refers to an action which writes a header to storage. + /// + /// Once this bound is reached the pallet will reject all commitments + /// until the request count has decreased. + #[pallet::constant] + type MaxRequests: Get; + + /// Maximal number of imported commitments to keep in the storage. + /// + /// The setting is there to prevent growing the on-chain state indefinitely. Note + /// the setting does not relate to block numbers - we will simply keep as much items + /// in the storage, so it doesn't guarantee any fixed timeframe for imported commitments. + #[pallet::constant] + type CommitmentsToKeep: Get; + + /// The chain we are bridging to here. + type BridgedChain: ChainWithBeefy; + } + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet { + fn on_initialize(_n: T::BlockNumber) -> frame_support::weights::Weight { + >::mutate(|count| *count = count.saturating_sub(1)); + + Weight::from_parts(0, 0) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + } + + impl, I: 'static> OwnedBridgeModule for Pallet { + const LOG_TARGET: &'static str = LOG_TARGET; + type OwnerStorage = PalletOwner; + type OperatingMode = BasicOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + } + + #[pallet::call] + impl, I: 'static> Pallet + where + BridgedMmrHashing: 'static + Send + Sync, + { + /// Initialize pallet with BEEFY authority set and best known finalized block number. + #[pallet::call_index(0)] + #[pallet::weight((T::DbWeight::get().reads_writes(2, 3), DispatchClass::Operational))] + pub fn initialize( + origin: OriginFor, + init_data: InitializationDataOf, + ) -> DispatchResult { + Self::ensure_owner_or_root(origin)?; + + let is_initialized = >::exists(); + ensure!(!is_initialized, >::AlreadyInitialized); + + log::info!(target: LOG_TARGET, "Initializing bridge BEEFY pallet: {:?}", init_data); + Ok(initialize::(init_data)?) + } + + /// Change `PalletOwner`. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(1)] + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { + >::set_owner(origin, new_owner) + } + + /// Halt or resume all pallet operations. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(2)] + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_operating_mode( + origin: OriginFor, + operating_mode: BasicOperatingMode, + ) -> DispatchResult { + >::set_operating_mode(origin, operating_mode) + } + + /// Submit a commitment generated by BEEFY authority set. + /// + /// It will use the underlying storage pallet to fetch information about the current + /// authority set and best finalized block number in order to verify that the commitment + /// is valid. + /// + /// If successful in verification, it will update the underlying storage with the data + /// provided in the newly submitted commitment. + #[pallet::call_index(3)] + #[pallet::weight(0)] + pub fn submit_commitment( + origin: OriginFor, + commitment: BridgedBeefySignedCommitment, + validator_set: BridgedBeefyAuthoritySet, + mmr_leaf: Box>, + mmr_proof: BridgedMmrProof, + ) -> DispatchResult + where + BridgedBeefySignedCommitment: Clone, + { + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + ensure_signed(origin)?; + + ensure!(Self::request_count() < T::MaxRequests::get(), >::TooManyRequests); + + // Ensure that the commitment is for a better block. + let commitments_info = + ImportedCommitmentsInfo::::get().ok_or(Error::::NotInitialized)?; + ensure!( + commitment.commitment.block_number > commitments_info.best_block_number, + Error::::OldCommitment + ); + + // Verify commitment and mmr leaf. + let current_authority_set_info = CurrentAuthoritySetInfo::::get(); + let mmr_root = utils::verify_commitment::( + &commitment, + ¤t_authority_set_info, + &validator_set, + )?; + utils::verify_beefy_mmr_leaf::(&mmr_leaf, mmr_proof, mmr_root)?; + + // Update request count. + RequestCount::::mutate(|count| *count += 1); + // Update authority set if needed. + if mmr_leaf.beefy_next_authority_set.id > current_authority_set_info.id { + CurrentAuthoritySetInfo::::put(mmr_leaf.beefy_next_authority_set); + } + + // Import commitment. + let block_number_index = commitments_info.next_block_number_index; + let to_prune = ImportedBlockNumbers::::try_get(block_number_index); + ImportedCommitments::::insert( + commitment.commitment.block_number, + ImportedCommitment:: { + parent_number_and_hash: mmr_leaf.parent_number_and_hash, + mmr_root, + }, + ); + ImportedBlockNumbers::::insert( + block_number_index, + commitment.commitment.block_number, + ); + ImportedCommitmentsInfo::::put(ImportedCommitmentsInfoData { + best_block_number: commitment.commitment.block_number, + next_block_number_index: (block_number_index + 1) % T::CommitmentsToKeep::get(), + }); + if let Ok(old_block_number) = to_prune { + log::debug!( + target: LOG_TARGET, + "Pruning commitment for old block: {:?}.", + old_block_number + ); + ImportedCommitments::::remove(old_block_number); + } + + log::info!( + target: LOG_TARGET, + "Successfully imported commitment for block {:?}", + commitment.commitment.block_number, + ); + + Ok(()) + } + } + + /// The current number of requests which have written to storage. + /// + /// If the `RequestCount` hits `MaxRequests`, no more calls will be allowed to the pallet until + /// the request capacity is increased. + /// + /// The `RequestCount` is decreased by one at the beginning of every block. This is to ensure + /// that the pallet can always make progress. + #[pallet::storage] + #[pallet::getter(fn request_count)] + pub type RequestCount, I: 'static = ()> = StorageValue<_, u32, ValueQuery>; + + /// High level info about the imported commitments. + /// + /// Contains the following info: + /// - best known block number of the bridged chain, finalized by BEEFY + /// - the head of the `ImportedBlockNumbers` ring buffer + #[pallet::storage] + pub type ImportedCommitmentsInfo, I: 'static = ()> = + StorageValue<_, ImportedCommitmentsInfoData>>; + + /// A ring buffer containing the block numbers of the commitments that we have imported, + /// ordered by the insertion time. + #[pallet::storage] + pub(super) type ImportedBlockNumbers, I: 'static = ()> = + StorageMap<_, Identity, u32, BridgedBlockNumber>; + + /// All the commitments that we have imported and haven't been pruned yet. + #[pallet::storage] + pub type ImportedCommitments, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, BridgedBlockNumber, ImportedCommitment>; + + /// The current BEEFY authority set at the bridged chain. + #[pallet::storage] + pub type CurrentAuthoritySetInfo, I: 'static = ()> = + StorageValue<_, BridgedBeefyAuthoritySetInfo, ValueQuery>; + + /// Optional pallet owner. + /// + /// Pallet owner has the right to halt all pallet operations and then resume it. If it is + /// `None`, then there are no direct ways to halt/resume pallet operations, but other + /// runtime methods may still be used to do that (i.e. `democracy::referendum` to update halt + /// flag directly or calling `halt_operations`). + #[pallet::storage] + pub type PalletOwner, I: 'static = ()> = + StorageValue<_, T::AccountId, OptionQuery>; + + /// The current operating mode of the pallet. + /// + /// Depending on the mode either all, or no transactions will be allowed. + #[pallet::storage] + pub type PalletOperatingMode, I: 'static = ()> = + StorageValue<_, BasicOperatingMode, ValueQuery>; + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + /// Optional module owner account. + pub owner: Option, + /// Optional module initialization data. + pub init_data: Option>, + } + + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { owner: None, init_data: None } + } + } + + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + if let Some(ref owner) = self.owner { + >::put(owner); + } + + if let Some(init_data) = self.init_data.clone() { + initialize::(init_data) + .expect("invalid initialization data of BEEFY bridge pallet"); + } else { + // Since the bridge hasn't been initialized we shouldn't allow anyone to perform + // transactions. + >::put(BasicOperatingMode::Halted); + } + } + } + + #[pallet::error] + pub enum Error { + /// The pallet has not been initialized yet. + NotInitialized, + /// The pallet has already been initialized. + AlreadyInitialized, + /// Invalid initial authority set. + InvalidInitialAuthoritySet, + /// There are too many requests for the current window to handle. + TooManyRequests, + /// The imported commitment is older than the best commitment known to the pallet. + OldCommitment, + /// The commitment is signed by unknown validator set. + InvalidCommitmentValidatorSetId, + /// The id of the provided validator set is invalid. + InvalidValidatorSetId, + /// The number of signatures in the commitment is invalid. + InvalidCommitmentSignaturesLen, + /// The number of validator ids provided is invalid. + InvalidValidatorSetLen, + /// There aren't enough correct signatures in the commitment to finalize the block. + NotEnoughCorrectSignatures, + /// MMR root is missing from the commitment. + MmrRootMissingFromCommitment, + /// MMR proof verification has failed. + MmrProofVerificationFailed, + /// The validators are not matching the merkle tree root of the authority set. + InvalidValidatorSetRoot, + /// Error generated by the `OwnedBridgeModule` trait. + BridgeModule(bp_runtime::OwnedBridgeModuleError), + } + + /// Initialize pallet with given parameters. + pub(super) fn initialize, I: 'static>( + init_data: InitializationDataOf, + ) -> Result<(), Error> { + if init_data.authority_set.len == 0 { + return Err(Error::::InvalidInitialAuthoritySet) + } + CurrentAuthoritySetInfo::::put(init_data.authority_set); + + >::put(init_data.operating_mode); + ImportedCommitmentsInfo::::put(ImportedCommitmentsInfoData { + best_block_number: init_data.best_block_number, + next_block_number_index: 0, + }); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_runtime::{BasicOperatingMode, OwnedBridgeModuleError}; + use bp_test_utils::generate_owned_bridge_module_tests; + use frame_support::{assert_noop, assert_ok, traits::Get}; + use mock::*; + use mock_chain::*; + use sp_consensus_beefy::mmr::BeefyAuthoritySet; + use sp_runtime::DispatchError; + + fn next_block() { + use frame_support::traits::OnInitialize; + + let current_number = frame_system::Pallet::::block_number(); + frame_system::Pallet::::set_block_number(current_number + 1); + let _ = Pallet::::on_initialize(current_number); + } + + fn import_header_chain(headers: Vec) { + for header in headers { + if header.commitment.is_some() { + assert_ok!(import_commitment(header)); + } + } + } + + #[test] + fn fails_to_initialize_if_already_initialized() { + run_test_with_initialize(32, || { + assert_noop!( + Pallet::::initialize( + RuntimeOrigin::root(), + InitializationData { + operating_mode: BasicOperatingMode::Normal, + best_block_number: 0, + authority_set: BeefyAuthoritySet { id: 0, len: 1, root: [0u8; 32].into() } + } + ), + Error::::AlreadyInitialized, + ); + }); + } + + #[test] + fn fails_to_initialize_if_authority_set_is_empty() { + run_test(|| { + assert_noop!( + Pallet::::initialize( + RuntimeOrigin::root(), + InitializationData { + operating_mode: BasicOperatingMode::Normal, + best_block_number: 0, + authority_set: BeefyAuthoritySet { id: 0, len: 0, root: [0u8; 32].into() } + } + ), + Error::::InvalidInitialAuthoritySet, + ); + }); + } + + #[test] + fn fails_to_import_commitment_if_halted() { + run_test_with_initialize(1, || { + assert_ok!(Pallet::::set_operating_mode( + RuntimeOrigin::root(), + BasicOperatingMode::Halted + )); + assert_noop!( + import_commitment(ChainBuilder::new(1).append_finalized_header().to_header()), + Error::::BridgeModule(OwnedBridgeModuleError::Halted), + ); + }) + } + + #[test] + fn fails_to_import_commitment_if_too_many_requests() { + run_test_with_initialize(1, || { + let max_requests = <::MaxRequests as Get>::get() as u64; + let mut chain = ChainBuilder::new(1); + for _ in 0..max_requests + 2 { + chain = chain.append_finalized_header(); + } + + // import `max_request` headers + for i in 0..max_requests { + assert_ok!(import_commitment(chain.header(i + 1))); + } + + // try to import next header: it fails because we are no longer accepting commitments + assert_noop!( + import_commitment(chain.header(max_requests + 1)), + Error::::TooManyRequests, + ); + + // when next block is "started", we allow import of next header + next_block(); + assert_ok!(import_commitment(chain.header(max_requests + 1))); + + // but we can't import two headers until next block and so on + assert_noop!( + import_commitment(chain.header(max_requests + 2)), + Error::::TooManyRequests, + ); + }) + } + + #[test] + fn fails_to_import_commitment_if_not_initialized() { + run_test(|| { + assert_noop!( + import_commitment(ChainBuilder::new(1).append_finalized_header().to_header()), + Error::::NotInitialized, + ); + }) + } + + #[test] + fn submit_commitment_works_with_long_chain_with_handoffs() { + run_test_with_initialize(3, || { + let chain = ChainBuilder::new(3) + .append_finalized_header() + .append_default_headers(16) // 2..17 + .append_finalized_header() // 18 + .append_default_headers(16) // 19..34 + .append_handoff_header(9) // 35 + .append_default_headers(8) // 36..43 + .append_finalized_header() // 44 + .append_default_headers(8) // 45..52 + .append_handoff_header(17) // 53 + .append_default_headers(4) // 54..57 + .append_finalized_header() // 58 + .append_default_headers(4); // 59..63 + import_header_chain(chain.to_chain()); + + assert_eq!( + ImportedCommitmentsInfo::::get().unwrap().best_block_number, + 58 + ); + assert_eq!(CurrentAuthoritySetInfo::::get().id, 2); + assert_eq!(CurrentAuthoritySetInfo::::get().len, 17); + + let imported_commitment = ImportedCommitments::::get(58).unwrap(); + assert_eq!( + imported_commitment, + bp_beefy::ImportedCommitment { + parent_number_and_hash: (57, chain.header(57).header.hash()), + mmr_root: chain.header(58).mmr_root, + }, + ); + }) + } + + #[test] + fn commitment_pruning_works() { + run_test_with_initialize(3, || { + let commitments_to_keep = >::CommitmentsToKeep::get(); + let commitments_to_import: Vec = ChainBuilder::new(3) + .append_finalized_headers(commitments_to_keep as usize + 2) + .to_chain(); + + // import exactly `CommitmentsToKeep` commitments + for index in 0..commitments_to_keep { + next_block(); + import_commitment(commitments_to_import[index as usize].clone()) + .expect("must succeed"); + assert_eq!( + ImportedCommitmentsInfo::::get().unwrap().next_block_number_index, + (index + 1) % commitments_to_keep + ); + } + + // ensure that all commitments are in the storage + assert_eq!( + ImportedCommitmentsInfo::::get().unwrap().best_block_number, + commitments_to_keep as TestBridgedBlockNumber + ); + assert_eq!( + ImportedCommitmentsInfo::::get().unwrap().next_block_number_index, + 0 + ); + for index in 0..commitments_to_keep { + assert!(ImportedCommitments::::get( + index as TestBridgedBlockNumber + 1 + ) + .is_some()); + assert_eq!( + ImportedBlockNumbers::::get(index), + Some(index + 1).map(Into::into) + ); + } + + // import next commitment + next_block(); + import_commitment(commitments_to_import[commitments_to_keep as usize].clone()) + .expect("must succeed"); + assert_eq!( + ImportedCommitmentsInfo::::get().unwrap().next_block_number_index, + 1 + ); + assert!(ImportedCommitments::::get( + commitments_to_keep as TestBridgedBlockNumber + 1 + ) + .is_some()); + assert_eq!( + ImportedBlockNumbers::::get(0), + Some(commitments_to_keep + 1).map(Into::into) + ); + // the side effect of the import is that the commitment#1 is pruned + assert!(ImportedCommitments::::get(1).is_none()); + + // import next commitment + next_block(); + import_commitment(commitments_to_import[commitments_to_keep as usize + 1].clone()) + .expect("must succeed"); + assert_eq!( + ImportedCommitmentsInfo::::get().unwrap().next_block_number_index, + 2 + ); + assert!(ImportedCommitments::::get( + commitments_to_keep as TestBridgedBlockNumber + 2 + ) + .is_some()); + assert_eq!( + ImportedBlockNumbers::::get(1), + Some(commitments_to_keep + 2).map(Into::into) + ); + // the side effect of the import is that the commitment#2 is pruned + assert!(ImportedCommitments::::get(1).is_none()); + assert!(ImportedCommitments::::get(2).is_none()); + }); + } + + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); +} diff --git a/modules/beefy/src/mock.rs b/modules/beefy/src/mock.rs new file mode 100644 index 00000000000..1ffccc5dfbc --- /dev/null +++ b/modules/beefy/src/mock.rs @@ -0,0 +1,224 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate as beefy; +use crate::{ + utils::get_authorities_mmr_root, BridgedBeefyAuthoritySet, BridgedBeefyAuthoritySetInfo, + BridgedBeefyCommitmentHasher, BridgedBeefyMmrLeafExtra, BridgedBeefySignedCommitment, + BridgedMmrHash, BridgedMmrHashing, BridgedMmrProof, +}; + +use bp_beefy::{BeefyValidatorSignatureOf, ChainWithBeefy, Commitment, MmrDataOrHash}; +use bp_runtime::{BasicOperatingMode, Chain}; +use codec::Encode; +use frame_support::{construct_runtime, parameter_types, traits::ConstU64, weights::Weight}; +use sp_core::{sr25519::Signature, Pair}; +use sp_runtime::{ + testing::{Header, H256}, + traits::{BlakeTwo256, Hash, IdentityLookup}, + Perbill, +}; + +pub use sp_consensus_beefy::crypto::{AuthorityId as BeefyId, Pair as BeefyPair}; +use sp_core::crypto::Wraps; +use sp_runtime::traits::Keccak256; + +pub type TestAccountId = u64; +pub type TestBridgedBlockNumber = u64; +pub type TestBridgedBlockHash = H256; +pub type TestBridgedHeader = Header; +pub type TestBridgedAuthoritySetInfo = BridgedBeefyAuthoritySetInfo; +pub type TestBridgedValidatorSet = BridgedBeefyAuthoritySet; +pub type TestBridgedCommitment = BridgedBeefySignedCommitment; +pub type TestBridgedValidatorSignature = BeefyValidatorSignatureOf; +pub type TestBridgedCommitmentHasher = BridgedBeefyCommitmentHasher; +pub type TestBridgedMmrHashing = BridgedMmrHashing; +pub type TestBridgedMmrHash = BridgedMmrHash; +pub type TestBridgedBeefyMmrLeafExtra = BridgedBeefyMmrLeafExtra; +pub type TestBridgedMmrProof = BridgedMmrProof; +pub type TestBridgedRawMmrLeaf = sp_consensus_beefy::mmr::MmrLeaf< + TestBridgedBlockNumber, + TestBridgedBlockHash, + TestBridgedMmrHash, + TestBridgedBeefyMmrLeafExtra, +>; +pub type TestBridgedMmrNode = MmrDataOrHash; + +type TestBlock = frame_system::mocking::MockBlock; +type TestUncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + +construct_runtime! { + pub enum TestRuntime where + Block = TestBlock, + NodeBlock = TestBlock, + UncheckedExtrinsic = TestUncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Beefy: beefy::{Pallet}, + } +} + +parameter_types! { + pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 0); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} + +impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = TestAccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = (); + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type DbWeight = (); + type BlockWeights = (); + type BlockLength = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl beefy::Config for TestRuntime { + type MaxRequests = frame_support::traits::ConstU32<16>; + type BridgedChain = TestBridgedChain; + type CommitmentsToKeep = frame_support::traits::ConstU32<16>; +} + +#[derive(Debug)] +pub struct TestBridgedChain; + +impl Chain for TestBridgedChain { + type BlockNumber = TestBridgedBlockNumber; + type Hash = H256; + type Hasher = BlakeTwo256; + type Header = ::Header; + + type AccountId = TestAccountId; + type Balance = u64; + type Index = u64; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + fn max_extrinsic_weight() -> Weight { + unreachable!() + } +} + +impl ChainWithBeefy for TestBridgedChain { + type CommitmentHasher = Keccak256; + type MmrHashing = Keccak256; + type MmrHash = ::Output; + type BeefyMmrLeafExtra = (); + type AuthorityId = BeefyId; + type AuthorityIdToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; +} + +/// Run test within test runtime. +pub fn run_test(test: impl FnOnce() -> T) -> T { + sp_io::TestExternalities::new(Default::default()).execute_with(test) +} + +/// Initialize pallet and run test. +pub fn run_test_with_initialize(initial_validators_count: u32, test: impl FnOnce() -> T) -> T { + run_test(|| { + let validators = validator_ids(0, initial_validators_count); + let authority_set = authority_set_info(0, &validators); + + crate::Pallet::::initialize( + RuntimeOrigin::root(), + bp_beefy::InitializationData { + operating_mode: BasicOperatingMode::Normal, + best_block_number: 0, + authority_set, + }, + ) + .expect("initialization data is correct"); + + test() + }) +} + +/// Import given commitment. +pub fn import_commitment( + header: crate::mock_chain::HeaderAndCommitment, +) -> sp_runtime::DispatchResult { + crate::Pallet::::submit_commitment( + RuntimeOrigin::signed(1), + header + .commitment + .expect("thou shall not call import_commitment on header without commitment"), + header.validator_set, + Box::new(header.leaf), + header.leaf_proof, + ) +} + +pub fn validator_pairs(index: u32, count: u32) -> Vec { + (index..index + count) + .map(|index| { + let mut seed = [1u8; 32]; + seed[0..8].copy_from_slice(&(index as u64).encode()); + BeefyPair::from_seed(&seed) + }) + .collect() +} + +/// Return identifiers of validators, starting at given index. +pub fn validator_ids(index: u32, count: u32) -> Vec { + validator_pairs(index, count).into_iter().map(|pair| pair.public()).collect() +} + +pub fn authority_set_info(id: u64, validators: &Vec) -> TestBridgedAuthoritySetInfo { + let merkle_root = get_authorities_mmr_root::(validators.iter()); + + TestBridgedAuthoritySetInfo { id, len: validators.len() as u32, root: merkle_root } +} + +/// Sign BEEFY commitment. +pub fn sign_commitment( + commitment: Commitment, + validator_pairs: &[BeefyPair], + signature_count: usize, +) -> TestBridgedCommitment { + let total_validators = validator_pairs.len(); + let random_validators = + rand::seq::index::sample(&mut rand::thread_rng(), total_validators, signature_count); + + let commitment_hash = TestBridgedCommitmentHasher::hash(&commitment.encode()); + let mut signatures = vec![None; total_validators]; + for validator_idx in random_validators.iter() { + let validator = &validator_pairs[validator_idx]; + signatures[validator_idx] = + Some(validator.as_inner_ref().sign_prehashed(commitment_hash.as_fixed_bytes()).into()); + } + + TestBridgedCommitment { commitment, signatures } +} diff --git a/modules/beefy/src/mock_chain.rs b/modules/beefy/src/mock_chain.rs new file mode 100644 index 00000000000..c736aed0742 --- /dev/null +++ b/modules/beefy/src/mock_chain.rs @@ -0,0 +1,299 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Utilities to build bridged chain and BEEFY+MMR structures. + +use crate::{ + mock::{ + sign_commitment, validator_pairs, BeefyPair, TestBridgedBlockNumber, TestBridgedCommitment, + TestBridgedHeader, TestBridgedMmrHash, TestBridgedMmrHashing, TestBridgedMmrNode, + TestBridgedMmrProof, TestBridgedRawMmrLeaf, TestBridgedValidatorSet, + TestBridgedValidatorSignature, TestRuntime, + }, + utils::get_authorities_mmr_root, +}; + +use bp_beefy::{BeefyPayload, Commitment, ValidatorSetId, MMR_ROOT_PAYLOAD_ID}; +use codec::Encode; +use pallet_mmr::NodeIndex; +use rand::Rng; +use sp_consensus_beefy::mmr::{BeefyNextAuthoritySet, MmrLeafVersion}; +use sp_core::Pair; +use sp_runtime::traits::{Hash, Header as HeaderT}; +use std::collections::HashMap; + +#[derive(Debug, Clone)] +pub struct HeaderAndCommitment { + pub header: TestBridgedHeader, + pub commitment: Option, + pub validator_set: TestBridgedValidatorSet, + pub leaf: TestBridgedRawMmrLeaf, + pub leaf_proof: TestBridgedMmrProof, + pub mmr_root: TestBridgedMmrHash, +} + +impl HeaderAndCommitment { + pub fn customize_signatures( + &mut self, + f: impl FnOnce(&mut Vec>), + ) { + if let Some(commitment) = &mut self.commitment { + f(&mut commitment.signatures); + } + } + + pub fn customize_commitment( + &mut self, + f: impl FnOnce(&mut Commitment), + validator_pairs: &[BeefyPair], + signature_count: usize, + ) { + if let Some(mut commitment) = self.commitment.take() { + f(&mut commitment.commitment); + self.commitment = + Some(sign_commitment(commitment.commitment, validator_pairs, signature_count)); + } + } +} + +pub struct ChainBuilder { + headers: Vec, + validator_set_id: ValidatorSetId, + validator_keys: Vec, + mmr: mmr_lib::MMR, +} + +struct BridgedMmrStorage { + nodes: HashMap, +} + +impl mmr_lib::MMRStore for BridgedMmrStorage { + fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result> { + Ok(self.nodes.get(&pos).cloned()) + } + + fn append(&mut self, pos: NodeIndex, elems: Vec) -> mmr_lib::Result<()> { + for (i, elem) in elems.into_iter().enumerate() { + self.nodes.insert(pos + i as NodeIndex, elem); + } + Ok(()) + } +} + +impl ChainBuilder { + /// Creates new chain builder with given validator set size. + pub fn new(initial_validators_count: u32) -> Self { + ChainBuilder { + headers: Vec::new(), + validator_set_id: 0, + validator_keys: validator_pairs(0, initial_validators_count), + mmr: mmr_lib::MMR::new(0, BridgedMmrStorage { nodes: HashMap::new() }), + } + } + + /// Get header with given number. + pub fn header(&self, number: TestBridgedBlockNumber) -> HeaderAndCommitment { + self.headers[number as usize - 1].clone() + } + + /// Returns single built header. + pub fn to_header(&self) -> HeaderAndCommitment { + assert_eq!(self.headers.len(), 1); + self.headers[0].clone() + } + + /// Returns built chain. + pub fn to_chain(&self) -> Vec { + self.headers.clone() + } + + /// Appends header, that has been finalized by BEEFY (so it has a linked signed commitment). + pub fn append_finalized_header(self) -> Self { + let next_validator_set_id = self.validator_set_id; + let next_validator_keys = self.validator_keys.clone(); + HeaderBuilder::with_chain(self, next_validator_set_id, next_validator_keys).finalize() + } + + /// Append multiple finalized headers at once. + pub fn append_finalized_headers(mut self, count: usize) -> Self { + for _ in 0..count { + self = self.append_finalized_header(); + } + self + } + + /// Appends header, that enacts new validator set. + /// + /// Such headers are explicitly finalized by BEEFY. + pub fn append_handoff_header(self, next_validators_len: u32) -> Self { + let new_validator_set_id = self.validator_set_id + 1; + let new_validator_pairs = + validator_pairs(rand::thread_rng().gen::() % (u32::MAX / 2), next_validators_len); + + HeaderBuilder::with_chain(self, new_validator_set_id, new_validator_pairs).finalize() + } + + /// Append several default header without commitment. + pub fn append_default_headers(mut self, count: usize) -> Self { + for _ in 0..count { + let next_validator_set_id = self.validator_set_id; + let next_validator_keys = self.validator_keys.clone(); + self = + HeaderBuilder::with_chain(self, next_validator_set_id, next_validator_keys).build() + } + self + } +} + +/// Custom header builder. +pub struct HeaderBuilder { + chain: ChainBuilder, + header: TestBridgedHeader, + leaf: TestBridgedRawMmrLeaf, + leaf_proof: Option, + next_validator_set_id: ValidatorSetId, + next_validator_keys: Vec, +} + +impl HeaderBuilder { + fn with_chain( + chain: ChainBuilder, + next_validator_set_id: ValidatorSetId, + next_validator_keys: Vec, + ) -> Self { + // we're starting with header#1, since header#0 is always finalized + let header_number = chain.headers.len() as TestBridgedBlockNumber + 1; + let header = TestBridgedHeader::new( + header_number, + Default::default(), + Default::default(), + chain.headers.last().map(|h| h.header.hash()).unwrap_or_default(), + Default::default(), + ); + + let next_validators = + next_validator_keys.iter().map(|pair| pair.public()).collect::>(); + let next_validators_mmr_root = + get_authorities_mmr_root::(next_validators.iter()); + let leaf = sp_consensus_beefy::mmr::MmrLeaf { + version: MmrLeafVersion::new(1, 0), + parent_number_and_hash: (header.number().saturating_sub(1), *header.parent_hash()), + beefy_next_authority_set: BeefyNextAuthoritySet { + id: next_validator_set_id, + len: next_validators.len() as u32, + root: next_validators_mmr_root, + }, + leaf_extra: (), + }; + + HeaderBuilder { + chain, + header, + leaf, + leaf_proof: None, + next_validator_keys, + next_validator_set_id, + } + } + + /// Customize generated proof of header MMR leaf. + /// + /// Can only be called once. + pub fn customize_proof( + mut self, + f: impl FnOnce(TestBridgedMmrProof) -> TestBridgedMmrProof, + ) -> Self { + assert!(self.leaf_proof.is_none()); + + let leaf_hash = TestBridgedMmrHashing::hash(&self.leaf.encode()); + let node = TestBridgedMmrNode::Hash(leaf_hash); + let leaf_position = self.chain.mmr.push(node).unwrap(); + + let proof = self.chain.mmr.gen_proof(vec![leaf_position]).unwrap(); + // genesis has no leaf => leaf index is header number minus 1 + let leaf_index = *self.header.number() - 1; + let leaf_count = *self.header.number(); + self.leaf_proof = Some(f(TestBridgedMmrProof { + leaf_indices: vec![leaf_index], + leaf_count, + items: proof.proof_items().iter().map(|i| i.hash()).collect(), + })); + + self + } + + /// Build header without commitment. + pub fn build(mut self) -> ChainBuilder { + if self.leaf_proof.is_none() { + self = self.customize_proof(|proof| proof); + } + + let validators = + self.chain.validator_keys.iter().map(|pair| pair.public()).collect::>(); + self.chain.headers.push(HeaderAndCommitment { + header: self.header, + commitment: None, + validator_set: TestBridgedValidatorSet::new(validators, self.chain.validator_set_id) + .unwrap(), + leaf: self.leaf, + leaf_proof: self.leaf_proof.expect("guaranteed by the customize_proof call above; qed"), + mmr_root: self.chain.mmr.get_root().unwrap().hash(), + }); + + self.chain.validator_set_id = self.next_validator_set_id; + self.chain.validator_keys = self.next_validator_keys; + + self.chain + } + + /// Build header with commitment. + pub fn finalize(self) -> ChainBuilder { + let validator_count = self.chain.validator_keys.len(); + let current_validator_set_id = self.chain.validator_set_id; + let current_validator_set_keys = self.chain.validator_keys.clone(); + let mut chain = self.build(); + + let last_header = chain.headers.last_mut().expect("added by append_header; qed"); + last_header.commitment = Some(sign_commitment( + Commitment { + payload: BeefyPayload::from_single_entry( + MMR_ROOT_PAYLOAD_ID, + chain.mmr.get_root().unwrap().hash().encode(), + ), + block_number: *last_header.header.number(), + validator_set_id: current_validator_set_id, + }, + ¤t_validator_set_keys, + validator_count * 2 / 3 + 1, + )); + + chain + } +} + +/// Default Merging & Hashing behavior for MMR. +pub struct BridgedMmrHashMerge; + +impl mmr_lib::Merge for BridgedMmrHashMerge { + type Item = TestBridgedMmrNode; + + fn merge(left: &Self::Item, right: &Self::Item) -> Self::Item { + let mut concat = left.hash().as_ref().to_vec(); + concat.extend_from_slice(right.hash().as_ref()); + + TestBridgedMmrNode::Hash(TestBridgedMmrHashing::hash(&concat)) + } +} diff --git a/modules/beefy/src/utils.rs b/modules/beefy/src/utils.rs new file mode 100644 index 00000000000..45e14ecbfe0 --- /dev/null +++ b/modules/beefy/src/utils.rs @@ -0,0 +1,361 @@ +use crate::{ + BridgedBeefyAuthorityId, BridgedBeefyAuthoritySet, BridgedBeefyAuthoritySetInfo, + BridgedBeefyMmrLeaf, BridgedBeefySignedCommitment, BridgedChain, BridgedMmrHash, + BridgedMmrHashing, BridgedMmrProof, Config, Error, LOG_TARGET, +}; +use bp_beefy::{merkle_root, verify_mmr_leaves_proof, BeefyAuthorityId, MmrDataOrHash}; +use codec::Encode; +use frame_support::ensure; +use sp_runtime::traits::{Convert, Hash}; +use sp_std::{vec, vec::Vec}; + +type BridgedMmrDataOrHash = MmrDataOrHash, BridgedBeefyMmrLeaf>; +/// A way to encode validator id to the BEEFY merkle tree leaf. +type BridgedBeefyAuthorityIdToMerkleLeaf = + bp_beefy::BeefyAuthorityIdToMerkleLeafOf>; + +/// Get the MMR root for a collection of validators. +pub(crate) fn get_authorities_mmr_root< + 'a, + T: Config, + I: 'static, + V: Iterator>, +>( + authorities: V, +) -> BridgedMmrHash { + let merkle_leafs = authorities + .cloned() + .map(BridgedBeefyAuthorityIdToMerkleLeaf::::convert) + .collect::>(); + merkle_root::, _>(merkle_leafs) +} + +fn verify_authority_set, I: 'static>( + authority_set_info: &BridgedBeefyAuthoritySetInfo, + authority_set: &BridgedBeefyAuthoritySet, +) -> Result<(), Error> { + ensure!(authority_set.id() == authority_set_info.id, Error::::InvalidValidatorSetId); + ensure!( + authority_set.len() == authority_set_info.len as usize, + Error::::InvalidValidatorSetLen + ); + + // Ensure that the authority set that signed the commitment is the expected one. + let root = get_authorities_mmr_root::(authority_set.validators().iter()); + ensure!(root == authority_set_info.root, Error::::InvalidValidatorSetRoot); + + Ok(()) +} + +/// Number of correct signatures, required from given validators set to accept signed +/// commitment. +/// +/// We're using 'conservative' approach here, where signatures of `2/3+1` validators are +/// required.. +pub(crate) fn signatures_required(validators_len: usize) -> usize { + validators_len - validators_len.saturating_sub(1) / 3 +} + +fn verify_signatures, I: 'static>( + commitment: &BridgedBeefySignedCommitment, + authority_set: &BridgedBeefyAuthoritySet, +) -> Result<(), Error> { + ensure!( + commitment.signatures.len() == authority_set.len(), + Error::::InvalidCommitmentSignaturesLen + ); + + // Ensure that the commitment was signed by enough authorities. + let msg = commitment.commitment.encode(); + let mut missing_signatures = signatures_required(authority_set.len()); + for (idx, (authority, maybe_sig)) in + authority_set.validators().iter().zip(commitment.signatures.iter()).enumerate() + { + if let Some(sig) = maybe_sig { + if authority.verify(sig, &msg) { + missing_signatures = missing_signatures.saturating_sub(1); + if missing_signatures == 0 { + break + } + } else { + log::debug!( + target: LOG_TARGET, + "Signed commitment contains incorrect signature of validator {} ({:?}): {:?}", + idx, + authority, + sig, + ); + } + } + } + ensure!(missing_signatures == 0, Error::::NotEnoughCorrectSignatures); + + Ok(()) +} + +/// Extract MMR root from commitment payload. +fn extract_mmr_root, I: 'static>( + commitment: &BridgedBeefySignedCommitment, +) -> Result, Error> { + commitment + .commitment + .payload + .get_decoded(&bp_beefy::MMR_ROOT_PAYLOAD_ID) + .ok_or(Error::MmrRootMissingFromCommitment) +} + +pub(crate) fn verify_commitment, I: 'static>( + commitment: &BridgedBeefySignedCommitment, + authority_set_info: &BridgedBeefyAuthoritySetInfo, + authority_set: &BridgedBeefyAuthoritySet, +) -> Result, Error> { + // Ensure that the commitment is signed by the best known BEEFY validator set. + ensure!( + commitment.commitment.validator_set_id == authority_set_info.id, + Error::::InvalidCommitmentValidatorSetId + ); + ensure!( + commitment.signatures.len() == authority_set_info.len as usize, + Error::::InvalidCommitmentSignaturesLen + ); + + verify_authority_set(authority_set_info, authority_set)?; + verify_signatures(commitment, authority_set)?; + + extract_mmr_root(commitment) +} + +/// Verify MMR proof of given leaf. +pub(crate) fn verify_beefy_mmr_leaf, I: 'static>( + mmr_leaf: &BridgedBeefyMmrLeaf, + mmr_proof: BridgedMmrProof, + mmr_root: BridgedMmrHash, +) -> Result<(), Error> { + let mmr_proof_leaf_count = mmr_proof.leaf_count; + let mmr_proof_length = mmr_proof.items.len(); + + // Verify the mmr proof for the provided leaf. + let mmr_leaf_hash = BridgedMmrHashing::::hash(&mmr_leaf.encode()); + verify_mmr_leaves_proof( + mmr_root, + vec![BridgedMmrDataOrHash::::Hash(mmr_leaf_hash)], + mmr_proof, + ) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "MMR proof of leaf {:?} (root: {:?}, leaf count: {}, len: {}) \ + verification has failed with error: {:?}", + mmr_leaf_hash, + mmr_root, + mmr_proof_leaf_count, + mmr_proof_length, + e, + ); + + Error::::MmrProofVerificationFailed + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{mock::*, mock_chain::*, *}; + use bp_beefy::{BeefyPayload, MMR_ROOT_PAYLOAD_ID}; + use frame_support::{assert_noop, assert_ok}; + use sp_consensus_beefy::ValidatorSet; + + #[test] + fn submit_commitment_checks_metadata() { + run_test_with_initialize(8, || { + // Fails if `commitment.commitment.validator_set_id` differs. + let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); + header.customize_commitment( + |commitment| { + commitment.validator_set_id += 1; + }, + &validator_pairs(0, 8), + 6, + ); + assert_noop!( + import_commitment(header), + Error::::InvalidCommitmentValidatorSetId, + ); + + // Fails if `commitment.signatures.len()` differs. + let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); + header.customize_signatures(|signatures| { + signatures.pop(); + }); + assert_noop!( + import_commitment(header), + Error::::InvalidCommitmentSignaturesLen, + ); + }); + } + + #[test] + fn submit_commitment_checks_validator_set() { + run_test_with_initialize(8, || { + // Fails if `ValidatorSet::id` differs. + let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); + header.validator_set = ValidatorSet::new(validator_ids(0, 8), 1).unwrap(); + assert_noop!( + import_commitment(header), + Error::::InvalidValidatorSetId, + ); + + // Fails if `ValidatorSet::len()` differs. + let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); + header.validator_set = ValidatorSet::new(validator_ids(0, 5), 0).unwrap(); + assert_noop!( + import_commitment(header), + Error::::InvalidValidatorSetLen, + ); + + // Fails if the validators differ. + let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); + header.validator_set = ValidatorSet::new(validator_ids(3, 8), 0).unwrap(); + assert_noop!( + import_commitment(header), + Error::::InvalidValidatorSetRoot, + ); + }); + } + + #[test] + fn submit_commitment_checks_signatures() { + run_test_with_initialize(20, || { + // Fails when there aren't enough signatures. + let mut header = ChainBuilder::new(20).append_finalized_header().to_header(); + header.customize_signatures(|signatures| { + let first_signature_idx = signatures.iter().position(Option::is_some).unwrap(); + signatures[first_signature_idx] = None; + }); + assert_noop!( + import_commitment(header), + Error::::NotEnoughCorrectSignatures, + ); + + // Fails when there aren't enough correct signatures. + let mut header = ChainBuilder::new(20).append_finalized_header().to_header(); + header.customize_signatures(|signatures| { + let first_signature_idx = signatures.iter().position(Option::is_some).unwrap(); + let last_signature_idx = signatures.len() - + signatures.iter().rev().position(Option::is_some).unwrap() - + 1; + signatures[first_signature_idx] = signatures[last_signature_idx].clone(); + }); + assert_noop!( + import_commitment(header), + Error::::NotEnoughCorrectSignatures, + ); + + // Returns Ok(()) when there are enough signatures, even if some are incorrect. + let mut header = ChainBuilder::new(20).append_finalized_header().to_header(); + header.customize_signatures(|signatures| { + let first_signature_idx = signatures.iter().position(Option::is_some).unwrap(); + let first_missing_signature_idx = + signatures.iter().position(Option::is_none).unwrap(); + signatures[first_missing_signature_idx] = signatures[first_signature_idx].clone(); + }); + assert_ok!(import_commitment(header)); + }); + } + + #[test] + fn submit_commitment_checks_mmr_proof() { + run_test_with_initialize(1, || { + let validators = validator_pairs(0, 1); + + // Fails if leaf is not for parent. + let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); + header.leaf.parent_number_and_hash.0 += 1; + assert_noop!( + import_commitment(header), + Error::::MmrProofVerificationFailed, + ); + + // Fails if mmr proof is incorrect. + let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); + header.leaf_proof.leaf_indices[0] += 1; + assert_noop!( + import_commitment(header), + Error::::MmrProofVerificationFailed, + ); + + // Fails if mmr root is incorrect. + let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); + // Replace MMR root with zeroes. + header.customize_commitment( + |commitment| { + commitment.payload = + BeefyPayload::from_single_entry(MMR_ROOT_PAYLOAD_ID, [0u8; 32].encode()); + }, + &validators, + 1, + ); + assert_noop!( + import_commitment(header), + Error::::MmrProofVerificationFailed, + ); + }); + } + + #[test] + fn submit_commitment_extracts_mmr_root() { + run_test_with_initialize(1, || { + let validators = validator_pairs(0, 1); + + // Fails if there is no mmr root in the payload. + let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); + // Remove MMR root from the payload. + header.customize_commitment( + |commitment| { + commitment.payload = BeefyPayload::from_single_entry(*b"xy", vec![]); + }, + &validators, + 1, + ); + assert_noop!( + import_commitment(header), + Error::::MmrRootMissingFromCommitment, + ); + + // Fails if mmr root can't be decoded. + let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); + // MMR root is a 32-byte array and we have replaced it with single byte + header.customize_commitment( + |commitment| { + commitment.payload = + BeefyPayload::from_single_entry(MMR_ROOT_PAYLOAD_ID, vec![42]); + }, + &validators, + 1, + ); + assert_noop!( + import_commitment(header), + Error::::MmrRootMissingFromCommitment, + ); + }); + } + + #[test] + fn submit_commitment_stores_valid_data() { + run_test_with_initialize(20, || { + let header = ChainBuilder::new(20).append_handoff_header(30).to_header(); + assert_ok!(import_commitment(header.clone())); + + assert_eq!(ImportedCommitmentsInfo::::get().unwrap().best_block_number, 1); + assert_eq!(CurrentAuthoritySetInfo::::get().id, 1); + assert_eq!(CurrentAuthoritySetInfo::::get().len, 30); + assert_eq!( + ImportedCommitments::::get(1).unwrap(), + bp_beefy::ImportedCommitment { + parent_number_and_hash: (0, [0; 32].into()), + mmr_root: header.mmr_root, + }, + ); + }); + } +} diff --git a/modules/grandpa/Cargo.toml b/modules/grandpa/Cargo.toml new file mode 100644 index 00000000000..07d2593b914 --- /dev/null +++ b/modules/grandpa/Cargo.toml @@ -0,0 +1,63 @@ +[package] +name = "pallet-bridge-grandpa" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +finality-grandpa = { version = "0.16.2", default-features = false } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } + +# Bridge Dependencies + +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-header-chain = { path = "../../primitives/header-chain", default-features = false } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +# Optional Benchmarking Dependencies +bp-test-utils = { path = "../../primitives/test-utils", default-features = false, optional = true } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } + +[dev-dependencies] +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "bp-runtime/std", + "bp-test-utils/std", + "codec/std", + "finality-grandpa/std", + "frame-support/std", + "frame-system/std", + "frame-benchmarking/std", + "log/std", + "scale-info/std", + "sp-consensus-grandpa/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", +] +runtime-benchmarks = [ + "bp-test-utils", + "frame-benchmarking/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/modules/grandpa/README.md b/modules/grandpa/README.md new file mode 100644 index 00000000000..27b4d2389c4 --- /dev/null +++ b/modules/grandpa/README.md @@ -0,0 +1,101 @@ +# Bridge GRANDPA Pallet + +The bridge GRANDPA pallet is a light client for the GRANDPA finality gadget, running at the bridged chain. +It may import headers and their GRANDPA finality proofs (justifications) of the bridged chain. Imported +headers then may be used to verify storage proofs by other pallets. This makes the bridge GRANDPA pallet +a basic pallet of all bridges with Substrate-based chains. It is used by all bridge types (bridge between +standalone chains, between parachains and any combination of those) and is used by other bridge pallets. +It is used by the parachains light client (bridge parachains pallet) and by messages pallet. + +## A Brief Introduction into GRANDPA Finality + +You can find detailed information on GRANDPA, by exploring its [repository](https://github.com/paritytech/finality-grandpa). +Here is the minimal reqiuired GRANDPA information to understand how pallet works. + +Any Substrate chain may use different block authorship algorithms (like BABE or Aura) to determine block producers and +generate blocks. This has nothing common with finality, though - the task of block authorship is to coordinate +blocks generation. Any block may be reverted (if there's a fork) if it is not finalized. The finality solution +for (standalone) Substrate-based chains is the GRANDPA finality gadget. If some block is finalized by the gadget, it +can't be reverted. + +In GRANDPA, there are validators, identified by their public keys. They select some generated block and produce +signatures on this block hash. If there are enough (more than `2 / 3 * N`, where `N` is number of validators) +signatures, then the block is considered finalized. The set of signatures for the block is called justification. +Anyone who knows the public keys of validators is able to verify GRANDPA justification and that it is generated +for provided header. + +There are two main things in GRANDPA that help building light clients: + +- there's no need to import all headers of the bridged chain. Light client may import finalized headers or just + some of finalized headders that it consider useful. While the validators set stays the same, the client may + import any header that is finalized by this set; + +- when validators set changes, the GRANDPA gadget adds next set to the header. So light client doesn't need to + verify storage proofs when this happens - it only needs to look at the header and see if it changes the set. + Once set is changed, all following justifications are generated by the new set. Header that is changing the + set is called "mandatory" in the pallet. As the name says, the light client need to import all such headers + to be able to operate properly. + +## Pallet Operations + +The main entrypoint of the pallet is the `submit_finality_proof` call. It has two arguments - the finalized +headers and associated GRANDPA justification. The call simply verifies the justification using current +validators set and checks if header is better than the previous best header. If both checks are passed, the +header (only its useful fields) is inserted into the runtime storage and may be used by other pallets to verify +storage proofs. + +The submitter pays regular fee for submitting all headers, except for the mandatory header. Since it is +required for the pallet operations, submitting such header is free. So if you're ok with session-length +lags (meaning that there's exactly 1 mandatory header per session), the cost of pallet calls is zero. + +When the pallet sees mandatory header, it updates the validators set with the set from the header. All +following justifications (until next mandatory header) must be generated by this new set. + +## Pallet Initialization + +As the previous section states, there are two things that are mandatory for pallet operations: best finalized +header and the current validators set. Without it the pallet can't import any headers. But how to provide +initial values for these fields? There are two options. + +First option, while it is easier, doesn't work in all cases. It is to start chain with initial header and +validators set specified in the chain specification. This won't work, however, if we want to add bridge +to already started chain. + +For the latter case we have the `initialize` call. It accepts the initial header and initial validators set. +The call may be called by the governance, root or by the pallet owner (if it is set). + +## Non-Essential Functionality + +There may be a special account in every runtime where the bridge GRANDPA module is deployed. This +account, named 'module owner', is like a module-level sudo account - he's able to halt and +resume all module operations without requiring runtime upgrade. Calls that are related to this +account are: + +- `fn set_owner()`: current module owner may call it to transfer "ownership" to another account; + +- `fn set_operating_mode()`: the module owner (or sudo account) may call this function to stop all + module operations. After this call, all finality proofs will be rejected until further `set_operating_mode` call'. + This call may be used when something extraordinary happens with the bridge; + +- `fn initialize()`: module owner may call this function to initialize the bridge. + +If pallet owner is not defined, the governance may be used to make those calls. + +## Signed Extension to Reject Obsolete Headers + +It'd be better for anyone (for chain and for submitters) to reject all transactions that are submitting +already known headers to the pallet. This way, we leave block space to other useful transactions and +we don't charge concurrent submitters for their honest actions. + +To deal with that, we have a [signed extension](./src/call_ext) that may be added to the runtime. +It does exactly what is required - rejects all transactions with already known headers. The submitter +pays nothing for such transactions - they're simply removed from the transaction pool, when the block +is built. + +You may also take a look at the [`generate_bridge_reject_obsolete_headers_and_messages`](../../bin/runtime-common/src/lib.rs) +macro that bundles several similar signed extensions in a single one. + +## GRANDPA Finality Relay + +We have an offchain actor, who is watching for GRANDPA justifications and submits them to the bridged chain. +It is the finality relay - you may look at the [crate level documentation and the code](../../relays/finality/). diff --git a/modules/grandpa/src/benchmarking.rs b/modules/grandpa/src/benchmarking.rs new file mode 100644 index 00000000000..aa222d6e4de --- /dev/null +++ b/modules/grandpa/src/benchmarking.rs @@ -0,0 +1,141 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Benchmarks for the GRANDPA Pallet. +//! +//! The main dispatchable for the GRANDPA pallet is `submit_finality_proof`, so these benchmarks are +//! based around that. There are to main factors which affect finality proof verification: +//! +//! 1. The number of `votes-ancestries` in the justification +//! 2. The number of `pre-commits` in the justification +//! +//! Vote ancestries are the headers between (`finality_target`, `head_of_chain`], where +//! `header_of_chain` is a descendant of `finality_target`. +//! +//! Pre-commits are messages which are signed by validators at the head of the chain they think is +//! the best. +//! +//! Consider the following: +//! +//! / B <- C' +//! A <- B <- C +//! +//! The common ancestor of both forks is block A, so this is what GRANDPA will finalize. In order to +//! verify this we will have vote ancestries of `[B, C, B', C']` and pre-commits `[C, C']`. +//! +//! Note that the worst case scenario here would be a justification where each validator has it's +//! own fork which is `SESSION_LENGTH` blocks long. + +use crate::*; + +use bp_header_chain::justification::required_justification_precommits; +use bp_runtime::BasicOperatingMode; +use bp_test_utils::{ + accounts, make_justification_for_header, JustificationGeneratorParams, TEST_GRANDPA_ROUND, + TEST_GRANDPA_SET_ID, +}; +use frame_benchmarking::{benchmarks_instance_pallet, whitelisted_caller}; +use frame_system::RawOrigin; +use sp_consensus_grandpa::AuthorityId; +use sp_runtime::traits::{One, Zero}; +use sp_std::vec::Vec; + +/// The maximum number of vote ancestries to include in a justification. +/// +/// In practice this would be limited by the session length (number of blocks a single authority set +/// can produce) of a given chain. +const MAX_VOTE_ANCESTRIES: u32 = 1000; + +// `1..MAX_VOTE_ANCESTRIES` is too large && benchmarks are running for almost 40m (steps=50, +// repeat=20) on a decent laptop, which is too much. Since we're building linear function here, +// let's just select some limited subrange for benchmarking. +const MAX_VOTE_ANCESTRIES_RANGE_BEGIN: u32 = MAX_VOTE_ANCESTRIES / 20; +const MAX_VOTE_ANCESTRIES_RANGE_END: u32 = + MAX_VOTE_ANCESTRIES_RANGE_BEGIN + MAX_VOTE_ANCESTRIES_RANGE_BEGIN; + +// the same with validators - if there are too much validators, let's run benchmarks on subrange +fn precommits_range_end, I: 'static>() -> u32 { + let max_bridged_authorities = T::BridgedChain::MAX_AUTHORITIES_COUNT; + if max_bridged_authorities > 128 { + sp_std::cmp::max(128, max_bridged_authorities / 5) + } else { + max_bridged_authorities + }; + required_justification_precommits(max_bridged_authorities) +} + +/// Prepare header and its justification to submit using `submit_finality_proof`. +fn prepare_benchmark_data, I: 'static>( + precommits: u32, + ancestors: u32, +) -> (BridgedHeader, GrandpaJustification>) { + // going from precommits to total authorities count + let total_authorities_count = (3 * precommits - 1) / 2; + + let authority_list = accounts(total_authorities_count as u16) + .iter() + .map(|id| (AuthorityId::from(*id), 1)) + .collect::>(); + + let genesis_header: BridgedHeader = bp_test_utils::test_header(Zero::zero()); + let genesis_hash = genesis_header.hash(); + let init_data = InitializationData { + header: Box::new(genesis_header), + authority_list, + set_id: TEST_GRANDPA_SET_ID, + operating_mode: BasicOperatingMode::Normal, + }; + + bootstrap_bridge::(init_data); + assert!(>::contains_key(genesis_hash)); + + let header: BridgedHeader = bp_test_utils::test_header(One::one()); + let params = JustificationGeneratorParams { + header: header.clone(), + round: TEST_GRANDPA_ROUND, + set_id: TEST_GRANDPA_SET_ID, + authorities: accounts(precommits as u16).iter().map(|k| (*k, 1)).collect::>(), + ancestors, + forks: 1, + }; + let justification = make_justification_for_header(params); + (header, justification) +} + +benchmarks_instance_pallet! { + // This is the "gold standard" benchmark for this extrinsic, and it's what should be used to + // annotate the weight in the pallet. + submit_finality_proof { + let p in 1 .. precommits_range_end::(); + let v in MAX_VOTE_ANCESTRIES_RANGE_BEGIN..MAX_VOTE_ANCESTRIES_RANGE_END; + let caller: T::AccountId = whitelisted_caller(); + let (header, justification) = prepare_benchmark_data::(p, v); + }: submit_finality_proof(RawOrigin::Signed(caller), Box::new(header), justification) + verify { + let genesis_header: BridgedHeader = bp_test_utils::test_header(Zero::zero()); + let header: BridgedHeader = bp_test_utils::test_header(One::one()); + let expected_hash = header.hash(); + + // check that the header#1 has been inserted + assert_eq!(>::get().unwrap().1, expected_hash); + assert!(>::contains_key(expected_hash)); + + // check that the header#0 has been pruned + assert!(!>::contains_key(genesis_header.hash())); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) +} diff --git a/modules/grandpa/src/call_ext.rs b/modules/grandpa/src/call_ext.rs new file mode 100644 index 00000000000..b57aebb1ac1 --- /dev/null +++ b/modules/grandpa/src/call_ext.rs @@ -0,0 +1,311 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{weights::WeightInfo, BridgedBlockNumber, BridgedHeader, Config, Error, Pallet}; +use bp_header_chain::{justification::GrandpaJustification, ChainWithGrandpa}; +use bp_runtime::BlockNumberOf; +use codec::Encode; +use frame_support::{dispatch::CallableCallFor, traits::IsSubType, weights::Weight, RuntimeDebug}; +use sp_runtime::{ + traits::{Header, Zero}, + transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, + SaturatedConversion, +}; + +/// Info about a `SubmitParachainHeads` call which tries to update a single parachain. +#[derive(Copy, Clone, PartialEq, RuntimeDebug)] +pub struct SubmitFinalityProofInfo { + /// Number of the finality target. + pub block_number: N, + /// Extra weight that we assume is included in the call. + /// + /// We have some assumptions about headers and justifications of the bridged chain. + /// We know that if our assumptions are correct, then the call must not have the + /// weight above some limit. The fee paid for weight above that limit, is never refunded. + pub extra_weight: Weight, + /// Extra size (in bytes) that we assume are included in the call. + /// + /// We have some assumptions about headers and justifications of the bridged chain. + /// We know that if our assumptions are correct, then the call must not have the + /// weight above some limit. The fee paid for bytes above that limit, is never refunded. + pub extra_size: u32, +} + +impl SubmitFinalityProofInfo { + /// Returns `true` if call size/weight is below our estimations for regular calls. + pub fn fits_limits(&self) -> bool { + self.extra_weight.is_zero() && self.extra_size.is_zero() + } +} + +/// Helper struct that provides methods for working with the `SubmitFinalityProof` call. +pub struct SubmitFinalityProofHelper, I: 'static> { + _phantom_data: sp_std::marker::PhantomData<(T, I)>, +} + +impl, I: 'static> SubmitFinalityProofHelper { + /// Check that the GRANDPA head provided by the `SubmitFinalityProof` is better than the best + /// one we know. + pub fn check_obsolete( + finality_target: BlockNumberOf, + ) -> Result<(), Error> { + let best_finalized = crate::BestFinalized::::get().ok_or_else(|| { + log::trace!( + target: crate::LOG_TARGET, + "Cannot finalize header {:?} because pallet is not yet initialized", + finality_target, + ); + >::NotInitialized + })?; + + if best_finalized.number() >= finality_target { + log::trace!( + target: crate::LOG_TARGET, + "Cannot finalize obsolete header: bundled {:?}, best {:?}", + finality_target, + best_finalized, + ); + + return Err(Error::::OldHeader) + } + + Ok(()) + } + + /// Check if the `SubmitFinalityProof` was successfully executed. + pub fn was_successful(finality_target: BlockNumberOf) -> bool { + match crate::BestFinalized::::get() { + Some(best_finalized) => best_finalized.number() == finality_target, + None => false, + } + } +} + +/// Trait representing a call that is a sub type of this pallet's call. +pub trait CallSubType, I: 'static>: + IsSubType, T>> +{ + /// Extract finality proof info from a runtime call. + fn submit_finality_proof_info( + &self, + ) -> Option>> { + if let Some(crate::Call::::submit_finality_proof { finality_target, justification }) = + self.is_sub_type() + { + return Some(submit_finality_proof_info_from_args::( + finality_target, + justification, + )) + } + + None + } + + /// Validate Grandpa headers in order to avoid "mining" transactions that provide outdated + /// bridged chain headers. Without this validation, even honest relayers may lose their funds + /// if there are multiple relays running and submitting the same information. + fn check_obsolete_submit_finality_proof(&self) -> TransactionValidity + where + Self: Sized, + { + let finality_target = match self.submit_finality_proof_info() { + Some(finality_proof) => finality_proof, + _ => return Ok(ValidTransaction::default()), + }; + + match SubmitFinalityProofHelper::::check_obsolete(finality_target.block_number) { + Ok(_) => Ok(ValidTransaction::default()), + Err(Error::::OldHeader) => InvalidTransaction::Stale.into(), + Err(_) => InvalidTransaction::Call.into(), + } + } +} + +impl, I: 'static> CallSubType for T::RuntimeCall where + T::RuntimeCall: IsSubType, T>> +{ +} + +/// Extract finality proof info from the submitted header and justification. +pub(crate) fn submit_finality_proof_info_from_args, I: 'static>( + finality_target: &BridgedHeader, + justification: &GrandpaJustification>, +) -> SubmitFinalityProofInfo> { + let block_number = *finality_target.number(); + + // the `submit_finality_proof` call will reject justifications with invalid, duplicate, + // unknown and extra signatures. It'll also reject justifications with less than necessary + // signatures. So we do not care about extra weight because of additional signatures here. + let precommits_len = justification.commit.precommits.len().saturated_into(); + let required_precommits = precommits_len; + + // We do care about extra weight because of more-than-expected headers in the votes + // ancestries. But we have problems computing extra weight for additional headers (weight of + // additional header is too small, so that our benchmarks aren't detecting that). So if there + // are more than expected headers in votes ancestries, we will treat the whole call weight + // as an extra weight. + let votes_ancestries_len = justification.votes_ancestries.len().saturated_into(); + let extra_weight = + if votes_ancestries_len > T::BridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY { + T::WeightInfo::submit_finality_proof(precommits_len, votes_ancestries_len) + } else { + Weight::zero() + }; + + // we can estimate extra call size easily, without any additional significant overhead + let actual_call_size: u32 = finality_target + .encoded_size() + .saturating_add(justification.encoded_size()) + .saturated_into(); + let max_expected_call_size = max_expected_call_size::(required_precommits); + let extra_size = actual_call_size.saturating_sub(max_expected_call_size); + + SubmitFinalityProofInfo { block_number, extra_weight, extra_size } +} + +/// Returns maximal expected size of `submit_finality_proof` call arguments. +fn max_expected_call_size, I: 'static>(required_precommits: u32) -> u32 { + let max_expected_justification_size = + GrandpaJustification::max_reasonable_size::(required_precommits); + + // call arguments are header and justification + T::BridgedChain::MAX_HEADER_SIZE.saturating_add(max_expected_justification_size) +} + +#[cfg(test)] +mod tests { + use crate::{ + call_ext::CallSubType, + mock::{run_test, test_header, RuntimeCall, TestBridgedChain, TestNumber, TestRuntime}, + BestFinalized, Config, WeightInfo, + }; + use bp_header_chain::ChainWithGrandpa; + use bp_runtime::HeaderId; + use bp_test_utils::{ + make_default_justification, make_justification_for_header, JustificationGeneratorParams, + }; + use frame_support::weights::Weight; + use sp_runtime::{testing::DigestItem, traits::Header as _, SaturatedConversion}; + + fn validate_block_submit(num: TestNumber) -> bool { + let bridge_grandpa_call = crate::Call::::submit_finality_proof { + finality_target: Box::new(test_header(num)), + justification: make_default_justification(&test_header(num)), + }; + RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa( + bridge_grandpa_call, + )) + .is_ok() + } + + fn sync_to_header_10() { + let header10_hash = sp_core::H256::default(); + BestFinalized::::put(HeaderId(10, header10_hash)); + } + + #[test] + fn extension_rejects_obsolete_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5 => tx is + // rejected + sync_to_header_10(); + assert!(!validate_block_submit(5)); + }); + } + + #[test] + fn extension_rejects_same_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_header_10(); + assert!(!validate_block_submit(10)); + }); + } + + #[test] + fn extension_accepts_new_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#15 => tx is + // accepted + sync_to_header_10(); + assert!(validate_block_submit(15)); + }); + } + + #[test] + fn extension_returns_correct_extra_size_if_call_arguments_are_too_large() { + // when call arguments are below our limit => no refund + let small_finality_target = test_header(1); + let justification_params = JustificationGeneratorParams { + header: small_finality_target.clone(), + ..Default::default() + }; + let small_justification = make_justification_for_header(justification_params); + let small_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof { + finality_target: Box::new(small_finality_target), + justification: small_justification, + }); + assert_eq!(small_call.submit_finality_proof_info().unwrap().extra_size, 0); + + // when call arguments are too large => partial refund + let mut large_finality_target = test_header(1); + large_finality_target + .digest_mut() + .push(DigestItem::Other(vec![42u8; 1024 * 1024])); + let justification_params = JustificationGeneratorParams { + header: large_finality_target.clone(), + ..Default::default() + }; + let large_justification = make_justification_for_header(justification_params); + let large_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof { + finality_target: Box::new(large_finality_target), + justification: large_justification, + }); + assert_ne!(large_call.submit_finality_proof_info().unwrap().extra_size, 0); + } + + #[test] + fn extension_returns_correct_extra_weight_if_there_are_too_many_headers_in_votes_ancestry() { + let finality_target = test_header(1); + let mut justification_params = JustificationGeneratorParams { + header: finality_target.clone(), + ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY, + ..Default::default() + }; + + // when there are `REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY` headers => no refund + let justification = make_justification_for_header(justification_params.clone()); + let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof { + finality_target: Box::new(finality_target.clone()), + justification, + }); + assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, Weight::zero()); + + // when there are `REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY + 1` headers => full refund + justification_params.ancestors += 1; + let justification = make_justification_for_header(justification_params); + let call_weight = ::WeightInfo::submit_finality_proof( + justification.commit.precommits.len().saturated_into(), + justification.votes_ancestries.len().saturated_into(), + ); + let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof { + finality_target: Box::new(finality_target), + justification, + }); + assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, call_weight); + } +} diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs new file mode 100644 index 00000000000..9d38c9723d7 --- /dev/null +++ b/modules/grandpa/src/lib.rs @@ -0,0 +1,1423 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate GRANDPA Pallet +//! +//! This pallet is an on-chain GRANDPA light client for Substrate based chains. +//! +//! This pallet achieves this by trustlessly verifying GRANDPA finality proofs on-chain. Once +//! verified, finalized headers are stored in the pallet, thereby creating a sparse header chain. +//! This sparse header chain can be used as a source of truth for other higher-level applications. +//! +//! The pallet is responsible for tracking GRANDPA validator set hand-offs. We only import headers +//! with justifications signed by the current validator set we know of. The header is inspected for +//! a `ScheduledChanges` digest item, which is then used to update to next validator set. +//! +//! Since this pallet only tracks finalized headers it does not deal with forks. Forks can only +//! occur if the GRANDPA validator set on the bridged chain is either colluding or there is a severe +//! bug causing resulting in an equivocation. Such events are outside the scope of this pallet. +//! Shall the fork occur on the bridged chain governance intervention will be required to +//! re-initialize the bridge and track the right fork. + +#![cfg_attr(not(feature = "std"), no_std)] +// Runtime-generated enums +#![allow(clippy::large_enum_variant)] + +pub use storage_types::StoredAuthoritySet; + +use bp_header_chain::{ + justification::GrandpaJustification, ChainWithGrandpa, HeaderChain, InitializationData, + StoredHeaderData, StoredHeaderDataBuilder, +}; +use bp_runtime::{BlockNumberOf, HashOf, HasherOf, HeaderId, HeaderOf, OwnedBridgeModule}; +use finality_grandpa::voter_set::VoterSet; +use frame_support::{dispatch::PostDispatchInfo, ensure}; +use sp_consensus_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID}; +use sp_runtime::{ + traits::{Header as HeaderT, Zero}, + SaturatedConversion, +}; +use sp_std::{boxed::Box, convert::TryInto}; + +mod call_ext; +#[cfg(test)] +mod mock; +mod storage_types; + +/// Module, containing weights for this pallet. +pub mod weights; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + +// Re-export in crate namespace for `construct_runtime!` +pub use call_ext::*; +pub use pallet::*; +pub use weights::WeightInfo; + +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-grandpa"; + +/// Bridged chain from the pallet configuration. +pub type BridgedChain = >::BridgedChain; +/// Block number of the bridged chain. +pub type BridgedBlockNumber = BlockNumberOf<>::BridgedChain>; +/// Block hash of the bridged chain. +pub type BridgedBlockHash = HashOf<>::BridgedChain>; +/// Block id of the bridged chain. +pub type BridgedBlockId = HeaderId, BridgedBlockNumber>; +/// Hasher of the bridged chain. +pub type BridgedBlockHasher = HasherOf<>::BridgedChain>; +/// Header of the bridged chain. +pub type BridgedHeader = HeaderOf<>::BridgedChain>; +/// Header data of the bridged chain that is stored at this chain by this pallet. +pub type BridgedStoredHeaderData = + StoredHeaderData, BridgedBlockHash>; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use bp_runtime::BasicOperatingMode; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + + /// The chain we are bridging to here. + type BridgedChain: ChainWithGrandpa; + + /// Maximal number of "free" mandatory header transactions per block. + /// + /// To be able to track the bridged chain, the pallet requires all headers that are + /// changing GRANDPA authorities set at the bridged chain (we call them mandatory). + /// So it is a common good deed to submit mandatory headers to the pallet. However, if the + /// bridged chain gets compromised, its validators may generate as many mandatory headers + /// as they want. And they may fill the whole block (at this chain) for free. This constants + /// limits number of calls that we may refund in a single block. All calls above this + /// limit are accepted, but are not refunded. + #[pallet::constant] + type MaxFreeMandatoryHeadersPerBlock: Get; + + /// Maximal number of finalized headers to keep in the storage. + /// + /// The setting is there to prevent growing the on-chain state indefinitely. Note + /// the setting does not relate to block numbers - we will simply keep as much items + /// in the storage, so it doesn't guarantee any fixed timeframe for finality headers. + /// + /// Incautious change of this constant may lead to orphan entries in the runtime storage. + #[pallet::constant] + type HeadersToKeep: Get; + + /// Weights gathered through benchmarking. + type WeightInfo: WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet { + fn on_initialize(_n: BlockNumberFor) -> Weight { + FreeMandatoryHeadersRemaining::::put(T::MaxFreeMandatoryHeadersPerBlock::get()); + Weight::zero() + } + + fn on_finalize(_n: BlockNumberFor) { + FreeMandatoryHeadersRemaining::::kill(); + } + } + + impl, I: 'static> OwnedBridgeModule for Pallet { + const LOG_TARGET: &'static str = LOG_TARGET; + type OwnerStorage = PalletOwner; + type OperatingMode = BasicOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + } + + #[pallet::call] + impl, I: 'static> Pallet { + /// Verify a target header is finalized according to the given finality proof. + /// + /// It will use the underlying storage pallet to fetch information about the current + /// authorities and best finalized header in order to verify that the header is finalized. + /// + /// If successful in verification, it will write the target header to the underlying storage + /// pallet. + /// + /// The call fails if: + /// + /// - the pallet is halted; + /// + /// - the pallet knows better header than the `finality_target`; + /// + /// - verification is not optimized or invalid; + /// + /// - header contains forced authorities set change or change with non-zero delay. + #[pallet::call_index(0)] + #[pallet::weight(::submit_finality_proof( + justification.commit.precommits.len().saturated_into(), + justification.votes_ancestries.len().saturated_into(), + ))] + pub fn submit_finality_proof( + _origin: OriginFor, + finality_target: Box>, + justification: GrandpaJustification>, + ) -> DispatchResultWithPostInfo { + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + + let (hash, number) = (finality_target.hash(), *finality_target.number()); + log::trace!( + target: LOG_TARGET, + "Going to try and finalize header {:?}", + finality_target + ); + + SubmitFinalityProofHelper::::check_obsolete(number)?; + + let authority_set = >::get(); + let unused_proof_size = authority_set.unused_proof_size(); + let set_id = authority_set.set_id; + verify_justification::(&justification, hash, number, authority_set.into())?; + + let is_authorities_change_enacted = + try_enact_authority_change::(&finality_target, set_id)?; + let may_refund_call_fee = is_authorities_change_enacted && + // if we have seen too many mandatory headers in this block, we don't want to refund + Self::free_mandatory_headers_remaining() > 0 && + // if arguments out of expected bounds, we don't want to refund + submit_finality_proof_info_from_args::(&finality_target, &justification) + .fits_limits(); + if may_refund_call_fee { + FreeMandatoryHeadersRemaining::::mutate(|count| { + *count = count.saturating_sub(1) + }); + } + insert_header::(*finality_target, hash); + log::info!( + target: LOG_TARGET, + "Successfully imported finalized header with hash {:?}!", + hash + ); + + // mandatory header is a header that changes authorities set. The pallet can't go + // further without importing this header. So every bridge MUST import mandatory headers. + // + // We don't want to charge extra costs for mandatory operations. So relayer is not + // paying fee for mandatory headers import transactions. + // + // If size/weight of the call is exceeds our estimated limits, the relayer still needs + // to pay for the transaction. + let pays_fee = if may_refund_call_fee { Pays::No } else { Pays::Yes }; + + // the proof size component of the call weight assumes that there are + // `MaxBridgedAuthorities` in the `CurrentAuthoritySet` (we use `MaxEncodedLen` + // estimation). But if their number is lower, then we may "refund" some `proof_size`, + // making proof smaller and leaving block space to other useful transactions + let pre_dispatch_weight = T::WeightInfo::submit_finality_proof( + justification.commit.precommits.len().saturated_into(), + justification.votes_ancestries.len().saturated_into(), + ); + let actual_weight = pre_dispatch_weight + .set_proof_size(pre_dispatch_weight.proof_size().saturating_sub(unused_proof_size)); + + Self::deposit_event(Event::UpdatedBestFinalizedHeader { number, hash }); + + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee }) + } + + /// Bootstrap the bridge pallet with an initial header and authority set from which to sync. + /// + /// The initial configuration provided does not need to be the genesis header of the bridged + /// chain, it can be any arbitrary header. You can also provide the next scheduled set + /// change if it is already know. + /// + /// This function is only allowed to be called from a trusted origin and writes to storage + /// with practically no checks in terms of the validity of the data. It is important that + /// you ensure that valid data is being passed in. + #[pallet::call_index(1)] + #[pallet::weight((T::DbWeight::get().reads_writes(2, 5), DispatchClass::Operational))] + pub fn initialize( + origin: OriginFor, + init_data: super::InitializationData>, + ) -> DispatchResultWithPostInfo { + Self::ensure_owner_or_root(origin)?; + + let init_allowed = !>::exists(); + ensure!(init_allowed, >::AlreadyInitialized); + initialize_bridge::(init_data.clone())?; + + log::info!( + target: LOG_TARGET, + "Pallet has been initialized with the following parameters: {:?}", + init_data + ); + + Ok(().into()) + } + + /// Change `PalletOwner`. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(2)] + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { + >::set_owner(origin, new_owner) + } + + /// Halt or resume all pallet operations. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(3)] + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_operating_mode( + origin: OriginFor, + operating_mode: BasicOperatingMode, + ) -> DispatchResult { + >::set_operating_mode(origin, operating_mode) + } + } + + /// Number mandatory headers that we may accept in the current block for free (returning + /// `Pays::No`). + /// + /// If the `FreeMandatoryHeadersRemaining` hits zero, all following mandatory headers in the + /// current block are accepted with fee (`Pays::Yes` is returned). + /// + /// The `FreeMandatoryHeadersRemaining` is an ephemeral value that is set to + /// `MaxFreeMandatoryHeadersPerBlock` at each block initialization and is killed on block + /// finalization. So it never ends up in the storage trie. + #[pallet::storage] + #[pallet::whitelist_storage] + #[pallet::getter(fn free_mandatory_headers_remaining)] + pub(super) type FreeMandatoryHeadersRemaining, I: 'static = ()> = + StorageValue<_, u32, ValueQuery>; + + /// Hash of the header used to bootstrap the pallet. + #[pallet::storage] + pub(super) type InitialHash, I: 'static = ()> = + StorageValue<_, BridgedBlockHash, ValueQuery>; + + /// Hash of the best finalized header. + #[pallet::storage] + #[pallet::getter(fn best_finalized)] + pub type BestFinalized, I: 'static = ()> = + StorageValue<_, BridgedBlockId, OptionQuery>; + + /// A ring buffer of imported hashes. Ordered by the insertion time. + #[pallet::storage] + pub(super) type ImportedHashes, I: 'static = ()> = StorageMap< + Hasher = Identity, + Key = u32, + Value = BridgedBlockHash, + QueryKind = OptionQuery, + OnEmpty = GetDefault, + MaxValues = MaybeHeadersToKeep, + >; + + /// Current ring buffer position. + #[pallet::storage] + pub(super) type ImportedHashesPointer, I: 'static = ()> = + StorageValue<_, u32, ValueQuery>; + + /// Relevant fields of imported headers. + #[pallet::storage] + pub type ImportedHeaders, I: 'static = ()> = StorageMap< + Hasher = Identity, + Key = BridgedBlockHash, + Value = BridgedStoredHeaderData, + QueryKind = OptionQuery, + OnEmpty = GetDefault, + MaxValues = MaybeHeadersToKeep, + >; + + /// The current GRANDPA Authority set. + #[pallet::storage] + pub type CurrentAuthoritySet, I: 'static = ()> = + StorageValue<_, StoredAuthoritySet, ValueQuery>; + + /// Optional pallet owner. + /// + /// Pallet owner has a right to halt all pallet operations and then resume it. If it is + /// `None`, then there are no direct ways to halt/resume pallet operations, but other + /// runtime methods may still be used to do that (i.e. democracy::referendum to update halt + /// flag directly or call the `halt_operations`). + #[pallet::storage] + pub type PalletOwner, I: 'static = ()> = + StorageValue<_, T::AccountId, OptionQuery>; + + /// The current operating mode of the pallet. + /// + /// Depending on the mode either all, or no transactions will be allowed. + #[pallet::storage] + pub type PalletOperatingMode, I: 'static = ()> = + StorageValue<_, BasicOperatingMode, ValueQuery>; + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + /// Optional module owner account. + pub owner: Option, + /// Optional module initialization data. + pub init_data: Option>>, + } + + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { owner: None, init_data: None } + } + } + + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + if let Some(ref owner) = self.owner { + >::put(owner); + } + + if let Some(init_data) = self.init_data.clone() { + initialize_bridge::(init_data).expect("genesis config is correct; qed"); + } else { + // Since the bridge hasn't been initialized we shouldn't allow anyone to perform + // transactions. + >::put(BasicOperatingMode::Halted); + } + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// Best finalized chain header has been updated to the header with given number and hash. + UpdatedBestFinalizedHeader { + number: BridgedBlockNumber, + hash: BridgedBlockHash, + }, + } + + #[pallet::error] + pub enum Error { + /// The given justification is invalid for the given header. + InvalidJustification, + /// The authority set from the underlying header chain is invalid. + InvalidAuthoritySet, + /// The header being imported is older than the best finalized header known to the pallet. + OldHeader, + /// The scheduled authority set change found in the header is unsupported by the pallet. + /// + /// This is the case for non-standard (e.g forced) authority set changes. + UnsupportedScheduledChange, + /// The pallet is not yet initialized. + NotInitialized, + /// The pallet has already been initialized. + AlreadyInitialized, + /// Too many authorities in the set. + TooManyAuthoritiesInSet, + /// Error generated by the `OwnedBridgeModule` trait. + BridgeModule(bp_runtime::OwnedBridgeModuleError), + } + + /// Check the given header for a GRANDPA scheduled authority set change. If a change + /// is found it will be enacted immediately. + /// + /// This function does not support forced changes, or scheduled changes with delays + /// since these types of changes are indicative of abnormal behavior from GRANDPA. + /// + /// Returned value will indicate if a change was enacted or not. + pub(crate) fn try_enact_authority_change, I: 'static>( + header: &BridgedHeader, + current_set_id: sp_consensus_grandpa::SetId, + ) -> Result { + let mut change_enacted = false; + + // We don't support forced changes - at that point governance intervention is required. + ensure!( + super::find_forced_change(header).is_none(), + >::UnsupportedScheduledChange + ); + + if let Some(change) = super::find_scheduled_change(header) { + // GRANDPA only includes a `delay` for forced changes, so this isn't valid. + ensure!(change.delay == Zero::zero(), >::UnsupportedScheduledChange); + + // TODO [#788]: Stop manually increasing the `set_id` here. + let next_authorities = StoredAuthoritySet:: { + authorities: change + .next_authorities + .try_into() + .map_err(|_| Error::::TooManyAuthoritiesInSet)?, + set_id: current_set_id + 1, + }; + + // Since our header schedules a change and we know the delay is 0, it must also enact + // the change. + >::put(&next_authorities); + change_enacted = true; + + log::info!( + target: LOG_TARGET, + "Transitioned from authority set {} to {}! New authorities are: {:?}", + current_set_id, + current_set_id + 1, + next_authorities, + ); + }; + + Ok(change_enacted) + } + + /// Verify a GRANDPA justification (finality proof) for a given header. + /// + /// Will use the GRANDPA current authorities known to the pallet. + /// + /// If successful it returns the decoded GRANDPA justification so we can refund any weight which + /// was overcharged in the initial call. + pub(crate) fn verify_justification, I: 'static>( + justification: &GrandpaJustification>, + hash: BridgedBlockHash, + number: BridgedBlockNumber, + authority_set: bp_header_chain::AuthoritySet, + ) -> Result<(), sp_runtime::DispatchError> { + use bp_header_chain::justification::verify_justification; + + let voter_set = + VoterSet::new(authority_set.authorities).ok_or(>::InvalidAuthoritySet)?; + let set_id = authority_set.set_id; + + Ok(verify_justification::>( + (hash, number), + set_id, + &voter_set, + justification, + ) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Received invalid justification for {:?}: {:?}", + hash, + e, + ); + >::InvalidJustification + })?) + } + + /// Import a previously verified header to the storage. + /// + /// Note this function solely takes care of updating the storage and pruning old entries, + /// but does not verify the validity of such import. + pub(crate) fn insert_header, I: 'static>( + header: BridgedHeader, + hash: BridgedBlockHash, + ) { + let index = >::get(); + let pruning = >::try_get(index); + >::put(HeaderId(*header.number(), hash)); + >::insert(hash, header.build()); + >::insert(index, hash); + + // Update ring buffer pointer and remove old header. + >::put((index + 1) % T::HeadersToKeep::get()); + if let Ok(hash) = pruning { + log::debug!(target: LOG_TARGET, "Pruning old header: {:?}.", hash); + >::remove(hash); + } + } + + /// Since this writes to storage with no real checks this should only be used in functions that + /// were called by a trusted origin. + pub(crate) fn initialize_bridge, I: 'static>( + init_params: super::InitializationData>, + ) -> Result<(), Error> { + let super::InitializationData { header, authority_list, set_id, operating_mode } = + init_params; + let authority_set_length = authority_list.len(); + let authority_set = StoredAuthoritySet::::try_new(authority_list, set_id) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Failed to initialize bridge. Number of authorities in the set {} is larger than the configured value {}", + authority_set_length, + T::BridgedChain::MAX_AUTHORITIES_COUNT, + ); + + e + })?; + let initial_hash = header.hash(); + + >::put(initial_hash); + >::put(0); + insert_header::(*header, initial_hash); + + >::put(authority_set); + + >::put(operating_mode); + + Ok(()) + } + + /// Adapter for using `Config::HeadersToKeep` as `MaxValues` bound in our storage maps. + pub struct MaybeHeadersToKeep(PhantomData<(T, I)>); + + // this implementation is required to use the struct as `MaxValues` + impl, I: 'static> Get> for MaybeHeadersToKeep { + fn get() -> Option { + Some(T::HeadersToKeep::get()) + } + } + + /// Initialize pallet so that it is ready for inserting new header. + /// + /// The function makes sure that the new insertion will cause the pruning of some old header. + /// + /// Returns parent header for the new header. + #[cfg(feature = "runtime-benchmarks")] + pub(crate) fn bootstrap_bridge, I: 'static>( + init_params: super::InitializationData>, + ) -> BridgedHeader { + let start_header = init_params.header.clone(); + initialize_bridge::(init_params).expect("benchmarks are correct"); + + // the most obvious way to cause pruning during next insertion would be to insert + // `HeadersToKeep` headers. But it'll make our benchmarks slow. So we will just play with + // our pruning ring-buffer. + assert_eq!(ImportedHashesPointer::::get(), 1); + ImportedHashesPointer::::put(0); + + *start_header + } +} + +impl, I: 'static> Pallet { + /// Get the best finalized block number. + pub fn best_finalized_number() -> Option> { + BestFinalized::::get().map(|id| id.number()) + } +} + +/// Bridge GRANDPA pallet as header chain. +pub type GrandpaChainHeaders = Pallet; + +impl, I: 'static> HeaderChain> for GrandpaChainHeaders { + fn finalized_header_state_root( + header_hash: HashOf>, + ) -> Option>> { + ImportedHeaders::::get(header_hash).map(|h| h.state_root) + } +} + +pub(crate) fn find_scheduled_change( + header: &H, +) -> Option> { + use sp_runtime::generic::OpaqueDigestItemId; + + let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); + + let filter_log = |log: ConsensusLog| match log { + ConsensusLog::ScheduledChange(change) => Some(change), + _ => None, + }; + + // find the first consensus digest with the right ID which converts to + // the right kind of consensus log. + header.digest().convert_first(|l| l.try_to(id).and_then(filter_log)) +} + +/// Checks the given header for a consensus digest signaling a **forced** scheduled change and +/// extracts it. +pub(crate) fn find_forced_change( + header: &H, +) -> Option<(H::Number, sp_consensus_grandpa::ScheduledChange)> { + use sp_runtime::generic::OpaqueDigestItemId; + + let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); + + let filter_log = |log: ConsensusLog| match log { + ConsensusLog::ForcedChange(delay, change) => Some((delay, change)), + _ => None, + }; + + // find the first consensus digest with the right ID which converts to + // the right kind of consensus log. + header.digest().convert_first(|l| l.try_to(id).and_then(filter_log)) +} + +/// (Re)initialize bridge with given header for using it in `pallet-bridge-messages` benchmarks. +#[cfg(feature = "runtime-benchmarks")] +pub fn initialize_for_benchmarks, I: 'static>(header: BridgedHeader) { + initialize_bridge::(InitializationData { + header: Box::new(header), + authority_list: sp_std::vec::Vec::new(), /* we don't verify any proofs in external + * benchmarks */ + set_id: 0, + operating_mode: bp_runtime::BasicOperatingMode::Normal, + }) + .expect("only used from benchmarks; benchmarks are correct; qed"); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{ + run_test, test_header, RuntimeEvent as TestEvent, RuntimeOrigin, System, TestBridgedChain, + TestHeader, TestNumber, TestRuntime, MAX_BRIDGED_AUTHORITIES, + }; + use bp_header_chain::BridgeGrandpaCall; + use bp_runtime::BasicOperatingMode; + use bp_test_utils::{ + authority_list, generate_owned_bridge_module_tests, make_default_justification, + make_justification_for_header, JustificationGeneratorParams, ALICE, BOB, + }; + use codec::Encode; + use frame_support::{ + assert_err, assert_noop, assert_ok, + dispatch::{Pays, PostDispatchInfo}, + storage::generator::StorageValue, + }; + use frame_system::{EventRecord, Phase}; + use sp_core::Get; + use sp_runtime::{Digest, DigestItem, DispatchError}; + + fn initialize_substrate_bridge() { + System::set_block_number(1); + System::reset_events(); + + assert_ok!(init_with_origin(RuntimeOrigin::root())); + } + + fn init_with_origin( + origin: RuntimeOrigin, + ) -> Result< + InitializationData, + sp_runtime::DispatchErrorWithPostInfo, + > { + let genesis = test_header(0); + + let init_data = InitializationData { + header: Box::new(genesis), + authority_list: authority_list(), + set_id: 1, + operating_mode: BasicOperatingMode::Normal, + }; + + Pallet::::initialize(origin, init_data.clone()).map(|_| init_data) + } + + fn submit_finality_proof(header: u8) -> frame_support::dispatch::DispatchResultWithPostInfo { + let header = test_header(header.into()); + let justification = make_default_justification(&header); + + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification, + ) + } + + fn submit_finality_proof_with_set_id( + header: u8, + set_id: u64, + ) -> frame_support::dispatch::DispatchResultWithPostInfo { + let header = test_header(header.into()); + let justification = make_justification_for_header(JustificationGeneratorParams { + header: header.clone(), + set_id, + ..Default::default() + }); + + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification, + ) + } + + fn submit_mandatory_finality_proof( + number: u8, + set_id: u64, + ) -> frame_support::dispatch::DispatchResultWithPostInfo { + let mut header = test_header(number.into()); + // to ease tests that are using `submit_mandatory_finality_proof`, we'll be using the + // same set for all sessions + let consensus_log = + ConsensusLog::::ScheduledChange(sp_consensus_grandpa::ScheduledChange { + next_authorities: authority_list(), + delay: 0, + }); + header.digest = + Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] }; + let justification = make_justification_for_header(JustificationGeneratorParams { + header: header.clone(), + set_id, + ..Default::default() + }); + + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification, + ) + } + + fn next_block() { + use frame_support::traits::OnInitialize; + + let current_number = frame_system::Pallet::::block_number(); + frame_system::Pallet::::set_block_number(current_number + 1); + let _ = Pallet::::on_initialize(current_number); + } + + fn change_log(delay: u64) -> Digest { + let consensus_log = + ConsensusLog::::ScheduledChange(sp_consensus_grandpa::ScheduledChange { + next_authorities: vec![(ALICE.into(), 1), (BOB.into(), 1)], + delay, + }); + + Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] } + } + + fn forced_change_log(delay: u64) -> Digest { + let consensus_log = ConsensusLog::::ForcedChange( + delay, + sp_consensus_grandpa::ScheduledChange { + next_authorities: vec![(ALICE.into(), 1), (BOB.into(), 1)], + delay, + }, + ); + + Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] } + } + + fn many_authorities_log() -> Digest { + let consensus_log = + ConsensusLog::::ScheduledChange(sp_consensus_grandpa::ScheduledChange { + next_authorities: std::iter::repeat((ALICE.into(), 1)) + .take(MAX_BRIDGED_AUTHORITIES as usize + 1) + .collect(), + delay: 0, + }); + + Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] } + } + + #[test] + fn init_root_or_owner_origin_can_initialize_pallet() { + run_test(|| { + assert_noop!(init_with_origin(RuntimeOrigin::signed(1)), DispatchError::BadOrigin); + assert_ok!(init_with_origin(RuntimeOrigin::root())); + + // Reset storage so we can initialize the pallet again + BestFinalized::::kill(); + PalletOwner::::put(2); + assert_ok!(init_with_origin(RuntimeOrigin::signed(2))); + }) + } + + #[test] + fn init_storage_entries_are_correctly_initialized() { + run_test(|| { + assert_eq!(BestFinalized::::get(), None,); + assert_eq!(Pallet::::best_finalized(), None); + + let init_data = init_with_origin(RuntimeOrigin::root()).unwrap(); + + assert!(>::contains_key(init_data.header.hash())); + assert_eq!(BestFinalized::::get().unwrap().1, init_data.header.hash()); + assert_eq!( + CurrentAuthoritySet::::get().authorities, + init_data.authority_list + ); + assert_eq!(PalletOperatingMode::::get(), BasicOperatingMode::Normal); + }) + } + + #[test] + fn init_can_only_initialize_pallet_once() { + run_test(|| { + initialize_substrate_bridge(); + assert_noop!( + init_with_origin(RuntimeOrigin::root()), + >::AlreadyInitialized + ); + }) + } + + #[test] + fn init_fails_if_there_are_too_many_authorities_in_the_set() { + run_test(|| { + let genesis = test_header(0); + let init_data = InitializationData { + header: Box::new(genesis), + authority_list: std::iter::repeat(authority_list().remove(0)) + .take(MAX_BRIDGED_AUTHORITIES as usize + 1) + .collect(), + set_id: 1, + operating_mode: BasicOperatingMode::Normal, + }; + + assert_noop!( + Pallet::::initialize(RuntimeOrigin::root(), init_data), + Error::::TooManyAuthoritiesInSet, + ); + }); + } + + #[test] + fn pallet_rejects_transactions_if_halted() { + run_test(|| { + initialize_substrate_bridge(); + + assert_ok!(Pallet::::set_operating_mode( + RuntimeOrigin::root(), + BasicOperatingMode::Halted + )); + assert_noop!( + submit_finality_proof(1), + Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted) + ); + + assert_ok!(Pallet::::set_operating_mode( + RuntimeOrigin::root(), + BasicOperatingMode::Normal + )); + assert_ok!(submit_finality_proof(1)); + }) + } + + #[test] + fn pallet_rejects_header_if_not_initialized_yet() { + run_test(|| { + assert_noop!(submit_finality_proof(1), Error::::NotInitialized); + }); + } + + #[test] + fn succesfully_imports_header_with_valid_finality() { + run_test(|| { + initialize_substrate_bridge(); + + let header_number = 1; + let header = test_header(header_number.into()); + let justification = make_default_justification(&header); + + let pre_dispatch_weight = ::WeightInfo::submit_finality_proof( + justification.commit.precommits.len().try_into().unwrap_or(u32::MAX), + justification.votes_ancestries.len().try_into().unwrap_or(u32::MAX), + ); + + let result = submit_finality_proof(header_number); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); + // our test config assumes 2048 max authorities and we are just using couple + let pre_dispatch_proof_size = pre_dispatch_weight.proof_size(); + let actual_proof_size = result.unwrap().actual_weight.unwrap().proof_size(); + assert!(actual_proof_size > 0); + assert!( + actual_proof_size < pre_dispatch_proof_size, + "Actual proof size {actual_proof_size} must be less than the pre-dispatch {pre_dispatch_proof_size}", + ); + + let header = test_header(1); + assert_eq!(>::get().unwrap().1, header.hash()); + assert!(>::contains_key(header.hash())); + + assert_eq!( + System::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Grandpa(Event::UpdatedBestFinalizedHeader { + number: *header.number(), + hash: header.hash(), + }), + topics: vec![], + }], + ); + }) + } + + #[test] + fn rejects_justification_that_skips_authority_set_transition() { + run_test(|| { + initialize_substrate_bridge(); + + let header = test_header(1); + + let params = + JustificationGeneratorParams:: { set_id: 2, ..Default::default() }; + let justification = make_justification_for_header(params); + + assert_err!( + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification, + ), + >::InvalidJustification + ); + }) + } + + #[test] + fn does_not_import_header_with_invalid_finality_proof() { + run_test(|| { + initialize_substrate_bridge(); + + let header = test_header(1); + let mut justification = make_default_justification(&header); + justification.round = 42; + + assert_err!( + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification, + ), + >::InvalidJustification + ); + }) + } + + #[test] + fn disallows_invalid_authority_set() { + run_test(|| { + let genesis = test_header(0); + + let invalid_authority_list = vec![(ALICE.into(), u64::MAX), (BOB.into(), u64::MAX)]; + let init_data = InitializationData { + header: Box::new(genesis), + authority_list: invalid_authority_list, + set_id: 1, + operating_mode: BasicOperatingMode::Normal, + }; + + assert_ok!(Pallet::::initialize(RuntimeOrigin::root(), init_data)); + + let header = test_header(1); + let justification = make_default_justification(&header); + + assert_err!( + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification, + ), + >::InvalidAuthoritySet + ); + }) + } + + #[test] + fn importing_header_ensures_that_chain_is_extended() { + run_test(|| { + initialize_substrate_bridge(); + + assert_ok!(submit_finality_proof(4)); + assert_err!(submit_finality_proof(3), Error::::OldHeader); + assert_ok!(submit_finality_proof(5)); + }) + } + + #[test] + fn importing_header_enacts_new_authority_set() { + run_test(|| { + initialize_substrate_bridge(); + + let next_set_id = 2; + let next_authorities = vec![(ALICE.into(), 1), (BOB.into(), 1)]; + + // Need to update the header digest to indicate that our header signals an authority set + // change. The change will be enacted when we import our header. + let mut header = test_header(2); + header.digest = change_log(0); + + // Create a valid justification for the header + let justification = make_default_justification(&header); + + // Let's import our test header + let result = Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header.clone()), + justification, + ); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::No); + + // Make sure that our header is the best finalized + assert_eq!(>::get().unwrap().1, header.hash()); + assert!(>::contains_key(header.hash())); + + // Make sure that the authority set actually changed upon importing our header + assert_eq!( + >::get(), + StoredAuthoritySet::::try_new(next_authorities, next_set_id) + .unwrap(), + ); + }) + } + + #[test] + fn relayer_pays_tx_fee_when_submitting_huge_mandatory_header() { + run_test(|| { + initialize_substrate_bridge(); + + // let's prepare a huge authorities change header, which is definitely above size limits + let mut header = test_header(2); + header.digest = change_log(0); + header.digest.push(DigestItem::Other(vec![42u8; 1024 * 1024])); + let justification = make_default_justification(&header); + + // without large digest item ^^^ the relayer would have paid zero transaction fee + // (`Pays::No`) + let result = Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header.clone()), + justification, + ); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); + + // Make sure that our header is the best finalized + assert_eq!(>::get().unwrap().1, header.hash()); + assert!(>::contains_key(header.hash())); + }) + } + + #[test] + fn relayer_pays_tx_fee_when_submitting_justification_with_long_ancestry_votes() { + run_test(|| { + initialize_substrate_bridge(); + + // let's prepare a huge authorities change header, which is definitely above weight + // limits + let mut header = test_header(2); + header.digest = change_log(0); + let justification = make_justification_for_header(JustificationGeneratorParams { + header: header.clone(), + ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY + 1, + ..Default::default() + }); + + // without many headers in votes ancestries ^^^ the relayer would have paid zero + // transaction fee (`Pays::No`) + let result = Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header.clone()), + justification, + ); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); + + // Make sure that our header is the best finalized + assert_eq!(>::get().unwrap().1, header.hash()); + assert!(>::contains_key(header.hash())); + }) + } + + #[test] + fn importing_header_rejects_header_with_scheduled_change_delay() { + run_test(|| { + initialize_substrate_bridge(); + + // Need to update the header digest to indicate that our header signals an authority set + // change. However, the change doesn't happen until the next block. + let mut header = test_header(2); + header.digest = change_log(1); + + // Create a valid justification for the header + let justification = make_default_justification(&header); + + // Should not be allowed to import this header + assert_err!( + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification + ), + >::UnsupportedScheduledChange + ); + }) + } + + #[test] + fn importing_header_rejects_header_with_forced_changes() { + run_test(|| { + initialize_substrate_bridge(); + + // Need to update the header digest to indicate that it signals a forced authority set + // change. + let mut header = test_header(2); + header.digest = forced_change_log(0); + + // Create a valid justification for the header + let justification = make_default_justification(&header); + + // Should not be allowed to import this header + assert_err!( + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification + ), + >::UnsupportedScheduledChange + ); + }) + } + + #[test] + fn importing_header_rejects_header_with_too_many_authorities() { + run_test(|| { + initialize_substrate_bridge(); + + // Need to update the header digest to indicate that our header signals an authority set + // change. However, the change doesn't happen until the next block. + let mut header = test_header(2); + header.digest = many_authorities_log(); + + // Create a valid justification for the header + let justification = make_default_justification(&header); + + // Should not be allowed to import this header + assert_err!( + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification + ), + >::TooManyAuthoritiesInSet + ); + }); + } + + #[test] + fn parse_finalized_storage_proof_rejects_proof_on_unknown_header() { + run_test(|| { + assert_noop!( + Pallet::::parse_finalized_storage_proof( + Default::default(), + vec![], + |_| (), + ), + bp_header_chain::HeaderChainError::UnknownHeader, + ); + }); + } + + #[test] + fn parse_finalized_storage_accepts_valid_proof() { + run_test(|| { + let (state_root, storage_proof) = bp_runtime::craft_valid_storage_proof(); + + let mut header = test_header(2); + header.set_state_root(state_root); + + let hash = header.hash(); + >::put(HeaderId(2, hash)); + >::insert(hash, header.build()); + + assert_ok!( + Pallet::::parse_finalized_storage_proof(hash, storage_proof, |_| (),), + (), + ); + }); + } + + #[test] + fn rate_limiter_disallows_free_imports_once_limit_is_hit_in_single_block() { + run_test(|| { + initialize_substrate_bridge(); + + let result = submit_mandatory_finality_proof(1, 1); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(2, 2); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(3, 3); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); + }) + } + + #[test] + fn rate_limiter_invalid_requests_do_not_count_towards_request_count() { + run_test(|| { + let submit_invalid_request = || { + let mut header = test_header(1); + header.digest = change_log(0); + let mut invalid_justification = make_default_justification(&header); + invalid_justification.round = 42; + + Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + invalid_justification, + ) + }; + + initialize_substrate_bridge(); + + for _ in 0..::MaxFreeMandatoryHeadersPerBlock::get() + 1 { + assert_err!(submit_invalid_request(), >::InvalidJustification); + } + + // Can still submit free mandatory headers afterwards + let result = submit_mandatory_finality_proof(1, 1); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(2, 2); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(3, 3); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); + }) + } + + #[test] + fn rate_limiter_allows_request_after_new_block_has_started() { + run_test(|| { + initialize_substrate_bridge(); + + let result = submit_mandatory_finality_proof(1, 1); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(2, 2); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(3, 3); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); + + next_block(); + + let result = submit_mandatory_finality_proof(4, 4); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(5, 5); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_mandatory_finality_proof(6, 6); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); + }) + } + + #[test] + fn rate_limiter_ignores_non_mandatory_headers() { + run_test(|| { + initialize_substrate_bridge(); + + let result = submit_finality_proof(1); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); + + let result = submit_mandatory_finality_proof(2, 1); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_finality_proof_with_set_id(3, 2); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); + + let result = submit_mandatory_finality_proof(4, 2); + assert_eq!(result.expect("call failed").pays_fee, Pays::No); + + let result = submit_finality_proof_with_set_id(5, 3); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); + + let result = submit_mandatory_finality_proof(6, 3); + assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); + }) + } + + #[test] + fn should_prune_headers_over_headers_to_keep_parameter() { + run_test(|| { + initialize_substrate_bridge(); + assert_ok!(submit_finality_proof(1)); + let first_header_hash = Pallet::::best_finalized().unwrap().hash(); + next_block(); + + assert_ok!(submit_finality_proof(2)); + next_block(); + assert_ok!(submit_finality_proof(3)); + next_block(); + assert_ok!(submit_finality_proof(4)); + next_block(); + assert_ok!(submit_finality_proof(5)); + next_block(); + + assert_ok!(submit_finality_proof(6)); + + assert!( + !ImportedHeaders::::contains_key(first_header_hash), + "First header should be pruned.", + ); + }) + } + + #[test] + fn storage_keys_computed_properly() { + assert_eq!( + PalletOperatingMode::::storage_value_final_key().to_vec(), + bp_header_chain::storage_keys::pallet_operating_mode_key("Grandpa").0, + ); + + assert_eq!( + CurrentAuthoritySet::::storage_value_final_key().to_vec(), + bp_header_chain::storage_keys::current_authority_set_key("Grandpa").0, + ); + + assert_eq!( + BestFinalized::::storage_value_final_key().to_vec(), + bp_header_chain::storage_keys::best_finalized_key("Grandpa").0, + ); + } + + #[test] + fn test_bridge_grandpa_call_is_correctly_defined() { + let header = test_header(0); + let init_data = InitializationData { + header: Box::new(header.clone()), + authority_list: authority_list(), + set_id: 1, + operating_mode: BasicOperatingMode::Normal, + }; + let justification = make_default_justification(&header); + + let direct_initialize_call = + Call::::initialize { init_data: init_data.clone() }; + let indirect_initialize_call = BridgeGrandpaCall::::initialize { init_data }; + assert_eq!(direct_initialize_call.encode(), indirect_initialize_call.encode()); + + let direct_submit_finality_proof_call = Call::::submit_finality_proof { + finality_target: Box::new(header.clone()), + justification: justification.clone(), + }; + let indirect_submit_finality_proof_call = + BridgeGrandpaCall::::submit_finality_proof { + finality_target: Box::new(header), + justification, + }; + assert_eq!( + direct_submit_finality_proof_call.encode(), + indirect_submit_finality_proof_call.encode() + ); + } + + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); + + #[test] + fn maybe_headers_to_keep_returns_correct_value() { + assert_eq!(MaybeHeadersToKeep::::get(), Some(mock::HeadersToKeep::get())); + } +} diff --git a/modules/grandpa/src/mock.rs b/modules/grandpa/src/mock.rs new file mode 100644 index 00000000000..0ebbc0bccbb --- /dev/null +++ b/modules/grandpa/src/mock.rs @@ -0,0 +1,151 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +// From construct_runtime macro +#![allow(clippy::from_over_into)] + +use bp_header_chain::ChainWithGrandpa; +use bp_runtime::Chain; +use frame_support::{ + construct_runtime, parameter_types, + traits::{ConstU32, ConstU64, Hooks}, + weights::Weight, +}; +use sp_core::sr25519::Signature; +use sp_runtime::{ + testing::{Header, H256}, + traits::{BlakeTwo256, IdentityLookup}, + Perbill, +}; + +pub type AccountId = u64; +pub type TestHeader = crate::BridgedHeader; +pub type TestNumber = crate::BridgedBlockNumber; + +type Block = frame_system::mocking::MockBlock; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + +pub const MAX_BRIDGED_AUTHORITIES: u32 = 5; + +use crate as grandpa; + +construct_runtime! { + pub enum TestRuntime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Grandpa: grandpa::{Pallet, Call, Event}, + } +} + +parameter_types! { + pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 0); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} + +impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type DbWeight = (); + type BlockWeights = (); + type BlockLength = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +parameter_types! { + pub const MaxFreeMandatoryHeadersPerBlock: u32 = 2; + pub const HeadersToKeep: u32 = 5; + pub const SessionLength: u64 = 5; + pub const NumValidators: u32 = 5; +} + +impl grandpa::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type BridgedChain = TestBridgedChain; + type MaxFreeMandatoryHeadersPerBlock = MaxFreeMandatoryHeadersPerBlock; + type HeadersToKeep = HeadersToKeep; + type WeightInfo = (); +} + +#[derive(Debug)] +pub struct TestBridgedChain; + +impl Chain for TestBridgedChain { + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hasher = ::Hashing; + type Header = ::Header; + + type AccountId = AccountId; + type Balance = u64; + type Index = u64; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + fn max_extrinsic_weight() -> Weight { + unreachable!() + } +} + +impl ChainWithGrandpa for TestBridgedChain { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; + const MAX_AUTHORITIES_COUNT: u32 = MAX_BRIDGED_AUTHORITIES; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + const MAX_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64; +} + +/// Return test externalities to use in tests. +pub fn new_test_ext() -> sp_io::TestExternalities { + sp_io::TestExternalities::new(Default::default()) +} + +/// Return test within default test externalities context. +pub fn run_test(test: impl FnOnce() -> T) -> T { + new_test_ext().execute_with(|| { + let _ = Grandpa::on_initialize(0); + test() + }) +} + +/// Return test header with given number. +pub fn test_header(num: TestNumber) -> TestHeader { + // We wrap the call to avoid explicit type annotations in our tests + bp_test_utils::test_header(num) +} diff --git a/modules/grandpa/src/storage_types.rs b/modules/grandpa/src/storage_types.rs new file mode 100644 index 00000000000..70448286335 --- /dev/null +++ b/modules/grandpa/src/storage_types.rs @@ -0,0 +1,136 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Wrappers for public types that are implementing `MaxEncodedLen` + +use crate::{Config, Error}; + +use bp_header_chain::{AuthoritySet, ChainWithGrandpa}; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{traits::Get, BoundedVec, RuntimeDebugNoBound}; +use scale_info::TypeInfo; +use sp_consensus_grandpa::{AuthorityId, AuthorityList, AuthorityWeight, SetId}; +use sp_std::marker::PhantomData; + +/// A bounded list of Grandpa authorities with associated weights. +pub type StoredAuthorityList = + BoundedVec<(AuthorityId, AuthorityWeight), MaxBridgedAuthorities>; + +/// Adapter for using `T::BridgedChain::MAX_BRIDGED_AUTHORITIES` in `BoundedVec`. +pub struct StoredAuthorityListLimit(PhantomData<(T, I)>); + +impl, I: 'static> Get for StoredAuthorityListLimit { + fn get() -> u32 { + T::BridgedChain::MAX_AUTHORITIES_COUNT + } +} + +/// A bounded GRANDPA Authority List and ID. +#[derive(Clone, Decode, Encode, Eq, TypeInfo, MaxEncodedLen, RuntimeDebugNoBound)] +#[scale_info(skip_type_params(T, I))] +pub struct StoredAuthoritySet, I: 'static> { + /// List of GRANDPA authorities for the current round. + pub authorities: StoredAuthorityList>, + /// Monotonic identifier of the current GRANDPA authority set. + pub set_id: SetId, +} + +impl, I: 'static> StoredAuthoritySet { + /// Try to create a new bounded GRANDPA Authority Set from unbounded list. + /// + /// Returns error if number of authorities in the provided list is too large. + pub fn try_new(authorities: AuthorityList, set_id: SetId) -> Result> { + Ok(Self { + authorities: TryFrom::try_from(authorities) + .map_err(|_| Error::TooManyAuthoritiesInSet)?, + set_id, + }) + } + + /// Returns number of bytes that may be subtracted from the PoV component of + /// `submit_finality_proof` call, because the actual authorities set is smaller than the maximal + /// configured. + /// + /// Maximal authorities set size is configured by the `MaxBridgedAuthorities` constant from + /// the pallet configuration. The PoV of the call includes the size of maximal authorities + /// count. If the actual size is smaller, we may subtract extra bytes from this component. + pub fn unused_proof_size(&self) -> u64 { + // we can only safely estimate bytes that are occupied by the authority data itself. We have + // no means here to compute PoV bytes, occupied by extra trie nodes or extra bytes in the + // whole set encoding + let single_authority_max_encoded_len = + <(AuthorityId, AuthorityWeight)>::max_encoded_len() as u64; + let extra_authorities = + T::BridgedChain::MAX_AUTHORITIES_COUNT.saturating_sub(self.authorities.len() as _); + single_authority_max_encoded_len.saturating_mul(extra_authorities as u64) + } +} + +impl, I: 'static> PartialEq for StoredAuthoritySet { + fn eq(&self, other: &Self) -> bool { + self.set_id == other.set_id && self.authorities == other.authorities + } +} + +impl, I: 'static> Default for StoredAuthoritySet { + fn default() -> Self { + StoredAuthoritySet { authorities: BoundedVec::default(), set_id: 0 } + } +} + +impl, I: 'static> From> for AuthoritySet { + fn from(t: StoredAuthoritySet) -> Self { + AuthoritySet { authorities: t.authorities.into(), set_id: t.set_id } + } +} + +#[cfg(test)] +mod tests { + use crate::mock::{TestRuntime, MAX_BRIDGED_AUTHORITIES}; + use bp_test_utils::authority_list; + + type StoredAuthoritySet = super::StoredAuthoritySet; + + #[test] + fn unused_proof_size_works() { + let authority_entry = authority_list().pop().unwrap(); + + // when we have exactly `MaxBridgedAuthorities` authorities + assert_eq!( + StoredAuthoritySet::try_new( + vec![authority_entry.clone(); MAX_BRIDGED_AUTHORITIES as usize], + 0, + ) + .unwrap() + .unused_proof_size(), + 0, + ); + + // when we have less than `MaxBridgedAuthorities` authorities + assert_eq!( + StoredAuthoritySet::try_new( + vec![authority_entry; MAX_BRIDGED_AUTHORITIES as usize - 1], + 0, + ) + .unwrap() + .unused_proof_size(), + 40, + ); + + // and we can't have more than `MaxBridgedAuthorities` authorities in the bounded vec, so + // no test for this case + } +} diff --git a/modules/grandpa/src/weights.rs b/modules/grandpa/src/weights.rs new file mode 100644 index 00000000000..089aee8b569 --- /dev/null +++ b/modules/grandpa/src/weights.rs @@ -0,0 +1,167 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Autogenerated weights for pallet_bridge_grandpa +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/millau-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_bridge_grandpa +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./modules/grandpa/src/weights.rs +// --template=./.maintain/millau-weight-template.hbs + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_bridge_grandpa. +pub trait WeightInfo { + fn submit_finality_proof(p: u32, v: u32) -> Weight; +} + +/// Weights for `pallet_bridge_grandpa` that are generated using one of the Bridge testnets. +/// +/// Those weights are test only and must never be used in production. +pub struct BridgeWeight(PhantomData); +impl WeightInfo for BridgeWeight { + /// Storage: BridgeRialtoGrandpa PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa RequestCount (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa RequestCount (max_values: Some(1), max_size: Some(4), added: 499, + /// mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa BestFinalized (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa BestFinalized (max_values: Some(1), max_size: Some(36), added: + /// 531, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa CurrentAuthoritySet (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa CurrentAuthoritySet (max_values: Some(1), max_size: Some(209), + /// added: 704, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHashesPointer (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa ImportedHashesPointer (max_values: Some(1), max_size: Some(4), + /// added: 499, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa ImportedHashes (max_values: Some(14400), max_size: Some(36), + /// added: 2016, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:0 w:2) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// The range of component `p` is `[1, 4]`. + /// + /// The range of component `v` is `[50, 100]`. + fn submit_finality_proof(p: u32, v: u32) -> Weight { + // Proof Size summary in bytes: + // Measured: `394 + p * (60 ±0)` + // Estimated: `4745` + // Minimum execution time: 228_072 nanoseconds. + Weight::from_parts(57_853_228, 4745) + // Standard Error: 149_421 + .saturating_add(Weight::from_parts(36_708_702, 0).saturating_mul(p.into())) + // Standard Error: 10_625 + .saturating_add(Weight::from_parts(1_469_032, 0).saturating_mul(v.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: BridgeRialtoGrandpa PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa RequestCount (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa RequestCount (max_values: Some(1), max_size: Some(4), added: 499, + /// mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa BestFinalized (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa BestFinalized (max_values: Some(1), max_size: Some(36), added: + /// 531, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa CurrentAuthoritySet (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa CurrentAuthoritySet (max_values: Some(1), max_size: Some(209), + /// added: 704, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHashesPointer (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa ImportedHashesPointer (max_values: Some(1), max_size: Some(4), + /// added: 499, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoGrandpa ImportedHashes (max_values: Some(14400), max_size: Some(36), + /// added: 2016, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:0 w:2) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// The range of component `p` is `[1, 4]`. + /// + /// The range of component `v` is `[50, 100]`. + fn submit_finality_proof(p: u32, v: u32) -> Weight { + // Proof Size summary in bytes: + // Measured: `394 + p * (60 ±0)` + // Estimated: `4745` + // Minimum execution time: 228_072 nanoseconds. + Weight::from_parts(57_853_228, 4745) + // Standard Error: 149_421 + .saturating_add(Weight::from_parts(36_708_702, 0).saturating_mul(p.into())) + // Standard Error: 10_625 + .saturating_add(Weight::from_parts(1_469_032, 0).saturating_mul(v.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } +} diff --git a/modules/messages/Cargo.toml b/modules/messages/Cargo.toml new file mode 100644 index 00000000000..f733d62bf64 --- /dev/null +++ b/modules/messages/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "pallet-bridge-messages" +description = "Module that allows bridged chains to exchange messages using lane concept." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +log = { version = "0.4.17", default-features = false } +num-traits = { version = "0.2", default-features = false } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } + +# Bridge dependencies + +bp-messages = { path = "../../primitives/messages", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } + +# Substrate Dependencies + +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +bp-test-utils = { path = "../../primitives/test-utils" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "bp-messages/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "frame-system/std", + "frame-benchmarking/std", + "log/std", + "num-traits/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/modules/messages/README.md b/modules/messages/README.md new file mode 100644 index 00000000000..b717db6ad62 --- /dev/null +++ b/modules/messages/README.md @@ -0,0 +1,242 @@ +# Bridge Messages Pallet + +The messages pallet is used to deliver messages from source chain to target chain. Message is +(almost) opaque to the module and the final goal is to hand message to the message dispatch +mechanism. + +## Contents + +- [Overview](#overview) +- [Message Workflow](#message-workflow) +- [Integrating Message Lane Module into Runtime](#integrating-messages-module-into-runtime) +- [Non-Essential Functionality](#non-essential-functionality) +- [Weights of Module Extrinsics](#weights-of-module-extrinsics) + +## Overview + +Message lane is an unidirectional channel, where messages are sent from source chain to the target +chain. At the same time, a single instance of messages module supports both outbound lanes and +inbound lanes. So the chain where the module is deployed (this chain), may act as a source chain for +outbound messages (heading to a bridged chain) and as a target chain for inbound messages (coming +from a bridged chain). + +Messages module supports multiple message lanes. Every message lane is identified with a 4-byte +identifier. Messages sent through the lane are assigned unique (for this lane) increasing integer +value that is known as nonce ("number that can only be used once"). Messages that are sent over the +same lane are guaranteed to be delivered to the target chain in the same order they're sent from +the source chain. In other words, message with nonce `N` will be delivered right before delivering a +message with nonce `N+1`. + +Single message lane may be seen as a transport channel for single application (onchain, offchain or +mixed). At the same time the module itself never dictates any lane or message rules. In the end, it +is the runtime developer who defines what message lane and message mean for this runtime. + +In our [Kusama<>Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md) we are using lane +as a channel of communication between two parachains of different relay chains. For example, lane +`[0, 0, 0, 0]` is used for Statemint <> Statemine communications. Other lanes may be used to bridge +another parachains. + +## Message Workflow + +The pallet is not intended to be used by end users and provides no public calls to send the message. +Instead, it provides runtime-internal method that allows other pallets (or other runtime code) to queue +outbound messages. + +The message "appears" when some runtime code calls the `send_message()` method of the pallet. +The submitter specifies the lane that they're willing to use and the message itself. If some fee must +be paid for sending the message, it must be paid outside of the pallet. If a message passes all checks +(that include, for example, message size check, disabled lane check, ...), the nonce is assigned and +the message is stored in the module storage. The message is in an "undelivered" state now. + +We assume that there are external, offchain actors, called relayers, that are submitting module +related transactions to both target and source chains. The pallet itself has no assumptions about +relayers incentivization scheme, but it has some callbacks for paying rewards. See +[Integrating Messages Module into runtime](#Integrating-Messages-Module-into-runtime) +for details. + +Eventually, some relayer would notice this message in the "undelivered" state and it would decide to +deliver this message. Relayer then crafts `receive_messages_proof()` transaction (aka delivery +transaction) for the messages module instance, deployed at the target chain. Relayer provides +its account id at the source chain, the proof of message (or several messages), the number of +messages in the transaction and their cumulative dispatch weight. Once a transaction is mined, the +message is considered "delivered". + +Once a message is delivered, the relayer may want to confirm delivery back to the source chain. +There are two reasons why it would want to do that. The first is that we intentionally limit number +of "delivered", but not yet "confirmed" messages at inbound lanes +(see [What about other Constants in the Messages Module Configuration Trait](#What-about-other-Constants-in-the-Messages-Module-Configuration-Trait) for explanation). +So at some point, the target chain may stop accepting new messages until relayers confirm some of +these. The second is that if the relayer wants to be rewarded for delivery, it must prove the fact +that it has actually delivered the message. And this proof may only be generated after the delivery +transaction is mined. So relayer crafts the `receive_messages_delivery_proof()` transaction (aka +confirmation transaction) for the messages module instance, deployed at the source chain. Once +this transaction is mined, the message is considered "confirmed". + +The "confirmed" state is the final state of the message. But there's one last thing related to the +message - the fact that it is now "confirmed" and reward has been paid to the relayer (or at least +callback for this has been called), must be confirmed to the target chain. Otherwise, we may reach +the limit of "unconfirmed" messages at the target chain and it will stop accepting new messages. So +relayer sometimes includes a nonce of the latest "confirmed" message in the next +`receive_messages_proof()` transaction, proving that some messages have been confirmed. + +## Integrating Messages Module into Runtime + +As it has been said above, the messages module supports both outbound and inbound message lanes. +So if we will integrate a module in some runtime, it may act as the source chain runtime for +outbound messages and as the target chain runtime for inbound messages. In this section, we'll +sometimes refer to the chain we're currently integrating with, as "this chain" and the other +chain as "bridged chain". + +Messages module doesn't simply accept transactions that are claiming that the bridged chain has +some updated data for us. Instead of this, the module assumes that the bridged chain is able to +prove that updated data in some way. The proof is abstracted from the module and may be of any kind. +In our Substrate-to-Substrate bridge we're using runtime storage proofs. Other bridges may use +transaction proofs, Substrate header digests or anything else that may be proved. + +**IMPORTANT NOTE**: everything below in this chapter describes details of the messages module +configuration. But if you're interested in well-probed and relatively easy integration of two +Substrate-based chains, you may want to look at the +[bridge-runtime-common](../../bin/runtime-common/) crate. This crate is providing a lot of +helpers for integration, which may be directly used from within your runtime. Then if you'll decide +to change something in this scheme, get back here for detailed information. + +### General Information + +The messages module supports instances. Every module instance is supposed to bridge this chain +and some bridged chain. To bridge with another chain, using another instance is suggested (this +isn't forced anywhere in the code, though). Keep in mind, that the pallet may be used to build +virtual channels between multiple chains, as we do in our [Polkadot <> Kusama bridge](../../docs/polkadot-kusama-bridge-overview.md). +There, the pallet actually bridges only two parachains - Kusama Bridge Hub and Polkadot +Bridge Hub. However, other Kusama and Polkadot parachains are able to send (XCM) messages to their +Bridge Hubs. The messages will be delivered to the other side of the bridge and routed to the proper +destination parachain within the bridged chain consensus. + +Message submitters may track message progress by inspecting module events. When Message is accepted, +the `MessageAccepted` event is emitted. The event contains both message lane identifier and nonce that +has been assigned to the message. When a message is delivered to the target chain, the `MessagesDelivered` +event is emitted from the `receive_messages_delivery_proof()` transaction. The `MessagesDelivered` contains +the message lane identifier and inclusive range of delivered message nonces. + +The pallet provides no means to get the result of message dispatch at the target chain. If that is +required, it must be done outside of the pallet. For example, XCM messages, when dispatched, have +special instructions to send some data back to the sender. Other dispatchers may use similar +mechanism for that. +### How to plug-in Messages Module to Send Messages to the Bridged Chain? + +The `pallet_bridge_messages::Config` trait has 3 main associated types that are used to work with +outbound messages. The `pallet_bridge_messages::Config::TargetHeaderChain` defines how we see the +bridged chain as the target for our outbound messages. It must be able to check that the bridged +chain may accept our message - like that the message has size below maximal possible transaction +size of the chain and so on. And when the relayer sends us a confirmation transaction, this +implementation must be able to parse and verify the proof of messages delivery. Normally, you would +reuse the same (configurable) type on all chains that are sending messages to the same bridged +chain. + +The `pallet_bridge_messages::Config::LaneMessageVerifier` defines a single callback to verify outbound +messages. The simplest callback may just accept all messages. But in this case you'll need to answer +many questions first. Who will pay for the delivery and confirmation transaction? Are we sure that +someone will ever deliver this message to the bridged chain? Are we sure that we don't bloat our +runtime storage by accepting this message? What if the message is improperly encoded or has some +fields set to invalid values? Answering all those (and similar) questions would lead to correct +implementation. + +There's another thing to consider when implementing type for use in +`pallet_bridge_messages::Config::LaneMessageVerifier`. It is whether we treat all message lanes +identically, or they'll have different sets of verification rules? For example, you may reserve +lane#1 for messages coming from some 'wrapped-token' pallet - then you may verify in your +implementation that the origin is associated with this pallet. Lane#2 may be reserved for 'system' +messages and you may charge zero fee for such messages. You may have some rate limiting for messages +sent over the lane#3. Or you may just verify the same rules set for all outbound messages - it is +all up to the `pallet_bridge_messages::Config::LaneMessageVerifier` implementation. + +The last type is the `pallet_bridge_messages::Config::DeliveryConfirmationPayments`. When confirmation +transaction is received, we call the `pay_reward()` method, passing the range of delivered messages. +You may use the [`pallet-bridge-relayers`](../relayers/) pallet and its +[`DeliveryConfirmationPaymentsAdapter`](../relayers/src/payment_adapter.rs) adapter as a possible +implementation. It allows you to pay fixed reward for relaying the message and some of its portion +for confirming delivery. + +### I have a Messages Module in my Runtime, but I Want to Reject all Outbound Messages. What shall I do? + +You should be looking at the `bp_messages::source_chain::ForbidOutboundMessages` structure +[`bp_messages::source_chain`](../../primitives/messages/src/source_chain.rs). It implements +all required traits and will simply reject all transactions, related to outbound messages. + +### How to plug-in Messages Module to Receive Messages from the Bridged Chain? + +The `pallet_bridge_messages::Config` trait has 2 main associated types that are used to work with +inbound messages. The `pallet_bridge_messages::Config::SourceHeaderChain` defines how we see the +bridged chain as the source of our inbound messages. When relayer sends us a delivery transaction, +this implementation must be able to parse and verify the proof of messages wrapped in this +transaction. Normally, you would reuse the same (configurable) type on all chains that are sending +messages to the same bridged chain. + +The `pallet_bridge_messages::Config::MessageDispatch` defines a way on how to dispatch delivered +messages. Apart from actually dispatching the message, the implementation must return the correct +dispatch weight of the message before dispatch is called. + +### I have a Messages Module in my Runtime, but I Want to Reject all Inbound Messages. What shall I do? + +You should be looking at the `bp_messages::target_chain::ForbidInboundMessages` structure from +the [`bp_messages::target_chain`](../../primitives/messages/src/target_chain.rs) module. It +implements all required traits and will simply reject all transactions, related to inbound messages. + +### What about other Constants in the Messages Module Configuration Trait? + +Two settings that are used to check messages in the `send_message()` function. The +`pallet_bridge_messages::Config::ActiveOutboundLanes` is an array of all message lanes, that +may be used to send messages. All messages sent using other lanes are rejected. All messages that have +size above `pallet_bridge_messages::Config::MaximalOutboundPayloadSize` will also be rejected. + +To be able to reward the relayer for delivering messages, we store a map of message nonces range => +identifier of the relayer that has delivered this range at the target chain runtime storage. If a +relayer delivers multiple consequent ranges, they're merged into single entry. So there may be more +than one entry for the same relayer. Eventually, this whole map must be delivered back to the source +chain to confirm delivery and pay rewards. So to make sure we are able to craft this confirmation +transaction, we need to: (1) keep the size of this map below a certain limit and (2) make sure that +the weight of processing this map is below a certain limit. Both size and processing weight mostly +depend on the number of entries. The number of entries is limited with the +`pallet_bridge_messages::ConfigMaxUnrewardedRelayerEntriesAtInboundLane` parameter. Processing weight +also depends on the total number of messages that are being confirmed, because every confirmed +message needs to be read. So there's another +`pallet_bridge_messages::Config::MaxUnconfirmedMessagesAtInboundLane` parameter for that. + +When choosing values for these parameters, you must also keep in mind that if proof in your scheme +is based on finality of headers (and it is the most obvious option for Substrate-based chains with +finality notion), then choosing too small values for these parameters may cause significant delays +in message delivery. That's because there are too many actors involved in this scheme: 1) authorities +that are finalizing headers of the target chain need to finalize header with non-empty map; 2) the +headers relayer then needs to submit this header and its finality proof to the source chain; 3) the +messages relayer must then send confirmation transaction (storage proof of this map) to the source +chain; 4) when the confirmation transaction will be mined at some header, source chain authorities +must finalize this header; 5) the headers relay then needs to submit this header and its finality +proof to the target chain; 6) only now the messages relayer may submit new messages from the source +to target chain and prune the entry from the map. + +Delivery transaction requires the relayer to provide both number of entries and total number of +messages in the map. This means that the module never charges an extra cost for delivering a map - +the relayer would need to pay exactly for the number of entries+messages it has delivered. So the +best guess for values of these parameters would be the pair that would occupy `N` percent of the +maximal transaction size and weight of the source chain. The `N` should be large enough to process +large maps, at the same time keeping reserve for future source chain upgrades. + +## Non-Essential Functionality + +There may be a special account in every runtime where the messages module is deployed. This +account, named 'module owner', is like a module-level sudo account - he's able to halt and +resume all module operations without requiring runtime upgrade. Calls that are related to this +account are: +- `fn set_owner()`: current module owner may call it to transfer "ownership" to another account; +- `fn halt_operations()`: the module owner (or sudo account) may call this function to stop all + module operations. After this call, all message-related transactions will be rejected until + further `resume_operations` call'. This call may be used when something extraordinary happens with + the bridge; +- `fn resume_operations()`: module owner may call this function to resume bridge operations. The + module will resume its regular operations after this call. + +If pallet owner is not defined, the governance may be used to make those calls. + +## Messages Relay + +We have an offchain actor, who is watching for new messages and submits them to the bridged chain. +It is the messages relay - you may look at the [crate level documentation and the code](../../relays/messages/). diff --git a/modules/messages/src/benchmarking.rs b/modules/messages/src/benchmarking.rs new file mode 100644 index 00000000000..aab8855a729 --- /dev/null +++ b/modules/messages/src/benchmarking.rs @@ -0,0 +1,460 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Messages pallet benchmarking. + +use crate::{ + inbound_lane::InboundLaneStorage, inbound_lane_storage, outbound_lane, + weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, Call, OutboundLanes, +}; + +use bp_messages::{ + source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, DeliveredMessages, + InboundLaneData, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer, + UnrewardedRelayersState, +}; +use bp_runtime::StorageProofSize; +use codec::Decode; +use frame_benchmarking::{account, benchmarks_instance_pallet}; +use frame_support::weights::Weight; +use frame_system::RawOrigin; +use sp_runtime::traits::TrailingZeroInput; +use sp_std::{ops::RangeInclusive, prelude::*}; + +const SEED: u32 = 0; + +/// Pallet we're benchmarking here. +pub struct Pallet, I: 'static = ()>(crate::Pallet); + +/// Benchmark-specific message proof parameters. +#[derive(Debug)] +pub struct MessageProofParams { + /// Id of the lane. + pub lane: LaneId, + /// Range of messages to include in the proof. + pub message_nonces: RangeInclusive, + /// If `Some`, the proof needs to include this outbound lane data. + pub outbound_lane_data: Option, + /// If `true`, the caller expects that the proof will contain correct messages that will + /// be successfully dispatched. This is only called from the "optional" + /// `receive_single_message_proof_with_dispatch` benchmark. If you don't need it, just + /// return `true` from the `is_message_successfully_dispatched`. + pub is_successful_dispatch_expected: bool, + /// Proof size requirements. + pub size: StorageProofSize, +} + +/// Benchmark-specific message delivery proof parameters. +#[derive(Debug)] +pub struct MessageDeliveryProofParams { + /// Id of the lane. + pub lane: LaneId, + /// The proof needs to include this inbound lane data. + pub inbound_lane_data: InboundLaneData, + /// Proof size requirements. + pub size: StorageProofSize, +} + +/// Trait that must be implemented by runtime. +pub trait Config: crate::Config { + /// Lane id to use in benchmarks. + /// + /// By default, lane 00000000 is used. + fn bench_lane_id() -> LaneId { + LaneId([0, 0, 0, 0]) + } + + /// Return id of relayer account at the bridged chain. + /// + /// By default, zero account is returned. + fn bridged_relayer_id() -> Self::InboundRelayer { + Self::InboundRelayer::decode(&mut TrailingZeroInput::zeroes()).unwrap() + } + + /// Create given account and give it enough balance for test purposes. Used to create + /// relayer account at the target chain. Is strictly necessary when your rewards scheme + /// assumes that the relayer account must exist. + /// + /// Does nothing by default. + fn endow_account(_account: &Self::AccountId) {} + + /// Prepare messages proof to receive by the module. + fn prepare_message_proof( + params: MessageProofParams, + ) -> (::MessagesProof, Weight); + /// Prepare messages delivery proof to receive by the module. + fn prepare_message_delivery_proof( + params: MessageDeliveryProofParams, + ) -> >::MessagesDeliveryProof; + + /// Returns true if message has been successfully dispatched or not. + fn is_message_successfully_dispatched(_nonce: MessageNonce) -> bool { + true + } + + /// Returns true if given relayer has been rewarded for some of its actions. + fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool; +} + +benchmarks_instance_pallet! { + // + // Benchmarks that are used directly by the runtime calls weight formulae. + // + + // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: + // * proof does not include outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is dispatched (reminder: dispatch weight should be minimal); + // * message requires all heavy checks done by dispatcher. + // + // This is base benchmark for all other message delivery benchmarks. + receive_single_message_proof { + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + T::endow_account(&relayer_id_on_target); + + // mark messages 1..=20 as delivered + receive_messages::(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: T::bench_lane_id(), + message_nonces: 21..=21, + outbound_lane_data: None, + is_successful_dispatch_expected: false, + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) + verify { + assert_eq!( + crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), + 21, + ); + } + + // Benchmark `receive_messages_proof` extrinsic with two minimal-weight messages and following conditions: + // * proof does not include outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is dispatched (reminder: dispatch weight should be minimal); + // * message requires all heavy checks done by dispatcher. + // + // The weight of single message delivery could be approximated as + // `weight(receive_two_messages_proof) - weight(receive_single_message_proof)`. + // This won't be super-accurate if message has non-zero dispatch weight, but estimation should + // be close enough to real weight. + receive_two_messages_proof { + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + T::endow_account(&relayer_id_on_target); + + // mark messages 1..=20 as delivered + receive_messages::(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: T::bench_lane_id(), + message_nonces: 21..=22, + outbound_lane_data: None, + is_successful_dispatch_expected: false, + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 2, dispatch_weight) + verify { + assert_eq!( + crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), + 22, + ); + } + + // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: + // * proof includes outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is successfully dispatched (reminder: dispatch weight should be minimal); + // * message requires all heavy checks done by dispatcher. + // + // The weight of outbound lane state delivery would be + // `weight(receive_single_message_proof_with_outbound_lane_state) - weight(receive_single_message_proof)`. + // This won't be super-accurate if message has non-zero dispatch weight, but estimation should + // be close enough to real weight. + receive_single_message_proof_with_outbound_lane_state { + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + T::endow_account(&relayer_id_on_target); + + // mark messages 1..=20 as delivered + receive_messages::(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: T::bench_lane_id(), + message_nonces: 21..=21, + outbound_lane_data: Some(OutboundLaneData { + oldest_unpruned_nonce: 21, + latest_received_nonce: 20, + latest_generated_nonce: 21, + }), + is_successful_dispatch_expected: false, + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) + verify { + let lane_state = crate::InboundLanes::::get(&T::bench_lane_id()); + assert_eq!(lane_state.last_delivered_nonce(), 21); + assert_eq!(lane_state.last_confirmed_nonce, 20); + } + + // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: + // * the proof has large leaf with total size of approximately 1KB; + // * proof does not include outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is dispatched (reminder: dispatch weight should be minimal); + // * message requires all heavy checks done by dispatcher. + // + // With single KB of messages proof, the weight of the call is increased (roughly) by + // `(receive_single_message_proof_16KB - receive_single_message_proof_1_kb) / 15`. + receive_single_message_proof_1_kb { + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + T::endow_account(&relayer_id_on_target); + + // mark messages 1..=20 as delivered + receive_messages::(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: T::bench_lane_id(), + message_nonces: 21..=21, + outbound_lane_data: None, + is_successful_dispatch_expected: false, + size: StorageProofSize::HasLargeLeaf(1024), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) + verify { + assert_eq!( + crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), + 21, + ); + } + + // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: + // * the proof has large leaf with total size of approximately 16KB; + // * proof does not include outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is dispatched (reminder: dispatch weight should be minimal); + // * message requires all heavy checks done by dispatcher. + // + // Size of proof grows because it contains extra trie nodes in it. + // + // With single KB of messages proof, the weight of the call is increased (roughly) by + // `(receive_single_message_proof_16KB - receive_single_message_proof) / 15`. + receive_single_message_proof_16_kb { + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + T::endow_account(&relayer_id_on_target); + + // mark messages 1..=20 as delivered + receive_messages::(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: T::bench_lane_id(), + message_nonces: 21..=21, + outbound_lane_data: None, + is_successful_dispatch_expected: false, + size: StorageProofSize::HasLargeLeaf(16 * 1024), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) + verify { + assert_eq!( + crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), + 21, + ); + } + + // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: + // * single relayer is rewarded for relaying single message; + // * relayer account does not exist (in practice it needs to exist in production environment). + // + // This is base benchmark for all other confirmations delivery benchmarks. + receive_delivery_proof_for_single_message { + let relayer_id: T::AccountId = account("relayer", 0, SEED); + + // send message that we're going to confirm + send_regular_message::(); + + let relayers_state = UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }; + let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { + lane: T::bench_lane_id(), + inbound_lane_data: InboundLaneData { + relayers: vec![UnrewardedRelayer { + relayer: relayer_id.clone(), + messages: DeliveredMessages::new(1), + }].into_iter().collect(), + last_confirmed_nonce: 0, + }, + size: StorageProofSize::Minimal(0), + }); + }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) + verify { + assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 1); + assert!(T::is_relayer_rewarded(&relayer_id)); + } + + // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: + // * single relayer is rewarded for relaying two messages; + // * relayer account does not exist (in practice it needs to exist in production environment). + // + // Additional weight for paying single-message reward to the same relayer could be computed + // as `weight(receive_delivery_proof_for_two_messages_by_single_relayer) + // - weight(receive_delivery_proof_for_single_message)`. + receive_delivery_proof_for_two_messages_by_single_relayer { + let relayer_id: T::AccountId = account("relayer", 0, SEED); + + // send message that we're going to confirm + send_regular_message::(); + send_regular_message::(); + + let relayers_state = UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 2, + total_messages: 2, + last_delivered_nonce: 2, + }; + let mut delivered_messages = DeliveredMessages::new(1); + delivered_messages.note_dispatched_message(); + let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { + lane: T::bench_lane_id(), + inbound_lane_data: InboundLaneData { + relayers: vec![UnrewardedRelayer { + relayer: relayer_id.clone(), + messages: delivered_messages, + }].into_iter().collect(), + last_confirmed_nonce: 0, + }, + size: StorageProofSize::Minimal(0), + }); + }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) + verify { + assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); + assert!(T::is_relayer_rewarded(&relayer_id)); + } + + // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: + // * two relayers are rewarded for relaying single message each; + // * relayer account does not exist (in practice it needs to exist in production environment). + // + // Additional weight for paying reward to the next relayer could be computed + // as `weight(receive_delivery_proof_for_two_messages_by_two_relayers) + // - weight(receive_delivery_proof_for_two_messages_by_single_relayer)`. + receive_delivery_proof_for_two_messages_by_two_relayers { + let relayer1_id: T::AccountId = account("relayer1", 1, SEED); + let relayer2_id: T::AccountId = account("relayer2", 2, SEED); + + // send message that we're going to confirm + send_regular_message::(); + send_regular_message::(); + + let relayers_state = UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + messages_in_oldest_entry: 1, + total_messages: 2, + last_delivered_nonce: 2, + }; + let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { + lane: T::bench_lane_id(), + inbound_lane_data: InboundLaneData { + relayers: vec![ + UnrewardedRelayer { + relayer: relayer1_id.clone(), + messages: DeliveredMessages::new(1), + }, + UnrewardedRelayer { + relayer: relayer2_id.clone(), + messages: DeliveredMessages::new(2), + }, + ].into_iter().collect(), + last_confirmed_nonce: 0, + }, + size: StorageProofSize::Minimal(0), + }); + }: receive_messages_delivery_proof(RawOrigin::Signed(relayer1_id.clone()), proof, relayers_state) + verify { + assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); + assert!(T::is_relayer_rewarded(&relayer1_id)); + assert!(T::is_relayer_rewarded(&relayer2_id)); + } + + // + // Benchmarks that the runtime developers may use for proper pallet configuration. + // + + // This benchmark is optional and may be used when runtime developer need a way to compute + // message dispatch weight. In this case, he needs to provide messages that can go the whole + // dispatch + // + // Benchmark `receive_messages_proof` extrinsic with single message and following conditions: + // + // * proof does not include outbound lane state proof; + // * inbound lane already has state, so it needs to be read and decoded; + // * message is **SUCCESSFULLY** dispatched; + // * message requires all heavy checks done by dispatcher. + receive_single_message_proof_with_dispatch { + // maybe dispatch weight relies on the message size too? + let i in EXPECTED_DEFAULT_MESSAGE_LENGTH .. EXPECTED_DEFAULT_MESSAGE_LENGTH * 16; + + let relayer_id_on_source = T::bridged_relayer_id(); + let relayer_id_on_target = account("relayer", 0, SEED); + T::endow_account(&relayer_id_on_target); + + // mark messages 1..=20 as delivered + receive_messages::(20); + + let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { + lane: T::bench_lane_id(), + message_nonces: 21..=21, + outbound_lane_data: None, + is_successful_dispatch_expected: true, + size: StorageProofSize::Minimal(i), + }); + }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) + verify { + assert_eq!( + crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), + 21, + ); + assert!(T::is_message_successfully_dispatched(21)); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) +} + +fn send_regular_message, I: 'static>() { + let mut outbound_lane = outbound_lane::(T::bench_lane_id()); + outbound_lane.send_message(vec![]); +} + +fn receive_messages, I: 'static>(nonce: MessageNonce) { + let mut inbound_lane_storage = inbound_lane_storage::(T::bench_lane_id()); + inbound_lane_storage.set_data(InboundLaneData { + relayers: vec![UnrewardedRelayer { + relayer: T::bridged_relayer_id(), + messages: DeliveredMessages::new(nonce), + }] + .into_iter() + .collect(), + last_confirmed_nonce: 0, + }); +} diff --git a/modules/messages/src/inbound_lane.rs b/modules/messages/src/inbound_lane.rs new file mode 100644 index 00000000000..3f64ab765b5 --- /dev/null +++ b/modules/messages/src/inbound_lane.rs @@ -0,0 +1,550 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything about incoming messages receival. + +use crate::Config; + +use bp_messages::{ + target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, + DeliveredMessages, InboundLaneData, LaneId, MessageKey, MessageNonce, OutboundLaneData, + ReceivalResult, UnrewardedRelayer, +}; +use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; +use frame_support::{traits::Get, RuntimeDebug}; +use scale_info::{Type, TypeInfo}; +use sp_std::prelude::PartialEq; + +/// Inbound lane storage. +pub trait InboundLaneStorage { + /// Id of relayer on source chain. + type Relayer: Clone + PartialEq; + + /// Lane id. + fn id(&self) -> LaneId; + /// Return maximal number of unrewarded relayer entries in inbound lane. + fn max_unrewarded_relayer_entries(&self) -> MessageNonce; + /// Return maximal number of unconfirmed messages in inbound lane. + fn max_unconfirmed_messages(&self) -> MessageNonce; + /// Get lane data from the storage. + fn data(&self) -> InboundLaneData; + /// Update lane data in the storage. + fn set_data(&mut self, data: InboundLaneData); +} + +/// Inbound lane data wrapper that implements `MaxEncodedLen`. +/// +/// We have already had `MaxEncodedLen`-like functionality before, but its usage has +/// been localized and we haven't been passing bounds (maximal count of unrewarded relayer entries, +/// maximal count of unconfirmed messages) everywhere. This wrapper allows us to avoid passing +/// these generic bounds all over the code. +/// +/// The encoding of this type matches encoding of the corresponding `MessageData`. +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq)] +pub struct StoredInboundLaneData, I: 'static>(pub InboundLaneData); + +impl, I: 'static> sp_std::ops::Deref for StoredInboundLaneData { + type Target = InboundLaneData; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl, I: 'static> sp_std::ops::DerefMut for StoredInboundLaneData { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl, I: 'static> Default for StoredInboundLaneData { + fn default() -> Self { + StoredInboundLaneData(Default::default()) + } +} + +impl, I: 'static> From> + for InboundLaneData +{ + fn from(data: StoredInboundLaneData) -> Self { + data.0 + } +} + +impl, I: 'static> EncodeLike> + for InboundLaneData +{ +} + +impl, I: 'static> TypeInfo for StoredInboundLaneData { + type Identity = Self; + + fn type_info() -> Type { + InboundLaneData::::type_info() + } +} + +impl, I: 'static> MaxEncodedLen for StoredInboundLaneData { + fn max_encoded_len() -> usize { + InboundLaneData::::encoded_size_hint( + T::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize, + ) + .unwrap_or(usize::MAX) + } +} + +/// Inbound messages lane. +pub struct InboundLane { + storage: S, +} + +impl InboundLane { + /// Create new inbound lane backed by given storage. + pub fn new(storage: S) -> Self { + InboundLane { storage } + } + + /// Returns storage reference. + pub fn storage(&self) -> &S { + &self.storage + } + + /// Receive state of the corresponding outbound lane. + pub fn receive_state_update( + &mut self, + outbound_lane_data: OutboundLaneData, + ) -> Option { + let mut data = self.storage.data(); + let last_delivered_nonce = data.last_delivered_nonce(); + + if outbound_lane_data.latest_received_nonce > last_delivered_nonce { + // this is something that should never happen if proofs are correct + return None + } + if outbound_lane_data.latest_received_nonce <= data.last_confirmed_nonce { + return None + } + + let new_confirmed_nonce = outbound_lane_data.latest_received_nonce; + data.last_confirmed_nonce = new_confirmed_nonce; + // Firstly, remove all of the records where higher nonce <= new confirmed nonce + while data + .relayers + .front() + .map(|entry| entry.messages.end <= new_confirmed_nonce) + .unwrap_or(false) + { + data.relayers.pop_front(); + } + // Secondly, update the next record with lower nonce equal to new confirmed nonce if needed. + // Note: There will be max. 1 record to update as we don't allow messages from relayers to + // overlap. + match data.relayers.front_mut() { + Some(entry) if entry.messages.begin < new_confirmed_nonce => { + entry.messages.begin = new_confirmed_nonce + 1; + }, + _ => {}, + } + + self.storage.set_data(data); + Some(outbound_lane_data.latest_received_nonce) + } + + /// Receive new message. + pub fn receive_message, AccountId>( + &mut self, + relayer_at_bridged_chain: &S::Relayer, + relayer_at_this_chain: &AccountId, + nonce: MessageNonce, + message_data: DispatchMessageData, + ) -> ReceivalResult { + let mut data = self.storage.data(); + let is_correct_message = nonce == data.last_delivered_nonce() + 1; + if !is_correct_message { + return ReceivalResult::InvalidNonce + } + + // if there are more unrewarded relayer entries than we may accept, reject this message + if data.relayers.len() as MessageNonce >= self.storage.max_unrewarded_relayer_entries() { + return ReceivalResult::TooManyUnrewardedRelayers + } + + // if there are more unconfirmed messages than we may accept, reject this message + let unconfirmed_messages_count = nonce.saturating_sub(data.last_confirmed_nonce); + if unconfirmed_messages_count > self.storage.max_unconfirmed_messages() { + return ReceivalResult::TooManyUnconfirmedMessages + } + + // then, dispatch message + let dispatch_result = Dispatch::dispatch( + relayer_at_this_chain, + DispatchMessage { + key: MessageKey { lane_id: self.storage.id(), nonce }, + data: message_data, + }, + ); + + // now let's update inbound lane storage + let push_new = match data.relayers.back_mut() { + Some(entry) if entry.relayer == *relayer_at_bridged_chain => { + entry.messages.note_dispatched_message(); + false + }, + _ => true, + }; + if push_new { + data.relayers.push_back(UnrewardedRelayer { + relayer: (*relayer_at_bridged_chain).clone(), + messages: DeliveredMessages::new(nonce), + }); + } + self.storage.set_data(data); + + ReceivalResult::Dispatched(dispatch_result) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + inbound_lane, + mock::{ + dispatch_result, inbound_message_data, run_test, unrewarded_relayer, + TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, + TEST_RELAYER_B, TEST_RELAYER_C, + }, + RuntimeInboundLaneStorage, + }; + + fn receive_regular_message( + lane: &mut InboundLane>, + nonce: MessageNonce, + ) { + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + nonce, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::Dispatched(dispatch_result(0)) + ); + } + + #[test] + fn receive_status_update_ignores_status_from_the_future() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + receive_regular_message(&mut lane, 1); + assert_eq!( + lane.receive_state_update(OutboundLaneData { + latest_received_nonce: 10, + ..Default::default() + }), + None, + ); + + assert_eq!(lane.storage.data().last_confirmed_nonce, 0); + }); + } + + #[test] + fn receive_status_update_ignores_obsolete_status() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + receive_regular_message(&mut lane, 1); + receive_regular_message(&mut lane, 2); + receive_regular_message(&mut lane, 3); + assert_eq!( + lane.receive_state_update(OutboundLaneData { + latest_received_nonce: 3, + ..Default::default() + }), + Some(3), + ); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); + + assert_eq!( + lane.receive_state_update(OutboundLaneData { + latest_received_nonce: 3, + ..Default::default() + }), + None, + ); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); + }); + } + + #[test] + fn receive_status_update_works() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + receive_regular_message(&mut lane, 1); + receive_regular_message(&mut lane, 2); + receive_regular_message(&mut lane, 3); + assert_eq!(lane.storage.data().last_confirmed_nonce, 0); + assert_eq!( + lane.storage.data().relayers, + vec![unrewarded_relayer(1, 3, TEST_RELAYER_A)] + ); + + assert_eq!( + lane.receive_state_update(OutboundLaneData { + latest_received_nonce: 2, + ..Default::default() + }), + Some(2), + ); + assert_eq!(lane.storage.data().last_confirmed_nonce, 2); + assert_eq!( + lane.storage.data().relayers, + vec![unrewarded_relayer(3, 3, TEST_RELAYER_A)] + ); + + assert_eq!( + lane.receive_state_update(OutboundLaneData { + latest_received_nonce: 3, + ..Default::default() + }), + Some(3), + ); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().relayers, vec![]); + }); + } + + #[test] + fn receive_status_update_works_with_batches_from_relayers() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + let mut seed_storage_data = lane.storage.data(); + // Prepare data + seed_storage_data.last_confirmed_nonce = 0; + seed_storage_data.relayers.push_back(unrewarded_relayer(1, 1, TEST_RELAYER_A)); + // Simulate messages batch (2, 3, 4) from relayer #2 + seed_storage_data.relayers.push_back(unrewarded_relayer(2, 4, TEST_RELAYER_B)); + seed_storage_data.relayers.push_back(unrewarded_relayer(5, 5, TEST_RELAYER_C)); + lane.storage.set_data(seed_storage_data); + // Check + assert_eq!( + lane.receive_state_update(OutboundLaneData { + latest_received_nonce: 3, + ..Default::default() + }), + Some(3), + ); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); + assert_eq!( + lane.storage.data().relayers, + vec![ + unrewarded_relayer(4, 4, TEST_RELAYER_B), + unrewarded_relayer(5, 5, TEST_RELAYER_C) + ] + ); + }); + } + + #[test] + fn fails_to_receive_message_with_incorrect_nonce() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + 10, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::InvalidNonce + ); + assert_eq!(lane.storage.data().last_delivered_nonce(), 0); + }); + } + + #[test] + fn fails_to_receive_messages_above_unrewarded_relayer_entries_limit_per_lane() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + let max_nonce = + ::MaxUnrewardedRelayerEntriesAtInboundLane::get(); + for current_nonce in 1..max_nonce + 1 { + assert_eq!( + lane.receive_message::( + &(TEST_RELAYER_A + current_nonce), + &(TEST_RELAYER_A + current_nonce), + current_nonce, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::Dispatched(dispatch_result(0)) + ); + } + // Fails to dispatch new message from different than latest relayer. + assert_eq!( + lane.receive_message::( + &(TEST_RELAYER_A + max_nonce + 1), + &(TEST_RELAYER_A + max_nonce + 1), + max_nonce + 1, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::TooManyUnrewardedRelayers, + ); + // Fails to dispatch new messages from latest relayer. Prevents griefing attacks. + assert_eq!( + lane.receive_message::( + &(TEST_RELAYER_A + max_nonce), + &(TEST_RELAYER_A + max_nonce), + max_nonce + 1, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::TooManyUnrewardedRelayers, + ); + }); + } + + #[test] + fn fails_to_receive_messages_above_unconfirmed_messages_limit_per_lane() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + let max_nonce = ::MaxUnconfirmedMessagesAtInboundLane::get(); + for current_nonce in 1..=max_nonce { + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + current_nonce, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::Dispatched(dispatch_result(0)) + ); + } + // Fails to dispatch new message from different than latest relayer. + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_B, + &TEST_RELAYER_B, + max_nonce + 1, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::TooManyUnconfirmedMessages, + ); + // Fails to dispatch new messages from latest relayer. + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + max_nonce + 1, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::TooManyUnconfirmedMessages, + ); + }); + } + + #[test] + fn correctly_receives_following_messages_from_two_relayers_alternately() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + 1, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::Dispatched(dispatch_result(0)) + ); + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_B, + &TEST_RELAYER_B, + 2, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::Dispatched(dispatch_result(0)) + ); + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + 3, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::Dispatched(dispatch_result(0)) + ); + assert_eq!( + lane.storage.data().relayers, + vec![ + unrewarded_relayer(1, 1, TEST_RELAYER_A), + unrewarded_relayer(2, 2, TEST_RELAYER_B), + unrewarded_relayer(3, 3, TEST_RELAYER_A) + ] + ); + }); + } + + #[test] + fn rejects_same_message_from_two_different_relayers() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + 1, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::Dispatched(dispatch_result(0)) + ); + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_B, + &TEST_RELAYER_B, + 1, + inbound_message_data(REGULAR_PAYLOAD) + ), + ReceivalResult::InvalidNonce, + ); + }); + } + + #[test] + fn correct_message_is_processed_instantly() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + receive_regular_message(&mut lane, 1); + assert_eq!(lane.storage.data().last_delivered_nonce(), 1); + }); + } + + #[test] + fn unspent_weight_is_returned_by_receive_message() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + let mut payload = REGULAR_PAYLOAD; + *payload.dispatch_result.unspent_weight.ref_time_mut() = 1; + assert_eq!( + lane.receive_message::( + &TEST_RELAYER_A, + &TEST_RELAYER_A, + 1, + inbound_message_data(payload) + ), + ReceivalResult::Dispatched(dispatch_result(1)) + ); + }); + } +} diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs new file mode 100644 index 00000000000..c94f5ffa752 --- /dev/null +++ b/modules/messages/src/lib.rs @@ -0,0 +1,2176 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Runtime module that allows sending and receiving messages using lane concept: +//! +//! 1) the message is sent using `send_message()` call; +//! 2) every outbound message is assigned nonce; +//! 3) the messages are stored in the storage; +//! 4) external component (relay) delivers messages to bridged chain; +//! 5) messages are processed in order (ordered by assigned nonce); +//! 6) relay may send proof-of-delivery back to this chain. +//! +//! Once message is sent, its progress can be tracked by looking at module events. +//! The assigned nonce is reported using `MessageAccepted` event. When message is +//! delivered to the the bridged chain, it is reported using `MessagesDelivered` event. +//! +//! **IMPORTANT NOTE**: after generating weights (custom `WeighInfo` implementation) for +//! your runtime (where this module is plugged to), please add test for these weights. +//! The test should call the `ensure_weights_are_correct` function from this module. +//! If this test fails with your weights, then either weights are computed incorrectly, +//! or some benchmarks assumptions are broken for your runtime. + +#![cfg_attr(not(feature = "std"), no_std)] +// Generated by `decl_event!` +#![allow(clippy::unused_unit)] + +pub use inbound_lane::StoredInboundLaneData; +pub use outbound_lane::StoredMessagePayload; +pub use weights::WeightInfo; +pub use weights_ext::{ + ensure_able_to_receive_confirmation, ensure_able_to_receive_message, + ensure_weights_are_correct, WeightInfoExt, EXPECTED_DEFAULT_MESSAGE_LENGTH, + EXTRA_STORAGE_PROOF_SIZE, +}; + +use crate::{ + inbound_lane::{InboundLane, InboundLaneStorage}, + outbound_lane::{OutboundLane, OutboundLaneStorage, ReceivalConfirmationResult}, +}; + +use bp_messages::{ + source_chain::{ + DeliveryConfirmationPayments, LaneMessageVerifier, SendMessageArtifacts, TargetHeaderChain, + }, + target_chain::{ + DeliveryPayments, DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, + SourceHeaderChain, + }, + total_unrewarded_messages, DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId, + MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData, + OutboundMessageDetails, UnrewardedRelayersState, +}; +use bp_runtime::{BasicOperatingMode, ChainId, OwnedBridgeModule, PreComputedSize, Size}; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get}; +use sp_runtime::traits::UniqueSaturatedFrom; +use sp_std::{cell::RefCell, marker::PhantomData, prelude::*}; + +mod inbound_lane; +mod outbound_lane; +mod weights_ext; + +pub mod weights; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + +#[cfg(test)] +mod mock; + +pub use pallet::*; + +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-messages"; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use bp_messages::{ReceivalResult, ReceivedMessages}; + use bp_runtime::RangeInclusiveExt; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + // General types + + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + /// Benchmarks results from runtime we're plugged into. + type WeightInfo: WeightInfoExt; + + /// Gets the chain id value from the instance. + #[pallet::constant] + type BridgedChainId: Get; + + /// Get all active outbound lanes that the message pallet is serving. + type ActiveOutboundLanes: Get<&'static [LaneId]>; + /// Maximal number of unrewarded relayer entries at inbound lane. Unrewarded means that the + /// relayer has delivered messages, but either confirmations haven't been delivered back to + /// the source chain, or we haven't received reward confirmations yet. + /// + /// This constant limits maximal number of entries in the `InboundLaneData::relayers`. Keep + /// in mind that the same relayer account may take several (non-consecutive) entries in this + /// set. + type MaxUnrewardedRelayerEntriesAtInboundLane: Get; + /// Maximal number of unconfirmed messages at inbound lane. Unconfirmed means that the + /// message has been delivered, but either confirmations haven't been delivered back to the + /// source chain, or we haven't received reward confirmations for these messages yet. + /// + /// This constant limits difference between last message from last entry of the + /// `InboundLaneData::relayers` and first message at the first entry. + /// + /// There is no point of making this parameter lesser than + /// MaxUnrewardedRelayerEntriesAtInboundLane, because then maximal number of relayer entries + /// will be limited by maximal number of messages. + /// + /// This value also represents maximal number of messages in single delivery transaction. + /// Transaction that is declaring more messages than this value, will be rejected. Even if + /// these messages are from different lanes. + type MaxUnconfirmedMessagesAtInboundLane: Get; + + /// Maximal encoded size of the outbound payload. + #[pallet::constant] + type MaximalOutboundPayloadSize: Get; + /// Payload type of outbound messages. This payload is dispatched on the bridged chain. + type OutboundPayload: Parameter + Size; + + /// Payload type of inbound messages. This payload is dispatched on this chain. + type InboundPayload: Decode; + /// Identifier of relayer that deliver messages to this chain. Relayer reward is paid on the + /// bridged chain. + type InboundRelayer: Parameter + MaxEncodedLen; + /// Delivery payments. + type DeliveryPayments: DeliveryPayments; + + // Types that are used by outbound_lane (on source chain). + + /// Target header chain. + type TargetHeaderChain: TargetHeaderChain; + /// Message payload verifier. + type LaneMessageVerifier: LaneMessageVerifier; + /// Delivery confirmation payments. + type DeliveryConfirmationPayments: DeliveryConfirmationPayments; + + // Types that are used by inbound_lane (on target chain). + + /// Source header chain, as it is represented on target chain. + type SourceHeaderChain: SourceHeaderChain; + /// Message dispatch. + type MessageDispatch: MessageDispatch< + Self::AccountId, + DispatchPayload = Self::InboundPayload, + >; + } + + /// Shortcut to messages proof type for Config. + pub type MessagesProofOf = + <>::SourceHeaderChain as SourceHeaderChain>::MessagesProof; + /// Shortcut to messages delivery proof type for Config. + pub type MessagesDeliveryProofOf = + <>::TargetHeaderChain as TargetHeaderChain< + >::OutboundPayload, + ::AccountId, + >>::MessagesDeliveryProof; + + #[pallet::pallet] + pub struct Pallet(PhantomData<(T, I)>); + + impl, I: 'static> OwnedBridgeModule for Pallet { + const LOG_TARGET: &'static str = LOG_TARGET; + type OwnerStorage = PalletOwner; + type OperatingMode = MessagesOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + } + + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet + where + u32: TryFrom<::BlockNumber>, + { + fn on_idle(_block: T::BlockNumber, remaining_weight: Weight) -> Weight { + // we'll need at least to read outbound lane state, kill a message and update lane state + let db_weight = T::DbWeight::get(); + if !remaining_weight.all_gte(db_weight.reads_writes(1, 2)) { + return Weight::zero() + } + + // messages from lane with index `i` in `ActiveOutboundLanes` are pruned when + // `System::block_number() % lanes.len() == i`. Otherwise we need to read lane states on + // every block, wasting the whole `remaining_weight` for nothing and causing starvation + // of the last lane pruning + let active_lanes = T::ActiveOutboundLanes::get(); + let active_lanes_len = (active_lanes.len() as u32).into(); + let active_lane_index = u32::unique_saturated_from( + frame_system::Pallet::::block_number() % active_lanes_len, + ); + let active_lane_id = active_lanes[active_lane_index as usize]; + + // first db read - outbound lane state + let mut active_lane = outbound_lane::(active_lane_id); + let mut used_weight = db_weight.reads(1); + // and here we'll have writes + used_weight += active_lane.prune_messages(db_weight, remaining_weight - used_weight); + + // we already checked we have enough `remaining_weight` to cover this `used_weight` + used_weight + } + } + + #[pallet::call] + impl, I: 'static> Pallet { + /// Change `PalletOwner`. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(0)] + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { + >::set_owner(origin, new_owner) + } + + /// Halt or resume all/some pallet operations. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(1)] + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_operating_mode( + origin: OriginFor, + operating_mode: MessagesOperatingMode, + ) -> DispatchResult { + >::set_operating_mode(origin, operating_mode) + } + + /// Receive messages proof from bridged chain. + /// + /// The weight of the call assumes that the transaction always brings outbound lane + /// state update. Because of that, the submitter (relayer) has no benefit of not including + /// this data in the transaction, so reward confirmations lags should be minimal. + /// + /// The call fails if: + /// + /// - the pallet is halted; + /// + /// - the call origin is not `Signed(_)`; + /// + /// - there are too many messages in the proof; + /// + /// - the proof verification procedure returns an error - e.g. because header used to craft + /// proof is not imported by the associated finality pallet; + /// + /// - the `dispatch_weight` argument is not sufficient to dispatch all bundled messages. + /// + /// The call may succeed, but some messages may not be delivered e.g. if they are not fit + /// into the unrewarded relayers vector. + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight))] + pub fn receive_messages_proof( + origin: OriginFor, + relayer_id_at_bridged_chain: T::InboundRelayer, + proof: MessagesProofOf, + messages_count: u32, + dispatch_weight: Weight, + ) -> DispatchResultWithPostInfo { + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + let relayer_id_at_this_chain = ensure_signed(origin)?; + + // reject transactions that are declaring too many messages + ensure!( + MessageNonce::from(messages_count) <= T::MaxUnconfirmedMessagesAtInboundLane::get(), + Error::::TooManyMessagesInTheProof + ); + + // why do we need to know the weight of this (`receive_messages_proof`) call? Because + // we may want to return some funds for not-dispatching (or partially dispatching) some + // messages to the call origin (relayer). And this is done by returning actual weight + // from the call. But we only know dispatch weight of every messages. So to refund + // relayer because we have not dispatched Message, we need to: + // + // ActualWeight = DeclaredWeight - Message.DispatchWeight + // + // The DeclaredWeight is exactly what's computed here. Unfortunately it is impossible + // to get pre-computed value (and it has been already computed by the executive). + let declared_weight = T::WeightInfo::receive_messages_proof_weight( + &proof, + messages_count, + dispatch_weight, + ); + let mut actual_weight = declared_weight; + + // verify messages proof && convert proof into messages + let messages = verify_and_decode_messages_proof::< + T::SourceHeaderChain, + T::InboundPayload, + >(proof, messages_count) + .map_err(|err| { + log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,); + + Error::::InvalidMessagesProof + })?; + + // dispatch messages and (optionally) update lane(s) state(s) + let mut total_messages = 0; + let mut valid_messages = 0; + let mut messages_received_status = Vec::with_capacity(messages.len()); + let mut dispatch_weight_left = dispatch_weight; + for (lane_id, lane_data) in messages { + let mut lane = inbound_lane::(lane_id); + + // subtract extra storage proof bytes from the actual PoV size - there may be + // less unrewarded relayers than the maximal configured value + let lane_extra_proof_size_bytes = lane.storage().extra_proof_size_bytes(); + actual_weight = actual_weight.set_proof_size( + actual_weight.proof_size().saturating_sub(lane_extra_proof_size_bytes), + ); + + if let Some(lane_state) = lane_data.lane_state { + let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state); + if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce { + log::trace!( + target: LOG_TARGET, + "Received lane {:?} state update: latest_confirmed_nonce={}", + lane_id, + updated_latest_confirmed_nonce, + ); + } + } + + let mut lane_messages_received_status = + ReceivedMessages::new(lane_id, Vec::with_capacity(lane_data.messages.len())); + for mut message in lane_data.messages { + debug_assert_eq!(message.key.lane_id, lane_id); + total_messages += 1; + + // ensure that relayer has declared enough weight for dispatching next message + // on this lane. We can't dispatch lane messages out-of-order, so if declared + // weight is not enough, let's move to next lane + let message_dispatch_weight = T::MessageDispatch::dispatch_weight(&mut message); + if message_dispatch_weight.any_gt(dispatch_weight_left) { + log::trace!( + target: LOG_TARGET, + "Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}", + lane_id, + message_dispatch_weight, + dispatch_weight_left, + ); + + fail!(Error::::InsufficientDispatchWeight); + } + + let receival_result = lane.receive_message::( + &relayer_id_at_bridged_chain, + &relayer_id_at_this_chain, + message.key.nonce, + message.data, + ); + + // note that we're returning unspent weight to relayer even if message has been + // rejected by the lane. This allows relayers to submit spam transactions with + // e.g. the same set of already delivered messages over and over again, without + // losing funds for messages dispatch. But keep in mind that relayer pays base + // delivery transaction cost anyway. And base cost covers everything except + // dispatch, so we have a balance here. + let unspent_weight = match &receival_result { + ReceivalResult::Dispatched(dispatch_result) => { + valid_messages += 1; + dispatch_result.unspent_weight + }, + ReceivalResult::InvalidNonce | + ReceivalResult::TooManyUnrewardedRelayers | + ReceivalResult::TooManyUnconfirmedMessages => message_dispatch_weight, + }; + lane_messages_received_status.push(message.key.nonce, receival_result); + + let unspent_weight = unspent_weight.min(message_dispatch_weight); + dispatch_weight_left -= message_dispatch_weight - unspent_weight; + actual_weight = actual_weight.saturating_sub(unspent_weight); + } + + messages_received_status.push(lane_messages_received_status); + } + + // let's now deal with relayer payments + T::DeliveryPayments::pay_reward( + relayer_id_at_this_chain, + total_messages, + valid_messages, + actual_weight, + ); + + log::debug!( + target: LOG_TARGET, + "Received messages: total={}, valid={}. Weight used: {}/{}.", + total_messages, + valid_messages, + actual_weight, + declared_weight, + ); + + Self::deposit_event(Event::MessagesReceived(messages_received_status)); + + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) + } + + /// Receive messages delivery proof from bridged chain. + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::receive_messages_delivery_proof_weight( + proof, + relayers_state, + ))] + pub fn receive_messages_delivery_proof( + origin: OriginFor, + proof: MessagesDeliveryProofOf, + mut relayers_state: UnrewardedRelayersState, + ) -> DispatchResultWithPostInfo { + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + + let proof_size = proof.size(); + let confirmation_relayer = ensure_signed(origin)?; + let (lane_id, lane_data) = T::TargetHeaderChain::verify_messages_delivery_proof(proof) + .map_err(|err| { + log::trace!( + target: LOG_TARGET, + "Rejecting invalid messages delivery proof: {:?}", + err, + ); + + Error::::InvalidMessagesDeliveryProof + })?; + + // verify that the relayer has declared correct `lane_data::relayers` state + // (we only care about total number of entries and messages, because this affects call + // weight) + ensure!( + total_unrewarded_messages(&lane_data.relayers).unwrap_or(MessageNonce::MAX) == + relayers_state.total_messages && + lane_data.relayers.len() as MessageNonce == + relayers_state.unrewarded_relayer_entries, + Error::::InvalidUnrewardedRelayersState + ); + // the `last_delivered_nonce` field may also be used by the signed extension. Even + // though providing wrong value isn't critical, let's also check it here. + ensure!( + lane_data.last_delivered_nonce() == relayers_state.last_delivered_nonce, + Error::::InvalidUnrewardedRelayersState + ); + + // mark messages as delivered + let mut lane = outbound_lane::(lane_id); + let last_delivered_nonce = lane_data.last_delivered_nonce(); + let confirmed_messages = match lane.confirm_delivery( + relayers_state.total_messages, + last_delivered_nonce, + &lane_data.relayers, + ) { + ReceivalConfirmationResult::ConfirmedMessages(confirmed_messages) => + Some(confirmed_messages), + ReceivalConfirmationResult::NoNewConfirmations => None, + ReceivalConfirmationResult::TryingToConfirmMoreMessagesThanExpected( + to_confirm_messages_count, + ) => { + log::trace!( + target: LOG_TARGET, + "Messages delivery proof contains too many messages to confirm: {} vs declared {}", + to_confirm_messages_count, + relayers_state.total_messages, + ); + + fail!(Error::::TryingToConfirmMoreMessagesThanExpected); + }, + error => { + log::trace!( + target: LOG_TARGET, + "Messages delivery proof contains invalid unrewarded relayers vec: {:?}", + error, + ); + + fail!(Error::::InvalidUnrewardedRelayers); + }, + }; + + if let Some(confirmed_messages) = confirmed_messages { + // emit 'delivered' event + let received_range = confirmed_messages.begin..=confirmed_messages.end; + Self::deposit_event(Event::MessagesDelivered { + lane_id, + messages: confirmed_messages, + }); + + // if some new messages have been confirmed, reward relayers + let actually_rewarded_relayers = T::DeliveryConfirmationPayments::pay_reward( + lane_id, + lane_data.relayers, + &confirmation_relayer, + &received_range, + ); + + // update relayers state with actual numbers to compute actual weight below + relayers_state.unrewarded_relayer_entries = sp_std::cmp::min( + relayers_state.unrewarded_relayer_entries, + actually_rewarded_relayers, + ); + relayers_state.total_messages = sp_std::cmp::min( + relayers_state.total_messages, + received_range.checked_len().unwrap_or(MessageNonce::MAX), + ); + }; + + log::trace!( + target: LOG_TARGET, + "Received messages delivery proof up to (and including) {} at lane {:?}", + last_delivered_nonce, + lane_id, + ); + + // because of lags, the inbound lane state (`lane_data`) may have entries for + // already rewarded relayers and messages (if all entries are duplicated, then + // this transaction must be filtered out by our signed extension) + let actual_weight = T::WeightInfo::receive_messages_delivery_proof_weight( + &PreComputedSize(proof_size as usize), + &relayers_state, + ); + + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// Message has been accepted and is waiting to be delivered. + MessageAccepted { lane_id: LaneId, nonce: MessageNonce }, + /// Messages have been received from the bridged chain. + MessagesReceived( + Vec< + ReceivedMessages< + >::DispatchLevelResult, + >, + >, + ), + /// Messages in the inclusive range have been delivered to the bridged chain. + MessagesDelivered { lane_id: LaneId, messages: DeliveredMessages }, + } + + #[pallet::error] + pub enum Error { + /// Pallet is not in Normal operating mode. + NotOperatingNormally, + /// The outbound lane is inactive. + InactiveOutboundLane, + /// The message is too large to be sent over the bridge. + MessageIsTooLarge, + /// Message has been treated as invalid by chain verifier. + MessageRejectedByChainVerifier, + /// Message has been treated as invalid by lane verifier. + MessageRejectedByLaneVerifier, + /// Submitter has failed to pay fee for delivering and dispatching messages. + FailedToWithdrawMessageFee, + /// The transaction brings too many messages. + TooManyMessagesInTheProof, + /// Invalid messages has been submitted. + InvalidMessagesProof, + /// Invalid messages delivery proof has been submitted. + InvalidMessagesDeliveryProof, + /// The bridged chain has invalid `UnrewardedRelayers` in its storage (fatal for the lane). + InvalidUnrewardedRelayers, + /// The relayer has declared invalid unrewarded relayers state in the + /// `receive_messages_delivery_proof` call. + InvalidUnrewardedRelayersState, + /// The cumulative dispatch weight, passed by relayer is not enough to cover dispatch + /// of all bundled messages. + InsufficientDispatchWeight, + /// The message someone is trying to work with (i.e. increase fee) is not yet sent. + MessageIsNotYetSent, + /// The number of actually confirmed messages is going to be larger than the number of + /// messages in the proof. This may mean that this or bridged chain storage is corrupted. + TryingToConfirmMoreMessagesThanExpected, + /// Error generated by the `OwnedBridgeModule` trait. + BridgeModule(bp_runtime::OwnedBridgeModuleError), + } + + /// Optional pallet owner. + /// + /// Pallet owner has a right to halt all pallet operations and then resume it. If it is + /// `None`, then there are no direct ways to halt/resume pallet operations, but other + /// runtime methods may still be used to do that (i.e. democracy::referendum to update halt + /// flag directly or call the `halt_operations`). + #[pallet::storage] + #[pallet::getter(fn module_owner)] + pub type PalletOwner, I: 'static = ()> = StorageValue<_, T::AccountId>; + + /// The current operating mode of the pallet. + /// + /// Depending on the mode either all, some, or no transactions will be allowed. + #[pallet::storage] + #[pallet::getter(fn operating_mode)] + pub type PalletOperatingMode, I: 'static = ()> = + StorageValue<_, MessagesOperatingMode, ValueQuery>; + + /// Map of lane id => inbound lane data. + #[pallet::storage] + pub type InboundLanes, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, LaneId, StoredInboundLaneData, ValueQuery>; + + /// Map of lane id => outbound lane data. + #[pallet::storage] + pub type OutboundLanes, I: 'static = ()> = StorageMap< + Hasher = Blake2_128Concat, + Key = LaneId, + Value = OutboundLaneData, + QueryKind = ValueQuery, + OnEmpty = GetDefault, + MaxValues = MaybeOutboundLanesCount, + >; + + /// All queued outbound messages. + #[pallet::storage] + pub type OutboundMessages, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, MessageKey, StoredMessagePayload>; + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + /// Initial pallet operating mode. + pub operating_mode: MessagesOperatingMode, + /// Initial pallet owner. + pub owner: Option, + /// Dummy marker. + pub phantom: sp_std::marker::PhantomData, + } + + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { + operating_mode: Default::default(), + owner: Default::default(), + phantom: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + PalletOperatingMode::::put(self.operating_mode); + if let Some(ref owner) = self.owner { + PalletOwner::::put(owner); + } + } + } + + impl, I: 'static> Pallet { + /// Get stored data of the outbound message with given nonce. + pub fn outbound_message_data(lane: LaneId, nonce: MessageNonce) -> Option { + OutboundMessages::::get(MessageKey { lane_id: lane, nonce }).map(Into::into) + } + + /// Prepare data, related to given inbound message. + pub fn inbound_message_data( + lane: LaneId, + payload: MessagePayload, + outbound_details: OutboundMessageDetails, + ) -> InboundMessageDetails { + let mut dispatch_message = DispatchMessage { + key: MessageKey { lane_id: lane, nonce: outbound_details.nonce }, + data: payload.into(), + }; + InboundMessageDetails { + dispatch_weight: T::MessageDispatch::dispatch_weight(&mut dispatch_message), + } + } + + /// Return inbound lane data. + pub fn inbound_lane_data(lane: LaneId) -> InboundLaneData { + InboundLanes::::get(lane).0 + } + } + + /// Get-parameter that returns number of active outbound lanes that the pallet maintains. + pub struct MaybeOutboundLanesCount(PhantomData<(T, I)>); + + impl, I: 'static> Get> for MaybeOutboundLanesCount { + fn get() -> Option { + Some(T::ActiveOutboundLanes::get().len() as u32) + } + } +} + +impl bp_messages::source_chain::MessagesBridge + for Pallet +where + T: Config, + I: 'static, +{ + type Error = sp_runtime::DispatchErrorWithPostInfo; + + fn send_message( + sender: T::RuntimeOrigin, + lane: LaneId, + message: T::OutboundPayload, + ) -> Result { + crate::send_message::(sender, lane, message) + } +} + +/// Function that actually sends message. +fn send_message, I: 'static>( + submitter: T::RuntimeOrigin, + lane_id: LaneId, + payload: T::OutboundPayload, +) -> sp_std::result::Result< + SendMessageArtifacts, + sp_runtime::DispatchErrorWithPostInfo, +> { + ensure_normal_operating_mode::()?; + + // let's check if outbound lane is active + ensure!(T::ActiveOutboundLanes::get().contains(&lane_id), Error::::InactiveOutboundLane,); + + // let's first check if message can be delivered to target chain + T::TargetHeaderChain::verify_message(&payload).map_err(|err| { + log::trace!( + target: LOG_TARGET, + "Message to lane {:?} is rejected by target chain: {:?}", + lane_id, + err, + ); + + Error::::MessageRejectedByChainVerifier + })?; + + // now let's enforce any additional lane rules + let mut lane = outbound_lane::(lane_id); + T::LaneMessageVerifier::verify_message(&submitter, &lane_id, &lane.data(), &payload).map_err( + |err| { + log::trace!( + target: LOG_TARGET, + "Message to lane {:?} is rejected by lane verifier: {:?}", + lane_id, + err, + ); + + Error::::MessageRejectedByLaneVerifier + }, + )?; + + // finally, save message in outbound storage and emit event + let encoded_payload = payload.encode(); + let encoded_payload_len = encoded_payload.len(); + ensure!( + encoded_payload_len <= T::MaximalOutboundPayloadSize::get() as usize, + Error::::MessageIsTooLarge + ); + let nonce = lane.send_message(encoded_payload); + + log::trace!( + target: LOG_TARGET, + "Accepted message {} to lane {:?}. Message size: {:?}", + nonce, + lane_id, + encoded_payload_len, + ); + + Pallet::::deposit_event(Event::MessageAccepted { lane_id, nonce }); + + Ok(SendMessageArtifacts { nonce }) +} + +/// Ensure that the pallet is in normal operational mode. +fn ensure_normal_operating_mode, I: 'static>() -> Result<(), Error> { + if PalletOperatingMode::::get() == + MessagesOperatingMode::Basic(BasicOperatingMode::Normal) + { + return Ok(()) + } + + Err(Error::::NotOperatingNormally) +} + +/// Creates new inbound lane object, backed by runtime storage. +fn inbound_lane, I: 'static>( + lane_id: LaneId, +) -> InboundLane> { + InboundLane::new(inbound_lane_storage::(lane_id)) +} + +/// Creates new runtime inbound lane storage. +fn inbound_lane_storage, I: 'static>( + lane_id: LaneId, +) -> RuntimeInboundLaneStorage { + RuntimeInboundLaneStorage { + lane_id, + cached_data: RefCell::new(None), + _phantom: Default::default(), + } +} + +/// Creates new outbound lane object, backed by runtime storage. +fn outbound_lane, I: 'static>( + lane_id: LaneId, +) -> OutboundLane> { + OutboundLane::new(RuntimeOutboundLaneStorage { lane_id, _phantom: Default::default() }) +} + +/// Runtime inbound lane storage. +struct RuntimeInboundLaneStorage, I: 'static = ()> { + lane_id: LaneId, + cached_data: RefCell>>, + _phantom: PhantomData, +} + +impl, I: 'static> RuntimeInboundLaneStorage { + /// Returns number of bytes that may be subtracted from the PoV component of + /// `receive_messages_proof` call, because the actual inbound lane state is smaller than the + /// maximal configured. + /// + /// Maximal inbound lane state set size is configured by the + /// `MaxUnrewardedRelayerEntriesAtInboundLane` constant from the pallet configuration. The PoV + /// of the call includes the maximal size of inbound lane state. If the actual size is smaller, + /// we may subtract extra bytes from this component. + pub fn extra_proof_size_bytes(&self) -> u64 { + let max_encoded_len = StoredInboundLaneData::::max_encoded_len(); + let relayers_count = self.data().relayers.len(); + let actual_encoded_len = + InboundLaneData::::encoded_size_hint(relayers_count) + .unwrap_or(usize::MAX); + max_encoded_len.saturating_sub(actual_encoded_len) as _ + } +} + +impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage { + type Relayer = T::InboundRelayer; + + fn id(&self) -> LaneId { + self.lane_id + } + + fn max_unrewarded_relayer_entries(&self) -> MessageNonce { + T::MaxUnrewardedRelayerEntriesAtInboundLane::get() + } + + fn max_unconfirmed_messages(&self) -> MessageNonce { + T::MaxUnconfirmedMessagesAtInboundLane::get() + } + + fn data(&self) -> InboundLaneData { + match self.cached_data.clone().into_inner() { + Some(data) => data, + None => { + let data: InboundLaneData = + InboundLanes::::get(self.lane_id).into(); + *self.cached_data.try_borrow_mut().expect( + "we're in the single-threaded environment;\ + we have no recursive borrows; qed", + ) = Some(data.clone()); + data + }, + } + } + + fn set_data(&mut self, data: InboundLaneData) { + *self.cached_data.try_borrow_mut().expect( + "we're in the single-threaded environment;\ + we have no recursive borrows; qed", + ) = Some(data.clone()); + InboundLanes::::insert(self.lane_id, StoredInboundLaneData::(data)) + } +} + +/// Runtime outbound lane storage. +struct RuntimeOutboundLaneStorage { + lane_id: LaneId, + _phantom: PhantomData<(T, I)>, +} + +impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { + fn id(&self) -> LaneId { + self.lane_id + } + + fn data(&self) -> OutboundLaneData { + OutboundLanes::::get(self.lane_id) + } + + fn set_data(&mut self, data: OutboundLaneData) { + OutboundLanes::::insert(self.lane_id, data) + } + + #[cfg(test)] + fn message(&self, nonce: &MessageNonce) -> Option { + OutboundMessages::::get(MessageKey { lane_id: self.lane_id, nonce: *nonce }) + .map(Into::into) + } + + fn save_message(&mut self, nonce: MessageNonce, message_payload: MessagePayload) { + OutboundMessages::::insert( + MessageKey { lane_id: self.lane_id, nonce }, + StoredMessagePayload::::try_from(message_payload).expect( + "save_message is called after all checks in send_message; \ + send_message checks message size; \ + qed", + ), + ); + } + + fn remove_message(&mut self, nonce: &MessageNonce) { + OutboundMessages::::remove(MessageKey { lane_id: self.lane_id, nonce: *nonce }); + } +} + +/// Verify messages proof and return proved messages with decoded payload. +fn verify_and_decode_messages_proof( + proof: Chain::MessagesProof, + messages_count: u32, +) -> Result>, Chain::Error> { + // `receive_messages_proof` weight formula and `MaxUnconfirmedMessagesAtInboundLane` check + // guarantees that the `message_count` is sane and Vec may be allocated. + // (tx with too many messages will either be rejected from the pool, or will fail earlier) + Chain::verify_messages_proof(proof, messages_count).map(|messages_by_lane| { + messages_by_lane + .into_iter() + .map(|(lane, lane_data)| { + ( + lane, + ProvedLaneMessages { + lane_state: lane_data.lane_state, + messages: lane_data.messages.into_iter().map(Into::into).collect(), + }, + ) + }) + .collect() + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{ + message, message_payload, run_test, unrewarded_relayer, AccountId, DbWeight, + RuntimeEvent as TestEvent, RuntimeOrigin, TestDeliveryConfirmationPayments, + TestDeliveryPayments, TestMessagesDeliveryProof, TestMessagesProof, TestRelayer, + TestRuntime, TestWeightInfo, MAX_OUTBOUND_PAYLOAD_SIZE, PAYLOAD_REJECTED_BY_TARGET_CHAIN, + REGULAR_PAYLOAD, TEST_LANE_ID, TEST_LANE_ID_2, TEST_LANE_ID_3, TEST_RELAYER_A, + TEST_RELAYER_B, + }; + use bp_messages::{BridgeMessagesCall, UnrewardedRelayer, UnrewardedRelayersState}; + use bp_test_utils::generate_owned_bridge_module_tests; + use frame_support::{ + assert_noop, assert_ok, + dispatch::Pays, + storage::generator::{StorageMap, StorageValue}, + traits::Hooks, + weights::Weight, + }; + use frame_system::{EventRecord, Pallet as System, Phase}; + use sp_runtime::DispatchError; + + fn get_ready_for_events() { + System::::set_block_number(1); + System::::reset_events(); + } + + fn inbound_unrewarded_relayers_state( + lane: bp_messages::LaneId, + ) -> bp_messages::UnrewardedRelayersState { + let inbound_lane_data = InboundLanes::::get(lane).0; + let last_delivered_nonce = inbound_lane_data.last_delivered_nonce(); + let relayers = inbound_lane_data.relayers; + bp_messages::UnrewardedRelayersState { + unrewarded_relayer_entries: relayers.len() as _, + messages_in_oldest_entry: relayers + .front() + .map(|entry| 1 + entry.messages.end - entry.messages.begin) + .unwrap_or(0), + total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX), + last_delivered_nonce, + } + } + + fn send_regular_message() { + get_ready_for_events(); + + let message_nonce = + outbound_lane::(TEST_LANE_ID).data().latest_generated_nonce + 1; + send_message::(RuntimeOrigin::signed(1), TEST_LANE_ID, REGULAR_PAYLOAD) + .expect("send_message has failed"); + + // check event with assigned nonce + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Messages(Event::MessageAccepted { + lane_id: TEST_LANE_ID, + nonce: message_nonce + }), + topics: vec![], + }], + ); + } + + fn receive_messages_delivery_proof() { + System::::set_block_number(1); + System::::reset_events(); + + assert_ok!(Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 1, + relayers: vec![UnrewardedRelayer { + relayer: 0, + messages: DeliveredMessages::new(1), + }] + .into_iter() + .collect(), + }, + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + last_delivered_nonce: 1, + ..Default::default() + }, + )); + + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Messages(Event::MessagesDelivered { + lane_id: TEST_LANE_ID, + messages: DeliveredMessages::new(1), + }), + topics: vec![], + }], + ); + } + + #[test] + fn pallet_rejects_transactions_if_halted() { + run_test(|| { + // send message first to be able to check that delivery_proof fails later + send_regular_message(); + + PalletOperatingMode::::put(MessagesOperatingMode::Basic( + BasicOperatingMode::Halted, + )); + + assert_noop!( + send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + REGULAR_PAYLOAD, + ), + Error::::NotOperatingNormally, + ); + + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok(vec![message(2, REGULAR_PAYLOAD)]).into(), + 1, + REGULAR_PAYLOAD.declared_weight, + ), + Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), + ); + + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 1, + relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] + .into_iter() + .collect(), + }, + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }, + ), + Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), + ); + }); + } + + #[test] + fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { + run_test(|| { + // send message first to be able to check that delivery_proof fails later + send_regular_message(); + + PalletOperatingMode::::put( + MessagesOperatingMode::RejectingOutboundMessages, + ); + + assert_noop!( + send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + REGULAR_PAYLOAD, + ), + Error::::NotOperatingNormally, + ); + + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), + 1, + REGULAR_PAYLOAD.declared_weight, + ),); + + assert_ok!(Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 1, + relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] + .into_iter() + .collect(), + }, + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }, + )); + }); + } + + #[test] + fn send_message_works() { + run_test(|| { + send_regular_message(); + }); + } + + #[test] + fn send_message_rejects_too_large_message() { + run_test(|| { + let mut message_payload = message_payload(1, 0); + // the payload isn't simply extra, so it'll definitely overflow + // `MAX_OUTBOUND_PAYLOAD_SIZE` if we add `MAX_OUTBOUND_PAYLOAD_SIZE` bytes to extra + message_payload + .extra + .extend_from_slice(&[0u8; MAX_OUTBOUND_PAYLOAD_SIZE as usize]); + assert_noop!( + send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + message_payload.clone(), + ), + Error::::MessageIsTooLarge, + ); + + // let's check that we're able to send `MAX_OUTBOUND_PAYLOAD_SIZE` messages + while message_payload.encoded_size() as u32 > MAX_OUTBOUND_PAYLOAD_SIZE { + message_payload.extra.pop(); + } + assert_eq!(message_payload.encoded_size() as u32, MAX_OUTBOUND_PAYLOAD_SIZE); + assert_ok!(send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + message_payload, + ),); + }) + } + + #[test] + fn chain_verifier_rejects_invalid_message_in_send_message() { + run_test(|| { + // messages with this payload are rejected by target chain verifier + assert_noop!( + send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + PAYLOAD_REJECTED_BY_TARGET_CHAIN, + ), + Error::::MessageRejectedByChainVerifier, + ); + }); + } + + #[test] + fn lane_verifier_rejects_invalid_message_in_send_message() { + run_test(|| { + // messages with zero fee are rejected by lane verifier + let mut message = REGULAR_PAYLOAD; + message.reject_by_lane_verifier = true; + assert_noop!( + send_message::(RuntimeOrigin::signed(1), TEST_LANE_ID, message,), + Error::::MessageRejectedByLaneVerifier, + ); + }); + } + + #[test] + fn receive_messages_proof_works() { + run_test(|| { + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), + 1, + REGULAR_PAYLOAD.declared_weight, + )); + + assert_eq!(InboundLanes::::get(TEST_LANE_ID).0.last_delivered_nonce(), 1); + + assert!(TestDeliveryPayments::is_reward_paid(1)); + }); + } + + #[test] + fn receive_messages_proof_updates_confirmed_message_nonce() { + run_test(|| { + // say we have received 10 messages && last confirmed message is 8 + InboundLanes::::insert( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 8, + relayers: vec![ + unrewarded_relayer(9, 9, TEST_RELAYER_A), + unrewarded_relayer(10, 10, TEST_RELAYER_B), + ] + .into_iter() + .collect(), + }, + ); + assert_eq!( + inbound_unrewarded_relayers_state(TEST_LANE_ID), + UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + messages_in_oldest_entry: 1, + total_messages: 2, + last_delivered_nonce: 10, + }, + ); + + // message proof includes outbound lane state with latest confirmed message updated to 9 + let mut message_proof: TestMessagesProof = + Ok(vec![message(11, REGULAR_PAYLOAD)]).into(); + message_proof.result.as_mut().unwrap()[0].1.lane_state = + Some(OutboundLaneData { latest_received_nonce: 9, ..Default::default() }); + + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + message_proof, + 1, + REGULAR_PAYLOAD.declared_weight, + )); + + assert_eq!( + InboundLanes::::get(TEST_LANE_ID).0, + InboundLaneData { + last_confirmed_nonce: 9, + relayers: vec![ + unrewarded_relayer(10, 10, TEST_RELAYER_B), + unrewarded_relayer(11, 11, TEST_RELAYER_A) + ] + .into_iter() + .collect(), + }, + ); + assert_eq!( + inbound_unrewarded_relayers_state(TEST_LANE_ID), + UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + messages_in_oldest_entry: 1, + total_messages: 2, + last_delivered_nonce: 11, + }, + ); + }); + } + + #[test] + fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enough() { + run_test(|| { + let mut declared_weight = REGULAR_PAYLOAD.declared_weight; + *declared_weight.ref_time_mut() -= 1; + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), + 1, + declared_weight, + ), + Error::::InsufficientDispatchWeight + ); + assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); + }); + } + + #[test] + fn receive_messages_proof_rejects_invalid_proof() { + run_test(|| { + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Err(()).into(), + 1, + Weight::zero(), + ), + Error::::InvalidMessagesProof, + ); + }); + } + + #[test] + fn receive_messages_proof_rejects_proof_with_too_many_messages() { + run_test(|| { + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), + u32::MAX, + Weight::zero(), + ), + Error::::TooManyMessagesInTheProof, + ); + }); + } + + #[test] + fn receive_messages_delivery_proof_works() { + run_test(|| { + send_regular_message(); + receive_messages_delivery_proof(); + + assert_eq!( + OutboundLanes::::get(TEST_LANE_ID).latest_received_nonce, + 1, + ); + }); + } + + #[test] + fn receive_messages_delivery_proof_rewards_relayers() { + run_test(|| { + assert_ok!(send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + REGULAR_PAYLOAD, + )); + assert_ok!(send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID, + REGULAR_PAYLOAD, + )); + + // this reports delivery of message 1 => reward is paid to TEST_RELAYER_A + let single_message_delivery_proof = TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into_iter().collect(), + ..Default::default() + }, + ))); + let single_message_delivery_proof_size = single_message_delivery_proof.size(); + let result = Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + single_message_delivery_proof, + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + last_delivered_nonce: 1, + ..Default::default() + }, + ); + assert_ok!(result); + assert_eq!( + result.unwrap().actual_weight.unwrap(), + TestWeightInfo::receive_messages_delivery_proof_weight( + &PreComputedSize(single_message_delivery_proof_size as _), + &UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + ..Default::default() + }, + ) + ); + assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); + assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); + + // this reports delivery of both message 1 and message 2 => reward is paid only to + // TEST_RELAYER_B + let two_messages_delivery_proof = TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![ + unrewarded_relayer(1, 1, TEST_RELAYER_A), + unrewarded_relayer(2, 2, TEST_RELAYER_B), + ] + .into_iter() + .collect(), + ..Default::default() + }, + ))); + let two_messages_delivery_proof_size = two_messages_delivery_proof.size(); + let result = Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + two_messages_delivery_proof, + UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + total_messages: 2, + last_delivered_nonce: 2, + ..Default::default() + }, + ); + assert_ok!(result); + // even though the pre-dispatch weight was for two messages, the actual weight is + // for single message only + assert_eq!( + result.unwrap().actual_weight.unwrap(), + TestWeightInfo::receive_messages_delivery_proof_weight( + &PreComputedSize(two_messages_delivery_proof_size as _), + &UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + ..Default::default() + }, + ) + ); + assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); + assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); + }); + } + + #[test] + fn receive_messages_delivery_proof_rejects_invalid_proof() { + run_test(|| { + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Err(())), + Default::default(), + ), + Error::::InvalidMessagesDeliveryProof, + ); + }); + } + + #[test] + fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_invalid() { + run_test(|| { + // when number of relayers entries is invalid + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![ + unrewarded_relayer(1, 1, TEST_RELAYER_A), + unrewarded_relayer(2, 2, TEST_RELAYER_B) + ] + .into_iter() + .collect(), + ..Default::default() + } + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 2, + last_delivered_nonce: 2, + ..Default::default() + }, + ), + Error::::InvalidUnrewardedRelayersState, + ); + + // when number of messages is invalid + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![ + unrewarded_relayer(1, 1, TEST_RELAYER_A), + unrewarded_relayer(2, 2, TEST_RELAYER_B) + ] + .into_iter() + .collect(), + ..Default::default() + } + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + total_messages: 1, + last_delivered_nonce: 2, + ..Default::default() + }, + ), + Error::::InvalidUnrewardedRelayersState, + ); + + // when last delivered nonce is invalid + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![ + unrewarded_relayer(1, 1, TEST_RELAYER_A), + unrewarded_relayer(2, 2, TEST_RELAYER_B) + ] + .into_iter() + .collect(), + ..Default::default() + } + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + total_messages: 2, + last_delivered_nonce: 8, + ..Default::default() + }, + ), + Error::::InvalidUnrewardedRelayersState, + ); + }); + } + + #[test] + fn receive_messages_accepts_single_message_with_invalid_payload() { + run_test(|| { + let mut invalid_message = message(1, REGULAR_PAYLOAD); + invalid_message.payload = Vec::new(); + + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok(vec![invalid_message]).into(), + 1, + Weight::zero(), /* weight may be zero in this case (all messages are + * improperly encoded) */ + ),); + + assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 1,); + }); + } + + #[test] + fn receive_messages_accepts_batch_with_message_with_invalid_payload() { + run_test(|| { + let mut invalid_message = message(2, REGULAR_PAYLOAD); + invalid_message.payload = Vec::new(); + + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + Ok( + vec![message(1, REGULAR_PAYLOAD), invalid_message, message(3, REGULAR_PAYLOAD),] + ) + .into(), + 3, + REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight, + ),); + + assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 3,); + }); + } + + #[test] + fn actual_dispatch_weight_does_not_overlow() { + run_test(|| { + let message1 = message(1, message_payload(0, u64::MAX / 2)); + let message2 = message(2, message_payload(0, u64::MAX / 2)); + let message3 = message(3, message_payload(0, u64::MAX / 2)); + + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + // this may cause overflow if source chain storage is invalid + Ok(vec![message1, message2, message3]).into(), + 3, + Weight::MAX, + ), + Error::::InsufficientDispatchWeight + ); + assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); + }); + } + + #[test] + fn ref_time_refund_from_receive_messages_proof_works() { + run_test(|| { + fn submit_with_unspent_weight( + nonce: MessageNonce, + unspent_weight: u64, + ) -> (Weight, Weight) { + let mut payload = REGULAR_PAYLOAD; + *payload.dispatch_result.unspent_weight.ref_time_mut() = unspent_weight; + let proof = Ok(vec![message(nonce, payload)]).into(); + let messages_count = 1; + let pre_dispatch_weight = + ::WeightInfo::receive_messages_proof_weight( + &proof, + messages_count, + REGULAR_PAYLOAD.declared_weight, + ); + let result = Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + proof, + messages_count, + REGULAR_PAYLOAD.declared_weight, + ) + .expect("delivery has failed"); + let post_dispatch_weight = + result.actual_weight.expect("receive_messages_proof always returns Some"); + + // message delivery transactions are never free + assert_eq!(result.pays_fee, Pays::Yes); + + (pre_dispatch_weight, post_dispatch_weight) + } + + // when dispatch is returning `unspent_weight < declared_weight` + let (pre, post) = submit_with_unspent_weight(1, 1); + assert_eq!(post.ref_time(), pre.ref_time() - 1); + + // when dispatch is returning `unspent_weight = declared_weight` + let (pre, post) = + submit_with_unspent_weight(2, REGULAR_PAYLOAD.declared_weight.ref_time()); + assert_eq!( + post.ref_time(), + pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time() + ); + + // when dispatch is returning `unspent_weight > declared_weight` + let (pre, post) = + submit_with_unspent_weight(3, REGULAR_PAYLOAD.declared_weight.ref_time() + 1); + assert_eq!( + post.ref_time(), + pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time() + ); + + // when there's no unspent weight + let (pre, post) = submit_with_unspent_weight(4, 0); + assert_eq!(post.ref_time(), pre.ref_time()); + + // when dispatch is returning `unspent_weight < declared_weight` + let (pre, post) = submit_with_unspent_weight(5, 1); + assert_eq!(post.ref_time(), pre.ref_time() - 1); + }); + } + + #[test] + fn proof_size_refund_from_receive_messages_proof_works() { + run_test(|| { + let max_entries = crate::mock::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize; + + // if there's maximal number of unrewarded relayer entries at the inbound lane, then + // `proof_size` is unchanged in post-dispatch weight + let proof: TestMessagesProof = Ok(vec![message(101, REGULAR_PAYLOAD)]).into(); + let messages_count = 1; + let pre_dispatch_weight = + ::WeightInfo::receive_messages_proof_weight( + &proof, + messages_count, + REGULAR_PAYLOAD.declared_weight, + ); + InboundLanes::::insert( + TEST_LANE_ID, + StoredInboundLaneData(InboundLaneData { + relayers: vec![ + UnrewardedRelayer { + relayer: 42, + messages: DeliveredMessages { begin: 0, end: 100 } + }; + max_entries + ] + .into_iter() + .collect(), + last_confirmed_nonce: 0, + }), + ); + let post_dispatch_weight = Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + proof.clone(), + messages_count, + REGULAR_PAYLOAD.declared_weight, + ) + .unwrap() + .actual_weight + .unwrap(); + assert_eq!(post_dispatch_weight.proof_size(), pre_dispatch_weight.proof_size()); + + // if count of unrewarded relayer entries is less than maximal, then some `proof_size` + // must be refunded + InboundLanes::::insert( + TEST_LANE_ID, + StoredInboundLaneData(InboundLaneData { + relayers: vec![ + UnrewardedRelayer { + relayer: 42, + messages: DeliveredMessages { begin: 0, end: 100 } + }; + max_entries - 1 + ] + .into_iter() + .collect(), + last_confirmed_nonce: 0, + }), + ); + let post_dispatch_weight = Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + proof, + messages_count, + REGULAR_PAYLOAD.declared_weight, + ) + .unwrap() + .actual_weight + .unwrap(); + assert!( + post_dispatch_weight.proof_size() < pre_dispatch_weight.proof_size(), + "Expected post-dispatch PoV {} to be less than pre-dispatch PoV {}", + post_dispatch_weight.proof_size(), + pre_dispatch_weight.proof_size(), + ); + }); + } + + #[test] + fn messages_delivered_callbacks_are_called() { + run_test(|| { + send_regular_message(); + send_regular_message(); + send_regular_message(); + + // messages 1+2 are confirmed in 1 tx, message 3 in a separate tx + // dispatch of message 2 has failed + let mut delivered_messages_1_and_2 = DeliveredMessages::new(1); + delivered_messages_1_and_2.note_dispatched_message(); + let messages_1_and_2_proof = Ok(( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 0, + relayers: vec![UnrewardedRelayer { + relayer: 0, + messages: delivered_messages_1_and_2.clone(), + }] + .into_iter() + .collect(), + }, + )); + let delivered_message_3 = DeliveredMessages::new(3); + let messages_3_proof = Ok(( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 0, + relayers: vec![UnrewardedRelayer { relayer: 0, messages: delivered_message_3 }] + .into_iter() + .collect(), + }, + )); + + // first tx with messages 1+2 + assert_ok!(Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(messages_1_and_2_proof), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 2, + last_delivered_nonce: 2, + ..Default::default() + }, + )); + // second tx with message 3 + assert_ok!(Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(messages_3_proof), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + last_delivered_nonce: 3, + ..Default::default() + }, + )); + }); + } + + #[test] + fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messages_than_expected( + ) { + run_test(|| { + // send message first to be able to check that delivery_proof fails later + send_regular_message(); + + // 1) InboundLaneData declares that the `last_confirmed_nonce` is 1; + // 2) InboundLaneData has no entries => `InboundLaneData::last_delivered_nonce()` + // returns `last_confirmed_nonce`; + // 3) it means that we're going to confirm delivery of messages 1..=1; + // 4) so the number of declared messages (see `UnrewardedRelayersState`) is `0` and + // numer of actually confirmed messages is `1`. + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { last_confirmed_nonce: 1, relayers: Default::default() }, + ))), + UnrewardedRelayersState { last_delivered_nonce: 1, ..Default::default() }, + ), + Error::::TryingToConfirmMoreMessagesThanExpected, + ); + }); + } + + #[test] + fn storage_keys_computed_properly() { + assert_eq!( + PalletOperatingMode::::storage_value_final_key().to_vec(), + bp_messages::storage_keys::operating_mode_key("Messages").0, + ); + + assert_eq!( + OutboundMessages::::storage_map_final_key(MessageKey { + lane_id: TEST_LANE_ID, + nonce: 42 + }), + bp_messages::storage_keys::message_key("Messages", &TEST_LANE_ID, 42).0, + ); + + assert_eq!( + OutboundLanes::::storage_map_final_key(TEST_LANE_ID), + bp_messages::storage_keys::outbound_lane_data_key("Messages", &TEST_LANE_ID).0, + ); + + assert_eq!( + InboundLanes::::storage_map_final_key(TEST_LANE_ID), + bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0, + ); + } + + #[test] + fn inbound_message_details_works() { + run_test(|| { + assert_eq!( + Pallet::::inbound_message_data( + TEST_LANE_ID, + REGULAR_PAYLOAD.encode(), + OutboundMessageDetails { nonce: 0, dispatch_weight: Weight::zero(), size: 0 }, + ), + InboundMessageDetails { dispatch_weight: REGULAR_PAYLOAD.declared_weight }, + ); + }); + } + + #[test] + fn on_idle_callback_respects_remaining_weight() { + run_test(|| { + send_regular_message(); + send_regular_message(); + send_regular_message(); + send_regular_message(); + + assert_ok!(Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 4, + relayers: vec![unrewarded_relayer(1, 4, TEST_RELAYER_A)] + .into_iter() + .collect(), + }, + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 4, + total_messages: 4, + last_delivered_nonce: 4, + }, + )); + + // all 4 messages may be pruned now + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().latest_received_nonce, + 4 + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 1 + ); + System::::set_block_number(2); + + // if passed wight is too low to do anything + let dbw = DbWeight::get(); + assert_eq!( + Pallet::::on_idle(0, dbw.reads_writes(1, 1)), + Weight::zero(), + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 1 + ); + + // if passed wight is enough to prune single message + assert_eq!( + Pallet::::on_idle(0, dbw.reads_writes(1, 2)), + dbw.reads_writes(1, 2), + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 2 + ); + + // if passed wight is enough to prune two more messages + assert_eq!( + Pallet::::on_idle(0, dbw.reads_writes(1, 3)), + dbw.reads_writes(1, 3), + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 4 + ); + + // if passed wight is enough to prune many messages + assert_eq!( + Pallet::::on_idle(0, dbw.reads_writes(100, 100)), + dbw.reads_writes(1, 2), + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 5 + ); + }); + } + + #[test] + fn on_idle_callback_is_rotating_lanes_to_prune() { + run_test(|| { + // send + receive confirmation for lane 1 + send_regular_message(); + receive_messages_delivery_proof(); + // send + receive confirmation for lane 2 + assert_ok!(send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID_2, + REGULAR_PAYLOAD, + )); + assert_ok!(Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID_2, + InboundLaneData { + last_confirmed_nonce: 1, + relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] + .into_iter() + .collect(), + }, + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }, + )); + + // nothing is pruned yet + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().latest_received_nonce, + 1 + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 1 + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID_2).data().latest_received_nonce, + 1 + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, + 1 + ); + + // in block#2.on_idle lane messages of lane 1 are pruned + let dbw = DbWeight::get(); + System::::set_block_number(2); + assert_eq!( + Pallet::::on_idle(0, dbw.reads_writes(100, 100)), + dbw.reads_writes(1, 2), + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 2 + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, + 1 + ); + + // in block#3.on_idle lane messages of lane 2 are pruned + System::::set_block_number(3); + + assert_eq!( + Pallet::::on_idle(0, dbw.reads_writes(100, 100)), + dbw.reads_writes(1, 2), + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, + 2 + ); + assert_eq!( + outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, + 2 + ); + }); + } + + #[test] + fn outbound_message_from_unconfigured_lane_is_rejected() { + run_test(|| { + assert_noop!( + send_message::( + RuntimeOrigin::signed(1), + TEST_LANE_ID_3, + REGULAR_PAYLOAD, + ), + Error::::InactiveOutboundLane, + ); + }); + } + + #[test] + fn test_bridge_messages_call_is_correctly_defined() { + let account_id = 1; + let message_proof: TestMessagesProof = Ok(vec![message(1, REGULAR_PAYLOAD)]).into(); + let message_delivery_proof = TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + last_confirmed_nonce: 1, + relayers: vec![UnrewardedRelayer { + relayer: 0, + messages: DeliveredMessages::new(1), + }] + .into_iter() + .collect(), + }, + ))); + let unrewarded_relayer_state = UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 1, + last_delivered_nonce: 1, + ..Default::default() + }; + + let direct_receive_messages_proof_call = Call::::receive_messages_proof { + relayer_id_at_bridged_chain: account_id, + proof: message_proof.clone(), + messages_count: 1, + dispatch_weight: REGULAR_PAYLOAD.declared_weight, + }; + let indirect_receive_messages_proof_call = BridgeMessagesCall::< + AccountId, + TestMessagesProof, + TestMessagesDeliveryProof, + >::receive_messages_proof { + relayer_id_at_bridged_chain: account_id, + proof: message_proof, + messages_count: 1, + dispatch_weight: REGULAR_PAYLOAD.declared_weight, + }; + assert_eq!( + direct_receive_messages_proof_call.encode(), + indirect_receive_messages_proof_call.encode() + ); + + let direct_receive_messages_delivery_proof_call = + Call::::receive_messages_delivery_proof { + proof: message_delivery_proof.clone(), + relayers_state: unrewarded_relayer_state.clone(), + }; + let indirect_receive_messages_delivery_proof_call = BridgeMessagesCall::< + AccountId, + TestMessagesProof, + TestMessagesDeliveryProof, + >::receive_messages_delivery_proof { + proof: message_delivery_proof, + relayers_state: unrewarded_relayer_state, + }; + assert_eq!( + direct_receive_messages_delivery_proof_call.encode(), + indirect_receive_messages_delivery_proof_call.encode() + ); + } + + generate_owned_bridge_module_tests!( + MessagesOperatingMode::Basic(BasicOperatingMode::Normal), + MessagesOperatingMode::Basic(BasicOperatingMode::Halted) + ); + + #[test] + fn inbound_storage_extra_proof_size_bytes_works() { + fn relayer_entry() -> UnrewardedRelayer { + UnrewardedRelayer { relayer: 42u64, messages: DeliveredMessages { begin: 0, end: 100 } } + } + + fn storage(relayer_entries: usize) -> RuntimeInboundLaneStorage { + RuntimeInboundLaneStorage { + lane_id: Default::default(), + cached_data: RefCell::new(Some(InboundLaneData { + relayers: vec![relayer_entry(); relayer_entries].into_iter().collect(), + last_confirmed_nonce: 0, + })), + _phantom: Default::default(), + } + } + + let max_entries = crate::mock::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize; + + // when we have exactly `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers + assert_eq!(storage(max_entries).extra_proof_size_bytes(), 0); + + // when we have less than `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers + assert_eq!( + storage(max_entries - 1).extra_proof_size_bytes(), + relayer_entry().encode().len() as u64 + ); + assert_eq!( + storage(max_entries - 2).extra_proof_size_bytes(), + 2 * relayer_entry().encode().len() as u64 + ); + + // when we have more than `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers + // (shall not happen in practice) + assert_eq!(storage(max_entries + 1).extra_proof_size_bytes(), 0); + } + + #[test] + fn maybe_outbound_lanes_count_returns_correct_value() { + assert_eq!( + MaybeOutboundLanesCount::::get(), + Some(mock::ActiveOutboundLanes::get().len() as u32) + ); + } +} diff --git a/modules/messages/src/mock.rs b/modules/messages/src/mock.rs new file mode 100644 index 00000000000..75f05b4820a --- /dev/null +++ b/modules/messages/src/mock.rs @@ -0,0 +1,498 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +// From construct_runtime macro +#![allow(clippy::from_over_into)] + +use crate::Config; + +use bp_messages::{ + calc_relayers_rewards, + source_chain::{DeliveryConfirmationPayments, LaneMessageVerifier, TargetHeaderChain}, + target_chain::{ + DeliveryPayments, DispatchMessage, DispatchMessageData, MessageDispatch, + ProvedLaneMessages, ProvedMessages, SourceHeaderChain, + }, + DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, + OutboundLaneData, UnrewardedRelayer, +}; +use bp_runtime::{messages::MessageDispatchResult, Size}; +use codec::{Decode, Encode}; +use frame_support::{ + parameter_types, + traits::ConstU64, + weights::{constants::RocksDbWeight, Weight}, +}; +use scale_info::TypeInfo; +use sp_core::H256; +use sp_runtime::{ + testing::Header as SubstrateHeader, + traits::{BlakeTwo256, ConstU32, IdentityLookup}, + Perbill, +}; +use std::{ + collections::{BTreeMap, VecDeque}, + ops::RangeInclusive, +}; + +pub type AccountId = u64; +pub type Balance = u64; +#[derive(Decode, Encode, Clone, Debug, PartialEq, Eq, TypeInfo)] +pub struct TestPayload { + /// Field that may be used to identify messages. + pub id: u64, + /// Reject this message by lane verifier? + pub reject_by_lane_verifier: bool, + /// Dispatch weight that is declared by the message sender. + pub declared_weight: Weight, + /// Message dispatch result. + /// + /// Note: in correct code `dispatch_result.unspent_weight` will always be <= `declared_weight`, + /// but for test purposes we'll be making it larger than `declared_weight` sometimes. + pub dispatch_result: MessageDispatchResult, + /// Extra bytes that affect payload size. + pub extra: Vec, +} +pub type TestMessageFee = u64; +pub type TestRelayer = u64; +pub type TestDispatchLevelResult = (); + +type Block = frame_system::mocking::MockBlock; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + +use crate as pallet_bridge_messages; + +frame_support::construct_runtime! { + pub enum TestRuntime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Event}, + Messages: pallet_bridge_messages::{Pallet, Call, Event}, + } +} + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 0); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} + +pub type DbWeight = RocksDbWeight; + +impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = SubstrateHeader; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type DbWeight = DbWeight; + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for TestRuntime { + type MaxLocks = (); + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU64<1>; + type AccountStore = frame_system::Pallet; + type WeightInfo = (); + type MaxReserves = (); + type ReserveIdentifier = (); + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; +} + +parameter_types! { + pub const MaxMessagesToPruneAtOnce: u64 = 10; + pub const MaxUnrewardedRelayerEntriesAtInboundLane: u64 = 16; + pub const MaxUnconfirmedMessagesAtInboundLane: u64 = 32; + pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; + pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID, TEST_LANE_ID_2]; +} + +/// weights of messages pallet calls we use in tests. +pub type TestWeightInfo = (); + +impl Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = TestWeightInfo; + type ActiveOutboundLanes = ActiveOutboundLanes; + type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; + + type MaximalOutboundPayloadSize = frame_support::traits::ConstU32; + type OutboundPayload = TestPayload; + + type InboundPayload = TestPayload; + type InboundRelayer = TestRelayer; + type DeliveryPayments = TestDeliveryPayments; + + type TargetHeaderChain = TestTargetHeaderChain; + type LaneMessageVerifier = TestLaneMessageVerifier; + type DeliveryConfirmationPayments = TestDeliveryConfirmationPayments; + + type SourceHeaderChain = TestSourceHeaderChain; + type MessageDispatch = TestMessageDispatch; + type BridgedChainId = TestBridgedChainId; +} + +#[cfg(feature = "runtime-benchmarks")] +impl crate::benchmarking::Config<()> for TestRuntime { + fn bench_lane_id() -> LaneId { + TEST_LANE_ID + } + + fn prepare_message_proof( + params: crate::benchmarking::MessageProofParams, + ) -> (TestMessagesProof, Weight) { + // in mock run we only care about benchmarks correctness, not the benchmark results + // => ignore size related arguments + let (messages, total_dispatch_weight) = + params.message_nonces.into_iter().map(|n| message(n, REGULAR_PAYLOAD)).fold( + (Vec::new(), Weight::zero()), + |(mut messages, total_dispatch_weight), message| { + let weight = REGULAR_PAYLOAD.declared_weight; + messages.push(message); + (messages, total_dispatch_weight.saturating_add(weight)) + }, + ); + let mut proof: TestMessagesProof = Ok(messages).into(); + proof.result.as_mut().unwrap().get_mut(0).unwrap().1.lane_state = params.outbound_lane_data; + (proof, total_dispatch_weight) + } + + fn prepare_message_delivery_proof( + params: crate::benchmarking::MessageDeliveryProofParams, + ) -> TestMessagesDeliveryProof { + // in mock run we only care about benchmarks correctness, not the benchmark results + // => ignore size related arguments + TestMessagesDeliveryProof(Ok((params.lane, params.inbound_lane_data))) + } + + fn is_relayer_rewarded(_relayer: &AccountId) -> bool { + true + } +} + +impl Size for TestPayload { + fn size(&self) -> u32 { + 16 + self.extra.len() as u32 + } +} + +/// Maximal outbound payload size. +pub const MAX_OUTBOUND_PAYLOAD_SIZE: u32 = 4096; + +/// Account that has balance to use in tests. +pub const ENDOWED_ACCOUNT: AccountId = 0xDEAD; + +/// Account id of test relayer. +pub const TEST_RELAYER_A: AccountId = 100; + +/// Account id of additional test relayer - B. +pub const TEST_RELAYER_B: AccountId = 101; + +/// Account id of additional test relayer - C. +pub const TEST_RELAYER_C: AccountId = 102; + +/// Error that is returned by all test implementations. +pub const TEST_ERROR: &str = "Test error"; + +/// Lane that we're using in tests. +pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 1]); + +/// Secondary lane that we're using in tests. +pub const TEST_LANE_ID_2: LaneId = LaneId([0, 0, 0, 2]); + +/// Inactive outbound lane. +pub const TEST_LANE_ID_3: LaneId = LaneId([0, 0, 0, 3]); + +/// Regular message payload. +pub const REGULAR_PAYLOAD: TestPayload = message_payload(0, 50); + +/// Payload that is rejected by `TestTargetHeaderChain`. +pub const PAYLOAD_REJECTED_BY_TARGET_CHAIN: TestPayload = message_payload(1, 50); + +/// Vec of proved messages, grouped by lane. +pub type MessagesByLaneVec = Vec<(LaneId, ProvedLaneMessages)>; + +/// Test messages proof. +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] +pub struct TestMessagesProof { + pub result: Result, +} + +impl Size for TestMessagesProof { + fn size(&self) -> u32 { + 0 + } +} + +impl From, ()>> for TestMessagesProof { + fn from(result: Result, ()>) -> Self { + Self { + result: result.map(|messages| { + let mut messages_by_lane: BTreeMap> = + BTreeMap::new(); + for message in messages { + messages_by_lane.entry(message.key.lane_id).or_default().messages.push(message); + } + messages_by_lane.into_iter().collect() + }), + } + } +} + +/// Messages delivery proof used in tests. +#[derive(Debug, Encode, Decode, Eq, Clone, PartialEq, TypeInfo)] +pub struct TestMessagesDeliveryProof(pub Result<(LaneId, InboundLaneData), ()>); + +impl Size for TestMessagesDeliveryProof { + fn size(&self) -> u32 { + 0 + } +} + +/// Target header chain that is used in tests. +#[derive(Debug, Default)] +pub struct TestTargetHeaderChain; + +impl TargetHeaderChain for TestTargetHeaderChain { + type Error = &'static str; + + type MessagesDeliveryProof = TestMessagesDeliveryProof; + + fn verify_message(payload: &TestPayload) -> Result<(), Self::Error> { + if *payload == PAYLOAD_REJECTED_BY_TARGET_CHAIN { + Err(TEST_ERROR) + } else { + Ok(()) + } + } + + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData), Self::Error> { + proof.0.map_err(|_| TEST_ERROR) + } +} + +/// Lane message verifier that is used in tests. +#[derive(Debug, Default)] +pub struct TestLaneMessageVerifier; + +impl LaneMessageVerifier for TestLaneMessageVerifier { + type Error = &'static str; + + fn verify_message( + _submitter: &RuntimeOrigin, + _lane: &LaneId, + _lane_outbound_data: &OutboundLaneData, + payload: &TestPayload, + ) -> Result<(), Self::Error> { + if !payload.reject_by_lane_verifier { + Ok(()) + } else { + Err(TEST_ERROR) + } + } +} + +/// Reward payments at the target chain during delivery transaction. +#[derive(Debug, Default)] +pub struct TestDeliveryPayments; + +impl TestDeliveryPayments { + /// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is + /// cleared after the call. + pub fn is_reward_paid(relayer: AccountId) -> bool { + let key = (b":delivery-relayer-reward:", relayer).encode(); + frame_support::storage::unhashed::take::(&key).is_some() + } +} + +impl DeliveryPayments for TestDeliveryPayments { + type Error = &'static str; + + fn pay_reward( + relayer: AccountId, + _total_messages: MessageNonce, + _valid_messages: MessageNonce, + _actual_weight: Weight, + ) { + let key = (b":delivery-relayer-reward:", relayer).encode(); + frame_support::storage::unhashed::put(&key, &true); + } +} + +/// Reward payments at the source chain during delivery confirmation transaction. +#[derive(Debug, Default)] +pub struct TestDeliveryConfirmationPayments; + +impl TestDeliveryConfirmationPayments { + /// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is + /// cleared after the call. + pub fn is_reward_paid(relayer: AccountId, fee: TestMessageFee) -> bool { + let key = (b":relayer-reward:", relayer, fee).encode(); + frame_support::storage::unhashed::take::(&key).is_some() + } +} + +impl DeliveryConfirmationPayments for TestDeliveryConfirmationPayments { + type Error = &'static str; + + fn pay_reward( + _lane_id: LaneId, + messages_relayers: VecDeque>, + _confirmation_relayer: &AccountId, + received_range: &RangeInclusive, + ) -> MessageNonce { + let relayers_rewards = calc_relayers_rewards(messages_relayers, received_range); + let rewarded_relayers = relayers_rewards.len(); + for (relayer, reward) in &relayers_rewards { + let key = (b":relayer-reward:", relayer, reward).encode(); + frame_support::storage::unhashed::put(&key, &true); + } + + rewarded_relayers as _ + } +} + +/// Source header chain that is used in tests. +#[derive(Debug)] +pub struct TestSourceHeaderChain; + +impl SourceHeaderChain for TestSourceHeaderChain { + type Error = &'static str; + + type MessagesProof = TestMessagesProof; + + fn verify_messages_proof( + proof: Self::MessagesProof, + _messages_count: u32, + ) -> Result, Self::Error> { + proof.result.map(|proof| proof.into_iter().collect()).map_err(|_| TEST_ERROR) + } +} + +/// Source header chain that is used in tests. +#[derive(Debug)] +pub struct TestMessageDispatch; + +impl MessageDispatch for TestMessageDispatch { + type DispatchPayload = TestPayload; + type DispatchLevelResult = TestDispatchLevelResult; + + fn dispatch_weight(message: &mut DispatchMessage) -> Weight { + match message.data.payload.as_ref() { + Ok(payload) => payload.declared_weight, + Err(_) => Weight::zero(), + } + } + + fn dispatch( + _relayer_account: &AccountId, + message: DispatchMessage, + ) -> MessageDispatchResult { + match message.data.payload.as_ref() { + Ok(payload) => payload.dispatch_result.clone(), + Err(_) => dispatch_result(0), + } + } +} + +/// Return test lane message with given nonce and payload. +pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message { + Message { key: MessageKey { lane_id: TEST_LANE_ID, nonce }, payload: payload.encode() } +} + +/// Return valid outbound message data, constructed from given payload. +pub fn outbound_message_data(payload: TestPayload) -> MessagePayload { + payload.encode() +} + +/// Return valid inbound (dispatch) message data, constructed from given payload. +pub fn inbound_message_data(payload: TestPayload) -> DispatchMessageData { + DispatchMessageData { payload: Ok(payload) } +} + +/// Constructs message payload using given arguments and zero unspent weight. +pub const fn message_payload(id: u64, declared_weight: u64) -> TestPayload { + TestPayload { + id, + reject_by_lane_verifier: false, + declared_weight: Weight::from_parts(declared_weight, 0), + dispatch_result: dispatch_result(0), + extra: Vec::new(), + } +} + +/// Returns message dispatch result with given unspent weight. +pub const fn dispatch_result( + unspent_weight: u64, +) -> MessageDispatchResult { + MessageDispatchResult { + unspent_weight: Weight::from_parts(unspent_weight, 0), + dispatch_level_result: (), + } +} + +/// Constructs unrewarded relayer entry from nonces range and relayer id. +pub fn unrewarded_relayer( + begin: MessageNonce, + end: MessageNonce, + relayer: TestRelayer, +) -> UnrewardedRelayer { + UnrewardedRelayer { relayer, messages: DeliveredMessages { begin, end } } +} + +/// Return test externalities to use in tests. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig:: { balances: vec![(ENDOWED_ACCOUNT, 1_000_000)] } + .assimilate_storage(&mut t) + .unwrap(); + sp_io::TestExternalities::new(t) +} + +/// Run pallet test. +pub fn run_test(test: impl FnOnce() -> T) -> T { + new_test_ext().execute_with(test) +} diff --git a/modules/messages/src/outbound_lane.rs b/modules/messages/src/outbound_lane.rs new file mode 100644 index 00000000000..33a58a40400 --- /dev/null +++ b/modules/messages/src/outbound_lane.rs @@ -0,0 +1,436 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything about outgoing messages sending. + +use crate::Config; + +use bp_messages::{ + DeliveredMessages, LaneId, MessageNonce, MessagePayload, OutboundLaneData, UnrewardedRelayer, +}; +use frame_support::{ + weights::{RuntimeDbWeight, Weight}, + BoundedVec, RuntimeDebug, +}; +use num_traits::Zero; +use sp_std::collections::vec_deque::VecDeque; + +/// Outbound lane storage. +pub trait OutboundLaneStorage { + /// Lane id. + fn id(&self) -> LaneId; + /// Get lane data from the storage. + fn data(&self) -> OutboundLaneData; + /// Update lane data in the storage. + fn set_data(&mut self, data: OutboundLaneData); + /// Returns saved outbound message payload. + #[cfg(test)] + fn message(&self, nonce: &MessageNonce) -> Option; + /// Save outbound message in the storage. + fn save_message(&mut self, nonce: MessageNonce, message_payload: MessagePayload); + /// Remove outbound message from the storage. + fn remove_message(&mut self, nonce: &MessageNonce); +} + +/// Outbound message data wrapper that implements `MaxEncodedLen`. +pub type StoredMessagePayload = BoundedVec>::MaximalOutboundPayloadSize>; + +/// Result of messages receival confirmation. +#[derive(RuntimeDebug, PartialEq, Eq)] +pub enum ReceivalConfirmationResult { + /// New messages have been confirmed by the confirmation transaction. + ConfirmedMessages(DeliveredMessages), + /// Confirmation transaction brings no new confirmation. This may be a result of relayer + /// error or several relayers running. + NoNewConfirmations, + /// Bridged chain is trying to confirm more messages than we have generated. May be a result + /// of invalid bridged chain storage. + FailedToConfirmFutureMessages, + /// The unrewarded relayers vec contains an empty entry. May be a result of invalid bridged + /// chain storage. + EmptyUnrewardedRelayerEntry, + /// The unrewarded relayers vec contains non-consecutive entries. May be a result of invalid + /// bridged chain storage. + NonConsecutiveUnrewardedRelayerEntries, + /// The chain has more messages that need to be confirmed than there is in the proof. + TryingToConfirmMoreMessagesThanExpected(MessageNonce), +} + +/// Outbound messages lane. +pub struct OutboundLane { + storage: S, +} + +impl OutboundLane { + /// Create new outbound lane backed by given storage. + pub fn new(storage: S) -> Self { + OutboundLane { storage } + } + + /// Get this lane data. + pub fn data(&self) -> OutboundLaneData { + self.storage.data() + } + + /// Send message over lane. + /// + /// Returns new message nonce. + pub fn send_message(&mut self, message_payload: MessagePayload) -> MessageNonce { + let mut data = self.storage.data(); + let nonce = data.latest_generated_nonce + 1; + data.latest_generated_nonce = nonce; + + self.storage.save_message(nonce, message_payload); + self.storage.set_data(data); + + nonce + } + + /// Confirm messages delivery. + pub fn confirm_delivery( + &mut self, + max_allowed_messages: MessageNonce, + latest_delivered_nonce: MessageNonce, + relayers: &VecDeque>, + ) -> ReceivalConfirmationResult { + let mut data = self.storage.data(); + if latest_delivered_nonce <= data.latest_received_nonce { + return ReceivalConfirmationResult::NoNewConfirmations + } + if latest_delivered_nonce > data.latest_generated_nonce { + return ReceivalConfirmationResult::FailedToConfirmFutureMessages + } + if latest_delivered_nonce - data.latest_received_nonce > max_allowed_messages { + // that the relayer has declared correct number of messages that the proof contains (it + // is checked outside of the function). But it may happen (but only if this/bridged + // chain storage is corrupted, though) that the actual number of confirmed messages if + // larger than declared. This would mean that 'reward loop' will take more time than the + // weight formula accounts, so we can't allow that. + return ReceivalConfirmationResult::TryingToConfirmMoreMessagesThanExpected( + latest_delivered_nonce - data.latest_received_nonce, + ) + } + + if let Err(e) = ensure_unrewarded_relayers_are_correct(latest_delivered_nonce, relayers) { + return e + } + + let prev_latest_received_nonce = data.latest_received_nonce; + data.latest_received_nonce = latest_delivered_nonce; + self.storage.set_data(data); + + ReceivalConfirmationResult::ConfirmedMessages(DeliveredMessages { + begin: prev_latest_received_nonce + 1, + end: latest_delivered_nonce, + }) + } + + /// Prune at most `max_messages_to_prune` already received messages. + /// + /// Returns weight, consumed by messages pruning and lane state update. + pub fn prune_messages( + &mut self, + db_weight: RuntimeDbWeight, + mut remaining_weight: Weight, + ) -> Weight { + let write_weight = db_weight.writes(1); + let two_writes_weight = write_weight + write_weight; + let mut spent_weight = Weight::zero(); + let mut data = self.storage.data(); + while remaining_weight.all_gte(two_writes_weight) && + data.oldest_unpruned_nonce <= data.latest_received_nonce + { + self.storage.remove_message(&data.oldest_unpruned_nonce); + + spent_weight += write_weight; + remaining_weight -= write_weight; + data.oldest_unpruned_nonce += 1; + } + + if !spent_weight.is_zero() { + spent_weight += write_weight; + self.storage.set_data(data); + } + + spent_weight + } +} + +/// Verifies unrewarded relayers vec. +/// +/// Returns `Err(_)` if unrewarded relayers vec contains invalid data, meaning that the bridged +/// chain has invalid runtime storage. +fn ensure_unrewarded_relayers_are_correct( + latest_received_nonce: MessageNonce, + relayers: &VecDeque>, +) -> Result<(), ReceivalConfirmationResult> { + let mut last_entry_end: Option = None; + for entry in relayers { + // unrewarded relayer entry must have at least 1 unconfirmed message + // (guaranteed by the `InboundLane::receive_message()`) + if entry.messages.end < entry.messages.begin { + return Err(ReceivalConfirmationResult::EmptyUnrewardedRelayerEntry) + } + // every entry must confirm range of messages that follows previous entry range + // (guaranteed by the `InboundLane::receive_message()`) + if let Some(last_entry_end) = last_entry_end { + let expected_entry_begin = last_entry_end.checked_add(1); + if expected_entry_begin != Some(entry.messages.begin) { + return Err(ReceivalConfirmationResult::NonConsecutiveUnrewardedRelayerEntries) + } + } + last_entry_end = Some(entry.messages.end); + // entry can't confirm messages larger than `inbound_lane_data.latest_received_nonce()` + // (guaranteed by the `InboundLane::receive_message()`) + if entry.messages.end > latest_received_nonce { + // technically this will be detected in the next loop iteration as + // `InvalidNumberOfDispatchResults` but to guarantee safety of loop operations below + // this is detected now + return Err(ReceivalConfirmationResult::FailedToConfirmFutureMessages) + } + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + mock::{ + outbound_message_data, run_test, unrewarded_relayer, TestRelayer, TestRuntime, + REGULAR_PAYLOAD, TEST_LANE_ID, + }, + outbound_lane, + }; + use frame_support::weights::constants::RocksDbWeight; + use sp_std::ops::RangeInclusive; + + fn unrewarded_relayers( + nonces: RangeInclusive, + ) -> VecDeque> { + vec![unrewarded_relayer(*nonces.start(), *nonces.end(), 0)] + .into_iter() + .collect() + } + + fn delivered_messages(nonces: RangeInclusive) -> DeliveredMessages { + DeliveredMessages { begin: *nonces.start(), end: *nonces.end() } + } + + fn assert_3_messages_confirmation_fails( + latest_received_nonce: MessageNonce, + relayers: &VecDeque>, + ) -> ReceivalConfirmationResult { + run_test(|| { + let mut lane = outbound_lane::(TEST_LANE_ID); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + assert_eq!(lane.storage.data().latest_generated_nonce, 3); + assert_eq!(lane.storage.data().latest_received_nonce, 0); + let result = lane.confirm_delivery(3, latest_received_nonce, relayers); + assert_eq!(lane.storage.data().latest_generated_nonce, 3); + assert_eq!(lane.storage.data().latest_received_nonce, 0); + result + }) + } + + #[test] + fn send_message_works() { + run_test(|| { + let mut lane = outbound_lane::(TEST_LANE_ID); + assert_eq!(lane.storage.data().latest_generated_nonce, 0); + assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); + assert!(lane.storage.message(&1).is_some()); + assert_eq!(lane.storage.data().latest_generated_nonce, 1); + }); + } + + #[test] + fn confirm_delivery_works() { + run_test(|| { + let mut lane = outbound_lane::(TEST_LANE_ID); + assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); + assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 2); + assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3); + assert_eq!(lane.storage.data().latest_generated_nonce, 3); + assert_eq!(lane.storage.data().latest_received_nonce, 0); + assert_eq!( + lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), + ReceivalConfirmationResult::ConfirmedMessages(delivered_messages(1..=3)), + ); + assert_eq!(lane.storage.data().latest_generated_nonce, 3); + assert_eq!(lane.storage.data().latest_received_nonce, 3); + }); + } + + #[test] + fn confirm_delivery_rejects_nonce_lesser_than_latest_received() { + run_test(|| { + let mut lane = outbound_lane::(TEST_LANE_ID); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + assert_eq!(lane.storage.data().latest_generated_nonce, 3); + assert_eq!(lane.storage.data().latest_received_nonce, 0); + assert_eq!( + lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), + ReceivalConfirmationResult::ConfirmedMessages(delivered_messages(1..=3)), + ); + assert_eq!( + lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), + ReceivalConfirmationResult::NoNewConfirmations, + ); + assert_eq!(lane.storage.data().latest_generated_nonce, 3); + assert_eq!(lane.storage.data().latest_received_nonce, 3); + + assert_eq!( + lane.confirm_delivery(1, 2, &unrewarded_relayers(1..=1)), + ReceivalConfirmationResult::NoNewConfirmations, + ); + assert_eq!(lane.storage.data().latest_generated_nonce, 3); + assert_eq!(lane.storage.data().latest_received_nonce, 3); + }); + } + + #[test] + fn confirm_delivery_rejects_nonce_larger_than_last_generated() { + assert_eq!( + assert_3_messages_confirmation_fails(10, &unrewarded_relayers(1..=10),), + ReceivalConfirmationResult::FailedToConfirmFutureMessages, + ); + } + + #[test] + fn confirm_delivery_fails_if_entry_confirms_future_messages() { + assert_eq!( + assert_3_messages_confirmation_fails( + 3, + &unrewarded_relayers(1..=1) + .into_iter() + .chain(unrewarded_relayers(2..=30).into_iter()) + .chain(unrewarded_relayers(3..=3).into_iter()) + .collect(), + ), + ReceivalConfirmationResult::FailedToConfirmFutureMessages, + ); + } + + #[test] + #[allow(clippy::reversed_empty_ranges)] + fn confirm_delivery_fails_if_entry_is_empty() { + assert_eq!( + assert_3_messages_confirmation_fails( + 3, + &unrewarded_relayers(1..=1) + .into_iter() + .chain(unrewarded_relayers(2..=1).into_iter()) + .chain(unrewarded_relayers(2..=3).into_iter()) + .collect(), + ), + ReceivalConfirmationResult::EmptyUnrewardedRelayerEntry, + ); + } + + #[test] + fn confirm_delivery_fails_if_entries_are_non_consecutive() { + assert_eq!( + assert_3_messages_confirmation_fails( + 3, + &unrewarded_relayers(1..=1) + .into_iter() + .chain(unrewarded_relayers(3..=3).into_iter()) + .chain(unrewarded_relayers(2..=2).into_iter()) + .collect(), + ), + ReceivalConfirmationResult::NonConsecutiveUnrewardedRelayerEntries, + ); + } + + #[test] + fn prune_messages_works() { + run_test(|| { + let mut lane = outbound_lane::(TEST_LANE_ID); + // when lane is empty, nothing is pruned + assert_eq!( + lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), + Weight::zero() + ); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); + // when nothing is confirmed, nothing is pruned + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + assert!(lane.storage.message(&1).is_some()); + assert!(lane.storage.message(&2).is_some()); + assert!(lane.storage.message(&3).is_some()); + assert_eq!( + lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), + Weight::zero() + ); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); + // after confirmation, some messages are received + assert_eq!( + lane.confirm_delivery(2, 2, &unrewarded_relayers(1..=2)), + ReceivalConfirmationResult::ConfirmedMessages(delivered_messages(1..=2)), + ); + assert_eq!( + lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), + RocksDbWeight::get().writes(3), + ); + assert!(lane.storage.message(&1).is_none()); + assert!(lane.storage.message(&2).is_none()); + assert!(lane.storage.message(&3).is_some()); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 3); + // after last message is confirmed, everything is pruned + assert_eq!( + lane.confirm_delivery(1, 3, &unrewarded_relayers(3..=3)), + ReceivalConfirmationResult::ConfirmedMessages(delivered_messages(3..=3)), + ); + assert_eq!( + lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), + RocksDbWeight::get().writes(2), + ); + assert!(lane.storage.message(&1).is_none()); + assert!(lane.storage.message(&2).is_none()); + assert!(lane.storage.message(&3).is_none()); + assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4); + }); + } + + #[test] + fn confirm_delivery_detects_when_more_than_expected_messages_are_confirmed() { + run_test(|| { + let mut lane = outbound_lane::(TEST_LANE_ID); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); + assert_eq!( + lane.confirm_delivery(0, 3, &unrewarded_relayers(1..=3)), + ReceivalConfirmationResult::TryingToConfirmMoreMessagesThanExpected(3), + ); + assert_eq!( + lane.confirm_delivery(2, 3, &unrewarded_relayers(1..=3)), + ReceivalConfirmationResult::TryingToConfirmMoreMessagesThanExpected(3), + ); + assert_eq!( + lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), + ReceivalConfirmationResult::ConfirmedMessages(delivered_messages(1..=3)), + ); + }); + } +} diff --git a/modules/messages/src/weights.rs b/modules/messages/src/weights.rs new file mode 100644 index 00000000000..baaef317241 --- /dev/null +++ b/modules/messages/src/weights.rs @@ -0,0 +1,525 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Autogenerated weights for RialtoMessages +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-03-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/millau-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=RialtoMessages +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./modules/messages/src/weights.rs +// --template=./.maintain/millau-weight-template.hbs + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for RialtoMessages. +pub trait WeightInfo { + fn receive_single_message_proof() -> Weight; + fn receive_two_messages_proof() -> Weight; + fn receive_single_message_proof_with_outbound_lane_state() -> Weight; + fn receive_single_message_proof_1_kb() -> Weight; + fn receive_single_message_proof_16_kb() -> Weight; + fn receive_delivery_proof_for_single_message() -> Weight; + fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight; + fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight; + fn receive_single_message_proof_with_dispatch(i: u32) -> Weight; +} + +/// Weights for `RialtoMessages` that are generated using one of the Bridge testnets. +/// +/// Those weights are test only and must never be used in production. +pub struct BridgeWeight(PhantomData); +impl WeightInfo for BridgeWeight { + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + fn receive_single_message_proof() -> Weight { + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 52_321 nanoseconds. + Weight::from_parts(54_478_000, 57170) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + fn receive_two_messages_proof() -> Weight { + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 64_597 nanoseconds. + Weight::from_parts(69_267_000, 57170) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + fn receive_single_message_proof_with_outbound_lane_state() -> Weight { + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 64_079 nanoseconds. + Weight::from_parts(65_905_000, 57170) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + fn receive_single_message_proof_1_kb() -> Weight { + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 50_588 nanoseconds. + Weight::from_parts(53_544_000, 57170) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + fn receive_single_message_proof_16_kb() -> Weight { + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 78_269 nanoseconds. + Weight::from_parts(81_748_000, 57170) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: + /// 539, mode: MaxEncodedLen) + /// + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, + /// mode: MaxEncodedLen) + fn receive_delivery_proof_for_single_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `579` + // Estimated: `9584` + // Minimum execution time: 45_786 nanoseconds. + Weight::from_parts(47_382_000, 9584) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: + /// 539, mode: MaxEncodedLen) + /// + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, + /// mode: MaxEncodedLen) + fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { + // Proof Size summary in bytes: + // Measured: `596` + // Estimated: `9584` + // Minimum execution time: 44_544 nanoseconds. + Weight::from_parts(45_451_000, 9584) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: + /// 539, mode: MaxEncodedLen) + /// + /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, + /// mode: MaxEncodedLen) + fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { + // Proof Size summary in bytes: + // Measured: `596` + // Estimated: `12124` + // Minimum execution time: 47_344 nanoseconds. + Weight::from_parts(48_311_000, 12124) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + /// + /// The range of component `i` is `[128, 2048]`. + fn receive_single_message_proof_with_dispatch(i: u32) -> Weight { + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 52_385 nanoseconds. + Weight::from_parts(54_919_468, 57170) + // Standard Error: 108 + .saturating_add(Weight::from_parts(3_286, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + fn receive_single_message_proof() -> Weight { + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 52_321 nanoseconds. + Weight::from_parts(54_478_000, 57170) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + fn receive_two_messages_proof() -> Weight { + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 64_597 nanoseconds. + Weight::from_parts(69_267_000, 57170) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + fn receive_single_message_proof_with_outbound_lane_state() -> Weight { + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 64_079 nanoseconds. + Weight::from_parts(65_905_000, 57170) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + fn receive_single_message_proof_1_kb() -> Weight { + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 50_588 nanoseconds. + Weight::from_parts(53_544_000, 57170) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + fn receive_single_message_proof_16_kb() -> Weight { + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 78_269 nanoseconds. + Weight::from_parts(81_748_000, 57170) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: + /// 539, mode: MaxEncodedLen) + /// + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, + /// mode: MaxEncodedLen) + fn receive_delivery_proof_for_single_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `579` + // Estimated: `9584` + // Minimum execution time: 45_786 nanoseconds. + Weight::from_parts(47_382_000, 9584) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: + /// 539, mode: MaxEncodedLen) + /// + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, + /// mode: MaxEncodedLen) + fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { + // Proof Size summary in bytes: + // Measured: `596` + // Estimated: `9584` + // Minimum execution time: 44_544 nanoseconds. + Weight::from_parts(45_451_000, 9584) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: + /// 539, mode: MaxEncodedLen) + /// + /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, + /// mode: MaxEncodedLen) + fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { + // Proof Size summary in bytes: + // Measured: `596` + // Estimated: `12124` + // Minimum execution time: 47_344 nanoseconds. + Weight::from_parts(48_311_000, 12124) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), + /// added: 497, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: + /// 51655, mode: MaxEncodedLen) + /// + /// The range of component `i` is `[128, 2048]`. + fn receive_single_message_proof_with_dispatch(i: u32) -> Weight { + // Proof Size summary in bytes: + // Measured: `618` + // Estimated: `57170` + // Minimum execution time: 52_385 nanoseconds. + Weight::from_parts(54_919_468, 57170) + // Standard Error: 108 + .saturating_add(Weight::from_parts(3_286, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/modules/messages/src/weights_ext.rs b/modules/messages/src/weights_ext.rs new file mode 100644 index 00000000000..090c03390ba --- /dev/null +++ b/modules/messages/src/weights_ext.rs @@ -0,0 +1,444 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Weight-related utilities. + +use crate::weights::WeightInfo; + +use bp_messages::{MessageNonce, UnrewardedRelayersState}; +use bp_runtime::{PreComputedSize, Size}; +use frame_support::weights::Weight; + +/// Size of the message being delivered in benchmarks. +pub const EXPECTED_DEFAULT_MESSAGE_LENGTH: u32 = 128; + +/// We assume that size of signed extensions on all our chains and size of all 'small' arguments of +/// calls we're checking here would fit 1KB. +const SIGNED_EXTENSIONS_SIZE: u32 = 1024; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// Rialto chain. This mostly depends on number of entries (and their density) in the storage trie. +/// Some reserve is reserved to account future chain growth. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// Ensure that weights from `WeightInfoExt` implementation are looking correct. +pub fn ensure_weights_are_correct() { + // all components of weight formulae must have zero `proof_size`, because the `proof_size` is + // benchmarked using `MaxEncodedLen` approach and there are no components that cause additional + // db reads + + // verify `receive_messages_proof` weight components + assert_ne!(W::receive_messages_proof_overhead().ref_time(), 0); + assert_ne!(W::receive_messages_proof_overhead().proof_size(), 0); + // W::receive_messages_proof_messages_overhead(1).ref_time() may be zero because: + // the message processing code (`InboundLane::receive_message`) is minimal and may not be + // accounted by our benchmarks + assert_eq!(W::receive_messages_proof_messages_overhead(1).proof_size(), 0); + // W::receive_messages_proof_outbound_lane_state_overhead().ref_time() may be zero because: + // the outbound lane state processing code (`InboundLane::receive_state_update`) is minimal and + // may not be accounted by our benchmarks + assert_eq!(W::receive_messages_proof_outbound_lane_state_overhead().proof_size(), 0); + assert_ne!(W::storage_proof_size_overhead(1).ref_time(), 0); + assert_eq!(W::storage_proof_size_overhead(1).proof_size(), 0); + + // verify `receive_messages_delivery_proof` weight components + assert_ne!(W::receive_messages_delivery_proof_overhead().ref_time(), 0); + assert_ne!(W::receive_messages_delivery_proof_overhead().proof_size(), 0); + // W::receive_messages_delivery_proof_messages_overhead(1).ref_time() may be zero because: + // there's no code that iterates over confirmed messages in confirmation transaction + assert_eq!(W::receive_messages_delivery_proof_messages_overhead(1).proof_size(), 0); + assert_ne!(W::receive_messages_delivery_proof_relayers_overhead(1).ref_time(), 0); + // W::receive_messages_delivery_proof_relayers_overhead(1).proof_size() is an exception + // it may or may not cause additional db reads, so proof size may vary + assert_ne!(W::storage_proof_size_overhead(1).ref_time(), 0); + assert_eq!(W::storage_proof_size_overhead(1).proof_size(), 0); + + // verify `receive_message_proof` weight + let receive_messages_proof_weight = + W::receive_messages_proof_weight(&PreComputedSize(1), 10, Weight::zero()); + assert_ne!(receive_messages_proof_weight.ref_time(), 0); + assert_ne!(receive_messages_proof_weight.proof_size(), 0); + messages_proof_size_does_not_affect_proof_size::(); + messages_count_does_not_affect_proof_size::(); + + // verify `receive_message_proof` weight + let receive_messages_delivery_proof_weight = W::receive_messages_delivery_proof_weight( + &PreComputedSize(1), + &UnrewardedRelayersState::default(), + ); + assert_ne!(receive_messages_delivery_proof_weight.ref_time(), 0); + assert_ne!(receive_messages_delivery_proof_weight.proof_size(), 0); + messages_delivery_proof_size_does_not_affect_proof_size::(); + total_messages_in_delivery_proof_does_not_affect_proof_size::(); +} + +/// Ensure that we're able to receive maximal (by-size and by-weight) message from other chain. +pub fn ensure_able_to_receive_message( + max_extrinsic_size: u32, + max_extrinsic_weight: Weight, + max_incoming_message_proof_size: u32, + max_incoming_message_dispatch_weight: Weight, +) { + // verify that we're able to receive proof of maximal-size message + let max_delivery_transaction_size = + max_incoming_message_proof_size.saturating_add(SIGNED_EXTENSIONS_SIZE); + assert!( + max_delivery_transaction_size <= max_extrinsic_size, + "Size of maximal message delivery transaction {max_incoming_message_proof_size} + {SIGNED_EXTENSIONS_SIZE} is larger than maximal possible transaction size {max_extrinsic_size}", + ); + + // verify that we're able to receive proof of maximal-size message with maximal dispatch weight + let max_delivery_transaction_dispatch_weight = W::receive_messages_proof_weight( + &PreComputedSize( + (max_incoming_message_proof_size + W::expected_extra_storage_proof_size()) as usize, + ), + 1, + max_incoming_message_dispatch_weight, + ); + assert!( + max_delivery_transaction_dispatch_weight.all_lte(max_extrinsic_weight), + "Weight of maximal message delivery transaction + {max_delivery_transaction_dispatch_weight} is larger than maximal possible transaction weight {max_extrinsic_weight}", + ); +} + +/// Ensure that we're able to receive maximal confirmation from other chain. +pub fn ensure_able_to_receive_confirmation( + max_extrinsic_size: u32, + max_extrinsic_weight: Weight, + max_inbound_lane_data_proof_size_from_peer_chain: u32, + max_unrewarded_relayer_entries_at_peer_inbound_lane: MessageNonce, + max_unconfirmed_messages_at_inbound_lane: MessageNonce, +) { + // verify that we're able to receive confirmation of maximal-size + let max_confirmation_transaction_size = + max_inbound_lane_data_proof_size_from_peer_chain.saturating_add(SIGNED_EXTENSIONS_SIZE); + assert!( + max_confirmation_transaction_size <= max_extrinsic_size, + "Size of maximal message delivery confirmation transaction {max_inbound_lane_data_proof_size_from_peer_chain} + {SIGNED_EXTENSIONS_SIZE} is larger than maximal possible transaction size {max_extrinsic_size}", + ); + + // verify that we're able to reward maximal number of relayers that have delivered maximal + // number of messages + let max_confirmation_transaction_dispatch_weight = W::receive_messages_delivery_proof_weight( + &PreComputedSize(max_inbound_lane_data_proof_size_from_peer_chain as usize), + &UnrewardedRelayersState { + unrewarded_relayer_entries: max_unrewarded_relayer_entries_at_peer_inbound_lane, + total_messages: max_unconfirmed_messages_at_inbound_lane, + ..Default::default() + }, + ); + assert!( + max_confirmation_transaction_dispatch_weight.all_lte(max_extrinsic_weight), + "Weight of maximal confirmation transaction {max_confirmation_transaction_dispatch_weight} is larger than maximal possible transaction weight {max_extrinsic_weight}", + ); +} + +/// Panics if `proof_size` of message delivery call depends on the message proof size. +fn messages_proof_size_does_not_affect_proof_size() { + let dispatch_weight = Weight::zero(); + let weight_when_proof_size_is_8k = + W::receive_messages_proof_weight(&PreComputedSize(8 * 1024), 1, dispatch_weight); + let weight_when_proof_size_is_16k = + W::receive_messages_proof_weight(&PreComputedSize(16 * 1024), 1, dispatch_weight); + + ensure_weight_components_are_not_zero(weight_when_proof_size_is_8k); + ensure_weight_components_are_not_zero(weight_when_proof_size_is_16k); + ensure_proof_size_is_the_same( + weight_when_proof_size_is_8k, + weight_when_proof_size_is_16k, + "Messages proof size does not affect values that we read from our storage", + ); +} + +/// Panics if `proof_size` of message delivery call depends on the messages count. +/// +/// In practice, it will depend on the messages count, because most probably every +/// message will read something from db during dispatch. But this must be accounted +/// by the `dispatch_weight`. +fn messages_count_does_not_affect_proof_size() { + let messages_proof_size = PreComputedSize(8 * 1024); + let dispatch_weight = Weight::zero(); + let weight_of_one_incoming_message = + W::receive_messages_proof_weight(&messages_proof_size, 1, dispatch_weight); + let weight_of_two_incoming_messages = + W::receive_messages_proof_weight(&messages_proof_size, 2, dispatch_weight); + + ensure_weight_components_are_not_zero(weight_of_one_incoming_message); + ensure_weight_components_are_not_zero(weight_of_two_incoming_messages); + ensure_proof_size_is_the_same( + weight_of_one_incoming_message, + weight_of_two_incoming_messages, + "Number of same-lane incoming messages does not affect values that we read from our storage", + ); +} + +/// Panics if `proof_size` of delivery confirmation call depends on the delivery proof size. +fn messages_delivery_proof_size_does_not_affect_proof_size() { + let relayers_state = UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }; + let weight_when_proof_size_is_8k = + W::receive_messages_delivery_proof_weight(&PreComputedSize(8 * 1024), &relayers_state); + let weight_when_proof_size_is_16k = + W::receive_messages_delivery_proof_weight(&PreComputedSize(16 * 1024), &relayers_state); + + ensure_weight_components_are_not_zero(weight_when_proof_size_is_8k); + ensure_weight_components_are_not_zero(weight_when_proof_size_is_16k); + ensure_proof_size_is_the_same( + weight_when_proof_size_is_8k, + weight_when_proof_size_is_16k, + "Messages delivery proof size does not affect values that we read from our storage", + ); +} + +/// Panics if `proof_size` of delivery confirmation call depends on the number of confirmed +/// messages. +fn total_messages_in_delivery_proof_does_not_affect_proof_size() { + let proof_size = PreComputedSize(8 * 1024); + let weight_when_1k_messages_confirmed = W::receive_messages_delivery_proof_weight( + &proof_size, + &UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1024, + last_delivered_nonce: 1, + }, + ); + let weight_when_2k_messages_confirmed = W::receive_messages_delivery_proof_weight( + &proof_size, + &UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 2048, + last_delivered_nonce: 1, + }, + ); + + ensure_weight_components_are_not_zero(weight_when_1k_messages_confirmed); + ensure_weight_components_are_not_zero(weight_when_2k_messages_confirmed); + ensure_proof_size_is_the_same( + weight_when_1k_messages_confirmed, + weight_when_2k_messages_confirmed, + "More messages in delivery proof does not affect values that we read from our storage", + ); +} + +/// Panics if either Weight' `proof_size` or `ref_time` are zero. +fn ensure_weight_components_are_not_zero(weight: Weight) { + assert_ne!(weight.ref_time(), 0); + assert_ne!(weight.proof_size(), 0); +} + +/// Panics if `proof_size` of `weight1` is not equal to `proof_size` of `weight2`. +fn ensure_proof_size_is_the_same(weight1: Weight, weight2: Weight, msg: &str) { + assert_eq!( + weight1.proof_size(), + weight2.proof_size(), + "{msg}: {} must be equal to {}", + weight1.proof_size(), + weight2.proof_size(), + ); +} + +/// Extended weight info. +pub trait WeightInfoExt: WeightInfo { + /// Size of proof that is already included in the single message delivery weight. + /// + /// The message submitter (at source chain) has already covered this cost. But there are two + /// factors that may increase proof size: (1) the message size may be larger than predefined + /// and (2) relayer may add extra trie nodes to the proof. So if proof size is larger than + /// this value, we're going to charge relayer for that. + fn expected_extra_storage_proof_size() -> u32; + + // Functions that are directly mapped to extrinsics weights. + + /// Weight of message delivery extrinsic. + fn receive_messages_proof_weight( + proof: &impl Size, + messages_count: u32, + dispatch_weight: Weight, + ) -> Weight { + // basic components of extrinsic weight + let transaction_overhead = Self::receive_messages_proof_overhead(); + let outbound_state_delivery_weight = + Self::receive_messages_proof_outbound_lane_state_overhead(); + let messages_delivery_weight = + Self::receive_messages_proof_messages_overhead(MessageNonce::from(messages_count)); + let messages_dispatch_weight = dispatch_weight; + + // proof size overhead weight + let expected_proof_size = EXPECTED_DEFAULT_MESSAGE_LENGTH + .saturating_mul(messages_count.saturating_sub(1)) + .saturating_add(Self::expected_extra_storage_proof_size()); + let actual_proof_size = proof.size(); + let proof_size_overhead = Self::storage_proof_size_overhead( + actual_proof_size.saturating_sub(expected_proof_size), + ); + + transaction_overhead + .saturating_add(outbound_state_delivery_weight) + .saturating_add(messages_delivery_weight) + .saturating_add(messages_dispatch_weight) + .saturating_add(proof_size_overhead) + } + + /// Weight of confirmation delivery extrinsic. + fn receive_messages_delivery_proof_weight( + proof: &impl Size, + relayers_state: &UnrewardedRelayersState, + ) -> Weight { + // basic components of extrinsic weight + let transaction_overhead = Self::receive_messages_delivery_proof_overhead(); + let messages_overhead = + Self::receive_messages_delivery_proof_messages_overhead(relayers_state.total_messages); + let relayers_overhead = Self::receive_messages_delivery_proof_relayers_overhead( + relayers_state.unrewarded_relayer_entries, + ); + + // proof size overhead weight + let expected_proof_size = Self::expected_extra_storage_proof_size(); + let actual_proof_size = proof.size(); + let proof_size_overhead = Self::storage_proof_size_overhead( + actual_proof_size.saturating_sub(expected_proof_size), + ); + + transaction_overhead + .saturating_add(messages_overhead) + .saturating_add(relayers_overhead) + .saturating_add(proof_size_overhead) + } + + // Functions that are used by extrinsics weights formulas. + + /// Returns weight overhead of message delivery transaction (`receive_messages_proof`). + fn receive_messages_proof_overhead() -> Weight { + let weight_of_two_messages_and_two_tx_overheads = + Self::receive_single_message_proof().saturating_mul(2); + let weight_of_two_messages_and_single_tx_overhead = Self::receive_two_messages_proof(); + weight_of_two_messages_and_two_tx_overheads + .saturating_sub(weight_of_two_messages_and_single_tx_overhead) + } + + /// Returns weight that needs to be accounted when receiving given a number of messages with + /// message delivery transaction (`receive_messages_proof`). + fn receive_messages_proof_messages_overhead(messages: MessageNonce) -> Weight { + let weight_of_two_messages_and_single_tx_overhead = Self::receive_two_messages_proof(); + let weight_of_single_message_and_single_tx_overhead = Self::receive_single_message_proof(); + weight_of_two_messages_and_single_tx_overhead + .saturating_sub(weight_of_single_message_and_single_tx_overhead) + .saturating_mul(messages as _) + } + + /// Returns weight that needs to be accounted when message delivery transaction + /// (`receive_messages_proof`) is carrying outbound lane state proof. + fn receive_messages_proof_outbound_lane_state_overhead() -> Weight { + let weight_of_single_message_and_lane_state = + Self::receive_single_message_proof_with_outbound_lane_state(); + let weight_of_single_message = Self::receive_single_message_proof(); + weight_of_single_message_and_lane_state.saturating_sub(weight_of_single_message) + } + + /// Returns weight overhead of delivery confirmation transaction + /// (`receive_messages_delivery_proof`). + fn receive_messages_delivery_proof_overhead() -> Weight { + let weight_of_two_messages_and_two_tx_overheads = + Self::receive_delivery_proof_for_single_message().saturating_mul(2); + let weight_of_two_messages_and_single_tx_overhead = + Self::receive_delivery_proof_for_two_messages_by_single_relayer(); + weight_of_two_messages_and_two_tx_overheads + .saturating_sub(weight_of_two_messages_and_single_tx_overhead) + } + + /// Returns weight that needs to be accounted when receiving confirmations for given a number of + /// messages with delivery confirmation transaction (`receive_messages_delivery_proof`). + fn receive_messages_delivery_proof_messages_overhead(messages: MessageNonce) -> Weight { + let weight_of_two_messages = + Self::receive_delivery_proof_for_two_messages_by_single_relayer(); + let weight_of_single_message = Self::receive_delivery_proof_for_single_message(); + weight_of_two_messages + .saturating_sub(weight_of_single_message) + .saturating_mul(messages as _) + } + + /// Returns weight that needs to be accounted when receiving confirmations for given a number of + /// relayers entries with delivery confirmation transaction (`receive_messages_delivery_proof`). + fn receive_messages_delivery_proof_relayers_overhead(relayers: MessageNonce) -> Weight { + let weight_of_two_messages_by_two_relayers = + Self::receive_delivery_proof_for_two_messages_by_two_relayers(); + let weight_of_two_messages_by_single_relayer = + Self::receive_delivery_proof_for_two_messages_by_single_relayer(); + weight_of_two_messages_by_two_relayers + .saturating_sub(weight_of_two_messages_by_single_relayer) + .saturating_mul(relayers as _) + } + + /// Returns weight that needs to be accounted when storage proof of given size is received + /// (either in `receive_messages_proof` or `receive_messages_delivery_proof`). + /// + /// **IMPORTANT**: this overhead is already included in the 'base' transaction cost - e.g. proof + /// size depends on messages count or number of entries in the unrewarded relayers set. So this + /// shouldn't be added to cost of transaction, but instead should act as a minimal cost that the + /// relayer must pay when it relays proof of given size (even if cost based on other parameters + /// is less than that cost). + fn storage_proof_size_overhead(proof_size: u32) -> Weight { + let proof_size_in_bytes = proof_size; + let byte_weight = (Self::receive_single_message_proof_16_kb() - + Self::receive_single_message_proof_1_kb()) / + (15 * 1024); + proof_size_in_bytes * byte_weight + } + + // Functions that may be used by runtime developers. + + /// Returns dispatch weight of message of given size. + /// + /// This function would return correct value only if your runtime is configured to run + /// `receive_single_message_proof_with_dispatch` benchmark. See its requirements for + /// details. + fn message_dispatch_weight(message_size: u32) -> Weight { + // There may be a tiny overweight/underweight here, because we don't account how message + // size affects all steps before dispatch. But the effect should be small enough and we + // may ignore it. + Self::receive_single_message_proof_with_dispatch(message_size) + .saturating_sub(Self::receive_single_message_proof()) + } +} + +impl WeightInfoExt for () { + fn expected_extra_storage_proof_size() -> u32 { + EXTRA_STORAGE_PROOF_SIZE + } +} + +impl WeightInfoExt for crate::weights::BridgeWeight { + fn expected_extra_storage_proof_size() -> u32 { + EXTRA_STORAGE_PROOF_SIZE + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{mock::TestRuntime, weights::BridgeWeight}; + + #[test] + fn ensure_default_weights_are_correct() { + ensure_weights_are_correct::>(); + } +} diff --git a/modules/parachains/Cargo.toml b/modules/parachains/Cargo.toml new file mode 100644 index 00000000000..39c3ba626aa --- /dev/null +++ b/modules/parachains/Cargo.toml @@ -0,0 +1,60 @@ +[package] +name = "pallet-bridge-parachains" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } + +# Bridge Dependencies + +bp-header-chain = { path = "../../primitives/header-chain", default-features = false } +bp-parachains = { path = "../../primitives/parachains", default-features = false } +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +pallet-bridge-grandpa = { path = "../grandpa", default-features = false } + +# Substrate Dependencies + +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +bp-header-chain = { path = "../../primitives/header-chain" } +bp-test-utils = { path = "../../primitives/test-utils" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "bp-parachains/std", + "bp-polkadot-core/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "frame-system/std", + "frame-benchmarking/std", + "log/std", + "pallet-bridge-grandpa/std", + "scale-info/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/modules/parachains/README.md b/modules/parachains/README.md new file mode 100644 index 00000000000..5982c65ad31 --- /dev/null +++ b/modules/parachains/README.md @@ -0,0 +1,90 @@ +# Bridge Parachains Pallet + +The bridge parachains pallet is a light client for one or several parachains of the bridged relay chain. +It serves as a source of finalized parachain headers and is used when you need to build a bridge with +a parachain. + +The pallet requires [bridge GRANDPA pallet](../grandpa/) to be deployed at the same chain - it is used +to verify storage proofs, generated at the bridged relay chain. + +## A Brief Introduction into Parachains Finality + +You can find detailed information on parachains finality in the [Polkadot](https://github.com/paritytech/polkadot) +and [Cumulus](https://github.com/paritytech/cumulus) repositories. This section gives a brief overview of how +the parachain finality works and how to build a light client for a parachain. + +The main thing there is that the parachain generates blocks on its own, but it can't achieve finality without +help of its relay chain. Instead, the parachain collators create a block and hand it over to the relay chain +validators. Validators validate the block and register the new parachain head in the +[`Heads` map](https://github.com/paritytech/polkadot/blob/88013730166ba90745ae7c9eb3e0c1be1513c7cc/runtime/parachains/src/paras/mod.rs#L645) +of the [`paras`](https://github.com/paritytech/polkadot/tree/master/runtime/parachains/src/paras) pallet, +deployed at the relay chain. Keep in mind that this pallet, deployed at a relay chain, is **NOT** a bridge pallet, +even though the names are similar. + +And what the bridge parachains pallet does, is simply verifying storage proofs of parachain heads within that +`Heads` map. It does that using relay chain header, that has been previously imported by the +[bridge GRANDPA pallet](../grandpa/). Once the proof is verified, the pallet knows that the given parachain +header has been finalized by the relay chain. The parachain header fields may then be used to verify storage +proofs, coming from the parachain. This allows the pallet to be used e.g. as a source of finality for the messages +pallet. + +## Pallet Operations + +The main entrypoint of the pallet is the `submit_parachain_heads` call. It has three arguments: + +- storage proof of parachain heads from the `Heads` map; + +- parachain identifiers and hashes of their heads from the storage proof; + +- the relay block, at which the storage proof has been generated. + +The pallet may track multiple parachains. And the parachains may use different primitives - one may use 128-bit block +numbers, other - 32-bit. To avoid extra decode operations, the pallet is using relay chain block number to order +parachain headers. Any finalized descendant of finalized relay block `RB`, which has parachain block `PB` in +its `Heads` map, is guaranteed to have either `PB`, or its descendant. So parachain block number grows with relay +block number. + +The pallet may reject parachain head if it already knows better (or the same) head. In addition, pallet rejects +heads of untracked parachains. + +The pallet doesn't track anything behind parachain heads. So it requires no initialization - it is ready to accept +headers right after deployment. + +## Non-Essential Functionality + +There may be a special account in every runtime where the bridge parachains module is deployed. This +account, named 'module owner', is like a module-level sudo account - he's able to halt and +resume all module operations without requiring runtime upgrade. Calls that are related to this +account are: + +- `fn set_owner()`: current module owner may call it to transfer "ownership" to another account; + +- `fn set_operating_mode()`: the module owner (or sudo account) may call this function to stop all + module operations. After this call, all finality proofs will be rejected until further `set_operating_mode` call'. + This call may be used when something extraordinary happens with the bridge. + +If pallet owner is not defined, the governance may be used to make those calls. + +## Signed Extension to Reject Obsolete Headers + +It'd be better for anyone (for chain and for submitters) to reject all transactions that are submitting +already known parachain heads to the pallet. This way, we leave block space to other useful transactions and +we don't charge concurrent submitters for their honest actions. + +To deal with that, we have a [signed extension](./src/call_ext) that may be added to the runtime. +It does exactly what is required - rejects all transactions with already known heads. The submitter +pays nothing for such transactions - they're simply removed from the transaction pool, when the block +is built. + +The signed extension, however, is a bit limited - it only works with transactions that provide single +parachain head. So it won't work with multiple parachain heads transactions. This fits our needs +for [Kusama <> Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md). If you need to deal +with other transaction formats, you may implement similar extension for your runtime. + +You may also take a look at the [`generate_bridge_reject_obsolete_headers_and_messages`](../../bin/runtime-common/src/lib.rs) +macro that bundles several similar signed extensions in a single one. + +## Parachains Finality Relay + +We have an offchain actor, who is watching for new parachain heads and submits them to the bridged chain. +It is the parachains relay - you may look at the [crate level documentation and the code](../../relays/parachains/). diff --git a/modules/parachains/src/benchmarking.rs b/modules/parachains/src/benchmarking.rs new file mode 100644 index 00000000000..59c4642cde9 --- /dev/null +++ b/modules/parachains/src/benchmarking.rs @@ -0,0 +1,116 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Parachains finality pallet benchmarking. + +use crate::{ + weights_ext::DEFAULT_PARACHAIN_HEAD_SIZE, Call, RelayBlockHash, RelayBlockHasher, + RelayBlockNumber, +}; + +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use bp_runtime::StorageProofSize; +use frame_benchmarking::{account, benchmarks_instance_pallet}; +use frame_system::RawOrigin; +use sp_std::prelude::*; + +/// Pallet we're benchmarking here. +pub struct Pallet, I: 'static = ()>(crate::Pallet); + +/// Trait that must be implemented by runtime to benchmark the parachains finality pallet. +pub trait Config: crate::Config { + /// Returns vector of supported parachains. + fn parachains() -> Vec; + /// Generate parachain heads proof and prepare environment for verifying this proof. + fn prepare_parachain_heads_proof( + parachains: &[ParaId], + parachain_head_size: u32, + proof_size: StorageProofSize, + ) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>); +} + +benchmarks_instance_pallet! { + where_clause { + where + >::BridgedChain: + bp_runtime::Chain< + BlockNumber = RelayBlockNumber, + Hash = RelayBlockHash, + Hasher = RelayBlockHasher, + >, + } + + // Benchmark `submit_parachain_heads` extrinsic with different number of parachains. + submit_parachain_heads_with_n_parachains { + let p in 1..(T::parachains().len() + 1) as u32; + + let sender = account("sender", 0, 0); + let mut parachains = T::parachains(); + let _ = if p <= parachains.len() as u32 { + parachains.split_off(p as usize) + } else { + Default::default() + }; + log::trace!(target: crate::LOG_TARGET, "=== {:?}", parachains.len()); + let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::Minimal(0), + ); + let at_relay_block = (relay_block_number, relay_block_hash); + }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } + + // Benchmark `submit_parachain_heads` extrinsic with 1kb proof size. + submit_parachain_heads_with_1kb_proof { + let sender = account("sender", 0, 0); + let parachains = vec![T::parachains()[0]]; + let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::HasLargeLeaf(1024), + ); + let at_relay_block = (relay_block_number, relay_block_hash); + }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } + + // Benchmark `submit_parachain_heads` extrinsic with 16kb proof size. + submit_parachain_heads_with_16kb_proof { + let sender = account("sender", 0, 0); + let parachains = vec![T::parachains()[0]]; + let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::HasLargeLeaf(16 * 1024), + ); + let at_relay_block = (relay_block_number, relay_block_hash); + }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) +} diff --git a/modules/parachains/src/call_ext.rs b/modules/parachains/src/call_ext.rs new file mode 100644 index 00000000000..ee3770146c2 --- /dev/null +++ b/modules/parachains/src/call_ext.rs @@ -0,0 +1,243 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{Config, Pallet, RelayBlockNumber}; +use bp_parachains::BestParaHeadHash; +use bp_polkadot_core::parachains::{ParaHash, ParaId}; +use frame_support::{dispatch::CallableCallFor, traits::IsSubType, RuntimeDebug}; +use sp_runtime::transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}; + +/// Info about a `SubmitParachainHeads` call which tries to update a single parachain. +#[derive(PartialEq, RuntimeDebug)] +pub struct SubmitParachainHeadsInfo { + /// Number of the finalized relay block that has been used to prove parachain finality. + pub at_relay_block_number: RelayBlockNumber, + /// Parachain identifier. + pub para_id: ParaId, + /// Hash of the bundled parachain head. + pub para_head_hash: ParaHash, +} + +/// Helper struct that provides methods for working with the `SubmitParachainHeads` call. +pub struct SubmitParachainHeadsHelper, I: 'static> { + _phantom_data: sp_std::marker::PhantomData<(T, I)>, +} + +impl, I: 'static> SubmitParachainHeadsHelper { + /// Check if the para head provided by the `SubmitParachainHeads` is better than the best one + /// we know. + pub fn is_obsolete(update: &SubmitParachainHeadsInfo) -> bool { + let stored_best_head = match crate::ParasInfo::::get(update.para_id) { + Some(stored_best_head) => stored_best_head, + None => return false, + }; + + if stored_best_head.best_head_hash.at_relay_block_number >= update.at_relay_block_number { + log::trace!( + target: crate::LOG_TARGET, + "The parachain head can't be updated. The parachain head for {:?} \ + was already updated at better relay chain block {} >= {}.", + update.para_id, + stored_best_head.best_head_hash.at_relay_block_number, + update.at_relay_block_number + ); + return true + } + + if stored_best_head.best_head_hash.head_hash == update.para_head_hash { + log::trace!( + target: crate::LOG_TARGET, + "The parachain head can't be updated. The parachain head hash for {:?} \ + was already updated to {} at block {} < {}.", + update.para_id, + update.para_head_hash, + stored_best_head.best_head_hash.at_relay_block_number, + update.at_relay_block_number + ); + return true + } + + false + } + + /// Check if the `SubmitParachainHeads` was successfully executed. + pub fn was_successful(update: &SubmitParachainHeadsInfo) -> bool { + match crate::ParasInfo::::get(update.para_id) { + Some(stored_best_head) => + stored_best_head.best_head_hash == + BestParaHeadHash { + at_relay_block_number: update.at_relay_block_number, + head_hash: update.para_head_hash, + }, + None => false, + } + } +} + +/// Trait representing a call that is a sub type of this pallet's call. +pub trait CallSubType, I: 'static>: + IsSubType, T>> +{ + /// Create a new instance of `SubmitParachainHeadsInfo` from a `SubmitParachainHeads` call with + /// one single parachain entry. + fn one_entry_submit_parachain_heads_info(&self) -> Option { + if let Some(crate::Call::::submit_parachain_heads { + ref at_relay_block, + ref parachains, + .. + }) = self.is_sub_type() + { + if let &[(para_id, para_head_hash)] = parachains.as_slice() { + return Some(SubmitParachainHeadsInfo { + at_relay_block_number: at_relay_block.0, + para_id, + para_head_hash, + }) + } + } + + None + } + + /// Create a new instance of `SubmitParachainHeadsInfo` from a `SubmitParachainHeads` call with + /// one single parachain entry, if the entry is for the provided parachain id. + fn submit_parachain_heads_info_for(&self, para_id: u32) -> Option { + self.one_entry_submit_parachain_heads_info() + .filter(|update| update.para_id.0 == para_id) + } + + /// Validate parachain heads in order to avoid "mining" transactions that provide + /// outdated bridged parachain heads. Without this validation, even honest relayers + /// may lose their funds if there are multiple relays running and submitting the + /// same information. + /// + /// This validation only works with transactions that are updating single parachain + /// head. We can't use unbounded validation - it may take too long and either break + /// block production, or "eat" significant portion of block production time literally + /// for nothing. In addition, the single-parachain-head-per-transaction is how the + /// pallet will be used in our environment. + fn check_obsolete_submit_parachain_heads(&self) -> TransactionValidity + where + Self: Sized, + { + let update = match self.one_entry_submit_parachain_heads_info() { + Some(update) => update, + None => return Ok(ValidTransaction::default()), + }; + + if SubmitParachainHeadsHelper::::is_obsolete(&update) { + return InvalidTransaction::Stale.into() + } + + Ok(ValidTransaction::default()) + } +} + +impl CallSubType for T::RuntimeCall +where + T: Config, + T::RuntimeCall: IsSubType, T>>, +{ +} + +#[cfg(test)] +mod tests { + use crate::{ + mock::{run_test, RuntimeCall, TestRuntime}, + CallSubType, ParaInfo, ParasInfo, RelayBlockNumber, + }; + use bp_parachains::BestParaHeadHash; + use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; + + fn validate_submit_parachain_heads( + num: RelayBlockNumber, + parachains: Vec<(ParaId, ParaHash)>, + ) -> bool { + RuntimeCall::Parachains(crate::Call::::submit_parachain_heads { + at_relay_block: (num, Default::default()), + parachains, + parachain_heads_proof: ParaHeadsProof(Vec::new()), + }) + .check_obsolete_submit_parachain_heads() + .is_ok() + } + + fn sync_to_relay_header_10() { + ParasInfo::::insert( + ParaId(1), + ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 10, + head_hash: [1u8; 32].into(), + }, + next_imported_hash_position: 0, + }, + ); + } + + #[test] + fn extension_rejects_header_from_the_obsolete_relay_block() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(5, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_rejects_header_from_the_same_relay_block() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(10, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_rejects_header_from_new_relay_block_with_same_hash() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(20, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_accepts_new_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#15 => tx is + // accepted + sync_to_relay_header_10(); + assert!(validate_submit_parachain_heads(15, vec![(ParaId(1), [2u8; 32].into())])); + }); + } + + #[test] + fn extension_accepts_if_more_than_one_parachain_is_submitted() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5, but another + // parachain head is also supplied => tx is accepted + sync_to_relay_header_10(); + assert!(validate_submit_parachain_heads( + 5, + vec![(ParaId(1), [1u8; 32].into()), (ParaId(2), [1u8; 32].into())] + )); + }); + } +} diff --git a/modules/parachains/src/lib.rs b/modules/parachains/src/lib.rs new file mode 100644 index 00000000000..6c89b09513c --- /dev/null +++ b/modules/parachains/src/lib.rs @@ -0,0 +1,1609 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Parachains finality module. +//! +//! This module needs to be deployed with GRANDPA module, which is syncing relay +//! chain blocks. The main entry point of this module is `submit_parachain_heads`, which +//! accepts storage proof of some parachain `Heads` entries from bridged relay chain. +//! It requires corresponding relay headers to be already synced. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use weights::WeightInfo; +pub use weights_ext::WeightInfoExt; + +use bp_header_chain::{HeaderChain, HeaderChainError}; +use bp_parachains::{parachain_head_storage_key_at_source, ParaInfo, ParaStoredHeaderData}; +use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; +use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain, StorageProofError}; +use frame_support::dispatch::PostDispatchInfo; +use sp_std::{marker::PhantomData, vec::Vec}; + +#[cfg(feature = "runtime-benchmarks")] +use bp_parachains::ParaStoredHeaderDataBuilder; +#[cfg(feature = "runtime-benchmarks")] +use bp_runtime::HeaderOf; +#[cfg(feature = "runtime-benchmarks")] +use codec::Encode; + +// Re-export in crate namespace for `construct_runtime!`. +pub use call_ext::*; +pub use pallet::*; + +pub mod weights; +pub mod weights_ext; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + +mod call_ext; +#[cfg(test)] +mod mock; + +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-parachains"; + +/// Block hash of the bridged relay chain. +pub type RelayBlockHash = bp_polkadot_core::Hash; +/// Block number of the bridged relay chain. +pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; +/// Hasher of the bridged relay chain. +pub type RelayBlockHasher = bp_polkadot_core::Hasher; + +/// Artifacts of the parachains head update. +struct UpdateParachainHeadArtifacts { + /// New best head of the parachain. + pub best_head: ParaInfo, + /// If `true`, some old parachain head has been pruned during update. + pub prune_happened: bool, +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use bp_parachains::{ + BestParaHeadHash, ImportedParaHeadsKeyProvider, ParaStoredHeaderDataBuilder, + ParasInfoKeyProvider, + }; + use bp_runtime::{ + BasicOperatingMode, BoundedStorageValue, OwnedBridgeModule, StorageDoubleMapKeyProvider, + StorageMapKeyProvider, + }; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + /// Stored parachain head data of given parachains pallet. + pub type StoredParaHeadDataOf = + BoundedStorageValue<>::MaxParaHeadDataSize, ParaStoredHeaderData>; + /// Weight info of the given parachains pallet. + pub type WeightInfoOf = >::WeightInfo; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// The caller has provided head of parachain that the pallet is not configured to track. + UntrackedParachainRejected { parachain: ParaId }, + /// The caller has declared that he has provided given parachain head, but it is missing + /// from the storage proof. + MissingParachainHead { parachain: ParaId }, + /// The caller has provided parachain head hash that is not matching the hash read from the + /// storage proof. + IncorrectParachainHeadHash { + parachain: ParaId, + parachain_head_hash: ParaHash, + actual_parachain_head_hash: ParaHash, + }, + /// The caller has provided obsolete parachain head, which is already known to the pallet. + RejectedObsoleteParachainHead { parachain: ParaId, parachain_head_hash: ParaHash }, + /// The caller has provided parachain head that exceeds the maximal configured head size. + RejectedLargeParachainHead { + parachain: ParaId, + parachain_head_hash: ParaHash, + parachain_head_size: u32, + }, + /// Parachain head has been updated. + UpdatedParachainHead { parachain: ParaId, parachain_head_hash: ParaHash }, + } + + #[pallet::error] + pub enum Error { + /// Relay chain block hash is unknown to us. + UnknownRelayChainBlock, + /// The number of stored relay block is different from what the relayer has provided. + InvalidRelayChainBlockNumber, + /// Error generated by a method defined in `bp-header-chain`. + HeaderChain(HeaderChainError), + /// Given parachain head is unknown. + UnknownParaHead, + /// The storage proof doesn't contains storage root. So it is invalid for given header. + StorageRootMismatch, + /// Failed to extract state root from given parachain head. + FailedToExtractStateRoot, + /// Error generated by the `OwnedBridgeModule` trait. + BridgeModule(bp_runtime::OwnedBridgeModuleError), + } + + /// Convenience trait for defining `BridgedChain` bounds. + pub trait BoundedBridgeGrandpaConfig: + pallet_bridge_grandpa::Config + { + type BridgedRelayChain: Chain< + BlockNumber = RelayBlockNumber, + Hash = RelayBlockHash, + Hasher = RelayBlockHasher, + >; + } + + impl BoundedBridgeGrandpaConfig for T + where + T: pallet_bridge_grandpa::Config, + T::BridgedChain: + Chain, + { + type BridgedRelayChain = T::BridgedChain; + } + + #[pallet::config] + #[pallet::disable_frame_system_supertrait_check] + pub trait Config: + BoundedBridgeGrandpaConfig + { + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + /// Benchmarks results from runtime we're plugged into. + type WeightInfo: WeightInfoExt; + + /// Instance of bridges GRANDPA pallet (within this runtime) that this pallet is linked to. + /// + /// The GRANDPA pallet instance must be configured to import headers of relay chain that + /// we're interested in. + type BridgesGrandpaPalletInstance: 'static; + + /// Name of the original `paras` pallet in the `construct_runtime!()` call at the bridged + /// chain. + /// + /// Please keep in mind that this should be the name of the `runtime_parachains::paras` + /// pallet from polkadot repository, not the `pallet-bridge-parachains`. + #[pallet::constant] + type ParasPalletName: Get<&'static str>; + + /// Parachain head data builder. + /// + /// We never store parachain heads here, since they may be too big (e.g. because of large + /// digest items). Instead we're using the same approach as `pallet-bridge-grandpa` + /// pallet - we are only storing `bp_messages::StoredHeaderData` (number and state root), + /// which is enough for our applications. However, we work with different parachains here + /// and they can use different primitives (for block numbers and hash). So we can't store + /// it directly. Instead, we're storing `bp_messages::StoredHeaderData` in SCALE-encoded + /// form, wrapping it into `bp_parachains::ParaStoredHeaderData`. + /// + /// This builder helps to convert from `HeadData` to `bp_parachains::ParaStoredHeaderData`. + type ParaStoredHeaderDataBuilder: ParaStoredHeaderDataBuilder; + + /// Maximal number of single parachain heads to keep in the storage. + /// + /// The setting is there to prevent growing the on-chain state indefinitely. Note + /// the setting does not relate to parachain block numbers - we will simply keep as much + /// items in the storage, so it doesn't guarantee any fixed timeframe for heads. + /// + /// Incautious change of this constant may lead to orphan entries in the runtime storage. + #[pallet::constant] + type HeadsToKeep: Get; + + /// Maximal size (in bytes) of the SCALE-encoded parachain head data + /// (`bp_parachains::ParaStoredHeaderData`). + /// + /// Keep in mind that the size of any tracked parachain header data must not exceed this + /// value. So if you're going to track multiple parachains, one of which is using large + /// hashes, you shall choose this maximal value. + /// + /// There's no mandatory headers in this pallet, so it can't stall if there's some header + /// that exceeds this bound. + #[pallet::constant] + type MaxParaHeadDataSize: Get; + } + + /// Optional pallet owner. + /// + /// Pallet owner has a right to halt all pallet operations and then resume them. If it is + /// `None`, then there are no direct ways to halt/resume pallet operations, but other + /// runtime methods may still be used to do that (i.e. democracy::referendum to update halt + /// flag directly or call the `halt_operations`). + #[pallet::storage] + pub type PalletOwner, I: 'static = ()> = + StorageValue<_, T::AccountId, OptionQuery>; + + /// The current operating mode of the pallet. + /// + /// Depending on the mode either all, or no transactions will be allowed. + #[pallet::storage] + pub type PalletOperatingMode, I: 'static = ()> = + StorageValue<_, BasicOperatingMode, ValueQuery>; + + /// Parachains info. + /// + /// Contains the following info: + /// - best parachain head hash + /// - the head of the `ImportedParaHashes` ring buffer + #[pallet::storage] + pub type ParasInfo, I: 'static = ()> = StorageMap< + Hasher = ::Hasher, + Key = ::Key, + Value = ::Value, + QueryKind = OptionQuery, + OnEmpty = GetDefault, + MaxValues = MaybeMaxParachains, + >; + + /// State roots of parachain heads which have been imported into the pallet. + #[pallet::storage] + pub type ImportedParaHeads, I: 'static = ()> = StorageDoubleMap< + Hasher1 = ::Hasher1, + Key1 = ::Key1, + Hasher2 = ::Hasher2, + Key2 = ::Key2, + Value = StoredParaHeadDataOf, + QueryKind = OptionQuery, + OnEmpty = GetDefault, + MaxValues = MaybeMaxTotalParachainHashes, + >; + + /// A ring buffer of imported parachain head hashes. Ordered by the insertion time. + #[pallet::storage] + pub(super) type ImportedParaHashes, I: 'static = ()> = StorageDoubleMap< + Hasher1 = Blake2_128Concat, + Key1 = ParaId, + Hasher2 = Twox64Concat, + Key2 = u32, + Value = ParaHash, + QueryKind = OptionQuery, + OnEmpty = GetDefault, + MaxValues = MaybeMaxTotalParachainHashes, + >; + + #[pallet::pallet] + pub struct Pallet(PhantomData<(T, I)>); + + impl, I: 'static> OwnedBridgeModule for Pallet { + const LOG_TARGET: &'static str = LOG_TARGET; + type OwnerStorage = PalletOwner; + type OperatingMode = BasicOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + } + + #[pallet::call] + impl, I: 'static> Pallet { + /// Submit proof of one or several parachain heads. + /// + /// The proof is supposed to be proof of some `Heads` entries from the + /// `polkadot-runtime-parachains::paras` pallet instance, deployed at the bridged chain. + /// The proof is supposed to be crafted at the `relay_header_hash` that must already be + /// imported by corresponding GRANDPA pallet at this chain. + /// + /// The call fails if: + /// + /// - the pallet is halted; + /// + /// - the relay chain block `at_relay_block` is not imported by the associated bridge + /// GRANDPA pallet. + /// + /// The call may succeed, but some heads may not be updated e.g. because pallet knows + /// better head or it isn't tracked by the pallet. + #[pallet::call_index(0)] + #[pallet::weight(WeightInfoOf::::submit_parachain_heads_weight( + T::DbWeight::get(), + parachain_heads_proof, + parachains.len() as _, + ))] + pub fn submit_parachain_heads( + _origin: OriginFor, + at_relay_block: (RelayBlockNumber, RelayBlockHash), + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> DispatchResultWithPostInfo { + Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + + // we'll need relay chain header to verify that parachains heads are always increasing. + let (relay_block_number, relay_block_hash) = at_relay_block; + let relay_block = pallet_bridge_grandpa::ImportedHeaders::< + T, + T::BridgesGrandpaPalletInstance, + >::get(relay_block_hash) + .ok_or(Error::::UnknownRelayChainBlock)?; + ensure!( + relay_block.number == relay_block_number, + Error::::InvalidRelayChainBlockNumber, + ); + + // now parse storage proof and read parachain heads + let mut actual_weight = WeightInfoOf::::submit_parachain_heads_weight( + T::DbWeight::get(), + ¶chain_heads_proof, + parachains.len() as _, + ); + + pallet_bridge_grandpa::Pallet::::parse_finalized_storage_proof( + relay_block_hash, + parachain_heads_proof.0, + move |mut storage| { + for (parachain, parachain_head_hash) in parachains { + let parachain_head = match Pallet::::read_parachain_head(&mut storage, parachain) { + Ok(Some(parachain_head)) => parachain_head, + Ok(None) => { + log::trace!( + target: LOG_TARGET, + "The head of parachain {:?} is None. {}", + parachain, + if ParasInfo::::contains_key(parachain) { + "Looks like it is not yet registered at the source relay chain" + } else { + "Looks like it has been deregistered from the source relay chain" + }, + ); + Self::deposit_event(Event::MissingParachainHead { parachain }); + continue; + }, + Err(e) => { + log::trace!( + target: LOG_TARGET, + "The read of head of parachain {:?} has failed: {:?}", + parachain, + e, + ); + Self::deposit_event(Event::MissingParachainHead { parachain }); + continue; + }, + }; + + // if relayer has specified invalid parachain head hash, ignore the head + // (this isn't strictly necessary, but better safe than sorry) + let actual_parachain_head_hash = parachain_head.hash(); + if parachain_head_hash != actual_parachain_head_hash { + log::trace!( + target: LOG_TARGET, + "The submitter has specified invalid parachain {:?} head hash: {:?} vs {:?}", + parachain, + parachain_head_hash, + actual_parachain_head_hash, + ); + Self::deposit_event(Event::IncorrectParachainHeadHash { + parachain, + parachain_head_hash, + actual_parachain_head_hash, + }); + continue; + } + + // convert from parachain head into stored parachain head data + let parachain_head_data = match T::ParaStoredHeaderDataBuilder::try_build( + parachain, + ¶chain_head, + ) { + Some(parachain_head_data) => parachain_head_data, + None => { + log::trace!( + target: LOG_TARGET, + "The head of parachain {:?} has been provided, but it is not tracked by the pallet", + parachain, + ); + Self::deposit_event(Event::UntrackedParachainRejected { parachain }); + continue; + }, + }; + + let update_result: Result<_, ()> = ParasInfo::::try_mutate(parachain, |stored_best_head| { + let artifacts = Pallet::::update_parachain_head( + parachain, + stored_best_head.take(), + relay_block_number, + parachain_head_data, + parachain_head_hash, + )?; + *stored_best_head = Some(artifacts.best_head); + Ok(artifacts.prune_happened) + }); + + // we're refunding weight if update has not happened and if pruning has not happened + let is_update_happened = matches!(update_result, Ok(_)); + if !is_update_happened { + actual_weight = actual_weight + .saturating_sub(WeightInfoOf::::parachain_head_storage_write_weight(T::DbWeight::get())); + } + let is_prune_happened = matches!(update_result, Ok(true)); + if !is_prune_happened { + actual_weight = actual_weight + .saturating_sub(WeightInfoOf::::parachain_head_pruning_weight(T::DbWeight::get())); + } + } + + // even though we may have accepted some parachain heads, we can't allow relayers to submit + // proof with unused trie nodes + // => treat this as an error + // + // (we can throw error here, because now all our calls are transactional) + storage.ensure_no_unused_nodes() + }, + ) + .and_then(|r| r.map_err(HeaderChainError::StorageProof)) + .map_err(|e| { + log::trace!(target: LOG_TARGET, "Parachain heads storage proof is invalid: {:?}", e); + Error::::HeaderChain(e) + })?; + + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) + } + + /// Change `PalletOwner`. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(1)] + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { + >::set_owner(origin, new_owner) + } + + /// Halt or resume all pallet operations. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::call_index(2)] + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_operating_mode( + origin: OriginFor, + operating_mode: BasicOperatingMode, + ) -> DispatchResult { + >::set_operating_mode(origin, operating_mode) + } + } + + impl, I: 'static> Pallet { + /// Get stored parachain info. + pub fn best_parachain_info(parachain: ParaId) -> Option { + ParasInfo::::get(parachain) + } + + /// Get best finalized head data of the given parachain. + pub fn best_parachain_head(parachain: ParaId) -> Option { + let best_para_head_hash = ParasInfo::::get(parachain)?.best_head_hash.head_hash; + ImportedParaHeads::::get(parachain, best_para_head_hash).map(|h| h.into_inner()) + } + + /// Get best finalized head hash of the given parachain. + pub fn best_parachain_head_hash(parachain: ParaId) -> Option { + Some(ParasInfo::::get(parachain)?.best_head_hash.head_hash) + } + + /// Get best finalized head id of the given parachain. + pub fn best_parachain_head_id + Parachain>( + ) -> Result>, codec::Error> { + let parachain = ParaId(C::PARACHAIN_ID); + let best_head_hash = match Self::best_parachain_head_hash(parachain) { + Some(best_head_hash) => best_head_hash, + None => return Ok(None), + }; + let encoded_head = match Self::parachain_head(parachain, best_head_hash) { + Some(encoded_head) => encoded_head, + None => return Ok(None), + }; + encoded_head + .decode_parachain_head_data::() + .map(|data| Some(HeaderId(data.number, best_head_hash))) + } + + /// Get parachain head data with given hash. + pub fn parachain_head(parachain: ParaId, hash: ParaHash) -> Option { + ImportedParaHeads::::get(parachain, hash).map(|h| h.into_inner()) + } + + /// Read parachain head from storage proof. + fn read_parachain_head( + storage: &mut bp_runtime::StorageProofChecker, + parachain: ParaId, + ) -> Result, StorageProofError> { + let parachain_head_key = + parachain_head_storage_key_at_source(T::ParasPalletName::get(), parachain); + storage.read_and_decode_value(parachain_head_key.0.as_ref()) + } + + /// Try to update parachain head. + pub(super) fn update_parachain_head( + parachain: ParaId, + stored_best_head: Option, + new_at_relay_block_number: RelayBlockNumber, + new_head_data: ParaStoredHeaderData, + new_head_hash: ParaHash, + ) -> Result { + // check if head has been already updated at better relay chain block. Without this + // check, we may import heads in random order + let update = SubmitParachainHeadsInfo { + at_relay_block_number: new_at_relay_block_number, + para_id: parachain, + para_head_hash: new_head_hash, + }; + if SubmitParachainHeadsHelper::::is_obsolete(&update) { + Self::deposit_event(Event::RejectedObsoleteParachainHead { + parachain, + parachain_head_hash: new_head_hash, + }); + return Err(()) + } + + // verify that the parachain head data size is <= `MaxParaHeadDataSize` + let updated_head_data = + match StoredParaHeadDataOf::::try_from_inner(new_head_data) { + Ok(updated_head_data) => updated_head_data, + Err(e) => { + log::trace!( + target: LOG_TARGET, + "The parachain head can't be updated. The parachain head data size \ + for {:?} is {}. It exceeds maximal configured size {}.", + parachain, + e.value_size, + e.maximal_size, + ); + + Self::deposit_event(Event::RejectedLargeParachainHead { + parachain, + parachain_head_hash: new_head_hash, + parachain_head_size: e.value_size as _, + }); + + return Err(()) + }, + }; + + let next_imported_hash_position = stored_best_head + .map_or(0, |stored_best_head| stored_best_head.next_imported_hash_position); + + // insert updated best parachain head + let head_hash_to_prune = + ImportedParaHashes::::try_get(parachain, next_imported_hash_position); + let updated_best_para_head = ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: new_at_relay_block_number, + head_hash: new_head_hash, + }, + next_imported_hash_position: (next_imported_hash_position + 1) % + T::HeadsToKeep::get(), + }; + ImportedParaHashes::::insert( + parachain, + next_imported_hash_position, + new_head_hash, + ); + ImportedParaHeads::::insert(parachain, new_head_hash, updated_head_data); + log::trace!( + target: LOG_TARGET, + "Updated head of parachain {:?} to {}", + parachain, + new_head_hash, + ); + + // remove old head + let prune_happened = head_hash_to_prune.is_ok(); + if let Ok(head_hash_to_prune) = head_hash_to_prune { + log::trace!( + target: LOG_TARGET, + "Pruning old head of parachain {:?}: {}", + parachain, + head_hash_to_prune, + ); + ImportedParaHeads::::remove(parachain, head_hash_to_prune); + } + Self::deposit_event(Event::UpdatedParachainHead { + parachain, + parachain_head_hash: new_head_hash, + }); + + Ok(UpdateParachainHeadArtifacts { best_head: updated_best_para_head, prune_happened }) + } + } + + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + /// Initial pallet operating mode. + pub operating_mode: BasicOperatingMode, + /// Initial pallet owner. + pub owner: Option, + /// Dummy marker. + pub phantom: sp_std::marker::PhantomData, + } + + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { + operating_mode: Default::default(), + owner: Default::default(), + phantom: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + PalletOperatingMode::::put(self.operating_mode); + if let Some(ref owner) = self.owner { + PalletOwner::::put(owner); + } + } + } + + /// Returns maximal number of parachains, supported by the pallet. + pub struct MaybeMaxParachains(PhantomData<(T, I)>); + + impl, I: 'static> Get> for MaybeMaxParachains { + fn get() -> Option { + Some(T::ParaStoredHeaderDataBuilder::supported_parachains()) + } + } + + /// Returns total number of all parachains hashes/heads, stored by the pallet. + pub struct MaybeMaxTotalParachainHashes(PhantomData<(T, I)>); + + impl, I: 'static> Get> for MaybeMaxTotalParachainHashes { + fn get() -> Option { + Some( + T::ParaStoredHeaderDataBuilder::supported_parachains() + .saturating_mul(T::HeadsToKeep::get()), + ) + } + } +} + +/// Single parachain header chain adapter. +pub struct ParachainHeaders(PhantomData<(T, I, C)>); + +impl, I: 'static, C: Parachain> HeaderChain + for ParachainHeaders +{ + fn finalized_header_state_root(hash: HashOf) -> Option> { + Pallet::::parachain_head(ParaId(C::PARACHAIN_ID), hash) + .and_then(|head| head.decode_parachain_head_data::().ok()) + .map(|h| h.state_root) + } +} + +/// (Re)initialize pallet with given header for using it in `pallet-bridge-messages` benchmarks. +#[cfg(feature = "runtime-benchmarks")] +pub fn initialize_for_benchmarks, I: 'static, PC: Parachain>( + header: HeaderOf, +) { + let parachain = ParaId(PC::PARACHAIN_ID); + let parachain_head = ParaHead(header.encode()); + let updated_head_data = T::ParaStoredHeaderDataBuilder::try_build(parachain, ¶chain_head) + .expect("failed to build stored parachain head in benchmarks"); + Pallet::::update_parachain_head( + parachain, + None, + 0, + updated_head_data, + parachain_head.hash(), + ) + .expect("failed to insert parachain head in benchmarks"); +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use crate::mock::{ + run_test, test_relay_header, BigParachainHeader, RegularParachainHasher, + RegularParachainHeader, RuntimeEvent as TestEvent, RuntimeOrigin, TestRuntime, + PARAS_PALLET_NAME, UNTRACKED_PARACHAIN_ID, + }; + use codec::Encode; + + use bp_parachains::{ + BestParaHeadHash, BridgeParachainCall, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider, + }; + use bp_runtime::{ + record_all_trie_keys, BasicOperatingMode, OwnedBridgeModuleError, + StorageDoubleMapKeyProvider, StorageMapKeyProvider, + }; + use bp_test_utils::{ + authority_list, generate_owned_bridge_module_tests, make_default_justification, + }; + use frame_support::{ + assert_noop, assert_ok, + dispatch::DispatchResultWithPostInfo, + storage::generator::{StorageDoubleMap, StorageMap}, + traits::{Get, OnInitialize}, + weights::Weight, + }; + use frame_system::{EventRecord, Pallet as System, Phase}; + use sp_core::Hasher; + use sp_runtime::{traits::Header as HeaderT, DispatchError}; + use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; + + type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; + type WeightInfo = ::WeightInfo; + type DbWeight = ::DbWeight; + + pub(crate) fn initialize(state_root: RelayBlockHash) -> RelayBlockHash { + pallet_bridge_grandpa::Pallet::::initialize( + RuntimeOrigin::root(), + bp_header_chain::InitializationData { + header: Box::new(test_relay_header(0, state_root)), + authority_list: authority_list(), + set_id: 1, + operating_mode: BasicOperatingMode::Normal, + }, + ) + .unwrap(); + + System::::set_block_number(1); + System::::reset_events(); + + test_relay_header(0, state_root).hash() + } + + fn proceed(num: RelayBlockNumber, state_root: RelayBlockHash) -> ParaHash { + pallet_bridge_grandpa::Pallet::::on_initialize( + 0, + ); + + let header = test_relay_header(num, state_root); + let hash = header.hash(); + let justification = make_default_justification(&header); + assert_ok!( + pallet_bridge_grandpa::Pallet::::submit_finality_proof( + RuntimeOrigin::signed(1), + Box::new(header), + justification, + ) + ); + + hash + } + + pub(crate) fn prepare_parachain_heads_proof( + heads: Vec<(u32, ParaHead)>, + ) -> (RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) { + let mut parachains = Vec::with_capacity(heads.len()); + let mut root = Default::default(); + let mut mdb = MemoryDB::default(); + { + let mut trie = TrieDBMutBuilderV1::::new(&mut mdb, &mut root).build(); + for (parachain, head) in heads { + let storage_key = + parachain_head_storage_key_at_source(PARAS_PALLET_NAME, ParaId(parachain)); + trie.insert(&storage_key.0, &head.encode()) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in tests"); + parachains.push((ParaId(parachain), head.hash())); + } + } + + // generate storage proof to be delivered to This chain + let storage_proof = record_all_trie_keys::, _>(&mdb, &root) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); + + (root, ParaHeadsProof(storage_proof), parachains) + } + + fn initial_best_head(parachain: u32) -> ParaInfo { + ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(parachain, 0).hash(), + }, + next_imported_hash_position: 1, + } + } + + pub(crate) fn head_data(parachain: u32, head_number: u32) -> ParaHead { + ParaHead( + RegularParachainHeader::new( + head_number as _, + Default::default(), + RegularParachainHasher::hash(&(parachain, head_number).encode()), + Default::default(), + Default::default(), + ) + .encode(), + ) + } + + fn stored_head_data(parachain: u32, head_number: u32) -> ParaStoredHeaderData { + ParaStoredHeaderData( + (head_number as u64, RegularParachainHasher::hash(&(parachain, head_number).encode())) + .encode(), + ) + } + + fn big_head_data(parachain: u32, head_number: u32) -> ParaHead { + ParaHead( + BigParachainHeader::new( + head_number as _, + Default::default(), + RegularParachainHasher::hash(&(parachain, head_number).encode()), + Default::default(), + Default::default(), + ) + .encode(), + ) + } + + fn big_stored_head_data(parachain: u32, head_number: u32) -> ParaStoredHeaderData { + ParaStoredHeaderData( + (head_number as u128, RegularParachainHasher::hash(&(parachain, head_number).encode())) + .encode(), + ) + } + + fn head_hash(parachain: u32, head_number: u32) -> ParaHash { + head_data(parachain, head_number).hash() + } + + fn import_parachain_1_head( + relay_chain_block: RelayBlockNumber, + relay_state_root: RelayBlockHash, + parachains: Vec<(ParaId, ParaHash)>, + proof: ParaHeadsProof, + ) -> DispatchResultWithPostInfo { + Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (relay_chain_block, test_relay_header(relay_chain_block, relay_state_root).hash()), + parachains, + proof, + ) + } + + fn weight_of_import_parachain_1_head(proof: &ParaHeadsProof, prune_expected: bool) -> Weight { + let db_weight = ::DbWeight::get(); + WeightInfoOf::::submit_parachain_heads_weight(db_weight, proof, 1) + .saturating_sub(if prune_expected { + Weight::zero() + } else { + WeightInfoOf::::parachain_head_pruning_weight(db_weight) + }) + } + + #[test] + fn submit_parachain_heads_checks_operating_mode() { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + + run_test(|| { + initialize(state_root); + + // `submit_parachain_heads()` should fail when the pallet is halted. + PalletOperatingMode::::put(BasicOperatingMode::Halted); + assert_noop!( + Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains.clone(), + proof.clone(), + ), + Error::::BridgeModule(OwnedBridgeModuleError::Halted) + ); + + // `submit_parachain_heads()` should succeed now that the pallet is resumed. + PalletOperatingMode::::put(BasicOperatingMode::Normal); + assert_ok!(Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + ),); + }); + } + + #[test] + fn imports_initial_parachain_heads() { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 0)), (3, head_data(3, 10))]); + run_test(|| { + initialize(state_root); + + // we're trying to update heads of parachains 1, 2 and 3 + let expected_weight = + WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 2); + let result = Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + ); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); + + // but only 1 and 2 are updated, because proof is missing head of parachain#2 + assert_eq!(ParasInfo::::get(ParaId(1)), Some(initial_best_head(1))); + assert_eq!(ParasInfo::::get(ParaId(2)), None); + assert_eq!( + ParasInfo::::get(ParaId(3)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(3, 10).hash() + }, + next_imported_hash_position: 1, + }) + ); + + assert_eq!( + ImportedParaHeads::::get( + ParaId(1), + initial_best_head(1).best_head_hash.head_hash + ) + .map(|h| h.into_inner()), + Some(stored_head_data(1, 0)) + ); + assert_eq!( + ImportedParaHeads::::get( + ParaId(2), + initial_best_head(2).best_head_hash.head_hash + ) + .map(|h| h.into_inner()), + None + ); + assert_eq!( + ImportedParaHeads::::get(ParaId(3), head_hash(3, 10)) + .map(|h| h.into_inner()), + Some(stored_head_data(3, 10)) + ); + + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(3), + parachain_head_hash: head_data(3, 10).hash(), + }), + topics: vec![], + } + ], + ); + }); + } + + #[test] + fn imports_parachain_heads_is_able_to_progress() { + let (state_root_5, proof_5, parachains_5) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root_10, proof_10, parachains_10) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); + run_test(|| { + // start with relay block #0 and import head#5 of parachain#1 + initialize(state_root_5); + assert_ok!(import_parachain_1_head(0, state_root_5, parachains_5, proof_5)); + assert_eq!( + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(1, 5).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!( + ImportedParaHeads::::get(ParaId(1), head_data(1, 5).hash()) + .map(|h| h.into_inner()), + Some(stored_head_data(1, 5)) + ); + assert_eq!( + ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()) + .map(|h| h.into_inner()), + None + ); + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + }], + ); + + // import head#10 of parachain#1 at relay block #1 + let relay_1_hash = proceed(1, state_root_10); + assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10)); + assert_eq!( + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 1, + head_hash: head_data(1, 10).hash() + }, + next_imported_hash_position: 2, + }) + ); + assert_eq!( + ImportedParaHeads::::get(ParaId(1), head_data(1, 5).hash()) + .map(|h| h.into_inner()), + Some(stored_head_data(1, 5)) + ); + assert_eq!( + ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()) + .map(|h| h.into_inner()), + Some(stored_head_data(1, 10)) + ); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Grandpa1( + pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { + number: 1, + hash: relay_1_hash, + } + ), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 10).hash(), + }), + topics: vec![], + } + ], + ); + }); + } + + #[test] + fn ignores_untracked_parachain() { + let (state_root, proof, parachains) = prepare_parachain_heads_proof(vec![ + (1, head_data(1, 5)), + (UNTRACKED_PARACHAIN_ID, head_data(1, 5)), + (2, head_data(1, 5)), + ]); + run_test(|| { + // start with relay block #0 and try to import head#5 of parachain#1 and untracked + // parachain + let expected_weight = + WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 3) + .saturating_sub(WeightInfo::parachain_head_storage_write_weight( + DbWeight::get(), + )); + initialize(state_root); + let result = Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + ); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); + assert_eq!( + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(1, 5).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!(ParasInfo::::get(ParaId(UNTRACKED_PARACHAIN_ID)), None,); + assert_eq!( + ParasInfo::::get(ParaId(2)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(1, 5).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UntrackedParachainRejected { + parachain: ParaId(UNTRACKED_PARACHAIN_ID), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(2), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + } + ], + ); + }); + } + + #[test] + fn does_nothing_when_already_imported_this_head_at_previous_relay_header() { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + run_test(|| { + // import head#0 of parachain#1 at relay block#0 + initialize(state_root); + assert_ok!(import_parachain_1_head(0, state_root, parachains.clone(), proof.clone())); + assert_eq!(ParasInfo::::get(ParaId(1)), Some(initial_best_head(1))); + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, + }), + topics: vec![], + }], + ); + + // try to import head#0 of parachain#1 at relay block#1 + // => call succeeds, but nothing is changed + let relay_1_hash = proceed(1, state_root); + assert_ok!(import_parachain_1_head(1, state_root, parachains, proof)); + assert_eq!(ParasInfo::::get(ParaId(1)), Some(initial_best_head(1))); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Grandpa1( + pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { + number: 1, + hash: relay_1_hash, + } + ), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead { + parachain: ParaId(1), + parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, + }), + topics: vec![], + } + ], + ); + }); + } + + #[test] + fn does_nothing_when_already_imported_head_at_better_relay_header() { + let (state_root_5, proof_5, parachains_5) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root_10, proof_10, parachains_10) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); + run_test(|| { + // start with relay block #0 + initialize(state_root_5); + + // head#10 of parachain#1 at relay block#1 + let relay_1_hash = proceed(1, state_root_10); + assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10)); + assert_eq!( + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 1, + head_hash: head_data(1, 10).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Grandpa1( + pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { + number: 1, + hash: relay_1_hash, + } + ), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 10).hash(), + }), + topics: vec![], + } + ], + ); + + // now try to import head#5 at relay block#0 + // => nothing is changed, because better head has already been imported + assert_ok!(import_parachain_1_head(0, state_root_5, parachains_5, proof_5)); + assert_eq!( + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 1, + head_hash: head_data(1, 10).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Grandpa1( + pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { + number: 1, + hash: relay_1_hash, + } + ), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 10).hash(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + } + ], + ); + }); + } + + #[test] + fn does_nothing_when_parachain_head_is_too_large() { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5)), (4, big_head_data(1, 5))]); + run_test(|| { + // start with relay block #0 and try to import head#5 of parachain#1 and big parachain + initialize(state_root); + let result = Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + ); + assert_ok!(result); + assert_eq!( + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(1, 5).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!(ParasInfo::::get(ParaId(4)), None); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::RejectedLargeParachainHead { + parachain: ParaId(4), + parachain_head_hash: big_head_data(1, 5).hash(), + parachain_head_size: big_stored_head_data(1, 5).encoded_size() as u32, + }), + topics: vec![], + }, + ], + ); + }); + } + + #[test] + fn prunes_old_heads() { + run_test(|| { + let heads_to_keep = crate::mock::HeadsToKeep::get(); + + // import exactly `HeadsToKeep` headers + for i in 0..heads_to_keep { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, i))]); + if i == 0 { + initialize(state_root); + } else { + proceed(i, state_root); + } + + let expected_weight = weight_of_import_parachain_1_head(&proof, false); + let result = import_parachain_1_head(i, state_root, parachains, proof); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); + } + + // nothing is pruned yet + for i in 0..heads_to_keep { + assert!(ImportedParaHeads::::get(ParaId(1), head_data(1, i).hash()) + .is_some()); + } + + // import next relay chain header and next parachain head + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, heads_to_keep))]); + proceed(heads_to_keep, state_root); + let expected_weight = weight_of_import_parachain_1_head(&proof, true); + let result = import_parachain_1_head(heads_to_keep, state_root, parachains, proof); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); + + // and the head#0 is pruned + assert!( + ImportedParaHeads::::get(ParaId(1), head_data(1, 0).hash()).is_none() + ); + for i in 1..=heads_to_keep { + assert!(ImportedParaHeads::::get(ParaId(1), head_data(1, i).hash()) + .is_some()); + } + }); + } + + #[test] + fn fails_on_unknown_relay_chain_block() { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + run_test(|| { + // start with relay block #0 + initialize(state_root); + + // try to import head#5 of parachain#1 at unknown relay chain block #1 + assert_noop!( + import_parachain_1_head(1, state_root, parachains, proof), + Error::::UnknownRelayChainBlock + ); + }); + } + + #[test] + fn fails_on_invalid_storage_proof() { + let (_state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + run_test(|| { + // start with relay block #0 + initialize(Default::default()); + + // try to import head#5 of parachain#1 at relay chain block #0 + assert_noop!( + import_parachain_1_head(0, Default::default(), parachains, proof), + Error::::HeaderChain(HeaderChainError::StorageProof( + StorageProofError::StorageRootMismatch + )) + ); + }); + } + + #[test] + fn is_not_rewriting_existing_head_if_failed_to_read_updated_head() { + let (state_root_5, proof_5, parachains_5) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root_10_at_20, proof_10_at_20, parachains_10_at_20) = + prepare_parachain_heads_proof(vec![(2, head_data(2, 10))]); + let (state_root_10_at_30, proof_10_at_30, parachains_10_at_30) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); + run_test(|| { + // we've already imported head#5 of parachain#1 at relay block#10 + initialize(state_root_5); + import_parachain_1_head(0, state_root_5, parachains_5, proof_5).expect("ok"); + assert_eq!( + Pallet::::best_parachain_head(ParaId(1)), + Some(stored_head_data(1, 5)) + ); + + // then if someone is pretending to provide updated head#10 of parachain#1 at relay + // block#20, but fails to do that + // + // => we'll leave previous value + proceed(20, state_root_10_at_20); + assert_ok!(Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (20, test_relay_header(20, state_root_10_at_20).hash()), + parachains_10_at_20, + proof_10_at_20, + ),); + assert_eq!( + Pallet::::best_parachain_head(ParaId(1)), + Some(stored_head_data(1, 5)) + ); + + // then if someone is pretending to provide updated head#10 of parachain#1 at relay + // block#30, and actualy provides it + // + // => we'll update value + proceed(30, state_root_10_at_30); + assert_ok!(Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (30, test_relay_header(30, state_root_10_at_30).hash()), + parachains_10_at_30, + proof_10_at_30, + ),); + assert_eq!( + Pallet::::best_parachain_head(ParaId(1)), + Some(stored_head_data(1, 10)) + ); + }); + } + + #[test] + fn storage_keys_computed_properly() { + assert_eq!( + ParasInfo::::storage_map_final_key(ParaId(42)).to_vec(), + ParasInfoKeyProvider::final_key("Parachains", &ParaId(42)).0 + ); + + assert_eq!( + ImportedParaHeads::::storage_double_map_final_key( + ParaId(42), + ParaHash::from([21u8; 32]) + ) + .to_vec(), + ImportedParaHeadsKeyProvider::final_key( + "Parachains", + &ParaId(42), + &ParaHash::from([21u8; 32]) + ) + .0, + ); + } + + #[test] + fn ignores_parachain_head_if_it_is_missing_from_storage_proof() { + let (state_root, proof, _) = prepare_parachain_heads_proof(vec![]); + let parachains = vec![(ParaId(2), Default::default())]; + run_test(|| { + initialize(state_root); + assert_ok!(Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + )); + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::MissingParachainHead { + parachain: ParaId(2), + }), + topics: vec![], + }], + ); + }); + } + + #[test] + fn ignores_parachain_head_if_parachain_head_hash_is_wrong() { + let (state_root, proof, _) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + let parachains = vec![(ParaId(1), head_data(1, 10).hash())]; + run_test(|| { + initialize(state_root); + assert_ok!(Pallet::::submit_parachain_heads( + RuntimeOrigin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + )); + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::IncorrectParachainHeadHash { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 10).hash(), + actual_parachain_head_hash: head_data(1, 0).hash(), + }), + topics: vec![], + }], + ); + }); + } + + #[test] + fn test_bridge_parachain_call_is_correctly_defined() { + let (state_root, proof, _) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + let parachains = vec![(ParaId(2), Default::default())]; + let relay_header_id = (0, test_relay_header(0, state_root).hash()); + + let direct_submit_parachain_heads_call = Call::::submit_parachain_heads { + at_relay_block: relay_header_id, + parachains: parachains.clone(), + parachain_heads_proof: proof.clone(), + }; + let indirect_submit_parachain_heads_call = BridgeParachainCall::submit_parachain_heads { + at_relay_block: relay_header_id, + parachains, + parachain_heads_proof: proof, + }; + assert_eq!( + direct_submit_parachain_heads_call.encode(), + indirect_submit_parachain_heads_call.encode() + ); + } + + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); + + #[test] + fn maybe_max_parachains_returns_correct_value() { + assert_eq!(MaybeMaxParachains::::get(), Some(mock::TOTAL_PARACHAINS)); + } + + #[test] + fn maybe_max_total_parachain_hashes_returns_correct_value() { + assert_eq!( + MaybeMaxTotalParachainHashes::::get(), + Some(mock::TOTAL_PARACHAINS * mock::HeadsToKeep::get()), + ); + } +} diff --git a/modules/parachains/src/mock.rs b/modules/parachains/src/mock.rs new file mode 100644 index 00000000000..3086adc1cc2 --- /dev/null +++ b/modules/parachains/src/mock.rs @@ -0,0 +1,349 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use bp_header_chain::ChainWithGrandpa; +use bp_polkadot_core::parachains::ParaId; +use bp_runtime::{Chain, Parachain}; +use frame_support::{construct_runtime, parameter_types, traits::ConstU32, weights::Weight}; +use sp_runtime::{ + testing::{Header, H256}, + traits::{BlakeTwo256, Header as HeaderT, IdentityLookup}, + MultiSignature, Perbill, +}; + +use crate as pallet_bridge_parachains; + +pub type AccountId = u64; +pub type TestNumber = u64; + +pub type RelayBlockHeader = + sp_runtime::generic::Header; + +type Block = frame_system::mocking::MockBlock; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + +pub const PARAS_PALLET_NAME: &str = "Paras"; +pub const UNTRACKED_PARACHAIN_ID: u32 = 10; +// use exact expected encoded size: `vec_len_size + header_number_size + state_root_hash_size` +pub const MAXIMAL_PARACHAIN_HEAD_DATA_SIZE: u32 = 1 + 8 + 32; +// total parachains that we use in tests +pub const TOTAL_PARACHAINS: u32 = 4; + +pub type RegularParachainHeader = sp_runtime::testing::Header; +pub type RegularParachainHasher = BlakeTwo256; +pub type BigParachainHeader = sp_runtime::generic::Header; + +pub struct Parachain1; + +impl Chain for Parachain1 { + type BlockNumber = u64; + type Hash = H256; + type Hasher = RegularParachainHasher; + type Header = RegularParachainHeader; + type AccountId = u64; + type Balance = u64; + type Index = u64; + type Signature = MultiSignature; + + fn max_extrinsic_size() -> u32 { + 0 + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl Parachain for Parachain1 { + const PARACHAIN_ID: u32 = 1; +} + +pub struct Parachain2; + +impl Chain for Parachain2 { + type BlockNumber = u64; + type Hash = H256; + type Hasher = RegularParachainHasher; + type Header = RegularParachainHeader; + type AccountId = u64; + type Balance = u64; + type Index = u64; + type Signature = MultiSignature; + + fn max_extrinsic_size() -> u32 { + 0 + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl Parachain for Parachain2 { + const PARACHAIN_ID: u32 = 2; +} + +pub struct Parachain3; + +impl Chain for Parachain3 { + type BlockNumber = u64; + type Hash = H256; + type Hasher = RegularParachainHasher; + type Header = RegularParachainHeader; + type AccountId = u64; + type Balance = u64; + type Index = u64; + type Signature = MultiSignature; + + fn max_extrinsic_size() -> u32 { + 0 + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl Parachain for Parachain3 { + const PARACHAIN_ID: u32 = 3; +} + +// this parachain is using u128 as block number and stored head data size exceeds limit +pub struct BigParachain; + +impl Chain for BigParachain { + type BlockNumber = u128; + type Hash = H256; + type Hasher = RegularParachainHasher; + type Header = BigParachainHeader; + type AccountId = u64; + type Balance = u64; + type Index = u64; + type Signature = MultiSignature; + + fn max_extrinsic_size() -> u32 { + 0 + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl Parachain for BigParachain { + const PARACHAIN_ID: u32 = 4; +} + +construct_runtime! { + pub enum TestRuntime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Grandpa1: pallet_bridge_grandpa::::{Pallet, Event}, + Grandpa2: pallet_bridge_grandpa::::{Pallet, Event}, + Parachains: pallet_bridge_parachains::{Call, Pallet, Event}, + } +} + +parameter_types! { + pub const BlockHashCount: TestNumber = 250; + pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 0); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} + +impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = TestNumber; + type Hash = H256; + type Hashing = RegularParachainHasher; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type DbWeight = (); + type BlockWeights = (); + type BlockLength = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +parameter_types! { + pub const SessionLength: u64 = 5; + pub const NumValidators: u32 = 5; + pub const HeadersToKeep: u32 = 5; +} + +impl pallet_bridge_grandpa::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type BridgedChain = TestBridgedChain; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<2>; + type HeadersToKeep = HeadersToKeep; + type WeightInfo = (); +} + +impl pallet_bridge_grandpa::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type BridgedChain = TestBridgedChain; + type MaxFreeMandatoryHeadersPerBlock = ConstU32<2>; + type HeadersToKeep = HeadersToKeep; + type WeightInfo = (); +} + +parameter_types! { + pub const HeadsToKeep: u32 = 4; + pub const ParasPalletName: &'static str = PARAS_PALLET_NAME; + pub GetTenFirstParachains: Vec = (0..10).map(ParaId).collect(); +} + +impl pallet_bridge_parachains::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; + type ParasPalletName = ParasPalletName; + type ParaStoredHeaderDataBuilder = (Parachain1, Parachain2, Parachain3, BigParachain); + type HeadsToKeep = HeadsToKeep; + type MaxParaHeadDataSize = ConstU32; +} + +#[cfg(feature = "runtime-benchmarks")] +impl pallet_bridge_parachains::benchmarking::Config<()> for TestRuntime { + fn parachains() -> Vec { + vec![ + ParaId(Parachain1::PARACHAIN_ID), + ParaId(Parachain2::PARACHAIN_ID), + ParaId(Parachain3::PARACHAIN_ID), + ] + } + + fn prepare_parachain_heads_proof( + parachains: &[ParaId], + _parachain_head_size: u32, + _proof_size: bp_runtime::StorageProofSize, + ) -> ( + crate::RelayBlockNumber, + crate::RelayBlockHash, + bp_polkadot_core::parachains::ParaHeadsProof, + Vec<(ParaId, bp_polkadot_core::parachains::ParaHash)>, + ) { + // in mock run we only care about benchmarks correctness, not the benchmark results + // => ignore size related arguments + let (state_root, proof, parachains) = crate::tests::prepare_parachain_heads_proof( + parachains.iter().map(|p| (p.0, crate::tests::head_data(p.0, 1))).collect(), + ); + let relay_genesis_hash = crate::tests::initialize(state_root); + (0, relay_genesis_hash, proof, parachains) + } +} + +#[derive(Debug)] +pub struct TestBridgedChain; + +impl Chain for TestBridgedChain { + type BlockNumber = crate::RelayBlockNumber; + type Hash = crate::RelayBlockHash; + type Hasher = crate::RelayBlockHasher; + type Header = RelayBlockHeader; + + type AccountId = AccountId; + type Balance = u32; + type Index = u32; + type Signature = sp_runtime::testing::TestSignature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + + fn max_extrinsic_weight() -> Weight { + unreachable!() + } +} + +impl ChainWithGrandpa for TestBridgedChain { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; + const MAX_AUTHORITIES_COUNT: u32 = 16; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + const MAX_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64; +} + +#[derive(Debug)] +pub struct OtherBridgedChain; + +impl Chain for OtherBridgedChain { + type BlockNumber = u64; + type Hash = crate::RelayBlockHash; + type Hasher = crate::RelayBlockHasher; + type Header = sp_runtime::generic::Header; + + type AccountId = AccountId; + type Balance = u32; + type Index = u32; + type Signature = sp_runtime::testing::TestSignature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + + fn max_extrinsic_weight() -> Weight { + unreachable!() + } +} + +impl ChainWithGrandpa for OtherBridgedChain { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; + const MAX_AUTHORITIES_COUNT: u32 = 16; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + const MAX_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64; +} + +/// Return test externalities to use in tests. +pub fn new_test_ext() -> sp_io::TestExternalities { + sp_io::TestExternalities::new(Default::default()) +} + +/// Run pallet test. +pub fn run_test(test: impl FnOnce() -> T) -> T { + new_test_ext().execute_with(|| { + System::set_block_number(1); + System::reset_events(); + test() + }) +} + +/// Return test relay chain header with given number. +pub fn test_relay_header( + num: crate::RelayBlockNumber, + state_root: crate::RelayBlockHash, +) -> RelayBlockHeader { + RelayBlockHeader::new( + num, + Default::default(), + state_root, + Default::default(), + Default::default(), + ) +} diff --git a/modules/parachains/src/weights.rs b/modules/parachains/src/weights.rs new file mode 100644 index 00000000000..54a835cbc87 --- /dev/null +++ b/modules/parachains/src/weights.rs @@ -0,0 +1,273 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Autogenerated weights for pallet_bridge_parachains +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/millau-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_bridge_parachains +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./modules/parachains/src/weights.rs +// --template=./.maintain/millau-weight-template.hbs + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_bridge_parachains. +pub trait WeightInfo { + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight; + fn submit_parachain_heads_with_1kb_proof() -> Weight; + fn submit_parachain_heads_with_16kb_proof() -> Weight; +} + +/// Weights for `pallet_bridge_parachains` that are generated using one of the Bridge testnets. +/// +/// Those weights are test only and must never be used in production. +pub struct BridgeWeight(PhantomData); +impl WeightInfo for BridgeWeight { + /// Storage: BridgeRialtoParachains PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: + /// 555, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(1024), max_size: + /// Some(64), added: 1549, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: + /// Some(196), added: 1681, mode: MaxEncodedLen) + /// + /// The range of component `p` is `[1, 2]`. + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `4648` + // Minimum execution time: 36_701 nanoseconds. + Weight::from_parts(38_597_828, 4648) + // Standard Error: 190_859 + .saturating_add(Weight::from_parts(60_685, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: BridgeRialtoParachains PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: + /// 555, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(1024), max_size: + /// Some(64), added: 1549, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: + /// Some(196), added: 1681, mode: MaxEncodedLen) + fn submit_parachain_heads_with_1kb_proof() -> Weight { + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `4648` + // Minimum execution time: 38_189 nanoseconds. + Weight::from_parts(39_252_000, 4648) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: BridgeRialtoParachains PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: + /// 555, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(1024), max_size: + /// Some(64), added: 1549, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: + /// Some(196), added: 1681, mode: MaxEncodedLen) + fn submit_parachain_heads_with_16kb_proof() -> Weight { + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `4648` + // Minimum execution time: 62_868 nanoseconds. + Weight::from_parts(63_581_000, 4648) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: BridgeRialtoParachains PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: + /// 555, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(1024), max_size: + /// Some(64), added: 1549, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: + /// Some(196), added: 1681, mode: MaxEncodedLen) + /// + /// The range of component `p` is `[1, 2]`. + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `4648` + // Minimum execution time: 36_701 nanoseconds. + Weight::from_parts(38_597_828, 4648) + // Standard Error: 190_859 + .saturating_add(Weight::from_parts(60_685, 0).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: BridgeRialtoParachains PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: + /// 555, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(1024), max_size: + /// Some(64), added: 1549, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: + /// Some(196), added: 1681, mode: MaxEncodedLen) + fn submit_parachain_heads_with_1kb_proof() -> Weight { + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `4648` + // Minimum execution time: 38_189 nanoseconds. + Weight::from_parts(39_252_000, 4648) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: BridgeRialtoParachains PalletOperatingMode (r:1 w:0) + /// + /// Proof: BridgeRialtoParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), + /// added: 496, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), + /// added: 2048, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ParasInfo (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: + /// 555, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHashes (r:1 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHashes (max_values: Some(1024), max_size: + /// Some(64), added: 1549, mode: MaxEncodedLen) + /// + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:0 w:1) + /// + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: + /// Some(196), added: 1681, mode: MaxEncodedLen) + fn submit_parachain_heads_with_16kb_proof() -> Weight { + // Proof Size summary in bytes: + // Measured: `366` + // Estimated: `4648` + // Minimum execution time: 62_868 nanoseconds. + Weight::from_parts(63_581_000, 4648) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } +} diff --git a/modules/parachains/src/weights_ext.rs b/modules/parachains/src/weights_ext.rs new file mode 100644 index 00000000000..eecdfe90359 --- /dev/null +++ b/modules/parachains/src/weights_ext.rs @@ -0,0 +1,107 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Weight-related utilities. + +use crate::weights::{BridgeWeight, WeightInfo}; + +use bp_runtime::Size; +use frame_support::weights::{RuntimeDbWeight, Weight}; + +/// Size of the regular parachain head. +/// +/// It's not that we are expecting all parachain heads to share the same size or that we would +/// reject all heads that have larger/lesser size. It is about head size that we use in benchmarks. +/// Relayer would need to pay additional fee for extra bytes. +/// +/// 384 is a bit larger (1.3 times) than the size of the randomly chosen Polkadot block. +pub const DEFAULT_PARACHAIN_HEAD_SIZE: u32 = 384; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// the Rialto chain. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// Extended weight info. +pub trait WeightInfoExt: WeightInfo { + /// Storage proof overhead, that is included in every storage proof. + /// + /// The relayer would pay some extra fee for additional proof bytes, since they mean + /// more hashing operations. + fn expected_extra_storage_proof_size() -> u32; + + /// Weight of the parachain heads delivery extrinsic. + fn submit_parachain_heads_weight( + db_weight: RuntimeDbWeight, + proof: &impl Size, + parachains_count: u32, + ) -> Weight { + // weight of the `submit_parachain_heads` with exactly `parachains_count` parachain + // heads of the default size (`DEFAULT_PARACHAIN_HEAD_SIZE`) + let base_weight = Self::submit_parachain_heads_with_n_parachains(parachains_count); + + // overhead because of extra storage proof bytes + let expected_proof_size = parachains_count + .saturating_mul(DEFAULT_PARACHAIN_HEAD_SIZE) + .saturating_add(Self::expected_extra_storage_proof_size()); + let actual_proof_size = proof.size(); + let proof_size_overhead = Self::storage_proof_size_overhead( + actual_proof_size.saturating_sub(expected_proof_size), + ); + + // potential pruning weight (refunded if hasn't happened) + let pruning_weight = + Self::parachain_head_pruning_weight(db_weight).saturating_mul(parachains_count as u64); + + base_weight.saturating_add(proof_size_overhead).saturating_add(pruning_weight) + } + + /// Returns weight of single parachain head storage update. + /// + /// This weight only includes db write operations that happens if parachain head is actually + /// updated. All extra weights (weight of storage proof validation, additional checks, ...) is + /// not included. + fn parachain_head_storage_write_weight(db_weight: RuntimeDbWeight) -> Weight { + // it's just a couple of operations - we need to write the hash (`ImportedParaHashes`) and + // the head itself (`ImportedParaHeads`. Pruning is not included here + db_weight.writes(2) + } + + /// Returns weight of single parachain head pruning. + fn parachain_head_pruning_weight(db_weight: RuntimeDbWeight) -> Weight { + // it's just one write operation, we don't want any benchmarks for that + db_weight.writes(1) + } + + /// Returns weight that needs to be accounted when storage proof of given size is received. + fn storage_proof_size_overhead(extra_proof_bytes: u32) -> Weight { + let extra_byte_weight = (Self::submit_parachain_heads_with_16kb_proof() - + Self::submit_parachain_heads_with_1kb_proof()) / + (15 * 1024); + extra_byte_weight.saturating_mul(extra_proof_bytes as u64) + } +} + +impl WeightInfoExt for () { + fn expected_extra_storage_proof_size() -> u32 { + EXTRA_STORAGE_PROOF_SIZE + } +} + +impl WeightInfoExt for BridgeWeight { + fn expected_extra_storage_proof_size() -> u32 { + EXTRA_STORAGE_PROOF_SIZE + } +} diff --git a/modules/relayers/Cargo.toml b/modules/relayers/Cargo.toml new file mode 100644 index 00000000000..c654c60d02b --- /dev/null +++ b/modules/relayers/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "pallet-bridge-relayers" +description = "Module used to store relayer rewards and coordinate relayers set." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } + +# Bridge dependencies + +bp-messages = { path = "../../primitives/messages", default-features = false } +bp-relayers = { path = "../../primitives/relayers", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +pallet-bridge-messages = { path = "../messages", default-features = false } + +# Substrate Dependencies + +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +bp-runtime = { path = "../../primitives/runtime" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "bp-messages/std", + "bp-relayers/std", + "bp-runtime/std", + "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-arithmetic/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/modules/relayers/README.md b/modules/relayers/README.md new file mode 100644 index 00000000000..656200f4486 --- /dev/null +++ b/modules/relayers/README.md @@ -0,0 +1,14 @@ +# Bridge Relayers Pallet + +The pallet serves as a storage for pending bridge relayer rewards. Any runtime component may register reward +to some relayer for doing some useful job at some messages lane. Later, the relayer may claim its rewards +using the `claim_rewards` call. + +The reward payment procedure is abstracted from the pallet code. One of possible implementations, is the +[`PayLaneRewardFromAccount`](../../primitives/relayers/src/lib.rs), which just does a `Currency::transfer` +call to relayer account from the relayer-rewards account, determined by the message lane id. + +We have two examples of how this pallet is used in production. Rewards are registered at the target chain to +compensate fees of message delivery transactions (and linked finality delivery calls). At the source chain, rewards +are registered during delivery confirmation transactions. You may find more information about that in the +[Kusama <> Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md) documentation. diff --git a/modules/relayers/src/benchmarking.rs b/modules/relayers/src/benchmarking.rs new file mode 100644 index 00000000000..a762a5693c2 --- /dev/null +++ b/modules/relayers/src/benchmarking.rs @@ -0,0 +1,59 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Benchmarks for the relayers Pallet. + +#![cfg(feature = "runtime-benchmarks")] + +use crate::*; + +use bp_messages::LaneId; +use bp_relayers::RewardsAccountOwner; +use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_system::RawOrigin; + +/// Reward amount that is (hopefully) is larger than existential deposit across all chains. +const REWARD_AMOUNT: u32 = u32::MAX; + +/// Pallet we're benchmarking here. +pub struct Pallet(crate::Pallet); + +/// Trait that must be implemented by runtime. +pub trait Config: crate::Config { + /// Prepare environment for paying given reward for serving given lane. + fn prepare_environment(account_params: RewardsAccountParams, reward: Self::Reward); +} + +benchmarks! { + // Benchmark `claim_rewards` call. + claim_rewards { + let lane = LaneId([0, 0, 0, 0]); + let account_params = + RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); + let relayer: T::AccountId = whitelisted_caller(); + let reward = T::Reward::from(REWARD_AMOUNT); + + T::prepare_environment(account_params, reward); + RelayerRewards::::insert(&relayer, account_params, reward); + }: _(RawOrigin::Signed(relayer), account_params) + verify { + // we can't check anything here, because `PaymentProcedure` is responsible for + // payment logic, so we assume that if call has succeeded, the procedure has + // also completed successfully + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) +} diff --git a/modules/relayers/src/lib.rs b/modules/relayers/src/lib.rs new file mode 100644 index 00000000000..bd33b811b30 --- /dev/null +++ b/modules/relayers/src/lib.rs @@ -0,0 +1,309 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Runtime module that is used to store relayer rewards and (in the future) to +//! coordinate relations between relayers. + +#![cfg_attr(not(feature = "std"), no_std)] +#![warn(missing_docs)] + +use bp_relayers::{PaymentProcedure, RelayerRewardsKeyProvider, RewardsAccountParams}; +use bp_runtime::StorageDoubleMapKeyProvider; +use frame_support::sp_runtime::Saturating; +use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero}; +use sp_std::marker::PhantomData; + +pub use pallet::*; +pub use payment_adapter::DeliveryConfirmationPaymentsAdapter; +pub use weights::WeightInfo; + +pub mod benchmarking; + +mod mock; +mod payment_adapter; + +pub mod weights; + +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-relayers"; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + /// `RelayerRewardsKeyProvider` for given configuration. + type RelayerRewardsKeyProviderOf = + RelayerRewardsKeyProvider<::AccountId, ::Reward>; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Type of relayer reward. + type Reward: AtLeast32BitUnsigned + Copy + Parameter + MaxEncodedLen; + /// Pay rewards adapter. + type PaymentProcedure: PaymentProcedure; + /// Pallet call weights. + type WeightInfo: WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::call] + impl Pallet { + /// Claim accumulated rewards. + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::claim_rewards())] + pub fn claim_rewards( + origin: OriginFor, + rewards_account_params: RewardsAccountParams, + ) -> DispatchResult { + let relayer = ensure_signed(origin)?; + + RelayerRewards::::try_mutate_exists( + &relayer, + rewards_account_params, + |maybe_reward| -> DispatchResult { + let reward = maybe_reward.take().ok_or(Error::::NoRewardForRelayer)?; + T::PaymentProcedure::pay_reward(&relayer, rewards_account_params, reward) + .map_err(|e| { + log::trace!( + target: LOG_TARGET, + "Failed to pay {:?} rewards to {:?}: {:?}", + rewards_account_params, + relayer, + e, + ); + Error::::FailedToPayReward + })?; + + Self::deposit_event(Event::::RewardPaid { + relayer: relayer.clone(), + rewards_account_params, + reward, + }); + Ok(()) + }, + ) + } + } + + impl Pallet { + /// Register reward for given relayer. + pub fn register_relayer_reward( + rewards_account_params: RewardsAccountParams, + relayer: &T::AccountId, + reward: T::Reward, + ) { + if reward.is_zero() { + return + } + + RelayerRewards::::mutate( + relayer, + rewards_account_params, + |old_reward: &mut Option| { + let new_reward = old_reward.unwrap_or_else(Zero::zero).saturating_add(reward); + *old_reward = Some(new_reward); + + log::trace!( + target: crate::LOG_TARGET, + "Relayer {:?} can now claim reward for serving payer {:?}: {:?}", + relayer, + rewards_account_params, + new_reward, + ); + }, + ); + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Reward has been paid to the relayer. + RewardPaid { + /// Relayer account that has been rewarded. + relayer: T::AccountId, + /// Relayer has received reward from this account. + rewards_account_params: RewardsAccountParams, + /// Reward amount. + reward: T::Reward, + }, + } + + #[pallet::error] + pub enum Error { + /// No reward can be claimed by given relayer. + NoRewardForRelayer, + /// Reward payment procedure has failed. + FailedToPayReward, + } + + /// Map of the relayer => accumulated reward. + #[pallet::storage] + #[pallet::getter(fn relayer_reward)] + pub type RelayerRewards = StorageDoubleMap< + _, + as StorageDoubleMapKeyProvider>::Hasher1, + as StorageDoubleMapKeyProvider>::Key1, + as StorageDoubleMapKeyProvider>::Hasher2, + as StorageDoubleMapKeyProvider>::Key2, + as StorageDoubleMapKeyProvider>::Value, + OptionQuery, + >; +} + +#[cfg(test)] +mod tests { + use super::*; + use mock::{RuntimeEvent as TestEvent, *}; + + use crate::Event::RewardPaid; + use bp_messages::LaneId; + use bp_relayers::RewardsAccountOwner; + use frame_support::{ + assert_noop, assert_ok, + traits::fungible::{Inspect, Mutate}, + }; + use frame_system::{EventRecord, Pallet as System, Phase}; + use sp_runtime::DispatchError; + + fn get_ready_for_events() { + System::::set_block_number(1); + System::::reset_events(); + } + + #[test] + fn root_cant_claim_anything() { + run_test(|| { + assert_noop!( + Pallet::::claim_rewards( + RuntimeOrigin::root(), + TEST_REWARDS_ACCOUNT_PARAMS + ), + DispatchError::BadOrigin, + ); + }); + } + + #[test] + fn relayer_cant_claim_if_no_reward_exists() { + run_test(|| { + assert_noop!( + Pallet::::claim_rewards( + RuntimeOrigin::signed(REGULAR_RELAYER), + TEST_REWARDS_ACCOUNT_PARAMS + ), + Error::::NoRewardForRelayer, + ); + }); + } + + #[test] + fn relayer_cant_claim_if_payment_procedure_fails() { + run_test(|| { + RelayerRewards::::insert( + FAILING_RELAYER, + TEST_REWARDS_ACCOUNT_PARAMS, + 100, + ); + assert_noop!( + Pallet::::claim_rewards( + RuntimeOrigin::signed(FAILING_RELAYER), + TEST_REWARDS_ACCOUNT_PARAMS + ), + Error::::FailedToPayReward, + ); + }); + } + + #[test] + fn relayer_can_claim_reward() { + run_test(|| { + get_ready_for_events(); + + RelayerRewards::::insert( + REGULAR_RELAYER, + TEST_REWARDS_ACCOUNT_PARAMS, + 100, + ); + assert_ok!(Pallet::::claim_rewards( + RuntimeOrigin::signed(REGULAR_RELAYER), + TEST_REWARDS_ACCOUNT_PARAMS + )); + assert_eq!( + RelayerRewards::::get(REGULAR_RELAYER, TEST_REWARDS_ACCOUNT_PARAMS), + None + ); + + //Check if the `RewardPaid` event was emitted. + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Relayers(RewardPaid { + relayer: REGULAR_RELAYER, + rewards_account_params: TEST_REWARDS_ACCOUNT_PARAMS, + reward: 100 + }), + topics: vec![], + }], + ); + }); + } + + #[test] + fn pay_reward_from_account_actually_pays_reward() { + type Balances = pallet_balances::Pallet; + type PayLaneRewardFromAccount = bp_relayers::PayRewardFromAccount; + + run_test(|| { + let in_lane_0 = RewardsAccountParams::new( + LaneId([0, 0, 0, 0]), + *b"test", + RewardsAccountOwner::ThisChain, + ); + let out_lane_1 = RewardsAccountParams::new( + LaneId([0, 0, 0, 1]), + *b"test", + RewardsAccountOwner::BridgedChain, + ); + + let in_lane0_rewards_account = PayLaneRewardFromAccount::rewards_account(in_lane_0); + let out_lane1_rewards_account = PayLaneRewardFromAccount::rewards_account(out_lane_1); + + Balances::mint_into(&in_lane0_rewards_account, 100).unwrap(); + Balances::mint_into(&out_lane1_rewards_account, 100).unwrap(); + assert_eq!(Balances::balance(&in_lane0_rewards_account), 100); + assert_eq!(Balances::balance(&out_lane1_rewards_account), 100); + assert_eq!(Balances::balance(&1), 0); + + PayLaneRewardFromAccount::pay_reward(&1, in_lane_0, 100).unwrap(); + assert_eq!(Balances::balance(&in_lane0_rewards_account), 0); + assert_eq!(Balances::balance(&out_lane1_rewards_account), 100); + assert_eq!(Balances::balance(&1), 100); + + PayLaneRewardFromAccount::pay_reward(&1, out_lane_1, 100).unwrap(); + assert_eq!(Balances::balance(&in_lane0_rewards_account), 0); + assert_eq!(Balances::balance(&out_lane1_rewards_account), 0); + assert_eq!(Balances::balance(&1), 200); + }); + } +} diff --git a/modules/relayers/src/mock.rs b/modules/relayers/src/mock.rs new file mode 100644 index 00000000000..fe8c586eecc --- /dev/null +++ b/modules/relayers/src/mock.rs @@ -0,0 +1,151 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg(test)] + +use crate as pallet_bridge_relayers; + +use bp_messages::LaneId; +use bp_relayers::{PaymentProcedure, RewardsAccountOwner, RewardsAccountParams}; +use frame_support::{parameter_types, weights::RuntimeDbWeight}; +use sp_core::H256; +use sp_runtime::{ + testing::Header as SubstrateHeader, + traits::{BlakeTwo256, ConstU32, IdentityLookup}, +}; + +pub type AccountId = u64; +pub type Balance = u64; + +type Block = frame_system::mocking::MockBlock; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + +frame_support::construct_runtime! { + pub enum TestRuntime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Event}, + Relayers: pallet_bridge_relayers::{Pallet, Call, Event}, + } +} + +parameter_types! { + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; +} + +impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = SubstrateHeader; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = frame_support::traits::ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type DbWeight = DbWeight; + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for TestRuntime { + type MaxLocks = (); + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = frame_support::traits::ConstU64<1>; + type AccountStore = frame_system::Pallet; + type WeightInfo = (); + type MaxReserves = (); + type ReserveIdentifier = (); + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; +} + +impl pallet_bridge_relayers::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type Reward = Balance; + type PaymentProcedure = TestPaymentProcedure; + type WeightInfo = (); +} + +#[cfg(feature = "runtime-benchmarks")] +impl pallet_bridge_relayers::benchmarking::Config for TestRuntime { + fn prepare_environment(account_params: RewardsAccountParams, reward: Balance) { + use frame_support::traits::fungible::Mutate; + let rewards_account = + bp_relayers::PayRewardFromAccount::::rewards_account( + account_params, + ); + Balances::mint_into(&rewards_account, reward).unwrap(); + } +} + +/// Message lane that we're using in tests. +pub const TEST_REWARDS_ACCOUNT_PARAMS: RewardsAccountParams = + RewardsAccountParams::new(LaneId([0, 0, 0, 0]), *b"test", RewardsAccountOwner::ThisChain); + +/// Regular relayer that may receive rewards. +pub const REGULAR_RELAYER: AccountId = 1; + +/// Relayer that can't receive rewards. +pub const FAILING_RELAYER: AccountId = 2; + +/// Payment procedure that rejects payments to the `FAILING_RELAYER`. +pub struct TestPaymentProcedure; + +impl PaymentProcedure for TestPaymentProcedure { + type Error = (); + + fn pay_reward( + relayer: &AccountId, + _lane_id: RewardsAccountParams, + _reward: Balance, + ) -> Result<(), Self::Error> { + match *relayer { + FAILING_RELAYER => Err(()), + _ => Ok(()), + } + } +} + +/// Return test externalities to use in tests. +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + sp_io::TestExternalities::new(t) +} + +/// Run pallet test. +pub fn run_test(test: impl FnOnce() -> T) -> T { + new_test_ext().execute_with(test) +} diff --git a/modules/relayers/src/payment_adapter.rs b/modules/relayers/src/payment_adapter.rs new file mode 100644 index 00000000000..a9536cfc027 --- /dev/null +++ b/modules/relayers/src/payment_adapter.rs @@ -0,0 +1,158 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Code that allows relayers pallet to be used as a payment mechanism for the messages pallet. + +use crate::{Config, Pallet}; + +use bp_messages::{ + source_chain::{DeliveryConfirmationPayments, RelayersRewards}, + LaneId, MessageNonce, +}; +use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; +use frame_support::{sp_runtime::SaturatedConversion, traits::Get}; +use sp_arithmetic::traits::{Saturating, Zero}; +use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive}; + +/// Adapter that allows relayers pallet to be used as a delivery+dispatch payment mechanism +/// for the messages pallet. +pub struct DeliveryConfirmationPaymentsAdapter( + PhantomData<(T, MI, DeliveryReward)>, +); + +impl DeliveryConfirmationPayments + for DeliveryConfirmationPaymentsAdapter +where + T: Config + pallet_bridge_messages::Config, + MI: 'static, + DeliveryReward: Get, +{ + type Error = &'static str; + + fn pay_reward( + lane_id: LaneId, + messages_relayers: VecDeque>, + confirmation_relayer: &T::AccountId, + received_range: &RangeInclusive, + ) -> MessageNonce { + let relayers_rewards = + bp_messages::calc_relayers_rewards::(messages_relayers, received_range); + let rewarded_relayers = relayers_rewards.len(); + + register_relayers_rewards::( + confirmation_relayer, + relayers_rewards, + RewardsAccountParams::new( + lane_id, + T::BridgedChainId::get(), + RewardsAccountOwner::BridgedChain, + ), + DeliveryReward::get(), + ); + + rewarded_relayers as _ + } +} + +// Update rewards to given relayers, optionally rewarding confirmation relayer. +fn register_relayers_rewards( + confirmation_relayer: &T::AccountId, + relayers_rewards: RelayersRewards, + lane_id: RewardsAccountParams, + delivery_fee: T::Reward, +) { + // reward every relayer except `confirmation_relayer` + let mut confirmation_relayer_reward = T::Reward::zero(); + for (relayer, messages) in relayers_rewards { + // sane runtime configurations guarantee that the number of messages will be below + // `u32::MAX` + let relayer_reward = T::Reward::saturated_from(messages).saturating_mul(delivery_fee); + + if relayer != *confirmation_relayer { + Pallet::::register_relayer_reward(lane_id, &relayer, relayer_reward); + } else { + confirmation_relayer_reward = + confirmation_relayer_reward.saturating_add(relayer_reward); + } + } + + // finally - pay reward to confirmation relayer + Pallet::::register_relayer_reward( + lane_id, + confirmation_relayer, + confirmation_relayer_reward, + ); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{mock::*, RelayerRewards}; + + const RELAYER_1: AccountId = 1; + const RELAYER_2: AccountId = 2; + const RELAYER_3: AccountId = 3; + + fn relayers_rewards() -> RelayersRewards { + vec![(RELAYER_1, 2), (RELAYER_2, 3)].into_iter().collect() + } + + #[test] + fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() { + run_test(|| { + register_relayers_rewards::( + &RELAYER_2, + relayers_rewards(), + TEST_REWARDS_ACCOUNT_PARAMS, + 50, + ); + + assert_eq!( + RelayerRewards::::get(RELAYER_1, TEST_REWARDS_ACCOUNT_PARAMS), + Some(100) + ); + assert_eq!( + RelayerRewards::::get(RELAYER_2, TEST_REWARDS_ACCOUNT_PARAMS), + Some(150) + ); + }); + } + + #[test] + fn confirmation_relayer_is_not_rewarded_if_it_has_not_delivered_any_messages() { + run_test(|| { + register_relayers_rewards::( + &RELAYER_3, + relayers_rewards(), + TEST_REWARDS_ACCOUNT_PARAMS, + 50, + ); + + assert_eq!( + RelayerRewards::::get(RELAYER_1, TEST_REWARDS_ACCOUNT_PARAMS), + Some(100) + ); + assert_eq!( + RelayerRewards::::get(RELAYER_2, TEST_REWARDS_ACCOUNT_PARAMS), + Some(150) + ); + assert_eq!( + RelayerRewards::::get(RELAYER_3, TEST_REWARDS_ACCOUNT_PARAMS), + None + ); + }); + } +} diff --git a/modules/relayers/src/weights.rs b/modules/relayers/src/weights.rs new file mode 100644 index 00000000000..1f111aaf136 --- /dev/null +++ b/modules/relayers/src/weights.rs @@ -0,0 +1,101 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Autogenerated weights for pallet_bridge_relayers +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/millau-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_bridge_relayers +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./modules/relayers/src/weights.rs +// --template=./.maintain/millau-weight-template.hbs + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_bridge_relayers. +pub trait WeightInfo { + fn claim_rewards() -> Weight; +} + +/// Weights for `pallet_bridge_relayers` that are generated using one of the Bridge testnets. +/// +/// Those weights are test only and must never be used in production. +pub struct BridgeWeight(PhantomData); +impl WeightInfo for BridgeWeight { + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, + /// mode: MaxEncodedLen) + /// + /// Storage: System Account (r:1 w:1) + /// + /// Proof: System Account (max_values: None, max_size: Some(96), added: 2571, mode: + /// MaxEncodedLen) + fn claim_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `275` + // Estimated: `5111` + // Minimum execution time: 48_639 nanoseconds. + Weight::from_parts(49_600_000, 5111) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, + /// mode: MaxEncodedLen) + /// + /// Storage: System Account (r:1 w:1) + /// + /// Proof: System Account (max_values: None, max_size: Some(96), added: 2571, mode: + /// MaxEncodedLen) + fn claim_rewards() -> Weight { + // Proof Size summary in bytes: + // Measured: `275` + // Estimated: `5111` + // Minimum execution time: 48_639 nanoseconds. + Weight::from_parts(49_600_000, 5111) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } +} diff --git a/modules/shift-session-manager/Cargo.toml b/modules/shift-session-manager/Cargo.toml new file mode 100644 index 00000000000..2d7dc272a6f --- /dev/null +++ b/modules/shift-session-manager/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "pallet-shift-session-manager" +description = "A Substrate Runtime module that selects 2/3 of initial validators for every session" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "pallet-session/std", + "scale-info/std", + "sp-staking/std", + "sp-std/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/modules/shift-session-manager/README.md b/modules/shift-session-manager/README.md new file mode 100644 index 00000000000..8dfbfd416e3 --- /dev/null +++ b/modules/shift-session-manager/README.md @@ -0,0 +1,10 @@ +# Shift Session Manager Pallet + +**THIS PALLET IS NOT INTENDED TO BE USED IN PRODUCTION** + +The pallet does not provide any calls or runtime storage entries. It only provides implementation of the +`pallet_session::SessionManager`. This implementation, starting from session `3` selects two thirds of initial +validators and changes the set on every session. We are using it at our testnets ([Rialto](../../bin/rialto/) and +[Millau](../../bin/millau/)) to be sure that the set changes every session. On well-known production chains +(like Kusama and Polkadot) the alternative is the set of [nPoS](https://research.web3.foundation/en/latest/polkadot/NPoS/index.html) +pallets, which selects validators, based on their nominations. diff --git a/modules/shift-session-manager/src/lib.rs b/modules/shift-session-manager/src/lib.rs new file mode 100644 index 00000000000..1c38a52b0c2 --- /dev/null +++ b/modules/shift-session-manager/src/lib.rs @@ -0,0 +1,266 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate session manager that selects 2/3 validators from initial set, +//! starting from session 2. + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::traits::{ValidatorSet, ValidatorSetWithIdentification}; +use sp_std::prelude::*; + +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + #[pallet::disable_frame_system_supertrait_check] + pub trait Config: pallet_session::Config {} + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet {} + + /// Validators of first two sessions. + #[pallet::storage] + pub(super) type InitialValidators = StorageValue<_, Vec>; +} + +impl ValidatorSet for Pallet { + type ValidatorId = T::ValidatorId; + type ValidatorIdOf = T::ValidatorIdOf; + + fn session_index() -> sp_staking::SessionIndex { + pallet_session::Pallet::::current_index() + } + + fn validators() -> Vec { + pallet_session::Pallet::::validators() + } +} + +impl ValidatorSetWithIdentification for Pallet { + type Identification = (); + type IdentificationOf = (); +} + +impl pallet_session::SessionManager for Pallet { + fn end_session(_: sp_staking::SessionIndex) {} + fn start_session(_: sp_staking::SessionIndex) {} + fn new_session(session_index: sp_staking::SessionIndex) -> Option> { + // we don't want to add even more fields to genesis config => just return None + if session_index == 0 || session_index == 1 { + return None + } + + // the idea that on first call (i.e. when session 1 ends) we're reading current + // set of validators from session module (they are initial validators) and save + // in our 'local storage'. + // then for every session we select (deterministically) 2/3 of these initial + // validators to serve validators of new session + let available_validators = InitialValidators::::get().unwrap_or_else(|| { + let validators = >::validators(); + InitialValidators::::put(validators.clone()); + validators + }); + + Some(Self::select_validators(session_index, &available_validators)) + } +} + +impl Pallet { + /// Select validators for session. + fn select_validators( + session_index: sp_staking::SessionIndex, + available_validators: &[T::ValidatorId], + ) -> Vec { + let available_validators_count = available_validators.len(); + let count = sp_std::cmp::max(1, 2 * available_validators_count / 3); + let offset = session_index as usize % available_validators_count; + let end = offset + count; + let session_validators = match end.overflowing_sub(available_validators_count) { + (wrapped_end, false) if wrapped_end != 0 => available_validators[offset..] + .iter() + .chain(available_validators[..wrapped_end].iter()) + .cloned() + .collect(), + _ => available_validators[offset..end].to_vec(), + }; + + session_validators + } +} + +#[cfg(test)] +mod tests { + // From construct_runtime macro + #![allow(clippy::from_over_into)] + + use super::*; + use frame_support::{ + parameter_types, + sp_io::TestExternalities, + sp_runtime::{ + testing::{Header, UintAuthorityId}, + traits::{BlakeTwo256, ConvertInto, IdentityLookup}, + Perbill, RuntimeAppPublic, + }, + traits::GenesisBuild, + weights::Weight, + BasicExternalities, + }; + use sp_core::H256; + + type AccountId = u64; + + type Block = frame_system::mocking::MockBlock; + type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + + frame_support::construct_runtime! { + pub enum TestRuntime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Session: pallet_session::{Pallet}, + } + } + + parameter_types! { + pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 0); + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + } + + impl frame_system::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = (); + type BlockHashCount = frame_support::traits::ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + } + + parameter_types! { + pub const Period: u64 = 1; + pub const Offset: u64 = 0; + } + + impl pallet_session::Config for TestRuntime { + type RuntimeEvent = (); + type ValidatorId = ::AccountId; + type ValidatorIdOf = ConvertInto; + type ShouldEndSession = pallet_session::PeriodicSessions; + type NextSessionRotation = pallet_session::PeriodicSessions; + type SessionManager = (); + type SessionHandler = TestSessionHandler; + type Keys = UintAuthorityId; + type WeightInfo = (); + } + + impl Config for TestRuntime {} + + pub struct TestSessionHandler; + impl pallet_session::SessionHandler for TestSessionHandler { + const KEY_TYPE_IDS: &'static [sp_runtime::KeyTypeId] = &[UintAuthorityId::ID]; + + fn on_genesis_session(_validators: &[(AccountId, Ks)]) { + } + + fn on_new_session( + _: bool, + _: &[(AccountId, Ks)], + _: &[(AccountId, Ks)], + ) { + } + + fn on_disabled(_: u32) {} + } + + fn new_test_ext() -> TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + let keys = vec![ + (1, 1, UintAuthorityId(1)), + (2, 2, UintAuthorityId(2)), + (3, 3, UintAuthorityId(3)), + (4, 4, UintAuthorityId(4)), + (5, 5, UintAuthorityId(5)), + ]; + + BasicExternalities::execute_with_storage(&mut t, || { + for (ref k, ..) in &keys { + frame_system::Pallet::::inc_providers(k); + } + }); + + pallet_session::GenesisConfig:: { keys } + .assimilate_storage(&mut t) + .unwrap(); + TestExternalities::new(t) + } + + #[test] + fn shift_session_manager_works() { + new_test_ext().execute_with(|| { + let all_accs = vec![1, 2, 3, 4, 5]; + + // at least 1 validator is selected + assert_eq!(Pallet::::select_validators(0, &[1]), vec![1],); + + // at session#0, shift is also 0 + assert_eq!(Pallet::::select_validators(0, &all_accs), vec![1, 2, 3],); + + // at session#1, shift is also 1 + assert_eq!(Pallet::::select_validators(1, &all_accs), vec![2, 3, 4],); + + // at session#3, we're wrapping + assert_eq!(Pallet::::select_validators(3, &all_accs), vec![4, 5, 1],); + + // at session#5, we're starting from the beginning again + assert_eq!(Pallet::::select_validators(5, &all_accs), vec![1, 2, 3],); + }); + } +} diff --git a/primitives/beefy/Cargo.toml b/primitives/beefy/Cargo.toml new file mode 100644 index 00000000000..9039064fbf4 --- /dev/null +++ b/primitives/beefy/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "bp-beefy" +description = "Primitives of pallet-bridge-beefy module." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "bit-vec"] } +scale-info = { version = "2.5.0", default-features = false, features = ["bit-vec", "derive"] } +serde = { version = "1.0", optional = true } + +# Bridge Dependencies + +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Dependencies + +binary-merkle-tree = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-runtime/std", + "codec/std", + "frame-support/std", + "pallet-beefy-mmr/std", + "pallet-mmr/std", + "scale-info/std", + "serde", + "sp-consensus-beefy/std", + "sp-runtime/std", + "sp-std/std" +] diff --git a/primitives/beefy/src/lib.rs b/primitives/beefy/src/lib.rs new file mode 100644 index 00000000000..de260d45eca --- /dev/null +++ b/primitives/beefy/src/lib.rs @@ -0,0 +1,149 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives that are used to interact with BEEFY bridge pallet. + +#![cfg_attr(not(feature = "std"), no_std)] +#![warn(missing_docs)] + +pub use binary_merkle_tree::merkle_root; +pub use pallet_beefy_mmr::BeefyEcdsaToEthereum; +pub use pallet_mmr::{ + primitives::{DataOrHash as MmrDataOrHash, Proof as MmrProof}, + verify_leaves_proof as verify_mmr_leaves_proof, +}; +pub use sp_consensus_beefy::{ + crypto::{AuthorityId as EcdsaValidatorId, AuthoritySignature as EcdsaValidatorSignature}, + known_payloads::MMR_ROOT_ID as MMR_ROOT_PAYLOAD_ID, + mmr::{BeefyAuthoritySet, MmrLeafVersion}, + BeefyAuthorityId, Commitment, Payload as BeefyPayload, SignedCommitment, ValidatorSet, + ValidatorSetId, BEEFY_ENGINE_ID, +}; + +use bp_runtime::{BasicOperatingMode, BlockNumberOf, Chain, HashOf}; +use codec::{Decode, Encode}; +use frame_support::Parameter; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{Convert, MaybeSerializeDeserialize}, + RuntimeAppPublic, RuntimeDebug, +}; +use sp_std::prelude::*; + +/// Substrate-based chain with BEEFY && MMR pallets deployed. +/// +/// Both BEEFY and MMR pallets and their clients may be configured to use different +/// primitives. Some of types can be configured in low-level pallets, but are constrained +/// when BEEFY+MMR bundle is used. +pub trait ChainWithBeefy: Chain { + /// The hashing algorithm used to compute the digest of the BEEFY commitment. + /// + /// Corresponds to the hashing algorithm, used by `sc_consensus_beefy::BeefyKeystore`. + type CommitmentHasher: sp_runtime::traits::Hash; + + /// The hashing algorithm used to build the MMR. + /// + /// The same algorithm is also used to compute merkle roots in BEEFY + /// (e.g. validator addresses root in leaf data). + /// + /// Corresponds to the `Hashing` field of the `pallet-mmr` configuration. + type MmrHashing: sp_runtime::traits::Hash; + + /// The output type of the hashing algorithm used to build the MMR. + /// + /// This type is actually stored in the MMR. + + /// Corresponds to the `Hash` field of the `pallet-mmr` configuration. + type MmrHash: sp_std::hash::Hash + + Parameter + + Copy + + AsRef<[u8]> + + Default + + MaybeSerializeDeserialize + + PartialOrd; + + /// The type expected for the MMR leaf extra data. + type BeefyMmrLeafExtra: Parameter; + + /// A way to identify a BEEFY validator. + /// + /// Corresponds to the `BeefyId` field of the `pallet-beefy` configuration. + type AuthorityId: BeefyAuthorityId + Parameter; + + /// A way to convert validator id to its raw representation in the BEEFY merkle tree. + /// + /// Corresponds to the `BeefyAuthorityToMerkleLeaf` field of the `pallet-beefy-mmr` + /// configuration. + type AuthorityIdToMerkleLeaf: Convert>; +} + +/// BEEFY validator id used by given Substrate chain. +pub type BeefyAuthorityIdOf = ::AuthorityId; +/// BEEFY validator set, containing both validator identifiers and the numeric set id. +pub type BeefyAuthoritySetOf = ValidatorSet>; +/// BEEFY authority set, containing both validator identifiers and the numeric set id. +pub type BeefyAuthoritySetInfoOf = sp_consensus_beefy::mmr::BeefyAuthoritySet>; +/// BEEFY validator signature used by given Substrate chain. +pub type BeefyValidatorSignatureOf = + <::AuthorityId as RuntimeAppPublic>::Signature; +/// Signed BEEFY commitment used by given Substrate chain. +pub type BeefySignedCommitmentOf = + SignedCommitment, BeefyValidatorSignatureOf>; +/// Hash algorithm, used to compute the digest of the BEEFY commitment before signing it. +pub type BeefyCommitmentHasher = ::CommitmentHasher; +/// Hash algorithm used in Beefy MMR construction by given Substrate chain. +pub type MmrHashingOf = ::MmrHashing; +/// Hash type, used in MMR construction by given Substrate chain. +pub type MmrHashOf = ::MmrHash; +/// BEEFY MMR proof type used by the given Substrate chain. +pub type MmrProofOf = MmrProof>; +/// The type of the MMR leaf extra data used by the given Substrate chain. +pub type BeefyMmrLeafExtraOf = ::BeefyMmrLeafExtra; +/// A way to convert a validator id to its raw representation in the BEEFY merkle tree, used by +/// the given Substrate chain. +pub type BeefyAuthorityIdToMerkleLeafOf = ::AuthorityIdToMerkleLeaf; +/// Actual type of leafs in the BEEFY MMR. +pub type BeefyMmrLeafOf = sp_consensus_beefy::mmr::MmrLeaf< + BlockNumberOf, + HashOf, + MmrHashOf, + BeefyMmrLeafExtraOf, +>; + +/// Data required for initializing the BEEFY pallet. +/// +/// Provides the initial context that the bridge needs in order to know +/// where to start the sync process from. +#[derive(Encode, Decode, RuntimeDebug, PartialEq, Clone, TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct InitializationData { + /// Pallet operating mode. + pub operating_mode: BasicOperatingMode, + /// Number of the best block, finalized by BEEFY. + pub best_block_number: BlockNumber, + /// BEEFY authority set that will be finalizing descendants of the `best_beefy_block_number` + /// block. + pub authority_set: BeefyAuthoritySet, +} + +/// Basic data, stored by the pallet for every imported commitment. +#[derive(Encode, Decode, RuntimeDebug, PartialEq, TypeInfo)] +pub struct ImportedCommitment { + /// Block number and hash of the finalized block parent. + pub parent_number_and_hash: (BlockNumber, BlockHash), + /// MMR root at the imported block. + pub mmr_root: MmrHash, +} diff --git a/primitives/chain-bridge-hub-cumulus/Cargo.toml b/primitives/chain-bridge-hub-cumulus/Cargo.toml new file mode 100644 index 00000000000..2bbe3d029a3 --- /dev/null +++ b/primitives/chain-bridge-hub-cumulus/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "bp-bridge-hub-cumulus" +description = "Primitives of BridgeHubRococo parachain runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +# Bridge Dependencies + +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } + +# Substrate Based Dependencies + +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +# Polkadot Dependencies +polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-polkadot-core/std", + "bp-messages/std", + "bp-runtime/std", + "frame-system/std", + "frame-support/std", + "sp-api/std", + "sp-std/std", + "polkadot-primitives/std", +] diff --git a/primitives/chain-bridge-hub-cumulus/src/lib.rs b/primitives/chain-bridge-hub-cumulus/src/lib.rs new file mode 100644 index 00000000000..4c9f9e20468 --- /dev/null +++ b/primitives/chain-bridge-hub-cumulus/src/lib.rs @@ -0,0 +1,216 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use bp_polkadot_core::{ + AccountId, AccountInfoStorageMapKeyProvider, AccountPublic, Balance, BlockNumber, Hash, Hasher, + Hashing, Header, Index, Nonce, Perbill, Signature, SignedBlock, UncheckedExtrinsic, + EXTRA_STORAGE_PROOF_SIZE, TX_EXTRA_BYTES, +}; + +use bp_messages::*; +use bp_runtime::extensions::{ + BridgeRejectObsoleteHeadersAndMessages, ChargeTransactionPayment, CheckEra, CheckGenesis, + CheckNonZeroSender, CheckNonce, CheckSpecVersion, CheckTxVersion, CheckWeight, + GenericSignedExtension, RefundBridgedParachainMessagesSchema, +}; +use frame_support::{ + dispatch::DispatchClass, + parameter_types, + sp_runtime::{MultiAddress, MultiSigner}, + weights::constants, +}; +use frame_system::limits; +use sp_std::time::Duration; + +/// Average block interval in Cumulus-based parachains. +/// +/// Corresponds to the `MILLISECS_PER_BLOCK` from `parachains_common` crate. +pub const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(12); + +/// All cumulus bridge hubs allow normal extrinsics to fill block up to 75 percent. +/// +/// This is a copy-paste from the cumulus repo's `parachains-common` crate. +pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// All cumulus bridge hubs chains allow for 0.5 seconds of compute with a 6-second average block +/// time. +/// +/// This is a copy-paste from the cumulus repo's `parachains-common` crate. +const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts(constants::WEIGHT_REF_TIME_PER_SECOND, 0) + .saturating_div(2) + .set_proof_size(polkadot_primitives::v4::MAX_POV_SIZE as u64); + +/// All cumulus bridge hubs assume that about 5 percent of the block weight is consumed by +/// `on_initialize` handlers. This is used to limit the maximal weight of a single extrinsic. +/// +/// This is a copy-paste from the cumulus repo's `parachains-common` crate. +pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); + +parameter_types! { + pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio( + 5 * 1024 * 1024, + NORMAL_DISPATCH_RATIO, + ); + + /// Importing a block with 0 Extrinsics. + pub const BlockExecutionWeight: Weight = Weight::from_parts(constants::WEIGHT_REF_TIME_PER_NANOS, 0) + .saturating_mul(5_000_000); + /// Executing a NO-OP `System::remarks` Extrinsic. + pub const ExtrinsicBaseWeight: Weight = Weight::from_parts(constants::WEIGHT_REF_TIME_PER_NANOS, 0) + .saturating_mul(125_000); + + pub BlockWeights: limits::BlockWeights = limits::BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + // Operational transactions have an extra reserved space, so that they + // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT, + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); +} + +/// Public key of the chain account that may be used to verify signatures. +pub type AccountSigner = MultiSigner; + +/// The address format for describing accounts. +pub type Address = MultiAddress; + +// Note about selecting values of two following constants: +// +// Normal transactions have limit of 75% of 1/2 second weight for Cumulus parachains. Let's keep +// some reserve for the rest of stuff there => let's select values that fit in 50% of maximal limit. +// +// Using current constants, the limit would be: +// +// `75% * WEIGHT_REF_TIME_PER_SECOND * 1 / 2 * 50% = 0.75 * 1_000_000_000_000 / 2 * 0.5 = +// 187_500_000_000` +// +// According to (preliminary) weights of messages pallet, cost of additional message is zero and the +// cost of additional relayer is `8_000_000 + db read + db write`. Let's say we want no more than +// 4096 unconfirmed messages (no any scientific justification for that - it just looks large +// enough). And then we can't have more than 4096 relayers. E.g. for 1024 relayers is (using +// `RocksDbWeight`): +// +// `1024 * (8_000_000 + db read + db write) = 1024 * (8_000_000 + 25_000_000 + 100_000_000) = +// 136_192_000_000` +// +// So 1024 looks like good approximation for the number of relayers. If something is wrong in those +// assumptions, or something will change, it shall be caught by the +// `ensure_able_to_receive_confirmation` test. + +/// Maximal number of unrewarded relayer entries at inbound lane for Cumulus-based parachains. +pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; + +/// Maximal number of unconfirmed messages at inbound lane for Cumulus-based parachains. +pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 4096; + +/// Extra signed extension data that is used by all bridge hubs. +pub type SignedExtra = ( + CheckNonZeroSender, + CheckSpecVersion, + CheckTxVersion, + CheckGenesis, + CheckEra, + CheckNonce, + CheckWeight, + ChargeTransactionPayment, + BridgeRejectObsoleteHeadersAndMessages, + RefundBridgedParachainMessagesSchema, +); + +/// Signed extension that is used by all bridge hubs. +pub type SignedExtension = GenericSignedExtension; + +/// Helper trait to define some extra methods on bridge hubs signed extension (and +/// overcome Rust limitations). +pub trait BridgeHubSignedExtension { + /// Create signed extension from its components. + fn from_params( + spec_version: u32, + transaction_version: u32, + era: bp_runtime::TransactionEra, + genesis_hash: Hash, + nonce: Index, + tip: Balance, + ) -> Self; + + /// Return transaction nonce. + fn nonce(&self) -> Index; + + /// Return transaction tip. + fn tip(&self) -> Balance; +} + +impl BridgeHubSignedExtension for SignedExtension { + /// Create signed extension from its components. + fn from_params( + spec_version: u32, + transaction_version: u32, + era: bp_runtime::TransactionEra, + genesis_hash: Hash, + nonce: Index, + tip: Balance, + ) -> Self { + GenericSignedExtension::new( + ( + (), // non-zero sender + (), // spec version + (), // tx version + (), // genesis + era.frame_era(), // era + nonce.into(), // nonce (compact encoding) + (), // Check weight + tip.into(), // transaction payment / tip (compact encoding) + (), // bridge reject obsolete headers and msgs + (), // bridge reward to relayer for message passing + ), + Some(( + (), + spec_version, + transaction_version, + genesis_hash, + era.signed_payload(genesis_hash), + (), + (), + (), + (), + (), + )), + ) + } + + /// Return transaction nonce. + fn nonce(&self) -> Index { + self.payload.5 .0 + } + + /// Return transaction tip. + fn tip(&self) -> Balance { + self.payload.7 .0 + } +} diff --git a/primitives/chain-bridge-hub-kusama/Cargo.toml b/primitives/chain-bridge-hub-kusama/Cargo.toml new file mode 100644 index 00000000000..6d4334eaa57 --- /dev/null +++ b/primitives/chain-bridge-hub-kusama/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "bp-bridge-hub-kusama" +description = "Primitives of BridgeHubRococo parachain runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +# Bridge Dependencies + +bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-bridge-hub-cumulus/std", + "bp-messages/std", + "bp-runtime/std", + "frame-support/std", + "sp-api/std", + "sp-std/std", +] diff --git a/primitives/chain-bridge-hub-kusama/src/lib.rs b/primitives/chain-bridge-hub-kusama/src/lib.rs new file mode 100644 index 00000000000..6ca2cd047fb --- /dev/null +++ b/primitives/chain-bridge-hub-kusama/src/lib.rs @@ -0,0 +1,84 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Module with configuration which reflects BridgeHubKusama runtime setup (AccountId, Headers, +//! Hashes...) + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use bp_bridge_hub_cumulus::*; +use bp_messages::*; +use bp_runtime::{ + decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, Parachain, +}; +use frame_support::{ + dispatch::DispatchClass, + sp_runtime::{MultiAddress, MultiSigner}, + RuntimeDebug, +}; +use sp_std::prelude::*; + +/// BridgeHubKusama parachain. +#[derive(RuntimeDebug)] +pub struct BridgeHubKusama; + +impl Chain for BridgeHubKusama { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +impl Parachain for BridgeHubKusama { + const PARACHAIN_ID: u32 = BRIDGE_HUB_KUSAMA_PARACHAIN_ID; +} + +/// Public key of the chain account that may be used to verify signatures. +pub type AccountSigner = MultiSigner; + +/// The address format for describing accounts. +pub type Address = MultiAddress; + +/// Identifier of BridgeHubKusama in the Kusama relay chain. +pub const BRIDGE_HUB_KUSAMA_PARACHAIN_ID: u32 = 1002; + +/// Name of the With-BridgeHubKusama messages pallet instance that is deployed at bridged chains. +// TODO: check me (https://github.com/paritytech/parity-bridges-common/issues/1945) +pub const WITH_BRIDGE_HUB_KUSAMA_MESSAGES_PALLET_NAME: &str = "BridgeKusamaMessages"; + +/// Name of the With-BridgeHubKusama bridge-relayers pallet instance that is deployed at bridged +/// chains. +// TODO: check me (https://github.com/paritytech/parity-bridges-common/issues/1945) +pub const WITH_BRIDGE_HUB_KUSAMA_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; + +decl_bridge_finality_runtime_apis!(bridge_hub_kusama); +decl_bridge_messages_runtime_apis!(bridge_hub_kusama); diff --git a/primitives/chain-bridge-hub-polkadot/Cargo.toml b/primitives/chain-bridge-hub-polkadot/Cargo.toml new file mode 100644 index 00000000000..2a0ab3213c8 --- /dev/null +++ b/primitives/chain-bridge-hub-polkadot/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "bp-bridge-hub-polkadot" +description = "Primitives of BridgeHubWococo parachain runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-bridge-hub-cumulus/std", + "bp-runtime/std", + "bp-messages/std", + "frame-support/std", + "sp-api/std", + "sp-std/std", +] diff --git a/primitives/chain-bridge-hub-polkadot/src/lib.rs b/primitives/chain-bridge-hub-polkadot/src/lib.rs new file mode 100644 index 00000000000..646fb0a6e41 --- /dev/null +++ b/primitives/chain-bridge-hub-polkadot/src/lib.rs @@ -0,0 +1,75 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Module with configuration which reflects BridgeHubPolkadot runtime setup +//! (AccountId, Headers, Hashes...) + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use bp_bridge_hub_cumulus::*; +use bp_messages::*; +use bp_runtime::{ + decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, Parachain, +}; +use frame_support::{dispatch::DispatchClass, RuntimeDebug}; +use sp_std::prelude::*; + +/// BridgeHubPolkadot parachain. +#[derive(RuntimeDebug)] +pub struct BridgeHubPolkadot; + +impl Chain for BridgeHubPolkadot { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +impl Parachain for BridgeHubPolkadot { + const PARACHAIN_ID: u32 = BRIDGE_HUB_POLKADOT_PARACHAIN_ID; +} + +/// Identifier of BridgeHubPolkadot in the Polkadot relay chain. +// TODO: check me (https://github.com/paritytech/parity-bridges-common/issues/1945) +pub const BRIDGE_HUB_POLKADOT_PARACHAIN_ID: u32 = 1002; + +/// Name of the With-BridgeHubPolkadot messages pallet instance that is deployed at bridged chains. +// TODO: check me (https://github.com/paritytech/parity-bridges-common/issues/1945) +pub const WITH_BRIDGE_HUB_POLKADOT_MESSAGES_PALLET_NAME: &str = "BridgePolkadotMessages"; + +/// Name of the With-BridgeHubPolkadot bridge-relayers pallet instance that is deployed at bridged +/// chains. +// TODO: check me (https://github.com/paritytech/parity-bridges-common/issues/1945) +pub const WITH_BRIDGE_HUB_POLKADOT_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; + +decl_bridge_finality_runtime_apis!(bridge_hub_polkadot); +decl_bridge_messages_runtime_apis!(bridge_hub_polkadot); diff --git a/primitives/chain-bridge-hub-rococo/Cargo.toml b/primitives/chain-bridge-hub-rococo/Cargo.toml new file mode 100644 index 00000000000..85c4225ab55 --- /dev/null +++ b/primitives/chain-bridge-hub-rococo/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "bp-bridge-hub-rococo" +description = "Primitives of BridgeHubRococo parachain runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +# Bridge Dependencies + +bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-bridge-hub-cumulus/std", + "bp-messages/std", + "bp-runtime/std", + "frame-support/std", + "sp-api/std", + "sp-std/std", +] diff --git a/primitives/chain-bridge-hub-rococo/src/lib.rs b/primitives/chain-bridge-hub-rococo/src/lib.rs new file mode 100644 index 00000000000..936e4d1beb7 --- /dev/null +++ b/primitives/chain-bridge-hub-rococo/src/lib.rs @@ -0,0 +1,82 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Module with configuration which reflects BridgeHubRococo runtime setup (AccountId, Headers, +//! Hashes...) + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use bp_bridge_hub_cumulus::*; +use bp_messages::*; +use bp_runtime::{ + decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, Parachain, +}; +use frame_support::{ + dispatch::DispatchClass, + sp_runtime::{MultiAddress, MultiSigner}, + RuntimeDebug, +}; +use sp_std::prelude::*; + +/// BridgeHubRococo parachain. +#[derive(RuntimeDebug)] +pub struct BridgeHubRococo; + +impl Chain for BridgeHubRococo { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +impl Parachain for BridgeHubRococo { + const PARACHAIN_ID: u32 = BRIDGE_HUB_ROCOCO_PARACHAIN_ID; +} + +/// Public key of the chain account that may be used to verify signatures. +pub type AccountSigner = MultiSigner; + +/// The address format for describing accounts. +pub type Address = MultiAddress; + +/// Identifier of BridgeHubRococo in the Rococo relay chain. +pub const BRIDGE_HUB_ROCOCO_PARACHAIN_ID: u32 = 1013; + +/// Name of the With-BridgeHubRococo messages pallet instance that is deployed at bridged chains. +pub const WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME: &str = "BridgeRococoMessages"; + +/// Name of the With-BridgeHubRococo bridge-relayers pallet instance that is deployed at bridged +/// chains. +pub const WITH_BRIDGE_HUB_ROCOCO_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; + +decl_bridge_finality_runtime_apis!(bridge_hub_rococo); +decl_bridge_messages_runtime_apis!(bridge_hub_rococo); diff --git a/primitives/chain-bridge-hub-wococo/Cargo.toml b/primitives/chain-bridge-hub-wococo/Cargo.toml new file mode 100644 index 00000000000..24ecdb7adbc --- /dev/null +++ b/primitives/chain-bridge-hub-wococo/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "bp-bridge-hub-wococo" +description = "Primitives of BridgeHubWococo parachain runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-messages = { path = "../../primitives/messages", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-bridge-hub-cumulus/std", + "bp-runtime/std", + "bp-messages/std", + "frame-support/std", + "sp-api/std", + "sp-std/std", +] diff --git a/primitives/chain-bridge-hub-wococo/src/lib.rs b/primitives/chain-bridge-hub-wococo/src/lib.rs new file mode 100644 index 00000000000..00704995c5e --- /dev/null +++ b/primitives/chain-bridge-hub-wococo/src/lib.rs @@ -0,0 +1,72 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Module with configuration which reflects BridgeHubWococo runtime setup +//! (AccountId, Headers, Hashes...) + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use bp_bridge_hub_cumulus::*; +use bp_messages::*; +use bp_runtime::{ + decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, Parachain, +}; +use frame_support::{dispatch::DispatchClass, RuntimeDebug}; +use sp_std::prelude::*; + +/// BridgeHubWococo parachain. +#[derive(RuntimeDebug)] +pub struct BridgeHubWococo; + +impl Chain for BridgeHubWococo { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +impl Parachain for BridgeHubWococo { + const PARACHAIN_ID: u32 = BRIDGE_HUB_WOCOCO_PARACHAIN_ID; +} + +/// Identifier of BridgeHubWococo in the Wococo relay chain. +pub const BRIDGE_HUB_WOCOCO_PARACHAIN_ID: u32 = 1014; + +/// Name of the With-BridgeHubWococo messages pallet instance that is deployed at bridged chains. +pub const WITH_BRIDGE_HUB_WOCOCO_MESSAGES_PALLET_NAME: &str = "BridgeWococoMessages"; + +/// Name of the With-BridgeHubWococo bridge-relayers pallet instance that is deployed at bridged +/// chains. +pub const WITH_BRIDGE_HUB_WOCOCO_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; + +decl_bridge_finality_runtime_apis!(bridge_hub_wococo); +decl_bridge_messages_runtime_apis!(bridge_hub_wococo); diff --git a/primitives/chain-kusama/Cargo.toml b/primitives/chain-kusama/Cargo.toml new file mode 100644 index 00000000000..7f48ded1a37 --- /dev/null +++ b/primitives/chain-kusama/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "bp-kusama" +description = "Primitives of Kusama runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-header-chain = { path = "../header-chain", default-features = false } +bp-polkadot-core = { path = "../polkadot-core", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "bp-polkadot-core/std", + "bp-runtime/std", + "frame-support/std", + "sp-api/std", +] diff --git a/primitives/chain-kusama/src/lib.rs b/primitives/chain-kusama/src/lib.rs new file mode 100644 index 00000000000..8e5aec8afda --- /dev/null +++ b/primitives/chain-kusama/src/lib.rs @@ -0,0 +1,65 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +pub use bp_polkadot_core::*; + +use bp_header_chain::ChainWithGrandpa; +use bp_runtime::{decl_bridge_finality_runtime_apis, Chain}; +use frame_support::weights::Weight; + +/// Kusama Chain +pub struct Kusama; + +impl Chain for Kusama { + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hasher = ::Hasher; + type Header = ::Header; + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Index = ::Index; + type Signature = ::Signature; + + fn max_extrinsic_size() -> u32 { + PolkadotLike::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + PolkadotLike::max_extrinsic_weight() + } +} + +impl ChainWithGrandpa for Kusama { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_KUSAMA_GRANDPA_PALLET_NAME; + const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; +} + +/// Name of the parachains pallet in the Kusama runtime. +pub const PARAS_PALLET_NAME: &str = "Paras"; + +/// Name of the With-Kusama GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_KUSAMA_GRANDPA_PALLET_NAME: &str = "BridgeKusamaGrandpa"; + +decl_bridge_finality_runtime_apis!(kusama); diff --git a/primitives/chain-millau/Cargo.toml b/primitives/chain-millau/Cargo.toml new file mode 100644 index 00000000000..d1e2e0edd96 --- /dev/null +++ b/primitives/chain-millau/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "bp-millau" +description = "Primitives of Millau runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +fixed-hash = { version = "0.8.0", default-features = false } +hash256-std-hasher = { version = "0.15.2", default-features = false } +impl-codec = { version = "0.6", default-features = false } +impl-serde = { version = "0.4.0", optional = true } +parity-util-mem = { version = "0.12.0", default-features = false, features = ["primitive-types"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } + +# Bridge Dependencies + +bp-beefy = { path = "../beefy", default-features = false } +bp-header-chain = { path = "../header-chain", default-features = false } +bp-messages = { path = "../messages", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-beefy/std", + "bp-header-chain/std", + "bp-messages/std", + "bp-runtime/std", + "fixed-hash/std", + "frame-support/std", + "frame-system/std", + "hash256-std-hasher/std", + "impl-codec/std", + "impl-serde", + "parity-util-mem/std", + "scale-info/std", + "serde", + "sp-api/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", +] diff --git a/primitives/chain-millau/src/lib.rs b/primitives/chain-millau/src/lib.rs new file mode 100644 index 00000000000..b7f67ca07e2 --- /dev/null +++ b/primitives/chain-millau/src/lib.rs @@ -0,0 +1,249 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +mod millau_hash; + +use bp_beefy::ChainWithBeefy; +use bp_header_chain::ChainWithGrandpa; +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; +use bp_runtime::{decl_bridge_runtime_apis, Chain}; +use frame_support::{ + dispatch::DispatchClass, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, IdentityFee, Weight}, + RuntimeDebug, +}; +use frame_system::limits; +use scale_info::TypeInfo; +use sp_core::{storage::StateVersion, Hasher as HasherT}; +use sp_runtime::{ + traits::{IdentifyAccount, Verify}, + MultiSignature, MultiSigner, Perbill, +}; +use sp_std::prelude::*; +use sp_trie::{LayoutV0, LayoutV1, TrieConfiguration}; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; +use sp_runtime::traits::Keccak256; + +pub use millau_hash::MillauHash; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// Millau chain. This mostly depends on number of entries (and their density) in the storage trie. +/// Some reserve is reserved to account future chain growth. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// Number of bytes, included in the signed Millau transaction apart from the encoded call itself. +/// +/// Can be computed by subtracting encoded call size from raw transaction size. +pub const TX_EXTRA_BYTES: u32 = 103; + +/// Maximum weight of single Millau block. +/// +/// This represents 0.5 seconds of compute assuming a target block time of six seconds. +/// +/// Max PoV size is set to max value, since it isn't important for relay/standalone chains. +pub const MAXIMUM_BLOCK_WEIGHT: Weight = + Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), u64::MAX); + +/// Represents the portion of a block that will be used by Normal extrinsics. +pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// Maximal number of unrewarded relayer entries in Millau confirmation transaction. +pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 128; + +/// Maximal number of unconfirmed messages in Millau confirmation transaction. +pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128; + +/// The target length of a session (how often authorities change) on Millau measured in of number of +/// blocks. +/// +/// Note that since this is a target sessions may change before/after this time depending on network +/// conditions. +pub const SESSION_LENGTH: BlockNumber = 5 * time_units::MINUTES; + +/// Maximal number of GRANDPA authorities at Millau. +pub const MAX_AUTHORITIES_COUNT: u32 = 5; + +/// Reasonable number of headers in the `votes_ancestries` on Millau chain. +/// +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. +pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + +/// Approximate average header size in `votes_ancestries` field of justification on Millau chain. +/// +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. +pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 256; + +/// Approximate maximal header size on Millau chain. +/// +/// We expect maximal header to have digest item with the new authorities set for every consensus +/// engine (GRANDPA, Babe, BEEFY, ...) - so we multiply it by 3. And also +/// `AVERAGE_HEADER_SIZE_IN_JUSTIFICATION` bytes for other stuff. +/// +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. +pub const MAX_HEADER_SIZE: u32 = MAX_AUTHORITIES_COUNT + .saturating_mul(3) + .saturating_add(AVERAGE_HEADER_SIZE_IN_JUSTIFICATION); + +/// Re-export `time_units` to make usage easier. +pub use time_units::*; + +/// Human readable time units defined in terms of number of blocks. +pub mod time_units { + use super::BlockNumber; + + pub const MILLISECS_PER_BLOCK: u64 = 6000; + pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + + pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); + pub const HOURS: BlockNumber = MINUTES * 60; + pub const DAYS: BlockNumber = HOURS * 24; +} + +/// Block number type used in Millau. +pub type BlockNumber = u64; + +/// Hash type used in Millau. +pub type Hash = ::Out; + +/// Type of object that can produce hashes on Millau. +pub type Hasher = BlakeTwoAndKeccak256; + +/// The header type used by Millau. +pub type Header = sp_runtime::generic::Header; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = MultiSignature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +/// Public key of the chain account that may be used to verify signatures. +pub type AccountSigner = MultiSigner; + +/// Balance of an account. +pub type Balance = u64; + +/// Index of a transaction in the chain. +pub type Index = u32; + +/// Weight-to-Fee type used by Millau. +pub type WeightToFee = IdentityFee; + +/// Millau chain. +#[derive(RuntimeDebug)] +pub struct Millau; + +impl Chain for Millau { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +impl ChainWithGrandpa for Millau { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_MILLAU_GRANDPA_PALLET_NAME; + const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; +} + +impl ChainWithBeefy for Millau { + type CommitmentHasher = Keccak256; + type MmrHashing = Keccak256; + type MmrHash = ::Output; + type BeefyMmrLeafExtra = (); + type AuthorityId = bp_beefy::EcdsaValidatorId; + type AuthorityIdToMerkleLeaf = bp_beefy::BeefyEcdsaToEthereum; +} + +/// Millau Hasher (Blake2-256 ++ Keccak-256) implementation. +#[derive(PartialEq, Eq, Clone, Copy, RuntimeDebug, TypeInfo)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct BlakeTwoAndKeccak256; + +impl sp_core::Hasher for BlakeTwoAndKeccak256 { + type Out = MillauHash; + type StdHasher = hash256_std_hasher::Hash256StdHasher; + const LENGTH: usize = 64; + + fn hash(s: &[u8]) -> Self::Out { + let mut combined_hash = MillauHash::default(); + combined_hash.as_mut()[..32].copy_from_slice(&sp_io::hashing::blake2_256(s)); + combined_hash.as_mut()[32..].copy_from_slice(&sp_io::hashing::keccak_256(s)); + combined_hash + } +} + +impl sp_runtime::traits::Hash for BlakeTwoAndKeccak256 { + type Output = MillauHash; + + fn trie_root(input: Vec<(Vec, Vec)>, state_version: StateVersion) -> Self::Output { + match state_version { + StateVersion::V0 => LayoutV0::::trie_root(input), + StateVersion::V1 => LayoutV1::::trie_root(input), + } + } + + fn ordered_trie_root(input: Vec>, state_version: StateVersion) -> Self::Output { + match state_version { + StateVersion::V0 => LayoutV0::::ordered_trie_root(input), + StateVersion::V1 => LayoutV1::::ordered_trie_root(input), + } + } +} + +frame_support::parameter_types! { + pub BlockLength: limits::BlockLength = + limits::BlockLength::max_with_normal_ratio(2 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub BlockWeights: limits::BlockWeights = + limits::BlockWeights::with_sensible_defaults(MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO); +} + +/// Name of the With-Millau GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_MILLAU_GRANDPA_PALLET_NAME: &str = "BridgeMillauGrandpa"; +/// Name of the With-Millau messages pallet instance that is deployed at bridged chains. +pub const WITH_MILLAU_MESSAGES_PALLET_NAME: &str = "BridgeMillauMessages"; +/// Name of the transaction payment pallet at the Millau runtime. +pub const TRANSACTION_PAYMENT_PALLET_NAME: &str = "TransactionPayment"; + +decl_bridge_runtime_apis!(millau); diff --git a/primitives/chain-millau/src/millau_hash.rs b/primitives/chain-millau/src/millau_hash.rs new file mode 100644 index 00000000000..11968b2f282 --- /dev/null +++ b/primitives/chain-millau/src/millau_hash.rs @@ -0,0 +1,58 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use parity_util_mem::MallocSizeOf; +use scale_info::TypeInfo; +use sp_runtime::traits::CheckEqual; + +// `sp_core::H512` can't be used, because it doesn't implement `CheckEqual`, which is required +// by `frame_system::Config::Hash`. + +fixed_hash::construct_fixed_hash! { + /// Hash type used in Millau chain. + #[derive(MallocSizeOf, TypeInfo)] + pub struct MillauHash(64); +} + +#[cfg(feature = "std")] +impl_serde::impl_fixed_hash_serde!(MillauHash, 64); + +impl_codec::impl_fixed_hash_codec!(MillauHash, 64); + +impl CheckEqual for MillauHash { + #[cfg(feature = "std")] + fn check_equal(&self, other: &Self) { + use sp_core::hexdisplay::HexDisplay; + if self != other { + println!( + "Hash: given={}, expected={}", + HexDisplay::from(self.as_fixed_bytes()), + HexDisplay::from(other.as_fixed_bytes()), + ); + } + } + + #[cfg(not(feature = "std"))] + fn check_equal(&self, other: &Self) { + use frame_support::Printable; + + if self != other { + "Hash not equal".print(); + self.as_bytes().print(); + other.as_bytes().print(); + } + } +} diff --git a/primitives/chain-polkadot/Cargo.toml b/primitives/chain-polkadot/Cargo.toml new file mode 100644 index 00000000000..def26bdda1c --- /dev/null +++ b/primitives/chain-polkadot/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "bp-polkadot" +description = "Primitives of Polkadot runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-header-chain = { path = "../header-chain", default-features = false } +bp-polkadot-core = { path = "../polkadot-core", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "bp-polkadot-core/std", + "bp-runtime/std", + "frame-support/std", + "sp-api/std", +] diff --git a/primitives/chain-polkadot/src/lib.rs b/primitives/chain-polkadot/src/lib.rs new file mode 100644 index 00000000000..92995601698 --- /dev/null +++ b/primitives/chain-polkadot/src/lib.rs @@ -0,0 +1,65 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +pub use bp_polkadot_core::*; + +use bp_header_chain::ChainWithGrandpa; +use bp_runtime::{decl_bridge_finality_runtime_apis, Chain}; +use frame_support::weights::Weight; + +/// Polkadot Chain +pub struct Polkadot; + +impl Chain for Polkadot { + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hasher = ::Hasher; + type Header = ::Header; + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Index = ::Index; + type Signature = ::Signature; + + fn max_extrinsic_size() -> u32 { + PolkadotLike::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + PolkadotLike::max_extrinsic_weight() + } +} + +impl ChainWithGrandpa for Polkadot { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_POLKADOT_GRANDPA_PALLET_NAME; + const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; +} + +/// Name of the parachains pallet in the Polkadot runtime. +pub const PARAS_PALLET_NAME: &str = "Paras"; + +/// Name of the With-Polkadot GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_POLKADOT_GRANDPA_PALLET_NAME: &str = "BridgePolkadotGrandpa"; + +decl_bridge_finality_runtime_apis!(polkadot); diff --git a/primitives/chain-rialto-parachain/Cargo.toml b/primitives/chain-rialto-parachain/Cargo.toml new file mode 100644 index 00000000000..3335c6bcc41 --- /dev/null +++ b/primitives/chain-rialto-parachain/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "bp-rialto-parachain" +description = "Primitives of Rialto parachain runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } +bp-messages = { path = "../messages", default-features = false } +bp-polkadot-core = { path = "../polkadot-core", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-bridge-hub-cumulus/std", + "bp-messages/std", + "bp-runtime/std", + "frame-support/std", + "frame-system/std", + "sp-api/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/primitives/chain-rialto-parachain/src/lib.rs b/primitives/chain-rialto-parachain/src/lib.rs new file mode 100644 index 00000000000..67b1a135f34 --- /dev/null +++ b/primitives/chain-rialto-parachain/src/lib.rs @@ -0,0 +1,152 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; +use bp_runtime::{decl_bridge_runtime_apis, Chain, Parachain}; +use frame_support::{ + dispatch::DispatchClass, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, IdentityFee, Weight}, + RuntimeDebug, +}; +use frame_system::limits; +use sp_core::Hasher as HasherT; +use sp_runtime::{ + traits::{BlakeTwo256, IdentifyAccount, Verify}, + MultiSignature, MultiSigner, Perbill, +}; +use sp_std::vec::Vec; + +/// Identifier of RialtoParachain in the Rialto relay chain. +/// +/// This identifier is not something that is declared either by Rialto or RialtoParachain. This +/// is an identifier of registration. So in theory it may be changed. But since bridge is going +/// to be deployed after parachain registration AND since parachain de-registration is highly +/// likely impossible, it is fine to declare this constant here. +pub const RIALTO_PARACHAIN_ID: u32 = 2000; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// RialtoParachain chain. This mostly depends on number of entries (and their density) in the +/// storage trie. Some reserve is reserved to account future chain growth. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// Can be computed by subtracting encoded call size from raw transaction size. +pub const TX_EXTRA_BYTES: u32 = 104; + +/// Maximal weight of single RialtoParachain block. +/// +/// This represents two seconds of compute assuming a target block time of six seconds. +/// +/// Max PoV size is set to `5Mb` as all Cumulus-based parachains do. +pub const MAXIMUM_BLOCK_WEIGHT: Weight = + Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), 5 * 1024 * 1024); + +/// Represents the portion of a block that will be used by Normal extrinsics. +pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// Maximal number of unrewarded relayer entries in Rialto confirmation transaction. +pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; + +/// Maximal number of unconfirmed messages in Rialto confirmation transaction. +pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1024; + +/// Block number type used in Rialto. +pub type BlockNumber = u32; + +/// Hash type used in Rialto. +pub type Hash = ::Out; + +/// The type of object that can produce hashes on Rialto. +pub type Hasher = BlakeTwo256; + +/// The header type used by Rialto. +pub type Header = sp_runtime::generic::Header; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = MultiSignature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +/// Public key of the chain account that may be used to verify signatures. +pub type AccountSigner = MultiSigner; + +/// Balance of an account. +pub type Balance = u128; + +/// An instant or duration in time. +pub type Moment = u64; + +/// Index of a transaction in the parachain. +pub type Index = u32; + +/// Weight-to-Fee type used by Rialto parachain. +pub type WeightToFee = IdentityFee; + +/// Rialto parachain. +#[derive(RuntimeDebug)] +pub struct RialtoParachain; + +impl Chain for RialtoParachain { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +impl Parachain for RialtoParachain { + const PARACHAIN_ID: u32 = RIALTO_PARACHAIN_ID; +} + +// Technically this is incorrect, because rialto-parachain isn't a bridge hub, but we're +// trying to keep it close to the bridge hubs code (at least in this aspect). +pub use bp_bridge_hub_cumulus::SignedExtension; + +frame_support::parameter_types! { + pub BlockLength: limits::BlockLength = + limits::BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub BlockWeights: limits::BlockWeights = + limits::BlockWeights::with_sensible_defaults(MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO); +} + +/// Name of the With-Rialto-Parachain messages pallet instance that is deployed at bridged chains. +pub const WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME: &str = "BridgeRialtoParachainMessages"; +/// Name of the transaction payment pallet at the Rialto parachain runtime. +pub const TRANSACTION_PAYMENT_PALLET_NAME: &str = "TransactionPayment"; + +decl_bridge_runtime_apis!(rialto_parachain); diff --git a/primitives/chain-rialto/Cargo.toml b/primitives/chain-rialto/Cargo.toml new file mode 100644 index 00000000000..0a70e0504c9 --- /dev/null +++ b/primitives/chain-rialto/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "bp-rialto" +description = "Primitives of Rialto runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-header-chain = { path = "../header-chain", default-features = false } +bp-messages = { path = "../messages", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "bp-messages/std", + "bp-runtime/std", + "frame-support/std", + "frame-system/std", + "sp-api/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/primitives/chain-rialto/src/lib.rs b/primitives/chain-rialto/src/lib.rs new file mode 100644 index 00000000000..01349f131be --- /dev/null +++ b/primitives/chain-rialto/src/lib.rs @@ -0,0 +1,214 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +use bp_header_chain::ChainWithGrandpa; +use bp_messages::{ + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, +}; +use bp_runtime::{decl_bridge_runtime_apis, Chain}; +use frame_support::{ + dispatch::DispatchClass, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, IdentityFee, Weight}, + RuntimeDebug, +}; +use frame_system::limits; +use sp_core::Hasher as HasherT; +use sp_runtime::{ + traits::{BlakeTwo256, IdentifyAccount, Verify}, + MultiSignature, MultiSigner, Perbill, +}; +use sp_std::prelude::*; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// Rialto chain. This mostly depends on number of entries (and their density) in the storage trie. +/// Some reserve is reserved to account future chain growth. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// Number of bytes, included in the signed Rialto transaction apart from the encoded call itself. +/// +/// Can be computed by subtracting encoded call size from raw transaction size. +pub const TX_EXTRA_BYTES: u32 = 104; + +/// Maximal weight of single Rialto block. +/// +/// This represents two seconds of compute assuming a target block time of six seconds. +/// +/// Max PoV size is set to max value, since it isn't important for relay/standalone chains. +pub const MAXIMUM_BLOCK_WEIGHT: Weight = + Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), u64::MAX); + +/// Represents the portion of a block that will be used by Normal extrinsics. +pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// Maximal number of unrewarded relayer entries in Rialto confirmation transaction. +pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; + +/// Maximal number of unconfirmed messages in Rialto confirmation transaction. +pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1024; + +/// The target length of a session (how often authorities change) on Rialto measured in of number of +/// blocks. +/// +/// Note that since this is a target sessions may change before/after this time depending on network +/// conditions. +pub const SESSION_LENGTH: BlockNumber = 4; + +/// Maximal number of GRANDPA authorities at Rialto. +pub const MAX_AUTHORITIES_COUNT: u32 = 5; + +/// Reasonable number of headers in the `votes_ancestries` on Rialto chain. +/// +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. +pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; + +/// Approximate average header size in `votes_ancestries` field of justification on Rialto chain. +/// +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. +pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 256; + +/// Approximate maximal header size on Rialto chain. +/// +/// We expect maximal header to have digest item with the new authorities set for every consensus +/// engine (GRANDPA, Babe, BEEFY, ...) - so we multiply it by 3. And also +/// `AVERAGE_HEADER_SIZE_IN_JUSTIFICATION` bytes for other stuff. +/// +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. +pub const MAX_HEADER_SIZE: u32 = MAX_AUTHORITIES_COUNT + .saturating_mul(3) + .saturating_add(AVERAGE_HEADER_SIZE_IN_JUSTIFICATION); + +/// Maximal size of encoded `bp_parachains::ParaStoredHeaderData` structure among all Rialto +/// parachains. +/// +/// It includes the block number and state root, so it shall be near 40 bytes, but let's have some +/// reserve. +pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; + +/// Re-export `time_units` to make usage easier. +pub use time_units::*; + +/// Human readable time units defined in terms of number of blocks. +pub mod time_units { + use super::{BlockNumber, SESSION_LENGTH}; + + pub const MILLISECS_PER_BLOCK: u64 = 6000; + pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + + pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); + pub const HOURS: BlockNumber = MINUTES * 60; + pub const DAYS: BlockNumber = HOURS * 24; + + pub const EPOCH_DURATION_IN_SLOTS: BlockNumber = SESSION_LENGTH; + + // 1 in 4 blocks (on average, not counting collisions) will be primary babe blocks. + pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4); +} + +/// Block number type used in Rialto. +pub type BlockNumber = u32; + +/// Hash type used in Rialto. +pub type Hash = ::Out; + +/// The type of object that can produce hashes on Rialto. +pub type Hasher = BlakeTwo256; + +/// The header type used by Rialto. +pub type Header = sp_runtime::generic::Header; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = MultiSignature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +/// Public key of the chain account that may be used to verify signatures. +pub type AccountSigner = MultiSigner; + +/// Balance of an account. +pub type Balance = u128; + +/// An instant or duration in time. +pub type Moment = u64; + +/// Index of a transaction in the chain. +pub type Index = u32; + +/// Weight-to-Fee type used by Rialto. +pub type WeightToFee = IdentityFee; + +/// Rialto chain. +#[derive(RuntimeDebug)] +pub struct Rialto; + +impl Chain for Rialto { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +impl ChainWithGrandpa for Rialto { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_RIALTO_GRANDPA_PALLET_NAME; + const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; +} + +frame_support::parameter_types! { + pub BlockLength: limits::BlockLength = + limits::BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub BlockWeights: limits::BlockWeights = + limits::BlockWeights::with_sensible_defaults(MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO); +} + +/// Name of the With-Rialto GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_RIALTO_GRANDPA_PALLET_NAME: &str = "BridgeRialtoGrandpa"; +/// Name of the With-Rialto messages pallet instance that is deployed at bridged chains. +pub const WITH_RIALTO_MESSAGES_PALLET_NAME: &str = "BridgeRialtoMessages"; +/// Name of the With-Rialto parachains bridge pallet instance that is deployed at bridged chains. +pub const WITH_RIALTO_BRIDGE_PARAS_PALLET_NAME: &str = "BridgeRialtoParachains"; + +/// Name of the parachain registrar pallet in the Rialto runtime. +pub const PARAS_REGISTRAR_PALLET_NAME: &str = "Registrar"; + +/// Name of the parachains pallet in the Rialto runtime. +pub const PARAS_PALLET_NAME: &str = "Paras"; + +decl_bridge_runtime_apis!(rialto); diff --git a/primitives/chain-rococo/Cargo.toml b/primitives/chain-rococo/Cargo.toml new file mode 100644 index 00000000000..4e21bd38b7a --- /dev/null +++ b/primitives/chain-rococo/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "bp-rococo" +description = "Primitives of Rococo runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-header-chain = { path = "../header-chain", default-features = false } +bp-polkadot-core = { path = "../polkadot-core", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies + +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "bp-polkadot-core/std", + "bp-runtime/std", + "frame-support/std", + "sp-api/std", +] diff --git a/primitives/chain-rococo/src/lib.rs b/primitives/chain-rococo/src/lib.rs new file mode 100644 index 00000000000..0cb0b1d41e6 --- /dev/null +++ b/primitives/chain-rococo/src/lib.rs @@ -0,0 +1,76 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +pub use bp_polkadot_core::*; + +use bp_header_chain::ChainWithGrandpa; +use bp_runtime::{decl_bridge_finality_runtime_apis, Chain}; +use frame_support::{parameter_types, weights::Weight}; + +/// Rococo Chain +pub struct Rococo; + +impl Chain for Rococo { + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hasher = ::Hasher; + type Header = ::Header; + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Index = ::Index; + type Signature = ::Signature; + + fn max_extrinsic_size() -> u32 { + PolkadotLike::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + PolkadotLike::max_extrinsic_weight() + } +} + +impl ChainWithGrandpa for Rococo { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_ROCOCO_GRANDPA_PALLET_NAME; + const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; +} + +parameter_types! { + pub const SS58Prefix: u8 = 42; +} + +/// Name of the parachains pallet in the Rococo runtime. +pub const PARAS_PALLET_NAME: &str = "Paras"; + +/// Name of the With-Rococo GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_ROCOCO_GRANDPA_PALLET_NAME: &str = "BridgeRococoGrandpa"; + +/// Maximal size of encoded `bp_parachains::ParaStoredHeaderData` structure among all Rococo +/// parachains. +/// +/// It includes the block number and state root, so it shall be near 40 bytes, but let's have some +/// reserve. +pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; + +decl_bridge_finality_runtime_apis!(rococo); diff --git a/primitives/chain-westend/Cargo.toml b/primitives/chain-westend/Cargo.toml new file mode 100644 index 00000000000..13a2e597f9d --- /dev/null +++ b/primitives/chain-westend/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "bp-westend" +description = "Primitives of Westend runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-header-chain = { path = "../header-chain", default-features = false } +bp-polkadot-core = { path = "../polkadot-core", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "bp-polkadot-core/std", + "bp-runtime/std", + "frame-support/std", + "sp-api/std", +] diff --git a/primitives/chain-westend/src/lib.rs b/primitives/chain-westend/src/lib.rs new file mode 100644 index 00000000000..74e8895aff9 --- /dev/null +++ b/primitives/chain-westend/src/lib.rs @@ -0,0 +1,108 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +pub use bp_polkadot_core::*; + +use bp_header_chain::ChainWithGrandpa; +use bp_runtime::{decl_bridge_finality_runtime_apis, Chain, Parachain}; +use frame_support::weights::Weight; + +/// Westend Chain +pub struct Westend; + +impl Chain for Westend { + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hasher = ::Hasher; + type Header = ::Header; + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Index = ::Index; + type Signature = ::Signature; + + fn max_extrinsic_size() -> u32 { + PolkadotLike::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + PolkadotLike::max_extrinsic_weight() + } +} + +impl ChainWithGrandpa for Westend { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_WESTEND_GRANDPA_PALLET_NAME; + const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; +} + +/// Westmint parachain definition +#[derive(Debug, Clone, Copy)] +pub struct Westmint; + +// Westmint seems to use the same configuration as all Polkadot-like chains, so we'll use Westend +// primitives here. +impl Chain for Westmint { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Nonce; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + Westend::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + Westend::max_extrinsic_weight() + } +} + +impl Parachain for Westmint { + const PARACHAIN_ID: u32 = WESTMINT_PARACHAIN_ID; +} + +/// Name of the parachains pallet at the Westend runtime. +pub const PARAS_PALLET_NAME: &str = "Paras"; + +/// Name of the With-Westend GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_WESTEND_GRANDPA_PALLET_NAME: &str = "BridgeWestendGrandpa"; +/// Name of the With-Westend parachains bridge pallet instance that is deployed at bridged chains. +pub const WITH_WESTEND_BRIDGE_PARAS_PALLET_NAME: &str = "BridgeWestendParachains"; + +/// Maximal SCALE-encoded size of parachains headers that are stored at Westend `Paras` pallet. +/// +/// It includes the block number and state root, so it shall be near 40 bytes, but let's have some +/// reserve. +pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; + +/// Identifier of Westmint parachain at the Westend relay chain. +pub const WESTMINT_PARACHAIN_ID: u32 = 1000; + +decl_bridge_finality_runtime_apis!(westend); + +decl_bridge_finality_runtime_apis!(westmint); diff --git a/primitives/chain-wococo/Cargo.toml b/primitives/chain-wococo/Cargo.toml new file mode 100644 index 00000000000..25fd7b9fd94 --- /dev/null +++ b/primitives/chain-wococo/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "bp-wococo" +description = "Primitives of Wococo runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] + +# Bridge Dependencies + +bp-header-chain = { path = "../header-chain", default-features = false } +bp-polkadot-core = { path = "../polkadot-core", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } +bp-rococo = { path = "../chain-rococo", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "bp-polkadot-core/std", + "bp-runtime/std", + "bp-rococo/std", + "frame-support/std", + "sp-api/std", +] diff --git a/primitives/chain-wococo/src/lib.rs b/primitives/chain-wococo/src/lib.rs new file mode 100644 index 00000000000..2df019496ab --- /dev/null +++ b/primitives/chain-wococo/src/lib.rs @@ -0,0 +1,65 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +pub use bp_polkadot_core::*; +pub use bp_rococo::{ + SS58Prefix, MAX_AUTHORITIES_COUNT, MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE, PARAS_PALLET_NAME, +}; + +use bp_header_chain::ChainWithGrandpa; +use bp_runtime::{decl_bridge_finality_runtime_apis, Chain}; +use frame_support::weights::Weight; + +/// Wococo Chain +pub struct Wococo; + +impl Chain for Wococo { + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hasher = ::Hasher; + type Header = ::Header; + + type AccountId = ::AccountId; + type Balance = ::Balance; + type Index = ::Index; + type Signature = ::Signature; + + fn max_extrinsic_size() -> u32 { + PolkadotLike::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + PolkadotLike::max_extrinsic_weight() + } +} + +impl ChainWithGrandpa for Wococo { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_WOCOCO_GRANDPA_PALLET_NAME; + const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = + REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; + const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; +} + +/// Name of the With-Wococo GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_WOCOCO_GRANDPA_PALLET_NAME: &str = "BridgeWococoGrandpa"; + +decl_bridge_finality_runtime_apis!(wococo); diff --git a/primitives/header-chain/Cargo.toml b/primitives/header-chain/Cargo.toml new file mode 100644 index 00000000000..5b9f87614a8 --- /dev/null +++ b/primitives/header-chain/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "bp-header-chain" +description = "A common interface for describing what a bridge pallet should be able to do." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +finality-grandpa = { version = "0.16.2", default-features = false } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true } + +# Bridge dependencies + +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +bp-test-utils = { path = "../test-utils" } +hex = "0.4" +hex-literal = "0.4" + +[features] +default = ["std"] +std = [ + "bp-runtime/std", + "codec/std", + "finality-grandpa/std", + "serde/std", + "frame-support/std", + "scale-info/std", + "sp-core/std", + "sp-consensus-grandpa/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/primitives/header-chain/src/justification.rs b/primitives/header-chain/src/justification.rs new file mode 100644 index 00000000000..06ed782763d --- /dev/null +++ b/primitives/header-chain/src/justification.rs @@ -0,0 +1,390 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Pallet for checking GRANDPA Finality Proofs. +//! +//! Adapted copy of substrate/client/finality-grandpa/src/justification.rs. If origin +//! will ever be moved to the sp_consensus_grandpa, we should reuse that implementation. + +use crate::ChainWithGrandpa; + +use bp_runtime::{BlockNumberOf, Chain, HashOf}; +use codec::{Decode, Encode, MaxEncodedLen}; +use finality_grandpa::voter_set::VoterSet; +use frame_support::RuntimeDebug; +use scale_info::TypeInfo; +use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, SetId}; +use sp_runtime::{traits::Header as HeaderT, SaturatedConversion}; +use sp_std::{ + collections::{btree_map::BTreeMap, btree_set::BTreeSet}, + prelude::*, +}; + +/// A GRANDPA Justification is a proof that a given header was finalized +/// at a certain height and with a certain set of authorities. +/// +/// This particular proof is used to prove that headers on a bridged chain +/// (so not our chain) have been finalized correctly. +#[derive(Encode, Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] +pub struct GrandpaJustification { + /// The round (voting period) this justification is valid for. + pub round: u64, + /// The set of votes for the chain which is to be finalized. + pub commit: + finality_grandpa::Commit, + /// A proof that the chain of blocks in the commit are related to each other. + pub votes_ancestries: Vec
, +} + +impl GrandpaJustification { + /// Returns reasonable size of justification using constants from the provided chain. + /// + /// An imprecise analogue of `MaxEncodedLen` implementation. We don't use it for + /// any precise calculations - that's just an estimation. + pub fn max_reasonable_size(required_precommits: u32) -> u32 + where + C: Chain
+ ChainWithGrandpa, + { + // we don't need precise results here - just estimations, so some details + // are removed from computations (e.g. bytes required to encode vector length) + + // structures in `finality_grandpa` crate are not implementing `MaxEncodedLength`, so + // here's our estimation for the `finality_grandpa::Commit` struct size + // + // precommit is: hash + number + // signed precommit is: precommit + signature (64b) + authority id + // commit is: hash + number + vec of signed precommits + let signed_precommit_size: u32 = BlockNumberOf::::max_encoded_len() + .saturating_add(HashOf::::max_encoded_len().saturated_into()) + .saturating_add(64) + .saturating_add(AuthorityId::max_encoded_len().saturated_into()) + .saturated_into(); + let max_expected_signed_commit_size = signed_precommit_size + .saturating_mul(required_precommits) + .saturating_add(BlockNumberOf::::max_encoded_len().saturated_into()) + .saturating_add(HashOf::::max_encoded_len().saturated_into()); + + // justification is a signed GRANDPA commit, `votes_ancestries` vector and round number + let max_expected_votes_ancestries_size = C::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY + .saturating_mul(C::AVERAGE_HEADER_SIZE_IN_JUSTIFICATION); + + 8u32.saturating_add(max_expected_signed_commit_size) + .saturating_add(max_expected_votes_ancestries_size) + } +} + +impl crate::FinalityProof for GrandpaJustification { + fn target_header_number(&self) -> H::Number { + self.commit.target_number + } +} + +/// Justification verification error. +#[derive(Eq, RuntimeDebug, PartialEq)] +pub enum Error { + /// Failed to decode justification. + JustificationDecode, + /// Justification is finalizing unexpected header. + InvalidJustificationTarget, + /// Justification contains redundant votes. + RedundantVotesInJustification, + /// Justification contains unknown authority precommit. + UnknownAuthorityVote, + /// Justification contains duplicate authority precommit. + DuplicateAuthorityVote, + /// The authority has provided an invalid signature. + InvalidAuthoritySignature, + /// The justification contains precommit for header that is not a descendant of the commit + /// header. + PrecommitIsNotCommitDescendant, + /// The cumulative weight of all votes in the justification is not enough to justify commit + /// header finalization. + TooLowCumulativeWeight, + /// The justification contains extra (unused) headers in its `votes_ancestries` field. + ExtraHeadersInVotesAncestries, +} + +/// Given GRANDPA authorities set size, return number of valid authorities votes that the +/// justification must have to be valid. +/// +/// This function assumes that all authorities have the same vote weight. +pub fn required_justification_precommits(authorities_set_length: u32) -> u32 { + authorities_set_length - authorities_set_length.saturating_sub(1) / 3 +} + +/// Decode justification target. +pub fn decode_justification_target( + raw_justification: &[u8], +) -> Result<(Header::Hash, Header::Number), Error> { + GrandpaJustification::
::decode(&mut &*raw_justification) + .map(|justification| (justification.commit.target_hash, justification.commit.target_number)) + .map_err(|_| Error::JustificationDecode) +} + +/// Verify and optimize given justification by removing unknown and duplicate votes. +pub fn verify_and_optimize_justification( + finalized_target: (Header::Hash, Header::Number), + authorities_set_id: SetId, + authorities_set: &VoterSet, + justification: GrandpaJustification
, +) -> Result, Error> +where + Header::Number: finality_grandpa::BlockNumberOps, +{ + let mut optimizer = OptimizationCallbacks(Vec::new()); + verify_justification_with_callbacks( + finalized_target, + authorities_set_id, + authorities_set, + &justification, + &mut optimizer, + )?; + Ok(optimizer.optimize(justification)) +} + +/// Verify that justification, that is generated by given authority set, finalizes given header. +pub fn verify_justification( + finalized_target: (Header::Hash, Header::Number), + authorities_set_id: SetId, + authorities_set: &VoterSet, + justification: &GrandpaJustification
, +) -> Result<(), Error> +where + Header::Number: finality_grandpa::BlockNumberOps, +{ + verify_justification_with_callbacks( + finalized_target, + authorities_set_id, + authorities_set, + justification, + &mut StrictVerificationCallbacks, + ) +} + +/// Verification callbacks. +trait VerificationCallbacks { + /// Called when we see a precommit from unknown authority. + fn on_unkown_authority(&mut self, precommit_idx: usize) -> Result<(), Error>; + /// Called when we see a precommit with duplicate vote from known authority. + fn on_duplicate_authority_vote(&mut self, precommit_idx: usize) -> Result<(), Error>; + /// Called when we see a precommit after we've collected enough votes from authorities. + fn on_redundant_authority_vote(&mut self, precommit_idx: usize) -> Result<(), Error>; +} + +/// Verification callbacks that reject all unknown, duplicate or redundant votes. +struct StrictVerificationCallbacks; + +impl VerificationCallbacks for StrictVerificationCallbacks { + fn on_unkown_authority(&mut self, _precommit_idx: usize) -> Result<(), Error> { + Err(Error::UnknownAuthorityVote) + } + + fn on_duplicate_authority_vote(&mut self, _precommit_idx: usize) -> Result<(), Error> { + Err(Error::DuplicateAuthorityVote) + } + + fn on_redundant_authority_vote(&mut self, _precommit_idx: usize) -> Result<(), Error> { + Err(Error::RedundantVotesInJustification) + } +} + +/// Verification callbacks for justification optimization. +struct OptimizationCallbacks(Vec); + +impl OptimizationCallbacks { + fn optimize( + self, + mut justification: GrandpaJustification
, + ) -> GrandpaJustification
{ + for invalid_precommit_idx in self.0.into_iter().rev() { + justification.commit.precommits.remove(invalid_precommit_idx); + } + justification + } +} + +impl VerificationCallbacks for OptimizationCallbacks { + fn on_unkown_authority(&mut self, precommit_idx: usize) -> Result<(), Error> { + self.0.push(precommit_idx); + Ok(()) + } + + fn on_duplicate_authority_vote(&mut self, precommit_idx: usize) -> Result<(), Error> { + self.0.push(precommit_idx); + Ok(()) + } + + fn on_redundant_authority_vote(&mut self, precommit_idx: usize) -> Result<(), Error> { + self.0.push(precommit_idx); + Ok(()) + } +} + +/// Verify that justification, that is generated by given authority set, finalizes given header. +fn verify_justification_with_callbacks( + finalized_target: (Header::Hash, Header::Number), + authorities_set_id: SetId, + authorities_set: &VoterSet, + justification: &GrandpaJustification
, + callbacks: &mut C, +) -> Result<(), Error> +where + Header::Number: finality_grandpa::BlockNumberOps, +{ + // ensure that it is justification for the expected header + if (justification.commit.target_hash, justification.commit.target_number) != finalized_target { + return Err(Error::InvalidJustificationTarget) + } + + let threshold = authorities_set.threshold().0.into(); + let mut chain = AncestryChain::new(&justification.votes_ancestries); + let mut signature_buffer = Vec::new(); + let mut votes = BTreeSet::new(); + let mut cumulative_weight = 0u64; + + for (precommit_idx, signed) in justification.commit.precommits.iter().enumerate() { + // if we have collected enough precommits, we probabably want to fail/remove extra + // precommits + if cumulative_weight >= threshold { + callbacks.on_redundant_authority_vote(precommit_idx)?; + continue + } + + // authority must be in the set + let authority_info = match authorities_set.get(&signed.id) { + Some(authority_info) => authority_info, + None => { + callbacks.on_unkown_authority(precommit_idx)?; + continue + }, + }; + + // check if authority has already voted in the same round. + // + // there's a lot of code in `validate_commit` and `import_precommit` functions inside + // `finality-grandpa` crate (mostly related to reporting equivocations). But the only thing + // that we care about is that only first vote from the authority is accepted + if !votes.insert(signed.id.clone()) { + callbacks.on_duplicate_authority_vote(precommit_idx)?; + continue + } + + // everything below this line can't just `continue`, because state is already altered + + // precommits aren't allowed for block lower than the target + if signed.precommit.target_number < justification.commit.target_number { + return Err(Error::PrecommitIsNotCommitDescendant) + } + // all precommits must be descendants of target block + chain = chain + .ensure_descendant(&justification.commit.target_hash, &signed.precommit.target_hash)?; + // since we know now that the precommit target is the descendant of the justification + // target, we may increase 'weight' of the justification target + // + // there's a lot of code in the `VoteGraph::insert` method inside `finality-grandpa` crate, + // but in the end it is only used to find GHOST, which we don't care about. The only thing + // that we care about is that the justification target has enough weight + cumulative_weight = cumulative_weight.checked_add(authority_info.weight().0.into()).expect( + "sum of weights of ALL authorities is expected not to overflow - this is guaranteed by\ + existence of VoterSet;\ + the order of loop conditions guarantees that we can account vote from same authority\ + multiple times;\ + thus we'll never overflow the u64::MAX;\ + qed", + ); + + // verify authority signature + if !sp_consensus_grandpa::check_message_signature_with_buffer( + &finality_grandpa::Message::Precommit(signed.precommit.clone()), + &signed.id, + &signed.signature, + justification.round, + authorities_set_id, + &mut signature_buffer, + ) { + return Err(Error::InvalidAuthoritySignature) + } + } + + // check that there are no extra headers in the justification + if !chain.unvisited.is_empty() { + return Err(Error::ExtraHeadersInVotesAncestries) + } + + // check that the cumulative weight of validators voted for the justification target (or one + // of its descendents) is larger than required threshold. + if cumulative_weight >= threshold { + Ok(()) + } else { + Err(Error::TooLowCumulativeWeight) + } +} + +/// Votes ancestries with useful methods. +#[derive(RuntimeDebug)] +pub struct AncestryChain { + /// Header hash => parent header hash mapping. + pub parents: BTreeMap, + /// Hashes of headers that were not visited by `is_ancestor` method. + pub unvisited: BTreeSet, +} + +impl AncestryChain
{ + /// Create new ancestry chain. + pub fn new(ancestry: &[Header]) -> AncestryChain
{ + let mut parents = BTreeMap::new(); + let mut unvisited = BTreeSet::new(); + for ancestor in ancestry { + let hash = ancestor.hash(); + let parent_hash = *ancestor.parent_hash(); + parents.insert(hash, parent_hash); + unvisited.insert(hash); + } + AncestryChain { parents, unvisited } + } + + /// Returns `Ok(_)` if `precommit_target` is a descendant of the `commit_target` block and + /// `Err(_)` otherwise. + pub fn ensure_descendant( + mut self, + commit_target: &Header::Hash, + precommit_target: &Header::Hash, + ) -> Result { + let mut current_hash = *precommit_target; + loop { + if current_hash == *commit_target { + break + } + + let is_visited_before = !self.unvisited.remove(¤t_hash); + current_hash = match self.parents.get(¤t_hash) { + Some(parent_hash) => { + if is_visited_before { + // `Some(parent_hash)` means that the `current_hash` is in the `parents` + // container `is_visited_before` means that it has been visited before in + // some of previous calls => since we assume that previous call has finished + // with `true`, this also will be finished with `true` + return Ok(self) + } + + *parent_hash + }, + None => return Err(Error::PrecommitIsNotCommitDescendant), + }; + } + Ok(self) + } +} diff --git a/primitives/header-chain/src/lib.rs b/primitives/header-chain/src/lib.rs new file mode 100644 index 00000000000..5e2bbad242e --- /dev/null +++ b/primitives/header-chain/src/lib.rs @@ -0,0 +1,227 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Defines traits which represent a common interface for Substrate pallets which want to +//! incorporate bridge functionality. + +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_runtime::{ + BasicOperatingMode, Chain, HashOf, HasherOf, HeaderOf, RawStorageProof, StorageProofChecker, + StorageProofError, +}; +use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}; +use core::{clone::Clone, cmp::Eq, default::Default, fmt::Debug}; +use frame_support::PalletError; +use scale_info::TypeInfo; +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; +use sp_consensus_grandpa::{AuthorityList, ConsensusLog, SetId, GRANDPA_ENGINE_ID}; +use sp_runtime::{traits::Header as HeaderT, Digest, RuntimeDebug}; +use sp_std::boxed::Box; + +pub mod justification; +pub mod storage_keys; + +/// Header chain error. +#[derive(Clone, Decode, Encode, Eq, PartialEq, PalletError, Debug, TypeInfo)] +pub enum HeaderChainError { + /// Header with given hash is missing from the chain. + UnknownHeader, + /// Storage proof related error. + StorageProof(StorageProofError), +} + +/// Header data that we're storing on-chain. +/// +/// Even though we may store full header, our applications (XCM) only use couple of header +/// fields. Extracting those values makes on-chain storage and PoV smaller, which is good. +#[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] +pub struct StoredHeaderData { + /// Header number. + pub number: Number, + /// Header state root. + pub state_root: Hash, +} + +/// Stored header data builder. +pub trait StoredHeaderDataBuilder { + /// Build header data from self. + fn build(&self) -> StoredHeaderData; +} + +impl StoredHeaderDataBuilder for H { + fn build(&self) -> StoredHeaderData { + StoredHeaderData { number: *self.number(), state_root: *self.state_root() } + } +} + +/// Substrate header chain, abstracted from the way it is stored. +pub trait HeaderChain { + /// Returns state (storage) root of given finalized header. + fn finalized_header_state_root(header_hash: HashOf) -> Option>; + /// Parse storage proof using finalized header. + fn parse_finalized_storage_proof( + header_hash: HashOf, + storage_proof: RawStorageProof, + parse: impl FnOnce(StorageProofChecker>) -> R, + ) -> Result { + let state_root = Self::finalized_header_state_root(header_hash) + .ok_or(HeaderChainError::UnknownHeader)?; + let storage_proof_checker = bp_runtime::StorageProofChecker::new(state_root, storage_proof) + .map_err(HeaderChainError::StorageProof)?; + + Ok(parse(storage_proof_checker)) + } +} + +/// A type that can be used as a parameter in a dispatchable function. +/// +/// When using `decl_module` all arguments for call functions must implement this trait. +pub trait Parameter: Codec + EncodeLike + Clone + Eq + Debug + TypeInfo {} +impl Parameter for T where T: Codec + EncodeLike + Clone + Eq + Debug + TypeInfo {} + +/// A GRANDPA Authority List and ID. +#[derive(Default, Encode, Eq, Decode, RuntimeDebug, PartialEq, Clone, TypeInfo)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct AuthoritySet { + /// List of GRANDPA authorities for the current round. + pub authorities: AuthorityList, + /// Monotonic identifier of the current GRANDPA authority set. + pub set_id: SetId, +} + +impl AuthoritySet { + /// Create a new GRANDPA Authority Set. + pub fn new(authorities: AuthorityList, set_id: SetId) -> Self { + Self { authorities, set_id } + } +} + +/// Data required for initializing the GRANDPA bridge pallet. +/// +/// The bridge needs to know where to start its sync from, and this provides that initial context. +#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, Clone, TypeInfo)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct InitializationData { + /// The header from which we should start syncing. + pub header: Box, + /// The initial authorities of the pallet. + pub authority_list: AuthorityList, + /// The ID of the initial authority set. + pub set_id: SetId, + /// Pallet operating mode. + pub operating_mode: BasicOperatingMode, +} + +/// Abstract finality proof that is justifying block finality. +pub trait FinalityProof: Clone + Send + Sync + Debug { + /// Return number of header that this proof is generated for. + fn target_header_number(&self) -> Number; +} + +/// A trait that provides helper methods for querying the consensus log. +pub trait ConsensusLogReader { + /// Returns true if digest contains item that schedules authorities set change. + fn schedules_authorities_change(digest: &Digest) -> bool; +} + +/// A struct that provides helper methods for querying the GRANDPA consensus log. +pub struct GrandpaConsensusLogReader(sp_std::marker::PhantomData); + +impl GrandpaConsensusLogReader { + pub fn find_authorities_change( + digest: &Digest, + ) -> Option> { + // find the first consensus digest with the right ID which converts to + // the right kind of consensus log. + digest + .convert_first(|log| log.consensus_try_to(&GRANDPA_ENGINE_ID)) + .and_then(|log| match log { + ConsensusLog::ScheduledChange(change) => Some(change), + _ => None, + }) + } +} + +impl ConsensusLogReader for GrandpaConsensusLogReader { + fn schedules_authorities_change(digest: &Digest) -> bool { + GrandpaConsensusLogReader::::find_authorities_change(digest).is_some() + } +} + +/// A minimized version of `pallet-bridge-grandpa::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeGrandpaCall { + /// `pallet-bridge-grandpa::Call::submit_finality_proof` + #[codec(index = 0)] + submit_finality_proof { + finality_target: Box
, + justification: justification::GrandpaJustification
, + }, + /// `pallet-bridge-grandpa::Call::initialize` + #[codec(index = 1)] + initialize { init_data: InitializationData
}, +} + +/// The `BridgeGrandpaCall` used by a chain. +pub type BridgeGrandpaCallOf = BridgeGrandpaCall>; + +/// Substrate-based chain that is using direct GRANDPA finality. +/// +/// Keep in mind that parachains are relying on relay chain GRANDPA, so they should not implement +/// this trait. +pub trait ChainWithGrandpa: Chain { + /// Name of the bridge GRANDPA pallet (used in `construct_runtime` macro call) that is deployed + /// at some other chain to bridge with this `ChainWithGrandpa`. + /// + /// We assume that all chains that are bridging with this `ChainWithGrandpa` are using + /// the same name. + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str; + + /// Max number of GRANDPA authorities at the chain. + /// + /// This is a strict constant. If bridged chain will have more authorities than that, + /// the GRANDPA bridge pallet may halt. + const MAX_AUTHORITIES_COUNT: u32; + + /// Max reasonable number of headers in `votes_ancestries` vector of the GRANDPA justification. + /// + /// This isn't a strict limit. The relay may submit justifications with more headers in its + /// ancestry and the pallet will accept such justification. The limit is only used to compute + /// maximal refund amount and submitting justifications which exceed the limit, may be costly + /// to submitter. + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32; + + /// Maximal size of the chain header. The header may be the header that enacts new GRANDPA + /// authorities set (so it has large digest inside). + /// + /// This isn't a strict limit. The relay may submit larger headers and the pallet will accept + /// the call. The limit is only used to compute maximal refund amount and doing calls which + /// exceed the limit, may be costly to submitter. + const MAX_HEADER_SIZE: u32; + + /// Average size of the chain header from justification ancestry. We don't expect to see there + /// headers that change GRANDPA authorities set (GRANDPA will probably be able to finalize at + /// least one additional header per session on non test chains), so this is average size of + /// headers that aren't changing the set. + /// + /// This isn't a strict limit. The relay may submit justifications with larger headers in its + /// ancestry and the pallet will accept the call. The limit is only used to compute maximal + /// refund amount and doing calls which exceed the limit, may be costly to submitter. + const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32; +} diff --git a/primitives/header-chain/src/storage_keys.rs b/primitives/header-chain/src/storage_keys.rs new file mode 100644 index 00000000000..c4dbe53bd9a --- /dev/null +++ b/primitives/header-chain/src/storage_keys.rs @@ -0,0 +1,104 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Storage keys of bridge GRANDPA pallet. + +/// Name of the `IsHalted` storage value. +pub const PALLET_OPERATING_MODE_VALUE_NAME: &str = "PalletOperatingMode"; +/// Name of the `BestFinalized` storage value. +pub const BEST_FINALIZED_VALUE_NAME: &str = "BestFinalized"; +/// Name of the `CurrentAuthoritySet` storage value. +pub const CURRENT_AUTHORITY_SET_VALUE_NAME: &str = "CurrentAuthoritySet"; + +use sp_core::storage::StorageKey; + +/// Storage key of the `PalletOperatingMode` variable in the runtime storage. +pub fn pallet_operating_mode_key(pallet_prefix: &str) -> StorageKey { + StorageKey( + bp_runtime::storage_value_final_key( + pallet_prefix.as_bytes(), + PALLET_OPERATING_MODE_VALUE_NAME.as_bytes(), + ) + .to_vec(), + ) +} + +/// Storage key of the `CurrentAuthoritySet` variable in the runtime storage. +pub fn current_authority_set_key(pallet_prefix: &str) -> StorageKey { + StorageKey( + bp_runtime::storage_value_final_key( + pallet_prefix.as_bytes(), + CURRENT_AUTHORITY_SET_VALUE_NAME.as_bytes(), + ) + .to_vec(), + ) +} + +/// Storage key of the best finalized header number and hash value in the runtime storage. +pub fn best_finalized_key(pallet_prefix: &str) -> StorageKey { + StorageKey( + bp_runtime::storage_value_final_key( + pallet_prefix.as_bytes(), + BEST_FINALIZED_VALUE_NAME.as_bytes(), + ) + .to_vec(), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use hex_literal::hex; + + #[test] + fn pallet_operating_mode_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is breaking + // compatibility with previous pallet. + let storage_key = pallet_operating_mode_key("BridgeGrandpa").0; + assert_eq!( + storage_key, + hex!("0b06f475eddb98cf933a12262e0388de0f4cf0917788d791142ff6c1f216e7b3").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } + + #[test] + fn current_authority_set_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is breaking + // compatibility with previous pallet. + let storage_key = current_authority_set_key("BridgeGrandpa").0; + assert_eq!( + storage_key, + hex!("0b06f475eddb98cf933a12262e0388de24a7b8b5717ea33346fa595a66ccbcb0").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } + + #[test] + fn best_finalized_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is breaking + // compatibility with previous pallet. + let storage_key = best_finalized_key("BridgeGrandpa").0; + assert_eq!( + storage_key, + hex!("0b06f475eddb98cf933a12262e0388dea4ebafdd473c549fdb24c5c991c5591c").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } +} diff --git a/primitives/header-chain/tests/implementation_match.rs b/primitives/header-chain/tests/implementation_match.rs new file mode 100644 index 00000000000..c70683b173d --- /dev/null +++ b/primitives/header-chain/tests/implementation_match.rs @@ -0,0 +1,418 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Tests inside this module are made to ensure that our custom justification verification +//! implementation works similar to the [`finality_grandpa::validate_commit`] and explicitly +//! show where we behave different. +//! +//! Some of tests in this module may partially duplicate tests from `justification.rs`, +//! but their purpose is different. + +use bp_header_chain::justification::{verify_justification, Error, GrandpaJustification}; +use bp_test_utils::{ + header_id, make_justification_for_header, signed_precommit, test_header, Account, + JustificationGeneratorParams, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, TEST_GRANDPA_SET_ID, +}; +use finality_grandpa::voter_set::VoterSet; +use sp_consensus_grandpa::{AuthorityId, AuthorityWeight}; +use sp_runtime::traits::Header as HeaderT; + +type TestHeader = sp_runtime::testing::Header; +type TestHash = ::Hash; +type TestNumber = ::Number; + +/// Implementation of `finality_grandpa::Chain` that is used in tests. +struct AncestryChain(bp_header_chain::justification::AncestryChain); + +impl AncestryChain { + fn new(ancestry: &[TestHeader]) -> Self { + Self(bp_header_chain::justification::AncestryChain::new(ancestry)) + } +} + +impl finality_grandpa::Chain for AncestryChain { + fn ancestry( + &self, + base: TestHash, + block: TestHash, + ) -> Result, finality_grandpa::Error> { + let mut route = Vec::new(); + let mut current_hash = block; + loop { + if current_hash == base { + break + } + match self.0.parents.get(¤t_hash).cloned() { + Some(parent_hash) => { + current_hash = parent_hash; + route.push(current_hash); + }, + _ => return Err(finality_grandpa::Error::NotDescendent), + } + } + route.pop(); // remove the base + + Ok(route) + } +} + +/// Get a full set of accounts. +fn full_accounts_set() -> Vec<(Account, AuthorityWeight)> { + vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1), (DAVE, 1), (EVE, 1)] +} + +/// Get a full set of GRANDPA authorities. +fn full_voter_set() -> VoterSet { + VoterSet::new(full_accounts_set().iter().map(|(id, w)| (AuthorityId::from(*id), *w))).unwrap() +} + +/// Get a minimal set of accounts. +fn minimal_accounts_set() -> Vec<(Account, AuthorityWeight)> { + // there are 5 accounts in the full set => we need 2/3 + 1 accounts, which results in 4 accounts + vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1), (DAVE, 1)] +} + +/// Get a minimal subset of GRANDPA authorities that have enough cumulative vote weight to justify a +/// header finality. +pub fn minimal_voter_set() -> VoterSet { + VoterSet::new(minimal_accounts_set().iter().map(|(id, w)| (AuthorityId::from(*id), *w))) + .unwrap() +} + +/// Make a valid GRANDPA justification with sensible defaults. +pub fn make_default_justification(header: &TestHeader) -> GrandpaJustification { + make_justification_for_header(JustificationGeneratorParams { + header: header.clone(), + authorities: minimal_accounts_set(), + ..Default::default() + }) +} + +// the `finality_grandpa::validate_commit` function has two ways to report an unsuccessful +// commit validation: +// +// 1) to return `Err()` (which only may happen if `finality_grandpa::Chain` implementation +// returns an error); +// 2) to return `Ok(validation_result)` if `validation_result.is_valid()` is false. +// +// Our implementation would just return error in both cases. + +#[test] +fn same_result_when_precommit_target_has_lower_number_than_commit_target() { + let mut justification = make_default_justification(&test_header(1)); + // the number of header in precommit (0) is lower than number of header in commit (1) + justification.commit.precommits[0].precommit.target_number = 0; + + // our implementation returns an error + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Err(Error::PrecommitIsNotCommitDescendant), + ); + + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == false`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(!result.is_valid()); +} + +#[test] +fn same_result_when_precommit_target_is_not_descendant_of_commit_target() { + let not_descendant = test_header::(10); + let mut justification = make_default_justification(&test_header(1)); + // the route from header of commit (1) to header of precommit (10) is missing from + // the votes ancestries + justification.commit.precommits[0].precommit.target_number = *not_descendant.number(); + justification.commit.precommits[0].precommit.target_hash = not_descendant.hash(); + justification.votes_ancestries.push(not_descendant); + + // our implementation returns an error + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Err(Error::PrecommitIsNotCommitDescendant), + ); + + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == false`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(!result.is_valid()); +} + +#[test] +fn same_result_when_there_are_not_enough_cumulative_weight_to_finalize_commit_target() { + // just remove one authority from the minimal set and we shall not reach the threshold + let mut authorities_set = minimal_accounts_set(); + authorities_set.pop(); + let justification = make_justification_for_header(JustificationGeneratorParams { + header: test_header(1), + authorities: authorities_set, + ..Default::default() + }); + + // our implementation returns an error + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Err(Error::TooLowCumulativeWeight), + ); + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == false`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(!result.is_valid()); +} + +// tests below are our differences with the original implementation + +#[test] +fn different_result_when_justification_contains_duplicate_vote() { + let mut justification = make_justification_for_header(JustificationGeneratorParams { + header: test_header(1), + authorities: minimal_accounts_set(), + ancestors: 0, + ..Default::default() + }); + // the justification may contain exactly the same vote (i.e. same precommit and same signature) + // multiple times && it isn't treated as an error by original implementation + let last_precommit = justification.commit.precommits.pop().unwrap(); + justification.commit.precommits.push(justification.commit.precommits[0].clone()); + justification.commit.precommits.push(last_precommit); + + // our implementation fails + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Err(Error::DuplicateAuthorityVote), + ); + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == true`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(result.is_valid()); +} + +#[test] +fn different_results_when_authority_equivocates_once_in_a_round() { + let mut justification = make_justification_for_header(JustificationGeneratorParams { + header: test_header(1), + authorities: minimal_accounts_set(), + ancestors: 0, + ..Default::default() + }); + // the justification original implementation allows authority to submit two different + // votes in a single round, of which only first is 'accepted' + let last_precommit = justification.commit.precommits.pop().unwrap(); + justification.commit.precommits.push(signed_precommit::( + &ALICE, + header_id::(1), + justification.round, + TEST_GRANDPA_SET_ID, + )); + justification.commit.precommits.push(last_precommit); + + // our implementation fails + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Err(Error::DuplicateAuthorityVote), + ); + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == true`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(result.is_valid()); +} + +#[test] +fn different_results_when_authority_equivocates_twice_in_a_round() { + let mut justification = make_justification_for_header(JustificationGeneratorParams { + header: test_header(1), + authorities: minimal_accounts_set(), + ancestors: 0, + ..Default::default() + }); + // there's some code in the original implementation that should return an error when + // same authority submits more than two different votes in a single round: + // https://github.com/paritytech/finality-grandpa/blob/6aeea2d1159d0f418f0b86e70739f2130629ca09/src/lib.rs#L473 + // but there's also a code that prevents this from happening: + // https://github.com/paritytech/finality-grandpa/blob/6aeea2d1159d0f418f0b86e70739f2130629ca09/src/round.rs#L287 + // => so now we are also just ignoring all votes from the same authority, except the first one + let last_precommit = justification.commit.precommits.pop().unwrap(); + let prev_last_precommit = justification.commit.precommits.pop().unwrap(); + justification.commit.precommits.push(signed_precommit::( + &ALICE, + header_id::(1), + justification.round, + TEST_GRANDPA_SET_ID, + )); + justification.commit.precommits.push(signed_precommit::( + &ALICE, + header_id::(1), + justification.round, + TEST_GRANDPA_SET_ID, + )); + justification.commit.precommits.push(last_precommit); + justification.commit.precommits.push(prev_last_precommit); + + // our implementation fails + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Err(Error::DuplicateAuthorityVote), + ); + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == true`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(result.is_valid()); +} + +#[test] +fn different_results_when_there_are_more_than_enough_votes() { + let mut justification = make_justification_for_header(JustificationGeneratorParams { + header: test_header(1), + authorities: minimal_accounts_set(), + ancestors: 0, + ..Default::default() + }); + // the reference implementation just keep verifying signatures even if we have + // collected enough votes. We are not + justification.commit.precommits.push(signed_precommit::( + &EVE, + header_id::(1), + justification.round, + TEST_GRANDPA_SET_ID, + )); + + // our implementation fails + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Err(Error::RedundantVotesInJustification), + ); + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == true`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(result.is_valid()); +} + +#[test] +fn different_results_when_there_is_a_vote_of_unknown_authority() { + let mut justification = make_justification_for_header(JustificationGeneratorParams { + header: test_header(1), + authorities: minimal_accounts_set(), + ancestors: 0, + ..Default::default() + }); + // the reference implementation just keep verifying signatures even if we have + // collected enough votes. We are not + let last_precommit = justification.commit.precommits.pop().unwrap(); + justification.commit.precommits.push(signed_precommit::( + &FERDIE, + header_id::(1), + justification.round, + TEST_GRANDPA_SET_ID, + )); + justification.commit.precommits.push(last_precommit); + + // our implementation fails + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &full_voter_set(), + &justification, + ), + Err(Error::UnknownAuthorityVote), + ); + // original implementation returns `Ok(validation_result)` + // with `validation_result.is_valid() == true`. + let result = finality_grandpa::validate_commit( + &justification.commit, + &full_voter_set(), + &AncestryChain::new(&justification.votes_ancestries), + ) + .unwrap(); + + assert!(result.is_valid()); +} diff --git a/primitives/header-chain/tests/justification.rs b/primitives/header-chain/tests/justification.rs new file mode 100644 index 00000000000..3cd63b935d0 --- /dev/null +++ b/primitives/header-chain/tests/justification.rs @@ -0,0 +1,280 @@ +// Copyright 2020-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Tests for Grandpa Justification code. + +use bp_header_chain::justification::{ + required_justification_precommits, verify_and_optimize_justification, verify_justification, + Error, +}; +use bp_test_utils::*; + +type TestHeader = sp_runtime::testing::Header; + +#[test] +fn valid_justification_accepted() { + let authorities = vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1)]; + let params = JustificationGeneratorParams { + header: test_header(1), + round: TEST_GRANDPA_ROUND, + set_id: TEST_GRANDPA_SET_ID, + authorities: authorities.clone(), + ancestors: 7, + forks: 3, + }; + + let justification = make_justification_for_header::(params.clone()); + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + &justification, + ), + Ok(()), + ); + + assert_eq!(justification.commit.precommits.len(), authorities.len()); + assert_eq!(justification.votes_ancestries.len(), params.ancestors as usize); +} + +#[test] +fn valid_justification_accepted_with_single_fork() { + let params = JustificationGeneratorParams { + header: test_header(1), + round: TEST_GRANDPA_ROUND, + set_id: TEST_GRANDPA_SET_ID, + authorities: vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1)], + ancestors: 5, + forks: 1, + }; + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + &make_justification_for_header::(params) + ), + Ok(()), + ); +} + +#[test] +fn valid_justification_accepted_with_arbitrary_number_of_authorities() { + use finality_grandpa::voter_set::VoterSet; + use sp_consensus_grandpa::AuthorityId; + + let n = 15; + let required_signatures = required_justification_precommits(n as _); + let authorities = accounts(n).iter().map(|k| (*k, 1)).collect::>(); + + let params = JustificationGeneratorParams { + header: test_header(1), + round: TEST_GRANDPA_ROUND, + set_id: TEST_GRANDPA_SET_ID, + authorities: authorities.clone().into_iter().take(required_signatures as _).collect(), + ancestors: n.into(), + forks: required_signatures, + }; + + let authorities = authorities + .iter() + .map(|(id, w)| (AuthorityId::from(*id), *w)) + .collect::>(); + let voter_set = VoterSet::new(authorities).unwrap(); + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set, + &make_justification_for_header::(params) + ), + Ok(()), + ); +} + +#[test] +fn justification_with_invalid_target_rejected() { + assert_eq!( + verify_justification::( + header_id::(2), + TEST_GRANDPA_SET_ID, + &voter_set(), + &make_default_justification::(&test_header(1)), + ), + Err(Error::InvalidJustificationTarget), + ); +} + +#[test] +fn justification_with_invalid_commit_rejected() { + let mut justification = make_default_justification::(&test_header(1)); + justification.commit.precommits.clear(); + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + &justification, + ), + Err(Error::ExtraHeadersInVotesAncestries), + ); +} + +#[test] +fn justification_with_invalid_authority_signature_rejected() { + let mut justification = make_default_justification::(&test_header(1)); + justification.commit.precommits[0].signature = + sp_core::crypto::UncheckedFrom::unchecked_from([1u8; 64]); + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + &justification, + ), + Err(Error::InvalidAuthoritySignature), + ); +} + +#[test] +fn justification_with_invalid_precommit_ancestry() { + let mut justification = make_default_justification::(&test_header(1)); + justification.votes_ancestries.push(test_header(10)); + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + &justification, + ), + Err(Error::ExtraHeadersInVotesAncestries), + ); +} + +#[test] +fn justification_is_invalid_if_we_dont_meet_threshold() { + // Need at least three authorities to sign off or else the voter set threshold can't be reached + let authorities = vec![(ALICE, 1), (BOB, 1)]; + + let params = JustificationGeneratorParams { + header: test_header(1), + round: TEST_GRANDPA_ROUND, + set_id: TEST_GRANDPA_SET_ID, + authorities: authorities.clone(), + ancestors: 2 * authorities.len() as u32, + forks: 2, + }; + + assert_eq!( + verify_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + &make_justification_for_header::(params) + ), + Err(Error::TooLowCumulativeWeight), + ); +} + +#[test] +fn optimizer_does_noting_with_minimal_justification() { + let justification = make_default_justification::(&test_header(1)); + + let num_precommits_before = justification.commit.precommits.len(); + let justification = verify_and_optimize_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + justification, + ) + .unwrap(); + let num_precommits_after = justification.commit.precommits.len(); + + assert_eq!(num_precommits_before, num_precommits_after); +} + +#[test] +fn unknown_authority_votes_are_removed_by_optimizer() { + let mut justification = make_default_justification::(&test_header(1)); + justification.commit.precommits.push(signed_precommit::( + &bp_test_utils::Account(42), + header_id::(1), + justification.round, + TEST_GRANDPA_SET_ID, + )); + + let num_precommits_before = justification.commit.precommits.len(); + let justification = verify_and_optimize_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + justification, + ) + .unwrap(); + let num_precommits_after = justification.commit.precommits.len(); + + assert_eq!(num_precommits_before - 1, num_precommits_after); +} + +#[test] +fn duplicate_authority_votes_are_removed_by_optimizer() { + let mut justification = make_default_justification::(&test_header(1)); + justification + .commit + .precommits + .push(justification.commit.precommits.first().cloned().unwrap()); + + let num_precommits_before = justification.commit.precommits.len(); + let justification = verify_and_optimize_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + justification, + ) + .unwrap(); + let num_precommits_after = justification.commit.precommits.len(); + + assert_eq!(num_precommits_before - 1, num_precommits_after); +} + +#[test] +fn redundant_authority_votes_are_removed_by_optimizer() { + let mut justification = make_default_justification::(&test_header(1)); + justification.commit.precommits.push(signed_precommit::( + &EVE, + header_id::(1), + justification.round, + TEST_GRANDPA_SET_ID, + )); + + let num_precommits_before = justification.commit.precommits.len(); + let justification = verify_and_optimize_justification::( + header_id::(1), + TEST_GRANDPA_SET_ID, + &voter_set(), + justification, + ) + .unwrap(); + let num_precommits_after = justification.commit.precommits.len(); + + assert_eq!(num_precommits_before - 1, num_precommits_after); +} diff --git a/primitives/messages/Cargo.toml b/primitives/messages/Cargo.toml new file mode 100644 index 00000000000..32d7c65ebcb --- /dev/null +++ b/primitives/messages/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "bp-messages" +description = "Primitives of messages module." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive", "bit-vec"] } +scale-info = { version = "2.5.0", default-features = false, features = ["bit-vec", "derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } + +# Bridge dependencies + +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +hex = "0.4" +hex-literal = "0.4" + +[features] +default = ["std"] +std = [ + "bp-runtime/std", + "codec/std", + "frame-support/std", + "scale-info/std", + "serde", + "sp-core/std", + "sp-std/std" +] diff --git a/primitives/messages/src/lib.rs b/primitives/messages/src/lib.rs new file mode 100644 index 00000000000..3910837a442 --- /dev/null +++ b/primitives/messages/src/lib.rs @@ -0,0 +1,471 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives of messages module. + +#![cfg_attr(not(feature = "std"), no_std)] +// RuntimeApi generated functions +#![allow(clippy::too_many_arguments)] + +use bp_runtime::{BasicOperatingMode, OperatingMode, RangeInclusiveExt}; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::RuntimeDebug; +use scale_info::TypeInfo; +use source_chain::RelayersRewards; +use sp_core::TypeId; +use sp_std::{collections::vec_deque::VecDeque, ops::RangeInclusive, prelude::*}; + +pub mod source_chain; +pub mod storage_keys; +pub mod target_chain; + +use bp_runtime::messages::MessageDispatchResult; +// Weight is reexported to avoid additional frame-support dependencies in related crates. +pub use frame_support::weights::Weight; + +/// Messages pallet operating mode. +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub enum MessagesOperatingMode { + /// Basic operating mode (Normal/Halted) + Basic(BasicOperatingMode), + /// The pallet is not accepting outbound messages. Inbound messages and receiving proofs + /// are still accepted. + /// + /// This mode may be used e.g. when bridged chain expects upgrade. Then to avoid dispatch + /// failures, the pallet owner may stop accepting new messages, while continuing to deliver + /// queued messages to the bridged chain. Once upgrade is completed, the mode may be switched + /// back to `Normal`. + RejectingOutboundMessages, +} + +impl Default for MessagesOperatingMode { + fn default() -> Self { + MessagesOperatingMode::Basic(BasicOperatingMode::Normal) + } +} + +impl OperatingMode for MessagesOperatingMode { + fn is_halted(&self) -> bool { + match self { + Self::Basic(operating_mode) => operating_mode.is_halted(), + _ => false, + } + } +} + +/// Lane id which implements `TypeId`. +#[derive( + Clone, Copy, Decode, Default, Encode, Eq, Ord, PartialOrd, PartialEq, TypeInfo, MaxEncodedLen, +)] +pub struct LaneId(pub [u8; 4]); + +impl core::fmt::Debug for LaneId { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(fmt) + } +} + +impl AsRef<[u8]> for LaneId { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl TypeId for LaneId { + const TYPE_ID: [u8; 4] = *b"blan"; +} + +/// Message nonce. Valid messages will never have 0 nonce. +pub type MessageNonce = u64; + +/// Message id as a tuple. +pub type BridgeMessageId = (LaneId, MessageNonce); + +/// Opaque message payload. We only decode this payload when it is dispatched. +pub type MessagePayload = Vec; + +/// Message key (unique message identifier) as it is stored in the storage. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub struct MessageKey { + /// ID of the message lane. + pub lane_id: LaneId, + /// Message nonce. + pub nonce: MessageNonce, +} + +/// Message as it is stored in the storage. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct Message { + /// Message key. + pub key: MessageKey, + /// Message payload. + pub payload: MessagePayload, +} + +/// Inbound lane data. +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct InboundLaneData { + /// Identifiers of relayers and messages that they have delivered to this lane (ordered by + /// message nonce). + /// + /// This serves as a helper storage item, to allow the source chain to easily pay rewards + /// to the relayers who successfully delivered messages to the target chain (inbound lane). + /// + /// It is guaranteed to have at most N entries, where N is configured at the module level. + /// If there are N entries in this vec, then: + /// 1) all incoming messages are rejected if they're missing corresponding + /// `proof-of(outbound-lane.state)`; 2) all incoming messages are rejected if + /// `proof-of(outbound-lane.state).last_delivered_nonce` is equal to + /// `self.last_confirmed_nonce`. Given what is said above, all nonces in this queue are in + /// range: `(self.last_confirmed_nonce; self.last_delivered_nonce()]`. + /// + /// When a relayer sends a single message, both of MessageNonces are the same. + /// When relayer sends messages in a batch, the first arg is the lowest nonce, second arg the + /// highest nonce. Multiple dispatches from the same relayer are allowed. + pub relayers: VecDeque>, + + /// Nonce of the last message that + /// a) has been delivered to the target (this) chain and + /// b) the delivery has been confirmed on the source chain + /// + /// that the target chain knows of. + /// + /// This value is updated indirectly when an `OutboundLane` state of the source + /// chain is received alongside with new messages delivery. + pub last_confirmed_nonce: MessageNonce, +} + +impl Default for InboundLaneData { + fn default() -> Self { + InboundLaneData { relayers: VecDeque::new(), last_confirmed_nonce: 0 } + } +} + +impl InboundLaneData { + /// Returns approximate size of the struct, given a number of entries in the `relayers` set and + /// size of each entry. + /// + /// Returns `None` if size overflows `usize` limits. + pub fn encoded_size_hint(relayers_entries: usize) -> Option + where + RelayerId: MaxEncodedLen, + { + let message_nonce_size = MessageNonce::max_encoded_len(); + let relayer_id_encoded_size = RelayerId::max_encoded_len(); + let relayers_entry_size = relayer_id_encoded_size.checked_add(2 * message_nonce_size)?; + let relayers_size = relayers_entries.checked_mul(relayers_entry_size)?; + relayers_size.checked_add(message_nonce_size) + } + + /// Returns the approximate size of the struct as u32, given a number of entries in the + /// `relayers` set and the size of each entry. + /// + /// Returns `u32::MAX` if size overflows `u32` limits. + pub fn encoded_size_hint_u32(relayers_entries: usize) -> u32 + where + RelayerId: MaxEncodedLen, + { + Self::encoded_size_hint(relayers_entries) + .and_then(|x| u32::try_from(x).ok()) + .unwrap_or(u32::MAX) + } + + /// Nonce of the last message that has been delivered to this (target) chain. + pub fn last_delivered_nonce(&self) -> MessageNonce { + self.relayers + .back() + .map(|entry| entry.messages.end) + .unwrap_or(self.last_confirmed_nonce) + } +} + +/// Outbound message details, returned by runtime APIs. +#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct OutboundMessageDetails { + /// Nonce assigned to the message. + pub nonce: MessageNonce, + /// Message dispatch weight. + /// + /// Depending on messages pallet configuration, it may be declared by the message submitter, + /// computed automatically or just be zero if dispatch fee is paid at the target chain. + pub dispatch_weight: Weight, + /// Size of the encoded message. + pub size: u32, +} + +/// Inbound message details, returned by runtime APIs. +#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct InboundMessageDetails { + /// Computed message dispatch weight. + /// + /// Runtime API guarantees that it will match the value, returned by + /// `target_chain::MessageDispatch::dispatch_weight`. This means that if the runtime + /// has failed to decode the message, it will be zero - that's because `undecodable` + /// message cannot be dispatched. + pub dispatch_weight: Weight, +} + +/// Unrewarded relayer entry stored in the inbound lane data. +/// +/// This struct represents a continuous range of messages that have been delivered by the same +/// relayer and whose confirmations are still pending. +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct UnrewardedRelayer { + /// Identifier of the relayer. + pub relayer: RelayerId, + /// Messages range, delivered by this relayer. + pub messages: DeliveredMessages, +} + +/// Received messages with their dispatch result. +#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct ReceivedMessages { + /// Id of the lane which is receiving messages. + pub lane: LaneId, + /// Result of messages which we tried to dispatch + pub receive_results: Vec<(MessageNonce, ReceivalResult)>, +} + +impl ReceivedMessages { + pub fn new( + lane: LaneId, + receive_results: Vec<(MessageNonce, ReceivalResult)>, + ) -> Self { + ReceivedMessages { lane, receive_results } + } + + pub fn push(&mut self, message: MessageNonce, result: ReceivalResult) { + self.receive_results.push((message, result)); + } +} + +/// Result of single message receival. +#[derive(RuntimeDebug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)] +pub enum ReceivalResult { + /// Message has been received and dispatched. Note that we don't care whether dispatch has + /// been successful or not - in both case message falls into this category. + /// + /// The message dispatch result is also returned. + Dispatched(MessageDispatchResult), + /// Message has invalid nonce and lane has rejected to accept this message. + InvalidNonce, + /// There are too many unrewarded relayer entries at the lane. + TooManyUnrewardedRelayers, + /// There are too many unconfirmed messages at the lane. + TooManyUnconfirmedMessages, +} + +/// Delivered messages with their dispatch result. +#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct DeliveredMessages { + /// Nonce of the first message that has been delivered (inclusive). + pub begin: MessageNonce, + /// Nonce of the last message that has been delivered (inclusive). + pub end: MessageNonce, +} + +impl DeliveredMessages { + /// Create new `DeliveredMessages` struct that confirms delivery of single nonce with given + /// dispatch result. + pub fn new(nonce: MessageNonce) -> Self { + DeliveredMessages { begin: nonce, end: nonce } + } + + /// Return total count of delivered messages. + pub fn total_messages(&self) -> MessageNonce { + (self.begin..=self.end).checked_len().unwrap_or(0) + } + + /// Note new dispatched message. + pub fn note_dispatched_message(&mut self) { + self.end += 1; + } + + /// Returns true if delivered messages contain message with given nonce. + pub fn contains_message(&self, nonce: MessageNonce) -> bool { + (self.begin..=self.end).contains(&nonce) + } +} + +/// Gist of `InboundLaneData::relayers` field used by runtime APIs. +#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct UnrewardedRelayersState { + /// Number of entries in the `InboundLaneData::relayers` set. + pub unrewarded_relayer_entries: MessageNonce, + /// Number of messages in the oldest entry of `InboundLaneData::relayers`. This is the + /// minimal number of reward proofs required to push out this entry from the set. + pub messages_in_oldest_entry: MessageNonce, + /// Total number of messages in the relayers vector. + pub total_messages: MessageNonce, + /// Nonce of the latest message that has been delivered to the target chain. + /// + /// This corresponds to the result of the `InboundLaneData::last_delivered_nonce` call + /// at the bridged chain. + pub last_delivered_nonce: MessageNonce, +} + +/// Outbound lane data. +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub struct OutboundLaneData { + /// Nonce of the oldest message that we haven't yet pruned. May point to not-yet-generated + /// message if all sent messages are already pruned. + pub oldest_unpruned_nonce: MessageNonce, + /// Nonce of the latest message, received by bridged chain. + pub latest_received_nonce: MessageNonce, + /// Nonce of the latest message, generated by us. + pub latest_generated_nonce: MessageNonce, +} + +impl Default for OutboundLaneData { + fn default() -> Self { + OutboundLaneData { + // it is 1 because we're pruning everything in [oldest_unpruned_nonce; + // latest_received_nonce] + oldest_unpruned_nonce: 1, + latest_received_nonce: 0, + latest_generated_nonce: 0, + } + } +} + +/// Returns total number of messages in the `InboundLaneData::relayers` vector. +/// +/// Returns `None` if there are more messages that `MessageNonce` may fit (i.e. `MessageNonce + 1`). +pub fn total_unrewarded_messages( + relayers: &VecDeque>, +) -> Option { + match (relayers.front(), relayers.back()) { + (Some(front), Some(back)) => { + if let Some(difference) = back.messages.end.checked_sub(front.messages.begin) { + difference.checked_add(1) + } else { + Some(0) + } + }, + _ => Some(0), + } +} + +/// Calculate the number of messages that the relayers have delivered. +pub fn calc_relayers_rewards( + messages_relayers: VecDeque>, + received_range: &RangeInclusive, +) -> RelayersRewards +where + AccountId: sp_std::cmp::Ord, +{ + // remember to reward relayers that have delivered messages + // this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain + let mut relayers_rewards = RelayersRewards::new(); + for entry in messages_relayers { + let nonce_begin = sp_std::cmp::max(entry.messages.begin, *received_range.start()); + let nonce_end = sp_std::cmp::min(entry.messages.end, *received_range.end()); + if nonce_end >= nonce_begin { + *relayers_rewards.entry(entry.relayer).or_default() += nonce_end - nonce_begin + 1; + } + } + relayers_rewards +} + +/// A minimized version of `pallet-bridge-messages::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeMessagesCall { + /// `pallet-bridge-messages::Call::receive_messages_proof` + #[codec(index = 2)] + receive_messages_proof { + relayer_id_at_bridged_chain: AccountId, + proof: MessagesProof, + messages_count: u32, + dispatch_weight: Weight, + }, + /// `pallet-bridge-messages::Call::receive_messages_delivery_proof` + #[codec(index = 3)] + receive_messages_delivery_proof { + proof: MessagesDeliveryProof, + relayers_state: UnrewardedRelayersState, + }, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn total_unrewarded_messages_does_not_overflow() { + assert_eq!( + total_unrewarded_messages( + &vec![ + UnrewardedRelayer { relayer: 1, messages: DeliveredMessages::new(0) }, + UnrewardedRelayer { + relayer: 2, + messages: DeliveredMessages::new(MessageNonce::MAX) + }, + ] + .into_iter() + .collect() + ), + None, + ); + } + + #[test] + fn inbound_lane_data_returns_correct_hint() { + let test_cases = vec![ + // single relayer, multiple messages + (1, 128u8), + // multiple relayers, single message per relayer + (128u8, 128u8), + // several messages per relayer + (13u8, 128u8), + ]; + for (relayer_entries, messages_count) in test_cases { + let expected_size = InboundLaneData::::encoded_size_hint(relayer_entries as _); + let actual_size = InboundLaneData { + relayers: (1u8..=relayer_entries) + .map(|i| UnrewardedRelayer { + relayer: i, + messages: DeliveredMessages::new(i as _), + }) + .collect(), + last_confirmed_nonce: messages_count as _, + } + .encode() + .len(); + let difference = (expected_size.unwrap() as f64 - actual_size as f64).abs(); + assert!( + difference / (std::cmp::min(actual_size, expected_size.unwrap()) as f64) < 0.1, + "Too large difference between actual ({actual_size}) and expected ({expected_size:?}) inbound lane data size. Test case: {relayer_entries}+{messages_count}", + ); + } + } + + #[test] + fn contains_result_works() { + let delivered_messages = DeliveredMessages { begin: 100, end: 150 }; + + assert!(!delivered_messages.contains_message(99)); + assert!(delivered_messages.contains_message(100)); + assert!(delivered_messages.contains_message(150)); + assert!(!delivered_messages.contains_message(151)); + } + + #[test] + fn lane_id_debug_format_matches_inner_array_format() { + assert_eq!(format!("{:?}", LaneId([0, 0, 0, 0])), format!("{:?}", [0, 0, 0, 0]),); + } +} diff --git a/primitives/messages/src/source_chain.rs b/primitives/messages/src/source_chain.rs new file mode 100644 index 00000000000..394a934171f --- /dev/null +++ b/primitives/messages/src/source_chain.rs @@ -0,0 +1,211 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives of messages module, that are used on the source chain. + +use crate::{InboundLaneData, LaneId, MessageNonce, OutboundLaneData}; + +use crate::UnrewardedRelayer; +use bp_runtime::Size; +use frame_support::{Parameter, RuntimeDebug}; +use sp_std::{ + collections::{btree_map::BTreeMap, vec_deque::VecDeque}, + fmt::Debug, + ops::RangeInclusive, +}; + +/// Number of messages, delivered by relayers. +pub type RelayersRewards = BTreeMap; + +/// Target chain API. Used by source chain to verify target chain proofs. +/// +/// All implementations of this trait should only work with finalized data that +/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane +/// that's stuck) and/or processing messages without paying fees. +/// +/// The `Payload` type here means the payload of the message that is sent from the +/// source chain to the target chain. The `AccountId` type here means the account +/// type used by the source chain. +pub trait TargetHeaderChain { + /// Error type. + type Error: Debug; + + /// Proof that messages have been received by target chain. + type MessagesDeliveryProof: Parameter + Size; + + /// Verify message payload before we accept it. + /// + /// **CAUTION**: this is very important function. Incorrect implementation may lead + /// to stuck lanes and/or relayers loses. + /// + /// The proper implementation must ensure that the delivery-transaction with this + /// payload would (at least) be accepted into target chain transaction pool AND + /// eventually will be successfully mined. The most obvious incorrect implementation + /// example would be implementation for BTC chain that accepts payloads larger than + /// 1MB. BTC nodes aren't accepting transactions that are larger than 1MB, so relayer + /// will be unable to craft valid transaction => this (and all subsequent) messages will + /// never be delivered. + fn verify_message(payload: &Payload) -> Result<(), Self::Error>; + + /// Verify messages delivery proof and return lane && nonce of the latest received message. + fn verify_messages_delivery_proof( + proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData), Self::Error>; +} + +/// Lane message verifier. +/// +/// Runtime developer may implement any additional validation logic over message-lane mechanism. +/// E.g. if lanes should have some security (e.g. you can only accept Lane1 messages from +/// Submitter1, Lane2 messages for those who has submitted first message to this lane, disable +/// Lane3 until some block, ...), then it may be built using this verifier. +/// +/// Any fee requirements should also be enforced here. +pub trait LaneMessageVerifier { + /// Error type. + type Error: Debug + Into<&'static str>; + + /// Verify message payload and return Ok(()) if message is valid and allowed to be sent over the + /// lane. + fn verify_message( + submitter: &SenderOrigin, + lane: &LaneId, + outbound_data: &OutboundLaneData, + payload: &Payload, + ) -> Result<(), Self::Error>; +} + +/// Manages payments that are happening at the source chain during delivery confirmation +/// transaction. +pub trait DeliveryConfirmationPayments { + /// Error type. + type Error: Debug + Into<&'static str>; + + /// Pay rewards for delivering messages to the given relayers. + /// + /// The implementation may also choose to pay reward to the `confirmation_relayer`, which is + /// a relayer that has submitted delivery confirmation transaction. + /// + /// Returns number of actually rewarded relayers. + fn pay_reward( + lane_id: LaneId, + messages_relayers: VecDeque>, + confirmation_relayer: &AccountId, + received_range: &RangeInclusive, + ) -> MessageNonce; +} + +impl DeliveryConfirmationPayments for () { + type Error = &'static str; + + fn pay_reward( + _lane_id: LaneId, + _messages_relayers: VecDeque>, + _confirmation_relayer: &AccountId, + _received_range: &RangeInclusive, + ) -> MessageNonce { + // this implementation is not rewarding relayers at all + 0 + } +} + +/// Send message artifacts. +#[derive(Eq, RuntimeDebug, PartialEq)] +pub struct SendMessageArtifacts { + /// Nonce of the message. + pub nonce: MessageNonce, +} + +/// Messages bridge API to be used from other pallets. +pub trait MessagesBridge { + /// Error type. + type Error: Debug; + + /// Send message over the bridge. + /// + /// Returns unique message nonce or error if send has failed. + fn send_message( + sender: SenderOrigin, + lane: LaneId, + message: Payload, + ) -> Result; +} + +/// Bridge that does nothing when message is being sent. +#[derive(Eq, RuntimeDebug, PartialEq)] +pub struct NoopMessagesBridge; + +impl MessagesBridge for NoopMessagesBridge { + type Error = &'static str; + + fn send_message( + _sender: SenderOrigin, + _lane: LaneId, + _message: Payload, + ) -> Result { + Ok(SendMessageArtifacts { nonce: 0 }) + } +} + +/// Structure that may be used in place of `TargetHeaderChain`, `LaneMessageVerifier` and +/// `MessageDeliveryAndDispatchPayment` on chains, where outbound messages are forbidden. +pub struct ForbidOutboundMessages; + +/// Error message that is used in `ForbidOutboundMessages` implementation. +const ALL_OUTBOUND_MESSAGES_REJECTED: &str = + "This chain is configured to reject all outbound messages"; + +impl TargetHeaderChain for ForbidOutboundMessages { + type Error = &'static str; + + type MessagesDeliveryProof = (); + + fn verify_message(_payload: &Payload) -> Result<(), Self::Error> { + Err(ALL_OUTBOUND_MESSAGES_REJECTED) + } + + fn verify_messages_delivery_proof( + _proof: Self::MessagesDeliveryProof, + ) -> Result<(LaneId, InboundLaneData), Self::Error> { + Err(ALL_OUTBOUND_MESSAGES_REJECTED) + } +} + +impl LaneMessageVerifier for ForbidOutboundMessages { + type Error = &'static str; + + fn verify_message( + _submitter: &SenderOrigin, + _lane: &LaneId, + _outbound_data: &OutboundLaneData, + _payload: &Payload, + ) -> Result<(), Self::Error> { + Err(ALL_OUTBOUND_MESSAGES_REJECTED) + } +} + +impl DeliveryConfirmationPayments for ForbidOutboundMessages { + type Error = &'static str; + + fn pay_reward( + _lane_id: LaneId, + _messages_relayers: VecDeque>, + _confirmation_relayer: &AccountId, + _received_range: &RangeInclusive, + ) -> MessageNonce { + 0 + } +} diff --git a/primitives/messages/src/storage_keys.rs b/primitives/messages/src/storage_keys.rs new file mode 100644 index 00000000000..4edf9828cfd --- /dev/null +++ b/primitives/messages/src/storage_keys.rs @@ -0,0 +1,128 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Storage keys of bridge messages pallet. + +/// Name of the `OPERATING_MODE_VALUE_NAME` storage value. +pub const OPERATING_MODE_VALUE_NAME: &str = "PalletOperatingMode"; +/// Name of the `OutboundMessages` storage map. +pub const OUTBOUND_MESSAGES_MAP_NAME: &str = "OutboundMessages"; +/// Name of the `OutboundLanes` storage map. +pub const OUTBOUND_LANES_MAP_NAME: &str = "OutboundLanes"; +/// Name of the `InboundLanes` storage map. +pub const INBOUND_LANES_MAP_NAME: &str = "InboundLanes"; + +use crate::{LaneId, MessageKey, MessageNonce}; + +use codec::Encode; +use frame_support::Blake2_128Concat; +use sp_core::storage::StorageKey; + +/// Storage key of the `PalletOperatingMode` value in the runtime storage. +pub fn operating_mode_key(pallet_prefix: &str) -> StorageKey { + StorageKey( + bp_runtime::storage_value_final_key( + pallet_prefix.as_bytes(), + OPERATING_MODE_VALUE_NAME.as_bytes(), + ) + .to_vec(), + ) +} + +/// Storage key of the outbound message in the runtime storage. +pub fn message_key(pallet_prefix: &str, lane: &LaneId, nonce: MessageNonce) -> StorageKey { + bp_runtime::storage_map_final_key::( + pallet_prefix, + OUTBOUND_MESSAGES_MAP_NAME, + &MessageKey { lane_id: *lane, nonce }.encode(), + ) +} + +/// Storage key of the outbound message lane state in the runtime storage. +pub fn outbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { + bp_runtime::storage_map_final_key::( + pallet_prefix, + OUTBOUND_LANES_MAP_NAME, + &lane.encode(), + ) +} + +/// Storage key of the inbound message lane state in the runtime storage. +pub fn inbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { + bp_runtime::storage_map_final_key::( + pallet_prefix, + INBOUND_LANES_MAP_NAME, + &lane.encode(), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use hex_literal::hex; + + #[test] + fn operating_mode_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is possibly + // breaking all existing message relays. + let storage_key = operating_mode_key("BridgeMessages").0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed010f4cf0917788d791142ff6c1f216e7b3").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } + + #[test] + fn storage_message_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is breaking + // all previously crafted messages proofs. + let storage_key = message_key("BridgeMessages", &LaneId(*b"test"), 42).0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } + + #[test] + fn outbound_lane_data_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is breaking + // all previously crafted outbound lane state proofs. + let storage_key = outbound_lane_data_key("BridgeMessages", &LaneId(*b"test")).0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1f44a8995dd50b6657a037a7839304535b74657374").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } + + #[test] + fn inbound_lane_data_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is breaking + // all previously crafted inbound lane state proofs. + let storage_key = inbound_lane_data_key("BridgeMessages", &LaneId(*b"test")).0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } +} diff --git a/primitives/messages/src/target_chain.rs b/primitives/messages/src/target_chain.rs new file mode 100644 index 00000000000..8496b90214c --- /dev/null +++ b/primitives/messages/src/target_chain.rs @@ -0,0 +1,205 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives of messages module, that are used on the target chain. + +use crate::{LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData}; + +use bp_runtime::{messages::MessageDispatchResult, Size}; +use codec::{Decode, Encode, Error as CodecError}; +use frame_support::{weights::Weight, Parameter, RuntimeDebug}; +use scale_info::TypeInfo; +use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData, prelude::*}; + +/// Proved messages from the source chain. +pub type ProvedMessages = BTreeMap>; + +/// Proved messages from single lane of the source chain. +#[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] +pub struct ProvedLaneMessages { + /// Optional outbound lane state. + pub lane_state: Option, + /// Messages sent through this lane. + pub messages: Vec, +} + +/// Message data with decoded dispatch payload. +#[derive(RuntimeDebug)] +pub struct DispatchMessageData { + /// Result of dispatch payload decoding. + pub payload: Result, +} + +/// Message with decoded dispatch payload. +#[derive(RuntimeDebug)] +pub struct DispatchMessage { + /// Message key. + pub key: MessageKey, + /// Message data with decoded dispatch payload. + pub data: DispatchMessageData, +} + +/// Source chain API. Used by target chain, to verify source chain proofs. +/// +/// All implementations of this trait should only work with finalized data that +/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane +/// that's stuck) and/or processing messages without paying fees. +pub trait SourceHeaderChain { + /// Error type. + type Error: Debug; + + /// Proof that messages are sent from source chain. This may also include proof + /// of corresponding outbound lane states. + type MessagesProof: Parameter + Size; + + /// Verify messages proof and return proved messages. + /// + /// Returns error if either proof is incorrect, or the number of messages in the proof + /// is not matching the `messages_count`. + /// + /// Messages vector is required to be sorted by nonce within each lane. Out-of-order + /// messages will be rejected. + /// + /// The `messages_count` argument verification (sane limits) is supposed to be made + /// outside this function. This function only verifies that the proof declares exactly + /// `messages_count` messages. + fn verify_messages_proof( + proof: Self::MessagesProof, + messages_count: u32, + ) -> Result, Self::Error>; +} + +/// Called when inbound message is received. +pub trait MessageDispatch { + /// Decoded message payload type. Valid message may contain invalid payload. In this case + /// message is delivered, but dispatch fails. Therefore, two separate types of payload + /// (opaque `MessagePayload` used in delivery and this `DispatchPayload` used in dispatch). + type DispatchPayload: Decode; + + /// Fine-grained result of single message dispatch (for better diagnostic purposes) + type DispatchLevelResult: Clone + sp_std::fmt::Debug + Eq; + + /// Estimate dispatch weight. + /// + /// This function must return correct upper bound of dispatch weight. The return value + /// of this function is expected to match return value of the corresponding + /// `FromInboundLaneApi::message_details().dispatch_weight` call. + fn dispatch_weight(message: &mut DispatchMessage) -> Weight; + + /// Called when inbound message is received. + /// + /// It is up to the implementers of this trait to determine whether the message + /// is invalid (i.e. improperly encoded, has too large weight, ...) or not. + /// + /// If your configuration allows paying dispatch fee at the target chain, then + /// it must be paid inside this method to the `relayer_account`. + fn dispatch( + relayer_account: &AccountId, + message: DispatchMessage, + ) -> MessageDispatchResult; +} + +/// Manages payments that are happening at the target chain during message delivery transaction. +pub trait DeliveryPayments { + /// Error type. + type Error: Debug + Into<&'static str>; + + /// Pay rewards for delivering messages to the given relayer. + /// + /// This method is called during message delivery transaction which has been submitted + /// by the `relayer`. The transaction brings `total_messages` messages but only + /// `valid_messages` have been accepted. The post-dispatch transaction weight is the + /// `actual_weight`. + fn pay_reward( + relayer: AccountId, + total_messages: MessageNonce, + valid_messages: MessageNonce, + actual_weight: Weight, + ); +} + +impl Default for ProvedLaneMessages { + fn default() -> Self { + ProvedLaneMessages { lane_state: None, messages: Vec::new() } + } +} + +impl From for DispatchMessage { + fn from(message: Message) -> Self { + DispatchMessage { key: message.key, data: message.payload.into() } + } +} + +impl From for DispatchMessageData { + fn from(payload: MessagePayload) -> Self { + DispatchMessageData { payload: DispatchPayload::decode(&mut &payload[..]) } + } +} + +impl DeliveryPayments for () { + type Error = &'static str; + + fn pay_reward( + _relayer: AccountId, + _total_messages: MessageNonce, + _valid_messages: MessageNonce, + _actual_weight: Weight, + ) { + // this implementation is not rewarding relayer at all + } +} + +/// Structure that may be used in place of `SourceHeaderChain` and `MessageDispatch` on chains, +/// where inbound messages are forbidden. +pub struct ForbidInboundMessages( + PhantomData<(MessagesProof, DispatchPayload)>, +); + +/// Error message that is used in `ForbidOutboundMessages` implementation. +const ALL_INBOUND_MESSAGES_REJECTED: &str = + "This chain is configured to reject all inbound messages"; + +impl SourceHeaderChain + for ForbidInboundMessages +{ + type Error = &'static str; + type MessagesProof = MessagesProof; + + fn verify_messages_proof( + _proof: Self::MessagesProof, + _messages_count: u32, + ) -> Result, Self::Error> { + Err(ALL_INBOUND_MESSAGES_REJECTED) + } +} + +impl MessageDispatch + for ForbidInboundMessages +{ + type DispatchPayload = DispatchPayload; + type DispatchLevelResult = (); + + fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { + Weight::MAX + } + + fn dispatch( + _: &AccountId, + _: DispatchMessage, + ) -> MessageDispatchResult { + MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } + } +} diff --git a/primitives/parachains/Cargo.toml b/primitives/parachains/Cargo.toml new file mode 100644 index 00000000000..e47b8c5e68c --- /dev/null +++ b/primitives/parachains/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "bp-parachains" +description = "Primitives of parachains module." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } +impl-trait-for-tuples = "0.2" +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } + +# Bridge dependencies + +bp-header-chain = { path = "../header-chain", default-features = false } +bp-polkadot-core = { path = "../polkadot-core", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "bp-polkadot-core/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/primitives/parachains/src/lib.rs b/primitives/parachains/src/lib.rs new file mode 100644 index 00000000000..e619fc7b641 --- /dev/null +++ b/primitives/parachains/src/lib.rs @@ -0,0 +1,180 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives of parachains module. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use bp_header_chain::StoredHeaderData; + +use bp_polkadot_core::{ + parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}, + BlockNumber as RelayBlockNumber, Hash as RelayBlockHash, +}; +use bp_runtime::{ + BlockNumberOf, Chain, HashOf, HeaderOf, Parachain, StorageDoubleMapKeyProvider, + StorageMapKeyProvider, +}; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{Blake2_128Concat, RuntimeDebug, Twox64Concat}; +use scale_info::TypeInfo; +use sp_core::storage::StorageKey; +use sp_runtime::traits::Header as HeaderT; +use sp_std::{marker::PhantomData, prelude::*}; + +/// Best known parachain head hash. +#[derive(Clone, Decode, Encode, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] +pub struct BestParaHeadHash { + /// Number of relay block where this head has been read. + /// + /// Parachain head is opaque to relay chain. So we can't simply decode it as a header of + /// parachains and call `block_number()` on it. Instead, we're using the fact that parachain + /// head is always built on top of previous head (because it is blockchain) and relay chain + /// always imports parachain heads in order. What it means for us is that at any given + /// **finalized** relay block `B`, head of parachain will be ancestor (or the same) of all + /// parachain heads available at descendants of `B`. + pub at_relay_block_number: RelayBlockNumber, + /// Hash of parachain head. + pub head_hash: ParaHash, +} + +/// Best known parachain head as it is stored in the runtime storage. +#[derive(Decode, Encode, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] +pub struct ParaInfo { + /// Best known parachain head hash. + pub best_head_hash: BestParaHeadHash, + /// Current ring buffer position for this parachain. + pub next_imported_hash_position: u32, +} + +/// Returns runtime storage key of given parachain head at the source chain. +/// +/// The head is stored by the `paras` pallet in the `Heads` map. +pub fn parachain_head_storage_key_at_source( + paras_pallet_name: &str, + para_id: ParaId, +) -> StorageKey { + bp_runtime::storage_map_final_key::(paras_pallet_name, "Heads", ¶_id.encode()) +} + +/// Can be use to access the runtime storage key of the parachains info at the target chain. +/// +/// The info is stored by the `pallet-bridge-parachains` pallet in the `ParasInfo` map. +pub struct ParasInfoKeyProvider; +impl StorageMapKeyProvider for ParasInfoKeyProvider { + const MAP_NAME: &'static str = "ParasInfo"; + + type Hasher = Blake2_128Concat; + type Key = ParaId; + type Value = ParaInfo; +} + +/// Can be use to access the runtime storage key of the parachain head at the target chain. +/// +/// The head is stored by the `pallet-bridge-parachains` pallet in the `ImportedParaHeads` map. +pub struct ImportedParaHeadsKeyProvider; +impl StorageDoubleMapKeyProvider for ImportedParaHeadsKeyProvider { + const MAP_NAME: &'static str = "ImportedParaHeads"; + + type Hasher1 = Blake2_128Concat; + type Key1 = ParaId; + type Hasher2 = Blake2_128Concat; + type Key2 = ParaHash; + type Value = ParaStoredHeaderData; +} + +/// Stored data of the parachain head. It is encoded version of the +/// `bp_runtime::StoredHeaderData` structure. +/// +/// We do not know exact structure of the parachain head, so we always store encoded version +/// of the `bp_runtime::StoredHeaderData`. It is only decoded when we talk about specific parachain. +#[derive(Clone, Decode, Encode, PartialEq, RuntimeDebug, TypeInfo)] +pub struct ParaStoredHeaderData(pub Vec); + +impl ParaStoredHeaderData { + /// Decode stored parachain head data. + pub fn decode_parachain_head_data( + &self, + ) -> Result, HashOf>, codec::Error> { + StoredHeaderData::, HashOf>::decode(&mut &self.0[..]) + } +} + +/// Stored parachain head data builder. +pub trait ParaStoredHeaderDataBuilder { + /// Return number of parachains that are supported by this builder. + fn supported_parachains() -> u32; + + /// Try to build head data from encoded head of parachain with given id. + fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option; +} + +/// Helper for using single parachain as `ParaStoredHeaderDataBuilder`. +pub struct SingleParaStoredHeaderDataBuilder(PhantomData); + +impl ParaStoredHeaderDataBuilder for SingleParaStoredHeaderDataBuilder { + fn supported_parachains() -> u32 { + 1 + } + + fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option { + if para_id == ParaId(C::PARACHAIN_ID) { + let header = HeaderOf::::decode(&mut ¶_head.0[..]).ok()?; + return Some(ParaStoredHeaderData( + StoredHeaderData { number: *header.number(), state_root: *header.state_root() } + .encode(), + )) + } + None + } +} + +// Tries to build header data from each tuple member, short-circuiting on first successful one. +#[impl_trait_for_tuples::impl_for_tuples(1, 30)] +#[tuple_types_custom_trait_bound(Parachain)] +impl ParaStoredHeaderDataBuilder for C { + fn supported_parachains() -> u32 { + let mut result = 0; + for_tuples!( #( + result += SingleParaStoredHeaderDataBuilder::::supported_parachains(); + )* ); + result + } + + fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option { + for_tuples!( #( + let maybe_para_head = SingleParaStoredHeaderDataBuilder::::try_build(para_id, para_head); + if let Some(maybe_para_head) = maybe_para_head { + return Some(maybe_para_head); + } + )* ); + + None + } +} + +/// A minimized version of `pallet-bridge-parachains::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeParachainCall { + /// `pallet-bridge-parachains::Call::submit_parachain_heads` + #[codec(index = 0)] + submit_parachain_heads { + at_relay_block: (RelayBlockNumber, RelayBlockHash), + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + }, +} diff --git a/primitives/polkadot-core/Cargo.toml b/primitives/polkadot-core/Cargo.toml new file mode 100644 index 00000000000..daae99ec71c --- /dev/null +++ b/primitives/polkadot-core/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "bp-polkadot-core" +description = "Primitives of Polkadot-like runtime." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } +parity-util-mem = { version = "0.12.0", optional = true } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } + +# Bridge Dependencies + +bp-messages = { path = "../messages", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Based Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +hex = "0.4" + +[features] +default = ["std"] +std = [ + "bp-messages/std", + "bp-runtime/std", + "frame-support/std", + "frame-system/std", + "codec/std", + "parity-util-mem", + "scale-info/std", + "serde", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/primitives/polkadot-core/src/lib.rs b/primitives/polkadot-core/src/lib.rs new file mode 100644 index 00000000000..3774d283fc4 --- /dev/null +++ b/primitives/polkadot-core/src/lib.rs @@ -0,0 +1,292 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_messages::MessageNonce; +use bp_runtime::{Chain, EncodedOrDecodedCall, StorageMapKeyProvider}; +use frame_support::{ + dispatch::DispatchClass, + parameter_types, + weights::{ + constants::{BlockExecutionWeight, WEIGHT_REF_TIME_PER_SECOND}, + Weight, + }, + Blake2_128Concat, RuntimeDebug, +}; +use frame_system::limits; +use sp_core::{storage::StorageKey, Hasher as HasherT}; +use sp_runtime::{ + generic, + traits::{BlakeTwo256, IdentifyAccount, Verify}, + MultiAddress, MultiSignature, OpaqueExtrinsic, +}; +use sp_std::prelude::Vec; + +// Re-export's to avoid extra substrate dependencies in chain-specific crates. +pub use frame_support::{weights::constants::ExtrinsicBaseWeight, Parameter}; +pub use sp_runtime::{traits::Convert, Perbill}; + +pub mod parachains; + +/// Maximal number of GRANDPA authorities at Polkadot-like chains. +/// +/// Ideally, we would set it to the value of `MaxAuthorities` constant from bridged runtime +/// configurations. But right now it is set to the `100_000`, which makes PoV size for +/// our bridge hub parachains huge. So let's stick to the real-world value here. +/// +/// Right now both Kusama and Polkadot aim to have around 1000 validators. Let's be safe here and +/// take a bit more here. +pub const MAX_AUTHORITIES_COUNT: u32 = 1_256; + +/// Reasonable number of headers in the `votes_ancestries` on Polkadot-like chains. +/// +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. +/// +/// This value comes from recent (February, 2023) Kusama and Polkadot headers. There are no +/// justifications with any additional headers in votes ancestry, so reasonable headers may +/// be set to zero. But we assume that there may be small GRANDPA lags, so we're leaving some +/// reserve here. +pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 2; + +/// Approximate average header size in `votes_ancestries` field of justification on Polkadot-like +/// chains. +/// +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. +/// +/// This value comes from recent (February, 2023) Kusama headers. Average is `336` there, but some +/// non-mandatory headers has size `40kb` (they contain the BABE epoch descriptor with all +/// authorities - just like our mandatory header). Since we assume `2` headers in justification +/// votes ancestry, let's set average header to `40kb / 2`. +pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 20 * 1024; + +/// Approximate maximal header size on Polkadot-like chains. +/// +/// See [`bp-header-chain::ChainWithGrandpa`] for more details. +/// +/// This value comes from recent (February, 2023) Kusama headers. Maximal header is a mandatory +/// header. In its SCALE-encoded form it is `80348` bytes. Let's have some reserve here. +pub const MAX_HEADER_SIZE: u32 = 90_000; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// Polkadot-like chain. This mostly depends on number of entries in the storage trie. +/// Some reserve is reserved to account future chain growth. +/// +/// To compute this value, we've synced Kusama chain blocks [0; 6545733] to see if there were +/// any significant changes of the storage proof size (NO): +/// +/// - at block 3072 the storage proof size overhead was 579 bytes; +/// - at block 2479616 it was 578 bytes; +/// - at block 4118528 it was 711 bytes; +/// - at block 6540800 it was 779 bytes. +/// +/// The number of storage entries at the block 6546170 was 351207 and number of trie nodes in +/// the storage proof was 5 (log(16, 351207) ~ 4.6). +/// +/// So the assumption is that the storage proof size overhead won't be larger than 1024 in the +/// nearest future. If it'll ever break this barrier, then we'll need to update this constant +/// at next runtime upgrade. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// All Polkadot-like chains allow normal extrinsics to fill block up to 75 percent. +/// +/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// All Polkadot-like chains allow 2 seconds of compute with a 6-second average block time. +/// +/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. +pub const MAXIMUM_BLOCK_WEIGHT: Weight = + Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), u64::MAX); + +/// All Polkadot-like chains assume that an on-initialize consumes 1 percent of the weight on +/// average, hence a single extrinsic will not be allowed to consume more than +/// `AvailableBlockRatio - 1 percent`. +/// +/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. +pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(1); + +parameter_types! { + /// All Polkadot-like chains have maximal block size set to 5MB. + /// + /// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. + pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio( + 5 * 1024 * 1024, + NORMAL_DISPATCH_RATIO, + ); + /// All Polkadot-like chains have the same block weights. + /// + /// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. + pub BlockWeights: limits::BlockWeights = limits::BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + // Operational transactions have an extra reserved space, so that they + // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT, + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); +} + +// TODO [#78] may need to be updated after https://github.com/paritytech/parity-bridges-common/issues/78 +/// Maximal number of messages in single delivery transaction. +pub const MAX_MESSAGES_IN_DELIVERY_TRANSACTION: MessageNonce = 128; + +/// Maximal number of bytes, included in the signed Polkadot-like transaction apart from the encoded +/// call itself. +/// +/// Can be computed by subtracting encoded call size from raw transaction size. +pub const TX_EXTRA_BYTES: u32 = 256; + +/// Re-export `time_units` to make usage easier. +pub use time_units::*; + +/// Human readable time units defined in terms of number of blocks. +pub mod time_units { + use super::BlockNumber; + + pub const MILLISECS_PER_BLOCK: u64 = 6000; + pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + + pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); + pub const HOURS: BlockNumber = MINUTES * 60; + pub const DAYS: BlockNumber = HOURS * 24; +} + +/// Block number type used in Polkadot-like chains. +pub type BlockNumber = u32; + +/// Hash type used in Polkadot-like chains. +pub type Hash = ::Out; + +/// Account Index (a.k.a. nonce). +pub type Index = u32; + +/// Hashing type. +pub type Hashing = BlakeTwo256; + +/// The type of object that can produce hashes on Polkadot-like chains. +pub type Hasher = BlakeTwo256; + +/// The header type used by Polkadot-like chains. +pub type Header = generic::Header; + +/// Signature type used by Polkadot-like chains. +pub type Signature = MultiSignature; + +/// Public key of account on Polkadot-like chains. +pub type AccountPublic = ::Signer; + +/// Id of account on Polkadot-like chains. +pub type AccountId = ::AccountId; + +/// Address of account on Polkadot-like chains. +pub type AccountAddress = MultiAddress; + +/// Index of a transaction on the Polkadot-like chains. +pub type Nonce = u32; + +/// Block type of Polkadot-like chains. +pub type Block = generic::Block; + +/// Polkadot-like block signed with a Justification. +pub type SignedBlock = generic::SignedBlock; + +/// The balance of an account on Polkadot-like chain. +pub type Balance = u128; + +/// Unchecked Extrinsic type. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic, Signature, SignedExt>; + +/// Account address, used by the Polkadot-like chain. +pub type Address = MultiAddress; + +/// Polkadot-like chain. +#[derive(RuntimeDebug)] +pub struct PolkadotLike; + +impl Chain for PolkadotLike { + type BlockNumber = BlockNumber; + type Hash = Hash; + type Hasher = Hasher; + type Header = Header; + + type AccountId = AccountId; + type Balance = Balance; + type Index = Index; + type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + *BlockLength::get().max.get(DispatchClass::Normal) + } + + fn max_extrinsic_weight() -> Weight { + BlockWeights::get() + .get(DispatchClass::Normal) + .max_extrinsic + .unwrap_or(Weight::MAX) + } +} + +/// Provides a storage key for account data. +/// +/// We need to use this approach when we don't have access to the runtime. +/// The equivalent command to invoke in case full `Runtime` is known is this: +/// `let key = frame_system::Account::::storage_map_final_key(&account_id);` +pub struct AccountInfoStorageMapKeyProvider; + +impl StorageMapKeyProvider for AccountInfoStorageMapKeyProvider { + const MAP_NAME: &'static str = "Account"; + type Hasher = Blake2_128Concat; + type Key = AccountId; + // This should actually be `AccountInfo`, but we don't use this property in order to decode the + // data. So we use `Vec` as if we would work with encoded data. + type Value = Vec; +} + +impl AccountInfoStorageMapKeyProvider { + const PALLET_NAME: &'static str = "System"; + + pub fn final_key(id: &AccountId) -> StorageKey { + ::final_key(Self::PALLET_NAME, id) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_generate_storage_key() { + let acc = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, + ] + .into(); + let key = AccountInfoStorageMapKeyProvider::final_key(&acc); + assert_eq!(hex::encode(key), "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92dccd599abfe1920a1cff8a7358231430102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"); + } +} diff --git a/primitives/polkadot-core/src/parachains.rs b/primitives/polkadot-core/src/parachains.rs new file mode 100644 index 00000000000..0b410dff49f --- /dev/null +++ b/primitives/polkadot-core/src/parachains.rs @@ -0,0 +1,98 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives of polkadot-like chains, that are related to parachains functionality. +//! +//! Even though this (bridges) repository references polkadot repository, we can't +//! reference polkadot crates from pallets. That's because bridges repository is +//! included in the polkadot repository and included pallets are used by polkadot +//! chains. Having pallets that are referencing polkadot, would mean that there may +//! be two versions of polkadot crates included in the runtime. Which is bad. + +use bp_runtime::{RawStorageProof, Size}; +use codec::{CompactAs, Decode, Encode, MaxEncodedLen}; +use frame_support::RuntimeDebug; +use scale_info::TypeInfo; +use sp_core::Hasher; +use sp_std::vec::Vec; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "std")] +use parity_util_mem::MallocSizeOf; + +/// Parachain id. +/// +/// This is an equivalent of the `polkadot_parachain::Id`, which is a compact-encoded `u32`. +#[derive( + Clone, + CompactAs, + Copy, + Decode, + Default, + Encode, + Eq, + Hash, + MaxEncodedLen, + Ord, + PartialEq, + PartialOrd, + RuntimeDebug, + TypeInfo, +)] +pub struct ParaId(pub u32); + +impl From for ParaId { + fn from(id: u32) -> Self { + ParaId(id) + } +} + +/// Parachain head. +/// +/// This is an equivalent of the `polkadot_parachain::HeadData`. +/// +/// The parachain head means (at least in Cumulus) a SCALE-encoded parachain header. +#[derive( + PartialEq, Eq, Clone, PartialOrd, Ord, Encode, Decode, RuntimeDebug, TypeInfo, Default, +)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, MallocSizeOf))] +pub struct ParaHead(pub Vec); + +impl ParaHead { + /// Returns the hash of this head data. + pub fn hash(&self) -> crate::Hash { + sp_runtime::traits::BlakeTwo256::hash(&self.0) + } +} + +/// Parachain head hash. +pub type ParaHash = crate::Hash; + +/// Parachain head hasher. +pub type ParaHasher = crate::Hasher; + +/// Raw storage proof of parachain heads, stored in polkadot-like chain runtime. +#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] +pub struct ParaHeadsProof(pub RawStorageProof); + +impl Size for ParaHeadsProof { + fn size(&self) -> u32 { + u32::try_from(self.0.iter().fold(0usize, |sum, node| sum.saturating_add(node.len()))) + .unwrap_or(u32::MAX) + } +} diff --git a/primitives/relayers/Cargo.toml b/primitives/relayers/Cargo.toml new file mode 100644 index 00000000000..8ac31258488 --- /dev/null +++ b/primitives/relayers/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "bp-relayers" +description = "Primitives of relayers module." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive", "bit-vec"] } +scale-info = { version = "2.5.0", default-features = false, features = ["bit-vec", "derive"] } + +# Bridge Dependencies + +bp-messages = { path = "../messages", default-features = false } +bp-runtime = { path = "../runtime", default-features = false } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[dev-dependencies] +hex = "0.4" +hex-literal = "0.4" + +[features] +default = ["std"] +std = [ + "bp-messages/std", + "bp-runtime/std", + "frame-support/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/primitives/relayers/src/lib.rs b/primitives/relayers/src/lib.rs new file mode 100644 index 00000000000..f14b841fa9e --- /dev/null +++ b/primitives/relayers/src/lib.rs @@ -0,0 +1,202 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives of messages module. + +#![warn(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_messages::LaneId; +use bp_runtime::{ChainId, StorageDoubleMapKeyProvider}; +use frame_support::{traits::tokens::Preservation, Blake2_128Concat, Identity}; +use scale_info::TypeInfo; +use sp_runtime::{ + codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}, + traits::AccountIdConversion, + TypeId, +}; +use sp_std::{fmt::Debug, marker::PhantomData}; + +/// The owner of the sovereign account that should pay the rewards. +/// +/// Each of the 2 final points connected by a bridge owns a sovereign account at each end of the +/// bridge. So here, at this end of the bridge there can be 2 sovereign accounts that pay rewards. +#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] +pub enum RewardsAccountOwner { + /// The sovereign account of the final chain on this end of the bridge. + ThisChain, + /// The sovereign account of the final chain on the other end of the bridge. + BridgedChain, +} + +/// Structure used to identify the account that pays a reward to the relayer. +/// +/// A bridge connects 2 bridge ends. Each one is located on a separate relay chain. The bridge ends +/// can be the final destinations of the bridge, or they can be intermediary points +/// (e.g. a bridge hub) used to forward messages between pairs of parachains on the bridged relay +/// chains. A pair of such parachains is connected using a bridge lane. Each of the 2 final +/// destinations of a bridge lane must have a sovereign account at each end of the bridge and each +/// of the sovereign accounts will pay rewards for different operations. So we need multiple +/// parameters to identify the account that pays a reward to the relayer. +#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] +pub struct RewardsAccountParams { + lane_id: LaneId, + bridged_chain_id: ChainId, + owner: RewardsAccountOwner, +} + +impl RewardsAccountParams { + /// Create a new instance of `RewardsAccountParams`. + pub const fn new( + lane_id: LaneId, + bridged_chain_id: ChainId, + owner: RewardsAccountOwner, + ) -> Self { + Self { lane_id, bridged_chain_id, owner } + } +} + +impl TypeId for RewardsAccountParams { + const TYPE_ID: [u8; 4] = *b"brap"; +} + +/// Reward payment procedure. +pub trait PaymentProcedure { + /// Error that may be returned by the procedure. + type Error: Debug; + + /// Pay reward to the relayer from the account with provided params. + fn pay_reward( + relayer: &Relayer, + rewards_account_params: RewardsAccountParams, + reward: Reward, + ) -> Result<(), Self::Error>; +} + +impl PaymentProcedure for () { + type Error = &'static str; + + fn pay_reward(_: &Relayer, _: RewardsAccountParams, _: Reward) -> Result<(), Self::Error> { + Ok(()) + } +} + +/// Reward payment procedure that does `balances::transfer` call from the account, derived from +/// given params. +pub struct PayRewardFromAccount(PhantomData<(T, Relayer)>); + +impl PayRewardFromAccount +where + Relayer: Decode + Encode, +{ + /// Return account that pays rewards based on the provided parameters. + pub fn rewards_account(params: RewardsAccountParams) -> Relayer { + params.into_sub_account_truncating(b"rewards-account") + } +} + +impl PaymentProcedure for PayRewardFromAccount +where + T: frame_support::traits::fungible::Mutate, + Relayer: Decode + Encode, +{ + type Error = sp_runtime::DispatchError; + + fn pay_reward( + relayer: &Relayer, + rewards_account_params: RewardsAccountParams, + reward: T::Balance, + ) -> Result<(), Self::Error> { + T::transfer( + &Self::rewards_account(rewards_account_params), + relayer, + reward, + Preservation::Expendable, + ) + .map(drop) + } +} + +/// Can be use to access the runtime storage key within the `RelayerRewards` map of the relayers +/// pallet. +pub struct RelayerRewardsKeyProvider(PhantomData<(AccountId, Reward)>); + +impl StorageDoubleMapKeyProvider for RelayerRewardsKeyProvider +where + AccountId: Codec + EncodeLike, + Reward: Codec + EncodeLike, +{ + const MAP_NAME: &'static str = "RelayerRewards"; + + type Hasher1 = Blake2_128Concat; + type Key1 = AccountId; + type Hasher2 = Identity; + type Key2 = RewardsAccountParams; + type Value = Reward; +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_messages::LaneId; + use sp_runtime::testing::H256; + + #[test] + fn different_lanes_are_using_different_accounts() { + assert_eq!( + PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( + LaneId([0, 0, 0, 0]), + *b"test", + RewardsAccountOwner::ThisChain + )), + hex_literal::hex!("62726170000000007465737400726577617264732d6163636f756e7400000000") + .into(), + ); + + assert_eq!( + PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( + LaneId([0, 0, 0, 1]), + *b"test", + RewardsAccountOwner::ThisChain + )), + hex_literal::hex!("62726170000000017465737400726577617264732d6163636f756e7400000000") + .into(), + ); + } + + #[test] + fn different_directions_are_using_different_accounts() { + assert_eq!( + PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( + LaneId([0, 0, 0, 0]), + *b"test", + RewardsAccountOwner::ThisChain + )), + hex_literal::hex!("62726170000000007465737400726577617264732d6163636f756e7400000000") + .into(), + ); + + assert_eq!( + PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( + LaneId([0, 0, 0, 0]), + *b"test", + RewardsAccountOwner::BridgedChain + )), + hex_literal::hex!("62726170000000007465737401726577617264732d6163636f756e7400000000") + .into(), + ); + } +} diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml new file mode 100644 index 00000000000..4d48ad61894 --- /dev/null +++ b/primitives/runtime/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "bp-runtime" +description = "Primitives that may be used at (bridges) runtime level." +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +hash-db = { version = "0.16.0", default-features = false } +impl-trait-for-tuples = "0.2.2" +num-traits = { version = "0.2", default-features = false } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +trie-db = { version = "0.27.1", default-features = false } + +[dev-dependencies] +hex-literal = "0.4" + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "hash-db/std", + "num-traits/std", + "scale-info/std", + "serde", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "sp-state-machine/std", + "sp-trie/std", + "trie-db/std", +] diff --git a/primitives/runtime/src/chain.rs b/primitives/runtime/src/chain.rs new file mode 100644 index 00000000000..94b3a193c58 --- /dev/null +++ b/primitives/runtime/src/chain.rs @@ -0,0 +1,375 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::HeaderIdProvider; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{weights::Weight, Parameter}; +use num_traits::{AsPrimitive, Bounded, CheckedSub, Saturating, SaturatingAdd, Zero}; +use sp_runtime::{ + traits::{ + AtLeast32Bit, AtLeast32BitUnsigned, Hash as HashT, Header as HeaderT, MaybeDisplay, + MaybeSerialize, MaybeSerializeDeserialize, Member, SimpleBitOps, Verify, + }, + FixedPointOperand, +}; +use sp_std::{convert::TryFrom, fmt::Debug, hash::Hash, str::FromStr, vec, vec::Vec}; + +/// Chain call, that is either SCALE-encoded, or decoded. +#[derive(Debug, Clone, PartialEq)] +pub enum EncodedOrDecodedCall { + /// The call that is SCALE-encoded. + /// + /// This variant is used when we the chain runtime is not bundled with the relay, but + /// we still need the represent call in some RPC calls or transactions. + Encoded(Vec), + /// The decoded call. + Decoded(ChainCall), +} + +impl EncodedOrDecodedCall { + /// Returns decoded call. + pub fn to_decoded(&self) -> Result { + match self { + Self::Encoded(ref encoded_call) => + ChainCall::decode(&mut &encoded_call[..]).map_err(Into::into), + Self::Decoded(ref decoded_call) => Ok(decoded_call.clone()), + } + } + + /// Converts self to decoded call. + pub fn into_decoded(self) -> Result { + match self { + Self::Encoded(encoded_call) => + ChainCall::decode(&mut &encoded_call[..]).map_err(Into::into), + Self::Decoded(decoded_call) => Ok(decoded_call), + } + } +} + +impl From for EncodedOrDecodedCall { + fn from(call: ChainCall) -> EncodedOrDecodedCall { + EncodedOrDecodedCall::Decoded(call) + } +} + +impl Decode for EncodedOrDecodedCall { + fn decode(input: &mut I) -> Result { + // having encoded version is better than decoded, because decoding isn't required + // everywhere and for mocked calls it may lead to **unneeded** errors + match input.remaining_len()? { + Some(remaining_len) => { + let mut encoded_call = vec![0u8; remaining_len]; + input.read(&mut encoded_call)?; + Ok(EncodedOrDecodedCall::Encoded(encoded_call)) + }, + None => Ok(EncodedOrDecodedCall::Decoded(ChainCall::decode(input)?)), + } + } +} + +impl Encode for EncodedOrDecodedCall { + fn encode(&self) -> Vec { + match *self { + Self::Encoded(ref encoded_call) => encoded_call.clone(), + Self::Decoded(ref decoded_call) => decoded_call.encode(), + } + } +} + +/// Minimal Substrate-based chain representation that may be used from no_std environment. +pub trait Chain: Send + Sync + 'static { + /// A type that fulfills the abstract idea of what a Substrate block number is. + // Constraits come from the associated Number type of `sp_runtime::traits::Header` + // See here for more info: + // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Number + // + // Note that the `AsPrimitive` trait is required by the GRANDPA justification + // verifier, and is not usually part of a Substrate Header's Number type. + type BlockNumber: Parameter + + Member + + MaybeSerializeDeserialize + + Hash + + Copy + + Default + + MaybeDisplay + + AtLeast32BitUnsigned + + FromStr + + AsPrimitive + + Default + + Saturating + + MaxEncodedLen; + + /// A type that fulfills the abstract idea of what a Substrate hash is. + // Constraits come from the associated Hash type of `sp_runtime::traits::Header` + // See here for more info: + // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Hash + type Hash: Parameter + + Member + + MaybeSerializeDeserialize + + Hash + + Ord + + Copy + + MaybeDisplay + + Default + + SimpleBitOps + + AsRef<[u8]> + + AsMut<[u8]> + + MaxEncodedLen; + + /// A type that fulfills the abstract idea of what a Substrate hasher (a type + /// that produces hashes) is. + // Constraits come from the associated Hashing type of `sp_runtime::traits::Header` + // See here for more info: + // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Hashing + type Hasher: HashT; + + /// A type that fulfills the abstract idea of what a Substrate header is. + // See here for more info: + // https://crates.parity.io/sp_runtime/traits/trait.Header.html + type Header: Parameter + + HeaderT + + HeaderIdProvider + + MaybeSerializeDeserialize; + + /// The user account identifier type for the runtime. + type AccountId: Parameter + + Member + + MaybeSerializeDeserialize + + Debug + + MaybeDisplay + + Ord + + MaxEncodedLen; + /// Balance of an account in native tokens. + /// + /// The chain may support multiple tokens, but this particular type is for token that is used + /// to pay for transaction dispatch, to reward different relayers (headers, messages), etc. + type Balance: AtLeast32BitUnsigned + + FixedPointOperand + + Parameter + + Member + + MaybeSerializeDeserialize + + Clone + + Copy + + Bounded + + CheckedSub + + PartialOrd + + SaturatingAdd + + Zero + + TryFrom + + MaxEncodedLen; + /// Index of a transaction used by the chain. + type Index: Parameter + + Member + + MaybeSerialize + + Debug + + Default + + MaybeDisplay + + MaybeSerializeDeserialize + + AtLeast32Bit + + Copy + + MaxEncodedLen; + /// Signature type, used on this chain. + type Signature: Parameter + Verify; + + /// Get the maximum size (in bytes) of a Normal extrinsic at this chain. + fn max_extrinsic_size() -> u32; + /// Get the maximum weight (compute time) that a Normal extrinsic at this chain can use. + fn max_extrinsic_weight() -> Weight; +} + +/// A trait that provides the type of the underlying chain. +pub trait UnderlyingChainProvider { + /// Underlying chain type. + type Chain: Chain; +} + +impl Chain for T +where + T: Send + Sync + 'static + UnderlyingChainProvider, +{ + type BlockNumber = ::BlockNumber; + type Hash = ::Hash; + type Hasher = ::Hasher; + type Header = ::Header; + type AccountId = ::AccountId; + type Balance = ::Balance; + type Index = ::Index; + type Signature = ::Signature; + + fn max_extrinsic_size() -> u32 { + ::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + ::max_extrinsic_weight() + } +} + +/// Minimal parachain representation that may be used from no_std environment. +pub trait Parachain: Chain { + /// Parachain identifier. + const PARACHAIN_ID: u32; +} + +impl Parachain for T +where + T: Chain + UnderlyingChainProvider, + ::Chain: Parachain, +{ + const PARACHAIN_ID: u32 = <::Chain as Parachain>::PARACHAIN_ID; +} + +/// Underlying chain type. +pub type UnderlyingChainOf = ::Chain; + +/// Block number used by the chain. +pub type BlockNumberOf = ::BlockNumber; + +/// Hash type used by the chain. +pub type HashOf = ::Hash; + +/// Hasher type used by the chain. +pub type HasherOf = ::Hasher; + +/// Header type used by the chain. +pub type HeaderOf = ::Header; + +/// Account id type used by the chain. +pub type AccountIdOf = ::AccountId; + +/// Balance type used by the chain. +pub type BalanceOf = ::Balance; + +/// Transaction index type used by the chain. +pub type IndexOf = ::Index; + +/// Signature type used by the chain. +pub type SignatureOf = ::Signature; + +/// Account public type used by the chain. +pub type AccountPublicOf = as Verify>::Signer; + +/// Transaction era used by the chain. +pub type TransactionEraOf = crate::TransactionEra, HashOf>; + +/// Convenience macro that declares bridge finality runtime apis and related constants for a chain. +/// This includes: +/// - chain-specific bridge runtime APIs: +/// - `FinalityApi` +/// - constants that are stringified names of runtime API methods: +/// - `BEST_FINALIZED__HEADER_METHOD` +/// The name of the chain has to be specified in snake case (e.g. `rialto_parachain`). +#[macro_export] +macro_rules! decl_bridge_finality_runtime_apis { + ($chain: ident) => { + bp_runtime::paste::item! { + mod [<$chain _finality_api>] { + use super::*; + + /// Name of the `FinalityApi::best_finalized` runtime method. + pub const []: &str = + stringify!([<$chain:camel FinalityApi_best_finalized>]); + + sp_api::decl_runtime_apis! { + /// API for querying information about the finalized chain headers. + /// + /// This API is implemented by runtimes that are receiving messages from this chain, not by this + /// chain's runtime itself. + pub trait [<$chain:camel FinalityApi>] { + /// Returns number and hash of the best finalized header known to the bridge module. + fn best_finalized() -> Option>; + } + } + } + + pub use [<$chain _finality_api>]::*; + } + }; +} + +/// Convenience macro that declares bridge messages runtime apis and related constants for a chain. +/// This includes: +/// - chain-specific bridge runtime APIs: +/// - `ToOutboundLaneApi` +/// - `FromInboundLaneApi` +/// - constants that are stringified names of runtime API methods: +/// - `FROM__MESSAGE_DETAILS_METHOD`, +/// The name of the chain has to be specified in snake case (e.g. `rialto_parachain`). +#[macro_export] +macro_rules! decl_bridge_messages_runtime_apis { + ($chain: ident) => { + bp_runtime::paste::item! { + mod [<$chain _messages_api>] { + use super::*; + + /// Name of the `ToOutboundLaneApi::message_details` runtime method. + pub const []: &str = + stringify!([]); + + /// Name of the `FromInboundLaneApi::message_details` runtime method. + pub const []: &str = + stringify!([]); + + sp_api::decl_runtime_apis! { + /// Outbound message lane API for messages that are sent to this chain. + /// + /// This API is implemented by runtimes that are receiving messages from this chain, not by this + /// chain's runtime itself. + pub trait [] { + /// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all + /// messages in given inclusive range. + /// + /// If some (or all) messages are missing from the storage, they'll also will + /// be missing from the resulting vector. The vector is ordered by the nonce. + fn message_details( + lane: LaneId, + begin: MessageNonce, + end: MessageNonce, + ) -> Vec; + } + + /// Inbound message lane API for messages sent by this chain. + /// + /// This API is implemented by runtimes that are receiving messages from this chain, not by this + /// chain's runtime itself. + /// + /// Entries of the resulting vector are matching entries of the `messages` vector. Entries of the + /// `messages` vector may (and need to) be read using `ToOutboundLaneApi::message_details`. + pub trait [] { + /// Return details of given inbound messages. + fn message_details( + lane: LaneId, + messages: Vec<(MessagePayload, OutboundMessageDetails)>, + ) -> Vec; + } + } + } + + pub use [<$chain _messages_api>]::*; + } + }; +} + +/// Convenience macro that declares bridge finality runtime apis, bridge messages runtime apis +/// and related constants for a chain. +/// The name of the chain has to be specified in snake case (e.g. `rialto_parachain`). +#[macro_export] +macro_rules! decl_bridge_runtime_apis { + ($chain: ident) => { + bp_runtime::decl_bridge_finality_runtime_apis!($chain); + bp_runtime::decl_bridge_messages_runtime_apis!($chain); + }; +} diff --git a/primitives/runtime/src/extensions.rs b/primitives/runtime/src/extensions.rs new file mode 100644 index 00000000000..96ee9d1e6ec --- /dev/null +++ b/primitives/runtime/src/extensions.rs @@ -0,0 +1,144 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives that may be used for creating signed extensions for indirect runtimes. + +use codec::{Compact, Decode, Encode}; +use impl_trait_for_tuples::impl_for_tuples; +use scale_info::{StaticTypeInfo, TypeInfo}; +use sp_runtime::{ + traits::{DispatchInfoOf, SignedExtension}, + transaction_validity::TransactionValidityError, +}; +use sp_std::{fmt::Debug, marker::PhantomData}; + +/// Trait that describes some properties of a `SignedExtension` that are needed in order to send a +/// transaction to the chain. +pub trait SignedExtensionSchema: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo { + /// A type of the data encoded as part of the transaction. + type Payload: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo; + /// Parameters which are part of the payload used to produce transaction signature, + /// but don't end up in the transaction itself (i.e. inherent part of the runtime). + type AdditionalSigned: Encode + Debug + Eq + Clone + StaticTypeInfo; +} + +// An implementation of `SignedExtensionSchema` using generic params. +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo)] +pub struct GenericSignedExtensionSchema(PhantomData<(P, S)>); + +impl SignedExtensionSchema for GenericSignedExtensionSchema +where + P: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo, + S: Encode + Debug + Eq + Clone + StaticTypeInfo, +{ + type Payload = P; + type AdditionalSigned = S; +} + +/// The `SignedExtensionSchema` for `frame_system::CheckNonZeroSender`. +pub type CheckNonZeroSender = GenericSignedExtensionSchema<(), ()>; + +/// The `SignedExtensionSchema` for `frame_system::CheckSpecVersion`. +pub type CheckSpecVersion = GenericSignedExtensionSchema<(), u32>; + +/// The `SignedExtensionSchema` for `frame_system::CheckTxVersion`. +pub type CheckTxVersion = GenericSignedExtensionSchema<(), u32>; + +/// The `SignedExtensionSchema` for `frame_system::CheckGenesis`. +pub type CheckGenesis = GenericSignedExtensionSchema<(), Hash>; + +/// The `SignedExtensionSchema` for `frame_system::CheckEra`. +pub type CheckEra = GenericSignedExtensionSchema; + +/// The `SignedExtensionSchema` for `frame_system::CheckNonce`. +pub type CheckNonce = GenericSignedExtensionSchema, ()>; + +/// The `SignedExtensionSchema` for `frame_system::CheckWeight`. +pub type CheckWeight = GenericSignedExtensionSchema<(), ()>; + +/// The `SignedExtensionSchema` for `pallet_transaction_payment::ChargeTransactionPayment`. +pub type ChargeTransactionPayment = GenericSignedExtensionSchema, ()>; + +/// The `SignedExtensionSchema` for `BridgeRejectObsoleteHeadersAndMessages`. +pub type BridgeRejectObsoleteHeadersAndMessages = GenericSignedExtensionSchema<(), ()>; + +/// The `SignedExtensionSchema` for `RefundBridgedParachainMessages`. +/// This schema is dedicated for `RefundBridgedParachainMessages` signed extension as +/// wildcard/placeholder, which relies on the scale encoding for `()` or `((), ())`, or `((), (), +/// ())` is the same. So runtime can contains any kind of tuple: +/// `(BridgeRefundBridgeHubRococoMessages)` +/// `(BridgeRefundBridgeHubRococoMessages, BridgeRefundBridgeHubWococoMessages)` +/// `(BridgeRefundParachainMessages1, ..., BridgeRefundParachainMessagesN)` +pub type RefundBridgedParachainMessagesSchema = GenericSignedExtensionSchema<(), ()>; + +#[impl_for_tuples(1, 12)] +impl SignedExtensionSchema for Tuple { + for_tuples!( type Payload = ( #( Tuple::Payload ),* ); ); + for_tuples!( type AdditionalSigned = ( #( Tuple::AdditionalSigned ),* ); ); +} + +/// A simplified version of signed extensions meant for producing signed transactions +/// and signed payloads in the client code. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub struct GenericSignedExtension { + pub payload: S::Payload, + #[codec(skip)] + // It may be set to `None` if extensions are decoded. We are never reconstructing transactions + // (and it makes no sense to do that) => decoded version of `SignedExtensions` is only used to + // read fields of the `payload`. And when resigning transaction, we're reconstructing + // `SignedExtensions` from the scratch. + additional_signed: Option, +} + +impl GenericSignedExtension { + pub fn new(payload: S::Payload, additional_signed: Option) -> Self { + Self { payload, additional_signed } + } +} + +impl SignedExtension for GenericSignedExtension +where + S: SignedExtensionSchema, + S::Payload: Send + Sync, + S::AdditionalSigned: Send + Sync, +{ + const IDENTIFIER: &'static str = "Not needed."; + type AccountId = (); + type Call = (); + type AdditionalSigned = S::AdditionalSigned; + type Pre = (); + + fn additional_signed(&self) -> Result { + // we shall not ever see this error in relay, because we are never signing decoded + // transactions. Instead we're constructing and signing new transactions. So the error code + // is kinda random here + self.additional_signed.clone().ok_or( + frame_support::unsigned::TransactionValidityError::Unknown( + frame_support::unsigned::UnknownTransaction::Custom(0xFF), + ), + ) + } + + fn pre_dispatch( + self, + _who: &Self::AccountId, + _call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + Ok(()) + } +} diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs new file mode 100644 index 00000000000..df77745bc02 --- /dev/null +++ b/primitives/runtime/src/lib.rs @@ -0,0 +1,573 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives that may be used at (bridges) runtime level. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode, FullCodec, MaxEncodedLen}; +use frame_support::{ + log, pallet_prelude::DispatchResult, weights::Weight, PalletError, RuntimeDebug, StorageHasher, + StorageValue, +}; +use frame_system::RawOrigin; +use scale_info::TypeInfo; +use sp_core::storage::StorageKey; +use sp_runtime::traits::{BadOrigin, Header as HeaderT, UniqueSaturatedInto}; +use sp_std::{convert::TryFrom, fmt::Debug, ops::RangeInclusive, vec, vec::Vec}; + +pub use chain::{ + AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf, + HasherOf, HeaderOf, IndexOf, Parachain, SignatureOf, TransactionEraOf, UnderlyingChainOf, + UnderlyingChainProvider, +}; +pub use frame_support::storage::storage_prefix as storage_value_final_key; +use num_traits::{CheckedAdd, CheckedSub, One}; +pub use storage_proof::{ + record_all_keys as record_all_trie_keys, Error as StorageProofError, + ProofSize as StorageProofSize, RawStorageProof, StorageProofChecker, +}; +pub use storage_types::BoundedStorageValue; + +#[cfg(feature = "std")] +pub use storage_proof::craft_valid_storage_proof; + +pub mod extensions; +pub mod messages; + +mod chain; +mod storage_proof; +mod storage_types; + +// Re-export macro to aviod include paste dependency everywhere +pub use sp_runtime::paste; + +/// Use this when something must be shared among all instances. +pub const NO_INSTANCE_ID: ChainId = [0, 0, 0, 0]; + +/// Rialto chain id. +pub const RIALTO_CHAIN_ID: ChainId = *b"rlto"; + +/// RialtoParachain chain id. +pub const RIALTO_PARACHAIN_CHAIN_ID: ChainId = *b"rlpa"; + +/// Millau chain id. +pub const MILLAU_CHAIN_ID: ChainId = *b"mlau"; + +/// Polkadot chain id. +pub const POLKADOT_CHAIN_ID: ChainId = *b"pdot"; + +/// Kusama chain id. +pub const KUSAMA_CHAIN_ID: ChainId = *b"ksma"; + +/// Westend chain id. +pub const WESTEND_CHAIN_ID: ChainId = *b"wend"; + +/// Westend chain id. +pub const WESTMINT_CHAIN_ID: ChainId = *b"wmnt"; + +/// Rococo chain id. +pub const ROCOCO_CHAIN_ID: ChainId = *b"roco"; + +/// Wococo chain id. +pub const WOCOCO_CHAIN_ID: ChainId = *b"woco"; + +/// BridgeHubRococo chain id. +pub const BRIDGE_HUB_ROCOCO_CHAIN_ID: ChainId = *b"bhro"; + +/// BridgeHubWococo chain id. +pub const BRIDGE_HUB_WOCOCO_CHAIN_ID: ChainId = *b"bhwo"; + +/// BridgeHubKusama chain id. +pub const BRIDGE_HUB_KUSAMA_CHAIN_ID: ChainId = *b"bhks"; + +/// BridgeHubPolkadot chain id. +pub const BRIDGE_HUB_POLKADOT_CHAIN_ID: ChainId = *b"bhwo"; + +/// Generic header Id. +#[derive( + RuntimeDebug, + Default, + Clone, + Encode, + Decode, + Copy, + Eq, + Hash, + MaxEncodedLen, + PartialEq, + PartialOrd, + Ord, + TypeInfo, +)] +pub struct HeaderId(pub Number, pub Hash); + +impl HeaderId { + /// Return header number. + pub fn number(&self) -> Number { + self.0 + } + + /// Return header hash. + pub fn hash(&self) -> Hash { + self.1 + } +} + +/// Header id used by the chain. +pub type HeaderIdOf = HeaderId, BlockNumberOf>; + +/// Generic header id provider. +pub trait HeaderIdProvider { + // Get the header id. + fn id(&self) -> HeaderId; + + // Get the header id for the parent block. + fn parent_id(&self) -> Option>; +} + +impl HeaderIdProvider
for Header { + fn id(&self) -> HeaderId { + HeaderId(*self.number(), self.hash()) + } + + fn parent_id(&self) -> Option> { + self.number() + .checked_sub(&One::one()) + .map(|parent_number| HeaderId(parent_number, *self.parent_hash())) + } +} + +/// Unique identifier of the chain. +/// +/// In addition to its main function (identifying the chain), this type may also be used to +/// identify module instance. We have a bunch of pallets that may be used in different bridges. E.g. +/// messages pallet may be deployed twice in the same runtime to bridge ThisChain with Chain1 and +/// Chain2. Sometimes we need to be able to identify deployed instance dynamically. This type may be +/// used for that. +pub type ChainId = [u8; 4]; + +/// Anything that has size. +pub trait Size { + /// Return size of this object (in bytes). + fn size(&self) -> u32; +} + +impl Size for () { + fn size(&self) -> u32 { + 0 + } +} + +impl Size for Vec { + fn size(&self) -> u32 { + self.len() as _ + } +} + +/// Pre-computed size. +pub struct PreComputedSize(pub usize); + +impl Size for PreComputedSize { + fn size(&self) -> u32 { + u32::try_from(self.0).unwrap_or(u32::MAX) + } +} + +/// Era of specific transaction. +#[derive(RuntimeDebug, Clone, Copy, PartialEq)] +pub enum TransactionEra { + /// Transaction is immortal. + Immortal, + /// Transaction is valid for a given number of blocks, starting from given block. + Mortal(HeaderId, u32), +} + +impl, BlockHash: Copy> + TransactionEra +{ + /// Prepare transaction era, based on mortality period and current best block number. + pub fn new( + best_block_id: HeaderId, + mortality_period: Option, + ) -> Self { + mortality_period + .map(|mortality_period| TransactionEra::Mortal(best_block_id, mortality_period)) + .unwrap_or(TransactionEra::Immortal) + } + + /// Create new immortal transaction era. + pub fn immortal() -> Self { + TransactionEra::Immortal + } + + /// Returns mortality period if transaction is mortal. + pub fn mortality_period(&self) -> Option { + match *self { + TransactionEra::Immortal => None, + TransactionEra::Mortal(_, period) => Some(period), + } + } + + /// Returns era that is used by FRAME-based runtimes. + pub fn frame_era(&self) -> sp_runtime::generic::Era { + match *self { + TransactionEra::Immortal => sp_runtime::generic::Era::immortal(), + // `unique_saturated_into` is fine here - mortality `u64::MAX` is not something we + // expect to see on any chain + TransactionEra::Mortal(header_id, period) => + sp_runtime::generic::Era::mortal(period as _, header_id.0.unique_saturated_into()), + } + } + + /// Returns header hash that needs to be included in the signature payload. + pub fn signed_payload(&self, genesis_hash: BlockHash) -> BlockHash { + match *self { + TransactionEra::Immortal => genesis_hash, + TransactionEra::Mortal(header_id, _) => header_id.1, + } + } +} + +/// This is a copy of the +/// `frame_support::storage::generator::StorageMap::storage_map_final_key` for maps based +/// on selected hasher. +/// +/// We're using it because to call `storage_map_final_key` directly, we need access to the runtime +/// and pallet instance, which (sometimes) is impossible. +pub fn storage_map_final_key( + pallet_prefix: &str, + map_name: &str, + key: &[u8], +) -> StorageKey { + let key_hashed = H::hash(key); + let pallet_prefix_hashed = frame_support::Twox128::hash(pallet_prefix.as_bytes()); + let storage_prefix_hashed = frame_support::Twox128::hash(map_name.as_bytes()); + + let mut final_key = Vec::with_capacity( + pallet_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.as_ref().len(), + ); + + final_key.extend_from_slice(&pallet_prefix_hashed[..]); + final_key.extend_from_slice(&storage_prefix_hashed[..]); + final_key.extend_from_slice(key_hashed.as_ref()); + + StorageKey(final_key) +} + +/// This is how a storage key of storage parameter (`parameter_types! { storage Param: bool = false; +/// }`) is computed. +/// +/// Copied from `frame_support::parameter_types` macro. +pub fn storage_parameter_key(parameter_name: &str) -> StorageKey { + let mut buffer = Vec::with_capacity(1 + parameter_name.len() + 1); + buffer.push(b':'); + buffer.extend_from_slice(parameter_name.as_bytes()); + buffer.push(b':'); + StorageKey(sp_io::hashing::twox_128(&buffer).to_vec()) +} + +/// This is how a storage key of storage value is computed. +/// +/// Copied from `frame_support::storage::storage_prefix`. +pub fn storage_value_key(pallet_prefix: &str, value_name: &str) -> StorageKey { + let pallet_hash = sp_io::hashing::twox_128(pallet_prefix.as_bytes()); + let storage_hash = sp_io::hashing::twox_128(value_name.as_bytes()); + + let mut final_key = vec![0u8; 32]; + final_key[..16].copy_from_slice(&pallet_hash); + final_key[16..].copy_from_slice(&storage_hash); + + StorageKey(final_key) +} + +/// Can be use to access the runtime storage key of a `StorageMap`. +pub trait StorageMapKeyProvider { + /// The name of the variable that holds the `StorageMap`. + const MAP_NAME: &'static str; + + /// The same as `StorageMap::Hasher1`. + type Hasher: StorageHasher; + /// The same as `StorageMap::Key1`. + type Key: FullCodec; + /// The same as `StorageMap::Value`. + type Value: FullCodec; + + /// This is a copy of the + /// `frame_support::storage::generator::StorageMap::storage_map_final_key`. + /// + /// We're using it because to call `storage_map_final_key` directly, we need access + /// to the runtime and pallet instance, which (sometimes) is impossible. + fn final_key(pallet_prefix: &str, key: &Self::Key) -> StorageKey { + storage_map_final_key::(pallet_prefix, Self::MAP_NAME, &key.encode()) + } +} + +/// Can be use to access the runtime storage key of a `StorageDoubleMap`. +pub trait StorageDoubleMapKeyProvider { + /// The name of the variable that holds the `StorageDoubleMap`. + const MAP_NAME: &'static str; + + /// The same as `StorageDoubleMap::Hasher1`. + type Hasher1: StorageHasher; + /// The same as `StorageDoubleMap::Key1`. + type Key1: FullCodec; + /// The same as `StorageDoubleMap::Hasher2`. + type Hasher2: StorageHasher; + /// The same as `StorageDoubleMap::Key2`. + type Key2: FullCodec; + /// The same as `StorageDoubleMap::Value`. + type Value: FullCodec; + + /// This is a copy of the + /// `frame_support::storage::generator::StorageDoubleMap::storage_double_map_final_key`. + /// + /// We're using it because to call `storage_double_map_final_key` directly, we need access + /// to the runtime and pallet instance, which (sometimes) is impossible. + fn final_key(pallet_prefix: &str, key1: &Self::Key1, key2: &Self::Key2) -> StorageKey { + let key1_hashed = Self::Hasher1::hash(&key1.encode()); + let key2_hashed = Self::Hasher2::hash(&key2.encode()); + let pallet_prefix_hashed = frame_support::Twox128::hash(pallet_prefix.as_bytes()); + let storage_prefix_hashed = frame_support::Twox128::hash(Self::MAP_NAME.as_bytes()); + + let mut final_key = Vec::with_capacity( + pallet_prefix_hashed.len() + + storage_prefix_hashed.len() + + key1_hashed.as_ref().len() + + key2_hashed.as_ref().len(), + ); + + final_key.extend_from_slice(&pallet_prefix_hashed[..]); + final_key.extend_from_slice(&storage_prefix_hashed[..]); + final_key.extend_from_slice(key1_hashed.as_ref()); + final_key.extend_from_slice(key2_hashed.as_ref()); + + StorageKey(final_key) + } +} + +/// Error generated by the `OwnedBridgeModule` trait. +#[derive(Encode, Decode, TypeInfo, PalletError)] +pub enum OwnedBridgeModuleError { + /// All pallet operations are halted. + Halted, +} + +/// Operating mode for a bridge module. +pub trait OperatingMode: Send + Copy + Debug + FullCodec { + // Returns true if the bridge module is halted. + fn is_halted(&self) -> bool; +} + +/// Basic operating modes for a bridges module (Normal/Halted). +#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub enum BasicOperatingMode { + /// Normal mode, when all operations are allowed. + Normal, + /// The pallet is halted. All operations (except operating mode change) are prohibited. + Halted, +} + +impl Default for BasicOperatingMode { + fn default() -> Self { + Self::Normal + } +} + +impl OperatingMode for BasicOperatingMode { + fn is_halted(&self) -> bool { + *self == BasicOperatingMode::Halted + } +} + +/// Bridge module that has owner and operating mode +pub trait OwnedBridgeModule { + /// The target that will be used when publishing logs related to this module. + const LOG_TARGET: &'static str; + + type OwnerStorage: StorageValue>; + type OperatingMode: OperatingMode; + type OperatingModeStorage: StorageValue; + + /// Check if the module is halted. + fn is_halted() -> bool { + Self::OperatingModeStorage::get().is_halted() + } + + /// Ensure that the origin is either root, or `PalletOwner`. + fn ensure_owner_or_root(origin: T::RuntimeOrigin) -> Result<(), BadOrigin> { + match origin.into() { + Ok(RawOrigin::Root) => Ok(()), + Ok(RawOrigin::Signed(ref signer)) + if Self::OwnerStorage::get().as_ref() == Some(signer) => + Ok(()), + _ => Err(BadOrigin), + } + } + + /// Ensure that the module is not halted. + fn ensure_not_halted() -> Result<(), OwnedBridgeModuleError> { + match Self::is_halted() { + true => Err(OwnedBridgeModuleError::Halted), + false => Ok(()), + } + } + + /// Change the owner of the module. + fn set_owner(origin: T::RuntimeOrigin, maybe_owner: Option) -> DispatchResult { + Self::ensure_owner_or_root(origin)?; + match maybe_owner { + Some(owner) => { + Self::OwnerStorage::put(&owner); + log::info!(target: Self::LOG_TARGET, "Setting pallet Owner to: {:?}", owner); + }, + None => { + Self::OwnerStorage::kill(); + log::info!(target: Self::LOG_TARGET, "Removed Owner of pallet."); + }, + } + + Ok(()) + } + + /// Halt or resume all/some module operations. + fn set_operating_mode( + origin: T::RuntimeOrigin, + operating_mode: Self::OperatingMode, + ) -> DispatchResult { + Self::ensure_owner_or_root(origin)?; + Self::OperatingModeStorage::put(operating_mode); + log::info!(target: Self::LOG_TARGET, "Setting operating mode to {:?}.", operating_mode); + Ok(()) + } +} + +/// All extra operations with weights that we need in bridges. +pub trait WeightExtraOps { + /// Checked division of individual components of two weights. + /// + /// Divides components and returns minimal division result. Returns `None` if one + /// of `other` weight components is zero. + fn min_components_checked_div(&self, other: Weight) -> Option; +} + +impl WeightExtraOps for Weight { + fn min_components_checked_div(&self, other: Weight) -> Option { + Some(sp_std::cmp::min( + self.ref_time().checked_div(other.ref_time())?, + self.proof_size().checked_div(other.proof_size())?, + )) + } +} + +/// Trait that provides a static `str`. +pub trait StaticStrProvider { + const STR: &'static str; +} + +#[macro_export] +macro_rules! generate_static_str_provider { + ($str:expr) => { + $crate::paste::item! { + pub struct []; + + impl $crate::StaticStrProvider for [] { + const STR: &'static str = stringify!($str); + } + } + }; +} + +#[derive(Encode, Decode, Clone, Eq, PartialEq, PalletError, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct StrippableError { + _phantom_data: sp_std::marker::PhantomData, + #[codec(skip)] + #[cfg(feature = "std")] + message: String, +} + +impl From for StrippableError { + fn from(_err: T) -> Self { + Self { + _phantom_data: Default::default(), + #[cfg(feature = "std")] + message: format!("{:?}", _err), + } + } +} + +impl Debug for StrippableError { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + f.write_str(&self.message) + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + f.write_str("Stripped error") + } +} + +/// A trait defining helper methods for `RangeInclusive` (start..=end) +pub trait RangeInclusiveExt { + /// Computes the length of the `RangeInclusive`, checking for underflow and overflow. + fn checked_len(&self) -> Option; +} + +impl RangeInclusiveExt for RangeInclusive +where + Idx: CheckedSub + CheckedAdd + One, +{ + fn checked_len(&self) -> Option { + self.end() + .checked_sub(self.start()) + .and_then(|len| len.checked_add(&Idx::one())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn storage_parameter_key_works() { + assert_eq!( + storage_parameter_key("MillauToRialtoConversionRate"), + StorageKey(hex_literal::hex!("58942375551bb0af1682f72786b59d04").to_vec()), + ); + } + + #[test] + fn storage_value_key_works() { + assert_eq!( + storage_value_key("PalletTransactionPayment", "NextFeeMultiplier"), + StorageKey( + hex_literal::hex!( + "f0e954dfcca51a255ab12c60c789256a3f2edf3bdf381debe331ab7446addfdc" + ) + .to_vec() + ), + ); + } + + #[test] + fn generate_static_str_provider_works() { + generate_static_str_provider!(Test); + assert_eq!(StrTest::STR, "Test"); + } +} diff --git a/primitives/runtime/src/messages.rs b/primitives/runtime/src/messages.rs new file mode 100644 index 00000000000..9f7c8ab5ca4 --- /dev/null +++ b/primitives/runtime/src/messages.rs @@ -0,0 +1,35 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives that may be used by different message delivery and dispatch mechanisms. + +use codec::{Decode, Encode}; +use frame_support::{weights::Weight, RuntimeDebug}; +use scale_info::TypeInfo; + +/// Message dispatch result. +#[derive(Encode, Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] +pub struct MessageDispatchResult { + /// Unspent dispatch weight. This weight that will be deducted from total delivery transaction + /// weight, thus reducing the transaction cost. This shall not be zero in (at least) two cases: + /// + /// 1) if message has been dispatched successfully, but post-dispatch weight is less than + /// the weight, declared by the message sender; + /// 2) if message has not been dispatched at all. + pub unspent_weight: Weight, + /// Fine-grained result of single message dispatch (for better diagnostic purposes) + pub dispatch_level_result: DispatchLevelResult, +} diff --git a/primitives/runtime/src/storage_proof.rs b/primitives/runtime/src/storage_proof.rs new file mode 100644 index 00000000000..09641376666 --- /dev/null +++ b/primitives/runtime/src/storage_proof.rs @@ -0,0 +1,272 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Logic for checking Substrate storage proofs. + +use crate::StrippableError; +use codec::{Decode, Encode}; +use frame_support::PalletError; +use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; +use scale_info::TypeInfo; +use sp_std::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec}; +use sp_trie::{ + read_trie_value, LayoutV1, MemoryDB, Recorder, StorageProof, Trie, TrieConfiguration, + TrieDBBuilder, TrieError, TrieHash, +}; + +/// Raw storage proof type (just raw trie nodes). +pub type RawStorageProof = Vec>; + +/// Storage proof size requirements. +/// +/// This is currently used by benchmarks when generating storage proofs. +#[derive(Clone, Copy, Debug)] +pub enum ProofSize { + /// The proof is expected to be minimal. If value size may be changed, then it is expected to + /// have given size. + Minimal(u32), + /// The proof is expected to have at least given size and grow by increasing value that is + /// stored in the trie. + HasLargeLeaf(u32), +} + +/// This struct is used to read storage values from a subset of a Merklized database. The "proof" +/// is a subset of the nodes in the Merkle structure of the database, so that it provides +/// authentication against a known Merkle root as well as the values in the +/// database themselves. +pub struct StorageProofChecker +where + H: Hasher, +{ + proof_nodes_count: usize, + root: H::Out, + db: MemoryDB, + recorder: Recorder>, +} + +impl StorageProofChecker +where + H: Hasher, +{ + /// Constructs a new storage proof checker. + /// + /// This returns an error if the given proof is invalid with respect to the given root. + pub fn new(root: H::Out, proof: RawStorageProof) -> Result { + // 1. we don't want extra items in the storage proof + // 2. `StorageProof` is storing all trie nodes in the `BTreeSet` + // + // => someone could simply add duplicate items to the proof and we won't be + // able to detect that by just using `StorageProof` + // + // => let's check it when we are converting our "raw proof" into `StorageProof` + let proof_nodes_count = proof.len(); + let proof = StorageProof::new(proof); + if proof_nodes_count != proof.iter_nodes().count() { + return Err(Error::DuplicateNodesInProof) + } + + let db = proof.into_memory_db(); + if !db.contains(&root, EMPTY_PREFIX) { + return Err(Error::StorageRootMismatch) + } + + let recorder = Recorder::default(); + let checker = StorageProofChecker { proof_nodes_count, root, db, recorder }; + Ok(checker) + } + + /// Returns error if the proof has some nodes that are left intact by previous `read_value` + /// calls. + pub fn ensure_no_unused_nodes(mut self) -> Result<(), Error> { + let visited_nodes = self + .recorder + .drain() + .into_iter() + .map(|record| record.data) + .collect::>(); + let visited_nodes_count = visited_nodes.len(); + if self.proof_nodes_count == visited_nodes_count { + Ok(()) + } else { + Err(Error::UnusedNodesInTheProof) + } + } + + /// Reads a value from the available subset of storage. If the value cannot be read due to an + /// incomplete or otherwise invalid proof, this function returns an error. + pub fn read_value(&mut self, key: &[u8]) -> Result>, Error> { + // LayoutV1 or LayoutV0 is identical for proof that only read values. + read_trie_value::, _>(&self.db, &self.root, key, Some(&mut self.recorder), None) + .map_err(|_| Error::StorageValueUnavailable) + } + + /// Reads and decodes a value from the available subset of storage. If the value cannot be read + /// due to an incomplete or otherwise invalid proof, this function returns an error. If value is + /// read, but decoding fails, this function returns an error. + pub fn read_and_decode_value(&mut self, key: &[u8]) -> Result, Error> { + self.read_value(key).and_then(|v| { + v.map(|v| T::decode(&mut &v[..]).map_err(|e| Error::StorageValueDecodeFailed(e.into()))) + .transpose() + }) + } + + /// Reads and decodes a value from the available subset of storage. If the value cannot be read + /// due to an incomplete or otherwise invalid proof, or if the value is `None`, this function + /// returns an error. If value is read, but decoding fails, this function returns an error. + pub fn read_and_decode_mandatory_value(&mut self, key: &[u8]) -> Result { + self.read_and_decode_value(key)?.ok_or(Error::StorageValueEmpty) + } + + /// Reads and decodes a value from the available subset of storage. If the value cannot be read + /// due to an incomplete or otherwise invalid proof, this function returns `Ok(None)`. + /// If value is read, but decoding fails, this function returns an error. + pub fn read_and_decode_opt_value(&mut self, key: &[u8]) -> Result, Error> { + match self.read_and_decode_value(key) { + Ok(outbound_lane_data) => Ok(outbound_lane_data), + Err(Error::StorageValueUnavailable) => Ok(None), + Err(e) => Err(e), + } + } +} + +/// Storage proof related errors. +#[derive(Encode, Decode, Clone, Eq, PartialEq, PalletError, Debug, TypeInfo)] +pub enum Error { + /// Duplicate trie nodes are found in the proof. + DuplicateNodesInProof, + /// Unused trie nodes are found in the proof. + UnusedNodesInTheProof, + /// Expected storage root is missing from the proof. + StorageRootMismatch, + /// Unable to reach expected storage value using provided trie nodes. + StorageValueUnavailable, + /// The storage value is `None`. + StorageValueEmpty, + /// Failed to decode storage value. + StorageValueDecodeFailed(StrippableError), +} + +/// Return valid storage proof and state root. +/// +/// NOTE: This should only be used for **testing**. +#[cfg(feature = "std")] +pub fn craft_valid_storage_proof() -> (sp_core::H256, RawStorageProof) { + use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend}; + + let state_version = sp_runtime::StateVersion::default(); + + // construct storage proof + let backend = >::from(( + vec![ + (None, vec![(b"key1".to_vec(), Some(b"value1".to_vec()))]), + (None, vec![(b"key2".to_vec(), Some(b"value2".to_vec()))]), + (None, vec![(b"key3".to_vec(), Some(b"value3".to_vec()))]), + (None, vec![(b"key4".to_vec(), Some((42u64, 42u32, 42u16, 42u8).encode()))]), + // Value is too big to fit in a branch node + (None, vec![(b"key11".to_vec(), Some(vec![0u8; 32]))]), + ], + state_version, + )); + let root = backend.storage_root(std::iter::empty(), state_version).0; + let proof = + prove_read(backend, &[&b"key1"[..], &b"key2"[..], &b"key4"[..], &b"key22"[..]]).unwrap(); + + (root, proof.into_nodes().into_iter().collect()) +} + +/// Record all keys for a given root. +pub fn record_all_keys( + db: &DB, + root: &TrieHash, +) -> Result>> +where + DB: hash_db::HashDBRef, +{ + let mut recorder = Recorder::::new(); + let trie = TrieDBBuilder::::new(db, root).with_recorder(&mut recorder).build(); + for x in trie.iter()? { + let (key, _) = x?; + trie.get(&key)?; + } + + // recorder may record the same trie node multiple times and we don't want duplicate nodes + // in our proofs => let's deduplicate it by collecting to the BTreeSet first + Ok(recorder + .drain() + .into_iter() + .map(|n| n.data.to_vec()) + .collect::>() + .into_iter() + .collect()) +} + +#[cfg(test)] +pub mod tests { + use super::*; + use codec::Encode; + + #[test] + fn storage_proof_check() { + let (root, proof) = craft_valid_storage_proof(); + + // check proof in runtime + let mut checker = + >::new(root, proof.clone()).unwrap(); + assert_eq!(checker.read_value(b"key1"), Ok(Some(b"value1".to_vec()))); + assert_eq!(checker.read_value(b"key2"), Ok(Some(b"value2".to_vec()))); + assert_eq!(checker.read_value(b"key4"), Ok(Some((42u64, 42u32, 42u16, 42u8).encode()))); + assert_eq!(checker.read_value(b"key11111"), Err(Error::StorageValueUnavailable)); + assert_eq!(checker.read_value(b"key22"), Ok(None)); + assert_eq!(checker.read_and_decode_value(b"key4"), Ok(Some((42u64, 42u32, 42u16, 42u8))),); + assert!(matches!( + checker.read_and_decode_value::<[u8; 64]>(b"key4"), + Err(Error::StorageValueDecodeFailed(_)), + )); + + // checking proof against invalid commitment fails + assert_eq!( + >::new(sp_core::H256::random(), proof).err(), + Some(Error::StorageRootMismatch) + ); + } + + #[test] + fn proof_with_duplicate_items_is_rejected() { + let (root, mut proof) = craft_valid_storage_proof(); + proof.push(proof.first().unwrap().clone()); + + assert_eq!( + StorageProofChecker::::new(root, proof).map(drop), + Err(Error::DuplicateNodesInProof), + ); + } + + #[test] + fn proof_with_unused_items_is_rejected() { + let (root, proof) = craft_valid_storage_proof(); + + let mut checker = + StorageProofChecker::::new(root, proof.clone()).unwrap(); + checker.read_value(b"key1").unwrap(); + checker.read_value(b"key2").unwrap(); + checker.read_value(b"key4").unwrap(); + checker.read_value(b"key22").unwrap(); + assert_eq!(checker.ensure_no_unused_nodes(), Ok(())); + + let checker = StorageProofChecker::::new(root, proof).unwrap(); + assert_eq!(checker.ensure_no_unused_nodes(), Err(Error::UnusedNodesInTheProof)); + } +} diff --git a/primitives/runtime/src/storage_types.rs b/primitives/runtime/src/storage_types.rs new file mode 100644 index 00000000000..b37f779d00b --- /dev/null +++ b/primitives/runtime/src/storage_types.rs @@ -0,0 +1,90 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Wrapper for a runtime storage value that checks if value exceeds given maximum +//! during conversion. + +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{traits::Get, RuntimeDebug}; +use scale_info::{Type, TypeInfo}; +use sp_std::{marker::PhantomData, ops::Deref}; + +/// Error that is returned when the value size exceeds maximal configured size. +#[derive(RuntimeDebug)] +pub struct MaximalSizeExceededError { + /// Size of the value. + pub value_size: usize, + /// Maximal configured size. + pub maximal_size: usize, +} + +/// A bounded runtime storage value. +#[derive(Clone, Decode, Encode, Eq, PartialEq)] +pub struct BoundedStorageValue { + value: V, + _phantom: PhantomData, +} + +impl sp_std::fmt::Debug for BoundedStorageValue { + fn fmt(&self, fmt: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + self.value.fmt(fmt) + } +} + +impl, V: Encode> BoundedStorageValue { + /// Construct `BoundedStorageValue` from the underlying `value` with all required checks. + /// + /// Returns error if value size exceeds given bounds. + pub fn try_from_inner(value: V) -> Result { + // this conversion is heavy (since we do encoding here), so we may want to optimize it later + // (e.g. by introducing custom Encode implementation, and turning `BoundedStorageValue` into + // `enum BoundedStorageValue { Decoded(V), Encoded(Vec) }`) + let value_size = value.encoded_size(); + let maximal_size = B::get() as usize; + if value_size > maximal_size { + Err(MaximalSizeExceededError { value_size, maximal_size }) + } else { + Ok(BoundedStorageValue { value, _phantom: Default::default() }) + } + } + + /// Convert into the inner type + pub fn into_inner(self) -> V { + self.value + } +} + +impl Deref for BoundedStorageValue { + type Target = V; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + +impl TypeInfo for BoundedStorageValue { + type Identity = Self; + + fn type_info() -> Type { + V::type_info() + } +} + +impl, V: Encode> MaxEncodedLen for BoundedStorageValue { + fn max_encoded_len() -> usize { + B::get() as usize + } +} diff --git a/primitives/test-utils/Cargo.toml b/primitives/test-utils/Cargo.toml new file mode 100644 index 00000000000..5ed835857d1 --- /dev/null +++ b/primitives/test-utils/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "bp-test-utils" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +bp-header-chain = { path = "../header-chain", default-features = false } +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } +ed25519-dalek = { version = "1.0", default-features = false, features = ["u64_backend"] } +finality-grandpa = { version = "0.16.2", default-features = false } +sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "codec/std", + "ed25519-dalek/std", + "finality-grandpa/std", + "sp-application-crypto/std", + "sp-consensus-grandpa/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/primitives/test-utils/src/keyring.rs b/primitives/test-utils/src/keyring.rs new file mode 100644 index 00000000000..b1782109668 --- /dev/null +++ b/primitives/test-utils/src/keyring.rs @@ -0,0 +1,94 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Utilities for working with test accounts. + +use codec::Encode; +use ed25519_dalek::{Keypair, PublicKey, SecretKey, Signature}; +use finality_grandpa::voter_set::VoterSet; +use sp_consensus_grandpa::{AuthorityId, AuthorityList, AuthorityWeight}; +use sp_runtime::RuntimeDebug; +use sp_std::prelude::*; + +/// Set of test accounts with friendly names. +pub const ALICE: Account = Account(0); +pub const BOB: Account = Account(1); +pub const CHARLIE: Account = Account(2); +pub const DAVE: Account = Account(3); +pub const EVE: Account = Account(4); +pub const FERDIE: Account = Account(5); + +/// A test account which can be used to sign messages. +#[derive(RuntimeDebug, Clone, Copy)] +pub struct Account(pub u16); + +impl Account { + pub fn public(&self) -> PublicKey { + (&self.secret()).into() + } + + pub fn secret(&self) -> SecretKey { + let data = self.0.encode(); + let mut bytes = [0_u8; 32]; + bytes[0..data.len()].copy_from_slice(&data); + SecretKey::from_bytes(&bytes) + .expect("A static array of the correct length is a known good.") + } + + pub fn pair(&self) -> Keypair { + let mut pair: [u8; 64] = [0; 64]; + + let secret = self.secret(); + pair[..32].copy_from_slice(&secret.to_bytes()); + + let public = self.public(); + pair[32..].copy_from_slice(&public.to_bytes()); + + Keypair::from_bytes(&pair) + .expect("We expect the SecretKey to be good, so this must also be good.") + } + + pub fn sign(&self, msg: &[u8]) -> Signature { + use ed25519_dalek::Signer; + self.pair().sign(msg) + } +} + +impl From for AuthorityId { + fn from(p: Account) -> Self { + sp_application_crypto::UncheckedFrom::unchecked_from(p.public().to_bytes()) + } +} + +/// Get a valid set of voters for a Grandpa round. +pub fn voter_set() -> VoterSet { + VoterSet::new(authority_list()).unwrap() +} + +/// Convenience function to get a list of Grandpa authorities. +pub fn authority_list() -> AuthorityList { + test_keyring().iter().map(|(id, w)| (AuthorityId::from(*id), *w)).collect() +} + +/// Get the corresponding identities from the keyring for the "standard" authority set. +pub fn test_keyring() -> Vec<(Account, AuthorityWeight)> { + vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1)] +} + +/// Get a list of "unique" accounts. +pub fn accounts(len: u16) -> Vec { + (0..len).map(Account).collect() +} diff --git a/primitives/test-utils/src/lib.rs b/primitives/test-utils/src/lib.rs new file mode 100644 index 00000000000..6bb4adbf450 --- /dev/null +++ b/primitives/test-utils/src/lib.rs @@ -0,0 +1,302 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Utilities for testing runtime code. + +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_header_chain::justification::{required_justification_precommits, GrandpaJustification}; +use codec::Encode; +use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, AuthorityWeight, SetId}; +use sp_runtime::traits::{Header as HeaderT, One, Zero}; +use sp_std::prelude::*; + +// Re-export all our test account utilities +pub use keyring::*; + +mod keyring; + +pub const TEST_GRANDPA_ROUND: u64 = 1; +pub const TEST_GRANDPA_SET_ID: SetId = 1; + +/// Configuration parameters when generating test GRANDPA justifications. +#[derive(Clone)] +pub struct JustificationGeneratorParams { + /// The header which we want to finalize. + pub header: H, + /// The GRANDPA round number for the current authority set. + pub round: u64, + /// The current authority set ID. + pub set_id: SetId, + /// The current GRANDPA authority set. + /// + /// The size of the set will determine the number of pre-commits in our justification. + pub authorities: Vec<(Account, AuthorityWeight)>, + /// The total number of precommit ancestors in the `votes_ancestries` field our justification. + /// + /// These may be distributed among many forks. + pub ancestors: u32, + /// The number of forks. + /// + /// Useful for creating a "worst-case" scenario in which each authority is on its own fork. + pub forks: u32, +} + +impl Default for JustificationGeneratorParams { + fn default() -> Self { + let required_signatures = required_justification_precommits(test_keyring().len() as _); + Self { + header: test_header(One::one()), + round: TEST_GRANDPA_ROUND, + set_id: TEST_GRANDPA_SET_ID, + authorities: test_keyring().into_iter().take(required_signatures as _).collect(), + ancestors: 2, + forks: 1, + } + } +} + +/// Make a valid GRANDPA justification with sensible defaults +pub fn make_default_justification(header: &H) -> GrandpaJustification { + let params = JustificationGeneratorParams:: { header: header.clone(), ..Default::default() }; + + make_justification_for_header(params) +} + +/// Generate justifications in a way where we are able to tune the number of pre-commits +/// and vote ancestries which are included in the justification. +/// +/// This is useful for benchmarkings where we want to generate valid justifications with +/// a specific number of pre-commits (tuned with the number of "authorities") and/or a specific +/// number of vote ancestries (tuned with the "votes" parameter). +/// +/// Note: This needs at least three authorities or else the verifier will complain about +/// being given an invalid commit. +pub fn make_justification_for_header( + params: JustificationGeneratorParams, +) -> GrandpaJustification { + let JustificationGeneratorParams { header, round, set_id, authorities, mut ancestors, forks } = + params; + let (target_hash, target_number) = (header.hash(), *header.number()); + let mut votes_ancestries = vec![]; + let mut precommits = vec![]; + + assert!(forks != 0, "Need at least one fork to have a chain.."); + assert!( + forks as usize <= authorities.len(), + "If we have more forks than authorities we can't create valid pre-commits for all the forks." + ); + + // Roughly, how many vote ancestries do we want per fork + let target_depth = (ancestors + forks - 1) / forks; + + let mut unsigned_precommits = vec![]; + for i in 0..forks { + let depth = if ancestors >= target_depth { + ancestors -= target_depth; + target_depth + } else { + ancestors + }; + + // Note: Adding 1 to account for the target header + let chain = generate_chain(i, depth + 1, &header); + + // We don't include our finality target header in the vote ancestries + for child in &chain[1..] { + votes_ancestries.push(child.clone()); + } + + // The header we need to use when pre-commiting is the one at the highest height + // on our chain. + let precommit_candidate = chain.last().map(|h| (h.hash(), *h.number())).unwrap(); + unsigned_precommits.push(precommit_candidate); + } + + for (i, (id, _weight)) in authorities.iter().enumerate() { + // Assign authorities to sign pre-commits in a round-robin fashion + let target = unsigned_precommits[i % forks as usize]; + let precommit = signed_precommit::(id, target, round, set_id); + + precommits.push(precommit); + } + + GrandpaJustification { + round, + commit: finality_grandpa::Commit { target_hash, target_number, precommits }, + votes_ancestries, + } +} + +fn generate_chain(fork_id: u32, depth: u32, ancestor: &H) -> Vec { + let mut headers = vec![ancestor.clone()]; + + for i in 1..depth { + let parent = &headers[(i - 1) as usize]; + let (hash, num) = (parent.hash(), *parent.number()); + + let mut header = test_header::(num + One::one()); + header.set_parent_hash(hash); + + // Modifying the digest so headers at the same height but in different forks have different + // hashes + header.digest_mut().logs.push(sp_runtime::DigestItem::Other(fork_id.encode())); + + headers.push(header); + } + + headers +} + +/// Create signed precommit with given target. +pub fn signed_precommit( + signer: &Account, + target: (H::Hash, H::Number), + round: u64, + set_id: SetId, +) -> finality_grandpa::SignedPrecommit { + let precommit = finality_grandpa::Precommit { target_hash: target.0, target_number: target.1 }; + + let encoded = sp_consensus_grandpa::localized_payload( + round, + set_id, + &finality_grandpa::Message::Precommit(precommit.clone()), + ); + + let signature = signer.sign(&encoded); + let raw_signature: Vec = signature.to_bytes().into(); + + // Need to wrap our signature and id types that they match what our `SignedPrecommit` is + // expecting + let signature = AuthoritySignature::try_from(raw_signature).expect( + "We know our Keypair is good, + so our signature must also be good.", + ); + let id = (*signer).into(); + + finality_grandpa::SignedPrecommit { precommit, signature, id } +} + +/// Get a header for testing. +/// +/// The correct parent hash will be used if given a non-zero header. +pub fn test_header(number: H::Number) -> H { + let default = |num| { + H::new(num, Default::default(), Default::default(), Default::default(), Default::default()) + }; + + let mut header = default(number); + if number != Zero::zero() { + let parent_hash = default(number - One::one()).hash(); + header.set_parent_hash(parent_hash); + } + + header +} + +/// Convenience function for generating a Header ID at a given block number. +pub fn header_id(index: u8) -> (H::Hash, H::Number) { + (test_header::(index.into()).hash(), index.into()) +} + +#[macro_export] +/// Adds methods for testing the `set_owner()` and `set_operating_mode()` for a pallet. +/// Some values are hardcoded like: +/// - `run_test()` +/// - `Pallet::` +/// - `PalletOwner::` +/// - `PalletOperatingMode::` +/// While this is not ideal, all the pallets use the same names, so it works for the moment. +/// We can revisit this in the future if anything changes. +macro_rules! generate_owned_bridge_module_tests { + ($normal_operating_mode: expr, $halted_operating_mode: expr) => { + #[test] + fn test_set_owner() { + run_test(|| { + PalletOwner::::put(1); + + // The root should be able to change the owner. + assert_ok!(Pallet::::set_owner(RuntimeOrigin::root(), Some(2))); + assert_eq!(PalletOwner::::get(), Some(2)); + + // The owner should be able to change the owner. + assert_ok!(Pallet::::set_owner(RuntimeOrigin::signed(2), Some(3))); + assert_eq!(PalletOwner::::get(), Some(3)); + + // Other users shouldn't be able to change the owner. + assert_noop!( + Pallet::::set_owner(RuntimeOrigin::signed(1), Some(4)), + DispatchError::BadOrigin + ); + assert_eq!(PalletOwner::::get(), Some(3)); + }); + } + + #[test] + fn test_set_operating_mode() { + run_test(|| { + PalletOwner::::put(1); + PalletOperatingMode::::put($normal_operating_mode); + + // The root should be able to halt the pallet. + assert_ok!(Pallet::::set_operating_mode( + RuntimeOrigin::root(), + $halted_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + // The root should be able to resume the pallet. + assert_ok!(Pallet::::set_operating_mode( + RuntimeOrigin::root(), + $normal_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + + // The owner should be able to halt the pallet. + assert_ok!(Pallet::::set_operating_mode( + RuntimeOrigin::signed(1), + $halted_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + // The owner should be able to resume the pallet. + assert_ok!(Pallet::::set_operating_mode( + RuntimeOrigin::signed(1), + $normal_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + + // Other users shouldn't be able to halt the pallet. + assert_noop!( + Pallet::::set_operating_mode( + RuntimeOrigin::signed(2), + $halted_operating_mode + ), + DispatchError::BadOrigin + ); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + // Other users shouldn't be able to resume the pallet. + PalletOperatingMode::::put($halted_operating_mode); + assert_noop!( + Pallet::::set_operating_mode( + RuntimeOrigin::signed(2), + $normal_operating_mode + ), + DispatchError::BadOrigin + ); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + }); + } + }; +} diff --git a/relays/bin-substrate/Cargo.toml b/relays/bin-substrate/Cargo.toml new file mode 100644 index 00000000000..7853b9cb599 --- /dev/null +++ b/relays/bin-substrate/Cargo.toml @@ -0,0 +1,73 @@ +[package] +name = "substrate-relay" +version = "1.0.1" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +anyhow = "1.0" +async-std = "1.9.0" +async-trait = "0.1" +codec = { package = "parity-scale-codec", version = "3.1.5" } +futures = "0.3.28" +hex = "0.4" +log = "0.4.17" +num-format = "0.4" +num-traits = "0.2" +rbtag = "0.3" +structopt = "0.3" +signal-hook = "0.3.15" +signal-hook-async-std = "0.2.2" +strum = { version = "0.24.1", features = ["derive"] } + +# Bridge dependencies +bp-header-chain = { path = "../../primitives/header-chain" } +bp-messages = { path = "../../primitives/messages" } +bp-parachains = { path = "../../primitives/parachains" } +bp-millau = { path = "../../primitives/chain-millau" } +bp-polkadot-core = { path = "../../primitives/polkadot-core" } +bp-rialto = { path = "../../primitives/chain-rialto" } +bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" } +bp-runtime = { path = "../../primitives/runtime" } +bridge-runtime-common = { path = "../../bin/runtime-common" } +millau-runtime = { path = "../../bin/millau/runtime" } +pallet-bridge-parachains = { path = "../../modules/parachains" } +parachains-relay = { path = "../parachains" } +relay-millau-client = { path = "../client-millau" } +relay-rialto-client = { path = "../client-rialto" } +relay-rialto-parachain-client = { path = "../client-rialto-parachain" } +relay-bridge-hub-kusama-client = { path = "../client-bridge-hub-kusama" } +relay-bridge-hub-polkadot-client = { path = "../client-bridge-hub-polkadot" } +relay-bridge-hub-rococo-client = { path = "../client-bridge-hub-rococo" } +relay-bridge-hub-wococo-client = { path = "../client-bridge-hub-wococo" } +relay-kusama-client = { path = "../client-kusama" } +relay-polkadot-client = { path = "../client-polkadot" } +relay-rococo-client = { path = "../client-rococo" } +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } +relay-westend-client = { path = "../client-westend" } +relay-wococo-client = { path = "../client-wococo" } +rialto-runtime = { path = "../../bin/rialto/runtime" } +substrate-relay-helper = { path = "../lib-substrate-relay" } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } + +# Polkadot Dependencies +polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "master" } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + + +[dev-dependencies] +bp-test-utils = { path = "../../primitives/test-utils" } +hex-literal = "0.4" +sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } +tempfile = "3.5" +finality-grandpa = { version = "0.16.2" } diff --git a/relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_kusama_messages_to_bridge_hub_polkadot.rs b/relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_kusama_messages_to_bridge_hub_polkadot.rs new file mode 100644 index 00000000000..9abec22b981 --- /dev/null +++ b/relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_kusama_messages_to_bridge_hub_polkadot.rs @@ -0,0 +1,65 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! BridgeHubKusama-to-BridgeHubPolkadot messages sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge}; +use relay_bridge_hub_kusama_client::BridgeHubKusama; +use relay_bridge_hub_polkadot_client::BridgeHubPolkadot; +use substrate_relay_helper::{messages_lane::SubstrateMessageLane, UtilityPalletBatchCallBuilder}; + +/// BridgeHubKusama-to-BridgeHubPolkadot messages bridge. +pub struct BridgeHubKusamaToBridgeHubPolkadotMessagesCliBridge {} + +impl CliBridgeBase for BridgeHubKusamaToBridgeHubPolkadotMessagesCliBridge { + type Source = BridgeHubKusama; + type Target = BridgeHubPolkadot; +} + +impl MessagesCliBridge for BridgeHubKusamaToBridgeHubPolkadotMessagesCliBridge { + type MessagesLane = BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLane; +} + +substrate_relay_helper::generate_receive_message_proof_call_builder!( + BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLane, + BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLaneReceiveMessagesProofCallBuilder, + relay_bridge_hub_polkadot_client::runtime::Call::BridgeKusamaMessages, + relay_bridge_hub_polkadot_client::runtime::BridgeKusamaMessagesCall::receive_messages_proof +); + +substrate_relay_helper::generate_receive_message_delivery_proof_call_builder!( + BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLane, + BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLaneReceiveMessagesDeliveryProofCallBuilder, + relay_bridge_hub_kusama_client::runtime::Call::BridgePolkadotMessages, + relay_bridge_hub_kusama_client::runtime::BridgePolkadotMessagesCall::receive_messages_delivery_proof +); + +/// BridgeHubKusama-to-BridgeHubPolkadot messages lane. +#[derive(Clone, Debug)] +pub struct BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLane; + +impl SubstrateMessageLane for BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLane { + type SourceChain = BridgeHubKusama; + type TargetChain = BridgeHubPolkadot; + + type ReceiveMessagesProofCallBuilder = + BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLaneReceiveMessagesProofCallBuilder; + type ReceiveMessagesDeliveryProofCallBuilder = + BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLaneReceiveMessagesDeliveryProofCallBuilder; + + type SourceBatchCallBuilder = UtilityPalletBatchCallBuilder; + type TargetBatchCallBuilder = UtilityPalletBatchCallBuilder; +} diff --git a/relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_polkadot_messages_to_bridge_hub_kusama.rs b/relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_polkadot_messages_to_bridge_hub_kusama.rs new file mode 100644 index 00000000000..191a84b27c2 --- /dev/null +++ b/relays/bin-substrate/src/bridges/kusama_polkadot/bridge_hub_polkadot_messages_to_bridge_hub_kusama.rs @@ -0,0 +1,65 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! BridgeHubPolkadot-to-BridgeHubKusama messages sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge}; +use relay_bridge_hub_kusama_client::BridgeHubKusama; +use relay_bridge_hub_polkadot_client::BridgeHubPolkadot; +use substrate_relay_helper::{messages_lane::SubstrateMessageLane, UtilityPalletBatchCallBuilder}; + +/// BridgeHubPolkadot-to-BridgeHubKusama messages bridge. +pub struct BridgeHubPolkadotToBridgeHubKusamaMessagesCliBridge {} + +impl CliBridgeBase for BridgeHubPolkadotToBridgeHubKusamaMessagesCliBridge { + type Source = BridgeHubPolkadot; + type Target = BridgeHubKusama; +} + +impl MessagesCliBridge for BridgeHubPolkadotToBridgeHubKusamaMessagesCliBridge { + type MessagesLane = BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLane; +} + +substrate_relay_helper::generate_receive_message_proof_call_builder!( + BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLane, + BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLaneReceiveMessagesProofCallBuilder, + relay_bridge_hub_kusama_client::runtime::Call::BridgePolkadotMessages, + relay_bridge_hub_kusama_client::runtime::BridgePolkadotMessagesCall::receive_messages_proof +); + +substrate_relay_helper::generate_receive_message_delivery_proof_call_builder!( + BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLane, + BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLaneReceiveMessagesDeliveryProofCallBuilder, + relay_bridge_hub_polkadot_client::runtime::Call::BridgeKusamaMessages, + relay_bridge_hub_polkadot_client::runtime::BridgeKusamaMessagesCall::receive_messages_delivery_proof +); + +/// BridgeHubPolkadot-to-BridgeHubKusama messages lane. +#[derive(Clone, Debug)] +pub struct BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLane; + +impl SubstrateMessageLane for BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLane { + type SourceChain = BridgeHubPolkadot; + type TargetChain = BridgeHubKusama; + + type ReceiveMessagesProofCallBuilder = + BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLaneReceiveMessagesProofCallBuilder; + type ReceiveMessagesDeliveryProofCallBuilder = + BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLaneReceiveMessagesDeliveryProofCallBuilder; + + type SourceBatchCallBuilder = UtilityPalletBatchCallBuilder; + type TargetBatchCallBuilder = UtilityPalletBatchCallBuilder; +} diff --git a/relays/bin-substrate/src/bridges/kusama_polkadot/kusama_headers_to_bridge_hub_polkadot.rs b/relays/bin-substrate/src/bridges/kusama_polkadot/kusama_headers_to_bridge_hub_polkadot.rs new file mode 100644 index 00000000000..1cfaf922692 --- /dev/null +++ b/relays/bin-substrate/src/bridges/kusama_polkadot/kusama_headers_to_bridge_hub_polkadot.rs @@ -0,0 +1,72 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Kusama-to-BridgeHubPolkadot headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, RelayToRelayHeadersCliBridge}; + +use async_trait::async_trait; +use relay_substrate_client::{AccountKeyPairOf, Client}; +use substrate_relay_helper::{ + finality::{engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline}, + TransactionParams, +}; + +/// Description of Kusama -> PolkadotBridgeHub finalized headers bridge. +#[derive(Clone, Debug)] +pub struct KusamaFinalityToBridgeHubPolkadot; + +substrate_relay_helper::generate_submit_finality_proof_call_builder!( + KusamaFinalityToBridgeHubPolkadot, + KusamaFinalityToBridgeHubPolkadotCallBuilder, + relay_bridge_hub_polkadot_client::runtime::Call::BridgeKusamaGrandpa, + relay_bridge_hub_polkadot_client::runtime::BridgeKusamaGrandpaCall::submit_finality_proof +); + +#[async_trait] +impl SubstrateFinalitySyncPipeline for KusamaFinalityToBridgeHubPolkadot { + type SourceChain = relay_kusama_client::Kusama; + type TargetChain = relay_bridge_hub_polkadot_client::BridgeHubPolkadot; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = KusamaFinalityToBridgeHubPolkadotCallBuilder; + + async fn start_relay_guards( + target_client: &Client, + _transaction_params: &TransactionParams>, + enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + if enable_version_guard { + relay_substrate_client::guard::abort_on_spec_version_change( + target_client.clone(), + target_client.simple_runtime_version().await?.spec_version, + ); + } + Ok(()) + } +} + +/// `Kusama` to BridgeHub `Polkadot` bridge definition. +pub struct KusamaToBridgeHubPolkadotCliBridge {} + +impl CliBridgeBase for KusamaToBridgeHubPolkadotCliBridge { + type Source = relay_kusama_client::Kusama; + type Target = relay_bridge_hub_polkadot_client::BridgeHubPolkadot; +} + +impl RelayToRelayHeadersCliBridge for KusamaToBridgeHubPolkadotCliBridge { + type Finality = KusamaFinalityToBridgeHubPolkadot; +} diff --git a/relays/bin-substrate/src/bridges/kusama_polkadot/kusama_parachains_to_bridge_hub_polkadot.rs b/relays/bin-substrate/src/bridges/kusama_polkadot/kusama_parachains_to_bridge_hub_polkadot.rs new file mode 100644 index 00000000000..e5936640cb3 --- /dev/null +++ b/relays/bin-substrate/src/bridges/kusama_polkadot/kusama_parachains_to_bridge_hub_polkadot.rs @@ -0,0 +1,75 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Kusama-to-BridgeHubPolkadot parachains sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}; +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use relay_substrate_client::{CallOf, HeaderIdOf}; +use substrate_relay_helper::parachains::{ + SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, +}; + +/// Kusama-to-BridgeHubPolkadot parachain sync description. +#[derive(Clone, Debug)] +pub struct BridgeHubKusamaToBridgeHubPolkadot; + +impl SubstrateParachainsPipeline for BridgeHubKusamaToBridgeHubPolkadot { + type SourceParachain = relay_bridge_hub_kusama_client::BridgeHubKusama; + type SourceRelayChain = relay_kusama_client::Kusama; + type TargetChain = relay_bridge_hub_polkadot_client::BridgeHubPolkadot; + + type SubmitParachainHeadsCallBuilder = BridgeHubKusamaToBridgeHubPolkadotCallBuilder; +} + +pub struct BridgeHubKusamaToBridgeHubPolkadotCallBuilder; +impl SubmitParachainHeadsCallBuilder + for BridgeHubKusamaToBridgeHubPolkadotCallBuilder +{ + fn build_submit_parachain_heads_call( + at_relay_block: HeaderIdOf, + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> CallOf { + relay_bridge_hub_polkadot_client::runtime::Call::BridgeKusamaParachain( + relay_bridge_hub_polkadot_client::runtime::BridgeParachainCall::submit_parachain_heads { + at_relay_block: (at_relay_block.0, at_relay_block.1), + parachains, + parachain_heads_proof, + }, + ) + } +} + +/// Kusama-to-BridgeHubPolkadot parachain sync description for the CLI. +pub struct BridgeHubKusamaToBridgeHubPolkadotCliBridge {} + +impl ParachainToRelayHeadersCliBridge for BridgeHubKusamaToBridgeHubPolkadotCliBridge { + type SourceRelay = relay_kusama_client::Kusama; + type ParachainFinality = BridgeHubKusamaToBridgeHubPolkadot; + type RelayFinality = + crate::bridges::kusama_polkadot::kusama_headers_to_bridge_hub_polkadot::KusamaFinalityToBridgeHubPolkadot; +} + +impl CliBridgeBase for BridgeHubKusamaToBridgeHubPolkadotCliBridge { + type Source = relay_bridge_hub_kusama_client::BridgeHubKusama; + type Target = relay_bridge_hub_polkadot_client::BridgeHubPolkadot; +} + +impl MessagesCliBridge for BridgeHubKusamaToBridgeHubPolkadotCliBridge { + type MessagesLane = + crate::bridges::kusama_polkadot::bridge_hub_kusama_messages_to_bridge_hub_polkadot::BridgeHubKusamaMessagesToBridgeHubPolkadotMessageLane; +} diff --git a/relays/bin-substrate/src/bridges/kusama_polkadot/mod.rs b/relays/bin-substrate/src/bridges/kusama_polkadot/mod.rs new file mode 100644 index 00000000000..65cd8d9ded6 --- /dev/null +++ b/relays/bin-substrate/src/bridges/kusama_polkadot/mod.rs @@ -0,0 +1,24 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Declaration of all bridges between Kusama Bridge Hub and Polkadot Bridge Hub. + +pub mod bridge_hub_kusama_messages_to_bridge_hub_polkadot; +pub mod bridge_hub_polkadot_messages_to_bridge_hub_kusama; +pub mod kusama_headers_to_bridge_hub_polkadot; +pub mod kusama_parachains_to_bridge_hub_polkadot; +pub mod polkadot_headers_to_bridge_hub_kusama; +pub mod polkadot_parachains_to_bridge_hub_kusama; diff --git a/relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_headers_to_bridge_hub_kusama.rs b/relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_headers_to_bridge_hub_kusama.rs new file mode 100644 index 00000000000..6827c24768c --- /dev/null +++ b/relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_headers_to_bridge_hub_kusama.rs @@ -0,0 +1,72 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Polkadot-to-KusamaBridgeHub headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, RelayToRelayHeadersCliBridge}; + +use async_trait::async_trait; +use relay_substrate_client::{AccountKeyPairOf, Client}; +use substrate_relay_helper::{ + finality::{engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline}, + TransactionParams, +}; + +/// Description of Polkadot -> KusamaBridgeHub finalized headers bridge. +#[derive(Clone, Debug)] +pub struct PolkadotFinalityToBridgeHubKusama; + +substrate_relay_helper::generate_submit_finality_proof_call_builder!( + PolkadotFinalityToBridgeHubKusama, + PolkadotFinalityToBridgeHubKusamaCallBuilder, + relay_bridge_hub_kusama_client::runtime::Call::BridgePolkadotGrandpa, + relay_bridge_hub_kusama_client::runtime::BridgePolkadotGrandpaCall::submit_finality_proof +); + +#[async_trait] +impl SubstrateFinalitySyncPipeline for PolkadotFinalityToBridgeHubKusama { + type SourceChain = relay_polkadot_client::Polkadot; + type TargetChain = relay_bridge_hub_kusama_client::BridgeHubKusama; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = PolkadotFinalityToBridgeHubKusamaCallBuilder; + + async fn start_relay_guards( + target_client: &Client, + _transaction_params: &TransactionParams>, + enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + if enable_version_guard { + relay_substrate_client::guard::abort_on_spec_version_change( + target_client.clone(), + target_client.simple_runtime_version().await?.spec_version, + ); + } + Ok(()) + } +} + +/// `Polkadot` to BridgeHub `Kusama` bridge definition. +pub struct PolkadotToBridgeHubKusamaCliBridge {} + +impl CliBridgeBase for PolkadotToBridgeHubKusamaCliBridge { + type Source = relay_polkadot_client::Polkadot; + type Target = relay_bridge_hub_kusama_client::BridgeHubKusama; +} + +impl RelayToRelayHeadersCliBridge for PolkadotToBridgeHubKusamaCliBridge { + type Finality = PolkadotFinalityToBridgeHubKusama; +} diff --git a/relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_parachains_to_bridge_hub_kusama.rs b/relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_parachains_to_bridge_hub_kusama.rs new file mode 100644 index 00000000000..f2a7f7309cf --- /dev/null +++ b/relays/bin-substrate/src/bridges/kusama_polkadot/polkadot_parachains_to_bridge_hub_kusama.rs @@ -0,0 +1,75 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Polkadot-to-BridgeHubKusama parachains sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}; +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use relay_substrate_client::{CallOf, HeaderIdOf}; +use substrate_relay_helper::parachains::{ + SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, +}; + +/// Polkadot-to-BridgeHubKusama parachain sync description. +#[derive(Clone, Debug)] +pub struct BridgeHubPolkadotToBridgeHubKusama; + +impl SubstrateParachainsPipeline for BridgeHubPolkadotToBridgeHubKusama { + type SourceParachain = relay_bridge_hub_polkadot_client::BridgeHubPolkadot; + type SourceRelayChain = relay_polkadot_client::Polkadot; + type TargetChain = relay_bridge_hub_kusama_client::BridgeHubKusama; + + type SubmitParachainHeadsCallBuilder = BridgeHubPolkadotToBridgeHubKusamaCallBuilder; +} + +pub struct BridgeHubPolkadotToBridgeHubKusamaCallBuilder; +impl SubmitParachainHeadsCallBuilder + for BridgeHubPolkadotToBridgeHubKusamaCallBuilder +{ + fn build_submit_parachain_heads_call( + at_relay_block: HeaderIdOf, + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> CallOf { + relay_bridge_hub_kusama_client::runtime::Call::BridgePolkadotParachain( + bp_parachains::BridgeParachainCall::submit_parachain_heads { + at_relay_block: (at_relay_block.0, at_relay_block.1), + parachains, + parachain_heads_proof, + }, + ) + } +} + +/// Polkadot-to-BridgeHubKusama parachain sync description for the CLI. +pub struct BridgeHubPolkadotToBridgeHubKusamaCliBridge {} + +impl ParachainToRelayHeadersCliBridge for BridgeHubPolkadotToBridgeHubKusamaCliBridge { + type SourceRelay = relay_polkadot_client::Polkadot; + type ParachainFinality = BridgeHubPolkadotToBridgeHubKusama; + type RelayFinality = + crate::bridges::kusama_polkadot::polkadot_headers_to_bridge_hub_kusama::PolkadotFinalityToBridgeHubKusama; +} + +impl CliBridgeBase for BridgeHubPolkadotToBridgeHubKusamaCliBridge { + type Source = relay_bridge_hub_polkadot_client::BridgeHubPolkadot; + type Target = relay_bridge_hub_kusama_client::BridgeHubKusama; +} + +impl MessagesCliBridge for BridgeHubPolkadotToBridgeHubKusamaCliBridge { + type MessagesLane = + crate::bridges::kusama_polkadot::bridge_hub_polkadot_messages_to_bridge_hub_kusama::BridgeHubPolkadotMessagesToBridgeHubKusamaMessageLane; +} diff --git a/relays/bin-substrate/src/bridges/mod.rs b/relays/bin-substrate/src/bridges/mod.rs new file mode 100644 index 00000000000..62e69cc0e5f --- /dev/null +++ b/relays/bin-substrate/src/bridges/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Declaration of all bridges that the relay is able to serve. + +pub mod kusama_polkadot; +pub mod rialto_millau; +pub mod rialto_parachain_millau; +pub mod rococo_wococo; +pub mod westend_millau; diff --git a/relays/bin-substrate/src/bridges/rialto_millau/millau_headers_to_rialto.rs b/relays/bin-substrate/src/bridges/rialto_millau/millau_headers_to_rialto.rs new file mode 100644 index 00000000000..f805b29c6a9 --- /dev/null +++ b/relays/bin-substrate/src/bridges/rialto_millau/millau_headers_to_rialto.rs @@ -0,0 +1,56 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Millau-to-Rialto headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, RelayToRelayHeadersCliBridge}; +use substrate_relay_helper::finality::{ + engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder, + SubstrateFinalitySyncPipeline, +}; + +/// Description of Millau -> Rialto finalized headers bridge. +#[derive(Clone, Debug)] +pub struct MillauFinalityToRialto; + +impl SubstrateFinalitySyncPipeline for MillauFinalityToRialto { + type SourceChain = relay_millau_client::Millau; + type TargetChain = relay_rialto_client::Rialto; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder< + Self, + rialto_runtime::Runtime, + rialto_runtime::MillauGrandpaInstance, + >; +} + +//// `Millau` to `Rialto` bridge definition. +pub struct MillauToRialtoCliBridge {} + +impl CliBridgeBase for MillauToRialtoCliBridge { + type Source = relay_millau_client::Millau; + type Target = relay_rialto_client::Rialto; +} + +impl RelayToRelayHeadersCliBridge for MillauToRialtoCliBridge { + type Finality = MillauFinalityToRialto; +} + +impl MessagesCliBridge for MillauToRialtoCliBridge { + type MessagesLane = + crate::bridges::rialto_millau::millau_messages_to_rialto::MillauMessagesToRialto; +} diff --git a/relays/bin-substrate/src/bridges/rialto_millau/millau_messages_to_rialto.rs b/relays/bin-substrate/src/bridges/rialto_millau/millau_messages_to_rialto.rs new file mode 100644 index 00000000000..e6a2ef1a856 --- /dev/null +++ b/relays/bin-substrate/src/bridges/rialto_millau/millau_messages_to_rialto.rs @@ -0,0 +1,47 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Millau-to-Rialto messages sync entrypoint. + +use relay_millau_client::Millau; +use relay_rialto_client::Rialto; +use substrate_relay_helper::messages_lane::{ + DirectReceiveMessagesDeliveryProofCallBuilder, DirectReceiveMessagesProofCallBuilder, + SubstrateMessageLane, +}; + +/// Description of Millau -> Rialto messages bridge. +#[derive(Clone, Debug)] +pub struct MillauMessagesToRialto; + +impl SubstrateMessageLane for MillauMessagesToRialto { + type SourceChain = Millau; + type TargetChain = Rialto; + + type ReceiveMessagesProofCallBuilder = DirectReceiveMessagesProofCallBuilder< + Self, + rialto_runtime::Runtime, + rialto_runtime::WithMillauMessagesInstance, + >; + type ReceiveMessagesDeliveryProofCallBuilder = DirectReceiveMessagesDeliveryProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WithRialtoMessagesInstance, + >; + + type SourceBatchCallBuilder = (); + type TargetBatchCallBuilder = (); +} diff --git a/relays/bin-substrate/src/bridges/rialto_millau/mod.rs b/relays/bin-substrate/src/bridges/rialto_millau/mod.rs new file mode 100644 index 00000000000..2353b58ce61 --- /dev/null +++ b/relays/bin-substrate/src/bridges/rialto_millau/mod.rs @@ -0,0 +1,22 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Declaration of all bridges between Rialto and Millau. + +pub mod millau_headers_to_rialto; +pub mod millau_messages_to_rialto; +pub mod rialto_headers_to_millau; +pub mod rialto_messages_to_millau; diff --git a/relays/bin-substrate/src/bridges/rialto_millau/rialto_headers_to_millau.rs b/relays/bin-substrate/src/bridges/rialto_millau/rialto_headers_to_millau.rs new file mode 100644 index 00000000000..7c979f52795 --- /dev/null +++ b/relays/bin-substrate/src/bridges/rialto_millau/rialto_headers_to_millau.rs @@ -0,0 +1,56 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rialto-to-Millau headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, RelayToRelayHeadersCliBridge}; +use substrate_relay_helper::finality::{ + engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder, + SubstrateFinalitySyncPipeline, +}; + +/// Description of Millau -> Rialto finalized headers bridge. +#[derive(Clone, Debug)] +pub struct RialtoFinalityToMillau; + +impl SubstrateFinalitySyncPipeline for RialtoFinalityToMillau { + type SourceChain = relay_rialto_client::Rialto; + type TargetChain = relay_millau_client::Millau; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::RialtoGrandpaInstance, + >; +} + +//// `Rialto` to `Millau` bridge definition. +pub struct RialtoToMillauCliBridge {} + +impl CliBridgeBase for RialtoToMillauCliBridge { + type Source = relay_rialto_client::Rialto; + type Target = relay_millau_client::Millau; +} + +impl RelayToRelayHeadersCliBridge for RialtoToMillauCliBridge { + type Finality = RialtoFinalityToMillau; +} + +impl MessagesCliBridge for RialtoToMillauCliBridge { + type MessagesLane = + crate::bridges::rialto_millau::rialto_messages_to_millau::RialtoMessagesToMillau; +} diff --git a/relays/bin-substrate/src/bridges/rialto_millau/rialto_messages_to_millau.rs b/relays/bin-substrate/src/bridges/rialto_millau/rialto_messages_to_millau.rs new file mode 100644 index 00000000000..b45239fb9a9 --- /dev/null +++ b/relays/bin-substrate/src/bridges/rialto_millau/rialto_messages_to_millau.rs @@ -0,0 +1,47 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rialto-to-Millau messages sync entrypoint. + +use relay_millau_client::Millau; +use relay_rialto_client::Rialto; +use substrate_relay_helper::messages_lane::{ + DirectReceiveMessagesDeliveryProofCallBuilder, DirectReceiveMessagesProofCallBuilder, + SubstrateMessageLane, +}; + +/// Description of Rialto -> Millau messages bridge. +#[derive(Clone, Debug)] +pub struct RialtoMessagesToMillau; + +impl SubstrateMessageLane for RialtoMessagesToMillau { + type SourceChain = Rialto; + type TargetChain = Millau; + + type ReceiveMessagesProofCallBuilder = DirectReceiveMessagesProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WithRialtoMessagesInstance, + >; + type ReceiveMessagesDeliveryProofCallBuilder = DirectReceiveMessagesDeliveryProofCallBuilder< + Self, + rialto_runtime::Runtime, + rialto_runtime::WithMillauMessagesInstance, + >; + + type SourceBatchCallBuilder = (); + type TargetBatchCallBuilder = (); +} diff --git a/relays/bin-substrate/src/bridges/rialto_parachain_millau/millau_headers_to_rialto_parachain.rs b/relays/bin-substrate/src/bridges/rialto_parachain_millau/millau_headers_to_rialto_parachain.rs new file mode 100644 index 00000000000..d1c090c0797 --- /dev/null +++ b/relays/bin-substrate/src/bridges/rialto_parachain_millau/millau_headers_to_rialto_parachain.rs @@ -0,0 +1,76 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Millau-to-RialtoParachain headers sync entrypoint. + +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Millau-to-RialtoParachain headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, RelayToRelayHeadersCliBridge}; +use substrate_relay_helper::finality::{ + engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline, +}; + +substrate_relay_helper::generate_submit_finality_proof_call_builder!( + MillauFinalityToRialtoParachain, + MillauFinalityToRialtoParachainCallBuilder, + relay_rialto_parachain_client::RuntimeCall::BridgeMillauGrandpa, + relay_rialto_parachain_client::BridgeGrandpaCall::submit_finality_proof +); + +/// Description of Millau -> Rialto finalized headers bridge. +#[derive(Clone, Debug)] +pub struct MillauFinalityToRialtoParachain; + +impl SubstrateFinalitySyncPipeline for MillauFinalityToRialtoParachain { + type SourceChain = relay_millau_client::Millau; + type TargetChain = relay_rialto_parachain_client::RialtoParachain; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = MillauFinalityToRialtoParachainCallBuilder; +} + +//// `Millau` to `RialtoParachain` bridge definition. +pub struct MillauToRialtoParachainCliBridge {} + +impl CliBridgeBase for MillauToRialtoParachainCliBridge { + type Source = relay_millau_client::Millau; + type Target = relay_rialto_parachain_client::RialtoParachain; +} + +impl RelayToRelayHeadersCliBridge for MillauToRialtoParachainCliBridge { + type Finality = MillauFinalityToRialtoParachain; +} + +impl MessagesCliBridge for MillauToRialtoParachainCliBridge { + type MessagesLane = + crate::bridges::rialto_parachain_millau::millau_messages_to_rialto_parachain::MillauMessagesToRialtoParachain; +} diff --git a/relays/bin-substrate/src/bridges/rialto_parachain_millau/millau_messages_to_rialto_parachain.rs b/relays/bin-substrate/src/bridges/rialto_parachain_millau/millau_messages_to_rialto_parachain.rs new file mode 100644 index 00000000000..8fedd22a40a --- /dev/null +++ b/relays/bin-substrate/src/bridges/rialto_parachain_millau/millau_messages_to_rialto_parachain.rs @@ -0,0 +1,51 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Millau-to-RialtoParachain messages sync entrypoint. + +use relay_millau_client::Millau; +use relay_rialto_parachain_client::RialtoParachain; +use substrate_relay_helper::{ + messages_lane::{DirectReceiveMessagesDeliveryProofCallBuilder, SubstrateMessageLane}, + UtilityPalletBatchCallBuilder, +}; + +substrate_relay_helper::generate_receive_message_proof_call_builder!( + MillauMessagesToRialtoParachain, + MillauMessagesToRialtoParachainReceiveMessagesProofCallBuilder, + relay_rialto_parachain_client::RuntimeCall::BridgeMillauMessages, + relay_rialto_parachain_client::BridgeMessagesCall::receive_messages_proof +); + +/// Description of Millau -> RialtoParachain messages bridge. +#[derive(Clone, Debug)] +pub struct MillauMessagesToRialtoParachain; + +impl SubstrateMessageLane for MillauMessagesToRialtoParachain { + type SourceChain = Millau; + type TargetChain = RialtoParachain; + + type ReceiveMessagesProofCallBuilder = + MillauMessagesToRialtoParachainReceiveMessagesProofCallBuilder; + type ReceiveMessagesDeliveryProofCallBuilder = DirectReceiveMessagesDeliveryProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WithRialtoParachainMessagesInstance, + >; + + type SourceBatchCallBuilder = UtilityPalletBatchCallBuilder; + type TargetBatchCallBuilder = (); +} diff --git a/relays/bin-substrate/src/bridges/rialto_parachain_millau/mod.rs b/relays/bin-substrate/src/bridges/rialto_parachain_millau/mod.rs new file mode 100644 index 00000000000..f0613d1511e --- /dev/null +++ b/relays/bin-substrate/src/bridges/rialto_parachain_millau/mod.rs @@ -0,0 +1,22 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Declaration of all bridges between Rialto Parachain and Millau. + +pub mod millau_headers_to_rialto_parachain; +pub mod millau_messages_to_rialto_parachain; +pub mod rialto_parachain_messages_to_millau; +pub mod rialto_parachains_to_millau; diff --git a/relays/bin-substrate/src/bridges/rialto_parachain_millau/rialto_parachain_messages_to_millau.rs b/relays/bin-substrate/src/bridges/rialto_parachain_millau/rialto_parachain_messages_to_millau.rs new file mode 100644 index 00000000000..e19953eac55 --- /dev/null +++ b/relays/bin-substrate/src/bridges/rialto_parachain_millau/rialto_parachain_messages_to_millau.rs @@ -0,0 +1,51 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! RialtoParachain-to-Millau messages sync entrypoint. + +use relay_millau_client::Millau; +use relay_rialto_parachain_client::RialtoParachain; +use substrate_relay_helper::{ + messages_lane::{DirectReceiveMessagesProofCallBuilder, SubstrateMessageLane}, + UtilityPalletBatchCallBuilder, +}; + +substrate_relay_helper::generate_receive_message_delivery_proof_call_builder!( + RialtoParachainMessagesToMillau, + RialtoParachainMessagesToMillauReceiveMessagesDeliveryProofCallBuilder, + relay_rialto_parachain_client::RuntimeCall::BridgeMillauMessages, + relay_rialto_parachain_client::BridgeMessagesCall::receive_messages_delivery_proof +); + +/// Description of RialtoParachain -> Millau messages bridge. +#[derive(Clone, Debug)] +pub struct RialtoParachainMessagesToMillau; + +impl SubstrateMessageLane for RialtoParachainMessagesToMillau { + type SourceChain = RialtoParachain; + type TargetChain = Millau; + + type ReceiveMessagesProofCallBuilder = DirectReceiveMessagesProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WithRialtoParachainMessagesInstance, + >; + type ReceiveMessagesDeliveryProofCallBuilder = + RialtoParachainMessagesToMillauReceiveMessagesDeliveryProofCallBuilder; + + type SourceBatchCallBuilder = (); + type TargetBatchCallBuilder = UtilityPalletBatchCallBuilder; +} diff --git a/relays/bin-substrate/src/bridges/rialto_parachain_millau/rialto_parachains_to_millau.rs b/relays/bin-substrate/src/bridges/rialto_parachain_millau/rialto_parachains_to_millau.rs new file mode 100644 index 00000000000..04f2b5aa7c7 --- /dev/null +++ b/relays/bin-substrate/src/bridges/rialto_parachain_millau/rialto_parachains_to_millau.rs @@ -0,0 +1,65 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rialto-to-Millau parachains sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}; +use relay_millau_client::Millau; +use relay_rialto_client::Rialto; +use relay_rialto_parachain_client::RialtoParachain; +use substrate_relay_helper::parachains::{ + DirectSubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, +}; + +/// Rialto-to-Millau parachains sync description. +#[derive(Clone, Debug)] +pub struct RialtoParachainsToMillau; + +impl SubstrateParachainsPipeline for RialtoParachainsToMillau { + type SourceParachain = RialtoParachain; + type SourceRelayChain = Rialto; + type TargetChain = Millau; + + type SubmitParachainHeadsCallBuilder = RialtoParachainsToMillauSubmitParachainHeadsCallBuilder; +} + +/// `submit_parachain_heads` call builder for Rialto-to-Millau parachains sync pipeline. +pub type RialtoParachainsToMillauSubmitParachainHeadsCallBuilder = + DirectSubmitParachainHeadsCallBuilder< + RialtoParachainsToMillau, + millau_runtime::Runtime, + millau_runtime::WithRialtoParachainsInstance, + >; + +//// `RialtoParachain` to `Millau` bridge definition. +pub struct RialtoParachainToMillauCliBridge {} + +impl CliBridgeBase for RialtoParachainToMillauCliBridge { + type Source = RialtoParachain; + type Target = Millau; +} + +impl ParachainToRelayHeadersCliBridge for RialtoParachainToMillauCliBridge { + type SourceRelay = Rialto; + type ParachainFinality = RialtoParachainsToMillau; + type RelayFinality = + crate::bridges::rialto_millau::rialto_headers_to_millau::RialtoFinalityToMillau; +} + +impl MessagesCliBridge for RialtoParachainToMillauCliBridge { + type MessagesLane = + crate::bridges::rialto_parachain_millau::rialto_parachain_messages_to_millau::RialtoParachainMessagesToMillau; +} diff --git a/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs new file mode 100644 index 00000000000..9e4b5b15fd6 --- /dev/null +++ b/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs @@ -0,0 +1,64 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! BridgeHubRococo-to-BridgeHubWococo messages sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge}; +use relay_bridge_hub_rococo_client::BridgeHubRococo; +use relay_bridge_hub_wococo_client::BridgeHubWococo; +use substrate_relay_helper::{messages_lane::SubstrateMessageLane, UtilityPalletBatchCallBuilder}; + +pub struct BridgeHubRococoToBridgeHubWococoMessagesCliBridge {} + +impl CliBridgeBase for BridgeHubRococoToBridgeHubWococoMessagesCliBridge { + type Source = BridgeHubRococo; + type Target = BridgeHubWococo; +} + +impl MessagesCliBridge for BridgeHubRococoToBridgeHubWococoMessagesCliBridge { + type MessagesLane = BridgeHubRococoMessagesToBridgeHubWococoMessageLane; +} + +substrate_relay_helper::generate_receive_message_proof_call_builder!( + BridgeHubRococoMessagesToBridgeHubWococoMessageLane, + BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesProofCallBuilder, + relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoMessages, + relay_bridge_hub_wococo_client::runtime::BridgeRococoMessagesCall::receive_messages_proof +); + +substrate_relay_helper::generate_receive_message_delivery_proof_call_builder!( + BridgeHubRococoMessagesToBridgeHubWococoMessageLane, + BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesDeliveryProofCallBuilder, + relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoMessages, + relay_bridge_hub_rococo_client::runtime::BridgeWococoMessagesCall::receive_messages_delivery_proof +); + +/// Description of BridgeHubRococo -> BridgeHubWococo messages bridge. +#[derive(Clone, Debug)] +pub struct BridgeHubRococoMessagesToBridgeHubWococoMessageLane; + +impl SubstrateMessageLane for BridgeHubRococoMessagesToBridgeHubWococoMessageLane { + type SourceChain = BridgeHubRococo; + type TargetChain = BridgeHubWococo; + + type ReceiveMessagesProofCallBuilder = + BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesProofCallBuilder; + type ReceiveMessagesDeliveryProofCallBuilder = + BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesDeliveryProofCallBuilder; + + type SourceBatchCallBuilder = UtilityPalletBatchCallBuilder; + type TargetBatchCallBuilder = UtilityPalletBatchCallBuilder; +} diff --git a/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs new file mode 100644 index 00000000000..fb5a81c021e --- /dev/null +++ b/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs @@ -0,0 +1,64 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! BridgeHubWococo-to-BridgeHubRococo messages sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge}; +use relay_bridge_hub_rococo_client::BridgeHubRococo; +use relay_bridge_hub_wococo_client::BridgeHubWococo; +use substrate_relay_helper::{messages_lane::SubstrateMessageLane, UtilityPalletBatchCallBuilder}; + +pub struct BridgeHubWococoToBridgeHubRococoMessagesCliBridge {} + +impl CliBridgeBase for BridgeHubWococoToBridgeHubRococoMessagesCliBridge { + type Source = BridgeHubWococo; + type Target = BridgeHubRococo; +} + +impl MessagesCliBridge for BridgeHubWococoToBridgeHubRococoMessagesCliBridge { + type MessagesLane = BridgeHubWococoMessagesToBridgeHubRococoMessageLane; +} + +substrate_relay_helper::generate_receive_message_proof_call_builder!( + BridgeHubWococoMessagesToBridgeHubRococoMessageLane, + BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesProofCallBuilder, + relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoMessages, + relay_bridge_hub_rococo_client::runtime::BridgeWococoMessagesCall::receive_messages_proof +); + +substrate_relay_helper::generate_receive_message_delivery_proof_call_builder!( + BridgeHubWococoMessagesToBridgeHubRococoMessageLane, + BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesDeliveryProofCallBuilder, + relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoMessages, + relay_bridge_hub_wococo_client::runtime::BridgeRococoMessagesCall::receive_messages_delivery_proof +); + +/// Description of BridgeHubWococo -> BridgeHubRococo messages bridge. +#[derive(Clone, Debug)] +pub struct BridgeHubWococoMessagesToBridgeHubRococoMessageLane; + +impl SubstrateMessageLane for BridgeHubWococoMessagesToBridgeHubRococoMessageLane { + type SourceChain = BridgeHubWococo; + type TargetChain = BridgeHubRococo; + + type ReceiveMessagesProofCallBuilder = + BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesProofCallBuilder; + type ReceiveMessagesDeliveryProofCallBuilder = + BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesDeliveryProofCallBuilder; + + type SourceBatchCallBuilder = UtilityPalletBatchCallBuilder; + type TargetBatchCallBuilder = UtilityPalletBatchCallBuilder; +} diff --git a/relays/bin-substrate/src/bridges/rococo_wococo/mod.rs b/relays/bin-substrate/src/bridges/rococo_wococo/mod.rs new file mode 100644 index 00000000000..64330a92252 --- /dev/null +++ b/relays/bin-substrate/src/bridges/rococo_wococo/mod.rs @@ -0,0 +1,24 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Declaration of all bridges between Rococo Bridge Hub and Wococo Bridge Hub. + +pub mod bridge_hub_rococo_messages_to_bridge_hub_wococo; +pub mod bridge_hub_wococo_messages_to_bridge_hub_rococo; +pub mod rococo_headers_to_bridge_hub_wococo; +pub mod rococo_parachains_to_bridge_hub_wococo; +pub mod wococo_headers_to_bridge_hub_rococo; +pub mod wococo_parachains_to_bridge_hub_rococo; diff --git a/relays/bin-substrate/src/bridges/rococo_wococo/rococo_headers_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/rococo_headers_to_bridge_hub_wococo.rs new file mode 100644 index 00000000000..2272144311c --- /dev/null +++ b/relays/bin-substrate/src/bridges/rococo_wococo/rococo_headers_to_bridge_hub_wococo.rs @@ -0,0 +1,72 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rococo-to-Wococo bridge hubs headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, RelayToRelayHeadersCliBridge}; + +use async_trait::async_trait; +use relay_substrate_client::{AccountKeyPairOf, Client}; +use substrate_relay_helper::{ + finality::{engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline}, + TransactionParams, +}; + +/// Description of Rococo -> Wococo finalized headers bridge. +#[derive(Clone, Debug)] +pub struct RococoFinalityToBridgeHubWococo; + +substrate_relay_helper::generate_submit_finality_proof_call_builder!( + RococoFinalityToBridgeHubWococo, + RococoFinalityToBridgeHubWococoCallBuilder, + relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoGrandpa, + relay_bridge_hub_wococo_client::runtime::BridgeRococoGrandpaCall::submit_finality_proof +); + +#[async_trait] +impl SubstrateFinalitySyncPipeline for RococoFinalityToBridgeHubWococo { + type SourceChain = relay_rococo_client::Rococo; + type TargetChain = relay_bridge_hub_wococo_client::BridgeHubWococo; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = RococoFinalityToBridgeHubWococoCallBuilder; + + async fn start_relay_guards( + target_client: &Client, + _transaction_params: &TransactionParams>, + enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + if enable_version_guard { + relay_substrate_client::guard::abort_on_spec_version_change( + target_client.clone(), + target_client.simple_runtime_version().await?.spec_version, + ); + } + Ok(()) + } +} + +/// `Rococo` to BridgeHub `Wococo` bridge definition. +pub struct RococoToBridgeHubWococoCliBridge {} + +impl CliBridgeBase for RococoToBridgeHubWococoCliBridge { + type Source = relay_rococo_client::Rococo; + type Target = relay_bridge_hub_wococo_client::BridgeHubWococo; +} + +impl RelayToRelayHeadersCliBridge for RococoToBridgeHubWococoCliBridge { + type Finality = RococoFinalityToBridgeHubWococo; +} diff --git a/relays/bin-substrate/src/bridges/rococo_wococo/rococo_parachains_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/rococo_parachains_to_bridge_hub_wococo.rs new file mode 100644 index 00000000000..78b4ff35a7d --- /dev/null +++ b/relays/bin-substrate/src/bridges/rococo_wococo/rococo_parachains_to_bridge_hub_wococo.rs @@ -0,0 +1,75 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Wococo-to-Rococo parachains sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}; +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use relay_substrate_client::{CallOf, HeaderIdOf}; +use substrate_relay_helper::parachains::{ + SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, +}; + +/// BridgeHub-to-BridgeHub parachain sync description. +#[derive(Clone, Debug)] +pub struct BridgeHubRococoToBridgeHubWococo; + +impl SubstrateParachainsPipeline for BridgeHubRococoToBridgeHubWococo { + type SourceParachain = relay_bridge_hub_rococo_client::BridgeHubRococo; + type SourceRelayChain = relay_rococo_client::Rococo; + type TargetChain = relay_bridge_hub_wococo_client::BridgeHubWococo; + + type SubmitParachainHeadsCallBuilder = BridgeHubRococoToBridgeHubWococoCallBuilder; +} + +pub struct BridgeHubRococoToBridgeHubWococoCallBuilder; +impl SubmitParachainHeadsCallBuilder + for BridgeHubRococoToBridgeHubWococoCallBuilder +{ + fn build_submit_parachain_heads_call( + at_relay_block: HeaderIdOf, + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> CallOf { + relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoParachain( + relay_bridge_hub_wococo_client::runtime::BridgeParachainCall::submit_parachain_heads { + at_relay_block: (at_relay_block.0, at_relay_block.1), + parachains, + parachain_heads_proof, + }, + ) + } +} + +/// `BridgeHubParachain` to `BridgeHubParachain` bridge definition. +pub struct BridgeHubRococoToBridgeHubWococoCliBridge {} + +impl ParachainToRelayHeadersCliBridge for BridgeHubRococoToBridgeHubWococoCliBridge { + type SourceRelay = relay_rococo_client::Rococo; + type ParachainFinality = BridgeHubRococoToBridgeHubWococo; + type RelayFinality = + crate::bridges::rococo_wococo::rococo_headers_to_bridge_hub_wococo::RococoFinalityToBridgeHubWococo; +} + +impl CliBridgeBase for BridgeHubRococoToBridgeHubWococoCliBridge { + type Source = relay_bridge_hub_rococo_client::BridgeHubRococo; + type Target = relay_bridge_hub_wococo_client::BridgeHubWococo; +} + +impl MessagesCliBridge for BridgeHubRococoToBridgeHubWococoCliBridge { + type MessagesLane = + crate::bridges::rococo_wococo::bridge_hub_rococo_messages_to_bridge_hub_wococo::BridgeHubRococoMessagesToBridgeHubWococoMessageLane; +} diff --git a/relays/bin-substrate/src/bridges/rococo_wococo/wococo_headers_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/wococo_headers_to_bridge_hub_rococo.rs new file mode 100644 index 00000000000..39043009582 --- /dev/null +++ b/relays/bin-substrate/src/bridges/rococo_wococo/wococo_headers_to_bridge_hub_rococo.rs @@ -0,0 +1,72 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Wococo-to-Rococo bridge hubs headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, RelayToRelayHeadersCliBridge}; + +use async_trait::async_trait; +use relay_substrate_client::{AccountKeyPairOf, Client}; +use substrate_relay_helper::{ + finality::{engine::Grandpa as GrandpaFinalityEngine, SubstrateFinalitySyncPipeline}, + TransactionParams, +}; + +/// Description of Wococo -> Rococo finalized headers bridge. +#[derive(Clone, Debug)] +pub struct WococoFinalityToBridgeHubRococo; + +substrate_relay_helper::generate_submit_finality_proof_call_builder!( + WococoFinalityToBridgeHubRococo, + WococoFinalityToBridgeHubRococoCallBuilder, + relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoGrandpa, + relay_bridge_hub_rococo_client::runtime::BridgeWococoGrandpaCall::submit_finality_proof +); + +#[async_trait] +impl SubstrateFinalitySyncPipeline for WococoFinalityToBridgeHubRococo { + type SourceChain = relay_wococo_client::Wococo; + type TargetChain = relay_bridge_hub_rococo_client::BridgeHubRococo; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = WococoFinalityToBridgeHubRococoCallBuilder; + + async fn start_relay_guards( + target_client: &Client, + _transaction_params: &TransactionParams>, + enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + if enable_version_guard { + relay_substrate_client::guard::abort_on_spec_version_change( + target_client.clone(), + target_client.simple_runtime_version().await?.spec_version, + ); + } + Ok(()) + } +} + +/// `Wococo` to BridgeHub `Rococo` bridge definition. +pub struct WococoToBridgeHubRococoCliBridge {} + +impl CliBridgeBase for WococoToBridgeHubRococoCliBridge { + type Source = relay_wococo_client::Wococo; + type Target = relay_bridge_hub_rococo_client::BridgeHubRococo; +} + +impl RelayToRelayHeadersCliBridge for WococoToBridgeHubRococoCliBridge { + type Finality = WococoFinalityToBridgeHubRococo; +} diff --git a/relays/bin-substrate/src/bridges/rococo_wococo/wococo_parachains_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/wococo_parachains_to_bridge_hub_rococo.rs new file mode 100644 index 00000000000..bc76e377c92 --- /dev/null +++ b/relays/bin-substrate/src/bridges/rococo_wococo/wococo_parachains_to_bridge_hub_rococo.rs @@ -0,0 +1,75 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rococo-to-Wococo parachains sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}; +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use relay_substrate_client::{CallOf, HeaderIdOf}; +use substrate_relay_helper::parachains::{ + SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, +}; + +/// BridgeHub-to-BridgeHub parachain sync description. +#[derive(Clone, Debug)] +pub struct BridgeHubWococoToBridgeHubRococo; + +impl SubstrateParachainsPipeline for BridgeHubWococoToBridgeHubRococo { + type SourceParachain = relay_bridge_hub_wococo_client::BridgeHubWococo; + type SourceRelayChain = relay_wococo_client::Wococo; + type TargetChain = relay_bridge_hub_rococo_client::BridgeHubRococo; + + type SubmitParachainHeadsCallBuilder = BridgeHubWococoToBridgeHubRococoCallBuilder; +} + +pub struct BridgeHubWococoToBridgeHubRococoCallBuilder; +impl SubmitParachainHeadsCallBuilder + for BridgeHubWococoToBridgeHubRococoCallBuilder +{ + fn build_submit_parachain_heads_call( + at_relay_block: HeaderIdOf, + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> CallOf { + relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoParachain( + bp_parachains::BridgeParachainCall::submit_parachain_heads { + at_relay_block: (at_relay_block.0, at_relay_block.1), + parachains, + parachain_heads_proof, + }, + ) + } +} + +/// `BridgeHubParachain` to `BridgeHubParachain` bridge definition. +pub struct BridgeHubWococoToBridgeHubRococoCliBridge {} + +impl ParachainToRelayHeadersCliBridge for BridgeHubWococoToBridgeHubRococoCliBridge { + type SourceRelay = relay_wococo_client::Wococo; + type ParachainFinality = BridgeHubWococoToBridgeHubRococo; + type RelayFinality = + crate::bridges::rococo_wococo::wococo_headers_to_bridge_hub_rococo::WococoFinalityToBridgeHubRococo; +} + +impl CliBridgeBase for BridgeHubWococoToBridgeHubRococoCliBridge { + type Source = relay_bridge_hub_wococo_client::BridgeHubWococo; + type Target = relay_bridge_hub_rococo_client::BridgeHubRococo; +} + +impl MessagesCliBridge for BridgeHubWococoToBridgeHubRococoCliBridge { + type MessagesLane = + crate::bridges::rococo_wococo::bridge_hub_wococo_messages_to_bridge_hub_rococo::BridgeHubWococoMessagesToBridgeHubRococoMessageLane; +} diff --git a/relays/bin-substrate/src/bridges/westend_millau/mod.rs b/relays/bin-substrate/src/bridges/westend_millau/mod.rs new file mode 100644 index 00000000000..10bc19241ce --- /dev/null +++ b/relays/bin-substrate/src/bridges/westend_millau/mod.rs @@ -0,0 +1,20 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Declaration of all bridges between Westend and Millau. + +pub mod westend_headers_to_millau; +pub mod westend_parachains_to_millau; diff --git a/relays/bin-substrate/src/bridges/westend_millau/westend_headers_to_millau.rs b/relays/bin-substrate/src/bridges/westend_millau/westend_headers_to_millau.rs new file mode 100644 index 00000000000..2a253756c2b --- /dev/null +++ b/relays/bin-substrate/src/bridges/westend_millau/westend_headers_to_millau.rs @@ -0,0 +1,51 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Westend-to-Millau headers sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, RelayToRelayHeadersCliBridge}; +use substrate_relay_helper::finality::{ + engine::Grandpa as GrandpaFinalityEngine, DirectSubmitGrandpaFinalityProofCallBuilder, + SubstrateFinalitySyncPipeline, +}; + +/// Description of Westend -> Millau finalized headers bridge. +#[derive(Clone, Debug)] +pub struct WestendFinalityToMillau; + +impl SubstrateFinalitySyncPipeline for WestendFinalityToMillau { + type SourceChain = relay_westend_client::Westend; + type TargetChain = relay_millau_client::Millau; + + type FinalityEngine = GrandpaFinalityEngine; + type SubmitFinalityProofCallBuilder = DirectSubmitGrandpaFinalityProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WestendGrandpaInstance, + >; +} + +//// `Westend` to `Millau` bridge definition. +pub struct WestendToMillauCliBridge {} + +impl CliBridgeBase for WestendToMillauCliBridge { + type Source = relay_westend_client::Westend; + type Target = relay_millau_client::Millau; +} + +impl RelayToRelayHeadersCliBridge for WestendToMillauCliBridge { + type Finality = WestendFinalityToMillau; +} diff --git a/relays/bin-substrate/src/bridges/westend_millau/westend_parachains_to_millau.rs b/relays/bin-substrate/src/bridges/westend_millau/westend_parachains_to_millau.rs new file mode 100644 index 00000000000..0556ecab368 --- /dev/null +++ b/relays/bin-substrate/src/bridges/westend_millau/westend_parachains_to_millau.rs @@ -0,0 +1,59 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Westend-to-Millau parachains sync entrypoint. + +use crate::cli::bridge::{CliBridgeBase, ParachainToRelayHeadersCliBridge}; +use relay_millau_client::Millau; +use relay_westend_client::{Westend, Westmint}; +use substrate_relay_helper::parachains::{ + DirectSubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, +}; + +/// Westend-to-Millau parachains sync description. +#[derive(Clone, Debug)] +pub struct WestendParachainsToMillau; + +impl SubstrateParachainsPipeline for WestendParachainsToMillau { + type SourceParachain = Westmint; + type SourceRelayChain = Westend; + type TargetChain = Millau; + + type SubmitParachainHeadsCallBuilder = WestendParachainsToMillauSubmitParachainHeadsCallBuilder; +} + +/// `submit_parachain_heads` call builder for Rialto-to-Millau parachains sync pipeline. +pub type WestendParachainsToMillauSubmitParachainHeadsCallBuilder = + DirectSubmitParachainHeadsCallBuilder< + WestendParachainsToMillau, + millau_runtime::Runtime, + millau_runtime::WithWestendParachainsInstance, + >; + +//// `WestendParachain` to `Millau` bridge definition. +pub struct WestmintToMillauCliBridge {} + +impl ParachainToRelayHeadersCliBridge for WestmintToMillauCliBridge { + type SourceRelay = Westend; + type ParachainFinality = WestendParachainsToMillau; + type RelayFinality = + crate::bridges::westend_millau::westend_headers_to_millau::WestendFinalityToMillau; +} + +impl CliBridgeBase for WestmintToMillauCliBridge { + type Source = Westmint; + type Target = Millau; +} diff --git a/relays/bin-substrate/src/chains/kusama.rs b/relays/bin-substrate/src/chains/kusama.rs new file mode 100644 index 00000000000..6d5a4764f91 --- /dev/null +++ b/relays/bin-substrate/src/chains/kusama.rs @@ -0,0 +1,32 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Kusama + Kusama parachains specification for CLI. + +use crate::cli::CliChain; +use relay_bridge_hub_kusama_client::BridgeHubKusama; +use relay_kusama_client::Kusama; +use relay_substrate_client::SimpleRuntimeVersion; + +impl CliChain for Kusama { + const RUNTIME_VERSION: Option = None; +} + +impl CliChain for BridgeHubKusama { + // TODO: fix me (https://github.com/paritytech/parity-bridges-common/issues/1945) + const RUNTIME_VERSION: Option = + Some(SimpleRuntimeVersion { spec_version: 4242, transaction_version: 42 }); +} diff --git a/relays/bin-substrate/src/chains/millau.rs b/relays/bin-substrate/src/chains/millau.rs new file mode 100644 index 00000000000..44416195c6a --- /dev/null +++ b/relays/bin-substrate/src/chains/millau.rs @@ -0,0 +1,39 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Millau chain specification for CLI. + +use crate::cli::{encode_message::CliEncodeMessage, CliChain}; +use bp_runtime::EncodedOrDecodedCall; +use relay_millau_client::Millau; +use relay_substrate_client::SimpleRuntimeVersion; + +impl CliEncodeMessage for Millau { + fn encode_execute_xcm( + message: xcm::VersionedXcm, + ) -> anyhow::Result> { + Ok(millau_runtime::RuntimeCall::XcmPallet(millau_runtime::XcmCall::execute { + message: Box::new(message), + max_weight: Self::estimate_execute_xcm_weight(), + }) + .into()) + } +} + +impl CliChain for Millau { + const RUNTIME_VERSION: Option = + Some(SimpleRuntimeVersion::from_runtime_version(&millau_runtime::VERSION)); +} diff --git a/relays/bin-substrate/src/chains/mod.rs b/relays/bin-substrate/src/chains/mod.rs new file mode 100644 index 00000000000..b1a91ed1e85 --- /dev/null +++ b/relays/bin-substrate/src/chains/mod.rs @@ -0,0 +1,110 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Chain-specific relayer configuration. + +mod kusama; +mod millau; +mod polkadot; +mod rialto; +mod rialto_parachain; +mod rococo; +mod westend; +mod wococo; + +#[cfg(test)] +mod tests { + use crate::cli::encode_message; + use bp_messages::source_chain::TargetHeaderChain; + use bp_runtime::Chain as _; + use codec::Encode; + use relay_millau_client::Millau; + use relay_rialto_client::Rialto; + use relay_substrate_client::{ChainWithTransactions, SignParam, UnsignedTransaction}; + + #[test] + fn maximal_rialto_to_millau_message_size_is_computed_correctly() { + use rialto_runtime::millau_messages::MillauAsTargetHeaderChain; + + let maximal_message_size = encode_message::compute_maximal_message_size( + bp_rialto::Rialto::max_extrinsic_size(), + bp_millau::Millau::max_extrinsic_size(), + ); + + let message = vec![42; maximal_message_size as _]; + assert_eq!(MillauAsTargetHeaderChain::verify_message(&message), Ok(())); + + let message = vec![42; (maximal_message_size + 1) as _]; + assert!(MillauAsTargetHeaderChain::verify_message(&message).is_err()); + } + + #[test] + fn maximal_size_remark_to_rialto_is_generated_correctly() { + assert!( + bridge_runtime_common::messages::target::maximal_incoming_message_size( + bp_rialto::Rialto::max_extrinsic_size() + ) > bp_millau::Millau::max_extrinsic_size(), + "We can't actually send maximal messages to Rialto from Millau, because Millau extrinsics can't be that large", + ) + } + #[test] + fn rialto_tx_extra_bytes_constant_is_correct() { + let rialto_call = rialto_runtime::RuntimeCall::System(rialto_runtime::SystemCall::remark { + remark: vec![], + }); + let rialto_tx = Rialto::sign_transaction( + SignParam { + spec_version: 1, + transaction_version: 1, + genesis_hash: Default::default(), + signer: sp_keyring::AccountKeyring::Alice.pair(), + }, + UnsignedTransaction::new(rialto_call.clone().into(), 0), + ) + .unwrap(); + let extra_bytes_in_transaction = rialto_tx.encode().len() - rialto_call.encode().len(); + assert!( + bp_rialto::TX_EXTRA_BYTES as usize >= extra_bytes_in_transaction, + "Hardcoded number of extra bytes in Rialto transaction {} is lower than actual value: {}", + bp_rialto::TX_EXTRA_BYTES, + extra_bytes_in_transaction, + ); + } + + #[test] + fn millau_tx_extra_bytes_constant_is_correct() { + let millau_call = millau_runtime::RuntimeCall::System(millau_runtime::SystemCall::remark { + remark: vec![], + }); + let millau_tx = Millau::sign_transaction( + SignParam { + spec_version: 0, + transaction_version: 0, + genesis_hash: Default::default(), + signer: sp_keyring::AccountKeyring::Alice.pair(), + }, + UnsignedTransaction::new(millau_call.clone().into(), 0), + ) + .unwrap(); + let extra_bytes_in_transaction = millau_tx.encode().len() - millau_call.encode().len(); + assert!( + bp_millau::TX_EXTRA_BYTES as usize >= extra_bytes_in_transaction, + "Hardcoded number of extra bytes in Millau transaction {} is lower than actual value: {}", + bp_millau::TX_EXTRA_BYTES, + extra_bytes_in_transaction, + ); + } +} diff --git a/relays/bin-substrate/src/chains/polkadot.rs b/relays/bin-substrate/src/chains/polkadot.rs new file mode 100644 index 00000000000..4fe5a48398b --- /dev/null +++ b/relays/bin-substrate/src/chains/polkadot.rs @@ -0,0 +1,32 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Polkadot + Polkadot parachains specification for CLI. + +use crate::cli::CliChain; +use relay_bridge_hub_polkadot_client::BridgeHubPolkadot; +use relay_polkadot_client::Polkadot; +use relay_substrate_client::SimpleRuntimeVersion; + +impl CliChain for Polkadot { + const RUNTIME_VERSION: Option = None; +} + +impl CliChain for BridgeHubPolkadot { + // TODO: fix me (https://github.com/paritytech/parity-bridges-common/issues/1945) + const RUNTIME_VERSION: Option = + Some(SimpleRuntimeVersion { spec_version: 4242, transaction_version: 42 }); +} diff --git a/relays/bin-substrate/src/chains/rialto.rs b/relays/bin-substrate/src/chains/rialto.rs new file mode 100644 index 00000000000..34a448ae4cb --- /dev/null +++ b/relays/bin-substrate/src/chains/rialto.rs @@ -0,0 +1,39 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rialto chain specification for CLI. + +use crate::cli::{encode_message::CliEncodeMessage, CliChain}; +use bp_runtime::EncodedOrDecodedCall; +use relay_rialto_client::Rialto; +use relay_substrate_client::SimpleRuntimeVersion; + +impl CliEncodeMessage for Rialto { + fn encode_execute_xcm( + message: xcm::VersionedXcm, + ) -> anyhow::Result> { + Ok(rialto_runtime::RuntimeCall::XcmPallet(rialto_runtime::XcmCall::execute { + message: Box::new(message), + max_weight: Self::estimate_execute_xcm_weight(), + }) + .into()) + } +} + +impl CliChain for Rialto { + const RUNTIME_VERSION: Option = + Some(SimpleRuntimeVersion::from_runtime_version(&rialto_runtime::VERSION)); +} diff --git a/relays/bin-substrate/src/chains/rialto_parachain.rs b/relays/bin-substrate/src/chains/rialto_parachain.rs new file mode 100644 index 00000000000..8ea2c1ffd43 --- /dev/null +++ b/relays/bin-substrate/src/chains/rialto_parachain.rs @@ -0,0 +1,42 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rialto parachain specification for CLI. + +use crate::cli::{encode_message::CliEncodeMessage, CliChain}; +use bp_runtime::EncodedOrDecodedCall; +use relay_rialto_parachain_client::RialtoParachain; +use relay_substrate_client::SimpleRuntimeVersion; + +impl CliEncodeMessage for RialtoParachain { + fn encode_execute_xcm( + message: xcm::VersionedXcm, + ) -> anyhow::Result> { + type RuntimeCall = relay_rialto_parachain_client::RuntimeCall; + type XcmCall = relay_rialto_parachain_client::runtime_types::pallet_xcm::pallet::Call; + + let xcm_call = XcmCall::execute { + message: Box::new(unsafe { std::mem::transmute(message) }), + max_weight: Self::estimate_execute_xcm_weight(), + }; + + Ok(RuntimeCall::PolkadotXcm(xcm_call).into()) + } +} + +impl CliChain for RialtoParachain { + const RUNTIME_VERSION: Option = None; +} diff --git a/relays/bin-substrate/src/chains/rococo.rs b/relays/bin-substrate/src/chains/rococo.rs new file mode 100644 index 00000000000..c0041c53ddb --- /dev/null +++ b/relays/bin-substrate/src/chains/rococo.rs @@ -0,0 +1,31 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Rococo + Rococo parachains specification for CLI. + +use crate::cli::CliChain; +use relay_bridge_hub_rococo_client::BridgeHubRococo; +use relay_rococo_client::Rococo; +use relay_substrate_client::SimpleRuntimeVersion; + +impl CliChain for Rococo { + const RUNTIME_VERSION: Option = None; +} + +impl CliChain for BridgeHubRococo { + const RUNTIME_VERSION: Option = + Some(SimpleRuntimeVersion { spec_version: 9381, transaction_version: 2 }); +} diff --git a/relays/bin-substrate/src/chains/westend.rs b/relays/bin-substrate/src/chains/westend.rs new file mode 100644 index 00000000000..9cf639e9874 --- /dev/null +++ b/relays/bin-substrate/src/chains/westend.rs @@ -0,0 +1,29 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Westend chain specification for CLI. + +use crate::cli::CliChain; +use relay_substrate_client::SimpleRuntimeVersion; +use relay_westend_client::{Westend, Westmint}; + +impl CliChain for Westend { + const RUNTIME_VERSION: Option = None; +} + +impl CliChain for Westmint { + const RUNTIME_VERSION: Option = None; +} diff --git a/relays/bin-substrate/src/chains/wococo.rs b/relays/bin-substrate/src/chains/wococo.rs new file mode 100644 index 00000000000..73b3884c289 --- /dev/null +++ b/relays/bin-substrate/src/chains/wococo.rs @@ -0,0 +1,31 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Wococo + Wococo parachains specification for CLI. + +use crate::cli::CliChain; +use relay_bridge_hub_wococo_client::BridgeHubWococo; +use relay_substrate_client::SimpleRuntimeVersion; +use relay_wococo_client::Wococo; + +impl CliChain for Wococo { + const RUNTIME_VERSION: Option = None; +} + +impl CliChain for BridgeHubWococo { + const RUNTIME_VERSION: Option = + Some(SimpleRuntimeVersion { spec_version: 9381, transaction_version: 2 }); +} diff --git a/relays/bin-substrate/src/cli/bridge.rs b/relays/bin-substrate/src/cli/bridge.rs new file mode 100644 index 00000000000..62c71183a48 --- /dev/null +++ b/relays/bin-substrate/src/cli/bridge.rs @@ -0,0 +1,87 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::cli::CliChain; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use relay_substrate_client::{Chain, ChainWithTransactions, Parachain, RelayChain}; +use strum::{EnumString, EnumVariantNames}; +use substrate_relay_helper::{ + finality::SubstrateFinalitySyncPipeline, messages_lane::SubstrateMessageLane, + parachains::SubstrateParachainsPipeline, +}; + +#[derive(Debug, PartialEq, Eq, EnumString, EnumVariantNames)] +#[strum(serialize_all = "kebab_case")] +/// Supported full bridges (headers + messages). +pub enum FullBridge { + MillauToRialto, + RialtoToMillau, + MillauToRialtoParachain, + RialtoParachainToMillau, + BridgeHubRococoToBridgeHubWococo, + BridgeHubWococoToBridgeHubRococo, + BridgeHubKusamaToBridgeHubPolkadot, + BridgeHubPolkadotToBridgeHubKusama, +} + +/// Minimal bridge representation that can be used from the CLI. +/// It connects a source chain to a target chain. +pub trait CliBridgeBase: Sized { + /// The source chain. + type Source: Chain + CliChain; + /// The target chain. + type Target: ChainWithTransactions + CliChain; +} + +/// Bridge representation that can be used from the CLI for relaying headers +/// from a relay chain to a relay chain. +pub trait RelayToRelayHeadersCliBridge: CliBridgeBase { + /// Finality proofs synchronization pipeline. + type Finality: SubstrateFinalitySyncPipeline< + SourceChain = Self::Source, + TargetChain = Self::Target, + >; +} + +/// Bridge representation that can be used from the CLI for relaying headers +/// from a parachain to a relay chain. +pub trait ParachainToRelayHeadersCliBridge: CliBridgeBase +where + Self::Source: Parachain, +{ + // The `CliBridgeBase` type represents the parachain in this situation. + // We need to add an extra type for the relay chain. + type SourceRelay: Chain + + CliChain + + RelayChain; + /// Finality proofs synchronization pipeline (source parachain -> target). + type ParachainFinality: SubstrateParachainsPipeline< + SourceRelayChain = Self::SourceRelay, + SourceParachain = Self::Source, + TargetChain = Self::Target, + >; + /// Finality proofs synchronization pipeline (source relay chain -> target). + type RelayFinality: SubstrateFinalitySyncPipeline< + SourceChain = Self::SourceRelay, + TargetChain = Self::Target, + >; +} + +/// Bridge representation that can be used from the CLI for relaying messages. +pub trait MessagesCliBridge: CliBridgeBase { + /// The Source -> Destination messages synchronization pipeline. + type MessagesLane: SubstrateMessageLane; +} diff --git a/relays/bin-substrate/src/cli/chain_schema.rs b/relays/bin-substrate/src/cli/chain_schema.rs new file mode 100644 index 00000000000..bbc95d7dcdb --- /dev/null +++ b/relays/bin-substrate/src/cli/chain_schema.rs @@ -0,0 +1,370 @@ +// Copyright 2019-2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use relay_substrate_client::{AccountKeyPairOf, ChainWithTransactions}; +use structopt::StructOpt; +use strum::{EnumString, EnumVariantNames}; + +use crate::cli::CliChain; +pub use relay_substrate_client::{ChainRuntimeVersion, SimpleRuntimeVersion}; +use substrate_relay_helper::TransactionParams; + +#[doc = "Runtime version params."] +#[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy, EnumString, EnumVariantNames)] +pub enum RuntimeVersionType { + /// Auto query version from chain + Auto, + /// Custom `spec_version` and `transaction_version` + Custom, + /// Read version from bundle dependencies directly. + Bundle, +} + +/// Create chain-specific set of runtime version parameters. +#[macro_export] +macro_rules! declare_chain_runtime_version_params_cli_schema { + ($chain:ident, $chain_prefix:ident) => { + bp_runtime::paste::item! { + #[doc = $chain " runtime version params."] + #[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy)] + pub struct [<$chain RuntimeVersionParams>] { + #[doc = "The type of runtime version for chain " $chain] + #[structopt(long, default_value = "Bundle")] + pub [<$chain_prefix _version_mode>]: RuntimeVersionType, + #[doc = "The custom sepc_version for chain " $chain] + #[structopt(long)] + pub [<$chain_prefix _spec_version>]: Option, + #[doc = "The custom transaction_version for chain " $chain] + #[structopt(long)] + pub [<$chain_prefix _transaction_version>]: Option, + } + + impl [<$chain RuntimeVersionParams>] { + /// Converts self into `ChainRuntimeVersion`. + pub fn into_runtime_version( + self, + bundle_runtime_version: Option, + ) -> anyhow::Result { + Ok(match self.[<$chain_prefix _version_mode>] { + RuntimeVersionType::Auto => ChainRuntimeVersion::Auto, + RuntimeVersionType::Custom => { + let custom_spec_version = self.[<$chain_prefix _spec_version>] + .ok_or_else(|| anyhow::Error::msg(format!("The {}-spec-version is required when choose custom mode", stringify!($chain_prefix))))?; + let custom_transaction_version = self.[<$chain_prefix _transaction_version>] + .ok_or_else(|| anyhow::Error::msg(format!("The {}-transaction-version is required when choose custom mode", stringify!($chain_prefix))))?; + ChainRuntimeVersion::Custom( + SimpleRuntimeVersion { + spec_version: custom_spec_version, + transaction_version: custom_transaction_version + } + ) + }, + RuntimeVersionType::Bundle => match bundle_runtime_version { + Some(runtime_version) => ChainRuntimeVersion::Custom(runtime_version), + None => ChainRuntimeVersion::Auto + }, + }) + } + } + } + }; +} + +/// Create chain-specific set of runtime version parameters. +#[macro_export] +macro_rules! declare_chain_connection_params_cli_schema { + ($chain:ident, $chain_prefix:ident) => { + bp_runtime::paste::item! { + #[doc = $chain " connection params."] + #[derive(StructOpt, Debug, PartialEq, Eq, Clone)] + pub struct [<$chain ConnectionParams>] { + #[doc = "Connect to " $chain " node at given host."] + #[structopt(long, default_value = "127.0.0.1")] + pub [<$chain_prefix _host>]: String, + #[doc = "Connect to " $chain " node websocket server at given port."] + #[structopt(long, default_value = "9944")] + pub [<$chain_prefix _port>]: u16, + #[doc = "Use secure websocket connection."] + #[structopt(long)] + pub [<$chain_prefix _secure>]: bool, + #[doc = "Custom runtime version"] + #[structopt(flatten)] + pub [<$chain_prefix _runtime_version>]: [<$chain RuntimeVersionParams>], + } + + impl [<$chain ConnectionParams>] { + /// Convert connection params into Substrate client. + #[allow(dead_code)] + pub async fn into_client( + self, + ) -> anyhow::Result> { + let chain_runtime_version = self + .[<$chain_prefix _runtime_version>] + .into_runtime_version(Chain::RUNTIME_VERSION)?; + Ok(relay_substrate_client::Client::new(relay_substrate_client::ConnectionParams { + host: self.[<$chain_prefix _host>], + port: self.[<$chain_prefix _port>], + secure: self.[<$chain_prefix _secure>], + chain_runtime_version, + }) + .await + ) + } + } + } + }; +} + +/// Helper trait to override transaction parameters differently. +pub trait TransactionParamsProvider { + /// Returns `true` if transaction parameters are defined by this provider. + fn is_defined(&self) -> bool; + /// Returns transaction parameters. + fn transaction_params( + &self, + ) -> anyhow::Result>>; + + /// Returns transaction parameters, defined by `self` provider or, if they're not defined, + /// defined by `other` provider. + fn transaction_params_or( + &self, + other: &T, + ) -> anyhow::Result>> { + if self.is_defined() { + self.transaction_params::() + } else { + other.transaction_params::() + } + } +} + +/// Create chain-specific set of signing parameters. +#[macro_export] +macro_rules! declare_chain_signing_params_cli_schema { + ($chain:ident, $chain_prefix:ident) => { + bp_runtime::paste::item! { + #[doc = $chain " signing params."] + #[derive(StructOpt, Debug, PartialEq, Eq, Clone)] + pub struct [<$chain SigningParams>] { + #[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."] + #[structopt(long)] + pub [<$chain_prefix _signer>]: Option, + #[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."] + #[structopt(long)] + pub [<$chain_prefix _signer_password>]: Option, + + #[doc = "Path to the file, that contains SURI of secret key to use when transactions are submitted to the " $chain " node. Can be overridden with " $chain_prefix "_signer option."] + #[structopt(long)] + pub [<$chain_prefix _signer_file>]: Option, + #[doc = "Path to the file, that password for the SURI of secret key to use when transactions are submitted to the " $chain " node. Can be overridden with " $chain_prefix "_signer_password option."] + #[structopt(long)] + pub [<$chain_prefix _signer_password_file>]: Option, + + #[doc = "Transactions mortality period, in blocks. MUST be a power of two in [4; 65536] range. MAY NOT be larger than `BlockHashCount` parameter of the chain system module."] + #[structopt(long)] + pub [<$chain_prefix _transactions_mortality>]: Option, + } + + impl [<$chain SigningParams>] { + /// Return transactions mortality. + #[allow(dead_code)] + pub fn transactions_mortality(&self) -> anyhow::Result> { + self.[<$chain_prefix _transactions_mortality>] + .map(|transactions_mortality| { + if !(4..=65536).contains(&transactions_mortality) + || !transactions_mortality.is_power_of_two() + { + Err(anyhow::format_err!( + "Transactions mortality {} is not a power of two in a [4; 65536] range", + transactions_mortality, + )) + } else { + Ok(transactions_mortality) + } + }) + .transpose() + } + + /// Parse signing params into chain-specific KeyPair. + #[allow(dead_code)] + pub fn to_keypair(&self) -> anyhow::Result> { + let suri = match (self.[<$chain_prefix _signer>].as_ref(), self.[<$chain_prefix _signer_file>].as_ref()) { + (Some(suri), _) => suri.to_owned(), + (None, Some(suri_file)) => std::fs::read_to_string(suri_file) + .map_err(|err| anyhow::format_err!( + "Failed to read SURI from file {:?}: {}", + suri_file, + err, + ))?, + (None, None) => return Err(anyhow::format_err!( + "One of options must be specified: '{}' or '{}'", + stringify!([<$chain_prefix _signer>]), + stringify!([<$chain_prefix _signer_file>]), + )), + }; + + let suri_password = match ( + self.[<$chain_prefix _signer_password>].as_ref(), + self.[<$chain_prefix _signer_password_file>].as_ref(), + ) { + (Some(suri_password), _) => Some(suri_password.to_owned()), + (None, Some(suri_password_file)) => std::fs::read_to_string(suri_password_file) + .map(Some) + .map_err(|err| anyhow::format_err!( + "Failed to read SURI password from file {:?}: {}", + suri_password_file, + err, + ))?, + _ => None, + }; + + use sp_core::crypto::Pair; + + AccountKeyPairOf::::from_string( + &suri, + suri_password.as_deref() + ).map_err(|e| anyhow::format_err!("{:?}", e)) + } + } + + #[allow(dead_code)] + impl TransactionParamsProvider for [<$chain SigningParams>] { + fn is_defined(&self) -> bool { + self.[<$chain_prefix _signer>].is_some() || self.[<$chain_prefix _signer_file>].is_some() + } + + fn transaction_params(&self) -> anyhow::Result>> { + Ok(TransactionParams { + mortality: self.transactions_mortality()?, + signer: self.to_keypair::()?, + }) + } + } + } + }; +} + +/// Create chain-specific set of configuration objects: connection parameters, +/// signing parameters and bridge initialization parameters. +#[macro_export] +macro_rules! declare_chain_cli_schema { + ($chain:ident, $chain_prefix:ident) => { + $crate::declare_chain_runtime_version_params_cli_schema!($chain, $chain_prefix); + $crate::declare_chain_connection_params_cli_schema!($chain, $chain_prefix); + $crate::declare_chain_signing_params_cli_schema!($chain, $chain_prefix); + }; +} + +declare_chain_cli_schema!(Source, source); +declare_chain_cli_schema!(Target, target); +declare_chain_cli_schema!(Relaychain, relaychain); +declare_chain_cli_schema!(Parachain, parachain); + +#[cfg(test)] +mod tests { + use super::*; + use sp_core::Pair; + + #[test] + fn reads_suri_from_file() { + const ALICE: &str = "//Alice"; + const BOB: &str = "//Bob"; + const ALICE_PASSWORD: &str = "alice_password"; + const BOB_PASSWORD: &str = "bob_password"; + + let alice: sp_core::sr25519::Pair = Pair::from_string(ALICE, Some(ALICE_PASSWORD)).unwrap(); + let bob: sp_core::sr25519::Pair = Pair::from_string(BOB, Some(BOB_PASSWORD)).unwrap(); + let bob_with_alice_password = + sp_core::sr25519::Pair::from_string(BOB, Some(ALICE_PASSWORD)).unwrap(); + + let temp_dir = tempfile::tempdir().unwrap(); + let mut suri_file_path = temp_dir.path().to_path_buf(); + let mut password_file_path = temp_dir.path().to_path_buf(); + suri_file_path.push("suri"); + password_file_path.push("password"); + std::fs::write(&suri_file_path, BOB.as_bytes()).unwrap(); + std::fs::write(&password_file_path, BOB_PASSWORD.as_bytes()).unwrap(); + + // when both seed and password are read from file + assert_eq!( + TargetSigningParams { + target_signer: Some(ALICE.into()), + target_signer_password: Some(ALICE_PASSWORD.into()), + + target_signer_file: None, + target_signer_password_file: None, + + target_transactions_mortality: None, + } + .to_keypair::() + .map(|p| p.public()) + .map_err(drop), + Ok(alice.public()), + ); + + // when both seed and password are read from file + assert_eq!( + TargetSigningParams { + target_signer: None, + target_signer_password: None, + + target_signer_file: Some(suri_file_path.clone()), + target_signer_password_file: Some(password_file_path.clone()), + + target_transactions_mortality: None, + } + .to_keypair::() + .map(|p| p.public()) + .map_err(drop), + Ok(bob.public()), + ); + + // when password are is overriden by cli option + assert_eq!( + TargetSigningParams { + target_signer: None, + target_signer_password: Some(ALICE_PASSWORD.into()), + + target_signer_file: Some(suri_file_path.clone()), + target_signer_password_file: Some(password_file_path.clone()), + + target_transactions_mortality: None, + } + .to_keypair::() + .map(|p| p.public()) + .map_err(drop), + Ok(bob_with_alice_password.public()), + ); + + // when both seed and password are overriden by cli options + assert_eq!( + TargetSigningParams { + target_signer: Some(ALICE.into()), + target_signer_password: Some(ALICE_PASSWORD.into()), + + target_signer_file: Some(suri_file_path), + target_signer_password_file: Some(password_file_path), + + target_transactions_mortality: None, + } + .to_keypair::() + .map(|p| p.public()) + .map_err(drop), + Ok(alice.public()), + ); + } +} diff --git a/relays/bin-substrate/src/cli/encode_message.rs b/relays/bin-substrate/src/cli/encode_message.rs new file mode 100644 index 00000000000..9abf8b2df6d --- /dev/null +++ b/relays/bin-substrate/src/cli/encode_message.rs @@ -0,0 +1,151 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::cli::{ExplicitOrMaximal, HexBytes}; +use bp_runtime::EncodedOrDecodedCall; +use codec::Encode; +use frame_support::weights::Weight; +use relay_substrate_client::Chain; +use structopt::StructOpt; + +/// All possible messages that may be delivered to generic Substrate chain. +/// +/// Note this enum may be used in the context of both Source (as part of `encode-call`) +/// and Target chain (as part of `encode-message/send-message`). +#[derive(StructOpt, Debug, PartialEq, Eq)] +pub enum Message { + /// Raw bytes for the message. + Raw { + /// Raw message bytes. + data: HexBytes, + }, + /// Message with given size. + Sized { + /// Sized of the message. + size: ExplicitOrMaximal, + }, +} + +/// Raw, SCALE-encoded message payload used in expected deployment. +pub type RawMessage = Vec; + +pub trait CliEncodeMessage: Chain { + /// Encode an `execute` XCM call of the XCM pallet. + fn encode_execute_xcm( + message: xcm::VersionedXcm, + ) -> anyhow::Result>; + + /// Estimate value of `max_weight` argument for the `execute` XCM call of the XCM pallet. + fn estimate_execute_xcm_weight() -> Weight { + // we are only executing XCM on our testnets and 1/100 of max extrinsic weight is ok + Self::max_extrinsic_weight() / 100 + } +} + +/// Encode message payload passed through CLI flags. +pub(crate) fn encode_message( + message: &Message, +) -> anyhow::Result { + Ok(match message { + Message::Raw { ref data } => data.0.clone(), + Message::Sized { ref size } => { + let expected_xcm_size = match *size { + ExplicitOrMaximal::Explicit(size) => size, + ExplicitOrMaximal::Maximal => compute_maximal_message_size( + Source::max_extrinsic_size(), + Target::max_extrinsic_size(), + ), + }; + + // there's no way to craft XCM of the given size - we'll be using `ExpectPallet` + // instruction, which has byte vector inside + let mut current_vec_size = expected_xcm_size; + let xcm = loop { + let xcm = xcm::VersionedXcm::<()>::V3( + vec![xcm::v3::Instruction::ExpectPallet { + index: 0, + name: vec![42; current_vec_size as usize], + module_name: vec![], + crate_major: 0, + min_crate_minor: 0, + }] + .into(), + ); + if xcm.encode().len() <= expected_xcm_size as usize { + break xcm + } + + current_vec_size -= 1; + }; + xcm.encode() + }, + }) +} + +/// Compute maximal message size, given max extrinsic size at source and target chains. +pub(crate) fn compute_maximal_message_size( + maximal_source_extrinsic_size: u32, + maximal_target_extrinsic_size: u32, +) -> u32 { + // assume that both signed extensions and other arguments fit 1KB + let service_tx_bytes_on_source_chain = 1024; + let maximal_source_extrinsic_size = + maximal_source_extrinsic_size - service_tx_bytes_on_source_chain; + let maximal_message_size = + bridge_runtime_common::messages::target::maximal_incoming_message_size( + maximal_target_extrinsic_size, + ); + if maximal_message_size > maximal_source_extrinsic_size { + maximal_source_extrinsic_size + } else { + maximal_message_size + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::send_message::decode_xcm; + use bp_runtime::Chain; + use relay_millau_client::Millau; + use relay_rialto_client::Rialto; + + #[test] + fn encode_explicit_size_message_works() { + let msg = encode_message::(&Message::Sized { + size: ExplicitOrMaximal::Explicit(100), + }) + .unwrap(); + assert_eq!(msg.len(), 100); + // check that it decodes to valid xcm + let _ = decode_xcm::<()>(msg).unwrap(); + } + + #[test] + fn encode_maximal_size_message_works() { + let maximal_size = compute_maximal_message_size( + Rialto::max_extrinsic_size(), + Millau::max_extrinsic_size(), + ); + + let msg = + encode_message::(&Message::Sized { size: ExplicitOrMaximal::Maximal }) + .unwrap(); + assert_eq!(msg.len(), maximal_size as usize); + // check that it decodes to valid xcm + let _ = decode_xcm::<()>(msg).unwrap(); + } +} diff --git a/relays/bin-substrate/src/cli/init_bridge.rs b/relays/bin-substrate/src/cli/init_bridge.rs new file mode 100644 index 00000000000..95cd02f6c6f --- /dev/null +++ b/relays/bin-substrate/src/cli/init_bridge.rs @@ -0,0 +1,221 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use async_trait::async_trait; +use codec::Encode; + +use crate::{ + bridges::{ + rialto_millau::{ + millau_headers_to_rialto::MillauToRialtoCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + }, + rialto_parachain_millau::millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rococo_wococo::{ + rococo_headers_to_bridge_hub_wococo::RococoToBridgeHubWococoCliBridge, + wococo_headers_to_bridge_hub_rococo::WococoToBridgeHubRococoCliBridge, + }, + westend_millau::westend_headers_to_millau::WestendToMillauCliBridge, + }, + cli::{bridge::CliBridgeBase, chain_schema::*}, +}; +use bp_runtime::Chain as ChainBase; +use relay_substrate_client::{AccountKeyPairOf, Chain, UnsignedTransaction}; +use sp_core::Pair; +use structopt::StructOpt; +use strum::{EnumString, EnumVariantNames, VariantNames}; +use substrate_relay_helper::finality::engine::{Engine, Grandpa as GrandpaFinalityEngine}; + +/// Initialize bridge pallet. +#[derive(StructOpt)] +pub struct InitBridge { + /// A bridge instance to initialize. + #[structopt(possible_values = InitBridgeName::VARIANTS, case_insensitive = true)] + bridge: InitBridgeName, + #[structopt(flatten)] + source: SourceConnectionParams, + #[structopt(flatten)] + target: TargetConnectionParams, + #[structopt(flatten)] + target_sign: TargetSigningParams, + /// Generates all required data, but does not submit extrinsic + #[structopt(long)] + dry_run: bool, +} + +#[derive(Debug, EnumString, EnumVariantNames)] +#[strum(serialize_all = "kebab_case")] +/// Bridge to initialize. +pub enum InitBridgeName { + MillauToRialto, + RialtoToMillau, + WestendToMillau, + MillauToRialtoParachain, + RococoToBridgeHubWococo, + WococoToBridgeHubRococo, +} + +#[async_trait] +trait BridgeInitializer: CliBridgeBase +where + ::AccountId: From< as Pair>::Public>, +{ + type Engine: Engine; + + /// Get the encoded call to init the bridge. + fn encode_init_bridge( + init_data: >::InitializationData, + ) -> ::Call; + + /// Initialize the bridge. + async fn init_bridge(data: InitBridge) -> anyhow::Result<()> { + let source_client = data.source.into_client::().await?; + let target_client = data.target.into_client::().await?; + let target_sign = data.target_sign.to_keypair::()?; + let dry_run = data.dry_run; + + substrate_relay_helper::finality::initialize::initialize::( + source_client, + target_client.clone(), + target_sign, + move |transaction_nonce, initialization_data| { + let call = Self::encode_init_bridge(initialization_data); + log::info!( + target: "bridge", + "Initialize bridge call encoded as hex string: {:?}", + format!("0x{}", hex::encode(call.encode())) + ); + Ok(UnsignedTransaction::new(call.into(), transaction_nonce)) + }, + dry_run, + ) + .await; + + Ok(()) + } +} + +impl BridgeInitializer for MillauToRialtoCliBridge { + type Engine = GrandpaFinalityEngine; + + fn encode_init_bridge( + init_data: >::InitializationData, + ) -> ::Call { + rialto_runtime::SudoCall::sudo { + call: Box::new(rialto_runtime::BridgeGrandpaCall::initialize { init_data }.into()), + } + .into() + } +} + +impl BridgeInitializer for MillauToRialtoParachainCliBridge { + type Engine = GrandpaFinalityEngine; + + fn encode_init_bridge( + init_data: >::InitializationData, + ) -> ::Call { + type RuntimeCall = relay_rialto_parachain_client::RuntimeCall; + type BridgeGrandpaCall = relay_rialto_parachain_client::BridgeGrandpaCall; + type SudoCall = relay_rialto_parachain_client::SudoCall; + + let initialize_call = + RuntimeCall::BridgeMillauGrandpa(BridgeGrandpaCall::initialize { init_data }); + + RuntimeCall::Sudo(SudoCall::sudo { call: Box::new(initialize_call) }) + } +} + +impl BridgeInitializer for RialtoToMillauCliBridge { + type Engine = GrandpaFinalityEngine; + + fn encode_init_bridge( + init_data: >::InitializationData, + ) -> ::Call { + let initialize_call = millau_runtime::BridgeGrandpaCall::< + millau_runtime::Runtime, + millau_runtime::RialtoGrandpaInstance, + >::initialize { + init_data, + }; + millau_runtime::SudoCall::sudo { call: Box::new(initialize_call.into()) }.into() + } +} + +impl BridgeInitializer for WestendToMillauCliBridge { + type Engine = GrandpaFinalityEngine; + + fn encode_init_bridge( + init_data: >::InitializationData, + ) -> ::Call { + // at Westend -> Millau initialization we're not using sudo, because otherwise + // our deployments may fail, because we need to initialize both Rialto -> Millau + // and Westend -> Millau bridge. => since there's single possible sudo account, + // one of transaction may fail with duplicate nonce error + millau_runtime::BridgeGrandpaCall::< + millau_runtime::Runtime, + millau_runtime::WestendGrandpaInstance, + >::initialize { + init_data, + } + .into() + } +} + +impl BridgeInitializer for RococoToBridgeHubWococoCliBridge { + type Engine = GrandpaFinalityEngine; + + fn encode_init_bridge( + init_data: >::InitializationData, + ) -> ::Call { + relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoGrandpa( + relay_bridge_hub_wococo_client::runtime::BridgeRococoGrandpaCall::initialize { + init_data, + }, + ) + } +} + +impl BridgeInitializer for WococoToBridgeHubRococoCliBridge { + type Engine = GrandpaFinalityEngine; + + fn encode_init_bridge( + init_data: >::InitializationData, + ) -> ::Call { + relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoGrandpa( + relay_bridge_hub_rococo_client::runtime::BridgeWococoGrandpaCall::initialize { + init_data, + }, + ) + } +} + +impl InitBridge { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + match self.bridge { + InitBridgeName::MillauToRialto => MillauToRialtoCliBridge::init_bridge(self), + InitBridgeName::RialtoToMillau => RialtoToMillauCliBridge::init_bridge(self), + InitBridgeName::WestendToMillau => WestendToMillauCliBridge::init_bridge(self), + InitBridgeName::MillauToRialtoParachain => + MillauToRialtoParachainCliBridge::init_bridge(self), + InitBridgeName::RococoToBridgeHubWococo => + RococoToBridgeHubWococoCliBridge::init_bridge(self), + InitBridgeName::WococoToBridgeHubRococo => + WococoToBridgeHubRococoCliBridge::init_bridge(self), + } + .await + } +} diff --git a/relays/bin-substrate/src/cli/mod.rs b/relays/bin-substrate/src/cli/mod.rs new file mode 100644 index 00000000000..a5b90744067 --- /dev/null +++ b/relays/bin-substrate/src/cli/mod.rs @@ -0,0 +1,355 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Deal with CLI args of substrate-to-substrate relay. + +use std::convert::TryInto; + +use async_std::prelude::*; +use codec::{Decode, Encode}; +use futures::{select, FutureExt}; +use rbtag::BuildInfo; +use signal_hook::consts::*; +use signal_hook_async_std::Signals; +use structopt::{clap::arg_enum, StructOpt}; +use strum::{EnumString, EnumVariantNames}; + +use bp_messages::LaneId; +use relay_substrate_client::SimpleRuntimeVersion; + +pub(crate) mod bridge; +pub(crate) mod encode_message; +pub(crate) mod send_message; + +mod chain_schema; +mod init_bridge; +mod register_parachain; +mod relay_headers; +mod relay_headers_and_messages; +mod relay_messages; +mod relay_parachains; +mod resubmit_transactions; + +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "bridge"; + +/// Parse relay CLI args. +pub fn parse_args() -> Command { + Command::from_args() +} + +/// Substrate-to-Substrate bridge utilities. +#[derive(StructOpt)] +#[structopt(about = "Substrate-to-Substrate relay")] +pub enum Command { + /// Start headers relay between two chains. + /// + /// The on-chain bridge component should have been already initialized with + /// `init-bridge` sub-command. + RelayHeaders(relay_headers::RelayHeaders), + /// Start messages relay between two chains. + /// + /// Ties up to `Messages` pallets on both chains and starts relaying messages. + /// Requires the header relay to be already running. + RelayMessages(relay_messages::RelayMessages), + /// Start headers and messages relay between two Substrate chains. + /// + /// This high-level relay internally starts four low-level relays: two `RelayHeaders` + /// and two `RelayMessages` relays. Headers are only relayed when they are required by + /// the message relays - i.e. when there are messages or confirmations that needs to be + /// relayed between chains. + RelayHeadersAndMessages(Box), + /// Initialize on-chain bridge pallet with current header data. + /// + /// Sends initialization transaction to bootstrap the bridge with current finalized block data. + InitBridge(init_bridge::InitBridge), + /// Send custom message over the bridge. + /// + /// Allows interacting with the bridge by sending messages over `Messages` component. + /// The message is being sent to the source chain, delivered to the target chain and dispatched + /// there. + SendMessage(send_message::SendMessage), + /// Resubmit transactions with increased tip if they are stalled. + ResubmitTransactions(resubmit_transactions::ResubmitTransactions), + /// Register parachain. + RegisterParachain(register_parachain::RegisterParachain), + /// + RelayParachains(relay_parachains::RelayParachains), +} + +impl Command { + // Initialize logger depending on the command. + fn init_logger(&self) { + use relay_utils::initialize::{initialize_logger, initialize_relay}; + + match self { + Self::RelayHeaders(_) | + Self::RelayMessages(_) | + Self::RelayHeadersAndMessages(_) | + Self::InitBridge(_) => { + initialize_relay(); + }, + _ => { + initialize_logger(false); + }, + } + } + + /// Run the command. + async fn do_run(self) -> anyhow::Result<()> { + match self { + Self::RelayHeaders(arg) => arg.run().await?, + Self::RelayMessages(arg) => arg.run().await?, + Self::RelayHeadersAndMessages(arg) => arg.run().await?, + Self::InitBridge(arg) => arg.run().await?, + Self::SendMessage(arg) => arg.run().await?, + Self::ResubmitTransactions(arg) => arg.run().await?, + Self::RegisterParachain(arg) => arg.run().await?, + Self::RelayParachains(arg) => arg.run().await?, + } + Ok(()) + } + + /// Run the command. + pub async fn run(self) { + self.init_logger(); + + let exit_signals = match Signals::new([SIGINT, SIGTERM]) { + Ok(signals) => signals, + Err(e) => { + log::error!(target: LOG_TARGET, "Could not register exit signals: {}", e); + return + }, + }; + let run = self.do_run().fuse(); + futures::pin_mut!(exit_signals, run); + + select! { + signal = exit_signals.next().fuse() => { + log::info!(target: LOG_TARGET, "Received exit signal {:?}", signal); + }, + result = run => { + if let Err(e) = result { + log::error!(target: LOG_TARGET, "substrate-relay: {}", e); + } + }, + } + } +} + +arg_enum! { + #[derive(Debug)] + /// The origin to use when dispatching the message on the target chain. + /// + /// - `Target` uses account existing on the target chain (requires target private key). + /// - `Origin` uses account derived from the source-chain account. + pub enum Origins { + Target, + Source, + } +} + +/// Generic balance type. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Balance(pub u128); + +impl std::fmt::Display for Balance { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + use num_format::{Locale, ToFormattedString}; + write!(fmt, "{}", self.0.to_formatted_string(&Locale::en)) + } +} + +impl std::str::FromStr for Balance { + type Err = ::Err; + + fn from_str(s: &str) -> Result { + Ok(Self(s.parse()?)) + } +} + +impl Balance { + /// Cast balance to `u64` type, panicking if it's too large. + pub fn cast(&self) -> u64 { + self.0.try_into().expect("Balance is too high for this chain.") + } +} + +// Bridge-supported network definition. +/// +/// Used to abstract away CLI commands. +pub trait CliChain: relay_substrate_client::Chain { + /// Current version of the chain runtime, known to relay. + /// + /// can be `None` if relay is not going to submit transactions to that chain. + const RUNTIME_VERSION: Option; +} + +/// Lane id. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct HexLaneId(pub [u8; 4]); + +impl From for LaneId { + fn from(lane_id: HexLaneId) -> LaneId { + LaneId(lane_id.0) + } +} + +impl std::str::FromStr for HexLaneId { + type Err = hex::FromHexError; + + fn from_str(s: &str) -> Result { + let mut lane_id = [0u8; 4]; + hex::decode_to_slice(s, &mut lane_id)?; + Ok(HexLaneId(lane_id)) + } +} + +/// Nicer formatting for raw bytes vectors. +#[derive(Default, Encode, Decode, PartialEq, Eq)] +pub struct HexBytes(pub Vec); + +impl std::str::FromStr for HexBytes { + type Err = hex::FromHexError; + + fn from_str(s: &str) -> Result { + Ok(Self(hex::decode(s)?)) + } +} + +impl std::fmt::Debug for HexBytes { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "0x{self}") + } +} + +impl std::fmt::Display for HexBytes { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "{}", hex::encode(&self.0)) + } +} + +/// Prometheus metrics params. +#[derive(Clone, Debug, PartialEq, StructOpt)] +pub struct PrometheusParams { + /// Do not expose a Prometheus metric endpoint. + #[structopt(long)] + pub no_prometheus: bool, + /// Expose Prometheus endpoint at given interface. + #[structopt(long, default_value = "127.0.0.1")] + pub prometheus_host: String, + /// Expose Prometheus endpoint at given port. + #[structopt(long, default_value = "9616")] + pub prometheus_port: u16, +} + +/// Struct to get git commit info and build time. +#[derive(BuildInfo)] +struct SubstrateRelayBuildInfo; + +impl SubstrateRelayBuildInfo { + /// Get git commit in form ``. + pub fn get_git_commit() -> String { + // on gitlab we use images without git installed, so we can't use `rbtag` there + // locally we don't have `CI_*` env variables, so we can't rely on them + // => we are using `CI_*` env variables or else `rbtag` + let maybe_sha_from_ci = option_env!("CI_COMMIT_SHORT_SHA"); + maybe_sha_from_ci + .map(|short_sha| { + // we assume that on CI the copy is always clean + format!("{short_sha}-clean") + }) + .unwrap_or_else(|| SubstrateRelayBuildInfo.get_build_commit().into()) + } +} + +impl PrometheusParams { + /// Tries to convert CLI metrics params into metrics params, used by the relay. + pub fn into_metrics_params(self) -> anyhow::Result { + let metrics_address = if !self.no_prometheus { + Some(relay_utils::metrics::MetricsAddress { + host: self.prometheus_host, + port: self.prometheus_port, + }) + } else { + None + }; + + let relay_version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"); + let relay_commit = SubstrateRelayBuildInfo::get_git_commit(); + relay_utils::metrics::MetricsParams::new( + metrics_address, + relay_version.into(), + relay_commit, + ) + .map_err(|e| anyhow::format_err!("{:?}", e)) + } +} + +/// Either explicit or maximal allowed value. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ExplicitOrMaximal { + /// User has explicitly specified argument value. + Explicit(V), + /// Maximal allowed value for this argument. + Maximal, +} + +impl std::str::FromStr for ExplicitOrMaximal +where + V::Err: std::fmt::Debug, +{ + type Err = String; + + fn from_str(s: &str) -> Result { + if s.to_lowercase() == "max" { + return Ok(ExplicitOrMaximal::Maximal) + } + + V::from_str(s) + .map(ExplicitOrMaximal::Explicit) + .map_err(|e| format!("Failed to parse '{e:?}'. Expected 'max' or explicit value")) + } +} + +#[doc = "Runtime version params."] +#[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy, EnumString, EnumVariantNames)] +pub enum RuntimeVersionType { + /// Auto query version from chain + Auto, + /// Custom `spec_version` and `transaction_version` + Custom, + /// Read version from bundle dependencies directly. + Bundle, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn hex_bytes_display_matches_from_str_for_clap() { + // given + let hex = HexBytes(vec![1, 2, 3, 4]); + let display = format!("{hex}"); + + // when + let hex2: HexBytes = display.parse().unwrap(); + + // then + assert_eq!(hex.0, hex2.0); + } +} diff --git a/relays/bin-substrate/src/cli/register_parachain.rs b/relays/bin-substrate/src/cli/register_parachain.rs new file mode 100644 index 00000000000..320277590bc --- /dev/null +++ b/relays/bin-substrate/src/cli/register_parachain.rs @@ -0,0 +1,324 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::cli::{chain_schema::*, Balance}; + +use codec::Encode; +use frame_support::Twox64Concat; +use num_traits::Zero; +use polkadot_parachain::primitives::{ + HeadData as ParaHeadData, Id as ParaId, ValidationCode as ParaValidationCode, +}; +use polkadot_runtime_common::{ + paras_registrar::Call as ParaRegistrarCall, slots::Call as ParaSlotsCall, +}; +use polkadot_runtime_parachains::paras::ParaLifecycle; +use relay_substrate_client::{AccountIdOf, CallOf, Chain, Client, UnsignedTransaction}; +use relay_utils::{TrackedTransactionStatus, TransactionTracker}; +use rialto_runtime::SudoCall; +use sp_core::{ + storage::{well_known_keys::CODE, StorageKey}, + Pair, +}; +use structopt::StructOpt; +use strum::{EnumString, EnumVariantNames, VariantNames}; + +/// Name of the `NextFreeParaId` value in the `polkadot_runtime_common::paras_registrar` pallet. +const NEXT_FREE_PARA_ID_STORAGE_NAME: &str = "NextFreeParaId"; +/// Name of the `ParaLifecycles` map in the `polkadot_runtime_parachains::paras` pallet. +const PARAS_LIFECYCLES_STORAGE_NAME: &str = "ParaLifecycles"; + +/// Register parachain. +#[derive(StructOpt, Debug, PartialEq, Eq)] +pub struct RegisterParachain { + /// A parachain to register. + #[structopt(possible_values = Parachain::VARIANTS, case_insensitive = true)] + parachain: Parachain, + /// Parachain deposit. + #[structopt(long, default_value = "0")] + deposit: Balance, + /// Lease begin. + #[structopt(long, default_value = "0")] + lease_begin: u32, + /// Lease end. + #[structopt(long, default_value = "256")] + lease_end: u32, + #[structopt(flatten)] + relay_connection: RelaychainConnectionParams, + #[structopt(flatten)] + relay_sign: RelaychainSigningParams, + #[structopt(flatten)] + para_connection: ParachainConnectionParams, +} + +/// Parachain to register. +#[derive(Debug, EnumString, EnumVariantNames, PartialEq, Eq)] +#[strum(serialize_all = "kebab_case")] +pub enum Parachain { + RialtoParachain, +} + +macro_rules! select_bridge { + ($bridge: expr, $generic: tt) => { + match $bridge { + Parachain::RialtoParachain => { + type Relaychain = relay_rialto_client::Rialto; + type Parachain = relay_rialto_parachain_client::RialtoParachain; + + use bp_rialto::{PARAS_PALLET_NAME, PARAS_REGISTRAR_PALLET_NAME}; + + $generic + }, + } + }; +} + +impl RegisterParachain { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + select_bridge!(self.parachain, { + let relay_client = self.relay_connection.into_client::().await?; + let relay_sign = self.relay_sign.to_keypair::()?; + let para_client = self.para_connection.into_client::().await?; + + // hopefully we're the only actor that is registering parachain right now + // => read next parachain id + let para_id_key = bp_runtime::storage_value_final_key( + PARAS_REGISTRAR_PALLET_NAME.as_bytes(), + NEXT_FREE_PARA_ID_STORAGE_NAME.as_bytes(), + ); + let para_id: ParaId = relay_client + .storage_value(StorageKey(para_id_key.to_vec()), None) + .await? + .unwrap_or(polkadot_primitives::v4::LOWEST_PUBLIC_ID) + .max(polkadot_primitives::v4::LOWEST_PUBLIC_ID); + log::info!(target: "bridge", "Going to reserve parachain id: {:?}", para_id); + + // step 1: reserve a parachain id + let relay_sudo_account: AccountIdOf = relay_sign.public().into(); + let reserve_parachain_id_call: CallOf = + ParaRegistrarCall::reserve {}.into(); + let reserve_result = relay_client + .submit_and_watch_signed_extrinsic(&relay_sign, move |_, transaction_nonce| { + Ok(UnsignedTransaction::new( + reserve_parachain_id_call.into(), + transaction_nonce, + )) + }) + .await? + .wait() + .await; + if reserve_result == TrackedTransactionStatus::Lost { + return Err(anyhow::format_err!( + "Failed to finalize `reserve-parachain-id` transaction" + )) + } + log::info!(target: "bridge", "Reserved parachain id: {:?}", para_id); + + // step 2: register parathread + let para_genesis_header = para_client.header_by_number(Zero::zero()).await?; + let para_code = para_client + .raw_storage_value(StorageKey(CODE.to_vec()), Some(para_genesis_header.hash())) + .await? + .ok_or_else(|| { + anyhow::format_err!("Cannot fetch validation code of {}", Parachain::NAME) + })? + .0; + log::info!( + target: "bridge", + "Going to register parachain {:?}: genesis len = {} code len = {}", + para_id, + para_genesis_header.encode().len(), + para_code.len(), + ); + let register_parathread_call: CallOf = ParaRegistrarCall::register { + id: para_id, + genesis_head: ParaHeadData(para_genesis_header.encode()), + validation_code: ParaValidationCode(para_code), + } + .into(); + let register_result = relay_client + .submit_and_watch_signed_extrinsic(&relay_sign, move |_, transaction_nonce| { + Ok(UnsignedTransaction::new(register_parathread_call.into(), transaction_nonce)) + }) + .await? + .wait() + .await; + if register_result == TrackedTransactionStatus::Lost { + return Err(anyhow::format_err!( + "Failed to finalize `register-parathread` transaction" + )) + } + log::info!(target: "bridge", "Registered parachain: {:?}. Waiting for onboarding", para_id); + + // wait until parathread is onboarded + let para_state_key = bp_runtime::storage_map_final_key::( + PARAS_PALLET_NAME, + PARAS_LIFECYCLES_STORAGE_NAME, + ¶_id.encode(), + ); + wait_para_state( + &relay_client, + ¶_state_key.0, + &[ParaLifecycle::Onboarding, ParaLifecycle::Parathread], + ParaLifecycle::Parathread, + ) + .await?; + + // step 3: force parachain leases + let lease_begin = self.lease_begin; + let lease_end = self.lease_end; + let para_deposit = self.deposit.cast().into(); + log::info!( + target: "bridge", + "Going to force leases of parachain {:?}: [{}; {}]", + para_id, + lease_begin, + lease_end, + ); + let force_lease_call: CallOf = SudoCall::sudo { + call: Box::new( + ParaSlotsCall::force_lease { + para: para_id, + leaser: relay_sudo_account.clone(), + amount: para_deposit, + period_begin: lease_begin, + period_count: lease_end.saturating_sub(lease_begin).saturating_add(1), + } + .into(), + ), + } + .into(); + relay_client + .submit_signed_extrinsic(&relay_sign, move |_, transaction_nonce| { + Ok(UnsignedTransaction::new(force_lease_call.into(), transaction_nonce)) + }) + .await?; + log::info!(target: "bridge", "Registered parachain leases: {:?}. Waiting for onboarding", para_id); + + // wait until parachain is onboarded + wait_para_state( + &relay_client, + ¶_state_key.0, + &[ + ParaLifecycle::Onboarding, + ParaLifecycle::UpgradingParathread, + ParaLifecycle::Parathread, + ], + ParaLifecycle::Parachain, + ) + .await?; + + Ok(()) + }) + } +} + +/// Wait until parachain state is changed. +async fn wait_para_state( + relay_client: &Client, + para_state_key: &[u8], + from_states: &[ParaLifecycle], + to_state: ParaLifecycle, +) -> anyhow::Result<()> { + loop { + let para_state: ParaLifecycle = relay_client + .storage_value(StorageKey(para_state_key.to_vec()), None) + .await? + .ok_or_else(|| { + anyhow::format_err!( + "Cannot fetch next free parachain lifecycle from the runtime storage of {}", + Relaychain::NAME, + ) + })?; + if para_state == to_state { + log::info!(target: "bridge", "Parachain state is now: {:?}", to_state); + return Ok(()) + } + if !from_states.contains(¶_state) { + return Err(anyhow::format_err!("Invalid parachain lifecycle: {:?}", para_state)) + } + + log::info!(target: "bridge", "Parachain state: {:?}. Waiting for {:?}", para_state, to_state); + async_std::task::sleep(Relaychain::AVERAGE_BLOCK_INTERVAL).await; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn register_rialto_parachain() { + let register_parachain = RegisterParachain::from_iter(vec![ + "register-parachain", + "rialto-parachain", + "--parachain-host", + "127.0.0.1", + "--parachain-port", + "11949", + "--relaychain-host", + "127.0.0.1", + "--relaychain-port", + "9944", + "--relaychain-signer", + "//Alice", + "--deposit", + "42", + "--lease-begin", + "100", + "--lease-end", + "200", + ]); + + assert_eq!( + register_parachain, + RegisterParachain { + parachain: Parachain::RialtoParachain, + deposit: Balance(42), + lease_begin: 100, + lease_end: 200, + relay_connection: RelaychainConnectionParams { + relaychain_host: "127.0.0.1".into(), + relaychain_port: 9944, + relaychain_secure: false, + relaychain_runtime_version: RelaychainRuntimeVersionParams { + relaychain_version_mode: RuntimeVersionType::Bundle, + relaychain_spec_version: None, + relaychain_transaction_version: None, + } + }, + relay_sign: RelaychainSigningParams { + relaychain_signer: Some("//Alice".into()), + relaychain_signer_password: None, + relaychain_signer_file: None, + relaychain_signer_password_file: None, + relaychain_transactions_mortality: None, + }, + para_connection: ParachainConnectionParams { + parachain_host: "127.0.0.1".into(), + parachain_port: 11949, + parachain_secure: false, + parachain_runtime_version: ParachainRuntimeVersionParams { + parachain_version_mode: RuntimeVersionType::Bundle, + parachain_spec_version: None, + parachain_transaction_version: None, + } + }, + } + ); + } +} diff --git a/relays/bin-substrate/src/cli/relay_headers.rs b/relays/bin-substrate/src/cli/relay_headers.rs new file mode 100644 index 00000000000..fbe360d3cd7 --- /dev/null +++ b/relays/bin-substrate/src/cli/relay_headers.rs @@ -0,0 +1,145 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use async_trait::async_trait; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf}; +use sp_core::Pair; +use structopt::StructOpt; +use strum::{EnumString, EnumVariantNames, VariantNames}; + +use crate::bridges::{ + kusama_polkadot::{ + kusama_headers_to_bridge_hub_polkadot::KusamaToBridgeHubPolkadotCliBridge, + polkadot_headers_to_bridge_hub_kusama::PolkadotToBridgeHubKusamaCliBridge, + }, + rialto_millau::{ + millau_headers_to_rialto::MillauToRialtoCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + }, + rialto_parachain_millau::millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rococo_wococo::{ + rococo_headers_to_bridge_hub_wococo::RococoToBridgeHubWococoCliBridge, + wococo_headers_to_bridge_hub_rococo::WococoToBridgeHubRococoCliBridge, + }, + westend_millau::westend_headers_to_millau::WestendToMillauCliBridge, +}; +use relay_utils::metrics::{GlobalMetrics, StandaloneMetric}; +use substrate_relay_helper::finality::SubstrateFinalitySyncPipeline; + +use crate::cli::{bridge::*, chain_schema::*, PrometheusParams}; + +/// Start headers relayer process. +#[derive(StructOpt)] +pub struct RelayHeaders { + /// A bridge instance to relay headers for. + #[structopt(possible_values = RelayHeadersBridge::VARIANTS, case_insensitive = true)] + bridge: RelayHeadersBridge, + /// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) + /// are relayed. + #[structopt(long)] + only_mandatory_headers: bool, + #[structopt(flatten)] + source: SourceConnectionParams, + #[structopt(flatten)] + target: TargetConnectionParams, + #[structopt(flatten)] + target_sign: TargetSigningParams, + #[structopt(flatten)] + prometheus_params: PrometheusParams, +} + +#[derive(Debug, EnumString, EnumVariantNames)] +#[strum(serialize_all = "kebab_case")] +/// Headers relay bridge. +pub enum RelayHeadersBridge { + MillauToRialto, + RialtoToMillau, + WestendToMillau, + MillauToRialtoParachain, + RococoToBridgeHubWococo, + WococoToBridgeHubRococo, + KusamaToBridgeHubPolkadot, + PolkadotToBridgeHubKusama, +} + +#[async_trait] +trait HeadersRelayer: RelayToRelayHeadersCliBridge +where + AccountIdOf: From< as Pair>::Public>, +{ + /// Relay headers. + async fn relay_headers(data: RelayHeaders) -> anyhow::Result<()> { + let source_client = data.source.into_client::().await?; + let target_client = data.target.into_client::().await?; + let target_transactions_mortality = data.target_sign.target_transactions_mortality; + let target_sign = data.target_sign.to_keypair::()?; + + let metrics_params: relay_utils::metrics::MetricsParams = + data.prometheus_params.into_metrics_params()?; + GlobalMetrics::new()?.register_and_spawn(&metrics_params.registry)?; + + let target_transactions_params = substrate_relay_helper::TransactionParams { + signer: target_sign, + mortality: target_transactions_mortality, + }; + Self::Finality::start_relay_guards( + &target_client, + &target_transactions_params, + target_client.can_start_version_guard(), + ) + .await?; + + substrate_relay_helper::finality::run::( + source_client, + target_client, + data.only_mandatory_headers, + target_transactions_params, + metrics_params, + ) + .await + } +} + +impl HeadersRelayer for MillauToRialtoCliBridge {} +impl HeadersRelayer for RialtoToMillauCliBridge {} +impl HeadersRelayer for WestendToMillauCliBridge {} +impl HeadersRelayer for MillauToRialtoParachainCliBridge {} +impl HeadersRelayer for RococoToBridgeHubWococoCliBridge {} +impl HeadersRelayer for WococoToBridgeHubRococoCliBridge {} +impl HeadersRelayer for KusamaToBridgeHubPolkadotCliBridge {} +impl HeadersRelayer for PolkadotToBridgeHubKusamaCliBridge {} + +impl RelayHeaders { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + match self.bridge { + RelayHeadersBridge::MillauToRialto => MillauToRialtoCliBridge::relay_headers(self), + RelayHeadersBridge::RialtoToMillau => RialtoToMillauCliBridge::relay_headers(self), + RelayHeadersBridge::WestendToMillau => WestendToMillauCliBridge::relay_headers(self), + RelayHeadersBridge::MillauToRialtoParachain => + MillauToRialtoParachainCliBridge::relay_headers(self), + RelayHeadersBridge::RococoToBridgeHubWococo => + RococoToBridgeHubWococoCliBridge::relay_headers(self), + RelayHeadersBridge::WococoToBridgeHubRococo => + WococoToBridgeHubRococoCliBridge::relay_headers(self), + RelayHeadersBridge::KusamaToBridgeHubPolkadot => + KusamaToBridgeHubPolkadotCliBridge::relay_headers(self), + RelayHeadersBridge::PolkadotToBridgeHubKusama => + PolkadotToBridgeHubKusamaCliBridge::relay_headers(self), + } + .await + } +} diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs new file mode 100644 index 00000000000..5384c8ff9e9 --- /dev/null +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages/mod.rs @@ -0,0 +1,715 @@ +// Copyright 2019-2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Complex 2-ways headers+messages relays support. +//! +//! To add new complex relay between `ChainA` and `ChainB`, you must: +//! +//! 1) ensure that there's a `declare_chain_cli_schema!(...)` for both chains. +//! 2) add `declare_chain_to_chain_bridge_schema!(...)` or +//! `declare_chain_to_parachain_bridge_schema` for the bridge. +//! 3) declare a new struct for the added bridge and implement the `Full2WayBridge` trait for it. + +#[macro_use] +mod parachain_to_parachain; +#[macro_use] +mod relay_to_relay; +#[macro_use] +mod relay_to_parachain; + +use async_trait::async_trait; +use std::{marker::PhantomData, sync::Arc}; +use structopt::StructOpt; + +use futures::{FutureExt, TryFutureExt}; +use relay_to_parachain::*; +use relay_to_relay::*; + +use crate::{ + bridges::{ + rialto_millau::{ + millau_headers_to_rialto::MillauToRialtoCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + }, + rialto_parachain_millau::{ + millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, + }, + rococo_wococo::{ + rococo_parachains_to_bridge_hub_wococo::BridgeHubRococoToBridgeHubWococoCliBridge, + wococo_parachains_to_bridge_hub_rococo::BridgeHubWococoToBridgeHubRococoCliBridge, + }, + }, + cli::{ + bridge::{ + CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge, + RelayToRelayHeadersCliBridge, + }, + chain_schema::*, + relay_headers_and_messages::parachain_to_parachain::ParachainToParachainBridge, + CliChain, HexLaneId, PrometheusParams, + }, + declare_chain_cli_schema, +}; +use bp_messages::LaneId; +use bp_runtime::BalanceOf; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, Chain, ChainWithBalances, ChainWithMessages, + ChainWithTransactions, Client, Parachain, +}; +use relay_utils::metrics::MetricsParams; +use sp_core::Pair; +use substrate_relay_helper::{ + messages_lane::MessagesRelayParams, on_demand::OnDemandRelay, TaggedAccount, TransactionParams, +}; + +/// Parameters that have the same names across all bridges. +#[derive(Debug, PartialEq, StructOpt)] +pub struct HeadersAndMessagesSharedParams { + /// Hex-encoded lane identifiers that should be served by the complex relay. + #[structopt(long, default_value = "00000000")] + pub lane: Vec, + /// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) + /// are relayed. + #[structopt(long)] + pub only_mandatory_headers: bool, + #[structopt(flatten)] + pub prometheus_params: PrometheusParams, +} + +/// Bridge parameters, shared by all bridge types. +pub struct Full2WayBridgeCommonParams< + Left: ChainWithTransactions + CliChain, + Right: ChainWithTransactions + CliChain, +> { + /// Shared parameters. + pub shared: HeadersAndMessagesSharedParams, + /// Parameters of the left chain. + pub left: BridgeEndCommonParams, + /// Parameters of the right chain. + pub right: BridgeEndCommonParams, + + /// Common metric parameters. + pub metrics_params: MetricsParams, +} + +impl + Full2WayBridgeCommonParams +{ + /// Creates new bridge parameters from its components. + pub fn new>( + shared: HeadersAndMessagesSharedParams, + left: BridgeEndCommonParams, + right: BridgeEndCommonParams, + ) -> anyhow::Result { + // Create metrics registry. + let metrics_params = shared.prometheus_params.clone().into_metrics_params()?; + let metrics_params = relay_utils::relay_metrics(metrics_params).into_params(); + + Ok(Self { shared, left, right, metrics_params }) + } +} + +/// Parameters that are associated with one side of the bridge. +pub struct BridgeEndCommonParams { + /// Chain client. + pub client: Client, + /// Transactions signer. + pub sign: AccountKeyPairOf, + /// Transactions mortality. + pub transactions_mortality: Option, + /// Accounts, which balances are exposed as metrics by the relay process. + pub accounts: Vec>>, +} + +/// All data of the bidirectional complex relay. +struct FullBridge< + 'a, + Source: ChainWithTransactions + CliChain, + Target: ChainWithTransactions + CliChain, + Bridge: MessagesCliBridge, +> { + source: &'a mut BridgeEndCommonParams, + target: &'a mut BridgeEndCommonParams, + metrics_params: &'a MetricsParams, + _phantom_data: PhantomData, +} + +impl< + 'a, + Source: ChainWithTransactions + CliChain, + Target: ChainWithTransactions + CliChain, + Bridge: MessagesCliBridge, + > FullBridge<'a, Source, Target, Bridge> +where + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, + BalanceOf: TryFrom> + Into, +{ + /// Construct complex relay given it components. + fn new( + source: &'a mut BridgeEndCommonParams, + target: &'a mut BridgeEndCommonParams, + metrics_params: &'a MetricsParams, + ) -> Self { + Self { source, target, metrics_params, _phantom_data: Default::default() } + } + + /// Returns message relay parameters. + fn messages_relay_params( + &self, + source_to_target_headers_relay: Arc>, + target_to_source_headers_relay: Arc>, + lane_id: LaneId, + ) -> MessagesRelayParams { + MessagesRelayParams { + source_client: self.source.client.clone(), + source_transaction_params: TransactionParams { + signer: self.source.sign.clone(), + mortality: self.source.transactions_mortality, + }, + target_client: self.target.client.clone(), + target_transaction_params: TransactionParams { + signer: self.target.sign.clone(), + mortality: self.target.transactions_mortality, + }, + source_to_target_headers_relay: Some(source_to_target_headers_relay), + target_to_source_headers_relay: Some(target_to_source_headers_relay), + lane_id, + metrics_params: self.metrics_params.clone().disable(), + } + } +} + +// All supported chains. +declare_chain_cli_schema!(Millau, millau); +declare_chain_cli_schema!(Rialto, rialto); +declare_chain_cli_schema!(RialtoParachain, rialto_parachain); +declare_chain_cli_schema!(Rococo, rococo); +declare_chain_cli_schema!(BridgeHubRococo, bridge_hub_rococo); +declare_chain_cli_schema!(Wococo, wococo); +declare_chain_cli_schema!(BridgeHubWococo, bridge_hub_wococo); +// Means to override signers of different layer transactions. +declare_chain_cli_schema!(MillauHeadersToRialto, millau_headers_to_rialto); +declare_chain_cli_schema!(MillauHeadersToRialtoParachain, millau_headers_to_rialto_parachain); +declare_chain_cli_schema!(RialtoHeadersToMillau, rialto_headers_to_millau); +declare_chain_cli_schema!(RialtoParachainsToMillau, rialto_parachains_to_millau); +declare_chain_cli_schema!(RococoHeadersToBridgeHubWococo, rococo_headers_to_bridge_hub_wococo); +declare_chain_cli_schema!( + RococoParachainsToBridgeHubWococo, + rococo_parachains_to_bridge_hub_wococo +); +declare_chain_cli_schema!(WococoHeadersToBridgeHubRococo, wococo_headers_to_bridge_hub_rococo); +declare_chain_cli_schema!( + WococoParachainsToBridgeHubRococo, + wococo_parachains_to_bridge_hub_rococo +); +// All supported bridges. +declare_relay_to_relay_bridge_schema!(Millau, Rialto); +declare_relay_to_parachain_bridge_schema!(Millau, RialtoParachain, Rialto); +declare_parachain_to_parachain_bridge_schema!(BridgeHubRococo, Rococo, BridgeHubWococo, Wococo); + +/// Base portion of the bidirectional complex relay. +/// +/// This main purpose of extracting this trait is that in different relays the implementation +/// of `start_on_demand_headers_relayers` method will be different. But the number of +/// implementations is limited to relay <> relay, parachain <> relay and parachain <> parachain. +/// This trait allows us to reuse these implementations in different bridges. +#[async_trait] +trait Full2WayBridgeBase: Sized + Send + Sync { + /// The CLI params for the bridge. + type Params; + /// The left relay chain. + type Left: ChainWithTransactions + CliChain; + /// The right destination chain (it can be a relay or a parachain). + type Right: ChainWithTransactions + CliChain; + + /// Reference to common relay parameters. + fn common(&self) -> &Full2WayBridgeCommonParams; + + /// Mutable reference to common relay parameters. + fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams; + + /// Start on-demand headers relays. + async fn start_on_demand_headers_relayers( + &mut self, + ) -> anyhow::Result<( + Arc>, + Arc>, + )>; +} + +/// Bidirectional complex relay. +#[async_trait] +trait Full2WayBridge: Sized + Sync +where + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, + BalanceOf: TryFrom> + Into, + BalanceOf: TryFrom> + Into, +{ + /// Base portion of the bidirectional complex relay. + type Base: Full2WayBridgeBase; + + /// The left relay chain. + type Left: ChainWithTransactions + ChainWithBalances + ChainWithMessages + CliChain; + /// The right relay chain. + type Right: ChainWithTransactions + ChainWithBalances + ChainWithMessages + CliChain; + + /// Left to Right bridge. + type L2R: MessagesCliBridge; + /// Right to Left bridge + type R2L: MessagesCliBridge; + + /// Construct new bridge. + fn new(params: ::Params) -> anyhow::Result; + + /// Reference to the base relay portion. + fn base(&self) -> &Self::Base; + + /// Mutable reference to the base relay portion. + fn mut_base(&mut self) -> &mut Self::Base; + + /// Creates and returns Left to Right complex relay. + fn left_to_right(&mut self) -> FullBridge { + let common = self.mut_base().mut_common(); + FullBridge::<_, _, Self::L2R>::new( + &mut common.left, + &mut common.right, + &common.metrics_params, + ) + } + + /// Creates and returns Right to Left complex relay. + fn right_to_left(&mut self) -> FullBridge { + let common = self.mut_base().mut_common(); + FullBridge::<_, _, Self::R2L>::new( + &mut common.right, + &mut common.left, + &common.metrics_params, + ) + } + + /// Start complex relay. + async fn run(&mut self) -> anyhow::Result<()> { + // Register standalone metrics. + { + let common = self.mut_base().mut_common(); + common.left.accounts.push(TaggedAccount::Messages { + id: common.left.sign.public().into(), + bridged_chain: Self::Right::NAME.to_string(), + }); + common.right.accounts.push(TaggedAccount::Messages { + id: common.right.sign.public().into(), + bridged_chain: Self::Left::NAME.to_string(), + }); + } + + // start on-demand header relays + let (left_to_right_on_demand_headers, right_to_left_on_demand_headers) = + self.mut_base().start_on_demand_headers_relayers().await?; + + // add balance-related metrics + let lanes = self + .base() + .common() + .shared + .lane + .iter() + .cloned() + .map(Into::into) + .collect::>(); + { + let common = self.mut_base().mut_common(); + substrate_relay_helper::messages_metrics::add_relay_balances_metrics::<_, Self::Right>( + common.left.client.clone(), + &mut common.metrics_params, + &common.left.accounts, + &lanes, + ) + .await?; + substrate_relay_helper::messages_metrics::add_relay_balances_metrics::<_, Self::Left>( + common.right.client.clone(), + &mut common.metrics_params, + &common.right.accounts, + &lanes, + ) + .await?; + } + + // Need 2x capacity since we consider both directions for each lane + let mut message_relays = Vec::with_capacity(lanes.len() * 2); + for lane in lanes { + let left_to_right_messages = substrate_relay_helper::messages_lane::run::< + ::MessagesLane, + >(self.left_to_right().messages_relay_params( + left_to_right_on_demand_headers.clone(), + right_to_left_on_demand_headers.clone(), + lane, + )) + .map_err(|e| anyhow::format_err!("{}", e)) + .boxed(); + message_relays.push(left_to_right_messages); + + let right_to_left_messages = substrate_relay_helper::messages_lane::run::< + ::MessagesLane, + >(self.right_to_left().messages_relay_params( + right_to_left_on_demand_headers.clone(), + left_to_right_on_demand_headers.clone(), + lane, + )) + .map_err(|e| anyhow::format_err!("{}", e)) + .boxed(); + message_relays.push(right_to_left_messages); + } + + relay_utils::relay_metrics(self.base().common().metrics_params.clone()) + .expose() + .await + .map_err(|e| anyhow::format_err!("{}", e))?; + + futures::future::select_all(message_relays).await.0 + } +} + +/// Millau <> Rialto complex relay. +pub struct MillauRialtoFull2WayBridge { + base: ::Base, +} + +#[async_trait] +impl Full2WayBridge for MillauRialtoFull2WayBridge { + type Base = RelayToRelayBridge; + type Left = relay_millau_client::Millau; + type Right = relay_rialto_client::Rialto; + type L2R = MillauToRialtoCliBridge; + type R2L = RialtoToMillauCliBridge; + + fn new(base: Self::Base) -> anyhow::Result { + Ok(Self { base }) + } + + fn base(&self) -> &Self::Base { + &self.base + } + + fn mut_base(&mut self) -> &mut Self::Base { + &mut self.base + } +} + +/// Millau <> RialtoParachain complex relay. +pub struct MillauRialtoParachainFull2WayBridge { + base: ::Base, +} + +#[async_trait] +impl Full2WayBridge for MillauRialtoParachainFull2WayBridge { + type Base = RelayToParachainBridge; + type Left = relay_millau_client::Millau; + type Right = relay_rialto_parachain_client::RialtoParachain; + type L2R = MillauToRialtoParachainCliBridge; + type R2L = RialtoParachainToMillauCliBridge; + + fn new(base: Self::Base) -> anyhow::Result { + Ok(Self { base }) + } + + fn base(&self) -> &Self::Base { + &self.base + } + + fn mut_base(&mut self) -> &mut Self::Base { + &mut self.base + } +} + +/// BridgeHubRococo <> BridgeHubWococo complex relay. +pub struct BridgeHubRococoBridgeHubWococoFull2WayBridge { + base: ::Base, +} + +#[async_trait] +impl Full2WayBridge for BridgeHubRococoBridgeHubWococoFull2WayBridge { + type Base = ParachainToParachainBridge; + type Left = relay_bridge_hub_rococo_client::BridgeHubRococo; + type Right = relay_bridge_hub_wococo_client::BridgeHubWococo; + type L2R = BridgeHubRococoToBridgeHubWococoCliBridge; + type R2L = BridgeHubWococoToBridgeHubRococoCliBridge; + + fn new(base: Self::Base) -> anyhow::Result { + Ok(Self { base }) + } + + fn base(&self) -> &Self::Base { + &self.base + } + + fn mut_base(&mut self) -> &mut Self::Base { + &mut self.base + } +} + +/// Complex headers+messages relay. +#[derive(Debug, PartialEq, StructOpt)] +pub enum RelayHeadersAndMessages { + /// Millau <> Rialto relay. + MillauRialto(MillauRialtoHeadersAndMessages), + /// Millau <> RialtoParachain relay. + MillauRialtoParachain(MillauRialtoParachainHeadersAndMessages), + /// BridgeHubRococo <> BridgeHubWococo relay. + BridgeHubRococoBridgeHubWococo(BridgeHubRococoBridgeHubWococoHeadersAndMessages), +} + +impl RelayHeadersAndMessages { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + match self { + RelayHeadersAndMessages::MillauRialto(params) => + MillauRialtoFull2WayBridge::new(params.into_bridge().await?)?.run().await, + RelayHeadersAndMessages::MillauRialtoParachain(params) => + MillauRialtoParachainFull2WayBridge::new(params.into_bridge().await?)? + .run() + .await, + RelayHeadersAndMessages::BridgeHubRococoBridgeHubWococo(params) => + BridgeHubRococoBridgeHubWococoFull2WayBridge::new(params.into_bridge().await?)? + .run() + .await, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_parse_relay_to_relay_options() { + // when + let res = RelayHeadersAndMessages::from_iter(vec![ + "relay-headers-and-messages", + "millau-rialto", + "--millau-host", + "millau-node-alice", + "--millau-port", + "9944", + "--millau-signer", + "//Charlie", + "--millau-transactions-mortality", + "64", + "--rialto-host", + "rialto-node-alice", + "--rialto-port", + "9944", + "--rialto-signer", + "//Charlie", + "--rialto-transactions-mortality", + "64", + "--lane", + "00000000", + "--lane", + "73776170", + "--prometheus-host", + "0.0.0.0", + ]); + + // then + assert_eq!( + res, + RelayHeadersAndMessages::MillauRialto(MillauRialtoHeadersAndMessages { + shared: HeadersAndMessagesSharedParams { + lane: vec![ + HexLaneId([0x00, 0x00, 0x00, 0x00]), + HexLaneId([0x73, 0x77, 0x61, 0x70]) + ], + only_mandatory_headers: false, + prometheus_params: PrometheusParams { + no_prometheus: false, + prometheus_host: "0.0.0.0".into(), + prometheus_port: 9616, + }, + }, + left: MillauConnectionParams { + millau_host: "millau-node-alice".into(), + millau_port: 9944, + millau_secure: false, + millau_runtime_version: MillauRuntimeVersionParams { + millau_version_mode: RuntimeVersionType::Bundle, + millau_spec_version: None, + millau_transaction_version: None, + }, + }, + left_sign: MillauSigningParams { + millau_signer: Some("//Charlie".into()), + millau_signer_password: None, + millau_signer_file: None, + millau_signer_password_file: None, + millau_transactions_mortality: Some(64), + }, + left_headers_to_right_sign_override: MillauHeadersToRialtoSigningParams { + millau_headers_to_rialto_signer: None, + millau_headers_to_rialto_signer_password: None, + millau_headers_to_rialto_signer_file: None, + millau_headers_to_rialto_signer_password_file: None, + millau_headers_to_rialto_transactions_mortality: None, + }, + right: RialtoConnectionParams { + rialto_host: "rialto-node-alice".into(), + rialto_port: 9944, + rialto_secure: false, + rialto_runtime_version: RialtoRuntimeVersionParams { + rialto_version_mode: RuntimeVersionType::Bundle, + rialto_spec_version: None, + rialto_transaction_version: None, + }, + }, + right_sign: RialtoSigningParams { + rialto_signer: Some("//Charlie".into()), + rialto_signer_password: None, + rialto_signer_file: None, + rialto_signer_password_file: None, + rialto_transactions_mortality: Some(64), + }, + right_headers_to_left_sign_override: RialtoHeadersToMillauSigningParams { + rialto_headers_to_millau_signer: None, + rialto_headers_to_millau_signer_password: None, + rialto_headers_to_millau_signer_file: None, + rialto_headers_to_millau_signer_password_file: None, + rialto_headers_to_millau_transactions_mortality: None, + }, + }), + ); + } + + #[test] + fn should_parse_relay_to_parachain_options() { + // when + let res = RelayHeadersAndMessages::from_iter(vec![ + "relay-headers-and-messages", + "millau-rialto-parachain", + "--millau-host", + "millau-node-alice", + "--millau-port", + "9944", + "--millau-signer", + "//Iden", + "--rialto-headers-to-millau-signer", + "//Ken", + "--millau-transactions-mortality", + "64", + "--rialto-parachain-host", + "rialto-parachain-collator-charlie", + "--rialto-parachain-port", + "9944", + "--rialto-parachain-signer", + "//George", + "--rialto-parachain-transactions-mortality", + "64", + "--rialto-host", + "rialto-node-alice", + "--rialto-port", + "9944", + "--lane", + "00000000", + "--prometheus-host", + "0.0.0.0", + ]); + + // then + assert_eq!( + res, + RelayHeadersAndMessages::MillauRialtoParachain( + MillauRialtoParachainHeadersAndMessages { + shared: HeadersAndMessagesSharedParams { + lane: vec![HexLaneId([0x00, 0x00, 0x00, 0x00])], + only_mandatory_headers: false, + prometheus_params: PrometheusParams { + no_prometheus: false, + prometheus_host: "0.0.0.0".into(), + prometheus_port: 9616, + }, + }, + left: MillauConnectionParams { + millau_host: "millau-node-alice".into(), + millau_port: 9944, + millau_secure: false, + millau_runtime_version: MillauRuntimeVersionParams { + millau_version_mode: RuntimeVersionType::Bundle, + millau_spec_version: None, + millau_transaction_version: None, + }, + }, + left_sign: MillauSigningParams { + millau_signer: Some("//Iden".into()), + millau_signer_password: None, + millau_signer_file: None, + millau_signer_password_file: None, + millau_transactions_mortality: Some(64), + }, + left_headers_to_right_sign_override: + MillauHeadersToRialtoParachainSigningParams { + millau_headers_to_rialto_parachain_signer: None, + millau_headers_to_rialto_parachain_signer_password: None, + millau_headers_to_rialto_parachain_signer_file: None, + millau_headers_to_rialto_parachain_signer_password_file: None, + millau_headers_to_rialto_parachain_transactions_mortality: None, + }, + right: RialtoParachainConnectionParams { + rialto_parachain_host: "rialto-parachain-collator-charlie".into(), + rialto_parachain_port: 9944, + rialto_parachain_secure: false, + rialto_parachain_runtime_version: RialtoParachainRuntimeVersionParams { + rialto_parachain_version_mode: RuntimeVersionType::Bundle, + rialto_parachain_spec_version: None, + rialto_parachain_transaction_version: None, + }, + }, + right_sign: RialtoParachainSigningParams { + rialto_parachain_signer: Some("//George".into()), + rialto_parachain_signer_password: None, + rialto_parachain_signer_file: None, + rialto_parachain_signer_password_file: None, + rialto_parachain_transactions_mortality: Some(64), + }, + right_relay_headers_to_left_sign_override: RialtoHeadersToMillauSigningParams { + rialto_headers_to_millau_signer: Some("//Ken".into()), + rialto_headers_to_millau_signer_password: None, + rialto_headers_to_millau_signer_file: None, + rialto_headers_to_millau_signer_password_file: None, + rialto_headers_to_millau_transactions_mortality: None, + }, + right_parachains_to_left_sign_override: RialtoParachainsToMillauSigningParams { + rialto_parachains_to_millau_signer: None, + rialto_parachains_to_millau_signer_password: None, + rialto_parachains_to_millau_signer_file: None, + rialto_parachains_to_millau_signer_password_file: None, + rialto_parachains_to_millau_transactions_mortality: None, + }, + right_relay: RialtoConnectionParams { + rialto_host: "rialto-node-alice".into(), + rialto_port: 9944, + rialto_secure: false, + rialto_runtime_version: RialtoRuntimeVersionParams { + rialto_version_mode: RuntimeVersionType::Bundle, + rialto_spec_version: None, + rialto_transaction_version: None, + }, + }, + } + ), + ); + } +} diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages/parachain_to_parachain.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages/parachain_to_parachain.rs new file mode 100644 index 00000000000..b4c858e566b --- /dev/null +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages/parachain_to_parachain.rs @@ -0,0 +1,275 @@ +// Copyright 2019-2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use async_trait::async_trait; +use std::sync::Arc; + +use crate::cli::{ + bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}, + relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams}, + CliChain, +}; +use bp_polkadot_core::parachains::ParaHash; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, Chain, ChainWithTransactions, Client, Parachain, +}; +use sp_core::Pair; +use substrate_relay_helper::{ + finality::SubstrateFinalitySyncPipeline, + on_demand::{ + headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay, + }, + TaggedAccount, TransactionParams, +}; + +/// A base relay between two parachain from different consensus systems. +/// +/// Such relay starts 2 messages relay. It also starts 2 on-demand header relays and 2 on-demand +/// parachain heads relay. +pub struct ParachainToParachainBridge< + L2R: MessagesCliBridge + ParachainToRelayHeadersCliBridge, + R2L: MessagesCliBridge + ParachainToRelayHeadersCliBridge, +> where + ::Source: Parachain, + ::Source: Parachain, +{ + /// Parameters that are shared by all bridge types. + pub common: + Full2WayBridgeCommonParams<::Target, ::Target>, + /// Client of the left relay chain. + pub left_relay: Client<::SourceRelay>, + /// Client of the right relay chain. + pub right_relay: Client<::SourceRelay>, + + /// Override for right_relay->left headers signer. + pub right_headers_to_left_transaction_params: + TransactionParams::Target>>, + /// Override for left_relay->right headers signer. + pub left_headers_to_right_transaction_params: + TransactionParams::Target>>, + + /// Override for right->left parachains signer. + pub right_parachains_to_left_transaction_params: + TransactionParams::Target>>, + /// Override for left->right parachains signer. + pub left_parachains_to_right_transaction_params: + TransactionParams::Target>>, +} + +macro_rules! declare_parachain_to_parachain_bridge_schema { + // left-parachain, relay-chain-of-left-parachain, right-parachain, relay-chain-of-right-parachain + ($left_parachain:ident, $left_chain:ident, $right_parachain:ident, $right_chain:ident) => { + bp_runtime::paste::item! { + #[doc = $left_parachain ", " $left_chain ", " $right_parachain " and " $right_chain " headers+parachains+messages relay params."] + #[derive(Debug, PartialEq, StructOpt)] + pub struct [<$left_parachain $right_parachain HeadersAndMessages>] { + // shared parameters + #[structopt(flatten)] + shared: HeadersAndMessagesSharedParams, + + #[structopt(flatten)] + left: [<$left_parachain ConnectionParams>], + #[structopt(flatten)] + left_relay: [<$left_chain ConnectionParams>], + + // default signer, which is always used to sign messages relay transactions on the left chain + #[structopt(flatten)] + left_sign: [<$left_parachain SigningParams>], + + #[structopt(flatten)] + right: [<$right_parachain ConnectionParams>], + #[structopt(flatten)] + right_relay: [<$right_chain ConnectionParams>], + + // default signer, which is always used to sign messages relay transactions on the right chain + #[structopt(flatten)] + right_sign: [<$right_parachain SigningParams>], + + // override for right_relay->left-parachain headers signer + #[structopt(flatten)] + right_relay_headers_to_left_sign_override: [<$right_chain HeadersTo $left_parachain SigningParams>], + // override for left_relay->right-parachain headers signer + #[structopt(flatten)] + left_relay_headers_to_right_sign_override: [<$left_chain HeadersTo $right_parachain SigningParams>], + + // override for right->left parachains signer + #[structopt(flatten)] + right_parachains_to_left_sign_override: [<$right_chain ParachainsTo $left_parachain SigningParams>], + // override for left->right parachains signer + #[structopt(flatten)] + left_parachains_to_right_sign_override: [<$left_chain ParachainsTo $right_parachain SigningParams>], + } + + impl [<$left_parachain $right_parachain HeadersAndMessages>] { + async fn into_bridge< + Left: ChainWithTransactions + CliChain + Parachain, + LeftRelay: CliChain, + Right: ChainWithTransactions + CliChain + Parachain, + RightRelay: CliChain, + L2R: CliBridgeBase + + MessagesCliBridge + + ParachainToRelayHeadersCliBridge, + R2L: CliBridgeBase + + MessagesCliBridge + + ParachainToRelayHeadersCliBridge, + >( + self, + ) -> anyhow::Result> { + Ok(ParachainToParachainBridge { + common: Full2WayBridgeCommonParams::new::( + self.shared, + BridgeEndCommonParams { + client: self.left.into_client::().await?, + sign: self.left_sign.to_keypair::()?, + transactions_mortality: self.left_sign.transactions_mortality()?, + accounts: vec![], + }, + BridgeEndCommonParams { + client: self.right.into_client::().await?, + sign: self.right_sign.to_keypair::()?, + transactions_mortality: self.right_sign.transactions_mortality()?, + accounts: vec![], + }, + )?, + left_relay: self.left_relay.into_client::().await?, + right_relay: self.right_relay.into_client::().await?, + right_headers_to_left_transaction_params: self + .right_relay_headers_to_left_sign_override + .transaction_params_or::(&self.left_sign)?, + left_headers_to_right_transaction_params: self + .left_relay_headers_to_right_sign_override + .transaction_params_or::(&self.right_sign)?, + right_parachains_to_left_transaction_params: self + .right_parachains_to_left_sign_override + .transaction_params_or::(&self.left_sign)?, + left_parachains_to_right_transaction_params: self + .left_parachains_to_right_sign_override + .transaction_params_or::(&self.right_sign)?, + }) + } + } + } + }; +} + +#[async_trait] +impl< + Left: Chain + ChainWithTransactions + CliChain + Parachain, + Right: Chain + ChainWithTransactions + CliChain + Parachain, + LeftRelay: Chain + + CliChain, + RightRelay: Chain + + CliChain, + L2R: CliBridgeBase + + MessagesCliBridge + + ParachainToRelayHeadersCliBridge, + R2L: CliBridgeBase + + MessagesCliBridge + + ParachainToRelayHeadersCliBridge, + > Full2WayBridgeBase for ParachainToParachainBridge +where + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, +{ + type Params = ParachainToParachainBridge; + type Left = Left; + type Right = Right; + + fn common(&self) -> &Full2WayBridgeCommonParams { + &self.common + } + + fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams { + &mut self.common + } + + async fn start_on_demand_headers_relayers( + &mut self, + ) -> anyhow::Result<( + Arc>, + Arc>, + )> { + self.common.left.accounts.push(TaggedAccount::Headers { + id: self.right_headers_to_left_transaction_params.signer.public().into(), + bridged_chain: RightRelay::NAME.to_string(), + }); + self.common.left.accounts.push(TaggedAccount::Parachains { + id: self.right_parachains_to_left_transaction_params.signer.public().into(), + bridged_chain: RightRelay::NAME.to_string(), + }); + self.common.right.accounts.push(TaggedAccount::Headers { + id: self.left_headers_to_right_transaction_params.signer.public().into(), + bridged_chain: Left::NAME.to_string(), + }); + self.common.right.accounts.push(TaggedAccount::Parachains { + id: self.left_parachains_to_right_transaction_params.signer.public().into(), + bridged_chain: LeftRelay::NAME.to_string(), + }); + + ::RelayFinality::start_relay_guards( + &self.common.right.client, + &self.left_headers_to_right_transaction_params, + self.common.right.client.can_start_version_guard(), + ) + .await?; + ::RelayFinality::start_relay_guards( + &self.common.left.client, + &self.right_headers_to_left_transaction_params, + self.common.left.client.can_start_version_guard(), + ) + .await?; + + let left_relay_to_right_on_demand_headers = + OnDemandHeadersRelay::<::RelayFinality>::new( + self.left_relay.clone(), + self.common.right.client.clone(), + self.left_headers_to_right_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + Some(self.common.metrics_params.clone()), + ); + let right_relay_to_left_on_demand_headers = + OnDemandHeadersRelay::<::RelayFinality>::new( + self.right_relay.clone(), + self.common.left.client.clone(), + self.right_headers_to_left_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + Some(self.common.metrics_params.clone()), + ); + + let left_to_right_on_demand_parachains = OnDemandParachainsRelay::< + ::ParachainFinality, + >::new( + self.left_relay.clone(), + self.common.right.client.clone(), + self.left_parachains_to_right_transaction_params.clone(), + Arc::new(left_relay_to_right_on_demand_headers), + ); + let right_to_left_on_demand_parachains = OnDemandParachainsRelay::< + ::ParachainFinality, + >::new( + self.right_relay.clone(), + self.common.left.client.clone(), + self.right_parachains_to_left_transaction_params.clone(), + Arc::new(right_relay_to_left_on_demand_headers), + ); + + Ok(( + Arc::new(left_to_right_on_demand_parachains), + Arc::new(right_to_left_on_demand_parachains), + )) + } +} diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs new file mode 100644 index 00000000000..d632cce08e9 --- /dev/null +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_parachain.rs @@ -0,0 +1,248 @@ +// Copyright 2019-2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use async_trait::async_trait; +use std::sync::Arc; + +use crate::cli::{ + bridge::{ + CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge, + RelayToRelayHeadersCliBridge, + }, + relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams}, + CliChain, +}; +use bp_polkadot_core::parachains::ParaHash; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, Chain, ChainWithTransactions, Client, Parachain, +}; +use sp_core::Pair; +use substrate_relay_helper::{ + finality::SubstrateFinalitySyncPipeline, + on_demand::{ + headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay, + }, + TaggedAccount, TransactionParams, +}; + +/// A base relay between standalone (relay) chain and a parachain from another consensus system. +/// +/// Such relay starts 2 messages relay. It also starts 2 on-demand header relays and 1 on-demand +/// parachain heads relay. +pub struct RelayToParachainBridge< + L2R: MessagesCliBridge + RelayToRelayHeadersCliBridge, + R2L: MessagesCliBridge + ParachainToRelayHeadersCliBridge, +> where + ::Source: Parachain, +{ + /// Parameters that are shared by all bridge types. + pub common: + Full2WayBridgeCommonParams<::Target, ::Target>, + /// Client of the right relay chain. + pub right_relay: Client<::SourceRelay>, + + /// Override for right_relay->left headers signer. + pub right_headers_to_left_transaction_params: + TransactionParams::Target>>, + /// Override for right->left parachains signer. + pub right_parachains_to_left_transaction_params: + TransactionParams::Target>>, + /// Override for left->right headers signer. + pub left_headers_to_right_transaction_params: + TransactionParams::Target>>, +} + +macro_rules! declare_relay_to_parachain_bridge_schema { + // chain, parachain, relay-chain-of-parachain + ($left_chain:ident, $right_parachain:ident, $right_chain:ident) => { + bp_runtime::paste::item! { + #[doc = $left_chain ", " $right_parachain " and " $right_chain " headers+parachains+messages relay params."] + #[derive(Debug, PartialEq, StructOpt)] + pub struct [<$left_chain $right_parachain HeadersAndMessages>] { + // shared parameters + #[structopt(flatten)] + shared: HeadersAndMessagesSharedParams, + + #[structopt(flatten)] + left: [<$left_chain ConnectionParams>], + + // default signer, which is always used to sign messages relay transactions on the left chain + #[structopt(flatten)] + left_sign: [<$left_chain SigningParams>], + + #[structopt(flatten)] + right: [<$right_parachain ConnectionParams>], + #[structopt(flatten)] + right_relay: [<$right_chain ConnectionParams>], + + // default signer, which is always used to sign messages relay transactions on the right chain + #[structopt(flatten)] + right_sign: [<$right_parachain SigningParams>], + + // override for right_relay->left headers signer + #[structopt(flatten)] + right_relay_headers_to_left_sign_override: [<$right_chain HeadersTo $left_chain SigningParams>], + // override for left->right headers signer + #[structopt(flatten)] + left_headers_to_right_sign_override: [<$left_chain HeadersTo $right_parachain SigningParams>], + + // override for right->left parachains signer + #[structopt(flatten)] + right_parachains_to_left_sign_override: [<$right_chain ParachainsTo $left_chain SigningParams>], + } + + impl [<$left_chain $right_parachain HeadersAndMessages>] { + async fn into_bridge< + Left: ChainWithTransactions + CliChain, + Right: ChainWithTransactions + CliChain + Parachain, + RightRelay: CliChain, + L2R: CliBridgeBase + MessagesCliBridge + RelayToRelayHeadersCliBridge, + R2L: CliBridgeBase + + MessagesCliBridge + + ParachainToRelayHeadersCliBridge, + >( + self, + ) -> anyhow::Result> { + Ok(RelayToParachainBridge { + common: Full2WayBridgeCommonParams::new::( + self.shared, + BridgeEndCommonParams { + client: self.left.into_client::().await?, + sign: self.left_sign.to_keypair::()?, + transactions_mortality: self.left_sign.transactions_mortality()?, + accounts: vec![], + }, + BridgeEndCommonParams { + client: self.right.into_client::().await?, + sign: self.right_sign.to_keypair::()?, + transactions_mortality: self.right_sign.transactions_mortality()?, + accounts: vec![], + }, + )?, + right_relay: self.right_relay.into_client::().await?, + right_headers_to_left_transaction_params: self + .right_relay_headers_to_left_sign_override + .transaction_params_or::( + &self.left_sign, + )?, + right_parachains_to_left_transaction_params: self + .right_parachains_to_left_sign_override + .transaction_params_or::( + &self.left_sign, + )?, + left_headers_to_right_transaction_params: self + .left_headers_to_right_sign_override + .transaction_params_or::(&self.right_sign)?, + }) + } + } + } + }; +} + +#[async_trait] +impl< + Left: ChainWithTransactions + CliChain, + Right: Chain + ChainWithTransactions + CliChain + Parachain, + RightRelay: Chain + + CliChain, + L2R: CliBridgeBase + + MessagesCliBridge + + RelayToRelayHeadersCliBridge, + R2L: CliBridgeBase + + MessagesCliBridge + + ParachainToRelayHeadersCliBridge, + > Full2WayBridgeBase for RelayToParachainBridge +where + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, +{ + type Params = RelayToParachainBridge; + type Left = Left; + type Right = Right; + + fn common(&self) -> &Full2WayBridgeCommonParams { + &self.common + } + + fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams { + &mut self.common + } + + async fn start_on_demand_headers_relayers( + &mut self, + ) -> anyhow::Result<( + Arc>, + Arc>, + )> { + self.common.left.accounts.push(TaggedAccount::Headers { + id: self.right_headers_to_left_transaction_params.signer.public().into(), + bridged_chain: RightRelay::NAME.to_string(), + }); + self.common.left.accounts.push(TaggedAccount::Parachains { + id: self.right_parachains_to_left_transaction_params.signer.public().into(), + bridged_chain: RightRelay::NAME.to_string(), + }); + self.common.right.accounts.push(TaggedAccount::Headers { + id: self.left_headers_to_right_transaction_params.signer.public().into(), + bridged_chain: Left::NAME.to_string(), + }); + + ::Finality::start_relay_guards( + &self.common.right.client, + &self.left_headers_to_right_transaction_params, + self.common.right.client.can_start_version_guard(), + ) + .await?; + ::RelayFinality::start_relay_guards( + &self.common.left.client, + &self.right_headers_to_left_transaction_params, + self.common.left.client.can_start_version_guard(), + ) + .await?; + + let left_to_right_on_demand_headers = + OnDemandHeadersRelay::<::Finality>::new( + self.common.left.client.clone(), + self.common.right.client.clone(), + self.left_headers_to_right_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + None, + ); + let right_relay_to_left_on_demand_headers = + OnDemandHeadersRelay::<::RelayFinality>::new( + self.right_relay.clone(), + self.common.left.client.clone(), + self.right_headers_to_left_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + Some(self.common.metrics_params.clone()), + ); + let right_to_left_on_demand_parachains = OnDemandParachainsRelay::< + ::ParachainFinality, + >::new( + self.right_relay.clone(), + self.common.left.client.clone(), + self.right_parachains_to_left_transaction_params.clone(), + Arc::new(right_relay_to_left_on_demand_headers), + ); + + Ok(( + Arc::new(left_to_right_on_demand_headers), + Arc::new(right_to_left_on_demand_parachains), + )) + } +} diff --git a/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs b/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs new file mode 100644 index 00000000000..fce80492fc0 --- /dev/null +++ b/relays/bin-substrate/src/cli/relay_headers_and_messages/relay_to_relay.rs @@ -0,0 +1,189 @@ +// Copyright 2019-2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use async_trait::async_trait; +use std::sync::Arc; + +use crate::cli::{ + bridge::{CliBridgeBase, MessagesCliBridge, RelayToRelayHeadersCliBridge}, + relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams}, + CliChain, +}; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, ChainWithTransactions}; +use sp_core::Pair; +use substrate_relay_helper::{ + finality::SubstrateFinalitySyncPipeline, + on_demand::{headers::OnDemandHeadersRelay, OnDemandRelay}, + TaggedAccount, TransactionParams, +}; + +/// A base relay between two standalone (relay) chains. +/// +/// Such relay starts 2 messages relay and 2 on-demand header relays. +pub struct RelayToRelayBridge< + L2R: MessagesCliBridge + RelayToRelayHeadersCliBridge, + R2L: MessagesCliBridge + RelayToRelayHeadersCliBridge, +> { + /// Parameters that are shared by all bridge types. + pub common: + Full2WayBridgeCommonParams<::Target, ::Target>, + /// Override for right->left headers signer. + pub right_to_left_transaction_params: + TransactionParams::Target>>, + /// Override for left->right headers signer. + pub left_to_right_transaction_params: + TransactionParams::Target>>, +} + +macro_rules! declare_relay_to_relay_bridge_schema { + ($left_chain:ident, $right_chain:ident) => { + bp_runtime::paste::item! { + #[doc = $left_chain " and " $right_chain " headers+messages relay params."] + #[derive(Debug, PartialEq, StructOpt)] + pub struct [<$left_chain $right_chain HeadersAndMessages>] { + #[structopt(flatten)] + shared: HeadersAndMessagesSharedParams, + // default signer, which is always used to sign messages relay transactions on the left chain + #[structopt(flatten)] + left: [<$left_chain ConnectionParams>], + // override for right->left headers signer + #[structopt(flatten)] + right_headers_to_left_sign_override: [<$right_chain HeadersTo $left_chain SigningParams>], + #[structopt(flatten)] + left_sign: [<$left_chain SigningParams>], + // default signer, which is always used to sign messages relay transactions on the right chain + #[structopt(flatten)] + right: [<$right_chain ConnectionParams>], + // override for left->right headers signer + #[structopt(flatten)] + left_headers_to_right_sign_override: [<$left_chain HeadersTo $right_chain SigningParams>], + #[structopt(flatten)] + right_sign: [<$right_chain SigningParams>], + } + + impl [<$left_chain $right_chain HeadersAndMessages>] { + async fn into_bridge< + Left: ChainWithTransactions + CliChain, + Right: ChainWithTransactions + CliChain, + L2R: CliBridgeBase + MessagesCliBridge + RelayToRelayHeadersCliBridge, + R2L: CliBridgeBase + MessagesCliBridge + RelayToRelayHeadersCliBridge, + >( + self, + ) -> anyhow::Result> { + Ok(RelayToRelayBridge { + common: Full2WayBridgeCommonParams::new::( + self.shared, + BridgeEndCommonParams { + client: self.left.into_client::().await?, + sign: self.left_sign.to_keypair::()?, + transactions_mortality: self.left_sign.transactions_mortality()?, + accounts: vec![], + }, + BridgeEndCommonParams { + client: self.right.into_client::().await?, + sign: self.right_sign.to_keypair::()?, + transactions_mortality: self.right_sign.transactions_mortality()?, + accounts: vec![], + }, + )?, + right_to_left_transaction_params: self + .right_headers_to_left_sign_override + .transaction_params_or::(&self.left_sign)?, + left_to_right_transaction_params: self + .left_headers_to_right_sign_override + .transaction_params_or::(&self.right_sign)?, + }) + } + } + } + }; +} + +#[async_trait] +impl< + Left: ChainWithTransactions + CliChain, + Right: ChainWithTransactions + CliChain, + L2R: CliBridgeBase + + MessagesCliBridge + + RelayToRelayHeadersCliBridge, + R2L: CliBridgeBase + + MessagesCliBridge + + RelayToRelayHeadersCliBridge, + > Full2WayBridgeBase for RelayToRelayBridge +where + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, +{ + type Params = RelayToRelayBridge; + type Left = Left; + type Right = Right; + + fn common(&self) -> &Full2WayBridgeCommonParams { + &self.common + } + + fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams { + &mut self.common + } + + async fn start_on_demand_headers_relayers( + &mut self, + ) -> anyhow::Result<( + Arc>, + Arc>, + )> { + self.common.right.accounts.push(TaggedAccount::Headers { + id: self.left_to_right_transaction_params.signer.public().into(), + bridged_chain: Self::Left::NAME.to_string(), + }); + self.common.left.accounts.push(TaggedAccount::Headers { + id: self.right_to_left_transaction_params.signer.public().into(), + bridged_chain: Self::Right::NAME.to_string(), + }); + + ::Finality::start_relay_guards( + &self.common.right.client, + &self.left_to_right_transaction_params, + self.common.right.client.can_start_version_guard(), + ) + .await?; + ::Finality::start_relay_guards( + &self.common.left.client, + &self.right_to_left_transaction_params, + self.common.left.client.can_start_version_guard(), + ) + .await?; + + let left_to_right_on_demand_headers = + OnDemandHeadersRelay::<::Finality>::new( + self.common.left.client.clone(), + self.common.right.client.clone(), + self.left_to_right_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + None, + ); + let right_to_left_on_demand_headers = + OnDemandHeadersRelay::<::Finality>::new( + self.common.right.client.clone(), + self.common.left.client.clone(), + self.right_to_left_transaction_params.clone(), + self.common.shared.only_mandatory_headers, + None, + ); + + Ok((Arc::new(left_to_right_on_demand_headers), Arc::new(right_to_left_on_demand_headers))) + } +} diff --git a/relays/bin-substrate/src/cli/relay_messages.rs b/relays/bin-substrate/src/cli/relay_messages.rs new file mode 100644 index 00000000000..1624524e4a5 --- /dev/null +++ b/relays/bin-substrate/src/cli/relay_messages.rs @@ -0,0 +1,133 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use async_trait::async_trait; +use sp_core::Pair; +use structopt::StructOpt; +use strum::VariantNames; + +use crate::bridges::{ + kusama_polkadot::{ + bridge_hub_kusama_messages_to_bridge_hub_polkadot::BridgeHubKusamaToBridgeHubPolkadotMessagesCliBridge, + bridge_hub_polkadot_messages_to_bridge_hub_kusama::BridgeHubPolkadotToBridgeHubKusamaMessagesCliBridge, + }, + rialto_millau::{ + millau_headers_to_rialto::MillauToRialtoCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + }, + rialto_parachain_millau::{ + millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, + }, + rococo_wococo::{ + bridge_hub_rococo_messages_to_bridge_hub_wococo::BridgeHubRococoToBridgeHubWococoMessagesCliBridge, + bridge_hub_wococo_messages_to_bridge_hub_rococo::BridgeHubWococoToBridgeHubRococoMessagesCliBridge, + }, +}; +use relay_substrate_client::{AccountIdOf, AccountKeyPairOf, BalanceOf, ChainWithTransactions}; +use substrate_relay_helper::{messages_lane::MessagesRelayParams, TransactionParams}; + +use crate::cli::{bridge::*, chain_schema::*, CliChain, HexLaneId, PrometheusParams}; + +/// Start messages relayer process. +#[derive(StructOpt)] +pub struct RelayMessages { + /// A bridge instance to relay messages for. + #[structopt(possible_values = FullBridge::VARIANTS, case_insensitive = true)] + bridge: FullBridge, + /// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`. + #[structopt(long, default_value = "00000000")] + lane: HexLaneId, + #[structopt(flatten)] + source: SourceConnectionParams, + #[structopt(flatten)] + source_sign: SourceSigningParams, + #[structopt(flatten)] + target: TargetConnectionParams, + #[structopt(flatten)] + target_sign: TargetSigningParams, + #[structopt(flatten)] + prometheus_params: PrometheusParams, +} + +#[async_trait] +trait MessagesRelayer: MessagesCliBridge +where + Self::Source: ChainWithTransactions + CliChain, + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, + BalanceOf: TryFrom>, +{ + async fn relay_messages(data: RelayMessages) -> anyhow::Result<()> { + let source_client = data.source.into_client::().await?; + let source_sign = data.source_sign.to_keypair::()?; + let source_transactions_mortality = data.source_sign.transactions_mortality()?; + let target_client = data.target.into_client::().await?; + let target_sign = data.target_sign.to_keypair::()?; + let target_transactions_mortality = data.target_sign.transactions_mortality()?; + + substrate_relay_helper::messages_lane::run::(MessagesRelayParams { + source_client, + source_transaction_params: TransactionParams { + signer: source_sign, + mortality: source_transactions_mortality, + }, + target_client, + target_transaction_params: TransactionParams { + signer: target_sign, + mortality: target_transactions_mortality, + }, + source_to_target_headers_relay: None, + target_to_source_headers_relay: None, + lane_id: data.lane.into(), + metrics_params: data.prometheus_params.into_metrics_params()?, + }) + .await + .map_err(|e| anyhow::format_err!("{}", e)) + } +} + +impl MessagesRelayer for MillauToRialtoCliBridge {} +impl MessagesRelayer for RialtoToMillauCliBridge {} +impl MessagesRelayer for MillauToRialtoParachainCliBridge {} +impl MessagesRelayer for RialtoParachainToMillauCliBridge {} +impl MessagesRelayer for BridgeHubRococoToBridgeHubWococoMessagesCliBridge {} +impl MessagesRelayer for BridgeHubWococoToBridgeHubRococoMessagesCliBridge {} +impl MessagesRelayer for BridgeHubKusamaToBridgeHubPolkadotMessagesCliBridge {} +impl MessagesRelayer for BridgeHubPolkadotToBridgeHubKusamaMessagesCliBridge {} + +impl RelayMessages { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + match self.bridge { + FullBridge::MillauToRialto => MillauToRialtoCliBridge::relay_messages(self), + FullBridge::RialtoToMillau => RialtoToMillauCliBridge::relay_messages(self), + FullBridge::MillauToRialtoParachain => + MillauToRialtoParachainCliBridge::relay_messages(self), + FullBridge::RialtoParachainToMillau => + RialtoParachainToMillauCliBridge::relay_messages(self), + FullBridge::BridgeHubRococoToBridgeHubWococo => + BridgeHubRococoToBridgeHubWococoMessagesCliBridge::relay_messages(self), + FullBridge::BridgeHubWococoToBridgeHubRococo => + BridgeHubWococoToBridgeHubRococoMessagesCliBridge::relay_messages(self), + FullBridge::BridgeHubKusamaToBridgeHubPolkadot => + BridgeHubKusamaToBridgeHubPolkadotMessagesCliBridge::relay_messages(self), + FullBridge::BridgeHubPolkadotToBridgeHubKusama => + BridgeHubPolkadotToBridgeHubKusamaMessagesCliBridge::relay_messages(self), + } + .await + } +} diff --git a/relays/bin-substrate/src/cli/relay_parachains.rs b/relays/bin-substrate/src/cli/relay_parachains.rs new file mode 100644 index 00000000000..8c2aa1cc8c1 --- /dev/null +++ b/relays/bin-substrate/src/cli/relay_parachains.rs @@ -0,0 +1,143 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::bridges::{ + kusama_polkadot::{ + kusama_parachains_to_bridge_hub_polkadot::BridgeHubKusamaToBridgeHubPolkadotCliBridge, + polkadot_parachains_to_bridge_hub_kusama::BridgeHubPolkadotToBridgeHubKusamaCliBridge, + }, + rialto_parachain_millau::rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, + rococo_wococo::{ + rococo_parachains_to_bridge_hub_wococo::BridgeHubRococoToBridgeHubWococoCliBridge, + wococo_parachains_to_bridge_hub_rococo::BridgeHubWococoToBridgeHubRococoCliBridge, + }, + westend_millau::westend_parachains_to_millau::WestmintToMillauCliBridge, +}; +use async_std::sync::Mutex; +use async_trait::async_trait; +use parachains_relay::parachains_loop::{AvailableHeader, SourceClient, TargetClient}; +use relay_substrate_client::Parachain; +use relay_utils::metrics::{GlobalMetrics, StandaloneMetric}; +use std::sync::Arc; +use structopt::StructOpt; +use strum::{EnumString, EnumVariantNames, VariantNames}; +use substrate_relay_helper::{ + parachains::{source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter}, + TransactionParams, +}; + +use crate::cli::{ + bridge::{CliBridgeBase, ParachainToRelayHeadersCliBridge}, + chain_schema::*, + PrometheusParams, +}; + +/// Start parachain heads relayer process. +#[derive(StructOpt)] +pub struct RelayParachains { + /// A bridge instance to relay parachains heads for. + #[structopt(possible_values = RelayParachainsBridge::VARIANTS, case_insensitive = true)] + bridge: RelayParachainsBridge, + #[structopt(flatten)] + source: SourceConnectionParams, + #[structopt(flatten)] + target: TargetConnectionParams, + #[structopt(flatten)] + target_sign: TargetSigningParams, + #[structopt(flatten)] + prometheus_params: PrometheusParams, +} + +/// Parachain heads relay bridge. +#[derive(Debug, EnumString, EnumVariantNames)] +#[strum(serialize_all = "kebab_case")] +pub enum RelayParachainsBridge { + RialtoToMillau, + WestendToMillau, + RococoToBridgeHubWococo, + WococoToBridgeHubRococo, + KusamaToBridgeHubPolkadot, + PolkadotToBridgeHubKusama, +} + +#[async_trait] +trait ParachainsRelayer: ParachainToRelayHeadersCliBridge +where + ParachainsSource: + SourceClient>, + ParachainsTarget: + TargetClient>, + ::Source: Parachain, +{ + async fn relay_parachains(data: RelayParachains) -> anyhow::Result<()> { + let source_client = data.source.into_client::().await?; + let source_client = ParachainsSource::::new( + source_client, + Arc::new(Mutex::new(AvailableHeader::Missing)), + ); + + let target_transaction_params = TransactionParams { + signer: data.target_sign.to_keypair::()?, + mortality: data.target_sign.target_transactions_mortality, + }; + let target_client = data.target.into_client::().await?; + let target_client = ParachainsTarget::::new( + target_client.clone(), + target_transaction_params, + ); + + let metrics_params: relay_utils::metrics::MetricsParams = + data.prometheus_params.into_metrics_params()?; + GlobalMetrics::new()?.register_and_spawn(&metrics_params.registry)?; + + parachains_relay::parachains_loop::run( + source_client, + target_client, + metrics_params, + futures::future::pending(), + ) + .await + .map_err(|e| anyhow::format_err!("{}", e)) + } +} + +impl ParachainsRelayer for RialtoParachainToMillauCliBridge {} +impl ParachainsRelayer for WestmintToMillauCliBridge {} +impl ParachainsRelayer for BridgeHubRococoToBridgeHubWococoCliBridge {} +impl ParachainsRelayer for BridgeHubWococoToBridgeHubRococoCliBridge {} +impl ParachainsRelayer for BridgeHubKusamaToBridgeHubPolkadotCliBridge {} +impl ParachainsRelayer for BridgeHubPolkadotToBridgeHubKusamaCliBridge {} + +impl RelayParachains { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + match self.bridge { + RelayParachainsBridge::RialtoToMillau => + RialtoParachainToMillauCliBridge::relay_parachains(self), + RelayParachainsBridge::WestendToMillau => + WestmintToMillauCliBridge::relay_parachains(self), + RelayParachainsBridge::RococoToBridgeHubWococo => + BridgeHubRococoToBridgeHubWococoCliBridge::relay_parachains(self), + RelayParachainsBridge::WococoToBridgeHubRococo => + BridgeHubWococoToBridgeHubRococoCliBridge::relay_parachains(self), + RelayParachainsBridge::KusamaToBridgeHubPolkadot => + BridgeHubKusamaToBridgeHubPolkadotCliBridge::relay_parachains(self), + RelayParachainsBridge::PolkadotToBridgeHubKusama => + BridgeHubPolkadotToBridgeHubKusamaCliBridge::relay_parachains(self), + } + .await + } +} diff --git a/relays/bin-substrate/src/cli/resubmit_transactions.rs b/relays/bin-substrate/src/cli/resubmit_transactions.rs new file mode 100644 index 00000000000..c5b20e939c1 --- /dev/null +++ b/relays/bin-substrate/src/cli/resubmit_transactions.rs @@ -0,0 +1,560 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::cli::{chain_schema::*, Balance}; + +use bp_runtime::HeaderIdProvider; +use codec::{Decode, Encode}; +use num_traits::{One, Zero}; +use relay_substrate_client::{ + AccountKeyPairOf, BlockWithJustification, Chain, ChainWithTransactions, Client, + Error as SubstrateError, HeaderIdOf, HeaderOf, SignParam, +}; +use relay_utils::FailedClient; +use sp_core::Bytes; +use sp_runtime::{ + traits::{Hash, Header as HeaderT}, + transaction_validity::TransactionPriority, +}; +use structopt::StructOpt; +use strum::{EnumString, EnumVariantNames, VariantNames}; +use substrate_relay_helper::TransactionParams; + +/// Start resubmit transactions process. +#[derive(StructOpt)] +pub struct ResubmitTransactions { + /// A bridge instance to relay headers for. + #[structopt(possible_values = RelayChain::VARIANTS, case_insensitive = true)] + chain: RelayChain, + #[structopt(flatten)] + target: TargetConnectionParams, + #[structopt(flatten)] + target_sign: TargetSigningParams, + /// Number of blocks we see before considering queued transaction as stalled. + #[structopt(long, default_value = "5")] + stalled_blocks: u32, + /// Tip limit. We'll never submit transaction with larger tip. + #[structopt(long)] + tip_limit: Balance, + /// Tip increase step. We'll be checking updated transaction priority by increasing its tip by + /// this step. + #[structopt(long)] + tip_step: Balance, + /// Priority selection strategy. + #[structopt(subcommand)] + strategy: PrioritySelectionStrategy, +} + +/// Chain, which transactions we're going to track && resubmit. +#[derive(Debug, EnumString, EnumVariantNames)] +#[strum(serialize_all = "kebab_case")] +pub enum RelayChain { + Millau, +} + +/// Strategy to use for priority selection. +#[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy)] +pub enum PrioritySelectionStrategy { + /// Strategy selects tip that changes transaction priority to be better than priority of + /// the first transaction of previous block. + /// + /// It only makes sense to use this strategy for Millau transactions. Millau has transactions + /// that are close to block limits, so if there are any other queued transactions, 'large' + /// transaction won't fit the block && will be postponed. To avoid this, we change its priority + /// to some large value, making it best transaction => it'll be 'mined' first. + MakeItBestTransaction, + /// Strategy selects tip that changes transaction priority to be better than priority of + /// selected queued transaction. + /// + /// When we first see stalled transaction, we make it better than worst 1/4 of queued + /// transactions. If it is still stalled, we'll make it better than 1/3 of queued transactions, + /// ... + MakeItBetterThanQueuedTransaction, +} + +macro_rules! select_bridge { + ($bridge: expr, $generic: tt) => { + match $bridge { + RelayChain::Millau => { + type Target = relay_millau_client::Millau; + + $generic + }, + } + }; +} + +impl ResubmitTransactions { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + select_bridge!(self.chain, { + let relay_loop_name = format!("ResubmitTransactions{}", Target::NAME); + let client = self.target.into_client::().await?; + let transaction_params = TransactionParams { + signer: self.target_sign.to_keypair::()?, + mortality: self.target_sign.target_transactions_mortality, + }; + + relay_utils::relay_loop((), client) + .run(relay_loop_name, move |_, client, _| { + run_until_connection_lost( + client, + transaction_params.clone(), + Context { + strategy: self.strategy, + best_header: HeaderOf::::new( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ), + transaction: None, + resubmitted: 0, + stalled_for: Zero::zero(), + stalled_for_limit: self.stalled_blocks as _, + tip_step: self.tip_step.cast() as _, + tip_limit: self.tip_limit.cast() as _, + }, + ) + }) + .await + .map_err(Into::into) + }) + } +} + +impl PrioritySelectionStrategy { + /// Select target priority. + async fn select_target_priority( + &self, + client: &Client, + context: &Context, + ) -> Result, SubstrateError> { + match *self { + PrioritySelectionStrategy::MakeItBestTransaction => + read_previous_block_best_priority(client, context).await, + PrioritySelectionStrategy::MakeItBetterThanQueuedTransaction => + select_priority_from_queue(client, context).await, + } + } +} + +#[derive(Debug)] +struct Context { + /// Priority selection strategy. + strategy: PrioritySelectionStrategy, + /// Best known block header. + best_header: C::Header, + /// Hash of the (potentially) stalled transaction. + transaction: Option, + /// How many times we have resubmitted this `transaction`? + resubmitted: u32, + /// This transaction is in pool for `stalled_for` wakeup intervals. + stalled_for: C::BlockNumber, + /// When `stalled_for` reaching this limit, transaction is considered stalled. + stalled_for_limit: C::BlockNumber, + /// Tip step interval. + tip_step: C::Balance, + /// Maximal tip. + tip_limit: C::Balance, +} + +impl Context { + /// Return true if transaction has stalled. + fn is_stalled(&self) -> bool { + self.stalled_for >= self.stalled_for_limit + } + + /// Notice resubmitted transaction. + fn notice_resubmitted_transaction(mut self, transaction: C::Hash) -> Self { + self.transaction = Some(transaction); + self.stalled_for = Zero::zero(); + self.resubmitted += 1; + self + } + + /// Notice transaction from the transaction pool. + fn notice_transaction(mut self, transaction: C::Hash) -> Self { + if self.transaction == Some(transaction) { + self.stalled_for += One::one(); + } else { + self.transaction = Some(transaction); + self.stalled_for = One::one(); + self.resubmitted = 0; + } + self + } +} + +/// Run resubmit transactions loop. +async fn run_until_connection_lost( + client: Client, + transaction_params: TransactionParams>, + mut context: Context, +) -> Result<(), FailedClient> { + loop { + async_std::task::sleep(C::AVERAGE_BLOCK_INTERVAL).await; + + let result = run_loop_iteration(client.clone(), transaction_params.clone(), context).await; + context = match result { + Ok(context) => context, + Err(error) => { + log::error!( + target: "bridge", + "Resubmit {} transactions loop has failed with error: {:?}", + C::NAME, + error, + ); + return Err(FailedClient::Target) + }, + }; + } +} + +/// Run single loop iteration. +async fn run_loop_iteration( + client: Client, + transaction_params: TransactionParams>, + mut context: Context, +) -> Result, SubstrateError> { + // correct best header is required for all other actions + context.best_header = client.best_header().await?; + + // check if there's queued transaction, signed by given author + let original_transaction = + match lookup_signer_transaction(&client, &transaction_params.signer).await? { + Some(original_transaction) => original_transaction, + None => { + log::trace!(target: "bridge", "No {} transactions from required signer in the txpool", C::NAME); + return Ok(context) + }, + }; + let original_transaction_hash = C::Hasher::hash(&original_transaction.encode()); + let context = context.notice_transaction(original_transaction_hash); + + // if transaction hasn't been mined for `stalled_blocks`, we'll need to resubmit it + if !context.is_stalled() { + log::trace!( + target: "bridge", + "{} transaction {:?} is not yet stalled ({:?}/{:?})", + C::NAME, + context.transaction, + context.stalled_for, + context.stalled_for_limit, + ); + return Ok(context) + } + + // select priority for updated transaction + let target_priority = match context.strategy.select_target_priority(&client, &context).await? { + Some(target_priority) => target_priority, + None => { + log::trace!(target: "bridge", "Failed to select target priority"); + return Ok(context) + }, + }; + + // update transaction tip + let (is_updated, updated_transaction) = update_transaction_tip( + &client, + &transaction_params, + context.best_header.id(), + original_transaction, + context.tip_step, + context.tip_limit, + target_priority, + ) + .await?; + + if !is_updated { + log::trace!(target: "bridge", "{} transaction tip can not be updated. Reached limit?", C::NAME); + return Ok(context) + } + + let updated_transaction = updated_transaction.encode(); + let updated_transaction_hash = C::Hasher::hash(&updated_transaction); + client.submit_unsigned_extrinsic(Bytes(updated_transaction)).await?; + + log::info!( + target: "bridge", + "Replaced {} transaction {} with {} in txpool", + C::NAME, + original_transaction_hash, + updated_transaction_hash, + ); + + Ok(context.notice_resubmitted_transaction(updated_transaction_hash)) +} + +/// Search transaction pool for transaction, signed by given key pair. +async fn lookup_signer_transaction( + client: &Client, + key_pair: &AccountKeyPairOf, +) -> Result, SubstrateError> { + let pending_transactions = client.pending_extrinsics().await?; + for pending_transaction in pending_transactions { + let pending_transaction = C::SignedTransaction::decode(&mut &pending_transaction.0[..]) + .map_err(SubstrateError::ResponseParseFailed)?; + if !C::is_signed_by(key_pair, &pending_transaction) { + continue + } + + return Ok(Some(pending_transaction)) + } + + Ok(None) +} + +/// Read priority of best signed transaction of previous block. +async fn read_previous_block_best_priority( + client: &Client, + context: &Context, +) -> Result, SubstrateError> { + let best_block = client.get_block(Some(context.best_header.hash())).await?; + let best_transaction = best_block + .extrinsics() + .iter() + .filter_map(|xt| C::SignedTransaction::decode(&mut &xt[..]).ok()) + .find(|xt| C::is_signed(xt)); + match best_transaction { + Some(best_transaction) => Ok(Some( + client + .validate_transaction(*context.best_header.parent_hash(), best_transaction) + .await?? + .priority, + )), + None => Ok(None), + } +} + +/// Select priority of some queued transaction. +async fn select_priority_from_queue( + client: &Client, + context: &Context, +) -> Result, SubstrateError> { + // select transaction from the queue + let queued_transactions = client.pending_extrinsics().await?; + let selected_transaction = match select_transaction_from_queue(queued_transactions, context) { + Some(selected_transaction) => selected_transaction, + None => return Ok(None), + }; + + let selected_transaction = C::SignedTransaction::decode(&mut &selected_transaction[..]) + .map_err(SubstrateError::ResponseParseFailed)?; + let target_priority = client + .validate_transaction(context.best_header.hash(), selected_transaction) + .await?? + .priority; + Ok(Some(target_priority)) +} + +/// Select transaction with target priority from the vec of queued transactions. +fn select_transaction_from_queue( + mut queued_transactions: Vec, + context: &Context, +) -> Option { + if queued_transactions.is_empty() { + return None + } + + // the more times we resubmit transaction (`context.resubmitted`), the closer we move + // to the front of the transaction queue + let total_transactions = queued_transactions.len(); + let resubmitted_factor = context.resubmitted; + let divisor = + 1usize.saturating_add(1usize.checked_shl(resubmitted_factor).unwrap_or(usize::MAX)); + let transactions_to_skip = total_transactions / divisor; + + Some( + queued_transactions + .swap_remove(std::cmp::min(total_transactions - 1, transactions_to_skip)), + ) +} + +/// Try to find appropriate tip for transaction so that its priority is larger than given. +async fn update_transaction_tip( + client: &Client, + transaction_params: &TransactionParams>, + at_block: HeaderIdOf, + tx: C::SignedTransaction, + tip_step: C::Balance, + tip_limit: C::Balance, + target_priority: TransactionPriority, +) -> Result<(bool, C::SignedTransaction), SubstrateError> { + let stx = format!("{tx:?}"); + let mut current_priority = client.validate_transaction(at_block.1, tx.clone()).await??.priority; + let mut unsigned_tx = C::parse_transaction(tx).ok_or_else(|| { + SubstrateError::Custom(format!("Failed to parse {} transaction {stx}", C::NAME,)) + })?; + let old_tip = unsigned_tx.tip; + + let runtime_version = client.simple_runtime_version().await?; + while current_priority < target_priority { + let next_tip = unsigned_tx.tip + tip_step; + if next_tip > tip_limit { + break + } + + log::trace!( + target: "bridge", + "{} transaction priority with tip={:?}: {}. Target priority: {}", + C::NAME, + unsigned_tx.tip, + current_priority, + target_priority, + ); + + unsigned_tx.tip = next_tip; + current_priority = client + .validate_transaction( + at_block.1, + C::sign_transaction( + SignParam { + spec_version: runtime_version.spec_version, + transaction_version: runtime_version.transaction_version, + genesis_hash: *client.genesis_hash(), + signer: transaction_params.signer.clone(), + }, + unsigned_tx.clone(), + )?, + ) + .await?? + .priority; + } + + log::debug!( + target: "bridge", + "{} transaction tip has changed from {:?} to {:?}", + C::NAME, + old_tip, + unsigned_tx.tip, + ); + + Ok(( + old_tip != unsigned_tx.tip, + C::sign_transaction( + SignParam { + spec_version: runtime_version.spec_version, + transaction_version: runtime_version.transaction_version, + genesis_hash: *client.genesis_hash(), + signer: transaction_params.signer.clone(), + }, + unsigned_tx.era(relay_substrate_client::TransactionEra::new( + at_block, + transaction_params.mortality, + )), + )?, + )) +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_rialto::Hash; + use relay_rialto_client::Rialto; + + fn context() -> Context { + Context { + strategy: PrioritySelectionStrategy::MakeItBestTransaction, + best_header: HeaderOf::::new( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ), + transaction: None, + resubmitted: 0, + stalled_for: Zero::zero(), + stalled_for_limit: 3, + tip_step: 100, + tip_limit: 1000, + } + } + + #[test] + fn context_works() { + let mut context = context(); + + // when transaction is noticed 2/3 times, it isn't stalled + context = context.notice_transaction(Default::default()); + assert!(!context.is_stalled()); + assert_eq!(context.stalled_for, 1); + assert_eq!(context.resubmitted, 0); + context = context.notice_transaction(Default::default()); + assert!(!context.is_stalled()); + assert_eq!(context.stalled_for, 2); + assert_eq!(context.resubmitted, 0); + + // when transaction is noticed for 3rd time in a row, it is considered stalled + context = context.notice_transaction(Default::default()); + assert!(context.is_stalled()); + assert_eq!(context.stalled_for, 3); + assert_eq!(context.resubmitted, 0); + + // and after we resubmit it, we forget previous transaction + context = context.notice_resubmitted_transaction(Hash::from([1; 32])); + assert_eq!(context.transaction, Some(Hash::from([1; 32]))); + assert_eq!(context.resubmitted, 1); + assert_eq!(context.stalled_for, 0); + } + + #[test] + fn select_transaction_from_queue_works_with_empty_queue() { + assert_eq!(select_transaction_from_queue(vec![], &context()), None); + } + + #[test] + fn select_transaction_from_queue_works() { + let mut context = context(); + let queued_transactions = vec![ + Bytes(vec![1]), + Bytes(vec![2]), + Bytes(vec![3]), + Bytes(vec![4]), + Bytes(vec![5]), + Bytes(vec![6]), + ]; + + // when we resubmit tx for the first time, 1/2 of queue is skipped + assert_eq!( + select_transaction_from_queue(queued_transactions.clone(), &context), + Some(Bytes(vec![4])), + ); + + // when we resubmit tx for the second time, 1/3 of queue is skipped + context = context.notice_resubmitted_transaction(Hash::from([1; 32])); + assert_eq!( + select_transaction_from_queue(queued_transactions.clone(), &context), + Some(Bytes(vec![3])), + ); + + // when we resubmit tx for the third time, 1/5 of queue is skipped + context = context.notice_resubmitted_transaction(Hash::from([2; 32])); + assert_eq!( + select_transaction_from_queue(queued_transactions.clone(), &context), + Some(Bytes(vec![2])), + ); + + // when we resubmit tx for the second time, 1/9 of queue is skipped + context = context.notice_resubmitted_transaction(Hash::from([3; 32])); + assert_eq!( + select_transaction_from_queue(queued_transactions, &context), + Some(Bytes(vec![1])), + ); + } +} diff --git a/relays/bin-substrate/src/cli/send_message.rs b/relays/bin-substrate/src/cli/send_message.rs new file mode 100644 index 00000000000..8f76e501f69 --- /dev/null +++ b/relays/bin-substrate/src/cli/send_message.rs @@ -0,0 +1,187 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{ + bridges::{ + rialto_millau::{ + millau_headers_to_rialto::MillauToRialtoCliBridge, + rialto_headers_to_millau::RialtoToMillauCliBridge, + }, + rialto_parachain_millau::{ + millau_headers_to_rialto_parachain::MillauToRialtoParachainCliBridge, + rialto_parachains_to_millau::RialtoParachainToMillauCliBridge, + }, + }, + cli::{ + bridge::{FullBridge, MessagesCliBridge}, + chain_schema::*, + encode_message::{self, CliEncodeMessage, RawMessage}, + CliChain, + }, +}; +use async_trait::async_trait; +use codec::{Decode, Encode}; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, Chain, ChainBase, ChainWithTransactions, UnsignedTransaction, +}; +use sp_core::Pair; +use sp_runtime::AccountId32; +use std::fmt::Display; +use structopt::StructOpt; +use strum::VariantNames; + +/// Send bridge message. +#[derive(StructOpt)] +pub struct SendMessage { + /// A bridge instance to encode call for. + #[structopt(possible_values = FullBridge::VARIANTS, case_insensitive = true)] + bridge: FullBridge, + #[structopt(flatten)] + source: SourceConnectionParams, + #[structopt(flatten)] + source_sign: SourceSigningParams, + /// Message type. + #[structopt(subcommand)] + message: crate::cli::encode_message::Message, +} + +#[async_trait] +trait MessageSender: MessagesCliBridge +where + Self::Source: ChainBase + ChainWithTransactions + CliChain + CliEncodeMessage, + ::Balance: Display + From + Into, + ::Call: Sync, + ::SignedTransaction: Sync, + AccountIdOf: From< as Pair>::Public>, + AccountId32: From< as Pair>::Public>, +{ + async fn send_message(data: SendMessage) -> anyhow::Result<()> { + let payload = encode_message::encode_message::(&data.message)?; + + let source_client = data.source.into_client::().await?; + let source_sign = data.source_sign.to_keypair::()?; + + let payload_len = payload.encoded_size(); + let send_message_call = Self::Source::encode_execute_xcm(decode_xcm(payload)?)?; + + source_client + .submit_signed_extrinsic(&source_sign, move |_, transaction_nonce| { + let unsigned = UnsignedTransaction::new(send_message_call, transaction_nonce); + log::info!( + target: "bridge", + "Sending message to {}. Size: {}", + Self::Target::NAME, + payload_len, + ); + Ok(unsigned) + }) + .await?; + + Ok(()) + } +} + +impl MessageSender for MillauToRialtoCliBridge {} +impl MessageSender for RialtoToMillauCliBridge {} +impl MessageSender for MillauToRialtoParachainCliBridge {} +impl MessageSender for RialtoParachainToMillauCliBridge {} + +impl SendMessage { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + match self.bridge { + FullBridge::MillauToRialto => MillauToRialtoCliBridge::send_message(self), + FullBridge::RialtoToMillau => RialtoToMillauCliBridge::send_message(self), + FullBridge::MillauToRialtoParachain => + MillauToRialtoParachainCliBridge::send_message(self), + FullBridge::RialtoParachainToMillau => + RialtoParachainToMillauCliBridge::send_message(self), + FullBridge::BridgeHubRococoToBridgeHubWococo => unimplemented!( + "Sending message from BridgeHubRococo to BridgeHubWococo is not supported" + ), + FullBridge::BridgeHubWococoToBridgeHubRococo => unimplemented!( + "Sending message from BridgeHubWococo to BridgeHubRococo is not supported" + ), + FullBridge::BridgeHubKusamaToBridgeHubPolkadot => unimplemented!( + "Sending message from BridgeHubKusama to BridgeHubPolkadot is not supported" + ), + FullBridge::BridgeHubPolkadotToBridgeHubKusama => unimplemented!( + "Sending message from BridgeHubPolkadot to BridgeHubKusama is not supported" + ), + } + .await + } +} + +/// Decode SCALE encoded raw XCM message. +pub(crate) fn decode_xcm(message: RawMessage) -> anyhow::Result> { + Decode::decode(&mut &message[..]) + .map_err(|e| anyhow::format_err!("Failed to decode XCM program: {:?}", e)) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::{ExplicitOrMaximal, HexBytes}; + + #[test] + fn send_raw_rialto_to_millau() { + // given + let send_message = SendMessage::from_iter(vec![ + "send-message", + "rialto-to-millau", + "--source-port", + "1234", + "--source-signer", + "//Alice", + "raw", + "dead", + ]); + + // then + assert_eq!(send_message.bridge, FullBridge::RialtoToMillau); + assert_eq!(send_message.source.source_port, 1234); + assert_eq!(send_message.source_sign.source_signer, Some("//Alice".into())); + assert_eq!( + send_message.message, + crate::cli::encode_message::Message::Raw { data: HexBytes(vec![0xDE, 0xAD]) } + ); + } + + #[test] + fn send_sized_rialto_to_millau() { + // given + let send_message = SendMessage::from_iter(vec![ + "send-message", + "rialto-to-millau", + "--source-port", + "1234", + "--source-signer", + "//Alice", + "sized", + "max", + ]); + + // then + assert_eq!(send_message.bridge, FullBridge::RialtoToMillau); + assert_eq!(send_message.source.source_port, 1234); + assert_eq!(send_message.source_sign.source_signer, Some("//Alice".into())); + assert_eq!( + send_message.message, + crate::cli::encode_message::Message::Sized { size: ExplicitOrMaximal::Maximal } + ); + } +} diff --git a/relays/bin-substrate/src/main.rs b/relays/bin-substrate/src/main.rs new file mode 100644 index 00000000000..33a423b0766 --- /dev/null +++ b/relays/bin-substrate/src/main.rs @@ -0,0 +1,29 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate-to-substrate relay entrypoint. + +#![warn(missing_docs)] + +mod bridges; +mod chains; +mod cli; + +fn main() { + let command = cli::parse_args(); + let run = command.run(); + async_std::task::block_on(run); +} diff --git a/relays/client-bridge-hub-kusama/Cargo.toml b/relays/client-bridge-hub-kusama/Cargo.toml new file mode 100644 index 00000000000..96650710f67 --- /dev/null +++ b/relays/client-bridge-hub-kusama/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "relay-bridge-hub-kusama-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +relay-substrate-client = { path = "../client-substrate" } + +# Bridge dependencies + +bp-bridge-hub-kusama = { path = "../../primitives/chain-bridge-hub-kusama" } +bp-bridge-hub-polkadot = { path = "../../primitives/chain-bridge-hub-polkadot" } +bp-header-chain = { path = "../../primitives/header-chain" } +bp-messages = { path = "../../primitives/messages" } +bp-parachains = { path = "../../primitives/parachains" } +bp-runtime = { path = "../../primitives/runtime" } +bp-polkadot = { path = "../../primitives/chain-polkadot" } + +bridge-runtime-common = { path = "../../bin/runtime-common" } + +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-bridge-hub-kusama/src/lib.rs b/relays/client-bridge-hub-kusama/src/lib.rs new file mode 100644 index 00000000000..6549a32ca79 --- /dev/null +++ b/relays/client-bridge-hub-kusama/src/lib.rs @@ -0,0 +1,162 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the BridgeHub-Kusama-Substrate parachain. + +use bp_bridge_hub_kusama::{BridgeHubSignedExtension, AVERAGE_BLOCK_INTERVAL}; +use bp_messages::MessageNonce; +use bp_runtime::ChainId; +use codec::Encode; +use relay_substrate_client::{ + Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, ChainWithUtilityPallet, + Error as SubstrateError, MockedRuntimeUtilityPallet, SignParam, UnderlyingChainProvider, + UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; +use std::time::Duration; + +/// Re-export runtime wrapper +pub mod runtime_wrapper; +pub use runtime_wrapper as runtime; + +/// Kusama chain definition +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct BridgeHubKusama; + +impl UnderlyingChainProvider for BridgeHubKusama { + type Chain = bp_bridge_hub_kusama::BridgeHubKusama; +} + +impl Chain for BridgeHubKusama { + const ID: ChainId = bp_runtime::BRIDGE_HUB_KUSAMA_CHAIN_ID; + const NAME: &'static str = "BridgeHubKusama"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_bridge_hub_kusama::BEST_FINALIZED_BRIDGE_HUB_KUSAMA_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = AVERAGE_BLOCK_INTERVAL; + + type SignedBlock = bp_bridge_hub_kusama::SignedBlock; + type Call = runtime::Call; +} + +impl ChainWithBalances for BridgeHubKusama { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + bp_bridge_hub_kusama::AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +impl ChainWithUtilityPallet for BridgeHubKusama { + type UtilityPallet = MockedRuntimeUtilityPallet; +} + +impl ChainWithTransactions for BridgeHubKusama { + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = runtime::UncheckedExtrinsic; + + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result { + let raw_payload = SignedPayload::new( + unsigned.call, + runtime::SignedExtension::from_params( + param.spec_version, + param.transaction_version, + unsigned.era, + param.genesis_hash, + unsigned.nonce, + unsigned.tip, + ), + )?; + + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(runtime::UncheckedExtrinsic::new_signed( + call, + signer.into_account().into(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| { + *address == bp_bridge_hub_kusama::Address::Id(signer.public().into()) + }) + .unwrap_or(false) + } + + fn parse_transaction(tx: Self::SignedTransaction) -> Option> { + let extra = &tx.signature.as_ref()?.2; + Some(UnsignedTransaction::new(tx.function, extra.nonce()).tip(extra.tip())) + } +} + +impl ChainWithMessages for BridgeHubKusama { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_bridge_hub_kusama::WITH_BRIDGE_HUB_KUSAMA_MESSAGES_PALLET_NAME; + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = + Some(bp_bridge_hub_kusama::WITH_BRIDGE_HUB_KUSAMA_RELAYERS_PALLET_NAME); + + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_kusama::TO_BRIDGE_HUB_KUSAMA_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_kusama::FROM_BRIDGE_HUB_KUSAMA_MESSAGE_DETAILS_METHOD; + + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_kusama::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_kusama::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; +} + +#[cfg(test)] +mod tests { + use super::*; + use relay_substrate_client::TransactionEra; + + #[test] + fn parse_transaction_works() { + let unsigned = UnsignedTransaction { + call: runtime::Call::System(relay_substrate_client::calls::SystemCall::remark( + b"Hello world!".to_vec(), + )) + .into(), + nonce: 777, + tip: 888, + era: TransactionEra::immortal(), + }; + let signed_transaction = BridgeHubKusama::sign_transaction( + SignParam { + spec_version: 42, + transaction_version: 50000, + genesis_hash: [42u8; 32].into(), + signer: sp_core::sr25519::Pair::from_seed_slice(&[1u8; 32]).unwrap(), + }, + unsigned.clone(), + ) + .unwrap(); + let parsed_transaction = BridgeHubKusama::parse_transaction(signed_transaction).unwrap(); + assert_eq!(parsed_transaction, unsigned); + } +} diff --git a/relays/client-bridge-hub-kusama/src/runtime_wrapper.rs b/relays/client-bridge-hub-kusama/src/runtime_wrapper.rs new file mode 100644 index 00000000000..18bdafc0f04 --- /dev/null +++ b/relays/client-bridge-hub-kusama/src/runtime_wrapper.rs @@ -0,0 +1,74 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types that are specific to the BridgeHubKusama runtime. +// TODO: regenerate me using `runtime-codegen` tool? (https://github.com/paritytech/parity-bridges-common/issues/1945) + +use codec::{Decode, Encode}; +use scale_info::TypeInfo; + +pub use bp_bridge_hub_kusama::SignedExtension; +pub use bp_header_chain::BridgeGrandpaCallOf; +pub use bp_parachains::BridgeParachainCall; +pub use bridge_runtime_common::messages::BridgeMessagesCallOf; +pub use relay_substrate_client::calls::{SystemCall, UtilityCall}; + +/// Unchecked BridgeHubKusama extrinsic. +pub type UncheckedExtrinsic = bp_bridge_hub_kusama::UncheckedExtrinsic; + +// The indirect pallet call used to sync `Polkadot` GRANDPA finality to `BHKusama`. +pub type BridgePolkadotGrandpaCall = BridgeGrandpaCallOf; +// The indirect pallet call used to sync `BridgeHubPolkadot` messages to `BHKusama`. +pub type BridgePolkadotMessagesCall = + BridgeMessagesCallOf; + +/// `BridgeHubKusama` Runtime `Call` enum. +/// +/// The enum represents a subset of possible `Call`s we can send to `BridgeHubKusama` chain. +/// Ideally this code would be auto-generated from metadata, because we want to +/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. +/// +/// All entries here (like pretty much in the entire file) must be kept in sync with +/// `BridgeHubKusama` `construct_runtime`, so that we maintain SCALE-compatibility. +#[allow(clippy::large_enum_variant)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum Call { + #[cfg(test)] + #[codec(index = 0)] + System(SystemCall), + /// Utility pallet. + #[codec(index = 40)] + Utility(UtilityCall), + + /// Polkadot bridge pallet. + // TODO: the index is wrong (https://github.com/paritytech/parity-bridges-common/issues/1945) + #[codec(index = 41)] + BridgePolkadotGrandpa(BridgePolkadotGrandpaCall), + /// Polkadot parachain bridge pallet. + // TODO: the index is wrong (https://github.com/paritytech/parity-bridges-common/issues/1945) + #[codec(index = 42)] + BridgePolkadotParachain(BridgeParachainCall), + /// Polkadot messages bridge pallet. + // TODO: the index is wrong (https://github.com/paritytech/parity-bridges-common/issues/1945) + #[codec(index = 46)] + BridgePolkadotMessages(BridgePolkadotMessagesCall), +} + +impl From> for Call { + fn from(call: UtilityCall) -> Call { + Call::Utility(call) + } +} diff --git a/relays/client-bridge-hub-polkadot/Cargo.toml b/relays/client-bridge-hub-polkadot/Cargo.toml new file mode 100644 index 00000000000..6126a8f2b3f --- /dev/null +++ b/relays/client-bridge-hub-polkadot/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "relay-bridge-hub-polkadot-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +relay-substrate-client = { path = "../client-substrate" } + +# Bridge dependencies + +bp-bridge-hub-kusama = { path = "../../primitives/chain-bridge-hub-kusama" } +bp-bridge-hub-polkadot = { path = "../../primitives/chain-bridge-hub-polkadot" } +bp-header-chain = { path = "../../primitives/header-chain" } +bp-messages = { path = "../../primitives/messages" } +bp-parachains = { path = "../../primitives/parachains" } +bp-kusama = { path = "../../primitives/chain-kusama" } +bp-runtime = { path = "../../primitives/runtime" } + +bridge-runtime-common = { path = "../../bin/runtime-common" } + +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[dev-dependencies] +bp-polkadot-core = { path = "../../primitives/polkadot-core" } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-bridge-hub-polkadot/src/lib.rs b/relays/client-bridge-hub-polkadot/src/lib.rs new file mode 100644 index 00000000000..6428b055dd5 --- /dev/null +++ b/relays/client-bridge-hub-polkadot/src/lib.rs @@ -0,0 +1,160 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the BridgeHub-Polkadot-Substrate parachain. + +use bp_bridge_hub_polkadot::{BridgeHubSignedExtension, AVERAGE_BLOCK_INTERVAL}; +use bp_messages::MessageNonce; +use bp_runtime::ChainId; +use codec::Encode; +use relay_substrate_client::{ + Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, ChainWithUtilityPallet, + Error as SubstrateError, MockedRuntimeUtilityPallet, SignParam, UnderlyingChainProvider, + UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; +use std::time::Duration; + +/// Re-export runtime wrapper +pub mod runtime_wrapper; +pub use runtime_wrapper as runtime; + +/// Polkadot chain definition +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct BridgeHubPolkadot; + +impl UnderlyingChainProvider for BridgeHubPolkadot { + type Chain = bp_bridge_hub_polkadot::BridgeHubPolkadot; +} + +impl Chain for BridgeHubPolkadot { + const ID: ChainId = bp_runtime::BRIDGE_HUB_POLKADOT_CHAIN_ID; + const NAME: &'static str = "BridgeHubPolkadot"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_bridge_hub_polkadot::BEST_FINALIZED_BRIDGE_HUB_POLKADOT_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = AVERAGE_BLOCK_INTERVAL; + + type SignedBlock = bp_bridge_hub_polkadot::SignedBlock; + type Call = runtime::Call; +} + +impl ChainWithBalances for BridgeHubPolkadot { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + bp_bridge_hub_polkadot::AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +impl ChainWithUtilityPallet for BridgeHubPolkadot { + type UtilityPallet = MockedRuntimeUtilityPallet; +} + +impl ChainWithTransactions for BridgeHubPolkadot { + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = runtime::UncheckedExtrinsic; + + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result { + let raw_payload = SignedPayload::new( + unsigned.call, + runtime::SignedExtension::from_params( + param.spec_version, + param.transaction_version, + unsigned.era, + param.genesis_hash, + unsigned.nonce, + unsigned.tip, + ), + )?; + + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(runtime::UncheckedExtrinsic::new_signed( + call, + signer.into_account().into(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| { + *address == bp_bridge_hub_polkadot::Address::Id(signer.public().into()) + }) + .unwrap_or(false) + } + + fn parse_transaction(tx: Self::SignedTransaction) -> Option> { + let extra = &tx.signature.as_ref()?.2; + Some(UnsignedTransaction::new(tx.function, extra.nonce()).tip(extra.tip())) + } +} + +impl ChainWithMessages for BridgeHubPolkadot { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_bridge_hub_polkadot::WITH_BRIDGE_HUB_POLKADOT_MESSAGES_PALLET_NAME; + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = + Some(bp_bridge_hub_polkadot::WITH_BRIDGE_HUB_POLKADOT_RELAYERS_PALLET_NAME); + + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_polkadot::TO_BRIDGE_HUB_POLKADOT_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_polkadot::FROM_BRIDGE_HUB_POLKADOT_MESSAGE_DETAILS_METHOD; + + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_polkadot::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_polkadot::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; +} + +#[cfg(test)] +mod tests { + use super::*; + use relay_substrate_client::TransactionEra; + + #[test] + fn parse_transaction_works() { + let unsigned = UnsignedTransaction { + call: runtime::Call::System(runtime::SystemCall::remark(b"Hello world!".to_vec())) + .into(), + nonce: 777, + tip: 888, + era: TransactionEra::immortal(), + }; + let signed_transaction = BridgeHubPolkadot::sign_transaction( + SignParam { + spec_version: 42, + transaction_version: 50000, + genesis_hash: [42u8; 32].into(), + signer: sp_core::sr25519::Pair::from_seed_slice(&[1u8; 32]).unwrap(), + }, + unsigned.clone(), + ) + .unwrap(); + let parsed_transaction = BridgeHubPolkadot::parse_transaction(signed_transaction).unwrap(); + assert_eq!(parsed_transaction, unsigned); + } +} diff --git a/relays/client-bridge-hub-polkadot/src/runtime_wrapper.rs b/relays/client-bridge-hub-polkadot/src/runtime_wrapper.rs new file mode 100644 index 00000000000..5afac61dbb3 --- /dev/null +++ b/relays/client-bridge-hub-polkadot/src/runtime_wrapper.rs @@ -0,0 +1,119 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types that are specific to the BridgeHubPolkadot runtime. +// TODO: regenerate me using `runtime-codegen` tool? (https://github.com/paritytech/parity-bridges-common/issues/1945) + +use codec::{Decode, Encode}; +use scale_info::TypeInfo; + +pub use bp_bridge_hub_polkadot::SignedExtension; +pub use bp_header_chain::BridgeGrandpaCallOf; +pub use bp_parachains::BridgeParachainCall; +pub use bridge_runtime_common::messages::BridgeMessagesCallOf; +pub use relay_substrate_client::calls::{SystemCall, UtilityCall}; + +/// Unchecked BridgeHubPolkadot extrinsic. +pub type UncheckedExtrinsic = bp_bridge_hub_polkadot::UncheckedExtrinsic; + +// The indirect pallet call used to sync `Kusama` GRANDPA finality to `BHPolkadot`. +pub type BridgeKusamaGrandpaCall = BridgeGrandpaCallOf; +// The indirect pallet call used to sync `BridgeHubKusama` messages to `BridgeHubPolkadot`. +pub type BridgeKusamaMessagesCall = BridgeMessagesCallOf; + +/// `BridgeHubPolkadot` Runtime `Call` enum. +/// +/// The enum represents a subset of possible `Call`s we can send to `BridgeHubPolkadot` chain. +/// Ideally this code would be auto-generated from metadata, because we want to +/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. +/// +/// All entries here (like pretty much in the entire file) must be kept in sync with +/// `BridgeHubPolkadot` `construct_runtime`, so that we maintain SCALE-compatibility. +#[allow(clippy::large_enum_variant)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum Call { + #[cfg(test)] + #[codec(index = 0)] + System(SystemCall), + /// Utility pallet. + #[codec(index = 40)] + Utility(UtilityCall), + + /// Kusama bridge pallet. + // TODO: the index is wrong (https://github.com/paritytech/parity-bridges-common/issues/1945) + #[codec(index = 43)] + BridgeKusamaGrandpa(BridgeKusamaGrandpaCall), + /// Kusama parachain bridge pallet. + // TODO: the index is wrong (https://github.com/paritytech/parity-bridges-common/issues/1945) + #[codec(index = 44)] + BridgeKusamaParachain(BridgeParachainCall), + /// Kusama messages bridge pallet. + // TODO: the index is wrong (https://github.com/paritytech/parity-bridges-common/issues/1945) + #[codec(index = 45)] + BridgeKusamaMessages(BridgeKusamaMessagesCall), +} + +impl From> for Call { + fn from(call: UtilityCall) -> Call { + Call::Utility(call) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_runtime::BasicOperatingMode; + use sp_consensus_grandpa::AuthorityList; + use sp_core::hexdisplay::HexDisplay; + use sp_runtime::traits::Header; + use std::str::FromStr; + + pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; + pub type RelayBlockHasher = bp_polkadot_core::Hasher; + pub type RelayBlockHeader = sp_runtime::generic::Header; + + #[test] + fn encode_decode_calls() { + let header = RelayBlockHeader::new( + 75, + bp_polkadot_core::Hash::from_str( + "0xd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d", + ) + .expect("invalid value"), + bp_polkadot_core::Hash::from_str( + "0x92b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141b", + ) + .expect("invalid value"), + bp_polkadot_core::Hash::from_str( + "0xae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d", + ) + .expect("invalid value"), + Default::default(), + ); + let init_data = bp_header_chain::InitializationData { + header: Box::new(header), + authority_list: AuthorityList::default(), + set_id: 6, + operating_mode: BasicOperatingMode::Normal, + }; + let call = BridgeKusamaGrandpaCall::initialize { init_data }; + let tx = Call::BridgeKusamaGrandpa(call); + + // encode call as hex string + let hex_encoded_call = format!("0x{:?}", HexDisplay::from(&Encode::encode(&tx))); + assert_eq!(hex_encoded_call, "0x2b01ae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d2d0192b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141bd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d0000060000000000000000"); + } +} diff --git a/relays/client-bridge-hub-rococo/Cargo.toml b/relays/client-bridge-hub-rococo/Cargo.toml new file mode 100644 index 00000000000..48df2e56cf0 --- /dev/null +++ b/relays/client-bridge-hub-rococo/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "relay-bridge-hub-rococo-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +relay-substrate-client = { path = "../client-substrate" } + +# Bridge dependencies + +bp-bridge-hub-rococo = { path = "../../primitives/chain-bridge-hub-rococo" } +bp-bridge-hub-wococo = { path = "../../primitives/chain-bridge-hub-wococo" } +bp-header-chain = { path = "../../primitives/header-chain" } +bp-messages = { path = "../../primitives/messages" } +bp-parachains = { path = "../../primitives/parachains" } +bp-runtime = { path = "../../primitives/runtime" } +bp-wococo = { path = "../../primitives/chain-wococo" } + +bridge-runtime-common = { path = "../../bin/runtime-common" } + +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-bridge-hub-rococo/src/lib.rs b/relays/client-bridge-hub-rococo/src/lib.rs new file mode 100644 index 00000000000..bc2ec3aca41 --- /dev/null +++ b/relays/client-bridge-hub-rococo/src/lib.rs @@ -0,0 +1,162 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the BridgeHub-Rococo-Substrate parachain. + +use bp_bridge_hub_rococo::{BridgeHubSignedExtension, AVERAGE_BLOCK_INTERVAL}; +use bp_messages::MessageNonce; +use bp_runtime::ChainId; +use codec::Encode; +use relay_substrate_client::{ + Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, ChainWithUtilityPallet, + Error as SubstrateError, MockedRuntimeUtilityPallet, SignParam, UnderlyingChainProvider, + UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; +use std::time::Duration; + +/// Re-export runtime wrapper +pub mod runtime_wrapper; +pub use runtime_wrapper as runtime; + +/// Rococo chain definition +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct BridgeHubRococo; + +impl UnderlyingChainProvider for BridgeHubRococo { + type Chain = bp_bridge_hub_rococo::BridgeHubRococo; +} + +impl Chain for BridgeHubRococo { + const ID: ChainId = bp_runtime::BRIDGE_HUB_ROCOCO_CHAIN_ID; + const NAME: &'static str = "BridgeHubRococo"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_bridge_hub_rococo::BEST_FINALIZED_BRIDGE_HUB_ROCOCO_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = AVERAGE_BLOCK_INTERVAL; + + type SignedBlock = bp_bridge_hub_rococo::SignedBlock; + type Call = runtime::Call; +} + +impl ChainWithBalances for BridgeHubRococo { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + bp_bridge_hub_rococo::AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +impl ChainWithUtilityPallet for BridgeHubRococo { + type UtilityPallet = MockedRuntimeUtilityPallet; +} + +impl ChainWithTransactions for BridgeHubRococo { + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = runtime::UncheckedExtrinsic; + + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result { + let raw_payload = SignedPayload::new( + unsigned.call, + runtime::SignedExtension::from_params( + param.spec_version, + param.transaction_version, + unsigned.era, + param.genesis_hash, + unsigned.nonce, + unsigned.tip, + ), + )?; + + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(runtime::UncheckedExtrinsic::new_signed( + call, + signer.into_account().into(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| { + *address == bp_bridge_hub_rococo::Address::Id(signer.public().into()) + }) + .unwrap_or(false) + } + + fn parse_transaction(tx: Self::SignedTransaction) -> Option> { + let extra = &tx.signature.as_ref()?.2; + Some(UnsignedTransaction::new(tx.function, extra.nonce()).tip(extra.tip())) + } +} + +impl ChainWithMessages for BridgeHubRococo { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME; + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = + Some(bp_bridge_hub_rococo::WITH_BRIDGE_HUB_ROCOCO_RELAYERS_PALLET_NAME); + + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_rococo::TO_BRIDGE_HUB_ROCOCO_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_rococo::FROM_BRIDGE_HUB_ROCOCO_MESSAGE_DETAILS_METHOD; + + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; +} + +#[cfg(test)] +mod tests { + use super::*; + use relay_substrate_client::TransactionEra; + + #[test] + fn parse_transaction_works() { + let unsigned = UnsignedTransaction { + call: runtime::Call::System(relay_substrate_client::calls::SystemCall::remark( + b"Hello world!".to_vec(), + )) + .into(), + nonce: 777, + tip: 888, + era: TransactionEra::immortal(), + }; + let signed_transaction = BridgeHubRococo::sign_transaction( + SignParam { + spec_version: 42, + transaction_version: 50000, + genesis_hash: [42u8; 32].into(), + signer: sp_core::sr25519::Pair::from_seed_slice(&[1u8; 32]).unwrap(), + }, + unsigned.clone(), + ) + .unwrap(); + let parsed_transaction = BridgeHubRococo::parse_transaction(signed_transaction).unwrap(); + assert_eq!(parsed_transaction, unsigned); + } +} diff --git a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs new file mode 100644 index 00000000000..26e0bd9da5e --- /dev/null +++ b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs @@ -0,0 +1,69 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types that are specific to the BridgeHubRococo runtime. + +use codec::{Decode, Encode}; +use scale_info::TypeInfo; + +pub use bp_bridge_hub_rococo::SignedExtension; +pub use bp_header_chain::BridgeGrandpaCallOf; +pub use bp_parachains::BridgeParachainCall; +pub use bridge_runtime_common::messages::BridgeMessagesCallOf; +pub use relay_substrate_client::calls::{SystemCall, UtilityCall}; + +/// Unchecked BridgeHubRococo extrinsic. +pub type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic; + +// The indirect pallet call used to sync `Wococo` GRANDPA finality to `BHRococo`. +pub type BridgeWococoGrandpaCall = BridgeGrandpaCallOf; +// The indirect pallet call used to sync `BridgeHubWococo` messages to `BHRococo`. +pub type BridgeWococoMessagesCall = BridgeMessagesCallOf; + +/// `BridgeHubRococo` Runtime `Call` enum. +/// +/// The enum represents a subset of possible `Call`s we can send to `BridgeHubRococo` chain. +/// Ideally this code would be auto-generated from metadata, because we want to +/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. +/// +/// All entries here (like pretty much in the entire file) must be kept in sync with +/// `BridgeHubRococo` `construct_runtime`, so that we maintain SCALE-compatibility. +#[allow(clippy::large_enum_variant)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum Call { + #[cfg(test)] + #[codec(index = 0)] + System(SystemCall), + /// Utility pallet. + #[codec(index = 40)] + Utility(UtilityCall), + + /// Wococo bridge pallet. + #[codec(index = 41)] + BridgeWococoGrandpa(BridgeWococoGrandpaCall), + /// Wococo parachain bridge pallet. + #[codec(index = 42)] + BridgeWococoParachain(BridgeParachainCall), + /// Wococo messages bridge pallet. + #[codec(index = 46)] + BridgeWococoMessages(BridgeWococoMessagesCall), +} + +impl From> for Call { + fn from(call: UtilityCall) -> Call { + Call::Utility(call) + } +} diff --git a/relays/client-bridge-hub-wococo/Cargo.toml b/relays/client-bridge-hub-wococo/Cargo.toml new file mode 100644 index 00000000000..d4578fcd488 --- /dev/null +++ b/relays/client-bridge-hub-wococo/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "relay-bridge-hub-wococo-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +relay-substrate-client = { path = "../client-substrate" } + +# Bridge dependencies + +bp-bridge-hub-rococo = { path = "../../primitives/chain-bridge-hub-rococo" } +bp-bridge-hub-wococo = { path = "../../primitives/chain-bridge-hub-wococo" } +bp-header-chain = { path = "../../primitives/header-chain" } +bp-messages = { path = "../../primitives/messages" } +bp-parachains = { path = "../../primitives/parachains" } +bp-rococo = { path = "../../primitives/chain-rococo" } +bp-runtime = { path = "../../primitives/runtime" } + +bridge-runtime-common = { path = "../../bin/runtime-common" } + +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[dev-dependencies] +bp-polkadot-core = { path = "../../primitives/polkadot-core" } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-bridge-hub-wococo/src/lib.rs b/relays/client-bridge-hub-wococo/src/lib.rs new file mode 100644 index 00000000000..c9acc47dd95 --- /dev/null +++ b/relays/client-bridge-hub-wococo/src/lib.rs @@ -0,0 +1,160 @@ +// Copyright 2022 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the BridgeHub-Wococo-Substrate parachain. + +use bp_bridge_hub_wococo::{BridgeHubSignedExtension, AVERAGE_BLOCK_INTERVAL}; +use bp_messages::MessageNonce; +use bp_runtime::ChainId; +use codec::Encode; +use relay_substrate_client::{ + Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, ChainWithUtilityPallet, + Error as SubstrateError, MockedRuntimeUtilityPallet, SignParam, UnderlyingChainProvider, + UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; +use std::time::Duration; + +/// Re-export runtime wrapper +pub mod runtime_wrapper; +pub use runtime_wrapper as runtime; + +/// Wococo chain definition +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct BridgeHubWococo; + +impl UnderlyingChainProvider for BridgeHubWococo { + type Chain = bp_bridge_hub_wococo::BridgeHubWococo; +} + +impl Chain for BridgeHubWococo { + const ID: ChainId = bp_runtime::BRIDGE_HUB_WOCOCO_CHAIN_ID; + const NAME: &'static str = "BridgeHubWococo"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_bridge_hub_wococo::BEST_FINALIZED_BRIDGE_HUB_WOCOCO_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = AVERAGE_BLOCK_INTERVAL; + + type SignedBlock = bp_bridge_hub_wococo::SignedBlock; + type Call = runtime::Call; +} + +impl ChainWithBalances for BridgeHubWococo { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + bp_bridge_hub_wococo::AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +impl ChainWithUtilityPallet for BridgeHubWococo { + type UtilityPallet = MockedRuntimeUtilityPallet; +} + +impl ChainWithTransactions for BridgeHubWococo { + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = runtime::UncheckedExtrinsic; + + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result { + let raw_payload = SignedPayload::new( + unsigned.call, + runtime::SignedExtension::from_params( + param.spec_version, + param.transaction_version, + unsigned.era, + param.genesis_hash, + unsigned.nonce, + unsigned.tip, + ), + )?; + + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(runtime::UncheckedExtrinsic::new_signed( + call, + signer.into_account().into(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| { + *address == bp_bridge_hub_wococo::Address::Id(signer.public().into()) + }) + .unwrap_or(false) + } + + fn parse_transaction(tx: Self::SignedTransaction) -> Option> { + let extra = &tx.signature.as_ref()?.2; + Some(UnsignedTransaction::new(tx.function, extra.nonce()).tip(extra.tip())) + } +} + +impl ChainWithMessages for BridgeHubWococo { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_bridge_hub_wococo::WITH_BRIDGE_HUB_WOCOCO_MESSAGES_PALLET_NAME; + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = + Some(bp_bridge_hub_wococo::WITH_BRIDGE_HUB_WOCOCO_RELAYERS_PALLET_NAME); + + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_wococo::TO_BRIDGE_HUB_WOCOCO_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_bridge_hub_wococo::FROM_BRIDGE_HUB_WOCOCO_MESSAGE_DETAILS_METHOD; + + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_wococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_bridge_hub_wococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; +} + +#[cfg(test)] +mod tests { + use super::*; + use relay_substrate_client::TransactionEra; + + #[test] + fn parse_transaction_works() { + let unsigned = UnsignedTransaction { + call: runtime::Call::System(runtime::SystemCall::remark(b"Hello world!".to_vec())) + .into(), + nonce: 777, + tip: 888, + era: TransactionEra::immortal(), + }; + let signed_transaction = BridgeHubWococo::sign_transaction( + SignParam { + spec_version: 42, + transaction_version: 50000, + genesis_hash: [42u8; 32].into(), + signer: sp_core::sr25519::Pair::from_seed_slice(&[1u8; 32]).unwrap(), + }, + unsigned.clone(), + ) + .unwrap(); + let parsed_transaction = BridgeHubWococo::parse_transaction(signed_transaction).unwrap(); + assert_eq!(parsed_transaction, unsigned); + } +} diff --git a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs new file mode 100644 index 00000000000..b9803955922 --- /dev/null +++ b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs @@ -0,0 +1,115 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types that are specific to the BridgeHubWococo runtime. + +use codec::{Decode, Encode}; +use scale_info::TypeInfo; + +pub use bp_bridge_hub_wococo::SignedExtension; +pub use bp_header_chain::BridgeGrandpaCallOf; +pub use bp_parachains::BridgeParachainCall; +pub use bridge_runtime_common::messages::BridgeMessagesCallOf; +pub use relay_substrate_client::calls::{SystemCall, UtilityCall}; + +/// Unchecked BridgeHubWococo extrinsic. +pub type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic; + +// The indirect pallet call used to sync `Rococo` GRANDPA finality to `BHWococo`. +pub type BridgeRococoGrandpaCall = BridgeGrandpaCallOf; +// The indirect pallet call used to sync `BridgeHubRococo` messages to `BridgeHubWococo`. +pub type BridgeRococoMessagesCall = BridgeMessagesCallOf; + +/// `BridgeHubWococo` Runtime `Call` enum. +/// +/// The enum represents a subset of possible `Call`s we can send to `BridgeHubWococo` chain. +/// Ideally this code would be auto-generated from metadata, because we want to +/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. +/// +/// All entries here (like pretty much in the entire file) must be kept in sync with +/// `BridgeHubWococo` `construct_runtime`, so that we maintain SCALE-compatibility. +#[allow(clippy::large_enum_variant)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum Call { + #[cfg(test)] + #[codec(index = 0)] + System(SystemCall), + /// Utility pallet. + #[codec(index = 40)] + Utility(UtilityCall), + + /// Rococo bridge pallet. + #[codec(index = 43)] + BridgeRococoGrandpa(BridgeRococoGrandpaCall), + /// Rococo parachain bridge pallet. + #[codec(index = 44)] + BridgeRococoParachain(BridgeParachainCall), + /// Rococo messages bridge pallet. + #[codec(index = 45)] + BridgeRococoMessages(BridgeRococoMessagesCall), +} + +impl From> for Call { + fn from(call: UtilityCall) -> Call { + Call::Utility(call) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_runtime::BasicOperatingMode; + use sp_consensus_grandpa::AuthorityList; + use sp_core::hexdisplay::HexDisplay; + use sp_runtime::traits::Header; + use std::str::FromStr; + + pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; + pub type RelayBlockHasher = bp_polkadot_core::Hasher; + pub type RelayBlockHeader = sp_runtime::generic::Header; + + #[test] + fn encode_decode_calls() { + let header = RelayBlockHeader::new( + 75, + bp_polkadot_core::Hash::from_str( + "0xd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d", + ) + .expect("invalid value"), + bp_polkadot_core::Hash::from_str( + "0x92b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141b", + ) + .expect("invalid value"), + bp_polkadot_core::Hash::from_str( + "0xae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d", + ) + .expect("invalid value"), + Default::default(), + ); + let init_data = bp_header_chain::InitializationData { + header: Box::new(header), + authority_list: AuthorityList::default(), + set_id: 6, + operating_mode: BasicOperatingMode::Normal, + }; + let call = BridgeRococoGrandpaCall::initialize { init_data }; + let tx = Call::BridgeRococoGrandpa(call); + + // encode call as hex string + let hex_encoded_call = format!("0x{:?}", HexDisplay::from(&Encode::encode(&tx))); + assert_eq!(hex_encoded_call, "0x2b01ae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d2d0192b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141bd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d0000060000000000000000"); + } +} diff --git a/relays/client-kusama/Cargo.toml b/relays/client-kusama/Cargo.toml new file mode 100644 index 00000000000..302f530eb4e --- /dev/null +++ b/relays/client-kusama/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "relay-kusama-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-kusama = { path = "../../primitives/chain-kusama" } +bp-runtime = { path = "../../primitives/runtime" } + +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-kusama/src/lib.rs b/relays/client-kusama/src/lib.rs new file mode 100644 index 00000000000..99a52b4baf4 --- /dev/null +++ b/relays/client-kusama/src/lib.rs @@ -0,0 +1,60 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Kusama chain. + +use bp_kusama::AccountInfoStorageMapKeyProvider; +use bp_runtime::ChainId; +use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider}; +use sp_core::storage::StorageKey; +use std::time::Duration; + +/// Kusama header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Kusama header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + +/// Kusama chain definition +#[derive(Debug, Clone, Copy)] +pub struct Kusama; + +impl UnderlyingChainProvider for Kusama { + type Chain = bp_kusama::Kusama; +} + +impl Chain for Kusama { + const ID: ChainId = bp_runtime::KUSAMA_CHAIN_ID; + const NAME: &'static str = "Kusama"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type SignedBlock = bp_kusama::SignedBlock; + type Call = (); +} + +impl ChainWithBalances for Kusama { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +impl RelayChain for Kusama { + const PARAS_PALLET_NAME: &'static str = bp_kusama::PARAS_PALLET_NAME; + // TODO: check me (https://github.com/paritytech/parity-bridges-common/issues/1945) + const PARACHAINS_FINALITY_PALLET_NAME: &'static str = "BridgeKusamaParachain"; +} diff --git a/relays/client-millau/Cargo.toml b/relays/client-millau/Cargo.toml new file mode 100644 index 00000000000..fe5482e5b67 --- /dev/null +++ b/relays/client-millau/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "relay-millau-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5" } +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Supported Chains + +bp-messages = { path = "../../primitives/messages" } +bp-millau = { path = "../../primitives/chain-millau" } +bp-runtime = { path = "../../primitives/runtime" } +millau-runtime = { path = "../../bin/millau/runtime" } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-millau/src/lib.rs b/relays/client-millau/src/lib.rs new file mode 100644 index 00000000000..ce42d004bb8 --- /dev/null +++ b/relays/client-millau/src/lib.rs @@ -0,0 +1,188 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Millau-Substrate chain. + +use bp_messages::MessageNonce; +use bp_runtime::ChainId; +use codec::{Compact, Decode, Encode}; +use relay_substrate_client::{ + BalanceOf, Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, + ChainWithUtilityPallet, Error as SubstrateError, FullRuntimeUtilityPallet, IndexOf, SignParam, + UnderlyingChainProvider, UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; +use std::time::Duration; + +/// Millau header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Millau chain definition. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Millau; + +impl UnderlyingChainProvider for Millau { + type Chain = bp_millau::Millau; +} + +impl ChainWithMessages for Millau { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME; + // TODO (https://github.com/paritytech/parity-bridges-common/issues/1692): change the name + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = Some("BridgeRelayers"); + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_millau::TO_MILLAU_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_millau::FROM_MILLAU_MESSAGE_DETAILS_METHOD; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; +} + +impl Chain for Millau { + const ID: ChainId = bp_runtime::MILLAU_CHAIN_ID; + const NAME: &'static str = "Millau"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); + + type SignedBlock = millau_runtime::SignedBlock; + type Call = millau_runtime::RuntimeCall; +} + +impl ChainWithBalances for Millau { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + use frame_support::storage::generator::StorageMap; + StorageKey(frame_system::Account::::storage_map_final_key( + account_id, + )) + } +} + +impl ChainWithTransactions for Millau { + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = millau_runtime::UncheckedExtrinsic; + + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result { + let raw_payload = SignedPayload::from_raw( + unsigned.call.clone(), + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(unsigned.era.frame_era()), + frame_system::CheckNonce::::from(unsigned.nonce), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(unsigned.tip), + millau_runtime::BridgeRejectObsoleteHeadersAndMessages, + millau_runtime::BridgeRefundRialtoParachainMessages::default(), + ), + ( + (), + param.spec_version, + param.transaction_version, + param.genesis_hash, + unsigned.era.signed_payload(param.genesis_hash), + (), + (), + (), + (), + () + ), + ); + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(millau_runtime::UncheckedExtrinsic::new_signed( + call.into_decoded()?, + signer.into_account(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| { + *address == millau_runtime::Address::from(*signer.public().as_array_ref()) + }) + .unwrap_or(false) + } + + fn parse_transaction(tx: Self::SignedTransaction) -> Option> { + let extra = &tx.signature.as_ref()?.2; + Some( + UnsignedTransaction::new( + tx.function.into(), + Compact::>::decode(&mut &extra.5.encode()[..]).ok()?.into(), + ) + .tip(Compact::>::decode(&mut &extra.7.encode()[..]).ok()?.into()), + ) + } +} + +impl ChainWithUtilityPallet for Millau { + type UtilityPallet = FullRuntimeUtilityPallet; +} + +/// Millau signing params. +pub type SigningParams = sp_core::sr25519::Pair; + +/// Millau header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + +#[cfg(test)] +mod tests { + use super::*; + use relay_substrate_client::TransactionEra; + + #[test] + fn parse_transaction_works() { + let unsigned = UnsignedTransaction { + call: millau_runtime::RuntimeCall::System(millau_runtime::SystemCall::remark { + remark: b"Hello world!".to_vec(), + }) + .into(), + nonce: 777, + tip: 888, + era: TransactionEra::immortal(), + }; + let signed_transaction = Millau::sign_transaction( + SignParam { + spec_version: 42, + transaction_version: 50000, + genesis_hash: [42u8; 64].into(), + signer: sp_core::sr25519::Pair::from_seed_slice(&[1u8; 32]).unwrap(), + }, + unsigned.clone(), + ) + .unwrap(); + let parsed_transaction = Millau::parse_transaction(signed_transaction).unwrap(); + assert_eq!(parsed_transaction, unsigned); + } +} diff --git a/relays/client-polkadot/Cargo.toml b/relays/client-polkadot/Cargo.toml new file mode 100644 index 00000000000..0d3a30949cb --- /dev/null +++ b/relays/client-polkadot/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "relay-polkadot-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-polkadot = { path = "../../primitives/chain-polkadot" } +bp-runtime = { path = "../../primitives/runtime" } + +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-polkadot/src/lib.rs b/relays/client-polkadot/src/lib.rs new file mode 100644 index 00000000000..7f8615e0cd0 --- /dev/null +++ b/relays/client-polkadot/src/lib.rs @@ -0,0 +1,60 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Polkadot chain. + +use bp_polkadot::AccountInfoStorageMapKeyProvider; +use bp_runtime::ChainId; +use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider}; +use sp_core::storage::StorageKey; +use std::time::Duration; + +/// Polkadot header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Polkadot header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + +/// Polkadot chain definition +#[derive(Debug, Clone, Copy)] +pub struct Polkadot; + +impl UnderlyingChainProvider for Polkadot { + type Chain = bp_polkadot::Polkadot; +} + +impl Chain for Polkadot { + const ID: ChainId = bp_runtime::POLKADOT_CHAIN_ID; + const NAME: &'static str = "Polkadot"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type SignedBlock = bp_polkadot::SignedBlock; + type Call = (); +} + +impl ChainWithBalances for Polkadot { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +impl RelayChain for Polkadot { + const PARAS_PALLET_NAME: &'static str = bp_polkadot::PARAS_PALLET_NAME; + // TODO: check me (https://github.com/paritytech/parity-bridges-common/issues/1945) + const PARACHAINS_FINALITY_PALLET_NAME: &'static str = "BridgePolkadotParachain"; +} diff --git a/relays/client-rialto-parachain/Cargo.toml b/relays/client-rialto-parachain/Cargo.toml new file mode 100644 index 00000000000..4450dee3711 --- /dev/null +++ b/relays/client-rialto-parachain/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "relay-rialto-parachain-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5" } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +subxt = { version = "0.27.1", default-features = false, features = [] } + +# Bridge dependencies + +bp-bridge-hub-cumulus = { path = "../../primitives/chain-bridge-hub-cumulus" } +bp-header-chain = { path = "../../primitives/header-chain" } +bp-messages = { path = "../../primitives/messages" } +bp-millau = { path = "../../primitives/chain-millau" } +bp-polkadot-core = { path = "../../primitives/polkadot-core" } +bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" } +bp-runtime = { path = "../../primitives/runtime" } + +bridge-runtime-common = { path = "../../bin/runtime-common" } +relay-substrate-client = { path = "../client-substrate" } + +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-weights = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-rialto-parachain/src/codegen_runtime.rs b/relays/client-rialto-parachain/src/codegen_runtime.rs new file mode 100644 index 00000000000..3ea4a0ac2b5 --- /dev/null +++ b/relays/client-rialto-parachain/src/codegen_runtime.rs @@ -0,0 +1,10210 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Autogenerated runtime API +//! THIS FILE WAS AUTOGENERATED USING parity-bridges-common::runtime-codegen +//! EXECUTED COMMAND: target/debug/runtime-codegen --from-node-url http://localhost:20433 + +#[allow(dead_code, unused_imports, non_camel_case_types)] +#[allow(clippy::all)] +pub mod api { + use super::api as root_mod; + pub static PALLETS: [&str; 16usize] = [ + "System", + "Timestamp", + "Sudo", + "TransactionPayment", + "ParachainSystem", + "ParachainInfo", + "Balances", + "Aura", + "AuraExt", + "XcmpQueue", + "PolkadotXcm", + "CumulusXcm", + "DmpQueue", + "BridgeRelayers", + "BridgeMillauGrandpa", + "BridgeMillauMessages", + ]; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum Event { + #[codec(index = 0)] + System(system::Event), + #[codec(index = 2)] + Sudo(sudo::Event), + #[codec(index = 3)] + TransactionPayment(transaction_payment::Event), + #[codec(index = 20)] + ParachainSystem(parachain_system::Event), + #[codec(index = 30)] + Balances(balances::Event), + #[codec(index = 50)] + XcmpQueue(xcmp_queue::Event), + #[codec(index = 51)] + PolkadotXcm(polkadot_xcm::Event), + #[codec(index = 52)] + CumulusXcm(cumulus_xcm::Event), + #[codec(index = 53)] + DmpQueue(dmp_queue::Event), + #[codec(index = 54)] + BridgeRelayers(bridge_relayers::Event), + #[codec(index = 55)] + BridgeMillauGrandpa(bridge_millau_grandpa::Event), + #[codec(index = 56)] + BridgeMillauMessages(bridge_millau_messages::Event), + } + pub mod system { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Remark { + pub remark: ::std::vec::Vec<::core::primitive::u8>, + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct SetHeapPages { + pub pages: ::core::primitive::u64, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetCode { + pub code: ::std::vec::Vec<::core::primitive::u8>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetCodeWithoutChecks { + pub code: ::std::vec::Vec<::core::primitive::u8>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetStorage { + pub items: ::std::vec::Vec<( + ::std::vec::Vec<::core::primitive::u8>, + ::std::vec::Vec<::core::primitive::u8>, + )>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct KillStorage { + pub keys: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct KillPrefix { + pub prefix: ::std::vec::Vec<::core::primitive::u8>, + pub subkeys: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct RemarkWithEvent { + pub remark: ::std::vec::Vec<::core::primitive::u8>, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Make some on-chain remark."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- `O(1)`"] + pub fn remark( + &self, + remark: ::std::vec::Vec<::core::primitive::u8>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "remark", + Remark { remark }, + [ + 101u8, 80u8, 195u8, 226u8, 224u8, 247u8, 60u8, 128u8, 3u8, 101u8, 51u8, + 147u8, 96u8, 126u8, 76u8, 230u8, 194u8, 227u8, 191u8, 73u8, 160u8, + 146u8, 87u8, 147u8, 243u8, 28u8, 228u8, 116u8, 224u8, 181u8, 129u8, + 160u8, + ], + ) + } + #[doc = "Set the number of pages in the WebAssembly environment's heap."] + pub fn set_heap_pages( + &self, + pages: ::core::primitive::u64, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "set_heap_pages", + SetHeapPages { pages }, + [ + 43u8, 103u8, 128u8, 49u8, 156u8, 136u8, 11u8, 204u8, 80u8, 6u8, 244u8, + 86u8, 171u8, 44u8, 140u8, 225u8, 142u8, 198u8, 43u8, 87u8, 26u8, 45u8, + 125u8, 222u8, 165u8, 254u8, 172u8, 158u8, 39u8, 178u8, 86u8, 87u8, + ], + ) + } + #[doc = "Set the new runtime code."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code`"] + pub fn set_code( + &self, + code: ::std::vec::Vec<::core::primitive::u8>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "set_code", + SetCode { code }, + [ + 27u8, 104u8, 244u8, 205u8, 188u8, 254u8, 121u8, 13u8, 106u8, 120u8, + 244u8, 108u8, 97u8, 84u8, 100u8, 68u8, 26u8, 69u8, 93u8, 128u8, 107u8, + 4u8, 3u8, 142u8, 13u8, 134u8, 196u8, 62u8, 113u8, 181u8, 14u8, 40u8, + ], + ) + } + #[doc = "Set the new runtime code without doing any checks of the given `code`."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- `O(C)` where `C` length of `code`"] + pub fn set_code_without_checks( + &self, + code: ::std::vec::Vec<::core::primitive::u8>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "set_code_without_checks", + SetCodeWithoutChecks { code }, + [ + 102u8, 160u8, 125u8, 235u8, 30u8, 23u8, 45u8, 239u8, 112u8, 148u8, + 159u8, 158u8, 42u8, 93u8, 206u8, 94u8, 80u8, 250u8, 66u8, 195u8, 60u8, + 40u8, 142u8, 169u8, 183u8, 80u8, 80u8, 96u8, 3u8, 231u8, 99u8, 216u8, + ], + ) + } + #[doc = "Set some items of storage."] + pub fn set_storage( + &self, + items: ::std::vec::Vec<( + ::std::vec::Vec<::core::primitive::u8>, + ::std::vec::Vec<::core::primitive::u8>, + )>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "set_storage", + SetStorage { items }, + [ + 74u8, 43u8, 106u8, 255u8, 50u8, 151u8, 192u8, 155u8, 14u8, 90u8, 19u8, + 45u8, 165u8, 16u8, 235u8, 242u8, 21u8, 131u8, 33u8, 172u8, 119u8, 78u8, + 140u8, 10u8, 107u8, 202u8, 122u8, 235u8, 181u8, 191u8, 22u8, 116u8, + ], + ) + } + #[doc = "Kill some items from storage."] + pub fn kill_storage( + &self, + keys: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "kill_storage", + KillStorage { keys }, + [ + 174u8, 174u8, 13u8, 174u8, 75u8, 138u8, 128u8, 235u8, 222u8, 216u8, + 85u8, 18u8, 198u8, 1u8, 138u8, 70u8, 19u8, 108u8, 209u8, 41u8, 228u8, + 67u8, 130u8, 230u8, 160u8, 207u8, 11u8, 180u8, 139u8, 242u8, 41u8, + 15u8, + ], + ) + } + #[doc = "Kill all storage items with a key that starts with the given prefix."] + #[doc = ""] + #[doc = "**NOTE:** We rely on the Root origin to provide us the number of subkeys under"] + #[doc = "the prefix we are removing to accurately calculate the weight of this function."] + pub fn kill_prefix( + &self, + prefix: ::std::vec::Vec<::core::primitive::u8>, + subkeys: ::core::primitive::u32, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "kill_prefix", + KillPrefix { prefix, subkeys }, + [ + 203u8, 116u8, 217u8, 42u8, 154u8, 215u8, 77u8, 217u8, 13u8, 22u8, + 193u8, 2u8, 128u8, 115u8, 179u8, 115u8, 187u8, 218u8, 129u8, 34u8, + 80u8, 4u8, 173u8, 120u8, 92u8, 35u8, 237u8, 112u8, 201u8, 207u8, 200u8, + 48u8, + ], + ) + } + #[doc = "Make some on-chain remark and emit event."] + pub fn remark_with_event( + &self, + remark: ::std::vec::Vec<::core::primitive::u8>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "System", + "remark_with_event", + RemarkWithEvent { remark }, + [ + 123u8, 225u8, 180u8, 179u8, 144u8, 74u8, 27u8, 85u8, 101u8, 75u8, + 134u8, 44u8, 181u8, 25u8, 183u8, 158u8, 14u8, 213u8, 56u8, 225u8, + 136u8, 88u8, 26u8, 114u8, 178u8, 43u8, 176u8, 43u8, 240u8, 84u8, 116u8, + 46u8, + ], + ) + } + } + } + #[doc = "Event for the System pallet."] + pub type Event = runtime_types::frame_system::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An extrinsic completed successfully."] + pub struct ExtrinsicSuccess { + pub dispatch_info: runtime_types::frame_support::dispatch::DispatchInfo, + } + impl ::subxt::events::StaticEvent for ExtrinsicSuccess { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "ExtrinsicSuccess"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An extrinsic failed."] + pub struct ExtrinsicFailed { + pub dispatch_error: runtime_types::sp_runtime::DispatchError, + pub dispatch_info: runtime_types::frame_support::dispatch::DispatchInfo, + } + impl ::subxt::events::StaticEvent for ExtrinsicFailed { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "ExtrinsicFailed"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "`:code` was updated."] + pub struct CodeUpdated; + impl ::subxt::events::StaticEvent for CodeUpdated { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "CodeUpdated"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A new account was created."] + pub struct NewAccount { + pub account: ::sp_core::crypto::AccountId32, + } + impl ::subxt::events::StaticEvent for NewAccount { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "NewAccount"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An account was reaped."] + pub struct KilledAccount { + pub account: ::sp_core::crypto::AccountId32, + } + impl ::subxt::events::StaticEvent for KilledAccount { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "KilledAccount"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "On on-chain remark happened."] + pub struct Remarked { + pub sender: ::sp_core::crypto::AccountId32, + pub hash: ::subxt::utils::H256, + } + impl ::subxt::events::StaticEvent for Remarked { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "Remarked"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " The full account information for a particular account ID."] + pub fn account( + &self, + _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::frame_system::AccountInfo< + ::core::primitive::u32, + runtime_types::pallet_balances::types::AccountData< + ::core::primitive::u128, + >, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "Account", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 248u8, 178u8, 160u8, 222u8, 45u8, 231u8, 115u8, 164u8, 98u8, 184u8, + 174u8, 206u8, 149u8, 190u8, 175u8, 34u8, 202u8, 230u8, 69u8, 218u8, + 83u8, 43u8, 170u8, 41u8, 106u8, 77u8, 233u8, 97u8, 114u8, 14u8, 155u8, + 131u8, + ], + ) + } + #[doc = " The full account information for a particular account ID."] + pub fn account_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::frame_system::AccountInfo< + ::core::primitive::u32, + runtime_types::pallet_balances::types::AccountData< + ::core::primitive::u128, + >, + >, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "Account", + Vec::new(), + [ + 248u8, 178u8, 160u8, 222u8, 45u8, 231u8, 115u8, 164u8, 98u8, 184u8, + 174u8, 206u8, 149u8, 190u8, 175u8, 34u8, 202u8, 230u8, 69u8, 218u8, + 83u8, 43u8, 170u8, 41u8, 106u8, 77u8, 233u8, 97u8, 114u8, 14u8, 155u8, + 131u8, + ], + ) + } + #[doc = " Total extrinsics count for the current block."] + pub fn extrinsic_count( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "ExtrinsicCount", + vec![], + [ + 223u8, 60u8, 201u8, 120u8, 36u8, 44u8, 180u8, 210u8, 242u8, 53u8, + 222u8, 154u8, 123u8, 176u8, 249u8, 8u8, 225u8, 28u8, 232u8, 4u8, 136u8, + 41u8, 151u8, 82u8, 189u8, 149u8, 49u8, 166u8, 139u8, 9u8, 163u8, 231u8, + ], + ) + } + #[doc = " The current weight for the block."] + pub fn block_weight( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::frame_support::dispatch::PerDispatchClass< + ::sp_weights::Weight, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "BlockWeight", + vec![], + [ + 120u8, 67u8, 71u8, 163u8, 36u8, 202u8, 52u8, 106u8, 143u8, 155u8, + 144u8, 87u8, 142u8, 241u8, 232u8, 183u8, 56u8, 235u8, 27u8, 237u8, + 20u8, 202u8, 33u8, 85u8, 189u8, 0u8, 28u8, 52u8, 198u8, 40u8, 219u8, + 54u8, + ], + ) + } + #[doc = " Total length (in bytes) for all extrinsics put together, for the current block."] + pub fn all_extrinsics_len( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "AllExtrinsicsLen", + vec![], + [ + 202u8, 145u8, 209u8, 225u8, 40u8, 220u8, 174u8, 74u8, 93u8, 164u8, + 254u8, 248u8, 254u8, 192u8, 32u8, 117u8, 96u8, 149u8, 53u8, 145u8, + 219u8, 64u8, 234u8, 18u8, 217u8, 200u8, 203u8, 141u8, 145u8, 28u8, + 134u8, 60u8, + ], + ) + } + #[doc = " Map of block numbers to block hashes."] + pub fn block_hash( + &self, + _0: impl ::std::borrow::Borrow<::core::primitive::u32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::subxt::utils::H256>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "BlockHash", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Twox64Concat, + )], + [ + 50u8, 112u8, 176u8, 239u8, 175u8, 18u8, 205u8, 20u8, 241u8, 195u8, + 21u8, 228u8, 186u8, 57u8, 200u8, 25u8, 38u8, 44u8, 106u8, 20u8, 168u8, + 80u8, 76u8, 235u8, 12u8, 51u8, 137u8, 149u8, 200u8, 4u8, 220u8, 237u8, + ], + ) + } + #[doc = " Map of block numbers to block hashes."] + pub fn block_hash_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::subxt::utils::H256>, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "BlockHash", + Vec::new(), + [ + 50u8, 112u8, 176u8, 239u8, 175u8, 18u8, 205u8, 20u8, 241u8, 195u8, + 21u8, 228u8, 186u8, 57u8, 200u8, 25u8, 38u8, 44u8, 106u8, 20u8, 168u8, + 80u8, 76u8, 235u8, 12u8, 51u8, 137u8, 149u8, 200u8, 4u8, 220u8, 237u8, + ], + ) + } + #[doc = " Extrinsics data for the current block (maps an extrinsic's index to its data)."] + pub fn extrinsic_data( + &self, + _0: impl ::std::borrow::Borrow<::core::primitive::u32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "ExtrinsicData", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Twox64Concat, + )], + [ + 210u8, 224u8, 211u8, 186u8, 118u8, 210u8, 185u8, 194u8, 238u8, 211u8, + 254u8, 73u8, 67u8, 184u8, 31u8, 229u8, 168u8, 125u8, 98u8, 23u8, 241u8, + 59u8, 49u8, 86u8, 126u8, 9u8, 114u8, 163u8, 160u8, 62u8, 50u8, 67u8, + ], + ) + } + #[doc = " Extrinsics data for the current block (maps an extrinsic's index to its data)."] + pub fn extrinsic_data_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "ExtrinsicData", + Vec::new(), + [ + 210u8, 224u8, 211u8, 186u8, 118u8, 210u8, 185u8, 194u8, 238u8, 211u8, + 254u8, 73u8, 67u8, 184u8, 31u8, 229u8, 168u8, 125u8, 98u8, 23u8, 241u8, + 59u8, 49u8, 86u8, 126u8, 9u8, 114u8, 163u8, 160u8, 62u8, 50u8, 67u8, + ], + ) + } + #[doc = " The current block number being processed. Set by `execute_block`."] + pub fn number( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "Number", + vec![], + [ + 228u8, 96u8, 102u8, 190u8, 252u8, 130u8, 239u8, 172u8, 126u8, 235u8, + 246u8, 139u8, 208u8, 15u8, 88u8, 245u8, 141u8, 232u8, 43u8, 204u8, + 36u8, 87u8, 211u8, 141u8, 187u8, 68u8, 236u8, 70u8, 193u8, 235u8, + 164u8, 191u8, + ], + ) + } + #[doc = " Hash of the previous block."] + pub fn parent_hash( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::subxt::utils::H256>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "ParentHash", + vec![], + [ + 232u8, 206u8, 177u8, 119u8, 38u8, 57u8, 233u8, 50u8, 225u8, 49u8, + 169u8, 176u8, 210u8, 51u8, 231u8, 176u8, 234u8, 186u8, 188u8, 112u8, + 15u8, 152u8, 195u8, 232u8, 201u8, 97u8, 208u8, 249u8, 9u8, 163u8, 69u8, + 36u8, + ], + ) + } + #[doc = " Digest of the current block, also part of the block header."] + pub fn digest( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::sp_runtime::generic::Digest>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "Digest", + vec![], + [ + 83u8, 141u8, 200u8, 132u8, 182u8, 55u8, 197u8, 122u8, 13u8, 159u8, + 31u8, 42u8, 60u8, 191u8, 89u8, 221u8, 242u8, 47u8, 199u8, 213u8, 48u8, + 216u8, 131u8, 168u8, 245u8, 82u8, 56u8, 190u8, 62u8, 69u8, 96u8, 37u8, + ], + ) + } + #[doc = " Events deposited for the current block."] + #[doc = ""] + #[doc = " NOTE: The item is unbound and should therefore never be read on chain."] + #[doc = " It could otherwise inflate the PoV size of a block."] + #[doc = ""] + #[doc = " Events have a large in-memory size. Box the events to not go out-of-memory"] + #[doc = " just in case someone still reads them from within the runtime."] + pub fn events( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec< + runtime_types::frame_system::EventRecord< + runtime_types::rialto_parachain_runtime::RuntimeEvent, + ::subxt::utils::H256, + >, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "Events", + vec![], + [ + 76u8, 19u8, 44u8, 121u8, 59u8, 168u8, 101u8, 101u8, 248u8, 3u8, 172u8, + 27u8, 249u8, 200u8, 147u8, 17u8, 4u8, 102u8, 186u8, 6u8, 152u8, 62u8, + 76u8, 195u8, 45u8, 188u8, 191u8, 30u8, 134u8, 78u8, 199u8, 93u8, + ], + ) + } + #[doc = " The number of events in the `Events` list."] + pub fn event_count( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "EventCount", + vec![], + [ + 236u8, 93u8, 90u8, 177u8, 250u8, 211u8, 138u8, 187u8, 26u8, 208u8, + 203u8, 113u8, 221u8, 233u8, 227u8, 9u8, 249u8, 25u8, 202u8, 185u8, + 161u8, 144u8, 167u8, 104u8, 127u8, 187u8, 38u8, 18u8, 52u8, 61u8, 66u8, + 112u8, + ], + ) + } + #[doc = " Mapping between a topic (represented by T::Hash) and a vector of indexes"] + #[doc = " of events in the `>` list."] + #[doc = ""] + #[doc = " All topic vectors have deterministic storage locations depending on the topic. This"] + #[doc = " allows light-clients to leverage the changes trie storage tracking mechanism and"] + #[doc = " in case of changes fetch the list of events of interest."] + #[doc = ""] + #[doc = " The value has the type `(T::BlockNumber, EventIndex)` because if we used only just"] + #[doc = " the `EventIndex` then in case if the topic has the same contents on the next block"] + #[doc = " no notification will be triggered thus the event might be lost."] + pub fn event_topics( + &self, + _0: impl ::std::borrow::Borrow<::subxt::utils::H256>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec<(::core::primitive::u32, ::core::primitive::u32)>, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "EventTopics", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 205u8, 90u8, 142u8, 190u8, 176u8, 37u8, 94u8, 82u8, 98u8, 1u8, 129u8, + 63u8, 246u8, 101u8, 130u8, 58u8, 216u8, 16u8, 139u8, 196u8, 154u8, + 111u8, 110u8, 178u8, 24u8, 44u8, 183u8, 176u8, 232u8, 82u8, 223u8, + 38u8, + ], + ) + } + #[doc = " Mapping between a topic (represented by T::Hash) and a vector of indexes"] + #[doc = " of events in the `>` list."] + #[doc = ""] + #[doc = " All topic vectors have deterministic storage locations depending on the topic. This"] + #[doc = " allows light-clients to leverage the changes trie storage tracking mechanism and"] + #[doc = " in case of changes fetch the list of events of interest."] + #[doc = ""] + #[doc = " The value has the type `(T::BlockNumber, EventIndex)` because if we used only just"] + #[doc = " the `EventIndex` then in case if the topic has the same contents on the next block"] + #[doc = " no notification will be triggered thus the event might be lost."] + pub fn event_topics_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec<(::core::primitive::u32, ::core::primitive::u32)>, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "EventTopics", + Vec::new(), + [ + 205u8, 90u8, 142u8, 190u8, 176u8, 37u8, 94u8, 82u8, 98u8, 1u8, 129u8, + 63u8, 246u8, 101u8, 130u8, 58u8, 216u8, 16u8, 139u8, 196u8, 154u8, + 111u8, 110u8, 178u8, 24u8, 44u8, 183u8, 176u8, 232u8, 82u8, 223u8, + 38u8, + ], + ) + } + #[doc = " Stores the `spec_version` and `spec_name` of when the last runtime upgrade happened."] + pub fn last_runtime_upgrade( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::frame_system::LastRuntimeUpgradeInfo, + >, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "LastRuntimeUpgrade", + vec![], + [ + 52u8, 37u8, 117u8, 111u8, 57u8, 130u8, 196u8, 14u8, 99u8, 77u8, 91u8, + 126u8, 178u8, 249u8, 78u8, 34u8, 9u8, 194u8, 92u8, 105u8, 113u8, 81u8, + 185u8, 127u8, 245u8, 184u8, 60u8, 29u8, 234u8, 182u8, 96u8, 196u8, + ], + ) + } + #[doc = " True if we have upgraded so that `type RefCount` is `u32`. False (default) if not."] + pub fn upgraded_to_u32_ref_count( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "UpgradedToU32RefCount", + vec![], + [ + 171u8, 88u8, 244u8, 92u8, 122u8, 67u8, 27u8, 18u8, 59u8, 175u8, 175u8, + 178u8, 20u8, 150u8, 213u8, 59u8, 222u8, 141u8, 32u8, 107u8, 3u8, 114u8, + 83u8, 250u8, 180u8, 233u8, 152u8, 54u8, 187u8, 99u8, 131u8, 204u8, + ], + ) + } + #[doc = " True if we have upgraded so that AccountInfo contains three types of `RefCount`. False"] + #[doc = " (default) if not."] + pub fn upgraded_to_triple_ref_count( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "UpgradedToTripleRefCount", + vec![], + [ + 90u8, 33u8, 56u8, 86u8, 90u8, 101u8, 89u8, 133u8, 203u8, 56u8, 201u8, + 210u8, 244u8, 232u8, 150u8, 18u8, 51u8, 105u8, 14u8, 230u8, 103u8, + 155u8, 246u8, 99u8, 53u8, 207u8, 225u8, 128u8, 186u8, 76u8, 40u8, + 185u8, + ], + ) + } + #[doc = " The execution phase of the block."] + pub fn execution_phase( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "System", + "ExecutionPhase", + vec![], + [ + 230u8, 183u8, 221u8, 135u8, 226u8, 223u8, 55u8, 104u8, 138u8, 224u8, + 103u8, 156u8, 222u8, 99u8, 203u8, 199u8, 164u8, 168u8, 193u8, 133u8, + 201u8, 155u8, 63u8, 95u8, 17u8, 206u8, 165u8, 123u8, 161u8, 33u8, + 172u8, 93u8, + ], + ) + } + } + } + pub mod constants { + use super::runtime_types; + pub struct ConstantsApi; + impl ConstantsApi { + #[doc = " Block & extrinsics weights: base values and limits."] + pub fn block_weights( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::frame_system::limits::BlockWeights, + >, + > { + ::subxt::constants::StaticConstantAddress::new( + "System", + "BlockWeights", + [ + 118u8, 253u8, 239u8, 217u8, 145u8, 115u8, 85u8, 86u8, 172u8, 248u8, + 139u8, 32u8, 158u8, 126u8, 172u8, 188u8, 197u8, 105u8, 145u8, 235u8, + 171u8, 50u8, 31u8, 225u8, 167u8, 187u8, 241u8, 87u8, 6u8, 17u8, 234u8, + 185u8, + ], + ) + } + #[doc = " The maximum length of a block (in bytes)."] + pub fn block_length( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::frame_system::limits::BlockLength, + >, + > { + ::subxt::constants::StaticConstantAddress::new( + "System", + "BlockLength", + [ + 116u8, 184u8, 225u8, 228u8, 207u8, 203u8, 4u8, 220u8, 234u8, 198u8, + 150u8, 108u8, 205u8, 87u8, 194u8, 131u8, 229u8, 51u8, 140u8, 4u8, 47u8, + 12u8, 200u8, 144u8, 153u8, 62u8, 51u8, 39u8, 138u8, 205u8, 203u8, + 236u8, + ], + ) + } + #[doc = " Maximum number of block number to block hash mappings to keep (oldest pruned first)."] + pub fn block_hash_count( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "System", + "BlockHashCount", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + #[doc = " The weight of runtime database operations the runtime can invoke."] + pub fn db_weight( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType, + > { + ::subxt::constants::StaticConstantAddress::new( + "System", + "DbWeight", + [ + 124u8, 162u8, 190u8, 149u8, 49u8, 177u8, 162u8, 231u8, 62u8, 167u8, + 199u8, 181u8, 43u8, 232u8, 185u8, 116u8, 195u8, 51u8, 233u8, 223u8, + 20u8, 129u8, 246u8, 13u8, 65u8, 180u8, 64u8, 9u8, 157u8, 59u8, 245u8, + 118u8, + ], + ) + } + #[doc = " Get the chain's current version."] + pub fn version( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType, + > { + ::subxt::constants::StaticConstantAddress::new( + "System", + "Version", + [ + 93u8, 98u8, 57u8, 243u8, 229u8, 8u8, 234u8, 231u8, 72u8, 230u8, 139u8, + 47u8, 63u8, 181u8, 17u8, 2u8, 220u8, 231u8, 104u8, 237u8, 185u8, 143u8, + 165u8, 253u8, 188u8, 76u8, 147u8, 12u8, 170u8, 26u8, 74u8, 200u8, + ], + ) + } + #[doc = " The designated SS58 prefix of this chain."] + #[doc = ""] + #[doc = " This replaces the \"ss58Format\" property declared in the chain spec. Reason is"] + #[doc = " that the runtime should know about the prefix in order to make use of it as"] + #[doc = " an identifier of the chain."] + pub fn ss58_prefix( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u16>, + > { + ::subxt::constants::StaticConstantAddress::new( + "System", + "SS58Prefix", + [ + 116u8, 33u8, 2u8, 170u8, 181u8, 147u8, 171u8, 169u8, 167u8, 227u8, + 41u8, 144u8, 11u8, 236u8, 82u8, 100u8, 74u8, 60u8, 184u8, 72u8, 169u8, + 90u8, 208u8, 135u8, 15u8, 117u8, 10u8, 123u8, 128u8, 193u8, 29u8, 70u8, + ], + ) + } + } + } + } + pub mod timestamp { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Set { + #[codec(compact)] + pub now: ::core::primitive::u64, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Set the current time."] + #[doc = ""] + #[doc = "This call should be invoked exactly once per block. It will panic at the finalization"] + #[doc = "phase, if this call hasn't been invoked by that time."] + #[doc = ""] + #[doc = "The timestamp should be greater than the previous one by the amount specified by"] + #[doc = "`MinimumPeriod`."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be `Inherent`."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`)"] + #[doc = "- 1 storage read and 1 storage mutation (codec `O(1)`). (because of `DidUpdate::take` in"] + #[doc = " `on_finalize`)"] + #[doc = "- 1 event handler `on_timestamp_set`. Must be `O(1)`."] + pub fn set( + &self, + now: ::core::primitive::u64, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Timestamp", + "set", + Set { now }, + [ + 6u8, 97u8, 172u8, 236u8, 118u8, 238u8, 228u8, 114u8, 15u8, 115u8, + 102u8, 85u8, 66u8, 151u8, 16u8, 33u8, 187u8, 17u8, 166u8, 88u8, 127u8, + 214u8, 182u8, 51u8, 168u8, 88u8, 43u8, 101u8, 185u8, 8u8, 1u8, 28u8, + ], + ) + } + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " Current time for the current block."] + pub fn now( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u64>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Timestamp", + "Now", + vec![], + [ + 148u8, 53u8, 50u8, 54u8, 13u8, 161u8, 57u8, 150u8, 16u8, 83u8, 144u8, + 221u8, 59u8, 75u8, 158u8, 130u8, 39u8, 123u8, 106u8, 134u8, 202u8, + 185u8, 83u8, 85u8, 60u8, 41u8, 120u8, 96u8, 210u8, 34u8, 2u8, 250u8, + ], + ) + } + #[doc = " Did the timestamp get updated in this block?"] + pub fn did_update( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Timestamp", + "DidUpdate", + vec![], + [ + 70u8, 13u8, 92u8, 186u8, 80u8, 151u8, 167u8, 90u8, 158u8, 232u8, 175u8, + 13u8, 103u8, 135u8, 2u8, 78u8, 16u8, 6u8, 39u8, 158u8, 167u8, 85u8, + 27u8, 47u8, 122u8, 73u8, 127u8, 26u8, 35u8, 168u8, 72u8, 204u8, + ], + ) + } + } + } + pub mod constants { + use super::runtime_types; + pub struct ConstantsApi; + impl ConstantsApi { + #[doc = " The minimum period between blocks. Beware that this is different to the *expected*"] + #[doc = " period that the block production apparatus provides. Your chosen consensus system will"] + #[doc = " generally work with this to determine a sensible block time. e.g. For Aura, it will be"] + #[doc = " double this period on default settings."] + pub fn minimum_period( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u64>, + > { + ::subxt::constants::StaticConstantAddress::new( + "Timestamp", + "MinimumPeriod", + [ + 128u8, 214u8, 205u8, 242u8, 181u8, 142u8, 124u8, 231u8, 190u8, 146u8, + 59u8, 226u8, 157u8, 101u8, 103u8, 117u8, 249u8, 65u8, 18u8, 191u8, + 103u8, 119u8, 53u8, 85u8, 81u8, 96u8, 220u8, 42u8, 184u8, 239u8, 42u8, + 246u8, + ], + ) + } + } + } + } + pub mod sudo { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Sudo { + pub call: ::std::boxed::Box, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SudoUncheckedWeight { + pub call: ::std::boxed::Box, + pub weight: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetKey { + pub new: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SudoAs { + pub who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + pub call: ::std::boxed::Box, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Authenticates the sudo key and dispatches a function call with `Root` origin."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- O(1)."] + pub fn sudo( + &self, + call: runtime_types::rialto_parachain_runtime::RuntimeCall, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Sudo", + "sudo", + Sudo { call: ::std::boxed::Box::new(call) }, + [ + 19u8, 178u8, 172u8, 30u8, 28u8, 209u8, 160u8, 61u8, 76u8, 239u8, 71u8, + 124u8, 21u8, 34u8, 233u8, 176u8, 100u8, 90u8, 198u8, 118u8, 117u8, 2u8, + 147u8, 7u8, 109u8, 1u8, 32u8, 35u8, 99u8, 0u8, 107u8, 145u8, + ], + ) + } + #[doc = "Authenticates the sudo key and dispatches a function call with `Root` origin."] + #[doc = "This function does not check the weight of the call, and instead allows the"] + #[doc = "Sudo user to specify the weight of the call."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- O(1)."] + pub fn sudo_unchecked_weight( + &self, + call: runtime_types::rialto_parachain_runtime::RuntimeCall, + weight: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Sudo", + "sudo_unchecked_weight", + SudoUncheckedWeight { call: ::std::boxed::Box::new(call), weight }, + [ + 145u8, 160u8, 12u8, 105u8, 228u8, 240u8, 115u8, 105u8, 220u8, 99u8, + 215u8, 228u8, 115u8, 71u8, 109u8, 28u8, 149u8, 247u8, 159u8, 216u8, + 76u8, 71u8, 68u8, 87u8, 254u8, 146u8, 185u8, 174u8, 251u8, 209u8, 72u8, + 122u8, + ], + ) + } + #[doc = "Authenticates the current sudo key and sets the given AccountId (`new`) as the new sudo"] + #[doc = "key."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- O(1)."] + pub fn set_key( + &self, + new: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Sudo", + "set_key", + SetKey { new }, + [ + 23u8, 224u8, 218u8, 169u8, 8u8, 28u8, 111u8, 199u8, 26u8, 88u8, 225u8, + 105u8, 17u8, 19u8, 87u8, 156u8, 97u8, 67u8, 89u8, 173u8, 70u8, 0u8, + 5u8, 246u8, 198u8, 135u8, 182u8, 180u8, 44u8, 9u8, 212u8, 95u8, + ], + ) + } + #[doc = "Authenticates the sudo key and dispatches a function call with `Signed` origin from"] + #[doc = "a given account."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- O(1)."] + pub fn sudo_as( + &self, + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + call: runtime_types::rialto_parachain_runtime::RuntimeCall, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Sudo", + "sudo_as", + SudoAs { who, call: ::std::boxed::Box::new(call) }, + [ + 229u8, 45u8, 129u8, 96u8, 54u8, 29u8, 159u8, 77u8, 210u8, 144u8, 29u8, + 97u8, 127u8, 133u8, 122u8, 110u8, 152u8, 194u8, 211u8, 246u8, 97u8, + 197u8, 187u8, 203u8, 46u8, 12u8, 104u8, 125u8, 15u8, 226u8, 28u8, + 183u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::pallet_sudo::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A sudo just took place. \\[result\\]"] + pub struct Sudid { + pub sudo_result: + ::core::result::Result<(), runtime_types::sp_runtime::DispatchError>, + } + impl ::subxt::events::StaticEvent for Sudid { + const PALLET: &'static str = "Sudo"; + const EVENT: &'static str = "Sudid"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "The \\[sudoer\\] just switched identity; the old key is supplied if one existed."] + pub struct KeyChanged { + pub old_sudoer: ::core::option::Option<::sp_core::crypto::AccountId32>, + } + impl ::subxt::events::StaticEvent for KeyChanged { + const PALLET: &'static str = "Sudo"; + const EVENT: &'static str = "KeyChanged"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A sudo just took place. \\[result\\]"] + pub struct SudoAsDone { + pub sudo_result: + ::core::result::Result<(), runtime_types::sp_runtime::DispatchError>, + } + impl ::subxt::events::StaticEvent for SudoAsDone { + const PALLET: &'static str = "Sudo"; + const EVENT: &'static str = "SudoAsDone"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " The `AccountId` of the sudo key."] + pub fn key( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::sp_core::crypto::AccountId32>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Sudo", + "Key", + vec![], + [ + 244u8, 73u8, 188u8, 136u8, 218u8, 163u8, 68u8, 179u8, 122u8, 173u8, + 34u8, 108u8, 137u8, 28u8, 182u8, 16u8, 196u8, 92u8, 138u8, 34u8, 102u8, + 80u8, 199u8, 88u8, 107u8, 207u8, 36u8, 22u8, 168u8, 167u8, 20u8, 142u8, + ], + ) + } + } + } + } + pub mod transaction_payment { + use super::{root_mod, runtime_types}; + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::pallet_transaction_payment::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A transaction fee `actual_fee`, of which `tip` was added to the minimum inclusion fee,"] + #[doc = "has been paid by `who`."] + pub struct TransactionFeePaid { + pub who: ::sp_core::crypto::AccountId32, + pub actual_fee: ::core::primitive::u128, + pub tip: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for TransactionFeePaid { + const PALLET: &'static str = "TransactionPayment"; + const EVENT: &'static str = "TransactionFeePaid"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + pub fn next_fee_multiplier( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::sp_arithmetic::fixed_point::FixedU128, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "TransactionPayment", + "NextFeeMultiplier", + vec![], + [ + 210u8, 0u8, 206u8, 165u8, 183u8, 10u8, 206u8, 52u8, 14u8, 90u8, 218u8, + 197u8, 189u8, 125u8, 113u8, 216u8, 52u8, 161u8, 45u8, 24u8, 245u8, + 237u8, 121u8, 41u8, 106u8, 29u8, 45u8, 129u8, 250u8, 203u8, 206u8, + 180u8, + ], + ) + } + pub fn storage_version( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::pallet_transaction_payment::Releases, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "TransactionPayment", + "StorageVersion", + vec![], + [ + 219u8, 243u8, 82u8, 176u8, 65u8, 5u8, 132u8, 114u8, 8u8, 82u8, 176u8, + 200u8, 97u8, 150u8, 177u8, 164u8, 166u8, 11u8, 34u8, 12u8, 12u8, 198u8, + 58u8, 191u8, 186u8, 221u8, 221u8, 119u8, 181u8, 253u8, 154u8, 228u8, + ], + ) + } + } + } + pub mod constants { + use super::runtime_types; + pub struct ConstantsApi; + impl ConstantsApi { + #[doc = " A fee mulitplier for `Operational` extrinsics to compute \"virtual tip\" to boost their"] + #[doc = " `priority`"] + #[doc = ""] + #[doc = " This value is multipled by the `final_fee` to obtain a \"virtual tip\" that is later"] + #[doc = " added to a tip component in regular `priority` calculations."] + #[doc = " It means that a `Normal` transaction can front-run a similarly-sized `Operational`"] + #[doc = " extrinsic (with no tip), by including a tip value greater than the virtual tip."] + #[doc = ""] + #[doc = " ```rust,ignore"] + #[doc = " // For `Normal`"] + #[doc = " let priority = priority_calc(tip);"] + #[doc = ""] + #[doc = " // For `Operational`"] + #[doc = " let virtual_tip = (inclusion_fee + tip) * OperationalFeeMultiplier;"] + #[doc = " let priority = priority_calc(tip + virtual_tip);"] + #[doc = " ```"] + #[doc = ""] + #[doc = " Note that since we use `final_fee` the multiplier applies also to the regular `tip`"] + #[doc = " sent with the transaction. So, not only does the transaction get a priority bump based"] + #[doc = " on the `inclusion_fee`, but we also amplify the impact of tips applied to `Operational`"] + #[doc = " transactions."] + pub fn operational_fee_multiplier( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u8>, + > { + ::subxt::constants::StaticConstantAddress::new( + "TransactionPayment", + "OperationalFeeMultiplier", + [ + 141u8, 130u8, 11u8, 35u8, 226u8, 114u8, 92u8, 179u8, 168u8, 110u8, + 28u8, 91u8, 221u8, 64u8, 4u8, 148u8, 201u8, 193u8, 185u8, 66u8, 226u8, + 114u8, 97u8, 79u8, 62u8, 212u8, 202u8, 114u8, 237u8, 228u8, 183u8, + 165u8, + ], + ) + } + } + } + } + pub mod parachain_system { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetValidationData { + pub data: + runtime_types::cumulus_primitives_parachain_inherent::ParachainInherentData, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SudoSendUpwardMessage { + pub message: ::std::vec::Vec<::core::primitive::u8>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct AuthorizeUpgrade { + pub code_hash: ::subxt::utils::H256, + pub check_version: ::core::primitive::bool, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct EnactAuthorizedUpgrade { + pub code: ::std::vec::Vec<::core::primitive::u8>, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Set the current validation data."] + #[doc = ""] + #[doc = "This should be invoked exactly once per block. It will panic at the finalization"] + #[doc = "phase if the call was not invoked."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be `Inherent`"] + #[doc = ""] + #[doc = "As a side effect, this function upgrades the current validation function"] + #[doc = "if the appropriate time has come."] + pub fn set_validation_data( + &self, + data : runtime_types :: cumulus_primitives_parachain_inherent :: ParachainInherentData, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "ParachainSystem", + "set_validation_data", + SetValidationData { data }, + [ + 200u8, 80u8, 163u8, 177u8, 184u8, 117u8, 61u8, 203u8, 244u8, 214u8, + 106u8, 151u8, 128u8, 131u8, 254u8, 120u8, 254u8, 76u8, 104u8, 39u8, + 215u8, 227u8, 233u8, 254u8, 26u8, 62u8, 17u8, 42u8, 19u8, 127u8, 108u8, + 242u8, + ], + ) + } + pub fn sudo_send_upward_message( + &self, + message: ::std::vec::Vec<::core::primitive::u8>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "ParachainSystem", + "sudo_send_upward_message", + SudoSendUpwardMessage { message }, + [ + 127u8, 79u8, 45u8, 183u8, 190u8, 205u8, 184u8, 169u8, 255u8, 191u8, + 86u8, 154u8, 134u8, 25u8, 249u8, 63u8, 47u8, 194u8, 108u8, 62u8, 60u8, + 170u8, 81u8, 240u8, 113u8, 48u8, 181u8, 171u8, 95u8, 63u8, 26u8, 222u8, + ], + ) + } + #[doc = "Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied"] + #[doc = "later."] + #[doc = ""] + #[doc = "The `check_version` parameter sets a boolean flag for whether or not the runtime's spec"] + #[doc = "version and name should be verified on upgrade. Since the authorization only has a hash,"] + #[doc = "it cannot actually perform the verification."] + #[doc = ""] + #[doc = "This call requires Root origin."] + pub fn authorize_upgrade( + &self, + code_hash: ::subxt::utils::H256, + check_version: ::core::primitive::bool, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "ParachainSystem", + "authorize_upgrade", + AuthorizeUpgrade { code_hash, check_version }, + [ + 208u8, 115u8, 62u8, 35u8, 70u8, 223u8, 65u8, 57u8, 216u8, 44u8, 169u8, + 249u8, 90u8, 112u8, 17u8, 208u8, 30u8, 131u8, 102u8, 131u8, 240u8, + 217u8, 230u8, 214u8, 145u8, 198u8, 55u8, 13u8, 217u8, 51u8, 178u8, + 141u8, + ], + ) + } + #[doc = "Provide the preimage (runtime binary) `code` for an upgrade that has been authorized."] + #[doc = ""] + #[doc = "If the authorization required a version check, this call will ensure the spec name"] + #[doc = "remains unchanged and that the spec version has increased."] + #[doc = ""] + #[doc = "Note that this function will not apply the new `code`, but only attempt to schedule the"] + #[doc = "upgrade with the Relay Chain."] + #[doc = ""] + #[doc = "All origins are allowed."] + pub fn enact_authorized_upgrade( + &self, + code: ::std::vec::Vec<::core::primitive::u8>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "ParachainSystem", + "enact_authorized_upgrade", + EnactAuthorizedUpgrade { code }, + [ + 43u8, 157u8, 1u8, 230u8, 134u8, 72u8, 230u8, 35u8, 159u8, 13u8, 201u8, + 134u8, 184u8, 94u8, 167u8, 13u8, 108u8, 157u8, 145u8, 166u8, 119u8, + 37u8, 51u8, 121u8, 252u8, 255u8, 48u8, 251u8, 126u8, 152u8, 247u8, 5u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::cumulus_pallet_parachain_system::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "The validation function has been scheduled to apply."] + pub struct ValidationFunctionStored; + impl ::subxt::events::StaticEvent for ValidationFunctionStored { + const PALLET: &'static str = "ParachainSystem"; + const EVENT: &'static str = "ValidationFunctionStored"; + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "The validation function was applied as of the contained relay chain block number."] + pub struct ValidationFunctionApplied { + pub relay_chain_block_num: ::core::primitive::u32, + } + impl ::subxt::events::StaticEvent for ValidationFunctionApplied { + const PALLET: &'static str = "ParachainSystem"; + const EVENT: &'static str = "ValidationFunctionApplied"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "The relay-chain aborted the upgrade process."] + pub struct ValidationFunctionDiscarded; + impl ::subxt::events::StaticEvent for ValidationFunctionDiscarded { + const PALLET: &'static str = "ParachainSystem"; + const EVENT: &'static str = "ValidationFunctionDiscarded"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An upgrade has been authorized."] + pub struct UpgradeAuthorized { + pub code_hash: ::subxt::utils::H256, + } + impl ::subxt::events::StaticEvent for UpgradeAuthorized { + const PALLET: &'static str = "ParachainSystem"; + const EVENT: &'static str = "UpgradeAuthorized"; + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Some downward messages have been received and will be processed."] + pub struct DownwardMessagesReceived { + pub count: ::core::primitive::u32, + } + impl ::subxt::events::StaticEvent for DownwardMessagesReceived { + const PALLET: &'static str = "ParachainSystem"; + const EVENT: &'static str = "DownwardMessagesReceived"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward messages were processed using the given weight."] + pub struct DownwardMessagesProcessed { + pub weight_used: ::sp_weights::Weight, + pub dmq_head: ::subxt::utils::H256, + } + impl ::subxt::events::StaticEvent for DownwardMessagesProcessed { + const PALLET: &'static str = "ParachainSystem"; + const EVENT: &'static str = "DownwardMessagesProcessed"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An upward message was sent to the relay chain."] + pub struct UpwardMessageSent { + pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + } + impl ::subxt::events::StaticEvent for UpwardMessageSent { + const PALLET: &'static str = "ParachainSystem"; + const EVENT: &'static str = "UpwardMessageSent"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " In case of a scheduled upgrade, this storage field contains the validation code to be applied."] + #[doc = ""] + #[doc = " As soon as the relay chain gives us the go-ahead signal, we will overwrite the [`:code`][well_known_keys::CODE]"] + #[doc = " which will result the next block process with the new validation code. This concludes the upgrade process."] + #[doc = ""] + #[doc = " [well_known_keys::CODE]: sp_core::storage::well_known_keys::CODE"] + pub fn pending_validation_code( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "PendingValidationCode", + vec![], + [ + 162u8, 35u8, 108u8, 76u8, 160u8, 93u8, 215u8, 84u8, 20u8, 249u8, 57u8, + 187u8, 88u8, 161u8, 15u8, 131u8, 213u8, 89u8, 140u8, 20u8, 227u8, + 204u8, 79u8, 176u8, 114u8, 119u8, 8u8, 7u8, 64u8, 15u8, 90u8, 92u8, + ], + ) + } + #[doc = " Validation code that is set by the parachain and is to be communicated to collator and"] + #[doc = " consequently the relay-chain."] + #[doc = ""] + #[doc = " This will be cleared in `on_initialize` of each new block if no other pallet already set"] + #[doc = " the value."] + pub fn new_validation_code( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "NewValidationCode", + vec![], + [ + 224u8, 174u8, 53u8, 106u8, 240u8, 49u8, 48u8, 79u8, 219u8, 74u8, 142u8, + 166u8, 92u8, 204u8, 244u8, 200u8, 43u8, 169u8, 177u8, 207u8, 190u8, + 106u8, 180u8, 65u8, 245u8, 131u8, 134u8, 4u8, 53u8, 45u8, 76u8, 3u8, + ], + ) + } + #[doc = " The [`PersistedValidationData`] set for this block."] + #[doc = " This value is expected to be set only once per block and it's never stored"] + #[doc = " in the trie."] + pub fn validation_data( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::polkadot_primitives::v4::PersistedValidationData< + ::subxt::utils::H256, + ::core::primitive::u32, + >, + >, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "ValidationData", + vec![], + [ + 112u8, 58u8, 240u8, 81u8, 219u8, 110u8, 244u8, 186u8, 251u8, 90u8, + 195u8, 217u8, 229u8, 102u8, 233u8, 24u8, 109u8, 96u8, 219u8, 72u8, + 139u8, 93u8, 58u8, 140u8, 40u8, 110u8, 167u8, 98u8, 199u8, 12u8, 138u8, + 131u8, + ], + ) + } + #[doc = " Were the validation data set to notify the relay chain?"] + pub fn did_set_validation_code( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "DidSetValidationCode", + vec![], + [ + 89u8, 83u8, 74u8, 174u8, 234u8, 188u8, 149u8, 78u8, 140u8, 17u8, 92u8, + 165u8, 243u8, 87u8, 59u8, 97u8, 135u8, 81u8, 192u8, 86u8, 193u8, 187u8, + 113u8, 22u8, 108u8, 83u8, 242u8, 208u8, 174u8, 40u8, 49u8, 245u8, + ], + ) + } + #[doc = " The relay chain block number associated with the last parachain block."] + pub fn last_relay_chain_block_number( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "LastRelayChainBlockNumber", + vec![], + [ + 68u8, 121u8, 6u8, 159u8, 181u8, 94u8, 151u8, 215u8, 225u8, 244u8, 4u8, + 158u8, 216u8, 85u8, 55u8, 228u8, 197u8, 35u8, 200u8, 33u8, 29u8, 182u8, + 17u8, 83u8, 59u8, 63u8, 25u8, 180u8, 132u8, 23u8, 97u8, 252u8, + ], + ) + } + #[doc = " An option which indicates if the relay-chain restricts signalling a validation code upgrade."] + #[doc = " In other words, if this is `Some` and [`NewValidationCode`] is `Some` then the produced"] + #[doc = " candidate will be invalid."] + #[doc = ""] + #[doc = " This storage item is a mirror of the corresponding value for the current parachain from the"] + #[doc = " relay-chain. This value is ephemeral which means it doesn't hit the storage. This value is"] + #[doc = " set after the inherent."] + pub fn upgrade_restriction_signal( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::core::option::Option< + runtime_types::polkadot_primitives::v4::UpgradeRestriction, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "UpgradeRestrictionSignal", + vec![], + [ + 61u8, 3u8, 26u8, 6u8, 88u8, 114u8, 109u8, 63u8, 7u8, 115u8, 245u8, + 198u8, 73u8, 234u8, 28u8, 228u8, 126u8, 27u8, 151u8, 18u8, 133u8, 54u8, + 144u8, 149u8, 246u8, 43u8, 83u8, 47u8, 77u8, 238u8, 10u8, 196u8, + ], + ) + } + #[doc = " The state proof for the last relay parent block."] + #[doc = ""] + #[doc = " This field is meant to be updated each block with the validation data inherent. Therefore,"] + #[doc = " before processing of the inherent, e.g. in `on_initialize` this data may be stale."] + #[doc = ""] + #[doc = " This data is also absent from the genesis."] + pub fn relay_state_proof( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::sp_trie::storage_proof::StorageProof, + >, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "RelayStateProof", + vec![], + [ + 35u8, 124u8, 167u8, 221u8, 162u8, 145u8, 158u8, 186u8, 57u8, 154u8, + 225u8, 6u8, 176u8, 13u8, 178u8, 195u8, 209u8, 122u8, 221u8, 26u8, + 155u8, 126u8, 153u8, 246u8, 101u8, 221u8, 61u8, 145u8, 211u8, 236u8, + 48u8, 130u8, + ], + ) + } + #[doc = " The snapshot of some state related to messaging relevant to the current parachain as per"] + #[doc = " the relay parent."] + #[doc = ""] + #[doc = " This field is meant to be updated each block with the validation data inherent. Therefore,"] + #[doc = " before processing of the inherent, e.g. in `on_initialize` this data may be stale."] + #[doc = ""] + #[doc = " This data is also absent from the genesis."] pub fn relevant_messaging_state (& self ,) -> :: subxt :: storage :: address :: StaticStorageAddress :: < :: subxt :: metadata :: DecodeStaticType < runtime_types :: cumulus_pallet_parachain_system :: relay_state_snapshot :: MessagingStateSnapshot > , :: subxt :: storage :: address :: Yes , () , () >{ + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "RelevantMessagingState", + vec![], + [ + 68u8, 241u8, 114u8, 83u8, 200u8, 99u8, 8u8, 244u8, 110u8, 134u8, 106u8, + 153u8, 17u8, 90u8, 184u8, 157u8, 100u8, 140u8, 157u8, 83u8, 25u8, + 166u8, 173u8, 31u8, 221u8, 24u8, 236u8, 85u8, 176u8, 223u8, 237u8, + 65u8, + ], + ) + } + #[doc = " The parachain host configuration that was obtained from the relay parent."] + #[doc = ""] + #[doc = " This field is meant to be updated each block with the validation data inherent. Therefore,"] + #[doc = " before processing of the inherent, e.g. in `on_initialize` this data may be stale."] + #[doc = ""] + #[doc = " This data is also absent from the genesis."] + pub fn host_configuration( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::polkadot_primitives::v4::AbridgedHostConfiguration, + >, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "HostConfiguration", + vec![], + [ + 104u8, 200u8, 30u8, 202u8, 119u8, 204u8, 233u8, 20u8, 67u8, 199u8, + 47u8, 166u8, 254u8, 152u8, 10u8, 187u8, 240u8, 255u8, 148u8, 201u8, + 134u8, 41u8, 130u8, 201u8, 112u8, 65u8, 68u8, 103u8, 56u8, 123u8, + 178u8, 113u8, + ], + ) + } + #[doc = " The last downward message queue chain head we have observed."] + #[doc = ""] + #[doc = " This value is loaded before and saved after processing inbound downward messages carried"] + #[doc = " by the system inherent."] + pub fn last_dmq_mqc_head( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::cumulus_primitives_parachain_inherent::MessageQueueChain, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "LastDmqMqcHead", + vec![], + [ + 176u8, 255u8, 246u8, 125u8, 36u8, 120u8, 24u8, 44u8, 26u8, 64u8, 236u8, + 210u8, 189u8, 237u8, 50u8, 78u8, 45u8, 139u8, 58u8, 141u8, 112u8, + 253u8, 178u8, 198u8, 87u8, 71u8, 77u8, 248u8, 21u8, 145u8, 187u8, 52u8, + ], + ) + } + #[doc = " The message queue chain heads we have observed per each channel incoming channel."] + #[doc = ""] + #[doc = " This value is loaded before and saved after processing inbound downward messages carried"] + #[doc = " by the system inherent."] + pub fn last_hrmp_mqc_heads( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::subxt::utils::KeyedVec< + runtime_types::polkadot_parachain::primitives::Id, + runtime_types::cumulus_primitives_parachain_inherent::MessageQueueChain, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "LastHrmpMqcHeads", + vec![], + [ + 55u8, 179u8, 35u8, 16u8, 173u8, 0u8, 122u8, 179u8, 236u8, 98u8, 9u8, + 112u8, 11u8, 219u8, 241u8, 89u8, 131u8, 198u8, 64u8, 139u8, 103u8, + 158u8, 77u8, 107u8, 83u8, 236u8, 255u8, 208u8, 47u8, 61u8, 219u8, + 240u8, + ], + ) + } + #[doc = " Number of downward messages processed in a block."] + #[doc = ""] + #[doc = " This will be cleared in `on_initialize` of each new block."] + pub fn processed_downward_messages( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "ProcessedDownwardMessages", + vec![], + [ + 48u8, 177u8, 84u8, 228u8, 101u8, 235u8, 181u8, 27u8, 66u8, 55u8, 50u8, + 146u8, 245u8, 223u8, 77u8, 132u8, 178u8, 80u8, 74u8, 90u8, 166u8, 81u8, + 109u8, 25u8, 91u8, 69u8, 5u8, 69u8, 123u8, 197u8, 160u8, 146u8, + ], + ) + } + #[doc = " HRMP watermark that was set in a block."] + #[doc = ""] + #[doc = " This will be cleared in `on_initialize` of each new block."] + pub fn hrmp_watermark( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "HrmpWatermark", + vec![], + [ + 189u8, 59u8, 183u8, 195u8, 69u8, 185u8, 241u8, 226u8, 62u8, 204u8, + 230u8, 77u8, 102u8, 75u8, 86u8, 157u8, 249u8, 140u8, 219u8, 72u8, 94u8, + 64u8, 176u8, 72u8, 34u8, 205u8, 114u8, 103u8, 231u8, 233u8, 206u8, + 111u8, + ], + ) + } + #[doc = " HRMP messages that were sent in a block."] + #[doc = ""] + #[doc = " This will be cleared in `on_initialize` of each new block."] + pub fn hrmp_outbound_messages( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec< + runtime_types::polkadot_core_primitives::OutboundHrmpMessage< + runtime_types::polkadot_parachain::primitives::Id, + >, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "HrmpOutboundMessages", + vec![], + [ + 74u8, 86u8, 173u8, 248u8, 90u8, 230u8, 71u8, 225u8, 127u8, 164u8, + 221u8, 62u8, 146u8, 13u8, 73u8, 9u8, 98u8, 168u8, 6u8, 14u8, 97u8, + 166u8, 45u8, 70u8, 62u8, 210u8, 9u8, 32u8, 83u8, 18u8, 4u8, 201u8, + ], + ) + } + #[doc = " Upward messages that were sent in a block."] + #[doc = ""] + #[doc = " This will be cleared in `on_initialize` of each new block."] + pub fn upward_messages( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "UpwardMessages", + vec![], + [ + 129u8, 208u8, 187u8, 36u8, 48u8, 108u8, 135u8, 56u8, 204u8, 60u8, + 100u8, 158u8, 113u8, 238u8, 46u8, 92u8, 228u8, 41u8, 178u8, 177u8, + 208u8, 195u8, 148u8, 149u8, 127u8, 21u8, 93u8, 92u8, 29u8, 115u8, 10u8, + 248u8, + ], + ) + } + #[doc = " Upward messages that are still pending and not yet send to the relay chain."] + pub fn pending_upward_messages( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "PendingUpwardMessages", + vec![], + [ + 223u8, 46u8, 224u8, 227u8, 222u8, 119u8, 225u8, 244u8, 59u8, 87u8, + 127u8, 19u8, 217u8, 237u8, 103u8, 61u8, 6u8, 210u8, 107u8, 201u8, + 117u8, 25u8, 85u8, 248u8, 36u8, 231u8, 28u8, 202u8, 41u8, 140u8, 208u8, + 254u8, + ], + ) + } + #[doc = " The number of HRMP messages we observed in `on_initialize` and thus used that number for"] + #[doc = " announcing the weight of `on_initialize` and `on_finalize`."] + pub fn announced_hrmp_messages_per_candidate( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "AnnouncedHrmpMessagesPerCandidate", + vec![], + [ + 132u8, 61u8, 162u8, 129u8, 251u8, 243u8, 20u8, 144u8, 162u8, 73u8, + 237u8, 51u8, 248u8, 41u8, 127u8, 171u8, 180u8, 79u8, 137u8, 23u8, 66u8, + 134u8, 106u8, 222u8, 182u8, 154u8, 0u8, 145u8, 184u8, 156u8, 36u8, + 97u8, + ], + ) + } + #[doc = " The weight we reserve at the beginning of the block for processing XCMP messages. This"] + #[doc = " overrides the amount set in the Config trait."] + pub fn reserved_xcmp_weight_override( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::sp_weights::Weight>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "ReservedXcmpWeightOverride", + vec![], + [ + 180u8, 90u8, 34u8, 178u8, 1u8, 242u8, 211u8, 97u8, 100u8, 34u8, 39u8, + 42u8, 142u8, 249u8, 236u8, 194u8, 244u8, 164u8, 96u8, 54u8, 98u8, 46u8, + 92u8, 196u8, 185u8, 51u8, 231u8, 234u8, 249u8, 143u8, 244u8, 64u8, + ], + ) + } + #[doc = " The weight we reserve at the beginning of the block for processing DMP messages. This"] + #[doc = " overrides the amount set in the Config trait."] + pub fn reserved_dmp_weight_override( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::sp_weights::Weight>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "ReservedDmpWeightOverride", + vec![], + [ + 90u8, 122u8, 168u8, 240u8, 95u8, 195u8, 160u8, 109u8, 175u8, 170u8, + 227u8, 44u8, 139u8, 176u8, 32u8, 161u8, 57u8, 233u8, 56u8, 55u8, 123u8, + 168u8, 174u8, 96u8, 159u8, 62u8, 186u8, 186u8, 17u8, 70u8, 57u8, 246u8, + ], + ) + } + #[doc = " The next authorized upgrade, if there is one."] + pub fn authorized_upgrade( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::cumulus_pallet_parachain_system::CodeUpgradeAuthorization, + >, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "AuthorizedUpgrade", + vec![], + [ + 12u8, 212u8, 71u8, 191u8, 89u8, 101u8, 195u8, 3u8, 23u8, 180u8, 233u8, + 52u8, 53u8, 133u8, 207u8, 94u8, 58u8, 43u8, 221u8, 236u8, 161u8, 41u8, + 30u8, 194u8, 125u8, 2u8, 118u8, 152u8, 197u8, 49u8, 34u8, 33u8, + ], + ) + } + #[doc = " A custom head data that should be returned as result of `validate_block`."] + #[doc = ""] + #[doc = " See [`Pallet::set_custom_validation_head_data`] for more information."] + pub fn custom_validation_head_data( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainSystem", + "CustomValidationHeadData", + vec![], + [ + 189u8, 150u8, 234u8, 128u8, 111u8, 27u8, 173u8, 92u8, 109u8, 4u8, 98u8, + 103u8, 158u8, 19u8, 16u8, 5u8, 107u8, 135u8, 126u8, 170u8, 62u8, 64u8, + 149u8, 80u8, 33u8, 17u8, 83u8, 22u8, 176u8, 118u8, 26u8, 223u8, + ], + ) + } + } + } + } + pub mod parachain_info { + use super::{root_mod, runtime_types}; + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + pub fn parachain_id( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::polkadot_parachain::primitives::Id, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "ParachainInfo", + "ParachainId", + vec![], + [ + 151u8, 191u8, 241u8, 118u8, 192u8, 47u8, 166u8, 151u8, 217u8, 240u8, + 165u8, 232u8, 51u8, 113u8, 243u8, 1u8, 89u8, 240u8, 11u8, 1u8, 77u8, + 104u8, 12u8, 56u8, 17u8, 135u8, 214u8, 19u8, 114u8, 135u8, 66u8, 76u8, + ], + ) + } + } + } + } + pub mod balances { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct TransferAllowDeath { + pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + pub value: ::core::primitive::u128, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetBalanceDeprecated { + pub who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + pub new_free: ::core::primitive::u128, + #[codec(compact)] + pub old_reserved: ::core::primitive::u128, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ForceTransfer { + pub source: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + pub value: ::core::primitive::u128, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct TransferKeepAlive { + pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + pub value: ::core::primitive::u128, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct TransferAll { + pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + pub keep_alive: ::core::primitive::bool, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ForceUnreserve { + pub who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + pub amount: ::core::primitive::u128, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct UpgradeAccounts { + pub who: ::std::vec::Vec<::sp_core::crypto::AccountId32>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Transfer { + pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + pub value: ::core::primitive::u128, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ForceSetBalance { + pub who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + pub new_free: ::core::primitive::u128, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Transfer some liquid free balance to another account."] + #[doc = ""] + #[doc = "`transfer_allow_death` will set the `FreeBalance` of the sender and receiver."] + #[doc = "If the sender's account is below the existential deposit as a result"] + #[doc = "of the transfer, the account will be reaped."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be `Signed` by the transactor."] + pub fn transfer_allow_death( + &self, + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + value: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "transfer_allow_death", + TransferAllowDeath { dest, value }, + [ + 234u8, 130u8, 149u8, 36u8, 235u8, 112u8, 159u8, 189u8, 104u8, 148u8, + 108u8, 230u8, 25u8, 198u8, 71u8, 158u8, 112u8, 3u8, 162u8, 25u8, 145u8, + 252u8, 44u8, 63u8, 47u8, 34u8, 47u8, 158u8, 61u8, 14u8, 120u8, 255u8, + ], + ) + } + #[doc = "Set the regular balance of a given account; it also takes a reserved balance but this"] + #[doc = "must be the same as the account's current reserved balance."] + #[doc = ""] + #[doc = "The dispatch origin for this call is `root`."] + #[doc = ""] + #[doc = "WARNING: This call is DEPRECATED! Use `force_set_balance` instead."] + pub fn set_balance_deprecated( + &self, + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + new_free: ::core::primitive::u128, + old_reserved: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "set_balance_deprecated", + SetBalanceDeprecated { who, new_free, old_reserved }, + [ + 240u8, 107u8, 184u8, 206u8, 78u8, 106u8, 115u8, 152u8, 130u8, 56u8, + 156u8, 176u8, 105u8, 27u8, 176u8, 187u8, 49u8, 171u8, 229u8, 79u8, + 254u8, 248u8, 8u8, 162u8, 134u8, 12u8, 89u8, 100u8, 137u8, 102u8, + 132u8, 158u8, + ], + ) + } + #[doc = "Exactly as `transfer_allow_death`, except the origin must be root and the source account"] + #[doc = "may be specified."] + pub fn force_transfer( + &self, + source: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + value: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "force_transfer", + ForceTransfer { source, dest, value }, + [ + 79u8, 174u8, 212u8, 108u8, 184u8, 33u8, 170u8, 29u8, 232u8, 254u8, + 195u8, 218u8, 221u8, 134u8, 57u8, 99u8, 6u8, 70u8, 181u8, 227u8, 56u8, + 239u8, 243u8, 158u8, 157u8, 245u8, 36u8, 162u8, 11u8, 237u8, 147u8, + 15u8, + ], + ) + } + #[doc = "Same as the [`transfer_allow_death`] call, but with a check that the transfer will not"] + #[doc = "kill the origin account."] + #[doc = ""] + #[doc = "99% of the time you want [`transfer_allow_death`] instead."] + #[doc = ""] + #[doc = "[`transfer_allow_death`]: struct.Pallet.html#method.transfer"] + pub fn transfer_keep_alive( + &self, + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + value: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "transfer_keep_alive", + TransferKeepAlive { dest, value }, + [ + 112u8, 179u8, 75u8, 168u8, 193u8, 221u8, 9u8, 82u8, 190u8, 113u8, + 253u8, 13u8, 130u8, 134u8, 170u8, 216u8, 136u8, 111u8, 242u8, 220u8, + 202u8, 112u8, 47u8, 79u8, 73u8, 244u8, 226u8, 59u8, 240u8, 188u8, + 210u8, 208u8, + ], + ) + } + #[doc = "Transfer the entire transferable balance from the caller account."] + #[doc = ""] + #[doc = "NOTE: This function only attempts to transfer _transferable_ balances. This means that"] + #[doc = "any locked, reserved, or existential deposits (when `keep_alive` is `true`), will not be"] + #[doc = "transferred by this function. To ensure that this function results in a killed account,"] + #[doc = "you might need to prepare the account by removing any reference counters, storage"] + #[doc = "deposits, etc..."] + #[doc = ""] + #[doc = "The dispatch origin of this call must be Signed."] + #[doc = ""] + #[doc = "- `dest`: The recipient of the transfer."] + #[doc = "- `keep_alive`: A boolean to determine if the `transfer_all` operation should send all"] + #[doc = " of the funds the account has, causing the sender account to be killed (false), or"] + #[doc = " transfer everything except at least the existential deposit, which will guarantee to"] + #[doc = " keep the sender account alive (true)."] + pub fn transfer_all( + &self, + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + keep_alive: ::core::primitive::bool, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "transfer_all", + TransferAll { dest, keep_alive }, + [ + 46u8, 129u8, 29u8, 177u8, 221u8, 107u8, 245u8, 69u8, 238u8, 126u8, + 145u8, 26u8, 219u8, 208u8, 14u8, 80u8, 149u8, 1u8, 214u8, 63u8, 67u8, + 201u8, 144u8, 45u8, 129u8, 145u8, 174u8, 71u8, 238u8, 113u8, 208u8, + 34u8, + ], + ) + } + #[doc = "Unreserve some balance from a user by force."] + #[doc = ""] + #[doc = "Can only be called by ROOT."] + pub fn force_unreserve( + &self, + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + amount: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "force_unreserve", + ForceUnreserve { who, amount }, + [ + 160u8, 146u8, 137u8, 76u8, 157u8, 187u8, 66u8, 148u8, 207u8, 76u8, + 32u8, 254u8, 82u8, 215u8, 35u8, 161u8, 213u8, 52u8, 32u8, 98u8, 102u8, + 106u8, 234u8, 123u8, 6u8, 175u8, 184u8, 188u8, 174u8, 106u8, 176u8, + 78u8, + ], + ) + } + #[doc = "Upgrade a specified account."] + #[doc = ""] + #[doc = "- `origin`: Must be `Signed`."] + #[doc = "- `who`: The account to be upgraded."] + #[doc = ""] + #[doc = "This will waive the transaction fee if at least all but 10% of the accounts needed to"] + #[doc = "be upgraded. (We let some not have to be upgraded just in order to allow for the"] + #[doc = "possibililty of churn)."] + pub fn upgrade_accounts( + &self, + who: ::std::vec::Vec<::sp_core::crypto::AccountId32>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "upgrade_accounts", + UpgradeAccounts { who }, + [ + 164u8, 61u8, 119u8, 24u8, 165u8, 46u8, 197u8, 59u8, 39u8, 198u8, 228u8, + 96u8, 228u8, 45u8, 85u8, 51u8, 37u8, 5u8, 75u8, 40u8, 241u8, 163u8, + 86u8, 228u8, 151u8, 217u8, 47u8, 105u8, 203u8, 103u8, 207u8, 4u8, + ], + ) + } + #[doc = "Alias for `transfer_allow_death`, provided only for name-wise compatibility."] + #[doc = ""] + #[doc = "WARNING: DEPRECATED! Will be released in approximately 3 months."] + pub fn transfer( + &self, + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + value: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "transfer", + Transfer { dest, value }, + [ + 111u8, 222u8, 32u8, 56u8, 171u8, 77u8, 252u8, 29u8, 194u8, 155u8, + 200u8, 192u8, 198u8, 81u8, 23u8, 115u8, 236u8, 91u8, 218u8, 114u8, + 107u8, 141u8, 138u8, 100u8, 237u8, 21u8, 58u8, 172u8, 3u8, 20u8, 216u8, + 38u8, + ], + ) + } + #[doc = "Set the regular balance of a given account."] + #[doc = ""] + #[doc = "The dispatch origin for this call is `root`."] + pub fn force_set_balance( + &self, + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + new_free: ::core::primitive::u128, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "Balances", + "force_set_balance", + ForceSetBalance { who, new_free }, + [ + 237u8, 4u8, 41u8, 58u8, 62u8, 179u8, 160u8, 4u8, 50u8, 71u8, 178u8, + 36u8, 130u8, 130u8, 92u8, 229u8, 16u8, 245u8, 169u8, 109u8, 165u8, + 72u8, 94u8, 70u8, 196u8, 136u8, 37u8, 94u8, 140u8, 215u8, 125u8, 125u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::pallet_balances::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An account was created with some free balance."] + pub struct Endowed { + pub account: ::sp_core::crypto::AccountId32, + pub free_balance: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Endowed { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Endowed"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An account was removed whose balance was non-zero but below ExistentialDeposit,"] + #[doc = "resulting in an outright loss."] + pub struct DustLost { + pub account: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for DustLost { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "DustLost"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Transfer succeeded."] + pub struct Transfer { + pub from: ::sp_core::crypto::AccountId32, + pub to: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Transfer { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Transfer"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A balance was set by root."] + pub struct BalanceSet { + pub who: ::sp_core::crypto::AccountId32, + pub free: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for BalanceSet { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "BalanceSet"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some balance was reserved (moved from free to reserved)."] + pub struct Reserved { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Reserved { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Reserved"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some balance was unreserved (moved from reserved to free)."] + pub struct Unreserved { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Unreserved { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Unreserved"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some balance was moved from the reserve of the first account to the second account."] + #[doc = "Final argument indicates the destination balance type."] + pub struct ReserveRepatriated { + pub from: ::sp_core::crypto::AccountId32, + pub to: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + pub destination_status: + runtime_types::frame_support::traits::tokens::misc::BalanceStatus, + } + impl ::subxt::events::StaticEvent for ReserveRepatriated { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "ReserveRepatriated"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some amount was deposited (e.g. for transaction fees)."] + pub struct Deposit { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Deposit { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Deposit"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some amount was withdrawn from the account (e.g. for transaction fees)."] + pub struct Withdraw { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Withdraw { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Withdraw"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some amount was removed from the account (e.g. for misbehavior)."] + pub struct Slashed { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Slashed { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Slashed"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some amount was minted into an account."] + pub struct Minted { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Minted { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Minted"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some amount was burned from an account."] + pub struct Burned { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Burned { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Burned"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some amount was suspended from an account (it can be restored later)."] + pub struct Suspended { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Suspended { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Suspended"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some amount was restored into an account."] + pub struct Restored { + pub who: ::sp_core::crypto::AccountId32, + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Restored { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Restored"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An account was upgraded."] + pub struct Upgraded { + pub who: ::sp_core::crypto::AccountId32, + } + impl ::subxt::events::StaticEvent for Upgraded { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Upgraded"; + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Total issuance was increased by `amount`, creating a credit to be balanced."] + pub struct Issued { + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Issued { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Issued"; + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Total issuance was decreased by `amount`, creating a debt to be balanced."] + pub struct Rescinded { + pub amount: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for Rescinded { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Rescinded"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " The total units issued in the system."] + pub fn total_issuance( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "TotalIssuance", + vec![], + [ + 1u8, 206u8, 252u8, 237u8, 6u8, 30u8, 20u8, 232u8, 164u8, 115u8, 51u8, + 156u8, 156u8, 206u8, 241u8, 187u8, 44u8, 84u8, 25u8, 164u8, 235u8, + 20u8, 86u8, 242u8, 124u8, 23u8, 28u8, 140u8, 26u8, 73u8, 231u8, 51u8, + ], + ) + } + #[doc = " The total units of outstanding deactivated balance in the system."] + pub fn inactive_issuance( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "InactiveIssuance", + vec![], + [ + 74u8, 203u8, 111u8, 142u8, 225u8, 104u8, 173u8, 51u8, 226u8, 12u8, + 85u8, 135u8, 41u8, 206u8, 177u8, 238u8, 94u8, 246u8, 184u8, 250u8, + 140u8, 213u8, 91u8, 118u8, 163u8, 111u8, 211u8, 46u8, 204u8, 160u8, + 154u8, 21u8, + ], + ) + } + #[doc = " The Balances pallet example of storing the balance of an account."] + #[doc = ""] + #[doc = " # Example"] + #[doc = ""] + #[doc = " ```nocompile"] + #[doc = " impl pallet_balances::Config for Runtime {"] + #[doc = " type AccountStore = StorageMapShim, frame_system::Provider, AccountId, Self::AccountData>"] + #[doc = " }"] + #[doc = " ```"] + #[doc = ""] + #[doc = " You can also store the balance of an account in the `System` pallet."] + #[doc = ""] + #[doc = " # Example"] + #[doc = ""] + #[doc = " ```nocompile"] + #[doc = " impl pallet_balances::Config for Runtime {"] + #[doc = " type AccountStore = System"] + #[doc = " }"] + #[doc = " ```"] + #[doc = ""] + #[doc = " But this comes with tradeoffs, storing account balances in the system pallet stores"] + #[doc = " `frame_system` data alongside the account data contrary to storing account balances in the"] + #[doc = " `Balances` pallet, which uses a `StorageMap` to store balances data only."] + #[doc = " NOTE: This is only used in the case that this pallet is used to store balances."] + pub fn account( + &self, + _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::pallet_balances::types::AccountData<::core::primitive::u128>, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Account", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 109u8, 250u8, 18u8, 96u8, 139u8, 232u8, 4u8, 139u8, 133u8, 239u8, 30u8, + 237u8, 73u8, 209u8, 143u8, 160u8, 94u8, 248u8, 124u8, 43u8, 224u8, + 165u8, 11u8, 6u8, 176u8, 144u8, 189u8, 161u8, 174u8, 210u8, 56u8, + 225u8, + ], + ) + } + #[doc = " The Balances pallet example of storing the balance of an account."] + #[doc = ""] + #[doc = " # Example"] + #[doc = ""] + #[doc = " ```nocompile"] + #[doc = " impl pallet_balances::Config for Runtime {"] + #[doc = " type AccountStore = StorageMapShim, frame_system::Provider, AccountId, Self::AccountData>"] + #[doc = " }"] + #[doc = " ```"] + #[doc = ""] + #[doc = " You can also store the balance of an account in the `System` pallet."] + #[doc = ""] + #[doc = " # Example"] + #[doc = ""] + #[doc = " ```nocompile"] + #[doc = " impl pallet_balances::Config for Runtime {"] + #[doc = " type AccountStore = System"] + #[doc = " }"] + #[doc = " ```"] + #[doc = ""] + #[doc = " But this comes with tradeoffs, storing account balances in the system pallet stores"] + #[doc = " `frame_system` data alongside the account data contrary to storing account balances in the"] + #[doc = " `Balances` pallet, which uses a `StorageMap` to store balances data only."] + #[doc = " NOTE: This is only used in the case that this pallet is used to store balances."] + pub fn account_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::pallet_balances::types::AccountData<::core::primitive::u128>, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Account", + Vec::new(), + [ + 109u8, 250u8, 18u8, 96u8, 139u8, 232u8, 4u8, 139u8, 133u8, 239u8, 30u8, + 237u8, 73u8, 209u8, 143u8, 160u8, 94u8, 248u8, 124u8, 43u8, 224u8, + 165u8, 11u8, 6u8, 176u8, 144u8, 189u8, 161u8, 174u8, 210u8, 56u8, + 225u8, + ], + ) + } + #[doc = " Any liquidity locks on some account balances."] + #[doc = " NOTE: Should only be accessed when setting, changing and freeing a lock."] + pub fn locks( + &self, + _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< + runtime_types::pallet_balances::types::BalanceLock< + ::core::primitive::u128, + >, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Locks", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 216u8, 253u8, 87u8, 73u8, 24u8, 218u8, 35u8, 0u8, 244u8, 134u8, 195u8, + 58u8, 255u8, 64u8, 153u8, 212u8, 210u8, 232u8, 4u8, 122u8, 90u8, 212u8, + 136u8, 14u8, 127u8, 232u8, 8u8, 192u8, 40u8, 233u8, 18u8, 250u8, + ], + ) + } + #[doc = " Any liquidity locks on some account balances."] + #[doc = " NOTE: Should only be accessed when setting, changing and freeing a lock."] + pub fn locks_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< + runtime_types::pallet_balances::types::BalanceLock< + ::core::primitive::u128, + >, + >, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Locks", + Vec::new(), + [ + 216u8, 253u8, 87u8, 73u8, 24u8, 218u8, 35u8, 0u8, 244u8, 134u8, 195u8, + 58u8, 255u8, 64u8, 153u8, 212u8, 210u8, 232u8, 4u8, 122u8, 90u8, 212u8, + 136u8, 14u8, 127u8, 232u8, 8u8, 192u8, 40u8, 233u8, 18u8, 250u8, + ], + ) + } + #[doc = " Named reserves on some account balances."] + pub fn reserves( + &self, + _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::pallet_balances::types::ReserveData< + [::core::primitive::u8; 8usize], + ::core::primitive::u128, + >, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Reserves", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 17u8, 32u8, 191u8, 46u8, 76u8, 220u8, 101u8, 100u8, 42u8, 250u8, 128u8, + 167u8, 117u8, 44u8, 85u8, 96u8, 105u8, 216u8, 16u8, 147u8, 74u8, 55u8, + 183u8, 94u8, 160u8, 177u8, 26u8, 187u8, 71u8, 197u8, 187u8, 163u8, + ], + ) + } + #[doc = " Named reserves on some account balances."] + pub fn reserves_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::pallet_balances::types::ReserveData< + [::core::primitive::u8; 8usize], + ::core::primitive::u128, + >, + >, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Reserves", + Vec::new(), + [ + 17u8, 32u8, 191u8, 46u8, 76u8, 220u8, 101u8, 100u8, 42u8, 250u8, 128u8, + 167u8, 117u8, 44u8, 85u8, 96u8, 105u8, 216u8, 16u8, 147u8, 74u8, 55u8, + 183u8, 94u8, 160u8, 177u8, 26u8, 187u8, 71u8, 197u8, 187u8, 163u8, + ], + ) + } + #[doc = " Holds on account balances."] + pub fn holds( + &self, + _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::pallet_balances::types::IdAmount< + (), + ::core::primitive::u128, + >, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Holds", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 247u8, 81u8, 4u8, 220u8, 77u8, 205u8, 28u8, 131u8, 215u8, 74u8, 197u8, + 137u8, 113u8, 214u8, 249u8, 91u8, 81u8, 216u8, 8u8, 5u8, 233u8, 39u8, + 104u8, 250u8, 3u8, 228u8, 148u8, 78u8, 4u8, 34u8, 45u8, 143u8, + ], + ) + } + #[doc = " Holds on account balances."] + pub fn holds_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::pallet_balances::types::IdAmount< + (), + ::core::primitive::u128, + >, + >, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Holds", + Vec::new(), + [ + 247u8, 81u8, 4u8, 220u8, 77u8, 205u8, 28u8, 131u8, 215u8, 74u8, 197u8, + 137u8, 113u8, 214u8, 249u8, 91u8, 81u8, 216u8, 8u8, 5u8, 233u8, 39u8, + 104u8, 250u8, 3u8, 228u8, 148u8, 78u8, 4u8, 34u8, 45u8, 143u8, + ], + ) + } + #[doc = " Freeze locks on account balances."] + pub fn freezes( + &self, + _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::pallet_balances::types::IdAmount< + (), + ::core::primitive::u128, + >, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Freezes", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 211u8, 24u8, 237u8, 217u8, 47u8, 230u8, 147u8, 39u8, 112u8, 209u8, + 193u8, 47u8, 242u8, 13u8, 241u8, 0u8, 100u8, 45u8, 116u8, 130u8, 246u8, + 196u8, 50u8, 134u8, 135u8, 112u8, 206u8, 1u8, 12u8, 53u8, 106u8, 131u8, + ], + ) + } + #[doc = " Freeze locks on account balances."] + pub fn freezes_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::pallet_balances::types::IdAmount< + (), + ::core::primitive::u128, + >, + >, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "Balances", + "Freezes", + Vec::new(), + [ + 211u8, 24u8, 237u8, 217u8, 47u8, 230u8, 147u8, 39u8, 112u8, 209u8, + 193u8, 47u8, 242u8, 13u8, 241u8, 0u8, 100u8, 45u8, 116u8, 130u8, 246u8, + 196u8, 50u8, 134u8, 135u8, 112u8, 206u8, 1u8, 12u8, 53u8, 106u8, 131u8, + ], + ) + } + } + } + pub mod constants { + use super::runtime_types; + pub struct ConstantsApi; + impl ConstantsApi { + #[doc = " The minimum amount required to keep an account open."] + pub fn existential_deposit( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, + > { + ::subxt::constants::StaticConstantAddress::new( + "Balances", + "ExistentialDeposit", + [ + 84u8, 157u8, 140u8, 4u8, 93u8, 57u8, 29u8, 133u8, 105u8, 200u8, 214u8, + 27u8, 144u8, 208u8, 218u8, 160u8, 130u8, 109u8, 101u8, 54u8, 210u8, + 136u8, 71u8, 63u8, 49u8, 237u8, 234u8, 15u8, 178u8, 98u8, 148u8, 156u8, + ], + ) + } + #[doc = " The maximum number of locks that should exist on an account."] + #[doc = " Not strictly enforced, but used for weight estimation."] + pub fn max_locks( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "Balances", + "MaxLocks", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + #[doc = " The maximum number of named reserves that can exist on an account."] + pub fn max_reserves( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "Balances", + "MaxReserves", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + #[doc = " The maximum number of holds that can exist on an account at any time."] + pub fn max_holds( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "Balances", + "MaxHolds", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + #[doc = " The maximum number of individual freeze locks that can exist on an account at any time."] + pub fn max_freezes( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "Balances", + "MaxFreezes", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + } + } + } + pub mod aura { + use super::{root_mod, runtime_types}; + } + pub mod aura_ext { + use super::{root_mod, runtime_types}; + } + pub mod xcmp_queue { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ServiceOverweight { + pub index: ::core::primitive::u64, + pub weight_limit: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SuspendXcmExecution; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ResumeXcmExecution; + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct UpdateSuspendThreshold { + pub new: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct UpdateDropThreshold { + pub new: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct UpdateResumeThreshold { + pub new: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct UpdateThresholdWeight { + pub new: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct UpdateWeightRestrictDecay { + pub new: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct UpdateXcmpMaxIndividualWeight { + pub new: ::sp_weights::Weight, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Services a single overweight XCM."] + #[doc = ""] + #[doc = "- `origin`: Must pass `ExecuteOverweightOrigin`."] + #[doc = "- `index`: The index of the overweight XCM to service"] + #[doc = "- `weight_limit`: The amount of weight that XCM execution may take."] + #[doc = ""] + #[doc = "Errors:"] + #[doc = "- `BadOverweightIndex`: XCM under `index` is not found in the `Overweight` storage map."] + #[doc = "- `BadXcm`: XCM under `index` cannot be properly decoded into a valid XCM format."] + #[doc = "- `WeightOverLimit`: XCM execution may use greater `weight_limit`."] + #[doc = ""] + #[doc = "Events:"] + #[doc = "- `OverweightServiced`: On success."] + pub fn service_overweight( + &self, + index: ::core::primitive::u64, + weight_limit: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "service_overweight", + ServiceOverweight { index, weight_limit }, + [ + 121u8, 236u8, 235u8, 23u8, 210u8, 238u8, 238u8, 122u8, 15u8, 86u8, + 34u8, 119u8, 105u8, 100u8, 214u8, 236u8, 117u8, 39u8, 254u8, 235u8, + 189u8, 15u8, 72u8, 74u8, 225u8, 134u8, 148u8, 126u8, 31u8, 203u8, + 144u8, 106u8, + ], + ) + } + #[doc = "Suspends all XCM executions for the XCMP queue, regardless of the sender's origin."] + #[doc = ""] + #[doc = "- `origin`: Must pass `ControllerOrigin`."] + pub fn suspend_xcm_execution( + &self, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "suspend_xcm_execution", + SuspendXcmExecution {}, + [ + 139u8, 76u8, 166u8, 86u8, 106u8, 144u8, 16u8, 47u8, 105u8, 185u8, 7u8, + 7u8, 63u8, 14u8, 250u8, 236u8, 99u8, 121u8, 101u8, 143u8, 28u8, 175u8, + 108u8, 197u8, 226u8, 43u8, 103u8, 92u8, 186u8, 12u8, 51u8, 153u8, + ], + ) + } + #[doc = "Resumes all XCM executions for the XCMP queue."] + #[doc = ""] + #[doc = "Note that this function doesn't change the status of the in/out bound channels."] + #[doc = ""] + #[doc = "- `origin`: Must pass `ControllerOrigin`."] + pub fn resume_xcm_execution( + &self, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "resume_xcm_execution", + ResumeXcmExecution {}, + [ + 67u8, 111u8, 47u8, 237u8, 79u8, 42u8, 90u8, 56u8, 245u8, 2u8, 20u8, + 23u8, 33u8, 121u8, 135u8, 50u8, 204u8, 147u8, 195u8, 80u8, 177u8, + 202u8, 8u8, 160u8, 164u8, 138u8, 64u8, 252u8, 178u8, 63u8, 102u8, + 245u8, + ], + ) + } + #[doc = "Overwrites the number of pages of messages which must be in the queue for the other side to be told to"] + #[doc = "suspend their sending."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.suspend_value`"] + pub fn update_suspend_threshold( + &self, + new: ::core::primitive::u32, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "update_suspend_threshold", + UpdateSuspendThreshold { new }, + [ + 155u8, 120u8, 9u8, 228u8, 110u8, 62u8, 233u8, 36u8, 57u8, 85u8, 19u8, + 67u8, 246u8, 88u8, 81u8, 116u8, 243u8, 236u8, 174u8, 130u8, 8u8, 246u8, + 254u8, 97u8, 155u8, 207u8, 123u8, 60u8, 164u8, 14u8, 196u8, 97u8, + ], + ) + } + #[doc = "Overwrites the number of pages of messages which must be in the queue after which we drop any further"] + #[doc = "messages from the channel."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.drop_threshold`"] + pub fn update_drop_threshold( + &self, + new: ::core::primitive::u32, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "update_drop_threshold", + UpdateDropThreshold { new }, + [ + 146u8, 177u8, 164u8, 96u8, 247u8, 182u8, 229u8, 175u8, 194u8, 101u8, + 186u8, 168u8, 94u8, 114u8, 172u8, 119u8, 35u8, 222u8, 175u8, 21u8, + 67u8, 61u8, 216u8, 144u8, 194u8, 10u8, 181u8, 62u8, 166u8, 198u8, + 138u8, 243u8, + ], + ) + } + #[doc = "Overwrites the number of pages of messages which the queue must be reduced to before it signals that"] + #[doc = "message sending may recommence after it has been suspended."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.resume_threshold`"] + pub fn update_resume_threshold( + &self, + new: ::core::primitive::u32, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "update_resume_threshold", + UpdateResumeThreshold { new }, + [ + 231u8, 128u8, 80u8, 179u8, 61u8, 50u8, 103u8, 209u8, 103u8, 55u8, + 101u8, 113u8, 150u8, 10u8, 202u8, 7u8, 0u8, 77u8, 58u8, 4u8, 227u8, + 17u8, 225u8, 112u8, 121u8, 203u8, 184u8, 113u8, 231u8, 156u8, 174u8, + 154u8, + ], + ) + } + #[doc = "Overwrites the amount of remaining weight under which we stop processing messages."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.threshold_weight`"] + pub fn update_threshold_weight( + &self, + new: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "update_threshold_weight", + UpdateThresholdWeight { new }, + [ + 14u8, 144u8, 112u8, 207u8, 195u8, 208u8, 184u8, 164u8, 94u8, 41u8, 8u8, + 58u8, 180u8, 80u8, 239u8, 39u8, 210u8, 159u8, 114u8, 169u8, 152u8, + 176u8, 26u8, 161u8, 32u8, 43u8, 250u8, 156u8, 56u8, 21u8, 43u8, 159u8, + ], + ) + } + #[doc = "Overwrites the speed to which the available weight approaches the maximum weight."] + #[doc = "A lower number results in a faster progression. A value of 1 makes the entire weight available initially."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.weight_restrict_decay`."] + pub fn update_weight_restrict_decay( + &self, + new: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "update_weight_restrict_decay", + UpdateWeightRestrictDecay { new }, + [ + 42u8, 53u8, 83u8, 191u8, 51u8, 227u8, 210u8, 193u8, 142u8, 218u8, + 244u8, 177u8, 19u8, 87u8, 148u8, 177u8, 231u8, 197u8, 196u8, 255u8, + 41u8, 130u8, 245u8, 139u8, 107u8, 212u8, 90u8, 161u8, 82u8, 248u8, + 160u8, 223u8, + ], + ) + } + #[doc = "Overwrite the maximum amount of weight any individual message may consume."] + #[doc = "Messages above this weight go into the overweight queue and may only be serviced explicitly."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.xcmp_max_individual_weight`."] + pub fn update_xcmp_max_individual_weight( + &self, + new: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "XcmpQueue", + "update_xcmp_max_individual_weight", + UpdateXcmpMaxIndividualWeight { new }, + [ + 148u8, 185u8, 89u8, 36u8, 152u8, 220u8, 248u8, 233u8, 236u8, 82u8, + 170u8, 111u8, 225u8, 142u8, 25u8, 211u8, 72u8, 248u8, 250u8, 14u8, + 45u8, 72u8, 78u8, 95u8, 92u8, 196u8, 245u8, 104u8, 112u8, 128u8, 27u8, + 109u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::cumulus_pallet_xcmp_queue::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some XCM was executed ok."] + pub struct Success { + pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + pub weight: ::sp_weights::Weight, + } + impl ::subxt::events::StaticEvent for Success { + const PALLET: &'static str = "XcmpQueue"; + const EVENT: &'static str = "Success"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some XCM failed."] + pub struct Fail { + pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + pub error: runtime_types::xcm::v3::traits::Error, + pub weight: ::sp_weights::Weight, + } + impl ::subxt::events::StaticEvent for Fail { + const PALLET: &'static str = "XcmpQueue"; + const EVENT: &'static str = "Fail"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Bad XCM version used."] + pub struct BadVersion { + pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + } + impl ::subxt::events::StaticEvent for BadVersion { + const PALLET: &'static str = "XcmpQueue"; + const EVENT: &'static str = "BadVersion"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Bad XCM format used."] + pub struct BadFormat { + pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + } + impl ::subxt::events::StaticEvent for BadFormat { + const PALLET: &'static str = "XcmpQueue"; + const EVENT: &'static str = "BadFormat"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An HRMP message was sent to a sibling parachain."] + pub struct XcmpMessageSent { + pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + } + impl ::subxt::events::StaticEvent for XcmpMessageSent { + const PALLET: &'static str = "XcmpQueue"; + const EVENT: &'static str = "XcmpMessageSent"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An XCM exceeded the individual message weight budget."] + pub struct OverweightEnqueued { + pub sender: runtime_types::polkadot_parachain::primitives::Id, + pub sent_at: ::core::primitive::u32, + pub index: ::core::primitive::u64, + pub required: ::sp_weights::Weight, + } + impl ::subxt::events::StaticEvent for OverweightEnqueued { + const PALLET: &'static str = "XcmpQueue"; + const EVENT: &'static str = "OverweightEnqueued"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An XCM from the overweight queue was executed with the given actual weight used."] + pub struct OverweightServiced { + pub index: ::core::primitive::u64, + pub used: ::sp_weights::Weight, + } + impl ::subxt::events::StaticEvent for OverweightServiced { + const PALLET: &'static str = "XcmpQueue"; + const EVENT: &'static str = "OverweightServiced"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " Status of the inbound XCMP channels."] + pub fn inbound_xcmp_status( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec< + runtime_types::cumulus_pallet_xcmp_queue::InboundChannelDetails, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "InboundXcmpStatus", + vec![], + [ + 183u8, 198u8, 237u8, 153u8, 132u8, 201u8, 87u8, 182u8, 121u8, 164u8, + 129u8, 241u8, 58u8, 192u8, 115u8, 152u8, 7u8, 33u8, 95u8, 51u8, 2u8, + 176u8, 144u8, 12u8, 125u8, 83u8, 92u8, 198u8, 211u8, 101u8, 28u8, 50u8, + ], + ) + } + #[doc = " Inbound aggregate XCMP messages. It can only be one per ParaId/block."] + pub fn inbound_xcmp_messages( + &self, + _0: impl ::std::borrow::Borrow, + _1: impl ::std::borrow::Borrow<::core::primitive::u32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "InboundXcmpMessages", + vec![ + ::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + ), + ::subxt::storage::address::StorageMapKey::new( + _1.borrow(), + ::subxt::storage::address::StorageHasher::Twox64Concat, + ), + ], + [ + 157u8, 232u8, 222u8, 97u8, 218u8, 96u8, 96u8, 90u8, 216u8, 205u8, 39u8, + 130u8, 109u8, 152u8, 127u8, 57u8, 54u8, 63u8, 104u8, 135u8, 33u8, + 175u8, 197u8, 166u8, 238u8, 22u8, 137u8, 162u8, 226u8, 199u8, 87u8, + 25u8, + ], + ) + } + #[doc = " Inbound aggregate XCMP messages. It can only be one per ParaId/block."] + pub fn inbound_xcmp_messages_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "InboundXcmpMessages", + Vec::new(), + [ + 157u8, 232u8, 222u8, 97u8, 218u8, 96u8, 96u8, 90u8, 216u8, 205u8, 39u8, + 130u8, 109u8, 152u8, 127u8, 57u8, 54u8, 63u8, 104u8, 135u8, 33u8, + 175u8, 197u8, 166u8, 238u8, 22u8, 137u8, 162u8, 226u8, 199u8, 87u8, + 25u8, + ], + ) + } + #[doc = " The non-empty XCMP channels in order of becoming non-empty, and the index of the first"] + #[doc = " and last outbound message. If the two indices are equal, then it indicates an empty"] + #[doc = " queue and there must be a non-`Ok` `OutboundStatus`. We assume queues grow no greater"] + #[doc = " than 65535 items. Queue indices for normal messages begin at one; zero is reserved in"] + #[doc = " case of the need to send a high-priority signal message this block."] + #[doc = " The bool is true if there is a signal message waiting to be sent."] + pub fn outbound_xcmp_status( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec< + runtime_types::cumulus_pallet_xcmp_queue::OutboundChannelDetails, + >, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "OutboundXcmpStatus", + vec![], + [ + 238u8, 120u8, 185u8, 141u8, 82u8, 159u8, 41u8, 68u8, 204u8, 15u8, 46u8, + 152u8, 144u8, 74u8, 250u8, 83u8, 71u8, 105u8, 54u8, 53u8, 226u8, 87u8, + 14u8, 202u8, 58u8, 160u8, 54u8, 162u8, 239u8, 248u8, 227u8, 116u8, + ], + ) + } + #[doc = " The messages outbound in a given XCMP channel."] + pub fn outbound_xcmp_messages( + &self, + _0: impl ::std::borrow::Borrow, + _1: impl ::std::borrow::Borrow<::core::primitive::u16>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "OutboundXcmpMessages", + vec![ + ::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + ), + ::subxt::storage::address::StorageMapKey::new( + _1.borrow(), + ::subxt::storage::address::StorageHasher::Twox64Concat, + ), + ], + [ + 50u8, 182u8, 237u8, 191u8, 106u8, 67u8, 54u8, 1u8, 17u8, 107u8, 70u8, + 90u8, 202u8, 8u8, 63u8, 184u8, 171u8, 111u8, 192u8, 196u8, 7u8, 31u8, + 186u8, 68u8, 31u8, 63u8, 71u8, 61u8, 83u8, 223u8, 79u8, 200u8, + ], + ) + } + #[doc = " The messages outbound in a given XCMP channel."] + pub fn outbound_xcmp_messages_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "OutboundXcmpMessages", + Vec::new(), + [ + 50u8, 182u8, 237u8, 191u8, 106u8, 67u8, 54u8, 1u8, 17u8, 107u8, 70u8, + 90u8, 202u8, 8u8, 63u8, 184u8, 171u8, 111u8, 192u8, 196u8, 7u8, 31u8, + 186u8, 68u8, 31u8, 63u8, 71u8, 61u8, 83u8, 223u8, 79u8, 200u8, + ], + ) + } + #[doc = " Any signal messages waiting to be sent."] + pub fn signal_messages( + &self, + _0: impl ::std::borrow::Borrow, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "SignalMessages", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 156u8, 242u8, 186u8, 89u8, 177u8, 195u8, 90u8, 121u8, 94u8, 106u8, + 222u8, 78u8, 19u8, 162u8, 179u8, 96u8, 38u8, 113u8, 209u8, 148u8, 29u8, + 110u8, 106u8, 167u8, 162u8, 96u8, 221u8, 20u8, 33u8, 179u8, 168u8, + 142u8, + ], + ) + } + #[doc = " Any signal messages waiting to be sent."] + pub fn signal_messages_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "SignalMessages", + Vec::new(), + [ + 156u8, 242u8, 186u8, 89u8, 177u8, 195u8, 90u8, 121u8, 94u8, 106u8, + 222u8, 78u8, 19u8, 162u8, 179u8, 96u8, 38u8, 113u8, 209u8, 148u8, 29u8, + 110u8, 106u8, 167u8, 162u8, 96u8, 221u8, 20u8, 33u8, 179u8, 168u8, + 142u8, + ], + ) + } + #[doc = " The configuration which controls the dynamics of the outbound queue."] + pub fn queue_config( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::cumulus_pallet_xcmp_queue::QueueConfigData, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "QueueConfig", + vec![], + [ + 154u8, 172u8, 227u8, 208u8, 130u8, 93u8, 173u8, 129u8, 33u8, 75u8, + 180u8, 100u8, 35u8, 154u8, 40u8, 188u8, 86u8, 53u8, 74u8, 118u8, 131u8, + 159u8, 240u8, 159u8, 185u8, 45u8, 165u8, 6u8, 90u8, 125u8, 77u8, 253u8, + ], + ) + } + #[doc = " The messages that exceeded max individual message weight budget."] + #[doc = ""] + #[doc = " These message stay in this storage map until they are manually dispatched via"] + #[doc = " `service_overweight`."] + pub fn overweight( + &self, + _0: impl ::std::borrow::Borrow<::core::primitive::u64>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<( + runtime_types::polkadot_parachain::primitives::Id, + ::core::primitive::u32, + ::std::vec::Vec<::core::primitive::u8>, + )>, + ::subxt::storage::address::Yes, + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "Overweight", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Twox64Concat, + )], + [ + 222u8, 249u8, 232u8, 110u8, 117u8, 229u8, 165u8, 164u8, 219u8, 219u8, + 149u8, 204u8, 25u8, 78u8, 204u8, 116u8, 111u8, 114u8, 120u8, 222u8, + 56u8, 77u8, 122u8, 147u8, 108u8, 15u8, 94u8, 161u8, 212u8, 50u8, 7u8, + 7u8, + ], + ) + } + #[doc = " The messages that exceeded max individual message weight budget."] + #[doc = ""] + #[doc = " These message stay in this storage map until they are manually dispatched via"] + #[doc = " `service_overweight`."] + pub fn overweight_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<( + runtime_types::polkadot_parachain::primitives::Id, + ::core::primitive::u32, + ::std::vec::Vec<::core::primitive::u8>, + )>, + (), + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "Overweight", + Vec::new(), + [ + 222u8, 249u8, 232u8, 110u8, 117u8, 229u8, 165u8, 164u8, 219u8, 219u8, + 149u8, 204u8, 25u8, 78u8, 204u8, 116u8, 111u8, 114u8, 120u8, 222u8, + 56u8, 77u8, 122u8, 147u8, 108u8, 15u8, 94u8, 161u8, 212u8, 50u8, 7u8, + 7u8, + ], + ) + } + #[doc = "Counter for the related counted storage map"] + pub fn counter_for_overweight( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "CounterForOverweight", + vec![], + [ + 148u8, 226u8, 248u8, 107u8, 165u8, 97u8, 218u8, 160u8, 127u8, 48u8, + 185u8, 251u8, 35u8, 137u8, 119u8, 251u8, 151u8, 167u8, 189u8, 66u8, + 80u8, 74u8, 134u8, 129u8, 222u8, 180u8, 51u8, 182u8, 50u8, 110u8, 10u8, + 43u8, + ], + ) + } + #[doc = " The number of overweight messages ever recorded in `Overweight`. Also doubles as the next"] + #[doc = " available free overweight index."] + pub fn overweight_count( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u64>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "OverweightCount", + vec![], + [ + 102u8, 180u8, 196u8, 148u8, 115u8, 62u8, 46u8, 238u8, 97u8, 116u8, + 117u8, 42u8, 14u8, 5u8, 72u8, 237u8, 230u8, 46u8, 150u8, 126u8, 89u8, + 64u8, 233u8, 166u8, 180u8, 137u8, 52u8, 233u8, 252u8, 255u8, 36u8, + 20u8, + ], + ) + } + #[doc = " Whether or not the XCMP queue is suspended from executing incoming XCMs or not."] + pub fn queue_suspended( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "XcmpQueue", + "QueueSuspended", + vec![], + [ + 23u8, 37u8, 48u8, 112u8, 222u8, 17u8, 252u8, 65u8, 160u8, 217u8, 218u8, + 30u8, 2u8, 1u8, 204u8, 0u8, 251u8, 17u8, 138u8, 197u8, 164u8, 50u8, + 122u8, 0u8, 31u8, 238u8, 147u8, 213u8, 30u8, 132u8, 184u8, 215u8, + ], + ) + } + } + } + } + pub mod polkadot_xcm { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Send { + pub dest: ::std::boxed::Box, + pub message: ::std::boxed::Box, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct TeleportAssets { + pub dest: ::std::boxed::Box, + pub beneficiary: ::std::boxed::Box, + pub assets: ::std::boxed::Box, + pub fee_asset_item: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ReserveTransferAssets { + pub dest: ::std::boxed::Box, + pub beneficiary: ::std::boxed::Box, + pub assets: ::std::boxed::Box, + pub fee_asset_item: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Execute { + pub message: ::std::boxed::Box, + pub max_weight: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ForceXcmVersion { + pub location: + ::std::boxed::Box, + pub xcm_version: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ForceDefaultXcmVersion { + pub maybe_xcm_version: ::core::option::Option<::core::primitive::u32>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ForceSubscribeVersionNotify { + pub location: ::std::boxed::Box, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ForceUnsubscribeVersionNotify { + pub location: ::std::boxed::Box, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct LimitedReserveTransferAssets { + pub dest: ::std::boxed::Box, + pub beneficiary: ::std::boxed::Box, + pub assets: ::std::boxed::Box, + pub fee_asset_item: ::core::primitive::u32, + pub weight_limit: runtime_types::xcm::v3::WeightLimit, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct LimitedTeleportAssets { + pub dest: ::std::boxed::Box, + pub beneficiary: ::std::boxed::Box, + pub assets: ::std::boxed::Box, + pub fee_asset_item: ::core::primitive::u32, + pub weight_limit: runtime_types::xcm::v3::WeightLimit, + } + pub struct TransactionApi; + impl TransactionApi { + pub fn send( + &self, + dest: runtime_types::xcm::VersionedMultiLocation, + message: runtime_types::xcm::VersionedXcm, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "send", + Send { + dest: ::std::boxed::Box::new(dest), + message: ::std::boxed::Box::new(message), + }, + [ + 246u8, 35u8, 227u8, 112u8, 223u8, 7u8, 44u8, 186u8, 60u8, 225u8, 153u8, + 249u8, 104u8, 51u8, 123u8, 227u8, 143u8, 65u8, 232u8, 209u8, 178u8, + 104u8, 70u8, 56u8, 230u8, 14u8, 75u8, 83u8, 250u8, 160u8, 9u8, 39u8, + ], + ) + } + #[doc = "Teleport some assets from the local chain to some destination chain."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,"] + #[doc = "with all fees taken as needed from the asset."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the"] + #[doc = " `dest` side. May not be empty."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + pub fn teleport_assets( + &self, + dest: runtime_types::xcm::VersionedMultiLocation, + beneficiary: runtime_types::xcm::VersionedMultiLocation, + assets: runtime_types::xcm::VersionedMultiAssets, + fee_asset_item: ::core::primitive::u32, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "teleport_assets", + TeleportAssets { + dest: ::std::boxed::Box::new(dest), + beneficiary: ::std::boxed::Box::new(beneficiary), + assets: ::std::boxed::Box::new(assets), + fee_asset_item, + }, + [ + 187u8, 42u8, 2u8, 96u8, 105u8, 125u8, 74u8, 53u8, 2u8, 21u8, 31u8, + 160u8, 201u8, 197u8, 157u8, 190u8, 40u8, 145u8, 5u8, 99u8, 194u8, 41u8, + 114u8, 60u8, 165u8, 186u8, 15u8, 226u8, 85u8, 113u8, 159u8, 136u8, + ], + ) + } + #[doc = "Transfer some assets from the local chain to the sovereign account of a destination"] + #[doc = "chain and forward a notification XCM."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,"] + #[doc = "with all fees taken as needed from the asset."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the"] + #[doc = " `dest` side."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + pub fn reserve_transfer_assets( + &self, + dest: runtime_types::xcm::VersionedMultiLocation, + beneficiary: runtime_types::xcm::VersionedMultiLocation, + assets: runtime_types::xcm::VersionedMultiAssets, + fee_asset_item: ::core::primitive::u32, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "reserve_transfer_assets", + ReserveTransferAssets { + dest: ::std::boxed::Box::new(dest), + beneficiary: ::std::boxed::Box::new(beneficiary), + assets: ::std::boxed::Box::new(assets), + fee_asset_item, + }, + [ + 249u8, 177u8, 76u8, 204u8, 186u8, 165u8, 16u8, 186u8, 129u8, 239u8, + 65u8, 252u8, 9u8, 132u8, 32u8, 164u8, 117u8, 177u8, 40u8, 21u8, 196u8, + 246u8, 147u8, 2u8, 95u8, 110u8, 68u8, 162u8, 148u8, 9u8, 59u8, 170u8, + ], + ) + } + #[doc = "Execute an XCM message from a local, signed, origin."] + #[doc = ""] + #[doc = "An event is deposited indicating whether `msg` could be executed completely or only"] + #[doc = "partially."] + #[doc = ""] + #[doc = "No more than `max_weight` will be used in its attempted execution. If this is less than the"] + #[doc = "maximum amount of weight that the message could take to be executed, then no execution"] + #[doc = "attempt will be made."] + #[doc = ""] + #[doc = "NOTE: A successful return to this does *not* imply that the `msg` was executed successfully"] + #[doc = "to completion; only that *some* of it was executed."] + pub fn execute( + &self, + message: runtime_types::xcm::VersionedXcm, + max_weight: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "execute", + Execute { message: ::std::boxed::Box::new(message), max_weight }, + [ + 102u8, 41u8, 146u8, 29u8, 241u8, 205u8, 95u8, 153u8, 228u8, 141u8, + 11u8, 228u8, 13u8, 44u8, 75u8, 204u8, 174u8, 35u8, 155u8, 104u8, 204u8, + 82u8, 239u8, 98u8, 249u8, 187u8, 193u8, 1u8, 122u8, 88u8, 162u8, 200u8, + ], + ) + } + #[doc = "Extoll that a particular destination can be communicated with through a particular"] + #[doc = "version of XCM."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `location`: The destination that is being described."] + #[doc = "- `xcm_version`: The latest version of XCM that `location` supports."] + pub fn force_xcm_version( + &self, + location: runtime_types::xcm::v3::multilocation::MultiLocation, + xcm_version: ::core::primitive::u32, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "force_xcm_version", + ForceXcmVersion { location: ::std::boxed::Box::new(location), xcm_version }, + [ + 68u8, 48u8, 95u8, 61u8, 152u8, 95u8, 213u8, 126u8, 209u8, 176u8, 230u8, + 160u8, 164u8, 42u8, 128u8, 62u8, 175u8, 3u8, 161u8, 170u8, 20u8, 31u8, + 216u8, 122u8, 31u8, 77u8, 64u8, 182u8, 121u8, 41u8, 23u8, 80u8, + ], + ) + } + #[doc = "Set a safe XCM version (the version that XCM should be encoded with if the most recent"] + #[doc = "version a destination can accept is unknown)."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `maybe_xcm_version`: The default XCM encoding version, or `None` to disable."] + pub fn force_default_xcm_version( + &self, + maybe_xcm_version: ::core::option::Option<::core::primitive::u32>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "force_default_xcm_version", + ForceDefaultXcmVersion { maybe_xcm_version }, + [ + 38u8, 36u8, 59u8, 231u8, 18u8, 79u8, 76u8, 9u8, 200u8, 125u8, 214u8, + 166u8, 37u8, 99u8, 111u8, 161u8, 135u8, 2u8, 133u8, 157u8, 165u8, 18u8, + 152u8, 81u8, 209u8, 255u8, 137u8, 237u8, 28u8, 126u8, 224u8, 141u8, + ], + ) + } + #[doc = "Ask a location to notify us regarding their XCM version and any changes to it."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `location`: The location to which we should subscribe for XCM version notifications."] + pub fn force_subscribe_version_notify( + &self, + location: runtime_types::xcm::VersionedMultiLocation, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "force_subscribe_version_notify", + ForceSubscribeVersionNotify { location: ::std::boxed::Box::new(location) }, + [ + 236u8, 37u8, 153u8, 26u8, 174u8, 187u8, 154u8, 38u8, 179u8, 223u8, + 130u8, 32u8, 128u8, 30u8, 148u8, 229u8, 7u8, 185u8, 174u8, 9u8, 96u8, + 215u8, 189u8, 178u8, 148u8, 141u8, 249u8, 118u8, 7u8, 238u8, 1u8, 49u8, + ], + ) + } + #[doc = "Require that a particular destination should no longer notify us regarding any XCM"] + #[doc = "version changes."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `location`: The location to which we are currently subscribed for XCM version"] + #[doc = " notifications which we no longer desire."] + pub fn force_unsubscribe_version_notify( + &self, + location: runtime_types::xcm::VersionedMultiLocation, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "force_unsubscribe_version_notify", + ForceUnsubscribeVersionNotify { + location: ::std::boxed::Box::new(location), + }, + [ + 154u8, 169u8, 145u8, 211u8, 185u8, 71u8, 9u8, 63u8, 3u8, 158u8, 187u8, + 173u8, 115u8, 166u8, 100u8, 66u8, 12u8, 40u8, 198u8, 40u8, 213u8, + 104u8, 95u8, 183u8, 215u8, 53u8, 94u8, 158u8, 106u8, 56u8, 149u8, 52u8, + ], + ) + } + #[doc = "Transfer some assets from the local chain to the sovereign account of a destination"] + #[doc = "chain and forward a notification XCM."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight"] + #[doc = "is needed than `weight_limit`, then the operation will fail and the assets send may be"] + #[doc = "at risk."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the"] + #[doc = " `dest` side."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + #[doc = "- `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase."] + pub fn limited_reserve_transfer_assets( + &self, + dest: runtime_types::xcm::VersionedMultiLocation, + beneficiary: runtime_types::xcm::VersionedMultiLocation, + assets: runtime_types::xcm::VersionedMultiAssets, + fee_asset_item: ::core::primitive::u32, + weight_limit: runtime_types::xcm::v3::WeightLimit, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "limited_reserve_transfer_assets", + LimitedReserveTransferAssets { + dest: ::std::boxed::Box::new(dest), + beneficiary: ::std::boxed::Box::new(beneficiary), + assets: ::std::boxed::Box::new(assets), + fee_asset_item, + weight_limit, + }, + [ + 131u8, 191u8, 89u8, 27u8, 236u8, 142u8, 130u8, 129u8, 245u8, 95u8, + 159u8, 96u8, 252u8, 80u8, 28u8, 40u8, 128u8, 55u8, 41u8, 123u8, 22u8, + 18u8, 0u8, 236u8, 77u8, 68u8, 135u8, 181u8, 40u8, 47u8, 92u8, 240u8, + ], + ) + } + #[doc = "Teleport some assets from the local chain to some destination chain."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight"] + #[doc = "is needed than `weight_limit`, then the operation will fail and the assets send may be"] + #[doc = "at risk."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the"] + #[doc = " `dest` side. May not be empty."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + #[doc = "- `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase."] + pub fn limited_teleport_assets( + &self, + dest: runtime_types::xcm::VersionedMultiLocation, + beneficiary: runtime_types::xcm::VersionedMultiLocation, + assets: runtime_types::xcm::VersionedMultiAssets, + fee_asset_item: ::core::primitive::u32, + weight_limit: runtime_types::xcm::v3::WeightLimit, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "PolkadotXcm", + "limited_teleport_assets", + LimitedTeleportAssets { + dest: ::std::boxed::Box::new(dest), + beneficiary: ::std::boxed::Box::new(beneficiary), + assets: ::std::boxed::Box::new(assets), + fee_asset_item, + weight_limit, + }, + [ + 234u8, 19u8, 104u8, 174u8, 98u8, 159u8, 205u8, 110u8, 240u8, 78u8, + 186u8, 138u8, 236u8, 116u8, 104u8, 215u8, 57u8, 178u8, 166u8, 208u8, + 197u8, 113u8, 101u8, 56u8, 23u8, 56u8, 84u8, 14u8, 173u8, 70u8, 211u8, + 201u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::pallet_xcm::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Execution of an XCM message was attempted."] + #[doc = ""] + #[doc = "\\[ outcome \\]"] + pub struct Attempted(pub runtime_types::xcm::v3::traits::Outcome); + impl ::subxt::events::StaticEvent for Attempted { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "Attempted"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A XCM message was sent."] + #[doc = ""] + #[doc = "\\[ origin, destination, message \\]"] + pub struct Sent( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::v3::Xcm, + ); + impl ::subxt::events::StaticEvent for Sent { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "Sent"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Query response received which does not match a registered query. This may be because a"] + #[doc = "matching query was never registered, it may be because it is a duplicate response, or"] + #[doc = "because the query timed out."] + #[doc = ""] + #[doc = "\\[ origin location, id \\]"] + pub struct UnexpectedResponse( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u64, + ); + impl ::subxt::events::StaticEvent for UnexpectedResponse { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "UnexpectedResponse"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Query response has been received and is ready for taking with `take_response`. There is"] + #[doc = "no registered notification call."] + #[doc = ""] + #[doc = "\\[ id, response \\]"] + pub struct ResponseReady( + pub ::core::primitive::u64, + pub runtime_types::xcm::v3::Response, + ); + impl ::subxt::events::StaticEvent for ResponseReady { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "ResponseReady"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Query response has been received and query is removed. The registered notification has"] + #[doc = "been dispatched and executed successfully."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index \\]"] + pub struct Notified( + pub ::core::primitive::u64, + pub ::core::primitive::u8, + pub ::core::primitive::u8, + ); + impl ::subxt::events::StaticEvent for Notified { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "Notified"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Query response has been received and query is removed. The registered notification could"] + #[doc = "not be dispatched because the dispatch weight is greater than the maximum weight"] + #[doc = "originally budgeted by this runtime for the query result."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index, actual weight, max budgeted weight \\]"] + pub struct NotifyOverweight( + pub ::core::primitive::u64, + pub ::core::primitive::u8, + pub ::core::primitive::u8, + pub ::sp_weights::Weight, + pub ::sp_weights::Weight, + ); + impl ::subxt::events::StaticEvent for NotifyOverweight { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "NotifyOverweight"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Query response has been received and query is removed. There was a general error with"] + #[doc = "dispatching the notification call."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index \\]"] + pub struct NotifyDispatchError( + pub ::core::primitive::u64, + pub ::core::primitive::u8, + pub ::core::primitive::u8, + ); + impl ::subxt::events::StaticEvent for NotifyDispatchError { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "NotifyDispatchError"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Query response has been received and query is removed. The dispatch was unable to be"] + #[doc = "decoded into a `Call`; this might be due to dispatch function having a signature which"] + #[doc = "is not `(origin, QueryId, Response)`."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index \\]"] + pub struct NotifyDecodeFailed( + pub ::core::primitive::u64, + pub ::core::primitive::u8, + pub ::core::primitive::u8, + ); + impl ::subxt::events::StaticEvent for NotifyDecodeFailed { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "NotifyDecodeFailed"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Expected query response has been received but the origin location of the response does"] + #[doc = "not match that expected. The query remains registered for a later, valid, response to"] + #[doc = "be received and acted upon."] + #[doc = ""] + #[doc = "\\[ origin location, id, expected location \\]"] + pub struct InvalidResponder( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u64, + pub ::core::option::Option, + ); + impl ::subxt::events::StaticEvent for InvalidResponder { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "InvalidResponder"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Expected query response has been received but the expected origin location placed in"] + #[doc = "storage by this runtime previously cannot be decoded. The query remains registered."] + #[doc = ""] + #[doc = "This is unexpected (since a location placed in storage in a previously executing"] + #[doc = "runtime should be readable prior to query timeout) and dangerous since the possibly"] + #[doc = "valid response will be dropped. Manual governance intervention is probably going to be"] + #[doc = "needed."] + #[doc = ""] + #[doc = "\\[ origin location, id \\]"] + pub struct InvalidResponderVersion( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u64, + ); + impl ::subxt::events::StaticEvent for InvalidResponderVersion { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "InvalidResponderVersion"; + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Received query response has been read and removed."] + #[doc = ""] + #[doc = "\\[ id \\]"] + pub struct ResponseTaken(pub ::core::primitive::u64); + impl ::subxt::events::StaticEvent for ResponseTaken { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "ResponseTaken"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some assets have been placed in an asset trap."] + #[doc = ""] + #[doc = "\\[ hash, origin, assets \\]"] + pub struct AssetsTrapped( + pub ::subxt::utils::H256, + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::VersionedMultiAssets, + ); + impl ::subxt::events::StaticEvent for AssetsTrapped { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "AssetsTrapped"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "An XCM version change notification message has been attempted to be sent."] + #[doc = ""] + #[doc = "The cost of sending it (borne by the chain) is included."] + #[doc = ""] + #[doc = "\\[ destination, result, cost \\]"] + pub struct VersionChangeNotified( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u32, + pub runtime_types::xcm::v3::multiasset::MultiAssets, + ); + impl ::subxt::events::StaticEvent for VersionChangeNotified { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "VersionChangeNotified"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "The supported version of a location has been changed. This might be through an"] + #[doc = "automatic notification or a manual intervention."] + #[doc = ""] + #[doc = "\\[ location, XCM version \\]"] + pub struct SupportedVersionChanged( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u32, + ); + impl ::subxt::events::StaticEvent for SupportedVersionChanged { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "SupportedVersionChanged"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A given location which had a version change subscription was dropped owing to an error"] + #[doc = "sending the notification to it."] + #[doc = ""] + #[doc = "\\[ location, query ID, error \\]"] + pub struct NotifyTargetSendFail( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u64, + pub runtime_types::xcm::v3::traits::Error, + ); + impl ::subxt::events::StaticEvent for NotifyTargetSendFail { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "NotifyTargetSendFail"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A given location which had a version change subscription was dropped owing to an error"] + #[doc = "migrating the location to our new XCM format."] + #[doc = ""] + #[doc = "\\[ location, query ID \\]"] + pub struct NotifyTargetMigrationFail( + pub runtime_types::xcm::VersionedMultiLocation, + pub ::core::primitive::u64, + ); + impl ::subxt::events::StaticEvent for NotifyTargetMigrationFail { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "NotifyTargetMigrationFail"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Expected query response has been received but the expected querier location placed in"] + #[doc = "storage by this runtime previously cannot be decoded. The query remains registered."] + #[doc = ""] + #[doc = "This is unexpected (since a location placed in storage in a previously executing"] + #[doc = "runtime should be readable prior to query timeout) and dangerous since the possibly"] + #[doc = "valid response will be dropped. Manual governance intervention is probably going to be"] + #[doc = "needed."] + #[doc = ""] + #[doc = "\\[ origin location, id \\]"] + pub struct InvalidQuerierVersion( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u64, + ); + impl ::subxt::events::StaticEvent for InvalidQuerierVersion { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "InvalidQuerierVersion"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Expected query response has been received but the querier location of the response does"] + #[doc = "not match the expected. The query remains registered for a later, valid, response to"] + #[doc = "be received and acted upon."] + #[doc = ""] + #[doc = "\\[ origin location, id, expected querier, maybe actual querier \\]"] + pub struct InvalidQuerier( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::primitive::u64, + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub ::core::option::Option, + ); + impl ::subxt::events::StaticEvent for InvalidQuerier { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "InvalidQuerier"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "A remote has requested XCM version change notification from us and we have honored it."] + #[doc = "A version information message is sent to them and its cost is included."] + #[doc = ""] + #[doc = "\\[ destination location, cost \\]"] + pub struct VersionNotifyStarted( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::v3::multiasset::MultiAssets, + ); + impl ::subxt::events::StaticEvent for VersionNotifyStarted { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "VersionNotifyStarted"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "We have requested that a remote chain sends us XCM version change notifications."] + #[doc = ""] + #[doc = "\\[ destination location, cost \\]"] + pub struct VersionNotifyRequested( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::v3::multiasset::MultiAssets, + ); + impl ::subxt::events::StaticEvent for VersionNotifyRequested { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "VersionNotifyRequested"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "We have requested that a remote chain stops sending us XCM version change notifications."] + #[doc = ""] + #[doc = "\\[ destination location, cost \\]"] + pub struct VersionNotifyUnrequested( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::v3::multiasset::MultiAssets, + ); + impl ::subxt::events::StaticEvent for VersionNotifyUnrequested { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "VersionNotifyUnrequested"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Fees were paid from a location for an operation (often for using `SendXcm`)."] + #[doc = ""] + #[doc = "\\[ paying location, fees \\]"] + pub struct FeesPaid( + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::v3::multiasset::MultiAssets, + ); + impl ::subxt::events::StaticEvent for FeesPaid { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "FeesPaid"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Some assets have been claimed from an asset trap"] + #[doc = ""] + #[doc = "\\[ hash, origin, assets \\]"] + pub struct AssetsClaimed( + pub ::subxt::utils::H256, + pub runtime_types::xcm::v3::multilocation::MultiLocation, + pub runtime_types::xcm::VersionedMultiAssets, + ); + impl ::subxt::events::StaticEvent for AssetsClaimed { + const PALLET: &'static str = "PolkadotXcm"; + const EVENT: &'static str = "AssetsClaimed"; + } + } + } + pub mod cumulus_xcm { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + pub struct TransactionApi; + impl TransactionApi {} + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::cumulus_pallet_xcm::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message is invalid XCM."] + #[doc = "\\[ id \\]"] + pub struct InvalidFormat(pub [::core::primitive::u8; 32usize]); + impl ::subxt::events::StaticEvent for InvalidFormat { + const PALLET: &'static str = "CumulusXcm"; + const EVENT: &'static str = "InvalidFormat"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message is unsupported version of XCM."] + #[doc = "\\[ id \\]"] + pub struct UnsupportedVersion(pub [::core::primitive::u8; 32usize]); + impl ::subxt::events::StaticEvent for UnsupportedVersion { + const PALLET: &'static str = "CumulusXcm"; + const EVENT: &'static str = "UnsupportedVersion"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message executed with the given outcome."] + #[doc = "\\[ id, outcome \\]"] + pub struct ExecutedDownward( + pub [::core::primitive::u8; 32usize], + pub runtime_types::xcm::v3::traits::Outcome, + ); + impl ::subxt::events::StaticEvent for ExecutedDownward { + const PALLET: &'static str = "CumulusXcm"; + const EVENT: &'static str = "ExecutedDownward"; + } + } + } + pub mod dmp_queue { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ServiceOverweight { + pub index: ::core::primitive::u64, + pub weight_limit: ::sp_weights::Weight, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Service a single overweight message."] + pub fn service_overweight( + &self, + index: ::core::primitive::u64, + weight_limit: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "DmpQueue", + "service_overweight", + ServiceOverweight { index, weight_limit }, + [ + 121u8, 236u8, 235u8, 23u8, 210u8, 238u8, 238u8, 122u8, 15u8, 86u8, + 34u8, 119u8, 105u8, 100u8, 214u8, 236u8, 117u8, 39u8, 254u8, 235u8, + 189u8, 15u8, 72u8, 74u8, 225u8, 134u8, 148u8, 126u8, 31u8, 203u8, + 144u8, 106u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::cumulus_pallet_dmp_queue::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message is invalid XCM."] + pub struct InvalidFormat { + pub message_id: [::core::primitive::u8; 32usize], + } + impl ::subxt::events::StaticEvent for InvalidFormat { + const PALLET: &'static str = "DmpQueue"; + const EVENT: &'static str = "InvalidFormat"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message is unsupported version of XCM."] + pub struct UnsupportedVersion { + pub message_id: [::core::primitive::u8; 32usize], + } + impl ::subxt::events::StaticEvent for UnsupportedVersion { + const PALLET: &'static str = "DmpQueue"; + const EVENT: &'static str = "UnsupportedVersion"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message executed with the given outcome."] + pub struct ExecutedDownward { + pub message_id: [::core::primitive::u8; 32usize], + pub outcome: runtime_types::xcm::v3::traits::Outcome, + } + impl ::subxt::events::StaticEvent for ExecutedDownward { + const PALLET: &'static str = "DmpQueue"; + const EVENT: &'static str = "ExecutedDownward"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "The weight limit for handling downward messages was reached."] + pub struct WeightExhausted { + pub message_id: [::core::primitive::u8; 32usize], + pub remaining_weight: ::sp_weights::Weight, + pub required_weight: ::sp_weights::Weight, + } + impl ::subxt::events::StaticEvent for WeightExhausted { + const PALLET: &'static str = "DmpQueue"; + const EVENT: &'static str = "WeightExhausted"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message is overweight and was placed in the overweight queue."] + pub struct OverweightEnqueued { + pub message_id: [::core::primitive::u8; 32usize], + pub overweight_index: ::core::primitive::u64, + pub required_weight: ::sp_weights::Weight, + } + impl ::subxt::events::StaticEvent for OverweightEnqueued { + const PALLET: &'static str = "DmpQueue"; + const EVENT: &'static str = "OverweightEnqueued"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Downward message from the overweight queue was executed."] + pub struct OverweightServiced { + pub overweight_index: ::core::primitive::u64, + pub weight_used: ::sp_weights::Weight, + } + impl ::subxt::events::StaticEvent for OverweightServiced { + const PALLET: &'static str = "DmpQueue"; + const EVENT: &'static str = "OverweightServiced"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "The maximum number of downward messages was."] + pub struct MaxMessagesExhausted { + pub message_id: [::core::primitive::u8; 32usize], + } + impl ::subxt::events::StaticEvent for MaxMessagesExhausted { + const PALLET: &'static str = "DmpQueue"; + const EVENT: &'static str = "MaxMessagesExhausted"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " The configuration."] + pub fn configuration( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::cumulus_pallet_dmp_queue::ConfigData, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "DmpQueue", + "Configuration", + vec![], + [ + 133u8, 113u8, 115u8, 164u8, 128u8, 145u8, 234u8, 106u8, 150u8, 54u8, + 247u8, 135u8, 181u8, 197u8, 178u8, 30u8, 204u8, 46u8, 6u8, 137u8, 82u8, + 1u8, 75u8, 171u8, 7u8, 157u8, 3u8, 19u8, 92u8, 10u8, 234u8, 66u8, + ], + ) + } + #[doc = " The page index."] + pub fn page_index( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::cumulus_pallet_dmp_queue::PageIndexData, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "DmpQueue", + "PageIndex", + vec![], + [ + 94u8, 132u8, 34u8, 67u8, 10u8, 22u8, 235u8, 96u8, 168u8, 26u8, 57u8, + 200u8, 130u8, 218u8, 37u8, 71u8, 28u8, 119u8, 78u8, 107u8, 209u8, + 120u8, 190u8, 2u8, 101u8, 215u8, 122u8, 187u8, 94u8, 38u8, 255u8, + 234u8, + ], + ) + } + #[doc = " The queue pages."] + pub fn pages( + &self, + _0: impl ::std::borrow::Borrow<::core::primitive::u32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec<( + ::core::primitive::u32, + ::std::vec::Vec<::core::primitive::u8>, + )>, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "DmpQueue", + "Pages", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 228u8, 86u8, 33u8, 107u8, 248u8, 4u8, 223u8, 175u8, 222u8, 25u8, 204u8, + 42u8, 235u8, 21u8, 215u8, 91u8, 167u8, 14u8, 133u8, 151u8, 190u8, 57u8, + 138u8, 208u8, 79u8, 244u8, 132u8, 14u8, 48u8, 247u8, 171u8, 108u8, + ], + ) + } + #[doc = " The queue pages."] + pub fn pages_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + ::std::vec::Vec<( + ::core::primitive::u32, + ::std::vec::Vec<::core::primitive::u8>, + )>, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "DmpQueue", + "Pages", + Vec::new(), + [ + 228u8, 86u8, 33u8, 107u8, 248u8, 4u8, 223u8, 175u8, 222u8, 25u8, 204u8, + 42u8, 235u8, 21u8, 215u8, 91u8, 167u8, 14u8, 133u8, 151u8, 190u8, 57u8, + 138u8, 208u8, 79u8, 244u8, 132u8, 14u8, 48u8, 247u8, 171u8, 108u8, + ], + ) + } + #[doc = " The overweight messages."] + pub fn overweight( + &self, + _0: impl ::std::borrow::Borrow<::core::primitive::u64>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<( + ::core::primitive::u32, + ::std::vec::Vec<::core::primitive::u8>, + )>, + ::subxt::storage::address::Yes, + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "DmpQueue", + "Overweight", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 222u8, 85u8, 143u8, 49u8, 42u8, 248u8, 138u8, 163u8, 46u8, 199u8, + 188u8, 61u8, 137u8, 135u8, 127u8, 146u8, 210u8, 254u8, 121u8, 42u8, + 112u8, 114u8, 22u8, 228u8, 207u8, 207u8, 245u8, 175u8, 152u8, 140u8, + 225u8, 237u8, + ], + ) + } + #[doc = " The overweight messages."] + pub fn overweight_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<( + ::core::primitive::u32, + ::std::vec::Vec<::core::primitive::u8>, + )>, + (), + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "DmpQueue", + "Overweight", + Vec::new(), + [ + 222u8, 85u8, 143u8, 49u8, 42u8, 248u8, 138u8, 163u8, 46u8, 199u8, + 188u8, 61u8, 137u8, 135u8, 127u8, 146u8, 210u8, 254u8, 121u8, 42u8, + 112u8, 114u8, 22u8, 228u8, 207u8, 207u8, 245u8, 175u8, 152u8, 140u8, + 225u8, 237u8, + ], + ) + } + #[doc = "Counter for the related counted storage map"] + pub fn counter_for_overweight( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "DmpQueue", + "CounterForOverweight", + vec![], + [ + 148u8, 226u8, 248u8, 107u8, 165u8, 97u8, 218u8, 160u8, 127u8, 48u8, + 185u8, 251u8, 35u8, 137u8, 119u8, 251u8, 151u8, 167u8, 189u8, 66u8, + 80u8, 74u8, 134u8, 129u8, 222u8, 180u8, 51u8, 182u8, 50u8, 110u8, 10u8, + 43u8, + ], + ) + } + } + } + } + pub mod bridge_relayers { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ClaimRewards { + pub rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Claim accumulated rewards."] + pub fn claim_rewards( + &self, + rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeRelayers", + "claim_rewards", + ClaimRewards { rewards_account_params }, + [ + 141u8, 52u8, 193u8, 42u8, 145u8, 26u8, 147u8, 35u8, 227u8, 221u8, + 221u8, 188u8, 104u8, 186u8, 123u8, 46u8, 190u8, 236u8, 120u8, 19u8, + 230u8, 219u8, 238u8, 227u8, 75u8, 35u8, 36u8, 177u8, 227u8, 130u8, + 103u8, 128u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::pallet_bridge_relayers::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Reward has been paid to the relayer."] + pub struct RewardPaid { + pub relayer: ::sp_core::crypto::AccountId32, + pub rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, + pub reward: ::core::primitive::u128, + } + impl ::subxt::events::StaticEvent for RewardPaid { + const PALLET: &'static str = "BridgeRelayers"; + const EVENT: &'static str = "RewardPaid"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " Map of the relayer => accumulated reward."] + pub fn relayer_rewards( + &self, + _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, + _1: impl ::std::borrow::Borrow, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, + ::subxt::storage::address::Yes, + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeRelayers", + "RelayerRewards", + vec![ + ::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + ), + ::subxt::storage::address::StorageMapKey::new( + _1.borrow(), + ::subxt::storage::address::StorageHasher::Identity, + ), + ], + [ + 116u8, 81u8, 48u8, 55u8, 199u8, 26u8, 100u8, 7u8, 177u8, 230u8, 132u8, + 248u8, 221u8, 90u8, 33u8, 155u8, 198u8, 216u8, 43u8, 149u8, 92u8, + 100u8, 199u8, 183u8, 150u8, 214u8, 199u8, 222u8, 224u8, 228u8, 238u8, + 108u8, + ], + ) + } + #[doc = " Map of the relayer => accumulated reward."] + pub fn relayer_rewards_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, + (), + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeRelayers", + "RelayerRewards", + Vec::new(), + [ + 116u8, 81u8, 48u8, 55u8, 199u8, 26u8, 100u8, 7u8, 177u8, 230u8, 132u8, + 248u8, 221u8, 90u8, 33u8, 155u8, 198u8, 216u8, 43u8, 149u8, 92u8, + 100u8, 199u8, 183u8, 150u8, 214u8, 199u8, 222u8, 224u8, 228u8, 238u8, + 108u8, + ], + ) + } + } + } + } + pub mod bridge_millau_grandpa { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SubmitFinalityProof { + pub finality_target: ::std::boxed::Box< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + pub justification: ::bp_header_chain::justification::GrandpaJustification< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Initialize { + pub init_data: ::bp_header_chain::InitializationData< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetOwner { + pub new_owner: ::core::option::Option<::sp_core::crypto::AccountId32>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetOperatingMode { + pub operating_mode: runtime_types::bp_runtime::BasicOperatingMode, + } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Verify a target header is finalized according to the given finality proof."] + #[doc = ""] + #[doc = "It will use the underlying storage pallet to fetch information about the current"] + #[doc = "authorities and best finalized header in order to verify that the header is finalized."] + #[doc = ""] + #[doc = "If successful in verification, it will write the target header to the underlying storage"] + #[doc = "pallet."] + pub fn submit_finality_proof( + &self, + finality_target: ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + justification: ::bp_header_chain::justification::GrandpaJustification< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauGrandpa", + "submit_finality_proof", + SubmitFinalityProof { + finality_target: ::std::boxed::Box::new(finality_target), + justification, + }, + [ + 3u8, 161u8, 243u8, 208u8, 245u8, 135u8, 86u8, 233u8, 103u8, 140u8, + 81u8, 3u8, 119u8, 185u8, 68u8, 167u8, 208u8, 155u8, 169u8, 201u8, + 209u8, 248u8, 162u8, 45u8, 155u8, 225u8, 173u8, 62u8, 156u8, 171u8, + 19u8, 190u8, + ], + ) + } + #[doc = "Bootstrap the bridge pallet with an initial header and authority set from which to sync."] + #[doc = ""] + #[doc = "The initial configuration provided does not need to be the genesis header of the bridged"] + #[doc = "chain, it can be any arbitrary header. You can also provide the next scheduled set"] + #[doc = "change if it is already know."] + #[doc = ""] + #[doc = "This function is only allowed to be called from a trusted origin and writes to storage"] + #[doc = "with practically no checks in terms of the validity of the data. It is important that"] + #[doc = "you ensure that valid data is being passed in."] + pub fn initialize( + &self, + init_data: ::bp_header_chain::InitializationData< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauGrandpa", + "initialize", + Initialize { init_data }, + [ + 244u8, 188u8, 202u8, 145u8, 218u8, 91u8, 74u8, 80u8, 41u8, 185u8, + 239u8, 178u8, 231u8, 128u8, 198u8, 90u8, 135u8, 219u8, 200u8, 23u8, + 194u8, 47u8, 61u8, 222u8, 194u8, 84u8, 142u8, 37u8, 64u8, 37u8, 69u8, + 198u8, + ], + ) + } + #[doc = "Change `PalletOwner`."] + #[doc = ""] + #[doc = "May only be called either by root, or by `PalletOwner`."] + pub fn set_owner( + &self, + new_owner: ::core::option::Option<::sp_core::crypto::AccountId32>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauGrandpa", + "set_owner", + SetOwner { new_owner }, + [ + 100u8, 221u8, 84u8, 142u8, 158u8, 5u8, 47u8, 212u8, 9u8, 35u8, 82u8, + 135u8, 108u8, 238u8, 231u8, 197u8, 77u8, 219u8, 176u8, 222u8, 88u8, + 167u8, 152u8, 34u8, 177u8, 244u8, 160u8, 195u8, 211u8, 3u8, 66u8, + 253u8, + ], + ) + } + #[doc = "Halt or resume all pallet operations."] + #[doc = ""] + #[doc = "May only be called either by root, or by `PalletOwner`."] + pub fn set_operating_mode( + &self, + operating_mode: runtime_types::bp_runtime::BasicOperatingMode, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauGrandpa", + "set_operating_mode", + SetOperatingMode { operating_mode }, + [ + 128u8, 25u8, 81u8, 145u8, 111u8, 185u8, 226u8, 152u8, 18u8, 51u8, 89u8, + 236u8, 200u8, 157u8, 157u8, 186u8, 207u8, 208u8, 152u8, 168u8, 12u8, + 39u8, 249u8, 48u8, 195u8, 160u8, 54u8, 73u8, 30u8, 230u8, 25u8, 46u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::pallet_bridge_grandpa::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Best finalized chain header has been updated to the header with given number and hash."] + pub struct UpdatedBestFinalizedHeader { + pub number: ::core::primitive::u64, + pub hash: ::bp_millau::MillauHash, + } + impl ::subxt::events::StaticEvent for UpdatedBestFinalizedHeader { + const PALLET: &'static str = "BridgeMillauGrandpa"; + const EVENT: &'static str = "UpdatedBestFinalizedHeader"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " The current number of requests which have written to storage."] + #[doc = ""] + #[doc = " If the `RequestCount` hits `MaxRequests`, no more calls will be allowed to the pallet until"] + #[doc = " the request capacity is increased."] + #[doc = ""] + #[doc = " The `RequestCount` is decreased by one at the beginning of every block. This is to ensure"] + #[doc = " that the pallet can always make progress."] + pub fn request_count( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "RequestCount", + vec![], + [ + 100u8, 156u8, 98u8, 176u8, 229u8, 85u8, 81u8, 159u8, 120u8, 156u8, + 33u8, 179u8, 224u8, 237u8, 52u8, 198u8, 81u8, 81u8, 10u8, 180u8, 53u8, + 141u8, 96u8, 4u8, 39u8, 217u8, 58u8, 9u8, 57u8, 79u8, 47u8, 201u8, + ], + ) + } + #[doc = " Hash of the header used to bootstrap the pallet."] + pub fn initial_hash( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::bp_millau::MillauHash>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "InitialHash", + vec![], + [ + 167u8, 131u8, 64u8, 215u8, 102u8, 70u8, 21u8, 34u8, 254u8, 233u8, 2u8, + 49u8, 253u8, 67u8, 235u8, 10u8, 21u8, 223u8, 220u8, 198u8, 180u8, + 137u8, 88u8, 251u8, 230u8, 108u8, 9u8, 104u8, 101u8, 105u8, 38u8, + 138u8, + ], + ) + } + #[doc = " Hash of the best finalized header."] + pub fn best_finalized( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_runtime::HeaderId< + ::bp_millau::MillauHash, + ::core::primitive::u64, + >, + >, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "BestFinalized", + vec![], + [ + 61u8, 173u8, 97u8, 223u8, 180u8, 235u8, 85u8, 126u8, 217u8, 228u8, + 87u8, 174u8, 116u8, 156u8, 162u8, 252u8, 100u8, 200u8, 138u8, 14u8, + 102u8, 177u8, 66u8, 164u8, 216u8, 114u8, 18u8, 223u8, 94u8, 78u8, + 107u8, 58u8, + ], + ) + } + #[doc = " A ring buffer of imported hashes. Ordered by the insertion time."] + pub fn imported_hashes( + &self, + _0: impl ::std::borrow::Borrow<::core::primitive::u32>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::bp_millau::MillauHash>, + ::subxt::storage::address::Yes, + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "ImportedHashes", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Identity, + )], + [ + 249u8, 185u8, 202u8, 36u8, 40u8, 197u8, 111u8, 168u8, 136u8, 138u8, + 84u8, 135u8, 69u8, 34u8, 181u8, 46u8, 158u8, 16u8, 150u8, 190u8, 81u8, + 53u8, 239u8, 199u8, 83u8, 93u8, 197u8, 205u8, 129u8, 173u8, 141u8, + 43u8, + ], + ) + } + #[doc = " A ring buffer of imported hashes. Ordered by the insertion time."] + pub fn imported_hashes_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::bp_millau::MillauHash>, + (), + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "ImportedHashes", + Vec::new(), + [ + 249u8, 185u8, 202u8, 36u8, 40u8, 197u8, 111u8, 168u8, 136u8, 138u8, + 84u8, 135u8, 69u8, 34u8, 181u8, 46u8, 158u8, 16u8, 150u8, 190u8, 81u8, + 53u8, 239u8, 199u8, 83u8, 93u8, 197u8, 205u8, 129u8, 173u8, 141u8, + 43u8, + ], + ) + } + #[doc = " Current ring buffer position."] + pub fn imported_hashes_pointer( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "ImportedHashesPointer", + vec![], + [ + 159u8, 83u8, 35u8, 45u8, 27u8, 249u8, 155u8, 131u8, 181u8, 196u8, + 224u8, 26u8, 92u8, 132u8, 127u8, 237u8, 13u8, 142u8, 196u8, 147u8, + 221u8, 216u8, 11u8, 78u8, 190u8, 241u8, 201u8, 96u8, 74u8, 185u8, + 208u8, 42u8, + ], + ) + } + #[doc = " Relevant fields of imported headers."] + pub fn imported_headers( + &self, + _0: impl ::std::borrow::Borrow<::bp_millau::MillauHash>, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_header_chain::StoredHeaderData< + ::core::primitive::u64, + ::bp_millau::MillauHash, + >, + >, + ::subxt::storage::address::Yes, + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "ImportedHeaders", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Identity, + )], + [ + 109u8, 41u8, 102u8, 223u8, 133u8, 169u8, 46u8, 221u8, 235u8, 67u8, + 28u8, 192u8, 2u8, 242u8, 215u8, 166u8, 227u8, 182u8, 136u8, 217u8, + 61u8, 10u8, 246u8, 70u8, 17u8, 246u8, 223u8, 113u8, 9u8, 136u8, 181u8, + 242u8, + ], + ) + } + #[doc = " Relevant fields of imported headers."] + pub fn imported_headers_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_header_chain::StoredHeaderData< + ::core::primitive::u64, + ::bp_millau::MillauHash, + >, + >, + (), + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "ImportedHeaders", + Vec::new(), + [ + 109u8, 41u8, 102u8, 223u8, 133u8, 169u8, 46u8, 221u8, 235u8, 67u8, + 28u8, 192u8, 2u8, 242u8, 215u8, 166u8, 227u8, 182u8, 136u8, 217u8, + 61u8, 10u8, 246u8, 70u8, 17u8, 246u8, 223u8, 113u8, 9u8, 136u8, 181u8, + 242u8, + ], + ) + } + #[doc = " The current GRANDPA Authority set."] + pub fn current_authority_set( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::pallet_bridge_grandpa::storage_types::StoredAuthoritySet, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "CurrentAuthoritySet", + vec![], + [ + 249u8, 40u8, 229u8, 120u8, 141u8, 219u8, 206u8, 5u8, 54u8, 121u8, + 207u8, 77u8, 8u8, 80u8, 105u8, 107u8, 151u8, 111u8, 82u8, 119u8, 8u8, + 31u8, 104u8, 82u8, 92u8, 156u8, 37u8, 160u8, 235u8, 64u8, 62u8, 94u8, + ], + ) + } + #[doc = " Optional pallet owner."] + #[doc = ""] + #[doc = " Pallet owner has a right to halt all pallet operations and then resume it. If it is"] + #[doc = " `None`, then there are no direct ways to halt/resume pallet operations, but other"] + #[doc = " runtime methods may still be used to do that (i.e. democracy::referendum to update halt"] + #[doc = " flag directly or call the `halt_operations`)."] + pub fn pallet_owner( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::sp_core::crypto::AccountId32>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "PalletOwner", + vec![], + [ + 89u8, 42u8, 74u8, 119u8, 21u8, 164u8, 30u8, 115u8, 207u8, 126u8, 98u8, + 16u8, 162u8, 214u8, 67u8, 172u8, 178u8, 223u8, 139u8, 121u8, 174u8, + 89u8, 215u8, 75u8, 200u8, 161u8, 61u8, 195u8, 65u8, 222u8, 246u8, + 233u8, + ], + ) + } + #[doc = " The current operating mode of the pallet."] + #[doc = ""] + #[doc = " Depending on the mode either all, or no transactions will be allowed."] + pub fn pallet_operating_mode( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_runtime::BasicOperatingMode, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauGrandpa", + "PalletOperatingMode", + vec![], + [ + 218u8, 66u8, 212u8, 71u8, 38u8, 152u8, 55u8, 129u8, 125u8, 231u8, 91u8, + 216u8, 157u8, 141u8, 173u8, 146u8, 30u8, 40u8, 132u8, 107u8, 97u8, + 39u8, 36u8, 81u8, 231u8, 222u8, 113u8, 136u8, 233u8, 212u8, 225u8, + 75u8, + ], + ) + } + } + } + pub mod constants { + use super::runtime_types; + pub struct ConstantsApi; + impl ConstantsApi { + #[doc = " The upper bound on the number of requests allowed by the pallet."] + #[doc = ""] + #[doc = " A request refers to an action which writes a header to storage."] + #[doc = ""] + #[doc = " Once this bound is reached the pallet will not allow any dispatchables to be called"] + #[doc = " until the request count has decreased."] + pub fn max_requests( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "BridgeMillauGrandpa", + "MaxRequests", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + #[doc = " Maximal number of finalized headers to keep in the storage."] + #[doc = ""] + #[doc = " The setting is there to prevent growing the on-chain state indefinitely. Note"] + #[doc = " the setting does not relate to block numbers - we will simply keep as much items"] + #[doc = " in the storage, so it doesn't guarantee any fixed timeframe for finality headers."] + #[doc = ""] + #[doc = " Incautious change of this constant may lead to orphan entries in the runtime storage."] + pub fn headers_to_keep( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "BridgeMillauGrandpa", + "HeadersToKeep", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + } + } + } + pub mod bridge_millau_messages { + use super::{root_mod, runtime_types}; + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub mod calls { + use super::{root_mod, runtime_types}; + type DispatchError = runtime_types::sp_runtime::DispatchError; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetOwner { + pub new_owner: ::core::option::Option<::sp_core::crypto::AccountId32>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SetOperatingMode { + pub operating_mode: runtime_types::bp_messages::MessagesOperatingMode, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ReceiveMessagesProof { + pub relayer_id_at_bridged_chain: ::sp_core::crypto::AccountId32, + pub proof: ::bridge_runtime_common::messages::target::FromBridgedChainMessagesProof< + ::bp_millau::MillauHash, + >, + pub messages_count: ::core::primitive::u32, + pub dispatch_weight: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ReceiveMessagesDeliveryProof { pub proof : :: bridge_runtime_common :: messages :: source :: FromBridgedChainMessagesDeliveryProof < :: bp_millau :: MillauHash > , pub relayers_state : :: bp_messages :: UnrewardedRelayersState , } + pub struct TransactionApi; + impl TransactionApi { + #[doc = "Change `PalletOwner`."] + #[doc = ""] + #[doc = "May only be called either by root, or by `PalletOwner`."] + pub fn set_owner( + &self, + new_owner: ::core::option::Option<::sp_core::crypto::AccountId32>, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauMessages", + "set_owner", + SetOwner { new_owner }, + [ + 100u8, 221u8, 84u8, 142u8, 158u8, 5u8, 47u8, 212u8, 9u8, 35u8, 82u8, + 135u8, 108u8, 238u8, 231u8, 197u8, 77u8, 219u8, 176u8, 222u8, 88u8, + 167u8, 152u8, 34u8, 177u8, 244u8, 160u8, 195u8, 211u8, 3u8, 66u8, + 253u8, + ], + ) + } + #[doc = "Halt or resume all/some pallet operations."] + #[doc = ""] + #[doc = "May only be called either by root, or by `PalletOwner`."] + pub fn set_operating_mode( + &self, + operating_mode: runtime_types::bp_messages::MessagesOperatingMode, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauMessages", + "set_operating_mode", + SetOperatingMode { operating_mode }, + [ + 236u8, 230u8, 127u8, 17u8, 145u8, 186u8, 102u8, 200u8, 227u8, 208u8, + 230u8, 121u8, 102u8, 199u8, 123u8, 118u8, 199u8, 160u8, 131u8, 116u8, + 102u8, 167u8, 119u8, 144u8, 70u8, 114u8, 0u8, 223u8, 54u8, 197u8, 39u8, + 58u8, + ], + ) + } + #[doc = "Receive messages proof from bridged chain."] + #[doc = ""] + #[doc = "The weight of the call assumes that the transaction always brings outbound lane"] + #[doc = "state update. Because of that, the submitter (relayer) has no benefit of not including"] + #[doc = "this data in the transaction, so reward confirmations lags should be minimal."] + pub fn receive_messages_proof( + &self, + relayer_id_at_bridged_chain: ::sp_core::crypto::AccountId32, + proof: ::bridge_runtime_common::messages::target::FromBridgedChainMessagesProof< + ::bp_millau::MillauHash, + >, + messages_count: ::core::primitive::u32, + dispatch_weight: ::sp_weights::Weight, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauMessages", + "receive_messages_proof", + ReceiveMessagesProof { + relayer_id_at_bridged_chain, + proof, + messages_count, + dispatch_weight, + }, + [ + 14u8, 23u8, 191u8, 242u8, 252u8, 163u8, 87u8, 149u8, 211u8, 25u8, + 112u8, 192u8, 206u8, 49u8, 135u8, 124u8, 79u8, 92u8, 204u8, 39u8, 80u8, + 139u8, 155u8, 124u8, 134u8, 1u8, 66u8, 68u8, 213u8, 85u8, 42u8, 250u8, + ], + ) + } + #[doc = "Receive messages delivery proof from bridged chain."] + pub fn receive_messages_delivery_proof( + &self, + proof : :: bridge_runtime_common :: messages :: source :: FromBridgedChainMessagesDeliveryProof < :: bp_millau :: MillauHash >, + relayers_state: ::bp_messages::UnrewardedRelayersState, + ) -> ::subxt::tx::StaticTxPayload { + ::subxt::tx::StaticTxPayload::new( + "BridgeMillauMessages", + "receive_messages_delivery_proof", + ReceiveMessagesDeliveryProof { proof, relayers_state }, + [ + 216u8, 113u8, 64u8, 20u8, 16u8, 64u8, 144u8, 143u8, 105u8, 30u8, 192u8, + 89u8, 74u8, 34u8, 56u8, 151u8, 20u8, 234u8, 14u8, 211u8, 64u8, 103u8, + 218u8, 164u8, 69u8, 107u8, 6u8, 119u8, 13u8, 90u8, 150u8, 24u8, + ], + ) + } + } + } + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub type Event = runtime_types::pallet_bridge_messages::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Message has been accepted and is waiting to be delivered."] + pub struct MessageAccepted { + pub lane_id: runtime_types::bp_messages::LaneId, + pub nonce: ::core::primitive::u64, + } + impl ::subxt::events::StaticEvent for MessageAccepted { + const PALLET: &'static str = "BridgeMillauMessages"; + const EVENT: &'static str = "MessageAccepted"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Messages have been received from the bridged chain."] + pub struct MessagesReceived (pub :: std :: vec :: Vec < runtime_types :: bp_messages :: ReceivedMessages < runtime_types :: bridge_runtime_common :: messages_xcm_extension :: XcmBlobMessageDispatchResult > > ,) ; + impl ::subxt::events::StaticEvent for MessagesReceived { + const PALLET: &'static str = "BridgeMillauMessages"; + const EVENT: &'static str = "MessagesReceived"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + #[doc = "Messages in the inclusive range have been delivered to the bridged chain."] + pub struct MessagesDelivered { + pub lane_id: runtime_types::bp_messages::LaneId, + pub messages: runtime_types::bp_messages::DeliveredMessages, + } + impl ::subxt::events::StaticEvent for MessagesDelivered { + const PALLET: &'static str = "BridgeMillauMessages"; + const EVENT: &'static str = "MessagesDelivered"; + } + } + pub mod storage { + use super::runtime_types; + pub struct StorageApi; + impl StorageApi { + #[doc = " Optional pallet owner."] + #[doc = ""] + #[doc = " Pallet owner has a right to halt all pallet operations and then resume it. If it is"] + #[doc = " `None`, then there are no direct ways to halt/resume pallet operations, but other"] + #[doc = " runtime methods may still be used to do that (i.e. democracy::referendum to update halt"] + #[doc = " flag directly or call the `halt_operations`)."] + pub fn pallet_owner( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType<::sp_core::crypto::AccountId32>, + ::subxt::storage::address::Yes, + (), + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "PalletOwner", + vec![], + [ + 89u8, 42u8, 74u8, 119u8, 21u8, 164u8, 30u8, 115u8, 207u8, 126u8, 98u8, + 16u8, 162u8, 214u8, 67u8, 172u8, 178u8, 223u8, 139u8, 121u8, 174u8, + 89u8, 215u8, 75u8, 200u8, 161u8, 61u8, 195u8, 65u8, 222u8, 246u8, + 233u8, + ], + ) + } + #[doc = " The current operating mode of the pallet."] + #[doc = ""] + #[doc = " Depending on the mode either all, some, or no transactions will be allowed."] + pub fn pallet_operating_mode( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_messages::MessagesOperatingMode, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + (), + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "PalletOperatingMode", + vec![], + [ + 215u8, 195u8, 85u8, 231u8, 158u8, 22u8, 160u8, 132u8, 69u8, 206u8, + 238u8, 14u8, 56u8, 100u8, 134u8, 41u8, 58u8, 120u8, 225u8, 164u8, + 173u8, 87u8, 22u8, 123u8, 102u8, 167u8, 68u8, 70u8, 184u8, 131u8, + 232u8, 65u8, + ], + ) + } + #[doc = " Map of lane id => inbound lane data."] + pub fn inbound_lanes( + &self, + _0: impl ::std::borrow::Borrow, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_messages::InboundLaneData<::sp_core::crypto::AccountId32>, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "InboundLanes", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 38u8, 58u8, 110u8, 130u8, 112u8, 76u8, 231u8, 76u8, 56u8, 241u8, 183u8, + 153u8, 112u8, 41u8, 248u8, 208u8, 217u8, 57u8, 102u8, 30u8, 107u8, + 98u8, 59u8, 78u8, 56u8, 119u8, 186u8, 183u8, 213u8, 72u8, 199u8, 90u8, + ], + ) + } + #[doc = " Map of lane id => inbound lane data."] + pub fn inbound_lanes_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_messages::InboundLaneData<::sp_core::crypto::AccountId32>, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "InboundLanes", + Vec::new(), + [ + 38u8, 58u8, 110u8, 130u8, 112u8, 76u8, 231u8, 76u8, 56u8, 241u8, 183u8, + 153u8, 112u8, 41u8, 248u8, 208u8, 217u8, 57u8, 102u8, 30u8, 107u8, + 98u8, 59u8, 78u8, 56u8, 119u8, 186u8, 183u8, 213u8, 72u8, 199u8, 90u8, + ], + ) + } + #[doc = " Map of lane id => outbound lane data."] + pub fn outbound_lanes( + &self, + _0: impl ::std::borrow::Borrow, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_messages::OutboundLaneData, + >, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "OutboundLanes", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 67u8, 155u8, 173u8, 244u8, 80u8, 38u8, 26u8, 71u8, 51u8, 150u8, 86u8, + 146u8, 132u8, 122u8, 70u8, 122u8, 172u8, 246u8, 106u8, 232u8, 149u8, + 227u8, 240u8, 146u8, 51u8, 184u8, 30u8, 182u8, 200u8, 43u8, 190u8, + 38u8, + ], + ) + } + #[doc = " Map of lane id => outbound lane data."] + pub fn outbound_lanes_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bp_messages::OutboundLaneData, + >, + (), + ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "OutboundLanes", + Vec::new(), + [ + 67u8, 155u8, 173u8, 244u8, 80u8, 38u8, 26u8, 71u8, 51u8, 150u8, 86u8, + 146u8, 132u8, 122u8, 70u8, 122u8, 172u8, 246u8, 106u8, 232u8, 149u8, + 227u8, 240u8, 146u8, 51u8, 184u8, 30u8, 182u8, 200u8, 43u8, 190u8, + 38u8, + ], + ) + } + #[doc = " All queued outbound messages."] + pub fn outbound_messages( + &self, + _0: impl ::std::borrow::Borrow, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + >, + ::subxt::storage::address::Yes, + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "OutboundMessages", + vec![::subxt::storage::address::StorageMapKey::new( + _0.borrow(), + ::subxt::storage::address::StorageHasher::Blake2_128Concat, + )], + [ + 44u8, 35u8, 2u8, 25u8, 91u8, 101u8, 152u8, 23u8, 48u8, 250u8, 178u8, + 15u8, 194u8, 118u8, 146u8, 1u8, 112u8, 83u8, 243u8, 166u8, 124u8, + 153u8, 48u8, 193u8, 43u8, 31u8, 33u8, 72u8, 228u8, 113u8, 86u8, 217u8, + ], + ) + } + #[doc = " All queued outbound messages."] + pub fn outbound_messages_root( + &self, + ) -> ::subxt::storage::address::StaticStorageAddress< + ::subxt::metadata::DecodeStaticType< + runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + >, + (), + (), + ::subxt::storage::address::Yes, + > { + ::subxt::storage::address::StaticStorageAddress::new( + "BridgeMillauMessages", + "OutboundMessages", + Vec::new(), + [ + 44u8, 35u8, 2u8, 25u8, 91u8, 101u8, 152u8, 23u8, 48u8, 250u8, 178u8, + 15u8, 194u8, 118u8, 146u8, 1u8, 112u8, 83u8, 243u8, 166u8, 124u8, + 153u8, 48u8, 193u8, 43u8, 31u8, 33u8, 72u8, 228u8, 113u8, 86u8, 217u8, + ], + ) + } + } + } + pub mod constants { + use super::runtime_types; + pub struct ConstantsApi; + impl ConstantsApi { + #[doc = " Gets the chain id value from the instance."] + pub fn bridged_chain_id( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<[::core::primitive::u8; 4usize]>, + > { + ::subxt::constants::StaticConstantAddress::new( + "BridgeMillauMessages", + "BridgedChainId", + [ + 101u8, 157u8, 37u8, 163u8, 190u8, 134u8, 129u8, 212u8, 240u8, 135u8, + 174u8, 76u8, 220u8, 179u8, 252u8, 69u8, 65u8, 253u8, 69u8, 214u8, 61u8, + 249u8, 4u8, 38u8, 181u8, 237u8, 25u8, 131u8, 242u8, 20u8, 17u8, 152u8, + ], + ) + } + #[doc = " Maximal encoded size of the outbound payload."] + pub fn maximal_outbound_payload_size( + &self, + ) -> ::subxt::constants::StaticConstantAddress< + ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, + > { + ::subxt::constants::StaticConstantAddress::new( + "BridgeMillauMessages", + "MaximalOutboundPayloadSize", + [ + 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, + 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, + 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, + 145u8, + ], + ) + } + } + } + } + pub mod runtime_types { + use super::runtime_types; + pub mod bounded_collections { + use super::runtime_types; + pub mod bounded_vec { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct BoundedVec<_0>(pub ::std::vec::Vec<_0>); + } + pub mod weak_bounded_vec { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct WeakBoundedVec<_0>(pub ::std::vec::Vec<_0>); + } + } + pub mod bp_header_chain { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct StoredHeaderData<_0, _1> { + pub number: _0, + pub state_root: _1, + } + } + pub mod bp_messages { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct DeliveredMessages { + pub begin: ::core::primitive::u64, + pub end: ::core::primitive::u64, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct InboundLaneData<_0> { + pub relayers: ::std::vec::Vec>, + pub last_confirmed_nonce: ::core::primitive::u64, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct LaneId(pub [::core::primitive::u8; 4usize]); + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct MessageKey { + pub lane_id: runtime_types::bp_messages::LaneId, + pub nonce: ::core::primitive::u64, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum MessagesOperatingMode { + #[codec(index = 0)] + Basic(runtime_types::bp_runtime::BasicOperatingMode), + #[codec(index = 1)] + RejectingOutboundMessages, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct OutboundLaneData { + pub oldest_unpruned_nonce: ::core::primitive::u64, + pub latest_received_nonce: ::core::primitive::u64, + pub latest_generated_nonce: ::core::primitive::u64, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum ReceivalResult<_0> { + #[codec(index = 0)] + Dispatched(runtime_types::bp_runtime::messages::MessageDispatchResult<_0>), + #[codec(index = 1)] + InvalidNonce, + #[codec(index = 2)] + TooManyUnrewardedRelayers, + #[codec(index = 3)] + TooManyUnconfirmedMessages, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ReceivedMessages<_0> { + pub lane: runtime_types::bp_messages::LaneId, + pub receive_results: ::std::vec::Vec<( + ::core::primitive::u64, + runtime_types::bp_messages::ReceivalResult<_0>, + )>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct UnrewardedRelayer<_0> { + pub relayer: _0, + pub messages: runtime_types::bp_messages::DeliveredMessages, + } + } + pub mod bp_relayers { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum RewardsAccountOwner { + #[codec(index = 0)] + ThisChain, + #[codec(index = 1)] + BridgedChain, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct RewardsAccountParams { + pub lane_id: runtime_types::bp_messages::LaneId, + pub bridged_chain_id: [::core::primitive::u8; 4usize], + pub owner: runtime_types::bp_relayers::RewardsAccountOwner, + } + } + pub mod bp_runtime { + use super::runtime_types; + pub mod messages { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MessageDispatchResult<_0> { + pub unspent_weight: ::sp_weights::Weight, + pub dispatch_level_result: _0, + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum BasicOperatingMode { + #[codec(index = 0)] + Normal, + #[codec(index = 1)] + Halted, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct HeaderId<_0, _1>(pub _1, pub _0); + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum OwnedBridgeModuleError { + #[codec(index = 0)] + Halted, + } + } + pub mod bridge_runtime_common { + use super::runtime_types; + pub mod messages_xcm_extension { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum XcmBlobMessageDispatchResult { + #[codec(index = 0)] + InvalidPayload, + #[codec(index = 1)] + Dispatched, + #[codec(index = 2)] + NotDispatched, + } + } + } + pub mod cumulus_pallet_dmp_queue { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Service a single overweight message."] + service_overweight { + index: ::core::primitive::u64, + weight_limit: ::sp_weights::Weight, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "The message index given is unknown."] + Unknown, + #[codec(index = 1)] + #[doc = "The amount of weight given is possibly not enough for executing the message."] + OverLimit, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "Downward message is invalid XCM."] + InvalidFormat { message_id: [::core::primitive::u8; 32usize] }, + #[codec(index = 1)] + #[doc = "Downward message is unsupported version of XCM."] + UnsupportedVersion { message_id: [::core::primitive::u8; 32usize] }, + #[codec(index = 2)] + #[doc = "Downward message executed with the given outcome."] + ExecutedDownward { + message_id: [::core::primitive::u8; 32usize], + outcome: runtime_types::xcm::v3::traits::Outcome, + }, + #[codec(index = 3)] + #[doc = "The weight limit for handling downward messages was reached."] + WeightExhausted { + message_id: [::core::primitive::u8; 32usize], + remaining_weight: ::sp_weights::Weight, + required_weight: ::sp_weights::Weight, + }, + #[codec(index = 4)] + #[doc = "Downward message is overweight and was placed in the overweight queue."] + OverweightEnqueued { + message_id: [::core::primitive::u8; 32usize], + overweight_index: ::core::primitive::u64, + required_weight: ::sp_weights::Weight, + }, + #[codec(index = 5)] + #[doc = "Downward message from the overweight queue was executed."] + OverweightServiced { + overweight_index: ::core::primitive::u64, + weight_used: ::sp_weights::Weight, + }, + #[codec(index = 6)] + #[doc = "The maximum number of downward messages was."] + MaxMessagesExhausted { message_id: [::core::primitive::u8; 32usize] }, + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ConfigData { + pub max_individual: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct PageIndexData { + pub begin_used: ::core::primitive::u32, + pub end_used: ::core::primitive::u32, + pub overweight_count: ::core::primitive::u64, + } + } + pub mod cumulus_pallet_parachain_system { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + # [codec (index = 0)] # [doc = "Set the current validation data."] # [doc = ""] # [doc = "This should be invoked exactly once per block. It will panic at the finalization"] # [doc = "phase if the call was not invoked."] # [doc = ""] # [doc = "The dispatch origin for this call must be `Inherent`"] # [doc = ""] # [doc = "As a side effect, this function upgrades the current validation function"] # [doc = "if the appropriate time has come."] set_validation_data { data : runtime_types :: cumulus_primitives_parachain_inherent :: ParachainInherentData , } , # [codec (index = 1)] sudo_send_upward_message { message : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , # [codec (index = 2)] # [doc = "Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied"] # [doc = "later."] # [doc = ""] # [doc = "The `check_version` parameter sets a boolean flag for whether or not the runtime's spec"] # [doc = "version and name should be verified on upgrade. Since the authorization only has a hash,"] # [doc = "it cannot actually perform the verification."] # [doc = ""] # [doc = "This call requires Root origin."] authorize_upgrade { code_hash : :: subxt :: utils :: H256 , check_version : :: core :: primitive :: bool , } , # [codec (index = 3)] # [doc = "Provide the preimage (runtime binary) `code` for an upgrade that has been authorized."] # [doc = ""] # [doc = "If the authorization required a version check, this call will ensure the spec name"] # [doc = "remains unchanged and that the spec version has increased."] # [doc = ""] # [doc = "Note that this function will not apply the new `code`, but only attempt to schedule the"] # [doc = "upgrade with the Relay Chain."] # [doc = ""] # [doc = "All origins are allowed."] enact_authorized_upgrade { code : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "Attempt to upgrade validation function while existing upgrade pending."] + OverlappingUpgrades, + #[codec(index = 1)] + #[doc = "Polkadot currently prohibits this parachain from upgrading its validation function."] + ProhibitedByPolkadot, + #[codec(index = 2)] + #[doc = "The supplied validation function has compiled into a blob larger than Polkadot is"] + #[doc = "willing to run."] + TooBig, + #[codec(index = 3)] + #[doc = "The inherent which supplies the validation data did not run this block."] + ValidationDataNotAvailable, + #[codec(index = 4)] + #[doc = "The inherent which supplies the host configuration did not run this block."] + HostConfigurationNotAvailable, + #[codec(index = 5)] + #[doc = "No validation function upgrade is currently scheduled."] + NotScheduled, + #[codec(index = 6)] + #[doc = "No code upgrade has been authorized."] + NothingAuthorized, + #[codec(index = 7)] + #[doc = "The given code upgrade has not been authorized."] + Unauthorized, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "The validation function has been scheduled to apply."] + ValidationFunctionStored, + #[codec(index = 1)] + #[doc = "The validation function was applied as of the contained relay chain block number."] + ValidationFunctionApplied { relay_chain_block_num: ::core::primitive::u32 }, + #[codec(index = 2)] + #[doc = "The relay-chain aborted the upgrade process."] + ValidationFunctionDiscarded, + #[codec(index = 3)] + #[doc = "An upgrade has been authorized."] + UpgradeAuthorized { code_hash: ::subxt::utils::H256 }, + #[codec(index = 4)] + #[doc = "Some downward messages have been received and will be processed."] + DownwardMessagesReceived { count: ::core::primitive::u32 }, + #[codec(index = 5)] + #[doc = "Downward messages were processed using the given weight."] + DownwardMessagesProcessed { + weight_used: ::sp_weights::Weight, + dmq_head: ::subxt::utils::H256, + }, + #[codec(index = 6)] + #[doc = "An upward message was sent to the relay chain."] + UpwardMessageSent { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + }, + } + } + pub mod relay_state_snapshot { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MessagingStateSnapshot { + pub dmq_mqc_head: ::subxt::utils::H256, + pub relay_dispatch_queue_size: (::core::primitive::u32, ::core::primitive::u32), + pub ingress_channels: ::std::vec::Vec<( + runtime_types::polkadot_parachain::primitives::Id, + runtime_types::polkadot_primitives::v4::AbridgedHrmpChannel, + )>, + pub egress_channels: ::std::vec::Vec<( + runtime_types::polkadot_parachain::primitives::Id, + runtime_types::polkadot_primitives::v4::AbridgedHrmpChannel, + )>, + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct CodeUpgradeAuthorization { + pub code_hash: ::subxt::utils::H256, + pub check_version: ::core::primitive::bool, + } + } + pub mod cumulus_pallet_xcm { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call {} + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error {} + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "Downward message is invalid XCM."] + #[doc = "\\[ id \\]"] + InvalidFormat([::core::primitive::u8; 32usize]), + #[codec(index = 1)] + #[doc = "Downward message is unsupported version of XCM."] + #[doc = "\\[ id \\]"] + UnsupportedVersion([::core::primitive::u8; 32usize]), + #[codec(index = 2)] + #[doc = "Downward message executed with the given outcome."] + #[doc = "\\[ id, outcome \\]"] + ExecutedDownward( + [::core::primitive::u8; 32usize], + runtime_types::xcm::v3::traits::Outcome, + ), + } + } + } + pub mod cumulus_pallet_xcmp_queue { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Services a single overweight XCM."] + #[doc = ""] + #[doc = "- `origin`: Must pass `ExecuteOverweightOrigin`."] + #[doc = "- `index`: The index of the overweight XCM to service"] + #[doc = "- `weight_limit`: The amount of weight that XCM execution may take."] + #[doc = ""] + #[doc = "Errors:"] + #[doc = "- `BadOverweightIndex`: XCM under `index` is not found in the `Overweight` storage map."] + #[doc = "- `BadXcm`: XCM under `index` cannot be properly decoded into a valid XCM format."] + #[doc = "- `WeightOverLimit`: XCM execution may use greater `weight_limit`."] + #[doc = ""] + #[doc = "Events:"] + #[doc = "- `OverweightServiced`: On success."] + service_overweight { + index: ::core::primitive::u64, + weight_limit: ::sp_weights::Weight, + }, + #[codec(index = 1)] + #[doc = "Suspends all XCM executions for the XCMP queue, regardless of the sender's origin."] + #[doc = ""] + #[doc = "- `origin`: Must pass `ControllerOrigin`."] + suspend_xcm_execution, + #[codec(index = 2)] + #[doc = "Resumes all XCM executions for the XCMP queue."] + #[doc = ""] + #[doc = "Note that this function doesn't change the status of the in/out bound channels."] + #[doc = ""] + #[doc = "- `origin`: Must pass `ControllerOrigin`."] + resume_xcm_execution, + #[codec(index = 3)] + #[doc = "Overwrites the number of pages of messages which must be in the queue for the other side to be told to"] + #[doc = "suspend their sending."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.suspend_value`"] + update_suspend_threshold { new: ::core::primitive::u32 }, + #[codec(index = 4)] + #[doc = "Overwrites the number of pages of messages which must be in the queue after which we drop any further"] + #[doc = "messages from the channel."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.drop_threshold`"] + update_drop_threshold { new: ::core::primitive::u32 }, + #[codec(index = 5)] + #[doc = "Overwrites the number of pages of messages which the queue must be reduced to before it signals that"] + #[doc = "message sending may recommence after it has been suspended."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.resume_threshold`"] + update_resume_threshold { new: ::core::primitive::u32 }, + #[codec(index = 6)] + #[doc = "Overwrites the amount of remaining weight under which we stop processing messages."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.threshold_weight`"] + update_threshold_weight { new: ::sp_weights::Weight }, + #[codec(index = 7)] + #[doc = "Overwrites the speed to which the available weight approaches the maximum weight."] + #[doc = "A lower number results in a faster progression. A value of 1 makes the entire weight available initially."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.weight_restrict_decay`."] + update_weight_restrict_decay { new: ::sp_weights::Weight }, + #[codec(index = 8)] + #[doc = "Overwrite the maximum amount of weight any individual message may consume."] + #[doc = "Messages above this weight go into the overweight queue and may only be serviced explicitly."] + #[doc = ""] + #[doc = "- `origin`: Must pass `Root`."] + #[doc = "- `new`: Desired value for `QueueConfigData.xcmp_max_individual_weight`."] + update_xcmp_max_individual_weight { new: ::sp_weights::Weight }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "Failed to send XCM message."] + FailedToSend, + #[codec(index = 1)] + #[doc = "Bad XCM origin."] + BadXcmOrigin, + #[codec(index = 2)] + #[doc = "Bad XCM data."] + BadXcm, + #[codec(index = 3)] + #[doc = "Bad overweight index."] + BadOverweightIndex, + #[codec(index = 4)] + #[doc = "Provided weight is possibly not enough to execute the message."] + WeightOverLimit, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "Some XCM was executed ok."] + Success { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + weight: ::sp_weights::Weight, + }, + #[codec(index = 1)] + #[doc = "Some XCM failed."] + Fail { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + error: runtime_types::xcm::v3::traits::Error, + weight: ::sp_weights::Weight, + }, + #[codec(index = 2)] + #[doc = "Bad XCM version used."] + BadVersion { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + }, + #[codec(index = 3)] + #[doc = "Bad XCM format used."] + BadFormat { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + }, + #[codec(index = 4)] + #[doc = "An HRMP message was sent to a sibling parachain."] + XcmpMessageSent { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + }, + #[codec(index = 5)] + #[doc = "An XCM exceeded the individual message weight budget."] + OverweightEnqueued { + sender: runtime_types::polkadot_parachain::primitives::Id, + sent_at: ::core::primitive::u32, + index: ::core::primitive::u64, + required: ::sp_weights::Weight, + }, + #[codec(index = 6)] + #[doc = "An XCM from the overweight queue was executed with the given actual weight used."] + OverweightServiced { index: ::core::primitive::u64, used: ::sp_weights::Weight }, + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct InboundChannelDetails { + pub sender: runtime_types::polkadot_parachain::primitives::Id, + pub state: runtime_types::cumulus_pallet_xcmp_queue::InboundState, + pub message_metadata: ::std::vec::Vec<( + ::core::primitive::u32, + runtime_types::polkadot_parachain::primitives::XcmpMessageFormat, + )>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum InboundState { + #[codec(index = 0)] + Ok, + #[codec(index = 1)] + Suspended, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct OutboundChannelDetails { + pub recipient: runtime_types::polkadot_parachain::primitives::Id, + pub state: runtime_types::cumulus_pallet_xcmp_queue::OutboundState, + pub signals_exist: ::core::primitive::bool, + pub first_index: ::core::primitive::u16, + pub last_index: ::core::primitive::u16, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum OutboundState { + #[codec(index = 0)] + Ok, + #[codec(index = 1)] + Suspended, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct QueueConfigData { + pub suspend_threshold: ::core::primitive::u32, + pub drop_threshold: ::core::primitive::u32, + pub resume_threshold: ::core::primitive::u32, + pub threshold_weight: ::sp_weights::Weight, + pub weight_restrict_decay: ::sp_weights::Weight, + pub xcmp_max_individual_weight: ::sp_weights::Weight, + } + } + pub mod cumulus_primitives_parachain_inherent { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct MessageQueueChain(pub ::subxt::utils::H256); + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ParachainInherentData { + pub validation_data: + runtime_types::polkadot_primitives::v4::PersistedValidationData< + ::subxt::utils::H256, + ::core::primitive::u32, + >, + pub relay_chain_state: runtime_types::sp_trie::storage_proof::StorageProof, + pub downward_messages: ::std::vec::Vec< + runtime_types::polkadot_core_primitives::InboundDownwardMessage< + ::core::primitive::u32, + >, + >, + pub horizontal_messages: ::subxt::utils::KeyedVec< + runtime_types::polkadot_parachain::primitives::Id, + ::std::vec::Vec< + runtime_types::polkadot_core_primitives::InboundHrmpMessage< + ::core::primitive::u32, + >, + >, + >, + } + } + pub mod finality_grandpa { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Commit<_0, _1, _2, _3> { + pub target_hash: _0, + pub target_number: _1, + pub precommits: ::std::vec::Vec< + runtime_types::finality_grandpa::SignedPrecommit<_0, _1, _2, _3>, + >, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Precommit<_0, _1> { + pub target_hash: _0, + pub target_number: _1, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct SignedPrecommit<_0, _1, _2, _3> { + pub precommit: runtime_types::finality_grandpa::Precommit<_0, _1>, + pub signature: _2, + pub id: _3, + } + } + pub mod frame_support { + use super::runtime_types; + pub mod dispatch { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum DispatchClass { + #[codec(index = 0)] + Normal, + #[codec(index = 1)] + Operational, + #[codec(index = 2)] + Mandatory, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct DispatchInfo { + pub weight: ::sp_weights::Weight, + pub class: runtime_types::frame_support::dispatch::DispatchClass, + pub pays_fee: runtime_types::frame_support::dispatch::Pays, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Pays { + #[codec(index = 0)] + Yes, + #[codec(index = 1)] + No, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct PerDispatchClass<_0> { + pub normal: _0, + pub operational: _0, + pub mandatory: _0, + } + } + pub mod traits { + use super::runtime_types; + pub mod tokens { + use super::runtime_types; + pub mod misc { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum BalanceStatus { + #[codec(index = 0)] + Free, + #[codec(index = 1)] + Reserved, + } + } + } + } + } + pub mod frame_system { + use super::runtime_types; + pub mod extensions { + use super::runtime_types; + pub mod check_genesis { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct CheckGenesis; + } + pub mod check_mortality { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct CheckMortality(pub ::sp_runtime::generic::Era); + } + pub mod check_non_zero_sender { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct CheckNonZeroSender; + } + pub mod check_nonce { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct CheckNonce(#[codec(compact)] pub ::core::primitive::u32); + } + pub mod check_spec_version { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct CheckSpecVersion; + } + pub mod check_tx_version { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct CheckTxVersion; + } + pub mod check_weight { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct CheckWeight; + } + } + pub mod limits { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct BlockLength { + pub max: runtime_types::frame_support::dispatch::PerDispatchClass< + ::core::primitive::u32, + >, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct BlockWeights { + pub base_block: ::sp_weights::Weight, + pub max_block: ::sp_weights::Weight, + pub per_class: runtime_types::frame_support::dispatch::PerDispatchClass< + runtime_types::frame_system::limits::WeightsPerClass, + >, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct WeightsPerClass { + pub base_extrinsic: ::sp_weights::Weight, + pub max_extrinsic: ::core::option::Option<::sp_weights::Weight>, + pub max_total: ::core::option::Option<::sp_weights::Weight>, + pub reserved: ::core::option::Option<::sp_weights::Weight>, + } + } + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Make some on-chain remark."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- `O(1)`"] + remark { remark: ::std::vec::Vec<::core::primitive::u8> }, + #[codec(index = 1)] + #[doc = "Set the number of pages in the WebAssembly environment's heap."] + set_heap_pages { pages: ::core::primitive::u64 }, + #[codec(index = 2)] + #[doc = "Set the new runtime code."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code`"] + set_code { code: ::std::vec::Vec<::core::primitive::u8> }, + #[codec(index = 3)] + #[doc = "Set the new runtime code without doing any checks of the given `code`."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- `O(C)` where `C` length of `code`"] + set_code_without_checks { code: ::std::vec::Vec<::core::primitive::u8> }, + #[codec(index = 4)] + #[doc = "Set some items of storage."] + set_storage { + items: ::std::vec::Vec<( + ::std::vec::Vec<::core::primitive::u8>, + ::std::vec::Vec<::core::primitive::u8>, + )>, + }, + #[codec(index = 5)] + #[doc = "Kill some items from storage."] + kill_storage { keys: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>> }, + #[codec(index = 6)] + #[doc = "Kill all storage items with a key that starts with the given prefix."] + #[doc = ""] + #[doc = "**NOTE:** We rely on the Root origin to provide us the number of subkeys under"] + #[doc = "the prefix we are removing to accurately calculate the weight of this function."] + kill_prefix { + prefix: ::std::vec::Vec<::core::primitive::u8>, + subkeys: ::core::primitive::u32, + }, + #[codec(index = 7)] + #[doc = "Make some on-chain remark and emit event."] + remark_with_event { remark: ::std::vec::Vec<::core::primitive::u8> }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Error for the System pallet"] + pub enum Error { + #[codec(index = 0)] + #[doc = "The name of specification does not match between the current runtime"] + #[doc = "and the new runtime."] + InvalidSpecName, + #[codec(index = 1)] + #[doc = "The specification version is not allowed to decrease between the current runtime"] + #[doc = "and the new runtime."] + SpecVersionNeedsToIncrease, + #[codec(index = 2)] + #[doc = "Failed to extract the runtime version from the new runtime."] + #[doc = ""] + #[doc = "Either calling `Core_version` or decoding `RuntimeVersion` failed."] + FailedToExtractRuntimeVersion, + #[codec(index = 3)] + #[doc = "Suicide called when the account has non-default composite data."] + NonDefaultComposite, + #[codec(index = 4)] + #[doc = "There is a non-zero reference count preventing the account from being purged."] + NonZeroRefCount, + #[codec(index = 5)] + #[doc = "The origin filter prevent the call to be dispatched."] + CallFiltered, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Event for the System pallet."] + pub enum Event { + #[codec(index = 0)] + #[doc = "An extrinsic completed successfully."] + ExtrinsicSuccess { + dispatch_info: runtime_types::frame_support::dispatch::DispatchInfo, + }, + #[codec(index = 1)] + #[doc = "An extrinsic failed."] + ExtrinsicFailed { + dispatch_error: runtime_types::sp_runtime::DispatchError, + dispatch_info: runtime_types::frame_support::dispatch::DispatchInfo, + }, + #[codec(index = 2)] + #[doc = "`:code` was updated."] + CodeUpdated, + #[codec(index = 3)] + #[doc = "A new account was created."] + NewAccount { account: ::sp_core::crypto::AccountId32 }, + #[codec(index = 4)] + #[doc = "An account was reaped."] + KilledAccount { account: ::sp_core::crypto::AccountId32 }, + #[codec(index = 5)] + #[doc = "On on-chain remark happened."] + Remarked { sender: ::sp_core::crypto::AccountId32, hash: ::subxt::utils::H256 }, + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct AccountInfo<_0, _1> { + pub nonce: _0, + pub consumers: _0, + pub providers: _0, + pub sufficients: _0, + pub data: _1, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct EventRecord<_0, _1> { + pub phase: runtime_types::frame_system::Phase, + pub event: _0, + pub topics: ::std::vec::Vec<_1>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct LastRuntimeUpgradeInfo { + #[codec(compact)] + pub spec_version: ::core::primitive::u32, + pub spec_name: ::std::string::String, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum Phase { + #[codec(index = 0)] + ApplyExtrinsic(::core::primitive::u32), + #[codec(index = 1)] + Finalization, + #[codec(index = 2)] + Initialization, + } + } + pub mod pallet_balances { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Transfer some liquid free balance to another account."] + #[doc = ""] + #[doc = "`transfer_allow_death` will set the `FreeBalance` of the sender and receiver."] + #[doc = "If the sender's account is below the existential deposit as a result"] + #[doc = "of the transfer, the account will be reaped."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be `Signed` by the transactor."] + transfer_allow_death { + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + value: ::core::primitive::u128, + }, + #[codec(index = 1)] + #[doc = "Set the regular balance of a given account; it also takes a reserved balance but this"] + #[doc = "must be the same as the account's current reserved balance."] + #[doc = ""] + #[doc = "The dispatch origin for this call is `root`."] + #[doc = ""] + #[doc = "WARNING: This call is DEPRECATED! Use `force_set_balance` instead."] + set_balance_deprecated { + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + new_free: ::core::primitive::u128, + #[codec(compact)] + old_reserved: ::core::primitive::u128, + }, + #[codec(index = 2)] + #[doc = "Exactly as `transfer_allow_death`, except the origin must be root and the source account"] + #[doc = "may be specified."] + force_transfer { + source: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + value: ::core::primitive::u128, + }, + #[codec(index = 3)] + #[doc = "Same as the [`transfer_allow_death`] call, but with a check that the transfer will not"] + #[doc = "kill the origin account."] + #[doc = ""] + #[doc = "99% of the time you want [`transfer_allow_death`] instead."] + #[doc = ""] + #[doc = "[`transfer_allow_death`]: struct.Pallet.html#method.transfer"] + transfer_keep_alive { + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + value: ::core::primitive::u128, + }, + #[codec(index = 4)] + #[doc = "Transfer the entire transferable balance from the caller account."] + #[doc = ""] + #[doc = "NOTE: This function only attempts to transfer _transferable_ balances. This means that"] + #[doc = "any locked, reserved, or existential deposits (when `keep_alive` is `true`), will not be"] + #[doc = "transferred by this function. To ensure that this function results in a killed account,"] + #[doc = "you might need to prepare the account by removing any reference counters, storage"] + #[doc = "deposits, etc..."] + #[doc = ""] + #[doc = "The dispatch origin of this call must be Signed."] + #[doc = ""] + #[doc = "- `dest`: The recipient of the transfer."] + #[doc = "- `keep_alive`: A boolean to determine if the `transfer_all` operation should send all"] + #[doc = " of the funds the account has, causing the sender account to be killed (false), or"] + #[doc = " transfer everything except at least the existential deposit, which will guarantee to"] + #[doc = " keep the sender account alive (true)."] + transfer_all { + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + keep_alive: ::core::primitive::bool, + }, + #[codec(index = 5)] + #[doc = "Unreserve some balance from a user by force."] + #[doc = ""] + #[doc = "Can only be called by ROOT."] + force_unreserve { + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + amount: ::core::primitive::u128, + }, + #[codec(index = 6)] + #[doc = "Upgrade a specified account."] + #[doc = ""] + #[doc = "- `origin`: Must be `Signed`."] + #[doc = "- `who`: The account to be upgraded."] + #[doc = ""] + #[doc = "This will waive the transaction fee if at least all but 10% of the accounts needed to"] + #[doc = "be upgraded. (We let some not have to be upgraded just in order to allow for the"] + #[doc = "possibililty of churn)."] + upgrade_accounts { who: ::std::vec::Vec<::sp_core::crypto::AccountId32> }, + #[codec(index = 7)] + #[doc = "Alias for `transfer_allow_death`, provided only for name-wise compatibility."] + #[doc = ""] + #[doc = "WARNING: DEPRECATED! Will be released in approximately 3 months."] + transfer { + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + value: ::core::primitive::u128, + }, + #[codec(index = 8)] + #[doc = "Set the regular balance of a given account."] + #[doc = ""] + #[doc = "The dispatch origin for this call is `root`."] + force_set_balance { + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + new_free: ::core::primitive::u128, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "Vesting balance too high to send value."] + VestingBalance, + #[codec(index = 1)] + #[doc = "Account liquidity restrictions prevent withdrawal."] + LiquidityRestrictions, + #[codec(index = 2)] + #[doc = "Balance too low to send value."] + InsufficientBalance, + #[codec(index = 3)] + #[doc = "Value too low to create account due to existential deposit."] + ExistentialDeposit, + #[codec(index = 4)] + #[doc = "Transfer/payment would kill account."] + Expendability, + #[codec(index = 5)] + #[doc = "A vesting schedule already exists for this account."] + ExistingVestingSchedule, + #[codec(index = 6)] + #[doc = "Beneficiary account must pre-exist."] + DeadAccount, + #[codec(index = 7)] + #[doc = "Number of named reserves exceed `MaxReserves`."] + TooManyReserves, + #[codec(index = 8)] + #[doc = "Number of holds exceed `MaxHolds`."] + TooManyHolds, + #[codec(index = 9)] + #[doc = "Number of freezes exceed `MaxFreezes`."] + TooManyFreezes, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "An account was created with some free balance."] + Endowed { + account: ::sp_core::crypto::AccountId32, + free_balance: ::core::primitive::u128, + }, + #[codec(index = 1)] + #[doc = "An account was removed whose balance was non-zero but below ExistentialDeposit,"] + #[doc = "resulting in an outright loss."] + DustLost { + account: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 2)] + #[doc = "Transfer succeeded."] + Transfer { + from: ::sp_core::crypto::AccountId32, + to: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 3)] + #[doc = "A balance was set by root."] + BalanceSet { + who: ::sp_core::crypto::AccountId32, + free: ::core::primitive::u128, + }, + #[codec(index = 4)] + #[doc = "Some balance was reserved (moved from free to reserved)."] + Reserved { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 5)] + #[doc = "Some balance was unreserved (moved from reserved to free)."] + Unreserved { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 6)] + #[doc = "Some balance was moved from the reserve of the first account to the second account."] + #[doc = "Final argument indicates the destination balance type."] + ReserveRepatriated { + from: ::sp_core::crypto::AccountId32, + to: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + destination_status: + runtime_types::frame_support::traits::tokens::misc::BalanceStatus, + }, + #[codec(index = 7)] + #[doc = "Some amount was deposited (e.g. for transaction fees)."] + Deposit { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, + #[codec(index = 8)] + #[doc = "Some amount was withdrawn from the account (e.g. for transaction fees)."] + Withdraw { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 9)] + #[doc = "Some amount was removed from the account (e.g. for misbehavior)."] + Slashed { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, + #[codec(index = 10)] + #[doc = "Some amount was minted into an account."] + Minted { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, + #[codec(index = 11)] + #[doc = "Some amount was burned from an account."] + Burned { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, + #[codec(index = 12)] + #[doc = "Some amount was suspended from an account (it can be restored later)."] + Suspended { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 13)] + #[doc = "Some amount was restored into an account."] + Restored { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 14)] + #[doc = "An account was upgraded."] + Upgraded { who: ::sp_core::crypto::AccountId32 }, + #[codec(index = 15)] + #[doc = "Total issuance was increased by `amount`, creating a credit to be balanced."] + Issued { amount: ::core::primitive::u128 }, + #[codec(index = 16)] + #[doc = "Total issuance was decreased by `amount`, creating a debt to be balanced."] + Rescinded { amount: ::core::primitive::u128 }, + } + } + pub mod types { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct AccountData<_0> { + pub free: _0, + pub reserved: _0, + pub frozen: _0, + pub flags: runtime_types::pallet_balances::types::ExtraFlags, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct BalanceLock<_0> { + pub id: [::core::primitive::u8; 8usize], + pub amount: _0, + pub reasons: runtime_types::pallet_balances::types::Reasons, + } + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct ExtraFlags(pub ::core::primitive::u128); + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct IdAmount<_0, _1> { + pub id: _0, + pub amount: _1, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Reasons { + #[codec(index = 0)] + Fee, + #[codec(index = 1)] + Misc, + #[codec(index = 2)] + All, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct ReserveData<_0, _1> { + pub id: _0, + pub amount: _1, + } + } + } + pub mod pallet_bridge_grandpa { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Verify a target header is finalized according to the given finality proof."] + #[doc = ""] + #[doc = "It will use the underlying storage pallet to fetch information about the current"] + #[doc = "authorities and best finalized header in order to verify that the header is finalized."] + #[doc = ""] + #[doc = "If successful in verification, it will write the target header to the underlying storage"] + #[doc = "pallet."] + submit_finality_proof { + finality_target: ::std::boxed::Box< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + justification: ::bp_header_chain::justification::GrandpaJustification< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + }, + #[codec(index = 1)] + #[doc = "Bootstrap the bridge pallet with an initial header and authority set from which to sync."] + #[doc = ""] + #[doc = "The initial configuration provided does not need to be the genesis header of the bridged"] + #[doc = "chain, it can be any arbitrary header. You can also provide the next scheduled set"] + #[doc = "change if it is already know."] + #[doc = ""] + #[doc = "This function is only allowed to be called from a trusted origin and writes to storage"] + #[doc = "with practically no checks in terms of the validity of the data. It is important that"] + #[doc = "you ensure that valid data is being passed in."] + initialize { + init_data: ::bp_header_chain::InitializationData< + ::sp_runtime::generic::Header< + ::core::primitive::u64, + ::bp_millau::BlakeTwoAndKeccak256, + >, + >, + }, + #[codec(index = 2)] + #[doc = "Change `PalletOwner`."] + #[doc = ""] + #[doc = "May only be called either by root, or by `PalletOwner`."] + set_owner { new_owner: ::core::option::Option<::sp_core::crypto::AccountId32> }, + #[codec(index = 3)] + #[doc = "Halt or resume all pallet operations."] + #[doc = ""] + #[doc = "May only be called either by root, or by `PalletOwner`."] + set_operating_mode { + operating_mode: runtime_types::bp_runtime::BasicOperatingMode, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "The given justification is invalid for the given header."] + InvalidJustification, + #[codec(index = 1)] + #[doc = "The authority set from the underlying header chain is invalid."] + InvalidAuthoritySet, + #[codec(index = 2)] + #[doc = "There are too many requests for the current window to handle."] + TooManyRequests, + #[codec(index = 3)] + #[doc = "The header being imported is older than the best finalized header known to the pallet."] + OldHeader, + #[codec(index = 4)] + #[doc = "The scheduled authority set change found in the header is unsupported by the pallet."] + #[doc = ""] + #[doc = "This is the case for non-standard (e.g forced) authority set changes."] + UnsupportedScheduledChange, + #[codec(index = 5)] + #[doc = "The pallet is not yet initialized."] + NotInitialized, + #[codec(index = 6)] + #[doc = "The pallet has already been initialized."] + AlreadyInitialized, + #[codec(index = 7)] + #[doc = "Too many authorities in the set."] + TooManyAuthoritiesInSet, + #[codec(index = 8)] + #[doc = "Error generated by the `OwnedBridgeModule` trait."] + BridgeModule(runtime_types::bp_runtime::OwnedBridgeModuleError), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "Best finalized chain header has been updated to the header with given number and hash."] + UpdatedBestFinalizedHeader { + number: ::core::primitive::u64, + hash: ::bp_millau::MillauHash, + }, + } + } + pub mod storage_types { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct StoredAuthoritySet { + pub authorities: runtime_types::bounded_collections::bounded_vec::BoundedVec<( + runtime_types::sp_consensus_grandpa::app::Public, + ::core::primitive::u64, + )>, + pub set_id: ::core::primitive::u64, + } + } + } + pub mod pallet_bridge_messages { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + # [codec (index = 0)] # [doc = "Change `PalletOwner`."] # [doc = ""] # [doc = "May only be called either by root, or by `PalletOwner`."] set_owner { new_owner : :: core :: option :: Option < :: sp_core :: crypto :: AccountId32 > , } , # [codec (index = 1)] # [doc = "Halt or resume all/some pallet operations."] # [doc = ""] # [doc = "May only be called either by root, or by `PalletOwner`."] set_operating_mode { operating_mode : runtime_types :: bp_messages :: MessagesOperatingMode , } , # [codec (index = 2)] # [doc = "Receive messages proof from bridged chain."] # [doc = ""] # [doc = "The weight of the call assumes that the transaction always brings outbound lane"] # [doc = "state update. Because of that, the submitter (relayer) has no benefit of not including"] # [doc = "this data in the transaction, so reward confirmations lags should be minimal."] receive_messages_proof { relayer_id_at_bridged_chain : :: sp_core :: crypto :: AccountId32 , proof : :: bridge_runtime_common :: messages :: target :: FromBridgedChainMessagesProof < :: bp_millau :: MillauHash > , messages_count : :: core :: primitive :: u32 , dispatch_weight : :: sp_weights :: Weight , } , # [codec (index = 3)] # [doc = "Receive messages delivery proof from bridged chain."] receive_messages_delivery_proof { proof : :: bridge_runtime_common :: messages :: source :: FromBridgedChainMessagesDeliveryProof < :: bp_millau :: MillauHash > , relayers_state : :: bp_messages :: UnrewardedRelayersState , } , } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "Pallet is not in Normal operating mode."] + NotOperatingNormally, + #[codec(index = 1)] + #[doc = "The outbound lane is inactive."] + InactiveOutboundLane, + #[codec(index = 2)] + #[doc = "The message is too large to be sent over the bridge."] + MessageIsTooLarge, + #[codec(index = 3)] + #[doc = "Message has been treated as invalid by chain verifier."] + MessageRejectedByChainVerifier, + #[codec(index = 4)] + #[doc = "Message has been treated as invalid by lane verifier."] + MessageRejectedByLaneVerifier, + #[codec(index = 5)] + #[doc = "Submitter has failed to pay fee for delivering and dispatching messages."] + FailedToWithdrawMessageFee, + #[codec(index = 6)] + #[doc = "The transaction brings too many messages."] + TooManyMessagesInTheProof, + #[codec(index = 7)] + #[doc = "Invalid messages has been submitted."] + InvalidMessagesProof, + #[codec(index = 8)] + #[doc = "Invalid messages delivery proof has been submitted."] + InvalidMessagesDeliveryProof, + #[codec(index = 9)] + #[doc = "The bridged chain has invalid `UnrewardedRelayers` in its storage (fatal for the lane)."] + InvalidUnrewardedRelayers, + #[codec(index = 10)] + #[doc = "The relayer has declared invalid unrewarded relayers state in the"] + #[doc = "`receive_messages_delivery_proof` call."] + InvalidUnrewardedRelayersState, + #[codec(index = 11)] + #[doc = "The cumulative dispatch weight, passed by relayer is not enough to cover dispatch"] + #[doc = "of all bundled messages."] + InsufficientDispatchWeight, + #[codec(index = 12)] + #[doc = "The message someone is trying to work with (i.e. increase fee) is not yet sent."] + MessageIsNotYetSent, + #[codec(index = 13)] + #[doc = "The number of actually confirmed messages is going to be larger than the number of"] + #[doc = "messages in the proof. This may mean that this or bridged chain storage is corrupted."] + TryingToConfirmMoreMessagesThanExpected, + #[codec(index = 14)] + #[doc = "Error generated by the `OwnedBridgeModule` trait."] + BridgeModule(runtime_types::bp_runtime::OwnedBridgeModuleError), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + # [codec (index = 0)] # [doc = "Message has been accepted and is waiting to be delivered."] MessageAccepted { lane_id : runtime_types :: bp_messages :: LaneId , nonce : :: core :: primitive :: u64 , } , # [codec (index = 1)] # [doc = "Messages have been received from the bridged chain."] MessagesReceived (:: std :: vec :: Vec < runtime_types :: bp_messages :: ReceivedMessages < runtime_types :: bridge_runtime_common :: messages_xcm_extension :: XcmBlobMessageDispatchResult > > ,) , # [codec (index = 2)] # [doc = "Messages in the inclusive range have been delivered to the bridged chain."] MessagesDelivered { lane_id : runtime_types :: bp_messages :: LaneId , messages : runtime_types :: bp_messages :: DeliveredMessages , } , } + } + } + pub mod pallet_bridge_relayers { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Claim accumulated rewards."] + claim_rewards { + rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "No reward can be claimed by given relayer."] + NoRewardForRelayer, + #[codec(index = 1)] + #[doc = "Reward payment procedure has failed."] + FailedToPayReward, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "Reward has been paid to the relayer."] + RewardPaid { + relayer: ::sp_core::crypto::AccountId32, + rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, + reward: ::core::primitive::u128, + }, + } + } + } + pub mod pallet_sudo { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Authenticates the sudo key and dispatches a function call with `Root` origin."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- O(1)."] + sudo { + call: + ::std::boxed::Box, + }, + #[codec(index = 1)] + #[doc = "Authenticates the sudo key and dispatches a function call with `Root` origin."] + #[doc = "This function does not check the weight of the call, and instead allows the"] + #[doc = "Sudo user to specify the weight of the call."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- O(1)."] + sudo_unchecked_weight { + call: + ::std::boxed::Box, + weight: ::sp_weights::Weight, + }, + #[codec(index = 2)] + #[doc = "Authenticates the current sudo key and sets the given AccountId (`new`) as the new sudo"] + #[doc = "key."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- O(1)."] + set_key { + new: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + }, + #[codec(index = 3)] + #[doc = "Authenticates the sudo key and dispatches a function call with `Signed` origin from"] + #[doc = "a given account."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be _Signed_."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- O(1)."] + sudo_as { + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + call: + ::std::boxed::Box, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Error for the Sudo pallet"] + pub enum Error { + #[codec(index = 0)] + #[doc = "Sender must be the Sudo account"] + RequireSudo, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "A sudo just took place. \\[result\\]"] + Sudid { + sudo_result: + ::core::result::Result<(), runtime_types::sp_runtime::DispatchError>, + }, + #[codec(index = 1)] + #[doc = "The \\[sudoer\\] just switched identity; the old key is supplied if one existed."] + KeyChanged { + old_sudoer: ::core::option::Option<::sp_core::crypto::AccountId32>, + }, + #[codec(index = 2)] + #[doc = "A sudo just took place. \\[result\\]"] + SudoAsDone { + sudo_result: + ::core::result::Result<(), runtime_types::sp_runtime::DispatchError>, + }, + } + } + } + pub mod pallet_timestamp { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + #[doc = "Set the current time."] + #[doc = ""] + #[doc = "This call should be invoked exactly once per block. It will panic at the finalization"] + #[doc = "phase, if this call hasn't been invoked by that time."] + #[doc = ""] + #[doc = "The timestamp should be greater than the previous one by the amount specified by"] + #[doc = "`MinimumPeriod`."] + #[doc = ""] + #[doc = "The dispatch origin for this call must be `Inherent`."] + #[doc = ""] + #[doc = "## Complexity"] + #[doc = "- `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`)"] + #[doc = "- 1 storage read and 1 storage mutation (codec `O(1)`). (because of `DidUpdate::take` in"] + #[doc = " `on_finalize`)"] + #[doc = "- 1 event handler `on_timestamp_set`. Must be `O(1)`."] + set { + #[codec(compact)] + now: ::core::primitive::u64, + }, + } + } + } + pub mod pallet_transaction_payment { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "A transaction fee `actual_fee`, of which `tip` was added to the minimum inclusion fee,"] + #[doc = "has been paid by `who`."] + TransactionFeePaid { + who: ::sp_core::crypto::AccountId32, + actual_fee: ::core::primitive::u128, + tip: ::core::primitive::u128, + }, + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ChargeTransactionPayment(#[codec(compact)] pub ::core::primitive::u128); + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum Releases { + #[codec(index = 0)] + V1Ancient, + #[codec(index = 1)] + V2, + } + } + pub mod pallet_xcm { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + pub enum Call { + #[codec(index = 0)] + send { + dest: ::std::boxed::Box, + message: ::std::boxed::Box, + }, + #[codec(index = 1)] + #[doc = "Teleport some assets from the local chain to some destination chain."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,"] + #[doc = "with all fees taken as needed from the asset."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the"] + #[doc = " `dest` side. May not be empty."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + teleport_assets { + dest: ::std::boxed::Box, + beneficiary: ::std::boxed::Box, + assets: ::std::boxed::Box, + fee_asset_item: ::core::primitive::u32, + }, + #[codec(index = 2)] + #[doc = "Transfer some assets from the local chain to the sovereign account of a destination"] + #[doc = "chain and forward a notification XCM."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,"] + #[doc = "with all fees taken as needed from the asset."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the"] + #[doc = " `dest` side."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + reserve_transfer_assets { + dest: ::std::boxed::Box, + beneficiary: ::std::boxed::Box, + assets: ::std::boxed::Box, + fee_asset_item: ::core::primitive::u32, + }, + #[codec(index = 3)] + #[doc = "Execute an XCM message from a local, signed, origin."] + #[doc = ""] + #[doc = "An event is deposited indicating whether `msg` could be executed completely or only"] + #[doc = "partially."] + #[doc = ""] + #[doc = "No more than `max_weight` will be used in its attempted execution. If this is less than the"] + #[doc = "maximum amount of weight that the message could take to be executed, then no execution"] + #[doc = "attempt will be made."] + #[doc = ""] + #[doc = "NOTE: A successful return to this does *not* imply that the `msg` was executed successfully"] + #[doc = "to completion; only that *some* of it was executed."] + execute { + message: ::std::boxed::Box, + max_weight: ::sp_weights::Weight, + }, + #[codec(index = 4)] + #[doc = "Extoll that a particular destination can be communicated with through a particular"] + #[doc = "version of XCM."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `location`: The destination that is being described."] + #[doc = "- `xcm_version`: The latest version of XCM that `location` supports."] + force_xcm_version { + location: + ::std::boxed::Box, + xcm_version: ::core::primitive::u32, + }, + #[codec(index = 5)] + #[doc = "Set a safe XCM version (the version that XCM should be encoded with if the most recent"] + #[doc = "version a destination can accept is unknown)."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `maybe_xcm_version`: The default XCM encoding version, or `None` to disable."] + force_default_xcm_version { + maybe_xcm_version: ::core::option::Option<::core::primitive::u32>, + }, + #[codec(index = 6)] + #[doc = "Ask a location to notify us regarding their XCM version and any changes to it."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `location`: The location to which we should subscribe for XCM version notifications."] + force_subscribe_version_notify { + location: ::std::boxed::Box, + }, + #[codec(index = 7)] + #[doc = "Require that a particular destination should no longer notify us regarding any XCM"] + #[doc = "version changes."] + #[doc = ""] + #[doc = "- `origin`: Must be Root."] + #[doc = "- `location`: The location to which we are currently subscribed for XCM version"] + #[doc = " notifications which we no longer desire."] + force_unsubscribe_version_notify { + location: ::std::boxed::Box, + }, + #[codec(index = 8)] + #[doc = "Transfer some assets from the local chain to the sovereign account of a destination"] + #[doc = "chain and forward a notification XCM."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight"] + #[doc = "is needed than `weight_limit`, then the operation will fail and the assets send may be"] + #[doc = "at risk."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the"] + #[doc = " `dest` side."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + #[doc = "- `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase."] + limited_reserve_transfer_assets { + dest: ::std::boxed::Box, + beneficiary: ::std::boxed::Box, + assets: ::std::boxed::Box, + fee_asset_item: ::core::primitive::u32, + weight_limit: runtime_types::xcm::v3::WeightLimit, + }, + #[codec(index = 9)] + #[doc = "Teleport some assets from the local chain to some destination chain."] + #[doc = ""] + #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] + #[doc = "index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight"] + #[doc = "is needed than `weight_limit`, then the operation will fail and the assets send may be"] + #[doc = "at risk."] + #[doc = ""] + #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] + #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] + #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] + #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] + #[doc = " an `AccountId32` value."] + #[doc = "- `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the"] + #[doc = " `dest` side. May not be empty."] + #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] + #[doc = " fees."] + #[doc = "- `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase."] + limited_teleport_assets { + dest: ::std::boxed::Box, + beneficiary: ::std::boxed::Box, + assets: ::std::boxed::Box, + fee_asset_item: ::core::primitive::u32, + weight_limit: runtime_types::xcm::v3::WeightLimit, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + pub enum Error { + #[codec(index = 0)] + #[doc = "The desired destination was unreachable, generally because there is a no way of routing"] + #[doc = "to it."] + Unreachable, + #[codec(index = 1)] + #[doc = "There was some other issue (i.e. not to do with routing) in sending the message. Perhaps"] + #[doc = "a lack of space for buffering the message."] + SendFailure, + #[codec(index = 2)] + #[doc = "The message execution fails the filter."] + Filtered, + #[codec(index = 3)] + #[doc = "The message's weight could not be determined."] + UnweighableMessage, + #[codec(index = 4)] + #[doc = "The destination `MultiLocation` provided cannot be inverted."] + DestinationNotInvertible, + #[codec(index = 5)] + #[doc = "The assets to be sent are empty."] + Empty, + #[codec(index = 6)] + #[doc = "Could not re-anchor the assets to declare the fees for the destination chain."] + CannotReanchor, + #[codec(index = 7)] + #[doc = "Too many assets have been attempted for transfer."] + TooManyAssets, + #[codec(index = 8)] + #[doc = "Origin is invalid for sending."] + InvalidOrigin, + #[codec(index = 9)] + #[doc = "The version of the `Versioned` value used is not able to be interpreted."] + BadVersion, + #[codec(index = 10)] + #[doc = "The given location could not be used (e.g. because it cannot be expressed in the"] + #[doc = "desired version of XCM)."] + BadLocation, + #[codec(index = 11)] + #[doc = "The referenced subscription could not be found."] + NoSubscription, + #[codec(index = 12)] + #[doc = "The location is invalid since it already has a subscription from us."] + AlreadySubscribed, + #[codec(index = 13)] + #[doc = "Invalid asset for the operation."] + InvalidAsset, + #[codec(index = 14)] + #[doc = "The owner does not own (all) of the asset that they wish to do the operation on."] + LowBalance, + #[codec(index = 15)] + #[doc = "The asset owner has too many locks on the asset."] + TooManyLocks, + #[codec(index = 16)] + #[doc = "The given account is not an identifiable sovereign account for any location."] + AccountNotSovereign, + #[codec(index = 17)] + #[doc = "The operation required fees to be paid which the initiator could not meet."] + FeesNotMet, + #[codec(index = 18)] + #[doc = "A remote lock with the corresponding data could not be found."] + LockNotFound, + #[codec(index = 19)] + #[doc = "The unlock operation cannot succeed because there are still users of the lock."] + InUse, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + pub enum Event { + #[codec(index = 0)] + #[doc = "Execution of an XCM message was attempted."] + #[doc = ""] + #[doc = "\\[ outcome \\]"] + Attempted(runtime_types::xcm::v3::traits::Outcome), + #[codec(index = 1)] + #[doc = "A XCM message was sent."] + #[doc = ""] + #[doc = "\\[ origin, destination, message \\]"] + Sent( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::Xcm, + ), + #[codec(index = 2)] + #[doc = "Query response received which does not match a registered query. This may be because a"] + #[doc = "matching query was never registered, it may be because it is a duplicate response, or"] + #[doc = "because the query timed out."] + #[doc = ""] + #[doc = "\\[ origin location, id \\]"] + UnexpectedResponse( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + ), + #[codec(index = 3)] + #[doc = "Query response has been received and is ready for taking with `take_response`. There is"] + #[doc = "no registered notification call."] + #[doc = ""] + #[doc = "\\[ id, response \\]"] + ResponseReady(::core::primitive::u64, runtime_types::xcm::v3::Response), + #[codec(index = 4)] + #[doc = "Query response has been received and query is removed. The registered notification has"] + #[doc = "been dispatched and executed successfully."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index \\]"] + Notified(::core::primitive::u64, ::core::primitive::u8, ::core::primitive::u8), + #[codec(index = 5)] + #[doc = "Query response has been received and query is removed. The registered notification could"] + #[doc = "not be dispatched because the dispatch weight is greater than the maximum weight"] + #[doc = "originally budgeted by this runtime for the query result."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index, actual weight, max budgeted weight \\]"] + NotifyOverweight( + ::core::primitive::u64, + ::core::primitive::u8, + ::core::primitive::u8, + ::sp_weights::Weight, + ::sp_weights::Weight, + ), + #[codec(index = 6)] + #[doc = "Query response has been received and query is removed. There was a general error with"] + #[doc = "dispatching the notification call."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index \\]"] + NotifyDispatchError( + ::core::primitive::u64, + ::core::primitive::u8, + ::core::primitive::u8, + ), + #[codec(index = 7)] + #[doc = "Query response has been received and query is removed. The dispatch was unable to be"] + #[doc = "decoded into a `Call`; this might be due to dispatch function having a signature which"] + #[doc = "is not `(origin, QueryId, Response)`."] + #[doc = ""] + #[doc = "\\[ id, pallet index, call index \\]"] + NotifyDecodeFailed( + ::core::primitive::u64, + ::core::primitive::u8, + ::core::primitive::u8, + ), + #[codec(index = 8)] + #[doc = "Expected query response has been received but the origin location of the response does"] + #[doc = "not match that expected. The query remains registered for a later, valid, response to"] + #[doc = "be received and acted upon."] + #[doc = ""] + #[doc = "\\[ origin location, id, expected location \\]"] + InvalidResponder( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + ), + #[codec(index = 9)] + #[doc = "Expected query response has been received but the expected origin location placed in"] + #[doc = "storage by this runtime previously cannot be decoded. The query remains registered."] + #[doc = ""] + #[doc = "This is unexpected (since a location placed in storage in a previously executing"] + #[doc = "runtime should be readable prior to query timeout) and dangerous since the possibly"] + #[doc = "valid response will be dropped. Manual governance intervention is probably going to be"] + #[doc = "needed."] + #[doc = ""] + #[doc = "\\[ origin location, id \\]"] + InvalidResponderVersion( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + ), + #[codec(index = 10)] + #[doc = "Received query response has been read and removed."] + #[doc = ""] + #[doc = "\\[ id \\]"] + ResponseTaken(::core::primitive::u64), + #[codec(index = 11)] + #[doc = "Some assets have been placed in an asset trap."] + #[doc = ""] + #[doc = "\\[ hash, origin, assets \\]"] + AssetsTrapped( + ::subxt::utils::H256, + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::VersionedMultiAssets, + ), + #[codec(index = 12)] + #[doc = "An XCM version change notification message has been attempted to be sent."] + #[doc = ""] + #[doc = "The cost of sending it (borne by the chain) is included."] + #[doc = ""] + #[doc = "\\[ destination, result, cost \\]"] + VersionChangeNotified( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u32, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 13)] + #[doc = "The supported version of a location has been changed. This might be through an"] + #[doc = "automatic notification or a manual intervention."] + #[doc = ""] + #[doc = "\\[ location, XCM version \\]"] + SupportedVersionChanged( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u32, + ), + #[codec(index = 14)] + #[doc = "A given location which had a version change subscription was dropped owing to an error"] + #[doc = "sending the notification to it."] + #[doc = ""] + #[doc = "\\[ location, query ID, error \\]"] + NotifyTargetSendFail( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + runtime_types::xcm::v3::traits::Error, + ), + #[codec(index = 15)] + #[doc = "A given location which had a version change subscription was dropped owing to an error"] + #[doc = "migrating the location to our new XCM format."] + #[doc = ""] + #[doc = "\\[ location, query ID \\]"] + NotifyTargetMigrationFail( + runtime_types::xcm::VersionedMultiLocation, + ::core::primitive::u64, + ), + #[codec(index = 16)] + #[doc = "Expected query response has been received but the expected querier location placed in"] + #[doc = "storage by this runtime previously cannot be decoded. The query remains registered."] + #[doc = ""] + #[doc = "This is unexpected (since a location placed in storage in a previously executing"] + #[doc = "runtime should be readable prior to query timeout) and dangerous since the possibly"] + #[doc = "valid response will be dropped. Manual governance intervention is probably going to be"] + #[doc = "needed."] + #[doc = ""] + #[doc = "\\[ origin location, id \\]"] + InvalidQuerierVersion( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + ), + #[codec(index = 17)] + #[doc = "Expected query response has been received but the querier location of the response does"] + #[doc = "not match the expected. The query remains registered for a later, valid, response to"] + #[doc = "be received and acted upon."] + #[doc = ""] + #[doc = "\\[ origin location, id, expected querier, maybe actual querier \\]"] + InvalidQuerier( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + ), + #[codec(index = 18)] + #[doc = "A remote has requested XCM version change notification from us and we have honored it."] + #[doc = "A version information message is sent to them and its cost is included."] + #[doc = ""] + #[doc = "\\[ destination location, cost \\]"] + VersionNotifyStarted( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 19)] + #[doc = "We have requested that a remote chain sends us XCM version change notifications."] + #[doc = ""] + #[doc = "\\[ destination location, cost \\]"] + VersionNotifyRequested( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 20)] + #[doc = "We have requested that a remote chain stops sending us XCM version change notifications."] + #[doc = ""] + #[doc = "\\[ destination location, cost \\]"] + VersionNotifyUnrequested( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 21)] + #[doc = "Fees were paid from a location for an operation (often for using `SendXcm`)."] + #[doc = ""] + #[doc = "\\[ paying location, fees \\]"] + FeesPaid( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 22)] + #[doc = "Some assets have been claimed from an asset trap"] + #[doc = ""] + #[doc = "\\[ hash, origin, assets \\]"] + AssetsClaimed( + ::subxt::utils::H256, + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::VersionedMultiAssets, + ), + } + } + } + pub mod polkadot_core_primitives { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct InboundDownwardMessage<_0> { + pub sent_at: _0, + pub msg: ::std::vec::Vec<::core::primitive::u8>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct InboundHrmpMessage<_0> { + pub sent_at: _0, + pub data: ::std::vec::Vec<::core::primitive::u8>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct OutboundHrmpMessage<_0> { + pub recipient: _0, + pub data: ::std::vec::Vec<::core::primitive::u8>, + } + } + pub mod polkadot_parachain { + use super::runtime_types; + pub mod primitives { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct HeadData(pub ::std::vec::Vec<::core::primitive::u8>); + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Id(pub ::core::primitive::u32); + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum XcmpMessageFormat { + #[codec(index = 0)] + ConcatenatedVersionedXcm, + #[codec(index = 1)] + ConcatenatedEncodedBlob, + #[codec(index = 2)] + Signals, + } + } + } + pub mod polkadot_primitives { + use super::runtime_types; + pub mod v4 { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct AbridgedHostConfiguration { + pub max_code_size: ::core::primitive::u32, + pub max_head_data_size: ::core::primitive::u32, + pub max_upward_queue_count: ::core::primitive::u32, + pub max_upward_queue_size: ::core::primitive::u32, + pub max_upward_message_size: ::core::primitive::u32, + pub max_upward_message_num_per_candidate: ::core::primitive::u32, + pub hrmp_max_message_num_per_candidate: ::core::primitive::u32, + pub validation_upgrade_cooldown: ::core::primitive::u32, + pub validation_upgrade_delay: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct AbridgedHrmpChannel { + pub max_capacity: ::core::primitive::u32, + pub max_total_size: ::core::primitive::u32, + pub max_message_size: ::core::primitive::u32, + pub msg_count: ::core::primitive::u32, + pub total_size: ::core::primitive::u32, + pub mqc_head: ::core::option::Option<::subxt::utils::H256>, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct PersistedValidationData<_0, _1> { + pub parent_head: runtime_types::polkadot_parachain::primitives::HeadData, + pub relay_parent_number: _1, + pub relay_parent_storage_root: _0, + pub max_pov_size: _1, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum UpgradeRestriction { + #[codec(index = 0)] + Present, + } + } + } + pub mod rialto_parachain_runtime { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct BridgeRejectObsoleteHeadersAndMessages; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct DummyBridgeRefundMillauMessages; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct Runtime; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum RuntimeCall { + #[codec(index = 0)] + System(runtime_types::frame_system::pallet::Call), + #[codec(index = 1)] + Timestamp(runtime_types::pallet_timestamp::pallet::Call), + #[codec(index = 2)] + Sudo(runtime_types::pallet_sudo::pallet::Call), + #[codec(index = 20)] + ParachainSystem(runtime_types::cumulus_pallet_parachain_system::pallet::Call), + #[codec(index = 30)] + Balances(runtime_types::pallet_balances::pallet::Call), + #[codec(index = 50)] + XcmpQueue(runtime_types::cumulus_pallet_xcmp_queue::pallet::Call), + #[codec(index = 51)] + PolkadotXcm(runtime_types::pallet_xcm::pallet::Call), + #[codec(index = 52)] + CumulusXcm(runtime_types::cumulus_pallet_xcm::pallet::Call), + #[codec(index = 53)] + DmpQueue(runtime_types::cumulus_pallet_dmp_queue::pallet::Call), + #[codec(index = 54)] + BridgeRelayers(runtime_types::pallet_bridge_relayers::pallet::Call), + #[codec(index = 55)] + BridgeMillauGrandpa(runtime_types::pallet_bridge_grandpa::pallet::Call), + #[codec(index = 56)] + BridgeMillauMessages(runtime_types::pallet_bridge_messages::pallet::Call), + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum RuntimeEvent { + #[codec(index = 0)] + System(runtime_types::frame_system::pallet::Event), + #[codec(index = 2)] + Sudo(runtime_types::pallet_sudo::pallet::Event), + #[codec(index = 3)] + TransactionPayment(runtime_types::pallet_transaction_payment::pallet::Event), + #[codec(index = 20)] + ParachainSystem(runtime_types::cumulus_pallet_parachain_system::pallet::Event), + #[codec(index = 30)] + Balances(runtime_types::pallet_balances::pallet::Event), + #[codec(index = 50)] + XcmpQueue(runtime_types::cumulus_pallet_xcmp_queue::pallet::Event), + #[codec(index = 51)] + PolkadotXcm(runtime_types::pallet_xcm::pallet::Event), + #[codec(index = 52)] + CumulusXcm(runtime_types::cumulus_pallet_xcm::pallet::Event), + #[codec(index = 53)] + DmpQueue(runtime_types::cumulus_pallet_dmp_queue::pallet::Event), + #[codec(index = 54)] + BridgeRelayers(runtime_types::pallet_bridge_relayers::pallet::Event), + #[codec(index = 55)] + BridgeMillauGrandpa(runtime_types::pallet_bridge_grandpa::pallet::Event), + #[codec(index = 56)] + BridgeMillauMessages(runtime_types::pallet_bridge_messages::pallet::Event), + } + } + pub mod sp_arithmetic { + use super::runtime_types; + pub mod fixed_point { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: CompactAs, + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct FixedU128(pub ::core::primitive::u128); + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum ArithmeticError { + #[codec(index = 0)] + Underflow, + #[codec(index = 1)] + Overflow, + #[codec(index = 2)] + DivisionByZero, + } + } + pub mod sp_consensus_grandpa { + use super::runtime_types; + pub mod app { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Public(pub runtime_types::sp_core::ed25519::Public); + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Signature(pub runtime_types::sp_core::ed25519::Signature); + } + } + pub mod sp_core { + use super::runtime_types; + pub mod ecdsa { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Signature(pub [::core::primitive::u8; 65usize]); + } + pub mod ed25519 { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Public(pub [::core::primitive::u8; 32usize]); + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Signature(pub [::core::primitive::u8; 64usize]); + } + pub mod sr25519 { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Signature(pub [::core::primitive::u8; 64usize]); + } + } + pub mod sp_runtime { + use super::runtime_types; + pub mod generic { + use super::runtime_types; + pub mod digest { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum DigestItem { + #[codec(index = 6)] + PreRuntime( + [::core::primitive::u8; 4usize], + ::std::vec::Vec<::core::primitive::u8>, + ), + #[codec(index = 4)] + Consensus( + [::core::primitive::u8; 4usize], + ::std::vec::Vec<::core::primitive::u8>, + ), + #[codec(index = 5)] + Seal( + [::core::primitive::u8; 4usize], + ::std::vec::Vec<::core::primitive::u8>, + ), + #[codec(index = 0)] + Other(::std::vec::Vec<::core::primitive::u8>), + #[codec(index = 8)] + RuntimeEnvironmentUpdated, + } + } + pub mod unchecked_extrinsic { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct UncheckedExtrinsic<_0, _1, _2, _3>( + pub ::std::vec::Vec<::core::primitive::u8>, + #[codec(skip)] pub ::core::marker::PhantomData<(_1, _0, _2, _3)>, + ); + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum DispatchError { + #[codec(index = 0)] + Other, + #[codec(index = 1)] + CannotLookup, + #[codec(index = 2)] + BadOrigin, + #[codec(index = 3)] + Module(runtime_types::sp_runtime::ModuleError), + #[codec(index = 4)] + ConsumerRemaining, + #[codec(index = 5)] + NoProviders, + #[codec(index = 6)] + TooManyConsumers, + #[codec(index = 7)] + Token(runtime_types::sp_runtime::TokenError), + #[codec(index = 8)] + Arithmetic(runtime_types::sp_arithmetic::ArithmeticError), + #[codec(index = 9)] + Transactional(runtime_types::sp_runtime::TransactionalError), + #[codec(index = 10)] + Exhausted, + #[codec(index = 11)] + Corruption, + #[codec(index = 12)] + Unavailable, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct ModuleError { + pub index: ::core::primitive::u8, + pub error: [::core::primitive::u8; 4usize], + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum MultiSignature { + #[codec(index = 0)] + Ed25519(runtime_types::sp_core::ed25519::Signature), + #[codec(index = 1)] + Sr25519(runtime_types::sp_core::sr25519::Signature), + #[codec(index = 2)] + Ecdsa(runtime_types::sp_core::ecdsa::Signature), + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum TokenError { + #[codec(index = 0)] + FundsUnavailable, + #[codec(index = 1)] + OnlyProvider, + #[codec(index = 2)] + BelowMinimum, + #[codec(index = 3)] + CannotCreate, + #[codec(index = 4)] + UnknownAsset, + #[codec(index = 5)] + Frozen, + #[codec(index = 6)] + Unsupported, + #[codec(index = 7)] + CannotCreateHold, + #[codec(index = 8)] + NotExpendable, + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum TransactionalError { + #[codec(index = 0)] + LimitReached, + #[codec(index = 1)] + NoLayer, + } + } + pub mod sp_trie { + use super::runtime_types; + pub mod storage_proof { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct StorageProof { + pub trie_nodes: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, + } + } + } + pub mod sp_version { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct RuntimeVersion { + pub spec_name: ::std::string::String, + pub impl_name: ::std::string::String, + pub authoring_version: ::core::primitive::u32, + pub spec_version: ::core::primitive::u32, + pub impl_version: ::core::primitive::u32, + pub apis: + ::std::vec::Vec<([::core::primitive::u8; 8usize], ::core::primitive::u32)>, + pub transaction_version: ::core::primitive::u32, + pub state_version: ::core::primitive::u8, + } + } + pub mod sp_weights { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub struct RuntimeDbWeight { + pub read: ::core::primitive::u64, + pub write: ::core::primitive::u64, + } + } + pub mod xcm { + use super::runtime_types; + pub mod double_encoded { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct DoubleEncoded { + pub encoded: ::std::vec::Vec<::core::primitive::u8>, + } + } + pub mod v2 { + use super::runtime_types; + pub mod junction { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Junction { + #[codec(index = 0)] + Parachain(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 1)] + AccountId32 { + network: runtime_types::xcm::v2::NetworkId, + id: [::core::primitive::u8; 32usize], + }, + #[codec(index = 2)] + AccountIndex64 { + network: runtime_types::xcm::v2::NetworkId, + #[codec(compact)] + index: ::core::primitive::u64, + }, + #[codec(index = 3)] + AccountKey20 { + network: runtime_types::xcm::v2::NetworkId, + key: [::core::primitive::u8; 20usize], + }, + #[codec(index = 4)] + PalletInstance(::core::primitive::u8), + #[codec(index = 5)] + GeneralIndex(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 6)] + GeneralKey( + runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< + ::core::primitive::u8, + >, + ), + #[codec(index = 7)] + OnlyChild, + #[codec(index = 8)] + Plurality { + id: runtime_types::xcm::v2::BodyId, + part: runtime_types::xcm::v2::BodyPart, + }, + } + } + pub mod multiasset { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum AssetId { + #[codec(index = 0)] + Concrete(runtime_types::xcm::v2::multilocation::MultiLocation), + #[codec(index = 1)] + Abstract(::std::vec::Vec<::core::primitive::u8>), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum AssetInstance { + #[codec(index = 0)] + Undefined, + #[codec(index = 1)] + Index(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 2)] + Array4([::core::primitive::u8; 4usize]), + #[codec(index = 3)] + Array8([::core::primitive::u8; 8usize]), + #[codec(index = 4)] + Array16([::core::primitive::u8; 16usize]), + #[codec(index = 5)] + Array32([::core::primitive::u8; 32usize]), + #[codec(index = 6)] + Blob(::std::vec::Vec<::core::primitive::u8>), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Fungibility { + #[codec(index = 0)] + Fungible(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 1)] + NonFungible(runtime_types::xcm::v2::multiasset::AssetInstance), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MultiAsset { + pub id: runtime_types::xcm::v2::multiasset::AssetId, + pub fun: runtime_types::xcm::v2::multiasset::Fungibility, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum MultiAssetFilter { + #[codec(index = 0)] + Definite(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 1)] + Wild(runtime_types::xcm::v2::multiasset::WildMultiAsset), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MultiAssets( + pub ::std::vec::Vec, + ); + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum WildFungibility { + #[codec(index = 0)] + Fungible, + #[codec(index = 1)] + NonFungible, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum WildMultiAsset { + #[codec(index = 0)] + All, + #[codec(index = 1)] + AllOf { + id: runtime_types::xcm::v2::multiasset::AssetId, + fun: runtime_types::xcm::v2::multiasset::WildFungibility, + }, + } + } + pub mod multilocation { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Junctions { + #[codec(index = 0)] + Here, + #[codec(index = 1)] + X1(runtime_types::xcm::v2::junction::Junction), + #[codec(index = 2)] + X2( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 3)] + X3( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 4)] + X4( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 5)] + X5( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 6)] + X6( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 7)] + X7( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 8)] + X8( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MultiLocation { + pub parents: ::core::primitive::u8, + pub interior: runtime_types::xcm::v2::multilocation::Junctions, + } + } + pub mod traits { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Error { + #[codec(index = 0)] + Overflow, + #[codec(index = 1)] + Unimplemented, + #[codec(index = 2)] + UntrustedReserveLocation, + #[codec(index = 3)] + UntrustedTeleportLocation, + #[codec(index = 4)] + MultiLocationFull, + #[codec(index = 5)] + MultiLocationNotInvertible, + #[codec(index = 6)] + BadOrigin, + #[codec(index = 7)] + InvalidLocation, + #[codec(index = 8)] + AssetNotFound, + #[codec(index = 9)] + FailedToTransactAsset, + #[codec(index = 10)] + NotWithdrawable, + #[codec(index = 11)] + LocationCannotHold, + #[codec(index = 12)] + ExceedsMaxMessageSize, + #[codec(index = 13)] + DestinationUnsupported, + #[codec(index = 14)] + Transport, + #[codec(index = 15)] + Unroutable, + #[codec(index = 16)] + UnknownClaim, + #[codec(index = 17)] + FailedToDecode, + #[codec(index = 18)] + MaxWeightInvalid, + #[codec(index = 19)] + NotHoldingFees, + #[codec(index = 20)] + TooExpensive, + #[codec(index = 21)] + Trap(::core::primitive::u64), + #[codec(index = 22)] + UnhandledXcmVersion, + #[codec(index = 23)] + WeightLimitReached(::core::primitive::u64), + #[codec(index = 24)] + Barrier, + #[codec(index = 25)] + WeightNotComputable, + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum BodyId { + #[codec(index = 0)] + Unit, + #[codec(index = 1)] + Named( + runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< + ::core::primitive::u8, + >, + ), + #[codec(index = 2)] + Index(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 3)] + Executive, + #[codec(index = 4)] + Technical, + #[codec(index = 5)] + Legislative, + #[codec(index = 6)] + Judicial, + #[codec(index = 7)] + Defense, + #[codec(index = 8)] + Administration, + #[codec(index = 9)] + Treasury, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum BodyPart { + #[codec(index = 0)] + Voice, + #[codec(index = 1)] + Members { + #[codec(compact)] + count: ::core::primitive::u32, + }, + #[codec(index = 2)] + Fraction { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + #[codec(index = 3)] + AtLeastProportion { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + #[codec(index = 4)] + MoreThanProportion { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Instruction { + #[codec(index = 0)] + WithdrawAsset(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 1)] + ReserveAssetDeposited(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 2)] + ReceiveTeleportedAsset(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 3)] + QueryResponse { + #[codec(compact)] + query_id: ::core::primitive::u64, + response: runtime_types::xcm::v2::Response, + #[codec(compact)] + max_weight: ::core::primitive::u64, + }, + #[codec(index = 4)] + TransferAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssets, + beneficiary: runtime_types::xcm::v2::multilocation::MultiLocation, + }, + #[codec(index = 5)] + TransferReserveAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssets, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + xcm: runtime_types::xcm::v2::Xcm, + }, + #[codec(index = 6)] + Transact { + origin_type: runtime_types::xcm::v2::OriginKind, + #[codec(compact)] + require_weight_at_most: ::core::primitive::u64, + call: runtime_types::xcm::double_encoded::DoubleEncoded, + }, + #[codec(index = 7)] + HrmpNewChannelOpenRequest { + #[codec(compact)] + sender: ::core::primitive::u32, + #[codec(compact)] + max_message_size: ::core::primitive::u32, + #[codec(compact)] + max_capacity: ::core::primitive::u32, + }, + #[codec(index = 8)] + HrmpChannelAccepted { + #[codec(compact)] + recipient: ::core::primitive::u32, + }, + #[codec(index = 9)] + HrmpChannelClosing { + #[codec(compact)] + initiator: ::core::primitive::u32, + #[codec(compact)] + sender: ::core::primitive::u32, + #[codec(compact)] + recipient: ::core::primitive::u32, + }, + #[codec(index = 10)] + ClearOrigin, + #[codec(index = 11)] + DescendOrigin(runtime_types::xcm::v2::multilocation::Junctions), + #[codec(index = 12)] + ReportError { + #[codec(compact)] + query_id: ::core::primitive::u64, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + #[codec(compact)] + max_response_weight: ::core::primitive::u64, + }, + #[codec(index = 13)] + DepositAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + #[codec(compact)] + max_assets: ::core::primitive::u32, + beneficiary: runtime_types::xcm::v2::multilocation::MultiLocation, + }, + #[codec(index = 14)] + DepositReserveAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + #[codec(compact)] + max_assets: ::core::primitive::u32, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + xcm: runtime_types::xcm::v2::Xcm, + }, + #[codec(index = 15)] + ExchangeAsset { + give: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + receive: runtime_types::xcm::v2::multiasset::MultiAssets, + }, + #[codec(index = 16)] + InitiateReserveWithdraw { + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + reserve: runtime_types::xcm::v2::multilocation::MultiLocation, + xcm: runtime_types::xcm::v2::Xcm, + }, + #[codec(index = 17)] + InitiateTeleport { + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + xcm: runtime_types::xcm::v2::Xcm, + }, + #[codec(index = 18)] + QueryHolding { + #[codec(compact)] + query_id: ::core::primitive::u64, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + #[codec(compact)] + max_response_weight: ::core::primitive::u64, + }, + #[codec(index = 19)] + BuyExecution { + fees: runtime_types::xcm::v2::multiasset::MultiAsset, + weight_limit: runtime_types::xcm::v2::WeightLimit, + }, + #[codec(index = 20)] + RefundSurplus, + #[codec(index = 21)] + SetErrorHandler(runtime_types::xcm::v2::Xcm), + #[codec(index = 22)] + SetAppendix(runtime_types::xcm::v2::Xcm), + #[codec(index = 23)] + ClearError, + #[codec(index = 24)] + ClaimAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssets, + ticket: runtime_types::xcm::v2::multilocation::MultiLocation, + }, + #[codec(index = 25)] + Trap(#[codec(compact)] ::core::primitive::u64), + #[codec(index = 26)] + SubscribeVersion { + #[codec(compact)] + query_id: ::core::primitive::u64, + #[codec(compact)] + max_response_weight: ::core::primitive::u64, + }, + #[codec(index = 27)] + UnsubscribeVersion, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum NetworkId { + #[codec(index = 0)] + Any, + #[codec(index = 1)] + Named( + runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< + ::core::primitive::u8, + >, + ), + #[codec(index = 2)] + Polkadot, + #[codec(index = 3)] + Kusama, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum OriginKind { + #[codec(index = 0)] + Native, + #[codec(index = 1)] + SovereignAccount, + #[codec(index = 2)] + Superuser, + #[codec(index = 3)] + Xcm, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Response { + #[codec(index = 0)] + Null, + #[codec(index = 1)] + Assets(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 2)] + ExecutionResult( + ::core::option::Option<( + ::core::primitive::u32, + runtime_types::xcm::v2::traits::Error, + )>, + ), + #[codec(index = 3)] + Version(::core::primitive::u32), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum WeightLimit { + #[codec(index = 0)] + Unlimited, + #[codec(index = 1)] + Limited(#[codec(compact)] ::core::primitive::u64), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Xcm(pub ::std::vec::Vec); + } + pub mod v3 { + use super::runtime_types; + pub mod junction { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum BodyId { + #[codec(index = 0)] + Unit, + #[codec(index = 1)] + Moniker([::core::primitive::u8; 4usize]), + #[codec(index = 2)] + Index(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 3)] + Executive, + #[codec(index = 4)] + Technical, + #[codec(index = 5)] + Legislative, + #[codec(index = 6)] + Judicial, + #[codec(index = 7)] + Defense, + #[codec(index = 8)] + Administration, + #[codec(index = 9)] + Treasury, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum BodyPart { + #[codec(index = 0)] + Voice, + #[codec(index = 1)] + Members { + #[codec(compact)] + count: ::core::primitive::u32, + }, + #[codec(index = 2)] + Fraction { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + #[codec(index = 3)] + AtLeastProportion { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + #[codec(index = 4)] + MoreThanProportion { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Junction { + #[codec(index = 0)] + Parachain(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 1)] + AccountId32 { + network: + ::core::option::Option, + id: [::core::primitive::u8; 32usize], + }, + #[codec(index = 2)] + AccountIndex64 { + network: + ::core::option::Option, + #[codec(compact)] + index: ::core::primitive::u64, + }, + #[codec(index = 3)] + AccountKey20 { + network: + ::core::option::Option, + key: [::core::primitive::u8; 20usize], + }, + #[codec(index = 4)] + PalletInstance(::core::primitive::u8), + #[codec(index = 5)] + GeneralIndex(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 6)] + GeneralKey { + length: ::core::primitive::u8, + data: [::core::primitive::u8; 32usize], + }, + #[codec(index = 7)] + OnlyChild, + #[codec(index = 8)] + Plurality { + id: runtime_types::xcm::v3::junction::BodyId, + part: runtime_types::xcm::v3::junction::BodyPart, + }, + #[codec(index = 9)] + GlobalConsensus(runtime_types::xcm::v3::junction::NetworkId), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum NetworkId { + #[codec(index = 0)] + ByGenesis([::core::primitive::u8; 32usize]), + #[codec(index = 1)] + ByFork { + block_number: ::core::primitive::u64, + block_hash: [::core::primitive::u8; 32usize], + }, + #[codec(index = 2)] + Polkadot, + #[codec(index = 3)] + Kusama, + #[codec(index = 4)] + Westend, + #[codec(index = 5)] + Rococo, + #[codec(index = 6)] + Wococo, + #[codec(index = 7)] + Ethereum { + #[codec(compact)] + chain_id: ::core::primitive::u64, + }, + #[codec(index = 8)] + BitcoinCore, + #[codec(index = 9)] + BitcoinCash, + } + } + pub mod junctions { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Junctions { + #[codec(index = 0)] + Here, + #[codec(index = 1)] + X1(runtime_types::xcm::v3::junction::Junction), + #[codec(index = 2)] + X2( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 3)] + X3( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 4)] + X4( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 5)] + X5( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 6)] + X6( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 7)] + X7( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 8)] + X8( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + } + } + pub mod multiasset { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum AssetId { + #[codec(index = 0)] + Concrete(runtime_types::xcm::v3::multilocation::MultiLocation), + #[codec(index = 1)] + Abstract([::core::primitive::u8; 32usize]), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum AssetInstance { + #[codec(index = 0)] + Undefined, + #[codec(index = 1)] + Index(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 2)] + Array4([::core::primitive::u8; 4usize]), + #[codec(index = 3)] + Array8([::core::primitive::u8; 8usize]), + #[codec(index = 4)] + Array16([::core::primitive::u8; 16usize]), + #[codec(index = 5)] + Array32([::core::primitive::u8; 32usize]), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Fungibility { + #[codec(index = 0)] + Fungible(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 1)] + NonFungible(runtime_types::xcm::v3::multiasset::AssetInstance), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MultiAsset { + pub id: runtime_types::xcm::v3::multiasset::AssetId, + pub fun: runtime_types::xcm::v3::multiasset::Fungibility, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum MultiAssetFilter { + #[codec(index = 0)] + Definite(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 1)] + Wild(runtime_types::xcm::v3::multiasset::WildMultiAsset), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MultiAssets( + pub ::std::vec::Vec, + ); + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum WildFungibility { + #[codec(index = 0)] + Fungible, + #[codec(index = 1)] + NonFungible, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum WildMultiAsset { + #[codec(index = 0)] + All, + #[codec(index = 1)] + AllOf { + id: runtime_types::xcm::v3::multiasset::AssetId, + fun: runtime_types::xcm::v3::multiasset::WildFungibility, + }, + #[codec(index = 2)] + AllCounted(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 3)] + AllOfCounted { + id: runtime_types::xcm::v3::multiasset::AssetId, + fun: runtime_types::xcm::v3::multiasset::WildFungibility, + #[codec(compact)] + count: ::core::primitive::u32, + }, + } + } + pub mod multilocation { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct MultiLocation { + pub parents: ::core::primitive::u8, + pub interior: runtime_types::xcm::v3::junctions::Junctions, + } + } + pub mod traits { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Error { + #[codec(index = 0)] + Overflow, + #[codec(index = 1)] + Unimplemented, + #[codec(index = 2)] + UntrustedReserveLocation, + #[codec(index = 3)] + UntrustedTeleportLocation, + #[codec(index = 4)] + LocationFull, + #[codec(index = 5)] + LocationNotInvertible, + #[codec(index = 6)] + BadOrigin, + #[codec(index = 7)] + InvalidLocation, + #[codec(index = 8)] + AssetNotFound, + #[codec(index = 9)] + FailedToTransactAsset, + #[codec(index = 10)] + NotWithdrawable, + #[codec(index = 11)] + LocationCannotHold, + #[codec(index = 12)] + ExceedsMaxMessageSize, + #[codec(index = 13)] + DestinationUnsupported, + #[codec(index = 14)] + Transport, + #[codec(index = 15)] + Unroutable, + #[codec(index = 16)] + UnknownClaim, + #[codec(index = 17)] + FailedToDecode, + #[codec(index = 18)] + MaxWeightInvalid, + #[codec(index = 19)] + NotHoldingFees, + #[codec(index = 20)] + TooExpensive, + #[codec(index = 21)] + Trap(::core::primitive::u64), + #[codec(index = 22)] + ExpectationFalse, + #[codec(index = 23)] + PalletNotFound, + #[codec(index = 24)] + NameMismatch, + #[codec(index = 25)] + VersionIncompatible, + #[codec(index = 26)] + HoldingWouldOverflow, + #[codec(index = 27)] + ExportError, + #[codec(index = 28)] + ReanchorFailed, + #[codec(index = 29)] + NoDeal, + #[codec(index = 30)] + FeesNotMet, + #[codec(index = 31)] + LockError, + #[codec(index = 32)] + NoPermission, + #[codec(index = 33)] + Unanchored, + #[codec(index = 34)] + NotDepositable, + #[codec(index = 35)] + UnhandledXcmVersion, + #[codec(index = 36)] + WeightLimitReached(::sp_weights::Weight), + #[codec(index = 37)] + Barrier, + #[codec(index = 38)] + WeightNotComputable, + #[codec(index = 39)] + ExceedsStackLimit, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Outcome { + #[codec(index = 0)] + Complete(::sp_weights::Weight), + #[codec(index = 1)] + Incomplete(::sp_weights::Weight, runtime_types::xcm::v3::traits::Error), + #[codec(index = 2)] + Error(runtime_types::xcm::v3::traits::Error), + } + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Instruction { + #[codec(index = 0)] + WithdrawAsset(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 1)] + ReserveAssetDeposited(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 2)] + ReceiveTeleportedAsset(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 3)] + QueryResponse { + #[codec(compact)] + query_id: ::core::primitive::u64, + response: runtime_types::xcm::v3::Response, + max_weight: ::sp_weights::Weight, + querier: ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + }, + #[codec(index = 4)] + TransferAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssets, + beneficiary: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 5)] + TransferReserveAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssets, + dest: runtime_types::xcm::v3::multilocation::MultiLocation, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 6)] + Transact { + origin_kind: runtime_types::xcm::v2::OriginKind, + require_weight_at_most: ::sp_weights::Weight, + call: runtime_types::xcm::double_encoded::DoubleEncoded, + }, + #[codec(index = 7)] + HrmpNewChannelOpenRequest { + #[codec(compact)] + sender: ::core::primitive::u32, + #[codec(compact)] + max_message_size: ::core::primitive::u32, + #[codec(compact)] + max_capacity: ::core::primitive::u32, + }, + #[codec(index = 8)] + HrmpChannelAccepted { + #[codec(compact)] + recipient: ::core::primitive::u32, + }, + #[codec(index = 9)] + HrmpChannelClosing { + #[codec(compact)] + initiator: ::core::primitive::u32, + #[codec(compact)] + sender: ::core::primitive::u32, + #[codec(compact)] + recipient: ::core::primitive::u32, + }, + #[codec(index = 10)] + ClearOrigin, + #[codec(index = 11)] + DescendOrigin(runtime_types::xcm::v3::junctions::Junctions), + #[codec(index = 12)] + ReportError(runtime_types::xcm::v3::QueryResponseInfo), + #[codec(index = 13)] + DepositAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + beneficiary: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 14)] + DepositReserveAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + dest: runtime_types::xcm::v3::multilocation::MultiLocation, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 15)] + ExchangeAsset { + give: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + want: runtime_types::xcm::v3::multiasset::MultiAssets, + maximal: ::core::primitive::bool, + }, + #[codec(index = 16)] + InitiateReserveWithdraw { + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + reserve: runtime_types::xcm::v3::multilocation::MultiLocation, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 17)] + InitiateTeleport { + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + dest: runtime_types::xcm::v3::multilocation::MultiLocation, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 18)] + ReportHolding { + response_info: runtime_types::xcm::v3::QueryResponseInfo, + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + }, + #[codec(index = 19)] + BuyExecution { + fees: runtime_types::xcm::v3::multiasset::MultiAsset, + weight_limit: runtime_types::xcm::v3::WeightLimit, + }, + #[codec(index = 20)] + RefundSurplus, + #[codec(index = 21)] + SetErrorHandler(runtime_types::xcm::v3::Xcm), + #[codec(index = 22)] + SetAppendix(runtime_types::xcm::v3::Xcm), + #[codec(index = 23)] + ClearError, + #[codec(index = 24)] + ClaimAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssets, + ticket: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 25)] + Trap(#[codec(compact)] ::core::primitive::u64), + #[codec(index = 26)] + SubscribeVersion { + #[codec(compact)] + query_id: ::core::primitive::u64, + max_response_weight: ::sp_weights::Weight, + }, + #[codec(index = 27)] + UnsubscribeVersion, + #[codec(index = 28)] + BurnAsset(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 29)] + ExpectAsset(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 30)] + ExpectOrigin( + ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + ), + #[codec(index = 31)] + ExpectError( + ::core::option::Option<( + ::core::primitive::u32, + runtime_types::xcm::v3::traits::Error, + )>, + ), + #[codec(index = 32)] + ExpectTransactStatus(runtime_types::xcm::v3::MaybeErrorCode), + #[codec(index = 33)] + QueryPallet { + module_name: ::std::vec::Vec<::core::primitive::u8>, + response_info: runtime_types::xcm::v3::QueryResponseInfo, + }, + #[codec(index = 34)] + ExpectPallet { + #[codec(compact)] + index: ::core::primitive::u32, + name: ::std::vec::Vec<::core::primitive::u8>, + module_name: ::std::vec::Vec<::core::primitive::u8>, + #[codec(compact)] + crate_major: ::core::primitive::u32, + #[codec(compact)] + min_crate_minor: ::core::primitive::u32, + }, + #[codec(index = 35)] + ReportTransactStatus(runtime_types::xcm::v3::QueryResponseInfo), + #[codec(index = 36)] + ClearTransactStatus, + #[codec(index = 37)] + UniversalOrigin(runtime_types::xcm::v3::junction::Junction), + #[codec(index = 38)] + ExportMessage { + network: runtime_types::xcm::v3::junction::NetworkId, + destination: runtime_types::xcm::v3::junctions::Junctions, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 39)] + LockAsset { + asset: runtime_types::xcm::v3::multiasset::MultiAsset, + unlocker: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 40)] + UnlockAsset { + asset: runtime_types::xcm::v3::multiasset::MultiAsset, + target: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 41)] + NoteUnlockable { + asset: runtime_types::xcm::v3::multiasset::MultiAsset, + owner: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 42)] + RequestUnlock { + asset: runtime_types::xcm::v3::multiasset::MultiAsset, + locker: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 43)] + SetFeesMode { jit_withdraw: ::core::primitive::bool }, + #[codec(index = 44)] + SetTopic([::core::primitive::u8; 32usize]), + #[codec(index = 45)] + ClearTopic, + #[codec(index = 46)] + AliasOrigin(runtime_types::xcm::v3::multilocation::MultiLocation), + #[codec(index = 47)] + UnpaidExecution { + weight_limit: runtime_types::xcm::v3::WeightLimit, + check_origin: ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum MaybeErrorCode { + #[codec(index = 0)] + Success, + #[codec(index = 1)] + Error( + runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + ), + #[codec(index = 2)] + TruncatedError( + runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + ), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct PalletInfo { + #[codec(compact)] + pub index: ::core::primitive::u32, + pub name: runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + pub module_name: runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + #[codec(compact)] + pub major: ::core::primitive::u32, + #[codec(compact)] + pub minor: ::core::primitive::u32, + #[codec(compact)] + pub patch: ::core::primitive::u32, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct QueryResponseInfo { + pub destination: runtime_types::xcm::v3::multilocation::MultiLocation, + #[codec(compact)] + pub query_id: ::core::primitive::u64, + pub max_weight: ::sp_weights::Weight, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum Response { + #[codec(index = 0)] + Null, + #[codec(index = 1)] + Assets(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 2)] + ExecutionResult( + ::core::option::Option<( + ::core::primitive::u32, + runtime_types::xcm::v3::traits::Error, + )>, + ), + #[codec(index = 3)] + Version(::core::primitive::u32), + #[codec(index = 4)] + PalletsInfo( + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::xcm::v3::PalletInfo, + >, + ), + #[codec(index = 5)] + DispatchResult(runtime_types::xcm::v3::MaybeErrorCode), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub enum WeightLimit { + #[codec(index = 0)] + Unlimited, + #[codec(index = 1)] + Limited(::sp_weights::Weight), + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + Clone, + Debug, + )] + pub struct Xcm(pub ::std::vec::Vec); + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum VersionedMultiAssets { + #[codec(index = 1)] + V2(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 3)] + V3(runtime_types::xcm::v3::multiasset::MultiAssets), + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum VersionedMultiLocation { + #[codec(index = 1)] + V2(runtime_types::xcm::v2::multilocation::MultiLocation), + #[codec(index = 3)] + V3(runtime_types::xcm::v3::multilocation::MultiLocation), + } + #[derive( + :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, + )] + pub enum VersionedXcm { + #[codec(index = 2)] + V2(runtime_types::xcm::v2::Xcm), + #[codec(index = 3)] + V3(runtime_types::xcm::v3::Xcm), + } + } + } + #[doc = r" The default error type returned when there is a runtime issue,"] + #[doc = r" exposed here for ease of use."] + pub type DispatchError = runtime_types::sp_runtime::DispatchError; + pub fn constants() -> ConstantsApi { + ConstantsApi + } + pub fn storage() -> StorageApi { + StorageApi + } + pub fn tx() -> TransactionApi { + TransactionApi + } + pub struct ConstantsApi; + impl ConstantsApi { + pub fn system(&self) -> system::constants::ConstantsApi { + system::constants::ConstantsApi + } + pub fn timestamp(&self) -> timestamp::constants::ConstantsApi { + timestamp::constants::ConstantsApi + } + pub fn transaction_payment(&self) -> transaction_payment::constants::ConstantsApi { + transaction_payment::constants::ConstantsApi + } + pub fn balances(&self) -> balances::constants::ConstantsApi { + balances::constants::ConstantsApi + } + pub fn bridge_millau_grandpa(&self) -> bridge_millau_grandpa::constants::ConstantsApi { + bridge_millau_grandpa::constants::ConstantsApi + } + pub fn bridge_millau_messages(&self) -> bridge_millau_messages::constants::ConstantsApi { + bridge_millau_messages::constants::ConstantsApi + } + } + pub struct StorageApi; + impl StorageApi { + pub fn system(&self) -> system::storage::StorageApi { + system::storage::StorageApi + } + pub fn timestamp(&self) -> timestamp::storage::StorageApi { + timestamp::storage::StorageApi + } + pub fn sudo(&self) -> sudo::storage::StorageApi { + sudo::storage::StorageApi + } + pub fn transaction_payment(&self) -> transaction_payment::storage::StorageApi { + transaction_payment::storage::StorageApi + } + pub fn parachain_system(&self) -> parachain_system::storage::StorageApi { + parachain_system::storage::StorageApi + } + pub fn parachain_info(&self) -> parachain_info::storage::StorageApi { + parachain_info::storage::StorageApi + } + pub fn balances(&self) -> balances::storage::StorageApi { + balances::storage::StorageApi + } + pub fn xcmp_queue(&self) -> xcmp_queue::storage::StorageApi { + xcmp_queue::storage::StorageApi + } + pub fn dmp_queue(&self) -> dmp_queue::storage::StorageApi { + dmp_queue::storage::StorageApi + } + pub fn bridge_relayers(&self) -> bridge_relayers::storage::StorageApi { + bridge_relayers::storage::StorageApi + } + pub fn bridge_millau_grandpa(&self) -> bridge_millau_grandpa::storage::StorageApi { + bridge_millau_grandpa::storage::StorageApi + } + pub fn bridge_millau_messages(&self) -> bridge_millau_messages::storage::StorageApi { + bridge_millau_messages::storage::StorageApi + } + } + pub struct TransactionApi; + impl TransactionApi { + pub fn system(&self) -> system::calls::TransactionApi { + system::calls::TransactionApi + } + pub fn timestamp(&self) -> timestamp::calls::TransactionApi { + timestamp::calls::TransactionApi + } + pub fn sudo(&self) -> sudo::calls::TransactionApi { + sudo::calls::TransactionApi + } + pub fn parachain_system(&self) -> parachain_system::calls::TransactionApi { + parachain_system::calls::TransactionApi + } + pub fn balances(&self) -> balances::calls::TransactionApi { + balances::calls::TransactionApi + } + pub fn xcmp_queue(&self) -> xcmp_queue::calls::TransactionApi { + xcmp_queue::calls::TransactionApi + } + pub fn polkadot_xcm(&self) -> polkadot_xcm::calls::TransactionApi { + polkadot_xcm::calls::TransactionApi + } + pub fn cumulus_xcm(&self) -> cumulus_xcm::calls::TransactionApi { + cumulus_xcm::calls::TransactionApi + } + pub fn dmp_queue(&self) -> dmp_queue::calls::TransactionApi { + dmp_queue::calls::TransactionApi + } + pub fn bridge_relayers(&self) -> bridge_relayers::calls::TransactionApi { + bridge_relayers::calls::TransactionApi + } + pub fn bridge_millau_grandpa(&self) -> bridge_millau_grandpa::calls::TransactionApi { + bridge_millau_grandpa::calls::TransactionApi + } + pub fn bridge_millau_messages(&self) -> bridge_millau_messages::calls::TransactionApi { + bridge_millau_messages::calls::TransactionApi + } + } + #[doc = r" check whether the Client you are using is aligned with the statically generated codegen."] + pub fn validate_codegen>( + client: &C, + ) -> Result<(), ::subxt::error::MetadataError> { + let runtime_metadata_hash = client.metadata().metadata_hash(&PALLETS); + if runtime_metadata_hash != + [ + 244u8, 26u8, 245u8, 96u8, 241u8, 56u8, 22u8, 163u8, 219u8, 209u8, 103u8, 161u8, + 138u8, 242u8, 33u8, 114u8, 162u8, 107u8, 1u8, 216u8, 115u8, 116u8, 164u8, 126u8, + 81u8, 51u8, 88u8, 36u8, 180u8, 113u8, 18u8, 58u8, + ] { + Err(::subxt::error::MetadataError::IncompatibleMetadata) + } else { + Ok(()) + } + } +} diff --git a/relays/client-rialto-parachain/src/lib.rs b/relays/client-rialto-parachain/src/lib.rs new file mode 100644 index 00000000000..ddb54fbefc0 --- /dev/null +++ b/relays/client-rialto-parachain/src/lib.rs @@ -0,0 +1,138 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Rialto-Substrate chain. + +pub mod codegen_runtime; + +use bp_bridge_hub_cumulus::BridgeHubSignedExtension; +use bp_messages::MessageNonce; +use bp_runtime::ChainId; +use codec::Encode; +use relay_substrate_client::{ + Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, Error as SubstrateError, + SignParam, UnderlyingChainProvider, UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount, MultiAddress}; +use std::time::Duration; + +pub use codegen_runtime::api::runtime_types; + +pub type RuntimeCall = runtime_types::rialto_parachain_runtime::RuntimeCall; +pub type SudoCall = runtime_types::pallet_sudo::pallet::Call; +pub type BridgeGrandpaCall = runtime_types::pallet_bridge_grandpa::pallet::Call; +pub type BridgeMessagesCall = runtime_types::pallet_bridge_messages::pallet::Call; + +/// The address format for describing accounts. +pub type Address = MultiAddress; + +/// Rialto parachain definition +#[derive(Debug, Clone, Copy)] +pub struct RialtoParachain; + +impl UnderlyingChainProvider for RialtoParachain { + type Chain = bp_rialto_parachain::RialtoParachain; +} + +impl Chain for RialtoParachain { + const ID: ChainId = bp_runtime::RIALTO_PARACHAIN_CHAIN_ID; + const NAME: &'static str = "RialtoParachain"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_rialto_parachain::BEST_FINALIZED_RIALTO_PARACHAIN_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); + + type SignedBlock = bp_polkadot_core::SignedBlock; + type Call = runtime_types::rialto_parachain_runtime::RuntimeCall; +} + +impl ChainWithBalances for RialtoParachain { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + let key = codegen_runtime::api::storage().system().account(account_id); + StorageKey(key.to_bytes()) + } +} + +impl ChainWithMessages for RialtoParachain { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_rialto_parachain::WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME; + // TODO (https://github.com/paritytech/parity-bridges-common/issues/1692): change the name + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = Some("BridgeRelayers"); + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_rialto_parachain::TO_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_rialto_parachain::FROM_RIALTO_PARACHAIN_MESSAGE_DETAILS_METHOD; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_rialto_parachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_rialto_parachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; +} + +impl ChainWithTransactions for RialtoParachain { + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = + bp_polkadot_core::UncheckedExtrinsic; + + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result { + let raw_payload = SignedPayload::new( + unsigned.call, + bp_rialto_parachain::SignedExtension::from_params( + param.spec_version, + param.transaction_version, + unsigned.era, + param.genesis_hash, + unsigned.nonce, + unsigned.tip, + ), + )?; + + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(Self::SignedTransaction::new_signed( + call, + signer.into_account().into(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| *address == Address::Id(signer.public().into())) + .unwrap_or(false) + } + + fn parse_transaction(tx: Self::SignedTransaction) -> Option> { + let extra = &tx.signature.as_ref()?.2; + Some(UnsignedTransaction::new(tx.function, extra.nonce()).tip(extra.tip())) + } +} + +/// RialtoParachain signing params. +pub type SigningParams = sp_core::sr25519::Pair; + +/// RialtoParachain header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; diff --git a/relays/client-rialto/Cargo.toml b/relays/client-rialto/Cargo.toml new file mode 100644 index 00000000000..c1ae0dfabe7 --- /dev/null +++ b/relays/client-rialto/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "relay-rialto-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5" } +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-messages = { path = "../../primitives/messages" } +bp-rialto = { path = "../../primitives/chain-rialto" } +bp-runtime = { path = "../../primitives/runtime" } +rialto-runtime = { path = "../../bin/rialto/runtime" } + +# Substrate Dependencies + +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-rialto/src/lib.rs b/relays/client-rialto/src/lib.rs new file mode 100644 index 00000000000..f8f696068fe --- /dev/null +++ b/relays/client-rialto/src/lib.rs @@ -0,0 +1,184 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Rialto-Substrate chain. + +use bp_messages::MessageNonce; +use bp_runtime::ChainId; +use codec::{Compact, Decode, Encode}; +use relay_substrate_client::{ + BalanceOf, Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, + Error as SubstrateError, IndexOf, RelayChain, SignParam, UnderlyingChainProvider, + UnsignedTransaction, +}; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; +use std::time::Duration; + +/// Rialto header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Rialto chain definition +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Rialto; + +impl UnderlyingChainProvider for Rialto { + type Chain = bp_rialto::Rialto; +} + +impl Chain for Rialto { + const ID: ChainId = bp_runtime::RIALTO_CHAIN_ID; + const NAME: &'static str = "Rialto"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); + + type SignedBlock = rialto_runtime::SignedBlock; + type Call = rialto_runtime::RuntimeCall; +} + +impl RelayChain for Rialto { + const PARAS_PALLET_NAME: &'static str = bp_rialto::PARAS_PALLET_NAME; + const PARACHAINS_FINALITY_PALLET_NAME: &'static str = + bp_rialto::WITH_RIALTO_BRIDGE_PARAS_PALLET_NAME; +} + +impl ChainWithMessages for Rialto { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME; + // TODO (https://github.com/paritytech/parity-bridges-common/issues/1692): change the name + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = Some("BridgeRelayers"); + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_rialto::TO_RIALTO_MESSAGE_DETAILS_METHOD; + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_rialto::FROM_RIALTO_MESSAGE_DETAILS_METHOD; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; +} + +impl ChainWithBalances for Rialto { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + use frame_support::storage::generator::StorageMap; + StorageKey(frame_system::Account::::storage_map_final_key( + account_id, + )) + } +} + +impl ChainWithTransactions for Rialto { + type AccountKeyPair = sp_core::sr25519::Pair; + type SignedTransaction = rialto_runtime::UncheckedExtrinsic; + + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result { + let raw_payload = SignedPayload::from_raw( + unsigned.call.clone(), + ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(unsigned.era.frame_era()), + frame_system::CheckNonce::::from(unsigned.nonce), + frame_system::CheckWeight::::new(), + pallet_transaction_payment::ChargeTransactionPayment::::from(unsigned.tip), + ), + ( + (), + param.spec_version, + param.transaction_version, + param.genesis_hash, + unsigned.era.signed_payload(param.genesis_hash), + (), + (), + (), + ), + ); + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); + let (call, extra, _) = raw_payload.deconstruct(); + + Ok(rialto_runtime::UncheckedExtrinsic::new_signed( + call.into_decoded()?, + signer.into_account().into(), + signature.into(), + extra, + )) + } + + fn is_signed(tx: &Self::SignedTransaction) -> bool { + tx.signature.is_some() + } + + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool { + tx.signature + .as_ref() + .map(|(address, _, _)| *address == rialto_runtime::Address::Id(signer.public().into())) + .unwrap_or(false) + } + + fn parse_transaction(tx: Self::SignedTransaction) -> Option> { + let extra = &tx.signature.as_ref()?.2; + Some( + UnsignedTransaction::new( + tx.function.into(), + Compact::>::decode(&mut &extra.5.encode()[..]).ok()?.into(), + ) + .tip(Compact::>::decode(&mut &extra.7.encode()[..]).ok()?.into()), + ) + } +} + +/// Rialto signing params. +pub type SigningParams = sp_core::sr25519::Pair; + +/// Rialto header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + +#[cfg(test)] +mod tests { + use super::*; + use relay_substrate_client::TransactionEra; + + #[test] + fn parse_transaction_works() { + let unsigned = UnsignedTransaction { + call: rialto_runtime::RuntimeCall::System(rialto_runtime::SystemCall::remark { + remark: b"Hello world!".to_vec(), + }) + .into(), + nonce: 777, + tip: 888, + era: TransactionEra::immortal(), + }; + let signed_transaction = Rialto::sign_transaction( + SignParam { + spec_version: 42, + transaction_version: 50000, + genesis_hash: [42u8; 32].into(), + signer: sp_core::sr25519::Pair::from_seed_slice(&[1u8; 32]).unwrap(), + }, + unsigned.clone(), + ) + .unwrap(); + let parsed_transaction = Rialto::parse_transaction(signed_transaction).unwrap(); + assert_eq!(parsed_transaction, unsigned); + } +} diff --git a/relays/client-rococo/Cargo.toml b/relays/client-rococo/Cargo.toml new file mode 100644 index 00000000000..2a5b556dce2 --- /dev/null +++ b/relays/client-rococo/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "relay-rococo-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-rococo = { path = "../../primitives/chain-rococo" } +bp-runtime = { path = "../../primitives/runtime" } + +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-rococo/src/lib.rs b/relays/client-rococo/src/lib.rs new file mode 100644 index 00000000000..ede1bf1ded4 --- /dev/null +++ b/relays/client-rococo/src/lib.rs @@ -0,0 +1,58 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Rococo-Substrate chain. + +use bp_runtime::ChainId; +use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider}; +use sp_core::storage::StorageKey; +use std::time::Duration; + +/// Rococo header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Rococo header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + +/// Rococo chain definition +#[derive(Debug, Clone, Copy)] +pub struct Rococo; + +impl UnderlyingChainProvider for Rococo { + type Chain = bp_rococo::Rococo; +} + +impl Chain for Rococo { + const ID: ChainId = bp_runtime::ROCOCO_CHAIN_ID; + const NAME: &'static str = "Rococo"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type SignedBlock = bp_rococo::SignedBlock; + type Call = (); +} + +impl ChainWithBalances for Rococo { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + bp_rococo::AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +impl RelayChain for Rococo { + const PARAS_PALLET_NAME: &'static str = bp_rococo::PARAS_PALLET_NAME; + const PARACHAINS_FINALITY_PALLET_NAME: &'static str = "BridgeRococoParachain"; +} diff --git a/relays/client-substrate/Cargo.toml b/relays/client-substrate/Cargo.toml new file mode 100644 index 00000000000..2a79832bf9d --- /dev/null +++ b/relays/client-substrate/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "relay-substrate-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +async-std = { version = "1.6.5", features = ["attributes"] } +async-trait = "0.1" +codec = { package = "parity-scale-codec", version = "3.1.5" } +futures = "0.3.28" +jsonrpsee = { version = "0.16", features = ["macros", "ws-client"] } +log = "0.4.17" +num-traits = "0.2" +rand = "0.8" +scale-info = { version = "2.5.0", features = ["derive"] } +tokio = { version = "1.27", features = ["rt-multi-thread"] } +thiserror = "1.0.40" + +# Bridge dependencies + +bp-header-chain = { path = "../../primitives/header-chain" } +bp-messages = { path = "../../primitives/messages" } +bp-polkadot-core = { path = "../../primitives/polkadot-core" } +bp-runtime = { path = "../../primitives/runtime" } +pallet-bridge-messages = { path = "../../modules/messages" } +finality-relay = { path = "../finality" } +relay-utils = { path = "../utils" } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-utility = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-chain-spec = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-rpc-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-transaction-pool-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-version = { git = "https://github.com/paritytech/substrate", branch = "master" } + +# Polkadot Dependencies + +xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } + +[features] +default = [] +test-helpers = [] diff --git a/relays/client-substrate/src/calls.rs b/relays/client-substrate/src/calls.rs new file mode 100644 index 00000000000..4e0ae9d99d2 --- /dev/null +++ b/relays/client-substrate/src/calls.rs @@ -0,0 +1,59 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Basic runtime calls. + +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_std::{boxed::Box, vec::Vec}; + +use xcm::{VersionedMultiLocation, VersionedXcm}; + +/// A minimized version of `frame-system::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum SystemCall { + /// `frame-system::Call::remark` + #[codec(index = 1)] + remark(Vec), +} + +/// A minimized version of `pallet-utility::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum UtilityCall { + /// `pallet-utility::Call::batch_all` + #[codec(index = 2)] + batch_all(Vec), +} + +/// A minimized version of `pallet-sudo::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum SudoCall { + /// `pallet-sudo::Call::sudo` + #[codec(index = 0)] + sudo(Box), +} + +/// A minimized version of `pallet-xcm::Call`, that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum XcmCall { + /// `pallet-xcm::Call::send` + #[codec(index = 0)] + send(Box, Box>), +} diff --git a/relays/client-substrate/src/chain.rs b/relays/client-substrate/src/chain.rs new file mode 100644 index 00000000000..8c7dc00aa67 --- /dev/null +++ b/relays/client-substrate/src/chain.rs @@ -0,0 +1,294 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::calls::UtilityCall; + +use bp_messages::MessageNonce; +use bp_runtime::{ + Chain as ChainBase, ChainId, EncodedOrDecodedCall, HashOf, Parachain as ParachainBase, + TransactionEra, TransactionEraOf, UnderlyingChainProvider, +}; +use codec::{Codec, Encode}; +use jsonrpsee::core::{DeserializeOwned, Serialize}; +use num_traits::Zero; +use sc_transaction_pool_api::TransactionStatus; +use sp_core::{storage::StorageKey, Pair}; +use sp_runtime::{ + generic::SignedBlock, + traits::{Block as BlockT, Member}, + ConsensusEngineId, EncodedJustification, +}; +use std::{fmt::Debug, time::Duration}; + +/// Substrate-based chain from minimal relay-client point of view. +pub trait Chain: ChainBase + Clone { + /// Chain id. + const ID: ChainId; + /// Chain name. + const NAME: &'static str; + /// Name of the runtime API method that is returning best known finalized header number + /// and hash (as tuple). + /// + /// Keep in mind that this method is normally provided by the other chain, which is + /// bridged with this chain. + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str; + + /// Average block interval. + /// + /// How often blocks are produced on that chain. It's suggested to set this value + /// to match the block time of the chain. + const AVERAGE_BLOCK_INTERVAL: Duration; + + /// Block type. + type SignedBlock: Member + Serialize + DeserializeOwned + BlockWithJustification; + /// The aggregated `Call` type. + type Call: Clone + Codec + Debug + Send; +} + +/// Substrate-based relay chain that supports parachains. +/// +/// We assume that the parachains are supported using `runtime_parachains::paras` pallet. +pub trait RelayChain: Chain { + /// Name of the `runtime_parachains::paras` pallet in the runtime of this chain. + const PARAS_PALLET_NAME: &'static str; + /// Name of the bridge parachains pallet (used in `construct_runtime` macro call) that is + /// deployed at the **bridged** chain. + /// + /// We assume that all chains that are bridging with this `RelayChain` are using + /// the same name. + const PARACHAINS_FINALITY_PALLET_NAME: &'static str; +} + +/// Substrate-based chain that is using direct GRANDPA finality from minimal relay-client point of +/// view. +/// +/// Keep in mind that parachains are relying on relay chain GRANDPA, so they should not implement +/// this trait. +pub trait ChainWithGrandpa: Chain { + /// Name of the bridge GRANDPA pallet (used in `construct_runtime` macro call) that is deployed + /// at some other chain to bridge with this `ChainWithGrandpa`. + /// + /// We assume that all chains that are bridging with this `ChainWithGrandpa` are using + /// the same name. + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str; +} + +impl ChainWithGrandpa for T +where + T: Chain + UnderlyingChainProvider, + T::Chain: bp_header_chain::ChainWithGrandpa, +{ + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = + ::WITH_CHAIN_GRANDPA_PALLET_NAME; +} + +/// Substrate-based parachain from minimal relay-client point of view. +pub trait Parachain: Chain + ParachainBase {} + +impl Parachain for T where T: UnderlyingChainProvider + Chain + ParachainBase {} + +/// Substrate-based chain with messaging support from minimal relay-client point of view. +pub trait ChainWithMessages: Chain { + /// Name of the bridge messages pallet (used in `construct_runtime` macro call) that is deployed + /// at some other chain to bridge with this `ChainWithMessages`. + /// + /// We assume that all chains that are bridging with this `ChainWithMessages` are using + /// the same name. + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str; + + // TODO (https://github.com/paritytech/parity-bridges-common/issues/1692): check all the names + // after the issue is fixed - all names must be changed + + /// Name of the bridge relayers pallet (used in `construct_runtime` macro call) that is deployed + /// at some other chain to bridge with this `ChainWithMessages`. + /// + /// We assume that all chains that are bridging with this `ChainWithMessages` are using + /// the same name. + const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str>; + + /// Name of the `ToOutboundLaneApi::message_details` runtime API method. + /// The method is provided by the runtime that is bridged with this `ChainWithMessages`. + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str; + + /// Name of the `FromInboundLaneApi::message_details` runtime API method. + /// The method is provided by the runtime that is bridged with this `ChainWithMessages`. + const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str; + + /// Maximal number of unrewarded relayers in a single confirmation transaction at this + /// `ChainWithMessages`. + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce; + /// Maximal number of unconfirmed messages in a single confirmation transaction at this + /// `ChainWithMessages`. + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce; +} + +/// Call type used by the chain. +pub type CallOf = ::Call; +/// Transaction status of the chain. +pub type TransactionStatusOf = TransactionStatus, HashOf>; + +/// Substrate-based chain with `AccountData` generic argument of `frame_system::AccountInfo` set to +/// the `pallet_balances::AccountData`. +pub trait ChainWithBalances: Chain { + /// Return runtime storage key for getting `frame_system::AccountInfo` of given account. + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey; +} + +/// SCALE-encoded extrinsic. +pub type EncodedExtrinsic = Vec; + +/// Block with justification. +pub trait BlockWithJustification
{ + /// Return block header. + fn header(&self) -> Header; + /// Return encoded block extrinsics. + fn extrinsics(&self) -> Vec; + /// Return block justification, if known. + fn justification(&self, engine_id: ConsensusEngineId) -> Option<&EncodedJustification>; +} + +/// Transaction before it is signed. +#[derive(Clone, Debug, PartialEq)] +pub struct UnsignedTransaction { + /// Runtime call of this transaction. + pub call: EncodedOrDecodedCall, + /// Transaction nonce. + pub nonce: C::Index, + /// Tip included into transaction. + pub tip: C::Balance, + /// Transaction era used by the chain. + pub era: TransactionEraOf, +} + +impl UnsignedTransaction { + /// Create new unsigned transaction with given call, nonce, era and zero tip. + pub fn new(call: EncodedOrDecodedCall, nonce: C::Index) -> Self { + Self { call, nonce, era: TransactionEra::Immortal, tip: Zero::zero() } + } + + /// Set transaction tip. + #[must_use] + pub fn tip(mut self, tip: C::Balance) -> Self { + self.tip = tip; + self + } + + /// Set transaction era. + #[must_use] + pub fn era(mut self, era: TransactionEraOf) -> Self { + self.era = era; + self + } +} + +/// Account key pair used by transactions signing scheme. +pub type AccountKeyPairOf = ::AccountKeyPair; + +/// Substrate-based chain transactions signing scheme. +pub trait ChainWithTransactions: Chain { + /// Type of key pairs used to sign transactions. + type AccountKeyPair: Pair; + /// Signed transaction. + type SignedTransaction: Clone + Debug + Codec + Send + 'static; + + /// Create transaction for given runtime call, signed by given account. + fn sign_transaction( + param: SignParam, + unsigned: UnsignedTransaction, + ) -> Result + where + Self: Sized; + + /// Returns true if transaction is signed. + fn is_signed(tx: &Self::SignedTransaction) -> bool; + + /// Returns true if transaction is signed by given signer. + fn is_signed_by(signer: &Self::AccountKeyPair, tx: &Self::SignedTransaction) -> bool; + + /// Parse signed transaction into its unsigned part. + /// + /// Returns `None` if signed transaction has unsupported format. + fn parse_transaction(tx: Self::SignedTransaction) -> Option>; +} + +/// Sign transaction parameters +pub struct SignParam { + /// Version of the runtime specification. + pub spec_version: u32, + /// Transaction version + pub transaction_version: u32, + /// Hash of the genesis block. + pub genesis_hash: HashOf, + /// Signer account + pub signer: AccountKeyPairOf, +} + +impl BlockWithJustification for SignedBlock { + fn header(&self) -> Block::Header { + self.block.header().clone() + } + + fn extrinsics(&self) -> Vec { + self.block.extrinsics().iter().map(Encode::encode).collect() + } + + fn justification(&self, engine_id: ConsensusEngineId) -> Option<&EncodedJustification> { + self.justifications.as_ref().and_then(|j| j.get(engine_id)) + } +} + +/// Trait that provides functionality defined inside `pallet-utility` +pub trait UtilityPallet { + /// Create batch call from given calls vector. + fn build_batch_call(calls: Vec) -> C::Call; +} + +/// Structure that implements `UtilityPalletProvider` based on a full runtime. +pub struct FullRuntimeUtilityPallet { + _phantom: std::marker::PhantomData, +} + +impl UtilityPallet for FullRuntimeUtilityPallet +where + C: Chain, + R: pallet_utility::Config, + ::RuntimeCall: From>, +{ + fn build_batch_call(calls: Vec) -> C::Call { + pallet_utility::Call::batch_all { calls }.into() + } +} + +/// Structure that implements `UtilityPalletProvider` based on a call conversion. +pub struct MockedRuntimeUtilityPallet { + _phantom: std::marker::PhantomData, +} + +impl UtilityPallet for MockedRuntimeUtilityPallet +where + C: Chain, + C::Call: From>, +{ + fn build_batch_call(calls: Vec) -> C::Call { + UtilityCall::batch_all(calls).into() + } +} + +/// Substrate-based chain that uses `pallet-utility`. +pub trait ChainWithUtilityPallet: Chain { + /// The utility pallet provider. + type UtilityPallet: UtilityPallet; +} diff --git a/relays/client-substrate/src/client.rs b/relays/client-substrate/src/client.rs new file mode 100644 index 00000000000..27af7d7d77f --- /dev/null +++ b/relays/client-substrate/src/client.rs @@ -0,0 +1,813 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate node client. + +use crate::{ + chain::{Chain, ChainWithBalances, ChainWithTransactions}, + rpc::{ + SubstrateAuthorClient, SubstrateChainClient, SubstrateFinalityClient, + SubstrateFrameSystemClient, SubstrateStateClient, SubstrateSystemClient, + }, + transaction_stall_timeout, AccountKeyPairOf, ConnectionParams, Error, HashOf, HeaderIdOf, + Result, SignParam, TransactionTracker, UnsignedTransaction, +}; + +use async_std::sync::{Arc, Mutex, RwLock}; +use async_trait::async_trait; +use bp_runtime::{HeaderIdProvider, StorageDoubleMapKeyProvider, StorageMapKeyProvider}; +use codec::{Decode, Encode}; +use frame_support::weights::Weight; +use frame_system::AccountInfo; +use futures::{SinkExt, StreamExt}; +use jsonrpsee::{ + core::DeserializeOwned, + ws_client::{WsClient as RpcClient, WsClientBuilder as RpcClientBuilder}, +}; +use num_traits::{Saturating, Zero}; +use pallet_balances::AccountData; +use pallet_transaction_payment::RuntimeDispatchInfo; +use relay_utils::{relay_loop::RECONNECT_DELAY, STALL_TIMEOUT}; +use sp_core::{ + storage::{StorageData, StorageKey}, + Bytes, Hasher, Pair, +}; +use sp_runtime::{ + traits::Header as HeaderT, + transaction_validity::{TransactionSource, TransactionValidity}, +}; +use sp_trie::StorageProof; +use sp_version::RuntimeVersion; +use std::future::Future; + +const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities"; +const SUB_API_TXPOOL_VALIDATE_TRANSACTION: &str = "TaggedTransactionQueue_validate_transaction"; +const SUB_API_TX_PAYMENT_QUERY_INFO: &str = "TransactionPaymentApi_query_info"; +const MAX_SUBSCRIPTION_CAPACITY: usize = 4096; + +/// The difference between best block number and number of its ancestor, that is enough +/// for us to consider that ancestor an "ancient" block with dropped state. +/// +/// The relay does not assume that it is connected to the archive node, so it always tries +/// to use the best available chain state. But sometimes it still may use state of some +/// old block. If the state of that block is already dropped, relay will see errors when +/// e.g. it tries to prove something. +/// +/// By default Substrate-based nodes are storing state for last 256 blocks. We'll use +/// half of this value. +pub const ANCIENT_BLOCK_THRESHOLD: u32 = 128; + +/// Returns `true` if we think that the state is already discarded for given block. +pub fn is_ancient_block + PartialOrd + Saturating>(block: N, best: N) -> bool { + best.saturating_sub(block) >= N::from(ANCIENT_BLOCK_THRESHOLD) +} + +/// Opaque justifications subscription type. +pub struct Subscription(pub(crate) Mutex>>); + +/// Opaque GRANDPA authorities set. +pub type OpaqueGrandpaAuthoritiesSet = Vec; + +/// A simple runtime version. It only includes the `spec_version` and `transaction_version`. +#[derive(Copy, Clone, Debug)] +pub struct SimpleRuntimeVersion { + /// Version of the runtime specification. + pub spec_version: u32, + /// All existing dispatches are fully compatible when this number doesn't change. + pub transaction_version: u32, +} + +impl SimpleRuntimeVersion { + /// Create a new instance of `SimpleRuntimeVersion` from a `RuntimeVersion`. + pub const fn from_runtime_version(runtime_version: &RuntimeVersion) -> Self { + Self { + spec_version: runtime_version.spec_version, + transaction_version: runtime_version.transaction_version, + } + } +} + +/// Chain runtime version in client +#[derive(Clone, Debug)] +pub enum ChainRuntimeVersion { + /// Auto query from chain. + Auto, + /// Custom runtime version, defined by user. + Custom(SimpleRuntimeVersion), +} + +/// Substrate client type. +/// +/// Cloning `Client` is a cheap operation that only clones internal references. Different +/// clones of the same client are guaranteed to use the same references. +pub struct Client { + // Lock order: `submit_signed_extrinsic_lock`, `data` + /// Client connection params. + params: Arc, + /// Saved chain runtime version. + chain_runtime_version: ChainRuntimeVersion, + /// If several tasks are submitting their transactions simultaneously using + /// `submit_signed_extrinsic` method, they may get the same transaction nonce. So one of + /// transactions will be rejected from the pool. This lock is here to prevent situations like + /// that. + submit_signed_extrinsic_lock: Arc>, + /// Genesis block hash. + genesis_hash: HashOf, + /// Shared dynamic data. + data: Arc>, +} + +/// Client data, shared by all `Client` clones. +struct ClientData { + /// Tokio runtime handle. + tokio: Arc, + /// Substrate RPC client. + client: Arc, +} + +#[async_trait] +impl relay_utils::relay_loop::Client for Client { + type Error = Error; + + async fn reconnect(&mut self) -> Result<()> { + let mut data = self.data.write().await; + let (tokio, client) = Self::build_client(&self.params).await?; + data.tokio = tokio; + data.client = client; + Ok(()) + } +} + +impl Clone for Client { + fn clone(&self) -> Self { + Client { + params: self.params.clone(), + chain_runtime_version: self.chain_runtime_version.clone(), + submit_signed_extrinsic_lock: self.submit_signed_extrinsic_lock.clone(), + genesis_hash: self.genesis_hash, + data: self.data.clone(), + } + } +} + +impl std::fmt::Debug for Client { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.debug_struct("Client").field("genesis_hash", &self.genesis_hash).finish() + } +} + +impl Client { + /// Returns client that is able to call RPCs on Substrate node over websocket connection. + /// + /// This function will keep connecting to given Substrate node until connection is established + /// and is functional. If attempt fail, it will wait for `RECONNECT_DELAY` and retry again. + pub async fn new(params: ConnectionParams) -> Self { + let params = Arc::new(params); + loop { + match Self::try_connect(params.clone()).await { + Ok(client) => return client, + Err(error) => log::error!( + target: "bridge", + "Failed to connect to {} node: {:?}. Going to retry in {}s", + C::NAME, + error, + RECONNECT_DELAY.as_secs(), + ), + } + + async_std::task::sleep(RECONNECT_DELAY).await; + } + } + + /// Try to connect to Substrate node over websocket. Returns Substrate RPC client if connection + /// has been established or error otherwise. + pub async fn try_connect(params: Arc) -> Result { + let (tokio, client) = Self::build_client(¶ms).await?; + + let number: C::BlockNumber = Zero::zero(); + let genesis_hash_client = client.clone(); + let genesis_hash = tokio + .spawn(async move { + SubstrateChainClient::::block_hash(&*genesis_hash_client, Some(number)).await + }) + .await??; + + let chain_runtime_version = params.chain_runtime_version.clone(); + Ok(Self { + params, + chain_runtime_version, + submit_signed_extrinsic_lock: Arc::new(Mutex::new(())), + genesis_hash, + data: Arc::new(RwLock::new(ClientData { tokio, client })), + }) + } + + /// Build client to use in connection. + async fn build_client( + params: &ConnectionParams, + ) -> Result<(Arc, Arc)> { + let tokio = tokio::runtime::Runtime::new()?; + let uri = format!( + "{}://{}:{}", + if params.secure { "wss" } else { "ws" }, + params.host, + params.port, + ); + log::info!(target: "bridge", "Connecting to {} node at {}", C::NAME, uri); + + let client = tokio + .spawn(async move { + RpcClientBuilder::default() + .max_notifs_per_subscription(MAX_SUBSCRIPTION_CAPACITY) + .build(&uri) + .await + }) + .await??; + + Ok((Arc::new(tokio), Arc::new(client))) + } +} + +impl Client { + /// Return simple runtime version, only include `spec_version` and `transaction_version`. + pub async fn simple_runtime_version(&self) -> Result { + Ok(match &self.chain_runtime_version { + ChainRuntimeVersion::Auto => { + let runtime_version = self.runtime_version().await?; + SimpleRuntimeVersion::from_runtime_version(&runtime_version) + }, + ChainRuntimeVersion::Custom(version) => *version, + }) + } + + /// Returns true if client is connected to at least one peer and is in synced state. + pub async fn ensure_synced(&self) -> Result<()> { + self.jsonrpsee_execute(|client| async move { + let health = SubstrateSystemClient::::health(&*client).await?; + let is_synced = !health.is_syncing && (!health.should_have_peers || health.peers > 0); + if is_synced { + Ok(()) + } else { + Err(Error::ClientNotSynced(health)) + } + }) + .await + } + + /// Return hash of the genesis block. + pub fn genesis_hash(&self) -> &C::Hash { + &self.genesis_hash + } + + /// Return hash of the best finalized block. + pub async fn best_finalized_header_hash(&self) -> Result { + self.jsonrpsee_execute(|client| async move { + Ok(SubstrateChainClient::::finalized_head(&*client).await?) + }) + .await + .map_err(|e| Error::FailedToReadBestFinalizedHeaderHash { + chain: C::NAME.into(), + error: e.boxed(), + }) + } + + /// Return number of the best finalized block. + pub async fn best_finalized_header_number(&self) -> Result { + Ok(*self.best_finalized_header().await?.number()) + } + + /// Return header of the best finalized block. + pub async fn best_finalized_header(&self) -> Result { + self.header_by_hash(self.best_finalized_header_hash().await?).await + } + + /// Returns the best Substrate header. + pub async fn best_header(&self) -> Result + where + C::Header: DeserializeOwned, + { + self.jsonrpsee_execute(|client| async move { + Ok(SubstrateChainClient::::header(&*client, None).await?) + }) + .await + .map_err(|e| Error::FailedToReadBestHeader { chain: C::NAME.into(), error: e.boxed() }) + } + + /// Get a Substrate block from its hash. + pub async fn get_block(&self, block_hash: Option) -> Result { + self.jsonrpsee_execute(move |client| async move { + Ok(SubstrateChainClient::::block(&*client, block_hash).await?) + }) + .await + } + + /// Get a Substrate header by its hash. + pub async fn header_by_hash(&self, block_hash: C::Hash) -> Result + where + C::Header: DeserializeOwned, + { + self.jsonrpsee_execute(move |client| async move { + Ok(SubstrateChainClient::::header(&*client, Some(block_hash)).await?) + }) + .await + .map_err(|e| Error::FailedToReadHeaderByHash { + chain: C::NAME.into(), + hash: format!("{block_hash}"), + error: e.boxed(), + }) + } + + /// Get a Substrate block hash by its number. + pub async fn block_hash_by_number(&self, number: C::BlockNumber) -> Result { + self.jsonrpsee_execute(move |client| async move { + Ok(SubstrateChainClient::::block_hash(&*client, Some(number)).await?) + }) + .await + } + + /// Get a Substrate header by its number. + pub async fn header_by_number(&self, block_number: C::BlockNumber) -> Result + where + C::Header: DeserializeOwned, + { + let block_hash = Self::block_hash_by_number(self, block_number).await?; + let header_by_hash = Self::header_by_hash(self, block_hash).await?; + Ok(header_by_hash) + } + + /// Return runtime version. + pub async fn runtime_version(&self) -> Result { + self.jsonrpsee_execute(move |client| async move { + Ok(SubstrateStateClient::::runtime_version(&*client).await?) + }) + .await + } + + /// Read value from runtime storage. + pub async fn storage_value( + &self, + storage_key: StorageKey, + block_hash: Option, + ) -> Result> { + self.raw_storage_value(storage_key, block_hash) + .await? + .map(|encoded_value| { + T::decode(&mut &encoded_value.0[..]).map_err(Error::ResponseParseFailed) + }) + .transpose() + } + + /// Read `MapStorage` value from runtime storage. + pub async fn storage_map_value( + &self, + pallet_prefix: &str, + key: &T::Key, + block_hash: Option, + ) -> Result> { + let storage_key = T::final_key(pallet_prefix, key); + + self.raw_storage_value(storage_key, block_hash) + .await? + .map(|encoded_value| { + T::Value::decode(&mut &encoded_value.0[..]).map_err(Error::ResponseParseFailed) + }) + .transpose() + } + + /// Read `DoubleMapStorage` value from runtime storage. + pub async fn storage_double_map_value( + &self, + pallet_prefix: &str, + key1: &T::Key1, + key2: &T::Key2, + block_hash: Option, + ) -> Result> { + let storage_key = T::final_key(pallet_prefix, key1, key2); + + self.raw_storage_value(storage_key, block_hash) + .await? + .map(|encoded_value| { + T::Value::decode(&mut &encoded_value.0[..]).map_err(Error::ResponseParseFailed) + }) + .transpose() + } + + /// Read raw value from runtime storage. + pub async fn raw_storage_value( + &self, + storage_key: StorageKey, + block_hash: Option, + ) -> Result> { + let cloned_storage_key = storage_key.clone(); + self.jsonrpsee_execute(move |client| async move { + Ok(SubstrateStateClient::::storage(&*client, storage_key.clone(), block_hash) + .await?) + }) + .await + .map_err(|e| Error::FailedToReadRuntimeStorageValue { + chain: C::NAME.into(), + key: cloned_storage_key, + error: e.boxed(), + }) + } + + /// Return native tokens balance of the account. + pub async fn free_native_balance(&self, account: C::AccountId) -> Result + where + C: ChainWithBalances, + { + self.jsonrpsee_execute(move |client| async move { + let storage_key = C::account_info_storage_key(&account); + let encoded_account_data = + SubstrateStateClient::::storage(&*client, storage_key, None) + .await? + .ok_or(Error::AccountDoesNotExist)?; + let decoded_account_data = AccountInfo::>::decode( + &mut &encoded_account_data.0[..], + ) + .map_err(Error::ResponseParseFailed)?; + Ok(decoded_account_data.data.free) + }) + .await + } + + /// Get the nonce of the given Substrate account. + /// + /// Note: It's the caller's responsibility to make sure `account` is a valid SS58 address. + pub async fn next_account_index(&self, account: C::AccountId) -> Result { + self.jsonrpsee_execute(move |client| async move { + Ok(SubstrateFrameSystemClient::::account_next_index(&*client, account).await?) + }) + .await + } + + /// Submit unsigned extrinsic for inclusion in a block. + /// + /// Note: The given transaction needs to be SCALE encoded beforehand. + pub async fn submit_unsigned_extrinsic(&self, transaction: Bytes) -> Result { + self.jsonrpsee_execute(move |client| async move { + let tx_hash = SubstrateAuthorClient::::submit_extrinsic(&*client, transaction) + .await + .map_err(|e| { + log::error!(target: "bridge", "Failed to send transaction to {} node: {:?}", C::NAME, e); + e + })?; + log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash); + Ok(tx_hash) + }) + .await + } + + async fn build_sign_params(&self, signer: AccountKeyPairOf) -> Result> + where + C: ChainWithTransactions, + { + let runtime_version = self.simple_runtime_version().await?; + Ok(SignParam:: { + spec_version: runtime_version.spec_version, + transaction_version: runtime_version.transaction_version, + genesis_hash: self.genesis_hash, + signer, + }) + } + + /// Submit an extrinsic signed by given account. + /// + /// All calls of this method are synchronized, so there can't be more than one active + /// `submit_signed_extrinsic()` call. This guarantees that no nonces collision may happen + /// if all client instances are clones of the same initial `Client`. + /// + /// Note: The given transaction needs to be SCALE encoded beforehand. + pub async fn submit_signed_extrinsic( + &self, + signer: &AccountKeyPairOf, + prepare_extrinsic: impl FnOnce(HeaderIdOf, C::Index) -> Result> + + Send + + 'static, + ) -> Result + where + C: ChainWithTransactions, + C::AccountId: From<::Public>, + { + let _guard = self.submit_signed_extrinsic_lock.lock().await; + let transaction_nonce = self.next_account_index(signer.public().into()).await?; + let best_header = self.best_header().await?; + let signing_data = self.build_sign_params(signer.clone()).await?; + + // By using parent of best block here, we are protecing again best-block reorganizations. + // E.g. transaction may have been submitted when the best block was `A[num=100]`. Then it + // has been changed to `B[num=100]`. Hash of `A` has been included into transaction + // signature payload. So when signature will be checked, the check will fail and transaction + // will be dropped from the pool. + let best_header_id = best_header.parent_id().unwrap_or_else(|| best_header.id()); + + self.jsonrpsee_execute(move |client| async move { + let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?; + let signed_extrinsic = C::sign_transaction(signing_data, extrinsic)?.encode(); + let tx_hash = + SubstrateAuthorClient::::submit_extrinsic(&*client, Bytes(signed_extrinsic)) + .await + .map_err(|e| { + log::error!(target: "bridge", "Failed to send transaction to {} node: {:?}", C::NAME, e); + e + })?; + log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash); + Ok(tx_hash) + }) + .await + } + + /// Does exactly the same as `submit_signed_extrinsic`, but keeps watching for extrinsic status + /// after submission. + pub async fn submit_and_watch_signed_extrinsic( + &self, + signer: &AccountKeyPairOf, + prepare_extrinsic: impl FnOnce(HeaderIdOf, C::Index) -> Result> + + Send + + 'static, + ) -> Result> + where + C: ChainWithTransactions, + C::AccountId: From<::Public>, + { + let self_clone = self.clone(); + let signing_data = self.build_sign_params(signer.clone()).await?; + let _guard = self.submit_signed_extrinsic_lock.lock().await; + let transaction_nonce = self.next_account_index(signer.public().into()).await?; + let best_header = self.best_header().await?; + let best_header_id = best_header.id(); + let (sender, receiver) = futures::channel::mpsc::channel(MAX_SUBSCRIPTION_CAPACITY); + let (tracker, subscription) = self + .jsonrpsee_execute(move |client| async move { + let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?; + let stall_timeout = transaction_stall_timeout( + extrinsic.era.mortality_period(), + C::AVERAGE_BLOCK_INTERVAL, + STALL_TIMEOUT, + ); + let signed_extrinsic = C::sign_transaction(signing_data, extrinsic)?.encode(); + let tx_hash = C::Hasher::hash(&signed_extrinsic); + let subscription = SubstrateAuthorClient::::submit_and_watch_extrinsic( + &*client, + Bytes(signed_extrinsic), + ) + .await + .map_err(|e| { + log::error!(target: "bridge", "Failed to send transaction to {} node: {:?}", C::NAME, e); + e + })?; + log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash); + let tracker = TransactionTracker::new( + self_clone, + stall_timeout, + tx_hash, + Subscription(Mutex::new(receiver)), + ); + Ok((tracker, subscription)) + }) + .await?; + self.data.read().await.tokio.spawn(Subscription::background_worker( + C::NAME.into(), + "extrinsic".into(), + subscription, + sender, + )); + Ok(tracker) + } + + /// Returns pending extrinsics from transaction pool. + pub async fn pending_extrinsics(&self) -> Result> { + self.jsonrpsee_execute(move |client| async move { + Ok(SubstrateAuthorClient::::pending_extrinsics(&*client).await?) + }) + .await + } + + /// Validate transaction at given block state. + pub async fn validate_transaction( + &self, + at_block: C::Hash, + transaction: SignedTransaction, + ) -> Result { + self.jsonrpsee_execute(move |client| async move { + let call = SUB_API_TXPOOL_VALIDATE_TRANSACTION.to_string(); + let data = Bytes((TransactionSource::External, transaction, at_block).encode()); + + let encoded_response = + SubstrateStateClient::::call(&*client, call, data, Some(at_block)).await?; + let validity = TransactionValidity::decode(&mut &encoded_response.0[..]) + .map_err(Error::ResponseParseFailed)?; + + Ok(validity) + }) + .await + } + + /// Returns weight of the given transaction. + pub async fn extimate_extrinsic_weight( + &self, + transaction: SignedTransaction, + ) -> Result { + self.jsonrpsee_execute(move |client| async move { + let transaction_len = transaction.encoded_size() as u32; + + let call = SUB_API_TX_PAYMENT_QUERY_INFO.to_string(); + let data = Bytes((transaction, transaction_len).encode()); + + let encoded_response = + SubstrateStateClient::::call(&*client, call, data, None).await?; + let dispatch_info = + RuntimeDispatchInfo::::decode(&mut &encoded_response.0[..]) + .map_err(Error::ResponseParseFailed)?; + + Ok(dispatch_info.weight) + }) + .await + } + + /// Get the GRANDPA authority set at given block. + pub async fn grandpa_authorities_set( + &self, + block: C::Hash, + ) -> Result { + self.jsonrpsee_execute(move |client| async move { + let call = SUB_API_GRANDPA_AUTHORITIES.to_string(); + let data = Bytes(Vec::new()); + + let encoded_response = + SubstrateStateClient::::call(&*client, call, data, Some(block)).await?; + let authority_list = encoded_response.0; + + Ok(authority_list) + }) + .await + } + + /// Execute runtime call at given block, provided the input and output types. + /// It also performs the input encode and output decode. + pub async fn typed_state_call( + &self, + method_name: String, + input: Input, + at_block: Option, + ) -> Result { + let encoded_output = self + .state_call(method_name.clone(), Bytes(input.encode()), at_block) + .await + .map_err(|e| Error::ErrorExecutingRuntimeCall { + chain: C::NAME.into(), + method: method_name, + error: e.boxed(), + })?; + Output::decode(&mut &encoded_output.0[..]).map_err(Error::ResponseParseFailed) + } + + /// Execute runtime call at given block. + pub async fn state_call( + &self, + method: String, + data: Bytes, + at_block: Option, + ) -> Result { + self.jsonrpsee_execute(move |client| async move { + SubstrateStateClient::::call(&*client, method, data, at_block) + .await + .map_err(Into::into) + }) + .await + } + + /// Returns storage proof of given storage keys. + pub async fn prove_storage( + &self, + keys: Vec, + at_block: C::Hash, + ) -> Result { + self.jsonrpsee_execute(move |client| async move { + SubstrateStateClient::::prove_storage(&*client, keys, Some(at_block)) + .await + .map(|proof| { + StorageProof::new(proof.proof.into_iter().map(|b| b.0).collect::>()) + }) + .map_err(Into::into) + }) + .await + } + + /// Return `tokenDecimals` property from the set of chain properties. + pub async fn token_decimals(&self) -> Result> { + self.jsonrpsee_execute(move |client| async move { + let system_properties = SubstrateSystemClient::::properties(&*client).await?; + Ok(system_properties.get("tokenDecimals").and_then(|v| v.as_u64())) + }) + .await + } + + /// Return new finality justifications stream. + pub async fn subscribe_finality_justifications>( + &self, + ) -> Result> { + let subscription = self + .jsonrpsee_execute(move |client| async move { + Ok(FC::subscribe_justifications(&client).await?) + }) + .await?; + let (sender, receiver) = futures::channel::mpsc::channel(MAX_SUBSCRIPTION_CAPACITY); + self.data.read().await.tokio.spawn(Subscription::background_worker( + C::NAME.into(), + "justification".into(), + subscription, + sender, + )); + Ok(Subscription(Mutex::new(receiver))) + } + + /// Execute jsonrpsee future in tokio context. + async fn jsonrpsee_execute(&self, make_jsonrpsee_future: MF) -> Result + where + MF: FnOnce(Arc) -> F + Send + 'static, + F: Future> + Send + 'static, + T: Send + 'static, + { + let data = self.data.read().await; + let client = data.client.clone(); + data.tokio.spawn(make_jsonrpsee_future(client)).await? + } + + /// Returns `true` if version guard can be started. + /// + /// There's no reason to run version guard when version mode is set to `Auto`. It can + /// lead to relay shutdown when chain is upgraded, even though we have explicitly + /// said that we don't want to shutdown. + pub fn can_start_version_guard(&self) -> bool { + !matches!(self.chain_runtime_version, ChainRuntimeVersion::Auto) + } +} + +impl Subscription { + /// Consumes subscription and returns future statuses stream. + pub fn into_stream(self) -> impl futures::Stream { + futures::stream::unfold(self, |this| async { + let item = this.0.lock().await.next().await.unwrap_or(None); + item.map(|i| (i, this)) + }) + } + + /// Return next item from the subscription. + pub async fn next(&self) -> Result> { + let mut receiver = self.0.lock().await; + let item = receiver.next().await; + Ok(item.unwrap_or(None)) + } + + /// Background worker that is executed in tokio context as `jsonrpsee` requires. + async fn background_worker( + chain_name: String, + item_type: String, + mut subscription: jsonrpsee::core::client::Subscription, + mut sender: futures::channel::mpsc::Sender>, + ) { + loop { + match subscription.next().await { + Some(Ok(item)) => + if sender.send(Some(item)).await.is_err() { + break + }, + Some(Err(e)) => { + log::trace!( + target: "bridge", + "{} {} subscription stream has returned '{:?}'. Stream needs to be restarted.", + chain_name, + item_type, + e, + ); + let _ = sender.send(None).await; + break + }, + None => { + log::trace!( + target: "bridge", + "{} {} subscription stream has returned None. Stream needs to be restarted.", + chain_name, + item_type, + ); + let _ = sender.send(None).await; + break + }, + } + } + } +} diff --git a/relays/client-substrate/src/error.rs b/relays/client-substrate/src/error.rs new file mode 100644 index 00000000000..54247c5dc17 --- /dev/null +++ b/relays/client-substrate/src/error.rs @@ -0,0 +1,155 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate node RPC errors. + +use bp_polkadot_core::parachains::ParaId; +use jsonrpsee::core::Error as RpcError; +use relay_utils::MaybeConnectionError; +use sc_rpc_api::system::Health; +use sp_core::storage::StorageKey; +use sp_runtime::transaction_validity::TransactionValidityError; +use thiserror::Error; + +/// Result type used by Substrate client. +pub type Result = std::result::Result; + +/// Errors that can occur only when interacting with +/// a Substrate node through RPC. +#[derive(Error, Debug)] +pub enum Error { + /// IO error. + #[error("IO error: {0}")] + Io(#[from] std::io::Error), + /// An error that can occur when making a request to + /// an JSON-RPC server. + #[error("RPC error: {0}")] + RpcError(#[from] RpcError), + /// The response from the server could not be SCALE decoded. + #[error("Response parse failed: {0}")] + ResponseParseFailed(#[from] codec::Error), + /// Account does not exist on the chain. + #[error("Account does not exist on the chain.")] + AccountDoesNotExist, + /// Runtime storage is missing some mandatory value. + #[error("Mandatory storage value is missing from the runtime storage.")] + MissingMandatoryStorageValue, + /// Required parachain head is not present at the relay chain. + #[error("Parachain {0:?} head {1} is missing from the relay chain storage.")] + MissingRequiredParachainHead(ParaId, u64), + /// Failed to find finality proof for the given header. + #[error("Failed to find finality proof for header {0}.")] + FinalityProofNotFound(u64), + /// The client we're connected to is not synced, so we can't rely on its state. + #[error("Substrate client is not synced {0}.")] + ClientNotSynced(Health), + /// Failed to read best finalized header hash from given chain. + #[error("Failed to read best finalized header hash of {chain}: {error:?}.")] + FailedToReadBestFinalizedHeaderHash { + /// Name of the chain where the error has happened. + chain: String, + /// Underlying error. + error: Box, + }, + /// Failed to read best finalized header from given chain. + #[error("Failed to read best header of {chain}: {error:?}.")] + FailedToReadBestHeader { + /// Name of the chain where the error has happened. + chain: String, + /// Underlying error. + error: Box, + }, + /// Failed to read header by hash from given chain. + #[error("Failed to read header {hash} of {chain}: {error:?}.")] + FailedToReadHeaderByHash { + /// Name of the chain where the error has happened. + chain: String, + /// Hash of the header we've tried to read. + hash: String, + /// Underlying error. + error: Box, + }, + /// Failed to execute runtime call at given chain. + #[error("Failed to execute runtime call {method} at {chain}: {error:?}.")] + ErrorExecutingRuntimeCall { + /// Name of the chain where the error has happened. + chain: String, + /// Runtime method name. + method: String, + /// Underlying error. + error: Box, + }, + /// Failed to read sotrage value at given chain. + #[error("Failed to read storage value {key:?} at {chain}: {error:?}.")] + FailedToReadRuntimeStorageValue { + /// Name of the chain where the error has happened. + chain: String, + /// Runtime storage key + key: StorageKey, + /// Underlying error. + error: Box, + }, + /// The bridge pallet is halted and all transactions will be rejected. + #[error("Bridge pallet is halted.")] + BridgePalletIsHalted, + /// The bridge pallet is not yet initialized and all transactions will be rejected. + #[error("Bridge pallet is not initialized.")] + BridgePalletIsNotInitialized, + /// There's no best head of the parachain at the `pallet-bridge-parachains` at the target side. + #[error("No head of the ParaId({0}) at the bridge parachains pallet at {1}.")] + NoParachainHeadAtTarget(u32, String), + /// An error has happened when we have tried to parse storage proof. + #[error("Error when parsing storage proof: {0:?}.")] + StorageProofError(bp_runtime::StorageProofError), + /// The Substrate transaction is invalid. + #[error("Substrate transaction is invalid: {0:?}")] + TransactionInvalid(#[from] TransactionValidityError), + /// Custom logic error. + #[error("{0}")] + Custom(String), +} + +impl From for Error { + fn from(error: tokio::task::JoinError) -> Self { + Error::Custom(format!("Failed to wait tokio task: {error}")) + } +} + +impl Error { + /// Box the error. + pub fn boxed(self) -> Box { + Box::new(self) + } +} + +impl MaybeConnectionError for Error { + fn is_connection_error(&self) -> bool { + match *self { + Error::RpcError(RpcError::Transport(_)) + // right now if connection to the ws server is dropped (after it is already established), + // we're getting this error + | Error::RpcError(RpcError::Internal(_)) + | Error::RpcError(RpcError::RestartNeeded(_)) + | Error::ClientNotSynced(_) => true, + Error::FailedToReadBestFinalizedHeaderHash { ref error, .. } => error.is_connection_error(), + Error::FailedToReadBestHeader { ref error, .. } => error.is_connection_error(), + Error::FailedToReadHeaderByHash { ref error, .. } => error.is_connection_error(), + Error::ErrorExecutingRuntimeCall { ref error, .. } => error.is_connection_error(), + Error::FailedToReadRuntimeStorageValue { ref error, .. } => error.is_connection_error(), + _ => false, + } + } +} diff --git a/relays/client-substrate/src/guard.rs b/relays/client-substrate/src/guard.rs new file mode 100644 index 00000000000..1afbf0d3d17 --- /dev/null +++ b/relays/client-substrate/src/guard.rs @@ -0,0 +1,373 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Pallet provides a set of guard functions that are running in background threads +//! and are aborting process if some condition fails. + +use crate::{error::Error, Chain, ChainWithBalances, Client}; + +use async_trait::async_trait; +use num_traits::CheckedSub; +use sp_version::RuntimeVersion; +use std::{ + collections::VecDeque, + fmt::Display, + time::{Duration, Instant}, +}; + +/// Guards environment. +#[async_trait] +pub trait Environment: Send + Sync + 'static { + /// Error type. + type Error: Display + Send + Sync + 'static; + + /// Return current runtime version. + async fn runtime_version(&mut self) -> Result; + /// Return free native balance of the account on the chain. + async fn free_native_balance( + &mut self, + account: C::AccountId, + ) -> Result; + + /// Return current time. + fn now(&self) -> Instant { + Instant::now() + } + + /// Sleep given amount of time. + async fn sleep(&mut self, duration: Duration) { + async_std::task::sleep(duration).await + } + + /// Abort current process. Called when guard condition check fails. + async fn abort(&mut self) { + std::process::abort(); + } +} + +/// Abort when runtime spec version is different from specified. +pub fn abort_on_spec_version_change( + mut env: impl Environment, + expected_spec_version: u32, +) { + async_std::task::spawn(async move { + log::info!( + target: "bridge-guard", + "Starting spec_version guard for {}. Expected spec_version: {}", + C::NAME, + expected_spec_version, + ); + + loop { + let actual_spec_version = env.runtime_version().await; + match actual_spec_version { + Ok(version) if version.spec_version == expected_spec_version => (), + Ok(version) => { + log::error!( + target: "bridge-guard", + "{} runtime spec version has changed from {} to {}. Aborting relay", + C::NAME, + expected_spec_version, + version.spec_version, + ); + + env.abort().await; + }, + Err(error) => log::warn!( + target: "bridge-guard", + "Failed to read {} runtime version: {}. Relay may need to be stopped manually", + C::NAME, + error, + ), + } + + env.sleep(conditions_check_delay::()).await; + } + }); +} + +/// Abort if, during 24 hours, free balance of given account is decreased at least by given value. +/// Other components may increase (or decrease) balance of account and it WILL affect logic of the +/// guard. +pub fn abort_when_account_balance_decreased( + mut env: impl Environment, + account_id: C::AccountId, + maximal_decrease: C::Balance, +) { + const DAY: Duration = Duration::from_secs(60 * 60 * 24); + + async_std::task::spawn(async move { + log::info!( + target: "bridge-guard", + "Starting balance guard for {}/{:?}. Maximal decrease: {:?}", + C::NAME, + account_id, + maximal_decrease, + ); + + let mut balances = VecDeque::new(); + + loop { + let current_time = env.now(); + + // remember balances that are beyound 24h border + if let Some(time_border) = current_time.checked_sub(DAY) { + while balances.front().map(|(time, _)| *time < time_border).unwrap_or(false) { + balances.pop_front(); + } + } + + // read balance of the account + let current_balance = env.free_native_balance(account_id.clone()).await; + + // remember balance and check difference + match current_balance { + Ok(current_balance) => { + // remember balance + balances.push_back((current_time, current_balance)); + + // check if difference between current and oldest balance is too large + let (oldest_time, oldest_balance) = + balances.front().expect("pushed to queue couple of lines above; qed"); + let balances_difference = oldest_balance.checked_sub(¤t_balance); + if balances_difference > Some(maximal_decrease) { + log::error!( + target: "bridge-guard", + "Balance of {} account {:?} has decreased from {:?} to {:?} in {} minutes. Aborting relay", + C::NAME, + account_id, + oldest_balance, + current_balance, + current_time.duration_since(*oldest_time).as_secs() / 60, + ); + + env.abort().await; + } + }, + Err(error) => { + log::warn!( + target: "bridge-guard", + "Failed to read {} account {:?} balance: {}. Relay may need to be stopped manually", + C::NAME, + account_id, + error, + ); + }, + }; + + env.sleep(conditions_check_delay::()).await; + } + }); +} + +/// Delay between conditions check. +fn conditions_check_delay() -> Duration { + C::AVERAGE_BLOCK_INTERVAL * (10 + rand::random::() % 10) +} + +#[async_trait] +impl Environment for Client { + type Error = Error; + + async fn runtime_version(&mut self) -> Result { + Client::::runtime_version(self).await + } + + async fn free_native_balance( + &mut self, + account: C::AccountId, + ) -> Result { + Client::::free_native_balance(self, account).await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_chain::TestChain; + use futures::{ + channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}, + future::FutureExt, + stream::StreamExt, + SinkExt, + }; + + struct TestEnvironment { + runtime_version_rx: UnboundedReceiver, + free_native_balance_rx: UnboundedReceiver, + slept_tx: UnboundedSender<()>, + aborted_tx: UnboundedSender<()>, + } + + #[async_trait] + impl Environment for TestEnvironment { + type Error = Error; + + async fn runtime_version(&mut self) -> Result { + Ok(self.runtime_version_rx.next().await.unwrap_or_default()) + } + + async fn free_native_balance(&mut self, _account: u32) -> Result { + Ok(self.free_native_balance_rx.next().await.unwrap_or_default()) + } + + async fn sleep(&mut self, _duration: Duration) { + let _ = self.slept_tx.send(()).await; + } + + async fn abort(&mut self) { + let _ = self.aborted_tx.send(()).await; + // simulate process abort :) + async_std::task::sleep(Duration::from_secs(60)).await; + } + } + + #[test] + fn aborts_when_spec_version_is_changed() { + async_std::task::block_on(async { + let ( + (mut runtime_version_tx, runtime_version_rx), + (_free_native_balance_tx, free_native_balance_rx), + (slept_tx, mut slept_rx), + (aborted_tx, mut aborted_rx), + ) = (unbounded(), unbounded(), unbounded(), unbounded()); + abort_on_spec_version_change( + TestEnvironment { + runtime_version_rx, + free_native_balance_rx, + slept_tx, + aborted_tx, + }, + 0, + ); + + // client responds with wrong version + runtime_version_tx + .send(RuntimeVersion { spec_version: 42, ..Default::default() }) + .await + .unwrap(); + + // then the `abort` function is called + aborted_rx.next().await; + // and we do not reach the `sleep` function call + assert!(slept_rx.next().now_or_never().is_none()); + }); + } + + #[test] + fn does_not_aborts_when_spec_version_is_unchanged() { + async_std::task::block_on(async { + let ( + (mut runtime_version_tx, runtime_version_rx), + (_free_native_balance_tx, free_native_balance_rx), + (slept_tx, mut slept_rx), + (aborted_tx, mut aborted_rx), + ) = (unbounded(), unbounded(), unbounded(), unbounded()); + abort_on_spec_version_change( + TestEnvironment { + runtime_version_rx, + free_native_balance_rx, + slept_tx, + aborted_tx, + }, + 42, + ); + + // client responds with the same version + runtime_version_tx + .send(RuntimeVersion { spec_version: 42, ..Default::default() }) + .await + .unwrap(); + + // then the `sleep` function is called + slept_rx.next().await; + // and the `abort` function is not called + assert!(aborted_rx.next().now_or_never().is_none()); + }); + } + + #[test] + fn aborts_when_balance_is_too_low() { + async_std::task::block_on(async { + let ( + (_runtime_version_tx, runtime_version_rx), + (mut free_native_balance_tx, free_native_balance_rx), + (slept_tx, mut slept_rx), + (aborted_tx, mut aborted_rx), + ) = (unbounded(), unbounded(), unbounded(), unbounded()); + abort_when_account_balance_decreased( + TestEnvironment { + runtime_version_rx, + free_native_balance_rx, + slept_tx, + aborted_tx, + }, + 0, + 100, + ); + + // client responds with initial balance + free_native_balance_tx.send(1000).await.unwrap(); + + // then the guard sleeps + slept_rx.next().await; + + // and then client responds with updated balance, which is too low + free_native_balance_tx.send(899).await.unwrap(); + + // then the `abort` function is called + aborted_rx.next().await; + // and we do not reach next `sleep` function call + assert!(slept_rx.next().now_or_never().is_none()); + }); + } + + #[test] + fn does_not_aborts_when_balance_is_enough() { + async_std::task::block_on(async { + let ( + (_runtime_version_tx, runtime_version_rx), + (mut free_native_balance_tx, free_native_balance_rx), + (slept_tx, mut slept_rx), + (aborted_tx, mut aborted_rx), + ) = (unbounded(), unbounded(), unbounded(), unbounded()); + abort_when_account_balance_decreased( + TestEnvironment { + runtime_version_rx, + free_native_balance_rx, + slept_tx, + aborted_tx, + }, + 0, + 100, + ); + + // client responds with initial balance + free_native_balance_tx.send(1000).await.unwrap(); + + // then the guard sleeps + slept_rx.next().await; + + // and then client responds with updated balance, which is enough + free_native_balance_tx.send(950).await.unwrap(); + + // then the `sleep` function is called + slept_rx.next().await; + // and `abort` is not called + assert!(aborted_rx.next().now_or_never().is_none()); + }); + } +} diff --git a/relays/client-substrate/src/lib.rs b/relays/client-substrate/src/lib.rs new file mode 100644 index 00000000000..c8d8b6f8129 --- /dev/null +++ b/relays/client-substrate/src/lib.rs @@ -0,0 +1,94 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Tools to interact with Substrate node using RPC methods. + +#![warn(missing_docs)] + +mod chain; +mod client; +mod error; +mod rpc; +mod sync_header; +mod transaction_tracker; + +pub mod calls; +pub mod guard; +pub mod metrics; +pub mod test_chain; + +use std::time::Duration; + +pub use crate::{ + chain::{ + AccountKeyPairOf, BlockWithJustification, CallOf, Chain, ChainWithBalances, + ChainWithGrandpa, ChainWithMessages, ChainWithTransactions, ChainWithUtilityPallet, + FullRuntimeUtilityPallet, MockedRuntimeUtilityPallet, Parachain, RelayChain, SignParam, + TransactionStatusOf, UnsignedTransaction, UtilityPallet, + }, + client::{ + is_ancient_block, ChainRuntimeVersion, Client, OpaqueGrandpaAuthoritiesSet, + SimpleRuntimeVersion, Subscription, ANCIENT_BLOCK_THRESHOLD, + }, + error::{Error, Result}, + rpc::{SubstrateBeefyFinalityClient, SubstrateFinalityClient, SubstrateGrandpaFinalityClient}, + sync_header::SyncHeader, + transaction_tracker::TransactionTracker, +}; +pub use bp_runtime::{ + AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain as ChainBase, HashOf, HeaderIdOf, + HeaderOf, IndexOf, Parachain as ParachainBase, SignatureOf, TransactionEra, TransactionEraOf, + UnderlyingChainProvider, +}; + +/// Substrate-over-websocket connection params. +#[derive(Debug, Clone)] +pub struct ConnectionParams { + /// Websocket server host name. + pub host: String, + /// Websocket server TCP port. + pub port: u16, + /// Use secure websocket connection. + pub secure: bool, + /// Defined chain runtime version + pub chain_runtime_version: ChainRuntimeVersion, +} + +impl Default for ConnectionParams { + fn default() -> Self { + ConnectionParams { + host: "localhost".into(), + port: 9944, + secure: false, + chain_runtime_version: ChainRuntimeVersion::Auto, + } + } +} + +/// Returns stall timeout for relay loop. +/// +/// Relay considers himself stalled if he has submitted transaction to the node, but it has not +/// been mined for this period. +pub fn transaction_stall_timeout( + mortality_period: Option, + average_block_interval: Duration, + default_stall_timeout: Duration, +) -> Duration { + // 1 extra block for transaction to reach the pool && 1 for relayer to awake after it is mined + mortality_period + .map(|mortality_period| average_block_interval.saturating_mul(mortality_period + 1 + 1)) + .unwrap_or(default_stall_timeout) +} diff --git a/relays/client-substrate/src/metrics/float_storage_value.rs b/relays/client-substrate/src/metrics/float_storage_value.rs new file mode 100644 index 00000000000..7bb92693b38 --- /dev/null +++ b/relays/client-substrate/src/metrics/float_storage_value.rs @@ -0,0 +1,133 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{chain::Chain, client::Client, Error as SubstrateError}; + +use async_std::sync::{Arc, RwLock}; +use async_trait::async_trait; +use codec::Decode; +use num_traits::One; +use relay_utils::metrics::{ + metric_name, register, F64SharedRef, Gauge, Metric, PrometheusError, Registry, + StandaloneMetric, F64, +}; +use sp_core::storage::{StorageData, StorageKey}; +use sp_runtime::{traits::UniqueSaturatedInto, FixedPointNumber, FixedU128}; +use std::{marker::PhantomData, time::Duration}; + +/// Storage value update interval (in blocks). +const UPDATE_INTERVAL_IN_BLOCKS: u32 = 5; + +/// Fied-point storage value and the way it is decoded from the raw storage value. +pub trait FloatStorageValue: 'static + Clone + Send + Sync { + /// Type of the value. + type Value: FixedPointNumber; + /// Try to decode value from the raw storage value. + fn decode( + &self, + maybe_raw_value: Option, + ) -> Result, SubstrateError>; +} + +/// Implementation of `FloatStorageValue` that expects encoded `FixedU128` value and returns `1` if +/// value is missing from the storage. +#[derive(Clone, Debug, Default)] +pub struct FixedU128OrOne; + +impl FloatStorageValue for FixedU128OrOne { + type Value = FixedU128; + + fn decode( + &self, + maybe_raw_value: Option, + ) -> Result, SubstrateError> { + maybe_raw_value + .map(|raw_value| { + FixedU128::decode(&mut &raw_value.0[..]) + .map_err(SubstrateError::ResponseParseFailed) + .map(Some) + }) + .unwrap_or_else(|| Ok(Some(FixedU128::one()))) + } +} + +/// Metric that represents fixed-point runtime storage value as float gauge. +#[derive(Clone, Debug)] +pub struct FloatStorageValueMetric { + value_converter: V, + client: Client, + storage_key: StorageKey, + metric: Gauge, + shared_value_ref: F64SharedRef, + _phantom: PhantomData, +} + +impl FloatStorageValueMetric { + /// Create new metric. + pub fn new( + value_converter: V, + client: Client, + storage_key: StorageKey, + name: String, + help: String, + ) -> Result { + let shared_value_ref = Arc::new(RwLock::new(None)); + Ok(FloatStorageValueMetric { + value_converter, + client, + storage_key, + metric: Gauge::new(metric_name(None, &name), help)?, + shared_value_ref, + _phantom: Default::default(), + }) + } + + /// Get shared reference to metric value. + pub fn shared_value_ref(&self) -> F64SharedRef { + self.shared_value_ref.clone() + } +} + +impl Metric for FloatStorageValueMetric { + fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { + register(self.metric.clone(), registry).map(drop) + } +} + +#[async_trait] +impl StandaloneMetric for FloatStorageValueMetric { + fn update_interval(&self) -> Duration { + C::AVERAGE_BLOCK_INTERVAL * UPDATE_INTERVAL_IN_BLOCKS + } + + async fn update(&self) { + let value = self + .client + .raw_storage_value(self.storage_key.clone(), None) + .await + .and_then(|maybe_storage_value| { + self.value_converter.decode(maybe_storage_value).map(|maybe_fixed_point_value| { + maybe_fixed_point_value.map(|fixed_point_value| { + fixed_point_value.into_inner().unique_saturated_into() as f64 / + V::Value::DIV.unique_saturated_into() as f64 + }) + }) + }) + .map_err(|e| e.to_string()); + relay_utils::metrics::set_gauge_value(&self.metric, value.clone()); + *self.shared_value_ref.write().await = value.ok().and_then(|x| x); + } +} diff --git a/relays/client-substrate/src/metrics/mod.rs b/relays/client-substrate/src/metrics/mod.rs new file mode 100644 index 00000000000..fe200e2d3dc --- /dev/null +++ b/relays/client-substrate/src/metrics/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Contains several Substrate-specific metrics that may be exposed by relay. + +pub use float_storage_value::{FixedU128OrOne, FloatStorageValue, FloatStorageValueMetric}; + +mod float_storage_value; diff --git a/relays/client-substrate/src/rpc.rs b/relays/client-substrate/src/rpc.rs new file mode 100644 index 00000000000..083b1dea761 --- /dev/null +++ b/relays/client-substrate/src/rpc.rs @@ -0,0 +1,170 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! The most generic Substrate node RPC interface. + +use async_trait::async_trait; + +use crate::{Chain, ChainWithGrandpa, TransactionStatusOf}; + +use jsonrpsee::{ + core::{client::Subscription, RpcResult}, + proc_macros::rpc, + ws_client::WsClient, +}; +use pallet_transaction_payment_rpc_runtime_api::FeeDetails; +use sc_rpc_api::{state::ReadProof, system::Health}; +use sp_core::{ + storage::{StorageData, StorageKey}, + Bytes, +}; +use sp_rpc::number::NumberOrHex; +use sp_version::RuntimeVersion; + +/// RPC methods of Substrate `system` namespace, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "system")] +pub(crate) trait SubstrateSystem { + /// Return node health. + #[method(name = "health")] + async fn health(&self) -> RpcResult; + /// Return system properties. + #[method(name = "properties")] + async fn properties(&self) -> RpcResult; +} + +/// RPC methods of Substrate `chain` namespace, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "chain")] +pub(crate) trait SubstrateChain { + /// Get block hash by its number. + #[method(name = "getBlockHash")] + async fn block_hash(&self, block_number: Option) -> RpcResult; + /// Return block header by its hash. + #[method(name = "getHeader")] + async fn header(&self, block_hash: Option) -> RpcResult; + /// Return best finalized block hash. + #[method(name = "getFinalizedHead")] + async fn finalized_head(&self) -> RpcResult; + /// Return signed block (with justifications) by its hash. + #[method(name = "getBlock")] + async fn block(&self, block_hash: Option) -> RpcResult; +} + +/// RPC methods of Substrate `author` namespace, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "author")] +pub(crate) trait SubstrateAuthor { + /// Submit extrinsic to the transaction pool. + #[method(name = "submitExtrinsic")] + async fn submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult; + /// Return vector of pending extrinsics from the transaction pool. + #[method(name = "pendingExtrinsics")] + async fn pending_extrinsics(&self) -> RpcResult>; + /// Submit and watch for extrinsic state. + #[subscription(name = "submitAndWatchExtrinsic", unsubscribe = "unwatchExtrinsic", item = TransactionStatusOf)] + fn submit_and_watch_extrinsic(&self, extrinsic: Bytes); +} + +/// RPC methods of Substrate `state` namespace, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "state")] +pub(crate) trait SubstrateState { + /// Get current runtime version. + #[method(name = "getRuntimeVersion")] + async fn runtime_version(&self) -> RpcResult; + /// Call given runtime method. + #[method(name = "call")] + async fn call( + &self, + method: String, + data: Bytes, + at_block: Option, + ) -> RpcResult; + /// Get value of the runtime storage. + #[method(name = "getStorage")] + async fn storage( + &self, + key: StorageKey, + at_block: Option, + ) -> RpcResult>; + /// Get proof of the runtime storage value. + #[method(name = "getReadProof")] + async fn prove_storage( + &self, + keys: Vec, + hash: Option, + ) -> RpcResult>; +} + +/// RPC methods that we are using for a certain finality gadget. +#[async_trait] +pub trait SubstrateFinalityClient { + /// Subscribe to finality justifications. + async fn subscribe_justifications(client: &WsClient) -> RpcResult>; +} + +/// RPC methods of Substrate `grandpa` namespace, that we are using. +#[rpc(client, client_bounds(C: ChainWithGrandpa), namespace = "grandpa")] +pub(crate) trait SubstrateGrandpa { + /// Subscribe to GRANDPA justifications. + #[subscription(name = "subscribeJustifications", unsubscribe = "unsubscribeJustifications", item = Bytes)] + fn subscribe_justifications(&self); +} + +/// RPC finality methods of Substrate `grandpa` namespace, that we are using. +pub struct SubstrateGrandpaFinalityClient; +#[async_trait] +impl SubstrateFinalityClient for SubstrateGrandpaFinalityClient { + async fn subscribe_justifications(client: &WsClient) -> RpcResult> { + SubstrateGrandpaClient::::subscribe_justifications(client).await + } +} + +// TODO: Use `ChainWithBeefy` instead of `Chain` after #1606 is merged +/// RPC methods of Substrate `beefy` namespace, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "beefy")] +pub(crate) trait SubstrateBeefy { + /// Subscribe to BEEFY justifications. + #[subscription(name = "subscribeJustifications", unsubscribe = "unsubscribeJustifications", item = Bytes)] + fn subscribe_justifications(&self); +} + +/// RPC finality methods of Substrate `beefy` namespace, that we are using. +pub struct SubstrateBeefyFinalityClient; +// TODO: Use `ChainWithBeefy` instead of `Chain` after #1606 is merged +#[async_trait] +impl SubstrateFinalityClient for SubstrateBeefyFinalityClient { + async fn subscribe_justifications(client: &WsClient) -> RpcResult> { + SubstrateBeefyClient::::subscribe_justifications(client).await + } +} + +/// RPC methods of Substrate `system` frame pallet, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "system")] +pub(crate) trait SubstrateFrameSystem { + /// Return index of next account transaction. + #[method(name = "accountNextIndex")] + async fn account_next_index(&self, account_id: C::AccountId) -> RpcResult; +} + +/// RPC methods of Substrate `pallet_transaction_payment` frame pallet, that we are using. +#[rpc(client, client_bounds(C: Chain), namespace = "payment")] +pub(crate) trait SubstrateTransactionPayment { + /// Query transaction fee details. + #[method(name = "queryFeeDetails")] + async fn fee_details( + &self, + extrinsic: Bytes, + at_block: Option, + ) -> RpcResult>; +} diff --git a/relays/client-substrate/src/sync_header.rs b/relays/client-substrate/src/sync_header.rs new file mode 100644 index 00000000000..fdfd1f22ce9 --- /dev/null +++ b/relays/client-substrate/src/sync_header.rs @@ -0,0 +1,61 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use bp_header_chain::ConsensusLogReader; +use finality_relay::SourceHeader as FinalitySourceHeader; +use sp_runtime::traits::Header as HeaderT; + +/// Generic wrapper for `sp_runtime::traits::Header` based headers, that +/// implements `finality_relay::SourceHeader` and may be used in headers sync directly. +#[derive(Clone, Debug, PartialEq)] +pub struct SyncHeader
(Header); + +impl
SyncHeader
{ + /// Extracts wrapped header from self. + pub fn into_inner(self) -> Header { + self.0 + } +} + +impl
std::ops::Deref for SyncHeader
{ + type Target = Header; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl
From
for SyncHeader
{ + fn from(header: Header) -> Self { + Self(header) + } +} + +impl FinalitySourceHeader + for SyncHeader
+{ + fn hash(&self) -> Header::Hash { + self.0.hash() + } + + fn number(&self) -> Header::Number { + *self.0.number() + } + + fn is_mandatory(&self) -> bool { + R::schedules_authorities_change(self.digest()) + } +} diff --git a/relays/client-substrate/src/test_chain.rs b/relays/client-substrate/src/test_chain.rs new file mode 100644 index 00000000000..64c7590ee52 --- /dev/null +++ b/relays/client-substrate/src/test_chain.rs @@ -0,0 +1,117 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Pallet provides a set of guard functions that are running in background threads +//! and are aborting process if some condition fails. + +//! Test chain implementation to use in tests. + +#![cfg(any(feature = "test-helpers", test))] + +use crate::{Chain, ChainWithBalances}; +use bp_runtime::ChainId; +use frame_support::weights::Weight; +use std::time::Duration; + +/// Chain that may be used in tests. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TestChain; + +impl bp_runtime::Chain for TestChain { + type BlockNumber = u32; + type Hash = sp_core::H256; + type Hasher = sp_runtime::traits::BlakeTwo256; + type Header = sp_runtime::generic::Header; + + type AccountId = u32; + type Balance = u32; + type Index = u32; + type Signature = sp_runtime::testing::TestSignature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + + fn max_extrinsic_weight() -> Weight { + unreachable!() + } +} + +impl Chain for TestChain { + const ID: ChainId = *b"test"; + const NAME: &'static str = "Test"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "TestMethod"; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(0); + + type SignedBlock = sp_runtime::generic::SignedBlock< + sp_runtime::generic::Block, + >; + type Call = (); +} + +impl ChainWithBalances for TestChain { + fn account_info_storage_key(_account_id: &u32) -> sp_core::storage::StorageKey { + unreachable!() + } +} + +/// Primitives-level parachain that may be used in tests. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TestParachainBase; + +impl bp_runtime::Chain for TestParachainBase { + type BlockNumber = u32; + type Hash = sp_core::H256; + type Hasher = sp_runtime::traits::BlakeTwo256; + type Header = sp_runtime::generic::Header; + + type AccountId = u32; + type Balance = u32; + type Index = u32; + type Signature = sp_runtime::testing::TestSignature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + + fn max_extrinsic_weight() -> Weight { + unreachable!() + } +} + +impl bp_runtime::Parachain for TestParachainBase { + const PARACHAIN_ID: u32 = 1000; +} + +/// Parachain that may be used in tests. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TestParachain; + +impl bp_runtime::UnderlyingChainProvider for TestParachain { + type Chain = TestParachainBase; +} + +impl Chain for TestParachain { + const ID: ChainId = *b"test"; + const NAME: &'static str = "TestParachain"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "TestParachainMethod"; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(0); + + type SignedBlock = sp_runtime::generic::SignedBlock< + sp_runtime::generic::Block, + >; + type Call = (); +} diff --git a/relays/client-substrate/src/transaction_tracker.rs b/relays/client-substrate/src/transaction_tracker.rs new file mode 100644 index 00000000000..211f7faab0e --- /dev/null +++ b/relays/client-substrate/src/transaction_tracker.rs @@ -0,0 +1,447 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Helper for tracking transaction invalidation events. + +use crate::{Chain, Client, Error, HashOf, HeaderIdOf, Subscription, TransactionStatusOf}; + +use async_trait::async_trait; +use futures::{future::Either, Future, FutureExt, Stream, StreamExt}; +use relay_utils::{HeaderId, TrackedTransactionStatus}; +use sp_runtime::traits::Header as _; +use std::time::Duration; + +/// Transaction tracker environment. +#[async_trait] +pub trait Environment: Send + Sync { + /// Returns header id by its hash. + async fn header_id_by_hash(&self, hash: HashOf) -> Result, Error>; +} + +#[async_trait] +impl Environment for Client { + async fn header_id_by_hash(&self, hash: HashOf) -> Result, Error> { + self.header_by_hash(hash).await.map(|h| HeaderId(*h.number(), hash)) + } +} + +/// Substrate transaction tracker implementation. +/// +/// Substrate node provides RPC API to submit and watch for transaction events. This way +/// we may know when transaction is included into block, finalized or rejected. There are +/// some edge cases, when we can't fully trust this mechanism - e.g. transaction may broadcasted +/// and then dropped out of node transaction pool (some other cases are also possible - node +/// restarts, connection lost, ...). Then we can't know for sure - what is currently happening +/// with our transaction. Is the transaction really lost? Is it still alive on the chain network? +/// +/// We have several options to handle such cases: +/// +/// 1) hope that the transaction is still alive and wait for its mining until it is spoiled; +/// +/// 2) assume that the transaction is lost and resubmit another transaction instantly; +/// +/// 3) wait for some time (if transaction is mortal - then until block where it dies; if it is +/// immortal - then for some time that we assume is long enough to mine it) and assume that +/// it is lost. +/// +/// This struct implements third option as it seems to be the most optimal. +pub struct TransactionTracker { + environment: E, + transaction_hash: HashOf, + stall_timeout: Duration, + subscription: Subscription>, +} + +impl> TransactionTracker { + /// Create transaction tracker. + pub fn new( + environment: E, + stall_timeout: Duration, + transaction_hash: HashOf, + subscription: Subscription>, + ) -> Self { + Self { environment, stall_timeout, transaction_hash, subscription } + } + + /// Wait for final transaction status and return it along with last known internal invalidation + /// status. + async fn do_wait( + self, + wait_for_stall_timeout: impl Future, + wait_for_stall_timeout_rest: impl Future, + ) -> (TrackedTransactionStatus>, Option>>) { + // sometimes we want to wait for the rest of the stall timeout even if + // `wait_for_invalidation` has been "select"ed first => it is shared + let wait_for_invalidation = watch_transaction_status::<_, C, _>( + self.environment, + self.transaction_hash, + self.subscription.into_stream(), + ); + futures::pin_mut!(wait_for_stall_timeout, wait_for_invalidation); + + match futures::future::select(wait_for_stall_timeout, wait_for_invalidation).await { + Either::Left((_, _)) => { + log::trace!( + target: "bridge", + "{} transaction {:?} is considered lost after timeout (no status response from the node)", + C::NAME, + self.transaction_hash, + ); + + (TrackedTransactionStatus::Lost, None) + }, + Either::Right((invalidation_status, _)) => match invalidation_status { + InvalidationStatus::Finalized(at_block) => + (TrackedTransactionStatus::Finalized(at_block), Some(invalidation_status)), + InvalidationStatus::Invalid => + (TrackedTransactionStatus::Lost, Some(invalidation_status)), + InvalidationStatus::Lost => { + // wait for the rest of stall timeout - this way we'll be sure that the + // transaction is actually dead if it has been crafted properly + wait_for_stall_timeout_rest.await; + // if someone is still watching for our transaction, then we're reporting + // an error here (which is treated as "transaction lost") + log::trace!( + target: "bridge", + "{} transaction {:?} is considered lost after timeout", + C::NAME, + self.transaction_hash, + ); + + (TrackedTransactionStatus::Lost, Some(invalidation_status)) + }, + }, + } + } +} + +#[async_trait] +impl> relay_utils::TransactionTracker for TransactionTracker { + type HeaderId = HeaderIdOf; + + async fn wait(self) -> TrackedTransactionStatus> { + let wait_for_stall_timeout = async_std::task::sleep(self.stall_timeout).shared(); + let wait_for_stall_timeout_rest = wait_for_stall_timeout.clone(); + self.do_wait(wait_for_stall_timeout, wait_for_stall_timeout_rest).await.0 + } +} + +/// Transaction invalidation status. +/// +/// Note that in places where the `TransactionTracker` is used, the finalization event will be +/// ignored - relay loops are detecting the mining/finalization using their own +/// techniques. That's why we're using `InvalidationStatus` here. +#[derive(Debug, PartialEq)] +enum InvalidationStatus { + /// Transaction has been included into block and finalized at given block. + Finalized(BlockId), + /// Transaction has been invalidated. + Invalid, + /// We have lost track of transaction status. + Lost, +} + +/// Watch for transaction status until transaction is finalized or we lose track of its status. +async fn watch_transaction_status< + E: Environment, + C: Chain, + S: Stream>, +>( + environment: E, + transaction_hash: HashOf, + subscription: S, +) -> InvalidationStatus> { + futures::pin_mut!(subscription); + + loop { + match subscription.next().await { + Some(TransactionStatusOf::::Finalized((block_hash, _))) => { + // the only "successful" outcome of this method is when the block with transaction + // has been finalized + log::trace!( + target: "bridge", + "{} transaction {:?} has been finalized at block: {:?}", + C::NAME, + transaction_hash, + block_hash, + ); + + let header_id = match environment.header_id_by_hash(block_hash).await { + Ok(header_id) => header_id, + Err(e) => { + log::error!( + target: "bridge", + "Failed to read header {:?} when watching for {} transaction {:?}: {:?}", + block_hash, + C::NAME, + transaction_hash, + e, + ); + // that's the best option we have here + return InvalidationStatus::Lost + }, + }; + return InvalidationStatus::Finalized(header_id) + }, + Some(TransactionStatusOf::::Invalid) => { + // if node says that the transaction is invalid, there are still chances that + // it is not actually invalid - e.g. if the block where transaction has been + // revalidated is retracted and transaction (at some other node pool) becomes + // valid again on other fork. But let's assume that the chances of this event + // are almost zero - there's a lot of things that must happen for this to be the + // case. + log::trace!( + target: "bridge", + "{} transaction {:?} has been invalidated", + C::NAME, + transaction_hash, + ); + return InvalidationStatus::Invalid + }, + Some(TransactionStatusOf::::Future) | + Some(TransactionStatusOf::::Ready) | + Some(TransactionStatusOf::::Broadcast(_)) => { + // nothing important (for us) has happened + }, + Some(TransactionStatusOf::::InBlock(block_hash)) => { + // TODO: read matching system event (ExtrinsicSuccess or ExtrinsicFailed), log it + // here and use it later (on finality) for reporting invalid transaction + // https://github.com/paritytech/parity-bridges-common/issues/1464 + log::trace!( + target: "bridge", + "{} transaction {:?} has been included in block: {:?}", + C::NAME, + transaction_hash, + block_hash, + ); + }, + Some(TransactionStatusOf::::Retracted(block_hash)) => { + log::trace!( + target: "bridge", + "{} transaction {:?} at block {:?} has been retracted", + C::NAME, + transaction_hash, + block_hash, + ); + }, + Some(TransactionStatusOf::::FinalityTimeout(block_hash)) => { + // finality is lagging? let's wait a bit more and report a stall + log::trace!( + target: "bridge", + "{} transaction {:?} block {:?} has not been finalized for too long", + C::NAME, + transaction_hash, + block_hash, + ); + return InvalidationStatus::Lost + }, + Some(TransactionStatusOf::::Usurped(new_transaction_hash)) => { + // this may be result of our transaction resubmitter work or some manual + // intervention. In both cases - let's start stall timeout, because the meaning + // of transaction may have changed + log::trace!( + target: "bridge", + "{} transaction {:?} has been usurped by new transaction: {:?}", + C::NAME, + transaction_hash, + new_transaction_hash, + ); + return InvalidationStatus::Lost + }, + Some(TransactionStatusOf::::Dropped) => { + // the transaction has been removed from the pool because of its limits. Let's wait + // a bit and report a stall + log::trace!( + target: "bridge", + "{} transaction {:?} has been dropped from the pool", + C::NAME, + transaction_hash, + ); + return InvalidationStatus::Lost + }, + None => { + // the status of transaction is unknown to us (the subscription has been closed?). + // Let's wait a bit and report a stall + return InvalidationStatus::Lost + }, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_chain::TestChain; + use futures::{FutureExt, SinkExt}; + use sc_transaction_pool_api::TransactionStatus; + + struct TestEnvironment(Result, Error>); + + #[async_trait] + impl Environment for TestEnvironment { + async fn header_id_by_hash( + &self, + _hash: HashOf, + ) -> Result, Error> { + self.0.as_ref().map_err(|_| Error::BridgePalletIsNotInitialized).cloned() + } + } + + async fn on_transaction_status( + status: TransactionStatus, HashOf>, + ) -> Option<( + TrackedTransactionStatus>, + InvalidationStatus>, + )> { + let (mut sender, receiver) = futures::channel::mpsc::channel(1); + let tx_tracker = TransactionTracker::::new( + TestEnvironment(Ok(HeaderId(0, Default::default()))), + Duration::from_secs(0), + Default::default(), + Subscription(async_std::sync::Mutex::new(receiver)), + ); + + let wait_for_stall_timeout = futures::future::pending(); + let wait_for_stall_timeout_rest = futures::future::ready(()); + sender.send(Some(status)).await.unwrap(); + tx_tracker + .do_wait(wait_for_stall_timeout, wait_for_stall_timeout_rest) + .now_or_never() + .map(|(ts, is)| (ts, is.unwrap())) + } + + #[async_std::test] + async fn returns_finalized_on_finalized() { + assert_eq!( + on_transaction_status(TransactionStatus::Finalized(Default::default())).await, + Some(( + TrackedTransactionStatus::Finalized(Default::default()), + InvalidationStatus::Finalized(Default::default()) + )), + ); + } + + #[async_std::test] + async fn returns_lost_on_finalized_and_environment_error() { + assert_eq!( + watch_transaction_status::<_, TestChain, _>( + TestEnvironment(Err(Error::BridgePalletIsNotInitialized)), + Default::default(), + futures::stream::iter([TransactionStatus::Finalized(Default::default())]) + ) + .now_or_never(), + Some(InvalidationStatus::Lost), + ); + } + + #[async_std::test] + async fn returns_invalid_on_invalid() { + assert_eq!( + on_transaction_status(TransactionStatus::Invalid).await, + Some((TrackedTransactionStatus::Lost, InvalidationStatus::Invalid)), + ); + } + + #[async_std::test] + async fn waits_on_future() { + assert_eq!(on_transaction_status(TransactionStatus::Future).await, None,); + } + + #[async_std::test] + async fn waits_on_ready() { + assert_eq!(on_transaction_status(TransactionStatus::Ready).await, None,); + } + + #[async_std::test] + async fn waits_on_broadcast() { + assert_eq!( + on_transaction_status(TransactionStatus::Broadcast(Default::default())).await, + None, + ); + } + + #[async_std::test] + async fn waits_on_in_block() { + assert_eq!( + on_transaction_status(TransactionStatus::InBlock(Default::default())).await, + None, + ); + } + + #[async_std::test] + async fn waits_on_retracted() { + assert_eq!( + on_transaction_status(TransactionStatus::Retracted(Default::default())).await, + None, + ); + } + + #[async_std::test] + async fn lost_on_finality_timeout() { + assert_eq!( + on_transaction_status(TransactionStatus::FinalityTimeout(Default::default())).await, + Some((TrackedTransactionStatus::Lost, InvalidationStatus::Lost)), + ); + } + + #[async_std::test] + async fn lost_on_usurped() { + assert_eq!( + on_transaction_status(TransactionStatus::Usurped(Default::default())).await, + Some((TrackedTransactionStatus::Lost, InvalidationStatus::Lost)), + ); + } + + #[async_std::test] + async fn lost_on_dropped() { + assert_eq!( + on_transaction_status(TransactionStatus::Dropped).await, + Some((TrackedTransactionStatus::Lost, InvalidationStatus::Lost)), + ); + } + + #[async_std::test] + async fn lost_on_subscription_error() { + assert_eq!( + watch_transaction_status::<_, TestChain, _>( + TestEnvironment(Ok(HeaderId(0, Default::default()))), + Default::default(), + futures::stream::iter([]) + ) + .now_or_never(), + Some(InvalidationStatus::Lost), + ); + } + + #[async_std::test] + async fn lost_on_timeout_when_waiting_for_invalidation_status() { + let (_sender, receiver) = futures::channel::mpsc::channel(1); + let tx_tracker = TransactionTracker::::new( + TestEnvironment(Ok(HeaderId(0, Default::default()))), + Duration::from_secs(0), + Default::default(), + Subscription(async_std::sync::Mutex::new(receiver)), + ); + + let wait_for_stall_timeout = futures::future::ready(()).shared(); + let wait_for_stall_timeout_rest = wait_for_stall_timeout.clone(); + let wait_result = tx_tracker + .do_wait(wait_for_stall_timeout, wait_for_stall_timeout_rest) + .now_or_never(); + + assert_eq!(wait_result, Some((TrackedTransactionStatus::Lost, None))); + } +} diff --git a/relays/client-westend/Cargo.toml b/relays/client-westend/Cargo.toml new file mode 100644 index 00000000000..351013bd864 --- /dev/null +++ b/relays/client-westend/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "relay-westend-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-runtime = { path = "../../primitives/runtime" } +bp-westend = { path = "../../primitives/chain-westend" } + +# Substrate Dependencies + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-westend/src/lib.rs b/relays/client-westend/src/lib.rs new file mode 100644 index 00000000000..6013bfad128 --- /dev/null +++ b/relays/client-westend/src/lib.rs @@ -0,0 +1,80 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Westend chain. + +use bp_runtime::ChainId; +use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider}; +use sp_core::storage::StorageKey; +use std::time::Duration; + +/// Westend header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Westend header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + +/// Westend chain definition +#[derive(Debug, Clone, Copy)] +pub struct Westend; + +impl UnderlyingChainProvider for Westend { + type Chain = bp_westend::Westend; +} + +impl Chain for Westend { + const ID: ChainId = bp_runtime::WESTEND_CHAIN_ID; + const NAME: &'static str = "Westend"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_westend::BEST_FINALIZED_WESTEND_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type SignedBlock = bp_westend::SignedBlock; + type Call = (); +} + +impl RelayChain for Westend { + const PARAS_PALLET_NAME: &'static str = bp_westend::PARAS_PALLET_NAME; + const PARACHAINS_FINALITY_PALLET_NAME: &'static str = + bp_westend::WITH_WESTEND_BRIDGE_PARAS_PALLET_NAME; +} + +impl ChainWithBalances for Westend { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + bp_westend::AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +/// Westmint parachain definition +#[derive(Debug, Clone, Copy)] +pub struct Westmint; + +impl UnderlyingChainProvider for Westmint { + type Chain = bp_westend::Westmint; +} + +// Westmint seems to use the same configuration as all Polkadot-like chains, so we'll use Westend +// primitives here. +impl Chain for Westmint { + const ID: ChainId = bp_runtime::WESTMINT_CHAIN_ID; + const NAME: &'static str = "Westmint"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_westend::BEST_FINALIZED_WESTMINT_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type SignedBlock = bp_westend::SignedBlock; + type Call = (); +} diff --git a/relays/client-wococo/Cargo.toml b/relays/client-wococo/Cargo.toml new file mode 100644 index 00000000000..78a5afe17ca --- /dev/null +++ b/relays/client-wococo/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "relay-wococo-client" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +relay-substrate-client = { path = "../client-substrate" } +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-runtime = { path = "../../primitives/runtime" } +bp-wococo = { path = "../../primitives/chain-wococo" } + +# Substrate Dependencies +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-wococo/src/lib.rs b/relays/client-wococo/src/lib.rs new file mode 100644 index 00000000000..2f0d6b22f92 --- /dev/null +++ b/relays/client-wococo/src/lib.rs @@ -0,0 +1,58 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types used to connect to the Wococo-Substrate chain. + +use bp_runtime::ChainId; +use relay_substrate_client::{Chain, ChainWithBalances, RelayChain, UnderlyingChainProvider}; +use sp_core::storage::StorageKey; +use std::time::Duration; + +/// Wococo header id. +pub type HeaderId = relay_utils::HeaderId; + +/// Wococo header type used in headers sync. +pub type SyncHeader = relay_substrate_client::SyncHeader; + +/// Wococo chain definition +#[derive(Debug, Clone, Copy)] +pub struct Wococo; + +impl UnderlyingChainProvider for Wococo { + type Chain = bp_wococo::Wococo; +} + +impl Chain for Wococo { + const ID: ChainId = bp_runtime::WOCOCO_CHAIN_ID; + const NAME: &'static str = "Wococo"; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD; + const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); + + type SignedBlock = bp_wococo::SignedBlock; + type Call = (); +} + +impl ChainWithBalances for Wococo { + fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { + bp_wococo::AccountInfoStorageMapKeyProvider::final_key(account_id) + } +} + +impl RelayChain for Wococo { + const PARAS_PALLET_NAME: &'static str = bp_wococo::PARAS_PALLET_NAME; + const PARACHAINS_FINALITY_PALLET_NAME: &'static str = "BridgeWococoParachain"; +} diff --git a/relays/finality/Cargo.toml b/relays/finality/Cargo.toml new file mode 100644 index 00000000000..ab75533b023 --- /dev/null +++ b/relays/finality/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "finality-relay" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +description = "Finality proofs relay" + +[dependencies] +async-std = "1.6.5" +async-trait = "0.1" +backoff = "0.4" +bp-header-chain = { path = "../../primitives/header-chain" } +futures = "0.3.28" +log = "0.4.17" +num-traits = "0.2" +relay-utils = { path = "../utils" } + +[dev-dependencies] +parking_lot = "0.12.1" diff --git a/relays/finality/README.md b/relays/finality/README.md new file mode 100644 index 00000000000..444056e7563 --- /dev/null +++ b/relays/finality/README.md @@ -0,0 +1,58 @@ +# GRANDPA Finality Relay + +The finality relay is able to work with different finality engines. In the modern Substrate world they are GRANDPA +and BEEFY. Let's talk about GRANDPA here, because BEEFY relay and bridge BEEFY pallet are in development. + +In general, the relay works as follows: it connects to the source and target chain. The source chain must have the +[GRANDPA gadget](https://github.com/paritytech/finality-grandpa) running (so it can't be a parachain). The target +chain must have the [bridge GRANDPA pallet](../../modules/grandpa/) deployed at its runtime. The relay subscribes +to the GRANDPA finality notifications at the source chain and when the new justification is received, it is submitted +to the pallet at the target chain. + +Apart from that, the relay is watching for every source header that is missing at target. If it finds the missing +mandatory header (header that is changing the current GRANDPA validators set), it submits the justification for +this header. The case when the source node can't return the mandatory justification is considered a fatal error, +because the pallet can't proceed without it. + +More: [GRANDPA Finality Relay Sequence Diagram](../../docs/grandpa-finality-relay.html). + +## How to Use the Finality Relay + +The most important trait is the [`FinalitySyncPipeline`](./src/lib.rs), which defines the basic primitives of the +source chain (like block hash and number) and the type of finality proof (GRANDPA justification or MMR proof). Once +that is defined, there are two other traits - [`SourceClient`](./src/finality_loop.rs) and +[`TargetClient`](./src/finality_loop.rs). + +The `SourceClient` represents the Substrate node client that connects to the source chain. The client needs to +be able to return the best finalized header number, finalized header and its finality proof and the stream of +finality proofs. + +The `TargetClient` implementation must be able to craft finality delivery transaction and submit it to the target +node. The transaction is then tracked by the relay until it is mined and finalized. + +The main entrypoint for the crate is the [`run` function](./src/finality_loop.rs), which takes source and target +clients and [`FinalitySyncParams`](./src/finality_loop.rs) parameters. The most important parameter is the +`only_mandatory_headers` - it is set to `true`, the relay will only submit mandatory headers. Since transactions +with mandatory headers are fee-free, the cost of running such relay is zero (in terms of fees). + +## Finality Relay Metrics + +Finality relay provides several metrics. Metrics names depend on names of source and target chains. The list below +shows metrics names for Rialto (source chain) to Millau (target chain) finality relay. For other chains, simply +change chain names. So the metrics are: + +- `Rialto_to_Millau_Sync_best_source_block_number` - returns best finalized source chain (Rialto) block number, known + to the relay. If relay is running in [on-demand mode](../bin-substrate/src/cli/relay_headers_and_messages/), the + number may not match (it may be far behind) the actual best finalized number; + +- `Rialto_to_Millau_Sync_best_source_at_target_block_number` - returns best finalized source chain (Rialto) block + number that is known to the bridge GRANDPA pallet at the target chain. + +- `Rialto_to_Millau_Sync_is_source_and_source_at_target_using_different_forks` - if this metrics is set to `1`, then + the best source chain header, known to the target chain doesn't match the same-number-header at the source chain. + It means that the GRANDPA validators set has crafted the duplicate justification and it has been submitted to the + target chain. Normally (if majority of validators are honest and if you're running finality relay without large + breaks) this shall not happen and the metric will have `0` value. + +If relay operates properly, you should see that the `Rialto_to_Millau_Sync_best_source_at_target_block_number` +tries to reach the `Rialto_to_Millau_Sync_best_source_block_number`. And the latter one always increases. diff --git a/relays/finality/src/finality_loop.rs b/relays/finality/src/finality_loop.rs new file mode 100644 index 00000000000..1ee1a8d9db6 --- /dev/null +++ b/relays/finality/src/finality_loop.rs @@ -0,0 +1,761 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! The loop basically reads all missing headers and their finality proofs from the source client. +//! The proof for the best possible header is then submitted to the target node. The only exception +//! is the mandatory headers, which we always submit to the target node. For such headers, we +//! assume that the persistent proof either exists, or will eventually become available. + +use crate::{ + sync_loop_metrics::SyncLoopMetrics, FinalityProof, FinalitySyncPipeline, SourceHeader, +}; + +use async_trait::async_trait; +use backoff::backoff::Backoff; +use futures::{select, Future, FutureExt, Stream, StreamExt}; +use num_traits::{One, Saturating}; +use relay_utils::{ + metrics::MetricsParams, relay_loop::Client as RelayClient, retry_backoff, FailedClient, + HeaderId, MaybeConnectionError, TrackedTransactionStatus, TransactionTracker, +}; +use std::{ + fmt::Debug, + pin::Pin, + time::{Duration, Instant}, +}; + +/// Finality proof synchronization loop parameters. +#[derive(Debug, Clone)] +pub struct FinalitySyncParams { + /// Interval at which we check updates on both clients. Normally should be larger than + /// `min(source_block_time, target_block_time)`. + /// + /// This parameter may be used to limit transactions rate. Increase the value && you'll get + /// infrequent updates => sparse headers => potential slow down of bridge applications, but + /// pallet storage won't be super large. Decrease the value to near `source_block_time` and + /// you'll get transaction for (almost) every block of the source chain => all source headers + /// will be known to the target chain => bridge applications will run faster, but pallet + /// storage may explode (but if pruning is there, then it's fine). + pub tick: Duration, + /// Number of finality proofs to keep in internal buffer between loop iterations. + /// + /// While in "major syncing" state, we still read finality proofs from the stream. They're + /// stored in the internal buffer between loop iterations. When we're close to the tip of the + /// chain, we may meet finality delays if headers are not finalized frequently. So instead of + /// waiting for next finality proof to appear in the stream, we may use existing proof from + /// that buffer. + pub recent_finality_proofs_limit: usize, + /// Timeout before we treat our transactions as lost and restart the whole sync process. + pub stall_timeout: Duration, + /// If true, only mandatory headers are relayed. + pub only_mandatory_headers: bool, +} + +/// Source client used in finality synchronization loop. +#[async_trait] +pub trait SourceClient: RelayClient { + /// Stream of new finality proofs. The stream is allowed to miss proofs for some + /// headers, even if those headers are mandatory. + type FinalityProofsStream: Stream + Send; + + /// Get best finalized block number. + async fn best_finalized_block_number(&self) -> Result; + + /// Get canonical header and its finality proof by number. + async fn header_and_finality_proof( + &self, + number: P::Number, + ) -> Result<(P::Header, Option), Self::Error>; + + /// Subscribe to new finality proofs. + async fn finality_proofs(&self) -> Result; +} + +/// Target client used in finality synchronization loop. +#[async_trait] +pub trait TargetClient: RelayClient { + /// Transaction tracker to track submitted transactions. + type TransactionTracker: TransactionTracker; + + /// Get best finalized source block number. + async fn best_finalized_source_block_id( + &self, + ) -> Result, Self::Error>; + + /// Submit header finality proof. + async fn submit_finality_proof( + &self, + header: P::Header, + proof: P::FinalityProof, + ) -> Result; +} + +/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs +/// sync loop. +pub fn metrics_prefix() -> String { + format!("{}_to_{}_Sync", P::SOURCE_NAME, P::TARGET_NAME) +} + +/// Run finality proofs synchronization loop. +pub async fn run( + source_client: impl SourceClient

, + target_client: impl TargetClient

, + sync_params: FinalitySyncParams, + metrics_params: MetricsParams, + exit_signal: impl Future + 'static + Send, +) -> Result<(), relay_utils::Error> { + let exit_signal = exit_signal.shared(); + relay_utils::relay_loop(source_client, target_client) + .with_metrics(metrics_params) + .loop_metric(SyncLoopMetrics::new( + Some(&metrics_prefix::

()), + "source", + "source_at_target", + )?)? + .expose() + .await? + .run(metrics_prefix::

(), move |source_client, target_client, metrics| { + run_until_connection_lost( + source_client, + target_client, + sync_params.clone(), + metrics, + exit_signal.clone(), + ) + }) + .await +} + +/// Unjustified headers container. Ordered by header number. +pub(crate) type UnjustifiedHeaders = Vec; +/// Finality proofs container. Ordered by target header number. +pub(crate) type FinalityProofs

= + Vec<(

::Number,

::FinalityProof)>; +/// Reference to finality proofs container. +pub(crate) type FinalityProofsRef<'a, P> = + &'a [(

::Number,

::FinalityProof)]; + +/// Error that may happen inside finality synchronization loop. +#[derive(Debug)] +pub(crate) enum Error { + /// Source client request has failed with given error. + Source(SourceError), + /// Target client request has failed with given error. + Target(TargetError), + /// Finality proof for mandatory header is missing from the source node. + MissingMandatoryFinalityProof(P::Number), +} + +impl Error +where + P: FinalitySyncPipeline, + SourceError: MaybeConnectionError, + TargetError: MaybeConnectionError, +{ + fn fail_if_connection_error(&self) -> Result<(), FailedClient> { + match *self { + Error::Source(ref error) if error.is_connection_error() => Err(FailedClient::Source), + Error::Target(ref error) if error.is_connection_error() => Err(FailedClient::Target), + _ => Ok(()), + } + } +} + +/// Information about transaction that we have submitted. +#[derive(Debug, Clone)] +pub(crate) struct Transaction { + /// Submitted transaction tracker. + pub tracker: Tracker, + /// The number of the header we have submitted. + pub submitted_header_number: Number, +} + +impl Transaction { + pub async fn submit< + C: TargetClient, + P: FinalitySyncPipeline, + >( + target_client: &C, + header: P::Header, + justification: P::FinalityProof, + ) -> Result { + let submitted_header_number = header.number(); + log::debug!( + target: "bridge", + "Going to submit finality proof of {} header #{:?} to {}", + P::SOURCE_NAME, + submitted_header_number, + P::TARGET_NAME, + ); + + let tracker = target_client.submit_finality_proof(header, justification).await?; + Ok(Transaction { tracker, submitted_header_number }) + } + + pub async fn track, P: FinalitySyncPipeline>( + self, + target_client: &C, + ) -> Result<(), String> { + match self.tracker.wait().await { + TrackedTransactionStatus::Finalized(_) => { + // The transaction has been finalized, but it may have been finalized in the + // "failed" state. So let's check if the block number was actually updated. + // If it wasn't then we are stalled. + // + // Please also note that we're returning an error if we fail to read required data + // from the target client - that's the best we can do here to avoid actual stall. + target_client + .best_finalized_source_block_id() + .await + .map_err(|e| format!("failed to read best block from target node: {e:?}")) + .and_then(|best_id_at_target| { + if self.submitted_header_number > best_id_at_target.0 { + return Err(format!( + "best block at target after tx is {:?} and we've submitted {:?}", + best_id_at_target.0, self.submitted_header_number, + )) + } + Ok(()) + }) + }, + TrackedTransactionStatus::Lost => Err("transaction failed".to_string()), + } + } +} + +/// Finality proofs stream that may be restarted. +pub(crate) struct RestartableFinalityProofsStream { + /// Flag that the stream needs to be restarted. + pub(crate) needs_restart: bool, + /// The stream itself. + stream: Pin>, +} + +impl RestartableFinalityProofsStream { + pub async fn create_raw_stream< + C: SourceClient, + P: FinalitySyncPipeline, + >( + source_client: &C, + ) -> Result { + source_client.finality_proofs().await.map_err(|error| { + log::error!( + target: "bridge", + "Failed to subscribe to {} justifications: {:?}. Going to reconnect", + P::SOURCE_NAME, + error, + ); + + FailedClient::Source + }) + } + + pub async fn restart_if_scheduled< + C: SourceClient, + P: FinalitySyncPipeline, + >( + &mut self, + source_client: &C, + ) -> Result<(), FailedClient> { + if self.needs_restart { + log::warn!(target: "bridge", "{} finality proofs stream is being restarted", P::SOURCE_NAME); + + self.needs_restart = false; + self.stream = Box::pin(Self::create_raw_stream(source_client).await?); + } + Ok(()) + } + + pub fn next(&mut self) -> Option { + match self.stream.next().now_or_never() { + Some(Some(finality_proof)) => Some(finality_proof), + Some(None) => { + self.needs_restart = true; + None + }, + None => None, + } + } +} + +impl From for RestartableFinalityProofsStream { + fn from(stream: S) -> Self { + RestartableFinalityProofsStream { needs_restart: false, stream: Box::pin(stream) } + } +} + +/// Finality synchronization loop state. +pub(crate) struct FinalityLoopState<'a, P: FinalitySyncPipeline, FinalityProofsStream> { + /// Synchronization loop progress. + pub(crate) progress: &'a mut (Instant, Option), + /// Finality proofs stream. + pub(crate) finality_proofs_stream: + &'a mut RestartableFinalityProofsStream, + /// Recent finality proofs that we have read from the stream. + pub(crate) recent_finality_proofs: &'a mut FinalityProofs

, + /// Number of the last header, submitted to the target node. + pub(crate) submitted_header_number: Option, +} + +/// Run finality relay loop until connection to one of nodes is lost. +pub(crate) async fn run_until_connection_lost( + source_client: impl SourceClient

, + target_client: impl TargetClient

, + sync_params: FinalitySyncParams, + metrics_sync: Option, + exit_signal: impl Future, +) -> Result<(), FailedClient> { + let last_transaction_tracker = futures::future::Fuse::terminated(); + let exit_signal = exit_signal.fuse(); + futures::pin_mut!(last_transaction_tracker, exit_signal); + + let mut finality_proofs_stream = + RestartableFinalityProofsStream::create_raw_stream(&source_client).await?.into(); + let mut recent_finality_proofs = Vec::new(); + + let mut progress = (Instant::now(), None); + let mut retry_backoff = retry_backoff(); + let mut last_submitted_header_number = None; + + loop { + // run loop iteration + let iteration_result = run_loop_iteration( + &source_client, + &target_client, + FinalityLoopState { + progress: &mut progress, + finality_proofs_stream: &mut finality_proofs_stream, + recent_finality_proofs: &mut recent_finality_proofs, + submitted_header_number: last_submitted_header_number, + }, + &sync_params, + &metrics_sync, + ) + .await; + + // deal with errors + let next_tick = match iteration_result { + Ok(Some(updated_transaction)) => { + last_submitted_header_number = Some(updated_transaction.submitted_header_number); + last_transaction_tracker.set(updated_transaction.track(&target_client).fuse()); + retry_backoff.reset(); + sync_params.tick + }, + Ok(None) => { + retry_backoff.reset(); + sync_params.tick + }, + Err(error) => { + log::error!(target: "bridge", "Finality sync loop iteration has failed with error: {:?}", error); + error.fail_if_connection_error()?; + retry_backoff.next_backoff().unwrap_or(relay_utils::relay_loop::RECONNECT_DELAY) + }, + }; + finality_proofs_stream.restart_if_scheduled(&source_client).await?; + + // wait till exit signal, or new source block + select! { + transaction_result = last_transaction_tracker => { + transaction_result.map_err(|e| { + log::error!( + target: "bridge", + "Finality synchronization from {} to {} has stalled with error: {}. Going to restart", + P::SOURCE_NAME, + P::TARGET_NAME, + e, + ); + + // Restart the loop if we're stalled. + FailedClient::Both + })? + }, + _ = async_std::task::sleep(next_tick).fuse() => {}, + _ = exit_signal => return Ok(()), + } + } +} + +pub(crate) async fn run_loop_iteration( + source_client: &SC, + target_client: &TC, + state: FinalityLoopState<'_, P, SC::FinalityProofsStream>, + sync_params: &FinalitySyncParams, + metrics_sync: &Option, +) -> Result>, Error> +where + P: FinalitySyncPipeline, + SC: SourceClient

, + TC: TargetClient

, +{ + // read best source headers ids from source and target nodes + let best_number_at_source = + source_client.best_finalized_block_number().await.map_err(Error::Source)?; + let best_id_at_target = + target_client.best_finalized_source_block_id().await.map_err(Error::Target)?; + let best_number_at_target = best_id_at_target.0; + + let different_hash_at_source = ensure_same_fork::(&best_id_at_target, source_client) + .await + .map_err(Error::Source)?; + let using_same_fork = different_hash_at_source.is_none(); + if let Some(ref different_hash_at_source) = different_hash_at_source { + log::error!( + target: "bridge", + "Source node ({}) and pallet at target node ({}) have different headers at the same height {:?}: \ + at-source {:?} vs at-target {:?}", + P::SOURCE_NAME, + P::TARGET_NAME, + best_number_at_target, + different_hash_at_source, + best_id_at_target.1, + ); + } + + if let Some(ref metrics_sync) = *metrics_sync { + metrics_sync.update_best_block_at_source(best_number_at_source); + metrics_sync.update_best_block_at_target(best_number_at_target); + metrics_sync.update_using_same_fork(using_same_fork); + } + *state.progress = + print_sync_progress::

(*state.progress, best_number_at_source, best_number_at_target); + + // if we have already submitted header, then we just need to wait for it + // if we're waiting too much, then we believe our transaction has been lost and restart sync + if let Some(submitted_header_number) = state.submitted_header_number { + if best_number_at_target >= submitted_header_number { + // transaction has been mined && we can continue + } else { + return Ok(None) + } + } + + // submit new header if we have something new + match select_header_to_submit( + source_client, + target_client, + state.finality_proofs_stream, + state.recent_finality_proofs, + best_number_at_source, + best_number_at_target, + sync_params, + ) + .await? + { + Some((header, justification)) => { + let transaction = Transaction::submit(target_client, header, justification) + .await + .map_err(Error::Target)?; + Ok(Some(transaction)) + }, + None => Ok(None), + } +} + +pub(crate) async fn select_header_to_submit( + source_client: &SC, + target_client: &TC, + finality_proofs_stream: &mut RestartableFinalityProofsStream, + recent_finality_proofs: &mut FinalityProofs

, + best_number_at_source: P::Number, + best_number_at_target: P::Number, + sync_params: &FinalitySyncParams, +) -> Result, Error> +where + P: FinalitySyncPipeline, + SC: SourceClient

, + TC: TargetClient

, +{ + // to see that the loop is progressing + log::trace!( + target: "bridge", + "Considering range of headers ({:?}; {:?}]", + best_number_at_target, + best_number_at_source, + ); + + // read missing headers. if we see that the header schedules GRANDPA change, we need to + // submit this header + let selected_finality_proof = read_missing_headers::( + source_client, + target_client, + best_number_at_source, + best_number_at_target, + ) + .await?; + let (mut unjustified_headers, mut selected_finality_proof) = match selected_finality_proof { + SelectedFinalityProof::Mandatory(header, finality_proof) => + return Ok(Some((header, finality_proof))), + _ if sync_params.only_mandatory_headers => { + // we are not reading finality proofs from the stream, so eventually it'll break + // but we don't care about transient proofs at all, so it is acceptable + return Ok(None) + }, + SelectedFinalityProof::Regular(unjustified_headers, header, finality_proof) => + (unjustified_headers, Some((header, finality_proof))), + SelectedFinalityProof::None(unjustified_headers) => (unjustified_headers, None), + }; + + // all headers that are missing from the target client are non-mandatory + // => even if we have already selected some header and its persistent finality proof, + // we may try to select better header by reading non-persistent proofs from the stream + read_finality_proofs_from_stream::(finality_proofs_stream, recent_finality_proofs); + selected_finality_proof = select_better_recent_finality_proof::

( + recent_finality_proofs, + &mut unjustified_headers, + selected_finality_proof, + ); + + // remove obsolete 'recent' finality proofs + keep its size under certain limit + let oldest_finality_proof_to_keep = selected_finality_proof + .as_ref() + .map(|(header, _)| header.number()) + .unwrap_or(best_number_at_target); + prune_recent_finality_proofs::

( + oldest_finality_proof_to_keep, + recent_finality_proofs, + sync_params.recent_finality_proofs_limit, + ); + + Ok(selected_finality_proof) +} + +/// Ensures that both clients are on the same fork. +/// +/// Returns `Some(_)` with header has at the source client if headers are different. +async fn ensure_same_fork>( + best_id_at_target: &HeaderId, + source_client: &SC, +) -> Result, SC::Error> { + let header_at_source = source_client.header_and_finality_proof(best_id_at_target.0).await?.0; + let header_hash_at_source = header_at_source.hash(); + Ok(if best_id_at_target.1 == header_hash_at_source { + None + } else { + Some(header_hash_at_source) + }) +} + +/// Finality proof that has been selected by the `read_missing_headers` function. +pub(crate) enum SelectedFinalityProof { + /// Mandatory header and its proof has been selected. We shall submit proof for this header. + Mandatory(Header, FinalityProof), + /// Regular header and its proof has been selected. We may submit this proof, or proof for + /// some better header. + Regular(UnjustifiedHeaders

, Header, FinalityProof), + /// We haven't found any missing header with persistent proof at the target client. + None(UnjustifiedHeaders
), +} + +/// Read missing headers and their persistent finality proofs from the target client. +/// +/// If we have found some header with known proof, it is returned. +/// Otherwise, `SelectedFinalityProof::None` is returned. +/// +/// Unless we have found mandatory header, all missing headers are collected and returned. +pub(crate) async fn read_missing_headers< + P: FinalitySyncPipeline, + SC: SourceClient

, + TC: TargetClient

, +>( + source_client: &SC, + _target_client: &TC, + best_number_at_source: P::Number, + best_number_at_target: P::Number, +) -> Result, Error> { + let mut unjustified_headers = Vec::new(); + let mut selected_finality_proof = None; + let mut header_number = best_number_at_target + One::one(); + while header_number <= best_number_at_source { + let (header, finality_proof) = source_client + .header_and_finality_proof(header_number) + .await + .map_err(Error::Source)?; + let is_mandatory = header.is_mandatory(); + + match (is_mandatory, finality_proof) { + (true, Some(finality_proof)) => { + log::trace!(target: "bridge", "Header {:?} is mandatory", header_number); + return Ok(SelectedFinalityProof::Mandatory(header, finality_proof)) + }, + (true, None) => return Err(Error::MissingMandatoryFinalityProof(header.number())), + (false, Some(finality_proof)) => { + log::trace!(target: "bridge", "Header {:?} has persistent finality proof", header_number); + unjustified_headers.clear(); + selected_finality_proof = Some((header, finality_proof)); + }, + (false, None) => { + unjustified_headers.push(header); + }, + } + + header_number = header_number + One::one(); + } + + log::trace!( + target: "bridge", + "Read {} {} headers. Selected finality proof for header: {:?}", + best_number_at_source.saturating_sub(best_number_at_target), + P::SOURCE_NAME, + selected_finality_proof.as_ref().map(|(header, _)| header), + ); + + Ok(match selected_finality_proof { + Some((header, proof)) => SelectedFinalityProof::Regular(unjustified_headers, header, proof), + None => SelectedFinalityProof::None(unjustified_headers), + }) +} + +/// Read finality proofs from the stream. +pub(crate) fn read_finality_proofs_from_stream< + P: FinalitySyncPipeline, + FPS: Stream, +>( + finality_proofs_stream: &mut RestartableFinalityProofsStream, + recent_finality_proofs: &mut FinalityProofs

, +) { + let mut proofs_count = 0; + let mut first_header_number = None; + let mut last_header_number = None; + while let Some(finality_proof) = finality_proofs_stream.next() { + let target_header_number = finality_proof.target_header_number(); + if first_header_number.is_none() { + first_header_number = Some(target_header_number); + } + last_header_number = Some(target_header_number); + proofs_count += 1; + + recent_finality_proofs.push((target_header_number, finality_proof)); + } + + if proofs_count != 0 { + log::trace!( + target: "bridge", + "Read {} finality proofs from {} finality stream for headers in range [{:?}; {:?}]", + proofs_count, + P::SOURCE_NAME, + first_header_number, + last_header_number, + ); + } +} + +/// Try to select better header and its proof, given finality proofs that we +/// have recently read from the stream. +pub(crate) fn select_better_recent_finality_proof( + recent_finality_proofs: FinalityProofsRef

, + unjustified_headers: &mut UnjustifiedHeaders, + selected_finality_proof: Option<(P::Header, P::FinalityProof)>, +) -> Option<(P::Header, P::FinalityProof)> { + if unjustified_headers.is_empty() || recent_finality_proofs.is_empty() { + log::trace!( + target: "bridge", + "Can not improve selected {} finality proof {:?}. No unjustified headers and recent proofs", + P::SOURCE_NAME, + selected_finality_proof.as_ref().map(|(h, _)| h.number()), + ); + return selected_finality_proof + } + + const NOT_EMPTY_PROOF: &str = "we have checked that the vec is not empty; qed"; + + // we need proofs for headers in range unjustified_range_begin..=unjustified_range_end + let unjustified_range_begin = unjustified_headers.first().expect(NOT_EMPTY_PROOF).number(); + let unjustified_range_end = unjustified_headers.last().expect(NOT_EMPTY_PROOF).number(); + + // we have proofs for headers in range buffered_range_begin..=buffered_range_end + let buffered_range_begin = recent_finality_proofs.first().expect(NOT_EMPTY_PROOF).0; + let buffered_range_end = recent_finality_proofs.last().expect(NOT_EMPTY_PROOF).0; + + // we have two ranges => find intersection + let intersection_begin = std::cmp::max(unjustified_range_begin, buffered_range_begin); + let intersection_end = std::cmp::min(unjustified_range_end, buffered_range_end); + let intersection = intersection_begin..=intersection_end; + + // find last proof from intersection + let selected_finality_proof_index = recent_finality_proofs + .binary_search_by_key(intersection.end(), |(number, _)| *number) + .unwrap_or_else(|index| index.saturating_sub(1)); + let (selected_header_number, finality_proof) = + &recent_finality_proofs[selected_finality_proof_index]; + let has_selected_finality_proof = intersection.contains(selected_header_number); + log::trace!( + target: "bridge", + "Trying to improve selected {} finality proof {:?}. Headers range: [{:?}; {:?}]. Proofs range: [{:?}; {:?}].\ + Trying to improve to: {:?}. Result: {}", + P::SOURCE_NAME, + selected_finality_proof.as_ref().map(|(h, _)| h.number()), + unjustified_range_begin, + unjustified_range_end, + buffered_range_begin, + buffered_range_end, + selected_header_number, + if has_selected_finality_proof { "improved" } else { "not improved" }, + ); + if !has_selected_finality_proof { + return selected_finality_proof + } + + // now remove all obsolete headers and extract selected header + let selected_header_position = unjustified_headers + .binary_search_by_key(selected_header_number, |header| header.number()) + .expect("unjustified_headers contain all headers from intersection; qed"); + let selected_header = unjustified_headers.swap_remove(selected_header_position); + Some((selected_header, finality_proof.clone())) +} + +pub(crate) fn prune_recent_finality_proofs( + justified_header_number: P::Number, + recent_finality_proofs: &mut FinalityProofs

, + recent_finality_proofs_limit: usize, +) { + let justified_header_idx = recent_finality_proofs + .binary_search_by_key(&justified_header_number, |(header_number, _)| *header_number) + .map(|idx| idx + 1) + .unwrap_or_else(|idx| idx); + let proofs_limit_idx = + recent_finality_proofs.len().saturating_sub(recent_finality_proofs_limit); + + *recent_finality_proofs = + recent_finality_proofs.split_off(std::cmp::max(justified_header_idx, proofs_limit_idx)); +} + +fn print_sync_progress( + progress_context: (Instant, Option), + best_number_at_source: P::Number, + best_number_at_target: P::Number, +) -> (Instant, Option) { + let (prev_time, prev_best_number_at_target) = progress_context; + let now = Instant::now(); + + let need_update = now - prev_time > Duration::from_secs(10) || + prev_best_number_at_target + .map(|prev_best_number_at_target| { + best_number_at_target.saturating_sub(prev_best_number_at_target) > 10.into() + }) + .unwrap_or(true); + + if !need_update { + return (prev_time, prev_best_number_at_target) + } + + log::info!( + target: "bridge", + "Synced {:?} of {:?} headers", + best_number_at_target, + best_number_at_source, + ); + (now, Some(best_number_at_target)) +} diff --git a/relays/finality/src/finality_loop_tests.rs b/relays/finality/src/finality_loop_tests.rs new file mode 100644 index 00000000000..1853c095f70 --- /dev/null +++ b/relays/finality/src/finality_loop_tests.rs @@ -0,0 +1,598 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Tests for finality synchronization loop. + +#![cfg(test)] + +use crate::{ + finality_loop::{ + prune_recent_finality_proofs, read_finality_proofs_from_stream, run_loop_iteration, + run_until_connection_lost, select_better_recent_finality_proof, select_header_to_submit, + FinalityLoopState, FinalityProofs, FinalitySyncParams, RestartableFinalityProofsStream, + SourceClient, TargetClient, + }, + sync_loop_metrics::SyncLoopMetrics, + FinalityProof, FinalitySyncPipeline, SourceHeader, +}; + +use async_trait::async_trait; +use bp_header_chain::GrandpaConsensusLogReader; +use futures::{FutureExt, Stream, StreamExt}; +use parking_lot::Mutex; +use relay_utils::{ + relay_loop::Client as RelayClient, FailedClient, HeaderId, MaybeConnectionError, + TrackedTransactionStatus, TransactionTracker, +}; +use std::{ + collections::HashMap, + pin::Pin, + sync::Arc, + time::{Duration, Instant}, +}; + +type IsMandatory = bool; +type TestNumber = u64; +type TestHash = u64; + +#[derive(Clone, Debug)] +struct TestTransactionTracker(TrackedTransactionStatus>); + +impl Default for TestTransactionTracker { + fn default() -> TestTransactionTracker { + TestTransactionTracker(TrackedTransactionStatus::Finalized(Default::default())) + } +} + +#[async_trait] +impl TransactionTracker for TestTransactionTracker { + type HeaderId = HeaderId; + + async fn wait(self) -> TrackedTransactionStatus> { + self.0 + } +} + +#[derive(Debug, Clone)] +enum TestError { + NonConnection, +} + +impl MaybeConnectionError for TestError { + fn is_connection_error(&self) -> bool { + false + } +} + +#[derive(Debug, Clone)] +struct TestFinalitySyncPipeline; + +impl FinalitySyncPipeline for TestFinalitySyncPipeline { + const SOURCE_NAME: &'static str = "TestSource"; + const TARGET_NAME: &'static str = "TestTarget"; + + type Hash = TestHash; + type Number = TestNumber; + type ConsensusLogReader = GrandpaConsensusLogReader; + type Header = TestSourceHeader; + type FinalityProof = TestFinalityProof; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct TestSourceHeader(IsMandatory, TestNumber, TestHash); + +impl SourceHeader> + for TestSourceHeader +{ + fn hash(&self) -> TestHash { + self.2 + } + + fn number(&self) -> TestNumber { + self.1 + } + + fn is_mandatory(&self) -> bool { + self.0 + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct TestFinalityProof(TestNumber); + +impl FinalityProof for TestFinalityProof { + fn target_header_number(&self) -> TestNumber { + self.0 + } +} + +#[derive(Debug, Clone, Default)] +struct ClientsData { + source_best_block_number: TestNumber, + source_headers: HashMap)>, + source_proofs: Vec, + + target_best_block_id: HeaderId, + target_headers: Vec<(TestSourceHeader, TestFinalityProof)>, + target_transaction_tracker: TestTransactionTracker, +} + +#[derive(Clone)] +struct TestSourceClient { + on_method_call: Arc, + data: Arc>, +} + +#[async_trait] +impl RelayClient for TestSourceClient { + type Error = TestError; + + async fn reconnect(&mut self) -> Result<(), TestError> { + unreachable!() + } +} + +#[async_trait] +impl SourceClient for TestSourceClient { + type FinalityProofsStream = Pin + 'static + Send>>; + + async fn best_finalized_block_number(&self) -> Result { + let mut data = self.data.lock(); + (self.on_method_call)(&mut data); + Ok(data.source_best_block_number) + } + + async fn header_and_finality_proof( + &self, + number: TestNumber, + ) -> Result<(TestSourceHeader, Option), TestError> { + let mut data = self.data.lock(); + (self.on_method_call)(&mut data); + data.source_headers.get(&number).cloned().ok_or(TestError::NonConnection) + } + + async fn finality_proofs(&self) -> Result { + let mut data = self.data.lock(); + (self.on_method_call)(&mut data); + Ok(futures::stream::iter(data.source_proofs.clone()).boxed()) + } +} + +#[derive(Clone)] +struct TestTargetClient { + on_method_call: Arc, + data: Arc>, +} + +#[async_trait] +impl RelayClient for TestTargetClient { + type Error = TestError; + + async fn reconnect(&mut self) -> Result<(), TestError> { + unreachable!() + } +} + +#[async_trait] +impl TargetClient for TestTargetClient { + type TransactionTracker = TestTransactionTracker; + + async fn best_finalized_source_block_id( + &self, + ) -> Result, TestError> { + let mut data = self.data.lock(); + (self.on_method_call)(&mut data); + Ok(data.target_best_block_id) + } + + async fn submit_finality_proof( + &self, + header: TestSourceHeader, + proof: TestFinalityProof, + ) -> Result { + let mut data = self.data.lock(); + (self.on_method_call)(&mut data); + data.target_best_block_id = HeaderId(header.number(), header.hash()); + data.target_headers.push((header, proof)); + (self.on_method_call)(&mut data); + Ok(data.target_transaction_tracker.clone()) + } +} + +fn prepare_test_clients( + exit_sender: futures::channel::mpsc::UnboundedSender<()>, + state_function: impl Fn(&mut ClientsData) -> bool + Send + Sync + 'static, + source_headers: HashMap)>, +) -> (TestSourceClient, TestTargetClient) { + let internal_state_function: Arc = + Arc::new(move |data| { + if state_function(data) { + exit_sender.unbounded_send(()).unwrap(); + } + }); + let clients_data = Arc::new(Mutex::new(ClientsData { + source_best_block_number: 10, + source_headers, + source_proofs: vec![TestFinalityProof(12), TestFinalityProof(14)], + + target_best_block_id: HeaderId(5, 5), + target_headers: vec![], + target_transaction_tracker: TestTransactionTracker(TrackedTransactionStatus::Finalized( + Default::default(), + )), + })); + ( + TestSourceClient { + on_method_call: internal_state_function.clone(), + data: clients_data.clone(), + }, + TestTargetClient { on_method_call: internal_state_function, data: clients_data }, + ) +} + +fn test_sync_params() -> FinalitySyncParams { + FinalitySyncParams { + tick: Duration::from_secs(0), + recent_finality_proofs_limit: 1024, + stall_timeout: Duration::from_secs(1), + only_mandatory_headers: false, + } +} + +fn run_sync_loop( + state_function: impl Fn(&mut ClientsData) -> bool + Send + Sync + 'static, +) -> (ClientsData, Result<(), FailedClient>) { + let (exit_sender, exit_receiver) = futures::channel::mpsc::unbounded(); + let (source_client, target_client) = prepare_test_clients( + exit_sender, + state_function, + vec![ + (5, (TestSourceHeader(false, 5, 5), None)), + (6, (TestSourceHeader(false, 6, 6), None)), + (7, (TestSourceHeader(false, 7, 7), Some(TestFinalityProof(7)))), + (8, (TestSourceHeader(true, 8, 8), Some(TestFinalityProof(8)))), + (9, (TestSourceHeader(false, 9, 9), Some(TestFinalityProof(9)))), + (10, (TestSourceHeader(false, 10, 10), None)), + ] + .into_iter() + .collect(), + ); + let sync_params = test_sync_params(); + + let clients_data = source_client.data.clone(); + let result = async_std::task::block_on(run_until_connection_lost( + source_client, + target_client, + sync_params, + None, + exit_receiver.into_future().map(|(_, _)| ()), + )); + + let clients_data = clients_data.lock().clone(); + (clients_data, result) +} + +#[test] +fn finality_sync_loop_works() { + let (client_data, result) = run_sync_loop(|data| { + // header#7 has persistent finality proof, but it isn't mandatory => it isn't submitted, + // because header#8 has persistent finality proof && it is mandatory => it is submitted + // header#9 has persistent finality proof, but it isn't mandatory => it is submitted, + // because there are no more persistent finality proofs + // + // once this ^^^ is done, we generate more blocks && read proof for blocks 12 and 14 from + // the stream + if data.target_best_block_id.0 == 9 { + data.source_best_block_number = 14; + data.source_headers.insert(11, (TestSourceHeader(false, 11, 11), None)); + data.source_headers + .insert(12, (TestSourceHeader(false, 12, 12), Some(TestFinalityProof(12)))); + data.source_headers.insert(13, (TestSourceHeader(false, 13, 13), None)); + data.source_headers + .insert(14, (TestSourceHeader(false, 14, 14), Some(TestFinalityProof(14)))); + } + // once this ^^^ is done, we generate more blocks && read persistent proof for block 16 + if data.target_best_block_id.0 == 14 { + data.source_best_block_number = 17; + data.source_headers.insert(15, (TestSourceHeader(false, 15, 15), None)); + data.source_headers + .insert(16, (TestSourceHeader(false, 16, 16), Some(TestFinalityProof(16)))); + data.source_headers.insert(17, (TestSourceHeader(false, 17, 17), None)); + } + + data.target_best_block_id.0 == 16 + }); + + assert_eq!(result, Ok(())); + assert_eq!( + client_data.target_headers, + vec![ + // before adding 11..14: finality proof for mandatory header#8 + (TestSourceHeader(true, 8, 8), TestFinalityProof(8)), + // before adding 11..14: persistent finality proof for non-mandatory header#9 + (TestSourceHeader(false, 9, 9), TestFinalityProof(9)), + // after adding 11..14: ephemeral finality proof for non-mandatory header#14 + (TestSourceHeader(false, 14, 14), TestFinalityProof(14)), + // after adding 15..17: persistent finality proof for non-mandatory header#16 + (TestSourceHeader(false, 16, 16), TestFinalityProof(16)), + ], + ); +} + +fn run_only_mandatory_headers_mode_test( + only_mandatory_headers: bool, + has_mandatory_headers: bool, +) -> Option<(TestSourceHeader, TestFinalityProof)> { + let (exit_sender, _) = futures::channel::mpsc::unbounded(); + let (source_client, target_client) = prepare_test_clients( + exit_sender, + |_| false, + vec![ + (6, (TestSourceHeader(false, 6, 6), Some(TestFinalityProof(6)))), + (7, (TestSourceHeader(false, 7, 7), Some(TestFinalityProof(7)))), + (8, (TestSourceHeader(has_mandatory_headers, 8, 8), Some(TestFinalityProof(8)))), + (9, (TestSourceHeader(false, 9, 9), Some(TestFinalityProof(9)))), + (10, (TestSourceHeader(false, 10, 10), Some(TestFinalityProof(10)))), + ] + .into_iter() + .collect(), + ); + async_std::task::block_on(select_header_to_submit( + &source_client, + &target_client, + &mut RestartableFinalityProofsStream::from(futures::stream::empty().boxed()), + &mut vec![], + 10, + 5, + &FinalitySyncParams { + tick: Duration::from_secs(0), + recent_finality_proofs_limit: 0, + stall_timeout: Duration::from_secs(0), + only_mandatory_headers, + }, + )) + .unwrap() +} + +#[test] +fn select_header_to_submit_skips_non_mandatory_headers_when_only_mandatory_headers_are_required() { + assert_eq!(run_only_mandatory_headers_mode_test(true, false), None); + assert_eq!( + run_only_mandatory_headers_mode_test(false, false), + Some((TestSourceHeader(false, 10, 10), TestFinalityProof(10))), + ); +} + +#[test] +fn select_header_to_submit_selects_mandatory_headers_when_only_mandatory_headers_are_required() { + assert_eq!( + run_only_mandatory_headers_mode_test(true, true), + Some((TestSourceHeader(true, 8, 8), TestFinalityProof(8))), + ); + assert_eq!( + run_only_mandatory_headers_mode_test(false, true), + Some((TestSourceHeader(true, 8, 8), TestFinalityProof(8))), + ); +} + +#[test] +fn select_better_recent_finality_proof_works() { + // if there are no unjustified headers, nothing is changed + assert_eq!( + select_better_recent_finality_proof::( + &[(5, TestFinalityProof(5))], + &mut vec![], + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ); + + // if there are no recent finality proofs, nothing is changed + assert_eq!( + select_better_recent_finality_proof::( + &[], + &mut vec![TestSourceHeader(false, 5, 5)], + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ); + + // if there's no intersection between recent finality proofs and unjustified headers, nothing is + // changed + let mut unjustified_headers = + vec![TestSourceHeader(false, 9, 9), TestSourceHeader(false, 10, 10)]; + assert_eq!( + select_better_recent_finality_proof::( + &[(1, TestFinalityProof(1)), (4, TestFinalityProof(4))], + &mut unjustified_headers, + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ); + + // if there's intersection between recent finality proofs and unjustified headers, but there are + // no proofs in this intersection, nothing is changed + let mut unjustified_headers = vec![ + TestSourceHeader(false, 8, 8), + TestSourceHeader(false, 9, 9), + TestSourceHeader(false, 10, 10), + ]; + assert_eq!( + select_better_recent_finality_proof::( + &[(7, TestFinalityProof(7)), (11, TestFinalityProof(11))], + &mut unjustified_headers, + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ); + assert_eq!( + unjustified_headers, + vec![ + TestSourceHeader(false, 8, 8), + TestSourceHeader(false, 9, 9), + TestSourceHeader(false, 10, 10) + ] + ); + + // if there's intersection between recent finality proofs and unjustified headers and there's + // a proof in this intersection: + // - this better (last from intersection) proof is selected; + // - 'obsolete' unjustified headers are pruned. + let mut unjustified_headers = vec![ + TestSourceHeader(false, 8, 8), + TestSourceHeader(false, 9, 9), + TestSourceHeader(false, 10, 10), + ]; + assert_eq!( + select_better_recent_finality_proof::( + &[(7, TestFinalityProof(7)), (9, TestFinalityProof(9))], + &mut unjustified_headers, + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), + ), + Some((TestSourceHeader(false, 9, 9), TestFinalityProof(9))), + ); +} + +#[test] +fn read_finality_proofs_from_stream_works() { + // when stream is currently empty, nothing is changed + let mut recent_finality_proofs = vec![(1, TestFinalityProof(1))]; + let mut stream = futures::stream::pending().into(); + read_finality_proofs_from_stream::( + &mut stream, + &mut recent_finality_proofs, + ); + assert_eq!(recent_finality_proofs, vec![(1, TestFinalityProof(1))]); + assert!(!stream.needs_restart); + + // when stream has entry with target, it is added to the recent proofs container + let mut stream = futures::stream::iter(vec![TestFinalityProof(4)]) + .chain(futures::stream::pending()) + .into(); + read_finality_proofs_from_stream::( + &mut stream, + &mut recent_finality_proofs, + ); + assert_eq!(recent_finality_proofs, vec![(1, TestFinalityProof(1)), (4, TestFinalityProof(4))]); + assert!(!stream.needs_restart); + + // when stream has ended, we'll need to restart it + let mut stream = futures::stream::empty().into(); + read_finality_proofs_from_stream::( + &mut stream, + &mut recent_finality_proofs, + ); + assert_eq!(recent_finality_proofs, vec![(1, TestFinalityProof(1)), (4, TestFinalityProof(4))]); + assert!(stream.needs_restart); +} + +#[test] +fn prune_recent_finality_proofs_works() { + let original_recent_finality_proofs: FinalityProofs = vec![ + (10, TestFinalityProof(10)), + (13, TestFinalityProof(13)), + (15, TestFinalityProof(15)), + (17, TestFinalityProof(17)), + (19, TestFinalityProof(19)), + ] + .into_iter() + .collect(); + + // when there's proof for justified header in the vec + let mut recent_finality_proofs = original_recent_finality_proofs.clone(); + prune_recent_finality_proofs::(10, &mut recent_finality_proofs, 1024); + assert_eq!(&original_recent_finality_proofs[1..], recent_finality_proofs,); + + // when there are no proof for justified header in the vec + let mut recent_finality_proofs = original_recent_finality_proofs.clone(); + prune_recent_finality_proofs::(11, &mut recent_finality_proofs, 1024); + assert_eq!(&original_recent_finality_proofs[1..], recent_finality_proofs,); + + // when there are too many entries after initial prune && they also need to be pruned + let mut recent_finality_proofs = original_recent_finality_proofs.clone(); + prune_recent_finality_proofs::(10, &mut recent_finality_proofs, 2); + assert_eq!(&original_recent_finality_proofs[3..], recent_finality_proofs,); + + // when last entry is pruned + let mut recent_finality_proofs = original_recent_finality_proofs.clone(); + prune_recent_finality_proofs::(19, &mut recent_finality_proofs, 2); + assert_eq!(&original_recent_finality_proofs[5..], recent_finality_proofs,); + + // when post-last entry is pruned + let mut recent_finality_proofs = original_recent_finality_proofs.clone(); + prune_recent_finality_proofs::(20, &mut recent_finality_proofs, 2); + assert_eq!(&original_recent_finality_proofs[5..], recent_finality_proofs,); +} + +#[test] +fn different_forks_at_source_and_at_target_are_detected() { + let (exit_sender, _exit_receiver) = futures::channel::mpsc::unbounded(); + let (source_client, target_client) = prepare_test_clients( + exit_sender, + |_| false, + vec![ + (5, (TestSourceHeader(false, 5, 42), None)), + (6, (TestSourceHeader(false, 6, 6), None)), + (7, (TestSourceHeader(false, 7, 7), None)), + (8, (TestSourceHeader(false, 8, 8), None)), + (9, (TestSourceHeader(false, 9, 9), None)), + (10, (TestSourceHeader(false, 10, 10), None)), + ] + .into_iter() + .collect(), + ); + + let mut progress = (Instant::now(), None); + let mut finality_proofs_stream = futures::stream::iter(vec![]).boxed().into(); + let mut recent_finality_proofs = Vec::new(); + let metrics_sync = SyncLoopMetrics::new(None, "source", "target").unwrap(); + async_std::task::block_on(run_loop_iteration::( + &source_client, + &target_client, + FinalityLoopState { + progress: &mut progress, + finality_proofs_stream: &mut finality_proofs_stream, + recent_finality_proofs: &mut recent_finality_proofs, + submitted_header_number: None, + }, + &test_sync_params(), + &Some(metrics_sync.clone()), + )) + .unwrap(); + + assert!(!metrics_sync.is_using_same_fork()); +} + +#[test] +fn stalls_when_transaction_tracker_returns_error() { + let (_, result) = run_sync_loop(|data| { + data.target_transaction_tracker = TestTransactionTracker(TrackedTransactionStatus::Lost); + data.target_best_block_id = HeaderId(5, 5); + data.target_best_block_id.0 == 16 + }); + + assert_eq!(result, Err(FailedClient::Both)); +} + +#[test] +fn stalls_when_transaction_tracker_returns_finalized_but_transaction_fails() { + let (_, result) = run_sync_loop(|data| { + data.target_best_block_id = HeaderId(5, 5); + data.target_best_block_id.0 == 16 + }); + + assert_eq!(result, Err(FailedClient::Both)); +} diff --git a/relays/finality/src/lib.rs b/relays/finality/src/lib.rs new file mode 100644 index 00000000000..dca47c6a572 --- /dev/null +++ b/relays/finality/src/lib.rs @@ -0,0 +1,61 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! This crate has single entrypoint to run synchronization loop that is built around finality +//! proofs, as opposed to headers synchronization loop, which is built around headers. The headers +//! are still submitted to the target node, but are treated as auxiliary data as we are not trying +//! to submit all source headers to the target node. + +pub use crate::{ + finality_loop::{metrics_prefix, run, FinalitySyncParams, SourceClient, TargetClient}, + sync_loop_metrics::SyncLoopMetrics, +}; + +use bp_header_chain::{ConsensusLogReader, FinalityProof}; +use std::fmt::Debug; + +mod finality_loop; +mod finality_loop_tests; +mod sync_loop_metrics; + +/// Finality proofs synchronization pipeline. +pub trait FinalitySyncPipeline: 'static + Clone + Debug + Send + Sync { + /// Name of the finality proofs source. + const SOURCE_NAME: &'static str; + /// Name of the finality proofs target. + const TARGET_NAME: &'static str; + + /// Headers we're syncing are identified by this hash. + type Hash: Eq + Clone + Copy + Send + Sync + Debug; + /// Headers we're syncing are identified by this number. + type Number: relay_utils::BlockNumberBase; + /// A reader that can extract the consensus log from the header digest and interpret it. + type ConsensusLogReader: ConsensusLogReader; + /// Type of header that we're syncing. + type Header: SourceHeader; + /// Finality proof type. + type FinalityProof: FinalityProof; +} + +/// Header that we're receiving from source node. +pub trait SourceHeader: Clone + Debug + PartialEq + Send + Sync { + /// Returns hash of header. + fn hash(&self) -> Hash; + /// Returns number of header. + fn number(&self) -> Number; + /// Returns true if this header needs to be submitted to target node. + fn is_mandatory(&self) -> bool; +} diff --git a/relays/finality/src/sync_loop_metrics.rs b/relays/finality/src/sync_loop_metrics.rs new file mode 100644 index 00000000000..4da1df811f6 --- /dev/null +++ b/relays/finality/src/sync_loop_metrics.rs @@ -0,0 +1,95 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Metrics for headers synchronization relay loop. + +use relay_utils::{ + metrics::{metric_name, register, IntGauge, Metric, PrometheusError, Registry}, + UniqueSaturatedInto, +}; + +/// Headers sync metrics. +#[derive(Clone)] +pub struct SyncLoopMetrics { + /// Best syncing header at the source. + best_source_block_number: IntGauge, + /// Best syncing header at the target. + best_target_block_number: IntGauge, + /// Flag that has `0` value when best source headers at the source node and at-target-chain + /// are matching and `1` otherwise. + using_different_forks: IntGauge, +} + +impl SyncLoopMetrics { + /// Create and register headers loop metrics. + pub fn new( + prefix: Option<&str>, + at_source_chain_label: &str, + at_target_chain_label: &str, + ) -> Result { + Ok(SyncLoopMetrics { + best_source_block_number: IntGauge::new( + metric_name(prefix, &format!("best_{at_source_chain_label}_block_number")), + format!("Best block number at the {at_source_chain_label}"), + )?, + best_target_block_number: IntGauge::new( + metric_name(prefix, &format!("best_{at_target_chain_label}_block_number")), + format!("Best block number at the {at_target_chain_label}"), + )?, + using_different_forks: IntGauge::new( + metric_name(prefix, &format!("is_{at_source_chain_label}_and_{at_target_chain_label}_using_different_forks")), + "Whether the best finalized source block at target node is different (value 1) from the \ + corresponding block at the source node", + )?, + }) + } + + /// Returns current value of the using-same-fork flag. + #[cfg(test)] + pub(crate) fn is_using_same_fork(&self) -> bool { + self.using_different_forks.get() == 0 + } + + /// Update best block number at source. + pub fn update_best_block_at_source>( + &self, + source_best_number: Number, + ) { + self.best_source_block_number.set(source_best_number.unique_saturated_into()); + } + + /// Update best block number at target. + pub fn update_best_block_at_target>( + &self, + target_best_number: Number, + ) { + self.best_target_block_number.set(target_best_number.unique_saturated_into()); + } + + /// Update using-same-fork flag. + pub fn update_using_same_fork(&self, using_same_fork: bool) { + self.using_different_forks.set((!using_same_fork).into()) + } +} + +impl Metric for SyncLoopMetrics { + fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { + register(self.best_source_block_number.clone(), registry)?; + register(self.best_target_block_number.clone(), registry)?; + register(self.using_different_forks.clone(), registry)?; + Ok(()) + } +} diff --git a/relays/lib-substrate-relay/Cargo.toml b/relays/lib-substrate-relay/Cargo.toml new file mode 100644 index 00000000000..d07aa936b57 --- /dev/null +++ b/relays/lib-substrate-relay/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "substrate-relay-helper" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +anyhow = "1.0" +thiserror = "1.0.40" +async-std = "1.9.0" +async-trait = "0.1" +codec = { package = "parity-scale-codec", version = "3.1.5" } +futures = "0.3.28" +hex = "0.4" +num-traits = "0.2" +log = "0.4.17" + +# Bridge dependencies + +bp-header-chain = { path = "../../primitives/header-chain" } +bp-parachains = { path = "../../primitives/parachains" } +bp-polkadot-core = { path = "../../primitives/polkadot-core" } +bp-relayers = { path = "../../primitives/relayers" } +bridge-runtime-common = { path = "../../bin/runtime-common" } + +finality-grandpa = { version = "0.16.2" } +finality-relay = { path = "../finality" } +parachains-relay = { path = "../parachains" } +relay-utils = { path = "../utils" } +messages-relay = { path = "../messages" } +relay-substrate-client = { path = "../client-substrate" } + +pallet-bridge-grandpa = { path = "../../modules/grandpa" } +pallet-bridge-messages = { path = "../../modules/messages" } +pallet-bridge-parachains = { path = "../../modules/parachains" } + +bp-runtime = { path = "../../primitives/runtime" } +bp-messages = { path = "../../primitives/messages" } + +# Substrate Dependencies + +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } + +[dev-dependencies] +bp-rialto = { path = "../../primitives/chain-rialto" } +bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" } +bp-rococo = { path = "../../primitives/chain-rococo" } +bp-wococo = { path = "../../primitives/chain-wococo" } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } +relay-rialto-client = { path = "../client-rialto" } +relay-rococo-client = { path = "../client-rococo" } +relay-wococo-client = { path = "../client-wococo" } +rialto-runtime = { path = "../../bin/rialto/runtime" } diff --git a/relays/lib-substrate-relay/src/error.rs b/relays/lib-substrate-relay/src/error.rs new file mode 100644 index 00000000000..2ebd9130f39 --- /dev/null +++ b/relays/lib-substrate-relay/src/error.rs @@ -0,0 +1,63 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Relay errors. + +use relay_substrate_client as client; +use sp_consensus_grandpa::AuthorityList; +use sp_runtime::traits::MaybeDisplay; +use std::fmt::Debug; +use thiserror::Error; + +/// Relay errors. +#[derive(Error, Debug)] +pub enum Error { + /// Failed to submit signed extrinsic from to the target chain. + #[error("Failed to submit {0} transaction: {1:?}")] + SubmitTransaction(&'static str, client::Error), + /// Failed subscribe to justification stream of the source chain. + #[error("Failed to subscribe to {0} justifications: {1:?}")] + Subscribe(&'static str, client::Error), + /// Failed subscribe to read justification from the source chain (client error). + #[error("Failed to read {0} justification from the stream: {1}")] + ReadJustification(&'static str, client::Error), + /// Failed subscribe to read justification from the source chain (stream ended). + #[error("Failed to read {0} justification from the stream: stream has ended unexpectedly")] + ReadJustificationStreamEnded(&'static str), + /// Failed subscribe to decode justification from the source chain. + #[error("Failed to decode {0} justification: {1:?}")] + DecodeJustification(&'static str, codec::Error), + /// GRANDPA authorities read from the source chain are invalid. + #[error("Read invalid {0} authorities set: {1:?}")] + ReadInvalidAuthorities(&'static str, AuthorityList), + /// Failed to guess initial GRANDPA authorities at the given header of the source chain. + #[error("Failed to guess initial {0} GRANDPA authorities set id: checked all possible ids in range [0; {1}]")] + GuessInitialAuthorities(&'static str, HeaderNumber), + /// Failed to retrieve GRANDPA authorities at the given header from the source chain. + #[error("Failed to retrive {0} GRANDPA authorities set at header {1}: {2:?}")] + RetrieveAuthorities(&'static str, Hash, client::Error), + /// Failed to decode GRANDPA authorities at the given header of the source chain. + #[error("Failed to decode {0} GRANDPA authorities set at header {1}: {2:?}")] + DecodeAuthorities(&'static str, Hash, codec::Error), + /// Failed to retrieve header by the hash from the source chain. + #[error("Failed to retrieve {0} header with hash {1}: {2:?}")] + RetrieveHeader(&'static str, Hash, client::Error), + /// Failed to submit signed extrinsic from to the target chain. + #[error( + "Failed to retrieve `is_initialized` flag of the with-{0} finality pallet at {1}: {2:?}" + )] + IsInitializedRetrieve(&'static str, &'static str, client::Error), +} diff --git a/relays/lib-substrate-relay/src/finality/engine.rs b/relays/lib-substrate-relay/src/finality/engine.rs new file mode 100644 index 00000000000..f2ddd32db86 --- /dev/null +++ b/relays/lib-substrate-relay/src/finality/engine.rs @@ -0,0 +1,308 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Support of different finality engines, available in Substrate. + +use crate::error::Error; +use async_trait::async_trait; +use bp_header_chain::{ + justification::{verify_and_optimize_justification, GrandpaJustification}, + ConsensusLogReader, FinalityProof, GrandpaConsensusLogReader, +}; +use bp_runtime::{BasicOperatingMode, HeaderIdProvider, OperatingMode}; +use codec::{Decode, Encode}; +use finality_grandpa::voter_set::VoterSet; +use num_traits::{One, Zero}; +use relay_substrate_client::{ + BlockNumberOf, Chain, ChainWithGrandpa, Client, Error as SubstrateError, HashOf, HeaderOf, + Subscription, SubstrateFinalityClient, SubstrateGrandpaFinalityClient, +}; +use sp_consensus_grandpa::{AuthorityList as GrandpaAuthoritiesSet, GRANDPA_ENGINE_ID}; +use sp_core::{storage::StorageKey, Bytes}; +use sp_runtime::{traits::Header, ConsensusEngineId}; +use std::marker::PhantomData; + +/// Finality engine, used by the Substrate chain. +#[async_trait] +pub trait Engine: Send { + /// Unique consensus engine identifier. + const ID: ConsensusEngineId; + /// A reader that can extract the consensus log from the header digest and interpret it. + type ConsensusLogReader: ConsensusLogReader; + /// Type of Finality RPC client used by this engine. + type FinalityClient: SubstrateFinalityClient; + /// Type of finality proofs, used by consensus engine. + type FinalityProof: FinalityProof> + Decode + Encode; + /// Type of bridge pallet initialization data. + type InitializationData: std::fmt::Debug + Send + Sync + 'static; + /// Type of bridge pallet operating mode. + type OperatingMode: OperatingMode + 'static; + + /// Returns storage at the bridged (target) chain that corresponds to some value that is + /// missing from the storage until bridge pallet is initialized. + /// + /// Note that we don't care about type of the value - just if it present or not. + fn is_initialized_key() -> StorageKey; + + /// Returns `Ok(true)` if finality pallet at the bridged chain has already been initialized. + async fn is_initialized( + target_client: &Client, + ) -> Result { + Ok(target_client + .raw_storage_value(Self::is_initialized_key(), None) + .await? + .is_some()) + } + + /// Returns storage key at the bridged (target) chain that corresponds to the variable + /// that holds the operating mode of the pallet. + fn pallet_operating_mode_key() -> StorageKey; + + /// Returns `Ok(true)` if finality pallet at the bridged chain is halted. + async fn is_halted( + target_client: &Client, + ) -> Result { + Ok(target_client + .storage_value::(Self::pallet_operating_mode_key(), None) + .await? + .map(|operating_mode| operating_mode.is_halted()) + .unwrap_or(false)) + } + + /// A method to subscribe to encoded finality proofs, given source client. + async fn finality_proofs(client: &Client) -> Result, SubstrateError> { + client.subscribe_finality_justifications::().await + } + + /// Optimize finality proof before sending it to the target node. + async fn optimize_proof( + target_client: &Client, + header: &C::Header, + proof: Self::FinalityProof, + ) -> Result; + + /// Prepare initialization data for the finality bridge pallet. + async fn prepare_initialization_data( + client: Client, + ) -> Result, BlockNumberOf>>; +} + +/// GRANDPA finality engine. +pub struct Grandpa(PhantomData); + +impl Grandpa { + /// Read header by hash from the source client. + async fn source_header( + source_client: &Client, + header_hash: C::Hash, + ) -> Result, BlockNumberOf>> { + source_client + .header_by_hash(header_hash) + .await + .map_err(|err| Error::RetrieveHeader(C::NAME, header_hash, err)) + } + + /// Read GRANDPA authorities set at given header. + async fn source_authorities_set( + source_client: &Client, + header_hash: C::Hash, + ) -> Result, BlockNumberOf>> { + let raw_authorities_set = source_client + .grandpa_authorities_set(header_hash) + .await + .map_err(|err| Error::RetrieveAuthorities(C::NAME, header_hash, err))?; + GrandpaAuthoritiesSet::decode(&mut &raw_authorities_set[..]) + .map_err(|err| Error::DecodeAuthorities(C::NAME, header_hash, err)) + } +} + +#[async_trait] +impl Engine for Grandpa { + const ID: ConsensusEngineId = GRANDPA_ENGINE_ID; + type ConsensusLogReader = GrandpaConsensusLogReader<::Number>; + type FinalityClient = SubstrateGrandpaFinalityClient; + type FinalityProof = GrandpaJustification>; + type InitializationData = bp_header_chain::InitializationData; + type OperatingMode = BasicOperatingMode; + + fn is_initialized_key() -> StorageKey { + bp_header_chain::storage_keys::best_finalized_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME) + } + + fn pallet_operating_mode_key() -> StorageKey { + bp_header_chain::storage_keys::pallet_operating_mode_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME) + } + + async fn optimize_proof( + target_client: &Client, + header: &C::Header, + proof: Self::FinalityProof, + ) -> Result { + let current_authority_set_key = bp_header_chain::storage_keys::current_authority_set_key( + C::WITH_CHAIN_GRANDPA_PALLET_NAME, + ); + let (authority_set, authority_set_id): ( + sp_consensus_grandpa::AuthorityList, + sp_consensus_grandpa::SetId, + ) = target_client + .storage_value(current_authority_set_key, None) + .await? + .map(Ok) + .unwrap_or(Err(SubstrateError::Custom(format!( + "{} `CurrentAuthoritySet` is missing from the {} storage", + C::NAME, + TargetChain::NAME, + ))))?; + let authority_set = + finality_grandpa::voter_set::VoterSet::new(authority_set).expect("TODO"); + // we're risking with race here - we have decided to submit justification some time ago and + // actual authorities set (which we have read now) may have changed, so this + // `optimize_justification` may fail. But if target chain is configured properly, it'll fail + // anyway, after we submit transaction and failing earlier is better. So - it is fine + verify_and_optimize_justification( + (header.hash(), *header.number()), + authority_set_id, + &authority_set, + proof, + ) + .map_err(|e| { + SubstrateError::Custom(format!( + "Failed to optimize {} GRANDPA jutification for header {:?}: {:?}", + C::NAME, + header.id(), + e, + )) + }) + } + + /// Prepare initialization data for the GRANDPA verifier pallet. + async fn prepare_initialization_data( + source_client: Client, + ) -> Result, BlockNumberOf>> { + // In ideal world we just need to get best finalized header and then to read GRANDPA + // authorities set (`pallet_grandpa::CurrentSetId` + `GrandpaApi::grandpa_authorities()`) at + // this header. + // + // But now there are problems with this approach - `CurrentSetId` may return invalid value. + // So here we're waiting for the next justification, read the authorities set and then try + // to figure out the set id with bruteforce. + let justifications = Self::finality_proofs(&source_client) + .await + .map_err(|err| Error::Subscribe(C::NAME, err))?; + // Read next justification - the header that it finalizes will be used as initial header. + let justification = justifications + .next() + .await + .map_err(|e| Error::ReadJustification(C::NAME, e)) + .and_then(|justification| { + justification.ok_or(Error::ReadJustificationStreamEnded(C::NAME)) + })?; + + // Read initial header. + let justification: GrandpaJustification = + Decode::decode(&mut &justification.0[..]) + .map_err(|err| Error::DecodeJustification(C::NAME, err))?; + + let (initial_header_hash, initial_header_number) = + (justification.commit.target_hash, justification.commit.target_number); + + let initial_header = Self::source_header(&source_client, initial_header_hash).await?; + log::trace!(target: "bridge", "Selected {} initial header: {}/{}", + C::NAME, + initial_header_number, + initial_header_hash, + ); + + // Read GRANDPA authorities set at initial header. + let initial_authorities_set = + Self::source_authorities_set(&source_client, initial_header_hash).await?; + log::trace!(target: "bridge", "Selected {} initial authorities set: {:?}", + C::NAME, + initial_authorities_set, + ); + + // If initial header changes the GRANDPA authorities set, then we need previous authorities + // to verify justification. + let mut authorities_for_verification = initial_authorities_set.clone(); + let scheduled_change = + GrandpaConsensusLogReader::>::find_authorities_change( + initial_header.digest(), + ); + assert!( + scheduled_change.as_ref().map(|c| c.delay.is_zero()).unwrap_or(true), + "GRANDPA authorities change at {} scheduled to happen in {:?} blocks. We expect\ + regular change to have zero delay", + initial_header_hash, + scheduled_change.as_ref().map(|c| c.delay), + ); + let schedules_change = scheduled_change.is_some(); + if schedules_change { + authorities_for_verification = + Self::source_authorities_set(&source_client, *initial_header.parent_hash()).await?; + log::trace!( + target: "bridge", + "Selected {} header is scheduling GRANDPA authorities set changes. Using previous set: {:?}", + C::NAME, + authorities_for_verification, + ); + } + + // Now let's try to guess authorities set id by verifying justification. + let mut initial_authorities_set_id = 0; + let mut min_possible_block_number = C::BlockNumber::zero(); + let authorities_for_verification = VoterSet::new(authorities_for_verification.clone()) + .ok_or(Error::ReadInvalidAuthorities(C::NAME, authorities_for_verification))?; + loop { + log::trace!( + target: "bridge", "Trying {} GRANDPA authorities set id: {}", + C::NAME, + initial_authorities_set_id, + ); + + let is_valid_set_id = verify_and_optimize_justification( + (initial_header_hash, initial_header_number), + initial_authorities_set_id, + &authorities_for_verification, + justification.clone(), + ) + .is_ok(); + + if is_valid_set_id { + break + } + + initial_authorities_set_id += 1; + min_possible_block_number += One::one(); + if min_possible_block_number > initial_header_number { + // there can't be more authorities set changes than headers => if we have reached + // `initial_block_number` and still have not found correct value of + // `initial_authorities_set_id`, then something else is broken => fail + return Err(Error::GuessInitialAuthorities(C::NAME, initial_header_number)) + } + } + + Ok(bp_header_chain::InitializationData { + header: Box::new(initial_header), + authority_list: initial_authorities_set, + set_id: if schedules_change { + initial_authorities_set_id + 1 + } else { + initial_authorities_set_id + }, + operating_mode: BasicOperatingMode::Normal, + }) + } +} diff --git a/relays/lib-substrate-relay/src/finality/guards.rs b/relays/lib-substrate-relay/src/finality/guards.rs new file mode 100644 index 00000000000..188a03733a3 --- /dev/null +++ b/relays/lib-substrate-relay/src/finality/guards.rs @@ -0,0 +1,48 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Tools for starting guards of finality relays. + +use crate::TransactionParams; + +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, ChainWithBalances, ChainWithTransactions, +}; +use sp_core::Pair; + +/// Start finality relay guards. +pub async fn start( + target_client: &relay_substrate_client::Client, + transaction_params: &TransactionParams>, + enable_version_guard: bool, + maximal_balance_decrease_per_day: C::Balance, +) -> relay_substrate_client::Result<()> +where + AccountIdOf: From< as Pair>::Public>, +{ + if enable_version_guard { + relay_substrate_client::guard::abort_on_spec_version_change( + target_client.clone(), + target_client.simple_runtime_version().await?.spec_version, + ); + } + relay_substrate_client::guard::abort_when_account_balance_decreased( + target_client.clone(), + transaction_params.signer.public().into(), + maximal_balance_decrease_per_day, + ); + Ok(()) +} diff --git a/relays/lib-substrate-relay/src/finality/initialize.rs b/relays/lib-substrate-relay/src/finality/initialize.rs new file mode 100644 index 00000000000..87052cf7aa4 --- /dev/null +++ b/relays/lib-substrate-relay/src/finality/initialize.rs @@ -0,0 +1,163 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Initialize Substrate -> Substrate finality bridge. +//! +//! Initialization is a transaction that calls `initialize()` function of the +//! finality pallet (GRANDPA/BEEFY/...). This transaction brings initial header +//! and authorities set from source to target chain. The finality sync starts +//! with this header. + +use crate::{error::Error, finality::engine::Engine}; +use sp_core::Pair; + +use bp_runtime::HeaderIdOf; +use relay_substrate_client::{ + AccountKeyPairOf, Chain, ChainWithTransactions, Client, Error as SubstrateError, + UnsignedTransaction, +}; +use relay_utils::{TrackedTransactionStatus, TransactionTracker}; +use sp_runtime::traits::Header as HeaderT; + +/// Submit headers-bridge initialization transaction. +pub async fn initialize< + E: Engine, + SourceChain: Chain, + TargetChain: ChainWithTransactions, + F, +>( + source_client: Client, + target_client: Client, + target_signer: AccountKeyPairOf, + prepare_initialize_transaction: F, + dry_run: bool, +) where + F: FnOnce( + TargetChain::Index, + E::InitializationData, + ) -> Result, SubstrateError> + + Send + + 'static, + TargetChain::AccountId: From<::Public>, +{ + let result = do_initialize::( + source_client, + target_client, + target_signer, + prepare_initialize_transaction, + dry_run, + ) + .await; + + match result { + Ok(Some(tx_status)) => match tx_status { + TrackedTransactionStatus::Lost => { + log::error!( + target: "bridge", + "Failed to execute {}-headers bridge initialization transaction on {}: {:?}.", + SourceChain::NAME, + TargetChain::NAME, + tx_status + ) + }, + TrackedTransactionStatus::Finalized(_) => { + log::info!( + target: "bridge", + "Successfully executed {}-headers bridge initialization transaction on {}: {:?}.", + SourceChain::NAME, + TargetChain::NAME, + tx_status + ) + }, + }, + Ok(None) => (), + Err(err) => log::error!( + target: "bridge", + "Failed to submit {}-headers bridge initialization transaction to {}: {:?}", + SourceChain::NAME, + TargetChain::NAME, + err, + ), + } +} + +/// Craft and submit initialization transaction, returning any error that may occur. +async fn do_initialize< + E: Engine, + SourceChain: Chain, + TargetChain: ChainWithTransactions, + F, +>( + source_client: Client, + target_client: Client, + target_signer: AccountKeyPairOf, + prepare_initialize_transaction: F, + dry_run: bool, +) -> Result< + Option>>, + Error::Number>, +> +where + F: FnOnce( + TargetChain::Index, + E::InitializationData, + ) -> Result, SubstrateError> + + Send + + 'static, + TargetChain::AccountId: From<::Public>, +{ + let is_initialized = E::is_initialized(&target_client) + .await + .map_err(|e| Error::IsInitializedRetrieve(SourceChain::NAME, TargetChain::NAME, e))?; + if is_initialized { + log::info!( + target: "bridge", + "{}-headers bridge at {} is already initialized. Skipping", + SourceChain::NAME, + TargetChain::NAME, + ); + if !dry_run { + return Ok(None) + } + } + + let initialization_data = E::prepare_initialization_data(source_client).await?; + log::info!( + target: "bridge", + "Prepared initialization data for {}-headers bridge at {}: {:?}", + SourceChain::NAME, + TargetChain::NAME, + initialization_data, + ); + + let tx_status = target_client + .submit_and_watch_signed_extrinsic(&target_signer, move |_, transaction_nonce| { + let tx = prepare_initialize_transaction(transaction_nonce, initialization_data); + if dry_run { + Err(SubstrateError::Custom( + "Not submitting extrinsic in `dry-run` mode!".to_string(), + )) + } else { + tx + } + }) + .await + .map_err(|err| Error::SubmitTransaction(TargetChain::NAME, err))? + .wait() + .await; + + Ok(Some(tx_status)) +} diff --git a/relays/lib-substrate-relay/src/finality/mod.rs b/relays/lib-substrate-relay/src/finality/mod.rs new file mode 100644 index 00000000000..b0f0ee4e52c --- /dev/null +++ b/relays/lib-substrate-relay/src/finality/mod.rs @@ -0,0 +1,209 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types and functions intended to ease adding of new Substrate -> Substrate +//! finality proofs synchronization pipelines. + +use crate::{ + finality::{ + engine::Engine, + source::{SubstrateFinalityProof, SubstrateFinalitySource}, + target::SubstrateFinalityTarget, + }, + TransactionParams, +}; + +use async_trait::async_trait; +use bp_header_chain::justification::GrandpaJustification; +use finality_relay::FinalitySyncPipeline; +use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig}; +use relay_substrate_client::{ + transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, + ChainWithTransactions, Client, HashOf, HeaderOf, SyncHeader, +}; +use relay_utils::metrics::MetricsParams; +use sp_core::Pair; +use std::{fmt::Debug, marker::PhantomData}; + +pub mod engine; +pub mod guards; +pub mod initialize; +pub mod source; +pub mod target; + +/// Default limit of recent finality proofs. +/// +/// Finality delay of 4096 blocks is unlikely to happen in practice in +/// Substrate+GRANDPA based chains (good to know). +pub(crate) const RECENT_FINALITY_PROOFS_LIMIT: usize = 4096; + +/// Substrate -> Substrate finality proofs synchronization pipeline. +#[async_trait] +pub trait SubstrateFinalitySyncPipeline: 'static + Clone + Debug + Send + Sync { + /// Headers of this chain are submitted to the `TargetChain`. + type SourceChain: Chain; + /// Headers of the `SourceChain` are submitted to this chain. + type TargetChain: ChainWithTransactions; + + /// Finality engine. + type FinalityEngine: Engine; + /// How submit finality proof call is built? + type SubmitFinalityProofCallBuilder: SubmitFinalityProofCallBuilder; + + /// Add relay guards if required. + async fn start_relay_guards( + _target_client: &Client, + _transaction_params: &TransactionParams>, + _enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + Ok(()) + } +} + +/// Adapter that allows all `SubstrateFinalitySyncPipeline` to act as `FinalitySyncPipeline`. +#[derive(Clone, Debug)] +pub struct FinalitySyncPipelineAdapter { + _phantom: PhantomData

, +} + +impl FinalitySyncPipeline for FinalitySyncPipelineAdapter

{ + const SOURCE_NAME: &'static str = P::SourceChain::NAME; + const TARGET_NAME: &'static str = P::TargetChain::NAME; + + type Hash = HashOf; + type Number = BlockNumberOf; + type ConsensusLogReader = >::ConsensusLogReader; + type Header = SyncHeader>; + type FinalityProof = SubstrateFinalityProof

; +} + +/// Different ways of building `submit_finality_proof` calls. +pub trait SubmitFinalityProofCallBuilder { + /// Given source chain header and its finality proofs, build call of `submit_finality_proof` + /// function of bridge GRANDPA module at the target chain. + fn build_submit_finality_proof_call( + header: SyncHeader>, + proof: SubstrateFinalityProof

, + ) -> CallOf; +} + +/// Building `submit_finality_proof` call when you have direct access to the target +/// chain runtime. +pub struct DirectSubmitGrandpaFinalityProofCallBuilder { + _phantom: PhantomData<(P, R, I)>, +} + +impl SubmitFinalityProofCallBuilder

+ for DirectSubmitGrandpaFinalityProofCallBuilder +where + P: SubstrateFinalitySyncPipeline, + R: BridgeGrandpaConfig, + I: 'static, + R::BridgedChain: bp_runtime::Chain

>, + CallOf: From>, + P::FinalityEngine: + Engine>>, +{ + fn build_submit_finality_proof_call( + header: SyncHeader>, + proof: GrandpaJustification>, + ) -> CallOf { + BridgeGrandpaCall::::submit_finality_proof { + finality_target: Box::new(header.into_inner()), + justification: proof, + } + .into() + } +} + +/// Macro that generates `SubmitFinalityProofCallBuilder` implementation for the case when +/// you only have an access to the mocked version of target chain runtime. In this case you +/// should provide "name" of the call variant for the bridge GRANDPA calls and the "name" of +/// the variant for the `submit_finality_proof` call within that first option. +#[rustfmt::skip] +#[macro_export] +macro_rules! generate_submit_finality_proof_call_builder { + ($pipeline:ident, $mocked_builder:ident, $bridge_grandpa:path, $submit_finality_proof:path) => { + pub struct $mocked_builder; + + impl $crate::finality::SubmitFinalityProofCallBuilder<$pipeline> + for $mocked_builder + { + fn build_submit_finality_proof_call( + header: relay_substrate_client::SyncHeader< + relay_substrate_client::HeaderOf< + <$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::SourceChain + > + >, + proof: bp_header_chain::justification::GrandpaJustification< + relay_substrate_client::HeaderOf< + <$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::SourceChain + > + >, + ) -> relay_substrate_client::CallOf< + <$pipeline as $crate::finality::SubstrateFinalitySyncPipeline>::TargetChain + > { + bp_runtime::paste::item! { + $bridge_grandpa($submit_finality_proof { + finality_target: Box::new(header.into_inner()), + justification: proof + }) + } + } + } + }; +} + +/// Run Substrate-to-Substrate finality sync loop. +pub async fn run( + source_client: Client, + target_client: Client, + only_mandatory_headers: bool, + transaction_params: TransactionParams>, + metrics_params: MetricsParams, +) -> anyhow::Result<()> +where + AccountIdOf: From< as Pair>::Public>, +{ + log::info!( + target: "bridge", + "Starting {} -> {} finality proof relay", + P::SourceChain::NAME, + P::TargetChain::NAME, + ); + + finality_relay::run( + SubstrateFinalitySource::

::new(source_client, None), + SubstrateFinalityTarget::

::new(target_client, transaction_params.clone()), + finality_relay::FinalitySyncParams { + tick: std::cmp::max( + P::SourceChain::AVERAGE_BLOCK_INTERVAL, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + ), + recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT, + stall_timeout: transaction_stall_timeout( + transaction_params.mortality, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + relay_utils::STALL_TIMEOUT, + ), + only_mandatory_headers, + }, + metrics_params, + futures::future::pending(), + ) + .await + .map_err(|e| anyhow::format_err!("{}", e)) +} diff --git a/relays/lib-substrate-relay/src/finality/source.rs b/relays/lib-substrate-relay/src/finality/source.rs new file mode 100644 index 00000000000..c8f11d99461 --- /dev/null +++ b/relays/lib-substrate-relay/src/finality/source.rs @@ -0,0 +1,298 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Default generic implementation of finality source for basic Substrate client. + +use crate::finality::{engine::Engine, FinalitySyncPipelineAdapter, SubstrateFinalitySyncPipeline}; + +use async_std::sync::{Arc, Mutex}; +use async_trait::async_trait; +use bp_header_chain::FinalityProof; +use codec::Decode; +use finality_relay::SourceClient; +use futures::{ + select, + stream::{try_unfold, unfold, Stream, StreamExt, TryStreamExt}, +}; +use num_traits::One; +use relay_substrate_client::{ + BlockNumberOf, BlockWithJustification, Chain, Client, Error, HeaderOf, +}; +use relay_utils::{relay_loop::Client as RelayClient, UniqueSaturatedInto}; +use std::pin::Pin; + +/// Shared updatable reference to the maximal header number that we want to sync from the source. +pub type RequiredHeaderNumberRef = Arc::BlockNumber>>; + +/// Substrate finality proofs stream. +pub type SubstrateFinalityProofsStream

= + Pin> + Send>>; + +/// Substrate finality proof. Specific to the used `FinalityEngine`. +pub type SubstrateFinalityProof

= + <

::FinalityEngine as Engine< +

::SourceChain, + >>::FinalityProof; + +/// Substrate node as finality source. +pub struct SubstrateFinalitySource { + client: Client, + maximal_header_number: Option>, +} + +impl SubstrateFinalitySource

{ + /// Create new headers source using given client. + pub fn new( + client: Client, + maximal_header_number: Option>, + ) -> Self { + SubstrateFinalitySource { client, maximal_header_number } + } + + /// Returns reference to the underlying RPC client. + pub fn client(&self) -> &Client { + &self.client + } + + /// Returns best finalized block number. + pub async fn on_chain_best_finalized_block_number( + &self, + ) -> Result, Error> { + // we **CAN** continue to relay finality proofs if source node is out of sync, because + // target node may be missing proofs that are already available at the source + self.client.best_finalized_header_number().await + } + + /// Return header and its justification of the given block or its descendant that + /// has a GRANDPA justification. + /// + /// This method is optimized for cases when `block_number` is close to the best finalized + /// chain block. + pub async fn prove_block_finality( + &self, + block_number: BlockNumberOf, + ) -> Result< + (relay_substrate_client::SyncHeader>, SubstrateFinalityProof

), + Error, + > { + // first, subscribe to proofs + let next_persistent_proof = + self.persistent_proofs_stream(block_number + One::one()).await?.fuse(); + let next_ephemeral_proof = self.ephemeral_proofs_stream(block_number).await?.fuse(); + + // in perfect world we'll need to return justfication for the requested `block_number` + let (header, maybe_proof) = self.header_and_finality_proof(block_number).await?; + if let Some(proof) = maybe_proof { + return Ok((header, proof)) + } + + // otherwise we don't care which header to return, so let's select first + futures::pin_mut!(next_persistent_proof, next_ephemeral_proof); + loop { + select! { + maybe_header_and_proof = next_persistent_proof.next() => match maybe_header_and_proof { + Some(header_and_proof) => return header_and_proof, + None => continue, + }, + maybe_header_and_proof = next_ephemeral_proof.next() => match maybe_header_and_proof { + Some(header_and_proof) => return header_and_proof, + None => continue, + }, + complete => return Err(Error::FinalityProofNotFound(block_number.unique_saturated_into())) + } + } + } + + /// Returns stream of headers and their persistent proofs, starting from given block. + async fn persistent_proofs_stream( + &self, + block_number: BlockNumberOf, + ) -> Result< + impl Stream< + Item = Result< + ( + relay_substrate_client::SyncHeader>, + SubstrateFinalityProof

, + ), + Error, + >, + >, + Error, + > { + let client = self.client.clone(); + let best_finalized_block_number = self.client.best_finalized_header_number().await?; + Ok(try_unfold((client, block_number), move |(client, current_block_number)| async move { + // if we've passed the `best_finalized_block_number`, we no longer need persistent + // justifications + if current_block_number > best_finalized_block_number { + return Ok(None) + } + + let (header, maybe_proof) = + header_and_finality_proof::

(&client, current_block_number).await?; + let next_block_number = current_block_number + One::one(); + let next_state = (client, next_block_number); + + Ok(Some((maybe_proof.map(|proof| (header, proof)), next_state))) + }) + .try_filter_map(|maybe_result| async { Ok(maybe_result) })) + } + + /// Returns stream of headers and their ephemeral proofs, starting from given block. + async fn ephemeral_proofs_stream( + &self, + block_number: BlockNumberOf, + ) -> Result< + impl Stream< + Item = Result< + ( + relay_substrate_client::SyncHeader>, + SubstrateFinalityProof

, + ), + Error, + >, + >, + Error, + > { + let client = self.client.clone(); + Ok(self.finality_proofs().await?.map(Ok).try_filter_map(move |proof| { + let client = client.clone(); + async move { + if proof.target_header_number() < block_number { + return Ok(None) + } + + let header = client.header_by_number(proof.target_header_number()).await?; + Ok(Some((header.into(), proof))) + } + })) + } +} + +impl Clone for SubstrateFinalitySource

{ + fn clone(&self) -> Self { + SubstrateFinalitySource { + client: self.client.clone(), + maximal_header_number: self.maximal_header_number.clone(), + } + } +} + +#[async_trait] +impl RelayClient for SubstrateFinalitySource

{ + type Error = Error; + + async fn reconnect(&mut self) -> Result<(), Error> { + self.client.reconnect().await + } +} + +#[async_trait] +impl SourceClient> + for SubstrateFinalitySource

+{ + type FinalityProofsStream = SubstrateFinalityProofsStream

; + + async fn best_finalized_block_number(&self) -> Result, Error> { + let mut finalized_header_number = self.on_chain_best_finalized_block_number().await?; + // never return block number larger than requested. This way we'll never sync headers + // past `maximal_header_number` + if let Some(ref maximal_header_number) = self.maximal_header_number { + let maximal_header_number = *maximal_header_number.lock().await; + if finalized_header_number > maximal_header_number { + finalized_header_number = maximal_header_number; + } + } + Ok(finalized_header_number) + } + + async fn header_and_finality_proof( + &self, + number: BlockNumberOf, + ) -> Result< + ( + relay_substrate_client::SyncHeader>, + Option>, + ), + Error, + > { + header_and_finality_proof::

(&self.client, number).await + } + + async fn finality_proofs(&self) -> Result { + Ok(unfold( + P::FinalityEngine::finality_proofs(&self.client).await?, + move |subscription| async move { + loop { + let log_error = |err| { + log::error!( + target: "bridge", + "Failed to read justification target from the {} justifications stream: {:?}", + P::SourceChain::NAME, + err, + ); + }; + + let next_justification = subscription + .next() + .await + .map_err(|err| log_error(err.to_string())) + .ok()??; + + let decoded_justification = + >::FinalityProof::decode( + &mut &next_justification[..], + ); + + let justification = match decoded_justification { + Ok(j) => j, + Err(err) => { + log_error(format!("decode failed with error {err:?}")); + continue + }, + }; + + return Some((justification, subscription)) + } + }, + ) + .boxed()) + } +} + +async fn header_and_finality_proof( + client: &Client, + number: BlockNumberOf, +) -> Result< + ( + relay_substrate_client::SyncHeader>, + Option>, + ), + Error, +> { + let header_hash = client.block_hash_by_number(number).await?; + let signed_block = client.get_block(Some(header_hash)).await?; + + let justification = signed_block + .justification(P::FinalityEngine::ID) + .map(|raw_justification| { + SubstrateFinalityProof::

::decode(&mut raw_justification.as_slice()) + }) + .transpose() + .map_err(Error::ResponseParseFailed)?; + + Ok((signed_block.header().into(), justification)) +} diff --git a/relays/lib-substrate-relay/src/finality/target.rs b/relays/lib-substrate-relay/src/finality/target.rs new file mode 100644 index 00000000000..81a22520fa9 --- /dev/null +++ b/relays/lib-substrate-relay/src/finality/target.rs @@ -0,0 +1,131 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate client as Substrate finality proof target. + +use crate::{ + finality::{ + engine::Engine, source::SubstrateFinalityProof, FinalitySyncPipelineAdapter, + SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, + }, + TransactionParams, +}; + +use async_trait::async_trait; +use finality_relay::TargetClient; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, Client, Error, HeaderIdOf, HeaderOf, SyncHeader, TransactionEra, + TransactionTracker, UnsignedTransaction, +}; +use relay_utils::relay_loop::Client as RelayClient; +use sp_core::Pair; + +/// Substrate client as Substrate finality target. +pub struct SubstrateFinalityTarget { + client: Client, + transaction_params: TransactionParams>, +} + +impl SubstrateFinalityTarget

{ + /// Create new Substrate headers target. + pub fn new( + client: Client, + transaction_params: TransactionParams>, + ) -> Self { + SubstrateFinalityTarget { client, transaction_params } + } + + /// Ensure that the bridge pallet at target chain is active. + pub async fn ensure_pallet_active(&self) -> Result<(), Error> { + let is_halted = P::FinalityEngine::is_halted(&self.client).await?; + if is_halted { + return Err(Error::BridgePalletIsHalted) + } + + let is_initialized = P::FinalityEngine::is_initialized(&self.client).await?; + if !is_initialized { + return Err(Error::BridgePalletIsNotInitialized) + } + + Ok(()) + } +} + +impl Clone for SubstrateFinalityTarget

{ + fn clone(&self) -> Self { + SubstrateFinalityTarget { + client: self.client.clone(), + transaction_params: self.transaction_params.clone(), + } + } +} + +#[async_trait] +impl RelayClient for SubstrateFinalityTarget

{ + type Error = Error; + + async fn reconnect(&mut self) -> Result<(), Error> { + self.client.reconnect().await + } +} + +#[async_trait] +impl TargetClient> + for SubstrateFinalityTarget

+where + AccountIdOf: From< as Pair>::Public>, +{ + type TransactionTracker = TransactionTracker>; + + async fn best_finalized_source_block_id(&self) -> Result, Error> { + // we can't continue to relay finality if target node is out of sync, because + // it may have already received (some of) headers that we're going to relay + self.client.ensure_synced().await?; + // we can't relay finality if bridge pallet at target chain is halted + self.ensure_pallet_active().await?; + + Ok(crate::messages_source::read_client_state::( + &self.client, + None, + ) + .await? + .best_finalized_peer_at_best_self + .ok_or(Error::BridgePalletIsNotInitialized)?) + } + + async fn submit_finality_proof( + &self, + header: SyncHeader>, + proof: SubstrateFinalityProof

, + ) -> Result { + // runtime module at target chain may require optimized finality proof + let proof = P::FinalityEngine::optimize_proof(&self.client, &header, proof).await?; + + // now we may submit optimized finality proof + let transaction_params = self.transaction_params.clone(); + let call = + P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof); + self.client + .submit_and_watch_signed_extrinsic( + &self.transaction_params.signer, + move |best_block_id, transaction_nonce| { + Ok(UnsignedTransaction::new(call.into(), transaction_nonce) + .era(TransactionEra::new(best_block_id, transaction_params.mortality))) + }, + ) + .await + } +} diff --git a/relays/lib-substrate-relay/src/lib.rs b/relays/lib-substrate-relay/src/lib.rs new file mode 100644 index 00000000000..37a4d602e59 --- /dev/null +++ b/relays/lib-substrate-relay/src/lib.rs @@ -0,0 +1,145 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! The library of substrate relay. contains some public codes to provide to substrate relay. + +#![warn(missing_docs)] + +use relay_substrate_client::{Chain, ChainWithUtilityPallet, UtilityPallet}; + +use std::marker::PhantomData; + +pub mod error; +pub mod finality; +pub mod messages_lane; +pub mod messages_metrics; +pub mod messages_source; +pub mod messages_target; +pub mod on_demand; +pub mod parachains; + +/// Transaction creation parameters. +#[derive(Clone, Debug)] +pub struct TransactionParams { + /// Transactions author. + pub signer: TS, + /// Transactions mortality. + pub mortality: Option, +} + +/// Tagged relay account, which balance may be exposed as metrics by the relay. +#[derive(Clone, Debug)] +pub enum TaggedAccount { + /// Account, used to sign headers relay transactions from given bridged chain. + Headers { + /// Account id. + id: AccountId, + /// Name of the bridged chain, which headers are relayed. + bridged_chain: String, + }, + /// Account, used to sign parachains relay transactions from given bridged relay chain. + Parachains { + /// Account id. + id: AccountId, + /// Name of the bridged relay chain with parachain heads. + bridged_chain: String, + }, + /// Account, used to sign message relay transactions from given bridged chain. + Messages { + /// Account id. + id: AccountId, + /// Name of the bridged chain, which sends us messages or delivery confirmations. + bridged_chain: String, + }, +} + +impl TaggedAccount { + /// Returns reference to the account id. + pub fn id(&self) -> &AccountId { + match *self { + TaggedAccount::Headers { ref id, .. } => id, + TaggedAccount::Parachains { ref id, .. } => id, + TaggedAccount::Messages { ref id, .. } => id, + } + } + + /// Returns stringified account tag. + pub fn tag(&self) -> String { + match *self { + TaggedAccount::Headers { ref bridged_chain, .. } => format!("{bridged_chain}Headers"), + TaggedAccount::Parachains { ref bridged_chain, .. } => { + format!("{bridged_chain}Parachains") + }, + TaggedAccount::Messages { ref bridged_chain, .. } => { + format!("{bridged_chain}Messages") + }, + } + } +} + +/// Batch call builder. +pub trait BatchCallBuilder: Clone + Send { + /// Create batch call from given calls vector. + fn build_batch_call(&self, _calls: Vec) -> Call; +} + +/// Batch call builder constructor. +pub trait BatchCallBuilderConstructor: Clone { + /// Call builder, used by this constructor. + type CallBuilder: BatchCallBuilder; + /// Create a new instance of a batch call builder. + fn new_builder() -> Option; +} + +/// Batch call builder based on `pallet-utility`. +#[derive(Clone)] +pub struct UtilityPalletBatchCallBuilder(PhantomData); + +impl BatchCallBuilder for UtilityPalletBatchCallBuilder +where + C: ChainWithUtilityPallet, +{ + fn build_batch_call(&self, calls: Vec) -> C::Call { + C::UtilityPallet::build_batch_call(calls) + } +} + +impl BatchCallBuilderConstructor for UtilityPalletBatchCallBuilder +where + C: ChainWithUtilityPallet, +{ + type CallBuilder = Self; + + fn new_builder() -> Option { + Some(Self(Default::default())) + } +} + +// A `BatchCallBuilderConstructor` that always returns `None`. +impl BatchCallBuilderConstructor for () { + type CallBuilder = (); + fn new_builder() -> Option { + None + } +} + +// Dummy `BatchCallBuilder` implementation that must never be used outside +// of the `impl BatchCallBuilderConstructor for ()` code. +impl BatchCallBuilder for () { + fn build_batch_call(&self, _calls: Vec) -> Call { + unreachable!("never called, because ()::new_builder() returns None; qed") + } +} diff --git a/relays/lib-substrate-relay/src/messages_lane.rs b/relays/lib-substrate-relay/src/messages_lane.rs new file mode 100644 index 00000000000..b86a2629b07 --- /dev/null +++ b/relays/lib-substrate-relay/src/messages_lane.rs @@ -0,0 +1,572 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Tools for supporting message lanes between two Substrate-based chains. + +use crate::{ + messages_source::{SubstrateMessagesProof, SubstrateMessagesSource}, + messages_target::{SubstrateMessagesDeliveryProof, SubstrateMessagesTarget}, + on_demand::OnDemandRelay, + BatchCallBuilder, BatchCallBuilderConstructor, TransactionParams, +}; + +use async_std::sync::Arc; +use bp_messages::{LaneId, MessageNonce}; +use bp_runtime::{ + AccountIdOf, Chain as _, EncodedOrDecodedCall, HeaderIdOf, TransactionEra, WeightExtraOps, +}; +use bridge_runtime_common::messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, +}; +use codec::Encode; +use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; +use messages_relay::{message_lane::MessageLane, message_lane_loop::BatchTransaction}; +use pallet_bridge_messages::{Call as BridgeMessagesCall, Config as BridgeMessagesConfig}; +use relay_substrate_client::{ + transaction_stall_timeout, AccountKeyPairOf, BalanceOf, BlockNumberOf, CallOf, Chain, + ChainWithMessages, ChainWithTransactions, Client, Error as SubstrateError, HashOf, SignParam, + UnsignedTransaction, +}; +use relay_utils::{ + metrics::{GlobalMetrics, MetricsParams, StandaloneMetric}, + STALL_TIMEOUT, +}; +use sp_core::Pair; +use sp_runtime::traits::Zero; +use std::{convert::TryFrom, fmt::Debug, marker::PhantomData}; + +/// Substrate -> Substrate messages synchronization pipeline. +pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync { + /// Messages of this chain are relayed to the `TargetChain`. + type SourceChain: ChainWithMessages + ChainWithTransactions; + /// Messages from the `SourceChain` are dispatched on this chain. + type TargetChain: ChainWithMessages + ChainWithTransactions; + + /// How receive messages proof call is built? + type ReceiveMessagesProofCallBuilder: ReceiveMessagesProofCallBuilder; + /// How receive messages delivery proof call is built? + type ReceiveMessagesDeliveryProofCallBuilder: ReceiveMessagesDeliveryProofCallBuilder; + + /// How batch calls are built at the source chain? + type SourceBatchCallBuilder: BatchCallBuilderConstructor>; + /// How batch calls are built at the target chain? + type TargetBatchCallBuilder: BatchCallBuilderConstructor>; +} + +/// Adapter that allows all `SubstrateMessageLane` to act as `MessageLane`. +#[derive(Clone, Debug)] +pub struct MessageLaneAdapter { + _phantom: PhantomData

, +} + +impl MessageLane for MessageLaneAdapter

{ + const SOURCE_NAME: &'static str = P::SourceChain::NAME; + const TARGET_NAME: &'static str = P::TargetChain::NAME; + + type MessagesProof = SubstrateMessagesProof; + type MessagesReceivingProof = SubstrateMessagesDeliveryProof; + + type SourceChainBalance = BalanceOf; + type SourceHeaderNumber = BlockNumberOf; + type SourceHeaderHash = HashOf; + + type TargetHeaderNumber = BlockNumberOf; + type TargetHeaderHash = HashOf; +} + +/// Substrate <-> Substrate messages relay parameters. +pub struct MessagesRelayParams { + /// Messages source client. + pub source_client: Client, + /// Source transaction params. + pub source_transaction_params: TransactionParams>, + /// Messages target client. + pub target_client: Client, + /// Target transaction params. + pub target_transaction_params: TransactionParams>, + /// Optional on-demand source to target headers relay. + pub source_to_target_headers_relay: + Option>>, + /// Optional on-demand target to source headers relay. + pub target_to_source_headers_relay: + Option>>, + /// Identifier of lane that needs to be served. + pub lane_id: LaneId, + /// Metrics parameters. + pub metrics_params: MetricsParams, +} + +/// Batch transaction that brings headers + and messages delivery/receiving confirmations to the +/// source node. +#[derive(Clone)] +pub struct BatchProofTransaction>> { + builder: B::CallBuilder, + proved_header: HeaderIdOf, + prove_calls: Vec>, + + /// Using `fn() -> B` in order to avoid implementing `Send` for `B`. + _phantom: PhantomData B>, +} + +impl>> std::fmt::Debug + for BatchProofTransaction +{ + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.debug_struct("BatchProofTransaction") + .field("proved_header", &self.proved_header) + .finish() + } +} + +impl>> + BatchProofTransaction +{ + /// Creates a new instance of `BatchProofTransaction`. + pub async fn new( + relay: Arc>, + block_num: BlockNumberOf, + ) -> Result, SubstrateError> { + if let Some(builder) = B::new_builder() { + let (proved_header, prove_calls) = relay.prove_header(block_num).await?; + return Ok(Some(Self { + builder, + proved_header, + prove_calls, + _phantom: Default::default(), + })) + } + + Ok(None) + } + + /// Return a batch call that includes the provided call. + pub fn append_call_and_build(mut self, call: CallOf) -> CallOf { + self.prove_calls.push(call); + self.builder.build_batch_call(self.prove_calls) + } +} + +impl>> + BatchTransaction> for BatchProofTransaction +{ + fn required_header_id(&self) -> HeaderIdOf { + self.proved_header + } +} + +/// Run Substrate-to-Substrate messages sync loop. +pub async fn run(params: MessagesRelayParams

) -> anyhow::Result<()> +where + AccountIdOf: From< as Pair>::Public>, + AccountIdOf: From< as Pair>::Public>, + BalanceOf: TryFrom>, +{ + // 2/3 is reserved for proofs and tx overhead + let max_messages_size_in_single_batch = P::TargetChain::max_extrinsic_size() / 3; + // we don't know exact weights of the Polkadot runtime. So to guess weights we'll be using + // weights from Rialto and then simply dividing it by x2. + let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = + select_delivery_transaction_limits_rpc::

( + ¶ms, + P::TargetChain::max_extrinsic_weight(), + P::SourceChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + ) + .await?; + let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = + (max_messages_in_single_batch / 2, max_messages_weight_in_single_batch / 2); + + let source_client = params.source_client; + let target_client = params.target_client; + let relayer_id_at_source: AccountIdOf = + params.source_transaction_params.signer.public().into(); + + log::info!( + target: "bridge", + "Starting {} -> {} messages relay.\n\t\ + {} relayer account id: {:?}\n\t\ + Max messages in single transaction: {}\n\t\ + Max messages size in single transaction: {}\n\t\ + Max messages weight in single transaction: {}\n\t\ + Tx mortality: {:?} (~{}m)/{:?} (~{}m)", + P::SourceChain::NAME, + P::TargetChain::NAME, + P::SourceChain::NAME, + relayer_id_at_source, + max_messages_in_single_batch, + max_messages_size_in_single_batch, + max_messages_weight_in_single_batch, + params.source_transaction_params.mortality, + transaction_stall_timeout( + params.source_transaction_params.mortality, + P::SourceChain::AVERAGE_BLOCK_INTERVAL, + STALL_TIMEOUT, + ).as_secs_f64() / 60.0f64, + params.target_transaction_params.mortality, + transaction_stall_timeout( + params.target_transaction_params.mortality, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + STALL_TIMEOUT, + ).as_secs_f64() / 60.0f64, + ); + + messages_relay::message_lane_loop::run( + messages_relay::message_lane_loop::Params { + lane: params.lane_id, + source_tick: P::SourceChain::AVERAGE_BLOCK_INTERVAL, + target_tick: P::TargetChain::AVERAGE_BLOCK_INTERVAL, + reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY, + delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams { + max_unrewarded_relayer_entries_at_target: + P::SourceChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + max_unconfirmed_nonces_at_target: + P::SourceChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + max_messages_in_single_batch, + max_messages_weight_in_single_batch, + max_messages_size_in_single_batch, + }, + }, + SubstrateMessagesSource::

::new( + source_client.clone(), + target_client.clone(), + params.lane_id, + params.source_transaction_params, + params.target_to_source_headers_relay, + ), + SubstrateMessagesTarget::

::new( + target_client, + source_client, + params.lane_id, + relayer_id_at_source, + params.target_transaction_params, + params.source_to_target_headers_relay, + ), + { + GlobalMetrics::new()?.register_and_spawn(¶ms.metrics_params.registry)?; + params.metrics_params + }, + futures::future::pending(), + ) + .await + .map_err(Into::into) +} + +/// Different ways of building `receive_messages_proof` calls. +pub trait ReceiveMessagesProofCallBuilder { + /// Given messages proof, build call of `receive_messages_proof` function of bridge + /// messages module at the target chain. + fn build_receive_messages_proof_call( + relayer_id_at_source: AccountIdOf, + proof: SubstrateMessagesProof, + messages_count: u32, + dispatch_weight: Weight, + trace_call: bool, + ) -> CallOf; +} + +/// Building `receive_messages_proof` call when you have direct access to the target +/// chain runtime. +pub struct DirectReceiveMessagesProofCallBuilder { + _phantom: PhantomData<(P, R, I)>, +} + +impl ReceiveMessagesProofCallBuilder

for DirectReceiveMessagesProofCallBuilder +where + P: SubstrateMessageLane, + R: BridgeMessagesConfig>, + I: 'static, + R::SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain< + MessagesProof = FromBridgedChainMessagesProof>, + >, + CallOf: From> + GetDispatchInfo, +{ + fn build_receive_messages_proof_call( + relayer_id_at_source: AccountIdOf, + proof: SubstrateMessagesProof, + messages_count: u32, + dispatch_weight: Weight, + trace_call: bool, + ) -> CallOf { + let call: CallOf = BridgeMessagesCall::::receive_messages_proof { + relayer_id_at_bridged_chain: relayer_id_at_source, + proof: proof.1, + messages_count, + dispatch_weight, + } + .into(); + if trace_call { + // this trace isn't super-accurate, because limits are for transactions and we + // have a call here, but it provides required information + log::trace!( + target: "bridge", + "Prepared {} -> {} messages delivery call. Weight: {}/{}, size: {}/{}", + P::SourceChain::NAME, + P::TargetChain::NAME, + call.get_dispatch_info().weight, + P::TargetChain::max_extrinsic_weight(), + call.encode().len(), + P::TargetChain::max_extrinsic_size(), + ); + } + call + } +} + +/// Macro that generates `ReceiveMessagesProofCallBuilder` implementation for the case when +/// you only have an access to the mocked version of target chain runtime. In this case you +/// should provide "name" of the call variant for the bridge messages calls and the "name" of +/// the variant for the `receive_messages_proof` call within that first option. +#[rustfmt::skip] +#[macro_export] +macro_rules! generate_receive_message_proof_call_builder { + ($pipeline:ident, $mocked_builder:ident, $bridge_messages:path, $receive_messages_proof:path) => { + pub struct $mocked_builder; + + impl $crate::messages_lane::ReceiveMessagesProofCallBuilder<$pipeline> + for $mocked_builder + { + fn build_receive_messages_proof_call( + relayer_id_at_source: relay_substrate_client::AccountIdOf< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::SourceChain + >, + proof: $crate::messages_source::SubstrateMessagesProof< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::SourceChain + >, + messages_count: u32, + dispatch_weight: bp_messages::Weight, + _trace_call: bool, + ) -> relay_substrate_client::CallOf< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::TargetChain + > { + bp_runtime::paste::item! { + $bridge_messages($receive_messages_proof { + relayer_id_at_bridged_chain: relayer_id_at_source, + proof: proof.1, + messages_count: messages_count, + dispatch_weight: dispatch_weight, + }) + } + } + } + }; +} + +/// Different ways of building `receive_messages_delivery_proof` calls. +pub trait ReceiveMessagesDeliveryProofCallBuilder { + /// Given messages delivery proof, build call of `receive_messages_delivery_proof` function of + /// bridge messages module at the source chain. + fn build_receive_messages_delivery_proof_call( + proof: SubstrateMessagesDeliveryProof, + trace_call: bool, + ) -> CallOf; +} + +/// Building `receive_messages_delivery_proof` call when you have direct access to the source +/// chain runtime. +pub struct DirectReceiveMessagesDeliveryProofCallBuilder { + _phantom: PhantomData<(P, R, I)>, +} + +impl ReceiveMessagesDeliveryProofCallBuilder

+ for DirectReceiveMessagesDeliveryProofCallBuilder +where + P: SubstrateMessageLane, + R: BridgeMessagesConfig, + I: 'static, + R::TargetHeaderChain: bp_messages::source_chain::TargetHeaderChain< + R::OutboundPayload, + R::AccountId, + MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof>, + >, + CallOf: From> + GetDispatchInfo, +{ + fn build_receive_messages_delivery_proof_call( + proof: SubstrateMessagesDeliveryProof, + trace_call: bool, + ) -> CallOf { + let call: CallOf = + BridgeMessagesCall::::receive_messages_delivery_proof { + proof: proof.1, + relayers_state: proof.0, + } + .into(); + if trace_call { + // this trace isn't super-accurate, because limits are for transactions and we + // have a call here, but it provides required information + log::trace!( + target: "bridge", + "Prepared {} -> {} delivery confirmation transaction. Weight: {}/{}, size: {}/{}", + P::TargetChain::NAME, + P::SourceChain::NAME, + call.get_dispatch_info().weight, + P::SourceChain::max_extrinsic_weight(), + call.encode().len(), + P::SourceChain::max_extrinsic_size(), + ); + } + call + } +} + +/// Macro that generates `ReceiveMessagesDeliveryProofCallBuilder` implementation for the case when +/// you only have an access to the mocked version of source chain runtime. In this case you +/// should provide "name" of the call variant for the bridge messages calls and the "name" of +/// the variant for the `receive_messages_delivery_proof` call within that first option. +#[rustfmt::skip] +#[macro_export] +macro_rules! generate_receive_message_delivery_proof_call_builder { + ($pipeline:ident, $mocked_builder:ident, $bridge_messages:path, $receive_messages_delivery_proof:path) => { + pub struct $mocked_builder; + + impl $crate::messages_lane::ReceiveMessagesDeliveryProofCallBuilder<$pipeline> + for $mocked_builder + { + fn build_receive_messages_delivery_proof_call( + proof: $crate::messages_target::SubstrateMessagesDeliveryProof< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::TargetChain + >, + _trace_call: bool, + ) -> relay_substrate_client::CallOf< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::SourceChain + > { + bp_runtime::paste::item! { + $bridge_messages($receive_messages_delivery_proof { + proof: proof.1, + relayers_state: proof.0 + }) + } + } + } + }; +} + +/// Returns maximal number of messages and their maximal cumulative dispatch weight. +async fn select_delivery_transaction_limits_rpc( + params: &MessagesRelayParams

, + max_extrinsic_weight: Weight, + max_unconfirmed_messages_at_inbound_lane: MessageNonce, +) -> anyhow::Result<(MessageNonce, Weight)> +where + AccountIdOf: From< as Pair>::Public>, +{ + // We may try to guess accurate value, based on maximal number of messages and per-message + // weight overhead, but the relay loop isn't using this info in a super-accurate way anyway. + // So just a rough guess: let's say 1/3 of max tx weight is for tx itself and the rest is + // for messages dispatch. + + // Another thing to keep in mind is that our runtimes (when this code was written) accept + // messages with dispatch weight <= max_extrinsic_weight/2. So we can't reserve less than + // that for dispatch. + + let weight_for_delivery_tx = max_extrinsic_weight / 3; + let weight_for_messages_dispatch = max_extrinsic_weight - weight_for_delivery_tx; + + // weight of empty message delivery with outbound lane state + let delivery_tx_with_zero_messages = dummy_messages_delivery_transaction::

(params, 0)?; + let delivery_tx_with_zero_messages_weight = params + .target_client + .extimate_extrinsic_weight(delivery_tx_with_zero_messages) + .await + .map_err(|e| { + anyhow::format_err!("Failed to estimate delivery extrinsic weight: {:?}", e) + })?; + + // weight of single message delivery with outbound lane state + let delivery_tx_with_one_message = dummy_messages_delivery_transaction::

(params, 1)?; + let delivery_tx_with_one_message_weight = params + .target_client + .extimate_extrinsic_weight(delivery_tx_with_one_message) + .await + .map_err(|e| { + anyhow::format_err!("Failed to estimate delivery extrinsic weight: {:?}", e) + })?; + + // message overhead is roughly `delivery_tx_with_one_message_weight - + // delivery_tx_with_zero_messages_weight` + let delivery_tx_weight_rest = weight_for_delivery_tx - delivery_tx_with_zero_messages_weight; + let delivery_tx_message_overhead = + delivery_tx_with_one_message_weight.saturating_sub(delivery_tx_with_zero_messages_weight); + + let max_number_of_messages = std::cmp::min( + delivery_tx_weight_rest + .min_components_checked_div(delivery_tx_message_overhead) + .unwrap_or(u64::MAX), + max_unconfirmed_messages_at_inbound_lane, + ); + + assert!( + max_number_of_messages > 0, + "Relay should fit at least one message in every delivery transaction", + ); + assert!( + weight_for_messages_dispatch.ref_time() >= max_extrinsic_weight.ref_time() / 2, + "Relay shall be able to deliver messages with dispatch weight = max_extrinsic_weight / 2", + ); + + Ok((max_number_of_messages, weight_for_messages_dispatch)) +} + +/// Returns dummy message delivery transaction with zero messages and `1kb` proof. +fn dummy_messages_delivery_transaction( + params: &MessagesRelayParams

, + messages: u32, +) -> anyhow::Result<::SignedTransaction> +where + AccountIdOf: From< as Pair>::Public>, +{ + // we don't care about any call values here, because all that the estimation RPC does + // is calls `GetDispatchInfo::get_dispatch_info` for the wrapped call. So we only are + // interested in values that affect call weight - e.g. number of messages and the + // storage proof size + + let dummy_messages_delivery_call = + P::ReceiveMessagesProofCallBuilder::build_receive_messages_proof_call( + params.source_transaction_params.signer.public().into(), + ( + Weight::zero(), + FromBridgedChainMessagesProof { + bridged_header_hash: Default::default(), + // we may use per-chain `EXTRA_STORAGE_PROOF_SIZE`, but since we don't need + // exact values, this global estimation is fine + storage_proof: vec![vec![ + 42u8; + pallet_bridge_messages::EXTRA_STORAGE_PROOF_SIZE + as usize + ]], + lane: Default::default(), + nonces_start: 1, + nonces_end: messages as u64, + }, + ), + messages, + Weight::zero(), + false, + ); + P::TargetChain::sign_transaction( + SignParam { + spec_version: 0, + transaction_version: 0, + genesis_hash: Default::default(), + signer: params.target_transaction_params.signer.clone(), + }, + UnsignedTransaction { + call: EncodedOrDecodedCall::Decoded(dummy_messages_delivery_call), + nonce: Zero::zero(), + tip: Zero::zero(), + era: TransactionEra::Immortal, + }, + ) + .map_err(Into::into) +} diff --git a/relays/lib-substrate-relay/src/messages_metrics.rs b/relays/lib-substrate-relay/src/messages_metrics.rs new file mode 100644 index 00000000000..33d855a0263 --- /dev/null +++ b/relays/lib-substrate-relay/src/messages_metrics.rs @@ -0,0 +1,190 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Tools for supporting message lanes between two Substrate-based chains. + +use crate::TaggedAccount; + +use bp_messages::LaneId; +use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; +use bp_runtime::StorageDoubleMapKeyProvider; +use codec::Decode; +use frame_system::AccountInfo; +use pallet_balances::AccountData; +use relay_substrate_client::{ + metrics::{FloatStorageValue, FloatStorageValueMetric}, + AccountIdOf, BalanceOf, Chain, ChainWithBalances, ChainWithMessages, Client, + Error as SubstrateError, IndexOf, +}; +use relay_utils::metrics::{MetricsParams, StandaloneMetric}; +use sp_core::storage::StorageData; +use sp_runtime::{FixedPointNumber, FixedU128}; +use std::{convert::TryFrom, fmt::Debug, marker::PhantomData}; + +/// Add relay accounts balance metrics. +pub async fn add_relay_balances_metrics( + client: Client, + metrics: &mut MetricsParams, + relay_accounts: &Vec>>, + lanes: &[LaneId], +) -> anyhow::Result<()> +where + BalanceOf: Into + std::fmt::Debug, +{ + if relay_accounts.is_empty() { + return Ok(()) + } + + // if `tokenDecimals` is missing from system properties, we'll be using + let token_decimals = client + .token_decimals() + .await? + .map(|token_decimals| { + log::info!(target: "bridge", "Read `tokenDecimals` for {}: {}", C::NAME, token_decimals); + token_decimals + }) + .unwrap_or_else(|| { + // turns out it is normal not to have this property - e.g. when polkadot binary is + // started using `polkadot-local` chain. Let's use minimal nominal here + log::info!(target: "bridge", "Using default (zero) `tokenDecimals` value for {}", C::NAME); + 0 + }); + let token_decimals = u32::try_from(token_decimals).map_err(|e| { + anyhow::format_err!( + "Token decimals value ({}) of {} doesn't fit into u32: {:?}", + token_decimals, + C::NAME, + e, + ) + })?; + + for account in relay_accounts { + let relay_account_balance_metric = FloatStorageValueMetric::new( + AccountBalanceFromAccountInfo:: { token_decimals, _phantom: Default::default() }, + client.clone(), + C::account_info_storage_key(account.id()), + format!("at_{}_relay_{}_balance", C::NAME, account.tag()), + format!("Balance of the {} relay account at the {}", account.tag(), C::NAME), + )?; + relay_account_balance_metric.register_and_spawn(&metrics.registry)?; + + if let Some(relayers_pallet_name) = BC::WITH_CHAIN_RELAYERS_PALLET_NAME { + for lane in lanes { + FloatStorageValueMetric::new( + AccountBalance:: { token_decimals, _phantom: Default::default() }, + client.clone(), + bp_relayers::RelayerRewardsKeyProvider::, BalanceOf>::final_key( + relayers_pallet_name, + account.id(), + &RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::ThisChain), + ), + format!("at_{}_relay_{}_reward_for_msgs_from_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, hex::encode(lane.as_ref())), + format!("Reward of the {} relay account at {} for delivering messages from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane), + )?.register_and_spawn(&metrics.registry)?; + + FloatStorageValueMetric::new( + AccountBalance:: { token_decimals, _phantom: Default::default() }, + client.clone(), + bp_relayers::RelayerRewardsKeyProvider::, BalanceOf>::final_key( + relayers_pallet_name, + account.id(), + &RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::BridgedChain), + ), + format!("at_{}_relay_{}_reward_for_msgs_to_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, hex::encode(lane.as_ref())), + format!("Reward of the {} relay account at {} for delivering messages confirmations from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane), + )?.register_and_spawn(&metrics.registry)?; + } + } + } + + Ok(()) +} + +/// Adapter for `FloatStorageValueMetric` to decode account free balance. +#[derive(Clone, Debug)] +struct AccountBalanceFromAccountInfo { + token_decimals: u32, + _phantom: PhantomData, +} + +impl FloatStorageValue for AccountBalanceFromAccountInfo +where + C: Chain, + BalanceOf: Into, +{ + type Value = FixedU128; + + fn decode( + &self, + maybe_raw_value: Option, + ) -> Result, SubstrateError> { + maybe_raw_value + .map(|raw_value| { + AccountInfo::, AccountData>>::decode(&mut &raw_value.0[..]) + .map_err(SubstrateError::ResponseParseFailed) + .map(|account_data| { + convert_to_token_balance(account_data.data.free.into(), self.token_decimals) + }) + }) + .transpose() + } +} + +/// Adapter for `FloatStorageValueMetric` to decode account free balance. +#[derive(Clone, Debug)] +struct AccountBalance { + token_decimals: u32, + _phantom: PhantomData, +} + +impl FloatStorageValue for AccountBalance +where + C: Chain, + BalanceOf: Into, +{ + type Value = FixedU128; + + fn decode( + &self, + maybe_raw_value: Option, + ) -> Result, SubstrateError> { + maybe_raw_value + .map(|raw_value| { + BalanceOf::::decode(&mut &raw_value.0[..]) + .map_err(SubstrateError::ResponseParseFailed) + .map(|balance| convert_to_token_balance(balance.into(), self.token_decimals)) + }) + .transpose() + } +} + +/// Convert from raw `u128` balance (nominated in smallest chain token units) to the float regular +/// tokens value. +fn convert_to_token_balance(balance: u128, token_decimals: u32) -> FixedU128 { + FixedU128::from_inner(balance.saturating_mul(FixedU128::DIV / 10u128.pow(token_decimals))) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn token_decimals_used_properly() { + let plancks = 425_000_000_000; + let token_decimals = 10; + let dots = convert_to_token_balance(plancks, token_decimals); + assert_eq!(dots, FixedU128::saturating_from_rational(425, 10)); + } +} diff --git a/relays/lib-substrate-relay/src/messages_source.rs b/relays/lib-substrate-relay/src/messages_source.rs new file mode 100644 index 00000000000..de795beb787 --- /dev/null +++ b/relays/lib-substrate-relay/src/messages_source.rs @@ -0,0 +1,721 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate client as Substrate messages source. The chain we connect to should have +//! runtime that implements `HeaderApi` to allow bridging with +//! `` chain. + +use crate::{ + messages_lane::{ + BatchProofTransaction, MessageLaneAdapter, ReceiveMessagesDeliveryProofCallBuilder, + SubstrateMessageLane, + }, + on_demand::OnDemandRelay, + TransactionParams, +}; + +use async_std::sync::Arc; +use async_trait::async_trait; +use bp_messages::{ + storage_keys::{operating_mode_key, outbound_lane_data_key}, + InboundMessageDetails, LaneId, MessageNonce, MessagePayload, MessagesOperatingMode, + OutboundLaneData, OutboundMessageDetails, +}; +use bp_runtime::{BasicOperatingMode, HeaderIdProvider}; +use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; +use codec::Encode; +use frame_support::weights::Weight; +use messages_relay::{ + message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, + message_lane_loop::{ + ClientState, MessageDetails, MessageDetailsMap, MessageProofParameters, SourceClient, + SourceClientState, + }, +}; +use num_traits::Zero; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, BalanceOf, Chain, ChainWithMessages, Client, + Error as SubstrateError, HashOf, HeaderIdOf, TransactionEra, TransactionTracker, + UnsignedTransaction, +}; +use relay_utils::relay_loop::Client as RelayClient; +use sp_core::Pair; +use std::ops::RangeInclusive; + +/// Intermediate message proof returned by the source Substrate node. Includes everything +/// required to submit to the target node: cumulative dispatch weight of bundled messages and +/// the proof itself. +pub type SubstrateMessagesProof = (Weight, FromBridgedChainMessagesProof>); +type MessagesToRefine<'a> = Vec<(MessagePayload, &'a mut OutboundMessageDetails)>; + +/// Substrate client as Substrate messages source. +pub struct SubstrateMessagesSource { + source_client: Client, + target_client: Client, + lane_id: LaneId, + transaction_params: TransactionParams>, + target_to_source_headers_relay: Option>>, +} + +impl SubstrateMessagesSource

{ + /// Create new Substrate headers source. + pub fn new( + source_client: Client, + target_client: Client, + lane_id: LaneId, + transaction_params: TransactionParams>, + target_to_source_headers_relay: Option< + Arc>, + >, + ) -> Self { + SubstrateMessagesSource { + source_client, + target_client, + lane_id, + transaction_params, + target_to_source_headers_relay, + } + } + + /// Read outbound lane state from the on-chain storage at given block. + async fn outbound_lane_data( + &self, + id: SourceHeaderIdOf>, + ) -> Result, SubstrateError> { + self.source_client + .storage_value( + outbound_lane_data_key( + P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + ), + Some(id.1), + ) + .await + } + + /// Ensure that the messages pallet at source chain is active. + async fn ensure_pallet_active(&self) -> Result<(), SubstrateError> { + ensure_messages_pallet_active::(&self.source_client).await + } +} + +impl Clone for SubstrateMessagesSource

{ + fn clone(&self) -> Self { + Self { + source_client: self.source_client.clone(), + target_client: self.target_client.clone(), + lane_id: self.lane_id, + transaction_params: self.transaction_params.clone(), + target_to_source_headers_relay: self.target_to_source_headers_relay.clone(), + } + } +} + +#[async_trait] +impl RelayClient for SubstrateMessagesSource

{ + type Error = SubstrateError; + + async fn reconnect(&mut self) -> Result<(), SubstrateError> { + // since the client calls RPC methods on both sides, we need to reconnect both + self.source_client.reconnect().await?; + self.target_client.reconnect().await?; + + // call reconnect on on-demand headers relay, because we may use different chains there + // and the error that has lead to reconnect may have came from those other chains + // (see `require_target_header_on_source`) + // + // this may lead to multiple reconnects to the same node during the same call and it + // needs to be addressed in the future + // TODO: https://github.com/paritytech/parity-bridges-common/issues/1928 + if let Some(ref mut target_to_source_headers_relay) = self.target_to_source_headers_relay { + target_to_source_headers_relay.reconnect().await?; + } + + Ok(()) + } +} + +#[async_trait] +impl SourceClient> for SubstrateMessagesSource

+where + AccountIdOf: From< as Pair>::Public>, +{ + type BatchTransaction = + BatchProofTransaction; + type TransactionTracker = TransactionTracker>; + + async fn state(&self) -> Result>, SubstrateError> { + // we can't continue to deliver confirmations if source node is out of sync, because + // it may have already received confirmations that we're going to deliver + // + // we can't continue to deliver messages if target node is out of sync, because + // it may have already received (some of) messages that we're going to deliver + self.source_client.ensure_synced().await?; + self.target_client.ensure_synced().await?; + // we can't relay confirmations if messages pallet at source chain is halted + self.ensure_pallet_active().await?; + + read_client_state(&self.source_client, Some(&self.target_client)).await + } + + async fn latest_generated_nonce( + &self, + id: SourceHeaderIdOf>, + ) -> Result<(SourceHeaderIdOf>, MessageNonce), SubstrateError> { + // lane data missing from the storage is fine until first message is sent + let latest_generated_nonce = self + .outbound_lane_data(id) + .await? + .map(|data| data.latest_generated_nonce) + .unwrap_or(0); + Ok((id, latest_generated_nonce)) + } + + async fn latest_confirmed_received_nonce( + &self, + id: SourceHeaderIdOf>, + ) -> Result<(SourceHeaderIdOf>, MessageNonce), SubstrateError> { + // lane data missing from the storage is fine until first message is sent + let latest_received_nonce = self + .outbound_lane_data(id) + .await? + .map(|data| data.latest_received_nonce) + .unwrap_or(0); + Ok((id, latest_received_nonce)) + } + + async fn generated_message_details( + &self, + id: SourceHeaderIdOf>, + nonces: RangeInclusive, + ) -> Result>, SubstrateError> { + let mut out_msgs_details = self + .source_client + .typed_state_call::<_, Vec<_>>( + P::TargetChain::TO_CHAIN_MESSAGE_DETAILS_METHOD.into(), + (self.lane_id, *nonces.start(), *nonces.end()), + Some(id.1), + ) + .await?; + validate_out_msgs_details::(&out_msgs_details, nonces)?; + + // prepare arguments of the inbound message details call (if we need it) + let mut msgs_to_refine = vec![]; + for out_msg_details in out_msgs_details.iter_mut() { + // in our current strategy all messages are supposed to be paid at the target chain + + // for pay-at-target messages we may want to ask target chain for + // refined dispatch weight + let msg_key = bp_messages::storage_keys::message_key( + P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + out_msg_details.nonce, + ); + let msg_payload: MessagePayload = + self.source_client.storage_value(msg_key, Some(id.1)).await?.ok_or_else(|| { + SubstrateError::Custom(format!( + "Message to {} {:?}/{} is missing from runtime the storage of {} at {:?}", + P::TargetChain::NAME, + self.lane_id, + out_msg_details.nonce, + P::SourceChain::NAME, + id, + )) + })?; + + msgs_to_refine.push((msg_payload, out_msg_details)); + } + + for mut msgs_to_refine_batch in + split_msgs_to_refine::(self.lane_id, msgs_to_refine)? + { + let in_msgs_details = self + .target_client + .typed_state_call::<_, Vec>( + P::SourceChain::FROM_CHAIN_MESSAGE_DETAILS_METHOD.into(), + (self.lane_id, &msgs_to_refine_batch), + None, + ) + .await?; + if in_msgs_details.len() != msgs_to_refine_batch.len() { + return Err(SubstrateError::Custom(format!( + "Call of {} at {} has returned {} entries instead of expected {}", + P::SourceChain::FROM_CHAIN_MESSAGE_DETAILS_METHOD, + P::TargetChain::NAME, + in_msgs_details.len(), + msgs_to_refine_batch.len(), + ))) + } + for ((_, out_msg_details), in_msg_details) in + msgs_to_refine_batch.iter_mut().zip(in_msgs_details) + { + log::trace!( + target: "bridge", + "Refined weight of {}->{} message {:?}/{}: at-source: {}, at-target: {}", + P::SourceChain::NAME, + P::TargetChain::NAME, + self.lane_id, + out_msg_details.nonce, + out_msg_details.dispatch_weight, + in_msg_details.dispatch_weight, + ); + out_msg_details.dispatch_weight = in_msg_details.dispatch_weight; + } + } + + let mut msgs_details_map = MessageDetailsMap::new(); + for out_msg_details in out_msgs_details { + msgs_details_map.insert( + out_msg_details.nonce, + MessageDetails { + dispatch_weight: out_msg_details.dispatch_weight, + size: out_msg_details.size as _, + reward: Zero::zero(), + }, + ); + } + + Ok(msgs_details_map) + } + + async fn prove_messages( + &self, + id: SourceHeaderIdOf>, + nonces: RangeInclusive, + proof_parameters: MessageProofParameters, + ) -> Result< + ( + SourceHeaderIdOf>, + RangeInclusive, + as MessageLane>::MessagesProof, + ), + SubstrateError, + > { + let mut storage_keys = + Vec::with_capacity(nonces.end().saturating_sub(*nonces.start()) as usize + 1); + let mut message_nonce = *nonces.start(); + while message_nonce <= *nonces.end() { + let message_key = bp_messages::storage_keys::message_key( + P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + message_nonce, + ); + storage_keys.push(message_key); + message_nonce += 1; + } + if proof_parameters.outbound_state_proof_required { + storage_keys.push(bp_messages::storage_keys::outbound_lane_data_key( + P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + )); + } + + let proof = self + .source_client + .prove_storage(storage_keys, id.1) + .await? + .into_iter_nodes() + .collect(); + let proof = FromBridgedChainMessagesProof { + bridged_header_hash: id.1, + storage_proof: proof, + lane: self.lane_id, + nonces_start: *nonces.start(), + nonces_end: *nonces.end(), + }; + Ok((id, nonces, (proof_parameters.dispatch_weight, proof))) + } + + async fn submit_messages_receiving_proof( + &self, + maybe_batch_tx: Option, + _generated_at_block: TargetHeaderIdOf>, + proof: as MessageLane>::MessagesReceivingProof, + ) -> Result { + let messages_proof_call = + P::ReceiveMessagesDeliveryProofCallBuilder::build_receive_messages_delivery_proof_call( + proof, + maybe_batch_tx.is_none(), + ); + let final_call = match maybe_batch_tx { + Some(batch_tx) => batch_tx.append_call_and_build(messages_proof_call), + None => messages_proof_call, + }; + + let transaction_params = self.transaction_params.clone(); + self.source_client + .submit_and_watch_signed_extrinsic( + &self.transaction_params.signer, + move |best_block_id, transaction_nonce| { + Ok(UnsignedTransaction::new(final_call.into(), transaction_nonce) + .era(TransactionEra::new(best_block_id, transaction_params.mortality))) + }, + ) + .await + } + + async fn require_target_header_on_source( + &self, + id: TargetHeaderIdOf>, + ) -> Result, SubstrateError> { + if let Some(ref target_to_source_headers_relay) = self.target_to_source_headers_relay { + if let Some(batch_tx) = + BatchProofTransaction::new(target_to_source_headers_relay.clone(), id.0).await? + { + return Ok(Some(batch_tx)) + } + + target_to_source_headers_relay.require_more_headers(id.0).await; + } + + Ok(None) + } +} + +/// Ensure that the messages pallet at source chain is active. +pub(crate) async fn ensure_messages_pallet_active( + client: &Client, +) -> Result<(), SubstrateError> +where + AtChain: ChainWithMessages, + WithChain: ChainWithMessages, +{ + let operating_mode = client + .storage_value(operating_mode_key(WithChain::WITH_CHAIN_MESSAGES_PALLET_NAME), None) + .await?; + let is_halted = + operating_mode == Some(MessagesOperatingMode::Basic(BasicOperatingMode::Halted)); + if is_halted { + Err(SubstrateError::BridgePalletIsHalted) + } else { + Ok(()) + } +} + +/// Read best blocks from given client. +/// +/// This function assumes that the chain that is followed by the `self_client` has +/// bridge GRANDPA pallet deployed and it provides `best_finalized_header_id_method_name` +/// runtime API to read the best finalized Bridged chain header. +/// +/// If `peer_client` is `None`, the value of `actual_best_finalized_peer_at_best_self` will +/// always match the `best_finalized_peer_at_best_self`. +pub async fn read_client_state( + self_client: &Client, + peer_client: Option<&Client>, +) -> Result, HeaderIdOf>, SubstrateError> +where + SelfChain: Chain, + PeerChain: Chain, +{ + // let's read our state first: we need best finalized header hash on **this** chain + let self_best_finalized_id = self_client.best_finalized_header().await?.id(); + // now let's read our best header on **this** chain + let self_best_id = self_client.best_header().await?.id(); + + // now let's read id of best finalized peer header at our best finalized block + let peer_on_self_best_finalized_id = + best_finalized_peer_header_at_self::( + self_client, + self_best_id.hash(), + ) + .await?; + + // read actual header, matching the `peer_on_self_best_finalized_id` from the peer chain + let actual_peer_on_self_best_finalized_id = + match (peer_client, peer_on_self_best_finalized_id.as_ref()) { + (Some(peer_client), Some(peer_on_self_best_finalized_id)) => { + let actual_peer_on_self_best_finalized = + peer_client.header_by_number(peer_on_self_best_finalized_id.number()).await?; + Some(actual_peer_on_self_best_finalized.id()) + }, + _ => peer_on_self_best_finalized_id, + }; + + Ok(ClientState { + best_self: self_best_id, + best_finalized_self: self_best_finalized_id, + best_finalized_peer_at_best_self: peer_on_self_best_finalized_id, + actual_best_finalized_peer_at_best_self: actual_peer_on_self_best_finalized_id, + }) +} + +/// Reads best `PeerChain` header known to the `SelfChain` using provided runtime API method. +/// +/// Method is supposed to be the `FinalityApi::best_finalized()` method. +pub async fn best_finalized_peer_header_at_self( + self_client: &Client, + at_self_hash: HashOf, +) -> Result>, SubstrateError> +where + SelfChain: Chain, + PeerChain: Chain, +{ + // now let's read id of best finalized peer header at our best finalized block + self_client + .typed_state_call::<_, Option<_>>( + PeerChain::BEST_FINALIZED_HEADER_ID_METHOD.into(), + (), + Some(at_self_hash), + ) + .await +} + +fn validate_out_msgs_details( + out_msgs_details: &[OutboundMessageDetails], + nonces: RangeInclusive, +) -> Result<(), SubstrateError> { + let make_missing_nonce_error = |expected_nonce| { + Err(SubstrateError::Custom(format!( + "Missing nonce {expected_nonce} in message_details call result. Expected all nonces from {nonces:?}", + ))) + }; + + if out_msgs_details.len() > nonces.clone().count() { + return Err(SubstrateError::Custom( + "More messages than requested returned by the message_details call.".into(), + )) + } + + // Check if last nonce is missing. The loop below is not checking this. + if out_msgs_details.is_empty() && !nonces.is_empty() { + return make_missing_nonce_error(*nonces.end()) + } + + let mut nonces_iter = nonces.clone().rev().peekable(); + let mut out_msgs_details_iter = out_msgs_details.iter().rev(); + while let Some((out_msg_details, &nonce)) = out_msgs_details_iter.next().zip(nonces_iter.peek()) + { + nonces_iter.next(); + if out_msg_details.nonce != nonce { + // Some nonces are missing from the middle/tail of the range. This is critical error. + return make_missing_nonce_error(nonce) + } + } + + // Check if some nonces from the beginning of the range are missing. This may happen if + // some messages were already pruned from the source node. This is not a critical error + // and will be auto-resolved by messages lane (and target node). + if nonces_iter.peek().is_some() { + log::info!( + target: "bridge", + "Some messages are missing from the {} node: {:?}. Target node may be out of sync?", + C::NAME, + nonces_iter.rev().collect::>(), + ); + } + + Ok(()) +} + +fn split_msgs_to_refine( + lane_id: LaneId, + msgs_to_refine: MessagesToRefine, +) -> Result, SubstrateError> { + let max_batch_size = Target::max_extrinsic_size() as usize; + let mut batches = vec![]; + + let mut current_msgs_batch = msgs_to_refine; + while !current_msgs_batch.is_empty() { + let mut next_msgs_batch = vec![]; + while (lane_id, ¤t_msgs_batch).encoded_size() > max_batch_size { + if current_msgs_batch.len() <= 1 { + return Err(SubstrateError::Custom(format!( + "Call of {} at {} can't be executed even if only one message is supplied. \ + max_extrinsic_size(): {}", + Source::FROM_CHAIN_MESSAGE_DETAILS_METHOD, + Target::NAME, + Target::max_extrinsic_size(), + ))) + } + + if let Some(msg) = current_msgs_batch.pop() { + next_msgs_batch.insert(0, msg); + } + } + + batches.push(current_msgs_batch); + current_msgs_batch = next_msgs_batch; + } + + Ok(batches) +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_runtime::Chain as ChainBase; + use relay_rialto_client::Rialto; + use relay_rococo_client::Rococo; + use relay_wococo_client::Wococo; + + fn message_details_from_rpc( + nonces: RangeInclusive, + ) -> Vec { + nonces + .into_iter() + .map(|nonce| bp_messages::OutboundMessageDetails { + nonce, + dispatch_weight: Weight::zero(), + size: 0, + }) + .collect() + } + + #[test] + fn validate_out_msgs_details_succeeds_if_no_messages_are_missing() { + assert!( + validate_out_msgs_details::(&message_details_from_rpc(1..=3), 1..=3,).is_ok() + ); + } + + #[test] + fn validate_out_msgs_details_succeeds_if_head_messages_are_missing() { + assert!( + validate_out_msgs_details::(&message_details_from_rpc(2..=3), 1..=3,).is_ok() + ) + } + + #[test] + fn validate_out_msgs_details_fails_if_mid_messages_are_missing() { + let mut message_details_from_rpc = message_details_from_rpc(1..=3); + message_details_from_rpc.remove(1); + assert!(matches!( + validate_out_msgs_details::(&message_details_from_rpc, 1..=3,), + Err(SubstrateError::Custom(_)) + )); + } + + #[test] + fn validate_out_msgs_details_map_fails_if_tail_messages_are_missing() { + assert!(matches!( + validate_out_msgs_details::(&message_details_from_rpc(1..=2), 1..=3,), + Err(SubstrateError::Custom(_)) + )); + } + + #[test] + fn validate_out_msgs_details_fails_if_all_messages_are_missing() { + assert!(matches!( + validate_out_msgs_details::(&[], 1..=3), + Err(SubstrateError::Custom(_)) + )); + } + + #[test] + fn validate_out_msgs_details_fails_if_more_messages_than_nonces() { + assert!(matches!( + validate_out_msgs_details::(&message_details_from_rpc(1..=5), 2..=5,), + Err(SubstrateError::Custom(_)) + )); + } + + fn check_split_msgs_to_refine( + payload_sizes: Vec, + expected_batches: Result, ()>, + ) { + let mut out_msgs_details = vec![]; + for (idx, _) in payload_sizes.iter().enumerate() { + out_msgs_details.push(OutboundMessageDetails { + nonce: idx as MessageNonce, + dispatch_weight: Weight::zero(), + size: 0, + }); + } + + let mut msgs_to_refine = vec![]; + for (&payload_size, out_msg_details) in + payload_sizes.iter().zip(out_msgs_details.iter_mut()) + { + let payload = vec![1u8; payload_size]; + msgs_to_refine.push((payload, out_msg_details)); + } + + let maybe_batches = + split_msgs_to_refine::(LaneId([0, 0, 0, 0]), msgs_to_refine); + match expected_batches { + Ok(expected_batches) => { + let batches = maybe_batches.unwrap(); + let mut idx = 0; + assert_eq!(batches.len(), expected_batches.len()); + for (batch, &expected_batch_size) in batches.iter().zip(expected_batches.iter()) { + assert_eq!(batch.len(), expected_batch_size); + for msg_to_refine in batch { + assert_eq!(msg_to_refine.0.len(), payload_sizes[idx]); + idx += 1; + } + } + }, + Err(_) => { + matches!(maybe_batches, Err(SubstrateError::Custom(_))); + }, + } + } + + #[test] + fn test_split_msgs_to_refine() { + let max_extrinsic_size = Rococo::max_extrinsic_size() as usize; + + // Check that an error is returned when one of the messages is too big. + check_split_msgs_to_refine(vec![max_extrinsic_size], Err(())); + check_split_msgs_to_refine(vec![50, 100, max_extrinsic_size, 200], Err(())); + + // Otherwise check that the split is valid. + check_split_msgs_to_refine(vec![100, 200, 300, 400], Ok(vec![4])); + check_split_msgs_to_refine( + vec![ + 50, + 100, + max_extrinsic_size - 500, + 500, + 1000, + 1500, + max_extrinsic_size - 3500, + 5000, + 10000, + ], + Ok(vec![3, 4, 2]), + ); + check_split_msgs_to_refine( + vec![ + 50, + 100, + max_extrinsic_size - 150, + 500, + 1000, + 1500, + max_extrinsic_size - 3000, + 5000, + 10000, + ], + Ok(vec![2, 1, 3, 1, 2]), + ); + check_split_msgs_to_refine( + vec![ + 5000, + 10000, + max_extrinsic_size - 3500, + 500, + 1000, + 1500, + max_extrinsic_size - 500, + 50, + 100, + ], + Ok(vec![2, 4, 3]), + ); + } +} diff --git a/relays/lib-substrate-relay/src/messages_target.rs b/relays/lib-substrate-relay/src/messages_target.rs new file mode 100644 index 00000000000..68b68b19936 --- /dev/null +++ b/relays/lib-substrate-relay/src/messages_target.rs @@ -0,0 +1,311 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Substrate client as Substrate messages target. The chain we connect to should have +//! runtime that implements `HeaderApi` to allow bridging with +//! `` chain. + +use crate::{ + messages_lane::{ + BatchProofTransaction, MessageLaneAdapter, ReceiveMessagesProofCallBuilder, + SubstrateMessageLane, + }, + messages_source::{ensure_messages_pallet_active, read_client_state, SubstrateMessagesProof}, + on_demand::OnDemandRelay, + TransactionParams, +}; + +use async_std::sync::Arc; +use async_trait::async_trait; +use bp_messages::{ + storage_keys::inbound_lane_data_key, total_unrewarded_messages, InboundLaneData, LaneId, + MessageNonce, UnrewardedRelayersState, +}; +use bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof; +use messages_relay::{ + message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, + message_lane_loop::{NoncesSubmitArtifacts, TargetClient, TargetClientState}, +}; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, BalanceOf, CallOf, ChainWithMessages, Client, + Error as SubstrateError, HashOf, TransactionEra, TransactionTracker, UnsignedTransaction, +}; +use relay_utils::relay_loop::Client as RelayClient; +use sp_core::Pair; +use std::{collections::VecDeque, convert::TryFrom, ops::RangeInclusive}; + +/// Message receiving proof returned by the target Substrate node. +pub type SubstrateMessagesDeliveryProof = + (UnrewardedRelayersState, FromBridgedChainMessagesDeliveryProof>); + +/// Substrate client as Substrate messages target. +pub struct SubstrateMessagesTarget { + target_client: Client, + source_client: Client, + lane_id: LaneId, + relayer_id_at_source: AccountIdOf, + transaction_params: TransactionParams>, + source_to_target_headers_relay: Option>>, +} + +impl SubstrateMessagesTarget

{ + /// Create new Substrate headers target. + pub fn new( + target_client: Client, + source_client: Client, + lane_id: LaneId, + relayer_id_at_source: AccountIdOf, + transaction_params: TransactionParams>, + source_to_target_headers_relay: Option< + Arc>, + >, + ) -> Self { + SubstrateMessagesTarget { + target_client, + source_client, + lane_id, + relayer_id_at_source, + transaction_params, + source_to_target_headers_relay, + } + } + + /// Read inbound lane state from the on-chain storage at given block. + async fn inbound_lane_data( + &self, + id: TargetHeaderIdOf>, + ) -> Result>>, SubstrateError> { + self.target_client + .storage_value( + inbound_lane_data_key( + P::SourceChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + ), + Some(id.1), + ) + .await + } + + /// Ensure that the messages pallet at target chain is active. + async fn ensure_pallet_active(&self) -> Result<(), SubstrateError> { + ensure_messages_pallet_active::(&self.target_client).await + } +} + +impl Clone for SubstrateMessagesTarget

{ + fn clone(&self) -> Self { + Self { + target_client: self.target_client.clone(), + source_client: self.source_client.clone(), + lane_id: self.lane_id, + relayer_id_at_source: self.relayer_id_at_source.clone(), + transaction_params: self.transaction_params.clone(), + source_to_target_headers_relay: self.source_to_target_headers_relay.clone(), + } + } +} + +#[async_trait] +impl RelayClient for SubstrateMessagesTarget

{ + type Error = SubstrateError; + + async fn reconnect(&mut self) -> Result<(), SubstrateError> { + // since the client calls RPC methods on both sides, we need to reconnect both + self.target_client.reconnect().await?; + self.source_client.reconnect().await?; + + // call reconnect on on-demand headers relay, because we may use different chains there + // and the error that has lead to reconnect may have came from those other chains + // (see `require_source_header_on_target`) + // + // this may lead to multiple reconnects to the same node during the same call and it + // needs to be addressed in the future + // TODO: https://github.com/paritytech/parity-bridges-common/issues/1928 + if let Some(ref mut source_to_target_headers_relay) = self.source_to_target_headers_relay { + source_to_target_headers_relay.reconnect().await?; + } + + Ok(()) + } +} + +#[async_trait] +impl TargetClient> for SubstrateMessagesTarget

+where + AccountIdOf: From< as Pair>::Public>, + BalanceOf: TryFrom>, +{ + type BatchTransaction = + BatchProofTransaction; + type TransactionTracker = TransactionTracker>; + + async fn state(&self) -> Result>, SubstrateError> { + // we can't continue to deliver confirmations if source node is out of sync, because + // it may have already received confirmations that we're going to deliver + // + // we can't continue to deliver messages if target node is out of sync, because + // it may have already received (some of) messages that we're going to deliver + self.source_client.ensure_synced().await?; + self.target_client.ensure_synced().await?; + // we can't relay messages if messages pallet at target chain is halted + self.ensure_pallet_active().await?; + + read_client_state(&self.target_client, Some(&self.source_client)).await + } + + async fn latest_received_nonce( + &self, + id: TargetHeaderIdOf>, + ) -> Result<(TargetHeaderIdOf>, MessageNonce), SubstrateError> { + // lane data missing from the storage is fine until first message is received + let latest_received_nonce = self + .inbound_lane_data(id) + .await? + .map(|data| data.last_delivered_nonce()) + .unwrap_or(0); + Ok((id, latest_received_nonce)) + } + + async fn latest_confirmed_received_nonce( + &self, + id: TargetHeaderIdOf>, + ) -> Result<(TargetHeaderIdOf>, MessageNonce), SubstrateError> { + // lane data missing from the storage is fine until first message is received + let last_confirmed_nonce = self + .inbound_lane_data(id) + .await? + .map(|data| data.last_confirmed_nonce) + .unwrap_or(0); + Ok((id, last_confirmed_nonce)) + } + + async fn unrewarded_relayers_state( + &self, + id: TargetHeaderIdOf>, + ) -> Result<(TargetHeaderIdOf>, UnrewardedRelayersState), SubstrateError> + { + let inbound_lane_data = self.inbound_lane_data(id).await?; + let last_delivered_nonce = + inbound_lane_data.as_ref().map(|data| data.last_delivered_nonce()).unwrap_or(0); + let relayers = inbound_lane_data.map(|data| data.relayers).unwrap_or_else(VecDeque::new); + let unrewarded_relayers_state = bp_messages::UnrewardedRelayersState { + unrewarded_relayer_entries: relayers.len() as _, + messages_in_oldest_entry: relayers + .front() + .map(|entry| 1 + entry.messages.end - entry.messages.begin) + .unwrap_or(0), + total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX), + last_delivered_nonce, + }; + Ok((id, unrewarded_relayers_state)) + } + + async fn prove_messages_receiving( + &self, + id: TargetHeaderIdOf>, + ) -> Result< + ( + TargetHeaderIdOf>, + as MessageLane>::MessagesReceivingProof, + ), + SubstrateError, + > { + let (id, relayers_state) = self.unrewarded_relayers_state(id).await?; + let inbound_data_key = bp_messages::storage_keys::inbound_lane_data_key( + P::SourceChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + ); + let proof = self + .target_client + .prove_storage(vec![inbound_data_key], id.1) + .await? + .into_iter_nodes() + .collect(); + let proof = FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: id.1, + storage_proof: proof, + lane: self.lane_id, + }; + Ok((id, (relayers_state, proof))) + } + + async fn submit_messages_proof( + &self, + maybe_batch_tx: Option, + _generated_at_header: SourceHeaderIdOf>, + nonces: RangeInclusive, + proof: as MessageLane>::MessagesProof, + ) -> Result, SubstrateError> { + let messages_proof_call = make_messages_delivery_call::

( + self.relayer_id_at_source.clone(), + proof.1.nonces_start..=proof.1.nonces_end, + proof, + maybe_batch_tx.is_none(), + ); + let final_call = match maybe_batch_tx { + Some(batch_tx) => batch_tx.append_call_and_build(messages_proof_call), + None => messages_proof_call, + }; + + let transaction_params = self.transaction_params.clone(); + let tx_tracker = self + .target_client + .submit_and_watch_signed_extrinsic( + &self.transaction_params.signer, + move |best_block_id, transaction_nonce| { + Ok(UnsignedTransaction::new(final_call.into(), transaction_nonce) + .era(TransactionEra::new(best_block_id, transaction_params.mortality))) + }, + ) + .await?; + Ok(NoncesSubmitArtifacts { nonces, tx_tracker }) + } + + async fn require_source_header_on_target( + &self, + id: SourceHeaderIdOf>, + ) -> Result, SubstrateError> { + if let Some(ref source_to_target_headers_relay) = self.source_to_target_headers_relay { + if let Some(batch_tx) = + BatchProofTransaction::new(source_to_target_headers_relay.clone(), id.0).await? + { + return Ok(Some(batch_tx)) + } + + source_to_target_headers_relay.require_more_headers(id.0).await; + } + + Ok(None) + } +} + +/// Make messages delivery call from given proof. +fn make_messages_delivery_call( + relayer_id_at_source: AccountIdOf, + nonces: RangeInclusive, + proof: SubstrateMessagesProof, + trace_call: bool, +) -> CallOf { + let messages_count = nonces.end() - nonces.start() + 1; + let dispatch_weight = proof.0; + P::ReceiveMessagesProofCallBuilder::build_receive_messages_proof_call( + relayer_id_at_source, + proof, + messages_count as _, + dispatch_weight, + trace_call, + ) +} diff --git a/relays/lib-substrate-relay/src/on_demand/headers.rs b/relays/lib-substrate-relay/src/on_demand/headers.rs new file mode 100644 index 00000000000..aded79a857a --- /dev/null +++ b/relays/lib-substrate-relay/src/on_demand/headers.rs @@ -0,0 +1,511 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! On-demand Substrate -> Substrate header finality relay. + +use crate::finality::SubmitFinalityProofCallBuilder; + +use async_std::sync::{Arc, Mutex}; +use async_trait::async_trait; +use bp_header_chain::ConsensusLogReader; +use bp_runtime::HeaderIdProvider; +use futures::{select, FutureExt}; +use num_traits::{One, Zero}; +use sp_runtime::traits::Header; + +use finality_relay::{FinalitySyncParams, TargetClient as FinalityTargetClient}; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, Error as SubstrateError, + HeaderIdOf, +}; +use relay_utils::{ + metrics::MetricsParams, relay_loop::Client as RelayClient, FailedClient, MaybeConnectionError, + STALL_TIMEOUT, +}; + +use crate::{ + finality::{ + engine::Engine, + source::{RequiredHeaderNumberRef, SubstrateFinalitySource}, + target::SubstrateFinalityTarget, + SubstrateFinalitySyncPipeline, RECENT_FINALITY_PROOFS_LIMIT, + }, + on_demand::OnDemandRelay, + TransactionParams, +}; + +/// On-demand Substrate <-> Substrate header finality relay. +/// +/// This relay may be requested to sync more headers, whenever some other relay (e.g. messages +/// relay) needs it to continue its regular work. When enough headers are relayed, on-demand stops +/// syncing headers. +#[derive(Clone)] +pub struct OnDemandHeadersRelay { + /// Relay task name. + relay_task_name: String, + /// Shared reference to maximal required finalized header number. + required_header_number: RequiredHeaderNumberRef, + /// Client of the source chain. + source_client: Client, + /// Client of the target chain. + target_client: Client, +} + +impl OnDemandHeadersRelay

{ + /// Create new on-demand headers relay. + /// + /// If `metrics_params` is `Some(_)`, the metrics of the finality relay are registered. + /// Otherwise, all required metrics must be exposed outside of this method. + pub fn new( + source_client: Client, + target_client: Client, + target_transaction_params: TransactionParams>, + only_mandatory_headers: bool, + metrics_params: Option, + ) -> Self + where + AccountIdOf: + From< as sp_core::Pair>::Public>, + { + let required_header_number = Arc::new(Mutex::new(Zero::zero())); + let this = OnDemandHeadersRelay { + relay_task_name: on_demand_headers_relay_name::(), + required_header_number: required_header_number.clone(), + source_client: source_client.clone(), + target_client: target_client.clone(), + }; + async_std::task::spawn(async move { + background_task::

( + source_client, + target_client, + target_transaction_params, + only_mandatory_headers, + required_header_number, + metrics_params, + ) + .await; + }); + + this + } +} + +#[async_trait] +impl OnDemandRelay + for OnDemandHeadersRelay

+{ + async fn reconnect(&self) -> Result<(), SubstrateError> { + // using clone is fine here (to avoid mut requirement), because clone on Client clones + // internal references + self.source_client.clone().reconnect().await?; + self.target_client.clone().reconnect().await + } + + async fn require_more_headers(&self, required_header: BlockNumberOf) { + let mut required_header_number = self.required_header_number.lock().await; + if required_header > *required_header_number { + log::trace!( + target: "bridge", + "[{}] More {} headers required. Going to sync up to the {}", + self.relay_task_name, + P::SourceChain::NAME, + required_header, + ); + + *required_header_number = required_header; + } + } + + async fn prove_header( + &self, + required_header: BlockNumberOf, + ) -> Result<(HeaderIdOf, Vec>), SubstrateError> { + // first find proper header (either `required_header`) or its descendant + let finality_source = SubstrateFinalitySource::

::new(self.source_client.clone(), None); + let (header, proof) = finality_source.prove_block_finality(required_header).await?; + let header_id = header.id(); + + // optimize justification before including it into the call + let proof = P::FinalityEngine::optimize_proof(&self.target_client, &header, proof).await?; + + log::debug!( + target: "bridge", + "[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?}", + self.relay_task_name, + P::SourceChain::NAME, + required_header, + P::SourceChain::NAME, + header_id, + ); + + // and then craft the submit-proof call + let call = + P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof); + + Ok((header_id, vec![call])) + } +} + +/// Background task that is responsible for starting headers relay. +async fn background_task( + source_client: Client, + target_client: Client, + target_transaction_params: TransactionParams>, + only_mandatory_headers: bool, + required_header_number: RequiredHeaderNumberRef, + metrics_params: Option, +) where + AccountIdOf: From< as sp_core::Pair>::Public>, +{ + let relay_task_name = on_demand_headers_relay_name::(); + let target_transactions_mortality = target_transaction_params.mortality; + let mut finality_source = SubstrateFinalitySource::

::new( + source_client.clone(), + Some(required_header_number.clone()), + ); + let mut finality_target = + SubstrateFinalityTarget::new(target_client.clone(), target_transaction_params); + let mut latest_non_mandatory_at_source = Zero::zero(); + + let mut restart_relay = true; + let finality_relay_task = futures::future::Fuse::terminated(); + futures::pin_mut!(finality_relay_task); + + loop { + select! { + _ = async_std::task::sleep(P::TargetChain::AVERAGE_BLOCK_INTERVAL).fuse() => {}, + _ = finality_relay_task => { + // this should never happen in practice given the current code + restart_relay = true; + }, + } + + // read best finalized source header number from source + let best_finalized_source_header_at_source = + best_finalized_source_header_at_source(&finality_source, &relay_task_name).await; + if matches!(best_finalized_source_header_at_source, Err(ref e) if e.is_connection_error()) { + relay_utils::relay_loop::reconnect_failed_client( + FailedClient::Source, + relay_utils::relay_loop::RECONNECT_DELAY, + &mut finality_source, + &mut finality_target, + ) + .await; + continue + } + + // read best finalized source header number from target + let best_finalized_source_header_at_target = + best_finalized_source_header_at_target::

(&finality_target, &relay_task_name).await; + if matches!(best_finalized_source_header_at_target, Err(ref e) if e.is_connection_error()) { + relay_utils::relay_loop::reconnect_failed_client( + FailedClient::Target, + relay_utils::relay_loop::RECONNECT_DELAY, + &mut finality_source, + &mut finality_target, + ) + .await; + continue + } + + // submit mandatory header if some headers are missing + let best_finalized_source_header_at_source_fmt = + format!("{best_finalized_source_header_at_source:?}"); + let best_finalized_source_header_at_target_fmt = + format!("{best_finalized_source_header_at_target:?}"); + let required_header_number_value = *required_header_number.lock().await; + let mandatory_scan_range = mandatory_headers_scan_range::( + best_finalized_source_header_at_source.ok(), + best_finalized_source_header_at_target.ok(), + required_header_number_value, + ) + .await; + + log::trace!( + target: "bridge", + "[{}] Mandatory headers scan range: ({:?}, {:?}, {:?}) -> {:?}", + relay_task_name, + required_header_number_value, + best_finalized_source_header_at_source_fmt, + best_finalized_source_header_at_target_fmt, + mandatory_scan_range, + ); + + if let Some(mandatory_scan_range) = mandatory_scan_range { + let relay_mandatory_header_result = relay_mandatory_header_from_range( + &finality_source, + &required_header_number, + best_finalized_source_header_at_target_fmt, + ( + std::cmp::max(mandatory_scan_range.0, latest_non_mandatory_at_source), + mandatory_scan_range.1, + ), + &relay_task_name, + ) + .await; + match relay_mandatory_header_result { + Ok(true) => (), + Ok(false) => { + // there are no (or we don't need to relay them) mandatory headers in the range + // => to avoid scanning the same headers over and over again, remember that + latest_non_mandatory_at_source = mandatory_scan_range.1; + + log::trace!( + target: "bridge", + "[{}] No mandatory {} headers in the range {:?}", + relay_task_name, + P::SourceChain::NAME, + mandatory_scan_range, + ); + }, + Err(e) => { + log::warn!( + target: "bridge", + "[{}] Failed to scan mandatory {} headers range ({:?}): {:?}", + relay_task_name, + P::SourceChain::NAME, + mandatory_scan_range, + e, + ); + + if e.is_connection_error() { + relay_utils::relay_loop::reconnect_failed_client( + FailedClient::Source, + relay_utils::relay_loop::RECONNECT_DELAY, + &mut finality_source, + &mut finality_target, + ) + .await; + continue + } + }, + } + } + + // start/restart relay + if restart_relay { + let stall_timeout = relay_substrate_client::transaction_stall_timeout( + target_transactions_mortality, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + STALL_TIMEOUT, + ); + + log::info!( + target: "bridge", + "[{}] Starting on-demand headers relay task\n\t\ + Only mandatory headers: {}\n\t\ + Tx mortality: {:?} (~{}m)\n\t\ + Stall timeout: {:?}", + relay_task_name, + only_mandatory_headers, + target_transactions_mortality, + stall_timeout.as_secs_f64() / 60.0f64, + stall_timeout, + ); + + finality_relay_task.set( + finality_relay::run( + finality_source.clone(), + finality_target.clone(), + FinalitySyncParams { + tick: std::cmp::max( + P::SourceChain::AVERAGE_BLOCK_INTERVAL, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + ), + recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT, + stall_timeout, + only_mandatory_headers, + }, + metrics_params.clone().unwrap_or_else(MetricsParams::disabled), + futures::future::pending(), + ) + .fuse(), + ); + + restart_relay = false; + } + } +} + +/// Returns `Some()` with inclusive range of headers which must be scanned for mandatory headers +/// and the first of such headers must be submitted to the target node. +async fn mandatory_headers_scan_range( + best_finalized_source_header_at_source: Option, + best_finalized_source_header_at_target: Option, + required_header_number: BlockNumberOf, +) -> Option<(C::BlockNumber, C::BlockNumber)> { + // if we have been unable to read header number from the target, then let's assume + // that it is the same as required header number. Otherwise we risk submitting + // unneeded transactions + let best_finalized_source_header_at_target = + best_finalized_source_header_at_target.unwrap_or(required_header_number); + + // if we have been unable to read header number from the source, then let's assume + // that it is the same as at the target + let best_finalized_source_header_at_source = + best_finalized_source_header_at_source.unwrap_or(best_finalized_source_header_at_target); + + // if relay is already asked to sync more headers than we have at source, don't do anything yet + if required_header_number >= best_finalized_source_header_at_source { + return None + } + + Some(( + best_finalized_source_header_at_target + One::one(), + best_finalized_source_header_at_source, + )) +} + +/// Try to find mandatory header in the inclusive headers range and, if one is found, ask to relay +/// it. +/// +/// Returns `true` if header was found and (asked to be) relayed and `false` otherwise. +async fn relay_mandatory_header_from_range( + finality_source: &SubstrateFinalitySource

, + required_header_number: &RequiredHeaderNumberRef, + best_finalized_source_header_at_target: String, + range: (BlockNumberOf, BlockNumberOf), + relay_task_name: &str, +) -> Result { + // search for mandatory header first + let mandatory_source_header_number = + find_mandatory_header_in_range(finality_source, range).await?; + + // if there are no mandatory headers - we have nothing to do + let mandatory_source_header_number = match mandatory_source_header_number { + Some(mandatory_source_header_number) => mandatory_source_header_number, + None => return Ok(false), + }; + + // `find_mandatory_header` call may take a while => check if `required_header_number` is still + // less than our `mandatory_source_header_number` before logging anything + let mut required_header_number = required_header_number.lock().await; + if *required_header_number >= mandatory_source_header_number { + return Ok(false) + } + + log::trace!( + target: "bridge", + "[{}] Too many {} headers missing at target ({} vs {}). Going to sync up to the mandatory {}", + relay_task_name, + P::SourceChain::NAME, + best_finalized_source_header_at_target, + range.1, + mandatory_source_header_number, + ); + + *required_header_number = mandatory_source_header_number; + Ok(true) +} + +/// Read best finalized source block number from source client. +/// +/// Returns `None` if we have failed to read the number. +async fn best_finalized_source_header_at_source( + finality_source: &SubstrateFinalitySource

, + relay_task_name: &str, +) -> Result, relay_substrate_client::Error> { + finality_source.on_chain_best_finalized_block_number().await.map_err(|error| { + log::error!( + target: "bridge", + "[{}] Failed to read best finalized source header from source: {:?}", + relay_task_name, + error, + ); + + error + }) +} + +/// Read best finalized source block number from target client. +/// +/// Returns `None` if we have failed to read the number. +async fn best_finalized_source_header_at_target( + finality_target: &SubstrateFinalityTarget

, + relay_task_name: &str, +) -> Result, as RelayClient>::Error> +where + AccountIdOf: From< as sp_core::Pair>::Public>, +{ + finality_target + .best_finalized_source_block_id() + .await + .map_err(|error| { + log::error!( + target: "bridge", + "[{}] Failed to read best finalized source header from target: {:?}", + relay_task_name, + error, + ); + + error + }) + .map(|id| id.0) +} + +/// Read first mandatory header in given inclusive range. +/// +/// Returns `Ok(None)` if there were no mandatory headers in the range. +async fn find_mandatory_header_in_range( + finality_source: &SubstrateFinalitySource

, + range: (BlockNumberOf, BlockNumberOf), +) -> Result>, relay_substrate_client::Error> { + let mut current = range.0; + while current <= range.1 { + let header = finality_source.client().header_by_number(current).await?; + if >::ConsensusLogReader::schedules_authorities_change( + header.digest(), + ) { + return Ok(Some(current)) + } + + current += One::one(); + } + + Ok(None) +} + +/// On-demand headers relay task name. +fn on_demand_headers_relay_name() -> String { + format!("{}-to-{}-on-demand-headers", SourceChain::NAME, TargetChain::NAME) +} + +#[cfg(test)] +mod tests { + use super::*; + + type TestChain = relay_rococo_client::Rococo; + + const AT_SOURCE: Option = Some(10); + const AT_TARGET: Option = Some(1); + + #[async_std::test] + async fn mandatory_headers_scan_range_selects_range_if_some_headers_are_missing() { + assert_eq!( + mandatory_headers_scan_range::(AT_SOURCE, AT_TARGET, 0,).await, + Some((AT_TARGET.unwrap() + 1, AT_SOURCE.unwrap())), + ); + } + + #[async_std::test] + async fn mandatory_headers_scan_range_selects_nothing_if_already_queued() { + assert_eq!( + mandatory_headers_scan_range::(AT_SOURCE, AT_TARGET, AT_SOURCE.unwrap(),) + .await, + None, + ); + } +} diff --git a/relays/lib-substrate-relay/src/on_demand/mod.rs b/relays/lib-substrate-relay/src/on_demand/mod.rs new file mode 100644 index 00000000000..00bb33d6740 --- /dev/null +++ b/relays/lib-substrate-relay/src/on_demand/mod.rs @@ -0,0 +1,48 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types and functions intended to ease adding of new Substrate -> Substrate +//! on-demand pipelines. + +use async_trait::async_trait; +use relay_substrate_client::{BlockNumberOf, CallOf, Chain, Error as SubstrateError, HeaderIdOf}; + +pub mod headers; +pub mod parachains; + +/// On-demand headers relay that is relaying finalizing headers only when requested. +#[async_trait] +pub trait OnDemandRelay: Send + Sync { + /// Reconnect to source and target nodes. + async fn reconnect(&self) -> Result<(), SubstrateError>; + + /// Ask relay to relay source header with given number to the target chain. + /// + /// Depending on implementation, on-demand relay may also relay `required_header` ancestors + /// (e.g. if they're mandatory), or its descendants. The request is considered complete if + /// the best avbailable header at the target chain has number that is larger than or equal + /// to the `required_header`. + async fn require_more_headers(&self, required_header: BlockNumberOf); + + /// Ask relay to prove source `required_header` to the `TargetChain`. + /// + /// Returns number of header that is proved (it may be the `required_header` or one of its + /// descendants) and calls for delivering the proof. + async fn prove_header( + &self, + required_header: BlockNumberOf, + ) -> Result<(HeaderIdOf, Vec>), SubstrateError>; +} diff --git a/relays/lib-substrate-relay/src/on_demand/parachains.rs b/relays/lib-substrate-relay/src/on_demand/parachains.rs new file mode 100644 index 00000000000..4c205770d4a --- /dev/null +++ b/relays/lib-substrate-relay/src/on_demand/parachains.rs @@ -0,0 +1,1033 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! On-demand Substrate -> Substrate parachain finality relay. + +use crate::{ + messages_source::best_finalized_peer_header_at_self, + on_demand::OnDemandRelay, + parachains::{ + source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter, + SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, + }, + TransactionParams, +}; + +use async_std::{ + channel::{unbounded, Receiver, Sender}, + sync::{Arc, Mutex}, +}; +use async_trait::async_trait; +use bp_polkadot_core::parachains::{ParaHash, ParaId}; +use bp_runtime::HeaderIdProvider; +use futures::{select, FutureExt}; +use num_traits::Zero; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use parachains_relay::parachains_loop::{AvailableHeader, SourceClient, TargetClient}; +use relay_substrate_client::{ + is_ancient_block, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, + Error as SubstrateError, HashOf, HeaderIdOf, ParachainBase, +}; +use relay_utils::{ + metrics::MetricsParams, relay_loop::Client as RelayClient, BlockNumberBase, FailedClient, + HeaderId, UniqueSaturatedInto, +}; +use std::fmt::Debug; + +/// On-demand Substrate <-> Substrate parachain finality relay. +/// +/// This relay may be requested to sync more parachain headers, whenever some other relay +/// (e.g. messages relay) needs it to continue its regular work. When enough parachain headers +/// are relayed, on-demand stops syncing headers. +#[derive(Clone)] +pub struct OnDemandParachainsRelay { + /// Relay task name. + relay_task_name: String, + /// Channel used to communicate with background task and ask for relay of parachain heads. + required_header_number_sender: Sender>, + /// Source relay chain client. + source_relay_client: Client, + /// Target chain client. + target_client: Client, + /// On-demand relay chain relay. + on_demand_source_relay_to_target_headers: + Arc>, +} + +impl OnDemandParachainsRelay

{ + /// Create new on-demand parachains relay. + /// + /// Note that the argument is the source relay chain client, not the parachain client. + /// That's because parachain finality is determined by the relay chain and we don't + /// need to connect to the parachain itself here. + pub fn new( + source_relay_client: Client, + target_client: Client, + target_transaction_params: TransactionParams>, + on_demand_source_relay_to_target_headers: Arc< + dyn OnDemandRelay, + >, + ) -> Self + where + P::SourceParachain: Chain, + P::SourceRelayChain: + Chain, + AccountIdOf: + From< as sp_core::Pair>::Public>, + { + let (required_header_number_sender, required_header_number_receiver) = unbounded(); + let this = OnDemandParachainsRelay { + relay_task_name: on_demand_parachains_relay_name::( + ), + required_header_number_sender, + source_relay_client: source_relay_client.clone(), + target_client: target_client.clone(), + on_demand_source_relay_to_target_headers: on_demand_source_relay_to_target_headers + .clone(), + }; + async_std::task::spawn(async move { + background_task::

( + source_relay_client, + target_client, + target_transaction_params, + on_demand_source_relay_to_target_headers, + required_header_number_receiver, + ) + .await; + }); + + this + } +} + +#[async_trait] +impl OnDemandRelay + for OnDemandParachainsRelay

+where + P::SourceParachain: Chain, +{ + async fn reconnect(&self) -> Result<(), SubstrateError> { + // using clone is fine here (to avoid mut requirement), because clone on Client clones + // internal references + self.source_relay_client.clone().reconnect().await?; + self.target_client.clone().reconnect().await?; + // we'll probably need to reconnect relay chain relayer clients also + self.on_demand_source_relay_to_target_headers.reconnect().await + } + + async fn require_more_headers(&self, required_header: BlockNumberOf) { + if let Err(e) = self.required_header_number_sender.send(required_header).await { + log::trace!( + target: "bridge", + "[{}] Failed to request {} header {:?}: {:?}", + self.relay_task_name, + P::SourceParachain::NAME, + required_header, + e, + ); + } + } + + /// Ask relay to prove source `required_header` to the `TargetChain`. + async fn prove_header( + &self, + required_parachain_header: BlockNumberOf, + ) -> Result<(HeaderIdOf, Vec>), SubstrateError> { + // select headers to prove + let parachains_source = ParachainsSource::

::new( + self.source_relay_client.clone(), + Arc::new(Mutex::new(AvailableHeader::Missing)), + ); + let env = (self, ¶chains_source); + let (need_to_prove_relay_block, selected_relay_block, selected_parachain_block) = + select_headers_to_prove(env, required_parachain_header).await?; + + log::debug!( + target: "bridge", + "[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?} and {} head {:?}", + self.relay_task_name, + P::SourceParachain::NAME, + required_parachain_header, + P::SourceParachain::NAME, + selected_parachain_block, + P::SourceRelayChain::NAME, + if need_to_prove_relay_block { + Some(selected_relay_block) + } else { + None + }, + ); + + // now let's prove relay chain block (if needed) + let mut calls = Vec::new(); + let mut proved_relay_block = selected_relay_block; + if need_to_prove_relay_block { + let (relay_block, relay_prove_call) = self + .on_demand_source_relay_to_target_headers + .prove_header(selected_relay_block.number()) + .await?; + proved_relay_block = relay_block; + calls.extend(relay_prove_call); + } + + // despite what we've selected before (in `select_headers_to_prove` call), if headers relay + // have chose the different header (e.g. because there's no GRANDPA jusstification for it), + // we need to prove parachain head available at this header + let para_id = ParaId(P::SourceParachain::PARACHAIN_ID); + let mut proved_parachain_block = selected_parachain_block; + if proved_relay_block != selected_relay_block { + proved_parachain_block = parachains_source + .on_chain_para_head_id(proved_relay_block) + .await? + // this could happen e.g. if parachain has been offboarded? + .ok_or_else(|| { + SubstrateError::MissingRequiredParachainHead( + para_id, + proved_relay_block.number().unique_saturated_into(), + ) + })?; + + log::debug!( + target: "bridge", + "[{}] Selected to prove {} head {:?} and {} head {:?}. Instead proved {} head {:?} and {} head {:?}", + self.relay_task_name, + P::SourceParachain::NAME, + selected_parachain_block, + P::SourceRelayChain::NAME, + selected_relay_block, + P::SourceParachain::NAME, + proved_parachain_block, + P::SourceRelayChain::NAME, + proved_relay_block, + ); + } + + // and finally - prove parachain head + let (para_proof, para_hash) = + parachains_source.prove_parachain_head(proved_relay_block).await?; + calls.push(P::SubmitParachainHeadsCallBuilder::build_submit_parachain_heads_call( + proved_relay_block, + vec![(para_id, para_hash)], + para_proof, + )); + + Ok((proved_parachain_block, calls)) + } +} + +/// Background task that is responsible for starting parachain headers relay. +async fn background_task( + source_relay_client: Client, + target_client: Client, + target_transaction_params: TransactionParams>, + on_demand_source_relay_to_target_headers: Arc< + dyn OnDemandRelay, + >, + required_parachain_header_number_receiver: Receiver>, +) where + P::SourceParachain: Chain, + P::SourceRelayChain: + Chain, + AccountIdOf: From< as sp_core::Pair>::Public>, +{ + let relay_task_name = on_demand_parachains_relay_name::(); + let target_transactions_mortality = target_transaction_params.mortality; + + let mut relay_state = RelayState::Idle; + let mut required_parachain_header_number = Zero::zero(); + let required_para_header_ref = Arc::new(Mutex::new(AvailableHeader::Unavailable)); + + let mut restart_relay = true; + let parachains_relay_task = futures::future::Fuse::terminated(); + futures::pin_mut!(parachains_relay_task); + + let mut parachains_source = + ParachainsSource::

::new(source_relay_client.clone(), required_para_header_ref.clone()); + let mut parachains_target = + ParachainsTarget::

::new(target_client.clone(), target_transaction_params.clone()); + + loop { + select! { + new_required_parachain_header_number = required_parachain_header_number_receiver.recv().fuse() => { + let new_required_parachain_header_number = match new_required_parachain_header_number { + Ok(new_required_parachain_header_number) => new_required_parachain_header_number, + Err(e) => { + log::error!( + target: "bridge", + "[{}] Background task has exited with error: {:?}", + relay_task_name, + e, + ); + + return; + }, + }; + + // keep in mind that we are not updating `required_para_header_ref` here, because + // then we'll be submitting all previous headers as well (while required relay headers are + // delivered) and we want to avoid that (to reduce cost) + if new_required_parachain_header_number > required_parachain_header_number { + log::trace!( + target: "bridge", + "[{}] More {} headers required. Going to sync up to the {}", + relay_task_name, + P::SourceParachain::NAME, + new_required_parachain_header_number, + ); + + required_parachain_header_number = new_required_parachain_header_number; + } + }, + _ = async_std::task::sleep(P::TargetChain::AVERAGE_BLOCK_INTERVAL).fuse() => {}, + _ = parachains_relay_task => { + // this should never happen in practice given the current code + restart_relay = true; + }, + } + + // the workflow of the on-demand parachains relay is: + // + // 1) message relay (or any other dependent relay) sees new message at parachain header + // `PH`; + // + // 2) it sees that the target chain does not know `PH`; + // + // 3) it asks on-demand parachains relay to relay `PH` to the target chain; + // + // Phase#1: relaying relay chain header + // + // 4) on-demand parachains relay waits for GRANDPA-finalized block of the source relay chain + // `RH` that is storing `PH` or its descendant. Let it be `PH'`; + // 5) it asks on-demand headers relay to relay `RH` to the target chain; + // 6) it waits until `RH` (or its descendant) is relayed to the target chain; + // + // Phase#2: relaying parachain header + // + // 7) on-demand parachains relay sets `ParachainsSource::maximal_header_number` to the + // `PH'.number()`. + // 8) parachains finality relay sees that the parachain head has been + // updated and relays `PH'` to the target chain. + + // select headers to relay + let relay_data = read_relay_data( + ¶chains_source, + ¶chains_target, + required_parachain_header_number, + ) + .await; + match relay_data { + Ok(relay_data) => { + let prev_relay_state = relay_state; + relay_state = select_headers_to_relay(&relay_data, relay_state); + log::trace!( + target: "bridge", + "[{}] Selected new relay state: {:?} using old state {:?} and data {:?}", + relay_task_name, + relay_state, + prev_relay_state, + relay_data, + ); + }, + Err(failed_client) => { + relay_utils::relay_loop::reconnect_failed_client( + failed_client, + relay_utils::relay_loop::RECONNECT_DELAY, + &mut parachains_source, + &mut parachains_target, + ) + .await; + continue + }, + } + + // we have selected our new 'state' => let's notify our source clients about our new + // requirements + match relay_state { + RelayState::Idle => (), + RelayState::RelayingRelayHeader(required_relay_header) => { + on_demand_source_relay_to_target_headers + .require_more_headers(required_relay_header) + .await; + }, + RelayState::RelayingParaHeader(required_para_header) => { + *required_para_header_ref.lock().await = + AvailableHeader::Available(required_para_header); + }, + } + + // start/restart relay + if restart_relay { + let stall_timeout = relay_substrate_client::transaction_stall_timeout( + target_transactions_mortality, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + relay_utils::STALL_TIMEOUT, + ); + + log::info!( + target: "bridge", + "[{}] Starting on-demand-parachains relay task\n\t\ + Tx mortality: {:?} (~{}m)\n\t\ + Stall timeout: {:?}", + relay_task_name, + target_transactions_mortality, + stall_timeout.as_secs_f64() / 60.0f64, + stall_timeout, + ); + + parachains_relay_task.set( + parachains_relay::parachains_loop::run( + parachains_source.clone(), + parachains_target.clone(), + MetricsParams::disabled(), + futures::future::pending(), + ) + .fuse(), + ); + + restart_relay = false; + } + } +} + +/// On-demand parachains relay task name. +fn on_demand_parachains_relay_name() -> String { + format!("{}-to-{}-on-demand-parachain", SourceChain::NAME, TargetChain::NAME) +} + +/// On-demand relay state. +#[derive(Clone, Copy, Debug, PartialEq)] +enum RelayState { + /// On-demand relay is not doing anything. + Idle, + /// Relaying given relay header to relay given parachain header later. + RelayingRelayHeader(RelayNumber), + /// Relaying given parachain header. + RelayingParaHeader(HeaderId), +} + +/// Data gathered from source and target clients, used by on-demand relay. +#[derive(Debug)] +struct RelayData { + /// Parachain header number that is required at the target chain. + pub required_para_header: ParaNumber, + /// Parachain header number, known to the target chain. + pub para_header_at_target: Option, + /// Parachain header id, known to the source (relay) chain. + pub para_header_at_source: Option>, + /// Parachain header, that is available at the source relay chain at `relay_header_at_target` + /// block. + /// + /// May be `None` if there's no `relay_header_at_target` yet, or if the + /// `relay_header_at_target` is too old and we think its state has been pruned. + pub para_header_at_relay_header_at_target: Option>, + /// Relay header number at the source chain. + pub relay_header_at_source: RelayNumber, + /// Relay header number at the target chain. + pub relay_header_at_target: Option, +} + +/// Read required data from source and target clients. +async fn read_relay_data( + source: &ParachainsSource

, + target: &ParachainsTarget

, + required_header_number: BlockNumberOf, +) -> Result< + RelayData< + HashOf, + BlockNumberOf, + BlockNumberOf, + >, + FailedClient, +> +where + ParachainsTarget

: + TargetClient> + RelayClient, +{ + let map_target_err = |e| { + log::error!( + target: "bridge", + "[{}] Failed to read relay data from {} client: {:?}", + on_demand_parachains_relay_name::(), + P::TargetChain::NAME, + e, + ); + FailedClient::Target + }; + let map_source_err = |e| { + log::error!( + target: "bridge", + "[{}] Failed to read relay data from {} client: {:?}", + on_demand_parachains_relay_name::(), + P::SourceRelayChain::NAME, + e, + ); + FailedClient::Source + }; + + let best_target_block_hash = target.best_block().await.map_err(map_target_err)?.1; + let para_header_at_target = best_finalized_peer_header_at_self::< + P::TargetChain, + P::SourceParachain, + >(target.client(), best_target_block_hash) + .await; + // if there are no parachain heads at the target (`NoParachainHeadAtTarget`), we'll need to + // submit at least one. Otherwise the pallet will be treated as uninitialized and messages + // sync will stall. + let para_header_at_target = match para_header_at_target { + Ok(Some(para_header_at_target)) => Some(para_header_at_target.0), + Ok(None) => None, + Err(e) => return Err(map_target_err(e)), + }; + + let best_finalized_relay_header = + source.client().best_finalized_header().await.map_err(map_source_err)?; + let best_finalized_relay_block_id = best_finalized_relay_header.id(); + let para_header_at_source = source + .on_chain_para_head_id(best_finalized_relay_block_id) + .await + .map_err(map_source_err)?; + + let relay_header_at_source = best_finalized_relay_block_id.0; + let relay_header_at_target = best_finalized_peer_header_at_self::< + P::TargetChain, + P::SourceRelayChain, + >(target.client(), best_target_block_hash) + .await + .map_err(map_target_err)?; + + // if relay header at target is too old then its state may already be discarded at the source + // => just use `None` in this case + // + // the same is for case when there's no relay header at target at all + let available_relay_header_at_target = + relay_header_at_target.filter(|relay_header_at_target| { + !is_ancient_block(relay_header_at_target.number(), relay_header_at_source) + }); + let para_header_at_relay_header_at_target = + if let Some(available_relay_header_at_target) = available_relay_header_at_target { + source + .on_chain_para_head_id(available_relay_header_at_target) + .await + .map_err(map_source_err)? + } else { + None + }; + + Ok(RelayData { + required_para_header: required_header_number, + para_header_at_target, + para_header_at_source, + relay_header_at_source, + relay_header_at_target: relay_header_at_target + .map(|relay_header_at_target| relay_header_at_target.0), + para_header_at_relay_header_at_target, + }) +} + +/// Select relay and parachain headers that need to be relayed. +fn select_headers_to_relay( + data: &RelayData, + state: RelayState, +) -> RelayState +where + ParaHash: Clone, + ParaNumber: Copy + PartialOrd + Zero, + RelayNumber: Copy + Debug + Ord, +{ + // we can't do anything until **relay chain** bridge GRANDPA pallet is not initialized at the + // target chain + let relay_header_at_target = match data.relay_header_at_target { + Some(relay_header_at_target) => relay_header_at_target, + None => return RelayState::Idle, + }; + + // Process the `RelayingRelayHeader` state. + if let &RelayState::RelayingRelayHeader(relay_header_number) = &state { + if relay_header_at_target < relay_header_number { + // The required relay header hasn't yet been relayed. Ask / wait for it. + return state + } + + // We may switch to `RelayingParaHeader` if parachain head is available. + if let Some(para_header_at_relay_header_at_target) = + data.para_header_at_relay_header_at_target.as_ref() + { + return RelayState::RelayingParaHeader(para_header_at_relay_header_at_target.clone()) + } + + // else use the regular process - e.g. we may require to deliver new relay header first + } + + // Process the `RelayingParaHeader` state. + if let RelayState::RelayingParaHeader(para_header_id) = &state { + let para_header_at_target_or_zero = data.para_header_at_target.unwrap_or_else(Zero::zero); + if para_header_at_target_or_zero < para_header_id.0 { + // The required parachain header hasn't yet been relayed. Ask / wait for it. + return state + } + } + + // if we haven't read para head from the source, we can't yet do anything + let para_header_at_source = match data.para_header_at_source { + Some(ref para_header_at_source) => para_header_at_source.clone(), + None => return RelayState::Idle, + }; + + // if we have parachain head at the source, but no parachain heads at the target, we'll need + // to deliver at least one parachain head + let (required_para_header, para_header_at_target) = match data.para_header_at_target { + Some(para_header_at_target) => (data.required_para_header, para_header_at_target), + None => (para_header_at_source.0, Zero::zero()), + }; + + // if we have already satisfied our "customer", do nothing + if required_para_header <= para_header_at_target { + return RelayState::Idle + } + + // if required header is not available even at the source chain, let's wait + if required_para_header > para_header_at_source.0 { + return RelayState::Idle + } + + // we will always try to sync latest parachain/relay header, even if we've been asked for some + // its ancestor + + // we need relay chain header first + if relay_header_at_target < data.relay_header_at_source { + return RelayState::RelayingRelayHeader(data.relay_header_at_source) + } + + // if all relay headers synced, we may start directly with parachain header + RelayState::RelayingParaHeader(para_header_at_source) +} + +/// Environment for the `select_headers_to_prove` call. +#[async_trait] +trait SelectHeadersToProveEnvironment { + /// Returns associated parachain id. + fn parachain_id(&self) -> ParaId; + /// Returns best finalized relay block. + async fn best_finalized_relay_block_at_source( + &self, + ) -> Result, SubstrateError>; + /// Returns best finalized relay block that is known at `P::TargetChain`. + async fn best_finalized_relay_block_at_target( + &self, + ) -> Result, SubstrateError>; + /// Returns best finalized parachain block at given source relay chain block. + async fn best_finalized_para_block_at_source( + &self, + at_relay_block: HeaderId, + ) -> Result>, SubstrateError>; +} + +#[async_trait] +impl<'a, P: SubstrateParachainsPipeline> + SelectHeadersToProveEnvironment< + BlockNumberOf, + HashOf, + BlockNumberOf, + HashOf, + > for (&'a OnDemandParachainsRelay

, &'a ParachainsSource

) +{ + fn parachain_id(&self) -> ParaId { + ParaId(P::SourceParachain::PARACHAIN_ID) + } + + async fn best_finalized_relay_block_at_source( + &self, + ) -> Result, SubstrateError> { + Ok(self.0.source_relay_client.best_finalized_header().await?.id()) + } + + async fn best_finalized_relay_block_at_target( + &self, + ) -> Result, SubstrateError> { + Ok(crate::messages_source::read_client_state::( + &self.0.target_client, + None, + ) + .await? + .best_finalized_peer_at_best_self + .ok_or(SubstrateError::BridgePalletIsNotInitialized)?) + } + + async fn best_finalized_para_block_at_source( + &self, + at_relay_block: HeaderIdOf, + ) -> Result>, SubstrateError> { + self.1.on_chain_para_head_id(at_relay_block).await + } +} + +/// Given request to prove `required_parachain_header`, select actual headers that need to be +/// proved. +async fn select_headers_to_prove( + env: impl SelectHeadersToProveEnvironment, + required_parachain_header: PBN, +) -> Result<(bool, HeaderId, HeaderId), SubstrateError> +where + RBH: Copy, + RBN: BlockNumberBase, + PBH: Copy, + PBN: BlockNumberBase, +{ + // parachains proof also requires relay header proof. Let's first select relay block + // number that we'll be dealing with + let best_finalized_relay_block_at_source = env.best_finalized_relay_block_at_source().await?; + let best_finalized_relay_block_at_target = env.best_finalized_relay_block_at_target().await?; + + // if we can't prove `required_header` even using `best_finalized_relay_block_at_source`, we + // can't do anything here + // (this shall not actually happen, given current code, because we only require finalized + // headers) + let best_possible_parachain_block = env + .best_finalized_para_block_at_source(best_finalized_relay_block_at_source) + .await? + .filter(|best_possible_parachain_block| { + best_possible_parachain_block.number() >= required_parachain_header + }) + .ok_or(SubstrateError::MissingRequiredParachainHead( + env.parachain_id(), + required_parachain_header.unique_saturated_into(), + ))?; + + // we don't require source node to be archive, so we can't craft storage proofs using + // ancient headers. So if the `best_finalized_relay_block_at_target` is too ancient, we + // can't craft storage proofs using it + let may_use_state_at_best_finalized_relay_block_at_target = !is_ancient_block( + best_finalized_relay_block_at_target.number(), + best_finalized_relay_block_at_source.number(), + ); + + // now let's check if `required_header` may be proved using + // `best_finalized_relay_block_at_target` + let selection = if may_use_state_at_best_finalized_relay_block_at_target { + env.best_finalized_para_block_at_source(best_finalized_relay_block_at_target) + .await? + .filter(|best_finalized_para_block_at_target| { + best_finalized_para_block_at_target.number() >= required_parachain_header + }) + .map(|best_finalized_para_block_at_target| { + (false, best_finalized_relay_block_at_target, best_finalized_para_block_at_target) + }) + } else { + None + }; + + Ok(selection.unwrap_or(( + true, + best_finalized_relay_block_at_source, + best_possible_parachain_block, + ))) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn relay_waits_for_relay_header_to_be_delivered() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 90, + para_header_at_target: Some(50), + para_header_at_source: Some(HeaderId(110, 110)), + relay_header_at_source: 800, + relay_header_at_target: Some(700), + para_header_at_relay_header_at_target: Some(HeaderId(100, 100)), + }, + RelayState::RelayingRelayHeader(750), + ), + RelayState::RelayingRelayHeader(750), + ); + } + + #[test] + fn relay_starts_relaying_requested_para_header_after_relay_header_is_delivered() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 90, + para_header_at_target: Some(50), + para_header_at_source: Some(HeaderId(110, 110)), + relay_header_at_source: 800, + relay_header_at_target: Some(750), + para_header_at_relay_header_at_target: Some(HeaderId(100, 100)), + }, + RelayState::RelayingRelayHeader(750), + ), + RelayState::RelayingParaHeader(HeaderId(100, 100)), + ); + } + + #[test] + fn relay_selects_better_para_header_after_better_relay_header_is_delivered() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 90, + para_header_at_target: Some(50), + para_header_at_source: Some(HeaderId(110, 110)), + relay_header_at_source: 800, + relay_header_at_target: Some(780), + para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), + }, + RelayState::RelayingRelayHeader(750), + ), + RelayState::RelayingParaHeader(HeaderId(105, 105)), + ); + } + #[test] + fn relay_waits_for_para_header_to_be_delivered() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 90, + para_header_at_target: Some(50), + para_header_at_source: Some(HeaderId(110, 110)), + relay_header_at_source: 800, + relay_header_at_target: Some(780), + para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), + }, + RelayState::RelayingParaHeader(HeaderId(105, 105)), + ), + RelayState::RelayingParaHeader(HeaderId(105, 105)), + ); + } + + #[test] + fn relay_stays_idle_if_required_para_header_is_already_delivered() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 90, + para_header_at_target: Some(105), + para_header_at_source: Some(HeaderId(110, 110)), + relay_header_at_source: 800, + relay_header_at_target: Some(780), + para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), + }, + RelayState::Idle, + ), + RelayState::Idle, + ); + } + + #[test] + fn relay_waits_for_required_para_header_to_appear_at_source_1() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 120, + para_header_at_target: Some(105), + para_header_at_source: None, + relay_header_at_source: 800, + relay_header_at_target: Some(780), + para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), + }, + RelayState::Idle, + ), + RelayState::Idle, + ); + } + + #[test] + fn relay_waits_for_required_para_header_to_appear_at_source_2() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 120, + para_header_at_target: Some(105), + para_header_at_source: Some(HeaderId(110, 110)), + relay_header_at_source: 800, + relay_header_at_target: Some(780), + para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), + }, + RelayState::Idle, + ), + RelayState::Idle, + ); + } + + #[test] + fn relay_starts_relaying_relay_header_when_new_para_header_is_requested() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 120, + para_header_at_target: Some(105), + para_header_at_source: Some(HeaderId(125, 125)), + relay_header_at_source: 800, + relay_header_at_target: Some(780), + para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), + }, + RelayState::Idle, + ), + RelayState::RelayingRelayHeader(800), + ); + } + + #[test] + fn relay_starts_relaying_para_header_when_new_para_header_is_requested() { + assert_eq!( + select_headers_to_relay( + &RelayData { + required_para_header: 120, + para_header_at_target: Some(105), + para_header_at_source: Some(HeaderId(125, 125)), + relay_header_at_source: 800, + relay_header_at_target: Some(800), + para_header_at_relay_header_at_target: Some(HeaderId(125, 125)), + }, + RelayState::Idle, + ), + RelayState::RelayingParaHeader(HeaderId(125, 125)), + ); + } + + #[test] + fn relay_goes_idle_when_parachain_is_deregistered() { + assert_eq!( + select_headers_to_relay::( + &RelayData { + required_para_header: 120, + para_header_at_target: Some(105), + para_header_at_source: None, + relay_header_at_source: 800, + relay_header_at_target: Some(800), + para_header_at_relay_header_at_target: None, + }, + RelayState::RelayingRelayHeader(800), + ), + RelayState::Idle, + ); + } + + #[test] + fn relay_starts_relaying_first_parachain_header() { + assert_eq!( + select_headers_to_relay::( + &RelayData { + required_para_header: 0, + para_header_at_target: None, + para_header_at_source: Some(HeaderId(125, 125)), + relay_header_at_source: 800, + relay_header_at_target: Some(800), + para_header_at_relay_header_at_target: Some(HeaderId(125, 125)), + }, + RelayState::Idle, + ), + RelayState::RelayingParaHeader(HeaderId(125, 125)), + ); + } + + #[test] + fn relay_starts_relaying_relay_header_to_relay_first_parachain_header() { + assert_eq!( + select_headers_to_relay::( + &RelayData { + required_para_header: 0, + para_header_at_target: None, + para_header_at_source: Some(HeaderId(125, 125)), + relay_header_at_source: 800, + relay_header_at_target: Some(700), + para_header_at_relay_header_at_target: Some(HeaderId(125, 125)), + }, + RelayState::Idle, + ), + RelayState::RelayingRelayHeader(800), + ); + } + + // tuple is: + // + // - best_finalized_relay_block_at_source + // - best_finalized_relay_block_at_target + // - best_finalized_para_block_at_source at best_finalized_relay_block_at_source + // - best_finalized_para_block_at_source at best_finalized_relay_block_at_target + #[async_trait] + impl SelectHeadersToProveEnvironment for (u32, u32, u32, u32) { + fn parachain_id(&self) -> ParaId { + ParaId(0) + } + + async fn best_finalized_relay_block_at_source( + &self, + ) -> Result, SubstrateError> { + Ok(HeaderId(self.0, self.0)) + } + + async fn best_finalized_relay_block_at_target( + &self, + ) -> Result, SubstrateError> { + Ok(HeaderId(self.1, self.1)) + } + + async fn best_finalized_para_block_at_source( + &self, + at_relay_block: HeaderId, + ) -> Result>, SubstrateError> { + if at_relay_block.0 == self.0 { + Ok(Some(HeaderId(self.2, self.2))) + } else if at_relay_block.0 == self.1 { + Ok(Some(HeaderId(self.3, self.3))) + } else { + Ok(None) + } + } + } + + #[async_std::test] + async fn select_headers_to_prove_returns_err_if_required_para_block_is_missing_at_source() { + assert!(matches!( + select_headers_to_prove((20_u32, 10_u32, 200_u32, 100_u32), 300_u32,).await, + Err(SubstrateError::MissingRequiredParachainHead(ParaId(0), 300_u64)), + )); + } + + #[async_std::test] + async fn select_headers_to_prove_fails_to_use_existing_ancient_relay_block() { + assert_eq!( + select_headers_to_prove((220_u32, 10_u32, 200_u32, 100_u32), 100_u32,) + .await + .map_err(drop), + Ok((true, HeaderId(220, 220), HeaderId(200, 200))), + ); + } + + #[async_std::test] + async fn select_headers_to_prove_is_able_to_use_existing_recent_relay_block() { + assert_eq!( + select_headers_to_prove((40_u32, 10_u32, 200_u32, 100_u32), 100_u32,) + .await + .map_err(drop), + Ok((false, HeaderId(10, 10), HeaderId(100, 100))), + ); + } + + #[async_std::test] + async fn select_headers_to_prove_uses_new_relay_block() { + assert_eq!( + select_headers_to_prove((20_u32, 10_u32, 200_u32, 100_u32), 200_u32,) + .await + .map_err(drop), + Ok((true, HeaderId(20, 20), HeaderId(200, 200))), + ); + } +} diff --git a/relays/lib-substrate-relay/src/parachains/mod.rs b/relays/lib-substrate-relay/src/parachains/mod.rs new file mode 100644 index 00000000000..722f9b61f9f --- /dev/null +++ b/relays/lib-substrate-relay/src/parachains/mod.rs @@ -0,0 +1,108 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Types and functions intended to ease adding of new Substrate -> Substrate +//! parachain finality proofs synchronization pipelines. + +use async_trait::async_trait; +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use pallet_bridge_parachains::{ + Call as BridgeParachainsCall, Config as BridgeParachainsConfig, RelayBlockHash, + RelayBlockHasher, RelayBlockNumber, +}; +use parachains_relay::ParachainsPipeline; +use relay_substrate_client::{ + CallOf, Chain, ChainWithTransactions, HeaderIdOf, Parachain, RelayChain, +}; +use std::{fmt::Debug, marker::PhantomData}; + +pub mod source; +pub mod target; + +/// Substrate -> Substrate parachain finality proofs synchronization pipeline. +/// +/// This is currently restricted to the single parachain, because it is how it +/// will be used (at least) initially. +#[async_trait] +pub trait SubstrateParachainsPipeline: 'static + Clone + Debug + Send + Sync { + /// Headers of this parachain are submitted to the `Self::TargetChain`. + type SourceParachain: Parachain; + /// Relay chain that is storing headers of `Self::SourceParachain`. + type SourceRelayChain: RelayChain; + /// Target chain where `Self::SourceParachain` headers are submitted. + type TargetChain: ChainWithTransactions; + + /// How submit parachains heads call is built? + type SubmitParachainHeadsCallBuilder: SubmitParachainHeadsCallBuilder; +} + +/// Adapter that allows all `SubstrateParachainsPipeline` to act as `ParachainsPipeline`. +#[derive(Clone, Debug)] +pub struct ParachainsPipelineAdapter { + _phantom: PhantomData

, +} + +impl ParachainsPipeline for ParachainsPipelineAdapter

{ + type SourceParachain = P::SourceParachain; + type SourceRelayChain = P::SourceRelayChain; + type TargetChain = P::TargetChain; +} + +/// Different ways of building `submit_parachain_heads` calls. +pub trait SubmitParachainHeadsCallBuilder: + 'static + Send + Sync +{ + /// Given parachains and their heads proof, build call of `submit_parachain_heads` + /// function of bridge parachains module at the target chain. + fn build_submit_parachain_heads_call( + at_relay_block: HeaderIdOf, + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> CallOf; +} + +/// Building `submit_parachain_heads` call when you have direct access to the target +/// chain runtime. +pub struct DirectSubmitParachainHeadsCallBuilder { + _phantom: PhantomData<(P, R, I)>, +} + +impl SubmitParachainHeadsCallBuilder

for DirectSubmitParachainHeadsCallBuilder +where + P: SubstrateParachainsPipeline, + P::SourceRelayChain: Chain, + R: BridgeParachainsConfig + Send + Sync, + I: 'static + Send + Sync, + R::BridgedChain: bp_runtime::Chain< + BlockNumber = RelayBlockNumber, + Hash = RelayBlockHash, + Hasher = RelayBlockHasher, + >, + CallOf: From>, +{ + fn build_submit_parachain_heads_call( + at_relay_block: HeaderIdOf, + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> CallOf { + BridgeParachainsCall::::submit_parachain_heads { + at_relay_block: (at_relay_block.0, at_relay_block.1), + parachains, + parachain_heads_proof, + } + .into() + } +} diff --git a/relays/lib-substrate-relay/src/parachains/source.rs b/relays/lib-substrate-relay/src/parachains/source.rs new file mode 100644 index 00000000000..146c5840cd5 --- /dev/null +++ b/relays/lib-substrate-relay/src/parachains/source.rs @@ -0,0 +1,169 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Parachain heads source. + +use crate::parachains::{ParachainsPipelineAdapter, SubstrateParachainsPipeline}; + +use async_std::sync::{Arc, Mutex}; +use async_trait::async_trait; +use bp_parachains::parachain_head_storage_key_at_source; +use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; +use bp_runtime::HeaderIdProvider; +use codec::Decode; +use parachains_relay::parachains_loop::{AvailableHeader, SourceClient}; +use relay_substrate_client::{ + is_ancient_block, Chain, Client, Error as SubstrateError, HeaderIdOf, HeaderOf, ParachainBase, + RelayChain, +}; +use relay_utils::relay_loop::Client as RelayClient; + +/// Shared updatable reference to the maximal parachain header id that we want to sync from the +/// source. +pub type RequiredHeaderIdRef = Arc>>>; + +/// Substrate client as parachain heads source. +#[derive(Clone)] +pub struct ParachainsSource { + client: Client, + max_head_id: RequiredHeaderIdRef, +} + +impl ParachainsSource

{ + /// Creates new parachains source client. + pub fn new( + client: Client, + max_head_id: RequiredHeaderIdRef, + ) -> Self { + ParachainsSource { client, max_head_id } + } + + /// Returns reference to the underlying RPC client. + pub fn client(&self) -> &Client { + &self.client + } + + /// Return decoded head of given parachain. + pub async fn on_chain_para_head_id( + &self, + at_block: HeaderIdOf, + ) -> Result>, SubstrateError> { + let para_id = ParaId(P::SourceParachain::PARACHAIN_ID); + let storage_key = + parachain_head_storage_key_at_source(P::SourceRelayChain::PARAS_PALLET_NAME, para_id); + let para_head = self.client.raw_storage_value(storage_key, Some(at_block.1)).await?; + let para_head = para_head.map(|h| ParaHead::decode(&mut &h.0[..])).transpose()?; + let para_head = match para_head { + Some(para_head) => para_head, + None => return Ok(None), + }; + let para_head: HeaderOf = Decode::decode(&mut ¶_head.0[..])?; + Ok(Some(para_head.id())) + } +} + +#[async_trait] +impl RelayClient for ParachainsSource

{ + type Error = SubstrateError; + + async fn reconnect(&mut self) -> Result<(), SubstrateError> { + self.client.reconnect().await + } +} + +#[async_trait] +impl SourceClient> + for ParachainsSource

+where + P::SourceParachain: Chain, +{ + async fn ensure_synced(&self) -> Result { + match self.client.ensure_synced().await { + Ok(_) => Ok(true), + Err(SubstrateError::ClientNotSynced(_)) => Ok(false), + Err(e) => Err(e), + } + } + + async fn parachain_head( + &self, + at_block: HeaderIdOf, + ) -> Result>, Self::Error> { + // if requested relay header is ancient, then we don't even want to try to read the + // parachain head - we simply return `Unavailable` + let best_block_number = self.client.best_finalized_header_number().await?; + if is_ancient_block(at_block.number(), best_block_number) { + return Ok(AvailableHeader::Unavailable) + } + + // else - try to read head from the source client + let mut para_head_id = AvailableHeader::Missing; + if let Some(on_chain_para_head_id) = self.on_chain_para_head_id(at_block).await? { + // Never return head that is larger than requested. This way we'll never sync + // headers past `max_header_id`. + para_head_id = match *self.max_head_id.lock().await { + AvailableHeader::Unavailable => AvailableHeader::Unavailable, + AvailableHeader::Missing => { + // `max_header_id` is not set. There is no limit. + AvailableHeader::Available(on_chain_para_head_id) + }, + AvailableHeader::Available(max_head_id) => { + // We report at most `max_header_id`. + AvailableHeader::Available(std::cmp::min(on_chain_para_head_id, max_head_id)) + }, + } + } + + Ok(para_head_id) + } + + async fn prove_parachain_head( + &self, + at_block: HeaderIdOf, + ) -> Result<(ParaHeadsProof, ParaHash), Self::Error> { + let parachain = ParaId(P::SourceParachain::PARACHAIN_ID); + let storage_key = + parachain_head_storage_key_at_source(P::SourceRelayChain::PARAS_PALLET_NAME, parachain); + let parachain_heads_proof = self + .client + .prove_storage(vec![storage_key.clone()], at_block.1) + .await? + .into_iter_nodes() + .collect(); + + // why we're reading parachain head here once again (it has already been read at the + // `parachain_head`)? that's because `parachain_head` sometimes returns obsolete parachain + // head and loop sometimes asks to prove this obsolete head and gets other (actual) head + // instead + // + // => since we want to provide proper hashes in our `submit_parachain_heads` call, we're + // rereading actual value here + let parachain_head = self + .client + .raw_storage_value(storage_key, Some(at_block.1)) + .await? + .map(|h| ParaHead::decode(&mut &h.0[..])) + .transpose()? + .ok_or_else(|| { + SubstrateError::Custom(format!( + "Failed to read expected parachain {parachain:?} head at {at_block:?}" + )) + })?; + let parachain_head_hash = parachain_head.hash(); + + Ok((ParaHeadsProof(parachain_heads_proof), parachain_head_hash)) + } +} diff --git a/relays/lib-substrate-relay/src/parachains/target.rs b/relays/lib-substrate-relay/src/parachains/target.rs new file mode 100644 index 00000000000..6df7bc0a742 --- /dev/null +++ b/relays/lib-substrate-relay/src/parachains/target.rs @@ -0,0 +1,148 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Parachain heads target. + +use crate::{ + parachains::{ + ParachainsPipelineAdapter, SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, + }, + TransactionParams, +}; + +use async_trait::async_trait; +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use bp_runtime::HeaderIdProvider; +use codec::Decode; +use parachains_relay::parachains_loop::TargetClient; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, Chain, Client, Error as SubstrateError, HeaderIdOf, + ParachainBase, TransactionEra, TransactionTracker, UnsignedTransaction, +}; +use relay_utils::relay_loop::Client as RelayClient; +use sp_core::{Bytes, Pair}; + +/// Substrate client as parachain heads source. +pub struct ParachainsTarget { + client: Client, + transaction_params: TransactionParams>, +} + +impl ParachainsTarget

{ + /// Creates new parachains target client. + pub fn new( + client: Client, + transaction_params: TransactionParams>, + ) -> Self { + ParachainsTarget { client, transaction_params } + } + + /// Returns reference to the underlying RPC client. + pub fn client(&self) -> &Client { + &self.client + } +} + +impl Clone for ParachainsTarget

{ + fn clone(&self) -> Self { + ParachainsTarget { + client: self.client.clone(), + transaction_params: self.transaction_params.clone(), + } + } +} + +#[async_trait] +impl RelayClient for ParachainsTarget

{ + type Error = SubstrateError; + + async fn reconnect(&mut self) -> Result<(), SubstrateError> { + self.client.reconnect().await + } +} + +#[async_trait] +impl

TargetClient> for ParachainsTarget

+where + P: SubstrateParachainsPipeline, + AccountIdOf: From< as Pair>::Public>, +{ + type TransactionTracker = TransactionTracker>; + + async fn best_block(&self) -> Result, Self::Error> { + let best_header = self.client.best_header().await?; + let best_id = best_header.id(); + + Ok(best_id) + } + + async fn best_finalized_source_relay_chain_block( + &self, + at_block: &HeaderIdOf, + ) -> Result, Self::Error> { + self.client + .typed_state_call::<_, Option>>( + P::SourceRelayChain::BEST_FINALIZED_HEADER_ID_METHOD.into(), + (), + Some(at_block.1), + ) + .await? + .map(Ok) + .unwrap_or(Err(SubstrateError::BridgePalletIsNotInitialized)) + } + + async fn parachain_head( + &self, + at_block: HeaderIdOf, + ) -> Result>, Self::Error> { + let encoded_best_finalized_source_para_block = self + .client + .state_call( + P::SourceParachain::BEST_FINALIZED_HEADER_ID_METHOD.into(), + Bytes(Vec::new()), + Some(at_block.1), + ) + .await?; + + Ok(Option::>::decode( + &mut &encoded_best_finalized_source_para_block.0[..], + ) + .map_err(SubstrateError::ResponseParseFailed)?) + } + + async fn submit_parachain_head_proof( + &self, + at_relay_block: HeaderIdOf, + updated_head_hash: ParaHash, + proof: ParaHeadsProof, + ) -> Result { + let transaction_params = self.transaction_params.clone(); + let call = P::SubmitParachainHeadsCallBuilder::build_submit_parachain_heads_call( + at_relay_block, + vec![(ParaId(P::SourceParachain::PARACHAIN_ID), updated_head_hash)], + proof, + ); + self.client + .submit_and_watch_signed_extrinsic( + &transaction_params.signer, + move |best_block_id, transaction_nonce| { + Ok(UnsignedTransaction::new(call.into(), transaction_nonce) + .era(TransactionEra::new(best_block_id, transaction_params.mortality))) + }, + ) + .await + } +} diff --git a/relays/messages/Cargo.toml b/relays/messages/Cargo.toml new file mode 100644 index 00000000000..8c4b8257d5a --- /dev/null +++ b/relays/messages/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "messages-relay" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +async-std = { version = "1.6.5", features = ["attributes"] } +async-trait = "0.1" +futures = "0.3.28" +hex = "0.4" +log = "0.4.17" +num-traits = "0.2" +parking_lot = "0.12.1" + +# Bridge Dependencies + +bp-messages = { path = "../../primitives/messages" } +finality-relay = { path = "../finality" } +relay-utils = { path = "../utils" } + +sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/messages/src/lib.rs b/relays/messages/src/lib.rs new file mode 100644 index 00000000000..9c62cee5ee3 --- /dev/null +++ b/relays/messages/src/lib.rs @@ -0,0 +1,37 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Relaying [`pallet-bridge-messages`](../pallet_bridge_messages/index.html) application specific +//! data. Message lane allows sending arbitrary messages between bridged chains. This +//! module provides entrypoint that starts reading messages from given message lane +//! of source chain and submits proof-of-message-at-source-chain transactions to the +//! target chain. Additionally, proofs-of-messages-delivery are sent back from the +//! target chain to the source chain. + +// required for futures::select! +#![recursion_limit = "1024"] +#![warn(missing_docs)] + +mod metrics; + +pub mod message_lane; +pub mod message_lane_loop; + +mod message_race_delivery; +mod message_race_limits; +mod message_race_loop; +mod message_race_receiving; +mod message_race_strategy; diff --git a/relays/messages/src/message_lane.rs b/relays/messages/src/message_lane.rs new file mode 100644 index 00000000000..5c9728ad93a --- /dev/null +++ b/relays/messages/src/message_lane.rs @@ -0,0 +1,71 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! One-way message lane types. Within single one-way lane we have three 'races' where we try to: +//! +//! 1) relay new messages from source to target node; +//! 2) relay proof-of-delivery from target to source node. + +use num_traits::{SaturatingAdd, Zero}; +use relay_utils::{BlockNumberBase, HeaderId}; +use sp_arithmetic::traits::AtLeast32BitUnsigned; +use std::{fmt::Debug, ops::Sub}; + +/// One-way message lane. +pub trait MessageLane: 'static + Clone + Send + Sync { + /// Name of the messages source. + const SOURCE_NAME: &'static str; + /// Name of the messages target. + const TARGET_NAME: &'static str; + + /// Messages proof. + type MessagesProof: Clone + Debug + Send + Sync; + /// Messages receiving proof. + type MessagesReceivingProof: Clone + Debug + Send + Sync; + + /// The type of the source chain token balance, that is used to: + /// + /// 1) pay transaction fees; + /// 2) pay message delivery and dispatch fee; + /// 3) pay relayer rewards. + type SourceChainBalance: AtLeast32BitUnsigned + + Clone + + Copy + + Debug + + PartialOrd + + Sub + + SaturatingAdd + + Zero + + Send + + Sync; + /// Number of the source header. + type SourceHeaderNumber: BlockNumberBase; + /// Hash of the source header. + type SourceHeaderHash: Clone + Debug + Default + PartialEq + Send + Sync; + + /// Number of the target header. + type TargetHeaderNumber: BlockNumberBase; + /// Hash of the target header. + type TargetHeaderHash: Clone + Debug + Default + PartialEq + Send + Sync; +} + +/// Source header id within given one-way message lane. +pub type SourceHeaderIdOf

= + HeaderId<

::SourceHeaderHash,

::SourceHeaderNumber>; + +/// Target header id within given one-way message lane. +pub type TargetHeaderIdOf

= + HeaderId<

::TargetHeaderHash,

::TargetHeaderNumber>; diff --git a/relays/messages/src/message_lane_loop.rs b/relays/messages/src/message_lane_loop.rs new file mode 100644 index 00000000000..ba86f05ffd3 --- /dev/null +++ b/relays/messages/src/message_lane_loop.rs @@ -0,0 +1,1261 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Message delivery loop. Designed to work with messages pallet. +//! +//! Single relay instance delivers messages of single lane in single direction. +//! To serve two-way lane, you would need two instances of relay. +//! To serve N two-way lanes, you would need N*2 instances of relay. +//! +//! Please keep in mind that the best header in this file is actually best +//! finalized header. I.e. when talking about headers in lane context, we +//! only care about finalized headers. + +use std::{collections::BTreeMap, fmt::Debug, future::Future, ops::RangeInclusive, time::Duration}; + +use async_trait::async_trait; +use futures::{channel::mpsc::unbounded, future::FutureExt, stream::StreamExt}; + +use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState, Weight}; +use relay_utils::{ + interval, metrics::MetricsParams, process_future_result, relay_loop::Client as RelayClient, + retry_backoff, FailedClient, TransactionTracker, +}; + +use crate::{ + message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, + message_race_delivery::run as run_message_delivery_race, + message_race_receiving::run as run_message_receiving_race, + metrics::MessageLaneLoopMetrics, +}; + +/// Message lane loop configuration params. +#[derive(Debug, Clone)] +pub struct Params { + /// Id of lane this loop is servicing. + pub lane: LaneId, + /// Interval at which we ask target node about its updates. + pub source_tick: Duration, + /// Interval at which we ask target node about its updates. + pub target_tick: Duration, + /// Delay between moments when connection error happens and our reconnect attempt. + pub reconnect_delay: Duration, + /// Message delivery race parameters. + pub delivery_params: MessageDeliveryParams, +} + +/// Message delivery race parameters. +#[derive(Debug, Clone)] +pub struct MessageDeliveryParams { + /// Maximal number of unconfirmed relayer entries at the inbound lane. If there's that number + /// of entries in the `InboundLaneData::relayers` set, all new messages will be rejected until + /// reward payment will be proved (by including outbound lane state to the message delivery + /// transaction). + pub max_unrewarded_relayer_entries_at_target: MessageNonce, + /// Message delivery race will stop delivering messages if there are + /// `max_unconfirmed_nonces_at_target` unconfirmed nonces on the target node. The race would + /// continue once they're confirmed by the receiving race. + pub max_unconfirmed_nonces_at_target: MessageNonce, + /// Maximal number of relayed messages in single delivery transaction. + pub max_messages_in_single_batch: MessageNonce, + /// Maximal cumulative dispatch weight of relayed messages in single delivery transaction. + pub max_messages_weight_in_single_batch: Weight, + /// Maximal cumulative size of relayed messages in single delivery transaction. + pub max_messages_size_in_single_batch: u32, +} + +/// Message details. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MessageDetails { + /// Message dispatch weight. + pub dispatch_weight: Weight, + /// Message size (number of bytes in encoded payload). + pub size: u32, + /// The relayer reward paid in the source chain tokens. + pub reward: SourceChainBalance, +} + +/// Messages details map. +pub type MessageDetailsMap = + BTreeMap>; + +/// Message delivery race proof parameters. +#[derive(Debug, PartialEq, Eq)] +pub struct MessageProofParameters { + /// Include outbound lane state proof? + pub outbound_state_proof_required: bool, + /// Cumulative dispatch weight of messages that we're building proof for. + pub dispatch_weight: Weight, +} + +/// Artifacts of submitting nonces proof. +pub struct NoncesSubmitArtifacts { + /// Submitted nonces range. + pub nonces: RangeInclusive, + /// Submitted transaction tracker. + pub tx_tracker: T, +} + +/// Batch transaction that already submit some headers and needs to be extended with +/// messages/delivery proof before sending. +pub trait BatchTransaction: Debug + Send { + /// Header that was required in the original call and which is bundled within this + /// batch transaction. + fn required_header_id(&self) -> HeaderId; +} + +/// Source client trait. +#[async_trait] +pub trait SourceClient: RelayClient { + /// Type of batch transaction that submits finality and message receiving proof. + type BatchTransaction: BatchTransaction> + Clone; + /// Transaction tracker to track submitted transactions. + type TransactionTracker: TransactionTracker>; + + /// Returns state of the client. + async fn state(&self) -> Result, Self::Error>; + + /// Get nonce of instance of latest generated message. + async fn latest_generated_nonce( + &self, + id: SourceHeaderIdOf

, + ) -> Result<(SourceHeaderIdOf

, MessageNonce), Self::Error>; + + /// Get nonce of the latest message, which receiving has been confirmed by the target chain. + async fn latest_confirmed_received_nonce( + &self, + id: SourceHeaderIdOf

, + ) -> Result<(SourceHeaderIdOf

, MessageNonce), Self::Error>; + + /// Returns mapping of message nonces, generated on this client, to their weights. + /// + /// Some messages may be missing from returned map, if corresponding messages were pruned at + /// the source chain. + async fn generated_message_details( + &self, + id: SourceHeaderIdOf

, + nonces: RangeInclusive, + ) -> Result, Self::Error>; + + /// Prove messages in inclusive range [begin; end]. + async fn prove_messages( + &self, + id: SourceHeaderIdOf

, + nonces: RangeInclusive, + proof_parameters: MessageProofParameters, + ) -> Result<(SourceHeaderIdOf

, RangeInclusive, P::MessagesProof), Self::Error>; + + /// Submit messages receiving proof. + async fn submit_messages_receiving_proof( + &self, + maybe_batch_tx: Option, + generated_at_block: TargetHeaderIdOf

, + proof: P::MessagesReceivingProof, + ) -> Result; + + /// We need given finalized target header on source to continue synchronization. + /// + /// We assume that the absence of header `id` has already been checked by caller. + /// + /// The client may return `Some(_)`, which means that nothing has happened yet and + /// the caller must generate and append message receiving proof to the batch transaction + /// to actually send it (along with required header) to the node. + /// + /// If function has returned `None`, it means that the caller now must wait for the + /// appearance of the target header `id` at the source client. + async fn require_target_header_on_source( + &self, + id: TargetHeaderIdOf

, + ) -> Result, Self::Error>; +} + +/// Target client trait. +#[async_trait] +pub trait TargetClient: RelayClient { + /// Type of batch transaction that submits finality and messages proof. + type BatchTransaction: BatchTransaction> + Clone; + /// Transaction tracker to track submitted transactions. + type TransactionTracker: TransactionTracker>; + + /// Returns state of the client. + async fn state(&self) -> Result, Self::Error>; + + /// Get nonce of latest received message. + async fn latest_received_nonce( + &self, + id: TargetHeaderIdOf

, + ) -> Result<(TargetHeaderIdOf

, MessageNonce), Self::Error>; + + /// Get nonce of the latest confirmed message. + async fn latest_confirmed_received_nonce( + &self, + id: TargetHeaderIdOf

, + ) -> Result<(TargetHeaderIdOf

, MessageNonce), Self::Error>; + + /// Get state of unrewarded relayers set at the inbound lane. + async fn unrewarded_relayers_state( + &self, + id: TargetHeaderIdOf

, + ) -> Result<(TargetHeaderIdOf

, UnrewardedRelayersState), Self::Error>; + + /// Prove messages receiving at given block. + async fn prove_messages_receiving( + &self, + id: TargetHeaderIdOf

, + ) -> Result<(TargetHeaderIdOf

, P::MessagesReceivingProof), Self::Error>; + + /// Submit messages proof. + async fn submit_messages_proof( + &self, + maybe_batch_tx: Option, + generated_at_header: SourceHeaderIdOf

, + nonces: RangeInclusive, + proof: P::MessagesProof, + ) -> Result, Self::Error>; + + /// We need given finalized source header on target to continue synchronization. + /// + /// The client may return `Some(_)`, which means that nothing has happened yet and + /// the caller must generate and append messages proof to the batch transaction + /// to actually send it (along with required header) to the node. + /// + /// If function has returned `None`, it means that the caller now must wait for the + /// appearance of the source header `id` at the target client. + async fn require_source_header_on_target( + &self, + id: SourceHeaderIdOf

, + ) -> Result, Self::Error>; +} + +/// State of the client. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct ClientState { + /// The best header id of this chain. + pub best_self: SelfHeaderId, + /// Best finalized header id of this chain. + pub best_finalized_self: SelfHeaderId, + /// Best finalized header id of the peer chain read at the best block of this chain (at + /// `best_finalized_self`). + /// + /// It may be `None` e,g. if peer is a parachain and we haven't yet relayed any parachain + /// heads. + pub best_finalized_peer_at_best_self: Option, + /// Header id of the peer chain with the number, matching the + /// `best_finalized_peer_at_best_self`. + pub actual_best_finalized_peer_at_best_self: Option, +} + +/// State of source client in one-way message lane. +pub type SourceClientState

= ClientState, TargetHeaderIdOf

>; + +/// State of target client in one-way message lane. +pub type TargetClientState

= ClientState, SourceHeaderIdOf

>; + +/// Both clients state. +#[derive(Debug, Default)] +pub struct ClientsState { + /// Source client state. + pub source: Option>, + /// Target client state. + pub target: Option>, +} + +/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs +/// sync loop. +pub fn metrics_prefix(lane: &LaneId) -> String { + format!("{}_to_{}_MessageLane_{}", P::SOURCE_NAME, P::TARGET_NAME, hex::encode(lane)) +} + +/// Run message lane service loop. +pub async fn run( + params: Params, + source_client: impl SourceClient

, + target_client: impl TargetClient

, + metrics_params: MetricsParams, + exit_signal: impl Future + Send + 'static, +) -> Result<(), relay_utils::Error> { + let exit_signal = exit_signal.shared(); + relay_utils::relay_loop(source_client, target_client) + .reconnect_delay(params.reconnect_delay) + .with_metrics(metrics_params) + .loop_metric(MessageLaneLoopMetrics::new(Some(&metrics_prefix::

(¶ms.lane)))?)? + .expose() + .await? + .run(metrics_prefix::

(¶ms.lane), move |source_client, target_client, metrics| { + run_until_connection_lost( + params.clone(), + source_client, + target_client, + metrics, + exit_signal.clone(), + ) + }) + .await +} + +/// Run one-way message delivery loop until connection with target or source node is lost, or exit +/// signal is received. +async fn run_until_connection_lost, TC: TargetClient

>( + params: Params, + source_client: SC, + target_client: TC, + metrics_msg: Option, + exit_signal: impl Future, +) -> Result<(), FailedClient> { + let mut source_retry_backoff = retry_backoff(); + let mut source_client_is_online = false; + let mut source_state_required = true; + let source_state = source_client.state().fuse(); + let source_go_offline_future = futures::future::Fuse::terminated(); + let source_tick_stream = interval(params.source_tick).fuse(); + + let mut target_retry_backoff = retry_backoff(); + let mut target_client_is_online = false; + let mut target_state_required = true; + let target_state = target_client.state().fuse(); + let target_go_offline_future = futures::future::Fuse::terminated(); + let target_tick_stream = interval(params.target_tick).fuse(); + + let ( + (delivery_source_state_sender, delivery_source_state_receiver), + (delivery_target_state_sender, delivery_target_state_receiver), + ) = (unbounded(), unbounded()); + let delivery_race_loop = run_message_delivery_race( + source_client.clone(), + delivery_source_state_receiver, + target_client.clone(), + delivery_target_state_receiver, + metrics_msg.clone(), + params.delivery_params, + ) + .fuse(); + + let ( + (receiving_source_state_sender, receiving_source_state_receiver), + (receiving_target_state_sender, receiving_target_state_receiver), + ) = (unbounded(), unbounded()); + let receiving_race_loop = run_message_receiving_race( + source_client.clone(), + receiving_source_state_receiver, + target_client.clone(), + receiving_target_state_receiver, + metrics_msg.clone(), + ) + .fuse(); + + let exit_signal = exit_signal.fuse(); + + futures::pin_mut!( + source_state, + source_go_offline_future, + source_tick_stream, + target_state, + target_go_offline_future, + target_tick_stream, + delivery_race_loop, + receiving_race_loop, + exit_signal + ); + + loop { + futures::select! { + new_source_state = source_state => { + source_state_required = false; + + source_client_is_online = process_future_result( + new_source_state, + &mut source_retry_backoff, + |new_source_state| { + log::debug!( + target: "bridge", + "Received state from {} node: {:?}", + P::SOURCE_NAME, + new_source_state, + ); + let _ = delivery_source_state_sender.unbounded_send(new_source_state.clone()); + let _ = receiving_source_state_sender.unbounded_send(new_source_state.clone()); + + if let Some(metrics_msg) = metrics_msg.as_ref() { + metrics_msg.update_source_state::

(new_source_state); + } + }, + &mut source_go_offline_future, + async_std::task::sleep, + || format!("Error retrieving state from {} node", P::SOURCE_NAME), + ).fail_if_connection_error(FailedClient::Source)?; + }, + _ = source_go_offline_future => { + source_client_is_online = true; + }, + _ = source_tick_stream.next() => { + source_state_required = true; + }, + new_target_state = target_state => { + target_state_required = false; + + target_client_is_online = process_future_result( + new_target_state, + &mut target_retry_backoff, + |new_target_state| { + log::debug!( + target: "bridge", + "Received state from {} node: {:?}", + P::TARGET_NAME, + new_target_state, + ); + let _ = delivery_target_state_sender.unbounded_send(new_target_state.clone()); + let _ = receiving_target_state_sender.unbounded_send(new_target_state.clone()); + + if let Some(metrics_msg) = metrics_msg.as_ref() { + metrics_msg.update_target_state::

(new_target_state); + } + }, + &mut target_go_offline_future, + async_std::task::sleep, + || format!("Error retrieving state from {} node", P::TARGET_NAME), + ).fail_if_connection_error(FailedClient::Target)?; + }, + _ = target_go_offline_future => { + target_client_is_online = true; + }, + _ = target_tick_stream.next() => { + target_state_required = true; + }, + + delivery_error = delivery_race_loop => { + match delivery_error { + Ok(_) => unreachable!("only ends with error; qed"), + Err(err) => return Err(err), + } + }, + receiving_error = receiving_race_loop => { + match receiving_error { + Ok(_) => unreachable!("only ends with error; qed"), + Err(err) => return Err(err), + } + }, + + () = exit_signal => { + return Ok(()); + } + } + + if source_client_is_online && source_state_required { + log::debug!(target: "bridge", "Asking {} node about its state", P::SOURCE_NAME); + source_state.set(source_client.state().fuse()); + source_client_is_online = false; + } + + if target_client_is_online && target_state_required { + log::debug!(target: "bridge", "Asking {} node about its state", P::TARGET_NAME); + target_state.set(target_client.state().fuse()); + target_client_is_online = false; + } + } +} + +#[cfg(test)] +pub(crate) mod tests { + use std::sync::Arc; + + use futures::stream::StreamExt; + use parking_lot::Mutex; + + use relay_utils::{HeaderId, MaybeConnectionError, TrackedTransactionStatus}; + + use super::*; + + pub fn header_id(number: TestSourceHeaderNumber) -> TestSourceHeaderId { + HeaderId(number, number) + } + + pub type TestSourceChainBalance = u64; + pub type TestSourceHeaderId = HeaderId; + pub type TestTargetHeaderId = HeaderId; + + pub type TestMessagesProof = (RangeInclusive, Option); + pub type TestMessagesReceivingProof = MessageNonce; + + pub type TestSourceHeaderNumber = u64; + pub type TestSourceHeaderHash = u64; + + pub type TestTargetHeaderNumber = u64; + pub type TestTargetHeaderHash = u64; + + #[derive(Debug)] + pub struct TestError; + + impl MaybeConnectionError for TestError { + fn is_connection_error(&self) -> bool { + true + } + } + + #[derive(Clone)] + pub struct TestMessageLane; + + impl MessageLane for TestMessageLane { + const SOURCE_NAME: &'static str = "TestSource"; + const TARGET_NAME: &'static str = "TestTarget"; + + type MessagesProof = TestMessagesProof; + type MessagesReceivingProof = TestMessagesReceivingProof; + + type SourceChainBalance = TestSourceChainBalance; + type SourceHeaderNumber = TestSourceHeaderNumber; + type SourceHeaderHash = TestSourceHeaderHash; + + type TargetHeaderNumber = TestTargetHeaderNumber; + type TargetHeaderHash = TestTargetHeaderHash; + } + + #[derive(Clone, Debug)] + pub struct TestMessagesBatchTransaction { + required_header_id: TestSourceHeaderId, + } + + #[async_trait] + impl BatchTransaction for TestMessagesBatchTransaction { + fn required_header_id(&self) -> TestSourceHeaderId { + self.required_header_id + } + } + + #[derive(Clone, Debug)] + pub struct TestConfirmationBatchTransaction { + required_header_id: TestTargetHeaderId, + } + + #[async_trait] + impl BatchTransaction for TestConfirmationBatchTransaction { + fn required_header_id(&self) -> TestTargetHeaderId { + self.required_header_id + } + } + + #[derive(Clone, Debug)] + pub struct TestTransactionTracker(TrackedTransactionStatus); + + impl Default for TestTransactionTracker { + fn default() -> TestTransactionTracker { + TestTransactionTracker(TrackedTransactionStatus::Finalized(Default::default())) + } + } + + #[async_trait] + impl TransactionTracker for TestTransactionTracker { + type HeaderId = TestTargetHeaderId; + + async fn wait(self) -> TrackedTransactionStatus { + self.0 + } + } + + #[derive(Debug, Clone)] + pub struct TestClientData { + is_source_fails: bool, + is_source_reconnected: bool, + source_state: SourceClientState, + source_latest_generated_nonce: MessageNonce, + source_latest_confirmed_received_nonce: MessageNonce, + source_tracked_transaction_status: TrackedTransactionStatus, + submitted_messages_receiving_proofs: Vec, + is_target_fails: bool, + is_target_reconnected: bool, + target_state: SourceClientState, + target_latest_received_nonce: MessageNonce, + target_latest_confirmed_received_nonce: MessageNonce, + target_tracked_transaction_status: TrackedTransactionStatus, + submitted_messages_proofs: Vec, + target_to_source_batch_transaction: Option, + target_to_source_header_required: Option, + target_to_source_header_requirements: Vec, + source_to_target_batch_transaction: Option, + source_to_target_header_required: Option, + source_to_target_header_requirements: Vec, + } + + impl Default for TestClientData { + fn default() -> TestClientData { + TestClientData { + is_source_fails: false, + is_source_reconnected: false, + source_state: Default::default(), + source_latest_generated_nonce: 0, + source_latest_confirmed_received_nonce: 0, + source_tracked_transaction_status: TrackedTransactionStatus::Finalized(HeaderId( + 0, + Default::default(), + )), + submitted_messages_receiving_proofs: Vec::new(), + is_target_fails: false, + is_target_reconnected: false, + target_state: Default::default(), + target_latest_received_nonce: 0, + target_latest_confirmed_received_nonce: 0, + target_tracked_transaction_status: TrackedTransactionStatus::Finalized(HeaderId( + 0, + Default::default(), + )), + submitted_messages_proofs: Vec::new(), + target_to_source_batch_transaction: None, + target_to_source_header_required: None, + target_to_source_header_requirements: Vec::new(), + source_to_target_batch_transaction: None, + source_to_target_header_required: None, + source_to_target_header_requirements: Vec::new(), + } + } + } + + impl TestClientData { + fn receive_messages(&mut self, proof: TestMessagesProof) { + self.target_state.best_self = + HeaderId(self.target_state.best_self.0 + 1, self.target_state.best_self.1 + 1); + self.target_state.best_finalized_self = self.target_state.best_self; + self.target_latest_received_nonce = *proof.0.end(); + if let Some(target_latest_confirmed_received_nonce) = proof.1 { + self.target_latest_confirmed_received_nonce = + target_latest_confirmed_received_nonce; + } + self.submitted_messages_proofs.push(proof); + } + + fn receive_messages_delivery_proof(&mut self, proof: TestMessagesReceivingProof) { + self.source_state.best_self = + HeaderId(self.source_state.best_self.0 + 1, self.source_state.best_self.1 + 1); + self.source_state.best_finalized_self = self.source_state.best_self; + self.submitted_messages_receiving_proofs.push(proof); + self.source_latest_confirmed_received_nonce = proof; + } + } + + #[derive(Clone)] + pub struct TestSourceClient { + data: Arc>, + tick: Arc, + post_tick: Arc, + } + + impl Default for TestSourceClient { + fn default() -> Self { + TestSourceClient { + data: Arc::new(Mutex::new(TestClientData::default())), + tick: Arc::new(|_| {}), + post_tick: Arc::new(|_| {}), + } + } + } + + #[async_trait] + impl RelayClient for TestSourceClient { + type Error = TestError; + + async fn reconnect(&mut self) -> Result<(), TestError> { + { + let mut data = self.data.lock(); + (self.tick)(&mut data); + data.is_source_reconnected = true; + (self.post_tick)(&mut data); + } + Ok(()) + } + } + + #[async_trait] + impl SourceClient for TestSourceClient { + type BatchTransaction = TestConfirmationBatchTransaction; + type TransactionTracker = TestTransactionTracker; + + async fn state(&self) -> Result, TestError> { + let mut data = self.data.lock(); + (self.tick)(&mut data); + if data.is_source_fails { + return Err(TestError) + } + (self.post_tick)(&mut data); + Ok(data.source_state.clone()) + } + + async fn latest_generated_nonce( + &self, + id: SourceHeaderIdOf, + ) -> Result<(SourceHeaderIdOf, MessageNonce), TestError> { + let mut data = self.data.lock(); + (self.tick)(&mut data); + if data.is_source_fails { + return Err(TestError) + } + (self.post_tick)(&mut data); + Ok((id, data.source_latest_generated_nonce)) + } + + async fn latest_confirmed_received_nonce( + &self, + id: SourceHeaderIdOf, + ) -> Result<(SourceHeaderIdOf, MessageNonce), TestError> { + let mut data = self.data.lock(); + (self.tick)(&mut data); + (self.post_tick)(&mut data); + Ok((id, data.source_latest_confirmed_received_nonce)) + } + + async fn generated_message_details( + &self, + _id: SourceHeaderIdOf, + nonces: RangeInclusive, + ) -> Result, TestError> { + Ok(nonces + .map(|nonce| { + ( + nonce, + MessageDetails { + dispatch_weight: Weight::from_parts(1, 0), + size: 1, + reward: 1, + }, + ) + }) + .collect()) + } + + async fn prove_messages( + &self, + id: SourceHeaderIdOf, + nonces: RangeInclusive, + proof_parameters: MessageProofParameters, + ) -> Result< + (SourceHeaderIdOf, RangeInclusive, TestMessagesProof), + TestError, + > { + let mut data = self.data.lock(); + (self.tick)(&mut data); + (self.post_tick)(&mut data); + Ok(( + id, + nonces.clone(), + ( + nonces, + if proof_parameters.outbound_state_proof_required { + Some(data.source_latest_confirmed_received_nonce) + } else { + None + }, + ), + )) + } + + async fn submit_messages_receiving_proof( + &self, + _maybe_batch_tx: Option, + _generated_at_block: TargetHeaderIdOf, + proof: TestMessagesReceivingProof, + ) -> Result { + let mut data = self.data.lock(); + (self.tick)(&mut data); + data.receive_messages_delivery_proof(proof); + (self.post_tick)(&mut data); + Ok(TestTransactionTracker(data.source_tracked_transaction_status)) + } + + async fn require_target_header_on_source( + &self, + id: TargetHeaderIdOf, + ) -> Result, Self::Error> { + let mut data = self.data.lock(); + data.target_to_source_header_required = Some(id); + data.target_to_source_header_requirements.push(id); + (self.tick)(&mut data); + (self.post_tick)(&mut data); + + Ok(data.target_to_source_batch_transaction.take().map(|mut tx| { + tx.required_header_id = id; + tx + })) + } + } + + #[derive(Clone)] + pub struct TestTargetClient { + data: Arc>, + tick: Arc, + post_tick: Arc, + } + + impl Default for TestTargetClient { + fn default() -> Self { + TestTargetClient { + data: Arc::new(Mutex::new(TestClientData::default())), + tick: Arc::new(|_| {}), + post_tick: Arc::new(|_| {}), + } + } + } + + #[async_trait] + impl RelayClient for TestTargetClient { + type Error = TestError; + + async fn reconnect(&mut self) -> Result<(), TestError> { + { + let mut data = self.data.lock(); + (self.tick)(&mut data); + data.is_target_reconnected = true; + (self.post_tick)(&mut data); + } + Ok(()) + } + } + + #[async_trait] + impl TargetClient for TestTargetClient { + type BatchTransaction = TestMessagesBatchTransaction; + type TransactionTracker = TestTransactionTracker; + + async fn state(&self) -> Result, TestError> { + let mut data = self.data.lock(); + (self.tick)(&mut data); + if data.is_target_fails { + return Err(TestError) + } + (self.post_tick)(&mut data); + Ok(data.target_state.clone()) + } + + async fn latest_received_nonce( + &self, + id: TargetHeaderIdOf, + ) -> Result<(TargetHeaderIdOf, MessageNonce), TestError> { + let mut data = self.data.lock(); + (self.tick)(&mut data); + if data.is_target_fails { + return Err(TestError) + } + (self.post_tick)(&mut data); + Ok((id, data.target_latest_received_nonce)) + } + + async fn unrewarded_relayers_state( + &self, + id: TargetHeaderIdOf, + ) -> Result<(TargetHeaderIdOf, UnrewardedRelayersState), TestError> { + Ok(( + id, + UnrewardedRelayersState { + unrewarded_relayer_entries: 0, + messages_in_oldest_entry: 0, + total_messages: 0, + last_delivered_nonce: 0, + }, + )) + } + + async fn latest_confirmed_received_nonce( + &self, + id: TargetHeaderIdOf, + ) -> Result<(TargetHeaderIdOf, MessageNonce), TestError> { + let mut data = self.data.lock(); + (self.tick)(&mut data); + if data.is_target_fails { + return Err(TestError) + } + (self.post_tick)(&mut data); + Ok((id, data.target_latest_confirmed_received_nonce)) + } + + async fn prove_messages_receiving( + &self, + id: TargetHeaderIdOf, + ) -> Result<(TargetHeaderIdOf, TestMessagesReceivingProof), TestError> { + Ok((id, self.data.lock().target_latest_received_nonce)) + } + + async fn submit_messages_proof( + &self, + _maybe_batch_tx: Option, + _generated_at_header: SourceHeaderIdOf, + nonces: RangeInclusive, + proof: TestMessagesProof, + ) -> Result, TestError> { + let mut data = self.data.lock(); + (self.tick)(&mut data); + if data.is_target_fails { + return Err(TestError) + } + data.receive_messages(proof); + (self.post_tick)(&mut data); + Ok(NoncesSubmitArtifacts { + nonces, + tx_tracker: TestTransactionTracker(data.target_tracked_transaction_status), + }) + } + + async fn require_source_header_on_target( + &self, + id: SourceHeaderIdOf, + ) -> Result, Self::Error> { + let mut data = self.data.lock(); + data.source_to_target_header_required = Some(id); + data.source_to_target_header_requirements.push(id); + (self.tick)(&mut data); + (self.post_tick)(&mut data); + + Ok(data.source_to_target_batch_transaction.take().map(|mut tx| { + tx.required_header_id = id; + tx + })) + } + } + + fn run_loop_test( + data: Arc>, + source_tick: Arc, + source_post_tick: Arc, + target_tick: Arc, + target_post_tick: Arc, + exit_signal: impl Future + 'static + Send, + ) -> TestClientData { + async_std::task::block_on(async { + let source_client = TestSourceClient { + data: data.clone(), + tick: source_tick, + post_tick: source_post_tick, + }; + let target_client = TestTargetClient { + data: data.clone(), + tick: target_tick, + post_tick: target_post_tick, + }; + let _ = run( + Params { + lane: LaneId([0, 0, 0, 0]), + source_tick: Duration::from_millis(100), + target_tick: Duration::from_millis(100), + reconnect_delay: Duration::from_millis(0), + delivery_params: MessageDeliveryParams { + max_unrewarded_relayer_entries_at_target: 4, + max_unconfirmed_nonces_at_target: 4, + max_messages_in_single_batch: 4, + max_messages_weight_in_single_batch: Weight::from_parts(4, 0), + max_messages_size_in_single_batch: 4, + }, + }, + source_client, + target_client, + MetricsParams::disabled(), + exit_signal, + ) + .await; + let result = data.lock().clone(); + result + }) + } + + #[test] + fn message_lane_loop_is_able_to_recover_from_connection_errors() { + // with this configuration, source client will return Err, making source client + // reconnect. Then the target client will fail with Err + reconnect. Then we finally + // able to deliver messages. + let (exit_sender, exit_receiver) = unbounded(); + let result = run_loop_test( + Arc::new(Mutex::new(TestClientData { + is_source_fails: true, + source_state: ClientState { + best_self: HeaderId(0, 0), + best_finalized_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + }, + source_latest_generated_nonce: 1, + target_state: ClientState { + best_self: HeaderId(0, 0), + best_finalized_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + }, + target_latest_received_nonce: 0, + ..Default::default() + })), + Arc::new(|data: &mut TestClientData| { + if data.is_source_reconnected { + data.is_source_fails = false; + data.is_target_fails = true; + } + }), + Arc::new(|_| {}), + Arc::new(move |data: &mut TestClientData| { + if data.is_target_reconnected { + data.is_target_fails = false; + } + if data.target_state.best_finalized_peer_at_best_self.unwrap().0 < 10 { + data.target_state.best_finalized_peer_at_best_self = Some(HeaderId( + data.target_state.best_finalized_peer_at_best_self.unwrap().0 + 1, + data.target_state.best_finalized_peer_at_best_self.unwrap().0 + 1, + )); + } + if !data.submitted_messages_proofs.is_empty() { + exit_sender.unbounded_send(()).unwrap(); + } + }), + Arc::new(|_| {}), + exit_receiver.into_future().map(|(_, _)| ()), + ); + + assert_eq!(result.submitted_messages_proofs, vec![(1..=1, None)],); + } + + #[test] + fn message_lane_loop_is_able_to_recover_from_unsuccessful_transaction() { + // with this configuration, both source and target clients will mine their transactions, but + // their corresponding nonce won't be udpated => reconnect will happen + let (exit_sender, exit_receiver) = unbounded(); + let result = run_loop_test( + Arc::new(Mutex::new(TestClientData { + source_state: ClientState { + best_self: HeaderId(0, 0), + best_finalized_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + }, + source_latest_generated_nonce: 1, + target_state: ClientState { + best_self: HeaderId(0, 0), + best_finalized_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + }, + target_latest_received_nonce: 0, + ..Default::default() + })), + Arc::new(move |data: &mut TestClientData| { + // blocks are produced on every tick + data.source_state.best_self = + HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.1 + 1); + data.source_state.best_finalized_self = data.source_state.best_self; + // syncing target headers -> source chain + if let Some(last_requirement) = data.target_to_source_header_requirements.last() { + if *last_requirement != + data.source_state.best_finalized_peer_at_best_self.unwrap() + { + data.source_state.best_finalized_peer_at_best_self = + Some(*last_requirement); + } + } + }), + Arc::new(move |data: &mut TestClientData| { + // if it is the first time we're submitting delivery proof, let's revert changes + // to source status => then the delivery confirmation transaction is "finalized", + // but the state is not altered + if data.submitted_messages_receiving_proofs.len() == 1 { + data.source_latest_confirmed_received_nonce = 0; + } + }), + Arc::new(move |data: &mut TestClientData| { + // blocks are produced on every tick + data.target_state.best_self = + HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1); + data.target_state.best_finalized_self = data.target_state.best_self; + // syncing source headers -> target chain + if let Some(last_requirement) = data.source_to_target_header_requirements.last() { + if *last_requirement != + data.target_state.best_finalized_peer_at_best_self.unwrap() + { + data.target_state.best_finalized_peer_at_best_self = + Some(*last_requirement); + } + } + // if source has received all messages receiving confirmations => stop + if data.source_latest_confirmed_received_nonce == 1 { + exit_sender.unbounded_send(()).unwrap(); + } + }), + Arc::new(move |data: &mut TestClientData| { + // if it is the first time we're submitting messages proof, let's revert changes + // to target status => then the messages delivery transaction is "finalized", but + // the state is not altered + if data.submitted_messages_proofs.len() == 1 { + data.target_latest_received_nonce = 0; + data.target_latest_confirmed_received_nonce = 0; + } + }), + exit_receiver.into_future().map(|(_, _)| ()), + ); + + assert_eq!(result.submitted_messages_proofs.len(), 2); + assert_eq!(result.submitted_messages_receiving_proofs.len(), 2); + } + + #[test] + fn message_lane_loop_works() { + let (exit_sender, exit_receiver) = unbounded(); + let result = run_loop_test( + Arc::new(Mutex::new(TestClientData { + source_state: ClientState { + best_self: HeaderId(10, 10), + best_finalized_self: HeaderId(10, 10), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + }, + source_latest_generated_nonce: 10, + target_state: ClientState { + best_self: HeaderId(0, 0), + best_finalized_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + }, + target_latest_received_nonce: 0, + ..Default::default() + })), + Arc::new(|data: &mut TestClientData| { + // blocks are produced on every tick + data.source_state.best_self = + HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.1 + 1); + data.source_state.best_finalized_self = data.source_state.best_self; + // headers relay must only be started when we need new target headers at source node + if data.target_to_source_header_required.is_some() { + assert!( + data.source_state.best_finalized_peer_at_best_self.unwrap().0 < + data.target_state.best_self.0 + ); + data.target_to_source_header_required = None; + } + // syncing target headers -> source chain + if let Some(last_requirement) = data.target_to_source_header_requirements.last() { + if *last_requirement != + data.source_state.best_finalized_peer_at_best_self.unwrap() + { + data.source_state.best_finalized_peer_at_best_self = + Some(*last_requirement); + } + } + }), + Arc::new(|_| {}), + Arc::new(move |data: &mut TestClientData| { + // blocks are produced on every tick + data.target_state.best_self = + HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1); + data.target_state.best_finalized_self = data.target_state.best_self; + // headers relay must only be started when we need new source headers at target node + if data.source_to_target_header_required.is_some() { + assert!( + data.target_state.best_finalized_peer_at_best_self.unwrap().0 < + data.source_state.best_self.0 + ); + data.source_to_target_header_required = None; + } + // syncing source headers -> target chain + if let Some(last_requirement) = data.source_to_target_header_requirements.last() { + if *last_requirement != + data.target_state.best_finalized_peer_at_best_self.unwrap() + { + data.target_state.best_finalized_peer_at_best_self = + Some(*last_requirement); + } + } + // if source has received all messages receiving confirmations => stop + if data.source_latest_confirmed_received_nonce == 10 { + exit_sender.unbounded_send(()).unwrap(); + } + }), + Arc::new(|_| {}), + exit_receiver.into_future().map(|(_, _)| ()), + ); + + // there are no strict restrictions on when reward confirmation should come + // (because `max_unconfirmed_nonces_at_target` is `100` in tests and this confirmation + // depends on the state of both clients) + // => we do not check it here + assert_eq!(result.submitted_messages_proofs[0].0, 1..=4); + assert_eq!(result.submitted_messages_proofs[1].0, 5..=8); + assert_eq!(result.submitted_messages_proofs[2].0, 9..=10); + assert!(!result.submitted_messages_receiving_proofs.is_empty()); + + // check that we have at least once required new source->target or target->source headers + assert!(!result.target_to_source_header_requirements.is_empty()); + assert!(!result.source_to_target_header_requirements.is_empty()); + } + + #[test] + fn message_lane_loop_works_with_batch_transactions() { + let (exit_sender, exit_receiver) = unbounded(); + let original_data = Arc::new(Mutex::new(TestClientData { + source_state: ClientState { + best_self: HeaderId(10, 10), + best_finalized_self: HeaderId(10, 10), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + }, + source_latest_generated_nonce: 10, + target_state: ClientState { + best_self: HeaderId(0, 0), + best_finalized_self: HeaderId(0, 0), + best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), + }, + target_latest_received_nonce: 0, + ..Default::default() + })); + let result = run_loop_test( + original_data, + Arc::new(|_| {}), + Arc::new(move |data: &mut TestClientData| { + data.source_state.best_self = + HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.1 + 1); + data.source_state.best_finalized_self = data.source_state.best_self; + if let Some(target_to_source_header_required) = + data.target_to_source_header_required.take() + { + data.target_to_source_batch_transaction = + Some(TestConfirmationBatchTransaction { + required_header_id: target_to_source_header_required, + }) + } + }), + Arc::new(|_| {}), + Arc::new(move |data: &mut TestClientData| { + data.target_state.best_self = + HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1); + data.target_state.best_finalized_self = data.target_state.best_self; + + if let Some(source_to_target_header_required) = + data.source_to_target_header_required.take() + { + data.source_to_target_batch_transaction = Some(TestMessagesBatchTransaction { + required_header_id: source_to_target_header_required, + }) + } + + if data.source_latest_confirmed_received_nonce == 10 { + exit_sender.unbounded_send(()).unwrap(); + } + }), + exit_receiver.into_future().map(|(_, _)| ()), + ); + + // there are no strict restrictions on when reward confirmation should come + // (because `max_unconfirmed_nonces_at_target` is `100` in tests and this confirmation + // depends on the state of both clients) + // => we do not check it here + assert_eq!(result.submitted_messages_proofs[0].0, 1..=4); + assert_eq!(result.submitted_messages_proofs[1].0, 5..=8); + assert_eq!(result.submitted_messages_proofs[2].0, 9..=10); + assert!(!result.submitted_messages_receiving_proofs.is_empty()); + + // check that we have at least once required new source->target or target->source headers + assert!(!result.target_to_source_header_requirements.is_empty()); + assert!(!result.source_to_target_header_requirements.is_empty()); + } +} diff --git a/relays/messages/src/message_race_delivery.rs b/relays/messages/src/message_race_delivery.rs new file mode 100644 index 00000000000..7a245858b32 --- /dev/null +++ b/relays/messages/src/message_race_delivery.rs @@ -0,0 +1,1162 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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. + +//! Message delivery race delivers proof-of-messages from "lane.source" to "lane.target". + +use std::{collections::VecDeque, marker::PhantomData, ops::RangeInclusive}; + +use async_trait::async_trait; +use futures::stream::FusedStream; + +use bp_messages::{MessageNonce, UnrewardedRelayersState, Weight}; +use relay_utils::FailedClient; + +use crate::{ + message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, + message_lane_loop::{ + MessageDeliveryParams, MessageDetailsMap, MessageProofParameters, NoncesSubmitArtifacts, + SourceClient as MessageLaneSourceClient, SourceClientState, + TargetClient as MessageLaneTargetClient, TargetClientState, + }, + message_race_limits::{MessageRaceLimits, RelayMessagesBatchReference}, + message_race_loop::{ + MessageRace, NoncesRange, RaceState, RaceStrategy, SourceClient, SourceClientNonces, + TargetClient, TargetClientNonces, + }, + message_race_strategy::BasicStrategy, + metrics::MessageLaneLoopMetrics, +}; + +/// Run message delivery race. +pub async fn run( + source_client: impl MessageLaneSourceClient

, + source_state_updates: impl FusedStream>, + target_client: impl MessageLaneTargetClient

, + target_state_updates: impl FusedStream>, + metrics_msg: Option, + params: MessageDeliveryParams, +) -> Result<(), FailedClient> { + crate::message_race_loop::run( + MessageDeliveryRaceSource { + client: source_client.clone(), + metrics_msg: metrics_msg.clone(), + _phantom: Default::default(), + }, + source_state_updates, + MessageDeliveryRaceTarget { + client: target_client.clone(), + metrics_msg: metrics_msg.clone(), + _phantom: Default::default(), + }, + target_state_updates, + MessageDeliveryStrategy:: { + lane_source_client: source_client, + lane_target_client: target_client, + max_unrewarded_relayer_entries_at_target: params + .max_unrewarded_relayer_entries_at_target, + max_unconfirmed_nonces_at_target: params.max_unconfirmed_nonces_at_target, + max_messages_in_single_batch: params.max_messages_in_single_batch, + max_messages_weight_in_single_batch: params.max_messages_weight_in_single_batch, + max_messages_size_in_single_batch: params.max_messages_size_in_single_batch, + latest_confirmed_nonces_at_source: VecDeque::new(), + target_nonces: None, + strategy: BasicStrategy::new(), + metrics_msg, + }, + ) + .await +} + +/// Message delivery race. +struct MessageDeliveryRace

(std::marker::PhantomData

); + +impl MessageRace for MessageDeliveryRace

{ + type SourceHeaderId = SourceHeaderIdOf

; + type TargetHeaderId = TargetHeaderIdOf

; + + type MessageNonce = MessageNonce; + type Proof = P::MessagesProof; + + fn source_name() -> String { + format!("{}::MessagesDelivery", P::SOURCE_NAME) + } + + fn target_name() -> String { + format!("{}::MessagesDelivery", P::TARGET_NAME) + } +} + +/// Message delivery race source, which is a source of the lane. +struct MessageDeliveryRaceSource { + client: C, + metrics_msg: Option, + _phantom: PhantomData

, +} + +#[async_trait] +impl SourceClient> for MessageDeliveryRaceSource +where + P: MessageLane, + C: MessageLaneSourceClient

, +{ + type Error = C::Error; + type NoncesRange = MessageDetailsMap; + type ProofParameters = MessageProofParameters; + + async fn nonces( + &self, + at_block: SourceHeaderIdOf

, + prev_latest_nonce: MessageNonce, + ) -> Result<(SourceHeaderIdOf

, SourceClientNonces), Self::Error> { + let (at_block, latest_generated_nonce) = + self.client.latest_generated_nonce(at_block).await?; + let (at_block, latest_confirmed_nonce) = + self.client.latest_confirmed_received_nonce(at_block).await?; + + if let Some(metrics_msg) = self.metrics_msg.as_ref() { + metrics_msg.update_source_latest_generated_nonce(latest_generated_nonce); + metrics_msg.update_source_latest_confirmed_nonce(latest_confirmed_nonce); + } + + let new_nonces = if latest_generated_nonce > prev_latest_nonce { + self.client + .generated_message_details( + at_block.clone(), + prev_latest_nonce + 1..=latest_generated_nonce, + ) + .await? + } else { + MessageDetailsMap::new() + }; + + Ok(( + at_block, + SourceClientNonces { new_nonces, confirmed_nonce: Some(latest_confirmed_nonce) }, + )) + } + + async fn generate_proof( + &self, + at_block: SourceHeaderIdOf

, + nonces: RangeInclusive, + proof_parameters: Self::ProofParameters, + ) -> Result<(SourceHeaderIdOf

, RangeInclusive, P::MessagesProof), Self::Error> + { + self.client.prove_messages(at_block, nonces, proof_parameters).await + } +} + +/// Message delivery race target, which is a target of the lane. +struct MessageDeliveryRaceTarget { + client: C, + metrics_msg: Option, + _phantom: PhantomData

, +} + +#[async_trait] +impl TargetClient> for MessageDeliveryRaceTarget +where + P: MessageLane, + C: MessageLaneTargetClient

, +{ + type Error = C::Error; + type TargetNoncesData = DeliveryRaceTargetNoncesData; + type BatchTransaction = C::BatchTransaction; + type TransactionTracker = C::TransactionTracker; + + async fn require_source_header( + &self, + id: SourceHeaderIdOf

, + ) -> Result, Self::Error> { + self.client.require_source_header_on_target(id).await + } + + async fn nonces( + &self, + at_block: TargetHeaderIdOf

, + update_metrics: bool, + ) -> Result<(TargetHeaderIdOf

, TargetClientNonces), Self::Error> + { + let (at_block, latest_received_nonce) = self.client.latest_received_nonce(at_block).await?; + let (at_block, latest_confirmed_nonce) = + self.client.latest_confirmed_received_nonce(at_block).await?; + let (at_block, unrewarded_relayers) = + self.client.unrewarded_relayers_state(at_block).await?; + + if update_metrics { + if let Some(metrics_msg) = self.metrics_msg.as_ref() { + metrics_msg.update_target_latest_received_nonce(latest_received_nonce); + metrics_msg.update_target_latest_confirmed_nonce(latest_confirmed_nonce); + } + } + + Ok(( + at_block, + TargetClientNonces { + latest_nonce: latest_received_nonce, + nonces_data: DeliveryRaceTargetNoncesData { + confirmed_nonce: latest_confirmed_nonce, + unrewarded_relayers, + }, + }, + )) + } + + async fn submit_proof( + &self, + maybe_batch_tx: Option, + generated_at_block: SourceHeaderIdOf

, + nonces: RangeInclusive, + proof: P::MessagesProof, + ) -> Result, Self::Error> { + self.client + .submit_messages_proof(maybe_batch_tx, generated_at_block, nonces, proof) + .await + } +} + +/// Additional nonces data from the target client used by message delivery race. +#[derive(Debug, Clone)] +struct DeliveryRaceTargetNoncesData { + /// The latest nonce that we know: (1) has been delivered to us (2) has been confirmed + /// back to the source node (by confirmations race) and (3) relayer has received + /// reward for (and this has been confirmed by the message delivery race). + confirmed_nonce: MessageNonce, + /// State of the unrewarded relayers set at the target node. + unrewarded_relayers: UnrewardedRelayersState, +} + +/// Messages delivery strategy. +struct MessageDeliveryStrategy { + /// The client that is connected to the message lane source node. + lane_source_client: SC, + /// The client that is connected to the message lane target node. + lane_target_client: TC, + /// Maximal unrewarded relayer entries at target client. + max_unrewarded_relayer_entries_at_target: MessageNonce, + /// Maximal unconfirmed nonces at target client. + max_unconfirmed_nonces_at_target: MessageNonce, + /// Maximal number of messages in the single delivery transaction. + max_messages_in_single_batch: MessageNonce, + /// Maximal cumulative messages weight in the single delivery transaction. + max_messages_weight_in_single_batch: Weight, + /// Maximal messages size in the single delivery transaction. + max_messages_size_in_single_batch: u32, + /// Latest confirmed nonces at the source client + the header id where we have first met this + /// nonce. + latest_confirmed_nonces_at_source: VecDeque<(SourceHeaderIdOf

, MessageNonce)>, + /// Target nonces available at the **best** block of the target chain. + target_nonces: Option>, + /// Basic delivery strategy. + strategy: MessageDeliveryStrategyBase

, + /// Message lane metrics. + metrics_msg: Option, +} + +type MessageDeliveryStrategyBase

= BasicStrategy< +

::SourceHeaderNumber, +

::SourceHeaderHash, +

::TargetHeaderNumber, +

::TargetHeaderHash, + MessageDetailsMap<

::SourceChainBalance>, +

::MessagesProof, +>; + +impl std::fmt::Debug for MessageDeliveryStrategy { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.debug_struct("MessageDeliveryStrategy") + .field( + "max_unrewarded_relayer_entries_at_target", + &self.max_unrewarded_relayer_entries_at_target, + ) + .field("max_unconfirmed_nonces_at_target", &self.max_unconfirmed_nonces_at_target) + .field("max_messages_in_single_batch", &self.max_messages_in_single_batch) + .field("max_messages_weight_in_single_batch", &self.max_messages_weight_in_single_batch) + .field("max_messages_size_in_single_batch", &self.max_messages_size_in_single_batch) + .field("latest_confirmed_nonces_at_source", &self.latest_confirmed_nonces_at_source) + .field("target_nonces", &self.target_nonces) + .field("strategy", &self.strategy) + .finish() + } +} + +impl MessageDeliveryStrategy { + /// Returns total weight of all undelivered messages. + fn dispatch_weight_for_range(&self, range: &RangeInclusive) -> Weight { + self.strategy + .source_queue() + .iter() + .flat_map(|(_, subrange)| { + subrange + .iter() + .filter(|(nonce, _)| range.contains(nonce)) + .map(|(_, details)| details.dispatch_weight) + }) + .fold(Weight::zero(), |total, weight| total.saturating_add(weight)) + } +} + +#[async_trait] +impl RaceStrategy, TargetHeaderIdOf

, P::MessagesProof> + for MessageDeliveryStrategy +where + P: MessageLane, + SC: MessageLaneSourceClient

, + TC: MessageLaneTargetClient

, +{ + type SourceNoncesRange = MessageDetailsMap; + type ProofParameters = MessageProofParameters; + type TargetNoncesData = DeliveryRaceTargetNoncesData; + + fn is_empty(&self) -> bool { + self.strategy.is_empty() + } + + fn required_source_header_at_target, TargetHeaderIdOf

>>( + &self, + current_best: &SourceHeaderIdOf

, + race_state: RS, + ) -> Option> { + // we have already submitted something - let's wait until it is mined + if race_state.nonces_submitted().is_some() { + return None + } + + let has_nonces_to_deliver = !self.strategy.is_empty(); + let header_required_for_messages_delivery = + self.strategy.required_source_header_at_target(current_best, race_state); + let header_required_for_reward_confirmations_delivery = self + .latest_confirmed_nonces_at_source + .back() + .filter(|(id, nonce)| *nonce != 0 && id.0 > current_best.0) + .map(|(id, _)| id.clone()); + match ( + has_nonces_to_deliver, + header_required_for_messages_delivery, + header_required_for_reward_confirmations_delivery, + ) { + // if we need to delver messages and proof-of-delivery-confirmations, then we need to + // select the most recent header to avoid extra roundtrips + (true, Some(id1), Some(id2)) => Some(if id1.0 > id2.0 { id1 } else { id2 }), + // if we only need to deliver messages - fine, let's require some source header + // + // if we need new header for proof-of-delivery-confirmations - let's also ask for that. + // Even though it may require additional header, we'll be sure that we won't block the + // lane (sometimes we can't deliver messages without proof-of-delivery-confirmations) + (true, a, b) => a.or(b), + // we never submit delivery transaction without messages, so if `has_nonces_to_deliver` + // if `false`, we don't need any source headers at target + (false, _, _) => None, + } + } + + fn best_at_source(&self) -> Option { + self.strategy.best_at_source() + } + + fn best_at_target(&self) -> Option { + self.strategy.best_at_target() + } + + fn source_nonces_updated( + &mut self, + at_block: SourceHeaderIdOf

, + nonces: SourceClientNonces, + ) { + if let Some(confirmed_nonce) = nonces.confirmed_nonce { + let is_confirmed_nonce_updated = self + .latest_confirmed_nonces_at_source + .back() + .map(|(_, prev_nonce)| *prev_nonce != confirmed_nonce) + .unwrap_or(true); + if is_confirmed_nonce_updated { + self.latest_confirmed_nonces_at_source + .push_back((at_block.clone(), confirmed_nonce)); + } + } + self.strategy.source_nonces_updated(at_block, nonces) + } + + fn best_target_nonces_updated, TargetHeaderIdOf

>>( + &mut self, + nonces: TargetClientNonces, + race_state: &mut RS, + ) { + // best target nonces must always be ge than finalized target nonces + let latest_nonce = nonces.latest_nonce; + self.target_nonces = Some(nonces); + + self.strategy.best_target_nonces_updated( + TargetClientNonces { latest_nonce, nonces_data: () }, + race_state, + ) + } + + fn finalized_target_nonces_updated, TargetHeaderIdOf

>>( + &mut self, + nonces: TargetClientNonces, + race_state: &mut RS, + ) { + if let Some(ref best_finalized_source_header_id_at_best_target) = + race_state.best_finalized_source_header_id_at_best_target() + { + let oldest_header_number_to_keep = best_finalized_source_header_id_at_best_target.0; + while self + .latest_confirmed_nonces_at_source + .front() + .map(|(id, _)| id.0 < oldest_header_number_to_keep) + .unwrap_or(false) + { + self.latest_confirmed_nonces_at_source.pop_front(); + } + } + + if let Some(ref mut target_nonces) = self.target_nonces { + target_nonces.latest_nonce = + std::cmp::max(target_nonces.latest_nonce, nonces.latest_nonce); + } + + self.strategy.finalized_target_nonces_updated( + TargetClientNonces { latest_nonce: nonces.latest_nonce, nonces_data: () }, + race_state, + ) + } + + async fn select_nonces_to_deliver, TargetHeaderIdOf

>>( + &self, + race_state: RS, + ) -> Option<(RangeInclusive, Self::ProofParameters)> { + let best_target_nonce = self.strategy.best_at_target()?; + let best_finalized_source_header_id_at_best_target = + race_state.best_finalized_source_header_id_at_best_target()?; + let latest_confirmed_nonce_at_source = self + .latest_confirmed_nonces_at_source + .iter() + .take_while(|(id, _)| id.0 <= best_finalized_source_header_id_at_best_target.0) + .last() + .map(|(_, nonce)| *nonce) + .unwrap_or(best_target_nonce); + let target_nonces = self.target_nonces.as_ref()?; + + // There's additional condition in the message delivery race: target would reject messages + // if there are too much unconfirmed messages at the inbound lane. + + // The receiving race is responsible to deliver confirmations back to the source chain. So + // if there's a lot of unconfirmed messages, let's wait until it'll be able to do its job. + let latest_received_nonce_at_target = target_nonces.latest_nonce; + let confirmations_missing = + latest_received_nonce_at_target.checked_sub(latest_confirmed_nonce_at_source); + match confirmations_missing { + Some(confirmations_missing) + if confirmations_missing >= self.max_unconfirmed_nonces_at_target => + { + log::debug!( + target: "bridge", + "Cannot deliver any more messages from {} to {}. Too many unconfirmed nonces \ + at target: target.latest_received={:?}, source.latest_confirmed={:?}, max={:?}", + MessageDeliveryRace::

::source_name(), + MessageDeliveryRace::

::target_name(), + latest_received_nonce_at_target, + latest_confirmed_nonce_at_source, + self.max_unconfirmed_nonces_at_target, + ); + + return None + }, + _ => (), + } + + // Ok - we may have new nonces to deliver. But target may still reject new messages, because + // we haven't notified it that (some) messages have been confirmed. So we may want to + // include updated `source.latest_confirmed` in the proof. + // + // Important note: we're including outbound state lane proof whenever there are unconfirmed + // nonces on the target chain. Other strategy is to include it only if it's absolutely + // necessary. + let latest_confirmed_nonce_at_target = target_nonces.nonces_data.confirmed_nonce; + let outbound_state_proof_required = + latest_confirmed_nonce_at_target < latest_confirmed_nonce_at_source; + + // The target node would also reject messages if there are too many entries in the + // "unrewarded relayers" set. If we are unable to prove new rewards to the target node, then + // we should wait for confirmations race. + let unrewarded_relayer_entries_limit_reached = + target_nonces.nonces_data.unrewarded_relayers.unrewarded_relayer_entries >= + self.max_unrewarded_relayer_entries_at_target; + if unrewarded_relayer_entries_limit_reached { + // so there are already too many unrewarded relayer entries in the set + // + // => check if we can prove enough rewards. If not, we should wait for more rewards to + // be paid + let number_of_rewards_being_proved = + latest_confirmed_nonce_at_source.saturating_sub(latest_confirmed_nonce_at_target); + let enough_rewards_being_proved = number_of_rewards_being_proved >= + target_nonces.nonces_data.unrewarded_relayers.messages_in_oldest_entry; + if !enough_rewards_being_proved { + return None + } + } + + // If we're here, then the confirmations race did its job && sending side now knows that + // messages have been delivered. Now let's select nonces that we want to deliver. + // + // We may deliver at most: + // + // max_unconfirmed_nonces_at_target - (latest_received_nonce_at_target - + // latest_confirmed_nonce_at_target) + // + // messages in the batch. But since we're including outbound state proof in the batch, then + // it may be increased to: + // + // max_unconfirmed_nonces_at_target - (latest_received_nonce_at_target - + // latest_confirmed_nonce_at_source) + let future_confirmed_nonce_at_target = if outbound_state_proof_required { + latest_confirmed_nonce_at_source + } else { + latest_confirmed_nonce_at_target + }; + let max_nonces = latest_received_nonce_at_target + .checked_sub(future_confirmed_nonce_at_target) + .and_then(|diff| self.max_unconfirmed_nonces_at_target.checked_sub(diff)) + .unwrap_or_default(); + let max_nonces = std::cmp::min(max_nonces, self.max_messages_in_single_batch); + let max_messages_weight_in_single_batch = self.max_messages_weight_in_single_batch; + let max_messages_size_in_single_batch = self.max_messages_size_in_single_batch; + let lane_source_client = self.lane_source_client.clone(); + let lane_target_client = self.lane_target_client.clone(); + + let available_source_queue_indices = + self.strategy.available_source_queue_indices(race_state)?; + let source_queue = self.strategy.source_queue(); + + let reference = RelayMessagesBatchReference { + max_messages_in_this_batch: max_nonces, + max_messages_weight_in_single_batch, + max_messages_size_in_single_batch, + lane_source_client: lane_source_client.clone(), + lane_target_client: lane_target_client.clone(), + best_target_nonce, + nonces_queue: source_queue.clone(), + nonces_queue_range: available_source_queue_indices, + metrics: self.metrics_msg.clone(), + }; + + let selected_nonces = MessageRaceLimits::decide(reference).await?; + let dispatch_weight = self.dispatch_weight_for_range(&selected_nonces); + + Some(( + selected_nonces, + MessageProofParameters { outbound_state_proof_required, dispatch_weight }, + )) + } +} + +impl NoncesRange for MessageDetailsMap { + fn begin(&self) -> MessageNonce { + self.keys().next().cloned().unwrap_or_default() + } + + fn end(&self) -> MessageNonce { + self.keys().next_back().cloned().unwrap_or_default() + } + + fn greater_than(mut self, nonce: MessageNonce) -> Option { + let gte = self.split_off(&(nonce + 1)); + if gte.is_empty() { + None + } else { + Some(gte) + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + message_lane_loop::{ + tests::{ + header_id, TestMessageLane, TestMessagesBatchTransaction, TestMessagesProof, + TestSourceChainBalance, TestSourceClient, TestSourceHeaderId, TestTargetClient, + TestTargetHeaderId, + }, + MessageDetails, + }, + message_race_loop::RaceStateImpl, + }; + + use super::*; + + const DEFAULT_DISPATCH_WEIGHT: Weight = Weight::from_parts(1, 0); + const DEFAULT_SIZE: u32 = 1; + + type TestRaceState = RaceStateImpl< + TestSourceHeaderId, + TestTargetHeaderId, + TestMessagesProof, + TestMessagesBatchTransaction, + >; + type TestStrategy = + MessageDeliveryStrategy; + + fn source_nonces( + new_nonces: RangeInclusive, + confirmed_nonce: MessageNonce, + reward: TestSourceChainBalance, + ) -> SourceClientNonces> { + SourceClientNonces { + new_nonces: new_nonces + .into_iter() + .map(|nonce| { + ( + nonce, + MessageDetails { + dispatch_weight: DEFAULT_DISPATCH_WEIGHT, + size: DEFAULT_SIZE, + reward, + }, + ) + }) + .collect(), + confirmed_nonce: Some(confirmed_nonce), + } + } + + fn prepare_strategy() -> (TestRaceState, TestStrategy) { + let mut race_state = RaceStateImpl { + best_finalized_source_header_id_at_source: Some(header_id(1)), + best_finalized_source_header_id_at_best_target: Some(header_id(1)), + best_target_header_id: Some(header_id(1)), + best_finalized_target_header_id: Some(header_id(1)), + nonces_to_submit: None, + nonces_to_submit_batch: None, + nonces_submitted: None, + }; + + let mut race_strategy = TestStrategy { + max_unrewarded_relayer_entries_at_target: 4, + max_unconfirmed_nonces_at_target: 4, + max_messages_in_single_batch: 4, + max_messages_weight_in_single_batch: Weight::from_parts(4, 0), + max_messages_size_in_single_batch: 4, + latest_confirmed_nonces_at_source: vec![(header_id(1), 19)].into_iter().collect(), + lane_source_client: TestSourceClient::default(), + lane_target_client: TestTargetClient::default(), + metrics_msg: None, + target_nonces: Some(TargetClientNonces { + latest_nonce: 19, + nonces_data: DeliveryRaceTargetNoncesData { + confirmed_nonce: 19, + unrewarded_relayers: UnrewardedRelayersState { + unrewarded_relayer_entries: 0, + messages_in_oldest_entry: 0, + total_messages: 0, + last_delivered_nonce: 0, + }, + }, + }), + strategy: BasicStrategy::new(), + }; + + race_strategy + .strategy + .source_nonces_updated(header_id(1), source_nonces(20..=23, 19, 0)); + + let target_nonces = TargetClientNonces { latest_nonce: 19, nonces_data: () }; + race_strategy + .strategy + .best_target_nonces_updated(target_nonces.clone(), &mut race_state); + race_strategy + .strategy + .finalized_target_nonces_updated(target_nonces, &mut race_state); + + (race_state, race_strategy) + } + + fn proof_parameters(state_required: bool, weight: u32) -> MessageProofParameters { + MessageProofParameters { + outbound_state_proof_required: state_required, + dispatch_weight: Weight::from_parts(weight as u64, 0), + } + } + + #[test] + fn weights_map_works_as_nonces_range() { + fn build_map( + range: RangeInclusive, + ) -> MessageDetailsMap { + range + .map(|idx| { + ( + idx, + MessageDetails { + dispatch_weight: Weight::from_parts(idx, 0), + size: idx as _, + reward: idx as _, + }, + ) + }) + .collect() + } + + let map = build_map(20..=30); + + assert_eq!(map.begin(), 20); + assert_eq!(map.end(), 30); + assert_eq!(map.clone().greater_than(10), Some(build_map(20..=30))); + assert_eq!(map.clone().greater_than(19), Some(build_map(20..=30))); + assert_eq!(map.clone().greater_than(20), Some(build_map(21..=30))); + assert_eq!(map.clone().greater_than(25), Some(build_map(26..=30))); + assert_eq!(map.clone().greater_than(29), Some(build_map(30..=30))); + assert_eq!(map.greater_than(30), None); + } + + #[async_std::test] + async fn message_delivery_strategy_selects_messages_to_deliver() { + let (state, strategy) = prepare_strategy(); + + // both sides are ready to relay new messages + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=23), proof_parameters(false, 4))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_selects_nothing_if_too_many_confirmations_missing() { + let (state, mut strategy) = prepare_strategy(); + + // if there are already `max_unconfirmed_nonces_at_target` messages on target, + // we need to wait until confirmations will be delivered by receiving race + strategy.latest_confirmed_nonces_at_source = vec![( + header_id(1), + strategy.target_nonces.as_ref().unwrap().latest_nonce - + strategy.max_unconfirmed_nonces_at_target, + )] + .into_iter() + .collect(); + assert_eq!(strategy.select_nonces_to_deliver(state).await, None); + } + + #[async_std::test] + async fn message_delivery_strategy_includes_outbound_state_proof_when_new_nonces_are_available() + { + let (state, mut strategy) = prepare_strategy(); + + // if there are new confirmed nonces on source, we want to relay this information + // to target to prune rewards queue + let prev_confirmed_nonce_at_source = + strategy.latest_confirmed_nonces_at_source.back().unwrap().1; + strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = + prev_confirmed_nonce_at_source - 1; + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=23), proof_parameters(true, 4))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_selects_nothing_if_there_are_too_many_unrewarded_relayers() { + let (state, mut strategy) = prepare_strategy(); + + // if there are already `max_unrewarded_relayer_entries_at_target` entries at target, + // we need to wait until rewards will be paid + { + let mut unrewarded_relayers = + &mut strategy.target_nonces.as_mut().unwrap().nonces_data.unrewarded_relayers; + unrewarded_relayers.unrewarded_relayer_entries = + strategy.max_unrewarded_relayer_entries_at_target; + unrewarded_relayers.messages_in_oldest_entry = 4; + } + assert_eq!(strategy.select_nonces_to_deliver(state).await, None); + } + + #[async_std::test] + async fn message_delivery_strategy_selects_nothing_if_proved_rewards_is_not_enough_to_remove_oldest_unrewarded_entry( + ) { + let (state, mut strategy) = prepare_strategy(); + + // if there are already `max_unrewarded_relayer_entries_at_target` entries at target, + // we need to prove at least `messages_in_oldest_entry` rewards + let prev_confirmed_nonce_at_source = + strategy.latest_confirmed_nonces_at_source.back().unwrap().1; + { + let mut nonces_data = &mut strategy.target_nonces.as_mut().unwrap().nonces_data; + nonces_data.confirmed_nonce = prev_confirmed_nonce_at_source - 1; + let mut unrewarded_relayers = &mut nonces_data.unrewarded_relayers; + unrewarded_relayers.unrewarded_relayer_entries = + strategy.max_unrewarded_relayer_entries_at_target; + unrewarded_relayers.messages_in_oldest_entry = 4; + } + assert_eq!(strategy.select_nonces_to_deliver(state).await, None); + } + + #[async_std::test] + async fn message_delivery_strategy_includes_outbound_state_proof_if_proved_rewards_is_enough() { + let (state, mut strategy) = prepare_strategy(); + + // if there are already `max_unrewarded_relayer_entries_at_target` entries at target, + // we need to prove at least `messages_in_oldest_entry` rewards + let prev_confirmed_nonce_at_source = + strategy.latest_confirmed_nonces_at_source.back().unwrap().1; + { + let mut nonces_data = &mut strategy.target_nonces.as_mut().unwrap().nonces_data; + nonces_data.confirmed_nonce = prev_confirmed_nonce_at_source - 3; + let mut unrewarded_relayers = &mut nonces_data.unrewarded_relayers; + unrewarded_relayers.unrewarded_relayer_entries = + strategy.max_unrewarded_relayer_entries_at_target; + unrewarded_relayers.messages_in_oldest_entry = 3; + } + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=23), proof_parameters(true, 4))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_limits_batch_by_messages_weight() { + let (state, mut strategy) = prepare_strategy(); + + // not all queued messages may fit in the batch, because batch has max weight + strategy.max_messages_weight_in_single_batch = Weight::from_parts(3, 0); + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=22), proof_parameters(false, 3))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_accepts_single_message_even_if_its_weight_overflows_maximal_weight( + ) { + let (state, mut strategy) = prepare_strategy(); + + // first message doesn't fit in the batch, because it has weight (10) that overflows max + // weight (4) + strategy.strategy.source_queue_mut()[0].1.get_mut(&20).unwrap().dispatch_weight = + Weight::from_parts(10, 0); + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=20), proof_parameters(false, 10))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_limits_batch_by_messages_size() { + let (state, mut strategy) = prepare_strategy(); + + // not all queued messages may fit in the batch, because batch has max weight + strategy.max_messages_size_in_single_batch = 3; + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=22), proof_parameters(false, 3))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_accepts_single_message_even_if_its_weight_overflows_maximal_size( + ) { + let (state, mut strategy) = prepare_strategy(); + + // first message doesn't fit in the batch, because it has weight (10) that overflows max + // weight (4) + strategy.strategy.source_queue_mut()[0].1.get_mut(&20).unwrap().size = 10; + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=20), proof_parameters(false, 1))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_limits_batch_by_messages_count_when_there_is_upper_limit() { + let (state, mut strategy) = prepare_strategy(); + + // not all queued messages may fit in the batch, because batch has max number of messages + // limit + strategy.max_messages_in_single_batch = 3; + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=22), proof_parameters(false, 3))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_limits_batch_by_messages_count_when_there_are_unconfirmed_nonces( + ) { + let (state, mut strategy) = prepare_strategy(); + + // 1 delivery confirmation from target to source is still missing, so we may only + // relay 3 new messages + let prev_confirmed_nonce_at_source = + strategy.latest_confirmed_nonces_at_source.back().unwrap().1; + strategy.latest_confirmed_nonces_at_source = + vec![(header_id(1), prev_confirmed_nonce_at_source - 1)].into_iter().collect(); + strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = + prev_confirmed_nonce_at_source - 1; + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=22), proof_parameters(false, 3))) + ); + } + + #[async_std::test] + async fn message_delivery_strategy_waits_for_confirmed_nonce_header_to_appear_on_target() { + // 1 delivery confirmation from target to source is still missing, so we may deliver + // reward confirmation with our message delivery transaction. But the problem is that + // the reward has been paid at header 2 && this header is still unknown to target node. + // + // => so we can't deliver more than 3 messages + let (mut state, mut strategy) = prepare_strategy(); + let prev_confirmed_nonce_at_source = + strategy.latest_confirmed_nonces_at_source.back().unwrap().1; + strategy.latest_confirmed_nonces_at_source = vec![ + (header_id(1), prev_confirmed_nonce_at_source - 1), + (header_id(2), prev_confirmed_nonce_at_source), + ] + .into_iter() + .collect(); + strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = + prev_confirmed_nonce_at_source - 1; + state.best_finalized_source_header_id_at_best_target = Some(header_id(1)); + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=22), proof_parameters(false, 3))) + ); + + // the same situation, but the header 2 is known to the target node, so we may deliver + // reward confirmation + let (mut state, mut strategy) = prepare_strategy(); + let prev_confirmed_nonce_at_source = + strategy.latest_confirmed_nonces_at_source.back().unwrap().1; + strategy.latest_confirmed_nonces_at_source = vec![ + (header_id(1), prev_confirmed_nonce_at_source - 1), + (header_id(2), prev_confirmed_nonce_at_source), + ] + .into_iter() + .collect(); + strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = + prev_confirmed_nonce_at_source - 1; + state.best_finalized_source_header_id_at_source = Some(header_id(2)); + state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=23), proof_parameters(true, 4))) + ); + } + + #[async_std::test] + async fn source_header_is_required_when_confirmations_are_required() { + // let's prepare situation when: + // - all messages [20; 23] have been generated at source block#1; + let (mut state, mut strategy) = prepare_strategy(); + // + // - messages [20; 23] have been delivered + assert_eq!( + strategy.select_nonces_to_deliver(state.clone()).await, + Some(((20..=23), proof_parameters(false, 4))) + ); + strategy.finalized_target_nonces_updated( + TargetClientNonces { + latest_nonce: 23, + nonces_data: DeliveryRaceTargetNoncesData { + confirmed_nonce: 19, + unrewarded_relayers: UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 4, + total_messages: 4, + last_delivered_nonce: 23, + }, + }, + }, + &mut state, + ); + // nothing needs to be delivered now and we don't need any new headers + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); + assert_eq!(strategy.required_source_header_at_target(&header_id(1), state.clone()), None); + + // now let's generate two more nonces [24; 25] at the soruce; + strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 19, 0)); + // + // - so now we'll need to relay source block#2 to be able to accept messages [24; 25]. + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); + assert_eq!( + strategy.required_source_header_at_target(&header_id(1), state.clone()), + Some(header_id(2)) + ); + + // let's relay source block#2 + state.best_finalized_source_header_id_at_source = Some(header_id(2)); + state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); + state.best_target_header_id = Some(header_id(2)); + state.best_finalized_target_header_id = Some(header_id(2)); + + // and ask strategy again => still nothing to deliver, because parallel confirmations + // race need to be pushed further + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); + assert_eq!(strategy.required_source_header_at_target(&header_id(2), state.clone()), None); + + // let's confirm messages [20; 23] + strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 23, 0)); + + // and ask strategy again => now we have everything required to deliver remaining + // [24; 25] nonces and proof of [20; 23] confirmation + assert_eq!( + strategy.select_nonces_to_deliver(state.clone()).await, + Some(((24..=25), proof_parameters(true, 2))), + ); + assert_eq!(strategy.required_source_header_at_target(&header_id(2), state), None); + } + + #[async_std::test] + async fn relayer_uses_flattened_view_of_the_source_queue_to_select_nonces() { + // Real scenario that has happened on test deployments: + // 1) relayer witnessed M1 at block 1 => it has separate entry in the `source_queue` + // 2) relayer witnessed M2 at block 2 => it has separate entry in the `source_queue` + // 3) if block 2 is known to the target node, then both M1 and M2 are selected for single + // delivery, even though weight(M1+M2) > larger than largest allowed weight + // + // This was happening because selector (`select_nonces_for_delivery_transaction`) has been + // called for every `source_queue` entry separately without preserving any context. + let (mut state, mut strategy) = prepare_strategy(); + let nonces = source_nonces(24..=25, 19, 0); + strategy.strategy.source_nonces_updated(header_id(2), nonces); + strategy.max_unrewarded_relayer_entries_at_target = 100; + strategy.max_unconfirmed_nonces_at_target = 100; + strategy.max_messages_in_single_batch = 5; + strategy.max_messages_weight_in_single_batch = Weight::from_parts(100, 0); + strategy.max_messages_size_in_single_batch = 100; + state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); + + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((20..=24), proof_parameters(false, 5))) + ); + } + + #[test] + #[allow(clippy::reversed_empty_ranges)] + fn no_source_headers_required_at_target_if_lanes_are_empty() { + let (state, _) = prepare_strategy(); + let mut strategy = TestStrategy { + max_unrewarded_relayer_entries_at_target: 4, + max_unconfirmed_nonces_at_target: 4, + max_messages_in_single_batch: 4, + max_messages_weight_in_single_batch: Weight::from_parts(4, 0), + max_messages_size_in_single_batch: 4, + latest_confirmed_nonces_at_source: VecDeque::new(), + lane_source_client: TestSourceClient::default(), + lane_target_client: TestTargetClient::default(), + metrics_msg: None, + target_nonces: None, + strategy: BasicStrategy::new(), + }; + + let source_header_id = header_id(10); + strategy.source_nonces_updated( + source_header_id, + // MessageDeliveryRaceSource::nonces returns Some(0), because that's how it is + // represented in memory (there's no Options in OutboundLaneState) + source_nonces(1u64..=0u64, 0, 0), + ); + + // even though `latest_confirmed_nonces_at_source` is not empty, new headers are not + // requested + assert_eq!( + strategy.latest_confirmed_nonces_at_source, + VecDeque::from([(source_header_id, 0)]) + ); + assert_eq!(strategy.required_source_header_at_target(&source_header_id, state), None); + } + + #[async_std::test] + async fn previous_nonces_are_selected_if_reorg_happens_at_target_chain() { + // this is the copy of the similar test in the `mesage_race_strategy.rs`, but it also tests + // that the `MessageDeliveryStrategy` acts properly in the similar scenario + + // tune parameters to allow 5 nonces per delivery transaction + let (mut state, mut strategy) = prepare_strategy(); + strategy.max_unrewarded_relayer_entries_at_target = 5; + strategy.max_unconfirmed_nonces_at_target = 5; + strategy.max_messages_in_single_batch = 5; + strategy.max_messages_weight_in_single_batch = Weight::from_parts(5, 0); + strategy.max_messages_size_in_single_batch = 5; + + // in this state we have 4 available nonces for delivery + assert_eq!( + strategy.select_nonces_to_deliver(state.clone()).await, + Some(( + 20..=23, + MessageProofParameters { + outbound_state_proof_required: false, + dispatch_weight: Weight::from_parts(4, 0), + } + )), + ); + + // let's say we have submitted 20..=23 + state.nonces_submitted = Some(20..=23); + + // then new nonce 24 appear at the source block 2 + let new_nonce_24 = vec![( + 24, + MessageDetails { dispatch_weight: Weight::from_parts(1, 0), size: 0, reward: 0 }, + )] + .into_iter() + .collect(); + let source_header_2 = header_id(2); + state.best_finalized_source_header_id_at_source = Some(source_header_2); + strategy.source_nonces_updated( + source_header_2, + SourceClientNonces { new_nonces: new_nonce_24, confirmed_nonce: None }, + ); + // and nonce 23 appear at the best block of the target node (best finalized still has 0 + // nonces) + let target_nonces_data = DeliveryRaceTargetNoncesData { + confirmed_nonce: 19, + unrewarded_relayers: UnrewardedRelayersState::default(), + }; + let target_header_2 = header_id(2); + state.best_target_header_id = Some(target_header_2); + strategy.best_target_nonces_updated( + TargetClientNonces { latest_nonce: 23, nonces_data: target_nonces_data.clone() }, + &mut state, + ); + + // then best target header is retracted + strategy.best_target_nonces_updated( + TargetClientNonces { latest_nonce: 19, nonces_data: target_nonces_data.clone() }, + &mut state, + ); + + // ... and some fork with 19 delivered nonces is finalized + let target_header_2_fork = header_id(2_1); + state.best_finalized_source_header_id_at_source = Some(source_header_2); + state.best_finalized_source_header_id_at_best_target = Some(source_header_2); + state.best_target_header_id = Some(target_header_2_fork); + state.best_finalized_target_header_id = Some(target_header_2_fork); + strategy.finalized_target_nonces_updated( + TargetClientNonces { latest_nonce: 19, nonces_data: target_nonces_data.clone() }, + &mut state, + ); + + // now we have to select nonces 20..=23 for delivery again + assert_eq!( + strategy.select_nonces_to_deliver(state.clone()).await, + Some(( + 20..=24, + MessageProofParameters { + outbound_state_proof_required: false, + dispatch_weight: Weight::from_parts(5, 0), + } + )), + ); + } +} diff --git a/relays/messages/src/message_race_limits.rs b/relays/messages/src/message_race_limits.rs new file mode 100644 index 00000000000..873bb6aad04 --- /dev/null +++ b/relays/messages/src/message_race_limits.rs @@ -0,0 +1,206 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! enforcement strategy + +use num_traits::Zero; +use std::ops::RangeInclusive; + +use bp_messages::{MessageNonce, Weight}; + +use crate::{ + message_lane::MessageLane, + message_lane_loop::{ + MessageDetails, MessageDetailsMap, SourceClient as MessageLaneSourceClient, + TargetClient as MessageLaneTargetClient, + }, + message_race_loop::NoncesRange, + message_race_strategy::SourceRangesQueue, + metrics::MessageLaneLoopMetrics, +}; + +/// Reference data for participating in relay +pub struct RelayReference< + P: MessageLane, + SourceClient: MessageLaneSourceClient

, + TargetClient: MessageLaneTargetClient

, +> { + /// The client that is connected to the message lane source node. + pub lane_source_client: SourceClient, + /// The client that is connected to the message lane target node. + pub lane_target_client: TargetClient, + /// Metrics reference. + pub metrics: Option, + /// Messages size summary + pub selected_size: u32, + + /// Hard check begin nonce + pub hard_selected_begin_nonce: MessageNonce, + + /// Index by all ready nonces + pub index: usize, + /// Current nonce + pub nonce: MessageNonce, + /// Current nonce details + pub details: MessageDetails, +} + +/// Relay reference data +pub struct RelayMessagesBatchReference< + P: MessageLane, + SourceClient: MessageLaneSourceClient

, + TargetClient: MessageLaneTargetClient

, +> { + /// Maximal number of relayed messages in single delivery transaction. + pub max_messages_in_this_batch: MessageNonce, + /// Maximal cumulative dispatch weight of relayed messages in single delivery transaction. + pub max_messages_weight_in_single_batch: Weight, + /// Maximal cumulative size of relayed messages in single delivery transaction. + pub max_messages_size_in_single_batch: u32, + /// The client that is connected to the message lane source node. + pub lane_source_client: SourceClient, + /// The client that is connected to the message lane target node. + pub lane_target_client: TargetClient, + /// Metrics reference. + pub metrics: Option, + /// Best available nonce at the **best** target block. We do not want to deliver nonces + /// less than this nonce, even though the block may be retracted. + pub best_target_nonce: MessageNonce, + /// Source queue. + pub nonces_queue: SourceRangesQueue< + P::SourceHeaderHash, + P::SourceHeaderNumber, + MessageDetailsMap, + >, + /// Range of indices within the `nonces_queue` that are available for selection. + pub nonces_queue_range: RangeInclusive, +} + +/// Limits of the message race transactions. +#[derive(Clone)] +pub struct MessageRaceLimits; + +impl MessageRaceLimits { + pub async fn decide< + P: MessageLane, + SourceClient: MessageLaneSourceClient

, + TargetClient: MessageLaneTargetClient

, + >( + reference: RelayMessagesBatchReference, + ) -> Option> { + let mut hard_selected_count = 0; + + let mut selected_weight = Weight::zero(); + let mut selected_count: MessageNonce = 0; + + let hard_selected_begin_nonce = std::cmp::max( + reference.best_target_nonce + 1, + reference.nonces_queue[*reference.nonces_queue_range.start()].1.begin(), + ); + + // relay reference + let mut relay_reference = RelayReference { + lane_source_client: reference.lane_source_client.clone(), + lane_target_client: reference.lane_target_client.clone(), + metrics: reference.metrics.clone(), + + selected_size: 0, + + hard_selected_begin_nonce, + + index: 0, + nonce: 0, + details: MessageDetails { + dispatch_weight: Weight::zero(), + size: 0, + reward: P::SourceChainBalance::zero(), + }, + }; + + let all_ready_nonces = reference + .nonces_queue + .range(reference.nonces_queue_range.clone()) + .flat_map(|(_, ready_nonces)| ready_nonces.iter()) + .filter(|(nonce, _)| **nonce >= hard_selected_begin_nonce) + .enumerate(); + for (index, (nonce, details)) in all_ready_nonces { + relay_reference.index = index; + relay_reference.nonce = *nonce; + relay_reference.details = *details; + + // Since we (hopefully) have some reserves in `max_messages_weight_in_single_batch` + // and `max_messages_size_in_single_batch`, we may still try to submit transaction + // with single message if message overflows these limits. The worst case would be if + // transaction will be rejected by the target runtime, but at least we have tried. + + // limit messages in the batch by weight + let new_selected_weight = match selected_weight.checked_add(&details.dispatch_weight) { + Some(new_selected_weight) + if new_selected_weight + .all_lte(reference.max_messages_weight_in_single_batch) => + new_selected_weight, + new_selected_weight if selected_count == 0 => { + log::warn!( + target: "bridge", + "Going to submit message delivery transaction with declared dispatch \ + weight {:?} that overflows maximal configured weight {}", + new_selected_weight, + reference.max_messages_weight_in_single_batch, + ); + new_selected_weight.unwrap_or(Weight::MAX) + }, + _ => break, + }; + + // limit messages in the batch by size + let new_selected_size = match relay_reference.selected_size.checked_add(details.size) { + Some(new_selected_size) + if new_selected_size <= reference.max_messages_size_in_single_batch => + new_selected_size, + new_selected_size if selected_count == 0 => { + log::warn!( + target: "bridge", + "Going to submit message delivery transaction with message \ + size {:?} that overflows maximal configured size {}", + new_selected_size, + reference.max_messages_size_in_single_batch, + ); + new_selected_size.unwrap_or(u32::MAX) + }, + _ => break, + }; + + // limit number of messages in the batch + let new_selected_count = selected_count + 1; + if new_selected_count > reference.max_messages_in_this_batch { + break + } + relay_reference.selected_size = new_selected_size; + + hard_selected_count = index + 1; + selected_weight = new_selected_weight; + selected_count = new_selected_count; + } + + if hard_selected_count != 0 { + let selected_max_nonce = + hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1; + Some(hard_selected_begin_nonce..=selected_max_nonce) + } else { + None + } + } +} diff --git a/relays/messages/src/message_race_loop.rs b/relays/messages/src/message_race_loop.rs new file mode 100644 index 00000000000..7e3f84dd5d1 --- /dev/null +++ b/relays/messages/src/message_race_loop.rs @@ -0,0 +1,816 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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. + +//! Loop that is serving single race within message lane. This could be +//! message delivery race, receiving confirmations race or processing +//! confirmations race. +//! +//! The idea of the race is simple - we have `nonce`-s on source and target +//! nodes. We're trying to prove that the source node has this nonce (and +//! associated data - like messages, lane state, etc) to the target node by +//! generating and submitting proof. + +use crate::message_lane_loop::{BatchTransaction, ClientState, NoncesSubmitArtifacts}; + +use async_trait::async_trait; +use bp_messages::MessageNonce; +use futures::{ + future::{FutureExt, TryFutureExt}, + stream::{FusedStream, StreamExt}, +}; +use relay_utils::{ + process_future_result, retry_backoff, FailedClient, MaybeConnectionError, + TrackedTransactionStatus, TransactionTracker, +}; +use std::{ + fmt::Debug, + ops::RangeInclusive, + time::{Duration, Instant}, +}; + +/// One of races within lane. +pub trait MessageRace { + /// Header id of the race source. + type SourceHeaderId: Debug + Clone + PartialEq + Send; + /// Header id of the race source. + type TargetHeaderId: Debug + Clone + PartialEq + Send; + + /// Message nonce used in the race. + type MessageNonce: Debug + Clone; + /// Proof that is generated and delivered in this race. + type Proof: Debug + Clone + Send; + + /// Name of the race source. + fn source_name() -> String; + /// Name of the race target. + fn target_name() -> String; +} + +/// State of race source client. +type SourceClientState

= + ClientState<

::SourceHeaderId,

::TargetHeaderId>; + +/// State of race target client. +type TargetClientState

= + ClientState<

::TargetHeaderId,

::SourceHeaderId>; + +/// Inclusive nonces range. +pub trait NoncesRange: Debug + Sized { + /// Get begin of the range. + fn begin(&self) -> MessageNonce; + /// Get end of the range. + fn end(&self) -> MessageNonce; + /// Returns new range with current range nonces that are greater than the passed `nonce`. + /// If there are no such nonces, `None` is returned. + fn greater_than(self, nonce: MessageNonce) -> Option; +} + +/// Nonces on the race source client. +#[derive(Debug, Clone)] +pub struct SourceClientNonces { + /// New nonces range known to the client. `New` here means all nonces generated after + /// `prev_latest_nonce` passed to the `SourceClient::nonces` method. + pub new_nonces: NoncesRange, + /// The latest nonce that is confirmed to the bridged client. This nonce only makes + /// sense in some races. In other races it is `None`. + pub confirmed_nonce: Option, +} + +/// Nonces on the race target client. +#[derive(Debug, Clone)] +pub struct TargetClientNonces { + /// The latest nonce that is known to the target client. + pub latest_nonce: MessageNonce, + /// Additional data from target node that may be used by the race. + pub nonces_data: TargetNoncesData, +} + +/// One of message lane clients, which is source client for the race. +#[async_trait] +pub trait SourceClient { + /// Type of error these clients returns. + type Error: std::fmt::Debug + MaybeConnectionError; + /// Type of nonces range returned by the source client. + type NoncesRange: NoncesRange; + /// Additional proof parameters required to generate proof. + type ProofParameters; + + /// Return nonces that are known to the source client. + async fn nonces( + &self, + at_block: P::SourceHeaderId, + prev_latest_nonce: MessageNonce, + ) -> Result<(P::SourceHeaderId, SourceClientNonces), Self::Error>; + /// Generate proof for delivering to the target client. + async fn generate_proof( + &self, + at_block: P::SourceHeaderId, + nonces: RangeInclusive, + proof_parameters: Self::ProofParameters, + ) -> Result<(P::SourceHeaderId, RangeInclusive, P::Proof), Self::Error>; +} + +/// One of message lane clients, which is target client for the race. +#[async_trait] +pub trait TargetClient { + /// Type of error these clients returns. + type Error: std::fmt::Debug + MaybeConnectionError; + /// Type of the additional data from the target client, used by the race. + type TargetNoncesData: std::fmt::Debug; + /// Type of batch transaction that submits finality and proof to the target node. + type BatchTransaction: BatchTransaction + Clone; + /// Transaction tracker to track submitted transactions. + type TransactionTracker: TransactionTracker; + + /// Ask headers relay to relay finalized headers up to (and including) given header + /// from race source to race target. + /// + /// The client may return `Some(_)`, which means that nothing has happened yet and + /// the caller must generate and append proof to the batch transaction + /// to actually send it (along with required header) to the node. + /// + /// If function has returned `None`, it means that the caller now must wait for the + /// appearance of the required header `id` at the target client. + async fn require_source_header( + &self, + id: P::SourceHeaderId, + ) -> Result, Self::Error>; + + /// Return nonces that are known to the target client. + async fn nonces( + &self, + at_block: P::TargetHeaderId, + update_metrics: bool, + ) -> Result<(P::TargetHeaderId, TargetClientNonces), Self::Error>; + /// Submit proof to the target client. + async fn submit_proof( + &self, + maybe_batch_tx: Option, + generated_at_block: P::SourceHeaderId, + nonces: RangeInclusive, + proof: P::Proof, + ) -> Result, Self::Error>; +} + +/// Race strategy. +#[async_trait] +pub trait RaceStrategy: Debug { + /// Type of nonces range expected from the source client. + type SourceNoncesRange: NoncesRange; + /// Additional proof parameters required to generate proof. + type ProofParameters; + /// Additional data expected from the target client. + type TargetNoncesData; + + /// Should return true if nothing has to be synced. + fn is_empty(&self) -> bool; + /// Return id of source header that is required to be on target to continue synchronization. + fn required_source_header_at_target>( + &self, + current_best: &SourceHeaderId, + race_state: RS, + ) -> Option; + /// Return the best nonce at source node. + /// + /// `Some` is returned only if we are sure that the value is greater or equal + /// than the result of `best_at_target`. + fn best_at_source(&self) -> Option; + /// Return the best nonce at target node. + /// + /// May return `None` if value is yet unknown. + fn best_at_target(&self) -> Option; + + /// Called when nonces are updated at source node of the race. + fn source_nonces_updated( + &mut self, + at_block: SourceHeaderId, + nonces: SourceClientNonces, + ); + /// Called when best nonces are updated at target node of the race. + fn best_target_nonces_updated>( + &mut self, + nonces: TargetClientNonces, + race_state: &mut RS, + ); + /// Called when finalized nonces are updated at target node of the race. + fn finalized_target_nonces_updated>( + &mut self, + nonces: TargetClientNonces, + race_state: &mut RS, + ); + /// Should return `Some(nonces)` if we need to deliver proof of `nonces` (and associated + /// data) from source to target node. + /// Additionally, parameters required to generate proof are returned. + async fn select_nonces_to_deliver>( + &self, + race_state: RS, + ) -> Option<(RangeInclusive, Self::ProofParameters)>; +} + +/// State of the race. +pub trait RaceState: Send { + /// Best finalized source header id at the source client. + fn best_finalized_source_header_id_at_source(&self) -> Option; + /// Best finalized source header id at the best block on the target + /// client (at the `best_finalized_source_header_id_at_best_target`). + fn best_finalized_source_header_id_at_best_target(&self) -> Option; + /// The best header id at the target client. + fn best_target_header_id(&self) -> Option; + /// Best finalized header id at the target client. + fn best_finalized_target_header_id(&self) -> Option; + + /// Returns `true` if we have selected nonces to submit to the target node. + fn nonces_to_submit(&self) -> Option>; + /// Reset our nonces selection. + fn reset_nonces_to_submit(&mut self); + + /// Returns `true` if we have submitted some nonces to the target node and are + /// waiting for them to appear there. + fn nonces_submitted(&self) -> Option>; + /// Reset our nonces submission. + fn reset_nonces_submitted(&mut self); +} + +/// State of the race and prepared batch transaction (if available). +#[derive(Debug, Clone)] +pub(crate) struct RaceStateImpl { + /// Best finalized source header id at the source client. + pub best_finalized_source_header_id_at_source: Option, + /// Best finalized source header id at the best block on the target + /// client (at the `best_finalized_source_header_id_at_best_target`). + pub best_finalized_source_header_id_at_best_target: Option, + /// The best header id at the target client. + pub best_target_header_id: Option, + /// Best finalized header id at the target client. + pub best_finalized_target_header_id: Option, + /// Range of nonces that we have selected to submit. + pub nonces_to_submit: Option<(SourceHeaderId, RangeInclusive, Proof)>, + /// Batch transaction ready to include and deliver selected `nonces_to_submit` from the + /// `state`. + pub nonces_to_submit_batch: Option, + /// Range of nonces that is currently submitted. + pub nonces_submitted: Option>, +} + +impl Default + for RaceStateImpl +{ + fn default() -> Self { + RaceStateImpl { + best_finalized_source_header_id_at_source: None, + best_finalized_source_header_id_at_best_target: None, + best_target_header_id: None, + best_finalized_target_header_id: None, + nonces_to_submit: None, + nonces_to_submit_batch: None, + nonces_submitted: None, + } + } +} + +impl RaceState + for RaceStateImpl +where + SourceHeaderId: Clone + Send, + TargetHeaderId: Clone + Send, + Proof: Clone + Send, + BatchTx: Clone + Send, +{ + fn best_finalized_source_header_id_at_source(&self) -> Option { + self.best_finalized_source_header_id_at_source.clone() + } + + fn best_finalized_source_header_id_at_best_target(&self) -> Option { + self.best_finalized_source_header_id_at_best_target.clone() + } + + fn best_target_header_id(&self) -> Option { + self.best_target_header_id.clone() + } + + fn best_finalized_target_header_id(&self) -> Option { + self.best_finalized_target_header_id.clone() + } + + fn nonces_to_submit(&self) -> Option> { + self.nonces_to_submit.as_ref().map(|(_, nonces, _)| nonces.clone()) + } + + fn reset_nonces_to_submit(&mut self) { + self.nonces_to_submit = None; + self.nonces_to_submit_batch = None; + } + + fn nonces_submitted(&self) -> Option> { + self.nonces_submitted.clone() + } + + fn reset_nonces_submitted(&mut self) { + self.nonces_submitted = None; + } +} + +/// Run race loop until connection with target or source node is lost. +pub async fn run, TC: TargetClient

>( + race_source: SC, + race_source_updated: impl FusedStream>, + race_target: TC, + race_target_updated: impl FusedStream>, + mut strategy: impl RaceStrategy< + P::SourceHeaderId, + P::TargetHeaderId, + P::Proof, + SourceNoncesRange = SC::NoncesRange, + ProofParameters = SC::ProofParameters, + TargetNoncesData = TC::TargetNoncesData, + >, +) -> Result<(), FailedClient> { + let mut progress_context = Instant::now(); + let mut race_state = RaceStateImpl::default(); + + let mut source_retry_backoff = retry_backoff(); + let mut source_client_is_online = true; + let mut source_nonces_required = false; + let mut source_required_header = None; + let source_nonces = futures::future::Fuse::terminated(); + let source_generate_proof = futures::future::Fuse::terminated(); + let source_go_offline_future = futures::future::Fuse::terminated(); + + let mut target_retry_backoff = retry_backoff(); + let mut target_client_is_online = true; + let mut target_best_nonces_required = false; + let mut target_finalized_nonces_required = false; + let mut target_batch_transaction = None; + let target_require_source_header = futures::future::Fuse::terminated(); + let target_best_nonces = futures::future::Fuse::terminated(); + let target_finalized_nonces = futures::future::Fuse::terminated(); + let target_submit_proof = futures::future::Fuse::terminated(); + let target_tx_tracker = futures::future::Fuse::terminated(); + let target_go_offline_future = futures::future::Fuse::terminated(); + + futures::pin_mut!( + race_source_updated, + source_nonces, + source_generate_proof, + source_go_offline_future, + race_target_updated, + target_require_source_header, + target_best_nonces, + target_finalized_nonces, + target_submit_proof, + target_tx_tracker, + target_go_offline_future, + ); + + loop { + futures::select! { + // when headers ids are updated + source_state = race_source_updated.next() => { + if let Some(source_state) = source_state { + let is_source_state_updated = race_state.best_finalized_source_header_id_at_source.as_ref() + != Some(&source_state.best_finalized_self); + if is_source_state_updated { + source_nonces_required = true; + race_state.best_finalized_source_header_id_at_source + = Some(source_state.best_finalized_self); + } + } + }, + target_state = race_target_updated.next() => { + if let Some(target_state) = target_state { + let is_target_best_state_updated = race_state.best_target_header_id.as_ref() + != Some(&target_state.best_self); + + if is_target_best_state_updated { + target_best_nonces_required = true; + race_state.best_target_header_id = Some(target_state.best_self); + race_state.best_finalized_source_header_id_at_best_target + = target_state.best_finalized_peer_at_best_self; + } + + let is_target_finalized_state_updated = race_state.best_finalized_target_header_id.as_ref() + != Some(&target_state.best_finalized_self); + if is_target_finalized_state_updated { + target_finalized_nonces_required = true; + race_state.best_finalized_target_header_id = Some(target_state.best_finalized_self); + } + } + }, + + // when nonces are updated + nonces = source_nonces => { + source_nonces_required = false; + + source_client_is_online = process_future_result( + nonces, + &mut source_retry_backoff, + |(at_block, nonces)| { + log::debug!( + target: "bridge", + "Received nonces from {}: {:?}", + P::source_name(), + nonces, + ); + + strategy.source_nonces_updated(at_block, nonces); + }, + &mut source_go_offline_future, + async_std::task::sleep, + || format!("Error retrieving nonces from {}", P::source_name()), + ).fail_if_connection_error(FailedClient::Source)?; + + // ask for more headers if we have nonces to deliver and required headers are missing + source_required_header = race_state + .best_finalized_source_header_id_at_best_target + .as_ref() + .and_then(|best| strategy.required_source_header_at_target(best, race_state.clone())); + }, + nonces = target_best_nonces => { + target_best_nonces_required = false; + + target_client_is_online = process_future_result( + nonces, + &mut target_retry_backoff, + |(_, nonces)| { + log::debug!( + target: "bridge", + "Received best nonces from {}: {:?}", + P::target_name(), + nonces, + ); + + strategy.best_target_nonces_updated(nonces, &mut race_state); + }, + &mut target_go_offline_future, + async_std::task::sleep, + || format!("Error retrieving best nonces from {}", P::target_name()), + ).fail_if_connection_error(FailedClient::Target)?; + }, + nonces = target_finalized_nonces => { + target_finalized_nonces_required = false; + + target_client_is_online = process_future_result( + nonces, + &mut target_retry_backoff, + |(_, nonces)| { + log::debug!( + target: "bridge", + "Received finalized nonces from {}: {:?}", + P::target_name(), + nonces, + ); + + strategy.finalized_target_nonces_updated(nonces, &mut race_state); + }, + &mut target_go_offline_future, + async_std::task::sleep, + || format!("Error retrieving finalized nonces from {}", P::target_name()), + ).fail_if_connection_error(FailedClient::Target)?; + }, + + // proof generation and submission + maybe_batch_transaction = target_require_source_header => { + source_required_header = None; + + target_client_is_online = process_future_result( + maybe_batch_transaction, + &mut target_retry_backoff, + |maybe_batch_transaction: Option| { + log::debug!( + target: "bridge", + "Target {} client has been asked for more {} headers. Batch tx: {}", + P::target_name(), + P::source_name(), + maybe_batch_transaction + .as_ref() + .map(|bt| format!("yes ({:?})", bt.required_header_id())) + .unwrap_or_else(|| "no".into()), + ); + + target_batch_transaction = maybe_batch_transaction; + }, + &mut target_go_offline_future, + async_std::task::sleep, + || format!("Error asking for source headers at {}", P::target_name()), + ).fail_if_connection_error(FailedClient::Target)?; + }, + proof = source_generate_proof => { + source_client_is_online = process_future_result( + proof, + &mut source_retry_backoff, + |(at_block, nonces_range, proof, batch_transaction)| { + log::debug!( + target: "bridge", + "Received proof for nonces in range {:?} from {}", + nonces_range, + P::source_name(), + ); + + race_state.nonces_to_submit = Some((at_block, nonces_range, proof)); + race_state.nonces_to_submit_batch = batch_transaction; + }, + &mut source_go_offline_future, + async_std::task::sleep, + || format!("Error generating proof at {}", P::source_name()), + ).fail_if_error(FailedClient::Source).map(|_| true)?; + }, + proof_submit_result = target_submit_proof => { + target_client_is_online = process_future_result( + proof_submit_result, + &mut target_retry_backoff, + |artifacts: NoncesSubmitArtifacts| { + log::debug!( + target: "bridge", + "Successfully submitted proof of nonces {:?} to {}", + artifacts.nonces, + P::target_name(), + ); + + race_state.reset_nonces_to_submit(); + race_state.nonces_submitted = Some(artifacts.nonces); + target_tx_tracker.set(artifacts.tx_tracker.wait().fuse()); + }, + &mut target_go_offline_future, + async_std::task::sleep, + || format!("Error submitting proof {}", P::target_name()), + ).fail_if_error(FailedClient::Target).map(|_| true)?; + }, + target_transaction_status = target_tx_tracker => { + match (target_transaction_status, race_state.nonces_submitted.as_ref()) { + (TrackedTransactionStatus::Finalized(at_block), Some(nonces_submitted)) => { + // our transaction has been mined, but was it successful or not? let's check the best + // nonce at the target node. + let _ = race_target.nonces(at_block, false) + .await + .map_err(|e| format!("failed to read nonces from target node: {e:?}")) + .and_then(|(_, nonces_at_target)| { + if nonces_at_target.latest_nonce < *nonces_submitted.end() { + Err(format!( + "best nonce at target after tx is {:?} and we've submitted {:?}", + nonces_at_target.latest_nonce, + nonces_submitted.end(), + )) + } else { + Ok(()) + } + }) + .map_err(|e| { + log::error!( + target: "bridge", + "{} -> {} race transaction failed: {}", + P::source_name(), + P::target_name(), + e, + ); + + race_state.reset_nonces_submitted(); + }); + }, + (TrackedTransactionStatus::Lost, _) => { + log::warn!( + target: "bridge", + "{} -> {} race transaction has been lost. State: {:?}. Strategy: {:?}", + P::source_name(), + P::target_name(), + race_state, + strategy, + ); + + race_state.reset_nonces_submitted(); + }, + _ => (), + } + }, + + // when we're ready to retry request + _ = source_go_offline_future => { + source_client_is_online = true; + }, + _ = target_go_offline_future => { + target_client_is_online = true; + }, + } + + progress_context = print_race_progress::(progress_context, &strategy); + + if source_client_is_online { + source_client_is_online = false; + + // if we've started to submit batch transaction, let's prioritize it + // + // we're using `take` here, because we don't need batch transaction (i.e. some + // underlying finality proof) anymore for our future calls - we were unable to + // use it for our current state, so why would we need to keep an obsolete proof + // for the future? + let target_batch_transaction = target_batch_transaction.take(); + let expected_race_state = + if let Some(ref target_batch_transaction) = target_batch_transaction { + // when selecting nonces for the batch transaction, we assume that the required + // source header is already at the target chain + let required_source_header_at_target = + target_batch_transaction.required_header_id(); + let mut expected_race_state = race_state.clone(); + expected_race_state.best_finalized_source_header_id_at_best_target = + Some(required_source_header_at_target); + expected_race_state + } else { + race_state.clone() + }; + + let nonces_to_deliver = select_nonces_to_deliver(expected_race_state, &strategy).await; + let best_at_source = strategy.best_at_source(); + + if let Some((at_block, nonces_range, proof_parameters)) = nonces_to_deliver { + log::debug!( + target: "bridge", + "Asking {} to prove nonces in range {:?} at block {:?}", + P::source_name(), + nonces_range, + at_block, + ); + + source_generate_proof.set( + race_source + .generate_proof(at_block, nonces_range, proof_parameters) + .and_then(|(at_source_block, nonces, proof)| async { + Ok((at_source_block, nonces, proof, target_batch_transaction)) + }) + .fuse(), + ); + } else if source_nonces_required && best_at_source.is_some() { + log::debug!(target: "bridge", "Asking {} about message nonces", P::source_name()); + let at_block = race_state + .best_finalized_source_header_id_at_source + .as_ref() + .expect( + "source_nonces_required is only true when\ + best_finalized_source_header_id_at_source is Some; qed", + ) + .clone(); + source_nonces.set( + race_source + .nonces(at_block, best_at_source.expect("guaranteed by if condition; qed")) + .fuse(), + ); + } else { + source_client_is_online = true; + } + } + + if target_client_is_online { + target_client_is_online = false; + + if let Some((at_block, nonces_range, proof)) = race_state.nonces_to_submit.as_ref() { + log::debug!( + target: "bridge", + "Going to submit proof of messages in range {:?} to {} node{}", + nonces_range, + P::target_name(), + race_state.nonces_to_submit_batch.as_ref().map(|tx| format!( + ". This transaction is batched with sending the proof for header {:?}.", + tx.required_header_id()) + ).unwrap_or_default(), + ); + + target_submit_proof.set( + race_target + .submit_proof( + race_state.nonces_to_submit_batch.clone(), + at_block.clone(), + nonces_range.clone(), + proof.clone(), + ) + .fuse(), + ); + } else if let Some(source_required_header) = source_required_header.clone() { + log::debug!(target: "bridge", "Going to require {} header {:?} at {}", P::source_name(), source_required_header, P::target_name()); + target_require_source_header + .set(race_target.require_source_header(source_required_header).fuse()); + } else if target_best_nonces_required { + log::debug!(target: "bridge", "Asking {} about best message nonces", P::target_name()); + let at_block = race_state + .best_target_header_id + .as_ref() + .expect("target_best_nonces_required is only true when best_target_header_id is Some; qed") + .clone(); + target_best_nonces.set(race_target.nonces(at_block, false).fuse()); + } else if target_finalized_nonces_required { + log::debug!(target: "bridge", "Asking {} about finalized message nonces", P::target_name()); + let at_block = race_state + .best_finalized_target_header_id + .as_ref() + .expect( + "target_finalized_nonces_required is only true when\ + best_finalized_target_header_id is Some; qed", + ) + .clone(); + target_finalized_nonces.set(race_target.nonces(at_block, true).fuse()); + } else { + target_client_is_online = true; + } + } + } +} + +/// Print race progress. +fn print_race_progress(prev_time: Instant, strategy: &S) -> Instant +where + P: MessageRace, + S: RaceStrategy, +{ + let now_time = Instant::now(); + + let need_update = now_time.saturating_duration_since(prev_time) > Duration::from_secs(10); + if !need_update { + return prev_time + } + + let now_best_nonce_at_source = strategy.best_at_source(); + let now_best_nonce_at_target = strategy.best_at_target(); + log::info!( + target: "bridge", + "Synced {:?} of {:?} nonces in {} -> {} race", + now_best_nonce_at_target, + now_best_nonce_at_source, + P::source_name(), + P::target_name(), + ); + now_time +} + +async fn select_nonces_to_deliver( + race_state: impl RaceState, + strategy: &Strategy, +) -> Option<(SourceHeaderId, RangeInclusive, Strategy::ProofParameters)> +where + SourceHeaderId: Clone, + Strategy: RaceStrategy, +{ + let best_finalized_source_header_id_at_best_target = + race_state.best_finalized_source_header_id_at_best_target()?; + strategy + .select_nonces_to_deliver(race_state) + .await + .map(|(nonces_range, proof_parameters)| { + (best_finalized_source_header_id_at_best_target, nonces_range, proof_parameters) + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::message_race_strategy::BasicStrategy; + use relay_utils::HeaderId; + + #[async_std::test] + async fn proof_is_generated_at_best_block_known_to_target_node() { + const GENERATED_AT: u64 = 6; + const BEST_AT_SOURCE: u64 = 10; + const BEST_AT_TARGET: u64 = 8; + + // target node only knows about source' BEST_AT_TARGET block + // source node has BEST_AT_SOURCE > BEST_AT_TARGET block + let mut race_state = RaceStateImpl::<_, _, (), ()> { + best_finalized_source_header_id_at_source: Some(HeaderId( + BEST_AT_SOURCE, + BEST_AT_SOURCE, + )), + best_finalized_source_header_id_at_best_target: Some(HeaderId( + BEST_AT_TARGET, + BEST_AT_TARGET, + )), + best_target_header_id: Some(HeaderId(0, 0)), + best_finalized_target_header_id: Some(HeaderId(0, 0)), + nonces_to_submit: None, + nonces_to_submit_batch: None, + nonces_submitted: None, + }; + + // we have some nonces to deliver and they're generated at GENERATED_AT < BEST_AT_SOURCE + let mut strategy = BasicStrategy::<_, _, _, _, _, ()>::new(); + strategy.source_nonces_updated( + HeaderId(GENERATED_AT, GENERATED_AT), + SourceClientNonces { new_nonces: 0..=10, confirmed_nonce: None }, + ); + strategy.best_target_nonces_updated( + TargetClientNonces { latest_nonce: 5u64, nonces_data: () }, + &mut race_state, + ); + + // the proof will be generated on source, but using BEST_AT_TARGET block + assert_eq!( + select_nonces_to_deliver(race_state, &strategy).await, + Some((HeaderId(BEST_AT_TARGET, BEST_AT_TARGET), 6..=10, (),)) + ); + } +} diff --git a/relays/messages/src/message_race_receiving.rs b/relays/messages/src/message_race_receiving.rs new file mode 100644 index 00000000000..e6497a1b79e --- /dev/null +++ b/relays/messages/src/message_race_receiving.rs @@ -0,0 +1,235 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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. + +//! Message receiving race delivers proof-of-messages-delivery from "lane.target" to "lane.source". + +use crate::{ + message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, + message_lane_loop::{ + NoncesSubmitArtifacts, SourceClient as MessageLaneSourceClient, SourceClientState, + TargetClient as MessageLaneTargetClient, TargetClientState, + }, + message_race_loop::{ + MessageRace, NoncesRange, SourceClient, SourceClientNonces, TargetClient, + TargetClientNonces, + }, + message_race_strategy::BasicStrategy, + metrics::MessageLaneLoopMetrics, +}; + +use async_trait::async_trait; +use bp_messages::MessageNonce; +use futures::stream::FusedStream; +use relay_utils::FailedClient; +use std::{marker::PhantomData, ops::RangeInclusive}; + +/// Message receiving confirmations delivery strategy. +type ReceivingConfirmationsBasicStrategy

= BasicStrategy< +

::TargetHeaderNumber, +

::TargetHeaderHash, +

::SourceHeaderNumber, +

::SourceHeaderHash, + RangeInclusive, +

::MessagesReceivingProof, +>; + +/// Run receiving confirmations race. +pub async fn run( + source_client: impl MessageLaneSourceClient

, + source_state_updates: impl FusedStream>, + target_client: impl MessageLaneTargetClient

, + target_state_updates: impl FusedStream>, + metrics_msg: Option, +) -> Result<(), FailedClient> { + crate::message_race_loop::run( + ReceivingConfirmationsRaceSource { + client: target_client, + metrics_msg: metrics_msg.clone(), + _phantom: Default::default(), + }, + target_state_updates, + ReceivingConfirmationsRaceTarget { + client: source_client, + metrics_msg, + _phantom: Default::default(), + }, + source_state_updates, + ReceivingConfirmationsBasicStrategy::

::new(), + ) + .await +} + +/// Messages receiving confirmations race. +struct ReceivingConfirmationsRace

(std::marker::PhantomData

); + +impl MessageRace for ReceivingConfirmationsRace

{ + type SourceHeaderId = TargetHeaderIdOf

; + type TargetHeaderId = SourceHeaderIdOf

; + + type MessageNonce = MessageNonce; + type Proof = P::MessagesReceivingProof; + + fn source_name() -> String { + format!("{}::ReceivingConfirmationsDelivery", P::TARGET_NAME) + } + + fn target_name() -> String { + format!("{}::ReceivingConfirmationsDelivery", P::SOURCE_NAME) + } +} + +/// Message receiving confirmations race source, which is a target of the lane. +struct ReceivingConfirmationsRaceSource { + client: C, + metrics_msg: Option, + _phantom: PhantomData

, +} + +#[async_trait] +impl SourceClient> for ReceivingConfirmationsRaceSource +where + P: MessageLane, + C: MessageLaneTargetClient

, +{ + type Error = C::Error; + type NoncesRange = RangeInclusive; + type ProofParameters = (); + + async fn nonces( + &self, + at_block: TargetHeaderIdOf

, + prev_latest_nonce: MessageNonce, + ) -> Result<(TargetHeaderIdOf

, SourceClientNonces), Self::Error> { + let (at_block, latest_received_nonce) = self.client.latest_received_nonce(at_block).await?; + if let Some(metrics_msg) = self.metrics_msg.as_ref() { + metrics_msg.update_target_latest_received_nonce(latest_received_nonce); + } + Ok(( + at_block, + SourceClientNonces { + new_nonces: prev_latest_nonce + 1..=latest_received_nonce, + confirmed_nonce: None, + }, + )) + } + + #[allow(clippy::unit_arg)] + async fn generate_proof( + &self, + at_block: TargetHeaderIdOf

, + nonces: RangeInclusive, + _proof_parameters: Self::ProofParameters, + ) -> Result< + (TargetHeaderIdOf

, RangeInclusive, P::MessagesReceivingProof), + Self::Error, + > { + self.client + .prove_messages_receiving(at_block) + .await + .map(|(at_block, proof)| (at_block, nonces, proof)) + } +} + +/// Message receiving confirmations race target, which is a source of the lane. +struct ReceivingConfirmationsRaceTarget { + client: C, + metrics_msg: Option, + _phantom: PhantomData

, +} + +#[async_trait] +impl TargetClient> for ReceivingConfirmationsRaceTarget +where + P: MessageLane, + C: MessageLaneSourceClient

, +{ + type Error = C::Error; + type TargetNoncesData = (); + type BatchTransaction = C::BatchTransaction; + type TransactionTracker = C::TransactionTracker; + + async fn require_source_header( + &self, + id: TargetHeaderIdOf

, + ) -> Result, Self::Error> { + self.client.require_target_header_on_source(id).await + } + + async fn nonces( + &self, + at_block: SourceHeaderIdOf

, + update_metrics: bool, + ) -> Result<(SourceHeaderIdOf

, TargetClientNonces<()>), Self::Error> { + let (at_block, latest_confirmed_nonce) = + self.client.latest_confirmed_received_nonce(at_block).await?; + if update_metrics { + if let Some(metrics_msg) = self.metrics_msg.as_ref() { + metrics_msg.update_source_latest_confirmed_nonce(latest_confirmed_nonce); + } + } + Ok((at_block, TargetClientNonces { latest_nonce: latest_confirmed_nonce, nonces_data: () })) + } + + async fn submit_proof( + &self, + maybe_batch_tx: Option, + generated_at_block: TargetHeaderIdOf

, + nonces: RangeInclusive, + proof: P::MessagesReceivingProof, + ) -> Result, Self::Error> { + let tx_tracker = self + .client + .submit_messages_receiving_proof(maybe_batch_tx, generated_at_block, proof) + .await?; + Ok(NoncesSubmitArtifacts { nonces, tx_tracker }) + } +} + +impl NoncesRange for RangeInclusive { + fn begin(&self) -> MessageNonce { + *RangeInclusive::::start(self) + } + + fn end(&self) -> MessageNonce { + *RangeInclusive::::end(self) + } + + fn greater_than(self, nonce: MessageNonce) -> Option { + let next_nonce = nonce + 1; + let end = *self.end(); + if next_nonce > end { + None + } else { + Some(std::cmp::max(self.begin(), next_nonce)..=end) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn range_inclusive_works_as_nonces_range() { + let range = 20..=30; + + assert_eq!(NoncesRange::begin(&range), 20); + assert_eq!(NoncesRange::end(&range), 30); + assert_eq!(range.clone().greater_than(10), Some(20..=30)); + assert_eq!(range.clone().greater_than(19), Some(20..=30)); + assert_eq!(range.clone().greater_than(20), Some(21..=30)); + assert_eq!(range.clone().greater_than(25), Some(26..=30)); + assert_eq!(range.clone().greater_than(29), Some(30..=30)); + assert_eq!(range.greater_than(30), None); + } +} diff --git a/relays/messages/src/message_race_strategy.rs b/relays/messages/src/message_race_strategy.rs new file mode 100644 index 00000000000..e6016448c95 --- /dev/null +++ b/relays/messages/src/message_race_strategy.rs @@ -0,0 +1,596 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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. + +//! Basic delivery strategy. The strategy selects nonces if: +//! +//! 1) there are more nonces on the source side than on the target side; +//! 2) new nonces may be proved to target node (i.e. they have appeared at the +//! block, which is known to the target node). + +use crate::message_race_loop::{ + NoncesRange, RaceState, RaceStrategy, SourceClientNonces, TargetClientNonces, +}; + +use async_trait::async_trait; +use bp_messages::MessageNonce; +use relay_utils::HeaderId; +use std::{collections::VecDeque, fmt::Debug, marker::PhantomData, ops::RangeInclusive}; + +/// Queue of nonces known to the source node. +pub type SourceRangesQueue = + VecDeque<(HeaderId, SourceNoncesRange)>; + +/// Nonces delivery strategy. +#[derive(Debug)] +pub struct BasicStrategy< + SourceHeaderNumber, + SourceHeaderHash, + TargetHeaderNumber, + TargetHeaderHash, + SourceNoncesRange, + Proof, +> { + /// All queued nonces. + /// + /// The queue may contain already delivered nonces. We only remove entries from this + /// queue after corresponding nonces are finalized by the target chain. + source_queue: SourceRangesQueue, + /// The best nonce known to target node at its best block. `None` if it has not been received + /// yet. + best_target_nonce: Option, + /// Unused generic types dump. + _phantom: PhantomData<(TargetHeaderNumber, TargetHeaderHash, Proof)>, +} + +impl< + SourceHeaderNumber, + SourceHeaderHash, + TargetHeaderNumber, + TargetHeaderHash, + SourceNoncesRange, + Proof, + > + BasicStrategy< + SourceHeaderNumber, + SourceHeaderHash, + TargetHeaderNumber, + TargetHeaderHash, + SourceNoncesRange, + Proof, + > where + SourceHeaderHash: Clone, + SourceHeaderNumber: Clone + Ord, + SourceNoncesRange: NoncesRange, +{ + /// Create new delivery strategy. + pub fn new() -> Self { + BasicStrategy { + source_queue: VecDeque::new(), + best_target_nonce: None, + _phantom: Default::default(), + } + } + + /// Reference to source queue. + pub(crate) fn source_queue( + &self, + ) -> &VecDeque<(HeaderId, SourceNoncesRange)> { + &self.source_queue + } + + /// Mutable reference to source queue to use in tests. + #[cfg(test)] + pub(crate) fn source_queue_mut( + &mut self, + ) -> &mut VecDeque<(HeaderId, SourceNoncesRange)> { + &mut self.source_queue + } + + /// Returns indices of source queue entries, which may be delivered to the target node. + /// + /// The function may skip some nonces from the queue front if nonces from this entry are + /// already available at the **best** target block. After this block is finalized, the entry + /// will be removed from the queue. + /// + /// All entries before and including the range end index, are guaranteed to be witnessed + /// at source blocks that are known to be finalized at the target node. + /// + /// Returns `None` if no entries may be delivered. + pub fn available_source_queue_indices< + RS: RaceState< + HeaderId, + HeaderId, + >, + >( + &self, + race_state: RS, + ) -> Option> { + // if we do not know best nonce at target node, we can't select anything + let best_target_nonce = self.best_target_nonce?; + + // if we have already selected nonces that we want to submit, do nothing + if race_state.nonces_to_submit().is_some() { + return None + } + + // if we already submitted some nonces, do nothing + if race_state.nonces_submitted().is_some() { + return None + } + + // find first entry that may be delivered to the target node + let begin_index = self + .source_queue + .iter() + .enumerate() + .skip_while(|(_, (_, nonces))| nonces.end() <= best_target_nonce) + .map(|(index, _)| index) + .next()?; + + // 1) we want to deliver all nonces, starting from `target_nonce + 1` + // 2) we can't deliver new nonce until header, that has emitted this nonce, is finalized + // by target client + // 3) selector is used for more complicated logic + // + // => let's first select range of entries inside deque that are already finalized at + // the target client and pass this range to the selector + let best_header_at_target = race_state.best_finalized_source_header_id_at_best_target()?; + let end_index = self + .source_queue + .iter() + .enumerate() + .skip(begin_index) + .take_while(|(_, (queued_at, _))| queued_at.0 <= best_header_at_target.0) + .map(|(index, _)| index) + .last()?; + + Some(begin_index..=end_index) + } + + /// Remove all nonces that are less than or equal to given nonce from the source queue. + fn remove_le_nonces_from_source_queue(&mut self, nonce: MessageNonce) { + while let Some((queued_at, queued_range)) = self.source_queue.pop_front() { + if let Some(range_to_requeue) = queued_range.greater_than(nonce) { + self.source_queue.push_front((queued_at, range_to_requeue)); + break + } + } + } +} + +#[async_trait] +impl< + SourceHeaderNumber, + SourceHeaderHash, + TargetHeaderNumber, + TargetHeaderHash, + SourceNoncesRange, + Proof, + > + RaceStrategy< + HeaderId, + HeaderId, + Proof, + > + for BasicStrategy< + SourceHeaderNumber, + SourceHeaderHash, + TargetHeaderNumber, + TargetHeaderHash, + SourceNoncesRange, + Proof, + > where + SourceHeaderHash: Clone + Debug + Send + Sync, + SourceHeaderNumber: Clone + Ord + Debug + Send + Sync, + SourceNoncesRange: NoncesRange + Debug + Send + Sync, + TargetHeaderHash: Debug + Send + Sync, + TargetHeaderNumber: Debug + Send + Sync, + Proof: Debug + Send + Sync, +{ + type SourceNoncesRange = SourceNoncesRange; + type ProofParameters = (); + type TargetNoncesData = (); + + fn is_empty(&self) -> bool { + self.source_queue.is_empty() + } + + fn required_source_header_at_target< + RS: RaceState< + HeaderId, + HeaderId, + >, + >( + &self, + current_best: &HeaderId, + _race_state: RS, + ) -> Option> { + self.source_queue + .back() + .and_then(|(h, _)| if h.0 > current_best.0 { Some(h.clone()) } else { None }) + } + + fn best_at_source(&self) -> Option { + let best_in_queue = self.source_queue.back().map(|(_, range)| range.end()); + match (best_in_queue, self.best_target_nonce) { + (Some(best_in_queue), Some(best_target_nonce)) if best_in_queue > best_target_nonce => + Some(best_in_queue), + (_, Some(best_target_nonce)) => Some(best_target_nonce), + (_, None) => None, + } + } + + fn best_at_target(&self) -> Option { + self.best_target_nonce + } + + fn source_nonces_updated( + &mut self, + at_block: HeaderId, + nonces: SourceClientNonces, + ) { + let best_in_queue = self + .source_queue + .back() + .map(|(_, range)| range.end()) + .or(self.best_target_nonce) + .unwrap_or_default(); + self.source_queue.extend( + nonces + .new_nonces + .greater_than(best_in_queue) + .into_iter() + .map(move |range| (at_block.clone(), range)), + ) + } + + fn best_target_nonces_updated< + RS: RaceState< + HeaderId, + HeaderId, + >, + >( + &mut self, + nonces: TargetClientNonces<()>, + race_state: &mut RS, + ) { + let nonce = nonces.latest_nonce; + + let need_to_select_new_nonces = race_state + .nonces_to_submit() + .map(|nonces| *nonces.end() <= nonce) + .unwrap_or(false); + if need_to_select_new_nonces { + race_state.reset_nonces_to_submit(); + } + + let need_new_nonces_to_submit = race_state + .nonces_submitted() + .map(|nonces| *nonces.end() <= nonce) + .unwrap_or(false); + if need_new_nonces_to_submit { + race_state.reset_nonces_submitted(); + } + + self.best_target_nonce = Some(nonce); + } + + fn finalized_target_nonces_updated< + RS: RaceState< + HeaderId, + HeaderId, + >, + >( + &mut self, + nonces: TargetClientNonces<()>, + _race_state: &mut RS, + ) { + self.remove_le_nonces_from_source_queue(nonces.latest_nonce); + self.best_target_nonce = Some(std::cmp::max( + self.best_target_nonce.unwrap_or(nonces.latest_nonce), + nonces.latest_nonce, + )); + } + + async fn select_nonces_to_deliver< + RS: RaceState< + HeaderId, + HeaderId, + >, + >( + &self, + race_state: RS, + ) -> Option<(RangeInclusive, Self::ProofParameters)> { + let available_indices = self.available_source_queue_indices(race_state)?; + let range_begin = std::cmp::max( + self.best_target_nonce? + 1, + self.source_queue[*available_indices.start()].1.begin(), + ); + let range_end = self.source_queue[*available_indices.end()].1.end(); + Some((range_begin..=range_end, ())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, + message_lane_loop::tests::{ + header_id, TestMessageLane, TestMessagesProof, TestSourceHeaderHash, + TestSourceHeaderNumber, + }, + message_race_loop::RaceStateImpl, + }; + + type SourceNoncesRange = RangeInclusive; + + type TestRaceStateImpl = RaceStateImpl< + SourceHeaderIdOf, + TargetHeaderIdOf, + TestMessagesProof, + (), + >; + + type BasicStrategy

= super::BasicStrategy< +

::SourceHeaderNumber, +

::SourceHeaderHash, +

::TargetHeaderNumber, +

::TargetHeaderHash, + SourceNoncesRange, +

::MessagesProof, + >; + + fn source_nonces(new_nonces: SourceNoncesRange) -> SourceClientNonces { + SourceClientNonces { new_nonces, confirmed_nonce: None } + } + + fn target_nonces(latest_nonce: MessageNonce) -> TargetClientNonces<()> { + TargetClientNonces { latest_nonce, nonces_data: () } + } + + #[test] + fn strategy_is_empty_works() { + let mut strategy = BasicStrategy::::new(); + assert!(strategy.is_empty()); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=1)); + assert!(!strategy.is_empty()); + } + + #[test] + fn best_at_source_is_never_lower_than_target_nonce() { + let mut strategy = BasicStrategy::::new(); + assert_eq!(strategy.best_at_source(), None); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); + assert_eq!(strategy.best_at_source(), None); + strategy.best_target_nonces_updated(target_nonces(10), &mut TestRaceStateImpl::default()); + assert_eq!(strategy.source_queue, vec![(header_id(1), 1..=5)]); + assert_eq!(strategy.best_at_source(), Some(10)); + } + + #[test] + fn source_nonce_is_never_lower_than_known_target_nonce() { + let mut strategy = BasicStrategy::::new(); + strategy.best_target_nonces_updated(target_nonces(10), &mut TestRaceStateImpl::default()); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); + assert_eq!(strategy.source_queue, vec![]); + } + + #[test] + fn source_nonce_is_never_lower_than_latest_known_source_nonce() { + let mut strategy = BasicStrategy::::new(); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); + strategy.source_nonces_updated(header_id(2), source_nonces(1..=3)); + strategy.source_nonces_updated(header_id(2), source_nonces(1..=5)); + assert_eq!(strategy.source_queue, vec![(header_id(1), 1..=5)]); + } + + #[test] + fn updated_target_nonce_removes_queued_entries() { + let mut strategy = BasicStrategy::::new(); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); + strategy.source_nonces_updated(header_id(2), source_nonces(6..=10)); + strategy.source_nonces_updated(header_id(3), source_nonces(11..=15)); + strategy.source_nonces_updated(header_id(4), source_nonces(16..=20)); + strategy + .finalized_target_nonces_updated(target_nonces(15), &mut TestRaceStateImpl::default()); + assert_eq!(strategy.source_queue, vec![(header_id(4), 16..=20)]); + strategy + .finalized_target_nonces_updated(target_nonces(17), &mut TestRaceStateImpl::default()); + assert_eq!(strategy.source_queue, vec![(header_id(4), 18..=20)]); + } + + #[test] + fn selected_nonces_are_dropped_on_target_nonce_update() { + let mut state = TestRaceStateImpl::default(); + let mut strategy = BasicStrategy::::new(); + state.nonces_to_submit = Some((header_id(1), 5..=10, (5..=10, None))); + strategy.best_target_nonces_updated(target_nonces(7), &mut state); + assert!(state.nonces_to_submit.is_some()); + strategy.best_target_nonces_updated(target_nonces(10), &mut state); + assert!(state.nonces_to_submit.is_none()); + } + + #[test] + fn submitted_nonces_are_dropped_on_target_nonce_update() { + let mut state = TestRaceStateImpl::default(); + let mut strategy = BasicStrategy::::new(); + state.nonces_submitted = Some(5..=10); + strategy.best_target_nonces_updated(target_nonces(7), &mut state); + assert!(state.nonces_submitted.is_some()); + strategy.best_target_nonces_updated(target_nonces(10), &mut state); + assert!(state.nonces_submitted.is_none()); + } + + #[async_std::test] + async fn nothing_is_selected_if_something_is_already_selected() { + let mut state = TestRaceStateImpl::default(); + let mut strategy = BasicStrategy::::new(); + state.nonces_to_submit = Some((header_id(1), 1..=10, (1..=10, None))); + strategy.best_target_nonces_updated(target_nonces(0), &mut state); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=10)); + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); + } + + #[async_std::test] + async fn nothing_is_selected_if_something_is_already_submitted() { + let mut state = TestRaceStateImpl::default(); + let mut strategy = BasicStrategy::::new(); + state.nonces_submitted = Some(1..=10); + strategy.best_target_nonces_updated(target_nonces(0), &mut state); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=10)); + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); + } + + #[async_std::test] + async fn select_nonces_to_deliver_works() { + let mut state = TestRaceStateImpl::default(); + let mut strategy = BasicStrategy::::new(); + strategy.best_target_nonces_updated(target_nonces(0), &mut state); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=1)); + strategy.source_nonces_updated(header_id(2), source_nonces(2..=2)); + strategy.source_nonces_updated(header_id(3), source_nonces(3..=6)); + strategy.source_nonces_updated(header_id(5), source_nonces(7..=8)); + + state.best_finalized_source_header_id_at_best_target = Some(header_id(4)); + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((1..=6, ()))); + strategy.best_target_nonces_updated(target_nonces(6), &mut state); + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); + + state.best_finalized_source_header_id_at_best_target = Some(header_id(5)); + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((7..=8, ()))); + strategy.best_target_nonces_updated(target_nonces(8), &mut state); + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); + } + + #[test] + fn available_source_queue_indices_works() { + let mut state = TestRaceStateImpl::default(); + let mut strategy = BasicStrategy::::new(); + strategy.best_target_nonces_updated(target_nonces(0), &mut state); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=3)); + strategy.source_nonces_updated(header_id(2), source_nonces(4..=6)); + strategy.source_nonces_updated(header_id(3), source_nonces(7..=9)); + + state.best_finalized_source_header_id_at_best_target = Some(header_id(0)); + assert_eq!(strategy.available_source_queue_indices(state.clone()), None); + + state.best_finalized_source_header_id_at_best_target = Some(header_id(1)); + assert_eq!(strategy.available_source_queue_indices(state.clone()), Some(0..=0)); + + state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); + assert_eq!(strategy.available_source_queue_indices(state.clone()), Some(0..=1)); + + state.best_finalized_source_header_id_at_best_target = Some(header_id(3)); + assert_eq!(strategy.available_source_queue_indices(state.clone()), Some(0..=2)); + + state.best_finalized_source_header_id_at_best_target = Some(header_id(4)); + assert_eq!(strategy.available_source_queue_indices(state), Some(0..=2)); + } + + #[test] + fn remove_le_nonces_from_source_queue_works() { + let mut state = TestRaceStateImpl::default(); + let mut strategy = BasicStrategy::::new(); + strategy.best_target_nonces_updated(target_nonces(0), &mut state); + strategy.source_nonces_updated(header_id(1), source_nonces(1..=3)); + strategy.source_nonces_updated(header_id(2), source_nonces(4..=6)); + strategy.source_nonces_updated(header_id(3), source_nonces(7..=9)); + + fn source_queue_nonces( + source_queue: &SourceRangesQueue< + TestSourceHeaderHash, + TestSourceHeaderNumber, + SourceNoncesRange, + >, + ) -> Vec { + source_queue.iter().flat_map(|(_, range)| range.clone()).collect() + } + + strategy.remove_le_nonces_from_source_queue(1); + assert_eq!(source_queue_nonces(&strategy.source_queue), vec![2, 3, 4, 5, 6, 7, 8, 9],); + + strategy.remove_le_nonces_from_source_queue(5); + assert_eq!(source_queue_nonces(&strategy.source_queue), vec![6, 7, 8, 9],); + + strategy.remove_le_nonces_from_source_queue(9); + assert_eq!(source_queue_nonces(&strategy.source_queue), Vec::::new(),); + + strategy.remove_le_nonces_from_source_queue(100); + assert_eq!(source_queue_nonces(&strategy.source_queue), Vec::::new(),); + } + + #[async_std::test] + async fn previous_nonces_are_selected_if_reorg_happens_at_target_chain() { + let source_header_1 = header_id(1); + let target_header_1 = header_id(1); + + // we start in perfec sync state - all headers are synced and finalized on both ends + let mut state = TestRaceStateImpl { + best_finalized_source_header_id_at_source: Some(source_header_1), + best_finalized_source_header_id_at_best_target: Some(source_header_1), + best_target_header_id: Some(target_header_1), + best_finalized_target_header_id: Some(target_header_1), + nonces_to_submit: None, + nonces_to_submit_batch: None, + nonces_submitted: None, + }; + + // in this state we have 1 available nonce for delivery + let mut strategy = BasicStrategy:: { + source_queue: vec![(header_id(1), 1..=1)].into_iter().collect(), + best_target_nonce: Some(0), + _phantom: PhantomData, + }; + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((1..=1, ())),); + + // let's say we have submitted 1..=1 + state.nonces_submitted = Some(1..=1); + + // then new nonce 2 appear at the source block 2 + let source_header_2 = header_id(2); + state.best_finalized_source_header_id_at_source = Some(source_header_2); + strategy.source_nonces_updated( + source_header_2, + SourceClientNonces { new_nonces: 2..=2, confirmed_nonce: None }, + ); + // and nonce 1 appear at the best block of the target node (best finalized still has 0 + // nonces) + let target_header_2 = header_id(2); + state.best_target_header_id = Some(target_header_2); + strategy.best_target_nonces_updated( + TargetClientNonces { latest_nonce: 1, nonces_data: () }, + &mut state, + ); + + // then best target header is retracted + strategy.best_target_nonces_updated( + TargetClientNonces { latest_nonce: 0, nonces_data: () }, + &mut state, + ); + + // ... and some fork with zero delivered nonces is finalized + let target_header_2_fork = header_id(2_1); + state.best_finalized_source_header_id_at_source = Some(source_header_2); + state.best_finalized_source_header_id_at_best_target = Some(source_header_2); + state.best_target_header_id = Some(target_header_2_fork); + state.best_finalized_target_header_id = Some(target_header_2_fork); + strategy.finalized_target_nonces_updated( + TargetClientNonces { latest_nonce: 0, nonces_data: () }, + &mut state, + ); + + // now we have to select nonce 1 for delivery again + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((1..=2, ())),); + } +} diff --git a/relays/messages/src/metrics.rs b/relays/messages/src/metrics.rs new file mode 100644 index 00000000000..69d80d178de --- /dev/null +++ b/relays/messages/src/metrics.rs @@ -0,0 +1,148 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Metrics for message lane relay loop. + +use crate::{ + message_lane::MessageLane, + message_lane_loop::{SourceClientState, TargetClientState}, +}; + +use bp_messages::MessageNonce; +use finality_relay::SyncLoopMetrics; +use relay_utils::metrics::{ + metric_name, register, GaugeVec, Metric, Opts, PrometheusError, Registry, U64, +}; + +/// Message lane relay metrics. +/// +/// Cloning only clones references. +#[derive(Clone)] +pub struct MessageLaneLoopMetrics { + /// Best finalized block numbers - "source", "source_at_target", "target_at_source". + source_to_target_finality_metrics: SyncLoopMetrics, + /// Best finalized block numbers - "source", "target", "source_at_target", "target_at_source". + target_to_source_finality_metrics: SyncLoopMetrics, + /// Lane state nonces: "source_latest_generated", "source_latest_confirmed", + /// "target_latest_received", "target_latest_confirmed". + lane_state_nonces: GaugeVec, +} + +impl MessageLaneLoopMetrics { + /// Create and register messages loop metrics. + pub fn new(prefix: Option<&str>) -> Result { + Ok(MessageLaneLoopMetrics { + source_to_target_finality_metrics: SyncLoopMetrics::new( + prefix, + "source", + "source_at_target", + )?, + target_to_source_finality_metrics: SyncLoopMetrics::new( + prefix, + "target", + "target_at_source", + )?, + lane_state_nonces: GaugeVec::new( + Opts::new(metric_name(prefix, "lane_state_nonces"), "Nonces of the lane state"), + &["type"], + )?, + }) + } + + /// Update source client state metrics. + pub fn update_source_state(&self, source_client_state: SourceClientState

) { + self.source_to_target_finality_metrics + .update_best_block_at_source(source_client_state.best_self.0); + if let Some(best_finalized_peer_at_best_self) = + source_client_state.best_finalized_peer_at_best_self + { + self.target_to_source_finality_metrics + .update_best_block_at_target(best_finalized_peer_at_best_self.0); + if let Some(actual_best_finalized_peer_at_best_self) = + source_client_state.actual_best_finalized_peer_at_best_self + { + self.target_to_source_finality_metrics.update_using_same_fork( + best_finalized_peer_at_best_self.1 == actual_best_finalized_peer_at_best_self.1, + ); + } + } + } + + /// Update target client state metrics. + pub fn update_target_state(&self, target_client_state: TargetClientState

) { + self.target_to_source_finality_metrics + .update_best_block_at_source(target_client_state.best_self.0); + if let Some(best_finalized_peer_at_best_self) = + target_client_state.best_finalized_peer_at_best_self + { + self.source_to_target_finality_metrics + .update_best_block_at_target(best_finalized_peer_at_best_self.0); + if let Some(actual_best_finalized_peer_at_best_self) = + target_client_state.actual_best_finalized_peer_at_best_self + { + self.source_to_target_finality_metrics.update_using_same_fork( + best_finalized_peer_at_best_self.1 == actual_best_finalized_peer_at_best_self.1, + ); + } + } + } + + /// Update latest generated nonce at source. + pub fn update_source_latest_generated_nonce( + &self, + source_latest_generated_nonce: MessageNonce, + ) { + self.lane_state_nonces + .with_label_values(&["source_latest_generated"]) + .set(source_latest_generated_nonce); + } + + /// Update the latest confirmed nonce at source. + pub fn update_source_latest_confirmed_nonce( + &self, + source_latest_confirmed_nonce: MessageNonce, + ) { + self.lane_state_nonces + .with_label_values(&["source_latest_confirmed"]) + .set(source_latest_confirmed_nonce); + } + + /// Update the latest received nonce at target. + pub fn update_target_latest_received_nonce(&self, target_latest_generated_nonce: MessageNonce) { + self.lane_state_nonces + .with_label_values(&["target_latest_received"]) + .set(target_latest_generated_nonce); + } + + /// Update the latest confirmed nonce at target. + pub fn update_target_latest_confirmed_nonce( + &self, + target_latest_confirmed_nonce: MessageNonce, + ) { + self.lane_state_nonces + .with_label_values(&["target_latest_confirmed"]) + .set(target_latest_confirmed_nonce); + } +} + +impl Metric for MessageLaneLoopMetrics { + fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { + self.source_to_target_finality_metrics.register(registry)?; + self.target_to_source_finality_metrics.register(registry)?; + register(self.lane_state_nonces.clone(), registry)?; + Ok(()) + } +} diff --git a/relays/parachains/Cargo.toml b/relays/parachains/Cargo.toml new file mode 100644 index 00000000000..0cecf063922 --- /dev/null +++ b/relays/parachains/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "parachains-relay" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +async-std = "1.6.5" +async-trait = "0.1.68" +futures = "0.3.28" +log = "0.4.17" +relay-utils = { path = "../utils" } + +# Bridge dependencies + +bp-polkadot-core = { path = "../../primitives/polkadot-core" } +relay-substrate-client = { path = "../client-substrate" } + +[dev-dependencies] +codec = { package = "parity-scale-codec", version = "3.1.5" } +relay-substrate-client = { path = "../client-substrate", features = ["test-helpers"] } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/parachains/README.md b/relays/parachains/README.md new file mode 100644 index 00000000000..fc5ad03fb34 --- /dev/null +++ b/relays/parachains/README.md @@ -0,0 +1,49 @@ +# Parachains Finality Relay + +The parachains finality relay works with two chains - source relay chain and target chain (which may be standalone +chain, relay chain or a parachain). The source chain must have the +[`paras` pallet](https://github.com/paritytech/polkadot/tree/master/runtime/parachains/src/paras) deployed at its +runtime. The target chain must have the [bridge parachains pallet](../../modules/parachains/) deployed at its runtime. + +The relay is configured to submit heads of one or several parachains. It pokes source chain periodically and compares +parachain heads that are known to the source relay chain to heads at the target chain. If there are new heads, +the relay submits them to the target chain. + +More: [Parachains Finality Relay Sequence Diagram](../../docs/parachains-finality-relay.html). + +## How to Use the Parachains Finality Relay + +There are only two traits that need to be implemented. The [`SourceChain`](./src/parachains_loop.rs) implementation +is supposed to connect to the source chain node. It must be able to read parachain heads from the `Heads` map of +the [`paras` pallet](https://github.com/paritytech/polkadot/tree/master/runtime/parachains/src/paras). +It also must create storage proofs of `Heads` map entries, when required. + +The [`TargetChain`](./src/parachains_loop.rs) implementation connects to the target chain node. It must be able +to return the best known head of given parachain. When required, it must be able to craft and submit parachains +finality delivery transaction to the target node. + +The main entrypoint for the crate is the [`run` function](./src/parachains_loop.rs), which takes source and target +clients and [`ParachainSyncParams`](./src/parachains_loop.rs) parameters. The most imporant parameter is the +`parachains` - it is the set of parachains, which relay tracks and updates. The other important parameter that +may affect the relay operational costs is the `strategy`. If it is set to `Any`, then the finality delivery +transaction is submitted if at least one of tracked parachain heads is updated. The other option is `All`. Then +the relay waits until all tracked parachain heads are updated and submits them all in a single finality delivery +transaction. + +## Parachain Finality Relay Metrics + +Every parachain in Polkadot is identified by the 32-bit number. All metrics, exposed by the parachains finality +relay have the `parachain` label, which is set to the parachain id. And the metrics are prefixed with the prefix, +that depends on the name of the source relay and target chains. The list below shows metrics names for +Rialto (source relay chain) to Millau (target chain) parachains finality relay. For other chains, simply +change chain names. So the metrics are: + +- `Rialto_to_Millau_Parachains_best_parachain_block_number_at_source` - returns best known parachain block + number, registered in the `paras` pallet at the source relay chain (Rialto in our example); + +- `Rialto_to_Millau_Parachains_best_parachain_block_number_at_target` - returns best known parachain block + number, registered in the bridge parachains pallet at the target chain (Millau in our example). + +If relay operates properly, you should see that the `Rialto_to_Millau_Parachains_best_parachain_block_number_at_target` +tries to reach the `Rialto_to_Millau_Parachains_best_parachain_block_number_at_source`. And the latter one +always increases. diff --git a/relays/parachains/src/lib.rs b/relays/parachains/src/lib.rs new file mode 100644 index 00000000000..81ea983a6f7 --- /dev/null +++ b/relays/parachains/src/lib.rs @@ -0,0 +1,32 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use std::fmt::Debug; + +use relay_substrate_client::{Chain, Parachain}; + +pub mod parachains_loop; +pub mod parachains_loop_metrics; + +/// Finality proofs synchronization pipeline. +pub trait ParachainsPipeline: 'static + Clone + Debug + Send + Sync { + /// Relay chain which is storing parachain heads in its `paras` module. + type SourceRelayChain: Chain; + /// Parachain which headers we are syncing here. + type SourceParachain: Parachain; + /// Target chain (either relay or para) which wants to know about new parachain heads. + type TargetChain: Chain; +} diff --git a/relays/parachains/src/parachains_loop.rs b/relays/parachains/src/parachains_loop.rs new file mode 100644 index 00000000000..9b9038fd976 --- /dev/null +++ b/relays/parachains/src/parachains_loop.rs @@ -0,0 +1,953 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{parachains_loop_metrics::ParachainsLoopMetrics, ParachainsPipeline}; + +use async_trait::async_trait; +use bp_polkadot_core::{ + parachains::{ParaHash, ParaHeadsProof, ParaId}, + BlockNumber as RelayBlockNumber, +}; +use futures::{ + future::{FutureExt, Shared}, + poll, select_biased, +}; +use relay_substrate_client::{Chain, HeaderIdOf, ParachainBase}; +use relay_utils::{ + metrics::MetricsParams, relay_loop::Client as RelayClient, FailedClient, + TrackedTransactionStatus, TransactionTracker, +}; +use std::{future::Future, pin::Pin, task::Poll}; + +/// Parachain header availability at a certain chain. +#[derive(Clone, Copy, Debug)] +pub enum AvailableHeader { + /// The client can not report actual parachain head at this moment. + /// + /// It is a "mild" error, which may appear when e.g. on-demand parachains relay is used. + /// This variant must be treated as "we don't want to update parachain head value at the + /// target chain at this moment". + Unavailable, + /// There's no parachain header at the relay chain. + /// + /// Normally it means that the parachain is not registered there. + Missing, + /// Parachain head with given hash is available at the source chain. + Available(T), +} + +impl AvailableHeader { + /// Return available header. + pub fn as_available(&self) -> Option<&T> { + match *self { + AvailableHeader::Available(ref header) => Some(header), + _ => None, + } + } +} + +impl From> for AvailableHeader { + fn from(maybe_header: Option) -> AvailableHeader { + match maybe_header { + Some(header) => AvailableHeader::Available(header), + None => AvailableHeader::Missing, + } + } +} + +/// Source client used in parachain heads synchronization loop. +#[async_trait] +pub trait SourceClient: RelayClient { + /// Returns `Ok(true)` if client is in synced state. + async fn ensure_synced(&self) -> Result; + + /// Get parachain head id at given block. + async fn parachain_head( + &self, + at_block: HeaderIdOf, + ) -> Result>, Self::Error>; + + /// Get parachain head proof at given block. + async fn prove_parachain_head( + &self, + at_block: HeaderIdOf, + ) -> Result<(ParaHeadsProof, ParaHash), Self::Error>; +} + +/// Target client used in parachain heads synchronization loop. +#[async_trait] +pub trait TargetClient: RelayClient { + /// Transaction tracker to track submitted transactions. + type TransactionTracker: TransactionTracker>; + + /// Get best block id. + async fn best_block(&self) -> Result, Self::Error>; + + /// Get best finalized source relay chain block id. + async fn best_finalized_source_relay_chain_block( + &self, + at_block: &HeaderIdOf, + ) -> Result, Self::Error>; + + /// Get parachain head id at given block. + async fn parachain_head( + &self, + at_block: HeaderIdOf, + ) -> Result>, Self::Error>; + + /// Submit parachain heads proof. + async fn submit_parachain_head_proof( + &self, + at_source_block: HeaderIdOf, + para_head_hash: ParaHash, + proof: ParaHeadsProof, + ) -> Result; +} + +/// Return prefix that will be used by default to expose Prometheus metrics of the parachains +/// sync loop. +pub fn metrics_prefix() -> String { + format!( + "{}_to_{}_Parachains_{}", + P::SourceRelayChain::NAME, + P::TargetChain::NAME, + P::SourceParachain::PARACHAIN_ID + ) +} + +/// Run parachain heads synchronization. +pub async fn run( + source_client: impl SourceClient

, + target_client: impl TargetClient

, + metrics_params: MetricsParams, + exit_signal: impl Future + 'static + Send, +) -> Result<(), relay_utils::Error> +where + P::SourceRelayChain: Chain, +{ + let exit_signal = exit_signal.shared(); + relay_utils::relay_loop(source_client, target_client) + .with_metrics(metrics_params) + .loop_metric(ParachainsLoopMetrics::new(Some(&metrics_prefix::

()))?)? + .expose() + .await? + .run(metrics_prefix::

(), move |source_client, target_client, metrics| { + run_until_connection_lost(source_client, target_client, metrics, exit_signal.clone()) + }) + .await +} + +/// Run parachain heads synchronization. +async fn run_until_connection_lost( + source_client: impl SourceClient

, + target_client: impl TargetClient

, + metrics: Option, + exit_signal: impl Future + Send, +) -> Result<(), FailedClient> +where + P::SourceRelayChain: Chain, +{ + let exit_signal = exit_signal.fuse(); + let min_block_interval = std::cmp::min( + P::SourceRelayChain::AVERAGE_BLOCK_INTERVAL, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + ); + + let mut submitted_heads_tracker: Option> = None; + + futures::pin_mut!(exit_signal); + + // Note that the internal loop breaks with `FailedClient` error even if error is non-connection. + // It is Ok for now, but it may need to be fixed in the future to use exponential backoff for + // regular errors. + + loop { + // Either wait for new block, or exit signal. + // Please note that we are prioritizing the exit signal since if both events happen at once + // it doesn't make sense to perform one more loop iteration. + select_biased! { + _ = exit_signal => return Ok(()), + _ = async_std::task::sleep(min_block_interval).fuse() => {}, + } + + // if source client is not yet synced, we'll need to sleep. Otherwise we risk submitting too + // much redundant transactions + match source_client.ensure_synced().await { + Ok(true) => (), + Ok(false) => { + log::warn!( + target: "bridge", + "{} client is syncing. Won't do anything until it is synced", + P::SourceRelayChain::NAME, + ); + continue + }, + Err(e) => { + log::warn!( + target: "bridge", + "{} client has failed to return its sync status: {:?}", + P::SourceRelayChain::NAME, + e, + ); + return Err(FailedClient::Source) + }, + } + + // if we have active transaction, we'll need to wait until it is mined or dropped + let best_target_block = target_client.best_block().await.map_err(|e| { + log::warn!(target: "bridge", "Failed to read best {} block: {:?}", P::SourceRelayChain::NAME, e); + FailedClient::Target + })?; + let head_at_target = + read_head_at_target(&target_client, metrics.as_ref(), &best_target_block).await?; + + // check if our transaction has been mined + if let Some(tracker) = submitted_heads_tracker.take() { + match tracker.update(&best_target_block, &head_at_target).await { + SubmittedHeadStatus::Waiting(tracker) => { + // no news about our transaction and we shall keep waiting + submitted_heads_tracker = Some(tracker); + continue + }, + SubmittedHeadStatus::Final(TrackedTransactionStatus::Finalized(_)) => { + // all heads have been updated, we don't need this tracker anymore + }, + SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost) => { + log::warn!( + target: "bridge", + "Parachains synchronization from {} to {} has stalled. Going to restart", + P::SourceRelayChain::NAME, + P::TargetChain::NAME, + ); + + return Err(FailedClient::Both) + }, + } + } + + // we have no active transaction and may need to update heads, but do we have something for + // update? + let best_finalized_relay_block = target_client + .best_finalized_source_relay_chain_block(&best_target_block) + .await + .map_err(|e| { + log::warn!( + target: "bridge", + "Failed to read best finalized {} block from {}: {:?}", + P::SourceRelayChain::NAME, + P::TargetChain::NAME, + e, + ); + FailedClient::Target + })?; + let head_at_source = + read_head_at_source(&source_client, metrics.as_ref(), &best_finalized_relay_block) + .await?; + let is_update_required = is_update_required::

(head_at_source, head_at_target); + + if is_update_required { + let (head_proof, head_hash) = source_client + .prove_parachain_head(best_finalized_relay_block) + .await + .map_err(|e| { + log::warn!( + target: "bridge", + "Failed to prove {} parachain ParaId({}) heads: {:?}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, + e, + ); + FailedClient::Source + })?; + log::info!( + target: "bridge", + "Submitting {} parachain ParaId({}) head update transaction to {}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, + P::TargetChain::NAME, + ); + + let transaction_tracker = target_client + .submit_parachain_head_proof(best_finalized_relay_block, head_hash, head_proof) + .await + .map_err(|e| { + log::warn!( + target: "bridge", + "Failed to submit {} parachain ParaId({}) heads proof to {}: {:?}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, + P::TargetChain::NAME, + e, + ); + FailedClient::Target + })?; + submitted_heads_tracker = + Some(SubmittedHeadsTracker::

::new(head_at_source, transaction_tracker)); + } + } +} + +/// Returns `true` if we need to submit parachain-head-update transaction. +fn is_update_required( + head_at_source: AvailableHeader>, + head_at_target: Option>, +) -> bool +where + P::SourceRelayChain: Chain, +{ + log::trace!( + target: "bridge", + "Checking if {} parachain ParaId({}) needs update at {}:\n\t\ + At {}: {:?}\n\t\ + At {}: {:?}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, + P::TargetChain::NAME, + P::SourceRelayChain::NAME, + head_at_source, + P::TargetChain::NAME, + head_at_target, + ); + + let needs_update = match (head_at_source, head_at_target) { + (AvailableHeader::Unavailable, _) => { + // source client has politely asked us not to update current parachain head + // at the target chain + false + }, + (AvailableHeader::Available(head_at_source), Some(head_at_target)) + if head_at_source.number() > head_at_target.number() => + { + // source client knows head that is better than the head known to the target + // client + true + }, + (AvailableHeader::Available(_), Some(_)) => { + // this is normal case when relay has recently updated heads, when parachain is + // not progressing, or when our source client is still syncing + false + }, + (AvailableHeader::Available(_), None) => { + // parachain is not yet known to the target client. This is true when parachain + // or bridge has been just onboarded/started + true + }, + (AvailableHeader::Missing, Some(_)) => { + // parachain/parathread has been offboarded removed from the system. It needs to + // be propageted to the target client + true + }, + (AvailableHeader::Missing, None) => { + // all's good - parachain is unknown to both clients + false + }, + }; + + if needs_update { + log::trace!( + target: "bridge", + "{} parachain ParaId({}) needs update at {}: {:?} vs {:?}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, + P::TargetChain::NAME, + head_at_source, + head_at_target, + ); + } + + needs_update +} + +/// Reads parachain head from the source client. +async fn read_head_at_source( + source_client: &impl SourceClient

, + metrics: Option<&ParachainsLoopMetrics>, + at_relay_block: &HeaderIdOf, +) -> Result>, FailedClient> { + let para_head = source_client.parachain_head(*at_relay_block).await; + match para_head { + Ok(AvailableHeader::Available(para_head)) => { + if let Some(metrics) = metrics { + metrics.update_best_parachain_block_at_source( + ParaId(P::SourceParachain::PARACHAIN_ID), + para_head.number(), + ); + } + Ok(AvailableHeader::Available(para_head)) + }, + Ok(r) => Ok(r), + Err(e) => { + log::warn!( + target: "bridge", + "Failed to read head of {} parachain ParaId({:?}): {:?}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, + e, + ); + Err(FailedClient::Source) + }, + } +} + +/// Reads parachain head from the target client. +async fn read_head_at_target( + target_client: &impl TargetClient

, + metrics: Option<&ParachainsLoopMetrics>, + at_block: &HeaderIdOf, +) -> Result>, FailedClient> { + let para_head_id = target_client.parachain_head(*at_block).await; + match para_head_id { + Ok(Some(para_head_id)) => { + if let Some(metrics) = metrics { + metrics.update_best_parachain_block_at_target( + ParaId(P::SourceParachain::PARACHAIN_ID), + para_head_id.number(), + ); + } + Ok(Some(para_head_id)) + }, + Ok(None) => Ok(None), + Err(e) => { + log::warn!( + target: "bridge", + "Failed to read head of {} parachain ParaId({}) at {}: {:?}", + P::SourceRelayChain::NAME, + P::SourceParachain::PARACHAIN_ID, + P::TargetChain::NAME, + e, + ); + Err(FailedClient::Target) + }, + } +} + +/// Submitted heads status. +enum SubmittedHeadStatus { + /// Heads are not yet updated. + Waiting(SubmittedHeadsTracker

), + /// Heads transaction has either been finalized or lost (i.e. received its "final" status). + Final(TrackedTransactionStatus>), +} + +/// Type of the transaction tracker that the `SubmittedHeadsTracker` is using. +/// +/// It needs to be shared because of `poll` macro and our consuming `update` method. +type SharedTransactionTracker

= Shared< + Pin< + Box< + dyn Future< + Output = TrackedTransactionStatus< + HeaderIdOf<

::TargetChain>, + >, + > + Send, + >, + >, +>; + +/// Submitted parachain heads transaction. +struct SubmittedHeadsTracker { + /// Parachain header id that we have submitted. + submitted_head: AvailableHeader>, + /// Future that waits for submitted transaction finality or loss. + /// + /// It needs to be shared because of `poll` macro and our consuming `update` method. + transaction_tracker: SharedTransactionTracker

, +} + +impl SubmittedHeadsTracker

{ + /// Creates new parachain heads transaction tracker. + pub fn new( + submitted_head: AvailableHeader>, + transaction_tracker: impl TransactionTracker> + 'static, + ) -> Self { + SubmittedHeadsTracker { + submitted_head, + transaction_tracker: transaction_tracker.wait().fuse().boxed().shared(), + } + } + + /// Returns `None` if all submitted parachain heads have been updated. + pub async fn update( + self, + at_target_block: &HeaderIdOf, + head_at_target: &Option>, + ) -> SubmittedHeadStatus

{ + // check if our head has been updated + let is_head_updated = match (self.submitted_head, head_at_target) { + (AvailableHeader::Available(submitted_head), Some(head_at_target)) + if head_at_target.number() >= submitted_head.number() => + true, + (AvailableHeader::Missing, None) => true, + _ => false, + }; + if is_head_updated { + log::trace!( + target: "bridge", + "Head of parachain ParaId({}) has been updated at {}: {:?}", + P::SourceParachain::PARACHAIN_ID, + P::TargetChain::NAME, + head_at_target, + ); + + return SubmittedHeadStatus::Final(TrackedTransactionStatus::Finalized(*at_target_block)) + } + + // if underlying transaction tracker has reported that the transaction is lost, we may + // then restart our sync + let transaction_tracker = self.transaction_tracker.clone(); + match poll!(transaction_tracker) { + Poll::Ready(TrackedTransactionStatus::Lost) => + return SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost), + Poll::Ready(TrackedTransactionStatus::Finalized(_)) => { + // so we are here and our transaction is mined+finalized, but some of heads were not + // updated => we're considering our loop as stalled + return SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost) + }, + _ => (), + } + + SubmittedHeadStatus::Waiting(self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use async_std::sync::{Arc, Mutex}; + use codec::Encode; + use futures::{SinkExt, StreamExt}; + use relay_substrate_client::test_chain::{TestChain, TestParachain}; + use relay_utils::{HeaderId, MaybeConnectionError}; + use sp_core::H256; + + const PARA_10_HASH: ParaHash = H256([10u8; 32]); + const PARA_20_HASH: ParaHash = H256([20u8; 32]); + + #[derive(Clone, Debug)] + enum TestError { + Error, + } + + impl MaybeConnectionError for TestError { + fn is_connection_error(&self) -> bool { + false + } + } + + #[derive(Clone, Debug, PartialEq, Eq)] + struct TestParachainsPipeline; + + impl ParachainsPipeline for TestParachainsPipeline { + type SourceRelayChain = TestChain; + type SourceParachain = TestParachain; + type TargetChain = TestChain; + } + + #[derive(Clone, Debug)] + struct TestClient { + data: Arc>, + } + + #[derive(Clone, Debug)] + struct TestTransactionTracker(Option>>); + + #[async_trait] + impl TransactionTracker for TestTransactionTracker { + type HeaderId = HeaderIdOf; + + async fn wait(self) -> TrackedTransactionStatus> { + match self.0 { + Some(status) => status, + None => futures::future::pending().await, + } + } + } + + #[derive(Clone, Debug)] + struct TestClientData { + source_sync_status: Result, + source_head: Result>, TestError>, + source_proof: Result<(), TestError>, + + target_best_block: Result, TestError>, + target_best_finalized_source_block: Result, TestError>, + target_head: Result>, TestError>, + target_submit_result: Result<(), TestError>, + + exit_signal_sender: Option>>, + } + + impl TestClientData { + pub fn minimal() -> Self { + TestClientData { + source_sync_status: Ok(true), + source_head: Ok(AvailableHeader::Available(HeaderId(0, PARA_20_HASH))), + source_proof: Ok(()), + + target_best_block: Ok(HeaderId(0, Default::default())), + target_best_finalized_source_block: Ok(HeaderId(0, Default::default())), + target_head: Ok(None), + target_submit_result: Ok(()), + + exit_signal_sender: None, + } + } + + pub fn with_exit_signal_sender( + sender: futures::channel::mpsc::UnboundedSender<()>, + ) -> Self { + let mut client = Self::minimal(); + client.exit_signal_sender = Some(Box::new(sender)); + client + } + } + + impl From for TestClient { + fn from(data: TestClientData) -> TestClient { + TestClient { data: Arc::new(Mutex::new(data)) } + } + } + + #[async_trait] + impl RelayClient for TestClient { + type Error = TestError; + + async fn reconnect(&mut self) -> Result<(), TestError> { + unimplemented!() + } + } + + #[async_trait] + impl SourceClient for TestClient { + async fn ensure_synced(&self) -> Result { + self.data.lock().await.source_sync_status.clone() + } + + async fn parachain_head( + &self, + _at_block: HeaderIdOf, + ) -> Result>, TestError> { + self.data.lock().await.source_head.clone() + } + + async fn prove_parachain_head( + &self, + _at_block: HeaderIdOf, + ) -> Result<(ParaHeadsProof, ParaHash), TestError> { + let head = *self.data.lock().await.source_head.clone()?.as_available().unwrap(); + let proof = (ParaHeadsProof(vec![head.hash().encode()]), head.hash()); + self.data.lock().await.source_proof.clone().map(|_| proof) + } + } + + #[async_trait] + impl TargetClient for TestClient { + type TransactionTracker = TestTransactionTracker; + + async fn best_block(&self) -> Result, TestError> { + self.data.lock().await.target_best_block.clone() + } + + async fn best_finalized_source_relay_chain_block( + &self, + _at_block: &HeaderIdOf, + ) -> Result, TestError> { + self.data.lock().await.target_best_finalized_source_block.clone() + } + + async fn parachain_head( + &self, + _at_block: HeaderIdOf, + ) -> Result>, TestError> { + self.data.lock().await.target_head.clone() + } + + async fn submit_parachain_head_proof( + &self, + _at_source_block: HeaderIdOf, + _updated_parachain_head: ParaHash, + _proof: ParaHeadsProof, + ) -> Result { + let mut data = self.data.lock().await; + data.target_submit_result.clone()?; + + if let Some(mut exit_signal_sender) = data.exit_signal_sender.take() { + exit_signal_sender.send(()).await.unwrap(); + } + Ok(TestTransactionTracker(Some( + TrackedTransactionStatus::Finalized(Default::default()), + ))) + } + } + + #[test] + fn when_source_client_fails_to_return_sync_state() { + let mut test_source_client = TestClientData::minimal(); + test_source_client.source_sync_status = Err(TestError::Error); + + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(test_source_client), + TestClient::from(TestClientData::minimal()), + None, + futures::future::pending(), + )), + Err(FailedClient::Source), + ); + } + + #[test] + fn when_target_client_fails_to_return_best_block() { + let mut test_target_client = TestClientData::minimal(); + test_target_client.target_best_block = Err(TestError::Error); + + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(TestClientData::minimal()), + TestClient::from(test_target_client), + None, + futures::future::pending(), + )), + Err(FailedClient::Target), + ); + } + + #[test] + fn when_target_client_fails_to_read_heads() { + let mut test_target_client = TestClientData::minimal(); + test_target_client.target_head = Err(TestError::Error); + + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(TestClientData::minimal()), + TestClient::from(test_target_client), + None, + futures::future::pending(), + )), + Err(FailedClient::Target), + ); + } + + #[test] + fn when_target_client_fails_to_read_best_finalized_source_block() { + let mut test_target_client = TestClientData::minimal(); + test_target_client.target_best_finalized_source_block = Err(TestError::Error); + + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(TestClientData::minimal()), + TestClient::from(test_target_client), + None, + futures::future::pending(), + )), + Err(FailedClient::Target), + ); + } + + #[test] + fn when_source_client_fails_to_read_heads() { + let mut test_source_client = TestClientData::minimal(); + test_source_client.source_head = Err(TestError::Error); + + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(test_source_client), + TestClient::from(TestClientData::minimal()), + None, + futures::future::pending(), + )), + Err(FailedClient::Source), + ); + } + + #[test] + fn when_source_client_fails_to_prove_heads() { + let mut test_source_client = TestClientData::minimal(); + test_source_client.source_proof = Err(TestError::Error); + + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(test_source_client), + TestClient::from(TestClientData::minimal()), + None, + futures::future::pending(), + )), + Err(FailedClient::Source), + ); + } + + #[test] + fn when_target_client_rejects_update_transaction() { + let mut test_target_client = TestClientData::minimal(); + test_target_client.target_submit_result = Err(TestError::Error); + + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(TestClientData::minimal()), + TestClient::from(test_target_client), + None, + futures::future::pending(), + )), + Err(FailedClient::Target), + ); + } + + #[test] + fn minimal_working_case() { + let (exit_signal_sender, exit_signal) = futures::channel::mpsc::unbounded(); + assert_eq!( + async_std::task::block_on(run_until_connection_lost( + TestClient::from(TestClientData::minimal()), + TestClient::from(TestClientData::with_exit_signal_sender(exit_signal_sender)), + None, + exit_signal.into_future().map(|(_, _)| ()), + )), + Ok(()), + ); + } + + fn test_tx_tracker() -> SubmittedHeadsTracker { + SubmittedHeadsTracker::new( + AvailableHeader::Available(HeaderId(20, PARA_20_HASH)), + TestTransactionTracker(None), + ) + } + + impl From> for Option<()> { + fn from(status: SubmittedHeadStatus) -> Option<()> { + match status { + SubmittedHeadStatus::Waiting(_) => Some(()), + _ => None, + } + } + } + + #[async_std::test] + async fn tx_tracker_update_when_head_at_target_has_none_value() { + assert_eq!( + Some(()), + test_tx_tracker() + .update(&HeaderId(0, Default::default()), &Some(HeaderId(10, PARA_10_HASH))) + .await + .into(), + ); + } + + #[async_std::test] + async fn tx_tracker_update_when_head_at_target_has_old_value() { + assert_eq!( + Some(()), + test_tx_tracker() + .update(&HeaderId(0, Default::default()), &Some(HeaderId(10, PARA_10_HASH))) + .await + .into(), + ); + } + + #[async_std::test] + async fn tx_tracker_update_when_head_at_target_has_same_value() { + assert!(matches!( + test_tx_tracker() + .update(&HeaderId(0, Default::default()), &Some(HeaderId(20, PARA_20_HASH))) + .await, + SubmittedHeadStatus::Final(TrackedTransactionStatus::Finalized(_)), + )); + } + + #[async_std::test] + async fn tx_tracker_update_when_head_at_target_has_better_value() { + assert!(matches!( + test_tx_tracker() + .update(&HeaderId(0, Default::default()), &Some(HeaderId(30, PARA_20_HASH))) + .await, + SubmittedHeadStatus::Final(TrackedTransactionStatus::Finalized(_)), + )); + } + + #[async_std::test] + async fn tx_tracker_update_when_tx_is_lost() { + let mut tx_tracker = test_tx_tracker(); + tx_tracker.transaction_tracker = + futures::future::ready(TrackedTransactionStatus::Lost).boxed().shared(); + assert!(matches!( + tx_tracker + .update(&HeaderId(0, Default::default()), &Some(HeaderId(10, PARA_10_HASH))) + .await, + SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost), + )); + } + + #[async_std::test] + async fn tx_tracker_update_when_tx_is_finalized_but_heads_are_not_updated() { + let mut tx_tracker = test_tx_tracker(); + tx_tracker.transaction_tracker = + futures::future::ready(TrackedTransactionStatus::Finalized(Default::default())) + .boxed() + .shared(); + assert!(matches!( + tx_tracker + .update(&HeaderId(0, Default::default()), &Some(HeaderId(10, PARA_10_HASH))) + .await, + SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost), + )); + } + + #[test] + fn parachain_is_not_updated_if_it_is_unavailable() { + assert!(!is_update_required::(AvailableHeader::Unavailable, None)); + assert!(!is_update_required::( + AvailableHeader::Unavailable, + Some(HeaderId(10, PARA_10_HASH)) + )); + } + + #[test] + fn parachain_is_not_updated_if_it_is_unknown_to_both_clients() { + assert!(!is_update_required::(AvailableHeader::Missing, None),); + } + + #[test] + fn parachain_is_not_updated_if_target_has_better_head() { + assert!(!is_update_required::( + AvailableHeader::Available(HeaderId(10, Default::default())), + Some(HeaderId(20, Default::default())), + ),); + } + + #[test] + fn parachain_is_updated_after_offboarding() { + assert!(is_update_required::( + AvailableHeader::Missing, + Some(HeaderId(20, Default::default())), + ),); + } + + #[test] + fn parachain_is_updated_after_onboarding() { + assert!(is_update_required::( + AvailableHeader::Available(HeaderId(30, Default::default())), + None, + ),); + } + + #[test] + fn parachain_is_updated_if_newer_head_is_known() { + assert!(is_update_required::( + AvailableHeader::Available(HeaderId(40, Default::default())), + Some(HeaderId(30, Default::default())), + ),); + } +} diff --git a/relays/parachains/src/parachains_loop_metrics.rs b/relays/parachains/src/parachains_loop_metrics.rs new file mode 100644 index 00000000000..8138a43b3b3 --- /dev/null +++ b/relays/parachains/src/parachains_loop_metrics.rs @@ -0,0 +1,86 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use bp_polkadot_core::parachains::ParaId; +use relay_utils::{ + metrics::{metric_name, register, Gauge, Metric, PrometheusError, Registry, U64}, + UniqueSaturatedInto, +}; + +/// Parachains sync metrics. +#[derive(Clone)] +pub struct ParachainsLoopMetrics { + /// Best parachains header numbers at the source. + best_source_block_numbers: Gauge, + /// Best parachains header numbers at the target. + best_target_block_numbers: Gauge, +} + +impl ParachainsLoopMetrics { + /// Create and register parachains loop metrics. + pub fn new(prefix: Option<&str>) -> Result { + Ok(ParachainsLoopMetrics { + best_source_block_numbers: Gauge::new( + metric_name(prefix, "best_parachain_block_number_at_source"), + "Best parachain block numbers at the source relay chain".to_string(), + )?, + best_target_block_numbers: Gauge::new( + metric_name(prefix, "best_parachain_block_number_at_target"), + "Best parachain block numbers at the target chain".to_string(), + )?, + }) + } + + /// Update best block number at source. + pub fn update_best_parachain_block_at_source>( + &self, + parachain: ParaId, + block_number: Number, + ) { + let block_number = block_number.unique_saturated_into(); + log::trace!( + target: "bridge-metrics", + "Updated value of metric 'best_parachain_block_number_at_source[{:?}]': {:?}", + parachain, + block_number, + ); + self.best_source_block_numbers.set(block_number); + } + + /// Update best block number at target. + pub fn update_best_parachain_block_at_target>( + &self, + parachain: ParaId, + block_number: Number, + ) { + let block_number = block_number.unique_saturated_into(); + log::trace!( + target: "bridge-metrics", + "Updated value of metric 'best_parachain_block_number_at_target[{:?}]': {:?}", + parachain, + block_number, + ); + self.best_target_block_numbers.set(block_number); + } +} + +impl Metric for ParachainsLoopMetrics { + fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { + register(self.best_source_block_numbers.clone(), registry)?; + register(self.best_target_block_numbers.clone(), registry)?; + Ok(()) + } +} diff --git a/relays/utils/Cargo.toml b/relays/utils/Cargo.toml new file mode 100644 index 00000000000..aa14e2ae2e3 --- /dev/null +++ b/relays/utils/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "relay-utils" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +[dependencies] +ansi_term = "0.12" +anyhow = "1.0" +async-std = "1.6.5" +async-trait = "0.1" +backoff = "0.4" +isahc = "1.2" +env_logger = "0.10.0" +futures = "0.3.28" +jsonpath_lib = "0.3" +log = "0.4.17" +num-traits = "0.2" +serde_json = "1.0" +sysinfo = "0.28" +time = { version = "0.3", features = ["formatting", "local-offset", "std"] } +tokio = { version = "1.27", features = ["rt"] } +thiserror = "1.0.40" + +# Bridge dependencies + +bp-runtime = { path = "../../primitives/runtime" } + +# Substrate dependencies + +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +substrate-prometheus-endpoint = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/utils/src/error.rs b/relays/utils/src/error.rs new file mode 100644 index 00000000000..26f1d0cacef --- /dev/null +++ b/relays/utils/src/error.rs @@ -0,0 +1,46 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. +// Parity Bridges Common 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. +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use std::net::AddrParseError; +use thiserror::Error; + +/// Result type used by relay utilities. +pub type Result = std::result::Result; + +/// Relay utilities errors. +#[derive(Error, Debug)] +pub enum Error { + /// Failed to request a float value from HTTP service. + #[error("Failed to fetch token price from remote server: {0}")] + FetchTokenPrice(#[source] anyhow::Error), + /// Failed to parse the response from HTTP service. + #[error("Failed to parse HTTP service response: {0:?}. Response: {1:?}")] + ParseHttp(serde_json::Error, String), + /// Failed to select response value from the Json response. + #[error("Failed to select value from response: {0:?}. Response: {1:?}")] + SelectResponseValue(jsonpath_lib::JsonPathError, String), + /// Failed to parse float value from the selected value. + #[error( + "Failed to parse float value {0:?} from response. It is assumed to be positive and normal" + )] + ParseFloat(f64), + /// Couldn't found value in the JSON response. + #[error("Missing required value from response: {0:?}")] + MissingResponseValue(String), + /// Invalid host address was used for exposing Prometheus metrics. + #[error("Invalid host {0} is used to expose Prometheus metrics: {1}")] + ExposingMetricsInvalidHost(String, AddrParseError), + /// Prometheus error. + #[error("{0}")] + Prometheus(#[from] substrate_prometheus_endpoint::prometheus::Error), +} diff --git a/relays/utils/src/initialize.rs b/relays/utils/src/initialize.rs new file mode 100644 index 00000000000..8224c1803ad --- /dev/null +++ b/relays/utils/src/initialize.rs @@ -0,0 +1,136 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Relayer initialization functions. + +use std::{cell::RefCell, fmt::Display, io::Write}; + +async_std::task_local! { + pub(crate) static LOOP_NAME: RefCell = RefCell::new(String::default()); +} + +/// Initialize relay environment. +pub fn initialize_relay() { + initialize_logger(true); +} + +/// Initialize Relay logger instance. +pub fn initialize_logger(with_timestamp: bool) { + let format = time::format_description::parse( + "[year]-[month]-[day] \ + [hour repr:24]:[minute]:[second] [offset_hour sign:mandatory]", + ) + .expect("static format string is valid"); + + let mut builder = env_logger::Builder::new(); + builder.filter_level(log::LevelFilter::Warn); + builder.filter_module("bridge", log::LevelFilter::Info); + builder.parse_default_env(); + if with_timestamp { + builder.format(move |buf, record| { + let timestamp = time::OffsetDateTime::now_local() + .unwrap_or_else(|_| time::OffsetDateTime::now_utc()); + let timestamp = timestamp.format(&format).unwrap_or_else(|_| timestamp.to_string()); + + let log_level = color_level(record.level()); + let log_target = color_target(record.target()); + let timestamp = if cfg!(windows) { + Either::Left(timestamp) + } else { + Either::Right(ansi_term::Colour::Fixed(8).bold().paint(timestamp)) + }; + + writeln!( + buf, + "{}{} {} {} {}", + loop_name_prefix(), + timestamp, + log_level, + log_target, + record.args(), + ) + }); + } else { + builder.format(move |buf, record| { + let log_level = color_level(record.level()); + let log_target = color_target(record.target()); + + writeln!(buf, "{}{log_level} {log_target} {}", loop_name_prefix(), record.args(),) + }); + } + + builder.init(); +} + +/// Initialize relay loop. Must only be called once per every loop task. +pub(crate) fn initialize_loop(loop_name: String) { + LOOP_NAME.with(|g_loop_name| *g_loop_name.borrow_mut() = loop_name); +} + +/// Returns loop name prefix to use in logs. The prefix is initialized with the `initialize_loop` +/// call. +fn loop_name_prefix() -> String { + // try_with to avoid panic outside of async-std task context + LOOP_NAME + .try_with(|loop_name| { + // using borrow is ok here, because loop is only initialized once (=> borrow_mut will + // only be called once) + let loop_name = loop_name.borrow(); + if loop_name.is_empty() { + String::new() + } else { + format!("[{loop_name}] ") + } + }) + .unwrap_or_else(|_| String::new()) +} + +enum Either { + Left(A), + Right(B), +} +impl Display for Either { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Left(a) => write!(fmt, "{a}"), + Self::Right(b) => write!(fmt, "{b}"), + } + } +} + +fn color_target(target: &str) -> impl Display + '_ { + if cfg!(windows) { + Either::Left(target) + } else { + Either::Right(ansi_term::Colour::Fixed(8).paint(target)) + } +} + +fn color_level(level: log::Level) -> impl Display { + if cfg!(windows) { + Either::Left(level) + } else { + let s = level.to_string(); + use ansi_term::Colour as Color; + Either::Right(match level { + log::Level::Error => Color::Fixed(9).bold().paint(s), + log::Level::Warn => Color::Fixed(11).bold().paint(s), + log::Level::Info => Color::Fixed(10).paint(s), + log::Level::Debug => Color::Fixed(14).paint(s), + log::Level::Trace => Color::Fixed(12).paint(s), + }) + } +} diff --git a/relays/utils/src/lib.rs b/relays/utils/src/lib.rs new file mode 100644 index 00000000000..428ee33494c --- /dev/null +++ b/relays/utils/src/lib.rs @@ -0,0 +1,313 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Utilities used by different relays. + +pub use bp_runtime::HeaderId; +pub use error::Error; +pub use relay_loop::{relay_loop, relay_metrics}; +pub use sp_runtime::traits::{UniqueSaturatedFrom, UniqueSaturatedInto}; + +use async_trait::async_trait; +use backoff::{backoff::Backoff, ExponentialBackoff}; +use futures::future::FutureExt; +use std::time::Duration; +use thiserror::Error; + +/// Default relay loop stall timeout. If transactions generated by relay are immortal, then +/// this timeout is used. +/// +/// There are no any strict requirements on block time in Substrate. But we assume here that all +/// Substrate-based chains will be designed to produce relatively fast (compared to the slowest +/// blockchains) blocks. So 1 hour seems to be a good guess for (even congested) chains to mine +/// transaction, or remove it from the pool. +pub const STALL_TIMEOUT: Duration = Duration::from_secs(60 * 60); + +/// Max delay after connection-unrelated error happened before we'll try the +/// same request again. +pub const MAX_BACKOFF_INTERVAL: Duration = Duration::from_secs(60); +/// Delay after connection-related error happened before we'll try +/// reconnection again. +pub const CONNECTION_ERROR_DELAY: Duration = Duration::from_secs(10); + +pub mod error; +pub mod initialize; +pub mod metrics; +pub mod relay_loop; + +/// Block number traits shared by all chains that relay is able to serve. +pub trait BlockNumberBase: + 'static + + From + + UniqueSaturatedInto + + Ord + + Clone + + Copy + + Default + + Send + + Sync + + std::fmt::Debug + + std::fmt::Display + + std::hash::Hash + + std::ops::Add + + std::ops::Sub + + num_traits::CheckedSub + + num_traits::Saturating + + num_traits::Zero + + num_traits::One +{ +} + +impl BlockNumberBase for T where + T: 'static + + From + + UniqueSaturatedInto + + Ord + + Clone + + Copy + + Default + + Send + + Sync + + std::fmt::Debug + + std::fmt::Display + + std::hash::Hash + + std::ops::Add + + std::ops::Sub + + num_traits::CheckedSub + + num_traits::Saturating + + num_traits::Zero + + num_traits::One +{ +} + +/// Macro that returns (client, Err(error)) tuple from function if result is Err(error). +#[macro_export] +macro_rules! bail_on_error { + ($result: expr) => { + match $result { + (client, Ok(result)) => (client, result), + (client, Err(error)) => return (client, Err(error)), + } + }; +} + +/// Macro that returns (client, Err(error)) tuple from function if result is Err(error). +#[macro_export] +macro_rules! bail_on_arg_error { + ($result: expr, $client: ident) => { + match $result { + Ok(result) => result, + Err(error) => return ($client, Err(error)), + } + }; +} + +/// Error type that can signal connection errors. +pub trait MaybeConnectionError { + /// Returns true if error (maybe) represents connection error. + fn is_connection_error(&self) -> bool; +} + +/// Final status of the tracked transaction. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum TrackedTransactionStatus { + /// Transaction has been lost. + Lost, + /// Transaction has been mined and finalized at given block. + Finalized(BlockId), +} + +/// Transaction tracker. +#[async_trait] +pub trait TransactionTracker: Send { + /// Header id, used by the chain. + type HeaderId: Clone + Send; + + /// Wait until transaction is either finalized or invalidated/lost. + async fn wait(self) -> TrackedTransactionStatus; +} + +/// Stringified error that may be either connection-related or not. +#[derive(Error, Debug)] +pub enum StringifiedMaybeConnectionError { + /// The error is connection-related error. + #[error("{0}")] + Connection(String), + /// The error is connection-unrelated error. + #[error("{0}")] + NonConnection(String), +} + +impl StringifiedMaybeConnectionError { + /// Create new stringified connection error. + pub fn new(is_connection_error: bool, error: String) -> Self { + if is_connection_error { + StringifiedMaybeConnectionError::Connection(error) + } else { + StringifiedMaybeConnectionError::NonConnection(error) + } + } +} + +impl MaybeConnectionError for StringifiedMaybeConnectionError { + fn is_connection_error(&self) -> bool { + match *self { + StringifiedMaybeConnectionError::Connection(_) => true, + StringifiedMaybeConnectionError::NonConnection(_) => false, + } + } +} + +/// Exponential backoff for connection-unrelated errors retries. +pub fn retry_backoff() -> ExponentialBackoff { + ExponentialBackoff { + // we do not want relayer to stop + max_elapsed_time: None, + max_interval: MAX_BACKOFF_INTERVAL, + ..Default::default() + } +} + +/// Compact format of IDs vector. +pub fn format_ids(mut ids: impl ExactSizeIterator) -> String { + const NTH_PROOF: &str = "we have checked len; qed"; + match ids.len() { + 0 => "".into(), + 1 => format!("{:?}", ids.next().expect(NTH_PROOF)), + 2 => { + let id0 = ids.next().expect(NTH_PROOF); + let id1 = ids.next().expect(NTH_PROOF); + format!("[{id0:?}, {id1:?}]") + }, + len => { + let id0 = ids.next().expect(NTH_PROOF); + let id_last = ids.last().expect(NTH_PROOF); + format!("{len}:[{id0:?} ... {id_last:?}]") + }, + } +} + +/// Stream that emits item every `timeout_ms` milliseconds. +pub fn interval(timeout: Duration) -> impl futures::Stream { + futures::stream::unfold((), move |_| async move { + async_std::task::sleep(timeout).await; + Some(((), ())) + }) +} + +/// Which client has caused error. +#[derive(Debug, Eq, Clone, Copy, PartialEq)] +pub enum FailedClient { + /// It is the source client who has caused error. + Source, + /// It is the target client who has caused error. + Target, + /// Both clients are failing, or we just encountered some other error that + /// should be treated like that. + Both, +} + +/// Future process result. +#[derive(Debug, Clone, Copy)] +pub enum ProcessFutureResult { + /// Future has been processed successfully. + Success, + /// Future has failed with non-connection error. + Failed, + /// Future has failed with connection error. + ConnectionFailed, +} + +impl ProcessFutureResult { + /// Returns true if result is Success. + pub fn is_ok(self) -> bool { + match self { + ProcessFutureResult::Success => true, + ProcessFutureResult::Failed | ProcessFutureResult::ConnectionFailed => false, + } + } + + /// Returns `Ok(())` if future has succeeded. + /// Returns `Err(failed_client)` otherwise. + pub fn fail_if_error(self, failed_client: FailedClient) -> Result<(), FailedClient> { + if self.is_ok() { + Ok(()) + } else { + Err(failed_client) + } + } + + /// Returns Ok(true) if future has succeeded. + /// Returns Ok(false) if future has failed with non-connection error. + /// Returns Err if future is `ConnectionFailed`. + pub fn fail_if_connection_error( + self, + failed_client: FailedClient, + ) -> Result { + match self { + ProcessFutureResult::Success => Ok(true), + ProcessFutureResult::Failed => Ok(false), + ProcessFutureResult::ConnectionFailed => Err(failed_client), + } + } +} + +/// Process result of the future from a client. +pub fn process_future_result( + result: Result, + retry_backoff: &mut ExponentialBackoff, + on_success: impl FnOnce(TResult), + go_offline_future: &mut std::pin::Pin<&mut futures::future::Fuse>, + go_offline: impl FnOnce(Duration) -> TGoOfflineFuture, + error_pattern: impl FnOnce() -> String, +) -> ProcessFutureResult +where + TError: std::fmt::Debug + MaybeConnectionError, + TGoOfflineFuture: FutureExt, +{ + match result { + Ok(result) => { + on_success(result); + retry_backoff.reset(); + ProcessFutureResult::Success + }, + Err(error) if error.is_connection_error() => { + log::error!( + target: "bridge", + "{}: {:?}. Going to restart", + error_pattern(), + error, + ); + + retry_backoff.reset(); + go_offline_future.set(go_offline(CONNECTION_ERROR_DELAY).fuse()); + ProcessFutureResult::ConnectionFailed + }, + Err(error) => { + let retry_delay = retry_backoff.next_backoff().unwrap_or(CONNECTION_ERROR_DELAY); + log::error!( + target: "bridge", + "{}: {:?}. Retrying in {}", + error_pattern(), + error, + retry_delay.as_secs_f64(), + ); + + go_offline_future.set(go_offline(retry_delay).fuse()); + ProcessFutureResult::Failed + }, + } +} diff --git a/relays/utils/src/metrics.rs b/relays/utils/src/metrics.rs new file mode 100644 index 00000000000..2e6c8236da4 --- /dev/null +++ b/relays/utils/src/metrics.rs @@ -0,0 +1,192 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +pub use float_json_value::FloatJsonValueMetric; +pub use global::GlobalMetrics; +pub use substrate_prometheus_endpoint::{ + prometheus::core::{Atomic, Collector}, + register, Counter, CounterVec, Gauge, GaugeVec, Opts, PrometheusError, Registry, F64, I64, U64, +}; + +use async_std::sync::{Arc, RwLock}; +use async_trait::async_trait; +use std::{fmt::Debug, time::Duration}; + +mod float_json_value; +mod global; + +/// Shared reference to `f64` value that is updated by the metric. +pub type F64SharedRef = Arc>>; +/// Int gauge metric type. +pub type IntGauge = Gauge; + +/// Unparsed address that needs to be used to expose Prometheus metrics. +#[derive(Debug, Clone)] +pub struct MetricsAddress { + /// Serve HTTP requests at given host. + pub host: String, + /// Serve HTTP requests at given port. + pub port: u16, +} + +/// Prometheus endpoint MetricsParams. +#[derive(Debug, Clone)] +pub struct MetricsParams { + /// Interface and TCP port to be used when exposing Prometheus metrics. + pub address: Option, + /// Metrics registry. May be `Some(_)` if several components share the same endpoint. + pub registry: Registry, +} + +/// Metric API. +pub trait Metric: Clone + Send + Sync + 'static { + fn register(&self, registry: &Registry) -> Result<(), PrometheusError>; +} + +/// Standalone metric API. +/// +/// Metrics of this kind know how to update themselves, so we may just spawn and forget the +/// asynchronous self-update task. +#[async_trait] +pub trait StandaloneMetric: Metric { + /// Update metric values. + async fn update(&self); + + /// Metrics update interval. + fn update_interval(&self) -> Duration; + + /// Register and spawn metric. Metric is only spawned if it is registered for the first time. + fn register_and_spawn(self, registry: &Registry) -> Result<(), PrometheusError> { + match self.register(registry) { + Ok(()) => { + self.spawn(); + Ok(()) + }, + Err(PrometheusError::AlreadyReg) => Ok(()), + Err(e) => Err(e), + } + } + + /// Spawn the self update task that will keep update metric value at given intervals. + fn spawn(self) { + async_std::task::spawn(async move { + let update_interval = self.update_interval(); + loop { + self.update().await; + async_std::task::sleep(update_interval).await; + } + }); + } +} + +impl Default for MetricsAddress { + fn default() -> Self { + MetricsAddress { host: "127.0.0.1".into(), port: 9616 } + } +} + +impl MetricsParams { + /// Creates metrics params from metrics address. + pub fn new( + address: Option, + relay_version: String, + relay_commit: String, + ) -> Result { + const BUILD_INFO_METRIC: &str = "substrate_relay_build_info"; + + let registry = Registry::new(); + register( + Gauge::::with_opts( + Opts::new( + BUILD_INFO_METRIC, + "A metric with a constant '1' value labeled by version", + ) + .const_label("version", &relay_version) + .const_label("commit", &relay_commit), + )?, + ®istry, + )? + .set(1); + + log::info!( + target: "bridge", + "Exposed {} metric: version={} commit={}", + BUILD_INFO_METRIC, + relay_version, + relay_commit, + ); + + Ok(MetricsParams { address, registry }) + } + + /// Creates metrics params so that metrics are not exposed. + pub fn disabled() -> Self { + MetricsParams { address: None, registry: Registry::new() } + } + + /// Do not expose metrics. + #[must_use] + pub fn disable(mut self) -> Self { + self.address = None; + self + } +} + +/// Returns metric name optionally prefixed with given prefix. +pub fn metric_name(prefix: Option<&str>, name: &str) -> String { + if let Some(prefix) = prefix { + format!("{prefix}_{name}") + } else { + name.into() + } +} + +/// Set value of gauge metric. +/// +/// If value is `Ok(None)` or `Err(_)`, metric would have default value. +pub fn set_gauge_value, E: Debug>( + gauge: &Gauge, + value: Result, E>, +) { + gauge.set(match value { + Ok(Some(value)) => { + log::trace!( + target: "bridge-metrics", + "Updated value of metric '{:?}': {:?}", + gauge.desc().first().map(|d| &d.fq_name), + value, + ); + value + }, + Ok(None) => { + log::warn!( + target: "bridge-metrics", + "Failed to update metric '{:?}': value is empty", + gauge.desc().first().map(|d| &d.fq_name), + ); + Default::default() + }, + Err(error) => { + log::warn!( + target: "bridge-metrics", + "Failed to update metric '{:?}': {:?}", + gauge.desc().first().map(|d| &d.fq_name), + error, + ); + Default::default() + }, + }) +} diff --git a/relays/utils/src/metrics/float_json_value.rs b/relays/utils/src/metrics/float_json_value.rs new file mode 100644 index 00000000000..17b09e05097 --- /dev/null +++ b/relays/utils/src/metrics/float_json_value.rs @@ -0,0 +1,147 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{ + error::{self, Error}, + metrics::{ + metric_name, register, F64SharedRef, Gauge, Metric, PrometheusError, Registry, + StandaloneMetric, F64, + }, +}; + +use async_std::sync::{Arc, RwLock}; +use async_trait::async_trait; +use std::time::Duration; + +/// Value update interval. +const UPDATE_INTERVAL: Duration = Duration::from_secs(300); + +/// Metric that represents float value received from HTTP service as float gauge. +/// +/// The float value returned by the service is assumed to be normal (`f64::is_normal` +/// should return `true`) and strictly positive. +#[derive(Debug, Clone)] +pub struct FloatJsonValueMetric { + url: String, + json_path: String, + metric: Gauge, + shared_value_ref: F64SharedRef, +} + +impl FloatJsonValueMetric { + /// Create new metric instance with given name and help. + pub fn new( + url: String, + json_path: String, + name: String, + help: String, + ) -> Result { + let shared_value_ref = Arc::new(RwLock::new(None)); + Ok(FloatJsonValueMetric { + url, + json_path, + metric: Gauge::new(metric_name(None, &name), help)?, + shared_value_ref, + }) + } + + /// Get shared reference to metric value. + pub fn shared_value_ref(&self) -> F64SharedRef { + self.shared_value_ref.clone() + } + + /// Request value from HTTP service. + async fn request_value(&self) -> anyhow::Result { + use isahc::{AsyncReadResponseExt, HttpClient, Request}; + + let request = Request::get(&self.url).header("Accept", "application/json").body(())?; + let raw_response = HttpClient::new()?.send_async(request).await?.text().await?; + Ok(raw_response) + } + + /// Read value from HTTP service. + async fn read_value(&self) -> error::Result { + let raw_response = self.request_value().await.map_err(Error::FetchTokenPrice)?; + parse_service_response(&self.json_path, &raw_response) + } +} + +impl Metric for FloatJsonValueMetric { + fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { + register(self.metric.clone(), registry).map(drop) + } +} + +#[async_trait] +impl StandaloneMetric for FloatJsonValueMetric { + fn update_interval(&self) -> Duration { + UPDATE_INTERVAL + } + + async fn update(&self) { + let value = self.read_value().await; + let maybe_ok = value.as_ref().ok().copied(); + crate::metrics::set_gauge_value(&self.metric, value.map(Some)); + *self.shared_value_ref.write().await = maybe_ok; + } +} + +/// Parse HTTP service response. +fn parse_service_response(json_path: &str, response: &str) -> error::Result { + let json = + serde_json::from_str(response).map_err(|err| Error::ParseHttp(err, response.to_owned()))?; + + let mut selector = jsonpath_lib::selector(&json); + let maybe_selected_value = + selector(json_path).map_err(|err| Error::SelectResponseValue(err, response.to_owned()))?; + let selected_value = maybe_selected_value + .first() + .and_then(|v| v.as_f64()) + .ok_or_else(|| Error::MissingResponseValue(response.to_owned()))?; + if !selected_value.is_normal() || selected_value < 0.0 { + return Err(Error::ParseFloat(selected_value)) + } + + Ok(selected_value) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_service_response_works() { + assert_eq!( + parse_service_response("$.kusama.usd", r#"{"kusama":{"usd":433.05}}"#).map_err(drop), + Ok(433.05), + ); + } + + #[test] + fn parse_service_response_rejects_negative_numbers() { + assert!(parse_service_response("$.kusama.usd", r#"{"kusama":{"usd":-433.05}}"#).is_err()); + } + + #[test] + fn parse_service_response_rejects_zero_numbers() { + assert!(parse_service_response("$.kusama.usd", r#"{"kusama":{"usd":0.0}}"#).is_err()); + } + + #[test] + fn parse_service_response_rejects_nan() { + assert!(parse_service_response("$.kusama.usd", r#"{"kusama":{"usd":NaN}}"#).is_err()); + } +} diff --git a/relays/utils/src/metrics/global.rs b/relays/utils/src/metrics/global.rs new file mode 100644 index 00000000000..f7d3e25c964 --- /dev/null +++ b/relays/utils/src/metrics/global.rs @@ -0,0 +1,118 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Global system-wide Prometheus metrics exposed by relays. + +use crate::metrics::{ + metric_name, register, Gauge, GaugeVec, Metric, Opts, PrometheusError, Registry, + StandaloneMetric, F64, U64, +}; + +use async_std::sync::{Arc, Mutex}; +use async_trait::async_trait; +use std::time::Duration; +use sysinfo::{ProcessExt, RefreshKind, System, SystemExt}; + +/// Global metrics update interval. +const UPDATE_INTERVAL: Duration = Duration::from_secs(10); + +/// Global Prometheus metrics. +#[derive(Debug, Clone)] +pub struct GlobalMetrics { + system: Arc>, + system_average_load: GaugeVec, + process_cpu_usage_percentage: Gauge, + process_memory_usage_bytes: Gauge, +} + +impl GlobalMetrics { + /// Create and register global metrics. + pub fn new() -> Result { + Ok(GlobalMetrics { + system: Arc::new(Mutex::new(System::new_with_specifics(RefreshKind::everything()))), + system_average_load: GaugeVec::new( + Opts::new(metric_name(None, "system_average_load"), "System load average"), + &["over"], + )?, + process_cpu_usage_percentage: Gauge::new( + metric_name(None, "process_cpu_usage_percentage"), + "Process CPU usage", + )?, + process_memory_usage_bytes: Gauge::new( + metric_name(None, "process_memory_usage_bytes"), + "Process memory (resident set size) usage", + )?, + }) + } +} + +impl Metric for GlobalMetrics { + fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { + register(self.system_average_load.clone(), registry)?; + register(self.process_cpu_usage_percentage.clone(), registry)?; + register(self.process_memory_usage_bytes.clone(), registry)?; + Ok(()) + } +} + +#[async_trait] +impl StandaloneMetric for GlobalMetrics { + async fn update(&self) { + // update system-wide metrics + let mut system = self.system.lock().await; + let load = system.load_average(); + self.system_average_load.with_label_values(&["1min"]).set(load.one); + self.system_average_load.with_label_values(&["5min"]).set(load.five); + self.system_average_load.with_label_values(&["15min"]).set(load.fifteen); + + // update process-related metrics + let pid = sysinfo::get_current_pid().expect( + "only fails where pid is unavailable (os=unknown || arch=wasm32);\ + relay is not supposed to run in such MetricsParamss;\ + qed", + ); + let is_process_refreshed = system.refresh_process(pid); + match (is_process_refreshed, system.process(pid)) { + (true, Some(process_info)) => { + let cpu_usage = process_info.cpu_usage() as f64; + let memory_usage = process_info.memory() * 1024; + log::trace!( + target: "bridge-metrics", + "Refreshed process metrics: CPU={}, memory={}", + cpu_usage, + memory_usage, + ); + + self.process_cpu_usage_percentage.set(if cpu_usage.is_finite() { + cpu_usage + } else { + 0f64 + }); + self.process_memory_usage_bytes.set(memory_usage); + }, + _ => { + log::warn!( + target: "bridge-metrics", + "Failed to refresh process information. Metrics may show obsolete values", + ); + }, + } + } + + fn update_interval(&self) -> Duration { + UPDATE_INTERVAL + } +} diff --git a/relays/utils/src/relay_loop.rs b/relays/utils/src/relay_loop.rs new file mode 100644 index 00000000000..11e14744a07 --- /dev/null +++ b/relays/utils/src/relay_loop.rs @@ -0,0 +1,269 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use crate::{ + error::Error, + metrics::{Metric, MetricsAddress, MetricsParams}, + FailedClient, MaybeConnectionError, +}; + +use async_trait::async_trait; +use std::{fmt::Debug, future::Future, net::SocketAddr, time::Duration}; +use substrate_prometheus_endpoint::{init_prometheus, Registry}; + +/// Default pause between reconnect attempts. +pub const RECONNECT_DELAY: Duration = Duration::from_secs(10); + +/// Basic blockchain client from relay perspective. +#[async_trait] +pub trait Client: 'static + Clone + Send + Sync { + /// Type of error these clients returns. + type Error: 'static + Debug + MaybeConnectionError + Send + Sync; + + /// Try to reconnect to source node. + async fn reconnect(&mut self) -> Result<(), Self::Error>; +} + +#[async_trait] +impl Client for () { + type Error = crate::StringifiedMaybeConnectionError; + + async fn reconnect(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} + +/// Returns generic loop that may be customized and started. +pub fn relay_loop(source_client: SC, target_client: TC) -> Loop { + Loop { reconnect_delay: RECONNECT_DELAY, source_client, target_client, loop_metric: None } +} + +/// Returns generic relay loop metrics that may be customized and used in one or several relay +/// loops. +pub fn relay_metrics(params: MetricsParams) -> LoopMetrics<(), (), ()> { + LoopMetrics { + relay_loop: Loop { + reconnect_delay: RECONNECT_DELAY, + source_client: (), + target_client: (), + loop_metric: None, + }, + address: params.address, + registry: params.registry, + loop_metric: None, + } +} + +/// Generic relay loop. +pub struct Loop { + reconnect_delay: Duration, + source_client: SC, + target_client: TC, + loop_metric: Option, +} + +/// Relay loop metrics builder. +pub struct LoopMetrics { + relay_loop: Loop, + address: Option, + registry: Registry, + loop_metric: Option, +} + +impl Loop { + /// Customize delay between reconnect attempts. + #[must_use] + pub fn reconnect_delay(mut self, reconnect_delay: Duration) -> Self { + self.reconnect_delay = reconnect_delay; + self + } + + /// Start building loop metrics using given prefix. + pub fn with_metrics(self, params: MetricsParams) -> LoopMetrics { + LoopMetrics { + relay_loop: Loop { + reconnect_delay: self.reconnect_delay, + source_client: self.source_client, + target_client: self.target_client, + loop_metric: None, + }, + address: params.address, + registry: params.registry, + loop_metric: None, + } + } + + /// Run relay loop. + /// + /// This function represents an outer loop, which in turn calls provided `run_loop` function to + /// do actual job. When `run_loop` returns, this outer loop reconnects to failed client (source, + /// target or both) and calls `run_loop` again. + pub async fn run(mut self, loop_name: String, run_loop: R) -> Result<(), Error> + where + R: 'static + Send + Fn(SC, TC, Option) -> F, + F: 'static + Send + Future>, + SC: 'static + Client, + TC: 'static + Client, + LM: 'static + Send + Clone, + { + let run_loop_task = async move { + crate::initialize::initialize_loop(loop_name); + + loop { + let loop_metric = self.loop_metric.clone(); + let future_result = + run_loop(self.source_client.clone(), self.target_client.clone(), loop_metric); + let result = future_result.await; + + match result { + Ok(()) => break, + Err(failed_client) => + reconnect_failed_client( + failed_client, + self.reconnect_delay, + &mut self.source_client, + &mut self.target_client, + ) + .await, + } + + log::debug!(target: "bridge", "Restarting relay loop"); + } + + Ok(()) + }; + + async_std::task::spawn(run_loop_task).await + } +} + +impl LoopMetrics { + /// Add relay loop metrics. + /// + /// Loop metrics will be passed to the loop callback. + pub fn loop_metric( + self, + metric: NewLM, + ) -> Result, Error> { + metric.register(&self.registry)?; + + Ok(LoopMetrics { + relay_loop: self.relay_loop, + address: self.address, + registry: self.registry, + loop_metric: Some(metric), + }) + } + + /// Convert into `MetricsParams` structure so that metrics registry may be extended later. + pub fn into_params(self) -> MetricsParams { + MetricsParams { address: self.address, registry: self.registry } + } + + /// Expose metrics using address passed at creation. + /// + /// If passed `address` is `None`, metrics are not exposed. + pub async fn expose(self) -> Result, Error> { + if let Some(address) = self.address { + let socket_addr = SocketAddr::new( + address + .host + .parse() + .map_err(|err| Error::ExposingMetricsInvalidHost(address.host.clone(), err))?, + address.port, + ); + + let registry = self.registry; + async_std::task::spawn(async move { + let runtime = + match tokio::runtime::Builder::new_current_thread().enable_all().build() { + Ok(runtime) => runtime, + Err(err) => { + log::trace!( + target: "bridge-metrics", + "Failed to create tokio runtime. Prometheus meterics are not available: {:?}", + err, + ); + return + }, + }; + + runtime.block_on(async move { + log::trace!( + target: "bridge-metrics", + "Starting prometheus endpoint at: {:?}", + socket_addr, + ); + let result = init_prometheus(socket_addr, registry).await; + log::trace!( + target: "bridge-metrics", + "Prometheus endpoint has exited with result: {:?}", + result, + ); + }); + }); + } + + Ok(Loop { + reconnect_delay: self.relay_loop.reconnect_delay, + source_client: self.relay_loop.source_client, + target_client: self.relay_loop.target_client, + loop_metric: self.loop_metric, + }) + } +} + +/// Deal with the client who has returned connection error. +pub async fn reconnect_failed_client( + failed_client: FailedClient, + reconnect_delay: Duration, + source_client: &mut impl Client, + target_client: &mut impl Client, +) { + loop { + async_std::task::sleep(reconnect_delay).await; + if failed_client == FailedClient::Both || failed_client == FailedClient::Source { + match source_client.reconnect().await { + Ok(()) => (), + Err(error) => { + log::warn!( + target: "bridge", + "Failed to reconnect to source client. Going to retry in {}s: {:?}", + reconnect_delay.as_secs(), + error, + ); + continue + }, + } + } + if failed_client == FailedClient::Both || failed_client == FailedClient::Target { + match target_client.reconnect().await { + Ok(()) => (), + Err(error) => { + log::warn!( + target: "bridge", + "Failed to reconnect to target client. Going to retry in {}s: {:?}", + reconnect_delay.as_secs(), + error, + ); + continue + }, + } + } + + break + } +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 00000000000..082150daf04 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,24 @@ +# Basic +hard_tabs = true +max_width = 100 +use_small_heuristics = "Max" +# Imports +imports_granularity = "Crate" +reorder_imports = true +# Consistency +newline_style = "Unix" +# Format comments +comment_width = 100 +wrap_comments = true +# Misc +chain_width = 80 +spaces_around_ranges = false +binop_separator = "Back" +reorder_impl_items = false +match_arm_leading_pipes = "Preserve" +match_arm_blocks = false +match_block_trailing_comma = true +trailing_comma = "Vertical" +trailing_semicolon = false +use_field_init_shorthand = true + diff --git a/scripts/add_license.sh b/scripts/add_license.sh new file mode 100755 index 00000000000..49864b47c05 --- /dev/null +++ b/scripts/add_license.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +PAT_GPL="^// Copyright.*If not, see \.$" +PAT_OTHER="^// Copyright" + +SCRIPTS_DIR=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) + +for f in $(find . -type f | egrep '\.(c|cpp|rs)$'); do + HEADER=$(head -16 $f) + if [[ $HEADER =~ $PAT_GPL ]]; then + BODY=$(tail -n +17 $f) + cat $SCRIPTS_DIR/license_header > temp + echo "$BODY" >> temp + mv temp $f + elif [[ $HEADER =~ $PAT_OTHER ]]; then + echo "Other license was found do nothing" + else + echo "$f was missing header" + cat $SCRIPTS_DIR/license_header $f > temp + mv temp $f + fi +done diff --git a/scripts/build-containers.sh b/scripts/build-containers.sh new file mode 100755 index 00000000000..d0af254d93f --- /dev/null +++ b/scripts/build-containers.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -eux + +time docker build . -t local/substrate-relay --build-arg=PROJECT=substrate-relay +time docker build . -t local/rialto-bridge-node --build-arg=PROJECT=rialto-bridge-node +time docker build . -t local/millau-bridge-node --build-arg=PROJECT=millau-bridge-node +time docker build . -t local/rialto-parachain-collator --build-arg=PROJECT=rialto-parachain-collator diff --git a/scripts/ci-cache.sh b/scripts/ci-cache.sh new file mode 100755 index 00000000000..040d44fa74a --- /dev/null +++ b/scripts/ci-cache.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -xeu + +echo $CARGO_TARGET_DIR; +mkdir -p $CARGO_TARGET_DIR; +echo "Current Rust nightly version:"; +rustc +nightly --version; +echo "Cached Rust nightly version:"; +if [ ! -f $CARGO_TARGET_DIR/check_nightly_rust ]; then + echo "" > $CARGO_TARGET_DIR/check_nightly_rust; +fi +cat $CARGO_TARGET_DIR/check_nightly_rust; +if [[ $(cat $CARGO_TARGET_DIR/check_nightly_rust) == $(rustc +nightly --version) ]]; then + echo "The Rust nightly version has not changed"; +else + echo "The Rust nightly version has changed. Clearing the cache"; + rm -rf $CARGO_TARGET_DIR/*; +fi diff --git a/scripts/dump-logs.sh b/scripts/dump-logs.sh new file mode 100755 index 00000000000..709a5065887 --- /dev/null +++ b/scripts/dump-logs.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# A script to dump logs from selected important docker containers +# to make it easier to analyze locally. + +set -xeu + +DATE=$(date +"%Y-%m-%d-%T") +LOGS_DIR="${DATE//:/-}-logs" +mkdir $LOGS_DIR +cd $LOGS_DIR + +# From $ docker ps --format '{{.Names}}' + +SERVICES=(\ + deployments_relay-messages-millau-to-rialto-generator_1 \ + deployments_relay-messages-rialto-to-millau-generator_1 \ + deployments_relay-millau-rialto_1 \ + deployments_relay-headers-westend-to-millau-1_1 \ + deployments_relay-headers-westend-to-millau-2_1 \ + deployments_relay-parachains-westend-to-millau-1_1 \ + deployments_relay-parachains-westend-to-millau-1_2 \ + deployments_relay-messages-millau-to-rialto-parachain-generator_1 \ + deployments_relay-messages-rialto-parachain-to-millau-generator_1 \ + deployments_relay-millau-rialto-parachain-1_1 \ + deployments_relay-millau-rialto-parachain-2_1 \ + deployments_rialto-node-alice_1 \ + deployments_rialto-node-bob_1 \ + deployments_millau-node-alice_1 \ + deployments_millau-node-bob_1 \ + deployments_rialto-parachain-collator-alice_1 \ + deployments_rialto-parachain-collator-bob_1 \ + deployments_relay-messages-millau-to-rialto-resubmitter_1 \ + deployments_relay-messages-millau-to-rialto-parachain-resubmitter_1 \ +) + +for SVC in ${SERVICES[*]} +do + SHORT_NAME="${SVC//deployments_/}" + docker logs $SVC &> $SHORT_NAME.log | true +done + +cd - +tar cvjf $LOGS_DIR.tar.bz2 $LOGS_DIR diff --git a/scripts/license_header b/scripts/license_header new file mode 100644 index 00000000000..f9b301209bb --- /dev/null +++ b/scripts/license_header @@ -0,0 +1,16 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + diff --git a/scripts/send-message-from-millau-rialto.sh b/scripts/send-message-from-millau-rialto.sh new file mode 100755 index 00000000000..1a736b4f8bd --- /dev/null +++ b/scripts/send-message-from-millau-rialto.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Used for manually sending a message to a running network. +# +# You could for example spin up a full network using the Docker Compose files +# we have (to make sure the message relays are running), but remove the message +# generator service. From there you may submit messages manually using this script. + +# TODO: Fix demeo scripts https://github.com/paritytech/parity-bridges-common/issues/1406 + +MILLAU_PORT="${MILLAU_PORT:-9945}" + +RUST_LOG=runtime=trace,substrate-relay=trace,bridge=trace \ +./target/debug/substrate-relay send-message millau-to-rialto \ + --source-host localhost \ + --source-port $MILLAU_PORT \ + --source-signer //Alice \ + raw 030426020109020419a8 diff --git a/scripts/send-message-from-rialto-millau.sh b/scripts/send-message-from-rialto-millau.sh new file mode 100755 index 00000000000..8133978f6ce --- /dev/null +++ b/scripts/send-message-from-rialto-millau.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Used for manually sending a message to a running network. +# +# You could for example spin up a full network using the Docker Compose files +# we have (to make sure the message relays are running), but remove the message +# generator service. From there you may submit messages manually using this script. + +# TODO: Fix demeo scripts https://github.com/paritytech/parity-bridges-common/issues/1406 + +RIALTO_PORT="${RIALTO_PORT:-9944}" + +RUST_LOG=runtime=trace,substrate-relay=trace,bridge=trace \ +./target/debug/substrate-relay send-message rialto-to-millau \ + --source-host localhost \ + --source-port $RIALTO_PORT \ + --source-signer //Bob \ + raw 030426030109030419a8 diff --git a/scripts/update-weights-setup.sh b/scripts/update-weights-setup.sh new file mode 100644 index 00000000000..72534423d63 --- /dev/null +++ b/scripts/update-weights-setup.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -exu + +# Set up the standardized machine and run `update-weights.sh` script. +# The system is assumed to be pristine Ubuntu 20.04 and we install +# all required dependencies. + +# To avoid interruptions you might want to run this script in `screen` cause it will take a while +# to finish. + +# We start off with upgrading the system +apt update && apt dist-upgrade + +# and installing `git` and other required deps. +apt install -y git clang curl libssl-dev llvm libudev-dev screen + +# Now we clone the repository +git clone https://github.com/paritytech/parity-bridges-common.git +cd parity-bridges-common + +# Install rustup & toolchain +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | bash -s -- -y + +# Source config +source ~/.cargo/env + +# Add nightly and WASM +rustup install nightly +rustup target add wasm32-unknown-unknown --toolchain nightly + +# Update the weights +./scripts/update-weights.sh diff --git a/scripts/update-weights.sh b/scripts/update-weights.sh new file mode 100755 index 00000000000..84ddf0cdaae --- /dev/null +++ b/scripts/update-weights.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# +# Runtime benchmarks for the `pallet-bridge-messages` and `pallet-bridge-grandpa` pallets. +# +# Run this script from root of the repo. + +set -eux + +time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet \ + --chain=dev \ + --steps=50 \ + --repeat=20 \ + --pallet=RialtoMessages \ + --extrinsic=* \ + --execution=wasm \ + --wasm-execution=Compiled \ + --heap-pages=4096 \ + --output=./modules/messages/src/weights.rs \ + --template=./.maintain/millau-weight-template.hbs + +time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet \ + --chain=dev \ + --steps=50 \ + --repeat=20 \ + --pallet=pallet_bridge_grandpa \ + --extrinsic=* \ + --execution=wasm \ + --wasm-execution=Compiled \ + --heap-pages=4096 \ + --output=./modules/grandpa/src/weights.rs \ + --template=./.maintain/millau-weight-template.hbs + +time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet \ + --chain=dev \ + --steps=50 \ + --repeat=20 \ + --pallet=pallet_bridge_parachains \ + --extrinsic=* \ + --execution=wasm \ + --wasm-execution=Compiled \ + --heap-pages=4096 \ + --output=./modules/parachains/src/weights.rs \ + --template=./.maintain/millau-weight-template.hbs + +time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet \ + --chain=dev \ + --steps=50 \ + --repeat=20 \ + --pallet=pallet_bridge_relayers \ + --extrinsic=* \ + --execution=wasm \ + --wasm-execution=Compiled \ + --heap-pages=4096 \ + --output=./modules/relayers/src/weights.rs \ + --template=./.maintain/millau-weight-template.hbs diff --git a/scripts/update_substrate.sh b/scripts/update_substrate.sh new file mode 100755 index 00000000000..f7715bda5d1 --- /dev/null +++ b/scripts/update_substrate.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# One-liner to update between Substrate releases +# Usage: ./update_substrate.sh 2.0.0-rc6 2.0.0 +set -xeu + +OLD_VERSION=$1 +NEW_VERSION=$2 + +find . -type f -name 'Cargo.toml' -exec sed -i '' -e "s/$OLD_VERSION/$NEW_VERSION/g" {} \; diff --git a/scripts/verify-pallets-build.sh b/scripts/verify-pallets-build.sh new file mode 100755 index 00000000000..ad69c5df008 --- /dev/null +++ b/scripts/verify-pallets-build.sh @@ -0,0 +1,133 @@ +#!/bin/bash + +# A script to remove everything from bridges repository/subtree, except: +# +# - modules/grandpa; +# - modules/messages; +# - modules/parachains; +# - modules/relayers; +# - everything required from primitives folder. + +set -eux + +# show CLI help +function show_help() { + set +x + echo " " + echo Error: $1 + echo "Usage:" + echo " ./scripts/verify-pallets-build.sh Exit with code 0 if pallets code is well decoupled from the other code in the repo" + echo "Options:" + echo " --no-revert Leaves only runtime code on exit" + echo " --ignore-git-state Ignores git actual state" + exit 1 +} + +# parse CLI args +NO_REVERT= +IGNORE_GIT_STATE= +for i in "$@" +do + case $i in + --no-revert) + NO_REVERT=true + shift + ;; + --ignore-git-state) + IGNORE_GIT_STATE=true + shift + ;; + *) + show_help "Unknown option: $i" + ;; + esac +done + +# the script is able to work only on clean git copy, unless we want to ignore this check +[[ ! -z "${IGNORE_GIT_STATE}" ]] || [[ -z "$(git status --porcelain)" ]] || { echo >&2 "The git copy must be clean"; exit 1; } + +# let's avoid any restrictions on where this script can be called for - bridges repo may be +# plugged into any other repo folder. So the script (and other stuff that needs to be removed) +# may be located either in call dir, or one of it subdirs. +BRIDGES_FOLDER="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )/.." + +# let's leave repository/subtree in its original (clean) state if something fails below +function revert_to_clean_state { + [[ ! -z "${NO_REVERT}" ]] || { echo "Reverting to clean state..."; git checkout .; } +} +trap revert_to_clean_state EXIT + +# remove everything we think is not required for our needs +rm -rf $BRIDGES_FOLDER/.config +rm -rf $BRIDGES_FOLDER/.github +rm -rf $BRIDGES_FOLDER/.maintain +rm -rf $BRIDGES_FOLDER/bin/millau +rm -rf $BRIDGES_FOLDER/bin/rialto +rm -rf $BRIDGES_FOLDER/bin/rialto-parachain +rm -rf $BRIDGES_FOLDER/bin/.keep +rm -rf $BRIDGES_FOLDER/deployments +rm -f $BRIDGES_FOLDER/docs/dockerhub-* +rm -rf $BRIDGES_FOLDER/fuzz +rm -rf $BRIDGES_FOLDER/modules/beefy +rm -rf $BRIDGES_FOLDER/modules/shift-session-manager +rm -rf $BRIDGES_FOLDER/primitives/beefy +rm -rf $BRIDGES_FOLDER/primitives/chain-millau +rm -rf $BRIDGES_FOLDER/primitives/chain-rialto +rm -rf $BRIDGES_FOLDER/primitives/chain-rialto-parachain +rm -rf $BRIDGES_FOLDER/primitives/chain-westend +rm -rf $BRIDGES_FOLDER/relays +rm -rf $BRIDGES_FOLDER/scripts/add_license.sh +rm -rf $BRIDGES_FOLDER/scripts/build-containers.sh +rm -rf $BRIDGES_FOLDER/scripts/ci-cache.sh +rm -rf $BRIDGES_FOLDER/scripts/dump-logs.sh +rm -rf $BRIDGES_FOLDER/scripts/license_header +rm -rf $BRIDGES_FOLDER/scripts/send-message-from-millau-rialto.sh +rm -rf $BRIDGES_FOLDER/scripts/send-message-from-rialto-millau.sh +rm -rf $BRIDGES_FOLDER/scripts/update-weights.sh +rm -rf $BRIDGES_FOLDER/scripts/update-weights-setup.sh +rm -rf $BRIDGES_FOLDER/scripts/update_substrate.sh +rm -rf $BRIDGES_FOLDER/tools +rm -f $BRIDGES_FOLDER/.dockerignore +rm -f $BRIDGES_FOLDER/deny.toml +rm -f $BRIDGES_FOLDER/.gitlab-ci.yml +rm -f $BRIDGES_FOLDER/.editorconfig +rm -f $BRIDGES_FOLDER/Cargo.toml +rm -f $BRIDGES_FOLDER/ci.Dockerfile +rm -f $BRIDGES_FOLDER/Dockerfile + +# let's fix Cargo.toml a bit (it'll be helpful if we are in the bridges repo) +if [[ ! -f "Cargo.toml" ]]; then + cat > Cargo.toml <<-CARGO_TOML + [workspace] + resolver = "2" + + members = [ + "bin/runtime-common", + "modules/*", + "primitives/*", + ] + CARGO_TOML +fi + +# let's test if everything we need compiles + +cargo check -p pallet-bridge-grandpa +cargo check -p pallet-bridge-grandpa --features runtime-benchmarks +cargo check -p pallet-bridge-grandpa --features try-runtime +cargo check -p pallet-bridge-messages +cargo check -p pallet-bridge-messages --features runtime-benchmarks +cargo check -p pallet-bridge-messages --features try-runtime +cargo check -p pallet-bridge-parachains +cargo check -p pallet-bridge-parachains --features runtime-benchmarks +cargo check -p pallet-bridge-parachains --features try-runtime +cargo check -p pallet-bridge-relayers +cargo check -p pallet-bridge-relayers --features runtime-benchmarks +cargo check -p pallet-bridge-relayers --features try-runtime +cargo check -p bridge-runtime-common +cargo check -p bridge-runtime-common --features runtime-benchmarks + +# we're removing lock file after all chechs are done. Otherwise we may use different +# Substrate/Polkadot/Cumulus commits and our checks will fail +rm -f $BRIDGES_FOLDER/Cargo.lock + +echo "OK" diff --git a/tools/runtime-codegen/Cargo.lock b/tools/runtime-codegen/Cargo.lock new file mode 100644 index 00000000000..46093b70727 --- /dev/null +++ b/tools/runtime-codegen/Cargo.lock @@ -0,0 +1,4508 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli 0.26.2", +] + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.2", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.8", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "getrandom 0.2.8", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" + +[[package]] +name = "array-bytes" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "async-lock" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-trait" +version = "0.1.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ea188f25f0255d8f92797797c97ebf5631fa88178beb1a46fdf5622c9a00e4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.8", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line 0.19.0", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object 0.30.3", + "rustc-demangle", +] + +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "bounded-collections" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a071c348a5ef6da1d3a87166b408170b46002382b1dda83992b5c2208cefb370" +dependencies = [ + "log", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +dependencies = [ + "iana-time-zone", + "num-integer", + "num-traits", + "winapi", +] + +[[package]] +name = "clap" +version = "4.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c911b090850d79fc64fe9ea01e28e465f65e821e08813ced95bced72f7a8a9b" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a932373bab67b984c790ddf2c9ca295d8e3af3b7ef92de5a5bacdccdee4b09b" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.8", +] + +[[package]] +name = "clap_lex" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7379abaacee0f14abf3204a7606118f0465785252169d186337bcb75030815a" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9489fa336927df749631f1008007ced2871068544f40a202ce6d93fbf2366a7b" +dependencies = [ + "arrayvec 0.7.2", + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "cranelift-isle", + "gimli 0.26.2", + "hashbrown 0.12.3", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05bbb67da91ec721ed57cef2f7c5ef7728e1cd9bde9ffd3ef8601022e73e3239" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418ecb2f36032f6665dc1a5e2060a143dbab41d83b784882e97710e890a7a16d" + +[[package]] +name = "cranelift-entity" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cf583f7b093f291005f9fb1323e2c37f6ee4c7909e39ce016b2e8360d461705" +dependencies = [ + "serde", +] + +[[package]] +name = "cranelift-frontend" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b66bf9e916f57fbbd0f7703ec6286f4624866bf45000111627c70d272c8dda1" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "649782a39ce99798dd6b4029e2bb318a2fbeaade1b4fa25330763c10c65bc358" + +[[package]] +name = "cranelift-native" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "937e021e089c51f9749d09e7ad1c4f255c2f8686cb8c3df63a34b3ec9921bc41" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.93.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d850cf6775477747c9dfda9ae23355dd70512ffebc70cf82b85a5b111ae668b5" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools", + "log", + "smallvec", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.8.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.6", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.6", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array 0.14.6", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "cxx" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c00419335c41018365ddf7e4d5f1c12ee3659ddcf3e01974650ba1de73d038" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8307ad413a98fff033c8545ecf133e3257747b3bae935e7602aab8aa92d4ca" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.8", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc52e2eb08915cb12596d29d55f0b5384f00d697a646dbd269b6ecb0fbd9d31" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.8", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.6", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "dyn-clonable" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +dependencies = [ + "dyn-clonable-impl", + "dyn-clone", +] + +[[package]] +name = "dyn-clonable-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dyn-clone" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek 3.2.0", + "hashbrown 0.12.3", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "environmental" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "eyre" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "file-per-thread-logger" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "frame-metadata" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" +dependencies = [ + "cfg-if", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" + +[[package]] +name = "futures-executor" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" + +[[package]] +name = "futures-macro" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "futures-sink" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" + +[[package]] +name = "futures-task" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +dependencies = [ + "gloo-timers", + "send_wrapper", +] + +[[package]] +name = "futures-util" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + +[[package]] +name = "gloo-net" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "h2" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hash-db" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array 0.14.6", + "hmac 0.8.1", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "webpki-roots", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpsee" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" +dependencies = [ + "jsonrpsee-client-transport 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpsee-core 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpsee-http-client 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpsee-types 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "jsonrpsee" +version = "0.16.2" +source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" +dependencies = [ + "jsonrpsee-client-transport 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-http-client 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-wasm-client", + "jsonrpsee-ws-client", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965de52763f2004bc91ac5bcec504192440f0b568a5d621c59d9dbd6f886c3fb" +dependencies = [ + "futures-util", + "http", + "jsonrpsee-core 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpsee-types 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", + "webpki-roots", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.16.2" +source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" +dependencies = [ + "futures-channel", + "futures-util", + "gloo-net", + "http", + "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", + "webpki-roots", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" +dependencies = [ + "anyhow", + "async-lock", + "async-trait", + "beef", + "futures-channel", + "futures-timer", + "futures-util", + "hyper", + "jsonrpsee-types 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.16.2" +source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" +dependencies = [ + "anyhow", + "async-lock", + "async-trait", + "beef", + "futures-timer", + "futures-util", + "hyper", + "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "wasm-bindgen-futures", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls", + "jsonrpsee-core 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpsee-types 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.16.2" +source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls", + "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd522fe1ce3702fd94812965d7bb7a3364b1c9aba743944c5a00529aae80f8c" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.16.2" +source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "jsonrpsee-wasm-client" +version = "0.16.2" +source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" +dependencies = [ + "jsonrpsee-client-transport 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.16.2" +source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" +dependencies = [ + "http", + "jsonrpsee-client-transport 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "libm" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lru" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" +dependencies = [ + "hashbrown 0.12.3", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memfd" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" +dependencies = [ + "rustix", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memory-db" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" +dependencies = [ + "hash-db", + "hashbrown 0.12.3", +] + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "merlin" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.45.0", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec 0.7.2", + "itoa", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "crc32fast", + "hashbrown 0.12.3", + "indexmap", + "memchr", +] + +[[package]] +name = "object" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "os_str_bytes" +version = "6.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "parity-scale-codec" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" +dependencies = [ + "arrayvec 0.7.2", + "bitvec", + "byte-slice-cast", + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parity-wasm" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac 0.11.1", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-serde", + "scale-info", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.8", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.8", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "ref-cast" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.8", +] + +[[package]] +name = "regalloc2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" +dependencies = [ + "fxhash", + "log", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "region" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "runtime-codegen" +version = "0.1.0" +dependencies = [ + "clap", + "color-eyre", + "parity-scale-codec", + "proc-macro2", + "subxt-codegen", + "syn 1.0.109", + "wasm-loader", + "wasm-testbed", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustix" +version = "0.36.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64 0.21.0", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "sc-allocator" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "log", + "sp-core", + "sp-wasm-interface", + "thiserror", +] + +[[package]] +name = "sc-executor" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "lru", + "parity-scale-codec", + "parking_lot", + "sc-executor-common", + "sc-executor-wasmi", + "sc-executor-wasmtime", + "sp-api", + "sp-core", + "sp-externalities", + "sp-io", + "sp-panic-handler", + "sp-runtime-interface", + "sp-trie", + "sp-version", + "sp-wasm-interface", + "tracing", + "wasmi", +] + +[[package]] +name = "sc-executor-common" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "sc-allocator", + "sp-maybe-compressed-blob", + "sp-wasm-interface", + "thiserror", + "wasm-instrument", + "wasmi", +] + +[[package]] +name = "sc-executor-wasmi" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "log", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface", + "sp-wasm-interface", + "wasmi", +] + +[[package]] +name = "sc-executor-wasmtime" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "anyhow", + "cfg-if", + "libc", + "log", + "once_cell", + "rustix", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface", + "sp-wasm-interface", + "wasmtime", +] + +[[package]] +name = "scale-info" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61471dff9096de1d8b2319efed7162081e96793f5ebb147e50db10d50d648a4d" +dependencies = [ + "bitvec", + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "serde", +] + +[[package]] +name = "scale-info-derive" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219580e803a66b3f05761fd06f1f879a872444e49ce23f73694d26e5a954c7e6" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "schnellru" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +dependencies = [ + "ahash 0.8.3", + "cfg-if", + "hashbrown 0.13.2", +] + +[[package]] +name = "schnorrkel" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "curve25519-dalek 2.1.3", + "getrandom 0.1.16", + "merlin", + "rand 0.7.3", + "rand_core 0.5.1", + "sha2 0.8.2", + "subtle", + "zeroize", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "secp256k1" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +dependencies = [ + "cc", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + +[[package]] +name = "serde" +version = "1.0.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.8", +] + +[[package]] +name = "serde_json" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slice-group-by" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "soketto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +dependencies = [ + "base64 0.13.1", + "bytes", + "futures", + "httparse", + "log", + "rand 0.8.5", + "sha-1", +] + +[[package]] +name = "sp-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "sp-api-proc-macro", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", + "sp-trie", + "sp-version", + "thiserror", +] + +[[package]] +name = "sp-api-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "blake2", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sp-application-crypto" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-arithmetic" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-std 5.0.0", + "static_assertions", +] + +[[package]] +name = "sp-core" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "array-bytes", + "base58", + "bitflags", + "blake2", + "bounded-collections", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db", + "hash256-std-hasher", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log", + "merlin", + "parity-scale-codec", + "parking_lot", + "primitive-types", + "rand 0.8.5", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing 5.0.0", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std 5.0.0", + "sp-storage", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "zeroize", +] + +[[package]] +name = "sp-core-hashing" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "blake2", + "byteorder", + "digest 0.10.6", + "sha2 0.10.6", + "sha3", + "sp-std 5.0.0", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc2d1947252b7a4e403b0a260f596920443742791765ec111daa2bbf98eff25" +dependencies = [ + "blake2", + "byteorder", + "digest 0.10.6", + "sha2 0.10.6", + "sha3", + "sp-std 6.0.0", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "proc-macro2", + "quote", + "sp-core-hashing 5.0.0", + "syn 1.0.109", +] + +[[package]] +name = "sp-debug-derive" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sp-externalities" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std 5.0.0", + "sp-storage", +] + +[[package]] +name = "sp-io" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "bytes", + "ed25519", + "ed25519-dalek", + "futures", + "libsecp256k1", + "log", + "parity-scale-codec", + "secp256k1", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", + "sp-std 5.0.0", + "sp-tracing", + "sp-trie", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-keystore" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "async-trait", + "futures", + "merlin", + "parity-scale-codec", + "parking_lot", + "schnorrkel", + "sp-core", + "sp-externalities", + "thiserror", +] + +[[package]] +name = "sp-maybe-compressed-blob" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "thiserror", + "zstd", +] + +[[package]] +name = "sp-panic-handler" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + +[[package]] +name = "sp-runtime" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "paste", + "rand 0.8.5", + "scale-info", + "serde", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-std 5.0.0", + "sp-weights", +] + +[[package]] +name = "sp-runtime-interface" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std 5.0.0", + "sp-storage", + "sp-tracing", + "sp-wasm-interface", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sp-state-machine" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "parking_lot", + "rand 0.8.5", + "smallvec", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-std 5.0.0", + "sp-trie", + "thiserror", + "tracing", +] + +[[package]] +name = "sp-std" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" + +[[package]] +name = "sp-std" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af0ee286f98455272f64ac5bb1384ff21ac029fbb669afbaf48477faff12760e" + +[[package]] +name = "sp-storage" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-tracing" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "parity-scale-codec", + "sp-std 5.0.0", + "tracing", + "tracing-core", + "tracing-subscriber 0.2.25", +] + +[[package]] +name = "sp-trie" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "ahash 0.8.3", + "hash-db", + "hashbrown 0.12.3", + "lazy_static", + "memory-db", + "nohash-hasher", + "parity-scale-codec", + "parking_lot", + "scale-info", + "schnellru", + "sp-core", + "sp-std 5.0.0", + "thiserror", + "tracing", + "trie-db", + "trie-root", +] + +[[package]] +name = "sp-version" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "parity-wasm", + "scale-info", + "serde", + "sp-core-hashing-proc-macro", + "sp-runtime", + "sp-std 5.0.0", + "sp-version-proc-macro", + "thiserror", +] + +[[package]] +name = "sp-version-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "parity-scale-codec", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sp-wasm-interface" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "anyhow", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std 5.0.0", + "wasmi", + "wasmtime", +] + +[[package]] +name = "sp-weights" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic", + "sp-core", + "sp-debug-derive", + "sp-std 5.0.0", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "ss58-registry" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecf0bd63593ef78eca595a7fc25e9a443ca46fe69fd472f8f09f5245cdcd769d" +dependencies = [ + "Inflector", + "num-format", + "proc-macro2", + "quote", + "serde", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "substrate-bip39" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" +dependencies = [ + "hmac 0.11.0", + "pbkdf2 0.8.0", + "schnorrkel", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "substrate-runtime-proposal-hash" +version = "0.19.0" +source = "git+https://github.com/chevdor/subwasm?branch=master#fd047d76d52beab8c062eb94141f8d14ddc0f59d" +dependencies = [ + "blake2", + "hex", + "parity-scale-codec", + "sp-core", + "sp-io", + "sp-runtime", + "sp-wasm-interface", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "subxt-codegen" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e86cb719003f1cedf2710a6e55ca4c37aba4c989bbd3b81dd1c52af9e4827e" +dependencies = [ + "darling", + "frame-metadata", + "heck", + "hex", + "jsonrpsee 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec", + "proc-macro-error", + "proc-macro2", + "quote", + "scale-info", + "subxt-metadata", + "syn 1.0.109", + "tokio", +] + +[[package]] +name = "subxt-metadata" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2593ab5f53435e6352675af4f9851342607f37785d84c7a3fb3139550d3c35f0" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-core-hashing 6.0.0", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.8", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiny-bip39" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" +dependencies = [ + "anyhow", + "hmac 0.12.1", + "once_cell", + "pbkdf2 0.11.0", + "rand 0.8.5", + "rustc-hash", + "sha2 0.10.6", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.45.0", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" + +[[package]] +name = "toml_edit" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber 0.3.16", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "trie-db" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3390c0409daaa6027d6681393316f4ccd3ff82e1590a1e4725014e3ae2bf1920" +dependencies = [ + "hash-db", + "hashbrown 0.13.2", + "log", + "rustc-hex", + "smallvec", +] + +[[package]] +name = "trie-root" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891" +dependencies = [ + "hash-db", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "digest 0.10.6", + "rand 0.8.5", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "wasm-instrument" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa1dafb3e60065305741e83db35c6c2584bb3725b692b5b66148a38d72ace6cd" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasm-loader" +version = "0.19.0" +source = "git+https://github.com/chevdor/subwasm?branch=master#fd047d76d52beab8c062eb94141f8d14ddc0f59d" +dependencies = [ + "hex", + "jsonrpsee 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "log", + "serde", + "sp-maybe-compressed-blob", + "tokio", +] + +[[package]] +name = "wasm-testbed" +version = "0.19.0" +source = "git+https://github.com/chevdor/subwasm?branch=master#fd047d76d52beab8c062eb94141f8d14ddc0f59d" +dependencies = [ + "frame-metadata", + "hex", + "log", + "parity-scale-codec", + "sc-executor", + "sc-executor-common", + "sp-core", + "sp-io", + "sp-runtime", + "sp-state-machine", + "sp-version", + "sp-wasm-interface", + "substrate-runtime-proposal-hash", + "wasm-loader", +] + +[[package]] +name = "wasmi" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" +dependencies = [ + "parity-wasm", + "wasmi-validation", + "wasmi_core", +] + +[[package]] +name = "wasmi-validation" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasmi_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +dependencies = [ + "downcast-rs", + "libm", + "memory_units", + "num-rational", + "num-traits", + "region", +] + +[[package]] +name = "wasmparser" +version = "0.100.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" +dependencies = [ + "indexmap", + "url", +] + +[[package]] +name = "wasmtime" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e89f9819523447330ffd70367ef4a18d8c832e24e8150fe054d1d912841632" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "indexmap", + "libc", + "log", + "object 0.29.0", + "once_cell", + "paste", + "psm", + "rayon", + "serde", + "target-lexicon", + "wasmparser", + "wasmtime-cache", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd3a5e46c198032da934469f3a6e48649d1f9142438e4fd4617b68a35644b8a" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-cache" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b389ae9b678b9c3851091a4804f4182d688d27aff7abc9aa37fa7be37d8ecffa" +dependencies = [ + "anyhow", + "base64 0.13.1", + "bincode", + "directories-next", + "file-per-thread-logger", + "log", + "rustix", + "serde", + "sha2 0.10.6", + "toml", + "windows-sys 0.42.0", + "zstd", +] + +[[package]] +name = "wasmtime-cranelift" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b2c92a08c0db6efffd88fdc97d7aa9c7c63b03edb0971dbca745469f820e8c" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli 0.26.2", + "log", + "object 0.29.0", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a6db9fc52985ba06ca601f2ff0ff1f526c5d724c7ac267b47326304b0c97883" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli 0.26.2", + "indexmap", + "log", + "object 0.29.0", + "serde", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-jit" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77e3a52cd84d0f7f18554afa8060cfe564ccac61e3b0802d3fd4084772fa5f6" +dependencies = [ + "addr2line 0.17.0", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli 0.26.2", + "log", + "object 0.29.0", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-debug", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" +dependencies = [ + "object 0.29.0", + "once_cell", + "rustix", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67d412e9340ab1c83867051d8d1d7c90aa8c9afc91da086088068e2734e25064" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d594e791b5fdd4dbaf8cf7ae62f2e4ff85018ce90f483ca6f42947688e48827d" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap", + "libc", + "log", + "mach", + "memfd", + "memoffset 0.6.5", + "paste", + "rand 0.8.5", + "rustix", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-types" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6688d6f96d4dbc1f89fab626c56c1778936d122b5f4ae7a57c2eb42b8d982e2" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror", + "wasmparser", +] + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "winnow" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deac0939bd6e4f24ab5919fbf751c97a8cfc8543bb083a305ed5c0c10bb241d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.7+zstd.1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" +dependencies = [ + "cc", + "libc", + "pkg-config", +] diff --git a/tools/runtime-codegen/Cargo.toml b/tools/runtime-codegen/Cargo.toml new file mode 100644 index 00000000000..5eea4cdbc88 --- /dev/null +++ b/tools/runtime-codegen/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "runtime-codegen" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[workspace] + +[dependencies] +clap = { version = "4.0.8", features = ["derive", "cargo"] } +codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } +color-eyre = "0.6.1" +proc-macro2 = "1.0.51" +subxt-codegen = "0.27.1" +syn = "1.0" +wasm-loader = { git = "https://github.com/chevdor/subwasm", branch = "master" } +wasm-testbed = { git = "https://github.com/chevdor/subwasm", branch = "master" } \ No newline at end of file diff --git a/tools/runtime-codegen/README.md b/tools/runtime-codegen/README.md new file mode 100644 index 00000000000..155e90b369c --- /dev/null +++ b/tools/runtime-codegen/README.md @@ -0,0 +1,11 @@ +This is a tool for generating the bridge runtime code from metadata. + +Example commands: + +``` +cargo run --bin runtime-codegen -- --from-node-url "http://localhost:20433" > /tmp/rialto_codegen.rs +``` + +``` +cargo run --bin runtime-codegen -- --from-wasm ~/workplace/bridge-hub-rococo_runtime-v9360.compact.compressed.wasm > /tmp/rococo_bridge_hub_codegen.rs +``` \ No newline at end of file diff --git a/tools/runtime-codegen/src/main.rs b/tools/runtime-codegen/src/main.rs new file mode 100644 index 00000000000..e1aa142e558 --- /dev/null +++ b/tools/runtime-codegen/src/main.rs @@ -0,0 +1,173 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +use clap::Parser as ClapParser; +use codec::Encode; +use color_eyre::eyre; +use std::{env, path::PathBuf}; +use subxt_codegen::{ + generate_runtime_api_from_bytes, generate_runtime_api_from_url, utils::Uri, CratePath, + DerivesRegistry, TypeSubstitutes, +}; +use wasm_testbed::WasmTestBed; + +/// Command for generating indirect runtimes code. +#[derive(Debug, ClapParser)] +struct Command { + #[clap(name = "from-node-url", long, value_parser)] + node_url: Option, + #[clap(name = "from-wasm-file", long, value_parser)] + wasm_file: Option, +} + +enum RuntimeMetadataSource { + NodeUrl(Uri), + WasmFile(wasm_loader::Source), +} + +impl RuntimeMetadataSource { + fn from_command(cmd: Command) -> color_eyre::Result { + match (cmd.node_url, cmd.wasm_file) { + (Some(_), Some(_)) => Err(eyre::eyre!( + "Please specify one of `--from-node-url` or `--from-wasm-file` but not both" + )), + (None, None) => + Err(eyre::eyre!("Please specify one of `--from-node-url` or `--from-wasm-file`")), + (Some(node_url), None) => Ok(Self::NodeUrl(node_url)), + (None, Some(source)) => + Ok(Self::WasmFile(wasm_loader::Source::File(PathBuf::from(source)))), + } + } +} + +struct TypeSubstitute { + subxt_type: syn::Path, + substitute: syn::Path, +} + +impl TypeSubstitute { + fn simple(subxt_type: &str) -> Self { + Self { + subxt_type: syn::parse_str::(subxt_type).unwrap(), + substitute: syn::parse_str::(&format!("::{subxt_type}")).unwrap(), + } + } + + fn custom(subxt_type: &str, substitute: &str) -> Self { + Self { + subxt_type: syn::parse_str::(subxt_type).unwrap(), + substitute: syn::parse_str::(substitute).unwrap(), + } + } +} + +fn print_runtime(runtime_api: proc_macro2::TokenStream) { + println!( + "// Copyright 2019-2023 Parity Technologies (UK) Ltd. + // This file is part of Parity Bridges Common. + + // Parity Bridges Common 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. + + // Parity Bridges Common 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 Parity Bridges Common. If not, see . + + //! Autogenerated runtime API + //! THIS FILE WAS AUTOGENERATED USING parity-bridges-common::runtime-codegen + //! EXECUTED COMMAND: {} + + {} + ", + env::args().collect::>().join(" "), + runtime_api + ); +} + +fn main() -> color_eyre::Result<()> { + let args: Command = Command::parse(); + let metadata_source = RuntimeMetadataSource::from_command(args)?; + + // Module under which the API is generated. + let item_mod = syn::parse_quote!( + pub mod api {} + ); + // Default module derivatives. + let mut derives = DerivesRegistry::new(&CratePath::default()); + derives.extend_for_all(vec![syn::parse_quote!(Clone)]); + // Type substitutes + let mut type_substitutes = TypeSubstitutes::new(&CratePath::default()); + type_substitutes.extend( + vec![ + TypeSubstitute::simple("sp_core::crypto::AccountId32"), + TypeSubstitute::custom("bp_millau::millau_hash::MillauHash", "::bp_millau::MillauHash"), + TypeSubstitute::simple("bp_millau::BlakeTwoAndKeccak256"), + TypeSubstitute::custom( + "sp_runtime::generic::digest::Digest", + "::sp_runtime::generic::Digest", + ), + TypeSubstitute::custom("sp_runtime::generic::era::Era", "::sp_runtime::generic::Era"), + TypeSubstitute::custom( + "sp_runtime::generic::header::Header", + "::sp_runtime::generic::Header", + ), + TypeSubstitute::simple("bp_header_chain::justification::GrandpaJustification"), + TypeSubstitute::simple("bp_header_chain::InitializationData"), + TypeSubstitute::simple( + "bridge_runtime_common::messages::target::FromBridgedChainMessagesProof", + ), + TypeSubstitute::custom("sp_weights::weight_v2::Weight", "::sp_weights::Weight"), + TypeSubstitute::simple( + "bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof", + ), + TypeSubstitute::simple("bp_messages::UnrewardedRelayersState"), + ] + .drain(..) + .map(|substitute| (substitute.subxt_type, substitute.substitute.try_into().unwrap())), + ); + + // Generate the Runtime API. + let runtime_api = match metadata_source { + RuntimeMetadataSource::NodeUrl(node_url) => generate_runtime_api_from_url( + item_mod, + &node_url, + derives, + type_substitutes, + CratePath::default(), + ), + RuntimeMetadataSource::WasmFile(source) => { + let testbed = WasmTestBed::new(&source) + .map_err(|e| eyre::eyre!("Error creating WasmTestBed: {:?}", e))?; + generate_runtime_api_from_bytes( + item_mod, + &testbed.runtime_metadata_prefixed().encode(), + derives, + type_substitutes, + CratePath::default(), + ) + }, + }; + + print_runtime(runtime_api); + + Ok(()) +} From 1e489dddce1f36824bfe00157f9972f7e7ad22ee Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 17 Apr 2023 14:18:00 +0200 Subject: [PATCH 241/263] Updated REAMDE.md and BRIDGES.md (inspired by original https://github.com/paritytech/polkadot/blob/d22eb62fe40e55e15eb91d375f48cc540d83a47e/BRIDGES.md) --- BRIDGES.md | 88 +++++++++++++++++++++++ parachains/runtimes/bridge-hubs/README.md | 38 ---------- 2 files changed, 88 insertions(+), 38 deletions(-) create mode 100644 BRIDGES.md diff --git a/BRIDGES.md b/BRIDGES.md new file mode 100644 index 00000000000..a9fff624cdc --- /dev/null +++ b/BRIDGES.md @@ -0,0 +1,88 @@ +# Using Parity Bridges Common dependency (`git subtree`). + +In `./bridges` sub-directory you can find a `git subtree` imported version of: +[parity-bridges-common](https://github.com/paritytech/parity-bridges-common/) repository. + +(For regular Cumulus contributor 1. is relevant) \ +(For Cumulus maintainer 1. and 2. are relevant) \ +(For Bridges team 1. and 2. and 3. are relevant) + +# 1. How to fix broken Bridges code? + +To fix Bridges code simply create a commit in current (`Cumulus`) repo. Best if +the commit is isolated to changes in `./bridges` sub-directory, because it makes +it easier to import that change back to upstream repo. + +(Any changes to `bridges` subtree require Bridges team approve and they should manage backport to Bridges repo) + + +# 2. How to pull latest Bridges code to the `bridges` subtree +(in practice) +``` +cd + +# this will update new git branches from bridges repo +# there could be unresolved conflicts, but dont worry, +# lots of them are caused because of removed unneeded files with patch step, +./scripts/bridges_update_subtree.sh fetch + +# so, after fetch and before solving conflicts just run patch, +# this will remove unneeded files and checks if subtree modules compiles +./scripts/bridges_update_subtree.sh patch + +# if there are conflicts, this could help, +# this removes locally deleted files at least (move changes to git stash for commit) +./scripts/bridges_update_subtree.sh merge + +# (optional) when conflicts resolved, you can check build again - should pass +# also important: this updates global Cargo.lock +./scripts/bridges_update_subtree.sh patch + +# add changes to the commit, first command `fetch` starts merge, +# so after all conflicts are solved and patch passes and compiles, +# then we need to finish merge with: +git merge --continue +```` + +# 3. How to pull latest Bridges code or contribute back? +(in theory) + +Note that it's totally fine to ping the **Bridges Team** to do that for you. The point +of adding the code as `git subtree` is to **reduce maintenance cost** for Cumulus/Polkadot +developers. + +If you still would like to either update the code to match latest code from the repo +or create an upstream PR read below. The following commands should be run in the +current (`polkadot`) repo. + +1. Add Bridges repo as a local remote: +``` +$ git remote add -f bridges git@github.com:paritytech/parity-bridges-common.git +``` + +If you plan to contribute back, consider forking the repository on Github and adding +your personal fork as a remote as well. +``` +$ git remote add -f my-bridges git@github.com:tomusdrw/parity-bridges-common.git +``` + +2. To update Bridges: +``` +$ git fetch bridges master +$ git subtree pull --prefix=bridges bridges master --squash +```` + +We use `--squash` to avoid adding individual commits and rather squashing them +all into one. + +3. Clean unneeded files here: +``` +./bridges/scripts/verify-pallets-build.sh --ignore-git-state --no-revert +``` + +4. Contributing back to Bridges (creating upstream PR) +``` +$ git subtree push --prefix=bridges my-bridges master +``` +This command will push changes to your personal fork of Bridges repo, from where +you can simply create a PR to the main repo. diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 7a6cb35aaf7..79e82f3497a 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -35,41 +35,3 @@ or # BridgeHubPolkadot zombienet-linux --provider native spawn ./zombienet/examples/bridge_hub_polkadot_local_network.toml ``` - ----- -## Git subtree `./bridges` - -Add Bridges repo as a local remote and synchronize it with latest `master` from bridges repo: - -### How to update `bridges` subtree -``` -cd -# this will update new git branches from bridges repo -# there could be unresolved conflicts, but dont worry, -# lots of them are caused because of removed unneeded files with patch step :) -# so before solving conflicts just run patch -./scripts/bridges_update_subtree.sh fetch -# this will remove unneeded files and checks if subtree modules compiles -./scripts/bridges_update_subtree.sh patch -# if there are conflicts, this could help, removes locally deleted files at least -# (but you can also do this manually) -./scripts/bridges_update_subtree.sh merge -# when conflicts resolved, you can check build again - should pass -# also important: this updates global Cargo.lock -./scripts/bridges_update_subtree.sh patch -```` -We use `--squash` to avoid adding individual commits and rather squashing them -all into one. -Now we use `master` branch, but in future, it could change to some release branch/tag. - -### How was first time initialized (does not need anymore) -``` -cd -git remote add -f bridges git@github.com:paritytech/parity-bridges-common.git -# (ran just only first time, when subtree was initialized) -git subtree add --prefix=bridges bridges master --squash -# remove unnecessery files -./scripts/bridges_update_subtree.sh patch -./scripts/bridges_update_subtree.sh merge -git commit --amend -``` From 0670d9fa5ae67f6f01028440318a723d1b330720 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 17 Apr 2023 14:19:03 +0200 Subject: [PATCH 242/263] Squashed 'bridges/' changes from d30927c08..d3970944b d3970944b Small simplifications (#2050) git-subtree-dir: bridges git-subtree-split: d3970944b0cfc4ea5226225e1ca07dab234c3556 --- modules/messages/src/inbound_lane.rs | 16 +++++++--------- primitives/messages/src/lib.rs | 12 +++++------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/modules/messages/src/inbound_lane.rs b/modules/messages/src/inbound_lane.rs index 3f64ab765b5..59ff5667195 100644 --- a/modules/messages/src/inbound_lane.rs +++ b/modules/messages/src/inbound_lane.rs @@ -198,19 +198,17 @@ impl InboundLane { ); // now let's update inbound lane storage - let push_new = match data.relayers.back_mut() { + match data.relayers.back_mut() { Some(entry) if entry.relayer == *relayer_at_bridged_chain => { entry.messages.note_dispatched_message(); - false }, - _ => true, + _ => { + data.relayers.push_back(UnrewardedRelayer { + relayer: relayer_at_bridged_chain.clone(), + messages: DeliveredMessages::new(nonce), + }); + }, }; - if push_new { - data.relayers.push_back(UnrewardedRelayer { - relayer: (*relayer_at_bridged_chain).clone(), - messages: DeliveredMessages::new(nonce), - }); - } self.storage.set_data(data); ReceivalResult::Dispatched(dispatch_result) diff --git a/primitives/messages/src/lib.rs b/primitives/messages/src/lib.rs index 3910837a442..2828d5af006 100644 --- a/primitives/messages/src/lib.rs +++ b/primitives/messages/src/lib.rs @@ -164,11 +164,9 @@ impl InboundLaneData { where RelayerId: MaxEncodedLen, { - let message_nonce_size = MessageNonce::max_encoded_len(); - let relayer_id_encoded_size = RelayerId::max_encoded_len(); - let relayers_entry_size = relayer_id_encoded_size.checked_add(2 * message_nonce_size)?; - let relayers_size = relayers_entries.checked_mul(relayers_entry_size)?; - relayers_size.checked_add(message_nonce_size) + relayers_entries + .checked_mul(UnrewardedRelayer::::max_encoded_len())? + .checked_add(MessageNonce::max_encoded_len()) } /// Returns the approximate size of the struct as u32, given a number of entries in the @@ -223,7 +221,7 @@ pub struct InboundMessageDetails { /// /// This struct represents a continuous range of messages that have been delivered by the same /// relayer and whose confirmations are still pending. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] pub struct UnrewardedRelayer { /// Identifier of the relayer. pub relayer: RelayerId, @@ -270,7 +268,7 @@ pub enum ReceivalResult { } /// Delivered messages with their dispatch result. -#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] pub struct DeliveredMessages { /// Nonce of the first message that has been delivered (inclusive). pub begin: MessageNonce, From f2277b6f81208bd38a15cb74ebd535c4cd851b32 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 18 Apr 2023 11:26:17 +0200 Subject: [PATCH 243/263] Squashed 'bridges/' changes from d3970944b..2180797fb 2180797fb Removed CODEOWNERS (#2051) git-subtree-dir: bridges git-subtree-split: 2180797fbf8a990490c67853dcffd81bc8dd083c --- scripts/verify-pallets-build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/verify-pallets-build.sh b/scripts/verify-pallets-build.sh index ad69c5df008..4b6fadb79e4 100755 --- a/scripts/verify-pallets-build.sh +++ b/scripts/verify-pallets-build.sh @@ -93,6 +93,7 @@ rm -f $BRIDGES_FOLDER/.gitlab-ci.yml rm -f $BRIDGES_FOLDER/.editorconfig rm -f $BRIDGES_FOLDER/Cargo.toml rm -f $BRIDGES_FOLDER/ci.Dockerfile +rm -f $BRIDGES_FOLDER/CODEOWNERS rm -f $BRIDGES_FOLDER/Dockerfile # let's fix Cargo.toml a bit (it'll be helpful if we are in the bridges repo) From 20647a649d29f4b7bc4e5f280c39953d9e53f3b2 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 13 Apr 2023 15:13:08 +0200 Subject: [PATCH 244/263] Reused `teleports_for_native_asset_works` test to all bridge-hub runtime Extract runtime_para_id for test Fix test Typos Added helper for `execute_as_governance` --- Cargo.lock | 4 ++ .../runtimes/assets/statemine/tests/tests.rs | 3 +- .../runtimes/assets/statemint/tests/tests.rs | 3 +- .../runtimes/assets/test-utils/Cargo.toml | 2 + .../runtimes/assets/test-utils/src/lib.rs | 27 ++++++++- .../assets/test-utils/src/test_cases.rs | 24 ++++---- .../runtimes/assets/westmint/tests/tests.rs | 3 +- .../bridge-hubs/bridge-hub-kusama/Cargo.toml | 3 + .../bridge-hub-kusama/tests/tests.rs | 56 +++++++++++++++++++ .../bridge-hub-polkadot/Cargo.toml | 3 + .../bridge-hub-polkadot/tests/tests.rs | 56 +++++++++++++++++++ .../bridge-hub-rococo/tests/tests.rs | 43 ++++++++++++-- .../bridge-hubs/test-utils/Cargo.toml | 2 + .../bridge-hubs/test-utils/src/lib.rs | 4 +- .../bridge-hubs/test-utils/src/test_cases.rs | 20 +++++++ 15 files changed, 233 insertions(+), 20 deletions(-) create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-kusama/tests/tests.rs create mode 100644 parachains/runtimes/bridge-hubs/bridge-hub-polkadot/tests/tests.rs create mode 100644 parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs diff --git a/Cargo.lock b/Cargo.lock index c2ecaf800cd..eadcb444054 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -341,6 +341,7 @@ name = "asset-test-utils" version = "1.0.0" dependencies = [ "assets-common", + "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-xcmp-queue", "cumulus-primitives-core", @@ -887,6 +888,7 @@ dependencies = [ name = "bridge-hub-kusama-runtime" version = "0.1.0" dependencies = [ + "bridge-hub-test-utils", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -950,6 +952,7 @@ dependencies = [ name = "bridge-hub-polkadot-runtime" version = "0.1.0" dependencies = [ + "bridge-hub-test-utils", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -1091,6 +1094,7 @@ dependencies = [ name = "bridge-hub-test-utils" version = "0.1.0" dependencies = [ + "asset-test-utils", "bp-header-chain", "bp-messages", "bp-polkadot-core", diff --git a/parachains/runtimes/assets/statemine/tests/tests.rs b/parachains/runtimes/assets/statemine/tests/tests.rs index acfb2417e4b..b9001a35a99 100644 --- a/parachains/runtimes/assets/statemine/tests/tests.rs +++ b/parachains/runtimes/assets/statemine/tests/tests.rs @@ -492,7 +492,8 @@ asset_test_utils::include_teleports_for_native_asset_works!( Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), _ => None, } - }) + }), + 1000 ); asset_test_utils::include_teleports_for_foreign_assets_works!( diff --git a/parachains/runtimes/assets/statemint/tests/tests.rs b/parachains/runtimes/assets/statemint/tests/tests.rs index 75f6aaf6d57..7bbed6bb54a 100644 --- a/parachains/runtimes/assets/statemint/tests/tests.rs +++ b/parachains/runtimes/assets/statemint/tests/tests.rs @@ -470,7 +470,8 @@ asset_test_utils::include_teleports_for_native_asset_works!( Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), _ => None, } - }) + }), + 1000 ); asset_test_utils::include_asset_transactor_transfer_with_local_consensus_currency_works!( diff --git a/parachains/runtimes/assets/test-utils/Cargo.toml b/parachains/runtimes/assets/test-utils/Cargo.toml index 2a9e70ce2dd..8a1ce0e6e3c 100644 --- a/parachains/runtimes/assets/test-utils/Cargo.toml +++ b/parachains/runtimes/assets/test-utils/Cargo.toml @@ -23,6 +23,7 @@ sp-core = { git = "https://github.com/paritytech/substrate", default-features = # Cumulus cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false } cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } +cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachains-common = { path = "../../../common", default-features = false } assets-common = { path = "../common", default-features = false } @@ -69,4 +70,5 @@ std = [ "xcm-executor/std", "pallet-xcm/std", "cumulus-pallet-xcmp-queue/std", + "cumulus-pallet-dmp-queue/std", ] diff --git a/parachains/runtimes/assets/test-utils/src/lib.rs b/parachains/runtimes/assets/test-utils/src/lib.rs index 06d6282e008..1e0b31f18a3 100644 --- a/parachains/runtimes/assets/test-utils/src/lib.rs +++ b/parachains/runtimes/assets/test-utils/src/lib.rs @@ -16,7 +16,7 @@ use sp_core::Encode; use sp_runtime::{Digest, DigestItem}; use xcm::{ latest::{MultiAsset, MultiLocation, XcmContext, XcmHash}, - prelude::{Concrete, Fungible, Outcome, XcmError, XcmVersion}, + prelude::*, }; use xcm_executor::{traits::TransactAsset, Assets}; @@ -252,6 +252,31 @@ impl Runt } } +impl + RuntimeHelper +{ + pub fn execute_as_governance(call: Vec, require_weight_at_most: Weight) -> Outcome { + // prepare xcm as governance will do + let xcm = Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + Transact { + origin_kind: OriginKind::Superuser, + require_weight_at_most, + call: call.into(), + }, + ]); + + // execute xcm as parent origin + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + <::XcmExecutor>::execute_xcm( + MultiLocation::parent(), + xcm, + hash, + Self::xcm_max_weight(XcmReceivedFrom::Parent), + ) + } +} + pub enum XcmReceivedFrom { Parent, Sibling, diff --git a/parachains/runtimes/assets/test-utils/src/test_cases.rs b/parachains/runtimes/assets/test-utils/src/test_cases.rs index 079e6bd60ae..954ff0d7589 100644 --- a/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -78,6 +78,7 @@ pub fn teleports_for_native_asset_works< unwrap_xcmp_queue_event: Box< dyn Fn(Vec) -> Option>, >, + runtime_para_id: u32, ) where Runtime: frame_system::Config + pallet_balances::Config @@ -102,7 +103,6 @@ pub fn teleports_for_native_asset_works< Call = cumulus_pallet_parachain_system::Call, >, { - let runtime_para_id = 1000; ExtBuilder::::default() .with_collators(collator_session_keys.collators()) .with_session_keys(collator_session_keys.session_keys()) @@ -273,14 +273,15 @@ macro_rules! include_teleports_for_native_asset_works( $collator_session_key:expr, $existential_deposit:expr, $unwrap_pallet_xcm_event:expr, - $unwrap_xcmp_queue_event:expr + $unwrap_xcmp_queue_event:expr, + $runtime_para_id:expr ) => { #[test] fn teleports_for_native_asset_works() { const BOB: [u8; 32] = [2u8; 32]; let target_account = parachains_common::AccountId::from(BOB); - asset_test_utils::test_cases::teleports_for_native_asset_works::< + $crate::test_cases::teleports_for_native_asset_works::< $runtime, $xcm_config, $checking_account, @@ -291,7 +292,8 @@ macro_rules! include_teleports_for_native_asset_works( $existential_deposit, target_account, $unwrap_pallet_xcm_event, - $unwrap_xcmp_queue_event + $unwrap_xcmp_queue_event, + $runtime_para_id ) } } @@ -598,7 +600,7 @@ macro_rules! include_teleports_for_foreign_assets_works( const SOME_ASSET_OWNER: [u8; 32] = [5u8; 32]; let asset_owner = parachains_common::AccountId::from(SOME_ASSET_OWNER); - asset_test_utils::test_cases::teleports_for_foreign_assets_works::< + $crate::test_cases::teleports_for_foreign_assets_works::< $runtime, $xcm_config, $checking_account, @@ -715,7 +717,7 @@ macro_rules! include_asset_transactor_transfer_with_local_consensus_currency_wor const BOB: [u8; 32] = [2u8; 32]; let target_account = parachains_common::AccountId::from(BOB); - asset_test_utils::test_cases::asset_transactor_transfer_with_local_consensus_currency_works::< + $crate::test_cases::asset_transactor_transfer_with_local_consensus_currency_works::< $runtime, $xcm_config >( @@ -969,7 +971,7 @@ macro_rules! include_asset_transactor_transfer_with_pallet_assets_instance_works const CHARLIE: [u8; 32] = [3u8; 32]; let charlie_account = parachains_common::AccountId::from(CHARLIE); - asset_test_utils::test_cases::asset_transactor_transfer_with_pallet_assets_instance_works::< + $crate::test_cases::asset_transactor_transfer_with_pallet_assets_instance_works::< $runtime, $xcm_config, $assets_pallet_instance, @@ -1094,7 +1096,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor additional_checks_before(); // execute XCM with Transacts to create/manage foreign assets by foreign governance - // prepapre data for xcm::Transact(create) + // prepare data for xcm::Transact(create) let foreign_asset_create = runtime_call_encode(pallet_assets::Call::< Runtime, ForeignAssetsPalletInstance, @@ -1104,7 +1106,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor admin: foreign_creator_as_account_id.clone().into(), min_balance: 1.into(), }); - // prepapre data for xcm::Transact(set_metadata) + // prepare data for xcm::Transact(set_metadata) let foreign_asset_set_metadata = runtime_call_encode(pallet_assets::Call::< Runtime, ForeignAssetsPalletInstance, @@ -1114,7 +1116,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor symbol: Vec::from(ASSET_SYMBOL), decimals: 12, }); - // prepapre data for xcm::Transact(set_team - change just freezer to Bob) + // prepare data for xcm::Transact(set_team - change just freezer to Bob) let foreign_asset_set_team = runtime_call_encode(pallet_assets::Call::< Runtime, ForeignAssetsPalletInstance, @@ -1297,7 +1299,7 @@ macro_rules! include_create_and_manage_foreign_assets_for_local_consensus_parach const BOB: [u8; 32] = [2u8; 32]; let bob_account = parachains_common::AccountId::from(BOB); - asset_test_utils::test_cases::create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works::< + $crate::test_cases::create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works::< $runtime, $xcm_config, $weight_to_fee, diff --git a/parachains/runtimes/assets/westmint/tests/tests.rs b/parachains/runtimes/assets/westmint/tests/tests.rs index c0c20c6b61a..3ef09d14e52 100644 --- a/parachains/runtimes/assets/westmint/tests/tests.rs +++ b/parachains/runtimes/assets/westmint/tests/tests.rs @@ -497,7 +497,8 @@ asset_test_utils::include_teleports_for_native_asset_works!( Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), _ => None, } - }) + }), + 1000 ); asset_test_utils::include_teleports_for_foreign_assets_works!( diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/Cargo.toml index 00a9da0103c..02722e73a83 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/Cargo.toml @@ -72,6 +72,9 @@ pallet-collator-selection = { path = "../../../../pallets/collator-selection", d parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false } parachains-common = { path = "../../../../parachains/common", default-features = false } +[dev-dependencies] +bridge-hub-test-utils = { path = "../test-utils"} + [features] default = [ "std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/tests/tests.rs new file mode 100644 index 00000000000..9998e3d804d --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/tests/tests.rs @@ -0,0 +1,56 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +pub use bridge_hub_kusama_runtime::{ + constants::fee::WeightToFee, xcm_config::XcmConfig, Balances, ExistentialDeposit, + ParachainSystem, PolkadotXcm, Runtime, RuntimeEvent, SessionKeys, +}; +use codec::Decode; +use frame_support::parameter_types; +use parachains_common::{AccountId, AuraId}; + +const ALICE: [u8; 32] = [1u8; 32]; + +parameter_types! { + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); +} + +bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( + Runtime, + XcmConfig, + CheckingAccount, + WeightToFee, + ParachainSystem, + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + 1002 +); diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/Cargo.toml b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/Cargo.toml index 86bb0780027..28d2cb50a10 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/Cargo.toml @@ -72,6 +72,9 @@ pallet-collator-selection = { path = "../../../../pallets/collator-selection", d parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false } parachains-common = { path = "../../../../parachains/common", default-features = false } +[dev-dependencies] +bridge-hub-test-utils = { path = "../test-utils"} + [features] default = [ "std", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/tests/tests.rs new file mode 100644 index 00000000000..9a3ccd59cd6 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/tests/tests.rs @@ -0,0 +1,56 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +pub use bridge_hub_polkadot_runtime::{ + constants::fee::WeightToFee, xcm_config::XcmConfig, Balances, ExistentialDeposit, + ParachainSystem, PolkadotXcm, Runtime, RuntimeEvent, SessionKeys, +}; +use codec::Decode; +use frame_support::parameter_types; +use parachains_common::{AccountId, AuraId}; + +const ALICE: [u8; 32] = [1u8; 32]; + +parameter_types! { + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); +} + +bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( + Runtime, + XcmConfig, + CheckingAccount, + WeightToFee, + ParachainSystem, + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + 1002 +); diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index 84cdb3ecb4c..e644cf13f44 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2022 Parity Technologies (UK) Ltd. +// Copyright 2023 Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -17,15 +17,17 @@ use bp_messages::target_chain::MessageDispatch; use bp_runtime::messages::MessageDispatchResult; pub use bridge_hub_rococo_runtime::{ + constants::fee::WeightToFee, xcm_config::{XcmConfig, XcmRouter}, - Runtime, *, + Balances, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeEvent, SessionKeys, }; -use codec::Encode; +use codec::{Decode, Encode}; use xcm::latest::prelude::*; use bridge_hub_test_utils::*; use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult; -use frame_support::weights::Weight; +use frame_support::{parameter_types, weights::Weight}; +use parachains_common::{AccountId, AuraId}; use xcm_builder::DispatchBlobError; use xcm_executor::XcmExecutor; @@ -268,3 +270,36 @@ fn can_govornance_call_xcm_transact_with_initialize_bridge_on_bridge_hub_wococo( } // TODO:check-parameter - add test for DeliveryConfirmationPayments when receive_messages_delivery_proof + +const ALICE: [u8; 32] = [1u8; 32]; + +parameter_types! { + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); +} + +bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( + Runtime, + XcmConfig, + CheckingAccount, + WeightToFee, + ParachainSystem, + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + 1013 +); diff --git a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml index 123bbfb501a..a606b02a04c 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -19,6 +19,7 @@ cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system cumulus-primitives-parachain-inherent = { path = "../../../../primitives/parachain-inherent", default-features = false } cumulus-test-relay-sproof-builder = { path = "../../../../test/relay-sproof-builder", default-features = false } parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false } +asset-test-utils = { path = "../../assets/test-utils"} # Polkadot polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } @@ -57,4 +58,5 @@ std = [ "xcm/std", "xcm-builder/std", "xcm-executor/std", + "asset-test-utils/std", ] diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs index 0605677c4df..cfb9a4f81d2 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2022 Parity Technologies (UK) Ltd. +// Copyright 2023 Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -34,6 +34,8 @@ use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; use xcm_executor::traits::{validate_export, ExportXcm}; pub use bp_test_utils::test_header; +pub mod test_cases; +pub use test_cases::CollatorSessionKeys; /// Dummy xcm pub fn dummy_xcm() -> Xcm<()> { diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs new file mode 100644 index 00000000000..cdbeb65fab1 --- /dev/null +++ b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs @@ -0,0 +1,20 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities. + +// Re-export test_cases from assets +pub use asset_test_utils::{include_teleports_for_native_asset_works, CollatorSessionKeys}; From c7e5556145325b154eb635ee300957c3f66cc5a5 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 20 Apr 2023 15:22:15 +0200 Subject: [PATCH 245/263] Added test case `initialize_bridge_by_governance_works` --- Cargo.lock | 5 + .../bridge-hub-rococo/tests/tests.rs | 159 ++++++++---------- .../bridge-hubs/test-utils/Cargo.toml | 9 + .../bridge-hubs/test-utils/src/lib.rs | 30 +--- .../bridge-hubs/test-utils/src/test_cases.rs | 122 ++++++++++++++ 5 files changed, 208 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eadcb444054..5a7c21f1a31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1100,12 +1100,17 @@ dependencies = [ "bp-polkadot-core", "bp-runtime", "bp-test-utils", + "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-test-relay-sproof-builder", "frame-support", "frame-system", + "pallet-balances", + "pallet-bridge-grandpa", + "pallet-collator-selection", + "pallet-session", "pallet-xcm", "pallet-xcm-benchmarks", "parachain-info", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index e644cf13f44..814523cffd6 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -19,17 +19,25 @@ use bp_runtime::messages::MessageDispatchResult; pub use bridge_hub_rococo_runtime::{ constants::fee::WeightToFee, xcm_config::{XcmConfig, XcmRouter}, - Balances, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeEvent, SessionKeys, + Balances, BridgeGrandpaRococoInstance, BridgeGrandpaWococoInstance, ExistentialDeposit, + ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, }; use codec::{Decode, Encode}; use xcm::latest::prelude::*; -use bridge_hub_test_utils::*; +use bridge_hub_rococo_runtime::{ + bridge_hub_rococo_config, bridge_hub_wococo_config, WithBridgeHubRococoMessagesInstance, + WithBridgeHubWococoMessagesInstance, +}; + +use bridge_hub_test_utils::{ + dummy_account, dummy_xcm, mock_open_hrmp_channel, new_test_ext, simulate_export_message, + wrap_as_dispatch_message, +}; use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult; -use frame_support::{parameter_types, weights::Weight}; +use frame_support::parameter_types; use parachains_common::{AccountId, AuraId}; use xcm_builder::DispatchBlobError; -use xcm_executor::XcmExecutor; fn execute_on_runtime( with_para_id: u32, @@ -185,90 +193,6 @@ fn dispatch_blob_and_xcm_routing_works_on_bridge_hub_rococo() { } } -#[test] -fn can_govornance_call_xcm_transact_with_initialize_on_bridge_hub_rococo() { - // prepare xcm as govornance will do - let initialize_call: RuntimeCall = - RuntimeCall::BridgeRococoGrandpa(pallet_bridge_grandpa::Call::< - Runtime, - BridgeGrandpaRococoInstance, - >::initialize { - init_data: mock_initialiation_data(), - }); - let xcm = Xcm(vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Superuser, - require_weight_at_most: Weight::from_parts(1000000000, 0), - call: initialize_call.encode().into(), - }, - ]); - // origin as relay chain - let origin = MultiLocation { parents: 1, interior: Here }; - - execute_on_runtime(bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, None, || { - // check mode before - assert_eq!( - pallet_bridge_grandpa::PalletOperatingMode::::try_get(), - Err(()) - ); - - // initialize bridge through governance-like - let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - let weight_limit = Weight::from_parts(41666666666, 0); - let outcome = XcmExecutor::::execute_xcm(origin, xcm, hash, weight_limit); - - // check mode after - assert_eq!(outcome.ensure_complete(), Ok(())); - assert_eq!( - pallet_bridge_grandpa::PalletOperatingMode::::try_get(), - Ok(bp_runtime::BasicOperatingMode::Normal) - ); - }) -} - -#[test] -fn can_govornance_call_xcm_transact_with_initialize_bridge_on_bridge_hub_wococo() { - // prepare xcm as govornance will do - let initialize_call: RuntimeCall = - RuntimeCall::BridgeWococoGrandpa(pallet_bridge_grandpa::Call::< - Runtime, - BridgeGrandpaWococoInstance, - >::initialize { - init_data: mock_initialiation_data(), - }); - let xcm = Xcm(vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Superuser, - require_weight_at_most: Weight::from_parts(1000000000, 0), - call: initialize_call.encode().into(), - }, - ]); - // origin as relay chain - let origin = MultiLocation { parents: 1, interior: Here }; - - execute_on_runtime(bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, None, || { - // check mode before - assert_eq!( - pallet_bridge_grandpa::PalletOperatingMode::::try_get(), - Err(()) - ); - - // initialize bridge through governance-like - let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - let weight_limit = Weight::from_parts(41666666666, 0); - let outcome = XcmExecutor::::execute_xcm(origin, xcm, hash, weight_limit); - - // check mode after - assert_eq!(outcome.ensure_complete(), Ok(())); - assert_eq!( - pallet_bridge_grandpa::PalletOperatingMode::::try_get(), - Ok(bp_runtime::BasicOperatingMode::Normal) - ); - }) -} - // TODO:check-parameter - add test for DeliveryConfirmationPayments when receive_messages_delivery_proof const ALICE: [u8; 32] = [1u8; 32]; @@ -303,3 +227,62 @@ bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( }), 1013 ); + +bridge_hub_test_utils::include_initialize_bridge_by_governance_works!( + initialize_bridge_to_wococo_by_governance_works, + Runtime, + XcmConfig, + BridgeGrandpaWococoInstance, + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + Box::new(|call| RuntimeCall::BridgeWococoGrandpa(call).encode()), + 1013 +); + +mod bridge_hub_wococo { + use super::*; + + bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( + Runtime, + XcmConfig, + CheckingAccount, + WeightToFee, + ParachainSystem, + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + 1014 + ); + + bridge_hub_test_utils::include_initialize_bridge_by_governance_works!( + initialize_bridge_to_rococo_by_governance_works, + Runtime, + XcmConfig, + BridgeGrandpaRococoInstance, + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + Box::new(|call| RuntimeCall::BridgeRococoGrandpa(call).encode()), + 1014 + ); +} diff --git a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml index a606b02a04c..3d084aec125 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -11,9 +11,13 @@ description = "Utils for BridgeHub testing" frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +pallet-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } # Cumulus parachains-common = { path = "../../../common", default-features = false } +cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-features = false } +pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false } cumulus-primitives-parachain-inherent = { path = "../../../../primitives/parachain-inherent", default-features = false } @@ -35,6 +39,7 @@ bp-messages = { path = "../../../../bridges/primitives/messages", default-featur bp-polkadot-core = { path = "../../../../bridges/primitives/polkadot-core", default-features = false } bp-runtime = { path = "../../../../bridges/primitives/runtime", default-features = false } bp-test-utils = { path = "../../../../bridges/primitives/test-utils", default-features = false } +pallet-bridge-grandpa = { path = "../../../../bridges/modules/grandpa", default-features = false } [features] default = [ "std" ] @@ -46,6 +51,7 @@ std = [ "bp-header-chain/std", "bp-runtime/std", "bp-test-utils/std", + "pallet-bridge-grandpa/std", "parachains-common/std", "parachain-info/std", "cumulus-primitives-core/std", @@ -59,4 +65,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "asset-test-utils/std", + "cumulus-pallet-dmp-queue/std", + "pallet-session/std", + "pallet-balances/std", ] diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs index cfb9a4f81d2..55c64b6f59c 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs @@ -178,35 +178,7 @@ pub fn mock_open_hrmp_channel< .expect("dispatch succeeded"); } +// RelayChain header mock representation pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; pub type RelayBlockHasher = bp_polkadot_core::Hasher; pub type RelayBlockHeader = sp_runtime::generic::Header; - -/// Helper that creates InitializationData mock data, that can be used to initialize bridge GRANDPA pallet -pub fn mock_initialiation_data() -> bp_header_chain::InitializationData { - use sp_runtime::traits::Header; - use std::str::FromStr; - - let header = RelayBlockHeader::new( - 75, - bp_polkadot_core::Hash::from_str( - "0xd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d", - ) - .expect("invalid value"), - bp_polkadot_core::Hash::from_str( - "0x92b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141b", - ) - .expect("invalid value"), - bp_polkadot_core::Hash::from_str( - "0xae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d", - ) - .expect("invalid value"), - Default::default(), - ); - bp_header_chain::InitializationData { - header: Box::new(header), - authority_list: Default::default(), - set_id: 6, - operating_mode: bp_runtime::BasicOperatingMode::Normal, - } -} diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs index cdbeb65fab1..50b57b77eb7 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs @@ -16,5 +16,127 @@ //! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities. +use frame_support::{assert_ok, traits::Get}; + +// Lets re-use this stuff from assets (later we plan to move it outside of assets as `runtimes/test-utils`) +use asset_test_utils::{AccountIdOf, ExtBuilder, RuntimeHelper, ValidatorIdOf}; + // Re-export test_cases from assets pub use asset_test_utils::{include_teleports_for_native_asset_works, CollatorSessionKeys}; + +/// Test-case makes sure that `Runtime` can process bridging initialize via governance-like call +pub fn initialize_bridge_by_governance_works( + collator_session_key: CollatorSessionKeys, + runtime_call_encode: Box< + dyn Fn(pallet_bridge_grandpa::Call) -> Vec, + >, + runtime_para_id: u32, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_dmp_queue::Config + + cumulus_pallet_parachain_system::Config + + pallet_bridge_grandpa::Config, + GrandpaPalletInstance: 'static, + ValidatorIdOf: From>, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + // check mode before + assert_eq!( + pallet_bridge_grandpa::PalletOperatingMode::::try_get(), + Err(()) + ); + + // encode `initialize` call + let initialize_call = runtime_call_encode(pallet_bridge_grandpa::Call::< + Runtime, + GrandpaPalletInstance, + >::initialize { + init_data: test_data::initialiation_data::(12345), + }); + + // overestimate - check `pallet_bridge_grandpa::Pallet::initialize()` call + let require_weight_at_most = + ::DbWeight::get().reads_writes(7, 7); + + // execute XCM with Transacts to initialize bridge as governance does + // prepare data for xcm::Transact(create) + assert_ok!(RuntimeHelper::::execute_as_governance( + initialize_call, + require_weight_at_most + ) + .ensure_complete()); + + // check mode after + assert_eq!( + pallet_bridge_grandpa::PalletOperatingMode::::try_get(), + Ok(bp_runtime::BasicOperatingMode::Normal) + ); + }) +} + +#[macro_export] +macro_rules! include_initialize_bridge_by_governance_works( + ( + $test_name:tt, + $runtime:path, + $xcm_config:path, + $pallet_bridge_grandpa_instance:path, + $collator_session_key:expr, + $runtime_call_encode:expr, + $runtime_para_id:expr + ) => { + #[test] + fn $test_name() { + $crate::test_cases::initialize_bridge_by_governance_works::< + $runtime, + $xcm_config, + $pallet_bridge_grandpa_instance, + >( + $collator_session_key, + $runtime_call_encode, + $runtime_para_id + ) + } + } +); + +// process_export_message_from_system_parachain_works +// +// test_back_preasure_xcmp +// +// dispatch_blob_and_xcm_routing_works_on_bridge_hub_rococo +// dispatch_blob_and_xcm_routing_works_on_bridge_hub_wococo +// +// can_govornance_call_xcm_transact_with_initialize_on_bridge_hub_rococo +// can_govornance_call_xcm_transact_with_initialize_bridge_on_bridge_hub_wococo + +mod test_data { + + /// Helper that creates InitializationData mock data, that can be used to initialize bridge GRANDPA pallet + pub fn initialiation_data< + Runtime: pallet_bridge_grandpa::Config, + GrandpaPalletInstance: 'static, + >( + block_number: u32, + ) -> bp_header_chain::InitializationData< + pallet_bridge_grandpa::BridgedHeader, + > { + bp_header_chain::InitializationData { + header: Box::new(bp_test_utils::test_header(block_number.into())), + authority_list: Default::default(), + set_id: 6, + operating_mode: bp_runtime::BasicOperatingMode::Normal, + } + } +} From 1dba38d9d40a77c95aa4b91a29b9e59ee0d8b8b7 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Mon, 24 Apr 2023 14:33:21 +0200 Subject: [PATCH 246/263] Added test case `handle_export_message_from_system_parachain_to_outbound_queue_works` fix script Removed BridgeGrandpaRococoInstance --- Cargo.lock | 3 + .../src/bridge_hub_rococo_config.rs | 2 +- .../src/bridge_hub_wococo_config.rs | 2 +- .../bridge-hub-rococo/src/xcm_config.rs | 3 +- .../bridge-hub-rococo/tests/tests.rs | 54 +++++- .../bridge-hubs/test-utils/Cargo.toml | 6 + .../bridge-hubs/test-utils/src/test_cases.rs | 162 +++++++++++++++--- scripts/bridges_rococo_wococo.sh | 4 +- 8 files changed, 200 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a7c21f1a31..723920b4c00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1109,13 +1109,16 @@ dependencies = [ "frame-system", "pallet-balances", "pallet-bridge-grandpa", + "pallet-bridge-messages", "pallet-collator-selection", "pallet-session", "pallet-xcm", "pallet-xcm-benchmarks", "parachain-info", "parachains-common", + "parity-scale-codec", "polkadot-parachain", + "sp-io", "sp-runtime", "xcm", "xcm-builder", diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index b2f4a7af81b..263310a10e7 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -86,7 +86,7 @@ impl XcmBlobHauler for ToBridgeHubWococoXcmBlobHauler { DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO } } -const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO: LaneId = LaneId([0, 0, 0, 1]); +pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO: LaneId = LaneId([0, 0, 0, 1]); /// Messaging Bridge configuration for BridgeHubRococo -> BridgeHubWococo pub struct WithBridgeHubWococoMessageBridge; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 02d95c0646a..5ac9cb13596 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -86,7 +86,7 @@ impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO } } -const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO: LaneId = LaneId([0, 0, 0, 1]); +pub const DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO: LaneId = LaneId([0, 0, 0, 1]); /// Messaging Bridge configuration for BridgeHubWococo -> BridgeHubRococo pub struct WithBridgeHubRococoMessageBridge; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index af2b8ba7868..b8e4d8748da 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -202,6 +202,7 @@ pub type Barrier = DenyThenTry< ( // Allow anything to pay for execution. AllowTopLevelPaidExecutionFrom, + // TODO:check-parameter - add here "or SystemParachains" once merged https://github.com/paritytech/polkadot/pull/7005 // Parent and its plurality (i.e. governance bodies) gets free execution. AllowExplicitUnpaidExecutionFrom, // Subscriptions for version tracking are OK. @@ -210,7 +211,7 @@ pub type Barrier = DenyThenTry< UniversalLocation, ConstU32<8>, >, - // TODO:check-parameter - supporting unpaid execution at first, then SovereignPaid + // TODO:check-parameter - supporting unpaid execution at first, then SovereignPaid, remove this once https://github.com/paritytech/polkadot/pull/7005 AllowUnpaidExecutionFrom, ), >; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index 814523cffd6..64ae2c8c10f 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -231,15 +231,36 @@ bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( bridge_hub_test_utils::include_initialize_bridge_by_governance_works!( initialize_bridge_to_wococo_by_governance_works, Runtime, - XcmConfig, BridgeGrandpaWococoInstance, bridge_hub_test_utils::CollatorSessionKeys::new( AccountId::from(ALICE), AccountId::from(ALICE), SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } ), - Box::new(|call| RuntimeCall::BridgeWococoGrandpa(call).encode()), - 1013 + 1013, + Box::new(|call| RuntimeCall::BridgeWococoGrandpa(call).encode()) +); + +bridge_hub_test_utils::include_handle_export_message_from_system_parachain_to_outbound_queue_works!( + handle_export_message_from_system_parachain_to_outbound_queue_works_for_wococo, + Runtime, + XcmConfig, + WithBridgeHubWococoMessagesInstance, + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + 1013, + 1000, + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::BridgeWococoMessages(event)) => Some(event), + _ => None, + } + }), + || ExportMessage { network: Wococo, destination: X1(Parachain(1234)), xcm: Xcm(vec![]) }, + bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO ); mod bridge_hub_wococo { @@ -275,14 +296,35 @@ mod bridge_hub_wococo { bridge_hub_test_utils::include_initialize_bridge_by_governance_works!( initialize_bridge_to_rococo_by_governance_works, Runtime, - XcmConfig, BridgeGrandpaRococoInstance, bridge_hub_test_utils::CollatorSessionKeys::new( AccountId::from(ALICE), AccountId::from(ALICE), SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } ), - Box::new(|call| RuntimeCall::BridgeRococoGrandpa(call).encode()), - 1014 + 1014, + Box::new(|call| RuntimeCall::BridgeRococoGrandpa(call).encode()) + ); + + bridge_hub_test_utils::include_handle_export_message_from_system_parachain_to_outbound_queue_works!( + handle_export_message_from_system_parachain_to_outbound_queue_works_for_rococo, + Runtime, + XcmConfig, + WithBridgeHubRococoMessagesInstance, + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + 1014, + 1000, + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::BridgeRococoMessages(event)) => Some(event), + _ => None, + } + }), + || ExportMessage { network: Rococo, destination: X1(Parachain(4321)), xcm: Xcm(vec![]) }, + bridge_hub_wococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO ); } diff --git a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml index 3d084aec125..70dfc4ce4c4 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -6,10 +6,12 @@ edition = "2021" description = "Utils for BridgeHub testing" [dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } # Substrate frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } pallet-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } @@ -40,10 +42,12 @@ bp-polkadot-core = { path = "../../../../bridges/primitives/polkadot-core", defa bp-runtime = { path = "../../../../bridges/primitives/runtime", default-features = false } bp-test-utils = { path = "../../../../bridges/primitives/test-utils", default-features = false } pallet-bridge-grandpa = { path = "../../../../bridges/modules/grandpa", default-features = false } +pallet-bridge-messages = { path = "../../../../bridges/modules/messages", default-features = false } [features] default = [ "std" ] std = [ + "codec/std", "frame-support/std", "frame-system/std", "bp-messages/std", @@ -52,6 +56,7 @@ std = [ "bp-runtime/std", "bp-test-utils/std", "pallet-bridge-grandpa/std", + "pallet-bridge-messages/std", "parachains-common/std", "parachain-info/std", "cumulus-primitives-core/std", @@ -60,6 +65,7 @@ std = [ "cumulus-test-relay-sproof-builder/std", "polkadot-parachain/std", "pallet-xcm/std", + "sp-io/std", "sp-runtime/std", "xcm/std", "xcm-builder/std", diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs index 50b57b77eb7..3417e76ac19 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs @@ -16,21 +16,27 @@ //! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities. +use codec::Encode; use frame_support::{assert_ok, traits::Get}; +use xcm::latest::prelude::*; +use xcm_executor::XcmExecutor; // Lets re-use this stuff from assets (later we plan to move it outside of assets as `runtimes/test-utils`) use asset_test_utils::{AccountIdOf, ExtBuilder, RuntimeHelper, ValidatorIdOf}; // Re-export test_cases from assets -pub use asset_test_utils::{include_teleports_for_native_asset_works, CollatorSessionKeys}; +pub use asset_test_utils::{ + include_teleports_for_native_asset_works, CollatorSessionKeys, XcmReceivedFrom, +}; +use bp_messages::{LaneId, OutboundLaneData}; /// Test-case makes sure that `Runtime` can process bridging initialize via governance-like call -pub fn initialize_bridge_by_governance_works( +pub fn initialize_bridge_by_governance_works( collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, runtime_call_encode: Box< dyn Fn(pallet_bridge_grandpa::Call) -> Vec, >, - runtime_para_id: u32, ) where Runtime: frame_system::Config + pallet_balances::Config @@ -53,9 +59,9 @@ pub fn initialize_bridge_by_governance_works::try_get(), - Err(()) - ); + pallet_bridge_grandpa::PalletOperatingMode::::try_get(), + Err(()) + ); // encode `initialize` call let initialize_call = runtime_call_encode(pallet_bridge_grandpa::Call::< @@ -65,7 +71,7 @@ pub fn initialize_bridge_by_governance_works(12345), }); - // overestimate - check `pallet_bridge_grandpa::Pallet::initialize()` call + // overestimate - check weight for `pallet_bridge_grandpa::Pallet::initialize()` call let require_weight_at_most = ::DbWeight::get().reads_writes(7, 7); @@ -79,9 +85,9 @@ pub fn initialize_bridge_by_governance_works::try_get(), - Ok(bp_runtime::BasicOperatingMode::Normal) - ); + pallet_bridge_grandpa::PalletOperatingMode::::try_get(), + Ok(bp_runtime::BasicOperatingMode::Normal) + ); }) } @@ -90,36 +96,142 @@ macro_rules! include_initialize_bridge_by_governance_works( ( $test_name:tt, $runtime:path, - $xcm_config:path, $pallet_bridge_grandpa_instance:path, $collator_session_key:expr, - $runtime_call_encode:expr, - $runtime_para_id:expr + $runtime_para_id:expr, + $runtime_call_encode:expr ) => { #[test] fn $test_name() { $crate::test_cases::initialize_bridge_by_governance_works::< $runtime, - $xcm_config, $pallet_bridge_grandpa_instance, >( $collator_session_key, - $runtime_call_encode, - $runtime_para_id + $runtime_para_id, + $runtime_call_encode ) } } ); -// process_export_message_from_system_parachain_works -// -// test_back_preasure_xcmp -// -// dispatch_blob_and_xcm_routing_works_on_bridge_hub_rococo -// dispatch_blob_and_xcm_routing_works_on_bridge_hub_wococo -// -// can_govornance_call_xcm_transact_with_initialize_on_bridge_hub_rococo -// can_govornance_call_xcm_transact_with_initialize_bridge_on_bridge_hub_wococo +/// Test-case makes sure that `Runtime` can handle xcm `ExportMessage`: +/// Checks if received XCM messages is correctly added to the message outbound queue for delivery. +/// For SystemParachains we expect unpaid execution. +pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< + Runtime, + XcmConfig, + MessagesPalletInstance, +>( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + sibling_parachain_id: u32, + unwrap_pallet_bridge_messages_event: Box< + dyn Fn(Vec) -> Option>, + >, + export_message_instruction: fn() -> Instruction, + expected_lane_id: LaneId, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_dmp_queue::Config + + cumulus_pallet_parachain_system::Config + + pallet_bridge_messages::Config, + XcmConfig: xcm_executor::Config, + MessagesPalletInstance: 'static, + ValidatorIdOf: From>, +{ + assert_ne!(runtime_para_id, sibling_parachain_id); + let sibling_parachain_location = MultiLocation::new(1, Parachain(sibling_parachain_id)); + + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + // check queue before + assert_eq!( + pallet_bridge_messages::OutboundLanes::::try_get( + &expected_lane_id + ), + Err(()) + ); + + // prepare `ExportMessage` + let xcm = Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + export_message_instruction(), + ]); + + // execute XCM + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + assert_ok!(XcmExecutor::::execute_xcm( + sibling_parachain_location, + xcm, + hash, + RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), + ) + .ensure_complete()); + + // check queue after + assert_eq!( + pallet_bridge_messages::OutboundLanes::::try_get( + &expected_lane_id + ), + Ok(OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 0, + latest_generated_nonce: 1, + }) + ); + + // check events + let mut events = >::events() + .into_iter() + .filter_map(|e| unwrap_pallet_bridge_messages_event(e.event.encode())); + assert!( + events.any(|e| matches!(e, pallet_bridge_messages::Event::MessageAccepted { .. })) + ); + }) +} + +#[macro_export] +macro_rules! include_handle_export_message_from_system_parachain_to_outbound_queue_works( + ( + $test_name:tt, + $runtime:path, + $xcm_config:path, + $pallet_bridge_messages_instance:path, + $collator_session_key:expr, + $runtime_para_id:expr, + $sibling_parachain_id:expr, + $unwrap_pallet_bridge_messages_event:expr, + $export_message_instruction:expr, + $expected_lane_id:expr + ) => { + #[test] + fn $test_name() { + $crate::test_cases::handle_export_message_from_system_parachain_to_outbound_queue_works::< + $runtime, + $xcm_config, + $pallet_bridge_messages_instance + >( + $collator_session_key, + $runtime_para_id, + $sibling_parachain_id, + $unwrap_pallet_bridge_messages_event, + $export_message_instruction, + $expected_lane_id + ) + } + } +); mod test_data { diff --git a/scripts/bridges_rococo_wococo.sh b/scripts/bridges_rococo_wococo.sh index 55edd8dee24..fa79ab884ab 100755 --- a/scripts/bridges_rococo_wococo.sh +++ b/scripts/bridges_rococo_wococo.sh @@ -233,7 +233,7 @@ function allow_assets_transfer_send() { ] } }, - "targetLocationFee": { + "maxTargetLocationFee": { "id": { "Concrete": { "parents": 1, @@ -597,7 +597,7 @@ case "$1" in "ws://127.0.0.1:9010" \ "//Alice" \ "5DHZvp523gmJWxg9UcLVbofyu5nZkPvATeP1ciYncpFpXtiG" \ - $((1000000000 + 50000000000 * 20)) # ExistentialDeposit + targetLocationFee * 20 + $((1000000000 + 50000000000 * 20)) # ExistentialDeposit + maxTargetLocationFee * 20 # create foreign assets for native Statemine token (yes, Kusama, because we are using Statemine runtime on rococo) force_create_foreign_asset \ "ws://127.0.0.1:9945" \ From e6fedb9c800937aaf0d00f5c536cdf6b50c7bc51 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 27 Apr 2023 10:14:20 +0200 Subject: [PATCH 247/263] Added test-case `message_dispatch_routing_works` --- Cargo.lock | 7 +- .../src/messages_xcm_extension.rs | 22 +- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 12 +- .../bridge-hub-rococo/tests/tests.rs | 243 +++++------------ .../bridge-hubs/test-utils/Cargo.toml | 14 +- .../bridge-hubs/test-utils/src/lib.rs | 165 ------------ .../bridge-hubs/test-utils/src/test_cases.rs | 254 +++++++++++++++++- 7 files changed, 344 insertions(+), 373 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 723920b4c00..2b29b062980 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1100,13 +1100,13 @@ dependencies = [ "bp-polkadot-core", "bp-runtime", "bp-test-utils", + "bridge-runtime-common", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", - "cumulus-primitives-core", - "cumulus-primitives-parachain-inherent", - "cumulus-test-relay-sproof-builder", + "cumulus-pallet-xcmp-queue", "frame-support", "frame-system", + "log", "pallet-balances", "pallet-bridge-grandpa", "pallet-bridge-messages", @@ -1117,7 +1117,6 @@ dependencies = [ "parachain-info", "parachains-common", "parity-scale-codec", - "polkadot-parachain", "sp-io", "sp-runtime", "xcm", diff --git a/bridges/bin/runtime-common/src/messages_xcm_extension.rs b/bridges/bin/runtime-common/src/messages_xcm_extension.rs index 4ccdd7a4b4d..2d7ad1f5dc8 100644 --- a/bridges/bin/runtime-common/src/messages_xcm_extension.rs +++ b/bridges/bin/runtime-common/src/messages_xcm_extension.rs @@ -46,23 +46,13 @@ pub enum XcmBlobMessageDispatchResult { } /// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages -pub struct XcmBlobMessageDispatch -{ - _marker: sp_std::marker::PhantomData<( - SourceBridgeHubChain, - TargetBridgeHubChain, - DispatchBlob, - Weights, - )>, +pub struct XcmBlobMessageDispatch { + _marker: sp_std::marker::PhantomData<(RelayerAccountChain, DispatchBlob, Weights)>, } -impl< - SourceBridgeHubChain: Chain, - TargetBridgeHubChain: Chain, - BlobDispatcher: DispatchBlob, - Weights: MessagesPalletWeights, - > MessageDispatch> - for XcmBlobMessageDispatch +impl + MessageDispatch> + for XcmBlobMessageDispatch { type DispatchPayload = XcmAsPlainPayload; type DispatchLevelResult = XcmBlobMessageDispatchResult; @@ -78,7 +68,7 @@ impl< } fn dispatch( - _relayer_account: &AccountIdOf, + _relayer_account: &AccountIdOf, message: DispatchMessage, ) -> MessageDispatchResult { let payload = match message.data.payload { diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index ee0c7a660d6..28cc76c32a9 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -487,7 +487,6 @@ impl pallet_bridge_messages::Config for Run type SourceHeaderChain = SourceHeaderChainAdapter; type MessageDispatch = XcmBlobMessageDispatch< - bp_bridge_hub_wococo::BridgeHubWococo, bp_bridge_hub_rococo::BridgeHubRococo, OnBridgeHubRococoBlobDispatcher, Self::WeightInfo, @@ -524,7 +523,6 @@ impl pallet_bridge_messages::Config for Run type SourceHeaderChain = SourceHeaderChainAdapter; type MessageDispatch = XcmBlobMessageDispatch< - bp_bridge_hub_rococo::BridgeHubRococo, bp_bridge_hub_wococo::BridgeHubWococo, OnBridgeHubWococoBlobDispatcher, Self::WeightInfo, @@ -1209,6 +1207,12 @@ mod tests { use bridge_hub_test_utils::test_header; use codec::Encode; + // RelayChain header mock representation + // pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; + // pub type RelayBlockHasher = bp_polkadot_core::Hasher; + pub type TestBlockHeader = + sp_runtime::generic::Header; + #[test] fn ensure_signed_extension_definition_is_compatible_with_relay() { let payload: SignedExtra = ( @@ -1233,7 +1237,7 @@ mod tests { 10, 10, TransactionEra::Immortal, - test_header::(1).hash(), + test_header::(1).hash(), 10, 10, ); @@ -1246,7 +1250,7 @@ mod tests { 10, 10, TransactionEra::Immortal, - test_header::(1).hash(), + test_header::(1).hash(), 10, 10, ); diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index 64ae2c8c10f..260037629fa 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -14,13 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use bp_messages::target_chain::MessageDispatch; -use bp_runtime::messages::MessageDispatchResult; pub use bridge_hub_rococo_runtime::{ constants::fee::WeightToFee, - xcm_config::{XcmConfig, XcmRouter}, - Balances, BridgeGrandpaRococoInstance, BridgeGrandpaWococoInstance, ExistentialDeposit, - ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, + xcm_config::{RelayNetwork, XcmConfig, XcmRouter}, + Balances, BridgeGrandpaRococoInstance, BridgeGrandpaWococoInstance, BridgeWococoMessages, + ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, + SessionKeys, }; use codec::{Decode, Encode}; use xcm::latest::prelude::*; @@ -30,170 +29,8 @@ use bridge_hub_rococo_runtime::{ WithBridgeHubWococoMessagesInstance, }; -use bridge_hub_test_utils::{ - dummy_account, dummy_xcm, mock_open_hrmp_channel, new_test_ext, simulate_export_message, - wrap_as_dispatch_message, -}; -use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult; use frame_support::parameter_types; use parachains_common::{AccountId, AuraId}; -use xcm_builder::DispatchBlobError; - -fn execute_on_runtime( - with_para_id: u32, - open_hrmp_to_para_id: Option, - execute: impl FnOnce() -> R, -) -> R { - new_test_ext::(with_para_id.into(), 3).execute_with(|| { - if let Some(open_hrmp_to_para_id) = open_hrmp_to_para_id { - mock_open_hrmp_channel::( - with_para_id.into(), - open_hrmp_to_para_id.into(), - ); - } - execute() - }) -} - -#[test] -fn dispatch_blob_and_xcm_routing_works_on_bridge_hub_wococo() { - let universal_source_as_senders = - vec![X1(GlobalConsensus(Rococo)), X2(GlobalConsensus(Rococo), Parachain(1000))]; - let runtime_para_id = bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID; - let destination_network_id = Wococo; - let destination_para_id = 1000; - - for univeral_source_as_sender in universal_source_as_senders { - // 1. message is sent to other global consensus - Wococo(Here) - let bridging_message = - simulate_export_message::( - univeral_source_as_sender, - destination_network_id, - Here, - dummy_xcm(), - ); - let result: MessageDispatchResult = execute_on_runtime( - runtime_para_id, - None, - || { - <>::MessageDispatch as MessageDispatch<_>>::dispatch( - &dummy_account(), - wrap_as_dispatch_message(bridging_message) - ) - }, - ); - assert_eq!(result.dispatch_level_result, XcmBlobMessageDispatchResult::Dispatched); - - // 2. message is sent to other global consensus and its parachains - Wococo(Here) - let bridging_message = - simulate_export_message::( - univeral_source_as_sender, - destination_network_id, - X1(Parachain(destination_para_id)), - dummy_xcm(), - ); - - // 2.1. WITHOUT hrmp channel -> RoutingError - let result: MessageDispatchResult = execute_on_runtime( - runtime_para_id, - None, - || { - <>::MessageDispatch as MessageDispatch<_>>::dispatch( - &dummy_account(), - wrap_as_dispatch_message(bridging_message.clone()) - ) - }, - ); - assert_eq!( - result.dispatch_level_result, - XcmBlobMessageDispatchResult::NotDispatched(Some(DispatchBlobError::RoutingError)) - ); - - // 2.1. WITH hrmp channel -> Ok - let result: MessageDispatchResult = execute_on_runtime( - runtime_para_id, - Some(destination_para_id), - || { - <>::MessageDispatch as MessageDispatch<_>>::dispatch( - &dummy_account(), - wrap_as_dispatch_message(bridging_message.clone()) - ) - }, - ); - assert_eq!(result.dispatch_level_result, XcmBlobMessageDispatchResult::Dispatched); - } -} - -#[test] -fn dispatch_blob_and_xcm_routing_works_on_bridge_hub_rococo() { - let universal_source_as_senders = - vec![X1(GlobalConsensus(Wococo)), X2(GlobalConsensus(Wococo), Parachain(1000))]; - let runtime_para_id = bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID; - let destination_network_id = Rococo; - let destination_para_id = 1000; - - for univeral_source_as_sender in universal_source_as_senders { - // 1. message is sent to other global consensus - Wococo(Here) - let bridging_message = - simulate_export_message::( - univeral_source_as_sender, - destination_network_id, - Here, - dummy_xcm(), - ); - let result: MessageDispatchResult = execute_on_runtime( - runtime_para_id, - None, - || { - <>::MessageDispatch as MessageDispatch<_>>::dispatch( - &dummy_account(), - wrap_as_dispatch_message(bridging_message) - ) - }, - ); - assert_eq!(result.dispatch_level_result, XcmBlobMessageDispatchResult::Dispatched); - - // 2. message is sent to other global consensus and its parachains - Wococo(Here) - let bridging_message = - simulate_export_message::( - univeral_source_as_sender, - destination_network_id, - X1(Parachain(destination_para_id)), - dummy_xcm(), - ); - - // 2.1. WITHOUT hrmp channel -> RoutingError - let result: MessageDispatchResult = execute_on_runtime( - runtime_para_id, - None, - || { - <>::MessageDispatch as MessageDispatch<_>>::dispatch( - &dummy_account(), - wrap_as_dispatch_message(bridging_message.clone()) - ) - }, - ); - assert_eq!( - result.dispatch_level_result, - XcmBlobMessageDispatchResult::NotDispatched(Some(DispatchBlobError::RoutingError)) - ); - - // 2.1. WITH hrmp channel -> Ok - let result: MessageDispatchResult = execute_on_runtime( - runtime_para_id, - Some(destination_para_id), - || { - <>::MessageDispatch as MessageDispatch<_>>::dispatch( - &dummy_account(), - wrap_as_dispatch_message(bridging_message.clone()) - ) - }, - ); - assert_eq!(result.dispatch_level_result, XcmBlobMessageDispatchResult::Dispatched); - } -} - -// TODO:check-parameter - add test for DeliveryConfirmationPayments when receive_messages_delivery_proof const ALICE: [u8; 32] = [1u8; 32]; @@ -225,7 +62,7 @@ bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( _ => None, } }), - 1013 + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID ); bridge_hub_test_utils::include_initialize_bridge_by_governance_works!( @@ -237,7 +74,7 @@ bridge_hub_test_utils::include_initialize_bridge_by_governance_works!( AccountId::from(ALICE), SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } ), - 1013, + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, Box::new(|call| RuntimeCall::BridgeWococoGrandpa(call).encode()) ); @@ -251,7 +88,7 @@ bridge_hub_test_utils::include_handle_export_message_from_system_parachain_to_ou AccountId::from(ALICE), SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } ), - 1013, + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, 1000, Box::new(|runtime_event_encoded: Vec| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { @@ -263,6 +100,36 @@ bridge_hub_test_utils::include_handle_export_message_from_system_parachain_to_ou bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO ); +bridge_hub_test_utils::include_message_dispatch_routing_works!( + message_dispatch_routing_works_for_wococo_messages, + Runtime, + XcmConfig, + ParachainSystem, + WithBridgeHubWococoMessagesInstance, + RelayNetwork, + bridge_hub_rococo_config::WococoGlobalConsensusNetwork, + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + 1000, + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::ParachainSystem(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO +); + mod bridge_hub_wococo { use super::*; @@ -290,7 +157,7 @@ mod bridge_hub_wococo { _ => None, } }), - 1014 + bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID ); bridge_hub_test_utils::include_initialize_bridge_by_governance_works!( @@ -302,7 +169,7 @@ mod bridge_hub_wococo { AccountId::from(ALICE), SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } ), - 1014, + bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, Box::new(|call| RuntimeCall::BridgeRococoGrandpa(call).encode()) ); @@ -316,7 +183,7 @@ mod bridge_hub_wococo { AccountId::from(ALICE), SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } ), - 1014, + bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, 1000, Box::new(|runtime_event_encoded: Vec| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { @@ -327,4 +194,34 @@ mod bridge_hub_wococo { || ExportMessage { network: Rococo, destination: X1(Parachain(4321)), xcm: Xcm(vec![]) }, bridge_hub_wococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO ); + + bridge_hub_test_utils::include_message_dispatch_routing_works!( + message_dispatch_routing_works_for_rococo_messages, + Runtime, + XcmConfig, + ParachainSystem, + WithBridgeHubRococoMessagesInstance, + RelayNetwork, + bridge_hub_wococo_config::RococoGlobalConsensusNetwork, + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID, + 1000, + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::ParachainSystem(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + bridge_hub_wococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_ROCOCO + ); } diff --git a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml index 70dfc4ce4c4..c00730a3afd 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml +++ b/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -7,6 +7,7 @@ description = "Utils for BridgeHub testing" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +log = { version = "0.4.17", default-features = false } # Substrate frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } @@ -20,15 +21,12 @@ pallet-balances = { git = "https://github.com/paritytech/substrate", default-fea parachains-common = { path = "../../../common", default-features = false } cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } -cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false } -cumulus-primitives-parachain-inherent = { path = "../../../../primitives/parachain-inherent", default-features = false } -cumulus-test-relay-sproof-builder = { path = "../../../../test/relay-sproof-builder", default-features = false } +cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false } asset-test-utils = { path = "../../assets/test-utils"} # Polkadot -polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } pallet-xcm-benchmarks = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false, optional = true } xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } @@ -43,11 +41,13 @@ bp-runtime = { path = "../../../../bridges/primitives/runtime", default-features bp-test-utils = { path = "../../../../bridges/primitives/test-utils", default-features = false } pallet-bridge-grandpa = { path = "../../../../bridges/modules/grandpa", default-features = false } pallet-bridge-messages = { path = "../../../../bridges/modules/messages", default-features = false } +bridge-runtime-common = { path = "../../../../bridges/bin/runtime-common", default-features = false } [features] default = [ "std" ] std = [ "codec/std", + "log/std", "frame-support/std", "frame-system/std", "bp-messages/std", @@ -55,15 +55,13 @@ std = [ "bp-header-chain/std", "bp-runtime/std", "bp-test-utils/std", + "bridge-runtime-common/std", "pallet-bridge-grandpa/std", "pallet-bridge-messages/std", "parachains-common/std", "parachain-info/std", - "cumulus-primitives-core/std", "cumulus-pallet-parachain-system/std", - "cumulus-primitives-parachain-inherent/std", - "cumulus-test-relay-sproof-builder/std", - "polkadot-parachain/std", + "cumulus-pallet-xcmp-queue/std", "pallet-xcm/std", "sp-io/std", "sp-runtime/std", diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs index 55c64b6f59c..b65b25c525d 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs @@ -14,171 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use bp_messages::{ - target_chain::{DispatchMessage, DispatchMessageData}, - LaneId, MessageKey, -}; -use cumulus_primitives_core::{AbridgedHrmpChannel, ParaId, PersistedValidationData}; -use cumulus_primitives_parachain_inherent::ParachainInherentData; -use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; -use frame_support::{ - dispatch::{RawOrigin, UnfilteredDispatchable}, - inherent::{InherentData, ProvideInherent}, - sp_io, - traits::Get, -}; -use parachains_common::AccountId; -use polkadot_parachain::primitives::{HrmpChannelId, RelayChainBlockNumber}; -use xcm::{latest::prelude::*, prelude::XcmVersion}; -use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; -use xcm_executor::traits::{validate_export, ExportXcm}; - pub use bp_test_utils::test_header; pub mod test_cases; pub use test_cases::CollatorSessionKeys; - -/// Dummy xcm -pub fn dummy_xcm() -> Xcm<()> { - vec![Trap(42)].into() -} - -pub fn wrap_as_dispatch_message(payload: Vec) -> DispatchMessage> { - DispatchMessage { - key: MessageKey { lane_id: LaneId([0, 0, 0, 0]), nonce: 1 }, - data: DispatchMessageData { payload: Ok(payload) }, - } -} - -/// Dummy account -pub fn dummy_account() -> AccountId { - AccountId::from([0u8; 32]) -} - -/// Macro used for simulate_export_message and capturing bytes -macro_rules! grab_haul_blob ( - ($name:ident, $grabbed_payload:ident) => { - std::thread_local! { - static $grabbed_payload: std::cell::RefCell>> = std::cell::RefCell::new(None); - } - - struct $name; - impl HaulBlob for $name { - fn haul_blob(blob: Vec) -> Result<(), HaulBlobError>{ - $grabbed_payload.with(|rm| *rm.borrow_mut() = Some(blob)); - Ok(()) - } - } - } -); - -/// Simulates HaulBlobExporter and all its wrapping and captures generated plain bytes -pub fn simulate_export_message>( - sender: Junctions, - destination_network: NetworkId, - destination: Junctions, - xcm: xcm::v3::Xcm<()>, -) -> Vec { - grab_haul_blob!(GrabbingHaulBlob, GRABBED_HAUL_BLOB_PAYLOAD); - - let channel = 1_u32; - let universal_source = sender; - - // simulate XCM message export - let (ticket, fee) = validate_export::>( - destination_network, - channel, - universal_source, - destination, - xcm, - ) - .expect("validate_export error"); - println!("[MessageExporter::fee] {:?}", fee); - let result = HaulBlobExporter::::deliver(ticket) - .expect("deliver error"); - println!("[MessageExporter::deliver] {:?}", result); - - GRABBED_HAUL_BLOB_PAYLOAD.with(|r| r.take().expect("xcm::ExportMessage should be here")) -} - -/// Initialize runtime/externalities -pub fn new_test_ext( - para_id: ParaId, - xcm_version: XcmVersion, -) -> sp_io::TestExternalities { - frame_support::sp_tracing::try_init_simple(); - - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - >::assimilate_storage( - &pallet_xcm::GenesisConfig { safe_xcm_version: Some(xcm_version) }, - &mut t, - ) - .unwrap(); - >::assimilate_storage( - ¶chain_info::GenesisConfig { parachain_id: para_id }, - &mut t, - ) - .unwrap(); - - sp_io::TestExternalities::new(t) -} - -/// Helper function which emulates opening HRMP channel which is needed for XcmpQueue xcm router to pass -pub fn mock_open_hrmp_channel< - C: cumulus_pallet_parachain_system::Config, - T: ProvideInherent>, ->( - sender: ParaId, - recipient: ParaId, -) { - let n = 1_u32; - let mut sproof_builder = RelayStateSproofBuilder::default(); - sproof_builder.para_id = sender; - sproof_builder.hrmp_channels.insert( - HrmpChannelId { sender, recipient }, - AbridgedHrmpChannel { - max_capacity: 10, - max_total_size: 10_000_000_u32, - max_message_size: 10_000_000_u32, - msg_count: 10, - total_size: 10_000_000_u32, - mqc_head: None, - }, - ); - sproof_builder.hrmp_egress_channel_index = Some(vec![recipient]); - - let (relay_parent_storage_root, relay_chain_state) = sproof_builder.into_state_root_and_proof(); - let vfp = PersistedValidationData { - relay_parent_number: n as RelayChainBlockNumber, - relay_parent_storage_root, - ..Default::default() - }; - // It is insufficient to push the validation function params - // to storage; they must also be included in the inherent data. - let inherent_data = { - let mut inherent_data = InherentData::default(); - let system_inherent_data = ParachainInherentData { - validation_data: vfp.clone(), - relay_chain_state, - downward_messages: Default::default(), - horizontal_messages: Default::default(), - }; - inherent_data - .put_data( - cumulus_primitives_parachain_inherent::INHERENT_IDENTIFIER, - &system_inherent_data, - ) - .expect("failed to put VFP inherent"); - inherent_data - }; - - // execute the block - T::create_inherent(&inherent_data) - .expect("got an inherent") - .dispatch_bypass_filter(RawOrigin::None.into()) - .expect("dispatch succeeded"); -} - -// RelayChain header mock representation -pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; -pub type RelayBlockHasher = bp_polkadot_core::Hasher; -pub type RelayBlockHeader = sp_runtime::generic::Header; diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs index 3417e76ac19..b1031ed92be 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs @@ -19,16 +19,25 @@ use codec::Encode; use frame_support::{assert_ok, traits::Get}; use xcm::latest::prelude::*; +use xcm_builder::DispatchBlobError; use xcm_executor::XcmExecutor; // Lets re-use this stuff from assets (later we plan to move it outside of assets as `runtimes/test-utils`) -use asset_test_utils::{AccountIdOf, ExtBuilder, RuntimeHelper, ValidatorIdOf}; +use asset_test_utils::{ + mock_open_hrmp_channel, AccountIdOf, ExtBuilder, RuntimeHelper, ValidatorIdOf, +}; // Re-export test_cases from assets pub use asset_test_utils::{ include_teleports_for_native_asset_works, CollatorSessionKeys, XcmReceivedFrom, }; -use bp_messages::{LaneId, OutboundLaneData}; +use bp_messages::{ + target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, + LaneId, MessageKey, OutboundLaneData, +}; +use bridge_runtime_common::messages_xcm_extension::{ + XcmAsPlainPayload, XcmBlobMessageDispatchResult, +}; /// Test-case makes sure that `Runtime` can process bridging initialize via governance-like call pub fn initialize_bridge_by_governance_works( @@ -233,10 +242,174 @@ macro_rules! include_handle_export_message_from_system_parachain_to_outbound_que } ); +/// Test-case makes sure that Runtime can route XCM messages received in inbound queue, +/// We just test here `MessageDispatch` configuration. +/// We expect that runtime can route messages: +/// 1. to Parent (relay chain) +/// 2. to Sibling parachain +pub fn message_dispatch_routing_works< + Runtime, + XcmConfig, + HrmpChannelOpener, + MessagesPalletInstance, + RuntimeNetwork, + BridgedNetwork, +>( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + sibling_parachain_id: u32, + unwrap_cumulus_pallet_parachain_system_event: Box< + dyn Fn(Vec) -> Option>, + >, + unwrap_cumulus_pallet_xcmp_queue_event: Box< + dyn Fn(Vec) -> Option>, + >, + expected_lane_id: LaneId, + this_chain_relayer_account: AccountIdOf, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_dmp_queue::Config + + cumulus_pallet_parachain_system::Config + + cumulus_pallet_xcmp_queue::Config + + pallet_bridge_messages::Config, + XcmConfig: xcm_executor::Config, + MessagesPalletInstance: 'static, + ValidatorIdOf: From>, + HrmpChannelOpener: frame_support::inherent::ProvideInherent< + Call = cumulus_pallet_parachain_system::Call, + >, + // MessageDispatcher: MessageDispatch, DispatchLevelResult = XcmBlobMessageDispatchResult, DispatchPayload = XcmAsPlainPayload>, + RuntimeNetwork: Get, + BridgedNetwork: Get, +{ + assert_ne!(runtime_para_id, sibling_parachain_id); + + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_safe_xcm_version(XCM_VERSION) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + // 1. this message is sent from other global consensus with destination of this Runtime relay chain (UMP) + let bridging_message = + test_data::simulate_message_exporter_on_bridged_chain::( + (RuntimeNetwork::get(), Here) + ); + let result = <>::MessageDispatch>::dispatch( + &this_chain_relayer_account, + test_data::dispatch_message(expected_lane_id, 1, bridging_message) + ); + assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::Dispatched)); + + // check events - UpwardMessageSent + let mut events = >::events() + .into_iter() + .filter_map(|e| unwrap_cumulus_pallet_parachain_system_event(e.event.encode())); + assert!( + events.any(|e| matches!(e, cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. })) + ); + + // 2. this message is sent from other global consensus with destination of this Runtime sibling parachain (HRMP) + let bridging_message = + test_data::simulate_message_exporter_on_bridged_chain::( + (RuntimeNetwork::get(), X1(Parachain(sibling_parachain_id))), + ); + + // 2.1. WITHOUT opened hrmp channel -> RoutingError + let result = + <>::MessageDispatch as MessageDispatch<_>>::dispatch( + &this_chain_relayer_account, + DispatchMessage { + key: MessageKey { lane_id: expected_lane_id, nonce: 1 }, + data: DispatchMessageData { payload: Ok(bridging_message.clone()) }, + } + ); + assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::NotDispatched(Some(DispatchBlobError::RoutingError)))); + + // check events - no XcmpMessageSent + assert_eq!(>::events() + .into_iter() + .filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode())) + .count(), 0); + + // 2.1. WITH hrmp channel -> Ok + mock_open_hrmp_channel::(runtime_para_id.into(), sibling_parachain_id.into()); + let result = <>::MessageDispatch as MessageDispatch<_>>::dispatch( + &this_chain_relayer_account, + DispatchMessage { + key: MessageKey { lane_id: expected_lane_id, nonce: 1 }, + data: DispatchMessageData { payload: Ok(bridging_message) }, + } + ); + assert_eq!(format!("{:?}", result.dispatch_level_result), format!("{:?}", XcmBlobMessageDispatchResult::Dispatched)); + + // check events - XcmpMessageSent + let mut events = >::events() + .into_iter() + .filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode())); + assert!( + events.any(|e| matches!(e, cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. })) + ); + }) +} + +#[macro_export] +macro_rules! include_message_dispatch_routing_works( + ( + $test_name:tt, + $runtime:path, + $xcm_config:path, + $hrmp_channel_opener:path, + $pallet_bridge_messages_instance:path, + $runtime_network:path, + $bridged_network:path, + $collator_session_key:expr, + $runtime_para_id:expr, + $sibling_parachain_id:expr, + $unwrap_cumulus_pallet_parachain_system_event:expr, + $unwrap_cumulus_pallet_xcmp_queue_event:expr, + $expected_lane_id:expr + ) => { + #[test] + fn $test_name() { + const THIS_CHAIN_RELAYER: [u8; 32] = [2u8; 32]; + let this_chain_relayer_account = parachains_common::AccountId::from(THIS_CHAIN_RELAYER); + + $crate::test_cases::message_dispatch_routing_works::< + $runtime, + $xcm_config, + $hrmp_channel_opener, + $pallet_bridge_messages_instance, + $runtime_network, + $bridged_network + >( + $collator_session_key, + $runtime_para_id, + $sibling_parachain_id, + $unwrap_cumulus_pallet_parachain_system_event, + $unwrap_cumulus_pallet_xcmp_queue_event, + $expected_lane_id, + this_chain_relayer_account + ) + } + } +); + mod test_data { + use super::*; + use bp_messages::MessageNonce; + use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; + use xcm_executor::traits::{validate_export, ExportXcm}; /// Helper that creates InitializationData mock data, that can be used to initialize bridge GRANDPA pallet - pub fn initialiation_data< + pub(crate) fn initialiation_data< Runtime: pallet_bridge_grandpa::Config, GrandpaPalletInstance: 'static, >( @@ -251,4 +424,79 @@ mod test_data { operating_mode: bp_runtime::BasicOperatingMode::Normal, } } + + /// Dummy xcm + pub(crate) fn dummy_xcm() -> Xcm<()> { + vec![Trap(42)].into() + } + + pub(crate) fn dispatch_message( + lane_id: LaneId, + nonce: MessageNonce, + payload: Vec, + ) -> DispatchMessage> { + DispatchMessage { + key: MessageKey { lane_id, nonce }, + data: DispatchMessageData { payload: Ok(payload) }, + } + } + + /// Macro used for simulate_export_message and capturing bytes + macro_rules! grab_haul_blob ( + ($name:ident, $grabbed_payload:ident) => { + std::thread_local! { + static $grabbed_payload: std::cell::RefCell>> = std::cell::RefCell::new(None); + } + + struct $name; + impl HaulBlob for $name { + fn haul_blob(blob: Vec) -> Result<(), HaulBlobError>{ + $grabbed_payload.with(|rm| *rm.borrow_mut() = Some(blob)); + Ok(()) + } + } + } + ); + + /// Simulates `HaulBlobExporter` and all its wrapping and captures generated plain bytes, + /// which are transfered over bridge. + pub(crate) fn simulate_message_exporter_on_bridged_chain< + SourceNetwork: Get, + DestinationNetwork: Get, + >( + (destination_network, destination_junctions): (NetworkId, Junctions), + ) -> Vec { + grab_haul_blob!(GrabbingHaulBlob, GRABBED_HAUL_BLOB_PAYLOAD); + + // lets pretend that some parachain on bridged chain exported the message + let universal_source_on_bridged_chain = + X2(GlobalConsensus(SourceNetwork::get()), Parachain(5678)); + let channel = 1_u32; + + // simulate XCM message export + let (ticket, fee) = + validate_export::>( + destination_network, + channel, + universal_source_on_bridged_chain, + destination_junctions, + dummy_xcm(), + ) + .expect("validate_export to pass"); + log::info!( + target: "simulate_message_exporter_on_bridged_chain", + "HaulBlobExporter::validate fee: {:?}", + fee + ); + let xcm_hash = + HaulBlobExporter::::deliver(ticket) + .expect("deliver to pass"); + log::info!( + target: "simulate_message_exporter_on_bridged_chain", + "HaulBlobExporter::deliver xcm_hash: {:?}", + xcm_hash + ); + + GRABBED_HAUL_BLOB_PAYLOAD.with(|r| r.take().expect("Encoded message should be here")) + } } From 9c5701c8ccafd307cae9abff43e8a3f86e855f47 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 27 Apr 2023 10:22:46 +0200 Subject: [PATCH 248/263] Squashed 'bridges/' changes from 2180797fbf..4850aac8ce 4850aac8ce Removed relayer_account: &AccountId from MessageDispatch (#2080) 8c8adafd54 Revert "Fix max-size messages at test chains (#2064)" (#2077) c01a63efd8 Fixed off-by-one when confirming rewards in messages pallet (#2075) a298be96aa Update subxt dependencies (#2072) c0eef51eab Fix max-size messages at test chains (#2064) 3a658e3697 Messages relay fixes (#2073) 0022b5ab22 Slash relayers for invalid transactions (#2025) 198104007f Bump enumflags2 from 0.7.5 to 0.7.7 9229b257e5 [ci] Fix rules for docker build (#2069) 660d791390 [ci] Update buildah command and version (#2058) e4535c0ca4 fix the way latest_confirmed_nonce_at_source is "calculated" (#2067) dbc2d37590 select nothing if we have already selected nonces to submit or have submitted something (#2065) a7eedd21fe [relay-substrate-client] Bump jsonrpsee (#2066) 8875d5aeae Bump clap from 4.2.2 to 4.2.4 25f9cf55e2 Another use of RangeInclusiveExt::checked_len() (#2060) 4942c12a5f submit lane unblock transactions from relay (#2030) c0325d3c9c Test deployments fixes (#2057) fc7b9b7ed7 Use the new matrix server (#2056) 63bcb5c10b Fixed delivery alert rule (#2052) git-subtree-dir: bridges git-subtree-split: 4850aac8ce6c34e5ca6246b88cd14c873a879cba --- .gitlab-ci.yml | 131 +- Cargo.lock | 331 +- bin/millau/node/Cargo.toml | 2 +- bin/millau/runtime/src/lib.rs | 9 + bin/millau/runtime/src/rialto_messages.rs | 2 - .../runtime/src/rialto_parachain_messages.rs | 2 - bin/millau/runtime/src/xcm_config.rs | 6 +- bin/rialto-parachain/node/Cargo.toml | 2 +- bin/rialto-parachain/runtime/src/lib.rs | 4 +- .../runtime/src/millau_messages.rs | 2 - bin/rialto/node/Cargo.toml | 2 +- bin/rialto/node/src/cli.rs | 10 +- bin/rialto/node/src/command.rs | 10 +- bin/rialto/runtime/src/lib.rs | 3 +- bin/rialto/runtime/src/millau_messages.rs | 2 - bin/rialto/runtime/src/xcm_config.rs | 3 +- bin/runtime-common/Cargo.toml | 2 + bin/runtime-common/src/messages.rs | 22 +- bin/runtime-common/src/messages_call_ext.rs | 10 + .../src/messages_xcm_extension.rs | 22 +- bin/runtime-common/src/mock.rs | 24 +- .../src/refund_relayer_extension.rs | 694 +- .../bridges/rialto-millau/docker-compose.yml | 1 + .../dashboard/grafana/bridges-alerts.json | 4083 +++++---- ...y-rococo-to-wococo-messages-dashboard.json | 1798 ++-- ...y-wococo-to-rococo-messages-dashboard.json | 1788 ++-- .../monitoring/grafana-matrix/config.yml | 4 +- .../dashboard/prometheus/rialto-targets.yml | 1 + deployments/networks/rialto.yml | 19 + modules/grandpa/src/lib.rs | 6 +- modules/messages/src/inbound_lane.rs | 90 +- modules/messages/src/lib.rs | 43 +- modules/messages/src/mock.rs | 25 +- modules/relayers/src/lib.rs | 581 +- modules/relayers/src/mock.rs | 45 +- modules/relayers/src/stake_adapter.rs | 186 + primitives/messages/src/target_chain.rs | 9 +- primitives/relayers/src/lib.rs | 4 + primitives/relayers/src/registration.rs | 121 + relays/client-rialto-parachain/Cargo.toml | 2 +- .../src/codegen_runtime.rs | 7715 +---------------- relays/client-rialto-parachain/src/lib.rs | 3 +- relays/client-substrate/Cargo.toml | 2 +- relays/client-substrate/src/chain.rs | 2 +- relays/client-substrate/src/client.rs | 2 +- relays/client-substrate/src/error.rs | 12 +- relays/client-substrate/src/rpc.rs | 6 +- relays/lib-substrate-relay/src/lib.rs | 2 +- relays/messages/Cargo.toml | 1 + relays/messages/src/message_lane_loop.rs | 30 +- relays/messages/src/message_race_delivery.rs | 611 +- relays/messages/src/message_race_loop.rs | 57 +- relays/messages/src/message_race_strategy.rs | 54 +- tools/runtime-codegen/Cargo.lock | 893 +- tools/runtime-codegen/Cargo.toml | 4 +- tools/runtime-codegen/src/main.rs | 31 +- 56 files changed, 7290 insertions(+), 12236 deletions(-) create mode 100644 modules/relayers/src/stake_adapter.rs create mode 100644 primitives/relayers/src/registration.rs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index eb88001b50c..9217a422908 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,4 @@ stages: - - lint - - check - test - build - publish @@ -13,8 +11,9 @@ variables: CARGO_INCREMENTAL: 0 ARCH: "x86_64" CI_IMAGE: "paritytech/bridges-ci:production" - BUILDAH_IMAGE: "quay.io/buildah/stable:v1.27" RUST_BACKTRACE: full + BUILDAH_IMAGE: "quay.io/buildah/stable:v1.29" + BUILDAH_COMMAND: "buildah --storage-driver overlay2" default: cache: {} @@ -50,15 +49,6 @@ default: .test-refs: &test-refs rules: - # FIXME: This is the cause why pipelines wouldn't start. The problem might be in our custom - # mirroring. This should be investigated further, but for now let's have the working - # pipeline. - # - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH - # changes: - # - '**.md' - # - diagrams/* - # - docs/* - # when: never - if: $CI_PIPELINE_SOURCE == "pipeline" - if: $CI_PIPELINE_SOURCE == "web" - if: $CI_PIPELINE_SOURCE == "schedule" @@ -66,7 +56,11 @@ default: - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 -.build-refs: &build-refs +.test-only-refs: &test-only-refs + rules: + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs + +.publish-refs: &publish-refs rules: # won't run on the CI image update pipeline - if: $CI_PIPELINE_SOURCE == "pipeline" @@ -78,6 +72,12 @@ default: # this job runs only on nightly pipeline with the mentioned variable, against `master` branch - if: $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "nightly" +.nightly-test: &nightly-test + rules: + # 2. another is triggered by scripts repo $CI_PIPELINE_SOURCE == "pipeline" it's for the CI image + # update, it also runs all the nightly checks. + - if: $CI_PIPELINE_SOURCE == "pipeline" + .deploy-refs: &deploy-refs rules: - if: $CI_PIPELINE_SOURCE == "pipeline" @@ -89,16 +89,12 @@ default: - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]{4}-[0-9]{2}-[0-9]{2}.*$/ # i.e. v2021-09-27, v2021-09-27-1 when: manual -.nightly-test: &nightly-test - rules: - # 2. another is triggered by scripts repo $CI_PIPELINE_SOURCE == "pipeline" it's for the CI image - # update, it also runs all the nightly checks. - - if: $CI_PIPELINE_SOURCE == "pipeline" -#### stage: lint + +#### stage: test clippy-nightly: - stage: lint + stage: test <<: *docker-env <<: *test-refs variables: @@ -108,23 +104,21 @@ clippy-nightly: - SKIP_WASM_BUILD=1 cargo +nightly clippy --all-targets -- -A clippy::redundant_closure -A clippy::derive-partial-eq-without-eq -A clippy::or_fun_call fmt: - stage: lint + stage: test <<: *docker-env <<: *test-refs script: - cargo +nightly fmt --all -- --check spellcheck: - stage: lint + stage: test <<: *docker-env <<: *test-refs script: - cargo spellcheck check --cfg=.config/spellcheck.toml --checkers hunspell -m 1 $(find . -type f -name '*.rs' ! -path "./target/*" ! -name 'codegen_runtime.rs' ! -name 'weights.rs') -#### stage: check - check: - stage: check + stage: test <<: *docker-env <<: *test-refs script: &check-script @@ -135,15 +129,13 @@ check: - SKIP_WASM_BUILD=1 time cargo check -p millau-runtime --locked --features runtime-benchmarks --verbose check-nightly: - stage: check + stage: test <<: *docker-env <<: *nightly-test script: - rustup default nightly - *check-script -#### stage: test - test: stage: test <<: *docker-env @@ -225,12 +217,17 @@ partial-repo-pallets-build-test: # we may live with failing partial repo build, it is just a signal for us allow_failure: true -#### stage: build - build: - stage: build + stage: test + rules: + # won't run on the CI image update pipeline + - if: $CI_PIPELINE_SOURCE == "pipeline" + when: never + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 + - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]{4}-[0-9]{2}-[0-9]{2}.*$/ # i.e. v2021-09-27, v2021-09-27-1 + - if: $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "nightly" + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs <<: *docker-env - <<: *build-refs <<: *collect-artifacts # master script: &build-script @@ -264,10 +261,66 @@ build-nightly: #### stage: publish +# check that images can be built +.build-image: &build-image + <<: *kubernetes-build + image: $BUILDAH_IMAGE + <<: *test-only-refs + variables: &build-image-variables + GIT_STRATEGY: none + DOCKERFILE: ci.Dockerfile + needs: + - job: build + artifacts: true + script: + # trim "-build-docker" from job name + - export DOCKER_IMAGE_NAME="${CI_JOB_NAME::-13}" + - if [[ "${CI_JOB_NAME::-13}" == "bridges-common-relay" ]]; then + export BRIDGES_PROJECT="substrate-relay"; + else + export BRIDGES_PROJECT="${CI_JOB_NAME::-13}"; + fi + - export IMAGE_NAME=docker.io/paritytech/${DOCKER_IMAGE_NAME} + - echo "Building ${IMAGE_NAME}" + - cd ./artifacts + - $BUILDAH_COMMAND build + --format=docker + --build-arg VCS_REF="${CI_COMMIT_SHORT_SHA}" + --build-arg BUILD_DATE="$(date +%d-%m-%Y)" + --build-arg PROJECT="${BRIDGES_PROJECT}" + --build-arg VERSION="${VERSION}" + --tag "${IMAGE_NAME}:latest" + --file "${DOCKERFILE}" . + +rialto-bridge-node-build-docker: + stage: publish + <<: *build-image + +rialto-parachain-collator-build-docker: + stage: publish + <<: *build-image + +millau-bridge-node-build-docker: + stage: publish + <<: *build-image + +substrate-relay-build-docker: + stage: publish + <<: *build-image + +bridges-common-relay-build-docker: + stage: publish + <<: *build-image + variables: + <<: *build-image-variables + BRIDGES_PROJECT: substrate-relay + DOCKER_IMAGE_NAME: bridges-common-relay + +# build and publish images .build-push-image: &build-push-image <<: *kubernetes-build image: $BUILDAH_IMAGE - <<: *build-refs + <<: *publish-refs variables: &image-variables GIT_STRATEGY: none DOCKERFILE: ci.Dockerfile @@ -297,7 +350,7 @@ build-nightly: - test "${Docker_Hub_User_Parity}" -a "${Docker_Hub_Pass_Parity}" || ( echo "no docker credentials provided"; exit 1 ) - cd ./artifacts - - buildah bud + - $BUILDAH_COMMAND build --format=docker --build-arg VCS_REF="${CI_COMMIT_SHORT_SHA}" --build-arg BUILD_DATE="$(date +%d-%m-%Y)" @@ -310,10 +363,10 @@ build-nightly: # The job will success only on the protected branch - echo "${Docker_Hub_Pass_Parity}" | buildah login --username "${Docker_Hub_User_Parity}" --password-stdin docker.io - - buildah info - - buildah push --format=v2s2 "${IMAGE_NAME}:${VERSION}" - - buildah push --format=v2s2 "${IMAGE_NAME}:sha-${CI_COMMIT_SHORT_SHA}" - - buildah push --format=v2s2 "${IMAGE_NAME}:${FLOATING_TAG}" + - $BUILDAH_COMMAND info + - $BUILDAH_COMMAND push --format=v2s2 "${IMAGE_NAME}:${VERSION}" + - $BUILDAH_COMMAND push --format=v2s2 "${IMAGE_NAME}:sha-${CI_COMMIT_SHORT_SHA}" + - $BUILDAH_COMMAND push --format=v2s2 "${IMAGE_NAME}:${FLOATING_TAG}" after_script: - env REGISTRY_AUTH_FILE= buildah logout --all @@ -343,7 +396,7 @@ bridges-common-relay: # Publish Docker images description to hub.docker.com -.publish-docker-image-description: &publish-docker-image-description +.publish-docker-image-description: stage: publish-docker-description image: paritytech/dockerhub-description variables: diff --git a/Cargo.lock b/Cargo.lock index f4b78e93bca..4c126a2ef9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1455,9 +1455,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.2.2" +version = "4.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b802d85aaf3a1cdb02b224ba472ebdea62014fccfcb269b95a4d76443b5ee5a" +checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62" dependencies = [ "clap_builder", "clap_derive", @@ -1466,9 +1466,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.2.2" +version = "4.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14a1a858f532119338887a4b8e1af9c60de8249cd7bafd68036a489e261e37b6" +checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" dependencies = [ "anstream", "anstyle", @@ -1884,7 +1884,7 @@ name = "cumulus-client-cli" version = "0.1.0" source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ - "clap 4.2.2", + "clap 4.2.4", "parity-scale-codec", "sc-chain-spec", "sc-cli", @@ -2247,7 +2247,7 @@ dependencies = [ "async-trait", "cumulus-primitives-core", "futures", - "jsonrpsee-core", + "jsonrpsee-core 0.16.2", "parity-scale-codec", "polkadot-overseer", "sc-client-api", @@ -2305,7 +2305,7 @@ dependencies = [ "cumulus-relay-chain-interface", "futures", "futures-timer", - "jsonrpsee", + "jsonrpsee 0.16.2", "lru 0.9.0", "parity-scale-codec", "polkadot-overseer", @@ -2892,22 +2892,22 @@ dependencies = [ [[package]] name = "enumflags2" -version = "0.7.5" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb" +checksum = "c041f5090df68b32bcd905365fd51769c8b9d553fe87fde0b683534f10c01bd2" dependencies = [ "enumflags2_derive", ] [[package]] name = "enumflags2_derive" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" +checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -3298,7 +3298,7 @@ dependencies = [ "Inflector", "array-bytes 4.2.0", "chrono", - "clap 4.2.2", + "clap 4.2.4", "comfy-table", "frame-benchmarking", "frame-support", @@ -4119,7 +4119,7 @@ dependencies = [ "rustls 0.20.8", "rustls-native-certs", "tokio", - "tokio-rustls", + "tokio-rustls 0.23.4", "webpki-roots", ] @@ -4434,13 +4434,26 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" dependencies = [ - "jsonrpsee-client-transport", - "jsonrpsee-core", + "jsonrpsee-client-transport 0.16.2", + "jsonrpsee-core 0.16.2", "jsonrpsee-http-client", - "jsonrpsee-proc-macros", + "jsonrpsee-proc-macros 0.16.2", "jsonrpsee-server", - "jsonrpsee-types", - "jsonrpsee-ws-client", + "jsonrpsee-types 0.16.2", + "jsonrpsee-ws-client 0.16.2", + "tracing", +] + +[[package]] +name = "jsonrpsee" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b971ce0f6cd1521ede485afc564b95b2c8e7079b9da41d4273bd9b55140a55d" +dependencies = [ + "jsonrpsee-core 0.17.1", + "jsonrpsee-proc-macros 0.17.1", + "jsonrpsee-types 0.17.1", + "jsonrpsee-ws-client 0.17.1", "tracing", ] @@ -4452,19 +4465,38 @@ checksum = "965de52763f2004bc91ac5bcec504192440f0b568a5d621c59d9dbd6f886c3fb" dependencies = [ "futures-util", "http", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.16.2", + "jsonrpsee-types 0.16.2", "pin-project", "rustls-native-certs", "soketto", "thiserror", "tokio", - "tokio-rustls", + "tokio-rustls 0.23.4", "tokio-util", "tracing", "webpki-roots", ] +[[package]] +name = "jsonrpsee-client-transport" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca00d975eda834826b04ad57d4e690c67439bb51b02eb0f8b7e4c30fcef8ab9" +dependencies = [ + "futures-util", + "http", + "jsonrpsee-core 0.17.1", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls 0.24.0", + "tokio-util", + "tracing", +] + [[package]] name = "jsonrpsee-core" version = "0.16.2" @@ -4481,7 +4513,7 @@ dependencies = [ "futures-util", "globset", "hyper", - "jsonrpsee-types", + "jsonrpsee-types 0.16.2", "parking_lot 0.12.1", "rand 0.8.5", "rustc-hash", @@ -4493,6 +4525,28 @@ dependencies = [ "tracing", ] +[[package]] +name = "jsonrpsee-core" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b83cca7a5a7899eed8b2935d5f755c8c4052ad66ab5b328bd34ac2b3ffd3515f" +dependencies = [ + "anyhow", + "async-lock", + "async-trait", + "beef", + "futures-timer", + "futures-util", + "jsonrpsee-types 0.17.1", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", + "tracing", +] + [[package]] name = "jsonrpsee-http-client" version = "0.16.2" @@ -4502,8 +4556,8 @@ dependencies = [ "async-trait", "hyper", "hyper-rustls", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.16.2", + "jsonrpsee-types 0.16.2", "rustc-hash", "serde", "serde_json", @@ -4525,6 +4579,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d814a21d9a819f8de1a41b819a263ffd68e4bb5f043d936db1c49b54684bde0a" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + [[package]] name = "jsonrpsee-server" version = "0.16.2" @@ -4535,8 +4602,8 @@ dependencies = [ "futures-util", "http", "hyper", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.16.2", + "jsonrpsee-types 0.16.2", "serde", "serde_json", "soketto", @@ -4561,6 +4628,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "jsonrpsee-types" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd301ccc3e08718393432d1961539d78c4580dcca86014dfe6769c308b2c08b2" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + [[package]] name = "jsonrpsee-ws-client" version = "0.16.2" @@ -4568,9 +4649,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b83daeecfc6517cfe210df24e570fb06213533dfb990318fae781f4c7119dd9" dependencies = [ "http", - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-client-transport 0.16.2", + "jsonrpsee-core 0.16.2", + "jsonrpsee-types 0.16.2", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a69852133d549b07cb37ff2d0ec540eae0d20abb75ae923f5d39bc7536d987" +dependencies = [ + "http", + "jsonrpsee-client-transport 0.17.1", + "jsonrpsee-core 0.17.1", + "jsonrpsee-types 0.17.1", ] [[package]] @@ -5569,6 +5662,7 @@ dependencies = [ "async-std", "async-trait", "bp-messages", + "env_logger", "finality-relay", "futures", "hex", @@ -5594,10 +5688,10 @@ dependencies = [ name = "millau-bridge-node" version = "0.1.0" dependencies = [ - "clap 4.2.2", + "clap 4.2.4", "frame-benchmarking", "frame-benchmarking-cli", - "jsonrpsee", + "jsonrpsee 0.16.2", "millau-runtime", "mmr-rpc", "node-inspect", @@ -5748,7 +5842,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "anyhow", - "jsonrpsee", + "jsonrpsee 0.16.2", "parity-scale-codec", "serde", "sp-api", @@ -6022,7 +6116,7 @@ name = "node-inspect" version = "0.9.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ - "clap 4.2.2", + "clap 4.2.4", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -7254,7 +7348,7 @@ name = "pallet-transaction-payment-rpc" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ - "jsonrpsee", + "jsonrpsee 0.16.2", "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", "sp-api", @@ -7812,7 +7906,7 @@ name = "polkadot-cli" version = "0.9.39" source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ - "clap 4.2.2", + "clap 4.2.4", "frame-benchmarking-cli", "futures", "log", @@ -8487,7 +8581,7 @@ name = "polkadot-rpc" version = "0.9.39" source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ - "jsonrpsee", + "jsonrpsee 0.16.2", "mmr-rpc", "pallet-transaction-payment-rpc", "polkadot-primitives", @@ -9684,7 +9778,7 @@ dependencies = [ "frame-support", "frame-system", "futures", - "jsonrpsee", + "jsonrpsee 0.17.1", "log", "num-traits", "pallet-balances", @@ -9792,7 +9886,7 @@ dependencies = [ name = "rialto-bridge-node" version = "0.1.0" dependencies = [ - "clap 4.2.2", + "clap 4.2.4", "frame-benchmarking", "frame-benchmarking-cli", "frame-support", @@ -9819,7 +9913,7 @@ dependencies = [ name = "rialto-parachain-collator" version = "0.1.0" dependencies = [ - "clap 4.2.2", + "clap 4.2.4", "cumulus-client-cli", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", @@ -9830,7 +9924,7 @@ dependencies = [ "cumulus-relay-chain-interface", "frame-benchmarking", "frame-benchmarking-cli", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "pallet-transaction-payment-rpc", "parity-scale-codec", @@ -10283,6 +10377,18 @@ dependencies = [ "webpki 0.22.0", ] +[[package]] +name = "rustls" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07180898a28ed6a7f7ba2311594308f595e3dd2e3c3812fa0a80a47b45f17e5d" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct 0.7.0", +] + [[package]] name = "rustls-native-certs" version = "0.6.2" @@ -10304,6 +10410,16 @@ dependencies = [ "base64 0.21.0", ] +[[package]] +name = "rustls-webpki" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.12" @@ -10459,7 +10575,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c dependencies = [ "array-bytes 4.2.0", "chrono", - "clap 4.2.2", + "clap 4.2.4", "fdlimit", "futures", "libp2p", @@ -10643,7 +10759,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "futures", - "jsonrpsee", + "jsonrpsee 0.16.2", "sc-consensus-babe", "sc-consensus-epochs", "sc-rpc-api", @@ -10700,7 +10816,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "futures", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -10773,7 +10889,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c dependencies = [ "finality-grandpa", "futures", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "parity-scale-codec", "sc-client-api", @@ -11155,7 +11271,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "futures", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -11184,7 +11300,7 @@ name = "sc-rpc-api" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ - "jsonrpsee", + "jsonrpsee 0.16.2", "parity-scale-codec", "sc-chain-spec", "sc-transaction-pool-api", @@ -11204,7 +11320,7 @@ version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "http", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "serde_json", "substrate-prometheus-endpoint", @@ -11222,7 +11338,7 @@ dependencies = [ "futures", "futures-util", "hex", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -11249,7 +11365,7 @@ dependencies = [ "exit-future", "futures", "futures-timer", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "parity-scale-codec", "parking_lot 0.12.1", @@ -11321,7 +11437,7 @@ name = "sc-storage-monitor" version = "0.1.0" source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ - "clap 4.2.2", + "clap 4.2.4", "fs4", "futures", "log", @@ -11337,7 +11453,7 @@ name = "sc-sync-state-rpc" version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ - "jsonrpsee", + "jsonrpsee 0.16.2", "parity-scale-codec", "sc-chain-spec", "sc-client-api", @@ -11500,16 +11616,58 @@ dependencies = [ [[package]] name = "scale-decode" -version = "0.4.0" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e5527e4b3bf079d4c0b2f253418598c380722ba37ef20fac9088081407f2b6" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-bits", + "scale-decode-derive", + "scale-info", + "thiserror", +] + +[[package]] +name = "scale-decode-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b38741b2f78e4391b94eac6b102af0f6ea2b0f7fe65adb55d7f4004f507854db" +dependencies = [ + "darling", + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "scale-encode" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d823d4be477fc33321f93d08fb6c2698273d044f01362dc27573a750deb7c233" +checksum = "15546e5efbb45f0fc2291f7e202dee8623274c5d8bbfdf9c6886cc8b44a7ced3" dependencies = [ "parity-scale-codec", + "primitive-types", "scale-bits", + "scale-encode-derive", "scale-info", "thiserror", ] +[[package]] +name = "scale-encode-derive" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd983cf0a9effd76138554ead18a6de542d1af175ac12fd5e91836c5c0268082" +dependencies = [ + "darling", + "proc-macro-crate", + "proc-macro2 1.0.56", + "quote 1.0.26", + "syn 1.0.109", +] + [[package]] name = "scale-info" version = "2.5.0" @@ -11538,15 +11696,16 @@ dependencies = [ [[package]] name = "scale-value" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a5e7810815bd295da73e4216d1dfbced3c7c7c7054d70fa5f6e4c58123fff4" +checksum = "11f549769261561e6764218f847e500588f9a79a289de49ce92f9e26642a3574" dependencies = [ "either", "frame-metadata", "parity-scale-codec", "scale-bits", "scale-decode", + "scale-encode", "scale-info", "serde", "thiserror", @@ -12317,16 +12476,16 @@ dependencies = [ [[package]] name = "sp-core-hashing" -version = "6.0.0" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc2d1947252b7a4e403b0a260f596920443742791765ec111daa2bbf98eff25" +checksum = "27449abdfbe41b473e625bce8113745e81d65777dd1d5a8462cf24137930dad8" dependencies = [ - "blake2", + "blake2b_simd", "byteorder", "digest 0.10.6", "sha2 0.10.6", "sha3", - "sp-std 6.0.0", + "sp-std 7.0.0", "twox-hash", ] @@ -12626,9 +12785,9 @@ source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c [[package]] name = "sp-std" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af0ee286f98455272f64ac5bb1384ff21ac029fbb669afbaf48477faff12760e" +checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986" [[package]] name = "sp-storage" @@ -13001,7 +13160,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c dependencies = [ "frame-system-rpc-runtime-api", "futures", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "parity-scale-codec", "sc-rpc-api", @@ -13138,7 +13297,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "sc-rpc-api", "serde", @@ -13150,7 +13309,7 @@ name = "substrate-state-trie-migration-rpc" version = "4.0.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ - "jsonrpsee", + "jsonrpsee 0.16.2", "log", "parity-scale-codec", "sc-client-api", @@ -13198,13 +13357,14 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "subxt" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54639dba6a113584083968b6a8f457dedae612abe1bd214762101ca29f12e332" +checksum = "53b9c4ddefcb2d87eb18a6336f65635c29208f766d0deefaa2a1a19f7426a993" dependencies = [ "base58", "blake2", "derivative", + "either", "frame-metadata", "futures", "getrandom 0.2.8", @@ -13215,11 +13375,12 @@ dependencies = [ "primitive-types", "scale-bits", "scale-decode", + "scale-encode", "scale-info", "scale-value", "serde", "serde_json", - "sp-core-hashing 6.0.0", + "sp-core-hashing 8.0.0", "subxt-macro", "subxt-metadata", "thiserror", @@ -13228,30 +13389,30 @@ dependencies = [ [[package]] name = "subxt-codegen" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e86cb719003f1cedf2710a6e55ca4c37aba4c989bbd3b81dd1c52af9e4827e" +checksum = "e924f41069e9273236398ff89662d6d336468a5d94faac812129d44547db0e7f" dependencies = [ "darling", "frame-metadata", "heck 0.4.1", "hex", - "jsonrpsee", + "jsonrpsee 0.16.2", "parity-scale-codec", - "proc-macro-error", "proc-macro2 1.0.56", "quote 1.0.26", "scale-info", "subxt-metadata", "syn 1.0.109", + "thiserror", "tokio", ] [[package]] name = "subxt-macro" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c08de402a78c4c06c3ee3702c80e519efdcb65911348e018b6998d04404916" +checksum = "ced0b043a069ee039f8700d3dfda01be156e4229c82277c305bc8e79a7dd855d" dependencies = [ "darling", "proc-macro-error", @@ -13261,14 +13422,14 @@ dependencies = [ [[package]] name = "subxt-metadata" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2593ab5f53435e6352675af4f9851342607f37785d84c7a3fb3139550d3c35f0" +checksum = "18be3b8f4308fe7369ee1df66ae59c2eca79de20eab57b0f41c75736e843300f" dependencies = [ "frame-metadata", "parity-scale-codec", "scale-info", - "sp-core-hashing 6.0.0", + "sp-core-hashing 8.0.0", ] [[package]] @@ -13614,6 +13775,16 @@ dependencies = [ "webpki 0.22.0", ] +[[package]] +name = "tokio-rustls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +dependencies = [ + "rustls 0.21.0", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.12" @@ -13916,7 +14087,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", - "clap 4.2.2", + "clap 4.2.4", "frame-remote-externalities", "hex", "log", @@ -15347,9 +15518,9 @@ dependencies = [ [[package]] name = "yap" -version = "0.7.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc77f52dc9e9b10d55d3f4462c3b7fc393c4f17975d641542833ab2d3bc26ef" +checksum = "e2a7eb6d82a11e4d0b8e6bda8347169aff4ccd8235d039bba7c47482d977dcf7" [[package]] name = "yasna" diff --git a/bin/millau/node/Cargo.toml b/bin/millau/node/Cargo.toml index 21b5cd02559..7eb7c79405b 100644 --- a/bin/millau/node/Cargo.toml +++ b/bin/millau/node/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.2.2", features = ["derive"] } +clap = { version = "4.2.4", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } serde_json = "1.0.96" diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index 4e6f1e43e8c..dccd75a5b00 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -372,6 +372,7 @@ parameter_types! { /// Authorities are changing every 5 minutes. pub const Period: BlockNumber = bp_millau::SESSION_LENGTH; pub const Offset: BlockNumber = 0; + pub const RelayerStakeReserveId: [u8; 8] = *b"brdgrlrs"; } impl pallet_session::Config for Runtime { @@ -392,6 +393,14 @@ impl pallet_bridge_relayers::Config for Runtime { type Reward = Balance; type PaymentProcedure = bp_relayers::PayRewardFromAccount, AccountId>; + type StakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< + AccountId, + BlockNumber, + Balances, + RelayerStakeReserveId, + ConstU64<1_000>, + ConstU64<8>, + >; type WeightInfo = (); } diff --git a/bin/millau/runtime/src/rialto_messages.rs b/bin/millau/runtime/src/rialto_messages.rs index 84535283d48..573e3f5e68d 100644 --- a/bin/millau/runtime/src/rialto_messages.rs +++ b/bin/millau/runtime/src/rialto_messages.rs @@ -64,8 +64,6 @@ pub type ToRialtoMessagesDeliveryProof = /// Call-dispatch based message dispatch for Rialto -> Millau messages. pub type FromRialtoMessageDispatch = bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatch< - bp_millau::Millau, - bp_rialto::Rialto, crate::xcm_config::OnMillauBlobDispatcher, (), >; diff --git a/bin/millau/runtime/src/rialto_parachain_messages.rs b/bin/millau/runtime/src/rialto_parachain_messages.rs index bef8a281188..041d3256f42 100644 --- a/bin/millau/runtime/src/rialto_parachain_messages.rs +++ b/bin/millau/runtime/src/rialto_parachain_messages.rs @@ -59,8 +59,6 @@ pub type FromRialtoParachainMessagePayload = messages::target::FromBridgedChainM /// Call-dispatch based message dispatch for RialtoParachain -> Millau messages. pub type FromRialtoParachainMessageDispatch = bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatch< - bp_millau::Millau, - bp_rialto::Rialto, crate::xcm_config::OnMillauBlobDispatcher, (), >; diff --git a/bin/millau/runtime/src/xcm_config.rs b/bin/millau/runtime/src/xcm_config.rs index 4aaec83771b..cabb70f40cc 100644 --- a/bin/millau/runtime/src/xcm_config.rs +++ b/bin/millau/runtime/src/xcm_config.rs @@ -349,8 +349,7 @@ mod tests { // we care only about handing message to the XCM dispatcher, so we don't care about its // actual dispatch - let dispatch_result = - FromRialtoMessageDispatch::dispatch(&AccountId::from([0u8; 32]), incoming_message); + let dispatch_result = FromRialtoMessageDispatch::dispatch(incoming_message); assert!(matches!( dispatch_result.dispatch_level_result, XcmBlobMessageDispatchResult::NotDispatched(_), @@ -363,8 +362,7 @@ mod tests { // we care only about handing message to the XCM dispatcher, so we don't care about its // actual dispatch - let dispatch_result = - FromRialtoMessageDispatch::dispatch(&AccountId::from([0u8; 32]), incoming_message); + let dispatch_result = FromRialtoMessageDispatch::dispatch(incoming_message); assert!(matches!( dispatch_result.dispatch_level_result, XcmBlobMessageDispatchResult::NotDispatched(_), diff --git a/bin/rialto-parachain/node/Cargo.toml b/bin/rialto-parachain/node/Cargo.toml index 2315eb94a11..38521d7aa0e 100644 --- a/bin/rialto-parachain/node/Cargo.toml +++ b/bin/rialto-parachain/node/Cargo.toml @@ -17,7 +17,7 @@ default = [] runtime-benchmarks = ['rialto-parachain-runtime/runtime-benchmarks'] [dependencies] -clap = { version = "4.2.2", features = ["derive"] } +clap = { version = "4.2.4", features = ["derive"] } log = '0.4.17' codec = { package = 'parity-scale-codec', version = '3.1.5' } serde = { version = '1.0', features = ['derive'] } diff --git a/bin/rialto-parachain/runtime/src/lib.rs b/bin/rialto-parachain/runtime/src/lib.rs index cd4e256f420..de8ca493520 100644 --- a/bin/rialto-parachain/runtime/src/lib.rs +++ b/bin/rialto-parachain/runtime/src/lib.rs @@ -533,6 +533,7 @@ impl pallet_bridge_relayers::Config for Runtime { type Reward = Balance; type PaymentProcedure = bp_relayers::PayRewardFromAccount, AccountId>; + type StakeAndSlash = (); type WeightInfo = (); } @@ -925,8 +926,7 @@ mod tests { // we care only about handing message to the XCM dispatcher, so we don't care about its // actual dispatch - let dispatch_result = - FromMillauMessageDispatch::dispatch(&AccountId::from([0u8; 32]), incoming_message); + let dispatch_result = FromMillauMessageDispatch::dispatch(incoming_message); assert!(matches!( dispatch_result.dispatch_level_result, XcmBlobMessageDispatchResult::NotDispatched(_), diff --git a/bin/rialto-parachain/runtime/src/millau_messages.rs b/bin/rialto-parachain/runtime/src/millau_messages.rs index 5d4a92b5091..9c00e6bad34 100644 --- a/bin/rialto-parachain/runtime/src/millau_messages.rs +++ b/bin/rialto-parachain/runtime/src/millau_messages.rs @@ -60,8 +60,6 @@ pub type FromMillauMessagePayload = messages::target::FromBridgedChainMessagePay /// Call-dispatch based message dispatch for Millau -> RialtoParachain messages. pub type FromMillauMessageDispatch = bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatch< - bp_rialto_parachain::RialtoParachain, - bp_millau::Millau, crate::OnRialtoParachainBlobDispatcher, (), >; diff --git a/bin/rialto/node/Cargo.toml b/bin/rialto/node/Cargo.toml index 23c7d3315e7..ffbb587dc1f 100644 --- a/bin/rialto/node/Cargo.toml +++ b/bin/rialto/node/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.2.2", features = ["derive"] } +clap = { version = "4.2.4", features = ["derive"] } serde_json = "1.0.96" # Bridge dependencies diff --git a/bin/rialto/node/src/cli.rs b/bin/rialto/node/src/cli.rs index 98323c9d9ca..c39efed4ce9 100644 --- a/bin/rialto/node/src/cli.rs +++ b/bin/rialto/node/src/cli.rs @@ -68,15 +68,15 @@ pub enum Subcommand { Inspect(node_inspect::cli::InspectCmd), /// Benchmark runtime pallets. - #[clap(subcommand)] + #[command(subcommand)] Benchmark(frame_benchmarking_cli::BenchmarkCmd), /// FOR INTERNAL USE: analog of the "prepare-worker" command of the polkadot binary. - #[clap(name = "prepare-worker", hide = true)] + #[command(name = "prepare-worker", hide = true)] PvfPrepareWorker(ValidationWorkerCommand), /// FOR INTERNAL USE: analog of the "execute-worker" command of the polkadot binary. - #[clap(name = "execute-worker", hide = true)] + #[command(name = "execute-worker", hide = true)] PvfExecuteWorker(ValidationWorkerCommand), } @@ -84,5 +84,9 @@ pub enum Subcommand { #[derive(Debug, Parser)] pub struct ValidationWorkerCommand { /// The path to the validation host's socket. + #[arg(long)] pub socket_path: String, + /// Calling node implementation version + #[arg(long)] + pub node_impl_version: String, } diff --git a/bin/rialto/node/src/command.rs b/bin/rialto/node/src/command.rs index 0bff2e523b4..a21b3a88d2c 100644 --- a/bin/rialto/node/src/command.rs +++ b/bin/rialto/node/src/command.rs @@ -164,7 +164,10 @@ pub fn run() -> sc_cli::Result<()> { builder.with_colors(false); let _ = builder.init(); - polkadot_node_core_pvf::prepare_worker_entrypoint(&cmd.socket_path, None); + polkadot_node_core_pvf::prepare_worker_entrypoint( + &cmd.socket_path, + Some(&cmd.node_impl_version), + ); Ok(()) }, Some(crate::cli::Subcommand::PvfExecuteWorker(cmd)) => { @@ -172,7 +175,10 @@ pub fn run() -> sc_cli::Result<()> { builder.with_colors(false); let _ = builder.init(); - polkadot_node_core_pvf::execute_worker_entrypoint(&cmd.socket_path, None); + polkadot_node_core_pvf::execute_worker_entrypoint( + &cmd.socket_path, + Some(&cmd.node_impl_version), + ); Ok(()) }, None => { diff --git a/bin/rialto/runtime/src/lib.rs b/bin/rialto/runtime/src/lib.rs index aeb0a3a7c57..0d2c667efa5 100644 --- a/bin/rialto/runtime/src/lib.rs +++ b/bin/rialto/runtime/src/lib.rs @@ -389,6 +389,7 @@ impl pallet_bridge_relayers::Config for Runtime { type Reward = Balance; type PaymentProcedure = bp_relayers::PayRewardFromAccount, AccountId>; + type StakeAndSlash = (); type WeightInfo = (); } @@ -495,7 +496,7 @@ construct_runtime!( Scheduler: polkadot_runtime_parachains::scheduler::{Pallet, Storage}, Paras: polkadot_runtime_parachains::paras::{Pallet, Call, Storage, Event, Config}, Initializer: polkadot_runtime_parachains::initializer::{Pallet, Call, Storage}, - Dmp: polkadot_runtime_parachains::dmp::{Pallet, Call, Storage}, + Dmp: polkadot_runtime_parachains::dmp::{Pallet, Storage}, Ump: polkadot_runtime_parachains::ump::{Pallet, Call, Storage, Event}, Hrmp: polkadot_runtime_parachains::hrmp::{Pallet, Call, Storage, Event, Config}, SessionInfo: polkadot_runtime_parachains::session_info::{Pallet, Storage}, diff --git a/bin/rialto/runtime/src/millau_messages.rs b/bin/rialto/runtime/src/millau_messages.rs index ab4b7dd521d..7c960639f2f 100644 --- a/bin/rialto/runtime/src/millau_messages.rs +++ b/bin/rialto/runtime/src/millau_messages.rs @@ -57,8 +57,6 @@ pub type FromMillauMessagePayload = messages::target::FromBridgedChainMessagePay /// Call-dispatch based message dispatch for Millau -> Rialto messages. pub type FromMillauMessageDispatch = bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatch< - bp_rialto::Rialto, - bp_millau::Millau, crate::xcm_config::OnRialtoBlobDispatcher, (), >; diff --git a/bin/rialto/runtime/src/xcm_config.rs b/bin/rialto/runtime/src/xcm_config.rs index 9f6488b4c4d..52c5af635b3 100644 --- a/bin/rialto/runtime/src/xcm_config.rs +++ b/bin/rialto/runtime/src/xcm_config.rs @@ -266,8 +266,7 @@ mod tests { // we care only about handing message to the XCM dispatcher, so we don't care about its // actual dispatch - let dispatch_result = - FromMillauMessageDispatch::dispatch(&AccountId::from([0u8; 32]), incoming_message); + let dispatch_result = FromMillauMessageDispatch::dispatch(incoming_message); assert!(matches!( dispatch_result.dispatch_level_result, XcmBlobMessageDispatchResult::NotDispatched(_), diff --git a/bin/runtime-common/Cargo.toml b/bin/runtime-common/Cargo.toml index 3db4ae9abca..e7cd39da90b 100644 --- a/bin/runtime-common/Cargo.toml +++ b/bin/runtime-common/Cargo.toml @@ -30,6 +30,7 @@ pallet-bridge-relayers = { path = "../../modules/relayers", default-features = f frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-utility = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -62,6 +63,7 @@ std = [ "frame-system/std", "hash-db/std", "log/std", + "pallet-balances/std", "pallet-bridge-grandpa/std", "pallet-bridge-messages/std", "pallet-bridge-parachains/std", diff --git a/bin/runtime-common/src/messages.rs b/bin/runtime-common/src/messages.rs index 9d2e5811380..6f6b1959577 100644 --- a/bin/runtime-common/src/messages.rs +++ b/bin/runtime-common/src/messages.rs @@ -20,7 +20,7 @@ //! pallet is used to dispatch incoming messages. Message identified by a tuple //! of to elements - message lane id and message nonce. -pub use bp_runtime::{UnderlyingChainOf, UnderlyingChainProvider}; +pub use bp_runtime::{RangeInclusiveExt, UnderlyingChainOf, UnderlyingChainProvider}; use bp_header_chain::{HeaderChain, HeaderChainError}; use bp_messages::{ @@ -365,6 +365,7 @@ pub mod target { nonces_start, nonces_end, } = proof; + let nonces_range = nonces_start..=nonces_end; B::BridgedHeaderChain::parse_finalized_storage_proof( bridged_header_hash, @@ -374,26 +375,17 @@ pub mod target { StorageProofCheckerAdapter::<_, B> { storage, _dummy: Default::default() }; // receiving proofs where end < begin is ok (if proof includes outbound lane state) - let messages_in_the_proof = - if let Some(nonces_difference) = nonces_end.checked_sub(nonces_start) { - // let's check that the user (relayer) has passed correct `messages_count` - // (this bounds maximal capacity of messages vec below) - let messages_in_the_proof = nonces_difference.saturating_add(1); - if messages_in_the_proof != MessageNonce::from(messages_count) { - return Err(Error::MessagesCountMismatch) - } - - messages_in_the_proof - } else { - 0 - }; + let messages_in_the_proof = nonces_range.checked_len().unwrap_or(0); + if messages_in_the_proof != MessageNonce::from(messages_count) { + return Err(Error::MessagesCountMismatch) + } // Read messages first. All messages that are claimed to be in the proof must // be in the proof. So any error in `read_value`, or even missing value is fatal. // // Mind that we allow proofs with no messages if outbound lane state is proved. let mut messages = Vec::with_capacity(messages_in_the_proof as _); - for nonce in nonces_start..=nonces_end { + for nonce in nonces_range { let message_key = MessageKey { lane_id: lane, nonce }; let message_payload = parser.read_and_decode_message_payload(&message_key)?; messages.push(Message { key: message_key, payload: message_payload }); diff --git a/bin/runtime-common/src/messages_call_ext.rs b/bin/runtime-common/src/messages_call_ext.rs index f3665a8d93b..3f48ce583f9 100644 --- a/bin/runtime-common/src/messages_call_ext.rs +++ b/bin/runtime-common/src/messages_call_ext.rs @@ -115,6 +115,16 @@ pub enum CallInfo { ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo), } +impl CallInfo { + /// Returns range of messages, bundled with the call. + pub fn bundled_messages(&self) -> RangeInclusive { + match *self { + Self::ReceiveMessagesProof(ref info) => info.base.bundled_range.clone(), + Self::ReceiveMessagesDeliveryProof(ref info) => info.0.bundled_range.clone(), + } + } +} + /// Helper struct that provides methods for working with a call supported by `CallInfo`. pub struct CallHelper, I: 'static> { pub _phantom_data: sp_std::marker::PhantomData<(T, I)>, diff --git a/bin/runtime-common/src/messages_xcm_extension.rs b/bin/runtime-common/src/messages_xcm_extension.rs index 4ccdd7a4b4d..96fdf1d5018 100644 --- a/bin/runtime-common/src/messages_xcm_extension.rs +++ b/bin/runtime-common/src/messages_xcm_extension.rs @@ -26,7 +26,7 @@ use bp_messages::{ target_chain::{DispatchMessage, MessageDispatch}, LaneId, }; -use bp_runtime::{messages::MessageDispatchResult, AccountIdOf, Chain}; +use bp_runtime::messages::MessageDispatchResult; use codec::{Decode, Encode}; use frame_support::{dispatch::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; use pallet_bridge_messages::WeightInfoExt as MessagesPalletWeights; @@ -46,23 +46,12 @@ pub enum XcmBlobMessageDispatchResult { } /// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages -pub struct XcmBlobMessageDispatch -{ - _marker: sp_std::marker::PhantomData<( - SourceBridgeHubChain, - TargetBridgeHubChain, - DispatchBlob, - Weights, - )>, +pub struct XcmBlobMessageDispatch { + _marker: sp_std::marker::PhantomData<(DispatchBlob, Weights)>, } -impl< - SourceBridgeHubChain: Chain, - TargetBridgeHubChain: Chain, - BlobDispatcher: DispatchBlob, - Weights: MessagesPalletWeights, - > MessageDispatch> - for XcmBlobMessageDispatch +impl MessageDispatch + for XcmBlobMessageDispatch { type DispatchPayload = XcmAsPlainPayload; type DispatchLevelResult = XcmBlobMessageDispatchResult; @@ -78,7 +67,6 @@ impl< } fn dispatch( - _relayer_account: &AccountIdOf, message: DispatchMessage, ) -> MessageDispatchResult { let payload = match message.data.payload { diff --git a/bin/runtime-common/src/mock.rs b/bin/runtime-common/src/mock.rs index 036813f6fd5..c1767199676 100644 --- a/bin/runtime-common/src/mock.rs +++ b/bin/runtime-common/src/mock.rs @@ -35,6 +35,7 @@ use crate::messages::{ use bp_header_chain::{ChainWithGrandpa, HeaderChain}; use bp_messages::{target_chain::ForbidInboundMessages, LaneId, MessageNonce}; use bp_parachains::SingleParaStoredHeaderDataBuilder; +use bp_relayers::PayRewardFromAccount; use bp_runtime::{Chain, ChainId, Parachain, UnderlyingChainProvider}; use codec::{Decode, Encode}; use frame_support::{ @@ -83,6 +84,20 @@ pub type BridgedChainHasher = BlakeTwo256; pub type BridgedChainHeader = sp_runtime::generic::Header; +/// Rewards payment procedure. +pub type TestPaymentProcedure = PayRewardFromAccount; +/// Stake that we are using in tests. +pub type TestStake = ConstU64<5_000>; +/// Stake and slash mechanism to use in tests. +pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< + ThisChainAccountId, + ThisChainBlockNumber, + Balances, + ReserveId, + TestStake, + ConstU32<8>, +>; + /// Message lane used in tests. pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]); /// Bridged chain id used in tests. @@ -128,6 +143,7 @@ parameter_types! { pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value(); pub const MaxUnrewardedRelayerEntriesAtInboundLane: MessageNonce = 16; pub const MaxUnconfirmedMessagesAtInboundLane: MessageNonce = 1_000; + pub const ReserveId: [u8; 8] = *b"brdgrlrs"; } impl frame_system::Config for TestRuntime { @@ -244,7 +260,8 @@ impl pallet_bridge_messages::Config for TestRuntime { impl pallet_bridge_relayers::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type Reward = ThisChainBalance; - type PaymentProcedure = (); + type PaymentProcedure = TestPaymentProcedure; + type StakeAndSlash = TestStakeAndSlash; type WeightInfo = (); } @@ -400,3 +417,8 @@ impl ThisChainWithMessages for BridgedChain { } impl BridgedChainWithMessages for BridgedChain {} + +/// Run test within test externalities. +pub fn run_test(test: impl FnOnce()) { + sp_io::TestExternalities::new(Default::default()).execute_with(test) +} diff --git a/bin/runtime-common/src/refund_relayer_extension.rs b/bin/runtime-common/src/refund_relayer_extension.rs index 925fea2a743..7d65263e9fd 100644 --- a/bin/runtime-common/src/refund_relayer_extension.rs +++ b/bin/runtime-common/src/refund_relayer_extension.rs @@ -22,7 +22,7 @@ use crate::messages_call_ext::{ CallHelper as MessagesCallHelper, CallInfo as MessagesCallInfo, MessagesCallSubType, }; -use bp_messages::LaneId; +use bp_messages::{LaneId, MessageNonce}; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::{RangeInclusiveExt, StaticStrProvider}; use codec::{Decode, Encode}; @@ -30,7 +30,7 @@ use frame_support::{ dispatch::{CallableCallFor, DispatchInfo, Dispatchable, PostDispatchInfo}, traits::IsSubType, weights::Weight, - CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, + CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebug, RuntimeDebugNoBound, }; use pallet_bridge_grandpa::{ CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper, SubmitFinalityProofInfo, @@ -53,6 +53,7 @@ use sp_runtime::{ }; use sp_std::{marker::PhantomData, vec, vec::Vec}; +type AccountIdOf = ::AccountId; // without this typedef rustfmt fails with internal err type BalanceOf = <::OnChargeTransaction as OnChargeTransaction>::Balance; @@ -158,6 +159,14 @@ pub enum CallInfo { } impl CallInfo { + /// Returns true if call is a message delivery call (with optional finality calls). + fn is_receive_messages_proof_call(&self) -> bool { + match self.messages_call_info() { + MessagesCallInfo::ReceiveMessagesProof(_) => true, + MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => false, + } + } + /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. fn submit_finality_proof_info(&self) -> Option> { match *self { @@ -185,6 +194,17 @@ impl CallInfo { } } +/// The actions on relayer account that need to be performed because of his actions. +#[derive(RuntimeDebug, PartialEq)] +enum RelayerAccountAction { + /// Do nothing with relayer account. + None, + /// Reward the relayer. + Reward(AccountId, RewardsAccountParams, Reward), + /// Slash the relayer. + Slash(AccountId, RewardsAccountParams), +} + /// Signed extension that refunds a relayer for new messages coming from a parachain. /// /// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) @@ -205,7 +225,25 @@ impl CallInfo { )] #[scale_info(skip_type_params(Runtime, Para, Msgs, Refund, Priority, Id))] pub struct RefundBridgedParachainMessages( - PhantomData<(Runtime, Para, Msgs, Refund, Priority, Id)>, + PhantomData<( + // runtime with `frame-utility`, `pallet-bridge-grandpa`, `pallet-bridge-parachains`, + // `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed + Runtime, + // implementation of `RefundableParachainId` trait, which specifies the instance of + // the used `pallet-bridge-parachains` pallet and the bridged parachain id + Para, + // implementation of `RefundableMessagesLaneId` trait, which specifies the instance of + // the used `pallet-bridge-messages` pallet and the lane within this pallet + Msgs, + // implementation of the `RefundCalculator` trait, that is used to compute refund that + // we give to relayer for his transaction + Refund, + // getter for per-message `TransactionPriority` boost that we give to message + // delivery transactions + Priority, + // the runtime-unique identifier of this signed extension + Id, + )>, ); impl @@ -215,9 +253,13 @@ where Runtime: UtilityConfig> + BoundedBridgeGrandpaConfig + ParachainsConfig - + MessagesConfig, + + MessagesConfig + + RelayersConfig, Para: RefundableParachainId, Msgs: RefundableMessagesLaneId, + Refund: RefundCalculator, + Priority: Get, + Id: StaticStrProvider, CallOf: Dispatchable + IsSubType, Runtime>> + GrandpaCallSubType @@ -268,118 +310,69 @@ where call.check_obsolete_call()?; Ok(call) } -} - -impl SignedExtension - for RefundBridgedParachainMessages -where - Self: 'static + Send + Sync, - Runtime: UtilityConfig> - + BoundedBridgeGrandpaConfig - + ParachainsConfig - + MessagesConfig - + RelayersConfig, - Para: RefundableParachainId, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + IsSubType, Runtime>> - + GrandpaCallSubType - + ParachainsCallSubType - + MessagesCallSubType, -{ - const IDENTIFIER: &'static str = Id::STR; - type AccountId = Runtime::AccountId; - type Call = CallOf; - type AdditionalSigned = (); - type Pre = Option>; - - fn additional_signed(&self) -> Result<(), TransactionValidityError> { - Ok(()) - } - fn validate( - &self, - _who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> TransactionValidity { - // this is the only relevant line of code for the `pre_dispatch` - // - // we're not calling `validato` from `pre_dispatch` directly because of performance - // reasons, so if you're adding some code that may fail here, please check if it needs - // to be added to the `pre_dispatch` as well - let parsed_call = self.parse_and_check_for_obsolete_call(call)?; + /// Given post-dispatch information, analyze the outcome of relayer call and return + /// actions that need to be performed on relayer account. + fn analyze_call_result( + pre: Option>>, + info: &DispatchInfo, + post_info: &PostDispatchInfo, + len: usize, + result: &DispatchResult, + ) -> RelayerAccountAction, Runtime::Reward> { + let mut extra_weight = Weight::zero(); + let mut extra_size = 0; - // the following code just plays with transaction priority and never returns an error - let mut valid_transaction = ValidTransactionBuilder::default(); - if let Some(parsed_call) = parsed_call { - // we give delivery transactions some boost, that depends on number of messages inside - let messages_call_info = parsed_call.messages_call_info(); - if let MessagesCallInfo::ReceiveMessagesProof(info) = messages_call_info { - // compute total number of messages in transaction - let bundled_messages = info.base.bundled_range.checked_len().unwrap_or(0); - - // a quick check to avoid invalid high-priority transactions - if bundled_messages <= Runtime::MaxUnconfirmedMessagesAtInboundLane::get() { - let priority_boost = crate::priority_calculator::compute_priority_boost::< - Priority, - >(bundled_messages); - valid_transaction = valid_transaction.priority(priority_boost); - } - } - } + // We don't refund anything for transactions that we don't support. + let (relayer, call_info) = match pre { + Some(Some(pre)) => (pre.relayer, pre.call_info), + _ => return RelayerAccountAction::None, + }; - valid_transaction.build() - } + // now we know that the relayer either needs to be rewarded, or slashed + // => let's prepare the correspondent account that pays reward/receives slashed amount + let reward_account_params = RewardsAccountParams::new( + Msgs::Id::get(), + Runtime::BridgedChainId::get(), + if call_info.is_receive_messages_proof_call() { + RewardsAccountOwner::ThisChain + } else { + RewardsAccountOwner::BridgedChain + }, + ); - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result { - // this is a relevant piece of `validate` that we need here (in `pre_dispatch`) - let parsed_call = self.parse_and_check_for_obsolete_call(call)?; + // prepare return value for the case if the call has failed or it has not caused + // expected side effects (e.g. not all messages have been accepted) + // + // we are not checking if relayer is registered here - it happens during the slash attempt + // + // there are couple of edge cases here: + // + // - when the relayer becomes registered during message dispatch: this is unlikely + relayer + // should be ready for slashing after registration; + // + // - when relayer is registered after `validate` is called and priority is not boosted: + // relayer should be ready for slashing after registration. + let may_slash_relayer = + Self::bundled_messages_for_priority_boost(Some(&call_info)).is_some(); + let slash_relayer_if_delivery_result = may_slash_relayer + .then(|| RelayerAccountAction::Slash(relayer.clone(), reward_account_params)) + .unwrap_or(RelayerAccountAction::None); - Ok(parsed_call.map(|call_info| { + // We don't refund anything if the transaction has failed. + if let Err(e) = result { log::trace!( target: "runtime::bridge", - "{} from parachain {} via {:?} parsed bridge transaction in pre-dispatch: {:?}", + "{} from parachain {} via {:?}: relayer {:?} has submitted invalid messages transaction: {:?}", Self::IDENTIFIER, Para::Id::get(), Msgs::Id::get(), - call_info, + relayer, + e, ); - PreDispatchData { relayer: who.clone(), call_info } - })) - } - - fn post_dispatch( - pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, - len: usize, - result: &DispatchResult, - ) -> Result<(), TransactionValidityError> { - let mut extra_weight = Weight::zero(); - let mut extra_size = 0; - - // We don't refund anything if the transaction has failed. - if result.is_err() { - return Ok(()) + return slash_relayer_if_delivery_result } - // We don't refund anything for transactions that we don't support. - let (relayer, call_info) = match pre { - Some(Some(pre)) => (pre.relayer, pre.call_info), - _ => return Ok(()), - }; - // check if relay chain state has been updated if let Some(finality_proof_info) = call_info.submit_finality_proof_info() { if !SubmitFinalityProofHelper::::was_successful( @@ -388,15 +381,13 @@ where // we only refund relayer if all calls have updated chain state log::trace!( target: "runtime::bridge", - "{} from parachain {} via {:?}: failed to refund relayer {:?}, because \ - relay chain finality proof has not been accepted", + "{} from parachain {} via {:?}: relayer {:?} has submitted invalid relay chain finality proof", Self::IDENTIFIER, Para::Id::get(), Msgs::Id::get(), relayer, ); - - return Ok(()) + return slash_relayer_if_delivery_result; } // there's a conflict between how bridge GRANDPA pallet works and a `utility.batchAll` @@ -420,33 +411,29 @@ where // we only refund relayer if all calls have updated chain state log::trace!( target: "runtime::bridge", - "{} from parachain {} via {:?}: failed to refund relayer {:?}, because \ - parachain finality proof has not been accepted", + "{} from parachain {} via {:?}: relayer {:?} has submitted invalid parachain finality proof", Self::IDENTIFIER, Para::Id::get(), Msgs::Id::get(), relayer, ); - - return Ok(()) + return slash_relayer_if_delivery_result } } - // Check if the `ReceiveMessagesProof` call delivered all the messages that + // Check if the `ReceiveMessagesProof` call delivered at least some of the messages that // it contained. If this happens, we consider the transaction "helpful" and refund it. let msgs_call_info = call_info.messages_call_info(); if !MessagesCallHelper::::was_successful(msgs_call_info) { log::trace!( target: "runtime::bridge", - "{} from parachain {} via {:?}: failed to refund relayer {:?}, because \ - some of messages have not been accepted", + "{} from parachain {} via {:?}: relayer {:?} has submitted invalid messages call", Self::IDENTIFIER, Para::Id::get(), Msgs::Id::get(), relayer, ); - - return Ok(()) + return slash_relayer_if_delivery_result } // regarding the tip - refund that happens here (at this side of the bridge) isn't the whole @@ -465,31 +452,172 @@ where // compute the relayer refund let refund = Refund::compute_refund(info, &post_info, post_info_len, tip); - // finally - register refund in relayers pallet - let rewards_account_owner = match msgs_call_info { - MessagesCallInfo::ReceiveMessagesProof(_) => RewardsAccountOwner::ThisChain, - MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => RewardsAccountOwner::BridgedChain, + // we can finally reward relayer + RelayerAccountAction::Reward(relayer, reward_account_params, refund) + } + + /// Returns number of bundled messages `Some(_)`, if the given call info is a: + /// + /// - message delivery transaction; + /// + /// - with reasonable bundled messages that may be accepted by the messages pallet. + /// + /// This function is used to check whether the transaction priority should be + /// virtually boosted. The relayer registration (we only boost priority for registered + /// relayer transactions) must be checked outside. + fn bundled_messages_for_priority_boost(call_info: Option<&CallInfo>) -> Option { + // we only boost priority of message delivery transactions + let parsed_call = match call_info { + Some(parsed_call) if parsed_call.is_receive_messages_proof_call() => parsed_call, + _ => return None, }; - RelayersPallet::::register_relayer_reward( - RewardsAccountParams::new( - Msgs::Id::get(), - Runtime::BridgedChainId::get(), - rewards_account_owner, - ), - &relayer, - refund, - ); + + // compute total number of messages in transaction + let bundled_messages = + parsed_call.messages_call_info().bundled_messages().checked_len().unwrap_or(0); + + // a quick check to avoid invalid high-priority transactions + if bundled_messages > Runtime::MaxUnconfirmedMessagesAtInboundLane::get() { + return None + } + + Some(bundled_messages) + } +} + +impl SignedExtension + for RefundBridgedParachainMessages +where + Self: 'static + Send + Sync, + Runtime: UtilityConfig> + + BoundedBridgeGrandpaConfig + + ParachainsConfig + + MessagesConfig + + RelayersConfig, + Para: RefundableParachainId, + Msgs: RefundableMessagesLaneId, + Refund: RefundCalculator, + Priority: Get, + Id: StaticStrProvider, + CallOf: Dispatchable + + IsSubType, Runtime>> + + GrandpaCallSubType + + ParachainsCallSubType + + MessagesCallSubType, +{ + const IDENTIFIER: &'static str = Id::STR; + type AccountId = Runtime::AccountId; + type Call = CallOf; + type AdditionalSigned = (); + type Pre = Option>; + + fn additional_signed(&self) -> Result<(), TransactionValidityError> { + Ok(()) + } + + fn validate( + &self, + who: &Self::AccountId, + call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> TransactionValidity { + // this is the only relevant line of code for the `pre_dispatch` + // + // we're not calling `validate` from `pre_dispatch` directly because of performance + // reasons, so if you're adding some code that may fail here, please check if it needs + // to be added to the `pre_dispatch` as well + let parsed_call = self.parse_and_check_for_obsolete_call(call)?; + + // the following code just plays with transaction priority and never returns an error + + // we only boost priority of presumably correct message delivery transactions + let bundled_messages = match Self::bundled_messages_for_priority_boost(parsed_call.as_ref()) + { + Some(bundled_messages) => bundled_messages, + None => return Ok(Default::default()), + }; + + // we only boost priority if relayer has staked required balance + if !RelayersPallet::::is_registration_active(who) { + return Ok(Default::default()) + } + + // compute priority boost + let priority_boost = + crate::priority_calculator::compute_priority_boost::(bundled_messages); + let valid_transaction = ValidTransactionBuilder::default().priority(priority_boost); log::trace!( target: "runtime::bridge", - "{} from parachain {} via {:?} has registered reward: {:?} for {:?}", + "{} from parachain {} via {:?} has boosted priority of message delivery transaction \ + of relayer {:?}: {} messages -> {} priority", Self::IDENTIFIER, Para::Id::get(), Msgs::Id::get(), - refund, - relayer, + who, + bundled_messages, + priority_boost, ); + valid_transaction.build() + } + + fn pre_dispatch( + self, + who: &Self::AccountId, + call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + // this is a relevant piece of `validate` that we need here (in `pre_dispatch`) + let parsed_call = self.parse_and_check_for_obsolete_call(call)?; + + Ok(parsed_call.map(|call_info| { + log::trace!( + target: "runtime::bridge", + "{} from parachain {} via {:?} parsed bridge transaction in pre-dispatch: {:?}", + Self::IDENTIFIER, + Para::Id::get(), + Msgs::Id::get(), + call_info, + ); + PreDispatchData { relayer: who.clone(), call_info } + })) + } + + fn post_dispatch( + pre: Option, + info: &DispatchInfoOf, + post_info: &PostDispatchInfoOf, + len: usize, + result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + let call_result = Self::analyze_call_result(pre, info, post_info, len, result); + + match call_result { + RelayerAccountAction::None => (), + RelayerAccountAction::Reward(relayer, reward_account, reward) => { + RelayersPallet::::register_relayer_reward( + reward_account, + &relayer, + reward, + ); + + log::trace!( + target: "runtime::bridge", + "{} from parachain {} via {:?} has registered reward: {:?} for {:?}", + Self::IDENTIFIER, + Para::Id::get(), + Msgs::Id::get(), + reward, + relayer, + ); + }, + RelayerAccountAction::Slash(relayer, slash_account) => + RelayersPallet::::slash_and_deregister(&relayer, slash_account), + } + Ok(()) } } @@ -509,10 +637,14 @@ mod tests { }; use bp_messages::{InboundLaneData, MessageNonce, OutboundLaneData, UnrewardedRelayersState}; use bp_parachains::{BestParaHeadHash, ParaInfo}; - use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; + use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId}; use bp_runtime::HeaderId; use bp_test_utils::{make_default_justification, test_keyring}; - use frame_support::{assert_storage_noop, parameter_types, weights::Weight}; + use frame_support::{ + assert_storage_noop, parameter_types, + traits::{fungible::Mutate, ReservableCurrency}, + weights::Weight, + }; use pallet_bridge_grandpa::{Call as GrandpaCall, StoredAuthoritySet}; use pallet_bridge_messages::Call as MessagesCall; use pallet_bridge_parachains::{Call as ParachainsCall, RelayBlockHash}; @@ -547,6 +679,22 @@ mod tests { StrTestExtension, >; + fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance { + let test_stake: ThisChainBalance = TestStake::get(); + ExistentialDeposit::get().saturating_add(test_stake * 100) + } + + // in tests, the following accounts are equal (because of how `into_sub_account_truncating` + // works) + + fn delivery_rewards_account() -> ThisChainAccountId { + TestPaymentProcedure::rewards_account(MsgProofsRewardsAccount::get()) + } + + fn confirmation_rewards_account() -> ThisChainAccountId { + TestPaymentProcedure::rewards_account(MsgDeliveryProofsRewardsAccount::get()) + } + fn relayer_account_at_this_chain() -> ThisChainAccountId { 0 } @@ -558,7 +706,6 @@ mod tests { fn initialize_environment( best_relay_header_number: RelayBlockNumber, parachain_head_at_relay_header_number: RelayBlockNumber, - parachain_head_hash: ParaHash, best_message: MessageNonce, ) { let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect(); @@ -572,7 +719,7 @@ mod tests { let para_info = ParaInfo { best_head_hash: BestParaHeadHash { at_relay_block_number: parachain_head_at_relay_header_number, - head_hash: parachain_head_hash, + head_hash: [parachain_head_at_relay_header_number as u8; 32].into(), }, next_imported_hash_position: 0, }; @@ -586,6 +733,14 @@ mod tests { let out_lane_data = OutboundLaneData { latest_received_nonce: best_message, ..Default::default() }; pallet_bridge_messages::OutboundLanes::::insert(lane_id, out_lane_data); + + Balances::mint_into(&delivery_rewards_account(), ExistentialDeposit::get()).unwrap(); + Balances::mint_into(&confirmation_rewards_account(), ExistentialDeposit::get()).unwrap(); + Balances::mint_into( + &relayer_account_at_this_chain(), + initial_balance_of_relayer_account_at_this_chain(), + ) + .unwrap(); } fn submit_relay_header_call(relay_header_number: RelayBlockNumber) -> RuntimeCall { @@ -609,7 +764,10 @@ mod tests { ) -> RuntimeCall { RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()), - parachains: vec![(ParaId(TestParachain::get()), [1u8; 32].into())], + parachains: vec![( + ParaId(TestParachain::get()), + [parachain_head_at_relay_header_number as u8; 32].into(), + )], parachain_heads_proof: ParaHeadsProof(vec![]), }) } @@ -711,7 +869,7 @@ mod tests { SubmitParachainHeadsInfo { at_relay_block_number: 200, para_id: ParaId(TestParachain::get()), - para_head_hash: [1u8; 32].into(), + para_head_hash: [200u8; 32].into(), }, MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { @@ -740,7 +898,7 @@ mod tests { SubmitParachainHeadsInfo { at_relay_block_number: 200, para_id: ParaId(TestParachain::get()), - para_head_hash: [1u8; 32].into(), + para_head_hash: [200u8; 32].into(), }, MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( BaseMessagesProofInfo { @@ -760,7 +918,7 @@ mod tests { SubmitParachainHeadsInfo { at_relay_block_number: 200, para_id: ParaId(TestParachain::get()), - para_head_hash: [1u8; 32].into(), + para_head_hash: [200u8; 32].into(), }, MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { @@ -784,7 +942,7 @@ mod tests { SubmitParachainHeadsInfo { at_relay_block_number: 200, para_id: ParaId(TestParachain::get()), - para_head_hash: [1u8; 32].into(), + para_head_hash: [200u8; 32].into(), }, MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( BaseMessagesProofInfo { @@ -829,8 +987,21 @@ mod tests { } } - fn run_test(test: impl FnOnce()) { - sp_io::TestExternalities::new(Default::default()).execute_with(test) + fn set_bundled_range_end( + mut pre_dispatch_data: PreDispatchData, + end: MessageNonce, + ) -> PreDispatchData { + let msg_info = match pre_dispatch_data.call_info { + CallInfo::AllFinalityAndMsgs(_, _, ref mut info) => info, + CallInfo::ParachainFinalityAndMsgs(_, ref mut info) => info, + CallInfo::Msgs(ref mut info) => info, + }; + + if let MessagesCallInfo::ReceiveMessagesProof(ref mut msg_info) = msg_info { + msg_info.base.bundled_range = *msg_info.base.bundled_range.start()..=end + } + + pre_dispatch_data } fn run_validate(call: RuntimeCall) -> TransactionValidity { @@ -838,6 +1009,13 @@ mod tests { extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } + fn run_validate_ignore_priority(call: RuntimeCall) -> TransactionValidity { + run_validate(call).map(|mut tx| { + tx.priority = 0; + tx + }) + } + fn run_pre_dispatch( call: RuntimeCall, ) -> Result>, TransactionValidityError> { @@ -883,10 +1061,49 @@ mod tests { ) } + #[test] + fn validate_doesnt_boost_transaction_priority_if_relayer_is_not_registered() { + run_test(|| { + initialize_environment(100, 100, 100); + Balances::set_balance(&relayer_account_at_this_chain(), ExistentialDeposit::get()); + + // message delivery is failing + assert_eq!(run_validate(message_delivery_call(200)), Ok(Default::default()),); + assert_eq!( + run_validate(parachain_finality_and_delivery_batch_call(200, 200)), + Ok(Default::default()), + ); + assert_eq!( + run_validate(all_finality_and_delivery_batch_call(200, 200, 200)), + Ok(Default::default()), + ); + // message confirmation validation is passing + assert_eq!( + run_validate_ignore_priority(message_confirmation_call(200)), + Ok(Default::default()), + ); + assert_eq!( + run_validate_ignore_priority(parachain_finality_and_confirmation_batch_call( + 200, 200 + )), + Ok(Default::default()), + ); + assert_eq!( + run_validate_ignore_priority(all_finality_and_confirmation_batch_call( + 200, 200, 200 + )), + Ok(Default::default()), + ); + }); + } + #[test] fn validate_boosts_priority_of_message_delivery_transactons() { run_test(|| { - initialize_environment(100, 100, Default::default(), 100); + initialize_environment(100, 100, 100); + + BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) + .unwrap(); let priority_of_100_messages_delivery = run_validate(message_delivery_call(200)).unwrap().priority; @@ -913,7 +1130,10 @@ mod tests { #[test] fn validate_does_not_boost_priority_of_message_delivery_transactons_with_too_many_messages() { run_test(|| { - initialize_environment(100, 100, Default::default(), 100); + initialize_environment(100, 100, 100); + + BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) + .unwrap(); let priority_of_max_messages_delivery = run_validate(message_delivery_call( 100 + MaxUnconfirmedMessagesAtInboundLane::get(), @@ -938,14 +1158,7 @@ mod tests { #[test] fn validate_allows_non_obsolete_transactions() { run_test(|| { - initialize_environment(100, 100, Default::default(), 100); - - fn run_validate_ignore_priority(call: RuntimeCall) -> TransactionValidity { - run_validate(call).map(|mut tx| { - tx.priority = 0; - tx - }) - } + initialize_environment(100, 100, 100); assert_eq!( run_validate_ignore_priority(message_delivery_call(200)), @@ -983,7 +1196,7 @@ mod tests { #[test] fn ext_rejects_batch_with_obsolete_relay_chain_header() { run_test(|| { - initialize_environment(100, 100, Default::default(), 100); + initialize_environment(100, 100, 100); assert_eq!( run_pre_dispatch(all_finality_and_delivery_batch_call(100, 200, 200)), @@ -1000,7 +1213,7 @@ mod tests { #[test] fn ext_rejects_batch_with_obsolete_parachain_head() { run_test(|| { - initialize_environment(100, 100, Default::default(), 100); + initialize_environment(100, 100, 100); assert_eq!( run_pre_dispatch(all_finality_and_delivery_batch_call(101, 100, 200)), @@ -1025,7 +1238,7 @@ mod tests { #[test] fn ext_rejects_batch_with_obsolete_messages() { run_test(|| { - initialize_environment(100, 100, Default::default(), 100); + initialize_environment(100, 100, 100); assert_eq!( run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 100)), @@ -1068,7 +1281,7 @@ mod tests { #[test] fn pre_dispatch_parses_batch_with_relay_chain_and_parachain_headers() { run_test(|| { - initialize_environment(100, 100, Default::default(), 100); + initialize_environment(100, 100, 100); assert_eq!( run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), @@ -1084,7 +1297,7 @@ mod tests { #[test] fn pre_dispatch_parses_batch_with_parachain_header() { run_test(|| { - initialize_environment(100, 100, Default::default(), 100); + initialize_environment(100, 100, 100); assert_eq!( run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), @@ -1100,7 +1313,7 @@ mod tests { #[test] fn pre_dispatch_fails_to_parse_batch_with_multiple_parachain_headers() { run_test(|| { - initialize_environment(100, 100, Default::default(), 100); + initialize_environment(100, 100, 100); let call = RuntimeCall::Utility(UtilityCall::batch_all { calls: vec![ @@ -1123,7 +1336,7 @@ mod tests { #[test] fn pre_dispatch_parses_message_transaction() { run_test(|| { - initialize_environment(100, 100, Default::default(), 100); + initialize_environment(100, 100, 100); assert_eq!( run_pre_dispatch(message_delivery_call(200)), @@ -1156,7 +1369,7 @@ mod tests { #[test] fn post_dispatch_ignores_transaction_that_has_not_updated_relay_chain_state() { run_test(|| { - initialize_environment(100, 200, Default::default(), 200); + initialize_environment(100, 200, 200); assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); }); @@ -1165,7 +1378,7 @@ mod tests { #[test] fn post_dispatch_ignores_transaction_that_has_not_updated_parachain_state() { run_test(|| { - initialize_environment(200, 100, Default::default(), 200); + initialize_environment(200, 100, 200); assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); assert_storage_noop!(run_post_dispatch( @@ -1178,7 +1391,7 @@ mod tests { #[test] fn post_dispatch_ignores_transaction_that_has_not_delivered_any_messages() { run_test(|| { - initialize_environment(200, 200, Default::default(), 100); + initialize_environment(200, 200, 100); assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); assert_storage_noop!(run_post_dispatch( @@ -1202,7 +1415,7 @@ mod tests { #[test] fn post_dispatch_ignores_transaction_that_has_not_delivered_all_messages() { run_test(|| { - initialize_environment(200, 200, Default::default(), 150); + initialize_environment(200, 200, 150); assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); assert_storage_noop!(run_post_dispatch( @@ -1226,7 +1439,7 @@ mod tests { #[test] fn post_dispatch_refunds_relayer_in_all_finality_batch_with_extra_weight() { run_test(|| { - initialize_environment(200, 200, [1u8; 32].into(), 200); + initialize_environment(200, 200, 200); let mut dispatch_info = dispatch_info(); dispatch_info.weight = Weight::from_parts( @@ -1275,7 +1488,7 @@ mod tests { #[test] fn post_dispatch_refunds_relayer_in_all_finality_batch() { run_test(|| { - initialize_environment(200, 200, [1u8; 32].into(), 200); + initialize_environment(200, 200, 200); run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(())); assert_eq!( @@ -1300,7 +1513,7 @@ mod tests { #[test] fn post_dispatch_refunds_relayer_in_parachain_finality_batch() { run_test(|| { - initialize_environment(200, 200, [1u8; 32].into(), 200); + initialize_environment(200, 200, 200); run_post_dispatch(Some(parachain_finality_pre_dispatch_data()), Ok(())); assert_eq!( @@ -1325,7 +1538,7 @@ mod tests { #[test] fn post_dispatch_refunds_relayer_in_message_transaction() { run_test(|| { - initialize_environment(200, 200, Default::default(), 200); + initialize_environment(200, 200, 200); run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(())); assert_eq!( @@ -1346,4 +1559,149 @@ mod tests { ); }); } + + #[test] + fn post_dispatch_slashing_relayer_stake() { + run_test(|| { + initialize_environment(200, 200, 100); + + let delivery_rewards_account_balance = + Balances::free_balance(delivery_rewards_account()); + + let test_stake: ThisChainBalance = TestStake::get(); + Balances::set_balance( + &relayer_account_at_this_chain(), + ExistentialDeposit::get() + test_stake * 10, + ); + + // slashing works for message delivery calls + BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) + .unwrap(); + assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); + run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(())); + assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), 0); + assert_eq!( + delivery_rewards_account_balance + test_stake, + Balances::free_balance(delivery_rewards_account()) + ); + + BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) + .unwrap(); + assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); + run_post_dispatch(Some(parachain_finality_pre_dispatch_data()), Ok(())); + assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), 0); + assert_eq!( + delivery_rewards_account_balance + test_stake * 2, + Balances::free_balance(delivery_rewards_account()) + ); + + BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) + .unwrap(); + assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); + run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(())); + assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), 0); + assert_eq!( + delivery_rewards_account_balance + test_stake * 3, + Balances::free_balance(delivery_rewards_account()) + ); + + // reserve doesn't work for message confirmation calls + let confirmation_rewards_account_balance = + Balances::free_balance(confirmation_rewards_account()); + + Balances::reserve(&relayer_account_at_this_chain(), test_stake).unwrap(); + assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); + + assert_eq!( + confirmation_rewards_account_balance, + Balances::free_balance(confirmation_rewards_account()) + ); + run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(())); + assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); + + run_post_dispatch(Some(parachain_finality_confirmation_pre_dispatch_data()), Ok(())); + assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); + + run_post_dispatch(Some(all_finality_confirmation_pre_dispatch_data()), Ok(())); + assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); + + // check that unreserve has happened, not slashing + assert_eq!( + delivery_rewards_account_balance + test_stake * 3, + Balances::free_balance(delivery_rewards_account()) + ); + assert_eq!( + confirmation_rewards_account_balance, + Balances::free_balance(confirmation_rewards_account()) + ); + }); + } + + fn run_analyze_call_result( + pre_dispatch_data: PreDispatchData, + dispatch_result: DispatchResult, + ) -> RelayerAccountAction { + TestExtension::analyze_call_result( + Some(Some(pre_dispatch_data)), + &dispatch_info(), + &post_dispatch_info(), + 1024, + &dispatch_result, + ) + } + + #[test] + fn analyze_call_result_shall_not_slash_for_transactions_with_too_many_messages() { + run_test(|| { + initialize_environment(100, 100, 100); + + // the `analyze_call_result` should return slash if number of bundled messages is + // within reasonable limits + assert_eq!( + run_analyze_call_result(all_finality_pre_dispatch_data(), Ok(())), + RelayerAccountAction::Slash( + relayer_account_at_this_chain(), + MsgProofsRewardsAccount::get() + ), + ); + assert_eq!( + run_analyze_call_result(parachain_finality_pre_dispatch_data(), Ok(())), + RelayerAccountAction::Slash( + relayer_account_at_this_chain(), + MsgProofsRewardsAccount::get() + ), + ); + assert_eq!( + run_analyze_call_result(delivery_pre_dispatch_data(), Ok(())), + RelayerAccountAction::Slash( + relayer_account_at_this_chain(), + MsgProofsRewardsAccount::get() + ), + ); + + // the `analyze_call_result` should not return slash if number of bundled messages is + // larger than the + assert_eq!( + run_analyze_call_result( + set_bundled_range_end(all_finality_pre_dispatch_data(), 1_000_000), + Ok(()) + ), + RelayerAccountAction::None, + ); + assert_eq!( + run_analyze_call_result( + set_bundled_range_end(parachain_finality_pre_dispatch_data(), 1_000_000), + Ok(()) + ), + RelayerAccountAction::None, + ); + assert_eq!( + run_analyze_call_result( + set_bundled_range_end(delivery_pre_dispatch_data(), 1_000_000), + Ok(()) + ), + RelayerAccountAction::None, + ); + }); + } } diff --git a/deployments/bridges/rialto-millau/docker-compose.yml b/deployments/bridges/rialto-millau/docker-compose.yml index 5d15a0398b1..7ce33da67c3 100644 --- a/deployments/bridges/rialto-millau/docker-compose.yml +++ b/deployments/bridges/rialto-millau/docker-compose.yml @@ -40,6 +40,7 @@ services: - rialto-node-charlie - rialto-node-dave - rialto-node-eve + - rialto-node-ferdie relay-messages-millau-to-rialto-generator: <<: *sub-bridge-relay diff --git a/deployments/bridges/rococo-wococo/dashboard/grafana/bridges-alerts.json b/deployments/bridges/rococo-wococo/dashboard/grafana/bridges-alerts.json index 6432f79deaf..51cea87cac9 100644 --- a/deployments/bridges/rococo-wococo/dashboard/grafana/bridges-alerts.json +++ b/deployments/bridges/rococo-wococo/dashboard/grafana/bridges-alerts.json @@ -1,1994 +1,2255 @@ { - "Bridges": [ - { - "name": "Bridges", - "interval": "1m", - "rules": [ - { - "expr": "", - "for": "10m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "tkgc6_bnk", - "__panelId__": "12", - "summary": "Messages from RococoBridgeHub to WococoBridgeHub (00000001) are either not delivered, or are delivered with lags" - }, - "grafana_alert": { - "id": 51, - "orgId": 1, - "title": "RococoBridgeHub -> WococoBridgeHub delivery lags (00000001)", - "condition": "B", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", - "interval": "", - "intervalMs": 30000, - "legendFormat": "Undelivered messages", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + "Bridges":[ + { + "name":"BEEFY Lagging Sessions", + "interval":"20m", + "rules":[ + { + "expr":"", + "for":"20m", + "labels":{ + "matrix_room":"!lMunCqbBqxEqfRuUDF:parity.io" }, - { - "refId": "B", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "annotations":{ + "__dashboardUid__":"3sEDRyl7z", + "__panelId__":"6", + "summary":"Some BEEFY validators experienced lagging sessions" + }, + "grafana_alert":{ + "id":42, + "orgId":1, + "title":"BEEFY Lagging Sessions", + "condition":"B", + "data":[ + { + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":10800, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "editorMode":"code", + "exemplar":true, + "expr":"increase(substrate_beefy_lagging_sessions{chain=\"rococo_v2_2\", node=~\"rococo.*(3-validator|3-rpc).*\"}[60m])", + "interval":"", + "intervalMs":30000, + "legendFormat":"", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, { - "evaluator": { - "params": [ - 0 - ], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "A" - ] - }, - "reducer": { - "params": [], - "type": "min" - }, - "type": "query" + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":0, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 0 + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "A" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"grafana-expression", + "uid":"-100" + }, + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"B", + "type":"classic_conditions" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "B", - "type": "classic_conditions" - } + ], + "updated":"2023-02-14T12:24:20Z", + "intervalSeconds":1200, + "version":9, + "uid":"eYY8ks_7z", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"BEEFY Lagging Sessions", + "no_data_state":"NoData", + "exec_err_state":"Alerting" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 90, - "uid": "r41otJp4k", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "10m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "zqjpgXxnk", - "__panelId__": "14", - "summary": "Messages from WococoBridgeHub to RococoBridgeHub (00000001) are either not delivered, or are delivered with lags" - }, - "grafana_alert": { - "id": 55, - "orgId": 1, - "title": "WococoBridgeHub -> RococoBridgeHub delivery lags (00000001)", - "condition": "B", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 300, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", - "interval": "", - "intervalMs": 30000, - "legendFormat": "Undelivered messages", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + } + ] + }, + { + "name":"Bridges", + "interval":"1m", + "rules":[ + { + "expr":"", + "for":"10m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" }, - { - "refId": "B", - "queryType": "", - "relativeTimeRange": { - "from": 300, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "annotations":{ + "__dashboardUid__":"tkgc6_bnk", + "__panelId__":"12", + "summary":"Messages from RococoBridgeHub to WococoBridgeHub (00000001) are either not delivered, or are delivered with lags" + }, + "grafana_alert":{ + "id":51, + "orgId":1, + "title":"RococoBridgeHub -\u003e WococoBridgeHub delivery lags (00000001)", + "condition":"A", + "data":[ + { + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":600, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"((vector(0) and ((BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"} \u003e on () BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}))) or vector(1)) + on () increase(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[10m]) * on () ((vector(1) and ((BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"} \u003e on () BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}))) or vector(0))", + "interval":"", + "intervalMs":30000, + "legendFormat":"Undelivered messages", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, { - "evaluator": { - "params": [ - 0 - ], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "A" - ] - }, - "reducer": { - "params": [], - "type": "min" - }, - "type": "query" + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":600, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 1 + ], + "type":"lt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "A" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"max" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"B", + "type":"classic_conditions" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "B", - "type": "classic_conditions" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":93, + "uid":"r41otJp4k", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 88, - "uid": "wqmPtJpVz", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "10m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "tkgc6_bnk", - "__panelId__": "14", - "summary": "Messages from RococoBridgeHub to WococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" - }, - "grafana_alert": { - "id": 56, - "orgId": 1, - "title": "RococoBridgeHub -> WococoBridgeHub confirmation lags (00000001)", - "condition": "B", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", - "interval": "", - "intervalMs": 30000, - "legendFormat": "Unconfirmed messages", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + }, + { + "expr":"", + "for":"10m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" }, - { - "refId": "B", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "annotations":{ + "__dashboardUid__":"zqjpgXxnk", + "__panelId__":"14", + "summary":"Messages from WococoBridgeHub to RococoBridgeHub (00000001) are either not delivered, or are delivered with lags" + }, + "grafana_alert":{ + "id":55, + "orgId":1, + "title":"WococoBridgeHub -\u003e RococoBridgeHub delivery lags (00000001)", + "condition":"B", + "data":[ + { + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":300, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"((vector(0) and ((BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"} \u003e on () BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}))) or vector(1)) + on () increase(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[10m]) * on () ((vector(1) and ((BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"} \u003e on () BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}))) or vector(0))", + "interval":"", + "intervalMs":30000, + "legendFormat":"Undelivered messages", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, { - "evaluator": { - "params": [ - 50 - ], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "A" - ] - }, - "reducer": { - "params": [], - "type": "min" - }, - "type": "query" + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":300, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 1 + ], + "type":"lt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "A" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"max" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"B", + "type":"classic_conditions" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "B", - "type": "classic_conditions" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":91, + "uid":"wqmPtJpVz", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 84, - "uid": "z4h3pJtVz", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "10m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "zqjpgXxnk", - "__panelId__": "16", - "summary": "Messages from WococoBridgeHub to RococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" - }, - "grafana_alert": { - "id": 57, - "orgId": 1, - "title": "WococoBridgeHub -> RococoBridgeHub confirmation lags (00000001)", - "condition": "B", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 300, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", - "interval": "", - "intervalMs": 30000, - "legendFormat": "Unconfirmed messages", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + }, + { + "expr":"", + "for":"10m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" + }, + "annotations":{ + "__dashboardUid__":"tkgc6_bnk", + "__panelId__":"14", + "summary":"Messages from RococoBridgeHub to WococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" }, - { - "refId": "B", - "queryType": "", - "relativeTimeRange": { - "from": 300, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "grafana_alert":{ + "id":56, + "orgId":1, + "title":"RococoBridgeHub -\u003e WococoBridgeHub confirmation lags (00000001)", + "condition":"B", + "data":[ { - "evaluator": { - "params": [ - 50 - ], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "A" - ] - }, - "reducer": { - "params": [], - "type": "min" - }, - "type": "query" + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval":"", + "intervalMs":30000, + "legendFormat":"Unconfirmed messages", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, + { + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 50 + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "A" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"min" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"B", + "type":"classic_conditions" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "B", - "type": "classic_conditions" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":87, + "uid":"z4h3pJtVz", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 83, - "uid": "Kj_z21t4k", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "10m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "zqjpgXxnk", - "__panelId__": "18", - "summary": "Rewards for messages from WococoBridgeHub to RococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" - }, - "grafana_alert": { - "id": 58, - "orgId": 1, - "title": "WococoBridgeHub -> RococoBridgeHub reward lags (00000001)", - "condition": "C", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 300, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", - "interval": "", - "intervalMs": 30000, - "legendFormat": "Unconfirmed rewards", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + }, + { + "expr":"", + "for":"10m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" }, - { - "refId": "B", - "queryType": "", - "relativeTimeRange": { - "from": 300, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "(scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", - "hide": true, - "interval": "", - "intervalMs": 30000, - "legendFormat": "__auto", - "maxDataPoints": 43200, - "range": true, - "refId": "B" - } + "annotations":{ + "__dashboardUid__":"zqjpgXxnk", + "__panelId__":"16", + "summary":"Messages from WococoBridgeHub to RococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" }, - { - "refId": "C", - "queryType": "", - "relativeTimeRange": { - "from": 300, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "grafana_alert":{ + "id":57, + "orgId":1, + "title":"WococoBridgeHub -\u003e RococoBridgeHub confirmation lags (00000001)", + "condition":"B", + "data":[ + { + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":300, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval":"", + "intervalMs":30000, + "legendFormat":"Unconfirmed messages", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, { - "evaluator": { - "params": [ - 10 - ], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "B" - ] - }, - "reducer": { - "params": [], - "type": "min" - }, - "type": "query" + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":300, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 50 + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "A" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"min" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"B", + "type":"classic_conditions" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "C", - "type": "classic_conditions" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":86, + "uid":"Kj_z21t4k", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 78, - "uid": "hw_a21pVk", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "10m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "tkgc6_bnk", - "__panelId__": "15", - "summary": "Rewards for messages from RococoBridgeHub to WococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" - }, - "grafana_alert": { - "id": 59, - "orgId": 1, - "title": "RococoBridgeHub -> WococoBridgeHub reward lags (00000001)", - "condition": "C", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", - "interval": "", - "intervalMs": 30000, - "legendFormat": "Unconfirmed rewards", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + }, + { + "expr":"", + "for":"10m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" }, - { - "refId": "B", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "(scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", - "hide": true, - "interval": "", - "intervalMs": 30000, - "legendFormat": "__auto", - "maxDataPoints": 43200, - "range": true, - "refId": "B" - } + "annotations":{ + "__dashboardUid__":"zqjpgXxnk", + "__panelId__":"18", + "summary":"Rewards for messages from WococoBridgeHub to RococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" }, - { - "refId": "C", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "grafana_alert":{ + "id":58, + "orgId":1, + "title":"WococoBridgeHub -\u003e RococoBridgeHub reward lags (00000001)", + "condition":"C", + "data":[ + { + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":300, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval":"", + "intervalMs":30000, + "legendFormat":"Unconfirmed rewards", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, { - "evaluator": { - "params": [ - 10 - ], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "A" - ] - }, - "reducer": { - "params": [], - "type": "min" - }, - "type": "query" + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":300, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"(scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) \u003e bool min_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "hide":true, + "interval":"", + "intervalMs":30000, + "legendFormat":"__auto", + "maxDataPoints":43200, + "range":true, + "refId":"B" + } + }, + { + "refId":"C", + "queryType":"", + "relativeTimeRange":{ + "from":300, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 10 + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "B" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"min" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"C", + "type":"classic_conditions" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "C", - "type": "classic_conditions" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":81, + "uid":"hw_a21pVk", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 77, - "uid": "daN62Jt4z", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "10m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "UFsgbJtVz", - "__panelId__": "2", - "summary": "Best BridgeHubRococo header at BridgeHubWococo (00000001) doesn't match the same header at BridgeHubRococo" - }, - "grafana_alert": { - "id": 60, - "orgId": 1, - "title": "BridgeHubRococo header mismatch (00000001)", - "condition": "B", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", - "interval": "", - "intervalMs": 30000, - "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + }, + { + "expr":"", + "for":"10m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" + }, + "annotations":{ + "__dashboardUid__":"tkgc6_bnk", + "__panelId__":"15", + "summary":"Rewards for messages from RococoBridgeHub to WococoBridgeHub (00000001) are either not confirmed, or are confirmed with lags" }, - { - "refId": "B", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "grafana_alert":{ + "id":59, + "orgId":1, + "title":"RococoBridgeHub -\u003e WococoBridgeHub reward lags (00000001)", + "condition":"C", + "data":[ + { + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", + "interval":"", + "intervalMs":30000, + "legendFormat":"Unconfirmed rewards", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, { - "evaluator": { - "params": [ - 0 - ], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "A" - ] - }, - "reducer": { - "params": [], - "type": "max" - }, - "type": "query" + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"(scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) \u003e bool min_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "hide":true, + "interval":"", + "intervalMs":30000, + "legendFormat":"__auto", + "maxDataPoints":43200, + "range":true, + "refId":"B" + } + }, + { + "refId":"C", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 10 + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "A" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"min" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"C", + "type":"classic_conditions" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "B", - "type": "classic_conditions" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":80, + "uid":"daN62Jt4z", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 74, - "uid": "BzBDb1pVz", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "10m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "UFsgbJtVz", - "__panelId__": "3", - "summary": "Best BridgeHubWococo header at BridgeHubRococo (00000001) doesn't match the same header at BridgeHubWococo" - }, - "grafana_alert": { - "id": 61, - "orgId": 1, - "title": "BridgeHubWococo header mismatch (00000001)", - "condition": "B", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", - "interval": "", - "intervalMs": 30000, - "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + }, + { + "expr":"", + "for":"10m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" + }, + "annotations":{ + "__dashboardUid__":"UFsgbJtVz", + "__panelId__":"2", + "summary":"Best BridgeHubRococo header at BridgeHubWococo (00000001) doesn't match the same header at BridgeHubRococo" }, - { - "refId": "B", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "grafana_alert":{ + "id":60, + "orgId":1, + "title":"BridgeHubRococo header mismatch (00000001)", + "condition":"B", + "data":[ { - "evaluator": { - "params": [ - 0 - ], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "A" - ] - }, - "reducer": { - "params": [], - "type": "max" - }, - "type": "query" + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "interval":"", + "intervalMs":30000, + "legendFormat":"Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, + { + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 0 + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "A" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"max" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"B", + "type":"classic_conditions" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "B", - "type": "classic_conditions" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":77, + "uid":"BzBDb1pVz", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 73, - "uid": "1W6lb1p4z", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "10m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "UFsgbJtVz", - "__panelId__": "5", - "summary": "With-WococoBridgeHub messages relay balance at RococoBridgeHub (00000001) is too low" - }, - "grafana_alert": { - "id": 62, - "orgId": 1, - "title": "With-WococoBridgeHub messages relay balance at RococoBridgeHub (00000001) is too low", - "condition": "B", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 300, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "last_over_time(at_BridgeHubRococo_relay_BridgeHubWococoMessages_balance{domain=\"parity-testnet\"}[1h])", - "interval": "", - "intervalMs": 30000, - "legendFormat": "Messages Relay Balance", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + }, + { + "expr":"", + "for":"10m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" + }, + "annotations":{ + "__dashboardUid__":"UFsgbJtVz", + "__panelId__":"3", + "summary":"Best BridgeHubWococo header at BridgeHubRococo (00000001) doesn't match the same header at BridgeHubWococo" }, - { - "refId": "B", - "queryType": "", - "relativeTimeRange": { - "from": 300, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "grafana_alert":{ + "id":61, + "orgId":1, + "title":"BridgeHubWococo header mismatch (00000001)", + "condition":"B", + "data":[ { - "evaluator": { - "params": [ - 10 - ], - "type": "lt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "A" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "interval":"", + "intervalMs":30000, + "legendFormat":"Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, + { + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 0 + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "A" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"max" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"B", + "type":"classic_conditions" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "B", - "type": "classic_conditions" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":76, + "uid":"1W6lb1p4z", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 72, - "uid": "Y5Dm-1tVz", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "10m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "UFsgbJtVz", - "__panelId__": "6", - "summary": "With-RococoBridgeHub messages relay balance at WococoBridgeHub (00000001) is too low" - }, - "grafana_alert": { - "id": 63, - "orgId": 1, - "title": "With-RococoBridgeHub messages relay balance at WococoBridgeHub (00000001) is too low", - "condition": "B", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 300, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "last_over_time(at_BridgeHubWococo_relay_BridgeHubRococoMessages_balance{domain=\"parity-testnet\"}[1h])", - "interval": "", - "intervalMs": 30000, - "legendFormat": "Messages Relay Balance", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + }, + { + "expr":"", + "for":"10m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" + }, + "annotations":{ + "__dashboardUid__":"UFsgbJtVz", + "__panelId__":"5", + "summary":"With-WococoBridgeHub messages relay balance at RococoBridgeHub (00000001) is too low" }, - { - "refId": "B", - "queryType": "", - "relativeTimeRange": { - "from": 300, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "grafana_alert":{ + "id":62, + "orgId":1, + "title":"With-WococoBridgeHub messages relay balance at RococoBridgeHub (00000001) is too low", + "condition":"B", + "data":[ + { + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":300, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"last_over_time(at_BridgeHubRococo_relay_BridgeHubWococoMessages_balance{domain=\"parity-testnet\"}[1h])", + "interval":"", + "intervalMs":30000, + "legendFormat":"Messages Relay Balance", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, { - "evaluator": { - "params": [ - 10 - ], - "type": "lt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "A" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":300, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 10 + ], + "type":"lt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "A" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"B", + "type":"classic_conditions" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "B", - "type": "classic_conditions" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":75, + "uid":"Y5Dm-1tVz", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 71, - "uid": "yUl4a1tVz", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "5m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "tkgc6_bnk", - "__panelId__": "6", - "summary": "Less than 500 Rococo headers have been synced to WococoBridgeHub in last 120 minutes. Relay is not running?" - }, - "grafana_alert": { - "id": 65, - "orgId": 1, - "title": "Rococo -> WococoBridgeHub finality sync lags (00000001)", - "condition": "D", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "max(increase(Rococo_to_BridgeHubWococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}[120m]))", - "interval": "", - "intervalMs": 30000, - "legendFormat": "At Rococo", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + }, + { + "expr":"", + "for":"10m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" }, - { - "refId": "C", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ - { - "evaluator": { - "params": [], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "C" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" - } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "reducer": "last", - "refId": "C", - "type": "reduce" - } + "annotations":{ + "__dashboardUid__":"UFsgbJtVz", + "__panelId__":"6", + "summary":"With-RococoBridgeHub messages relay balance at WococoBridgeHub (00000001) is too low" }, - { - "refId": "D", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "grafana_alert":{ + "id":63, + "orgId":1, + "title":"With-RococoBridgeHub messages relay balance at WococoBridgeHub (00000001) is too low", + "condition":"B", + "data":[ + { + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":300, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"last_over_time(at_BridgeHubWococo_relay_BridgeHubRococoMessages_balance{domain=\"parity-testnet\"}[1h])", + "interval":"", + "intervalMs":30000, + "legendFormat":"Messages Relay Balance", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, { - "evaluator": { - "params": [ - 500 - ], - "type": "lt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "D" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":300, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 10 + ], + "type":"lt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "A" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"B", + "type":"classic_conditions" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "C", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "D", - "type": "threshold" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":74, + "uid":"yUl4a1tVz", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 40, - "uid": "R6GKwNA4z", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "5m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "zqjpgXxnk", - "__panelId__": "2", - "summary": "Less than 500 Wococo headers have been synced to RococoBridgeHub in last 120 minutes. Relay is not running?" - }, - "grafana_alert": { - "id": 66, - "orgId": 1, - "title": "Wococo -> RococoBridgeHub finality sync lags (00000001)", - "condition": "D", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "max(increase(Wococo_to_BridgeHubRococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}[120m]))", - "interval": "", - "intervalMs": 30000, - "legendFormat": "At Wococo", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + }, + { + "expr":"", + "for":"5m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" }, - { - "refId": "C", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ - { - "evaluator": { - "params": [], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "C" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" - } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "reducer": "last", - "refId": "C", - "type": "reduce" - } + "annotations":{ + "__dashboardUid__":"tkgc6_bnk", + "__panelId__":"6", + "summary":"Less than 500 Rococo headers have been synced to WococoBridgeHub in last 120 minutes. Relay is not running?" }, - { - "refId": "D", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "grafana_alert":{ + "id":65, + "orgId":1, + "title":"Rococo -\u003e WococoBridgeHub finality sync lags (00000001)", + "condition":"D", + "data":[ + { + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"max(increase(Rococo_to_BridgeHubWococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}[120m]))", + "interval":"", + "intervalMs":30000, + "legendFormat":"At Rococo", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, { - "evaluator": { - "params": [ - 500 - ], - "type": "lt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "D" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" + "refId":"C", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "C" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "reducer":"last", + "refId":"C", + "type":"reduce" + } + }, + { + "refId":"D", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 500 + ], + "type":"lt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "D" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"C", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"D", + "type":"threshold" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "C", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "D", - "type": "threshold" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":43, + "uid":"R6GKwNA4z", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 38, - "uid": "rAM1QHAVk", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "0s", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "UFsgbJtVz", - "__panelId__": "11", - "summary": "The RococoBridgeHub <> WococoBridgeHub relay (00000001) has been aborted by version guard - i.e. one of chains has been upgraded and relay wasn't redeployed" - }, - "grafana_alert": { - "id": 67, - "orgId": 1, - "title": "Version guard has aborted RococoBridgeHub <> WococoBridgeHub relay (00000001)", - "condition": "B", - "data": [ - { - "refId": "B", - "queryType": "range", - "relativeTimeRange": { - "from": 600, - "to": 0 - }, - "datasourceUid": "P03E52D76DFE188C3", - "model": { - "datasource": { - "type": "loki", - "uid": "P03E52D76DFE188C3" - }, - "editorMode": "code", - "expr": "count_over_time({container=\"bridges-common-relay\"} |= `Aborting relay` [1m])", - "hide": false, - "intervalMs": 1000, - "legendFormat": "Aborts per minute", - "maxDataPoints": 43200, - "queryType": "range", - "refId": "B" - } + }, + { + "expr":"", + "for":"5m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" }, - { - "refId": "C", - "queryType": "", - "relativeTimeRange": { - "from": 600, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ - { - "evaluator": { - "params": [], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "C" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" - } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "B", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "reducer": "max", - "refId": "C", - "type": "reduce" - } + "annotations":{ + "__dashboardUid__":"zqjpgXxnk", + "__panelId__":"2", + "summary":"Less than 500 Wococo headers have been synced to RococoBridgeHub in last 120 minutes. Relay is not running?" }, - { - "refId": "D", - "queryType": "", - "relativeTimeRange": { - "from": 600, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "grafana_alert":{ + "id":66, + "orgId":1, + "title":"Wococo -\u003e RococoBridgeHub finality sync lags (00000001)", + "condition":"D", + "data":[ + { + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"max(increase(Wococo_to_BridgeHubRococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}[120m]))", + "interval":"", + "intervalMs":30000, + "legendFormat":"At Wococo", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, { - "evaluator": { - "params": [ - 0 - ], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "D" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" + "refId":"C", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "C" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "reducer":"last", + "refId":"C", + "type":"reduce" + } + }, + { + "refId":"D", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 500 + ], + "type":"lt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "D" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"C", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"D", + "type":"threshold" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "C", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "D", - "type": "threshold" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":41, + "uid":"rAM1QHAVk", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 37, - "uid": "TwWPeN04z", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "10m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "UFsgbJtVz", - "__panelId__": "12", - "summary": "Best Rococo header at BridgeHubWococo (00000001) doesn't match the same header at Rococo" - }, - "grafana_alert": { - "id": 69, - "orgId": 1, - "title": "Rococo headers mismatch", - "condition": "C", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "Rococo_to_BridgeHubWococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", - "interval": "", - "intervalMs": 30000, - "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + }, + { + "expr":"", + "for":"0s", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" }, - { - "refId": "B", - "queryType": "", - "relativeTimeRange": { - "from": 0, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ - { - "evaluator": { - "params": [], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "B" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" - } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "reducer": "last", - "refId": "B", - "type": "reduce" - } + "annotations":{ + "__dashboardUid__":"UFsgbJtVz", + "__panelId__":"11", + "summary":"The RococoBridgeHub \u003c\u003e WococoBridgeHub relay (00000001) has been aborted by version guard - i.e. one of chains has been upgraded and relay wasn't redeployed" }, - { - "refId": "C", - "queryType": "", - "relativeTimeRange": { - "from": 0, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "grafana_alert":{ + "id":67, + "orgId":1, + "title":"Version guard has aborted RococoBridgeHub \u003c\u003e WococoBridgeHub relay (00000001)", + "condition":"B", + "data":[ { - "evaluator": { - "params": [ - 0 - ], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "C" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" + "refId":"B", + "queryType":"range", + "relativeTimeRange":{ + "from":600, + "to":0 + }, + "datasourceUid":"P03E52D76DFE188C3", + "model":{ + "datasource":{ + "type":"loki", + "uid":"P03E52D76DFE188C3" + }, + "editorMode":"code", + "expr":"count_over_time({container=\"bridges-common-relay\"} |= `Aborting relay` [1m])", + "hide":false, + "intervalMs":1000, + "legendFormat":"Aborts per minute", + "maxDataPoints":43200, + "queryType":"range", + "refId":"B" + } + }, + { + "refId":"C", + "queryType":"", + "relativeTimeRange":{ + "from":600, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "C" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"B", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "reducer":"max", + "refId":"C", + "type":"reduce" + } + }, + { + "refId":"D", + "queryType":"", + "relativeTimeRange":{ + "from":600, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 0 + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "D" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"C", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"D", + "type":"threshold" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "B", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "C", - "type": "threshold" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":40, + "uid":"TwWPeN04z", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 32, - "uid": "08-5gv04k", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "10m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "UFsgbJtVz", - "__panelId__": "13", - "summary": "Best Wococo header at BridgeHubRococo (00000001) doesn't match the same header at Wococo" - }, - "grafana_alert": { - "id": 70, - "orgId": 1, - "title": "Wococo headers mismatch", - "condition": "C", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "Wococo_to_BridgeHubRococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", - "interval": "", - "intervalMs": 30000, - "legendFormat": "Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + }, + { + "expr":"", + "for":"10m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" }, - { - "refId": "B", - "queryType": "", - "relativeTimeRange": { - "from": 0, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ - { - "evaluator": { - "params": [], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "B" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" - } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "reducer": "last", - "refId": "B", - "type": "reduce" - } + "annotations":{ + "__dashboardUid__":"UFsgbJtVz", + "__panelId__":"12", + "summary":"Best Rococo header at BridgeHubWococo (00000001) doesn't match the same header at Rococo" }, - { - "refId": "C", - "queryType": "", - "relativeTimeRange": { - "from": 0, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "grafana_alert":{ + "id":69, + "orgId":1, + "title":"Rococo headers mismatch", + "condition":"C", + "data":[ + { + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"Rococo_to_BridgeHubWococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "interval":"", + "intervalMs":30000, + "legendFormat":"Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, + { + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":0, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "B" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "reducer":"last", + "refId":"B", + "type":"reduce" + } + }, { - "evaluator": { - "params": [ - 0 - ], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "C" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" + "refId":"C", + "queryType":"", + "relativeTimeRange":{ + "from":0, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 0 + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "C" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"B", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"C", + "type":"threshold" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "B", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "C", - "type": "threshold" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":35, + "uid":"08-5gv04k", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 31, - "uid": "Esj2gD0Vk", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "5m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "tkgc6_bnk", - "__panelId__": "9", - "summary": "Test messages from RococoBridgeHub to WococoBridgeHub are not generated. Our cronjob has died?" - }, - "grafana_alert": { - "id": 73, - "orgId": 1, - "title": "Test messages from RococoBridgeHub to WococoBridgeHub are not generated.", - "condition": "D", - "data": [ - { - "refId": "B", - "queryType": "", - "relativeTimeRange": { - "from": 21600, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "increase(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\", type=~\"source_latest_generated\"}[24h])", - "hide": true, - "interval": "", - "intervalMs": 30000, - "legendFormat": "Messages generated in last 24h", - "maxDataPoints": 43200, - "range": true, - "refId": "B" - } + }, + { + "expr":"", + "for":"10m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" }, - { - "refId": "C", - "queryType": "", - "relativeTimeRange": { - "from": 600, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "annotations":{ + "__dashboardUid__":"UFsgbJtVz", + "__panelId__":"13", + "summary":"Best Wococo header at BridgeHubRococo (00000001) doesn't match the same header at Wococo" + }, + "grafana_alert":{ + "id":70, + "orgId":1, + "title":"Wococo headers mismatch", + "condition":"C", + "data":[ + { + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"Wococo_to_BridgeHubRococo_Sync_is_source_and_source_at_target_using_different_forks{domain=\"parity-testnet\"}", + "interval":"", + "intervalMs":30000, + "legendFormat":"Best BridgeHubRococo header at BridgeHubWococo doesn't match the same header of BridgeHubRococo", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, { - "evaluator": { - "params": [], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "C" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":0, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "B" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "reducer":"last", + "refId":"B", + "type":"reduce" + } + }, + { + "refId":"C", + "queryType":"", + "relativeTimeRange":{ + "from":0, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 0 + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "C" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"B", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"C", + "type":"threshold" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "B", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "reducer": "max", - "refId": "C", - "type": "reduce" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":34, + "uid":"Esj2gD0Vk", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" + } + }, + { + "expr":"", + "for":"5m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" }, - { - "refId": "D", - "queryType": "", - "relativeTimeRange": { - "from": 600, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "annotations":{ + "__dashboardUid__":"tkgc6_bnk", + "__panelId__":"9", + "summary":"Test messages from RococoBridgeHub to WococoBridgeHub are not generated. Our cronjob has died?" + }, + "grafana_alert":{ + "id":73, + "orgId":1, + "title":"Test messages from RococoBridgeHub to WococoBridgeHub are not generated.", + "condition":"D", + "data":[ + { + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":21600, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "expr":"increase(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\", type=~\"source_latest_generated\"}[24h])", + "hide":true, + "interval":"", + "intervalMs":30000, + "legendFormat":"Messages generated in last 24h", + "maxDataPoints":43200, + "range":true, + "refId":"B" + } + }, + { + "refId":"C", + "queryType":"", + "relativeTimeRange":{ + "from":600, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "C" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"B", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "reducer":"max", + "refId":"C", + "type":"reduce" + } + }, { - "evaluator": { - "params": [ - 1 - ], - "type": "lt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "D" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" + "refId":"D", + "queryType":"", + "relativeTimeRange":{ + "from":600, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 1 + ], + "type":"lt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "D" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"C", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"D", + "type":"threshold" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "C", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "D", - "type": "threshold" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":6, + "uid":"ry1K5SB4k", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 3, - "uid": "ry1K5SB4k", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - }, - { - "expr": "", - "for": "5m", - "labels": { - "matrix_room": "XyVkmRJgIkjcvIBPsP" - }, - "annotations": { - "__dashboardUid__": "UFsgbJtVz", - "__panelId__": "16", - "summary": "RococoBridgeHub <> WococoBridgeHub relay (00000001) node is down" - }, - "grafana_alert": { - "id": 74, - "orgId": 1, - "title": "RococoBridgeHub <> WococoBridgeHub relay (00000001) node is down", - "condition": "C", - "data": [ - { - "refId": "A", - "queryType": "", - "relativeTimeRange": { - "from": 900, - "to": 0 - }, - "datasourceUid": "PC96415006F908B67", - "model": { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "exemplar": false, - "expr": "up{domain=\"parity-testnet\",container=\"bridges-common-relay\"}", - "instant": false, - "interval": "", - "intervalMs": 30000, - "legendFormat": "Is relay running", - "maxDataPoints": 43200, - "range": true, - "refId": "A" - } + }, + { + "expr":"", + "for":"5m", + "labels":{ + "matrix_room":"XyVkmRJgIkjcvIBPsP" }, - { - "refId": "B", - "queryType": "", - "relativeTimeRange": { - "from": 0, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "annotations":{ + "__dashboardUid__":"UFsgbJtVz", + "__panelId__":"16", + "summary":"RococoBridgeHub \u003c\u003e WococoBridgeHub relay (00000001) node is down" + }, + "grafana_alert":{ + "id":74, + "orgId":1, + "title":"RococoBridgeHub \u003c\u003e WococoBridgeHub relay (00000001) node is down", + "condition":"C", + "data":[ + { + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":900, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "datasource":{ + "type":"prometheus", + "uid":"PC96415006F908B67" + }, + "editorMode":"code", + "exemplar":false, + "expr":"up{domain=\"parity-testnet\",container=\"bridges-common-relay\"}", + "instant":false, + "interval":"", + "intervalMs":30000, + "legendFormat":"Is relay running", + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, + { + "refId":"B", + "queryType":"", + "relativeTimeRange":{ + "from":0, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + + ], + "type":"gt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "B" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"A", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "reducer":"max", + "refId":"B", + "type":"reduce" + } + }, { - "evaluator": { - "params": [], - "type": "gt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "B" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" + "refId":"C", + "queryType":"", + "relativeTimeRange":{ + "from":0, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 1 + ], + "type":"lt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "C" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"__expr__", + "uid":"-100" + }, + "expression":"B", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"C", + "type":"threshold" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "A", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "reducer": "max", - "refId": "B", - "type": "reduce" - } + ], + "updated":"2023-04-18T09:14:37Z", + "intervalSeconds":60, + "version":4, + "uid":"9YAdEUB4z", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Bridges", + "no_data_state":"OK", + "exec_err_state":"OK" + } + } + ] + }, + { + "name":"Rococo Beefy best blocks not advancing", + "interval":"20m", + "rules":[ + { + "expr":"", + "for":"1h", + "labels":{ + "matrix_room":"!lMunCqbBqxEqfRuUDF:parity.io" + }, + "annotations":{ + "__dashboardUid__":"3sEDRyl7z", + "__panelId__":"2", + "summary":"Rococo BEEFY best blocks have not advanced for at least 60 mins" }, - { - "refId": "C", - "queryType": "", - "relativeTimeRange": { - "from": 0, - "to": 0 - }, - "datasourceUid": "-100", - "model": { - "conditions": [ + "grafana_alert":{ + "id":41, + "orgId":1, + "title":"Rococo BEEFY best blocks not advancing", + "condition":"C", + "data":[ + { + "refId":"A", + "queryType":"", + "relativeTimeRange":{ + "from":10800, + "to":0 + }, + "datasourceUid":"PC96415006F908B67", + "model":{ + "editorMode":"code", + "expr":"increase(substrate_beefy_best_block{chain=\"rococo_v2_2\", node=~\"rococo.*(3-validator|3-rpc).*\"}[1h])", + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "range":true, + "refId":"A" + } + }, { - "evaluator": { - "params": [ - 1 - ], - "type": "lt" - }, - "operator": { - "type": "and" - }, - "query": { - "params": [ - "C" - ] - }, - "reducer": { - "params": [], - "type": "last" - }, - "type": "query" + "refId":"C", + "queryType":"", + "relativeTimeRange":{ + "from":0, + "to":0 + }, + "datasourceUid":"-100", + "model":{ + "conditions":[ + { + "evaluator":{ + "params":[ + 100 + ], + "type":"lt" + }, + "operator":{ + "type":"and" + }, + "query":{ + "params":[ + "A" + ] + }, + "reducer":{ + "params":[ + + ], + "type":"last" + }, + "type":"query" + } + ], + "datasource":{ + "type":"grafana-expression", + "uid":"-100" + }, + "hide":false, + "intervalMs":1000, + "maxDataPoints":43200, + "refId":"C", + "type":"classic_conditions" + } } - ], - "datasource": { - "type": "__expr__", - "uid": "-100" - }, - "expression": "B", - "hide": false, - "intervalMs": 1000, - "maxDataPoints": 43200, - "refId": "C", - "type": "threshold" - } + ], + "updated":"2023-02-14T12:26:10Z", + "intervalSeconds":1200, + "version":10, + "uid":"CBuugs_7k", + "namespace_uid":"eblDiw17z", + "namespace_id":140, + "rule_group":"Rococo Beefy best blocks not advancing", + "no_data_state":"Alerting", + "exec_err_state":"Alerting" } - ], - "updated": "2023-03-29T05:39:46Z", - "intervalSeconds": 60, - "version": 1, - "uid": "9YAdEUB4z", - "namespace_uid": "eblDiw17z", - "namespace_id": 140, - "rule_group": "Bridges", - "no_data_state": "OK", - "exec_err_state": "OK" - } - } - ] - } + } + ] + } ] - } + } + \ No newline at end of file diff --git a/deployments/bridges/rococo-wococo/dashboard/grafana/relay-rococo-to-wococo-messages-dashboard.json b/deployments/bridges/rococo-wococo/dashboard/grafana/relay-rococo-to-wococo-messages-dashboard.json index d1ecba2acfd..cf0fa1bff79 100644 --- a/deployments/bridges/rococo-wococo/dashboard/grafana/relay-rococo-to-wococo-messages-dashboard.json +++ b/deployments/bridges/rococo-wococo/dashboard/grafana/relay-rococo-to-wococo-messages-dashboard.json @@ -1,941 +1,965 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 141, - "links": [], - "liveNow": false, - "panels": [ + "annotations": { + "list": [ { + "builtIn": 1, "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 141, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 0 - }, - "id": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", - "legendFormat": "At Rococo", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", - "hide": false, - "legendFormat": "At BridgeHubWococo", - "range": true, - "refId": "B" - } - ], - "title": "Best finalized Rococo headers", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", + "legendFormat": "At Rococo", + "range": true, + "refId": "A" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At BridgeHubWococo", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized Rococo headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 0 - }, - "id": 7, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", - "legendFormat": "At Wococo", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", - "hide": false, - "legendFormat": "At BridgeHubRococo", - "range": true, - "refId": "B" - } - ], - "title": "Best finalized Wococo headers", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", + "legendFormat": "At Wococo", + "range": true, + "refId": "A" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At BridgeHubRococo", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized Wococo headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 12, - "y": 0 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "exemplar": true, - "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_source_block_number{domain=\"parity-testnet\"}", - "interval": "", - "legendFormat": "At RococoBridgeHub", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_source_at_target_block_number{domain=\"parity-testnet\"}", - "hide": false, - "legendFormat": "At WococoBridgeHub", - "range": true, - "refId": "B" - } - ], - "title": "Best finalized RococoBridgeHub headers", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": true, + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_source_block_number{domain=\"parity-testnet\"}", + "interval": "", + "legendFormat": "At RococoBridgeHub", + "range": true, + "refId": "A" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized RococoBridgeHub headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 18, - "y": 0 - }, - "id": 4, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "exemplar": true, - "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_target_block_number{domain=\"parity-testnet\"}", - "interval": "", - "legendFormat": "At WococoBridgeHub", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_target_at_source_block_number{domain=\"parity-testnet\"}", - "hide": false, - "legendFormat": "At RococoBridgeHub", - "range": true, - "refId": "B" - } - ], - "title": "Best finalized WococoBridgeHub headers", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": true, + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_target_block_number{domain=\"parity-testnet\"}", + "interval": "", + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "A" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_best_target_at_source_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At RococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized WococoBridgeHub headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 8 - }, - "id": 9, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "label_replace(label_replace(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\", type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from BridgeHubRococo\", \"type\", \"source_latest_generated\"), \"type\", \"Latest BridgeHubRococo message received by BridgeHubWococo\", \"type\", \"target_latest_received\")", - "legendFormat": "{{type}}", - "range": true, - "refId": "A" - } - ], - "title": "Delivery race (00000001)", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "label_replace(label_replace(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\", type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from BridgeHubRococo\", \"type\", \"source_latest_generated\"), \"type\", \"Latest BridgeHubRococo message received by BridgeHubWococo\", \"type\", \"target_latest_received\")", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "increase(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\", type=~\"source_latest_generated\"}[24h])", + "hide": true, + "legendFormat": "Messages generated in last 24h", + "range": true, + "refId": "B" + } + ], + "title": "Delivery race (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 8 - }, - "id": 10, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "label_replace(label_replace(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest delivery confirmation from BridgeHubWococo to BridgeHubRococo\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest BridgeHubRococo message received by BridgeHubWococo\", \"type\", \"target_latest_received\")", - "legendFormat": "{{type}}", - "range": true, - "refId": "A" - } - ], - "title": "Confirmations race (00000001)", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "label_replace(label_replace(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest delivery confirmation from BridgeHubWococo to BridgeHubRococo\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest BridgeHubRococo message received by BridgeHubWococo\", \"type\", \"target_latest_received\")", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + } + ], + "title": "Confirmations race (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 0, - "y": 16 - }, - "id": 12, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", - "legendFormat": "Undelivered messages", - "range": true, - "refId": "A" - } - ], - "title": "Delivery race lags (00000001)", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 16 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "legendFormat": "Undelivered messages", + "range": true, + "refId": "A" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "((vector(0) and ((BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"} > on () BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}))) or vector(1)) + on () increase(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[10m]) * on () ((vector(1) and ((BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"} > on () BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}))) or vector(0))", + "hide": true, + "legendFormat": "1 if all messages are delivered. Otherwise - number of delivered messages in last 10m", + "range": true, + "refId": "B" + } + ], + "title": "Delivery race lags (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 8, - "y": 16 - }, - "id": 14, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", - "legendFormat": "Unconfirmed messages", - "range": true, - "refId": "A" - } - ], - "title": "Confirmations race lags (00000001)", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 16 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(RococoBridgeHub_to_WococoBridgeHub_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "legendFormat": "Unconfirmed messages", + "range": true, + "refId": "A" + } + ], + "title": "Confirmations race lags (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 16, - "y": 16 - }, - "id": 15, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", - "legendFormat": "Unconfirmed rewards", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "(scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", - "hide": true, - "legendFormat": "__auto", - "range": true, - "refId": "B" - } - ], - "title": "Reward lags (00000001)", - "type": "timeseries" - } - ], - "refresh": "5s", - "schemaVersion": 37, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "BridgeHubRococo to BridgeHubWococo (00000001)", - "uid": "tkgc6_bnk", - "version": 42, - "weekStart": "" - } \ No newline at end of file + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 16 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", + "legendFormat": "Unconfirmed rewards", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "(scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubRococo_to_BridgeHubWococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "hide": true, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "Reward lags (00000001)", + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BridgeHubRococo to BridgeHubWococo (00000001)", + "uid": "tkgc6_bnk", + "version": 44, + "weekStart": "" +} diff --git a/deployments/bridges/rococo-wococo/dashboard/grafana/relay-wococo-to-rococo-messages-dashboard.json b/deployments/bridges/rococo-wococo/dashboard/grafana/relay-wococo-to-rococo-messages-dashboard.json index be33f0dcecc..15967adcb34 100644 --- a/deployments/bridges/rococo-wococo/dashboard/grafana/relay-wococo-to-rococo-messages-dashboard.json +++ b/deployments/bridges/rococo-wococo/dashboard/grafana/relay-wococo-to-rococo-messages-dashboard.json @@ -1,941 +1,953 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 142, - "links": [], - "liveNow": false, - "panels": [ + "annotations": { + "list": [ { + "builtIn": 1, "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 142, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 0 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", - "legendFormat": "At Wococo", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", - "hide": false, - "legendFormat": "At BridgeHubRococo", - "range": true, - "refId": "B" - } - ], - "title": "Best finalized Wococo headers", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", + "legendFormat": "At Wococo", + "range": true, + "refId": "A" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Wococo_to_BridgeHubRococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At BridgeHubRococo", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized Wococo headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 0 - }, - "id": 4, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", - "legendFormat": "At Rococo", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", - "hide": false, - "legendFormat": "At WococoBridgeHub", - "range": true, - "refId": "B" - } - ], - "title": "Best finalized Rococo headers", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_block_number{domain=\"parity-testnet\"}", + "legendFormat": "At Rococo", + "range": true, + "refId": "A" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "Rococo_to_BridgeHubWococo_Sync_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized Rococo headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 12, - "y": 0 - }, - "id": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "exemplar": true, - "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_source_block_number{domain=\"parity-testnet\"}", - "interval": "", - "legendFormat": "At WococoBridgeHub", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_source_at_target_block_number{domain=\"parity-testnet\"}", - "hide": false, - "legendFormat": "At RococoBridgeHub", - "range": true, - "refId": "B" - } - ], - "title": "Best finalized WococoBridgeHub headers", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 0 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": true, + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_source_block_number{domain=\"parity-testnet\"}", + "interval": "", + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "A" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_source_at_target_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At RococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized WococoBridgeHub headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 18, - "y": 0 - }, - "id": 8, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "exemplar": true, - "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_target_block_number{domain=\"parity-testnet\"}", - "interval": "", - "legendFormat": "At RococoBridgeHub", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_target_at_source_block_number{domain=\"parity-testnet\"}", - "hide": false, - "legendFormat": "At WococoBridgeHub", - "range": true, - "refId": "B" - } - ], - "title": "Best finalized RococoBridgeHub headers", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 0 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "exemplar": true, + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_target_block_number{domain=\"parity-testnet\"}", + "interval": "", + "legendFormat": "At RococoBridgeHub", + "range": true, + "refId": "A" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_best_target_at_source_block_number{domain=\"parity-testnet\"}", + "hide": false, + "legendFormat": "At WococoBridgeHub", + "range": true, + "refId": "B" + } + ], + "title": "Best finalized RococoBridgeHub headers", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 8 - }, - "id": 10, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "label_replace(label_replace(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from BridgeHubWococo\", \"type\", \"source_latest_generated\"), \"type\", \"Latest BridgeHubWococo message received by BridgeHubRococo\", \"type\", \"target_latest_received\")", - "legendFormat": "{{type}}", - "range": true, - "refId": "A" - } - ], - "title": "Delivery race (00000001)", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "label_replace(label_replace(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=~\"source_latest_generated|target_latest_received\"}, \"type\", \"Latest message sent from BridgeHubWococo\", \"type\", \"source_latest_generated\"), \"type\", \"Latest BridgeHubWococo message received by BridgeHubRococo\", \"type\", \"target_latest_received\")", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + } + ], + "title": "Delivery race (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 8 - }, - "id": 12, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "label_replace(label_replace(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest delivery confirmation from BridgeHubRococo to BridgeHubWococo\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest BridgeHubWococo message received by BridgeHubRococo\", \"type\", \"target_latest_received\")", - "legendFormat": "{{type}}", - "range": true, - "refId": "A" - } - ], - "title": "Confirmations race (00000001)", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "label_replace(label_replace(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=~\"source_latest_confirmed|target_latest_received\"}, \"type\", \"Latest delivery confirmation from BridgeHubRococo to BridgeHubWococo\", \"type\", \"source_latest_confirmed\"), \"type\", \"Latest BridgeHubWococo message received by BridgeHubRococo\", \"type\", \"target_latest_received\")", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + } + ], + "title": "Confirmations race (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 0, - "y": 16 - }, - "id": 14, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", - "legendFormat": "Undelivered messages", - "range": true, - "refId": "A" - } - ], - "title": "Delivery race lags (00000001)", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 16 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "legendFormat": "Undelivered messages", + "range": true, + "refId": "A" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "((vector(0) and ((BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"} > on () BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}))) or vector(1)) + on () increase(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[10m]) * on () ((vector(1) and ((BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_generated\"} > on () BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}))) or vector(0))", + "hide": true, + "legendFormat": "1 if all messages are delivered. Otherwise - number of delivered messages in last 10m", + "range": true, + "refId": "B" + } + ], + "title": "Delivery race lags (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 8, - "y": 16 - }, - "id": 16, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", - "legendFormat": "Unconfirmed messages", - "range": true, - "refId": "A" - } - ], - "title": "Confirmations race lags (00000001)", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 16 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0))", + "legendFormat": "Unconfirmed messages", + "range": true, + "refId": "A" + } + ], + "title": "Confirmations race lags (00000001)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 16, - "y": 16 - }, - "id": 18, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] } }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", - "legendFormat": "Unconfirmed rewards", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PC96415006F908B67" - }, - "editorMode": "code", - "expr": "(scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", - "hide": true, - "legendFormat": "__auto", - "range": true, - "refId": "B" - } - ], - "title": "Reward lags (00000001)", - "type": "timeseries" - } - ], - "refresh": "5s", - "schemaVersion": 37, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-5m", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "BridgeHubWococo to BridgeHubRococo (00000001)", - "uid": "zqjpgXxnk", - "version": 30, - "weekStart": "" - } \ No newline at end of file + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 16 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))", + "legendFormat": "Unconfirmed rewards", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PC96415006F908B67" + }, + "editorMode": "code", + "expr": "(scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"source_latest_confirmed\"}[2m]) OR on() vector(0)) - scalar(max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_confirmed\"}[2m]) OR on() vector(0))) * (max_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0) > bool min_over_time(BridgeHubWococo_to_BridgeHubRococo_MessageLane_00000001_lane_state_nonces{domain=\"parity-testnet\",type=\"target_latest_received\"}[2m]) OR on() vector(0))", + "hide": true, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "Reward lags (00000001)", + "type": "timeseries" + } + ], + "refresh": "5s", + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BridgeHubWococo to BridgeHubRococo (00000001)", + "uid": "zqjpgXxnk", + "version": 32, + "weekStart": "" +} diff --git a/deployments/monitoring/grafana-matrix/config.yml b/deployments/monitoring/grafana-matrix/config.yml index 123cf76a80b..662d7aa14d7 100644 --- a/deployments/monitoring/grafana-matrix/config.yml +++ b/deployments/monitoring/grafana-matrix/config.yml @@ -5,7 +5,7 @@ # Set up your HS connections matrix: - name: matrix-parity-io - url: https://matrix.parity.io + url: https://m.parity.io # Create a user - log that user in using a post request # curl -XPOST -d '{"type": "m.login.password", # "user":"grafana", @@ -22,7 +22,7 @@ msgtype: m.text # Set up notification ingress rules rules: - name: bridge # Name of the rule - room: "#bridges-rialto-millau-alerts:matrix.parity.io" # Room or ID + room: "#bridges-rialto-millau-alerts:parity.io" # Room or ID matrix: matrix-parity-io # The Matrix HS to use - defaults to first one msgtype: m.notice # The following values are optional: diff --git a/deployments/networks/dashboard/prometheus/rialto-targets.yml b/deployments/networks/dashboard/prometheus/rialto-targets.yml index 0c89926e8c3..7ec194f99d7 100644 --- a/deployments/networks/dashboard/prometheus/rialto-targets.yml +++ b/deployments/networks/dashboard/prometheus/rialto-targets.yml @@ -4,3 +4,4 @@ - rialto-node-charlie:9615 - rialto-node-dave:9615 - rialto-node-eve:9615 + - rialto-node-ferdie:9615 diff --git a/deployments/networks/rialto.yml b/deployments/networks/rialto.yml index fab85b89c04..ebd8393fcad 100644 --- a/deployments/networks/rialto.yml +++ b/deployments/networks/rialto.yml @@ -101,6 +101,24 @@ services: - "10344:9944" - "10315:9615" + rialto-node-ferdie: + <<: *rialto-bridge-node + entrypoint: + - /home/user/rialto-bridge-node + - --execution=Native + - --chain=local + - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE + - --ferdie + - --rpc-cors=all + - --enable-offchain-indexing=true + - --unsafe-rpc-external + - --unsafe-ws-external + - --prometheus-external + ports: + - "10433:9933" + - "10444:9944" + - "10415:9615" + rialto-chainspec-exporter: image: ${RIALTO_BRIDGE_NODE_IMAGE:-paritytech/rialto-bridge-node} entrypoint: /entrypoints/rialto-chainspec-exporter-entrypoint.sh @@ -118,6 +136,7 @@ services: - rialto-node-charlie - rialto-node-dave - rialto-node-eve + - rialto-node-ferdie # we're using `/rialto-share` to expose Rialto chain spec to those who are interested. Right # now it is Rialto Parachain collator nodes. Local + tmpfs combination allows sharing writable diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index 9d38c9723d7..329e4c21136 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -834,6 +834,7 @@ mod tests { run_test(|| { assert_eq!(BestFinalized::::get(), None,); assert_eq!(Pallet::::best_finalized(), None); + assert_eq!(PalletOperatingMode::::try_get(), Err(())); let init_data = init_with_origin(RuntimeOrigin::root()).unwrap(); @@ -843,7 +844,10 @@ mod tests { CurrentAuthoritySet::::get().authorities, init_data.authority_list ); - assert_eq!(PalletOperatingMode::::get(), BasicOperatingMode::Normal); + assert_eq!( + PalletOperatingMode::::try_get(), + Ok(BasicOperatingMode::Normal) + ); }) } diff --git a/modules/messages/src/inbound_lane.rs b/modules/messages/src/inbound_lane.rs index 59ff5667195..5ec4444dbdf 100644 --- a/modules/messages/src/inbound_lane.rs +++ b/modules/messages/src/inbound_lane.rs @@ -153,7 +153,7 @@ impl InboundLane { // Note: There will be max. 1 record to update as we don't allow messages from relayers to // overlap. match data.relayers.front_mut() { - Some(entry) if entry.messages.begin < new_confirmed_nonce => { + Some(entry) if entry.messages.begin <= new_confirmed_nonce => { entry.messages.begin = new_confirmed_nonce + 1; }, _ => {}, @@ -164,10 +164,9 @@ impl InboundLane { } /// Receive new message. - pub fn receive_message, AccountId>( + pub fn receive_message( &mut self, relayer_at_bridged_chain: &S::Relayer, - relayer_at_this_chain: &AccountId, nonce: MessageNonce, message_data: DispatchMessageData, ) -> ReceivalResult { @@ -189,13 +188,10 @@ impl InboundLane { } // then, dispatch message - let dispatch_result = Dispatch::dispatch( - relayer_at_this_chain, - DispatchMessage { - key: MessageKey { lane_id: self.storage.id(), nonce }, - data: message_data, - }, - ); + let dispatch_result = Dispatch::dispatch(DispatchMessage { + key: MessageKey { lane_id: self.storage.id(), nonce }, + data: message_data, + }); // now let's update inbound lane storage match data.relayers.back_mut() { @@ -221,20 +217,20 @@ mod tests { use crate::{ inbound_lane, mock::{ - dispatch_result, inbound_message_data, run_test, unrewarded_relayer, - TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, - TEST_RELAYER_B, TEST_RELAYER_C, + dispatch_result, inbound_message_data, inbound_unrewarded_relayers_state, run_test, + unrewarded_relayer, TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID, + TEST_RELAYER_A, TEST_RELAYER_B, TEST_RELAYER_C, }, RuntimeInboundLaneStorage, }; + use bp_messages::UnrewardedRelayersState; fn receive_regular_message( lane: &mut InboundLane>, nonce: MessageNonce, ) { assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, + lane.receive_message::( &TEST_RELAYER_A, nonce, inbound_message_data(REGULAR_PAYLOAD) @@ -361,8 +357,7 @@ mod tests { run_test(|| { let mut lane = inbound_lane::(TEST_LANE_ID); assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, + lane.receive_message::( &TEST_RELAYER_A, 10, inbound_message_data(REGULAR_PAYLOAD) @@ -381,8 +376,7 @@ mod tests { ::MaxUnrewardedRelayerEntriesAtInboundLane::get(); for current_nonce in 1..max_nonce + 1 { assert_eq!( - lane.receive_message::( - &(TEST_RELAYER_A + current_nonce), + lane.receive_message::( &(TEST_RELAYER_A + current_nonce), current_nonce, inbound_message_data(REGULAR_PAYLOAD) @@ -392,8 +386,7 @@ mod tests { } // Fails to dispatch new message from different than latest relayer. assert_eq!( - lane.receive_message::( - &(TEST_RELAYER_A + max_nonce + 1), + lane.receive_message::( &(TEST_RELAYER_A + max_nonce + 1), max_nonce + 1, inbound_message_data(REGULAR_PAYLOAD) @@ -402,8 +395,7 @@ mod tests { ); // Fails to dispatch new messages from latest relayer. Prevents griefing attacks. assert_eq!( - lane.receive_message::( - &(TEST_RELAYER_A + max_nonce), + lane.receive_message::( &(TEST_RELAYER_A + max_nonce), max_nonce + 1, inbound_message_data(REGULAR_PAYLOAD) @@ -420,8 +412,7 @@ mod tests { let max_nonce = ::MaxUnconfirmedMessagesAtInboundLane::get(); for current_nonce in 1..=max_nonce { assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, + lane.receive_message::( &TEST_RELAYER_A, current_nonce, inbound_message_data(REGULAR_PAYLOAD) @@ -431,8 +422,7 @@ mod tests { } // Fails to dispatch new message from different than latest relayer. assert_eq!( - lane.receive_message::( - &TEST_RELAYER_B, + lane.receive_message::( &TEST_RELAYER_B, max_nonce + 1, inbound_message_data(REGULAR_PAYLOAD) @@ -441,8 +431,7 @@ mod tests { ); // Fails to dispatch new messages from latest relayer. assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, + lane.receive_message::( &TEST_RELAYER_A, max_nonce + 1, inbound_message_data(REGULAR_PAYLOAD) @@ -457,8 +446,7 @@ mod tests { run_test(|| { let mut lane = inbound_lane::(TEST_LANE_ID); assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, + lane.receive_message::( &TEST_RELAYER_A, 1, inbound_message_data(REGULAR_PAYLOAD) @@ -466,8 +454,7 @@ mod tests { ReceivalResult::Dispatched(dispatch_result(0)) ); assert_eq!( - lane.receive_message::( - &TEST_RELAYER_B, + lane.receive_message::( &TEST_RELAYER_B, 2, inbound_message_data(REGULAR_PAYLOAD) @@ -475,8 +462,7 @@ mod tests { ReceivalResult::Dispatched(dispatch_result(0)) ); assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, + lane.receive_message::( &TEST_RELAYER_A, 3, inbound_message_data(REGULAR_PAYLOAD) @@ -499,8 +485,7 @@ mod tests { run_test(|| { let mut lane = inbound_lane::(TEST_LANE_ID); assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, + lane.receive_message::( &TEST_RELAYER_A, 1, inbound_message_data(REGULAR_PAYLOAD) @@ -508,8 +493,7 @@ mod tests { ReceivalResult::Dispatched(dispatch_result(0)) ); assert_eq!( - lane.receive_message::( - &TEST_RELAYER_B, + lane.receive_message::( &TEST_RELAYER_B, 1, inbound_message_data(REGULAR_PAYLOAD) @@ -535,8 +519,7 @@ mod tests { let mut payload = REGULAR_PAYLOAD; *payload.dispatch_result.unspent_weight.ref_time_mut() = 1; assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, + lane.receive_message::( &TEST_RELAYER_A, 1, inbound_message_data(payload) @@ -545,4 +528,29 @@ mod tests { ); }); } + + #[test] + fn first_message_is_confirmed_correctly() { + run_test(|| { + let mut lane = inbound_lane::(TEST_LANE_ID); + receive_regular_message(&mut lane, 1); + receive_regular_message(&mut lane, 2); + assert_eq!( + lane.receive_state_update(OutboundLaneData { + latest_received_nonce: 1, + ..Default::default() + }), + Some(1), + ); + assert_eq!( + inbound_unrewarded_relayers_state(TEST_LANE_ID), + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 2, + }, + ); + }); + } } diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index c94f5ffa752..8f4911b8d03 100644 --- a/modules/messages/src/lib.rs +++ b/modules/messages/src/lib.rs @@ -162,10 +162,7 @@ pub mod pallet { /// Source header chain, as it is represented on target chain. type SourceHeaderChain: SourceHeaderChain; /// Message dispatch. - type MessageDispatch: MessageDispatch< - Self::AccountId, - DispatchPayload = Self::InboundPayload, - >; + type MessageDispatch: MessageDispatch; } /// Shortcut to messages proof type for Config. @@ -361,9 +358,8 @@ pub mod pallet { fail!(Error::::InsufficientDispatchWeight); } - let receival_result = lane.receive_message::( + let receival_result = lane.receive_message::( &relayer_id_at_bridged_chain, - &relayer_id_at_this_chain, message.key.nonce, message.data, ); @@ -545,11 +541,7 @@ pub mod pallet { MessageAccepted { lane_id: LaneId, nonce: MessageNonce }, /// Messages have been received from the bridged chain. MessagesReceived( - Vec< - ReceivedMessages< - >::DispatchLevelResult, - >, - >, + Vec::DispatchLevelResult>>, ), /// Messages in the inclusive range have been delivered to the bridged chain. MessagesDelivered { lane_id: LaneId, messages: DeliveredMessages }, @@ -949,12 +941,12 @@ fn verify_and_decode_messages_proof::reset_events(); } - fn inbound_unrewarded_relayers_state( - lane: bp_messages::LaneId, - ) -> bp_messages::UnrewardedRelayersState { - let inbound_lane_data = InboundLanes::::get(lane).0; - let last_delivered_nonce = inbound_lane_data.last_delivered_nonce(); - let relayers = inbound_lane_data.relayers; - bp_messages::UnrewardedRelayersState { - unrewarded_relayer_entries: relayers.len() as _, - messages_in_oldest_entry: relayers - .front() - .map(|entry| 1 + entry.messages.end - entry.messages.begin) - .unwrap_or(0), - total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX), - last_delivered_nonce, - } - } - fn send_regular_message() { get_ready_for_events(); diff --git a/modules/messages/src/mock.rs b/modules/messages/src/mock.rs index 75f05b4820a..3d78ab562d2 100644 --- a/modules/messages/src/mock.rs +++ b/modules/messages/src/mock.rs @@ -26,8 +26,8 @@ use bp_messages::{ DeliveryPayments, DispatchMessage, DispatchMessageData, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain, }, - DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, - OutboundLaneData, UnrewardedRelayer, + total_unrewarded_messages, DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, + MessageNonce, MessagePayload, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use bp_runtime::{messages::MessageDispatchResult, Size}; use codec::{Decode, Encode}; @@ -142,7 +142,7 @@ impl pallet_balances::Config for TestRuntime { parameter_types! { pub const MaxMessagesToPruneAtOnce: u64 = 10; pub const MaxUnrewardedRelayerEntriesAtInboundLane: u64 = 16; - pub const MaxUnconfirmedMessagesAtInboundLane: u64 = 32; + pub const MaxUnconfirmedMessagesAtInboundLane: u64 = 128; pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID, TEST_LANE_ID_2]; } @@ -416,7 +416,7 @@ impl SourceHeaderChain for TestSourceHeaderChain { #[derive(Debug)] pub struct TestMessageDispatch; -impl MessageDispatch for TestMessageDispatch { +impl MessageDispatch for TestMessageDispatch { type DispatchPayload = TestPayload; type DispatchLevelResult = TestDispatchLevelResult; @@ -428,7 +428,6 @@ impl MessageDispatch for TestMessageDispatch { } fn dispatch( - _relayer_account: &AccountId, message: DispatchMessage, ) -> MessageDispatchResult { match message.data.payload.as_ref() { @@ -483,6 +482,22 @@ pub fn unrewarded_relayer( UnrewardedRelayer { relayer, messages: DeliveredMessages { begin, end } } } +/// Returns unrewarded relayers state at given lane. +pub fn inbound_unrewarded_relayers_state(lane: bp_messages::LaneId) -> UnrewardedRelayersState { + let inbound_lane_data = crate::InboundLanes::::get(lane).0; + let last_delivered_nonce = inbound_lane_data.last_delivered_nonce(); + let relayers = inbound_lane_data.relayers; + UnrewardedRelayersState { + unrewarded_relayer_entries: relayers.len() as _, + messages_in_oldest_entry: relayers + .front() + .map(|entry| 1 + entry.messages.end - entry.messages.begin) + .unwrap_or(0), + total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX), + last_delivered_nonce, + } +} + /// Return test externalities to use in tests. pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); diff --git a/modules/relayers/src/lib.rs b/modules/relayers/src/lib.rs index bd33b811b30..14e44d30f89 100644 --- a/modules/relayers/src/lib.rs +++ b/modules/relayers/src/lib.rs @@ -20,20 +20,25 @@ #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs)] -use bp_relayers::{PaymentProcedure, RelayerRewardsKeyProvider, RewardsAccountParams}; +use bp_relayers::{ + PaymentProcedure, Registration, RelayerRewardsKeyProvider, RewardsAccountParams, StakeAndSlash, +}; use bp_runtime::StorageDoubleMapKeyProvider; -use frame_support::sp_runtime::Saturating; +use frame_support::fail; use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero}; +use sp_runtime::{traits::CheckedSub, Saturating}; use sp_std::marker::PhantomData; pub use pallet::*; pub use payment_adapter::DeliveryConfirmationPaymentsAdapter; +pub use stake_adapter::StakeAndSlashNamed; pub use weights::WeightInfo; pub mod benchmarking; mod mock; mod payment_adapter; +mod stake_adapter; pub mod weights; @@ -56,8 +61,10 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// Type of relayer reward. type Reward: AtLeast32BitUnsigned + Copy + Parameter + MaxEncodedLen; - /// Pay rewards adapter. + /// Pay rewards scheme. type PaymentProcedure: PaymentProcedure; + /// Stake and slash scheme. + type StakeAndSlash: StakeAndSlash; /// Pallet call weights. type WeightInfo: WeightInfo; } @@ -102,9 +109,194 @@ pub mod pallet { }, ) } + + /// Register relayer or update its registration. + /// + /// Registration allows relayer to get priority boost for its message delivery transactions. + #[pallet::call_index(1)] + #[pallet::weight(Weight::zero())] // TODO: https://github.com/paritytech/parity-bridges-common/issues/2033 + pub fn register(origin: OriginFor, valid_till: T::BlockNumber) -> DispatchResult { + let relayer = ensure_signed(origin)?; + + // valid till must be larger than the current block number and the lease must be larger + // than the `RequiredRegistrationLease` + let lease = valid_till.saturating_sub(frame_system::Pallet::::block_number()); + ensure!( + lease > Pallet::::required_registration_lease(), + Error::::InvalidRegistrationLease + ); + + RegisteredRelayers::::try_mutate(&relayer, |maybe_registration| -> DispatchResult { + let mut registration = maybe_registration + .unwrap_or_else(|| Registration { valid_till, stake: Zero::zero() }); + + // new `valid_till` must be larger (or equal) than the old one + ensure!( + valid_till >= registration.valid_till, + Error::::CannotReduceRegistrationLease, + ); + registration.valid_till = valid_till; + + // regarding stake, there are three options: + // - if relayer stake is larger than required stake, we may do unreserve + // - if relayer stake equals to required stake, we do nothing + // - if relayer stake is smaller than required stake, we do additional reserve + let required_stake = Pallet::::required_stake(); + if let Some(to_unreserve) = registration.stake.checked_sub(&required_stake) { + Self::do_unreserve(&relayer, to_unreserve)?; + } else if let Some(to_reserve) = required_stake.checked_sub(®istration.stake) { + T::StakeAndSlash::reserve(&relayer, to_reserve).map_err(|e| { + log::trace!( + target: LOG_TARGET, + "Failed to reserve {:?} on relayer {:?} account: {:?}", + to_reserve, + relayer, + e, + ); + + Error::::FailedToReserve + })?; + } + registration.stake = required_stake; + + Self::deposit_event(Event::::RegistrationUpdated { + relayer: relayer.clone(), + registration, + }); + + *maybe_registration = Some(registration); + + Ok(()) + }) + } + + /// `Deregister` relayer. + /// + /// After this call, message delivery transactions of the relayer won't get any priority + /// boost. + #[pallet::call_index(2)] + #[pallet::weight(Weight::zero())] // TODO: https://github.com/paritytech/parity-bridges-common/issues/2033 + pub fn deregister(origin: OriginFor) -> DispatchResult { + let relayer = ensure_signed(origin)?; + + RegisteredRelayers::::try_mutate(&relayer, |maybe_registration| -> DispatchResult { + let registration = match maybe_registration.take() { + Some(registration) => registration, + None => fail!(Error::::NotRegistered), + }; + + // we can't deregister until `valid_till + 1` + ensure!( + registration.valid_till < frame_system::Pallet::::block_number(), + Error::::RegistrationIsStillActive, + ); + + // if stake is non-zero, we should do unreserve + if !registration.stake.is_zero() { + Self::do_unreserve(&relayer, registration.stake)?; + } + + Self::deposit_event(Event::::Deregistered { relayer: relayer.clone() }); + + *maybe_registration = None; + + Ok(()) + }) + } } impl Pallet { + /// Returns true if given relayer registration is active at current block. + /// + /// This call respects both `RequiredStake` and `RequiredRegistrationLease`, meaning that + /// it'll return false if registered stake is lower than required or if remaining lease + /// is less than `RequiredRegistrationLease`. + pub fn is_registration_active(relayer: &T::AccountId) -> bool { + let registration = match Self::registered_relayer(relayer) { + Some(registration) => registration, + None => return false, + }; + + // registration is inactive if relayer stake is less than required + if registration.stake < Self::required_stake() { + return false + } + + // registration is inactive if it ends soon + let remaining_lease = registration + .valid_till + .saturating_sub(frame_system::Pallet::::block_number()); + if remaining_lease <= Self::required_registration_lease() { + return false + } + + true + } + + /// Slash and `deregister` relayer. This function slashes all staked balance. + /// + /// It may fail inside, but error is swallowed and we only log it. + pub fn slash_and_deregister( + relayer: &T::AccountId, + slash_destination: RewardsAccountParams, + ) { + let registration = match RegisteredRelayers::::take(relayer) { + Some(registration) => registration, + None => { + log::trace!( + target: crate::LOG_TARGET, + "Cannot slash unregistered relayer {:?}", + relayer, + ); + + return + }, + }; + + match T::StakeAndSlash::repatriate_reserved( + relayer, + slash_destination, + registration.stake, + ) { + Ok(failed_to_slash) if failed_to_slash.is_zero() => { + log::trace!( + target: crate::LOG_TARGET, + "Relayer account {:?} has been slashed for {:?}. Funds were deposited to {:?}", + relayer, + registration.stake, + slash_destination, + ); + }, + Ok(failed_to_slash) => { + log::trace!( + target: crate::LOG_TARGET, + "Relayer account {:?} has been partially slashed for {:?}. Funds were deposited to {:?}. \ + Failed to slash: {:?}", + relayer, + registration.stake, + slash_destination, + failed_to_slash, + ); + }, + Err(e) => { + // TODO: document this. Where? + + // it may fail if there's no beneficiary account. For us it means that this + // account must exists before we'll deploy the bridge + log::debug!( + target: crate::LOG_TARGET, + "Failed to slash relayer account {:?}: {:?}. Maybe beneficiary account doesn't exist? \ + Beneficiary: {:?}, amount: {:?}, failed to slash: {:?}", + relayer, + e, + slash_destination, + registration.stake, + registration.stake, + ); + }, + } + } + /// Register reward for given relayer. pub fn register_relayer_reward( rewards_account_params: RewardsAccountParams, @@ -132,6 +324,42 @@ pub mod pallet { }, ); } + + /// Return required registration lease. + fn required_registration_lease() -> T::BlockNumber { + >::RequiredRegistrationLease::get() + } + + /// Return required stake. + fn required_stake() -> T::Reward { + >::RequiredStake::get() + } + + /// `Unreserve` given amount on relayer account. + fn do_unreserve(relayer: &T::AccountId, amount: T::Reward) -> DispatchResult { + let failed_to_unreserve = T::StakeAndSlash::unreserve(relayer, amount); + if !failed_to_unreserve.is_zero() { + log::trace!( + target: LOG_TARGET, + "Failed to unreserve {:?}/{:?} on relayer {:?} account", + failed_to_unreserve, + amount, + relayer, + ); + + fail!(Error::::FailedToUnreserve) + } + + Ok(()) + } } #[pallet::event] @@ -146,6 +374,25 @@ pub mod pallet { /// Reward amount. reward: T::Reward, }, + /// Relayer registration has been added or updated. + RegistrationUpdated { + /// Relayer account that has been registered. + relayer: T::AccountId, + /// Relayer registration. + registration: Registration, + }, + /// Relayer has been `deregistered`. + Deregistered { + /// Relayer account that has been `deregistered`. + relayer: T::AccountId, + }, + /// Relayer has been slashed and `deregistered`. + SlashedAndDeregistered { + /// Relayer account that has been `deregistered`. + relayer: T::AccountId, + /// Registration that was removed. + registration: Registration, + }, } #[pallet::error] @@ -154,6 +401,19 @@ pub mod pallet { NoRewardForRelayer, /// Reward payment procedure has failed. FailedToPayReward, + /// The relayer has tried to register for past block or registration lease + /// is too short. + InvalidRegistrationLease, + /// New registration lease is less than the previous one. + CannotReduceRegistrationLease, + /// Failed to reserve enough funds on relayer account. + FailedToReserve, + /// Failed to `unreserve` enough funds on relayer account. + FailedToUnreserve, + /// Cannot `deregister` if not registered. + NotRegistered, + /// Failed to `deregister` relayer, because lease is still active. + RegistrationIsStillActive, } /// Map of the relayer => accumulated reward. @@ -168,6 +428,22 @@ pub mod pallet { as StorageDoubleMapKeyProvider>::Value, OptionQuery, >; + + /// Relayers that have reserved some of their balance to get free priority boost + /// for their message delivery transactions. + /// + /// Other relayers may submit transactions as well, but they will have default + /// priority and will be rejected (without significant tip) in case if registered + /// relayer is present. + #[pallet::storage] + #[pallet::getter(fn registered_relayer)] + pub type RegisteredRelayers = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + Registration, + OptionQuery, + >; } #[cfg(test)] @@ -253,10 +529,10 @@ mod tests { None ); - //Check if the `RewardPaid` event was emitted. + // Check if the `RewardPaid` event was emitted. assert_eq!( - System::::events(), - vec![EventRecord { + System::::events().last(), + Some(&EventRecord { phase: Phase::Initialization, event: TestEvent::Relayers(RewardPaid { relayer: REGULAR_RELAYER, @@ -264,7 +540,7 @@ mod tests { reward: 100 }), topics: vec![], - }], + }), ); }); } @@ -306,4 +582,295 @@ mod tests { assert_eq!(Balances::balance(&1), 200); }); } + + #[test] + fn register_fails_if_valid_till_is_a_past_block() { + run_test(|| { + System::::set_block_number(100); + + assert_noop!( + Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 50), + Error::::InvalidRegistrationLease, + ); + }); + } + + #[test] + fn register_fails_if_valid_till_lease_is_less_than_required() { + run_test(|| { + System::::set_block_number(100); + + assert_noop!( + Pallet::::register( + RuntimeOrigin::signed(REGISTER_RELAYER), + 99 + Lease::get() + ), + Error::::InvalidRegistrationLease, + ); + }); + } + + #[test] + fn register_works() { + run_test(|| { + get_ready_for_events(); + + assert_ok!(Pallet::::register( + RuntimeOrigin::signed(REGISTER_RELAYER), + 150 + )); + assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get()); + assert_eq!( + Pallet::::registered_relayer(REGISTER_RELAYER), + Some(Registration { valid_till: 150, stake: Stake::get() }), + ); + + assert_eq!( + System::::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: TestEvent::Relayers(Event::RegistrationUpdated { + relayer: REGISTER_RELAYER, + registration: Registration { valid_till: 150, stake: Stake::get() }, + }), + topics: vec![], + }), + ); + }); + } + + #[test] + fn register_fails_if_new_valid_till_is_lesser_than_previous() { + run_test(|| { + assert_ok!(Pallet::::register( + RuntimeOrigin::signed(REGISTER_RELAYER), + 150 + )); + + assert_noop!( + Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 125), + Error::::CannotReduceRegistrationLease, + ); + }); + } + + #[test] + fn register_fails_if_it_cant_unreserve_some_balance_if_required_stake_decreases() { + run_test(|| { + RegisteredRelayers::::insert( + REGISTER_RELAYER, + Registration { valid_till: 150, stake: Stake::get() + 1 }, + ); + + assert_noop!( + Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 150), + Error::::FailedToUnreserve, + ); + }); + } + + #[test] + fn register_unreserves_some_balance_if_required_stake_decreases() { + run_test(|| { + get_ready_for_events(); + + RegisteredRelayers::::insert( + REGISTER_RELAYER, + Registration { valid_till: 150, stake: Stake::get() + 1 }, + ); + TestStakeAndSlash::reserve(®ISTER_RELAYER, Stake::get() + 1).unwrap(); + assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get() + 1); + let free_balance = Balances::free_balance(REGISTER_RELAYER); + + assert_ok!(Pallet::::register( + RuntimeOrigin::signed(REGISTER_RELAYER), + 150 + )); + assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get()); + assert_eq!(Balances::free_balance(REGISTER_RELAYER), free_balance + 1); + assert_eq!( + Pallet::::registered_relayer(REGISTER_RELAYER), + Some(Registration { valid_till: 150, stake: Stake::get() }), + ); + + assert_eq!( + System::::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: TestEvent::Relayers(Event::RegistrationUpdated { + relayer: REGISTER_RELAYER, + registration: Registration { valid_till: 150, stake: Stake::get() } + }), + topics: vec![], + }), + ); + }); + } + + #[test] + fn register_fails_if_it_cant_reserve_some_balance() { + run_test(|| { + Balances::set_balance(®ISTER_RELAYER, 0); + assert_noop!( + Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 150), + Error::::FailedToReserve, + ); + }); + } + + #[test] + fn register_fails_if_it_cant_reserve_some_balance_if_required_stake_increases() { + run_test(|| { + RegisteredRelayers::::insert( + REGISTER_RELAYER, + Registration { valid_till: 150, stake: Stake::get() - 1 }, + ); + Balances::set_balance(®ISTER_RELAYER, 0); + + assert_noop!( + Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 150), + Error::::FailedToReserve, + ); + }); + } + + #[test] + fn register_reserves_some_balance_if_required_stake_increases() { + run_test(|| { + get_ready_for_events(); + + RegisteredRelayers::::insert( + REGISTER_RELAYER, + Registration { valid_till: 150, stake: Stake::get() - 1 }, + ); + TestStakeAndSlash::reserve(®ISTER_RELAYER, Stake::get() - 1).unwrap(); + + let free_balance = Balances::free_balance(REGISTER_RELAYER); + assert_ok!(Pallet::::register( + RuntimeOrigin::signed(REGISTER_RELAYER), + 150 + )); + assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get()); + assert_eq!(Balances::free_balance(REGISTER_RELAYER), free_balance - 1); + assert_eq!( + Pallet::::registered_relayer(REGISTER_RELAYER), + Some(Registration { valid_till: 150, stake: Stake::get() }), + ); + + assert_eq!( + System::::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: TestEvent::Relayers(Event::RegistrationUpdated { + relayer: REGISTER_RELAYER, + registration: Registration { valid_till: 150, stake: Stake::get() } + }), + topics: vec![], + }), + ); + }); + } + + #[test] + fn deregister_fails_if_not_registered() { + run_test(|| { + assert_noop!( + Pallet::::deregister(RuntimeOrigin::signed(REGISTER_RELAYER)), + Error::::NotRegistered, + ); + }); + } + + #[test] + fn deregister_fails_if_registration_is_still_active() { + run_test(|| { + assert_ok!(Pallet::::register( + RuntimeOrigin::signed(REGISTER_RELAYER), + 150 + )); + + System::::set_block_number(100); + + assert_noop!( + Pallet::::deregister(RuntimeOrigin::signed(REGISTER_RELAYER)), + Error::::RegistrationIsStillActive, + ); + }); + } + + #[test] + fn deregister_works() { + run_test(|| { + get_ready_for_events(); + + assert_ok!(Pallet::::register( + RuntimeOrigin::signed(REGISTER_RELAYER), + 150 + )); + + System::::set_block_number(151); + + let reserved_balance = Balances::reserved_balance(REGISTER_RELAYER); + let free_balance = Balances::free_balance(REGISTER_RELAYER); + assert_ok!(Pallet::::deregister(RuntimeOrigin::signed(REGISTER_RELAYER))); + assert_eq!( + Balances::reserved_balance(REGISTER_RELAYER), + reserved_balance - Stake::get() + ); + assert_eq!(Balances::free_balance(REGISTER_RELAYER), free_balance + Stake::get()); + + assert_eq!( + System::::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: TestEvent::Relayers(Event::Deregistered { relayer: REGISTER_RELAYER }), + topics: vec![], + }), + ); + }); + } + + #[test] + fn is_registration_active_is_false_for_unregistered_relayer() { + run_test(|| { + assert!(!Pallet::::is_registration_active(®ISTER_RELAYER)); + }); + } + + #[test] + fn is_registration_active_is_false_when_stake_is_too_low() { + run_test(|| { + RegisteredRelayers::::insert( + REGISTER_RELAYER, + Registration { valid_till: 150, stake: Stake::get() - 1 }, + ); + assert!(!Pallet::::is_registration_active(®ISTER_RELAYER)); + }); + } + + #[test] + fn is_registration_active_is_false_when_remaining_lease_is_too_low() { + run_test(|| { + System::::set_block_number(150 - Lease::get()); + + RegisteredRelayers::::insert( + REGISTER_RELAYER, + Registration { valid_till: 150, stake: Stake::get() }, + ); + assert!(!Pallet::::is_registration_active(®ISTER_RELAYER)); + }); + } + + #[test] + fn is_registration_active_is_true_when_relayer_is_properly_registeered() { + run_test(|| { + System::::set_block_number(150 - Lease::get()); + + RegisteredRelayers::::insert( + REGISTER_RELAYER, + Registration { valid_till: 151, stake: Stake::get() }, + ); + assert!(Pallet::::is_registration_active(®ISTER_RELAYER)); + }); + } } diff --git a/modules/relayers/src/mock.rs b/modules/relayers/src/mock.rs index fe8c586eecc..406a365f350 100644 --- a/modules/relayers/src/mock.rs +++ b/modules/relayers/src/mock.rs @@ -19,8 +19,10 @@ use crate as pallet_bridge_relayers; use bp_messages::LaneId; -use bp_relayers::{PaymentProcedure, RewardsAccountOwner, RewardsAccountParams}; -use frame_support::{parameter_types, weights::RuntimeDbWeight}; +use bp_relayers::{ + PayRewardFromAccount, PaymentProcedure, RewardsAccountOwner, RewardsAccountParams, +}; +use frame_support::{parameter_types, traits::fungible::Mutate, weights::RuntimeDbWeight}; use sp_core::H256; use sp_runtime::{ testing::Header as SubstrateHeader, @@ -29,6 +31,16 @@ use sp_runtime::{ pub type AccountId = u64; pub type Balance = u64; +pub type BlockNumber = u64; + +pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< + AccountId, + BlockNumber, + Balances, + ReserveId, + Stake, + Lease, +>; type Block = frame_system::mocking::MockBlock; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; @@ -47,13 +59,17 @@ frame_support::construct_runtime! { parameter_types! { pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; + pub const ExistentialDeposit: Balance = 1; + pub const ReserveId: [u8; 8] = *b"brdgrlrs"; + pub const Stake: Balance = 1_000; + pub const Lease: BlockNumber = 8; } impl frame_system::Config for TestRuntime { type RuntimeOrigin = RuntimeOrigin; type Index = u64; type RuntimeCall = RuntimeCall; - type BlockNumber = u64; + type BlockNumber = BlockNumber; type Hash = H256; type Hashing = BlakeTwo256; type AccountId = AccountId; @@ -81,11 +97,11 @@ impl pallet_balances::Config for TestRuntime { type Balance = Balance; type DustRemoval = (); type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = frame_support::traits::ConstU64<1>; + type ExistentialDeposit = ExistentialDeposit; type AccountStore = frame_system::Pallet; type WeightInfo = (); - type MaxReserves = (); - type ReserveIdentifier = (); + type MaxReserves = ConstU32<1>; + type ReserveIdentifier = [u8; 8]; type HoldIdentifier = (); type FreezeIdentifier = (); type MaxHolds = ConstU32<0>; @@ -96,6 +112,7 @@ impl pallet_bridge_relayers::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type Reward = Balance; type PaymentProcedure = TestPaymentProcedure; + type StakeAndSlash = TestStakeAndSlash; type WeightInfo = (); } @@ -121,9 +138,18 @@ pub const REGULAR_RELAYER: AccountId = 1; /// Relayer that can't receive rewards. pub const FAILING_RELAYER: AccountId = 2; +/// Relayer that is able to register. +pub const REGISTER_RELAYER: AccountId = 42; + /// Payment procedure that rejects payments to the `FAILING_RELAYER`. pub struct TestPaymentProcedure; +impl TestPaymentProcedure { + pub fn rewards_account(params: RewardsAccountParams) -> AccountId { + PayRewardFromAccount::<(), AccountId>::rewards_account(params) + } +} + impl PaymentProcedure for TestPaymentProcedure { type Error = (); @@ -147,5 +173,10 @@ pub fn new_test_ext() -> sp_io::TestExternalities { /// Run pallet test. pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(test) + new_test_ext().execute_with(|| { + Balances::mint_into(®ISTER_RELAYER, ExistentialDeposit::get() + 10 * Stake::get()) + .unwrap(); + + test() + }) } diff --git a/modules/relayers/src/stake_adapter.rs b/modules/relayers/src/stake_adapter.rs new file mode 100644 index 00000000000..055b6a111ec --- /dev/null +++ b/modules/relayers/src/stake_adapter.rs @@ -0,0 +1,186 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Code that allows `NamedReservableCurrency` to be used as a `StakeAndSlash` +//! mechanism of the relayers pallet. + +use bp_relayers::{PayRewardFromAccount, RewardsAccountParams, StakeAndSlash}; +use codec::Codec; +use frame_support::traits::{tokens::BalanceStatus, NamedReservableCurrency}; +use sp_runtime::{traits::Get, DispatchError, DispatchResult}; +use sp_std::{fmt::Debug, marker::PhantomData}; + +/// `StakeAndSlash` that works with `NamedReservableCurrency` and uses named +/// reservations. +/// +/// **WARNING**: this implementation assumes that the relayers pallet is configured to +/// use the [`bp_relayers::PayRewardFromAccount`] as its relayers payment scheme. +pub struct StakeAndSlashNamed( + PhantomData<(AccountId, BlockNumber, Currency, ReserveId, Stake, Lease)>, +); + +impl + StakeAndSlash + for StakeAndSlashNamed +where + AccountId: Codec + Debug, + Currency: NamedReservableCurrency, + ReserveId: Get, + Stake: Get, + Lease: Get, +{ + type RequiredStake = Stake; + type RequiredRegistrationLease = Lease; + + fn reserve(relayer: &AccountId, amount: Currency::Balance) -> DispatchResult { + Currency::reserve_named(&ReserveId::get(), relayer, amount) + } + + fn unreserve(relayer: &AccountId, amount: Currency::Balance) -> Currency::Balance { + Currency::unreserve_named(&ReserveId::get(), relayer, amount) + } + + fn repatriate_reserved( + relayer: &AccountId, + beneficiary: RewardsAccountParams, + amount: Currency::Balance, + ) -> Result { + let beneficiary_account = + PayRewardFromAccount::<(), AccountId>::rewards_account(beneficiary); + Currency::repatriate_reserved_named( + &ReserveId::get(), + relayer, + &beneficiary_account, + amount, + BalanceStatus::Free, + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::*; + + use frame_support::traits::fungible::Mutate; + + fn test_stake() -> Balance { + Stake::get() + } + + #[test] + fn reserve_works() { + run_test(|| { + assert!(TestStakeAndSlash::reserve(&1, test_stake()).is_err()); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 0); + + Balances::mint_into(&2, test_stake() - 1).unwrap(); + assert!(TestStakeAndSlash::reserve(&2, test_stake()).is_err()); + assert_eq!(Balances::free_balance(2), test_stake() - 1); + assert_eq!(Balances::reserved_balance(2), 0); + + Balances::mint_into(&3, test_stake() * 2).unwrap(); + assert_eq!(TestStakeAndSlash::reserve(&3, test_stake()), Ok(())); + assert_eq!(Balances::free_balance(3), test_stake()); + assert_eq!(Balances::reserved_balance(3), test_stake()); + }) + } + + #[test] + fn unreserve_works() { + run_test(|| { + assert_eq!(TestStakeAndSlash::unreserve(&1, test_stake()), test_stake()); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 0); + + Balances::mint_into(&2, test_stake() * 2).unwrap(); + TestStakeAndSlash::reserve(&2, test_stake() / 3).unwrap(); + assert_eq!( + TestStakeAndSlash::unreserve(&2, test_stake()), + test_stake() - test_stake() / 3 + ); + assert_eq!(Balances::free_balance(2), test_stake() * 2); + assert_eq!(Balances::reserved_balance(2), 0); + + Balances::mint_into(&3, test_stake() * 2).unwrap(); + TestStakeAndSlash::reserve(&3, test_stake()).unwrap(); + assert_eq!(TestStakeAndSlash::unreserve(&3, test_stake()), 0); + assert_eq!(Balances::free_balance(3), test_stake() * 2); + assert_eq!(Balances::reserved_balance(3), 0); + }) + } + + #[test] + fn repatriate_reserved_works() { + run_test(|| { + let beneficiary = TEST_REWARDS_ACCOUNT_PARAMS; + let beneficiary_account = TestPaymentProcedure::rewards_account(beneficiary); + + let mut expected_balance = ExistentialDeposit::get(); + Balances::mint_into(&beneficiary_account, expected_balance).unwrap(); + + assert_eq!( + TestStakeAndSlash::repatriate_reserved(&1, beneficiary, test_stake()), + Ok(test_stake()) + ); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::free_balance(beneficiary_account), expected_balance); + assert_eq!(Balances::reserved_balance(beneficiary_account), 0); + + expected_balance += test_stake() / 3; + Balances::mint_into(&2, test_stake() * 2).unwrap(); + TestStakeAndSlash::reserve(&2, test_stake() / 3).unwrap(); + assert_eq!( + TestStakeAndSlash::repatriate_reserved(&2, beneficiary, test_stake()), + Ok(test_stake() - test_stake() / 3) + ); + assert_eq!(Balances::free_balance(2), test_stake() * 2 - test_stake() / 3); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::free_balance(beneficiary_account), expected_balance); + assert_eq!(Balances::reserved_balance(beneficiary_account), 0); + + expected_balance += test_stake(); + Balances::mint_into(&3, test_stake() * 2).unwrap(); + TestStakeAndSlash::reserve(&3, test_stake()).unwrap(); + assert_eq!( + TestStakeAndSlash::repatriate_reserved(&3, beneficiary, test_stake()), + Ok(0) + ); + assert_eq!(Balances::free_balance(3), test_stake()); + assert_eq!(Balances::reserved_balance(3), 0); + assert_eq!(Balances::free_balance(beneficiary_account), expected_balance); + assert_eq!(Balances::reserved_balance(beneficiary_account), 0); + }) + } + + #[test] + fn repatriate_reserved_doesnt_work_when_beneficiary_account_is_missing() { + run_test(|| { + let beneficiary = TEST_REWARDS_ACCOUNT_PARAMS; + let beneficiary_account = TestPaymentProcedure::rewards_account(beneficiary); + + Balances::mint_into(&3, test_stake() * 2).unwrap(); + TestStakeAndSlash::reserve(&3, test_stake()).unwrap(); + assert!(TestStakeAndSlash::repatriate_reserved(&3, beneficiary, test_stake()).is_err()); + assert_eq!(Balances::free_balance(3), test_stake()); + assert_eq!(Balances::reserved_balance(3), test_stake()); + assert_eq!(Balances::free_balance(beneficiary_account), 0); + assert_eq!(Balances::reserved_balance(beneficiary_account), 0); + }); + } +} diff --git a/primitives/messages/src/target_chain.rs b/primitives/messages/src/target_chain.rs index 8496b90214c..3c2e8cf0cb0 100644 --- a/primitives/messages/src/target_chain.rs +++ b/primitives/messages/src/target_chain.rs @@ -83,7 +83,7 @@ pub trait SourceHeaderChain { } /// Called when inbound message is received. -pub trait MessageDispatch { +pub trait MessageDispatch { /// Decoded message payload type. Valid message may contain invalid payload. In this case /// message is delivered, but dispatch fails. Therefore, two separate types of payload /// (opaque `MessagePayload` used in delivery and this `DispatchPayload` used in dispatch). @@ -103,11 +103,7 @@ pub trait MessageDispatch { /// /// It is up to the implementers of this trait to determine whether the message /// is invalid (i.e. improperly encoded, has too large weight, ...) or not. - /// - /// If your configuration allows paying dispatch fee at the target chain, then - /// it must be paid inside this method to the `relayer_account`. fn dispatch( - relayer_account: &AccountId, message: DispatchMessage, ) -> MessageDispatchResult; } @@ -186,7 +182,7 @@ impl SourceHeaderChain } } -impl MessageDispatch +impl MessageDispatch for ForbidInboundMessages { type DispatchPayload = DispatchPayload; @@ -197,7 +193,6 @@ impl MessageDispatch, ) -> MessageDispatchResult { MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } diff --git a/primitives/relayers/src/lib.rs b/primitives/relayers/src/lib.rs index f14b841fa9e..21f66a2ffa1 100644 --- a/primitives/relayers/src/lib.rs +++ b/primitives/relayers/src/lib.rs @@ -19,6 +19,8 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +pub use registration::{Registration, StakeAndSlash}; + use bp_messages::LaneId; use bp_runtime::{ChainId, StorageDoubleMapKeyProvider}; use frame_support::{traits::tokens::Preservation, Blake2_128Concat, Identity}; @@ -30,6 +32,8 @@ use sp_runtime::{ }; use sp_std::{fmt::Debug, marker::PhantomData}; +mod registration; + /// The owner of the sovereign account that should pay the rewards. /// /// Each of the 2 final points connected by a bridge owns a sovereign account at each end of the diff --git a/primitives/relayers/src/registration.rs b/primitives/relayers/src/registration.rs new file mode 100644 index 00000000000..da64bdde379 --- /dev/null +++ b/primitives/relayers/src/registration.rs @@ -0,0 +1,121 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Bridge relayers registration and slashing scheme. +//! +//! There is an option to add a refund-relayer signed extension that will compensate +//! relayer costs of the message delivery and confirmation transactions (as well as +//! required finality proofs). This extension boosts priority of message delivery +//! transactions, based on the number of bundled messages. So transaction with more +//! messages has larger priority than the transaction with less messages. +//! See [`bridge_runtime_common::priority_calculator`] for details; +//! +//! This encourages relayers to include more messages to their delivery transactions. +//! At the same time, we are not verifying storage proofs before boosting +//! priority. Instead, we simply trust relayer, when it says that transaction delivers +//! `N` messages. +//! +//! This allows relayers to submit transactions which declare large number of bundled +//! transactions to receive priority boost for free, potentially pushing actual delivery +//! transactions from the block (or even transaction queue). Such transactions are +//! not free, but their cost is relatively small. +//! +//! To alleviate that, we only boost transactions of relayers that have some stake +//! that guarantees that their transactions are valid. Such relayers get priority +//! for free, but they risk to lose their stake. + +use crate::RewardsAccountParams; + +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_runtime::{ + traits::{Get, Zero}, + DispatchError, DispatchResult, +}; + +/// Relayer registration. +#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] +pub struct Registration { + /// The last block number, where this registration is considered active. + /// + /// Relayer has an option to renew his registration (this may be done before it + /// is spoiled as well). Starting from block `valid_till + 1`, relayer may `deregister` + /// himself and get his stake back. + /// + /// Please keep in mind that priority boost stops working some blocks before the + /// registration ends (see [`StakeAndSlash::RequiredRegistrationLease`]). + pub valid_till: BlockNumber, + /// Active relayer stake, which is mapped to the relayer reserved balance. + /// + /// If `stake` is less than the [`StakeAndSlash::RequiredStake`], the registration + /// is considered inactive even if `valid_till + 1` is not yet reached. + pub stake: Balance, +} + +/// Relayer stake-and-slash mechanism. +pub trait StakeAndSlash { + /// The stake that the relayer must have to have its transactions boosted. + type RequiredStake: Get; + /// Required **remaining** registration lease to be able to get transaction priority boost. + /// + /// If the difference between registration's `valid_till` and the current block number + /// is less than the `RequiredRegistrationLease`, it becomes inactive and relayer transaction + /// won't get priority boost. This period exists, because priority is calculated when + /// transaction is placed to the queue (and it is reevaluated periodically) and then some time + /// may pass before transaction will be included into the block. + type RequiredRegistrationLease: Get; + + /// Reserve the given amount at relayer account. + fn reserve(relayer: &AccountId, amount: Balance) -> DispatchResult; + /// `Unreserve` the given amount from relayer account. + /// + /// Returns amount that we have failed to `unreserve`. + fn unreserve(relayer: &AccountId, amount: Balance) -> Balance; + /// Slash up to `amount` from reserved balance of account `relayer` and send funds to given + /// `beneficiary`. + /// + /// Returns `Ok(_)` with non-zero balance if we have failed to repatriate some portion of stake. + fn repatriate_reserved( + relayer: &AccountId, + beneficiary: RewardsAccountParams, + amount: Balance, + ) -> Result; +} + +impl StakeAndSlash for () +where + Balance: Default + Zero, + BlockNumber: Default, +{ + type RequiredStake = (); + type RequiredRegistrationLease = (); + + fn reserve(_relayer: &AccountId, _amount: Balance) -> DispatchResult { + Ok(()) + } + + fn unreserve(_relayer: &AccountId, _amount: Balance) -> Balance { + Zero::zero() + } + + fn repatriate_reserved( + _relayer: &AccountId, + _beneficiary: RewardsAccountParams, + _amount: Balance, + ) -> Result { + Ok(Zero::zero()) + } +} diff --git a/relays/client-rialto-parachain/Cargo.toml b/relays/client-rialto-parachain/Cargo.toml index 4450dee3711..b6d5c423336 100644 --- a/relays/client-rialto-parachain/Cargo.toml +++ b/relays/client-rialto-parachain/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5" } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -subxt = { version = "0.27.1", default-features = false, features = [] } +subxt = { version = "0.28.0", default-features = false, features = [] } # Bridge dependencies diff --git a/relays/client-rialto-parachain/src/codegen_runtime.rs b/relays/client-rialto-parachain/src/codegen_runtime.rs index 3ea4a0ac2b5..1338d4938a3 100644 --- a/relays/client-rialto-parachain/src/codegen_runtime.rs +++ b/relays/client-rialto-parachain/src/codegen_runtime.rs @@ -22,5956 +22,24 @@ #[allow(clippy::all)] pub mod api { use super::api as root_mod; - pub static PALLETS: [&str; 16usize] = [ - "System", - "Timestamp", - "Sudo", - "TransactionPayment", - "ParachainSystem", - "ParachainInfo", - "Balances", - "Aura", - "AuraExt", - "XcmpQueue", - "PolkadotXcm", - "CumulusXcm", - "DmpQueue", - "BridgeRelayers", - "BridgeMillauGrandpa", - "BridgeMillauMessages", - ]; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub enum Event { - #[codec(index = 0)] - System(system::Event), - #[codec(index = 2)] - Sudo(sudo::Event), - #[codec(index = 3)] - TransactionPayment(transaction_payment::Event), - #[codec(index = 20)] - ParachainSystem(parachain_system::Event), - #[codec(index = 30)] - Balances(balances::Event), - #[codec(index = 50)] - XcmpQueue(xcmp_queue::Event), - #[codec(index = 51)] - PolkadotXcm(polkadot_xcm::Event), - #[codec(index = 52)] - CumulusXcm(cumulus_xcm::Event), - #[codec(index = 53)] - DmpQueue(dmp_queue::Event), - #[codec(index = 54)] - BridgeRelayers(bridge_relayers::Event), - #[codec(index = 55)] - BridgeMillauGrandpa(bridge_millau_grandpa::Event), - #[codec(index = 56)] - BridgeMillauMessages(bridge_millau_messages::Event), - } - pub mod system { - use super::{root_mod, runtime_types}; - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] - pub mod calls { - use super::{root_mod, runtime_types}; - type DispatchError = runtime_types::sp_runtime::DispatchError; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct Remark { - pub remark: ::std::vec::Vec<::core::primitive::u8>, - } - #[derive( - :: subxt :: ext :: codec :: CompactAs, - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - pub struct SetHeapPages { - pub pages: ::core::primitive::u64, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SetCode { - pub code: ::std::vec::Vec<::core::primitive::u8>, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SetCodeWithoutChecks { - pub code: ::std::vec::Vec<::core::primitive::u8>, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SetStorage { - pub items: ::std::vec::Vec<( - ::std::vec::Vec<::core::primitive::u8>, - ::std::vec::Vec<::core::primitive::u8>, - )>, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct KillStorage { - pub keys: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct KillPrefix { - pub prefix: ::std::vec::Vec<::core::primitive::u8>, - pub subkeys: ::core::primitive::u32, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct RemarkWithEvent { - pub remark: ::std::vec::Vec<::core::primitive::u8>, - } - pub struct TransactionApi; - impl TransactionApi { - #[doc = "Make some on-chain remark."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- `O(1)`"] - pub fn remark( - &self, - remark: ::std::vec::Vec<::core::primitive::u8>, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "System", - "remark", - Remark { remark }, - [ - 101u8, 80u8, 195u8, 226u8, 224u8, 247u8, 60u8, 128u8, 3u8, 101u8, 51u8, - 147u8, 96u8, 126u8, 76u8, 230u8, 194u8, 227u8, 191u8, 73u8, 160u8, - 146u8, 87u8, 147u8, 243u8, 28u8, 228u8, 116u8, 224u8, 181u8, 129u8, - 160u8, - ], - ) - } - #[doc = "Set the number of pages in the WebAssembly environment's heap."] - pub fn set_heap_pages( - &self, - pages: ::core::primitive::u64, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "System", - "set_heap_pages", - SetHeapPages { pages }, - [ - 43u8, 103u8, 128u8, 49u8, 156u8, 136u8, 11u8, 204u8, 80u8, 6u8, 244u8, - 86u8, 171u8, 44u8, 140u8, 225u8, 142u8, 198u8, 43u8, 87u8, 26u8, 45u8, - 125u8, 222u8, 165u8, 254u8, 172u8, 158u8, 39u8, 178u8, 86u8, 87u8, - ], - ) - } - #[doc = "Set the new runtime code."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code`"] - pub fn set_code( - &self, - code: ::std::vec::Vec<::core::primitive::u8>, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "System", - "set_code", - SetCode { code }, - [ - 27u8, 104u8, 244u8, 205u8, 188u8, 254u8, 121u8, 13u8, 106u8, 120u8, - 244u8, 108u8, 97u8, 84u8, 100u8, 68u8, 26u8, 69u8, 93u8, 128u8, 107u8, - 4u8, 3u8, 142u8, 13u8, 134u8, 196u8, 62u8, 113u8, 181u8, 14u8, 40u8, - ], - ) - } - #[doc = "Set the new runtime code without doing any checks of the given `code`."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- `O(C)` where `C` length of `code`"] - pub fn set_code_without_checks( - &self, - code: ::std::vec::Vec<::core::primitive::u8>, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "System", - "set_code_without_checks", - SetCodeWithoutChecks { code }, - [ - 102u8, 160u8, 125u8, 235u8, 30u8, 23u8, 45u8, 239u8, 112u8, 148u8, - 159u8, 158u8, 42u8, 93u8, 206u8, 94u8, 80u8, 250u8, 66u8, 195u8, 60u8, - 40u8, 142u8, 169u8, 183u8, 80u8, 80u8, 96u8, 3u8, 231u8, 99u8, 216u8, - ], - ) - } - #[doc = "Set some items of storage."] - pub fn set_storage( - &self, - items: ::std::vec::Vec<( - ::std::vec::Vec<::core::primitive::u8>, - ::std::vec::Vec<::core::primitive::u8>, - )>, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "System", - "set_storage", - SetStorage { items }, - [ - 74u8, 43u8, 106u8, 255u8, 50u8, 151u8, 192u8, 155u8, 14u8, 90u8, 19u8, - 45u8, 165u8, 16u8, 235u8, 242u8, 21u8, 131u8, 33u8, 172u8, 119u8, 78u8, - 140u8, 10u8, 107u8, 202u8, 122u8, 235u8, 181u8, 191u8, 22u8, 116u8, - ], - ) - } - #[doc = "Kill some items from storage."] - pub fn kill_storage( - &self, - keys: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "System", - "kill_storage", - KillStorage { keys }, - [ - 174u8, 174u8, 13u8, 174u8, 75u8, 138u8, 128u8, 235u8, 222u8, 216u8, - 85u8, 18u8, 198u8, 1u8, 138u8, 70u8, 19u8, 108u8, 209u8, 41u8, 228u8, - 67u8, 130u8, 230u8, 160u8, 207u8, 11u8, 180u8, 139u8, 242u8, 41u8, - 15u8, - ], - ) - } - #[doc = "Kill all storage items with a key that starts with the given prefix."] - #[doc = ""] - #[doc = "**NOTE:** We rely on the Root origin to provide us the number of subkeys under"] - #[doc = "the prefix we are removing to accurately calculate the weight of this function."] - pub fn kill_prefix( - &self, - prefix: ::std::vec::Vec<::core::primitive::u8>, - subkeys: ::core::primitive::u32, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "System", - "kill_prefix", - KillPrefix { prefix, subkeys }, - [ - 203u8, 116u8, 217u8, 42u8, 154u8, 215u8, 77u8, 217u8, 13u8, 22u8, - 193u8, 2u8, 128u8, 115u8, 179u8, 115u8, 187u8, 218u8, 129u8, 34u8, - 80u8, 4u8, 173u8, 120u8, 92u8, 35u8, 237u8, 112u8, 201u8, 207u8, 200u8, - 48u8, - ], - ) - } - #[doc = "Make some on-chain remark and emit event."] - pub fn remark_with_event( - &self, - remark: ::std::vec::Vec<::core::primitive::u8>, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "System", - "remark_with_event", - RemarkWithEvent { remark }, - [ - 123u8, 225u8, 180u8, 179u8, 144u8, 74u8, 27u8, 85u8, 101u8, 75u8, - 134u8, 44u8, 181u8, 25u8, 183u8, 158u8, 14u8, 213u8, 56u8, 225u8, - 136u8, 88u8, 26u8, 114u8, 178u8, 43u8, 176u8, 43u8, 240u8, 84u8, 116u8, - 46u8, - ], - ) - } - } - } - #[doc = "Event for the System pallet."] - pub type Event = runtime_types::frame_system::pallet::Event; - pub mod events { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "An extrinsic completed successfully."] - pub struct ExtrinsicSuccess { - pub dispatch_info: runtime_types::frame_support::dispatch::DispatchInfo, - } - impl ::subxt::events::StaticEvent for ExtrinsicSuccess { - const PALLET: &'static str = "System"; - const EVENT: &'static str = "ExtrinsicSuccess"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "An extrinsic failed."] - pub struct ExtrinsicFailed { - pub dispatch_error: runtime_types::sp_runtime::DispatchError, - pub dispatch_info: runtime_types::frame_support::dispatch::DispatchInfo, - } - impl ::subxt::events::StaticEvent for ExtrinsicFailed { - const PALLET: &'static str = "System"; - const EVENT: &'static str = "ExtrinsicFailed"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "`:code` was updated."] - pub struct CodeUpdated; - impl ::subxt::events::StaticEvent for CodeUpdated { - const PALLET: &'static str = "System"; - const EVENT: &'static str = "CodeUpdated"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "A new account was created."] - pub struct NewAccount { - pub account: ::sp_core::crypto::AccountId32, - } - impl ::subxt::events::StaticEvent for NewAccount { - const PALLET: &'static str = "System"; - const EVENT: &'static str = "NewAccount"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "An account was reaped."] - pub struct KilledAccount { - pub account: ::sp_core::crypto::AccountId32, - } - impl ::subxt::events::StaticEvent for KilledAccount { - const PALLET: &'static str = "System"; - const EVENT: &'static str = "KilledAccount"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "On on-chain remark happened."] - pub struct Remarked { - pub sender: ::sp_core::crypto::AccountId32, - pub hash: ::subxt::utils::H256, - } - impl ::subxt::events::StaticEvent for Remarked { - const PALLET: &'static str = "System"; - const EVENT: &'static str = "Remarked"; - } - } - pub mod storage { - use super::runtime_types; - pub struct StorageApi; - impl StorageApi { - #[doc = " The full account information for a particular account ID."] - pub fn account( - &self, - _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::frame_system::AccountInfo< - ::core::primitive::u32, - runtime_types::pallet_balances::types::AccountData< - ::core::primitive::u128, - >, - >, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "Account", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - )], - [ - 248u8, 178u8, 160u8, 222u8, 45u8, 231u8, 115u8, 164u8, 98u8, 184u8, - 174u8, 206u8, 149u8, 190u8, 175u8, 34u8, 202u8, 230u8, 69u8, 218u8, - 83u8, 43u8, 170u8, 41u8, 106u8, 77u8, 233u8, 97u8, 114u8, 14u8, 155u8, - 131u8, - ], - ) - } - #[doc = " The full account information for a particular account ID."] - pub fn account_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::frame_system::AccountInfo< - ::core::primitive::u32, - runtime_types::pallet_balances::types::AccountData< - ::core::primitive::u128, - >, - >, - >, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "Account", - Vec::new(), - [ - 248u8, 178u8, 160u8, 222u8, 45u8, 231u8, 115u8, 164u8, 98u8, 184u8, - 174u8, 206u8, 149u8, 190u8, 175u8, 34u8, 202u8, 230u8, 69u8, 218u8, - 83u8, 43u8, 170u8, 41u8, 106u8, 77u8, 233u8, 97u8, 114u8, 14u8, 155u8, - 131u8, - ], - ) - } - #[doc = " Total extrinsics count for the current block."] - pub fn extrinsic_count( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "ExtrinsicCount", - vec![], - [ - 223u8, 60u8, 201u8, 120u8, 36u8, 44u8, 180u8, 210u8, 242u8, 53u8, - 222u8, 154u8, 123u8, 176u8, 249u8, 8u8, 225u8, 28u8, 232u8, 4u8, 136u8, - 41u8, 151u8, 82u8, 189u8, 149u8, 49u8, 166u8, 139u8, 9u8, 163u8, 231u8, - ], - ) - } - #[doc = " The current weight for the block."] - pub fn block_weight( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::frame_support::dispatch::PerDispatchClass< - ::sp_weights::Weight, - >, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "BlockWeight", - vec![], - [ - 120u8, 67u8, 71u8, 163u8, 36u8, 202u8, 52u8, 106u8, 143u8, 155u8, - 144u8, 87u8, 142u8, 241u8, 232u8, 183u8, 56u8, 235u8, 27u8, 237u8, - 20u8, 202u8, 33u8, 85u8, 189u8, 0u8, 28u8, 52u8, 198u8, 40u8, 219u8, - 54u8, - ], - ) - } - #[doc = " Total length (in bytes) for all extrinsics put together, for the current block."] - pub fn all_extrinsics_len( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "AllExtrinsicsLen", - vec![], - [ - 202u8, 145u8, 209u8, 225u8, 40u8, 220u8, 174u8, 74u8, 93u8, 164u8, - 254u8, 248u8, 254u8, 192u8, 32u8, 117u8, 96u8, 149u8, 53u8, 145u8, - 219u8, 64u8, 234u8, 18u8, 217u8, 200u8, 203u8, 141u8, 145u8, 28u8, - 134u8, 60u8, - ], - ) - } - #[doc = " Map of block numbers to block hashes."] - pub fn block_hash( - &self, - _0: impl ::std::borrow::Borrow<::core::primitive::u32>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::subxt::utils::H256>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "BlockHash", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Twox64Concat, - )], - [ - 50u8, 112u8, 176u8, 239u8, 175u8, 18u8, 205u8, 20u8, 241u8, 195u8, - 21u8, 228u8, 186u8, 57u8, 200u8, 25u8, 38u8, 44u8, 106u8, 20u8, 168u8, - 80u8, 76u8, 235u8, 12u8, 51u8, 137u8, 149u8, 200u8, 4u8, 220u8, 237u8, - ], - ) - } - #[doc = " Map of block numbers to block hashes."] - pub fn block_hash_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::subxt::utils::H256>, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "BlockHash", - Vec::new(), - [ - 50u8, 112u8, 176u8, 239u8, 175u8, 18u8, 205u8, 20u8, 241u8, 195u8, - 21u8, 228u8, 186u8, 57u8, 200u8, 25u8, 38u8, 44u8, 106u8, 20u8, 168u8, - 80u8, 76u8, 235u8, 12u8, 51u8, 137u8, 149u8, 200u8, 4u8, 220u8, 237u8, - ], - ) - } - #[doc = " Extrinsics data for the current block (maps an extrinsic's index to its data)."] - pub fn extrinsic_data( - &self, - _0: impl ::std::borrow::Borrow<::core::primitive::u32>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "ExtrinsicData", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Twox64Concat, - )], - [ - 210u8, 224u8, 211u8, 186u8, 118u8, 210u8, 185u8, 194u8, 238u8, 211u8, - 254u8, 73u8, 67u8, 184u8, 31u8, 229u8, 168u8, 125u8, 98u8, 23u8, 241u8, - 59u8, 49u8, 86u8, 126u8, 9u8, 114u8, 163u8, 160u8, 62u8, 50u8, 67u8, - ], - ) - } - #[doc = " Extrinsics data for the current block (maps an extrinsic's index to its data)."] - pub fn extrinsic_data_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "ExtrinsicData", - Vec::new(), - [ - 210u8, 224u8, 211u8, 186u8, 118u8, 210u8, 185u8, 194u8, 238u8, 211u8, - 254u8, 73u8, 67u8, 184u8, 31u8, 229u8, 168u8, 125u8, 98u8, 23u8, 241u8, - 59u8, 49u8, 86u8, 126u8, 9u8, 114u8, 163u8, 160u8, 62u8, 50u8, 67u8, - ], - ) - } - #[doc = " The current block number being processed. Set by `execute_block`."] - pub fn number( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "Number", - vec![], - [ - 228u8, 96u8, 102u8, 190u8, 252u8, 130u8, 239u8, 172u8, 126u8, 235u8, - 246u8, 139u8, 208u8, 15u8, 88u8, 245u8, 141u8, 232u8, 43u8, 204u8, - 36u8, 87u8, 211u8, 141u8, 187u8, 68u8, 236u8, 70u8, 193u8, 235u8, - 164u8, 191u8, - ], - ) - } - #[doc = " Hash of the previous block."] - pub fn parent_hash( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::subxt::utils::H256>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "ParentHash", - vec![], - [ - 232u8, 206u8, 177u8, 119u8, 38u8, 57u8, 233u8, 50u8, 225u8, 49u8, - 169u8, 176u8, 210u8, 51u8, 231u8, 176u8, 234u8, 186u8, 188u8, 112u8, - 15u8, 152u8, 195u8, 232u8, 201u8, 97u8, 208u8, 249u8, 9u8, 163u8, 69u8, - 36u8, - ], - ) - } - #[doc = " Digest of the current block, also part of the block header."] - pub fn digest( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::sp_runtime::generic::Digest>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "Digest", - vec![], - [ - 83u8, 141u8, 200u8, 132u8, 182u8, 55u8, 197u8, 122u8, 13u8, 159u8, - 31u8, 42u8, 60u8, 191u8, 89u8, 221u8, 242u8, 47u8, 199u8, 213u8, 48u8, - 216u8, 131u8, 168u8, 245u8, 82u8, 56u8, 190u8, 62u8, 69u8, 96u8, 37u8, - ], - ) - } - #[doc = " Events deposited for the current block."] - #[doc = ""] - #[doc = " NOTE: The item is unbound and should therefore never be read on chain."] - #[doc = " It could otherwise inflate the PoV size of a block."] - #[doc = ""] - #[doc = " Events have a large in-memory size. Box the events to not go out-of-memory"] - #[doc = " just in case someone still reads them from within the runtime."] - pub fn events( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - ::std::vec::Vec< - runtime_types::frame_system::EventRecord< - runtime_types::rialto_parachain_runtime::RuntimeEvent, - ::subxt::utils::H256, - >, - >, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "Events", - vec![], - [ - 76u8, 19u8, 44u8, 121u8, 59u8, 168u8, 101u8, 101u8, 248u8, 3u8, 172u8, - 27u8, 249u8, 200u8, 147u8, 17u8, 4u8, 102u8, 186u8, 6u8, 152u8, 62u8, - 76u8, 195u8, 45u8, 188u8, 191u8, 30u8, 134u8, 78u8, 199u8, 93u8, - ], - ) - } - #[doc = " The number of events in the `Events` list."] - pub fn event_count( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "EventCount", - vec![], - [ - 236u8, 93u8, 90u8, 177u8, 250u8, 211u8, 138u8, 187u8, 26u8, 208u8, - 203u8, 113u8, 221u8, 233u8, 227u8, 9u8, 249u8, 25u8, 202u8, 185u8, - 161u8, 144u8, 167u8, 104u8, 127u8, 187u8, 38u8, 18u8, 52u8, 61u8, 66u8, - 112u8, - ], - ) - } - #[doc = " Mapping between a topic (represented by T::Hash) and a vector of indexes"] - #[doc = " of events in the `>` list."] - #[doc = ""] - #[doc = " All topic vectors have deterministic storage locations depending on the topic. This"] - #[doc = " allows light-clients to leverage the changes trie storage tracking mechanism and"] - #[doc = " in case of changes fetch the list of events of interest."] - #[doc = ""] - #[doc = " The value has the type `(T::BlockNumber, EventIndex)` because if we used only just"] - #[doc = " the `EventIndex` then in case if the topic has the same contents on the next block"] - #[doc = " no notification will be triggered thus the event might be lost."] - pub fn event_topics( - &self, - _0: impl ::std::borrow::Borrow<::subxt::utils::H256>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - ::std::vec::Vec<(::core::primitive::u32, ::core::primitive::u32)>, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "EventTopics", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - )], - [ - 205u8, 90u8, 142u8, 190u8, 176u8, 37u8, 94u8, 82u8, 98u8, 1u8, 129u8, - 63u8, 246u8, 101u8, 130u8, 58u8, 216u8, 16u8, 139u8, 196u8, 154u8, - 111u8, 110u8, 178u8, 24u8, 44u8, 183u8, 176u8, 232u8, 82u8, 223u8, - 38u8, - ], - ) - } - #[doc = " Mapping between a topic (represented by T::Hash) and a vector of indexes"] - #[doc = " of events in the `>` list."] - #[doc = ""] - #[doc = " All topic vectors have deterministic storage locations depending on the topic. This"] - #[doc = " allows light-clients to leverage the changes trie storage tracking mechanism and"] - #[doc = " in case of changes fetch the list of events of interest."] - #[doc = ""] - #[doc = " The value has the type `(T::BlockNumber, EventIndex)` because if we used only just"] - #[doc = " the `EventIndex` then in case if the topic has the same contents on the next block"] - #[doc = " no notification will be triggered thus the event might be lost."] - pub fn event_topics_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - ::std::vec::Vec<(::core::primitive::u32, ::core::primitive::u32)>, - >, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "EventTopics", - Vec::new(), - [ - 205u8, 90u8, 142u8, 190u8, 176u8, 37u8, 94u8, 82u8, 98u8, 1u8, 129u8, - 63u8, 246u8, 101u8, 130u8, 58u8, 216u8, 16u8, 139u8, 196u8, 154u8, - 111u8, 110u8, 178u8, 24u8, 44u8, 183u8, 176u8, 232u8, 82u8, 223u8, - 38u8, - ], - ) - } - #[doc = " Stores the `spec_version` and `spec_name` of when the last runtime upgrade happened."] - pub fn last_runtime_upgrade( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::frame_system::LastRuntimeUpgradeInfo, - >, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "LastRuntimeUpgrade", - vec![], - [ - 52u8, 37u8, 117u8, 111u8, 57u8, 130u8, 196u8, 14u8, 99u8, 77u8, 91u8, - 126u8, 178u8, 249u8, 78u8, 34u8, 9u8, 194u8, 92u8, 105u8, 113u8, 81u8, - 185u8, 127u8, 245u8, 184u8, 60u8, 29u8, 234u8, 182u8, 96u8, 196u8, - ], - ) - } - #[doc = " True if we have upgraded so that `type RefCount` is `u32`. False (default) if not."] - pub fn upgraded_to_u32_ref_count( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "UpgradedToU32RefCount", - vec![], - [ - 171u8, 88u8, 244u8, 92u8, 122u8, 67u8, 27u8, 18u8, 59u8, 175u8, 175u8, - 178u8, 20u8, 150u8, 213u8, 59u8, 222u8, 141u8, 32u8, 107u8, 3u8, 114u8, - 83u8, 250u8, 180u8, 233u8, 152u8, 54u8, 187u8, 99u8, 131u8, 204u8, - ], - ) - } - #[doc = " True if we have upgraded so that AccountInfo contains three types of `RefCount`. False"] - #[doc = " (default) if not."] - pub fn upgraded_to_triple_ref_count( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "UpgradedToTripleRefCount", - vec![], - [ - 90u8, 33u8, 56u8, 86u8, 90u8, 101u8, 89u8, 133u8, 203u8, 56u8, 201u8, - 210u8, 244u8, 232u8, 150u8, 18u8, 51u8, 105u8, 14u8, 230u8, 103u8, - 155u8, 246u8, 99u8, 53u8, 207u8, 225u8, 128u8, 186u8, 76u8, 40u8, - 185u8, - ], - ) - } - #[doc = " The execution phase of the block."] - pub fn execution_phase( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "System", - "ExecutionPhase", - vec![], - [ - 230u8, 183u8, 221u8, 135u8, 226u8, 223u8, 55u8, 104u8, 138u8, 224u8, - 103u8, 156u8, 222u8, 99u8, 203u8, 199u8, 164u8, 168u8, 193u8, 133u8, - 201u8, 155u8, 63u8, 95u8, 17u8, 206u8, 165u8, 123u8, 161u8, 33u8, - 172u8, 93u8, - ], - ) - } - } - } - pub mod constants { - use super::runtime_types; - pub struct ConstantsApi; - impl ConstantsApi { - #[doc = " Block & extrinsics weights: base values and limits."] - pub fn block_weights( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::frame_system::limits::BlockWeights, - >, - > { - ::subxt::constants::StaticConstantAddress::new( - "System", - "BlockWeights", - [ - 118u8, 253u8, 239u8, 217u8, 145u8, 115u8, 85u8, 86u8, 172u8, 248u8, - 139u8, 32u8, 158u8, 126u8, 172u8, 188u8, 197u8, 105u8, 145u8, 235u8, - 171u8, 50u8, 31u8, 225u8, 167u8, 187u8, 241u8, 87u8, 6u8, 17u8, 234u8, - 185u8, - ], - ) - } - #[doc = " The maximum length of a block (in bytes)."] - pub fn block_length( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::frame_system::limits::BlockLength, - >, - > { - ::subxt::constants::StaticConstantAddress::new( - "System", - "BlockLength", - [ - 116u8, 184u8, 225u8, 228u8, 207u8, 203u8, 4u8, 220u8, 234u8, 198u8, - 150u8, 108u8, 205u8, 87u8, 194u8, 131u8, 229u8, 51u8, 140u8, 4u8, 47u8, - 12u8, 200u8, 144u8, 153u8, 62u8, 51u8, 39u8, 138u8, 205u8, 203u8, - 236u8, - ], - ) - } - #[doc = " Maximum number of block number to block hash mappings to keep (oldest pruned first)."] - pub fn block_hash_count( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - > { - ::subxt::constants::StaticConstantAddress::new( - "System", - "BlockHashCount", - [ - 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, - 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, - 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, - 145u8, - ], - ) - } - #[doc = " The weight of runtime database operations the runtime can invoke."] - pub fn db_weight( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType, - > { - ::subxt::constants::StaticConstantAddress::new( - "System", - "DbWeight", - [ - 124u8, 162u8, 190u8, 149u8, 49u8, 177u8, 162u8, 231u8, 62u8, 167u8, - 199u8, 181u8, 43u8, 232u8, 185u8, 116u8, 195u8, 51u8, 233u8, 223u8, - 20u8, 129u8, 246u8, 13u8, 65u8, 180u8, 64u8, 9u8, 157u8, 59u8, 245u8, - 118u8, - ], - ) - } - #[doc = " Get the chain's current version."] - pub fn version( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType, - > { - ::subxt::constants::StaticConstantAddress::new( - "System", - "Version", - [ - 93u8, 98u8, 57u8, 243u8, 229u8, 8u8, 234u8, 231u8, 72u8, 230u8, 139u8, - 47u8, 63u8, 181u8, 17u8, 2u8, 220u8, 231u8, 104u8, 237u8, 185u8, 143u8, - 165u8, 253u8, 188u8, 76u8, 147u8, 12u8, 170u8, 26u8, 74u8, 200u8, - ], - ) - } - #[doc = " The designated SS58 prefix of this chain."] - #[doc = ""] - #[doc = " This replaces the \"ss58Format\" property declared in the chain spec. Reason is"] - #[doc = " that the runtime should know about the prefix in order to make use of it as"] - #[doc = " an identifier of the chain."] - pub fn ss58_prefix( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u16>, - > { - ::subxt::constants::StaticConstantAddress::new( - "System", - "SS58Prefix", - [ - 116u8, 33u8, 2u8, 170u8, 181u8, 147u8, 171u8, 169u8, 167u8, 227u8, - 41u8, 144u8, 11u8, 236u8, 82u8, 100u8, 74u8, 60u8, 184u8, 72u8, 169u8, - 90u8, 208u8, 135u8, 15u8, 117u8, 10u8, 123u8, 128u8, 193u8, 29u8, 70u8, - ], - ) - } - } - } - } - pub mod timestamp { - use super::{root_mod, runtime_types}; - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] - pub mod calls { - use super::{root_mod, runtime_types}; - type DispatchError = runtime_types::sp_runtime::DispatchError; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct Set { - #[codec(compact)] - pub now: ::core::primitive::u64, - } - pub struct TransactionApi; - impl TransactionApi { - #[doc = "Set the current time."] - #[doc = ""] - #[doc = "This call should be invoked exactly once per block. It will panic at the finalization"] - #[doc = "phase, if this call hasn't been invoked by that time."] - #[doc = ""] - #[doc = "The timestamp should be greater than the previous one by the amount specified by"] - #[doc = "`MinimumPeriod`."] - #[doc = ""] - #[doc = "The dispatch origin for this call must be `Inherent`."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`)"] - #[doc = "- 1 storage read and 1 storage mutation (codec `O(1)`). (because of `DidUpdate::take` in"] - #[doc = " `on_finalize`)"] - #[doc = "- 1 event handler `on_timestamp_set`. Must be `O(1)`."] - pub fn set( - &self, - now: ::core::primitive::u64, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "Timestamp", - "set", - Set { now }, - [ - 6u8, 97u8, 172u8, 236u8, 118u8, 238u8, 228u8, 114u8, 15u8, 115u8, - 102u8, 85u8, 66u8, 151u8, 16u8, 33u8, 187u8, 17u8, 166u8, 88u8, 127u8, - 214u8, 182u8, 51u8, 168u8, 88u8, 43u8, 101u8, 185u8, 8u8, 1u8, 28u8, - ], - ) - } - } - } - pub mod storage { - use super::runtime_types; - pub struct StorageApi; - impl StorageApi { - #[doc = " Current time for the current block."] - pub fn now( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u64>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Timestamp", - "Now", - vec![], - [ - 148u8, 53u8, 50u8, 54u8, 13u8, 161u8, 57u8, 150u8, 16u8, 83u8, 144u8, - 221u8, 59u8, 75u8, 158u8, 130u8, 39u8, 123u8, 106u8, 134u8, 202u8, - 185u8, 83u8, 85u8, 60u8, 41u8, 120u8, 96u8, 210u8, 34u8, 2u8, 250u8, - ], - ) - } - #[doc = " Did the timestamp get updated in this block?"] - pub fn did_update( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Timestamp", - "DidUpdate", - vec![], - [ - 70u8, 13u8, 92u8, 186u8, 80u8, 151u8, 167u8, 90u8, 158u8, 232u8, 175u8, - 13u8, 103u8, 135u8, 2u8, 78u8, 16u8, 6u8, 39u8, 158u8, 167u8, 85u8, - 27u8, 47u8, 122u8, 73u8, 127u8, 26u8, 35u8, 168u8, 72u8, 204u8, - ], - ) - } - } - } - pub mod constants { - use super::runtime_types; - pub struct ConstantsApi; - impl ConstantsApi { - #[doc = " The minimum period between blocks. Beware that this is different to the *expected*"] - #[doc = " period that the block production apparatus provides. Your chosen consensus system will"] - #[doc = " generally work with this to determine a sensible block time. e.g. For Aura, it will be"] - #[doc = " double this period on default settings."] - pub fn minimum_period( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u64>, - > { - ::subxt::constants::StaticConstantAddress::new( - "Timestamp", - "MinimumPeriod", - [ - 128u8, 214u8, 205u8, 242u8, 181u8, 142u8, 124u8, 231u8, 190u8, 146u8, - 59u8, 226u8, 157u8, 101u8, 103u8, 117u8, 249u8, 65u8, 18u8, 191u8, - 103u8, 119u8, 53u8, 85u8, 81u8, 96u8, 220u8, 42u8, 184u8, 239u8, 42u8, - 246u8, - ], - ) - } - } - } - } - pub mod sudo { - use super::{root_mod, runtime_types}; - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] - pub mod calls { - use super::{root_mod, runtime_types}; - type DispatchError = runtime_types::sp_runtime::DispatchError; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct Sudo { - pub call: ::std::boxed::Box, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SudoUncheckedWeight { - pub call: ::std::boxed::Box, - pub weight: ::sp_weights::Weight, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SetKey { - pub new: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SudoAs { - pub who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - pub call: ::std::boxed::Box, - } - pub struct TransactionApi; - impl TransactionApi { - #[doc = "Authenticates the sudo key and dispatches a function call with `Root` origin."] - #[doc = ""] - #[doc = "The dispatch origin for this call must be _Signed_."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- O(1)."] - pub fn sudo( - &self, - call: runtime_types::rialto_parachain_runtime::RuntimeCall, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "Sudo", - "sudo", - Sudo { call: ::std::boxed::Box::new(call) }, - [ - 19u8, 178u8, 172u8, 30u8, 28u8, 209u8, 160u8, 61u8, 76u8, 239u8, 71u8, - 124u8, 21u8, 34u8, 233u8, 176u8, 100u8, 90u8, 198u8, 118u8, 117u8, 2u8, - 147u8, 7u8, 109u8, 1u8, 32u8, 35u8, 99u8, 0u8, 107u8, 145u8, - ], - ) - } - #[doc = "Authenticates the sudo key and dispatches a function call with `Root` origin."] - #[doc = "This function does not check the weight of the call, and instead allows the"] - #[doc = "Sudo user to specify the weight of the call."] - #[doc = ""] - #[doc = "The dispatch origin for this call must be _Signed_."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- O(1)."] - pub fn sudo_unchecked_weight( - &self, - call: runtime_types::rialto_parachain_runtime::RuntimeCall, - weight: ::sp_weights::Weight, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "Sudo", - "sudo_unchecked_weight", - SudoUncheckedWeight { call: ::std::boxed::Box::new(call), weight }, - [ - 145u8, 160u8, 12u8, 105u8, 228u8, 240u8, 115u8, 105u8, 220u8, 99u8, - 215u8, 228u8, 115u8, 71u8, 109u8, 28u8, 149u8, 247u8, 159u8, 216u8, - 76u8, 71u8, 68u8, 87u8, 254u8, 146u8, 185u8, 174u8, 251u8, 209u8, 72u8, - 122u8, - ], - ) - } - #[doc = "Authenticates the current sudo key and sets the given AccountId (`new`) as the new sudo"] - #[doc = "key."] - #[doc = ""] - #[doc = "The dispatch origin for this call must be _Signed_."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- O(1)."] - pub fn set_key( - &self, - new: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "Sudo", - "set_key", - SetKey { new }, - [ - 23u8, 224u8, 218u8, 169u8, 8u8, 28u8, 111u8, 199u8, 26u8, 88u8, 225u8, - 105u8, 17u8, 19u8, 87u8, 156u8, 97u8, 67u8, 89u8, 173u8, 70u8, 0u8, - 5u8, 246u8, 198u8, 135u8, 182u8, 180u8, 44u8, 9u8, 212u8, 95u8, - ], - ) - } - #[doc = "Authenticates the sudo key and dispatches a function call with `Signed` origin from"] - #[doc = "a given account."] - #[doc = ""] - #[doc = "The dispatch origin for this call must be _Signed_."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- O(1)."] - pub fn sudo_as( - &self, - who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - call: runtime_types::rialto_parachain_runtime::RuntimeCall, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "Sudo", - "sudo_as", - SudoAs { who, call: ::std::boxed::Box::new(call) }, - [ - 229u8, 45u8, 129u8, 96u8, 54u8, 29u8, 159u8, 77u8, 210u8, 144u8, 29u8, - 97u8, 127u8, 133u8, 122u8, 110u8, 152u8, 194u8, 211u8, 246u8, 97u8, - 197u8, 187u8, 203u8, 46u8, 12u8, 104u8, 125u8, 15u8, 226u8, 28u8, - 183u8, - ], - ) - } - } - } - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] - pub type Event = runtime_types::pallet_sudo::pallet::Event; - pub mod events { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "A sudo just took place. \\[result\\]"] - pub struct Sudid { - pub sudo_result: - ::core::result::Result<(), runtime_types::sp_runtime::DispatchError>, - } - impl ::subxt::events::StaticEvent for Sudid { - const PALLET: &'static str = "Sudo"; - const EVENT: &'static str = "Sudid"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "The \\[sudoer\\] just switched identity; the old key is supplied if one existed."] - pub struct KeyChanged { - pub old_sudoer: ::core::option::Option<::sp_core::crypto::AccountId32>, - } - impl ::subxt::events::StaticEvent for KeyChanged { - const PALLET: &'static str = "Sudo"; - const EVENT: &'static str = "KeyChanged"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "A sudo just took place. \\[result\\]"] - pub struct SudoAsDone { - pub sudo_result: - ::core::result::Result<(), runtime_types::sp_runtime::DispatchError>, - } - impl ::subxt::events::StaticEvent for SudoAsDone { - const PALLET: &'static str = "Sudo"; - const EVENT: &'static str = "SudoAsDone"; - } - } - pub mod storage { - use super::runtime_types; - pub struct StorageApi; - impl StorageApi { - #[doc = " The `AccountId` of the sudo key."] - pub fn key( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::sp_core::crypto::AccountId32>, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Sudo", - "Key", - vec![], - [ - 244u8, 73u8, 188u8, 136u8, 218u8, 163u8, 68u8, 179u8, 122u8, 173u8, - 34u8, 108u8, 137u8, 28u8, 182u8, 16u8, 196u8, 92u8, 138u8, 34u8, 102u8, - 80u8, 199u8, 88u8, 107u8, 207u8, 36u8, 22u8, 168u8, 167u8, 20u8, 142u8, - ], - ) - } - } - } - } - pub mod transaction_payment { - use super::{root_mod, runtime_types}; - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] - pub type Event = runtime_types::pallet_transaction_payment::pallet::Event; - pub mod events { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "A transaction fee `actual_fee`, of which `tip` was added to the minimum inclusion fee,"] - #[doc = "has been paid by `who`."] - pub struct TransactionFeePaid { - pub who: ::sp_core::crypto::AccountId32, - pub actual_fee: ::core::primitive::u128, - pub tip: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for TransactionFeePaid { - const PALLET: &'static str = "TransactionPayment"; - const EVENT: &'static str = "TransactionFeePaid"; - } - } - pub mod storage { - use super::runtime_types; - pub struct StorageApi; - impl StorageApi { - pub fn next_fee_multiplier( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::sp_arithmetic::fixed_point::FixedU128, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "TransactionPayment", - "NextFeeMultiplier", - vec![], - [ - 210u8, 0u8, 206u8, 165u8, 183u8, 10u8, 206u8, 52u8, 14u8, 90u8, 218u8, - 197u8, 189u8, 125u8, 113u8, 216u8, 52u8, 161u8, 45u8, 24u8, 245u8, - 237u8, 121u8, 41u8, 106u8, 29u8, 45u8, 129u8, 250u8, 203u8, 206u8, - 180u8, - ], - ) - } - pub fn storage_version( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::pallet_transaction_payment::Releases, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "TransactionPayment", - "StorageVersion", - vec![], - [ - 219u8, 243u8, 82u8, 176u8, 65u8, 5u8, 132u8, 114u8, 8u8, 82u8, 176u8, - 200u8, 97u8, 150u8, 177u8, 164u8, 166u8, 11u8, 34u8, 12u8, 12u8, 198u8, - 58u8, 191u8, 186u8, 221u8, 221u8, 119u8, 181u8, 253u8, 154u8, 228u8, - ], - ) - } - } - } - pub mod constants { - use super::runtime_types; - pub struct ConstantsApi; - impl ConstantsApi { - #[doc = " A fee mulitplier for `Operational` extrinsics to compute \"virtual tip\" to boost their"] - #[doc = " `priority`"] - #[doc = ""] - #[doc = " This value is multipled by the `final_fee` to obtain a \"virtual tip\" that is later"] - #[doc = " added to a tip component in regular `priority` calculations."] - #[doc = " It means that a `Normal` transaction can front-run a similarly-sized `Operational`"] - #[doc = " extrinsic (with no tip), by including a tip value greater than the virtual tip."] - #[doc = ""] - #[doc = " ```rust,ignore"] - #[doc = " // For `Normal`"] - #[doc = " let priority = priority_calc(tip);"] - #[doc = ""] - #[doc = " // For `Operational`"] - #[doc = " let virtual_tip = (inclusion_fee + tip) * OperationalFeeMultiplier;"] - #[doc = " let priority = priority_calc(tip + virtual_tip);"] - #[doc = " ```"] - #[doc = ""] - #[doc = " Note that since we use `final_fee` the multiplier applies also to the regular `tip`"] - #[doc = " sent with the transaction. So, not only does the transaction get a priority bump based"] - #[doc = " on the `inclusion_fee`, but we also amplify the impact of tips applied to `Operational`"] - #[doc = " transactions."] - pub fn operational_fee_multiplier( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u8>, - > { - ::subxt::constants::StaticConstantAddress::new( - "TransactionPayment", - "OperationalFeeMultiplier", - [ - 141u8, 130u8, 11u8, 35u8, 226u8, 114u8, 92u8, 179u8, 168u8, 110u8, - 28u8, 91u8, 221u8, 64u8, 4u8, 148u8, 201u8, 193u8, 185u8, 66u8, 226u8, - 114u8, 97u8, 79u8, 62u8, 212u8, 202u8, 114u8, 237u8, 228u8, 183u8, - 165u8, - ], - ) - } - } - } - } - pub mod parachain_system { - use super::{root_mod, runtime_types}; - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] - pub mod calls { - use super::{root_mod, runtime_types}; - type DispatchError = runtime_types::sp_runtime::DispatchError; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SetValidationData { - pub data: - runtime_types::cumulus_primitives_parachain_inherent::ParachainInherentData, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SudoSendUpwardMessage { - pub message: ::std::vec::Vec<::core::primitive::u8>, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct AuthorizeUpgrade { - pub code_hash: ::subxt::utils::H256, - pub check_version: ::core::primitive::bool, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct EnactAuthorizedUpgrade { - pub code: ::std::vec::Vec<::core::primitive::u8>, - } - pub struct TransactionApi; - impl TransactionApi { - #[doc = "Set the current validation data."] - #[doc = ""] - #[doc = "This should be invoked exactly once per block. It will panic at the finalization"] - #[doc = "phase if the call was not invoked."] - #[doc = ""] - #[doc = "The dispatch origin for this call must be `Inherent`"] - #[doc = ""] - #[doc = "As a side effect, this function upgrades the current validation function"] - #[doc = "if the appropriate time has come."] - pub fn set_validation_data( - &self, - data : runtime_types :: cumulus_primitives_parachain_inherent :: ParachainInherentData, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "ParachainSystem", - "set_validation_data", - SetValidationData { data }, - [ - 200u8, 80u8, 163u8, 177u8, 184u8, 117u8, 61u8, 203u8, 244u8, 214u8, - 106u8, 151u8, 128u8, 131u8, 254u8, 120u8, 254u8, 76u8, 104u8, 39u8, - 215u8, 227u8, 233u8, 254u8, 26u8, 62u8, 17u8, 42u8, 19u8, 127u8, 108u8, - 242u8, - ], - ) - } - pub fn sudo_send_upward_message( - &self, - message: ::std::vec::Vec<::core::primitive::u8>, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "ParachainSystem", - "sudo_send_upward_message", - SudoSendUpwardMessage { message }, - [ - 127u8, 79u8, 45u8, 183u8, 190u8, 205u8, 184u8, 169u8, 255u8, 191u8, - 86u8, 154u8, 134u8, 25u8, 249u8, 63u8, 47u8, 194u8, 108u8, 62u8, 60u8, - 170u8, 81u8, 240u8, 113u8, 48u8, 181u8, 171u8, 95u8, 63u8, 26u8, 222u8, - ], - ) - } - #[doc = "Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied"] - #[doc = "later."] - #[doc = ""] - #[doc = "The `check_version` parameter sets a boolean flag for whether or not the runtime's spec"] - #[doc = "version and name should be verified on upgrade. Since the authorization only has a hash,"] - #[doc = "it cannot actually perform the verification."] - #[doc = ""] - #[doc = "This call requires Root origin."] - pub fn authorize_upgrade( - &self, - code_hash: ::subxt::utils::H256, - check_version: ::core::primitive::bool, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "ParachainSystem", - "authorize_upgrade", - AuthorizeUpgrade { code_hash, check_version }, - [ - 208u8, 115u8, 62u8, 35u8, 70u8, 223u8, 65u8, 57u8, 216u8, 44u8, 169u8, - 249u8, 90u8, 112u8, 17u8, 208u8, 30u8, 131u8, 102u8, 131u8, 240u8, - 217u8, 230u8, 214u8, 145u8, 198u8, 55u8, 13u8, 217u8, 51u8, 178u8, - 141u8, - ], - ) - } - #[doc = "Provide the preimage (runtime binary) `code` for an upgrade that has been authorized."] - #[doc = ""] - #[doc = "If the authorization required a version check, this call will ensure the spec name"] - #[doc = "remains unchanged and that the spec version has increased."] - #[doc = ""] - #[doc = "Note that this function will not apply the new `code`, but only attempt to schedule the"] - #[doc = "upgrade with the Relay Chain."] - #[doc = ""] - #[doc = "All origins are allowed."] - pub fn enact_authorized_upgrade( - &self, - code: ::std::vec::Vec<::core::primitive::u8>, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "ParachainSystem", - "enact_authorized_upgrade", - EnactAuthorizedUpgrade { code }, - [ - 43u8, 157u8, 1u8, 230u8, 134u8, 72u8, 230u8, 35u8, 159u8, 13u8, 201u8, - 134u8, 184u8, 94u8, 167u8, 13u8, 108u8, 157u8, 145u8, 166u8, 119u8, - 37u8, 51u8, 121u8, 252u8, 255u8, 48u8, 251u8, 126u8, 152u8, 247u8, 5u8, - ], - ) - } - } - } - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] - pub type Event = runtime_types::cumulus_pallet_parachain_system::pallet::Event; - pub mod events { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "The validation function has been scheduled to apply."] - pub struct ValidationFunctionStored; - impl ::subxt::events::StaticEvent for ValidationFunctionStored { - const PALLET: &'static str = "ParachainSystem"; - const EVENT: &'static str = "ValidationFunctionStored"; - } - #[derive( - :: subxt :: ext :: codec :: CompactAs, - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "The validation function was applied as of the contained relay chain block number."] - pub struct ValidationFunctionApplied { - pub relay_chain_block_num: ::core::primitive::u32, - } - impl ::subxt::events::StaticEvent for ValidationFunctionApplied { - const PALLET: &'static str = "ParachainSystem"; - const EVENT: &'static str = "ValidationFunctionApplied"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "The relay-chain aborted the upgrade process."] - pub struct ValidationFunctionDiscarded; - impl ::subxt::events::StaticEvent for ValidationFunctionDiscarded { - const PALLET: &'static str = "ParachainSystem"; - const EVENT: &'static str = "ValidationFunctionDiscarded"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "An upgrade has been authorized."] - pub struct UpgradeAuthorized { - pub code_hash: ::subxt::utils::H256, - } - impl ::subxt::events::StaticEvent for UpgradeAuthorized { - const PALLET: &'static str = "ParachainSystem"; - const EVENT: &'static str = "UpgradeAuthorized"; - } - #[derive( - :: subxt :: ext :: codec :: CompactAs, - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Some downward messages have been received and will be processed."] - pub struct DownwardMessagesReceived { - pub count: ::core::primitive::u32, - } - impl ::subxt::events::StaticEvent for DownwardMessagesReceived { - const PALLET: &'static str = "ParachainSystem"; - const EVENT: &'static str = "DownwardMessagesReceived"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Downward messages were processed using the given weight."] - pub struct DownwardMessagesProcessed { - pub weight_used: ::sp_weights::Weight, - pub dmq_head: ::subxt::utils::H256, - } - impl ::subxt::events::StaticEvent for DownwardMessagesProcessed { - const PALLET: &'static str = "ParachainSystem"; - const EVENT: &'static str = "DownwardMessagesProcessed"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "An upward message was sent to the relay chain."] - pub struct UpwardMessageSent { - pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, - } - impl ::subxt::events::StaticEvent for UpwardMessageSent { - const PALLET: &'static str = "ParachainSystem"; - const EVENT: &'static str = "UpwardMessageSent"; - } - } - pub mod storage { - use super::runtime_types; - pub struct StorageApi; - impl StorageApi { - #[doc = " In case of a scheduled upgrade, this storage field contains the validation code to be applied."] - #[doc = ""] - #[doc = " As soon as the relay chain gives us the go-ahead signal, we will overwrite the [`:code`][well_known_keys::CODE]"] - #[doc = " which will result the next block process with the new validation code. This concludes the upgrade process."] - #[doc = ""] - #[doc = " [well_known_keys::CODE]: sp_core::storage::well_known_keys::CODE"] - pub fn pending_validation_code( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "PendingValidationCode", - vec![], - [ - 162u8, 35u8, 108u8, 76u8, 160u8, 93u8, 215u8, 84u8, 20u8, 249u8, 57u8, - 187u8, 88u8, 161u8, 15u8, 131u8, 213u8, 89u8, 140u8, 20u8, 227u8, - 204u8, 79u8, 176u8, 114u8, 119u8, 8u8, 7u8, 64u8, 15u8, 90u8, 92u8, - ], - ) - } - #[doc = " Validation code that is set by the parachain and is to be communicated to collator and"] - #[doc = " consequently the relay-chain."] - #[doc = ""] - #[doc = " This will be cleared in `on_initialize` of each new block if no other pallet already set"] - #[doc = " the value."] - pub fn new_validation_code( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "NewValidationCode", - vec![], - [ - 224u8, 174u8, 53u8, 106u8, 240u8, 49u8, 48u8, 79u8, 219u8, 74u8, 142u8, - 166u8, 92u8, 204u8, 244u8, 200u8, 43u8, 169u8, 177u8, 207u8, 190u8, - 106u8, 180u8, 65u8, 245u8, 131u8, 134u8, 4u8, 53u8, 45u8, 76u8, 3u8, - ], - ) - } - #[doc = " The [`PersistedValidationData`] set for this block."] - #[doc = " This value is expected to be set only once per block and it's never stored"] - #[doc = " in the trie."] - pub fn validation_data( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::polkadot_primitives::v4::PersistedValidationData< - ::subxt::utils::H256, - ::core::primitive::u32, - >, - >, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "ValidationData", - vec![], - [ - 112u8, 58u8, 240u8, 81u8, 219u8, 110u8, 244u8, 186u8, 251u8, 90u8, - 195u8, 217u8, 229u8, 102u8, 233u8, 24u8, 109u8, 96u8, 219u8, 72u8, - 139u8, 93u8, 58u8, 140u8, 40u8, 110u8, 167u8, 98u8, 199u8, 12u8, 138u8, - 131u8, - ], - ) - } - #[doc = " Were the validation data set to notify the relay chain?"] - pub fn did_set_validation_code( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "DidSetValidationCode", - vec![], - [ - 89u8, 83u8, 74u8, 174u8, 234u8, 188u8, 149u8, 78u8, 140u8, 17u8, 92u8, - 165u8, 243u8, 87u8, 59u8, 97u8, 135u8, 81u8, 192u8, 86u8, 193u8, 187u8, - 113u8, 22u8, 108u8, 83u8, 242u8, 208u8, 174u8, 40u8, 49u8, 245u8, - ], - ) - } - #[doc = " The relay chain block number associated with the last parachain block."] - pub fn last_relay_chain_block_number( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "LastRelayChainBlockNumber", - vec![], - [ - 68u8, 121u8, 6u8, 159u8, 181u8, 94u8, 151u8, 215u8, 225u8, 244u8, 4u8, - 158u8, 216u8, 85u8, 55u8, 228u8, 197u8, 35u8, 200u8, 33u8, 29u8, 182u8, - 17u8, 83u8, 59u8, 63u8, 25u8, 180u8, 132u8, 23u8, 97u8, 252u8, - ], - ) - } - #[doc = " An option which indicates if the relay-chain restricts signalling a validation code upgrade."] - #[doc = " In other words, if this is `Some` and [`NewValidationCode`] is `Some` then the produced"] - #[doc = " candidate will be invalid."] - #[doc = ""] - #[doc = " This storage item is a mirror of the corresponding value for the current parachain from the"] - #[doc = " relay-chain. This value is ephemeral which means it doesn't hit the storage. This value is"] - #[doc = " set after the inherent."] - pub fn upgrade_restriction_signal( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - ::core::option::Option< - runtime_types::polkadot_primitives::v4::UpgradeRestriction, - >, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "UpgradeRestrictionSignal", - vec![], - [ - 61u8, 3u8, 26u8, 6u8, 88u8, 114u8, 109u8, 63u8, 7u8, 115u8, 245u8, - 198u8, 73u8, 234u8, 28u8, 228u8, 126u8, 27u8, 151u8, 18u8, 133u8, 54u8, - 144u8, 149u8, 246u8, 43u8, 83u8, 47u8, 77u8, 238u8, 10u8, 196u8, - ], - ) - } - #[doc = " The state proof for the last relay parent block."] - #[doc = ""] - #[doc = " This field is meant to be updated each block with the validation data inherent. Therefore,"] - #[doc = " before processing of the inherent, e.g. in `on_initialize` this data may be stale."] - #[doc = ""] - #[doc = " This data is also absent from the genesis."] - pub fn relay_state_proof( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::sp_trie::storage_proof::StorageProof, - >, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "RelayStateProof", - vec![], - [ - 35u8, 124u8, 167u8, 221u8, 162u8, 145u8, 158u8, 186u8, 57u8, 154u8, - 225u8, 6u8, 176u8, 13u8, 178u8, 195u8, 209u8, 122u8, 221u8, 26u8, - 155u8, 126u8, 153u8, 246u8, 101u8, 221u8, 61u8, 145u8, 211u8, 236u8, - 48u8, 130u8, - ], - ) - } - #[doc = " The snapshot of some state related to messaging relevant to the current parachain as per"] - #[doc = " the relay parent."] - #[doc = ""] - #[doc = " This field is meant to be updated each block with the validation data inherent. Therefore,"] - #[doc = " before processing of the inherent, e.g. in `on_initialize` this data may be stale."] - #[doc = ""] - #[doc = " This data is also absent from the genesis."] pub fn relevant_messaging_state (& self ,) -> :: subxt :: storage :: address :: StaticStorageAddress :: < :: subxt :: metadata :: DecodeStaticType < runtime_types :: cumulus_pallet_parachain_system :: relay_state_snapshot :: MessagingStateSnapshot > , :: subxt :: storage :: address :: Yes , () , () >{ - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "RelevantMessagingState", - vec![], - [ - 68u8, 241u8, 114u8, 83u8, 200u8, 99u8, 8u8, 244u8, 110u8, 134u8, 106u8, - 153u8, 17u8, 90u8, 184u8, 157u8, 100u8, 140u8, 157u8, 83u8, 25u8, - 166u8, 173u8, 31u8, 221u8, 24u8, 236u8, 85u8, 176u8, 223u8, 237u8, - 65u8, - ], - ) - } - #[doc = " The parachain host configuration that was obtained from the relay parent."] - #[doc = ""] - #[doc = " This field is meant to be updated each block with the validation data inherent. Therefore,"] - #[doc = " before processing of the inherent, e.g. in `on_initialize` this data may be stale."] - #[doc = ""] - #[doc = " This data is also absent from the genesis."] - pub fn host_configuration( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::polkadot_primitives::v4::AbridgedHostConfiguration, - >, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "HostConfiguration", - vec![], - [ - 104u8, 200u8, 30u8, 202u8, 119u8, 204u8, 233u8, 20u8, 67u8, 199u8, - 47u8, 166u8, 254u8, 152u8, 10u8, 187u8, 240u8, 255u8, 148u8, 201u8, - 134u8, 41u8, 130u8, 201u8, 112u8, 65u8, 68u8, 103u8, 56u8, 123u8, - 178u8, 113u8, - ], - ) - } - #[doc = " The last downward message queue chain head we have observed."] - #[doc = ""] - #[doc = " This value is loaded before and saved after processing inbound downward messages carried"] - #[doc = " by the system inherent."] - pub fn last_dmq_mqc_head( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::cumulus_primitives_parachain_inherent::MessageQueueChain, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "LastDmqMqcHead", - vec![], - [ - 176u8, 255u8, 246u8, 125u8, 36u8, 120u8, 24u8, 44u8, 26u8, 64u8, 236u8, - 210u8, 189u8, 237u8, 50u8, 78u8, 45u8, 139u8, 58u8, 141u8, 112u8, - 253u8, 178u8, 198u8, 87u8, 71u8, 77u8, 248u8, 21u8, 145u8, 187u8, 52u8, - ], - ) - } - #[doc = " The message queue chain heads we have observed per each channel incoming channel."] - #[doc = ""] - #[doc = " This value is loaded before and saved after processing inbound downward messages carried"] - #[doc = " by the system inherent."] - pub fn last_hrmp_mqc_heads( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - ::subxt::utils::KeyedVec< - runtime_types::polkadot_parachain::primitives::Id, - runtime_types::cumulus_primitives_parachain_inherent::MessageQueueChain, - >, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "LastHrmpMqcHeads", - vec![], - [ - 55u8, 179u8, 35u8, 16u8, 173u8, 0u8, 122u8, 179u8, 236u8, 98u8, 9u8, - 112u8, 11u8, 219u8, 241u8, 89u8, 131u8, 198u8, 64u8, 139u8, 103u8, - 158u8, 77u8, 107u8, 83u8, 236u8, 255u8, 208u8, 47u8, 61u8, 219u8, - 240u8, - ], - ) - } - #[doc = " Number of downward messages processed in a block."] - #[doc = ""] - #[doc = " This will be cleared in `on_initialize` of each new block."] - pub fn processed_downward_messages( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "ProcessedDownwardMessages", - vec![], - [ - 48u8, 177u8, 84u8, 228u8, 101u8, 235u8, 181u8, 27u8, 66u8, 55u8, 50u8, - 146u8, 245u8, 223u8, 77u8, 132u8, 178u8, 80u8, 74u8, 90u8, 166u8, 81u8, - 109u8, 25u8, 91u8, 69u8, 5u8, 69u8, 123u8, 197u8, 160u8, 146u8, - ], - ) - } - #[doc = " HRMP watermark that was set in a block."] - #[doc = ""] - #[doc = " This will be cleared in `on_initialize` of each new block."] - pub fn hrmp_watermark( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "HrmpWatermark", - vec![], - [ - 189u8, 59u8, 183u8, 195u8, 69u8, 185u8, 241u8, 226u8, 62u8, 204u8, - 230u8, 77u8, 102u8, 75u8, 86u8, 157u8, 249u8, 140u8, 219u8, 72u8, 94u8, - 64u8, 176u8, 72u8, 34u8, 205u8, 114u8, 103u8, 231u8, 233u8, 206u8, - 111u8, - ], - ) - } - #[doc = " HRMP messages that were sent in a block."] - #[doc = ""] - #[doc = " This will be cleared in `on_initialize` of each new block."] - pub fn hrmp_outbound_messages( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - ::std::vec::Vec< - runtime_types::polkadot_core_primitives::OutboundHrmpMessage< - runtime_types::polkadot_parachain::primitives::Id, - >, - >, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "HrmpOutboundMessages", - vec![], - [ - 74u8, 86u8, 173u8, 248u8, 90u8, 230u8, 71u8, 225u8, 127u8, 164u8, - 221u8, 62u8, 146u8, 13u8, 73u8, 9u8, 98u8, 168u8, 6u8, 14u8, 97u8, - 166u8, 45u8, 70u8, 62u8, 210u8, 9u8, 32u8, 83u8, 18u8, 4u8, 201u8, - ], - ) - } - #[doc = " Upward messages that were sent in a block."] - #[doc = ""] - #[doc = " This will be cleared in `on_initialize` of each new block."] - pub fn upward_messages( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "UpwardMessages", - vec![], - [ - 129u8, 208u8, 187u8, 36u8, 48u8, 108u8, 135u8, 56u8, 204u8, 60u8, - 100u8, 158u8, 113u8, 238u8, 46u8, 92u8, 228u8, 41u8, 178u8, 177u8, - 208u8, 195u8, 148u8, 149u8, 127u8, 21u8, 93u8, 92u8, 29u8, 115u8, 10u8, - 248u8, - ], - ) - } - #[doc = " Upward messages that are still pending and not yet send to the relay chain."] - pub fn pending_upward_messages( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "PendingUpwardMessages", - vec![], - [ - 223u8, 46u8, 224u8, 227u8, 222u8, 119u8, 225u8, 244u8, 59u8, 87u8, - 127u8, 19u8, 217u8, 237u8, 103u8, 61u8, 6u8, 210u8, 107u8, 201u8, - 117u8, 25u8, 85u8, 248u8, 36u8, 231u8, 28u8, 202u8, 41u8, 140u8, 208u8, - 254u8, - ], - ) - } - #[doc = " The number of HRMP messages we observed in `on_initialize` and thus used that number for"] - #[doc = " announcing the weight of `on_initialize` and `on_finalize`."] - pub fn announced_hrmp_messages_per_candidate( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "AnnouncedHrmpMessagesPerCandidate", - vec![], - [ - 132u8, 61u8, 162u8, 129u8, 251u8, 243u8, 20u8, 144u8, 162u8, 73u8, - 237u8, 51u8, 248u8, 41u8, 127u8, 171u8, 180u8, 79u8, 137u8, 23u8, 66u8, - 134u8, 106u8, 222u8, 182u8, 154u8, 0u8, 145u8, 184u8, 156u8, 36u8, - 97u8, - ], - ) - } - #[doc = " The weight we reserve at the beginning of the block for processing XCMP messages. This"] - #[doc = " overrides the amount set in the Config trait."] - pub fn reserved_xcmp_weight_override( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::sp_weights::Weight>, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "ReservedXcmpWeightOverride", - vec![], - [ - 180u8, 90u8, 34u8, 178u8, 1u8, 242u8, 211u8, 97u8, 100u8, 34u8, 39u8, - 42u8, 142u8, 249u8, 236u8, 194u8, 244u8, 164u8, 96u8, 54u8, 98u8, 46u8, - 92u8, 196u8, 185u8, 51u8, 231u8, 234u8, 249u8, 143u8, 244u8, 64u8, - ], - ) - } - #[doc = " The weight we reserve at the beginning of the block for processing DMP messages. This"] - #[doc = " overrides the amount set in the Config trait."] - pub fn reserved_dmp_weight_override( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::sp_weights::Weight>, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "ReservedDmpWeightOverride", - vec![], - [ - 90u8, 122u8, 168u8, 240u8, 95u8, 195u8, 160u8, 109u8, 175u8, 170u8, - 227u8, 44u8, 139u8, 176u8, 32u8, 161u8, 57u8, 233u8, 56u8, 55u8, 123u8, - 168u8, 174u8, 96u8, 159u8, 62u8, 186u8, 186u8, 17u8, 70u8, 57u8, 246u8, - ], - ) - } - #[doc = " The next authorized upgrade, if there is one."] - pub fn authorized_upgrade( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::cumulus_pallet_parachain_system::CodeUpgradeAuthorization, - >, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "AuthorizedUpgrade", - vec![], - [ - 12u8, 212u8, 71u8, 191u8, 89u8, 101u8, 195u8, 3u8, 23u8, 180u8, 233u8, - 52u8, 53u8, 133u8, 207u8, 94u8, 58u8, 43u8, 221u8, 236u8, 161u8, 41u8, - 30u8, 194u8, 125u8, 2u8, 118u8, 152u8, 197u8, 49u8, 34u8, 33u8, - ], - ) - } - #[doc = " A custom head data that should be returned as result of `validate_block`."] - #[doc = ""] - #[doc = " See [`Pallet::set_custom_validation_head_data`] for more information."] - pub fn custom_validation_head_data( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainSystem", - "CustomValidationHeadData", - vec![], - [ - 189u8, 150u8, 234u8, 128u8, 111u8, 27u8, 173u8, 92u8, 109u8, 4u8, 98u8, - 103u8, 158u8, 19u8, 16u8, 5u8, 107u8, 135u8, 126u8, 170u8, 62u8, 64u8, - 149u8, 80u8, 33u8, 17u8, 83u8, 22u8, 176u8, 118u8, 26u8, 223u8, - ], - ) - } - } - } - } - pub mod parachain_info { - use super::{root_mod, runtime_types}; - pub mod storage { - use super::runtime_types; - pub struct StorageApi; - impl StorageApi { - pub fn parachain_id( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::polkadot_parachain::primitives::Id, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "ParachainInfo", - "ParachainId", - vec![], - [ - 151u8, 191u8, 241u8, 118u8, 192u8, 47u8, 166u8, 151u8, 217u8, 240u8, - 165u8, 232u8, 51u8, 113u8, 243u8, 1u8, 89u8, 240u8, 11u8, 1u8, 77u8, - 104u8, 12u8, 56u8, 17u8, 135u8, 214u8, 19u8, 114u8, 135u8, 66u8, 76u8, - ], - ) - } - } - } - } - pub mod balances { - use super::{root_mod, runtime_types}; - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] - pub mod calls { - use super::{root_mod, runtime_types}; - type DispatchError = runtime_types::sp_runtime::DispatchError; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct TransferAllowDeath { - pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - #[codec(compact)] - pub value: ::core::primitive::u128, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SetBalanceDeprecated { - pub who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - #[codec(compact)] - pub new_free: ::core::primitive::u128, - #[codec(compact)] - pub old_reserved: ::core::primitive::u128, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ForceTransfer { - pub source: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - #[codec(compact)] - pub value: ::core::primitive::u128, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct TransferKeepAlive { - pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - #[codec(compact)] - pub value: ::core::primitive::u128, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct TransferAll { - pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - pub keep_alive: ::core::primitive::bool, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ForceUnreserve { - pub who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - pub amount: ::core::primitive::u128, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct UpgradeAccounts { - pub who: ::std::vec::Vec<::sp_core::crypto::AccountId32>, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct Transfer { - pub dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - #[codec(compact)] - pub value: ::core::primitive::u128, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ForceSetBalance { - pub who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - #[codec(compact)] - pub new_free: ::core::primitive::u128, - } - pub struct TransactionApi; - impl TransactionApi { - #[doc = "Transfer some liquid free balance to another account."] - #[doc = ""] - #[doc = "`transfer_allow_death` will set the `FreeBalance` of the sender and receiver."] - #[doc = "If the sender's account is below the existential deposit as a result"] - #[doc = "of the transfer, the account will be reaped."] - #[doc = ""] - #[doc = "The dispatch origin for this call must be `Signed` by the transactor."] - pub fn transfer_allow_death( - &self, - dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - value: ::core::primitive::u128, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "Balances", - "transfer_allow_death", - TransferAllowDeath { dest, value }, - [ - 234u8, 130u8, 149u8, 36u8, 235u8, 112u8, 159u8, 189u8, 104u8, 148u8, - 108u8, 230u8, 25u8, 198u8, 71u8, 158u8, 112u8, 3u8, 162u8, 25u8, 145u8, - 252u8, 44u8, 63u8, 47u8, 34u8, 47u8, 158u8, 61u8, 14u8, 120u8, 255u8, - ], - ) - } - #[doc = "Set the regular balance of a given account; it also takes a reserved balance but this"] - #[doc = "must be the same as the account's current reserved balance."] - #[doc = ""] - #[doc = "The dispatch origin for this call is `root`."] - #[doc = ""] - #[doc = "WARNING: This call is DEPRECATED! Use `force_set_balance` instead."] - pub fn set_balance_deprecated( - &self, - who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - new_free: ::core::primitive::u128, - old_reserved: ::core::primitive::u128, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "Balances", - "set_balance_deprecated", - SetBalanceDeprecated { who, new_free, old_reserved }, - [ - 240u8, 107u8, 184u8, 206u8, 78u8, 106u8, 115u8, 152u8, 130u8, 56u8, - 156u8, 176u8, 105u8, 27u8, 176u8, 187u8, 49u8, 171u8, 229u8, 79u8, - 254u8, 248u8, 8u8, 162u8, 134u8, 12u8, 89u8, 100u8, 137u8, 102u8, - 132u8, 158u8, - ], - ) - } - #[doc = "Exactly as `transfer_allow_death`, except the origin must be root and the source account"] - #[doc = "may be specified."] - pub fn force_transfer( - &self, - source: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - value: ::core::primitive::u128, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "Balances", - "force_transfer", - ForceTransfer { source, dest, value }, - [ - 79u8, 174u8, 212u8, 108u8, 184u8, 33u8, 170u8, 29u8, 232u8, 254u8, - 195u8, 218u8, 221u8, 134u8, 57u8, 99u8, 6u8, 70u8, 181u8, 227u8, 56u8, - 239u8, 243u8, 158u8, 157u8, 245u8, 36u8, 162u8, 11u8, 237u8, 147u8, - 15u8, - ], - ) - } - #[doc = "Same as the [`transfer_allow_death`] call, but with a check that the transfer will not"] - #[doc = "kill the origin account."] - #[doc = ""] - #[doc = "99% of the time you want [`transfer_allow_death`] instead."] - #[doc = ""] - #[doc = "[`transfer_allow_death`]: struct.Pallet.html#method.transfer"] - pub fn transfer_keep_alive( - &self, - dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - value: ::core::primitive::u128, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "Balances", - "transfer_keep_alive", - TransferKeepAlive { dest, value }, - [ - 112u8, 179u8, 75u8, 168u8, 193u8, 221u8, 9u8, 82u8, 190u8, 113u8, - 253u8, 13u8, 130u8, 134u8, 170u8, 216u8, 136u8, 111u8, 242u8, 220u8, - 202u8, 112u8, 47u8, 79u8, 73u8, 244u8, 226u8, 59u8, 240u8, 188u8, - 210u8, 208u8, - ], - ) - } - #[doc = "Transfer the entire transferable balance from the caller account."] - #[doc = ""] - #[doc = "NOTE: This function only attempts to transfer _transferable_ balances. This means that"] - #[doc = "any locked, reserved, or existential deposits (when `keep_alive` is `true`), will not be"] - #[doc = "transferred by this function. To ensure that this function results in a killed account,"] - #[doc = "you might need to prepare the account by removing any reference counters, storage"] - #[doc = "deposits, etc..."] - #[doc = ""] - #[doc = "The dispatch origin of this call must be Signed."] - #[doc = ""] - #[doc = "- `dest`: The recipient of the transfer."] - #[doc = "- `keep_alive`: A boolean to determine if the `transfer_all` operation should send all"] - #[doc = " of the funds the account has, causing the sender account to be killed (false), or"] - #[doc = " transfer everything except at least the existential deposit, which will guarantee to"] - #[doc = " keep the sender account alive (true)."] - pub fn transfer_all( - &self, - dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - keep_alive: ::core::primitive::bool, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "Balances", - "transfer_all", - TransferAll { dest, keep_alive }, - [ - 46u8, 129u8, 29u8, 177u8, 221u8, 107u8, 245u8, 69u8, 238u8, 126u8, - 145u8, 26u8, 219u8, 208u8, 14u8, 80u8, 149u8, 1u8, 214u8, 63u8, 67u8, - 201u8, 144u8, 45u8, 129u8, 145u8, 174u8, 71u8, 238u8, 113u8, 208u8, - 34u8, - ], - ) - } - #[doc = "Unreserve some balance from a user by force."] - #[doc = ""] - #[doc = "Can only be called by ROOT."] - pub fn force_unreserve( - &self, - who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - amount: ::core::primitive::u128, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "Balances", - "force_unreserve", - ForceUnreserve { who, amount }, - [ - 160u8, 146u8, 137u8, 76u8, 157u8, 187u8, 66u8, 148u8, 207u8, 76u8, - 32u8, 254u8, 82u8, 215u8, 35u8, 161u8, 213u8, 52u8, 32u8, 98u8, 102u8, - 106u8, 234u8, 123u8, 6u8, 175u8, 184u8, 188u8, 174u8, 106u8, 176u8, - 78u8, - ], - ) - } - #[doc = "Upgrade a specified account."] - #[doc = ""] - #[doc = "- `origin`: Must be `Signed`."] - #[doc = "- `who`: The account to be upgraded."] - #[doc = ""] - #[doc = "This will waive the transaction fee if at least all but 10% of the accounts needed to"] - #[doc = "be upgraded. (We let some not have to be upgraded just in order to allow for the"] - #[doc = "possibililty of churn)."] - pub fn upgrade_accounts( - &self, - who: ::std::vec::Vec<::sp_core::crypto::AccountId32>, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "Balances", - "upgrade_accounts", - UpgradeAccounts { who }, - [ - 164u8, 61u8, 119u8, 24u8, 165u8, 46u8, 197u8, 59u8, 39u8, 198u8, 228u8, - 96u8, 228u8, 45u8, 85u8, 51u8, 37u8, 5u8, 75u8, 40u8, 241u8, 163u8, - 86u8, 228u8, 151u8, 217u8, 47u8, 105u8, 203u8, 103u8, 207u8, 4u8, - ], - ) - } - #[doc = "Alias for `transfer_allow_death`, provided only for name-wise compatibility."] - #[doc = ""] - #[doc = "WARNING: DEPRECATED! Will be released in approximately 3 months."] - pub fn transfer( - &self, - dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - value: ::core::primitive::u128, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "Balances", - "transfer", - Transfer { dest, value }, - [ - 111u8, 222u8, 32u8, 56u8, 171u8, 77u8, 252u8, 29u8, 194u8, 155u8, - 200u8, 192u8, 198u8, 81u8, 23u8, 115u8, 236u8, 91u8, 218u8, 114u8, - 107u8, 141u8, 138u8, 100u8, 237u8, 21u8, 58u8, 172u8, 3u8, 20u8, 216u8, - 38u8, - ], - ) - } - #[doc = "Set the regular balance of a given account."] - #[doc = ""] - #[doc = "The dispatch origin for this call is `root`."] - pub fn force_set_balance( - &self, - who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, - new_free: ::core::primitive::u128, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "Balances", - "force_set_balance", - ForceSetBalance { who, new_free }, - [ - 237u8, 4u8, 41u8, 58u8, 62u8, 179u8, 160u8, 4u8, 50u8, 71u8, 178u8, - 36u8, 130u8, 130u8, 92u8, 229u8, 16u8, 245u8, 169u8, 109u8, 165u8, - 72u8, 94u8, 70u8, 196u8, 136u8, 37u8, 94u8, 140u8, 215u8, 125u8, 125u8, - ], - ) - } - } - } - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] - pub type Event = runtime_types::pallet_balances::pallet::Event; - pub mod events { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "An account was created with some free balance."] - pub struct Endowed { - pub account: ::sp_core::crypto::AccountId32, - pub free_balance: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for Endowed { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Endowed"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "An account was removed whose balance was non-zero but below ExistentialDeposit,"] - #[doc = "resulting in an outright loss."] - pub struct DustLost { - pub account: ::sp_core::crypto::AccountId32, - pub amount: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for DustLost { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "DustLost"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Transfer succeeded."] - pub struct Transfer { - pub from: ::sp_core::crypto::AccountId32, - pub to: ::sp_core::crypto::AccountId32, - pub amount: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for Transfer { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Transfer"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "A balance was set by root."] - pub struct BalanceSet { - pub who: ::sp_core::crypto::AccountId32, - pub free: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for BalanceSet { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "BalanceSet"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Some balance was reserved (moved from free to reserved)."] - pub struct Reserved { - pub who: ::sp_core::crypto::AccountId32, - pub amount: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for Reserved { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Reserved"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Some balance was unreserved (moved from reserved to free)."] - pub struct Unreserved { - pub who: ::sp_core::crypto::AccountId32, - pub amount: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for Unreserved { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Unreserved"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Some balance was moved from the reserve of the first account to the second account."] - #[doc = "Final argument indicates the destination balance type."] - pub struct ReserveRepatriated { - pub from: ::sp_core::crypto::AccountId32, - pub to: ::sp_core::crypto::AccountId32, - pub amount: ::core::primitive::u128, - pub destination_status: - runtime_types::frame_support::traits::tokens::misc::BalanceStatus, - } - impl ::subxt::events::StaticEvent for ReserveRepatriated { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "ReserveRepatriated"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Some amount was deposited (e.g. for transaction fees)."] - pub struct Deposit { - pub who: ::sp_core::crypto::AccountId32, - pub amount: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for Deposit { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Deposit"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Some amount was withdrawn from the account (e.g. for transaction fees)."] - pub struct Withdraw { - pub who: ::sp_core::crypto::AccountId32, - pub amount: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for Withdraw { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Withdraw"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Some amount was removed from the account (e.g. for misbehavior)."] - pub struct Slashed { - pub who: ::sp_core::crypto::AccountId32, - pub amount: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for Slashed { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Slashed"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Some amount was minted into an account."] - pub struct Minted { - pub who: ::sp_core::crypto::AccountId32, - pub amount: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for Minted { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Minted"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Some amount was burned from an account."] - pub struct Burned { - pub who: ::sp_core::crypto::AccountId32, - pub amount: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for Burned { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Burned"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Some amount was suspended from an account (it can be restored later)."] - pub struct Suspended { - pub who: ::sp_core::crypto::AccountId32, - pub amount: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for Suspended { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Suspended"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Some amount was restored into an account."] - pub struct Restored { - pub who: ::sp_core::crypto::AccountId32, - pub amount: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for Restored { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Restored"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "An account was upgraded."] - pub struct Upgraded { - pub who: ::sp_core::crypto::AccountId32, - } - impl ::subxt::events::StaticEvent for Upgraded { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Upgraded"; - } - #[derive( - :: subxt :: ext :: codec :: CompactAs, - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Total issuance was increased by `amount`, creating a credit to be balanced."] - pub struct Issued { - pub amount: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for Issued { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Issued"; - } - #[derive( - :: subxt :: ext :: codec :: CompactAs, - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Total issuance was decreased by `amount`, creating a debt to be balanced."] - pub struct Rescinded { - pub amount: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for Rescinded { - const PALLET: &'static str = "Balances"; - const EVENT: &'static str = "Rescinded"; - } - } - pub mod storage { - use super::runtime_types; - pub struct StorageApi; - impl StorageApi { - #[doc = " The total units issued in the system."] - pub fn total_issuance( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Balances", - "TotalIssuance", - vec![], - [ - 1u8, 206u8, 252u8, 237u8, 6u8, 30u8, 20u8, 232u8, 164u8, 115u8, 51u8, - 156u8, 156u8, 206u8, 241u8, 187u8, 44u8, 84u8, 25u8, 164u8, 235u8, - 20u8, 86u8, 242u8, 124u8, 23u8, 28u8, 140u8, 26u8, 73u8, 231u8, 51u8, - ], - ) - } - #[doc = " The total units of outstanding deactivated balance in the system."] - pub fn inactive_issuance( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Balances", - "InactiveIssuance", - vec![], - [ - 74u8, 203u8, 111u8, 142u8, 225u8, 104u8, 173u8, 51u8, 226u8, 12u8, - 85u8, 135u8, 41u8, 206u8, 177u8, 238u8, 94u8, 246u8, 184u8, 250u8, - 140u8, 213u8, 91u8, 118u8, 163u8, 111u8, 211u8, 46u8, 204u8, 160u8, - 154u8, 21u8, - ], - ) - } - #[doc = " The Balances pallet example of storing the balance of an account."] - #[doc = ""] - #[doc = " # Example"] - #[doc = ""] - #[doc = " ```nocompile"] - #[doc = " impl pallet_balances::Config for Runtime {"] - #[doc = " type AccountStore = StorageMapShim, frame_system::Provider, AccountId, Self::AccountData>"] - #[doc = " }"] - #[doc = " ```"] - #[doc = ""] - #[doc = " You can also store the balance of an account in the `System` pallet."] - #[doc = ""] - #[doc = " # Example"] - #[doc = ""] - #[doc = " ```nocompile"] - #[doc = " impl pallet_balances::Config for Runtime {"] - #[doc = " type AccountStore = System"] - #[doc = " }"] - #[doc = " ```"] - #[doc = ""] - #[doc = " But this comes with tradeoffs, storing account balances in the system pallet stores"] - #[doc = " `frame_system` data alongside the account data contrary to storing account balances in the"] - #[doc = " `Balances` pallet, which uses a `StorageMap` to store balances data only."] - #[doc = " NOTE: This is only used in the case that this pallet is used to store balances."] - pub fn account( - &self, - _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::pallet_balances::types::AccountData<::core::primitive::u128>, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Balances", - "Account", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - )], - [ - 109u8, 250u8, 18u8, 96u8, 139u8, 232u8, 4u8, 139u8, 133u8, 239u8, 30u8, - 237u8, 73u8, 209u8, 143u8, 160u8, 94u8, 248u8, 124u8, 43u8, 224u8, - 165u8, 11u8, 6u8, 176u8, 144u8, 189u8, 161u8, 174u8, 210u8, 56u8, - 225u8, - ], - ) - } - #[doc = " The Balances pallet example of storing the balance of an account."] - #[doc = ""] - #[doc = " # Example"] - #[doc = ""] - #[doc = " ```nocompile"] - #[doc = " impl pallet_balances::Config for Runtime {"] - #[doc = " type AccountStore = StorageMapShim, frame_system::Provider, AccountId, Self::AccountData>"] - #[doc = " }"] - #[doc = " ```"] - #[doc = ""] - #[doc = " You can also store the balance of an account in the `System` pallet."] - #[doc = ""] - #[doc = " # Example"] - #[doc = ""] - #[doc = " ```nocompile"] - #[doc = " impl pallet_balances::Config for Runtime {"] - #[doc = " type AccountStore = System"] - #[doc = " }"] - #[doc = " ```"] - #[doc = ""] - #[doc = " But this comes with tradeoffs, storing account balances in the system pallet stores"] - #[doc = " `frame_system` data alongside the account data contrary to storing account balances in the"] - #[doc = " `Balances` pallet, which uses a `StorageMap` to store balances data only."] - #[doc = " NOTE: This is only used in the case that this pallet is used to store balances."] - pub fn account_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::pallet_balances::types::AccountData<::core::primitive::u128>, - >, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Balances", - "Account", - Vec::new(), - [ - 109u8, 250u8, 18u8, 96u8, 139u8, 232u8, 4u8, 139u8, 133u8, 239u8, 30u8, - 237u8, 73u8, 209u8, 143u8, 160u8, 94u8, 248u8, 124u8, 43u8, 224u8, - 165u8, 11u8, 6u8, 176u8, 144u8, 189u8, 161u8, 174u8, 210u8, 56u8, - 225u8, - ], - ) - } - #[doc = " Any liquidity locks on some account balances."] - #[doc = " NOTE: Should only be accessed when setting, changing and freeing a lock."] - pub fn locks( - &self, - _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< - runtime_types::pallet_balances::types::BalanceLock< - ::core::primitive::u128, - >, - >, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Balances", - "Locks", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - )], - [ - 216u8, 253u8, 87u8, 73u8, 24u8, 218u8, 35u8, 0u8, 244u8, 134u8, 195u8, - 58u8, 255u8, 64u8, 153u8, 212u8, 210u8, 232u8, 4u8, 122u8, 90u8, 212u8, - 136u8, 14u8, 127u8, 232u8, 8u8, 192u8, 40u8, 233u8, 18u8, 250u8, - ], - ) - } - #[doc = " Any liquidity locks on some account balances."] - #[doc = " NOTE: Should only be accessed when setting, changing and freeing a lock."] - pub fn locks_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< - runtime_types::pallet_balances::types::BalanceLock< - ::core::primitive::u128, - >, - >, - >, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Balances", - "Locks", - Vec::new(), - [ - 216u8, 253u8, 87u8, 73u8, 24u8, 218u8, 35u8, 0u8, 244u8, 134u8, 195u8, - 58u8, 255u8, 64u8, 153u8, 212u8, 210u8, 232u8, 4u8, 122u8, 90u8, 212u8, - 136u8, 14u8, 127u8, 232u8, 8u8, 192u8, 40u8, 233u8, 18u8, 250u8, - ], - ) - } - #[doc = " Named reserves on some account balances."] - pub fn reserves( - &self, - _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bounded_collections::bounded_vec::BoundedVec< - runtime_types::pallet_balances::types::ReserveData< - [::core::primitive::u8; 8usize], - ::core::primitive::u128, - >, - >, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Balances", - "Reserves", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - )], - [ - 17u8, 32u8, 191u8, 46u8, 76u8, 220u8, 101u8, 100u8, 42u8, 250u8, 128u8, - 167u8, 117u8, 44u8, 85u8, 96u8, 105u8, 216u8, 16u8, 147u8, 74u8, 55u8, - 183u8, 94u8, 160u8, 177u8, 26u8, 187u8, 71u8, 197u8, 187u8, 163u8, - ], - ) - } - #[doc = " Named reserves on some account balances."] - pub fn reserves_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bounded_collections::bounded_vec::BoundedVec< - runtime_types::pallet_balances::types::ReserveData< - [::core::primitive::u8; 8usize], - ::core::primitive::u128, - >, - >, - >, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Balances", - "Reserves", - Vec::new(), - [ - 17u8, 32u8, 191u8, 46u8, 76u8, 220u8, 101u8, 100u8, 42u8, 250u8, 128u8, - 167u8, 117u8, 44u8, 85u8, 96u8, 105u8, 216u8, 16u8, 147u8, 74u8, 55u8, - 183u8, 94u8, 160u8, 177u8, 26u8, 187u8, 71u8, 197u8, 187u8, 163u8, - ], - ) - } - #[doc = " Holds on account balances."] - pub fn holds( - &self, - _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bounded_collections::bounded_vec::BoundedVec< - runtime_types::pallet_balances::types::IdAmount< - (), - ::core::primitive::u128, - >, - >, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Balances", - "Holds", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - )], - [ - 247u8, 81u8, 4u8, 220u8, 77u8, 205u8, 28u8, 131u8, 215u8, 74u8, 197u8, - 137u8, 113u8, 214u8, 249u8, 91u8, 81u8, 216u8, 8u8, 5u8, 233u8, 39u8, - 104u8, 250u8, 3u8, 228u8, 148u8, 78u8, 4u8, 34u8, 45u8, 143u8, - ], - ) - } - #[doc = " Holds on account balances."] - pub fn holds_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bounded_collections::bounded_vec::BoundedVec< - runtime_types::pallet_balances::types::IdAmount< - (), - ::core::primitive::u128, - >, - >, - >, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Balances", - "Holds", - Vec::new(), - [ - 247u8, 81u8, 4u8, 220u8, 77u8, 205u8, 28u8, 131u8, 215u8, 74u8, 197u8, - 137u8, 113u8, 214u8, 249u8, 91u8, 81u8, 216u8, 8u8, 5u8, 233u8, 39u8, - 104u8, 250u8, 3u8, 228u8, 148u8, 78u8, 4u8, 34u8, 45u8, 143u8, - ], - ) - } - #[doc = " Freeze locks on account balances."] - pub fn freezes( - &self, - _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bounded_collections::bounded_vec::BoundedVec< - runtime_types::pallet_balances::types::IdAmount< - (), - ::core::primitive::u128, - >, - >, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Balances", - "Freezes", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - )], - [ - 211u8, 24u8, 237u8, 217u8, 47u8, 230u8, 147u8, 39u8, 112u8, 209u8, - 193u8, 47u8, 242u8, 13u8, 241u8, 0u8, 100u8, 45u8, 116u8, 130u8, 246u8, - 196u8, 50u8, 134u8, 135u8, 112u8, 206u8, 1u8, 12u8, 53u8, 106u8, 131u8, - ], - ) - } - #[doc = " Freeze locks on account balances."] - pub fn freezes_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bounded_collections::bounded_vec::BoundedVec< - runtime_types::pallet_balances::types::IdAmount< - (), - ::core::primitive::u128, - >, - >, - >, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "Balances", - "Freezes", - Vec::new(), - [ - 211u8, 24u8, 237u8, 217u8, 47u8, 230u8, 147u8, 39u8, 112u8, 209u8, - 193u8, 47u8, 242u8, 13u8, 241u8, 0u8, 100u8, 45u8, 116u8, 130u8, 246u8, - 196u8, 50u8, 134u8, 135u8, 112u8, 206u8, 1u8, 12u8, 53u8, 106u8, 131u8, - ], - ) - } - } - } - pub mod constants { - use super::runtime_types; - pub struct ConstantsApi; - impl ConstantsApi { - #[doc = " The minimum amount required to keep an account open."] - pub fn existential_deposit( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, - > { - ::subxt::constants::StaticConstantAddress::new( - "Balances", - "ExistentialDeposit", - [ - 84u8, 157u8, 140u8, 4u8, 93u8, 57u8, 29u8, 133u8, 105u8, 200u8, 214u8, - 27u8, 144u8, 208u8, 218u8, 160u8, 130u8, 109u8, 101u8, 54u8, 210u8, - 136u8, 71u8, 63u8, 49u8, 237u8, 234u8, 15u8, 178u8, 98u8, 148u8, 156u8, - ], - ) - } - #[doc = " The maximum number of locks that should exist on an account."] - #[doc = " Not strictly enforced, but used for weight estimation."] - pub fn max_locks( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - > { - ::subxt::constants::StaticConstantAddress::new( - "Balances", - "MaxLocks", - [ - 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, - 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, - 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, - 145u8, - ], - ) - } - #[doc = " The maximum number of named reserves that can exist on an account."] - pub fn max_reserves( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - > { - ::subxt::constants::StaticConstantAddress::new( - "Balances", - "MaxReserves", - [ - 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, - 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, - 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, - 145u8, - ], - ) - } - #[doc = " The maximum number of holds that can exist on an account at any time."] - pub fn max_holds( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - > { - ::subxt::constants::StaticConstantAddress::new( - "Balances", - "MaxHolds", - [ - 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, - 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, - 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, - 145u8, - ], - ) - } - #[doc = " The maximum number of individual freeze locks that can exist on an account at any time."] - pub fn max_freezes( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - > { - ::subxt::constants::StaticConstantAddress::new( - "Balances", - "MaxFreezes", - [ - 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, - 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, - 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, - 145u8, - ], - ) - } - } - } - } - pub mod aura { - use super::{root_mod, runtime_types}; - } - pub mod aura_ext { - use super::{root_mod, runtime_types}; - } - pub mod xcmp_queue { - use super::{root_mod, runtime_types}; - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] - pub mod calls { - use super::{root_mod, runtime_types}; - type DispatchError = runtime_types::sp_runtime::DispatchError; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ServiceOverweight { - pub index: ::core::primitive::u64, - pub weight_limit: ::sp_weights::Weight, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SuspendXcmExecution; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ResumeXcmExecution; - #[derive( - :: subxt :: ext :: codec :: CompactAs, - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - pub struct UpdateSuspendThreshold { - pub new: ::core::primitive::u32, - } - #[derive( - :: subxt :: ext :: codec :: CompactAs, - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - pub struct UpdateDropThreshold { - pub new: ::core::primitive::u32, - } - #[derive( - :: subxt :: ext :: codec :: CompactAs, - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - pub struct UpdateResumeThreshold { - pub new: ::core::primitive::u32, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct UpdateThresholdWeight { - pub new: ::sp_weights::Weight, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct UpdateWeightRestrictDecay { - pub new: ::sp_weights::Weight, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct UpdateXcmpMaxIndividualWeight { - pub new: ::sp_weights::Weight, - } - pub struct TransactionApi; - impl TransactionApi { - #[doc = "Services a single overweight XCM."] - #[doc = ""] - #[doc = "- `origin`: Must pass `ExecuteOverweightOrigin`."] - #[doc = "- `index`: The index of the overweight XCM to service"] - #[doc = "- `weight_limit`: The amount of weight that XCM execution may take."] - #[doc = ""] - #[doc = "Errors:"] - #[doc = "- `BadOverweightIndex`: XCM under `index` is not found in the `Overweight` storage map."] - #[doc = "- `BadXcm`: XCM under `index` cannot be properly decoded into a valid XCM format."] - #[doc = "- `WeightOverLimit`: XCM execution may use greater `weight_limit`."] - #[doc = ""] - #[doc = "Events:"] - #[doc = "- `OverweightServiced`: On success."] - pub fn service_overweight( - &self, - index: ::core::primitive::u64, - weight_limit: ::sp_weights::Weight, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "XcmpQueue", - "service_overweight", - ServiceOverweight { index, weight_limit }, - [ - 121u8, 236u8, 235u8, 23u8, 210u8, 238u8, 238u8, 122u8, 15u8, 86u8, - 34u8, 119u8, 105u8, 100u8, 214u8, 236u8, 117u8, 39u8, 254u8, 235u8, - 189u8, 15u8, 72u8, 74u8, 225u8, 134u8, 148u8, 126u8, 31u8, 203u8, - 144u8, 106u8, - ], - ) - } - #[doc = "Suspends all XCM executions for the XCMP queue, regardless of the sender's origin."] - #[doc = ""] - #[doc = "- `origin`: Must pass `ControllerOrigin`."] - pub fn suspend_xcm_execution( - &self, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "XcmpQueue", - "suspend_xcm_execution", - SuspendXcmExecution {}, - [ - 139u8, 76u8, 166u8, 86u8, 106u8, 144u8, 16u8, 47u8, 105u8, 185u8, 7u8, - 7u8, 63u8, 14u8, 250u8, 236u8, 99u8, 121u8, 101u8, 143u8, 28u8, 175u8, - 108u8, 197u8, 226u8, 43u8, 103u8, 92u8, 186u8, 12u8, 51u8, 153u8, - ], - ) - } - #[doc = "Resumes all XCM executions for the XCMP queue."] - #[doc = ""] - #[doc = "Note that this function doesn't change the status of the in/out bound channels."] - #[doc = ""] - #[doc = "- `origin`: Must pass `ControllerOrigin`."] - pub fn resume_xcm_execution( - &self, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "XcmpQueue", - "resume_xcm_execution", - ResumeXcmExecution {}, - [ - 67u8, 111u8, 47u8, 237u8, 79u8, 42u8, 90u8, 56u8, 245u8, 2u8, 20u8, - 23u8, 33u8, 121u8, 135u8, 50u8, 204u8, 147u8, 195u8, 80u8, 177u8, - 202u8, 8u8, 160u8, 164u8, 138u8, 64u8, 252u8, 178u8, 63u8, 102u8, - 245u8, - ], - ) - } - #[doc = "Overwrites the number of pages of messages which must be in the queue for the other side to be told to"] - #[doc = "suspend their sending."] - #[doc = ""] - #[doc = "- `origin`: Must pass `Root`."] - #[doc = "- `new`: Desired value for `QueueConfigData.suspend_value`"] - pub fn update_suspend_threshold( - &self, - new: ::core::primitive::u32, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "XcmpQueue", - "update_suspend_threshold", - UpdateSuspendThreshold { new }, - [ - 155u8, 120u8, 9u8, 228u8, 110u8, 62u8, 233u8, 36u8, 57u8, 85u8, 19u8, - 67u8, 246u8, 88u8, 81u8, 116u8, 243u8, 236u8, 174u8, 130u8, 8u8, 246u8, - 254u8, 97u8, 155u8, 207u8, 123u8, 60u8, 164u8, 14u8, 196u8, 97u8, - ], - ) - } - #[doc = "Overwrites the number of pages of messages which must be in the queue after which we drop any further"] - #[doc = "messages from the channel."] - #[doc = ""] - #[doc = "- `origin`: Must pass `Root`."] - #[doc = "- `new`: Desired value for `QueueConfigData.drop_threshold`"] - pub fn update_drop_threshold( - &self, - new: ::core::primitive::u32, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "XcmpQueue", - "update_drop_threshold", - UpdateDropThreshold { new }, - [ - 146u8, 177u8, 164u8, 96u8, 247u8, 182u8, 229u8, 175u8, 194u8, 101u8, - 186u8, 168u8, 94u8, 114u8, 172u8, 119u8, 35u8, 222u8, 175u8, 21u8, - 67u8, 61u8, 216u8, 144u8, 194u8, 10u8, 181u8, 62u8, 166u8, 198u8, - 138u8, 243u8, - ], - ) - } - #[doc = "Overwrites the number of pages of messages which the queue must be reduced to before it signals that"] - #[doc = "message sending may recommence after it has been suspended."] - #[doc = ""] - #[doc = "- `origin`: Must pass `Root`."] - #[doc = "- `new`: Desired value for `QueueConfigData.resume_threshold`"] - pub fn update_resume_threshold( - &self, - new: ::core::primitive::u32, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "XcmpQueue", - "update_resume_threshold", - UpdateResumeThreshold { new }, - [ - 231u8, 128u8, 80u8, 179u8, 61u8, 50u8, 103u8, 209u8, 103u8, 55u8, - 101u8, 113u8, 150u8, 10u8, 202u8, 7u8, 0u8, 77u8, 58u8, 4u8, 227u8, - 17u8, 225u8, 112u8, 121u8, 203u8, 184u8, 113u8, 231u8, 156u8, 174u8, - 154u8, - ], - ) - } - #[doc = "Overwrites the amount of remaining weight under which we stop processing messages."] - #[doc = ""] - #[doc = "- `origin`: Must pass `Root`."] - #[doc = "- `new`: Desired value for `QueueConfigData.threshold_weight`"] - pub fn update_threshold_weight( - &self, - new: ::sp_weights::Weight, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "XcmpQueue", - "update_threshold_weight", - UpdateThresholdWeight { new }, - [ - 14u8, 144u8, 112u8, 207u8, 195u8, 208u8, 184u8, 164u8, 94u8, 41u8, 8u8, - 58u8, 180u8, 80u8, 239u8, 39u8, 210u8, 159u8, 114u8, 169u8, 152u8, - 176u8, 26u8, 161u8, 32u8, 43u8, 250u8, 156u8, 56u8, 21u8, 43u8, 159u8, - ], - ) - } - #[doc = "Overwrites the speed to which the available weight approaches the maximum weight."] - #[doc = "A lower number results in a faster progression. A value of 1 makes the entire weight available initially."] - #[doc = ""] - #[doc = "- `origin`: Must pass `Root`."] - #[doc = "- `new`: Desired value for `QueueConfigData.weight_restrict_decay`."] - pub fn update_weight_restrict_decay( - &self, - new: ::sp_weights::Weight, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "XcmpQueue", - "update_weight_restrict_decay", - UpdateWeightRestrictDecay { new }, - [ - 42u8, 53u8, 83u8, 191u8, 51u8, 227u8, 210u8, 193u8, 142u8, 218u8, - 244u8, 177u8, 19u8, 87u8, 148u8, 177u8, 231u8, 197u8, 196u8, 255u8, - 41u8, 130u8, 245u8, 139u8, 107u8, 212u8, 90u8, 161u8, 82u8, 248u8, - 160u8, 223u8, - ], - ) - } - #[doc = "Overwrite the maximum amount of weight any individual message may consume."] - #[doc = "Messages above this weight go into the overweight queue and may only be serviced explicitly."] - #[doc = ""] - #[doc = "- `origin`: Must pass `Root`."] - #[doc = "- `new`: Desired value for `QueueConfigData.xcmp_max_individual_weight`."] - pub fn update_xcmp_max_individual_weight( - &self, - new: ::sp_weights::Weight, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "XcmpQueue", - "update_xcmp_max_individual_weight", - UpdateXcmpMaxIndividualWeight { new }, - [ - 148u8, 185u8, 89u8, 36u8, 152u8, 220u8, 248u8, 233u8, 236u8, 82u8, - 170u8, 111u8, 225u8, 142u8, 25u8, 211u8, 72u8, 248u8, 250u8, 14u8, - 45u8, 72u8, 78u8, 95u8, 92u8, 196u8, 245u8, 104u8, 112u8, 128u8, 27u8, - 109u8, - ], - ) - } - } - } - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] - pub type Event = runtime_types::cumulus_pallet_xcmp_queue::pallet::Event; - pub mod events { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Some XCM was executed ok."] - pub struct Success { - pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, - pub weight: ::sp_weights::Weight, - } - impl ::subxt::events::StaticEvent for Success { - const PALLET: &'static str = "XcmpQueue"; - const EVENT: &'static str = "Success"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Some XCM failed."] - pub struct Fail { - pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, - pub error: runtime_types::xcm::v3::traits::Error, - pub weight: ::sp_weights::Weight, - } - impl ::subxt::events::StaticEvent for Fail { - const PALLET: &'static str = "XcmpQueue"; - const EVENT: &'static str = "Fail"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Bad XCM version used."] - pub struct BadVersion { - pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, - } - impl ::subxt::events::StaticEvent for BadVersion { - const PALLET: &'static str = "XcmpQueue"; - const EVENT: &'static str = "BadVersion"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Bad XCM format used."] - pub struct BadFormat { - pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, - } - impl ::subxt::events::StaticEvent for BadFormat { - const PALLET: &'static str = "XcmpQueue"; - const EVENT: &'static str = "BadFormat"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "An HRMP message was sent to a sibling parachain."] - pub struct XcmpMessageSent { - pub message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, - } - impl ::subxt::events::StaticEvent for XcmpMessageSent { - const PALLET: &'static str = "XcmpQueue"; - const EVENT: &'static str = "XcmpMessageSent"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "An XCM exceeded the individual message weight budget."] - pub struct OverweightEnqueued { - pub sender: runtime_types::polkadot_parachain::primitives::Id, - pub sent_at: ::core::primitive::u32, - pub index: ::core::primitive::u64, - pub required: ::sp_weights::Weight, - } - impl ::subxt::events::StaticEvent for OverweightEnqueued { - const PALLET: &'static str = "XcmpQueue"; - const EVENT: &'static str = "OverweightEnqueued"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "An XCM from the overweight queue was executed with the given actual weight used."] - pub struct OverweightServiced { - pub index: ::core::primitive::u64, - pub used: ::sp_weights::Weight, - } - impl ::subxt::events::StaticEvent for OverweightServiced { - const PALLET: &'static str = "XcmpQueue"; - const EVENT: &'static str = "OverweightServiced"; - } - } - pub mod storage { - use super::runtime_types; - pub struct StorageApi; - impl StorageApi { - #[doc = " Status of the inbound XCMP channels."] - pub fn inbound_xcmp_status( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - ::std::vec::Vec< - runtime_types::cumulus_pallet_xcmp_queue::InboundChannelDetails, - >, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "XcmpQueue", - "InboundXcmpStatus", - vec![], - [ - 183u8, 198u8, 237u8, 153u8, 132u8, 201u8, 87u8, 182u8, 121u8, 164u8, - 129u8, 241u8, 58u8, 192u8, 115u8, 152u8, 7u8, 33u8, 95u8, 51u8, 2u8, - 176u8, 144u8, 12u8, 125u8, 83u8, 92u8, 198u8, 211u8, 101u8, 28u8, 50u8, - ], - ) - } - #[doc = " Inbound aggregate XCMP messages. It can only be one per ParaId/block."] - pub fn inbound_xcmp_messages( - &self, - _0: impl ::std::borrow::Borrow, - _1: impl ::std::borrow::Borrow<::core::primitive::u32>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "XcmpQueue", - "InboundXcmpMessages", - vec![ - ::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - ), - ::subxt::storage::address::StorageMapKey::new( - _1.borrow(), - ::subxt::storage::address::StorageHasher::Twox64Concat, - ), - ], - [ - 157u8, 232u8, 222u8, 97u8, 218u8, 96u8, 96u8, 90u8, 216u8, 205u8, 39u8, - 130u8, 109u8, 152u8, 127u8, 57u8, 54u8, 63u8, 104u8, 135u8, 33u8, - 175u8, 197u8, 166u8, 238u8, 22u8, 137u8, 162u8, 226u8, 199u8, 87u8, - 25u8, - ], - ) - } - #[doc = " Inbound aggregate XCMP messages. It can only be one per ParaId/block."] - pub fn inbound_xcmp_messages_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "XcmpQueue", - "InboundXcmpMessages", - Vec::new(), - [ - 157u8, 232u8, 222u8, 97u8, 218u8, 96u8, 96u8, 90u8, 216u8, 205u8, 39u8, - 130u8, 109u8, 152u8, 127u8, 57u8, 54u8, 63u8, 104u8, 135u8, 33u8, - 175u8, 197u8, 166u8, 238u8, 22u8, 137u8, 162u8, 226u8, 199u8, 87u8, - 25u8, - ], - ) - } - #[doc = " The non-empty XCMP channels in order of becoming non-empty, and the index of the first"] - #[doc = " and last outbound message. If the two indices are equal, then it indicates an empty"] - #[doc = " queue and there must be a non-`Ok` `OutboundStatus`. We assume queues grow no greater"] - #[doc = " than 65535 items. Queue indices for normal messages begin at one; zero is reserved in"] - #[doc = " case of the need to send a high-priority signal message this block."] - #[doc = " The bool is true if there is a signal message waiting to be sent."] - pub fn outbound_xcmp_status( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - ::std::vec::Vec< - runtime_types::cumulus_pallet_xcmp_queue::OutboundChannelDetails, - >, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "XcmpQueue", - "OutboundXcmpStatus", - vec![], - [ - 238u8, 120u8, 185u8, 141u8, 82u8, 159u8, 41u8, 68u8, 204u8, 15u8, 46u8, - 152u8, 144u8, 74u8, 250u8, 83u8, 71u8, 105u8, 54u8, 53u8, 226u8, 87u8, - 14u8, 202u8, 58u8, 160u8, 54u8, 162u8, 239u8, 248u8, 227u8, 116u8, - ], - ) - } - #[doc = " The messages outbound in a given XCMP channel."] - pub fn outbound_xcmp_messages( - &self, - _0: impl ::std::borrow::Borrow, - _1: impl ::std::borrow::Borrow<::core::primitive::u16>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "XcmpQueue", - "OutboundXcmpMessages", - vec![ - ::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - ), - ::subxt::storage::address::StorageMapKey::new( - _1.borrow(), - ::subxt::storage::address::StorageHasher::Twox64Concat, - ), - ], - [ - 50u8, 182u8, 237u8, 191u8, 106u8, 67u8, 54u8, 1u8, 17u8, 107u8, 70u8, - 90u8, 202u8, 8u8, 63u8, 184u8, 171u8, 111u8, 192u8, 196u8, 7u8, 31u8, - 186u8, 68u8, 31u8, 63u8, 71u8, 61u8, 83u8, 223u8, 79u8, 200u8, - ], - ) - } - #[doc = " The messages outbound in a given XCMP channel."] - pub fn outbound_xcmp_messages_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "XcmpQueue", - "OutboundXcmpMessages", - Vec::new(), - [ - 50u8, 182u8, 237u8, 191u8, 106u8, 67u8, 54u8, 1u8, 17u8, 107u8, 70u8, - 90u8, 202u8, 8u8, 63u8, 184u8, 171u8, 111u8, 192u8, 196u8, 7u8, 31u8, - 186u8, 68u8, 31u8, 63u8, 71u8, 61u8, 83u8, 223u8, 79u8, 200u8, - ], - ) - } - #[doc = " Any signal messages waiting to be sent."] - pub fn signal_messages( - &self, - _0: impl ::std::borrow::Borrow, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "XcmpQueue", - "SignalMessages", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - )], - [ - 156u8, 242u8, 186u8, 89u8, 177u8, 195u8, 90u8, 121u8, 94u8, 106u8, - 222u8, 78u8, 19u8, 162u8, 179u8, 96u8, 38u8, 113u8, 209u8, 148u8, 29u8, - 110u8, 106u8, 167u8, 162u8, 96u8, 221u8, 20u8, 33u8, 179u8, 168u8, - 142u8, - ], - ) - } - #[doc = " Any signal messages waiting to be sent."] - pub fn signal_messages_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::std::vec::Vec<::core::primitive::u8>>, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "XcmpQueue", - "SignalMessages", - Vec::new(), - [ - 156u8, 242u8, 186u8, 89u8, 177u8, 195u8, 90u8, 121u8, 94u8, 106u8, - 222u8, 78u8, 19u8, 162u8, 179u8, 96u8, 38u8, 113u8, 209u8, 148u8, 29u8, - 110u8, 106u8, 167u8, 162u8, 96u8, 221u8, 20u8, 33u8, 179u8, 168u8, - 142u8, - ], - ) - } - #[doc = " The configuration which controls the dynamics of the outbound queue."] - pub fn queue_config( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::cumulus_pallet_xcmp_queue::QueueConfigData, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "XcmpQueue", - "QueueConfig", - vec![], - [ - 154u8, 172u8, 227u8, 208u8, 130u8, 93u8, 173u8, 129u8, 33u8, 75u8, - 180u8, 100u8, 35u8, 154u8, 40u8, 188u8, 86u8, 53u8, 74u8, 118u8, 131u8, - 159u8, 240u8, 159u8, 185u8, 45u8, 165u8, 6u8, 90u8, 125u8, 77u8, 253u8, - ], - ) - } - #[doc = " The messages that exceeded max individual message weight budget."] - #[doc = ""] - #[doc = " These message stay in this storage map until they are manually dispatched via"] - #[doc = " `service_overweight`."] - pub fn overweight( - &self, - _0: impl ::std::borrow::Borrow<::core::primitive::u64>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<( - runtime_types::polkadot_parachain::primitives::Id, - ::core::primitive::u32, - ::std::vec::Vec<::core::primitive::u8>, - )>, - ::subxt::storage::address::Yes, - (), - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "XcmpQueue", - "Overweight", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Twox64Concat, - )], - [ - 222u8, 249u8, 232u8, 110u8, 117u8, 229u8, 165u8, 164u8, 219u8, 219u8, - 149u8, 204u8, 25u8, 78u8, 204u8, 116u8, 111u8, 114u8, 120u8, 222u8, - 56u8, 77u8, 122u8, 147u8, 108u8, 15u8, 94u8, 161u8, 212u8, 50u8, 7u8, - 7u8, - ], - ) - } - #[doc = " The messages that exceeded max individual message weight budget."] - #[doc = ""] - #[doc = " These message stay in this storage map until they are manually dispatched via"] - #[doc = " `service_overweight`."] - pub fn overweight_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<( - runtime_types::polkadot_parachain::primitives::Id, - ::core::primitive::u32, - ::std::vec::Vec<::core::primitive::u8>, - )>, - (), - (), - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "XcmpQueue", - "Overweight", - Vec::new(), - [ - 222u8, 249u8, 232u8, 110u8, 117u8, 229u8, 165u8, 164u8, 219u8, 219u8, - 149u8, 204u8, 25u8, 78u8, 204u8, 116u8, 111u8, 114u8, 120u8, 222u8, - 56u8, 77u8, 122u8, 147u8, 108u8, 15u8, 94u8, 161u8, 212u8, 50u8, 7u8, - 7u8, - ], - ) - } - #[doc = "Counter for the related counted storage map"] - pub fn counter_for_overweight( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "XcmpQueue", - "CounterForOverweight", - vec![], - [ - 148u8, 226u8, 248u8, 107u8, 165u8, 97u8, 218u8, 160u8, 127u8, 48u8, - 185u8, 251u8, 35u8, 137u8, 119u8, 251u8, 151u8, 167u8, 189u8, 66u8, - 80u8, 74u8, 134u8, 129u8, 222u8, 180u8, 51u8, 182u8, 50u8, 110u8, 10u8, - 43u8, - ], - ) - } - #[doc = " The number of overweight messages ever recorded in `Overweight`. Also doubles as the next"] - #[doc = " available free overweight index."] - pub fn overweight_count( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u64>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "XcmpQueue", - "OverweightCount", - vec![], - [ - 102u8, 180u8, 196u8, 148u8, 115u8, 62u8, 46u8, 238u8, 97u8, 116u8, - 117u8, 42u8, 14u8, 5u8, 72u8, 237u8, 230u8, 46u8, 150u8, 126u8, 89u8, - 64u8, 233u8, 166u8, 180u8, 137u8, 52u8, 233u8, 252u8, 255u8, 36u8, - 20u8, - ], - ) - } - #[doc = " Whether or not the XCMP queue is suspended from executing incoming XCMs or not."] - pub fn queue_suspended( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::bool>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "XcmpQueue", - "QueueSuspended", - vec![], - [ - 23u8, 37u8, 48u8, 112u8, 222u8, 17u8, 252u8, 65u8, 160u8, 217u8, 218u8, - 30u8, 2u8, 1u8, 204u8, 0u8, 251u8, 17u8, 138u8, 197u8, 164u8, 50u8, - 122u8, 0u8, 31u8, 238u8, 147u8, 213u8, 30u8, 132u8, 184u8, 215u8, - ], - ) - } - } - } - } - pub mod polkadot_xcm { - use super::{root_mod, runtime_types}; - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] - pub mod calls { - use super::{root_mod, runtime_types}; - type DispatchError = runtime_types::sp_runtime::DispatchError; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct Send { - pub dest: ::std::boxed::Box, - pub message: ::std::boxed::Box, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct TeleportAssets { - pub dest: ::std::boxed::Box, - pub beneficiary: ::std::boxed::Box, - pub assets: ::std::boxed::Box, - pub fee_asset_item: ::core::primitive::u32, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ReserveTransferAssets { - pub dest: ::std::boxed::Box, - pub beneficiary: ::std::boxed::Box, - pub assets: ::std::boxed::Box, - pub fee_asset_item: ::core::primitive::u32, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct Execute { - pub message: ::std::boxed::Box, - pub max_weight: ::sp_weights::Weight, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ForceXcmVersion { - pub location: - ::std::boxed::Box, - pub xcm_version: ::core::primitive::u32, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ForceDefaultXcmVersion { - pub maybe_xcm_version: ::core::option::Option<::core::primitive::u32>, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ForceSubscribeVersionNotify { - pub location: ::std::boxed::Box, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ForceUnsubscribeVersionNotify { - pub location: ::std::boxed::Box, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct LimitedReserveTransferAssets { - pub dest: ::std::boxed::Box, - pub beneficiary: ::std::boxed::Box, - pub assets: ::std::boxed::Box, - pub fee_asset_item: ::core::primitive::u32, - pub weight_limit: runtime_types::xcm::v3::WeightLimit, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct LimitedTeleportAssets { - pub dest: ::std::boxed::Box, - pub beneficiary: ::std::boxed::Box, - pub assets: ::std::boxed::Box, - pub fee_asset_item: ::core::primitive::u32, - pub weight_limit: runtime_types::xcm::v3::WeightLimit, - } - pub struct TransactionApi; - impl TransactionApi { - pub fn send( - &self, - dest: runtime_types::xcm::VersionedMultiLocation, - message: runtime_types::xcm::VersionedXcm, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "PolkadotXcm", - "send", - Send { - dest: ::std::boxed::Box::new(dest), - message: ::std::boxed::Box::new(message), - }, - [ - 246u8, 35u8, 227u8, 112u8, 223u8, 7u8, 44u8, 186u8, 60u8, 225u8, 153u8, - 249u8, 104u8, 51u8, 123u8, 227u8, 143u8, 65u8, 232u8, 209u8, 178u8, - 104u8, 70u8, 56u8, 230u8, 14u8, 75u8, 83u8, 250u8, 160u8, 9u8, 39u8, - ], - ) - } - #[doc = "Teleport some assets from the local chain to some destination chain."] - #[doc = ""] - #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] - #[doc = "index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,"] - #[doc = "with all fees taken as needed from the asset."] - #[doc = ""] - #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] - #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] - #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] - #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] - #[doc = " an `AccountId32` value."] - #[doc = "- `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the"] - #[doc = " `dest` side. May not be empty."] - #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] - #[doc = " fees."] - pub fn teleport_assets( - &self, - dest: runtime_types::xcm::VersionedMultiLocation, - beneficiary: runtime_types::xcm::VersionedMultiLocation, - assets: runtime_types::xcm::VersionedMultiAssets, - fee_asset_item: ::core::primitive::u32, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "PolkadotXcm", - "teleport_assets", - TeleportAssets { - dest: ::std::boxed::Box::new(dest), - beneficiary: ::std::boxed::Box::new(beneficiary), - assets: ::std::boxed::Box::new(assets), - fee_asset_item, - }, - [ - 187u8, 42u8, 2u8, 96u8, 105u8, 125u8, 74u8, 53u8, 2u8, 21u8, 31u8, - 160u8, 201u8, 197u8, 157u8, 190u8, 40u8, 145u8, 5u8, 99u8, 194u8, 41u8, - 114u8, 60u8, 165u8, 186u8, 15u8, 226u8, 85u8, 113u8, 159u8, 136u8, - ], - ) - } - #[doc = "Transfer some assets from the local chain to the sovereign account of a destination"] - #[doc = "chain and forward a notification XCM."] - #[doc = ""] - #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] - #[doc = "index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,"] - #[doc = "with all fees taken as needed from the asset."] - #[doc = ""] - #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] - #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] - #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] - #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] - #[doc = " an `AccountId32` value."] - #[doc = "- `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the"] - #[doc = " `dest` side."] - #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] - #[doc = " fees."] - pub fn reserve_transfer_assets( - &self, - dest: runtime_types::xcm::VersionedMultiLocation, - beneficiary: runtime_types::xcm::VersionedMultiLocation, - assets: runtime_types::xcm::VersionedMultiAssets, - fee_asset_item: ::core::primitive::u32, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "PolkadotXcm", - "reserve_transfer_assets", - ReserveTransferAssets { - dest: ::std::boxed::Box::new(dest), - beneficiary: ::std::boxed::Box::new(beneficiary), - assets: ::std::boxed::Box::new(assets), - fee_asset_item, - }, - [ - 249u8, 177u8, 76u8, 204u8, 186u8, 165u8, 16u8, 186u8, 129u8, 239u8, - 65u8, 252u8, 9u8, 132u8, 32u8, 164u8, 117u8, 177u8, 40u8, 21u8, 196u8, - 246u8, 147u8, 2u8, 95u8, 110u8, 68u8, 162u8, 148u8, 9u8, 59u8, 170u8, - ], - ) - } - #[doc = "Execute an XCM message from a local, signed, origin."] - #[doc = ""] - #[doc = "An event is deposited indicating whether `msg` could be executed completely or only"] - #[doc = "partially."] - #[doc = ""] - #[doc = "No more than `max_weight` will be used in its attempted execution. If this is less than the"] - #[doc = "maximum amount of weight that the message could take to be executed, then no execution"] - #[doc = "attempt will be made."] - #[doc = ""] - #[doc = "NOTE: A successful return to this does *not* imply that the `msg` was executed successfully"] - #[doc = "to completion; only that *some* of it was executed."] - pub fn execute( - &self, - message: runtime_types::xcm::VersionedXcm, - max_weight: ::sp_weights::Weight, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "PolkadotXcm", - "execute", - Execute { message: ::std::boxed::Box::new(message), max_weight }, - [ - 102u8, 41u8, 146u8, 29u8, 241u8, 205u8, 95u8, 153u8, 228u8, 141u8, - 11u8, 228u8, 13u8, 44u8, 75u8, 204u8, 174u8, 35u8, 155u8, 104u8, 204u8, - 82u8, 239u8, 98u8, 249u8, 187u8, 193u8, 1u8, 122u8, 88u8, 162u8, 200u8, - ], - ) - } - #[doc = "Extoll that a particular destination can be communicated with through a particular"] - #[doc = "version of XCM."] - #[doc = ""] - #[doc = "- `origin`: Must be Root."] - #[doc = "- `location`: The destination that is being described."] - #[doc = "- `xcm_version`: The latest version of XCM that `location` supports."] - pub fn force_xcm_version( - &self, - location: runtime_types::xcm::v3::multilocation::MultiLocation, - xcm_version: ::core::primitive::u32, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "PolkadotXcm", - "force_xcm_version", - ForceXcmVersion { location: ::std::boxed::Box::new(location), xcm_version }, - [ - 68u8, 48u8, 95u8, 61u8, 152u8, 95u8, 213u8, 126u8, 209u8, 176u8, 230u8, - 160u8, 164u8, 42u8, 128u8, 62u8, 175u8, 3u8, 161u8, 170u8, 20u8, 31u8, - 216u8, 122u8, 31u8, 77u8, 64u8, 182u8, 121u8, 41u8, 23u8, 80u8, - ], - ) - } - #[doc = "Set a safe XCM version (the version that XCM should be encoded with if the most recent"] - #[doc = "version a destination can accept is unknown)."] - #[doc = ""] - #[doc = "- `origin`: Must be Root."] - #[doc = "- `maybe_xcm_version`: The default XCM encoding version, or `None` to disable."] - pub fn force_default_xcm_version( - &self, - maybe_xcm_version: ::core::option::Option<::core::primitive::u32>, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "PolkadotXcm", - "force_default_xcm_version", - ForceDefaultXcmVersion { maybe_xcm_version }, - [ - 38u8, 36u8, 59u8, 231u8, 18u8, 79u8, 76u8, 9u8, 200u8, 125u8, 214u8, - 166u8, 37u8, 99u8, 111u8, 161u8, 135u8, 2u8, 133u8, 157u8, 165u8, 18u8, - 152u8, 81u8, 209u8, 255u8, 137u8, 237u8, 28u8, 126u8, 224u8, 141u8, - ], - ) - } - #[doc = "Ask a location to notify us regarding their XCM version and any changes to it."] - #[doc = ""] - #[doc = "- `origin`: Must be Root."] - #[doc = "- `location`: The location to which we should subscribe for XCM version notifications."] - pub fn force_subscribe_version_notify( - &self, - location: runtime_types::xcm::VersionedMultiLocation, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "PolkadotXcm", - "force_subscribe_version_notify", - ForceSubscribeVersionNotify { location: ::std::boxed::Box::new(location) }, - [ - 236u8, 37u8, 153u8, 26u8, 174u8, 187u8, 154u8, 38u8, 179u8, 223u8, - 130u8, 32u8, 128u8, 30u8, 148u8, 229u8, 7u8, 185u8, 174u8, 9u8, 96u8, - 215u8, 189u8, 178u8, 148u8, 141u8, 249u8, 118u8, 7u8, 238u8, 1u8, 49u8, - ], - ) - } - #[doc = "Require that a particular destination should no longer notify us regarding any XCM"] - #[doc = "version changes."] - #[doc = ""] - #[doc = "- `origin`: Must be Root."] - #[doc = "- `location`: The location to which we are currently subscribed for XCM version"] - #[doc = " notifications which we no longer desire."] - pub fn force_unsubscribe_version_notify( - &self, - location: runtime_types::xcm::VersionedMultiLocation, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "PolkadotXcm", - "force_unsubscribe_version_notify", - ForceUnsubscribeVersionNotify { - location: ::std::boxed::Box::new(location), - }, - [ - 154u8, 169u8, 145u8, 211u8, 185u8, 71u8, 9u8, 63u8, 3u8, 158u8, 187u8, - 173u8, 115u8, 166u8, 100u8, 66u8, 12u8, 40u8, 198u8, 40u8, 213u8, - 104u8, 95u8, 183u8, 215u8, 53u8, 94u8, 158u8, 106u8, 56u8, 149u8, 52u8, - ], - ) - } - #[doc = "Transfer some assets from the local chain to the sovereign account of a destination"] - #[doc = "chain and forward a notification XCM."] - #[doc = ""] - #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] - #[doc = "index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight"] - #[doc = "is needed than `weight_limit`, then the operation will fail and the assets send may be"] - #[doc = "at risk."] - #[doc = ""] - #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] - #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] - #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] - #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] - #[doc = " an `AccountId32` value."] - #[doc = "- `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the"] - #[doc = " `dest` side."] - #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] - #[doc = " fees."] - #[doc = "- `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase."] - pub fn limited_reserve_transfer_assets( - &self, - dest: runtime_types::xcm::VersionedMultiLocation, - beneficiary: runtime_types::xcm::VersionedMultiLocation, - assets: runtime_types::xcm::VersionedMultiAssets, - fee_asset_item: ::core::primitive::u32, - weight_limit: runtime_types::xcm::v3::WeightLimit, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "PolkadotXcm", - "limited_reserve_transfer_assets", - LimitedReserveTransferAssets { - dest: ::std::boxed::Box::new(dest), - beneficiary: ::std::boxed::Box::new(beneficiary), - assets: ::std::boxed::Box::new(assets), - fee_asset_item, - weight_limit, - }, - [ - 131u8, 191u8, 89u8, 27u8, 236u8, 142u8, 130u8, 129u8, 245u8, 95u8, - 159u8, 96u8, 252u8, 80u8, 28u8, 40u8, 128u8, 55u8, 41u8, 123u8, 22u8, - 18u8, 0u8, 236u8, 77u8, 68u8, 135u8, 181u8, 40u8, 47u8, 92u8, 240u8, - ], - ) - } - #[doc = "Teleport some assets from the local chain to some destination chain."] - #[doc = ""] - #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] - #[doc = "index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight"] - #[doc = "is needed than `weight_limit`, then the operation will fail and the assets send may be"] - #[doc = "at risk."] - #[doc = ""] - #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] - #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] - #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] - #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] - #[doc = " an `AccountId32` value."] - #[doc = "- `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the"] - #[doc = " `dest` side. May not be empty."] - #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] - #[doc = " fees."] - #[doc = "- `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase."] - pub fn limited_teleport_assets( - &self, - dest: runtime_types::xcm::VersionedMultiLocation, - beneficiary: runtime_types::xcm::VersionedMultiLocation, - assets: runtime_types::xcm::VersionedMultiAssets, - fee_asset_item: ::core::primitive::u32, - weight_limit: runtime_types::xcm::v3::WeightLimit, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "PolkadotXcm", - "limited_teleport_assets", - LimitedTeleportAssets { - dest: ::std::boxed::Box::new(dest), - beneficiary: ::std::boxed::Box::new(beneficiary), - assets: ::std::boxed::Box::new(assets), - fee_asset_item, - weight_limit, - }, - [ - 234u8, 19u8, 104u8, 174u8, 98u8, 159u8, 205u8, 110u8, 240u8, 78u8, - 186u8, 138u8, 236u8, 116u8, 104u8, 215u8, 57u8, 178u8, 166u8, 208u8, - 197u8, 113u8, 101u8, 56u8, 23u8, 56u8, 84u8, 14u8, 173u8, 70u8, 211u8, - 201u8, - ], - ) - } - } - } - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] - pub type Event = runtime_types::pallet_xcm::pallet::Event; - pub mod events { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Execution of an XCM message was attempted."] - #[doc = ""] - #[doc = "\\[ outcome \\]"] - pub struct Attempted(pub runtime_types::xcm::v3::traits::Outcome); - impl ::subxt::events::StaticEvent for Attempted { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "Attempted"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "A XCM message was sent."] - #[doc = ""] - #[doc = "\\[ origin, destination, message \\]"] - pub struct Sent( - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub runtime_types::xcm::v3::Xcm, - ); - impl ::subxt::events::StaticEvent for Sent { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "Sent"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Query response received which does not match a registered query. This may be because a"] - #[doc = "matching query was never registered, it may be because it is a duplicate response, or"] - #[doc = "because the query timed out."] - #[doc = ""] - #[doc = "\\[ origin location, id \\]"] - pub struct UnexpectedResponse( - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub ::core::primitive::u64, - ); - impl ::subxt::events::StaticEvent for UnexpectedResponse { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "UnexpectedResponse"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Query response has been received and is ready for taking with `take_response`. There is"] - #[doc = "no registered notification call."] - #[doc = ""] - #[doc = "\\[ id, response \\]"] - pub struct ResponseReady( - pub ::core::primitive::u64, - pub runtime_types::xcm::v3::Response, - ); - impl ::subxt::events::StaticEvent for ResponseReady { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "ResponseReady"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Query response has been received and query is removed. The registered notification has"] - #[doc = "been dispatched and executed successfully."] - #[doc = ""] - #[doc = "\\[ id, pallet index, call index \\]"] - pub struct Notified( - pub ::core::primitive::u64, - pub ::core::primitive::u8, - pub ::core::primitive::u8, - ); - impl ::subxt::events::StaticEvent for Notified { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "Notified"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Query response has been received and query is removed. The registered notification could"] - #[doc = "not be dispatched because the dispatch weight is greater than the maximum weight"] - #[doc = "originally budgeted by this runtime for the query result."] - #[doc = ""] - #[doc = "\\[ id, pallet index, call index, actual weight, max budgeted weight \\]"] - pub struct NotifyOverweight( - pub ::core::primitive::u64, - pub ::core::primitive::u8, - pub ::core::primitive::u8, - pub ::sp_weights::Weight, - pub ::sp_weights::Weight, - ); - impl ::subxt::events::StaticEvent for NotifyOverweight { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "NotifyOverweight"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Query response has been received and query is removed. There was a general error with"] - #[doc = "dispatching the notification call."] - #[doc = ""] - #[doc = "\\[ id, pallet index, call index \\]"] - pub struct NotifyDispatchError( - pub ::core::primitive::u64, - pub ::core::primitive::u8, - pub ::core::primitive::u8, - ); - impl ::subxt::events::StaticEvent for NotifyDispatchError { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "NotifyDispatchError"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Query response has been received and query is removed. The dispatch was unable to be"] - #[doc = "decoded into a `Call`; this might be due to dispatch function having a signature which"] - #[doc = "is not `(origin, QueryId, Response)`."] - #[doc = ""] - #[doc = "\\[ id, pallet index, call index \\]"] - pub struct NotifyDecodeFailed( - pub ::core::primitive::u64, - pub ::core::primitive::u8, - pub ::core::primitive::u8, - ); - impl ::subxt::events::StaticEvent for NotifyDecodeFailed { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "NotifyDecodeFailed"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Expected query response has been received but the origin location of the response does"] - #[doc = "not match that expected. The query remains registered for a later, valid, response to"] - #[doc = "be received and acted upon."] - #[doc = ""] - #[doc = "\\[ origin location, id, expected location \\]"] - pub struct InvalidResponder( - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub ::core::primitive::u64, - pub ::core::option::Option, - ); - impl ::subxt::events::StaticEvent for InvalidResponder { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "InvalidResponder"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Expected query response has been received but the expected origin location placed in"] - #[doc = "storage by this runtime previously cannot be decoded. The query remains registered."] - #[doc = ""] - #[doc = "This is unexpected (since a location placed in storage in a previously executing"] - #[doc = "runtime should be readable prior to query timeout) and dangerous since the possibly"] - #[doc = "valid response will be dropped. Manual governance intervention is probably going to be"] - #[doc = "needed."] - #[doc = ""] - #[doc = "\\[ origin location, id \\]"] - pub struct InvalidResponderVersion( - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub ::core::primitive::u64, - ); - impl ::subxt::events::StaticEvent for InvalidResponderVersion { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "InvalidResponderVersion"; - } - #[derive( - :: subxt :: ext :: codec :: CompactAs, - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Received query response has been read and removed."] - #[doc = ""] - #[doc = "\\[ id \\]"] - pub struct ResponseTaken(pub ::core::primitive::u64); - impl ::subxt::events::StaticEvent for ResponseTaken { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "ResponseTaken"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Some assets have been placed in an asset trap."] - #[doc = ""] - #[doc = "\\[ hash, origin, assets \\]"] - pub struct AssetsTrapped( - pub ::subxt::utils::H256, - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub runtime_types::xcm::VersionedMultiAssets, - ); - impl ::subxt::events::StaticEvent for AssetsTrapped { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "AssetsTrapped"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "An XCM version change notification message has been attempted to be sent."] - #[doc = ""] - #[doc = "The cost of sending it (borne by the chain) is included."] - #[doc = ""] - #[doc = "\\[ destination, result, cost \\]"] - pub struct VersionChangeNotified( - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub ::core::primitive::u32, - pub runtime_types::xcm::v3::multiasset::MultiAssets, - ); - impl ::subxt::events::StaticEvent for VersionChangeNotified { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "VersionChangeNotified"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "The supported version of a location has been changed. This might be through an"] - #[doc = "automatic notification or a manual intervention."] - #[doc = ""] - #[doc = "\\[ location, XCM version \\]"] - pub struct SupportedVersionChanged( - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub ::core::primitive::u32, - ); - impl ::subxt::events::StaticEvent for SupportedVersionChanged { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "SupportedVersionChanged"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "A given location which had a version change subscription was dropped owing to an error"] - #[doc = "sending the notification to it."] - #[doc = ""] - #[doc = "\\[ location, query ID, error \\]"] - pub struct NotifyTargetSendFail( - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub ::core::primitive::u64, - pub runtime_types::xcm::v3::traits::Error, - ); - impl ::subxt::events::StaticEvent for NotifyTargetSendFail { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "NotifyTargetSendFail"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "A given location which had a version change subscription was dropped owing to an error"] - #[doc = "migrating the location to our new XCM format."] - #[doc = ""] - #[doc = "\\[ location, query ID \\]"] - pub struct NotifyTargetMigrationFail( - pub runtime_types::xcm::VersionedMultiLocation, - pub ::core::primitive::u64, - ); - impl ::subxt::events::StaticEvent for NotifyTargetMigrationFail { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "NotifyTargetMigrationFail"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Expected query response has been received but the expected querier location placed in"] - #[doc = "storage by this runtime previously cannot be decoded. The query remains registered."] - #[doc = ""] - #[doc = "This is unexpected (since a location placed in storage in a previously executing"] - #[doc = "runtime should be readable prior to query timeout) and dangerous since the possibly"] - #[doc = "valid response will be dropped. Manual governance intervention is probably going to be"] - #[doc = "needed."] - #[doc = ""] - #[doc = "\\[ origin location, id \\]"] - pub struct InvalidQuerierVersion( - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub ::core::primitive::u64, - ); - impl ::subxt::events::StaticEvent for InvalidQuerierVersion { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "InvalidQuerierVersion"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Expected query response has been received but the querier location of the response does"] - #[doc = "not match the expected. The query remains registered for a later, valid, response to"] - #[doc = "be received and acted upon."] - #[doc = ""] - #[doc = "\\[ origin location, id, expected querier, maybe actual querier \\]"] - pub struct InvalidQuerier( - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub ::core::primitive::u64, - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub ::core::option::Option, - ); - impl ::subxt::events::StaticEvent for InvalidQuerier { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "InvalidQuerier"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "A remote has requested XCM version change notification from us and we have honored it."] - #[doc = "A version information message is sent to them and its cost is included."] - #[doc = ""] - #[doc = "\\[ destination location, cost \\]"] - pub struct VersionNotifyStarted( - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub runtime_types::xcm::v3::multiasset::MultiAssets, - ); - impl ::subxt::events::StaticEvent for VersionNotifyStarted { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "VersionNotifyStarted"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "We have requested that a remote chain sends us XCM version change notifications."] - #[doc = ""] - #[doc = "\\[ destination location, cost \\]"] - pub struct VersionNotifyRequested( - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub runtime_types::xcm::v3::multiasset::MultiAssets, - ); - impl ::subxt::events::StaticEvent for VersionNotifyRequested { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "VersionNotifyRequested"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "We have requested that a remote chain stops sending us XCM version change notifications."] - #[doc = ""] - #[doc = "\\[ destination location, cost \\]"] - pub struct VersionNotifyUnrequested( - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub runtime_types::xcm::v3::multiasset::MultiAssets, - ); - impl ::subxt::events::StaticEvent for VersionNotifyUnrequested { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "VersionNotifyUnrequested"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Fees were paid from a location for an operation (often for using `SendXcm`)."] - #[doc = ""] - #[doc = "\\[ paying location, fees \\]"] - pub struct FeesPaid( - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub runtime_types::xcm::v3::multiasset::MultiAssets, - ); - impl ::subxt::events::StaticEvent for FeesPaid { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "FeesPaid"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Some assets have been claimed from an asset trap"] - #[doc = ""] - #[doc = "\\[ hash, origin, assets \\]"] - pub struct AssetsClaimed( - pub ::subxt::utils::H256, - pub runtime_types::xcm::v3::multilocation::MultiLocation, - pub runtime_types::xcm::VersionedMultiAssets, - ); - impl ::subxt::events::StaticEvent for AssetsClaimed { - const PALLET: &'static str = "PolkadotXcm"; - const EVENT: &'static str = "AssetsClaimed"; - } - } - } - pub mod cumulus_xcm { - use super::{root_mod, runtime_types}; - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] - pub mod calls { - use super::{root_mod, runtime_types}; - type DispatchError = runtime_types::sp_runtime::DispatchError; - pub struct TransactionApi; - impl TransactionApi {} - } - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] - pub type Event = runtime_types::cumulus_pallet_xcm::pallet::Event; - pub mod events { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Downward message is invalid XCM."] - #[doc = "\\[ id \\]"] - pub struct InvalidFormat(pub [::core::primitive::u8; 32usize]); - impl ::subxt::events::StaticEvent for InvalidFormat { - const PALLET: &'static str = "CumulusXcm"; - const EVENT: &'static str = "InvalidFormat"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Downward message is unsupported version of XCM."] - #[doc = "\\[ id \\]"] - pub struct UnsupportedVersion(pub [::core::primitive::u8; 32usize]); - impl ::subxt::events::StaticEvent for UnsupportedVersion { - const PALLET: &'static str = "CumulusXcm"; - const EVENT: &'static str = "UnsupportedVersion"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Downward message executed with the given outcome."] - #[doc = "\\[ id, outcome \\]"] - pub struct ExecutedDownward( - pub [::core::primitive::u8; 32usize], - pub runtime_types::xcm::v3::traits::Outcome, - ); - impl ::subxt::events::StaticEvent for ExecutedDownward { - const PALLET: &'static str = "CumulusXcm"; - const EVENT: &'static str = "ExecutedDownward"; - } - } - } - pub mod dmp_queue { - use super::{root_mod, runtime_types}; - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] - pub mod calls { - use super::{root_mod, runtime_types}; - type DispatchError = runtime_types::sp_runtime::DispatchError; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ServiceOverweight { - pub index: ::core::primitive::u64, - pub weight_limit: ::sp_weights::Weight, - } - pub struct TransactionApi; - impl TransactionApi { - #[doc = "Service a single overweight message."] - pub fn service_overweight( - &self, - index: ::core::primitive::u64, - weight_limit: ::sp_weights::Weight, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "DmpQueue", - "service_overweight", - ServiceOverweight { index, weight_limit }, - [ - 121u8, 236u8, 235u8, 23u8, 210u8, 238u8, 238u8, 122u8, 15u8, 86u8, - 34u8, 119u8, 105u8, 100u8, 214u8, 236u8, 117u8, 39u8, 254u8, 235u8, - 189u8, 15u8, 72u8, 74u8, 225u8, 134u8, 148u8, 126u8, 31u8, 203u8, - 144u8, 106u8, - ], - ) - } - } - } - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] - pub type Event = runtime_types::cumulus_pallet_dmp_queue::pallet::Event; - pub mod events { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Downward message is invalid XCM."] - pub struct InvalidFormat { - pub message_id: [::core::primitive::u8; 32usize], - } - impl ::subxt::events::StaticEvent for InvalidFormat { - const PALLET: &'static str = "DmpQueue"; - const EVENT: &'static str = "InvalidFormat"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Downward message is unsupported version of XCM."] - pub struct UnsupportedVersion { - pub message_id: [::core::primitive::u8; 32usize], - } - impl ::subxt::events::StaticEvent for UnsupportedVersion { - const PALLET: &'static str = "DmpQueue"; - const EVENT: &'static str = "UnsupportedVersion"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Downward message executed with the given outcome."] - pub struct ExecutedDownward { - pub message_id: [::core::primitive::u8; 32usize], - pub outcome: runtime_types::xcm::v3::traits::Outcome, - } - impl ::subxt::events::StaticEvent for ExecutedDownward { - const PALLET: &'static str = "DmpQueue"; - const EVENT: &'static str = "ExecutedDownward"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "The weight limit for handling downward messages was reached."] - pub struct WeightExhausted { - pub message_id: [::core::primitive::u8; 32usize], - pub remaining_weight: ::sp_weights::Weight, - pub required_weight: ::sp_weights::Weight, - } - impl ::subxt::events::StaticEvent for WeightExhausted { - const PALLET: &'static str = "DmpQueue"; - const EVENT: &'static str = "WeightExhausted"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Downward message is overweight and was placed in the overweight queue."] - pub struct OverweightEnqueued { - pub message_id: [::core::primitive::u8; 32usize], - pub overweight_index: ::core::primitive::u64, - pub required_weight: ::sp_weights::Weight, - } - impl ::subxt::events::StaticEvent for OverweightEnqueued { - const PALLET: &'static str = "DmpQueue"; - const EVENT: &'static str = "OverweightEnqueued"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Downward message from the overweight queue was executed."] - pub struct OverweightServiced { - pub overweight_index: ::core::primitive::u64, - pub weight_used: ::sp_weights::Weight, - } - impl ::subxt::events::StaticEvent for OverweightServiced { - const PALLET: &'static str = "DmpQueue"; - const EVENT: &'static str = "OverweightServiced"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "The maximum number of downward messages was."] - pub struct MaxMessagesExhausted { - pub message_id: [::core::primitive::u8; 32usize], - } - impl ::subxt::events::StaticEvent for MaxMessagesExhausted { - const PALLET: &'static str = "DmpQueue"; - const EVENT: &'static str = "MaxMessagesExhausted"; - } - } - pub mod storage { - use super::runtime_types; - pub struct StorageApi; - impl StorageApi { - #[doc = " The configuration."] - pub fn configuration( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::cumulus_pallet_dmp_queue::ConfigData, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "DmpQueue", - "Configuration", - vec![], - [ - 133u8, 113u8, 115u8, 164u8, 128u8, 145u8, 234u8, 106u8, 150u8, 54u8, - 247u8, 135u8, 181u8, 197u8, 178u8, 30u8, 204u8, 46u8, 6u8, 137u8, 82u8, - 1u8, 75u8, 171u8, 7u8, 157u8, 3u8, 19u8, 92u8, 10u8, 234u8, 66u8, - ], - ) - } - #[doc = " The page index."] - pub fn page_index( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::cumulus_pallet_dmp_queue::PageIndexData, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "DmpQueue", - "PageIndex", - vec![], - [ - 94u8, 132u8, 34u8, 67u8, 10u8, 22u8, 235u8, 96u8, 168u8, 26u8, 57u8, - 200u8, 130u8, 218u8, 37u8, 71u8, 28u8, 119u8, 78u8, 107u8, 209u8, - 120u8, 190u8, 2u8, 101u8, 215u8, 122u8, 187u8, 94u8, 38u8, 255u8, - 234u8, - ], - ) - } - #[doc = " The queue pages."] - pub fn pages( - &self, - _0: impl ::std::borrow::Borrow<::core::primitive::u32>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - ::std::vec::Vec<( - ::core::primitive::u32, - ::std::vec::Vec<::core::primitive::u8>, - )>, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "DmpQueue", - "Pages", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - )], - [ - 228u8, 86u8, 33u8, 107u8, 248u8, 4u8, 223u8, 175u8, 222u8, 25u8, 204u8, - 42u8, 235u8, 21u8, 215u8, 91u8, 167u8, 14u8, 133u8, 151u8, 190u8, 57u8, - 138u8, 208u8, 79u8, 244u8, 132u8, 14u8, 48u8, 247u8, 171u8, 108u8, - ], - ) - } - #[doc = " The queue pages."] - pub fn pages_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - ::std::vec::Vec<( - ::core::primitive::u32, - ::std::vec::Vec<::core::primitive::u8>, - )>, - >, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "DmpQueue", - "Pages", - Vec::new(), - [ - 228u8, 86u8, 33u8, 107u8, 248u8, 4u8, 223u8, 175u8, 222u8, 25u8, 204u8, - 42u8, 235u8, 21u8, 215u8, 91u8, 167u8, 14u8, 133u8, 151u8, 190u8, 57u8, - 138u8, 208u8, 79u8, 244u8, 132u8, 14u8, 48u8, 247u8, 171u8, 108u8, - ], - ) - } - #[doc = " The overweight messages."] - pub fn overweight( - &self, - _0: impl ::std::borrow::Borrow<::core::primitive::u64>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<( - ::core::primitive::u32, - ::std::vec::Vec<::core::primitive::u8>, - )>, - ::subxt::storage::address::Yes, - (), - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "DmpQueue", - "Overweight", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - )], - [ - 222u8, 85u8, 143u8, 49u8, 42u8, 248u8, 138u8, 163u8, 46u8, 199u8, - 188u8, 61u8, 137u8, 135u8, 127u8, 146u8, 210u8, 254u8, 121u8, 42u8, - 112u8, 114u8, 22u8, 228u8, 207u8, 207u8, 245u8, 175u8, 152u8, 140u8, - 225u8, 237u8, - ], - ) - } - #[doc = " The overweight messages."] - pub fn overweight_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<( - ::core::primitive::u32, - ::std::vec::Vec<::core::primitive::u8>, - )>, - (), - (), - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "DmpQueue", - "Overweight", - Vec::new(), - [ - 222u8, 85u8, 143u8, 49u8, 42u8, 248u8, 138u8, 163u8, 46u8, 199u8, - 188u8, 61u8, 137u8, 135u8, 127u8, 146u8, 210u8, 254u8, 121u8, 42u8, - 112u8, 114u8, 22u8, 228u8, 207u8, 207u8, 245u8, 175u8, 152u8, 140u8, - 225u8, 237u8, - ], - ) - } - #[doc = "Counter for the related counted storage map"] - pub fn counter_for_overweight( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "DmpQueue", - "CounterForOverweight", - vec![], - [ - 148u8, 226u8, 248u8, 107u8, 165u8, 97u8, 218u8, 160u8, 127u8, 48u8, - 185u8, 251u8, 35u8, 137u8, 119u8, 251u8, 151u8, 167u8, 189u8, 66u8, - 80u8, 74u8, 134u8, 129u8, 222u8, 180u8, 51u8, 182u8, 50u8, 110u8, 10u8, - 43u8, - ], - ) - } - } - } - } - pub mod bridge_relayers { - use super::{root_mod, runtime_types}; - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] - pub mod calls { - use super::{root_mod, runtime_types}; - type DispatchError = runtime_types::sp_runtime::DispatchError; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ClaimRewards { - pub rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, - } - pub struct TransactionApi; - impl TransactionApi { - #[doc = "Claim accumulated rewards."] - pub fn claim_rewards( - &self, - rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "BridgeRelayers", - "claim_rewards", - ClaimRewards { rewards_account_params }, - [ - 141u8, 52u8, 193u8, 42u8, 145u8, 26u8, 147u8, 35u8, 227u8, 221u8, - 221u8, 188u8, 104u8, 186u8, 123u8, 46u8, 190u8, 236u8, 120u8, 19u8, - 230u8, 219u8, 238u8, 227u8, 75u8, 35u8, 36u8, 177u8, 227u8, 130u8, - 103u8, 128u8, - ], - ) - } - } - } - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] - pub type Event = runtime_types::pallet_bridge_relayers::pallet::Event; - pub mod events { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Reward has been paid to the relayer."] - pub struct RewardPaid { - pub relayer: ::sp_core::crypto::AccountId32, - pub rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, - pub reward: ::core::primitive::u128, - } - impl ::subxt::events::StaticEvent for RewardPaid { - const PALLET: &'static str = "BridgeRelayers"; - const EVENT: &'static str = "RewardPaid"; - } - } - pub mod storage { - use super::runtime_types; - pub struct StorageApi; - impl StorageApi { - #[doc = " Map of the relayer => accumulated reward."] - pub fn relayer_rewards( - &self, - _0: impl ::std::borrow::Borrow<::sp_core::crypto::AccountId32>, - _1: impl ::std::borrow::Borrow, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, - ::subxt::storage::address::Yes, - (), - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeRelayers", - "RelayerRewards", - vec![ - ::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - ), - ::subxt::storage::address::StorageMapKey::new( - _1.borrow(), - ::subxt::storage::address::StorageHasher::Identity, - ), - ], - [ - 116u8, 81u8, 48u8, 55u8, 199u8, 26u8, 100u8, 7u8, 177u8, 230u8, 132u8, - 248u8, 221u8, 90u8, 33u8, 155u8, 198u8, 216u8, 43u8, 149u8, 92u8, - 100u8, 199u8, 183u8, 150u8, 214u8, 199u8, 222u8, 224u8, 228u8, 238u8, - 108u8, - ], - ) - } - #[doc = " Map of the relayer => accumulated reward."] - pub fn relayer_rewards_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u128>, - (), - (), - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeRelayers", - "RelayerRewards", - Vec::new(), - [ - 116u8, 81u8, 48u8, 55u8, 199u8, 26u8, 100u8, 7u8, 177u8, 230u8, 132u8, - 248u8, 221u8, 90u8, 33u8, 155u8, 198u8, 216u8, 43u8, 149u8, 92u8, - 100u8, 199u8, 183u8, 150u8, 214u8, 199u8, 222u8, 224u8, 228u8, 238u8, - 108u8, - ], - ) - } - } - } - } - pub mod bridge_millau_grandpa { - use super::{root_mod, runtime_types}; - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] - pub mod calls { - use super::{root_mod, runtime_types}; - type DispatchError = runtime_types::sp_runtime::DispatchError; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SubmitFinalityProof { - pub finality_target: ::std::boxed::Box< - ::sp_runtime::generic::Header< - ::core::primitive::u64, - ::bp_millau::BlakeTwoAndKeccak256, - >, - >, - pub justification: ::bp_header_chain::justification::GrandpaJustification< - ::sp_runtime::generic::Header< - ::core::primitive::u64, - ::bp_millau::BlakeTwoAndKeccak256, - >, - >, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct Initialize { - pub init_data: ::bp_header_chain::InitializationData< - ::sp_runtime::generic::Header< - ::core::primitive::u64, - ::bp_millau::BlakeTwoAndKeccak256, - >, - >, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SetOwner { - pub new_owner: ::core::option::Option<::sp_core::crypto::AccountId32>, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SetOperatingMode { - pub operating_mode: runtime_types::bp_runtime::BasicOperatingMode, - } - pub struct TransactionApi; - impl TransactionApi { - #[doc = "Verify a target header is finalized according to the given finality proof."] - #[doc = ""] - #[doc = "It will use the underlying storage pallet to fetch information about the current"] - #[doc = "authorities and best finalized header in order to verify that the header is finalized."] - #[doc = ""] - #[doc = "If successful in verification, it will write the target header to the underlying storage"] - #[doc = "pallet."] - pub fn submit_finality_proof( - &self, - finality_target: ::sp_runtime::generic::Header< - ::core::primitive::u64, - ::bp_millau::BlakeTwoAndKeccak256, - >, - justification: ::bp_header_chain::justification::GrandpaJustification< - ::sp_runtime::generic::Header< - ::core::primitive::u64, - ::bp_millau::BlakeTwoAndKeccak256, - >, - >, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "BridgeMillauGrandpa", - "submit_finality_proof", - SubmitFinalityProof { - finality_target: ::std::boxed::Box::new(finality_target), - justification, - }, - [ - 3u8, 161u8, 243u8, 208u8, 245u8, 135u8, 86u8, 233u8, 103u8, 140u8, - 81u8, 3u8, 119u8, 185u8, 68u8, 167u8, 208u8, 155u8, 169u8, 201u8, - 209u8, 248u8, 162u8, 45u8, 155u8, 225u8, 173u8, 62u8, 156u8, 171u8, - 19u8, 190u8, - ], - ) - } - #[doc = "Bootstrap the bridge pallet with an initial header and authority set from which to sync."] - #[doc = ""] - #[doc = "The initial configuration provided does not need to be the genesis header of the bridged"] - #[doc = "chain, it can be any arbitrary header. You can also provide the next scheduled set"] - #[doc = "change if it is already know."] - #[doc = ""] - #[doc = "This function is only allowed to be called from a trusted origin and writes to storage"] - #[doc = "with practically no checks in terms of the validity of the data. It is important that"] - #[doc = "you ensure that valid data is being passed in."] - pub fn initialize( - &self, - init_data: ::bp_header_chain::InitializationData< - ::sp_runtime::generic::Header< - ::core::primitive::u64, - ::bp_millau::BlakeTwoAndKeccak256, - >, - >, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "BridgeMillauGrandpa", - "initialize", - Initialize { init_data }, - [ - 244u8, 188u8, 202u8, 145u8, 218u8, 91u8, 74u8, 80u8, 41u8, 185u8, - 239u8, 178u8, 231u8, 128u8, 198u8, 90u8, 135u8, 219u8, 200u8, 23u8, - 194u8, 47u8, 61u8, 222u8, 194u8, 84u8, 142u8, 37u8, 64u8, 37u8, 69u8, - 198u8, - ], - ) - } - #[doc = "Change `PalletOwner`."] - #[doc = ""] - #[doc = "May only be called either by root, or by `PalletOwner`."] - pub fn set_owner( - &self, - new_owner: ::core::option::Option<::sp_core::crypto::AccountId32>, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "BridgeMillauGrandpa", - "set_owner", - SetOwner { new_owner }, - [ - 100u8, 221u8, 84u8, 142u8, 158u8, 5u8, 47u8, 212u8, 9u8, 35u8, 82u8, - 135u8, 108u8, 238u8, 231u8, 197u8, 77u8, 219u8, 176u8, 222u8, 88u8, - 167u8, 152u8, 34u8, 177u8, 244u8, 160u8, 195u8, 211u8, 3u8, 66u8, - 253u8, - ], - ) - } - #[doc = "Halt or resume all pallet operations."] - #[doc = ""] - #[doc = "May only be called either by root, or by `PalletOwner`."] - pub fn set_operating_mode( - &self, - operating_mode: runtime_types::bp_runtime::BasicOperatingMode, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "BridgeMillauGrandpa", - "set_operating_mode", - SetOperatingMode { operating_mode }, - [ - 128u8, 25u8, 81u8, 145u8, 111u8, 185u8, 226u8, 152u8, 18u8, 51u8, 89u8, - 236u8, 200u8, 157u8, 157u8, 186u8, 207u8, 208u8, 152u8, 168u8, 12u8, - 39u8, 249u8, 48u8, 195u8, 160u8, 54u8, 73u8, 30u8, 230u8, 25u8, 46u8, - ], - ) - } - } - } - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] - pub type Event = runtime_types::pallet_bridge_grandpa::pallet::Event; - pub mod events { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Best finalized chain header has been updated to the header with given number and hash."] - pub struct UpdatedBestFinalizedHeader { - pub number: ::core::primitive::u64, - pub hash: ::bp_millau::MillauHash, - } - impl ::subxt::events::StaticEvent for UpdatedBestFinalizedHeader { - const PALLET: &'static str = "BridgeMillauGrandpa"; - const EVENT: &'static str = "UpdatedBestFinalizedHeader"; - } - } - pub mod storage { - use super::runtime_types; - pub struct StorageApi; - impl StorageApi { - #[doc = " The current number of requests which have written to storage."] - #[doc = ""] - #[doc = " If the `RequestCount` hits `MaxRequests`, no more calls will be allowed to the pallet until"] - #[doc = " the request capacity is increased."] - #[doc = ""] - #[doc = " The `RequestCount` is decreased by one at the beginning of every block. This is to ensure"] - #[doc = " that the pallet can always make progress."] - pub fn request_count( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauGrandpa", - "RequestCount", - vec![], - [ - 100u8, 156u8, 98u8, 176u8, 229u8, 85u8, 81u8, 159u8, 120u8, 156u8, - 33u8, 179u8, 224u8, 237u8, 52u8, 198u8, 81u8, 81u8, 10u8, 180u8, 53u8, - 141u8, 96u8, 4u8, 39u8, 217u8, 58u8, 9u8, 57u8, 79u8, 47u8, 201u8, - ], - ) - } - #[doc = " Hash of the header used to bootstrap the pallet."] - pub fn initial_hash( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::bp_millau::MillauHash>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauGrandpa", - "InitialHash", - vec![], - [ - 167u8, 131u8, 64u8, 215u8, 102u8, 70u8, 21u8, 34u8, 254u8, 233u8, 2u8, - 49u8, 253u8, 67u8, 235u8, 10u8, 21u8, 223u8, 220u8, 198u8, 180u8, - 137u8, 88u8, 251u8, 230u8, 108u8, 9u8, 104u8, 101u8, 105u8, 38u8, - 138u8, - ], - ) - } - #[doc = " Hash of the best finalized header."] - pub fn best_finalized( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bp_runtime::HeaderId< - ::bp_millau::MillauHash, - ::core::primitive::u64, - >, - >, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauGrandpa", - "BestFinalized", - vec![], - [ - 61u8, 173u8, 97u8, 223u8, 180u8, 235u8, 85u8, 126u8, 217u8, 228u8, - 87u8, 174u8, 116u8, 156u8, 162u8, 252u8, 100u8, 200u8, 138u8, 14u8, - 102u8, 177u8, 66u8, 164u8, 216u8, 114u8, 18u8, 223u8, 94u8, 78u8, - 107u8, 58u8, - ], - ) - } - #[doc = " A ring buffer of imported hashes. Ordered by the insertion time."] - pub fn imported_hashes( - &self, - _0: impl ::std::borrow::Borrow<::core::primitive::u32>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::bp_millau::MillauHash>, - ::subxt::storage::address::Yes, - (), - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauGrandpa", - "ImportedHashes", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Identity, - )], - [ - 249u8, 185u8, 202u8, 36u8, 40u8, 197u8, 111u8, 168u8, 136u8, 138u8, - 84u8, 135u8, 69u8, 34u8, 181u8, 46u8, 158u8, 16u8, 150u8, 190u8, 81u8, - 53u8, 239u8, 199u8, 83u8, 93u8, 197u8, 205u8, 129u8, 173u8, 141u8, - 43u8, - ], - ) - } - #[doc = " A ring buffer of imported hashes. Ordered by the insertion time."] - pub fn imported_hashes_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::bp_millau::MillauHash>, - (), - (), - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauGrandpa", - "ImportedHashes", - Vec::new(), - [ - 249u8, 185u8, 202u8, 36u8, 40u8, 197u8, 111u8, 168u8, 136u8, 138u8, - 84u8, 135u8, 69u8, 34u8, 181u8, 46u8, 158u8, 16u8, 150u8, 190u8, 81u8, - 53u8, 239u8, 199u8, 83u8, 93u8, 197u8, 205u8, 129u8, 173u8, 141u8, - 43u8, - ], - ) - } - #[doc = " Current ring buffer position."] - pub fn imported_hashes_pointer( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauGrandpa", - "ImportedHashesPointer", - vec![], - [ - 159u8, 83u8, 35u8, 45u8, 27u8, 249u8, 155u8, 131u8, 181u8, 196u8, - 224u8, 26u8, 92u8, 132u8, 127u8, 237u8, 13u8, 142u8, 196u8, 147u8, - 221u8, 216u8, 11u8, 78u8, 190u8, 241u8, 201u8, 96u8, 74u8, 185u8, - 208u8, 42u8, - ], - ) - } - #[doc = " Relevant fields of imported headers."] - pub fn imported_headers( - &self, - _0: impl ::std::borrow::Borrow<::bp_millau::MillauHash>, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bp_header_chain::StoredHeaderData< - ::core::primitive::u64, - ::bp_millau::MillauHash, - >, - >, - ::subxt::storage::address::Yes, - (), - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauGrandpa", - "ImportedHeaders", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Identity, - )], - [ - 109u8, 41u8, 102u8, 223u8, 133u8, 169u8, 46u8, 221u8, 235u8, 67u8, - 28u8, 192u8, 2u8, 242u8, 215u8, 166u8, 227u8, 182u8, 136u8, 217u8, - 61u8, 10u8, 246u8, 70u8, 17u8, 246u8, 223u8, 113u8, 9u8, 136u8, 181u8, - 242u8, - ], - ) - } - #[doc = " Relevant fields of imported headers."] - pub fn imported_headers_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bp_header_chain::StoredHeaderData< - ::core::primitive::u64, - ::bp_millau::MillauHash, - >, - >, - (), - (), - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauGrandpa", - "ImportedHeaders", - Vec::new(), - [ - 109u8, 41u8, 102u8, 223u8, 133u8, 169u8, 46u8, 221u8, 235u8, 67u8, - 28u8, 192u8, 2u8, 242u8, 215u8, 166u8, 227u8, 182u8, 136u8, 217u8, - 61u8, 10u8, 246u8, 70u8, 17u8, 246u8, 223u8, 113u8, 9u8, 136u8, 181u8, - 242u8, - ], - ) - } - #[doc = " The current GRANDPA Authority set."] - pub fn current_authority_set( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::pallet_bridge_grandpa::storage_types::StoredAuthoritySet, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauGrandpa", - "CurrentAuthoritySet", - vec![], - [ - 249u8, 40u8, 229u8, 120u8, 141u8, 219u8, 206u8, 5u8, 54u8, 121u8, - 207u8, 77u8, 8u8, 80u8, 105u8, 107u8, 151u8, 111u8, 82u8, 119u8, 8u8, - 31u8, 104u8, 82u8, 92u8, 156u8, 37u8, 160u8, 235u8, 64u8, 62u8, 94u8, - ], - ) - } - #[doc = " Optional pallet owner."] - #[doc = ""] - #[doc = " Pallet owner has a right to halt all pallet operations and then resume it. If it is"] - #[doc = " `None`, then there are no direct ways to halt/resume pallet operations, but other"] - #[doc = " runtime methods may still be used to do that (i.e. democracy::referendum to update halt"] - #[doc = " flag directly or call the `halt_operations`)."] - pub fn pallet_owner( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::sp_core::crypto::AccountId32>, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauGrandpa", - "PalletOwner", - vec![], - [ - 89u8, 42u8, 74u8, 119u8, 21u8, 164u8, 30u8, 115u8, 207u8, 126u8, 98u8, - 16u8, 162u8, 214u8, 67u8, 172u8, 178u8, 223u8, 139u8, 121u8, 174u8, - 89u8, 215u8, 75u8, 200u8, 161u8, 61u8, 195u8, 65u8, 222u8, 246u8, - 233u8, - ], - ) - } - #[doc = " The current operating mode of the pallet."] - #[doc = ""] - #[doc = " Depending on the mode either all, or no transactions will be allowed."] - pub fn pallet_operating_mode( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bp_runtime::BasicOperatingMode, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauGrandpa", - "PalletOperatingMode", - vec![], - [ - 218u8, 66u8, 212u8, 71u8, 38u8, 152u8, 55u8, 129u8, 125u8, 231u8, 91u8, - 216u8, 157u8, 141u8, 173u8, 146u8, 30u8, 40u8, 132u8, 107u8, 97u8, - 39u8, 36u8, 81u8, 231u8, 222u8, 113u8, 136u8, 233u8, 212u8, 225u8, - 75u8, - ], - ) - } - } - } - pub mod constants { - use super::runtime_types; - pub struct ConstantsApi; - impl ConstantsApi { - #[doc = " The upper bound on the number of requests allowed by the pallet."] - #[doc = ""] - #[doc = " A request refers to an action which writes a header to storage."] - #[doc = ""] - #[doc = " Once this bound is reached the pallet will not allow any dispatchables to be called"] - #[doc = " until the request count has decreased."] - pub fn max_requests( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - > { - ::subxt::constants::StaticConstantAddress::new( - "BridgeMillauGrandpa", - "MaxRequests", - [ - 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, - 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, - 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, - 145u8, - ], - ) - } - #[doc = " Maximal number of finalized headers to keep in the storage."] - #[doc = ""] - #[doc = " The setting is there to prevent growing the on-chain state indefinitely. Note"] - #[doc = " the setting does not relate to block numbers - we will simply keep as much items"] - #[doc = " in the storage, so it doesn't guarantee any fixed timeframe for finality headers."] - #[doc = ""] - #[doc = " Incautious change of this constant may lead to orphan entries in the runtime storage."] - pub fn headers_to_keep( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - > { - ::subxt::constants::StaticConstantAddress::new( - "BridgeMillauGrandpa", - "HeadersToKeep", - [ - 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, - 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, - 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, - 145u8, - ], - ) - } - } - } - } - pub mod bridge_millau_messages { - use super::{root_mod, runtime_types}; - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] - pub mod calls { - use super::{root_mod, runtime_types}; - type DispatchError = runtime_types::sp_runtime::DispatchError; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SetOwner { - pub new_owner: ::core::option::Option<::sp_core::crypto::AccountId32>, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct SetOperatingMode { - pub operating_mode: runtime_types::bp_messages::MessagesOperatingMode, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ReceiveMessagesProof { - pub relayer_id_at_bridged_chain: ::sp_core::crypto::AccountId32, - pub proof: ::bridge_runtime_common::messages::target::FromBridgedChainMessagesProof< - ::bp_millau::MillauHash, - >, - pub messages_count: ::core::primitive::u32, - pub dispatch_weight: ::sp_weights::Weight, - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - pub struct ReceiveMessagesDeliveryProof { pub proof : :: bridge_runtime_common :: messages :: source :: FromBridgedChainMessagesDeliveryProof < :: bp_millau :: MillauHash > , pub relayers_state : :: bp_messages :: UnrewardedRelayersState , } - pub struct TransactionApi; - impl TransactionApi { - #[doc = "Change `PalletOwner`."] - #[doc = ""] - #[doc = "May only be called either by root, or by `PalletOwner`."] - pub fn set_owner( - &self, - new_owner: ::core::option::Option<::sp_core::crypto::AccountId32>, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "BridgeMillauMessages", - "set_owner", - SetOwner { new_owner }, - [ - 100u8, 221u8, 84u8, 142u8, 158u8, 5u8, 47u8, 212u8, 9u8, 35u8, 82u8, - 135u8, 108u8, 238u8, 231u8, 197u8, 77u8, 219u8, 176u8, 222u8, 88u8, - 167u8, 152u8, 34u8, 177u8, 244u8, 160u8, 195u8, 211u8, 3u8, 66u8, - 253u8, - ], - ) - } - #[doc = "Halt or resume all/some pallet operations."] - #[doc = ""] - #[doc = "May only be called either by root, or by `PalletOwner`."] - pub fn set_operating_mode( - &self, - operating_mode: runtime_types::bp_messages::MessagesOperatingMode, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "BridgeMillauMessages", - "set_operating_mode", - SetOperatingMode { operating_mode }, - [ - 236u8, 230u8, 127u8, 17u8, 145u8, 186u8, 102u8, 200u8, 227u8, 208u8, - 230u8, 121u8, 102u8, 199u8, 123u8, 118u8, 199u8, 160u8, 131u8, 116u8, - 102u8, 167u8, 119u8, 144u8, 70u8, 114u8, 0u8, 223u8, 54u8, 197u8, 39u8, - 58u8, - ], - ) - } - #[doc = "Receive messages proof from bridged chain."] - #[doc = ""] - #[doc = "The weight of the call assumes that the transaction always brings outbound lane"] - #[doc = "state update. Because of that, the submitter (relayer) has no benefit of not including"] - #[doc = "this data in the transaction, so reward confirmations lags should be minimal."] - pub fn receive_messages_proof( - &self, - relayer_id_at_bridged_chain: ::sp_core::crypto::AccountId32, - proof: ::bridge_runtime_common::messages::target::FromBridgedChainMessagesProof< - ::bp_millau::MillauHash, - >, - messages_count: ::core::primitive::u32, - dispatch_weight: ::sp_weights::Weight, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "BridgeMillauMessages", - "receive_messages_proof", - ReceiveMessagesProof { - relayer_id_at_bridged_chain, - proof, - messages_count, - dispatch_weight, - }, - [ - 14u8, 23u8, 191u8, 242u8, 252u8, 163u8, 87u8, 149u8, 211u8, 25u8, - 112u8, 192u8, 206u8, 49u8, 135u8, 124u8, 79u8, 92u8, 204u8, 39u8, 80u8, - 139u8, 155u8, 124u8, 134u8, 1u8, 66u8, 68u8, 213u8, 85u8, 42u8, 250u8, - ], - ) - } - #[doc = "Receive messages delivery proof from bridged chain."] - pub fn receive_messages_delivery_proof( - &self, - proof : :: bridge_runtime_common :: messages :: source :: FromBridgedChainMessagesDeliveryProof < :: bp_millau :: MillauHash >, - relayers_state: ::bp_messages::UnrewardedRelayersState, - ) -> ::subxt::tx::StaticTxPayload { - ::subxt::tx::StaticTxPayload::new( - "BridgeMillauMessages", - "receive_messages_delivery_proof", - ReceiveMessagesDeliveryProof { proof, relayers_state }, - [ - 216u8, 113u8, 64u8, 20u8, 16u8, 64u8, 144u8, 143u8, 105u8, 30u8, 192u8, - 89u8, 74u8, 34u8, 56u8, 151u8, 20u8, 234u8, 14u8, 211u8, 64u8, 103u8, - 218u8, 164u8, 69u8, 107u8, 6u8, 119u8, 13u8, 90u8, 150u8, 24u8, - ], - ) - } - } - } - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] - pub type Event = runtime_types::pallet_bridge_messages::pallet::Event; - pub mod events { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Message has been accepted and is waiting to be delivered."] - pub struct MessageAccepted { - pub lane_id: runtime_types::bp_messages::LaneId, - pub nonce: ::core::primitive::u64, - } - impl ::subxt::events::StaticEvent for MessageAccepted { - const PALLET: &'static str = "BridgeMillauMessages"; - const EVENT: &'static str = "MessageAccepted"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Messages have been received from the bridged chain."] - pub struct MessagesReceived (pub :: std :: vec :: Vec < runtime_types :: bp_messages :: ReceivedMessages < runtime_types :: bridge_runtime_common :: messages_xcm_extension :: XcmBlobMessageDispatchResult > > ,) ; - impl ::subxt::events::StaticEvent for MessagesReceived { - const PALLET: &'static str = "BridgeMillauMessages"; - const EVENT: &'static str = "MessagesReceived"; - } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] - #[doc = "Messages in the inclusive range have been delivered to the bridged chain."] - pub struct MessagesDelivered { - pub lane_id: runtime_types::bp_messages::LaneId, - pub messages: runtime_types::bp_messages::DeliveredMessages, - } - impl ::subxt::events::StaticEvent for MessagesDelivered { - const PALLET: &'static str = "BridgeMillauMessages"; - const EVENT: &'static str = "MessagesDelivered"; - } - } - pub mod storage { - use super::runtime_types; - pub struct StorageApi; - impl StorageApi { - #[doc = " Optional pallet owner."] - #[doc = ""] - #[doc = " Pallet owner has a right to halt all pallet operations and then resume it. If it is"] - #[doc = " `None`, then there are no direct ways to halt/resume pallet operations, but other"] - #[doc = " runtime methods may still be used to do that (i.e. democracy::referendum to update halt"] - #[doc = " flag directly or call the `halt_operations`)."] - pub fn pallet_owner( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType<::sp_core::crypto::AccountId32>, - ::subxt::storage::address::Yes, - (), - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauMessages", - "PalletOwner", - vec![], - [ - 89u8, 42u8, 74u8, 119u8, 21u8, 164u8, 30u8, 115u8, 207u8, 126u8, 98u8, - 16u8, 162u8, 214u8, 67u8, 172u8, 178u8, 223u8, 139u8, 121u8, 174u8, - 89u8, 215u8, 75u8, 200u8, 161u8, 61u8, 195u8, 65u8, 222u8, 246u8, - 233u8, - ], - ) - } - #[doc = " The current operating mode of the pallet."] - #[doc = ""] - #[doc = " Depending on the mode either all, some, or no transactions will be allowed."] - pub fn pallet_operating_mode( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bp_messages::MessagesOperatingMode, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - (), - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauMessages", - "PalletOperatingMode", - vec![], - [ - 215u8, 195u8, 85u8, 231u8, 158u8, 22u8, 160u8, 132u8, 69u8, 206u8, - 238u8, 14u8, 56u8, 100u8, 134u8, 41u8, 58u8, 120u8, 225u8, 164u8, - 173u8, 87u8, 22u8, 123u8, 102u8, 167u8, 68u8, 70u8, 184u8, 131u8, - 232u8, 65u8, - ], - ) - } - #[doc = " Map of lane id => inbound lane data."] - pub fn inbound_lanes( - &self, - _0: impl ::std::borrow::Borrow, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bp_messages::InboundLaneData<::sp_core::crypto::AccountId32>, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauMessages", - "InboundLanes", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - )], - [ - 38u8, 58u8, 110u8, 130u8, 112u8, 76u8, 231u8, 76u8, 56u8, 241u8, 183u8, - 153u8, 112u8, 41u8, 248u8, 208u8, 217u8, 57u8, 102u8, 30u8, 107u8, - 98u8, 59u8, 78u8, 56u8, 119u8, 186u8, 183u8, 213u8, 72u8, 199u8, 90u8, - ], - ) - } - #[doc = " Map of lane id => inbound lane data."] - pub fn inbound_lanes_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bp_messages::InboundLaneData<::sp_core::crypto::AccountId32>, - >, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauMessages", - "InboundLanes", - Vec::new(), - [ - 38u8, 58u8, 110u8, 130u8, 112u8, 76u8, 231u8, 76u8, 56u8, 241u8, 183u8, - 153u8, 112u8, 41u8, 248u8, 208u8, 217u8, 57u8, 102u8, 30u8, 107u8, - 98u8, 59u8, 78u8, 56u8, 119u8, 186u8, 183u8, 213u8, 72u8, 199u8, 90u8, - ], - ) - } - #[doc = " Map of lane id => outbound lane data."] - pub fn outbound_lanes( - &self, - _0: impl ::std::borrow::Borrow, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bp_messages::OutboundLaneData, - >, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauMessages", - "OutboundLanes", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - )], - [ - 67u8, 155u8, 173u8, 244u8, 80u8, 38u8, 26u8, 71u8, 51u8, 150u8, 86u8, - 146u8, 132u8, 122u8, 70u8, 122u8, 172u8, 246u8, 106u8, 232u8, 149u8, - 227u8, 240u8, 146u8, 51u8, 184u8, 30u8, 182u8, 200u8, 43u8, 190u8, - 38u8, - ], - ) - } - #[doc = " Map of lane id => outbound lane data."] - pub fn outbound_lanes_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bp_messages::OutboundLaneData, - >, - (), - ::subxt::storage::address::Yes, - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauMessages", - "OutboundLanes", - Vec::new(), - [ - 67u8, 155u8, 173u8, 244u8, 80u8, 38u8, 26u8, 71u8, 51u8, 150u8, 86u8, - 146u8, 132u8, 122u8, 70u8, 122u8, 172u8, 246u8, 106u8, 232u8, 149u8, - 227u8, 240u8, 146u8, 51u8, 184u8, 30u8, 182u8, 200u8, 43u8, 190u8, - 38u8, - ], - ) - } - #[doc = " All queued outbound messages."] - pub fn outbound_messages( - &self, - _0: impl ::std::borrow::Borrow, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bounded_collections::bounded_vec::BoundedVec< - ::core::primitive::u8, - >, - >, - ::subxt::storage::address::Yes, - (), - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauMessages", - "OutboundMessages", - vec![::subxt::storage::address::StorageMapKey::new( - _0.borrow(), - ::subxt::storage::address::StorageHasher::Blake2_128Concat, - )], - [ - 44u8, 35u8, 2u8, 25u8, 91u8, 101u8, 152u8, 23u8, 48u8, 250u8, 178u8, - 15u8, 194u8, 118u8, 146u8, 1u8, 112u8, 83u8, 243u8, 166u8, 124u8, - 153u8, 48u8, 193u8, 43u8, 31u8, 33u8, 72u8, 228u8, 113u8, 86u8, 217u8, - ], - ) - } - #[doc = " All queued outbound messages."] - pub fn outbound_messages_root( - &self, - ) -> ::subxt::storage::address::StaticStorageAddress< - ::subxt::metadata::DecodeStaticType< - runtime_types::bounded_collections::bounded_vec::BoundedVec< - ::core::primitive::u8, - >, - >, - (), - (), - ::subxt::storage::address::Yes, - > { - ::subxt::storage::address::StaticStorageAddress::new( - "BridgeMillauMessages", - "OutboundMessages", - Vec::new(), - [ - 44u8, 35u8, 2u8, 25u8, 91u8, 101u8, 152u8, 23u8, 48u8, 250u8, 178u8, - 15u8, 194u8, 118u8, 146u8, 1u8, 112u8, 83u8, 243u8, 166u8, 124u8, - 153u8, 48u8, 193u8, 43u8, 31u8, 33u8, 72u8, 228u8, 113u8, 86u8, 217u8, - ], - ) - } - } - } - pub mod constants { - use super::runtime_types; - pub struct ConstantsApi; - impl ConstantsApi { - #[doc = " Gets the chain id value from the instance."] - pub fn bridged_chain_id( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType<[::core::primitive::u8; 4usize]>, - > { - ::subxt::constants::StaticConstantAddress::new( - "BridgeMillauMessages", - "BridgedChainId", - [ - 101u8, 157u8, 37u8, 163u8, 190u8, 134u8, 129u8, 212u8, 240u8, 135u8, - 174u8, 76u8, 220u8, 179u8, 252u8, 69u8, 65u8, 253u8, 69u8, 214u8, 61u8, - 249u8, 4u8, 38u8, 181u8, 237u8, 25u8, 131u8, 242u8, 20u8, 17u8, 152u8, - ], - ) - } - #[doc = " Maximal encoded size of the outbound payload."] - pub fn maximal_outbound_payload_size( - &self, - ) -> ::subxt::constants::StaticConstantAddress< - ::subxt::metadata::DecodeStaticType<::core::primitive::u32>, - > { - ::subxt::constants::StaticConstantAddress::new( - "BridgeMillauMessages", - "MaximalOutboundPayloadSize", - [ - 98u8, 252u8, 116u8, 72u8, 26u8, 180u8, 225u8, 83u8, 200u8, 157u8, - 125u8, 151u8, 53u8, 76u8, 168u8, 26u8, 10u8, 9u8, 98u8, 68u8, 9u8, - 178u8, 197u8, 113u8, 31u8, 79u8, 200u8, 90u8, 203u8, 100u8, 41u8, - 145u8, - ], - ) - } - } - } - } pub mod runtime_types { use super::runtime_types; pub mod bounded_collections { use super::runtime_types; pub mod bounded_vec { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct BoundedVec<_0>(pub ::std::vec::Vec<_0>); } pub mod weak_bounded_vec { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct WeakBoundedVec<_0>(pub ::std::vec::Vec<_0>); } } pub mod bp_header_chain { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct StoredHeaderData<_0, _1> { pub number: _0, pub state_root: _1, @@ -5979,51 +47,37 @@ pub mod api { } pub mod bp_messages { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct DeliveredMessages { pub begin: ::core::primitive::u64, pub end: ::core::primitive::u64, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct InboundLaneData<_0> { pub relayers: ::std::vec::Vec>, pub last_confirmed_nonce: ::core::primitive::u64, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct LaneId(pub [::core::primitive::u8; 4usize]); - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct MessageKey { pub lane_id: runtime_types::bp_messages::LaneId, pub nonce: ::core::primitive::u64, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum MessagesOperatingMode { #[codec(index = 0)] Basic(runtime_types::bp_runtime::BasicOperatingMode), #[codec(index = 1)] RejectingOutboundMessages, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct OutboundLaneData { pub oldest_unpruned_nonce: ::core::primitive::u64, pub latest_received_nonce: ::core::primitive::u64, pub latest_generated_nonce: ::core::primitive::u64, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum ReceivalResult<_0> { #[codec(index = 0)] Dispatched(runtime_types::bp_runtime::messages::MessageDispatchResult<_0>), @@ -6034,9 +88,7 @@ pub mod api { #[codec(index = 3)] TooManyUnconfirmedMessages, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct ReceivedMessages<_0> { pub lane: runtime_types::bp_messages::LaneId, pub receive_results: ::std::vec::Vec<( @@ -6044,9 +96,7 @@ pub mod api { runtime_types::bp_messages::ReceivalResult<_0>, )>, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct UnrewardedRelayer<_0> { pub relayer: _0, pub messages: runtime_types::bp_messages::DeliveredMessages, @@ -6054,18 +104,14 @@ pub mod api { } pub mod bp_relayers { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum RewardsAccountOwner { #[codec(index = 0)] ThisChain, #[codec(index = 1)] BridgedChain, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct RewardsAccountParams { pub lane_id: runtime_types::bp_messages::LaneId, pub bridged_chain_id: [::core::primitive::u8; 4usize], @@ -6076,33 +122,22 @@ pub mod api { use super::runtime_types; pub mod messages { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct MessageDispatchResult<_0> { pub unspent_weight: ::sp_weights::Weight, pub dispatch_level_result: _0, } } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum BasicOperatingMode { #[codec(index = 0)] Normal, #[codec(index = 1)] Halted, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct HeaderId<_0, _1>(pub _1, pub _0); - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum OwnedBridgeModuleError { #[codec(index = 0)] Halted, @@ -6112,12 +147,7 @@ pub mod api { use super::runtime_types; pub mod messages_xcm_extension { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum XcmBlobMessageDispatchResult { #[codec(index = 0)] InvalidPayload, @@ -6132,90 +162,58 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Call { #[codec(index = 0)] - #[doc = "Service a single overweight message."] service_overweight { index: ::core::primitive::u64, weight_limit: ::sp_weights::Weight, }, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Error { #[codec(index = 0)] - #[doc = "The message index given is unknown."] Unknown, #[codec(index = 1)] - #[doc = "The amount of weight given is possibly not enough for executing the message."] OverLimit, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Event { #[codec(index = 0)] - #[doc = "Downward message is invalid XCM."] InvalidFormat { message_id: [::core::primitive::u8; 32usize] }, #[codec(index = 1)] - #[doc = "Downward message is unsupported version of XCM."] UnsupportedVersion { message_id: [::core::primitive::u8; 32usize] }, #[codec(index = 2)] - #[doc = "Downward message executed with the given outcome."] ExecutedDownward { message_id: [::core::primitive::u8; 32usize], outcome: runtime_types::xcm::v3::traits::Outcome, }, #[codec(index = 3)] - #[doc = "The weight limit for handling downward messages was reached."] WeightExhausted { message_id: [::core::primitive::u8; 32usize], remaining_weight: ::sp_weights::Weight, required_weight: ::sp_weights::Weight, }, #[codec(index = 4)] - #[doc = "Downward message is overweight and was placed in the overweight queue."] OverweightEnqueued { message_id: [::core::primitive::u8; 32usize], overweight_index: ::core::primitive::u64, required_weight: ::sp_weights::Weight, }, #[codec(index = 5)] - #[doc = "Downward message from the overweight queue was executed."] OverweightServiced { overweight_index: ::core::primitive::u64, weight_used: ::sp_weights::Weight, }, #[codec(index = 6)] - #[doc = "The maximum number of downward messages was."] MaxMessagesExhausted { message_id: [::core::primitive::u8; 32usize] }, } } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct ConfigData { pub max_individual: ::sp_weights::Weight, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct PageIndexData { pub begin_used: ::core::primitive::u32, pub end_used: ::core::primitive::u32, @@ -6226,80 +224,46 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Call { - # [codec (index = 0)] # [doc = "Set the current validation data."] # [doc = ""] # [doc = "This should be invoked exactly once per block. It will panic at the finalization"] # [doc = "phase if the call was not invoked."] # [doc = ""] # [doc = "The dispatch origin for this call must be `Inherent`"] # [doc = ""] # [doc = "As a side effect, this function upgrades the current validation function"] # [doc = "if the appropriate time has come."] set_validation_data { data : runtime_types :: cumulus_primitives_parachain_inherent :: ParachainInherentData , } , # [codec (index = 1)] sudo_send_upward_message { message : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , # [codec (index = 2)] # [doc = "Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied"] # [doc = "later."] # [doc = ""] # [doc = "The `check_version` parameter sets a boolean flag for whether or not the runtime's spec"] # [doc = "version and name should be verified on upgrade. Since the authorization only has a hash,"] # [doc = "it cannot actually perform the verification."] # [doc = ""] # [doc = "This call requires Root origin."] authorize_upgrade { code_hash : :: subxt :: utils :: H256 , check_version : :: core :: primitive :: bool , } , # [codec (index = 3)] # [doc = "Provide the preimage (runtime binary) `code` for an upgrade that has been authorized."] # [doc = ""] # [doc = "If the authorization required a version check, this call will ensure the spec name"] # [doc = "remains unchanged and that the spec version has increased."] # [doc = ""] # [doc = "Note that this function will not apply the new `code`, but only attempt to schedule the"] # [doc = "upgrade with the Relay Chain."] # [doc = ""] # [doc = "All origins are allowed."] enact_authorized_upgrade { code : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + # [codec (index = 0)] set_validation_data { data : runtime_types :: cumulus_primitives_parachain_inherent :: ParachainInherentData , } , # [codec (index = 1)] sudo_send_upward_message { message : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , # [codec (index = 2)] authorize_upgrade { code_hash : :: subxt :: utils :: H256 , check_version : :: core :: primitive :: bool , } , # [codec (index = 3)] enact_authorized_upgrade { code : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Error { #[codec(index = 0)] - #[doc = "Attempt to upgrade validation function while existing upgrade pending."] OverlappingUpgrades, #[codec(index = 1)] - #[doc = "Polkadot currently prohibits this parachain from upgrading its validation function."] ProhibitedByPolkadot, #[codec(index = 2)] - #[doc = "The supplied validation function has compiled into a blob larger than Polkadot is"] - #[doc = "willing to run."] TooBig, #[codec(index = 3)] - #[doc = "The inherent which supplies the validation data did not run this block."] ValidationDataNotAvailable, #[codec(index = 4)] - #[doc = "The inherent which supplies the host configuration did not run this block."] HostConfigurationNotAvailable, #[codec(index = 5)] - #[doc = "No validation function upgrade is currently scheduled."] NotScheduled, #[codec(index = 6)] - #[doc = "No code upgrade has been authorized."] NothingAuthorized, #[codec(index = 7)] - #[doc = "The given code upgrade has not been authorized."] Unauthorized, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Event { #[codec(index = 0)] - #[doc = "The validation function has been scheduled to apply."] ValidationFunctionStored, #[codec(index = 1)] - #[doc = "The validation function was applied as of the contained relay chain block number."] ValidationFunctionApplied { relay_chain_block_num: ::core::primitive::u32 }, #[codec(index = 2)] - #[doc = "The relay-chain aborted the upgrade process."] ValidationFunctionDiscarded, #[codec(index = 3)] - #[doc = "An upgrade has been authorized."] UpgradeAuthorized { code_hash: ::subxt::utils::H256 }, #[codec(index = 4)] - #[doc = "Some downward messages have been received and will be processed."] DownwardMessagesReceived { count: ::core::primitive::u32 }, #[codec(index = 5)] - #[doc = "Downward messages were processed using the given weight."] DownwardMessagesProcessed { weight_used: ::sp_weights::Weight, dmq_head: ::subxt::utils::H256, }, #[codec(index = 6)] - #[doc = "An upward message was sent to the relay chain."] UpwardMessageSent { message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, }, @@ -6307,12 +271,7 @@ pub mod api { } pub mod relay_state_snapshot { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct MessagingStateSnapshot { pub dmq_mqc_head: ::subxt::utils::H256, pub relay_dispatch_queue_size: (::core::primitive::u32, ::core::primitive::u32), @@ -6326,9 +285,7 @@ pub mod api { )>, } } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct CodeUpgradeAuthorization { pub code_hash: ::subxt::utils::H256, pub check_version: ::core::primitive::bool, @@ -6338,41 +295,17 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Call {} - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Error {} - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Event { #[codec(index = 0)] - #[doc = "Downward message is invalid XCM."] - #[doc = "\\[ id \\]"] InvalidFormat([::core::primitive::u8; 32usize]), #[codec(index = 1)] - #[doc = "Downward message is unsupported version of XCM."] - #[doc = "\\[ id \\]"] UnsupportedVersion([::core::primitive::u8; 32usize]), #[codec(index = 2)] - #[doc = "Downward message executed with the given outcome."] - #[doc = "\\[ id, outcome \\]"] ExecutedDownward( [::core::primitive::u8; 32usize], runtime_types::xcm::v3::traits::Outcome, @@ -6384,148 +317,69 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Call { #[codec(index = 0)] - #[doc = "Services a single overweight XCM."] - #[doc = ""] - #[doc = "- `origin`: Must pass `ExecuteOverweightOrigin`."] - #[doc = "- `index`: The index of the overweight XCM to service"] - #[doc = "- `weight_limit`: The amount of weight that XCM execution may take."] - #[doc = ""] - #[doc = "Errors:"] - #[doc = "- `BadOverweightIndex`: XCM under `index` is not found in the `Overweight` storage map."] - #[doc = "- `BadXcm`: XCM under `index` cannot be properly decoded into a valid XCM format."] - #[doc = "- `WeightOverLimit`: XCM execution may use greater `weight_limit`."] - #[doc = ""] - #[doc = "Events:"] - #[doc = "- `OverweightServiced`: On success."] service_overweight { index: ::core::primitive::u64, weight_limit: ::sp_weights::Weight, }, #[codec(index = 1)] - #[doc = "Suspends all XCM executions for the XCMP queue, regardless of the sender's origin."] - #[doc = ""] - #[doc = "- `origin`: Must pass `ControllerOrigin`."] suspend_xcm_execution, #[codec(index = 2)] - #[doc = "Resumes all XCM executions for the XCMP queue."] - #[doc = ""] - #[doc = "Note that this function doesn't change the status of the in/out bound channels."] - #[doc = ""] - #[doc = "- `origin`: Must pass `ControllerOrigin`."] resume_xcm_execution, #[codec(index = 3)] - #[doc = "Overwrites the number of pages of messages which must be in the queue for the other side to be told to"] - #[doc = "suspend their sending."] - #[doc = ""] - #[doc = "- `origin`: Must pass `Root`."] - #[doc = "- `new`: Desired value for `QueueConfigData.suspend_value`"] update_suspend_threshold { new: ::core::primitive::u32 }, #[codec(index = 4)] - #[doc = "Overwrites the number of pages of messages which must be in the queue after which we drop any further"] - #[doc = "messages from the channel."] - #[doc = ""] - #[doc = "- `origin`: Must pass `Root`."] - #[doc = "- `new`: Desired value for `QueueConfigData.drop_threshold`"] update_drop_threshold { new: ::core::primitive::u32 }, #[codec(index = 5)] - #[doc = "Overwrites the number of pages of messages which the queue must be reduced to before it signals that"] - #[doc = "message sending may recommence after it has been suspended."] - #[doc = ""] - #[doc = "- `origin`: Must pass `Root`."] - #[doc = "- `new`: Desired value for `QueueConfigData.resume_threshold`"] update_resume_threshold { new: ::core::primitive::u32 }, #[codec(index = 6)] - #[doc = "Overwrites the amount of remaining weight under which we stop processing messages."] - #[doc = ""] - #[doc = "- `origin`: Must pass `Root`."] - #[doc = "- `new`: Desired value for `QueueConfigData.threshold_weight`"] update_threshold_weight { new: ::sp_weights::Weight }, #[codec(index = 7)] - #[doc = "Overwrites the speed to which the available weight approaches the maximum weight."] - #[doc = "A lower number results in a faster progression. A value of 1 makes the entire weight available initially."] - #[doc = ""] - #[doc = "- `origin`: Must pass `Root`."] - #[doc = "- `new`: Desired value for `QueueConfigData.weight_restrict_decay`."] update_weight_restrict_decay { new: ::sp_weights::Weight }, #[codec(index = 8)] - #[doc = "Overwrite the maximum amount of weight any individual message may consume."] - #[doc = "Messages above this weight go into the overweight queue and may only be serviced explicitly."] - #[doc = ""] - #[doc = "- `origin`: Must pass `Root`."] - #[doc = "- `new`: Desired value for `QueueConfigData.xcmp_max_individual_weight`."] update_xcmp_max_individual_weight { new: ::sp_weights::Weight }, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Error { #[codec(index = 0)] - #[doc = "Failed to send XCM message."] FailedToSend, #[codec(index = 1)] - #[doc = "Bad XCM origin."] BadXcmOrigin, #[codec(index = 2)] - #[doc = "Bad XCM data."] BadXcm, #[codec(index = 3)] - #[doc = "Bad overweight index."] BadOverweightIndex, #[codec(index = 4)] - #[doc = "Provided weight is possibly not enough to execute the message."] WeightOverLimit, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Event { #[codec(index = 0)] - #[doc = "Some XCM was executed ok."] Success { message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, weight: ::sp_weights::Weight, }, #[codec(index = 1)] - #[doc = "Some XCM failed."] Fail { message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, error: runtime_types::xcm::v3::traits::Error, weight: ::sp_weights::Weight, }, #[codec(index = 2)] - #[doc = "Bad XCM version used."] BadVersion { message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, }, #[codec(index = 3)] - #[doc = "Bad XCM format used."] BadFormat { message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, }, #[codec(index = 4)] - #[doc = "An HRMP message was sent to a sibling parachain."] XcmpMessageSent { message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, }, #[codec(index = 5)] - #[doc = "An XCM exceeded the individual message weight budget."] OverweightEnqueued { sender: runtime_types::polkadot_parachain::primitives::Id, sent_at: ::core::primitive::u32, @@ -6533,13 +387,10 @@ pub mod api { required: ::sp_weights::Weight, }, #[codec(index = 6)] - #[doc = "An XCM from the overweight queue was executed with the given actual weight used."] OverweightServiced { index: ::core::primitive::u64, used: ::sp_weights::Weight }, } } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct InboundChannelDetails { pub sender: runtime_types::polkadot_parachain::primitives::Id, pub state: runtime_types::cumulus_pallet_xcmp_queue::InboundState, @@ -6548,18 +399,14 @@ pub mod api { runtime_types::polkadot_parachain::primitives::XcmpMessageFormat, )>, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum InboundState { #[codec(index = 0)] Ok, #[codec(index = 1)] Suspended, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct OutboundChannelDetails { pub recipient: runtime_types::polkadot_parachain::primitives::Id, pub state: runtime_types::cumulus_pallet_xcmp_queue::OutboundState, @@ -6567,18 +414,14 @@ pub mod api { pub first_index: ::core::primitive::u16, pub last_index: ::core::primitive::u16, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum OutboundState { #[codec(index = 0)] Ok, #[codec(index = 1)] Suspended, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct QueueConfigData { pub suspend_threshold: ::core::primitive::u32, pub drop_threshold: ::core::primitive::u32, @@ -6590,13 +433,9 @@ pub mod api { } pub mod cumulus_primitives_parachain_inherent { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct MessageQueueChain(pub ::subxt::utils::H256); - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct ParachainInherentData { pub validation_data: runtime_types::polkadot_primitives::v4::PersistedValidationData< @@ -6621,9 +460,7 @@ pub mod api { } pub mod finality_grandpa { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct Commit<_0, _1, _2, _3> { pub target_hash: _0, pub target_number: _1, @@ -6631,16 +468,12 @@ pub mod api { runtime_types::finality_grandpa::SignedPrecommit<_0, _1, _2, _3>, >, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct Precommit<_0, _1> { pub target_hash: _0, pub target_number: _1, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct SignedPrecommit<_0, _1, _2, _3> { pub precommit: runtime_types::finality_grandpa::Precommit<_0, _1>, pub signature: _2, @@ -6651,12 +484,7 @@ pub mod api { use super::runtime_types; pub mod dispatch { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum DispatchClass { #[codec(index = 0)] Normal, @@ -6665,35 +493,20 @@ pub mod api { #[codec(index = 2)] Mandatory, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct DispatchInfo { pub weight: ::sp_weights::Weight, pub class: runtime_types::frame_support::dispatch::DispatchClass, pub pays_fee: runtime_types::frame_support::dispatch::Pays, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Pays { #[codec(index = 0)] Yes, #[codec(index = 1)] No, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct PerDispatchClass<_0> { pub normal: _0, pub operational: _0, @@ -6706,12 +519,7 @@ pub mod api { use super::runtime_types; pub mod misc { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum BalanceStatus { #[codec(index = 0)] Free, @@ -6728,94 +536,49 @@ pub mod api { use super::runtime_types; pub mod check_genesis { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct CheckGenesis; } pub mod check_mortality { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct CheckMortality(pub ::sp_runtime::generic::Era); } pub mod check_non_zero_sender { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct CheckNonZeroSender; } pub mod check_nonce { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct CheckNonce(#[codec(compact)] pub ::core::primitive::u32); } pub mod check_spec_version { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct CheckSpecVersion; } pub mod check_tx_version { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct CheckTxVersion; } pub mod check_weight { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct CheckWeight; } } pub mod limits { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct BlockLength { pub max: runtime_types::frame_support::dispatch::PerDispatchClass< ::core::primitive::u32, >, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct BlockWeights { pub base_block: ::sp_weights::Weight, pub max_block: ::sp_weights::Weight, @@ -6823,12 +586,7 @@ pub mod api { runtime_types::frame_system::limits::WeightsPerClass, >, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct WeightsPerClass { pub base_extrinsic: ::sp_weights::Weight, pub max_extrinsic: ::core::option::Option<::sp_weights::Weight>, @@ -6838,37 +596,17 @@ pub mod api { } pub mod pallet { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Call { #[codec(index = 0)] - #[doc = "Make some on-chain remark."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- `O(1)`"] remark { remark: ::std::vec::Vec<::core::primitive::u8> }, #[codec(index = 1)] - #[doc = "Set the number of pages in the WebAssembly environment's heap."] set_heap_pages { pages: ::core::primitive::u64 }, #[codec(index = 2)] - #[doc = "Set the new runtime code."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code`"] set_code { code: ::std::vec::Vec<::core::primitive::u8> }, #[codec(index = 3)] - #[doc = "Set the new runtime code without doing any checks of the given `code`."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- `O(C)` where `C` length of `code`"] set_code_without_checks { code: ::std::vec::Vec<::core::primitive::u8> }, #[codec(index = 4)] - #[doc = "Set some items of storage."] set_storage { items: ::std::vec::Vec<( ::std::vec::Vec<::core::primitive::u8>, @@ -6876,88 +614,52 @@ pub mod api { )>, }, #[codec(index = 5)] - #[doc = "Kill some items from storage."] kill_storage { keys: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>> }, #[codec(index = 6)] - #[doc = "Kill all storage items with a key that starts with the given prefix."] - #[doc = ""] - #[doc = "**NOTE:** We rely on the Root origin to provide us the number of subkeys under"] - #[doc = "the prefix we are removing to accurately calculate the weight of this function."] kill_prefix { prefix: ::std::vec::Vec<::core::primitive::u8>, subkeys: ::core::primitive::u32, }, #[codec(index = 7)] - #[doc = "Make some on-chain remark and emit event."] remark_with_event { remark: ::std::vec::Vec<::core::primitive::u8> }, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Error for the System pallet"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Error { #[codec(index = 0)] - #[doc = "The name of specification does not match between the current runtime"] - #[doc = "and the new runtime."] InvalidSpecName, #[codec(index = 1)] - #[doc = "The specification version is not allowed to decrease between the current runtime"] - #[doc = "and the new runtime."] SpecVersionNeedsToIncrease, #[codec(index = 2)] - #[doc = "Failed to extract the runtime version from the new runtime."] - #[doc = ""] - #[doc = "Either calling `Core_version` or decoding `RuntimeVersion` failed."] FailedToExtractRuntimeVersion, #[codec(index = 3)] - #[doc = "Suicide called when the account has non-default composite data."] NonDefaultComposite, #[codec(index = 4)] - #[doc = "There is a non-zero reference count preventing the account from being purged."] NonZeroRefCount, #[codec(index = 5)] - #[doc = "The origin filter prevent the call to be dispatched."] CallFiltered, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Event for the System pallet."] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Event { #[codec(index = 0)] - #[doc = "An extrinsic completed successfully."] ExtrinsicSuccess { dispatch_info: runtime_types::frame_support::dispatch::DispatchInfo, }, #[codec(index = 1)] - #[doc = "An extrinsic failed."] ExtrinsicFailed { dispatch_error: runtime_types::sp_runtime::DispatchError, dispatch_info: runtime_types::frame_support::dispatch::DispatchInfo, }, #[codec(index = 2)] - #[doc = "`:code` was updated."] CodeUpdated, #[codec(index = 3)] - #[doc = "A new account was created."] NewAccount { account: ::sp_core::crypto::AccountId32 }, #[codec(index = 4)] - #[doc = "An account was reaped."] KilledAccount { account: ::sp_core::crypto::AccountId32 }, #[codec(index = 5)] - #[doc = "On on-chain remark happened."] Remarked { sender: ::sp_core::crypto::AccountId32, hash: ::subxt::utils::H256 }, } } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct AccountInfo<_0, _1> { pub nonce: _0, pub consumers: _0, @@ -6965,25 +667,19 @@ pub mod api { pub sufficients: _0, pub data: _1, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct EventRecord<_0, _1> { pub phase: runtime_types::frame_system::Phase, pub event: _0, pub topics: ::std::vec::Vec<_1>, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct LastRuntimeUpgradeInfo { #[codec(compact)] pub spec_version: ::core::primitive::u32, pub spec_name: ::std::string::String, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Phase { #[codec(index = 0)] ApplyExtrinsic(::core::primitive::u32), @@ -6997,34 +693,15 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Call { #[codec(index = 0)] - #[doc = "Transfer some liquid free balance to another account."] - #[doc = ""] - #[doc = "`transfer_allow_death` will set the `FreeBalance` of the sender and receiver."] - #[doc = "If the sender's account is below the existential deposit as a result"] - #[doc = "of the transfer, the account will be reaped."] - #[doc = ""] - #[doc = "The dispatch origin for this call must be `Signed` by the transactor."] transfer_allow_death { dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, #[codec(compact)] value: ::core::primitive::u128, }, #[codec(index = 1)] - #[doc = "Set the regular balance of a given account; it also takes a reserved balance but this"] - #[doc = "must be the same as the account's current reserved balance."] - #[doc = ""] - #[doc = "The dispatch origin for this call is `root`."] - #[doc = ""] - #[doc = "WARNING: This call is DEPRECATED! Use `force_set_balance` instead."] set_balance_deprecated { who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, #[codec(compact)] @@ -7033,8 +710,6 @@ pub mod api { old_reserved: ::core::primitive::u128, }, #[codec(index = 2)] - #[doc = "Exactly as `transfer_allow_death`, except the origin must be root and the source account"] - #[doc = "may be specified."] force_transfer { source: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, @@ -7042,162 +717,93 @@ pub mod api { value: ::core::primitive::u128, }, #[codec(index = 3)] - #[doc = "Same as the [`transfer_allow_death`] call, but with a check that the transfer will not"] - #[doc = "kill the origin account."] - #[doc = ""] - #[doc = "99% of the time you want [`transfer_allow_death`] instead."] - #[doc = ""] - #[doc = "[`transfer_allow_death`]: struct.Pallet.html#method.transfer"] transfer_keep_alive { dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, #[codec(compact)] value: ::core::primitive::u128, }, #[codec(index = 4)] - #[doc = "Transfer the entire transferable balance from the caller account."] - #[doc = ""] - #[doc = "NOTE: This function only attempts to transfer _transferable_ balances. This means that"] - #[doc = "any locked, reserved, or existential deposits (when `keep_alive` is `true`), will not be"] - #[doc = "transferred by this function. To ensure that this function results in a killed account,"] - #[doc = "you might need to prepare the account by removing any reference counters, storage"] - #[doc = "deposits, etc..."] - #[doc = ""] - #[doc = "The dispatch origin of this call must be Signed."] - #[doc = ""] - #[doc = "- `dest`: The recipient of the transfer."] - #[doc = "- `keep_alive`: A boolean to determine if the `transfer_all` operation should send all"] - #[doc = " of the funds the account has, causing the sender account to be killed (false), or"] - #[doc = " transfer everything except at least the existential deposit, which will guarantee to"] - #[doc = " keep the sender account alive (true)."] transfer_all { dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, keep_alive: ::core::primitive::bool, }, #[codec(index = 5)] - #[doc = "Unreserve some balance from a user by force."] - #[doc = ""] - #[doc = "Can only be called by ROOT."] force_unreserve { who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, amount: ::core::primitive::u128, }, #[codec(index = 6)] - #[doc = "Upgrade a specified account."] - #[doc = ""] - #[doc = "- `origin`: Must be `Signed`."] - #[doc = "- `who`: The account to be upgraded."] - #[doc = ""] - #[doc = "This will waive the transaction fee if at least all but 10% of the accounts needed to"] - #[doc = "be upgraded. (We let some not have to be upgraded just in order to allow for the"] - #[doc = "possibililty of churn)."] upgrade_accounts { who: ::std::vec::Vec<::sp_core::crypto::AccountId32> }, #[codec(index = 7)] - #[doc = "Alias for `transfer_allow_death`, provided only for name-wise compatibility."] - #[doc = ""] - #[doc = "WARNING: DEPRECATED! Will be released in approximately 3 months."] transfer { dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, #[codec(compact)] value: ::core::primitive::u128, }, #[codec(index = 8)] - #[doc = "Set the regular balance of a given account."] - #[doc = ""] - #[doc = "The dispatch origin for this call is `root`."] force_set_balance { who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, #[codec(compact)] new_free: ::core::primitive::u128, }, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Error { #[codec(index = 0)] - #[doc = "Vesting balance too high to send value."] VestingBalance, #[codec(index = 1)] - #[doc = "Account liquidity restrictions prevent withdrawal."] LiquidityRestrictions, #[codec(index = 2)] - #[doc = "Balance too low to send value."] InsufficientBalance, #[codec(index = 3)] - #[doc = "Value too low to create account due to existential deposit."] ExistentialDeposit, #[codec(index = 4)] - #[doc = "Transfer/payment would kill account."] Expendability, #[codec(index = 5)] - #[doc = "A vesting schedule already exists for this account."] ExistingVestingSchedule, #[codec(index = 6)] - #[doc = "Beneficiary account must pre-exist."] DeadAccount, #[codec(index = 7)] - #[doc = "Number of named reserves exceed `MaxReserves`."] TooManyReserves, #[codec(index = 8)] - #[doc = "Number of holds exceed `MaxHolds`."] TooManyHolds, #[codec(index = 9)] - #[doc = "Number of freezes exceed `MaxFreezes`."] TooManyFreezes, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Event { #[codec(index = 0)] - #[doc = "An account was created with some free balance."] Endowed { account: ::sp_core::crypto::AccountId32, free_balance: ::core::primitive::u128, }, #[codec(index = 1)] - #[doc = "An account was removed whose balance was non-zero but below ExistentialDeposit,"] - #[doc = "resulting in an outright loss."] DustLost { account: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128, }, #[codec(index = 2)] - #[doc = "Transfer succeeded."] Transfer { from: ::sp_core::crypto::AccountId32, to: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128, }, #[codec(index = 3)] - #[doc = "A balance was set by root."] BalanceSet { who: ::sp_core::crypto::AccountId32, free: ::core::primitive::u128, }, #[codec(index = 4)] - #[doc = "Some balance was reserved (moved from free to reserved)."] Reserved { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128, }, #[codec(index = 5)] - #[doc = "Some balance was unreserved (moved from reserved to free)."] Unreserved { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128, }, #[codec(index = 6)] - #[doc = "Some balance was moved from the reserve of the first account to the second account."] - #[doc = "Final argument indicates the destination balance type."] ReserveRepatriated { from: ::sp_core::crypto::AccountId32, to: ::sp_core::crypto::AccountId32, @@ -7206,95 +812,72 @@ pub mod api { runtime_types::frame_support::traits::tokens::misc::BalanceStatus, }, #[codec(index = 7)] - #[doc = "Some amount was deposited (e.g. for transaction fees)."] Deposit { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, #[codec(index = 8)] - #[doc = "Some amount was withdrawn from the account (e.g. for transaction fees)."] Withdraw { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128, }, #[codec(index = 9)] - #[doc = "Some amount was removed from the account (e.g. for misbehavior)."] Slashed { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, #[codec(index = 10)] - #[doc = "Some amount was minted into an account."] Minted { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, #[codec(index = 11)] - #[doc = "Some amount was burned from an account."] Burned { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, #[codec(index = 12)] - #[doc = "Some amount was suspended from an account (it can be restored later)."] Suspended { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128, }, #[codec(index = 13)] - #[doc = "Some amount was restored into an account."] Restored { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128, }, #[codec(index = 14)] - #[doc = "An account was upgraded."] Upgraded { who: ::sp_core::crypto::AccountId32 }, #[codec(index = 15)] - #[doc = "Total issuance was increased by `amount`, creating a credit to be balanced."] Issued { amount: ::core::primitive::u128 }, #[codec(index = 16)] - #[doc = "Total issuance was decreased by `amount`, creating a debt to be balanced."] Rescinded { amount: ::core::primitive::u128 }, + #[codec(index = 17)] + Locked { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, + #[codec(index = 18)] + Unlocked { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, } - } - pub mod types { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + } + pub mod types { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct AccountData<_0> { pub free: _0, pub reserved: _0, pub frozen: _0, pub flags: runtime_types::pallet_balances::types::ExtraFlags, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct BalanceLock<_0> { pub id: [::core::primitive::u8; 8usize], pub amount: _0, pub reasons: runtime_types::pallet_balances::types::Reasons, } #[derive( + :: codec :: Decode, + :: codec :: Encode, :: subxt :: ext :: codec :: CompactAs, - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, Clone, Debug, )] pub struct ExtraFlags(pub ::core::primitive::u128); - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct IdAmount<_0, _1> { pub id: _0, pub amount: _1, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Reasons { #[codec(index = 0)] Fee, @@ -7303,12 +886,7 @@ pub mod api { #[codec(index = 2)] All, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct ReserveData<_0, _1> { pub id: _0, pub amount: _1, @@ -7319,22 +897,9 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Call { #[codec(index = 0)] - #[doc = "Verify a target header is finalized according to the given finality proof."] - #[doc = ""] - #[doc = "It will use the underlying storage pallet to fetch information about the current"] - #[doc = "authorities and best finalized header in order to verify that the header is finalized."] - #[doc = ""] - #[doc = "If successful in verification, it will write the target header to the underlying storage"] - #[doc = "pallet."] submit_finality_proof { finality_target: ::std::boxed::Box< ::sp_runtime::generic::Header< @@ -7350,15 +915,6 @@ pub mod api { >, }, #[codec(index = 1)] - #[doc = "Bootstrap the bridge pallet with an initial header and authority set from which to sync."] - #[doc = ""] - #[doc = "The initial configuration provided does not need to be the genesis header of the bridged"] - #[doc = "chain, it can be any arbitrary header. You can also provide the next scheduled set"] - #[doc = "change if it is already know."] - #[doc = ""] - #[doc = "This function is only allowed to be called from a trusted origin and writes to storage"] - #[doc = "with practically no checks in terms of the validity of the data. It is important that"] - #[doc = "you ensure that valid data is being passed in."] initialize { init_data: ::bp_header_chain::InitializationData< ::sp_runtime::generic::Header< @@ -7368,66 +924,34 @@ pub mod api { >, }, #[codec(index = 2)] - #[doc = "Change `PalletOwner`."] - #[doc = ""] - #[doc = "May only be called either by root, or by `PalletOwner`."] set_owner { new_owner: ::core::option::Option<::sp_core::crypto::AccountId32> }, #[codec(index = 3)] - #[doc = "Halt or resume all pallet operations."] - #[doc = ""] - #[doc = "May only be called either by root, or by `PalletOwner`."] set_operating_mode { operating_mode: runtime_types::bp_runtime::BasicOperatingMode, }, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Error { #[codec(index = 0)] - #[doc = "The given justification is invalid for the given header."] InvalidJustification, #[codec(index = 1)] - #[doc = "The authority set from the underlying header chain is invalid."] InvalidAuthoritySet, #[codec(index = 2)] - #[doc = "There are too many requests for the current window to handle."] - TooManyRequests, - #[codec(index = 3)] - #[doc = "The header being imported is older than the best finalized header known to the pallet."] OldHeader, - #[codec(index = 4)] - #[doc = "The scheduled authority set change found in the header is unsupported by the pallet."] - #[doc = ""] - #[doc = "This is the case for non-standard (e.g forced) authority set changes."] + #[codec(index = 3)] UnsupportedScheduledChange, - #[codec(index = 5)] - #[doc = "The pallet is not yet initialized."] + #[codec(index = 4)] NotInitialized, - #[codec(index = 6)] - #[doc = "The pallet has already been initialized."] + #[codec(index = 5)] AlreadyInitialized, - #[codec(index = 7)] - #[doc = "Too many authorities in the set."] + #[codec(index = 6)] TooManyAuthoritiesInSet, - #[codec(index = 8)] - #[doc = "Error generated by the `OwnedBridgeModule` trait."] + #[codec(index = 7)] BridgeModule(runtime_types::bp_runtime::OwnedBridgeModuleError), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Event { #[codec(index = 0)] - #[doc = "Best finalized chain header has been updated to the header with given number and hash."] UpdatedBestFinalizedHeader { number: ::core::primitive::u64, hash: ::bp_millau::MillauHash, @@ -7436,12 +960,7 @@ pub mod api { } pub mod storage_types { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct StoredAuthoritySet { pub authorities: runtime_types::bounded_collections::bounded_vec::BoundedVec<( runtime_types::sp_consensus_grandpa::app::Public, @@ -7455,126 +974,68 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Call { - # [codec (index = 0)] # [doc = "Change `PalletOwner`."] # [doc = ""] # [doc = "May only be called either by root, or by `PalletOwner`."] set_owner { new_owner : :: core :: option :: Option < :: sp_core :: crypto :: AccountId32 > , } , # [codec (index = 1)] # [doc = "Halt or resume all/some pallet operations."] # [doc = ""] # [doc = "May only be called either by root, or by `PalletOwner`."] set_operating_mode { operating_mode : runtime_types :: bp_messages :: MessagesOperatingMode , } , # [codec (index = 2)] # [doc = "Receive messages proof from bridged chain."] # [doc = ""] # [doc = "The weight of the call assumes that the transaction always brings outbound lane"] # [doc = "state update. Because of that, the submitter (relayer) has no benefit of not including"] # [doc = "this data in the transaction, so reward confirmations lags should be minimal."] receive_messages_proof { relayer_id_at_bridged_chain : :: sp_core :: crypto :: AccountId32 , proof : :: bridge_runtime_common :: messages :: target :: FromBridgedChainMessagesProof < :: bp_millau :: MillauHash > , messages_count : :: core :: primitive :: u32 , dispatch_weight : :: sp_weights :: Weight , } , # [codec (index = 3)] # [doc = "Receive messages delivery proof from bridged chain."] receive_messages_delivery_proof { proof : :: bridge_runtime_common :: messages :: source :: FromBridgedChainMessagesDeliveryProof < :: bp_millau :: MillauHash > , relayers_state : :: bp_messages :: UnrewardedRelayersState , } , } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + # [codec (index = 0)] set_owner { new_owner : :: core :: option :: Option < :: sp_core :: crypto :: AccountId32 > , } , # [codec (index = 1)] set_operating_mode { operating_mode : runtime_types :: bp_messages :: MessagesOperatingMode , } , # [codec (index = 2)] receive_messages_proof { relayer_id_at_bridged_chain : :: sp_core :: crypto :: AccountId32 , proof : :: bridge_runtime_common :: messages :: target :: FromBridgedChainMessagesProof < :: bp_millau :: MillauHash > , messages_count : :: core :: primitive :: u32 , dispatch_weight : :: sp_weights :: Weight , } , # [codec (index = 3)] receive_messages_delivery_proof { proof : :: bridge_runtime_common :: messages :: source :: FromBridgedChainMessagesDeliveryProof < :: bp_millau :: MillauHash > , relayers_state : :: bp_messages :: UnrewardedRelayersState , } , } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Error { #[codec(index = 0)] - #[doc = "Pallet is not in Normal operating mode."] NotOperatingNormally, #[codec(index = 1)] - #[doc = "The outbound lane is inactive."] InactiveOutboundLane, #[codec(index = 2)] - #[doc = "The message is too large to be sent over the bridge."] MessageIsTooLarge, #[codec(index = 3)] - #[doc = "Message has been treated as invalid by chain verifier."] MessageRejectedByChainVerifier, #[codec(index = 4)] - #[doc = "Message has been treated as invalid by lane verifier."] MessageRejectedByLaneVerifier, #[codec(index = 5)] - #[doc = "Submitter has failed to pay fee for delivering and dispatching messages."] FailedToWithdrawMessageFee, #[codec(index = 6)] - #[doc = "The transaction brings too many messages."] TooManyMessagesInTheProof, #[codec(index = 7)] - #[doc = "Invalid messages has been submitted."] InvalidMessagesProof, #[codec(index = 8)] - #[doc = "Invalid messages delivery proof has been submitted."] InvalidMessagesDeliveryProof, #[codec(index = 9)] - #[doc = "The bridged chain has invalid `UnrewardedRelayers` in its storage (fatal for the lane)."] InvalidUnrewardedRelayers, #[codec(index = 10)] - #[doc = "The relayer has declared invalid unrewarded relayers state in the"] - #[doc = "`receive_messages_delivery_proof` call."] InvalidUnrewardedRelayersState, #[codec(index = 11)] - #[doc = "The cumulative dispatch weight, passed by relayer is not enough to cover dispatch"] - #[doc = "of all bundled messages."] InsufficientDispatchWeight, #[codec(index = 12)] - #[doc = "The message someone is trying to work with (i.e. increase fee) is not yet sent."] MessageIsNotYetSent, #[codec(index = 13)] - #[doc = "The number of actually confirmed messages is going to be larger than the number of"] - #[doc = "messages in the proof. This may mean that this or bridged chain storage is corrupted."] TryingToConfirmMoreMessagesThanExpected, #[codec(index = 14)] - #[doc = "Error generated by the `OwnedBridgeModule` trait."] BridgeModule(runtime_types::bp_runtime::OwnedBridgeModuleError), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Event { - # [codec (index = 0)] # [doc = "Message has been accepted and is waiting to be delivered."] MessageAccepted { lane_id : runtime_types :: bp_messages :: LaneId , nonce : :: core :: primitive :: u64 , } , # [codec (index = 1)] # [doc = "Messages have been received from the bridged chain."] MessagesReceived (:: std :: vec :: Vec < runtime_types :: bp_messages :: ReceivedMessages < runtime_types :: bridge_runtime_common :: messages_xcm_extension :: XcmBlobMessageDispatchResult > > ,) , # [codec (index = 2)] # [doc = "Messages in the inclusive range have been delivered to the bridged chain."] MessagesDelivered { lane_id : runtime_types :: bp_messages :: LaneId , messages : runtime_types :: bp_messages :: DeliveredMessages , } , } + # [codec (index = 0)] MessageAccepted { lane_id : runtime_types :: bp_messages :: LaneId , nonce : :: core :: primitive :: u64 , } , # [codec (index = 1)] MessagesReceived (:: std :: vec :: Vec < runtime_types :: bp_messages :: ReceivedMessages < runtime_types :: bridge_runtime_common :: messages_xcm_extension :: XcmBlobMessageDispatchResult > > ,) , # [codec (index = 2)] MessagesDelivered { lane_id : runtime_types :: bp_messages :: LaneId , messages : runtime_types :: bp_messages :: DeliveredMessages , } , } } } pub mod pallet_bridge_relayers { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Call { #[codec(index = 0)] - #[doc = "Claim accumulated rewards."] claim_rewards { rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, }, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Error { #[codec(index = 0)] - #[doc = "No reward can be claimed by given relayer."] NoRewardForRelayer, #[codec(index = 1)] - #[doc = "Reward payment procedure has failed."] FailedToPayReward, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Event { #[codec(index = 0)] - #[doc = "Reward has been paid to the relayer."] RewardPaid { relayer: ::sp_core::crypto::AccountId32, rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, @@ -7587,97 +1048,47 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Call { #[codec(index = 0)] - #[doc = "Authenticates the sudo key and dispatches a function call with `Root` origin."] - #[doc = ""] - #[doc = "The dispatch origin for this call must be _Signed_."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- O(1)."] sudo { call: ::std::boxed::Box, }, #[codec(index = 1)] - #[doc = "Authenticates the sudo key and dispatches a function call with `Root` origin."] - #[doc = "This function does not check the weight of the call, and instead allows the"] - #[doc = "Sudo user to specify the weight of the call."] - #[doc = ""] - #[doc = "The dispatch origin for this call must be _Signed_."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- O(1)."] sudo_unchecked_weight { call: ::std::boxed::Box, weight: ::sp_weights::Weight, }, #[codec(index = 2)] - #[doc = "Authenticates the current sudo key and sets the given AccountId (`new`) as the new sudo"] - #[doc = "key."] - #[doc = ""] - #[doc = "The dispatch origin for this call must be _Signed_."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- O(1)."] set_key { new: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, }, #[codec(index = 3)] - #[doc = "Authenticates the sudo key and dispatches a function call with `Signed` origin from"] - #[doc = "a given account."] - #[doc = ""] - #[doc = "The dispatch origin for this call must be _Signed_."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- O(1)."] sudo_as { who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, call: ::std::boxed::Box, }, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Error for the Sudo pallet"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Error { #[codec(index = 0)] - #[doc = "Sender must be the Sudo account"] RequireSudo, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Event { #[codec(index = 0)] - #[doc = "A sudo just took place. \\[result\\]"] Sudid { sudo_result: ::core::result::Result<(), runtime_types::sp_runtime::DispatchError>, }, #[codec(index = 1)] - #[doc = "The \\[sudoer\\] just switched identity; the old key is supplied if one existed."] KeyChanged { old_sudoer: ::core::option::Option<::sp_core::crypto::AccountId32>, }, #[codec(index = 2)] - #[doc = "A sudo just took place. \\[result\\]"] SudoAsDone { sudo_result: ::core::result::Result<(), runtime_types::sp_runtime::DispatchError>, @@ -7689,30 +1100,9 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Call { #[codec(index = 0)] - #[doc = "Set the current time."] - #[doc = ""] - #[doc = "This call should be invoked exactly once per block. It will panic at the finalization"] - #[doc = "phase, if this call hasn't been invoked by that time."] - #[doc = ""] - #[doc = "The timestamp should be greater than the previous one by the amount specified by"] - #[doc = "`MinimumPeriod`."] - #[doc = ""] - #[doc = "The dispatch origin for this call must be `Inherent`."] - #[doc = ""] - #[doc = "## Complexity"] - #[doc = "- `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`)"] - #[doc = "- 1 storage read and 1 storage mutation (codec `O(1)`). (because of `DidUpdate::take` in"] - #[doc = " `on_finalize`)"] - #[doc = "- 1 event handler `on_timestamp_set`. Must be `O(1)`."] set { #[codec(compact)] now: ::core::primitive::u64, @@ -7724,17 +1114,9 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Event { #[codec(index = 0)] - #[doc = "A transaction fee `actual_fee`, of which `tip` was added to the minimum inclusion fee,"] - #[doc = "has been paid by `who`."] TransactionFeePaid { who: ::sp_core::crypto::AccountId32, actual_fee: ::core::primitive::u128, @@ -7742,13 +1124,9 @@ pub mod api { }, } } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct ChargeTransactionPayment(#[codec(compact)] pub ::core::primitive::u128); - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Releases { #[codec(index = 0)] V1Ancient, @@ -7760,13 +1138,7 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "Contains one variant per dispatchable that can be called by an extrinsic."] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Call { #[codec(index = 0)] send { @@ -7774,21 +1146,6 @@ pub mod api { message: ::std::boxed::Box, }, #[codec(index = 1)] - #[doc = "Teleport some assets from the local chain to some destination chain."] - #[doc = ""] - #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] - #[doc = "index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,"] - #[doc = "with all fees taken as needed from the asset."] - #[doc = ""] - #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] - #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] - #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] - #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] - #[doc = " an `AccountId32` value."] - #[doc = "- `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the"] - #[doc = " `dest` side. May not be empty."] - #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] - #[doc = " fees."] teleport_assets { dest: ::std::boxed::Box, beneficiary: ::std::boxed::Box, @@ -7796,22 +1153,6 @@ pub mod api { fee_asset_item: ::core::primitive::u32, }, #[codec(index = 2)] - #[doc = "Transfer some assets from the local chain to the sovereign account of a destination"] - #[doc = "chain and forward a notification XCM."] - #[doc = ""] - #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] - #[doc = "index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,"] - #[doc = "with all fees taken as needed from the asset."] - #[doc = ""] - #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] - #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] - #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] - #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] - #[doc = " an `AccountId32` value."] - #[doc = "- `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the"] - #[doc = " `dest` side."] - #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] - #[doc = " fees."] reserve_transfer_assets { dest: ::std::boxed::Box, beneficiary: ::std::boxed::Box, @@ -7819,79 +1160,29 @@ pub mod api { fee_asset_item: ::core::primitive::u32, }, #[codec(index = 3)] - #[doc = "Execute an XCM message from a local, signed, origin."] - #[doc = ""] - #[doc = "An event is deposited indicating whether `msg` could be executed completely or only"] - #[doc = "partially."] - #[doc = ""] - #[doc = "No more than `max_weight` will be used in its attempted execution. If this is less than the"] - #[doc = "maximum amount of weight that the message could take to be executed, then no execution"] - #[doc = "attempt will be made."] - #[doc = ""] - #[doc = "NOTE: A successful return to this does *not* imply that the `msg` was executed successfully"] - #[doc = "to completion; only that *some* of it was executed."] execute { message: ::std::boxed::Box, max_weight: ::sp_weights::Weight, }, #[codec(index = 4)] - #[doc = "Extoll that a particular destination can be communicated with through a particular"] - #[doc = "version of XCM."] - #[doc = ""] - #[doc = "- `origin`: Must be Root."] - #[doc = "- `location`: The destination that is being described."] - #[doc = "- `xcm_version`: The latest version of XCM that `location` supports."] force_xcm_version { location: ::std::boxed::Box, xcm_version: ::core::primitive::u32, }, #[codec(index = 5)] - #[doc = "Set a safe XCM version (the version that XCM should be encoded with if the most recent"] - #[doc = "version a destination can accept is unknown)."] - #[doc = ""] - #[doc = "- `origin`: Must be Root."] - #[doc = "- `maybe_xcm_version`: The default XCM encoding version, or `None` to disable."] force_default_xcm_version { maybe_xcm_version: ::core::option::Option<::core::primitive::u32>, }, #[codec(index = 6)] - #[doc = "Ask a location to notify us regarding their XCM version and any changes to it."] - #[doc = ""] - #[doc = "- `origin`: Must be Root."] - #[doc = "- `location`: The location to which we should subscribe for XCM version notifications."] force_subscribe_version_notify { location: ::std::boxed::Box, }, #[codec(index = 7)] - #[doc = "Require that a particular destination should no longer notify us regarding any XCM"] - #[doc = "version changes."] - #[doc = ""] - #[doc = "- `origin`: Must be Root."] - #[doc = "- `location`: The location to which we are currently subscribed for XCM version"] - #[doc = " notifications which we no longer desire."] force_unsubscribe_version_notify { location: ::std::boxed::Box, }, #[codec(index = 8)] - #[doc = "Transfer some assets from the local chain to the sovereign account of a destination"] - #[doc = "chain and forward a notification XCM."] - #[doc = ""] - #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] - #[doc = "index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight"] - #[doc = "is needed than `weight_limit`, then the operation will fail and the assets send may be"] - #[doc = "at risk."] - #[doc = ""] - #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] - #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] - #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] - #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] - #[doc = " an `AccountId32` value."] - #[doc = "- `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the"] - #[doc = " `dest` side."] - #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] - #[doc = " fees."] - #[doc = "- `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase."] limited_reserve_transfer_assets { dest: ::std::boxed::Box, beneficiary: ::std::boxed::Box, @@ -7900,23 +1191,6 @@ pub mod api { weight_limit: runtime_types::xcm::v3::WeightLimit, }, #[codec(index = 9)] - #[doc = "Teleport some assets from the local chain to some destination chain."] - #[doc = ""] - #[doc = "Fee payment on the destination side is made from the asset in the `assets` vector of"] - #[doc = "index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight"] - #[doc = "is needed than `weight_limit`, then the operation will fail and the assets send may be"] - #[doc = "at risk."] - #[doc = ""] - #[doc = "- `origin`: Must be capable of withdrawing the `assets` and executing XCM."] - #[doc = "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send"] - #[doc = " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain."] - #[doc = "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be"] - #[doc = " an `AccountId32` value."] - #[doc = "- `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the"] - #[doc = " `dest` side. May not be empty."] - #[doc = "- `fee_asset_item`: The index into `assets` of the item which should be used to pay"] - #[doc = " fees."] - #[doc = "- `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase."] limited_teleport_assets { dest: ::std::boxed::Box, beneficiary: ::std::boxed::Box, @@ -7925,128 +1199,69 @@ pub mod api { weight_limit: runtime_types::xcm::v3::WeightLimit, }, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/main-docs/build/events-errors/)\n\t\t\tof this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Error { #[codec(index = 0)] - #[doc = "The desired destination was unreachable, generally because there is a no way of routing"] - #[doc = "to it."] Unreachable, #[codec(index = 1)] - #[doc = "There was some other issue (i.e. not to do with routing) in sending the message. Perhaps"] - #[doc = "a lack of space for buffering the message."] SendFailure, #[codec(index = 2)] - #[doc = "The message execution fails the filter."] Filtered, #[codec(index = 3)] - #[doc = "The message's weight could not be determined."] UnweighableMessage, #[codec(index = 4)] - #[doc = "The destination `MultiLocation` provided cannot be inverted."] DestinationNotInvertible, #[codec(index = 5)] - #[doc = "The assets to be sent are empty."] Empty, #[codec(index = 6)] - #[doc = "Could not re-anchor the assets to declare the fees for the destination chain."] CannotReanchor, #[codec(index = 7)] - #[doc = "Too many assets have been attempted for transfer."] TooManyAssets, #[codec(index = 8)] - #[doc = "Origin is invalid for sending."] InvalidOrigin, #[codec(index = 9)] - #[doc = "The version of the `Versioned` value used is not able to be interpreted."] BadVersion, #[codec(index = 10)] - #[doc = "The given location could not be used (e.g. because it cannot be expressed in the"] - #[doc = "desired version of XCM)."] BadLocation, #[codec(index = 11)] - #[doc = "The referenced subscription could not be found."] NoSubscription, #[codec(index = 12)] - #[doc = "The location is invalid since it already has a subscription from us."] AlreadySubscribed, #[codec(index = 13)] - #[doc = "Invalid asset for the operation."] InvalidAsset, #[codec(index = 14)] - #[doc = "The owner does not own (all) of the asset that they wish to do the operation on."] LowBalance, #[codec(index = 15)] - #[doc = "The asset owner has too many locks on the asset."] TooManyLocks, #[codec(index = 16)] - #[doc = "The given account is not an identifiable sovereign account for any location."] AccountNotSovereign, #[codec(index = 17)] - #[doc = "The operation required fees to be paid which the initiator could not meet."] FeesNotMet, #[codec(index = 18)] - #[doc = "A remote lock with the corresponding data could not be found."] LockNotFound, #[codec(index = 19)] - #[doc = "The unlock operation cannot succeed because there are still users of the lock."] InUse, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] - #[doc = "\n\t\t\tThe [event](https://docs.substrate.io/main-docs/build/events-errors/) emitted\n\t\t\tby this pallet.\n\t\t\t"] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Event { #[codec(index = 0)] - #[doc = "Execution of an XCM message was attempted."] - #[doc = ""] - #[doc = "\\[ outcome \\]"] Attempted(runtime_types::xcm::v3::traits::Outcome), #[codec(index = 1)] - #[doc = "A XCM message was sent."] - #[doc = ""] - #[doc = "\\[ origin, destination, message \\]"] Sent( runtime_types::xcm::v3::multilocation::MultiLocation, runtime_types::xcm::v3::multilocation::MultiLocation, runtime_types::xcm::v3::Xcm, ), #[codec(index = 2)] - #[doc = "Query response received which does not match a registered query. This may be because a"] - #[doc = "matching query was never registered, it may be because it is a duplicate response, or"] - #[doc = "because the query timed out."] - #[doc = ""] - #[doc = "\\[ origin location, id \\]"] UnexpectedResponse( runtime_types::xcm::v3::multilocation::MultiLocation, ::core::primitive::u64, ), #[codec(index = 3)] - #[doc = "Query response has been received and is ready for taking with `take_response`. There is"] - #[doc = "no registered notification call."] - #[doc = ""] - #[doc = "\\[ id, response \\]"] ResponseReady(::core::primitive::u64, runtime_types::xcm::v3::Response), #[codec(index = 4)] - #[doc = "Query response has been received and query is removed. The registered notification has"] - #[doc = "been dispatched and executed successfully."] - #[doc = ""] - #[doc = "\\[ id, pallet index, call index \\]"] Notified(::core::primitive::u64, ::core::primitive::u8, ::core::primitive::u8), #[codec(index = 5)] - #[doc = "Query response has been received and query is removed. The registered notification could"] - #[doc = "not be dispatched because the dispatch weight is greater than the maximum weight"] - #[doc = "originally budgeted by this runtime for the query result."] - #[doc = ""] - #[doc = "\\[ id, pallet index, call index, actual weight, max budgeted weight \\]"] NotifyOverweight( ::core::primitive::u64, ::core::primitive::u8, @@ -8055,32 +1270,18 @@ pub mod api { ::sp_weights::Weight, ), #[codec(index = 6)] - #[doc = "Query response has been received and query is removed. There was a general error with"] - #[doc = "dispatching the notification call."] - #[doc = ""] - #[doc = "\\[ id, pallet index, call index \\]"] NotifyDispatchError( ::core::primitive::u64, ::core::primitive::u8, ::core::primitive::u8, ), #[codec(index = 7)] - #[doc = "Query response has been received and query is removed. The dispatch was unable to be"] - #[doc = "decoded into a `Call`; this might be due to dispatch function having a signature which"] - #[doc = "is not `(origin, QueryId, Response)`."] - #[doc = ""] - #[doc = "\\[ id, pallet index, call index \\]"] NotifyDecodeFailed( ::core::primitive::u64, ::core::primitive::u8, ::core::primitive::u8, ), #[codec(index = 8)] - #[doc = "Expected query response has been received but the origin location of the response does"] - #[doc = "not match that expected. The query remains registered for a later, valid, response to"] - #[doc = "be received and acted upon."] - #[doc = ""] - #[doc = "\\[ origin location, id, expected location \\]"] InvalidResponder( runtime_types::xcm::v3::multilocation::MultiLocation, ::core::primitive::u64, @@ -8089,92 +1290,46 @@ pub mod api { >, ), #[codec(index = 9)] - #[doc = "Expected query response has been received but the expected origin location placed in"] - #[doc = "storage by this runtime previously cannot be decoded. The query remains registered."] - #[doc = ""] - #[doc = "This is unexpected (since a location placed in storage in a previously executing"] - #[doc = "runtime should be readable prior to query timeout) and dangerous since the possibly"] - #[doc = "valid response will be dropped. Manual governance intervention is probably going to be"] - #[doc = "needed."] - #[doc = ""] - #[doc = "\\[ origin location, id \\]"] InvalidResponderVersion( runtime_types::xcm::v3::multilocation::MultiLocation, ::core::primitive::u64, ), #[codec(index = 10)] - #[doc = "Received query response has been read and removed."] - #[doc = ""] - #[doc = "\\[ id \\]"] ResponseTaken(::core::primitive::u64), #[codec(index = 11)] - #[doc = "Some assets have been placed in an asset trap."] - #[doc = ""] - #[doc = "\\[ hash, origin, assets \\]"] AssetsTrapped( ::subxt::utils::H256, runtime_types::xcm::v3::multilocation::MultiLocation, runtime_types::xcm::VersionedMultiAssets, ), #[codec(index = 12)] - #[doc = "An XCM version change notification message has been attempted to be sent."] - #[doc = ""] - #[doc = "The cost of sending it (borne by the chain) is included."] - #[doc = ""] - #[doc = "\\[ destination, result, cost \\]"] VersionChangeNotified( runtime_types::xcm::v3::multilocation::MultiLocation, ::core::primitive::u32, runtime_types::xcm::v3::multiasset::MultiAssets, ), #[codec(index = 13)] - #[doc = "The supported version of a location has been changed. This might be through an"] - #[doc = "automatic notification or a manual intervention."] - #[doc = ""] - #[doc = "\\[ location, XCM version \\]"] SupportedVersionChanged( runtime_types::xcm::v3::multilocation::MultiLocation, ::core::primitive::u32, ), #[codec(index = 14)] - #[doc = "A given location which had a version change subscription was dropped owing to an error"] - #[doc = "sending the notification to it."] - #[doc = ""] - #[doc = "\\[ location, query ID, error \\]"] NotifyTargetSendFail( runtime_types::xcm::v3::multilocation::MultiLocation, ::core::primitive::u64, runtime_types::xcm::v3::traits::Error, ), #[codec(index = 15)] - #[doc = "A given location which had a version change subscription was dropped owing to an error"] - #[doc = "migrating the location to our new XCM format."] - #[doc = ""] - #[doc = "\\[ location, query ID \\]"] NotifyTargetMigrationFail( runtime_types::xcm::VersionedMultiLocation, ::core::primitive::u64, ), #[codec(index = 16)] - #[doc = "Expected query response has been received but the expected querier location placed in"] - #[doc = "storage by this runtime previously cannot be decoded. The query remains registered."] - #[doc = ""] - #[doc = "This is unexpected (since a location placed in storage in a previously executing"] - #[doc = "runtime should be readable prior to query timeout) and dangerous since the possibly"] - #[doc = "valid response will be dropped. Manual governance intervention is probably going to be"] - #[doc = "needed."] - #[doc = ""] - #[doc = "\\[ origin location, id \\]"] InvalidQuerierVersion( runtime_types::xcm::v3::multilocation::MultiLocation, ::core::primitive::u64, ), #[codec(index = 17)] - #[doc = "Expected query response has been received but the querier location of the response does"] - #[doc = "not match the expected. The query remains registered for a later, valid, response to"] - #[doc = "be received and acted upon."] - #[doc = ""] - #[doc = "\\[ origin location, id, expected querier, maybe actual querier \\]"] InvalidQuerier( runtime_types::xcm::v3::multilocation::MultiLocation, ::core::primitive::u64, @@ -8184,42 +1339,26 @@ pub mod api { >, ), #[codec(index = 18)] - #[doc = "A remote has requested XCM version change notification from us and we have honored it."] - #[doc = "A version information message is sent to them and its cost is included."] - #[doc = ""] - #[doc = "\\[ destination location, cost \\]"] VersionNotifyStarted( runtime_types::xcm::v3::multilocation::MultiLocation, runtime_types::xcm::v3::multiasset::MultiAssets, ), #[codec(index = 19)] - #[doc = "We have requested that a remote chain sends us XCM version change notifications."] - #[doc = ""] - #[doc = "\\[ destination location, cost \\]"] VersionNotifyRequested( runtime_types::xcm::v3::multilocation::MultiLocation, runtime_types::xcm::v3::multiasset::MultiAssets, ), #[codec(index = 20)] - #[doc = "We have requested that a remote chain stops sending us XCM version change notifications."] - #[doc = ""] - #[doc = "\\[ destination location, cost \\]"] VersionNotifyUnrequested( runtime_types::xcm::v3::multilocation::MultiLocation, runtime_types::xcm::v3::multiasset::MultiAssets, ), #[codec(index = 21)] - #[doc = "Fees were paid from a location for an operation (often for using `SendXcm`)."] - #[doc = ""] - #[doc = "\\[ paying location, fees \\]"] FeesPaid( runtime_types::xcm::v3::multilocation::MultiLocation, runtime_types::xcm::v3::multiasset::MultiAssets, ), #[codec(index = 22)] - #[doc = "Some assets have been claimed from an asset trap"] - #[doc = ""] - #[doc = "\\[ hash, origin, assets \\]"] AssetsClaimed( ::subxt::utils::H256, runtime_types::xcm::v3::multilocation::MultiLocation, @@ -8230,23 +1369,17 @@ pub mod api { } pub mod polkadot_core_primitives { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct InboundDownwardMessage<_0> { pub sent_at: _0, pub msg: ::std::vec::Vec<::core::primitive::u8>, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct InboundHrmpMessage<_0> { pub sent_at: _0, pub data: ::std::vec::Vec<::core::primitive::u8>, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct OutboundHrmpMessage<_0> { pub recipient: _0, pub data: ::std::vec::Vec<::core::primitive::u8>, @@ -8256,27 +1389,17 @@ pub mod api { use super::runtime_types; pub mod primitives { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct HeadData(pub ::std::vec::Vec<::core::primitive::u8>); #[derive( + :: codec :: Decode, + :: codec :: Encode, :: subxt :: ext :: codec :: CompactAs, - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, Clone, Debug, )] pub struct Id(pub ::core::primitive::u32); - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum XcmpMessageFormat { #[codec(index = 0)] ConcatenatedVersionedXcm, @@ -8291,12 +1414,7 @@ pub mod api { use super::runtime_types; pub mod v4 { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct AbridgedHostConfiguration { pub max_code_size: ::core::primitive::u32, pub max_head_data_size: ::core::primitive::u32, @@ -8308,12 +1426,7 @@ pub mod api { pub validation_upgrade_cooldown: ::core::primitive::u32, pub validation_upgrade_delay: ::core::primitive::u32, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct AbridgedHrmpChannel { pub max_capacity: ::core::primitive::u32, pub max_total_size: ::core::primitive::u32, @@ -8322,24 +1435,14 @@ pub mod api { pub total_size: ::core::primitive::u32, pub mqc_head: ::core::option::Option<::subxt::utils::H256>, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct PersistedValidationData<_0, _1> { pub parent_head: runtime_types::polkadot_parachain::primitives::HeadData, pub relay_parent_number: _1, pub relay_parent_storage_root: _0, pub max_pov_size: _1, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum UpgradeRestriction { #[codec(index = 0)] Present, @@ -8348,21 +1451,13 @@ pub mod api { } pub mod rialto_parachain_runtime { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct BridgeRejectObsoleteHeadersAndMessages; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct DummyBridgeRefundMillauMessages; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct Runtime; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum RuntimeCall { #[codec(index = 0)] System(runtime_types::frame_system::pallet::Call), @@ -8389,9 +1484,7 @@ pub mod api { #[codec(index = 56)] BridgeMillauMessages(runtime_types::pallet_bridge_messages::pallet::Call), } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum RuntimeEvent { #[codec(index = 0)] System(runtime_types::frame_system::pallet::Event), @@ -8424,17 +1517,15 @@ pub mod api { pub mod fixed_point { use super::runtime_types; #[derive( + :: codec :: Decode, + :: codec :: Encode, :: subxt :: ext :: codec :: CompactAs, - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, Clone, Debug, )] pub struct FixedU128(pub ::core::primitive::u128); } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum ArithmeticError { #[codec(index = 0)] Underflow, @@ -8448,19 +1539,9 @@ pub mod api { use super::runtime_types; pub mod app { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct Public(pub runtime_types::sp_core::ed25519::Public); - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct Signature(pub runtime_types::sp_core::ed25519::Signature); } } @@ -8468,39 +1549,19 @@ pub mod api { use super::runtime_types; pub mod ecdsa { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct Signature(pub [::core::primitive::u8; 65usize]); } pub mod ed25519 { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct Public(pub [::core::primitive::u8; 32usize]); - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct Signature(pub [::core::primitive::u8; 64usize]); } pub mod sr25519 { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct Signature(pub [::core::primitive::u8; 64usize]); } } @@ -8510,12 +1571,7 @@ pub mod api { use super::runtime_types; pub mod digest { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum DigestItem { #[codec(index = 6)] PreRuntime( @@ -8540,21 +1596,14 @@ pub mod api { } pub mod unchecked_extrinsic { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct UncheckedExtrinsic<_0, _1, _2, _3>( pub ::std::vec::Vec<::core::primitive::u8>, #[codec(skip)] pub ::core::marker::PhantomData<(_1, _0, _2, _3)>, ); } } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum DispatchError { #[codec(index = 0)] Other, @@ -8583,16 +1632,12 @@ pub mod api { #[codec(index = 12)] Unavailable, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct ModuleError { pub index: ::core::primitive::u8, pub error: [::core::primitive::u8; 4usize], } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum MultiSignature { #[codec(index = 0)] Ed25519(runtime_types::sp_core::ed25519::Signature), @@ -8601,9 +1646,7 @@ pub mod api { #[codec(index = 2)] Ecdsa(runtime_types::sp_core::ecdsa::Signature), } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum TokenError { #[codec(index = 0)] FundsUnavailable, @@ -8624,9 +1667,7 @@ pub mod api { #[codec(index = 8)] NotExpendable, } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum TransactionalError { #[codec(index = 0)] LimitReached, @@ -8638,12 +1679,7 @@ pub mod api { use super::runtime_types; pub mod storage_proof { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct StorageProof { pub trie_nodes: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, } @@ -8651,9 +1687,7 @@ pub mod api { } pub mod sp_version { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct RuntimeVersion { pub spec_name: ::std::string::String, pub impl_name: ::std::string::String, @@ -8668,9 +1702,7 @@ pub mod api { } pub mod sp_weights { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct RuntimeDbWeight { pub read: ::core::primitive::u64, pub write: ::core::primitive::u64, @@ -8680,12 +1712,7 @@ pub mod api { use super::runtime_types; pub mod double_encoded { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct DoubleEncoded { pub encoded: ::std::vec::Vec<::core::primitive::u8>, } @@ -8694,12 +1721,7 @@ pub mod api { use super::runtime_types; pub mod junction { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Junction { #[codec(index = 0)] Parachain(#[codec(compact)] ::core::primitive::u32), @@ -8740,24 +1762,14 @@ pub mod api { } pub mod multiasset { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum AssetId { #[codec(index = 0)] Concrete(runtime_types::xcm::v2::multilocation::MultiLocation), #[codec(index = 1)] Abstract(::std::vec::Vec<::core::primitive::u8>), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum AssetInstance { #[codec(index = 0)] Undefined, @@ -8774,67 +1786,37 @@ pub mod api { #[codec(index = 6)] Blob(::std::vec::Vec<::core::primitive::u8>), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Fungibility { #[codec(index = 0)] Fungible(#[codec(compact)] ::core::primitive::u128), #[codec(index = 1)] NonFungible(runtime_types::xcm::v2::multiasset::AssetInstance), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct MultiAsset { pub id: runtime_types::xcm::v2::multiasset::AssetId, pub fun: runtime_types::xcm::v2::multiasset::Fungibility, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum MultiAssetFilter { #[codec(index = 0)] Definite(runtime_types::xcm::v2::multiasset::MultiAssets), #[codec(index = 1)] Wild(runtime_types::xcm::v2::multiasset::WildMultiAsset), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct MultiAssets( pub ::std::vec::Vec, ); - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum WildFungibility { #[codec(index = 0)] Fungible, #[codec(index = 1)] NonFungible, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum WildMultiAsset { #[codec(index = 0)] All, @@ -8847,12 +1829,7 @@ pub mod api { } pub mod multilocation { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Junctions { #[codec(index = 0)] Here, @@ -8915,12 +1892,7 @@ pub mod api { runtime_types::xcm::v2::junction::Junction, ), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct MultiLocation { pub parents: ::core::primitive::u8, pub interior: runtime_types::xcm::v2::multilocation::Junctions, @@ -8928,12 +1900,7 @@ pub mod api { } pub mod traits { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Error { #[codec(index = 0)] Overflow, @@ -8989,12 +1956,7 @@ pub mod api { WeightNotComputable, } } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum BodyId { #[codec(index = 0)] Unit, @@ -9021,12 +1983,7 @@ pub mod api { #[codec(index = 9)] Treasury, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum BodyPart { #[codec(index = 0)] Voice, @@ -9057,12 +2014,7 @@ pub mod api { denom: ::core::primitive::u32, }, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Instruction { #[codec(index = 0)] WithdrawAsset(runtime_types::xcm::v2::multiasset::MultiAssets), @@ -9202,12 +2154,7 @@ pub mod api { #[codec(index = 27)] UnsubscribeVersion, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum NetworkId { #[codec(index = 0)] Any, @@ -9222,12 +2169,7 @@ pub mod api { #[codec(index = 3)] Kusama, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum OriginKind { #[codec(index = 0)] Native, @@ -9238,12 +2180,7 @@ pub mod api { #[codec(index = 3)] Xcm, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Response { #[codec(index = 0)] Null, @@ -9259,36 +2196,21 @@ pub mod api { #[codec(index = 3)] Version(::core::primitive::u32), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum WeightLimit { #[codec(index = 0)] Unlimited, #[codec(index = 1)] Limited(#[codec(compact)] ::core::primitive::u64), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct Xcm(pub ::std::vec::Vec); } pub mod v3 { use super::runtime_types; pub mod junction { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum BodyId { #[codec(index = 0)] Unit, @@ -9311,12 +2233,7 @@ pub mod api { #[codec(index = 9)] Treasury, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum BodyPart { #[codec(index = 0)] Voice, @@ -9347,12 +2264,7 @@ pub mod api { denom: ::core::primitive::u32, }, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Junction { #[codec(index = 0)] Parachain(#[codec(compact)] ::core::primitive::u32), @@ -9394,12 +2306,7 @@ pub mod api { #[codec(index = 9)] GlobalConsensus(runtime_types::xcm::v3::junction::NetworkId), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum NetworkId { #[codec(index = 0)] ByGenesis([::core::primitive::u8; 32usize]), @@ -9431,12 +2338,7 @@ pub mod api { } pub mod junctions { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Junctions { #[codec(index = 0)] Here, @@ -9502,24 +2404,14 @@ pub mod api { } pub mod multiasset { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum AssetId { #[codec(index = 0)] Concrete(runtime_types::xcm::v3::multilocation::MultiLocation), #[codec(index = 1)] Abstract([::core::primitive::u8; 32usize]), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum AssetInstance { #[codec(index = 0)] Undefined, @@ -9534,67 +2426,37 @@ pub mod api { #[codec(index = 5)] Array32([::core::primitive::u8; 32usize]), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Fungibility { #[codec(index = 0)] Fungible(#[codec(compact)] ::core::primitive::u128), #[codec(index = 1)] NonFungible(runtime_types::xcm::v3::multiasset::AssetInstance), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct MultiAsset { pub id: runtime_types::xcm::v3::multiasset::AssetId, pub fun: runtime_types::xcm::v3::multiasset::Fungibility, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum MultiAssetFilter { #[codec(index = 0)] Definite(runtime_types::xcm::v3::multiasset::MultiAssets), #[codec(index = 1)] Wild(runtime_types::xcm::v3::multiasset::WildMultiAsset), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct MultiAssets( pub ::std::vec::Vec, ); - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum WildFungibility { #[codec(index = 0)] Fungible, #[codec(index = 1)] NonFungible, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum WildMultiAsset { #[codec(index = 0)] All, @@ -9616,12 +2478,7 @@ pub mod api { } pub mod multilocation { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct MultiLocation { pub parents: ::core::primitive::u8, pub interior: runtime_types::xcm::v3::junctions::Junctions, @@ -9629,12 +2486,7 @@ pub mod api { } pub mod traits { use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Error { #[codec(index = 0)] Overflow, @@ -9717,12 +2569,7 @@ pub mod api { #[codec(index = 39)] ExceedsStackLimit, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Outcome { #[codec(index = 0)] Complete(::sp_weights::Weight), @@ -9732,12 +2579,7 @@ pub mod api { Error(runtime_types::xcm::v3::traits::Error), } } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Instruction { #[codec(index = 0)] WithdrawAsset(runtime_types::xcm::v3::multiasset::MultiAssets), @@ -9946,12 +2788,7 @@ pub mod api { >, }, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum MaybeErrorCode { #[codec(index = 0)] Success, @@ -9968,12 +2805,7 @@ pub mod api { >, ), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct PalletInfo { #[codec(compact)] pub index: ::core::primitive::u32, @@ -9990,24 +2822,14 @@ pub mod api { #[codec(compact)] pub patch: ::core::primitive::u32, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct QueryResponseInfo { pub destination: runtime_types::xcm::v3::multilocation::MultiLocation, #[codec(compact)] pub query_id: ::core::primitive::u64, pub max_weight: ::sp_weights::Weight, } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum Response { #[codec(index = 0)] Null, @@ -10031,47 +2853,31 @@ pub mod api { #[codec(index = 5)] DispatchResult(runtime_types::xcm::v3::MaybeErrorCode), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum WeightLimit { #[codec(index = 0)] Unlimited, #[codec(index = 1)] Limited(::sp_weights::Weight), } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - Clone, - Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub struct Xcm(pub ::std::vec::Vec); } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum VersionedMultiAssets { #[codec(index = 1)] V2(runtime_types::xcm::v2::multiasset::MultiAssets), #[codec(index = 3)] V3(runtime_types::xcm::v3::multiasset::MultiAssets), } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum VersionedMultiLocation { #[codec(index = 1)] V2(runtime_types::xcm::v2::multilocation::MultiLocation), #[codec(index = 3)] V3(runtime_types::xcm::v3::multilocation::MultiLocation), } - #[derive( - :: subxt :: ext :: codec :: Decode, :: subxt :: ext :: codec :: Encode, Clone, Debug, - )] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] pub enum VersionedXcm { #[codec(index = 2)] V2(runtime_types::xcm::v2::Xcm), @@ -10080,131 +2886,4 @@ pub mod api { } } } - #[doc = r" The default error type returned when there is a runtime issue,"] - #[doc = r" exposed here for ease of use."] - pub type DispatchError = runtime_types::sp_runtime::DispatchError; - pub fn constants() -> ConstantsApi { - ConstantsApi - } - pub fn storage() -> StorageApi { - StorageApi - } - pub fn tx() -> TransactionApi { - TransactionApi - } - pub struct ConstantsApi; - impl ConstantsApi { - pub fn system(&self) -> system::constants::ConstantsApi { - system::constants::ConstantsApi - } - pub fn timestamp(&self) -> timestamp::constants::ConstantsApi { - timestamp::constants::ConstantsApi - } - pub fn transaction_payment(&self) -> transaction_payment::constants::ConstantsApi { - transaction_payment::constants::ConstantsApi - } - pub fn balances(&self) -> balances::constants::ConstantsApi { - balances::constants::ConstantsApi - } - pub fn bridge_millau_grandpa(&self) -> bridge_millau_grandpa::constants::ConstantsApi { - bridge_millau_grandpa::constants::ConstantsApi - } - pub fn bridge_millau_messages(&self) -> bridge_millau_messages::constants::ConstantsApi { - bridge_millau_messages::constants::ConstantsApi - } - } - pub struct StorageApi; - impl StorageApi { - pub fn system(&self) -> system::storage::StorageApi { - system::storage::StorageApi - } - pub fn timestamp(&self) -> timestamp::storage::StorageApi { - timestamp::storage::StorageApi - } - pub fn sudo(&self) -> sudo::storage::StorageApi { - sudo::storage::StorageApi - } - pub fn transaction_payment(&self) -> transaction_payment::storage::StorageApi { - transaction_payment::storage::StorageApi - } - pub fn parachain_system(&self) -> parachain_system::storage::StorageApi { - parachain_system::storage::StorageApi - } - pub fn parachain_info(&self) -> parachain_info::storage::StorageApi { - parachain_info::storage::StorageApi - } - pub fn balances(&self) -> balances::storage::StorageApi { - balances::storage::StorageApi - } - pub fn xcmp_queue(&self) -> xcmp_queue::storage::StorageApi { - xcmp_queue::storage::StorageApi - } - pub fn dmp_queue(&self) -> dmp_queue::storage::StorageApi { - dmp_queue::storage::StorageApi - } - pub fn bridge_relayers(&self) -> bridge_relayers::storage::StorageApi { - bridge_relayers::storage::StorageApi - } - pub fn bridge_millau_grandpa(&self) -> bridge_millau_grandpa::storage::StorageApi { - bridge_millau_grandpa::storage::StorageApi - } - pub fn bridge_millau_messages(&self) -> bridge_millau_messages::storage::StorageApi { - bridge_millau_messages::storage::StorageApi - } - } - pub struct TransactionApi; - impl TransactionApi { - pub fn system(&self) -> system::calls::TransactionApi { - system::calls::TransactionApi - } - pub fn timestamp(&self) -> timestamp::calls::TransactionApi { - timestamp::calls::TransactionApi - } - pub fn sudo(&self) -> sudo::calls::TransactionApi { - sudo::calls::TransactionApi - } - pub fn parachain_system(&self) -> parachain_system::calls::TransactionApi { - parachain_system::calls::TransactionApi - } - pub fn balances(&self) -> balances::calls::TransactionApi { - balances::calls::TransactionApi - } - pub fn xcmp_queue(&self) -> xcmp_queue::calls::TransactionApi { - xcmp_queue::calls::TransactionApi - } - pub fn polkadot_xcm(&self) -> polkadot_xcm::calls::TransactionApi { - polkadot_xcm::calls::TransactionApi - } - pub fn cumulus_xcm(&self) -> cumulus_xcm::calls::TransactionApi { - cumulus_xcm::calls::TransactionApi - } - pub fn dmp_queue(&self) -> dmp_queue::calls::TransactionApi { - dmp_queue::calls::TransactionApi - } - pub fn bridge_relayers(&self) -> bridge_relayers::calls::TransactionApi { - bridge_relayers::calls::TransactionApi - } - pub fn bridge_millau_grandpa(&self) -> bridge_millau_grandpa::calls::TransactionApi { - bridge_millau_grandpa::calls::TransactionApi - } - pub fn bridge_millau_messages(&self) -> bridge_millau_messages::calls::TransactionApi { - bridge_millau_messages::calls::TransactionApi - } - } - #[doc = r" check whether the Client you are using is aligned with the statically generated codegen."] - pub fn validate_codegen>( - client: &C, - ) -> Result<(), ::subxt::error::MetadataError> { - let runtime_metadata_hash = client.metadata().metadata_hash(&PALLETS); - if runtime_metadata_hash != - [ - 244u8, 26u8, 245u8, 96u8, 241u8, 56u8, 22u8, 163u8, 219u8, 209u8, 103u8, 161u8, - 138u8, 242u8, 33u8, 114u8, 162u8, 107u8, 1u8, 216u8, 115u8, 116u8, 164u8, 126u8, - 81u8, 51u8, 88u8, 36u8, 180u8, 113u8, 18u8, 58u8, - ] { - Err(::subxt::error::MetadataError::IncompatibleMetadata) - } else { - Ok(()) - } - } } diff --git a/relays/client-rialto-parachain/src/lib.rs b/relays/client-rialto-parachain/src/lib.rs index ddb54fbefc0..bb7be12979f 100644 --- a/relays/client-rialto-parachain/src/lib.rs +++ b/relays/client-rialto-parachain/src/lib.rs @@ -61,8 +61,7 @@ impl Chain for RialtoParachain { impl ChainWithBalances for RialtoParachain { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { - let key = codegen_runtime::api::storage().system().account(account_id); - StorageKey(key.to_bytes()) + bp_polkadot_core::AccountInfoStorageMapKeyProvider::final_key(account_id) } } diff --git a/relays/client-substrate/Cargo.toml b/relays/client-substrate/Cargo.toml index 2a79832bf9d..90904ab9be1 100644 --- a/relays/client-substrate/Cargo.toml +++ b/relays/client-substrate/Cargo.toml @@ -10,7 +10,7 @@ async-std = { version = "1.6.5", features = ["attributes"] } async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.1.5" } futures = "0.3.28" -jsonrpsee = { version = "0.16", features = ["macros", "ws-client"] } +jsonrpsee = { version = "0.17", features = ["macros", "ws-client"] } log = "0.4.17" num-traits = "0.2" rand = "0.8" diff --git a/relays/client-substrate/src/chain.rs b/relays/client-substrate/src/chain.rs index 8c7dc00aa67..54c9ad4f3b6 100644 --- a/relays/client-substrate/src/chain.rs +++ b/relays/client-substrate/src/chain.rs @@ -55,7 +55,7 @@ pub trait Chain: ChainBase + Clone { /// Block type. type SignedBlock: Member + Serialize + DeserializeOwned + BlockWithJustification; /// The aggregated `Call` type. - type Call: Clone + Codec + Debug + Send; + type Call: Clone + Codec + Debug + Send + Sync; } /// Substrate-based relay chain that supports parachains. diff --git a/relays/client-substrate/src/client.rs b/relays/client-substrate/src/client.rs index 27af7d7d77f..91ff7b9a214 100644 --- a/relays/client-substrate/src/client.rs +++ b/relays/client-substrate/src/client.rs @@ -231,7 +231,7 @@ impl Client { let client = tokio .spawn(async move { RpcClientBuilder::default() - .max_notifs_per_subscription(MAX_SUBSCRIPTION_CAPACITY) + .max_buffer_capacity_per_subscription(MAX_SUBSCRIPTION_CAPACITY) .build(&uri) .await }) diff --git a/relays/client-substrate/src/error.rs b/relays/client-substrate/src/error.rs index 54247c5dc17..40015c122bb 100644 --- a/relays/client-substrate/src/error.rs +++ b/relays/client-substrate/src/error.rs @@ -138,13 +138,11 @@ impl Error { impl MaybeConnectionError for Error { fn is_connection_error(&self) -> bool { match *self { - Error::RpcError(RpcError::Transport(_)) - // right now if connection to the ws server is dropped (after it is already established), - // we're getting this error - | Error::RpcError(RpcError::Internal(_)) - | Error::RpcError(RpcError::RestartNeeded(_)) - | Error::ClientNotSynced(_) => true, - Error::FailedToReadBestFinalizedHeaderHash { ref error, .. } => error.is_connection_error(), + Error::RpcError(RpcError::Transport(_)) | + Error::RpcError(RpcError::RestartNeeded(_)) | + Error::ClientNotSynced(_) => true, + Error::FailedToReadBestFinalizedHeaderHash { ref error, .. } => + error.is_connection_error(), Error::FailedToReadBestHeader { ref error, .. } => error.is_connection_error(), Error::FailedToReadHeaderByHash { ref error, .. } => error.is_connection_error(), Error::ErrorExecutingRuntimeCall { ref error, .. } => error.is_connection_error(), diff --git a/relays/client-substrate/src/rpc.rs b/relays/client-substrate/src/rpc.rs index 083b1dea761..94726e49187 100644 --- a/relays/client-substrate/src/rpc.rs +++ b/relays/client-substrate/src/rpc.rs @@ -73,7 +73,7 @@ pub(crate) trait SubstrateAuthor { async fn pending_extrinsics(&self) -> RpcResult>; /// Submit and watch for extrinsic state. #[subscription(name = "submitAndWatchExtrinsic", unsubscribe = "unwatchExtrinsic", item = TransactionStatusOf)] - fn submit_and_watch_extrinsic(&self, extrinsic: Bytes); + async fn submit_and_watch_extrinsic(&self, extrinsic: Bytes); } /// RPC methods of Substrate `state` namespace, that we are using. @@ -118,7 +118,7 @@ pub trait SubstrateFinalityClient { pub(crate) trait SubstrateGrandpa { /// Subscribe to GRANDPA justifications. #[subscription(name = "subscribeJustifications", unsubscribe = "unsubscribeJustifications", item = Bytes)] - fn subscribe_justifications(&self); + async fn subscribe_justifications(&self); } /// RPC finality methods of Substrate `grandpa` namespace, that we are using. @@ -136,7 +136,7 @@ impl SubstrateFinalityClient for SubstrateGrandpaFinalit pub(crate) trait SubstrateBeefy { /// Subscribe to BEEFY justifications. #[subscription(name = "subscribeJustifications", unsubscribe = "unsubscribeJustifications", item = Bytes)] - fn subscribe_justifications(&self); + async fn subscribe_justifications(&self); } /// RPC finality methods of Substrate `beefy` namespace, that we are using. diff --git a/relays/lib-substrate-relay/src/lib.rs b/relays/lib-substrate-relay/src/lib.rs index 37a4d602e59..f9bd80d5079 100644 --- a/relays/lib-substrate-relay/src/lib.rs +++ b/relays/lib-substrate-relay/src/lib.rs @@ -91,7 +91,7 @@ impl TaggedAccount { } /// Batch call builder. -pub trait BatchCallBuilder: Clone + Send { +pub trait BatchCallBuilder: Clone + Send + Sync { /// Create batch call from given calls vector. fn build_batch_call(&self, _calls: Vec) -> Call; } diff --git a/relays/messages/Cargo.toml b/relays/messages/Cargo.toml index 8c4b8257d5a..a45b2728105 100644 --- a/relays/messages/Cargo.toml +++ b/relays/messages/Cargo.toml @@ -8,6 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] async-std = { version = "1.6.5", features = ["attributes"] } async-trait = "0.1" +env_logger = "0.10" futures = "0.3.28" hex = "0.4" log = "0.4.17" diff --git a/relays/messages/src/message_lane_loop.rs b/relays/messages/src/message_lane_loop.rs index ba86f05ffd3..b681d86d2ae 100644 --- a/relays/messages/src/message_lane_loop.rs +++ b/relays/messages/src/message_lane_loop.rs @@ -111,7 +111,7 @@ pub struct NoncesSubmitArtifacts { /// Batch transaction that already submit some headers and needs to be extended with /// messages/delivery proof before sending. -pub trait BatchTransaction: Debug + Send { +pub trait BatchTransaction: Debug + Send + Sync { /// Header that was required in the original call and which is bundled within this /// batch transaction. fn required_header_id(&self) -> HeaderId; @@ -622,11 +622,19 @@ pub(crate) mod tests { } impl TestClientData { - fn receive_messages(&mut self, proof: TestMessagesProof) { + fn receive_messages( + &mut self, + maybe_batch_tx: Option, + proof: TestMessagesProof, + ) { self.target_state.best_self = HeaderId(self.target_state.best_self.0 + 1, self.target_state.best_self.1 + 1); self.target_state.best_finalized_self = self.target_state.best_self; self.target_latest_received_nonce = *proof.0.end(); + if let Some(maybe_batch_tx) = maybe_batch_tx { + self.target_state.best_finalized_peer_at_best_self = + Some(maybe_batch_tx.required_header_id()); + } if let Some(target_latest_confirmed_received_nonce) = proof.1 { self.target_latest_confirmed_received_nonce = target_latest_confirmed_received_nonce; @@ -634,10 +642,18 @@ pub(crate) mod tests { self.submitted_messages_proofs.push(proof); } - fn receive_messages_delivery_proof(&mut self, proof: TestMessagesReceivingProof) { + fn receive_messages_delivery_proof( + &mut self, + maybe_batch_tx: Option, + proof: TestMessagesReceivingProof, + ) { self.source_state.best_self = HeaderId(self.source_state.best_self.0 + 1, self.source_state.best_self.1 + 1); self.source_state.best_finalized_self = self.source_state.best_self; + if let Some(maybe_batch_tx) = maybe_batch_tx { + self.source_state.best_finalized_peer_at_best_self = + Some(maybe_batch_tx.required_header_id()); + } self.submitted_messages_receiving_proofs.push(proof); self.source_latest_confirmed_received_nonce = proof; } @@ -760,13 +776,13 @@ pub(crate) mod tests { async fn submit_messages_receiving_proof( &self, - _maybe_batch_tx: Option, + maybe_batch_tx: Option, _generated_at_block: TargetHeaderIdOf, proof: TestMessagesReceivingProof, ) -> Result { let mut data = self.data.lock(); (self.tick)(&mut data); - data.receive_messages_delivery_proof(proof); + data.receive_messages_delivery_proof(maybe_batch_tx, proof); (self.post_tick)(&mut data); Ok(TestTransactionTracker(data.source_tracked_transaction_status)) } @@ -885,7 +901,7 @@ pub(crate) mod tests { async fn submit_messages_proof( &self, - _maybe_batch_tx: Option, + maybe_batch_tx: Option, _generated_at_header: SourceHeaderIdOf, nonces: RangeInclusive, proof: TestMessagesProof, @@ -895,7 +911,7 @@ pub(crate) mod tests { if data.is_target_fails { return Err(TestError) } - data.receive_messages(proof); + data.receive_messages(maybe_batch_tx, proof); (self.post_tick)(&mut data); Ok(NoncesSubmitArtifacts { nonces, diff --git a/relays/messages/src/message_race_delivery.rs b/relays/messages/src/message_race_delivery.rs index 7a245858b32..06e02a06017 100644 --- a/relays/messages/src/message_race_delivery.rs +++ b/relays/messages/src/message_race_delivery.rs @@ -290,7 +290,171 @@ impl std::fmt::Debug for MessageDeliveryStrategy MessageDeliveryStrategy { +impl MessageDeliveryStrategy +where + P: MessageLane, + SC: MessageLaneSourceClient

, + TC: MessageLaneTargetClient

, +{ + /// Returns true if some race action can be selected (with `select_race_action`) at given + /// `best_finalized_source_header_id_at_best_target` source header at target. + async fn can_submit_transaction_with< + RS: RaceState, TargetHeaderIdOf

>, + >( + &self, + mut race_state: RS, + maybe_best_finalized_source_header_id_at_best_target: Option>, + ) -> bool { + if let Some(best_finalized_source_header_id_at_best_target) = + maybe_best_finalized_source_header_id_at_best_target + { + race_state.set_best_finalized_source_header_id_at_best_target( + best_finalized_source_header_id_at_best_target, + ); + + return self.select_race_action(race_state).await.is_some() + } + + false + } + + async fn select_race_action, TargetHeaderIdOf

>>( + &self, + race_state: RS, + ) -> Option<(RangeInclusive, MessageProofParameters)> { + // if we have already selected nonces that we want to submit, do nothing + if race_state.nonces_to_submit().is_some() { + return None + } + + // if we already submitted some nonces, do nothing + if race_state.nonces_submitted().is_some() { + return None + } + + let best_target_nonce = self.strategy.best_at_target()?; + let best_finalized_source_header_id_at_best_target = + race_state.best_finalized_source_header_id_at_best_target()?; + let target_nonces = self.target_nonces.as_ref()?; + let latest_confirmed_nonce_at_source = self + .latest_confirmed_nonce_at_source(&best_finalized_source_header_id_at_best_target) + .unwrap_or(target_nonces.nonces_data.confirmed_nonce); + + // There's additional condition in the message delivery race: target would reject messages + // if there are too much unconfirmed messages at the inbound lane. + + // Ok - we may have new nonces to deliver. But target may still reject new messages, because + // we haven't notified it that (some) messages have been confirmed. So we may want to + // include updated `source.latest_confirmed` in the proof. + // + // Important note: we're including outbound state lane proof whenever there are unconfirmed + // nonces on the target chain. Other strategy is to include it only if it's absolutely + // necessary. + let latest_received_nonce_at_target = target_nonces.latest_nonce; + let latest_confirmed_nonce_at_target = target_nonces.nonces_data.confirmed_nonce; + let outbound_state_proof_required = + latest_confirmed_nonce_at_target < latest_confirmed_nonce_at_source; + + // The target node would also reject messages if there are too many entries in the + // "unrewarded relayers" set. If we are unable to prove new rewards to the target node, then + // we should wait for confirmations race. + let unrewarded_limit_reached = + target_nonces.nonces_data.unrewarded_relayers.unrewarded_relayer_entries >= + self.max_unrewarded_relayer_entries_at_target || + target_nonces.nonces_data.unrewarded_relayers.total_messages >= + self.max_unconfirmed_nonces_at_target; + if unrewarded_limit_reached { + // so there are already too many unrewarded relayer entries in the set + // + // => check if we can prove enough rewards. If not, we should wait for more rewards to + // be paid + let number_of_rewards_being_proved = + latest_confirmed_nonce_at_source.saturating_sub(latest_confirmed_nonce_at_target); + let enough_rewards_being_proved = number_of_rewards_being_proved >= + target_nonces.nonces_data.unrewarded_relayers.messages_in_oldest_entry; + if !enough_rewards_being_proved { + return None + } + } + + // If we're here, then the confirmations race did its job && sending side now knows that + // messages have been delivered. Now let's select nonces that we want to deliver. + // + // We may deliver at most: + // + // max_unconfirmed_nonces_at_target - (latest_received_nonce_at_target - + // latest_confirmed_nonce_at_target) + // + // messages in the batch. But since we're including outbound state proof in the batch, then + // it may be increased to: + // + // max_unconfirmed_nonces_at_target - (latest_received_nonce_at_target - + // latest_confirmed_nonce_at_source) + let future_confirmed_nonce_at_target = if outbound_state_proof_required { + latest_confirmed_nonce_at_source + } else { + latest_confirmed_nonce_at_target + }; + let max_nonces = latest_received_nonce_at_target + .checked_sub(future_confirmed_nonce_at_target) + .and_then(|diff| self.max_unconfirmed_nonces_at_target.checked_sub(diff)) + .unwrap_or_default(); + let max_nonces = std::cmp::min(max_nonces, self.max_messages_in_single_batch); + let max_messages_weight_in_single_batch = self.max_messages_weight_in_single_batch; + let max_messages_size_in_single_batch = self.max_messages_size_in_single_batch; + let lane_source_client = self.lane_source_client.clone(); + let lane_target_client = self.lane_target_client.clone(); + + // select nonces from nonces, available for delivery + let selected_nonces = match self.strategy.available_source_queue_indices(race_state) { + Some(available_source_queue_indices) => { + let source_queue = self.strategy.source_queue(); + let reference = RelayMessagesBatchReference { + max_messages_in_this_batch: max_nonces, + max_messages_weight_in_single_batch, + max_messages_size_in_single_batch, + lane_source_client: lane_source_client.clone(), + lane_target_client: lane_target_client.clone(), + best_target_nonce, + nonces_queue: source_queue.clone(), + nonces_queue_range: available_source_queue_indices, + metrics: self.metrics_msg.clone(), + }; + + MessageRaceLimits::decide(reference).await + }, + None => { + // we still may need to submit delivery transaction with zero messages to + // unblock the lane. But it'll only be accepted if the lane is blocked + // (i.e. when `unrewarded_limit_reached` is `true`) + None + }, + }; + + // check if we need unblocking transaction and we may submit it + #[allow(clippy::reversed_empty_ranges)] + let selected_nonces = match selected_nonces { + Some(selected_nonces) => selected_nonces, + None if unrewarded_limit_reached && outbound_state_proof_required => 1..=0, + _ => return None, + }; + + let dispatch_weight = self.dispatch_weight_for_range(&selected_nonces); + Some(( + selected_nonces, + MessageProofParameters { outbound_state_proof_required, dispatch_weight }, + )) + } + + /// Returns lastest confirmed message at source chain, given source block. + fn latest_confirmed_nonce_at_source(&self, at: &SourceHeaderIdOf

) -> Option { + self.latest_confirmed_nonces_at_source + .iter() + .take_while(|(id, _)| id.0 <= at.0) + .last() + .map(|(_, nonce)| *nonce) + } + /// Returns total weight of all undelivered messages. fn dispatch_weight_for_range(&self, range: &RangeInclusive) -> Weight { self.strategy @@ -322,9 +486,10 @@ where self.strategy.is_empty() } - fn required_source_header_at_target, TargetHeaderIdOf

>>( + async fn required_source_header_at_target< + RS: RaceState, TargetHeaderIdOf

>, + >( &self, - current_best: &SourceHeaderIdOf

, race_state: RS, ) -> Option> { // we have already submitted something - let's wait until it is mined @@ -332,32 +497,41 @@ where return None } - let has_nonces_to_deliver = !self.strategy.is_empty(); - let header_required_for_messages_delivery = - self.strategy.required_source_header_at_target(current_best, race_state); - let header_required_for_reward_confirmations_delivery = self - .latest_confirmed_nonces_at_source - .back() - .filter(|(id, nonce)| *nonce != 0 && id.0 > current_best.0) - .map(|(id, _)| id.clone()); - match ( - has_nonces_to_deliver, - header_required_for_messages_delivery, - header_required_for_reward_confirmations_delivery, - ) { - // if we need to delver messages and proof-of-delivery-confirmations, then we need to - // select the most recent header to avoid extra roundtrips - (true, Some(id1), Some(id2)) => Some(if id1.0 > id2.0 { id1 } else { id2 }), - // if we only need to deliver messages - fine, let's require some source header - // - // if we need new header for proof-of-delivery-confirmations - let's also ask for that. - // Even though it may require additional header, we'll be sure that we won't block the - // lane (sometimes we can't deliver messages without proof-of-delivery-confirmations) - (true, a, b) => a.or(b), - // we never submit delivery transaction without messages, so if `has_nonces_to_deliver` - // if `false`, we don't need any source headers at target - (false, _, _) => None, + // if we can deliver something using current race state, go on + let selected_nonces = self.select_race_action(race_state.clone()).await; + if selected_nonces.is_some() { + return None + } + + // check if we may deliver some messages if we'll relay require source header + // to target first + let maybe_source_header_for_delivery = + self.strategy.source_queue().back().map(|(id, _)| id.clone()); + if self + .can_submit_transaction_with( + race_state.clone(), + maybe_source_header_for_delivery.clone(), + ) + .await + { + return maybe_source_header_for_delivery + } + + // ok, we can't delivery anything even if we relay some source blocks first. But maybe + // the lane is blocked and we need to submit unblock transaction? + let maybe_source_header_for_reward_confirmation = + self.latest_confirmed_nonces_at_source.back().map(|(id, _)| id.clone()); + if self + .can_submit_transaction_with( + race_state.clone(), + maybe_source_header_for_reward_confirmation.clone(), + ) + .await + { + return maybe_source_header_for_reward_confirmation } + + None } fn best_at_source(&self) -> Option { @@ -387,6 +561,11 @@ where self.strategy.source_nonces_updated(at_block, nonces) } + fn reset_best_target_nonces(&mut self) { + self.target_nonces = None; + self.strategy.reset_best_target_nonces(); + } + fn best_target_nonces_updated, TargetHeaderIdOf

>>( &mut self, nonces: TargetClientNonces, @@ -436,128 +615,7 @@ where &self, race_state: RS, ) -> Option<(RangeInclusive, Self::ProofParameters)> { - let best_target_nonce = self.strategy.best_at_target()?; - let best_finalized_source_header_id_at_best_target = - race_state.best_finalized_source_header_id_at_best_target()?; - let latest_confirmed_nonce_at_source = self - .latest_confirmed_nonces_at_source - .iter() - .take_while(|(id, _)| id.0 <= best_finalized_source_header_id_at_best_target.0) - .last() - .map(|(_, nonce)| *nonce) - .unwrap_or(best_target_nonce); - let target_nonces = self.target_nonces.as_ref()?; - - // There's additional condition in the message delivery race: target would reject messages - // if there are too much unconfirmed messages at the inbound lane. - - // The receiving race is responsible to deliver confirmations back to the source chain. So - // if there's a lot of unconfirmed messages, let's wait until it'll be able to do its job. - let latest_received_nonce_at_target = target_nonces.latest_nonce; - let confirmations_missing = - latest_received_nonce_at_target.checked_sub(latest_confirmed_nonce_at_source); - match confirmations_missing { - Some(confirmations_missing) - if confirmations_missing >= self.max_unconfirmed_nonces_at_target => - { - log::debug!( - target: "bridge", - "Cannot deliver any more messages from {} to {}. Too many unconfirmed nonces \ - at target: target.latest_received={:?}, source.latest_confirmed={:?}, max={:?}", - MessageDeliveryRace::

::source_name(), - MessageDeliveryRace::

::target_name(), - latest_received_nonce_at_target, - latest_confirmed_nonce_at_source, - self.max_unconfirmed_nonces_at_target, - ); - - return None - }, - _ => (), - } - - // Ok - we may have new nonces to deliver. But target may still reject new messages, because - // we haven't notified it that (some) messages have been confirmed. So we may want to - // include updated `source.latest_confirmed` in the proof. - // - // Important note: we're including outbound state lane proof whenever there are unconfirmed - // nonces on the target chain. Other strategy is to include it only if it's absolutely - // necessary. - let latest_confirmed_nonce_at_target = target_nonces.nonces_data.confirmed_nonce; - let outbound_state_proof_required = - latest_confirmed_nonce_at_target < latest_confirmed_nonce_at_source; - - // The target node would also reject messages if there are too many entries in the - // "unrewarded relayers" set. If we are unable to prove new rewards to the target node, then - // we should wait for confirmations race. - let unrewarded_relayer_entries_limit_reached = - target_nonces.nonces_data.unrewarded_relayers.unrewarded_relayer_entries >= - self.max_unrewarded_relayer_entries_at_target; - if unrewarded_relayer_entries_limit_reached { - // so there are already too many unrewarded relayer entries in the set - // - // => check if we can prove enough rewards. If not, we should wait for more rewards to - // be paid - let number_of_rewards_being_proved = - latest_confirmed_nonce_at_source.saturating_sub(latest_confirmed_nonce_at_target); - let enough_rewards_being_proved = number_of_rewards_being_proved >= - target_nonces.nonces_data.unrewarded_relayers.messages_in_oldest_entry; - if !enough_rewards_being_proved { - return None - } - } - - // If we're here, then the confirmations race did its job && sending side now knows that - // messages have been delivered. Now let's select nonces that we want to deliver. - // - // We may deliver at most: - // - // max_unconfirmed_nonces_at_target - (latest_received_nonce_at_target - - // latest_confirmed_nonce_at_target) - // - // messages in the batch. But since we're including outbound state proof in the batch, then - // it may be increased to: - // - // max_unconfirmed_nonces_at_target - (latest_received_nonce_at_target - - // latest_confirmed_nonce_at_source) - let future_confirmed_nonce_at_target = if outbound_state_proof_required { - latest_confirmed_nonce_at_source - } else { - latest_confirmed_nonce_at_target - }; - let max_nonces = latest_received_nonce_at_target - .checked_sub(future_confirmed_nonce_at_target) - .and_then(|diff| self.max_unconfirmed_nonces_at_target.checked_sub(diff)) - .unwrap_or_default(); - let max_nonces = std::cmp::min(max_nonces, self.max_messages_in_single_batch); - let max_messages_weight_in_single_batch = self.max_messages_weight_in_single_batch; - let max_messages_size_in_single_batch = self.max_messages_size_in_single_batch; - let lane_source_client = self.lane_source_client.clone(); - let lane_target_client = self.lane_target_client.clone(); - - let available_source_queue_indices = - self.strategy.available_source_queue_indices(race_state)?; - let source_queue = self.strategy.source_queue(); - - let reference = RelayMessagesBatchReference { - max_messages_in_this_batch: max_nonces, - max_messages_weight_in_single_batch, - max_messages_size_in_single_batch, - lane_source_client: lane_source_client.clone(), - lane_target_client: lane_target_client.clone(), - best_target_nonce, - nonces_queue: source_queue.clone(), - nonces_queue_range: available_source_queue_indices, - metrics: self.metrics_msg.clone(), - }; - - let selected_nonces = MessageRaceLimits::decide(reference).await?; - let dispatch_weight = self.dispatch_weight_for_range(&selected_nonces); - - Some(( - selected_nonces, - MessageProofParameters { outbound_state_proof_required, dispatch_weight }, - )) + self.select_race_action(race_state).await } } @@ -731,22 +789,6 @@ mod tests { ); } - #[async_std::test] - async fn message_delivery_strategy_selects_nothing_if_too_many_confirmations_missing() { - let (state, mut strategy) = prepare_strategy(); - - // if there are already `max_unconfirmed_nonces_at_target` messages on target, - // we need to wait until confirmations will be delivered by receiving race - strategy.latest_confirmed_nonces_at_source = vec![( - header_id(1), - strategy.target_nonces.as_ref().unwrap().latest_nonce - - strategy.max_unconfirmed_nonces_at_target, - )] - .into_iter() - .collect(); - assert_eq!(strategy.select_nonces_to_deliver(state).await, None); - } - #[async_std::test] async fn message_delivery_strategy_includes_outbound_state_proof_when_new_nonces_are_available() { @@ -980,31 +1022,41 @@ mod tests { ); // nothing needs to be delivered now and we don't need any new headers assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); - assert_eq!(strategy.required_source_header_at_target(&header_id(1), state.clone()), None); + assert_eq!(strategy.required_source_header_at_target(state.clone()).await, None); - // now let's generate two more nonces [24; 25] at the soruce; - strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 19, 0)); - // - // - so now we'll need to relay source block#2 to be able to accept messages [24; 25]. - assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); - assert_eq!( - strategy.required_source_header_at_target(&header_id(1), state.clone()), - Some(header_id(2)) - ); - - // let's relay source block#2 + // block#2 is generated state.best_finalized_source_header_id_at_source = Some(header_id(2)); state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); state.best_target_header_id = Some(header_id(2)); state.best_finalized_target_header_id = Some(header_id(2)); + // now let's generate two more nonces [24; 25] at the source; + strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 19, 0)); + // + // we don't need to relay more headers to target, because messages [20; 23] have + // not confirmed to source yet + assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); + assert_eq!(strategy.required_source_header_at_target(state.clone()).await, None); + + // let's relay source block#3 + state.best_finalized_source_header_id_at_source = Some(header_id(3)); + state.best_finalized_source_header_id_at_best_target = Some(header_id(3)); + state.best_target_header_id = Some(header_id(3)); + state.best_finalized_target_header_id = Some(header_id(3)); + // and ask strategy again => still nothing to deliver, because parallel confirmations // race need to be pushed further assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); - assert_eq!(strategy.required_source_header_at_target(&header_id(2), state.clone()), None); + assert_eq!(strategy.required_source_header_at_target(state.clone()).await, None); + + // let's relay source block#3 + state.best_finalized_source_header_id_at_source = Some(header_id(4)); + state.best_finalized_source_header_id_at_best_target = Some(header_id(4)); + state.best_target_header_id = Some(header_id(4)); + state.best_finalized_target_header_id = Some(header_id(4)); // let's confirm messages [20; 23] - strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 23, 0)); + strategy.source_nonces_updated(header_id(4), source_nonces(24..=25, 23, 0)); // and ask strategy again => now we have everything required to deliver remaining // [24; 25] nonces and proof of [20; 23] confirmation @@ -1012,7 +1064,7 @@ mod tests { strategy.select_nonces_to_deliver(state.clone()).await, Some(((24..=25), proof_parameters(true, 2))), ); - assert_eq!(strategy.required_source_header_at_target(&header_id(2), state), None); + assert_eq!(strategy.required_source_header_at_target(state).await, None); } #[async_std::test] @@ -1041,9 +1093,9 @@ mod tests { ); } - #[test] + #[async_std::test] #[allow(clippy::reversed_empty_ranges)] - fn no_source_headers_required_at_target_if_lanes_are_empty() { + async fn no_source_headers_required_at_target_if_lanes_are_empty() { let (state, _) = prepare_strategy(); let mut strategy = TestStrategy { max_unrewarded_relayer_entries_at_target: 4, @@ -1073,7 +1125,7 @@ mod tests { strategy.latest_confirmed_nonces_at_source, VecDeque::from([(source_header_id, 0)]) ); - assert_eq!(strategy.required_source_header_at_target(&source_header_id, state), None); + assert_eq!(strategy.required_source_header_at_target(state).await, None); } #[async_std::test] @@ -1159,4 +1211,195 @@ mod tests { )), ); } + + #[async_std::test] + #[allow(clippy::reversed_empty_ranges)] + async fn delivery_race_is_able_to_unblock_lane() { + // step 1: messages 20..=23 are delivered from source to target at target block 2 + fn at_target_block_2_deliver_messages( + strategy: &mut TestStrategy, + state: &mut TestRaceState, + occupied_relayer_slots: MessageNonce, + occupied_message_slots: MessageNonce, + ) { + let nonces_at_target = TargetClientNonces { + latest_nonce: 23, + nonces_data: DeliveryRaceTargetNoncesData { + confirmed_nonce: 19, + unrewarded_relayers: UnrewardedRelayersState { + unrewarded_relayer_entries: occupied_relayer_slots, + total_messages: occupied_message_slots, + ..Default::default() + }, + }, + }; + + state.best_target_header_id = Some(header_id(2)); + state.best_finalized_target_header_id = Some(header_id(2)); + + strategy.best_target_nonces_updated(nonces_at_target.clone(), state); + strategy.finalized_target_nonces_updated(nonces_at_target, state); + } + + // step 2: delivery of messages 20..=23 is confirmed to the source node at source block 2 + fn at_source_block_2_deliver_confirmations( + strategy: &mut TestStrategy, + state: &mut TestRaceState, + ) { + state.best_finalized_source_header_id_at_source = Some(header_id(2)); + + strategy.source_nonces_updated( + header_id(2), + SourceClientNonces { new_nonces: Default::default(), confirmed_nonce: Some(23) }, + ); + } + + // step 3: finalize source block 2 at target block 3 and select nonces to deliver + async fn at_target_block_3_select_nonces_to_deliver( + strategy: &TestStrategy, + mut state: TestRaceState, + ) -> Option<(RangeInclusive, MessageProofParameters)> { + state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); + state.best_target_header_id = Some(header_id(3)); + state.best_finalized_target_header_id = Some(header_id(3)); + + strategy.select_nonces_to_deliver(state).await + } + + let max_unrewarded_relayer_entries_at_target = 4; + let max_unconfirmed_nonces_at_target = 4; + let expected_rewards_proof = Some(( + 1..=0, + MessageProofParameters { + outbound_state_proof_required: true, + dispatch_weight: Weight::zero(), + }, + )); + + // when lane is NOT blocked + let (mut state, mut strategy) = prepare_strategy(); + at_target_block_2_deliver_messages( + &mut strategy, + &mut state, + max_unrewarded_relayer_entries_at_target - 1, + max_unconfirmed_nonces_at_target - 1, + ); + at_source_block_2_deliver_confirmations(&mut strategy, &mut state); + assert_eq!(strategy.required_source_header_at_target(state.clone()).await, None); + assert_eq!(at_target_block_3_select_nonces_to_deliver(&strategy, state).await, None); + + // when lane is blocked by no-relayer-slots in unrewarded relayers vector + let (mut state, mut strategy) = prepare_strategy(); + at_target_block_2_deliver_messages( + &mut strategy, + &mut state, + max_unrewarded_relayer_entries_at_target, + max_unconfirmed_nonces_at_target - 1, + ); + at_source_block_2_deliver_confirmations(&mut strategy, &mut state); + assert_eq!( + strategy.required_source_header_at_target(state.clone()).await, + Some(header_id(2)) + ); + assert_eq!( + at_target_block_3_select_nonces_to_deliver(&strategy, state).await, + expected_rewards_proof + ); + + // when lane is blocked by no-message-slots in unrewarded relayers vector + let (mut state, mut strategy) = prepare_strategy(); + at_target_block_2_deliver_messages( + &mut strategy, + &mut state, + max_unrewarded_relayer_entries_at_target - 1, + max_unconfirmed_nonces_at_target, + ); + at_source_block_2_deliver_confirmations(&mut strategy, &mut state); + assert_eq!( + strategy.required_source_header_at_target(state.clone()).await, + Some(header_id(2)) + ); + assert_eq!( + at_target_block_3_select_nonces_to_deliver(&strategy, state).await, + expected_rewards_proof + ); + + // when lane is blocked by no-message-slots and no-message-slots in unrewarded relayers + // vector + let (mut state, mut strategy) = prepare_strategy(); + at_target_block_2_deliver_messages( + &mut strategy, + &mut state, + max_unrewarded_relayer_entries_at_target - 1, + max_unconfirmed_nonces_at_target, + ); + at_source_block_2_deliver_confirmations(&mut strategy, &mut state); + assert_eq!( + strategy.required_source_header_at_target(state.clone()).await, + Some(header_id(2)) + ); + assert_eq!( + at_target_block_3_select_nonces_to_deliver(&strategy, state).await, + expected_rewards_proof + ); + + // when we have already selected some nonces to deliver, we don't need to select anything + let (mut state, mut strategy) = prepare_strategy(); + at_target_block_2_deliver_messages( + &mut strategy, + &mut state, + max_unrewarded_relayer_entries_at_target - 1, + max_unconfirmed_nonces_at_target, + ); + at_source_block_2_deliver_confirmations(&mut strategy, &mut state); + state.nonces_to_submit = Some((header_id(2), 1..=0, (1..=0, None))); + assert_eq!(strategy.required_source_header_at_target(state.clone()).await, None); + assert_eq!(at_target_block_3_select_nonces_to_deliver(&strategy, state).await, None); + + // when we have already submitted some nonces, we don't need to select anything + let (mut state, mut strategy) = prepare_strategy(); + at_target_block_2_deliver_messages( + &mut strategy, + &mut state, + max_unrewarded_relayer_entries_at_target - 1, + max_unconfirmed_nonces_at_target, + ); + at_source_block_2_deliver_confirmations(&mut strategy, &mut state); + state.nonces_submitted = Some(1..=0); + assert_eq!(strategy.required_source_header_at_target(state.clone()).await, None); + assert_eq!(at_target_block_3_select_nonces_to_deliver(&strategy, state).await, None); + } + + #[async_std::test] + async fn outbound_state_proof_is_not_required_when_we_have_no_new_confirmations() { + let (mut state, mut strategy) = prepare_strategy(); + + // pretend that we haven't seen any confirmations yet (or they're at the future target chain + // blocks) + strategy.latest_confirmed_nonces_at_source.clear(); + + // emulate delivery of some nonces (20..=23 are generated, but we only deliver 20..=21) + let nonces_at_target = TargetClientNonces { + latest_nonce: 21, + nonces_data: DeliveryRaceTargetNoncesData { + confirmed_nonce: 19, + unrewarded_relayers: UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + total_messages: 2, + ..Default::default() + }, + }, + }; + state.best_target_header_id = Some(header_id(2)); + state.best_finalized_target_header_id = Some(header_id(2)); + strategy.best_target_nonces_updated(nonces_at_target.clone(), &mut state); + strategy.finalized_target_nonces_updated(nonces_at_target, &mut state); + + // we won't include outbound lane state proof into 22..=23 delivery transaction + // because it brings no new reward confirmations + assert_eq!( + strategy.select_nonces_to_deliver(state).await, + Some(((22..=23), proof_parameters(false, 2))) + ); + } } diff --git a/relays/messages/src/message_race_loop.rs b/relays/messages/src/message_race_loop.rs index 7e3f84dd5d1..3df9eef4960 100644 --- a/relays/messages/src/message_race_loop.rs +++ b/relays/messages/src/message_race_loop.rs @@ -41,14 +41,14 @@ use std::{ /// One of races within lane. pub trait MessageRace { /// Header id of the race source. - type SourceHeaderId: Debug + Clone + PartialEq + Send; + type SourceHeaderId: Debug + Clone + PartialEq + Send + Sync; /// Header id of the race source. - type TargetHeaderId: Debug + Clone + PartialEq + Send; + type TargetHeaderId: Debug + Clone + PartialEq + Send + Sync; /// Message nonce used in the race. type MessageNonce: Debug + Clone; /// Proof that is generated and delivered in this race. - type Proof: Debug + Clone + Send; + type Proof: Debug + Clone + Send + Sync; /// Name of the race source. fn source_name() -> String; @@ -175,9 +175,8 @@ pub trait RaceStrategy: Debug { /// Should return true if nothing has to be synced. fn is_empty(&self) -> bool; /// Return id of source header that is required to be on target to continue synchronization. - fn required_source_header_at_target>( + async fn required_source_header_at_target>( &self, - current_best: &SourceHeaderId, race_state: RS, ) -> Option; /// Return the best nonce at source node. @@ -196,6 +195,9 @@ pub trait RaceStrategy: Debug { at_block: SourceHeaderId, nonces: SourceClientNonces, ); + /// Called when we want to wait until next `best_target_nonces_updated` before selecting + /// any nonces for delivery. + fn reset_best_target_nonces(&mut self); /// Called when best nonces are updated at target node of the race. fn best_target_nonces_updated>( &mut self, @@ -218,7 +220,11 @@ pub trait RaceStrategy: Debug { } /// State of the race. -pub trait RaceState: Send { +pub trait RaceState: Clone + Send + Sync { + /// Set best finalized source header id at the best block on the target + /// client (at the `best_finalized_source_header_id_at_best_target`). + fn set_best_finalized_source_header_id_at_best_target(&mut self, id: SourceHeaderId); + /// Best finalized source header id at the source client. fn best_finalized_source_header_id_at_source(&self) -> Option; /// Best finalized source header id at the best block on the target @@ -281,11 +287,15 @@ impl Default impl RaceState for RaceStateImpl where - SourceHeaderId: Clone + Send, - TargetHeaderId: Clone + Send, - Proof: Clone + Send, - BatchTx: Clone + Send, + SourceHeaderId: Clone + Send + Sync, + TargetHeaderId: Clone + Send + Sync, + Proof: Clone + Send + Sync, + BatchTx: Clone + Send + Sync, { + fn set_best_finalized_source_header_id_at_best_target(&mut self, id: SourceHeaderId) { + self.best_finalized_source_header_id_at_best_target = Some(id); + } + fn best_finalized_source_header_id_at_source(&self) -> Option { self.best_finalized_source_header_id_at_source.clone() } @@ -430,10 +440,9 @@ pub async fn run, TC: TargetClient

>( ).fail_if_connection_error(FailedClient::Source)?; // ask for more headers if we have nonces to deliver and required headers are missing - source_required_header = race_state - .best_finalized_source_header_id_at_best_target - .as_ref() - .and_then(|best| strategy.required_source_header_at_target(best, race_state.clone())); + source_required_header = strategy + .required_source_header_at_target(race_state.clone()) + .await; }, nonces = target_best_nonces => { target_best_nonces_required = false; @@ -536,14 +545,22 @@ pub async fn run, TC: TargetClient

>( P::target_name(), ); - race_state.reset_nonces_to_submit(); race_state.nonces_submitted = Some(artifacts.nonces); target_tx_tracker.set(artifacts.tx_tracker.wait().fuse()); }, &mut target_go_offline_future, async_std::task::sleep, || format!("Error submitting proof {}", P::target_name()), - ).fail_if_error(FailedClient::Target).map(|_| true)?; + ).fail_if_connection_error(FailedClient::Target)?; + + // in any case - we don't need to retry submitting the same nonces again until + // we read nonces from the target client + race_state.reset_nonces_to_submit(); + // if we have failed to submit transaction AND that is not the connection issue, + // then we need to read best target nonces before selecting nonces again + if !target_client_is_online { + strategy.reset_best_target_nonces(); + } }, target_transaction_status = target_tx_tracker => { match (target_transaction_status, race_state.nonces_submitted.as_ref()) { @@ -693,7 +710,13 @@ pub async fn run, TC: TargetClient

>( .fuse(), ); } else if let Some(source_required_header) = source_required_header.clone() { - log::debug!(target: "bridge", "Going to require {} header {:?} at {}", P::source_name(), source_required_header, P::target_name()); + log::debug!( + target: "bridge", + "Going to require {} header {:?} at {}", + P::source_name(), + source_required_header, + P::target_name(), + ); target_require_source_header .set(race_target.require_source_header(source_required_header).fuse()); } else if target_best_nonces_required { diff --git a/relays/messages/src/message_race_strategy.rs b/relays/messages/src/message_race_strategy.rs index e6016448c95..5c8f9a162b4 100644 --- a/relays/messages/src/message_race_strategy.rs +++ b/relays/messages/src/message_race_strategy.rs @@ -205,16 +205,16 @@ impl< self.source_queue.is_empty() } - fn required_source_header_at_target< + async fn required_source_header_at_target< RS: RaceState< HeaderId, HeaderId, >, >( &self, - current_best: &HeaderId, - _race_state: RS, + race_state: RS, ) -> Option> { + let current_best = race_state.best_finalized_source_header_id_at_best_target()?; self.source_queue .back() .and_then(|(h, _)| if h.0 > current_best.0 { Some(h.clone()) } else { None }) @@ -254,6 +254,10 @@ impl< ) } + fn reset_best_target_nonces(&mut self) { + self.best_target_nonce = None; + } + fn best_target_nonces_updated< RS: RaceState< HeaderId, @@ -266,19 +270,37 @@ impl< ) { let nonce = nonces.latest_nonce; + // if **some** of nonces that we have selected to submit already present at the + // target chain => select new nonces let need_to_select_new_nonces = race_state .nonces_to_submit() - .map(|nonces| *nonces.end() <= nonce) + .map(|nonces| nonce >= *nonces.start()) .unwrap_or(false); if need_to_select_new_nonces { + log::trace!( + target: "bridge", + "Latest nonce at target is {}. Clearing nonces to submit: {:?}", + nonce, + race_state.nonces_to_submit(), + ); + race_state.reset_nonces_to_submit(); } + // if **some** of nonces that we have submitted already present at the + // target chain => select new nonces let need_new_nonces_to_submit = race_state .nonces_submitted() - .map(|nonces| *nonces.end() <= nonce) + .map(|nonces| nonce >= *nonces.start()) .unwrap_or(false); if need_new_nonces_to_submit { + log::trace!( + target: "bridge", + "Latest nonce at target is {}. Clearing submitted nonces: {:?}", + nonce, + race_state.nonces_submitted(), + ); + race_state.reset_nonces_submitted(); } @@ -415,10 +437,15 @@ mod tests { let mut state = TestRaceStateImpl::default(); let mut strategy = BasicStrategy::::new(); state.nonces_to_submit = Some((header_id(1), 5..=10, (5..=10, None))); - strategy.best_target_nonces_updated(target_nonces(7), &mut state); + // we are going to submit 5..=10, so having latest nonce 4 at target is fine + strategy.best_target_nonces_updated(target_nonces(4), &mut state); assert!(state.nonces_to_submit.is_some()); - strategy.best_target_nonces_updated(target_nonces(10), &mut state); - assert!(state.nonces_to_submit.is_none()); + // any nonce larger than 4 invalidates the `nonces_to_submit` + for nonce in 5..=11 { + state.nonces_to_submit = Some((header_id(1), 5..=10, (5..=10, None))); + strategy.best_target_nonces_updated(target_nonces(nonce), &mut state); + assert!(state.nonces_to_submit.is_none()); + } } #[test] @@ -426,10 +453,15 @@ mod tests { let mut state = TestRaceStateImpl::default(); let mut strategy = BasicStrategy::::new(); state.nonces_submitted = Some(5..=10); - strategy.best_target_nonces_updated(target_nonces(7), &mut state); + // we have submitted 5..=10, so having latest nonce 4 at target is fine + strategy.best_target_nonces_updated(target_nonces(4), &mut state); assert!(state.nonces_submitted.is_some()); - strategy.best_target_nonces_updated(target_nonces(10), &mut state); - assert!(state.nonces_submitted.is_none()); + // any nonce larger than 4 invalidates the `nonces_submitted` + for nonce in 5..=11 { + state.nonces_submitted = Some(5..=10); + strategy.best_target_nonces_updated(target_nonces(nonce), &mut state); + assert!(state.nonces_submitted.is_none()); + } } #[async_std::test] diff --git a/tools/runtime-codegen/Cargo.lock b/tools/runtime-codegen/Cargo.lock index 46093b70727..8b103a9b944 100644 --- a/tools/runtime-codegen/Cargo.lock +++ b/tools/runtime-codegen/Cargo.lock @@ -42,7 +42,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", "once_cell", "version_check", ] @@ -54,16 +54,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", - "getrandom 0.2.8", + "getrandom 0.2.9", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] @@ -86,6 +86,55 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstream" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6342bd4f5a1205d7f41e94a41a901f5647c938cdfa96036338e8533c9d6c2450" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "anyhow" version = "1.0.70" @@ -127,13 +176,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.67" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ea188f25f0255d8f92797797c97ebf5631fa88178beb1a46fdf5622c9a00e4" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] @@ -157,12 +206,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base58" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" - [[package]] name = "base64" version = "0.13.1" @@ -220,6 +263,17 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "blake2b_simd" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.7.3" @@ -238,7 +292,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -247,7 +301,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -271,11 +325,17 @@ dependencies = [ "serde", ] +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" [[package]] name = "byte-slice-cast" @@ -330,39 +390,46 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.13" +version = "4.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c911b090850d79fc64fe9ea01e28e465f65e821e08813ced95bced72f7a8a9b" +checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62" dependencies = [ - "bitflags", + "clap_builder", "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" +dependencies = [ + "anstream", + "anstyle", + "bitflags", "clap_lex", - "is-terminal", "once_cell", "strsim", - "termcolor", ] [[package]] name = "clap_derive" -version = "4.1.12" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a932373bab67b984c790ddf2c9ca295d8e3af3b7ef92de5a5bacdccdee4b09b" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] name = "clap_lex" -version = "0.3.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" -dependencies = [ - "os_str_bytes", -] +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] name = "codespan-reporting" @@ -401,6 +468,18 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "constant_time_eq" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" + [[package]] name = "core-foundation" version = "0.9.3" @@ -413,9 +492,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpp_demangle" @@ -428,9 +507,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] @@ -545,9 +624,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -598,7 +677,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "typenum", ] @@ -608,7 +687,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle", ] @@ -618,7 +697,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle", ] @@ -650,9 +729,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c00419335c41018365ddf7e4d5f1c12ee3659ddcf3e01974650ba1de73d038" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" dependencies = [ "cc", "cxxbridge-flags", @@ -662,9 +741,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8307ad413a98fff033c8545ecf133e3257747b3bae935e7602aab8aa92d4ca" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" dependencies = [ "cc", "codespan-reporting", @@ -672,24 +751,24 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] name = "cxxbridge-flags" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc52e2eb08915cb12596d29d55f0b5384f00d697a646dbd269b6ecb0fbd9d31" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" [[package]] name = "cxxbridge-macro" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] @@ -753,7 +832,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -883,13 +962,13 @@ checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -908,6 +987,19 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "expander" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "eyre" version = "0.6.8" @@ -969,9 +1061,9 @@ dependencies = [ [[package]] name = "frame-metadata" -version = "15.0.0" +version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" +checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" dependencies = [ "cfg-if", "parity-scale-codec", @@ -979,6 +1071,12 @@ dependencies = [ "serde", ] +[[package]] +name = "fs-err" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" + [[package]] name = "funty" version = "2.0.0" @@ -987,9 +1085,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -1002,9 +1100,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -1012,15 +1110,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -1030,32 +1128,32 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "futures-sink" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-timer" @@ -1069,9 +1167,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -1105,9 +1203,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1126,9 +1224,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", @@ -1199,9 +1297,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -1218,9 +1316,9 @@ dependencies = [ [[package]] name = "hash-db" -version = "0.15.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" +checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" [[package]] name = "hash256-std-hasher" @@ -1312,7 +1410,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array 0.14.6", + "generic-array 0.14.7", "hmac 0.8.1", ] @@ -1358,9 +1456,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.25" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -1389,18 +1487,33 @@ dependencies = [ "http", "hyper", "log", - "rustls", + "rustls 0.20.8", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.23.4", + "webpki-roots 0.22.6", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" +dependencies = [ + "http", + "hyper", + "log", + "rustls 0.21.0", "rustls-native-certs", "tokio", - "tokio-rustls", - "webpki-roots", + "tokio-rustls 0.24.0", ] [[package]] name = "iana-time-zone" -version = "0.1.54" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1473,9 +1586,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", @@ -1493,25 +1606,25 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "is-terminal" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix", - "windows-sys 0.45.0", + "rustix 0.37.14", + "windows-sys 0.48.0", ] [[package]] @@ -1553,21 +1666,22 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" dependencies = [ - "jsonrpsee-client-transport 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpsee-core 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpsee-http-client 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpsee-types 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpsee-client-transport 0.16.2", + "jsonrpsee-core 0.16.2", + "jsonrpsee-http-client 0.16.2", + "jsonrpsee-types 0.16.2", ] [[package]] name = "jsonrpsee" -version = "0.16.2" -source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b971ce0f6cd1521ede485afc564b95b2c8e7079b9da41d4273bd9b55140a55d" dependencies = [ - "jsonrpsee-client-transport 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", - "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", - "jsonrpsee-http-client 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", - "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-client-transport 0.17.1", + "jsonrpsee-core 0.17.1", + "jsonrpsee-http-client 0.17.1", + "jsonrpsee-types 0.17.1", "jsonrpsee-wasm-client", "jsonrpsee-ws-client", ] @@ -1580,38 +1694,39 @@ checksum = "965de52763f2004bc91ac5bcec504192440f0b568a5d621c59d9dbd6f886c3fb" dependencies = [ "futures-util", "http", - "jsonrpsee-core 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpsee-types 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpsee-core 0.16.2", + "jsonrpsee-types 0.16.2", "pin-project", "rustls-native-certs", "soketto", "thiserror", "tokio", - "tokio-rustls", + "tokio-rustls 0.23.4", "tokio-util", "tracing", - "webpki-roots", + "webpki-roots 0.22.6", ] [[package]] name = "jsonrpsee-client-transport" -version = "0.16.2" -source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca00d975eda834826b04ad57d4e690c67439bb51b02eb0f8b7e4c30fcef8ab9" dependencies = [ "futures-channel", "futures-util", "gloo-net", "http", - "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-core 0.17.1", "pin-project", "rustls-native-certs", "soketto", "thiserror", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.0", "tokio-util", "tracing", - "webpki-roots", + "webpki-roots 0.23.0", ] [[package]] @@ -1628,7 +1743,7 @@ dependencies = [ "futures-timer", "futures-util", "hyper", - "jsonrpsee-types 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpsee-types 0.16.2", "rustc-hash", "serde", "serde_json", @@ -1639,8 +1754,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.16.2" -source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b83cca7a5a7899eed8b2935d5f755c8c4052ad66ab5b328bd34ac2b3ffd3515f" dependencies = [ "anyhow", "async-lock", @@ -1649,7 +1765,7 @@ dependencies = [ "futures-timer", "futures-util", "hyper", - "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-types 0.17.1", "rustc-hash", "serde", "serde_json", @@ -1668,9 +1784,9 @@ checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad" dependencies = [ "async-trait", "hyper", - "hyper-rustls", - "jsonrpsee-core 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpsee-types 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-rustls 0.23.2", + "jsonrpsee-core 0.16.2", + "jsonrpsee-types 0.16.2", "rustc-hash", "serde", "serde_json", @@ -1681,14 +1797,15 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.16.2" -source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6483fea826f62260a88a132fa750a47b40d4218d41e3391936579533c6c67509" dependencies = [ "async-trait", "hyper", - "hyper-rustls", - "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", - "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "hyper-rustls 0.24.0", + "jsonrpsee-core 0.17.1", + "jsonrpsee-types 0.17.1", "serde", "serde_json", "thiserror", @@ -1713,8 +1830,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.16.2" -source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd301ccc3e08718393432d1961539d78c4580dcca86014dfe6769c308b2c08b2" dependencies = [ "anyhow", "beef", @@ -1726,23 +1844,25 @@ dependencies = [ [[package]] name = "jsonrpsee-wasm-client" -version = "0.16.2" -source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85dc3aea8bbb844dacc45cd98d01f624e4485184149a045761888c2e5fa5a0c6" dependencies = [ - "jsonrpsee-client-transport 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", - "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", - "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-client-transport 0.17.1", + "jsonrpsee-core 0.17.1", + "jsonrpsee-types 0.17.1", ] [[package]] name = "jsonrpsee-ws-client" -version = "0.16.2" -source = "git+https://github.com/paritytech/jsonrpsee#7144be544f6fbbb607a5df858b736a2b917edc10" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a69852133d549b07cb37ff2d0ec540eae0d20abb75ae923f5d39bc7536d987" dependencies = [ "http", - "jsonrpsee-client-transport 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", - "jsonrpsee-core 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", - "jsonrpsee-types 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee-client-transport 0.17.1", + "jsonrpsee-core 0.17.1", + "jsonrpsee-types 0.17.1", ] [[package]] @@ -1762,9 +1882,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" [[package]] name = "libm" @@ -1835,6 +1955,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "linux-raw-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" + [[package]] name = "lock_api" version = "0.4.9" @@ -1889,11 +2015,11 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memfd" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" +checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" dependencies = [ - "rustix", + "rustix 0.37.14", ] [[package]] @@ -1916,12 +2042,11 @@ dependencies = [ [[package]] name = "memory-db" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" +checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" dependencies = [ "hash-db", - "hashbrown 0.12.3", ] [[package]] @@ -2076,12 +2201,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" -[[package]] -name = "os_str_bytes" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - [[package]] name = "owo-colors" version = "3.5.0" @@ -2241,35 +2360,11 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -2357,7 +2452,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", ] [[package]] @@ -2406,7 +2501,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.8", + "getrandom 0.2.9", "redox_syscall", "thiserror", ] @@ -2428,7 +2523,7 @@ checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] @@ -2445,13 +2540,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.2" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -2460,7 +2555,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", ] [[package]] @@ -2469,6 +2564,12 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + [[package]] name = "region" version = "3.0.0" @@ -2512,9 +2613,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -2530,18 +2631,32 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustix" -version = "0.36.11" +version = "0.36.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +checksum = "3a38f9520be93aba504e8ca974197f46158de5dcaa9fa04b57c57cd6a679d658" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.1.4", "windows-sys 0.45.0", ] +[[package]] +name = "rustix" +version = "0.37.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.4", + "windows-sys 0.48.0", +] + [[package]] name = "rustls" version = "0.20.8" @@ -2554,6 +2669,18 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustls" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07180898a28ed6a7f7ba2311594308f595e3dd2e3c3812fa0a80a47b45f17e5d" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + [[package]] name = "rustls-native-certs" version = "0.6.2" @@ -2575,6 +2702,22 @@ dependencies = [ "base64 0.21.0", ] +[[package]] +name = "rustls-webpki" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + [[package]] name = "ryu" version = "1.0.13" @@ -2584,7 +2727,7 @@ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "sc-allocator" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "log", "sp-core", @@ -2595,7 +2738,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "lru", "parity-scale-codec", @@ -2619,7 +2762,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "sc-allocator", "sp-maybe-compressed-blob", @@ -2632,7 +2775,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "log", "sc-allocator", @@ -2645,14 +2788,14 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "anyhow", "cfg-if", "libc", "log", "once_cell", - "rustix", + "rustix 0.36.13", "sc-allocator", "sc-executor-common", "sp-runtime-interface", @@ -2662,9 +2805,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61471dff9096de1d8b2319efed7162081e96793f5ebb147e50db10d50d648a4d" +checksum = "0cfdffd972d76b22f3d7f81c8be34b2296afd3a25e0a547bd9abe340a4dbbe97" dependencies = [ "bitvec", "cfg-if", @@ -2676,9 +2819,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219580e803a66b3f05761fd06f1f879a872444e49ce23f73694d26e5a954c7e6" +checksum = "61fa974aea2d63dd18a4ec3a49d59af9f34178c73a4f56d2f18205628d00681e" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2804,29 +2947,29 @@ checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" [[package]] name = "serde" -version = "1.0.158" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.158" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -2884,9 +3027,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" dependencies = [ "digest 0.10.6", "keccak", @@ -2965,7 +3108,7 @@ dependencies = [ [[package]] name = "sp-api" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "hash-db", "log", @@ -2983,9 +3126,11 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ + "Inflector", "blake2", + "expander", "proc-macro-crate", "proc-macro2", "quote", @@ -2995,7 +3140,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "parity-scale-codec", "scale-info", @@ -3008,7 +3153,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "integer-sqrt", "num-traits", @@ -3022,13 +3167,13 @@ dependencies = [ [[package]] name = "sp-core" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "array-bytes", - "base58", "bitflags", "blake2", "bounded-collections", + "bs58", "dyn-clonable", "ed25519-zebra", "futures", @@ -3065,9 +3210,9 @@ dependencies = [ [[package]] name = "sp-core-hashing" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ - "blake2", + "blake2b_simd", "byteorder", "digest 0.10.6", "sha2 0.10.6", @@ -3078,23 +3223,23 @@ dependencies = [ [[package]] name = "sp-core-hashing" -version = "6.0.0" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc2d1947252b7a4e403b0a260f596920443742791765ec111daa2bbf98eff25" +checksum = "27449abdfbe41b473e625bce8113745e81d65777dd1d5a8462cf24137930dad8" dependencies = [ - "blake2", + "blake2b_simd", "byteorder", "digest 0.10.6", "sha2 0.10.6", "sha3", - "sp-std 6.0.0", + "sp-std 7.0.0", "twox-hash", ] [[package]] name = "sp-core-hashing-proc-macro" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "proc-macro2", "quote", @@ -3105,7 +3250,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "proc-macro2", "quote", @@ -3115,7 +3260,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "environmental", "parity-scale-codec", @@ -3126,7 +3271,7 @@ dependencies = [ [[package]] name = "sp-io" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "bytes", "ed25519", @@ -3135,6 +3280,7 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", + "rustversion", "secp256k1", "sp-core", "sp-externalities", @@ -3151,9 +3297,8 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ - "async-trait", "futures", "merlin", "parity-scale-codec", @@ -3167,7 +3312,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "4.1.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "thiserror", "zstd", @@ -3176,7 +3321,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "backtrace", "lazy_static", @@ -3186,7 +3331,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "either", "hash256-std-hasher", @@ -3208,7 +3353,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -3226,7 +3371,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "Inflector", "proc-macro-crate", @@ -3238,7 +3383,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "hash-db", "log", @@ -3258,18 +3403,18 @@ dependencies = [ [[package]] name = "sp-std" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" [[package]] name = "sp-std" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af0ee286f98455272f64ac5bb1384ff21ac029fbb669afbaf48477faff12760e" +checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986" [[package]] name = "sp-storage" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "impl-serde", "parity-scale-codec", @@ -3282,7 +3427,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "parity-scale-codec", "sp-std 5.0.0", @@ -3294,11 +3439,11 @@ dependencies = [ [[package]] name = "sp-trie" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "ahash 0.8.3", "hash-db", - "hashbrown 0.12.3", + "hashbrown 0.13.2", "lazy_static", "memory-db", "nohash-hasher", @@ -3317,7 +3462,7 @@ dependencies = [ [[package]] name = "sp-version" version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "impl-serde", "parity-scale-codec", @@ -3334,7 +3479,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -3345,7 +3490,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -3359,7 +3504,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-03#ad5399644aebc54e32a107ac37ae08e6cd1f0cfb" +source = "git+https://github.com/paritytech/substrate?tag=monthly-2023-04#75dee3c6fc72f6399a43f498e0e42f94fb82a7bd" dependencies = [ "parity-scale-codec", "scale-info", @@ -3425,8 +3570,8 @@ dependencies = [ [[package]] name = "substrate-runtime-proposal-hash" -version = "0.19.0" -source = "git+https://github.com/chevdor/subwasm?branch=master#fd047d76d52beab8c062eb94141f8d14ddc0f59d" +version = "0.19.1" +source = "git+https://github.com/chevdor/subwasm?branch=master#04fd6218fb41c29d04b21db7e821810b25664d9b" dependencies = [ "blake2", "hex", @@ -3445,35 +3590,33 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "subxt-codegen" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e86cb719003f1cedf2710a6e55ca4c37aba4c989bbd3b81dd1c52af9e4827e" +version = "0.28.0" +source = "git+https://github.com/paritytech/subxt?branch=master#e583aa987eb57ac6ce70c7166925189a663d0d31" dependencies = [ "darling", "frame-metadata", "heck", "hex", - "jsonrpsee 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpsee 0.16.2", "parity-scale-codec", - "proc-macro-error", "proc-macro2", "quote", "scale-info", "subxt-metadata", "syn 1.0.109", + "thiserror", "tokio", ] [[package]] name = "subxt-metadata" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2593ab5f53435e6352675af4f9851342607f37785d84c7a3fb3139550d3c35f0" +version = "0.28.0" +source = "git+https://github.com/paritytech/subxt?branch=master#e583aa987eb57ac6ce70c7166925189a663d0d31" dependencies = [ "frame-metadata", "parity-scale-codec", "scale-info", - "sp-core-hashing 6.0.0", + "sp-core-hashing 8.0.0", ] [[package]] @@ -3489,27 +3632,15 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.8" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - [[package]] name = "tap" version = "1.0.1" @@ -3518,9 +3649,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.6" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" +checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" [[package]] name = "termcolor" @@ -3548,7 +3679,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] @@ -3597,14 +3728,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.26.0" +version = "1.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot", @@ -3612,18 +3742,18 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -3632,16 +3762,26 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls", + "rustls 0.20.8", "tokio", "webpki", ] +[[package]] +name = "tokio-rustls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +dependencies = [ + "rustls 0.21.0", + "tokio", +] + [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "76cd2598a37719e3cd4c28af93f978506a97a2920ef4d96e4b12e38b8cbc8940" dependencies = [ "futures-core", "pin-project-lite", @@ -3650,9 +3790,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -3718,11 +3858,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -3731,13 +3870,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -3757,7 +3896,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" dependencies = [ "tracing", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", ] [[package]] @@ -3805,9 +3944,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "sharded-slab", "thread_local", @@ -3816,9 +3955,9 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.25.1" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3390c0409daaa6027d6681393316f4ccd3ff82e1590a1e4725014e3ae2bf1920" +checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" dependencies = [ "hash-db", "hashbrown 0.13.2", @@ -3829,9 +3968,9 @@ dependencies = [ [[package]] name = "trie-root" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a36c5ca3911ed3c9a5416ee6c679042064b93fc637ded67e25f92e68d783891" +checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" dependencies = [ "hash-db", ] @@ -3922,6 +4061,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "valuable" version = "0.1.0" @@ -4033,11 +4178,11 @@ dependencies = [ [[package]] name = "wasm-loader" -version = "0.19.0" -source = "git+https://github.com/chevdor/subwasm?branch=master#fd047d76d52beab8c062eb94141f8d14ddc0f59d" +version = "0.19.1" +source = "git+https://github.com/chevdor/subwasm?branch=master#04fd6218fb41c29d04b21db7e821810b25664d9b" dependencies = [ "hex", - "jsonrpsee 0.16.2 (git+https://github.com/paritytech/jsonrpsee)", + "jsonrpsee 0.17.1", "log", "serde", "sp-maybe-compressed-blob", @@ -4046,8 +4191,8 @@ dependencies = [ [[package]] name = "wasm-testbed" -version = "0.19.0" -source = "git+https://github.com/chevdor/subwasm?branch=master#fd047d76d52beab8c062eb94141f8d14ddc0f59d" +version = "0.19.1" +source = "git+https://github.com/chevdor/subwasm?branch=master#04fd6218fb41c29d04b21db7e821810b25664d9b" dependencies = [ "frame-metadata", "hex", @@ -4158,7 +4303,7 @@ dependencies = [ "directories-next", "file-per-thread-logger", "log", - "rustix", + "rustix 0.36.13", "serde", "sha2 0.10.6", "toml", @@ -4238,7 +4383,7 @@ checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" dependencies = [ "object 0.29.0", "once_cell", - "rustix", + "rustix 0.36.13", ] [[package]] @@ -4269,7 +4414,7 @@ dependencies = [ "memoffset 0.6.5", "paste", "rand 0.8.5", - "rustix", + "rustix 0.36.13", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-jit-debug", @@ -4317,6 +4462,15 @@ dependencies = [ "webpki", ] +[[package]] +name = "webpki-roots" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa54963694b65584e170cf5dc46aeb4dcaa5584e652ff5f3952e56d66aff0125" +dependencies = [ + "rustls-webpki", +] + [[package]] name = "winapi" version = "0.3.9" @@ -4350,11 +4504,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.46.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets", + "windows-targets 0.48.0", ] [[package]] @@ -4363,13 +4517,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -4378,7 +4532,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -4387,13 +4550,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -4402,47 +4580,89 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winnow" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deac0939bd6e4f24ab5919fbf751c97a8cfc8543bb083a305ed5c0c10bb241d1" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" dependencies = [ "memchr", ] @@ -4458,23 +4678,22 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.3.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", - "synstructure", + "syn 2.0.15", ] [[package]] @@ -4498,9 +4717,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.7+zstd.1.5.4" +version = "2.0.8+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" dependencies = [ "cc", "libc", diff --git a/tools/runtime-codegen/Cargo.toml b/tools/runtime-codegen/Cargo.toml index 5eea4cdbc88..90af4b3a415 100644 --- a/tools/runtime-codegen/Cargo.toml +++ b/tools/runtime-codegen/Cargo.toml @@ -14,7 +14,7 @@ clap = { version = "4.0.8", features = ["derive", "cargo"] } codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } color-eyre = "0.6.1" proc-macro2 = "1.0.51" -subxt-codegen = "0.27.1" syn = "1.0" +subxt-codegen = { git = "https://github.com/paritytech/subxt", branch = "master", default-features = false, features = [] } wasm-loader = { git = "https://github.com/chevdor/subwasm", branch = "master" } -wasm-testbed = { git = "https://github.com/chevdor/subwasm", branch = "master" } \ No newline at end of file +wasm-testbed = { git = "https://github.com/chevdor/subwasm", branch = "master" } diff --git a/tools/runtime-codegen/src/main.rs b/tools/runtime-codegen/src/main.rs index e1aa142e558..a91a733da0b 100644 --- a/tools/runtime-codegen/src/main.rs +++ b/tools/runtime-codegen/src/main.rs @@ -112,12 +112,21 @@ fn main() -> color_eyre::Result<()> { pub mod api {} ); // Default module derivatives. - let mut derives = DerivesRegistry::new(&CratePath::default()); - derives.extend_for_all(vec![syn::parse_quote!(Clone)]); + let mut derives = DerivesRegistry::new(); + derives.extend_for_all( + vec![ + syn::parse_quote!(::codec::Encode), + syn::parse_quote!(::codec::Decode), + syn::parse_quote!(Clone), + syn::parse_quote!(Debug), + ], + vec![], + ); // Type substitutes let mut type_substitutes = TypeSubstitutes::new(&CratePath::default()); - type_substitutes.extend( - vec![ + type_substitutes + .extend( + vec![ TypeSubstitute::simple("sp_core::crypto::AccountId32"), TypeSubstitute::custom("bp_millau::millau_hash::MillauHash", "::bp_millau::MillauHash"), TypeSubstitute::simple("bp_millau::BlakeTwoAndKeccak256"), @@ -141,9 +150,10 @@ fn main() -> color_eyre::Result<()> { ), TypeSubstitute::simple("bp_messages::UnrewardedRelayersState"), ] - .drain(..) - .map(|substitute| (substitute.subxt_type, substitute.substitute.try_into().unwrap())), - ); + .drain(..) + .map(|substitute| (substitute.subxt_type, substitute.substitute.try_into().unwrap())), + ) + .map_err(|e| eyre::eyre!("Error extending type substitutes: {:?}", e))?; // Generate the Runtime API. let runtime_api = match metadata_source { @@ -153,6 +163,8 @@ fn main() -> color_eyre::Result<()> { derives, type_substitutes, CratePath::default(), + false, + true, ), RuntimeMetadataSource::WasmFile(source) => { let testbed = WasmTestBed::new(&source) @@ -163,9 +175,12 @@ fn main() -> color_eyre::Result<()> { derives, type_substitutes, CratePath::default(), + false, + true, ) }, - }; + } + .map_err(|e| eyre::eyre!("Error generating runtime api: {:?}", e))?; print_runtime(runtime_api); From a7b7f39d7915a25bbb17c378a292d7ec4cede0ca Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 27 Apr 2023 11:23:46 +0200 Subject: [PATCH 249/263] Fmt --- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 9821b910199..2ebb3943545 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -490,10 +490,8 @@ impl pallet_bridge_messages::Config for Run >; type SourceHeaderChain = SourceHeaderChainAdapter; - type MessageDispatch = XcmBlobMessageDispatch< - OnBridgeHubRococoBlobDispatcher, - Self::WeightInfo, - >; + type MessageDispatch = + XcmBlobMessageDispatch; } /// Add XCM messages support for BrigdeHubWococo to support Wococo->Rococo XCM messages @@ -525,10 +523,8 @@ impl pallet_bridge_messages::Config for Run >; type SourceHeaderChain = SourceHeaderChainAdapter; - type MessageDispatch = XcmBlobMessageDispatch< - OnBridgeHubWococoBlobDispatcher, - Self::WeightInfo, - >; + type MessageDispatch = + XcmBlobMessageDispatch; } /// Allows collect and claim rewards for relayers From fd69d9414cdc2586be3e26d7599dc50b587c7428 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 27 Apr 2023 12:09:11 +0200 Subject: [PATCH 250/263] Squashed 'bridges/' changes from 4850aac8ce..66aaf0dd23 66aaf0dd23 Nits (#2083) git-subtree-dir: bridges git-subtree-split: 66aaf0dd239dde40b64264061a77c921e2c82568 --- bin/runtime-common/Cargo.toml | 2 -- primitives/relayers/src/registration.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/bin/runtime-common/Cargo.toml b/bin/runtime-common/Cargo.toml index e7cd39da90b..3db4ae9abca 100644 --- a/bin/runtime-common/Cargo.toml +++ b/bin/runtime-common/Cargo.toml @@ -30,7 +30,6 @@ pallet-bridge-relayers = { path = "../../modules/relayers", default-features = f frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-utility = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -63,7 +62,6 @@ std = [ "frame-system/std", "hash-db/std", "log/std", - "pallet-balances/std", "pallet-bridge-grandpa/std", "pallet-bridge-messages/std", "pallet-bridge-parachains/std", diff --git a/primitives/relayers/src/registration.rs b/primitives/relayers/src/registration.rs index da64bdde379..7ab20844bdf 100644 --- a/primitives/relayers/src/registration.rs +++ b/primitives/relayers/src/registration.rs @@ -21,7 +21,7 @@ //! required finality proofs). This extension boosts priority of message delivery //! transactions, based on the number of bundled messages. So transaction with more //! messages has larger priority than the transaction with less messages. -//! See [`bridge_runtime_common::priority_calculator`] for details; +//! See `bridge_runtime_common::priority_calculator` for details; //! //! This encourages relayers to include more messages to their delivery transactions. //! At the same time, we are not verifying storage proofs before boosting From 6c5bec589b89489fe85fb774761a9fa2d544b9af Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 27 Apr 2023 14:32:32 +0200 Subject: [PATCH 251/263] Cleaning --- README.md | 6 ------ parachains/runtimes/bridge-hubs/README.md | 7 +++---- .../bridge-hub-rococo/src/bridge_hub_rococo_config.rs | 1 - .../bridge-hub-rococo/src/bridge_hub_wococo_config.rs | 1 - .../runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 5 ----- .../bridge-hubs/bridge-hub-rococo/src/xcm_config.rs | 6 +++--- 6 files changed, 6 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index a1c2b210b11..d1828eafce1 100644 --- a/README.md +++ b/README.md @@ -199,12 +199,6 @@ See [the `contracts-rococo` readme](parachains/runtimes/contracts/contracts-roco See [the `bridge-hubs` readme](parachains/runtimes/bridge-hubs/README.md) for details. -**_Important:_** - -BridgeHub stuff uses external dependencies from repo `https://github.com/paritytech/parity-bridges-common.git`, which are mirrored to `./bridges` directory with `git subtree` feature. - -See [readme](parachains/runtimes/bridge-hubs/README.md#git-subtree-bridges) for details - ## Rococo 👑 [Rococo](https://polkadot.js.org/apps/?rpc=wss://rococo-rpc.polkadot.io) is becoming a diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index cc9c76d75a7..35b64e6667b 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -10,9 +10,6 @@ - [Live Rockmine2 to Wockmint](#live-rockmine2-to-wockmint) * [How to test local BridgeHubKusama](#how-to-test-local-bridgehubkusama) * [How to test local BridgeHubPolkadot](#how-to-test-local-bridgehubpolkadot) - * [Git subtree `./bridges`](#git-subtree---bridges-) - + [How to update `bridges` subtree](#how-to-update--bridges--subtree) - + [How was first time initialized (dont need anymore)](#how-was-first-time-initialized--dont-need-anymore-) # Bridge-hub Parachains @@ -49,7 +46,9 @@ cp target/release/polkadot ~/local_bridge_testing/bin/polkadot # 3. Build cumulus polkadot-parachain binary cd -git checkout -b bridge-hub-rococo-wococo --track origin/bridge-hub-rococo-wococo +# checkout desired branch or use master: +# git checkout -b bridge-hub-rococo-wococo --track origin/bridge-hub-rococo-wococo +git checkout -b master --track origin/master cargo build --release --locked -p polkadot-parachain-bin cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain cp target/release/polkadot-parachain ~/local_bridge_testing/bin/polkadot-parachain-mint diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index 263310a10e7..aaffbcf7a90 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -40,7 +40,6 @@ use xcm::{ }; use xcm_builder::{BridgeBlobDispatcher, HaulBlobExporter}; -// TODO:check-parameter parameter_types! { pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = bp_bridge_hub_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 5ac9cb13596..8987b8eeb7e 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -40,7 +40,6 @@ use xcm::{ }; use xcm_builder::{BridgeBlobDispatcher, HaulBlobExporter}; -// TODO:check-parameter parameter_types! { pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = bp_bridge_hub_wococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 2ebb3943545..1d7d423fc9b 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -428,7 +428,6 @@ parameter_types! { pub const MaxRococoParaHeadDataSize: u32 = bp_rococo::MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE; pub const MaxWococoParaHeadDataSize: u32 = bp_wococo::MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE; - // TODO:check-parameter - setup initial values https://github.com/paritytech/parity-bridges-common/issues/1677 pub storage DeliveryRewardInBalance: u64 = 1_000_000; pub storage RequiredStakeForStakeAndSlash: Balance = 1_000_000; @@ -578,7 +577,6 @@ construct_runtime!( // Handy utilities. Utility: pallet_utility::{Pallet, Call, Event} = 40, - // TODO:check-parameter - change back to 41 a align bridge pallets Multisig: pallet_multisig::{Pallet, Call, Storage, Event} = 36, // Rococo and Wococo Bridge Hubs are sharing the runtime, so this runtime has two sets of @@ -1213,9 +1211,6 @@ mod tests { use bridge_hub_test_utils::test_header; use codec::Encode; - // RelayChain header mock representation - // pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; - // pub type RelayBlockHasher = bp_polkadot_core::Hasher; pub type TestBlockHeader = sp_runtime::generic::Header; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index ac38b837be9..91fd15136bb 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -201,7 +201,6 @@ pub type Barrier = DenyThenTry< ( // If the message is one that immediately attemps to pay for execution, then allow it. AllowTopLevelPaidExecutionFrom, - // TODO:check-parameter - add here "or SystemParachains" once merged https://github.com/paritytech/polkadot/pull/7005 // Parent and its pluralities (i.e. governance bodies) get free execution. AllowExplicitUnpaidExecutionFrom, // Subscriptions for version tracking are OK. @@ -210,7 +209,8 @@ pub type Barrier = DenyThenTry< UniversalLocation, ConstU32<8>, >, - // TODO:check-parameter - supporting unpaid execution at first, then SovereignPaid, remove this once https://github.com/paritytech/polkadot/pull/7005 + // TODO:check-parameter - (https://github.com/paritytech/parity-bridges-common/issues/2084) + // remove this and extend `AllowExplicitUnpaidExecutionFrom` with "or SystemParachains" once merged https://github.com/paritytech/polkadot/pull/7005 AllowUnpaidExecutionFrom, ), >; @@ -275,7 +275,7 @@ impl pallet_xcm::Config for Runtime { type SendXcmOrigin = EnsureXcmOrigin; // We support local origins dispatching XCM executions in principle... type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = Everything; + type XcmExecuteFilter = Nothing; type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Nothing; // This parachain is not meant as a reserve location. From 70b426bc7d2711ef01d4bb4c8c481049a2c86bff Mon Sep 17 00:00:00 2001 From: Adrian Catangiu Date: Tue, 2 May 2023 19:19:18 +0300 Subject: [PATCH 252/263] bridge-hub-rococo: minor fixes Signed-off-by: Adrian Catangiu --- parachains/runtimes/bridge-hubs/README.md | 19 +- .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 15 +- .../bridge-hub-rococo/tests/tests.rs | 190 +++++++++--------- .../bridge-hubs/test-utils/src/test_cases.rs | 13 +- 4 files changed, 118 insertions(+), 119 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/README.md b/parachains/runtimes/bridge-hubs/README.md index 35b64e6667b..9b024769195 100644 --- a/parachains/runtimes/bridge-hubs/README.md +++ b/parachains/runtimes/bridge-hubs/README.md @@ -13,14 +13,17 @@ # Bridge-hub Parachains -Implementation of _BridgeHub_, a blockchain to support message passing between Substrate based chains like Polkadot and Kusama networks. - -_BridgeHub_ allows users to: - -- Passing arbitrary messages between different Substrate chains (Polkadot <-> Kusama). - -_BridgeHub_ is meant to be **_system parachain_** with main responsibilities: -- sync finality proofs between relay chains +_BridgeHub(s)_ are **_system parachains_** that will house trustless bridges from the local +ecosystem to others. +The current trustless bridges planned for the BridgeHub(s) are: +- `BridgeHubPolkadot` system parachain: + 1. Polkadot <-> Kusama bridge + 2. Polkadot <-> Ethereum bridge (Snowbridge) +- `BridgeHubKusama` system parachain: + 1. Kusama <-> Polkadot bridge + 2. Kusama <-> Ethereum bridge + The high-level responsibilities of each bridge living on BridgeHub: +- sync finality proofs between relay chains (or equivalent) - sync finality proofs between BridgeHub parachains - pass (XCM) messages between different BridgeHub parachains diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 1d7d423fc9b..f343fd185e6 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -398,7 +398,7 @@ impl pallet_utility::Config for Runtime { // Add bridge pallets (GPA) -/// Add granda bridge pallet to track Wococo relay chain on Rococo BridgeHub +/// Add GRANDPA bridge pallet to track Wococo relay chain on Rococo BridgeHub pub type BridgeGrandpaWococoInstance = pallet_bridge_grandpa::Instance1; impl pallet_bridge_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; @@ -408,7 +408,7 @@ impl pallet_bridge_grandpa::Config for Runtime { type WeightInfo = weights::pallet_bridge_grandpa_bridge_wococo_grandpa::WeightInfo; } -/// Add granda bridge pallet to track Rococo relay chain on Wococo BridgeHub +/// Add GRANDPA bridge pallet to track Rococo relay chain on Wococo BridgeHub pub type BridgeGrandpaRococoInstance = pallet_bridge_grandpa::Instance2; impl pallet_bridge_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; @@ -421,7 +421,7 @@ impl pallet_bridge_grandpa::Config for Runtime { parameter_types! { pub const RelayChainHeadersToKeep: u32 = 1024; pub const ParachainHeadsToKeep: u32 = 64; - pub const MaxRequests: u32 = 64; + pub const RelayerStakeLease: u32 = 8; pub const RococoBridgeParachainPalletName: &'static str = "Paras"; pub const WococoBridgeParachainPalletName: &'static str = "Paras"; @@ -460,7 +460,7 @@ impl pallet_bridge_parachains::Config for Runtime type MaxParaHeadDataSize = MaxRococoParaHeadDataSize; } -/// Add XCM messages support for BrigdeHubRococo to support Rococo->Wococo XCM messages +/// Add XCM messages support for BridgeHubRococo to support Rococo->Wococo XCM messages pub type WithBridgeHubWococoMessagesInstance = pallet_bridge_messages::Instance1; impl pallet_bridge_messages::Config for Runtime { type RuntimeEvent = RuntimeEvent; @@ -493,7 +493,7 @@ impl pallet_bridge_messages::Config for Run XcmBlobMessageDispatch; } -/// Add XCM messages support for BrigdeHubWococo to support Wococo->Rococo XCM messages +/// Add XCM messages support for BridgeHubWococo to support Wococo->Rococo XCM messages pub type WithBridgeHubRococoMessagesInstance = pallet_bridge_messages::Instance2; impl pallet_bridge_messages::Config for Runtime { type RuntimeEvent = RuntimeEvent; @@ -538,7 +538,7 @@ impl pallet_bridge_relayers::Config for Runtime { Balances, RelayerStakeReserveId, RequiredStakeForStakeAndSlash, - ConstU32<8>, + RelayerStakeLease, >; type WeightInfo = weights::pallet_bridge_relayers::WeightInfo; } @@ -616,7 +616,6 @@ mod benches { define_benchmarks!( [frame_system, SystemBench::] [pallet_balances, Balances] - [pallet_session, SessionBench::] [pallet_multisig, Multisig] [pallet_session, SessionBench::] [pallet_utility, Utility] @@ -804,6 +803,7 @@ impl_runtime_apis! { } } + // This exposed by BridgeHubRococo impl bp_bridge_hub_wococo::ToBridgeHubWococoOutboundLaneApi for Runtime { fn message_details( lane: bp_messages::LaneId, @@ -830,6 +830,7 @@ impl_runtime_apis! { } } + // This is exposed by BridgeHubWococo impl bp_bridge_hub_rococo::ToBridgeHubRococoOutboundLaneApi for Runtime { fn message_details( lane: bp_messages::LaneId, diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index 260037629fa..a31171ba609 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -38,99 +38,100 @@ parameter_types! { pub CheckingAccount: AccountId = PolkadotXcm::check_account(); } -bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( - Runtime, - XcmConfig, - CheckingAccount, - WeightToFee, - ParachainSystem, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), - ExistentialDeposit::get(), - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), - _ => None, - } - }), - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), - _ => None, - } - }), - bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID -); - -bridge_hub_test_utils::include_initialize_bridge_by_governance_works!( - initialize_bridge_to_wococo_by_governance_works, - Runtime, - BridgeGrandpaWococoInstance, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), - bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - Box::new(|call| RuntimeCall::BridgeWococoGrandpa(call).encode()) -); - -bridge_hub_test_utils::include_handle_export_message_from_system_parachain_to_outbound_queue_works!( - handle_export_message_from_system_parachain_to_outbound_queue_works_for_wococo, - Runtime, - XcmConfig, - WithBridgeHubWococoMessagesInstance, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), - bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - 1000, - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::BridgeWococoMessages(event)) => Some(event), - _ => None, - } - }), - || ExportMessage { network: Wococo, destination: X1(Parachain(1234)), xcm: Xcm(vec![]) }, - bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO -); - -bridge_hub_test_utils::include_message_dispatch_routing_works!( - message_dispatch_routing_works_for_wococo_messages, - Runtime, - XcmConfig, - ParachainSystem, - WithBridgeHubWococoMessagesInstance, - RelayNetwork, - bridge_hub_rococo_config::WococoGlobalConsensusNetwork, - bridge_hub_test_utils::CollatorSessionKeys::new( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ), - bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - 1000, - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::ParachainSystem(event)) => Some(event), - _ => None, - } - }), - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), - _ => None, - } - }), - bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO -); - -mod bridge_hub_wococo { +mod bridge_hub_rococo_tests { + use super::*; + + bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( + Runtime, + XcmConfig, + CheckingAccount, + WeightToFee, + ParachainSystem, + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID + ); + + bridge_hub_test_utils::include_initialize_bridge_by_governance_works!( + Runtime, + BridgeGrandpaWococoInstance, + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + Box::new(|call| RuntimeCall::BridgeWococoGrandpa(call).encode()) + ); + + bridge_hub_test_utils::include_handle_export_message_from_system_parachain_to_outbound_queue_works!( + Runtime, + XcmConfig, + WithBridgeHubWococoMessagesInstance, + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + 1000, + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::BridgeWococoMessages(event)) => Some(event), + _ => None, + } + }), + || ExportMessage { network: Wococo, destination: X1(Parachain(1234)), xcm: Xcm(vec![]) }, + bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO + ); + + bridge_hub_test_utils::include_message_dispatch_routing_works!( + Runtime, + XcmConfig, + ParachainSystem, + WithBridgeHubWococoMessagesInstance, + RelayNetwork, + bridge_hub_rococo_config::WococoGlobalConsensusNetwork, + bridge_hub_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + 1000, + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::ParachainSystem(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }), + bridge_hub_rococo_config::DEFAULT_XCM_LANE_TO_BRIDGE_HUB_WOCOCO + ); +} + +mod bridge_hub_wococo_tests { use super::*; bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( @@ -161,7 +162,6 @@ mod bridge_hub_wococo { ); bridge_hub_test_utils::include_initialize_bridge_by_governance_works!( - initialize_bridge_to_rococo_by_governance_works, Runtime, BridgeGrandpaRococoInstance, bridge_hub_test_utils::CollatorSessionKeys::new( @@ -174,7 +174,6 @@ mod bridge_hub_wococo { ); bridge_hub_test_utils::include_handle_export_message_from_system_parachain_to_outbound_queue_works!( - handle_export_message_from_system_parachain_to_outbound_queue_works_for_rococo, Runtime, XcmConfig, WithBridgeHubRococoMessagesInstance, @@ -196,7 +195,6 @@ mod bridge_hub_wococo { ); bridge_hub_test_utils::include_message_dispatch_routing_works!( - message_dispatch_routing_works_for_rococo_messages, Runtime, XcmConfig, ParachainSystem, diff --git a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs index 9ea88b30561..5cdc2af9968 100644 --- a/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs +++ b/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs @@ -77,7 +77,7 @@ pub fn initialize_bridge_by_governance_works( Runtime, GrandpaPalletInstance, >::initialize { - init_data: test_data::initialiation_data::(12345), + init_data: test_data::initialization_data::(12345), }); // overestimate - check weight for `pallet_bridge_grandpa::Pallet::initialize()` call @@ -103,7 +103,6 @@ pub fn initialize_bridge_by_governance_works( #[macro_export] macro_rules! include_initialize_bridge_by_governance_works( ( - $test_name:tt, $runtime:path, $pallet_bridge_grandpa_instance:path, $collator_session_key:expr, @@ -111,7 +110,7 @@ macro_rules! include_initialize_bridge_by_governance_works( $runtime_call_encode:expr ) => { #[test] - fn $test_name() { + fn initialize_bridge_by_governance_works() { $crate::test_cases::initialize_bridge_by_governance_works::< $runtime, $pallet_bridge_grandpa_instance, @@ -213,7 +212,6 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< #[macro_export] macro_rules! include_handle_export_message_from_system_parachain_to_outbound_queue_works( ( - $test_name:tt, $runtime:path, $xcm_config:path, $pallet_bridge_messages_instance:path, @@ -225,7 +223,7 @@ macro_rules! include_handle_export_message_from_system_parachain_to_outbound_que $expected_lane_id:expr ) => { #[test] - fn $test_name() { + fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() { $crate::test_cases::handle_export_message_from_system_parachain_to_outbound_queue_works::< $runtime, $xcm_config, @@ -359,7 +357,6 @@ pub fn message_dispatch_routing_works< #[macro_export] macro_rules! include_message_dispatch_routing_works( ( - $test_name:tt, $runtime:path, $xcm_config:path, $hrmp_channel_opener:path, @@ -374,7 +371,7 @@ macro_rules! include_message_dispatch_routing_works( $expected_lane_id:expr ) => { #[test] - fn $test_name() { + fn message_dispatch_routing_works() { $crate::test_cases::message_dispatch_routing_works::< $runtime, $xcm_config, @@ -401,7 +398,7 @@ mod test_data { use xcm_executor::traits::{validate_export, ExportXcm}; /// Helper that creates InitializationData mock data, that can be used to initialize bridge GRANDPA pallet - pub(crate) fn initialiation_data< + pub(crate) fn initialization_data< Runtime: pallet_bridge_grandpa::Config, GrandpaPalletInstance: 'static, >( From 8b62a005717fbe976832504b1f0c39eee19ac0a6 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 3 May 2023 10:32:03 +0200 Subject: [PATCH 253/263] Squashed 'bridges/' changes from 66aaf0dd23..557ecbcecc 557ecbcecc Fix sized messages (Follow-up on #2064) (#2103) 54f587a066 Add weight of refund extension post_dispatch to the weights of messages pallet (#2089) 5b1626f8c4 fix pallet param for nightly benchmarks check (#2099) ae44c6b7a1 Add millau specific messages weights (#2097) 6ad0bd1f1e Add integrity tests to rialto parachain runtiime (#2096) 6919556de5 Bump tokio from 1.27.0 to 1.28.0 58795fcb75 Bump clap from 4.2.4 to 4.2.5 01bf31085b Bump scale-info from 2.5.0 to 2.6.0 8fe383240d Bump anyhow from 1.0.70 to 1.0.71 8d94e82ad5 deployments: add new BEEFY metrics and alarms (#2090) e9a4749e7e Bump wasmtime from 6.0.1 to 6.0.2 9d9936c0d9 Bump wasmtime from 6.0.1 to 6.0.2 in /tools/runtime-codegen 5d77cd7bee Add more logs to relayer and message pallets (#2082) 75fbb9d3ef Update comment (#2081) 9904d09cf6 Benchmarks for new relayers pallet calls (#2040) git-subtree-dir: bridges git-subtree-split: 557ecbcecc585547b744a5ac9fb8d7f3b9de4521 --- .gitlab-ci.yml | 5 +- ...emplate.hbs => bridge-weight-template.hbs} | 0 Cargo.lock | 129 +- bin/millau/node/Cargo.toml | 2 +- bin/millau/runtime/Cargo.toml | 2 +- bin/millau/runtime/src/lib.rs | 67 +- bin/millau/runtime/src/rialto_messages.rs | 16 + .../runtime/src/rialto_parachain_messages.rs | 18 + bin/millau/runtime/src/weights/mod.rs | 21 + ...h_runtime_with_rialto_messages_instance.rs | 188 +++ ...with_rialto_parachain_messages_instance.rs | 188 +++ bin/rialto-parachain/node/Cargo.toml | 2 +- bin/rialto-parachain/runtime/Cargo.toml | 3 +- .../runtime/src/millau_messages.rs | 62 + bin/rialto/node/Cargo.toml | 2 +- bin/rialto/runtime/Cargo.toml | 2 +- bin/rialto/runtime/src/millau_messages.rs | 3 +- bin/runtime-common/Cargo.toml | 2 +- bin/runtime-common/src/integrity.rs | 32 +- bin/runtime-common/src/lib.rs | 14 + .../src/refund_relayer_extension.rs | 47 +- .../dashboard/grafana/beefy-dashboard.json | 1067 ++++++++++++++++- deployments/networks/millau.yml | 5 - deployments/networks/rialto.yml | 6 - modules/beefy/Cargo.toml | 2 +- modules/grandpa/Cargo.toml | 2 +- modules/grandpa/src/weights.rs | 2 +- modules/messages/Cargo.toml | 2 +- modules/messages/src/lib.rs | 3 +- modules/messages/src/mock.rs | 16 +- modules/messages/src/outbound_lane.rs | 3 - modules/messages/src/weights.rs | 2 +- modules/messages/src/weights_ext.rs | 43 + modules/parachains/Cargo.toml | 2 +- modules/parachains/src/weights.rs | 2 +- modules/relayers/Cargo.toml | 2 +- modules/relayers/src/benchmarking.rs | 76 +- modules/relayers/src/lib.rs | 14 +- modules/relayers/src/mock.rs | 9 +- modules/relayers/src/weights.rs | 182 ++- modules/relayers/src/weights_ext.rs | 49 + modules/shift-session-manager/Cargo.toml | 2 +- primitives/beefy/Cargo.toml | 2 +- primitives/chain-millau/Cargo.toml | 2 +- primitives/header-chain/Cargo.toml | 2 +- primitives/messages/Cargo.toml | 2 +- primitives/messages/src/lib.rs | 15 + primitives/parachains/Cargo.toml | 2 +- primitives/polkadot-core/Cargo.toml | 2 +- primitives/relayers/Cargo.toml | 2 +- primitives/runtime/Cargo.toml | 2 +- relays/bin-substrate/Cargo.toml | 7 +- relays/bin-substrate/src/chains/millau.rs | 28 + relays/bin-substrate/src/chains/rialto.rs | 23 + .../src/chains/rialto_parachain.rs | 23 + .../bin-substrate/src/cli/encode_message.rs | 102 +- relays/client-bridge-hub-kusama/Cargo.toml | 2 +- relays/client-bridge-hub-polkadot/Cargo.toml | 2 +- relays/client-bridge-hub-rococo/Cargo.toml | 2 +- relays/client-bridge-hub-wococo/Cargo.toml | 2 +- relays/client-rialto-parachain/Cargo.toml | 2 +- relays/client-substrate/Cargo.toml | 4 +- relays/utils/Cargo.toml | 2 +- scripts/update-weights.sh | 27 +- tools/runtime-codegen/Cargo.lock | 78 +- 65 files changed, 2320 insertions(+), 311 deletions(-) rename .maintain/{millau-weight-template.hbs => bridge-weight-template.hbs} (100%) create mode 100644 bin/millau/runtime/src/weights/mod.rs create mode 100644 bin/millau/runtime/src/weights/pallet_bridge_messages_messages_bench_runtime_with_rialto_messages_instance.rs create mode 100644 bin/millau/runtime/src/weights/pallet_bridge_messages_messages_bench_runtime_with_rialto_parachain_messages_instance.rs create mode 100644 modules/relayers/src/weights_ext.rs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9217a422908..c0117509e70 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -115,7 +115,7 @@ spellcheck: <<: *docker-env <<: *test-refs script: - - cargo spellcheck check --cfg=.config/spellcheck.toml --checkers hunspell -m 1 $(find . -type f -name '*.rs' ! -path "./target/*" ! -name 'codegen_runtime.rs' ! -name 'weights.rs') + - cargo spellcheck check --cfg=.config/spellcheck.toml --checkers hunspell -m 1 $(find . -type f -name '*.rs' ! -path "./target/*" ! -path "./bin/millau/runtime/src/weights/*" ! -name 'codegen_runtime.rs' ! -name 'weights.rs') check: stage: test @@ -190,8 +190,7 @@ benchmarks-test: <<: *docker-env <<: *nightly-test script: - - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=RialtoMessages --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 - - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=RialtoParachainMessages --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 + - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_messages --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_grandpa --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_parachains --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 - time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet --chain=dev --steps=2 --repeat=1 --pallet=pallet_bridge_relayers --extrinsic=* --execution=wasm --wasm-execution=Compiled --heap-pages=4096 diff --git a/.maintain/millau-weight-template.hbs b/.maintain/bridge-weight-template.hbs similarity index 100% rename from .maintain/millau-weight-template.hbs rename to .maintain/bridge-weight-template.hbs diff --git a/Cargo.lock b/Cargo.lock index 4c126a2ef9d..a5202956796 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -254,9 +254,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "approx" @@ -1455,9 +1455,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.2.4" +version = "4.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62" +checksum = "8a1f23fa97e1d1641371b51f35535cb26959b8e27ab50d167a8b996b5bada819" dependencies = [ "clap_builder", "clap_derive", @@ -1466,9 +1466,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.2.4" +version = "4.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" +checksum = "0fdc5d93c358224b4d6867ef1356d740de2303e9892edc06c5340daeccd96bab" dependencies = [ "anstream", "anstyle", @@ -1616,18 +1616,18 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7379abaacee0f14abf3204a7606118f0465785252169d186337bcb75030815a" +checksum = "2bc42ba2e232e5b20ff7dc299a812d53337dadce9a7e39a238e6a5cb82d2e57b" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9489fa336927df749631f1008007ced2871068544f40a202ce6d93fbf2366a7b" +checksum = "253531aca9b6f56103c9420369db3263e784df39aa1c90685a1f69cfbba0623e" dependencies = [ "arrayvec 0.7.2", "bumpalo", @@ -1646,33 +1646,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05bbb67da91ec721ed57cef2f7c5ef7728e1cd9bde9ffd3ef8601022e73e3239" +checksum = "72f2154365e2bff1b1b8537a7181591fdff50d8e27fa6e40d5c69c3bad0ca7c8" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418ecb2f36032f6665dc1a5e2060a143dbab41d83b784882e97710e890a7a16d" +checksum = "687e14e3f5775248930e0d5a84195abef8b829958e9794bf8d525104993612b4" [[package]] name = "cranelift-entity" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cf583f7b093f291005f9fb1323e2c37f6ee4c7909e39ce016b2e8360d461705" +checksum = "f42ea692c7b450ad18b8c9889661505d51c09ec4380cf1c2d278dbb2da22cae1" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b66bf9e916f57fbbd0f7703ec6286f4624866bf45000111627c70d272c8dda1" +checksum = "8483c2db6f45fe9ace984e5adc5d058102227e4c62e5aa2054e16b0275fd3a6e" dependencies = [ "cranelift-codegen", "log", @@ -1682,15 +1682,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "649782a39ce99798dd6b4029e2bb318a2fbeaade1b4fa25330763c10c65bc358" +checksum = "e9793158837678902446c411741d87b43f57dadfb944f2440db4287cda8cbd59" [[package]] name = "cranelift-native" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "937e021e089c51f9749d09e7ad1c4f255c2f8686cb8c3df63a34b3ec9921bc41" +checksum = "72668c7755f2b880665cb422c8ad2d56db58a88b9bebfef0b73edc2277c13c49" dependencies = [ "cranelift-codegen", "libc", @@ -1699,9 +1699,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d850cf6775477747c9dfda9ae23355dd70512ffebc70cf82b85a5b111ae668b5" +checksum = "3852ce4b088b44ac4e29459573943009a70d1b192c8d77ef949b4e814f656fc1" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1884,7 +1884,7 @@ name = "cumulus-client-cli" version = "0.1.0" source = "git+https://github.com/paritytech/cumulus?branch=master#df9ed2455462e8c470a8a7ead44023b6eec79ed3" dependencies = [ - "clap 4.2.4", + "clap 4.2.5", "parity-scale-codec", "sc-chain-spec", "sc-cli", @@ -3298,7 +3298,7 @@ dependencies = [ "Inflector", "array-bytes 4.2.0", "chrono", - "clap 4.2.4", + "clap 4.2.5", "comfy-table", "frame-benchmarking", "frame-support", @@ -5688,7 +5688,7 @@ dependencies = [ name = "millau-bridge-node" version = "0.1.0" dependencies = [ - "clap 4.2.4", + "clap 4.2.5", "frame-benchmarking", "frame-benchmarking-cli", "jsonrpsee 0.16.2", @@ -6116,7 +6116,7 @@ name = "node-inspect" version = "0.9.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a14236059c2d3da052fb08295082341aa7b87240" dependencies = [ - "clap 4.2.4", + "clap 4.2.5", "parity-scale-codec", "sc-cli", "sc-client-api", @@ -7906,7 +7906,7 @@ name = "polkadot-cli" version = "0.9.39" source = "git+https://github.com/paritytech/polkadot?branch=master#ae96e09e6ad1810b8d04d4530c07173e604a763d" dependencies = [ - "clap 4.2.4", + "clap 4.2.5", "frame-benchmarking-cli", "futures", "log", @@ -9886,7 +9886,7 @@ dependencies = [ name = "rialto-bridge-node" version = "0.1.0" dependencies = [ - "clap 4.2.4", + "clap 4.2.5", "frame-benchmarking", "frame-benchmarking-cli", "frame-support", @@ -9913,7 +9913,7 @@ dependencies = [ name = "rialto-parachain-collator" version = "0.1.0" dependencies = [ - "clap 4.2.4", + "clap 4.2.5", "cumulus-client-cli", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", @@ -10012,6 +10012,7 @@ dependencies = [ "sp-std 5.0.0", "sp-transaction-pool", "sp-version", + "static_assertions", "substrate-wasm-builder", "xcm", "xcm-builder", @@ -10575,7 +10576,7 @@ source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c dependencies = [ "array-bytes 4.2.0", "chrono", - "clap 4.2.4", + "clap 4.2.5", "fdlimit", "futures", "libp2p", @@ -11437,7 +11438,7 @@ name = "sc-storage-monitor" version = "0.1.0" source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ - "clap 4.2.4", + "clap 4.2.5", "fs4", "futures", "log", @@ -11670,9 +11671,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cfdffd972d76b22f3d7f81c8be34b2296afd3a25e0a547bd9abe340a4dbbe97" +checksum = "dfdef77228a4c05dc94211441595746732131ad7f6530c6c18f045da7b7ab937" dependencies = [ "bitvec", "cfg-if 1.0.0", @@ -11684,9 +11685,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61fa974aea2d63dd18a4ec3a49d59af9f34178c73a4f56d2f18205628d00681e" +checksum = "53012eae69e5aa5c14671942a5dd47de59d4cdcff8532a6dd0e081faf1119482" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.56", @@ -13232,6 +13233,7 @@ dependencies = [ "relay-utils", "relay-westend-client", "relay-wococo-client", + "rialto-parachain-runtime", "rialto-runtime", "signal-hook", "signal-hook-async-std", @@ -13243,6 +13245,7 @@ dependencies = [ "substrate-relay-helper", "tempfile", "xcm", + "xcm-executor", ] [[package]] @@ -13736,9 +13739,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" dependencies = [ "autocfg", "bytes", @@ -13750,14 +13753,14 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2 1.0.56", "quote 1.0.26", @@ -14087,7 +14090,7 @@ version = "0.10.0-dev" source = "git+https://github.com/paritytech/substrate?branch=master#a6cb6d0fb79c303c9af513e856abfb61fc6d8d74" dependencies = [ "async-trait", - "clap 4.2.4", + "clap 4.2.5", "frame-remote-externalities", "hex", "log", @@ -14557,9 +14560,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e89f9819523447330ffd70367ef4a18d8c832e24e8150fe054d1d912841632" +checksum = "76a222f5fa1e14b2cefc286f1b68494d7a965f4bf57ec04c59bb62673d639af6" dependencies = [ "anyhow", "bincode", @@ -14585,18 +14588,18 @@ dependencies = [ [[package]] name = "wasmtime-asm-macros" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd3a5e46c198032da934469f3a6e48649d1f9142438e4fd4617b68a35644b8a" +checksum = "4407a7246e7d2f3d8fb1cf0c72fda8dbafdb6dd34d555ae8bea0e5ae031089cc" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "wasmtime-cache" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b389ae9b678b9c3851091a4804f4182d688d27aff7abc9aa37fa7be37d8ecffa" +checksum = "5ceb3adf61d654be0be67fffdce42447b0880481348785be5fe40b5dd7663a4c" dependencies = [ "anyhow", "base64 0.13.1", @@ -14614,9 +14617,9 @@ dependencies = [ [[package]] name = "wasmtime-cranelift" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b2c92a08c0db6efffd88fdc97d7aa9c7c63b03edb0971dbca745469f820e8c" +checksum = "3c366bb8647e01fd08cb5589976284b00abfded5529b33d7e7f3f086c68304a4" dependencies = [ "anyhow", "cranelift-codegen", @@ -14635,9 +14638,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a6db9fc52985ba06ca601f2ff0ff1f526c5d724c7ac267b47326304b0c97883" +checksum = "47b8b50962eae38ee319f7b24900b7cf371f03eebdc17400c1dc8575fc10c9a7" dependencies = [ "anyhow", "cranelift-entity", @@ -14654,9 +14657,9 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b77e3a52cd84d0f7f18554afa8060cfe564ccac61e3b0802d3fd4084772fa5f6" +checksum = "ffaed4f9a234ba5225d8e64eac7b4a5d13b994aeb37353cde2cbeb3febda9eaa" dependencies = [ "addr2line 0.17.0", "anyhow", @@ -14678,9 +14681,9 @@ dependencies = [ [[package]] name = "wasmtime-jit-debug" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" +checksum = "eed41cbcbf74ce3ff6f1d07d1b707888166dc408d1a880f651268f4f7c9194b2" dependencies = [ "object 0.29.0", "once_cell", @@ -14689,9 +14692,9 @@ dependencies = [ [[package]] name = "wasmtime-jit-icache-coherence" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67d412e9340ab1c83867051d8d1d7c90aa8c9afc91da086088068e2734e25064" +checksum = "43a28ae1e648461bfdbb79db3efdaee1bca5b940872e4175390f465593a2e54c" dependencies = [ "cfg-if 1.0.0", "libc", @@ -14700,9 +14703,9 @@ dependencies = [ [[package]] name = "wasmtime-runtime" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d594e791b5fdd4dbaf8cf7ae62f2e4ff85018ce90f483ca6f42947688e48827d" +checksum = "e704b126e4252788ccfc3526d4d4511d4b23c521bf123e447ac726c14545217b" dependencies = [ "anyhow", "cc", @@ -14724,9 +14727,9 @@ dependencies = [ [[package]] name = "wasmtime-types" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6688d6f96d4dbc1f89fab626c56c1778936d122b5f4ae7a57c2eb42b8d982e2" +checksum = "83e5572c5727c1ee7e8f28717aaa8400e4d22dcbd714ea5457d85b5005206568" dependencies = [ "cranelift-entity", "serde", diff --git a/bin/millau/node/Cargo.toml b/bin/millau/node/Cargo.toml index 7eb7c79405b..fd2096df5fa 100644 --- a/bin/millau/node/Cargo.toml +++ b/bin/millau/node/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.2.4", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } serde_json = "1.0.96" diff --git a/bin/millau/runtime/Cargo.toml b/bin/millau/runtime/Cargo.toml index e1a55ea6f24..4594c73fd92 100644 --- a/bin/millau/runtime/Cargo.toml +++ b/bin/millau/runtime/Cargo.toml @@ -9,7 +9,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] hex-literal = "0.4" codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } # Bridge dependencies diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index dccd75a5b00..60732339cf1 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -30,6 +30,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); pub mod rialto_messages; pub mod rialto_parachain_messages; +pub mod weights; pub mod xcm_config; use bp_parachains::SingleParaStoredHeaderDataBuilder; @@ -442,7 +443,7 @@ pub type WithRialtoMessagesInstance = (); impl pallet_bridge_messages::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + type WeightInfo = weights::RialtoMessagesWeightInfo; type ActiveOutboundLanes = RialtoActiveOutboundLanes; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; @@ -472,7 +473,7 @@ pub type WithRialtoParachainMessagesInstance = pallet_bridge_messages::Instance1 impl pallet_bridge_messages::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + type WeightInfo = weights::RialtoParachainMessagesWeightInfo; type ActiveOutboundLanes = RialtoParachainActiveOutboundLanes; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; @@ -598,7 +599,7 @@ generate_bridge_reject_obsolete_headers_and_messages! { bp_runtime::generate_static_str_provider!(BridgeRefundRialtoPara2000Lane0Msgs); /// Signed extension that refunds relayers that are delivering messages from the Rialto parachain. -pub type PriorityBoostPerMessage = ConstU64<921_900_294>; +pub type PriorityBoostPerMessage = ConstU64<324_316_715>; pub type BridgeRefundRialtoParachainMessages = RefundBridgedParachainMessages< Runtime, RefundableParachain, @@ -647,6 +648,17 @@ pub type Executive = frame_executive::Executive< AllPalletsWithSystem, >; +#[cfg(feature = "runtime-benchmarks")] +mod benches { + frame_benchmarking::define_benchmarks!( + [pallet_bridge_messages, MessagesBench::] + [pallet_bridge_messages, MessagesBench::] + [pallet_bridge_grandpa, BridgeRialtoGrandpa] + [pallet_bridge_parachains, ParachainsBench::] + [pallet_bridge_relayers, RelayersBench::] + ); +} + impl_runtime_apis! { impl sp_api::Core for Runtime { fn version() -> RuntimeVersion { @@ -954,7 +966,7 @@ impl_runtime_apis! { Vec, Vec, ) { - use frame_benchmarking::{list_benchmark, Benchmarking, BenchmarkList}; + use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; use pallet_bridge_messages::benchmarking::Pallet as MessagesBench; @@ -962,22 +974,16 @@ impl_runtime_apis! { use pallet_bridge_relayers::benchmarking::Pallet as RelayersBench; let mut list = Vec::::new(); - - list_benchmark!(list, extra, RialtoParachainMessages, MessagesBench::); - list_benchmark!(list, extra, RialtoMessages, MessagesBench::); - list_benchmark!(list, extra, pallet_bridge_grandpa, BridgeRialtoGrandpa); - list_benchmark!(list, extra, pallet_bridge_parachains, ParachainsBench::); - list_benchmark!(list, extra, pallet_bridge_relayers, RelayersBench::); + list_benchmarks!(list, extra); let storage_info = AllPalletsWithSystem::storage_info(); - return (list, storage_info) } fn dispatch_benchmark( config: frame_benchmarking::BenchmarkConfig, ) -> Result, sp_runtime::RuntimeString> { - use frame_benchmarking::{Benchmarking, BenchmarkBatch, TrackedStorageKey, add_benchmark}; + use frame_benchmarking::{Benchmarking, BenchmarkBatch, TrackedStorageKey}; let whitelist: Vec = vec![ // Block Number @@ -992,9 +998,6 @@ impl_runtime_apis! { hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da946c154ffd9992e395af90b5b13cc6f295c77033fce8a9045824a6690bbf99c6db269502f0a8d1d2a008542d5690a0749").to_vec().into(), ]; - let mut batches = Vec::::new(); - let params = (&config, &whitelist); - use bridge_runtime_common::messages_benchmarking::{ prepare_message_delivery_proof_from_grandpa_chain, prepare_message_delivery_proof_from_parachain, @@ -1108,39 +1111,27 @@ impl_runtime_apis! { } impl RelayersConfig for Runtime { - fn prepare_environment( + fn prepare_rewards_account( account_params: RewardsAccountParams, reward: Balance, ) { - use frame_support::traits::fungible::Mutate; let rewards_account = bp_relayers::PayRewardFromAccount::< Balances, AccountId >::rewards_account(account_params); - Balances::mint_into(&rewards_account, reward).unwrap(); + Self::deposit_account(rewards_account, reward); + } + + fn deposit_account(account: AccountId, balance: Balance) { + use frame_support::traits::fungible::Mutate; + Balances::mint_into(&account, balance.saturating_add(ExistentialDeposit::get())).unwrap(); } } - add_benchmark!( - params, - batches, - RialtoParachainMessages, - MessagesBench:: - ); - add_benchmark!( - params, - batches, - RialtoMessages, - MessagesBench:: - ); - add_benchmark!(params, batches, pallet_bridge_grandpa, BridgeRialtoGrandpa); - add_benchmark!( - params, - batches, - pallet_bridge_parachains, - ParachainsBench:: - ); - add_benchmark!(params, batches, pallet_bridge_relayers, RelayersBench::); + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + + add_benchmarks!(params, batches); Ok(batches) } diff --git a/bin/millau/runtime/src/rialto_messages.rs b/bin/millau/runtime/src/rialto_messages.rs index 573e3f5e68d..b71eb76d90a 100644 --- a/bin/millau/runtime/src/rialto_messages.rs +++ b/bin/millau/runtime/src/rialto_messages.rs @@ -26,6 +26,7 @@ use bridge_runtime_common::{ messages_xcm_extension::{XcmBlobHauler, XcmBlobHaulerAdapter}, }; use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; +use pallet_bridge_relayers::WeightInfoExt as _; use xcm::latest::prelude::*; use xcm_builder::HaulBlobExporter; @@ -135,6 +136,20 @@ impl XcmBlobHauler for ToRialtoXcmBlobHauler { } } +impl pallet_bridge_messages::WeightInfoExt for crate::weights::RialtoMessagesWeightInfo { + fn expected_extra_storage_proof_size() -> u32 { + bp_rialto::EXTRA_STORAGE_PROOF_SIZE + } + + fn receive_messages_proof_overhead_from_runtime() -> Weight { + pallet_bridge_relayers::weights::BridgeWeight::::receive_messages_proof_overhead_from_runtime() + } + + fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { + pallet_bridge_relayers::weights::BridgeWeight::::receive_messages_delivery_proof_overhead_from_runtime() + } +} + #[cfg(test)] mod tests { use super::*; @@ -155,6 +170,7 @@ mod tests { bp_rialto::EXTRA_STORAGE_PROOF_SIZE, bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + false, ); } diff --git a/bin/millau/runtime/src/rialto_parachain_messages.rs b/bin/millau/runtime/src/rialto_parachain_messages.rs index 041d3256f42..8d5216d96c9 100644 --- a/bin/millau/runtime/src/rialto_parachain_messages.rs +++ b/bin/millau/runtime/src/rialto_parachain_messages.rs @@ -28,6 +28,7 @@ use bridge_runtime_common::{ messages_xcm_extension::{XcmBlobHauler, XcmBlobHaulerAdapter}, }; use frame_support::{parameter_types, weights::Weight, RuntimeDebug}; +use pallet_bridge_relayers::WeightInfoExt as _; use xcm::latest::prelude::*; use xcm_builder::HaulBlobExporter; @@ -136,6 +137,22 @@ impl XcmBlobHauler for ToRialtoParachainXcmBlobHauler { } } +impl pallet_bridge_messages::WeightInfoExt + for crate::weights::RialtoParachainMessagesWeightInfo +{ + fn expected_extra_storage_proof_size() -> u32 { + bp_rialto_parachain::EXTRA_STORAGE_PROOF_SIZE + } + + fn receive_messages_proof_overhead_from_runtime() -> Weight { + pallet_bridge_relayers::weights::BridgeWeight::::receive_messages_proof_overhead_from_runtime() + } + + fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { + pallet_bridge_relayers::weights::BridgeWeight::::receive_messages_delivery_proof_overhead_from_runtime() + } +} + #[cfg(test)] mod tests { use super::*; @@ -159,6 +176,7 @@ mod tests { bp_rialto_parachain::EXTRA_STORAGE_PROOF_SIZE, bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + true, ); } diff --git a/bin/millau/runtime/src/weights/mod.rs b/bin/millau/runtime/src/weights/mod.rs new file mode 100644 index 00000000000..801ff9b3c8f --- /dev/null +++ b/bin/millau/runtime/src/weights/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +mod pallet_bridge_messages_messages_bench_runtime_with_rialto_messages_instance; +mod pallet_bridge_messages_messages_bench_runtime_with_rialto_parachain_messages_instance; + +pub use pallet_bridge_messages_messages_bench_runtime_with_rialto_messages_instance::WeightInfo as RialtoMessagesWeightInfo; +pub use pallet_bridge_messages_messages_bench_runtime_with_rialto_parachain_messages_instance::WeightInfo as RialtoParachainMessagesWeightInfo; diff --git a/bin/millau/runtime/src/weights/pallet_bridge_messages_messages_bench_runtime_with_rialto_messages_instance.rs b/bin/millau/runtime/src/weights/pallet_bridge_messages_messages_bench_runtime_with_rialto_messages_instance.rs new file mode 100644 index 00000000000..b58402d2756 --- /dev/null +++ b/bin/millau/runtime/src/weights/pallet_bridge_messages_messages_bench_runtime_with_rialto_messages_instance.rs @@ -0,0 +1,188 @@ + +//! Autogenerated weights for `pallet_bridge_messages` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-05-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/millau-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_bridge_messages +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./bin/millau/runtime/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_bridge_messages`. +pub struct WeightInfo(PhantomData); +impl pallet_bridge_messages::WeightInfo for WeightInfo { + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), added: 2048, mode: MaxEncodedLen) + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + fn receive_single_message_proof() -> Weight { + // Proof Size summary in bytes: + // Measured: `490` + // Estimated: `57170` + // Minimum execution time: 51_189_000 picoseconds. + Weight::from_parts(52_881_000, 0) + .saturating_add(Weight::from_parts(0, 57170)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), added: 2048, mode: MaxEncodedLen) + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + fn receive_two_messages_proof() -> Weight { + // Proof Size summary in bytes: + // Measured: `490` + // Estimated: `57170` + // Minimum execution time: 65_083_000 picoseconds. + Weight::from_parts(66_878_000, 0) + .saturating_add(Weight::from_parts(0, 57170)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), added: 2048, mode: MaxEncodedLen) + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + fn receive_single_message_proof_with_outbound_lane_state() -> Weight { + // Proof Size summary in bytes: + // Measured: `490` + // Estimated: `57170` + // Minimum execution time: 57_919_000 picoseconds. + Weight::from_parts(58_927_000, 0) + .saturating_add(Weight::from_parts(0, 57170)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), added: 2048, mode: MaxEncodedLen) + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + fn receive_single_message_proof_1_kb() -> Weight { + // Proof Size summary in bytes: + // Measured: `490` + // Estimated: `57170` + // Minimum execution time: 52_202_000 picoseconds. + Weight::from_parts(53_617_000, 0) + .saturating_add(Weight::from_parts(0, 57170)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), added: 2048, mode: MaxEncodedLen) + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + fn receive_single_message_proof_16_kb() -> Weight { + // Proof Size summary in bytes: + // Measured: `490` + // Estimated: `57170` + // Minimum execution time: 74_726_000 picoseconds. + Weight::from_parts(76_379_000, 0) + .saturating_add(Weight::from_parts(0, 57170)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), added: 2048, mode: MaxEncodedLen) + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: 539, mode: MaxEncodedLen) + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + fn receive_delivery_proof_for_single_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `515` + // Estimated: `9584` + // Minimum execution time: 45_156_000 picoseconds. + Weight::from_parts(46_535_000, 0) + .saturating_add(Weight::from_parts(0, 9584)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), added: 2048, mode: MaxEncodedLen) + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: 539, mode: MaxEncodedLen) + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { + // Proof Size summary in bytes: + // Measured: `532` + // Estimated: `9584` + // Minimum execution time: 44_104_000 picoseconds. + Weight::from_parts(45_602_000, 0) + .saturating_add(Weight::from_parts(0, 9584)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), added: 2048, mode: MaxEncodedLen) + /// Storage: BridgeRialtoMessages OutboundLanes (r:1 w:1) + /// Proof: BridgeRialtoMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: 539, mode: MaxEncodedLen) + /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { + // Proof Size summary in bytes: + // Measured: `532` + // Estimated: `12124` + // Minimum execution time: 46_880_000 picoseconds. + Weight::from_parts(48_472_000, 0) + .saturating_add(Weight::from_parts(0, 12124)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: BridgeRialtoMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoGrandpa ImportedHeaders (r:1 w:0) + /// Proof: BridgeRialtoGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), added: 2048, mode: MaxEncodedLen) + /// Storage: BridgeRialtoMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRialtoMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + /// The range of component `i` is `[128, 2048]`. + /// The range of component `i` is `[128, 2048]`. + fn receive_single_message_proof_with_dispatch(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `490` + // Estimated: `57170` + // Minimum execution time: 107_297_000 picoseconds. + Weight::from_parts(98_819_194, 0) + .saturating_add(Weight::from_parts(0, 57170)) + // Standard Error: 2_195 + .saturating_add(Weight::from_parts(448_268, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/bin/millau/runtime/src/weights/pallet_bridge_messages_messages_bench_runtime_with_rialto_parachain_messages_instance.rs b/bin/millau/runtime/src/weights/pallet_bridge_messages_messages_bench_runtime_with_rialto_parachain_messages_instance.rs new file mode 100644 index 00000000000..c71cb11fdc7 --- /dev/null +++ b/bin/millau/runtime/src/weights/pallet_bridge_messages_messages_bench_runtime_with_rialto_parachain_messages_instance.rs @@ -0,0 +1,188 @@ + +//! Autogenerated weights for `pallet_bridge_messages` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-05-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/millau-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_bridge_messages +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./bin/millau/runtime/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_bridge_messages`. +pub struct WeightInfo(PhantomData); +impl pallet_bridge_messages::WeightInfo for WeightInfo { + /// Storage: BridgeRialtoParachainMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoParachainMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: Some(196), added: 1681, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachainMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRialtoParachainMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + fn receive_single_message_proof() -> Weight { + // Proof Size summary in bytes: + // Measured: `428` + // Estimated: `56803` + // Minimum execution time: 49_949_000 picoseconds. + Weight::from_parts(51_439_000, 0) + .saturating_add(Weight::from_parts(0, 56803)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: BridgeRialtoParachainMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoParachainMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: Some(196), added: 1681, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachainMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRialtoParachainMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + fn receive_two_messages_proof() -> Weight { + // Proof Size summary in bytes: + // Measured: `428` + // Estimated: `56803` + // Minimum execution time: 62_552_000 picoseconds. + Weight::from_parts(64_899_000, 0) + .saturating_add(Weight::from_parts(0, 56803)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: BridgeRialtoParachainMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoParachainMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: Some(196), added: 1681, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachainMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRialtoParachainMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + fn receive_single_message_proof_with_outbound_lane_state() -> Weight { + // Proof Size summary in bytes: + // Measured: `428` + // Estimated: `56803` + // Minimum execution time: 56_795_000 picoseconds. + Weight::from_parts(58_441_000, 0) + .saturating_add(Weight::from_parts(0, 56803)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: BridgeRialtoParachainMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoParachainMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: Some(196), added: 1681, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachainMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRialtoParachainMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + fn receive_single_message_proof_1_kb() -> Weight { + // Proof Size summary in bytes: + // Measured: `428` + // Estimated: `56803` + // Minimum execution time: 51_340_000 picoseconds. + Weight::from_parts(52_533_000, 0) + .saturating_add(Weight::from_parts(0, 56803)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: BridgeRialtoParachainMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoParachainMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: Some(196), added: 1681, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachainMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRialtoParachainMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + fn receive_single_message_proof_16_kb() -> Weight { + // Proof Size summary in bytes: + // Measured: `428` + // Estimated: `56803` + // Minimum execution time: 74_022_000 picoseconds. + Weight::from_parts(75_115_000, 0) + .saturating_add(Weight::from_parts(0, 56803)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: BridgeRialtoParachainMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoParachainMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: Some(196), added: 1681, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachainMessages OutboundLanes (r:1 w:1) + /// Proof: BridgeRialtoParachainMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: 539, mode: MaxEncodedLen) + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + fn receive_delivery_proof_for_single_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `453` + // Estimated: `9217` + // Minimum execution time: 43_296_000 picoseconds. + Weight::from_parts(45_022_000, 0) + .saturating_add(Weight::from_parts(0, 9217)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: BridgeRialtoParachainMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoParachainMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: Some(196), added: 1681, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachainMessages OutboundLanes (r:1 w:1) + /// Proof: BridgeRialtoParachainMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: 539, mode: MaxEncodedLen) + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { + // Proof Size summary in bytes: + // Measured: `470` + // Estimated: `9217` + // Minimum execution time: 42_618_000 picoseconds. + Weight::from_parts(43_972_000, 0) + .saturating_add(Weight::from_parts(0, 9217)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: BridgeRialtoParachainMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoParachainMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: Some(196), added: 1681, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachainMessages OutboundLanes (r:1 w:1) + /// Proof: BridgeRialtoParachainMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: 539, mode: MaxEncodedLen) + /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, mode: MaxEncodedLen) + fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { + // Proof Size summary in bytes: + // Measured: `470` + // Estimated: `11757` + // Minimum execution time: 45_954_000 picoseconds. + Weight::from_parts(46_892_000, 0) + .saturating_add(Weight::from_parts(0, 11757)) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: BridgeRialtoParachainMessages PalletOperatingMode (r:1 w:0) + /// Proof: BridgeRialtoParachainMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), added: 497, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachains ImportedParaHeads (r:1 w:0) + /// Proof: BridgeRialtoParachains ImportedParaHeads (max_values: Some(1024), max_size: Some(196), added: 1681, mode: MaxEncodedLen) + /// Storage: BridgeRialtoParachainMessages InboundLanes (r:1 w:1) + /// Proof: BridgeRialtoParachainMessages InboundLanes (max_values: None, max_size: Some(49180), added: 51655, mode: MaxEncodedLen) + /// The range of component `i` is `[128, 2048]`. + /// The range of component `i` is `[128, 2048]`. + fn receive_single_message_proof_with_dispatch(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `428` + // Estimated: `56803` + // Minimum execution time: 107_766_000 picoseconds. + Weight::from_parts(97_865_035, 0) + .saturating_add(Weight::from_parts(0, 56803)) + // Standard Error: 2_179 + .saturating_add(Weight::from_parts(447_749, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/bin/rialto-parachain/node/Cargo.toml b/bin/rialto-parachain/node/Cargo.toml index 38521d7aa0e..075e70c961b 100644 --- a/bin/rialto-parachain/node/Cargo.toml +++ b/bin/rialto-parachain/node/Cargo.toml @@ -17,7 +17,7 @@ default = [] runtime-benchmarks = ['rialto-parachain-runtime/runtime-benchmarks'] [dependencies] -clap = { version = "4.2.4", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } log = '0.4.17' codec = { package = 'parity-scale-codec', version = '3.1.5' } serde = { version = '1.0', features = ['derive'] } diff --git a/bin/rialto-parachain/runtime/Cargo.toml b/bin/rialto-parachain/runtime/Cargo.toml index 53f57e02619..d44f4b42af4 100644 --- a/bin/rialto-parachain/runtime/Cargo.toml +++ b/bin/rialto-parachain/runtime/Cargo.toml @@ -12,7 +12,7 @@ substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", bran [dependencies] codec = { package = 'parity-scale-codec', version = '3.1.5', default-features = false, features = ['derive']} hex-literal = "0.4" -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } # Bridge depedencies @@ -76,6 +76,7 @@ pallet-xcm = { git = "https://github.com/paritytech/polkadot", branch = "master" [dev-dependencies] bridge-runtime-common = { path = "../../runtime-common", features = ["integrity-test"] } +static_assertions = "1.1" [features] default = ['std'] diff --git a/bin/rialto-parachain/runtime/src/millau_messages.rs b/bin/rialto-parachain/runtime/src/millau_messages.rs index 9c00e6bad34..3c523ebfa12 100644 --- a/bin/rialto-parachain/runtime/src/millau_messages.rs +++ b/bin/rialto-parachain/runtime/src/millau_messages.rs @@ -134,3 +134,65 @@ impl XcmBlobHauler for ToMillauXcmBlobHauler { XCM_LANE } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{MillauGrandpaInstance, Runtime, WithMillauMessagesInstance}; + use bridge_runtime_common::{ + assert_complete_bridge_types, + integrity::{ + assert_complete_bridge_constants, check_message_lane_weights, + AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants, + AssertCompleteBridgeConstants, + }, + }; + + #[test] + fn ensure_millau_message_lane_weights_are_correct() { + check_message_lane_weights::( + bp_millau::EXTRA_STORAGE_PROOF_SIZE, + bp_rialto_parachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + bp_rialto_parachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + false, + ); + } + + #[test] + fn ensure_bridge_integrity() { + assert_complete_bridge_types!( + runtime: Runtime, + with_bridged_chain_grandpa_instance: MillauGrandpaInstance, + with_bridged_chain_messages_instance: WithMillauMessagesInstance, + bridge: WithMillauMessageBridge, + this_chain: bp_rialto_parachain::RialtoParachain, + bridged_chain: bp_millau::Millau, + ); + + assert_complete_bridge_constants::< + Runtime, + MillauGrandpaInstance, + WithMillauMessagesInstance, + WithMillauMessageBridge, + >(AssertCompleteBridgeConstants { + this_chain_constants: AssertChainConstants { + block_length: bp_rialto_parachain::BlockLength::get(), + block_weights: bp_rialto_parachain::BlockWeights::get(), + }, + messages_pallet_constants: AssertBridgeMessagesPalletConstants { + max_unrewarded_relayers_in_bridged_confirmation_tx: + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + max_unconfirmed_messages_in_bridged_confirmation_tx: + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + bridged_chain_id: bp_runtime::MILLAU_CHAIN_ID, + }, + pallet_names: AssertBridgePalletNames { + with_this_chain_messages_pallet_name: + bp_rialto_parachain::WITH_RIALTO_PARACHAIN_MESSAGES_PALLET_NAME, + with_bridged_chain_grandpa_pallet_name: bp_millau::WITH_MILLAU_GRANDPA_PALLET_NAME, + with_bridged_chain_messages_pallet_name: + bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME, + }, + }); + } +} diff --git a/bin/rialto/node/Cargo.toml b/bin/rialto/node/Cargo.toml index ffbb587dc1f..36eaa7021a0 100644 --- a/bin/rialto/node/Cargo.toml +++ b/bin/rialto/node/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -clap = { version = "4.2.4", features = ["derive"] } +clap = { version = "4.2.5", features = ["derive"] } serde_json = "1.0.96" # Bridge dependencies diff --git a/bin/rialto/runtime/Cargo.toml b/bin/rialto/runtime/Cargo.toml index 2b68e72d6f8..f687905ad63 100644 --- a/bin/rialto/runtime/Cargo.toml +++ b/bin/rialto/runtime/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } # Bridge dependencies diff --git a/bin/rialto/runtime/src/millau_messages.rs b/bin/rialto/runtime/src/millau_messages.rs index 7c960639f2f..a7d1d19f0d8 100644 --- a/bin/rialto/runtime/src/millau_messages.rs +++ b/bin/rialto/runtime/src/millau_messages.rs @@ -149,11 +149,12 @@ mod tests { }; #[test] - fn ensure_rialto_message_lane_weights_are_correct() { + fn ensure_millau_message_lane_weights_are_correct() { check_message_lane_weights::( bp_millau::EXTRA_STORAGE_PROOF_SIZE, bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + false, ); } diff --git a/bin/runtime-common/Cargo.toml b/bin/runtime-common/Cargo.toml index 3db4ae9abca..039e323b9b7 100644 --- a/bin/runtime-common/Cargo.toml +++ b/bin/runtime-common/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } hash-db = { version = "0.16.0", default-features = false } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } static_assertions = { version = "1.1", optional = true } # Bridge dependencies diff --git a/bin/runtime-common/src/integrity.rs b/bin/runtime-common/src/integrity.rs index 5820dd99b91..bf3d239eefd 100644 --- a/bin/runtime-common/src/integrity.rs +++ b/bin/runtime-common/src/integrity.rs @@ -24,8 +24,9 @@ use crate::{messages, messages::MessageBridge}; use bp_messages::{InboundLaneData, MessageNonce}; use bp_runtime::{Chain, ChainId}; use codec::Encode; -use frame_support::{storage::generator::StorageValue, traits::Get}; +use frame_support::{storage::generator::StorageValue, traits::Get, weights::Weight}; use frame_system::limits; +use pallet_bridge_messages::WeightInfoExt as _; use sp_runtime::traits::SignedExtension; /// Macro that ensures that the runtime configuration and chain primitives crate are sharing @@ -289,15 +290,25 @@ where } /// Check that the message lane weights are correct. -pub fn check_message_lane_weights( +pub fn check_message_lane_weights< + C: Chain, + T: frame_system::Config + pallet_bridge_messages::Config, +>( bridged_chain_extra_storage_proof_size: u32, this_chain_max_unrewarded_relayers: MessageNonce, this_chain_max_unconfirmed_messages: MessageNonce, + // whether `RefundBridgedParachainMessages` extension is deployed at runtime and is used for + // refunding this bridge transactions? + // + // in other words: pass true for all known production chains + runtime_includes_refund_extension: bool, ) { - type Weights = pallet_bridge_messages::weights::BridgeWeight; + type Weights = ::WeightInfo; + // check basic weight assumptions pallet_bridge_messages::ensure_weights_are_correct::>(); + // check that weights allow us to receive messages let max_incoming_message_proof_size = bridged_chain_extra_storage_proof_size .saturating_add(messages::target::maximal_incoming_message_size(C::max_extrinsic_size())); pallet_bridge_messages::ensure_able_to_receive_message::>( @@ -307,6 +318,7 @@ pub fn check_message_lane_weights( messages::target::maximal_incoming_message_dispatch_weight(C::max_extrinsic_weight()), ); + // check that weights allow us to receive delivery confirmations let max_incoming_inbound_lane_data_proof_size = InboundLaneData::<()>::encoded_size_hint_u32(this_chain_max_unrewarded_relayers as _); pallet_bridge_messages::ensure_able_to_receive_confirmation::>( @@ -316,6 +328,20 @@ pub fn check_message_lane_weights( this_chain_max_unrewarded_relayers, this_chain_max_unconfirmed_messages, ); + + // check that extra weights of delivery/confirmation transactions include the weight + // of `RefundBridgedParachainMessages` operations. This signed extension assumes the worst case + // (i.e. slashing if delivery transaction was invalid) and refunds some weight if + // assumption was wrong (i.e. if we did refund instead of slashing). This check + // ensures the extension will not refund weight when it doesn't need to (i.e. if pallet + // weights do not account weights of refund extension). + if runtime_includes_refund_extension { + assert_ne!(Weights::::receive_messages_proof_overhead_from_runtime(), Weight::zero()); + assert_ne!( + Weights::::receive_messages_delivery_proof_overhead_from_runtime(), + Weight::zero() + ); + } } /// Check that the `AdditionalSigned` type of a wrapped runtime is the same as the one of the diff --git a/bin/runtime-common/src/lib.rs b/bin/runtime-common/src/lib.rs index e8a2d2470fa..12b096492cd 100644 --- a/bin/runtime-common/src/lib.rs +++ b/bin/runtime-common/src/lib.rs @@ -159,7 +159,21 @@ pub enum CustomNetworkId { RialtoParachain, } +impl TryFrom for CustomNetworkId { + type Error = (); + + fn try_from(chain: bp_runtime::ChainId) -> Result { + Ok(match chain { + bp_runtime::MILLAU_CHAIN_ID => Self::Millau, + bp_runtime::RIALTO_CHAIN_ID => Self::Rialto, + bp_runtime::RIALTO_PARACHAIN_CHAIN_ID => Self::RialtoParachain, + _ => return Err(()), + }) + } +} + impl CustomNetworkId { + /// Converts self to XCM' network id. pub const fn as_network_id(&self) -> NetworkId { match *self { CustomNetworkId::Millau => NetworkId::Kusama, diff --git a/bin/runtime-common/src/refund_relayer_extension.rs b/bin/runtime-common/src/refund_relayer_extension.rs index 7d65263e9fd..00ea70aa04e 100644 --- a/bin/runtime-common/src/refund_relayer_extension.rs +++ b/bin/runtime-common/src/refund_relayer_extension.rs @@ -40,7 +40,9 @@ use pallet_bridge_parachains::{ BoundedBridgeGrandpaConfig, CallSubType as ParachainsCallSubType, Config as ParachainsConfig, RelayBlockNumber, SubmitParachainHeadsHelper, SubmitParachainHeadsInfo, }; -use pallet_bridge_relayers::{Config as RelayersConfig, Pallet as RelayersPallet}; +use pallet_bridge_relayers::{ + Config as RelayersConfig, Pallet as RelayersPallet, WeightInfoExt as _, +}; use pallet_transaction_payment::{Config as TransactionPaymentConfig, OnChargeTransaction}; use pallet_utility::{Call as UtilityCall, Config as UtilityConfig, Pallet as UtilityPallet}; use scale_info::TypeInfo; @@ -445,11 +447,19 @@ where // decrease post-dispatch weight/size using extra weight/size that we know now let post_info_len = len.saturating_sub(extra_size as usize); - let mut post_info = *post_info; - post_info.actual_weight = - Some(post_info.actual_weight.unwrap_or(info.weight).saturating_sub(extra_weight)); + let mut post_info_weight = + post_info.actual_weight.unwrap_or(info.weight).saturating_sub(extra_weight); + + // let's also replace the weight of slashing relayer with the weight of rewarding relayer + if call_info.is_receive_messages_proof_call() { + post_info_weight = post_info_weight.saturating_sub( + ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(), + ); + } // compute the relayer refund + let mut post_info = *post_info; + post_info.actual_weight = Some(post_info_weight); let refund = Refund::compute_refund(info, &post_info, post_info_len, tip); // we can finally reward relayer @@ -1052,7 +1062,20 @@ mod tests { assert_eq!(post_dispatch_result, Ok(())); } - fn expected_reward() -> ThisChainBalance { + fn expected_delivery_reward() -> ThisChainBalance { + let mut post_dispatch_info = post_dispatch_info(); + let extra_weight = ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(); + post_dispatch_info.actual_weight = + Some(dispatch_info().weight.saturating_sub(extra_weight)); + pallet_transaction_payment::Pallet::::compute_actual_fee( + 1024, + &dispatch_info(), + &post_dispatch_info, + Zero::zero(), + ) + } + + fn expected_confirmation_reward() -> ThisChainBalance { pallet_transaction_payment::Pallet::::compute_actual_fee( 1024, &dispatch_info(), @@ -1449,7 +1472,7 @@ mod tests { // without any size/weight refund: we expect regular reward let pre_dispatch_data = all_finality_pre_dispatch_data(); - let regular_reward = expected_reward(); + let regular_reward = expected_delivery_reward(); run_post_dispatch(Some(pre_dispatch_data), Ok(())); assert_eq!( RelayersPallet::::relayer_reward( @@ -1496,7 +1519,7 @@ mod tests { relayer_account_at_this_chain(), MsgProofsRewardsAccount::get() ), - Some(expected_reward()), + Some(expected_delivery_reward()), ); run_post_dispatch(Some(all_finality_confirmation_pre_dispatch_data()), Ok(())); @@ -1505,7 +1528,7 @@ mod tests { relayer_account_at_this_chain(), MsgDeliveryProofsRewardsAccount::get() ), - Some(expected_reward()), + Some(expected_confirmation_reward()), ); }); } @@ -1521,7 +1544,7 @@ mod tests { relayer_account_at_this_chain(), MsgProofsRewardsAccount::get() ), - Some(expected_reward()), + Some(expected_delivery_reward()), ); run_post_dispatch(Some(parachain_finality_confirmation_pre_dispatch_data()), Ok(())); @@ -1530,7 +1553,7 @@ mod tests { relayer_account_at_this_chain(), MsgDeliveryProofsRewardsAccount::get() ), - Some(expected_reward()), + Some(expected_confirmation_reward()), ); }); } @@ -1546,7 +1569,7 @@ mod tests { relayer_account_at_this_chain(), MsgProofsRewardsAccount::get() ), - Some(expected_reward()), + Some(expected_delivery_reward()), ); run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(())); @@ -1555,7 +1578,7 @@ mod tests { relayer_account_at_this_chain(), MsgDeliveryProofsRewardsAccount::get() ), - Some(expected_reward()), + Some(expected_confirmation_reward()), ); }); } diff --git a/deployments/networks/dashboard/grafana/beefy-dashboard.json b/deployments/networks/dashboard/grafana/beefy-dashboard.json index 2e1e177641a..5cd74861a84 100644 --- a/deployments/networks/dashboard/grafana/beefy-dashboard.json +++ b/deployments/networks/dashboard/grafana/beefy-dashboard.json @@ -22,6 +22,7 @@ "fiscalYearStartMonth": 0, "gnetId": null, "graphTooltip": 0, + "id": 5, "links": [], "liveNow": false, "panels": [ @@ -220,29 +221,6 @@ "operator": { "type": "and" }, - "query": { - "params": [ - "A", - "5m", - "now" - ] - }, - "reducer": { - "params": [], - "type": "max" - }, - "type": "query" - }, - { - "evaluator": { - "params": [ - 0 - ], - "type": "gt" - }, - "operator": { - "type": "or" - }, "query": { "params": [ "B", @@ -371,19 +349,1046 @@ } }, { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "10m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "10m", + "frequency": "5m", + "handler": 1, + "name": "Equivocation votes detected alert", + "noDataState": "no_data", + "notifications": [] + }, "datasource": "Prometheus", - "fill": 1, - "fillGradient": 0, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, "gridPos": { - "h": 12, - "w": 18, + "h": 7, + "w": 6, "x": 0, "y": 14 }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "substrate_beefy_equivocation_votes", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "op": "gt", + "value": 0, + "visible": true + } + ], + "title": "Equivocation votes detected", + "type": "timeseries" + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "10m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "10m", + "frequency": "5m", + "handler": 1, + "name": "Invalid votes received alert", + "noDataState": "no_data", + "notifications": [] + }, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "substrate_beefy_invalid_votes", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "op": "gt", + "value": 0, + "visible": true + } + ], + "title": "Invalid votes received", + "type": "timeseries" + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "10m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "10m", + "frequency": "5m", + "handler": 1, + "name": "Dropped Buffered Justifications alert", + "noDataState": "no_data", + "notifications": [] + }, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 14 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "substrate_beefy_buffered_justifications_dropped", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "op": "gt", + "value": 0, + "visible": true + } + ], + "title": "Dropped Buffered Justifications", + "type": "timeseries" + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "10m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "10m", + "frequency": "5m", + "handler": 1, + "name": "OnDemand Justifications Engine alert", + "noDataState": "no_data", + "notifications": [] + }, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 21 + }, + "id": 26, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "substrate_beefy_on_demand_justification_invalid_proof", + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "exemplar": true, + "expr": "substrate_beefy_on_demand_justification_good_proof", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + }, + { + "exemplar": true, + "expr": "substrate_beefy_on_demand_justification_peer_error", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "C" + }, + { + "exemplar": true, + "expr": "beefy_on_demand_justification_peer_refused", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "D" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "op": "gt", + "value": 0, + "visible": true + } + ], + "title": "OnDemand Justifications Engine", + "type": "timeseries" + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "10m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "10m", + "frequency": "5m", + "handler": 1, + "name": "Incoming OnDemand Justifications alert", + "noDataState": "no_data", + "notifications": [] + }, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 21 + }, + "id": 24, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "substrate_beefy_successful_justification_responses", + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "exemplar": true, + "expr": "substrate_beefy_failed_justification_responses", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "op": "gt", + "value": 0, + "visible": true + } + ], + "title": "Incoming OnDemand Justifications", + "type": "timeseries" + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "10m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "10m", + "frequency": "5m", + "handler": 1, + "name": "Block-import Justifications alert", + "noDataState": "no_data", + "notifications": [] + }, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 21 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "substrate_beefy_good_justification_imports", + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "exemplar": true, + "expr": "substrate_beefy_bad_justification_imports", + "hide": false, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "op": "gt", + "value": 0, + "visible": true + } + ], + "title": "Block-import Justifications", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 6, + "x": 0, + "y": 28 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "substrate_beefy_buffered_justifications", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Buffered Justifications", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 6, + "x": 6, + "y": 28 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "substrate_beefy_imported_justifications", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Imported Justifications", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 6, + "x": 12, + "y": 28 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "substrate_beefy_stale_votes", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Stale votes received", + "type": "timeseries" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 13, + "w": 18, + "x": 0, + "y": 41 + }, "hiddenSeries": false, "id": 6, "legend": { diff --git a/deployments/networks/millau.yml b/deployments/networks/millau.yml index d91c5d83286..24b263d82e5 100644 --- a/deployments/networks/millau.yml +++ b/deployments/networks/millau.yml @@ -11,7 +11,6 @@ services: image: ${MILLAU_BRIDGE_NODE_IMAGE:-paritytech/millau-bridge-node} entrypoint: - /home/user/millau-bridge-node - - --execution=Native - --chain=local - --bootnodes=/dns4/millau-node-bob/tcp/30333/p2p/12D3KooWM5LFR5ne4yTQ4sBSXJ75M4bDo2MAhAW2GhL3i8fe5aRb - --alice @@ -32,7 +31,6 @@ services: <<: *millau-bridge-node entrypoint: - /home/user/millau-bridge-node - - --execution=Native - --chain=local - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H - --bob @@ -51,7 +49,6 @@ services: <<: *millau-bridge-node entrypoint: - /home/user/millau-bridge-node - - --execution=Native - --chain=local - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H - --charlie @@ -69,7 +66,6 @@ services: <<: *millau-bridge-node entrypoint: - /home/user/millau-bridge-node - - --execution=Native - --chain=local - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H - --dave @@ -87,7 +83,6 @@ services: <<: *millau-bridge-node entrypoint: - /home/user/millau-bridge-node - - --execution=Native - --chain=local - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H - --eve diff --git a/deployments/networks/rialto.yml b/deployments/networks/rialto.yml index ebd8393fcad..599b1c9a1b8 100644 --- a/deployments/networks/rialto.yml +++ b/deployments/networks/rialto.yml @@ -11,7 +11,6 @@ services: image: ${RIALTO_BRIDGE_NODE_IMAGE:-paritytech/rialto-bridge-node} entrypoint: - /home/user/rialto-bridge-node - - --execution=Native - --chain=local - --bootnodes=/dns4/rialto-node-bob/tcp/30333/p2p/12D3KooWSEpHJj29HEzgPFcRYVc5X3sEuP3KgiUoqJNCet51NiMX - --alice @@ -32,7 +31,6 @@ services: <<: *rialto-bridge-node entrypoint: - /home/user/rialto-bridge-node - - --execution=Native - --chain=local - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE - --bob @@ -51,7 +49,6 @@ services: <<: *rialto-bridge-node entrypoint: - /home/user/rialto-bridge-node - - --execution=Native - --chain=local - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE - --charlie @@ -69,7 +66,6 @@ services: <<: *rialto-bridge-node entrypoint: - /home/user/rialto-bridge-node - - --execution=Native - --chain=local - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE - --dave @@ -87,7 +83,6 @@ services: <<: *rialto-bridge-node entrypoint: - /home/user/rialto-bridge-node - - --execution=Native - --chain=local - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE - --eve @@ -105,7 +100,6 @@ services: <<: *rialto-bridge-node entrypoint: - /home/user/rialto-bridge-node - - --execution=Native - --chain=local - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE - --ferdie diff --git a/modules/beefy/Cargo.toml b/modules/beefy/Cargo.toml index 8aff2c169b4..4270b96e208 100644 --- a/modules/beefy/Cargo.toml +++ b/modules/beefy/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } log = { version = "0.4.14", default-features = false } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true } # Bridge Dependencies diff --git a/modules/grandpa/Cargo.toml b/modules/grandpa/Cargo.toml index 07d2593b914..9b97b518fc5 100644 --- a/modules/grandpa/Cargo.toml +++ b/modules/grandpa/Cargo.toml @@ -11,7 +11,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } finality-grandpa = { version = "0.16.2", default-features = false } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } # Bridge Dependencies diff --git a/modules/grandpa/src/weights.rs b/modules/grandpa/src/weights.rs index 089aee8b569..4b94f7adfe7 100644 --- a/modules/grandpa/src/weights.rs +++ b/modules/grandpa/src/weights.rs @@ -35,7 +35,7 @@ // --wasm-execution=Compiled // --heap-pages=4096 // --output=./modules/grandpa/src/weights.rs -// --template=./.maintain/millau-weight-template.hbs +// --template=./.maintain/bridge-weight-template.hbs #![allow(clippy::all)] #![allow(unused_parens)] diff --git a/modules/messages/Cargo.toml b/modules/messages/Cargo.toml index f733d62bf64..639ac9dc23c 100644 --- a/modules/messages/Cargo.toml +++ b/modules/messages/Cargo.toml @@ -10,7 +10,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } log = { version = "0.4.17", default-features = false } num-traits = { version = "0.2", default-features = false } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } # Bridge dependencies diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index 8f4911b8d03..045015b7751 100644 --- a/modules/messages/src/lib.rs +++ b/modules/messages/src/lib.rs @@ -329,9 +329,10 @@ pub mod pallet { if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce { log::trace!( target: LOG_TARGET, - "Received lane {:?} state update: latest_confirmed_nonce={}", + "Received lane {:?} state update: latest_confirmed_nonce={}. Unrewarded relayers: {:?}", lane_id, updated_latest_confirmed_nonce, + UnrewardedRelayersState::from(&lane.storage().data()), ); } } diff --git a/modules/messages/src/mock.rs b/modules/messages/src/mock.rs index 3d78ab562d2..2e45d5b601f 100644 --- a/modules/messages/src/mock.rs +++ b/modules/messages/src/mock.rs @@ -26,8 +26,8 @@ use bp_messages::{ DeliveryPayments, DispatchMessage, DispatchMessageData, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain, }, - total_unrewarded_messages, DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, - MessageNonce, MessagePayload, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, + DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, + OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use bp_runtime::{messages::MessageDispatchResult, Size}; use codec::{Decode, Encode}; @@ -485,17 +485,7 @@ pub fn unrewarded_relayer( /// Returns unrewarded relayers state at given lane. pub fn inbound_unrewarded_relayers_state(lane: bp_messages::LaneId) -> UnrewardedRelayersState { let inbound_lane_data = crate::InboundLanes::::get(lane).0; - let last_delivered_nonce = inbound_lane_data.last_delivered_nonce(); - let relayers = inbound_lane_data.relayers; - UnrewardedRelayersState { - unrewarded_relayer_entries: relayers.len() as _, - messages_in_oldest_entry: relayers - .front() - .map(|entry| 1 + entry.messages.end - entry.messages.begin) - .unwrap_or(0), - total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX), - last_delivered_nonce, - } + UnrewardedRelayersState::from(&inbound_lane_data) } /// Return test externalities to use in tests. diff --git a/modules/messages/src/outbound_lane.rs b/modules/messages/src/outbound_lane.rs index 33a58a40400..3d0d4de966a 100644 --- a/modules/messages/src/outbound_lane.rs +++ b/modules/messages/src/outbound_lane.rs @@ -196,9 +196,6 @@ fn ensure_unrewarded_relayers_are_correct( // entry can't confirm messages larger than `inbound_lane_data.latest_received_nonce()` // (guaranteed by the `InboundLane::receive_message()`) if entry.messages.end > latest_received_nonce { - // technically this will be detected in the next loop iteration as - // `InvalidNumberOfDispatchResults` but to guarantee safety of loop operations below - // this is detected now return Err(ReceivalConfirmationResult::FailedToConfirmFutureMessages) } } diff --git a/modules/messages/src/weights.rs b/modules/messages/src/weights.rs index baaef317241..9880f1dd1ea 100644 --- a/modules/messages/src/weights.rs +++ b/modules/messages/src/weights.rs @@ -35,7 +35,7 @@ // --wasm-execution=Compiled // --heap-pages=4096 // --output=./modules/messages/src/weights.rs -// --template=./.maintain/millau-weight-template.hbs +// --template=./.maintain/bridge-weight-template.hbs #![allow(clippy::all)] #![allow(unused_parens)] diff --git a/modules/messages/src/weights_ext.rs b/modules/messages/src/weights_ext.rs index 090c03390ba..3aefd6be7ca 100644 --- a/modules/messages/src/weights_ext.rs +++ b/modules/messages/src/weights_ext.rs @@ -266,6 +266,27 @@ pub trait WeightInfoExt: WeightInfo { /// this value, we're going to charge relayer for that. fn expected_extra_storage_proof_size() -> u32; + // Our configuration assumes that the runtime has special signed extensions used to: + // + // 1) reject obsolete delivery and confirmation transactions; + // + // 2) refund transaction cost to relayer and register his rewards. + // + // The checks in (1) are trivial, so its computation weight may be ignored. And we only touch + // storage values that are read during the call. So we may ignore the weight of this check. + // + // However, during (2) we read and update storage values of other pallets + // (`pallet-bridge-relayers` and balances/assets pallet). So we need to add this weight to the + // weight of our call. Hence two following methods. + + /// Extra weight that is added to the `receive_messages_proof` call weight by signed extensions + /// that are declared at runtime level. + fn receive_messages_proof_overhead_from_runtime() -> Weight; + + /// Extra weight that is added to the `receive_messages_delivery_proof` call weight by signed + /// extensions that are declared at runtime level. + fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight; + // Functions that are directly mapped to extrinsics weights. /// Weight of message delivery extrinsic. @@ -276,6 +297,8 @@ pub trait WeightInfoExt: WeightInfo { ) -> Weight { // basic components of extrinsic weight let transaction_overhead = Self::receive_messages_proof_overhead(); + let transaction_overhead_from_runtime = + Self::receive_messages_proof_overhead_from_runtime(); let outbound_state_delivery_weight = Self::receive_messages_proof_outbound_lane_state_overhead(); let messages_delivery_weight = @@ -292,6 +315,7 @@ pub trait WeightInfoExt: WeightInfo { ); transaction_overhead + .saturating_add(transaction_overhead_from_runtime) .saturating_add(outbound_state_delivery_weight) .saturating_add(messages_delivery_weight) .saturating_add(messages_dispatch_weight) @@ -305,6 +329,8 @@ pub trait WeightInfoExt: WeightInfo { ) -> Weight { // basic components of extrinsic weight let transaction_overhead = Self::receive_messages_delivery_proof_overhead(); + let transaction_overhead_from_runtime = + Self::receive_messages_delivery_proof_overhead_from_runtime(); let messages_overhead = Self::receive_messages_delivery_proof_messages_overhead(relayers_state.total_messages); let relayers_overhead = Self::receive_messages_delivery_proof_relayers_overhead( @@ -319,6 +345,7 @@ pub trait WeightInfoExt: WeightInfo { ); transaction_overhead + .saturating_add(transaction_overhead_from_runtime) .saturating_add(messages_overhead) .saturating_add(relayers_overhead) .saturating_add(proof_size_overhead) @@ -424,12 +451,28 @@ impl WeightInfoExt for () { fn expected_extra_storage_proof_size() -> u32 { EXTRA_STORAGE_PROOF_SIZE } + + fn receive_messages_proof_overhead_from_runtime() -> Weight { + Weight::zero() + } + + fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { + Weight::zero() + } } impl WeightInfoExt for crate::weights::BridgeWeight { fn expected_extra_storage_proof_size() -> u32 { EXTRA_STORAGE_PROOF_SIZE } + + fn receive_messages_proof_overhead_from_runtime() -> Weight { + Weight::zero() + } + + fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { + Weight::zero() + } } #[cfg(test)] diff --git a/modules/parachains/Cargo.toml b/modules/parachains/Cargo.toml index 39c3ba626aa..d8c89b79991 100644 --- a/modules/parachains/Cargo.toml +++ b/modules/parachains/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } # Bridge Dependencies diff --git a/modules/parachains/src/weights.rs b/modules/parachains/src/weights.rs index 54a835cbc87..1e81dba72fe 100644 --- a/modules/parachains/src/weights.rs +++ b/modules/parachains/src/weights.rs @@ -35,7 +35,7 @@ // --wasm-execution=Compiled // --heap-pages=4096 // --output=./modules/parachains/src/weights.rs -// --template=./.maintain/millau-weight-template.hbs +// --template=./.maintain/bridge-weight-template.hbs #![allow(clippy::all)] #![allow(unused_parens)] diff --git a/modules/relayers/Cargo.toml b/modules/relayers/Cargo.toml index c654c60d02b..857d47cc65a 100644 --- a/modules/relayers/Cargo.toml +++ b/modules/relayers/Cargo.toml @@ -9,7 +9,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } log = { version = "0.4.17", default-features = false } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } # Bridge dependencies diff --git a/modules/relayers/src/benchmarking.rs b/modules/relayers/src/benchmarking.rs index a762a5693c2..d66a11ff06d 100644 --- a/modules/relayers/src/benchmarking.rs +++ b/modules/relayers/src/benchmarking.rs @@ -24,6 +24,7 @@ use bp_messages::LaneId; use bp_relayers::RewardsAccountOwner; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_system::RawOrigin; +use sp_runtime::traits::One; /// Reward amount that is (hopefully) is larger than existential deposit across all chains. const REWARD_AMOUNT: u32 = u32::MAX; @@ -34,7 +35,9 @@ pub struct Pallet(crate::Pallet); /// Trait that must be implemented by runtime. pub trait Config: crate::Config { /// Prepare environment for paying given reward for serving given lane. - fn prepare_environment(account_params: RewardsAccountParams, reward: Self::Reward); + fn prepare_rewards_account(account_params: RewardsAccountParams, reward: Self::Reward); + /// Give enough balance to given account. + fn deposit_account(account: Self::AccountId, balance: Self::Reward); } benchmarks! { @@ -46,7 +49,7 @@ benchmarks! { let relayer: T::AccountId = whitelisted_caller(); let reward = T::Reward::from(REWARD_AMOUNT); - T::prepare_environment(account_params, reward); + T::prepare_rewards_account(account_params, reward); RelayerRewards::::insert(&relayer, account_params, reward); }: _(RawOrigin::Signed(relayer), account_params) verify { @@ -55,5 +58,74 @@ benchmarks! { // also completed successfully } + // Benchmark `register` call. + register { + let relayer: T::AccountId = whitelisted_caller(); + let valid_till = frame_system::Pallet::::block_number() + .saturating_add(crate::Pallet::::required_registration_lease()) + .saturating_add(One::one()) + .saturating_add(One::one()); + + T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); + }: _(RawOrigin::Signed(relayer.clone()), valid_till) + verify { + assert!(crate::Pallet::::is_registration_active(&relayer)); + } + + // Benchmark `deregister` call. + deregister { + let relayer: T::AccountId = whitelisted_caller(); + let valid_till = frame_system::Pallet::::block_number() + .saturating_add(crate::Pallet::::required_registration_lease()) + .saturating_add(One::one()) + .saturating_add(One::one()); + T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); + crate::Pallet::::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap(); + + frame_system::Pallet::::set_block_number(valid_till.saturating_add(One::one())); + }: _(RawOrigin::Signed(relayer.clone())) + verify { + assert!(!crate::Pallet::::is_registration_active(&relayer)); + } + + // Benchmark `slash_and_deregister` method of the pallet. We are adding this weight to + // the weight of message delivery call if `RefundBridgedParachainMessages` signed extension + // is deployed at runtime level. + slash_and_deregister { + // prepare and register relayer account + let relayer: T::AccountId = whitelisted_caller(); + let valid_till = frame_system::Pallet::::block_number() + .saturating_add(crate::Pallet::::required_registration_lease()) + .saturating_add(One::one()) + .saturating_add(One::one()); + T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); + crate::Pallet::::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap(); + + // create slash destination account + let lane = LaneId([0, 0, 0, 0]); + let slash_destination = RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); + T::prepare_rewards_account(slash_destination.clone(), Zero::zero()); + }: { + crate::Pallet::::slash_and_deregister(&relayer, slash_destination) + } + verify { + assert!(!crate::Pallet::::is_registration_active(&relayer)); + } + + // Benchmark `register_relayer_reward` method of the pallet. We are adding this weight to + // the weight of message delivery call if `RefundBridgedParachainMessages` signed extension + // is deployed at runtime level. + register_relayer_reward { + let lane = LaneId([0, 0, 0, 0]); + let relayer: T::AccountId = whitelisted_caller(); + let account_params = + RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); + }: { + crate::Pallet::::register_relayer_reward(account_params.clone(), &relayer, One::one()); + } + verify { + assert_eq!(RelayerRewards::::get(relayer, &account_params), Some(One::one())); + } + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) } diff --git a/modules/relayers/src/lib.rs b/modules/relayers/src/lib.rs index 14e44d30f89..54b888cf29d 100644 --- a/modules/relayers/src/lib.rs +++ b/modules/relayers/src/lib.rs @@ -33,12 +33,14 @@ pub use pallet::*; pub use payment_adapter::DeliveryConfirmationPaymentsAdapter; pub use stake_adapter::StakeAndSlashNamed; pub use weights::WeightInfo; +pub use weights_ext::WeightInfoExt; pub mod benchmarking; mod mock; mod payment_adapter; mod stake_adapter; +mod weights_ext; pub mod weights; @@ -66,7 +68,7 @@ pub mod pallet { /// Stake and slash scheme. type StakeAndSlash: StakeAndSlash; /// Pallet call weights. - type WeightInfo: WeightInfo; + type WeightInfo: WeightInfoExt; } #[pallet::pallet] @@ -114,7 +116,7 @@ pub mod pallet { /// /// Registration allows relayer to get priority boost for its message delivery transactions. #[pallet::call_index(1)] - #[pallet::weight(Weight::zero())] // TODO: https://github.com/paritytech/parity-bridges-common/issues/2033 + #[pallet::weight(T::WeightInfo::register())] pub fn register(origin: OriginFor, valid_till: T::BlockNumber) -> DispatchResult { let relayer = ensure_signed(origin)?; @@ -159,6 +161,7 @@ pub mod pallet { } registration.stake = required_stake; + log::trace!(target: LOG_TARGET, "Successfully registered relayer: {:?}", relayer); Self::deposit_event(Event::::RegistrationUpdated { relayer: relayer.clone(), registration, @@ -175,7 +178,7 @@ pub mod pallet { /// After this call, message delivery transactions of the relayer won't get any priority /// boost. #[pallet::call_index(2)] - #[pallet::weight(Weight::zero())] // TODO: https://github.com/paritytech/parity-bridges-common/issues/2033 + #[pallet::weight(T::WeightInfo::deregister())] pub fn deregister(origin: OriginFor) -> DispatchResult { let relayer = ensure_signed(origin)?; @@ -196,6 +199,7 @@ pub mod pallet { Self::do_unreserve(&relayer, registration.stake)?; } + log::trace!(target: LOG_TARGET, "Successfully deregistered relayer: {:?}", relayer); Self::deposit_event(Event::::Deregistered { relayer: relayer.clone() }); *maybe_registration = None; @@ -326,7 +330,7 @@ pub mod pallet { } /// Return required registration lease. - fn required_registration_lease() -> T::BlockNumber { + pub(crate) fn required_registration_lease() -> T::BlockNumber { T::Reward { + pub(crate) fn required_stake() -> T::Reward { ::rewards_account( account_params, ); - Balances::mint_into(&rewards_account, reward).unwrap(); + Self::deposit_account(rewards_account, reward); + } + + fn deposit_account(account: Self::AccountId, balance: Self::Reward) { + Balances::mint_into(&account, balance.saturating_add(ExistentialDeposit::get())).unwrap(); } } diff --git a/modules/relayers/src/weights.rs b/modules/relayers/src/weights.rs index 1f111aaf136..1bc195a5424 100644 --- a/modules/relayers/src/weights.rs +++ b/modules/relayers/src/weights.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for pallet_bridge_relayers //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-04-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -35,7 +35,7 @@ // --wasm-execution=Compiled // --heap-pages=4096 // --output=./modules/relayers/src/weights.rs -// --template=./.maintain/millau-weight-template.hbs +// --template=./.maintain/bridge-weight-template.hbs #![allow(clippy::all)] #![allow(unused_parens)] @@ -51,6 +51,10 @@ use sp_std::marker::PhantomData; /// Weight functions needed for pallet_bridge_relayers. pub trait WeightInfo { fn claim_rewards() -> Weight; + fn register() -> Weight; + fn deregister() -> Weight; + fn slash_and_deregister() -> Weight; + fn register_relayer_reward() -> Weight; } /// Weights for `pallet_bridge_relayers` that are generated using one of the Bridge testnets. @@ -63,19 +67,96 @@ impl WeightInfo for BridgeWeight { /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, /// mode: MaxEncodedLen) /// + /// Storage: Balances TotalIssuance (r:1 w:0) + /// + /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: + /// MaxEncodedLen) + /// /// Storage: System Account (r:1 w:1) /// - /// Proof: System Account (max_values: None, max_size: Some(96), added: 2571, mode: + /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: /// MaxEncodedLen) fn claim_rewards() -> Weight { // Proof Size summary in bytes: - // Measured: `275` - // Estimated: `5111` - // Minimum execution time: 48_639 nanoseconds. - Weight::from_parts(49_600_000, 5111) + // Measured: `294` + // Estimated: `8592` + // Minimum execution time: 77_614 nanoseconds. + Weight::from_parts(79_987_000, 8592) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) + /// + /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, + /// mode: MaxEncodedLen) + /// + /// Storage: Balances Reserves (r:1 w:1) + /// + /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: + /// MaxEncodedLen) + fn register() -> Weight { + // Proof Size summary in bytes: + // Measured: `87` + // Estimated: `7843` + // Minimum execution time: 39_590 nanoseconds. + Weight::from_parts(40_546_000, 7843) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } + /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) + /// + /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, + /// mode: MaxEncodedLen) + /// + /// Storage: Balances Reserves (r:1 w:1) + /// + /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: + /// MaxEncodedLen) + fn deregister() -> Weight { + // Proof Size summary in bytes: + // Measured: `264` + // Estimated: `7843` + // Minimum execution time: 43_332 nanoseconds. + Weight::from_parts(45_087_000, 7843) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) + /// + /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, + /// mode: MaxEncodedLen) + /// + /// Storage: Balances Reserves (r:1 w:1) + /// + /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: + /// MaxEncodedLen) + /// + /// Storage: System Account (r:1 w:1) + /// + /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: + /// MaxEncodedLen) + fn slash_and_deregister() -> Weight { + // Proof Size summary in bytes: + // Measured: `380` + // Estimated: `11412` + // Minimum execution time: 42_358 nanoseconds. + Weight::from_parts(43_539_000, 11412) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, + /// mode: MaxEncodedLen) + fn register_relayer_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `12` + // Estimated: `3530` + // Minimum execution time: 6_338 nanoseconds. + Weight::from_parts(6_526_000, 3530) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } // For backwards compatibility and tests @@ -85,17 +166,94 @@ impl WeightInfo for () { /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, /// mode: MaxEncodedLen) /// + /// Storage: Balances TotalIssuance (r:1 w:0) + /// + /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: + /// MaxEncodedLen) + /// /// Storage: System Account (r:1 w:1) /// - /// Proof: System Account (max_values: None, max_size: Some(96), added: 2571, mode: + /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: /// MaxEncodedLen) fn claim_rewards() -> Weight { // Proof Size summary in bytes: - // Measured: `275` - // Estimated: `5111` - // Minimum execution time: 48_639 nanoseconds. - Weight::from_parts(49_600_000, 5111) + // Measured: `294` + // Estimated: `8592` + // Minimum execution time: 77_614 nanoseconds. + Weight::from_parts(79_987_000, 8592) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) + /// + /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, + /// mode: MaxEncodedLen) + /// + /// Storage: Balances Reserves (r:1 w:1) + /// + /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: + /// MaxEncodedLen) + fn register() -> Weight { + // Proof Size summary in bytes: + // Measured: `87` + // Estimated: `7843` + // Minimum execution time: 39_590 nanoseconds. + Weight::from_parts(40_546_000, 7843) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } + /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) + /// + /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, + /// mode: MaxEncodedLen) + /// + /// Storage: Balances Reserves (r:1 w:1) + /// + /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: + /// MaxEncodedLen) + fn deregister() -> Weight { + // Proof Size summary in bytes: + // Measured: `264` + // Estimated: `7843` + // Minimum execution time: 43_332 nanoseconds. + Weight::from_parts(45_087_000, 7843) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) + /// + /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, + /// mode: MaxEncodedLen) + /// + /// Storage: Balances Reserves (r:1 w:1) + /// + /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: + /// MaxEncodedLen) + /// + /// Storage: System Account (r:1 w:1) + /// + /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: + /// MaxEncodedLen) + fn slash_and_deregister() -> Weight { + // Proof Size summary in bytes: + // Measured: `380` + // Estimated: `11412` + // Minimum execution time: 42_358 nanoseconds. + Weight::from_parts(43_539_000, 11412) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, + /// mode: MaxEncodedLen) + fn register_relayer_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `12` + // Estimated: `3530` + // Minimum execution time: 6_338 nanoseconds. + Weight::from_parts(6_526_000, 3530) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } diff --git a/modules/relayers/src/weights_ext.rs b/modules/relayers/src/weights_ext.rs new file mode 100644 index 00000000000..d459b0686bd --- /dev/null +++ b/modules/relayers/src/weights_ext.rs @@ -0,0 +1,49 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Weight-related utilities. + +use crate::weights::WeightInfo; + +use frame_support::pallet_prelude::Weight; + +/// Extended weight info. +pub trait WeightInfoExt: WeightInfo { + /// Returns weight, that needs to be added to the pre-dispatch weight of message delivery call, + /// if `RefundBridgedParachainMessages` signed extension is deployed at runtime level. + fn receive_messages_proof_overhead_from_runtime() -> Weight { + Self::slash_and_deregister().max(Self::register_relayer_reward()) + } + + /// Returns weight, that needs to be added to the pre-dispatch weight of message delivery + /// confirmation call, if `RefundBridgedParachainMessages` signed extension is deployed at + /// runtime level. + fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { + Self::register_relayer_reward() + } + + /// Returns weight that we need to deduct from the message delivery call weight that has + /// completed successfully. + /// + /// Usually, the weight of `slash_and_deregister` is larger than the weight of the + /// `register_relayer_reward`. So if relayer has been rewarded, we want to deduct the difference + /// to get the actual post-dispatch weight. + fn extra_weight_of_successful_receive_messages_proof_call() -> Weight { + Self::slash_and_deregister().saturating_sub(Self::register_relayer_reward()) + } +} + +impl WeightInfoExt for T {} diff --git a/modules/shift-session-manager/Cargo.toml b/modules/shift-session-manager/Cargo.toml index 2d7dc272a6f..1a14f230c6b 100644 --- a/modules/shift-session-manager/Cargo.toml +++ b/modules/shift-session-manager/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } # Substrate Dependencies diff --git a/primitives/beefy/Cargo.toml b/primitives/beefy/Cargo.toml index 9039064fbf4..3316bc09d51 100644 --- a/primitives/beefy/Cargo.toml +++ b/primitives/beefy/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "bit-vec"] } -scale-info = { version = "2.5.0", default-features = false, features = ["bit-vec", "derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["bit-vec", "derive"] } serde = { version = "1.0", optional = true } # Bridge Dependencies diff --git a/primitives/chain-millau/Cargo.toml b/primitives/chain-millau/Cargo.toml index d1e2e0edd96..7c5c5e11345 100644 --- a/primitives/chain-millau/Cargo.toml +++ b/primitives/chain-millau/Cargo.toml @@ -13,7 +13,7 @@ hash256-std-hasher = { version = "0.15.2", default-features = false } impl-codec = { version = "0.6", default-features = false } impl-serde = { version = "0.4.0", optional = true } parity-util-mem = { version = "0.12.0", default-features = false, features = ["primitive-types"] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } # Bridge Dependencies diff --git a/primitives/header-chain/Cargo.toml b/primitives/header-chain/Cargo.toml index 5b9f87614a8..e0349ebc9b9 100644 --- a/primitives/header-chain/Cargo.toml +++ b/primitives/header-chain/Cargo.toml @@ -9,7 +9,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } finality-grandpa = { version = "0.16.2", default-features = false } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true } # Bridge dependencies diff --git a/primitives/messages/Cargo.toml b/primitives/messages/Cargo.toml index 32d7c65ebcb..32a89f6cf78 100644 --- a/primitives/messages/Cargo.toml +++ b/primitives/messages/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive", "bit-vec"] } -scale-info = { version = "2.5.0", default-features = false, features = ["bit-vec", "derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["bit-vec", "derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } # Bridge dependencies diff --git a/primitives/messages/src/lib.rs b/primitives/messages/src/lib.rs index 2828d5af006..e485aa2f801 100644 --- a/primitives/messages/src/lib.rs +++ b/primitives/messages/src/lib.rs @@ -316,6 +316,21 @@ pub struct UnrewardedRelayersState { pub last_delivered_nonce: MessageNonce, } +impl From<&InboundLaneData> for UnrewardedRelayersState { + fn from(lane: &InboundLaneData) -> UnrewardedRelayersState { + UnrewardedRelayersState { + unrewarded_relayer_entries: lane.relayers.len() as _, + messages_in_oldest_entry: lane + .relayers + .front() + .and_then(|entry| (entry.messages.begin..=entry.messages.end).checked_len()) + .unwrap_or(0), + total_messages: total_unrewarded_messages(&lane.relayers).unwrap_or(MessageNonce::MAX), + last_delivered_nonce: lane.last_delivered_nonce(), + } + } +} + /// Outbound lane data. #[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] pub struct OutboundLaneData { diff --git a/primitives/parachains/Cargo.toml b/primitives/parachains/Cargo.toml index e47b8c5e68c..426597a2508 100644 --- a/primitives/parachains/Cargo.toml +++ b/primitives/parachains/Cargo.toml @@ -9,7 +9,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2" -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } # Bridge dependencies diff --git a/primitives/polkadot-core/Cargo.toml b/primitives/polkadot-core/Cargo.toml index daae99ec71c..56c6de04d41 100644 --- a/primitives/polkadot-core/Cargo.toml +++ b/primitives/polkadot-core/Cargo.toml @@ -9,7 +9,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive"] } parity-util-mem = { version = "0.12.0", optional = true } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } # Bridge Dependencies diff --git a/primitives/relayers/Cargo.toml b/primitives/relayers/Cargo.toml index 8ac31258488..b84b0393adf 100644 --- a/primitives/relayers/Cargo.toml +++ b/primitives/relayers/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false, features = ["derive", "bit-vec"] } -scale-info = { version = "2.5.0", default-features = false, features = ["bit-vec", "derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["bit-vec", "derive"] } # Bridge Dependencies diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index 4d48ad61894..694ff4e1aa6 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -11,7 +11,7 @@ codec = { package = "parity-scale-codec", version = "3.1.5", default-features = hash-db = { version = "0.16.0", default-features = false } impl-trait-for-tuples = "0.2.2" num-traits = { version = "0.2", default-features = false } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } # Substrate Dependencies diff --git a/relays/bin-substrate/Cargo.toml b/relays/bin-substrate/Cargo.toml index 7853b9cb599..0a315035042 100644 --- a/relays/bin-substrate/Cargo.toml +++ b/relays/bin-substrate/Cargo.toml @@ -49,6 +49,9 @@ relay-utils = { path = "../utils" } relay-westend-client = { path = "../client-westend" } relay-wococo-client = { path = "../client-wococo" } rialto-runtime = { path = "../../bin/rialto/runtime" } +# we are not using this runtime to craft callsour transactions, but we still need it +# to prepare large XCM messages +rialto-parachain-runtime = { path = "../../bin/rialto-parachain/runtime" } substrate-relay-helper = { path = "../lib-substrate-relay" } # Substrate Dependencies @@ -62,8 +65,8 @@ polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" } polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", branch = "master" } polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "master" } -xcm = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } - +xcm = { git = "https://github.com/paritytech/polkadot", branch = "master" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "master" } [dev-dependencies] bp-test-utils = { path = "../../primitives/test-utils" } diff --git a/relays/bin-substrate/src/chains/millau.rs b/relays/bin-substrate/src/chains/millau.rs index 44416195c6a..4cee26255d3 100644 --- a/relays/bin-substrate/src/chains/millau.rs +++ b/relays/bin-substrate/src/chains/millau.rs @@ -18,10 +18,38 @@ use crate::cli::{encode_message::CliEncodeMessage, CliChain}; use bp_runtime::EncodedOrDecodedCall; +use bridge_runtime_common::CustomNetworkId; use relay_millau_client::Millau; use relay_substrate_client::SimpleRuntimeVersion; +use xcm_executor::traits::ExportXcm; impl CliEncodeMessage for Millau { + fn encode_wire_message( + target: xcm::v3::NetworkId, + at_target_xcm: xcm::v3::Xcm<()>, + ) -> anyhow::Result> { + anyhow::ensure!( + [ + CustomNetworkId::Rialto.as_network_id(), + CustomNetworkId::RialtoParachain.as_network_id() + ] + .contains(&target), + anyhow::format_err!("Unsupported target chain: {:?}", target) + ); + + Ok(millau_runtime::xcm_config::ToRialtoOrRialtoParachainSwitchExporter::validate( + target, + 0, + &mut Some(Self::dummy_universal_source()?), + &mut Some(target.into()), + &mut Some(at_target_xcm), + ) + .map_err(|e| anyhow::format_err!("Failed to prepare outbound message: {:?}", e))? + .0 + .1 + .0) + } + fn encode_execute_xcm( message: xcm::VersionedXcm, ) -> anyhow::Result> { diff --git a/relays/bin-substrate/src/chains/rialto.rs b/relays/bin-substrate/src/chains/rialto.rs index 34a448ae4cb..30bc7eb13ca 100644 --- a/relays/bin-substrate/src/chains/rialto.rs +++ b/relays/bin-substrate/src/chains/rialto.rs @@ -18,10 +18,33 @@ use crate::cli::{encode_message::CliEncodeMessage, CliChain}; use bp_runtime::EncodedOrDecodedCall; +use bridge_runtime_common::CustomNetworkId; use relay_rialto_client::Rialto; use relay_substrate_client::SimpleRuntimeVersion; +use xcm_executor::traits::ExportXcm; impl CliEncodeMessage for Rialto { + fn encode_wire_message( + target: xcm::v3::NetworkId, + at_target_xcm: xcm::v3::Xcm<()>, + ) -> anyhow::Result> { + anyhow::ensure!( + target == CustomNetworkId::Millau.as_network_id(), + anyhow::format_err!("Unsupported target chain: {:?}", target) + ); + + Ok(rialto_runtime::millau_messages::ToMillauBlobExporter::validate( + target, + 0, + &mut Some(Self::dummy_universal_source()?), + &mut Some(target.into()), + &mut Some(at_target_xcm), + ) + .map_err(|e| anyhow::format_err!("Failed to prepare outbound message: {:?}", e))? + .0 + .0) + } + fn encode_execute_xcm( message: xcm::VersionedXcm, ) -> anyhow::Result> { diff --git a/relays/bin-substrate/src/chains/rialto_parachain.rs b/relays/bin-substrate/src/chains/rialto_parachain.rs index 8ea2c1ffd43..872d96981d0 100644 --- a/relays/bin-substrate/src/chains/rialto_parachain.rs +++ b/relays/bin-substrate/src/chains/rialto_parachain.rs @@ -18,10 +18,33 @@ use crate::cli::{encode_message::CliEncodeMessage, CliChain}; use bp_runtime::EncodedOrDecodedCall; +use bridge_runtime_common::CustomNetworkId; use relay_rialto_parachain_client::RialtoParachain; use relay_substrate_client::SimpleRuntimeVersion; +use xcm_executor::traits::ExportXcm; impl CliEncodeMessage for RialtoParachain { + fn encode_wire_message( + target: xcm::v3::NetworkId, + at_target_xcm: xcm::v3::Xcm<()>, + ) -> anyhow::Result> { + anyhow::ensure!( + target == CustomNetworkId::Millau.as_network_id(), + anyhow::format_err!("Unsupported target chain: {:?}", target) + ); + + Ok(rialto_parachain_runtime::millau_messages::ToMillauBlobExporter::validate( + target, + 0, + &mut Some(Self::dummy_universal_source()?), + &mut Some(target.into()), + &mut Some(at_target_xcm), + ) + .map_err(|e| anyhow::format_err!("Failed to prepare outbound message: {:?}", e))? + .0 + .0) + } + fn encode_execute_xcm( message: xcm::VersionedXcm, ) -> anyhow::Result> { diff --git a/relays/bin-substrate/src/cli/encode_message.rs b/relays/bin-substrate/src/cli/encode_message.rs index 9abf8b2df6d..646075e8326 100644 --- a/relays/bin-substrate/src/cli/encode_message.rs +++ b/relays/bin-substrate/src/cli/encode_message.rs @@ -16,10 +16,12 @@ use crate::cli::{ExplicitOrMaximal, HexBytes}; use bp_runtime::EncodedOrDecodedCall; +use bridge_runtime_common::CustomNetworkId; use codec::Encode; use frame_support::weights::Weight; use relay_substrate_client::Chain; use structopt::StructOpt; +use xcm::latest::prelude::*; /// All possible messages that may be delivered to generic Substrate chain. /// @@ -43,6 +45,22 @@ pub enum Message { pub type RawMessage = Vec; pub trait CliEncodeMessage: Chain { + /// Returns dummy `AccountId32` universal source given this network id. + fn dummy_universal_source() -> anyhow::Result { + use xcm::v3::prelude::*; + + let this_network = CustomNetworkId::try_from(Self::ID) + .map(|n| n.as_network_id()) + .map_err(|_| anyhow::format_err!("Unsupported chain: {:?}", Self::ID))?; + Ok(X2( + GlobalConsensus(this_network), + AccountId32 { network: Some(this_network), id: [0u8; 32] }, + )) + } + + /// Returns XCM blob that is passed to the `send_message` function of the messages pallet + /// and then is sent over the wire. + fn encode_wire_message(target: NetworkId, at_target_xcm: Xcm<()>) -> anyhow::Result>; /// Encode an `execute` XCM call of the XCM pallet. fn encode_execute_xcm( message: xcm::VersionedXcm, @@ -56,41 +74,52 @@ pub trait CliEncodeMessage: Chain { } /// Encode message payload passed through CLI flags. -pub(crate) fn encode_message( +pub(crate) fn encode_message( message: &Message, ) -> anyhow::Result { Ok(match message { Message::Raw { ref data } => data.0.clone(), Message::Sized { ref size } => { - let expected_xcm_size = match *size { + let destination = CustomNetworkId::try_from(Target::ID) + .map(|n| n.as_network_id()) + .map_err(|_| anyhow::format_err!("Unsupported target chain: {:?}", Target::ID))?; + let expected_size = match *size { ExplicitOrMaximal::Explicit(size) => size, ExplicitOrMaximal::Maximal => compute_maximal_message_size( Source::max_extrinsic_size(), Target::max_extrinsic_size(), ), - }; - - // there's no way to craft XCM of the given size - we'll be using `ExpectPallet` - // instruction, which has byte vector inside - let mut current_vec_size = expected_xcm_size; - let xcm = loop { - let xcm = xcm::VersionedXcm::<()>::V3( - vec![xcm::v3::Instruction::ExpectPallet { - index: 0, - name: vec![42; current_vec_size as usize], - module_name: vec![], - crate_major: 0, - min_crate_minor: 0, - }] - .into(), - ); - if xcm.encode().len() <= expected_xcm_size as usize { - break xcm - } - - current_vec_size -= 1; - }; - xcm.encode() + } as usize; + + let at_target_xcm = vec![ExpectPallet { + index: 0, + name: vec![42; expected_size], + module_name: vec![], + crate_major: 0, + min_crate_minor: 0, + }] + .into(); + let at_target_xcm_size = + Source::encode_wire_message(destination, at_target_xcm)?.encoded_size(); + let at_target_xcm_overhead = at_target_xcm_size.saturating_sub(expected_size); + let at_target_xcm = vec![ExpectPallet { + index: 0, + name: vec![42; expected_size.saturating_sub(at_target_xcm_overhead)], + module_name: vec![], + crate_major: 0, + min_crate_minor: 0, + }] + .into(); + + xcm::VersionedXcm::<()>::V3( + vec![ExportMessage { + network: destination, + destination: destination.into(), + xcm: at_target_xcm, + }] + .into(), + ) + .encode() }, }) } @@ -108,11 +137,7 @@ pub(crate) fn compute_maximal_message_size( bridge_runtime_common::messages::target::maximal_incoming_message_size( maximal_target_extrinsic_size, ); - if maximal_message_size > maximal_source_extrinsic_size { - maximal_source_extrinsic_size - } else { - maximal_message_size - } + std::cmp::min(maximal_message_size, maximal_source_extrinsic_size) } #[cfg(test)] @@ -123,13 +148,21 @@ mod tests { use relay_millau_client::Millau; use relay_rialto_client::Rialto; + fn approximate_message_size(xcm_msg_len: usize) -> usize { + xcm_msg_len + Source::dummy_universal_source().unwrap().encoded_size() + } + #[test] fn encode_explicit_size_message_works() { let msg = encode_message::(&Message::Sized { size: ExplicitOrMaximal::Explicit(100), }) .unwrap(); - assert_eq!(msg.len(), 100); + // since it isn't the returned XCM what is sent over the wire, we can only check if + // it is close to what we need + assert!( + (1f64 - (approximate_message_size::(msg.len()) as f64) / 100_f64).abs() < 0.1 + ); // check that it decodes to valid xcm let _ = decode_xcm::<()>(msg).unwrap(); } @@ -144,7 +177,12 @@ mod tests { let msg = encode_message::(&Message::Sized { size: ExplicitOrMaximal::Maximal }) .unwrap(); - assert_eq!(msg.len(), maximal_size as usize); + // since it isn't the returned XCM what is sent over the wire, we can only check if + // it is close to what we need + assert!( + (1f64 - approximate_message_size::(msg.len()) as f64 / maximal_size as f64) + .abs() < 0.1 + ); // check that it decodes to valid xcm let _ = decode_xcm::<()>(msg).unwrap(); } diff --git a/relays/client-bridge-hub-kusama/Cargo.toml b/relays/client-bridge-hub-kusama/Cargo.toml index 96650710f67..f6e2c853a10 100644 --- a/relays/client-bridge-hub-kusama/Cargo.toml +++ b/relays/client-bridge-hub-kusama/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } relay-substrate-client = { path = "../client-substrate" } # Bridge dependencies diff --git a/relays/client-bridge-hub-polkadot/Cargo.toml b/relays/client-bridge-hub-polkadot/Cargo.toml index 6126a8f2b3f..2b3ec0b6bad 100644 --- a/relays/client-bridge-hub-polkadot/Cargo.toml +++ b/relays/client-bridge-hub-polkadot/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } relay-substrate-client = { path = "../client-substrate" } # Bridge dependencies diff --git a/relays/client-bridge-hub-rococo/Cargo.toml b/relays/client-bridge-hub-rococo/Cargo.toml index 48df2e56cf0..5826a1321bc 100644 --- a/relays/client-bridge-hub-rococo/Cargo.toml +++ b/relays/client-bridge-hub-rococo/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } relay-substrate-client = { path = "../client-substrate" } # Bridge dependencies diff --git a/relays/client-bridge-hub-wococo/Cargo.toml b/relays/client-bridge-hub-wococo/Cargo.toml index d4578fcd488..72a25b001e5 100644 --- a/relays/client-bridge-hub-wococo/Cargo.toml +++ b/relays/client-bridge-hub-wococo/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } relay-substrate-client = { path = "../client-substrate" } # Bridge dependencies diff --git a/relays/client-rialto-parachain/Cargo.toml b/relays/client-rialto-parachain/Cargo.toml index b6d5c423336..789e0622cfc 100644 --- a/relays/client-rialto-parachain/Cargo.toml +++ b/relays/client-rialto-parachain/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5" } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } subxt = { version = "0.28.0", default-features = false, features = [] } # Bridge dependencies diff --git a/relays/client-substrate/Cargo.toml b/relays/client-substrate/Cargo.toml index 90904ab9be1..3dfeb9decbd 100644 --- a/relays/client-substrate/Cargo.toml +++ b/relays/client-substrate/Cargo.toml @@ -14,8 +14,8 @@ jsonrpsee = { version = "0.17", features = ["macros", "ws-client"] } log = "0.4.17" num-traits = "0.2" rand = "0.8" -scale-info = { version = "2.5.0", features = ["derive"] } -tokio = { version = "1.27", features = ["rt-multi-thread"] } +scale-info = { version = "2.6.0", features = ["derive"] } +tokio = { version = "1.28", features = ["rt-multi-thread"] } thiserror = "1.0.40" # Bridge dependencies diff --git a/relays/utils/Cargo.toml b/relays/utils/Cargo.toml index aa14e2ae2e3..78b7fd8fe97 100644 --- a/relays/utils/Cargo.toml +++ b/relays/utils/Cargo.toml @@ -20,7 +20,7 @@ num-traits = "0.2" serde_json = "1.0" sysinfo = "0.28" time = { version = "0.3", features = ["formatting", "local-offset", "std"] } -tokio = { version = "1.27", features = ["rt"] } +tokio = { version = "1.28", features = ["rt"] } thiserror = "1.0.40" # Bridge dependencies diff --git a/scripts/update-weights.sh b/scripts/update-weights.sh index 84ddf0cdaae..b5808042f48 100755 --- a/scripts/update-weights.sh +++ b/scripts/update-weights.sh @@ -6,17 +6,19 @@ set -eux +# default (test) weights that we'll bundle with our pallets + time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet \ --chain=dev \ --steps=50 \ --repeat=20 \ - --pallet=RialtoMessages \ + --pallet=pallet_bridge_messages \ --extrinsic=* \ --execution=wasm \ --wasm-execution=Compiled \ --heap-pages=4096 \ --output=./modules/messages/src/weights.rs \ - --template=./.maintain/millau-weight-template.hbs + --template=./.maintain/bridge-weight-template.hbs time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet \ --chain=dev \ @@ -28,7 +30,7 @@ time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- --wasm-execution=Compiled \ --heap-pages=4096 \ --output=./modules/grandpa/src/weights.rs \ - --template=./.maintain/millau-weight-template.hbs + --template=./.maintain/bridge-weight-template.hbs time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet \ --chain=dev \ @@ -40,7 +42,7 @@ time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- --wasm-execution=Compiled \ --heap-pages=4096 \ --output=./modules/parachains/src/weights.rs \ - --template=./.maintain/millau-weight-template.hbs + --template=./.maintain/bridge-weight-template.hbs time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet \ --chain=dev \ @@ -52,4 +54,19 @@ time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- --wasm-execution=Compiled \ --heap-pages=4096 \ --output=./modules/relayers/src/weights.rs \ - --template=./.maintain/millau-weight-template.hbs + --template=./.maintain/bridge-weight-template.hbs + +# weights for Millau runtime. We want to provide runtime weight overhead for messages calls, +# so we can't use "default" test weights directly - they'll be rejected by our integration tests. + +time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark pallet \ + --chain=dev \ + --steps=50 \ + --repeat=20 \ + --pallet=pallet_bridge_messages \ + --extrinsic=* \ + --execution=wasm \ + --wasm-execution=Compiled \ + --heap-pages=4096 \ + --output=./bin/millau/runtime/src/weights/ + diff --git a/tools/runtime-codegen/Cargo.lock b/tools/runtime-codegen/Cargo.lock index 8b103a9b944..f4c31636572 100644 --- a/tools/runtime-codegen/Cargo.lock +++ b/tools/runtime-codegen/Cargo.lock @@ -516,18 +516,18 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7379abaacee0f14abf3204a7606118f0465785252169d186337bcb75030815a" +checksum = "2bc42ba2e232e5b20ff7dc299a812d53337dadce9a7e39a238e6a5cb82d2e57b" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9489fa336927df749631f1008007ced2871068544f40a202ce6d93fbf2366a7b" +checksum = "253531aca9b6f56103c9420369db3263e784df39aa1c90685a1f69cfbba0623e" dependencies = [ "arrayvec 0.7.2", "bumpalo", @@ -546,33 +546,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05bbb67da91ec721ed57cef2f7c5ef7728e1cd9bde9ffd3ef8601022e73e3239" +checksum = "72f2154365e2bff1b1b8537a7181591fdff50d8e27fa6e40d5c69c3bad0ca7c8" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418ecb2f36032f6665dc1a5e2060a143dbab41d83b784882e97710e890a7a16d" +checksum = "687e14e3f5775248930e0d5a84195abef8b829958e9794bf8d525104993612b4" [[package]] name = "cranelift-entity" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cf583f7b093f291005f9fb1323e2c37f6ee4c7909e39ce016b2e8360d461705" +checksum = "f42ea692c7b450ad18b8c9889661505d51c09ec4380cf1c2d278dbb2da22cae1" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b66bf9e916f57fbbd0f7703ec6286f4624866bf45000111627c70d272c8dda1" +checksum = "8483c2db6f45fe9ace984e5adc5d058102227e4c62e5aa2054e16b0275fd3a6e" dependencies = [ "cranelift-codegen", "log", @@ -582,15 +582,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "649782a39ce99798dd6b4029e2bb318a2fbeaade1b4fa25330763c10c65bc358" +checksum = "e9793158837678902446c411741d87b43f57dadfb944f2440db4287cda8cbd59" [[package]] name = "cranelift-native" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "937e021e089c51f9749d09e7ad1c4f255c2f8686cb8c3df63a34b3ec9921bc41" +checksum = "72668c7755f2b880665cb422c8ad2d56db58a88b9bebfef0b73edc2277c13c49" dependencies = [ "cranelift-codegen", "libc", @@ -599,9 +599,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d850cf6775477747c9dfda9ae23355dd70512ffebc70cf82b85a5b111ae668b5" +checksum = "3852ce4b088b44ac4e29459573943009a70d1b192c8d77ef949b4e814f656fc1" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -3989,7 +3989,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.6", - "rand 0.8.5", + "rand 0.7.3", "static_assertions", ] @@ -4256,9 +4256,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e89f9819523447330ffd70367ef4a18d8c832e24e8150fe054d1d912841632" +checksum = "76a222f5fa1e14b2cefc286f1b68494d7a965f4bf57ec04c59bb62673d639af6" dependencies = [ "anyhow", "bincode", @@ -4284,18 +4284,18 @@ dependencies = [ [[package]] name = "wasmtime-asm-macros" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd3a5e46c198032da934469f3a6e48649d1f9142438e4fd4617b68a35644b8a" +checksum = "4407a7246e7d2f3d8fb1cf0c72fda8dbafdb6dd34d555ae8bea0e5ae031089cc" dependencies = [ "cfg-if", ] [[package]] name = "wasmtime-cache" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b389ae9b678b9c3851091a4804f4182d688d27aff7abc9aa37fa7be37d8ecffa" +checksum = "5ceb3adf61d654be0be67fffdce42447b0880481348785be5fe40b5dd7663a4c" dependencies = [ "anyhow", "base64 0.13.1", @@ -4313,9 +4313,9 @@ dependencies = [ [[package]] name = "wasmtime-cranelift" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b2c92a08c0db6efffd88fdc97d7aa9c7c63b03edb0971dbca745469f820e8c" +checksum = "3c366bb8647e01fd08cb5589976284b00abfded5529b33d7e7f3f086c68304a4" dependencies = [ "anyhow", "cranelift-codegen", @@ -4334,9 +4334,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a6db9fc52985ba06ca601f2ff0ff1f526c5d724c7ac267b47326304b0c97883" +checksum = "47b8b50962eae38ee319f7b24900b7cf371f03eebdc17400c1dc8575fc10c9a7" dependencies = [ "anyhow", "cranelift-entity", @@ -4353,9 +4353,9 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b77e3a52cd84d0f7f18554afa8060cfe564ccac61e3b0802d3fd4084772fa5f6" +checksum = "ffaed4f9a234ba5225d8e64eac7b4a5d13b994aeb37353cde2cbeb3febda9eaa" dependencies = [ "addr2line 0.17.0", "anyhow", @@ -4377,9 +4377,9 @@ dependencies = [ [[package]] name = "wasmtime-jit-debug" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0245e8a9347017c7185a72e215218a802ff561545c242953c11ba00fccc930f" +checksum = "eed41cbcbf74ce3ff6f1d07d1b707888166dc408d1a880f651268f4f7c9194b2" dependencies = [ "object 0.29.0", "once_cell", @@ -4388,9 +4388,9 @@ dependencies = [ [[package]] name = "wasmtime-jit-icache-coherence" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67d412e9340ab1c83867051d8d1d7c90aa8c9afc91da086088068e2734e25064" +checksum = "43a28ae1e648461bfdbb79db3efdaee1bca5b940872e4175390f465593a2e54c" dependencies = [ "cfg-if", "libc", @@ -4399,9 +4399,9 @@ dependencies = [ [[package]] name = "wasmtime-runtime" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d594e791b5fdd4dbaf8cf7ae62f2e4ff85018ce90f483ca6f42947688e48827d" +checksum = "e704b126e4252788ccfc3526d4d4511d4b23c521bf123e447ac726c14545217b" dependencies = [ "anyhow", "cc", @@ -4423,9 +4423,9 @@ dependencies = [ [[package]] name = "wasmtime-types" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6688d6f96d4dbc1f89fab626c56c1778936d122b5f4ae7a57c2eb42b8d982e2" +checksum = "83e5572c5727c1ee7e8f28717aaa8400e4d22dcbd714ea5457d85b5005206568" dependencies = [ "cranelift-entity", "serde", From b93712d738bbf6ce1420e5039a6174f67897119d Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 3 May 2023 10:56:00 +0200 Subject: [PATCH 254/263] fmt --- client/network/src/lib.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index 5a2043ab5f7..e226170d7c5 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -89,14 +89,13 @@ impl BlockAnnounceData { /// /// This will not check the signature, for this you should use [`BlockAnnounceData::check_signature`]. fn validate(&self, encoded_header: Vec) -> Result<(), Validation> { - let candidate_hash = if let CompactStatement::Seconded(h) = - self.statement.unchecked_payload() - { - h - } else { - tracing::debug!(target: LOG_TARGET, "`CompactStatement` isn't the candidate variant!",); - return Err(Validation::Failure { disconnect: true }) - }; + let candidate_hash = + if let CompactStatement::Seconded(h) = self.statement.unchecked_payload() { + h + } else { + tracing::debug!(target: LOG_TARGET, "`CompactStatement` isn't the candidate variant!",); + return Err(Validation::Failure { disconnect: true }) + }; if *candidate_hash != self.receipt.hash() { tracing::debug!( @@ -303,9 +302,9 @@ where Ok(Validation::Success { is_new_best: true }) } else if block_number >= known_best_number { tracing::debug!( - target: LOG_TARGET, - "Validation failed because a justification is needed if the block at the top of the chain." - ); + target: LOG_TARGET, + "Validation failed because a justification is needed if the block at the top of the chain." + ); Ok(Validation::Failure { disconnect: false }) } else { @@ -334,9 +333,9 @@ where let relay_chain_is_syncing = relay_chain_interface .is_major_syncing() .await - .map_err(|e| { - tracing::error!(target: LOG_TARGET, "Unable to determine sync status. {}", e) - }) + .map_err( + |e| tracing::error!(target: LOG_TARGET, "Unable to determine sync status. {}", e), + ) .unwrap_or(false); if relay_chain_is_syncing { From 8afd1fb576ca45479b16d2d003ec70e429f37aac Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 3 May 2023 12:02:15 +0200 Subject: [PATCH 255/263] Fix compile --- .../bridge-hub-rococo/src/weights/mod.rs | 22 ++++++ .../src/weights/pallet_bridge_relayers.rs | 72 +++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs index 59f3932ad2b..286c8010f8a 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs @@ -44,16 +44,38 @@ pub use extrinsic_weights::constants::ExtrinsicBaseWeight; pub use paritydb_weights::constants::ParityDbWeight; pub use rocksdb_weights::constants::RocksDbWeight; +use crate::Runtime; +use frame_support::weights::Weight; + +// import trait from dependency module +use ::pallet_bridge_relayers::WeightInfoExt as _; + impl pallet_bridge_messages::WeightInfoExt for pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance::WeightInfo { fn expected_extra_storage_proof_size() -> u32 { bp_bridge_hub_rococo::EXTRA_STORAGE_PROOF_SIZE } + + fn receive_messages_proof_overhead_from_runtime() -> Weight { + pallet_bridge_relayers::WeightInfo::::receive_messages_proof_overhead_from_runtime() + } + + fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { + pallet_bridge_relayers::WeightInfo::::receive_messages_delivery_proof_overhead_from_runtime() + } } impl pallet_bridge_messages::WeightInfoExt for pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance::WeightInfo { fn expected_extra_storage_proof_size() -> u32 { bp_bridge_hub_wococo::EXTRA_STORAGE_PROOF_SIZE } + + fn receive_messages_proof_overhead_from_runtime() -> Weight { + pallet_bridge_relayers::WeightInfo::::receive_messages_proof_overhead_from_runtime() + } + + fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { + pallet_bridge_relayers::WeightInfo::::receive_messages_delivery_proof_overhead_from_runtime() + } } impl pallet_bridge_parachains::WeightInfoExt for pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance::WeightInfo { diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs index 3959e424c26..2b4b5ac716b 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs @@ -62,4 +62,76 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } + /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) + /// + /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, + /// mode: MaxEncodedLen) + /// + /// Storage: Balances Reserves (r:1 w:1) + /// + /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: + /// MaxEncodedLen) + fn register() -> Weight { + // Proof Size summary in bytes: + // Measured: `87` + // Estimated: `7843` + // Minimum execution time: 39_590 nanoseconds. + Weight::from_parts(40_546_000, 7843) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) + /// + /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, + /// mode: MaxEncodedLen) + /// + /// Storage: Balances Reserves (r:1 w:1) + /// + /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: + /// MaxEncodedLen) + fn deregister() -> Weight { + // Proof Size summary in bytes: + // Measured: `264` + // Estimated: `7843` + // Minimum execution time: 43_332 nanoseconds. + Weight::from_parts(45_087_000, 7843) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) + /// + /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, + /// mode: MaxEncodedLen) + /// + /// Storage: Balances Reserves (r:1 w:1) + /// + /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: + /// MaxEncodedLen) + /// + /// Storage: System Account (r:1 w:1) + /// + /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: + /// MaxEncodedLen) + fn slash_and_deregister() -> Weight { + // Proof Size summary in bytes: + // Measured: `380` + // Estimated: `11412` + // Minimum execution time: 42_358 nanoseconds. + Weight::from_parts(43_539_000, 11412) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) + /// + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, + /// mode: MaxEncodedLen) + fn register_relayer_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `12` + // Estimated: `3530` + // Minimum execution time: 6_338 nanoseconds. + Weight::from_parts(6_526_000, 3530) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } From 6612cae69215a07e1f771ce6b0e777d2b215101c Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 3 May 2023 12:23:27 +0200 Subject: [PATCH 256/263] Fix benchmark --- .../runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index f343fd185e6..21f02ce4765 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -1141,16 +1141,20 @@ impl_runtime_apis! { } impl BridgeRelayersConfig for Runtime { - fn prepare_environment( + fn prepare_rewards_account( account_params: bp_relayers::RewardsAccountParams, reward: Balance, ) { - use frame_support::traits::fungible::Mutate; let rewards_account = bp_relayers::PayRewardFromAccount::< Balances, AccountId >::rewards_account(account_params); - Balances::mint_into(&rewards_account, reward).unwrap(); + Self::deposit_account(rewards_account, reward); + } + + fn deposit_account(account: AccountId, balance: Balance) { + use frame_support::traits::fungible::Mutate; + Balances::mint_into(&account, balance.saturating_add(ExistentialDeposit::get())).unwrap(); } } From 65ee2dc77f5c0832f48b5331371483ff5c3c2574 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 3 May 2023 15:45:49 +0200 Subject: [PATCH 257/263] Squashed 'bridges/' changes from 557ecbcecc..04b3dda6aa 04b3dda6aa Remove from subtree (#2111) f8ff15e7e7 Add `MessagesPalletInstance` for integrity tests (#2107) 92ccef58e6 Use generated runtimes for BHR/BHW (#2106) b33e0a585b Fix comment (#2105) git-subtree-dir: bridges git-subtree-split: 04b3dda6aa38599e612ff637710b6d2cff275ef3 --- Cargo.lock | 6 + bin/millau/runtime/src/rialto_messages.rs | 2 +- .../runtime/src/rialto_parachain_messages.rs | 2 +- .../runtime/src/millau_messages.rs | 6 +- bin/rialto/runtime/src/millau_messages.rs | 2 +- bin/runtime-common/src/integrity.rs | 18 +- relays/bin-substrate/Cargo.toml | 2 +- ...ub_rococo_messages_to_bridge_hub_wococo.rs | 8 +- ...ub_wococo_messages_to_bridge_hub_rococo.rs | 8 +- .../rococo_headers_to_bridge_hub_wococo.rs | 4 +- .../rococo_parachains_to_bridge_hub_wococo.rs | 4 +- .../wococo_headers_to_bridge_hub_rococo.rs | 4 +- .../wococo_parachains_to_bridge_hub_rococo.rs | 4 +- relays/bin-substrate/src/cli/init_bridge.rs | 12 +- relays/client-bridge-hub-rococo/Cargo.toml | 5 +- .../src/codegen_runtime.rs | 3348 +++++++++++++++++ relays/client-bridge-hub-rococo/src/lib.rs | 48 +- .../src/runtime_wrapper.rs | 69 - relays/client-bridge-hub-wococo/Cargo.toml | 7 +- relays/client-bridge-hub-wococo/src/lib.rs | 27 +- .../src/runtime_wrapper.rs | 115 - .../src/codegen_runtime.rs | 393 +- relays/client-rialto-parachain/src/lib.rs | 2 +- scripts/regenerate_runtimes.sh | 7 + scripts/verify-pallets-build.sh | 1 + tools/runtime-codegen/README.md | 4 + tools/runtime-codegen/src/main.rs | 18 +- 27 files changed, 3696 insertions(+), 430 deletions(-) create mode 100644 relays/client-bridge-hub-rococo/src/codegen_runtime.rs delete mode 100644 relays/client-bridge-hub-rococo/src/runtime_wrapper.rs delete mode 100644 relays/client-bridge-hub-wococo/src/runtime_wrapper.rs create mode 100755 scripts/regenerate_runtimes.sh diff --git a/Cargo.lock b/Cargo.lock index a5202956796..4cb61ba22b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9643,6 +9643,7 @@ dependencies = [ "bp-header-chain", "bp-messages", "bp-parachains", + "bp-polkadot-core", "bp-runtime", "bp-wococo", "bridge-runtime-common", @@ -9651,6 +9652,8 @@ dependencies = [ "scale-info", "sp-core", "sp-runtime", + "sp-weights", + "subxt", ] [[package]] @@ -9667,11 +9670,14 @@ dependencies = [ "bp-runtime", "bridge-runtime-common", "parity-scale-codec", + "relay-bridge-hub-rococo-client", "relay-substrate-client", "scale-info", "sp-consensus-grandpa", "sp-core", "sp-runtime", + "sp-weights", + "subxt", ] [[package]] diff --git a/bin/millau/runtime/src/rialto_messages.rs b/bin/millau/runtime/src/rialto_messages.rs index b71eb76d90a..04084e0b952 100644 --- a/bin/millau/runtime/src/rialto_messages.rs +++ b/bin/millau/runtime/src/rialto_messages.rs @@ -166,7 +166,7 @@ mod tests { #[test] fn ensure_millau_message_lane_weights_are_correct() { - check_message_lane_weights::( + check_message_lane_weights::( bp_rialto::EXTRA_STORAGE_PROOF_SIZE, bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, diff --git a/bin/millau/runtime/src/rialto_parachain_messages.rs b/bin/millau/runtime/src/rialto_parachain_messages.rs index 8d5216d96c9..1050f500fe8 100644 --- a/bin/millau/runtime/src/rialto_parachain_messages.rs +++ b/bin/millau/runtime/src/rialto_parachain_messages.rs @@ -172,7 +172,7 @@ mod tests { #[test] fn ensure_millau_message_lane_weights_are_correct() { - check_message_lane_weights::( + check_message_lane_weights::( bp_rialto_parachain::EXTRA_STORAGE_PROOF_SIZE, bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, diff --git a/bin/rialto-parachain/runtime/src/millau_messages.rs b/bin/rialto-parachain/runtime/src/millau_messages.rs index 3c523ebfa12..68c9cbbec60 100644 --- a/bin/rialto-parachain/runtime/src/millau_messages.rs +++ b/bin/rialto-parachain/runtime/src/millau_messages.rs @@ -150,7 +150,11 @@ mod tests { #[test] fn ensure_millau_message_lane_weights_are_correct() { - check_message_lane_weights::( + check_message_lane_weights::< + bp_rialto_parachain::RialtoParachain, + Runtime, + WithMillauMessagesInstance, + >( bp_millau::EXTRA_STORAGE_PROOF_SIZE, bp_rialto_parachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, bp_rialto_parachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, diff --git a/bin/rialto/runtime/src/millau_messages.rs b/bin/rialto/runtime/src/millau_messages.rs index a7d1d19f0d8..a99243cc3e6 100644 --- a/bin/rialto/runtime/src/millau_messages.rs +++ b/bin/rialto/runtime/src/millau_messages.rs @@ -150,7 +150,7 @@ mod tests { #[test] fn ensure_millau_message_lane_weights_are_correct() { - check_message_lane_weights::( + check_message_lane_weights::( bp_millau::EXTRA_STORAGE_PROOF_SIZE, bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, diff --git a/bin/runtime-common/src/integrity.rs b/bin/runtime-common/src/integrity.rs index bf3d239eefd..aa698b0b95e 100644 --- a/bin/runtime-common/src/integrity.rs +++ b/bin/runtime-common/src/integrity.rs @@ -292,7 +292,8 @@ where /// Check that the message lane weights are correct. pub fn check_message_lane_weights< C: Chain, - T: frame_system::Config + pallet_bridge_messages::Config, + T: frame_system::Config + pallet_bridge_messages::Config, + MessagesPalletInstance: 'static, >( bridged_chain_extra_storage_proof_size: u32, this_chain_max_unrewarded_relayers: MessageNonce, @@ -303,15 +304,15 @@ pub fn check_message_lane_weights< // in other words: pass true for all known production chains runtime_includes_refund_extension: bool, ) { - type Weights = ::WeightInfo; + type Weights = >::WeightInfo; // check basic weight assumptions - pallet_bridge_messages::ensure_weights_are_correct::>(); + pallet_bridge_messages::ensure_weights_are_correct::>(); // check that weights allow us to receive messages let max_incoming_message_proof_size = bridged_chain_extra_storage_proof_size .saturating_add(messages::target::maximal_incoming_message_size(C::max_extrinsic_size())); - pallet_bridge_messages::ensure_able_to_receive_message::>( + pallet_bridge_messages::ensure_able_to_receive_message::>( C::max_extrinsic_size(), C::max_extrinsic_weight(), max_incoming_message_proof_size, @@ -321,7 +322,7 @@ pub fn check_message_lane_weights< // check that weights allow us to receive delivery confirmations let max_incoming_inbound_lane_data_proof_size = InboundLaneData::<()>::encoded_size_hint_u32(this_chain_max_unrewarded_relayers as _); - pallet_bridge_messages::ensure_able_to_receive_confirmation::>( + pallet_bridge_messages::ensure_able_to_receive_confirmation::>( C::max_extrinsic_size(), C::max_extrinsic_weight(), max_incoming_inbound_lane_data_proof_size, @@ -336,9 +337,12 @@ pub fn check_message_lane_weights< // ensures the extension will not refund weight when it doesn't need to (i.e. if pallet // weights do not account weights of refund extension). if runtime_includes_refund_extension { - assert_ne!(Weights::::receive_messages_proof_overhead_from_runtime(), Weight::zero()); assert_ne!( - Weights::::receive_messages_delivery_proof_overhead_from_runtime(), + Weights::::receive_messages_proof_overhead_from_runtime(), + Weight::zero() + ); + assert_ne!( + Weights::::receive_messages_delivery_proof_overhead_from_runtime(), Weight::zero() ); } diff --git a/relays/bin-substrate/Cargo.toml b/relays/bin-substrate/Cargo.toml index 0a315035042..f71920a1d18 100644 --- a/relays/bin-substrate/Cargo.toml +++ b/relays/bin-substrate/Cargo.toml @@ -49,7 +49,7 @@ relay-utils = { path = "../utils" } relay-westend-client = { path = "../client-westend" } relay-wococo-client = { path = "../client-wococo" } rialto-runtime = { path = "../../bin/rialto/runtime" } -# we are not using this runtime to craft callsour transactions, but we still need it +# we are not using this runtime to craft call or transactions, but we still need it # to prepare large XCM messages rialto-parachain-runtime = { path = "../../bin/rialto-parachain/runtime" } substrate-relay-helper = { path = "../lib-substrate-relay" } diff --git a/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs index 9e4b5b15fd6..18d9c31089c 100644 --- a/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs +++ b/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_rococo_messages_to_bridge_hub_wococo.rs @@ -35,15 +35,15 @@ impl MessagesCliBridge for BridgeHubRococoToBridgeHubWococoMessagesCliBridge { substrate_relay_helper::generate_receive_message_proof_call_builder!( BridgeHubRococoMessagesToBridgeHubWococoMessageLane, BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesProofCallBuilder, - relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoMessages, - relay_bridge_hub_wococo_client::runtime::BridgeRococoMessagesCall::receive_messages_proof + relay_bridge_hub_wococo_client::RuntimeCall::BridgeRococoMessages, + relay_bridge_hub_wococo_client::BridgeMessagesCall::receive_messages_proof ); substrate_relay_helper::generate_receive_message_delivery_proof_call_builder!( BridgeHubRococoMessagesToBridgeHubWococoMessageLane, BridgeHubRococoMessagesToBridgeHubWococoMessageLaneReceiveMessagesDeliveryProofCallBuilder, - relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoMessages, - relay_bridge_hub_rococo_client::runtime::BridgeWococoMessagesCall::receive_messages_delivery_proof + relay_bridge_hub_rococo_client::RuntimeCall::BridgeWococoMessages, + relay_bridge_hub_rococo_client::BridgeMessagesCall::receive_messages_delivery_proof ); /// Description of BridgeHubRococo -> BridgeHubWococo messages bridge. diff --git a/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs index fb5a81c021e..d327926823b 100644 --- a/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs +++ b/relays/bin-substrate/src/bridges/rococo_wococo/bridge_hub_wococo_messages_to_bridge_hub_rococo.rs @@ -35,15 +35,15 @@ impl MessagesCliBridge for BridgeHubWococoToBridgeHubRococoMessagesCliBridge { substrate_relay_helper::generate_receive_message_proof_call_builder!( BridgeHubWococoMessagesToBridgeHubRococoMessageLane, BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesProofCallBuilder, - relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoMessages, - relay_bridge_hub_rococo_client::runtime::BridgeWococoMessagesCall::receive_messages_proof + relay_bridge_hub_rococo_client::RuntimeCall::BridgeWococoMessages, + relay_bridge_hub_rococo_client::BridgeMessagesCall::receive_messages_proof ); substrate_relay_helper::generate_receive_message_delivery_proof_call_builder!( BridgeHubWococoMessagesToBridgeHubRococoMessageLane, BridgeHubWococoMessagesToBridgeHubRococoMessageLaneReceiveMessagesDeliveryProofCallBuilder, - relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoMessages, - relay_bridge_hub_wococo_client::runtime::BridgeRococoMessagesCall::receive_messages_delivery_proof + relay_bridge_hub_wococo_client::RuntimeCall::BridgeRococoMessages, + relay_bridge_hub_wococo_client::BridgeMessagesCall::receive_messages_delivery_proof ); /// Description of BridgeHubWococo -> BridgeHubRococo messages bridge. diff --git a/relays/bin-substrate/src/bridges/rococo_wococo/rococo_headers_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/rococo_headers_to_bridge_hub_wococo.rs index 2272144311c..b7d17d931f8 100644 --- a/relays/bin-substrate/src/bridges/rococo_wococo/rococo_headers_to_bridge_hub_wococo.rs +++ b/relays/bin-substrate/src/bridges/rococo_wococo/rococo_headers_to_bridge_hub_wococo.rs @@ -32,8 +32,8 @@ pub struct RococoFinalityToBridgeHubWococo; substrate_relay_helper::generate_submit_finality_proof_call_builder!( RococoFinalityToBridgeHubWococo, RococoFinalityToBridgeHubWococoCallBuilder, - relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoGrandpa, - relay_bridge_hub_wococo_client::runtime::BridgeRococoGrandpaCall::submit_finality_proof + relay_bridge_hub_wococo_client::RuntimeCall::BridgeRococoGrandpa, + relay_bridge_hub_wococo_client::BridgeGrandpaCall::submit_finality_proof ); #[async_trait] diff --git a/relays/bin-substrate/src/bridges/rococo_wococo/rococo_parachains_to_bridge_hub_wococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/rococo_parachains_to_bridge_hub_wococo.rs index 78b4ff35a7d..b2704c092b8 100644 --- a/relays/bin-substrate/src/bridges/rococo_wococo/rococo_parachains_to_bridge_hub_wococo.rs +++ b/relays/bin-substrate/src/bridges/rococo_wococo/rococo_parachains_to_bridge_hub_wococo.rs @@ -44,8 +44,8 @@ impl SubmitParachainHeadsCallBuilder parachains: Vec<(ParaId, ParaHash)>, parachain_heads_proof: ParaHeadsProof, ) -> CallOf { - relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoParachain( - relay_bridge_hub_wococo_client::runtime::BridgeParachainCall::submit_parachain_heads { + relay_bridge_hub_wococo_client::RuntimeCall::BridgeRococoParachain( + relay_bridge_hub_wococo_client::BridgeParachainCall::submit_parachain_heads { at_relay_block: (at_relay_block.0, at_relay_block.1), parachains, parachain_heads_proof, diff --git a/relays/bin-substrate/src/bridges/rococo_wococo/wococo_headers_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/wococo_headers_to_bridge_hub_rococo.rs index 39043009582..206c83b8153 100644 --- a/relays/bin-substrate/src/bridges/rococo_wococo/wococo_headers_to_bridge_hub_rococo.rs +++ b/relays/bin-substrate/src/bridges/rococo_wococo/wococo_headers_to_bridge_hub_rococo.rs @@ -32,8 +32,8 @@ pub struct WococoFinalityToBridgeHubRococo; substrate_relay_helper::generate_submit_finality_proof_call_builder!( WococoFinalityToBridgeHubRococo, WococoFinalityToBridgeHubRococoCallBuilder, - relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoGrandpa, - relay_bridge_hub_rococo_client::runtime::BridgeWococoGrandpaCall::submit_finality_proof + relay_bridge_hub_rococo_client::RuntimeCall::BridgeWococoGrandpa, + relay_bridge_hub_rococo_client::BridgeGrandpaCall::submit_finality_proof ); #[async_trait] diff --git a/relays/bin-substrate/src/bridges/rococo_wococo/wococo_parachains_to_bridge_hub_rococo.rs b/relays/bin-substrate/src/bridges/rococo_wococo/wococo_parachains_to_bridge_hub_rococo.rs index bc76e377c92..ca77857833d 100644 --- a/relays/bin-substrate/src/bridges/rococo_wococo/wococo_parachains_to_bridge_hub_rococo.rs +++ b/relays/bin-substrate/src/bridges/rococo_wococo/wococo_parachains_to_bridge_hub_rococo.rs @@ -44,8 +44,8 @@ impl SubmitParachainHeadsCallBuilder parachains: Vec<(ParaId, ParaHash)>, parachain_heads_proof: ParaHeadsProof, ) -> CallOf { - relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoParachain( - bp_parachains::BridgeParachainCall::submit_parachain_heads { + relay_bridge_hub_rococo_client::RuntimeCall::BridgeWococoParachain( + relay_bridge_hub_rococo_client::BridgeParachainCall::submit_parachain_heads { at_relay_block: (at_relay_block.0, at_relay_block.1), parachains, parachain_heads_proof, diff --git a/relays/bin-substrate/src/cli/init_bridge.rs b/relays/bin-substrate/src/cli/init_bridge.rs index 95cd02f6c6f..7b38d812271 100644 --- a/relays/bin-substrate/src/cli/init_bridge.rs +++ b/relays/bin-substrate/src/cli/init_bridge.rs @@ -180,10 +180,8 @@ impl BridgeInitializer for RococoToBridgeHubWococoCliBridge { fn encode_init_bridge( init_data: >::InitializationData, ) -> ::Call { - relay_bridge_hub_wococo_client::runtime::Call::BridgeRococoGrandpa( - relay_bridge_hub_wococo_client::runtime::BridgeRococoGrandpaCall::initialize { - init_data, - }, + relay_bridge_hub_wococo_client::RuntimeCall::BridgeRococoGrandpa( + relay_bridge_hub_wococo_client::BridgeGrandpaCall::initialize { init_data }, ) } } @@ -194,10 +192,8 @@ impl BridgeInitializer for WococoToBridgeHubRococoCliBridge { fn encode_init_bridge( init_data: >::InitializationData, ) -> ::Call { - relay_bridge_hub_rococo_client::runtime::Call::BridgeWococoGrandpa( - relay_bridge_hub_rococo_client::runtime::BridgeWococoGrandpaCall::initialize { - init_data, - }, + relay_bridge_hub_rococo_client::RuntimeCall::BridgeWococoGrandpa( + relay_bridge_hub_rococo_client::BridgeGrandpaCall::initialize { init_data }, ) } } diff --git a/relays/client-bridge-hub-rococo/Cargo.toml b/relays/client-bridge-hub-rococo/Cargo.toml index 5826a1321bc..a901e029bf5 100644 --- a/relays/client-bridge-hub-rococo/Cargo.toml +++ b/relays/client-bridge-hub-rococo/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } -relay-substrate-client = { path = "../client-substrate" } +subxt = { version = "0.28.0", default-features = false, features = [] } # Bridge dependencies @@ -17,12 +17,15 @@ bp-bridge-hub-wococo = { path = "../../primitives/chain-bridge-hub-wococo" } bp-header-chain = { path = "../../primitives/header-chain" } bp-messages = { path = "../../primitives/messages" } bp-parachains = { path = "../../primitives/parachains" } +bp-polkadot-core = { path = "../../primitives/polkadot-core" } bp-runtime = { path = "../../primitives/runtime" } bp-wococo = { path = "../../primitives/chain-wococo" } bridge-runtime-common = { path = "../../bin/runtime-common" } +relay-substrate-client = { path = "../client-substrate" } # Substrate Dependencies sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-weights = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-bridge-hub-rococo/src/codegen_runtime.rs b/relays/client-bridge-hub-rococo/src/codegen_runtime.rs new file mode 100644 index 00000000000..6488d923d75 --- /dev/null +++ b/relays/client-bridge-hub-rococo/src/codegen_runtime.rs @@ -0,0 +1,3348 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Autogenerated runtime API +//! THIS FILE WAS AUTOGENERATED USING parity-bridges-common::runtime-codegen +//! EXECUTED COMMAND: target/debug/runtime-codegen --from-node-url +//! wss://rococo-bridge-hub-rpc.polkadot.io:443 + +#[allow(dead_code, unused_imports, non_camel_case_types)] +#[allow(clippy::all)] +pub mod api { + use super::api as root_mod; + pub mod runtime_types { + use super::runtime_types; + pub mod bounded_collections { + use super::runtime_types; + pub mod bounded_vec { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct BoundedVec<_0>(pub ::std::vec::Vec<_0>); + } + pub mod weak_bounded_vec { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct WeakBoundedVec<_0>(pub ::std::vec::Vec<_0>); + } + } + pub mod bp_header_chain { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum HeaderChainError { + #[codec(index = 0)] + UnknownHeader, + #[codec(index = 1)] + StorageProof(runtime_types::bp_runtime::storage_proof::Error), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct StoredHeaderData<_0, _1> { + pub number: _0, + pub state_root: _1, + } + } + pub mod bp_messages { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct DeliveredMessages { + pub begin: ::core::primitive::u64, + pub end: ::core::primitive::u64, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct InboundLaneData<_0> { + pub relayers: ::std::vec::Vec>, + pub last_confirmed_nonce: ::core::primitive::u64, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct LaneId(pub [::core::primitive::u8; 4usize]); + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct MessageKey { + pub lane_id: runtime_types::bp_messages::LaneId, + pub nonce: ::core::primitive::u64, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum MessagesOperatingMode { + #[codec(index = 0)] + Basic(runtime_types::bp_runtime::BasicOperatingMode), + #[codec(index = 1)] + RejectingOutboundMessages, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct OutboundLaneData { + pub oldest_unpruned_nonce: ::core::primitive::u64, + pub latest_received_nonce: ::core::primitive::u64, + pub latest_generated_nonce: ::core::primitive::u64, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum ReceivalResult<_0> { + #[codec(index = 0)] + Dispatched(runtime_types::bp_runtime::messages::MessageDispatchResult<_0>), + #[codec(index = 1)] + InvalidNonce, + #[codec(index = 2)] + TooManyUnrewardedRelayers, + #[codec(index = 3)] + TooManyUnconfirmedMessages, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct ReceivedMessages<_0> { + pub lane: runtime_types::bp_messages::LaneId, + pub receive_results: ::std::vec::Vec<( + ::core::primitive::u64, + runtime_types::bp_messages::ReceivalResult<_0>, + )>, + pub skipped_for_not_enough_weight: ::std::vec::Vec<::core::primitive::u64>, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct UnrewardedRelayer<_0> { + pub relayer: _0, + pub messages: runtime_types::bp_messages::DeliveredMessages, + } + } + pub mod bp_parachains { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct BestParaHeadHash { + pub at_relay_block_number: ::core::primitive::u32, + pub head_hash: ::subxt::utils::H256, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct ParaInfo { + pub best_head_hash: runtime_types::bp_parachains::BestParaHeadHash, + pub next_imported_hash_position: ::core::primitive::u32, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct ParaStoredHeaderData(pub ::std::vec::Vec<::core::primitive::u8>); + } + pub mod bp_relayers { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum RewardsAccountOwner { + #[codec(index = 0)] + ThisChain, + #[codec(index = 1)] + BridgedChain, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct RewardsAccountParams { + pub lane_id: runtime_types::bp_messages::LaneId, + pub bridged_chain_id: [::core::primitive::u8; 4usize], + pub owner: runtime_types::bp_relayers::RewardsAccountOwner, + } + } + pub mod bp_runtime { + use super::runtime_types; + pub mod messages { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct MessageDispatchResult<_0> { + pub unspent_weight: ::sp_weights::Weight, + pub dispatch_level_result: _0, + } + } + pub mod storage_proof { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + DuplicateNodesInProof, + #[codec(index = 1)] + UnusedNodesInTheProof, + #[codec(index = 2)] + StorageRootMismatch, + #[codec(index = 3)] + StorageValueUnavailable, + #[codec(index = 4)] + StorageValueEmpty, + #[codec(index = 5)] + StorageValueDecodeFailed(runtime_types::bp_runtime::StrippableError), + } + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum BasicOperatingMode { + #[codec(index = 0)] + Normal, + #[codec(index = 1)] + Halted, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct HeaderId<_0, _1>(pub _1, pub _0); + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum OwnedBridgeModuleError { + #[codec(index = 0)] + Halted, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct StrippableError; + } + pub mod bridge_hub_rococo_runtime { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct BridgeRejectObsoleteHeadersAndMessages; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum OriginCaller { + #[codec(index = 0)] + system( + runtime_types::frame_support::dispatch::RawOrigin< + ::sp_core::crypto::AccountId32, + >, + ), + #[codec(index = 31)] + PolkadotXcm(runtime_types::pallet_xcm::pallet::Origin), + #[codec(index = 32)] + CumulusXcm(runtime_types::cumulus_pallet_xcm::pallet::Origin), + #[codec(index = 3)] + Void(runtime_types::sp_core::Void), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Runtime; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum RuntimeCall { + #[codec(index = 0)] + System(runtime_types::frame_system::pallet::Call), + #[codec(index = 1)] + ParachainSystem(runtime_types::cumulus_pallet_parachain_system::pallet::Call), + #[codec(index = 2)] + Timestamp(runtime_types::pallet_timestamp::pallet::Call), + #[codec(index = 10)] + Balances(runtime_types::pallet_balances::pallet::Call), + #[codec(index = 21)] + CollatorSelection(runtime_types::pallet_collator_selection::pallet::Call), + #[codec(index = 22)] + Session(runtime_types::pallet_session::pallet::Call), + #[codec(index = 30)] + XcmpQueue(runtime_types::cumulus_pallet_xcmp_queue::pallet::Call), + #[codec(index = 31)] + PolkadotXcm(runtime_types::pallet_xcm::pallet::Call), + #[codec(index = 33)] + DmpQueue(runtime_types::cumulus_pallet_dmp_queue::pallet::Call), + #[codec(index = 40)] + Utility(runtime_types::pallet_utility::pallet::Call), + #[codec(index = 36)] + Multisig(runtime_types::pallet_multisig::pallet::Call), + #[codec(index = 41)] + BridgeWococoGrandpa(runtime_types::pallet_bridge_grandpa::pallet::Call), + #[codec(index = 42)] + BridgeWococoParachain(runtime_types::pallet_bridge_parachains::pallet::Call), + #[codec(index = 46)] + BridgeWococoMessages(runtime_types::pallet_bridge_messages::pallet::Call), + #[codec(index = 43)] + BridgeRococoGrandpa(runtime_types::pallet_bridge_grandpa::pallet::Call), + #[codec(index = 44)] + BridgeRococoParachain(runtime_types::pallet_bridge_parachains::pallet::Call), + #[codec(index = 45)] + BridgeRococoMessages(runtime_types::pallet_bridge_messages::pallet::Call), + #[codec(index = 47)] + BridgeRelayers(runtime_types::pallet_bridge_relayers::pallet::Call), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum RuntimeEvent { + #[codec(index = 0)] + System(runtime_types::frame_system::pallet::Event), + #[codec(index = 1)] + ParachainSystem(runtime_types::cumulus_pallet_parachain_system::pallet::Event), + #[codec(index = 10)] + Balances(runtime_types::pallet_balances::pallet::Event), + #[codec(index = 11)] + TransactionPayment(runtime_types::pallet_transaction_payment::pallet::Event), + #[codec(index = 21)] + CollatorSelection(runtime_types::pallet_collator_selection::pallet::Event), + #[codec(index = 22)] + Session(runtime_types::pallet_session::pallet::Event), + #[codec(index = 30)] + XcmpQueue(runtime_types::cumulus_pallet_xcmp_queue::pallet::Event), + #[codec(index = 31)] + PolkadotXcm(runtime_types::pallet_xcm::pallet::Event), + #[codec(index = 32)] + CumulusXcm(runtime_types::cumulus_pallet_xcm::pallet::Event), + #[codec(index = 33)] + DmpQueue(runtime_types::cumulus_pallet_dmp_queue::pallet::Event), + #[codec(index = 40)] + Utility(runtime_types::pallet_utility::pallet::Event), + #[codec(index = 36)] + Multisig(runtime_types::pallet_multisig::pallet::Event), + #[codec(index = 41)] + BridgeWococoGrandpa(runtime_types::pallet_bridge_grandpa::pallet::Event), + #[codec(index = 42)] + BridgeWococoParachain(runtime_types::pallet_bridge_parachains::pallet::Event), + #[codec(index = 46)] + BridgeWococoMessages(runtime_types::pallet_bridge_messages::pallet::Event), + #[codec(index = 43)] + BridgeRococoGrandpa(runtime_types::pallet_bridge_grandpa::pallet::Event), + #[codec(index = 44)] + BridgeRococoParachain(runtime_types::pallet_bridge_parachains::pallet::Event), + #[codec(index = 45)] + BridgeRococoMessages(runtime_types::pallet_bridge_messages::pallet::Event), + #[codec(index = 47)] + BridgeRelayers(runtime_types::pallet_bridge_relayers::pallet::Event), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct SessionKeys { + pub aura: runtime_types::sp_consensus_aura::sr25519::app_sr25519::Public, + } + } + pub mod bridge_runtime_common { + use super::runtime_types; + pub mod messages_xcm_extension { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum XcmBlobMessageDispatchResult { + #[codec(index = 0)] + InvalidPayload, + #[codec(index = 1)] + Dispatched, + #[codec(index = 2)] + NotDispatched, + } + } + pub mod refund_relayer_extension { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct RefundBridgedParachainMessages; + } + } + pub mod cumulus_pallet_dmp_queue { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + #[codec(index = 0)] + service_overweight { + index: ::core::primitive::u64, + weight_limit: ::sp_weights::Weight, + }, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + Unknown, + #[codec(index = 1)] + OverLimit, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + InvalidFormat { message_id: [::core::primitive::u8; 32usize] }, + #[codec(index = 1)] + UnsupportedVersion { message_id: [::core::primitive::u8; 32usize] }, + #[codec(index = 2)] + ExecutedDownward { + message_id: [::core::primitive::u8; 32usize], + outcome: runtime_types::xcm::v3::traits::Outcome, + }, + #[codec(index = 3)] + WeightExhausted { + message_id: [::core::primitive::u8; 32usize], + remaining_weight: ::sp_weights::Weight, + required_weight: ::sp_weights::Weight, + }, + #[codec(index = 4)] + OverweightEnqueued { + message_id: [::core::primitive::u8; 32usize], + overweight_index: ::core::primitive::u64, + required_weight: ::sp_weights::Weight, + }, + #[codec(index = 5)] + OverweightServiced { + overweight_index: ::core::primitive::u64, + weight_used: ::sp_weights::Weight, + }, + #[codec(index = 6)] + MaxMessagesExhausted { message_id: [::core::primitive::u8; 32usize] }, + } + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct ConfigData { + pub max_individual: ::sp_weights::Weight, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct PageIndexData { + pub begin_used: ::core::primitive::u32, + pub end_used: ::core::primitive::u32, + pub overweight_count: ::core::primitive::u64, + } + } + pub mod cumulus_pallet_parachain_system { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + # [codec (index = 0)] set_validation_data { data : runtime_types :: cumulus_primitives_parachain_inherent :: ParachainInherentData , } , # [codec (index = 1)] sudo_send_upward_message { message : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , # [codec (index = 2)] authorize_upgrade { code_hash : :: subxt :: utils :: H256 , check_version : :: core :: primitive :: bool , } , # [codec (index = 3)] enact_authorized_upgrade { code : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + OverlappingUpgrades, + #[codec(index = 1)] + ProhibitedByPolkadot, + #[codec(index = 2)] + TooBig, + #[codec(index = 3)] + ValidationDataNotAvailable, + #[codec(index = 4)] + HostConfigurationNotAvailable, + #[codec(index = 5)] + NotScheduled, + #[codec(index = 6)] + NothingAuthorized, + #[codec(index = 7)] + Unauthorized, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + ValidationFunctionStored, + #[codec(index = 1)] + ValidationFunctionApplied { relay_chain_block_num: ::core::primitive::u32 }, + #[codec(index = 2)] + ValidationFunctionDiscarded, + #[codec(index = 3)] + UpgradeAuthorized { code_hash: ::subxt::utils::H256 }, + #[codec(index = 4)] + DownwardMessagesReceived { count: ::core::primitive::u32 }, + #[codec(index = 5)] + DownwardMessagesProcessed { + weight_used: ::sp_weights::Weight, + dmq_head: ::subxt::utils::H256, + }, + #[codec(index = 6)] + UpwardMessageSent { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + }, + } + } + pub mod relay_state_snapshot { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct MessagingStateSnapshot { + pub dmq_mqc_head: ::subxt::utils::H256, + pub relay_dispatch_queue_size: (::core::primitive::u32, ::core::primitive::u32), + pub ingress_channels: ::std::vec::Vec<( + runtime_types::polkadot_parachain::primitives::Id, + runtime_types::polkadot_primitives::v4::AbridgedHrmpChannel, + )>, + pub egress_channels: ::std::vec::Vec<( + runtime_types::polkadot_parachain::primitives::Id, + runtime_types::polkadot_primitives::v4::AbridgedHrmpChannel, + )>, + } + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct CodeUpgradeAuthorization { + pub code_hash: ::subxt::utils::H256, + pub check_version: ::core::primitive::bool, + } + } + pub mod cumulus_pallet_xcm { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error {} + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + InvalidFormat([::core::primitive::u8; 32usize]), + #[codec(index = 1)] + UnsupportedVersion([::core::primitive::u8; 32usize]), + #[codec(index = 2)] + ExecutedDownward( + [::core::primitive::u8; 32usize], + runtime_types::xcm::v3::traits::Outcome, + ), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Origin { + #[codec(index = 0)] + Relay, + #[codec(index = 1)] + SiblingParachain(runtime_types::polkadot_parachain::primitives::Id), + } + } + } + pub mod cumulus_pallet_xcmp_queue { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + #[codec(index = 0)] + service_overweight { + index: ::core::primitive::u64, + weight_limit: ::sp_weights::Weight, + }, + #[codec(index = 1)] + suspend_xcm_execution, + #[codec(index = 2)] + resume_xcm_execution, + #[codec(index = 3)] + update_suspend_threshold { new: ::core::primitive::u32 }, + #[codec(index = 4)] + update_drop_threshold { new: ::core::primitive::u32 }, + #[codec(index = 5)] + update_resume_threshold { new: ::core::primitive::u32 }, + #[codec(index = 6)] + update_threshold_weight { new: ::sp_weights::Weight }, + #[codec(index = 7)] + update_weight_restrict_decay { new: ::sp_weights::Weight }, + #[codec(index = 8)] + update_xcmp_max_individual_weight { new: ::sp_weights::Weight }, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + FailedToSend, + #[codec(index = 1)] + BadXcmOrigin, + #[codec(index = 2)] + BadXcm, + #[codec(index = 3)] + BadOverweightIndex, + #[codec(index = 4)] + WeightOverLimit, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + Success { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + weight: ::sp_weights::Weight, + }, + #[codec(index = 1)] + Fail { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + error: runtime_types::xcm::v3::traits::Error, + weight: ::sp_weights::Weight, + }, + #[codec(index = 2)] + BadVersion { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + }, + #[codec(index = 3)] + BadFormat { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + }, + #[codec(index = 4)] + XcmpMessageSent { + message_hash: ::core::option::Option<[::core::primitive::u8; 32usize]>, + }, + #[codec(index = 5)] + OverweightEnqueued { + sender: runtime_types::polkadot_parachain::primitives::Id, + sent_at: ::core::primitive::u32, + index: ::core::primitive::u64, + required: ::sp_weights::Weight, + }, + #[codec(index = 6)] + OverweightServiced { index: ::core::primitive::u64, used: ::sp_weights::Weight }, + } + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct InboundChannelDetails { + pub sender: runtime_types::polkadot_parachain::primitives::Id, + pub state: runtime_types::cumulus_pallet_xcmp_queue::InboundState, + pub message_metadata: ::std::vec::Vec<( + ::core::primitive::u32, + runtime_types::polkadot_parachain::primitives::XcmpMessageFormat, + )>, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum InboundState { + #[codec(index = 0)] + Ok, + #[codec(index = 1)] + Suspended, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct OutboundChannelDetails { + pub recipient: runtime_types::polkadot_parachain::primitives::Id, + pub state: runtime_types::cumulus_pallet_xcmp_queue::OutboundState, + pub signals_exist: ::core::primitive::bool, + pub first_index: ::core::primitive::u16, + pub last_index: ::core::primitive::u16, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum OutboundState { + #[codec(index = 0)] + Ok, + #[codec(index = 1)] + Suspended, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct QueueConfigData { + pub suspend_threshold: ::core::primitive::u32, + pub drop_threshold: ::core::primitive::u32, + pub resume_threshold: ::core::primitive::u32, + pub threshold_weight: ::sp_weights::Weight, + pub weight_restrict_decay: ::sp_weights::Weight, + pub xcmp_max_individual_weight: ::sp_weights::Weight, + } + } + pub mod cumulus_primitives_parachain_inherent { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct MessageQueueChain(pub ::subxt::utils::H256); + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct ParachainInherentData { + pub validation_data: + runtime_types::polkadot_primitives::v4::PersistedValidationData< + ::subxt::utils::H256, + ::core::primitive::u32, + >, + pub relay_chain_state: runtime_types::sp_trie::storage_proof::StorageProof, + pub downward_messages: ::std::vec::Vec< + runtime_types::polkadot_core_primitives::InboundDownwardMessage< + ::core::primitive::u32, + >, + >, + pub horizontal_messages: ::subxt::utils::KeyedVec< + runtime_types::polkadot_parachain::primitives::Id, + ::std::vec::Vec< + runtime_types::polkadot_core_primitives::InboundHrmpMessage< + ::core::primitive::u32, + >, + >, + >, + } + } + pub mod finality_grandpa { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Commit<_0, _1, _2, _3> { + pub target_hash: _0, + pub target_number: _1, + pub precommits: ::std::vec::Vec< + runtime_types::finality_grandpa::SignedPrecommit<_0, _1, _2, _3>, + >, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Precommit<_0, _1> { + pub target_hash: _0, + pub target_number: _1, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct SignedPrecommit<_0, _1, _2, _3> { + pub precommit: runtime_types::finality_grandpa::Precommit<_0, _1>, + pub signature: _2, + pub id: _3, + } + } + pub mod frame_support { + use super::runtime_types; + pub mod dispatch { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum DispatchClass { + #[codec(index = 0)] + Normal, + #[codec(index = 1)] + Operational, + #[codec(index = 2)] + Mandatory, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct DispatchInfo { + pub weight: ::sp_weights::Weight, + pub class: runtime_types::frame_support::dispatch::DispatchClass, + pub pays_fee: runtime_types::frame_support::dispatch::Pays, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Pays { + #[codec(index = 0)] + Yes, + #[codec(index = 1)] + No, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct PerDispatchClass<_0> { + pub normal: _0, + pub operational: _0, + pub mandatory: _0, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum RawOrigin<_0> { + #[codec(index = 0)] + Root, + #[codec(index = 1)] + Signed(_0), + #[codec(index = 2)] + None, + } + } + pub mod traits { + use super::runtime_types; + pub mod tokens { + use super::runtime_types; + pub mod misc { + use super::runtime_types; + #[derive( + :: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq, + )] + pub enum BalanceStatus { + #[codec(index = 0)] + Free, + #[codec(index = 1)] + Reserved, + } + } + } + } + } + pub mod frame_system { + use super::runtime_types; + pub mod extensions { + use super::runtime_types; + pub mod check_genesis { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct CheckGenesis; + } + pub mod check_mortality { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct CheckMortality(pub ::sp_runtime::generic::Era); + } + pub mod check_non_zero_sender { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct CheckNonZeroSender; + } + pub mod check_nonce { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct CheckNonce(#[codec(compact)] pub ::core::primitive::u32); + } + pub mod check_spec_version { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct CheckSpecVersion; + } + pub mod check_tx_version { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct CheckTxVersion; + } + pub mod check_weight { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct CheckWeight; + } + } + pub mod limits { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct BlockLength { + pub max: runtime_types::frame_support::dispatch::PerDispatchClass< + ::core::primitive::u32, + >, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct BlockWeights { + pub base_block: ::sp_weights::Weight, + pub max_block: ::sp_weights::Weight, + pub per_class: runtime_types::frame_support::dispatch::PerDispatchClass< + runtime_types::frame_system::limits::WeightsPerClass, + >, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct WeightsPerClass { + pub base_extrinsic: ::sp_weights::Weight, + pub max_extrinsic: ::core::option::Option<::sp_weights::Weight>, + pub max_total: ::core::option::Option<::sp_weights::Weight>, + pub reserved: ::core::option::Option<::sp_weights::Weight>, + } + } + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + #[codec(index = 0)] + remark { remark: ::std::vec::Vec<::core::primitive::u8> }, + #[codec(index = 1)] + set_heap_pages { pages: ::core::primitive::u64 }, + #[codec(index = 2)] + set_code { code: ::std::vec::Vec<::core::primitive::u8> }, + #[codec(index = 3)] + set_code_without_checks { code: ::std::vec::Vec<::core::primitive::u8> }, + #[codec(index = 4)] + set_storage { + items: ::std::vec::Vec<( + ::std::vec::Vec<::core::primitive::u8>, + ::std::vec::Vec<::core::primitive::u8>, + )>, + }, + #[codec(index = 5)] + kill_storage { keys: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>> }, + #[codec(index = 6)] + kill_prefix { + prefix: ::std::vec::Vec<::core::primitive::u8>, + subkeys: ::core::primitive::u32, + }, + #[codec(index = 7)] + remark_with_event { remark: ::std::vec::Vec<::core::primitive::u8> }, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + InvalidSpecName, + #[codec(index = 1)] + SpecVersionNeedsToIncrease, + #[codec(index = 2)] + FailedToExtractRuntimeVersion, + #[codec(index = 3)] + NonDefaultComposite, + #[codec(index = 4)] + NonZeroRefCount, + #[codec(index = 5)] + CallFiltered, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + ExtrinsicSuccess { + dispatch_info: runtime_types::frame_support::dispatch::DispatchInfo, + }, + #[codec(index = 1)] + ExtrinsicFailed { + dispatch_error: runtime_types::sp_runtime::DispatchError, + dispatch_info: runtime_types::frame_support::dispatch::DispatchInfo, + }, + #[codec(index = 2)] + CodeUpdated, + #[codec(index = 3)] + NewAccount { account: ::sp_core::crypto::AccountId32 }, + #[codec(index = 4)] + KilledAccount { account: ::sp_core::crypto::AccountId32 }, + #[codec(index = 5)] + Remarked { sender: ::sp_core::crypto::AccountId32, hash: ::subxt::utils::H256 }, + } + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct AccountInfo<_0, _1> { + pub nonce: _0, + pub consumers: _0, + pub providers: _0, + pub sufficients: _0, + pub data: _1, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct EventRecord<_0, _1> { + pub phase: runtime_types::frame_system::Phase, + pub event: _0, + pub topics: ::std::vec::Vec<_1>, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct LastRuntimeUpgradeInfo { + #[codec(compact)] + pub spec_version: ::core::primitive::u32, + pub spec_name: ::std::string::String, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Phase { + #[codec(index = 0)] + ApplyExtrinsic(::core::primitive::u32), + #[codec(index = 1)] + Finalization, + #[codec(index = 2)] + Initialization, + } + } + pub mod pallet_balances { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + #[codec(index = 0)] + transfer_allow_death { + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + value: ::core::primitive::u128, + }, + #[codec(index = 1)] + set_balance_deprecated { + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + new_free: ::core::primitive::u128, + #[codec(compact)] + old_reserved: ::core::primitive::u128, + }, + #[codec(index = 2)] + force_transfer { + source: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + value: ::core::primitive::u128, + }, + #[codec(index = 3)] + transfer_keep_alive { + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + value: ::core::primitive::u128, + }, + #[codec(index = 4)] + transfer_all { + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + keep_alive: ::core::primitive::bool, + }, + #[codec(index = 5)] + force_unreserve { + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + amount: ::core::primitive::u128, + }, + #[codec(index = 6)] + upgrade_accounts { who: ::std::vec::Vec<::sp_core::crypto::AccountId32> }, + #[codec(index = 7)] + transfer { + dest: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + value: ::core::primitive::u128, + }, + #[codec(index = 8)] + force_set_balance { + who: ::subxt::utils::MultiAddress<::sp_core::crypto::AccountId32, ()>, + #[codec(compact)] + new_free: ::core::primitive::u128, + }, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + VestingBalance, + #[codec(index = 1)] + LiquidityRestrictions, + #[codec(index = 2)] + InsufficientBalance, + #[codec(index = 3)] + ExistentialDeposit, + #[codec(index = 4)] + Expendability, + #[codec(index = 5)] + ExistingVestingSchedule, + #[codec(index = 6)] + DeadAccount, + #[codec(index = 7)] + TooManyReserves, + #[codec(index = 8)] + TooManyHolds, + #[codec(index = 9)] + TooManyFreezes, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + Endowed { + account: ::sp_core::crypto::AccountId32, + free_balance: ::core::primitive::u128, + }, + #[codec(index = 1)] + DustLost { + account: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 2)] + Transfer { + from: ::sp_core::crypto::AccountId32, + to: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 3)] + BalanceSet { + who: ::sp_core::crypto::AccountId32, + free: ::core::primitive::u128, + }, + #[codec(index = 4)] + Reserved { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 5)] + Unreserved { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 6)] + ReserveRepatriated { + from: ::sp_core::crypto::AccountId32, + to: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + destination_status: + runtime_types::frame_support::traits::tokens::misc::BalanceStatus, + }, + #[codec(index = 7)] + Deposit { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, + #[codec(index = 8)] + Withdraw { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 9)] + Slashed { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, + #[codec(index = 10)] + Minted { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, + #[codec(index = 11)] + Burned { who: ::sp_core::crypto::AccountId32, amount: ::core::primitive::u128 }, + #[codec(index = 12)] + Suspended { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 13)] + Restored { + who: ::sp_core::crypto::AccountId32, + amount: ::core::primitive::u128, + }, + #[codec(index = 14)] + Upgraded { who: ::sp_core::crypto::AccountId32 }, + #[codec(index = 15)] + Issued { amount: ::core::primitive::u128 }, + #[codec(index = 16)] + Rescinded { amount: ::core::primitive::u128 }, + } + } + pub mod types { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct AccountData<_0> { + pub free: _0, + pub reserved: _0, + pub frozen: _0, + pub flags: runtime_types::pallet_balances::types::ExtraFlags, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct BalanceLock<_0> { + pub id: [::core::primitive::u8; 8usize], + pub amount: _0, + pub reasons: runtime_types::pallet_balances::types::Reasons, + } + #[derive( + :: codec :: Decode, + :: codec :: Encode, + :: subxt :: ext :: codec :: CompactAs, + Clone, + Debug, + PartialEq, + )] + pub struct ExtraFlags(pub ::core::primitive::u128); + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct IdAmount<_0, _1> { + pub id: _0, + pub amount: _1, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Reasons { + #[codec(index = 0)] + Fee, + #[codec(index = 1)] + Misc, + #[codec(index = 2)] + All, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct ReserveData<_0, _1> { + pub id: _0, + pub amount: _1, + } + } + } + pub mod pallet_bridge_grandpa { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + #[codec(index = 0)] + submit_finality_proof { + finality_target: ::std::boxed::Box< + ::sp_runtime::generic::Header< + ::core::primitive::u32, + ::sp_runtime::traits::BlakeTwo256, + >, + >, + justification: ::bp_header_chain::justification::GrandpaJustification< + ::sp_runtime::generic::Header< + ::core::primitive::u32, + ::sp_runtime::traits::BlakeTwo256, + >, + >, + }, + #[codec(index = 1)] + initialize { + init_data: ::bp_header_chain::InitializationData< + ::sp_runtime::generic::Header< + ::core::primitive::u32, + ::sp_runtime::traits::BlakeTwo256, + >, + >, + }, + #[codec(index = 2)] + set_owner { new_owner: ::core::option::Option<::sp_core::crypto::AccountId32> }, + #[codec(index = 3)] + set_operating_mode { + operating_mode: runtime_types::bp_runtime::BasicOperatingMode, + }, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + InvalidJustification, + #[codec(index = 1)] + InvalidAuthoritySet, + #[codec(index = 2)] + TooManyRequests, + #[codec(index = 3)] + OldHeader, + #[codec(index = 4)] + UnsupportedScheduledChange, + #[codec(index = 5)] + NotInitialized, + #[codec(index = 6)] + AlreadyInitialized, + #[codec(index = 7)] + TooManyAuthoritiesInSet, + #[codec(index = 8)] + BridgeModule(runtime_types::bp_runtime::OwnedBridgeModuleError), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + UpdatedBestFinalizedHeader { + number: ::core::primitive::u32, + hash: ::subxt::utils::H256, + }, + } + } + pub mod storage_types { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct StoredAuthoritySet { + pub authorities: runtime_types::bounded_collections::bounded_vec::BoundedVec<( + runtime_types::sp_consensus_grandpa::app::Public, + ::core::primitive::u64, + )>, + pub set_id: ::core::primitive::u64, + } + } + } + pub mod pallet_bridge_messages { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + # [codec (index = 0)] set_owner { new_owner : :: core :: option :: Option < :: sp_core :: crypto :: AccountId32 > , } , # [codec (index = 1)] set_operating_mode { operating_mode : runtime_types :: bp_messages :: MessagesOperatingMode , } , # [codec (index = 2)] receive_messages_proof { relayer_id_at_bridged_chain : :: sp_core :: crypto :: AccountId32 , proof : :: bridge_runtime_common :: messages :: target :: FromBridgedChainMessagesProof < :: subxt :: utils :: H256 > , messages_count : :: core :: primitive :: u32 , dispatch_weight : :: sp_weights :: Weight , } , # [codec (index = 3)] receive_messages_delivery_proof { proof : :: bridge_runtime_common :: messages :: source :: FromBridgedChainMessagesDeliveryProof < :: subxt :: utils :: H256 > , relayers_state : :: bp_messages :: UnrewardedRelayersState , } , } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + NotOperatingNormally, + #[codec(index = 1)] + InactiveOutboundLane, + #[codec(index = 2)] + MessageIsTooLarge, + #[codec(index = 3)] + MessageRejectedByChainVerifier, + #[codec(index = 4)] + MessageRejectedByLaneVerifier, + #[codec(index = 5)] + FailedToWithdrawMessageFee, + #[codec(index = 6)] + TooManyMessagesInTheProof, + #[codec(index = 7)] + InvalidMessagesProof, + #[codec(index = 8)] + InvalidMessagesDeliveryProof, + #[codec(index = 9)] + InvalidUnrewardedRelayers, + #[codec(index = 10)] + InvalidUnrewardedRelayersState, + #[codec(index = 11)] + MessageIsAlreadyDelivered, + #[codec(index = 12)] + MessageIsNotYetSent, + #[codec(index = 13)] + TryingToConfirmMoreMessagesThanExpected, + #[codec(index = 14)] + BridgeModule(runtime_types::bp_runtime::OwnedBridgeModuleError), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + # [codec (index = 0)] MessageAccepted { lane_id : runtime_types :: bp_messages :: LaneId , nonce : :: core :: primitive :: u64 , } , # [codec (index = 1)] MessagesReceived (:: std :: vec :: Vec < runtime_types :: bp_messages :: ReceivedMessages < runtime_types :: bridge_runtime_common :: messages_xcm_extension :: XcmBlobMessageDispatchResult > > ,) , # [codec (index = 2)] MessagesDelivered { lane_id : runtime_types :: bp_messages :: LaneId , messages : runtime_types :: bp_messages :: DeliveredMessages , } , } + } + } + pub mod pallet_bridge_parachains { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + #[codec(index = 0)] + submit_parachain_heads { + at_relay_block: (::core::primitive::u32, ::subxt::utils::H256), + parachains: ::std::vec::Vec<( + ::bp_polkadot_core::parachains::ParaId, + ::subxt::utils::H256, + )>, + parachain_heads_proof: ::bp_polkadot_core::parachains::ParaHeadsProof, + }, + #[codec(index = 1)] + set_owner { new_owner: ::core::option::Option<::sp_core::crypto::AccountId32> }, + #[codec(index = 2)] + set_operating_mode { + operating_mode: runtime_types::bp_runtime::BasicOperatingMode, + }, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + UnknownRelayChainBlock, + #[codec(index = 1)] + InvalidRelayChainBlockNumber, + #[codec(index = 2)] + HeaderChain(runtime_types::bp_header_chain::HeaderChainError), + #[codec(index = 3)] + UnknownParaHead, + #[codec(index = 4)] + StorageRootMismatch, + #[codec(index = 5)] + FailedToExtractStateRoot, + #[codec(index = 6)] + BridgeModule(runtime_types::bp_runtime::OwnedBridgeModuleError), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + UntrackedParachainRejected { parachain: ::bp_polkadot_core::parachains::ParaId }, + #[codec(index = 1)] + MissingParachainHead { parachain: ::bp_polkadot_core::parachains::ParaId }, + #[codec(index = 2)] + IncorrectParachainHeadHash { + parachain: ::bp_polkadot_core::parachains::ParaId, + parachain_head_hash: ::subxt::utils::H256, + actual_parachain_head_hash: ::subxt::utils::H256, + }, + #[codec(index = 3)] + RejectedObsoleteParachainHead { + parachain: ::bp_polkadot_core::parachains::ParaId, + parachain_head_hash: ::subxt::utils::H256, + }, + #[codec(index = 4)] + RejectedLargeParachainHead { + parachain: ::bp_polkadot_core::parachains::ParaId, + parachain_head_hash: ::subxt::utils::H256, + parachain_head_size: ::core::primitive::u32, + }, + #[codec(index = 5)] + UpdatedParachainHead { + parachain: ::bp_polkadot_core::parachains::ParaId, + parachain_head_hash: ::subxt::utils::H256, + }, + } + } + } + pub mod pallet_bridge_relayers { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + #[codec(index = 0)] + claim_rewards { + rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, + }, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + NoRewardForRelayer, + #[codec(index = 1)] + FailedToPayReward, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + RewardPaid { + relayer: ::sp_core::crypto::AccountId32, + rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, + reward: ::core::primitive::u128, + }, + } + } + } + pub mod pallet_collator_selection { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + #[codec(index = 0)] + set_invulnerables { new: ::std::vec::Vec<::sp_core::crypto::AccountId32> }, + #[codec(index = 1)] + set_desired_candidates { max: ::core::primitive::u32 }, + #[codec(index = 2)] + set_candidacy_bond { bond: ::core::primitive::u128 }, + #[codec(index = 3)] + register_as_candidate, + #[codec(index = 4)] + leave_intent, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct CandidateInfo<_0, _1> { + pub who: _0, + pub deposit: _1, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + TooManyCandidates, + #[codec(index = 1)] + TooFewCandidates, + #[codec(index = 2)] + Unknown, + #[codec(index = 3)] + Permission, + #[codec(index = 4)] + AlreadyCandidate, + #[codec(index = 5)] + NotCandidate, + #[codec(index = 6)] + TooManyInvulnerables, + #[codec(index = 7)] + AlreadyInvulnerable, + #[codec(index = 8)] + NoAssociatedValidatorId, + #[codec(index = 9)] + ValidatorNotRegistered, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + NewInvulnerables { + invulnerables: ::std::vec::Vec<::sp_core::crypto::AccountId32>, + }, + #[codec(index = 1)] + NewDesiredCandidates { desired_candidates: ::core::primitive::u32 }, + #[codec(index = 2)] + NewCandidacyBond { bond_amount: ::core::primitive::u128 }, + #[codec(index = 3)] + CandidateAdded { + account_id: ::sp_core::crypto::AccountId32, + deposit: ::core::primitive::u128, + }, + #[codec(index = 4)] + CandidateRemoved { account_id: ::sp_core::crypto::AccountId32 }, + } + } + } + pub mod pallet_multisig { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + #[codec(index = 0)] + as_multi_threshold_1 { + other_signatories: ::std::vec::Vec<::sp_core::crypto::AccountId32>, + call: ::std::boxed::Box< + runtime_types::bridge_hub_rococo_runtime::RuntimeCall, + >, + }, + #[codec(index = 1)] + as_multi { + threshold: ::core::primitive::u16, + other_signatories: ::std::vec::Vec<::sp_core::crypto::AccountId32>, + maybe_timepoint: ::core::option::Option< + runtime_types::pallet_multisig::Timepoint<::core::primitive::u32>, + >, + call: ::std::boxed::Box< + runtime_types::bridge_hub_rococo_runtime::RuntimeCall, + >, + max_weight: ::sp_weights::Weight, + }, + #[codec(index = 2)] + approve_as_multi { + threshold: ::core::primitive::u16, + other_signatories: ::std::vec::Vec<::sp_core::crypto::AccountId32>, + maybe_timepoint: ::core::option::Option< + runtime_types::pallet_multisig::Timepoint<::core::primitive::u32>, + >, + call_hash: [::core::primitive::u8; 32usize], + max_weight: ::sp_weights::Weight, + }, + #[codec(index = 3)] + cancel_as_multi { + threshold: ::core::primitive::u16, + other_signatories: ::std::vec::Vec<::sp_core::crypto::AccountId32>, + timepoint: + runtime_types::pallet_multisig::Timepoint<::core::primitive::u32>, + call_hash: [::core::primitive::u8; 32usize], + }, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + MinimumThreshold, + #[codec(index = 1)] + AlreadyApproved, + #[codec(index = 2)] + NoApprovalsNeeded, + #[codec(index = 3)] + TooFewSignatories, + #[codec(index = 4)] + TooManySignatories, + #[codec(index = 5)] + SignatoriesOutOfOrder, + #[codec(index = 6)] + SenderInSignatories, + #[codec(index = 7)] + NotFound, + #[codec(index = 8)] + NotOwner, + #[codec(index = 9)] + NoTimepoint, + #[codec(index = 10)] + WrongTimepoint, + #[codec(index = 11)] + UnexpectedTimepoint, + #[codec(index = 12)] + MaxWeightTooLow, + #[codec(index = 13)] + AlreadyStored, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + NewMultisig { + approving: ::sp_core::crypto::AccountId32, + multisig: ::sp_core::crypto::AccountId32, + call_hash: [::core::primitive::u8; 32usize], + }, + #[codec(index = 1)] + MultisigApproval { + approving: ::sp_core::crypto::AccountId32, + timepoint: + runtime_types::pallet_multisig::Timepoint<::core::primitive::u32>, + multisig: ::sp_core::crypto::AccountId32, + call_hash: [::core::primitive::u8; 32usize], + }, + #[codec(index = 2)] + MultisigExecuted { + approving: ::sp_core::crypto::AccountId32, + timepoint: + runtime_types::pallet_multisig::Timepoint<::core::primitive::u32>, + multisig: ::sp_core::crypto::AccountId32, + call_hash: [::core::primitive::u8; 32usize], + result: + ::core::result::Result<(), runtime_types::sp_runtime::DispatchError>, + }, + #[codec(index = 3)] + MultisigCancelled { + cancelling: ::sp_core::crypto::AccountId32, + timepoint: + runtime_types::pallet_multisig::Timepoint<::core::primitive::u32>, + multisig: ::sp_core::crypto::AccountId32, + call_hash: [::core::primitive::u8; 32usize], + }, + } + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Multisig<_0, _1, _2> { + pub when: runtime_types::pallet_multisig::Timepoint<_0>, + pub deposit: _1, + pub depositor: _2, + pub approvals: runtime_types::bounded_collections::bounded_vec::BoundedVec<_2>, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Timepoint<_0> { + pub height: _0, + pub index: _0, + } + } + pub mod pallet_session { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + #[codec(index = 0)] + set_keys { + keys: runtime_types::bridge_hub_rococo_runtime::SessionKeys, + proof: ::std::vec::Vec<::core::primitive::u8>, + }, + #[codec(index = 1)] + purge_keys, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + InvalidProof, + #[codec(index = 1)] + NoAssociatedValidatorId, + #[codec(index = 2)] + DuplicatedKey, + #[codec(index = 3)] + NoKeys, + #[codec(index = 4)] + NoAccount, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + NewSession { session_index: ::core::primitive::u32 }, + } + } + } + pub mod pallet_timestamp { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + #[codec(index = 0)] + set { + #[codec(compact)] + now: ::core::primitive::u64, + }, + } + } + } + pub mod pallet_transaction_payment { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + TransactionFeePaid { + who: ::sp_core::crypto::AccountId32, + actual_fee: ::core::primitive::u128, + tip: ::core::primitive::u128, + }, + } + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct ChargeTransactionPayment(#[codec(compact)] pub ::core::primitive::u128); + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Releases { + #[codec(index = 0)] + V1Ancient, + #[codec(index = 1)] + V2, + } + } + pub mod pallet_utility { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + #[codec(index = 0)] + batch { + calls: + ::std::vec::Vec, + }, + #[codec(index = 1)] + as_derivative { + index: ::core::primitive::u16, + call: ::std::boxed::Box< + runtime_types::bridge_hub_rococo_runtime::RuntimeCall, + >, + }, + #[codec(index = 2)] + batch_all { + calls: + ::std::vec::Vec, + }, + #[codec(index = 3)] + dispatch_as { + as_origin: ::std::boxed::Box< + runtime_types::bridge_hub_rococo_runtime::OriginCaller, + >, + call: ::std::boxed::Box< + runtime_types::bridge_hub_rococo_runtime::RuntimeCall, + >, + }, + #[codec(index = 4)] + force_batch { + calls: + ::std::vec::Vec, + }, + #[codec(index = 5)] + with_weight { + call: ::std::boxed::Box< + runtime_types::bridge_hub_rococo_runtime::RuntimeCall, + >, + weight: ::sp_weights::Weight, + }, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + TooManyCalls, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + BatchInterrupted { + index: ::core::primitive::u32, + error: runtime_types::sp_runtime::DispatchError, + }, + #[codec(index = 1)] + BatchCompleted, + #[codec(index = 2)] + BatchCompletedWithErrors, + #[codec(index = 3)] + ItemCompleted, + #[codec(index = 4)] + ItemFailed { error: runtime_types::sp_runtime::DispatchError }, + #[codec(index = 5)] + DispatchedAs { + result: + ::core::result::Result<(), runtime_types::sp_runtime::DispatchError>, + }, + } + } + } + pub mod pallet_xcm { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Call { + #[codec(index = 0)] + send { + dest: ::std::boxed::Box, + message: ::std::boxed::Box, + }, + #[codec(index = 1)] + teleport_assets { + dest: ::std::boxed::Box, + beneficiary: ::std::boxed::Box, + assets: ::std::boxed::Box, + fee_asset_item: ::core::primitive::u32, + }, + #[codec(index = 2)] + reserve_transfer_assets { + dest: ::std::boxed::Box, + beneficiary: ::std::boxed::Box, + assets: ::std::boxed::Box, + fee_asset_item: ::core::primitive::u32, + }, + #[codec(index = 3)] + execute { + message: ::std::boxed::Box, + max_weight: ::sp_weights::Weight, + }, + #[codec(index = 4)] + force_xcm_version { + location: + ::std::boxed::Box, + xcm_version: ::core::primitive::u32, + }, + #[codec(index = 5)] + force_default_xcm_version { + maybe_xcm_version: ::core::option::Option<::core::primitive::u32>, + }, + #[codec(index = 6)] + force_subscribe_version_notify { + location: ::std::boxed::Box, + }, + #[codec(index = 7)] + force_unsubscribe_version_notify { + location: ::std::boxed::Box, + }, + #[codec(index = 8)] + limited_reserve_transfer_assets { + dest: ::std::boxed::Box, + beneficiary: ::std::boxed::Box, + assets: ::std::boxed::Box, + fee_asset_item: ::core::primitive::u32, + weight_limit: runtime_types::xcm::v3::WeightLimit, + }, + #[codec(index = 9)] + limited_teleport_assets { + dest: ::std::boxed::Box, + beneficiary: ::std::boxed::Box, + assets: ::std::boxed::Box, + fee_asset_item: ::core::primitive::u32, + weight_limit: runtime_types::xcm::v3::WeightLimit, + }, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + Unreachable, + #[codec(index = 1)] + SendFailure, + #[codec(index = 2)] + Filtered, + #[codec(index = 3)] + UnweighableMessage, + #[codec(index = 4)] + DestinationNotInvertible, + #[codec(index = 5)] + Empty, + #[codec(index = 6)] + CannotReanchor, + #[codec(index = 7)] + TooManyAssets, + #[codec(index = 8)] + InvalidOrigin, + #[codec(index = 9)] + BadVersion, + #[codec(index = 10)] + BadLocation, + #[codec(index = 11)] + NoSubscription, + #[codec(index = 12)] + AlreadySubscribed, + #[codec(index = 13)] + InvalidAsset, + #[codec(index = 14)] + LowBalance, + #[codec(index = 15)] + TooManyLocks, + #[codec(index = 16)] + AccountNotSovereign, + #[codec(index = 17)] + FeesNotMet, + #[codec(index = 18)] + LockNotFound, + #[codec(index = 19)] + InUse, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Event { + #[codec(index = 0)] + Attempted(runtime_types::xcm::v3::traits::Outcome), + #[codec(index = 1)] + Sent( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::Xcm, + ), + #[codec(index = 2)] + UnexpectedResponse( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + ), + #[codec(index = 3)] + ResponseReady(::core::primitive::u64, runtime_types::xcm::v3::Response), + #[codec(index = 4)] + Notified(::core::primitive::u64, ::core::primitive::u8, ::core::primitive::u8), + #[codec(index = 5)] + NotifyOverweight( + ::core::primitive::u64, + ::core::primitive::u8, + ::core::primitive::u8, + ::sp_weights::Weight, + ::sp_weights::Weight, + ), + #[codec(index = 6)] + NotifyDispatchError( + ::core::primitive::u64, + ::core::primitive::u8, + ::core::primitive::u8, + ), + #[codec(index = 7)] + NotifyDecodeFailed( + ::core::primitive::u64, + ::core::primitive::u8, + ::core::primitive::u8, + ), + #[codec(index = 8)] + InvalidResponder( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + ), + #[codec(index = 9)] + InvalidResponderVersion( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + ), + #[codec(index = 10)] + ResponseTaken(::core::primitive::u64), + #[codec(index = 11)] + AssetsTrapped( + ::subxt::utils::H256, + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::VersionedMultiAssets, + ), + #[codec(index = 12)] + VersionChangeNotified( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u32, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 13)] + SupportedVersionChanged( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u32, + ), + #[codec(index = 14)] + NotifyTargetSendFail( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + runtime_types::xcm::v3::traits::Error, + ), + #[codec(index = 15)] + NotifyTargetMigrationFail( + runtime_types::xcm::VersionedMultiLocation, + ::core::primitive::u64, + ), + #[codec(index = 16)] + InvalidQuerierVersion( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + ), + #[codec(index = 17)] + InvalidQuerier( + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::primitive::u64, + runtime_types::xcm::v3::multilocation::MultiLocation, + ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + ), + #[codec(index = 18)] + VersionNotifyStarted( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 19)] + VersionNotifyRequested( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 20)] + VersionNotifyUnrequested( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 21)] + FeesPaid( + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::v3::multiasset::MultiAssets, + ), + #[codec(index = 22)] + AssetsClaimed( + ::subxt::utils::H256, + runtime_types::xcm::v3::multilocation::MultiLocation, + runtime_types::xcm::VersionedMultiAssets, + ), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Origin { + #[codec(index = 0)] + Xcm(runtime_types::xcm::v3::multilocation::MultiLocation), + #[codec(index = 1)] + Response(runtime_types::xcm::v3::multilocation::MultiLocation), + } + } + } + pub mod polkadot_core_primitives { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct InboundDownwardMessage<_0> { + pub sent_at: _0, + pub msg: ::std::vec::Vec<::core::primitive::u8>, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct InboundHrmpMessage<_0> { + pub sent_at: _0, + pub data: ::std::vec::Vec<::core::primitive::u8>, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct OutboundHrmpMessage<_0> { + pub recipient: _0, + pub data: ::std::vec::Vec<::core::primitive::u8>, + } + } + pub mod polkadot_parachain { + use super::runtime_types; + pub mod primitives { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct HeadData(pub ::std::vec::Vec<::core::primitive::u8>); + #[derive( + :: codec :: Decode, + :: codec :: Encode, + :: subxt :: ext :: codec :: CompactAs, + Clone, + Debug, + PartialEq, + )] + pub struct Id(pub ::core::primitive::u32); + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum XcmpMessageFormat { + #[codec(index = 0)] + ConcatenatedVersionedXcm, + #[codec(index = 1)] + ConcatenatedEncodedBlob, + #[codec(index = 2)] + Signals, + } + } + } + pub mod polkadot_primitives { + use super::runtime_types; + pub mod v4 { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct AbridgedHostConfiguration { + pub max_code_size: ::core::primitive::u32, + pub max_head_data_size: ::core::primitive::u32, + pub max_upward_queue_count: ::core::primitive::u32, + pub max_upward_queue_size: ::core::primitive::u32, + pub max_upward_message_size: ::core::primitive::u32, + pub max_upward_message_num_per_candidate: ::core::primitive::u32, + pub hrmp_max_message_num_per_candidate: ::core::primitive::u32, + pub validation_upgrade_cooldown: ::core::primitive::u32, + pub validation_upgrade_delay: ::core::primitive::u32, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct AbridgedHrmpChannel { + pub max_capacity: ::core::primitive::u32, + pub max_total_size: ::core::primitive::u32, + pub max_message_size: ::core::primitive::u32, + pub msg_count: ::core::primitive::u32, + pub total_size: ::core::primitive::u32, + pub mqc_head: ::core::option::Option<::subxt::utils::H256>, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct PersistedValidationData<_0, _1> { + pub parent_head: runtime_types::polkadot_parachain::primitives::HeadData, + pub relay_parent_number: _1, + pub relay_parent_storage_root: _0, + pub max_pov_size: _1, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum UpgradeRestriction { + #[codec(index = 0)] + Present, + } + } + } + pub mod sp_arithmetic { + use super::runtime_types; + pub mod fixed_point { + use super::runtime_types; + #[derive( + :: codec :: Decode, + :: codec :: Encode, + :: subxt :: ext :: codec :: CompactAs, + Clone, + Debug, + PartialEq, + )] + pub struct FixedU128(pub ::core::primitive::u128); + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum ArithmeticError { + #[codec(index = 0)] + Underflow, + #[codec(index = 1)] + Overflow, + #[codec(index = 2)] + DivisionByZero, + } + } + pub mod sp_consensus_aura { + use super::runtime_types; + pub mod sr25519 { + use super::runtime_types; + pub mod app_sr25519 { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Public(pub runtime_types::sp_core::sr25519::Public); + } + } + } + pub mod sp_consensus_grandpa { + use super::runtime_types; + pub mod app { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Public(pub runtime_types::sp_core::ed25519::Public); + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Signature(pub runtime_types::sp_core::ed25519::Signature); + } + } + pub mod sp_consensus_slots { + use super::runtime_types; + #[derive( + :: codec :: Decode, + :: codec :: Encode, + :: subxt :: ext :: codec :: CompactAs, + Clone, + Debug, + PartialEq, + )] + pub struct Slot(pub ::core::primitive::u64); + } + pub mod sp_core { + use super::runtime_types; + pub mod crypto { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct KeyTypeId(pub [::core::primitive::u8; 4usize]); + } + pub mod ecdsa { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Signature(pub [::core::primitive::u8; 65usize]); + } + pub mod ed25519 { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Public(pub [::core::primitive::u8; 32usize]); + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Signature(pub [::core::primitive::u8; 64usize]); + } + pub mod sr25519 { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Public(pub [::core::primitive::u8; 32usize]); + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Signature(pub [::core::primitive::u8; 64usize]); + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Void {} + } + pub mod sp_runtime { + use super::runtime_types; + pub mod generic { + use super::runtime_types; + pub mod digest { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum DigestItem { + #[codec(index = 6)] + PreRuntime( + [::core::primitive::u8; 4usize], + ::std::vec::Vec<::core::primitive::u8>, + ), + #[codec(index = 4)] + Consensus( + [::core::primitive::u8; 4usize], + ::std::vec::Vec<::core::primitive::u8>, + ), + #[codec(index = 5)] + Seal( + [::core::primitive::u8; 4usize], + ::std::vec::Vec<::core::primitive::u8>, + ), + #[codec(index = 0)] + Other(::std::vec::Vec<::core::primitive::u8>), + #[codec(index = 8)] + RuntimeEnvironmentUpdated, + } + } + pub mod unchecked_extrinsic { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct UncheckedExtrinsic<_0, _1, _2, _3>( + pub ::std::vec::Vec<::core::primitive::u8>, + #[codec(skip)] pub ::core::marker::PhantomData<(_0, _1, _2, _3)>, + ); + } + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum DispatchError { + #[codec(index = 0)] + Other, + #[codec(index = 1)] + CannotLookup, + #[codec(index = 2)] + BadOrigin, + #[codec(index = 3)] + Module(runtime_types::sp_runtime::ModuleError), + #[codec(index = 4)] + ConsumerRemaining, + #[codec(index = 5)] + NoProviders, + #[codec(index = 6)] + TooManyConsumers, + #[codec(index = 7)] + Token(runtime_types::sp_runtime::TokenError), + #[codec(index = 8)] + Arithmetic(runtime_types::sp_arithmetic::ArithmeticError), + #[codec(index = 9)] + Transactional(runtime_types::sp_runtime::TransactionalError), + #[codec(index = 10)] + Exhausted, + #[codec(index = 11)] + Corruption, + #[codec(index = 12)] + Unavailable, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct ModuleError { + pub index: ::core::primitive::u8, + pub error: [::core::primitive::u8; 4usize], + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum MultiSignature { + #[codec(index = 0)] + Ed25519(runtime_types::sp_core::ed25519::Signature), + #[codec(index = 1)] + Sr25519(runtime_types::sp_core::sr25519::Signature), + #[codec(index = 2)] + Ecdsa(runtime_types::sp_core::ecdsa::Signature), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum TokenError { + #[codec(index = 0)] + FundsUnavailable, + #[codec(index = 1)] + OnlyProvider, + #[codec(index = 2)] + BelowMinimum, + #[codec(index = 3)] + CannotCreate, + #[codec(index = 4)] + UnknownAsset, + #[codec(index = 5)] + Frozen, + #[codec(index = 6)] + Unsupported, + #[codec(index = 7)] + CannotCreateHold, + #[codec(index = 8)] + NotExpendable, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum TransactionalError { + #[codec(index = 0)] + LimitReached, + #[codec(index = 1)] + NoLayer, + } + } + pub mod sp_trie { + use super::runtime_types; + pub mod storage_proof { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct StorageProof { + pub trie_nodes: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, + } + } + } + pub mod sp_version { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct RuntimeVersion { + pub spec_name: ::std::string::String, + pub impl_name: ::std::string::String, + pub authoring_version: ::core::primitive::u32, + pub spec_version: ::core::primitive::u32, + pub impl_version: ::core::primitive::u32, + pub apis: + ::std::vec::Vec<([::core::primitive::u8; 8usize], ::core::primitive::u32)>, + pub transaction_version: ::core::primitive::u32, + pub state_version: ::core::primitive::u8, + } + } + pub mod sp_weights { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct RuntimeDbWeight { + pub read: ::core::primitive::u64, + pub write: ::core::primitive::u64, + } + } + pub mod xcm { + use super::runtime_types; + pub mod double_encoded { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct DoubleEncoded { + pub encoded: ::std::vec::Vec<::core::primitive::u8>, + } + } + pub mod v2 { + use super::runtime_types; + pub mod junction { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Junction { + #[codec(index = 0)] + Parachain(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 1)] + AccountId32 { + network: runtime_types::xcm::v2::NetworkId, + id: [::core::primitive::u8; 32usize], + }, + #[codec(index = 2)] + AccountIndex64 { + network: runtime_types::xcm::v2::NetworkId, + #[codec(compact)] + index: ::core::primitive::u64, + }, + #[codec(index = 3)] + AccountKey20 { + network: runtime_types::xcm::v2::NetworkId, + key: [::core::primitive::u8; 20usize], + }, + #[codec(index = 4)] + PalletInstance(::core::primitive::u8), + #[codec(index = 5)] + GeneralIndex(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 6)] + GeneralKey( + runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< + ::core::primitive::u8, + >, + ), + #[codec(index = 7)] + OnlyChild, + #[codec(index = 8)] + Plurality { + id: runtime_types::xcm::v2::BodyId, + part: runtime_types::xcm::v2::BodyPart, + }, + } + } + pub mod multiasset { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum AssetId { + #[codec(index = 0)] + Concrete(runtime_types::xcm::v2::multilocation::MultiLocation), + #[codec(index = 1)] + Abstract(::std::vec::Vec<::core::primitive::u8>), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum AssetInstance { + #[codec(index = 0)] + Undefined, + #[codec(index = 1)] + Index(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 2)] + Array4([::core::primitive::u8; 4usize]), + #[codec(index = 3)] + Array8([::core::primitive::u8; 8usize]), + #[codec(index = 4)] + Array16([::core::primitive::u8; 16usize]), + #[codec(index = 5)] + Array32([::core::primitive::u8; 32usize]), + #[codec(index = 6)] + Blob(::std::vec::Vec<::core::primitive::u8>), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Fungibility { + #[codec(index = 0)] + Fungible(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 1)] + NonFungible(runtime_types::xcm::v2::multiasset::AssetInstance), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct MultiAsset { + pub id: runtime_types::xcm::v2::multiasset::AssetId, + pub fun: runtime_types::xcm::v2::multiasset::Fungibility, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum MultiAssetFilter { + #[codec(index = 0)] + Definite(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 1)] + Wild(runtime_types::xcm::v2::multiasset::WildMultiAsset), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct MultiAssets( + pub ::std::vec::Vec, + ); + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum WildFungibility { + #[codec(index = 0)] + Fungible, + #[codec(index = 1)] + NonFungible, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum WildMultiAsset { + #[codec(index = 0)] + All, + #[codec(index = 1)] + AllOf { + id: runtime_types::xcm::v2::multiasset::AssetId, + fun: runtime_types::xcm::v2::multiasset::WildFungibility, + }, + } + } + pub mod multilocation { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Junctions { + #[codec(index = 0)] + Here, + #[codec(index = 1)] + X1(runtime_types::xcm::v2::junction::Junction), + #[codec(index = 2)] + X2( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 3)] + X3( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 4)] + X4( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 5)] + X5( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 6)] + X6( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 7)] + X7( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + #[codec(index = 8)] + X8( + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + runtime_types::xcm::v2::junction::Junction, + ), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct MultiLocation { + pub parents: ::core::primitive::u8, + pub interior: runtime_types::xcm::v2::multilocation::Junctions, + } + } + pub mod traits { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + Overflow, + #[codec(index = 1)] + Unimplemented, + #[codec(index = 2)] + UntrustedReserveLocation, + #[codec(index = 3)] + UntrustedTeleportLocation, + #[codec(index = 4)] + MultiLocationFull, + #[codec(index = 5)] + MultiLocationNotInvertible, + #[codec(index = 6)] + BadOrigin, + #[codec(index = 7)] + InvalidLocation, + #[codec(index = 8)] + AssetNotFound, + #[codec(index = 9)] + FailedToTransactAsset, + #[codec(index = 10)] + NotWithdrawable, + #[codec(index = 11)] + LocationCannotHold, + #[codec(index = 12)] + ExceedsMaxMessageSize, + #[codec(index = 13)] + DestinationUnsupported, + #[codec(index = 14)] + Transport, + #[codec(index = 15)] + Unroutable, + #[codec(index = 16)] + UnknownClaim, + #[codec(index = 17)] + FailedToDecode, + #[codec(index = 18)] + MaxWeightInvalid, + #[codec(index = 19)] + NotHoldingFees, + #[codec(index = 20)] + TooExpensive, + #[codec(index = 21)] + Trap(::core::primitive::u64), + #[codec(index = 22)] + UnhandledXcmVersion, + #[codec(index = 23)] + WeightLimitReached(::core::primitive::u64), + #[codec(index = 24)] + Barrier, + #[codec(index = 25)] + WeightNotComputable, + } + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum BodyId { + #[codec(index = 0)] + Unit, + #[codec(index = 1)] + Named( + runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< + ::core::primitive::u8, + >, + ), + #[codec(index = 2)] + Index(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 3)] + Executive, + #[codec(index = 4)] + Technical, + #[codec(index = 5)] + Legislative, + #[codec(index = 6)] + Judicial, + #[codec(index = 7)] + Defense, + #[codec(index = 8)] + Administration, + #[codec(index = 9)] + Treasury, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum BodyPart { + #[codec(index = 0)] + Voice, + #[codec(index = 1)] + Members { + #[codec(compact)] + count: ::core::primitive::u32, + }, + #[codec(index = 2)] + Fraction { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + #[codec(index = 3)] + AtLeastProportion { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + #[codec(index = 4)] + MoreThanProportion { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Instruction { + #[codec(index = 0)] + WithdrawAsset(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 1)] + ReserveAssetDeposited(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 2)] + ReceiveTeleportedAsset(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 3)] + QueryResponse { + #[codec(compact)] + query_id: ::core::primitive::u64, + response: runtime_types::xcm::v2::Response, + #[codec(compact)] + max_weight: ::core::primitive::u64, + }, + #[codec(index = 4)] + TransferAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssets, + beneficiary: runtime_types::xcm::v2::multilocation::MultiLocation, + }, + #[codec(index = 5)] + TransferReserveAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssets, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + xcm: runtime_types::xcm::v2::Xcm, + }, + #[codec(index = 6)] + Transact { + origin_type: runtime_types::xcm::v2::OriginKind, + #[codec(compact)] + require_weight_at_most: ::core::primitive::u64, + call: runtime_types::xcm::double_encoded::DoubleEncoded, + }, + #[codec(index = 7)] + HrmpNewChannelOpenRequest { + #[codec(compact)] + sender: ::core::primitive::u32, + #[codec(compact)] + max_message_size: ::core::primitive::u32, + #[codec(compact)] + max_capacity: ::core::primitive::u32, + }, + #[codec(index = 8)] + HrmpChannelAccepted { + #[codec(compact)] + recipient: ::core::primitive::u32, + }, + #[codec(index = 9)] + HrmpChannelClosing { + #[codec(compact)] + initiator: ::core::primitive::u32, + #[codec(compact)] + sender: ::core::primitive::u32, + #[codec(compact)] + recipient: ::core::primitive::u32, + }, + #[codec(index = 10)] + ClearOrigin, + #[codec(index = 11)] + DescendOrigin(runtime_types::xcm::v2::multilocation::Junctions), + #[codec(index = 12)] + ReportError { + #[codec(compact)] + query_id: ::core::primitive::u64, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + #[codec(compact)] + max_response_weight: ::core::primitive::u64, + }, + #[codec(index = 13)] + DepositAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + #[codec(compact)] + max_assets: ::core::primitive::u32, + beneficiary: runtime_types::xcm::v2::multilocation::MultiLocation, + }, + #[codec(index = 14)] + DepositReserveAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + #[codec(compact)] + max_assets: ::core::primitive::u32, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + xcm: runtime_types::xcm::v2::Xcm, + }, + #[codec(index = 15)] + ExchangeAsset { + give: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + receive: runtime_types::xcm::v2::multiasset::MultiAssets, + }, + #[codec(index = 16)] + InitiateReserveWithdraw { + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + reserve: runtime_types::xcm::v2::multilocation::MultiLocation, + xcm: runtime_types::xcm::v2::Xcm, + }, + #[codec(index = 17)] + InitiateTeleport { + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + xcm: runtime_types::xcm::v2::Xcm, + }, + #[codec(index = 18)] + QueryHolding { + #[codec(compact)] + query_id: ::core::primitive::u64, + dest: runtime_types::xcm::v2::multilocation::MultiLocation, + assets: runtime_types::xcm::v2::multiasset::MultiAssetFilter, + #[codec(compact)] + max_response_weight: ::core::primitive::u64, + }, + #[codec(index = 19)] + BuyExecution { + fees: runtime_types::xcm::v2::multiasset::MultiAsset, + weight_limit: runtime_types::xcm::v2::WeightLimit, + }, + #[codec(index = 20)] + RefundSurplus, + #[codec(index = 21)] + SetErrorHandler(runtime_types::xcm::v2::Xcm), + #[codec(index = 22)] + SetAppendix(runtime_types::xcm::v2::Xcm), + #[codec(index = 23)] + ClearError, + #[codec(index = 24)] + ClaimAsset { + assets: runtime_types::xcm::v2::multiasset::MultiAssets, + ticket: runtime_types::xcm::v2::multilocation::MultiLocation, + }, + #[codec(index = 25)] + Trap(#[codec(compact)] ::core::primitive::u64), + #[codec(index = 26)] + SubscribeVersion { + #[codec(compact)] + query_id: ::core::primitive::u64, + #[codec(compact)] + max_response_weight: ::core::primitive::u64, + }, + #[codec(index = 27)] + UnsubscribeVersion, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum NetworkId { + #[codec(index = 0)] + Any, + #[codec(index = 1)] + Named( + runtime_types::bounded_collections::weak_bounded_vec::WeakBoundedVec< + ::core::primitive::u8, + >, + ), + #[codec(index = 2)] + Polkadot, + #[codec(index = 3)] + Kusama, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum OriginKind { + #[codec(index = 0)] + Native, + #[codec(index = 1)] + SovereignAccount, + #[codec(index = 2)] + Superuser, + #[codec(index = 3)] + Xcm, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Response { + #[codec(index = 0)] + Null, + #[codec(index = 1)] + Assets(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 2)] + ExecutionResult( + ::core::option::Option<( + ::core::primitive::u32, + runtime_types::xcm::v2::traits::Error, + )>, + ), + #[codec(index = 3)] + Version(::core::primitive::u32), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum WeightLimit { + #[codec(index = 0)] + Unlimited, + #[codec(index = 1)] + Limited(#[codec(compact)] ::core::primitive::u64), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Xcm(pub ::std::vec::Vec); + } + pub mod v3 { + use super::runtime_types; + pub mod junction { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum BodyId { + #[codec(index = 0)] + Unit, + #[codec(index = 1)] + Moniker([::core::primitive::u8; 4usize]), + #[codec(index = 2)] + Index(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 3)] + Executive, + #[codec(index = 4)] + Technical, + #[codec(index = 5)] + Legislative, + #[codec(index = 6)] + Judicial, + #[codec(index = 7)] + Defense, + #[codec(index = 8)] + Administration, + #[codec(index = 9)] + Treasury, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum BodyPart { + #[codec(index = 0)] + Voice, + #[codec(index = 1)] + Members { + #[codec(compact)] + count: ::core::primitive::u32, + }, + #[codec(index = 2)] + Fraction { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + #[codec(index = 3)] + AtLeastProportion { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + #[codec(index = 4)] + MoreThanProportion { + #[codec(compact)] + nom: ::core::primitive::u32, + #[codec(compact)] + denom: ::core::primitive::u32, + }, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Junction { + #[codec(index = 0)] + Parachain(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 1)] + AccountId32 { + network: + ::core::option::Option, + id: [::core::primitive::u8; 32usize], + }, + #[codec(index = 2)] + AccountIndex64 { + network: + ::core::option::Option, + #[codec(compact)] + index: ::core::primitive::u64, + }, + #[codec(index = 3)] + AccountKey20 { + network: + ::core::option::Option, + key: [::core::primitive::u8; 20usize], + }, + #[codec(index = 4)] + PalletInstance(::core::primitive::u8), + #[codec(index = 5)] + GeneralIndex(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 6)] + GeneralKey { + length: ::core::primitive::u8, + data: [::core::primitive::u8; 32usize], + }, + #[codec(index = 7)] + OnlyChild, + #[codec(index = 8)] + Plurality { + id: runtime_types::xcm::v3::junction::BodyId, + part: runtime_types::xcm::v3::junction::BodyPart, + }, + #[codec(index = 9)] + GlobalConsensus(runtime_types::xcm::v3::junction::NetworkId), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum NetworkId { + #[codec(index = 0)] + ByGenesis([::core::primitive::u8; 32usize]), + #[codec(index = 1)] + ByFork { + block_number: ::core::primitive::u64, + block_hash: [::core::primitive::u8; 32usize], + }, + #[codec(index = 2)] + Polkadot, + #[codec(index = 3)] + Kusama, + #[codec(index = 4)] + Westend, + #[codec(index = 5)] + Rococo, + #[codec(index = 6)] + Wococo, + #[codec(index = 7)] + Ethereum { + #[codec(compact)] + chain_id: ::core::primitive::u64, + }, + #[codec(index = 8)] + BitcoinCore, + #[codec(index = 9)] + BitcoinCash, + } + } + pub mod junctions { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Junctions { + #[codec(index = 0)] + Here, + #[codec(index = 1)] + X1(runtime_types::xcm::v3::junction::Junction), + #[codec(index = 2)] + X2( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 3)] + X3( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 4)] + X4( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 5)] + X5( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 6)] + X6( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 7)] + X7( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + #[codec(index = 8)] + X8( + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + runtime_types::xcm::v3::junction::Junction, + ), + } + } + pub mod multiasset { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum AssetId { + #[codec(index = 0)] + Concrete(runtime_types::xcm::v3::multilocation::MultiLocation), + #[codec(index = 1)] + Abstract([::core::primitive::u8; 32usize]), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum AssetInstance { + #[codec(index = 0)] + Undefined, + #[codec(index = 1)] + Index(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 2)] + Array4([::core::primitive::u8; 4usize]), + #[codec(index = 3)] + Array8([::core::primitive::u8; 8usize]), + #[codec(index = 4)] + Array16([::core::primitive::u8; 16usize]), + #[codec(index = 5)] + Array32([::core::primitive::u8; 32usize]), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Fungibility { + #[codec(index = 0)] + Fungible(#[codec(compact)] ::core::primitive::u128), + #[codec(index = 1)] + NonFungible(runtime_types::xcm::v3::multiasset::AssetInstance), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct MultiAsset { + pub id: runtime_types::xcm::v3::multiasset::AssetId, + pub fun: runtime_types::xcm::v3::multiasset::Fungibility, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum MultiAssetFilter { + #[codec(index = 0)] + Definite(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 1)] + Wild(runtime_types::xcm::v3::multiasset::WildMultiAsset), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct MultiAssets( + pub ::std::vec::Vec, + ); + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum WildFungibility { + #[codec(index = 0)] + Fungible, + #[codec(index = 1)] + NonFungible, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum WildMultiAsset { + #[codec(index = 0)] + All, + #[codec(index = 1)] + AllOf { + id: runtime_types::xcm::v3::multiasset::AssetId, + fun: runtime_types::xcm::v3::multiasset::WildFungibility, + }, + #[codec(index = 2)] + AllCounted(#[codec(compact)] ::core::primitive::u32), + #[codec(index = 3)] + AllOfCounted { + id: runtime_types::xcm::v3::multiasset::AssetId, + fun: runtime_types::xcm::v3::multiasset::WildFungibility, + #[codec(compact)] + count: ::core::primitive::u32, + }, + } + } + pub mod multilocation { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct MultiLocation { + pub parents: ::core::primitive::u8, + pub interior: runtime_types::xcm::v3::junctions::Junctions, + } + } + pub mod traits { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Error { + #[codec(index = 0)] + Overflow, + #[codec(index = 1)] + Unimplemented, + #[codec(index = 2)] + UntrustedReserveLocation, + #[codec(index = 3)] + UntrustedTeleportLocation, + #[codec(index = 4)] + LocationFull, + #[codec(index = 5)] + LocationNotInvertible, + #[codec(index = 6)] + BadOrigin, + #[codec(index = 7)] + InvalidLocation, + #[codec(index = 8)] + AssetNotFound, + #[codec(index = 9)] + FailedToTransactAsset, + #[codec(index = 10)] + NotWithdrawable, + #[codec(index = 11)] + LocationCannotHold, + #[codec(index = 12)] + ExceedsMaxMessageSize, + #[codec(index = 13)] + DestinationUnsupported, + #[codec(index = 14)] + Transport, + #[codec(index = 15)] + Unroutable, + #[codec(index = 16)] + UnknownClaim, + #[codec(index = 17)] + FailedToDecode, + #[codec(index = 18)] + MaxWeightInvalid, + #[codec(index = 19)] + NotHoldingFees, + #[codec(index = 20)] + TooExpensive, + #[codec(index = 21)] + Trap(::core::primitive::u64), + #[codec(index = 22)] + ExpectationFalse, + #[codec(index = 23)] + PalletNotFound, + #[codec(index = 24)] + NameMismatch, + #[codec(index = 25)] + VersionIncompatible, + #[codec(index = 26)] + HoldingWouldOverflow, + #[codec(index = 27)] + ExportError, + #[codec(index = 28)] + ReanchorFailed, + #[codec(index = 29)] + NoDeal, + #[codec(index = 30)] + FeesNotMet, + #[codec(index = 31)] + LockError, + #[codec(index = 32)] + NoPermission, + #[codec(index = 33)] + Unanchored, + #[codec(index = 34)] + NotDepositable, + #[codec(index = 35)] + UnhandledXcmVersion, + #[codec(index = 36)] + WeightLimitReached(::sp_weights::Weight), + #[codec(index = 37)] + Barrier, + #[codec(index = 38)] + WeightNotComputable, + #[codec(index = 39)] + ExceedsStackLimit, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Outcome { + #[codec(index = 0)] + Complete(::sp_weights::Weight), + #[codec(index = 1)] + Incomplete(::sp_weights::Weight, runtime_types::xcm::v3::traits::Error), + #[codec(index = 2)] + Error(runtime_types::xcm::v3::traits::Error), + } + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Instruction { + #[codec(index = 0)] + WithdrawAsset(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 1)] + ReserveAssetDeposited(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 2)] + ReceiveTeleportedAsset(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 3)] + QueryResponse { + #[codec(compact)] + query_id: ::core::primitive::u64, + response: runtime_types::xcm::v3::Response, + max_weight: ::sp_weights::Weight, + querier: ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + }, + #[codec(index = 4)] + TransferAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssets, + beneficiary: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 5)] + TransferReserveAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssets, + dest: runtime_types::xcm::v3::multilocation::MultiLocation, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 6)] + Transact { + origin_kind: runtime_types::xcm::v2::OriginKind, + require_weight_at_most: ::sp_weights::Weight, + call: runtime_types::xcm::double_encoded::DoubleEncoded, + }, + #[codec(index = 7)] + HrmpNewChannelOpenRequest { + #[codec(compact)] + sender: ::core::primitive::u32, + #[codec(compact)] + max_message_size: ::core::primitive::u32, + #[codec(compact)] + max_capacity: ::core::primitive::u32, + }, + #[codec(index = 8)] + HrmpChannelAccepted { + #[codec(compact)] + recipient: ::core::primitive::u32, + }, + #[codec(index = 9)] + HrmpChannelClosing { + #[codec(compact)] + initiator: ::core::primitive::u32, + #[codec(compact)] + sender: ::core::primitive::u32, + #[codec(compact)] + recipient: ::core::primitive::u32, + }, + #[codec(index = 10)] + ClearOrigin, + #[codec(index = 11)] + DescendOrigin(runtime_types::xcm::v3::junctions::Junctions), + #[codec(index = 12)] + ReportError(runtime_types::xcm::v3::QueryResponseInfo), + #[codec(index = 13)] + DepositAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + beneficiary: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 14)] + DepositReserveAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + dest: runtime_types::xcm::v3::multilocation::MultiLocation, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 15)] + ExchangeAsset { + give: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + want: runtime_types::xcm::v3::multiasset::MultiAssets, + maximal: ::core::primitive::bool, + }, + #[codec(index = 16)] + InitiateReserveWithdraw { + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + reserve: runtime_types::xcm::v3::multilocation::MultiLocation, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 17)] + InitiateTeleport { + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + dest: runtime_types::xcm::v3::multilocation::MultiLocation, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 18)] + ReportHolding { + response_info: runtime_types::xcm::v3::QueryResponseInfo, + assets: runtime_types::xcm::v3::multiasset::MultiAssetFilter, + }, + #[codec(index = 19)] + BuyExecution { + fees: runtime_types::xcm::v3::multiasset::MultiAsset, + weight_limit: runtime_types::xcm::v3::WeightLimit, + }, + #[codec(index = 20)] + RefundSurplus, + #[codec(index = 21)] + SetErrorHandler(runtime_types::xcm::v3::Xcm), + #[codec(index = 22)] + SetAppendix(runtime_types::xcm::v3::Xcm), + #[codec(index = 23)] + ClearError, + #[codec(index = 24)] + ClaimAsset { + assets: runtime_types::xcm::v3::multiasset::MultiAssets, + ticket: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 25)] + Trap(#[codec(compact)] ::core::primitive::u64), + #[codec(index = 26)] + SubscribeVersion { + #[codec(compact)] + query_id: ::core::primitive::u64, + max_response_weight: ::sp_weights::Weight, + }, + #[codec(index = 27)] + UnsubscribeVersion, + #[codec(index = 28)] + BurnAsset(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 29)] + ExpectAsset(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 30)] + ExpectOrigin( + ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + ), + #[codec(index = 31)] + ExpectError( + ::core::option::Option<( + ::core::primitive::u32, + runtime_types::xcm::v3::traits::Error, + )>, + ), + #[codec(index = 32)] + ExpectTransactStatus(runtime_types::xcm::v3::MaybeErrorCode), + #[codec(index = 33)] + QueryPallet { + module_name: ::std::vec::Vec<::core::primitive::u8>, + response_info: runtime_types::xcm::v3::QueryResponseInfo, + }, + #[codec(index = 34)] + ExpectPallet { + #[codec(compact)] + index: ::core::primitive::u32, + name: ::std::vec::Vec<::core::primitive::u8>, + module_name: ::std::vec::Vec<::core::primitive::u8>, + #[codec(compact)] + crate_major: ::core::primitive::u32, + #[codec(compact)] + min_crate_minor: ::core::primitive::u32, + }, + #[codec(index = 35)] + ReportTransactStatus(runtime_types::xcm::v3::QueryResponseInfo), + #[codec(index = 36)] + ClearTransactStatus, + #[codec(index = 37)] + UniversalOrigin(runtime_types::xcm::v3::junction::Junction), + #[codec(index = 38)] + ExportMessage { + network: runtime_types::xcm::v3::junction::NetworkId, + destination: runtime_types::xcm::v3::junctions::Junctions, + xcm: runtime_types::xcm::v3::Xcm, + }, + #[codec(index = 39)] + LockAsset { + asset: runtime_types::xcm::v3::multiasset::MultiAsset, + unlocker: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 40)] + UnlockAsset { + asset: runtime_types::xcm::v3::multiasset::MultiAsset, + target: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 41)] + NoteUnlockable { + asset: runtime_types::xcm::v3::multiasset::MultiAsset, + owner: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 42)] + RequestUnlock { + asset: runtime_types::xcm::v3::multiasset::MultiAsset, + locker: runtime_types::xcm::v3::multilocation::MultiLocation, + }, + #[codec(index = 43)] + SetFeesMode { jit_withdraw: ::core::primitive::bool }, + #[codec(index = 44)] + SetTopic([::core::primitive::u8; 32usize]), + #[codec(index = 45)] + ClearTopic, + #[codec(index = 46)] + AliasOrigin(runtime_types::xcm::v3::multilocation::MultiLocation), + #[codec(index = 47)] + UnpaidExecution { + weight_limit: runtime_types::xcm::v3::WeightLimit, + check_origin: ::core::option::Option< + runtime_types::xcm::v3::multilocation::MultiLocation, + >, + }, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum MaybeErrorCode { + #[codec(index = 0)] + Success, + #[codec(index = 1)] + Error( + runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + ), + #[codec(index = 2)] + TruncatedError( + runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + ), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct PalletInfo { + #[codec(compact)] + pub index: ::core::primitive::u32, + pub name: runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + pub module_name: runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + #[codec(compact)] + pub major: ::core::primitive::u32, + #[codec(compact)] + pub minor: ::core::primitive::u32, + #[codec(compact)] + pub patch: ::core::primitive::u32, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct QueryResponseInfo { + pub destination: runtime_types::xcm::v3::multilocation::MultiLocation, + #[codec(compact)] + pub query_id: ::core::primitive::u64, + pub max_weight: ::sp_weights::Weight, + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum Response { + #[codec(index = 0)] + Null, + #[codec(index = 1)] + Assets(runtime_types::xcm::v3::multiasset::MultiAssets), + #[codec(index = 2)] + ExecutionResult( + ::core::option::Option<( + ::core::primitive::u32, + runtime_types::xcm::v3::traits::Error, + )>, + ), + #[codec(index = 3)] + Version(::core::primitive::u32), + #[codec(index = 4)] + PalletsInfo( + runtime_types::bounded_collections::bounded_vec::BoundedVec< + runtime_types::xcm::v3::PalletInfo, + >, + ), + #[codec(index = 5)] + DispatchResult(runtime_types::xcm::v3::MaybeErrorCode), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum WeightLimit { + #[codec(index = 0)] + Unlimited, + #[codec(index = 1)] + Limited(::sp_weights::Weight), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Xcm(pub ::std::vec::Vec); + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum VersionedMultiAssets { + #[codec(index = 1)] + V2(runtime_types::xcm::v2::multiasset::MultiAssets), + #[codec(index = 3)] + V3(runtime_types::xcm::v3::multiasset::MultiAssets), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum VersionedMultiLocation { + #[codec(index = 1)] + V2(runtime_types::xcm::v2::multilocation::MultiLocation), + #[codec(index = 3)] + V3(runtime_types::xcm::v3::multilocation::MultiLocation), + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub enum VersionedXcm { + #[codec(index = 2)] + V2(runtime_types::xcm::v2::Xcm), + #[codec(index = 3)] + V3(runtime_types::xcm::v3::Xcm), + } + } + } +} diff --git a/relays/client-bridge-hub-rococo/src/lib.rs b/relays/client-bridge-hub-rococo/src/lib.rs index bc2ec3aca41..2a059595395 100644 --- a/relays/client-bridge-hub-rococo/src/lib.rs +++ b/relays/client-bridge-hub-rococo/src/lib.rs @@ -16,22 +16,29 @@ //! Types used to connect to the BridgeHub-Rococo-Substrate parachain. -use bp_bridge_hub_rococo::{BridgeHubSignedExtension, AVERAGE_BLOCK_INTERVAL}; +pub mod codegen_runtime; + +use bp_bridge_hub_rococo::{BridgeHubSignedExtension, SignedExtension, AVERAGE_BLOCK_INTERVAL}; use bp_messages::MessageNonce; use bp_runtime::ChainId; use codec::Encode; use relay_substrate_client::{ - Chain, ChainWithBalances, ChainWithMessages, ChainWithTransactions, ChainWithUtilityPallet, - Error as SubstrateError, MockedRuntimeUtilityPallet, SignParam, UnderlyingChainProvider, - UnsignedTransaction, + calls::UtilityCall as MockUtilityCall, Chain, ChainWithBalances, ChainWithMessages, + ChainWithTransactions, ChainWithUtilityPallet, Error as SubstrateError, + MockedRuntimeUtilityPallet, SignParam, UnderlyingChainProvider, UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; use std::time::Duration; -/// Re-export runtime wrapper -pub mod runtime_wrapper; -pub use runtime_wrapper as runtime; +pub use codegen_runtime::api::runtime_types; + +pub type RuntimeCall = runtime_types::bridge_hub_rococo_runtime::RuntimeCall; +pub type BridgeMessagesCall = runtime_types::pallet_bridge_messages::pallet::Call; +pub type BridgeGrandpaCall = runtime_types::pallet_bridge_grandpa::pallet::Call; +pub type BridgeParachainCall = runtime_types::pallet_bridge_parachains::pallet::Call; +type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic; +type UtilityCall = runtime_types::pallet_utility::pallet::Call; /// Rococo chain definition #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -49,7 +56,7 @@ impl Chain for BridgeHubRococo { const AVERAGE_BLOCK_INTERVAL: Duration = AVERAGE_BLOCK_INTERVAL; type SignedBlock = bp_bridge_hub_rococo::SignedBlock; - type Call = runtime::Call; + type Call = RuntimeCall; } impl ChainWithBalances for BridgeHubRococo { @@ -58,13 +65,22 @@ impl ChainWithBalances for BridgeHubRococo { } } +impl From> for RuntimeCall { + fn from(value: MockUtilityCall) -> RuntimeCall { + match value { + MockUtilityCall::batch_all(calls) => + RuntimeCall::Utility(UtilityCall::batch_all { calls }), + } + } +} + impl ChainWithUtilityPallet for BridgeHubRococo { - type UtilityPallet = MockedRuntimeUtilityPallet; + type UtilityPallet = MockedRuntimeUtilityPallet; } impl ChainWithTransactions for BridgeHubRococo { type AccountKeyPair = sp_core::sr25519::Pair; - type SignedTransaction = runtime::UncheckedExtrinsic; + type SignedTransaction = UncheckedExtrinsic; fn sign_transaction( param: SignParam, @@ -72,7 +88,7 @@ impl ChainWithTransactions for BridgeHubRococo { ) -> Result { let raw_payload = SignedPayload::new( unsigned.call, - runtime::SignedExtension::from_params( + SignedExtension::from_params( param.spec_version, param.transaction_version, unsigned.era, @@ -86,7 +102,7 @@ impl ChainWithTransactions for BridgeHubRococo { let signer: sp_runtime::MultiSigner = param.signer.public().into(); let (call, extra, _) = raw_payload.deconstruct(); - Ok(runtime::UncheckedExtrinsic::new_signed( + Ok(UncheckedExtrinsic::new_signed( call, signer.into_account().into(), signature.into(), @@ -135,13 +151,13 @@ mod tests { use super::*; use relay_substrate_client::TransactionEra; + type SystemCall = runtime_types::frame_system::pallet::Call; + #[test] fn parse_transaction_works() { let unsigned = UnsignedTransaction { - call: runtime::Call::System(relay_substrate_client::calls::SystemCall::remark( - b"Hello world!".to_vec(), - )) - .into(), + call: RuntimeCall::System(SystemCall::remark { remark: b"Hello world!".to_vec() }) + .into(), nonce: 777, tip: 888, era: TransactionEra::immortal(), diff --git a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs deleted file mode 100644 index 26e0bd9da5e..00000000000 --- a/relays/client-bridge-hub-rococo/src/runtime_wrapper.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common 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. - -// Parity Bridges Common 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 Parity Bridges Common. If not, see . - -//! Types that are specific to the BridgeHubRococo runtime. - -use codec::{Decode, Encode}; -use scale_info::TypeInfo; - -pub use bp_bridge_hub_rococo::SignedExtension; -pub use bp_header_chain::BridgeGrandpaCallOf; -pub use bp_parachains::BridgeParachainCall; -pub use bridge_runtime_common::messages::BridgeMessagesCallOf; -pub use relay_substrate_client::calls::{SystemCall, UtilityCall}; - -/// Unchecked BridgeHubRococo extrinsic. -pub type UncheckedExtrinsic = bp_bridge_hub_rococo::UncheckedExtrinsic; - -// The indirect pallet call used to sync `Wococo` GRANDPA finality to `BHRococo`. -pub type BridgeWococoGrandpaCall = BridgeGrandpaCallOf; -// The indirect pallet call used to sync `BridgeHubWococo` messages to `BHRococo`. -pub type BridgeWococoMessagesCall = BridgeMessagesCallOf; - -/// `BridgeHubRococo` Runtime `Call` enum. -/// -/// The enum represents a subset of possible `Call`s we can send to `BridgeHubRococo` chain. -/// Ideally this code would be auto-generated from metadata, because we want to -/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. -/// -/// All entries here (like pretty much in the entire file) must be kept in sync with -/// `BridgeHubRococo` `construct_runtime`, so that we maintain SCALE-compatibility. -#[allow(clippy::large_enum_variant)] -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub enum Call { - #[cfg(test)] - #[codec(index = 0)] - System(SystemCall), - /// Utility pallet. - #[codec(index = 40)] - Utility(UtilityCall), - - /// Wococo bridge pallet. - #[codec(index = 41)] - BridgeWococoGrandpa(BridgeWococoGrandpaCall), - /// Wococo parachain bridge pallet. - #[codec(index = 42)] - BridgeWococoParachain(BridgeParachainCall), - /// Wococo messages bridge pallet. - #[codec(index = 46)] - BridgeWococoMessages(BridgeWococoMessagesCall), -} - -impl From> for Call { - fn from(call: UtilityCall) -> Call { - Call::Utility(call) - } -} diff --git a/relays/client-bridge-hub-wococo/Cargo.toml b/relays/client-bridge-hub-wococo/Cargo.toml index 72a25b001e5..e5c0684ba0a 100644 --- a/relays/client-bridge-hub-wococo/Cargo.toml +++ b/relays/client-bridge-hub-wococo/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", features = ["derive"] } scale-info = { version = "2.6.0", default-features = false, features = ["derive"] } -relay-substrate-client = { path = "../client-substrate" } +subxt = { version = "0.28.0", default-features = false, features = [] } # Bridge dependencies @@ -17,16 +17,19 @@ bp-bridge-hub-wococo = { path = "../../primitives/chain-bridge-hub-wococo" } bp-header-chain = { path = "../../primitives/header-chain" } bp-messages = { path = "../../primitives/messages" } bp-parachains = { path = "../../primitives/parachains" } +bp-polkadot-core = { path = "../../primitives/polkadot-core" } bp-rococo = { path = "../../primitives/chain-rococo" } bp-runtime = { path = "../../primitives/runtime" } bridge-runtime-common = { path = "../../bin/runtime-common" } +relay-bridge-hub-rococo-client = { path = "../client-bridge-hub-rococo" } +relay-substrate-client = { path = "../client-substrate" } # Substrate Dependencies sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-weights = { git = "https://github.com/paritytech/substrate", branch = "master" } [dev-dependencies] -bp-polkadot-core = { path = "../../primitives/polkadot-core" } sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/relays/client-bridge-hub-wococo/src/lib.rs b/relays/client-bridge-hub-wococo/src/lib.rs index c9acc47dd95..0e2b9839301 100644 --- a/relays/client-bridge-hub-wococo/src/lib.rs +++ b/relays/client-bridge-hub-wococo/src/lib.rs @@ -16,7 +16,7 @@ //! Types used to connect to the BridgeHub-Wococo-Substrate parachain. -use bp_bridge_hub_wococo::{BridgeHubSignedExtension, AVERAGE_BLOCK_INTERVAL}; +use bp_bridge_hub_wococo::{BridgeHubSignedExtension, SignedExtension, AVERAGE_BLOCK_INTERVAL}; use bp_messages::MessageNonce; use bp_runtime::ChainId; use codec::Encode; @@ -29,9 +29,14 @@ use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; use std::time::Duration; -/// Re-export runtime wrapper -pub mod runtime_wrapper; -pub use runtime_wrapper as runtime; +pub use codegen_runtime::api::runtime_types; +use relay_bridge_hub_rococo_client::codegen_runtime; + +pub type RuntimeCall = runtime_types::bridge_hub_rococo_runtime::RuntimeCall; +pub type BridgeMessagesCall = runtime_types::pallet_bridge_messages::pallet::Call; +pub type BridgeGrandpaCall = runtime_types::pallet_bridge_grandpa::pallet::Call; +pub type BridgeParachainCall = runtime_types::pallet_bridge_parachains::pallet::Call; +type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic; /// Wococo chain definition #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -49,7 +54,7 @@ impl Chain for BridgeHubWococo { const AVERAGE_BLOCK_INTERVAL: Duration = AVERAGE_BLOCK_INTERVAL; type SignedBlock = bp_bridge_hub_wococo::SignedBlock; - type Call = runtime::Call; + type Call = RuntimeCall; } impl ChainWithBalances for BridgeHubWococo { @@ -59,12 +64,12 @@ impl ChainWithBalances for BridgeHubWococo { } impl ChainWithUtilityPallet for BridgeHubWococo { - type UtilityPallet = MockedRuntimeUtilityPallet; + type UtilityPallet = MockedRuntimeUtilityPallet; } impl ChainWithTransactions for BridgeHubWococo { type AccountKeyPair = sp_core::sr25519::Pair; - type SignedTransaction = runtime::UncheckedExtrinsic; + type SignedTransaction = UncheckedExtrinsic; fn sign_transaction( param: SignParam, @@ -72,7 +77,7 @@ impl ChainWithTransactions for BridgeHubWococo { ) -> Result { let raw_payload = SignedPayload::new( unsigned.call, - runtime::SignedExtension::from_params( + SignedExtension::from_params( param.spec_version, param.transaction_version, unsigned.era, @@ -86,7 +91,7 @@ impl ChainWithTransactions for BridgeHubWococo { let signer: sp_runtime::MultiSigner = param.signer.public().into(); let (call, extra, _) = raw_payload.deconstruct(); - Ok(runtime::UncheckedExtrinsic::new_signed( + Ok(UncheckedExtrinsic::new_signed( call, signer.into_account().into(), signature.into(), @@ -135,10 +140,12 @@ mod tests { use super::*; use relay_substrate_client::TransactionEra; + type SystemCall = runtime_types::frame_system::pallet::Call; + #[test] fn parse_transaction_works() { let unsigned = UnsignedTransaction { - call: runtime::Call::System(runtime::SystemCall::remark(b"Hello world!".to_vec())) + call: RuntimeCall::System(SystemCall::remark { remark: b"Hello world!".to_vec() }) .into(), nonce: 777, tip: 888, diff --git a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs b/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs deleted file mode 100644 index b9803955922..00000000000 --- a/relays/client-bridge-hub-wococo/src/runtime_wrapper.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common 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. - -// Parity Bridges Common 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 Parity Bridges Common. If not, see . - -//! Types that are specific to the BridgeHubWococo runtime. - -use codec::{Decode, Encode}; -use scale_info::TypeInfo; - -pub use bp_bridge_hub_wococo::SignedExtension; -pub use bp_header_chain::BridgeGrandpaCallOf; -pub use bp_parachains::BridgeParachainCall; -pub use bridge_runtime_common::messages::BridgeMessagesCallOf; -pub use relay_substrate_client::calls::{SystemCall, UtilityCall}; - -/// Unchecked BridgeHubWococo extrinsic. -pub type UncheckedExtrinsic = bp_bridge_hub_wococo::UncheckedExtrinsic; - -// The indirect pallet call used to sync `Rococo` GRANDPA finality to `BHWococo`. -pub type BridgeRococoGrandpaCall = BridgeGrandpaCallOf; -// The indirect pallet call used to sync `BridgeHubRococo` messages to `BridgeHubWococo`. -pub type BridgeRococoMessagesCall = BridgeMessagesCallOf; - -/// `BridgeHubWococo` Runtime `Call` enum. -/// -/// The enum represents a subset of possible `Call`s we can send to `BridgeHubWococo` chain. -/// Ideally this code would be auto-generated from metadata, because we want to -/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. -/// -/// All entries here (like pretty much in the entire file) must be kept in sync with -/// `BridgeHubWococo` `construct_runtime`, so that we maintain SCALE-compatibility. -#[allow(clippy::large_enum_variant)] -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub enum Call { - #[cfg(test)] - #[codec(index = 0)] - System(SystemCall), - /// Utility pallet. - #[codec(index = 40)] - Utility(UtilityCall), - - /// Rococo bridge pallet. - #[codec(index = 43)] - BridgeRococoGrandpa(BridgeRococoGrandpaCall), - /// Rococo parachain bridge pallet. - #[codec(index = 44)] - BridgeRococoParachain(BridgeParachainCall), - /// Rococo messages bridge pallet. - #[codec(index = 45)] - BridgeRococoMessages(BridgeRococoMessagesCall), -} - -impl From> for Call { - fn from(call: UtilityCall) -> Call { - Call::Utility(call) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use bp_runtime::BasicOperatingMode; - use sp_consensus_grandpa::AuthorityList; - use sp_core::hexdisplay::HexDisplay; - use sp_runtime::traits::Header; - use std::str::FromStr; - - pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; - pub type RelayBlockHasher = bp_polkadot_core::Hasher; - pub type RelayBlockHeader = sp_runtime::generic::Header; - - #[test] - fn encode_decode_calls() { - let header = RelayBlockHeader::new( - 75, - bp_polkadot_core::Hash::from_str( - "0xd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d", - ) - .expect("invalid value"), - bp_polkadot_core::Hash::from_str( - "0x92b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141b", - ) - .expect("invalid value"), - bp_polkadot_core::Hash::from_str( - "0xae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d", - ) - .expect("invalid value"), - Default::default(), - ); - let init_data = bp_header_chain::InitializationData { - header: Box::new(header), - authority_list: AuthorityList::default(), - set_id: 6, - operating_mode: BasicOperatingMode::Normal, - }; - let call = BridgeRococoGrandpaCall::initialize { init_data }; - let tx = Call::BridgeRococoGrandpa(call); - - // encode call as hex string - let hex_encoded_call = format!("0x{:?}", HexDisplay::from(&Encode::encode(&tx))); - assert_eq!(hex_encoded_call, "0x2b01ae4a25acf250d72ed02c149ecc7dd3c9ee976d41a2888fc551de8064521dc01d2d0192b965f0656a4e0e5fc0167da2d4b5ee72b3be2c1583c4c1e5236c8c12aa141bd2c0afaab32de0cb8f7f0d89217e37c5ea302c1ffb5a7a83e10d20f12c32874d0000060000000000000000"); - } -} diff --git a/relays/client-rialto-parachain/src/codegen_runtime.rs b/relays/client-rialto-parachain/src/codegen_runtime.rs index 1338d4938a3..eb19d98d7e4 100644 --- a/relays/client-rialto-parachain/src/codegen_runtime.rs +++ b/relays/client-rialto-parachain/src/codegen_runtime.rs @@ -28,18 +28,18 @@ pub mod api { use super::runtime_types; pub mod bounded_vec { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct BoundedVec<_0>(pub ::std::vec::Vec<_0>); } pub mod weak_bounded_vec { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct WeakBoundedVec<_0>(pub ::std::vec::Vec<_0>); } } pub mod bp_header_chain { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct StoredHeaderData<_0, _1> { pub number: _0, pub state_root: _1, @@ -47,37 +47,37 @@ pub mod api { } pub mod bp_messages { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct DeliveredMessages { pub begin: ::core::primitive::u64, pub end: ::core::primitive::u64, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct InboundLaneData<_0> { pub relayers: ::std::vec::Vec>, pub last_confirmed_nonce: ::core::primitive::u64, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct LaneId(pub [::core::primitive::u8; 4usize]); - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct MessageKey { pub lane_id: runtime_types::bp_messages::LaneId, pub nonce: ::core::primitive::u64, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum MessagesOperatingMode { #[codec(index = 0)] Basic(runtime_types::bp_runtime::BasicOperatingMode), #[codec(index = 1)] RejectingOutboundMessages, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct OutboundLaneData { pub oldest_unpruned_nonce: ::core::primitive::u64, pub latest_received_nonce: ::core::primitive::u64, pub latest_generated_nonce: ::core::primitive::u64, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum ReceivalResult<_0> { #[codec(index = 0)] Dispatched(runtime_types::bp_runtime::messages::MessageDispatchResult<_0>), @@ -88,7 +88,7 @@ pub mod api { #[codec(index = 3)] TooManyUnconfirmedMessages, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct ReceivedMessages<_0> { pub lane: runtime_types::bp_messages::LaneId, pub receive_results: ::std::vec::Vec<( @@ -96,7 +96,7 @@ pub mod api { runtime_types::bp_messages::ReceivalResult<_0>, )>, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct UnrewardedRelayer<_0> { pub relayer: _0, pub messages: runtime_types::bp_messages::DeliveredMessages, @@ -104,14 +104,22 @@ pub mod api { } pub mod bp_relayers { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + pub mod registration { + use super::runtime_types; + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] + pub struct Registration<_0, _1> { + pub valid_till: _0, + pub stake: _1, + } + } + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum RewardsAccountOwner { #[codec(index = 0)] ThisChain, #[codec(index = 1)] BridgedChain, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct RewardsAccountParams { pub lane_id: runtime_types::bp_messages::LaneId, pub bridged_chain_id: [::core::primitive::u8; 4usize], @@ -122,22 +130,22 @@ pub mod api { use super::runtime_types; pub mod messages { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct MessageDispatchResult<_0> { pub unspent_weight: ::sp_weights::Weight, pub dispatch_level_result: _0, } } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum BasicOperatingMode { #[codec(index = 0)] Normal, #[codec(index = 1)] Halted, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct HeaderId<_0, _1>(pub _1, pub _0); - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum OwnedBridgeModuleError { #[codec(index = 0)] Halted, @@ -147,7 +155,7 @@ pub mod api { use super::runtime_types; pub mod messages_xcm_extension { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum XcmBlobMessageDispatchResult { #[codec(index = 0)] InvalidPayload, @@ -162,7 +170,7 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Call { #[codec(index = 0)] service_overweight { @@ -170,14 +178,14 @@ pub mod api { weight_limit: ::sp_weights::Weight, }, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Error { #[codec(index = 0)] Unknown, #[codec(index = 1)] OverLimit, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Event { #[codec(index = 0)] InvalidFormat { message_id: [::core::primitive::u8; 32usize] }, @@ -209,11 +217,11 @@ pub mod api { MaxMessagesExhausted { message_id: [::core::primitive::u8; 32usize] }, } } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct ConfigData { pub max_individual: ::sp_weights::Weight, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct PageIndexData { pub begin_used: ::core::primitive::u32, pub end_used: ::core::primitive::u32, @@ -224,10 +232,10 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Call { # [codec (index = 0)] set_validation_data { data : runtime_types :: cumulus_primitives_parachain_inherent :: ParachainInherentData , } , # [codec (index = 1)] sudo_send_upward_message { message : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , # [codec (index = 2)] authorize_upgrade { code_hash : :: subxt :: utils :: H256 , check_version : :: core :: primitive :: bool , } , # [codec (index = 3)] enact_authorized_upgrade { code : :: std :: vec :: Vec < :: core :: primitive :: u8 > , } , } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Error { #[codec(index = 0)] OverlappingUpgrades, @@ -246,7 +254,7 @@ pub mod api { #[codec(index = 7)] Unauthorized, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Event { #[codec(index = 0)] ValidationFunctionStored, @@ -271,7 +279,7 @@ pub mod api { } pub mod relay_state_snapshot { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct MessagingStateSnapshot { pub dmq_mqc_head: ::subxt::utils::H256, pub relay_dispatch_queue_size: (::core::primitive::u32, ::core::primitive::u32), @@ -285,7 +293,7 @@ pub mod api { )>, } } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct CodeUpgradeAuthorization { pub code_hash: ::subxt::utils::H256, pub check_version: ::core::primitive::bool, @@ -295,11 +303,11 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Call {} - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Error {} - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Event { #[codec(index = 0)] InvalidFormat([::core::primitive::u8; 32usize]), @@ -317,7 +325,7 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Call { #[codec(index = 0)] service_overweight { @@ -341,7 +349,7 @@ pub mod api { #[codec(index = 8)] update_xcmp_max_individual_weight { new: ::sp_weights::Weight }, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Error { #[codec(index = 0)] FailedToSend, @@ -354,7 +362,7 @@ pub mod api { #[codec(index = 4)] WeightOverLimit, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Event { #[codec(index = 0)] Success { @@ -390,7 +398,7 @@ pub mod api { OverweightServiced { index: ::core::primitive::u64, used: ::sp_weights::Weight }, } } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct InboundChannelDetails { pub sender: runtime_types::polkadot_parachain::primitives::Id, pub state: runtime_types::cumulus_pallet_xcmp_queue::InboundState, @@ -399,14 +407,14 @@ pub mod api { runtime_types::polkadot_parachain::primitives::XcmpMessageFormat, )>, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum InboundState { #[codec(index = 0)] Ok, #[codec(index = 1)] Suspended, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct OutboundChannelDetails { pub recipient: runtime_types::polkadot_parachain::primitives::Id, pub state: runtime_types::cumulus_pallet_xcmp_queue::OutboundState, @@ -414,14 +422,14 @@ pub mod api { pub first_index: ::core::primitive::u16, pub last_index: ::core::primitive::u16, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum OutboundState { #[codec(index = 0)] Ok, #[codec(index = 1)] Suspended, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct QueueConfigData { pub suspend_threshold: ::core::primitive::u32, pub drop_threshold: ::core::primitive::u32, @@ -433,9 +441,9 @@ pub mod api { } pub mod cumulus_primitives_parachain_inherent { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct MessageQueueChain(pub ::subxt::utils::H256); - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct ParachainInherentData { pub validation_data: runtime_types::polkadot_primitives::v4::PersistedValidationData< @@ -460,7 +468,7 @@ pub mod api { } pub mod finality_grandpa { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct Commit<_0, _1, _2, _3> { pub target_hash: _0, pub target_number: _1, @@ -468,12 +476,12 @@ pub mod api { runtime_types::finality_grandpa::SignedPrecommit<_0, _1, _2, _3>, >, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct Precommit<_0, _1> { pub target_hash: _0, pub target_number: _1, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct SignedPrecommit<_0, _1, _2, _3> { pub precommit: runtime_types::finality_grandpa::Precommit<_0, _1>, pub signature: _2, @@ -484,7 +492,7 @@ pub mod api { use super::runtime_types; pub mod dispatch { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum DispatchClass { #[codec(index = 0)] Normal, @@ -493,20 +501,20 @@ pub mod api { #[codec(index = 2)] Mandatory, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct DispatchInfo { pub weight: ::sp_weights::Weight, pub class: runtime_types::frame_support::dispatch::DispatchClass, pub pays_fee: runtime_types::frame_support::dispatch::Pays, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Pays { #[codec(index = 0)] Yes, #[codec(index = 1)] No, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct PerDispatchClass<_0> { pub normal: _0, pub operational: _0, @@ -519,7 +527,9 @@ pub mod api { use super::runtime_types; pub mod misc { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive( + :: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq, + )] pub enum BalanceStatus { #[codec(index = 0)] Free, @@ -536,49 +546,49 @@ pub mod api { use super::runtime_types; pub mod check_genesis { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct CheckGenesis; } pub mod check_mortality { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct CheckMortality(pub ::sp_runtime::generic::Era); } pub mod check_non_zero_sender { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct CheckNonZeroSender; } pub mod check_nonce { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct CheckNonce(#[codec(compact)] pub ::core::primitive::u32); } pub mod check_spec_version { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct CheckSpecVersion; } pub mod check_tx_version { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct CheckTxVersion; } pub mod check_weight { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct CheckWeight; } } pub mod limits { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct BlockLength { pub max: runtime_types::frame_support::dispatch::PerDispatchClass< ::core::primitive::u32, >, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct BlockWeights { pub base_block: ::sp_weights::Weight, pub max_block: ::sp_weights::Weight, @@ -586,7 +596,7 @@ pub mod api { runtime_types::frame_system::limits::WeightsPerClass, >, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct WeightsPerClass { pub base_extrinsic: ::sp_weights::Weight, pub max_extrinsic: ::core::option::Option<::sp_weights::Weight>, @@ -596,7 +606,7 @@ pub mod api { } pub mod pallet { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Call { #[codec(index = 0)] remark { remark: ::std::vec::Vec<::core::primitive::u8> }, @@ -623,7 +633,7 @@ pub mod api { #[codec(index = 7)] remark_with_event { remark: ::std::vec::Vec<::core::primitive::u8> }, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Error { #[codec(index = 0)] InvalidSpecName, @@ -638,7 +648,7 @@ pub mod api { #[codec(index = 5)] CallFiltered, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Event { #[codec(index = 0)] ExtrinsicSuccess { @@ -659,7 +669,7 @@ pub mod api { Remarked { sender: ::sp_core::crypto::AccountId32, hash: ::subxt::utils::H256 }, } } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct AccountInfo<_0, _1> { pub nonce: _0, pub consumers: _0, @@ -667,19 +677,19 @@ pub mod api { pub sufficients: _0, pub data: _1, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct EventRecord<_0, _1> { pub phase: runtime_types::frame_system::Phase, pub event: _0, pub topics: ::std::vec::Vec<_1>, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct LastRuntimeUpgradeInfo { #[codec(compact)] pub spec_version: ::core::primitive::u32, pub spec_name: ::std::string::String, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Phase { #[codec(index = 0)] ApplyExtrinsic(::core::primitive::u32), @@ -693,7 +703,7 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Call { #[codec(index = 0)] transfer_allow_death { @@ -747,7 +757,7 @@ pub mod api { new_free: ::core::primitive::u128, }, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Error { #[codec(index = 0)] VestingBalance, @@ -770,7 +780,7 @@ pub mod api { #[codec(index = 9)] TooManyFreezes, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Event { #[codec(index = 0)] Endowed { @@ -851,14 +861,14 @@ pub mod api { } pub mod types { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct AccountData<_0> { pub free: _0, pub reserved: _0, pub frozen: _0, pub flags: runtime_types::pallet_balances::types::ExtraFlags, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct BalanceLock<_0> { pub id: [::core::primitive::u8; 8usize], pub amount: _0, @@ -870,14 +880,15 @@ pub mod api { :: subxt :: ext :: codec :: CompactAs, Clone, Debug, + PartialEq, )] pub struct ExtraFlags(pub ::core::primitive::u128); - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct IdAmount<_0, _1> { pub id: _0, pub amount: _1, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Reasons { #[codec(index = 0)] Fee, @@ -886,7 +897,7 @@ pub mod api { #[codec(index = 2)] All, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct ReserveData<_0, _1> { pub id: _0, pub amount: _1, @@ -897,7 +908,7 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Call { #[codec(index = 0)] submit_finality_proof { @@ -930,7 +941,7 @@ pub mod api { operating_mode: runtime_types::bp_runtime::BasicOperatingMode, }, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Error { #[codec(index = 0)] InvalidJustification, @@ -949,7 +960,7 @@ pub mod api { #[codec(index = 7)] BridgeModule(runtime_types::bp_runtime::OwnedBridgeModuleError), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Event { #[codec(index = 0)] UpdatedBestFinalizedHeader { @@ -960,7 +971,7 @@ pub mod api { } pub mod storage_types { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct StoredAuthoritySet { pub authorities: runtime_types::bounded_collections::bounded_vec::BoundedVec<( runtime_types::sp_consensus_grandpa::app::Public, @@ -974,10 +985,10 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Call { # [codec (index = 0)] set_owner { new_owner : :: core :: option :: Option < :: sp_core :: crypto :: AccountId32 > , } , # [codec (index = 1)] set_operating_mode { operating_mode : runtime_types :: bp_messages :: MessagesOperatingMode , } , # [codec (index = 2)] receive_messages_proof { relayer_id_at_bridged_chain : :: sp_core :: crypto :: AccountId32 , proof : :: bridge_runtime_common :: messages :: target :: FromBridgedChainMessagesProof < :: bp_millau :: MillauHash > , messages_count : :: core :: primitive :: u32 , dispatch_weight : :: sp_weights :: Weight , } , # [codec (index = 3)] receive_messages_delivery_proof { proof : :: bridge_runtime_common :: messages :: source :: FromBridgedChainMessagesDeliveryProof < :: bp_millau :: MillauHash > , relayers_state : :: bp_messages :: UnrewardedRelayersState , } , } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Error { #[codec(index = 0)] NotOperatingNormally, @@ -1010,7 +1021,7 @@ pub mod api { #[codec(index = 14)] BridgeModule(runtime_types::bp_runtime::OwnedBridgeModuleError), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Event { # [codec (index = 0)] MessageAccepted { lane_id : runtime_types :: bp_messages :: LaneId , nonce : :: core :: primitive :: u64 , } , # [codec (index = 1)] MessagesReceived (:: std :: vec :: Vec < runtime_types :: bp_messages :: ReceivedMessages < runtime_types :: bridge_runtime_common :: messages_xcm_extension :: XcmBlobMessageDispatchResult > > ,) , # [codec (index = 2)] MessagesDelivered { lane_id : runtime_types :: bp_messages :: LaneId , messages : runtime_types :: bp_messages :: DeliveredMessages , } , } } @@ -1019,21 +1030,37 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Call { #[codec(index = 0)] claim_rewards { rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, }, + #[codec(index = 1)] + register { valid_till: ::core::primitive::u32 }, + #[codec(index = 2)] + deregister, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Error { #[codec(index = 0)] NoRewardForRelayer, #[codec(index = 1)] FailedToPayReward, + #[codec(index = 2)] + InvalidRegistrationLease, + #[codec(index = 3)] + CannotReduceRegistrationLease, + #[codec(index = 4)] + FailedToReserve, + #[codec(index = 5)] + FailedToUnreserve, + #[codec(index = 6)] + NotRegistered, + #[codec(index = 7)] + RegistrationIsStillActive, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Event { #[codec(index = 0)] RewardPaid { @@ -1041,6 +1068,24 @@ pub mod api { rewards_account_params: runtime_types::bp_relayers::RewardsAccountParams, reward: ::core::primitive::u128, }, + #[codec(index = 1)] + RegistrationUpdated { + relayer: ::sp_core::crypto::AccountId32, + registration: runtime_types::bp_relayers::registration::Registration< + ::core::primitive::u32, + ::core::primitive::u128, + >, + }, + #[codec(index = 2)] + Deregistered { relayer: ::sp_core::crypto::AccountId32 }, + #[codec(index = 3)] + SlashedAndDeregistered { + relayer: ::sp_core::crypto::AccountId32, + registration: runtime_types::bp_relayers::registration::Registration< + ::core::primitive::u32, + ::core::primitive::u128, + >, + }, } } } @@ -1048,7 +1093,7 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Call { #[codec(index = 0)] sudo { @@ -1072,12 +1117,12 @@ pub mod api { ::std::boxed::Box, }, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Error { #[codec(index = 0)] RequireSudo, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Event { #[codec(index = 0)] Sudid { @@ -1100,7 +1145,7 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Call { #[codec(index = 0)] set { @@ -1114,7 +1159,7 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Event { #[codec(index = 0)] TransactionFeePaid { @@ -1124,9 +1169,9 @@ pub mod api { }, } } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct ChargeTransactionPayment(#[codec(compact)] pub ::core::primitive::u128); - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Releases { #[codec(index = 0)] V1Ancient, @@ -1138,7 +1183,7 @@ pub mod api { use super::runtime_types; pub mod pallet { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Call { #[codec(index = 0)] send { @@ -1199,7 +1244,7 @@ pub mod api { weight_limit: runtime_types::xcm::v3::WeightLimit, }, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Error { #[codec(index = 0)] Unreachable, @@ -1242,7 +1287,7 @@ pub mod api { #[codec(index = 19)] InUse, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Event { #[codec(index = 0)] Attempted(runtime_types::xcm::v3::traits::Outcome), @@ -1369,17 +1414,17 @@ pub mod api { } pub mod polkadot_core_primitives { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct InboundDownwardMessage<_0> { pub sent_at: _0, pub msg: ::std::vec::Vec<::core::primitive::u8>, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct InboundHrmpMessage<_0> { pub sent_at: _0, pub data: ::std::vec::Vec<::core::primitive::u8>, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct OutboundHrmpMessage<_0> { pub recipient: _0, pub data: ::std::vec::Vec<::core::primitive::u8>, @@ -1389,7 +1434,7 @@ pub mod api { use super::runtime_types; pub mod primitives { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct HeadData(pub ::std::vec::Vec<::core::primitive::u8>); #[derive( :: codec :: Decode, @@ -1397,9 +1442,10 @@ pub mod api { :: subxt :: ext :: codec :: CompactAs, Clone, Debug, + PartialEq, )] pub struct Id(pub ::core::primitive::u32); - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum XcmpMessageFormat { #[codec(index = 0)] ConcatenatedVersionedXcm, @@ -1414,7 +1460,7 @@ pub mod api { use super::runtime_types; pub mod v4 { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct AbridgedHostConfiguration { pub max_code_size: ::core::primitive::u32, pub max_head_data_size: ::core::primitive::u32, @@ -1426,7 +1472,7 @@ pub mod api { pub validation_upgrade_cooldown: ::core::primitive::u32, pub validation_upgrade_delay: ::core::primitive::u32, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct AbridgedHrmpChannel { pub max_capacity: ::core::primitive::u32, pub max_total_size: ::core::primitive::u32, @@ -1435,14 +1481,14 @@ pub mod api { pub total_size: ::core::primitive::u32, pub mqc_head: ::core::option::Option<::subxt::utils::H256>, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct PersistedValidationData<_0, _1> { pub parent_head: runtime_types::polkadot_parachain::primitives::HeadData, pub relay_parent_number: _1, pub relay_parent_storage_root: _0, pub max_pov_size: _1, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum UpgradeRestriction { #[codec(index = 0)] Present, @@ -1451,13 +1497,13 @@ pub mod api { } pub mod rialto_parachain_runtime { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct BridgeRejectObsoleteHeadersAndMessages; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct DummyBridgeRefundMillauMessages; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct Runtime; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum RuntimeCall { #[codec(index = 0)] System(runtime_types::frame_system::pallet::Call), @@ -1484,7 +1530,7 @@ pub mod api { #[codec(index = 56)] BridgeMillauMessages(runtime_types::pallet_bridge_messages::pallet::Call), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum RuntimeEvent { #[codec(index = 0)] System(runtime_types::frame_system::pallet::Event), @@ -1522,10 +1568,11 @@ pub mod api { :: subxt :: ext :: codec :: CompactAs, Clone, Debug, + PartialEq, )] pub struct FixedU128(pub ::core::primitive::u128); } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum ArithmeticError { #[codec(index = 0)] Underflow, @@ -1539,9 +1586,9 @@ pub mod api { use super::runtime_types; pub mod app { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct Public(pub runtime_types::sp_core::ed25519::Public); - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct Signature(pub runtime_types::sp_core::ed25519::Signature); } } @@ -1549,19 +1596,19 @@ pub mod api { use super::runtime_types; pub mod ecdsa { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct Signature(pub [::core::primitive::u8; 65usize]); } pub mod ed25519 { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct Public(pub [::core::primitive::u8; 32usize]); - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct Signature(pub [::core::primitive::u8; 64usize]); } pub mod sr25519 { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct Signature(pub [::core::primitive::u8; 64usize]); } } @@ -1571,7 +1618,7 @@ pub mod api { use super::runtime_types; pub mod digest { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum DigestItem { #[codec(index = 6)] PreRuntime( @@ -1596,14 +1643,14 @@ pub mod api { } pub mod unchecked_extrinsic { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct UncheckedExtrinsic<_0, _1, _2, _3>( pub ::std::vec::Vec<::core::primitive::u8>, #[codec(skip)] pub ::core::marker::PhantomData<(_1, _0, _2, _3)>, ); } } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum DispatchError { #[codec(index = 0)] Other, @@ -1632,12 +1679,12 @@ pub mod api { #[codec(index = 12)] Unavailable, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct ModuleError { pub index: ::core::primitive::u8, pub error: [::core::primitive::u8; 4usize], } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum MultiSignature { #[codec(index = 0)] Ed25519(runtime_types::sp_core::ed25519::Signature), @@ -1646,7 +1693,7 @@ pub mod api { #[codec(index = 2)] Ecdsa(runtime_types::sp_core::ecdsa::Signature), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum TokenError { #[codec(index = 0)] FundsUnavailable, @@ -1667,7 +1714,7 @@ pub mod api { #[codec(index = 8)] NotExpendable, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum TransactionalError { #[codec(index = 0)] LimitReached, @@ -1679,7 +1726,7 @@ pub mod api { use super::runtime_types; pub mod storage_proof { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct StorageProof { pub trie_nodes: ::std::vec::Vec<::std::vec::Vec<::core::primitive::u8>>, } @@ -1687,7 +1734,7 @@ pub mod api { } pub mod sp_version { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct RuntimeVersion { pub spec_name: ::std::string::String, pub impl_name: ::std::string::String, @@ -1702,7 +1749,7 @@ pub mod api { } pub mod sp_weights { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct RuntimeDbWeight { pub read: ::core::primitive::u64, pub write: ::core::primitive::u64, @@ -1712,7 +1759,7 @@ pub mod api { use super::runtime_types; pub mod double_encoded { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct DoubleEncoded { pub encoded: ::std::vec::Vec<::core::primitive::u8>, } @@ -1721,7 +1768,7 @@ pub mod api { use super::runtime_types; pub mod junction { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Junction { #[codec(index = 0)] Parachain(#[codec(compact)] ::core::primitive::u32), @@ -1762,14 +1809,14 @@ pub mod api { } pub mod multiasset { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum AssetId { #[codec(index = 0)] Concrete(runtime_types::xcm::v2::multilocation::MultiLocation), #[codec(index = 1)] Abstract(::std::vec::Vec<::core::primitive::u8>), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum AssetInstance { #[codec(index = 0)] Undefined, @@ -1786,37 +1833,37 @@ pub mod api { #[codec(index = 6)] Blob(::std::vec::Vec<::core::primitive::u8>), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Fungibility { #[codec(index = 0)] Fungible(#[codec(compact)] ::core::primitive::u128), #[codec(index = 1)] NonFungible(runtime_types::xcm::v2::multiasset::AssetInstance), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct MultiAsset { pub id: runtime_types::xcm::v2::multiasset::AssetId, pub fun: runtime_types::xcm::v2::multiasset::Fungibility, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum MultiAssetFilter { #[codec(index = 0)] Definite(runtime_types::xcm::v2::multiasset::MultiAssets), #[codec(index = 1)] Wild(runtime_types::xcm::v2::multiasset::WildMultiAsset), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct MultiAssets( pub ::std::vec::Vec, ); - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum WildFungibility { #[codec(index = 0)] Fungible, #[codec(index = 1)] NonFungible, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum WildMultiAsset { #[codec(index = 0)] All, @@ -1829,7 +1876,7 @@ pub mod api { } pub mod multilocation { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Junctions { #[codec(index = 0)] Here, @@ -1892,7 +1939,7 @@ pub mod api { runtime_types::xcm::v2::junction::Junction, ), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct MultiLocation { pub parents: ::core::primitive::u8, pub interior: runtime_types::xcm::v2::multilocation::Junctions, @@ -1900,7 +1947,7 @@ pub mod api { } pub mod traits { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Error { #[codec(index = 0)] Overflow, @@ -1956,7 +2003,7 @@ pub mod api { WeightNotComputable, } } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum BodyId { #[codec(index = 0)] Unit, @@ -1983,7 +2030,7 @@ pub mod api { #[codec(index = 9)] Treasury, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum BodyPart { #[codec(index = 0)] Voice, @@ -2014,7 +2061,7 @@ pub mod api { denom: ::core::primitive::u32, }, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Instruction { #[codec(index = 0)] WithdrawAsset(runtime_types::xcm::v2::multiasset::MultiAssets), @@ -2154,7 +2201,7 @@ pub mod api { #[codec(index = 27)] UnsubscribeVersion, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum NetworkId { #[codec(index = 0)] Any, @@ -2169,7 +2216,7 @@ pub mod api { #[codec(index = 3)] Kusama, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum OriginKind { #[codec(index = 0)] Native, @@ -2180,7 +2227,7 @@ pub mod api { #[codec(index = 3)] Xcm, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Response { #[codec(index = 0)] Null, @@ -2196,21 +2243,21 @@ pub mod api { #[codec(index = 3)] Version(::core::primitive::u32), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum WeightLimit { #[codec(index = 0)] Unlimited, #[codec(index = 1)] Limited(#[codec(compact)] ::core::primitive::u64), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct Xcm(pub ::std::vec::Vec); } pub mod v3 { use super::runtime_types; pub mod junction { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum BodyId { #[codec(index = 0)] Unit, @@ -2233,7 +2280,7 @@ pub mod api { #[codec(index = 9)] Treasury, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum BodyPart { #[codec(index = 0)] Voice, @@ -2264,7 +2311,7 @@ pub mod api { denom: ::core::primitive::u32, }, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Junction { #[codec(index = 0)] Parachain(#[codec(compact)] ::core::primitive::u32), @@ -2306,7 +2353,7 @@ pub mod api { #[codec(index = 9)] GlobalConsensus(runtime_types::xcm::v3::junction::NetworkId), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum NetworkId { #[codec(index = 0)] ByGenesis([::core::primitive::u8; 32usize]), @@ -2338,7 +2385,7 @@ pub mod api { } pub mod junctions { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Junctions { #[codec(index = 0)] Here, @@ -2404,14 +2451,14 @@ pub mod api { } pub mod multiasset { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum AssetId { #[codec(index = 0)] Concrete(runtime_types::xcm::v3::multilocation::MultiLocation), #[codec(index = 1)] Abstract([::core::primitive::u8; 32usize]), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum AssetInstance { #[codec(index = 0)] Undefined, @@ -2426,37 +2473,37 @@ pub mod api { #[codec(index = 5)] Array32([::core::primitive::u8; 32usize]), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Fungibility { #[codec(index = 0)] Fungible(#[codec(compact)] ::core::primitive::u128), #[codec(index = 1)] NonFungible(runtime_types::xcm::v3::multiasset::AssetInstance), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct MultiAsset { pub id: runtime_types::xcm::v3::multiasset::AssetId, pub fun: runtime_types::xcm::v3::multiasset::Fungibility, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum MultiAssetFilter { #[codec(index = 0)] Definite(runtime_types::xcm::v3::multiasset::MultiAssets), #[codec(index = 1)] Wild(runtime_types::xcm::v3::multiasset::WildMultiAsset), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct MultiAssets( pub ::std::vec::Vec, ); - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum WildFungibility { #[codec(index = 0)] Fungible, #[codec(index = 1)] NonFungible, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum WildMultiAsset { #[codec(index = 0)] All, @@ -2478,7 +2525,7 @@ pub mod api { } pub mod multilocation { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct MultiLocation { pub parents: ::core::primitive::u8, pub interior: runtime_types::xcm::v3::junctions::Junctions, @@ -2486,7 +2533,7 @@ pub mod api { } pub mod traits { use super::runtime_types; - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Error { #[codec(index = 0)] Overflow, @@ -2569,7 +2616,7 @@ pub mod api { #[codec(index = 39)] ExceedsStackLimit, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Outcome { #[codec(index = 0)] Complete(::sp_weights::Weight), @@ -2579,7 +2626,7 @@ pub mod api { Error(runtime_types::xcm::v3::traits::Error), } } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Instruction { #[codec(index = 0)] WithdrawAsset(runtime_types::xcm::v3::multiasset::MultiAssets), @@ -2788,7 +2835,7 @@ pub mod api { >, }, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum MaybeErrorCode { #[codec(index = 0)] Success, @@ -2805,7 +2852,7 @@ pub mod api { >, ), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct PalletInfo { #[codec(compact)] pub index: ::core::primitive::u32, @@ -2822,14 +2869,14 @@ pub mod api { #[codec(compact)] pub patch: ::core::primitive::u32, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct QueryResponseInfo { pub destination: runtime_types::xcm::v3::multilocation::MultiLocation, #[codec(compact)] pub query_id: ::core::primitive::u64, pub max_weight: ::sp_weights::Weight, } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum Response { #[codec(index = 0)] Null, @@ -2853,31 +2900,31 @@ pub mod api { #[codec(index = 5)] DispatchResult(runtime_types::xcm::v3::MaybeErrorCode), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum WeightLimit { #[codec(index = 0)] Unlimited, #[codec(index = 1)] Limited(::sp_weights::Weight), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub struct Xcm(pub ::std::vec::Vec); } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum VersionedMultiAssets { #[codec(index = 1)] V2(runtime_types::xcm::v2::multiasset::MultiAssets), #[codec(index = 3)] V3(runtime_types::xcm::v3::multiasset::MultiAssets), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum VersionedMultiLocation { #[codec(index = 1)] V2(runtime_types::xcm::v2::multilocation::MultiLocation), #[codec(index = 3)] V3(runtime_types::xcm::v3::multilocation::MultiLocation), } - #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug)] + #[derive(:: codec :: Decode, :: codec :: Encode, Clone, Debug, PartialEq)] pub enum VersionedXcm { #[codec(index = 2)] V2(runtime_types::xcm::v2::Xcm), diff --git a/relays/client-rialto-parachain/src/lib.rs b/relays/client-rialto-parachain/src/lib.rs index bb7be12979f..e8d31ee83c3 100644 --- a/relays/client-rialto-parachain/src/lib.rs +++ b/relays/client-rialto-parachain/src/lib.rs @@ -56,7 +56,7 @@ impl Chain for RialtoParachain { const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); type SignedBlock = bp_polkadot_core::SignedBlock; - type Call = runtime_types::rialto_parachain_runtime::RuntimeCall; + type Call = RuntimeCall; } impl ChainWithBalances for RialtoParachain { diff --git a/scripts/regenerate_runtimes.sh b/scripts/regenerate_runtimes.sh new file mode 100755 index 00000000000..15dd70b52c8 --- /dev/null +++ b/scripts/regenerate_runtimes.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +cd tools/runtime-codegen +cargo run --bin runtime-codegen -- --from-node-url "wss://rococo-bridge-hub-rpc.polkadot.io:443" > ../../relays/client-bridge-hub-rococo/src/codegen_runtime.rs +cargo run --bin runtime-codegen -- --from-node-url "http://localhost:20433" > ../../relays/client-rialto-parachain/src/codegen_runtime.rs +cd - +cargo fmt --all \ No newline at end of file diff --git a/scripts/verify-pallets-build.sh b/scripts/verify-pallets-build.sh index 4b6fadb79e4..dfee5341673 100755 --- a/scripts/verify-pallets-build.sh +++ b/scripts/verify-pallets-build.sh @@ -81,6 +81,7 @@ rm -rf $BRIDGES_FOLDER/scripts/build-containers.sh rm -rf $BRIDGES_FOLDER/scripts/ci-cache.sh rm -rf $BRIDGES_FOLDER/scripts/dump-logs.sh rm -rf $BRIDGES_FOLDER/scripts/license_header +rm -rf $BRIDGES_FOLDER/scripts/regenerate_runtimes.sh rm -rf $BRIDGES_FOLDER/scripts/send-message-from-millau-rialto.sh rm -rf $BRIDGES_FOLDER/scripts/send-message-from-rialto-millau.sh rm -rf $BRIDGES_FOLDER/scripts/update-weights.sh diff --git a/tools/runtime-codegen/README.md b/tools/runtime-codegen/README.md index 155e90b369c..52ff3818461 100644 --- a/tools/runtime-codegen/README.md +++ b/tools/runtime-codegen/README.md @@ -6,6 +6,10 @@ Example commands: cargo run --bin runtime-codegen -- --from-node-url "http://localhost:20433" > /tmp/rialto_codegen.rs ``` +``` +cargo run --bin runtime-codegen -- --from-node-url "wss://rococo-bridge-hub-rpc.polkadot.io:443" > /tmp/rococo_codegen.rs +``` + ``` cargo run --bin runtime-codegen -- --from-wasm ~/workplace/bridge-hub-rococo_runtime-v9360.compact.compressed.wasm > /tmp/rococo_bridge_hub_codegen.rs ``` \ No newline at end of file diff --git a/tools/runtime-codegen/src/main.rs b/tools/runtime-codegen/src/main.rs index a91a733da0b..4db1b3a1740 100644 --- a/tools/runtime-codegen/src/main.rs +++ b/tools/runtime-codegen/src/main.rs @@ -119,6 +119,7 @@ fn main() -> color_eyre::Result<()> { syn::parse_quote!(::codec::Decode), syn::parse_quote!(Clone), syn::parse_quote!(Debug), + syn::parse_quote!(PartialEq), ], vec![], ); @@ -128,27 +129,30 @@ fn main() -> color_eyre::Result<()> { .extend( vec![ TypeSubstitute::simple("sp_core::crypto::AccountId32"), - TypeSubstitute::custom("bp_millau::millau_hash::MillauHash", "::bp_millau::MillauHash"), - TypeSubstitute::simple("bp_millau::BlakeTwoAndKeccak256"), - TypeSubstitute::custom( - "sp_runtime::generic::digest::Digest", - "::sp_runtime::generic::Digest", - ), + TypeSubstitute::custom("sp_weights::weight_v2::Weight", "::sp_weights::Weight"), TypeSubstitute::custom("sp_runtime::generic::era::Era", "::sp_runtime::generic::Era"), TypeSubstitute::custom( "sp_runtime::generic::header::Header", "::sp_runtime::generic::Header", ), + TypeSubstitute::simple("sp_runtime::traits::BlakeTwo256"), TypeSubstitute::simple("bp_header_chain::justification::GrandpaJustification"), TypeSubstitute::simple("bp_header_chain::InitializationData"), + TypeSubstitute::simple("bp_polkadot_core::parachains::ParaId"), + TypeSubstitute::simple("bp_polkadot_core::parachains::ParaHeadsProof"), TypeSubstitute::simple( "bridge_runtime_common::messages::target::FromBridgedChainMessagesProof", ), - TypeSubstitute::custom("sp_weights::weight_v2::Weight", "::sp_weights::Weight"), TypeSubstitute::simple( "bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof", ), TypeSubstitute::simple("bp_messages::UnrewardedRelayersState"), + TypeSubstitute::custom("bp_millau::millau_hash::MillauHash", "::bp_millau::MillauHash"), + TypeSubstitute::simple("bp_millau::BlakeTwoAndKeccak256"), + TypeSubstitute::custom( + "sp_runtime::generic::digest::Digest", + "::sp_runtime::generic::Digest", + ), ] .drain(..) .map(|substitute| (substitute.subxt_type, substitute.substitute.try_into().unwrap())), From a93f99511ed58b10eae936feba4ddcd5a1e8c762 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 3 May 2023 16:00:41 +0200 Subject: [PATCH 258/263] Fix --- .../bridge-hub-rococo/src/bridge_hub_rococo_config.rs | 7 ++++++- .../bridge-hub-rococo/src/bridge_hub_wococo_config.rs | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs index aaffbcf7a90..bfa6f98ebae 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_rococo_config.rs @@ -165,10 +165,15 @@ mod tests { #[test] fn ensure_bridge_hub_rococo_message_lane_weights_are_correct() { - check_message_lane_weights::( + check_message_lane_weights::< + bp_bridge_hub_rococo::BridgeHubRococo, + Runtime, + WithBridgeHubWococoMessagesInstance, + >( bp_bridge_hub_wococo::EXTRA_STORAGE_PROOF_SIZE, bp_bridge_hub_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, bp_bridge_hub_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + true, ); } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs index 8987b8eeb7e..2c9ec3c82bc 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_hub_wococo_config.rs @@ -165,10 +165,15 @@ mod tests { #[test] fn ensure_bridge_hub_wococo_message_lane_weights_are_correct() { - check_message_lane_weights::( + check_message_lane_weights::< + bp_bridge_hub_wococo::BridgeHubWococo, + Runtime, + WithBridgeHubRococoMessagesInstance, + >( bp_bridge_hub_rococo::EXTRA_STORAGE_PROOF_SIZE, bp_bridge_hub_wococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, bp_bridge_hub_wococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + true, ); } From 1a986c580055d3303ace5726790d70b5dd39956b Mon Sep 17 00:00:00 2001 From: command-bot <> Date: Wed, 3 May 2023 14:02:21 +0000 Subject: [PATCH 259/263] ".git/.scripts/commands/fmt/fmt.sh" --- client/network/src/lib.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index e226170d7c5..0c15ab3add5 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -89,13 +89,14 @@ impl BlockAnnounceData { /// /// This will not check the signature, for this you should use [`BlockAnnounceData::check_signature`]. fn validate(&self, encoded_header: Vec) -> Result<(), Validation> { - let candidate_hash = - if let CompactStatement::Seconded(h) = self.statement.unchecked_payload() { - h - } else { - tracing::debug!(target: LOG_TARGET, "`CompactStatement` isn't the candidate variant!",); - return Err(Validation::Failure { disconnect: true }) - }; + let candidate_hash = if let CompactStatement::Seconded(h) = + self.statement.unchecked_payload() + { + h + } else { + tracing::debug!(target: LOG_TARGET, "`CompactStatement` isn't the candidate variant!",); + return Err(Validation::Failure { disconnect: true }) + }; if *candidate_hash != self.receipt.hash() { tracing::debug!( @@ -333,9 +334,9 @@ where let relay_chain_is_syncing = relay_chain_interface .is_major_syncing() .await - .map_err( - |e| tracing::error!(target: LOG_TARGET, "Unable to determine sync status. {}", e), - ) + .map_err(|e| { + tracing::error!(target: LOG_TARGET, "Unable to determine sync status. {}", e) + }) .unwrap_or(false); if relay_chain_is_syncing { From e6b02ea17785109348d408283319ca3ad7f3613a Mon Sep 17 00:00:00 2001 From: command-bot <> Date: Wed, 3 May 2023 16:34:31 +0000 Subject: [PATCH 260/263] ".git/.scripts/commands/bench/bench.sh" pallet bridge-hub-rococo bridge-hubs pallet_bridge_grandpa --- ...et_bridge_grandpa_bridge_rococo_grandpa.rs | 47 +++++++++---------- ...et_bridge_grandpa_bridge_wococo_grandpa.rs | 43 +++++++++-------- 2 files changed, 43 insertions(+), 47 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_rococo_grandpa.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_rococo_grandpa.rs index 88e9c24bc58..68e540adb7b 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_rococo_grandpa.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_rococo_grandpa.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -17,41 +17,40 @@ //! Autogenerated weights for `pallet_bridge_grandpa` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `10`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-05-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --steps=10 -// --repeat=1 +// --steps=50 +// --repeat=20 // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=./bench.json -// --header=./file_header.txt -// --chain=bridge-hub-rococo-dev +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_bridge_grandpa -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions for `pallet_bridge_grandpa`. pub struct WeightInfo(PhantomData); impl pallet_bridge_grandpa::WeightInfo for WeightInfo { /// Storage: BridgeRococoGrandpa PalletOperatingMode (r:1 w:0) /// Proof: BridgeRococoGrandpa PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: BridgeRococoGrandpa RequestCount (r:1 w:1) - /// Proof: BridgeRococoGrandpa RequestCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: BridgeRococoGrandpa BestFinalized (r:1 w:1) /// Proof: BridgeRococoGrandpa BestFinalized (max_values: Some(1), max_size: Some(36), added: 531, mode: MaxEncodedLen) /// Storage: BridgeRococoGrandpa CurrentAuthoritySet (r:1 w:0) @@ -66,18 +65,16 @@ impl pallet_bridge_grandpa::WeightInfo for WeightInfo Weight { + fn submit_finality_proof(p: u32, _v: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `295 + p * (60 ±0)` - // Estimated: `60231` - // Minimum execution time: 616_404_000 picoseconds. - Weight::from_parts(616_404_000, 0) - .saturating_add(Weight::from_parts(0, 60231)) - // Standard Error: 10_615_697 - .saturating_add(Weight::from_parts(107_372_374, 0).saturating_mul(p.into())) - // Standard Error: 81_776_733 - .saturating_add(Weight::from_parts(97_333_232, 0).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(6)) + // Measured: `231 + p * (60 ±0)` + // Estimated: `51735` + // Minimum execution time: 224_590_000 picoseconds. + Weight::from_parts(225_581_000, 0) + .saturating_add(Weight::from_parts(0, 51735)) + // Standard Error: 5_887 + .saturating_add(Weight::from_parts(47_424_657, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) } } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_wococo_grandpa.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_wococo_grandpa.rs index 68265a8c489..1c3bfb2feef 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_wococo_grandpa.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa_bridge_wococo_grandpa.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -17,41 +17,40 @@ //! Autogenerated weights for `pallet_bridge_grandpa` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `10`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-05-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --steps=10 -// --repeat=1 +// --steps=50 +// --repeat=20 // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=./bench.json -// --header=./file_header.txt -// --chain=bridge-hub-rococo-dev +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_bridge_grandpa -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions for `pallet_bridge_grandpa`. pub struct WeightInfo(PhantomData); impl pallet_bridge_grandpa::WeightInfo for WeightInfo { /// Storage: BridgeWococoGrandpa PalletOperatingMode (r:1 w:0) /// Proof: BridgeWococoGrandpa PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: BridgeWococoGrandpa RequestCount (r:1 w:1) - /// Proof: BridgeWococoGrandpa RequestCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: BridgeWococoGrandpa BestFinalized (r:1 w:1) /// Proof: BridgeWococoGrandpa BestFinalized (max_values: Some(1), max_size: Some(36), added: 531, mode: MaxEncodedLen) /// Storage: BridgeWococoGrandpa CurrentAuthoritySet (r:1 w:0) @@ -68,14 +67,14 @@ impl pallet_bridge_grandpa::WeightInfo for WeightInfo Weight { // Proof Size summary in bytes: - // Measured: `332 + p * (60 ±0)` - // Estimated: `60231` - // Minimum execution time: 672_054_000 picoseconds. - Weight::from_parts(47_309_240_121, 0) - .saturating_add(Weight::from_parts(0, 60231)) - // Standard Error: 16_649_480 - .saturating_add(Weight::from_parts(98_202_871, 0).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(6)) - .saturating_add(T::DbWeight::get().writes(6)) + // Measured: `268 + p * (60 ±0)` + // Estimated: `51735` + // Minimum execution time: 224_665_000 picoseconds. + Weight::from_parts(225_737_000, 0) + .saturating_add(Weight::from_parts(0, 51735)) + // Standard Error: 5_325 + .saturating_add(Weight::from_parts(47_412_944, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) } } From 22d1f8ed4d850b6d2f9b716c4207b2f5255ff287 Mon Sep 17 00:00:00 2001 From: command-bot <> Date: Wed, 3 May 2023 17:36:09 +0000 Subject: [PATCH 261/263] ".git/.scripts/commands/bench/bench.sh" pallet bridge-hub-rococo bridge-hubs pallet_bridge_parachains --- ...untime_bridge_parachain_rococo_instance.rs | 53 +++++++++---------- ...untime_bridge_parachain_wococo_instance.rs | 53 +++++++++---------- 2 files changed, 52 insertions(+), 54 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance.rs index 410bd211af9..9e88bd08b7f 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_rococo_instance.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -17,13 +17,13 @@ //! Autogenerated weights for `pallet_bridge_parachains` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-05-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet // --steps=50 @@ -32,18 +32,19 @@ // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=./bench.json -// --header=./file_header.txt -// --chain=bridge-hub-rococo-dev +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_bridge_parachains -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions for `pallet_bridge_parachains`. pub struct WeightInfo(PhantomData); @@ -60,15 +61,13 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) /// The range of component `p` is `[1, 2]`. /// The range of component `p` is `[1, 2]`. - fn submit_parachain_heads_with_n_parachains(p: u32, ) -> Weight { + fn submit_parachain_heads_with_n_parachains(_p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `326` - // Estimated: `7618` - // Minimum execution time: 43_640_000 picoseconds. - Weight::from_parts(70_049_694, 0) - .saturating_add(Weight::from_parts(0, 7618)) - // Standard Error: 651_207 - .saturating_add(Weight::from_parts(7_744_677, 0).saturating_mul(p.into())) + // Measured: `294` + // Estimated: `2543` + // Minimum execution time: 33_300_000 picoseconds. + Weight::from_parts(34_117_420, 0) + .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -84,11 +83,11 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) fn submit_parachain_heads_with_1kb_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `326` - // Estimated: `7618` - // Minimum execution time: 69_524_000 picoseconds. - Weight::from_parts(83_696_000, 0) - .saturating_add(Weight::from_parts(0, 7618)) + // Measured: `294` + // Estimated: `2543` + // Minimum execution time: 34_550_000 picoseconds. + Weight::from_parts(35_046_000, 0) + .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -104,11 +103,11 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf /// Proof: BridgeRococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) fn submit_parachain_heads_with_16kb_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `326` - // Estimated: `7618` - // Minimum execution time: 126_315_000 picoseconds. - Weight::from_parts(147_026_000, 0) - .saturating_add(Weight::from_parts(0, 7618)) + // Measured: `294` + // Estimated: `2543` + // Minimum execution time: 61_071_000 picoseconds. + Weight::from_parts(61_554_000, 0) + .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance.rs index b02641ab918..238fa8725d3 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains_bridge_parachains_bench_runtime_bridge_parachain_wococo_instance.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -17,13 +17,13 @@ //! Autogenerated weights for `pallet_bridge_parachains` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-05-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet // --steps=50 @@ -32,18 +32,19 @@ // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=./bench.json -// --header=./file_header.txt -// --chain=bridge-hub-rococo-dev +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_bridge_parachains -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions for `pallet_bridge_parachains`. pub struct WeightInfo(PhantomData); @@ -60,15 +61,13 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) /// The range of component `p` is `[1, 2]`. /// The range of component `p` is `[1, 2]`. - fn submit_parachain_heads_with_n_parachains(p: u32, ) -> Weight { + fn submit_parachain_heads_with_n_parachains(_p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `399` - // Estimated: `7618` - // Minimum execution time: 47_436_000 picoseconds. - Weight::from_parts(75_670_977, 0) - .saturating_add(Weight::from_parts(0, 7618)) - // Standard Error: 724_179 - .saturating_add(Weight::from_parts(5_669_961, 0).saturating_mul(p.into())) + // Measured: `367` + // Estimated: `2543` + // Minimum execution time: 34_469_000 picoseconds. + Weight::from_parts(35_382_374, 0) + .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -84,11 +83,11 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) fn submit_parachain_heads_with_1kb_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `399` - // Estimated: `7618` - // Minimum execution time: 70_961_000 picoseconds. - Weight::from_parts(83_138_000, 0) - .saturating_add(Weight::from_parts(0, 7618)) + // Measured: `367` + // Estimated: `2543` + // Minimum execution time: 35_690_000 picoseconds. + Weight::from_parts(36_400_000, 0) + .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -104,11 +103,11 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf /// Proof: BridgeWococoParachain ImportedParaHeads (max_values: Some(64), max_size: Some(196), added: 1186, mode: MaxEncodedLen) fn submit_parachain_heads_with_16kb_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `399` - // Estimated: `7618` - // Minimum execution time: 122_639_000 picoseconds. - Weight::from_parts(142_091_000, 0) - .saturating_add(Weight::from_parts(0, 7618)) + // Measured: `367` + // Estimated: `2543` + // Minimum execution time: 62_242_000 picoseconds. + Weight::from_parts(62_690_000, 0) + .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } From 68cd27aa4e43ae3e88862c919bff0e21c21d0ec5 Mon Sep 17 00:00:00 2001 From: command-bot <> Date: Wed, 3 May 2023 18:36:45 +0000 Subject: [PATCH 262/263] ".git/.scripts/commands/bench/bench.sh" pallet bridge-hub-rococo bridge-hubs pallet_bridge_messages --- ...ith_bridge_hub_rococo_messages_instance.rs | 90 ++++++++++--------- ...ith_bridge_hub_wococo_messages_instance.rs | 90 ++++++++++--------- 2 files changed, 92 insertions(+), 88 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs index 7f61b0d6a11..47d1ec8d47c 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_rococo_messages_instance.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -17,13 +17,13 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-05-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// target/debug/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet // --steps=50 @@ -32,17 +32,19 @@ // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_bridge_messages // --chain=bridge-hub-rococo-dev // --header=./file_header.txt -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions for `pallet_bridge_messages`. pub struct WeightInfo(PhantomData); @@ -58,10 +60,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: // Measured: `367` - // Estimated: `57797` - // Minimum execution time: 1_052_304_000 picoseconds. - Weight::from_parts(1_067_318_000, 0) - .saturating_add(Weight::from_parts(0, 57797)) + // Estimated: `52645` + // Minimum execution time: 42_364_000 picoseconds. + Weight::from_parts(43_780_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -76,10 +78,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_two_messages_proof() -> Weight { // Proof Size summary in bytes: // Measured: `367` - // Estimated: `57797` - // Minimum execution time: 1_358_159_000 picoseconds. - Weight::from_parts(1_371_112_000, 0) - .saturating_add(Weight::from_parts(0, 57797)) + // Estimated: `52645` + // Minimum execution time: 54_010_000 picoseconds. + Weight::from_parts(66_691_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -94,10 +96,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: // Measured: `367` - // Estimated: `57797` - // Minimum execution time: 1_248_139_000 picoseconds. - Weight::from_parts(1_262_958_000, 0) - .saturating_add(Weight::from_parts(0, 57797)) + // Estimated: `52645` + // Minimum execution time: 48_066_000 picoseconds. + Weight::from_parts(48_635_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -110,10 +112,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_single_message_proof_1_kb() -> Weight { // Proof Size summary in bytes: // Measured: `335` - // Estimated: `56308` - // Minimum execution time: 1_006_936_000 picoseconds. - Weight::from_parts(1_017_299_000, 0) - .saturating_add(Weight::from_parts(0, 56308)) + // Estimated: `52645` + // Minimum execution time: 40_997_000 picoseconds. + Weight::from_parts(41_710_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -126,10 +128,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_single_message_proof_16_kb() -> Weight { // Proof Size summary in bytes: // Measured: `335` - // Estimated: `56308` - // Minimum execution time: 1_734_842_000 picoseconds. - Weight::from_parts(1_750_451_000, 0) - .saturating_add(Weight::from_parts(0, 56308)) + // Estimated: `52645` + // Minimum execution time: 67_556_000 picoseconds. + Weight::from_parts(68_573_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -146,10 +148,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: // Measured: `339` - // Estimated: `12534` - // Minimum execution time: 983_917_000 picoseconds. - Weight::from_parts(991_532_000, 0) - .saturating_add(Weight::from_parts(0, 12534)) + // Estimated: `3804` + // Minimum execution time: 32_221_000 picoseconds. + Weight::from_parts(32_582_000, 0) + .saturating_add(Weight::from_parts(0, 3804)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -166,10 +168,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: // Measured: `339` - // Estimated: `12534` - // Minimum execution time: 983_370_000 picoseconds. - Weight::from_parts(992_178_000, 0) - .saturating_add(Weight::from_parts(0, 12534)) + // Estimated: `3804` + // Minimum execution time: 32_395_000 picoseconds. + Weight::from_parts(32_703_000, 0) + .saturating_add(Weight::from_parts(0, 3804)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -186,10 +188,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: // Measured: `339` - // Estimated: `15082` - // Minimum execution time: 1_118_509_000 picoseconds. - Weight::from_parts(1_136_181_000, 0) - .saturating_add(Weight::from_parts(0, 15082)) + // Estimated: `6086` + // Minimum execution time: 34_409_000 picoseconds. + Weight::from_parts(34_927_000, 0) + .saturating_add(Weight::from_parts(0, 6086)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -218,12 +220,12 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_single_message_proof_with_dispatch(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `635` - // Estimated: `71012` - // Minimum execution time: 1_887_056_000 picoseconds. - Weight::from_parts(1_896_414_726, 0) - .saturating_add(Weight::from_parts(0, 71012)) - // Standard Error: 2_462 - .saturating_add(Weight::from_parts(557_738, 0).saturating_mul(i.into())) + // Estimated: `52645` + // Minimum execution time: 129_154_000 picoseconds. + Weight::from_parts(103_525_480, 0) + .saturating_add(Weight::from_parts(0, 52645)) + // Standard Error: 2_595 + .saturating_add(Weight::from_parts(536_761, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs index 811e483a779..0b2aceb489e 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_bridge_messages_bench_runtime_with_bridge_hub_wococo_messages_instance.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -17,13 +17,13 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-05-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// target/debug/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet // --steps=50 @@ -32,17 +32,19 @@ // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_bridge_messages // --chain=bridge-hub-rococo-dev // --header=./file_header.txt -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions for `pallet_bridge_messages`. pub struct WeightInfo(PhantomData); @@ -58,10 +60,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: // Measured: `404` - // Estimated: `57797` - // Minimum execution time: 1_063_700_000 picoseconds. - Weight::from_parts(1_073_859_000, 0) - .saturating_add(Weight::from_parts(0, 57797)) + // Estimated: `52645` + // Minimum execution time: 43_745_000 picoseconds. + Weight::from_parts(45_462_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -76,10 +78,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_two_messages_proof() -> Weight { // Proof Size summary in bytes: // Measured: `404` - // Estimated: `57797` - // Minimum execution time: 1_363_828_000 picoseconds. - Weight::from_parts(1_383_808_000, 0) - .saturating_add(Weight::from_parts(0, 57797)) + // Estimated: `52645` + // Minimum execution time: 55_455_000 picoseconds. + Weight::from_parts(65_023_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -94,10 +96,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: // Measured: `404` - // Estimated: `57797` - // Minimum execution time: 1_258_314_000 picoseconds. - Weight::from_parts(1_276_600_000, 0) - .saturating_add(Weight::from_parts(0, 57797)) + // Estimated: `52645` + // Minimum execution time: 48_603_000 picoseconds. + Weight::from_parts(50_377_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -110,10 +112,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_single_message_proof_1_kb() -> Weight { // Proof Size summary in bytes: // Measured: `372` - // Estimated: `56308` - // Minimum execution time: 1_019_420_000 picoseconds. - Weight::from_parts(1_031_056_000, 0) - .saturating_add(Weight::from_parts(0, 56308)) + // Estimated: `52645` + // Minimum execution time: 42_777_000 picoseconds. + Weight::from_parts(43_499_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -126,10 +128,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_single_message_proof_16_kb() -> Weight { // Proof Size summary in bytes: // Measured: `372` - // Estimated: `56308` - // Minimum execution time: 1_745_271_000 picoseconds. - Weight::from_parts(1_759_460_000, 0) - .saturating_add(Weight::from_parts(0, 56308)) + // Estimated: `52645` + // Minimum execution time: 69_408_000 picoseconds. + Weight::from_parts(69_916_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -146,10 +148,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: // Measured: `376` - // Estimated: `12571` - // Minimum execution time: 993_132_000 picoseconds. - Weight::from_parts(1_005_111_000, 0) - .saturating_add(Weight::from_parts(0, 12571)) + // Estimated: `3841` + // Minimum execution time: 33_122_000 picoseconds. + Weight::from_parts(34_379_000, 0) + .saturating_add(Weight::from_parts(0, 3841)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -166,10 +168,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: // Measured: `376` - // Estimated: `12571` - // Minimum execution time: 995_201_000 picoseconds. - Weight::from_parts(1_003_630_000, 0) - .saturating_add(Weight::from_parts(0, 12571)) + // Estimated: `3841` + // Minimum execution time: 33_049_000 picoseconds. + Weight::from_parts(33_747_000, 0) + .saturating_add(Weight::from_parts(0, 3841)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -186,10 +188,10 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: // Measured: `376` - // Estimated: `15119` - // Minimum execution time: 1_126_518_000 picoseconds. - Weight::from_parts(1_143_524_000, 0) - .saturating_add(Weight::from_parts(0, 15119)) + // Estimated: `6086` + // Minimum execution time: 35_578_000 picoseconds. + Weight::from_parts(36_007_000, 0) + .saturating_add(Weight::from_parts(0, 6086)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -218,12 +220,12 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< fn receive_single_message_proof_with_dispatch(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `672` - // Estimated: `71234` - // Minimum execution time: 1_893_906_000 picoseconds. - Weight::from_parts(1_907_046_615, 0) - .saturating_add(Weight::from_parts(0, 71234)) - // Standard Error: 2_494 - .saturating_add(Weight::from_parts(561_329, 0).saturating_mul(i.into())) + // Estimated: `52645` + // Minimum execution time: 130_070_000 picoseconds. + Weight::from_parts(104_597_637, 0) + .saturating_add(Weight::from_parts(0, 52645)) + // Standard Error: 2_607 + .saturating_add(Weight::from_parts(538_698, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } From 0f20894a488b6743d669fe916f2be4a0b592cc4a Mon Sep 17 00:00:00 2001 From: command-bot <> Date: Wed, 3 May 2023 19:38:28 +0000 Subject: [PATCH 263/263] ".git/.scripts/commands/bench/bench.sh" pallet bridge-hub-rococo bridge-hubs pallet_bridge_relayers --- .../src/weights/pallet_bridge_relayers.rs | 121 ++++++++---------- 1 file changed, 54 insertions(+), 67 deletions(-) diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs index 2b4b5ac716b..418f1326766 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. +// Copyright Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify @@ -17,33 +17,34 @@ //! Autogenerated weights for `pallet_bridge_relayers` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-15, STEPS: `50`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-05-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bkontur-ThinkPad-P14s-Gen-2i`, CPU: `11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet // --steps=50 -// --repeat=2 +// --repeat=20 // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --json-file=./bench.json -// --header=./file_header.txt -// --chain=bridge-hub-rococo-dev +// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/cumulus/.git/.artifacts/bench.json // --pallet=pallet_bridge_relayers -// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights +// --chain=bridge-hub-rococo-dev +// --header=./file_header.txt +// --output=./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::Weight}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; /// Weight functions for `pallet_bridge_relayers`. pub struct WeightInfo(PhantomData); @@ -54,84 +55,70 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn claim_rewards() -> Weight { // Proof Size summary in bytes: - // Measured: `239` - // Estimated: `7131` - // Minimum execution time: 37_638_000 picoseconds. - Weight::from_parts(43_222_000, 0) - .saturating_add(Weight::from_parts(0, 7131)) + // Measured: `207` + // Estimated: `3593` + // Minimum execution time: 53_286_000 picoseconds. + Weight::from_parts(53_905_000, 0) + .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// + /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: unknown `0x1e8445dc201eeb8560e5579a5dd54655` (r:1 w:0) + /// Proof Skipped: unknown `0x1e8445dc201eeb8560e5579a5dd54655` (r:1 w:0) /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) fn register() -> Weight { // Proof Size summary in bytes: - // Measured: `87` - // Estimated: `7843` - // Minimum execution time: 39_590 nanoseconds. - Weight::from_parts(40_546_000, 7843) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + // Measured: `61` + // Estimated: `4714` + // Minimum execution time: 29_145_000 picoseconds. + Weight::from_parts(29_698_000, 0) + .saturating_add(Weight::from_parts(0, 4714)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// + /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) fn deregister() -> Weight { // Proof Size summary in bytes: - // Measured: `264` - // Estimated: `7843` - // Minimum execution time: 43_332 nanoseconds. - Weight::from_parts(45_087_000, 7843) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + // Measured: `160` + // Estimated: `4714` + // Minimum execution time: 30_298_000 picoseconds. + Weight::from_parts(30_754_000, 0) + .saturating_add(Weight::from_parts(0, 4714)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// + /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) - /// + /// Proof: Balances Reserves (max_values: None, max_size: Some(1249), added: 3724, mode: MaxEncodedLen) /// Storage: System Account (r:1 w:1) - /// - /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: - /// MaxEncodedLen) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn slash_and_deregister() -> Weight { // Proof Size summary in bytes: - // Measured: `380` - // Estimated: `11412` - // Minimum execution time: 42_358 nanoseconds. - Weight::from_parts(43_539_000, 11412) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + // Measured: `263` + // Estimated: `4714` + // Minimum execution time: 30_109_000 picoseconds. + Weight::from_parts(30_330_000, 0) + .saturating_add(Weight::from_parts(0, 4714)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) + /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) fn register_relayer_reward() -> Weight { // Proof Size summary in bytes: - // Measured: `12` - // Estimated: `3530` - // Minimum execution time: 6_338 nanoseconds. - Weight::from_parts(6_526_000, 3530) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Measured: `6` + // Estimated: `3538` + // Minimum execution time: 3_016_000 picoseconds. + Weight::from_parts(3_130_000, 0) + .saturating_add(Weight::from_parts(0, 3538)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) } }